[
  {
    "path": ".clang-format",
    "content": "# please use clang-format version 16 or later\n\nStandard: c++17\nAccessModifierOffset: -8\nAlignAfterOpenBracket: Align\nAlignConsecutiveAssignments: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: Left\nAlignOperands: true\nAlignTrailingComments: true\nAllowAllArgumentsOnNextLine: false\nAllowAllConstructorInitializersOnNextLine: false\nAllowAllParametersOfDeclarationOnNextLine: false\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: Inline\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLambdasOnASingleLine: Inline\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: false\nBinPackArguments: true\nBinPackParameters: true\nBraceWrapping:\n  AfterClass: false\n  AfterControlStatement: false\n  AfterEnum: false\n  AfterFunction: true\n  AfterNamespace: false\n  AfterObjCDeclaration: false\n  AfterStruct: false\n  AfterUnion: false\n  AfterExternBlock: false\n  BeforeCatch: false\n  BeforeElse: false\n  IndentBraces: false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: None\nBreakBeforeBraces: Custom\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializers: BeforeColon\nBreakStringLiterals: false  # apparently unpredictable\nColumnLimit: 120\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nConstructorInitializerIndentWidth: 8\nContinuationIndentWidth: 8\nCpp11BracedListStyle: true\nDerivePointerAlignment: false\nDisableFormat: false\nFixNamespaceComments: true\nForEachMacros:\n  - 'json_object_foreach'\n  - 'json_object_foreach_safe'\n  - 'json_array_foreach'\n  - 'HASH_ITER'\nIncludeBlocks: Preserve\nIndentCaseLabels: false\nIndentPPDirectives: None\nIndentWidth: 8\nIndentWrappedFunctionNames: false\nKeepEmptyLinesAtTheStartOfBlocks: true\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 8\nObjCSpaceAfterProperty: true\nObjCSpaceBeforeProtocolList: true\n\nPenaltyBreakAssignment: 10\nPenaltyBreakBeforeFirstCallParameter: 30\nPenaltyBreakComment: 10\nPenaltyBreakFirstLessLess: 0\nPenaltyBreakString: 10\nPenaltyExcessCharacter: 100\nPenaltyReturnTypeOnItsOwnLine: 60\n\nPointerAlignment: Right\nReflowComments: false\nSkipMacroDefinitionBody: true\nSortIncludes: false\nSortUsingDeclarations: false\nSpaceAfterCStyleCast: false\nSpaceAfterLogicalNot: false\nSpaceAfterTemplateKeyword: false\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: ControlStatements\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 1\nSpacesInAngles: false\nSpacesInCStyleCastParentheses: false\nSpacesInContainerLiterals: false\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nStatementMacros:\n  - 'Q_OBJECT'\nTabWidth: 8\nTypenameMacros:\n  - 'DARRAY'\nUseTab: ForContinuationAndIndentation\n---\nLanguage: ObjC\nAccessModifierOffset: 2\nAlignArrayOfStructures: Right\nAlignConsecutiveAssignments: None\nAlignConsecutiveBitFields: None\nAlignConsecutiveDeclarations: None\nAlignConsecutiveMacros:\n  Enabled: true\n  AcrossEmptyLines: false\n  AcrossComments: true\nAllowShortBlocksOnASingleLine: Never\nAllowShortEnumsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: Empty\nAllowShortIfStatementsOnASingleLine: Never\nAllowShortLambdasOnASingleLine: None\nAttributeMacros: ['__unused', '__autoreleasing', '_Nonnull', '__bridge']\nBitFieldColonSpacing: Both\n#BreakBeforeBraces: Webkit\nBreakBeforeBraces: Custom\nBraceWrapping:\n  AfterCaseLabel: false\n  AfterClass: true\n  AfterControlStatement: Never\n  AfterEnum: false\n  AfterFunction: true\n  AfterNamespace: false\n  AfterObjCDeclaration: false\n  AfterStruct: false\n  AfterUnion: false\n  AfterExternBlock: false\n  BeforeCatch: false\n  BeforeElse: false\n  BeforeLambdaBody: false\n  BeforeWhile: false\n  IndentBraces: false\n  SplitEmptyFunction: false\n  SplitEmptyRecord: false\n  SplitEmptyNamespace: true\nBreakAfterAttributes: Never\nBreakArrays: false\nBreakBeforeConceptDeclarations: Allowed\nBreakBeforeInlineASMColon: OnlyMultiline\nBreakConstructorInitializers: AfterColon\nBreakInheritanceList: AfterComma\nColumnLimit: 120\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nEmptyLineAfterAccessModifier: Never\nEmptyLineBeforeAccessModifier: LogicalBlock\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: true\nIndentAccessModifiers: false\nIndentCaseBlocks: false\nIndentCaseLabels: true\nIndentExternBlock: Indent\nIndentGotoLabels: false\nIndentRequiresClause: true\nIndentWidth: 4\nIndentWrappedFunctionNames: true\nInsertBraces: false\nInsertNewlineAtEOF: true\nKeepEmptyLinesAtTheStartOfBlocks: false\nLambdaBodyIndentation: Signature\nNamespaceIndentation: All\nObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 4\nObjCBreakBeforeNestedBlockParam: false\nObjCSpaceAfterProperty: true\nObjCSpaceBeforeProtocolList: true\nPPIndentWidth: -1\nPackConstructorInitializers: NextLine\nQualifierAlignment: Leave\nReferenceAlignment: Right\nRemoveSemicolon: false\nRequiresClausePosition: WithPreceding\nRequiresExpressionIndentation: OuterScope\nSeparateDefinitionBlocks: Leave\nShortNamespaceLines: 1\nSortIncludes: false\n#SortUsingDeclarations: LexicographicNumeric\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: true\nSpaceAfterLogicalNot: false\nSpaceAroundPointerQualifiers: Default\nSpaceBeforeCaseColon: false\nSpaceBeforeCpp11BracedList: true\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: ControlStatements\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceBeforeSquareBrackets: false\nSpaceInEmptyBlock: false\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 2\nSpacesInConditionalStatement: false\nSpacesInLineCommentPrefix:\n  Minimum: 1\n  Maximum: -1\nStandard: c++17\nTabWidth: 4\nUseTab: Never\n"
  },
  {
    "path": ".editorconfig",
    "content": "# EditorConfig is awesome: http://EditorConfig.org\n# Since OBS follows the Linux kernel coding style I have started this file to\n# help with automatically setting various text editors to those requirements.\n\n# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with a newline ending every file.\n[*]\ninsert_final_newline = true\ntrim_trailing_whitespace = true\ncharset = utf-8\nindent_style = tab\nindent_size = 8\n\n# As per notr1ch, for 3rd party code that's a part of obs-outputs.\n[plugins/obs-outputs/librtmp/*.{cpp,c,h}]\nindent_style = space\nindent_size = 4\n\n[CMakeLists.txt]\nindent_style = space\nindent_size = 2\n\n[**/CMakeLists.txt]\nindent_style = space\nindent_size = 2\n\n[cmake/**/*.cmake]\nindent_style = space\nindent_size = 2\n\n[plugins/{rtmp-services,win-capture}/data/**/*.json]\nindent_style = space\nindent_size = 4\n\n[*.qss]\nindent_style = space\nindent_size = 4\n\n[build-aux/**/*.json]\nindent_style = space\nindent_size = 4\n\n[*.py]\nindent_style = space\nindent_size = 4\n\n[*.yaml]\nindent_style = space\nindent_size = 2\n\n[{*.zsh,.*.zsh,build-aux/.functions/*,.github/scripts/utils.zsh/*}]\nindent_style = space\nindent_size = 2\n\n[*.ui]\nindent_style = space\nindent_size = 1\n\n[{*.obt,*.oha,*.ovt}]\nindent_style = space\nindent_size = 4\n"
  },
  {
    "path": ".gersemirc",
    "content": "# yaml-language-server: $schema=https://raw.githubusercontent.com/BlankSpruce/gersemi/master/gersemi/configuration.schema.json\n\ndefinitions: []\nline_length: 120\nindent: 2\nlist_expansion: favour-inlining\nunsafe: false\nwarn_about_unknown_commands: false\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "open_collective: obsproject\npatreon: obsproject\n"
  },
  {
    "path": ".github/actions/build-obs/action.yaml",
    "content": "name: Set Up and Build obs-studio\ndescription: Builds obs-studio for specified architecture and build config\ninputs:\n  target:\n    description: Build target for obs-studio\n    required: true\n  config:\n    description: Build configuration\n    required: false\n    default: RelWithDebInfo\n  codesign:\n    description: Enable codesigning (macOS only)\n    required: false\n    default: 'false'\n  codesignIdent:\n    description: Developer ID for application codesigning (macOS only)\n    required: false\n    default: '-'\n  codesignTeam:\n    description: Team ID for application codesigning (macOS only)\n    required: false\n    default: ''\n  provisioningProfileUUID:\n    description: UUID of provisioning profile (macOS only)\n    required: false\n    default: ''\n  workingDirectory:\n    description: Working directory for packaging\n    required: false\n    default: ${{ github.workspace }}\nruns:\n  using: composite\n  steps:\n    - name: Run macOS Build\n      if: runner.os == 'macOS'\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      working-directory: ${{ inputs.workingDirectory }}\n      env:\n        CODESIGN_IDENT: ${{ inputs.codesignIdent }}\n        CODESIGN_TEAM: ${{ inputs.codesignTeam }}\n        PROVISIONING_PROFILE: ${{ inputs.provisioningProfileUUID }}\n      run: |\n        : Run macOS Build\n\n        local -a build_args=(\n          --config ${{ inputs.config }}\n          --target macos-${{ inputs.target }}\n        )\n        if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)\n\n        if [[ '${{ inputs.codesign }}' == true ]] build_args+=(--codesign)\n\n        git fetch origin --no-tags --no-recurse-submodules -q\n        .github/scripts/build-macos ${build_args}\n\n    - name: Install Dependencies 🛍️\n      if: runner.os == 'Linux'\n      shell: bash\n      run: |\n        : Install Dependencies 🛍️\n        echo ::group::Install Dependencies\n        eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n        echo \"/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin\" >> $GITHUB_PATH\n        brew install --quiet zsh\n        echo ::endgroup::\n\n    - name: Run Ubuntu Build\n      if: runner.os == 'Linux'\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      working-directory: ${{ inputs.workingDirectory }}\n      run: |\n        : Run Ubuntu Build\n\n        local -a build_args=(\n          --config ${{ inputs.config }}\n          --target ubuntu-${{ inputs.target }}\n        )\n        if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)\n\n        git fetch origin --no-tags --no-recurse-submodules -q\n        .github/scripts/build-ubuntu ${build_args}\n\n    - name: Run Windows Build\n      if: runner.os == 'Windows'\n      shell: pwsh\n      working-directory: ${{ inputs.workingDirectory }}\n      run: |\n        # Run Windows Build\n        $BuildArgs = @{\n          Target = '${{ inputs.target }}'\n          Configuration = '${{ inputs.config }}'\n        }\n\n        if ( $Env:RUNNER_DEBUG -ne $null ) {\n          $BuildArgs += @{ Debug = $true }\n        }\n\n        git fetch origin --no-tags --no-recurse-submodules -q\n        .github/scripts/Build-Windows.ps1 @BuildArgs\n\n    - name: Create Summary 📊\n      if: runner.os == 'Linux'\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Create Summary 📊\n\n        local -a ccache_data\n        if (( ${+RUNNER_DEBUG} )) {\n          setopt XTRACE\n          ccache_data=(\"${(fA)$(ccache -s -vv)}\")\n        } else {\n          ccache_data=(\"${(fA)$(ccache -s)}\")\n        }\n\n        print '### ${{ runner.os }} Ccache Stats (${{ inputs.target }})' >> $GITHUB_STEP_SUMMARY\n        print '```' >> $GITHUB_STEP_SUMMARY\n        for line (${ccache_data}) {\n          print ${line} >> $GITHUB_STEP_SUMMARY\n        }\n        print '```' >> $GITHUB_STEP_SUMMARY\n"
  },
  {
    "path": ".github/actions/check-changes/action.yaml",
    "content": "name: Check For Changed Files\ndescription: Checks for changed files compared to specific git reference and glob expression\ninputs:\n  baseRef:\n    description: Git reference to check against\n    required: false\n  ref:\n    description: Git reference to check with\n    required: false\n    default: HEAD\n  checkGlob:\n    description: Glob expression to limit check to specific files\n    required: false\n  useFallback:\n    description: Use fallback compare against prior commit\n    required: false\n    default: 'true'\n  diffFilter:\n    description: git diff-filter string to use\n    required: false\n    default: ''\noutputs:\n  hasChangedFiles:\n    value: ${{ steps.checks.outputs.hasChangedFiles }}\n    description: True if specified files were changed in comparison to specified git reference\n  changedFiles:\n    value: ${{ steps.checks.outputs.changedFiles }}\n    description: List of changed files\nruns:\n  using: composite\n  steps:\n    - name: Check For Changed Files ✅\n      shell: bash\n      id: checks\n      env:\n        GIT_BASE_REF: ${{ inputs.baseRef }}\n        GIT_REF: ${{ inputs.ref }}\n        GITHUB_EVENT_FORCED: ${{ github.event.forced }}\n        GITHUB_REF_BEFORE: ${{ github.event.before }}\n        USE_FALLBACK: ${{ inputs.useFallback }}\n        DIFF_FILTER: ${{ inputs.diffFilter }}\n      run: |\n        : Check for Changed Files ✅\n        if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n        shopt -s extglob\n        shopt -s dotglob\n\n        # 4b825dc642cb6eb9a060e54bf8d69288fbee4904 is a \"hidden\" sha1 hash of\n        # the \"empty tree\", retrieved via 'git hash-object -t tree /dev/null',\n        # and used here as a last-resort fallback to always provide a valid\n        # git ref.\n\n        if [[ \"${GIT_BASE_REF}\" ]]; then\n          if ! git cat-file -e \"${GIT_BASE_REF}\" &> /dev/null; then\n            echo \"::warning::Provided base reference ${GIT_BASE_REF} is invalid\"\n            if [[ \"${USE_FALLBACK}\" == 'true' ]]; then\n              GIT_BASE_REF='HEAD~1'\n            fi\n          fi\n        else\n          if ! git cat-file -e ${GITHUB_REF_BEFORE} &> /dev/null; then\n            GITHUB_REF_BEFORE='4b825dc642cb6eb9a060e54bf8d69288fbee4904'\n          fi\n\n          GIT_BASE_REF='HEAD~1'\n          case \"${GITHUB_EVENT_NAME}\" in\n            pull_request) GIT_BASE_REF=\"origin/${GITHUB_BASE_REF}\" ;;\n            push) if [[ \"${GITHUB_EVENT_FORCED}\" != 'true' ]]; then GIT_BASE_REF=\"${GITHUB_REF_BEFORE}\"; fi ;;\n            *) ;;\n          esac\n        fi\n\n        changes=($(git diff --name-only --diff-filter=\"${DIFF_FILTER}\" ${GIT_BASE_REF} ${GIT_REF} -- ${{ inputs.checkGlob }}))\n\n        if (( ${#changes[@]} )); then\n          file_string=\"${changes[*]}\"\n          echo \"hasChangedFiles=true\" >> $GITHUB_OUTPUT\n          echo \"changedFiles=[\\\"${file_string// /\\\",\\\"}\\\"]\" >> $GITHUB_OUTPUT\n        else\n          echo \"hasChangedFiles=false\" >> $GITHUB_OUTPUT\n          echo \"changedFiles=[]\" >> GITHUB_OUTPUT\n        fi\n"
  },
  {
    "path": ".github/actions/compatibility-validator/action.yaml",
    "content": "name: Compatibility Data Validator\ndescription: Checks Windows compatibility data files\ninputs:\n  repositorySecret:\n    description: GitHub token for API access\n    required: true\n  workingDirectory:\n    description: Working directory for checks\n    required: false\n    default: ${{ github.workspace }}\nruns:\n  using: composite\n  steps:\n    - name: Check Runner Operating System 🏃‍♂️\n      if: runner.os == 'Windows'\n      shell: bash\n      run: |\n        : Check Runner Operating System 🏃‍♂️\n        echo \"services-validation action requires a macOS-based or Linux-based runner.\"\n        exit 2\n\n    - name: Install and Configure Python 🐍\n      shell: bash\n      run: |\n        : Install and Configure Python 🐍\n        if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n        echo ::group::Python Set Up\n        if [[ \"${RUNNER_OS}\" == Linux ]]; then\n          eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n          echo \"/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin\" >> $GITHUB_PATH\n        fi\n        brew install --quiet python3\n\n        python3 -m venv .venv\n\n        source .venv/bin/activate\n        python3 -m pip install jsonschema json_source_map\n        echo ::endgroup::\n\n    - name: Validate Compatibility Files JSON Schema 🕵️\n      shell: bash\n      working-directory: ${{ inputs.workingDirectory }}\n      run: |\n        : Validate services file JSON schema 🕵️\n        if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n        shopt -s extglob\n\n        echo ::group::Schema Validation\n        source .venv/bin/activate\n        python3 -u \\\n          .github/scripts/utils.py/check-jsonschema.py \\\n          --loglevel INFO \\\n          plugins/win-capture/data/@(compatibility|package).json\n        echo ::endgroup::\n\n    - name: Annotate Schema Validation Errors 🏷️\n      uses: yuzutech/annotations-action@0e061a6e3ac848299310b6429b60d67cafd4e7f8\n      if: failure()\n      with:\n        repo-token: ${{ inputs.repositorySecret }}\n        title: Compatibility JSON Errors\n        input: ${{ inputs.workingDirectory }}/validation_errors.json\n"
  },
  {
    "path": ".github/actions/flatpak-builder-lint/action.yaml",
    "content": "name: Run flatpak-builder-lint\ndescription: Runs flatpak-builder-lint with exceptions\ninputs:\n  artifact:\n    description: Type of artifact to lint (builddir, repo, manifest, appstream)\n    required: true\n  path:\n    description: Path to flatpak-builder manifest or Flatpak build directory\n    required: true\n  workingDirectory:\n    description: Working directory to clone flatpak-builder-lint\n    required: false\n    default: ${{ github.workspace }}\nruns:\n  using: composite\n  steps:\n    - name: Check artifact type\n      shell: bash\n      working-directory: ${{ inputs.workingDirectory }}\n      run: |\n        : Check artifact input\n        if ! [[ \"${{ inputs.artifact }}\" =~ builddir|repo|manifest|appstream ]]; then\n          echo \"::error::Given artifact type is incorrect\"\n          exit 2\n        fi\n\n    - name: Run flatpak-builder-lint\n      id: result\n      shell: bash\n      working-directory: ${{ inputs.workingDirectory }}\n      run: |\n        : Run flatpak-builder-lint\n\n        return=0\n        result=\"$(flatpak-builder-lint --exceptions --user-exceptions ${GITHUB_ACTION_PATH}/exceptions.json ${{ inputs.artifact }} ${{ inputs.path }})\" || return=$?\n\n        if [[ ${return} != 0 && -z \"${result}\" ]]; then\n          echo \"::error::Error while running flatpak-builder-lint\"\n          exit 2\n        fi\n\n        if [[ \"${{ inputs.artifact }}\" == \"appstream\" ]]; then\n          echo \"${result}\"\n\n          if [[ ${return} != 0 ]]; then echo \"::error::Flatpak appstream info is not valid\"; fi\n\n          exit ${return}\n        fi\n\n        # This jq command selects any available array under the 'warnings' key in the JSON document\n        # or provides an empty array as a fallback if the key is not present. This array is then\n        # piped to the 'map' function to apply a transformation to every element in the array,\n        # converting it to a string prefixed with the output level, the actual element value, and\n        # finally the suffix string defined in 'template'.\n        #\n        # The result of this expression is concatenated with a similar expression doing the same\n        # but for the 'errors' key and its associated array.\n        #\n        # The second jq invocation then selects each element of the array and outputs it directly,\n        # which will be strings of the formats:\n        #\n        # * '::warning:: <original warning> <template text>'\n        # * '::error:: <original error> <template text>'\n        #\n        # If no warnings or errors were found, only empty arrays were used for 'map' and thus\n        # only an empty result array is generated.\n\n        template=\" found in the Flatpak ${{ inputs.artifact }}\"\n        while read -r log_line; do\n          if [[ \"${log_line}\" ]]; then echo \"${log_line}\"; fi\n        done <<< \"$(echo \"${result}\" \\\n          | jq -r '(.warnings // [] | try map(\"::warning:: \" + . + \"${template}\")) + (.errors // [] | try map(\"::error:: \" + . + \"${template}\"))' \\\n          | jq -r '.[]')\"\n\n        exit ${return}\n"
  },
  {
    "path": ".github/actions/flatpak-builder-lint/exceptions.json",
    "content": "{\n  \"com.obsproject.Studio\": [\n    \"finish-args-flatpak-spawn-access\",\n    \"external-gitmodule-url-found\",\n    \"finish-args-host-filesystem-access\",\n    \"appstream-flathub-manifest-url-not-reachable\"\n  ]\n}\n"
  },
  {
    "path": ".github/actions/flatpak-manifest-validator/action.yaml",
    "content": "name: Flatpak Manifest Validator\ndescription: Checks order of Flatpak modules in manifest file\ninputs:\n  manifestFile:\n    description: Flatpak manifest file to check\n  failCondition:\n    description: Controls whether failed checks also fail the workflow run\n    required: false\n    default: never\n  workingDirectory:\n    description: Working directory for checks\n    required: false\n    default: ${{ github.workspace }}\nruns:\n  using: composite\n  steps:\n    - name: Check Runner Operating System 🏃‍♂️\n      if: runner.os == 'Windows'\n      shell: bash\n      run: |\n        : Check Runner Operating System 🏃‍♂️\n        echo \"services-validation action requires a macOS-based or Linux-based runner.\"\n        exit 2\n\n    - name: Validate Flatpak Manifest 🕵️\n      shell: bash\n      working-directory: ${{ inputs.workingDirectory }}\n      run: |\n        : Validate Flatpak Manifest 🕵️\n\n        echo ::group::Run Validation\n        if [[ '${{ inputs.failCondition }}' == 'never' ]]; then set +e; fi\n        python3 -u \\\n          build-aux/format-manifest.py \\\n          build-aux/com.obsproject.Studio.json \\\n          --check \\\n          --loglevel INFO\n        echo ::endgroup::\n"
  },
  {
    "path": ".github/actions/generate-docs/action.yaml",
    "content": "name: Generate Documentation\ndescription: Updates Sphinx-based documentation\ninputs:\n  sourceDirectory:\n    description: Path to repository checkout\n    required: false\n    default: ${{ github.workspace }}\n  disableLinkExtensions:\n    description: Disable Sphinx link extensions\n    required: false\n    default: 'false'\nruns:\n  using: composite\n  steps:\n    - name: Update Version Number and Copyright ↗️\n      id: setup\n      shell: bash\n      run: |\n        : Update Version Number and Copyright ↗️\n        if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n        : \"${major:=}\"\n        : \"${minor:=}\"\n        : \"${patch:=}\"\n\n        # This expression will first try to match all the LIBOBS_API_[...]_VER\n        # lines in 'obs-config.h', before removing the '#define ' prefix and\n        # trimming away whitespace characters and linebreaks.\n        # This will yield a single line with the each major, minor, and patch\n        # version variable name followed by its value, so every even item\n        # in this string will contain a version part.\n\n        read -r _ major _ minor _ patch _ <<< \\\n          \"$(grep -E -e \"#define LIBOBS_API_(MAJOR|MINOR|PATCH)_VER *\" libobs/obs-config.h \\\n            | sed 's/#define //g' \\\n            | tr -s ' ' \\\n            | tr '\\n' ' ')\"\n\n        # This expression simply replaces the definition of the 'version' and\n        # 'release' variables in the Python script with updated variants using\n        # the version tokens set by the previous expression.\n        # The copyright variable assignment is updated to use the current\n        # local year as the second year value.\n\n        sed -i -E \\\n          -e \"s/version = '([0-9]+\\.[0-9]+\\.[0-9]+)'/version = '${major}.${minor}.${patch}'/g\" \\\n          -e \"s/release = '([0-9]+\\.[0-9]+\\.[0-9]+)'/release = '${major}.${minor}.${patch}'/g\" \\\n          -e \"s/copyright = '(2017-[0-9]+, Lain Bailey)'/copyright = '2017-$(date +\"%Y\"), Lain Bailey'/g\" \\\n          ${{ inputs.sourceDirectory }}/docs/sphinx/conf.py\n\n        if [[ '${{ inputs.disableLinkExtensions }}' == 'true' ]]; then\n          sed -i -e \"s/html_link_suffix = None/html_link_suffix = ''/g\" \\\n           ${{ inputs.sourceDirectory }}/docs/sphinx/conf.py\n          echo \"artifactName=OBS Studio Docs (No Extensions)\" >> $GITHUB_OUTPUT\n        else\n          echo \"artifactName=OBS Studio Docs\" >> $GITHUB_OUTPUT\n        fi\n\n        echo \"commitHash=${GITHUB_SHA:0:9}\" >> $GITHUB_OUTPUT\n\n    - name: Install Sphinx 📜\n      uses: totaldebug/sphinx-publish-action@a44364271972de3b13c5b63801c2596dfde82413\n      with:\n        sphinx_src: ${{ inputs.sourceDirectory }}/docs/sphinx\n        build_only: true\n        target_branch: master\n        target_path: '../home/_build'\n\n    - uses: actions/upload-artifact@v4\n      with:\n        name: ${{ steps.setup.outputs.artifactName }} ${{ steps.setup.outputs.commitHash }}\n        path: |\n          ${{ runner.temp }}/_github_home/_build\n          !${{ runner.temp }}/_github_home/_build/.doctrees\n"
  },
  {
    "path": ".github/actions/package-obs/action.yaml",
    "content": "name: Package obs-studio\ndescription: Packages obs-studio for specified architecture and build config\ninputs:\n  target:\n    description: Build target for dependencies\n    required: true\n  config:\n    description: Build configuration\n    required: false\n    default: Release\n  codesign:\n    description: Enable codesigning (macOS only)\n    required: false\n    default: 'false'\n  notarize:\n    description: Enable notarization (macOS only)\n    required: false\n    default: 'false'\n  codesignIdent:\n    description: Developer ID for application codesigning (macOS only)\n    required: false\n    default: '-'\n  codesignUser:\n    description: Apple ID username for notarization (macOS only)\n    required: false\n    default: ''\n  codesignPass:\n    description: Apple ID password for notarization (macOS only)\n    required: false\n    default: ''\n  package:\n    description: Create platform-specific packages instead of archives\n    required: false\n    default: 'false'\n  workingDirectory:\n    description: Working directory for packaging\n    required: false\n    default: ${{ github.workspace }}\nruns:\n  using: composite\n  steps:\n    - name: Run macOS packaging\n      if: runner.os == 'macOS'\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      working-directory: ${{ inputs.workingDirectory }}\n      env:\n        CODESIGN_IDENT: ${{ inputs.codesignIdent }}\n        CODESIGN_IDENT_USER: ${{ inputs.codesignUser }}\n        CODESIGN_IDENT_PASS: ${{ inputs.codesignPass }}\n      run: |\n        : Run macOS Packaging\n\n        local -a package_args=(\n          --target macos-${{ inputs.target }}\n          --config ${{ inputs.config }}\n        )\n        if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)\n\n        if [[ '${{ inputs.codesign }}' == true ]] package_args+=(--codesign)\n        if [[ '${{ inputs.notarize }}' == true ]] package_args+=(--notarize)\n        if [[ '${{ inputs.package }}' == true ]] package_args+=(--package)\n\n        .github/scripts/package-macos ${package_args}\n\n    - name: Install Dependencies 🛍️\n      if: runner.os == 'Linux'\n      shell: bash\n      run: |\n        : Install Dependencies 🛍️\n        echo ::group::Install Dependencies\n        eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n        echo \"/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin\" >> $GITHUB_PATH\n        brew install --quiet zsh\n        echo ::endgroup::\n\n    - name: Run Ubuntu packaging\n      if: runner.os == 'Linux'\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      working-directory: ${{ inputs.workingDirectory }}\n      run: |\n        : Run Ubuntu Packaging\n\n        local -a package_args=(\n          --target ubuntu-${{ inputs.target }}\n          --config ${{ inputs.config }}\n        )\n        if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)\n\n        if [[ '${{ inputs.package }}' == true ]] package_args+=(--package)\n\n        ${{ inputs.workingDirectory }}/.github/scripts/package-ubuntu ${package_args}\n\n    - name: Run Windows packaging\n      if: runner.os == 'Windows'\n      shell: pwsh\n      working-directory: ${{ inputs.workingDirectory }}\n      run: |\n        # Run Windows Packaging\n        $PackageArgs = @{\n          Target = '${{ inputs.target }}'\n          Configuration = '${{ inputs.config }}'\n        }\n\n        if ( $Env:RUNNER_DEBUG -ne $null ) {\n          $PackageArgs += @{ Debug = $true }\n        }\n\n        if ( ( Test-Path env:CI ) -and ( Test-Path env:RUNNER_DEBUG ) ) {\n          $BuildArgs += @{\n            Debug = $true\n          }\n        }\n\n        .github/scripts/Package-windows.ps1 @PackageArgs\n"
  },
  {
    "path": ".github/actions/qt-xml-validator/action.yaml",
    "content": "name: Validate UI XML\ndescription: Validates Qt UI XML files\ninputs:\n  failCondition:\n    description: Controls whether failed checks also fail the workflow run\n    required: false\n    default: never\n  workingDirectory:\n    description: Working directory for checks\n    required: false\n    default: ${{ github.workspace }}\nruns:\n  using: composite\n  steps:\n    - name: Check Runner Operating System 🏃‍♂️\n      if: runner.os == 'Windows'\n      shell: bash\n      run: |\n        : Check Runner Operating System 🏃‍♂️\n        echo \"::notice::qt-xml-validator action requires an Linux-based or macOS-based runner.\"\n        exit 2\n\n    - name: Install xmllint 🕵️\n      if: runner.os == 'Linux'\n      shell: bash\n      run: |\n        : Install xmllint 🕵️\n        if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n        echo ::group::Installing libxml2-utils\n        sudo apt-get -qq update\n        sudo apt-get install --no-install-recommends -y libxml2-utils\n        echo ::endgroup::\n\n    - name: Register Annotations 📝\n      uses: korelstar/xmllint-problem-matcher@1bd292d642ddf3d369d02aaa8b262834d61198c0\n\n    - name: Check for Changed Files ✅\n      uses: ./.github/actions/check-changes\n      id: checks\n      with:\n        checkGlob: 'frontend/forms/**/*.ui'\n\n    - name: Validate XML 💯\n      if: fromJSON(steps.checks.outputs.hasChangedFiles)\n      id: result\n      shell: bash\n      env:\n        CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}\n      run: |\n        : Validate XML 💯\n        if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n        CHANGED_FILES=($(echo \"${CHANGED_FILES//[\\[\\]\\'\\\"]/}\" | tr \",\" \"\\n\"))\n\n        if (( ${#CHANGED_FILES[@]} )); then\n          if [[ '${{ inputs.failCondition }}' == never ]]; then set +e; fi\n          xmllint \\\n            --schema ${{ github.workspace }}/frontend/forms/XML-Schema-Qt5.15.xsd \\\n            --noout \"${CHANGED_FILES[@]}\"\n        fi\n"
  },
  {
    "path": ".github/actions/run-clang-format/action.yaml",
    "content": "name: Run clang-format\ndescription: Runs clang-format and checks for any changes introduced by it\ninputs:\n  failCondition:\n    description: Controls whether failed checks also fail the workflow run\n    required: false\n    default: never\n  workingDirectory:\n    description: Working directory for checks\n    required: false\n    default: ${{ github.workspace }}\nruns:\n  using: composite\n  steps:\n    - name: Check Runner Operating System 🏃‍♂️\n      if: runner.os == 'Windows'\n      shell: bash\n      run: |\n        : Check Runner Operating System 🏃‍♂️\n        echo \"::notice::run-clang-format action requires a macOS-based or Linux-based runner.\"\n        exit 2\n\n    - name: Check for Changed Files ✅\n      uses: ./.github/actions/check-changes\n      id: checks\n      with:\n        checkGlob: \"'*.c' '*.h' '*.cpp' '*.hpp' '*.m' '*.mm'\"\n        diffFilter: 'ACM'\n\n    - name: Install Dependencies 🛍️\n      if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles)\n      shell: bash\n      run: |\n        : Install Dependencies 🛍️\n        echo ::group::Install Dependencies\n        eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n        echo \"/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin\" >> $GITHUB_PATH\n        echo \"/home/linuxbrew/.linuxbrew/opt/clang-format@19/bin\" >> $GITHUB_PATH\n        brew install --quiet zsh\n        echo ::endgroup::\n\n    - name: Run clang-format 🐉\n      if: fromJSON(steps.checks.outputs.hasChangedFiles)\n      id: result\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      working-directory: ${{ inputs.workingDirectory }}\n      env:\n        CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}\n      run: |\n        : Run clang-format 🐉\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n        \n        print ::group::Install clang-format-19\n        brew install --quiet obsproject/tools/clang-format@19\n        print ::endgroup::\n\n        print ::group::Run clang-format-19\n        local -a changes=(${(s:,:)CHANGED_FILES//[\\[\\]\\'\\\"]/})\n        ./build-aux/run-clang-format --fail-${{ inputs.failCondition }} --check ${changes}\n        print ::endgroup::\n"
  },
  {
    "path": ".github/actions/run-gersemi/action.yaml",
    "content": "name: Run gersemi\ndescription: Runs gersemi and checks for any changes introduced by it\ninputs:\n  failCondition:\n    description: Controls whether failed checks also fail the workflow run\n    required: false\n    default: never\n  workingDirectory:\n    description: Working directory for checks\n    required: false\n    default: ${{ github.workspace }}\nruns:\n  using: composite\n  steps:\n    - name: Check Runner Operating System 🏃‍♂️\n      if: runner.os == 'Windows'\n      shell: bash\n      run: |\n        : Check Runner Operating System 🏃‍♂️\n        echo \"::notice::run-gersemi action requires a macOS-based or Linux-based runner.\"\n        exit 2\n\n    - name: Check for Changed Files ✅\n      uses: ./.github/actions/check-changes\n      id: checks\n      with:\n        checkGlob: \"'*.cmake' '*CMakeLists.txt'\"\n        diffFilter: 'ACM'\n\n    - name: Install Dependencies 🛍️\n      if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles)\n      shell: bash\n      run: |\n        : Install Dependencies 🛍️\n        echo ::group::Install Dependencies\n        eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n        echo \"/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin\" >> $GITHUB_PATH\n        brew install --quiet zsh\n        echo ::endgroup::\n\n    - name: Run gersemi 🎛️\n      if: fromJSON(steps.checks.outputs.hasChangedFiles)\n      id: result\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      working-directory: ${{ github.workspace }}\n      env:\n        CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}\n      run: |\n        : Run gersemi 🎛️\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        print ::group::Install gersemi\n        brew install --quiet obsproject/tools/gersemi@0.25\n        print ::endgroup::\n\n        print ::group::Run gersemi\n        local -a changes=(${(s:,:)CHANGED_FILES//[\\[\\]\\'\\\"]/})\n        ./build-aux/run-gersemi --fail-${{ inputs.failCondition }} --check ${changes}\n        print ::endgroup::\n"
  },
  {
    "path": ".github/actions/run-swift-format/action.yaml",
    "content": "name: Run swift-format\ndescription: Runs swift-format and checks for any changes introduced by it\ninputs:\n  failCondition:\n    description: Controls whether failed checks also fail the workflow run\n    required: false\n    default: never\n  workingDirectory:\n    description: Working directory for checks\n    required: false\n    default: ${{ github.workspace }}\nruns:\n  using: composite\n  steps:\n    - name: Check Runner Operating System 🏃‍♂️\n      if: runner.os == 'Windows'\n      shell: bash\n      run: |\n        : Check Runner Operating System 🏃‍♂️\n        echo \"::notice::run-swift-format action requires a macOS-based or Linux-based runner.\"\n        exit 2\n\n    - name: Check for Changed Files ✅\n      uses: ./.github/actions/check-changes\n      id: checks\n      with:\n        checkGlob: \"'*.swift'\"\n        diffFilter: 'ACM'\n\n    - name: Install Dependencies 🛍️\n      if: runner.os == 'Linux' && fromJSON(steps.checks.outputs.hasChangedFiles)\n      shell: bash\n      run: |\n        : Install Dependencies 🛍️\n        echo ::group::Install Dependencies\n        eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n        echo \"/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin\" >> $GITHUB_PATH\n        brew install --quiet zsh\n        echo ::endgroup::\n\n    - name: Run swift-format 🔥\n      if: fromJSON(steps.checks.outputs.hasChangedFiles)\n      id: result\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      working-directory: ${{ github.workspace }}\n      env:\n        CHANGED_FILES: ${{ steps.checks.outputs.changedFiles }}\n      run: |\n        : Run swift-format 🔥\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        print ::group::Install swift-format\n        brew install --quiet swift-format\n        print ::endgroup::\n\n        print ::group::Run swift-format\n        local -a changes=(${(s:,:)CHANGED_FILES//[\\[\\]\\'\\\"]/})\n        ./build-aux/run-swift-format --fail-${{ inputs.failCondition }} --check ${changes}\n        print ::endgroup::\n"
  },
  {
    "path": ".github/actions/services-validator/action.yaml",
    "content": "name: Services Validation\ndescription: Checks services configuration file and checks for defunct services\ninputs:\n  repositorySecret:\n    description: GitHub token for API access\n    required: true\n  checkApiSecret:\n    description: Token for server check API\n    required: false\n  checkApiServers:\n    description: Servers for the check API\n    required: false\n  runSchemaChecks:\n    description: Enable schema checking\n    required: false\n    default: 'true'\n  runServiceChecks:\n    description: Enable defunct service checking\n    required: false\n    default: 'false'\n  createPullRequest:\n    description: Enable pull request creation after service checks\n    required: false\n    default: 'false'\n  workingDirectory:\n    description: Working directory for checks\n    required: false\n    default: ${{ github.workspace }}\noutputs:\n  hasDefunctServices:\n    description: True if defunct services were found in configuration\n    value: ${{ steps.check.outputs.make_pr }}\nruns:\n  using: composite\n  steps:\n    - name: Check Runner Operating System 🏃‍♂️\n      if: runner.os == 'Windows'\n      shell: bash\n      run: |\n        : Check Runner Operating System 🏃‍♂️\n        echo \"::notice::services-validation action requires a macOS-based or Linux-based runner.\"\n        exit 2\n\n    - name: Install and Configure Python 🐍\n      shell: bash\n      run: |\n        : Install and Configure Python 🐍\n        if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n        echo ::group::Python Set Up\n        if [[ \"${RUNNER_OS}\" == Linux ]]; then\n          eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n          echo \"/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin\" >> $GITHUB_PATH\n          brew install --overwrite --quiet python3\n        fi\n\n        python3 -m venv .venv\n\n        source .venv/bin/activate\n        python3 -m pip install jsonschema json_source_map requests aiohttp\n        echo ::endgroup::\n\n    - name: Validate Services File JSON Schema 🕵️\n      if: fromJSON(inputs.runSchemaChecks)\n      shell: bash\n      working-directory: ${{ inputs.workingDirectory }}\n      run: |\n        : Validate Services File JSON Schema 🕵️\n        if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n        shopt -s extglob\n\n        echo ::group::Run Validation\n\n        source .venv/bin/activate\n        python3 -u \\\n          .github/scripts/utils.py/check-jsonschema.py \\\n          plugins/rtmp-services/data/@(services|package).json \\\n          --loglevel INFO\n        echo ::endgroup::\n\n    - name: Annotate schema validation errors 🏷️\n      if: fromJSON(inputs.runSchemaChecks) && failure()\n      uses: yuzutech/annotations-action@0e061a6e3ac848299310b6429b60d67cafd4e7f8\n      with:\n        repo-token: ${{ inputs.repositorySecret }}\n        title: Service JSON Errors\n        input: ${{ inputs.workingDirectory }}/validation_errors.json\n\n    - name: Restore Timestamp Cache ⏳\n      if: fromJSON(inputs.runServiceChecks)\n      uses: actions/cache@v4\n      with:\n        path: ${{ github.workspace }}/other\n        key: service-check-${{ github.run_id }}\n        restore-keys: service-check-\n\n    - name: Check for defunct services 📉\n      id: services-check\n      if: fromJSON(inputs.runServiceChecks)\n      shell: bash\n      working-directory: ${{ inputs.workingDirectory }}\n      env:\n        GITHUB_TOKEN: ${{ inputs.repositorySecret }}\n        WORKFLOW_RUN_ID: ${{ github.run_id }}\n        REPOSITORY: ${{ github.repository }}\n        API_KEY: ${{ inputs.checkApiSecret }}\n        API_SERVERS: ${{ inputs.checkApiServers }}\n      run: |\n        : Check for defunct services 📉\n\n        source .venv/bin/activate\n        python3 -u .github/scripts/utils.py/check-services.py\n\n    - uses: actions/upload-artifact@v4\n      if: fromJSON(inputs.runServiceChecks)\n      with:\n        name: timestamps\n        path: ${{ inputs.workingDirectory }}/other/*\n\n    - name: Create pull request 🔧\n      uses: peter-evans/create-pull-request@d121e62763d8cc35b5fb1710e887d6e69a52d3a4\n      if: fromJSON(inputs.createPullRequest) && fromJSON(inputs.runServiceChecks) && fromJSON(steps.services-check.outputs.make_pr)\n      with:\n        author: 'Service Checker <commits@obsproject.com>'\n        commit-message: 'rtmp-services: Remove defunct servers/services'\n        title: 'rtmp-services: Remove defunct servers/services'\n        branch: 'automated/clean-services'\n        body: ${{ fromJSON(steps.services-check.outputs.pr_message) }}\n        delete-branch: true\n"
  },
  {
    "path": ".github/actions/setup-macos-codesigning/action.yaml",
    "content": "name: Set up macOS Code Signing\ndescription: Sets up code signing certificates, provisioning profiles, and notarization information\ninputs:\n  codesignIdentity:\n    description: Code signing identity\n    required: true\n  codesignCertificate:\n    description: PKCS12 certificate in base64 format\n    required: true\n  certificatePassword:\n    description: Password required to install PKCS12 certificate\n    required: true\n  keychainPassword:\n    description: Password to use for temporary keychain\n    required: false\n  notarizationUser:\n    description: Apple ID to use for notarization\n    required: false\n  notarizationPassword:\n    description: Application password for notarization\n  provisioningProfile:\n    description: Provisioning profile in base64 format\n    required: false\noutputs:\n  haveCodesignIdent:\n    description: True if necessary code signing credentials were found\n    value: ${{ steps.codesign.outputs.haveCodesignIdent }}\n  haveProvisioningProfile:\n    description: True if necessary provisioning profile credentials were found\n    value: ${{ steps.provisioning.outputs.haveProvisioningProfile || steps.codesign.outputs.haveProvisioningProfile }}\n  provisioningProfileUUID:\n    description: UUID of imported provisioning profile\n    value: ${{ steps.provisioning.outputs.provisioningProfileUUID }}\n  haveNotarizationUser:\n    description: True if necessary notarization credentials were found\n    value: ${{ steps.notarization.outputs.haveNotarizationUser || steps.codesign.outputs.haveNotarizationUser }}\n  codesignIdent:\n    description: Code signing identity\n    value: ${{ steps.codesign.outputs.codesignIdent }}\n  codesignTeam:\n    description: Code signing team\n    value: ${{ steps.codesign.outputs.codesignTeam }}\nruns:\n  using: composite\n  steps:\n    - name: Check Runner Operating System 🏃‍♂️\n      if: runner.os != 'macOS'\n      shell: bash\n      run: |\n        : Check Runner Operating System 🏃‍♂️\n        echo \"setup-macos-codesigning action requires a macOS-based runner.\"\n        exit 2\n\n    - name: macOS Code Signing ✍️\n      id: codesign\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      env:\n        MACOS_SIGNING_IDENTITY: ${{ inputs.codesignIdentity }}\n        MACOS_SIGNING_CERT: ${{ inputs.codesignCertificate }}\n        MACOS_SIGNING_CERT_PASSWORD: ${{ inputs.certificatePassword }}\n        MACOS_KEYCHAIN_PASSWORD: ${{ inputs.keychainPassword }}\n      run: |\n        : macOS Code Signing ✍️\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        if [[ ${MACOS_SIGNING_IDENTITY} && ${MACOS_SIGNING_CERT} ]] {\n          print 'haveCodesignIdent=true' >> $GITHUB_OUTPUT\n\n          local -r certificate_path=\"${RUNNER_TEMP}/build_certificate.p12\"\n          local -r keychain_path=\"${RUNNER_TEMP}/app-signing.keychain-db\"\n\n          print -n \"${MACOS_SIGNING_CERT}\" | base64 --decode --output=${certificate_path}\n\n          : \"${MACOS_KEYCHAIN_PASSWORD:=\"$(print ${RANDOM} | shasum | head -c 32)\"}\"\n\n          print '::group::Keychain setup'\n          security create-keychain -p \"${MACOS_KEYCHAIN_PASSWORD}\" ${keychain_path}\n          security set-keychain-settings -lut 21600 ${keychain_path}\n          security unlock-keychain -p \"${MACOS_KEYCHAIN_PASSWORD}\" ${keychain_path}\n\n          security import \"${certificate_path}\" -P \"${MACOS_SIGNING_CERT_PASSWORD}\" -A \\\n            -t cert -f pkcs12 -k ${keychain_path} \\\n            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/xcrun\n\n          security set-key-partition-list -S 'apple-tool:,apple:' -k \"${MACOS_KEYCHAIN_PASSWORD}\" \\\n            ${keychain_path} &> /dev/null\n\n          security list-keychain -d user -s ${keychain_path} 'login-keychain'\n          print '::endgroup::'\n\n          local -r team_id=\"${${MACOS_SIGNING_IDENTITY##* }//(\\(|\\))/}\"\n\n          print \"codesignIdent=${MACOS_SIGNING_IDENTITY}\" >> $GITHUB_OUTPUT\n          print \"MACOS_KEYCHAIN_PASSWORD=${MACOS_KEYCHAIN_PASSWORD}\" >> $GITHUB_ENV\n          print \"codesignTeam=${team_id}\" >> $GITHUB_OUTPUT\n        } else {\n          print 'haveCodesignIdent=false' >> $GITHUB_OUTPUT\n          print 'haveProvisioningProfile=false' >> $GITHUB_OUTPUT\n          print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT\n        }\n\n    - name: Provisioning Profile 👤\n      id: provisioning\n      if: fromJSON(steps.codesign.outputs.haveCodesignIdent)\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      env:\n          MACOS_SIGNING_PROVISIONING_PROFILE: ${{ inputs.provisioningProfile }}\n      run: |\n        : Provisioning Profile 👤\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        if [[ \"${MACOS_SIGNING_PROVISIONING_PROFILE}\" ]] {\n          print 'haveProvisioningProfile=true' >> $GITHUB_OUTPUT\n\n          local -r profile_path=\"${RUNNER_TEMP}/build_profile.provisionprofile\"\n          print -n \"${MACOS_SIGNING_PROVISIONING_PROFILE}\" \\\n            | base64 --decode --output=\"${profile_path}\"\n\n          print '::group::Provisioning Profile Setup'\n          mkdir -p ~/Library/MobileDevice/Provisioning\\ Profiles\n          security cms -D -i ${profile_path} -o ${RUNNER_TEMP}/build_profile.plist\n          local -r uuid=\"$(plutil -extract UUID raw ${RUNNER_TEMP}/build_profile.plist)\"\n          local -r team_id=\"$(plutil -extract TeamIdentifier.0 raw -expect string ${RUNNER_TEMP}/build_profile.plist)\"\n\n          if [[ ${team_id} != '${{ steps.codesign.outputs.codesignTeam }}' ]] {\n            print '::notice::Code Signing team in provisioning profile does not match certificate.'\n          }\n\n          cp ${profile_path} ~/Library/MobileDevice/Provisioning\\ Profiles/${uuid}.provisionprofile\n          print \"provisioningProfileUUID=${uuid}\" >> $GITHUB_OUTPUT\n          print '::endgroup::'\n        } else {\n          print 'haveProvisioningProfile=false' >> $GITHUB_OUTPUT\n        }\n\n    - name: Notarization 🧑‍💼\n      id: notarization\n      if: fromJSON(steps.codesign.outputs.haveCodesignIdent)\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      env:\n          MACOS_NOTARIZATION_USERNAME: ${{ inputs.notarizationUser }}\n          MACOS_NOTARIZATION_PASSWORD: ${{ inputs.notarizationPassword }}\n      run: |\n        : Notarization 🧑‍💼\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        if [[ ${MACOS_NOTARIZATION_USERNAME} && ${MACOS_NOTARIZATION_PASSWORD} ]] {\n          print 'haveNotarizationUser=true' >> $GITHUB_OUTPUT\n        } else {\n          print 'haveNotarizationUser=false' >> $GITHUB_OUTPUT\n        }\n"
  },
  {
    "path": ".github/actions/sparkle-appcast/action.yaml",
    "content": "name: Generate Sparkle Appcast\ndescription: Creates Sparkle Appcast for a new release and generates delta patch files\ninputs:\n  sparklePrivateKey:\n    description: Private key used for Sparkle signing\n    required: true\n  baseImage:\n    description: Disk image to base the Sparkle Appcast on\n    required: true\n  channel:\n    description: Sparkle Appcast channel to use\n    required: false\n    default: stable\n  count:\n    description: Number of old versions to generate deltas for\n    required: false\n    default: '1'\n  urlPrefix:\n    description: URL prefix to use for Sparkle downloads\n    required: true\n  customTitle:\n    description: Custom title to use for Appcast\n    required: false\n  customLink:\n    description: Custom link to use for Appcast\n    required: false\nruns:\n  using: composite\n  steps:\n    - name: Check Runner Operating System 🏃‍♂️\n      if: runner.os != 'macOS'\n      shell: bash\n      run: |\n        : Check Runner Operating System 🏃‍♂️\n        echo '::notice::sparkle-appcast action requires a macOS-based runner.'\n        exit 2\n\n    - name: Install Dependencies\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Install Dependencies\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        print ::group::Install Dependencies\n        brew install --quiet coreutils pandoc\n        print ::endgroup::\n\n    - name: Set Up Sparkle ✨\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Set Up Sparkle ✨\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        local version\n        local base_url\n        local hash\n        IFS=';' read -r version base_url hash <<< \\\n          \"$(jq -r '\n            .configurePresets[]\n            | select(.name==\"dependencies\")\n            | .vendor[\"obsproject.com/obs-studio\"].tools.sparkle\n            | {version, baseUrl, hash}\n            | join(\";\")\n          ' CMakePresets.json)\"\n\n        mkdir -p Sparkle && pushd Sparkle\n        curl -s -L -O \"${base_url}/${version}/Sparkle-${version}.tar.xz\"\n\n        local checksum=\"$(sha256sum Sparkle-${version}.tar.xz | cut -d \" \" -f 1)\"\n\n        if [[ ${hash} != ${checksum} ]] {\n          print \"::error::Sparkle-${version}.tar.xz checksum mismatch: ${checksum} (expected: ${hash})\"\n          exit 2\n        }\n\n        tar -xJf \"Sparkle-${version}.tar.xz\"\n        popd\n\n        mkdir builds\n        mkdir -p output/appcasts/stable\n\n    - name: Download Builds 📥\n      id: builds\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Download Builds 📥\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        pushd builds\n        local image_location=(${{ inputs.baseImage }})\n        hdiutil attach -readonly -noverify -noautoopen -plist ${image_location} > result.plist\n\n        local -i num_entities=$(( $(plutil -extract system-entities raw -- result.plist) - 1 ))\n        local keys\n        local mount_point\n        for i ({0..${num_entities}}) {\n          keys=($(plutil -extract system-entities.${i} raw -- result.plist))\n          if [[ ${keys} == *mount-point* ]] {\n            mount_point=$(plutil -extract system-entities.${i}.mount-point raw -- result.plist)\n            break\n          }\n        }\n\n        local feed_url\n        local info_plist=(${mount_point}/*.app/Contents/Info.plist)\n\n        if [[ -f ${info_plist} ]] {\n          feed_url=$(plutil -extract SUFeedURL raw -- ${info_plist})\n        } else {\n          print '::error:: No Info.plist file found in specified disk image.'\n          hdiutil detach ${mount_point}\n          exit 2\n        }\n\n        print \"feedUrl=${feed_url}\" >> $GITHUB_OUTPUT\n        hdiutil detach ${mount_point}\n\n        curl -s -L -O ${feed_url}\n\n        # The Xpath Xplained:\n        #\n        # //rss/channel/item              - Select every <item> node, under a\n        #                                   <channel> node, under a <rss> node,\n        #                                   which:\n        # [*...]                          - Has a child node, which\n        # [local-name()='channel']        - Has the local name \"channel\"\n        #                                   (required to match the\n        #                                   namespaced sparkle:channel node),\n        #                                   which in turn has\n        # [text()='<channel>']            - A text node that contains the\n        #                                   content of inputs.channel\n        # /enclosure/@url                 - Then select the \"url\" attribute of\n        #                                   every <enclosure> node under\n        #                                   these matching <item> nodes\n\n        local -a artifacts=($(\\\n          xmllint \\\n            -xpath \"//rss/channel/item[*[local-name()='channel'][text()='${{ inputs.channel }}']]/enclosure/@url\" \\\n            ${feed_url:t} \\\n          | sed -n 's/.*url=\"\\(.*\\)\"/\\1/p')\n        )\n\n        local url\n        local file_name\n        for i ({1..${{ inputs.count }}}) {\n          url=\"${artifacts[${i}]}\"\n          file_name=\"${artifacts[${i}]:t}\"\n          curl -s -L -O ${url}\n        }\n\n        mv ${{ inputs.baseImage }} ${PWD}\n        rm -rf - result.plist\n        popd\n\n    - name: Prepare Release Notes 📝\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Prepare Release Notes 📝\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        git tag -l --format='%(contents:subject)' ${GITHUB_REF_NAME} > notes.rst\n        echo '###################################################' >> notes.rst\n        echo '' >> notes.rst\n        git tag -l --format='%(contents:body)' ${GITHUB_REF_NAME} >> notes.rst\n        pandoc -f rst -t html notes.rst -o output/appcasts/notes_${{ inputs.channel }}.html\n\n    - name: Generate Appcast 🎙️\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Generate Appcast 🎙️\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        print -n '${{ inputs.sparklePrivateKey }}' >> eddsa_private.key\n        local feed_url='${{ steps.builds.outputs.feedUrl }}'\n        local arch=${${${(s:_:)feed_url:t}[2]}//x86/x86_64}\n        local sparkle_options=(\n          --verbose\n          --ed-key-file eddsa_private.key\n          --download-url-prefix '${{ inputs.urlPrefix }}/'\n          --full-release-notes-url \"${feed_url//updates_*/notes_${{ inputs.channel }}.html}\"\n          --maximum-versions 0\n          --maximum-deltas '${{ inputs.count }}'\n          --channel '${{ inputs.channel }}'\n        )\n\n        if [[ '${{ inputs.channel }}' == 'stable' ]] {\n          sparkle_options+=(--phased-rollout-interval 86400)\n        }\n\n        Sparkle/bin/generate_appcast \\\n          ${sparkle_options} \\\n          builds\n\n        local -a deltas=(builds/*.delta(N))\n\n        if (( #deltas )) {\n          mkdir -p output/sparkle_deltas/${arch}\n          mv ${deltas} output/sparkle_deltas/${arch}\n        }\n\n        mv builds/*.xml output/appcasts\n\n    - name: Adjust Appcast 🎙️\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Adjust Appcast 🎙️\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        local feed_url='${{ steps.builds.outputs.feedUrl }}'\n        local arch=${${${(s:_:)feed_url:t}[2]}//x86/x86_64}\n        local -a appcasts=(output/appcasts/*_v2.xml)\n        local adjusted\n        for appcast (${appcasts}) {\n          adjusted=\"${appcast//.xml/-adjusted.xml}\"\n          xsltproc \\\n            --stringparam pDeltaUrl \"${{ inputs.urlPrefix }}/sparkle_deltas/${arch}/\" \\\n            --stringparam pSparkleUrl '${{ inputs.urlPrefix }}/' \\\n            --stringparam pCustomTitle '${{ inputs.customTitle }}' \\\n            --stringparam pCustomLink '${{ inputs.customLink }}' \\\n            -o ${adjusted} ${GITHUB_ACTION_PATH}/appcast_adjust.xslt ${appcast}\n\n          xmllint --format ${adjusted} >! ${appcast}\n          rm ${adjusted}\n        }\n\n    - name: Create Legacy Appcast 📟\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Create Legacy Appcast 📟\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        local -a appcasts=(output/appcasts/*_v2.xml)\n        local legacy\n        local feed_url='${{ steps.builds.outputs.feedUrl }}'\n        local arch=${${${(s:_:)feed_url:t}[2]}//x86/x86_64}\n\n        for appcast (${appcasts}) {\n          legacy=\"${appcast//.xml/-legacy.xml}\"\n          xsltproc \\\n            -o ${legacy} ${GITHUB_ACTION_PATH}/appcast_legacy.xslt ${appcast}\n\n          xmllint --format ${legacy} >! output/appcasts/stable/${${appcast:t}//_v2.xml/.xml}\n\n          if [[ ${arch} == x86_64 ]] xmllint --format ${legacy} >! output/appcasts/stable/${${appcast:t}//_x86_64_v2.xml/.xml}\n          rm ${legacy}\n        }\n"
  },
  {
    "path": ".github/actions/sparkle-appcast/appcast_adjust.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet version=\"1.0\"\nxmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\nxmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/sparkle\">\n<xsl:output method=\"xml\" encoding=\"UTF-8\" omit-xml-declaration=\"no\"/>\n<xsl:strip-space elements=\"*\"/>\n\n<!-- Select /rss/channel/title and store it as default value for 'pCustomTitle' -->\n<xsl:param name=\"pCustomTitle\" select=\"/rss/channel/title\" />\n<!-- Select /rss/channel/link and store it as default value for 'pCustomLink' -->\n<xsl:param name=\"pCustomLink\" select=\"/rss/channel/link\" />\n<!-- Set empty strings as default values for pSparkleUrl and pDeltaUrl -->\n<xsl:param name=\"pSparkleUrl\" select=\"''\" />\n<xsl:param name=\"pDeltaUrl\" select=\"''\" />\n\n<!-- XSLT identity rule - copy all nodes and their child nodes as well as attributes\n     (attributes are _not_ descendants of the nodes they belong to).\n     This copy rule is applied as the \"default\" transformation for all nodes.\n-->\n<xsl:template match=\"@* | node()\">\n    <xsl:copy>\n        <xsl:apply-templates select=\"@* | node()\" />\n    </xsl:copy>\n</xsl:template>\n<!-- Match the <title> under <channel> and <rss> and do not translate it\n     (effectively removes it).\n-->\n<xsl:template match=\"/rss/channel/title\" />\n<!-- Match the <link> under <channel> and <rss> and do not translate it\n     (effectively removes it).\n -->\n<xsl:template match=\"/rss/channel/link\" />\n<!-- Match the <channel> under <rss> and apply a copy translation, which\n     * Creates a new <title> element with a text child node and the text content\n       of the pCustomTitle variable\n     * Creates a new <link> element with a text child node and the text content\n       of the pCustomLink variable\n     * Copies all child nodes and attributes of the original <channel> node\n       (this is why <title> and <link> were explicitly not translated before)\n-->\n<xsl:template match=\"/rss/channel\">\n    <xsl:copy>\n        <xsl:element name=\"title\"><xsl:value-of select=\"$pCustomTitle\" /></xsl:element>\n        <xsl:element name=\"link\"><xsl:value-of select=\"$pCustomLink\" /></xsl:element>\n        <xsl:apply-templates select=\"@* | node()\" />\n    </xsl:copy>\n</xsl:template>\n<!-- Match every url attribute of <enclosure> nodes in <sparkle:deltas> nodes\n     (which themselves are under <item>, <channel>, and <rss> nodes respectively).\n\n     Create a new attribute with the name \"url\" and then set its content to either\n     * The original value of the attribute if it starts with the value of the\n       pDeltaUrl variable, OR\n     * The actual value of the pDeltaUrl variable, with the value of the\n       pSparkleUrl variable removed in front of the current url value added after\n\n     This effectively updates every url attribute on a delta <enclosure> node\n     that does not start with the current delta url path.\n-->\n<xsl:template match=\"/rss/channel/item/sparkle:deltas/enclosure/@url\">\n    <xsl:attribute name=\"url\">\n        <xsl:choose>\n            <xsl:when test=\"starts-with(., $pDeltaUrl)\">\n                <xsl:value-of select=\".\" />\n            </xsl:when>\n            <xsl:otherwise>\n                <xsl:value-of select=\"$pDeltaUrl\" />\n                <xsl:value-of select=\"substring-after(., $pSparkleUrl)\" />\n            </xsl:otherwise>\n        </xsl:choose>\n    </xsl:attribute>\n</xsl:template>\n<!-- Match any <sparkle:fullReleaseNotesLink> node under <item>, <channel>,\n     and <rss> respectively, and replace it with a new node named\n     <sparkle:releaseNotesLink> and populate it with all child nodes and\n        attributes of the original node.\n-->\n<xsl:template match=\"/rss/channel/item/sparkle:fullReleaseNotesLink\">\n    <xsl:element name=\"sparkle:releaseNotesLink\"><xsl:apply-templates select=\"@* | node()\" /></xsl:element>\n</xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": ".github/actions/sparkle-appcast/appcast_legacy.xslt",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet version=\"1.0\"\nxmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\nxmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/sparkle\">\n<xsl:output method=\"xml\" encoding=\"UTF-8\" omit-xml-declaration=\"no\"/>\n<xsl:strip-space elements=\"*\"/>\n\n<!-- XSLT identity rule - copy all nodes and their child nodes as well as attributes\n     (attributes are _not_ descendants of the nodes they belong to).\n     This copy rule is applied as the \"default\" transformation for all nodes.\n-->\n<xsl:template match=\"@* | node()\">\n    <xsl:copy>\n        <xsl:apply-templates select=\"@* | node()\" />\n    </xsl:copy>\n</xsl:template>\n<!-- Select every <item> node under a <channel> and <rss> node respectively,\n     which has a <sparkle:channel> child node whose text child node's value\n     is not equal to 'stable', then apply to translation, effectively removing\n     it.\n-->\n<xsl:template match=\"/rss/channel/item[sparkle:channel[text()!='stable']]\" />\n<!-- Select every <sparkle:channel> node under a <item> node which sits under a\n     <channel> and <rss> node respectively, then apply no translation, effectively\n     removing it.\n-->\n<xsl:template match=\"/rss/channel/item/sparkle:channel\" />\n<!-- Select every <sparkle:deltas> node under a <item> node which sits under a\n     <channel> and <rss> node respectively, then apply no translation, effectively\n     removing it.\n-->\n<xsl:template match=\"/rss/channel/item/sparkle:deltas\" />\n</xsl:stylesheet>\n"
  },
  {
    "path": ".github/actions/steam-upload/action.yaml",
    "content": "name: Steam Upload\ndescription: Creates and uploads stable and nightly builds of obs-studio and beta builds (if available)\ninputs:\n  steamSecret:\n    description: Steam auth code\n    required: true\n  steamUser:\n    description: Steam user name\n    required: true\n  steamPassword:\n    description: Steam user password\n    required: true\n  workflowSecret:\n    description: GitHub API token to use for API calls\n    required: true\n  tagName:\n    description: Tag name to use for packaging\n    required: false\n    default: ''\n  stableBranch:\n    description: Name of the stable branch to use\n    required: false\n    default: staging\n  betaBranch:\n    description: Name of the beta branch to use\n    required: false\n    default: beta_staging\n  nightlyBranch:\n    description: Name of the nightly branch to use\n    required: false\n    default: nightly\n  playtestBranch:\n    description: Name of the playtest branch to use\n    required: false\n    default: staging\n  customAssetWindows:\n    description: Custom asset for Windows\n    required: false\n    default: ''\n  customAssetMacOSApple:\n    description: Custom asset for macOS Apple Silicon\n    required: false\n    default: ''\n  customAssetMacOSIntel:\n    description: Custom asset for macOS Intel\n    required: false\n    default: ''\n  preview:\n    description: Enable preview mode (no uploads done)\n    required: false\n    default: ''\nruns:\n  using: composite\n  steps:\n    - name: Check Runner Operating System 🏃‍♂️\n      if: runner.os != 'macOS'\n      shell: bash\n      run: |\n        : Check Runner Operating System 🏃‍♂️\n        echo '::error::steam-upload action requires a macOS-based runner.'\n        exit 2\n\n    - name: Check GitHub Event 🔬\n      if: contains(fromJSON('[\"release\", \"workflow_dispatch\", \"schedule\"]'), github.event_name) != true\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Check GitHub Event 🔬\n        print \"::error:steam-upload action can only be used with 'release', 'workflow-dispatch', or 'schedule' events.\"\n        exit 2\n\n    - name: Download Nightly Assets 🌙\n      id: asset-info-nightly\n      uses: actions/download-artifact@v4\n      if: github.event_name == 'schedule'\n\n    - name: Download Assets 📥\n      id: asset-info\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      env:\n        GH_TOKEN: ${{ inputs.workflowSecret }}\n        windows_custom_asset: ${{ inputs.customAssetWindows }}\n        macos_apple_custom_asset: ${{ inputs.customAssetMacOSApple }}\n        macos_intel_custom_asset: ${{ inputs.customAssetMacOSIntel }}\n      run: |\n        : Download Assets 📥\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        local root_dir=\"${PWD}\"\n        local description\n        local is_prerelease\n\n        case ${GITHUB_EVENT_NAME} {\n          release)\n            gh release download ${{ inputs.tagName }} \\\n              --pattern '*.dmg' \\\n              --pattern '*.zip' \\\n              --clobber\n\n            IFS=';' read -r is_prerelease description <<< \\\n              \"$(gh release view ${{ inputs.tagName }} --json isPrerelease,tagName --jq 'join(\";\")')\"\n            ;;\n          workflow_dispatch)\n            if [[ '${{ inputs.tagName }}' =~ [0-9]+\\.[0-9]+\\.[0-9]+(-(rc|beta)[0-9]+)*$ ]] {\n              gh release download ${{ inputs.tagName }} \\\n                --pattern '*.dmg' \\\n                --pattern '*.zip' \\\n                --clobber\n\n              description='${{ inputs.tagName }}'\n              read -r is_prerelease <<< \\\n                \"$(gh release view ${{ inputs.tagName }} --json isPrerelease --jq '.isPrerelease')\"\n\n              local -A custom_assets=(\n                windows \"Windows x64;${windows_custom_asset}\"\n                macos_apple \"macOS Apple;${macos_apple_custom_asset}\"\n                macos_intel \"macOS Intel;${macos_intel_custom_asset}\"\n              )\n\n              local display_name\n              local url\n              mkdir -p custom_assets && pushd custom_assets\n              for platform (windows macos_apple macos_intel) {\n                IFS=';' read -r display_name url <<< \"${custom_assets[${platform}]}\"\n                if [[ ${url} ]] {\n                  print \"::group::Download of ${display_name} custom asset\"\n                  curl --location --silent --remote-name ${url}\n\n                  if [[ ! -f ${root_dir}/${url:t} ]] {\n                    print \"::warning::Custom asset for ${display_name} does not replace an existing release asset\"\n                  } else {\n                    rm -rf -- ${root_dir}/${url:t}\n                  }\n                  mv ${url:t} ${root_dir}\n                  print '::endgroup::'\n                }\n              }\n              popd\n            } else {\n              print \"::error::Invalid tag name for non-release workflow run: '${{ inputs.tagName }}'.\"\n              exit 2\n            }\n            ;;\n          schedule)\n            local short_hash=\"${GITHUB_SHA:0:9}\"\n            mv obs-studio-windows-x64-${short_hash}/obs-studio-*-windows-x64.zip \\\n              ${root_dir}\n            mv obs-studio-macos-arm64-${short_hash}/obs-studio-*-macos-apple.dmg \\\n              ${root_dir}\n            mv obs-studio-macos-x86_64-${short_hash}/obs-studio-*-macos-intel.dmg \\\n              ${root_dir}\n\n            description=\"g${GITHUB_SHA:0:9}\"\n            is_prerelease='false'\n            ;;\n        }\n\n        print \"description=${description}\" >> $GITHUB_OUTPUT\n        print \"is_prerelease=${is_prerelease}\" >> $GITHUB_OUTPUT\n\n    - name: Prepare Builds for Steam 🍜\n      shell: zsh --no-rcs --errexit --pipefail --extendedglob {0}\n      run: |\n        : Prepare Builds for Steam 🍜\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        local root_dir=\"${PWD}\"\n        mkdir -p steam && pushd steam\n\n        print '::group::Prepare Windows x64 assets'\n        mkdir -p steam-windows && pushd steam-windows\n        unzip ${root_dir}/(#i)obs-studio-*-windows-x64.zip\n        rm ${root_dir}/(#i)obs-studio-*-windows*.zip\n\n        cp -r ${root_dir}/build-aux/steam/scripts_windows scripts\n        touch disable_updater\n        popd\n        print '::endgroup::'\n\n        print '::group::Prepare macOS Apple assets'\n        mkdir -p steam-macos/arm64/OBS.app\n        hdiutil attach -noverify -readonly -noautoopen -mountpoint /Volumes/obs-studio-arm64 ${root_dir}/(#i)obs-studio-*-macos-apple.dmg\n        ditto /Volumes/obs-studio-arm64/OBS.app steam-macos/arm64/OBS.app\n        hdiutil unmount /Volumes/obs-studio-arm64\n        rm ${root_dir}/(#i)obs-studio-*-macos-apple.dmg\n        print '::endgroup::'\n\n        print '::group::Prepare macOS Intel assets'\n        mkdir -p steam-macos/x86_64/OBS.app\n        hdiutil attach -noverify -readonly -noautoopen -mountpoint /Volumes/obs-studio-x86_64 ${root_dir}/(#i)obs-studio-*-macos-intel.dmg\n        ditto /Volumes/obs-studio-x86_64/OBS.app steam-macos/x86_64/OBS.app\n        hdiutil unmount /Volumes/obs-studio-x86_64\n        rm ${root_dir}/(#i)obs-studio-*-macos-intel.dmg\n        print '::endgroup::'\n\n        cp ${root_dir}/build-aux/steam/scripts_macos/launch.sh steam-macos/launch.sh\n\n        popd\n\n    - name: Set Up steamcmd 🚂\n      uses: CyberAndrii/setup-steamcmd@29e114af032a947f5ed57832409070d6e4cbfce3\n\n    - name: Generate Steam auth code 🔐\n      id: steam-totp\n      uses: CyberAndrii/steam-totp@45775c32193801a84d19d94076d72a2ece010948\n      if: ${{ ! fromJSON(inputs.preview) }}\n      with:\n        shared_secret: ${{ inputs.steamSecret }}\n\n    - name: Upload to Steam 📤\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Upload to Steam 📤\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        local root_dir=\"${PWD}\"\n        local build_file='build.vdf'\n        local branch_name\n\n        pushd steam\n        print '::group::Prepare Steam Build Script'\n\n        case ${GITHUB_EVENT_NAME} {\n          schedule) branch_name='${{ inputs.nightlyBranch }}' ;;\n          release|workflow_dispatch)\n            if [[ '${{ steps.asset-info.outputs.is_prerelease }}' == 'true' ]] {\n              branch_name='${{ inputs.betaBranch }}'\n            } else {\n              branch_name='${{ inputs.stableBranch }}'\n            }\n            ;;\n        }\n\n        sed \"s/@@DESC@@/${branch_name}-${{ steps.asset-info.outputs.description }}/;s/@@BRANCH@@/${branch_name}/\" \\\n          ${root_dir}/build-aux/steam/obs_build.vdf > ${build_file}\n\n        print \"Generated ${build_file}:\\n$(<${build_file})\"\n        print '::endgroup::'\n\n        print '::group::Upload to Steam'\n        local preview='${{ inputs.preview }}'\n\n        steamcmd \\\n          +login '${{ inputs.steamUser }}' '${{ inputs.steamPassword }}' '${{ steps.steam-totp.outputs.code }}' \\\n          +run_app_build ${preview:+-preview} ${build_file:a} \\\n          +quit\n        print '::endgroup'\n        popd\n\n    - name: Generate Steam auth code 🔐\n      id: steam-totp-playtest\n      uses: CyberAndrii/steam-totp@45775c32193801a84d19d94076d72a2ece010948\n      if: ${{ ! fromJSON(inputs.preview) && fromJSON(steps.asset-info.outputs.is_prerelease) }}\n      with:\n        shared_secret: ${{ inputs.steamSecret }}\n\n    - name: Upload to Steam (Playtest) 📤\n      if: fromJSON(steps.asset-info.outputs.is_prerelease)\n      shell: zsh --no-rcs --errexit --pipefail {0}\n      run: |\n        : Upload to Steam Playtest 📤\n        if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n        local root_dir=\"${PWD}\"\n        local build_file='build_playtest.vdf'\n        local branch_name='${{ inputs.playtestBranch }}'\n\n        pushd steam\n        print '::group::Prepare Steam Build Script'\n\n        sed \"s/@@DESC@@/${branch_name}-${{ steps.asset-info.outputs.description }}/;s/@@BRANCH@@/${branch_name}/\" \\\n          ${root_dir}/build-aux/steam/obs_playtest_build.vdf > ${build_file}\n\n        print \"Generated ${build_file}:\\n$(<${build_file})\"\n        print '::endgroup::'\n\n        print '::group::Upload to Steam'\n        local preview\n        if [[ '${{ inputs.preview }}' == 'true' ]] preview='-preview'\n\n        steamcmd \\\n          +login '${{ inputs.steamUser }}' '${{ inputs.steamPassword }}' '${{ steps.steam-totp-playtest.outputs.code }}' \\\n          +run_app_build ${preview} ${build_file:a} \\\n          +quit\n        print '::endgroup'\n        popd\n\n    - name: Upload Steam build logs\n      uses: actions/upload-artifact@v4\n      with:\n        name: steam-build-logs\n        path: ${{ github.workspace }}/steam/build/*.log\n"
  },
  {
    "path": ".github/actions/windows-analysis/Invoke-External.ps1",
    "content": "function Invoke-External {\n    <#\n        .SYNOPSIS\n            Invokes a non-PowerShell command.\n        .DESCRIPTION\n            Runs a non-PowerShell command, and captures its return code.\n            Throws an exception if the command returns non-zero.\n        .EXAMPLE\n            Invoke-External 7z x $MyArchive\n    #>\n\n    if ( $args.Count -eq 0 ) {\n        throw 'Invoke-External called without arguments.'\n    }\n\n    if ( ! ( Test-Path function:Log-Information ) ) {\n        . $PSScriptRoot/Logger.ps1\n    }\n\n    $Command = $args[0]\n    $CommandArgs = @()\n\n    if ( $args.Count -gt 1) {\n        $CommandArgs = $args[1..($args.Count - 1)]\n    }\n\n    $_EAP = $ErrorActionPreference\n    $ErrorActionPreference = \"Continue\"\n\n    Log-Debug \"Invoke-External: ${Command} ${CommandArgs}\"\n\n    & $command $commandArgs\n    $Result = $LASTEXITCODE\n\n    $ErrorActionPreference = $_EAP\n\n    if ( $Result -ne 0 ) {\n        throw \"${Command} ${CommandArgs} exited with non-zero code ${Result}.\"\n    }\n}\n"
  },
  {
    "path": ".github/actions/windows-analysis/Logger.ps1",
    "content": "function Log-Debug {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Debug $m\n        }\n    }\n}\n\nfunction Log-Verbose {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Verbose $m\n        }\n    }\n}\n\nfunction Log-Warning {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Warning $m\n        }\n    }\n}\n\nfunction Log-Error {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Error $m\n        }\n    }\n}\n\nfunction Log-Information {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })\n            $Icon = ' =>'\n\n            foreach($m in $Message) {\n                Write-Host -NoNewLine -ForegroundColor Blue \"  ${StageName} $($Icon.PadRight(5)) \"\n                Write-Host \"${m}\"\n            }\n        }\n    }\n}\n\nfunction Log-Group {\n    [CmdletBinding()]\n    param(\n        [Parameter(ValueFromPipeline)]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( $Env:CI -ne $null )  {\n            if ( $script:LogGroup ) {\n                Write-Output '::endgroup::'\n                $script:LogGroup = $false\n            }\n\n            if ( $Message.count -ge 1 ) {\n                Write-Output \"::group::$($Message -join ' ')\"\n                $script:LogGroup = $true\n            }\n        } else {\n            if ( $Message.count -ge 1 ) {\n                Log-Information $Message\n            }\n        }\n    }\n}\n\nfunction Log-Status {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' })\n            $Icon = '  >'\n\n            foreach($m in $Message) {\n                Write-Host -NoNewLine -ForegroundColor Green \"  ${StageName} $($Icon.PadRight(5)) \"\n                Write-Host \"${m}\"\n            }\n        }\n    }\n}\n\nfunction Log-Output {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })\n            $Icon = ''\n\n            foreach($m in $Message) {\n                Write-Output \"  ${StageName} $($Icon.PadRight(5)) ${m}\"\n            }\n        }\n    }\n}\n\n$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5\n"
  },
  {
    "path": ".github/actions/windows-analysis/action.yaml",
    "content": "name: Run PVS-Studio Analysis\ninputs:\n  pvsUsername:\n    description: PVS-Studio License Username\n    required: true\n  pvsKey:\n    description: PVS-Studio License Key\n    required: true\n  target:\n    description: Build Target\n    required: true\n  config:\n    description: Build Configuration\n    required: true\nruns:\n  using: composite\n  steps:\n    - name: Setup PVS-Studio\n      shell: pwsh\n      run: |\n        choco install pvs-studio --version=7.30.81185.980 -y --no-progress\n\n    - name: Activate PVS-Studio\n      shell: pwsh\n      run: |\n        . ${env:GITHUB_ACTION_PATH}\\Invoke-External.ps1\n        Invoke-External \"C:\\Program Files (x86)\\PVS-Studio\\PVS-Studio_Cmd.exe\" credentials -u ${{ inputs.pvsUsername }} -n ${{ inputs.pvsKey }}\n\n    - name: Run PVS-Studio Analysis\n      shell: pwsh\n      run: |\n        [flags()] Enum PVSErrorCodes {\n            Success = 0\n            AnalyzerCrash = 1\n            GenericError = 2\n            InvalidCommandLine = 4\n            FileNotFound = 8\n            ConfigurationNotFound = 16\n            InvalidProject = 32\n            InvalidExtension = 64\n            LicenseInvalid = 128\n            CodeErrorsFound = 256\n            SuppressionFailed = 512\n            LicenseExpiringSoon = 1024\n        }\n        \n        $pvsParams = @(\n            \"--progress\"\n            \"--disableLicenseExpirationCheck\"\n            \"-p\", \"${{ inputs.target }}\"\n            \"-c\", \"${{ inputs.config }}\"\n            \"-t\", \"${{ github.workspace }}\\build_x64\\obs-studio.sln\"\n            \"-o\", \"${{ github.workspace }}\\analysis.plog\"\n            \"-C\", \"${env:GITHUB_ACTION_PATH}\\obs.pvsconfig\"\n        )\n        & \"C:\\Program Files (x86)\\PVS-Studio\\PVS-Studio_Cmd.exe\" @pvsParams\n        \n        # Success, LicenseExpiringSoon, and CodeErrorsFound are fine as error codes, we only care if it is anything but those\n        $pvs_result = $LASTEXITCODE -band (-bnot ([PVSErrorCodes]::CodeErrorsFound -bor [PVSErrorCodes]::LicenseExpiringSoon))\n        if ($pvs_result -ne 0) {\n            Write-Output \"PVS-Studio Errors: $([PVSErrorCodes]$pvs_result)\"\n        }\n        exit $pvs_result\n\n    - name: Convert Analysis to SARIF\n      shell: pwsh\n      run: |\n        . ${env:GITHUB_ACTION_PATH}\\Invoke-External.ps1\n        \n        $conversionParams = @(\n            \"-a\", \"GA:1,2\",\n            \"-d\", \"V1042,Renew\"\n            \"-t\", \"Sarif\"\n            \"${{ github.workspace }}\\analysis.plog\"\n        )\n        Invoke-External \"C:\\Program Files (x86)\\PVS-Studio\\PlogConverter.exe\" @conversionParams\n\n    - name: Upload PVS-Studio Logs\n      uses: actions/upload-artifact@v4\n      with:\n        name: 'pvs-analysis-log'\n        path: |\n          ${{ github.workspace }}/analysis.plog\n          ${{ github.workspace }}/analysis.plog.sarif\n\n    - name: Upload PVS-Studio Report\n      uses: github/codeql-action/upload-sarif@v3\n      with:\n        sarif_file: \"${{ github.workspace }}/analysis.plog.sarif\"\n        category: 'PVS-Studio (Windows)'\n"
  },
  {
    "path": ".github/actions/windows-analysis/obs.pvsconfig",
    "content": "//V_EXCLUDE_PATH */.deps/*\n//V_EXCLUDE_PATH */blake2/*\n//V_EXCLUDE_PATH */json11/*\n//V_EXCLUDE_PATH */simde/*\n//V_EXCLUDE_PATH */w32-pthreads/*\n//V_EXCLUDE_PATH */plugins/obs-browser/*\n//V_EXCLUDE_PATH */plugins/obs-outputs/ftl-sdk/*\n//V_EXCLUDE_PATH */plugins/obs-websocket/*\n//V_EXCLUDE_PATH */plugins/win-dshow/libdshowcapture/*\n//V_EXCLUDE_PATH *_autogen/*\n"
  },
  {
    "path": ".github/actions/windows-patches/Ensure-Location.ps1",
    "content": "function Ensure-Location {\n    <#\n        .SYNOPSIS\n            Ensures current location to be set to specified directory.\n        .DESCRIPTION\n            If specified directory exists, switch to it. Otherwise create it,\n            then switch.\n        .EXAMPLE\n            Ensure-Location \"My-Directory\"\n            Ensure-Location -Path \"Path-To-My-Directory\"\n    #>\n\n    param(\n        [Parameter(Mandatory)]\n        [string] $Path\n    )\n\n    if ( ! ( Test-Path $Path ) ) {\n        $_Params = @{\n            ItemType = \"Directory\"\n            Path = ${Path}\n            ErrorAction = \"SilentlyContinue\"\n        }\n\n        New-Item @_Params | Set-Location\n    } else {\n        Set-Location -Path ${Path}\n    }\n}\n"
  },
  {
    "path": ".github/actions/windows-patches/Invoke-External.ps1",
    "content": "function Invoke-External {\n    <#\n        .SYNOPSIS\n            Invokes a non-PowerShell command.\n        .DESCRIPTION\n            Runs a non-PowerShell command, and captures its return code.\n            Throws an exception if the command returns non-zero.\n        .EXAMPLE\n            Invoke-External 7z x $MyArchive\n    #>\n\n    if ( $args.Count -eq 0 ) {\n        throw 'Invoke-External called without arguments.'\n    }\n\n    if ( ! ( Test-Path function:Log-Information ) ) {\n        . $PSScriptRoot/Logger.ps1\n    }\n\n    $Command = $args[0]\n    $CommandArgs = @()\n\n    if ( $args.Count -gt 1) {\n        $CommandArgs = $args[1..($args.Count - 1)]\n    }\n\n    $_EAP = $ErrorActionPreference\n    $ErrorActionPreference = \"Continue\"\n\n    Log-Debug \"Invoke-External: ${Command} ${CommandArgs}\"\n\n    & $command $commandArgs\n    $Result = $LASTEXITCODE\n\n    $ErrorActionPreference = $_EAP\n\n    if ( $Result -ne 0 ) {\n        throw \"${Command} ${CommandArgs} exited with non-zero code ${Result}.\"\n    }\n}\n"
  },
  {
    "path": ".github/actions/windows-patches/Logger.ps1",
    "content": "function Log-Debug {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Debug $m\n        }\n    }\n}\n\nfunction Log-Verbose {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Verbose $m\n        }\n    }\n}\n\nfunction Log-Warning {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Warning $m\n        }\n    }\n}\n\nfunction Log-Error {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Error $m\n        }\n    }\n}\n\nfunction Log-Information {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })\n            $Icon = ' =>'\n\n            foreach($m in $Message) {\n                Write-Host -NoNewLine -ForegroundColor Blue \"  ${StageName} $($Icon.PadRight(5)) \"\n                Write-Host \"${m}\"\n            }\n        }\n    }\n}\n\nfunction Log-Group {\n    [CmdletBinding()]\n    param(\n        [Parameter(ValueFromPipeline)]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( $Env:CI -ne $null )  {\n            if ( $script:LogGroup ) {\n                Write-Output '::endgroup::'\n                $script:LogGroup = $false\n            }\n\n            if ( $Message.count -ge 1 ) {\n                Write-Output \"::group::$($Message -join ' ')\"\n                $script:LogGroup = $true\n            }\n        } else {\n            if ( $Message.count -ge 1 ) {\n                Log-Information $Message\n            }\n        }\n    }\n}\n\nfunction Log-Status {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' })\n            $Icon = '  >'\n\n            foreach($m in $Message) {\n                Write-Host -NoNewLine -ForegroundColor Green \"  ${StageName} $($Icon.PadRight(5)) \"\n                Write-Host \"${m}\"\n            }\n        }\n    }\n}\n\nfunction Log-Output {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })\n            $Icon = ''\n\n            foreach($m in $Message) {\n                Write-Output \"  ${StageName} $($Icon.PadRight(5)) ${m}\"\n            }\n        }\n    }\n}\n\n$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5\n"
  },
  {
    "path": ".github/actions/windows-patches/action.yaml",
    "content": "name: Run bouf Patch Generation\ndescription: Generates OBS updater manifest and patches\ninputs:\n  gcsAccessKeyId:\n    description: GCS S3 Access Key ID\n    required: true\n  gcsAccessKeySecret:\n    description: GCS S3 Access Key Secret\n    required: true\n  workflowSecret:\n    description: GitHub API token to use for API calls\n    required: true\n  tagName:\n    description: GitHub Release tag\n    required: true\n  channel:\n    description: Update channel\n    required: false\n    default: 'stable'\n  architecture:\n    description: OBS build architecture\n    required: false\n    default: 'x64'\n\nruns:\n  using: composite\n  steps:\n    - uses: actions/checkout@v4\n      with:\n        path: \"repo\"\n        fetch-depth: 0\n        ref: ${{ inputs.tagName }}\n\n    - name: Download Release Artifact\n      shell: pwsh\n      env:\n        GH_TOKEN: ${{ inputs.workflowSecret }}\n      run: |\n        # Download OBS release\n        . ${env:GITHUB_ACTION_PATH}\\Invoke-External.ps1\n        Invoke-External gh release download \"${{ inputs.tagName }}\" -p \"OBS-Studio-${{ inputs.tagName }}-Windows-${{ inputs.architecture }}.zip\"\n        Invoke-External gh attestation verify \"OBS-Studio-${{ inputs.tagName }}-Windows-${{ inputs.architecture }}.zip\" --owner obsproject\n        Expand-Archive -Path \"OBS-Studio-${{ inputs.tagName }}-Windows-${{ inputs.architecture }}.zip\" -DestinationPath \"${{ github.workspace }}/build\"\n\n    - name: Setup bouf\n      shell: pwsh\n      env:\n        BOUF_TAG: 'v0.6.5'\n        BOUF_HASH: '2e16d5116415579b5dd8cd68b3fe6edaa7513938427567121942f592292605d5'\n        BOUF_NSIS_HASH: 'e323043627cfeebd237d306afc001c8c36a3c37f6ecef2b349a19a3209ae3450'\n        GH_TOKEN: ${{ github.token }}\n      run: |\n        # Download bouf release\n        . ${env:GITHUB_ACTION_PATH}\\Ensure-Location.ps1\n        . ${env:GITHUB_ACTION_PATH}\\Invoke-External.ps1\n        Ensure-Location bouf\n        $windows_zip = \"bouf-windows-${env:BOUF_TAG}.zip\"\n        $nsis_zip = \"bouf-nsis-${env:BOUF_TAG}.zip\"\n        Invoke-External gh release download \"${env:BOUF_TAG}\" -R \"obsproject/bouf\" -p $windows_zip -p $nsis_zip\n        \n        if ((Get-FileHash $windows_zip -Algorithm SHA256).Hash -ne \"${env:BOUF_HASH}\") {\n          throw \"bouf hash does not match.\"\n        }\n        if ((Get-FileHash $nsis_zip -Algorithm SHA256).Hash -ne \"${env:BOUF_NSIS_HASH}\") {\n          throw \"NSIS package hash does not match.\"\n        }\n        \n        Expand-Archive -Path $windows_zip -DestinationPath bin\n        Expand-Archive -Path $nsis_zip -DestinationPath nsis\n\n    - name: Install rclone and pandoc\n      shell: pwsh\n      run: |\n        choco install rclone pandoc -y --no-progress\n\n    - name: Download Previous Builds\n      shell: pwsh\n      env:\n        RCLONE_TRANSFERS: '100'\n        RCLONE_FAST_LIST: 'true'\n        RCLONE_EXCLUDE: '\"{pdbs/**,**/${{ inputs.tagName }}/**}\"'\n        RCLONE_S3_PROVIDER: 'GCS'\n        RCLONE_S3_ACCESS_KEY_ID: '${{ inputs.gcsAccessKeyId }}'\n        RCLONE_S3_SECRET_ACCESS_KEY: '${{ inputs.gcsAccessKeySecret }}'\n        RCLONE_S3_ENDPOINT: 'https://storage.googleapis.com'\n      run: |\n        rclone -q copy \":s3:obs-builds/${{ inputs.architecture }}\" \"${{ github.workspace }}/old_builds\"\n\n    - name: Prepare Release Notes\n      shell: pwsh\n      run: |\n        # Release notes are just the tag body on Windows\n        Set-Location repo\n        git tag -l --format='%(contents:subject)' ${{ inputs.tagName }} > \"${{ github.workspace }}/notes.rst\"\n        Write-Output \"###################################################\" >> \"${{ github.workspace }}/notes.rst\"\n        Write-Output \"\" >> \"${{ github.workspace }}/notes.rst\"\n        git tag -l --format='%(contents:body)' ${{ inputs.tagName }} >> \"${{ github.workspace }}/notes.rst\"\n\n    - name: Run bouf\n      shell: pwsh\n      run: |\n        . ${env:GITHUB_ACTION_PATH}\\Invoke-External.ps1\n        $boufArgs = @(\n           \"--config\",     \"${env:GITHUB_ACTION_PATH}/config.toml\",\n           \"--version\",    \"${{ inputs.tagName }}\"\n           \"--branch\",     \"${{ inputs.channel }}\"\n           \"--notes-file\", \"${{ github.workspace }}/notes.rst\"\n           \"-i\",           \"${{ github.workspace }}/build\"\n           \"-p\",           \"${{ github.workspace }}/old_builds\"\n           \"-o\",           \"${{ github.workspace }}/output\"\n           \"--updater-data-only\"\n        )\n        Invoke-External \"${{ github.workspace }}\\bouf\\bin\\bouf.exe\" @boufArgs\n\n    - name: Upload Outputs\n      uses: actions/upload-artifact@v4\n      with:\n        name: windows-updater-files\n        compression-level: 0\n        path: ${{ github.workspace }}/output\n"
  },
  {
    "path": ".github/actions/windows-patches/config.toml",
    "content": "[general]\nlog_level = \"trace\"\n\n[env]\n# On CI these should be in %PATH%\nsevenzip_path = \"7z\"\nmakensis_path = \"makensis\"\npandoc_path = \"pandoc\"\npdbcopy_path = \"C:/Program Files (x86)/Windows Kits/10/Debuggers/x64/pdbcopy.exe\"\n\n[prepare.codesign]\nskip_sign = true\n\n[generate]\npatch_type = \"zstd\"\ncompress_files = true\n\n# Prevent some legacy plugins from being marked as deleted\nexclude_from_removal = [\n    \"enc-amf\",\n    \"win-mf\",\n]\n\n[package]\n\n[package.installer]\nskip = true\n\n[package.updater]\nvc_redist_path = \"bouf/nsis/VC_redist.x64.exe\"\nskip_sign = true\n"
  },
  {
    "path": ".github/actions/windows-signing/Ensure-Location.ps1",
    "content": "function Ensure-Location {\n    <#\n        .SYNOPSIS\n            Ensures current location to be set to specified directory.\n        .DESCRIPTION\n            If specified directory exists, switch to it. Otherwise create it,\n            then switch.\n        .EXAMPLE\n            Ensure-Location \"My-Directory\"\n            Ensure-Location -Path \"Path-To-My-Directory\"\n    #>\n\n    param(\n        [Parameter(Mandatory)]\n        [string] $Path\n    )\n\n    if ( ! ( Test-Path $Path ) ) {\n        $_Params = @{\n            ItemType = \"Directory\"\n            Path = ${Path}\n            ErrorAction = \"SilentlyContinue\"\n        }\n\n        New-Item @_Params | Set-Location\n    } else {\n        Set-Location -Path ${Path}\n    }\n}\n"
  },
  {
    "path": ".github/actions/windows-signing/Invoke-External.ps1",
    "content": "function Invoke-External {\n    <#\n        .SYNOPSIS\n            Invokes a non-PowerShell command.\n        .DESCRIPTION\n            Runs a non-PowerShell command, and captures its return code.\n            Throws an exception if the command returns non-zero.\n        .EXAMPLE\n            Invoke-External 7z x $MyArchive\n    #>\n\n    if ( $args.Count -eq 0 ) {\n        throw 'Invoke-External called without arguments.'\n    }\n\n    if ( ! ( Test-Path function:Log-Information ) ) {\n        . $PSScriptRoot/Logger.ps1\n    }\n\n    $Command = $args[0]\n    $CommandArgs = @()\n\n    if ( $args.Count -gt 1) {\n        $CommandArgs = $args[1..($args.Count - 1)]\n    }\n\n    $_EAP = $ErrorActionPreference\n    $ErrorActionPreference = \"Continue\"\n\n    Log-Debug \"Invoke-External: ${Command} ${CommandArgs}\"\n\n    & $command $commandArgs\n    $Result = $LASTEXITCODE\n\n    $ErrorActionPreference = $_EAP\n\n    if ( $Result -ne 0 ) {\n        throw \"${Command} ${CommandArgs} exited with non-zero code ${Result}.\"\n    }\n}\n"
  },
  {
    "path": ".github/actions/windows-signing/Logger.ps1",
    "content": "function Log-Debug {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Debug $m\n        }\n    }\n}\n\nfunction Log-Verbose {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Verbose $m\n        }\n    }\n}\n\nfunction Log-Warning {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Warning $m\n        }\n    }\n}\n\nfunction Log-Error {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Error $m\n        }\n    }\n}\n\nfunction Log-Information {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })\n            $Icon = ' =>'\n\n            foreach($m in $Message) {\n                Write-Host -NoNewLine -ForegroundColor Blue \"  ${StageName} $($Icon.PadRight(5)) \"\n                Write-Host \"${m}\"\n            }\n        }\n    }\n}\n\nfunction Log-Group {\n    [CmdletBinding()]\n    param(\n        [Parameter(ValueFromPipeline)]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( $Env:CI -ne $null )  {\n            if ( $script:LogGroup ) {\n                Write-Output '::endgroup::'\n                $script:LogGroup = $false\n            }\n\n            if ( $Message.count -ge 1 ) {\n                Write-Output \"::group::$($Message -join ' ')\"\n                $script:LogGroup = $true\n            }\n        } else {\n            if ( $Message.count -ge 1 ) {\n                Log-Information $Message\n            }\n        }\n    }\n}\n\nfunction Log-Status {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' })\n            $Icon = '  >'\n\n            foreach($m in $Message) {\n                Write-Host -NoNewLine -ForegroundColor Green \"  ${StageName} $($Icon.PadRight(5)) \"\n                Write-Host \"${m}\"\n            }\n        }\n    }\n}\n\nfunction Log-Output {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })\n            $Icon = ''\n\n            foreach($m in $Message) {\n                Write-Output \"  ${StageName} $($Icon.PadRight(5)) ${m}\"\n            }\n        }\n    }\n}\n\n$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5\n"
  },
  {
    "path": ".github/actions/windows-signing/action.yaml",
    "content": "name: Run bouf Packaging\ndescription: Generates signed OBS install files\ninputs:\n  gcpWorkloadIdentityProvider:\n    description: GCP Identity Provider Pool ID\n    required: true\n  gcpServiceAccountName:\n    description: Google service account name\n    required: true\n  version:\n    description: Version string (e.g., 30.0.0-rc1)\n    required: true\n  channel:\n    description: Update channel\n    required: false\n    default: 'stable'\n  architecture:\n    description: OBS build architecture\n    required: false\n    default: 'x64'\n\nruns:\n  using: composite\n  steps:\n    - name: Extract Artifact\n      shell: pwsh\n      run: |\n        Expand-Archive -Path build\\*.zip -DestinationPath build\n        Remove-Item build\\*.zip\n\n    - name: Setup bouf\n      shell: pwsh\n      env:\n        BOUF_TAG: 'v0.6.5'\n        BOUF_HASH: '2e16d5116415579b5dd8cd68b3fe6edaa7513938427567121942f592292605d5'\n        BOUF_NSIS_HASH: 'e323043627cfeebd237d306afc001c8c36a3c37f6ecef2b349a19a3209ae3450'\n        GH_TOKEN: ${{ github.token }}\n      run: |\n        # Download bouf release\n        . ${env:GITHUB_ACTION_PATH}\\Ensure-Location.ps1\n        . ${env:GITHUB_ACTION_PATH}\\Invoke-External.ps1\n        Ensure-Location bouf\n        $windows_zip = \"bouf-windows-${env:BOUF_TAG}.zip\"\n        $nsis_zip = \"bouf-nsis-${env:BOUF_TAG}.zip\"\n        Invoke-External gh release download \"${env:BOUF_TAG}\" -R \"obsproject/bouf\" -p $windows_zip -p $nsis_zip\n        \n        if ((Get-FileHash $windows_zip -Algorithm SHA256).Hash -ne \"${env:BOUF_HASH}\") {\n          throw \"bouf hash does not match.\"\n        }\n        if ((Get-FileHash $nsis_zip -Algorithm SHA256).Hash -ne \"${env:BOUF_NSIS_HASH}\") {\n          throw \"NSIS package hash does not match.\"\n        }\n        \n        Expand-Archive -Path $windows_zip -DestinationPath bin\n        Expand-Archive -Path $nsis_zip -DestinationPath nsis\n\n    - name: Setup Google CNG Provider\n      shell: pwsh\n      env:\n        CNG_TAG: 'cng-v1.1'\n        GH_TOKEN: ${{ github.token }}\n      run: |\n        # Download Google CNG provider release from github\n        . ${env:GITHUB_ACTION_PATH}\\Ensure-Location.ps1\n        . ${env:GITHUB_ACTION_PATH}\\Invoke-External.ps1\n        Ensure-Location gcng\n        \n        Invoke-External gh release download \"${env:CNG_TAG}\" -R \"GoogleCloudPlatform/kms-integrations\" -p \"*amd64.zip\"\n        Expand-Archive -Path *.zip\n        $sigPath = Get-ChildItem *.sig -Recurse\n        $msiPath = Get-ChildItem *.msi -Recurse\n        # Verify digital signature against Google's public key\n        Invoke-External openssl dgst -sha384 -verify \"${env:GITHUB_ACTION_PATH}/cng-release-signing-key.pem\" -signature $sigPath $msiPath\n        # Finally, install the CNG provider\n        Invoke-External msiexec /i $msiPath /qn /norestart\n\n    - name: Install rclone\n      shell: pwsh\n      run: |\n        choco install rclone --version=1.69.3 -y --no-progress\n    \n    - name: 'Authenticate to Google Cloud'\n      uses: 'google-github-actions/auth@5a50e581162a13f4baa8916d01180d2acbc04363'\n      with:\n        workload_identity_provider: ${{ inputs.gcpWorkloadIdentityProvider }}\n        service_account: ${{ inputs.gcpServiceAccountName }}\n\n    - name: Download Previous Build\n      shell: pwsh\n      env:\n        RCLONE_GCS_ENV_AUTH: 'true'\n      run: |\n        . ${env:GITHUB_ACTION_PATH}\\Ensure-Location.ps1\n        Ensure-Location \"${{ github.workspace }}/old_builds\"\n        rclone copy --transfers 100 \":gcs:obs-latest/${{ inputs.architecture }}/${{ inputs.channel }}\" .\n\n    - name: Sign Game Capture with RSA cert\n      shell: pwsh\n      run: |\n        . ${env:GITHUB_ACTION_PATH}\\Invoke-External.ps1\n        $SignToolExe = \"C:\\Program Files (x86)\\Windows Kits\\10\\App Certification Kit\\signtool.exe\"\n        $signArgs = @(\n           \"sign\"\n           \"/fd\",   \"sha256\"\n           \"/t\",    \"http://timestamp.digicert.com\"\n           \"/f\",    \"repo/.github/actions/windows-signing/prod-gc.crt\"\n           \"/csp\",  \"Google Cloud KMS Provider\"\n           \"/kc\",   \"projects/ci-signing/locations/global/keyRings/production/cryptoKeys/game-capture-release-sign-hsm/cryptoKeyVersions/1\"\n           \"${{ github.workspace }}/build/data/obs-plugins/win-capture/*.dll\"\n        )\n        Invoke-External $SignToolExe @signArgs\n\n    - name: Run bouf\n      shell: pwsh\n      run: |\n        . ${env:GITHUB_ACTION_PATH}\\Invoke-External.ps1\n        $boufArgs = @(\n           \"--config\",     \"${env:GITHUB_ACTION_PATH}/config_${{ inputs.architecture }}.toml\"\n           \"--version\",    \"${{ inputs.version }}\"\n           \"--branch\",     \"${{ inputs.channel }}\"\n           \"-i\",           \"${{ github.workspace }}/build\"\n           \"-p\",           \"${{ github.workspace }}/old_builds\"\n           \"-o\",           \"${{ github.workspace }}/output\"\n           \"--packaging-only\"\n        )\n        Invoke-External \"${{ github.workspace }}\\bouf\\bin\\bouf.exe\" @boufArgs\n\n    - name: Sync Latest Build\n      shell: pwsh\n      env:\n        RCLONE_INCLUDE: '**/${{ inputs.version }}/**'\n        RCLONE_GCS_ENV_AUTH: 'true'\n        RCLONE_GCS_BUCKET_POLICY_ONLY: 'true'\n      run: |\n        rclone sync --delete-excluded --transfers 100 \"${{ github.workspace }}/old_builds\" \":gcs:obs-latest/${{ inputs.architecture }}/${{ inputs.channel }}\"\n\n    - name: Upload Build to Archive\n      shell: pwsh\n      env:\n        RCLONE_GCS_ENV_AUTH: 'true'\n        RCLONE_GCS_BUCKET_POLICY_ONLY: 'true'\n      run: |\n        rclone copy --transfers 100 \"${{ github.workspace }}/old_builds\" \":gcs:obs-builds/${{ inputs.architecture }}\"\n"
  },
  {
    "path": ".github/actions/windows-signing/cng-release-signing-key.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEtfLbXkHUVc9oUPTNyaEK3hIwmuGRoTtd\n6zDhwqjJuYaMwNd1aaFQLMawTwZgR0Xn27ymVWtqJHBe0FU9BPIQ+SFmKw+9jSwu\n/FuqbJnLmTnWMJ1jRCtyHNZawvv2wbiB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": ".github/actions/windows-signing/config_arm64.toml",
    "content": "[general]\nlog_level = \"trace\"\n\n[env]\n# On CI these should be in %PATH%\nsevenzip_path = \"7z\"\nmakensis_path = \"makensis\"\npandoc_path = \"pandoc\"\npdbcopy_path = \"C:/Program Files (x86)/Windows Kits/10/Debuggers/x64/pdbcopy.exe\"\n\n## Preparation steps\n[prepare]\n\n[prepare.copy]\nnever_copy = [\n    \"bin/32bit\",\n    \"obs-plugins/32bit\",\n    \".keepme\",\n]\n\n[prepare.codesign]\nsign_cert_file = \"repo/.github/actions/windows-signing/prod.crt\"\nsign_kms_key_id = \"projects/ci-signing/locations/global/keyRings/production/cryptoKeys/release-sign-hsm/cryptoKeyVersions/1\"\nsign_digest = \"sha384\"\nsign_ts_serv = \"http://timestamp.digicert.com\"\nsign_ts_algo = \"sha256\"\nsign_exts = ['exe', 'dll', 'pyd']\nsign_append = true\n\n[prepare.strip_pdbs]\n# PDBs to not strip\nexclude = [\n    \"obs-frontend-api.pdb\",\n    \"obs64.pdb\",\n    \"obs.pdb\",\n]\n\n[package]\n[package.installer]\nskip = true\n\n[package.zip]\nname = \"OBS-Studio-{version}-arm64.zip\"\npdb_name = \"OBS-Studio-{version}-arm64-pdbs.zip\"\n\n[package.updater]\nskip_sign = true\n"
  },
  {
    "path": ".github/actions/windows-signing/config_x64.toml",
    "content": "[general]\nlog_level = \"trace\"\n\n[env]\n# On CI these should be in %PATH%\nsevenzip_path = \"7z\"\nmakensis_path = \"makensis\"\npandoc_path = \"pandoc\"\npdbcopy_path = \"C:/Program Files (x86)/Windows Kits/10/Debuggers/x64/pdbcopy.exe\"\n\n## Preparation steps\n[prepare]\n\n[prepare.copy]\nnever_copy = [\n    \"bin/32bit\",\n    \"obs-plugins/32bit\",\n    \".keepme\",\n]\n\n[prepare.codesign]\nsign_cert_file = \"repo/.github/actions/windows-signing/prod.crt\"\nsign_kms_key_id = \"projects/ci-signing/locations/global/keyRings/production/cryptoKeys/release-sign-hsm/cryptoKeyVersions/1\"\nsign_digest = \"sha384\"\nsign_ts_serv = \"http://timestamp.digicert.com\"\nsign_ts_algo = \"sha256\"\nsign_exts = ['exe', 'dll', 'pyd']\nsign_append = true\n\n[prepare.strip_pdbs]\n# PDBs to not strip\nexclude = [\n    \"obs-frontend-api.pdb\",\n    \"obs64.pdb\",\n    \"obs.pdb\",\n]\n\n[package]\n[package.installer]\nnsis_script = \"bouf/nsis/mp-installer.nsi\"\n\n[package.zip]\nname = \"OBS-Studio-{version}-x64.zip\"\npdb_name = \"OBS-Studio-{version}-pdbs.zip\"\n\n[package.updater]\nskip_sign = true\n"
  },
  {
    "path": ".github/actions/windows-signing/prod-gc.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIHYDCCBUigAwIBAgIQCTRoMeK9NItAQamXbU8PnjANBgkqhkiG9w0BAQsFADBp\nMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMT\nOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0\nIDIwMjEgQ0ExMB4XDTI1MTExNzAwMDAwMFoXDTI4MDExNTIzNTk1OVowaDELMAkG\nA1UEBhMCVVMxEDAOBgNVBAgTB1d5b21pbmcxETAPBgNVBAcTCFNoZXJpZGFuMRkw\nFwYDVQQKExBPQlMgUHJvamVjdCwgTExDMRkwFwYDVQQDExBPQlMgUHJvamVjdCwg\nTExDMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA07e66QJeFjyk8p5l\n1/hOBt5qXf8paJIFoBsdy38qnkC6ZTJzrmSfERilRBM7UQ7Pzo9aE/On7aUrghdW\nZfG/U/3s4KKYZMh+mQscHdx37Y4sUC0Yk/3s+1H3jAz5tEx9FlUgO30MKjSTr3Lc\nHjqoibokGrZOzqSF2pLqTmSX92/P7E9ii2EnZnTSDWHHLtVmS0YkE6TKQ5v2VHYP\nynRVWuOl2wJFNctCYbcZAmBeVFne4k6w443Zvkz70m4lgtaJB24r2y2ay+vyQx2Q\ngEg3RgcW+3/zh/sqjCQ6RhUjFvdBHP9nPrhCw72P/2J04JrpMnTlHbwUp1ULyH9v\nrOYDu+8gk2sFgwjgKYGrjuehtwG8IokCppWPxUUyDTklFXbjDVlLQizmoPjwfUKy\nK6kJd6w6WR3jUdRZYIXuHPzzIQE3G2aB68tSyYANuXjQAOXtVKkFlMiI/KGATIKb\nFCnhFriqFOlG1vxeKUgqMNQqcaz52V8ZGEtVAOMZVP0FzZIDqrFwvDTQwsRVsRUU\nc6ACUGZVL5X5nn90XTYIf4oZGFIs7U/P+LmH7Hngb3ZnrvwhurSreaELR554ncOl\nfLJGpiJlTShkvubXycmYIiM+XLVkdziZlRFlMef5hp02fuT+825ivuWzaNTB0min\nhMatLBKIwxjO5Xlk6CztRQD6ezMCAwEAAaOCAgMwggH/MB8GA1UdIwQYMBaAFGg3\n4Ou2O/hfEYb7/mF7CIhl9E5CMB0GA1UdDgQWBBSNjnGJqRrmOQnj5YyA9Ax8ZpJ/\nejA+BgNVHSAENzA1MDMGBmeBDAEEATApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3\nLmRpZ2ljZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsG\nAQUFBwMDMIG1BgNVHR8Ega0wgaowU6BRoE+GTWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0\nLmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIw\nMjFDQTEuY3JsMFOgUaBPhk1odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNl\ncnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDCB\nlAYIKwYBBQUHAQEEgYcwgYQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2lj\nZXJ0LmNvbTBcBggrBgEFBQcwAoZQaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29t\nL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNB\nMS5jcnQwCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAgEAKhWQvdUAI3TLwy0j\nDBZflHdzq2qnsF7v7OCQEiG46CA1/37ZJrBx6B5u23Z4dS7Ruji679WAnhq5oxkC\nuRVwmjtcDmZ8m1iDmbN0yZzfyhzWYUDQJfOQRtRVJC/3MKy6uPw9IZsInOZDWEeg\nrVoKLnkrxSbFFRoKtzOrkFqO+bdo+atEUbYsPFAMXzvXTlEvuYqB5iWWbtA2/wN7\ndFPkB1T9PBp803PU10AeKxwI4q+Ih+Iul3hiPflvFtoY4SX8JBioEWBugcmiL6w9\n8A88zGnONFcuA2iVFjvJKon6g87mQMulqmZGgBjNmXipDQlsC3rUkdZHJrsKq6mI\nPkx5EFfIKJ/ibSQ5f6eKfPboh6hF7JutdoOEZ8Zo2A5M/1WV/kO18IJsKGDPEWBa\nY+kvSzdU1j9p7IGU6QRLrBTHO5yUyIoEVAXIPs4g6bRawFHVcO8zebAhg+CmDWsG\nJT7BDQeiVWuFHs9w6YlQoPQsCl7SL0v8d2JeXajchK65owCyV6dNctBzIIU0/5tW\neHGB+jd9Sd6b8z8lgdTBQRBMOxBw5+GGieR6VklPqnLo6nZiX1PVPauTIrp0XP0E\nZ/n6TCQpUc+EqD7dFAjxRQh0HV3Zx2Xc8QCtLc2cI+ShQrgGXyqo0GlhalNDZkj8\nWbVFjeU1iU9tPlee5BDXberDjFg=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": ".github/actions/windows-signing/prod.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIEIzCCA6mgAwIBAgIQA/1HO4x/Q51pGXj/sSt/ZzAKBggqhkjOPQQDAzBTMQsw\nCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xKzApBgNVBAMTIkRp\nZ2lDZXJ0IEc1IENTIEVDQyBTSEEzODQgMjAyMSBDQTEwHhcNMjYwMzA1MDAwMDAw\nWhcNMjcwMTI3MjM1OTU5WjCBzzETMBEGCysGAQQBgjc8AgEDEwJVUzEYMBYGCysG\nAQQBgjc8AgECEwdXeW9taW5nMR0wGwYDVQQPDBRQcml2YXRlIE9yZ2FuaXphdGlv\nbjEXMBUGA1UEBRMOMjAyMy0wMDEyNzIyNTIxCzAJBgNVBAYTAlVTMRAwDgYDVQQI\nEwdXeW9taW5nMREwDwYDVQQHEwhTaGVyaWRhbjEZMBcGA1UEChMQT0JTIFByb2pl\nY3QsIExMQzEZMBcGA1UEAxMQT0JTIFByb2plY3QsIExMQzB2MBAGByqGSM49AgEG\nBSuBBAAiA2IABKkywk1iHNOnpOU7QEAt5fddCH57pHHdxJxrgbj06UONaLHDY3AR\ngjqYbVBHYIr59lW59l0Xg/ruWzaX+YHWhJrzriaCI/zTf8lambBEDJBfIkMDQD8k\nJDxfza4DulU0eaOCAcMwggG/MB8GA1UdIwQYMBaAFNcdx/rG+oJ0IEVxNBIvVdWY\nQZJeMB0GA1UdDgQWBBTz68Ag8TYugImX7RVRd3qem1tY2zA9BgNVHSAENjA0MDIG\nBWeBDAEDMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQ\nUzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwgY0GA1UdHwSB\nhTCBgjA/oD2gO4Y5aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0RzVD\nU0VDQ1NIQTM4NDIwMjFDQTEuY3JsMD+gPaA7hjlodHRwOi8vY3JsNC5kaWdpY2Vy\ndC5jb20vRGlnaUNlcnRHNUNTRUNDU0hBMzg0MjAyMUNBMS5jcmwwfgYIKwYBBQUH\nAQEEcjBwMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wSAYI\nKwYBBQUHMAKGPGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEc1\nQ1NFQ0NTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMAoGCCqGSM49BAMDA2gA\nMGUCMDuU78m1lTDkCHRpMd1nOvvuRCTSglIo+pN9T0G1ziPi+MyeyVmERc2lqXlW\nTANo+wIxAL+y4Ttsw4Vs9qoFArNpA0EIIz2tKdKgRGNjcPNSZq6ZQkiyVFN4xsjc\nFCONL0pZJw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": ".github/actions/windows-signing/test.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIBpDCCAUmgAwIBAgIUXeKu2+AXK2yR8WyuTTRg8+t6p/kwCgYIKoZIzj0EAwIw\nJzElMCMGA1UEAwwcV2l6YXJkcyBvZiBPQlMgTExDIChURVNUSU5HKTAeFw0yMzEx\nMDQwMDA3NDBaFw0zMzExMDEwMDA3NDBaMCcxJTAjBgNVBAMMHFdpemFyZHMgb2Yg\nT0JTIExMQyAoVEVTVElORykwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARVAdL6\npkfHYuO8rtndlAjpbz9bMBIVN8elrg4cDJkReAqsjH78ulhKvkor6lfF7rbRzIKA\nu026v1aDE79Z+bz+o1MwUTAdBgNVHQ4EFgQUsFQqmZEEapghq7ZPqpejjFeu8Dsw\nHwYDVR0jBBgwFoAUsFQqmZEEapghq7ZPqpejjFeu8DswDwYDVR0TAQH/BAUwAwEB\n/zAKBggqhkjOPQQDAgNJADBGAiEAmNSMQqZu3U9eMP+PbmssCigkbFKG8mGLNLFN\nJ+KfcdkCIQCVOjg6cqk4zFe7H8EKhB6CwOAIOQrgjGWLihtO2lovCw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": ".github/scripts/.Aptfile",
    "content": "package 'ccache'\npackage 'cmake', bin: '/usr/bin/cmake'\npackage 'curl'\npackage 'git'\npackage 'jq'\npackage 'ninja-build', bin: 'ninja'\npackage 'pkg-config'\n"
  },
  {
    "path": ".github/scripts/.Brewfile",
    "content": "brew \"cmake\"\nbrew \"git\"\nbrew \"jq\"\nbrew \"xcbeautify\"\n"
  },
  {
    "path": ".github/scripts/.Wingetfile",
    "content": "package '7zip.7zip', path: '7-zip', bin: '7z'\npackage 'cmake', path: 'Cmake\\bin', bin: 'cmake'\n"
  },
  {
    "path": ".github/scripts/.build.zsh",
    "content": "#!/usr/bin/env zsh\n\nbuiltin emulate -L zsh\nsetopt EXTENDED_GLOB\nsetopt PUSHD_SILENT\nsetopt ERR_EXIT\nsetopt ERR_RETURN\nsetopt NO_UNSET\nsetopt PIPE_FAIL\nsetopt NO_AUTO_PUSHD\nsetopt NO_PUSHD_IGNORE_DUPS\nsetopt FUNCTION_ARGZERO\n\n## Enable for script debugging\n#setopt WARN_CREATE_GLOBAL\n#setopt WARN_NESTED_VAR\n#setopt XTRACE\n\nif (( ! ${+CI} )) {\n  print -u2 -PR \"%F{1}    ✖︎ ${ZSH_ARGZERO:t:r} requires CI environment.%f\"\n  exit 1\n}\n\nautoload -Uz is-at-least && if ! is-at-least 5.9; then\n  print -u2 -PR \"%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.9%b is the minimum supported version. Upgrade Zsh to fix this issue.\"\n  exit 1\nfi\n\nTRAPZERR() {\n  print -u2 -PR \"::error::%F{1}    ✖︎ script execution error.%f\"\n  print -PR -e \"\n  Callstack:\n  ${(j:\\n     :)funcfiletrace}\n  \"\n\n  exit 2\n}\n\nbuild() {\n  if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}\n  local host_os=${${(s:-:)ZSH_ARGZERO:t:r}[2]}\n  local project_root=${SCRIPT_HOME:A:h:h}\n\n  fpath=(${SCRIPT_HOME}/utils.zsh ${fpath})\n  autoload -Uz log_group log_error log_output check_${host_os}\n\n  local -i debug=0\n\n  local target\n  local -r -a _valid_targets=(\n    macos-x86_64\n    macos-arm64\n    ubuntu-x86_64\n  )\n\n  local config='RelWithDebInfo'\n  local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel)\n  local -i codesign=0\n  local -i analyze=0\n\n  local -a args\n  while (( # )) {\n    case ${1} {\n      -t|--target|-c|--config)\n        if (( # == 1 )) || [[ ${2:0:1} == '-' ]] {\n          log_error \"Missing value for option %B${1}%b\"\n          exit 2\n        }\n        ;;\n    }\n    case ${1} {\n      --) shift; args+=($@); break ;;\n      -a|--analyze) analyze=1; shift ;;\n      -t|--target)\n        if (( ! ${_valid_targets[(Ie)${2}]} )) {\n          log_error \"Invalid value %B${2}%b for option %B${1}%b\"\n          exit 2\n        }\n        target=${2}\n        shift 2\n        ;;\n      -c|--config)\n        if (( ! ${_valid_configs[(Ie)${2}]} )) {\n          log_error \"Invalid value %B${2}%b for option %B${1}%b\"\n          exit 2\n        }\n        config=${2}\n        shift 2\n        ;;\n      -s|--codesign) codesign=1; shift ;;\n      --debug) debug=1; shift ;;\n      *) log_error \"Unknown option: %B${1}%b\"; log_output ${_usage}; exit 2 ;;\n    }\n  }\n\n  : \"${target:=\"${host_os}-${CPUTYPE}\"}\"\n\n  set -- ${(@)args}\n\n  check_${host_os}\n\n  if [[ ${host_os} == ubuntu ]] {\n    autoload -Uz setup_ubuntu setup_ccache\n    setup_ccache\n    setup_ubuntu\n  }\n\n  local product_name='obs-studio'\n\n  pushd ${project_root}\n\n  local -a cmake_args=()\n  local -a cmake_build_args=(--build)\n  local -a cmake_install_args=(--install)\n\n  if (( debug )) cmake_args+=(--debug-output)\n\n  case ${target} {\n    macos-*)\n      cmake_args+=(\n        --preset 'macos-ci'\n        -DCMAKE_OSX_ARCHITECTURES:STRING=${target##*-}\n      )\n\n      if (( debug )) {\n        cmake_args+=(CMAKE_XCODE_ATTRIBUTE_COMPILATION_CACHE_ENABLE_DIAGNOSTIC_REMARKS:STRING=YES)\n      }\n\n      typeset -gx NSUnbufferedIO=YES\n\n      typeset -gx CODESIGN_IDENT=\"${CODESIGN_IDENT:--}\"\n      if (( codesign )) && [[ -z ${CODESIGN_TEAM} ]] {\n        typeset -gx CODESIGN_TEAM=\"$(print \"${CODESIGN_IDENT}\" | /usr/bin/sed -En 's/.+\\((.+)\\)/\\1/p')\"\n      }\n\n      log_group \"Configuring ${product_name}...\"\n      cmake -S ${project_root} ${cmake_args}\n\n      log_group \"Building ${product_name}...\"\n      run_xcodebuild() {\n        if (( debug )) {\n          xcodebuild ${@}\n        } else {\n          if [[ ${GITHUB_EVENT_NAME} == push ]] {\n            xcodebuild ${@} 2>&1 | xcbeautify --renderer terminal\n          } else {\n            xcodebuild ${@} 2>&1 | xcbeautify --renderer github-actions\n          }\n        }\n      }\n\n      local -a build_args=(\n        ONLY_ACTIVE_ARCH=NO\n        -project obs-studio.xcodeproj\n        -target obs-studio\n        -destination \"generic/platform=macOS,name=Any Mac\"\n        -configuration ${config}\n        -parallelizeTargets\n        -hideShellScriptEnvironment\n        build\n      )\n\n      local -a archive_args=(\n        ONLY_ACTIVE_ARCH=NO\n        -project obs-studio.xcodeproj\n        -scheme obs-studio\n        -destination \"generic/platform=macOS,name=Any Mac\"\n        -archivePath obs-studio.xcarchive\n        -parallelizeTargets\n        -hideShellScriptEnvironment\n        archive\n      )\n\n      local -a export_args=(\n        -exportArchive\n        -archivePath obs-studio.xcarchive\n        -exportOptionsPlist exportOptions.plist\n        -exportPath ${project_root}/build_macos\n      )\n\n      local -a analyze_args=(\n        CLANG_ANALYZER_OUTPUT=sarif\n        CLANG_ANALYZER_OUTPUT_DIR=${project_root}/analytics\n        -project obs-studio.xcodeproj\n        -target obs-studio\n        -destination \"generic/platform=macOS,name=Any Mac\"\n        -configuration ${config}\n        -parallelizeTargets\n        -hideShellScriptEnvironment\n        analyze\n      )\n\n      pushd build_macos\n      if (( analyze )) {\n        run_xcodebuild ${analyze_args}\n      } else {\n        if [[ ${GITHUB_EVENT_NAME} == push && ${GITHUB_REF_NAME} =~ [0-9]+.[0-9]+.[0-9]+(-(rc|beta).+)? ]] {\n          run_xcodebuild ${archive_args}\n          run_xcodebuild ${export_args}\n        } else {\n          run_xcodebuild ${build_args}\n\n          rm -rf OBS.app\n          mkdir OBS.app\n          ditto frontend/${config}/OBS.app OBS.app\n        }\n      }\n      popd\n      ;;\n    ubuntu-*)\n      local cmake_bin='/usr/bin/cmake'\n      cmake_args+=(\n        --preset ubuntu-ci\n        -DENABLE_BROWSER:BOOL=ON\n        -DCEF_ROOT_DIR:PATH=\"${project_root}/.deps/cef_binary_${CEF_VERSION}_${target//ubuntu-/linux_}\"\n      )\n\n      cmake_build_args+=(build_${target%%-*} --config ${config} --parallel)\n      cmake_install_args+=(build_${target%%-*} --prefix ${project_root}/build_${target%%-*}/install/${config})\n\n      export CLICOLOR_FORCE=1\n\n      log_group \"Configuring ${product_name}...\"\n      ${cmake_bin} -S ${project_root} ${cmake_args}\n\n      log_group \"Building ${product_name}...\"\n      if (( debug )) cmake_build_args+=(--verbose)\n      ${cmake_bin} ${cmake_build_args}\n\n      log_group \"Installing ${product_name}...\"\n      if (( debug )) cmake_install_args+=(--verbose)\n      ${cmake_bin} ${cmake_install_args}\n      ;;\n  }\n  popd\n  log_group\n}\n\nbuild ${@}\n"
  },
  {
    "path": ".github/scripts/.package.zsh",
    "content": "#!/usr/bin/env zsh\n\nbuiltin emulate -L zsh\nsetopt EXTENDED_GLOB\nsetopt PUSHD_SILENT\nsetopt ERR_EXIT\nsetopt ERR_RETURN\nsetopt NO_UNSET\nsetopt PIPE_FAIL\nsetopt NO_AUTO_PUSHD\nsetopt NO_PUSHD_IGNORE_DUPS\nsetopt FUNCTION_ARGZERO\n\n## Enable for script debugging\n#setopt WARN_CREATE_GLOBAL\n#setopt WARN_NESTED_VAR\n#setopt XTRACE\n\nif (( ! ${+CI} )) {\n  print -u2 -PR \"%F{1}    ✖︎ ${ZSH_ARGZERO:t:r} requires CI environment%f\"\n  exit 1\n}\n\nautoload -Uz is-at-least && if ! is-at-least 5.9; then\n  print -u2 -PR \"%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.9%b is the minimum supported version. Upgrade Zsh to fix this issue.\"\n  exit 1\nfi\n\nTRAPZERR() {\n  print -u2 -PR \"::error::%F{1}    ✖︎ script execution error%f\"\n  print -PR -e \"\n  Callstack:\n  ${(j:\\n     :)funcfiletrace}\n  \"\n\n  exit 2\n}\n\npackage() {\n  if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}\n  local host_os=${${(s:-:)ZSH_ARGZERO:t:r}[2]}\n  local project_root=${SCRIPT_HOME:A:h:h}\n\n  fpath=(${SCRIPT_HOME}/utils.zsh ${fpath})\n  autoload -Uz log_error log_output log_group check_${host_os}\n\n  local -i debug=0\n\n  local target\n  local -r -a _valid_targets=(\n    macos-x86_64\n    macos-arm64\n    ubuntu-x86_64\n  )\n\n  local config='RelWithDebInfo'\n  local -r -a _valid_configs=(Debug RelWithDebInfo Release MinSizeRel)\n\n  local -i codesign=0\n  local -i notarize=0\n  local -i package=0\n  local -i skip_deps=0\n\n  local -a args\n  while (( # )) {\n    case ${1} {\n      -t|--target|-c|--config)\n        if (( # == 1 )) || [[ ${2:0:1} == '-' ]] {\n          log_error \"Missing value for option %B${1}%b\"\n          exit 2\n        }\n        ;;\n    }\n    case ${1} {\n      --) shift; args+=($@); break ;;\n      -t|--target)\n        if (( ! ${_valid_targets[(Ie)${2}]} )) {\n          log_error \"Invalid value %B${2}%b for option %B${1}%b\"\n          exit 2\n        }\n        target=${2}\n        shift 2\n        ;;\n      -c|--config)\n        if (( ! ${_valid_configs[(Ie)${2}]} )) {\n          log_error \"Invalid value %B${2}%b for option %B${1}%b\"\n          exit 2\n        }\n        config=${2}\n        shift 2\n        ;;\n      -s|--codesign) codesign=1; shift ;;\n      -n|--notarize) notarize=1; shift ;;\n      -p|--package) typeset -g package=1; shift ;;\n      --debug) debug=0; shift ;;\n      *) log_error \"Unknown option: %B${1}%b\"; log_output ${_usage}; exit 2 ;;\n    }\n  }\n\n  : \"${target:=\"${host_os}-${CPUTYPE}\"}\"\n\n  set -- ${(@)args}\n\n  check_${host_os}\n\n  local product_name='obs-studio'\n\n  local commit_version='0.0.0'\n  local commit_distance='0'\n  local commit_hash\n\n  if [[ -d ${project_root}/.git ]] {\n    local git_description=\"$(git describe --tags --long)\"\n    commit_version=\"${${git_description%-*}%-*}\"\n    commit_hash=\"${git_description##*-g}\"\n    commit_distance=\"${${git_description%-*}##*-}\"\n  }\n\n  local output_name\n  if (( commit_distance > 0 )) {\n    output_name=\"obs-studio-${commit_version}-${commit_hash}\"\n  } else {\n    output_name=\"obs-studio-${commit_version}\"\n  }\n\n  if [[ ${host_os} == macos ]] {\n    if [[ ! -d build_macos/OBS.app ]] {\n      log_error 'No application bundle found. Run the build script to create a valid application bundle.'\n      return 0\n    }\n\n    local -A arch_names=(x86_64 Intel arm64 Apple)\n    output_name=\"${output_name}-macos-${(L)arch_names[${target##*-}]}\"\n\n    local volume_name\n    if (( commit_distance > 0 )) {\n      volume_name=\"OBS Studio ${commit_version}-${commit_hash} (${arch_names[${target##*-}]})\"\n    } else {\n      volume_name=\"OBS Studio ${commit_version} (${arch_names[${target##*-}]})\"\n    }\n\n    if (( package )) {\n      pushd build_macos\n\n      mkdir -p obs-studio/.background\n      cp ${project_root}/cmake/macos/resources/background.tiff obs-studio/.background/\n      cp ${project_root}/cmake/macos/resources/AppIcon.icns obs-studio/.VolumeIcon.icns\n      ln -s /Applications obs-studio/Applications\n\n      mkdir -p obs-studio/OBS.app\n      ditto OBS.app obs-studio/OBS.app\n\n      local -i _status=0\n\n      autoload -Uz create_diskimage\n      create_diskimage obs-studio ${volume_name} ${output_name} || _status=1\n\n      rm -r obs-studio\n      if (( _status )) {\n        log_error \"Disk image creation failed.\"\n        return 2\n      }\n\n      typeset -gx CODESIGN_IDENT=\"${CODESIGN_IDENT:--}\"\n      typeset -gx CODESIGN_TEAM=\"$(print \"${CODESIGN_IDENT}\" | /usr/bin/sed -En 's/.+\\((.+)\\)/\\1/p')\"\n\n      codesign --sign \"${CODESIGN_IDENT}\" ${output_name}.dmg\n\n      if (( codesign && notarize )) {\n        if ! [[ ${CODESIGN_IDENT} != '-' && ${CODESIGN_TEAM} && ${CODESIGN_IDENT_USER} && ${CODESIGN_IDENT_PASS} ]] {\n          log_error \"Notarization requires Apple ID and application password.\"\n          return 2\n        }\n\n        xcrun notarytool store-credentials 'OBS-Codesign-Password' --apple-id \"${CODESIGN_IDENT_USER}\" --team-id \"${CODESIGN_TEAM}\" --password \"${CODESIGN_IDENT_PASS}\"\n        xcrun notarytool submit \"${output_name}\".dmg --keychain-profile \"OBS-Codesign-Password\" --wait\n\n        local -i _status=0\n\n        xcrun stapler staple ${output_name}.dmg || _status=1\n\n        if (( _status )) {\n          log_error \"Notarization failed. Use 'xcrun notarytool log <submission ID>' to check errors.\"\n          return 2\n        }\n      }\n      popd\n    } else {\n      log_group \"Archiving obs-studio...\"\n      pushd build_macos\n      XZ_OPT=-T0 tar -cvJf ${output_name}.tar.xz OBS.app\n      popd\n    }\n\n    if [[ ${config} == Release ]] {\n      log_group \"Archiving debug symbols...\"\n      mkdir -p build_macos/dSYMs\n      pushd build_macos/dSYMs\n      rm -rf -- *.dSYM(N)\n      cp -pR ${PWD:h}/**/*.dSYM .\n      XZ_OPT=-T0 tar -cvJf ${output_name}-dSYMs.tar.xz -- *\n      mv ${output_name}-dSYMs.tar.xz ${PWD:h}\n      popd\n    }\n\n    log_group\n\n  } elif [[ ${host_os} == ubuntu ]] {\n    local cmake_bin='/usr/bin/cmake'\n    local -a cmake_args=()\n    if (( debug )) cmake_args+=(--verbose)\n\n    if (( package )) {\n      log_group \"Packaging obs-studio...\"\n      pushd ${project_root}\n      ${cmake_bin} --build build_${target%%-*} --config ${config} --target package ${cmake_args}\n      output_name=\"${output_name}-${target##*-}-ubuntu-gnu\"\n\n      pushd ${project_root}/build_${target%%-*}\n      local -a files=(obs-studio-*-Linux*.(ddeb|deb|ddeb.sha256|deb.sha256))\n      for file (${files}) {\n        mv ${file} ${file//obs-studio-*-Linux/${output_name}}\n      }\n      popd\n      popd\n    } else {\n      log_group \"Archiving obs-studio...\"\n      output_name=\"${output_name}-${target##*-}-ubuntu-gnu\"\n\n      pushd ${project_root}/build_${target%%-*}/install/${config}\n      XZ_OPT=-T0 tar -cvJf ${project_root}/build_${target%%-*}/${output_name}.tar.xz (bin|lib|share)\n      popd\n    }\n\n    pushd ${project_root}\n    ${cmake_bin} --build build_${target%%-*} --config ${config} --target package_source ${cmake_args}\n    output_name=\"${output_name}-sources\"\n\n    pushd ${project_root}/build_${target%%-*}\n    local -a files=(obs-studio-*-sources.tar.*)\n    for file (${files}) {\n      mv ${file} ${file//obs-studio-*-sources/${output_name}}\n    }\n    popd\n    popd\n\n    log_group\n  }\n}\n\npackage ${@}\n"
  },
  {
    "path": ".github/scripts/Build-Windows.ps1",
    "content": "[CmdletBinding()]\nparam(\n    [ValidateSet('x64', 'arm64')]\n    [string] $Target = 'x64',\n    [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')]\n    [string] $Configuration = 'RelWithDebInfo'\n)\n\n$ErrorActionPreference = 'Stop'\n\nif ( $DebugPreference -eq 'Continue' ) {\n    $VerbosePreference = 'Continue'\n    $InformationPreference = 'Continue'\n}\n\nif ( $env:CI -eq $null ) {\n    throw \"Build-Windows.ps1 requires CI environment\"\n}\n\nif ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) {\n    throw \"obs-studio requires a 64-bit system to build and run.\"\n}\n\nif ( $PSVersionTable.PSVersion -lt '7.2.0' ) {\n    Write-Warning 'The obs-studio PowerShell build script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6'\n    exit 2\n}\n\nfunction Build {\n    trap {\n        Pop-Location -Stack BuildTemp -ErrorAction 'SilentlyContinue'\n        Write-Error $_\n        Log-Group\n        exit 2\n    }\n\n    $ScriptHome = $PSScriptRoot\n    $ProjectRoot = Resolve-Path -Path \"$PSScriptRoot/../..\"\n\n    $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse\n\n    foreach($Utility in $UtilityFunctions) {\n        Write-Debug \"Loading $($Utility.FullName)\"\n        . $Utility.FullName\n    }\n\n    Install-BuildDependencies -WingetFile \"${ScriptHome}/.Wingetfile\"\n\n    Push-Location -Stack BuildTemp\n    Ensure-Location $ProjectRoot\n\n    $CmakeArgs = @('--preset', \"windows-ci-${Target}\")\n\n    $CmakeBuildArgs = @('--build')\n    $CmakeInstallArgs = @()\n\n    if ( $DebugPreference -eq 'Continue' ) {\n        $CmakeArgs += ('--debug-output')\n        $CmakeBuildArgs += ('--verbose')\n        $CmakeInstallArgs += ('--verbose')\n    }\n\n    $CmakeBuildArgs += @(\n        '--preset', \"windows-${Target}\"\n        '--config', $Configuration\n        '--parallel'\n        '--', '/consoleLoggerParameters:Summary', '/noLogo'\n    )\n\n    $CmakeInstallArgs += @(\n        '--install', \"build_${Target}\"\n        '--prefix', \"${ProjectRoot}/build_${Target}/install\"\n        '--config', $Configuration\n    )\n\n    Log-Group \"Configuring obs-studio...\"\n    Invoke-External cmake @CmakeArgs\n\n    Log-Group \"Building obs-studio...\"\n    Invoke-External cmake @CmakeBuildArgs\n\n    Log-Group \"Installing obs-studio...\"\n    Invoke-External cmake @CmakeInstallArgs\n\n    Pop-Location -Stack BuildTemp\n    Log-Group\n}\n\nBuild\n"
  },
  {
    "path": ".github/scripts/Package-Windows.ps1",
    "content": "[CmdletBinding()]\nparam(\n    [ValidateSet('x64', 'arm64')]\n    [string] $Target = 'x64',\n    [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')]\n    [string] $Configuration = 'RelWithDebInfo'\n)\n\n$ErrorActionPreference = 'Stop'\n\nif ( $DebugPreference -eq 'Continue' ) {\n    $VerbosePreference = 'Continue'\n    $InformationPreference = 'Continue'\n}\n\nif ( $env:CI -eq $null ) {\n    throw \"Package-Windows.ps1 requires CI environment\"\n}\n\nif ( ! ( [System.Environment]::Is64BitOperatingSystem ) ) {\n    throw \"obs-studio requires a 64-bit system to build and run.\"\n}\n\nif ( $PSVersionTable.PSVersion -lt '7.2.0' ) {\n    Write-Warning 'The obs-studio packaging script requires PowerShell Core 7. Install or upgrade your PowerShell version: https://aka.ms/pscore6'\n    exit 2\n}\n\nfunction Package {\n    trap {\n        Write-Error $_\n        exit 2\n    }\n\n    $ScriptHome = $PSScriptRoot\n    $ProjectRoot = Resolve-Path -Path \"$PSScriptRoot/../..\"\n\n    $UtilityFunctions = Get-ChildItem -Path $PSScriptRoot/utils.pwsh/*.ps1 -Recurse\n\n    foreach( $Utility in $UtilityFunctions ) {\n        Write-Debug \"Loading $($Utility.FullName)\"\n        . $Utility.FullName\n    }\n\n    Install-BuildDependencies -WingetFile \"${ScriptHome}/.Wingetfile\"\n\n    $GitDescription = Invoke-External git describe --tags --long\n    $Tokens = ($GitDescription -split '-')\n    $CommitVersion = $Tokens[0..$($Tokens.Count - 3)] -join '-'\n    $CommitHash = $($Tokens[-1]).SubString(1)\n    $CommitDistance = $Tokens[-2]\n\n    if ( $CommitDistance -gt 0 ) {\n        $OutputName = \"obs-studio-${CommitVersion}-${CommitHash}\"\n    } else {\n        $OutputName = \"obs-studio-${CommitVersion}\"\n    }\n\n    $CpackArgs = @(\n        '-C', \"${Configuration}\"\n    )\n\n    if ( $DebugPreference -eq 'Continue' ) {\n        $CpackArgs += ('--verbose')\n    }\n\n    Log-Group \"Packaging obs-studio...\"\n\n    Push-Location -Stack PackageTemp \"build_${Target}\"\n\n    cpack @CpackArgs\n\n    $Package = Get-ChildItem -filter \"obs-studio-*-windows-${Target}.zip\" -File\n    Move-Item -Path $Package -Destination \"${OutputName}-windows-${Target}.zip\"\n\n    Pop-Location -Stack PackageTemp\n}\n\nPackage\n"
  },
  {
    "path": ".github/scripts/utils.pwsh/Ensure-Location.ps1",
    "content": "function Ensure-Location {\n    <#\n        .SYNOPSIS\n            Ensures current location to be set to specified directory.\n        .DESCRIPTION\n            If specified directory exists, switch to it. Otherwise create it,\n            then switch.\n        .EXAMPLE\n            Ensure-Location \"My-Directory\"\n            Ensure-Location -Path \"Path-To-My-Directory\"\n    #>\n\n    param(\n        [Parameter(Mandatory)]\n        [string] $Path\n    )\n\n    if ( ! ( Test-Path $Path ) ) {\n        $_Params = @{\n            ItemType = \"Directory\"\n            Path = ${Path}\n            ErrorAction = \"SilentlyContinue\"\n        }\n\n        New-Item @_Params | Set-Location\n    } else {\n        Set-Location -Path ${Path}\n    }\n}\n"
  },
  {
    "path": ".github/scripts/utils.pwsh/Install-BuildDependencies.ps1",
    "content": "function Install-BuildDependencies {\n    <#\n        .SYNOPSIS\n            Installs required build dependencies.\n        .DESCRIPTION\n            Additional packages might be needed for successful builds. This module contains additional\n            dependencies available for installation via winget and, if possible, adds their locations\n            to the environment path for future invocation.\n        .EXAMPLE\n            Install-BuildDependencies\n    #>\n\n    param(\n        [string] $WingetFile = \"$PSScriptRoot/.Wingetfile\"\n    )\n\n    if ( ! ( Test-Path function:Log-Warning ) ) {\n        . $PSScriptRoot/Logger.ps1\n    }\n\n    $Prefixes = @{\n        'x64' = ${env:ProgramFiles}\n        'x86' = ${env:ProgramFiles(x86)}\n        'arm64' = ${env:ProgramFiles(arm)}\n    }\n    \n    $Paths = $env:Path -split [System.IO.Path]::PathSeparator\n\n    $WingetOptions = @('install', '--accept-package-agreements', '--accept-source-agreements')\n\n    if ( $script:Quiet ) {\n        $WingetOptions += '--silent'\n    }\n\n    Log-Group 'Check Windows build requirements'\n    Get-Content $WingetFile | ForEach-Object {\n        $_, $Package, $_, $Path, $_, $Binary, $_, $Version = $_ -replace ',','' -split \" +(?=(?:[^\\']*\\'[^\\']*\\')*[^\\']*$)\" -replace \"'\",''\n\n        $Prefixes.GetEnumerator() | ForEach-Object {\n            $Prefix = $_.value\n            $FullPath = \"${Prefix}\\${Path}\"\n            if ( ( Test-Path $FullPath ) -and ! ( $Paths -contains $FullPath ) ) {\n                $Paths = @($FullPath) + $Paths\n                $env:Path = $Paths -join [System.IO.Path]::PathSeparator\n            }\n        }\n\n        Log-Debug \"Checking for command ${Binary}\"\n        $Found = Get-Command -ErrorAction SilentlyContinue $Binary\n\n        if ( $Found ) {\n            Log-Status \"Found dependency ${Binary} as $($Found.Source)\"\n        } else {\n            Log-Status \"Installing package ${Package} $(if ( $Version -ne $null ) { \"Version: ${Version}\" } )\"\n\n            if ( $Version -ne $null ) {\n                $WingGetOptions += @('--version', ${Version})\n            }\n\n            try {\n                $Params = $WingetOptions + $Package\n\n                winget @Params\n            } catch {\n                throw \"Error while installing winget package ${Package}: $_\"\n            }\n        }\n    }\n    Log-Group\n}\n"
  },
  {
    "path": ".github/scripts/utils.pwsh/Invoke-External.ps1",
    "content": "function Invoke-External {\n    <#\n        .SYNOPSIS\n            Invokes a non-PowerShell command.\n        .DESCRIPTION\n            Runs a non-PowerShell command, and captures its return code.\n            Throws an exception if the command returns non-zero.\n        .EXAMPLE\n            Invoke-External 7z x $MyArchive\n    #>\n\n    if ( $args.Count -eq 0 ) {\n        throw 'Invoke-External called without arguments.'\n    }\n\n    if ( ! ( Test-Path function:Log-Information ) ) {\n        . $PSScriptRoot/Logger.ps1\n    }\n\n    $Command = $args[0]\n    $CommandArgs = @()\n\n    if ( $args.Count -gt 1) {\n        $CommandArgs = $args[1..($args.Count - 1)]\n    }\n\n    $_EAP = $ErrorActionPreference\n    $ErrorActionPreference = \"Continue\"\n\n    Log-Debug \"Invoke-External: ${Command} ${CommandArgs}\"\n\n    & $command $commandArgs\n    $Result = $LASTEXITCODE\n\n    $ErrorActionPreference = $_EAP\n\n    if ( $Result -ne 0 ) {\n        throw \"${Command} ${CommandArgs} exited with non-zero code ${Result}.\"\n    }\n}\n"
  },
  {
    "path": ".github/scripts/utils.pwsh/Logger.ps1",
    "content": "function Log-Debug {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Debug $m\n        }\n    }\n}\n\nfunction Log-Verbose {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Verbose $m\n        }\n    }\n}\n\nfunction Log-Warning {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Warning $m\n        }\n    }\n}\n\nfunction Log-Error {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        foreach($m in $Message) {\n            Write-Error $m\n        }\n    }\n}\n\nfunction Log-Information {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })\n            $Icon = ' =>'\n\n            foreach($m in $Message) {\n                Write-Host -NoNewLine -ForegroundColor Blue \"  ${StageName} $($Icon.PadRight(5)) \"\n                Write-Host \"${m}\"\n            }\n        }\n    }\n}\n\nfunction Log-Group {\n    [CmdletBinding()]\n    param(\n        [Parameter(ValueFromPipeline)]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( $Env:CI -ne $null )  {\n            if ( $script:LogGroup ) {\n                Write-Output '::endgroup::'\n                $script:LogGroup = $false\n            }\n\n            if ( $Message.count -ge 1 ) {\n                Write-Output \"::group::$($Message -join ' ')\"\n                $script:LogGroup = $true\n            }\n        } else {\n            if ( $Message.count -ge 1 ) {\n                Log-Information $Message\n            }\n        }\n    }\n}\n\nfunction Log-Status {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $StageName -ne $null ) { $StageName } else { '' })\n            $Icon = '  >'\n\n            foreach($m in $Message) {\n                Write-Host -NoNewLine -ForegroundColor Green \"  ${StageName} $($Icon.PadRight(5)) \"\n                Write-Host \"${m}\"\n            }\n        }\n    }\n}\n\nfunction Log-Output {\n    [CmdletBinding()]\n    param(\n        [Parameter(Mandatory,ValueFromPipeline)]\n        [ValidateNotNullOrEmpty()]\n        [string[]] $Message\n    )\n\n    Process {\n        if ( ! ( $script:Quiet ) ) {\n            $StageName = $( if ( $script:StageName -ne $null ) { $script:StageName } else { '' })\n            $Icon = ''\n\n            foreach($m in $Message) {\n                Write-Output \"  ${StageName} $($Icon.PadRight(5)) ${m}\"\n            }\n        }\n    }\n}\n\n$Columns = (Get-Host).UI.RawUI.WindowSize.Width - 5\n"
  },
  {
    "path": ".github/scripts/utils.py/check-jsonschema.py",
    "content": "import argparse\nimport json\nimport logging\nimport os\nimport sys\nfrom typing import Any\n\nfrom json_source_map import calculate\nfrom json_source_map.errors import InvalidInputError\nfrom jsonschema import Draft7Validator\n\n\ndef discover_schema_file(filename: str) -> tuple[str | None, Any]:\n    logger = logging.getLogger()\n\n    with open(filename) as json_file:\n        json_data = json.load(json_file)\n\n    schema_filename = json_data.get(\"$schema\", None)\n\n    if not schema_filename:\n        logger.info(f\"ℹ️ ${filename} has no schema definition\")\n        return (None, None)\n\n    schema_file = os.path.join(os.path.dirname(filename), schema_filename)\n\n    with open(schema_file) as schema_file:\n        schema_data = json.load(schema_file)\n\n    return (str(schema_file), schema_data)\n\n\ndef validate_json_files(\n    schema_data: dict[Any, Any], json_file_name: str\n) -> list[dict[str, str]]:\n    logger = logging.getLogger()\n\n    with open(json_file_name) as json_file:\n        text_data = json_file.read()\n\n    json_data = json.loads(text_data)\n    source_map = calculate(text_data)\n\n    validator = Draft7Validator(schema_data)\n\n    violations = []\n    for violation in sorted(validator.iter_errors(json_data), key=str):\n        logger.info(\n            f\"⚠️ Schema violation in file '{json_file_name}':\\n{violation}\\n----\\n\"\n        )\n\n        if len(violation.absolute_path):\n            error_path = \"/\".join(\n                str(path_element) for path_element in violation.absolute_path\n            )\n            error_entry = source_map[\"/{}\".format(error_path)]\n\n            violation_data = {\n                \"file\": json_file_name,\n                \"title\": \"Validation Error\",\n                \"message\": violation.message,\n                \"annotation_level\": \"failure\",\n                \"start_line\": error_entry.value_start.line + 1,\n                \"end_line\": error_entry.value_end.line + 1,\n            }\n\n            violations.append(violation_data)\n\n    return violations\n\n\ndef main() -> int:\n    parser = argparse.ArgumentParser(\n        description=\"Validate JSON files by schema definition\"\n    )\n    parser.add_argument(\n        \"json_files\", metavar=\"FILE\", type=str, nargs=\"+\", help=\"JSON file to validate\"\n    )\n    parser.add_argument(\n        \"--loglevel\", type=str, help=\"Set log level\", default=\"WARNING\", required=False\n    )\n\n    arguments = parser.parse_args()\n\n    logging.basicConfig(level=arguments.loglevel, format=\"%(levelname)s - %(message)s\")\n    logger = logging.getLogger()\n\n    schema_mappings = {}\n\n    for json_file in arguments.json_files:\n        try:\n            (schema_file, schema_data) = discover_schema_file(json_file)\n        except OSError as e:\n            logger.error(f\"❌ Failed to discover schema for file '{json_file}': {e}\")\n            return 2\n\n        if schema_file and schema_file not in schema_mappings.keys():\n            schema_mappings.update(\n                {schema_file: {\"schema_data\": schema_data, \"files\": set()}}\n            )\n\n        schema_mappings[schema_file][\"files\"].add(json_file)\n\n    validation_errors = []\n    for schema_entry in schema_mappings.values():\n        for json_file in schema_entry[\"files\"]:\n            try:\n                new_errors = validate_json_files(schema_entry[\"schema_data\"], json_file)\n            except (InvalidInputError, OSError) as e:\n                logger.error(\n                    f\"❌ Failed to create JSON source map for file '{json_file}': {e}\"\n                )\n                return 2\n\n            [validation_errors.append(error) for error in new_errors]\n\n    if validation_errors:\n        try:\n            with open(\"validation_errors.json\", \"w\") as results_file:\n                json.dump(validation_errors, results_file)\n        except OSError as e:\n            logger.error(f\"❌ Failed to write validation results file: {e}\")\n            return 2\n\n        return 1\n\n    return 0\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": ".github/scripts/utils.py/check-services.py",
    "content": "import asyncio\nimport json\nimport os\nimport time\nimport sys\nimport zipfile\n\nimport aiohttp\nimport requests\n\nfrom io import BytesIO\nfrom typing import List, Dict\nfrom collections import defaultdict\n\nMINIMUM_PURGE_AGE = 9.75 * 24 * 60 * 60  # slightly less than 10 days\nTIMEOUT = 10\nSKIPPED_SERVICES = {\"SHOWROOM\", \"Dacast\"}\nSERVICES_FILE = \"plugins/rtmp-services/data/services.json\"\nPACKAGE_FILE = \"plugins/rtmp-services/data/package.json\"\nCACHE_FILE = \"other/timestamps.json\"\nGITHUB_OUTPUT_FILE = os.environ.get(\"GITHUB_OUTPUT\", None)\n\nDO_NOT_PING = {\"jp9000\"}\nPR_MESSAGE = \"\"\"This is an automatically created pull request to remove unresponsive servers and services.\n\n| Service | Action Taken | Author(s) |\n| ------- | ------------ | --------- |\n{table}\n\nIf you are not responsible for an affected service and want to be excluded from future pings please let us know.\n\nCreated by workflow run: https://github.com/{repository}/actions/runs/{run_id}\"\"\"\n\n# GQL is great isn't it\nGQL_QUERY = \"\"\"{\n  repositoryOwner(login: \"obsproject\") {\n    repository(name: \"obs-studio\") {\n      object(expression: \"master\") {\n        ... on Commit {\n          blame(path: \"plugins/rtmp-services/data/services.json\") {\n            ranges {\n              startingLine\n              endingLine\n              commit {\n                author {\n                  user {\n                    login\n                  }\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\"\"\"\n\n\ndef get_last_artifact():\n    s = requests.session()\n    s.headers[\"Authorization\"] = f'Bearer {os.environ[\"GITHUB_TOKEN\"]}'\n\n    run_id = os.environ[\"WORKFLOW_RUN_ID\"]\n    repo = os.environ[\"REPOSITORY\"]\n\n    # fetch run first, get workflow id from there to get workflow runs\n    r = s.get(f\"https://api.github.com/repos/{repo}/actions/runs/{run_id}\")\n    r.raise_for_status()\n    workflow_id = r.json()[\"workflow_id\"]\n\n    r = s.get(\n        f\"https://api.github.com/repos/{repo}/actions/workflows/{workflow_id}/runs\",\n        params=dict(\n            per_page=1,\n            status=\"completed\",\n            branch=\"master\",\n            conclusion=\"success\",\n            event=\"schedule\",\n        ),\n    )\n    r.raise_for_status()\n    runs = r.json()\n    if not runs[\"workflow_runs\"]:\n        raise ValueError(\"No completed workflow runs found\")\n\n    r = s.get(runs[\"workflow_runs\"][0][\"artifacts_url\"])\n    r.raise_for_status()\n\n    for artifact in r.json()[\"artifacts\"]:\n        if artifact[\"name\"] == \"timestamps\":\n            artifact_url = artifact[\"archive_download_url\"]\n            break\n    else:\n        raise ValueError(\"No previous artifact found.\")\n\n    r = s.get(artifact_url)\n    r.raise_for_status()\n    zip_data = BytesIO()\n    zip_data.write(r.content)\n\n    with zipfile.ZipFile(zip_data) as zip_ref:\n        for info in zip_ref.infolist():\n            if info.filename == \"timestamps.json\":\n                return json.loads(zip_ref.read(info.filename))\n\n\ndef find_people_to_blame(raw_services: str, servers: list[tuple[str, str]]) -> dict:\n    if not servers:\n        return dict()\n\n    # Fetch Blame data from github\n    s = requests.session()\n    s.headers[\"Authorization\"] = f'Bearer {os.environ[\"GITHUB_TOKEN\"]}'\n\n    r = s.post(\n        \"https://api.github.com/graphql\", json=dict(query=GQL_QUERY, variables=dict())\n    )\n    r.raise_for_status()\n    j = r.json()\n\n    # The file is only ~2600 lines so this isn't too crazy and makes the lookup very easy\n    line_author = dict()\n    for blame in j[\"data\"][\"repositoryOwner\"][\"repository\"][\"object\"][\"blame\"][\n        \"ranges\"\n    ]:\n        for i in range(blame[\"startingLine\"] - 1, blame[\"endingLine\"]):\n            if user := blame[\"commit\"][\"author\"][\"user\"]:\n                line_author[i] = user[\"login\"]\n\n    service_authors = defaultdict(set)\n    for i, line in enumerate(raw_services.splitlines()):\n        if '\"url\":' not in line:\n            continue\n        for server, service in servers:\n            if server in line and (author := line_author.get(i)):\n                if author not in DO_NOT_PING:\n                    service_authors[service].add(author)\n\n    return service_authors\n\n\ndef set_output(name, value):\n    if not GITHUB_OUTPUT_FILE:\n        return\n\n    try:\n        with open(GITHUB_OUTPUT_FILE, \"a\", encoding=\"utf-8\", newline=\"\\n\") as f:\n            f.write(f\"{name}={value}\\n\")\n    except Exception as e:\n        print(f\"Writing to github output files failed: {e!r}\")\n\n\nasync def check_servers_task(\n    session: aiohttp.ClientSession, host: str, protocol: str, servers: List[str]\n) -> List[Dict]:\n    query = [dict(url=h, protocol=protocol) for h in servers]\n\n    async with session.get(\n        f\"http://{host}:8999/test_remote_servers\", json=query\n    ) as resp:\n        return await resp.json()\n\n\nasync def process_services(session: aiohttp.ClientSession, check_servers: List[str]):\n    try:\n        with open(SERVICES_FILE, encoding=\"utf-8\") as services_file:\n            raw_services = services_file.read()\n            services = json.loads(raw_services)\n        with open(PACKAGE_FILE, encoding=\"utf-8\") as package_file:\n            package = json.load(package_file)\n    except OSError as e:\n        print(f\"❌ Could not open services/package file: {e}\")\n        return 1\n\n    # attempt to load last check result cache\n    try:\n        with open(CACHE_FILE, encoding=\"utf-8\") as check_file:\n            fail_timestamps = json.load(check_file)\n    except OSError as e:\n        # cache might be evicted or not exist yet, so this is non-fatal\n        print(\n            f\"⚠️ Could not read cache file, trying to get last artifact (Exception: {e})\"\n        )\n\n        try:\n            fail_timestamps = get_last_artifact()\n        except Exception as e:\n            print(f\"⚠️ Could not fetch cache file, starting fresh. (Exception: {e})\")\n            fail_timestamps = dict()\n        else:\n            print(\"Fetched cache file from last run artifact.\")\n    else:\n        print(\"Successfully loaded cache file:\", CACHE_FILE)\n\n    start_time = int(time.time())\n    affected_services = dict()\n    removed_servers = list()\n\n    # create temporary new list\n    new_services = services.copy()\n    new_services[\"services\"] = []\n\n    for service in services[\"services\"]:\n        # skip services that do custom stuff that we can't easily check\n        if service[\"name\"] in SKIPPED_SERVICES:\n            new_services[\"services\"].append(service)\n            continue\n\n        service_type = service.get(\"recommended\", {}).get(\"output\", \"rtmp_output\")\n        if service_type not in {\"rtmp_output\", \"ffmpeg_hls_muxer\"}:\n            print(\"Unknown service type:\", service_type)\n            new_services[\"services\"].append(service)\n            continue\n\n        protocol = \"rtmp\" if service_type == \"rtmp_output\" else \"hls\"\n\n        # create a copy to mess with\n        new_service = service.copy()\n        new_service[\"servers\"] = []\n\n        # run checks for all the servers, and store results in timestamp cache\n        try:\n            servers = [s[\"url\"] for s in service[\"servers\"]]\n            tasks = []\n            for host in check_servers:\n                tasks.append(\n                    asyncio.create_task(\n                        check_servers_task(session, host, protocol, servers)\n                    )\n                )\n            results = await asyncio.gather(*tasks)\n        except Exception as e:\n            print(\n                f\"❌ Querying server status for \\\"{service['name']}\\\" failed with: {e}\"\n            )\n            return 1\n\n        # go over results\n        for server, result in zip(service[\"servers\"], zip(*results)):\n            failure_count = sum(not res[\"status\"] for res in result)\n            probe_count = len(result)\n            # only treat server as failed if all check servers reported a failure\n            is_ok = failure_count < probe_count\n\n            if not is_ok:\n                failures = {res[\"comment\"] for res in result if not res[\"status\"]}\n                print(\n                    f\"⚠️ Connecting to server failed: {server['url']} (Reason(s): {failures})\"\n                )\n\n                if ts := fail_timestamps.get(server[\"url\"], None):\n                    if (delta := start_time - ts) >= MINIMUM_PURGE_AGE:\n                        print(\n                            f'🗑️ Purging server \"{server[\"url\"]}\", it has been '\n                            f\"unresponsive for {round(delta/60/60/24)} days.\"\n                        )\n                        removed_servers.append((server[\"url\"], service[\"name\"]))\n                        # continuing here means not adding it to the new list, thus dropping it\n                        continue\n                else:\n                    fail_timestamps[server[\"url\"]] = start_time\n            elif is_ok and server[\"url\"] in fail_timestamps:\n                # remove timestamp of failed check if server is back\n                delta = start_time - fail_timestamps[server[\"url\"]]\n                print(\n                    f'💡 Server \"{server[\"url\"]}\" is back after {round(delta/60/60/24)} days!'\n                )\n                del fail_timestamps[server[\"url\"]]\n\n            new_service[\"servers\"].append(server)\n\n        if (diff := len(service[\"servers\"]) - len(new_service[\"servers\"])) > 0:\n            print(f'ℹ️ Removed {diff} server(s) from {service[\"name\"]}')\n            affected_services[service[\"name\"]] = f\"{diff} servers removed\"\n\n        # remove services with no valid servers\n        if not new_service[\"servers\"]:\n            print(f'💀 Service \"{service[\"name\"]}\" has no valid servers left, removing!')\n            affected_services[service[\"name\"]] = f\"Service removed\"\n        else:\n            new_services[\"services\"].append(new_service)\n\n        # wait a bit between services\n        await asyncio.sleep(2.0)\n\n    # write cache file\n    try:\n        os.makedirs(\"other\", exist_ok=True)\n        with open(CACHE_FILE, \"w\", encoding=\"utf-8\") as cache_file:\n            json.dump(fail_timestamps, cache_file)\n    except OSError as e:\n        print(f\"❌ Could not write cache file: {e}\")\n        return 1\n    else:\n        print(\"Successfully wrote cache file:\", CACHE_FILE)\n\n    if removed_servers:\n        # increment package version and save that as well\n        package[\"version\"] += 1\n        package[\"files\"][0][\"version\"] += 1\n\n        try:\n            with open(SERVICES_FILE, \"w\", encoding=\"utf-8\") as services_file:\n                json.dump(new_services, services_file, indent=4, ensure_ascii=False)\n                services_file.write(\"\\n\")\n\n            with open(PACKAGE_FILE, \"w\", encoding=\"utf-8\") as package_file:\n                json.dump(package, package_file, indent=4)\n                package_file.write(\"\\n\")\n        except OSError as e:\n            print(f\"❌ Could not write services/package file: {e}\")\n            return 1\n        else:\n            print(\n                f\"Successfully wrote services/package files:\\n- {SERVICES_FILE}\\n- {PACKAGE_FILE}\"\n            )\n\n        # try to find authors to ping, this is optional and is allowed to fail\n        try:\n            service_authors = find_people_to_blame(raw_services, removed_servers)\n        except Exception as e:\n            print(f\"⚠ Could not fetch blame for some reason: {e}\")\n            service_authors = dict()\n\n        # set GitHub outputs\n        set_output(\"make_pr\", \"true\")\n        msg = PR_MESSAGE.format(\n            repository=os.environ[\"REPOSITORY\"],\n            run_id=os.environ[\"WORKFLOW_RUN_ID\"],\n            table=\"\\n\".join(\n                \"| {name} | {action} | {authors} |\".format(\n                    name=name.replace(\"|\", \"\\\\|\"),\n                    action=action,\n                    authors=\", \".join(\n                        f\"@{author}\" for author in sorted(service_authors.get(name, []))\n                    ),\n                )\n                for name, action in sorted(affected_services.items())\n            ),\n        )\n        set_output(\"pr_message\", json.dumps(msg))\n    else:\n        set_output(\"make_pr\", \"false\")\n\n\nasync def main():\n    # check for environment variables\n    try:\n        api_key = os.environ[\"API_KEY\"]\n        servers = os.environ[\"API_SERVERS\"].split(\",\")\n        if not servers:\n            raise ValueError(\"No checker servers!\")\n        # Mask everything except the region code\n        for server in servers:\n            prefix = server[: server.index(\".\") + 1]\n            print(f\"::add-mask::{prefix}\")\n            suffix = server[server.index(\".\", len(prefix)) :]\n            print(f\"::add-mask::{suffix}\")\n    except Exception as e:\n        print(f\"❌ Failed getting required environment variables: {e}\")\n        return 1\n\n    # create aiohttp session\n    async with aiohttp.ClientSession() as session:\n        session.headers[\"Authorization\"] = api_key\n        session.headers[\"User-Agent\"] = \"OBS Repo Service Checker/1.0\"\n        return await process_services(session, servers)\n\n\nif __name__ == \"__main__\":\n    sys.exit(asyncio.run(main()))\n"
  },
  {
    "path": ".github/scripts/utils.zsh/check_macos",
    "content": "autoload -Uz is-at-least log_group log_info log_error log_status\n\nlocal macos_version=$(sw_vers -productVersion)\n\nlog_group 'Install macOS build requirements'\nlog_info 'Checking macOS version...'\nif ! is-at-least 12.0 ${macos_version}; then\n  log_error \"Minimum required macOS version is 12.0, but running on macOS ${macos_version}\"\n  return 2\nelse\n  log_status \"macOS ${macos_version} is recent\"\nfi\n\nlog_info 'Checking for Homebrew...'\nif (( ! ${+commands[brew]} )) {\n  log_error 'No Homebrew command found. Please install Homebrew (https://brew.sh)'\n  return 2\n}\n\nbrew bundle --file ${SCRIPT_HOME}/.Brewfile\nrehash\nlog_group\n"
  },
  {
    "path": ".github/scripts/utils.zsh/check_ubuntu",
    "content": "autoload -Uz log_debug log_group\n\nlog_group 'Check Ubuntu build requirements'\nlog_debug 'Checking Ubuntu distribution name and version...'\n\nif [[ -f /etc/os-release ]] {\n  local dist_name\n  local dist_version\n  read -r dist_name dist_version <<< \"$(source /etc/os-release; print \"${NAME} ${VERSION_ID}\")\"\n\n  if [[ ${dist_name} != Ubuntu ]] {\n    log_error \"Not running on an Ubuntu distribution. Aborting\"\n    log_group\n    return 2\n  }\n\n  autoload -Uz is-at-least && if ! is-at-least 24.04 ${dist_version}; then\n    log_error \"Not running on a recent-enough Ubuntu distribution. Aborting\"\n    log_group\n    return 2\n  fi\n} else {\n  log_error \"Unable to determine local Linux distribution, but Ubuntu is required. Aborting\"\n  log_group\n  return 2\n}\n\nlocal -a dependencies=(\"${(fA)$(<${SCRIPT_HOME}/.Aptfile)}\")\nlocal -a install_list\nlocal binary\n\nsudo apt-get update -qq\n\nfor dependency (${dependencies}) {\n  local -a tokens=(${=dependency//(,|:|\\')/})\n\n  if [[ ! ${tokens[1]} == package ]] continue\n\n  if [[ ${#tokens} -gt 2 && ${tokens[3]} == bin ]] {\n    binary=${tokens[4]}\n  } else {\n    binary=${tokens[2]}\n  }\n\n  if (( ! ${+commands[${binary}]} )) install_list+=(${tokens[2]})\n}\n\nlog_debug \"List of dependencies to install: ${install_list}\"\nif (( #install_list )) {\n  sudo apt-get -y --no-install-recommends install ${install_list}\n}\n\nrehash\nlog_group\n"
  },
  {
    "path": ".github/scripts/utils.zsh/create_diskimage",
    "content": "autoload -Uz log_debug log_error log_info log_status log_group log_output log_warning\n\nlocal -r _usage=\"Usage: %B${0}%b <source> <volume name> <output_name>\n\nCreate macOS disk image <volume name> <output_name> with contents of <source>\"\n\nif (( ! # )) {\n  log_error 'Called without arguments.'\n  log_output ${_usage}\n  return 2\n}\n\nlocal source=${1}\nlocal volume_name=${2}\nlocal output_name=${3}\n\nlog_group \"Create macOS disk image\"\n\ntrap \"safe_hdiutil detach /Volumes/${output_name}; rm temp.dmg; log_group; return 2\" ERR\n\nsafe_hdiutil() {\n  local _status=0\n  local -r -a _backoff=(2 5 10 15 30)\n\n  for i ({1..5}) {\n    hdiutil ${@} && _status=0 || _status=1\n\n    if (( _status )) {\n      log_warning \"Unable to run 'hdiutil ${@}' (attempt #${i}). Retrying.\"\n      if (( ${+CI} )) sudo pkill -9 XProtect >/dev/null || true\n      sleep ${_backoff[${i}]}\n    } else {\n      break\n    }\n  }\n\n  if (( _status )) {\n    log_error \"Unable to run 'hdiutil ${@}'. Aborting\"\n    log_group\n    return 2\n  }\n}\n\nsafe_hdiutil create \\\n  -volname \"${volume_name}\" \\\n  -srcfolder ${source} \\\n  -ov \\\n  -fs HFS+ \\\n  -format UDRW \\\n  temp.dmg\nsafe_hdiutil attach \\\n  -noverify \\\n  -readwrite \\\n  -mountpoint /Volumes/${output_name} \\\n  temp.dmg\n\nlog_info \"Waiting 2 seconds to ensure mounted volume is available...\"\nsleep 2\nlog_status \"Done\"\nlog_info \"Setting up disk volume...\"\nlog_status \"Volume icon\"\nSetFile -c icnC /Volumes/${output_name}/.VolumeIcon.icns\nlog_status \"Icon positions\"\nosascript package.applescript ${output_name}\nlog_status \"File permissions\"\nchmod -Rf go-w /Volumes/${output_name}\nSetFile -a C /Volumes/${output_name}\nrm -rf -- /Volumes/${output_name}/.fseventsd(N)\nlog_info \"Converting disk image...\"\n\nsafe_hdiutil detach /Volumes/${output_name}\n\nsafe_hdiutil convert \\\n  -format ULFO \\\n  -ov \\\n  -o ${output_name}.dmg temp.dmg\n\nrm temp.dmg\n\ntrap '' ERR\n\nlog_group\n\nreturn 0\n"
  },
  {
    "path": ".github/scripts/utils.zsh/log_debug",
    "content": "if (( debug )) print -PR -e \"::debug::%F{220}DEBUG: ${@}%f\"\n"
  },
  {
    "path": ".github/scripts/utils.zsh/log_error",
    "content": "print -u2 -PR \"::error::%F{1}    ✖︎%f  ${@}\"\n"
  },
  {
    "path": ".github/scripts/utils.zsh/log_group",
    "content": "autoload -Uz log_info\n\nif (( ! ${+_log_group} )) typeset -g _log_group=0\n\nif (( _log_group )) {\n  print \"::endgroup::\"\n  typeset -g _log_group=0\n}\nif (( # )) {\n  print \"::group::${@}\"\n  typeset -g _log_group=1\n}\n"
  },
  {
    "path": ".github/scripts/utils.zsh/log_info",
    "content": "print -PR \"%F{4}   =>%f  %B${@}%b\"\n"
  },
  {
    "path": ".github/scripts/utils.zsh/log_output",
    "content": "print -PR \"        ${@}\"\n"
  },
  {
    "path": ".github/scripts/utils.zsh/log_status",
    "content": "print -PR \"%F{2}    >%f  ${@}\"\n"
  },
  {
    "path": ".github/scripts/utils.zsh/log_warning",
    "content": "print -PR \"::warning::%F{3}   =>  ${@}%f\"\n"
  },
  {
    "path": ".github/scripts/utils.zsh/mkcd",
    "content": "[[ -n ${1} ]] && mkdir -p ${1} && builtin cd ${1}\n"
  },
  {
    "path": ".github/scripts/utils.zsh/setup_ccache",
    "content": "autoload -Uz is-at-least log_debug log_warning\n\nif (( ${+commands[ccache]} )) {\n  log_debug \"Found ccache at ${commands[ccache]}\"\n\n  local ccache_version=$(ccache --version | head -1 | cut -d ' ' -f 3)\n  if ! is-at-least 4.12 ${ccache_version}; then\n    ccache --set-config=run_second_cpp=true\n  fi\n\n  ccache --set-config=direct_mode=true\n  ccache --set-config=inode_cache=true\n  ccache --set-config=compiler_check=content\n  ccache --set-config=file_clone=true\n\n  local -a sloppiness=(\n    include_file_mtime\n    include_file_ctime\n    file_stat_matches\n    system_headers\n  )\n\n  if [[ ${host_os} == macos ]] {\n    sloppiness+=(\n      modules\n      clang_index_store\n    )\n\n    ccache --set-config=sloppiness=${(j:,:)sloppiness}\n  }\n\n  ccache --set-config=cache_dir=\"${GITHUB_WORKSPACE:-${HOME}}/.ccache\"\n  ccache --set-config=max_size=\"${CCACHE_SIZE:-1G}\"\n  ccache -z > /dev/null\n} else {\n  log_warning \"Ccache not available\"\n}\n"
  },
  {
    "path": ".github/scripts/utils.zsh/setup_ubuntu",
    "content": "autoload -Uz log_group log_error log_status log_info log_debug\n\nif (( ! ${+commands[curl]} )) {\n  log_error 'curl not available.'\n  return 2\n}\n\nif (( ! ${+commands[jq]} )) {\n  log_error 'jq not available.'\n  return 2\n}\n\nlocal -a curl_opts=(--show-error --silent --location -O ${@})\n\npushd ${project_root}\n\nlog_group 'Installing obs-studio build dependencies...'\n\nmkdir -p ${project_root}/.deps\nlocal deps_version\nlocal deps_baseurl\nlocal deps_label\nlocal deps_hash\n\nIFS=';' read -r deps_version deps_baseurl deps_label deps_hash deps_revision <<< \\\n  \"$(jq -r --arg target \"${target}\" '\n    .configurePresets[]\n    | select(.name==\"dependencies\")\n    | .vendor[\"obsproject.com/obs-studio\"].dependencies[\"cef\"]\n    | {version, baseUrl, \"label\", \"hash\": .hashes[$target], \"revision\": .revision[$target]}\n    | join(\";\")\n  ' ${project_root}/CMakePresets.json)\"\n\nif (( ! deps_version )) {\n  log_error 'No valid cef spec found in buildspec.json.'\n  return 2\n}\nlog_group 'Setting up pre-built Chromium Embedded Framework...'\n\npushd ${project_root}/.deps\nlocal _filename=\"cef_binary_${deps_version}_${target//ubuntu-/linux_}${deps_revision:+\"_v${deps_revision}\"}.tar.xz\"\nlocal _url=${deps_baseurl}/${_filename}\nlocal _target=\"cef_binary_${deps_version}_${target//ubuntu-/linux_}\"\ntypeset -g CEF_VERSION=${deps_version}\n\nlog_debug \"Running curl ${curl_opts} ${_url}\"\ncurl ${curl_opts} ${_url} && \\\n  log_status \"Downloaded ${deps_label} for ${target}.\"\n\nread -r artifact_checksum _ <<< \"$(sha256sum ${_filename})\"\nif [[ ${deps_hash} != ${artifact_checksum} ]] {\n  log_error \"Checksum of downloaded ${deps_label} does not match specification.\nExpected : ${deps_hash}\nActual   : ${artifact_checksum}\"\n  return 2\n}\nlog_status \"Checksum of downloaded ${deps_label} matches.\"\nmkdir -p ${_target} && pushd ${_target}\n\nXZ_OPT=-T0 tar --strip-components 1 -xJf ../${_filename} && log_status \"${deps_label} extracted.\"\npopd\n\nlog_group 'Installing obs-studio build dependencies from apt...'\n\nsudo apt-get install -y --no-install-recommends \\\n  build-essential libglib2.0-dev \\\n  extra-cmake-modules lsb-release dh-cmake \\\n  libcurl4-openssl-dev \\\n  libavcodec-dev libavdevice-dev libavfilter-dev libavformat-dev libavutil-dev \\\n  libswresample-dev libswscale-dev \\\n  libjansson-dev \\\n  libx11-xcb-dev \\\n  libgles2-mesa-dev \\\n  libwayland-dev \\\n  libpipewire-0.3-dev \\\n  libpulse-dev \\\n  libx264-dev \\\n  libmbedtls-dev \\\n  libgl1-mesa-dev \\\n  libjansson-dev \\\n  uthash-dev \\\n  libsimde-dev \\\n  libluajit-5.1-dev python3-dev \\\n  libx11-dev libxcb-randr0-dev libxcb-shm0-dev libxcb-xinerama0-dev \\\n  libxcb-composite0-dev libxinerama-dev libxcb1-dev libx11-xcb-dev libxcb-xfixes0-dev \\\n  swig libcmocka-dev libxss-dev libglvnd-dev \\\n  libxkbcommon-dev libatk1.0-dev libatk-bridge2.0-dev libxcomposite-dev libxdamage-dev \\\n  libasound2-dev libfdk-aac-dev libfontconfig-dev libfreetype6-dev libjack-jackd2-dev \\\n  libpulse-dev libsndio-dev libspeexdsp-dev libudev-dev libv4l-dev libva-dev libvlc-dev \\\n  libpci-dev libdrm-dev \\\n  nlohmann-json3-dev libwebsocketpp-dev libasio-dev libqrcodegencpp-dev \\\n  libffmpeg-nvenc-dev librist-dev libsrt-openssl-dev \\\n  qt6-base-dev libqt6svg6-dev qt6-base-private-dev \\\n  libvpl-dev libvpl2\n"
  },
  {
    "path": ".github/workflows/analyze-project.yaml",
    "content": "name: Analyze Project\non:\n  workflow_call:\njobs:\n  windows:\n    name: Windows 🪟 (PVS-Studio)\n    runs-on: windows-2022\n    if: false && github.repository_owner == 'obsproject'\n    defaults:\n      run:\n        shell: pwsh\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n\n      - name: Build OBS Studio 🧱\n        uses: ./.github/actions/build-obs\n        env:\n          TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}\n          TWITCH_HASH: ${{ secrets.TWITCH_HASH }}\n          RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}\n          RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}\n          YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}\n          YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}\n          YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}\n          YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}\n          GPU_PRIORITY_VAL: ${{ secrets.GPU_PRIORITY_VAL }}\n        with:\n          target: x64\n          config: Debug\n\n      - name: Run PVS-Studio Analysis\n        uses: ./.github/actions/windows-analysis\n        with:\n          pvsUsername: ${{ secrets.PVS_NAME }}\n          pvsKey: ${{ secrets.PVS_KEY }}\n          target: x64\n          config: Debug\n\n  macos:\n    name: macOS 🍏 (clang-analyze)\n    runs-on: macos-15\n    defaults:\n      run:\n        shell: zsh --no-rcs --errexit --pipefail {0}\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n\n      - name: Set Up Environment 🔧\n        id: setup\n        run: |\n          : Set Up Environment 🔧\n          if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n          print '::group::Enable Xcode 16.4'\n          sudo xcode-select --switch /Applications/Xcode_16.4.app/Contents/Developer\n          print '::endgroup::'\n\n          print '::group::Clean Homebrew Environment'\n          local -a unwanted_formulas=()\n          local -a remove_formulas=()\n          for formula (${unwanted_formulas}) {\n            if [[ -d ${HOMEBREW_PREFIX}/Cellar/${formula} ]] remove_formulas+=(${formula})\n          }\n\n          if (( #remove_formulas )) brew uninstall --ignore-dependencies ${remove_formulas}\n          print '::endgroup::'\n\n      - name: Set Up Code Signing 🔑\n        uses: ./.github/actions/setup-macos-codesigning\n        id: codesign\n        with:\n          codesignIdentity: ${{ secrets.MACOS_SIGNING_IDENTITY }}\n          codesignCertificate: ${{ secrets.MACOS_SIGNING_CERT }}\n          certificatePassword: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }}\n          keychainPassword: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}\n          provisioningProfile: ${{ secrets.MACOS_SIGNING_PROVISIONING_PROFILE }}\n          notarizationUser: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}\n          notarizationPassword: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}\n\n      - name: Build OBS Studio 🧱\n        env:\n          TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}\n          TWITCH_HASH: ${{ secrets.TWITCH_HASH }}\n          RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}\n          RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}\n          YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}\n          YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}\n          YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}\n          YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}\n          CODESIGN_IDENT: ${{ steps.codesign.outputs.codesignIdent }}\n          CODESIGN_TEAM: ${{ steps.codesign.outputs.codesignTeam }}\n          PROVISIONING_PROFILE: ${{ steps.codesign.outputs.provisioningProfileUUID }}\n        run: |\n          : Run macOS Build\n\n          local -a build_args=(\n            --config Debug\n            --target macos-arm64\n            --codesign\n            --analyze\n          )\n          if (( ${+RUNNER_DEBUG} )) build_args+=(--debug)\n\n          git fetch origin --no-tags --no-recurse-submodules -q\n          .github/scripts/build-macos ${build_args}\n\n      - name: Compile Analytics Data 📊\n        run: |\n          : Compile Analytics Data 📊\n\n          local analytics_root='${{ github.workspace }}/analytics'\n\n          local -a analytics_files=(${analytics_root}/StaticAnalyzer/obs-studio/**/*.plist)\n\n          for file (${analytics_files}) {\n            mv ${file} ${analytics_root}/${${file:t}//plist/sarif}\n          }\n\n          pushd ${analytics_root}\n\n          npx @microsoft/sarif-multitool merge --merge-runs *.sarif\n\n          popd\n\n      - name: Upload SARIF report files 📦\n        uses: github/codeql-action/upload-sarif@v3\n        with:\n          sarif_file: \"${{ github.workspace }}/analytics/merged.sarif\"\n          category: 'clang-analyze (macOS Apple Silicon)'\n"
  },
  {
    "path": ".github/workflows/build-project.yaml",
    "content": "name: Build Project\non:\n  workflow_call:\njobs:\n  check-event:\n    name: Event Data 🔎\n    runs-on: ubuntu-24.04\n    defaults:\n      run:\n        shell: bash\n    outputs:\n      package: ${{ steps.setup.outputs.package }}\n      codesign: ${{ steps.setup.outputs.codesign }}\n      notarize: ${{ steps.setup.outputs.notarize }}\n      config: ${{ steps.setup.outputs.config }}\n      commitHash: ${{ steps.setup.outputs.commitHash }}\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: Check Event Data ☑️\n        id: setup\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          : Check Event Data ☑️\n          if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n          case \"${GITHUB_EVENT_NAME}\" in\n            pull_request)\n              config_data=('codesign:false' 'notarize:false' 'package:false' 'config:RelWithDebInfo')\n              if gh pr view ${{ github.event.number }} --json labels \\\n                | jq -e -r '.labels[] | select(.name == \"Seeking Testers\")' > /dev/null; then\n                config_data[0]='codesign:true'\n                config_data[2]='package:true'\n              fi\n              ;;\n            push)\n              config_data=('codesign:true' 'notarize:false' 'package:true' 'config:RelWithDebInfo')\n              if [[ ${GITHUB_REF_NAME} =~ [0-9]+.[0-9]+.[0-9]+(-(rc|beta).+)? ]]; then\n                config_data[1]='notarize:true'\n                config_data[3]='config:Release'\n              fi\n              ;;\n            workflow_dispatch)\n              config_data=('codesign:true' 'notarize:false' 'package:false' 'config:RelWithDebInfo')\n              ;;\n            schedule)\n              config_data=('codesign:true' 'notarize:false' 'package:true' 'config:RelWithDebInfo')\n              ;;\n            *) ;;\n          esac\n\n          for config in \"${config_data[@]}\"; do\n            IFS=':' read -r key value <<< \"${config}\"\n            echo \"${key}=${value}\" >> $GITHUB_OUTPUT\n          done\n          echo \"commitHash=${GITHUB_SHA:0:9}\" >> $GITHUB_OUTPUT\n\n  macos-build:\n    name: macOS 🍏\n    runs-on: macos-15\n    needs: check-event\n    strategy:\n      fail-fast: false\n      matrix:\n        target: [arm64, x86_64]\n    defaults:\n      run:\n        shell: zsh --no-rcs --errexit --pipefail {0}\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n\n      - name: Set Up Environment 🔧\n        id: setup\n        run: |\n          : Set Up Environment 🔧\n          if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n          print '::group::Enable Xcode 26.1'\n          sudo xcode-select --switch /Applications/Xcode_26.1.app/Contents/Developer\n          print '::endgroup::'\n\n          print '::group::Clean Homebrew Environment'\n          local -a unwanted_formulas=()\n          local -a remove_formulas=()\n          for formula (${unwanted_formulas}) {\n            if [[ -d ${HOMEBREW_PREFIX}/Cellar/${formula} ]] remove_formulas+=(${formula})\n          }\n\n          if (( #remove_formulas )) brew uninstall --ignore-dependencies ${remove_formulas}\n          print '::endgroup::'\n\n          local -A arch_names=(x86_64 intel arm64 apple)\n          print \"cpuName=${arch_names[${{ matrix.target }}]}\" >> $GITHUB_OUTPUT\n\n          local xcode_cas_path=\"${HOME}/Library/Developer/Xcode/DerivedData/CompilationCache.noindex\"\n\n          if ! [[ -d ${xcode_cas_path} ]] mkdir -p ${xcode_cas_path}\n\n          print \"xcodeCasPath=${xcode_cas_path}\" >> $GITHUB_OUTPUT\n\n      - uses: actions/cache/restore@v4\n        id: xcode-cache\n        with:\n          path: ${{ steps.setup.outputs.xcodeCasPath }}\n          key: ${{ runner.os }}-xcode-${{ matrix.target }}-${{ needs.check-event.outputs.config }}\n          restore-keys: |\n            ${{ runner.os }}-xcode-${{ matrix.target }}-\n\n      - name: Set Up Code Signing 🔑\n        uses: ./.github/actions/setup-macos-codesigning\n        if: fromJSON(needs.check-event.outputs.codesign)\n        id: codesign\n        with:\n          codesignIdentity: ${{ secrets.MACOS_SIGNING_IDENTITY }}\n          codesignCertificate: ${{ secrets.MACOS_SIGNING_CERT }}\n          certificatePassword: ${{ secrets.MACOS_SIGNING_CERT_PASSWORD }}\n          keychainPassword: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}\n          provisioningProfile: ${{ secrets.MACOS_SIGNING_PROVISIONING_PROFILE }}\n          notarizationUser: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}\n          notarizationPassword: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}\n\n      - name: Build OBS Studio 🧱\n        uses: ./.github/actions/build-obs\n        env:\n          TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}\n          TWITCH_HASH: ${{ secrets.TWITCH_HASH }}\n          RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}\n          RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}\n          YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}\n          YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}\n          YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}\n          YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}\n          XCODE_CAS_PATH: ${{ steps.setup.outputs.xcodeCasPath }}\n        with:\n          target: ${{ matrix.target }}\n          config: ${{ needs.check-event.outputs.config }}\n          codesign: ${{ fromJSON(needs.check-event.outputs.codesign) }}\n          codesignIdent: ${{ steps.codesign.outputs.codesignIdent }}\n          codesignTeam: ${{ steps.codesign.outputs.codesignTeam }}\n          provisioningProfileUUID: ${{ steps.codesign.outputs.provisioningProfileUUID }}\n\n      - name: Package OBS Studio 📀\n        uses: ./.github/actions/package-obs\n        with:\n          target: ${{ matrix.target }}\n          config: ${{ needs.check-event.outputs.config }}\n          package: ${{ fromJSON(needs.check-event.outputs.package) }}\n          codesign: ${{ fromJSON(needs.check-event.outputs.codesign) && fromJSON(steps.codesign.outputs.haveCodesignIdent) }}\n          codesignIdent: ${{ steps.codesign.outputs.codesignIdent }}\n          notarize: ${{ fromJSON(needs.check-event.outputs.notarize) && fromJSON(steps.codesign.outputs.haveNotarizationUser) }}\n          codesignUser: ${{ secrets.MACOS_NOTARIZATION_USERNAME }}\n          codesignPass: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }}\n\n      - name: Upload Artifacts 📡\n        uses: actions/upload-artifact@v4\n        with:\n          name: obs-studio-macos-${{ matrix.target }}-${{ needs.check-event.outputs.commitHash }}\n          path: ${{ github.workspace }}/build_macos/obs-studio-*-macos-${{ steps.setup.outputs.cpuName }}.*\n\n      - name: Upload Debug Symbol Artifacts 🪲\n        uses: actions/upload-artifact@v4\n        if: ${{ needs.check-event.outputs.config == 'Release' }}\n        with:\n          name: obs-studio-macos-${{ matrix.target }}-${{ needs.check-event.outputs.commitHash }}-dSYMs\n          path: ${{ github.workspace }}/build_macos/obs-studio-*-macos-${{ steps.setup.outputs.cpuName }}-dSYMs.tar.xz\n\n      - uses: actions/cache/save@v4\n        if: github.event_name != 'pull_request' && steps.xcode-cache.outputs.cache-hit != 'true'\n        with:\n          path: ${{ steps.setup.outputs.xcodeCasPath }}\n          key: ${{ runner.os }}-xcode-${{ matrix.target }}-${{ needs.check-event.outputs.config }}\n\n  ubuntu-build:\n    name: Ubuntu 🐧\n    strategy:\n      matrix:\n        os: [ubuntu-24.04]\n    runs-on: ${{ matrix.os }}\n    needs: check-event\n    defaults:\n      run:\n        shell: bash\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n\n      - uses: actions/cache/restore@v4\n        id: ccache-cache\n        with:\n          path: ${{ github.workspace }}/.ccache\n          key: ${{ runner.os }}-${{ matrix.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }}\n          restore-keys: |\n            ${{ runner.os }}-${{ matrix.os }}-ccache-x86_64-\n\n      - name: Build OBS Studio 🧱\n        uses: ./.github/actions/build-obs\n        env:\n          TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}\n          TWITCH_HASH: ${{ secrets.TWITCH_HASH }}\n          RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}\n          RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}\n          YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}\n          YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}\n          YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}\n          YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}\n        with:\n          target: x86_64\n          config: ${{ needs.check-event.outputs.config }}\n\n      - name: Package OBS Studio 📀\n        uses: ./.github/actions/package-obs\n        with:\n          target: x86_64\n          config: ${{ needs.check-event.outputs.config }}\n          package: ${{ fromJSON(needs.check-event.outputs.package) }}\n\n      - name: Upload Source Tarball 🗜️\n        uses: actions/upload-artifact@v4\n        with:\n          name: obs-studio-${{ matrix.os }}-sources-${{ needs.check-event.outputs.commitHash }}\n          path: ${{ github.workspace }}/build_ubuntu/obs-studio-*-sources.*\n\n      - name: Upload Artifacts 📡\n        uses: actions/upload-artifact@v4\n        with:\n          name: obs-studio-${{ matrix.os }}-x86_64-${{ needs.check-event.outputs.commitHash }}\n          path: ${{ github.workspace }}/build_ubuntu/obs-studio-*-x86_64-ubuntu-gnu.*\n\n      - name: Upload Debug Symbol Artifacts 🪲\n        uses: actions/upload-artifact@v4\n        if: ${{ fromJSON(needs.check-event.outputs.package) }}\n        with:\n          name: obs-studio-${{ matrix.os }}-x86_64-${{ needs.check-event.outputs.commitHash }}-dbgsym\n          path: ${{ github.workspace }}/build_ubuntu/obs-studio-*-x86_64-ubuntu-gnu-dbgsym.ddeb\n\n      - uses: actions/cache/save@v4\n        if: github.event_name != 'pull_request' && steps.ccache-cache.outputs.cache-hit != 'true'\n        with:\n          path: ${{ github.workspace }}/.ccache\n          key: ${{ runner.os }}-${{ matrix.os }}-ccache-x86_64-${{ needs.check-event.outputs.config }}\n\n  flatpak-build:\n    name: Flatpak 📦\n    runs-on: ubuntu-24.04\n    needs: check-event\n    defaults:\n      run:\n        shell: bash\n    env:\n      FLATPAK_BUILD_SHARE_PATH: flatpak_app/files/share\n    container:\n      image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08\n      options: --privileged\n      volumes:\n        - /usr/local/lib/android:/to_clean/android\n        - /opt/hostedtoolcache/CodeQL:/to_clean/codeql\n        - /usr/local/.ghcup:/to_clean/ghcup\n        - /opt/hostedtoolcache/Python:/to_clean/python\n        - /usr/share/swift:/to_clean/swift\n        - /usr/share/dotnet:/to_clean/dotnet\n    steps:\n      - name: Prepare build space\n        shell: bash\n        run: |\n          : Prepare build space\n\n          echo ::group::Available storage\n          df -h\n          echo ::endgroup::\n\n          echo ::group::Remove Android stuff\n          rm -rf /to_clean/android/*\n          echo ::endgroup::\n\n          echo ::group::Remove CodeQL stuff\n          rm -rf /to_clean/codeql/*\n          echo ::endgroup::\n\n          echo ::group::Remove GHCup stuff\n          rm -rf /to_clean/ghcup/*\n          echo ::endgroup::\n\n          echo ::group::Remove Python stuff\n          rm -rf /to_clean/python/*\n          echo ::endgroup::\n\n          echo ::group::Remove Swift stuff\n          rm -rf /to_clean/swift/*\n          echo ::endgroup::\n\n          echo ::group::Remove .NET stuff\n          rm -rf /to_clean/dotnet/*\n          echo ::endgroup::\n\n          echo ::group::Available storage\n          df -h\n          echo ::endgroup::\n\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n          set-safe-directory: ${{ env.GITHUB_WORKSPACE }}\n\n      - name: Set Up Environment 🔧\n        id: setup\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          : Set Up Environment 🔧\n          if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n          git config --global --add safe.directory \"${GITHUB_WORKSPACE}\"\n\n          cache_key='flatpak-builder-${{ hashFiles('build-aux/com.obsproject.Studio.json') }}'\n          cache_ref='master'\n          read -r id key size unit created accessed <<< \\\n            \"$(gh cache list --ref \"refs/heads/${cache_ref}\" --key \"${cache_key}-x86_64\" | head -1)\"\n\n          if [[ \"${key}\" ]]; then\n            echo \"cacheHit=true\" >> $GITHUB_OUTPUT\n          else\n            echo \"cacheHit=false\" >> $GITHUB_OUTPUT\n          fi\n\n          echo \"cacheKey=${cache_key}\" >> $GITHUB_OUTPUT\n\n      - name: Validate Flatpak manifest\n        uses: ./.github/actions/flatpak-builder-lint\n        with:\n          artifact: manifest\n          path: build-aux/com.obsproject.Studio.json\n\n      - name: Build Flatpak Manifest 🧾\n        uses: flatpak/flatpak-github-actions/flatpak-builder@b8a638469ea7ec62844d7b6e487b697e6f249576\n        with:\n          build-bundle: ${{ fromJSON(needs.check-event.outputs.package) }}\n          bundle: obs-studio-flatpak-${{ needs.check-event.outputs.commitHash }}.flatpak\n          manifest-path: ${{ github.workspace }}/build-aux/com.obsproject.Studio.json\n          cache: ${{ fromJSON(steps.setup.outputs.cacheHit) || (github.event_name == 'push' && github.ref_name == 'master')}}\n          restore-cache: ${{ fromJSON(steps.setup.outputs.cacheHit) }}\n          cache-key: ${{ steps.setup.outputs.cacheKey }}\n          mirror-screenshots-url: https://dl.flathub.org/media\n\n      - name: Validate AppStream\n        uses: ./.github/actions/flatpak-builder-lint\n        with:\n          artifact: appstream\n          path: ${{ env.FLATPAK_BUILD_SHARE_PATH }}/metainfo/com.obsproject.Studio.metainfo.xml\n\n      - name: Verify Icon and Metadata in app-info\n        working-directory: ${{ env.FLATPAK_BUILD_SHARE_PATH }}\n        run: |\n          : Verify Icon and Metadata in app-info\n          test -f app-info/icons/flatpak/128x128/com.obsproject.Studio.png || { echo \"::error::Missing 128x128 icon in app-info\"; exit 1; }\n          test -f app-info/xmls/com.obsproject.Studio.xml.gz || { echo \"::error::Missing com.obsproject.Studio.xml.gz in app-info\"; exit 1; }\n\n      - name: Validate build directory\n        uses: ./.github/actions/flatpak-builder-lint\n        with:\n          artifact: builddir\n          path: flatpak_app\n\n      - name: Validate repository\n        uses: ./.github/actions/flatpak-builder-lint\n        with:\n          artifact: repo\n          path: repo\n\n  windows-build:\n    name: Windows 🪟\n    strategy:\n      matrix:\n        architecture: [x64, arm64]\n    runs-on: windows-2022\n    needs: check-event\n    defaults:\n      run:\n        shell: pwsh\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n\n      - name: Build OBS Studio 🧱\n        uses: ./.github/actions/build-obs\n        env:\n          TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}\n          TWITCH_HASH: ${{ secrets.TWITCH_HASH }}\n          RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}\n          RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}\n          YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}\n          YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}\n          YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}\n          YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}\n          GPU_PRIORITY_VAL: ${{ secrets.GPU_PRIORITY_VAL }}\n        with:\n          target: ${{ matrix.architecture }}\n          config: ${{ needs.check-event.outputs.config }}\n\n      - name: Package OBS Studio 📀\n        uses: ./.github/actions/package-obs\n        with:\n          target: ${{ matrix.architecture }}\n          config: ${{ needs.check-event.outputs.config }}\n          package: ${{ fromJSON(needs.check-event.outputs.package) }}\n\n      - name: Upload Artifacts 📡\n        uses: actions/upload-artifact@v4\n        with:\n          name: obs-studio-windows-${{ matrix.architecture }}-${{ needs.check-event.outputs.commitHash }}\n          path: ${{ github.workspace }}/build_${{ matrix.architecture }}/obs-studio-*-windows-${{ matrix.architecture }}.zip\n"
  },
  {
    "path": ".github/workflows/check-format.yaml",
    "content": "name: Check Code Formatting 🛠️\non:\n  workflow_call:\njobs:\n  clang-format:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: clang-format Check 🐉\n        id: clang-format\n        uses: ./.github/actions/run-clang-format\n        with:\n          failCondition: error\n\n  swift-format:\n    runs-on: macos-15\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: swift-format Check 🔥\n        id: swift-format\n        uses: ./.github/actions/run-swift-format\n        with:\n          failCondition: error\n\n  gersemi:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: gersemi Check 🎛️\n        id: gersemi\n        uses: ./.github/actions/run-gersemi\n        with:\n          failCondition: error\n\n  flatpak-validator:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: Flatpak Manifest Check 📦\n        id: flatpak-check\n        uses: ./.github/actions/flatpak-manifest-validator\n        with:\n          failCondition: error\n\n  qt-xml-validator:\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: Qt XML Check 🖼️\n        id: qt-xml-check\n        uses: ./.github/actions/qt-xml-validator\n        with:\n          failCondition: error\n"
  },
  {
    "path": ".github/workflows/dispatch.yaml",
    "content": "name: Dispatch\nrun-name: Dispatched Repository Actions - ${{ inputs.job }} ⌛️\non:\n  workflow_dispatch:\n    inputs:\n      job:\n        description: Dispatch job to run\n        required: true\n        type: choice\n        options:\n          - steam\n          - services\n          - translations\n          - documentation\n          - patches\n      ref:\n        description: GitHub reference to use for job\n        type: string\n        required: false\n      customAssetWindows:\n        description: Custom Windows build for Steam Upload\n        type: string\n        required: false\n      customAssetMacOSApple:\n        description: Custom macOS Apple Silicon build for Steam Upload\n        type: string\n        required: false\n      customAssetMacOSIntel:\n        description: Custom macOS Intel build for Steam Upload\n        type: string\n        required: false\n      channel:\n        description: Channel to use when generating Windows update files\n        type: string\n        required: false\npermissions:\n  contents: write\njobs:\n  services-validation:\n    name: Validate Services 🕵️\n    if: github.repository_owner == 'obsproject' && inputs.job == 'services'\n    runs-on: macos-15\n    permissions:\n      checks: write\n      contents: write\n      pull-requests: write\n    steps:\n      - uses: actions/checkout@v4\n      - name: Check for Defunct Services 📉\n        uses: ./.github/actions/services-validator\n        with:\n          repositorySecret: ${{ secrets.GITHUB_TOKEN }}\n          checkApiSecret: ${{ secrets.CHECK_SERVERS_API_KEY }}\n          checkApiServers: ${{ secrets.CHECK_SERVERS_LIST }}\n          runSchemaChecks: true\n          runServiceChecks: true\n          createPullRequest: true\n\n  download-language-files:\n    name: Download Language Files 🌐\n    if: github.repository_owner == 'obsproject' && inputs.job == 'translations'\n    runs-on: ubuntu-24.04\n    env:\n      CROWDIN_PAT: ${{ secrets.CROWDIN_SYNC_CROWDIN_PAT }}\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          token: ${{ secrets.CROWDIN_SYNC_GITHUB_PAT }}\n          fetch-depth: 0\n      - uses: obsproject/obs-crowdin-sync/download@430665179ed13233af2d83ec192c2ae8c40a29ae\n\n  steam-upload:\n    name: Upload Steam Builds 🚂\n    if: github.repository_owner == 'obsproject' && inputs.job == 'steam'\n    runs-on: macos-15\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/steam-upload\n        with:\n          steamSecret: ${{ secrets.STEAM_SHARED_SECRET }}\n          steamUser: ${{ secrets.STEAM_USER }}\n          steamPassword: ${{ secrets.STEAM_PASSWORD }}\n          tagName: ${{ inputs.ref }}\n          customAssetWindows: ${{ inputs.customAssetWindows }}\n          customAssetMacOSApple: ${{ inputs.customAssetMacOSApple }}\n          customAssetMacOSIntel: ${{ inputs.customAssetMacOSIntel }}\n          workflowSecret: ${{ github.token }}\n          preview: false\n\n  update-documentation:\n    name: Update Documentation 📖\n    if: github.repository_owner == 'obsproject' && inputs.job == 'documentation'\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/generate-docs\n\n  update-documentation-cloudflare:\n    name: Update Documentation for Cloudflare ☁️\n    if: github.repository_owner == 'obsproject' && inputs.job == 'documentation'\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/generate-docs\n        with:\n          disableLinkExtensions: true\n\n  deploy-documentation:\n    name: Deploy Documentation to Cloudflare ☁️\n    if: github.repository_owner == 'obsproject' && inputs.job == 'documentation'\n    runs-on: ubuntu-24.04\n    needs: update-documentation-cloudflare\n    defaults:\n      run:\n        shell: bash\n    environment:\n      name: cf-pages-deploy\n    steps:\n      - name: Get Commit Information 🆔\n        id: setup\n        run: |\n          : Get Commit Hash 🆔\n          echo \"commitHash=${GITHUB_SHA:0:9}\" >> $GITHUB_OUTPUT\n\n      - uses: actions/download-artifact@v4\n        with:\n          name: OBS Studio Docs (No Extensions) ${{ steps.setup.outputs.commitHash }}\n          path: docs\n\n      - name: Publish to Live Page\n        uses: cloudflare/wrangler-action@4c10c1822abba527d820b29e6333e7f5dac2cabd\n        with:\n          workingDirectory: docs\n          apiToken: ${{ secrets.CF_API_TOKEN }}\n          accountId: ${{ secrets.CF_ACCOUNT_ID }}\n          command: pages publish . --project-name=${{ vars.CF_PAGES_PROJECT }} --commit-hash='${{ steps.setup.outputs.commitHash }}'\n\n  windows-patches:\n    name: Create Windows Patches 🩹\n    if: github.repository_owner == 'obsproject' && inputs.job == 'patches'\n    runs-on: windows-2022\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/windows-patches\n        with:\n          tagName: ${{ inputs.ref }}\n          workflowSecret: ${{ github.token }}\n          channel: ${{ inputs.channel }}\n          gcsAccessKeyId: ${{ secrets.GCS_ACCESS_KEY_ID }}\n          gcsAccessKeySecret: ${{ secrets.GCS_ACCESS_KEY_SECRET }}\n"
  },
  {
    "path": ".github/workflows/pr-pull.yaml",
    "content": "name: Pull\nrun-name: ${{ github.event.pull_request.title }} pull request run 🚀\non:\n  workflow_dispatch:\n  pull_request:\n    paths-ignore:\n      - '**.md'\n    branches: [master]\n    types: [ opened, synchronize, reopened ]\npermissions:\n  contents: read\nconcurrency:\n  group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'\n  cancel-in-progress: true\njobs:\n  check-format:\n    name: Format 🔍\n    uses: ./.github/workflows/check-format.yaml\n    permissions:\n      contents: read\n\n  build-project:\n    name: Build 🧱\n    uses: ./.github/workflows/build-project.yaml\n    secrets: inherit\n    permissions:\n      contents: read\n\n  compatibility-validation:\n    name: Validate Compatibility 🕵️\n    if: github.base_ref == 'master'\n    runs-on: ubuntu-24.04\n    permissions:\n      checks: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Check for Changed Files ✅\n        uses: ./.github/actions/check-changes\n        id: checks\n        with:\n          baseRef: origin/${{ github.base_ref }}\n          checkGlob: plugins/win-capture/data/*.json\n\n      - name: Check for Invalid Compatibility Data 📉\n        if: fromJSON(steps.checks.outputs.hasChangedFiles)\n        uses: ./.github/actions/compatibility-validator\n\n  services-validation:\n    name: Validate Services 🕵️\n    if: github.base_ref == 'master'\n    runs-on: ubuntu-24.04\n    permissions:\n      checks: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Check for Changed Files ✅\n        uses: ./.github/actions/check-changes\n        id: checks\n        with:\n          baseRef: origin/${{ github.base_ref }}\n          checkGlob: plugins/rtmp-services/data/*.json\n\n      - name: Check Services JSON Schema 📉\n        if: fromJSON(steps.checks.outputs.hasChangedFiles)\n        uses: ./.github/actions/services-validator\n        with:\n          repositorySecret: ${{ secrets.GITHUB_TOKEN }}\n          runSchemaChecks: true\n          runServiceChecks: false\n\n  update-documentation:\n    name: Update Documentation 📖\n    if: github.repository_owner == 'obsproject' && github.base_ref == 'master'\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Check for Changed Files ✅\n        uses: ./.github/actions/check-changes\n        id: checks\n        with:\n          baseRef: origin/${{ github.base_ref }}\n          checkGlob: docs/sphinx\n\n      - uses: ./.github/actions/generate-docs\n        if: fromJSON(steps.checks.outputs.hasChangedFiles)\n"
  },
  {
    "path": ".github/workflows/publish.yaml",
    "content": "name: Publish\nrun-name: Publish Repository Actions 🛫\non:\n  release:\n    types:\n      - published\n    branches:\n      - master\n      - 'release/**'\npermissions:\n  contents: read\nconcurrency:\n  group: '${{ github.workflow }} @ ${{ github.head_ref || github.ref }}'\n  cancel-in-progress: true\njobs:\n  check-tag:\n    name: Check Release Tag\n    if: github.repository_owner == 'obsproject'\n    runs-on: ubuntu-24.04\n    outputs:\n      validTag: ${{ steps.check.outputs.validTag }}\n      flatpakMatrix: ${{ steps.check.outputs.flatpakMatrix }}\n      updateChannel: ${{ steps.check.outputs.updateChannel }}\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n          set-safe-directory: ${{ env.GITHUB_WORKSPACE }}\n\n      - name: Check Release Tag ☑️\n        id: check\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          : Check Release Tag ☑️\n          if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n          shopt -s extglob\n\n          case \"${GITHUB_REF_NAME}\" in\n            +([0-9]).+([0-9]).+([0-9]) )\n              lastPreRelease=\"$(gh release list --exclude-drafts --limit 10 --json \"publishedAt,tagName,isPrerelease\" \\\n                --jq '[.[] | select(.isPrerelease == true)] | first | .tagName')\"\n              currentRelease=\"${GITHUB_REF_NAME}\"\n              isPreReleaseAhead=false\n\n              printf '%s\\n%s\\n' \"${currentRelease}\" \"${lastPreRelease}\" | sort --version-sort --reverse --check=quiet &&\n                isPreReleaseAhead=false || isPreReleaseAhead=true\n\n              # Edge case: Sort considers the non-suffixed version older than a suffixed one\n              if ${isPreReleaseAhead} && [[ \"${currentRelease}\" == \"${lastPreRelease//-*}\" ]]; then\n                isPreReleaseAhead=false\n              fi\n\n              echo 'validTag=true' >> $GITHUB_OUTPUT\n              if ! ${isPreReleaseAhead}; then\n                echo 'flatpakMatrix=[\"beta\", \"stable\"]' >> $GITHUB_OUTPUT\n              else\n                echo 'flatpakMatrix=[\"stable\"]' >> $GITHUB_OUTPUT\n              fi\n              echo 'updateChannel=stable' >> $GITHUB_OUTPUT\n              ;;\n            +([0-9]).+([0-9]).+([0-9])-@(beta|rc)*([0-9]) )\n              echo 'validTag=true' >> $GITHUB_OUTPUT\n              echo 'flatpakMatrix=[\"beta\"]' >> $GITHUB_OUTPUT\n              echo 'updateChannel=beta' >> $GITHUB_OUTPUT\n              ;;\n            *) echo 'validTag=false' >> $GITHUB_OUTPUT ;;\n          esac\n\n  flatpak-publish:\n    name: Flathub 📦\n    needs: check-tag\n    if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag)\n    runs-on: ubuntu-24.04\n    defaults:\n      run:\n        shell: bash\n    env:\n      FLATPAK_BUILD_SHARE_PATH: flatpak_app/files/share\n      TWITCH_CLIENTID: ${{ secrets.TWITCH_CLIENT_ID }}\n      TWITCH_HASH: ${{ secrets.TWITCH_HASH }}\n      RESTREAM_CLIENTID: ${{ secrets.RESTREAM_CLIENTID }}\n      RESTREAM_HASH: ${{ secrets.RESTREAM_HASH }}\n      YOUTUBE_CLIENTID: ${{ secrets.YOUTUBE_CLIENTID }}\n      YOUTUBE_CLIENTID_HASH: ${{ secrets.YOUTUBE_CLIENTID_HASH }}\n      YOUTUBE_SECRET: ${{ secrets.YOUTUBE_SECRET }}\n      YOUTUBE_SECRET_HASH: ${{ secrets.YOUTUBE_SECRET_HASH }}\n    container:\n      image: ghcr.io/flathub-infra/flatpak-github-actions:freedesktop-25.08\n      options: --privileged\n      volumes:\n        - /usr/local/lib/android:/to_clean/android\n        - /opt/hostedtoolcache/CodeQL:/to_clean/codeql\n        - /usr/local/.ghcup:/to_clean/ghcup\n        - /opt/hostedtoolcache/Python:/to_clean/python\n        - /usr/share/swift:/to_clean/swift\n        - /usr/share/dotnet:/to_clean/dotnet\n    strategy:\n      matrix:\n        branch: ${{ fromJSON(needs.check-tag.outputs.flatpakMatrix) }}\n    steps:\n      - name: Prepare build space\n        shell: bash\n        run: |\n          : Prepare build space\n\n          echo ::group::Available storage\n          df -h\n          echo ::endgroup::\n\n          echo ::group::Remove Android stuff\n          rm -rf /to_clean/android/*\n          echo ::endgroup::\n\n          echo ::group::Remove CodeQL stuff\n          rm -rf /to_clean/codeql/*\n          echo ::endgroup::\n\n          echo ::group::Remove GHCup stuff\n          rm -rf /to_clean/ghcup/*\n          echo ::endgroup::\n\n          echo ::group::Remove Python stuff\n          rm -rf /to_clean/python/*\n          echo ::endgroup::\n\n          echo ::group::Remove Swift stuff\n          rm -rf /to_clean/swift/*\n          echo ::endgroup::\n\n          echo ::group::Remove .NET stuff\n          rm -rf /to_clean/dotnet/*\n          echo ::endgroup::\n\n          echo ::group::Available storage\n          df -h\n          echo ::endgroup::\n\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n          set-safe-directory: ${{ env.GITHUB_WORKSPACE }}\n\n      - name: Set Up Environment 🔧\n        id: setup\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          : Set Up Environment 🔧\n          if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n          git config --global --add safe.directory \"${GITHUB_WORKSPACE}\"\n\n          cache_key='flatpak-builder-${{ hashFiles('build-aux/com.obsproject.Studio.json') }}'\n          cache_ref='master'\n          read -r id key size unit created accessed <<< \\\n            \"$(gh cache list --ref \"refs/heads/${cache_ref}\" --key \"${cache_key}-x86_64\" | head -1)\"\n\n          if [[ \"${key}\" ]]; then\n            echo \"cacheHit=true\" >> $GITHUB_OUTPUT\n          else\n            echo \"cacheHit=false\" >> $GITHUB_OUTPUT\n          fi\n\n          echo \"cacheKey=${cache_key}\" >> $GITHUB_OUTPUT\n          echo \"commitHash=${GITHUB_SHA:0:9}\" >> $GITHUB_OUTPUT\n\n      - name: Validate Flatpak manifest\n        uses: ./.github/actions/flatpak-builder-lint\n        with:\n          artifact: manifest\n          path: build-aux/com.obsproject.Studio.json\n\n      - name: Build Flatpak Manifest\n        uses: flatpak/flatpak-github-actions/flatpak-builder@b8a638469ea7ec62844d7b6e487b697e6f249576\n        with:\n          build-bundle: false\n          manifest-path: ${{ github.workspace }}/build-aux/com.obsproject.Studio.json\n          cache: ${{ fromJSON(steps.setup.outputs.cacheHit) }}\n          cache-key: ${{ steps.setup.outputs.cacheKey }}\n          mirror-screenshots-url: https://dl.flathub.org/media\n          branch: ${{ matrix.branch }}\n\n      - name: Validate AppStream\n        uses: ./.github/actions/flatpak-builder-lint\n        with:\n          artifact: appstream\n          path: ${{ env.FLATPAK_BUILD_SHARE_PATH }}/metainfo/com.obsproject.Studio.metainfo.xml\n\n      - name: Verify Icon and Metadata in app-info\n        working-directory: ${{ env.FLATPAK_BUILD_SHARE_PATH }}\n        run: |\n          : Verify Icon and Metadata in app-info\n          test -f app-info/icons/flatpak/128x128/com.obsproject.Studio.png || { echo \"::error::Missing 128x128 icon in app-info\"; exit 1; }\n          test -f app-info/xmls/com.obsproject.Studio.xml.gz || { echo \"::error::Missing com.obsproject.Studio.xml.gz in app-info\"; exit 1; }\n\n      - name: Validate build directory\n        uses: ./.github/actions/flatpak-builder-lint\n        with:\n          artifact: builddir\n          path: flatpak_app\n\n      - name: Validate repository\n        uses: ./.github/actions/flatpak-builder-lint\n        with:\n          artifact: repo\n          path: repo\n\n      - name: Publish to Flathub Beta\n        uses: flatpak/flatpak-github-actions/flat-manager@b8a638469ea7ec62844d7b6e487b697e6f249576\n        if: ${{ matrix.branch == 'beta' }}\n        with:\n          flat-manager-url: https://hub.flathub.org/\n          repository: beta\n          token: ${{ secrets.FLATHUB_BETA_TOKEN }}\n          build-log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n\n      - name: Publish to Flathub\n        uses: flatpak/flatpak-github-actions/flat-manager@b8a638469ea7ec62844d7b6e487b697e6f249576\n        if: ${{ matrix.branch == 'stable' }}\n        with:\n          flat-manager-url: https://hub.flathub.org/\n          repository: stable\n          token: ${{ secrets.FLATHUB_TOKEN }}\n          build-log-url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\n\n  steam-upload:\n    name: Upload Steam Builds 🚂\n    needs: check-tag\n    if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag)\n    runs-on: macos-15\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/steam-upload\n        with:\n          steamSecret: ${{ secrets.STEAM_SHARED_SECRET }}\n          steamUser: ${{ secrets.STEAM_USER }}\n          steamPassword: ${{ secrets.STEAM_PASSWORD }}\n          workflowSecret: ${{ github.token }}\n          tagName: ${{ github.ref_name }}\n          preview: false\n\n  windows-patches:\n    name: Create Windows Patches 🩹\n    needs: check-tag\n    if: github.repository_owner == 'obsproject' && fromJSON(needs.check-tag.outputs.validTag)\n    runs-on: windows-2022\n    steps:\n      - uses: actions/checkout@v4\n      - uses: ./.github/actions/windows-patches\n        with:\n          tagName: ${{ github.ref_name }}\n          workflowSecret: ${{ github.token }}\n          channel: ${{ needs.check-tag.outputs.updateChannel }}\n          gcsAccessKeyId: ${{ secrets.GCS_ACCESS_KEY_ID }}\n          gcsAccessKeySecret: ${{ secrets.GCS_ACCESS_KEY_SECRET }}\n"
  },
  {
    "path": ".github/workflows/push.yaml",
    "content": "name: Push\nrun-name: ${{ github.ref_name }} push run 🚀\non:\n  push:\n    paths-ignore:\n      - '**.md'\n    branches:\n      - master\n      - 'release/**'\n    tags:\n      - '*'\npermissions:\n  contents: write\njobs:\n  check-format:\n    name: Format 🔍\n    if: github.ref_name == 'master'\n    uses: ./.github/workflows/check-format.yaml\n    permissions:\n      contents: read\n\n  build-project:\n    name: Build 🧱\n    uses: ./.github/workflows/build-project.yaml\n    secrets: inherit\n    permissions:\n      contents: read\n\n  compatibility-validation:\n    name: Validate Compatibility 🕵️\n    if: github.ref_name == 'master'\n    runs-on: ubuntu-24.04\n    permissions:\n      checks: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Check for Changed Files ✅\n        uses: ./.github/actions/check-changes\n        id: checks\n        with:\n          baseRef: ${{ github.event.before }}\n          checkGlob: plugins/win-capture/data/*.json\n\n      - name: Check for Invalid Compatibility Data 📉\n        if: fromJSON(steps.checks.outputs.hasChangedFiles)\n        uses: ./.github/actions/compatibility-validator\n        with:\n          repositorySecret: ${{ github.token }}\n\n  services-validation:\n    name: Validate Services 🕵️\n    if: github.ref_name == 'master'\n    runs-on: ubuntu-24.04\n    permissions:\n      checks: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Check for Changed Files ✅\n        uses: ./.github/actions/check-changes\n        id: checks\n        with:\n          baseRef: ${{ github.event.before }}\n          checkGlob: plugins/rtmp-services/data/*.json\n\n      - name: Check Services JSON Schema 📉\n        if: fromJSON(steps.checks.outputs.hasChangedFiles)\n        uses: ./.github/actions/services-validator\n        with:\n          repositorySecret: ${{ github.token }}\n          runSchemaChecks: true\n          runServiceChecks: false\n\n  update-documentation:\n    name: Update Documentation 📖\n    if: github.repository_owner == 'obsproject' && (github.ref_name == 'master' || github.ref_type == 'tag')\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Check for Changed Files ✅\n        if: github.ref_type != 'tag'\n        uses: ./.github/actions/check-changes\n        id: checks\n        with:\n          baseRef: ${{ github.event.before }}\n          checkGlob: '!(cmake*)'\n\n      - uses: ./.github/actions/generate-docs\n        if: github.ref_type == 'tag' || fromJSON(steps.checks.outputs.hasChangedFiles)\n        with:\n          disableLinkExtensions: ${{ github.ref_type == 'tag' }}\n\n  deploy-documentation:\n    name: Deploy Documentation to Cloudflare ☁️\n    if: github.repository_owner == 'obsproject' && github.ref_type == 'tag'\n    runs-on: ubuntu-24.04\n    needs: update-documentation\n    defaults:\n      run:\n        shell: bash\n    environment:\n      name: cf-pages-deploy\n    steps:\n      - name: Get Commit Information 🆔\n        id: setup\n        run: |\n          : Get Commit Hash 🆔\n          echo \"commitHash=${GITHUB_SHA:0:9}\" >> $GITHUB_OUTPUT\n\n      - uses: actions/download-artifact@v4\n        with:\n          name: OBS Studio Docs (No Extensions) ${{ steps.setup.outputs.commitHash }}\n          path: docs\n\n      - name: Set Up Redirects 🔄\n        run: |\n          : Set Up Redirects 🔄\n          if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n          echo \"/previous/27.2 https://obsproject.com/docs/27.2 302\" >> docs/_redirects\n          echo \"/previous/:major.:minor https://:major-:minor.${{ vars.CF_PAGES_PROJECT }}.pages.dev 302\" >> docs/_redirects\n\n      - name: Publish to Live Page\n        uses: cloudflare/wrangler-action@f84a562284fc78278ff9052435d9526f9c718361\n        with:\n          workingDirectory: docs\n          apiToken: ${{ secrets.CF_API_TOKEN }}\n          accountId: ${{ secrets.CF_ACCOUNT_ID }}\n          command: pages publish . --project-name=${{ vars.CF_PAGES_PROJECT }} --commit-hash='${{ steps.setup.outputs.commitHash }}'\n\n  create-appcast:\n    name: Create Sparkle Appcast 🎙️\n    if: github.repository_owner == 'obsproject' && github.ref_type == 'tag'\n    runs-on: macos-15\n    needs: build-project\n    strategy:\n      fail-fast: false\n      matrix:\n        target: [arm64, x86_64]\n    defaults:\n      run:\n        shell: zsh --no-rcs --errexit --pipefail {0}\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n          ref: ${{ github.ref }}\n\n      - name: Set Up Environment 🔧\n        id: setup\n        run: |\n          : Set Up Environment 🔧\n          if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n          local channel='stable'\n          if [[ ${GITHUB_REF_NAME} == *(beta|rc)* ]] {\n            channel='beta'\n          }\n\n          local -A arch_names=(x86_64 intel arm64 apple)\n          print \"cpuName=${arch_names[${{ matrix.target }}]}\" >> $GITHUB_OUTPUT\n          print \"commitHash=${GITHUB_SHA:0:9}\" >> $GITHUB_OUTPUT\n          print \"channel=${channel}\" >> $GITHUB_OUTPUT\n\n      - name: Download Artifact 📥\n        uses: actions/download-artifact@v4\n        with:\n          name: obs-studio-macos-${{ matrix.target }}-${{ steps.setup.outputs.commitHash }}\n\n      - name: Generate Appcast 🎙️\n        id: generate-appcast\n        uses: ./.github/actions/sparkle-appcast\n        with:\n          sparklePrivateKey: ${{ secrets.SPARKLE_PRIVATE_KEY }}\n          baseImage: ${{ github.workspace }}/obs-studio-*-macos-${{ steps.setup.outputs.cpuName }}.dmg\n          channel: ${{ steps.setup.outputs.channel }}\n          count: 1\n          urlPrefix: 'https://cdn-fastly.obsproject.com/downloads'\n          customTitle: 'OBS Studio'\n          customLink: 'https://obsproject.com/'\n\n      - name: Upload Artifacts 📡\n        uses: actions/upload-artifact@v4\n        with:\n          name: macos-sparkle-update-${{ matrix.target }}\n          path: ${{ github.workspace }}/output\n\n  merge-appcasts:\n    runs-on: ubuntu-24.04\n    needs: create-appcast\n    steps:\n      - name: Merge Appcasts\n        uses: actions/upload-artifact/merge@v4\n        with:\n          name: macos-sparkle-update\n          pattern: macos-sparkle-update-*\n          delete-merged: true\n\n  sign-windows-build:\n    name: Windows Signing ✍️\n    uses: obsproject/obs-studio/.github/workflows/sign-windows.yaml@1933c9e3205d67068f2c858a52ec06b68e3e65ee\n    if: github.repository_owner == 'obsproject' && github.ref_type == 'tag'\n    needs: build-project\n    permissions:\n      contents: 'read'\n      id-token: 'write'\n      attestations: 'write'\n    secrets: inherit\n\n  create-release:\n    name: Create Release 🛫\n    if: github.ref_type == 'tag'\n    runs-on: ubuntu-24.04\n    needs: [build-project, sign-windows-build]\n    defaults:\n      run:\n        shell: bash\n    steps:\n      - name: Check Release Tag ☑️\n        id: check\n        run: |\n          : Check Release Tag ☑️\n          if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n          shopt -s extglob\n\n          case \"${GITHUB_REF_NAME}\" in\n            +([0-9]).+([0-9]).+([0-9]) )\n              echo 'validTag=true' >> $GITHUB_OUTPUT\n              echo 'prerelease=false' >> $GITHUB_OUTPUT\n              echo \"version=${GITHUB_REF_NAME}\" >> $GITHUB_OUTPUT\n              ;;\n            +([0-9]).+([0-9]).+([0-9])-@(beta|rc)*([0-9]) )\n              echo 'validTag=true' >> $GITHUB_OUTPUT\n              echo 'prerelease=true' >> $GITHUB_OUTPUT\n              echo \"version=${GITHUB_REF_NAME}\" >> $GITHUB_OUTPUT\n              ;;\n            *) echo 'validTag=false' >> $GITHUB_OUTPUT ;;\n          esac\n\n      - name: Download Build Artifacts 📥\n        uses: actions/download-artifact@v4\n        if: ${{ fromJSON(steps.check.outputs.validTag) }}\n\n      - name: Rename Files 🏷️\n        if: fromJSON(steps.check.outputs.validTag)\n        run: |\n          : Rename Files 🏷️\n          if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n          root_dir=\"${PWD}\"\n\n          commit_hash=\"${GITHUB_SHA:0:9}\"\n          macos_arm64_artifact_name=\"obs-studio-macos-arm64-${commit_hash}\"\n          macos_arm64_dsym_artifact_name=\"obs-studio-macos-arm64-${commit_hash}-dSYMs\"\n          macos_intel_artifact_name=\"obs-studio-macos-x86_64-${commit_hash}\"\n          macos_intel_dsym_artifact_name=\"obs-studio-macos-x86_64-${commit_hash}-dSYMs\"\n          ubuntu_2404_x86_64_artifact_name=\"obs-studio-ubuntu-24.04-x86_64-${commit_hash}\"\n          ubuntu_2404_x86_64_debug_name=\"obs-studio-ubuntu-24.04-x86_64-${commit_hash}-dbgsym\"\n          ubuntu_2404_sources_name=\"obs-studio-ubuntu-24.04-sources-${commit_hash}\"\n          windows_x64_artifact_name=\"obs-studio-windows-x64-${{ steps.check.outputs.version }}-signed\"\n          windows_x64_installer_name=\"obs-studio-windows-x64-${{ steps.check.outputs.version }}-installer\"\n          windows_x64_debug_name=\"obs-studio-windows-x64-${{ steps.check.outputs.version }}-pdbs\"\n          windows_arm64_artifact_name=\"obs-studio-windows-arm64-${{ steps.check.outputs.version }}-signed\"\n          windows_arm64_debug_name=\"obs-studio-windows-arm64-${{ steps.check.outputs.version }}-pdbs\"\n\n          echo '::group::Renaming Artifacts'\n          mv -v \"${macos_arm64_artifact_name}/\"obs-studio-*-macos-apple.dmg \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Apple.dmg\n          mv -v \"${macos_arm64_dsym_artifact_name}/\"obs-studio-*-macos-apple-dSYMs.tar.xz \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Apple-dSYMs.tar.xz\n          mv -v \"${macos_intel_artifact_name}/\"obs-studio-*-macos-intel.dmg \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Intel.dmg\n          mv -v \"${macos_intel_dsym_artifact_name}/\"obs-studio-*-macos-intel-dSYMs.tar.xz \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-macOS-Intel-dSYMs.tar.xz\n          mv -v \"${ubuntu_2404_x86_64_artifact_name}/\"obs-studio-*-x86_64-ubuntu-gnu.deb \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-24.04-x86_64.deb\n          mv -v \"${ubuntu_2404_x86_64_debug_name}/\"obs-studio-*-x86_64-ubuntu-gnu-dbgsym.ddeb \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-24.04-x86_64-dbsym.ddeb\n          mv -v \"${ubuntu_2404_sources_name}/\"obs-studio-*-sources.tar.gz \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-Sources.tar.gz\n          mv -v \"${windows_x64_artifact_name}/\"OBS-Studio-*.zip \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64.zip\n          mv -v \"${windows_x64_installer_name}/\"OBS-Studio-*.exe \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64-Installer.exe\n          mv -v \"${windows_x64_debug_name}/\"OBS-Studio-*-pdbs.zip \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64-PDBs.zip\n          mv -v \"${windows_arm64_artifact_name}/\"OBS-Studio-*.zip \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-arm64.zip\n          mv -v \"${windows_arm64_debug_name}/\"OBS-Studio-*-pdbs.zip \\\n            \"${root_dir}\"/OBS-Studio-${{ steps.check.outputs.version }}-Windows-arm64-PDBs.zip\n          echo '::endgroup::'\n\n      - name: Generate Checksums 🪪\n        if: fromJSON(steps.check.outputs.validTag)\n        run: |\n          : Generate Checksums 🪪\n          if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n          shopt -s extglob\n\n          echo \"### Checksums\" > ${{ github.workspace }}/CHECKSUMS.txt\n          for file in ${{ github.workspace }}/@(*.deb|*.ddeb|*.dmg|*.tar.xz|*.tar.gz|*.exe|*.zip); do\n            echo \"    ${file##*/}: $(sha256sum \"${file}\" | cut -d \" \" -f 1)\" >> ${{ github.workspace }}/CHECKSUMS.txt\n          done\n\n      - name: Create Release 🛫\n        if: fromJSON(steps.check.outputs.validTag)\n        id: create_release\n        uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191\n        with:\n          draft: true\n          prerelease: ${{ fromJSON(steps.check.outputs.prerelease) }}\n          tag_name: ${{ steps.check.outputs.version }}\n          name: OBS Studio ${{ steps.check.outputs.version }}\n          body_path: ${{ github.workspace }}/CHECKSUMS.txt\n          files: |\n            ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-macOS-*.dmg\n            ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-macOS-*-dSYMs.tar.xz\n            ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-*.deb\n            ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Ubuntu-*.ddeb\n            ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Sources.tar.gz\n            ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64.zip\n            ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64-Installer.exe\n            ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-x64-PDBs.zip\n            ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-arm64.zip\n            ${{ github.workspace }}/OBS-Studio-${{ steps.check.outputs.version }}-Windows-arm64-PDBs.zip\n"
  },
  {
    "path": ".github/workflows/scheduled.yaml",
    "content": "name: Scheduled\nrun-name: Scheduled Repository Actions ⏰\non:\n  workflow_dispatch:\n  schedule:\n    - cron: 17 0 * * *\npermissions:\n  contents: write\nconcurrency:\n  group: '${{ github.workflow }} @ ${{ github.head_ref || github.ref }}'\n  cancel-in-progress: true\njobs:\n  services-availability:\n    name: Check Service Availability 🛜\n    if: github.repository_owner == 'obsproject'\n    runs-on: macos-15\n    permissions:\n      checks: write\n      contents: write\n      pull-requests: write\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n      - name: Set Up Homebrew 🍺\n        uses: Homebrew/actions/setup-homebrew@master\n      - name: Check for Defunct Services 📉\n        uses: ./.github/actions/services-validator\n        with:\n          repositorySecret: ${{ secrets.GITHUB_TOKEN }}\n          checkApiSecret: ${{ secrets.CHECK_SERVERS_API_KEY }}\n          checkApiServers: ${{ secrets.CHECK_SERVERS_LIST }}\n          runSchemaChecks: false\n          runServiceChecks: true\n          createPullRequest: true\n\n  cache-cleanup:\n    name: Cache Cleanup 🧹\n    runs-on: ubuntu-24.04\n    permissions:\n      actions: write\n    steps:\n      - name: Remove Stale Ccache Caches\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          : Remove Stale Ccache Caches\n\n          # The jq expressions below use multiple 'select' calls to filter\n          # each item in the array with the 'actions_caches' key.\n          # First it only selects objects whose 'ref' element has the value\n          # 'refs/heads/master', of those objects only those whose 'key'\n          # value matches the specified expression, before finally only\n          # selecting the 'id' and 'key' elements for a new object.\n          # The final 'join' command combines both elements with a semicolon\n          # into a raw string which can then be parsed directly.\n\n          echo '::group::Processing master branch cache entries'\n          while IFS=\";\" read -r cache_id cache_name; do\n            if [[ \"${cache_name}\" ]]; then\n              result=true\n              gh api -X DELETE repos/${GITHUB_REPOSITORY}/actions/caches?key=${cache_name} --jq '.total_count' &> /dev/null || result=false\n\n              if ${result}; then\n                echo \"Deleted cache entry ${cache_name}\"\n              else\n                echo \"::warning::Unable to delete cache entry ${cache_name}\"\n              fi\n            fi\n          done <<< \\\n          \"$(gh api repos/${GITHUB_REPOSITORY}/actions/caches \\\n            --jq '.actions_caches.[] | select(.ref|test(\"refs/heads/master\")) | select(.key|test(\".*-(ccache|xcode)-*\")) | {id, key} | join(\";\")')\"\n          echo '::endgroup::'\n\n\n          echo '::group::Processing pull request cache entries'\n          while IFS=\";\" read -r cache_id cache_name cache_ref; do\n            if [[ \"${cache_name}\" ]]; then\n              result=true\n              gh api -X DELETE repos/${GITHUB_REPOSITORY}/actions/caches?key=${cache_name} --jq '.total_count' &> /dev/null || result=false\n\n              pr_number=$(echo ${cache_ref} | cut -d '/' -f 3)\n\n              if ${result}; then\n                echo \"Deleted PR #${pr_number} cache entry ${cache_name}\"\n              else\n                echo \"::warning::Unable to delete PR #${pr_number} cache entry ${cache_name}\"\n              fi\n            fi\n          done <<< \\\n            \"$(gh api repos/${GITHUB_REPOSITORY}/actions/caches \\\n              --jq '.actions_caches.[] | select(.ref|test(\"refs/heads/master\")|not) | select(.key|test(\".*-ccache-*\")) | {id, key, ref} | join(\";\")')\"\n          echo '::endgroup::'\n\n  build-project:\n    name: Build 🧱\n    uses: ./.github/workflows/build-project.yaml\n    needs: cache-cleanup\n    secrets: inherit\n\n  analyze-project:\n    name: Analyze 🔬\n    uses: ./.github/workflows/analyze-project.yaml\n    needs: cache-cleanup\n    secrets: inherit\n    permissions:\n      security-events: write\n\n  upload-language-files:\n    name: Upload Language Files 🌐\n    if: github.repository_owner == 'obsproject' && github.ref_name == 'master'\n    runs-on: ubuntu-24.04\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          submodules: recursive\n          fetch-depth: 0\n\n      - name: Check Nightly Runs ☑️\n        id: nightly-checks\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          : Check Nightly Runs ☑️\n          if [[ \"${RUNNER_DEBUG}\" ]]; then set -x; fi\n\n          # This 'gh' command retrieves the last 2 runs of the workflow defined\n          # by 'scheduled.yaml' and retrieves just the 'headSha' value of the\n          # JSON response payload.\n          #\n          # As this job runs in context of the same workflow, the first element\n          # of the workflow list will be the currently active run.\n          #\n          # The jq expression then selects the 'headSha' element of the second\n          # element in the array, which is the SHA-1 hash of the commit used\n          # for the immediately prior run of this workflow.\n\n          last_nightly=$(gh run list --workflow scheduled.yaml --limit 2 --json headSha --jq '.[1].headSha')\n\n          if [[ \"${GITHUB_SHA}\" == \"${last_nightly}\" ]]; then\n            echo \"passed=false\" >> $GITHUB_OUTPUT\n          else\n            echo \"passed=true\" >> $GITHUB_OUTPUT\n            echo \"lastNightly=${last_nightly}\" >> $GITHUB_OUTPUT\n          fi\n\n      - name: Check for Changed Files ✅\n        if: fromJSON(steps.nightly-checks.outputs.passed)\n        uses: ./.github/actions/check-changes\n        id: checks\n        with:\n          baseRef: ${{ steps.nightly-checks.outputs.lastNightly }}\n          checkGlob: '**/en-US.ini'\n\n      - name: Upload US English Language Files 🇺🇸\n        if: steps.checks.outcome == 'success' && fromJSON(steps.checks.outputs.hasChangedFiles)\n        uses: obsproject/obs-crowdin-sync/upload@430665179ed13233af2d83ec192c2ae8c40a29ae\n        env:\n          CROWDIN_PAT: ${{ secrets.CROWDIN_SYNC_CROWDIN_PAT }}\n          GITHUB_EVENT_BEFORE: ${{ steps.nightly-checks.outputs.lastNightly }}\n\n  steam-upload:\n    name: Upload Steam Builds 🚂\n    needs: [build-project]\n    if: github.repository_owner == 'obsproject'\n    runs-on: macos-15\n    defaults:\n      run:\n        shell: zsh --no-rcs --errexit --pipefail {0}\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Check Nightly Runs ☑️\n        id: checks\n        env:\n          GH_TOKEN: ${{ github.token }}\n        run: |\n          : Check Nightly Runs ☑️\n          if (( ${+RUNNER_DEBUG} )) setopt XTRACE\n\n          # This 'gh' command retrieves the last 2 runs of the workflow defined\n          # by 'scheduled.yaml' and retrieves just the 'headSha' value of the\n          # JSON response payload.\n          #\n          # As this job runs in context of the same workflow, the first element\n          # of the workflow list will be the currently active run.\n          #\n          # The jq expression then selects the 'headSha' element of the second\n          # element in the array, which is the SHA-1 hash of the commit used\n          # for the immediately prior run of this workflow.\n\n          local last_nightly=$(gh run list --workflow scheduled.yaml --limit 2 --json headSha --jq '.[1].headSha')\n\n          if [[ \"${GITHUB_SHA}\" == \"${last_nightly}\" ]] {\n            print \"passed=false\" >> $GITHUB_OUTPUT\n          } else {\n            print \"passed=true\" >> $GITHUB_OUTPUT\n          }\n\n      - uses: ./.github/actions/steam-upload\n        if: fromJSON(steps.checks.outputs.passed)\n        with:\n          steamSecret: ${{ secrets.STEAM_SHARED_SECRET }}\n          steamUser: ${{ secrets.STEAM_USER }}\n          steamPassword: ${{ secrets.STEAM_PASSWORD }}\n          workflowSecret: ${{ secrets.GITHUB_TOKEN }}\n          preview: ${{ github.repository_owner != 'obsproject' }}\n"
  },
  {
    "path": ".github/workflows/sign-windows.yaml",
    "content": "name: Sign Windows Project\non:\n  workflow_call:\njobs:\n  create-windows-update:\n    name: Sign Windows Build 🥩\n    strategy:\n      matrix:\n        architecture: [x64, arm64]\n    runs-on: windows-2022\n    environment:\n      name: bouf\n    defaults:\n      run:\n        shell: pwsh\n    steps:\n      - name: Parse JWT\n        id: jwt\n        run: |\n          $token = ConvertTo-SecureString -String ${env:ACTIONS_ID_TOKEN_REQUEST_TOKEN} -AsPlainText\n          $jwt = Invoke-WebRequest -Uri \"${env:ACTIONS_ID_TOKEN_REQUEST_URL}&audience=ignore\" -Authentication Bearer -Token $token\n          $claim_b64 = (($jwt.Content | ConvertFrom-Json -AsHashtable).value -split '\\.')[1]\n          $mod = $claim_b64.Length % 4\n          if ($mod -gt 0) {$claim_b64 += '=' * (4 - $mod)}\n          $claim = [Text.Encoding]::Utf8.GetString([Convert]::FromBase64String($claim_b64)) | ConvertFrom-Json -AsHashtable\n          $sha = ${claim}.job_workflow_sha\n          Write-Output \"Workflow SHA: ${sha}\"\n          \"workflow_sha=${sha}\" >> $env:GITHUB_OUTPUT\n\n      - uses: actions/checkout@v4\n        with:\n          path: \"repo\"\n          fetch-depth: 0\n          ref: ${{ steps.jwt.outputs.workflow_sha }}\n\n      - name: Set Up Environment 🔧\n        id: setup\n        run: |\n          $channel = if ($env:GITHUB_REF_NAME -match \"(beta|rc)\") { \"beta\" } else { \"stable\" }\n          $shortHash = $env:GITHUB_SHA.Substring(0,9)\n          \"channel=${channel}\" >> $env:GITHUB_OUTPUT\n          \"commitHash=${shortHash}\" >> $env:GITHUB_OUTPUT\n\n      - name: Download Artifact 📥\n        uses: actions/download-artifact@v4\n        with:\n          name: obs-studio-windows-${{ matrix.architecture }}-${{ steps.setup.outputs.commitHash }}\n          path: ${{ github.workspace }}/build\n\n      - name: Run bouf 🥩\n        uses: ./repo/.github/actions/windows-signing\n        with:\n          gcpWorkloadIdentityProvider: ${{ secrets.GCP_IDENTITY_POOL }}\n          gcpServiceAccountName: ${{ secrets.GCP_SERVICE_ACCOUNT_NAME }}\n          version: ${{ github.ref_name }}\n          channel: ${{ steps.setup.outputs.channel }}\n          architecture: ${{ matrix.architecture }}\n\n      - name: Generate artifact attestation\n        uses: actions/attest-build-provenance@v1\n        with:\n          subject-path: ${{ github.workspace }}/output/*-${{ matrix.architecture }}.zip\n\n      - name: Upload Signed Build\n        uses: actions/upload-artifact@v4\n        with:\n          name: obs-studio-windows-${{ matrix.architecture }}-${{ github.ref_name }}-signed\n          compression-level: 0\n          path: ${{ github.workspace }}/output/*-${{ matrix.architecture }}.zip\n\n      - name: Upload PDBs\n        uses: actions/upload-artifact@v4\n        with:\n          name: obs-studio-windows-${{ matrix.architecture }}-${{ github.ref_name }}-pdbs\n          compression-level: 0\n          path: ${{ github.workspace }}/output/*-pdbs.zip\n\n      - name: Upload Installer\n        uses: actions/upload-artifact@v4\n        if: matrix.architecture == 'x64'\n        with:\n          name: obs-studio-windows-${{ matrix.architecture }}-${{ github.ref_name }}-installer\n          compression-level: 0\n          path: ${{ github.workspace }}/output/*.exe\n"
  },
  {
    "path": ".gitignore",
    "content": "# Exclude everything\n/*\n\n# Except for default project files\n!/.github\n!/build-aux\n!/cmake\n!/deps\n!/docs\n!/frontend\n!/libobs*\n!/plugins\n!/shared\n!/test\n!.cirrus.xml\n!.clang-format\n!.editorconfig\n!.gersemirc\n!.git-blame-ignore-devs\n!.gitmodules\n!.gitignore\n!.mailmap\n!.swift-format\n!AUTHORS\n!buildspec.json\n!CMakeLists.txt\n!CMakePresets.json\n!COC.rst\n!COMMITMENT\n!CONTRIBUTING.rst\n!COPYING\n!INSTALL\n!README.rst\n\n# Exclude lock files\n*.lock.json\n\n# Exclude files generated by Sphinx in-tree\n/docs/sphinx/_build/*\n!/docs/sphinx/_build/.gitignore\n!/docs/sphinx/Makefile\n\n# Exclude modified Flatpak files\nbuild-aux/flatpak-github-action-modified-*\n\n# Exclude macOS legacy resource forks\n.DS_Store\n\n# Exclude CMake build number cache\n/cmake/.CMakeBuildNumber\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"plugins/win-dshow/libdshowcapture\"]\n\tpath = deps/libdshowcapture/src\n\turl = https://github.com/obsproject/libdshowcapture.git\n[submodule \"plugins/obs-browser\"]\n\tpath = plugins/obs-browser\n\turl = https://github.com/obsproject/obs-browser.git\n[submodule \"plugins/obs-websocket\"]\n\tpath = plugins/obs-websocket\n\turl = https://github.com/obsproject/obs-websocket.git\n"
  },
  {
    "path": ".mailmap",
    "content": "#\n# Manually defined\n#\nLain <lain@obsproject.com>\nLain <obs.jim@gmail.com>\nBenjamin Klettbach <b.klettbach@gmail.com>\nBtbN <btbn@btbn.de>\nkc5nra <jrb@turrettech.com>\nHomeWorld <homeworld@gmail.com> hwdro <pdarvaru@yahoo.com>\nMichael Fabian 'Xaymar' Dirks <info@xaymar.com> <Xaymar@users.noreply.github.com>\nMichael Fabian 'Xaymar' Dirks <info@xaymar.com> <michael.dirks@xaymar.com> \nMichael Fabian 'Xaymar' Dirks <info@xaymar.com>\nMartell Malone <martellmalone@gmail.com>\nFord Smith <fordmsmith13@gmail.com> <xtreameprogram2@gmail.com>\nFord Smith <fordmsmith13@gmail.com>\nSebastian Beckmann <beckmann.sebastian@outlook.de>\nRodney <xlnedder@gmail.com>\nReboot <reboot@gmx.ch>\n#\n# Generated to prevent people with multiple names or emails appearing multiple times, for the manual changes made see below:\n# \njpark37 <jpark37@users.noreply.github.com> <jpark37@gmail.com>\njpark37 <jpark37@users.noreply.github.com>\nDDRBoxman <colin@recursivepenguin.com> <ddrboxman@Colins-MacBook-Pro.local>\nDDRBoxman <colin@recursivepenguin.com>\nR1CH <r1ch@r1ch.net> <r.stanway@gmail.com>\nR1CH <r1ch@r1ch.net> <notr1ch@users.noreply.github.com>\nR1CH <r1ch@r1ch.net>\nWizardCM <matt@obsproject.com> <matt@wizardcm.com>\nWizardCM <matt@obsproject.com> <git@wizardcm.com>\nWizardCM <matt@obsproject.com>\nZachary Lund <admin@computerquip.com> <zachary.lund@streamlabs.com>\nZachary Lund <admin@computerquip.com> <Zachary L1und>\nZachary Lund <admin@computerquip.com>\npkv <pkv.stream@gmail.com> <pkv@obsproject.com>\npkv <pkv.stream@gmail.com>\nPatTheMav <patthemav+github@gmail.com> <PatTheMav@users.noreply.github.com>\nPatTheMav <patthemav+github@gmail.com>\nbrittneysclark <brittney.s.clark@intel.com> <brittney.s.clark@gmail.com>\nbrittneysclark <brittney.s.clark@intel.com>\ncraftwar <craftwar@users.noreply.github.com> <coo.ky@hotmail.com>\ncraftwar <craftwar@users.noreply.github.com>\nSCG82 <scg082+github@gmail.com> <scg082@gmail.com>\nSCG82 <scg082+github@gmail.com>\nEd Maste <emaste@FreeBSD.org> <emaste@freebsd.org>\nEd Maste <emaste@FreeBSD.org>\nmntone <sd8@live.jp> <mntone@outlook,jp>\nmntone <sd8@live.jp>\njuvester <juvester@cs.helsinki.fi> <juvester@users.noreply.github.com>\njuvester <juvester@cs.helsinki.fi>\nSocapex <philippe.groarke@gmail.com> <philippe_groarke@yahoo.ca>\nSocapex <philippe.groarke@gmail.com>\nRat <52238970+ratwithacompiler@users.noreply.github.com> <37424386+ratwithashotgun@users.noreply.github.com>\nRat <52238970+ratwithacompiler@users.noreply.github.com>\nQuinn Damerell <qdamere@microsoft.com> <quinnd@outlook.com>\nQuinn Damerell <qdamere@microsoft.com>\nManuel Kroeber <manuel.kroeber@googlemail.com> <manuel.kroeber@gmail.com>\nManuel Kroeber <manuel.kroeber@googlemail.com>\nFord Smith <fordmsmith13@gmail.com> <fms34@case.edu>\nFord Smith <fordmsmith13@gmail.com>\nVainock <contact.vainock@gmail.com> <Vainock@users.noreply.github.com>\nVainock <contact.vainock@gmail.com> <39059951+Vainock@users.noreply.github.com>\nVainock <contact.vainock@gmail.com>\nWahaj Dar <mail@wahajdar.com> <wahajdar@outlook.com>\nWahaj Dar <mail@wahajdar.com>\nDavid Cooper <david@dtcooper.com> <davidc@eventbrite.com>\nDavid Cooper <david@dtcooper.com>\ndodgepong <ben.torell@gmail.com> <dodgepong@gmail.com>\ndodgepong <ben.torell@gmail.com>\nRobin Hielscher <git@robinhielscher.de> <git@jack0r.com>\nRobin Hielscher <git@robinhielscher.de>\nstump <git@stump.io> <stump@stump.io>\nstump <git@stump.io>\nGary Kramlich <gary@bitmex.com> <grim@reaperworld.com>\nGary Kramlich <gary@bitmex.com>\nJoshua Rowe <josh@nocoded.com> <joshua@nvisionweb.com>\nJoshua Rowe <josh@nocoded.com>\nMatthew McNamara <matt@mattmcn.com> <mattmcnam@gmail.com>\nMatthew McNamara <matt@mattmcn.com>\ncg2121 <claytong1214@gmail.com>\nGol-D-Ace <Gol-D-Ace@users.noreply.github.com>\nJohannMG <johannmg@fb.com>\nsorayuki <sorayuki@live.com>\nDead133 <Dead133@gmail.com>\nIlya Melamed <ilya77@gmail.com>\nMichel <michel@sokar.nl>\nWarchamp7 <warchamp2003@hotmail.com>\nbl <bl4@postpile.org>\nPalakis <contact@slepin.fr>\npaibox <beeanyew@gmail.com>\nboombatower <jimmy@boombatower.com>\ncristisilaghi <cristian.silaghi@mozilla.ro>\nnotmark <mark@vaughnsoft.com>\nunivrsal <univrsal@protonmail.ch>\nFarzad Karkhani <farzad.karkhani@gmail.com>\nMedicMomcilo <medicmomcilo@gmail.com>\n#\n# Manual changes made:\n#\n# <matt@wizardcm.com> -> <matt@obsproject.com>\n# Colin Edwards -> DDRBoxman\n# Richard Stanway -> R1CH\n# Matt Gajownik -> WizardCM\n# pkviet -> pkv\n# Ben Torrel -> dodgepong\n# Clayton Groeneveld -> cg2121\n# Andrew Surzhynskyi -> Dead133\n"
  },
  {
    "path": ".swift-format",
    "content": "{\n    \"version\": 1,\n    \"lineLength\": 120,\n    \"indentation\": {\n        \"spaces\": 4\n    },\n    \"tabWidth\": 4,\n    \"maximumBlankLines\": 1,\n    \"respectsExistingLineBreaks\": true,\n    \"lineBreakBeforeControlFlowKeywords\": false,\n    \"lineBreakBeforeEachArgument\": false,\n    \"lineBreakBeforeEachGenericRequirement\": false,\n    \"lineBreakBetweenDeclarationAttributes\": false,\n    \"prioritizeKeepingFunctionOutputTogether\": false,\n    \"indentConditionalCompilationBlocks\": true,\n    \"lineBreakAroundMultilineExpressionChainComponents\": false,\n    \"fileScopedDeclarationPrivacy\": {\"accessLevel\": \"private\"},\n    \"indentSwitchCaseLabels\": false,\n    \"spacesAroundRangeFormationOperators\": false,\n    \"noAssignmentInExpressions\": { \"allowedFunctions\" : [\"XCTAssertNoThrow\"] },\n    \"multiElementCollectionTrailingCommas\": true,\n    \"indentBlankLines\": false,\n}\n"
  },
  {
    "path": "AUTHORS",
    "content": "Original Author: Lain Bailey\n\nContributors are sorted by their amount of commits / translated words.\n\nContributors:\n Lain\n jpark37\n PatTheMav\n derrod\n Sebastian Beckmann\n Ryan Foster\n cg2121\n R1CH\n Palana\n tytan652\n WizardCM\n Norihiro Kamae\n fryshorts\n Georges Basile Stavracas Neto\n pkv\n DDRBoxman\n Kurt Kartaltepe\n Rodney\n Gol-D-Ace\n Warchamp7\n BtbN\n tt2468\n Shaolin\n Exeldro\n kc5nra\n Michael Fabian 'Xaymar' Dirks\n Ruwen Hahn\n VodBox\n Zachary Lund\n Dennis Sädtler\n SuslikV\n Paul Hindt\n Vainock\n shiina424\n Ed Maste\n CodeYan01\n Reboot\n Martell Malone\n Penwywern\n jcm\n Ford Smith\n columbarius\n Alex Luccisano\n dodgepong\n HomeWorld\n Joel Bethke\n Service Checker\n Alex Anderson\n Translation Updater\n brittneysclark\n Dmitry-Me\n Tommy Vercetti\n 田七不甜\n SCG82\n Roman Huts\n Gale\n Yuriy Chumak\n juvester\n mvji\n wangshaohui\n Sean DuBois\n craftwar\n sorayuki\n Dead133\n Eric Lindvall\n Florian Zwoch\n Jimi Huotari\n JohannMG\n Maya Venkatraman\n Radzaquiel\n Scratch\n Socapex\n Cephas Reis\n Chris\n Fabian Mastenbroek\n Hector Martin\n Huts, Roman\n Luke Yelavich\n bin\n mntone\n tududweb\n Developer-Ecosystem-Engineering\n Igor Bochkariov\n Ilya Melamed\n Stéphane Lepin\n Chip Bradford\n David Rosca\n Marvin Scholz\n Rat\n Skyler Lipthay\n Wim Taymans\n Arkkis\n GoaLitiuM\n Hunter L. Allen\n John Bradley\n Ka Ho Ng\n Manuel Kroeber\n Michel\n Quinn Damerell\n Anthony Torres\n Anton Bershanskiy\n Bennik2000\n Chris (Flaeri)\n Chris Angelico\n Danni\n Doug Kelly\n James Hurley\n Jan Beich\n Jess Mayo\n Kris Moore\n Mike\n Neal Gompa\n Palakis\n Theodore Dubois\n Tristan Matthews\n test\n Alexandre Vicenzi\n Carl Fürstenberg\n Kilian von Pflugk\n SoftArch\n Timo Gurr\n Tjienta Vara\n Wahaj Dar\n prgmitchell\n thirumalai-qcom\n Aaron Boxer\n Aleks Todorov\n Anry\n Bilal Elmoussaoui\n CoDEmanX\n Cody Jung\n David Bieber\n Hosein\n Ioan Loosley\n Ján Mlynek\n MaZderMind\n OldBaldGeek\n Stephen Seo\n Zhao Zhili\n bl\n makise-homura\n nleseul\n nquah\n univrsal\n Alexander Popov\n Artem Polishchuk\n Barnabás Pőcze\n Benjamin Klettbach\n Bird, Christopher\n Bl00drav3n\n Blackhive\n Charles Ray Shisler III\n Chaturbate\n Chensiyy\n Daniel Lopez\n David Cooper\n Doğukan Korkmaztürk\n FiniteSingularity\n Friedhelm Birth\n Gale, Thy-Lan\n Grant Likely\n Hernán\n Ivan Molodetskikh\n Jeremiah Senkpiel\n Jiaxun Yang\n John Bowers\n John R. Bradley\n Jonathan Bennett\n Joseph El-Khouri\n Kasin Sparks\n Lin\n Matthew Glazar\n Matthew Orlando\n Michael R. Crusoe\n Monsteer\n Peter Geis\n Philip Haynes\n Robin Hielscher\n Roman Sivriver\n Serge Paquet\n Stefan Hoffmeister\n Tatsuyuki Ishi\n Torge Matthies\n Valentin\n WarmUpTill\n Yoshimasa Niwa\n jeiea\n karenkim-AfreecaTV\n paibox\n pkubaj\n rhutsAMD\n shousa\n tlivegaming\n vokama\n Adrián Pardini\n Aidan Delaney\n Akihiko Koizuka\n Albert\n Alex Miller\n Alexander Schittler\n Amin.MasterkinG\n Andreas Reischuck\n Andrei Nistor\n Andrew Francis\n Antti Tapaninen\n Arthus\n AuroraWright\n Azat Khasanshin\n Be1erafon\n Benjamin Schubert\n Bongalive\n Brian S. Stephan\n Christine Lin\n Christopher P Yarger\n DeeDeeG\n Dossy Shiobara\n Douglas Rhine\n DungFu\n Eric Bataille\n Fabien Lavocat\n Farzad Karkhani\n Fred Emmott\n Gary Kramlich\n Grzegorz Godlewski\n Hans Petter Selasky\n Henrik \"Henke37\" Andersson\n Jeremy Woertink\n JiangXsong\n JoHn BoWeRs\n John Boiles\n Joshua Rowe\n Justin Bull\n Justin Love\n Jérémy Farnaud\n Kristian Ollikainen\n Len\n Lexsus\n Lionheart Zhang\n Lordmau5\n Lucian Poston\n Luke Strickland\n Marco\n Mat Clayton\n Mathijs Kadijk\n Matt Morrissette\n Matthew\n Matthew McNamara\n Mattias Landin\n MedicMomcilo\n Michael Goulet\n Nirusu\n Olle Kelderman\n PancakeTAS\n Paul Gregoire\n Piotr Esden-Tempski\n Rob Howell\n Sergio Garcia Murillo\n Sergo\n Skid-Inc\n Staacks\n Stéphane Cottin\n Suman Manjunath\n Take Vos\n Tarulia\n Taylor Blau\n Thomas Schnitzler\n Thulinma\n Tom Lynch\n Vadim Zhukov\n Warren Turkal\n Willy Liu(HQ ENG)\n WuLongyue\n Yuri Victorovich\n adray\n aggresss\n akapar\n astudios123\n boombatower\n bootkiller\n ckelsel\n comex\n cristisilaghi\n dgeibi\n e00E\n hgonomeg\n invalidtask\n jpk\n jw0z96\n kevin.dww\n liu.haibin\n moocowsheep\n notmark\n obiwac\n skwerlman\n stopforumspam\n stump\n taesheren\n tuduweb\n wolf247\n yogpstop\n 9p4\n Aarni Koskela\n Abelardo E. Mendoza\n Adam Taylor\n Adit Sharda\n Aesen Vismea\n Afriza N. Arief\n Aidan Epstein\n Akagi201\n Alcaros\n Aleix Pol\n Alex Kosenko\n Alexander Kozhevin\n Alexander Uhlmann\n Alexandre Biny\n Alexandre Paré\n Alexis King\n Ali Kaviani\n Amin Mahmoudi\n Amin Shayan\n Anastasiia Sidorovskaia\n Andrew\n Andrew Story\n Andrew Tropin\n Andrew Woodward\n Andrés Barreiro\n AngeredZeus\n Anthony Catel\n Anthony Super\n AntowaStrong\n Ariel D'Alessandro\n Artem Merkulov\n Asahi Lina\n Asgeir Mortensen\n Autumin\n Avital Yachin\n Aydin Akan\n Azamat H. Hackimov\n BGM99\n Bart F\n Bartłomiej Piotrowski\n Bas van Meel\n Bazhenoff\n Beau Russell\n Ben Alman\n Ben Bowler\n Ben Krueger\n Ben Stahl\n Bernd Buschinski\n Biswapriyo Nath\n Bleuzen\n Bo\n Bob van de Vijver\n Brahmajit Das\n Bruce Zhang\n Byoungchan Lee\n C. Daniel Sanchez R\n Caelan Sayler\n Caitlin Potter\n Caleb Anderson\n CallumHoward\n Cam\n Can Bal\n Carl Pédimina\n Carlo Castoldi\n CarrotCodes\n Cenk Uluisik\n Charlese2\n Charlie Li\n Chase Adams\n Cheeseness\n Christian Lockley\n Christopher P. Yarger\n Christopher Serr\n Claude Heiland-Allen\n Clemens Meinhart\n Cole\n Colerar\n CommanderRoot\n Copy Liu\n CrackThrough\n Cray Elliott\n Crocodile Gena\n Damian Marcin Szymański\n Dan Coffey\n Dan Dascalescu\n Daniel Escoz\n Daniel Hill\n Daniel Porter\n David Baker\n David Edmundson\n David McMackins II\n David Plappert\n Delta\n Dennis Marttinen\n Derrick Lambert\n Developer\n Devin Neal\n Devon Vitkovsky\n Dimitry Andric\n Dmitry Odintsov\n Dmitry Vovk\n Doug Ty\n DriftedClouds\n E Jikan\n EBK21\n Ed Ropple\n Ek Palmer\n Emil Sayahi\n EpicCoder\n Eric Laberge\n Etaash Mathamsetty\n Ethan Lee\n Evgeny Pavlov\n Faeez Kadiri\n Fortune-SOOP\n Francisco Oltra\n Frank\n Frank Gehann\n Frank Löffler\n GPattenden\n Garrett\n GingerAudio\n Giorgio Pellero\n Gregory Luneau\n Grigorii Chirkov\n Guang Zhou\n Guillermo A. Amaral\n Gökberk Yaltıraklı\n Haden F\n Han-Tai Chen\n Hayden McAfee\n Hicham LEMGHARI\n Hong Hak-Gyu\n Iblis Lin\n Ihor Kalnytskyi\n Ilya Chernikov\n Ivan Efremov\n J Lynn\n J.D. Purcell\n Jack Karamanian\n Jake Probst\n James Beddek\n Jamy Timmermans\n Jason Francis\n Jeff Ward\n Jeremy Cole\n JetMeta\n Jkoan\n Joan Miquel Fuster\n Joe Flateau\n John Cheng\n Jon Topper\n Jonathan\n Jonathan Baecker\n Joshua Ashton\n Joshua Berenhaus\n Julian Miller\n Julian Orth\n Juliane Holzt\n Juliusz Chroboczek\n Justin B. Watson\n K.B.Dharun Krishna\n KOU_CHANG\n Kacper Geisheimer\n Kamal Mostafa\n Kazuki Oishi\n Kazuki Yamaguchi\n Keith Packard\n Kevin Degeling\n Kevin Deng\n Kevin Ross\n Kevin Tardif\n Kevin Wang\n Keylase\n Kraz3D\n Lasse Dalegaard\n Laurin Müller\n Lev Leontev\n LiamCoal\n LightcastMediaCloud\n Linnun\n Lioncash\n Lluís Batlle i Rossell\n Lovecast Neil\n Lqlsoftware\n Lukas Monka\n Luke Granger-Brown\n Lukáš Cezner\n Lukáš Jech\n Lukáš Mojžíš\n MagicBear\n Makeenon\n Malcolm Bechard\n Marc Chambers\n Marcus Rückert\n Mariana\n Mark Sergienko\n Masato Takahashi\n Mathias Panzenböck\n Matt\n Matthew Szatmary\n Matthieu Cunzi\n Meng Li\n Micah Elizabeth Scott\n Michael Gillett\n Michael Hoang\n Mico Häsänen\n Miha Frangež\n Mike So\n Mikhail Kochegarov\n Mirza Özokyay\n Misutaa Asriel\n Morgan Rose\n Morten Bøgeskov\n MrMahgu\n Murnux\n Nathan-Huckleberry\n Nick Stockton\n Nicolas F\n Nicolas Fella\n Night\n Nikola Jovic\n Oliver Lietz\n Olivier Humbert\n OvchinnikovDmitrii\n Patrick Ancillotti\n Paul Mc Galey\n Per Heed\n Peter \"Marenthyu\" Fredebold\n Peter Eszlari\n Peter SZTANOJEV\n Philip Loche\n Philipp Bauer\n Piotr Kubaj\n Rafael Franco\n Ramjivan Jangid\n RedBlackAka\n Regan Black\n René Rebe\n Ricardo Constantino\n Ricardo Tavares\n Richard Fontana\n Ro\n Robert Mader\n Robert Wittams\n Robert de Bock\n Rodrigo Ipince\n Romain Vigier\n Ryan Sullivan\n Ryszard Knop\n SammyJames\n SarenDev\n ScoochAside\n Scott Cooper\n Sebastian Ramacher\n Sefa Eyeoglu\n Serge Martial Nguetta\n Sergo Pogosyan\n Seth Murphy\n Seth Williams\n Seung-Woo Kim\n Shamun\n Sherwin Dapito\n Shlee\n Simon\n Simon Rost\n Sludge\n Smartkin\n Sophie Hamilton\n Stanislav Izotov\n Stefan Murawski\n Steve Wills\n Stripchat\n Susko3\n TDV Alinsa\n Teemu Kauhanen\n Terje Gundersen\n Thomas Crider\n Thomas De Schampheleire\n Thomas McGrew\n Thomas Müller\n Thomas Picquet\n Tim Cooper\n Tim Vaca\n Toasterapp\n Tobias Speicher\n TobiasGSmollett\n Tom Haffenden\n Tom Peeters\n Tom Pichler\n Tophicles\n TotalCaesar659\n Translation\n Tunghohin\n Uro\n Weikardzaena\n Will Jamieson\n William Casarin\n Wooster\n Wouter\n Yann Salmon\n Ying Yin\n Younes SERRAJ\n Yves Dionne\n Yvo\n Ziemas\n Zulleyy3 Zulleyy3\n a1346054\n adocilesloth\n antho\n artemmdev\n bluekirby0\n bth\n chaselliu\n chinasarft\n chirenonsteem\n clone1018\n cmlin2\n confusionattack\n crazyheart\n cryptonaut\n davids\n deadmeu\n deltamaya\n dennis\n dimtpap\n duyquach-castr\n dwot\n eastkiki\n eightball567\n evandroduarte\n fefine\n fengyifan\n floele\n furious\n fuyingqi\n geekenmd\n geemion\n gkvjwa\n guangong7628\n hellowanda\n hongqingwan\n iRonBot\n iamflorencejay\n izwb003\n jake-shomer\n jamacanbacn\n jasonlee\n jbpratt78\n jeremiah\n joelgerard\n jsnkan\n katahtonic\n ka’imi\n kiwialec\n kokole\n kyle\n lemmi\n lorskun\n lucha\n luz.paz\n mape\n matoi974\n meta\n mhabedinpour\n michael bishop\n nd\n nico\n nixflorp\n nkmerrill\n pantonvich\n partouf\n pedanticdan\n pipll\n pkuznetsov\n poccariswet\n pongraczgabor87\n powersagitar\n praveenkumar\n probonopd\n qhy040404\n qiongzai.sun\n raincomplex\n rch850\n redcrazyheart\n repeat\n rkfg\n rpslack\n rvalue\n sam8641\n sergey\n shinji3\n skeletonbow\n skyboy\n sora-blue\n stephematician\n striphat-dev\n sunjingzhao\n tfo\n thekrzos\n trwnh\n tujinshu\n ty2570\n vasu\n vasuais\n venepe\n vfernandes-sympla\n vic\n vico-shang\n vishakh-h\n vividnightmare\n waffeln\n wang-bin\n wang.shaohui\n wangrui05\n wanhongqing\n wayne wang\n weabook\n windwakr\n wpstream\n xlbljz\n xlinshan\n yalpul\n yoho\n zhaolong\n наб\n 张昆\n 练亮斌\n 태스키_박태연\n\nTranslators:\n Afrikaans:\n  Gideon Wentink (gjwentink)\n  Baker123\n  Rory Townsend (f3emarketing)\n  Samuel Nthoroane (Samuel_Nthoroane)\n  speedytechdev\n  sadam2002 sadam4 (sadam2002sadam4)\n  Gol D. Ace (goldace)\n Albanian:\n  Begi Ganellari (begiganellari1997)\n  Aredio Vani (aredio.vani)\n  Albin Pllana (albinnpllanaa)\n  Enea (eneaaene1)\n  avnidj\n  Sigurður óskarsson (sig.oskarsson)\n  Baker123\n  unknowndomain\n  casttor\n  Gol D. Ace (goldace)\n Arabic:\n  Tareq Almutairi (Tareq_s)\n  ZILZAL\n  ‫علي السادة (‪games power‬‏)‬‎ (alidsds11)\n  Abdullah AL-Qahtani (Za7ef_SA)\n  lazerns\n  majdcomp\n  Ziad (ZiadElsayed1797)\n  Rami Kattan (rkattan)\n  Fady Adal (fadyadal)\n  Saud Alali (saud_alali)\n  sxm1\n  Wydady (Wyd1)\n  yn (user99)\n  aaakjt\n  Fahim Sabah (Road_ar)\n  doyTech (aldoyh)\n  waleedmortaja\n  AiAF (7MR)\n  ibrahim Younis (gr33v)\n  omar al_hasasneh (omar.alhasasneh)\n  Mustafa A. Al-syady (mostafa307)\n  E3saR (e3sa_r)\n  معتصم دعنا (rozana-media)\n  fawaz006\n  Mohamed Zain (Thecheeseman)\n  Abdulaziz (CleverShadow)\n  Gol D. Ace (goldace)\n  Saleh oukiki (salehoukiki)\n  Sami Kamal (samixgames)\n  BeleganStartup\n  Ahmad Alkurbi (DontPlay)\n  Tensai\n  Mustafa2018\n  FiberAhmed\n  Mohamed Saidi (ms0709037)\n  Ndalabo Taema (hake_bsowq)\n  Driss Baidou (drissbaidou)\n  MesterPerfect\n  Fahad Alshaya (fashaya)\n  azez sh (azezsh)\n  AL-3boud_25 (alaboud257)\n  Salah Naser (salah.k.2775)\n  Hani Sweileh (hno.sweileh)\n  zefr0x\n  Nasser Bin Laboun (nasserbinlaboun)\n  M24 AL-Zurqan (mohammedamer2443)\n  RedX\n  Awatif Koko (ngamu.zakaria15)\n  BWU Wheelman (Wheelman)\n  FC Barcelona HD (kurdnews)\n  osmansalih (hpcplus2016)\n  Umar Alfarook (alfarook)\n  Yhea Ahmad (yhea.ahmad7741)\n  unknowndomain\n  Ahmed Hawam (Hawam)\n  Mhd (thelord9200)\n  Mnsor The-Ghost (mnsor1722011)\n  Malaz Gold Edition (Malo7zz)\n  Hosein Hallavi (habibot81)\n  sadam2002 sadam4 (sadam2002sadam4)\n  Rex_sa (rex07)\n  ButterflyOfFire ⁂ (BoFFire)\n  OBS-Staff\n  chaironeko\n  aalhaif\n  07OM\n  Hadi Gamer (hadigamer3131)\n Aragonese:\n  juanpabl\n  Fabio Antonio Alanis Saushkina (fabio.alanis.saushkin)\n  mownd. (mownd0)\n Armenian:\n  ՀայՏղաՀՏ (Zoro02)\n  HayTxa02\n  Hrach Mkrtchyan (hrachmk)\n  Gevorg Tsaturyan (Geworg)\n  test123456ob\n  nkamae\n  Sergey Simonyan (we1nmann)\n  Tigran's Tips (tigrank08)\n  fordink257\n Azerbaijani:\n  Aqil Novruzov (aqilnova)\n  sanandev05\n  ShahinF27\n  Mirsahin Mirserifov (mirsahin.mirsarifov)\n  Magiraliyev\n  Sədi Məmmədov (sedi9816)\n  royal_reyx\n  Şamil Veliyev (TheShamil035)\n  Javid-An\n  unknowndomain\n  Kənan Axundov (kenanaxundov2)\n  gaspard.larrouturou\n Bashkir:\n  Әлмәт Ак Арыслан (19082004amir)\n  Bashquart (RadMir2)\n  Synbulat Biishev (Syjalo)\n  unknowndomain\n  Башҡорт агенты (vint6908)\n Basque:\n  Alexander Gabilondo (alexgabi)\n  Xabier Aramendi (azpidatziak)\n  Osoitz\n  malvru\n  Sergio Varela (IngrownMink4)\n  MCM Tuber (mcmtuber)\n  xabiu\n  Gol D. Ace (goldace)\n  Iñigo Zulueta (eggamer131)\n  nkamae\n  txaro\n  Galder L (315galder)\n  Ruben Pérez Krüger (churrerico100)\n  etxondoko\n  guyguy120 (gilenlecuona)\n  unknowndomain\n  Idoia Suárez (idoiasuarez86)\n  Fiwi1265\n  Aritz Herrero (aritzherrero4)\n  OBS-Staff\n Belarusian:\n  Eduard Račkoŭski (ERFromYouTube)\n  ymcei\n  Kriper1111\n  Аляксандр (parhomenka)\n  Kapatych (kapatych)\n  Dzmitry Zubialevich (dzmitry.zubialevich)\n  Hulec_blr\n  GamePlayTV (pavlus2023)\n  CelluloseNF\n  Aliaksandr Kliujeu (PlagaMedicum)\n  ToffeeMutant\n  raydzu (raydzzzu)\n  Laŭkoŭski Uładzisłaŭ (NonNegative)\n  Георгій Пугачоў (Heorhi_Puhachou)\n  yungvoix\n  Alyaksandr Koshal (Аляксандр Кошаль) (alkos85)\n  Kirya Kirya (Sk1pplex)\n Bemba:\n  P White (phiaramulima)\n Bengali:\n  Roktim Ibne Hasan (roktimih)\n  shamuntohamd\n  Saptarshi Mondal (gitSaptarshi)\n  C559 Raiyan :D (purebangali)\n  Rudro Rasel (rdrrsl)\n  Morshedul Alam (morshedintouch)\n  Hajaj\n  Oymate\n  Nurul Huda (nurulhuda859)\n  Asem Hamid (asem-hamid)\n  Gol D. Ace (goldace)\n  Bangla (Pride)\n  unknowndomain\n  Al Arafat (zonoiko)\n  Bashishtha Narayan Singh (bashishtha)\n  Sakib Shahariar Shimanto (sakib209870)\n  Shaheed Al Mahmud Niad (NDNIAD)\n Bulgarian:\n  Krezzin\n  j3dy\n  Dremski\n  kalmarin\n  Seyhan Halil (easythe)\n  BeCaReFuL24\n  Krasimir Dankov (krasidankov)\n  TraoX_ (traox_)\n  capitalrhino\n  Night (mcnight212)\n  Kosti (Kostirip)\n  g-k-m\n  DivideByNone\n  Kayos471\n  AntiTikTokGang\n  Viktor Kitov (viktorkitov)\n  djradimix\n  Stoyan Stoyanov (sstoyanov)\n  Alex Hristov (jokerabg2)\n  Stanislav_Evtimov\n  Deivid Ivanov (GODMANEX)\n  Akjo (Akjo03)\n  TwoOneTwo (TwoThreeTwo)\n  Gol D. Ace (goldace)\n  krlsky\n  Ivan (SKDown)\n  unknowndomain\n  Salif Mehmed (salifm)\n  Warchamp7\n  OBS-Staff\n Catalan:\n  Jaime Muñoz Martín (jmmartin_5)\n  Benet R. i Camps (BennyBeat) (BennyBeat)\n  jmontane\n  Nil Campamà (Soifam)\n  Aniol Pagès (aniolpages)\n  Aleix Vidal i Gaya (leixet)\n  Xavier Muntada Samà (XavierMSama)\n  Lluís R (LluisR98)\n  Radiaktive\n  Gol D. Ace (goldace)\n  jan (test83318)\n  vilagamer999\n  Vicenç Sallés (vicesalles)\n  Adolfo Jayme (fitojb)\n  ROkelbow\n  Pere O. (gotrunks)\n  unknowndomain\n  user (user1cat)\n  OBS-Staff\n Chinese Simplified:\n  Bob Liu (Akagi201)\n  fangzheng\n  AlexGuo1998\n  Andypsl8\n  wwj402_github\n  FrzMtrsprt (FrzHrzn)\n  Steven (stevenlele)\n  Unili\n  Origami\n  Hexcolyte\n  AthlonHD\n  Ka (Itukii)\n  Rortenfeat\n  Gol D. Ace (goldace)\n  Lion Yang (lionium)\n  田七不甜 (TianQiBuTian)\n  Albert Abdilim (Licardo)\n  PabloLiu (719018105)\n  BrianCheng\n  853\n  CoolKid (conannan)\n  Sasasu\n  Misha (Mishasama)\n  SpeedyOrc340069\n  王秉德 (LUMOGROUP)\n  David Kuo (s50407s)\n  鲜童 (xiananjyzy)\n  Rudro Rasel (rdrrsl)\n  KyleBing\n  十月 (10jugatsu)\n  cai_miao\n  张非酋 (tiantianle1213)\n  Opportunity (OpportunityLiu)\n  lm902\n  MarsYoung\n  Inku Xuan (inkuxuan)\n  一叶杳舟 (Yao_Zhou)\n  Michaelwu (michaelwucoc)\n  Yi-Jyun Pan (pan93412)\n  yunluzhang\n  Sora (candysweetplayzmc)\n  copyliu\n  Boyuan Yang (073plan)\n  CharlieTheCat\n  Flysoft\n  神枪968 (GodGun968)\n  崔致埔 (ACGPlanck)\n  wuliao-\n  Richard Stanway (r1ch)\n  Gō̤ Brian Dìng (brightlikesun)\n  希铁石z (XiTieShiZ)\n  自满 (ziman_)\n  Sea Stone (BiliSeaStone)\n  nkh0472\n  Bing Feng (fengbing123)\n  David Hwang (davidxuang)\n  OV3RWR1T3_ (FR0Z3)\n  jyc001\n  for_the_zero\n  科技小白堂 (lipeng0820)\n  WeiYuanStudio\n  pangbin\n  ROkelbow\n  Eric Zhang (ericzhang456)\n  feizhaishui\n  unknowndomain\n  RytoEX\n  sorayuki\n  Vince Black (vincejudge00)\n  Smpq117 (smpq117)\n  OBS-Staff\n  WaterOtaku\n  Small Dianer (smalldianer)\n  alicee augustwentith (alice_kbbb)\n  OYYZ\n  Bob Wei (BobWaver)\n  Haoran Du (bingchuanjuzi)\n  lifeeeeeeeeeeE\n  AperturePenguin\n  Chen99\n  cheriny\n  FaZe Fakay (fazefakay)\n  xtex\n  Akari (Akari233)\n  pluwen\n  cylin\n  赵杭灵 (h1679083640)\n  Jiaxun Yang (flygoatfree)\n Chinese Traditional:\n  Yi-Jyun Pan (pan93412)\n  TzeKei Lee (chikei)\n  Florid (u900011)\n  abc0922001\n  notlin4\n  Michael Yeh (hinet60613)\n  Julian_Lai\n  OBS-Staff\n  myjourney in Steemit (myjourney)\n  Yu-Juei Tseng (e222et)\n  SeasonChannel3322 (SeasonChannel32)\n  Kanbi Dungaitj (open3)\n  David Kuo (s50407s)\n  loulou921222\n  十月 (10jugatsu)\n  Gol D. Ace (goldace)\n  兔頭 (yeahdamn)\n  craftwar\n  Thomas (thomassth)\n  Jcys\n  菘菘 (SiongSng)\n  Rice Hung (ricehung29)\n  xixiaofan (XXXF)\n  吳軒竹 (ggjason.tw)\n  Supaplextw\n  hugoalh\n  Jimmy_sheep\n  Watson Tsai (ashaneba)\n  Han-Jen Cheng (notexist)\n  Inndy.Lin (inndy)\n  offthegrid-mike\n  yuna0x0\n  Starry Tacos (starrytacos)\n  khng300\n  Meng Hao Li (GazCore)\n  ak-47root\n  Append Huang (append)\n  X-RAY team film (961111ray)\n  fangzheng\n  zhtw\n  yeft\n  JackYeah\n  unknowndomain\n  Rudro Rasel (rdrrsl)\n  cai_miao\n  Tommy Chiang (oToToT)\n  Jimmy Huang (f56112000)\n  曹恩逢 (5idereal)\n  FaZe Fakay (fazefakay)\n  GodBacon (IsItGodBacon)\n  chaironeko\n  tomoe-musashi\n  weixiang0815\n Croatian:\n  Issa1553\n  medicmomcilo\n  MarvinDarwin\n  milotype\n  srdjan_m\n  MARINOMARINO\n  Adrian S (wuku0)\n  Fran Kelava (fkelava)\n  schwabengeiz\n  Runicar (dajtisina)\n  Filip Grubeša (filipgrubi)\n  damba_\n  penta (junp5i)\n  OfficialwobY\n  Gol D. Ace (goldace)\n  Maky (the.real.maky)\n  Flicker (galaxybro2411)\n  Mat GMC7 (MatGMC7)\n  tvoja_stara\n  unknowndomain\n  CroatianTranslator\n  iivkov\n  Denis Šardi (Denizio)\n  OBS-Staff\n Czech:\n  Jirka 'Venty' Michel (VentyCZ)\n  Arocles\n  Šimon Bartoš (TyphousCrane654)\n  Sedloman\n  Kryštof Černý (panKrysha)\n  multi.flexi\n  Matěj Kozák (GrrrandyCZ)\n  FoltinV\n  Martin H. (mhanak)\n  Stewio MC (stepandolsky)\n  Gol D. Ace (goldace)\n  Setam7 (setam7)\n  Kiznoh\n  MrJaroslavik\n  Melons\n  Sawanyo\n  Bear (Bear135)\n  MartinRatvaj\n  Lukáš D. (Asherro)\n  Marilok (Marilokms)\n  Bazer47\n  Alex Smotra (smotraalex75)\n  ROkelbow\n  Erik Bročko (ericek111)\n  unknowndomain\n  VikHill\n  OBS-Staff\n  Daniel Padrta (dpadrta)\n  Martin Pilát (piltmartin)\n  Tom_Hraje\n Danish:\n  NCAA\n  Nicolai Skødt Holmgaard (Nicolai9852)\n  Jens Hyllegaard (Hyllegaard)\n  scootergrisen\n  MaltahlGaming (maltahlgaming)\n  Anders G. Jørgensen (spirit55555)\n  Gol D. Ace (goldace)\n  Dominik Kolacz (Doko1980)\n  Victor Pahuus Petersen (victorpahuus)\n  jhertel\n  Anders Urban (minikaliffen)\n  Richard Stanway (r1ch)\n  Kenneth Nielsen (kenneth.nielsen)\n  Lukas Wiinholt (LukasW1337)\n  Marque Ziqulr (lugtelort)\n  Hero (HeroGamers)\n  Johan Keller Jensen (JKeller)\n  Christian Henriksen (cnhenriksen)\n  unknowndomain\n  Alex Smotra (smotraalex75)\n  Daniel Aundal (aundal)\n  OBS-Staff\n Dutch:\n  Harm van den Hoek (harm27)\n  Eric Bataille (ThoNohT)\n  A. Voogt (atvoogt)\n  Michel Snippe (michelsnippe)\n  Greendweller\n  exeldro\n  Albakham (albakham)\n  Lars Acou (lars.acou)\n  Trigstur\n  Danny (Dkamps18)\n  Richard Stanway (r1ch)\n  Nicole (Dutchess_Nicole)\n  theking2\n  robbert0891 (robbertoorschot38)\n  Jay P. (JayPaans)\n  Kjetil Verstrepen (kjetilv)\n  Gol D. Ace (goldace)\n  silverstorm993\n  jvr\n  Jasper J (JassieJ)\n  b__dm\n  Lars van Wijk (lhjvanwijk98)\n  Issa1553\n  Ik#1497 (ik1497)\n  Chunlu chunnyluny (Chunnyluny)\n  Bond-009\n  Jeroen Maathuis (Joennuh)\n  markpc\n  Jennifer Falco (JenTheBluePanda)\n  Vlame\n  andymidside\n  F_Producktions\n  Thom Knepper (MrKnepp)\n  Dobby-progam\n  lokerhp\n  Jasper (DJj123dj)\n  Lesley Natrop (lesleynatrop)\n  Quinten Althues (QkeleQ10)\n  Wouter Dijkman (Wjouter)\n  Bo Alsemgeest (bo.alsemgeest.wausie)\n  Andreas Delabie (Andreas_Delabie)\n  unknowndomain\n  umläute (umlaeute)\n  JorRy\n  Julian Meijboom (julianmeijboom)\n  Joey Slotboom (JoeyS)\n  RyyzQ\n  Mees (MeesJ)\n  Roeland Kussé (roeland)\n  OBS-Staff\n English, United Kingdom:\n  Andi Chandler (andibing)\n  Issa1553\n  unknowndomain\n  matwey\n  遥汐Aelura (kldhsh123)\n  Seb King (spud)\n  HENRY MARTIN (n3rdl0rd)\n  Cristhofer Sanchez Solano (JafetSS)\n  Endo (Balisong)\n  Pete Russell (pete.russell)\n  George Prime (Diacius)\n  Dani K (SkiaSDK)\n  beq (sorenxysm)\n  Amy (amyipdev)\n  ChrisR2602\n  Joel Jose (joeljose350)\n  Noah Husby (noahhusby)\n  rooban23\n  EvanK\n  Sophie L (Rexogamer)\n  Flint Insects (flintinsects)\n  HelloI'mUnderThe WaterPleaseHelpMe (BoBTHEbuilder)\n  Endysis\n  GarbageAtSB\n  Roofers\n  Minta Máté (Cubebanyasz)\n  Roktim Ibne Hasan (roktimih)\n  tossing\n  nkerr\n  Divaangel1994\n  Karthik Balaji (karthikbalaji174)\n Esperanto:\n  Matt Aaldenberg (MattAalden)\n  Mesu Devastator (Mesu_Devastator)\n  zer0ne (Rezoni)\n  webklaas\n  ProtoManly\n  kresten Sckerl (kressckerl)\n  siamano\n  Infinity ArcAngel (SirColdcrown)\n  Marco Ciampa (ciampix)\n  Fitik\n  Nova (Argenta)\n  David M. (Syndyo)\n  TNT The Noob (tntthenoob)\n Estonian:\n  Mr Wildlore (wildlore)\n  urmo tan (urmotan1)\n  MartinEwing\n  Agu Ratas (aguratas)\n  Avernys (Avernys_tv)\n  treierxyz\n  AndresTraks\n  jmcrwd\n  Ben Towali (BenTowali)\n  Mikk T (Mikkt86)\n  Dex (deloxs)\n  C3kos (C3kos1)\n  Bentologi\n  Gol D. Ace (goldace)\n  unknowndomain\n  Musaks Kõik (musakskoik)\n  Mardo M (stemugram)\n Filipino:\n  Francis Euclid Fernandez (fernandezfrancisE)\n  dwaeji-aizelle\n  nyakayed\n  Napstaguy04\n  CodeYan01\n  Loyd Stephen Jayme (loydjayme25)\n  jermel\n  StartsMercury\n  Raffy (Dharma_Buddha)\n  Gol D. Ace (goldace)\n  Iver (IverCoder)\n  nkamae\n  Zed (lyze_ff)\n  arnoldsecret\n  denove\n  Hagen Peter Marquez (HAJDog247)\n  Tros Mercado (User451)\n  Raylir\n  unknowndomain\n  atomicfart2009\n  Jake Llabres Rabelas (rabelasj)\n Finnish:\n  Arkkis (j)\n  Kimmo Kujansuu (mrkujansuu)\n  Jiri Grönroos (spammemoreplease)\n  supersingularisogeny\n  Ricky Tigg (ricky.tigg)\n  mirko (mirbyte)\n  OBS-Staff\n  Tero Keso (tero.keso)\n  Hemmuli X (hemmulix)\n  Gol D. Ace (goldace)\n  Pyscowicz\n  Qwertele\n  Arthur Kallinen (AKFilmIt) (akfilmit)\n  sZnvARmNaq\n  kettukettu\n  milo willow (millow)\n  ziizaa (zeezaa)\n  sienideluxe3\n  Obama (Obama44)\n  YummyTomatoes\n  K0p1m1\n  Mika Tuomaala (Mipsu)\n  banbaf (3peruna)\n  MCPE-1\n  unknowndomain\n  Juuso (juuso.keskikuru)\n  Xupie\n  Juha Köpman (e0f)\n  Ville Närhi (daimaah)\n  ZapX5_\n  Arttu Ylhävuori (arttu.ylhavuori)\n  Jaakko Saarikko (styxnix)\n  Seqv\n  Jere8091\n  chaironeko\n  fa booh (boohfa09)\n  Aamos (amppi)\n French:\n  pkviet\n  Guillaume Turchini (orion78fr)\n  Stéphane Lepin (Palakis)\n  ArsTechnis\n  Peter Noël Muller (PeterNMuller)\n  Tocram2 (tocram2)\n  Benjamin Cambour (lesinfox)\n  Takoz\n  Sipokad\n  Yberion\n  Léo (leeo97one)\n  RisedSky\n  Pikana\n  BoboopTeam\n  DoK_-\n  leluk\n  MrAngelos6\n  EN LYOWH (enlyowh)\n  EGuillemot\n  DarckCrystale\n  Alexia438\n  Ben Turner (ben-turner)\n  MaxenceVary\n  Azurian\n  DarkInFire\n  Theguiguix\n  Eric Savina (ericsavina)\n  Gol D. Ace (goldace)\n  Figurant16\n  Youtubeur FR│Giaco35 (Giaco35)\n  Wiwok\n  Kévin (FleuryK)\n  steve_fr\n  Valentin (valentin45)\n  LEMIBANDDEXARI\n  kyllian (tardigradeus)\n  Nathan Massoteau (LazyxNat)\n  Nicolas Leperlier (leperlier)\n  maxime (mm154215)\n  Hugo Varlet (Huvar)\n  Deski_\n  QD59\n  GANGAT Naeem (zboggum)\n  Julien R. (Turtiii)\n  Thomas BLANC (thb5309)\n  Aime23\n  Maxime77\n  Grisou2907\n  BaguetteDePain_\n  Christophe Carvalho Vilas-Boas (ChristopheCVB)\n  Zalki\n  Anat ROCHUT (arochut)\n  Christophe Gesché (Moosh-be)\n  Keter LordFR (YorHaDen)\n  Jérémy LITIQUE (JeremyLTE)\n  christophe (TedM)\n  MildToucan\n  Remy Geko (Geko_Officiel)\n  Wydady (Wyd1)\n  ImDaBigBoss\n  illusdidi\n  Mathieu Hautebas (matteyeux)\n  🌠 DarK | #Hello 🌠 (DarKTV_FR)\n  Noam (noam120606)\n  Gabriel Dugny (Dugab)\n  tytan652\n  Nans\n  Yolopix\n  tburette\n  Richard Stanway (r1ch)\n  Adrien “GameZone Tv” de Decker (redcraft007)\n  EvanK\n  ButterflyOfFire ⁂ (BoFFire)\n  Aryoll (Naomi_CAST)\n  SkytAsul (skytasul)\n  xav blacyre (xav35000)\n  Alex Smotra (smotraalex75)\n  HugOmega\n  Antoine Gros (gros_)\n  Alexis Brandner (Alexinfos)\n  Romain Gille (romgille)\n  Camille Nury (kamsdu30)\n  neesn\n  OBS-Staff\n  Jean Dubois (jd-develop)\n  BOSSFOX ! (BOSSFOX)\n  Jean-Mathieu Jm Samson (sjm450666)\n  Lexane Sirac (Exilexi)\n  Wizi_89\n  Thomas Fournier (GoudronViande24)\n  Arzugar\n  Dredd47\n  Benoit (BPTdev)\n  jérémie gagnant (jeremiegremaud2003)\n  HumainAbsurde (humain)\n  Albakham (albakham)\n  HistoriCode97\n  Mathieu Goossens (matgoossens)\n  Clem Gaming (clmntfeugere)\n  Saimyx\n  Paul (i8008) (i8008)\n  Melvyn Jehanne (melvyn.jehanne)\n  SkylixX\n  Warchamp7\n  Nicolas Richard (YoungFrog)\n  okzyrox\n  chaironeko\n  Lucas Juif (trycarnage)\n Galician:\n  mbouzada\n  ninjum\n  Xesús M. Mosquera Carregal (xesusmosquera)\n  Máximo A. Coejo (mcoejo)\n  Fran Diéguez (frandieguez)\n  Fernando Bello (1fbr)\n  Alex Smotra (smotraalex75)\n  Gol D. Ace (goldace)\n  Andrea (apalpadoira)\n  unknowndomain\n  css\n  chaironeko\n  aluaces\n  OBS-Staff\n Georgian:\n  georgianizator\n  Levani Khutsishvili (Tricksteridze)\n  SimModer\n  Razmik Badalyan (badalyanrazmik)\n  Gol D. Ace (goldace)\n  Temuri Doghonadze (temuri.doghonadze)\n  unknowndomain\n  Giorgi Mtchedlishvili (Giorgi1772)\n  Sandro Kakabadze (SANDRO797)\n German:\n  Gol D. Ace (goldace)\n  Michael Fabian Dirks (Xaymar)\n  Mr-Update\n  Letoonik\n  Tillikum Soyer (Tillikum)\n  Sven Kirschbaum (fallobst22) (fallobst22)\n  Dennis Giebert (Isegrim) (isegrimderwolf)\n  Gregor Bigalke (greg6775)\n  OBS-Staff\n  Manuel (ElectronicWar)\n  MyOwnBrain\n  gxalpha\n  Tim (robske_110) (robske110)\n  Marvin J. (juettemarvin)\n  Holger Sinn (Holger-Sinn)\n  Rodney (derrod)\n  lebaston100\n  mdod\n  Benjamin Klettbach (benklett)\n  cryxgio\n  Jonas Otto (jottosmail)\n  EinfachMerlin\n  Arion2000\n  WurstOnAir\n  yojixe\n  Der Ston (derston)\n  Splinter Cel (4eloweck)\n  Max Schulz (maxsschulz)\n  The_Legend_of_xD\n  DonutCrab\n  AbsolutLeon\n  Luchx\n  MatsMTN\n  Richard Stanway (r1ch)\n  J T (JT4)\n  Dominik (dschwarzd56)\n  Jesper Mahel (kinsej)\n  Palana\n  deexbeam\n  Bumpsy\n  Jonathan (greenbigfrog)\n  Achampnator\n  Geisteskranker\n  assistantmaster\n  Enderdrache LP (enderdrachelp)\n  Joshua Weber (joshuaw)\n  David (Tulpedublone)\n  Fabi135\n  Prince_of_Raop\n  SlimPlay\n  Patrick Frings (Ragnos)\n  Robin Hielscher (Jack0r)\n  unknowndomain\n  xxOrdulu52xx\n  css\n  Tomasz 'grocal' Grodzki (grocal)\n  David Schmidt (Dav32_1)\n  Finn Hoffhenke (Finnlife)\n  Adrian S (wuku0)\n  Holger Hupfer (2holgerhupfer)\n  Noah Alexander (noahalexanderhb)\n  AndreLeonardo (andreleonardoyt)\n  Lord Aidan (BadSideofBright)\n  BoJustus\n  CocofireHD\n  Wellington Estevo (propz)\n  Tiim\n  Hadi Gamer (hadigamer3131)\n  hakuchi\n  degra78 (degra789)\n  NotTwerp\n Greek:\n  swatzniker (dimitrisrtg324)\n  Katerina (katerinaramm)\n  jimkats\n  Scourgemcdak\n  Mepharees\n  Iwannhs Kokkinhs (pigot12)\n  TheMetalHelper\n  Dimitris Papaioannou (dimtpap)\n  Georgios Kafanas (gkaf)\n  Tasos Sahanidis (tatokis)\n  Gselivanof (gselivanof)\n  George T. (tzikas97)\n  Nikolaos Peltekis (nikpelgr)\n  opensavk\n  NickMihal\n  dimitris45\n  Greek Transliterator (greektransliterator)\n  Enes (Edinbo)\n  Qufy\n  ROkelbow\n  Friend Of Nature (kalisperakichris)\n  kioydio\n  Jim Spentzos (dspentzos)\n  leskal\n  Ανδρέας Γεωργόπουλος (andrewgeo)\n  Dimitris Vagiakakos (sv1sjp)\n  Warmaster\n  Kostas F (Kostas-F)\n  Gol D. Ace (goldace)\n  Efstathios Iosifidis (iosifidis)\n  Giorgos (Bald)\n  Themis T. (Deminho)\n  Jason_M\n  unknowndomain\n  mixasgr\n  Antwnhs M. (AntwnhsM.)\n  OBS-Staff\n  Christis Andreou (ch.andreou)\n  chaironeko\n  Dumb Thoughts (DumbThoughts)\n Hebrew:\n  Yaron Shahrabani (YaronSh)\n  djsavta\n  amirsher\n  lonelywolf11\n  David Furman (thefourCraft)\n  idanbo27\n  Yonatan (yonatando)\n  עידו דנה (idodana01)\n  nachmanpop\n  teddy vadmad (dubonchik)\n  jhon game (jhongamepc2)\n  Yotam Ziv (yotamziv87)\n  Ethan (EthanDevelop)\n  Green Army (ofek18)\n  Chemi\n  Gol D. Ace (goldace)\n  Eilon B. (eilon_beno)\n  yuvyuv500\n  Light1c3\n  Yoel (ye8323)\n  epic_ziver_D\n  Netro (roi_adler)\n  Ilya Neustroev (DasTen)\n  Hilla Glebotzki (Hilla123)\n  David Zabrovsky (david.zabrovsky)\n  Aguy33\n  yoni3D\n  TheOver (upmeboost)\n  unknowndomain\n  yair (5shekel)\n  ghsi\n  אפיק רוזנר (afikr333)\n  roeiad (roi-adler)\n  Tal Machani (talmachani)\n  DanPinkas\n  Temp Google Email Account (tempgaccj)\n Hindi:\n  Bashishtha Narayan Singh (bashishtha)\n  Saswata Banerjee (azure.saswata)\n  nkamae\n  Syed Ali Naqi Hasni (syedalinaqihasni)\n  BIJAY NAYAK (bijaykumar)\n  Ravindra Pednekar (raviped)\n  Rahul Dhangar (rahuldhangar)\n  SneakyFish5\n  shamuntohamd\n  csgd (csgdV2)\n  Devendra Poonia (programerd)\n  Sid-Shanker\n  ROkelbow\n  unknowndomain\n  Arnav Kumar (arnavkumar)\n  Saket Anand (saketanandjha)\n  Gol D. Ace (goldace)\n Hungarian:\n  Gige\n  Balázs Meskó (mesko.balazs)\n  Balázs Meskó (meskobalazs)\n  zolikonta\n  Gol D. Ace (goldace)\n  Sárga Pulcsis (SargaPulcsis)\n  ItzAndriss\n  Daniii_\n  Isti (eisti)\n  ROkelbow\n  Attila Németh (nmthatti)\n  TOM005\n  henn89\n  Schweiszgut Márk József (mynter)\n  Adam Liszkai (adamos42)\n  Tóth Ádám (totemadam)\n  IrepY\n  Dér Kristóf Gordon (Gordi90)\n  vargag159\n  Biró Bence (birobence19)\n  Levente Parádi (Techno.HaX)\n  unknowndomain\n  therealnandy\n  abydosan (abydoshun)\n  not.skelly\n  Minta Máté (Cubebanyasz)\n  damagepy\n  OBS-Staff\n Icelandic:\n  Sigurður óskarsson (sig.oskarsson)\n  Ashley (VipelyRS)\n  David Vector Varela Ungido (sdgvnonreal)\n Indonesian:\n  Anbiya Shafaat (Anfia)\n  M Akmal (akmalcakep59)\n  heydootdoot\n  Rafy Shafryan (SirNexa)\n  Lieba Natur Brilian (naturbrilian)\n  Rendi (rendiazmi)\n  ziasukmana\n  GudankGaming GG (GudankGaming)\n  Agustine Carolina (agustinecarolina483)\n  Rahman Yusri Aftian (aftian)\n  dnpxs (Dnoxs)\n  Deraidos\n  Asahi itoza (azza_su)\n  Dennisbdhdkdsgslaowhsosvsj Hskdldkd Trousershsjsodhdldheodhdisjsvs (saxwelcoki)\n  Irgi Fachrezi (IrgiFachrezi)\n  Jovan Ferryal E. F. (Jovanzers)\n  doplank\n  unknowndomain\n  Yanwardo Sitanggang (yanwardos)\n  MichaelJ\n  ROkelbow\n  Azhe Kun (azhe403)\n  Dian Wahyugi (IANSKUDO)\n  Jeff (ExpansionKit)\n  Efsan Simanjuntak (Efsan_Simanjuntak)\n Italian:\n  ROBERTO BORIOTTI (bovirus)\n  StarFang208\n  LordShadow95\n  Marocco2\n  uman\n  tiwi90\n  imcesca\n  smart2128\n  Vincenzo Reale (vinx.reale)\n  Sergio Beneduce (sbeneduce)\n  Ruggero Tomaselli (ruggi99)\n  Michele (ScrappyCocco)\n  Albakham (albakham)\n  OBS-Staff\n  Marco Ciampa (ciampix)\n  Alessandro Sarto (alesarto03)\n  Tommaso Cammelli (tomganguz) (tomganguz)\n  Christian Mazzola (ChriMazzola)\n  Martazza\n  Alfonso Scarpino (alfonso.scarpino)\n  mauriziopersi\n  ScemEnzo\n  M K (michelek.)\n  Edoardo Macrì (edomacri)\n  Alessandro (AlexzanDev)\n  riccardotornesello\n  Gol D. Ace (goldace)\n  Leonardo Bertani (bertanileonardo)\n  ilgattonelsacco\n  Eric Botter (EricBotter)\n  Edoardo “OfficialDJMela” Macrì (agersforum)\n  Elena Shevaara (elenapinna15)\n  Silpheel\n  gianni morandi (strabbioboy)\n  Giulia (GhostyJade)\n  nkamae\n  Fisherozzo\n  DomenicoDD\n  icovada\n  Eugenio Tomasella (eugeniotomasella9)\n  bart reynaard (bart.reynaard)\n  Alessio Ilardi (alantheandroid)\n  SkyLion\n  Cristian Alfano (CristianAUnisa)\n  unknowndomain\n  Benedetto Mazza (benedetto.mazza99)\n  Lucia Spinello (Freya89)\n  Clarensio\n  Furry (furry_femboy_uwu)\n  Owly\n  Federico Tensi (habby1337)\n  Matt (NightMat)\n  Flavio Rinaldi (flaviorinaldi)\n  Rodney (derrod)\n  Loweredgames\n  Augusto (ForenXis14)\n  Alessandro Iepure (alessandro_iepure)\n  Daniele02_777\n Japanese:\n  Kenta Takumi (style1925)\n  shousa\n  Bhanu Victor DiCara (SplatRT)\n  青柳和宏 (sherlock1891holmes)\n  saygo1125\n  OBS-Staff\n  森の子リスのミーコの大冒険 (Phroneris)\n  CKK COBALT (CKKCOBALT)\n  Hiroki IWAMA (higamma)\n  SeasonChannel3322 (SeasonChannel32)\n  akawshi\n  nishi-ruse\n  meitel1014 (meitel1234)\n  kotobato\n  ラズベリージャム (rpiuser1923)\n  Kanji1113 (kanji1113)\n  nkamae\n  Gol D. Ace (goldace)\n  ksdesign_jp\n  gogstudio\n  ato lash (hal_shu_sato)\n  unknowndomain\n  依神紫苑 (MonogoiNoobs)\n  神成フィルム (kami00nari)\n  ? (a2y4)\n  GO Channel (georgestv.0129)\n  Bob Liu (Akagi201)\n  Tolr\n  chika takada (miyu1224)\n  chaironeko\n  Alex Shafer (enzanki-ars)\n  Yuki Yu (Yukiyu)\n  Diogo Ramos (dr03ramos)\n  ushu kura (kuraushu)\n  pinkoro\n Kabyle:\n  Yacine Bouklif (YacineBouklif)\n  ZiriSut\n  Azwaw Aqbayli (Azwaw)\n Karakalpak:\n  KarLin - Karakalpak Translators Team (karakalpaklingvo)\n  flower (flower_flower)\n  Mukhit Nurmanov (nurmanov)\n  camexin\n  Zhanserik\n  Джон Доу (izmarsa)\n  Shingis Joldasbaev (joldasbaevkhan)\n  Niyazbek Tolibaev (niyazbeksayipnazarovish)\n  andyme\n Korean:\n  ynetwork\n  영구땡 (wonkyupark)\n  그냥기여자 (justcontributor)\n  Alex Lee (alex01763)\n  hibiya inemuri (hibiya)\n  EP45\n  yeoneer\n  Hwanyong Lee (grbear)\n  AlexKoala (alexkoala)\n  임세훈 (sh9351)\n  vyteking\n  임시이름 (H_FeelsGood)\n  PaperCore (pprfp)\n  John Lee (john3374)\n  Gol D. Ace (goldace)\n  TRESTEUK777 (TRESTEUK)\n  RedditRook\n  hoehoetvhs\n  이지완 (Jiwan)\n  ppapman\n  _XD\n  켈빈926 (hsjang2000)\n  Kim (Fomalhaut0098)\n  Jiwon Jung (Connor04)\n  Jong Kwon Choi (dailypro)\n  Ra.Workspace (RaWouk)\n  ilsubyeega\n  SteamB23 (steamb23)\n  무아MUA (moajikyeongtv1048)\n  rldbs3411\n  DHtheCreator\n  김희석 (hskimse1)\n  hykdem kim (hykdem)\n  미르냥 (Mirnyang)\n  Russell (crimeroyal)\n  unknowndomain\n  Charles Wallis (charlestw127)\n  이승진 (smctgrass)\n  Look Studio (yoon080708)\n  Hana (momenthana)\n  antome\n  Tristar Corp (Charleslee)\n  bluestar8\n  swatzniker (dimitrisrtg324)\n  Nesswit (rishubil)\n  김동현 (ehehguu)\n  Bi0 D. (lefhi0731)\n  OBS-Staff\n Kurmanji (Kurdish):\n  Cyax (Cyaxares)\n  cehnemdark\n  Roj (roj1512)\n  ROkelbow\n  bkrhnr\n Lao:\n  bone NI (bounkirdni)\n  nirankoon.singpraseuth (nirankoon)\n  Baker123\n Latvian:\n  oskars\n  rinalds rinalds (rinalds.dobelis)\n  Andris Liepiņš (ahgpuc)\n  Arthur (ArthurLV)\n  Imants Vancāns (Imants565)\n  Dejelnieks (dejelnieks212)\n  Mops3005\n  Peridot Nation (PeridotNation)\n  Markuss Strods (Marcuss_5001)\n Lithuanian:\n  Viljamas (Vegas1)\n  Justas Vilimas (tyntas)\n  xNaii (lyrikas5)\n  Tool Way (toolwayrblx)\n  TitasK (titukas1572)\n  Akela_LT\n  Mariio_\n  Drakas Drakun (drakonas2018)\n  Rolic\n  Tboy Adey (ttlolsucs)\n  Nikita Podvinskij (PODWINSKi)\n  MuscileTurze\n  justas beisinas (idkjhui)\n  dnew (dbot)\n  Pufikas\n  nojusu (nojus.urbutis1)\n  ugnyteaaa\n  unknowndomain\n  Gol D. Ace (goldace)\n  HighSierra Fox (highsierrafox)\n  justas beisinas (justas.beisinas2008)\n  AquantiuM\n  Baker123\n Malay:\n  abuyop (Abuyop)\n  AnNamir\n  amsyar ZeRo (amsyarminer555)\n  WeingHong\n  Srap Dsign (srapdsign)\n  ROkelbow\n  Gol D. Ace (goldace)\n  Intannatassya\n  unknowndomain\n  OBS-Staff\n Mongolian:\n  begjan\n  Erhmee\n  Uilsbat Arslan (Velox729)\n  Bilguun Ochirbat (BilguunOch)\n  unknowndomain\n  Mbay\n  Gol D. Ace (goldace)\n Norwegian Bokmal:\n  Imre Eilertsen (DandelionSprout)\n  Taesh (magnusmbratteng)\n  flaeri\n  Oddbjørn Grytdal (OGrytdal)\n  FredrikOversetter\n  Patrick Williamson (wpatrick59)\n  Mats Edvin Aarø (matsedvin)\n  OBS-Staff\n  LandyLERThERmfLOpi\n  areedw\n  Qrynill\n  Hvidsten Media (HvidstenMedia)\n  Brage Fuglseth (bragefuglseth)\n  mgKaiztra\n  Tommy (nwgat)\n  Syver Stensholt (7RST1)\n  Jordan Stutzmann (J-Stutzmann)\n  Alex Thomassen (Decicus)\n  Kenta Moe (Owndragoon)\n  Sander Skjegstad (r530er)\n  Gol D. Ace (goldace)\n  OsteHovel\n  Lauren (loritsi)\n  Legend27\n  Pigwin_\n  Scott Alvin Westbye (scottalwest00)\n  unknowndomain\n  chaironeko\n  Mats Andreassen (MatsA)\n  barecharge\n Norwegian Nynorsk:\n  Bjørn I. (bjorni)\n  Imre Eilertsen (DandelionSprout)\n  Eiliv Ulvestad (Eiliv)\n  morden\n  LandyLERThERmfLOpi\n  Qrynill\n  unknowndomain\n  Yngve Spjeld Landro (yslandro)\n  Gol D. Ace (goldace)\n Occitan:\n  Diluns\n  PinguiFabgeeklab\n Persian:\n  Johnny Warner (johhnywarner)\n  koper\n  MZ MAXIMUM (mahdigamermax)\n  Peyman M. (peymanr34)\n  AmirGhorbani_ir\n  ahangarha\n  Pikhosh (pikhosh)\n  Danial Behzadi (danialbehzadi)\n  TiXO\n  MohammadSaleh Kamyab (mskf1383)\n  Mohammad jawad (mrahmani788)\n  Amin Mahmoudi (masterking32)\n  Farshid Meidani (farshid_mi)\n  mahdi ram (mhadi-ram)\n  rahimz\n  ROkelbow\n  Gol D. Ace (goldace)\n  PRNUse\n  alefvanoon\n  TqkeL\n  Hosein Hallavi (habibot81)\n  MehdiAmirirad (Mehdiamirirad)\n  unknowndomain\n  RytoEX\n  Meysam Naghavi (MeysamNaghavi)\n  lostman2000\n  XubeIR\n  arashe_023 (arashe023)\n  Amirhossein yousefi (amir.sara)\n  Navid Sadeghieh (nsadeghieh)\n  Erfaniom\n Polish:\n  Tomasz 'grocal' Grodzki (grocal)\n  Matthaiks\n  Albakham (albakham)\n  Vassamo (jotpl69)\n  The Syntox (TheSyntox)\n  kaktus (_kaktus_)\n  mjluszko\n  Melons\n  Michał Durak (micechal)\n  Damian Korcz (damikiller)\n  Łukasz Wójcik (lwojcik)\n  Hexandcube (hexandcube)\n  Daniel Wieczorek (Kennyluz)\n  Iwo24pl\n  sebek1pan\n  Mateusz (Silesianek)\n  Mateusz Starzak (MatiasPL)\n  Gol D. Ace (goldace)\n  opl\n  Popke\n  Skajdrovski\n  Sebastian Jasiński (PrinceNorris)\n  Michal (progamesmicrok)\n  Patryk Radziszewski (Chefski)\n  Xslash\n  unknowndomain\n  RytoEX\n  Piteriuz\n  ThatSteve1721\n  Hubert Degler (Hubertoos)\n  seba (xseba)\n  adamek2314\n  Super Polakowy (pawi.sun)\n  OldShaterhan (tomek12-96)\n  OBS-Staff\n  Julia Drewniak (ewagsi)\n  partypiotr\n  Maciej Ślusarz (tendan)\n  Michał Lewczak (michal200507)\n  Patryk Kunda (ner.i.ol)\n  Nojterone\n  Maciej Błędkowski (mble)\n  Olsza (olsza)\n  cooky\n Portuguese:\n  horus68\n  Ricardo Subtil (rsubtil)\n  João Vieira (joaofvieira)\n  batatasmor\n  Manuela Silva (mansil)\n  André Biscaia (LazP)\n  Tomás Antunes (tomasantunes)\n  Albakham (albakham)\n  x1k1nh0\n  Rafael Peixoto (rapmje)\n  OBS-Staff\n  joaoboia\n  alexandre433\n  Pedro Simões (404PeterSimoes)\n  Pedro Mendonça (pedromendonca)\n  pedro.moreira.359 (pmmorsoft)\n  Nuno Matos (NunoCMatos)\n  Gol D. Ace (goldace)\n  Gatinhos\n  Christophe Carvalho Vilas-Boas (ChristopheCVB)\n  vanscorp\n  Gabriel Lima (gablm)\n  Diogo Soares (DiogoSoares)\n  Pedro Ricardo (Pedro270707)\n  kayal15948\n  andre_satorres\n  João Faro (FaroJoaoFaro)\n  Diogo Ramos (dr03ramos)\n  ROkelbow\n  unknowndomain\n  Paulo Soares (psoares.gm)\n  Gost_Evil\n  Tiago Mesquita (tiagomesquita)\n  Hélder Ferreira (Mtx32)\n  Rafa.Godoy\n  moonlightz (moonlightzzz)\n Portuguese, Brazilian:\n  Diogo Ramos (dr03ramos)\n  Shaolin (admshao)\n  Marcelo dos Santos Mafra (msmafra)\n  Lincoln H. (LincolnH)\n  Skellingtor\n  igorruckert\n  Ramon Mendes (rbrgameplays)\n  Fabio Madia (Shaolin)\n  Burkes\n  Eneias Polini (Eneias)\n  Emanoel Lopes (emanoelopes)\n  Pedro Ricardo (Pedro270707)\n  Guilherme Dias (Darkaiser)\n  Gabriel Conceição Nunes (GabrielCNunes)\n  Pedro Brantes (brantes)\n  Thiago França (TFSThiagoBR98)\n  Bruno Lopes Mattos Luiz (lopestranslate)\n  CaioWzy\n  mizifih\n  VALTER HITELMAN (vhitelman)\n  Luiz Machado (qluizmachado)\n  André Gama (ToeOficial)\n  andre_satorres\n  Luciano Santos Gonçalves (luciano.ivec)\n  Avellar (BetaTester)\n  Maison da Silva (maisondasilva)\n  VergiI\n  ドージェ (Doges)\n  clr0dr1g\n  aalonsomb\n  Gol D. Ace (goldace)\n  Gabriel Rodrigues (GBRodrigues)\n  Cauan Henrique Zorzenon (cauan_zorzenon)\n  Alexandre Frigeri (alexfrigeri)\n  Suni (SuniModni)\n  ROkelbow\n  Murilo Ferrari (ferrarintc)\n  Gabriel Thiago (gth1ago)\n  morittan\n  LoboMetalurgico\n  Leonardo Ostan (lostan)\n  dav (Davizinhn)\n  Ropelato (rRopelato)\n  Celso Fernandes (Celsof)\n  mathues\n  judy manzaneda (judymanzaneda95)\n  Eduardo Araujo (Finhawk)\n  Nicolas Gomes (Nicolas_Teixeira)\n  Matheus Fongaro (matheus.fongaro)\n  Hustuha\n  maribatis\n  Eriberto Mota (eribertomota)\n  Dieguinho\n  JNylson\n  DanielTaqueto\n  Alex Smotra (smotraalex75)\n  luis0252\n  Lucsdev_ (lucsdev24)\n  lakitoo\n  DJ Matheus Martins (DJMatheusMartins)\n  Tetri Mesquita Neto (tetri)\n  ghklug\n  Mario de Carvalho (mariooak83)\n  Matheus (zxya)\n  DevilLorde\n  João (fror)\n  unknowndomain\n  ThomasLawliet (thomaslawliet)\n  Cirnos\n  Ramon Gonzalez (ramon200000)\n  Esdras Tarsis (esdrastarsis)\n  Guilherme Cruz (gcrz)\n  Miguel Balbo Victor (miguelbalbovictor)\n  Rafhael Marchioretto (rafhaelmarchioretto)\n  OBS-Staff\n  Eduardo Addad de Oliveira - Eduaddad (eduardoaddad)\n  Com.praideal Com.praideal (ccompraideal)\n  Carlos Adriano de Souza (cadriza)\n  Tomy Grado (tomygrado)\n  Jair Reis Junior (jairreisjunior.pessoal)\n  andre.krdoso\n  dlageBR\n  park (TheBugMeNot)\n  Guimaverick\n  Xlindvain\n  Tiago Danin (TiagoDanin)\n  Diego Da Silva (diegosilva060597)\n Punjabi:\n  manjotsingh0202\n  unknowndomain\n  Horizon Plaza (plaza.horizon90)\n  Sheikh Ahmed (sheikhahmed)\n Romanian:\n  Cristian Silaghi (sno_w)\n  Kele Ion Ion (krovyoll)\n  DragonulRed\n  banrek\n  eex\n  Cristian Secară (secarica)\n  Hisashi\n  Andrei Ionescu (abcdedjdmddx)\n  Voie Tudor (tudorvoie2008)\n  BlakeNowah\n  Ant. Rares (Iepurooy)\n  Andrei (andreisavin)\n  Doru Stoica (Dorulian)\n  Victor Paul (corvinpaul)\n  Skellytone\n  Mihai G (babasghenciu)\n  Eduard Andrei (28edx)\n  Sw3aty1 Cristi (ca1510797)\n  Gol D. Ace (goldace)\n  nkamae\n  Melonoone\n  Trif Alexandru (LupusMechanicus)\n  EnderIce2 (enderice2)\n  Andrii Popovych (Andrii-Popovich)\n  PoisonedReality_exe (2xotravity)\n  Diamyx\n  unknowndomain\n  ICalz Plays (romanu100)\n  alex123 alex123 (dumitruvalentin44)\n  OBS-Staff\n  chaironeko\n Russian:\n  Andrei Stepanov (adem4ik)\n  Alek Nirov (dectanova)\n  iltrof\n  Blueberry (Blueberryy)\n  EDNVKjldr8vyu9\n  ViktorOn\n  Pavel (Shevalie)\n  VNGXR\n  Сергей (nasferatus)\n  Рушан Мухутдинов (deflecta)\n  Bugo\n  MishaRey (mishaareyy)\n  Әлмәт Ак Арыслан (19082004amir)\n  Kolya Майнкрафт (KolyaYT_)\n  Maxim Gribanov (MaximGribanov)\n  OBS-Staff\n  Oleg (yokidjo)\n  Romka Almazniy (turbozver)\n  Fitik\n  Gol D. Ace (goldace)\n  PanForPancakes\n  tochnonechelovek\n  Artem (Artem4ik)\n  Gleb (Apeol4)\n  Yaroslav (MrYadro)\n  css\n  Synbulat Biishev (Syjalo)\n  fromgate\n  Пахомов Александр (GXTrus)\n  Andrey (sandrix)\n  kirbyhi5\n  TDoT\n  Антон Илясов (Vewa9907)\n  Sirboys\n  WoWnik (wownik98)\n  Irisik\n  VikHill\n  er123rin (se123rin)\n  Andy (anry025)\n  Nemesis (Nemesis07)\n  NEO FEED (srdsafdsfsdfsdfsdgdfh)\n  Просто Взут (JustVzyt)\n  Zonega\n  bxkr\n  Utoog (rivus)\n  Serge Sklyarov (sergesklyarov)\n  Anton Bershanskiy (bershanskiy)\n  Makatavin # (makatavin0)\n  hsd channel (hsdch)\n  AnmiTaliDev\n  slicht\n  Mr Misha (MrMisha)\n  Nikita Epifanov (Nikets)\n  Fik Dencer (4Soul)\n  exzolink\n  Tihonov Matvey (Rythmical)\n  RaspingMango (RaspingMango637)\n  Vlad (KoTmaxHo)\n  keirokeer\n  Vladimir (jeffors)\n  xtemp09\n  Dan Ten (daniil.317.d)\n  Myasko\n  Mikhailus_\n  Mikhail Paulyshka (Mixaill)\n  Agehrg4\n  denfry\n  Super 2004 (pe86ki)\n  Иван Геймеров (hronos-2t)\n  Дима Исаев (dima.isaev2005)\n  Nikita Bibanaev (nicky18013)\n  Sergei Fug1t1v3 (fug)\n  Максим Куренков (kuplay)\n  pavuma\n  kana86\n  Цезарь Ролл (CeaserRoll)\n  Walt Gee (vovanych)\n  unknowndomain\n  zy9c 1337 (zy9c228)\n  Илья Кузнецов (ilyavj)\n  allan walpy (AndreyLysenkov)\n  Ninja Hacker3000 (ninjahacker3875)\n  evilserge\n  Kriper1111\n  Drahonn\n  Alex Smotra (smotraalex75)\n  Yuri Mihaqlov (yurijmi)\n  SandoBY\n  Meldone (meldone)\n  Mr. Games (oohskofgs)\n  BeDubber\n  dankuri (samaxe13)\n  Anatoly (Atsupak)\n  MeDustyy\n  Nezila Nez (Nezila)\n  Башҡорт агенты (vint6908)\n  Timofey Sosnovsky (timofey.sosnovsky)\n  Kcurett TM (thekcurett)\n  Leika098\n  Sigge Stjärnholm (Kladdy)\n  iLefty\n  TR1D\n  Evgeny Bogdanov (vtrifonov548)\n  TOWUK (towuk)\n  mchg _ (mchg)\n  SurPaul\n  Kirya Kirya (Sk1pplex)\n  MUHADDIS MEDIA (muhaddismedia)\n  Kirill Arkhipov (dokl957)\n  MeoWStraY\n  Flint Insects (flintinsects)\n  Eduard Račkoŭski (ERFromYouTube)\n Scottish Gaelic:\n  GunChleoc\n  Endo (Balisong)\n  Dani K (SkiaSDK)\n  Alex Hall (Decidy)\n  Alex Smotra (smotraalex75)\n  unknowndomain\n  Gol D. Ace (goldace)\n Serbian (Cyrillic):\n  nikolanikola\n  Issa1553\n  medicmomcilo\n  styrofoam\n  Анониман (anonymousprivate76)\n  lukazivanovic\n  Acamicamacaraca\n  М. П. (rwrwwwr)\n  Gol D. Ace (goldace)\n  veles330\n  LittleGirl_WithPonyTail (alexs1320)\n  unknowndomain\n  Daste\n  OBS-Staff\n  scienceangel\n Serbian (Latin):\n  Issa1553\n  nikolanikola\n  medicmomcilo\n  Igor Ilic (gac)\n  LittleGirl_WithPonyTail (alexs1320)\n  Nikola Perović (Fooftilly)\n  Gol D. Ace (goldace)\n  Rale Sarcevic (ralesarcevic)\n  unknowndomain\n  Marko Šekuljica (markoshaq)\n  OBS-Staff\n  scienceangel\n Silesian:\n  Psioczek\n Sinhala:\n  Namal Jayasingha (nimnaya)\n  HelaBasa Group (HelaBasa)\n  rpu ysam (rpuyasi)\n Slovak:\n  Lukáš Čeremeta (luki1412)\n  Erik Bročko (ericek111)\n  Daniel Dekan (daniel.dekan111)\n  Ján M (longmoped)\n  Filip Hanes (filip.hanes)\n  Anton Lokaj (anlo)\n  Vladimír Krajč (mkkyy)\n  MartinRatvaj\n  Gol D. Ace (goldace)\n  Miroslav Machala (Miroslav_Machala)\n  LoLLy Nka (lollynka279)\n  Richard Gráčik (Morc)\n  LUK3X - SK - (luk3x)\n  ROkelbow\n  unknowndomain\n  HelloI'mUnderThe WaterPleaseHelpMe (BoBTHEbuilder)\n  OBS-Staff\n Slovenian:\n  Arnold Marko (atomicmind)\n  Martin (miles)\n  kristjan.krusic (krusic22)\n  admrs\n  Aleksander Kovač (Acookook)\n  MG lolenstine (mglolenstine)\n  mspacapan\n  ROkelbow\n  Grimpy\n  Gol D. Ace (goldace)\n  ArcaneWater\n  unknowndomain\n  OBS-Staff\n  FixyFN (nikolaj.ceferin)\n Spanish:\n  Monsteer\n  Marcos Vidal Martinez (M4RK22)\n  Jaime Martinez Rincon (mrjaime1999)\n  Jaime Muñoz Martín (jmmartin_5)\n  Honzo (HonzoNebro)\n  Sofía Páramo (SweetSofiMC)\n  Alex E. D. B. (alexedb)\n  Iñigo Zulueta (eggamer131)\n  Trimax\n  Pilar G. (TheMadnessLady)\n  MarioMey\n  Adolfo Jayme (fitojb)\n  marcrisale\n  Gol D. Ace (goldace)\n  Manuel Matías (manuel.matias)\n  716gjesu\n  Alejandro Alzate Sanchez (alejandroalzate)\n  Alejandro Moctezuma (AlejandroMoc)\n  Cristhofer Sanchez Solano (JafetSS)\n  Lluís R (LluisR98)\n  francisco Emanuel montero cordoba (monterofranciscoemanuel)\n  Maximiliano Schtroumpftech Pena-Roig (som2tokmynam)\n  fordink257\n  Carlos Plata (Braixenfan989)\n  JaviermbPlays (JavierMB)\n  Gonzalo Lorenzo (Gxnzalo)\n  Kael (KaelSky)\n  eemiroj\n  Hector meneses (hector.meneses)\n  henrycontreras\n  saviour6699\n  Skiddome (sergiomalagonmartin)\n  Marcos Vidal (markitos.maki22)\n  Ruben Deig Ramos (rdeigramos)\n  1 (Ipsumry)\n  makiza1 (micosil_2)\n  Santiago Pereyra (SannttVIII)\n  FloodedVoyage45\n  jan (test83318)\n  David Sonico (davidsubsonico)\n  Zatki\n  Jokeestd\n  Sr_V\n  Eleazar (MtrElee3)\n  AndMe (accomigt)\n  Agente E (agentee200)\n  Kevin Reyes (Kvrnn)\n  Gabriel Arriagada (ggvbo)\n  ian Vatega (ianvatega)\n  amssusgameplays (willifake052)\n  D E B (D_E_B)\n  Stephen Q (qstephen73)\n  Krynlix\n  Jaire (corpi.98)\n  Luis Carlos González Morales (luiscarlosgm)\n  Dominik K. (mezotv)\n  Sigge Stjärnholm (Kladdy)\n  unknowndomain\n  Mahin Rafi Alam Hassan (Rafi13082007)\n  Charlongolo\n  Micaela Rivero Aguirre (riveromikaela15)\n  Amy (amyipdev)\n  neuzeh\n  Joshua Peisach (ItzSwirlz)\n  jiseokss\n  OBS-Staff\n  Rodrigo Ipince (ipince)\n  Juan Esteban (Juanskeree)\n  chaironeko\n  Gloriana Cocozza (gcocozza)\n  Streamlabs Translator (Streamlabs)\n  Andy Venegas (andyvenegashernandez)\n  Monoverde\n  Rubén Pérez (RixzZ)\n  Mogabie's Nation (Mogabie)\n  GarbageAtSB\n  LARRY1989\n  Radiaktive\n Swedish:\n  Anton R (FirePhoenix)\n  Sigge Stjärnholm (Kladdy)\n  Laccy IEST (Laccy)\n  LinusW\n  Bjorn Astrom (beeanyew)\n  0x9fff00\n  Kristoffer Grundström (Umeaboy)\n  Gustav Ekner (ekner)\n  Victor Ahlin (VSwede)\n  Johan Andersson (RealIndrit)\n  Really Super Otter (ReallySuperOtter)\n  Gustav Lövgren (morsgris)\n  Mats Karlsson (matska)\n  ArvidTheSwe\n  PonPonTheBonBon\n  hentor5\n  Olle Dahström (odahlstrom)\n  nahkampf\n  dotar (TorstenAugustsson)\n  Gol D. Ace (goldace)\n  Dennisbdhdkdsgslaowhsosvsj Hskdldkd Trousershsjsodhdldheodhdisjsvs (saxwelcoki)\n  Henrik Mattsson-Mårn (rchk)\n  unknowndomain\n  Axel Aminoff (axel.aminoff)\n  chaironeko\n  Lennart Bonnevier (lennartbon)\n  Jonas Svensson (jonassanojj99)\n  Hannes Blåman (hannesblaman)\n  pitedaniel\n  TacticalKebab\n  kronblom2\n  OBS-Staff\n Tagalog:\n  dandalion\n  jermel\n  jbeguna04\n  StartsMercury\n  philiparniebinag\n  TheBMG Playz (thebmgplayzyt)\n  Napstaguy04\n  Gol D. Ace (goldace)\n  Red Dayao (steemitph)\n  Zed (lyze_ff)\n  Laarnice\n  unknowndomain\n  Raylir\n Tamil:\n  anto27\n  Jaiganėsh Kumaran (Jaiganesh)\n  GobinathAL\n  rooban23\n  Devanathan T (nathandeva26895)\n  Gol D. Ace (goldace)\n  Kolappan N (kolappannathan)\n  unknowndomain\n  Naveen Techs (alexonpeace)\n Tatar:\n  Әлмәт Ак Арыслан (19082004amir)\n  Timur Seber (seber)\n  Рушан Мухутдинов (deflecta)\n  Ilgiz Zigangirov (inov8)\n Telugu:\n  Sampath Reddy (Sampath123)\n  ror0ror02\n  CRTL Amaravati (rrskiran)\n Thai:\n  aefgh3962\n  Yarika Sutthiwong (ammaneena)\n  เทพประสิทธิ์ โพลาหา (Thepprasit300150)\n  Alvah Connelly (AlvahConnelly)\n  Aefgh Threenine (aefgh39622)\n  Slowlife (Slooowlife)\n  戸渡 生野 (アオムシン) (aomyouza2543)\n  ppondd2020\n  Nawin Somprasong (thaipirch98)\n  Kamolpat Petsuwan (Paksa)\n  zero0200\n  ธีรภัทร์ โยชนา (Gataro)\n  Pawat Chivapruk (pawattoto)\n  PolaX3\n  Narintorn101\n  sakuhanachan* (sakuhanachanloli)\n  SurachaiJUI\n  Sakia Normal Human (arcanaarcana5)\n  XZP (XZP) (xe4a177)\n  นักลูบคม อันดับ 1 (mrmanyt629)\n  2RD Channel (2RD)\n  Worawut Weerawan (nero.exe)\n  PlyKung (plykung)\n  ธีร์ ธีรพล (teraphonk)\n  Aelirea Yu (aelireayu)\n  Oat. Thanawat (thanawat6484)\n  盛凤阁 (execzero)\n  Afdol Kareena (adrmnq)\n  nongnoobjung (kitcharuk_4)\n  301-14 สิรวิชญ์ ศรีคําเมือง (Sam_Hanhyongsok2006)\n  Anucha (achn.syps)\n  Rathchaarnon Threeanjuleethaan (rathchaarnon)\n  Yuthana Phanom (uyuth19thai)\n  unknowndomain\n  แมน ทศพร (lovemanna456)\n  Worametee Prasarnsieng (worametee)\n  Enigmatic_\n  Tinnaphat Somsang (tinarskii)\n  OBS-Staff\n  platong (PLATONG98)\n  Gol D. Ace (goldace)\n  Ryan Gleeson (Ryan_7149)\n  วรภร หนูปลอด6.1 (woraporn.hnu)\n  123456789sssssssssssssssssssss\n  Nydeawia Laurent (aeph1edur)\n Turkish:\n  monolifed\n  Ali Kömesöğütlü (Mobile46) (byzlo685)\n  omer.karagoz (mrkaragoz)\n  Metehan Özyürek (MetehanOzyurek)\n  vgezer\n  Ufuk Furkan Öztürk (UfukFurkanOzturk)\n  Emir (dirt3009)\n  Nejdet ACAR (nejdetacar)\n  Cemal Dursun (cmldrs)\n  Serhat Öktem (delidolu1adam)\n  Kayhan (SwitchAlpha)\n  Savas Tokmak (Laserist)\n  Burkicannjr\n  Samo (SamoE)\n  Umut kılıç (kilic190787)\n  Burak‏ Eker‏‏‏‏‏‏‎ (mburakeker)\n  Bora Atıcı (Brtc)\n  BreezeQS\n  keke_ea\n  furkanbicici\n  fikarme\n  İsmail Efe Top (Ektaynot)\n  MeskaX\n  Murat Karagöz (anemon_1994)\n  Miss.Syzygy\n  berkanx\n  Ömer Faruk Altınova (Cavit19)\n  Görkem Akova (gorkemakova)\n  Doğukan (Valentineu0)\n  Ebubekir Türker (ebubekirtrkr)\n  oth0pus\n  Bilgehan Demirel (bilgehandemirel)\n  Alperen Yıldız (Sparrow34Tr) (Sparrow34Tr)\n  tikhi\n  Sueda A (sue-a)\n  realdep (realdeep)\n  Gol D. Ace (goldace)\n  berkcan uçan (ibnehayati)\n  Zafer Çopur (zafercopur)\n  Solusyon2\n  Yoke6432\n  gecebekcisi1\n  Furkan Korhan (furkankorhan)\n  Emin Can OGUZ (emincanoguz11)\n  inci (sluggishwitch)\n  Gökay BAŞARAN (basharangyokay)\n  bitigchi\n  Berk Kırıkçı (berkkrkc09)\n  Zafer Yılmaz (yilmazzafer722)\n  myasincavdar\n  Arat Ünal (AratreyizprogamerX)\n  ownieyo\n  Tarık Coşkun (tarikcoskun)\n  ilba ilba (ilbailba)\n  BedirhanGltkn (bedirhangltkn)\n  Ege Uzun (kise5478)\n  Toraichi (toraichi)\n  bkzspam\n  Ferhat Geçdoğan (ferhatgec)\n  Onur Yılmaz (Makaron3422)\n  TahaCelik\n  Baran Mordoğan (okunamayanad)\n  Ata Çağan Atamer (atamerco23)\n  mustafaa\n  Botuser\n  Batıkan Ökten (xdieofgo)\n  Richard Stanway (r1ch)\n  Zay (Zayria)\n  TimurOzcan\n  Nightmare837 gaming (mutlupide)\n  Ömer (upp4r)\n  Caner (06_canerr)\n  Yıldırım Mehmet Nur (MehmetNY)\n  Khedi\n  Niemand (lelledguy)\n  cannnAvar\n  Minot\n  ROkelbow\n  Veysel Okan (viseyn)\n  Frogit DREMMUR (dremmurfrogit)\n  Hydroboost\n  unknowndomain\n  Mustafa Arslan (mstfaa)\n  ulutek\n  Alican Gultekin (Vitaefinis)\n  Fatih Güneş (fatihgunes)\n  Resul Kaya (Reyazeit)\n  Türker Yıldırım (turkeryildirim)\n  Ip Logging Boi (Karatchi)\n  basakbk\n  balik27\n  1 0 (efkanyz)\n  Emre  (Khapnols)\n  Uğurcan Sayan (ugurcansayan)\n  Mert (Memort)\n  chaironeko\n  Huseyin Emre (emreemuzik)\n  Selim Şumlu (maxcoder)\n  OBS-Staff\n Ukrainian:\n  SuslikV\n  Mykola Ronik (Mantikor)\n  Ihor Hordiichuk (ihor_ck)\n  lxlalexlxl\n  Anatolii Bielibov (anatoly136ua)\n  veydzh3r\n  Marm (Marm_Marm)\n  Kefir (Kefir2105)\n  Lino Bico (bicolino34)\n  Dekanenko (dekanenko)\n  slvvko\n  Dijerom\n  Magnust (Limichoper)\n  Юрій (Devinit)\n  ROkelbow\n  Oleh Hnat (aetrnm)\n  FOR___REST (konstantinkostenko96)\n  Свиридюк Іван (fl1per)\n  Дмитро Маркевич (hotr1pak)\n  MishaRey (mishaareyy)\n  Gol D. Ace (goldace)\n  Shannar de Kassal (587)\n  Andy (anry025)\n  kateryna.roiko\n  Andrey (Fokz)\n  fraffrog (zfraaaaa)\n  NoPressure\n  Kirill Stuzhuk (kirillstuzhuk)\n  បងមាន តែអូន (cheaiphone267)\n  geimfis\n  L1Q\n  powerdef\n  DE_Serhii (Fallenbard)\n  unknowndomain\n  1 (284)\n  skuu\n  Maksym Tymoshyk (maximillian_)\n  MeDustyy\n  Lev Leontev (gottagofaster)\n  Володимир Родич (vrodych)\n  Mark Chorney (chorneymark2006)\n  D_D\n  OBS-Staff\n Urdu (Pakistan):\n  Abuzar (a6y3ap)\n  Sheikh Ahmed (sheikhahmed)\n  Rana Awais (ehtisham)\n  tahirsada\n  shamuntohamd\n  unknowndomain\n  Gol D. Ace (goldace)\n  Baker123\n Uyghur:\n  Abduqadir Abliz (Sahran)\n Vietnamese:\n  IoeCmcomc (ioecmcomc)\n  I_am_Vietnam\n  Vichy (tnhung2011)\n  Johnny “max20091” Utah (boostyourprogram)\n  Quang Nguyen (quangnguyen.25)\n  Hưng Nguyễn (hoyostudio)\n  Drake Strike (phjtieudoc)\n  ngoisaosang\n  Dũng Nguyễn (CDzungx)\n  Sylvi? (Sylixa)\n  Gol D. Ace (goldace)\n  zungsle\n  Bui Huy Quang (bhuyquang1)\n  Bach Le (BachWumpus)\n  nguyenthanhphong\n  Loc Huynh (loccun)\n  BIGO - 지혜 (parkjihye98)\n  Nguyen Vu (songnguxyz)\n  Blog Đào Lê Minh (daoleminh2010)\n  Hà Phi Hùng (haphihungcom)\n  Thanh Doan (thanhcs)\n  Ngọc Anh TVE (ngocanh.tve)\n  MichaelTr\n  Bỉ Bỉ Lê (bbkuro)\n  anhduck14\n  Bùi Xuân Hải (real.xuanhai)\n  HUYwx (vodoanhuy301)\n  rezarria\n  Michio Nakano (michioxd)\n  Shinoge\n  Geenzo Gamer (geenzogamer164)\n  unknowndomain\n  Vũ Hải Tây (tayngungo1999)\n  SITUVNgcd\n  oranz21\n  OBS-Staff\n  Dawkin Nguyen (dawkinit)\n  V4Ki3t_VN\n  sharkie\n  NCAA\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\ninclude(\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/common/bootstrap.cmake\" NO_POLICY_SCOPE)\n\nproject(obs-studio VERSION ${OBS_VERSION_CANONICAL})\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Windows\")\n  include(\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/architecture.cmake\")\n  if(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME)\n    return()\n  endif()\nendif()\n\ninclude(compilerconfig)\ninclude(defaults)\ninclude(helpers)\n\noption(ENABLE_UI \"Enable building with UI (requires Qt)\" ON)\noption(ENABLE_SCRIPTING \"Enable scripting support\" ON)\noption(ENABLE_HEVC \"Enable HEVC encoders\" ON)\n\nadd_subdirectory(libobs)\nif(OS_WINDOWS)\n  add_subdirectory(libobs-d3d11)\n  add_subdirectory(libobs-winrt)\nendif()\nadd_subdirectory(libobs-opengl)\nif(OS_MACOS)\n  add_subdirectory(libobs-metal)\nendif()\nadd_subdirectory(plugins)\n\nadd_subdirectory(test/test-input)\n\nadd_subdirectory(frontend)\n\nmessage_configuration()\n"
  },
  {
    "path": "CMakePresets.json",
    "content": "{\n  \"version\": 8,\n  \"cmakeMinimumRequired\": {\n    \"major\": 3,\n    \"minor\": 28,\n    \"patch\": 0\n  },\n  \"configurePresets\": [\n    {\n      \"name\": \"environmentVars\",\n      \"hidden\": true,\n      \"cacheVariables\": {\n        \"RESTREAM_CLIENTID\": {\"type\": \"STRING\", \"value\": \"$penv{RESTREAM_CLIENTID}\"},\n        \"RESTREAM_HASH\": {\"type\": \"STRING\", \"value\": \"$penv{RESTREAM_HASH}\"},\n        \"TWITCH_CLIENTID\": {\"type\": \"STRING\", \"value\": \"$penv{TWITCH_CLIENTID}\"},\n        \"TWITCH_HASH\": {\"type\": \"STRING\", \"value\": \"$penv{TWITCH_HASH}\"},\n        \"YOUTUBE_CLIENTID\": {\"type\": \"STRING\", \"value\": \"$penv{YOUTUBE_CLIENTID}\"},\n        \"YOUTUBE_CLIENTID_HASH\": {\"type\": \"STRING\", \"value\": \"$penv{YOUTUBE_CLIENTID_HASH}\"},\n        \"YOUTUBE_SECRET\": {\"type\": \"STRING\", \"value\": \"$penv{YOUTUBE_SECRET}\"},\n        \"YOUTUBE_SECRET_HASH\": {\"type\": \"STRING\", \"value\": \"$penv{YOUTUBE_SECRET_HASH}\"}\n      }\n    },\n    {\n      \"name\": \"dependencies\",\n      \"hidden\": true,\n      \"vendor\": {\n        \"obsproject.com/obs-studio\": {\n          \"dependencies\": {\n              \"prebuilt\": {\n                  \"version\": \"2025-08-23\",\n                  \"baseUrl\": \"https://github.com/obsproject/obs-deps/releases/download\",\n                  \"label\": \"Pre-Built obs-deps\",\n                  \"hashes\": {\n                      \"macos-universal\": \"9403bb43fb0a9bb215739a5659ca274fe884dbbbcd22bd9ca781c961fb041c42\",\n                      \"windows-x64\": \"8de229cff6f1981508c0eb646b35e644633a5855787b9f5d3b90ae2aeb87ffc1\",\n                      \"windows-x86\": \"fb3c68b75911f292b3206e346053638db1c73605957207445a0a92b33ab5e00a\",\n                      \"windows-arm64\": \"dd87ba00a6cbc153182fb62b3678a3b5021d1d11eb2730442060937a645eb97e\"\n                  }\n              },\n              \"qt6\": {\n                  \"version\": \"2025-08-23\",\n                  \"baseUrl\": \"https://github.com/obsproject/obs-deps/releases/download\",\n                  \"label\": \"Pre-Built Qt6\",\n                  \"hashes\": {\n                      \"macos-universal\": \"990f11638b80a4509e14e8c315f6e4caa0861e37fcd3113a256fbff835ffca29\",\n                      \"windows-x64\": \"c62e82483bc7c0bf199e8ac3220c66a85a6e8a0cd69a05b6d44f873b830e415f\",\n                      \"windows-arm64\": \"cc8ec983de9b7d81aa98beeb1b989d707ee3c73b85b4d41c85d94114eba81f91\"\n                  },\n                  \"debugSymbols\": {\n                      \"windows-x64\": \"aae88a17e0211cb37db6a8602f2e20d69255be1f9700c699008ca5adbce1dde2\",\n                      \"windows-arm64\": \"6e866490277a8b29e82a87fc2f22407f93ddaf86444ea0d284370339a05511b3\"\n                  }\n              },\n              \"cef\": {\n                  \"version\": \"6533\",\n                  \"baseUrl\": \"https://cdn-fastly.obsproject.com/downloads\",\n                  \"label\": \"Chromium Embedded Framework\",\n                  \"hashes\": {\n                      \"macos-x86_64\": \"37bf7571a48c5dfa8519817e4a90a3503a0eb30f9eadd68f4c3e783e363f272a\",\n                      \"macos-arm64\": \"429b50e74f6c174dcfe2f14d8204b54add497eaafe117f7b69ce6bb2354d2626\",\n                      \"ubuntu-x86_64\": \"7963335519a19ccdc5233f7334c5ab023026e2f3e9a0cc417007c09d86608146\",\n                      \"ubuntu-aarch64\": \"642514469eaa29a5c887891084d2e73f7dc2d7405f7dfa7726b2dbc24b309999\",\n                      \"windows-x64\": \"922efbda1f2f8be9e5b2754d878a14d90afc81f04e94fc9101a7513e2b5cecc1\",\n                      \"windows-arm64\": \"df9df4bd85826b4c071c6db404fd59cf93efd9c58ec3ab64e204466ae19bb02a\"\n                  },\n                  \"revision\": {\n                      \"macos-x86_64\": 5,\n                      \"macos-arm64\": 5,\n                      \"ubuntu-x86_64\": 6,\n                      \"ubuntu-aarch64\": 6,\n                      \"windows-x64\": 2\n                  }\n              }\n            },\n            \"tools\": {\n              \"sparkle\": {\n                  \"version\": \"2.6.4\",\n                  \"baseUrl\": \"https://github.com/sparkle-project/Sparkle/releases/download\",\n                  \"label\": \"Sparkle 2\",\n                  \"hash\": \"50612a06038abc931f16011d7903b8326a362c1074dabccb718404ce8e585f0b\"\n              }\n            }\n          }\n        }\n    },\n    {\n      \"name\": \"macos\",\n      \"displayName\": \"macOS\",\n      \"description\": \"Default macOS build (single architecture only)\",\n      \"inherits\": [\"environmentVars\"],\n      \"condition\": {\n        \"type\": \"equals\",\n        \"lhs\": \"${hostSystemName}\",\n        \"rhs\": \"Darwin\"\n      },\n      \"generator\": \"Xcode\",\n      \"binaryDir\": \"${sourceDir}/build_macos\",\n      \"cacheVariables\": {\n        \"CMAKE_OSX_DEPLOYMENT_TARGET\": {\"type\": \"STRING\", \"value\": \"12.0\"},\n        \"OBS_CODESIGN_IDENTITY\": {\"type\": \"STRING\", \"value\": \"$penv{CODESIGN_IDENT}\"},\n        \"OBS_CODESIGN_TEAM\": {\"type\": \"STRING\", \"value\": \"$penv{CODESIGN_TEAM}\"},\n        \"OBS_PROVISIONING_PROFILE\": {\"type\": \"STRING\", \"value\": \"$penv{PROVISIONING_PROFILE}\"},\n        \"VIRTUALCAM_DEVICE_UUID\": {\"type\": \"STRING\", \"value\": \"7626645E-4425-469E-9D8B-97E0FA59AC75\"},\n        \"VIRTUALCAM_SINK_UUID\": {\"type\": \"STRING\", \"value\": \"A3F16177-7044-4DD8-B900-72E2419F7A9A\"},\n        \"VIRTUALCAM_SOURCE_UUID\": {\"type\": \"STRING\", \"value\": \"A8D7B8AA-65AD-4D21-9C42-66480DBFA8E1\"},\n        \"SPARKLE_APPCAST_URL\": {\"type\": \"STRING\", \"value\": \"https://obsproject.com/osx_update/updates_$(ARCHS)_v2.xml\"},\n        \"SPARKLE_PUBLIC_KEY\": {\"type\": \"STRING\", \"value\": \"HQ5/Ba9VHOuEWaM0jtVjZzgHKFJX9YTl+HNVpgNF0iM=\"},\n        \"ENABLE_BROWSER\": true\n      }\n    },\n    {\n      \"name\": \"macos-ci\",\n      \"displayName\": \"macOS (CI)\",\n      \"description\": \"CI macOS build (single architecture only)\",\n      \"inherits\": [\"macos\"],\n      \"warnings\": {\"dev\": true, \"deprecated\": true},\n      \"cacheVariables\": {\n        \"CMAKE_COMPILE_WARNING_AS_ERROR\": true,\n        \"CMAKE_XCODE_ATTRIBUTE_COMPILATION_CACHE_ENABLE_CACHING\": \"YES\",\n        \"CMAKE_XCODE_ATTRIBUTE_COMPILATION_CACHE_CAS_PATH\": \"$penv{XCODE_CAS_PATH}\"\n      }\n    },\n    {\n      \"name\": \"ubuntu\",\n      \"displayName\": \"Ubuntu\",\n      \"description\": \"obs-studio for Ubuntu\",\n      \"inherits\": [\"environmentVars\"],\n      \"condition\": {\n        \"type\": \"equals\",\n        \"lhs\": \"${hostSystemName}\",\n        \"rhs\": \"Linux\"\n      },\n      \"binaryDir\": \"${sourceDir}/build_ubuntu\",\n      \"generator\": \"Ninja\",\n      \"warnings\": {\"dev\": true, \"deprecated\": true},\n      \"cacheVariables\": {\n        \"CMAKE_BUILD_TYPE\": \"Debug\",\n        \"CMAKE_INSTALL_LIBDIR\": \"lib/CMAKE_SYSTEM_PROCESSOR-linux-gnu\",\n        \"ENABLE_AJA\": false,\n        \"ENABLE_VLC\": true,\n        \"ENABLE_WAYLAND\": true,\n        \"ENABLE_WEBRTC\": false\n      }\n    },\n    {\n      \"name\": \"ubuntu-ci\",\n      \"inherits\": [\"ubuntu\"],\n      \"cacheVariables\": {\n        \"CMAKE_BUILD_TYPE\": \"RelWithDebInfo\",\n        \"CMAKE_COMPILE_WARNING_AS_ERROR\": true,\n        \"CMAKE_COLOR_DIAGNOSTICS\": true,\n        \"ENABLE_CCACHE\": true\n      }\n    },\n    {\n      \"name\": \"windows-x64\",\n      \"displayName\": \"Windows x64\",\n      \"description\": \"Default Windows build (x64)\",\n      \"inherits\": [\"environmentVars\"],\n      \"condition\": {\n        \"type\": \"equals\",\n        \"lhs\": \"${hostSystemName}\",\n        \"rhs\": \"Windows\"\n      },\n      \"architecture\": \"x64,version=10.0.22621.0\",\n      \"binaryDir\": \"${sourceDir}/build_x64\",\n      \"generator\": \"Visual Studio 17 2022\",\n      \"cacheVariables\": {\n        \"GPU_PRIORITY_VAL\": {\"type\": \"STRING\", \"value\": \"$penv{GPU_PRIORITY_VAL}\"},\n        \"VIRTUALCAM_GUID\": {\"type\": \"STRING\", \"value\": \"A3FCE0F5-3493-419F-958A-ABA1250EC20B\"},\n        \"ENABLE_BROWSER\": true\n      }\n    },\n    {\n      \"name\": \"windows-ci-x64\",\n      \"displayName\": \"Windows x64 (CI)\",\n      \"description\": \"CI Windows build (x64)\",\n      \"inherits\": [\"windows-x64\"],\n      \"warnings\": {\"dev\": true, \"deprecated\": true},\n      \"cacheVariables\": {\n        \"CMAKE_COMPILE_WARNING_AS_ERROR\": true\n      }\n    },\n    {\n      \"name\": \"windows-arm64\",\n      \"displayName\": \"Windows ARM64\",\n      \"description\": \"Default Windows build (ARM64)\",\n      \"inherits\": [\"environmentVars\"],\n      \"condition\": {\n        \"type\": \"equals\",\n        \"lhs\": \"${hostSystemName}\",\n        \"rhs\": \"Windows\"\n      },\n      \"architecture\": \"ARM64,version=10.0.22621.0\",\n      \"binaryDir\": \"${sourceDir}/build_arm64\",\n      \"generator\": \"Visual Studio 17 2022\",\n      \"cacheVariables\": {\n        \"GPU_PRIORITY_VAL\": {\"type\": \"STRING\", \"value\": \"$penv{GPU_PRIORITY_VAL}\"},\n        \"VIRTUALCAM_GUID\": {\"type\": \"STRING\", \"value\": \"A3FCE0F5-3493-419F-958A-ABA1250EC20B\"},\n        \"ENABLE_AJA\": false,\n        \"ENABLE_BROWSER\": true,\n        \"ENABLE_SCRIPTING\": false,\n        \"ENABLE_VST\": false\n      }\n    },\n    {\n      \"name\": \"windows-ci-arm64\",\n      \"displayName\": \"Windows ARM64 (CI)\",\n      \"description\": \"CI Windows build (ARM64)\",\n      \"inherits\": [\"windows-arm64\"],\n      \"warnings\": {\"dev\": true, \"deprecated\": true},\n      \"cacheVariables\": {\n        \"CMAKE_COMPILE_WARNING_AS_ERROR\": true\n      }\n    }\n  ],\n  \"buildPresets\": [\n    {\n      \"name\": \"windows-x64\",\n      \"configurePreset\": \"windows-x64\",\n      \"displayName\": \"Windows 64-bit\",\n      \"description\": \"Windows build for 64-bit (aka x64)\",\n      \"configuration\": \"RelWithDebInfo\"\n    },\n    {\n      \"name\": \"windows-arm64\",\n      \"configurePreset\": \"windows-arm64\",\n      \"displayName\": \"Windows on ARM 64-bit\",\n      \"description\": \"Windows build for ARM 64-bit (aka ARM64)\",\n      \"configuration\": \"RelWithDebInfo\"\n    }\n  ]\n}\n"
  },
  {
    "path": "COC.rst",
    "content": "Code of Conduct\n===============\n\nPreamble\n--------\n\nOBS is made up of many people from all over the world, with different\nbackgrounds and experiences that they bring to the project. In order to\ncontinue letting the community grow, feel safe, and to encourage good\ncommunication, we are adopting the following Code of Conduct. This\ndocument—or any subsequent version—will be enforced from July 31st, 2021\nonwards.\n\nScope\n-----\n\nThis Code of Conduct applies to anyone who contributes—or wishes to\ncontribute—to the project directly, is taking part in community\ndiscussion in official channels, or are in a position where they’ll be\nseen as a representative of the project, regardless of whether they\nintend to speak for the project or not.\n\nA person is considered to be representing the project if they’re\nspeaking as a code contributor, support volunteer, member involved with\nthe project, or if they have some kind of visible role in an official\nchannel for OBS. This does not include roles for Sponsors or Patrons.\n\nOfficial channels for OBS include the Discord server, the forum, any\nGitHub repositories within the OBS Project organization, any social\nmedia accounts for OBS Project, any public events where OBS Project has\na presence, any presentations given on behalf of OBS Project, or any\nother situation that can be reasonably interpreted as being hosted,\nattended or communicated by the OBS Project.\n\nThis Code of Conduct is subject to change, and any changes will have a 2\nweek notice period before taking immediate effect. Changes will not be\nenforced retroactively unless it directly affects the security, safety\nor well-being of another individual, or other exceptional circumstances.\n\nGuidelines\n----------\n\nGeneral\n~~~~~~~\n\n-  We are committed to providing a friendly, safe and welcoming\n   environment for all, regardless of level of experience, gender\n   identity and expression, sexual orientation, disability, personal\n   appearance, body size, race, ethnicity, age, religion, nationality,\n   or other similar characteristic.\n-  Please avoid using overtly sexual aliases or other nicknames that\n   might detract from a friendly, safe and welcoming environment for\n   all.\n-  Please be kind and courteous. There’s no need to be mean or rude.\n-  Respect that people will have differences of opinion.\n-  We will exclude you from interaction if you insult, demean or harass\n   anyone. That is not welcome behavior. The definition of harassment is\n   provided in the section on Unacceptable Behavior. In particular, we\n   don’t tolerate behavior that excludes people in socially marginalized\n   groups. This includes private harassment.\n-  Likewise any spamming, trolling, flaming, baiting or other\n   attention-stealing behavior is not welcome.\n-  Being a recognized Contributor, Support Volunteer, Sponsor or Patron\n   does not give you special right to avoid following these guidelines\n   when taking part in community discussions.\n\nCode Contributors\n~~~~~~~~~~~~~~~~~\n\n-  Respect that every design or implementation choice carries a\n   trade-off and numerous costs. There is seldom a right answer.\n-  Please keep unstructured critique to a minimum. If you have solid\n   ideas you want to experiment with, make a fork and see how it works.\n-  Do not post negative or inflammatory comments on code submissions. If\n   you believe that a code submission has problems, provide constructive\n   or actionable feedback, or a reason for why a submission is being\n   rejected. Where it is possible, provide explanations that someone can\n   use to fix things.\n\nSupport Volunteers\n~~~~~~~~~~~~~~~~~~\n\n-  There are many users of differing skill levels or abilities. This is\n   never a reason to refuse support for someone, as long as it’s within\n   the scope of OBS support, and the user is willing to cooperate.\n-  Just because something seems obvious to you, doesn’t mean it’s\n   actually obvious. If someone is having difficulty understanding how\n   to use OBS, it should never be treated as their fault.\n-  Bad advice is often worse than no advice. If you don’t know how to\n   address someone’s issue, then get ahold of someone who’ll be able to\n   provide them help. We can only expect so much patience from a user\n   asking for support, and helping without understanding the problem\n   only results in using up that patience.\n\nUnacceptable Behavior\n---------------------\n\nThe following behaviors are considered harassment and are unacceptable\nwithin our community:\n\n-  Violence, threats of violence or violent language directed against\n   another person.\n-  Sexist, racist, homophobic, transphobic, ableist or otherwise\n   discriminatory jokes and language.\n-  Posting or displaying sexually explicit or violent material.\n-  Posting or threatening to post other people’s personally identifying\n   information (\"doxing\").\n-  Personal insults.\n-  Unwelcome sexual attention. This includes, sexualized comments or\n   jokes, and unwelcome sexual advances.\n-  Deliberate intimidation, stalking or following.\n-  Advocating for, or encouraging, any of the above behavior.\n\nEnforcement\n-----------\n\nInstances of unacceptable behavior may be reported by contacting an\nenforcement team member at one of the following addresses:\ncoc@obsproject.com. All complaints will be reviewed and investigated,\nand will result in a response that is deemed necessary and appropriate\nto the circumstances. The project team is obligated to maintain\nconfidentiality with regard to the reporter of an incident. Further\ndetails of specific enforcement policies may be posted separately.\n\nIn other cases where behavior is against the general guidelines set out\nin this Code of Conduct, this may be brought up with a moderator through\neither a Discord DM, or through an email to an enforcement member. In\nthese cases, it is always assumed in the first instance that a person\nmeans well, and action will only be taken in the case of repeated\nfailure to follow the guidelines.\n\nFeedback\n--------\n\nSuggestions for changes can be made by submitting a pull request on the\n`obsproject/obs-studio <https://github.com/obsproject/obs-studio>`__\nrepo, or if you wish to privately discuss your concerns, you can instead\nemail coc@obsproject.com.\n\nAttribution\n-----------\n\nThis Code of Conduct is adapted in part from the Contributor Covenant,\nversion 1.4, in part from the Citizen Code of Conduct (used under\nCC-BY-SA 3.0), and in part from the Rust Code of Conduct.\n\nThis Code of Conduct is released and licensed under a Creative Commons\nAttribution-ShareAlike 3.0 License.\n\nVersion History\n---------------\n\nV1.0 - First Iteration - July 31st, 2021\n"
  },
  {
    "path": "COMMITMENT",
    "content": "GPL Cooperation Commitment\nVersion 1.0\n\nBefore filing or continuing to prosecute any legal proceeding or claim\n(other than a Defensive Action) arising from termination of a Covered\nLicense, we commit to extend to the person or entity ('you') accused\nof violating the Covered License the following provisions regarding\ncure and reinstatement, taken from GPL version 3. As used here, the\nterm 'this License' refers to the specific Covered License being\nenforced.\n\n    However, if you cease all violation of this License, then your\n    license from a particular copyright holder is reinstated (a)\n    provisionally, unless and until the copyright holder explicitly\n    and finally terminates your license, and (b) permanently, if the\n    copyright holder fails to notify you of the violation by some\n    reasonable means prior to 60 days after the cessation.\n\n    Moreover, your license from a particular copyright holder is\n    reinstated permanently if the copyright holder notifies you of the\n    violation by some reasonable means, this is the first time you\n    have received notice of violation of this License (for any work)\n    from that copyright holder, and you cure the violation prior to 30\n    days after your receipt of the notice.\n\nWe intend this Commitment to be irrevocable, and binding and\nenforceable against us and assignees of or successors to our\ncopyrights.\n\nDefinitions\n\n'Covered License' means the GNU General Public License, version 2\n(GPLv2), the GNU Lesser General Public License, version 2.1\n(LGPLv2.1), or the GNU Library General Public License, version 2\n(LGPLv2), all as published by the Free Software Foundation.\n\n'Defensive Action' means a legal proceeding or claim that We bring\nagainst you in response to a prior proceeding or claim initiated by\nyou or your affiliate.\n\n'We' means each contributor to this repository as of the date of\ninclusion of this file, including subsidiaries of a corporate\ncontributor.\n\nThis work is available under a Creative Commons Attribution-ShareAlike\n4.0 International license (https://creativecommons.org/licenses/by-sa/4.0/).\n"
  },
  {
    "path": "CONTRIBUTING.rst",
    "content": "Contributing\n============\n\nQuick Links for Contributing\n----------------------------\n\n- Compiling and building OBS Studio:\n  https://github.com/obsproject/obs-studio/wiki/Install-Instructions\n\n- Our bug tracker:\n  https://github.com/obsproject/obs-studio/issues\n\n- Discord Server: https://obsproject.com/discord\n\n- Development chat: #development on the Discord server (see above)\n \n- Development forum:\n  https://obsproject.com/forum/list/general-development.21/\n\n- Developer/API Documentation:\n  https://obsproject.com/docs\n\n- To contribute language translations, do not make pull requests.\n  Instead, use crowdin.  Read here for more information:\n  https://github.com/obsproject/obs-studio/wiki/How-To-Contribute-Translations-For-OBS\n\n- To add a new service to OBS Studio please see the service submission guidelines:\n  https://github.com/obsproject/obs-studio/wiki/Service-Submission-Guidelines\n\nGeneral Guidelines\n------------------\n\n- The OBS Project uses English as a common language. Please ensure that any\n  submissions have at least machine-translated English descriptions and titles.\n\n- Templates for Pull Requests and Issues must be properly filled out. Failure\n  to do so may result in your PR or Issue being closed. The templates request\n  the bare minimum amount of information required for us to process them.\n\n- Contributors to the OBS Project are expected to abide by the OBS Project Code of Conduct: https://github.com/obsproject/obs-studio/blob/master/COC.rst\n\nCoding Guidelines\n-----------------\n\n- OBS Studio uses kernel normal form (linux variant), for more\n  information, please read here:\n  https://github.com/torvalds/linux/blob/master/Documentation/process/coding-style.rst\n\n- Avoid trailing spaces.  To view trailing spaces before making a\n  commit, use \"git diff\" on your changes.  If colors are enabled for\n  git in the command prompt, it will show you any whitespace issues\n  marked with red.\n\n- Tabs for indentation, spaces for alignment.  Tabs are treated as 8\n  columns wide.\n\n- 120 columns max\n\n- Comments and names of variables/functions/etc. must be in English\n\n- Formatting scripts (macOS/Linux only) are available `here <./build-aux>`__\n\nCommit Guidelines\n-----------------\n\n- OBS Studio uses the 50/72 standard for commits.  50 characters max\n  for the title (excluding module prefix), an empty line, and then a\n  full description of the commit, wrapped to 72 columns max.  See this\n  link for more information: http://chris.beams.io/posts/git-commit/\n\n- Make sure commit titles are always in present tense, and are not\n  followed by punctuation.\n\n- Prefix each commit's titles with the module name, followed by a colon\n  and a space (unless modifying a file in the base directory).  After\n  that, the first word should be capitalized.\n\n  So for example, if you are modifying the obs-ffmpeg plugin::\n\n    obs-ffmpeg: Fix bug with audio output\n\n  Or for libobs::\n\n    libobs: Fix source not displaying\n\n  Note: When modifying cmake modules, just prefix with \"cmake\".\n\n- If you still need examples, please view the commit history.\n\n- Commit titles and descriptions must be in English\n\nAI/Machine Learning Policy\n--------------------------\n\nAI/machine learning systems such as those based on the GPT family (Copilot, \nChatGPT, etc.) are prone to generating plausible-sounding, but wrong code that\nmakes incorrect assumptions about OBS internals or APIs it interfaces with.\n\nThis means code generated by such systems will require human review and is \nlikely to require human intervention. If the submitter is unable to undertake\nthat work themselves due to a lack of understanding of the OBS codebase and/or\nprogramming, the submission has a high likelihood of being invalid.\nSuch invalid submissions end up taking maintainers' time to review and respond\naway from legitimate submissions.\n\nAdditionally, such systems have been demonstrated to reproduce code contained\nin the training data, which may have been originally published under a license\nthat would prohibit its inclusion in OBS.\n\nBecause of the above concerns, we have opted to take the following policy\ntowards submissions with regard to the use of these AI tools:\n\n- Submissions created largely or entirely by AI systems are not allowed.\n\n- The use of GitHub Copilot and other assistive AI technologies is heavily\n  discouraged.\n\n- Low-effort or incorrect submissions that are determined to have been\n  generated by, or created with aid of such systems may lead to a ban from\n  contributing to the repository or project as a whole.\n"
  },
  {
    "path": "COPYING",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "INSTALL",
    "content": "For install instructions please visit https://github.com/obsproject/obs-studio/wiki/Install-Instructions\n"
  },
  {
    "path": "README.rst",
    "content": "OBS Studio <https://obsproject.com>\n===================================\n\n.. image:: https://github.com/obsproject/obs-studio/actions/workflows/push.yaml/badge.svg?branch=master\n   :alt: OBS Studio Build Status - GitHub Actions\n   :target: https://github.com/obsproject/obs-studio/actions/workflows/push.yaml?query=branch%3Amaster\n\n.. image:: https://badges.crowdin.net/obs-studio/localized.svg\n   :alt: OBS Studio Translation Project Progress\n   :target: https://crowdin.com/project/obs-studio\n\n.. image:: https://img.shields.io/discord/348973006581923840.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2\n   :alt: OBS Studio Discord Server\n   :target: https://obsproject.com/discord\n\nWhat is OBS Studio?\n-------------------\n\nOBS Studio is software designed for capturing, compositing, encoding,\nrecording, and streaming video content, efficiently.\n\nIt's distributed under the GNU General Public License v2 (or any later\nversion) - see the accompanying COPYING file for more details.\n\nQuick Links\n-----------\n\n- Website: https://obsproject.com\n\n- Help/Documentation/Guides: https://github.com/obsproject/obs-studio/wiki\n\n- Forums: https://obsproject.com/forum/\n\n- Build Instructions: https://github.com/obsproject/obs-studio/wiki/Install-Instructions\n\n- Developer/API Documentation: https://obsproject.com/docs\n\n- Donating/backing/sponsoring: https://obsproject.com/contribute\n\n- Bug Tracker: https://github.com/obsproject/obs-studio/issues\n\nContributing\n------------\n\n- If you would like to help fund or sponsor the project, you can do so\n  via `Patreon <https://www.patreon.com/obsproject>`_, `OpenCollective\n  <https://opencollective.com/obsproject>`_, or `PayPal\n  <https://www.paypal.me/obsproject>`_.  See our `contribute page\n  <https://obsproject.com/contribute>`_ for more information.\n\n- If you wish to contribute code to the project, please make sure to\n  read the coding and commit guidelines:\n  https://github.com/obsproject/obs-studio/blob/master/CONTRIBUTING.rst\n\n- Developer/API documentation can be found here:\n  https://obsproject.com/docs\n\n- If you wish to contribute translations, do not submit pull requests.\n  Instead, please use Crowdin.  For more information read this page:\n  https://obsproject.com/wiki/How-To-Contribute-Translations-For-OBS\n\n- Contributors to OBS Studio and related repositories are expected to\n  follow our Code of Conduct, which can be read here:\n  https://github.com/obsproject/obs-studio/blob/master/COC.rst\n\n- Other ways to contribute are by helping people out with support on\n  our forums or in our community chat.  Please limit support to topics\n  you fully understand -- bad advice is worse than no advice.  When it\n  comes to something that you don't fully know or understand, please\n  defer to the official help or official channels.\n\n\nSAST Tools\n----------\n\n`PVS-Studio <https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source>`_ - static analyzer for C, C++, C#, and Java code.\n"
  },
  {
    "path": "build-aux/.functions/log_debug",
    "content": "if (( ! ${+_loglevel} )) typeset -g _loglevel=1\n\nif (( _loglevel > 2 )) print -PR -e -- \"${CI:+::debug::}%F{220}DEBUG: ${@}%f\"\n"
  },
  {
    "path": "build-aux/.functions/log_error",
    "content": "local icon='  ✖︎ '\n\nprint -u2 -PR \"${CI:+::error::}%F{1} ${icon} %f ${@}\"\n"
  },
  {
    "path": "build-aux/.functions/log_group",
    "content": "autoload -Uz log_info\n\nif (( ! ${+_log_group} )) typeset -g _log_group=0\n\nif (( ${+CI} )) {\n  if (( _log_group )) {\n    print \"::endgroup::\"\n    typeset -g _log_group=0\n  }\n  if (( # )) {\n    print \"::group::${@}\"\n    typeset -g _log_group=1\n  }\n} else {\n  if (( # )) log_info ${@}\n}\n"
  },
  {
    "path": "build-aux/.functions/log_info",
    "content": "if (( ! ${+_loglevel} )) typeset -g _loglevel=1\n\nif (( _loglevel > 0 )) {\n  local icon=' =>'\n\n  print -PR \"%F{4}  ${(r:5:)icon}%f %B${@}%b\"\n}\n"
  },
  {
    "path": "build-aux/.functions/log_output",
    "content": "if (( ! ${+_loglevel} )) typeset -g _loglevel=1\n\nif (( _loglevel > 0 )) {\n  local icon=''\n\n  print -PR \"  ${(r:5:)icon} ${@}\"\n}\n"
  },
  {
    "path": "build-aux/.functions/log_status",
    "content": "if (( ! ${+_loglevel} )) typeset -g _loglevel=1\n\nif (( _loglevel > 0 )) {\n  local icon='  >'\n\n  print -PR \"%F{2}  ${(r:5:)icon}%f ${@}\"\n}\n"
  },
  {
    "path": "build-aux/.functions/log_warning",
    "content": "if (( ! ${+_loglevel} )) typeset -g _loglevel=1\n\nif (( _loglevel > 0 )) {\n  local icon=' =>'\n\n  print -PR \"${CI:+::warning::}%F{3}  ${(r:5:)icon} ${@}%f\"\n}\n"
  },
  {
    "path": "build-aux/.functions/set_loglevel",
    "content": "autoload -Uz log_debug log_error\n\nlocal -r _usage=\"Usage: %B${0}%b <loglevel>\n\nSet log level, following levels are supported: 0 (quiet), 1 (normal), 2 (verbose), 3 (debug)\"\n\nif (( ! # )); then\n  log_error 'Called without arguments.'\n  log_output ${_usage}\n  return 2\nelif (( ${1} >= 4 )); then\n  log_error 'Called with loglevel > 3.'\n  log_output ${_usage}\nfi\n\ntypeset -g -i -r _loglevel=${1}\nlog_debug \"Log level set to '${1}'\"\n"
  },
  {
    "path": "build-aux/.run-format.zsh",
    "content": "#!/usr/bin/env zsh\n\nbuiltin emulate -L zsh\nsetopt EXTENDED_GLOB\nsetopt PUSHD_SILENT\nsetopt ERR_EXIT\nsetopt ERR_RETURN\nsetopt NO_UNSET\nsetopt PIPE_FAIL\nsetopt NO_AUTO_PUSHD\nsetopt NO_PUSHD_IGNORE_DUPS\nsetopt FUNCTION_ARGZERO\n\n## Enable for script debugging\n# setopt WARN_CREATE_GLOBAL\n# setopt WARN_NESTED_VAR\n# setopt XTRACE\n\nautoload -Uz is-at-least && if ! is-at-least 5.2; then\n  print -u2 -PR \"%F{1}${funcstack[1]##*/}:%f Running on Zsh version %B${ZSH_VERSION}%b, but Zsh %B5.2%b is the minimum supported version. Upgrade zsh to fix this issue.\"\n  exit 1\nfi\n\ninvoke_formatter() {\n  if (( # < 1 )) {\n    log_error \"Usage invoke_formatter [formatter_name]\"\n    exit 2\n  }\n\n  local formatter=\"${1}\"\n  shift\n  local -a source_files=(${@})\n\n  case ${formatter} {\n    clang)\n      if (( ${+commands[clang-format-19]} )) {\n        local formatter=clang-format-19\n      } elif (( ${+commands[clang-format]} )) {\n        local formatter=clang-format\n      } else {\n        log_error \"No viable clang-format version found (required 19.1.1)\"\n        exit 2\n      }\n\n      local -a formatter_version=($(${formatter} --version))\n\n      if ! is-at-least 19.1.1 ${formatter_version[-1]}; then\n        log_error \"clang-format is not version 19.1.1 or above (found ${formatter_version[-1]}.\"\n        exit 2\n      fi\n\n      if ! is-at-least ${formatter_version[-1]} 19.1.1; then\n        log_error \"clang-format is more recent than version 19.1.1 (found ${formatter_version[-1]}).\"\n        exit 2\n      fi\n\n      if (( ! #source_files )) source_files=((libobs|libobs-*|frontend|plugins|deps|shared|test)/**/*.(c|cpp|h|hpp|m|mm)(.N))\n\n      source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|mac-syphon/syphon-framework|libdshowcapture)/*})\n\n      local -a format_args=(-style=file -fallback-style=none)\n      if (( _loglevel > 2 )) format_args+=(--verbose)\n\n      check_files() {\n        local -i num_failures=0\n        local -a source_files=($@)\n        local file\n        local -a format_args=(-style=file -fallback-style=none)\n        if (( _loglevel > 2 )) format_args+=(--verbose)\n\n        local -a command=(${formatter} ${format_args})\n\n        for file (${source_files}) {\n          if ! ${command} \"${file}\" | diff -q \"${file}\" - &> /dev/null; then\n            log_error \"${file} requires formatting changes.\"\n            if (( fail_on_error == 2 )) return 2;\n            num_failures=$(( num_failures + 1 ))\n          fi\n        }\n        if (( num_failures && fail_on_error == 1 )) return 2\n      }\n\n      format_files() {\n        local -a source_files=($@)\n\n        if (( ${#source_files} )) {\n          local -a format_args=(-style=file -fallback-style=none -i)\n          if (( _loglevel > 2 )) format_args+=(--verbose)\n\n          \"${formatter}\" ${format_args} ${source_files}\n        }\n      }\n      ;;\n    gersemi)\n\n      if (( ${+commands[gersemi-0.25]} )) {\n        local formatter=gersemi-0.25\n      } elif (( ${+commands[gersemi]} )) {\n        local formatter=gersemi\n      } else {\n        log_error \"No viable gersemi version found (required 0.25.0)\"\n        exit 2\n      }\n\n      local gersemi_version=($(${formatter} --version))\n\n      if ! is-at-least 0.25.0 ${gersemi_version[2]}; then\n        log_error \"gersemi is not version 0.25.0 or above (found ${gersemi_version[2]}.\"\n        exit 2\n      fi\n\n      if (( ! #source_files )) source_files=(CMakeLists.txt (libobs|libobs-*|frontend|plugins|deps|shared|cmake|test)/**/(CMakeLists.txt|*.cmake)(.N))\n\n      source_files=(${source_files:#*/(jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|libdshowcapture)/*})\n      source_files=(${source_files:#(cmake/Modules/*|*/legacy.cmake)})\n\n      check_files() {\n        local -i num_failures=0\n        local -a source_files=($@)\n        local file\n        local -a command=(${formatter} -c --no-cache ${source_files})\n        local -i in_error=0\n\n        if (( ${#source_files} )) {\n          while read -r line; do\n            local -a line_tokens=(${(z)line})\n            if (( #line_tokens )) {\n              file=${line_tokens[1]}\n\n              if [[ -r ${file} ]] {\n                in_error=0\n                file=${file//*${project_root}\\//}\n\n                log_error \"${file} requires formatting changes.\"\n              } else {\n                if (( in_error )) {\n                  log_output \"${line}\"\n                } else {\n                  log_error \"${line}\"\n                }\n                in_error=1\n              }\n            } else {\n              log_output \"${line}\"\n            }\n\n            if (( fail_on_error == 2 )) return 2\n            num_failures=$(( num_failures + 1 ))\n          done < <(${command} 2>&1)\n\n          if (( num_failures && fail_on_error == 1 )) return 2\n        }\n      }\n\n      format_files() {\n        local -a source_files=($@)\n\n        if (( ${#source_files} )) {\n          \"${formatter}\" -i ${source_files}\n        }\n      }\n      ;;\n    swift)\n      local formatter=swift-format\n      if (( ${+commands[swift-format]} )) {\n        local swift_format_version=$(swift-format --version)\n\n        if ! is-at-least 508.0.0 ${swift_format_version}; then\n          log_error \"swift-format is not version 508.0.0 or above (found ${swift_format_version}).\"\n          exit 2\n        fi\n      } else {\n        log_error \"No viable swift-format version found (required 508.0.0)\"\n        exit 2\n      }\n\n      if (( ! #source_files )) source_files=((libobs|libobs-*|frontend|plugins)/**/*.swift(.N))\n\n      check_files() {\n        local -i num_failures=0\n        local -a source_files=($@)\n        local file\n        local -a format_args=()\n\n        local -a command=(${formatter} ${format_args})\n\n        for file (${source_files}) {\n          if ! \"${command}\" \"${file}\" | diff -q \"${file}\" - &> /dev/null; then\n            log_error \"${file} requires formatting changes.\"\n            if (( fail_on_error == 2 )) return 2;\n            num_failures=$(( num_failures + 1 ))\n          fi\n        }\n        if (( num_failures && fail_on_error == 1 )) return 2\n      }\n\n      format_files() {\n        local -a source_files=($@)\n\n        if (( ${#source_files} )) {\n          local -a format_args=(-i)\n\n          \"${formatter}\" ${format_args} ${source_files}\n        }\n      }\n      ;;\n    *) log_error \"Invalid formatter specified: ${1}. Valid options are clang-format, gersemi, and swift-format.\"; exit 2 ;;\n  }\n\n  local file\n  local -i num_failures=0\n  if (( check_only )) {\n    if (( ${+functions[check_files]} )) {\n      check_files ${source_files}\n    } else {\n      log_error \"No format check function defined for formatter '${formatter}'\"\n      exit 2\n    }\n  } else {\n    if (( ${+functions[format_files]} )) {\n      format_files ${source_files}\n    } else {\n      log_error \"No format function defined for formatter '${formatter}'\"\n      exit 2\n    }\n  }\n}\n\nrun_format() {\n  if (( ! ${+SCRIPT_HOME} )) typeset -g SCRIPT_HOME=${ZSH_ARGZERO:A:h}\n  if (( ! ${+FORMATTER_NAME} )) typeset -g FORMATTER_NAME=${${(s:-:)ZSH_ARGZERO:t:r}[2]}\n  local project_root=${SCRIPT_HOME:A:h}\n\n  typeset -g host_os=${${(L)$(uname -s)}//darwin/macos}\n  local -i fail_on_error=0\n  local -i check_only=0\n  local -i verbosity=1\n  local -r _version='1.0.0'\n\n  fpath=(\"${SCRIPT_HOME}/.functions\" ${fpath})\n  autoload -Uz set_loglevel log_info log_error log_output log_status log_warning\n\n  local -r _usage=\"\nUsage: %B${functrace[1]%:*}%b <option>\n\n%BOptions%b:\n\n%F{yellow} Formatting options%f\n -----------------------------------------------------------------------------\n  %B-c | --check%b                      Check only, no actual formatting takes place\n\n%F{yellow} Output options%f\n -----------------------------------------------------------------------------\n  %B-v | --verbose%b                    Verbose (more detailed output)\n  %B--fail-[never|error]                Fail script never/on formatting change - default: %B%F{green}never%f%b\n  %B--debug%b                           Debug (very detailed and added output)\n\n%F{yellow} General options%f\n -----------------------------------------------------------------------------\n  %B-h | --help%b                       Print this usage help\n  %B-V | --version%b                    Print script version information\"\n\n  local -a args\n  while (( # )) {\n    case ${1} {\n      -c|--check) check_only=1; shift ;;\n      -v|--verbose) (( verbosity += 1 )); shift ;;\n      -h|--help) log_output ${_usage}; exit 0 ;;\n      -V|--version) print -Pr \"${_version}\"; exit 0 ;;\n      --debug) verbosity=3; shift ;;\n      --fail-never)\n        fail_on_error=0\n        shift\n        ;;\n      --fail-error)\n        fail_on_error=1\n        shift\n        ;;\n      --fail-fast)\n        fail_on_error=2\n        shift\n        ;;\n      *)\n        args+=($@)\n        break\n        ;;\n    }\n  }\n\n  set -- ${(@)args}\n  set_loglevel ${verbosity}\n\n  invoke_formatter ${FORMATTER_NAME} ${args}\n}\n\nrun_format ${@}\n"
  },
  {
    "path": "build-aux/README.md",
    "content": "# `build-aux` folder\n\nThis folder contains:\n- Various formatting scripts:\n  - `run-clang-format` which formats C/C++/ObjC/ObjC++ files\n  - `run-gersemi` which formats CMake files\n  - `run-swift-format` which formats Swift files\n  - `format-manifest.py` which formats Flatpak manifest JSON files\n- The Flatpak manifest used to build OBS Studio\n- Files used for Steam packaging\n\n\n## Formatting scripts\n\n### `run-clang-format`\n\nThis script allows to check the formatting and/or format of C/C++/ObjC/ObjC++ files and requires ZSH and a specific version of `clang-format`.\n\nIf the script does not find the latter it will return the required version, we provide `clang-format` Homebrew formulas in our [homebrew-tools repo](https://github.com/obsproject/homebrew-tools/).\n\nExample of use:\n```sh\n./build-aux/run-clang-format\n```\n\n### `run-gersemi`\n\nThis script allows to check the formatting and/or format of the CMake files and requires ZSH and `gersemi` Python package.\n\nExample of use:\n```sh\n./build-aux/run-gersemi\n```\n\n### `run-swift-format`\n\nThis script allows to check the formatting and/or format of the Swift files and requires ZSH and `swift-format`.\n\nExample of use:\n```sh\n./build-aux/run-swift-format\n```\n\n### `format-manifest.py`\n\nThis script allows to check the formatting and/or format of the Flatpak manifest and its modules.\n\nExample of use:\n```sh\npython3 ./build-aux/format-manifest.py com.obsproject.Studio.json\n```\n"
  },
  {
    "path": "build-aux/com.obsproject.Studio.json",
    "content": "{\n    \"id\": \"com.obsproject.Studio\",\n    \"runtime\": \"org.freedesktop.Platform\",\n    \"runtime-version\": \"25.08\",\n    \"sdk\": \"org.freedesktop.Sdk\",\n    \"command\": \"obs\",\n    \"finish-args\": [\n        \"--socket=wayland\",\n        \"--socket=fallback-x11\",\n        \"--socket=pulseaudio\",\n        \"--device=all\",\n        \"--share=network\",\n        \"--share=ipc\",\n        \"--filesystem=xdg-run/pipewire-0\",\n        \"--filesystem=host\",\n        \"--talk-name=org.kde.StatusNotifierWatcher\",\n        \"--talk-name=org.freedesktop.Flatpak\",\n        \"--talk-name=org.freedesktop.Notifications\",\n        \"--talk-name=org.a11y.Bus\",\n        \"--env=VST_PATH=/app/extensions/Plugins/vst\"\n    ],\n    \"add-extensions\": {\n        \"com.obsproject.Studio.Plugin\": {\n            \"directory\": \"plugins\",\n            \"subdirectories\": true,\n            \"add-ld-path\": \"lib\",\n            \"merge-dirs\": \"lib/obs-plugins;share/obs/obs-plugins\",\n            \"no-autodownload\": true,\n            \"autodelete\": true\n        },\n        \"org.freedesktop.LinuxAudio.Plugins\": {\n            \"directory\": \"extensions/Plugins\",\n            \"version\": \"25.08\",\n            \"add-ld-path\": \"lib\",\n            \"merge-dirs\": \"vst\",\n            \"subdirectories\": true,\n            \"no-autodownload\": true\n        }\n    },\n    \"modules\": [\n        {\n            \"name\": \"obs-deps-base\",\n            \"buildsystem\": \"simple\",\n            \"build-commands\": [\n                \"tar -xvf base.tar.xz -C /\"\n            ],\n            \"sources\": [\n                {\n                    \"type\": \"file\",\n                    \"dest-filename\": \"base.tar.xz\",\n                    \"url\": \"https://github.com/obsproject/obs-deps-buildstream/releases/download/2026-01-09/flatpak-base-x86_64-2026-01-09.tar.xz\",\n                    \"sha256\": \"664ff7c2e3cfa552a6b16e7b78428c1940a5a42f85911b5e4d57a053f0dcc9e1\"\n                }\n            ]\n        },\n        {\n            \"name\": \"obs-deps-devel\",\n            \"buildsystem\": \"simple\",\n            \"build-commands\": [\n                \"tar -xvf devel.tar.xz -C /\"\n            ],\n            \"sources\": [\n                {\n                    \"type\": \"file\",\n                    \"dest-filename\": \"devel.tar.xz\",\n                    \"url\": \"https://github.com/obsproject/obs-deps-buildstream/releases/download/2026-01-09/flatpak-devel-x86_64-2026-01-09.tar.xz\",\n                    \"sha256\": \"214a18feff6d7e1ed5b42820ac806eafec50029e41f03c197f76e06eafb591dc\"\n                }\n            ]\n        },\n        {\n            \"name\": \"obs-deps-devtools\",\n            \"buildsystem\": \"simple\",\n            \"build-commands\": [\n                \"tar -xvf devtools.tar.xz -C /\"\n            ],\n            \"cleanup\": [\n                \"*\"\n            ],\n            \"sources\": [\n                {\n                    \"type\": \"file\",\n                    \"dest-filename\": \"devtools.tar.xz\",\n                    \"url\": \"https://github.com/obsproject/obs-deps-buildstream/releases/download/2026-01-09/flatpak-devtools-x86_64-2026-01-09.tar.xz\",\n                    \"sha256\": \"f3381b9661925a593ebe42dded1a87f86d54e49c7fde92567f2f19d37564bb61\"\n                }\n            ]\n        },\n        {\n            \"name\": \"qt\",\n            \"buildsystem\": \"simple\",\n            \"build-commands\": [\n                \"tar -xvf qt.tar.xz -C /\"\n            ],\n            \"sources\": [\n                {\n                    \"type\": \"file\",\n                    \"dest-filename\": \"qt.tar.xz\",\n                    \"url\": \"https://github.com/obsproject/obs-deps-buildstream/releases/download/2026-01-09/flatpak-qt-x86_64-2026-01-09.tar.xz\",\n                    \"sha256\": \"57c5c90cd5824d2a634ba92a0a2ad1b1a57347ee14a9eae41cf77c74970cee11\"\n                }\n            ]\n        },\n        {\n            \"name\": \"cef\",\n            \"build-options\": {\n                \"no-debuginfo\": true\n            },\n            \"buildsystem\": \"simple\",\n            \"build-commands\": [\n                \"mkdir -p /app/cef/libcef_dll_wrapper\",\n                \"cp -R ./include /app/cef\",\n                \"cp -R ./Release /app/cef\",\n                \"cp -R ./Resources /app/cef\",\n                \"cp -R ./build/libcef_dll_wrapper/libcef_dll_wrapper.a /app/cef/libcef_dll_wrapper\"\n            ],\n            \"cleanup\": [\n                \"*\"\n            ],\n            \"sources\": [\n                {\n                    \"type\": \"archive\",\n                    \"url\": \"https://cdn-fastly.obsproject.com/downloads/cef_binary_6533_linux_x86_64_v6.tar.xz\",\n                    \"sha256\": \"7963335519a19ccdc5233f7334c5ab023026e2f3e9a0cc417007c09d86608146\"\n                }\n            ]\n        },\n        {\n            \"name\": \"obs\",\n            \"buildsystem\": \"cmake-ninja\",\n            \"builddir\": true,\n            \"config-opts\": [\n                \"-DCMAKE_BUILD_TYPE=RelWithDebInfo\",\n                \"-DENABLE_WAYLAND=ON\",\n                \"-DENABLE_BROWSER=ON\",\n                \"-DCEF_ROOT_DIR=/app/cef\",\n                \"-DENABLE_ALSA=OFF\",\n                \"-DENABLE_PULSEAUDIO=ON\",\n                \"-DENABLE_JACK=ON\",\n                \"-DENABLE_VLC=OFF\",\n                \"-DENABLE_AJA=ON\",\n                \"-DENABLE_LIBFDK=ON\",\n                \"-DENABLE_QSV11=ON\",\n                \"-DENABLE_DECKLINK=OFF\"\n            ],\n            \"secret-opts\": [\n                \"-DRESTREAM_CLIENTID=$RESTREAM_CLIENTID\",\n                \"-DRESTREAM_HASH=$RESTREAM_HASH\",\n                \"-DTWITCH_CLIENTID=$TWITCH_CLIENTID\",\n                \"-DTWITCH_HASH=$TWITCH_HASH\",\n                \"-DYOUTUBE_CLIENTID=$YOUTUBE_CLIENTID\",\n                \"-DYOUTUBE_CLIENTID_HASH=$YOUTUBE_CLIENTID_HASH\",\n                \"-DYOUTUBE_SECRET=$YOUTUBE_SECRET\",\n                \"-DYOUTUBE_SECRET_HASH=$YOUTUBE_SECRET_HASH\"\n            ],\n            \"post-install\": [\n                \"install -d /app/plugins\",\n                \"install -d /app/extensions/Plugins\"\n            ],\n            \"sources\": [\n                {\n                    \"type\": \"dir\",\n                    \"path\": \"../\"\n                }\n            ]\n        }\n    ]\n}\n"
  },
  {
    "path": "build-aux/format-manifest.py",
    "content": "import argparse\nimport glob\nimport json\nimport logging\nimport os\nimport sys\nfrom typing import Any\n\n\ndef main() -> int:\n    parser = argparse.ArgumentParser(description=\"Format Flatpak manifest\")\n    parser.add_argument(\n        \"manifest_file\",\n        metavar=\"FILE\",\n        type=str,\n        help=\"Manifest file to adjust format for\",\n    )\n    parser.add_argument(\n        \"--check\",\n        action=\"store_true\",\n        help=\"Check for necessary changes only\",\n        default=False,\n        required=False,\n    )\n    parser.add_argument(\n        \"--loglevel\", type=str, help=\"Set log level\", default=\"WARNING\", required=False\n    )\n\n    arguments = parser.parse_args()\n\n    logging.basicConfig(level=arguments.loglevel, format=\"%(message)s\")\n    logger = logging.getLogger()\n\n    manifest_file = arguments.manifest_file\n\n    try:\n        with open(manifest_file, \"r+\") as manifest:\n            manifest_path = os.path.dirname(manifest_file)\n            manifest_string = manifest.read()\n            manifest_data = json.loads(manifest_string)\n\n            new_manifest_string = (\n                f\"{json.dumps(manifest_data, indent=4, ensure_ascii=False)}\\n\"\n            )\n\n            if arguments.check:\n                if new_manifest_string != manifest_string:\n                    logger.error(f\"❌ Manifest file is not correctly formatted\")\n                    return 2\n                else:\n                    logger.info(f\"✅ Module list passed order validation\")\n                    return 0\n\n            manifest.seek(0)\n            manifest.truncate()\n            manifest.write(new_manifest_string)\n\n            logger.info(f\"✅ Updated manifest file '{manifest_file}\")\n    except IOError:\n        logger.error(f\"❌ Unable to read manifest file '{manifest_file}'\")\n        return 2\n\n    return 0\n\n\nif __name__ == \"__main__\":\n    sys.exit(main())\n"
  },
  {
    "path": "build-aux/steam/obs_build.vdf",
    "content": "\"AppBuild\"\n{\n\t\"AppID\" \"1905180\"\n\t\"Desc\" \"github_@@DESC@@\"\n\n\t\"ContentRoot\" \"./\"\n\t\"BuildOutput\" \"build/\"\n\t\n\t\"SetLive\" \"@@BRANCH@@\"\n\n\t\"Depots\"\n\t{\n\t\t\"1905181\" // Windows\n\t\t{\n\t\t\t\"ContentRoot\" \"./steam-windows\"\n\t\t\t\"InstallScript\" \"scripts/installscript.vdf\"\n\t\t\t\"FileMapping\"\n\t\t\t{\n\t\t\t\t\"LocalPath\" \"*\"\n\t\t\t\t\"DepotPath\" \".\"\n\t\t\t\t\"recursive\" \"1\"\n\t\t\t}\n\t\t}\n\t\t\n\t\t\"1905182\" // Mac\n\t \t{\n\t\t\t\"ContentRoot\" \"./steam-macos\"\n\t\t\t\"FileMapping\"\n\t\t\t{\n\t\t\t\t\"LocalPath\" \"*\"\n\t\t\t\t\"DepotPath\" \".\"\n\t\t\t\t\"recursive\" \"1\"\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "build-aux/steam/obs_playtest_build.vdf",
    "content": "\"AppBuild\"\n{\n\t\"AppID\" \"1905640\"\n\t\"Desc\" \"github_@@DESC@@\"\n\n\t\"ContentRoot\" \"./\"\n\t\"BuildOutput\" \"build/\"\n\t\n\t\"SetLive\" \"@@BRANCH@@\"\n\n\t\"Depots\"\n\t{\n\t\t\"1905642\" // Windows\n\t\t{\n\t\t\t\"ContentRoot\" \"./steam-windows\"\n\t\t\t\"FileMapping\"\n\t\t\t{\n\t\t\t\t\"LocalPath\" \"*\"\n\t\t\t\t\"DepotPath\" \".\"\n\t\t\t\t\"recursive\" \"1\"\n\t\t\t}\n\t\t}\n\t\t\n\t\t\"1905641\" // Mac\n\t \t{\n\t\t\t\"ContentRoot\" \"./steam-macos\"\n\t\t\t\"FileMapping\"\n\t\t\t{\n\t\t\t\t\"LocalPath\" \"*\"\n\t\t\t\t\"DepotPath\" \".\"\n\t\t\t\t\"recursive\" \"1\"\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "build-aux/steam/scripts_macos/launch.sh",
    "content": "#!/bin/zsh\n \narch_name=\"${CPUTYPE}\"\nis_translated=\"$(sysctl -in sysctl.proc_translated)\"\n\nif (( is_translated )) arch_name=\"arm64\"\nif [[ ${@} == *'--intel'* ]] arch_name=\"x86_64\"\nif [[ -d OBS.app ]] exec open OBS.app -W --args \"${@}\"\n\ncase ${arch_name} {\n    x86_64) exec open x86_64/OBS.app -W --args \"${@}\" ;;\n    arm64) exec open arm64/OBS.app -W --args \"${@}\" ;;\n    *) echo \"Unknown architecture: ${arch_name}\"; exit 2 ;;\n}\n"
  },
  {
    "path": "build-aux/steam/scripts_windows/install.bat",
    "content": "@echo off\n@cd /d \"%~dp0\"\n\ngoto checkAdmin\n\n\n:checkAdmin\n\tnet session >nul 2>&1\n\tif %errorLevel% == 0 (\n\t\techo.\n\t) else (\n\t\techo Administrative rights are required. Please re-run this script as Administrator.\n\t\tgoto end\n\t)\n\n:writeRegistry\n\treg add \"HKLM\\SOFTWARE\\OBS Studio\" /f /t REG_SZ /d %1 /reg:32\n\treg add \"HKLM\\SOFTWARE\\OBS Studio\" /f /t REG_SZ /d %1 /reg:64\n\n:setupProgramData\n\t:: Required for UWP applications\n\tmkdir \"%PROGRAMDATA%\\obs-studio-hook\"\n\ticacls \"%PROGRAMDATA%\\obs-studio-hook\" /grant \"ALL APPLICATION PACKAGES\":(OI)(CI)(GR,GE)\n\n:checkDLL\n\techo Checking for 32-bit Virtual Cam registration...\n\treg query \"HKLM\\SOFTWARE\\Classes\\CLSID\\{A3FCE0F5-3493-419F-958A-ABA1250EC20B}\" >nul 2>&1 /reg:32\n\tif %errorLevel% == 0 (\n\t\techo 32-bit Virtual Cam found, skipping install...\n\t\techo.\n\t) else (\n\t\techo 32-bit Virtual Cam not found, installing...\n\t\tgoto install32DLL\n\t)\n\n:CheckDLLContinue\n\techo Checking for 64-bit Virtual Cam registration...\n\treg query \"HKLM\\SOFTWARE\\Classes\\CLSID\\{A3FCE0F5-3493-419F-958A-ABA1250EC20B}\" >nul 2>&1 /reg:64\n\tif %errorLevel% == 0 (\n\t\techo 64-bit Virtual Cam found, skipping install...\n\t\techo.\n\t) else (\n\t\techo 64-bit Virtual Cam not found, installing...\n\t\tgoto install64DLL\n\t)\n\tgoto endSuccess\n\n:install32DLL\n\techo Installing 32-bit Virtual Cam...\n\tregsvr32.exe /i /s %1\\data\\obs-plugins\\win-dshow\\obs-virtualcam-module32.dll\n\treg query \"HKLM\\SOFTWARE\\Classes\\CLSID\\{A3FCE0F5-3493-419F-958A-ABA1250EC20B}\" >nul 2>&1 /reg:32\n\tif %errorLevel% == 0 (\n\t\techo 32-bit Virtual Cam successfully installed\n\t\techo.\n\t) else (\n\t\techo 32-bit Virtual Cam installation failed\n\t\techo.\n\t\tgoto endFail\n\t)\n\tgoto checkDLLContinue\n\n:install64DLL\n\techo Installing 64-bit Virtual Cam...\n\tregsvr32.exe /i /s %1\\data\\obs-plugins\\win-dshow\\obs-virtualcam-module64.dll\n\treg query \"HKLM\\SOFTWARE\\Classes\\CLSID\\{A3FCE0F5-3493-419F-958A-ABA1250EC20B}\" >nul 2>&1 /reg:64\n\tif %errorLevel% == 0 (\n\t\techo 64-bit Virtual Cam successfully installed\n\t\techo.\n\t\tgoto endSuccess\n\t) else (\n\t\techo 64-bit Virtual Cam installation failed\n\t\techo.\n\t\tgoto endFail\n\t)\n\n:endFail\n\techo Something failed, please report this on the OBS Discord or Forums!\n\tgoto end\n\n:endSuccess\n\techo Virtual Cam installed!\n\techo.\n\n:end\n\texit\n"
  },
  {
    "path": "build-aux/steam/scripts_windows/installscript.vdf",
    "content": "\"InstallScript\"\n{\n    \"Run Process\"\n    {\n        \"install\"\n        {\n            \"process 1\" \"scripts\\\\install.bat\"\n            \"command 1\" \"\\\"%INSTALLDIR%\\\"\"\n        }\n    }\n\n    \"Run Process On Uninstall\"\n    {\n        \"uninstall\"\n        {\n            \"process 1\" \"scripts\\\\uninstall.bat\"\n            \"command 1\" \"\\\"%INSTALLDIR%\\\"\"\n        }\n    }\n}\n"
  },
  {
    "path": "build-aux/steam/scripts_windows/uninstall.bat",
    "content": "@echo off\n@cd /d \"%~dp0\"\ngoto checkAdmin\n\n:checkAdmin\n\tnet session >nul 2>&1\n\tif %errorLevel% == 0 (\n\t\techo.\n\t) else (\n\t\techo Administrative rights are required. Please re-run this script as Administrator.\n\t\tgoto end\n\t)\n\n:clearRegistry\n\treg delete \"HKLM\\SOFTWARE\\OBS Studio\" /f /reg:32\n\treg delete \"HKLM\\SOFTWARE\\OBS Studio\" /f /reg:64\n\t:: Vulkan layer keys\n\treg delete \"HKLM\\SOFTWARE\\Khronos\\Vulkan\\ImplicitLayers\" /f /v \"%PROGRAMDATA%\\obs-studio-hook\\obs-vulkan64.json\" /reg:32\n\treg delete \"HKLM\\SOFTWARE\\Khronos\\Vulkan\\ImplicitLayers\" /f /v \"%PROGRAMDATA%\\obs-studio-hook\\obs-vulkan32.json\" /reg:64\n\n:deleteProgramDataFolder\n\tRMDIR /S /Q \"%PROGRAMDATA%\\obs-studio-hook\"\n\tRMDIR /S /Q \"%PROGRAMDATA%\\obs-studio\\shader-cache\"\n\n:uninstallDLLs\n\tregsvr32.exe /u /s %1\\data\\obs-plugins\\win-dshow\\obs-virtualcam-module32.dll\n\tregsvr32.exe /u /s %1\\data\\obs-plugins\\win-dshow\\obs-virtualcam-module64.dll\n\n:endSuccess\n\techo Virtual Cam uninstalled!\n\techo.\n\n:end\n\texit\n"
  },
  {
    "path": "cmake/bundle/windows/ALL_BUILD.vcxproj.user.in",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"12.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='RelWithDebInfo|x64'\">\n    <LocalDebuggerCommand>$(SolutionDir)rundir\\$(Configuration)\\bin\\64bit\\obs64.exe</LocalDebuggerCommand>\n    <LocalDebuggerWorkingDirectory>$(SolutionDir)rundir\\$(Configuration)\\bin\\64bit</LocalDebuggerWorkingDirectory>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\n    <LocalDebuggerCommand>$(SolutionDir)rundir\\$(Configuration)\\bin\\64bit\\obs64.exe</LocalDebuggerCommand>\n    <LocalDebuggerWorkingDirectory>$(SolutionDir)rundir\\$(Configuration)\\bin\\64bit</LocalDebuggerWorkingDirectory>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\n    <LocalDebuggerCommand>$(SolutionDir)rundir\\$(Configuration)\\bin\\64bit\\obs64.exe</LocalDebuggerCommand>\n    <LocalDebuggerWorkingDirectory>$(SolutionDir)rundir\\$(Configuration)\\bin\\64bit</LocalDebuggerWorkingDirectory>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='MinSizeRel|x64'\">\n    <LocalDebuggerCommand>$(SolutionDir)rundir\\$(Configuration)\\bin\\64bit\\obs64.exe</LocalDebuggerCommand>\n    <LocalDebuggerWorkingDirectory>$(SolutionDir)rundir\\$(Configuration)\\bin\\64bit</LocalDebuggerWorkingDirectory>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'\">\n    <LocalDebuggerCommand>$(SolutionDir)rundir\\$(Configuration)\\bin\\32bit\\obs32.exe</LocalDebuggerCommand>\n    <LocalDebuggerWorkingDirectory>$(SolutionDir)rundir\\$(Configuration)\\bin\\32bit</LocalDebuggerWorkingDirectory>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\n    <LocalDebuggerCommand>$(SolutionDir)rundir\\$(Configuration)\\bin\\32bit\\obs32.exe</LocalDebuggerCommand>\n    <LocalDebuggerWorkingDirectory>$(SolutionDir)rundir\\$(Configuration)\\bin\\32bit</LocalDebuggerWorkingDirectory>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\n    <LocalDebuggerCommand>$(SolutionDir)rundir\\$(Configuration)\\bin\\32bit\\obs32.exe</LocalDebuggerCommand>\n    <LocalDebuggerWorkingDirectory>$(SolutionDir)rundir\\$(Configuration)\\bin\\32bit</LocalDebuggerWorkingDirectory>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n  </PropertyGroup>\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'\">\n    <LocalDebuggerCommand>$(SolutionDir)rundir\\$(Configuration)\\bin\\32bit\\obs32.exe</LocalDebuggerCommand>\n    <LocalDebuggerWorkingDirectory>$(SolutionDir)rundir\\$(Configuration)\\bin\\32bit</LocalDebuggerWorkingDirectory>\n    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "cmake/bundle/windows/obs-module.rc.in",
    "content": "1 VERSIONINFO\nFILEVERSION ${UI_VERSION_MAJOR},${UI_VERSION_MINOR},${UI_VERSION_PATCH},0\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"040904B0\"\n    BEGIN\n      VALUE \"CompanyName\", \"${OBS_COMPANY_NAME}\"\n      VALUE \"FileDescription\", \"${MODULE_DESCRIPTION}\"\n      VALUE \"FileVersion\", \"${UI_VERSION}\"\n      VALUE \"ProductName\", \"${OBS_PRODUCT_NAME}\"\n      VALUE \"ProductVersion\", \"${UI_VERSION}\"\n      VALUE \"Comments\", \"${OBS_COMMENTS}\"\n      VALUE \"LegalCopyright\", \"${OBS_LEGAL_COPYRIGHT}\"\n      VALUE \"InternalName\", \"${PROJECT_NAME}\"\n      VALUE \"OriginalFilename\", \"${PROJECT_NAME}\"\n    END\n  END\n\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0409, 0x04B0\n  END\nEND"
  },
  {
    "path": "cmake/common/bootstrap.cmake",
    "content": "# OBS CMake bootstrap module\n\ninclude_guard(GLOBAL)\n\n# Map fallback configurations for optimized build configurations\n# gersemi: off\nset(\n  CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO\n    RelWithDebInfo\n    Release\n    MinSizeRel\n    None\n    \"\"\n)\nset(\n  CMAKE_MAP_IMPORTED_CONFIG_MINSIZEREL\n    MinSizeRel\n    Release\n    RelWithDebInfo\n    None\n    \"\"\n)\nset(\n  CMAKE_MAP_IMPORTED_CONFIG_RELEASE\n    Release\n    RelWithDebInfo\n    MinSizeRel\n    None\n    \"\"\n)\n# gersemi: on\n\n# Prohibit in-source builds\nif(\"${CMAKE_CURRENT_BINARY_DIR}\" STREQUAL \"${CMAKE_CURRENT_SOURCE_DIR}\")\n  message(\n    FATAL_ERROR\n    \"In-source builds of OBS are not supported. \"\n    \"Specify a build directory via 'cmake -S <SOURCE DIRECTORY> -B <BUILD_DIRECTORY>' instead.\"\n  )\n  file(REMOVE_RECURSE \"${CMAKE_CURRENT_SOURCE_DIR}/CMakeCache.txt\" \"${CMAKE_CURRENT_SOURCE_DIR}/CMakeFiles\")\nendif()\n\n# Set default global project variables\nset(OBS_COMPANY_NAME \"OBS Project\")\nset(OBS_PRODUCT_NAME \"OBS Studio\")\nset(OBS_WEBSITE \"https://www.obsproject.com\")\nset(OBS_COMMENTS \"Free and open source software for video recording and live streaming\")\nset(OBS_LEGAL_COPYRIGHT \"(C) Lain Bailey\")\nset(OBS_CMAKE_VERSION 3.0.0)\n\n# Configure default version strings\nset(_obs_default_version \"0\" \"0\" \"1\")\nset(_obs_release_candidate 0)\nset(_obs_beta 0)\n\n# Add common module directories to default search path\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/common\" \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/finders\")\n\ninclude(policies NO_POLICY_SCOPE)\ninclude(versionconfig)\ninclude(buildnumber)\ninclude(osconfig)\n\n# Allow selection of common build types via UI\nif(NOT CMAKE_GENERATOR MATCHES \"(Xcode|Visual Studio .+)\")\n  if(NOT CMAKE_BUILD_TYPE)\n    set(\n      CMAKE_BUILD_TYPE\n      \"RelWithDebInfo\"\n      CACHE STRING\n      \"OBS build type [Release, RelWithDebInfo, Debug, MinSizeRel]\"\n      FORCE\n    )\n    set_property(\n      CACHE CMAKE_BUILD_TYPE\n      PROPERTY STRINGS Release RelWithDebInfo Debug MinSizeRel\n    )\n  endif()\nendif()\n\n# Enable default inclusion of targets' source and binary directory\nset(CMAKE_INCLUDE_CURRENT_DIR TRUE)\n"
  },
  {
    "path": "cmake/common/buildnumber.cmake",
    "content": "# OBS CMake build number module\n\ninclude_guard(GLOBAL)\n\n# Define build number cache file\nset(\n  _BUILD_NUMBER_CACHE\n  \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/.CMakeBuildNumber\"\n  CACHE INTERNAL\n  \"OBS build number cache file\"\n)\n\n# Read build number from cache file or manual override\nif(NOT DEFINED OBS_BUILD_NUMBER)\n  if(EXISTS \"${_BUILD_NUMBER_CACHE}\")\n    file(READ \"${_BUILD_NUMBER_CACHE}\" OBS_BUILD_NUMBER)\n    math(EXPR OBS_BUILD_NUMBER \"${OBS_BUILD_NUMBER}+1\")\n  else()\n    if(\"$ENV{CI}\" AND \"$ENV{GITHUB_RUN_ID}\")\n      set(OBS_BUILD_NUMBER \"$ENV{GITHUB_RUN_ID}\")\n    else()\n      set(OBS_BUILD_NUMBER \"1\")\n    endif()\n  endif()\n  file(WRITE \"${_BUILD_NUMBER_CACHE}\" \"${OBS_BUILD_NUMBER}\")\nendif()\n"
  },
  {
    "path": "cmake/common/buildspec_common.cmake",
    "content": "# OBS common build dependencies module\n\ninclude_guard(GLOBAL)\n\n# _check_deps_version: Checks for obs-deps VERSION file in prefix paths\nfunction(_check_deps_version version)\n  set(found FALSE PARENT_SCOPE)\n\n  foreach(path IN LISTS CMAKE_PREFIX_PATH)\n    if(EXISTS \"${path}/share/obs-deps/VERSION\")\n      if(dependency STREQUAL qt6 AND NOT EXISTS \"${path}/lib/cmake/Qt6/Qt6Config.cmake\")\n        set(found FALSE PARENT_SCOPE)\n        continue()\n      endif()\n\n      file(READ \"${path}/share/obs-deps/VERSION\" _check_version)\n      string(REPLACE \"\\n\" \"\" _check_version \"${_check_version}\")\n      string(REPLACE \"-\" \".\" _check_version \"${_check_version}\")\n      string(REPLACE \"-\" \".\" version \"${version}\")\n\n      if(_check_version VERSION_EQUAL version)\n        set(found TRUE PARENT_SCOPE)\n        break()\n      elseif(_check_version VERSION_LESS version)\n        message(\n          AUTHOR_WARNING\n          \"Older ${label} version detected in ${path}: \\n\"\n          \"Found ${_check_version}, require ${version}\"\n        )\n        list(REMOVE_ITEM CMAKE_PREFIX_PATH \"${path}\")\n        list(APPEND CMAKE_PREFIX_PATH \"${path}\")\n\n        continue()\n      else()\n        message(\n          AUTHOR_WARNING\n          \"Newer ${label} version detected in ${path}: \\n\"\n          \"Found ${_check_version}, require ${version}\"\n        )\n        set(found TRUE PARENT_SCOPE)\n        break()\n      endif()\n    endif()\n  endforeach()\n\n  return(PROPAGATE found CMAKE_PREFIX_PATH)\nendfunction()\n\nfunction(_get_dependency_data variable_name)\n  file(READ \"${CMAKE_CURRENT_SOURCE_DIR}/CMakePresets.json\" preset_data)\n\n  string(JSON configure_presets GET ${preset_data} \"configurePresets\")\n\n  string(JSON preset_count LENGTH \"${configure_presets}\")\n  math(EXPR preset_count \"${preset_count}-1\")\n\n  foreach(index RANGE 0 ${preset_count})\n    string(JSON preset_member_data GET \"${configure_presets}\" ${index})\n    string(JSON preset_name GET ${preset_member_data} \"name\")\n\n    if(preset_name STREQUAL dependencies)\n      string(JSON vendor_data GET ${preset_member_data} \"vendor\")\n      string(JSON vendor_data GET ${vendor_data} \"obsproject.com/obs-studio\")\n      string(JSON dependency_data GET ${vendor_data} \"dependencies\")\n      break()\n    else()\n      continue()\n    endif()\n  endforeach()\n\n  set(${variable_name} \"${dependency_data}\")\n\n  return(PROPAGATE ${variable_name})\nendfunction()\n\n# _check_dependencies: Fetch and extract pre-built OBS build dependencies\nfunction(_check_dependencies)\n  set(dependencies_list ${ARGV})\n  _get_dependency_data(dependency_data)\n\n  foreach(dependency IN LISTS dependencies_list)\n    if(dependency STREQUAL cef AND NOT ENABLE_BROWSER)\n      continue()\n    endif()\n    if(dependency STREQUAL cef AND arch STREQUAL universal)\n      if(CMAKE_OSX_ARCHITECTURES MATCHES \".+;.+\")\n        continue()\n      elseif(CMAKE_OSX_ARCHITECTURES MATCHES \"(arm64|x86_64)\")\n        set(arch ${CMAKE_OSX_ARCHITECTURES})\n      else()\n        set(arch ${CMAKE_HOST_SYSTEM_PROCESSOR})\n      endif()\n      set(platform macos-${arch})\n    endif()\n\n    string(JSON data GET ${dependency_data} ${dependency})\n    string(JSON version GET ${data} version)\n    string(JSON hash GET ${data} hashes ${platform})\n    string(JSON url GET ${data} baseUrl)\n    string(JSON label GET ${data} label)\n    string(JSON revision ERROR_VARIABLE error GET ${data} revision ${platform})\n\n    message(STATUS \"Setting up ${label} (${arch})\")\n\n    set(file \"${${dependency}_filename}\")\n    set(destination \"${${dependency}_destination}\")\n    string(REPLACE \"VERSION\" \"${version}\" file \"${file}\")\n    string(REPLACE \"VERSION\" \"${version}\" destination \"${destination}\")\n    string(REPLACE \"ARCH\" \"${arch}\" file \"${file}\")\n    string(REPLACE \"ARCH\" \"${arch}\" destination \"${destination}\")\n    if(revision)\n      string(REPLACE \"_REVISION\" \"_v${revision}\" file \"${file}\")\n      string(REPLACE \"-REVISION\" \"-v${revision}\" file \"${file}\")\n    else()\n      string(REPLACE \"_REVISION\" \"\" file \"${file}\")\n      string(REPLACE \"-REVISION\" \"\" file \"${file}\")\n    endif()\n\n    if(EXISTS \"${dependencies_dir}/.dependency_${dependency}_${arch}.sha256\")\n      file(\n        READ \"${dependencies_dir}/.dependency_${dependency}_${arch}.sha256\"\n        OBS_DEPENDENCY_${dependency}_${arch}_HASH\n      )\n    endif()\n\n    set(skip FALSE)\n    if(dependency STREQUAL prebuilt OR dependency STREQUAL qt6)\n      if(OBS_DEPENDENCY_${dependency}_${arch}_HASH STREQUAL ${hash})\n        _check_deps_version(${version})\n\n        if(found)\n          set(skip TRUE)\n        endif()\n      endif()\n    elseif(dependency STREQUAL cef)\n      if(NOT ENABLE_BROWSER)\n        set(skip TRUE)\n      elseif(OBS_DEPENDENCY_${dependency}_${arch}_HASH STREQUAL ${hash} AND (CEF_ROOT_DIR AND EXISTS \"${CEF_ROOT_DIR}\"))\n        set(skip TRUE)\n      endif()\n    endif()\n\n    if(skip)\n      message(STATUS \"Setting up ${label} (${arch}) - skipped\")\n      continue()\n    endif()\n\n    if(dependency STREQUAL cef)\n      set(url ${url}/${file})\n    else()\n      set(url ${url}/${version}/${file})\n    endif()\n\n    if(NOT EXISTS \"${dependencies_dir}/${file}\")\n      message(STATUS \"Downloading ${url}\")\n      file(DOWNLOAD \"${url}\" \"${dependencies_dir}/${file}\" STATUS download_status EXPECTED_HASH SHA256=${hash})\n\n      list(GET download_status 0 error_code)\n      list(GET download_status 1 error_message)\n      if(error_code GREATER 0)\n        message(STATUS \"Downloading ${url} - Failure\")\n        message(FATAL_ERROR \"Unable to download ${url}, failed with error: ${error_message}\")\n        file(REMOVE \"${dependencies_dir}/${file}\")\n      else()\n        message(STATUS \"Downloading ${url} - done\")\n      endif()\n    endif()\n\n    if(NOT OBS_DEPENDENCY_${dependency}_${arch}_HASH STREQUAL ${hash})\n      file(REMOVE_RECURSE \"${dependencies_dir}/${destination}\")\n    endif()\n\n    if(NOT EXISTS \"${dependencies_dir}/${destination}\")\n      file(MAKE_DIRECTORY \"${dependencies_dir}/${destination}\")\n      if(dependency STREQUAL obs-studio)\n        file(ARCHIVE_EXTRACT INPUT \"${dependencies_dir}/${file}\" DESTINATION \"${dependencies_dir}\")\n      else()\n        file(ARCHIVE_EXTRACT INPUT \"${dependencies_dir}/${file}\" DESTINATION \"${dependencies_dir}/${destination}\")\n      endif()\n    endif()\n\n    file(WRITE \"${dependencies_dir}/.dependency_${dependency}_${arch}.sha256\" \"${hash}\")\n\n    if(dependency STREQUAL prebuilt)\n      set(VLC_PATH \"${dependencies_dir}/${destination}\" CACHE PATH \"VLC source code directory\" FORCE)\n      list(APPEND CMAKE_PREFIX_PATH \"${dependencies_dir}/${destination}\")\n    elseif(dependency STREQUAL qt6)\n      list(APPEND CMAKE_PREFIX_PATH \"${dependencies_dir}/${destination}\")\n    elseif(dependency STREQUAL cef)\n      set(CEF_ROOT_DIR \"${dependencies_dir}/${destination}\" CACHE PATH \"CEF root directory\" FORCE)\n    endif()\n\n    message(STATUS \"Setting up ${label} (${arch}) - done\")\n  endforeach()\n\n  list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH)\n\n  set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} CACHE PATH \"CMake prefix search path\" FORCE)\nendfunction()\n"
  },
  {
    "path": "cmake/common/ccache.cmake",
    "content": "# OBS CMake ccache module\n\ninclude_guard(GLOBAL)\n\nif(NOT DEFINED CCACHE_PROGRAM)\n  message(DEBUG \"Trying to find ccache on build host\")\n  find_program(CCACHE_PROGRAM \"ccache\")\n  mark_as_advanced(CCACHE_PROGRAM)\nendif()\n\nif(CCACHE_PROGRAM)\n  message(DEBUG \"Trying to find ccache on build host - done\")\n  message(DEBUG \"Ccache found as ${CCACHE_PROGRAM}\")\n  option(ENABLE_CCACHE \"Enable compiler acceleration with ccache\" OFF)\n\n  if(ENABLE_CCACHE)\n    set(CMAKE_C_COMPILER_LAUNCHER \"${CCACHE_PROGRAM}\")\n    set(CMAKE_CXX_COMPILER_LAUNCHER \"${CCACHE_PROGRAM}\")\n    set(CMAKE_OBJC_COMPILER_LAUNCHER \"${CCACHE_PROGRAM}\")\n    set(CMAKE_OBJCXX_COMPILER_LAUNCHER \"${CCACHE_PROGRAM}\")\n    set(CMAKE_CUDA_COMPILER_LAUNCHER \"${CCACHE_PROGRAM}\")\n  endif()\nelse()\n  message(DEBUG \"Trying to find ccache on build host - skipped\")\nendif()\n"
  },
  {
    "path": "cmake/common/compiler_common.cmake",
    "content": "# OBS CMake common compiler options module\n\ninclude_guard(GLOBAL)\n\noption(OBS_COMPILE_DEPRECATION_AS_WARNING \"Downgrade deprecation warnings to actual warnings\" FALSE)\nmark_as_advanced(OBS_COMPILE_DEPRECATION_AS_WARNING)\n\n# Set C and C++ language standards to C17 and C++17\nset(CMAKE_C_STANDARD 17)\nset(CMAKE_C_STANDARD_REQUIRED TRUE)\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_STANDARD_REQUIRED TRUE)\n\n# Set symbols to be hidden by default for C and C++\nset(CMAKE_C_VISIBILITY_PRESET hidden)\nset(CMAKE_CXX_VISIBILITY_PRESET hidden)\nset(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE)\n\n# clang options for C, C++, ObjC, and ObjC++\nset(\n  _obs_clang_common_options\n  -fno-strict-aliasing\n  -Wno-trigraphs\n  -Wno-missing-field-initializers\n  -Wno-missing-prototypes\n  -Werror=return-type\n  -Wunreachable-code\n  -Wquoted-include-in-framework-header\n  -Wno-missing-braces\n  -Wparentheses\n  -Wswitch\n  -Wno-unused-function\n  -Wno-unused-label\n  -Wunused-parameter\n  -Wunused-variable\n  -Wunused-value\n  -Wempty-body\n  -Wuninitialized\n  -Wno-unknown-pragmas\n  -Wfour-char-constants\n  -Wconstant-conversion\n  -Wno-conversion\n  -Wint-conversion\n  -Wbool-conversion\n  -Wenum-conversion\n  -Wnon-literal-null-conversion\n  -Wsign-compare\n  -Wshorten-64-to-32\n  -Wpointer-sign\n  -Wnewline-eof\n  -Wno-implicit-fallthrough\n  -Wdeprecated-declarations\n  -Wno-sign-conversion\n  -Winfinite-recursion\n  -Wcomma\n  -Wno-strict-prototypes\n  -Wno-semicolon-before-method-body\n  -Wformat-security\n  -Wvla\n  -Wno-error=shorten-64-to-32\n  $<$<BOOL:${OBS_COMPILE_DEPRECATION_AS_WARNING}>:-Wno-error=deprecated-declarations>\n)\n\n# clang options for C\nset(_obs_clang_c_options ${_obs_clang_common_options} -Wno-shadow -Wno-float-conversion)\n\n# clang options for C++\nset(\n  _obs_clang_cxx_options\n  ${_obs_clang_common_options}\n  -Wno-non-virtual-dtor\n  -Wno-overloaded-virtual\n  -Wno-exit-time-destructors\n  -Wno-shadow\n  -Winvalid-offsetof\n  -Wmove\n  -Werror=block-capture-autoreleasing\n  -Wrange-loop-analysis\n)\n\nif(CMAKE_CXX_STANDARD GREATER_EQUAL 20)\n  list(APPEND _obs_clang_cxx_options -fno-char8_t)\nendif()\n\nif(NOT DEFINED CMAKE_COMPILE_WARNING_AS_ERROR)\n  set(CMAKE_COMPILE_WARNING_AS_ERROR ON)\nendif()\n\n# Enable interprocedural optimization\nmessage(STATUS \"Checking for interprocedural optimization support\")\nif(NOT DEFINED HAS_INTERPROCEDURAL_OPTIMIZATION)\n  include(CheckIPOSupported)\n  check_ipo_supported(RESULT _ipo_result OUTPUT _ipo_output)\n  set(\n    HAS_INTERPROCEDURAL_OPTIMIZATION\n    ${_ipo_result}\n    CACHE BOOL\n    \"Result of compiler check for interprocedural optimization\"\n    FORCE\n  )\n\n  if(HAS_INTERPROCEDURAL_OPTIMIZATION)\n    message(STATUS \"Checking for interprocedural optimization support - available\")\n  else()\n    message(STATUS \"Checking for interprocedural optimization support - unavailable\")\n  endif()\n\n  mark_as_advanced(HAS_INTERPROCEDURAL_OPTIMIZATION)\n  unset(_ipo_result)\n  unset(_ipo_output)\nendif()\n\nif(HAS_INTERPROCEDURAL_OPTIMIZATION)\n  message(STATUS \"Checking for interprocedural optimization support - enabled [Release, MinSizeRel]\")\n  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DEBUG OFF)\n  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO OFF)\n  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)\n  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON)\nelse()\n  message(STATUS \"Checking for interprocedural optimization support - disabled\")\n  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DEBUG OFF)\n  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO OFF)\n  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE OFF)\n  set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL OFF)\nendif()\n"
  },
  {
    "path": "cmake/common/cpackconfig_common.cmake",
    "content": "# OBS CMake common CPack module\n\ninclude_guard(GLOBAL)\n\n# Set default global CPack variables\nset(CPACK_PACKAGE_NAME obs-studio)\nset(CPACK_PACKAGE_VENDOR \"${OBS_WEBSITE}\")\nset(CPACK_PACKAGE_HOMEPAGE_URL \"${OBS_WEBSITE}\")\nset(CPACK_PACKAGE_DESCRIPTION_SUMMARY \"${OBS_COMMENTS}\")\nset(CPACK_PACKAGE_CHECKSUM SHA256)\n\nset(CPACK_PACKAGE_VERSION_MAJOR ${OBS_VERSION_MAJOR})\nset(CPACK_PACKAGE_VERSION_MINOR ${OBS_VERSION_MINOR})\nset(CPACK_PACKAGE_VERSION_PATCH ${OBS_VERSION_PATCH})\n"
  },
  {
    "path": "cmake/common/helpers_common.cmake",
    "content": "# OBS CMake common helper functions module\n\ninclude_guard(GLOBAL)\n\n# message_configuration: Function to print configuration outcome\nfunction(message_configuration)\n  include(FeatureSummary)\n  feature_summary(WHAT ALL VAR _feature_summary)\n\n  message(DEBUG \"${_feature_summary}\")\n\n  message(\n    NOTICE\n    \"                      _                   _             _ _       \\n\"\n    \"                 ___ | |__  ___       ___| |_ _   _  __| (_) ___  \\n\"\n    \"                / _ \\\\| '_ \\\\/ __|_____/ __| __| | | |/ _` | |/ _ \\\\ \\n\"\n    \"               | (_) | |_) \\\\__ \\\\_____\\\\__ \\\\ |_| |_| | (_| | | (_) |\\n\"\n    \"                \\\\___/|_.__/|___/     |___/\\\\__|\\\\__,_|\\\\__,_|_|\\\\___/ \\n\"\n    \"\\nOBS:  Application Version: ${OBS_VERSION} - Build Number: ${OBS_BUILD_NUMBER}\\n\"\n    \"==================================================================================\\n\\n\"\n  )\n\n  get_property(OBS_FEATURES_ENABLED GLOBAL PROPERTY OBS_FEATURES_ENABLED)\n  list(SORT OBS_FEATURES_ENABLED COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING)\n\n  if(OBS_FEATURES_ENABLED)\n    message(NOTICE \"------------------------       Enabled Features           ------------------------\")\n    foreach(feature IN LISTS OBS_FEATURES_ENABLED)\n      message(NOTICE \" - ${feature}\")\n    endforeach()\n  endif()\n\n  get_property(OBS_FEATURES_DISABLED GLOBAL PROPERTY OBS_FEATURES_DISABLED)\n  list(SORT OBS_FEATURES_DISABLED COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING)\n\n  if(OBS_FEATURES_DISABLED)\n    message(NOTICE \"------------------------       Disabled Features          ------------------------\")\n    foreach(feature IN LISTS OBS_FEATURES_DISABLED)\n      message(NOTICE \" - ${feature}\")\n    endforeach()\n  endif()\n\n  if(ENABLE_PLUGINS)\n    get_property(OBS_MODULES_ENABLED GLOBAL PROPERTY OBS_MODULES_ENABLED)\n    list(SORT OBS_MODULES_ENABLED COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING)\n\n    if(OBS_MODULES_ENABLED)\n      message(NOTICE \"------------------------        Enabled Modules           ------------------------\")\n      foreach(feature IN LISTS OBS_MODULES_ENABLED)\n        message(NOTICE \" - ${feature}\")\n      endforeach()\n    endif()\n\n    get_property(OBS_MODULES_DISABLED GLOBAL PROPERTY OBS_MODULES_DISABLED)\n    list(SORT OBS_MODULES_DISABLED COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING)\n\n    if(OBS_MODULES_DISABLED)\n      message(NOTICE \"------------------------        Disabled Modules          ------------------------\")\n      foreach(feature IN LISTS OBS_MODULES_DISABLED)\n        message(NOTICE \" - ${feature}\")\n      endforeach()\n    endif()\n  endif()\n  message(NOTICE \"----------------------------------------------------------------------------------\")\nendfunction()\n\n# target_enable_feature: Adds feature to list of enabled application features and sets optional compile definitions\nfunction(target_enable_feature target feature_description)\n  set_property(GLOBAL APPEND PROPERTY OBS_FEATURES_ENABLED \"${feature_description}\")\n\n  if(ARGN)\n    target_compile_definitions(${target} PRIVATE ${ARGN})\n  endif()\nendfunction()\n\n# target_disable_feature: Adds feature to list of disabled application features and sets optional compile definitions\nfunction(target_disable_feature target feature_description)\n  set_property(GLOBAL APPEND PROPERTY OBS_FEATURES_DISABLED \"${feature_description}\")\n\n  if(ARGN)\n    target_compile_definitions(${target} PRIVATE ${ARGN})\n  endif()\nendfunction()\n\n# target_disable: Adds target to list of disabled modules\nfunction(target_disable target)\n  set_property(GLOBAL APPEND PROPERTY OBS_MODULES_DISABLED ${target})\nendfunction()\n\n# _handle_generator_expression_dependency: Helper function to yield dependency from a generator expression\nfunction(_handle_generator_expression_dependency library)\n  set(oneValueArgs FOUND_VAR)\n  set(multiValueArgs)\n  cmake_parse_arguments(var \"\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})\n\n  set(${var_FOUND_VAR} \"${var_FOUND_VAR}-NOTFOUND\")\n\n  message(DEBUG \"Checking ${library}...\")\n\n  if(library MATCHES \"\\\\$<\\\\$<PLATFORM_ID:[^>]+>:.+>\" OR library MATCHES \"\\\\$<\\\\$<NOT:\\\\$<PLATFORM_ID:[^>]+>>:.+>\")\n    # Platform-dependent generator expression found. Platforms are a comma-separated list of CMake host OS identifiers.\n    # Convert to CMake list and check if current host OS is contained in list.\n    string(REGEX REPLACE \"\\\\$<.*\\\\$<PLATFORM_ID:([^>]+)>>?:([^>]+)>\" \"\\\\1;\\\\2\" gen_expression \"${library}\")\n    list(GET gen_expression 0 gen_platform)\n    list(GET gen_expression 1 gen_library)\n    string(REPLACE \",\" \";\" gen_platform \"${gen_platform}\")\n\n    set(${var_FOUND_VAR} \"${var_FOUND_VAR}-SKIP\")\n\n    if(library MATCHES \"\\\\$<\\\\$<NOT:.+>.+>\")\n      if(NOT CMAKE_SYSTEM_NAME IN_LIST gen_platform)\n        set(${var_FOUND_VAR} \"${gen_library}\")\n      endif()\n    else()\n      if(CMAKE_SYSTEM_NAME IN_LIST gen_platform)\n        set(${var_FOUND_VAR} \"${gen_library}\")\n      endif()\n    endif()\n  elseif(library MATCHES \"\\\\$<\\\\$<BOOL:[^>]+>:.+>\")\n    # Boolean generator expression found. Consider parameter a CMake variable that resolves into a CMake-like boolean\n    # value for a simple conditional check.\n    string(REGEX REPLACE \"\\\\$<\\\\$<BOOL:([^>]+)>:([^>]+)>\" \"\\\\1;\\\\2\" gen_expression \"${library}\")\n    list(GET gen_expression 0 gen_boolean)\n    list(GET gen_expression 1 gen_library)\n\n    set(${var_FOUND_VAR} \"${var_FOUND_VAR}-SKIP\")\n\n    if(${gen_boolean})\n      set(${var_FOUND_VAR} \"${gen_library}\")\n    endif()\n  elseif(library MATCHES \"\\\\$<TARGET_NAME_IF_EXISTS:[^>]+>\")\n    # Target-dependent generator expression found. Consider parameter to be a CMake target identifier and check for\n    # target existence.\n    string(REGEX REPLACE \"\\\\$<TARGET_NAME_IF_EXISTS:([^>]+)>\" \"\\\\1\" gen_target \"${library}\")\n\n    set(${var_FOUND_VAR} \"${var_FOUND_VAR}-SKIP\")\n\n    if(TARGET ${gen_target})\n      set(${var_FOUND_VAR} \"${gen_target}\")\n    endif()\n  elseif(library MATCHES \"\\\\$<.*Qt6::(EntryPointPrivate|QDarwin.*PermissionPlugin)>\")\n    set(${var_FOUND_VAR} \"${var_FOUND_VAR}-SKIP\")\n  else()\n    # Unknown or unimplemented generator expression found. Abort script run to either add to ignore list or implement\n    # detection.\n    message(FATAL_ERROR \"${library} is an unsupported generator expression for linked libraries.\")\n  endif()\n\n  if(CMAKE_VERSION VERSION_LESS 3.25)\n    set(${var_FOUND_VAR} ${var_FOUND_VAR} PARENT_SCOPE)\n  else()\n    return(PROPAGATE ${var_FOUND_VAR})\n  endif()\nendfunction()\n\n# find_dependencies: Check linked interface and direct dependencies of target\nfunction(find_dependencies)\n  set(oneValueArgs TARGET FOUND_VAR)\n  set(multiValueArgs)\n  cmake_parse_arguments(var \"\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})\n\n  if(NOT DEFINED is_root)\n    # Root of recursive dependency resolution\n    set(is_root TRUE)\n    set(nested_depth 0)\n  else()\n    # Branch of recursive dependency resolution\n    set(is_root FALSE)\n    math(EXPR nested_depth \"${nested_depth}+1\")\n  endif()\n\n  # * LINK_LIBRARIES are direct dependencies\n  # * INTERFACE_LINK_LIBRARIES are transitive dependencies\n  get_target_property(linked_libraries ${var_TARGET} LINK_LIBRARIES)\n  get_target_property(interface_libraries ${var_TARGET} INTERFACE_LINK_LIBRARIES)\n  message(DEBUG \"[${nested_depth}] Linked libraries in target ${var_TARGET}: ${linked_libraries}\")\n  message(DEBUG \"[${nested_depth}] Linked interface libraries in target ${var_TARGET}: ${interface_libraries}\")\n\n  # Consider CMake targets only\n  list(FILTER linked_libraries INCLUDE REGEX \".+::.+\")\n  list(FILTER interface_libraries INCLUDE REGEX \".+::.+\")\n\n  foreach(library IN LISTS linked_libraries interface_libraries)\n    if(NOT library)\n      continue()\n    elseif(library MATCHES \"\\\\$<.*:[^>]+>\")\n      # Generator expression found\n      _handle_generator_expression_dependency(${library} FOUND_VAR found_library)\n      if(found_library STREQUAL found_library-SKIP)\n        continue()\n      elseif(found_library)\n        set(library ${found_library})\n      endif()\n    endif()\n\n    message(DEBUG \"[${nested_depth}] Found ${library}...\")\n\n    if(NOT library IN_LIST ${var_FOUND_VAR})\n      list(APPEND found_libraries ${library})\n      # Enter recursive branch\n      find_dependencies(TARGET ${library} FOUND_VAR ${var_FOUND_VAR})\n    endif()\n  endforeach()\n\n  if(NOT is_root)\n    # Exit recursive branch\n    return(PROPAGATE found_libraries)\n  endif()\n\n  list(REMOVE_DUPLICATES found_libraries)\n  list(APPEND ${var_FOUND_VAR} ${found_libraries})\n\n  return(PROPAGATE ${var_FOUND_VAR})\nendfunction()\n\n# find_qt_plugins: Find and add Qt plugin libraries associated with Qt component to target\nfunction(find_qt_plugins)\n  set(oneValueArgs COMPONENT TARGET FOUND_VAR)\n  cmake_parse_arguments(var \"\" \"${oneValueArgs}\" \"${multiValueArgs}\" ${ARGN})\n\n  string(REPLACE \"::\" \";\" library_tuple \"${var_COMPONENT}\")\n  list(GET library_tuple 0 library_namespace)\n  list(GET library_tuple 1 library_name)\n\n  if(NOT ${library_namespace} MATCHES \"Qt6?\")\n    message(FATAL_ERROR \"'find_qt_plugins' has to be called with a valid target from the Qt or Qt6 namespace.\")\n  endif()\n\n  list(\n    APPEND qt_plugins_Core\n    platforms\n    printsupport\n    styles\n    imageformats\n    iconengines\n  )\n  list(APPEND qt_plugins_Gui platforminputcontexts)\n  list(APPEND qt_plugins_Sql sqldrivers)\n  list(APPEND qt_plugins_3dRender sceneparsers geometryloaders)\n  list(APPEND qt_plugins_3dQuickRender renderplugins)\n  list(APPEND qt_plugins_Positioning position)\n  list(APPEND qt_plugins_Location geoservices)\n  list(APPEND qt_plugins_TextToSpeech texttospeech)\n  list(APPEND qt_plugins_WebView webview)\n\n  if(qt_plugins_${library_name})\n    get_target_property(library_location ${var_COMPONENT} IMPORTED_LOCATION)\n    get_target_property(is_framework ${var_COMPONENT} FRAMEWORK)\n\n    if(is_framework)\n      # Resolve Qt plugin location relative to framework binary location on macOS\n      set(plugins_location \"../../../../../plugins\")\n      cmake_path(ABSOLUTE_PATH plugins_location BASE_DIRECTORY \"${library_location}\" NORMALIZE)\n    else()\n      # Resolve Qt plugin location relative to dynamic library location\n      set(plugins_location \"../../plugins\")\n      cmake_path(ABSOLUTE_PATH plugins_location BASE_DIRECTORY \"${library_location}\" NORMALIZE)\n    endif()\n\n    foreach(plugin IN ITEMS ${qt_plugins_${library_name}})\n      if(NOT plugin IN_LIST plugins_list)\n        if(EXISTS \"${plugins_location}/${plugin}\")\n          # Gather all .dll or .dylib files in given plugin subdirectory\n          file(\n            GLOB plugin_libraries\n            RELATIVE \"${plugins_location}/${plugin}\"\n            \"${plugins_location}/${plugin}/*.dylib\"\n            \"${plugins_location}/${plugin}/*.dll\"\n          )\n          message(DEBUG \"Found Qt plugin ${plugin} libraries: ${plugin_libraries}\")\n          foreach(plugin_library IN ITEMS ${plugin_libraries})\n            set(plugin_full_path \"${plugins_location}/${plugin}/${plugin_library}\")\n            list(APPEND plugins_list ${plugin_full_path})\n          endforeach()\n        endif()\n      endif()\n    endforeach()\n  endif()\n\n  set(${var_FOUND_VAR} ${plugins_list})\n  return(PROPAGATE ${var_FOUND_VAR})\nendfunction()\n\n# target_export: Helper function to export target as CMake package\nfunction(target_export target)\n  if(NOT DEFINED exclude_variant)\n    set(exclude_variant EXCLUDE_FROM_ALL)\n  endif()\n\n  get_target_property(is_framework ${target} FRAMEWORK)\n  if(is_framework)\n    set(package_destination \"Frameworks/${target}.framework/Resources/cmake\")\n    set(include_destination \"Frameworks/${target}.framework/Headers\")\n  else()\n    if(OS_WINDOWS)\n      set(package_destination \"${OBS_CMAKE_DESTINATION}\")\n    else()\n      set(package_destination \"${OBS_CMAKE_DESTINATION}/${target}\")\n    endif()\n    set(include_destination \"${OBS_INCLUDE_DESTINATION}\")\n  endif()\n\n  install(\n    TARGETS ${target}\n    EXPORT ${target}Targets\n    RUNTIME DESTINATION \"${OBS_EXECUTABLE_DESTINATION}\" COMPONENT Development\n    ${exclude_variant}\n    LIBRARY DESTINATION \"${OBS_LIBRARY_DESTINATION}\" COMPONENT Development\n    ${exclude_variant}\n    ARCHIVE DESTINATION \"${OBS_LIBRARY_DESTINATION}\" COMPONENT Development\n    ${exclude_variant}\n    FRAMEWORK DESTINATION Frameworks COMPONENT Development\n    ${exclude_variant}\n    INCLUDES DESTINATION \"${include_destination}\"\n    PUBLIC_HEADER DESTINATION \"${include_destination}\" COMPONENT Development\n    ${exclude_variant}\n  )\n\n  get_target_property(obs_public_headers ${target} OBS_PUBLIC_HEADERS)\n\n  if(obs_public_headers)\n    foreach(header IN LISTS obs_public_headers)\n      cmake_path(GET header PARENT_PATH header_dir)\n      if(header_dir)\n        if(NOT ${header_dir} IN_LIST header_dirs)\n          list(APPEND header_dirs ${header_dir})\n        endif()\n        list(APPEND headers_${header_dir} ${header})\n      else()\n        list(APPEND headers ${header})\n      endif()\n    endforeach()\n\n    foreach(header_dir IN LISTS header_dirs)\n      install(\n        FILES ${headers_${header_dir}}\n        DESTINATION \"${include_destination}/${header_dir}\"\n        COMPONENT Development\n        ${exclude_variant}\n      )\n    endforeach()\n\n    if(headers)\n      install(FILES ${headers} DESTINATION \"${include_destination}\" COMPONENT Development ${exclude_variant})\n    endif()\n  endif()\n\n  if(target STREQUAL libobs AND NOT EXISTS \"${include_destination}/obsconfig.h\")\n    install(\n      FILES \"${CMAKE_BINARY_DIR}/config/obsconfig.h\"\n      DESTINATION \"${include_destination}\"\n      COMPONENT Development\n      ${exclude_variant}\n    )\n  endif()\n\n  get_target_property(target_type ${target} TYPE)\n\n  if(NOT target_type STREQUAL INTERFACE_LIBRARY)\n    message(DEBUG \"Generating export header for target ${target} as ${target}_EXPORT.h...\")\n    include(GenerateExportHeader)\n    generate_export_header(${target} EXPORT_FILE_NAME \"${target}_EXPORT.h\")\n    target_sources(${target} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/${target}_EXPORT.h>)\n\n    set_property(TARGET ${target} APPEND PROPERTY PUBLIC_HEADER \"${target}_EXPORT.h\")\n  endif()\n\n  set(TARGETS_EXPORT_NAME ${target}Targets)\n  message(\n    DEBUG\n    \"Generating CMake package configuration file ${target}Config.cmake with targets file ${TARGETS_EXPORT_NAME}...\"\n  )\n  include(CMakePackageConfigHelpers)\n  configure_package_config_file(\n    cmake/${target}Config.cmake.in\n    ${target}Config.cmake\n    INSTALL_DESTINATION \"${package_destination}\"\n  )\n\n  message(DEBUG \"Generating CMake package version configuration file ${target}ConfigVersion.cmake...\")\n  write_basic_package_version_file(\n    \"${target}ConfigVersion.cmake\"\n    VERSION ${OBS_VERSION_CANONICAL}\n    COMPATIBILITY SameMajorVersion\n  )\n\n  export(EXPORT ${target}Targets FILE \"${TARGETS_EXPORT_NAME}.cmake\" NAMESPACE OBS::)\n\n  export(PACKAGE ${target})\n\n  install(\n    EXPORT ${TARGETS_EXPORT_NAME}\n    FILE ${TARGETS_EXPORT_NAME}.cmake\n    NAMESPACE OBS::\n    DESTINATION \"${package_destination}\"\n    COMPONENT Development\n    ${exclude_variant}\n  )\n\n  install(\n    FILES \"${CMAKE_CURRENT_BINARY_DIR}/${target}Config.cmake\" \"${CMAKE_CURRENT_BINARY_DIR}/${target}ConfigVersion.cmake\"\n    DESTINATION \"${package_destination}\"\n    COMPONENT Development\n    ${exclude_variant}\n  )\n\n  if(target STREQUAL libobs)\n    install(\n      FILES \"${CMAKE_SOURCE_DIR}/cmake/finders/FindSIMDe.cmake\"\n      DESTINATION \"${package_destination}/finders\"\n      COMPONENT Development\n      ${exclude_variant}\n    )\n  endif()\nendfunction()\n\n# check_uuid: Helper function to check for valid UUID\nfunction(check_uuid uuid_string return_value)\n  set(valid_uuid TRUE)\n  # gersemi: off\n  set(uuid_token_lengths 8 4 4 4 12)\n  # gersemi: on\n  set(token_num 0)\n\n  string(REPLACE \"-\" \";\" uuid_tokens ${uuid_string})\n  list(LENGTH uuid_tokens uuid_num_tokens)\n\n  if(uuid_num_tokens EQUAL 5)\n    message(DEBUG \"UUID ${uuid_string} is valid with 5 tokens.\")\n    foreach(uuid_token IN LISTS uuid_tokens)\n      list(GET uuid_token_lengths ${token_num} uuid_target_length)\n      string(LENGTH \"${uuid_token}\" uuid_actual_length)\n      if(uuid_actual_length EQUAL uuid_target_length)\n        string(REGEX MATCH \"[0-9a-fA-F]+\" uuid_hex_match ${uuid_token})\n        if(NOT uuid_hex_match STREQUAL uuid_token)\n          set(valid_uuid FALSE)\n          break()\n        endif()\n      else()\n        set(valid_uuid FALSE)\n        break()\n      endif()\n      math(EXPR token_num \"${token_num}+1\")\n    endforeach()\n  else()\n    set(valid_uuid FALSE)\n  endif()\n  message(DEBUG \"UUID ${uuid_string} valid: ${valid_uuid}\")\n\n  set(${return_value} ${valid_uuid})\n  return(PROPAGATE ${return_value})\nendfunction()\n\n# add_obs_plugin: Add plugin subdirectory if host platform is in specified list of supported platforms and architectures\nfunction(add_obs_plugin target)\n  set(options WITH_MESSAGE)\n  set(oneValueArgs \"\")\n  set(multiValueArgs PLATFORMS ARCHITECTURES)\n  cmake_parse_arguments(PARSE_ARGV 0 _AOP \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\")\n\n  set(found_platform FALSE)\n  list(LENGTH _AOP_PLATFORMS _AOP_NUM_PLATFORMS)\n\n  set(found_architecture FALSE)\n  list(LENGTH _AOP_ARCHITECTURES _AOP_NUM_ARCHITECTURES)\n\n  if(_AOP_NUM_PLATFORMS EQUAL 0)\n    set(found_platform TRUE)\n  else()\n    foreach(platform IN LISTS _AOP_PLATFORMS)\n      set(check_var_name \"OS_${platform}\")\n      if(${${check_var_name}})\n        set(found_platform TRUE)\n        break()\n      endif()\n    endforeach()\n  endif()\n\n  if(_AOP_NUM_ARCHITECTURES EQUAL 0)\n    set(found_architecture TRUE)\n  else()\n    foreach(architecture IN LISTS _AOP_ARCHITECTURES)\n      if(OS_WINDOWS)\n        if(\"${architecture}\" STREQUAL CMAKE_VS_PLATFORM_NAME)\n          set(found_architecture TRUE)\n        endif()\n      elseif(OS_MACOS)\n        if(\n          \"${architecture}\" IN_LIST CMAKE_OSX_ARCHITECTURES\n          OR \"${architecture}\" STREQUAL \"${CMAKE_HOST_SYSTEM_PROCESSOR}\"\n        )\n          set(found_architecture TRUE)\n        endif()\n      elseif(\"${architecture}\" STREQUAL CMAKE_SYSTEM_PROCESSOR)\n        set(found_architecture TRUE)\n      endif()\n    endforeach()\n  endif()\n\n  if(found_platform AND found_architecture)\n    add_subdirectory(${target})\n  elseif(_AOP_WITH_MESSAGE)\n    add_custom_target(${target} COMMENT \"Dummy target for unavailable module ${target}\")\n    target_disable(${target})\n  endif()\nendfunction()\n"
  },
  {
    "path": "cmake/common/osconfig.cmake",
    "content": "# OBS CMake operating system bootstrap module\n\ninclude_guard(GLOBAL)\n\nif(CMAKE_HOST_SYSTEM_NAME STREQUAL \"Windows\")\n  set(CMAKE_C_EXTENSIONS FALSE)\n  set(CMAKE_CXX_EXTENSIONS FALSE)\n  list(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows\")\n  set(OS_WINDOWS TRUE)\nelseif(CMAKE_HOST_SYSTEM_NAME STREQUAL \"Darwin\")\n  set(CMAKE_C_EXTENSIONS FALSE)\n  set(CMAKE_CXX_EXTENSIONS FALSE)\n  list(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos\")\n  set(OS_MACOS TRUE)\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD|OpenBSD\")\n  set(CMAKE_CXX_EXTENSIONS FALSE)\n  list(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/linux\")\n  string(TOUPPER \"${CMAKE_HOST_SYSTEM_NAME}\" _SYSTEM_NAME_U)\n  set(OS_${_SYSTEM_NAME_U} TRUE)\nendif()\n"
  },
  {
    "path": "cmake/common/policies.cmake",
    "content": "# OBS CMake policies module\n\ninclude_guard(GLOBAL)\n"
  },
  {
    "path": "cmake/common/versionconfig.cmake",
    "content": "# OBS CMake common version helper module\n\ninclude_guard(GLOBAL)\n\nset(_obs_version ${_obs_default_version})\nset(_obs_version_canonical ${_obs_default_version})\n\n# Attempt to automatically discover expected OBS version\nif(NOT DEFINED OBS_VERSION_OVERRIDE AND EXISTS \"${CMAKE_CURRENT_SOURCE_DIR}/.git\")\n  execute_process(\n    COMMAND git describe --always --tags --dirty=-modified\n    OUTPUT_VARIABLE _obs_version\n    ERROR_VARIABLE _git_describe_err\n    WORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\n    RESULT_VARIABLE _obs_version_result\n    OUTPUT_STRIP_TRAILING_WHITESPACE\n  )\n\n  if(_git_describe_err)\n    message(FATAL_ERROR \"Could not fetch OBS version tag from git.\\n\" ${_git_describe_err})\n  endif()\n\n  if(_obs_version_result EQUAL 0)\n    string(REGEX REPLACE \"([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+).*\" \"\\\\1;\\\\2;\\\\3\" _obs_version_canonical ${_obs_version})\n  endif()\nelseif(DEFINED OBS_VERSION_OVERRIDE)\n  if(OBS_VERSION_OVERRIDE MATCHES \"([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+).*\")\n    string(\n      REGEX REPLACE\n      \"([0-9]+)\\\\.([0-9]+)\\\\.([0-9]+).*\"\n      \"\\\\1;\\\\2;\\\\3\"\n      _obs_version_canonical\n      ${OBS_VERSION_OVERRIDE}\n    )\n    set(_obs_version ${OBS_VERSION_OVERRIDE})\n  else()\n    message(FATAL_ERROR \"Invalid version supplied - must be <MAJOR>.<MINOR>.<PATCH>[-(rc|beta)<NUMBER>].\")\n  endif()\nendif()\n\n# Set beta/rc versions if suffix included in version string\nif(_obs_version MATCHES \"[0-9]+\\\\.[0-9]+\\\\.[0-9]+-rc[0-9]+\")\n  string(REGEX REPLACE \"[0-9]+\\\\.[0-9]+\\\\.[0-9]+-rc([0-9]+).*$\" \"\\\\1\" _obs_release_candidate ${_obs_version})\nelseif(_obs_version MATCHES \"[0-9]+\\\\.[0-9]+\\\\.[0-9]+-beta[0-9]+\")\n  string(REGEX REPLACE \"[0-9]+\\\\.[0-9]+\\\\.[0-9]+-beta([0-9]+).*$\" \"\\\\1\" _obs_beta ${_obs_version})\nendif()\n\nlist(GET _obs_version_canonical 0 OBS_VERSION_MAJOR)\nlist(GET _obs_version_canonical 1 OBS_VERSION_MINOR)\nlist(GET _obs_version_canonical 2 OBS_VERSION_PATCH)\n\nset(OBS_RELEASE_CANDIDATE ${_obs_release_candidate})\nset(OBS_BETA ${_obs_beta})\n\nstring(REPLACE \";\" \".\" OBS_VERSION_CANONICAL \"${_obs_version_canonical}\")\nstring(REPLACE \";\" \".\" OBS_VERSION \"${_obs_version}\")\n\nif(OBS_RELEASE_CANDIDATE GREATER 0)\n  message(\n    AUTHOR_WARNING\n    \"******************************************************************************\\n\"\n    \"  + OBS-Studio - Release candidate detected, OBS_VERSION is now: ${OBS_VERSION}\\n\"\n    \"******************************************************************************\"\n  )\nelseif(OBS_BETA GREATER 0)\n  message(\n    AUTHOR_WARNING\n    \"******************************************************************************\\n\"\n    \"  + OBS-Studio - Beta detected, OBS_VERSION is now: ${OBS_VERSION}\\n\"\n    \"******************************************************************************\"\n  )\nendif()\n\nunset(_obs_default_version)\nunset(_obs_version)\nunset(_obs_version_canonical)\nunset(_obs_release_candidate)\nunset(_obs_beta)\nunset(_obs_version_result)\n"
  },
  {
    "path": "cmake/finders/FindAMF.cmake",
    "content": "#[=======================================================================[.rst\nFindAMF\n-------\n\nFindModule for AMF and associated headers\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``AMF::AMF``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``AMF_FOUND``\n  True, if headers were found.\n``AMF_VERSION``\n  Detected version of found AMF headers.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``AMF_INCLUDE_DIR``\n  Directory containing ``AMF/core/Factory.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_path(AMF_INCLUDE_DIR NAMES AMF/core/Factory.h PATHS /usr/include /usr/local/include DOC \"AMF include directory\")\n\nif(EXISTS \"${AMF_INCLUDE_DIR}/AMF/core/Version.h\")\n  file(\n    STRINGS \"${AMF_INCLUDE_DIR}/AMF/core/Version.h\"\n    _version_string\n    REGEX \"^.*VERSION_(MAJOR|MINOR|RELEASE|BUILD_NUM)[ \\t]+[0-9]+[ \\t]*$\"\n  )\n\n  string(REGEX REPLACE \".*VERSION_MAJOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_major \"${_version_string}\")\n  string(REGEX REPLACE \".*VERSION_MINOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_minor \"${_version_string}\")\n  string(REGEX REPLACE \".*VERSION_RELEASE[ \\t]+([0-9]+).*\" \"\\\\1\" _version_release \"${_version_string}\")\n\n  set(AMF_VERSION \"${_version_major}.${_version_minor}.${_version_release}\")\n  unset(_version_major)\n  unset(_version_minor)\n  unset(_version_release)\nelse()\n  if(NOT AMF_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find AMF version.\")\n  endif()\n  set(AMF_VERSION 0.0.0)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(AMF_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(AMF_ERROR_REASON \"Ensure AMF headers are available in local library paths.\")\nendif()\n\nfind_package_handle_standard_args(\n  AMF\n  REQUIRED_VARS AMF_INCLUDE_DIR\n  VERSION_VAR AMF_VERSION\n  REASON_FAILURE_MESSAGE \"${AMF_ERROR_REASON}\"\n)\nmark_as_advanced(AMF_INCLUDE_DIR)\nunset(AMF_ERROR_REASON)\n\nif(AMF_FOUND)\n  if(NOT TARGET AMF::AMF)\n    add_library(AMF::AMF INTERFACE IMPORTED)\n    set_target_properties(AMF::AMF PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${AMF_INCLUDE_DIR}\")\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  AMF\n  PROPERTIES\n    URL \"https://github.com/GPUOpen-LibrariesAndSDKs/AMF\"\n    DESCRIPTION\n      \"AMF is a light-weight, portable multimedia framework that abstracts away most of the platform and API-specific details and allows for easy implementation of multimedia applications using a variety of technologies, such as DirectX 11, OpenGL, and OpenCL and facilitates an efficient interop between them.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindAsio.cmake",
    "content": "#[=======================================================================[.rst\nFindAsio\n--------\n\nFindModule for Asio and the associated library\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Asio::Asio``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Asio_FOUND``\n  True, if the library was found.\n``Asio_VERSION``\n  Detected version of found Asio library.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Asio_INCLUDE_DIR``\n  Directory containing ``asio.hpp``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Asio QUIET asio)\nendif()\n\nfind_path(\n  Asio_INCLUDE_DIR\n  NAMES asio.hpp\n  HINTS ${PC_Asio_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"Asio include directory\"\n)\n\nif(PC_Asio_VERSION VERSION_GREATER 0)\n  set(Asio_VERSION ${PC_Asio_VERSION})\nelseif(EXISTS \"${Asio_INCLUDE_DIR}/asio/version.hpp\")\n  file(\n    STRINGS \"${Asio_INCLUDE_DIR}/asio/version.hpp\"\n    _version_string\n    REGEX \"#define[ \\t]+ASIO_VERSION[ \\t]+[0-9]+[ \\t]+\\\\/\\\\/[ \\t][0-9]+\\\\.[0-9]+\\\\.[0-9]+\"\n  )\n\n  string(\n    REGEX REPLACE\n    \"#define[ \\t]+ASIO_VERSION[ \\t]+[0-9]+[ \\t]+\\\\/\\\\/[ \\t]([0-9]+\\\\.[0-9]+\\\\.[0-9]+)\"\n    \"\\\\1\"\n    Asio_VERSION\n    \"${_version_string}\"\n  )\nelse()\n  if(NOT Asio_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Asio version.\")\n  endif()\n  set(Asio_VERSION 0.0.0)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(Asio_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(Asio_ERROR_REASON \"Ensure Asio library is available in local include paths.\")\nendif()\n\nfind_package_handle_standard_args(\n  Asio\n  REQUIRED_VARS Asio_INCLUDE_DIR\n  VERSION_VAR Asio_VERSION\n  REASON_FAILURE_MESSAGE \"${Asio_ERROR_REASON}\"\n)\nmark_as_advanced(Asio_INCLUDE_DIR)\nunset(Asio_ERROR_REASON)\n\nif(Asio_FOUND)\n  if(NOT TARGET Asio::Asio)\n    add_library(Asio::Asio INTERFACE IMPORTED)\n    set_target_properties(Asio::Asio PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${Asio_INCLUDE_DIR}\")\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Asio\n  PROPERTIES\n    URL \"http://think-async.com/Asio\"\n    DESCRIPTION\n      \"Asio is a cross-platform C++ library for network and low-level I/O programming that provides developers with a consistent asynchronous model using a modern C++ approach.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindCEF.cmake",
    "content": "#[=======================================================================[.rst\nFindCEF\n----------\n\nFindModule for CEF and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` targets:\n\n``CEF::Wrapper``\n  Static library loading wrapper\n\n``CEF::Library``\n  Chromium Embedded Library\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``CEF_FOUND``\n  True, if all required components and the core library were found.\n``CEF_VERSION``\n  Detected version of found CEF libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``CEF_LIBRARY_WRAPPER_RELEASE``\n  Path to the optimized wrapper component of CEF.\n``CEF_LIBRARY_WRAPPER_DEBUG``\n  Path to the debug wrapper component of CEF.\n``CEF_LIBRARY_RELEASE``\n  Path to the library component of CEF.\n``CEF_LIBRARY_DEBUG``\n  Path to the debug library component of CEF.\n``CEF_INCLUDE_DIR``\n  Directory containing ``cef_version.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nset(CEF_ROOT_DIR \"\" CACHE PATH \"Alternative path to Chromium Embedded Framework\")\n\nif(NOT DEFINED CEF_ROOT_DIR OR CEF_ROOT_DIR STREQUAL \"\")\n  message(\n    FATAL_ERROR\n    \"CEF_ROOT_DIR is not set - if ENABLE_BROWSER is enabled, \"\n    \"a CEF distribution with compiled wrapper library is required.\\n\"\n    \"Please download a CEF distribution for your appropriate architecture \"\n    \"and specify CEF_ROOT_DIR to its location\"\n  )\nendif()\n\nfind_path(\n  CEF_INCLUDE_DIR\n  \"cef_version.h\"\n  HINTS \"${CEF_ROOT_DIR}/include\"\n  DOC \"Chromium Embedded Framework include directory.\"\n)\n\nif(CEF_INCLUDE_DIR)\n  file(\n    STRINGS \"${CEF_INCLUDE_DIR}/cef_version.h\"\n    _VERSION_STRING\n    REGEX \"^.*CEF_VERSION_(MAJOR|MINOR|PATCH)[ \\t]+[0-9]+[ \\t]*$\"\n  )\n  string(REGEX REPLACE \".*CEF_VERSION_MAJOR[ \\t]+([0-9]+).*\" \"\\\\1\" VERSION_MAJOR \"${_VERSION_STRING}\")\n  string(REGEX REPLACE \".*CEF_VERSION_MINOR[ \\t]+([0-9]+).*\" \"\\\\1\" VERSION_MINOR \"${_VERSION_STRING}\")\n  string(REGEX REPLACE \".*CEF_VERSION_PATCH[ \\t]+([0-9]+).*\" \"\\\\1\" VERSION_PATCH \"${_VERSION_STRING}\")\n  set(CEF_VERSION \"${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}\")\nelse()\n  if(NOT CEF_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Chromium Embedded Framework version.\")\n  endif()\n  set(CEF_VERSION 0.0.0)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME STREQUAL Windows)\n  find_library(\n    CEF_IMPLIB_RELEASE\n    NAMES cef.lib libcef.lib\n    NO_DEFAULT_PATH\n    PATHS \"${CEF_ROOT_DIR}\" \"${CEF_ROOT_DIR}/Release\"\n    DOC \"Chromium Embedded Framework import library location\"\n  )\n\n  find_program(\n    CEF_LIBRARY_RELEASE\n    NAMES cef.dll libcef.dll\n    NO_DEFAULT_PATH\n    PATHS \"${CEF_ROOT_DIR}\" \"${CEF_ROOT_DIR}/Release\"\n    DOC \"Chromium Embedded Framework library location\"\n  )\n\n  if(NOT CEF_LIBRARY_RELEASE)\n    set(CEF_LIBRARY_RELEASE \"${CEF_IMPLIB_RELEASE}\")\n  endif()\n\n  find_library(\n    CEF_LIBRARY_WRAPPER_RELEASE\n    NAMES cef_dll_wrapper libcef_dll_wrapper\n    NO_DEFAULT_PATH\n    PATHS\n      \"${CEF_ROOT_DIR}/build/libcef_dll/Release\"\n      \"${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Release\"\n      \"${CEF_ROOT_DIR}/build/libcef_dll\"\n      \"${CEF_ROOT_DIR}/build/libcef_dll_wrapper\"\n    DOC \"Chromium Embedded Framework static library wrapper.\"\n  )\n\n  find_library(\n    CEF_LIBRARY_WRAPPER_DEBUG\n    NAMES cef_dll_wrapper libcef_dll_wrapper\n    NO_DEFAULT_PATH\n    PATHS \"${CEF_ROOT_DIR}/build/libcef_dll/Debug\" \"${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Debug\"\n    DOC \"Chromium Embedded Framework static library wrapper (debug).\"\n  )\nelseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin)\n  find_library(\n    CEF_LIBRARY_RELEASE\n    NAMES \"Chromium Embedded Framework\"\n    NO_DEFAULT_PATH\n    PATHS \"${CEF_ROOT_DIR}\" \"${CEF_ROOT_DIR}/Release\"\n    DOC \"Chromium Embedded Framework\"\n  )\n\n  find_library(\n    CEF_LIBRARY_WRAPPER_RELEASE\n    NAMES cef_dll_wrapper libcef_dll_wrapper\n    NO_DEFAULT_PATH\n    PATHS\n      \"${CEF_ROOT_DIR}/build/libcef_dll/Release\"\n      \"${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Release\"\n      \"${CEF_ROOT_DIR}/build/libcef_dll\"\n      \"${CEF_ROOT_DIR}/build/libcef_dll_wrapper\"\n    DOC \"Chromium Embedded Framework static library wrapper.\"\n  )\n\n  find_library(\n    CEF_LIBRARY_WRAPPER_DEBUG\n    NAMES cef_dll_wrapper libcef_dll_wrapper\n    NO_DEFAULT_PATH\n    PATHS \"${CEF_ROOT_DIR}/build/libcef_dll/Debug\" \"${CEF_ROOT_DIR}/build/libcef_dll_wrapper/Debug\"\n    DOC \"Chromium Embedded Framework static library wrapper (debug).\"\n  )\nelseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)\n  find_library(\n    CEF_LIBRARY_RELEASE\n    NAMES libcef.so\n    NO_DEFAULT_PATH\n    PATHS \"${CEF_ROOT_DIR}\" \"${CEF_ROOT_DIR}/Release\"\n    DOC \"Chromium Embedded Framework\"\n  )\n\n  find_library(\n    CEF_LIBRARY_WRAPPER_RELEASE\n    NAMES cef_dll_wrapper.a libcef_dll_wrapper.a\n    NO_DEFAULT_PATH\n    PATHS\n      \"${CEF_ROOT_DIR}/libcef_dll_wrapper\"\n      \"${CEF_ROOT_DIR}/build/libcef_dll\"\n      \"${CEF_ROOT_DIR}/build/libcef_dll_wrapper\"\n    DOC \"Chromium Embedded Framework static library wrapper.\"\n  )\nendif()\n\ninclude(SelectLibraryConfigurations)\nselect_library_configurations(CEF)\n\nfind_package_handle_standard_args(\n  CEF\n  REQUIRED_VARS CEF_LIBRARY_RELEASE CEF_LIBRARY_WRAPPER_RELEASE CEF_INCLUDE_DIR\n  VERSION_VAR CEF_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that location of pre-compiled Chromium Embedded Framework is set as CEF_ROOT_DIR.\"\n)\nmark_as_advanced(CEF_LIBRARY CEF_LIBRARY_WRAPPER_RELEASE CEF_LIBRARY_WRAPPER_DEBUG CEF_INCLUDE_DIR)\n\nif(NOT TARGET CEF::Wrapper)\n  if(IS_ABSOLUTE \"${CEF_LIBRARY_WRAPPER_RELEASE}\")\n    add_library(CEF::Wrapper STATIC IMPORTED)\n    set_property(TARGET CEF::Wrapper PROPERTY IMPORTED_LOCATION_RELEASE \"${CEF_LIBRARY_WRAPPER_RELEASE}\")\n  else()\n    add_library(CEF::Wrapper INTERFACE IMPORTED)\n    set_property(TARGET CEF::Wrapper PROPERTY IMPORTED_LIBNAME_RELEASE \"${CEF_LIBRARY_WRAPPER_RELEASE}\")\n  endif()\n  set_property(TARGET CEF::Wrapper APPEND PROPERTY IMPORTED_CONFIGURATIONS \"Release\")\n\n  if(CEF_LIBRARY_WRAPPER_DEBUG)\n    if(IS_ABSOLUTE \"${CEF_LIBRARY_WRAPPER_DEBUG}\")\n      set_property(TARGET CEF::Wrapper PROPERTY IMPORTED_LOCATION_DEBUG \"${CEF_LIBRARY_WRAPPER_DEBUG}\")\n    else()\n      set_property(TARGET CEF::Wrapper PROPERTY IMPORTED_LIBNAME_DEBUG \"${CEF_LIBRARY_WRAPPER_DEBUG}\")\n    endif()\n    set_property(TARGET CEF::Wrapper APPEND PROPERTY IMPORTED_CONFIGURATIONS \"Debug\")\n  endif()\n\n  set_property(TARGET CEF::Wrapper APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES \"${CEF_INCLUDE_DIR}\" \"${CEF_ROOT_DIR}\")\nendif()\n\nif(NOT TARGET CEF::Library)\n  if(IS_ABSOLUTE \"${CEF_LIBRARY_RELEASE}\")\n    if(DEFINED CEF_IMPLIB_RELEASE)\n      if(CEF_IMPLIB_RELEASE STREQUAL CEF_LIBRARY_RELEASE)\n        add_library(CEF::Library STATIC IMPORTED)\n      else()\n        add_library(CEF::Library SHARED IMPORTED)\n        set_property(TARGET CEF::Library PROPERTY IMPORTED_IMPLIB_RELEASE \"${CEF_IMPLIB_RELEASE}\")\n      endif()\n    else()\n      add_library(CEF::Library UNKNOWN IMPORTED)\n    endif()\n    set_property(TARGET CEF::Library PROPERTY IMPORTED_LOCATION_RELEASE \"${CEF_LIBRARY_RELEASE}\")\n  else()\n    add_library(CEF::Library INTERFACE IMPORTED)\n    set_property(TARGET CEF::Library PROPERTY IMPORTED_LIBNAME_RELEASE \"${CEF_LIBRARY_RELEASE}\")\n  endif()\n\n  set_property(TARGET CEF::Library APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES \"${CEF_INCLUDE_DIR}\" \"${CEF_ROOT_DIR}\")\n  set_property(TARGET CEF::Library PROPERTY IMPORTED_CONFIGURATIONS \"Release\")\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  CEF\n  PROPERTIES\n    URL \"https://bitbucket.org/chromiumembedded/cef/\"\n    DESCRIPTION\n      \"Chromium Embedded Framework (CEF). A simple framework for embedding Chromium-based browsers in other applications.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindDetours.cmake",
    "content": "#[=======================================================================[.rst\nFindDetours\n-----------\n\nFindModule for Detours and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Detours::Detours``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Detours_FOUND``\n  True, if all required components and the core library were found.\n``Detours_VERSION``\n  Detected version of found Detours libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Detours_LIBRARY``\n  Path to the library component of Detours.\n``Detours_INCLUDE_DIR``\n  Directory containing ``detours.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_check_modules(PC_Detours QUIET detours)\nendif()\n\nfind_path(Detours_INCLUDE_DIR NAMES detours.h HINTS ${PC_Detours_INCLUDE_DIRS} DOC \"Detours include directory\")\n\nfind_library(Detours_IMPLIB NAMES detours HINTS ${PC_Detours_LIBRARY_DIRS} DOC \"Detours location\")\n\ncmake_path(GET Detours_IMPLIB PARENT_PATH _implib_path)\ncmake_path(SET _bin_path NORMALIZE \"${_implib_path}/../bin\")\n\nfind_program(Detours_LIBRARY NAMES detours.dll HINTS ${_implib_path} ${_bin_path} DOC \"Detours DLL location\")\n\nif(NOT Detours_LIBRARY)\n  set(Detours_LIBRARY \"${Detours_IMPLIB}\")\nendif()\nunset(_implib_path)\nunset(_bin_path)\n\nif(PC_Detours_VERSION VERSION_GREATER 0)\n  set(Detours_VERSION ${PC_Detours_VERSION})\nelse()\n  if(NOT Detours_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find detours version.\")\n  endif()\n  set(Detours_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Detours\n  REQUIRED_VARS Detours_LIBRARY Detours_INCLUDE_DIR\n  VERSION_VAR Detours_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\"\n)\nmark_as_advanced(Detours_INCLUDE_DIR Detours_LIBRARY)\n\nif(Detours_FOUND)\n  if(NOT TARGET Detours::Detours)\n    if(IS_ABSOLUTE \"${Detours_LIBRARY}\")\n      if(DEFINED Detours_IMPLIB)\n        if(Detours_IMPLIB STREQUAL Detours_LIBRARY)\n          add_library(Detours::Detours STATIC IMPORTED)\n        else()\n          add_library(Detours::Detours SHARED IMPORTED)\n          set_property(TARGET Detours::Detours PROPERTY IMPORTED_IMPLIB \"${Detours_IMPLIB}\")\n        endif()\n      else()\n        add_library(Detours::Detours UNKNOWN IMPORTED)\n      endif()\n\n      set_property(TARGET Detours::Detours PROPERTY IMPORTED_LOCATION \"${Detours_LIBRARY}\")\n    else()\n      add_library(Detours::Detours INTERFACE IMPORTED)\n      set_property(TARGET Detours::Detours PROPERTY IMPORTED_LIBNAME \"${Detours_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Detours::Detours\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Detours_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Detours_INCLUDE_DIR}\"\n        VERSION ${Detours_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Detours\n  PROPERTIES\n    URL \"https://github.com/microsoft/detours\"\n    DESCRIPTION \"Detours is a software package for monitoring and instrumenting API calls on Windows.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindFFmpeg.cmake",
    "content": "#[=======================================================================[.rst\nFindFFmpeg\n----------\n\nFindModule for FFmpeg and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nComponents\n^^^^^^^^^^\n\n.. versionadded:: 1.0\n\nThis module contains provides several components:\n\n``avcodec``\n``avdevice``\n``avfilter``\n``avformat``\n``avutil``\n``postproc``\n``swscale``\n``swresample``\n\nImport targets exist for each component.\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` targets:\n\n``FFmpeg::avcodec``\n  AVcodec component\n\n``FFmpeg::avdevice``\n  AVdevice component\n\n``FFmpeg::avfilter``\n  AVfilter component\n\n``FFmpeg::avformat``\n  AVformat component\n\n``FFmpeg::avutil``\n  AVutil component\n\n``FFmpeg::postproc``\n  postproc component\n\n``FFmpeg::swscale``\n  SWscale component\n\n``FFmpeg::swresample``\n  SWresample component\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``FFmpeg_FOUND``\n  True, if all required components and the core library were found.\n``FFmpeg_VERSION``\n  Detected version of found FFmpeg libraries.\n``FFmpeg_INCLUDE_DIRS``\n  Include directories needed for FFmpeg.\n``FFmpeg_LIBRARIES``\n  Libraries needed to link to FFmpeg.\n``FFmpeg_DEFINITIONS``\n  Compiler flags required for FFmpeg.\n\n``FFmpeg_<COMPONENT>_VERSION``\n  Detected version of found FFmpeg component library.\n``FFmpeg_<COMPONENT>_INCLUDE_DIRS``\n  Include directories needed for FFmpeg component.\n``FFmpeg_<COMPONENT>_LIBRARIES``\n  Libraries needed to link to FFmpeg component.\n``FFmpeg_<COMPONENT>_DEFINITIONS``\n  Compiler flags required for FFmpeg component.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``FFmpeg_<COMPONENT>_LIBRARY``\n  Path to the library component of FFmpeg.\n``FFmpeg_<COMPONENT>_INCLUDE_DIR``\n  Directory containing ``<COMPONENT>.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nset(\n  _DEFAULT_COMPONENTS\n  avcodec\n  avdevice\n  avformat\n  avfilter\n  avresample\n  avutil\n  postproc\n  swscale\n  swresample\n)\n\nset(component_avcodec libavcodec avcodec avcodec.h)\nset(component_avdevice libavdevice avdevice avdevice.h)\nset(component_avformat libavformat avformat avformat.h)\nset(component_avfilter libavfilter avfilter avfilter.h)\nset(component_avresample libavresample avresample avresample.h)\nset(component_avutil libavutil avutil avutil.h)\nset(component_postproc libpostproc postproc postprocess.h)\nset(component_swscale libswscale swscale swscale.h)\nset(component_swresample libswresample swresample swresample.h)\n\nif(NOT FFmpeg_FIND_COMPONENTS)\n  set(FFmpeg_FIND_COMPONENTS ${_DEFAULT_COMPONENTS})\nendif()\n\n# FFmpeg_find_component: Find and set up requested FFmpeg component\nmacro(FFmpeg_find_component component)\n  list(GET component_${component} 0 component_libname)\n  list(GET component_${component} 1 component_name)\n  list(GET component_${component} 2 component_header)\n\n  if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES \"Windows\")\n    find_package(PkgConfig QUIET)\n    if(PKG_CONFIG_FOUND)\n      pkg_search_module(PC_FFmpeg_${component} QUIET ${component_libname})\n    endif()\n  endif()\n\n  find_path(\n    FFmpeg_${component}_INCLUDE_DIR\n    NAMES ${component_libname}/${component_header} ${component_libname}/version.h\n    HINTS ${PC_FFmpeg_${component}_INCLUDE_DIRS}\n    PATHS /usr/include /usr/local/include\n    DOC \"FFmpeg component ${component_name} include directory\"\n  )\n\n  ffmpeg_check_version()\n\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Windows\")\n    find_library(\n      FFmpeg_${component}_IMPLIB\n      NAMES ${component_libname} ${component_name}\n      DOC \"FFmpeg component ${component_name} import library location\"\n    )\n\n    ffmpeg_find_dll()\n  else()\n    find_library(\n      FFmpeg_${component}_LIBRARY\n      NAMES ${component_libname} ${component_name}\n      HINTS ${PC_FFmpeg_${component}_LIBRARY_DIRS}\n      PATHS /usr/lib /usr/local/lib\n      DOC \"FFmpeg component ${component_name} location\"\n    )\n  endif()\n\n  if(FFmpeg_${component}_LIBRARY AND FFmpeg_${component}_INCLUDE_DIR)\n    set(FFmpeg_${component}_FOUND TRUE)\n    set(FFmpeg_${component}_LIBRARIES ${${_library_var}})\n    set(FFmpeg_${component}_INCLUDE_DIRS ${FFmpeg_${component}_INCLUDE_DIR})\n    set(FFmpeg_${component}_DEFINITIONS ${PC_FFmpeg_${component}_CFLAGS_OTHER})\n    mark_as_advanced(FFmpeg_${component}_LIBRARY FFmpeg_${component}_INCLUDE_DIR FFmpeg_${component}_IMPLIB)\n  endif()\nendmacro()\n\n# FFmpeg_find_dll: Macro to find DLL for corresponding import library\nmacro(FFmpeg_find_dll)\n  cmake_path(GET FFmpeg_${component}_IMPLIB PARENT_PATH _implib_path)\n  cmake_path(SET _bin_path NORMALIZE \"${_implib_path}/../bin\")\n\n  string(REGEX REPLACE \"([0-9]+)\\\\.[0-9]+\\\\.[0-9]+\" \"\\\\1\" _dll_version \"${FFmpeg_${component}_VERSION}\")\n\n  find_program(\n    FFmpeg_${component}_LIBRARY\n    NAMES ${component_name}-${_dll_version}.dll\n    HINTS ${_implib_path} ${_bin_path}\n    DOC \"FFmpeg component ${component_name} DLL location\"\n  )\n\n  if(NOT FFmpeg_${component}_LIBRARY)\n    set(FFmpeg_${component}_LIBRARY \"${FFmpeg_${component}_IMPLIB}\")\n  endif()\n\n  unset(_implib_path)\n  unset(_bin_path)\n  unset(_dll_version)\nendmacro()\n\n# FFmpeg_check_version: Macro to help extract version number from FFmpeg headers\nmacro(FFmpeg_check_version)\n  if(PC_FFmpeg_${component}_VERSION)\n    set(FFmpeg_${component}_VERSION ${PC_FFmpeg_${component}_VERSION})\n  elseif(EXISTS \"${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version.h\")\n    if(EXISTS \"${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version_major.h\")\n      file(\n        STRINGS \"${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version_major.h\"\n        _version_string\n        REGEX \"^.*VERSION_MAJOR[ \\t]+[0-9]+[ \\t]*$\"\n      )\n      string(REGEX REPLACE \".*VERSION_MAJOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_major \"${_version_string}\")\n\n      file(\n        STRINGS \"${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version.h\"\n        _version_string\n        REGEX \"^.*VERSION_(MINOR|MICRO)[ \\t]+[0-9]+[ \\t]*$\"\n      )\n      string(REGEX REPLACE \".*VERSION_MINOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_minor \"${_version_string}\")\n      string(REGEX REPLACE \".*VERSION_MICRO[ \\t]+([0-9]+).*\" \"\\\\1\" _version_patch \"${_version_string}\")\n    else()\n      file(\n        STRINGS \"${FFmpeg_${component}_INCLUDE_DIR}/${component_libname}/version.h\"\n        _version_string\n        REGEX \"^.*VERSION_(MAJOR|MINOR|MICRO)[ \\t]+[0-9]+[ \\t]*$\"\n      )\n      string(REGEX REPLACE \".*VERSION_MAJOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_major \"${_version_string}\")\n      string(REGEX REPLACE \".*VERSION_MINOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_minor \"${_version_string}\")\n      string(REGEX REPLACE \".*VERSION_MICRO[ \\t]+([0-9]+).*\" \"\\\\1\" _version_patch \"${_version_string}\")\n    endif()\n\n    set(FFmpeg_${component}_VERSION \"${_version_major}.${_version_minor}.${_version_patch}\")\n    unset(_version_major)\n    unset(_version_minor)\n    unset(_version_patch)\n  else()\n    if(NOT FFmpeg_FIND_QUIETLY)\n      message(AUTHOR_WARNING \"Failed to find ${component_name} version.\")\n    endif()\n    set(FFmpeg_${component}_VERSION 0.0.0)\n  endif()\nendmacro()\n\n# FFmpeg_set_soname: Set SONAME property on imported library targets\nmacro(FFmpeg_set_soname)\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    execute_process(\n      COMMAND sh -c \"otool -D '${FFmpeg_${component}_LIBRARY}' | grep -v '${FFmpeg_${component}_LIBRARY}'\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0 AND _output MATCHES \"^@rpath/\")\n      set_property(TARGET FFmpeg::${component} PROPERTY IMPORTED_SONAME \"${_output}\")\n    endif()\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n    execute_process(\n      COMMAND sh -c \"objdump -p '${FFmpeg_${component}_LIBRARY}' | grep SONAME\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0)\n      string(REGEX REPLACE \"[ \\t]+SONAME[ \\t]+([^ \\t]+)\" \"\\\\1\" _soname \"${_output}\")\n      set_property(TARGET FFmpeg::${component} PROPERTY IMPORTED_SONAME \"${_soname}\")\n      unset(_soname)\n    endif()\n  endif()\n  unset(_output)\n  unset(_result)\nendmacro()\n\nforeach(component IN LISTS FFmpeg_FIND_COMPONENTS)\n  if(NOT component IN_LIST _DEFAULT_COMPONENTS)\n    message(FATAL_ERROR \"Unknown FFmpeg component specified: ${component}.\")\n  endif()\n\n  if(NOT FFmpeg_${component}_FOUND)\n    ffmpeg_find_component(${component})\n  endif()\n\n  if(FFmpeg_${component}_FOUND)\n    list(APPEND FFmpeg_LIBRARIES ${FFmpeg_${component}_LIBRARY})\n    list(APPEND FFmpeg_DEFINITIONS ${FFmpeg_${component}_DEFINITIONS})\n    list(APPEND FFmpeg_INCLUDE_DIRS ${FFmpeg_${component}_INCLUDE_DIR})\n  endif()\nendforeach()\n\nif(NOT FFmpeg_avutil_FOUND)\n  ffmpeg_find_component(avutil)\nendif()\n\nif(EXISTS \"${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h\")\n  file(\n    STRINGS \"${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h\"\n    _version_string\n    REGEX \"^.*FFMPEG_VERSION[ \\t]+\\\"n?[0-9a-z\\\\~+.-]+\\\"[ \\t]*$\"\n  )\n  string(REGEX REPLACE \".*FFMPEG_VERSION[ \\t]+\\\"n?([0-9]+\\\\.[0-9]).*\\\".*\" \"\\\\1\" FFmpeg_VERSION \"${_version_string}\")\nendif()\n\nlist(REMOVE_DUPLICATES FFmpeg_INCLUDE_DIRS)\nlist(REMOVE_DUPLICATES FFmpeg_LIBRARIES)\nlist(REMOVE_DUPLICATES FFmpeg_DEFINITIONS)\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(FFmpeg_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(FFmpeg_ERROR_REASON \"Ensure that required FFmpeg libraries are installed on the system.\")\nendif()\n\nfind_package_handle_standard_args(\n  FFmpeg\n  REQUIRED_VARS FFmpeg_LIBRARIES FFmpeg_INCLUDE_DIRS\n  VERSION_VAR FFmpeg_VERSION\n  HANDLE_COMPONENTS\n  REASON_FAILURE_MESSAGE \"${FFmpeg_ERROR_REASON}\"\n)\n\nif(FFmpeg_FOUND AND NOT TARGET FFmpeg::FFmpeg)\n  add_library(FFmpeg::FFmpeg INTERFACE IMPORTED)\nendif()\n\nforeach(component IN LISTS FFmpeg_FIND_COMPONENTS)\n  if(FFmpeg_${component}_FOUND AND NOT TARGET FFmpeg::${component})\n    if(IS_ABSOLUTE \"${FFmpeg_${component}_LIBRARY}\")\n      if(DEFINED FFmpeg_${component}_IMPLIB)\n        if(FFmpeg_${component}_IMPLIB STREQUAL FFmpeg_${component}_LIBRARY)\n          add_library(FFmpeg::${component} STATIC IMPORTED)\n        else()\n          add_library(FFmpeg::${component} SHARED IMPORTED)\n          set_property(TARGET FFmpeg::${component} PROPERTY IMPORTED_IMPLIB \"${FFmpeg_${component}_IMPLIB}\")\n        endif()\n      else()\n        add_library(FFmpeg::${component} UNKNOWN IMPORTED)\n        ffmpeg_set_soname()\n      endif()\n\n      set_property(TARGET FFmpeg::${component} PROPERTY IMPORTED_LOCATION \"${FFmpeg_${component}_LIBRARY}\")\n    else()\n      add_library(FFmpeg::${component} INTERFACE IMPORTED)\n      set_property(TARGET FFmpeg::${component} PROPERTY IMPORTED_LIBNAME \"${FFmpeg_${component}_LIBRARY}\")\n    endif()\n    set_target_properties(\n      FFmpeg::${component}\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_FFmpeg_${component}_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${FFmpeg_${component}_INCLUDE_DIR}\"\n        VERSION ${FFmpeg_${component}_VERSION}\n    )\n\n    get_target_property(_ffmpeg_interface_libraries FFmpeg::FFmpeg INTERFACE_LINK_LIBRARIES)\n    if(NOT FFmpeg::${component} IN_LIST _ffmpeg_interface_libraries)\n      set_property(TARGET FFmpeg::FFmpeg APPEND PROPERTY INTERFACE_LINK_LIBRARIES FFmpeg::${component})\n    endif()\n  endif()\nendforeach()\n\ninclude(FeatureSummary)\nset_package_properties(\n  FFmpeg\n  PROPERTIES\n    URL \"https://www.ffmpeg.org\"\n    DESCRIPTION \"A complete, cross-platform solution to record, convert and stream audio and video.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindFFnvcodec.cmake",
    "content": "#[=======================================================================[.rst\nFindFFnvcodec\n-------------\n\nFindModule for FFnvcodec and the associated headers\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 3.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``FFnvcodec::FFnvcodec``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``FFnvcodec_FOUND``\n  True, if headers was found.\n``FFnvcodec_VERSION``\n  Detected version of found FFnvcodec headers.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``FFnvcodec_INCLUDE_DIR``\n  Directory containing ``nvEncodeAPI.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_FFnvcodec QUIET ffnvcodec)\nendif()\n\nfind_path(\n  FFnvcodec_INCLUDE_DIR\n  NAMES ffnvcodec/nvEncodeAPI.h\n  HINTS ${PC_FFnvcodec_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"FFnvcodec include directory\"\n)\n\nif(PC_FFnvcodec_VERSION VERSION_GREATER 0)\n  set(FFnvcodec_VERSION ${PC_FFnvcodec_VERSION})\nelseif(EXISTS \"${FFnvcodec_INCLUDE_DIR}/ffnvcodec/nvEncodeAPI.h\")\n  file(\n    STRINGS \"${FFnvcodec_INCLUDE_DIR}/ffnvcodec/nvEncodeAPI.h\"\n    _version_string\n    REGEX \"^.*NVENCAPI_(MAJOR|MINOR)_VERSION[ \\t]+[0-9]+[ \\t]*$\"\n  )\n\n  string(REGEX REPLACE \".*MAJOR_VERSION[ \\t]+([0-9]+).*\" \"\\\\1\" _version_major \"${_version_string}\")\n  string(REGEX REPLACE \".*MINOR_VERSION[ \\t]+([0-9]+).*\" \"\\\\1\" _version_minor \"${_version_string}\")\n\n  set(FFnvcodec_VERSION \"${_version_major}.${_version_minor}\")\n  unset(_version_major)\n  unset(_version_minor)\nelse()\n  if(NOT FFnvcodec_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find FFnvcodec version.\")\n  endif()\n  set(FFnvcodec_VERSION 0.0.0)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(FFnvcodec_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(FFnvcodec_ERROR_REASON \"Ensure FFnvcodec headers are available in local include paths.\")\nendif()\n\nfind_package_handle_standard_args(\n  FFnvcodec\n  REQUIRED_VARS FFnvcodec_INCLUDE_DIR\n  VERSION_VAR FFnvcodec_VERSION\n  HANDLE_VERSION_RANGE\n  REASON_FAILURE_MESSAGE \"${FFnvcodec_ERROR_REASON}\"\n)\nmark_as_advanced(FFnvcodec_INCLUDE_DIR)\nunset(FFnvcodec_ERROR_REASON)\n\nif(FFnvcodec_FOUND)\n  if(NOT TARGET FFnvcodec::FFnvcodec)\n    add_library(FFnvcodec::FFnvcodec INTERFACE IMPORTED)\n    set_target_properties(FFnvcodec::FFnvcodec PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${FFnvcodec_INCLUDE_DIR}\")\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  FFnvcodec\n  PROPERTIES\n    URL \"https://github.com/FFmpeg/nv-codec-headers/\"\n    DESCRIPTION \"FFmpeg version of headers required to interface with NVIDIA's codec APIs.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindGio.cmake",
    "content": "#[=======================================================================[.rst\nFindGio\n-------\n\nFindModule for gio and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``gio::gio``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Gio_FOUND``\n  True, if all required components and the core library were found.\n``Gio_VERSION``\n  Detected version of found gio libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Gio_LIBRARY``\n  Path to the library component of gio.\n``Gio_INCLUDE_DIR``\n  Directory containing ``gio.h``\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Gio QUIET gio-2.0 gio)\n  pkg_search_module(PC_GioUnix QUIET gio-unix-2.0)\nendif()\n\nfind_path(\n  Gio_INCLUDE_DIR\n  NAMES gio/gio.h\n  HINTS ${PC_Gio_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  PATH_SUFFIXES glib-2.0\n  DOC \"gio-2.0 include directory\"\n)\n\nfind_path(\n  GioUnix_INCLUDE_DIR\n  NAMES gio/gunixfdmessage.h\n  HINTS ${PC_GioUnix_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  PATH_SUFFIXES gio-unix-2.0\n  DOC \"gio-unix-2.0 include directory\"\n)\n\nfind_library(\n  Gio_LIBRARY\n  NAMES libgio-2.0 gio-2.0 gio-unix-2.0\n  HINTS ${PC_Gio_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"gio-2.0 location\"\n)\n\nfind_path(\n  Glib_INCLUDE_DIR\n  NAMES glibconfig.h\n  HINTS ${PC_Gio_INCLUDE_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  PATH_SUFFIXES glib-2.0/include\n)\n\nif(PC_Gio_VERSION VERSION_GREATER 0)\n  set(Gio_VERSION ${PC_Gio_VERSION})\nelse()\n  if(NOT Gio_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find gio version.\")\n  endif()\n  set(Gio_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Gio\n  REQUIRED_VARS Gio_LIBRARY Gio_INCLUDE_DIR GioUnix_INCLUDE_DIR Glib_INCLUDE_DIR\n  VERSION_VAR Gio_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that glib is installed on the system.\"\n)\nmark_as_advanced(Gio_INCLUDE_DIR Gio_LIBRARY Glib_INCLUDE_DIR)\n\nif(Gio_FOUND)\n  if(NOT TARGET gio::gio)\n    if(IS_ABSOLUTE \"${Gio_LIBRARY}\")\n      add_library(gio::gio UNKNOWN IMPORTED)\n      set_property(TARGET gio::gio PROPERTY IMPORTED_LOCATION \"${Gio_LIBRARY}\")\n    else()\n      add_library(gio::gio INTERFACE IMPORTED)\n      set_property(TARGET gio::gio PROPERTY IMPORTED_LIBNAME \"${Gio_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      gio::gio\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Gio_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Gio_INCLUDE_DIR};${GioUnix_INCLUDE_DIR};${Glib_INCLUDE_DIR}\"\n        VERSION ${Gio_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Gio\n  PROPERTIES\n    URL \"https://docs.gtk.org/gio\"\n    DESCRIPTION\n      \"A library providing useful classes for general purpose I/O, networking, IPC, settings, and other high level application functionality.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindJack.cmake",
    "content": "#[=======================================================================[.rst\nFindJack\n--------\n\nFindModule for Jack and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Jack::Jack``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Jack_FOUND``\n  True, if all required components and the core library were found.\n``Jack_VERSION``\n  Detected version of found Jack libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Jack_LIBRARY``\n  Path to the library component of Jack.\n``Jack_INCLUDE_DIR``\n  Directory containing ``jack.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Jack QUIET jack)\nendif()\n\nfind_path(\n  Jack_INCLUDE_DIR\n  NAMES jack/jack.h\n  HINTS ${PC_Jack_INCLUDE_DIR}\n  PATHS /usr/include /usr/local/include\n  DOC \"Jack include directory\"\n)\n\nfind_library(Jack_LIBRARY NAMES jack HINTS ${PC_Jack_LIBRARY_DIRS} PATHS /usr/lib /usr/local/lib DOC \"Jack location\")\n\nif(PC_Jack_VERSION VERSION_GREATER 0)\n  set(Jack_VERSION ${PC_Jack_VERSION})\nelse()\n  if(NOT Jack_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Jack version.\")\n  endif()\n  set(Jack_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Jack\n  REQUIRED_VARS Jack_LIBRARY Jack_INCLUDE_DIR\n  VERSION_VAR Jack_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that Jack is installed on the system.\"\n)\nmark_as_advanced(Jack_INCLUDE_DIR Jack_LIBRARY)\n\nif(Jack_FOUND)\n  if(NOT TARGET Jack::Jack)\n    if(IS_ABSOLUTE \"${Jack_LIBRARY}\")\n      add_library(Jack::Jack UNKNOWN IMPORTED)\n      set_property(TARGET Jack::Jack PROPERTY IMPORTED_LOCATION \"${Jack_LIBRARY}\")\n    else()\n      add_library(Jack::Jack INTERFACE IMPORTED)\n      set_property(TARGET Jack::Jack PROPERTY IMPORTED_LIBNAME \"${Jack_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Jack::Jack\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Jack_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Jack_INCLUDE_DIR}\"\n        VERSION ${Jack_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Jack\n  PROPERTIES\n    URL \"https://www.jackaudio.org\"\n    DESCRIPTION\n      \"JACK Audio Connection Kit (or JACK) is a professional sound server API and pair of daemon implementations to provide real-time, low-latency connections for both audio and MIDI data between applications.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibAJANTV2.cmake",
    "content": "#[=======================================================================[.rst\nFindLibAJANTV2\n----------\n\nFindModule for LibAJANTV2 and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``LibAJANTV2::LibAJANTV2``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``LibAJANTV2_FOUND``\n  True, if all required components and the core library were found.\n``LibAJANTV2_VERSION``\n  Detected version of found LibAJANTV2 libraries.\n``LibAJANTV2_INCLUDE_DIRS``\n  Include directories needed for LibAJANTV2.\n``LibAJANTV2_LIBRARIES``\n  Libraries needed to link to LibAJANTV2.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``LibAJANTV2_LIBRARY_RELEASE``\n  Path to the library component of LibAJANTV2 in non-debug configuration.\n``LibAJANTV2_LIBRARY_DEBUG``\n  Optional path to the library component of LibAJANTV2 in debug configuration.\n``LibAJANTV2_INCLUDE_DIR``\n  Directory containing ``LibAJANTV2.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_LibAJANTV2 QUIET ajantv2)\nendif()\n\nfind_path(\n  _LIBAJANTV2_NEW_INCLUDE_DIR\n  NAMES libajantv2\n  HINTS ${PC_LibAJANTV2_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"LibAJANTV2 (new) include directory\"\n)\nif(${_LIBAJANTV2_NEW_INCLUDE_DIR} STREQUAL \"_LIBAJANTV2_NEW_INCLUDE_DIR-NOTFOUND\")\n  find_path(\n    _LIBAJANTV2_OLD_INCLUDE_DIR\n    NAMES ajalibraries\n    HINTS ${PC_LibAJANTV2_INCLUDE_DIRS}\n    PATHS /usr/include /usr/local/include\n    DOC \"LibAJANTV2 (old) include directory\"\n  )\n  if(NOT ${_LIBAJANTV2_OLD_INCLUDE_DIR} STREQUAL \"_LIBAJANTV2_OLD_INCLUDE_DIR-NOTFOUND\")\n    set(LibAJANTV2_INCLUDE_DIR ${_LIBAJANTV2_OLD_INCLUDE_DIR}/ajalibraries)\n    if(NOT LibAJANTV2_FIND_QUIETLY)\n      message(DEPRECATION \"aja: Using old ntv2 library\")\n    endif()\n  endif()\nelse()\n  set(LibAJANTV2_INCLUDE_DIR ${_LIBAJANTV2_NEW_INCLUDE_DIR}/libajantv2)\n  if(NOT LibAJANTV2_FIND_QUIETLY)\n    message(STATUS \"aja: Using new libajantv2 library\")\n  endif()\nendif()\n\nfind_library(\n  LibAJANTV2_LIBRARY_RELEASE\n  NAMES ajantv2 libajantv2\n  HINTS ${PC_LibAJANTV2_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"LibAJANTV2 location\"\n)\n\nfind_library(\n  LibAJANTV2_LIBRARY_DEBUG\n  NAMES ajantv2d libajantv2d\n  HINTS ${PC_LibAJANTV2_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"LibAJANTV2 debug location.\"\n)\n\nif(PC_LibAJANTV2_VERSION VERSION_GREATER 0)\n  set(LibAJANTV2_VERSION ${PC_LibAJANTV2_VERSION})\nelse()\n  if(NOT LibAJANTV2_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find LibAJANTV2 version.\")\n  endif()\n  set(LibAJANTV2_VERSION 0.0.0)\nendif()\n\ninclude(SelectLibraryConfigurations)\nselect_library_configurations(LibAJANTV2)\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(LibAJANTV2_ERROR_REASON \"Ensure obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(LibAJANTV2_ERROR_REASON \"Ensure ajantv2 static libraries are available in local library paths.\")\nendif()\n\nfind_package_handle_standard_args(\n  LibAJANTV2\n  REQUIRED_VARS LibAJANTV2_LIBRARY LibAJANTV2_INCLUDE_DIR\n  VERSION_VAR LibAJANTV2_VERSION\n  REASON_FAILURE_MESSAGE ${LibAJANTV2_ERROR_REASON}\n)\nmark_as_advanced(LibAJANTV2_LIBRARY LibAJANTV2_INCLUDE_DIR)\nunset(LibAJANTV2_ERROR_REASON)\n\nif(LibAJANTV2_FOUND)\n  list(\n    APPEND LibAJANTV2_INCLUDE_DIRS\n    ${LibAJANTV2_INCLUDE_DIR}\n    ${LibAJANTV2_INCLUDE_DIR}/ajaanc\n    ${LibAJANTV2_INCLUDE_DIR}/ajabase\n    ${LibAJANTV2_INCLUDE_DIR}/ajantv2\n    ${LibAJANTV2_INCLUDE_DIR}/ajantv2/includes\n  )\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Windows\")\n    list(APPEND LibAJANTV2_INCLUDE_DIRS ${LibAJANTV2_INCLUDE_DIR}/ajantv2/src/win)\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    list(APPEND LibAJANTV2_INCLUDE_DIRS ${LibAJANTV2_INCLUDE_DIR}/ajantv2/src/mac)\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux\")\n    list(APPEND LibAJANTV2_INCLUDE_DIRS ${LibAJANTV2_INCLUDE_DIR}/ajantv2/src/lin)\n  endif()\n\n  set(LibAJANTV2_LIBRARIES ${LibAJANTV2_LIBRARY})\n  mark_as_advanced(LibAJANTV2_INCLUDE_DIR LibAJANTV2_LIBRARY)\n\n  if(NOT TARGET AJA::LibAJANTV2)\n    if(IS_ABSOLUTE \"${LibAJANTV2_LIBRARY_RELEASE}\")\n      add_library(AJA::LibAJANTV2 STATIC IMPORTED)\n      set_property(TARGET AJA::LibAJANTV2 PROPERTY IMPORTED_LOCATION \"${LibAJANTV2_LIBRARY_RELEASE}\")\n    else()\n      add_library(AJA::LibAJANTV2 INTERFACE IMPORTED)\n      set_property(TARGET AJA::LibAJANTV2 PROPERTY IMPORTED_LIBNAME \"${LibAJANTV2_LIBRARY_RELEASE}\")\n    endif()\n\n    set_target_properties(\n      AJA::LibAJANTV2\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_LibAJANTV2_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${LibAJANTV2_INCLUDE_DIR}\"\n        VERSION ${LibAJANTV2_VERSION}\n        IMPORTED_CONFIGURATIONS Release\n    )\n\n    if(LibAJANTV2_LIBRARY_DEBUG)\n      if(IS_ABSOLUTE \"${LibAJANTV2_LIBRARY_DEBUG}\")\n        set_property(TARGET AJA::LibAJANTV2 PROPERTY IMPORTED_LOCATION_DEBUG \"${LibAJANTV2_LIBRARY_DEBUG}\")\n      else()\n        set_property(TARGET AJA::LibAJANTV2 PROPERTY IMPORTED_LIBNAME_DEBUG \"${LibAJANTV2_LIBRARY_DEBUG}\")\n      endif()\n      set_property(TARGET AJA::LibAJANTV2 APPEND PROPERTY IMPORTED_CONFIGURATIONS Debug)\n    endif()\n\n    set_target_properties(AJA::LibAJANTV2 PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${LibAJANTV2_INCLUDE_DIRS}\")\n    set_target_properties(AJA::LibAJANTV2 PROPERTIES INTERFACE_LINK_OPTIONS $<$<PLATFORM_ID:Windows>:/IGNORE:4099>)\n    set_property(\n      TARGET AJA::LibAJANTV2\n      APPEND\n      PROPERTY\n        INTERFACE_LINK_LIBRARIES\n          $<$<PLATFORM_ID:Windows>:netapi32.lib>\n          $<$<PLATFORM_ID:Windows>:setupapi.lib>\n          $<$<PLATFORM_ID:Windows>:shlwapi.lib>\n          $<$<PLATFORM_ID:Windows>:wbemuuid.lib>\n          $<$<PLATFORM_ID:Windows>:winmm.lib>\n          $<$<PLATFORM_ID:Windows>:ws2_32.lib>\n          \"$<$<PLATFORM_ID:Darwin>:$<LINK_LIBRARY:FRAMEWORK,AppKit.framework>>\"\n          \"$<$<PLATFORM_ID:Darwin>:$<LINK_LIBRARY:FRAMEWORK,CoreFoundation.framework>>\"\n          \"$<$<PLATFORM_ID:Darwin>:$<LINK_LIBRARY:FRAMEWORK,IOKit.framework>>\"\n    )\n    set_property(\n      TARGET AJA::LibAJANTV2\n      APPEND\n      PROPERTY\n        INTERFACE_COMPILE_DEFINITIONS\n          \"$<$<BOOL:${OS_WINDOWS}>:AJA_WINDOWS;_WINDOWS;WIN32;MSWindows>\"\n          \"$<$<AND:$<BOOL:${OS_WINDOWS}>,$<CONFIG:DEBUG>>:_DEBUG;_NDEBUG>\"\n          \"$<$<BOOL:${OS_MACOS}>:AJAMac;AJA_MAC>\"\n          \"$<$<BOOL:${OS_LINUX}>:AJA_LINUX;AJALinux>\"\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  LibAJANTV2\n  PROPERTIES\n    URL \"https://www.aja.com\"\n    DESCRIPTION\n      \"AJA NTV2 SDK - AJA simplifies professional digital video workflows with a line of award-winning products designed and manufactured in Grass Valley, CA.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibUUID.cmake",
    "content": "#[=======================================================================[.rst\nFindLibUUID\n-----------\n\nFindModule for LibUUID and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``LibUUID::LibUUID``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``LibUUID_FOUND``\n  True, if all required components and the core library were found.\n``LibUUID_VERSION``\n  Detected version of found LibUUID libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``LibUUID_LIBRARY``\n  Path to the library component of LibUUID.\n``LibUUID_INCLUDE_DIR``\n  Directory containing ``LibUUID.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_LibUUID QUIET LibUUID uuid)\nendif()\n\nfind_path(\n  LibUUID_INCLUDE_DIR\n  NAMES uuid/uuid.h\n  HINTS ${PC_LibUUID_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"LibUUID include directory\"\n)\n\nfind_library(\n  LibUUID_LIBRARY\n  NAMES uuid\n  HINTS ${PC_LibUUID_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"LibUUID location\"\n)\n\nif(PC_LibUUID_VERSION VERSION_GREATER 0)\n  set(LibUUID_VERSION ${PC_LibUUID_VERSION})\nelse()\n  if(NOT LibUUID_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find LibUUID version.\")\n  endif()\n  set(LibUUID_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  LibUUID\n  REQUIRED_VARS LibUUID_LIBRARY LibUUID_INCLUDE_DIR\n  VERSION_VAR LibUUID_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that e2fsprogs is installed on the system.\"\n)\nmark_as_advanced(LibUUID_INCLUDE_DIR LibUUID_LIBRARY)\n\nif(LibUUID_FOUND)\n  if(NOT TARGET LibUUID::LibUUID)\n    if(IS_ABSOLUTE \"${LibUUID_LIBRARY}\")\n      add_library(LibUUID::LibUUID UNKNOWN IMPORTED)\n      set_property(TARGET LibUUID::LibUUID PROPERTY IMPORTED_LOCATION \"${LibUUID_LIBRARY}\")\n    else()\n      add_library(LibUUID::LibUUID INTERFACE IMPORTED)\n      set_property(TARGET LibUUID::LibUUID PROPERTY IMPORTED_LIBNAME \"${LibUUID_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      LibUUID::LibUUID\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_LibUUID_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${LibUUID_INCLUDE_DIR}\"\n        VERSION ${LibUUID_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  LibUUID\n  PROPERTIES\n    URL \"http://e2fsprogs.sourceforge.net/\"\n    DESCRIPTION\n      \"The libuuid library is used to generate unique identifiers for objects that may be accessible beyond the local system.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibVLC.cmake",
    "content": "#[=======================================================================[.rst\nFindLibVLC\n----------\n\nFindModule for LibVLC and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``VLC::LibVLC``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``LibVLC_FOUND``\n  True, if all required components and the core library were found.\n``LibVLC_VERSION``\n  Detected version of found LibVLC libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``LibVLC_LIBRARY``\n  Path to the library component of LibVLC.\n``LibVLC_INCLUDE_DIR``\n  Directory containing ``libvlc.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_LibVLC QUIET libvlc)\nendif()\n\nfind_path(\n  LibVLC_INCLUDE_DIR\n  NAMES libvlc.h\n  HINTS ${PC_LibVLC_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  PATH_SUFFIXES vlc include/vlc include\n  DOC \"LibVLC include directory\"\n)\n\nfind_library(\n  LibVLC_LIBRARY\n  NAMES vlc libvlc\n  HINTS ${PC_LibVLC_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"LibVLC location\"\n)\n\nif(PC_LibVLC_VERSION VERSION_GREATER 0)\n  set(LibVLC_VERSION ${PC_LibVLC_VERSION})\nelseif(EXISTS \"${LibVLC_INCLUDE_DIR}/libvlc_version.h\")\n  file(\n    STRINGS \"${LibVLC_INCLUDE_DIR}/libvlc_version.h\"\n    _VERSION_STRING\n    REGEX \"^.*LIBVLC_VERSION_(MAJOR|MINOR|PATCH)[ \\t]+[0-9]+[ \\t]*$\"\n  )\n  string(REGEX REPLACE \".*LIBVLC_VERSION_MAJOR[ \\t]+([0-9]+).*\" \"\\\\1\" VERSION_MAJOR \"${_VERSION_STRING}\")\n  string(REGEX REPLACE \".*LIBVLC_VERSION_MINOR[ \\t]+([0-9]+).*\" \"\\\\1\" VERSION_MINOR \"${_VERSION_STRING}\")\n  string(REGEX REPLACE \".*LIBVLC_VERSION_REVISION[ \\t]+([0-9]+).*\" \"\\\\1\" VERSION_REVISION \"${_VERSION_STRING}\")\n  set(LibVLC_VERSION \"${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}\")\nelse()\n  if(NOT LibVLC_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find LibVLC version.\")\n  endif()\n  set(LibVLC_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  LibVLC\n  REQUIRED_VARS LibVLC_LIBRARY LibVLC_INCLUDE_DIR\n  VERSION_VAR LibVLC_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that libvlc-dev (vlc on BSD) is installed on the system.\"\n)\nmark_as_advanced(LibVLC_INCLUDE_DIR LibVLC_LIBRARY)\n\nif(LibVLC_FOUND)\n  if(NOT TARGET VLC::LibVLC)\n    if(IS_ABSOLUTE \"${LibVLC_LIBRARY}\")\n      add_library(VLC::LibVLC UNKNOWN IMPORTED)\n      set_property(TARGET VLC::LibVLC PROPERTY IMPORTED_LOCATION \"${LibVLC_LIBRARY}\")\n    else()\n      add_library(VLC::LibVLC INTERFACE IMPORTED)\n      set_property(TARGET VLC::LibVLC PROPERTY IMPORTED_LIBNAME \"${LibVLC_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      VLC::LibVLC\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_LibVLC_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${LibVLC_INCLUDE_DIR}\"\n        VERSION ${LibVLC_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  LibVLC\n  PROPERTIES\n    URL \"https://www.videolan.org/vlc/libvlc.html\"\n    DESCRIPTION\n      \"libVLC is the core engine and the interface to the multimedia framework on which VLC media player is based.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibdrm.cmake",
    "content": "#[=======================================================================[.rst\nFindLibdrm\n----------\n\nFindModule for Libdrm and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Libdrm::Libdrm``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Libdrm_FOUND``\n  True, if all required components and the core library were found.\n``Libdrm_VERSION``\n  Detected version of found Libdrm libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Libdrm_LIBRARY``\n  Path to the library component of Libdrm.\n``Libdrm_INCLUDE_DIR``\n  Directory containing ``drm_fourcc.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Libdrm QUIET libdrm)\nendif()\n\nfind_path(\n  Libdrm_INCLUDE_DIR\n  NAMES drm_fourcc.h\n  HINTS ${PC_Libdrm_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  PATH_SUFFIXES libdrm\n  DOC \"Libdrm include directory\"\n)\n\nfind_library(\n  Libdrm_LIBRARY\n  NAMES drm libdrm\n  HINTS ${PC_Libdrm_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Libdrm location\"\n)\n\nif(PC_Libdrm_VERSION VERSION_GREATER 0)\n  set(Libdrm_VERSION ${PC_Libdrm_VERSION})\nelse()\n  if(NOT Libdrm_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Libdrm version.\")\n  endif()\n  set(Libdrm_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Libdrm\n  REQUIRED_VARS Libdrm_LIBRARY Libdrm_INCLUDE_DIR\n  VERSION_VAR Libdrm_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that libdrm is installed on the system.\"\n)\nmark_as_advanced(Libdrm_INCLUDE_DIR Libdrm_LIBRARY)\n\nif(Libdrm_FOUND)\n  if(NOT TARGET Libdrm::Libdrm)\n    if(IS_ABSOLUTE \"${Libdrm_LIBRARY}\")\n      add_library(Libdrm::Libdrm UNKNOWN IMPORTED)\n      set_property(TARGET Libdrm::Libdrm PROPERTY IMPORTED_LOCATION \"${Libdrm_LIBRARY}\")\n    else()\n      add_library(Libdrm::Libdrm INTERFACE IMPORTED)\n      set_property(TARGET Libdrm::Libdrm PROPERTY IMPORTED_LIBNAME \"${Libdrm_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Libdrm::Libdrm\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Libdrm_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Libdrm_INCLUDE_DIR}\"\n        VERSION ${Libdrm_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Libdrm\n  PROPERTIES\n    URL \"https://gitlab.freedesktop.org/mesa/drm\"\n    DESCRIPTION\n      \"A low-level library, typically used by graphics drivers such as the Mesa drivers, the X drivers, libva and similar projects.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibfdk.cmake",
    "content": "#[=======================================================================[.rst\nFindLibfdk\n----------\n\nFindModule for Libfdk and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Libfdk::Libfdk``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Libfdk_FOUND``\n  True, if all required components and the core library were found.\n``Libfdk_VERSION``\n  Detected version of found Libfdk libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Libfdk_LIBRARY``\n  Path to the library component of Libfdk.\n``Libfdk_INCLUDE_DIR``\n  Directory containing ``fdk-aac/aacenc_lib.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Libfdk QUIET fdk-aac Libfdk-aac)\nendif()\n\nfind_path(\n  Libfdk_INCLUDE_DIR\n  NAMES fdk-aac/aacenc_lib.h\n  HINTS ${PC_Libfdk_INCLUDE_DIRS}\n  PATHS /usr/include/ /usr/local/include\n  DOC \"Libfdk include directory\"\n)\n\nfind_library(\n  Libfdk_LIBRARY\n  NAMES fdk-aac Libfdk-aac\n  HINTS ${PC_Libfdk_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Libfdk location\"\n)\n\nif(PC_Libfdk_VERSION VERSION_GREATER 0)\n  set(Libfdk_VERSION ${PC_Libfdk_VERSION})\nelse()\n  if(NOT Libfdk_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Libfdk version.\")\n  endif()\n  set(Libfdk_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Libfdk\n  REQUIRED_VARS Libfdk_LIBRARY Libfdk_INCLUDE_DIR\n  VERSION_VAR Libfdk_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that Libfdk is installed on the system.\"\n)\nmark_as_advanced(Libfdk_INCLUDE_DIR Libfdk_LIBRARY)\n\nif(Libfdk_FOUND)\n  if(NOT TARGET Libfdk::Libfdk)\n    if(IS_ABSOLUTE \"${Libfdk_LIBRARY}\")\n      add_library(Libfdk::Libfdk UNKNOWN IMPORTED)\n      set_property(TARGET Libfdk::Libfdk PROPERTY IMPORTED_LOCATION \"${Libfdk_LIBRARY}\")\n    else()\n      add_library(Libfdk::Libfdk INTERFACE IMPORTED)\n      set_property(TARGET Libfdk::Libfdk PROPERTY IMPORTED_LIBNAME \"${Libfdk_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Libfdk::Libfdk\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Libfdk_CFLAFGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Libfdk_INCLUDE_DIR}\"\n        VERSION ${Libfdk_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Libfdk\n  PROPERTIES\n    URL \"https://github.com/mstorsjo/fdk-aac\"\n    DESCRIPTION \"A standalone library of the Fraunhofer FDK AAC code from Android.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibpci.cmake",
    "content": "#[=======================================================================[.rst\nFindLibpci\n----------\n\nFindModule for Libpci and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Libpci::pci``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Libpci_FOUND``\n  True, if all required components and the core library were found.\n``Libpci_VERSION``\n  Detected version of found Libpci libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Libpci_LIBRARY``\n  Path to the library component of Libpci.\n``Libpci_INCLUDE_DIR``\n  Directory containing ``Libpci.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Libpci QUIET libpci)\nendif()\n\nfind_path(\n  Libpci_INCLUDE_DIR\n  NAMES pci.h\n  HINTS ${PC_Libpci_INCLUDE_DIRS}\n  PATHS /usr/include/ /usr/local/include\n  PATH_SUFFIXES pci\n  DOC \"Libpci include directory\"\n)\n\nfind_library(\n  Libpci_LIBRARY\n  NAMES libpci pci\n  HINTS ${PC_Libpci_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Libpci location\"\n)\n\nif(PC_Libpci_VERSION VERSION_GREATER 0)\n  set(Libpci_VERSION ${PC_Libpci_VERSION})\nelseif(EXISTS \"${Libpci_INCLUDE_DIR}/config.h\")\n  file(STRINGS \"${Libpci_INCLUDE_DIR}/config.h\" _VERSION_STRING REGEX \"^.*PCILIB_VERSION[ \\t]+\\\"[0-9\\\\.]+\\\"[ \\t]*$\")\n  string(REGEX REPLACE \".*PCILIB_VERSION[ \\t]+\\\"([0-9\\\\.]+)\\\".*\" \"\\\\1\" Libpci_VERSION \"${_VERSION_STRING}\")\nelse()\n  if(NOT Libpci_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Libpci version.\")\n  endif()\n  set(Libpci_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Libpci\n  REQUIRED_VARS Libpci_LIBRARY Libpci_INCLUDE_DIR\n  VERSION_VAR Libpci_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that libpci is installed on the system.\"\n)\nmark_as_advanced(Libpci_INCLUDE_DIR Libpci_LIBRARY)\n\nif(Libpci_FOUND)\n  if(NOT TARGET Libpci::pci)\n    if(IS_ABSOLUTE \"${Libpci_LIBRARY}\")\n      add_library(Libpci::pci UNKNOWN IMPORTED)\n      set_property(TARGET Libpci::pci PROPERTY IMPORTED_LOCATION \"${Libpci_LIBRARY}\")\n    else()\n      add_library(Libpci::pci INTERFACE IMPORTED)\n      set_property(TARGET Libpci::pci PROPERTY IMPORTED_LIBNAME \"${Libpci_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Libpci::pci\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Libpci_CFLAFGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Libpci_INCLUDE_DIR}\"\n        VERSION ${Libpci_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Libpci\n  PROPERTIES\n    URL \"https://mj.ucw.cz/sw/pciutils\"\n    DESCRIPTION \"Offers access to the PCI configuration space on a variety of operating systems.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibrist.cmake",
    "content": "#[=======================================================================[.rst\nFindLibrist\n----------\n\nFindModule for Librist and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Librist::Librist``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Librist_FOUND``\n  True, if all required components and the core library were found.\n``Librist_VERSION``\n  Detected version of found Librist libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Librist_LIBRARY``\n  Path to the library component of Librist.\n``Librist_INCLUDE_DIR``\n  Directory containing ``librist.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Librist QUIET librist)\nendif()\n\n# Librist_set_soname: Set SONAME on imported library target\nmacro(Librist_set_soname)\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    execute_process(\n      COMMAND sh -c \"otool -D '${Librist_LIBRARY}' | grep -v '${Librist_LIBRARY}'\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0 AND _output MATCHES \"^@rpath/\")\n      set_property(TARGET Librist::Librist PROPERTY IMPORTED_SONAME \"${_output}\")\n    endif()\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n    execute_process(\n      COMMAND sh -c \"objdump -p '${Librist_LIBRARY}' | grep SONAME\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0)\n      string(REGEX REPLACE \"[ \\t]+SONAME[ \\t]+([^ \\t]+)\" \"\\\\1\" _soname \"${_output}\")\n      set_property(TARGET Librist::Librist PROPERTY IMPORTED_SONAME \"${_soname}\")\n      unset(_soname)\n    endif()\n  endif()\n  unset(_output)\n  unset(_result)\nendmacro()\n\nfind_path(\n  Librist_INCLUDE_DIR\n  NAMES librist.h librist/librist.h\n  HINTS ${PC_Librist_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"Librist include directory\"\n)\n\nif(PC_Librist_VERSION VERSION_GREATER 0)\n  set(Librist_VERSION ${PC_Librist_VERSION})\nelseif(EXISTS \"${Librist_INCLUDE_DIR}/version.h\")\n  file(STRINGS \"${_VERSION_FILE}\" _VERSION_STRING REGEX \"^.*VERSION_(MAJOR|MINOR|PATCH)[ \\t]+[0-9]+[ \\t]*$\")\n  string(REGEX REPLACE \".*VERSION_MAJOR[ \\t]+([0-9]+).*\" \"\\\\1\" _VERSION_MAJOR \"${_VERSION_STRING}\")\n  string(REGEX REPLACE \".*VERSION_MINOR[ \\t]+([0-9]+).*\" \"\\\\1\" _VERSION_MINOR \"${_VERSION_STRING}\")\n  string(REGEX REPLACE \".*VERSION_PATCH[ \\t]+([0-9]+).*\" \"\\\\1\" _VERSION_PATCH \"${_VERSION_STRING}\")\n  set(Librist_VERSION \"${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO}\")\nelse()\n  if(NOT Librist_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Librist version.\")\n  endif()\n  set(Librist_VERSION 0.0.0)\nendif()\n\nfind_library(\n  Librist_LIBRARY\n  NAMES librist rist\n  HINTS ${PC_Librist_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Librist location\"\n)\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(Librist_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(Librist_ERROR_REASON \"Ensure librist libraries are available in local library paths.\")\nendif()\n\nfind_package_handle_standard_args(\n  Librist\n  REQUIRED_VARS Librist_LIBRARY Librist_INCLUDE_DIR\n  VERSION_VAR Librist_VERSION\n  REASON_FAILURE_MESSAGE \"${Librist_ERROR_REASON}\"\n)\nmark_as_advanced(Librist_INCLUDE_DIR Librist_LIBRARY)\nunset(Librist_ERROR_REASON)\n\nif(Librist_FOUND)\n  if(NOT TARGET Librist::Librist)\n    if(IS_ABSOLUTE \"${Librist_LIBRARY}\")\n      add_library(Librist::Librist UNKNOWN IMPORTED)\n      set_property(TARGET Librist::Librist PROPERTY IMPORTED_LOCATION \"${Librist_LIBRARY}\")\n    else()\n      add_library(Librist::Librist INTERFACE IMPORTED)\n      set_property(TARGET Librist::Librist PROPERTY IMPORTED_LIBNAME \"${Librist_LIBRARY}\")\n    endif()\n\n    librist_set_soname()\n    set_target_properties(\n      Librist::Librist\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Librist_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Librist_INCLUDE_DIR}\"\n        VERSION ${Librist_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Librist\n  PROPERTIES\n    URL \"https://code.videolan.org/rist/librist\"\n    DESCRIPTION \"A library that can be used to easily add the RIST protocol to your application.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibrnnoise.cmake",
    "content": "#[=======================================================================[.rst\nFindLibrnnoise\n----------\n\nFindModule for Librnnoise and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Librnnoise::Librnnoise``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Librnnoise_FOUND``\n  True, if all required components and the core library were found.\n``Librnnoise_VERSION``\n  Detected version of found Librnnoise libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Librnnoise_LIBRARY``\n  Path to the library component of Librnnoise.\n``Librnnoise_INCLUDE_DIR``\n  Directory containing ``rnnoise.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Librnnoise QUIET rnnoise)\nendif()\n\n# librrnoise_set_soname: Set SONAME on imported library target\nmacro(librnnoise_set_soname)\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    execute_process(\n      COMMAND sh -c \"otool -D '${Librnnoise_LIBRARY}' | grep -v '${Librnnoise_LIBRARY}'\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0 AND _output MATCHES \"^@rpath/\")\n      set_property(TARGET Librnnoise::Librnnoise PROPERTY IMPORTED_SONAME \"${_output}\")\n    endif()\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n    execute_process(\n      COMMAND sh -c \"objdump -p '${Librnnoise_LIBRARY}' | grep SONAME\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0)\n      string(REGEX REPLACE \"[ \\t]+SONAME[ \\t]+([^ \\t]+)\" \"\\\\1\" _soname \"${_output}\")\n      set_property(TARGET Librnnoise::Librnnoise PROPERTY IMPORTED_SONAME \"${_soname}\")\n      unset(_soname)\n    endif()\n  endif()\n  unset(_output)\n  unset(_result)\nendmacro()\n\nfind_path(\n  Librnnoise_INCLUDE_DIR\n  NAMES rnnoise.h\n  HINTS ${PC_Librnnoise_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"Librnnoise include directory\"\n)\n\nif(PC_Librnnoise_VERSION VERSION_GREATER 0)\n  set(Librnnoise_VERSION ${PC_Librnnoise_VERSION})\nelse()\n  if(NOT Librnnoise_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Librnnoise version.\")\n  endif()\n  set(Librnnoise_VERSION 0.0.0)\nendif()\n\nfind_library(\n  Librnnoise_LIBRARY\n  NAMES rnnoise librnnoise\n  HINTS ${PC_Librnnoise_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Librnnoise location\"\n)\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(Librnnoise_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(Librnnoise_ERROR_REASON \"Ensure librnnoise libraries are available in local libary paths.\")\nendif()\n\nfind_package_handle_standard_args(\n  Librnnoise\n  REQUIRED_VARS Librnnoise_LIBRARY Librnnoise_INCLUDE_DIR\n  VERSION_VAR Librnnoise_VERSION\n  REASON_FAILURE_MESSAGE \"${Librnnoise_ERROR_REASON}\"\n)\nmark_as_advanced(Librnnoise_INCLUDE_DIR Librnnoise_LIBRARY)\nunset(Librnnoise_ERROR_REASON)\n\nif(Librnnoise_FOUND)\n  if(NOT TARGET Librnnoise::Librnnoise)\n    if(IS_ABSOLUTE \"${Librnnoise_LIBRARY}\")\n      add_library(Librnnoise::Librnnoise UNKNOWN IMPORTED)\n      librnnoise_set_soname()\n      set_property(TARGET Librnnoise::Librnnoise PROPERTY IMPORTED_LOCATION \"${Librnnoise_LIBRARY}\")\n    else()\n      add_library(Librnnoise::Librnnoise INTERFACE IMPORTED)\n      set_property(TARGET Librnnoise::Librnnoise PROPERTY IMPORTED_LIBNAME \"${Librnnoise_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Librnnoise::Librnnoise\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Librnnoise_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Librnnoise_INCLUDE_DIR}\"\n        VERSION ${Librnnoise_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Librnnoise\n  PROPERTIES\n    URL \"https://gitlab.xiph.org/xiph/rnnoise\"\n    DESCRIPTION \"Recurrent neural network for audio noise reduction.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibspeexdsp.cmake",
    "content": "#[=======================================================================[.rst\nFindLibspeexdsp\n----------\n\nFindModule for Libspeexdsp and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``SpeexDSP::Libspeexdsp``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Libspeexdsp_FOUND``\n  True, if all required components and the core library were found.\n``Libspeexdsp_VERSION``\n  Detected version of found Libspeexdsp libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Libspeexdsp_LIBRARY``\n  Path to the library component of Libspeexdsp.\n``Libspeexdsp_INCLUDE_DIR``\n  Directory containing ``speex/speex_preprocess.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Libspeexdsp QUIET speexdsp)\nendif()\n\n# libspeexdsp_set_soname: Set SONAME on imported library target\nmacro(libspeexdsp_set_soname)\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    execute_process(\n      COMMAND sh -c \"otool -D '${Libspeexdsp_LIBRARY}' | grep -v '${Libspeexdsp_LIBRARY}'\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0 AND _output MATCHES \"^@rpath/\")\n      set_property(TARGET SpeexDSP::Libspeexdsp PROPERTY IMPORTED_SONAME \"${_output}\")\n    endif()\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n    execute_process(\n      COMMAND sh -c \"objdump -p '${Libspeexdsp_LIBRARY}' | grep SONAME\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0)\n      string(REGEX REPLACE \"[ \\t]+SONAME[ \\t]+([^ \\t]+)\" \"\\\\1\" _soname \"${_output}\")\n      set_property(TARGET SpeexDSP::Libspeexdsp PROPERTY IMPORTED_SONAME \"${_soname}\")\n      unset(_soname)\n    endif()\n  endif()\n  unset(_output)\n  unset(_result)\nendmacro()\n\nfind_path(\n  Libspeexdsp_INCLUDE_DIR\n  NAMES speex/speex_preprocess.h\n  HINTS ${PC_Libspeexdsp_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"Libspeexdsp include directory\"\n)\n\nif(PC_Libspeexdsp_VERSION VERSION_GREATER 0)\n  set(Libspeexdsp_VERSION ${PC_Libspeexdsp_VERSION})\nelse()\n  if(NOT Libspeexdsp_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Libspeexdsp version.\")\n  endif()\n  set(Libspeexdsp_VERSION 0.0.0)\nendif()\n\nfind_library(\n  Libspeexdsp_LIBRARY\n  NAMES speexdsp libspeexdsp\n  HINTS ${PC_Libspeexdsp_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Libspeexdsp location\"\n)\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(Libspeexdsp_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(Libspeexdsp_ERROR_REASON \"Ensure that libspeexdsp is installed on the system.\")\nendif()\n\nfind_package_handle_standard_args(\n  Libspeexdsp\n  REQUIRED_VARS Libspeexdsp_LIBRARY Libspeexdsp_INCLUDE_DIR\n  VERSION_VAR Libspeexdsp_VERSION\n  REASON_FAILURE_MESSAGE \"${Libspeexdsp_ERROR_REASON}\"\n)\nmark_as_advanced(Libspeexdsp_INCLUDE_DIR Libspeexdsp_LIBRARY)\nunset(Libspeexdsp_ERROR_REASON)\n\nif(Libspeexdsp_FOUND)\n  if(NOT TARGET SpeexDSP::Libspeexdsp)\n    if(IS_ABSOLUTE \"${Libspeexdsp_LIBRARY}\")\n      add_library(SpeexDSP::Libspeexdsp UNKNOWN IMPORTED)\n      set_property(TARGET SpeexDSP::Libspeexdsp PROPERTY IMPORTED_LOCATION \"${Libspeexdsp_LIBRARY}\")\n    else()\n      add_library(SpeexDSP::Libspeexdsp INTERFACE IMPORTED)\n      set_property(TARGET SpeexDSP::Libspeexdsp PROPERTY IMPORTED_LIBNAME \"${Libspeexdsp_LIBRARY}\")\n    endif()\n\n    libspeexdsp_set_soname()\n    set_target_properties(\n      SpeexDSP::Libspeexdsp\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Libspeexdsp_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Libspeexdsp_INCLUDE_DIR}\"\n        VERSION ${Libspeexdsp_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Libspeexdsp\n  PROPERTIES URL \"https://gitlab.xiph.org/xiph/speexdsp\" DESCRIPTION \"DSP library derived from speex.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibsrt.cmake",
    "content": "#[=======================================================================[.rst\nFindLibsrt\n----------\n\nFindModule for Libsrt and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Libsrt::Libsrt``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Libsrt_FOUND``\n  True, if all required components and the core library were found.\n``Libsrt_VERSION``\n  Detected version of found Libsrt libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Libsrt_LIBRARY``\n  Path to the library component of Libsrt.\n``Libsrt_INCLUDE_DIR``\n  Directory containing ``srt.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Libsrt QUIET libsrt)\nendif()\n\n# libsrt_set_soname: Set SONAME on imported library target\nmacro(libsrt_set_soname)\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    execute_process(\n      COMMAND sh -c \"otool -D '${Libsrt_LIBRARY}' | grep -v '${Libsrt_LIBRARY}'\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0 AND _output MATCHES \"^@rpath/\")\n      set_property(TARGET Libsrt::Libsrt PROPERTY IMPORTED_SONAME \"${_output}\")\n    endif()\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n    execute_process(\n      COMMAND sh -c \"objdump -p '${Libsrt_LIBRARY}' | grep SONAME\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0)\n      string(REGEX REPLACE \"[ \\t]+SONAME[ \\t]+([^ \\t]+)\" \"\\\\1\" _soname \"${_output}\")\n      set_property(TARGET Libsrt::Libsrt PROPERTY IMPORTED_SONAME \"${_soname}\")\n      unset(_soname)\n    endif()\n  endif()\n  unset(_output)\n  unset(_result)\nendmacro()\n\nfind_path(\n  Libsrt_INCLUDE_DIR\n  NAMES srt.h srt/srt.h\n  HINTS ${PC_Libsrt_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"Libsrt include directory\"\n)\n\nif(PC_Libsrt_VERSION VERSION_GREATER 0)\n  set(Libsrt_VERSION ${PC_Libsrt_VERSION})\nelseif(EXISTS \"${Libsrt_INCLUDE_DIR}/version.h\")\n  file(STRINGS \"${Libsrt_INCLUDE_DIR}/version.h\" _VERSION_STRING REGEX \"#define[ \\t]+SRT_VERSION_STRING[ \\t]+.+\")\n  string(REGEX REPLACE \".*#define[ \\t]+SRT_VERSION_STRING[ \\t]+\\\"(.+)\\\".*\" \"\\\\1\" Libsrt_VERSION \"${_VERSION_STRING}\")\nelse()\n  if(NOT Libsrt_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Libsrt version.\")\n  endif()\n  set(Libsrt_VERSION 0.0.0)\nendif()\n\nfind_library(\n  Libsrt_LIBRARY\n  NAMES srt libsrt\n  HINTS ${PC_Libsrt_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Libsrt location\"\n)\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(Libsrt_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(Libsrt_ERROR_REASON \"Ensure libsrt libraries are available in local library paths.\")\nendif()\n\nfind_package_handle_standard_args(\n  Libsrt\n  REQUIRED_VARS Libsrt_LIBRARY Libsrt_INCLUDE_DIR\n  VERSION_VAR Libsrt_VERSION\n  REASON_FAILURE_MESSAGE \"${Libsrt_ERROR_REASON}\"\n)\nmark_as_advanced(Libsrt_INCLUDE_DIR Libsrt_LIBRARY)\nunset(Libsrt_ERROR_REASON)\n\nif(Libsrt_FOUND)\n  if(NOT TARGET Libsrt::Libsrt)\n    if(IS_ABSOLUTE \"${Libsrt_LIBRARY}\")\n      add_library(Libsrt::Libsrt UNKNOWN IMPORTED)\n      libsrt_set_soname()\n      set_property(TARGET Libsrt::Libsrt PROPERTY IMPORTED_LOCATION \"${Libsrt_LIBRARY}\")\n    else()\n      add_library(Libsrt::Libsrt INTERFACE IMPORTED)\n      set_property(TARGET Libsrt::Libsrt PROPERTY IMPORTED_LIBNAME \"${Libsrt_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Libsrt::Libsrt\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Libsrt_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Libsrt_INCLUDE_DIR}\"\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Libsrt\n  PROPERTIES\n    URL \"https://www.srtalliance.org\"\n    DESCRIPTION\n      \"Secure Reliable Transport (SRT) is a transport protocol for ultra low (sub-second) latency live video and audio streaming, as well as for generic bulk data transfer.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibudev.cmake",
    "content": "#[=======================================================================[.rst\nFindLibudev\n-----------\n\nFindModule for Libudev and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Libudev::Libudev``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Libudev_FOUND``\n  True, if all required components and the core library were found.\n``Libudev_VERSION``\n  Detected version of found Libudev libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Libudev_LIBRARY``\n  Path to the library component of Libudev.\n``Libudev_INCLUDE_DIR``\n  Directory containing ``libudev.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Libudev QUIET libudev)\nendif()\n\nfind_path(\n  Libudev_INCLUDE_DIR\n  NAMES libudev.h\n  HINTS ${PC_Libudev_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"Libudev include directory\"\n)\n\nfind_library(\n  Libudev_LIBRARY\n  NAMES udev libudev\n  HINTS ${PC_Libudev_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Libudev location\"\n)\n\nif(PC_Libudev_VERSION VERSION_GREATER 0)\n  set(Libudev_VERSION ${PC_Libudev_VERSION})\nelse()\n  if(NOT Libudev_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Libudev version.\")\n  endif()\n  set(Libudev_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Libudev\n  REQUIRED_VARS Libudev_LIBRARY Libudev_INCLUDE_DIR\n  VERSION_VAR Libudev_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that Libudev is installed on the system.\"\n)\nmark_as_advanced(Libudev_INCLUDE_DIR Libudev_LIBRARY)\n\nif(Libudev_FOUND)\n  if(NOT TARGET Libudev::Libudev)\n    if(IS_ABSOLUTE \"${Libudev_LIBRARY}\")\n      add_library(Libudev::Libudev UNKNOWN IMPORTED)\n      set_property(TARGET Libudev::Libudev PROPERTY IMPORTED_LOCATION \"${Libudev_LIBRARY}\")\n    else()\n      add_library(Libudev::Libudev INTERFACE IMPORTED)\n      set_property(TARGET Libudev::Libudev PROPERTY IMPORTED_LIBNAME \"${Libudev_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Libudev::Libudev\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Libudev_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Libudev_INCLUDE_DIR}\"\n        VERSION ${Libudev_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Libudev\n  PROPERTIES\n    URL \"https://www.freedesktop.org/wiki/Software/systemd/\"\n    DESCRIPTION \"API for enumerating and introspecting local devices.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibv4l2.cmake",
    "content": "#[=======================================================================[.rst\nFindLibv4l2\n-----------\n\nFindModule for Libv4l2 and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Libv4l2::Libv4l2``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Libv4l2_FOUND``\n  True, if all required components and the core library were found.\n``Libv4l2_VERSION``\n  Detected version of found Libv4l2 libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Libv4l2_LIBRARY``\n  Path to the library component of Libv4l2.\n``Libv4l2_INCLUDE_DIR``\n  Directory containing ``libv4l2.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Libv4l2 QUIET libv4l2 v4l2 v4l-utils)\nendif()\n\nfind_path(\n  Libv4l2_INCLUDE_DIR\n  NAMES libv4l2.h\n  HINTS ${PC_Libv4l2_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"Libv4l2 include directory\"\n)\n\nfind_library(\n  Libv4l2_LIBRARY\n  NAMES v4l2\n  HINTS ${PC_Libv4l2_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Libv4l2 location\"\n)\n\nif(PC_Libv4l2_VERSION VERSION_GREATER 0)\n  set(Libv4l2_VERSION ${PC_Libv4l2_VERSION})\nelse()\n  if(NOT Libv4l2_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Libv4l2 version.\")\n  endif()\n  set(Libv4l2_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Libv4l2\n  REQUIRED_VARS Libv4l2_LIBRARY Libv4l2_INCLUDE_DIR\n  VERSION_VAR Libv4l2_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that v4l-utils is installed on the system.\"\n)\nmark_as_advanced(Libv4l2_INCLUDE_DIR Libv4l2_LIBRARY)\n\nif(Libv4l2_FOUND)\n  if(NOT TARGET Libv4l2::Libv4l2)\n    if(IS_ABSOLUTE \"${Libv4l2_LIBRARY}\")\n      add_library(Libv4l2::Libv4l2 UNKNOWN IMPORTED)\n      set_property(TARGET Libv4l2::Libv4l2 PROPERTY IMPORTED_LOCATION \"${Libv4l2_LIBRARY}\")\n    else()\n      add_library(Libv4l2::Libv4l2 INTERFACE IMPORTED)\n      set_property(TARGET Libv4l2::Libv4l2 PROPERTY IMPORTED_LIBNAME \"${Libv4l2_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Libv4l2::Libv4l2\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Libv4l2_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Libv4l2_INCLUDE_DIR}\"\n        VERSION ${Libv4l2_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Lib4l2\n  PROPERTIES\n    URL \"https://linuxtv.org/wiki/index.php/V4l-utils\"\n    DESCRIPTION \"The v4l-utils are a series of packages for handling media devices.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibva.cmake",
    "content": "#[=======================================================================[.rst\nFindLibva\n---------\n\nFindModule for Libva and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the following :prop_tgt:`IMPORTED` targets:\n\n``Libva::va``\n  Video Acceleration (VA) API for Linux -- runtime\n\n``Libva::drm``\n  Video Acceleration (VA) API for Linux -- DRM runtime\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Libva_FOUND``\n  True, if all required components and the core library were found.\n``Libva_VERSION``\n  Detected version of found Libva libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Libva_LIBRARY``\n  Path to the library component of Libva.\n``Libva_INCLUDE_DIR``\n  Directory containing ``libva.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Libva QUIET libva)\n  pkg_search_module(PC_LibvaDrm QUIET libva-drm)\nendif()\n\nfind_path(\n  Libva_INCLUDE_DIR\n  NAMES va.h\n  HINTS ${PC_Libva_INCLUDE_DIRS}\n  PATHS /usr/include/ /usr/local/include\n  PATH_SUFFIXES va\n  DOC \"Libva include directory\"\n)\n\nfind_library(\n  Libva_LIBRARY\n  NAMES libva va\n  HINTS ${PC_Libva_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Libva location\"\n)\n\nfind_library(\n  LibvaDrm_LIBRARY\n  NAMES libva-drm va-drm\n  HINTS ${PC_LibvaDrm_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Libva-drm location\"\n)\n\nif(PC_Libva_VERSION VERSION_GREATER 0)\n  set(Libva_VERSION ${PC_Libva_VERSION})\nelseif(EXISTS \"${Libva_INCLUDE_DIR}/va_version.h\")\n  file(\n    STRINGS \"${Libva_INCLUDE_DIR}/va_version.h\"\n    _VERSION_STRING\n    REGEX \"^.*(MAJOR|MINOR|MICRO)_VERSION[ \\t]+[0-9]+[ \\t]*$\"\n  )\n  string(REGEX REPLACE \".*MAJOR_VERSION[ \\t]+([0-9]+).*\" \"\\\\1\" VERSION_MAJOR \"${_VERSION_STRING}\")\n  string(REGEX REPLACE \".*MINOR_VERSION[ \\t]+([0-9]+).*\" \"\\\\1\" VERSION_MINOR \"${_VERSION_STRING}\")\n  string(REGEX REPLACE \".*MICRO_VERSION[ \\t]+([0-9]+).*\" \"\\\\1\" VERSION_MICRO \"${_VERSION_STRING}\")\n\n  set(Libva_VERSION \"${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO}\")\nelse()\n  if(NOT Libva_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Libva version.\")\n  endif()\n  set(Libva_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Libva\n  REQUIRED_VARS Libva_LIBRARY Libva_INCLUDE_DIR\n  VERSION_VAR Libva_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that libva is installed on the system.\"\n)\nmark_as_advanced(Libva_INCLUDE_DIR Libva_LIBRARY)\n\nif(Libva_FOUND)\n  if(NOT TARGET Libva::va)\n    if(IS_ABSOLUTE \"${Libva_LIBRARY}\")\n      add_library(Libva::va UNKNOWN IMPORTED)\n      set_property(TARGET Libva::va PROPERTY IMPORTED_LOCATION \"${Libva_LIBRARY}\")\n    else()\n      add_library(Libva::va INTERFACE IMPORTED)\n      set_property(TARGET Libva::va PROPERTY IMPORTED_LIBNAME \"${Libva_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Libva::va\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Libva_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Libva_INCLUDE_DIR}\"\n        VERSION ${Libva_VERSION}\n    )\n  endif()\n\n  if(LibvaDrm_LIBRARY)\n    if(NOT TARGET Libva::drm)\n      if(IS_ABSOLUTE \"${LibvaDrm_LIBRARY}\")\n        add_library(Libva::drm UNKNOWN IMPORTED)\n        set_property(TARGET Libva::drm PROPERTY IMPORTED_LOCATION \"${LibvaDrm_LIBRARY}\")\n      else()\n        add_library(Libva::drm INTERFACE IMPORTED)\n        set_property(TARGET Libva::drm PROPERTY IMPORTED_LIBNAME \"${LibvaDrm_LIBRARY}\")\n      endif()\n\n      set_target_properties(\n        Libva::drm\n        PROPERTIES\n          INTERFACE_COMPILE_OPTIONS \"${PC_LibvaDrm_CFLAGS_OTHER}\"\n          INTERFACE_INCLUDE_DIRECTORIES \"${Libva_INCLUDE_DIR}\"\n          VERSION ${Libva_VERSION}\n      )\n    endif()\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Libva\n  PROPERTIES\n    URL \"https://01.org/intel-media-for-linux\"\n    DESCRIPTION\n      \"An implementation for VA-API (Video Acceleration API) - an open-source library which provides access to graphics hardware acceleration capabilities.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLibx264.cmake",
    "content": "#[=======================================================================[.rst\nFindLibx264\n----------\n\nFindModule for Libx264 and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Libx264::Libx264``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Libx264_FOUND``\n  True, if all required components and the core library were found.\n``Libx264_VERSION``\n  Detected version of found Libx264 libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Libx264_LIBRARY``\n  Path to the library component of Libx264.\n``Libx264_INCLUDE_DIR``\n  Directory containing ``x264.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Libx264 QUIET x264)\nendif()\n\n# Libx264_set_soname: Set SONAME on imported library target\nmacro(Libx264_set_soname)\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    execute_process(\n      COMMAND sh -c \"otool -D '${Libx264_LIBRARY}' | grep -v '${Libx264_LIBRARY}'\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0 AND _output MATCHES \"^@rpath/\")\n      set_property(TARGET Libx264::Libx264 PROPERTY IMPORTED_SONAME \"${_output}\")\n    endif()\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n    execute_process(\n      COMMAND sh -c \"objdump -p '${Libx264_LIBRARY}' | grep SONAME\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0)\n      string(REGEX REPLACE \"[ \\t]+SONAME[ \\t]+([^ \\t]+)\" \"\\\\1\" _soname \"${_output}\")\n      set_property(TARGET Libx264::Libx264 PROPERTY IMPORTED_SONAME \"${_soname}\")\n      unset(_soname)\n    endif()\n  endif()\n  unset(_output)\n  unset(_result)\nendmacro()\n\n# Libx264_find_dll: Find DLL for corresponding import library\nmacro(Libx264_find_dll)\n  cmake_path(GET Libx264_IMPLIB PARENT_PATH _implib_path)\n  cmake_path(SET _bin_path NORMALIZE \"${_implib_path}/../bin\")\n\n  string(REGEX REPLACE \"[0-9]+\\\\.([0-9]+)\\\\.[0-9]+.*\" \"\\\\1\" _dll_version \"${Libx264_VERSION}\")\n\n  find_program(\n    Libx264_LIBRARY\n    NAMES libx264-${_dll_version}.dll x264-${_dll_version}.dll libx264.dll x264.dll\n    HINTS ${_implib_path} ${_bin_path}\n    DOC \"Libx264 DLL location\"\n  )\n\n  if(NOT Libx264_LIBRARY)\n    set(Libx264_LIBRARY \"${Libx264_IMPLIB}\")\n  endif()\n  unset(_implib_path)\n  unset(_bin_path)\n  unset(_dll_version)\nendmacro()\n\nfind_path(\n  Libx264_INCLUDE_DIR\n  NAMES x264.h\n  HINTS ${PC_Libx264_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"Libx264 include directory\"\n)\n\nif(PC_Libx264_VERSION VERSION_GREATER 0)\n  set(Libx264_VERSION ${PC_Libx264_VERSION})\nelseif(EXISTS \"${Libx264_INCLUDE_DIR}/x264_config.h\")\n  file(STRINGS \"${Libx264_INCLUDE_DIR}/x264_config.h\" _VERSION_STRING REGEX \"#define[ \\t]+X264_POINTVER[ \\t]+.+\")\n  string(REGEX REPLACE \".*#define[ \\t]+X264_POINTVER[ \\t]+\\\"([^ \\t]+).*\\\".*\" \"\\\\1\" Libx264_VERSION \"${_VERSION_STRING}\")\nelse()\n  if(NOT Libx264_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Libx264 version.\")\n  endif()\n  set(Libx264_VERSION 0.0.0)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Windows\")\n  find_library(Libx264_IMPLIB NAMES x264 libx264 DOC \"Libx264 import library location\")\n\n  libx264_find_dll()\nelse()\n  find_library(\n    Libx264_LIBRARY\n    NAMES x264 libx264\n    HINTS ${PC_Libx264_LIBRARY_DIRS}\n    PATHS /usr/lib /usr/local/lib\n    DOC \"Libx264 location\"\n  )\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(Libx264_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(Libx264_ERROR_REASON \"Ensure that x264 is installed on the system.\")\nendif()\n\nfind_package_handle_standard_args(\n  Libx264\n  REQUIRED_VARS Libx264_LIBRARY Libx264_INCLUDE_DIR\n  VERSION_VAR Libx264_VERSION\n  REASON_FAILURE_MESSAGE \"${Libx264_ERROR_REASON}\"\n)\nmark_as_advanced(Libx264_INCLUDE_DIR Libx264_LIBRARY Libx264_IMPLIB)\nunset(Libx264_ERROR_REASON)\n\nif(Libx264_FOUND)\n  if(NOT TARGET Libx264::Libx264)\n    if(IS_ABSOLUTE \"${Libx264_LIBRARY}\")\n      if(DEFINED Libx264_IMPLIB)\n        if(Libx264_IMPLIB STREQUAL Libx264_LIBRARY)\n          add_library(Libx264::Libx264 STATIC IMPORTED)\n        else()\n          add_library(Libx264::Libx264 SHARED IMPORTED)\n          set_property(TARGET Libx264::Libx264 PROPERTY IMPORTED_IMPLIB \"${Libx264_IMPLIB}\")\n        endif()\n      else()\n        add_library(Libx264::Libx264 UNKNOWN IMPORTED)\n      endif()\n      set_property(TARGET Libx264::Libx264 PROPERTY IMPORTED_LOCATION \"${Libx264_LIBRARY}\")\n    else()\n      add_library(Libx264::Libx264 INTERFACE IMPORTED)\n      set_property(TARGET Libx264::Libx264 PROPERTY IMPORTED_LIBNAME \"${Libx264_LIBRARY}\")\n    endif()\n\n    libx264_set_soname()\n    set_target_properties(\n      Libx264::Libx264\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Libx264_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Libx264_INCLUDE_DIR}\"\n        VERSION ${Libx264_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Libx264\n  PROPERTIES\n    URL \"https://www.videolan.org/developers/x264.html\"\n    DESCRIPTION\n      \"x264 is a free software library and application for encoding video streams into the H.264/MPEG-4 AVC compression format.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindLuajit.cmake",
    "content": "#[=======================================================================[.rst\nFindLuajit\n----------\n\nFindModule for Luajit and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Luajit::Luajit``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Luajit_FOUND``\n  True, if all required components and the core library were found.\n``Luajit_VERSION``\n  Detected version of found Luajit libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Luajit_LIBRARY``\n  Path to the library component of Luajit.\n``Luajit_INCLUDE_DIR``\n  Directory containing ``luajit.h`` or ``lua.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Luajit QUIET luajit)\nendif()\n\n# Luajit_set_soname: Set SONAME on imported library target\nmacro(Luajit_set_soname)\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    execute_process(\n      COMMAND sh -c \"otool -D '${Luajit_LIBRARY}' | grep -v '${Luajit_LIBRARY}'\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0 AND _output MATCHES \"^@rpath/\")\n      set_property(TARGET Luajit::Luajit PROPERTY IMPORTED_SONAME \"${_output}\")\n    endif()\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n    execute_process(\n      COMMAND sh -c \"objdump -p '${Luajit_LIBRARY}' | grep SONAME\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0)\n      string(REGEX REPLACE \"[ \\t]+SONAME[ \\t]+([^ \\t]+)\" \"\\\\1\" _soname \"${_output}\")\n      set_property(TARGET Luajit::Luajit PROPERTY IMPORTED_SONAME \"${_soname}\")\n      unset(_soname)\n    endif()\n  endif()\n  unset(_output)\n  unset(_result)\nendmacro()\n\nfind_path(\n  Luajit_INCLUDE_DIR\n  NAMES lua.h luajit.h\n  HINTS ${PC_Luajit_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  PATH_SUFFIXES luajit-2.1 luajit-2.0 luajit\n  DOC \"Luajit include directory\"\n)\n\nif(PC_Luajit_VERSION VERSION_GREATER 0)\n  set(Luajit_VERSION ${PC_Luajit_VERSION})\nelseif(EXISTS \"${Luajit_INCLUDE_DIR}/luajit.h\")\n  file(STRINGS \"${Luajit_INCLUDE_DIR}/luajit.h\" _VERSION_STRING REGEX \"#define[ \\t]+LUAJIT_VERSION[ \\t]+\\\".+\\\".*\")\n  string(REGEX REPLACE \".*#define[ \\t]+LUAJIT_VERSION[ \\t]+\\\"LuaJIT (.+)\\\".*\" \"\\\\1\" Luajit_VERSION \"${_VERSION_STRING}\")\nelse()\n  if(NOT Luajit_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Luajit version.\")\n  endif()\nendif()\n\nfind_library(\n  Luajit_LIBRARY\n  NAMES luajit-5.1 luajit-51 luajit lua51\n  HINTS ${PC_Luajit_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Luajit location\"\n)\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(Luajit_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(Luajit_ERROR_REASON \"Ensure that LuaJIT is installed on the system.\")\nendif()\n\nfind_package_handle_standard_args(\n  Luajit\n  REQUIRED_VARS Luajit_LIBRARY Luajit_INCLUDE_DIR\n  VERSION_VAR Luajit_VERSION\n  REASON_FAILURE_MESSAGE \"${Luajit_ERROR_REASON}\"\n)\nmark_as_advanced(Luajit_INCLUDE_DIR Luajit_LIBRARY)\nunset(Luajit_ERROR_REASON)\n\nif(Luajit_FOUND)\n  if(NOT TARGET Luajit::Luajit)\n    if(IS_ABSOLUTE \"${Luajit_LIBRARY}\")\n      add_library(Luajit::Luajit UNKNOWN IMPORTED)\n      set_property(TARGET Luajit::Luajit PROPERTY IMPORTED_LOCATION \"${Luajit_LIBRARY}\")\n    else()\n      add_library(Luajit::Luajit INTERFACE IMPORTED)\n      set_property(TARGET Luajit::Luajit PROPERTY IMPORTED_LIBNAME \"${Luajit_LIBRARY}\")\n    endif()\n\n    luajit_set_soname()\n    set_target_properties(\n      Luajit::Luajit\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Luajit_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Luajit_INCLUDE_DIR}\"\n        VERSION ${Luajit_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Luajit\n  PROPERTIES\n    URL \"https://luajit.org/luajit.html\"\n    DESCRIPTION \"LuaJIT is a Just-In-Time Compiler (JIT) for the Lua programming language.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindMbedTLS.cmake",
    "content": "#[=======================================================================[.rst\nFindMbedTLS\n-----------\n\nFindModule for MbedTLS and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nComponents\n^^^^^^^^^^\n\n.. versionadded:: 1.0\n\nThis module contains provides several components:\n\n``mbedcrypto``\n``mbedtls``\n``mbedx509``\n\nImport targets exist for each component.\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` targets:\n\n``MbedTLS::mbedcrypto``\n  Crypto component\n\n``MbedTLS::mbedtls``\n  TLS component\n\n``MbedTLS::mbedX509``\n  X509 component\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``MbedTLS_FOUND``\n  True, if all required components and the core library were found.\n``MbedTLS_VERSION``\n  Detected version of found MbedTLS libraries.\n\n``MbedTLS_<COMPONENT>_VERSION``\n  Detected version of found MbedTLS component library.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``MbedTLS_<COMPONENT>_LIBRARY``\n  Path to the library component of MbedTLS.\n``MbedTLS_<COMPONENT>_INCLUDE_DIR``\n  Directory containing ``<COMPONENT>.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_check_modules(PC_MbedTLS QUIET mbedtls mbedcrypto mbedx509)\nendif()\n\n# MbedTLS_set_soname: Set SONAME on imported library targets\nmacro(MbedTLS_set_soname component)\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    execute_process(\n      COMMAND sh -c \"otool -D '${Mbed${component}_LIBRARY}' | grep -v '${Mbed${component}_LIBRARY}'\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0 AND _output MATCHES \"^@rpath/\")\n      set_property(TARGET MbedTLS::mbed${component} PROPERTY IMPORTED_SONAME \"${_output}\")\n    endif()\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n    execute_process(\n      COMMAND sh -c \"objdump -p '${Mbed${component}_LIBRARY}' | grep SONAME\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0)\n      string(REGEX REPLACE \"[ \\t]+SONAME[ \\t]+([^ \\t]+)\" \"\\\\1\" _soname \"${_output}\")\n      set_property(TARGET MbedTLS::mbed${component} PROPERTY IMPORTED_SONAME \"${_soname}\")\n      unset(_soname)\n    endif()\n  endif()\n  unset(_output)\n  unset(_result)\nendmacro()\n\nfind_path(\n  MbedTLS_INCLUDE_DIR\n  NAMES mbedtls/ssl.h\n  HINTS \"${PC_MbedTLS_INCLUDE_DIRS}\"\n  PATHS /usr/include /usr/local/include\n  DOC \"MbedTLS include directory\"\n)\n\nif(PC_MbedTLS_VERSION VERSION_GREATER 0)\n  set(MbedTLS_VERSION ${PC_MbedTLS_VERSION})\nelseif(EXISTS \"${MbedTLS_INCLUDE_DIR}/mbedtls/build_info.h\")\n  file(\n    STRINGS \"${MbedTLS_INCLUDE_DIR}/mbedtls/build_info.h\"\n    _VERSION_STRING\n    REGEX \"#define[ \\t]+MBEDTLS_VERSION_STRING[ \\t]+.+\"\n  )\n  string(\n    REGEX REPLACE\n    \".*#define[ \\t]+MBEDTLS_VERSION_STRING[ \\t]+\\\"(.+)\\\".*\"\n    \"\\\\1\"\n    MbedTLS_VERSION\n    \"${_VERSION_STRING}\"\n  )\nelseif(EXISTS \"${MbedTLS_INCLUDE_DIR}/mbedtls/version.h\")\n  file(\n    STRINGS \"${MbedTLS_INCLUDE_DIR}/mbedtls/version.h\"\n    _VERSION_STRING\n    REGEX \"#define[ \\t]+MBEDTLS_VERSION_STRING[ \\t]+.+\"\n  )\n  string(\n    REGEX REPLACE\n    \".*#define[ \\t]+MBEDTLS_VERSION_STRING[ \\t]+\\\"(.+)\\\".*\"\n    \"\\\\1\"\n    MbedTLS_VERSION\n    \"${_VERSION_STRING}\"\n  )\nelse()\n  if(NOT MbedTLS_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find MbedTLS version.\")\n  endif()\n  set(MbedTLS_VERSION 0.0.0)\nendif()\n\nif(MbedTLS_VERSION VERSION_GREATER_EQUAL 3.6.0)\n  message(\n    DEPRECATION\n    \"Use of the custom CMake find module for MbedTLS versions >= 3.6.0 is not supported - build errors might occur!\"\n  )\nendif()\n\nfind_library(\n  Mbedtls_LIBRARY\n  NAMES libmbedtls mbedtls\n  HINTS \"${PC_MbedTLS_LIBRARY_DIRS}\"\n  PATHS /usr/lib /usr/local/lib\n  DOC \"MbedTLS location\"\n)\n\nfind_library(\n  Mbedcrypto_LIBRARY\n  NAMES libmbedcrypto mbedcrypto\n  HINTS \"${PC_MbedTLS_LIBRARY_DIRS}\"\n  PATHS /usr/lib /usr/local/lib\n  DOC \"MbedCrypto location\"\n)\n\nfind_library(\n  Mbedx509_LIBRARY\n  NAMES libmbedx509 mbedx509\n  HINTS \"${PC_MbedTLS_LIBRARY_DIRS}\"\n  PATHS /usr/lib /usr/local/lib\n  DOC \"MbedX509 location\"\n)\n\nif(Mbedtls_LIBRARY AND NOT Mbedcrypto_LIBRARY AND NOT Mbedx509_LIBRARY)\n  set(CMAKE_REQUIRED_LIBRARIES \"${MbedTLS_LIBRARY}\")\n  set(CMAKE_REQUIRED_INCLUDES \"${MbedTLS_INCLUDE_DIR}\")\n\n  check_symbol_exists(mbedtls_x509_crt_init \"mbedtls/x590_crt.h\" MbedTLS_INCLUDES_X509)\n  check_symbol_exists(mbedtls_sha256_init \"mbedtls/sha256.h\" MbedTLS_INCLUDES_CRYPTO)\n  unset(CMAKE_REQUIRED_LIBRARIES)\n  unset(CMAKE_REQUIRED_INCLUDES)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(MbedTLS_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(MbedTLS_ERROR_REASON \"Ensure that MbedTLS is installed on the system.\")\nendif()\n\nif(MbedTLS_INCLUDES_X509 AND MbedTLS_INCLUDES_CRYPTO)\n  find_package_handle_standard_args(\n    MbedTLS\n    REQUIRED_VARS Mbedtls_LIBRARY MbedTLS_INCLUDE_DIR\n    VERSION_VAR MbedTLS_VERSION\n    REASON_FAILURE_MESSAGE \"${MbedTLS_ERROR_REASON}\"\n  )\n  mark_as_advanced(Mbedtls_LIBRARY MbedTLS_INCLUDE_DIR)\n  list(APPEND _COMPONENTS tls)\nelse()\n  find_package_handle_standard_args(\n    MbedTLS\n    REQUIRED_VARS Mbedtls_LIBRARY Mbedcrypto_LIBRARY Mbedx509_LIBRARY MbedTLS_INCLUDE_DIR\n    VERSION_VAR MbedTLS_VERSION\n    REASON_FAILURE_MESSAGE \"${MbedTLS_ERROR_REASON}\"\n  )\n  mark_as_advanced(Mbedtls_LIBRARY Mbedcrypto_LIBRARY Mbedx509_LIBRARY MbedTLS_INCLUDE_DIR)\n  list(APPEND _COMPONENTS tls crypto x509)\nendif()\nunset(MbedTLS_ERROR_REASON)\n\nif(MbedTLS_FOUND)\n  foreach(component IN LISTS _COMPONENTS)\n    if(NOT TARGET MbedTLS::mbed${component})\n      if(IS_ABSOLUTE \"${Mbed${component}_LIBRARY}\")\n        add_library(MbedTLS::mbed${component} UNKNOWN IMPORTED)\n        set_property(TARGET MbedTLS::mbed${component} PROPERTY IMPORTED_LOCATION \"${Mbed${component}_LIBRARY}\")\n      else()\n        add_library(MbedTLS::mbed${component} INTERFACE IMPORTED)\n        set_property(TARGET MbedTLS::mbed${component} PROPERTY IMPORTED_LIBNAME \"${Mbed${component}_LIBRARY}\")\n      endif()\n\n      mbedtls_set_soname(${component})\n      set_target_properties(\n        MbedTLS::mbedtls\n        PROPERTIES\n          INTERFACE_COMPILE_OPTIONS \"${PC_MbedTLS_CFLAGS_OTHER}\"\n          INTERFACE_INCLUDE_DIRECTORIES \"${MbedTLS_INCLUDE_DIR}\"\n          INTERFACE_LINK_OPTIONS \"$<$<AND:$<PLATFORM_ID:Windows>,$<CONFIG:DEBUG>>:/NODEFAULTLIB:MSVCRT>\"\n          VERSION ${MbedTLS_VERSION}\n      )\n    endif()\n  endforeach()\n\n  if(MbedTLS_INCLUDES_X509 AND MbedTLS_INCLUDES_CRYPTO)\n    set(MbedTLS_LIBRARIES ${MbedTLS_LIBRARY})\n  else()\n    set(MbedTLS_LIBRARIES ${MbedTLS_LIBRARY} ${MbedCrypto_LIBRARY} ${MbedX509_LIBRARY})\n    set_property(TARGET MbedTLS::mbedtls PROPERTY INTERFACE_LINK_LIBRARIES MbedTLS::mbedcrypto MbedTLS::mbedx509)\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  MbedTLS\n  PROPERTIES\n    URL \"https://www.trustedfirmware.org/projects/mbed-tls\"\n    DESCRIPTION\n      \"A C library implementing cryptographic primitives, X.509 certificate manipulation, and the SSL/TLS and DTLS protocols.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindOSS.cmake",
    "content": "#[=======================================================================[.rst\nFindOSS\n-------\n\nFindModule for OSS and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``OSS::OSS``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``OSS_FOUND``\n  True, if all required components and the core library were found.\n``OSS_VERSION``\n  Detected version of found OSS libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``OSS_INCLUDE_DIR``\n  Directory containing ``sys/soundcard.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_path(\n  OSS_INCLUDE_DIR\n  NAMES sys/soundcard.h\n  HINTS ${PC_OSS_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"OSS include directory\"\n)\n\nset(OSS_VERSION ${CMAKE_HOST_SYSTEM_VERSION})\n\nfind_package_handle_standard_args(\n  OSS\n  REQUIRED_VARS OSS_INCLUDE_DIR\n  VERSION_VAR OSS_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that OSS is installed on the system.\"\n)\nmark_as_advanced(OSS_INCLUDE_DIR OSS_LIBRARY)\n\nif(OSS_FOUND)\n  if(NOT TARGET OSS::OSS)\n    add_library(OSS::OSS INTERFACE IMPORTED)\n\n    set_target_properties(OSS::OSS PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${OSS_INCLUDE_DIR}\" VERSION ${OSS_VERSION})\n  endif()\nendif()\n"
  },
  {
    "path": "cmake/finders/FindPipeWire.cmake",
    "content": "#[=======================================================================[.rst\nFindPipeWire\n------------\n\nFindModule for PipeWire and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``PipeWire::PipeWire``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``PipeWire_FOUND``\n  True, if all required components and the core library were found.\n``PipeWire_VERSION``\n  Detected version of found PipeWire libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``PipeWire_LIBRARY``\n  Path to the library component of PipeWire.\n``PipeWire_INCLUDE_DIR``\n  Directory containing ``PipeWire.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_PipeWire libpipewire-0.3 QUIET)\n  pkg_search_module(PC_Libspa libspa-0.2 QUIET)\nendif()\n\nfind_path(\n  PipeWire_INCLUDE_DIR\n  NAMES pipewire/pipewire.h\n  HINTS ${PC_PipeWire_INCLUDE_DIRS}\n  PATH_SUFFIXES pipewire-0.3\n  PATHS /usr/include /usr/local/include\n  DOC \"PipeWire include directory\"\n)\n\nfind_path(\n  Libspa_INCLUDE_DIR\n  NAMES spa/param/props.h\n  HINTS ${PC_Libspa_INCLUDE_DIRS}\n  PATH_SUFFIXES spa-0.2\n  PATHS /usr/include /usr/local/include\n  DOC \"Libspa include directory\"\n)\n\nfind_library(\n  PipeWire_LIBRARY\n  NAMES pipewire-0.3\n  HINTS ${PC_PipeWire_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"PipeWire location\"\n)\n\nif(PC_PipeWire_VERSION VERSION_GREATER 0)\n  set(PipeWire_VERSION ${PC_PipeWire_VERSION})\nelseif(EXISTS \"${PipeWire_INCLUDE_DIR}/pipewire/version.h\")\n  file(\n    STRINGS \"${PipeWire_INCLUDE_DIR}/pipewire/version.h\"\n    _version_string\n    REGEX \"^.*PW_(MAJOR|MINOR|MICRO)[ \\t]+[0-9]+[ \\t]*$\"\n  )\n  string(REGEX REPLACE \".*PW_MAJOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_major \"${_version_string}\")\n  string(REGEX REPLACE \".*PW_MINOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_minor \"${_version_string}\")\n  string(REGEX REPLACE \".*PW_MICRO[ \\t]+([0-9]+).*\" \"\\\\1\" _version_micro \"${_version_string}\")\n\n  set(PipeWire_VERSION \"${_version_major}.${_version_minor}.${_version_micro}\")\n  unset(_version_major)\n  unset(_version_minor)\n  unset(_version_micro)\nelse()\n  if(NOT PipeWire_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find PipeWire version.\")\n  endif()\n  set(PipeWire_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  PipeWire\n  REQUIRED_VARS PipeWire_LIBRARY PipeWire_INCLUDE_DIR Libspa_INCLUDE_DIR\n  VERSION_VAR PipeWire_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that PipeWire is installed on the system.\"\n)\nmark_as_advanced(PipeWire_LIBRARY PipeWire_INCLUDE_DIR)\n\nif(PipeWire_FOUND)\n  if(NOT TARGET PipeWire::PipeWire)\n    if(IS_ABSOLUTE \"${PipeWire_LIBRARY}\")\n      add_library(PipeWire::PipeWire UNKNOWN IMPORTED)\n      set_property(TARGET PipeWire::PipeWire PROPERTY IMPORTED_LOCATION \"${PipeWire_LIBRARY}\")\n    else()\n      add_library(PipeWire::PipeWire INTERFACE IMPORTED)\n      set_property(TARGET PipeWire::PipeWire PROPERTY IMPORTED_LIBNAME \"${PipeWire_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      PipeWire::PipeWire\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_PipeWire_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${PipeWire_INCLUDE_DIR};${Libspa_INCLUDE_DIR}\"\n        VERSION ${PipeWire_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  PipeWire\n  PROPERTIES URL \"https://www.pipewire.org\" DESCRIPTION \"PipeWire - multimedia processing\"\n)\n"
  },
  {
    "path": "cmake/finders/FindPulseAudio.cmake",
    "content": "#[=======================================================================[.rst\nFindPulseAudio\n--------------\n\nFindModule for PulseAudio and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``PulseAudio::PulseAudio``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``PulseAudio_FOUND``\n  True, if all required components and the core library were found.\n``PulseAudio_VERSION``\n  Detected version of found PulseAudio libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``PulseAudio_LIBRARY``\n  Path to the library component of PulseAudio.\n``PulseAudio_INCLUDE_DIR``\n  Directory containing ``pulseaudio.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_PulseAudio QUIET libpulse)\nendif()\n\nfind_path(\n  PulseAudio_INCLUDE_DIR\n  NAMES pulse/pulseaudio.h\n  HINTS ${PC_PulseAudio_INCLUDE_DIRS}\n  PATHS /usr/include/ /usr/local/include\n  DOC \"PulseAudio include directory\"\n)\n\nfind_library(\n  PulseAudio_LIBRARY\n  NAMES pulse\n  HINTS ${PC_PulseAudio_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"PulseAudio location\"\n)\n\nif(PC_PulseAudio_VERSION VERSION_GREATER 0)\n  set(PulseAudio_VERSION ${PC_PulseAudio_VERSION})\nelseif(EXISTS \"${PulseAudio_INCLUDE_DIR}/version.h\")\n  file(\n    STRINGS \"${PulseAudio_INCLUDE_DIR}/version.h\"\n    _VERSION_STRING\n    REGEX \"^.*pa_get_headers_version\\\\(\\\\)[\\t ]+\\\\(\\\".*\\\"\\\\)[ \\t]*$\"\n  )\n  string(\n    REGEX REPLACE\n    \".*pa_get_headers_version\\\\(\\\\)[\\t ]+\\\\(\\\"([^\\\"]*)\\\"\\\\).*\"\n    \"\\\\1\"\n    PulseAudio_VERSION\n    \"${_VERSION_STRING}\"\n  )\nelse()\n  if(NOT PulseAudio_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find PulseAudio version.\")\n  endif()\n  set(PulseAudio_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  PulseAudio\n  REQUIRED_VARS PulseAudio_INCLUDE_DIR PulseAudio_LIBRARY\n  VERSION_VAR PulseAudio_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that PulseAudio is installed on the system.\"\n)\nmark_as_advanced(PulseAudio_INCLUDE_DIR PulseAudio_LIBRARY)\n\nif(PulseAudio_FOUND)\n  if(NOT TARGET PulseAudio::PulseAudio)\n    if(IS_ABSOLUTE \"${PulseAudio_LIBRARY}\")\n      add_library(PulseAudio::PulseAudio UNKNOWN IMPORTED)\n      set_property(TARGET PulseAudio::PulseAudio PROPERTY IMPORTED_LOCATION \"${PulseAudio_LIBRARY}\")\n    else()\n      add_library(PulseAudio::PulseAudio INTERFACE IMPORTED)\n      set_property(TARGET PulseAudio::PulseAudio PROPERTY IMPORTED_LIBNAME \"${PulseAudio_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      PulseAudio::PulseAudio\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_PulseAudio_CFLAFGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${PulseAudio_INCLUDE_DIR}\"\n        VERSION ${PulseAudio_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  PulseAudio\n  PROPERTIES\n    URL \"https://www.freedesktop.org/wiki/Software/PulseAudio/\"\n    DESCRIPTION\n      \"PulseAudio is a sound server system for POSIX OSes, meaning that it is a proxy for your sound applications.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindSIMDe.cmake",
    "content": "#[=======================================================================[.rst\nFindSIMDe\n---------\n\nFindModule for SIMD Everywhere\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 3.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``SIMDe::SIMDe``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``SIMDe_FOUND``\n  True, if headers were found.\n``SIMDe_VERSION``\n  Detected version of found SIMDe Everywhere.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``SIMDe_INCLUDE_DIR``\n  Directory containing ``simde/simde-common.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_SIMDe QUIET simde)\nendif()\n\nfind_path(\n  SIMDe_INCLUDE_DIR\n  NAMES simde/simde-common.h\n  HINTS ${PC_SIMDe_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"SIMD Everywhere include directory\"\n)\n\nif(EXISTS \"${SIMDe_INCLUDE_DIR}/simde/simde-common.h\")\n  file(\n    STRINGS \"${SIMDe_INCLUDE_DIR}/simde/simde-common.h\"\n    _version_string\n    REGEX \"^.*VERSION_(MAJOR|MINOR|MICRO)[ \\t]+[0-9]+[ \\t]*$\"\n  )\n\n  string(REGEX REPLACE \".*VERSION_MAJOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_major \"${_version_string}\")\n  string(REGEX REPLACE \".*VERSION_MINOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_minor \"${_version_string}\")\n  string(REGEX REPLACE \".*VERSION_MICRO[ \\t]+([0-9]+).*\" \"\\\\1\" _version_release \"${_version_string}\")\n\n  set(SIMDe_VERSION \"${_version_major}.${_version_minor}.${_version_release}\")\n  unset(_version_major)\n  unset(_version_minor)\n  unset(_version_release)\nelse()\n  if(NOT SIMDe_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find SIMD Everywhere version.\")\n  endif()\n  set(SIMDe_VERSION 0.0.0)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(SIMDe_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(SIMDe_ERROR_REASON \"Ensure SIMD Everywhere is available in local library paths.\")\nendif()\n\nfind_package_handle_standard_args(\n  SIMDe\n  REQUIRED_VARS SIMDe_INCLUDE_DIR\n  VERSION_VAR SIMDe_VERSION\n  REASON_FAILURE_MESSAGE \"${SIMDe_ERROR_REASON}\"\n)\nmark_as_advanced(SIMDe_INCLUDE_DIR)\nunset(SIMDe_ERROR_REASON)\n\nif(SIMDe_FOUND)\n  if(NOT TARGET SIMDe::SIMDe)\n    add_library(SIMDe::SIMDe INTERFACE IMPORTED)\n    set_target_properties(SIMDe::SIMDe PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${SIMDe_INCLUDE_DIR}\")\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  SIMDe\n  PROPERTIES\n    URL \"https://github.com/simd-everywhere/simde\"\n    DESCRIPTION \"Implementations of SIMD instruction sets for systems which don't natively support them.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindSndio.cmake",
    "content": "#[=======================================================================[.rst\nFindSndio\n---------\n\nFindModule for Sndio and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Sndio::Sndio``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Sndio_FOUND``\n  True, if all required components and the core library were found.\n``Sndio_VERSION``\n  Detected version of found Sndio libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Sndio_LIBRARY``\n  Path to the library component of Sndio.\n``Sndio_INCLUDE_DIR``\n  Directory containing ``sndio.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Sndio QUIET sndio)\nendif()\n\nfind_path(\n  Sndio_INCLUDE_DIR\n  NAMES sndio.h\n  HINTS ${PC_Sndio_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"Sndio include directory\"\n)\n\nfind_library(\n  Sndio_LIBRARY\n  NAMES sndio\n  HINTS ${PC_Sndio_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Sndio location\"\n)\n\nif(PC_Sndio_VERSION VERSION_GREATER 0)\n  set(Sndio_VERSION ${PC_Sndio_VERSION})\nelse()\n  if(NOT Sndio_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Sndio version.\")\n  endif()\n  set(Sndio_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Sndio\n  REQUIRED_VARS Sndio_LIBRARY Sndio_INCLUDE_DIR\n  VERSION_VAR Sndio_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that Sndio is installed on the system.\"\n)\nmark_as_advanced(Sndio_INCLUDE_DIR Sndio_LIBRARY)\n\nif(Sndio_FOUND)\n  if(NOT TARGET Sndio::Sndio)\n    if(IS_ABSOLUTE \"${Sndio_LIBRARY}\")\n      add_library(Sndio::Sndio UNKNOWN IMPORTED)\n      set_property(TARGET Sndio::Sndio PROPERTY IMPORTED_LOCATION \"${Sndio_LIBRARY}\")\n    else()\n      add_library(Sndio::Sndio INTERFACE IMPORTED)\n      set_property(TARGET Sndio::Sndio PROPERTY IMPORTED_LIBNAME \"${Sndio_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Sndio::Sndio\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Sndio_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Sndio_INCLUDE_DIR}\"\n        VERSION ${Sndio_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Sndio\n  PROPERTIES\n    URL \"https://www.sndio.org\"\n    DESCRIPTION \"Sndio is a small audio and MIDI framework part of the OpenBSD project and ported to FreeBSD.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindSysinfo.cmake",
    "content": "#[=======================================================================[.rst\nFindSysinfo\n-----------\n\nFindModule for Sysinfo and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Sysinfo::Sysinfo``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Sysinfo_FOUND``\n  True, if all required components and the core library were found.\n``Sysinfo_VERSION``\n  Detected version of found Sysinfo libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Sysinfo_LIBRARY``\n  Path to the library component of Sysinfo.\n``Sysinfo_INCLUDE_DIR``\n  Directory containing ``sys/sysinfo.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Sysinfo QUIET sysinfo)\nendif()\n\nfind_path(\n  Sysinfo_INCLUDE_DIR\n  NAMES sys/sysinfo.h\n  HINTS ${PC_Sysinfo_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  DOC \"Sysinfo include directory\"\n)\n\nfind_library(\n  Sysinfo_LIBRARY\n  NAMES sysinfo libsysinfo\n  HINTS ${PC_Sysinfo_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"Sysinfo location\"\n)\n\nif(PC_Sysinfo_VERSION VERSION_GREATER 0)\n  set(Sysinfo_VERSION ${PC_Sysinfo_VERSION})\nelse()\n  if(NOT Sysinfo_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Sysinfo version.\")\n  endif()\n  set(Sysinfo_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Sysinfo\n  REQUIRED_VARS Sysinfo_LIBRARY Sysinfo_INCLUDE_DIR\n  VERSION_VAR Sysinfo_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that Sysinfo is installed on the system.\"\n)\nmark_as_advanced(Sysinfo_INCLUDE_DIR Sysinfo_LIBRARY)\n\nif(Sysinfo_FOUND)\n  if(NOT TARGET Sysinfo::Sysinfo)\n    if(IS_ABSOLUTE \"${Sysinfo_LIBRARY}\")\n      add_library(Sysinfo::Sysinfo UNKNOWN IMPORTED)\n      set_property(TARGET Sysinfo::Sysinfo PROPERTY IMPORTED_LOCATION \"${Sysinfo_LIBRARY}\")\n    else()\n      add_library(Sysinfo::Sysinfo INTERFACE IMPORTED)\n      set_property(TARGET Sysinfo::Sysinfo PROPERTY IMPORTED_LIBNAME \"${Sysinfo_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Sysinfo::Sysinfo\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Sysinfo_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Sysinfo_INCLUDE_DIR}\"\n        VERSION ${Sysinfo_VERSION}\n    )\n  endif()\nendif()\n"
  },
  {
    "path": "cmake/finders/FindUthash.cmake",
    "content": "#[=======================================================================[.rst\nFindUthash\n----------\n\nFindModule for uthash and the associated library\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 3.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Uthash::Uthash``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Uthash_FOUND``\n  True, if the library was found.\n``Uthash_VERSION``\n  Detected version of found uthash library.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Uthash_INCLUDE_DIR``\n  Directory containing ``uthash.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_path(Uthash_INCLUDE_DIR NAMES uthash.h PATHS /usr/include /usr/local/include DOC \"uthash include directory\")\n\nif(EXISTS \"${Uthash_INCLUDE_DIR}/uthash.h\")\n  file(\n    STRINGS \"${Uthash_INCLUDE_DIR}/uthash.h\"\n    _version_string\n    REGEX \"#define[ \\t]+UTHASH_VERSION[ \\t]+[0-9]+\\\\.[0-9]+\\\\.[0-9]+\"\n  )\n\n  string(\n    REGEX REPLACE\n    \"#define[ \\t]+UTHASH_VERSION[ \\t]+([0-9]+\\\\.[0-9]+\\\\.[0-9]+)\"\n    \"\\\\1\"\n    Uthash_VERSION\n    \"${_version_string}\"\n  )\nelse()\n  if(NOT Uthash_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find uthash version.\")\n  endif()\n  set(Uthash_VERSION 0.0.0)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(Uthash_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(Uthash_ERROR_REASON \"Ensure uthash library is available in local include paths.\")\nendif()\n\nfind_package_handle_standard_args(\n  Uthash\n  REQUIRED_VARS Uthash_INCLUDE_DIR\n  VERSION_VAR Uthash_VERSION\n  REASON_FAILURE_MESSAGE \"${Uthash_ERROR_REASON}\"\n)\nmark_as_advanced(Uthash_INCLUDE_DIR)\nunset(Uthash_ERROR_REASON)\n\nif(Uthash_FOUND)\n  if(NOT TARGET Uthash::Uthash)\n    add_library(Uthash::Uthash INTERFACE IMPORTED)\n    set_target_properties(Uthash::Uthash PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${Uthash_INCLUDE_DIR}\")\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Uthash\n  PROPERTIES URL \"https://troydhanson.github.io/uthash\" DESCRIPTION \"A hash table for C structures\"\n)\n"
  },
  {
    "path": "cmake/finders/FindVPL.cmake",
    "content": "#[=======================================================================[.rst\nFindVPL\n-------\n\nFindModule for VPL and associated headers\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 3.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``VPL::VPL``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``VPL_FOUND``\n  True, if headers were found.\n``VPL_VERSION``\n  Detected version of found VPL headers.\n``VPL_LIBRARIES``\n  Libraries needed to link to VPL.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``VPL_INCLUDE_DIR``\n  Directory containing ``mfxdispatcher.h``.\n``VPL_LIBRARY_RELEASE``\n  Path to the library component of VPL in non-debug configuration.\n``VPL_LIBRARY_DEBUG``\n  Optional path to the library component of VPL in debug configuration.\n\n#]=======================================================================]\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(_VPL QUIET vpl)\nendif()\n\nfind_path(\n  VPL_INCLUDE_DIR\n  NAMES vpl/mfxstructures.h\n  HINTS ${_VPL_INCLUDE_DIRS} ${_VPL_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include /opt/local/include /sw/include\n  DOC \"VPL include directory\"\n)\n\nfind_library(\n  VPL_LIBRARY_RELEASE\n  NAMES ${_VPL_LIBRARIES} ${_VPL_LIBRARIES} vpl\n  HINTS ${_VPL_LIBRARY_DIRS} ${_VPL_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib\n  DOC \"VPL library location\"\n)\n\nfind_library(\n  VPL_LIBRARY_DEBUG\n  NAMES ${_VPL_LIBRARIES} ${_VPL_LIBRARIES} vpld\n  HINTS ${_VPL_LIBRARY_DIRS} ${_VPL_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib\n  DOC \"VPL debug library location\"\n)\n\ninclude(SelectLibraryConfigurations)\nselect_library_configurations(VPL)\n\ninclude(FindPackageHandleStandardArgs)\nfind_package_handle_standard_args(\n  VPL\n  REQUIRED_VARS VPL_LIBRARY VPL_INCLUDE_DIR\n  VERSION_VAR VPL_VERSION\n  REASON_FAILURE_MESSAGE \"${VPL_ERROR_REASON}\"\n)\nmark_as_advanced(VPL_INCLUDE_DIR VPL_LIBRARY)\nunset(VPL_ERROR_REASON)\n\nif(EXISTS \"${VPL_INCLUDE_DIR}/vpl/mfxdefs.h\")\n  file(STRINGS \"${VPL_INCLUDE_DIR}/vpl/mfxdefs.h\" _version_string REGEX \"^.*VERSION_(MAJOR|MINOR)[ \\t]+[0-9]+[ \\t]*$\")\n\n  string(REGEX REPLACE \".*VERSION_MAJOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_major \"${_version_string}\")\n  string(REGEX REPLACE \".*VERSION_MINOR[ \\t]+([0-9]+).*\" \"\\\\1\" _version_minor \"${_version_string}\")\n\n  set(VPL_VERSION \"${_version_major}.${_version_minor}\")\n  unset(_version_major)\n  unset(_version_minor)\nelse()\n  if(NOT VPL_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find VPL version.\")\n  endif()\n  set(VPL_VERSION 0.0.0)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(VPL_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(VPL_ERROR_REASON \"Ensure VPL headers are available in local library paths.\")\nendif()\n\nif(VPL_FOUND)\n  set(VPL_INCLUDE_DIRS ${VPL_INCLUDE_DIR})\n  set(VPL_LIBRARIES ${VPL_LIBRARY})\n\n  if(NOT TARGET VPL::VPL)\n    if(IS_ABSOLUTE \"${VPL_LIBRARY_RELEASE}\")\n      add_library(VPL::VPL UNKNOWN IMPORTED)\n      set_target_properties(VPL::VPL PROPERTIES IMPORTED_LOCATION \"${VPL_LIBRARY_RELEASE}\")\n    else()\n      add_library(VPL::VPL INTERFACE IMPORTED)\n      set_target_properties(VPL::VPL PROPERTIES IMPORTED_LIBNAME \"${VPL_LIBRARY_RELEASE}\")\n    endif()\n\n    set_target_properties(\n      VPL::VPL\n      PROPERTIES\n        INTERFACE_INCLUDE_DIRECTORIES \"${VPL_INCLUDE_DIRS}\"\n        VERSION ${VPL_VERSION}\n        IMPORTED_CONFIGURATIONS Release\n    )\n\n    if(VPL_LIBRARY_DEBUG)\n      if(IS_ABSOLUTE \"${VPL_LIBRARY_DEBUG}\")\n        set_target_properties(VPL::VPL PROPERTIES IMPORTED_LOCATION_DEBUG \"${VPL_LIBRARY_DEBUG}\")\n      else()\n        set_target_properties(VPL::VPL PROPERTIES IMPORTED_LIBNAME_DEBUG \"${VPL_LIBRARY_DEBUG}\")\n      endif()\n      set_property(TARGET VPL::VPL APPEND PROPERTY IMPORTED_CONFIGURATIONS Debug)\n    endif()\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  VPL\n  PROPERTIES\n    URL \"https://github.com/oneapi-src/oneVPL\"\n    DESCRIPTION\n      \"Intel® oneAPI Video Processing Library (oneVPL) supports AI visual inference, media delivery, cloud gaming, and virtual desktop infrastructure use cases by providing access to hardware accelerated video decode, encode, and frame processing capabilities on Intel® GPUs.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindWebsocketpp.cmake",
    "content": "#[=======================================================================[.rst\nFindWebsocketpp\n---------------\n\nFindModule for WebSocket++ and the associated library\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Websocketpp::Websocketpp``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Websocketpp_FOUND``\n  True, if the library was found.\n``Websocketpp_VERSION``\n  Detected version of found Websocketpp library.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Websocketpp_INCLUDE_DIR``\n  Directory containing ``websocketpp/client.hpp``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_path(\n  Websocketpp_INCLUDE_DIR\n  NAMES websocketpp/client.hpp\n  PATHS /usr/include /usr/local/include\n  DOC \"WebSocket++ include directory\"\n)\n\nif(EXISTS \"${Websocketpp_INCLUDE_DIR}/websocketpp/version.hpp\")\n  file(\n    STRINGS \"${Websocketpp_INCLUDE_DIR}/websocketpp/version.hpp\"\n    _version_string\n    REGEX \"^.*(major|minor|patch)_version[ \\t]+=[ \\t]+[0-9]+\"\n  )\n\n  string(REGEX REPLACE \".*major_version[ \\t]+=[ \\t]+([0-9]+).*\" \"\\\\1\" _version_major \"${_version_string}\")\n  string(REGEX REPLACE \".*minor_version[ \\t]+=[ \\t]+([0-9]+).*\" \"\\\\1\" _version_minor \"${_version_string}\")\n  string(REGEX REPLACE \".*patch_version[ \\t]+=[ \\t]+([0-9]+).*\" \"\\\\1\" _version_patch \"${_version_string}\")\n\n  set(Websocketpp_VERSION \"${_version_major}.${_version_minor}.${_version_patch}\")\n  unset(_version_major)\n  unset(_version_minor)\n  unset(_version_patch)\nelse()\n  if(NOT Websocketpp_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find WebSocket++ version.\")\n  endif()\n  set(Websocketpp_VERSION 0.0.0)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(Websocketpp_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(Websocketpp_ERROR_REASON \"Ensure WebSocket++ library is available in local include paths.\")\nendif()\n\nfind_package_handle_standard_args(\n  Websocketpp\n  REQUIRED_VARS Websocketpp_INCLUDE_DIR\n  VERSION_VAR Websocketpp_VERSION\n  REASON_FAILURE_MESSAGE \"${Websocketpp_ERROR_REASON}\"\n)\nmark_as_advanced(Websocketpp_INCLUDE_DIR)\nunset(Websocketpp_ERROR_REASON)\n\nif(Websocketpp_FOUND)\n  if(NOT TARGET Websocketpp::Websocketpp)\n    add_library(Websocketpp::Websocketpp INTERFACE IMPORTED)\n    set_target_properties(\n      Websocketpp::Websocketpp\n      PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${Websocketpp_INCLUDE_DIR}\"\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Websocketpp\n  PROPERTIES\n    URL \"https://www.zaphoyd.com/websocketpp/\"\n    DESCRIPTION \"WebSocket++ is a header only C++ library that implements RFC6455 The WebSocket Protocol.\"\n)\n"
  },
  {
    "path": "cmake/finders/FindXkbcommon.cmake",
    "content": "#[=======================================================================[.rst\nFindXkbcommon\n-------------\n\nFindModule for xkbcommon and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``xkbcommon::xkbcommon``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Xkbcommon_FOUND``\n  True, if all required components and the core library were found.\n``Xkbcommon_VERSION``\n  Detected version of found xkbcommon libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Xkbcommon_LIBRARY``\n  Path to the library component of xkbcommon.\n``Xkbcommon_INCLUDE_DIR``\n  Directory containing ``xkbcommon.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_Xkbcommon QUIET xkbcommon)\nendif()\n\nfind_path(\n  Xkbcommon_INCLUDE_DIR\n  NAMES xkbcommon/xkbcommon.h\n  HINTS ${PC_Xkbcommon_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  PATH_SUFFIXES xkbcommon\n  DOC \"xkbcommon include directory\"\n)\n\nfind_library(\n  Xkbcommon_LIBRARY\n  NAMES xkbcommon libxkbcommon\n  HINTS ${PC_Xkbcommon_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"xkbcommon location\"\n)\n\nif(PC_Xkbcommon_VERSION VERSION_GREATER 0)\n  set(Xkbcommon_VERSION ${PC_Xkbcommon_VERSION})\nelse()\n  if(NOT Xkbcommon_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find xkbcommon version.\")\n  endif()\n  set(Xkbcommon_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Xkbcommon\n  REQUIRED_VARS Xkbcommon_LIBRARY Xkbcommon_INCLUDE_DIR\n  VERSION_VAR Xkbcommon_VERSION\n  REASON_FAILURE_MESSAGE \"Ensure that xkbcommon is installed on the system.\"\n)\nmark_as_advanced(Xkbcommon_INCLUDE_DIR Xkbcommon_LIBRARY)\n\nif(Xkbcommon_FOUND)\n  if(NOT TARGET xkbcommon::xkbcommon)\n    if(IS_ABSOLUTE \"${Xkbcommon_LIBRARY}\")\n      add_library(xkbcommon::xkbcommon UNKNOWN IMPORTED)\n      set_property(TARGET xkbcommon::xkbcommon PROPERTY IMPORTED_LOCATION \"${Xkbcommon_LIBRARY}\")\n    else()\n      add_library(xkbcommon::xkbcommon INTERFACE IMPORTED)\n      set_property(TARGET xkbcommon::xkbcommon PROPERTY IMPORTED_LIBNAME \"${Xkbcommon_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      xkbcommon::xkbcommon\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_Xkbcommon_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${Xkbcommon_INCLUDE_DIR}\"\n        VERSION ${Xkbcommon_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Xkbcommon\n  PROPERTIES\n    URL \"https://www.xkbcommon.org\"\n    DESCRIPTION\n      \"A library for handling of keyboard descriptions, including loading them from disk, parsing them and handling their state.\"\n)\n"
  },
  {
    "path": "cmake/finders/Findjansson.cmake",
    "content": "#[=======================================================================[.rst\nFindjansson\n----------\n\nFindModule for jansson and associated libraries\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``jansson::jansson``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``jansson_FOUND``\n  True, if all required components and the core library were found.\n``jansson_VERSION``\n  Detected version of found jansson libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``jansson_LIBRARY``\n  Path to the library component of jansson.\n``jansson_INCLUDE_DIR``\n  Directory containing ``jansson.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_jansson QUIET jansson)\nendif()\n\n# jansson_set_soname: Set SONAME on imported library targets\nmacro(jansson_set_soname)\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    execute_process(\n      COMMAND sh -c \"otool -D '${jansson_LIBRARY}' | grep -v '${jansson_LIBRARY}'\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0 AND _output MATCHES \"^@rpath/\")\n      set_property(TARGET jansson::jansson PROPERTY IMPORTED_SONAME \"${_output}\")\n    endif()\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n    execute_process(\n      COMMAND sh -c \"objdump -p '${jansson_LIBRARY}' | grep SONAME\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0)\n      string(REGEX REPLACE \"[ \\t]+SONAME[ \\t]+([^ \\t]+)\" \"\\\\1\" _soname \"${_output}\")\n      set_property(TARGET jansson::jansson PROPERTY IMPORTED_SONAME \"${_soname}\")\n      unset(_soname)\n    endif()\n  endif()\n  unset(_output)\n  unset(_result)\nendmacro()\n\nfind_path(\n  jansson_INCLUDE_DIR\n  NAMES jansson.h\n  HINTS ${PC_jansson_INCLUDE_DIR}\n  PATHS /usr/include /usr/local/include\n  PATH_SUFFIXES jansson-2.1\n  DOC \"jansson include directory\"\n)\n\nif(PC_jansson_VERSION VERSION_GREATER 0)\n  set(jansson_VERSION ${PC_jansson_VERSION})\nelseif(EXISTS \"${jansson_INCLUDE_DIR}/jansson.h\")\n  file(STRINGS \"${jansson_INCLUDE_DIR}/jansson.h\" _VERSION_STRING REGEX \"#define[ \\t]+JANSSON_VERSION[ \\t]+.+\")\n  string(REGEX REPLACE \".*#define[ \\t]+JANSSON_VERSION[ \\t]+\\\"(.+)\\\".*\" \"\\\\1\" jansson_VERSION \"${_VERSION_STRING}\")\nelse()\n  if(NOT jansson_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find jansson version.\")\n  endif()\n  set(jansson_VERSION 0.0.0)\nendif()\n\nfind_library(\n  jansson_LIBRARY\n  NAMES jansson\n  HINTS ${PC_jansson_LIBRARY_DIRS}\n  PATHS /usr/lib /usr/local/lib\n  DOC \"jansson location\"\n)\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(jansson_ERROR_REASON \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(jansson_ERROR_REASON \"Ensure that jansson is installed on the system.\")\nendif()\n\nfind_package_handle_standard_args(\n  jansson\n  REQUIRED_VARS jansson_LIBRARY jansson_INCLUDE_DIR\n  VERSION_VAR jansson_VERSION\n  REASON_FAILURE_MESSAGE \"${jansson_ERROR_REASON}\"\n)\nmark_as_advanced(jansson_INCLUDE_DIR jansson_LIBRARY)\nunset(jansson_ERROR_REASON)\n\nif(jansson_FOUND)\n  if(NOT TARGET jansson::jansson)\n    if(IS_ABSOLUTE \"${jansson_LIBRARY}\")\n      add_library(jansson::jansson UNKNOWN IMPORTED)\n      set_property(TARGET jansson::jansson PROPERTY IMPORTED_LOCATION \"${jansson_LIBRARY}\")\n    else()\n      add_library(jansson::jansson INTERFACE IMPORTED)\n      set_property(TARGET jansson::jansson PROPERTY IMPORTED_LIBNAME \"${jansson_LIBRARY}\")\n    endif()\n\n    jansson_set_soname()\n    set_target_properties(\n      jansson::jansson\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_jansson_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${jansson_INCLUDE_DIR}\"\n        VERSION ${jansson_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  jansson\n  PROPERTIES\n    URL \"https://www.digip.org/jansson/\"\n    DESCRIPTION \"A C library for encoding, decoding, and manipulating JSON data.\"\n)\n"
  },
  {
    "path": "cmake/finders/Findqrcodegencpp.cmake",
    "content": "#[=======================================================================[.rst\nFindqrcodegencpp\n----------------\n\nFindModule for qrcodegencpp and associated libraries\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 3.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``qrcodegencpp::qrcodegencpp``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``qrcodegencpp_FOUND``\n  True, if all required components and the core library were found.\n``qrcodegencpp_VERSION``\n  Detected version of found qrcodegencpp libraries.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``qrcodegencpp_LIBRARY``\n  Path to the library component of qrcodegencpp.\n``qrcodegencpp_INCLUDE_DIR``\n  Directory containing ``qrcodegen.hpp``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_package(PkgConfig QUIET)\nif(PKG_CONFIG_FOUND)\n  pkg_search_module(PC_qrcodegencpp QUIET qrcodegencpp)\nendif()\n\n# qrcodegencpp_set_soname: Set SONAME on imported library target\nmacro(qrcodegencpp_set_soname)\n  if(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin\")\n    execute_process(\n      COMMAND sh -c \"otool -D '${qrcodegencpp_LIBRARY}' | grep -v '${qrcodegencpp_LIBRARY}'\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0 AND _output MATCHES \"^@rpath/\")\n      set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_SONAME \"${_output}\")\n    endif()\n  elseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n    execute_process(\n      COMMAND sh -c \"${CMAKE_OBJDUMP} -p '${qrcodegencpp_LIBRARY}' | grep SONAME\"\n      OUTPUT_VARIABLE _output\n      RESULT_VARIABLE _result\n    )\n\n    if(_result EQUAL 0)\n      string(REGEX REPLACE \"[ \\t]+SONAME[ \\t]+([^ \\t]+)\" \"\\\\1\" _soname \"${_output}\")\n      set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_SONAME \"${_soname}\")\n      unset(_soname)\n    endif()\n  endif()\n  unset(_output)\n  unset(_result)\nendmacro()\n\n# qrcodegencpp_find_dll: Find DLL for corresponding import library\nmacro(qrcodegencpp_find_dll)\n  cmake_path(GET qrcodegencpp_IMPLIB PARENT_PATH _implib_path)\n  cmake_path(SET _bin_path NORMALIZE \"${_implib_path}/../bin\")\n\n  string(REGEX REPLACE \"[0-9]+\\\\.([0-9]+)\\\\.[0-9]+\" \"\\\\1\" _dll_version \"${qrcodegencpp_VERSION}\")\n\n  find_program(\n    qrcodegencpp_LIBRARY\n    NAMES qrcodegencpp.dll\n    HINTS ${_implib_path} ${_bin_path}\n    DOC \"qrcodegencpp DLL location\"\n  )\n\n  if(NOT qrcodegencpp_LIBRARY)\n    set(qrcodegencpp_LIBRARY \"${qrcodegencpp_IMPLIB}\")\n  endif()\n  unset(_implib_path)\n  unset(_bin_path)\n  unset(_dll_version)\nendmacro()\n\nfind_path(\n  qrcodegencpp_INCLUDE_DIR\n  NAMES qrcodegen.hpp\n  HINTS ${PC_qrcodegencpp_INCLUDE_DIRS}\n  PATHS /usr/include /usr/local/include\n  PATH_SUFFIXES qrcodegencpp qrcodegen\n  DOC \"qrcodegencpp include directory\"\n)\n\nif(PC_qrcodegencpp_VERSION VERSION_GREATER 0)\n  set(qrcodegencpp_VERSION ${PC_qrcodegencpp_VERSION})\nelse()\n  if(NOT qrcodegencpp_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find qrcodegencpp version.\")\n  endif()\n  set(qrcodegencpp_VERSION 0.0.0)\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Windows\")\n  find_library(qrcodegencpp_IMPLIB NAMES qrcodegencpp qrcodegencpp DOC \"qrcodegencpp import library location\")\n\n  qrcodegencpp_find_dll()\nelse()\n  find_library(\n    qrcodegencpp_LIBRARY\n    NAMES qrcodegencpp qrcodegencpp\n    HINTS ${PC_qrcodegencpp_LIBRARY_DIRS}\n    PATHS /usr/lib /usr/local/lib\n    DOC \"qrcodegencpp location\"\n  )\nendif()\n\nif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Darwin|Windows\")\n  set(qrcodegencpp_ERROR_REASON \"Ensure that a qrcodegencpp distribution is provided as part of CMAKE_PREFIX_PATH.\")\nelseif(CMAKE_HOST_SYSTEM_NAME MATCHES \"Linux|FreeBSD\")\n  set(qrcodegencpp_ERROR_REASON \"Ensure that qrcodegencpp is installed on the system.\")\nendif()\n\nfind_package_handle_standard_args(\n  qrcodegencpp\n  REQUIRED_VARS qrcodegencpp_LIBRARY qrcodegencpp_INCLUDE_DIR\n  VERSION_VAR qrcodegencpp_VERSION\n  REASON_FAILURE_MESSAGE \"${qrcodegencpp_ERROR_REASON}\"\n)\nmark_as_advanced(qrcodegencpp_INCLUDE_DIR qrcodegencpp_LIBRARY qrcodegencpp_IMPLIB)\nunset(qrcodegencpp_ERROR_REASON)\n\nif(qrcodegencpp_FOUND)\n  if(NOT TARGET qrcodegencpp::qrcodegencpp)\n    if(IS_ABSOLUTE \"${qrcodegencpp_LIBRARY}\")\n      if(DEFINED qrcodegencpp_IMPLIB)\n        if(qrcodegencpp_IMPLIB STREQUAL qrcodegencpp_LIBRARY)\n          add_library(qrcodegencpp::qrcodegencpp STATIC IMPORTED)\n        else()\n          add_library(qrcodegencpp::qrcodegencpp SHARED IMPORTED)\n          set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_IMPLIB \"${qrcodegencpp_IMPLIB}\")\n        endif()\n      else()\n        add_library(qrcodegencpp::qrcodegencpp UNKNOWN IMPORTED)\n      endif()\n      set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_LOCATION \"${qrcodegencpp_LIBRARY}\")\n    else()\n      add_library(qrcodegencpp::qrcodegencpp INTERFACE IMPORTED)\n      set_property(TARGET qrcodegencpp::qrcodegencpp PROPERTY IMPORTED_LIBNAME \"${qrcodegencpp_LIBRARY}\")\n    endif()\n\n    qrcodegencpp_set_soname()\n    set_target_properties(\n      qrcodegencpp::qrcodegencpp\n      PROPERTIES\n        INTERFACE_COMPILE_OPTIONS \"${PC_qrcodegencpp_CFLAGS_OTHER}\"\n        INTERFACE_INCLUDE_DIRECTORIES \"${qrcodegencpp_INCLUDE_DIR}\"\n        VERSION ${qrcodegencpp_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  qrcodegencpp\n  PROPERTIES\n    URL \"https://www.nayuki.io/page/qr-code-generator-library\"\n    DESCRIPTION \"This project aims to be the best, clearest library for generating QR Codes in C++.\"\n)\n"
  },
  {
    "path": "cmake/linux/compilerconfig.cmake",
    "content": "# OBS CMake Linux compiler configuration module\n\ninclude_guard(GLOBAL)\n\ninclude(ccache)\ninclude(compiler_common)\n\noption(ENABLE_COMPILER_TRACE \"Enable Clang time-trace (requires Clang and Ninja)\" OFF)\nmark_as_advanced(ENABLE_COMPILER_TRACE)\n\n# gcc options for C\nset(\n  _obs_gcc_c_options\n  $<$<BOOL:${OBS_COMPILE_DEPRECATION_AS_WARNING}>:-Wno-error=deprecated-declarations>\n  -fno-strict-aliasing\n  -fopenmp-simd\n  -Wdeprecated-declarations\n  -Wempty-body\n  -Wenum-conversion\n  -Werror=return-type\n  -Wextra\n  -Wformat\n  -Wformat-security\n  -Wno-conversion\n  -Wno-float-conversion\n  -Wno-implicit-fallthrough\n  -Wno-missing-braces\n  -Wno-missing-field-initializers\n  -Wno-shadow\n  -Wno-sign-conversion\n  -Wno-trigraphs\n  -Wno-unknown-pragmas\n  -Wno-unused-function\n  -Wno-unused-label\n  -Wparentheses\n  -Wswitch\n  -Wuninitialized\n  -Wunreachable-code\n  -Wunused-parameter\n  -Wunused-value\n  -Wunused-variable\n  -Wvla\n)\n\nadd_compile_options(\n  -fopenmp-simd\n  \"$<$<COMPILE_LANG_AND_ID:C,GNU>:${_obs_gcc_c_options}>\"\n  \"$<$<COMPILE_LANG_AND_ID:C,GNU>:-Wint-conversion;-Wno-missing-prototypes;-Wno-strict-prototypes;-Wpointer-sign>\"\n  \"$<$<COMPILE_LANG_AND_ID:CXX,GNU>:${_obs_gcc_c_options}>\"\n  \"$<$<COMPILE_LANG_AND_ID:CXX,GNU>:-Winvalid-offsetof;-Wno-overloaded-virtual>\"\n  \"$<$<COMPILE_LANG_AND_ID:C,Clang>:${_obs_clang_c_options}>\"\n  \"$<$<COMPILE_LANG_AND_ID:CXX,Clang>:${_obs_clang_cxx_options}>\"\n)\n\n# CMake switch for warnings as errors to CMake < 3.24\nif(CMAKE_VERSION VERSION_LESS 3.24.0)\n  if(CMAKE_COMPILE_WARNING_AS_ERROR)\n    add_compile_options(-Werror)\n  endif()\nendif()\n\nif(CMAKE_CXX_COMPILER_ID STREQUAL GNU)\n  # * Disable false-positive warning in GCC 12.1.0 and later\n  # * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562\n  if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.1.0)\n    add_compile_options(-Wno-error=maybe-uninitialized)\n  endif()\n\n  # * Add warning for infinite recursion (added in GCC 12)\n  # * Also disable warnings for stringop-overflow due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106297\n  if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0.0)\n    add_compile_options(-Winfinite-recursion -Wno-stringop-overflow)\n  endif()\n\n  if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)\n    add_compile_options(-Wno-error=type-limits)\n  endif()\nendif()\n\n# Enable compiler and build tracing (requires Ninja generator)\nif(ENABLE_COMPILER_TRACE AND CMAKE_GENERATOR STREQUAL \"Ninja\")\n  add_compile_options($<$<COMPILE_LANG_AND_ID:C,Clang>:-ftime-trace> $<$<COMPILE_LANG_AND_ID:CXX,Clang>:-ftime-trace>)\nelse()\n  set(ENABLE_COMPILER_TRACE OFF CACHE STRING \"Enable Clang time-trace (required Clang and Ninja)\" FORCE)\nendif()\n\nadd_compile_definitions($<$<CONFIG:DEBUG>:DEBUG> $<$<CONFIG:DEBUG>:_DEBUG> SIMDE_ENABLE_OPENMP)\n"
  },
  {
    "path": "cmake/linux/cpackconfig.cmake",
    "content": "# OBS CMake Linux CPack configuration module\n\ninclude_guard(GLOBAL)\n\ninclude(cpackconfig_common)\n\n# Add GPLv2 license file to CPack\nset(CPACK_RESOURCE_FILE_LICENSE \"${CMAKE_SOURCE_DIR}/frontend/data/license/gplv2.txt\")\nset(CPACK_PACKAGE_EXECUTABLES \"obs\")\n\nif(ENABLE_RELEASE_BUILD)\n  set(CPACK_PACKAGE_VERSION \"${OBS_VERSION_CANONICAL}\")\nelse()\n  set(CPACK_PACKAGE_VERSION \"${OBS_VERSION}\")\nendif()\n\nset(CPACK_SOURCE_PACKAGE_FILE_NAME \"obs-studio-${CPACK_PACKAGE_VERSION}-sources\")\nset(CPACK_SOURCE_GENERATOR \"TGZ\")\nset(CPACK_SOURCE_IGNORE_FILES \"/.git\" \"${CMAKE_BINARY_DIR}\" \"/.ccache\" \"/.deps\")\nset(CPACK_ARCHIVE_THREADS 0)\n\nif(OS_LINUX)\n  set(CPACK_GENERATOR \"DEB\")\n  set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS TRUE)\n  set(CPACK_SET_DESTDIR TRUE)\n  set(CPACK_DEBIAN_DEBUGINFO_PACKAGE TRUE)\n  set(CPACK_DEBIAN_PACKAGE_MAINTAINER \"${OBS_COMPANY_NAME}\")\nelseif(OS_FREEBSD)\n  set(CPACK_GENERATOR \"FREEBSD\")\n\n  set(CPACK_FREEBSD_PACKAGE_MAINTAINER \"${OBS_COMPANY_NAME}\")\n  set(CPACK_FREEBSD_PACKAGE_LICENSE \"GPLv2\")\n\n  set(\n    CPACK_FREEBSD_PACKAGE_DEPS\n    \"audio/alsa-lib\"\n    \"audio/fdk-aac\"\n    \"audio/jack\"\n    \"audio/pulseaudio\"\n    \"audio/sndio\"\n    \"devel/jansson\"\n    \"devel/libpci\"\n    \"devel/libsysinfo\"\n    \"devel/nlohmann-json\"\n    \"devel/qt6-base\"\n    \"devel/qt6-svg\"\n    \"devel/swig\"\n    \"devel/websocketpp\"\n    \"ftp/curl\"\n    \"graphics/mesa-libs\"\n    \"graphics/qr-code-generator\"\n    \"lang/luajit\"\n    \"lang/python39\"\n    \"misc/e2fsprogs-libuuid\"\n    \"multimedia/ffmpeg\"\n    \"multimedia/librist\"\n    \"multimedia/pipewire\"\n    \"multimedia/v4l_compat\"\n    \"multimedia/vlc\"\n    \"net/asio\"\n    \"www/libdatachannel\"\n    \"www/srt\"\n  )\nendif()\n\ninclude(CPack)\n"
  },
  {
    "path": "cmake/linux/defaults.cmake",
    "content": "# OBS CMake Linux defaults module\n\ninclude_guard(GLOBAL)\n\noption(ENABLE_PULSEAUDIO \"Enable PulseAudio support\" ON)\noption(ENABLE_WAYLAND \"Enable building with support for Wayland\" ON)\n\noption(ENABLE_RELOCATABLE \"Enable relocatable build\" OFF)\noption(ENABLE_PORTABLE_CONFIG \"Enable support for portable config file location\" OFF)\n\n# Set default installation directories\ninclude(GNUInstallDirs)\n\nif(CMAKE_INSTALL_LIBDIR MATCHES \"(CMAKE_SYSTEM_PROCESSOR)\")\n  string(REPLACE \"CMAKE_SYSTEM_PROCESSOR\" \"${CMAKE_SYSTEM_PROCESSOR}\" CMAKE_INSTALL_LIBDIR \"${CMAKE_INSTALL_LIBDIR}\")\nendif()\n\nif(ENABLE_RELOCATABLE)\n  set(OBS_EXECUTABLE_RPATH \"$ORIGIN/;$ORIGIN/../${CMAKE_INSTALL_LIBDIR}\")\n  set(OBS_LIBRARY_RPATH \"$ORIGIN/\")\n  set(OBS_MODULE_RPATH \"$ORIGIN/;$ORIGIN/..\")\nendif()\n\nset(OBS_OUTPUT_DIR \"${CMAKE_CURRENT_BINARY_DIR}/rundir\")\nset(OBS_EXECUTABLE_DESTINATION \"${CMAKE_INSTALL_BINDIR}\")\nset(OBS_INCLUDE_DESTINATION \"${CMAKE_INSTALL_INCLUDEDIR}/obs\")\nset(OBS_LIBRARY_DESTINATION \"${CMAKE_INSTALL_LIBDIR}\")\nset(OBS_PLUGIN_DESTINATION \"${CMAKE_INSTALL_LIBDIR}/obs-plugins\")\nset(OBS_SCRIPT_PLUGIN_DESTINATION \"${CMAKE_INSTALL_LIBDIR}/obs-scripting\")\nset(OBS_DATA_DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/obs\")\nset(OBS_CMAKE_DESTINATION \"${CMAKE_INSTALL_LIBDIR}/cmake\")\n\n# Set additional paths used by OBS for self-discovery\nset(OBS_PLUGIN_PATH \"${CMAKE_INSTALL_LIBDIR}/obs-plugins\")\nset(OBS_SCRIPT_PLUGIN_PATH \"${CMAKE_INSTALL_LIBDIR}/obs-scripting\")\nset(OBS_DATA_PATH \"${OBS_DATA_DESTINATION}\")\nset(OBS_INSTALL_PREFIX \"${CMAKE_INSTALL_PREFIX}\")\n\n# Enable find_package targets to become globally available targets\nset(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL TRUE)\n\ninclude(cpackconfig)\ninclude(ecmconfig)\n"
  },
  {
    "path": "cmake/linux/ecmconfig.cmake",
    "content": "# OBS CMake Linux Extra CMake Module configuration module\n\ninclude_guard(GLOBAL)\n\nfind_package(ECM REQUIRED NO_MODULE)\n\nlist(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH})\n"
  },
  {
    "path": "cmake/linux/helpers.cmake",
    "content": "# OBS CMake Linux helper functions module\n\ninclude_guard(GLOBAL)\n\ninclude(helpers_common)\n\n# set_target_properties_obs: Set target properties for use in obs-studio\nfunction(set_target_properties_obs target)\n  set(options \"\")\n  set(oneValueArgs \"\")\n  set(multiValueArgs PROPERTIES)\n  cmake_parse_arguments(PARSE_ARGV 0 _STPO \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\")\n\n  message(DEBUG \"Setting additional properties for target ${target}...\")\n\n  while(_STPO_PROPERTIES)\n    list(POP_FRONT _STPO_PROPERTIES key value)\n    set_property(TARGET ${target} PROPERTY ${key} \"${value}\")\n  endwhile()\n\n  get_target_property(target_type ${target} TYPE)\n  set(OBS_SOVERSION 30)\n\n  if(target_type STREQUAL EXECUTABLE)\n    install(TARGETS ${target} RUNTIME DESTINATION \"${OBS_EXECUTABLE_DESTINATION}\" COMPONENT Runtime)\n\n    add_custom_command(\n      TARGET ${target}\n      POST_BUILD\n      COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_EXECUTABLE_DESTINATION}\"\n      COMMAND\n        \"${CMAKE_COMMAND}\" -E copy_if_different \"$<TARGET_FILE:${target}>\"\n        \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_EXECUTABLE_DESTINATION}\"\n      COMMENT \"Copy ${target} to binary directory\"\n      VERBATIM\n    )\n\n    if(target STREQUAL obs-studio)\n      get_property(obs_executables GLOBAL PROPERTY _OBS_EXECUTABLES)\n      get_property(obs_modules GLOBAL PROPERTY OBS_MODULES_ENABLED)\n      add_dependencies(${target} ${obs_executables} ${obs_modules})\n\n      target_add_resource(${target} \"${CMAKE_CURRENT_SOURCE_DIR}/../AUTHORS\"\n                          \"${OBS_DATA_DESTINATION}/obs-studio/authors\"\n      )\n    elseif(target STREQUAL browser-helper)\n      set_property(GLOBAL APPEND PROPERTY _OBS_EXECUTABLES ${target})\n      return()\n    else()\n      set_property(GLOBAL APPEND PROPERTY _OBS_EXECUTABLES ${target})\n    endif()\n\n    set_target_properties(\n      ${target}\n      PROPERTIES\n        BUILD_RPATH \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_LIBRARY_DESTINATION}\"\n        INSTALL_RPATH \"${OBS_EXECUTABLE_RPATH}\"\n    )\n  elseif(target_type STREQUAL SHARED_LIBRARY)\n    set_target_properties(\n      ${target}\n      PROPERTIES\n        VERSION ${OBS_SOVERSION}\n        SOVERSION ${OBS_SOVERSION}\n        BUILD_RPATH \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_LIBRARY_DESTINATION}\"\n        INSTALL_RPATH \"${OBS_LIBRARY_RPATH}\"\n    )\n\n    install(\n      TARGETS ${target}\n      LIBRARY DESTINATION \"${OBS_LIBRARY_DESTINATION}\" COMPONENT Runtime\n      PUBLIC_HEADER DESTINATION \"${OBS_INCLUDE_DESTINATION}\" COMPONENT Development EXCLUDE_FROM_ALL\n    )\n\n    add_custom_command(\n      TARGET ${target}\n      POST_BUILD\n      COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_LIBRARY_DESTINATION}\"\n      COMMAND\n        \"${CMAKE_COMMAND}\" -E copy_if_different \"$<TARGET_FILE:${target}>\"\n        \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_LIBRARY_DESTINATION}/\"\n      COMMAND\n        \"${CMAKE_COMMAND}\" -E copy_if_different \"$<TARGET_SONAME_FILE:${target}>\"\n        \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_LIBRARY_DESTINATION}/\"\n      COMMENT \"Copy ${target} to library directory (${OBS_LIBRARY_DESTINATION})\"\n      VERBATIM\n    )\n\n    if(target STREQUAL libobs OR target STREQUAL obs-frontend-api)\n      install(\n        FILES \"$<TARGET_FILE_DIR:${target}>/$<TARGET_FILE_PREFIX:${target}>$<TARGET_FILE_BASE_NAME:${target}>.so.0\"\n        DESTINATION \"${OBS_LIBRARY_DESTINATION}\"\n      )\n\n      add_custom_command(\n        TARGET ${target}\n        POST_BUILD\n        COMMAND\n          \"${CMAKE_COMMAND}\" -E create_symlink\n          \"$<TARGET_FILE_PREFIX:${target}>$<TARGET_FILE_BASE_NAME:${target}>.so.${OBS_SOVERSION}\"\n          \"$<TARGET_FILE_PREFIX:${target}>$<TARGET_FILE_BASE_NAME:${target}>.so.0\"\n        COMMAND\n          \"${CMAKE_COMMAND}\" -E copy_if_different\n          \"$<TARGET_FILE_DIR:${target}>/$<TARGET_FILE_PREFIX:${target}>$<TARGET_FILE_BASE_NAME:${target}>.so.0\"\n          \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_LIBRARY_DESTINATION}\"\n        COMMENT \"Create symlink for legacy ${target}\"\n      )\n    endif()\n  elseif(target_type STREQUAL MODULE_LIBRARY)\n    if(target STREQUAL obs-browser)\n      set_target_properties(${target} PROPERTIES VERSION 0 SOVERSION ${OBS_SOVERSION})\n    else()\n      set_target_properties(\n        ${target}\n        PROPERTIES\n          VERSION 0\n          SOVERSION ${OBS_SOVERSION}\n          BUILD_RPATH \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_LIBRARY_DESTINATION}\"\n          INSTALL_RPATH \"${OBS_MODULE_RPATH}\"\n      )\n    endif()\n\n    if(${target} STREQUAL obspython OR ${target} STREQUAL obslua)\n      set(plugin_destination \"${OBS_SCRIPT_PLUGIN_DESTINATION}\")\n      set_property(TARGET ${target} PROPERTY INSTALL_RPATH \"$ORIGIN/;$ORIGIN/..\")\n    else()\n      set(plugin_destination \"${OBS_PLUGIN_DESTINATION}\")\n    endif()\n\n    install(\n      TARGETS ${target}\n      LIBRARY DESTINATION \"${plugin_destination}\" COMPONENT Runtime NAMELINK_COMPONENT Development\n    )\n\n    add_custom_command(\n      TARGET ${target}\n      POST_BUILD\n      COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${plugin_destination}\"\n      COMMAND\n        \"${CMAKE_COMMAND}\" -E copy_if_different \"$<TARGET_FILE:${target}>\"\n        \"${OBS_OUTPUT_DIR}/$<CONFIG>/${plugin_destination}\"\n      COMMENT \"Copy ${target} to plugin directory (${plugin_destination})\"\n      VERBATIM\n    )\n\n    if(${target} STREQUAL obspython)\n      add_custom_command(\n        TARGET ${target}\n        POST_BUILD\n        COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_SCRIPT_PLUGIN_DESTINATION}/\"\n        COMMAND\n          \"${CMAKE_COMMAND}\" -E copy_if_different \"$<TARGET_FILE_DIR:obspython>/obspython.py\"\n          \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_SCRIPT_PLUGIN_DESTINATION}/\"\n        COMMENT \"Add obspython import module\"\n      )\n\n      install(\n        FILES \"$<TARGET_FILE_DIR:obspython>/obspython.py\"\n        DESTINATION \"${OBS_SCRIPT_PLUGIN_DESTINATION}\"\n        COMPONENT Runtime\n      )\n    elseif(${target} STREQUAL obs-browser)\n      message(DEBUG \"Add Chromium Embedded Framework to project for obs-browser plugin...\")\n      if(TARGET CEF::Library)\n        get_target_property(imported_location CEF::Library IMPORTED_LOCATION_RELEASE)\n\n        if(imported_location)\n          cmake_path(GET imported_location PARENT_PATH cef_location)\n          cmake_path(GET cef_location PARENT_PATH cef_root_location)\n          add_custom_command(\n            TARGET ${target}\n            POST_BUILD\n            COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_PLUGIN_DESTINATION}/\"\n            COMMAND\n              \"${CMAKE_COMMAND}\" -E copy_if_different \"${imported_location}\" \"${cef_location}/chrome-sandbox\"\n              \"${cef_location}/libEGL.so\" \"${cef_location}/libGLESv2.so\" \"${cef_location}/libvk_swiftshader.so\"\n              \"${cef_location}/libvulkan.so.1\" \"${cef_location}/v8_context_snapshot.bin\"\n              \"${cef_location}/vk_swiftshader_icd.json\" \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_PLUGIN_DESTINATION}/\"\n            COMMAND\n              \"${CMAKE_COMMAND}\" -E copy_if_different \"${cef_root_location}/Resources/chrome_100_percent.pak\"\n              \"${cef_root_location}/Resources/chrome_200_percent.pak\" \"${cef_root_location}/Resources/icudtl.dat\"\n              \"${cef_root_location}/Resources/resources.pak\" \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_PLUGIN_DESTINATION}/\"\n            COMMAND\n              \"${CMAKE_COMMAND}\" -E copy_directory \"${cef_root_location}/Resources/locales\"\n              \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_PLUGIN_DESTINATION}/locales\"\n            COMMENT \"Add Chromium Embedded Framework to library directory\"\n          )\n\n          install(\n            FILES\n              \"${imported_location}\"\n              \"${cef_location}/chrome-sandbox\"\n              \"${cef_location}/libEGL.so\"\n              \"${cef_location}/libGLESv2.so\"\n              \"${cef_location}/libvk_swiftshader.so\"\n              \"${cef_location}/libvulkan.so.1\"\n              \"${cef_location}/v8_context_snapshot.bin\"\n              \"${cef_location}/vk_swiftshader_icd.json\"\n              \"${cef_root_location}/Resources/chrome_100_percent.pak\"\n              \"${cef_root_location}/Resources/chrome_200_percent.pak\"\n              \"${cef_root_location}/Resources/icudtl.dat\"\n              \"${cef_root_location}/Resources/resources.pak\"\n            DESTINATION \"${OBS_PLUGIN_DESTINATION}\"\n            COMPONENT Runtime\n          )\n\n          install(\n            DIRECTORY \"${cef_root_location}/Resources/locales\"\n            DESTINATION \"${OBS_PLUGIN_DESTINATION}\"\n            USE_SOURCE_PERMISSIONS\n            COMPONENT Runtime\n          )\n        endif()\n      endif()\n    endif()\n\n    set_property(GLOBAL APPEND PROPERTY OBS_MODULES_ENABLED ${target})\n  endif()\n\n  target_install_resources(${target})\nendfunction()\n\n# Helper function to add resources into bundle\nfunction(target_install_resources target)\n  message(DEBUG \"Installing resources for target ${target}...\")\n  if(EXISTS \"${CMAKE_CURRENT_SOURCE_DIR}/data\")\n    file(GLOB_RECURSE data_files \"${CMAKE_CURRENT_SOURCE_DIR}/data/*\")\n    foreach(data_file IN LISTS data_files)\n      cmake_path(\n        RELATIVE_PATH data_file\n        BASE_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/data/\"\n        OUTPUT_VARIABLE relative_path\n      )\n      cmake_path(GET relative_path PARENT_PATH relative_path)\n      target_sources(${target} PRIVATE \"${data_file}\")\n      source_group(\"Resources/${relative_path}\" FILES \"${data_file}\")\n    endforeach()\n\n    get_property(obs_module_list GLOBAL PROPERTY OBS_MODULES_ENABLED)\n    if(target IN_LIST obs_module_list)\n      set(target_destination \"${OBS_DATA_DESTINATION}/obs-plugins/${target}\")\n    elseif(target STREQUAL obs)\n      set(target_destination \"${OBS_DATA_DESTINATION}/obs-studio\")\n    else()\n      set(target_destination \"${OBS_DATA_DESTINATION}/${target}\")\n    endif()\n\n    install(\n      DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/data/\"\n      DESTINATION \"${target_destination}\"\n      USE_SOURCE_PERMISSIONS\n      COMPONENT Runtime\n    )\n\n    add_custom_command(\n      TARGET ${target}\n      POST_BUILD\n      COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}\"\n      COMMAND\n        \"${CMAKE_COMMAND}\" -E copy_directory \"${CMAKE_CURRENT_SOURCE_DIR}/data\"\n        \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}\"\n      COMMENT \"Copy ${target} resources to data directory (${target_destination})\"\n      VERBATIM\n    )\n  endif()\nendfunction()\n\n# Helper function to add a specific resource to a bundle\nfunction(target_add_resource target resource)\n  get_property(obs_module_list GLOBAL PROPERTY OBS_MODULES_ENABLED)\n  if(ARGN)\n    set(target_destination \"${ARGN}\")\n  elseif(${target} IN_LIST obs_module_list)\n    set(target_destination \"${OBS_DATA_DESTINATION}/obs-plugins/${target}\")\n  elseif(target STREQUAL obs)\n    set(target_destination \"${OBS_DATA_DESTINATION}/obs-studio\")\n  else()\n    set(target_destination \"${OBS_DATA_DESTINATION}/${target}\")\n  endif()\n\n  message(DEBUG \"Add resource ${resource} to target ${target} at destination ${target_destination}...\")\n\n  install(FILES \"${resource}\" DESTINATION \"${target_destination}\" COMPONENT Runtime)\n\n  add_custom_command(\n    TARGET ${target}\n    POST_BUILD\n    COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}/\"\n    COMMAND \"${CMAKE_COMMAND}\" -E copy \"${resource}\" \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}/\"\n    COMMENT \"Copy ${target} resource ${resource} to library directory (${target_destination})\"\n    VERBATIM\n  )\nendfunction()\n\n# target_export: Helper function to export target as CMake package\nfunction(target_export target)\n  set(exclude_variant \"\")\n  _target_export(${target})\n\n  if(EXISTS \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/linux/${target}.pc.in\")\n    message(DEBUG \"Generating pkgconfig file ${target}.pc.in...\")\n\n    install(CODE \"set(OBS_VERSION_CANONICAL ${OBS_VERSION_CANONICAL})\" COMPONENT Development)\n    install(CODE \"set(CMAKE_C_STANDARD ${CMAKE_C_STANDARD})\" COMPONENT Development)\n    install(\n      CODE\n        \"configure_file(\\\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/linux/${target}.pc.in\\\" \\\"${CMAKE_CURRENT_BINARY_DIR}/${target}.pc\\\" @ONLY)\"\n      COMPONENT Development\n    )\n\n    install(\n      FILES \"${CMAKE_CURRENT_BINARY_DIR}/${target}.pc\"\n      DESTINATION \"${OBS_LIBRARY_DESTINATION}/pkgconfig\"\n      COMPONENT Development\n    )\n  endif()\nendfunction()\n"
  },
  {
    "path": "cmake/linux/toolchain-aarch64-clang.cmake",
    "content": "set(CMAKE_SYSTEM_NAME Linux)\nset(CMAKE_SYSTEM_PROCESSOR arm64)\n\nset(CMAKE_C_COMPILER clang)\nset(CMAKE_C_COMPILER_TARGET aarch64-linux-gnu)\n\nset(CMAKE_CXX_COMPILER clang++)\nset(CMAKE_CXX_COMPILER_TARGET aarch64-linux-gnu)\n\nset(CMAKE_ASM_COMPILER clang)\nset(CMAKE_ASM_COMPILER_TARGET aarch64-linux-gnu)\n"
  },
  {
    "path": "cmake/linux/toolchain-aarch64-gcc.cmake",
    "content": "set(CMAKE_SYSTEM_NAME Linux)\nset(CMAKE_SYSTEM_PROCESSOR arm64)\n\nif(CROSS STREQUAL \"\")\n  set(CROSS aarch64-linux-gnu-)\nendif()\n\nif(NOT CMAKE_C_COMPILER)\n  set(CMAKE_C_COMPILER ${CROSS}gcc)\nendif()\n\nif(NOT CMAKE_CXX_COMPILER)\n  set(CMAKE_CXX_COMPILER ${CROSS}g++)\nendif()\n\nif(NOT CMAKE_ASM_COMPILER)\n  set(CMAKE_ASM_COMPILER ${CROSS}as)\nendif()\n"
  },
  {
    "path": "cmake/linux/toolchain-x86_64-gcc.cmake",
    "content": "set(CMAKE_SYSTEM_NAME Linux)\nset(CMAKE_SYSTEM_PROCESSOR x86_64)\n\nif(CROSS STREQUAL \"\")\n  set(CROSS x86_64-linux-gnu-)\nendif()\n\nif(NOT CMAKE_C_COMPILER)\n  set(CMAKE_C_COMPILER ${CROSS}gcc)\nendif()\n\nif(NOT CMAKE_CXX_COMPILER)\n  set(CMAKE_CXX_COMPILER ${CROSS}g++)\nendif()\n\nif(NOT CMAKE_ASM_COMPILER)\n  set(CMAKE_ASM_COMPILER ${CROSS}as)\nendif()\n"
  },
  {
    "path": "cmake/macos/buildspec.cmake",
    "content": "# OBS CMake macOS build dependencies module\n\ninclude_guard(GLOBAL)\n\ninclude(buildspec_common)\n\n# _check_dependencies_macos: Set up macOS slice for _check_dependencies\nfunction(_check_dependencies_macos)\n  set(arch universal)\n  set(platform macos-${arch})\n\n  set(dependencies_dir \"${CMAKE_CURRENT_SOURCE_DIR}/.deps\")\n  set(prebuilt_filename \"macos-deps-VERSION-ARCH-REVISION.tar.xz\")\n  set(prebuilt_destination \"obs-deps-VERSION-ARCH\")\n  set(qt6_filename \"macos-deps-qt6-VERSION-ARCH-REVISION.tar.xz\")\n  set(qt6_destination \"obs-deps-qt6-VERSION-ARCH\")\n  set(cef_filename \"cef_binary_VERSION_macos_ARCH_REVISION.tar.xz\")\n  set(cef_destination \"cef_binary_VERSION_macos_ARCH\")\n  set(dependencies_list prebuilt qt6 cef)\n\n  _check_dependencies(${dependencies_list})\n\n  execute_process(\n    COMMAND \"xattr\" -r -d com.apple.quarantine \"${dependencies_dir}/${destination}\"\n    RESULT_VARIABLE result\n    COMMAND_ERROR_IS_FATAL ANY\n  )\nendfunction()\n\n_check_dependencies_macos()\n"
  },
  {
    "path": "cmake/macos/compilerconfig.cmake",
    "content": "# OBS CMake macOS compiler configuration module\n\ninclude_guard(GLOBAL)\n\noption(ENABLE_COMPILER_TRACE \"Enable clang time-trace\" OFF)\nmark_as_advanced(ENABLE_COMPILER_TRACE)\n\nif(NOT XCODE)\n  message(FATAL_ERROR \"Building OBS Studio on macOS requires Xcode generator.\")\nendif()\n\ninclude(compiler_common)\n\nadd_compile_options(\"$<$<NOT:$<COMPILE_LANGUAGE:Swift>>:-fopenmp-simd>\")\n\nif(CMAKE_CXX_STANDARD GREATER_EQUAL 20)\n  add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fno-char8_t>)\nendif()\n\n# Ensure recent enough Xcode and platform SDK\nfunction(check_sdk_requirements)\n  set(obs_macos_minimum_sdk 15.0) # Keep in sync with Xcode\n  set(obs_macos_minimum_xcode 16.0) # Keep in sync with SDK\n  execute_process(\n    COMMAND xcrun --sdk macosx --show-sdk-platform-version\n    OUTPUT_VARIABLE obs_macos_current_sdk\n    RESULT_VARIABLE result\n    OUTPUT_STRIP_TRAILING_WHITESPACE\n  )\n  if(NOT result EQUAL 0)\n    message(\n      FATAL_ERROR\n      \"Failed to fetch macOS SDK version. \"\n      \"Ensure that the macOS SDK is installed and that xcode-select points at the Xcode developer directory.\"\n    )\n  endif()\n  message(DEBUG \"macOS SDK version: ${obs_macos_current_sdk}\")\n  if(obs_macos_current_sdk VERSION_LESS obs_macos_minimum_sdk)\n    message(\n      FATAL_ERROR\n      \"Your macOS SDK version (${obs_macos_current_sdk}) is too low. \"\n      \"The macOS ${obs_macos_minimum_sdk} SDK (Xcode ${obs_macos_minimum_xcode}) is required to build OBS.\"\n    )\n  endif()\n  execute_process(COMMAND xcrun --find xcodebuild OUTPUT_VARIABLE obs_macos_xcodebuild RESULT_VARIABLE result)\n  if(NOT result EQUAL 0)\n    message(\n      FATAL_ERROR\n      \"Xcode was not found. \"\n      \"Ensure you have installed Xcode and that xcode-select points at the Xcode developer directory.\"\n    )\n  endif()\n  message(DEBUG \"Path to xcodebuild binary: ${obs_macos_xcodebuild}\")\n  if(XCODE_VERSION VERSION_LESS obs_macos_minimum_xcode)\n    message(\n      FATAL_ERROR\n      \"Your Xcode version (${XCODE_VERSION}) is too low. Xcode ${obs_macos_minimum_xcode} is required to build OBS.\"\n    )\n  endif()\nendfunction()\n\ncheck_sdk_requirements()\n\n# Enable dSYM generator for release builds\nstring(APPEND CMAKE_C_FLAGS_RELEASE \" -g\")\nstring(APPEND CMAKE_CXX_FLAGS_RELEASE \" -g\")\nstring(APPEND CMAKE_OBJC_FLAGS_RELEASE \" -g\")\nstring(APPEND CMAKE_OBJCXX_FLAGS_RELEASE \" -g\")\n\n# Default ObjC compiler options used by Xcode:\n#\n# * -Wno-implicit-atomic-properties\n# * -Wno-objc-interface-ivars\n# * -Warc-repeated-use-of-weak\n# * -Wno-arc-maybe-repeated-use-of-weak\n# * -Wimplicit-retain-self\n# * -Wduplicate-method-match\n# * -Wshadow\n# * -Wfloat-conversion\n# * -Wobjc-literal-conversion\n# * -Wno-selector\n# * -Wno-strict-selector-match\n# * -Wundeclared-selector\n# * -Wdeprecated-implementations\n# * -Wprotocol\n# * -Werror=block-capture-autoreleasing\n# * -Wrange-loop-analysis\n\n# Default ObjC++ compiler options used by Xcode:\n#\n# * -Wno-non-virtual-dtor\n\nadd_compile_definitions(\n  $<$<CONFIG:DEBUG>:DEBUG>\n  $<$<NOT:$<COMPILE_LANGUAGE:Swift>>:$<$<CONFIG:DEBUG>:_DEBUG>>\n  $<$<NOT:$<COMPILE_LANGUAGE:Swift>>:SIMDE_ENABLE_OPENMP>\n)\n\nif(ENABLE_COMPILER_TRACE)\n  add_compile_options(\n    $<$<NOT:$<COMPILE_LANGUAGE:Swift>>:-ftime-trace>\n    \"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -debug-time-expression-type-checking>\"\n    \"$<$<COMPILE_LANGUAGE:Swift>:SHELL:-Xfrontend -debug-time-function-bodies>\"\n  )\n  add_link_options(LINKER:-print_statistics)\nendif()\n"
  },
  {
    "path": "cmake/macos/defaults.cmake",
    "content": "# OBS CMake macOS defaults module\n\ninclude_guard(GLOBAL)\n\n# Set empty codesigning team if not specified as cache variable\nif(NOT OBS_CODESIGN_TEAM)\n  set(OBS_CODESIGN_TEAM \"\" CACHE STRING \"OBS code signing team for macOS\" FORCE)\n\n  # Set ad-hoc codesigning identity if not specified as cache variable\n  if(NOT OBS_CODESIGN_IDENTITY)\n    set(OBS_CODESIGN_IDENTITY \"-\" CACHE STRING \"OBS code signing identity for macOS\" FORCE)\n  endif()\nendif()\n\ninclude(xcode)\n\ninclude(buildspec)\n\n# SWIG hard codes the directory to its library directory at compile time. As obs-deps need to be relocatable, we need to\n# force SWIG to look for its files in a directory relative to the PREFIX_PATH. The best way to ensure this is to set the\n# SWIG_LIB environment variable.\nforeach(path IN LISTS CMAKE_PREFIX_PATH)\n  if(NOT DEFINED ENV{SWIG_LIB} AND EXISTS \"${path}/bin/swig\")\n    set(ENV{SWIG_LIB} \"${path}/share/swig/CURRENT\")\n    break()\n  endif()\nendforeach()\n\n# Enable find_package targets to become globally available targets\nset(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL TRUE)\n# Enable RPATH support for generated binaries\nset(CMAKE_MACOSX_RPATH TRUE)\n# Use RPATHs from build tree _in_ the build tree\nset(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)\n# Do not add default linker search paths to RPATH\nset(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)\n# Use common bundle-relative RPATH for installed targets\nset(CMAKE_INSTALL_RPATH \"@executable_path/../Frameworks\")\n# Ignore any dependent packages installed via Homebrew\nlist(APPEND CMAKE_IGNORE_PREFIX_PATH \"/opt/homebrew\" \"/usr/local\")\n\n# Used for library exports only (obs-frontend-api)\nset(OBS_LIBRARY_DESTINATION \"lib\")\nset(OBS_INCLUDE_DESTINATION \"include/obs\")\nset(OBS_CMAKE_DESTINATION \"lib/cmake\")\n\nconfigure_file(\n  \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/resources/package.applescript\"\n  \"${CMAKE_CURRENT_BINARY_DIR}/package.applescript\"\n  @ONLY\n)\n"
  },
  {
    "path": "cmake/macos/helpers.cmake",
    "content": "# OBS CMake macOS helper functions module\n\ninclude_guard(GLOBAL)\n\ninclude(helpers_common)\n\n# set_target_xcode_properties: Sets Xcode-specific target attributes\nfunction(set_target_xcode_properties target)\n  set(options \"\")\n  set(oneValueArgs \"\")\n  set(multiValueArgs PROPERTIES)\n  cmake_parse_arguments(PARSE_ARGV 0 _STXP \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\")\n\n  message(DEBUG \"Setting Xcode properties for target ${target}...\")\n\n  while(_STXP_PROPERTIES)\n    list(POP_FRONT _STXP_PROPERTIES key value)\n    set_property(TARGET ${target} PROPERTY XCODE_ATTRIBUTE_${key} \"${value}\")\n  endwhile()\nendfunction()\n\n# set_target_properties_obs: Set target properties for use in obs-studio\nfunction(set_target_properties_obs target)\n  set(options \"\")\n  set(oneValueArgs \"\")\n  set(multiValueArgs PROPERTIES)\n  cmake_parse_arguments(PARSE_ARGV 0 _STPO \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\")\n\n  message(DEBUG \"Setting additional properties for target ${target}...\")\n\n  while(_STPO_PROPERTIES)\n    list(POP_FRONT _STPO_PROPERTIES key value)\n    set_property(TARGET ${target} PROPERTY ${key} \"${value}\")\n  endwhile()\n\n  get_target_property(target_type ${target} TYPE)\n\n  string(TIMESTAMP CURRENT_YEAR \"%Y\")\n\n  # Target is a GUI or CLI application\n  if(target_type STREQUAL EXECUTABLE)\n    if(target STREQUAL obs-studio)\n      set_target_properties(\n        ${target}\n        PROPERTIES\n          OUTPUT_NAME OBS\n          MACOSX_BUNDLE TRUE\n          MACOSX_BUNDLE_INFO_PLIST \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Info.plist.in\"\n          XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY YES\n          XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY YES\n          XCODE_EMBED_PLUGINS_REMOVE_HEADERS_ON_COPY YES\n          XCODE_EMBED_PLUGINS_CODE_SIGN_ON_COPY YES\n      )\n\n      set_target_xcode_properties(\n        ${target}\n        PROPERTIES PRODUCT_BUNDLE_IDENTIFIER com.obsproject.obs-studio\n                   PRODUCT_NAME OBS\n                   ASSETCATALOG_COMPILER_APPICON_NAME AppIcon\n                   CURRENT_PROJECT_VERSION ${OBS_BUILD_NUMBER}\n                   MARKETING_VERSION ${OBS_VERSION_CANONICAL}\n                   GENERATE_INFOPLIST_FILE YES\n                   COPY_PHASE_STRIP NO\n                   CLANG_ENABLE_OBJC_ARC YES\n                   SKIP_INSTALL NO\n                   INSTALL_PATH \"$(LOCAL_APPS_DIR)\"\n                   INFOPLIST_KEY_CFBundleDisplayName \"OBS Studio\"\n                   INFOPLIST_KEY_NSHumanReadableCopyright \"(c) 2012-${CURRENT_YEAR} Lain Bailey\"\n                   INFOPLIST_KEY_NSCameraUsageDescription \"OBS needs to access the camera to enable camera sources to work.\"\n                   INFOPLIST_KEY_NSMicrophoneUsageDescription \"OBS needs to access the microphone to enable audio input.\"\n                   INFOPLIST_KEY_NSAppleEventsUsageDescription \"OBS needs to access background events to enable hotkeys while not in focus.\"\n      )\n\n      get_property(obs_dependencies GLOBAL PROPERTY _OBS_DEPENDENCIES)\n      add_dependencies(${target} ${obs_dependencies})\n\n      get_property(obs_frameworks GLOBAL PROPERTY _OBS_FRAMEWORKS)\n      set_property(TARGET ${target} APPEND PROPERTY XCODE_EMBED_FRAMEWORKS ${obs_frameworks})\n\n      if(SPARKLE_APPCAST_URL AND SPARKLE_PUBLIC_KEY)\n        set_property(TARGET ${target} APPEND PROPERTY XCODE_EMBED_FRAMEWORKS ${SPARKLE})\n      endif()\n\n      if(TARGET mac-syphon)\n        set_property(TARGET ${target} APPEND PROPERTY XCODE_EMBED_FRAMEWORKS ${SYPHON})\n      endif()\n\n      get_property(obs_executables GLOBAL PROPERTY _OBS_EXECUTABLES)\n      add_dependencies(${target} ${obs_executables})\n      foreach(executable IN LISTS obs_executables)\n        set_target_xcode_properties(${executable} PROPERTIES INSTALL_PATH\n                                    \"$(LOCAL_APPS_DIR)/$<TARGET_BUNDLE_DIR_NAME:${target}>/Contents/MacOS\"\n        )\n\n        add_custom_command(\n          TARGET ${target}\n          POST_BUILD\n          COMMAND\n            \"${CMAKE_COMMAND}\" -E copy_if_different \"$<TARGET_FILE:${executable}>\"\n            \"$<TARGET_BUNDLE_CONTENT_DIR:${target}>/MacOS/\"\n          COMMENT \"Copy ${executable} to application bundle\"\n        )\n      endforeach()\n\n      if(VIRTUALCAM_DEVICE_UUID AND VIRTUALCAM_SOURCE_UUID AND VIRTUALCAM_SINK_UUID)\n        set(has_virtualcam_uuids TRUE)\n      else()\n        set(has_virtualcam_uuids FALSE)\n      endif()\n\n      if(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE STREQUAL Automatic)\n        if(has_virtualcam_uuids)\n          set(entitlements_file \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements-extension.plist\")\n        else()\n          set(entitlements_file \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist\")\n        endif()\n      else()\n        if(has_virtualcam_uuids AND OBS_PROVISIONING_PROFILE)\n          set(entitlements_file \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements-extension.plist\")\n          set_target_properties(\n            ${target}\n            PROPERTIES XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER \"${OBS_PROVISIONING_PROFILE}\"\n          )\n          configure_file(cmake/macos/exportOptions-extension.plist.in ${CMAKE_BINARY_DIR}/exportOptions.plist)\n        else()\n          set(entitlements_file \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist\")\n          configure_file(cmake/macos/exportOptions.plist.in ${CMAKE_BINARY_DIR}/exportOptions.plist)\n        endif()\n      endif()\n\n      if(NOT EXISTS \"${entitlements_file}\")\n        message(FATAL_ERROR \"Target ${target} is missing an entitlements file in its cmake directory.\")\n      endif()\n\n      set_target_properties(${target} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS \"${entitlements_file}\")\n\n      add_custom_command(\n        TARGET ${target}\n        POST_BUILD\n        COMMAND /bin/ln -fs obs-frontend-api.dylib libobs-frontend-api.1.dylib\n        WORKING_DIRECTORY \"$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Frameworks\"\n        COMMENT \"Create symlink for legacy obs-frontend-api\"\n      )\n\n      if(EXISTS \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/qt.conf\")\n        target_add_resource(${target} \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/qt.conf\")\n      endif()\n\n      target_add_resource(${target} \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/Assets.xcassets\")\n      target_add_resource(${target} \"${CMAKE_CURRENT_SOURCE_DIR}/../AUTHORS\")\n\n      if(TARGET obs-dal-plugin)\n        add_custom_command(\n          TARGET ${target}\n          POST_BUILD\n          COMMAND\n            \"${CMAKE_COMMAND}\" -E copy_directory \"$<TARGET_BUNDLE_DIR:obs-dal-plugin>\"\n            \"$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Resources/$<TARGET_BUNDLE_DIR_NAME:obs-dal-plugin>\"\n          COMMENT \"Add OBS DAL plugin to application bundle\"\n        )\n      endif()\n\n      if(TARGET obspython)\n        add_custom_command(\n          TARGET ${target}\n          POST_BUILD\n          COMMAND\n            \"${CMAKE_COMMAND}\" -E copy_if_different \"$<TARGET_FILE_DIR:obspython>/obspython.py\"\n            \"$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Resources\"\n          COMMENT \"Add OBS::python import module\"\n        )\n      endif()\n\n      if(\n        TARGET mac-camera-extension\n        AND (CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE STREQUAL Automatic OR OBS_PROVISIONING_PROFILE)\n      )\n        target_enable_feature(mac-camera-extension \"macOS CMIO Camera Extension\")\n        add_custom_command(\n          TARGET ${target}\n          POST_BUILD\n          COMMAND\n            \"${CMAKE_COMMAND}\" -E copy_directory \"$<TARGET_BUNDLE_DIR:mac-camera-extension>\"\n            \"$<TARGET_BUNDLE_CONTENT_DIR:${target}>/Library/SystemExtensions/$<TARGET_BUNDLE_DIR_NAME:mac-camera-extension>\"\n          COMMENT \"Add Camera Extension to application bundle\"\n        )\n      else()\n        target_disable_feature(mac-camera-extension \"macOS CMIO Camera Extension\")\n      endif()\n\n      _bundle_dependencies(${target})\n\n      install(TARGETS ${target} BUNDLE DESTINATION \".\" COMPONENT Application)\n    elseif(${target} STREQUAL mac-camera-extension)\n      set_target_properties(${target} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)\n      set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target})\n    elseif(${target} STREQUAL obs-ffmpeg-mux)\n      if(OBS_CODESIGN_IDENTITY STREQUAL \"-\")\n        set_target_xcode_properties(${target} PROPERTIES ENABLE_HARDENED_RUNTIME NO)\n      endif()\n\n      set_target_xcode_properties(${target} PROPERTIES SKIP_INSTALL NO)\n      set_property(GLOBAL APPEND PROPERTY _OBS_EXECUTABLES ${target})\n      set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target})\n    else()\n      set_property(TARGET ${target} PROPERTY XCODE_ATTRIBUTE_SKIP_INSTALL NO)\n      set_property(GLOBAL APPEND PROPERTY _OBS_EXECUTABLES ${target})\n      set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target})\n      _add_entitlements()\n    endif()\n  elseif(target_type STREQUAL SHARED_LIBRARY)\n    set_target_properties(\n      ${target}\n      PROPERTIES\n        NO_SONAME TRUE\n        MACHO_COMPATIBILITY_VERSION 1.0\n        MACHO_CURRENT_VERSION ${OBS_VERSION_MAJOR}\n        SOVERSION 0\n        VERSION 0\n    )\n\n    set_target_xcode_properties(\n      ${target}\n      PROPERTIES DYLIB_COMPATIBILITY_VERSION 1.0\n                 DYLIB_CURRENT_VERSION ${OBS_VERSION_MAJOR}\n                 PRODUCT_NAME ${target}\n                 PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target}\n                 SKIP_INSTALL YES\n    )\n\n    get_target_property(is_framework ${target} FRAMEWORK)\n    if(is_framework)\n      set_target_properties(\n        ${target}\n        PROPERTIES FRAMEWORK_VERSION A MACOSX_FRAMEWORK_IDENTIFIER com.obsproject.${target}\n      )\n\n      set_target_xcode_properties(\n        ${target}\n        PROPERTIES CODE_SIGN_IDENTITY \"\"\n                   DEVELOPMENT_TEAM \"\"\n                   SKIP_INSTALL YES\n                   PRODUCT_NAME ${target}\n                   PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target}\n                   CURRENT_PROJECT_VERSION ${OBS_BUILD_NUMBER}\n                   MARKETING_VERSION ${OBS_VERSION_CANONICAL}\n                   GENERATE_INFOPLIST_FILE YES\n                   INFOPLIST_FILE \"\"\n                   INFOPLIST_KEY_CFBundleDisplayName ${target}\n                   INFOPLIST_KEY_NSHumanReadableCopyright \"(c) 2012-${CURRENT_YEAR} Lain Bailey\"\n      )\n    endif()\n\n    set_property(GLOBAL APPEND PROPERTY _OBS_FRAMEWORKS ${target})\n    set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target})\n  elseif(target_type STREQUAL MODULE_LIBRARY)\n    if(target STREQUAL obspython)\n      set_target_xcode_properties(\n        ${target}\n        PROPERTIES PRODUCT_NAME ${target}\n                   PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target}\n      )\n    elseif(target STREQUAL obslua)\n      set_target_xcode_properties(\n        ${target}\n        PROPERTIES PRODUCT_NAME ${target}\n                   PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target}\n      )\n    elseif(target STREQUAL obs-dal-plugin)\n      set_target_properties(${target} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)\n      set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target})\n      return()\n    else()\n      set_target_properties(${target} PROPERTIES BUNDLE TRUE BUNDLE_EXTENSION plugin)\n\n      set_target_xcode_properties(\n        ${target}\n        PROPERTIES PRODUCT_NAME ${target}\n                   PRODUCT_BUNDLE_IDENTIFIER com.obsproject.${target}\n                   CURRENT_PROJECT_VERSION ${OBS_BUILD_NUMBER}\n                   MARKETING_VERSION ${OBS_VERSION_CANONICAL}\n                   GENERATE_INFOPLIST_FILE YES\n                   INFOPLIST_KEY_CFBundleDisplayName ${target}\n                   INFOPLIST_KEY_NSHumanReadableCopyright \"(c) 2012-${CURRENT_YEAR} Lain Bailey\"\n      )\n\n      if(target STREQUAL obs-browser)\n        # Good-enough for now as there are no other variants - in _theory_ we should only add the appropriate variant,\n        # but that is only known at project generation and not build system configuration.\n        get_target_property(imported_location CEF::Library IMPORTED_LOCATION_RELEASE)\n        if(imported_location)\n          list(APPEND cef_items \"${imported_location}\")\n        endif()\n\n        foreach(helper IN ITEMS _gpu _plugin _renderer \"\")\n          if(TARGET OBS::browser-helper${helper})\n            set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES OBS::browser-helper${helper})\n            list(APPEND cef_items OBS::browser-helper${helper})\n          endif()\n        endforeach()\n\n        set_property(GLOBAL APPEND PROPERTY _OBS_FRAMEWORKS ${cef_items})\n      endif()\n    endif()\n\n    set_property(GLOBAL APPEND PROPERTY OBS_MODULES_ENABLED ${target})\n    set_property(GLOBAL APPEND PROPERTY _OBS_DEPENDENCIES ${target})\n  endif()\n\n  target_install_resources(${target})\n\n  get_target_property(target_sources ${target} SOURCES)\n  set(target_ui_files ${target_sources})\n  list(FILTER target_ui_files INCLUDE REGEX \".+\\\\.(ui|qrc)\")\n  source_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"UI Files\" FILES ${target_ui_files})\n\n  if(${target} STREQUAL libobs)\n    set(target_source_files ${target_sources})\n    set(target_header_files ${target_sources})\n    list(FILTER target_source_files INCLUDE REGEX \".+\\\\.(m|c[cp]?p?|swift)\")\n    list(FILTER target_header_files INCLUDE REGEX \".+\\\\.h(pp)?\")\n\n    source_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"Source Files\" FILES ${target_source_files})\n    source_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"Header Files\" FILES ${target_header_files})\n  endif()\nendfunction()\n\n# _check_entitlements: Macro to check if project ships with entitlements plist\nmacro(_check_entitlements)\n  if(NOT EXISTS \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist\")\n    message(FATAL_ERROR \"Target ${target} is missing an entitlements.plist in its cmake directory.\")\n  endif()\nendmacro()\n\n# _add_entitlements: Macro to add entitlements shipped with project\nmacro(_add_entitlements)\n  if(EXISTS \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist\")\n    set_target_xcode_properties(\n      ${target}\n      PROPERTIES CODE_SIGN_ENTITLEMENTS \"${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos/entitlements.plist\"\n    )\n  endif()\nendmacro()\n\n# target_export: Helper function to export target as CMake package\nfunction(target_export target)\n  # Exclude CMake package from 'ALL' target\n  set(exclude_variant EXCLUDE_FROM_ALL)\n  _target_export(${target})\nendfunction()\n\n# target_install_resources: Helper function to add resources into bundle\nfunction(target_install_resources target)\n  message(DEBUG \"Installing resources for target ${target}...\")\n  if(EXISTS \"${CMAKE_CURRENT_SOURCE_DIR}/data\")\n    file(GLOB_RECURSE data_files \"${CMAKE_CURRENT_SOURCE_DIR}/data/*\")\n    list(FILTER data_files EXCLUDE REGEX \"\\\\.DS_Store$\")\n    foreach(data_file IN LISTS data_files)\n      cmake_path(\n        RELATIVE_PATH data_file\n        BASE_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/data/\"\n        OUTPUT_VARIABLE relative_path\n      )\n      cmake_path(GET relative_path PARENT_PATH relative_path)\n      target_sources(${target} PRIVATE \"${data_file}\")\n      set_property(SOURCE \"${data_file}\" PROPERTY MACOSX_PACKAGE_LOCATION \"Resources/${relative_path}\")\n      source_group(\"Resources/${relative_path}\" FILES \"${data_file}\")\n    endforeach()\n  endif()\nendfunction()\n\n# target_add_resource: Helper function to add a specific resource to a bundle\nfunction(target_add_resource target resource)\n  message(DEBUG \"Add resource ${resource} to target ${target} at destination ${destination}...\")\n  target_sources(${target} PRIVATE \"${resource}\")\n  set_property(SOURCE \"${resource}\" PROPERTY MACOSX_PACKAGE_LOCATION Resources)\n  source_group(\"Resources\" FILES \"${resource}\")\nendfunction()\n\n# _bundle_dependencies: Resolve 3rd party dependencies and add them to macOS app bundle\nfunction(_bundle_dependencies target)\n  message(DEBUG \"Discover dependencies of target ${target}...\")\n  set(found_dependencies)\n  find_dependencies(TARGET ${target} FOUND_VAR found_dependencies)\n\n  get_property(obs_module_list GLOBAL PROPERTY OBS_MODULES_ENABLED)\n  list(LENGTH obs_module_list num_modules)\n  if(num_modules GREATER 0)\n    add_dependencies(${target} ${obs_module_list})\n    set_property(TARGET ${target} APPEND PROPERTY XCODE_EMBED_PLUGINS ${obs_module_list})\n    foreach(module IN LISTS obs_module_list)\n      find_dependencies(TARGET ${module} FOUND_VAR found_dependencies)\n    endforeach()\n  endif()\n\n  list(REMOVE_DUPLICATES found_dependencies)\n\n  set(library_paths)\n  set(plugins_list)\n  file(GLOB sdk_library_paths /Applications/Xcode*.app)\n  set(system_library_path \"/usr/lib/\")\n\n  foreach(library IN LISTS found_dependencies)\n    get_target_property(library_type ${library} TYPE)\n    get_target_property(is_framework ${library} FRAMEWORK)\n    get_target_property(is_imported ${library} IMPORTED)\n\n    if(is_imported)\n      get_target_property(imported_location ${library} LOCATION)\n      if(NOT imported_location)\n        continue()\n      endif()\n\n      set(is_xcode_framework FALSE)\n      set(is_system_framework FALSE)\n\n      foreach(sdk_library_path IN LISTS sdk_library_paths)\n        if(is_xcode_framework)\n          break()\n        endif()\n        cmake_path(IS_PREFIX sdk_library_path \"${imported_location}\" is_xcode_framework)\n      endforeach()\n      cmake_path(IS_PREFIX system_library_path \"${imported_location}\" is_system_framework)\n\n      if(is_system_framework OR is_xcode_framework)\n        continue()\n      elseif(is_framework)\n        file(REAL_PATH \"../../..\" library_location BASE_DIRECTORY \"${imported_location}\")\n      elseif(NOT library_type STREQUAL \"STATIC_LIBRARY\")\n        if(NOT imported_location MATCHES \".+\\\\.a\")\n          set(library_location \"${imported_location}\")\n        else()\n          continue()\n        endif()\n      else()\n        continue()\n      endif()\n\n      if(library MATCHES \"Qt6?::.+\")\n        find_qt_plugins(COMPONENT ${library} TARGET ${target} FOUND_VAR plugins_list)\n      endif()\n      list(APPEND library_paths ${library_location})\n    elseif(NOT is_imported AND library_type STREQUAL \"SHARED_LIBRARY\")\n      list(APPEND library_paths ${library})\n    endif()\n  endforeach()\n\n  list(REMOVE_DUPLICATES plugins_list)\n  foreach(plugin IN LISTS plugins_list)\n    cmake_path(GET plugin PARENT_PATH plugin_path)\n    set(plugin_base_dir \"${plugin_path}/../\")\n    cmake_path(SET plugin_stem_dir NORMALIZE \"${plugin_base_dir}\")\n    cmake_path(RELATIVE_PATH plugin_path BASE_DIRECTORY \"${plugin_stem_dir}\" OUTPUT_VARIABLE plugin_file_name)\n    target_sources(${target} PRIVATE \"${plugin}\")\n    set_source_files_properties(\n      \"${plugin}\"\n      PROPERTIES MACOSX_PACKAGE_LOCATION \"plugins/${plugin_file_name}\" XCODE_FILE_ATTRIBUTES \"CodeSignOnCopy\"\n    )\n    source_group(\"Qt plugins\" FILES \"${plugin}\")\n  endforeach()\n\n  list(REMOVE_DUPLICATES library_paths)\n  set_property(TARGET ${target} APPEND PROPERTY XCODE_EMBED_FRAMEWORKS ${library_paths})\nendfunction()\n"
  },
  {
    "path": "cmake/macos/resources/package.applescript",
    "content": "on run (volumeName)\n    tell application \"Finder\"\n        tell disk (volumeName as string)\n            open\n\n            set theXOrigin to 100\n            set theYOrigin to 100\n            set theWidth to 540\n            set theHeight to 380\n\n            set theBottomRightX to (theXOrigin + theWidth)\n            set theBottomRightY to (theYOrigin + theHeight)\n            set dsStore to \"\\\"\" & \"/Volumes/\" & volumeName & \"/\" & \".DS_STORE\\\"\"\n\n            tell container window\n                set current view to icon view\n                set toolbar visible to false\n                set statusbar visible to false\n                set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY}\n                set statusbar visible to false\n                set position of every item to {theBottomRightX + 100, 100}\n            end tell\n\n            set opts to the icon view options of container window\n            tell opts\n                set icon size to 96\n                set text size to 16\n                set arrangement to not arranged\n            end tell\n            set background picture of opts to file \".background:background.tiff\"\n            set position of item \"OBS.app\" to {124, 180}\n            set position of item \"Applications\" to {416, 180}\n            close\n            open\n            -- Force saving of the size\n            delay 1\n\n            tell container window\n                set statusbar visible to false\n                set the bounds to {theXOrigin, theYOrigin, theBottomRightX - 10, theBottomRightY - 10}\n            end tell\n        end tell\n\n        delay 1\n\n        tell disk (volumeName as string)\n            tell container window\n                set statusbar visible to false\n                set the bounds to {theXOrigin, theYOrigin, theBottomRightX, theBottomRightY}\n            end tell\n        end tell\n\n        --give the finder some time to write the .DS_Store file\n        delay 3\n\n        set waitTime to 0\n        set ejectMe to false\n        repeat while ejectMe is false\n            delay 1\n            set waitTime to waitTime + 1\n\n            if (do shell script \"[ -f \" & dsStore & \" ]; echo $?\") = \"0\" then set ejectMe to true\n        end repeat\n        log \"    >   Waited \" & waitTime & \" seconds for .DS_STORE to be created.\"\n\n        tell disk (volumeName as string)\n            close\n        end tell\n    end tell\nend run\n"
  },
  {
    "path": "cmake/macos/xcode.cmake",
    "content": "# OBS CMake macOS Xcode module\n\ninclude_guard(GLOBAL)\n\nset(CMAKE_XCODE_GENERATE_SCHEME TRUE)\n\n# Set project variables\nset(CMAKE_XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION ${OBS_BUILD_NUMBER})\nset(CMAKE_XCODE_ATTRIBUTE_DYLIB_COMPATIBILITY_VERSION 1.0.0)\nset(CMAKE_XCODE_ATTRIBUTE_MARKETING_VERSION ${OBS_VERSION_CANONICAL})\n\n# Set deployment target\nset(CMAKE_XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET})\n\nif(NOT OBS_PROVISIONING_PROFILE)\n  set(OBS_PROVISIONING_PROFILE \"\" CACHE STRING \"OBS provisioning profile name for macOS\" FORCE)\nelse()\n  set(CMAKE_XCODE_ATTRIBUTE_PROVISIONING_PROFILE_NAME \"${OBS_PROVISIONING_PROFILE}\")\nendif()\n\nif(NOT OBS_CODESIGN_TEAM)\n  # Switch to manual codesigning if no codesigning team is provided\n  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual)\n  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY \"${OBS_CODESIGN_IDENTITY}\")\nelse()\n  if(OBS_CODESIGN_IDENTITY AND NOT OBS_CODESIGN_IDENTITY STREQUAL \"-\")\n    # Switch to manual codesigning if a non-adhoc codesigning identity is provided\n    set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual)\n    set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY \"${OBS_CODESIGN_IDENTITY}\")\n  else()\n    # Switch to automatic codesigning via valid team ID\n    set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE Automatic)\n    set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY \"Apple Development\")\n  endif()\n  set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM \"${OBS_CODESIGN_TEAM}\")\nendif()\n\n# Only create a single Xcode project file\nset(CMAKE_XCODE_GENERATE_TOP_LEVEL_PROJECT_ONLY TRUE)\n# Add all libraries to project link phase (lets Xcode handle linking)\nset(CMAKE_XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION)\n\n# Enable codesigning with secure timestamp when not in Debug configuration (required for Notarization)\nset(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=Release] \"--timestamp\")\nset(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=RelWithDebInfo] \"--timestamp\")\nset(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS[variant=MinSizeRel] \"--timestamp\")\n\n# Enable codesigning with hardened runtime option when not in Debug configuration (required for Notarization)\nset(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=Release] YES)\nset(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=RelWithDebInfo] YES)\nset(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME[variant=MinSizeRel] YES)\n\n# Disable injection of Xcode's base entitlements used for debugging when not in Debug configuration (required for\n# Notarization)\nset(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=Release] NO)\nset(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=RelWithDebInfo] NO)\nset(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_INJECT_BASE_ENTITLEMENTS[variant=MinSizeRel] NO)\n\n# Use Swift version 5.0 by default\nset(CMAKE_XCODE_ATTRIBUTE_SWIFT_VERSION 5.0)\n\n# Use DWARF with separate dSYM files when in Release or MinSizeRel configuration.\n#\n# * Currently overruled by CMake's Xcode generator, requires adding '-g' flag to raw compiler command line for desired\n#   output configuration. Report to KitWare.\n#\nset(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Debug] dwarf)\nset(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=RelWithDebInfo] dwarf)\nset(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=Release] dwarf-with-dsym)\nset(CMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT[variant=MinSizeRel] dwarf-with-dsym)\n\n# Make all symbols hidden by default (currently overridden by CMake's compiler flags)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_INLINES_ARE_PRIVATE_EXTERN YES)\n\n# Strip unused code\nset(CMAKE_XCODE_ATTRIBUTE_DEAD_CODE_STRIPPING YES)\n\n# Build active architecture only in Debug configuration\nset(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] YES)\n\n# Enable testability in Debug configuration\nset(CMAKE_XCODE_ATTRIBUTE_ENABLE_TESTABILITY[variant=Debug] YES)\n\n# Disable using ARC in ObjC by default (OBS does not support this - yet)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC NO)\n# Enable weak references in manual retain release\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_WEAK YES)\n# Disable strict aliasing\nset(CMAKE_XCODE_ATTRIBUTE_GCC_STRICT_ALIASING NO)\n\n# Set C++ language default to c17\n#\n# * CMake explicitly sets the version via compiler flag when transitive dependencies require specific compiler feature\n#   set, resulting in the flag being added twice. Report to KitWare as a feature request for Xcode generator\n#\n# set(CMAKE_XCODE_ATTRIBUTE_GCC_C_LANGUAGE_STANDARD c17)\n#\n# Set C++ language default to c++17\n#\n# * See above. Report to KitWare as a feature request for Xcode generator\n#\n# set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD c++17)\n\n# Disable support for module imports in ObjC\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES NO)\n# Disable automatic linking of imported modules in ObjC\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_MODULES_AUTOLINK NO)\n# Enable strict msg_send rules for ObjC\nset(CMAKE_XCODE_ATTRIBUTE_ENABLE_STRICT_OBJC_MSGSEND YES)\n\n# Set default warnings for ObjC and C++\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING YES_ERROR)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_BOOL_CONVERSION YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_COMMA YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_CONSTANT_CONVERSION YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_EMPTY_BODY YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_ENUM_CONVERSION YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_INFINITE_RECURSION YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_INT_CONVERSION YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_NON_LITERAL_NULL_CONVERSION YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_OBJC_LITERAL_CONVERSION YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_RANGE_LOOP_ANALYSIS YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_STRICT_PROTOTYPES NO)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION NO)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_SUSPICIOUS_MOVE YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN_UNREACHABLE_CODE YES)\nset(CMAKE_XCODE_ATTRIBUTE_CLANG_WARN__DUPLICATE_METHOD_MATCH YES)\n\n# Set default warnings for C and C++\nset(CMAKE_XCODE_ATTRIBUTE_GCC_NO_COMMON_BLOCKS YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_64_TO_32_BIT_CONVERSION YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS NO)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_ABOUT_MISSING_NEWLINE YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_ABOUT_RETURN_TYPE YES_ERROR)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_CHECK_SWITCH_STATEMENTS YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_FOUR_CHARACTER_CONSTANTS YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_SHADOW NO)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_SIGN_COMPARE YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_TYPECHECK_CALLS_TO_PRINTF YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNDECLARED_SELECTOR YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNINITIALIZED_AUTOS YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNUSED_FUNCTION NO)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNUSED_PARAMETER YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNUSED_VALUE YES)\nset(CMAKE_XCODE_ATTRIBUTE_GCC_WARN_UNUSED_VARIABLE YES)\n\n# Add additional warning compiler flags\nset(CMAKE_XCODE_ATTRIBUTE_WARNING_CFLAGS \"-Wvla -Wformat-security -Wno-error=shorten-64-to-32\")\n\nif(CMAKE_COMPILE_WARNING_AS_ERROR)\n  set(CMAKE_XCODE_ATTRIBUTE_GCC_TREAT_WARNINGS_AS_ERRORS YES)\nendif()\n\n# Enable color diagnostics\nset(CMAKE_COLOR_DIAGNOSTICS TRUE)\n\n# Disable usage of RPATH in build or install configurations\nset(CMAKE_SKIP_RPATH TRUE)\n# Have Xcode set default RPATH entries\nset(CMAKE_XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS \"@executable_path/../Frameworks\")\n"
  },
  {
    "path": "cmake/windows/FindPython.cmake",
    "content": "#[=======================================================================[.rst\nFindPython\n----------\n\nFindModule for Python import libraries and header on Windows\n\n.. versionchanged:: 3.0\n  Updated FindModule to CMake standards\n\nImported Targets\n^^^^^^^^^^^^^^^^\n\n.. versionadded:: 2.0\n\nThis module defines the :prop_tgt:`IMPORTED` target ``Python::Python``.\n\nResult Variables\n^^^^^^^^^^^^^^^^\n\nThis module sets the following variables:\n\n``Python_FOUND``\n  True, if all required components and the core library were found.\n``Python_VERSION``\n  Detected version of found Python libraries.\n``Python_INCLUDE_DIRS``\n  Include directories needed for Python.\n``Python_LIBRARIES``\n  Libraries needed to link to Python.\n\nCache variables\n^^^^^^^^^^^^^^^\n\nThe following cache variables may also be set:\n\n``Python_LIBRARY``\n  Path to the library component of Python.\n``Python_INCLUDE_DIR``\n  Directory containing ``python.h``.\n\n#]=======================================================================]\n\ninclude(FindPackageHandleStandardArgs)\n\nfind_path(Python_INCLUDE_DIR NAMES Python.h PATH_SUFFIXES include include/python DOC \"Python include directory\")\n\nfind_library(Python_LIBRARY NAMES python3 PATHS lib DOC \"Python location\")\n\nif(EXISTS \"${Python_INCLUDE_DIR}/patchlevel.h\")\n  file(STRINGS \"${Python_INCLUDE_DIR}/patchlevel.h\" _VERSION_STRING REGEX \"^.*PY_VERSION[ \\t]+\\\"[0-9\\\\.]+\\\"[ \\t]*$\")\n  string(REGEX REPLACE \".*PY_VERSION[ \\t]+\\\"([0-9\\\\.]+)\\\".*\" \"\\\\1\" Python_VERSION \"${_VERSION_STRING}\")\nelse()\n  if(NOT Python_FIND_QUIETLY)\n    message(AUTHOR_WARNING \"Failed to find Python version.\")\n  endif()\n  set(Python_VERSION 0.0.0)\nendif()\n\nfind_package_handle_standard_args(\n  Python\n  REQUIRED_VARS Python_LIBRARY Python_INCLUDE_DIR\n  VERSION_VAR Python_VERSION\n  HANDLE_VERSION_RANGE\n  REASON_FAILURE_MESSAGE \"Ensure that obs-deps is provided as part of CMAKE_PREFIX_PATH.\"\n)\nmark_as_advanced(Python_INCLUDE_DIR Python_LIBRARY)\n\nif(Python_FOUND)\n  set(Python_INCLUDE_DIRS ${Python_INCLUDE_DIR})\n  set(Python_LIBRARIES ${Python_LIBRARY})\n  set(Python_DEFINITIONS ${PC_Python_CFLAGS_OTHER})\n\n  if(NOT TARGET Python::Python)\n    if(IS_ABSOLUTE \"${Python_LIBRARY}\")\n      add_library(Python::Python UNKNOWN IMPORTED)\n      set_property(TARGET Python::Python PROPERTY IMPORTED_LOCATION \"${Python_LIBRARY}\")\n    else()\n      add_library(Python::Python INTERFACE IMPORTED)\n      set_property(TARGET Python::Python PROPERTY IMPORTED_LIBNAME \"${Python_LIBRARY}\")\n    endif()\n\n    set_target_properties(\n      Python::Python\n      PROPERTIES INTERFACE_INCLUDE_DIRECTORIES \"${Python_INCLUDE_DIR}\" VERSION ${Python_VERSION}\n    )\n  endif()\nendif()\n\ninclude(FeatureSummary)\nset_package_properties(\n  Python\n  PROPERTIES\n    URL \"https://www.python.org\"\n    DESCRIPTION\n      \"Python is a programming language that lets you work more quickly and integrate your systems more effectively.\"\n)\n"
  },
  {
    "path": "cmake/windows/architecture.cmake",
    "content": "# OBS CMake Windows Architecture Helper\n\ninclude_guard(GLOBAL)\n\ninclude(compilerconfig)\n\nif(NOT DEFINED OBS_PARENT_ARCHITECTURE)\n  if(CMAKE_VS_PLATFORM_NAME MATCHES \"(Win32|x64|ARM64)\")\n    set(OBS_PARENT_ARCHITECTURE ${CMAKE_VS_PLATFORM_NAME})\n  else()\n    message(FATAL_ERROR \"Unsupported generator platform for Windows builds: ${CMAKE_VS_PLATFORM_NAME}!\")\n  endif()\nendif()\n\nif(OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME)\n  if(OBS_PARENT_ARCHITECTURE STREQUAL ARM64)\n    execute_process(\n      COMMAND\n        \"${CMAKE_COMMAND}\" -S ${CMAKE_CURRENT_SOURCE_DIR} -B ${CMAKE_SOURCE_DIR}/build_x64 -A\n        \"x64,version=${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}\" -G \"${CMAKE_GENERATOR}\"\n        -DCMAKE_SYSTEM_VERSION:STRING='${CMAKE_SYSTEM_VERSION}' -DVIRTUALCAM_GUID:STRING=${VIRTUALCAM_GUID}\n        -DCMAKE_MESSAGE_LOG_LEVEL:STRING=${CMAKE_MESSAGE_LOG_LEVEL} -DOBS_PARENT_ARCHITECTURE:STRING=ARM64\n      RESULT_VARIABLE _process_result\n      COMMAND_ERROR_IS_FATAL ANY\n    )\n    execute_process(\n      COMMAND\n        \"${CMAKE_COMMAND}\" -S ${CMAKE_CURRENT_SOURCE_DIR} -B ${CMAKE_SOURCE_DIR}/build_x86 -A\n        \"Win32,version=${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}\" -G \"${CMAKE_GENERATOR}\"\n        -DCMAKE_SYSTEM_VERSION:STRING='${CMAKE_SYSTEM_VERSION}' -DVIRTUALCAM_GUID:STRING=${VIRTUALCAM_GUID}\n        -DCMAKE_MESSAGE_LOG_LEVEL:STRING=${CMAKE_MESSAGE_LOG_LEVEL} -DOBS_PARENT_ARCHITECTURE:STRING=ARM64\n      RESULT_VARIABLE _process_result\n      COMMAND_ERROR_IS_FATAL ANY\n    )\n  elseif(OBS_PARENT_ARCHITECTURE STREQUAL x64)\n    execute_process(\n      COMMAND\n        \"${CMAKE_COMMAND}\" -S ${CMAKE_CURRENT_SOURCE_DIR} -B ${CMAKE_SOURCE_DIR}/build_x86 -A\n        \"Win32,version=${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}\" -G \"${CMAKE_GENERATOR}\"\n        -DCMAKE_SYSTEM_VERSION:STRING='${CMAKE_SYSTEM_VERSION}' -DVIRTUALCAM_GUID:STRING=${VIRTUALCAM_GUID}\n        -DCMAKE_MESSAGE_LOG_LEVEL:STRING=${CMAKE_MESSAGE_LOG_LEVEL} -DOBS_PARENT_ARCHITECTURE:STRING=x64\n      RESULT_VARIABLE _process_result\n      COMMAND_ERROR_IS_FATAL ANY\n    )\n  endif()\nelse()\n  # target_disable_feature: Stub macro for child architecture builds\n  macro(target_disable_feature)\n  endmacro()\n\n  # target_disable: Stub macro for child architecture builds\n  macro(target_disable)\n  endmacro()\n\n  # target_add_resource: Stub macro for child architecture builds\n  macro(target_add_resource)\n  endmacro()\n\n  # target_export: Stub macro for child architecture builds\n  macro(target_export)\n  endmacro()\n\n  # set_target_properties_obs: Stub macro for child architecture builds\n  macro(set_target_properties_obs)\n    set_target_properties(${ARGV})\n  endmacro()\n\n  # check_uuid: Helper function to check for valid UUID\n  function(check_uuid uuid_string return_value)\n    set(valid_uuid TRUE)\n    # gersemi: off\n    set(uuid_token_lengths 8 4 4 4 12)\n    # gersemi: on\n    set(token_num 0)\n\n    string(REPLACE \"-\" \";\" uuid_tokens ${uuid_string})\n    list(LENGTH uuid_tokens uuid_num_tokens)\n\n    if(uuid_num_tokens EQUAL 5)\n      message(DEBUG \"UUID ${uuid_string} is valid with 5 tokens.\")\n      foreach(uuid_token IN LISTS uuid_tokens)\n        list(GET uuid_token_lengths ${token_num} uuid_target_length)\n        string(LENGTH \"${uuid_token}\" uuid_actual_length)\n        if(uuid_actual_length EQUAL uuid_target_length)\n          string(REGEX MATCH \"[0-9a-fA-F]+\" uuid_hex_match ${uuid_token})\n          if(NOT uuid_hex_match STREQUAL uuid_token)\n            set(valid_uuid FALSE)\n            break()\n          endif()\n        else()\n          set(valid_uuid FALSE)\n          break()\n        endif()\n        math(EXPR token_num \"${token_num}+1\")\n      endforeach()\n    else()\n      set(valid_uuid FALSE)\n    endif()\n    message(DEBUG \"UUID ${uuid_string} valid: ${valid_uuid}\")\n    set(${return_value} ${valid_uuid} PARENT_SCOPE)\n  endfunction()\n\n  include(\"${CMAKE_CURRENT_SOURCE_DIR}/cmake/windows/buildspec.cmake\")\n\n  add_subdirectory(libobs)\n  add_subdirectory(plugins/win-capture/get-graphics-offsets)\n  add_subdirectory(plugins/win-capture/graphics-hook)\n  add_subdirectory(plugins/win-capture/inject-helper)\n  add_subdirectory(plugins/win-dshow/virtualcam-module)\n\n  return()\nendif()\n"
  },
  {
    "path": "cmake/windows/buildspec.cmake",
    "content": "# OBS CMake Windows build dependencies module\n\ninclude_guard(GLOBAL)\n\ninclude(buildspec_common)\n\n# _handle_qt_cross_compile: Check for and handle cross compiled Qt dependency\nfunction(_handle_qt_cross_compile architecture)\n  set(options \"\")\n  set(oneValueArgs DIRECTORY)\n  set(multiValueArgs \"\")\n  cmake_parse_arguments(PARSE_ARGV 0 _HQCC \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\")\n\n  _get_dependency_data(dependency_data)\n\n  string(JSON data GET ${dependency_data} qt6)\n  string(JSON version GET ${data} version)\n\n  set(qt_build_arch \"\")\n  set(qt_target_arch \"\")\n  set(host_arch \"\")\n  set(platform_name \"\")\n  set(config_has_buildabi FALSE)\n  set(qt_cross_compiled FALSE)\n\n  string(REPLACE \"VERSION\" \"${version}\" directory \"${_HQCC_DIRECTORY}\")\n  string(TOLOWER \"${CMAKE_VS_PLATFORM_NAME}\" platform_name)\n  string(REPLACE \"ARCH\" \"${platform_name}\" qt_arch_location \"${directory}\")\n\n  file(READ \"${qt_arch_location}/mkspecs/qconfig.pri\" qt_arch_config)\n  string(REGEX MATCH \".+QT_TARGET_BUILDABI = (.+)\\n.+\" config_has_buildabi \"${qt_arch_config}\")\n\n  if(config_has_buildabi)\n    string(\n      REGEX REPLACE\n      \"host_build {\\n[ \\t]+QT_ARCH = (x86_64|arm64)\\n.+[ \\t]+QT_TARGET_ARCH = (x86_64|arm64)\\n.+}.+\"\n      \"\\\\1;\\\\2\"\n      host_build_tuple\n      \"${qt_arch_config}\"\n    )\n    list(GET host_build_tuple 0 qt_build_arch)\n    list(GET host_build_tuple 1 qt_target_arch)\n    set(qt_cross_compiled TRUE)\n  else()\n    string(REGEX REPLACE \".*QT_ARCH = (x86_64|arm64)\\n.+\" \"\\\\1\" build_arch \"${qt_arch_config}\")\n    set(qt_build_arch \"${build_arch}\")\n    set(qt_target_arch \"${build_arch}\")\n  endif()\n\n  if(NOT qt_build_arch MATCHES \"x86_64|arm64\" OR NOT qt_target_arch MATCHES \"x86_64|arm64\")\n    message(FATAL_ERROR \"Unable to detect host or target architecture from Qt dependencies in '${qt_arch_location}'\")\n  endif()\n\n  string(REPLACE \"x86_64\" \"x64\" qt_build_arch \"${qt_build_arch}\")\n  string(REPLACE \"x86_64\" \"x64\" qt_target_arch \"${qt_target_arch}\")\n  string(REPLACE \"AMD64\" \"x64\" architecture \"${architecture}\")\n  string(REPLACE \"ARM64\" \"arm64\" architecture \"${architecture}\")\n\n  if(NOT qt_cross_compiled)\n    if(architecture STREQUAL qt_target_arch OR (architecture STREQUAL arm64 AND qt_target_arch STREQUAL x64))\n      unset(QT_HOST_PATH CACHE)\n      unset(QT_REQUIRE_HOST_PATH_CHECK CACHE)\n      return()\n    endif()\n\n    set(QT_REQUIRE_HOST_PATH_CHECK TRUE CACHE STRING \"Qt Host Tools Check Required\" FORCE)\n  endif()\n\n  if(NOT DEFINED QT_HOST_PATH)\n    string(REPLACE \"${qt_target_arch}\" \"${architecture}\" host_tools_directory \"${qt_arch_location}\")\n\n    if(NOT IS_DIRECTORY \"${host_tools_directory}\")\n      message(\n        FATAL_ERROR\n        \"Required Qt host tools for ${architecture} when building for ${qt_target_arch} not found in '${host_tools_directory}'\"\n      )\n    endif()\n\n    set(QT_HOST_PATH \"${host_tools_directory}\" CACHE STRING \"Qt Host Tools Path\" FORCE)\n  endif()\nendfunction()\n\n# _check_dependencies_windows: Set up Windows slice for _check_dependencies\nfunction(_check_dependencies_windows)\n  set(dependencies_dir \"${CMAKE_CURRENT_SOURCE_DIR}/.deps\")\n  set(prebuilt_filename \"windows-deps-VERSION-ARCH-REVISION.zip\")\n  set(prebuilt_destination \"obs-deps-VERSION-ARCH\")\n  set(qt6_filename \"windows-deps-qt6-VERSION-ARCH-REVISION.zip\")\n  set(qt6_destination \"obs-deps-qt6-VERSION-ARCH\")\n  set(cef_filename \"cef_binary_VERSION_windows_ARCH_REVISION.zip\")\n  set(cef_destination \"cef_binary_VERSION_windows_ARCH\")\n\n  if(CMAKE_VS_PLATFORM_NAME STREQUAL Win32)\n    set(arch x86)\n    set(dependencies_list prebuilt)\n  else()\n    string(TOLOWER \"${CMAKE_VS_PLATFORM_NAME}\" arch)\n    set(dependencies_list prebuilt qt6 cef)\n  endif()\n  set(platform windows-${arch})\n\n  _check_dependencies(${dependencies_list})\n\n  if(NOT CMAKE_VS_PLATFORM_NAME STREQUAL Win32)\n    _handle_qt_cross_compile(${CMAKE_HOST_SYSTEM_PROCESSOR} DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/.deps/${qt6_destination}\")\n  endif()\nendfunction()\n\n_check_dependencies_windows()\n"
  },
  {
    "path": "cmake/windows/compilerconfig.cmake",
    "content": "# OBS CMake Windows compiler configuration module\n\ninclude_guard(GLOBAL)\n\ninclude(ccache)\ninclude(compiler_common)\n\nif(ENABLE_CCACHE AND CCACHE_PROGRAM)\n  if(CMAKE_C_COMPILER_ID STREQUAL \"MSVC\")\n    file(COPY_FILE ${CCACHE_PROGRAM} \"${CMAKE_CURRENT_BINARY_DIR}/cl.exe\")\n    set(\n      CMAKE_VS_GLOBALS\n      \"CLToolExe=cl.exe\"\n      \"CLToolPath=${CMAKE_BINARY_DIR}\"\n      \"TrackFileAccess=false\"\n      \"UseMultiToolTask=true\"\n    )\n    # Ccache does not support debug information stored in program database (PDB) files\n    set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded)\n  elseif(CMAKE_C_COMPILER_ID STREQUAL \"Clang\")\n    file(COPY_FILE ${CCACHE_PROGRAM} \"${CMAKE_CURRENT_BINARY_DIR}/clang-cl.exe\")\n    set(\n      CMAKE_VS_GLOBALS\n      \"CLToolExe=clang-cl.exe\"\n      \"CLToolPath=${CMAKE_BINARY_DIR}\"\n      \"TrackFileAccess=false\"\n      \"UseMultiToolTask=true\"\n    )\n  endif()\nendif()\n\n# CMake 3.24 introduces a bug mistakenly interpreting MSVC as supporting the '-pthread' compiler flag\nif(CMAKE_VERSION VERSION_EQUAL 3.24.0)\n  set(THREADS_HAVE_PTHREAD_ARG FALSE)\nendif()\n\n# CMake 3.25 changed the way symbol generation is handled on Windows\nif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.25.0)\n  if(CMAKE_C_COMPILER_ID STREQUAL \"MSVC\")\n    set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT ProgramDatabase)\n  else()\n    set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT Embedded)\n  endif()\nendif()\n\nmessage(DEBUG \"Current Windows API version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}\")\nif(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM)\n  message(DEBUG \"Maximum Windows API version: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM}\")\nendif()\n\nif(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION VERSION_LESS 10.0.20348)\n  message(\n    FATAL_ERROR\n    \"OBS requires Windows SDK version 10.0.20348.0 or more recent.\\n\"\n    \"Please download and install the most recent Windows SDK.\"\n  )\nendif()\n\nset(_obs_msvc_c_options /MP /Zc:__cplusplus /Zc:preprocessor)\nset(_obs_msvc_cpp_options /MP /Zc:__cplusplus /Zc:preprocessor)\n\nif(CMAKE_CXX_STANDARD GREATER_EQUAL 20)\n  list(APPEND _obs_msvc_cpp_options /Zc:char8_t-)\nendif()\n\nadd_compile_options(\n  /W3\n  /utf-8\n  /Brepro\n  /permissive-\n  \"$<$<COMPILE_LANG_AND_ID:C,MSVC>:${_obs_msvc_c_options}>\"\n  \"$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:${_obs_msvc_cpp_options}>\"\n  \"$<$<COMPILE_LANG_AND_ID:C,Clang>:${_obs_clang_c_options}>\"\n  \"$<$<COMPILE_LANG_AND_ID:CXX,Clang>:${_obs_clang_cxx_options}>\"\n  $<$<NOT:$<CONFIG:Debug>>:/Gy>\n  $<$<NOT:$<CONFIG:Debug>>:/Oi>\n)\n\nadd_compile_definitions(\n  NOMINMAX\n  UNICODE\n  _UNICODE\n  _CRT_SECURE_NO_WARNINGS\n  _CRT_NONSTDC_NO_WARNINGS\n  $<$<CONFIG:DEBUG>:DEBUG>\n  $<$<CONFIG:DEBUG>:_DEBUG>\n)\n\n# Visual Studio sets \"/LTCG:INCREMENTAL\" when \"Whole Program Optimization\" is enabled for a x86 or x64 target. To\n# ensure \"proper\" link-time optimization, the LTCG flag has to be explicitly added here.\nadd_link_options(\n  $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>:/LTCG>\n  $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>:/OPT:REF>\n  $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>:/OPT:ICF>\n  $<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>:/EMITTOOLVERSIONINFO:NO>\n  /DEBUG\n  /Brepro\n)\n\nif(CMAKE_COMPILE_WARNING_AS_ERROR)\n  add_link_options(/WX)\nendif()\n"
  },
  {
    "path": "cmake/windows/cpackconfig.cmake",
    "content": "# OBS CMake Windows CPack configuration module\n\ninclude_guard(GLOBAL)\n\ninclude(cpackconfig_common)\n\n# Add GPLv2 license file to CPack\nset(CPACK_RESOURCE_FILE_LICENSE \"${CMAKE_SOURCE_DIR}/frontend/data/license/gplv2.txt\")\nset(CPACK_PACKAGE_VERSION \"${OBS_VERSION_CANONICAL}\")\nset(CPACK_PACKAGE_FILE_NAME \"${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-windows-${CMAKE_VS_PLATFORM_NAME}\")\nset(CPACK_INCLUDE_TOPLEVEL_DIRECTORY FALSE)\nset(CPACK_GENERATOR ZIP)\nset(CPACK_THREADS 0)\n\ninclude(CPack)\n"
  },
  {
    "path": "cmake/windows/defaults.cmake",
    "content": "# OBS CMake Windows defaults module\n\ninclude_guard(GLOBAL)\n\nset(OBS_SOURCE_DIR \"${CMAKE_CURRENT_SOURCE_DIR}\")\nset(OBS_OUTPUT_DIR \"${CMAKE_CURRENT_BINARY_DIR}/rundir\")\n\nset(OBS_PLUGIN_DESTINATION obs-plugins/64bit)\nset(OBS_DATA_DESTINATION data)\nset(OBS_CMAKE_DESTINATION cmake)\nset(OBS_SCRIPT_PLUGIN_DESTINATION \"${OBS_DATA_DESTINATION}/obs-scripting/64bit\")\n\nset(OBS_EXECUTABLE_DESTINATION bin/64bit)\nset(OBS_LIBRARY_DESTINATION lib)\nset(OBS_INCLUDE_DESTINATION include)\n# Set relative paths used by OBS for self-discovery\nset(OBS_PLUGIN_PATH \"../../${CMAKE_INSTALL_LIBDIR}/obs-plugins/64bit\")\nset(OBS_SCRIPT_PLUGIN_PATH \"../../${OBS_DATA_DESTINATION}/obs-scripting/64bit\")\nset(OBS_DATA_PATH \"../../${OBS_DATA_DESTINATION}\")\n\n# Enable find_package targets to become globally available targets\nset(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL TRUE)\n\ninclude(buildspec)\ninclude(cpackconfig)\n"
  },
  {
    "path": "cmake/windows/helpers.cmake",
    "content": "# OBS CMake Windows helper functions module\n\ninclude_guard(GLOBAL)\n\ninclude(helpers_common)\n\n# set_target_properties_obs: Set target properties for use in obs-studio\nfunction(set_target_properties_obs target)\n  set(options \"\")\n  set(oneValueArgs \"\")\n  set(multiValueArgs PROPERTIES)\n  cmake_parse_arguments(PARSE_ARGV 0 _STPO \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\")\n\n  message(DEBUG \"Setting additional properties for target ${target}...\")\n\n  while(_STPO_PROPERTIES)\n    list(POP_FRONT _STPO_PROPERTIES key value)\n    set_property(TARGET ${target} PROPERTY ${key} \"${value}\")\n  endwhile()\n\n  get_target_property(target_type ${target} TYPE)\n\n  if(target_type STREQUAL EXECUTABLE)\n    if(target STREQUAL obs-browser-helper)\n      set(OBS_EXECUTABLE_DESTINATION \"${OBS_PLUGIN_DESTINATION}\")\n    elseif(target STREQUAL inject-helper OR target STREQUAL get-graphics-offsets)\n      set(OBS_EXECUTABLE_DESTINATION \"${OBS_DATA_DESTINATION}/obs-plugins/win-capture\")\n\n      _target_install_obs(${target} DESTINATION ${OBS_EXECUTABLE_DESTINATION} x86)\n\n      if(CMAKE_VS_PLATFORM_NAME STREQUAL ARM64)\n        _target_install_obs(${target} DESTINATION ${OBS_EXECUTABLE_DESTINATION} x64)\n      endif()\n    endif()\n\n    _target_install_obs(${target} DESTINATION ${OBS_EXECUTABLE_DESTINATION})\n\n    if(target STREQUAL obs-studio)\n      get_property(obs_executables GLOBAL PROPERTY _OBS_EXECUTABLES)\n      get_property(obs_modules GLOBAL PROPERTY OBS_MODULES_ENABLED)\n      add_dependencies(${target} ${obs_executables} ${obs_modules})\n      _bundle_dependencies(${target})\n      target_add_resource(${target} \"${CMAKE_CURRENT_SOURCE_DIR}/../AUTHORS\"\n                          \"${OBS_DATA_DESTINATION}/obs-studio/authors\"\n      )\n    elseif(target STREQUAL obs-browser-helper)\n      set_property(GLOBAL APPEND PROPERTY _OBS_EXECUTABLES ${target})\n      return()\n    else()\n      set_property(GLOBAL APPEND PROPERTY _OBS_EXECUTABLES ${target})\n    endif()\n  elseif(target_type STREQUAL SHARED_LIBRARY)\n    set_target_properties(${target} PROPERTIES VERSION ${OBS_VERSION_MAJOR} SOVERSION ${OBS_VERSION_CANONICAL})\n\n    _target_install_obs(\n      ${target}\n        DESTINATION \"${OBS_EXECUTABLE_DESTINATION}\"\n        LIBRARY_DESTINATION \"${OBS_LIBRARY_DESTINATION}\"\n        HEADER_DESTINATION \"${OBS_INCLUDE_DESTINATION}\"\n    )\n  elseif(target_type STREQUAL MODULE_LIBRARY)\n    set_target_properties(${target} PROPERTIES VERSION 0 SOVERSION ${OBS_VERSION_CANONICAL})\n\n    if(target STREQUAL libobs-d3d11 OR target STREQUAL libobs-opengl OR target STREQUAL libobs-winrt)\n      set(target_destination \"${OBS_EXECUTABLE_DESTINATION}\")\n    elseif(target STREQUAL \"obspython\" OR target STREQUAL \"obslua\")\n      set(target_destination \"${OBS_SCRIPT_PLUGIN_DESTINATION}\")\n    elseif(target STREQUAL graphics-hook)\n      set(target_destination \"${OBS_DATA_DESTINATION}/obs-plugins/win-capture\")\n      target_add_resource(graphics-hook \"${CMAKE_CURRENT_SOURCE_DIR}/obs-vulkan64.json\" \"${target_destination}\")\n      target_add_resource(graphics-hook \"${CMAKE_CURRENT_SOURCE_DIR}/obs-vulkan32.json\" \"${target_destination}\")\n\n      _target_install_obs(${target} DESTINATION ${target_destination} x86)\n\n      if(CMAKE_VS_PLATFORM_NAME STREQUAL ARM64)\n        _target_install_obs(${target} DESTINATION ${target_destination} x64)\n      endif()\n    elseif(target STREQUAL obs-virtualcam-module)\n      set(target_destination \"${OBS_DATA_DESTINATION}/obs-plugins/win-dshow\")\n\n      _target_install_obs(${target} DESTINATION ${target_destination} x86)\n\n      if(CMAKE_VS_PLATFORM_NAME STREQUAL ARM64)\n        _target_install_obs(${target} DESTINATION ${target_destination} x64)\n      endif()\n    else()\n      set(target_destination \"${OBS_PLUGIN_DESTINATION}\")\n    endif()\n\n    _target_install_obs(${target} DESTINATION ${target_destination})\n\n    if(${target} STREQUAL obspython)\n      add_custom_command(\n        TARGET ${target}\n        POST_BUILD\n        COMMAND \"${CMAKE_COMMAND}\" -E echo \"Add obspython import module\"\n        COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_SCRIPT_PLUGIN_DESTINATION}/\"\n        COMMAND\n          \"${CMAKE_COMMAND}\" -E copy_if_different \"$<TARGET_FILE_DIR:obspython>/obspython.py\"\n          \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_SCRIPT_PLUGIN_DESTINATION}/\"\n        COMMENT \"\"\n      )\n\n      install(\n        FILES \"$<TARGET_FILE_DIR:obspython>/obspython.py\"\n        DESTINATION \"${OBS_SCRIPT_PLUGIN_DESTINATION}\"\n        COMPONENT Runtime\n      )\n    elseif(${target} STREQUAL obs-browser)\n      message(DEBUG \"Add Chromium Embedded Framework to project for obs-browser plugin...\")\n      if(TARGET CEF::Library)\n        get_target_property(imported_location CEF::Library IMPORTED_LOCATION_RELEASE)\n\n        if(imported_location)\n          cmake_path(GET imported_location PARENT_PATH cef_location)\n          cmake_path(GET cef_location PARENT_PATH cef_root_location)\n          add_custom_command(\n            TARGET ${target}\n            POST_BUILD\n            COMMAND \"${CMAKE_COMMAND}\" -E echo \"Add Chromium Embedded Framework to library directory\"\n            COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}\"\n            COMMAND\n              \"${CMAKE_COMMAND}\" -E copy_if_different \"${imported_location}\" \"${cef_location}/chrome_elf.dll\"\n              \"${cef_location}/libEGL.dll\" \"${cef_location}/libGLESv2.dll\" \"${cef_location}/v8_context_snapshot.bin\"\n              \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}\"\n            COMMAND\n              \"${CMAKE_COMMAND}\" -E copy_if_different \"${cef_root_location}/Resources/chrome_100_percent.pak\"\n              \"${cef_root_location}/Resources/chrome_200_percent.pak\" \"${cef_root_location}/Resources/icudtl.dat\"\n              \"${cef_root_location}/Resources/resources.pak\" \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}/\"\n            COMMAND\n              \"${CMAKE_COMMAND}\" -E copy_directory \"${cef_root_location}/Resources/locales\"\n              \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}/locales\"\n            COMMENT \"\"\n          )\n\n          install(\n            FILES\n              \"${imported_location}\"\n              \"${cef_location}/chrome_elf.dll\"\n              \"${cef_location}/libEGL.dll\"\n              \"${cef_location}/libGLESv2.dll\"\n              \"${cef_location}/v8_context_snapshot.bin\"\n              \"${cef_root_location}/Resources/chrome_100_percent.pak\"\n              \"${cef_root_location}/Resources/chrome_200_percent.pak\"\n              \"${cef_root_location}/Resources/icudtl.dat\"\n              \"${cef_root_location}/Resources/resources.pak\"\n            DESTINATION \"${target_destination}\"\n            COMPONENT Runtime\n          )\n\n          install(\n            DIRECTORY \"${cef_root_location}/Resources/locales\"\n            DESTINATION \"${target_destination}\"\n            USE_SOURCE_PERMISSIONS\n            COMPONENT Runtime\n          )\n        endif()\n      endif()\n    endif()\n\n    set_property(GLOBAL APPEND PROPERTY OBS_MODULES_ENABLED ${target})\n  endif()\n\n  target_link_options(${target} PRIVATE \"/PDBALTPATH:$<TARGET_PDB_FILE_NAME:${target}>\")\n  target_install_resources(${target})\n\n  get_target_property(target_sources ${target} SOURCES)\n  set(target_ui_files ${target_sources})\n  list(FILTER target_ui_files INCLUDE REGEX \".+\\\\.(ui|qrc)\")\n  source_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"UI Files\" FILES ${target_ui_files})\n\n  if(${target} STREQUAL libobs)\n    set(target_source_files ${target_sources})\n    set(target_header_files ${target_sources})\n    list(FILTER target_source_files INCLUDE REGEX \".+\\\\.(m|c[cp]?p?|swift)\")\n    list(FILTER target_header_files INCLUDE REGEX \".+\\\\.h(pp)?\")\n\n    source_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"Source Files\" FILES ${target_source_files})\n    source_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"Header Files\" FILES ${target_header_files})\n  endif()\nendfunction()\n\n# _target_install_obs: Helper function to install build artifacts to rundir and install location\nfunction(_target_install_obs target)\n  set(options \"x86\" \"x64\")\n  set(oneValueArgs \"DESTINATION\" \"LIBRARY_DESTINATION\" \"HEADER_DESTINATION\")\n  set(multiValueArgs \"\")\n  cmake_parse_arguments(PARSE_ARGV 0 _TIO \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\")\n\n  if(_TIO_x86)\n    get_target_property(target_type ${target} TYPE)\n    if(target_type STREQUAL EXECUTABLE)\n      set(suffix exe)\n    else()\n      set(suffix dll)\n    endif()\n\n    cmake_path(RELATIVE_PATH CMAKE_CURRENT_SOURCE_DIR BASE_DIRECTORY \"${OBS_SOURCE_DIR}\" OUTPUT_VARIABLE project_path)\n\n    set(32bit_project_path \"${OBS_SOURCE_DIR}/build_x86/${project_path}\")\n    set(target_file \"${32bit_project_path}/$<CONFIG>/${target}32.${suffix}\")\n    set(target_pdb_file \"${32bit_project_path}/$<CONFIG>/${target}32.pdb\")\n    set(comment \"Copy ${target} (x86) to destination\")\n\n    install(\n      FILES \"${32bit_project_path}/$<CONFIG>/${target}32.${suffix}\"\n      DESTINATION \"${_TIO_DESTINATION}\"\n      COMPONENT Runtime\n      OPTIONAL\n    )\n  elseif(_TIO_x64)\n    get_target_property(target_type ${target} TYPE)\n    if(target_type STREQUAL EXECUTABLE)\n      set(suffix exe)\n    else()\n      set(suffix dll)\n    endif()\n\n    cmake_path(RELATIVE_PATH CMAKE_CURRENT_SOURCE_DIR BASE_DIRECTORY \"${OBS_SOURCE_DIR}\" OUTPUT_VARIABLE project_path)\n\n    set(64bit_project_path \"${OBS_SOURCE_DIR}/build_x64/${project_path}\")\n    set(target_file \"${64bit_project_path}/$<CONFIG>/${target}64.${suffix}\")\n    set(target_pdb_file \"${64bit_project_path}/$<CONFIG>/${target}64.pdb\")\n    set(comment \"Copy ${target} (x64) to destination\")\n\n    install(\n      FILES \"${64bit_project_path}/$<CONFIG>/${target}64.${suffix}\"\n      DESTINATION \"${_TIO_DESTINATION}\"\n      COMPONENT Runtime\n      OPTIONAL\n    )\n  else()\n    set(target_file \"$<TARGET_FILE:${target}>\")\n    set(target_pdb_file \"$<TARGET_PDB_FILE:${target}>\")\n    set(comment \"Copy ${target} to destination\")\n\n    get_target_property(target_type ${target} TYPE)\n    if(target_type STREQUAL EXECUTABLE)\n      install(TARGETS ${target} RUNTIME DESTINATION \"${_TIO_DESTINATION}\" COMPONENT Runtime)\n    elseif(target_type STREQUAL SHARED_LIBRARY)\n      if(NOT _TIO_LIBRARY_DESTINATION)\n        set(_TIO_LIBRARY_DESTINATION ${_TIO_DESTINATION})\n      endif()\n      if(NOT _TIO_HEADER_DESTINATION)\n        set(_TIO_HEADER_DESTINATION include)\n      endif()\n      install(\n        TARGETS ${target}\n        RUNTIME DESTINATION \"${_TIO_DESTINATION}\"\n        LIBRARY DESTINATION \"${_TIO_LIBRARY_DESTINATION}\" COMPONENT Runtime EXCLUDE_FROM_ALL\n        PUBLIC_HEADER DESTINATION \"${_TIO_HEADER_DESTINATION}\" COMPONENT Development EXCLUDE_FROM_ALL\n      )\n    elseif(target_type STREQUAL MODULE_LIBRARY)\n      install(\n        TARGETS ${target}\n        LIBRARY DESTINATION \"${_TIO_DESTINATION}\" COMPONENT Runtime NAMELINK_COMPONENT Development\n      )\n    endif()\n  endif()\n\n  add_custom_command(\n    TARGET ${target}\n    POST_BUILD\n    COMMAND \"${CMAKE_COMMAND}\" -E echo \"${comment}\"\n    COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${_TIO_DESTINATION}\"\n    COMMAND \"${CMAKE_COMMAND}\" -E copy ${target_file} \"${OBS_OUTPUT_DIR}/$<CONFIG>/${_TIO_DESTINATION}\"\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E $<IF:$<CONFIG:Debug,RelWithDebInfo,Release>,copy,true> ${target_pdb_file}\n      \"${OBS_OUTPUT_DIR}/$<CONFIG>/${_TIO_DESTINATION}\"\n    COMMENT \"\"\n    VERBATIM\n  )\n\n  install(\n    FILES ${target_pdb_file}\n    CONFIGURATIONS RelWithDebInfo Debug Release\n    DESTINATION \"${_TIO_DESTINATION}\"\n    COMPONENT Runtime\n    OPTIONAL\n  )\nendfunction()\n\n# target_export: Helper function to export target as CMake package\nfunction(target_export target)\n  # Exclude CMake package from 'ALL' target\n  set(exclude_variant EXCLUDE_FROM_ALL)\n  _target_export(${target})\n\n  get_target_property(target_type ${target} TYPE)\n  if(NOT target_type STREQUAL INTERFACE_LIBRARY)\n    install(\n      FILES \"$<TARGET_PDB_FILE:${target}>\"\n      CONFIGURATIONS RelWithDebInfo Debug Release\n      DESTINATION \"${OBS_EXECUTABLE_DESTINATION}\"\n      COMPONENT Development\n      OPTIONAL\n    )\n  endif()\nendfunction()\n\n# Helper function to add resources into bundle\nfunction(target_install_resources target)\n  message(DEBUG \"Installing resources for target ${target}...\")\n  if(EXISTS \"${CMAKE_CURRENT_SOURCE_DIR}/data\")\n    file(GLOB_RECURSE data_files \"${CMAKE_CURRENT_SOURCE_DIR}/data/*\")\n    foreach(data_file IN LISTS data_files)\n      cmake_path(\n        RELATIVE_PATH data_file\n        BASE_DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/data/\"\n        OUTPUT_VARIABLE relative_path\n      )\n      cmake_path(GET relative_path PARENT_PATH relative_path)\n      target_sources(${target} PRIVATE \"${data_file}\")\n      source_group(\"Resources/${relative_path}\" FILES \"${data_file}\")\n    endforeach()\n\n    get_property(obs_module_list GLOBAL PROPERTY OBS_MODULES_ENABLED)\n    if(target IN_LIST obs_module_list)\n      set(target_destination \"${OBS_DATA_DESTINATION}/obs-plugins/${target}\")\n    elseif(target STREQUAL obs-studio)\n      set(target_destination \"${OBS_DATA_DESTINATION}/obs-studio\")\n    else()\n      set(target_destination \"${OBS_DATA_DESTINATION}/${target}\")\n    endif()\n\n    install(\n      DIRECTORY \"${CMAKE_CURRENT_SOURCE_DIR}/data/\"\n      DESTINATION \"${target_destination}\"\n      USE_SOURCE_PERMISSIONS\n      COMPONENT Runtime\n    )\n\n    add_custom_command(\n      TARGET ${target}\n      POST_BUILD\n      COMMAND \"${CMAKE_COMMAND}\" -E echo \"Copy ${target} resources to data directory\"\n      COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}\"\n      COMMAND\n        \"${CMAKE_COMMAND}\" -E copy_directory \"${CMAKE_CURRENT_SOURCE_DIR}/data\"\n        \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}\"\n      COMMENT \"\"\n      VERBATIM\n    )\n  endif()\nendfunction()\n\n# Helper function to add a specific resource to a bundle\nfunction(target_add_resource target resource)\n  get_property(obs_module_list GLOBAL PROPERTY OBS_MODULES_ENABLED)\n  if(ARGN)\n    set(target_destination \"${ARGN}\")\n  elseif(${target} IN_LIST obs_module_list)\n    set(target_destination \"${OBS_DATA_DESTINATION}/obs-plugins/${target}\")\n  elseif(target STREQUAL obs-studio)\n    set(target_destination \"${OBS_DATA_DESTINATION}/obs-studio\")\n  else()\n    set(target_destination \"${OBS_DATA_DESTINATION}/${target}\")\n  endif()\n\n  message(DEBUG \"Add resource '${resource}' to target ${target} at destination '${target_destination}'...\")\n\n  install(FILES \"${resource}\" DESTINATION \"${target_destination}\" COMPONENT Runtime)\n\n  add_custom_command(\n    TARGET ${target}\n    POST_BUILD\n    COMMAND \"${CMAKE_COMMAND}\" -E echo \"Copy ${target} resource ${resource} to library directory\"\n    COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}/\"\n    COMMAND \"${CMAKE_COMMAND}\" -E copy \"${resource}\" \"${OBS_OUTPUT_DIR}/$<CONFIG>/${target_destination}/\"\n    COMMENT \"\"\n    VERBATIM\n  )\n\n  source_group(\"Resources\" FILES \"${resource}\")\nendfunction()\n\n# _bundle_dependencies: Resolve third party dependencies and add them to Windows binary directory\nfunction(_bundle_dependencies target)\n  message(DEBUG \"Discover dependencies of target ${target}...\")\n  set(found_dependencies)\n  find_dependencies(TARGET ${target} FOUND_VAR found_dependencies)\n\n  get_property(obs_module_list GLOBAL PROPERTY OBS_MODULES_ENABLED)\n  list(LENGTH obs_module_list num_modules)\n  if(num_modules GREATER 0)\n    add_dependencies(${target} ${obs_module_list})\n    foreach(module IN LISTS obs_module_list)\n      find_dependencies(TARGET ${module} FOUND_VAR found_dependencies)\n    endforeach()\n  endif()\n\n  list(REMOVE_DUPLICATES found_dependencies)\n  set(library_paths_DEBUG)\n  set(library_paths_RELWITHDEBINFO)\n  set(library_paths_RELEASE)\n  set(library_paths_MINSIZEREL)\n  set(plugins_list)\n\n  foreach(library IN LISTS found_dependencies)\n    # CEF needs to be placed in obs-plugins directory on Windows, which is handled already\n    if(${library} STREQUAL CEF::Library)\n      continue()\n    endif()\n\n    get_target_property(library_type ${library} TYPE)\n    get_target_property(is_imported ${library} IMPORTED)\n\n    if(is_imported)\n      get_target_property(imported_location ${library} IMPORTED_LOCATION)\n\n      foreach(config IN ITEMS RELEASE RELWITHDEBINFO MINSIZEREL DEBUG)\n        get_target_property(imported_location_${config} ${library} IMPORTED_LOCATION_${config})\n        if(imported_location_${config})\n          _check_library_location(${imported_location_${config}})\n        elseif(NOT imported_location_${config} AND imported_location_RELEASE)\n          _check_library_location(${imported_location_RELEASE})\n        else()\n          _check_library_location(${imported_location})\n        endif()\n      endforeach()\n\n      if(library MATCHES \"Qt6?::.+\")\n        find_qt_plugins(COMPONENT ${library} TARGET ${target} FOUND_VAR plugins_list)\n      endif()\n    endif()\n  endforeach()\n\n  foreach(config IN ITEMS DEBUG RELWITHDEBINFO RELEASE MINSIZEREL)\n    list(REMOVE_DUPLICATES library_paths_${config})\n  endforeach()\n\n  add_custom_command(\n    TARGET ${target}\n    POST_BUILD\n    COMMAND \"${CMAKE_COMMAND}\" -E echo \"Copy dependencies to binary directory (${OBS_EXECUTABLE_DESTINATION})...\"\n    COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_EXECUTABLE_DESTINATION}\"\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E \"$<IF:$<CONFIG:Debug>,copy_if_different,true>\" \"$<$<CONFIG:Debug>:${library_paths_DEBUG}>\"\n      \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_EXECUTABLE_DESTINATION}\"\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E \"$<IF:$<CONFIG:RelWithDebInfo>,copy_if_different,true>\"\n      \"$<$<CONFIG:RelWithDebInfo>:${library_paths_RELWITHDEBINFO}>\"\n      \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_EXECUTABLE_DESTINATION}\"\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E \"$<IF:$<CONFIG:Release>,copy_if_different,true>\"\n      \"$<$<CONFIG:Release>:${library_paths_RELEASE}>\" \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_EXECUTABLE_DESTINATION}\"\n    COMMAND\n      \"${CMAKE_COMMAND}\" -E \"$<IF:$<CONFIG:MinSizeRel>,copy_if_different,true>\"\n      \"$<$<CONFIG:MinSizeRel>:${library_paths_MINSIZEREL}>\" \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_EXECUTABLE_DESTINATION}\"\n    COMMENT \".\"\n    VERBATIM\n    COMMAND_EXPAND_LISTS\n  )\n\n  install(\n    FILES ${library_paths_DEBUG}\n    CONFIGURATIONS Debug\n    DESTINATION \"${OBS_EXECUTABLE_DESTINATION}\"\n    COMPONENT Runtime\n  )\n\n  install(\n    FILES ${library_paths_RELWITHDEBINFO}\n    CONFIGURATIONS RelWithDebInfo\n    DESTINATION \"${OBS_EXECUTABLE_DESTINATION}\"\n    COMPONENT Runtime\n  )\n\n  install(\n    FILES ${library_paths_RELEASE}\n    CONFIGURATIONS Release\n    DESTINATION \"${OBS_EXECUTABLE_DESTINATION}\"\n    COMPONENT Runtime\n  )\n\n  install(\n    FILES ${library_paths_MINSIZEREL}\n    CONFIGURATIONS MinSizeRel\n    DESTINATION \"${OBS_EXECUTABLE_DESTINATION}\"\n    COMPONENT Runtime\n  )\n\n  set(debug_dll_exceptions qdirect2d qcertonlybackend qopensslbackend qschannelbackend)\n  list(REMOVE_DUPLICATES plugins_list)\n  foreach(plugin IN LISTS plugins_list)\n    message(TRACE \"Adding Qt plugin ${plugin}...\")\n\n    cmake_path(GET plugin PARENT_PATH plugin_path)\n    cmake_path(GET plugin_path STEM plugin_stem)\n\n    list(APPEND plugin_stems ${plugin_stem})\n\n    if(plugin MATCHES \"(.+d)\\\\.dll$\" AND CMAKE_MATCH_COUNT EQUAL 1 AND NOT CMAKE_MATCH_1 IN_LIST debug_dll_exceptions)\n      list(APPEND plugin_${plugin_stem}_debug ${plugin})\n    else()\n      list(APPEND plugin_${plugin_stem} ${plugin})\n    endif()\n  endforeach()\n  unset(debug_exceptions)\n\n  list(REMOVE_DUPLICATES plugin_stems)\n  foreach(stem IN LISTS plugin_stems)\n    set(plugin_list ${plugin_${stem}})\n    set(plugin_list_debug ${plugin_${stem}_debug})\n    add_custom_command(\n      TARGET ${target}\n      POST_BUILD\n      COMMAND\n        \"${CMAKE_COMMAND}\" -E echo \"Copy Qt plugins ${stem} to binary directory (${OBS_EXECUTABLE_DESTINATION}/${stem})\"\n      COMMAND \"${CMAKE_COMMAND}\" -E make_directory \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_EXECUTABLE_DESTINATION}/${stem}\"\n      COMMAND\n        \"${CMAKE_COMMAND}\" -E \"$<IF:$<CONFIG:Debug>,copy_if_different,true>\" \"${plugin_list_debug}\"\n        \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_EXECUTABLE_DESTINATION}/${stem}\"\n      COMMAND\n        \"${CMAKE_COMMAND}\" -E \"$<IF:$<CONFIG:Debug>,true,copy_if_different>\" \"${plugin_list}\"\n        \"${OBS_OUTPUT_DIR}/$<CONFIG>/${OBS_EXECUTABLE_DESTINATION}/${stem}\"\n      COMMENT \"\"\n      VERBATIM\n      COMMAND_EXPAND_LISTS\n    )\n\n    install(\n      FILES ${plugin_list_debug}\n      CONFIGURATIONS Debug\n      DESTINATION \"${OBS_EXECUTABLE_DESTINATION}/${stem}\"\n      COMPONENT Runtime\n    )\n\n    install(\n      FILES ${plugin_list}\n      CONFIGURATIONS RelWithDebInfo Release MinSizeRel\n      DESTINATION \"${OBS_EXECUTABLE_DESTINATION}/${stem}\"\n      COMPONENT Runtime\n    )\n  endforeach()\nendfunction()\n\n# _check_library_location: Check for corresponding DLL given an import library path\nmacro(_check_library_location location)\n  if(library_type STREQUAL \"SHARED_LIBRARY\")\n    set(library_location \"${location}\")\n  else()\n    string(STRIP \"${location}\" location)\n    if(location MATCHES \".+lib$\")\n      cmake_path(GET location FILENAME _dll_name)\n      cmake_path(GET location PARENT_PATH _implib_path)\n      cmake_path(SET _bin_path NORMALIZE \"${_implib_path}/../bin\")\n      string(REPLACE \".lib\" \".dll\" _dll_name \"${_dll_name}\")\n      string(REPLACE \".dll\" \".pdb\" _pdb_name \"${_dll_name}\")\n\n      find_program(_dll_path NAMES \"${_dll_name}\" HINTS ${_implib_path} ${_bin_path} NO_CACHE NO_DEFAULT_PATH)\n\n      find_program(_pdb_path NAMES \"${_pdb_name}\" HINTS ${_implib_path} ${_bin_path} NO_CACHE NO_DEFAULT_PATH)\n\n      if(_dll_path)\n        set(library_location \"${_dll_path}\")\n        set(library_pdb_location \"${_pdb_path}\")\n      else()\n        unset(library_location)\n        unset(library_pdb_location)\n      endif()\n      unset(_dll_path)\n      unset(_pdb_path)\n      unset(_bin_path)\n      unset(_implib_path)\n      unset(_dll_name)\n      unset(_pdb_name)\n    else()\n      unset(library_location)\n      unset(library_pdb_location)\n    endif()\n  endif()\n\n  if(library_location)\n    list(APPEND library_paths_${config} ${library_location})\n  endif()\n  if(library_pdb_location)\n    list(APPEND library_paths_${config} ${library_pdb_location})\n  endif()\n  unset(location)\n  unset(library_location)\n  unset(library_pdb_location)\nendmacro()\n"
  },
  {
    "path": "cmake/windows/idlfilehelper.cmake",
    "content": "# target_add_idl_files: Compile IDL file and add generated source files to target\nfunction(target_add_idl_files target)\n  set(options target WITH_TLB)\n  set(oneValueArgs \"\")\n  set(multiValueArgs \"\")\n  cmake_parse_arguments(PARSE_ARGV 1 _AIF \"${options}\" \"${oneValueArgs}\" \"${multiValueArgs}\")\n  set(aif_files ${_AIF_UNPARSED_ARGUMENTS})\n\n  foreach(idl_file IN LISTS aif_files)\n    cmake_path(GET idl_file STEM idl_file_name)\n    cmake_path(GET idl_file PARENT_PATH idl_file_path)\n\n    set(idl_file_header \"${CMAKE_CURRENT_BINARY_DIR}/${idl_file_name}.h\")\n    set(idl_file_source \"${CMAKE_CURRENT_BINARY_DIR}/${idl_file_name}_i.c\")\n\n    add_custom_command(\n      OUTPUT \"${idl_file_header}\" \"${idl_file_source}\"\n      DEPENDS \"${idl_file}\"\n      COMMAND\n        midl /h \"${idl_file_name}.h\" /iid \"${idl_file_name}_i.c\" \"$<$<NOT:$<BOOL:${_AIF_WITH_TLIB}>>:/notlb>\" /win64\n        \"${CMAKE_CURRENT_SOURCE_DIR}/${idl_file}\"\n      WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}\"\n      COMMENT \"Generate idl files\"\n    )\n\n    set_source_files_properties(${idl_file} PROPERTIES HEADER_FILE_ONLY TRUE)\n    target_sources(${target} PRIVATE \"${idl_file_source}\" \"${idl_file_header}\")\n  endforeach()\nendfunction()\n"
  },
  {
    "path": "deps/blake2/.clang-format",
    "content": "Language: Cpp\nSortIncludes: false\nDisableFormat: true\n"
  },
  {
    "path": "deps/blake2/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nadd_library(blake2 OBJECT)\nadd_library(OBS::blake2 ALIAS blake2)\n\ntarget_sources(blake2 PRIVATE src/blake2-impl.h src/blake2b-ref.c PUBLIC src/blake2.h)\n\ntarget_include_directories(blake2 PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/src\")\n\nset_target_properties(blake2 PROPERTIES FOLDER deps)\n\nif(OS_WINDOWS)\n  add_library(blake2_static OBJECT)\n  add_library(OBS::blake2_static ALIAS blake2_static)\n\n  target_sources(blake2_static PRIVATE src/blake2-impl.h src/blake2b-ref.c PUBLIC src/blake2.h)\n\n  target_include_directories(blake2_static PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/src\")\n  set_target_properties(blake2_static PROPERTIES MSVC_RUNTIME_LIBRARY \"MultiThreaded$<$<CONFIG:Debug>:Debug>\")\n\n  set_target_properties(blake2 PROPERTIES FOLDER deps/blake2)\n  set_target_properties(blake2_static PROPERTIES FOLDER deps/blake2)\nendif()\n"
  },
  {
    "path": "deps/blake2/LICENSE.blake2",
    "content": "BLAKE2 is licensed to permit usage under the terms of the CC0, the OpenSSL License,\n or the Apache Public License 2.0 at the user's option.\n\nThe license grant is reproduced in full below.\n\n/*\n   BLAKE2 reference source code package - reference C implementations\n\n   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the\n   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at\n   your option.  The terms of these licenses can be found at:\n\n   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0\n   - OpenSSL license   : https://www.openssl.org/source/license.html\n   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0\n\n   More information about the BLAKE2 hash function can be found at\n   https://blake2.net.\n*/\n"
  },
  {
    "path": "deps/blake2/src/blake2-impl.h",
    "content": "/*\n   BLAKE2 reference source code package - reference C implementations\n\n   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the\n   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at\n   your option.  The terms of these licenses can be found at:\n\n   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0\n   - OpenSSL license   : https://www.openssl.org/source/license.html\n   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0\n\n   More information about the BLAKE2 hash function can be found at\n   https://blake2.net.\n*/\n#ifndef BLAKE2_IMPL_H\n#define BLAKE2_IMPL_H\n\n#include <stdint.h>\n#include <string.h>\n\n#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)\n  #if   defined(_MSC_VER)\n    #define BLAKE2_INLINE __inline\n  #elif defined(__GNUC__)\n    #define BLAKE2_INLINE __inline__\n  #else\n    #define BLAKE2_INLINE\n  #endif\n#else\n  #define BLAKE2_INLINE inline\n#endif\n\nstatic BLAKE2_INLINE uint32_t load32( const void *src )\n{\n#if defined(NATIVE_LITTLE_ENDIAN)\n  uint32_t w;\n  memcpy(&w, src, sizeof w);\n  return w;\n#else\n  const uint8_t *p = ( const uint8_t * )src;\n  return (( uint32_t )( p[0] ) <<  0) |\n         (( uint32_t )( p[1] ) <<  8) |\n         (( uint32_t )( p[2] ) << 16) |\n         (( uint32_t )( p[3] ) << 24) ;\n#endif\n}\n\nstatic BLAKE2_INLINE uint64_t load64( const void *src )\n{\n#if defined(NATIVE_LITTLE_ENDIAN)\n  uint64_t w;\n  memcpy(&w, src, sizeof w);\n  return w;\n#else\n  const uint8_t *p = ( const uint8_t * )src;\n  return (( uint64_t )( p[0] ) <<  0) |\n         (( uint64_t )( p[1] ) <<  8) |\n         (( uint64_t )( p[2] ) << 16) |\n         (( uint64_t )( p[3] ) << 24) |\n         (( uint64_t )( p[4] ) << 32) |\n         (( uint64_t )( p[5] ) << 40) |\n         (( uint64_t )( p[6] ) << 48) |\n         (( uint64_t )( p[7] ) << 56) ;\n#endif\n}\n\nstatic BLAKE2_INLINE uint16_t load16( const void *src )\n{\n#if defined(NATIVE_LITTLE_ENDIAN)\n  uint16_t w;\n  memcpy(&w, src, sizeof w);\n  return w;\n#else\n  const uint8_t *p = ( const uint8_t * )src;\n  return (( uint16_t )( p[0] ) <<  0) |\n         (( uint16_t )( p[1] ) <<  8) ;\n#endif\n}\n\nstatic BLAKE2_INLINE void store16( void *dst, uint16_t w )\n{\n#if defined(NATIVE_LITTLE_ENDIAN)\n  memcpy(dst, &w, sizeof w);\n#else\n  uint8_t *p = ( uint8_t * )dst;\n  *p++ = ( uint8_t )w; w >>= 8;\n  *p++ = ( uint8_t )w;\n#endif\n}\n\nstatic BLAKE2_INLINE void store32( void *dst, uint32_t w )\n{\n#if defined(NATIVE_LITTLE_ENDIAN)\n  memcpy(dst, &w, sizeof w);\n#else\n  uint8_t *p = ( uint8_t * )dst;\n  p[0] = (uint8_t)(w >>  0);\n  p[1] = (uint8_t)(w >>  8);\n  p[2] = (uint8_t)(w >> 16);\n  p[3] = (uint8_t)(w >> 24);\n#endif\n}\n\nstatic BLAKE2_INLINE void store64( void *dst, uint64_t w )\n{\n#if defined(NATIVE_LITTLE_ENDIAN)\n  memcpy(dst, &w, sizeof w);\n#else\n  uint8_t *p = ( uint8_t * )dst;\n  p[0] = (uint8_t)(w >>  0);\n  p[1] = (uint8_t)(w >>  8);\n  p[2] = (uint8_t)(w >> 16);\n  p[3] = (uint8_t)(w >> 24);\n  p[4] = (uint8_t)(w >> 32);\n  p[5] = (uint8_t)(w >> 40);\n  p[6] = (uint8_t)(w >> 48);\n  p[7] = (uint8_t)(w >> 56);\n#endif\n}\n\nstatic BLAKE2_INLINE uint64_t load48( const void *src )\n{\n  const uint8_t *p = ( const uint8_t * )src;\n  return (( uint64_t )( p[0] ) <<  0) |\n         (( uint64_t )( p[1] ) <<  8) |\n         (( uint64_t )( p[2] ) << 16) |\n         (( uint64_t )( p[3] ) << 24) |\n         (( uint64_t )( p[4] ) << 32) |\n         (( uint64_t )( p[5] ) << 40) ;\n}\n\nstatic BLAKE2_INLINE void store48( void *dst, uint64_t w )\n{\n  uint8_t *p = ( uint8_t * )dst;\n  p[0] = (uint8_t)(w >>  0);\n  p[1] = (uint8_t)(w >>  8);\n  p[2] = (uint8_t)(w >> 16);\n  p[3] = (uint8_t)(w >> 24);\n  p[4] = (uint8_t)(w >> 32);\n  p[5] = (uint8_t)(w >> 40);\n}\n\nstatic BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c )\n{\n  return ( w >> c ) | ( w << ( 32 - c ) );\n}\n\nstatic BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c )\n{\n  return ( w >> c ) | ( w << ( 64 - c ) );\n}\n\n/* prevents compiler optimizing out memset() */\nstatic BLAKE2_INLINE void secure_zero_memory(void *v, size_t n)\n{\n  static void *(*const volatile memset_v)(void *, int, size_t) = &memset;\n  memset_v(v, 0, n);\n}\n\n#endif\n"
  },
  {
    "path": "deps/blake2/src/blake2.h",
    "content": "/*\n   BLAKE2 reference source code package - reference C implementations\n\n   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the\n   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at\n   your option.  The terms of these licenses can be found at:\n\n   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0\n   - OpenSSL license   : https://www.openssl.org/source/license.html\n   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0\n\n   More information about the BLAKE2 hash function can be found at\n   https://blake2.net.\n*/\n#ifndef BLAKE2_H\n#define BLAKE2_H\n\n#include <stddef.h>\n#include <stdint.h>\n\n#if defined(_MSC_VER)\n#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))\n#else\n#define BLAKE2_PACKED(x) x __attribute__((packed))\n#endif\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\n  enum blake2s_constant\n  {\n    BLAKE2S_BLOCKBYTES = 64,\n    BLAKE2S_OUTBYTES   = 32,\n    BLAKE2S_KEYBYTES   = 32,\n    BLAKE2S_SALTBYTES  = 8,\n    BLAKE2S_PERSONALBYTES = 8\n  };\n\n  enum blake2b_constant\n  {\n    BLAKE2B_BLOCKBYTES = 128,\n    BLAKE2B_OUTBYTES   = 64,\n    BLAKE2B_KEYBYTES   = 64,\n    BLAKE2B_SALTBYTES  = 16,\n    BLAKE2B_PERSONALBYTES = 16\n  };\n\n  typedef struct blake2s_state__\n  {\n    uint32_t h[8];\n    uint32_t t[2];\n    uint32_t f[2];\n    uint8_t  buf[BLAKE2S_BLOCKBYTES];\n    size_t   buflen;\n    size_t   outlen;\n    uint8_t  last_node;\n  } blake2s_state;\n\n  typedef struct blake2b_state__\n  {\n    uint64_t h[8];\n    uint64_t t[2];\n    uint64_t f[2];\n    uint8_t  buf[BLAKE2B_BLOCKBYTES];\n    size_t   buflen;\n    size_t   outlen;\n    uint8_t  last_node;\n  } blake2b_state;\n\n  typedef struct blake2sp_state__\n  {\n    blake2s_state S[8][1];\n    blake2s_state R[1];\n    uint8_t       buf[8 * BLAKE2S_BLOCKBYTES];\n    size_t        buflen;\n    size_t        outlen;\n  } blake2sp_state;\n\n  typedef struct blake2bp_state__\n  {\n    blake2b_state S[4][1];\n    blake2b_state R[1];\n    uint8_t       buf[4 * BLAKE2B_BLOCKBYTES];\n    size_t        buflen;\n    size_t        outlen;\n  } blake2bp_state;\n\n\n  BLAKE2_PACKED(struct blake2s_param__\n  {\n    uint8_t  digest_length; /* 1 */\n    uint8_t  key_length;    /* 2 */\n    uint8_t  fanout;        /* 3 */\n    uint8_t  depth;         /* 4 */\n    uint32_t leaf_length;   /* 8 */\n    uint32_t node_offset;  /* 12 */\n    uint16_t xof_length;    /* 14 */\n    uint8_t  node_depth;    /* 15 */\n    uint8_t  inner_length;  /* 16 */\n    /* uint8_t  reserved[0]; */\n    uint8_t  salt[BLAKE2S_SALTBYTES]; /* 24 */\n    uint8_t  personal[BLAKE2S_PERSONALBYTES];  /* 32 */\n  });\n\n  typedef struct blake2s_param__ blake2s_param;\n\n  BLAKE2_PACKED(struct blake2b_param__\n  {\n    uint8_t  digest_length; /* 1 */\n    uint8_t  key_length;    /* 2 */\n    uint8_t  fanout;        /* 3 */\n    uint8_t  depth;         /* 4 */\n    uint32_t leaf_length;   /* 8 */\n    uint32_t node_offset;   /* 12 */\n    uint32_t xof_length;    /* 16 */\n    uint8_t  node_depth;    /* 17 */\n    uint8_t  inner_length;  /* 18 */\n    uint8_t  reserved[14];  /* 32 */\n    uint8_t  salt[BLAKE2B_SALTBYTES]; /* 48 */\n    uint8_t  personal[BLAKE2B_PERSONALBYTES];  /* 64 */\n  });\n\n  typedef struct blake2b_param__ blake2b_param;\n\n  typedef struct blake2xs_state__\n  {\n    blake2s_state S[1];\n    blake2s_param P[1];\n  } blake2xs_state;\n\n  typedef struct blake2xb_state__\n  {\n    blake2b_state S[1];\n    blake2b_param P[1];\n  } blake2xb_state;\n\n  /* Padded structs result in a compile-time error */\n  enum {\n    BLAKE2_DUMMY_1 = 1/(int)(sizeof(blake2s_param) == BLAKE2S_OUTBYTES),\n    BLAKE2_DUMMY_2 = 1/(int)(sizeof(blake2b_param) == BLAKE2B_OUTBYTES)\n  };\n\n  /* Streaming API */\n  int blake2s_init( blake2s_state *S, size_t outlen );\n  int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen );\n  int blake2s_init_param( blake2s_state *S, const blake2s_param *P );\n  int blake2s_update( blake2s_state *S, const void *in, size_t inlen );\n  int blake2s_final( blake2s_state *S, void *out, size_t outlen );\n\n  int blake2b_init( blake2b_state *S, size_t outlen );\n  int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen );\n  int blake2b_init_param( blake2b_state *S, const blake2b_param *P );\n  int blake2b_update( blake2b_state *S, const void *in, size_t inlen );\n  int blake2b_final( blake2b_state *S, void *out, size_t outlen );\n\n  int blake2sp_init( blake2sp_state *S, size_t outlen );\n  int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen );\n  int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen );\n  int blake2sp_final( blake2sp_state *S, void *out, size_t outlen );\n\n  int blake2bp_init( blake2bp_state *S, size_t outlen );\n  int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen );\n  int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen );\n  int blake2bp_final( blake2bp_state *S, void *out, size_t outlen );\n\n  /* Variable output length API */\n  int blake2xs_init( blake2xs_state *S, const size_t outlen );\n  int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen );\n  int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen );\n  int blake2xs_final(blake2xs_state *S, void *out, size_t outlen);\n\n  int blake2xb_init( blake2xb_state *S, const size_t outlen );\n  int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen );\n  int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen );\n  int blake2xb_final(blake2xb_state *S, void *out, size_t outlen);\n\n  /* Simple API */\n  int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );\n  int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );\n\n  int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );\n  int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );\n\n  int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );\n  int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );\n\n  /* This is simply an alias for blake2b */\n  int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen );\n\n#if defined(__cplusplus)\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "deps/blake2/src/blake2b-ref.c",
    "content": "/*\n   BLAKE2 reference source code package - reference C implementations\n\n   Copyright 2012, Samuel Neves <sneves@dei.uc.pt>.  You may use this under the\n   terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at\n   your option.  The terms of these licenses can be found at:\n\n   - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0\n   - OpenSSL license   : https://www.openssl.org/source/license.html\n   - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0\n\n   More information about the BLAKE2 hash function can be found at\n   https://blake2.net.\n*/\n\n#include <stdint.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"blake2.h\"\n#include \"blake2-impl.h\"\n\nstatic const uint64_t blake2b_IV[8] =\n{\n  0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,\n  0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,\n  0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,\n  0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL\n};\n\nstatic const uint8_t blake2b_sigma[12][16] =\n{\n  {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,\n  { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 } ,\n  { 11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4 } ,\n  {  7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8 } ,\n  {  9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13 } ,\n  {  2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9 } ,\n  { 12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11 } ,\n  { 13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10 } ,\n  {  6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5 } ,\n  { 10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13 , 0 } ,\n  {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,\n  { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 }\n};\n\n\nstatic void blake2b_set_lastnode( blake2b_state *S )\n{\n  S->f[1] = (uint64_t)-1;\n}\n\n/* Some helper functions, not necessarily useful */\nstatic int blake2b_is_lastblock( const blake2b_state *S )\n{\n  return S->f[0] != 0;\n}\n\nstatic void blake2b_set_lastblock( blake2b_state *S )\n{\n  if( S->last_node ) blake2b_set_lastnode( S );\n\n  S->f[0] = (uint64_t)-1;\n}\n\nstatic void blake2b_increment_counter( blake2b_state *S, const uint64_t inc )\n{\n  S->t[0] += inc;\n  S->t[1] += ( S->t[0] < inc );\n}\n\nstatic void blake2b_init0( blake2b_state *S )\n{\n  size_t i;\n  memset( S, 0, sizeof( blake2b_state ) );\n\n  for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i];\n}\n\n/* init xors IV with input parameter block */\nint blake2b_init_param( blake2b_state *S, const blake2b_param *P )\n{\n  const uint8_t *p = ( const uint8_t * )( P );\n  size_t i;\n\n  blake2b_init0( S );\n\n  /* IV XOR ParamBlock */\n  for( i = 0; i < 8; ++i )\n    S->h[i] ^= load64( p + sizeof( S->h[i] ) * i );\n\n  S->outlen = P->digest_length;\n  return 0;\n}\n\n\n\nint blake2b_init( blake2b_state *S, size_t outlen )\n{\n  blake2b_param P[1];\n\n  if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;\n\n  P->digest_length = (uint8_t)outlen;\n  P->key_length    = 0;\n  P->fanout        = 1;\n  P->depth         = 1;\n  store32( &P->leaf_length, 0 );\n  store32( &P->node_offset, 0 );\n  store32( &P->xof_length, 0 );\n  P->node_depth    = 0;\n  P->inner_length  = 0;\n  memset( P->reserved, 0, sizeof( P->reserved ) );\n  memset( P->salt,     0, sizeof( P->salt ) );\n  memset( P->personal, 0, sizeof( P->personal ) );\n  return blake2b_init_param( S, P );\n}\n\n\nint blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen )\n{\n  blake2b_param P[1];\n\n  if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1;\n\n  if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1;\n\n  P->digest_length = (uint8_t)outlen;\n  P->key_length    = (uint8_t)keylen;\n  P->fanout        = 1;\n  P->depth         = 1;\n  store32( &P->leaf_length, 0 );\n  store32( &P->node_offset, 0 );\n  store32( &P->xof_length, 0 );\n  P->node_depth    = 0;\n  P->inner_length  = 0;\n  memset( P->reserved, 0, sizeof( P->reserved ) );\n  memset( P->salt,     0, sizeof( P->salt ) );\n  memset( P->personal, 0, sizeof( P->personal ) );\n\n  if( blake2b_init_param( S, P ) < 0 ) return -1;\n\n  {\n    uint8_t block[BLAKE2B_BLOCKBYTES];\n    memset( block, 0, BLAKE2B_BLOCKBYTES );\n    memcpy( block, key, keylen );\n    blake2b_update( S, block, BLAKE2B_BLOCKBYTES );\n    secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */\n  }\n  return 0;\n}\n\n#define G(r,i,a,b,c,d)                      \\\n  do {                                      \\\n    a = a + b + m[blake2b_sigma[r][2*i+0]]; \\\n    d = rotr64(d ^ a, 32);                  \\\n    c = c + d;                              \\\n    b = rotr64(b ^ c, 24);                  \\\n    a = a + b + m[blake2b_sigma[r][2*i+1]]; \\\n    d = rotr64(d ^ a, 16);                  \\\n    c = c + d;                              \\\n    b = rotr64(b ^ c, 63);                  \\\n  } while(0)\n\n#define ROUND(r)                    \\\n  do {                              \\\n    G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \\\n    G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \\\n    G(r,2,v[ 2],v[ 6],v[10],v[14]); \\\n    G(r,3,v[ 3],v[ 7],v[11],v[15]); \\\n    G(r,4,v[ 0],v[ 5],v[10],v[15]); \\\n    G(r,5,v[ 1],v[ 6],v[11],v[12]); \\\n    G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \\\n    G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \\\n  } while(0)\n\nstatic void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )\n{\n  uint64_t m[16];\n  uint64_t v[16];\n  size_t i;\n\n  for( i = 0; i < 16; ++i ) {\n    m[i] = load64( block + i * sizeof( m[i] ) );\n  }\n\n  for( i = 0; i < 8; ++i ) {\n    v[i] = S->h[i];\n  }\n\n  v[ 8] = blake2b_IV[0];\n  v[ 9] = blake2b_IV[1];\n  v[10] = blake2b_IV[2];\n  v[11] = blake2b_IV[3];\n  v[12] = blake2b_IV[4] ^ S->t[0];\n  v[13] = blake2b_IV[5] ^ S->t[1];\n  v[14] = blake2b_IV[6] ^ S->f[0];\n  v[15] = blake2b_IV[7] ^ S->f[1];\n\n  ROUND( 0 );\n  ROUND( 1 );\n  ROUND( 2 );\n  ROUND( 3 );\n  ROUND( 4 );\n  ROUND( 5 );\n  ROUND( 6 );\n  ROUND( 7 );\n  ROUND( 8 );\n  ROUND( 9 );\n  ROUND( 10 );\n  ROUND( 11 );\n\n  for( i = 0; i < 8; ++i ) {\n    S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];\n  }\n}\n\n#undef G\n#undef ROUND\n\nint blake2b_update( blake2b_state *S, const void *pin, size_t inlen )\n{\n  const unsigned char * in = (const unsigned char *)pin;\n  if( inlen > 0 )\n  {\n    size_t left = S->buflen;\n    size_t fill = BLAKE2B_BLOCKBYTES - left;\n    if( inlen > fill )\n    {\n      S->buflen = 0;\n      memcpy( S->buf + left, in, fill ); /* Fill buffer */\n      blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES );\n      blake2b_compress( S, S->buf ); /* Compress */\n      in += fill; inlen -= fill;\n      while(inlen > BLAKE2B_BLOCKBYTES) {\n        blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES);\n        blake2b_compress( S, in );\n        in += BLAKE2B_BLOCKBYTES;\n        inlen -= BLAKE2B_BLOCKBYTES;\n      }\n    }\n    memcpy( S->buf + S->buflen, in, inlen );\n    S->buflen += inlen;\n  }\n  return 0;\n}\n\nint blake2b_final( blake2b_state *S, void *out, size_t outlen )\n{\n  uint8_t buffer[BLAKE2B_OUTBYTES] = {0};\n  size_t i;\n\n  if( out == NULL || outlen < S->outlen )\n    return -1;\n\n  if( blake2b_is_lastblock( S ) )\n    return -1;\n\n  blake2b_increment_counter( S, S->buflen );\n  blake2b_set_lastblock( S );\n  memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */\n  blake2b_compress( S, S->buf );\n\n  for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */\n    store64( buffer + sizeof( S->h[i] ) * i, S->h[i] );\n\n  memcpy( out, buffer, S->outlen );\n  secure_zero_memory(buffer, sizeof(buffer));\n  return 0;\n}\n\n/* inlen, at least, should be uint64_t. Others can be size_t. */\nint blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen )\n{\n  blake2b_state S[1];\n\n  /* Verify parameters */\n  if ( NULL == in && inlen > 0 ) return -1;\n\n  if ( NULL == out ) return -1;\n\n  if( NULL == key && keylen > 0 ) return -1;\n\n  if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1;\n\n  if( keylen > BLAKE2B_KEYBYTES ) return -1;\n\n  if( keylen > 0 )\n  {\n    if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1;\n  }\n  else\n  {\n    if( blake2b_init( S, outlen ) < 0 ) return -1;\n  }\n\n  blake2b_update( S, ( const uint8_t * )in, inlen );\n  blake2b_final( S, out, outlen );\n  return 0;\n}\n\nint blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) {\n  return blake2b(out, outlen, in, inlen, key, keylen);\n}\n\n#if defined(SUPERCOP)\nint crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen )\n{\n  return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 );\n}\n#endif\n\n#if defined(BLAKE2B_SELFTEST)\n#include <string.h>\n#include \"blake2-kat.h\"\nint main( void )\n{\n  uint8_t key[BLAKE2B_KEYBYTES];\n  uint8_t buf[BLAKE2_KAT_LENGTH];\n  size_t i, step;\n\n  for( i = 0; i < BLAKE2B_KEYBYTES; ++i )\n    key[i] = ( uint8_t )i;\n\n  for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )\n    buf[i] = ( uint8_t )i;\n\n  /* Test simple API */\n  for( i = 0; i < BLAKE2_KAT_LENGTH; ++i )\n  {\n    uint8_t hash[BLAKE2B_OUTBYTES];\n    blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES );\n\n    if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) )\n    {\n      goto fail;\n    }\n  }\n\n  /* Test streaming API */\n  for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) {\n    for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) {\n      uint8_t hash[BLAKE2B_OUTBYTES];\n      blake2b_state S;\n      uint8_t * p = buf;\n      size_t mlen = i;\n      int err = 0;\n\n      if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) {\n        goto fail;\n      }\n\n      while (mlen >= step) {\n        if ( (err = blake2b_update(&S, p, step)) < 0 ) {\n          goto fail;\n        }\n        mlen -= step;\n        p += step;\n      }\n      if ( (err = blake2b_update(&S, p, mlen)) < 0) {\n        goto fail;\n      }\n      if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) {\n        goto fail;\n      }\n\n      if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) {\n        goto fail;\n      }\n    }\n  }\n\n  puts( \"ok\" );\n  return 0;\nfail:\n  puts(\"error\");\n  return -1;\n}\n#endif\n"
  },
  {
    "path": "deps/glad/.clang-format",
    "content": "Language: Cpp\nSortIncludes: false\nDisableFormat: true\n"
  },
  {
    "path": "deps/glad/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nfind_package(OpenGL REQUIRED)\n\nadd_library(obsglad OBJECT)\nadd_library(OBS::glad ALIAS obsglad)\n\ntarget_sources(\n  obsglad\n  PRIVATE\n    src/glad.c\n    $<$<PLATFORM_ID:Windows>:src/glad_wgl.c>\n    $<$<TARGET_EXISTS:OpenGL::EGL>:src/glad_egl.c>\n    $<$<TARGET_EXISTS:OpenGL::EGL>:include/EGL/eglplatform.h>\n  PUBLIC\n    include/glad/glad.h\n    \"$<$<PLATFORM_ID:Windows>:${CMAKE_CURRENT_SOURCE_DIR}/include/glad/glad_wgl.h>\"\n    \"$<$<TARGET_EXISTS:OpenGL::EGL>:${CMAKE_CURRENT_SOURCE_DIR}/include/glad/glad_egl.h>\"\n)\n\ntarget_compile_options(obsglad PRIVATE $<$<COMPILE_LANG_AND_ID:C,AppleClang,Clang>:-Wno-strict-prototypes>)\n\ntarget_include_directories(obsglad PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}/include\")\n\ntarget_link_libraries(\n  obsglad\n  PRIVATE $<$<NOT:$<PLATFORM_ID:Windows,Darwin>>:${CMAKE_DL_LIBS}>\n  PUBLIC OpenGL::GL $<$<TARGET_EXISTS:OpenGL::EGL>:OpenGL::EGL>\n)\n\nset_target_properties(obsglad PROPERTIES FOLDER deps POSITION_INDEPENDENT_CODE TRUE)\n"
  },
  {
    "path": "deps/glad/include/EGL/eglplatform.h",
    "content": "#ifndef __eglplatform_h_\n#define __eglplatform_h_\n\n/*\n** Copyright 2007-2020 The Khronos Group Inc.\n** SPDX-License-Identifier: Apache-2.0\n*/\n\n/* Platform-specific types and definitions for egl.h\n *\n * Adopters may modify khrplatform.h and this file to suit their platform.\n * You are encouraged to submit all modifications to the Khronos group so that\n * they can be included in future versions of this file.  Please submit changes\n * by filing an issue or pull request on the public Khronos EGL Registry, at\n * https://www.github.com/KhronosGroup/EGL-Registry/\n */\n\n#include <KHR/khrplatform.h>\n\n/* Macros used in EGL function prototype declarations.\n *\n * EGL functions should be prototyped as:\n *\n * EGLAPI return-type EGLAPIENTRY eglFunction(arguments);\n * typedef return-type (EXPAPIENTRYP PFNEGLFUNCTIONPROC) (arguments);\n *\n * KHRONOS_APICALL and KHRONOS_APIENTRY are defined in KHR/khrplatform.h\n */\n\n#ifndef EGLAPI\n#define EGLAPI KHRONOS_APICALL\n#endif\n\n#ifndef EGLAPIENTRY\n#define EGLAPIENTRY  KHRONOS_APIENTRY\n#endif\n#define EGLAPIENTRYP EGLAPIENTRY*\n\n/* The types NativeDisplayType, NativeWindowType, and NativePixmapType\n * are aliases of window-system-dependent types, such as X Display * or\n * Windows Device Context. They must be defined in platform-specific\n * code below. The EGL-prefixed versions of Native*Type are the same\n * types, renamed in EGL 1.3 so all types in the API start with \"EGL\".\n *\n * Khronos STRONGLY RECOMMENDS that you use the default definitions\n * provided below, since these changes affect both binary and source\n * portability of applications using EGL running on different EGL\n * implementations.\n */\n\n#if defined(EGL_NO_PLATFORM_SPECIFIC_TYPES)\n\ntypedef void *EGLNativeDisplayType;\ntypedef void *EGLNativePixmapType;\ntypedef void *EGLNativeWindowType;\n\n#elif defined(_WIN32) || defined(__VC32__) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) /* Win32 and WinCE */\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN 1\n#endif\n#include <windows.h>\n\ntypedef HDC     EGLNativeDisplayType;\ntypedef HBITMAP EGLNativePixmapType;\ntypedef HWND    EGLNativeWindowType;\n\n#elif defined(__QNX__)\n\ntypedef khronos_uintptr_t      EGLNativeDisplayType;\ntypedef struct _screen_pixmap* EGLNativePixmapType;  /* screen_pixmap_t */\ntypedef struct _screen_window* EGLNativeWindowType;  /* screen_window_t */\n\n#elif defined(__EMSCRIPTEN__)\n\ntypedef int EGLNativeDisplayType;\ntypedef int EGLNativePixmapType;\ntypedef int EGLNativeWindowType;\n\n#elif defined(__WINSCW__) || defined(__SYMBIAN32__)  /* Symbian */\n\ntypedef int   EGLNativeDisplayType;\ntypedef void *EGLNativePixmapType;\ntypedef void *EGLNativeWindowType;\n\n#elif defined(WL_EGL_PLATFORM)\n\ntypedef struct wl_display     *EGLNativeDisplayType;\ntypedef struct wl_egl_pixmap  *EGLNativePixmapType;\ntypedef struct wl_egl_window  *EGLNativeWindowType;\n\n#elif defined(__GBM__)\n\ntypedef struct gbm_device  *EGLNativeDisplayType;\ntypedef struct gbm_bo      *EGLNativePixmapType;\ntypedef void               *EGLNativeWindowType;\n\n#elif defined(__ANDROID__) || defined(ANDROID)\n\nstruct ANativeWindow;\nstruct egl_native_pixmap_t;\n\ntypedef void*                           EGLNativeDisplayType;\ntypedef struct egl_native_pixmap_t*     EGLNativePixmapType;\ntypedef struct ANativeWindow*           EGLNativeWindowType;\n\n#elif defined(USE_OZONE)\n\ntypedef intptr_t EGLNativeDisplayType;\ntypedef intptr_t EGLNativePixmapType;\ntypedef intptr_t EGLNativeWindowType;\n\n#elif defined(USE_X11)\n\n/* X11 (tentative)  */\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\ntypedef Display *EGLNativeDisplayType;\ntypedef Pixmap   EGLNativePixmapType;\ntypedef Window   EGLNativeWindowType;\n\n#elif defined(__unix__)\n\ntypedef void             *EGLNativeDisplayType;\ntypedef khronos_uintptr_t EGLNativePixmapType;\ntypedef khronos_uintptr_t EGLNativeWindowType;\n\n#elif defined(__APPLE__)\n\ntypedef int   EGLNativeDisplayType;\ntypedef void *EGLNativePixmapType;\ntypedef void *EGLNativeWindowType;\n\n#elif defined(__HAIKU__)\n\n#include <kernel/image.h>\n\ntypedef void              *EGLNativeDisplayType;\ntypedef khronos_uintptr_t  EGLNativePixmapType;\ntypedef khronos_uintptr_t  EGLNativeWindowType;\n\n#elif defined(__Fuchsia__)\n\ntypedef void              *EGLNativeDisplayType;\ntypedef khronos_uintptr_t  EGLNativePixmapType;\ntypedef khronos_uintptr_t  EGLNativeWindowType;\n\n#else\n#error \"Platform not recognized\"\n#endif\n\n/* EGL 1.2 types, renamed for consistency in EGL 1.3 */\ntypedef EGLNativeDisplayType NativeDisplayType;\ntypedef EGLNativePixmapType  NativePixmapType;\ntypedef EGLNativeWindowType  NativeWindowType;\n\n\n/* Define EGLint. This must be a signed integral type large enough to contain\n * all legal attribute names and values passed into and out of EGL, whether\n * their type is boolean, bitmask, enumerant (symbolic constant), integer,\n * handle, or other.  While in general a 32-bit integer will suffice, if\n * handles are 64 bit types, then EGLint should be defined as a signed 64-bit\n * integer type.\n */\ntypedef khronos_int32_t EGLint;\n\n\n/* C++ / C typecast macros for special EGL handle values */\n#if defined(__cplusplus)\n#define EGL_CAST(type, value) (static_cast<type>(value))\n#else\n#define EGL_CAST(type, value) ((type) (value))\n#endif\n\n#endif /* __eglplatform_h */\n"
  },
  {
    "path": "deps/glad/include/KHR/khrplatform.h",
    "content": "#ifndef __khrplatform_h_\n#define __khrplatform_h_\n\n/*\n** Copyright (c) 2008-2018 The Khronos Group Inc.\n**\n** Permission is hereby granted, free of charge, to any person obtaining a\n** copy of this software and/or associated documentation files (the\n** \"Materials\"), to deal in the Materials without restriction, including\n** without limitation the rights to use, copy, modify, merge, publish,\n** distribute, sublicense, and/or sell copies of the Materials, and to\n** permit persons to whom the Materials are furnished to do so, subject to\n** the following conditions:\n**\n** The above copyright notice and this permission notice shall be included\n** in all copies or substantial portions of the Materials.\n**\n** THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.\n*/\n\n/* Khronos platform-specific types and definitions.\n *\n * The master copy of khrplatform.h is maintained in the Khronos EGL\n * Registry repository at https://github.com/KhronosGroup/EGL-Registry\n * The last semantic modification to khrplatform.h was at commit ID:\n *      67a3e0864c2d75ea5287b9f3d2eb74a745936692\n *\n * Adopters may modify this file to suit their platform. Adopters are\n * encouraged to submit platform specific modifications to the Khronos\n * group so that they can be included in future versions of this file.\n * Please submit changes by filing pull requests or issues on\n * the EGL Registry repository linked above.\n *\n *\n * See the Implementer's Guidelines for information about where this file\n * should be located on your system and for more details of its use:\n *    http://www.khronos.org/registry/implementers_guide.pdf\n *\n * This file should be included as\n *        #include <KHR/khrplatform.h>\n * by Khronos client API header files that use its types and defines.\n *\n * The types in khrplatform.h should only be used to define API-specific types.\n *\n * Types defined in khrplatform.h:\n *    khronos_int8_t              signed   8  bit\n *    khronos_uint8_t             unsigned 8  bit\n *    khronos_int16_t             signed   16 bit\n *    khronos_uint16_t            unsigned 16 bit\n *    khronos_int32_t             signed   32 bit\n *    khronos_uint32_t            unsigned 32 bit\n *    khronos_int64_t             signed   64 bit\n *    khronos_uint64_t            unsigned 64 bit\n *    khronos_intptr_t            signed   same number of bits as a pointer\n *    khronos_uintptr_t           unsigned same number of bits as a pointer\n *    khronos_ssize_t             signed   size\n *    khronos_usize_t             unsigned size\n *    khronos_float_t             signed   32 bit floating point\n *    khronos_time_ns_t           unsigned 64 bit time in nanoseconds\n *    khronos_utime_nanoseconds_t unsigned time interval or absolute time in\n *                                         nanoseconds\n *    khronos_stime_nanoseconds_t signed time interval in nanoseconds\n *    khronos_boolean_enum_t      enumerated boolean type. This should\n *      only be used as a base type when a client API's boolean type is\n *      an enum. Client APIs which use an integer or other type for\n *      booleans cannot use this as the base type for their boolean.\n *\n * Tokens defined in khrplatform.h:\n *\n *    KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.\n *\n *    KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.\n *    KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.\n *\n * Calling convention macros defined in this file:\n *    KHRONOS_APICALL\n *    KHRONOS_APIENTRY\n *    KHRONOS_APIATTRIBUTES\n *\n * These may be used in function prototypes as:\n *\n *      KHRONOS_APICALL void KHRONOS_APIENTRY funcname(\n *                                  int arg1,\n *                                  int arg2) KHRONOS_APIATTRIBUTES;\n */\n\n#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)\n#   define KHRONOS_STATIC 1\n#endif\n\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APICALL\n *-------------------------------------------------------------------------\n * This precedes the return type of the function in the function prototype.\n */\n#if defined(KHRONOS_STATIC)\n    /* If the preprocessor constant KHRONOS_STATIC is defined, make the\n     * header compatible with static linking. */\n#   define KHRONOS_APICALL\n#elif defined(_WIN32)\n#   define KHRONOS_APICALL __declspec(dllimport)\n#elif defined (__SYMBIAN32__)\n#   define KHRONOS_APICALL IMPORT_C\n#elif defined(__ANDROID__)\n#   define KHRONOS_APICALL __attribute__((visibility(\"default\")))\n#else\n#   define KHRONOS_APICALL\n#endif\n\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APIENTRY\n *-------------------------------------------------------------------------\n * This follows the return type of the function  and precedes the function\n * name in the function prototype.\n */\n#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)\n    /* Win32 but not WinCE */\n#   define KHRONOS_APIENTRY __stdcall\n#else\n#   define KHRONOS_APIENTRY\n#endif\n\n/*-------------------------------------------------------------------------\n * Definition of KHRONOS_APIATTRIBUTES\n *-------------------------------------------------------------------------\n * This follows the closing parenthesis of the function prototype arguments.\n */\n#if defined (__ARMCC_2__)\n#define KHRONOS_APIATTRIBUTES __softfp\n#else\n#define KHRONOS_APIATTRIBUTES\n#endif\n\n/*-------------------------------------------------------------------------\n * basic type definitions\n *-----------------------------------------------------------------------*/\n#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)\n\n\n/*\n * Using <stdint.h>\n */\n#include <stdint.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n/*\n * To support platform where unsigned long cannot be used interchangeably with\n * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.\n * Ideally, we could just use (u)intptr_t everywhere, but this could result in\n * ABI breakage if khronos_uintptr_t is changed from unsigned long to\n * unsigned long long or similar (this results in different C++ name mangling).\n * To avoid changes for existing platforms, we restrict usage of intptr_t to\n * platforms where the size of a pointer is larger than the size of long.\n */\n#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)\n#if __SIZEOF_POINTER__ > __SIZEOF_LONG__\n#define KHRONOS_USE_INTPTR_T\n#endif\n#endif\n\n#elif defined(__VMS ) || defined(__sgi)\n\n/*\n * Using <inttypes.h>\n */\n#include <inttypes.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)\n\n/*\n * Win32\n */\ntypedef __int32                 khronos_int32_t;\ntypedef unsigned __int32        khronos_uint32_t;\ntypedef __int64                 khronos_int64_t;\ntypedef unsigned __int64        khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#elif defined(__sun__) || defined(__digital__)\n\n/*\n * Sun or Digital\n */\ntypedef int                     khronos_int32_t;\ntypedef unsigned int            khronos_uint32_t;\n#if defined(__arch64__) || defined(_LP64)\ntypedef long int                khronos_int64_t;\ntypedef unsigned long int       khronos_uint64_t;\n#else\ntypedef long long int           khronos_int64_t;\ntypedef unsigned long long int  khronos_uint64_t;\n#endif /* __arch64__ */\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#elif 0\n\n/*\n * Hypothetical platform with no float or int64 support\n */\ntypedef int                     khronos_int32_t;\ntypedef unsigned int            khronos_uint32_t;\n#define KHRONOS_SUPPORT_INT64   0\n#define KHRONOS_SUPPORT_FLOAT   0\n\n#else\n\n/*\n * Generic fallback\n */\n#include <stdint.h>\ntypedef int32_t                 khronos_int32_t;\ntypedef uint32_t                khronos_uint32_t;\ntypedef int64_t                 khronos_int64_t;\ntypedef uint64_t                khronos_uint64_t;\n#define KHRONOS_SUPPORT_INT64   1\n#define KHRONOS_SUPPORT_FLOAT   1\n\n#endif\n\n\n/*\n * Types that are (so far) the same on all platforms\n */\ntypedef signed   char          khronos_int8_t;\ntypedef unsigned char          khronos_uint8_t;\ntypedef signed   short int     khronos_int16_t;\ntypedef unsigned short int     khronos_uint16_t;\n\n/*\n * Types that differ between LLP64 and LP64 architectures - in LLP64,\n * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears\n * to be the only LLP64 architecture in current use.\n */\n#ifdef KHRONOS_USE_INTPTR_T\ntypedef intptr_t               khronos_intptr_t;\ntypedef uintptr_t              khronos_uintptr_t;\n#elif defined(_WIN64)\ntypedef signed   long long int khronos_intptr_t;\ntypedef unsigned long long int khronos_uintptr_t;\n#else\ntypedef signed   long  int     khronos_intptr_t;\ntypedef unsigned long  int     khronos_uintptr_t;\n#endif\n\n#if defined(_WIN64)\ntypedef signed   long long int khronos_ssize_t;\ntypedef unsigned long long int khronos_usize_t;\n#else\ntypedef signed   long  int     khronos_ssize_t;\ntypedef unsigned long  int     khronos_usize_t;\n#endif\n\n#if KHRONOS_SUPPORT_FLOAT\n/*\n * Float type\n */\ntypedef          float         khronos_float_t;\n#endif\n\n#if KHRONOS_SUPPORT_INT64\n/* Time types\n *\n * These types can be used to represent a time interval in nanoseconds or\n * an absolute Unadjusted System Time.  Unadjusted System Time is the number\n * of nanoseconds since some arbitrary system event (e.g. since the last\n * time the system booted).  The Unadjusted System Time is an unsigned\n * 64 bit value that wraps back to 0 every 584 years.  Time intervals\n * may be either signed or unsigned.\n */\ntypedef khronos_uint64_t       khronos_utime_nanoseconds_t;\ntypedef khronos_int64_t        khronos_stime_nanoseconds_t;\n#endif\n\n/*\n * Dummy value used to pad enum types to 32 bits.\n */\n#ifndef KHRONOS_MAX_ENUM\n#define KHRONOS_MAX_ENUM 0x7FFFFFFF\n#endif\n\n/*\n * Enumerated boolean type\n *\n * Values other than zero should be considered to be true.  Therefore\n * comparisons should not be made against KHRONOS_TRUE.\n */\ntypedef enum {\n    KHRONOS_FALSE = 0,\n    KHRONOS_TRUE  = 1,\n    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM\n} khronos_boolean_enum_t;\n\n#endif /* __khrplatform_h_ */\n"
  },
  {
    "path": "deps/glad/include/glad/glad.h",
    "content": "\n#ifndef __glad_h_\n\n#ifdef __gl_h_\n#error OpenGL header already included, remove this include, glad already provides it\n#endif\n\n#define __glad_h_\n#define __gl_h_\n\n#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN 1\n#endif\n#include <windows.h>\n#endif\n\n#ifndef APIENTRY\n#define APIENTRY\n#endif\n#ifndef APIENTRYP\n#define APIENTRYP APIENTRY *\n#endif\n\nextern struct gladGLversionStruct {\n    int major;\n    int minor;\n} GLVersion;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void* (* GLADloadproc)(const char *name);\n\n#ifndef GLAPI\n# if defined(GLAD_GLAPI_EXPORT)\n#  if defined(WIN32) || defined(__CYGWIN__)\n#   if defined(GLAD_GLAPI_EXPORT_BUILD)\n#    if defined(__GNUC__)\n#     define GLAPI __attribute__ ((dllexport)) extern\n#    else\n#     define GLAPI __declspec(dllexport) extern\n#    endif\n#   else\n#    if defined(__GNUC__)\n#     define GLAPI __attribute__ ((dllimport)) extern\n#    else\n#     define GLAPI __declspec(dllimport) extern\n#    endif\n#   endif\n#  elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD)\n#   define GLAPI __attribute__ ((visibility (\"default\"))) extern\n#  else\n#   define GLAPI extern\n#  endif\n# else\n#  define GLAPI extern\n# endif\n#endif\n\nGLAPI int gladLoadGL(void);\n\nGLAPI void gladLoadGLLoader(GLADloadproc);\n\n#include <stddef.h>\n#include <KHR/khrplatform.h>\n#ifndef GLEXT_64_TYPES_DEFINED\n/* This code block is duplicated in glxext.h, so must be protected */\n#define GLEXT_64_TYPES_DEFINED\n/* Define int32_t, int64_t, and uint64_t types for UST/MSC */\n/* (as used in the GL_EXT_timer_query extension). */\n#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L\n#include <inttypes.h>\n#elif defined(__sun__) || defined(__digital__)\n#include <inttypes.h>\n#if defined(__STDC__)\n#if defined(__arch64__) || defined(_LP64)\ntypedef long int int64_t;\ntypedef unsigned long int uint64_t;\n#else\ntypedef long long int int64_t;\ntypedef unsigned long long int uint64_t;\n#endif /* __arch64__ */\n#endif /* __STDC__ */\n#elif defined( __VMS ) || defined(__sgi)\n#include <inttypes.h>\n#elif defined(__SCO__) || defined(__USLC__)\n#include <stdint.h>\n#elif defined(__UNIXOS2__) || defined(__SOL64__)\ntypedef long int int32_t;\ntypedef long long int int64_t;\ntypedef unsigned long long int uint64_t;\n#elif defined(_WIN32) && defined(__GNUC__)\n#include <stdint.h>\n#elif defined(_WIN32)\ntypedef __int32 int32_t;\ntypedef __int64 int64_t;\ntypedef unsigned __int64 uint64_t;\n#else\n/* Fallback if nothing above works */\n#include <inttypes.h>\n#endif\n#endif\ntypedef unsigned int GLenum;\ntypedef unsigned char GLboolean;\ntypedef unsigned int GLbitfield;\ntypedef void GLvoid;\ntypedef signed char GLbyte;\ntypedef short GLshort;\ntypedef int GLint;\ntypedef int GLclampx;\ntypedef unsigned char GLubyte;\ntypedef unsigned short GLushort;\ntypedef unsigned int GLuint;\ntypedef int GLsizei;\ntypedef float GLfloat;\ntypedef float GLclampf;\ntypedef double GLdouble;\ntypedef double GLclampd;\ntypedef void *GLeglImageOES;\ntypedef char GLchar;\ntypedef char GLcharARB;\n#ifdef __APPLE__\ntypedef void *GLhandleARB;\n#else\ntypedef unsigned int GLhandleARB;\n#endif\ntypedef unsigned short GLhalfARB;\ntypedef unsigned short GLhalf;\ntypedef GLint GLfixed;\ntypedef ptrdiff_t GLintptr;\ntypedef ptrdiff_t GLsizeiptr;\ntypedef int64_t GLint64;\ntypedef uint64_t GLuint64;\ntypedef ptrdiff_t GLintptrARB;\ntypedef ptrdiff_t GLsizeiptrARB;\ntypedef int64_t GLint64EXT;\ntypedef uint64_t GLuint64EXT;\ntypedef struct __GLsync *GLsync;\nstruct _cl_context;\nstruct _cl_event;\ntypedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (APIENTRY *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam);\ntypedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam);\ntypedef unsigned short GLhalfNV;\ntypedef GLintptr GLvdpauSurfaceNV;\n#define GL_DEPTH_BUFFER_BIT 0x00000100\n#define GL_STENCIL_BUFFER_BIT 0x00000400\n#define GL_COLOR_BUFFER_BIT 0x00004000\n#define GL_FALSE 0\n#define GL_TRUE 1\n#define GL_POINTS 0x0000\n#define GL_LINES 0x0001\n#define GL_LINE_LOOP 0x0002\n#define GL_LINE_STRIP 0x0003\n#define GL_TRIANGLES 0x0004\n#define GL_TRIANGLE_STRIP 0x0005\n#define GL_TRIANGLE_FAN 0x0006\n#define GL_NEVER 0x0200\n#define GL_LESS 0x0201\n#define GL_EQUAL 0x0202\n#define GL_LEQUAL 0x0203\n#define GL_GREATER 0x0204\n#define GL_NOTEQUAL 0x0205\n#define GL_GEQUAL 0x0206\n#define GL_ALWAYS 0x0207\n#define GL_ZERO 0\n#define GL_ONE 1\n#define GL_SRC_COLOR 0x0300\n#define GL_ONE_MINUS_SRC_COLOR 0x0301\n#define GL_SRC_ALPHA 0x0302\n#define GL_ONE_MINUS_SRC_ALPHA 0x0303\n#define GL_DST_ALPHA 0x0304\n#define GL_ONE_MINUS_DST_ALPHA 0x0305\n#define GL_DST_COLOR 0x0306\n#define GL_ONE_MINUS_DST_COLOR 0x0307\n#define GL_SRC_ALPHA_SATURATE 0x0308\n#define GL_NONE 0\n#define GL_FRONT_LEFT 0x0400\n#define GL_FRONT_RIGHT 0x0401\n#define GL_BACK_LEFT 0x0402\n#define GL_BACK_RIGHT 0x0403\n#define GL_FRONT 0x0404\n#define GL_BACK 0x0405\n#define GL_LEFT 0x0406\n#define GL_RIGHT 0x0407\n#define GL_FRONT_AND_BACK 0x0408\n#define GL_NO_ERROR 0\n#define GL_INVALID_ENUM 0x0500\n#define GL_INVALID_VALUE 0x0501\n#define GL_INVALID_OPERATION 0x0502\n#define GL_OUT_OF_MEMORY 0x0505\n#define GL_CW 0x0900\n#define GL_CCW 0x0901\n#define GL_POINT_SIZE 0x0B11\n#define GL_POINT_SIZE_RANGE 0x0B12\n#define GL_POINT_SIZE_GRANULARITY 0x0B13\n#define GL_LINE_SMOOTH 0x0B20\n#define GL_LINE_WIDTH 0x0B21\n#define GL_LINE_WIDTH_RANGE 0x0B22\n#define GL_LINE_WIDTH_GRANULARITY 0x0B23\n#define GL_POLYGON_MODE 0x0B40\n#define GL_POLYGON_SMOOTH 0x0B41\n#define GL_CULL_FACE 0x0B44\n#define GL_CULL_FACE_MODE 0x0B45\n#define GL_FRONT_FACE 0x0B46\n#define GL_DEPTH_RANGE 0x0B70\n#define GL_DEPTH_TEST 0x0B71\n#define GL_DEPTH_WRITEMASK 0x0B72\n#define GL_DEPTH_CLEAR_VALUE 0x0B73\n#define GL_DEPTH_FUNC 0x0B74\n#define GL_STENCIL_TEST 0x0B90\n#define GL_STENCIL_CLEAR_VALUE 0x0B91\n#define GL_STENCIL_FUNC 0x0B92\n#define GL_STENCIL_VALUE_MASK 0x0B93\n#define GL_STENCIL_FAIL 0x0B94\n#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95\n#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96\n#define GL_STENCIL_REF 0x0B97\n#define GL_STENCIL_WRITEMASK 0x0B98\n#define GL_VIEWPORT 0x0BA2\n#define GL_DITHER 0x0BD0\n#define GL_BLEND_DST 0x0BE0\n#define GL_BLEND_SRC 0x0BE1\n#define GL_BLEND 0x0BE2\n#define GL_LOGIC_OP_MODE 0x0BF0\n#define GL_COLOR_LOGIC_OP 0x0BF2\n#define GL_DRAW_BUFFER 0x0C01\n#define GL_READ_BUFFER 0x0C02\n#define GL_SCISSOR_BOX 0x0C10\n#define GL_SCISSOR_TEST 0x0C11\n#define GL_COLOR_CLEAR_VALUE 0x0C22\n#define GL_COLOR_WRITEMASK 0x0C23\n#define GL_DOUBLEBUFFER 0x0C32\n#define GL_STEREO 0x0C33\n#define GL_LINE_SMOOTH_HINT 0x0C52\n#define GL_POLYGON_SMOOTH_HINT 0x0C53\n#define GL_UNPACK_SWAP_BYTES 0x0CF0\n#define GL_UNPACK_LSB_FIRST 0x0CF1\n#define GL_UNPACK_ROW_LENGTH 0x0CF2\n#define GL_UNPACK_SKIP_ROWS 0x0CF3\n#define GL_UNPACK_SKIP_PIXELS 0x0CF4\n#define GL_UNPACK_ALIGNMENT 0x0CF5\n#define GL_PACK_SWAP_BYTES 0x0D00\n#define GL_PACK_LSB_FIRST 0x0D01\n#define GL_PACK_ROW_LENGTH 0x0D02\n#define GL_PACK_SKIP_ROWS 0x0D03\n#define GL_PACK_SKIP_PIXELS 0x0D04\n#define GL_PACK_ALIGNMENT 0x0D05\n#define GL_MAX_TEXTURE_SIZE 0x0D33\n#define GL_MAX_VIEWPORT_DIMS 0x0D3A\n#define GL_SUBPIXEL_BITS 0x0D50\n#define GL_TEXTURE_1D 0x0DE0\n#define GL_TEXTURE_2D 0x0DE1\n#define GL_POLYGON_OFFSET_UNITS 0x2A00\n#define GL_POLYGON_OFFSET_POINT 0x2A01\n#define GL_POLYGON_OFFSET_LINE 0x2A02\n#define GL_POLYGON_OFFSET_FILL 0x8037\n#define GL_POLYGON_OFFSET_FACTOR 0x8038\n#define GL_TEXTURE_BINDING_1D 0x8068\n#define GL_TEXTURE_BINDING_2D 0x8069\n#define GL_TEXTURE_WIDTH 0x1000\n#define GL_TEXTURE_HEIGHT 0x1001\n#define GL_TEXTURE_INTERNAL_FORMAT 0x1003\n#define GL_TEXTURE_BORDER_COLOR 0x1004\n#define GL_TEXTURE_RED_SIZE 0x805C\n#define GL_TEXTURE_GREEN_SIZE 0x805D\n#define GL_TEXTURE_BLUE_SIZE 0x805E\n#define GL_TEXTURE_ALPHA_SIZE 0x805F\n#define GL_DONT_CARE 0x1100\n#define GL_FASTEST 0x1101\n#define GL_NICEST 0x1102\n#define GL_BYTE 0x1400\n#define GL_UNSIGNED_BYTE 0x1401\n#define GL_SHORT 0x1402\n#define GL_UNSIGNED_SHORT 0x1403\n#define GL_INT 0x1404\n#define GL_UNSIGNED_INT 0x1405\n#define GL_FLOAT 0x1406\n#define GL_DOUBLE 0x140A\n#define GL_CLEAR 0x1500\n#define GL_AND 0x1501\n#define GL_AND_REVERSE 0x1502\n#define GL_COPY 0x1503\n#define GL_AND_INVERTED 0x1504\n#define GL_NOOP 0x1505\n#define GL_XOR 0x1506\n#define GL_OR 0x1507\n#define GL_NOR 0x1508\n#define GL_EQUIV 0x1509\n#define GL_INVERT 0x150A\n#define GL_OR_REVERSE 0x150B\n#define GL_COPY_INVERTED 0x150C\n#define GL_OR_INVERTED 0x150D\n#define GL_NAND 0x150E\n#define GL_SET 0x150F\n#define GL_TEXTURE 0x1702\n#define GL_COLOR 0x1800\n#define GL_DEPTH 0x1801\n#define GL_STENCIL 0x1802\n#define GL_STENCIL_INDEX 0x1901\n#define GL_DEPTH_COMPONENT 0x1902\n#define GL_RED 0x1903\n#define GL_GREEN 0x1904\n#define GL_BLUE 0x1905\n#define GL_ALPHA 0x1906\n#define GL_RGB 0x1907\n#define GL_RGBA 0x1908\n#define GL_POINT 0x1B00\n#define GL_LINE 0x1B01\n#define GL_FILL 0x1B02\n#define GL_KEEP 0x1E00\n#define GL_REPLACE 0x1E01\n#define GL_INCR 0x1E02\n#define GL_DECR 0x1E03\n#define GL_VENDOR 0x1F00\n#define GL_RENDERER 0x1F01\n#define GL_VERSION 0x1F02\n#define GL_EXTENSIONS 0x1F03\n#define GL_NEAREST 0x2600\n#define GL_LINEAR 0x2601\n#define GL_NEAREST_MIPMAP_NEAREST 0x2700\n#define GL_LINEAR_MIPMAP_NEAREST 0x2701\n#define GL_NEAREST_MIPMAP_LINEAR 0x2702\n#define GL_LINEAR_MIPMAP_LINEAR 0x2703\n#define GL_TEXTURE_MAG_FILTER 0x2800\n#define GL_TEXTURE_MIN_FILTER 0x2801\n#define GL_TEXTURE_WRAP_S 0x2802\n#define GL_TEXTURE_WRAP_T 0x2803\n#define GL_PROXY_TEXTURE_1D 0x8063\n#define GL_PROXY_TEXTURE_2D 0x8064\n#define GL_REPEAT 0x2901\n#define GL_R3_G3_B2 0x2A10\n#define GL_RGB4 0x804F\n#define GL_RGB5 0x8050\n#define GL_RGB8 0x8051\n#define GL_RGB10 0x8052\n#define GL_RGB12 0x8053\n#define GL_RGB16 0x8054\n#define GL_RGBA2 0x8055\n#define GL_RGBA4 0x8056\n#define GL_RGB5_A1 0x8057\n#define GL_RGBA8 0x8058\n#define GL_RGB10_A2 0x8059\n#define GL_RGBA12 0x805A\n#define GL_RGBA16 0x805B\n#define GL_UNSIGNED_BYTE_3_3_2 0x8032\n#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033\n#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034\n#define GL_UNSIGNED_INT_8_8_8_8 0x8035\n#define GL_UNSIGNED_INT_10_10_10_2 0x8036\n#define GL_TEXTURE_BINDING_3D 0x806A\n#define GL_PACK_SKIP_IMAGES 0x806B\n#define GL_PACK_IMAGE_HEIGHT 0x806C\n#define GL_UNPACK_SKIP_IMAGES 0x806D\n#define GL_UNPACK_IMAGE_HEIGHT 0x806E\n#define GL_TEXTURE_3D 0x806F\n#define GL_PROXY_TEXTURE_3D 0x8070\n#define GL_TEXTURE_DEPTH 0x8071\n#define GL_TEXTURE_WRAP_R 0x8072\n#define GL_MAX_3D_TEXTURE_SIZE 0x8073\n#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362\n#define GL_UNSIGNED_SHORT_5_6_5 0x8363\n#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364\n#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365\n#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366\n#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367\n#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368\n#define GL_BGR 0x80E0\n#define GL_BGRA 0x80E1\n#define GL_MAX_ELEMENTS_VERTICES 0x80E8\n#define GL_MAX_ELEMENTS_INDICES 0x80E9\n#define GL_CLAMP_TO_EDGE 0x812F\n#define GL_TEXTURE_MIN_LOD 0x813A\n#define GL_TEXTURE_MAX_LOD 0x813B\n#define GL_TEXTURE_BASE_LEVEL 0x813C\n#define GL_TEXTURE_MAX_LEVEL 0x813D\n#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12\n#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13\n#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22\n#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23\n#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E\n#define GL_TEXTURE0 0x84C0\n#define GL_TEXTURE1 0x84C1\n#define GL_TEXTURE2 0x84C2\n#define GL_TEXTURE3 0x84C3\n#define GL_TEXTURE4 0x84C4\n#define GL_TEXTURE5 0x84C5\n#define GL_TEXTURE6 0x84C6\n#define GL_TEXTURE7 0x84C7\n#define GL_TEXTURE8 0x84C8\n#define GL_TEXTURE9 0x84C9\n#define GL_TEXTURE10 0x84CA\n#define GL_TEXTURE11 0x84CB\n#define GL_TEXTURE12 0x84CC\n#define GL_TEXTURE13 0x84CD\n#define GL_TEXTURE14 0x84CE\n#define GL_TEXTURE15 0x84CF\n#define GL_TEXTURE16 0x84D0\n#define GL_TEXTURE17 0x84D1\n#define GL_TEXTURE18 0x84D2\n#define GL_TEXTURE19 0x84D3\n#define GL_TEXTURE20 0x84D4\n#define GL_TEXTURE21 0x84D5\n#define GL_TEXTURE22 0x84D6\n#define GL_TEXTURE23 0x84D7\n#define GL_TEXTURE24 0x84D8\n#define GL_TEXTURE25 0x84D9\n#define GL_TEXTURE26 0x84DA\n#define GL_TEXTURE27 0x84DB\n#define GL_TEXTURE28 0x84DC\n#define GL_TEXTURE29 0x84DD\n#define GL_TEXTURE30 0x84DE\n#define GL_TEXTURE31 0x84DF\n#define GL_ACTIVE_TEXTURE 0x84E0\n#define GL_MULTISAMPLE 0x809D\n#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E\n#define GL_SAMPLE_ALPHA_TO_ONE 0x809F\n#define GL_SAMPLE_COVERAGE 0x80A0\n#define GL_SAMPLE_BUFFERS 0x80A8\n#define GL_SAMPLES 0x80A9\n#define GL_SAMPLE_COVERAGE_VALUE 0x80AA\n#define GL_SAMPLE_COVERAGE_INVERT 0x80AB\n#define GL_TEXTURE_CUBE_MAP 0x8513\n#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A\n#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B\n#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C\n#define GL_COMPRESSED_RGB 0x84ED\n#define GL_COMPRESSED_RGBA 0x84EE\n#define GL_TEXTURE_COMPRESSION_HINT 0x84EF\n#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0\n#define GL_TEXTURE_COMPRESSED 0x86A1\n#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2\n#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3\n#define GL_CLAMP_TO_BORDER 0x812D\n#define GL_BLEND_DST_RGB 0x80C8\n#define GL_BLEND_SRC_RGB 0x80C9\n#define GL_BLEND_DST_ALPHA 0x80CA\n#define GL_BLEND_SRC_ALPHA 0x80CB\n#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128\n#define GL_DEPTH_COMPONENT16 0x81A5\n#define GL_DEPTH_COMPONENT24 0x81A6\n#define GL_DEPTH_COMPONENT32 0x81A7\n#define GL_MIRRORED_REPEAT 0x8370\n#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD\n#define GL_TEXTURE_LOD_BIAS 0x8501\n#define GL_INCR_WRAP 0x8507\n#define GL_DECR_WRAP 0x8508\n#define GL_TEXTURE_DEPTH_SIZE 0x884A\n#define GL_TEXTURE_COMPARE_MODE 0x884C\n#define GL_TEXTURE_COMPARE_FUNC 0x884D\n#define GL_FUNC_ADD 0x8006\n#define GL_FUNC_SUBTRACT 0x800A\n#define GL_FUNC_REVERSE_SUBTRACT 0x800B\n#define GL_MIN 0x8007\n#define GL_MAX 0x8008\n#define GL_CONSTANT_COLOR 0x8001\n#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002\n#define GL_CONSTANT_ALPHA 0x8003\n#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004\n#define GL_BUFFER_SIZE 0x8764\n#define GL_BUFFER_USAGE 0x8765\n#define GL_QUERY_COUNTER_BITS 0x8864\n#define GL_CURRENT_QUERY 0x8865\n#define GL_QUERY_RESULT 0x8866\n#define GL_QUERY_RESULT_AVAILABLE 0x8867\n#define GL_ARRAY_BUFFER 0x8892\n#define GL_ELEMENT_ARRAY_BUFFER 0x8893\n#define GL_ARRAY_BUFFER_BINDING 0x8894\n#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895\n#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F\n#define GL_READ_ONLY 0x88B8\n#define GL_WRITE_ONLY 0x88B9\n#define GL_READ_WRITE 0x88BA\n#define GL_BUFFER_ACCESS 0x88BB\n#define GL_BUFFER_MAPPED 0x88BC\n#define GL_BUFFER_MAP_POINTER 0x88BD\n#define GL_STREAM_DRAW 0x88E0\n#define GL_STREAM_READ 0x88E1\n#define GL_STREAM_COPY 0x88E2\n#define GL_STATIC_DRAW 0x88E4\n#define GL_STATIC_READ 0x88E5\n#define GL_STATIC_COPY 0x88E6\n#define GL_DYNAMIC_DRAW 0x88E8\n#define GL_DYNAMIC_READ 0x88E9\n#define GL_DYNAMIC_COPY 0x88EA\n#define GL_SAMPLES_PASSED 0x8914\n#define GL_SRC1_ALPHA 0x8589\n#define GL_BLEND_EQUATION_RGB 0x8009\n#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622\n#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623\n#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624\n#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625\n#define GL_CURRENT_VERTEX_ATTRIB 0x8626\n#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642\n#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645\n#define GL_STENCIL_BACK_FUNC 0x8800\n#define GL_STENCIL_BACK_FAIL 0x8801\n#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802\n#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803\n#define GL_MAX_DRAW_BUFFERS 0x8824\n#define GL_DRAW_BUFFER0 0x8825\n#define GL_DRAW_BUFFER1 0x8826\n#define GL_DRAW_BUFFER2 0x8827\n#define GL_DRAW_BUFFER3 0x8828\n#define GL_DRAW_BUFFER4 0x8829\n#define GL_DRAW_BUFFER5 0x882A\n#define GL_DRAW_BUFFER6 0x882B\n#define GL_DRAW_BUFFER7 0x882C\n#define GL_DRAW_BUFFER8 0x882D\n#define GL_DRAW_BUFFER9 0x882E\n#define GL_DRAW_BUFFER10 0x882F\n#define GL_DRAW_BUFFER11 0x8830\n#define GL_DRAW_BUFFER12 0x8831\n#define GL_DRAW_BUFFER13 0x8832\n#define GL_DRAW_BUFFER14 0x8833\n#define GL_DRAW_BUFFER15 0x8834\n#define GL_BLEND_EQUATION_ALPHA 0x883D\n#define GL_MAX_VERTEX_ATTRIBS 0x8869\n#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A\n#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872\n#define GL_FRAGMENT_SHADER 0x8B30\n#define GL_VERTEX_SHADER 0x8B31\n#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49\n#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A\n#define GL_MAX_VARYING_FLOATS 0x8B4B\n#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C\n#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D\n#define GL_SHADER_TYPE 0x8B4F\n#define GL_FLOAT_VEC2 0x8B50\n#define GL_FLOAT_VEC3 0x8B51\n#define GL_FLOAT_VEC4 0x8B52\n#define GL_INT_VEC2 0x8B53\n#define GL_INT_VEC3 0x8B54\n#define GL_INT_VEC4 0x8B55\n#define GL_BOOL 0x8B56\n#define GL_BOOL_VEC2 0x8B57\n#define GL_BOOL_VEC3 0x8B58\n#define GL_BOOL_VEC4 0x8B59\n#define GL_FLOAT_MAT2 0x8B5A\n#define GL_FLOAT_MAT3 0x8B5B\n#define GL_FLOAT_MAT4 0x8B5C\n#define GL_SAMPLER_1D 0x8B5D\n#define GL_SAMPLER_2D 0x8B5E\n#define GL_SAMPLER_3D 0x8B5F\n#define GL_SAMPLER_CUBE 0x8B60\n#define GL_SAMPLER_1D_SHADOW 0x8B61\n#define GL_SAMPLER_2D_SHADOW 0x8B62\n#define GL_DELETE_STATUS 0x8B80\n#define GL_COMPILE_STATUS 0x8B81\n#define GL_LINK_STATUS 0x8B82\n#define GL_VALIDATE_STATUS 0x8B83\n#define GL_INFO_LOG_LENGTH 0x8B84\n#define GL_ATTACHED_SHADERS 0x8B85\n#define GL_ACTIVE_UNIFORMS 0x8B86\n#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87\n#define GL_SHADER_SOURCE_LENGTH 0x8B88\n#define GL_ACTIVE_ATTRIBUTES 0x8B89\n#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A\n#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B\n#define GL_SHADING_LANGUAGE_VERSION 0x8B8C\n#define GL_CURRENT_PROGRAM 0x8B8D\n#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0\n#define GL_LOWER_LEFT 0x8CA1\n#define GL_UPPER_LEFT 0x8CA2\n#define GL_STENCIL_BACK_REF 0x8CA3\n#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4\n#define GL_STENCIL_BACK_WRITEMASK 0x8CA5\n#define GL_PIXEL_PACK_BUFFER 0x88EB\n#define GL_PIXEL_UNPACK_BUFFER 0x88EC\n#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED\n#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF\n#define GL_FLOAT_MAT2x3 0x8B65\n#define GL_FLOAT_MAT2x4 0x8B66\n#define GL_FLOAT_MAT3x2 0x8B67\n#define GL_FLOAT_MAT3x4 0x8B68\n#define GL_FLOAT_MAT4x2 0x8B69\n#define GL_FLOAT_MAT4x3 0x8B6A\n#define GL_SRGB 0x8C40\n#define GL_SRGB8 0x8C41\n#define GL_SRGB_ALPHA 0x8C42\n#define GL_SRGB8_ALPHA8 0x8C43\n#define GL_COMPRESSED_SRGB 0x8C48\n#define GL_COMPRESSED_SRGB_ALPHA 0x8C49\n#define GL_COMPARE_REF_TO_TEXTURE 0x884E\n#define GL_CLIP_DISTANCE0 0x3000\n#define GL_CLIP_DISTANCE1 0x3001\n#define GL_CLIP_DISTANCE2 0x3002\n#define GL_CLIP_DISTANCE3 0x3003\n#define GL_CLIP_DISTANCE4 0x3004\n#define GL_CLIP_DISTANCE5 0x3005\n#define GL_CLIP_DISTANCE6 0x3006\n#define GL_CLIP_DISTANCE7 0x3007\n#define GL_MAX_CLIP_DISTANCES 0x0D32\n#define GL_MAJOR_VERSION 0x821B\n#define GL_MINOR_VERSION 0x821C\n#define GL_NUM_EXTENSIONS 0x821D\n#define GL_CONTEXT_FLAGS 0x821E\n#define GL_COMPRESSED_RED 0x8225\n#define GL_COMPRESSED_RG 0x8226\n#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001\n#define GL_RGBA32F 0x8814\n#define GL_RGB32F 0x8815\n#define GL_RGBA16F 0x881A\n#define GL_RGB16F 0x881B\n#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD\n#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF\n#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904\n#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905\n#define GL_CLAMP_READ_COLOR 0x891C\n#define GL_FIXED_ONLY 0x891D\n#define GL_MAX_VARYING_COMPONENTS 0x8B4B\n#define GL_TEXTURE_1D_ARRAY 0x8C18\n#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19\n#define GL_TEXTURE_2D_ARRAY 0x8C1A\n#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B\n#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C\n#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D\n#define GL_R11F_G11F_B10F 0x8C3A\n#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B\n#define GL_RGB9_E5 0x8C3D\n#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E\n#define GL_TEXTURE_SHARED_SIZE 0x8C3F\n#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76\n#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80\n#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83\n#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84\n#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85\n#define GL_PRIMITIVES_GENERATED 0x8C87\n#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88\n#define GL_RASTERIZER_DISCARD 0x8C89\n#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B\n#define GL_INTERLEAVED_ATTRIBS 0x8C8C\n#define GL_SEPARATE_ATTRIBS 0x8C8D\n#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E\n#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F\n#define GL_RGBA32UI 0x8D70\n#define GL_RGB32UI 0x8D71\n#define GL_RGBA16UI 0x8D76\n#define GL_RGB16UI 0x8D77\n#define GL_RGBA8UI 0x8D7C\n#define GL_RGB8UI 0x8D7D\n#define GL_RGBA32I 0x8D82\n#define GL_RGB32I 0x8D83\n#define GL_RGBA16I 0x8D88\n#define GL_RGB16I 0x8D89\n#define GL_RGBA8I 0x8D8E\n#define GL_RGB8I 0x8D8F\n#define GL_RED_INTEGER 0x8D94\n#define GL_GREEN_INTEGER 0x8D95\n#define GL_BLUE_INTEGER 0x8D96\n#define GL_RGB_INTEGER 0x8D98\n#define GL_RGBA_INTEGER 0x8D99\n#define GL_BGR_INTEGER 0x8D9A\n#define GL_BGRA_INTEGER 0x8D9B\n#define GL_SAMPLER_1D_ARRAY 0x8DC0\n#define GL_SAMPLER_2D_ARRAY 0x8DC1\n#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3\n#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4\n#define GL_SAMPLER_CUBE_SHADOW 0x8DC5\n#define GL_UNSIGNED_INT_VEC2 0x8DC6\n#define GL_UNSIGNED_INT_VEC3 0x8DC7\n#define GL_UNSIGNED_INT_VEC4 0x8DC8\n#define GL_INT_SAMPLER_1D 0x8DC9\n#define GL_INT_SAMPLER_2D 0x8DCA\n#define GL_INT_SAMPLER_3D 0x8DCB\n#define GL_INT_SAMPLER_CUBE 0x8DCC\n#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE\n#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF\n#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1\n#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2\n#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3\n#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4\n#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6\n#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7\n#define GL_QUERY_WAIT 0x8E13\n#define GL_QUERY_NO_WAIT 0x8E14\n#define GL_QUERY_BY_REGION_WAIT 0x8E15\n#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16\n#define GL_BUFFER_ACCESS_FLAGS 0x911F\n#define GL_BUFFER_MAP_LENGTH 0x9120\n#define GL_BUFFER_MAP_OFFSET 0x9121\n#define GL_DEPTH_COMPONENT32F 0x8CAC\n#define GL_DEPTH32F_STENCIL8 0x8CAD\n#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD\n#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506\n#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210\n#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211\n#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212\n#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213\n#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214\n#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215\n#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216\n#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217\n#define GL_FRAMEBUFFER_DEFAULT 0x8218\n#define GL_FRAMEBUFFER_UNDEFINED 0x8219\n#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A\n#define GL_MAX_RENDERBUFFER_SIZE 0x84E8\n#define GL_DEPTH_STENCIL 0x84F9\n#define GL_UNSIGNED_INT_24_8 0x84FA\n#define GL_DEPTH24_STENCIL8 0x88F0\n#define GL_TEXTURE_STENCIL_SIZE 0x88F1\n#define GL_TEXTURE_RED_TYPE 0x8C10\n#define GL_TEXTURE_GREEN_TYPE 0x8C11\n#define GL_TEXTURE_BLUE_TYPE 0x8C12\n#define GL_TEXTURE_ALPHA_TYPE 0x8C13\n#define GL_TEXTURE_DEPTH_TYPE 0x8C16\n#define GL_UNSIGNED_NORMALIZED 0x8C17\n#define GL_FRAMEBUFFER_BINDING 0x8CA6\n#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6\n#define GL_RENDERBUFFER_BINDING 0x8CA7\n#define GL_READ_FRAMEBUFFER 0x8CA8\n#define GL_DRAW_FRAMEBUFFER 0x8CA9\n#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA\n#define GL_RENDERBUFFER_SAMPLES 0x8CAB\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4\n#define GL_FRAMEBUFFER_COMPLETE 0x8CD5\n#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6\n#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7\n#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB\n#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC\n#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD\n#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF\n#define GL_COLOR_ATTACHMENT0 0x8CE0\n#define GL_COLOR_ATTACHMENT1 0x8CE1\n#define GL_COLOR_ATTACHMENT2 0x8CE2\n#define GL_COLOR_ATTACHMENT3 0x8CE3\n#define GL_COLOR_ATTACHMENT4 0x8CE4\n#define GL_COLOR_ATTACHMENT5 0x8CE5\n#define GL_COLOR_ATTACHMENT6 0x8CE6\n#define GL_COLOR_ATTACHMENT7 0x8CE7\n#define GL_COLOR_ATTACHMENT8 0x8CE8\n#define GL_COLOR_ATTACHMENT9 0x8CE9\n#define GL_COLOR_ATTACHMENT10 0x8CEA\n#define GL_COLOR_ATTACHMENT11 0x8CEB\n#define GL_COLOR_ATTACHMENT12 0x8CEC\n#define GL_COLOR_ATTACHMENT13 0x8CED\n#define GL_COLOR_ATTACHMENT14 0x8CEE\n#define GL_COLOR_ATTACHMENT15 0x8CEF\n#define GL_DEPTH_ATTACHMENT 0x8D00\n#define GL_STENCIL_ATTACHMENT 0x8D20\n#define GL_FRAMEBUFFER 0x8D40\n#define GL_RENDERBUFFER 0x8D41\n#define GL_RENDERBUFFER_WIDTH 0x8D42\n#define GL_RENDERBUFFER_HEIGHT 0x8D43\n#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44\n#define GL_STENCIL_INDEX1 0x8D46\n#define GL_STENCIL_INDEX4 0x8D47\n#define GL_STENCIL_INDEX8 0x8D48\n#define GL_STENCIL_INDEX16 0x8D49\n#define GL_RENDERBUFFER_RED_SIZE 0x8D50\n#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51\n#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52\n#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53\n#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54\n#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55\n#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56\n#define GL_MAX_SAMPLES 0x8D57\n#define GL_INDEX 0x8222\n#define GL_FRAMEBUFFER_SRGB 0x8DB9\n#define GL_HALF_FLOAT 0x140B\n#define GL_MAP_READ_BIT 0x0001\n#define GL_MAP_WRITE_BIT 0x0002\n#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004\n#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008\n#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010\n#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020\n#define GL_COMPRESSED_RED_RGTC1 0x8DBB\n#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC\n#define GL_COMPRESSED_RG_RGTC2 0x8DBD\n#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE\n#define GL_RG 0x8227\n#define GL_RG_INTEGER 0x8228\n#define GL_R8 0x8229\n#define GL_R16 0x822A\n#define GL_RG8 0x822B\n#define GL_RG16 0x822C\n#define GL_R16F 0x822D\n#define GL_R32F 0x822E\n#define GL_RG16F 0x822F\n#define GL_RG32F 0x8230\n#define GL_R8I 0x8231\n#define GL_R8UI 0x8232\n#define GL_R16I 0x8233\n#define GL_R16UI 0x8234\n#define GL_R32I 0x8235\n#define GL_R32UI 0x8236\n#define GL_RG8I 0x8237\n#define GL_RG8UI 0x8238\n#define GL_RG16I 0x8239\n#define GL_RG16UI 0x823A\n#define GL_RG32I 0x823B\n#define GL_RG32UI 0x823C\n#define GL_VERTEX_ARRAY_BINDING 0x85B5\n#define GL_SAMPLER_2D_RECT 0x8B63\n#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64\n#define GL_SAMPLER_BUFFER 0x8DC2\n#define GL_INT_SAMPLER_2D_RECT 0x8DCD\n#define GL_INT_SAMPLER_BUFFER 0x8DD0\n#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5\n#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8\n#define GL_TEXTURE_BUFFER 0x8C2A\n#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B\n#define GL_TEXTURE_BINDING_BUFFER 0x8C2C\n#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D\n#define GL_TEXTURE_RECTANGLE 0x84F5\n#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6\n#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7\n#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8\n#define GL_R8_SNORM 0x8F94\n#define GL_RG8_SNORM 0x8F95\n#define GL_RGB8_SNORM 0x8F96\n#define GL_RGBA8_SNORM 0x8F97\n#define GL_R16_SNORM 0x8F98\n#define GL_RG16_SNORM 0x8F99\n#define GL_RGB16_SNORM 0x8F9A\n#define GL_RGBA16_SNORM 0x8F9B\n#define GL_SIGNED_NORMALIZED 0x8F9C\n#define GL_PRIMITIVE_RESTART 0x8F9D\n#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E\n#define GL_COPY_READ_BUFFER 0x8F36\n#define GL_COPY_WRITE_BUFFER 0x8F37\n#define GL_UNIFORM_BUFFER 0x8A11\n#define GL_UNIFORM_BUFFER_BINDING 0x8A28\n#define GL_UNIFORM_BUFFER_START 0x8A29\n#define GL_UNIFORM_BUFFER_SIZE 0x8A2A\n#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B\n#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D\n#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E\n#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F\n#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30\n#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31\n#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33\n#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34\n#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35\n#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36\n#define GL_UNIFORM_TYPE 0x8A37\n#define GL_UNIFORM_SIZE 0x8A38\n#define GL_UNIFORM_NAME_LENGTH 0x8A39\n#define GL_UNIFORM_BLOCK_INDEX 0x8A3A\n#define GL_UNIFORM_OFFSET 0x8A3B\n#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C\n#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D\n#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E\n#define GL_UNIFORM_BLOCK_BINDING 0x8A3F\n#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40\n#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41\n#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42\n#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46\n#define GL_INVALID_INDEX 0xFFFFFFFF\n#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001\n#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002\n#define GL_LINES_ADJACENCY 0x000A\n#define GL_LINE_STRIP_ADJACENCY 0x000B\n#define GL_TRIANGLES_ADJACENCY 0x000C\n#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D\n#define GL_PROGRAM_POINT_SIZE 0x8642\n#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29\n#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7\n#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8\n#define GL_GEOMETRY_SHADER 0x8DD9\n#define GL_GEOMETRY_VERTICES_OUT 0x8916\n#define GL_GEOMETRY_INPUT_TYPE 0x8917\n#define GL_GEOMETRY_OUTPUT_TYPE 0x8918\n#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF\n#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0\n#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1\n#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122\n#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123\n#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124\n#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125\n#define GL_CONTEXT_PROFILE_MASK 0x9126\n#define GL_DEPTH_CLAMP 0x864F\n#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C\n#define GL_FIRST_VERTEX_CONVENTION 0x8E4D\n#define GL_LAST_VERTEX_CONVENTION 0x8E4E\n#define GL_PROVOKING_VERTEX 0x8E4F\n#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F\n#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111\n#define GL_OBJECT_TYPE 0x9112\n#define GL_SYNC_CONDITION 0x9113\n#define GL_SYNC_STATUS 0x9114\n#define GL_SYNC_FLAGS 0x9115\n#define GL_SYNC_FENCE 0x9116\n#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117\n#define GL_UNSIGNALED 0x9118\n#define GL_SIGNALED 0x9119\n#define GL_ALREADY_SIGNALED 0x911A\n#define GL_TIMEOUT_EXPIRED 0x911B\n#define GL_CONDITION_SATISFIED 0x911C\n#define GL_WAIT_FAILED 0x911D\n#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF\n#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001\n#define GL_SAMPLE_POSITION 0x8E50\n#define GL_SAMPLE_MASK 0x8E51\n#define GL_SAMPLE_MASK_VALUE 0x8E52\n#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59\n#define GL_TEXTURE_2D_MULTISAMPLE 0x9100\n#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101\n#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102\n#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103\n#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104\n#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105\n#define GL_TEXTURE_SAMPLES 0x9106\n#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107\n#define GL_SAMPLER_2D_MULTISAMPLE 0x9108\n#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109\n#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A\n#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B\n#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C\n#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D\n#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E\n#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F\n#define GL_MAX_INTEGER_SAMPLES 0x9110\n#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE\n#define GL_SRC1_COLOR 0x88F9\n#define GL_ONE_MINUS_SRC1_COLOR 0x88FA\n#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB\n#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC\n#define GL_ANY_SAMPLES_PASSED 0x8C2F\n#define GL_SAMPLER_BINDING 0x8919\n#define GL_RGB10_A2UI 0x906F\n#define GL_TEXTURE_SWIZZLE_R 0x8E42\n#define GL_TEXTURE_SWIZZLE_G 0x8E43\n#define GL_TEXTURE_SWIZZLE_B 0x8E44\n#define GL_TEXTURE_SWIZZLE_A 0x8E45\n#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46\n#define GL_TIME_ELAPSED 0x88BF\n#define GL_TIMESTAMP 0x8E28\n#define GL_INT_2_10_10_10_REV 0x8D9F\n#define GL_SAMPLE_SHADING 0x8C36\n#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37\n#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E\n#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F\n#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009\n#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A\n#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B\n#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C\n#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D\n#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E\n#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F\n#define GL_DRAW_INDIRECT_BUFFER 0x8F3F\n#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43\n#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F\n#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A\n#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B\n#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C\n#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D\n#define GL_MAX_VERTEX_STREAMS 0x8E71\n#define GL_DOUBLE_VEC2 0x8FFC\n#define GL_DOUBLE_VEC3 0x8FFD\n#define GL_DOUBLE_VEC4 0x8FFE\n#define GL_DOUBLE_MAT2 0x8F46\n#define GL_DOUBLE_MAT3 0x8F47\n#define GL_DOUBLE_MAT4 0x8F48\n#define GL_DOUBLE_MAT2x3 0x8F49\n#define GL_DOUBLE_MAT2x4 0x8F4A\n#define GL_DOUBLE_MAT3x2 0x8F4B\n#define GL_DOUBLE_MAT3x4 0x8F4C\n#define GL_DOUBLE_MAT4x2 0x8F4D\n#define GL_DOUBLE_MAT4x3 0x8F4E\n#define GL_ACTIVE_SUBROUTINES 0x8DE5\n#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6\n#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47\n#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48\n#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49\n#define GL_MAX_SUBROUTINES 0x8DE7\n#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8\n#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A\n#define GL_COMPATIBLE_SUBROUTINES 0x8E4B\n#define GL_PATCHES 0x000E\n#define GL_PATCH_VERTICES 0x8E72\n#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73\n#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74\n#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75\n#define GL_TESS_GEN_MODE 0x8E76\n#define GL_TESS_GEN_SPACING 0x8E77\n#define GL_TESS_GEN_VERTEX_ORDER 0x8E78\n#define GL_TESS_GEN_POINT_MODE 0x8E79\n#define GL_ISOLINES 0x8E7A\n#define GL_FRACTIONAL_ODD 0x8E7B\n#define GL_FRACTIONAL_EVEN 0x8E7C\n#define GL_MAX_PATCH_VERTICES 0x8E7D\n#define GL_MAX_TESS_GEN_LEVEL 0x8E7E\n#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F\n#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80\n#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81\n#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82\n#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83\n#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84\n#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85\n#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86\n#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89\n#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A\n#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C\n#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D\n#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E\n#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1\n#define GL_TESS_EVALUATION_SHADER 0x8E87\n#define GL_TESS_CONTROL_SHADER 0x8E88\n#define GL_TRANSFORM_FEEDBACK 0x8E22\n#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23\n#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24\n#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25\n#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70\n#define GL_FIXED 0x140C\n#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A\n#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B\n#define GL_LOW_FLOAT 0x8DF0\n#define GL_MEDIUM_FLOAT 0x8DF1\n#define GL_HIGH_FLOAT 0x8DF2\n#define GL_LOW_INT 0x8DF3\n#define GL_MEDIUM_INT 0x8DF4\n#define GL_HIGH_INT 0x8DF5\n#define GL_SHADER_COMPILER 0x8DFA\n#define GL_SHADER_BINARY_FORMATS 0x8DF8\n#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9\n#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB\n#define GL_MAX_VARYING_VECTORS 0x8DFC\n#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD\n#define GL_RGB565 0x8D62\n#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257\n#define GL_PROGRAM_BINARY_LENGTH 0x8741\n#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE\n#define GL_PROGRAM_BINARY_FORMATS 0x87FF\n#define GL_VERTEX_SHADER_BIT 0x00000001\n#define GL_FRAGMENT_SHADER_BIT 0x00000002\n#define GL_GEOMETRY_SHADER_BIT 0x00000004\n#define GL_TESS_CONTROL_SHADER_BIT 0x00000008\n#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010\n#define GL_ALL_SHADER_BITS 0xFFFFFFFF\n#define GL_PROGRAM_SEPARABLE 0x8258\n#define GL_ACTIVE_PROGRAM 0x8259\n#define GL_PROGRAM_PIPELINE_BINDING 0x825A\n#define GL_MAX_VIEWPORTS 0x825B\n#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C\n#define GL_VIEWPORT_BOUNDS_RANGE 0x825D\n#define GL_LAYER_PROVOKING_VERTEX 0x825E\n#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F\n#define GL_UNDEFINED_VERTEX 0x8260\n#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127\n#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128\n#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129\n#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A\n#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B\n#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C\n#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D\n#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E\n#define GL_NUM_SAMPLE_COUNTS 0x9380\n#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC\n#define GL_ATOMIC_COUNTER_BUFFER 0x92C0\n#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1\n#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2\n#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3\n#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4\n#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5\n#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB\n#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC\n#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD\n#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE\n#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF\n#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0\n#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1\n#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2\n#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3\n#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4\n#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5\n#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6\n#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7\n#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8\n#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC\n#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9\n#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA\n#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB\n#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001\n#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002\n#define GL_UNIFORM_BARRIER_BIT 0x00000004\n#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008\n#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020\n#define GL_COMMAND_BARRIER_BIT 0x00000040\n#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080\n#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100\n#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200\n#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400\n#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800\n#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000\n#define GL_ALL_BARRIER_BITS 0xFFFFFFFF\n#define GL_MAX_IMAGE_UNITS 0x8F38\n#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39\n#define GL_IMAGE_BINDING_NAME 0x8F3A\n#define GL_IMAGE_BINDING_LEVEL 0x8F3B\n#define GL_IMAGE_BINDING_LAYERED 0x8F3C\n#define GL_IMAGE_BINDING_LAYER 0x8F3D\n#define GL_IMAGE_BINDING_ACCESS 0x8F3E\n#define GL_IMAGE_1D 0x904C\n#define GL_IMAGE_2D 0x904D\n#define GL_IMAGE_3D 0x904E\n#define GL_IMAGE_2D_RECT 0x904F\n#define GL_IMAGE_CUBE 0x9050\n#define GL_IMAGE_BUFFER 0x9051\n#define GL_IMAGE_1D_ARRAY 0x9052\n#define GL_IMAGE_2D_ARRAY 0x9053\n#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054\n#define GL_IMAGE_2D_MULTISAMPLE 0x9055\n#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056\n#define GL_INT_IMAGE_1D 0x9057\n#define GL_INT_IMAGE_2D 0x9058\n#define GL_INT_IMAGE_3D 0x9059\n#define GL_INT_IMAGE_2D_RECT 0x905A\n#define GL_INT_IMAGE_CUBE 0x905B\n#define GL_INT_IMAGE_BUFFER 0x905C\n#define GL_INT_IMAGE_1D_ARRAY 0x905D\n#define GL_INT_IMAGE_2D_ARRAY 0x905E\n#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F\n#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060\n#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061\n#define GL_UNSIGNED_INT_IMAGE_1D 0x9062\n#define GL_UNSIGNED_INT_IMAGE_2D 0x9063\n#define GL_UNSIGNED_INT_IMAGE_3D 0x9064\n#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065\n#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066\n#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067\n#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068\n#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069\n#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A\n#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B\n#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C\n#define GL_MAX_IMAGE_SAMPLES 0x906D\n#define GL_IMAGE_BINDING_FORMAT 0x906E\n#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7\n#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8\n#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9\n#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA\n#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB\n#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC\n#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD\n#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE\n#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF\n#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C\n#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D\n#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E\n#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F\n#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F\n#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9\n#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E\n#define GL_COMPRESSED_RGB8_ETC2 0x9274\n#define GL_COMPRESSED_SRGB8_ETC2 0x9275\n#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276\n#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277\n#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278\n#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279\n#define GL_COMPRESSED_R11_EAC 0x9270\n#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271\n#define GL_COMPRESSED_RG11_EAC 0x9272\n#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273\n#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69\n#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A\n#define GL_MAX_ELEMENT_INDEX 0x8D6B\n#define GL_COMPUTE_SHADER 0x91B9\n#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB\n#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC\n#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD\n#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262\n#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263\n#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264\n#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265\n#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266\n#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB\n#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE\n#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF\n#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC\n#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED\n#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE\n#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF\n#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242\n#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243\n#define GL_DEBUG_CALLBACK_FUNCTION 0x8244\n#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245\n#define GL_DEBUG_SOURCE_API 0x8246\n#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247\n#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248\n#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249\n#define GL_DEBUG_SOURCE_APPLICATION 0x824A\n#define GL_DEBUG_SOURCE_OTHER 0x824B\n#define GL_DEBUG_TYPE_ERROR 0x824C\n#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D\n#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E\n#define GL_DEBUG_TYPE_PORTABILITY 0x824F\n#define GL_DEBUG_TYPE_PERFORMANCE 0x8250\n#define GL_DEBUG_TYPE_OTHER 0x8251\n#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143\n#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144\n#define GL_DEBUG_LOGGED_MESSAGES 0x9145\n#define GL_DEBUG_SEVERITY_HIGH 0x9146\n#define GL_DEBUG_SEVERITY_MEDIUM 0x9147\n#define GL_DEBUG_SEVERITY_LOW 0x9148\n#define GL_DEBUG_TYPE_MARKER 0x8268\n#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269\n#define GL_DEBUG_TYPE_POP_GROUP 0x826A\n#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B\n#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C\n#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D\n#define GL_BUFFER 0x82E0\n#define GL_SHADER 0x82E1\n#define GL_PROGRAM 0x82E2\n#define GL_QUERY 0x82E3\n#define GL_PROGRAM_PIPELINE 0x82E4\n#define GL_SAMPLER 0x82E6\n#define GL_MAX_LABEL_LENGTH 0x82E8\n#define GL_DEBUG_OUTPUT 0x92E0\n#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002\n#define GL_MAX_UNIFORM_LOCATIONS 0x826E\n#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310\n#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311\n#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312\n#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313\n#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314\n#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315\n#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316\n#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317\n#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318\n#define GL_INTERNALFORMAT_SUPPORTED 0x826F\n#define GL_INTERNALFORMAT_PREFERRED 0x8270\n#define GL_INTERNALFORMAT_RED_SIZE 0x8271\n#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272\n#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273\n#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274\n#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275\n#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276\n#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277\n#define GL_INTERNALFORMAT_RED_TYPE 0x8278\n#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279\n#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A\n#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B\n#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C\n#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D\n#define GL_MAX_WIDTH 0x827E\n#define GL_MAX_HEIGHT 0x827F\n#define GL_MAX_DEPTH 0x8280\n#define GL_MAX_LAYERS 0x8281\n#define GL_MAX_COMBINED_DIMENSIONS 0x8282\n#define GL_COLOR_COMPONENTS 0x8283\n#define GL_DEPTH_COMPONENTS 0x8284\n#define GL_STENCIL_COMPONENTS 0x8285\n#define GL_COLOR_RENDERABLE 0x8286\n#define GL_DEPTH_RENDERABLE 0x8287\n#define GL_STENCIL_RENDERABLE 0x8288\n#define GL_FRAMEBUFFER_RENDERABLE 0x8289\n#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A\n#define GL_FRAMEBUFFER_BLEND 0x828B\n#define GL_READ_PIXELS 0x828C\n#define GL_READ_PIXELS_FORMAT 0x828D\n#define GL_READ_PIXELS_TYPE 0x828E\n#define GL_TEXTURE_IMAGE_FORMAT 0x828F\n#define GL_TEXTURE_IMAGE_TYPE 0x8290\n#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291\n#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292\n#define GL_MIPMAP 0x8293\n#define GL_MANUAL_GENERATE_MIPMAP 0x8294\n#define GL_AUTO_GENERATE_MIPMAP 0x8295\n#define GL_COLOR_ENCODING 0x8296\n#define GL_SRGB_READ 0x8297\n#define GL_SRGB_WRITE 0x8298\n#define GL_FILTER 0x829A\n#define GL_VERTEX_TEXTURE 0x829B\n#define GL_TESS_CONTROL_TEXTURE 0x829C\n#define GL_TESS_EVALUATION_TEXTURE 0x829D\n#define GL_GEOMETRY_TEXTURE 0x829E\n#define GL_FRAGMENT_TEXTURE 0x829F\n#define GL_COMPUTE_TEXTURE 0x82A0\n#define GL_TEXTURE_SHADOW 0x82A1\n#define GL_TEXTURE_GATHER 0x82A2\n#define GL_TEXTURE_GATHER_SHADOW 0x82A3\n#define GL_SHADER_IMAGE_LOAD 0x82A4\n#define GL_SHADER_IMAGE_STORE 0x82A5\n#define GL_SHADER_IMAGE_ATOMIC 0x82A6\n#define GL_IMAGE_TEXEL_SIZE 0x82A7\n#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8\n#define GL_IMAGE_PIXEL_FORMAT 0x82A9\n#define GL_IMAGE_PIXEL_TYPE 0x82AA\n#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC\n#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD\n#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE\n#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF\n#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1\n#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2\n#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3\n#define GL_CLEAR_BUFFER 0x82B4\n#define GL_TEXTURE_VIEW 0x82B5\n#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6\n#define GL_FULL_SUPPORT 0x82B7\n#define GL_CAVEAT_SUPPORT 0x82B8\n#define GL_IMAGE_CLASS_4_X_32 0x82B9\n#define GL_IMAGE_CLASS_2_X_32 0x82BA\n#define GL_IMAGE_CLASS_1_X_32 0x82BB\n#define GL_IMAGE_CLASS_4_X_16 0x82BC\n#define GL_IMAGE_CLASS_2_X_16 0x82BD\n#define GL_IMAGE_CLASS_1_X_16 0x82BE\n#define GL_IMAGE_CLASS_4_X_8 0x82BF\n#define GL_IMAGE_CLASS_2_X_8 0x82C0\n#define GL_IMAGE_CLASS_1_X_8 0x82C1\n#define GL_IMAGE_CLASS_11_11_10 0x82C2\n#define GL_IMAGE_CLASS_10_10_10_2 0x82C3\n#define GL_VIEW_CLASS_128_BITS 0x82C4\n#define GL_VIEW_CLASS_96_BITS 0x82C5\n#define GL_VIEW_CLASS_64_BITS 0x82C6\n#define GL_VIEW_CLASS_48_BITS 0x82C7\n#define GL_VIEW_CLASS_32_BITS 0x82C8\n#define GL_VIEW_CLASS_24_BITS 0x82C9\n#define GL_VIEW_CLASS_16_BITS 0x82CA\n#define GL_VIEW_CLASS_8_BITS 0x82CB\n#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC\n#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD\n#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE\n#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF\n#define GL_VIEW_CLASS_RGTC1_RED 0x82D0\n#define GL_VIEW_CLASS_RGTC2_RG 0x82D1\n#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2\n#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3\n#define GL_UNIFORM 0x92E1\n#define GL_UNIFORM_BLOCK 0x92E2\n#define GL_PROGRAM_INPUT 0x92E3\n#define GL_PROGRAM_OUTPUT 0x92E4\n#define GL_BUFFER_VARIABLE 0x92E5\n#define GL_SHADER_STORAGE_BLOCK 0x92E6\n#define GL_VERTEX_SUBROUTINE 0x92E8\n#define GL_TESS_CONTROL_SUBROUTINE 0x92E9\n#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA\n#define GL_GEOMETRY_SUBROUTINE 0x92EB\n#define GL_FRAGMENT_SUBROUTINE 0x92EC\n#define GL_COMPUTE_SUBROUTINE 0x92ED\n#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE\n#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF\n#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0\n#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1\n#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2\n#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3\n#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4\n#define GL_ACTIVE_RESOURCES 0x92F5\n#define GL_MAX_NAME_LENGTH 0x92F6\n#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7\n#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8\n#define GL_NAME_LENGTH 0x92F9\n#define GL_TYPE 0x92FA\n#define GL_ARRAY_SIZE 0x92FB\n#define GL_OFFSET 0x92FC\n#define GL_BLOCK_INDEX 0x92FD\n#define GL_ARRAY_STRIDE 0x92FE\n#define GL_MATRIX_STRIDE 0x92FF\n#define GL_IS_ROW_MAJOR 0x9300\n#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301\n#define GL_BUFFER_BINDING 0x9302\n#define GL_BUFFER_DATA_SIZE 0x9303\n#define GL_NUM_ACTIVE_VARIABLES 0x9304\n#define GL_ACTIVE_VARIABLES 0x9305\n#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306\n#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307\n#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308\n#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309\n#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A\n#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B\n#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C\n#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D\n#define GL_LOCATION 0x930E\n#define GL_LOCATION_INDEX 0x930F\n#define GL_IS_PER_PATCH 0x92E7\n#define GL_SHADER_STORAGE_BUFFER 0x90D2\n#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3\n#define GL_SHADER_STORAGE_BUFFER_START 0x90D4\n#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5\n#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6\n#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7\n#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8\n#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9\n#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA\n#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB\n#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC\n#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD\n#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE\n#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF\n#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000\n#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39\n#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA\n#define GL_TEXTURE_BUFFER_OFFSET 0x919D\n#define GL_TEXTURE_BUFFER_SIZE 0x919E\n#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F\n#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB\n#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC\n#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD\n#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE\n#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF\n#define GL_VERTEX_ATTRIB_BINDING 0x82D4\n#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5\n#define GL_VERTEX_BINDING_DIVISOR 0x82D6\n#define GL_VERTEX_BINDING_OFFSET 0x82D7\n#define GL_VERTEX_BINDING_STRIDE 0x82D8\n#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9\n#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA\n#define GL_VERTEX_BINDING_BUFFER 0x8F4F\n#define GL_DISPLAY_LIST 0x82E7\n#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5\n#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221\n#define GL_TEXTURE_BUFFER_BINDING 0x8C2A\n#define GL_MAP_PERSISTENT_BIT 0x0040\n#define GL_MAP_COHERENT_BIT 0x0080\n#define GL_DYNAMIC_STORAGE_BIT 0x0100\n#define GL_CLIENT_STORAGE_BIT 0x0200\n#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000\n#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F\n#define GL_BUFFER_STORAGE_FLAGS 0x8220\n#define GL_CLEAR_TEXTURE 0x9365\n#define GL_LOCATION_COMPONENT 0x934A\n#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B\n#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C\n#define GL_QUERY_BUFFER 0x9192\n#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000\n#define GL_QUERY_BUFFER_BINDING 0x9193\n#define GL_QUERY_RESULT_NO_WAIT 0x9194\n#define GL_MIRROR_CLAMP_TO_EDGE 0x8743\n#ifndef GL_VERSION_1_0\n#define GL_VERSION_1_0 1\nGLAPI int GLAD_GL_VERSION_1_0;\ntypedef void (APIENTRYP PFNGLCULLFACEPROC)(GLenum);\nGLAPI PFNGLCULLFACEPROC glad_glCullFace;\n#define glCullFace glad_glCullFace\ntypedef void (APIENTRYP PFNGLFRONTFACEPROC)(GLenum);\nGLAPI PFNGLFRONTFACEPROC glad_glFrontFace;\n#define glFrontFace glad_glFrontFace\ntypedef void (APIENTRYP PFNGLHINTPROC)(GLenum, GLenum);\nGLAPI PFNGLHINTPROC glad_glHint;\n#define glHint glad_glHint\ntypedef void (APIENTRYP PFNGLLINEWIDTHPROC)(GLfloat);\nGLAPI PFNGLLINEWIDTHPROC glad_glLineWidth;\n#define glLineWidth glad_glLineWidth\ntypedef void (APIENTRYP PFNGLPOINTSIZEPROC)(GLfloat);\nGLAPI PFNGLPOINTSIZEPROC glad_glPointSize;\n#define glPointSize glad_glPointSize\ntypedef void (APIENTRYP PFNGLPOLYGONMODEPROC)(GLenum, GLenum);\nGLAPI PFNGLPOLYGONMODEPROC glad_glPolygonMode;\n#define glPolygonMode glad_glPolygonMode\ntypedef void (APIENTRYP PFNGLSCISSORPROC)(GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLSCISSORPROC glad_glScissor;\n#define glScissor glad_glScissor\ntypedef void (APIENTRYP PFNGLTEXPARAMETERFPROC)(GLenum, GLenum, GLfloat);\nGLAPI PFNGLTEXPARAMETERFPROC glad_glTexParameterf;\n#define glTexParameterf glad_glTexParameterf\ntypedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv;\n#define glTexParameterfv glad_glTexParameterfv\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIPROC)(GLenum, GLenum, GLint);\nGLAPI PFNGLTEXPARAMETERIPROC glad_glTexParameteri;\n#define glTexParameteri glad_glTexParameteri\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv;\n#define glTexParameteriv glad_glTexParameteriv\ntypedef void (APIENTRYP PFNGLTEXIMAGE1DPROC)(GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXIMAGE1DPROC glad_glTexImage1D;\n#define glTexImage1D glad_glTexImage1D\ntypedef void (APIENTRYP PFNGLTEXIMAGE2DPROC)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXIMAGE2DPROC glad_glTexImage2D;\n#define glTexImage2D glad_glTexImage2D\ntypedef void (APIENTRYP PFNGLDRAWBUFFERPROC)(GLenum);\nGLAPI PFNGLDRAWBUFFERPROC glad_glDrawBuffer;\n#define glDrawBuffer glad_glDrawBuffer\ntypedef void (APIENTRYP PFNGLCLEARPROC)(GLbitfield);\nGLAPI PFNGLCLEARPROC glad_glClear;\n#define glClear glad_glClear\ntypedef void (APIENTRYP PFNGLCLEARCOLORPROC)(GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLCLEARCOLORPROC glad_glClearColor;\n#define glClearColor glad_glClearColor\ntypedef void (APIENTRYP PFNGLCLEARSTENCILPROC)(GLint);\nGLAPI PFNGLCLEARSTENCILPROC glad_glClearStencil;\n#define glClearStencil glad_glClearStencil\ntypedef void (APIENTRYP PFNGLCLEARDEPTHPROC)(GLdouble);\nGLAPI PFNGLCLEARDEPTHPROC glad_glClearDepth;\n#define glClearDepth glad_glClearDepth\ntypedef void (APIENTRYP PFNGLSTENCILMASKPROC)(GLuint);\nGLAPI PFNGLSTENCILMASKPROC glad_glStencilMask;\n#define glStencilMask glad_glStencilMask\ntypedef void (APIENTRYP PFNGLCOLORMASKPROC)(GLboolean, GLboolean, GLboolean, GLboolean);\nGLAPI PFNGLCOLORMASKPROC glad_glColorMask;\n#define glColorMask glad_glColorMask\ntypedef void (APIENTRYP PFNGLDEPTHMASKPROC)(GLboolean);\nGLAPI PFNGLDEPTHMASKPROC glad_glDepthMask;\n#define glDepthMask glad_glDepthMask\ntypedef void (APIENTRYP PFNGLDISABLEPROC)(GLenum);\nGLAPI PFNGLDISABLEPROC glad_glDisable;\n#define glDisable glad_glDisable\ntypedef void (APIENTRYP PFNGLENABLEPROC)(GLenum);\nGLAPI PFNGLENABLEPROC glad_glEnable;\n#define glEnable glad_glEnable\ntypedef void (APIENTRYP PFNGLFINISHPROC)();\nGLAPI PFNGLFINISHPROC glad_glFinish;\n#define glFinish glad_glFinish\ntypedef void (APIENTRYP PFNGLFLUSHPROC)();\nGLAPI PFNGLFLUSHPROC glad_glFlush;\n#define glFlush glad_glFlush\ntypedef void (APIENTRYP PFNGLBLENDFUNCPROC)(GLenum, GLenum);\nGLAPI PFNGLBLENDFUNCPROC glad_glBlendFunc;\n#define glBlendFunc glad_glBlendFunc\ntypedef void (APIENTRYP PFNGLLOGICOPPROC)(GLenum);\nGLAPI PFNGLLOGICOPPROC glad_glLogicOp;\n#define glLogicOp glad_glLogicOp\ntypedef void (APIENTRYP PFNGLSTENCILFUNCPROC)(GLenum, GLint, GLuint);\nGLAPI PFNGLSTENCILFUNCPROC glad_glStencilFunc;\n#define glStencilFunc glad_glStencilFunc\ntypedef void (APIENTRYP PFNGLSTENCILOPPROC)(GLenum, GLenum, GLenum);\nGLAPI PFNGLSTENCILOPPROC glad_glStencilOp;\n#define glStencilOp glad_glStencilOp\ntypedef void (APIENTRYP PFNGLDEPTHFUNCPROC)(GLenum);\nGLAPI PFNGLDEPTHFUNCPROC glad_glDepthFunc;\n#define glDepthFunc glad_glDepthFunc\ntypedef void (APIENTRYP PFNGLPIXELSTOREFPROC)(GLenum, GLfloat);\nGLAPI PFNGLPIXELSTOREFPROC glad_glPixelStoref;\n#define glPixelStoref glad_glPixelStoref\ntypedef void (APIENTRYP PFNGLPIXELSTOREIPROC)(GLenum, GLint);\nGLAPI PFNGLPIXELSTOREIPROC glad_glPixelStorei;\n#define glPixelStorei glad_glPixelStorei\ntypedef void (APIENTRYP PFNGLREADBUFFERPROC)(GLenum);\nGLAPI PFNGLREADBUFFERPROC glad_glReadBuffer;\n#define glReadBuffer glad_glReadBuffer\ntypedef void (APIENTRYP PFNGLREADPIXELSPROC)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, void*);\nGLAPI PFNGLREADPIXELSPROC glad_glReadPixels;\n#define glReadPixels glad_glReadPixels\ntypedef void (APIENTRYP PFNGLGETBOOLEANVPROC)(GLenum, GLboolean*);\nGLAPI PFNGLGETBOOLEANVPROC glad_glGetBooleanv;\n#define glGetBooleanv glad_glGetBooleanv\ntypedef void (APIENTRYP PFNGLGETDOUBLEVPROC)(GLenum, GLdouble*);\nGLAPI PFNGLGETDOUBLEVPROC glad_glGetDoublev;\n#define glGetDoublev glad_glGetDoublev\ntypedef GLenum (APIENTRYP PFNGLGETERRORPROC)();\nGLAPI PFNGLGETERRORPROC glad_glGetError;\n#define glGetError glad_glGetError\ntypedef void (APIENTRYP PFNGLGETFLOATVPROC)(GLenum, GLfloat*);\nGLAPI PFNGLGETFLOATVPROC glad_glGetFloatv;\n#define glGetFloatv glad_glGetFloatv\ntypedef void (APIENTRYP PFNGLGETINTEGERVPROC)(GLenum, GLint*);\nGLAPI PFNGLGETINTEGERVPROC glad_glGetIntegerv;\n#define glGetIntegerv glad_glGetIntegerv\ntypedef const GLubyte* (APIENTRYP PFNGLGETSTRINGPROC)(GLenum);\nGLAPI PFNGLGETSTRINGPROC glad_glGetString;\n#define glGetString glad_glGetString\ntypedef void (APIENTRYP PFNGLGETTEXIMAGEPROC)(GLenum, GLint, GLenum, GLenum, void*);\nGLAPI PFNGLGETTEXIMAGEPROC glad_glGetTexImage;\n#define glGetTexImage glad_glGetTexImage\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv;\n#define glGetTexParameterfv glad_glGetTexParameterfv\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv;\n#define glGetTexParameteriv glad_glGetTexParameteriv\ntypedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum, GLint, GLenum, GLfloat*);\nGLAPI PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv;\n#define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv\ntypedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum, GLint, GLenum, GLint*);\nGLAPI PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv;\n#define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv\ntypedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum);\nGLAPI PFNGLISENABLEDPROC glad_glIsEnabled;\n#define glIsEnabled glad_glIsEnabled\ntypedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble, GLdouble);\nGLAPI PFNGLDEPTHRANGEPROC glad_glDepthRange;\n#define glDepthRange glad_glDepthRange\ntypedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLVIEWPORTPROC glad_glViewport;\n#define glViewport glad_glViewport\n#endif\n#ifndef GL_VERSION_1_1\n#define GL_VERSION_1_1 1\nGLAPI int GLAD_GL_VERSION_1_1;\ntypedef void (APIENTRYP PFNGLDRAWARRAYSPROC)(GLenum, GLint, GLsizei);\nGLAPI PFNGLDRAWARRAYSPROC glad_glDrawArrays;\n#define glDrawArrays glad_glDrawArrays\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSPROC)(GLenum, GLsizei, GLenum, const void*);\nGLAPI PFNGLDRAWELEMENTSPROC glad_glDrawElements;\n#define glDrawElements glad_glDrawElements\ntypedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC)(GLfloat, GLfloat);\nGLAPI PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset;\n#define glPolygonOffset glad_glPolygonOffset\ntypedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC)(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLint);\nGLAPI PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D;\n#define glCopyTexImage1D glad_glCopyTexImage1D\ntypedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC)(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint);\nGLAPI PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D;\n#define glCopyTexImage2D glad_glCopyTexImage2D\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum, GLint, GLint, GLint, GLint, GLsizei);\nGLAPI PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D;\n#define glCopyTexSubImage1D glad_glCopyTexSubImage1D\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D;\n#define glCopyTexSubImage2D glad_glCopyTexSubImage2D\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC)(GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D;\n#define glTexSubImage1D glad_glTexSubImage1D\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D;\n#define glTexSubImage2D glad_glTexSubImage2D\ntypedef void (APIENTRYP PFNGLBINDTEXTUREPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDTEXTUREPROC glad_glBindTexture;\n#define glBindTexture glad_glBindTexture\ntypedef void (APIENTRYP PFNGLDELETETEXTURESPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETETEXTURESPROC glad_glDeleteTextures;\n#define glDeleteTextures glad_glDeleteTextures\ntypedef void (APIENTRYP PFNGLGENTEXTURESPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENTEXTURESPROC glad_glGenTextures;\n#define glGenTextures glad_glGenTextures\ntypedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC)(GLuint);\nGLAPI PFNGLISTEXTUREPROC glad_glIsTexture;\n#define glIsTexture glad_glIsTexture\n#endif\n#ifndef GL_VERSION_1_2\n#define GL_VERSION_1_2 1\nGLAPI int GLAD_GL_VERSION_1_2;\ntypedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC)(GLenum, GLuint, GLuint, GLsizei, GLenum, const void*);\nGLAPI PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements;\n#define glDrawRangeElements glad_glDrawRangeElements\ntypedef void (APIENTRYP PFNGLTEXIMAGE3DPROC)(GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXIMAGE3DPROC glad_glTexImage3D;\n#define glTexImage3D glad_glTexImage3D\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D;\n#define glTexSubImage3D glad_glTexSubImage3D\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D;\n#define glCopyTexSubImage3D glad_glCopyTexSubImage3D\n#endif\n#ifndef GL_VERSION_1_3\n#define GL_VERSION_1_3 1\nGLAPI int GLAD_GL_VERSION_1_3;\ntypedef void (APIENTRYP PFNGLACTIVETEXTUREPROC)(GLenum);\nGLAPI PFNGLACTIVETEXTUREPROC glad_glActiveTexture;\n#define glActiveTexture glad_glActiveTexture\ntypedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC)(GLfloat, GLboolean);\nGLAPI PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage;\n#define glSampleCoverage glad_glSampleCoverage\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D;\n#define glCompressedTexImage3D glad_glCompressedTexImage3D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D;\n#define glCompressedTexImage2D glad_glCompressedTexImage2D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D;\n#define glCompressedTexImage1D glad_glCompressedTexImage1D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D;\n#define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D;\n#define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D;\n#define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum, GLint, void*);\nGLAPI PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage;\n#define glGetCompressedTexImage glad_glGetCompressedTexImage\n#endif\n#ifndef GL_VERSION_1_4\n#define GL_VERSION_1_4 1\nGLAPI int GLAD_GL_VERSION_1_4;\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC)(GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate;\n#define glBlendFuncSeparate glad_glBlendFuncSeparate\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC)(GLenum, const GLint*, const GLsizei*, GLsizei);\nGLAPI PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays;\n#define glMultiDrawArrays glad_glMultiDrawArrays\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC)(GLenum, const GLsizei*, GLenum, const void**, GLsizei);\nGLAPI PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements;\n#define glMultiDrawElements glad_glMultiDrawElements\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC)(GLenum, GLfloat);\nGLAPI PFNGLPOINTPARAMETERFPROC glad_glPointParameterf;\n#define glPointParameterf glad_glPointParameterf\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv;\n#define glPointParameterfv glad_glPointParameterfv\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC)(GLenum, GLint);\nGLAPI PFNGLPOINTPARAMETERIPROC glad_glPointParameteri;\n#define glPointParameteri glad_glPointParameteri\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC)(GLenum, const GLint*);\nGLAPI PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv;\n#define glPointParameteriv glad_glPointParameteriv\ntypedef void (APIENTRYP PFNGLBLENDCOLORPROC)(GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLBLENDCOLORPROC glad_glBlendColor;\n#define glBlendColor glad_glBlendColor\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONPROC)(GLenum);\nGLAPI PFNGLBLENDEQUATIONPROC glad_glBlendEquation;\n#define glBlendEquation glad_glBlendEquation\n#endif\n#ifndef GL_VERSION_1_5\n#define GL_VERSION_1_5 1\nGLAPI int GLAD_GL_VERSION_1_5;\ntypedef void (APIENTRYP PFNGLGENQUERIESPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENQUERIESPROC glad_glGenQueries;\n#define glGenQueries glad_glGenQueries\ntypedef void (APIENTRYP PFNGLDELETEQUERIESPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEQUERIESPROC glad_glDeleteQueries;\n#define glDeleteQueries glad_glDeleteQueries\ntypedef GLboolean (APIENTRYP PFNGLISQUERYPROC)(GLuint);\nGLAPI PFNGLISQUERYPROC glad_glIsQuery;\n#define glIsQuery glad_glIsQuery\ntypedef void (APIENTRYP PFNGLBEGINQUERYPROC)(GLenum, GLuint);\nGLAPI PFNGLBEGINQUERYPROC glad_glBeginQuery;\n#define glBeginQuery glad_glBeginQuery\ntypedef void (APIENTRYP PFNGLENDQUERYPROC)(GLenum);\nGLAPI PFNGLENDQUERYPROC glad_glEndQuery;\n#define glEndQuery glad_glEndQuery\ntypedef void (APIENTRYP PFNGLGETQUERYIVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETQUERYIVPROC glad_glGetQueryiv;\n#define glGetQueryiv glad_glGetQueryiv\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv;\n#define glGetQueryObjectiv glad_glGetQueryObjectiv\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC)(GLuint, GLenum, GLuint*);\nGLAPI PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv;\n#define glGetQueryObjectuiv glad_glGetQueryObjectuiv\ntypedef void (APIENTRYP PFNGLBINDBUFFERPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDBUFFERPROC glad_glBindBuffer;\n#define glBindBuffer glad_glBindBuffer\ntypedef void (APIENTRYP PFNGLDELETEBUFFERSPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;\n#define glDeleteBuffers glad_glDeleteBuffers\ntypedef void (APIENTRYP PFNGLGENBUFFERSPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENBUFFERSPROC glad_glGenBuffers;\n#define glGenBuffers glad_glGenBuffers\ntypedef GLboolean (APIENTRYP PFNGLISBUFFERPROC)(GLuint);\nGLAPI PFNGLISBUFFERPROC glad_glIsBuffer;\n#define glIsBuffer glad_glIsBuffer\ntypedef void (APIENTRYP PFNGLBUFFERDATAPROC)(GLenum, GLsizeiptr, const void*, GLenum);\nGLAPI PFNGLBUFFERDATAPROC glad_glBufferData;\n#define glBufferData glad_glBufferData\ntypedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC)(GLenum, GLintptr, GLsizeiptr, const void*);\nGLAPI PFNGLBUFFERSUBDATAPROC glad_glBufferSubData;\n#define glBufferSubData glad_glBufferSubData\ntypedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC)(GLenum, GLintptr, GLsizeiptr, void*);\nGLAPI PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData;\n#define glGetBufferSubData glad_glGetBufferSubData\ntypedef void* (APIENTRYP PFNGLMAPBUFFERPROC)(GLenum, GLenum);\nGLAPI PFNGLMAPBUFFERPROC glad_glMapBuffer;\n#define glMapBuffer glad_glMapBuffer\ntypedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC)(GLenum);\nGLAPI PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer;\n#define glUnmapBuffer glad_glUnmapBuffer\ntypedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv;\n#define glGetBufferParameteriv glad_glGetBufferParameteriv\ntypedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC)(GLenum, GLenum, void**);\nGLAPI PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv;\n#define glGetBufferPointerv glad_glGetBufferPointerv\n#endif\n#ifndef GL_VERSION_2_0\n#define GL_VERSION_2_0 1\nGLAPI int GLAD_GL_VERSION_2_0;\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum, GLenum);\nGLAPI PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate;\n#define glBlendEquationSeparate glad_glBlendEquationSeparate\ntypedef void (APIENTRYP PFNGLDRAWBUFFERSPROC)(GLsizei, const GLenum*);\nGLAPI PFNGLDRAWBUFFERSPROC glad_glDrawBuffers;\n#define glDrawBuffers glad_glDrawBuffers\ntypedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC)(GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate;\n#define glStencilOpSeparate glad_glStencilOpSeparate\ntypedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC)(GLenum, GLenum, GLint, GLuint);\nGLAPI PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate;\n#define glStencilFuncSeparate glad_glStencilFuncSeparate\ntypedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC)(GLenum, GLuint);\nGLAPI PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate;\n#define glStencilMaskSeparate glad_glStencilMaskSeparate\ntypedef void (APIENTRYP PFNGLATTACHSHADERPROC)(GLuint, GLuint);\nGLAPI PFNGLATTACHSHADERPROC glad_glAttachShader;\n#define glAttachShader glad_glAttachShader\ntypedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC)(GLuint, GLuint, const GLchar*);\nGLAPI PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation;\n#define glBindAttribLocation glad_glBindAttribLocation\ntypedef void (APIENTRYP PFNGLCOMPILESHADERPROC)(GLuint);\nGLAPI PFNGLCOMPILESHADERPROC glad_glCompileShader;\n#define glCompileShader glad_glCompileShader\ntypedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC)();\nGLAPI PFNGLCREATEPROGRAMPROC glad_glCreateProgram;\n#define glCreateProgram glad_glCreateProgram\ntypedef GLuint (APIENTRYP PFNGLCREATESHADERPROC)(GLenum);\nGLAPI PFNGLCREATESHADERPROC glad_glCreateShader;\n#define glCreateShader glad_glCreateShader\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMPROC)(GLuint);\nGLAPI PFNGLDELETEPROGRAMPROC glad_glDeleteProgram;\n#define glDeleteProgram glad_glDeleteProgram\ntypedef void (APIENTRYP PFNGLDELETESHADERPROC)(GLuint);\nGLAPI PFNGLDELETESHADERPROC glad_glDeleteShader;\n#define glDeleteShader glad_glDeleteShader\ntypedef void (APIENTRYP PFNGLDETACHSHADERPROC)(GLuint, GLuint);\nGLAPI PFNGLDETACHSHADERPROC glad_glDetachShader;\n#define glDetachShader glad_glDetachShader\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint);\nGLAPI PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray;\n#define glDisableVertexAttribArray glad_glDisableVertexAttribArray\ntypedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint);\nGLAPI PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray;\n#define glEnableVertexAttribArray glad_glEnableVertexAttribArray\ntypedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLint*, GLenum*, GLchar*);\nGLAPI PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib;\n#define glGetActiveAttrib glad_glGetActiveAttrib\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLint*, GLenum*, GLchar*);\nGLAPI PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform;\n#define glGetActiveUniform glad_glGetActiveUniform\ntypedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC)(GLuint, GLsizei, GLsizei*, GLuint*);\nGLAPI PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders;\n#define glGetAttachedShaders glad_glGetAttachedShaders\ntypedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC)(GLuint, const GLchar*);\nGLAPI PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation;\n#define glGetAttribLocation glad_glGetAttribLocation\ntypedef void (APIENTRYP PFNGLGETPROGRAMIVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETPROGRAMIVPROC glad_glGetProgramiv;\n#define glGetProgramiv glad_glGetProgramiv\ntypedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC)(GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;\n#define glGetProgramInfoLog glad_glGetProgramInfoLog\ntypedef void (APIENTRYP PFNGLGETSHADERIVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETSHADERIVPROC glad_glGetShaderiv;\n#define glGetShaderiv glad_glGetShaderiv\ntypedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC)(GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog;\n#define glGetShaderInfoLog glad_glGetShaderInfoLog\ntypedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC)(GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource;\n#define glGetShaderSource glad_glGetShaderSource\ntypedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC)(GLuint, const GLchar*);\nGLAPI PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation;\n#define glGetUniformLocation glad_glGetUniformLocation\ntypedef void (APIENTRYP PFNGLGETUNIFORMFVPROC)(GLuint, GLint, GLfloat*);\nGLAPI PFNGLGETUNIFORMFVPROC glad_glGetUniformfv;\n#define glGetUniformfv glad_glGetUniformfv\ntypedef void (APIENTRYP PFNGLGETUNIFORMIVPROC)(GLuint, GLint, GLint*);\nGLAPI PFNGLGETUNIFORMIVPROC glad_glGetUniformiv;\n#define glGetUniformiv glad_glGetUniformiv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC)(GLuint, GLenum, GLdouble*);\nGLAPI PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv;\n#define glGetVertexAttribdv glad_glGetVertexAttribdv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv;\n#define glGetVertexAttribfv glad_glGetVertexAttribfv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv;\n#define glGetVertexAttribiv glad_glGetVertexAttribiv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint, GLenum, void**);\nGLAPI PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv;\n#define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC)(GLuint);\nGLAPI PFNGLISPROGRAMPROC glad_glIsProgram;\n#define glIsProgram glad_glIsProgram\ntypedef GLboolean (APIENTRYP PFNGLISSHADERPROC)(GLuint);\nGLAPI PFNGLISSHADERPROC glad_glIsShader;\n#define glIsShader glad_glIsShader\ntypedef void (APIENTRYP PFNGLLINKPROGRAMPROC)(GLuint);\nGLAPI PFNGLLINKPROGRAMPROC glad_glLinkProgram;\n#define glLinkProgram glad_glLinkProgram\ntypedef void (APIENTRYP PFNGLSHADERSOURCEPROC)(GLuint, GLsizei, const GLchar**, const GLint*);\nGLAPI PFNGLSHADERSOURCEPROC glad_glShaderSource;\n#define glShaderSource glad_glShaderSource\ntypedef void (APIENTRYP PFNGLUSEPROGRAMPROC)(GLuint);\nGLAPI PFNGLUSEPROGRAMPROC glad_glUseProgram;\n#define glUseProgram glad_glUseProgram\ntypedef void (APIENTRYP PFNGLUNIFORM1FPROC)(GLint, GLfloat);\nGLAPI PFNGLUNIFORM1FPROC glad_glUniform1f;\n#define glUniform1f glad_glUniform1f\ntypedef void (APIENTRYP PFNGLUNIFORM2FPROC)(GLint, GLfloat, GLfloat);\nGLAPI PFNGLUNIFORM2FPROC glad_glUniform2f;\n#define glUniform2f glad_glUniform2f\ntypedef void (APIENTRYP PFNGLUNIFORM3FPROC)(GLint, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLUNIFORM3FPROC glad_glUniform3f;\n#define glUniform3f glad_glUniform3f\ntypedef void (APIENTRYP PFNGLUNIFORM4FPROC)(GLint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLUNIFORM4FPROC glad_glUniform4f;\n#define glUniform4f glad_glUniform4f\ntypedef void (APIENTRYP PFNGLUNIFORM1IPROC)(GLint, GLint);\nGLAPI PFNGLUNIFORM1IPROC glad_glUniform1i;\n#define glUniform1i glad_glUniform1i\ntypedef void (APIENTRYP PFNGLUNIFORM2IPROC)(GLint, GLint, GLint);\nGLAPI PFNGLUNIFORM2IPROC glad_glUniform2i;\n#define glUniform2i glad_glUniform2i\ntypedef void (APIENTRYP PFNGLUNIFORM3IPROC)(GLint, GLint, GLint, GLint);\nGLAPI PFNGLUNIFORM3IPROC glad_glUniform3i;\n#define glUniform3i glad_glUniform3i\ntypedef void (APIENTRYP PFNGLUNIFORM4IPROC)(GLint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLUNIFORM4IPROC glad_glUniform4i;\n#define glUniform4i glad_glUniform4i\ntypedef void (APIENTRYP PFNGLUNIFORM1FVPROC)(GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLUNIFORM1FVPROC glad_glUniform1fv;\n#define glUniform1fv glad_glUniform1fv\ntypedef void (APIENTRYP PFNGLUNIFORM2FVPROC)(GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLUNIFORM2FVPROC glad_glUniform2fv;\n#define glUniform2fv glad_glUniform2fv\ntypedef void (APIENTRYP PFNGLUNIFORM3FVPROC)(GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLUNIFORM3FVPROC glad_glUniform3fv;\n#define glUniform3fv glad_glUniform3fv\ntypedef void (APIENTRYP PFNGLUNIFORM4FVPROC)(GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLUNIFORM4FVPROC glad_glUniform4fv;\n#define glUniform4fv glad_glUniform4fv\ntypedef void (APIENTRYP PFNGLUNIFORM1IVPROC)(GLint, GLsizei, const GLint*);\nGLAPI PFNGLUNIFORM1IVPROC glad_glUniform1iv;\n#define glUniform1iv glad_glUniform1iv\ntypedef void (APIENTRYP PFNGLUNIFORM2IVPROC)(GLint, GLsizei, const GLint*);\nGLAPI PFNGLUNIFORM2IVPROC glad_glUniform2iv;\n#define glUniform2iv glad_glUniform2iv\ntypedef void (APIENTRYP PFNGLUNIFORM3IVPROC)(GLint, GLsizei, const GLint*);\nGLAPI PFNGLUNIFORM3IVPROC glad_glUniform3iv;\n#define glUniform3iv glad_glUniform3iv\ntypedef void (APIENTRYP PFNGLUNIFORM4IVPROC)(GLint, GLsizei, const GLint*);\nGLAPI PFNGLUNIFORM4IVPROC glad_glUniform4iv;\n#define glUniform4iv glad_glUniform4iv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv;\n#define glUniformMatrix2fv glad_glUniformMatrix2fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv;\n#define glUniformMatrix3fv glad_glUniformMatrix3fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv;\n#define glUniformMatrix4fv glad_glUniformMatrix4fv\ntypedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC)(GLuint);\nGLAPI PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram;\n#define glValidateProgram glad_glValidateProgram\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC)(GLuint, GLdouble);\nGLAPI PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d;\n#define glVertexAttrib1d glad_glVertexAttrib1d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv;\n#define glVertexAttrib1dv glad_glVertexAttrib1dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC)(GLuint, GLfloat);\nGLAPI PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f;\n#define glVertexAttrib1f glad_glVertexAttrib1f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv;\n#define glVertexAttrib1fv glad_glVertexAttrib1fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC)(GLuint, GLshort);\nGLAPI PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s;\n#define glVertexAttrib1s glad_glVertexAttrib1s\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv;\n#define glVertexAttrib1sv glad_glVertexAttrib1sv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC)(GLuint, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d;\n#define glVertexAttrib2d glad_glVertexAttrib2d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv;\n#define glVertexAttrib2dv glad_glVertexAttrib2dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC)(GLuint, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f;\n#define glVertexAttrib2f glad_glVertexAttrib2f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv;\n#define glVertexAttrib2fv glad_glVertexAttrib2fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC)(GLuint, GLshort, GLshort);\nGLAPI PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s;\n#define glVertexAttrib2s glad_glVertexAttrib2s\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv;\n#define glVertexAttrib2sv glad_glVertexAttrib2sv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC)(GLuint, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d;\n#define glVertexAttrib3d glad_glVertexAttrib3d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv;\n#define glVertexAttrib3dv glad_glVertexAttrib3dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC)(GLuint, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f;\n#define glVertexAttrib3f glad_glVertexAttrib3f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv;\n#define glVertexAttrib3fv glad_glVertexAttrib3fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC)(GLuint, GLshort, GLshort, GLshort);\nGLAPI PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s;\n#define glVertexAttrib3s glad_glVertexAttrib3s\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv;\n#define glVertexAttrib3sv glad_glVertexAttrib3sv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC)(GLuint, const GLbyte*);\nGLAPI PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv;\n#define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv;\n#define glVertexAttrib4Niv glad_glVertexAttrib4Niv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv;\n#define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC)(GLuint, GLubyte, GLubyte, GLubyte, GLubyte);\nGLAPI PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub;\n#define glVertexAttrib4Nub glad_glVertexAttrib4Nub\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC)(GLuint, const GLubyte*);\nGLAPI PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv;\n#define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv;\n#define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC)(GLuint, const GLushort*);\nGLAPI PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv;\n#define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC)(GLuint, const GLbyte*);\nGLAPI PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv;\n#define glVertexAttrib4bv glad_glVertexAttrib4bv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC)(GLuint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d;\n#define glVertexAttrib4d glad_glVertexAttrib4d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv;\n#define glVertexAttrib4dv glad_glVertexAttrib4dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f;\n#define glVertexAttrib4f glad_glVertexAttrib4f\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv;\n#define glVertexAttrib4fv glad_glVertexAttrib4fv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv;\n#define glVertexAttrib4iv glad_glVertexAttrib4iv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC)(GLuint, GLshort, GLshort, GLshort, GLshort);\nGLAPI PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s;\n#define glVertexAttrib4s glad_glVertexAttrib4s\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv;\n#define glVertexAttrib4sv glad_glVertexAttrib4sv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC)(GLuint, const GLubyte*);\nGLAPI PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv;\n#define glVertexAttrib4ubv glad_glVertexAttrib4ubv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv;\n#define glVertexAttrib4uiv glad_glVertexAttrib4uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC)(GLuint, const GLushort*);\nGLAPI PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv;\n#define glVertexAttrib4usv glad_glVertexAttrib4usv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC)(GLuint, GLint, GLenum, GLboolean, GLsizei, const void*);\nGLAPI PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer;\n#define glVertexAttribPointer glad_glVertexAttribPointer\n#endif\n#ifndef GL_VERSION_2_1\n#define GL_VERSION_2_1 1\nGLAPI int GLAD_GL_VERSION_2_1;\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv;\n#define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv;\n#define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv;\n#define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv;\n#define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv;\n#define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv;\n#define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv\n#endif\n#ifndef GL_VERSION_3_0\n#define GL_VERSION_3_0 1\nGLAPI int GLAD_GL_VERSION_3_0;\ntypedef void (APIENTRYP PFNGLCOLORMASKIPROC)(GLuint, GLboolean, GLboolean, GLboolean, GLboolean);\nGLAPI PFNGLCOLORMASKIPROC glad_glColorMaski;\n#define glColorMaski glad_glColorMaski\ntypedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC)(GLenum, GLuint, GLboolean*);\nGLAPI PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v;\n#define glGetBooleani_v glad_glGetBooleani_v\ntypedef void (APIENTRYP PFNGLGETINTEGERI_VPROC)(GLenum, GLuint, GLint*);\nGLAPI PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v;\n#define glGetIntegeri_v glad_glGetIntegeri_v\ntypedef void (APIENTRYP PFNGLENABLEIPROC)(GLenum, GLuint);\nGLAPI PFNGLENABLEIPROC glad_glEnablei;\n#define glEnablei glad_glEnablei\ntypedef void (APIENTRYP PFNGLDISABLEIPROC)(GLenum, GLuint);\nGLAPI PFNGLDISABLEIPROC glad_glDisablei;\n#define glDisablei glad_glDisablei\ntypedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC)(GLenum, GLuint);\nGLAPI PFNGLISENABLEDIPROC glad_glIsEnabledi;\n#define glIsEnabledi glad_glIsEnabledi\ntypedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum);\nGLAPI PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback;\n#define glBeginTransformFeedback glad_glBeginTransformFeedback\ntypedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC)();\nGLAPI PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback;\n#define glEndTransformFeedback glad_glEndTransformFeedback\ntypedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC)(GLenum, GLuint, GLuint, GLintptr, GLsizeiptr);\nGLAPI PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange;\n#define glBindBufferRange glad_glBindBufferRange\ntypedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC)(GLenum, GLuint, GLuint);\nGLAPI PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase;\n#define glBindBufferBase glad_glBindBufferBase\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint, GLsizei, const GLchar**, GLenum);\nGLAPI PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings;\n#define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLsizei*, GLenum*, GLchar*);\nGLAPI PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying;\n#define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying\ntypedef void (APIENTRYP PFNGLCLAMPCOLORPROC)(GLenum, GLenum);\nGLAPI PFNGLCLAMPCOLORPROC glad_glClampColor;\n#define glClampColor glad_glClampColor\ntypedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC)(GLuint, GLenum);\nGLAPI PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender;\n#define glBeginConditionalRender glad_glBeginConditionalRender\ntypedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC)();\nGLAPI PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender;\n#define glEndConditionalRender glad_glEndConditionalRender\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint, GLint, GLenum, GLsizei, const void*);\nGLAPI PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer;\n#define glVertexAttribIPointer glad_glVertexAttribIPointer\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv;\n#define glGetVertexAttribIiv glad_glGetVertexAttribIiv\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint, GLenum, GLuint*);\nGLAPI PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv;\n#define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC)(GLuint, GLint);\nGLAPI PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i;\n#define glVertexAttribI1i glad_glVertexAttribI1i\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC)(GLuint, GLint, GLint);\nGLAPI PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i;\n#define glVertexAttribI2i glad_glVertexAttribI2i\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC)(GLuint, GLint, GLint, GLint);\nGLAPI PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i;\n#define glVertexAttribI3i glad_glVertexAttribI3i\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC)(GLuint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i;\n#define glVertexAttribI4i glad_glVertexAttribI4i\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC)(GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui;\n#define glVertexAttribI1ui glad_glVertexAttribI1ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui;\n#define glVertexAttribI2ui glad_glVertexAttribI2ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC)(GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui;\n#define glVertexAttribI3ui glad_glVertexAttribI3ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC)(GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui;\n#define glVertexAttribI4ui glad_glVertexAttribI4ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv;\n#define glVertexAttribI1iv glad_glVertexAttribI1iv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv;\n#define glVertexAttribI2iv glad_glVertexAttribI2iv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv;\n#define glVertexAttribI3iv glad_glVertexAttribI3iv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv;\n#define glVertexAttribI4iv glad_glVertexAttribI4iv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv;\n#define glVertexAttribI1uiv glad_glVertexAttribI1uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv;\n#define glVertexAttribI2uiv glad_glVertexAttribI2uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv;\n#define glVertexAttribI3uiv glad_glVertexAttribI3uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv;\n#define glVertexAttribI4uiv glad_glVertexAttribI4uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC)(GLuint, const GLbyte*);\nGLAPI PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv;\n#define glVertexAttribI4bv glad_glVertexAttribI4bv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv;\n#define glVertexAttribI4sv glad_glVertexAttribI4sv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC)(GLuint, const GLubyte*);\nGLAPI PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv;\n#define glVertexAttribI4ubv glad_glVertexAttribI4ubv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC)(GLuint, const GLushort*);\nGLAPI PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv;\n#define glVertexAttribI4usv glad_glVertexAttribI4usv\ntypedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC)(GLuint, GLint, GLuint*);\nGLAPI PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv;\n#define glGetUniformuiv glad_glGetUniformuiv\ntypedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC)(GLuint, GLuint, const GLchar*);\nGLAPI PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation;\n#define glBindFragDataLocation glad_glBindFragDataLocation\ntypedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC)(GLuint, const GLchar*);\nGLAPI PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation;\n#define glGetFragDataLocation glad_glGetFragDataLocation\ntypedef void (APIENTRYP PFNGLUNIFORM1UIPROC)(GLint, GLuint);\nGLAPI PFNGLUNIFORM1UIPROC glad_glUniform1ui;\n#define glUniform1ui glad_glUniform1ui\ntypedef void (APIENTRYP PFNGLUNIFORM2UIPROC)(GLint, GLuint, GLuint);\nGLAPI PFNGLUNIFORM2UIPROC glad_glUniform2ui;\n#define glUniform2ui glad_glUniform2ui\ntypedef void (APIENTRYP PFNGLUNIFORM3UIPROC)(GLint, GLuint, GLuint, GLuint);\nGLAPI PFNGLUNIFORM3UIPROC glad_glUniform3ui;\n#define glUniform3ui glad_glUniform3ui\ntypedef void (APIENTRYP PFNGLUNIFORM4UIPROC)(GLint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLUNIFORM4UIPROC glad_glUniform4ui;\n#define glUniform4ui glad_glUniform4ui\ntypedef void (APIENTRYP PFNGLUNIFORM1UIVPROC)(GLint, GLsizei, const GLuint*);\nGLAPI PFNGLUNIFORM1UIVPROC glad_glUniform1uiv;\n#define glUniform1uiv glad_glUniform1uiv\ntypedef void (APIENTRYP PFNGLUNIFORM2UIVPROC)(GLint, GLsizei, const GLuint*);\nGLAPI PFNGLUNIFORM2UIVPROC glad_glUniform2uiv;\n#define glUniform2uiv glad_glUniform2uiv\ntypedef void (APIENTRYP PFNGLUNIFORM3UIVPROC)(GLint, GLsizei, const GLuint*);\nGLAPI PFNGLUNIFORM3UIVPROC glad_glUniform3uiv;\n#define glUniform3uiv glad_glUniform3uiv\ntypedef void (APIENTRYP PFNGLUNIFORM4UIVPROC)(GLint, GLsizei, const GLuint*);\nGLAPI PFNGLUNIFORM4UIVPROC glad_glUniform4uiv;\n#define glUniform4uiv glad_glUniform4uiv\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv;\n#define glTexParameterIiv glad_glTexParameterIiv\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC)(GLenum, GLenum, const GLuint*);\nGLAPI PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv;\n#define glTexParameterIuiv glad_glTexParameterIuiv\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv;\n#define glGetTexParameterIiv glad_glGetTexParameterIiv\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC)(GLenum, GLenum, GLuint*);\nGLAPI PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv;\n#define glGetTexParameterIuiv glad_glGetTexParameterIuiv\ntypedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC)(GLenum, GLint, const GLint*);\nGLAPI PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv;\n#define glClearBufferiv glad_glClearBufferiv\ntypedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC)(GLenum, GLint, const GLuint*);\nGLAPI PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv;\n#define glClearBufferuiv glad_glClearBufferuiv\ntypedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC)(GLenum, GLint, const GLfloat*);\nGLAPI PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv;\n#define glClearBufferfv glad_glClearBufferfv\ntypedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC)(GLenum, GLint, GLfloat, GLint);\nGLAPI PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi;\n#define glClearBufferfi glad_glClearBufferfi\ntypedef const GLubyte* (APIENTRYP PFNGLGETSTRINGIPROC)(GLenum, GLuint);\nGLAPI PFNGLGETSTRINGIPROC glad_glGetStringi;\n#define glGetStringi glad_glGetStringi\ntypedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC)(GLuint);\nGLAPI PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer;\n#define glIsRenderbuffer glad_glIsRenderbuffer\ntypedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer;\n#define glBindRenderbuffer glad_glBindRenderbuffer\ntypedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers;\n#define glDeleteRenderbuffers glad_glDeleteRenderbuffers\ntypedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers;\n#define glGenRenderbuffers glad_glGenRenderbuffers\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC)(GLenum, GLenum, GLsizei, GLsizei);\nGLAPI PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage;\n#define glRenderbufferStorage glad_glRenderbufferStorage\ntypedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv;\n#define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv\ntypedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC)(GLuint);\nGLAPI PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer;\n#define glIsFramebuffer glad_glIsFramebuffer\ntypedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer;\n#define glBindFramebuffer glad_glBindFramebuffer\ntypedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers;\n#define glDeleteFramebuffers glad_glDeleteFramebuffers\ntypedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers;\n#define glGenFramebuffers glad_glGenFramebuffers\ntypedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum);\nGLAPI PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus;\n#define glCheckFramebufferStatus glad_glCheckFramebufferStatus\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum, GLenum, GLenum, GLuint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D;\n#define glFramebufferTexture1D glad_glFramebufferTexture1D\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum, GLenum, GLenum, GLuint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D;\n#define glFramebufferTexture2D glad_glFramebufferTexture2D\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D;\n#define glFramebufferTexture3D glad_glFramebufferTexture3D\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum, GLenum, GLenum, GLuint);\nGLAPI PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer;\n#define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv;\n#define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv\ntypedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC)(GLenum);\nGLAPI PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap;\n#define glGenerateMipmap glad_glGenerateMipmap\ntypedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC)(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum);\nGLAPI PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer;\n#define glBlitFramebuffer glad_glBlitFramebuffer\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei);\nGLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample;\n#define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum, GLenum, GLuint, GLint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer;\n#define glFramebufferTextureLayer glad_glFramebufferTextureLayer\ntypedef void* (APIENTRYP PFNGLMAPBUFFERRANGEPROC)(GLenum, GLintptr, GLsizeiptr, GLbitfield);\nGLAPI PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange;\n#define glMapBufferRange glad_glMapBufferRange\ntypedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum, GLintptr, GLsizeiptr);\nGLAPI PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange;\n#define glFlushMappedBufferRange glad_glFlushMappedBufferRange\ntypedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC)(GLuint);\nGLAPI PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray;\n#define glBindVertexArray glad_glBindVertexArray\ntypedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays;\n#define glDeleteVertexArrays glad_glDeleteVertexArrays\ntypedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays;\n#define glGenVertexArrays glad_glGenVertexArrays\ntypedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC)(GLuint);\nGLAPI PFNGLISVERTEXARRAYPROC glad_glIsVertexArray;\n#define glIsVertexArray glad_glIsVertexArray\n#endif\n#ifndef GL_VERSION_3_1\n#define GL_VERSION_3_1 1\nGLAPI int GLAD_GL_VERSION_3_1;\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum, GLint, GLsizei, GLsizei);\nGLAPI PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced;\n#define glDrawArraysInstanced glad_glDrawArraysInstanced\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum, GLsizei, GLenum, const void*, GLsizei);\nGLAPI PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced;\n#define glDrawElementsInstanced glad_glDrawElementsInstanced\ntypedef void (APIENTRYP PFNGLTEXBUFFERPROC)(GLenum, GLenum, GLuint);\nGLAPI PFNGLTEXBUFFERPROC glad_glTexBuffer;\n#define glTexBuffer glad_glTexBuffer\ntypedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint);\nGLAPI PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex;\n#define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex\ntypedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC)(GLenum, GLenum, GLintptr, GLintptr, GLsizeiptr);\nGLAPI PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData;\n#define glCopyBufferSubData glad_glCopyBufferSubData\ntypedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC)(GLuint, GLsizei, const GLchar**, GLuint*);\nGLAPI PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices;\n#define glGetUniformIndices glad_glGetUniformIndices\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint, GLsizei, const GLuint*, GLenum, GLint*);\nGLAPI PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv;\n#define glGetActiveUniformsiv glad_glGetActiveUniformsiv\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName;\n#define glGetActiveUniformName glad_glGetActiveUniformName\ntypedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint, const GLchar*);\nGLAPI PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex;\n#define glGetUniformBlockIndex glad_glGetUniformBlockIndex\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint, GLuint, GLenum, GLint*);\nGLAPI PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv;\n#define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName;\n#define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName\ntypedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding;\n#define glUniformBlockBinding glad_glUniformBlockBinding\n#endif\n#ifndef GL_VERSION_3_2\n#define GL_VERSION_3_2 1\nGLAPI int GLAD_GL_VERSION_3_2;\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum, GLsizei, GLenum, const void*, GLint);\nGLAPI PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex;\n#define glDrawElementsBaseVertex glad_glDrawElementsBaseVertex\ntypedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum, GLuint, GLuint, GLsizei, GLenum, const void*, GLint);\nGLAPI PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex;\n#define glDrawRangeElementsBaseVertex glad_glDrawRangeElementsBaseVertex\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum, GLsizei, GLenum, const void*, GLsizei, GLint);\nGLAPI PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex;\n#define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum, const GLsizei*, GLenum, const void**, GLsizei, const GLint*);\nGLAPI PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex;\n#define glMultiDrawElementsBaseVertex glad_glMultiDrawElementsBaseVertex\ntypedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC)(GLenum);\nGLAPI PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex;\n#define glProvokingVertex glad_glProvokingVertex\ntypedef GLsync (APIENTRYP PFNGLFENCESYNCPROC)(GLenum, GLbitfield);\nGLAPI PFNGLFENCESYNCPROC glad_glFenceSync;\n#define glFenceSync glad_glFenceSync\ntypedef GLboolean (APIENTRYP PFNGLISSYNCPROC)(GLsync);\nGLAPI PFNGLISSYNCPROC glad_glIsSync;\n#define glIsSync glad_glIsSync\ntypedef void (APIENTRYP PFNGLDELETESYNCPROC)(GLsync);\nGLAPI PFNGLDELETESYNCPROC glad_glDeleteSync;\n#define glDeleteSync glad_glDeleteSync\ntypedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC)(GLsync, GLbitfield, GLuint64);\nGLAPI PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync;\n#define glClientWaitSync glad_glClientWaitSync\ntypedef void (APIENTRYP PFNGLWAITSYNCPROC)(GLsync, GLbitfield, GLuint64);\nGLAPI PFNGLWAITSYNCPROC glad_glWaitSync;\n#define glWaitSync glad_glWaitSync\ntypedef void (APIENTRYP PFNGLGETINTEGER64VPROC)(GLenum, GLint64*);\nGLAPI PFNGLGETINTEGER64VPROC glad_glGetInteger64v;\n#define glGetInteger64v glad_glGetInteger64v\ntypedef void (APIENTRYP PFNGLGETSYNCIVPROC)(GLsync, GLenum, GLsizei, GLsizei*, GLint*);\nGLAPI PFNGLGETSYNCIVPROC glad_glGetSynciv;\n#define glGetSynciv glad_glGetSynciv\ntypedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC)(GLenum, GLuint, GLint64*);\nGLAPI PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v;\n#define glGetInteger64i_v glad_glGetInteger64i_v\ntypedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum, GLenum, GLint64*);\nGLAPI PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v;\n#define glGetBufferParameteri64v glad_glGetBufferParameteri64v\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum, GLenum, GLuint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture;\n#define glFramebufferTexture glad_glFramebufferTexture\ntypedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample;\n#define glTexImage2DMultisample glad_glTexImage2DMultisample\ntypedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample;\n#define glTexImage3DMultisample glad_glTexImage3DMultisample\ntypedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC)(GLenum, GLuint, GLfloat*);\nGLAPI PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv;\n#define glGetMultisamplefv glad_glGetMultisamplefv\ntypedef void (APIENTRYP PFNGLSAMPLEMASKIPROC)(GLuint, GLbitfield);\nGLAPI PFNGLSAMPLEMASKIPROC glad_glSampleMaski;\n#define glSampleMaski glad_glSampleMaski\n#endif\n#ifndef GL_VERSION_3_3\n#define GL_VERSION_3_3 1\nGLAPI int GLAD_GL_VERSION_3_3;\ntypedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint, GLuint, GLuint, const GLchar*);\nGLAPI PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed;\n#define glBindFragDataLocationIndexed glad_glBindFragDataLocationIndexed\ntypedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC)(GLuint, const GLchar*);\nGLAPI PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex;\n#define glGetFragDataIndex glad_glGetFragDataIndex\ntypedef void (APIENTRYP PFNGLGENSAMPLERSPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENSAMPLERSPROC glad_glGenSamplers;\n#define glGenSamplers glad_glGenSamplers\ntypedef void (APIENTRYP PFNGLDELETESAMPLERSPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers;\n#define glDeleteSamplers glad_glDeleteSamplers\ntypedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC)(GLuint);\nGLAPI PFNGLISSAMPLERPROC glad_glIsSampler;\n#define glIsSampler glad_glIsSampler\ntypedef void (APIENTRYP PFNGLBINDSAMPLERPROC)(GLuint, GLuint);\nGLAPI PFNGLBINDSAMPLERPROC glad_glBindSampler;\n#define glBindSampler glad_glBindSampler\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC)(GLuint, GLenum, GLint);\nGLAPI PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri;\n#define glSamplerParameteri glad_glSamplerParameteri\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC)(GLuint, GLenum, const GLint*);\nGLAPI PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv;\n#define glSamplerParameteriv glad_glSamplerParameteriv\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC)(GLuint, GLenum, GLfloat);\nGLAPI PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf;\n#define glSamplerParameterf glad_glSamplerParameterf\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC)(GLuint, GLenum, const GLfloat*);\nGLAPI PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv;\n#define glSamplerParameterfv glad_glSamplerParameterfv\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC)(GLuint, GLenum, const GLint*);\nGLAPI PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv;\n#define glSamplerParameterIiv glad_glSamplerParameterIiv\ntypedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint, GLenum, const GLuint*);\nGLAPI PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv;\n#define glSamplerParameterIuiv glad_glSamplerParameterIuiv\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv;\n#define glGetSamplerParameteriv glad_glGetSamplerParameteriv\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv;\n#define glGetSamplerParameterIiv glad_glGetSamplerParameterIiv\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv;\n#define glGetSamplerParameterfv glad_glGetSamplerParameterfv\ntypedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint, GLenum, GLuint*);\nGLAPI PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv;\n#define glGetSamplerParameterIuiv glad_glGetSamplerParameterIuiv\ntypedef void (APIENTRYP PFNGLQUERYCOUNTERPROC)(GLuint, GLenum);\nGLAPI PFNGLQUERYCOUNTERPROC glad_glQueryCounter;\n#define glQueryCounter glad_glQueryCounter\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC)(GLuint, GLenum, GLint64*);\nGLAPI PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v;\n#define glGetQueryObjecti64v glad_glGetQueryObjecti64v\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC)(GLuint, GLenum, GLuint64*);\nGLAPI PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v;\n#define glGetQueryObjectui64v glad_glGetQueryObjectui64v\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC)(GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor;\n#define glVertexAttribDivisor glad_glVertexAttribDivisor\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC)(GLuint, GLenum, GLboolean, GLuint);\nGLAPI PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui;\n#define glVertexAttribP1ui glad_glVertexAttribP1ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC)(GLuint, GLenum, GLboolean, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv;\n#define glVertexAttribP1uiv glad_glVertexAttribP1uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC)(GLuint, GLenum, GLboolean, GLuint);\nGLAPI PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui;\n#define glVertexAttribP2ui glad_glVertexAttribP2ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC)(GLuint, GLenum, GLboolean, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv;\n#define glVertexAttribP2uiv glad_glVertexAttribP2uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC)(GLuint, GLenum, GLboolean, GLuint);\nGLAPI PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui;\n#define glVertexAttribP3ui glad_glVertexAttribP3ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC)(GLuint, GLenum, GLboolean, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv;\n#define glVertexAttribP3uiv glad_glVertexAttribP3uiv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC)(GLuint, GLenum, GLboolean, GLuint);\nGLAPI PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui;\n#define glVertexAttribP4ui glad_glVertexAttribP4ui\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC)(GLuint, GLenum, GLboolean, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv;\n#define glVertexAttribP4uiv glad_glVertexAttribP4uiv\ntypedef void (APIENTRYP PFNGLVERTEXP2UIPROC)(GLenum, GLuint);\nGLAPI PFNGLVERTEXP2UIPROC glad_glVertexP2ui;\n#define glVertexP2ui glad_glVertexP2ui\ntypedef void (APIENTRYP PFNGLVERTEXP2UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv;\n#define glVertexP2uiv glad_glVertexP2uiv\ntypedef void (APIENTRYP PFNGLVERTEXP3UIPROC)(GLenum, GLuint);\nGLAPI PFNGLVERTEXP3UIPROC glad_glVertexP3ui;\n#define glVertexP3ui glad_glVertexP3ui\ntypedef void (APIENTRYP PFNGLVERTEXP3UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv;\n#define glVertexP3uiv glad_glVertexP3uiv\ntypedef void (APIENTRYP PFNGLVERTEXP4UIPROC)(GLenum, GLuint);\nGLAPI PFNGLVERTEXP4UIPROC glad_glVertexP4ui;\n#define glVertexP4ui glad_glVertexP4ui\ntypedef void (APIENTRYP PFNGLVERTEXP4UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv;\n#define glVertexP4uiv glad_glVertexP4uiv\ntypedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC)(GLenum, GLuint);\nGLAPI PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui;\n#define glTexCoordP1ui glad_glTexCoordP1ui\ntypedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv;\n#define glTexCoordP1uiv glad_glTexCoordP1uiv\ntypedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC)(GLenum, GLuint);\nGLAPI PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui;\n#define glTexCoordP2ui glad_glTexCoordP2ui\ntypedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv;\n#define glTexCoordP2uiv glad_glTexCoordP2uiv\ntypedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC)(GLenum, GLuint);\nGLAPI PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui;\n#define glTexCoordP3ui glad_glTexCoordP3ui\ntypedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv;\n#define glTexCoordP3uiv glad_glTexCoordP3uiv\ntypedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC)(GLenum, GLuint);\nGLAPI PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui;\n#define glTexCoordP4ui glad_glTexCoordP4ui\ntypedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv;\n#define glTexCoordP4uiv glad_glTexCoordP4uiv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC)(GLenum, GLenum, GLuint);\nGLAPI PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui;\n#define glMultiTexCoordP1ui glad_glMultiTexCoordP1ui\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC)(GLenum, GLenum, const GLuint*);\nGLAPI PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv;\n#define glMultiTexCoordP1uiv glad_glMultiTexCoordP1uiv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC)(GLenum, GLenum, GLuint);\nGLAPI PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui;\n#define glMultiTexCoordP2ui glad_glMultiTexCoordP2ui\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC)(GLenum, GLenum, const GLuint*);\nGLAPI PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv;\n#define glMultiTexCoordP2uiv glad_glMultiTexCoordP2uiv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC)(GLenum, GLenum, GLuint);\nGLAPI PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui;\n#define glMultiTexCoordP3ui glad_glMultiTexCoordP3ui\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC)(GLenum, GLenum, const GLuint*);\nGLAPI PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv;\n#define glMultiTexCoordP3uiv glad_glMultiTexCoordP3uiv\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC)(GLenum, GLenum, GLuint);\nGLAPI PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui;\n#define glMultiTexCoordP4ui glad_glMultiTexCoordP4ui\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC)(GLenum, GLenum, const GLuint*);\nGLAPI PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv;\n#define glMultiTexCoordP4uiv glad_glMultiTexCoordP4uiv\ntypedef void (APIENTRYP PFNGLNORMALP3UIPROC)(GLenum, GLuint);\nGLAPI PFNGLNORMALP3UIPROC glad_glNormalP3ui;\n#define glNormalP3ui glad_glNormalP3ui\ntypedef void (APIENTRYP PFNGLNORMALP3UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLNORMALP3UIVPROC glad_glNormalP3uiv;\n#define glNormalP3uiv glad_glNormalP3uiv\ntypedef void (APIENTRYP PFNGLCOLORP3UIPROC)(GLenum, GLuint);\nGLAPI PFNGLCOLORP3UIPROC glad_glColorP3ui;\n#define glColorP3ui glad_glColorP3ui\ntypedef void (APIENTRYP PFNGLCOLORP3UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLCOLORP3UIVPROC glad_glColorP3uiv;\n#define glColorP3uiv glad_glColorP3uiv\ntypedef void (APIENTRYP PFNGLCOLORP4UIPROC)(GLenum, GLuint);\nGLAPI PFNGLCOLORP4UIPROC glad_glColorP4ui;\n#define glColorP4ui glad_glColorP4ui\ntypedef void (APIENTRYP PFNGLCOLORP4UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLCOLORP4UIVPROC glad_glColorP4uiv;\n#define glColorP4uiv glad_glColorP4uiv\ntypedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC)(GLenum, GLuint);\nGLAPI PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui;\n#define glSecondaryColorP3ui glad_glSecondaryColorP3ui\ntypedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC)(GLenum, const GLuint*);\nGLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv;\n#define glSecondaryColorP3uiv glad_glSecondaryColorP3uiv\n#endif\n#ifndef GL_VERSION_4_0\n#define GL_VERSION_4_0 1\nGLAPI int GLAD_GL_VERSION_4_0;\ntypedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC)(GLfloat);\nGLAPI PFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading;\n#define glMinSampleShading glad_glMinSampleShading\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC)(GLuint, GLenum);\nGLAPI PFNGLBLENDEQUATIONIPROC glad_glBlendEquationi;\n#define glBlendEquationi glad_glBlendEquationi\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC)(GLuint, GLenum, GLenum);\nGLAPI PFNGLBLENDEQUATIONSEPARATEIPROC glad_glBlendEquationSeparatei;\n#define glBlendEquationSeparatei glad_glBlendEquationSeparatei\ntypedef void (APIENTRYP PFNGLBLENDFUNCIPROC)(GLuint, GLenum, GLenum);\nGLAPI PFNGLBLENDFUNCIPROC glad_glBlendFunci;\n#define glBlendFunci glad_glBlendFunci\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC)(GLuint, GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei;\n#define glBlendFuncSeparatei glad_glBlendFuncSeparatei\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC)(GLenum, const void*);\nGLAPI PFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect;\n#define glDrawArraysIndirect glad_glDrawArraysIndirect\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC)(GLenum, GLenum, const void*);\nGLAPI PFNGLDRAWELEMENTSINDIRECTPROC glad_glDrawElementsIndirect;\n#define glDrawElementsIndirect glad_glDrawElementsIndirect\ntypedef void (APIENTRYP PFNGLUNIFORM1DPROC)(GLint, GLdouble);\nGLAPI PFNGLUNIFORM1DPROC glad_glUniform1d;\n#define glUniform1d glad_glUniform1d\ntypedef void (APIENTRYP PFNGLUNIFORM2DPROC)(GLint, GLdouble, GLdouble);\nGLAPI PFNGLUNIFORM2DPROC glad_glUniform2d;\n#define glUniform2d glad_glUniform2d\ntypedef void (APIENTRYP PFNGLUNIFORM3DPROC)(GLint, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLUNIFORM3DPROC glad_glUniform3d;\n#define glUniform3d glad_glUniform3d\ntypedef void (APIENTRYP PFNGLUNIFORM4DPROC)(GLint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLUNIFORM4DPROC glad_glUniform4d;\n#define glUniform4d glad_glUniform4d\ntypedef void (APIENTRYP PFNGLUNIFORM1DVPROC)(GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLUNIFORM1DVPROC glad_glUniform1dv;\n#define glUniform1dv glad_glUniform1dv\ntypedef void (APIENTRYP PFNGLUNIFORM2DVPROC)(GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLUNIFORM2DVPROC glad_glUniform2dv;\n#define glUniform2dv glad_glUniform2dv\ntypedef void (APIENTRYP PFNGLUNIFORM3DVPROC)(GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLUNIFORM3DVPROC glad_glUniform3dv;\n#define glUniform3dv glad_glUniform3dv\ntypedef void (APIENTRYP PFNGLUNIFORM4DVPROC)(GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLUNIFORM4DVPROC glad_glUniform4dv;\n#define glUniform4dv glad_glUniform4dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC)(GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLUNIFORMMATRIX2DVPROC glad_glUniformMatrix2dv;\n#define glUniformMatrix2dv glad_glUniformMatrix2dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC)(GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLUNIFORMMATRIX3DVPROC glad_glUniformMatrix3dv;\n#define glUniformMatrix3dv glad_glUniformMatrix3dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC)(GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLUNIFORMMATRIX4DVPROC glad_glUniformMatrix4dv;\n#define glUniformMatrix4dv glad_glUniformMatrix4dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC)(GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLUNIFORMMATRIX2X3DVPROC glad_glUniformMatrix2x3dv;\n#define glUniformMatrix2x3dv glad_glUniformMatrix2x3dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC)(GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLUNIFORMMATRIX2X4DVPROC glad_glUniformMatrix2x4dv;\n#define glUniformMatrix2x4dv glad_glUniformMatrix2x4dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC)(GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLUNIFORMMATRIX3X2DVPROC glad_glUniformMatrix3x2dv;\n#define glUniformMatrix3x2dv glad_glUniformMatrix3x2dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC)(GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLUNIFORMMATRIX3X4DVPROC glad_glUniformMatrix3x4dv;\n#define glUniformMatrix3x4dv glad_glUniformMatrix3x4dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC)(GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLUNIFORMMATRIX4X2DVPROC glad_glUniformMatrix4x2dv;\n#define glUniformMatrix4x2dv glad_glUniformMatrix4x2dv\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC)(GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLUNIFORMMATRIX4X3DVPROC glad_glUniformMatrix4x3dv;\n#define glUniformMatrix4x3dv glad_glUniformMatrix4x3dv\ntypedef void (APIENTRYP PFNGLGETUNIFORMDVPROC)(GLuint, GLint, GLdouble*);\nGLAPI PFNGLGETUNIFORMDVPROC glad_glGetUniformdv;\n#define glGetUniformdv glad_glGetUniformdv\ntypedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)(GLuint, GLenum, const GLchar*);\nGLAPI PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glad_glGetSubroutineUniformLocation;\n#define glGetSubroutineUniformLocation glad_glGetSubroutineUniformLocation\ntypedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC)(GLuint, GLenum, const GLchar*);\nGLAPI PFNGLGETSUBROUTINEINDEXPROC glad_glGetSubroutineIndex;\n#define glGetSubroutineIndex glad_glGetSubroutineIndex\ntypedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)(GLuint, GLenum, GLuint, GLenum, GLint*);\nGLAPI PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glad_glGetActiveSubroutineUniformiv;\n#define glGetActiveSubroutineUniformiv glad_glGetActiveSubroutineUniformiv\ntypedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)(GLuint, GLenum, GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glad_glGetActiveSubroutineUniformName;\n#define glGetActiveSubroutineUniformName glad_glGetActiveSubroutineUniformName\ntypedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC)(GLuint, GLenum, GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName;\n#define glGetActiveSubroutineName glad_glGetActiveSubroutineName\ntypedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC)(GLenum, GLsizei, const GLuint*);\nGLAPI PFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv;\n#define glUniformSubroutinesuiv glad_glUniformSubroutinesuiv\ntypedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC)(GLenum, GLint, GLuint*);\nGLAPI PFNGLGETUNIFORMSUBROUTINEUIVPROC glad_glGetUniformSubroutineuiv;\n#define glGetUniformSubroutineuiv glad_glGetUniformSubroutineuiv\ntypedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC)(GLuint, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv;\n#define glGetProgramStageiv glad_glGetProgramStageiv\ntypedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC)(GLenum, GLint);\nGLAPI PFNGLPATCHPARAMETERIPROC glad_glPatchParameteri;\n#define glPatchParameteri glad_glPatchParameteri\ntypedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLPATCHPARAMETERFVPROC glad_glPatchParameterfv;\n#define glPatchParameterfv glad_glPatchParameterfv\ntypedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback;\n#define glBindTransformFeedback glad_glBindTransformFeedback\ntypedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks;\n#define glDeleteTransformFeedbacks glad_glDeleteTransformFeedbacks\ntypedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks;\n#define glGenTransformFeedbacks glad_glGenTransformFeedbacks\ntypedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC)(GLuint);\nGLAPI PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback;\n#define glIsTransformFeedback glad_glIsTransformFeedback\ntypedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC)();\nGLAPI PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback;\n#define glPauseTransformFeedback glad_glPauseTransformFeedback\ntypedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC)();\nGLAPI PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback;\n#define glResumeTransformFeedback glad_glResumeTransformFeedback\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC)(GLenum, GLuint);\nGLAPI PFNGLDRAWTRANSFORMFEEDBACKPROC glad_glDrawTransformFeedback;\n#define glDrawTransformFeedback glad_glDrawTransformFeedback\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)(GLenum, GLuint, GLuint);\nGLAPI PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream;\n#define glDrawTransformFeedbackStream glad_glDrawTransformFeedbackStream\ntypedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC)(GLenum, GLuint, GLuint);\nGLAPI PFNGLBEGINQUERYINDEXEDPROC glad_glBeginQueryIndexed;\n#define glBeginQueryIndexed glad_glBeginQueryIndexed\ntypedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC)(GLenum, GLuint);\nGLAPI PFNGLENDQUERYINDEXEDPROC glad_glEndQueryIndexed;\n#define glEndQueryIndexed glad_glEndQueryIndexed\ntypedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC)(GLenum, GLuint, GLenum, GLint*);\nGLAPI PFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv;\n#define glGetQueryIndexediv glad_glGetQueryIndexediv\n#endif\n#ifndef GL_VERSION_4_1\n#define GL_VERSION_4_1 1\nGLAPI int GLAD_GL_VERSION_4_1;\ntypedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC)();\nGLAPI PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler;\n#define glReleaseShaderCompiler glad_glReleaseShaderCompiler\ntypedef void (APIENTRYP PFNGLSHADERBINARYPROC)(GLsizei, const GLuint*, GLenum, const void*, GLsizei);\nGLAPI PFNGLSHADERBINARYPROC glad_glShaderBinary;\n#define glShaderBinary glad_glShaderBinary\ntypedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum, GLenum, GLint*, GLint*);\nGLAPI PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat;\n#define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat\ntypedef void (APIENTRYP PFNGLDEPTHRANGEFPROC)(GLfloat, GLfloat);\nGLAPI PFNGLDEPTHRANGEFPROC glad_glDepthRangef;\n#define glDepthRangef glad_glDepthRangef\ntypedef void (APIENTRYP PFNGLCLEARDEPTHFPROC)(GLfloat);\nGLAPI PFNGLCLEARDEPTHFPROC glad_glClearDepthf;\n#define glClearDepthf glad_glClearDepthf\ntypedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC)(GLuint, GLsizei, GLsizei*, GLenum*, void*);\nGLAPI PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary;\n#define glGetProgramBinary glad_glGetProgramBinary\ntypedef void (APIENTRYP PFNGLPROGRAMBINARYPROC)(GLuint, GLenum, const void*, GLsizei);\nGLAPI PFNGLPROGRAMBINARYPROC glad_glProgramBinary;\n#define glProgramBinary glad_glProgramBinary\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC)(GLuint, GLenum, GLint);\nGLAPI PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri;\n#define glProgramParameteri glad_glProgramParameteri\ntypedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC)(GLuint, GLbitfield, GLuint);\nGLAPI PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages;\n#define glUseProgramStages glad_glUseProgramStages\ntypedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC)(GLuint, GLuint);\nGLAPI PFNGLACTIVESHADERPROGRAMPROC glad_glActiveShaderProgram;\n#define glActiveShaderProgram glad_glActiveShaderProgram\ntypedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC)(GLenum, GLsizei, const GLchar**);\nGLAPI PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv;\n#define glCreateShaderProgramv glad_glCreateShaderProgramv\ntypedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC)(GLuint);\nGLAPI PFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline;\n#define glBindProgramPipeline glad_glBindProgramPipeline\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEPROGRAMPIPELINESPROC glad_glDeleteProgramPipelines;\n#define glDeleteProgramPipelines glad_glDeleteProgramPipelines\ntypedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENPROGRAMPIPELINESPROC glad_glGenProgramPipelines;\n#define glGenProgramPipelines glad_glGenProgramPipelines\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC)(GLuint);\nGLAPI PFNGLISPROGRAMPIPELINEPROC glad_glIsProgramPipeline;\n#define glIsProgramPipeline glad_glIsProgramPipeline\ntypedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETPROGRAMPIPELINEIVPROC glad_glGetProgramPipelineiv;\n#define glGetProgramPipelineiv glad_glGetProgramPipelineiv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC)(GLuint, GLint, GLint);\nGLAPI PFNGLPROGRAMUNIFORM1IPROC glad_glProgramUniform1i;\n#define glProgramUniform1i glad_glProgramUniform1i\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC)(GLuint, GLint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMUNIFORM1IVPROC glad_glProgramUniform1iv;\n#define glProgramUniform1iv glad_glProgramUniform1iv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC)(GLuint, GLint, GLfloat);\nGLAPI PFNGLPROGRAMUNIFORM1FPROC glad_glProgramUniform1f;\n#define glProgramUniform1f glad_glProgramUniform1f\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC)(GLuint, GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORM1FVPROC glad_glProgramUniform1fv;\n#define glProgramUniform1fv glad_glProgramUniform1fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC)(GLuint, GLint, GLdouble);\nGLAPI PFNGLPROGRAMUNIFORM1DPROC glad_glProgramUniform1d;\n#define glProgramUniform1d glad_glProgramUniform1d\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC)(GLuint, GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORM1DVPROC glad_glProgramUniform1dv;\n#define glProgramUniform1dv glad_glProgramUniform1dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC)(GLuint, GLint, GLuint);\nGLAPI PFNGLPROGRAMUNIFORM1UIPROC glad_glProgramUniform1ui;\n#define glProgramUniform1ui glad_glProgramUniform1ui\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC)(GLuint, GLint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMUNIFORM1UIVPROC glad_glProgramUniform1uiv;\n#define glProgramUniform1uiv glad_glProgramUniform1uiv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC)(GLuint, GLint, GLint, GLint);\nGLAPI PFNGLPROGRAMUNIFORM2IPROC glad_glProgramUniform2i;\n#define glProgramUniform2i glad_glProgramUniform2i\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC)(GLuint, GLint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMUNIFORM2IVPROC glad_glProgramUniform2iv;\n#define glProgramUniform2iv glad_glProgramUniform2iv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC)(GLuint, GLint, GLfloat, GLfloat);\nGLAPI PFNGLPROGRAMUNIFORM2FPROC glad_glProgramUniform2f;\n#define glProgramUniform2f glad_glProgramUniform2f\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC)(GLuint, GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORM2FVPROC glad_glProgramUniform2fv;\n#define glProgramUniform2fv glad_glProgramUniform2fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC)(GLuint, GLint, GLdouble, GLdouble);\nGLAPI PFNGLPROGRAMUNIFORM2DPROC glad_glProgramUniform2d;\n#define glProgramUniform2d glad_glProgramUniform2d\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC)(GLuint, GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORM2DVPROC glad_glProgramUniform2dv;\n#define glProgramUniform2dv glad_glProgramUniform2dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC)(GLuint, GLint, GLuint, GLuint);\nGLAPI PFNGLPROGRAMUNIFORM2UIPROC glad_glProgramUniform2ui;\n#define glProgramUniform2ui glad_glProgramUniform2ui\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC)(GLuint, GLint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMUNIFORM2UIVPROC glad_glProgramUniform2uiv;\n#define glProgramUniform2uiv glad_glProgramUniform2uiv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC)(GLuint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLPROGRAMUNIFORM3IPROC glad_glProgramUniform3i;\n#define glProgramUniform3i glad_glProgramUniform3i\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC)(GLuint, GLint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMUNIFORM3IVPROC glad_glProgramUniform3iv;\n#define glProgramUniform3iv glad_glProgramUniform3iv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC)(GLuint, GLint, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLPROGRAMUNIFORM3FPROC glad_glProgramUniform3f;\n#define glProgramUniform3f glad_glProgramUniform3f\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC)(GLuint, GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORM3FVPROC glad_glProgramUniform3fv;\n#define glProgramUniform3fv glad_glProgramUniform3fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC)(GLuint, GLint, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLPROGRAMUNIFORM3DPROC glad_glProgramUniform3d;\n#define glProgramUniform3d glad_glProgramUniform3d\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC)(GLuint, GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORM3DVPROC glad_glProgramUniform3dv;\n#define glProgramUniform3dv glad_glProgramUniform3dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC)(GLuint, GLint, GLuint, GLuint, GLuint);\nGLAPI PFNGLPROGRAMUNIFORM3UIPROC glad_glProgramUniform3ui;\n#define glProgramUniform3ui glad_glProgramUniform3ui\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC)(GLuint, GLint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMUNIFORM3UIVPROC glad_glProgramUniform3uiv;\n#define glProgramUniform3uiv glad_glProgramUniform3uiv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC)(GLuint, GLint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLPROGRAMUNIFORM4IPROC glad_glProgramUniform4i;\n#define glProgramUniform4i glad_glProgramUniform4i\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC)(GLuint, GLint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMUNIFORM4IVPROC glad_glProgramUniform4iv;\n#define glProgramUniform4iv glad_glProgramUniform4iv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC)(GLuint, GLint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLPROGRAMUNIFORM4FPROC glad_glProgramUniform4f;\n#define glProgramUniform4f glad_glProgramUniform4f\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC)(GLuint, GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORM4FVPROC glad_glProgramUniform4fv;\n#define glProgramUniform4fv glad_glProgramUniform4fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC)(GLuint, GLint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLPROGRAMUNIFORM4DPROC glad_glProgramUniform4d;\n#define glProgramUniform4d glad_glProgramUniform4d\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC)(GLuint, GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORM4DVPROC glad_glProgramUniform4dv;\n#define glProgramUniform4dv glad_glProgramUniform4dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC)(GLuint, GLint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLPROGRAMUNIFORM4UIPROC glad_glProgramUniform4ui;\n#define glProgramUniform4ui glad_glProgramUniform4ui\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC)(GLuint, GLint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMUNIFORM4UIVPROC glad_glProgramUniform4uiv;\n#define glProgramUniform4uiv glad_glProgramUniform4uiv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2FVPROC glad_glProgramUniformMatrix2fv;\n#define glProgramUniformMatrix2fv glad_glProgramUniformMatrix2fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3FVPROC glad_glProgramUniformMatrix3fv;\n#define glProgramUniformMatrix3fv glad_glProgramUniformMatrix3fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4FVPROC glad_glProgramUniformMatrix4fv;\n#define glProgramUniformMatrix4fv glad_glProgramUniformMatrix4fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2DVPROC glad_glProgramUniformMatrix2dv;\n#define glProgramUniformMatrix2dv glad_glProgramUniformMatrix2dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3DVPROC glad_glProgramUniformMatrix3dv;\n#define glProgramUniformMatrix3dv glad_glProgramUniformMatrix3dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4DVPROC glad_glProgramUniformMatrix4dv;\n#define glProgramUniformMatrix4dv glad_glProgramUniformMatrix4dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glad_glProgramUniformMatrix2x3fv;\n#define glProgramUniformMatrix2x3fv glad_glProgramUniformMatrix2x3fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glad_glProgramUniformMatrix3x2fv;\n#define glProgramUniformMatrix3x2fv glad_glProgramUniformMatrix3x2fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glad_glProgramUniformMatrix2x4fv;\n#define glProgramUniformMatrix2x4fv glad_glProgramUniformMatrix2x4fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glad_glProgramUniformMatrix4x2fv;\n#define glProgramUniformMatrix4x2fv glad_glProgramUniformMatrix4x2fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glad_glProgramUniformMatrix3x4fv;\n#define glProgramUniformMatrix3x4fv glad_glProgramUniformMatrix3x4fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glad_glProgramUniformMatrix4x3fv;\n#define glProgramUniformMatrix4x3fv glad_glProgramUniformMatrix4x3fv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glad_glProgramUniformMatrix2x3dv;\n#define glProgramUniformMatrix2x3dv glad_glProgramUniformMatrix2x3dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glad_glProgramUniformMatrix3x2dv;\n#define glProgramUniformMatrix3x2dv glad_glProgramUniformMatrix3x2dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glad_glProgramUniformMatrix2x4dv;\n#define glProgramUniformMatrix2x4dv glad_glProgramUniformMatrix2x4dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glad_glProgramUniformMatrix4x2dv;\n#define glProgramUniformMatrix4x2dv glad_glProgramUniformMatrix4x2dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glad_glProgramUniformMatrix3x4dv;\n#define glProgramUniformMatrix3x4dv glad_glProgramUniformMatrix3x4dv\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glad_glProgramUniformMatrix4x3dv;\n#define glProgramUniformMatrix4x3dv glad_glProgramUniformMatrix4x3dv\ntypedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC)(GLuint);\nGLAPI PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline;\n#define glValidateProgramPipeline glad_glValidateProgramPipeline\ntypedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC)(GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETPROGRAMPIPELINEINFOLOGPROC glad_glGetProgramPipelineInfoLog;\n#define glGetProgramPipelineInfoLog glad_glGetProgramPipelineInfoLog\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC)(GLuint, GLdouble);\nGLAPI PFNGLVERTEXATTRIBL1DPROC glad_glVertexAttribL1d;\n#define glVertexAttribL1d glad_glVertexAttribL1d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC)(GLuint, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIBL2DPROC glad_glVertexAttribL2d;\n#define glVertexAttribL2d glad_glVertexAttribL2d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC)(GLuint, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIBL3DPROC glad_glVertexAttribL3d;\n#define glVertexAttribL3d glad_glVertexAttribL3d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC)(GLuint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIBL4DPROC glad_glVertexAttribL4d;\n#define glVertexAttribL4d glad_glVertexAttribL4d\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBL1DVPROC glad_glVertexAttribL1dv;\n#define glVertexAttribL1dv glad_glVertexAttribL1dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBL2DVPROC glad_glVertexAttribL2dv;\n#define glVertexAttribL2dv glad_glVertexAttribL2dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBL3DVPROC glad_glVertexAttribL3dv;\n#define glVertexAttribL3dv glad_glVertexAttribL3dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBL4DVPROC glad_glVertexAttribL4dv;\n#define glVertexAttribL4dv glad_glVertexAttribL4dv\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC)(GLuint, GLint, GLenum, GLsizei, const void*);\nGLAPI PFNGLVERTEXATTRIBLPOINTERPROC glad_glVertexAttribLPointer;\n#define glVertexAttribLPointer glad_glVertexAttribLPointer\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC)(GLuint, GLenum, GLdouble*);\nGLAPI PFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv;\n#define glGetVertexAttribLdv glad_glGetVertexAttribLdv\ntypedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC)(GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLVIEWPORTARRAYVPROC glad_glViewportArrayv;\n#define glViewportArrayv glad_glViewportArrayv\ntypedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLVIEWPORTINDEXEDFPROC glad_glViewportIndexedf;\n#define glViewportIndexedf glad_glViewportIndexedf\ntypedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv;\n#define glViewportIndexedfv glad_glViewportIndexedfv\ntypedef void (APIENTRYP PFNGLSCISSORARRAYVPROC)(GLuint, GLsizei, const GLint*);\nGLAPI PFNGLSCISSORARRAYVPROC glad_glScissorArrayv;\n#define glScissorArrayv glad_glScissorArrayv\ntypedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC)(GLuint, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLSCISSORINDEXEDPROC glad_glScissorIndexed;\n#define glScissorIndexed glad_glScissorIndexed\ntypedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC)(GLuint, const GLint*);\nGLAPI PFNGLSCISSORINDEXEDVPROC glad_glScissorIndexedv;\n#define glScissorIndexedv glad_glScissorIndexedv\ntypedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC)(GLuint, GLsizei, const GLdouble*);\nGLAPI PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv;\n#define glDepthRangeArrayv glad_glDepthRangeArrayv\ntypedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC)(GLuint, GLdouble, GLdouble);\nGLAPI PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed;\n#define glDepthRangeIndexed glad_glDepthRangeIndexed\ntypedef void (APIENTRYP PFNGLGETFLOATI_VPROC)(GLenum, GLuint, GLfloat*);\nGLAPI PFNGLGETFLOATI_VPROC glad_glGetFloati_v;\n#define glGetFloati_v glad_glGetFloati_v\ntypedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC)(GLenum, GLuint, GLdouble*);\nGLAPI PFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v;\n#define glGetDoublei_v glad_glGetDoublei_v\n#endif\n#ifndef GL_VERSION_4_2\n#define GL_VERSION_4_2 1\nGLAPI int GLAD_GL_VERSION_4_2;\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)(GLenum, GLint, GLsizei, GLsizei, GLuint);\nGLAPI PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance;\n#define glDrawArraysInstancedBaseInstance glad_glDrawArraysInstancedBaseInstance\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)(GLenum, GLsizei, GLenum, const void*, GLsizei, GLuint);\nGLAPI PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glad_glDrawElementsInstancedBaseInstance;\n#define glDrawElementsInstancedBaseInstance glad_glDrawElementsInstancedBaseInstance\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)(GLenum, GLsizei, GLenum, const void*, GLsizei, GLint, GLuint);\nGLAPI PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance;\n#define glDrawElementsInstancedBaseVertexBaseInstance glad_glDrawElementsInstancedBaseVertexBaseInstance\ntypedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC)(GLenum, GLenum, GLenum, GLsizei, GLint*);\nGLAPI PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ;\n#define glGetInternalformativ glad_glGetInternalformativ\ntypedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)(GLuint, GLuint, GLenum, GLint*);\nGLAPI PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv;\n#define glGetActiveAtomicCounterBufferiv glad_glGetActiveAtomicCounterBufferiv\ntypedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC)(GLuint, GLuint, GLint, GLboolean, GLint, GLenum, GLenum);\nGLAPI PFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture;\n#define glBindImageTexture glad_glBindImageTexture\ntypedef void (APIENTRYP PFNGLMEMORYBARRIERPROC)(GLbitfield);\nGLAPI PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier;\n#define glMemoryBarrier glad_glMemoryBarrier\ntypedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC)(GLenum, GLsizei, GLenum, GLsizei);\nGLAPI PFNGLTEXSTORAGE1DPROC glad_glTexStorage1D;\n#define glTexStorage1D glad_glTexStorage1D\ntypedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei);\nGLAPI PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D;\n#define glTexStorage2D glad_glTexStorage2D\ntypedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei);\nGLAPI PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D;\n#define glTexStorage3D glad_glTexStorage3D\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)(GLenum, GLuint, GLsizei);\nGLAPI PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glad_glDrawTransformFeedbackInstanced;\n#define glDrawTransformFeedbackInstanced glad_glDrawTransformFeedbackInstanced\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)(GLenum, GLuint, GLuint, GLsizei);\nGLAPI PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced;\n#define glDrawTransformFeedbackStreamInstanced glad_glDrawTransformFeedbackStreamInstanced\n#endif\n#ifndef GL_VERSION_4_3\n#define GL_VERSION_4_3 1\nGLAPI int GLAD_GL_VERSION_4_3;\ntypedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC)(GLenum, GLenum, GLenum, GLenum, const void*);\nGLAPI PFNGLCLEARBUFFERDATAPROC glad_glClearBufferData;\n#define glClearBufferData glad_glClearBufferData\ntypedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC)(GLenum, GLenum, GLintptr, GLsizeiptr, GLenum, GLenum, const void*);\nGLAPI PFNGLCLEARBUFFERSUBDATAPROC glad_glClearBufferSubData;\n#define glClearBufferSubData glad_glClearBufferSubData\ntypedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute;\n#define glDispatchCompute glad_glDispatchCompute\ntypedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC)(GLintptr);\nGLAPI PFNGLDISPATCHCOMPUTEINDIRECTPROC glad_glDispatchComputeIndirect;\n#define glDispatchComputeIndirect glad_glDispatchComputeIndirect\ntypedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC)(GLuint, GLenum, GLint, GLint, GLint, GLint, GLuint, GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei);\nGLAPI PFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData;\n#define glCopyImageSubData glad_glCopyImageSubData\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC)(GLenum, GLenum, GLint);\nGLAPI PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri;\n#define glFramebufferParameteri glad_glFramebufferParameteri\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETFRAMEBUFFERPARAMETERIVPROC glad_glGetFramebufferParameteriv;\n#define glGetFramebufferParameteriv glad_glGetFramebufferParameteriv\ntypedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC)(GLenum, GLenum, GLenum, GLsizei, GLint64*);\nGLAPI PFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v;\n#define glGetInternalformati64v glad_glGetInternalformati64v\ntypedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC)(GLuint, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei);\nGLAPI PFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage;\n#define glInvalidateTexSubImage glad_glInvalidateTexSubImage\ntypedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC)(GLuint, GLint);\nGLAPI PFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage;\n#define glInvalidateTexImage glad_glInvalidateTexImage\ntypedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC)(GLuint, GLintptr, GLsizeiptr);\nGLAPI PFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData;\n#define glInvalidateBufferSubData glad_glInvalidateBufferSubData\ntypedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint);\nGLAPI PFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData;\n#define glInvalidateBufferData glad_glInvalidateBufferData\ntypedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC)(GLenum, GLsizei, const GLenum*);\nGLAPI PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer;\n#define glInvalidateFramebuffer glad_glInvalidateFramebuffer\ntypedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC)(GLenum, GLsizei, const GLenum*, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer;\n#define glInvalidateSubFramebuffer glad_glInvalidateSubFramebuffer\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC)(GLenum, const void*, GLsizei, GLsizei);\nGLAPI PFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect;\n#define glMultiDrawArraysIndirect glad_glMultiDrawArraysIndirect\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC)(GLenum, GLenum, const void*, GLsizei, GLsizei);\nGLAPI PFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect;\n#define glMultiDrawElementsIndirect glad_glMultiDrawElementsIndirect\ntypedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC)(GLuint, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETPROGRAMINTERFACEIVPROC glad_glGetProgramInterfaceiv;\n#define glGetProgramInterfaceiv glad_glGetProgramInterfaceiv\ntypedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC)(GLuint, GLenum, const GLchar*);\nGLAPI PFNGLGETPROGRAMRESOURCEINDEXPROC glad_glGetProgramResourceIndex;\n#define glGetProgramResourceIndex glad_glGetProgramResourceIndex\ntypedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC)(GLuint, GLenum, GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName;\n#define glGetProgramResourceName glad_glGetProgramResourceName\ntypedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC)(GLuint, GLenum, GLuint, GLsizei, const GLenum*, GLsizei, GLsizei*, GLint*);\nGLAPI PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv;\n#define glGetProgramResourceiv glad_glGetProgramResourceiv\ntypedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC)(GLuint, GLenum, const GLchar*);\nGLAPI PFNGLGETPROGRAMRESOURCELOCATIONPROC glad_glGetProgramResourceLocation;\n#define glGetProgramResourceLocation glad_glGetProgramResourceLocation\ntypedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)(GLuint, GLenum, const GLchar*);\nGLAPI PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glad_glGetProgramResourceLocationIndex;\n#define glGetProgramResourceLocationIndex glad_glGetProgramResourceLocationIndex\ntypedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding;\n#define glShaderStorageBlockBinding glad_glShaderStorageBlockBinding\ntypedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC)(GLenum, GLenum, GLuint, GLintptr, GLsizeiptr);\nGLAPI PFNGLTEXBUFFERRANGEPROC glad_glTexBufferRange;\n#define glTexBufferRange glad_glTexBufferRange\ntypedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXSTORAGE2DMULTISAMPLEPROC glad_glTexStorage2DMultisample;\n#define glTexStorage2DMultisample glad_glTexStorage2DMultisample\ntypedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXSTORAGE3DMULTISAMPLEPROC glad_glTexStorage3DMultisample;\n#define glTexStorage3DMultisample glad_glTexStorage3DMultisample\ntypedef void (APIENTRYP PFNGLTEXTUREVIEWPROC)(GLuint, GLenum, GLuint, GLenum, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLTEXTUREVIEWPROC glad_glTextureView;\n#define glTextureView glad_glTextureView\ntypedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC)(GLuint, GLuint, GLintptr, GLsizei);\nGLAPI PFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer;\n#define glBindVertexBuffer glad_glBindVertexBuffer\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC)(GLuint, GLint, GLenum, GLboolean, GLuint);\nGLAPI PFNGLVERTEXATTRIBFORMATPROC glad_glVertexAttribFormat;\n#define glVertexAttribFormat glad_glVertexAttribFormat\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC)(GLuint, GLint, GLenum, GLuint);\nGLAPI PFNGLVERTEXATTRIBIFORMATPROC glad_glVertexAttribIFormat;\n#define glVertexAttribIFormat glad_glVertexAttribIFormat\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC)(GLuint, GLint, GLenum, GLuint);\nGLAPI PFNGLVERTEXATTRIBLFORMATPROC glad_glVertexAttribLFormat;\n#define glVertexAttribLFormat glad_glVertexAttribLFormat\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC)(GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBBINDINGPROC glad_glVertexAttribBinding;\n#define glVertexAttribBinding glad_glVertexAttribBinding\ntypedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC)(GLuint, GLuint);\nGLAPI PFNGLVERTEXBINDINGDIVISORPROC glad_glVertexBindingDivisor;\n#define glVertexBindingDivisor glad_glVertexBindingDivisor\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC)(GLenum, GLenum, GLenum, GLsizei, const GLuint*, GLboolean);\nGLAPI PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl;\n#define glDebugMessageControl glad_glDebugMessageControl\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*);\nGLAPI PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert;\n#define glDebugMessageInsert glad_glDebugMessageInsert\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC, const void*);\nGLAPI PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback;\n#define glDebugMessageCallback glad_glDebugMessageCallback\ntypedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC)(GLuint, GLsizei, GLenum*, GLenum*, GLuint*, GLenum*, GLsizei*, GLchar*);\nGLAPI PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog;\n#define glGetDebugMessageLog glad_glGetDebugMessageLog\ntypedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC)(GLenum, GLuint, GLsizei, const GLchar*);\nGLAPI PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup;\n#define glPushDebugGroup glad_glPushDebugGroup\ntypedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC)();\nGLAPI PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup;\n#define glPopDebugGroup glad_glPopDebugGroup\ntypedef void (APIENTRYP PFNGLOBJECTLABELPROC)(GLenum, GLuint, GLsizei, const GLchar*);\nGLAPI PFNGLOBJECTLABELPROC glad_glObjectLabel;\n#define glObjectLabel glad_glObjectLabel\ntypedef void (APIENTRYP PFNGLGETOBJECTLABELPROC)(GLenum, GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel;\n#define glGetObjectLabel glad_glGetObjectLabel\ntypedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC)(const void*, GLsizei, const GLchar*);\nGLAPI PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel;\n#define glObjectPtrLabel glad_glObjectPtrLabel\ntypedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC)(const void*, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel;\n#define glGetObjectPtrLabel glad_glGetObjectPtrLabel\n#endif\n#ifndef GL_VERSION_4_4\n#define GL_VERSION_4_4 1\nGLAPI int GLAD_GL_VERSION_4_4;\ntypedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC)(GLenum, GLsizeiptr, const void*, GLbitfield);\nGLAPI PFNGLBUFFERSTORAGEPROC glad_glBufferStorage;\n#define glBufferStorage glad_glBufferStorage\ntypedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC)(GLuint, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage;\n#define glClearTexImage glad_glClearTexImage\ntypedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC)(GLuint, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage;\n#define glClearTexSubImage glad_glClearTexSubImage\ntypedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC)(GLenum, GLuint, GLsizei, const GLuint*);\nGLAPI PFNGLBINDBUFFERSBASEPROC glad_glBindBuffersBase;\n#define glBindBuffersBase glad_glBindBuffersBase\ntypedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC)(GLenum, GLuint, GLsizei, const GLuint*, const GLintptr*, const GLsizeiptr*);\nGLAPI PFNGLBINDBUFFERSRANGEPROC glad_glBindBuffersRange;\n#define glBindBuffersRange glad_glBindBuffersRange\ntypedef void (APIENTRYP PFNGLBINDTEXTURESPROC)(GLuint, GLsizei, const GLuint*);\nGLAPI PFNGLBINDTEXTURESPROC glad_glBindTextures;\n#define glBindTextures glad_glBindTextures\ntypedef void (APIENTRYP PFNGLBINDSAMPLERSPROC)(GLuint, GLsizei, const GLuint*);\nGLAPI PFNGLBINDSAMPLERSPROC glad_glBindSamplers;\n#define glBindSamplers glad_glBindSamplers\ntypedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC)(GLuint, GLsizei, const GLuint*);\nGLAPI PFNGLBINDIMAGETEXTURESPROC glad_glBindImageTextures;\n#define glBindImageTextures glad_glBindImageTextures\ntypedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC)(GLuint, GLsizei, const GLuint*, const GLintptr*, const GLsizei*);\nGLAPI PFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers;\n#define glBindVertexBuffers glad_glBindVertexBuffers\n#endif\n#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E\n#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F\n#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140\n#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141\n#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142\n#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143\n#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144\n#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145\n#define GL_POINT_SPRITE_NV 0x8861\n#define GL_COORD_REPLACE_NV 0x8862\n#define GL_POINT_SPRITE_R_MODE_NV 0x8863\n#define GL_ELEMENT_ARRAY_APPLE 0x8A0C\n#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D\n#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E\n#define GL_FUNC_SUBTRACT_EXT 0x800A\n#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B\n#define GL_MIRRORED_REPEAT_IBM 0x8370\n#define GL_TRANSFORM_HINT_APPLE 0x85B1\n#define GL_STENCIL_BACK_FUNC_ATI 0x8800\n#define GL_STENCIL_BACK_FAIL_ATI 0x8801\n#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802\n#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803\n#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4\n#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5\n#define GL_TEXTURE_BUFFER_EXT 0x8C2A\n#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B\n#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C\n#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D\n#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E\n#define GL_MAX_VERTEX_UNITS_ARB 0x86A4\n#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5\n#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6\n#define GL_VERTEX_BLEND_ARB 0x86A7\n#define GL_CURRENT_WEIGHT_ARB 0x86A8\n#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9\n#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA\n#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB\n#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC\n#define GL_WEIGHT_ARRAY_ARB 0x86AD\n#define GL_MODELVIEW0_ARB 0x1700\n#define GL_MODELVIEW1_ARB 0x850A\n#define GL_MODELVIEW2_ARB 0x8722\n#define GL_MODELVIEW3_ARB 0x8723\n#define GL_MODELVIEW4_ARB 0x8724\n#define GL_MODELVIEW5_ARB 0x8725\n#define GL_MODELVIEW6_ARB 0x8726\n#define GL_MODELVIEW7_ARB 0x8727\n#define GL_MODELVIEW8_ARB 0x8728\n#define GL_MODELVIEW9_ARB 0x8729\n#define GL_MODELVIEW10_ARB 0x872A\n#define GL_MODELVIEW11_ARB 0x872B\n#define GL_MODELVIEW12_ARB 0x872C\n#define GL_MODELVIEW13_ARB 0x872D\n#define GL_MODELVIEW14_ARB 0x872E\n#define GL_MODELVIEW15_ARB 0x872F\n#define GL_MODELVIEW16_ARB 0x8730\n#define GL_MODELVIEW17_ARB 0x8731\n#define GL_MODELVIEW18_ARB 0x8732\n#define GL_MODELVIEW19_ARB 0x8733\n#define GL_MODELVIEW20_ARB 0x8734\n#define GL_MODELVIEW21_ARB 0x8735\n#define GL_MODELVIEW22_ARB 0x8736\n#define GL_MODELVIEW23_ARB 0x8737\n#define GL_MODELVIEW24_ARB 0x8738\n#define GL_MODELVIEW25_ARB 0x8739\n#define GL_MODELVIEW26_ARB 0x873A\n#define GL_MODELVIEW27_ARB 0x873B\n#define GL_MODELVIEW28_ARB 0x873C\n#define GL_MODELVIEW29_ARB 0x873D\n#define GL_MODELVIEW30_ARB 0x873E\n#define GL_MODELVIEW31_ARB 0x873F\n#define GL_SAMPLES_ARB 0x80A9\n#define GL_COLOR_SAMPLES_NV 0x8E20\n#define GL_NORMAL_MAP_EXT 0x8511\n#define GL_REFLECTION_MAP_EXT 0x8512\n#define GL_TEXTURE_CUBE_MAP_EXT 0x8513\n#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A\n#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B\n#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C\n#define GL_INDEX_TEST_EXT 0x81B5\n#define GL_INDEX_TEST_FUNC_EXT 0x81B6\n#define GL_INDEX_TEST_REF_EXT 0x81B7\n#define GL_PALETTE4_RGB8_OES 0x8B90\n#define GL_PALETTE4_RGBA8_OES 0x8B91\n#define GL_PALETTE4_R5_G6_B5_OES 0x8B92\n#define GL_PALETTE4_RGBA4_OES 0x8B93\n#define GL_PALETTE4_RGB5_A1_OES 0x8B94\n#define GL_PALETTE8_RGB8_OES 0x8B95\n#define GL_PALETTE8_RGBA8_OES 0x8B96\n#define GL_PALETTE8_R5_G6_B5_OES 0x8B97\n#define GL_PALETTE8_RGBA4_OES 0x8B98\n#define GL_PALETTE8_RGB5_A1_OES 0x8B99\n#define GL_DEPTH_CLAMP_NV 0x864F\n#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D\n#define GL_GPU_ADDRESS_NV 0x8F34\n#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35\n#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5\n#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6\n#define GL_QUERY_WAIT_NV 0x8E13\n#define GL_QUERY_NO_WAIT_NV 0x8E14\n#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15\n#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16\n#define GL_COMBINE_EXT 0x8570\n#define GL_COMBINE_RGB_EXT 0x8571\n#define GL_COMBINE_ALPHA_EXT 0x8572\n#define GL_RGB_SCALE_EXT 0x8573\n#define GL_ADD_SIGNED_EXT 0x8574\n#define GL_INTERPOLATE_EXT 0x8575\n#define GL_CONSTANT_EXT 0x8576\n#define GL_PRIMARY_COLOR_EXT 0x8577\n#define GL_PREVIOUS_EXT 0x8578\n#define GL_SOURCE0_RGB_EXT 0x8580\n#define GL_SOURCE1_RGB_EXT 0x8581\n#define GL_SOURCE2_RGB_EXT 0x8582\n#define GL_SOURCE0_ALPHA_EXT 0x8588\n#define GL_SOURCE1_ALPHA_EXT 0x8589\n#define GL_SOURCE2_ALPHA_EXT 0x858A\n#define GL_OPERAND0_RGB_EXT 0x8590\n#define GL_OPERAND1_RGB_EXT 0x8591\n#define GL_OPERAND2_RGB_EXT 0x8592\n#define GL_OPERAND0_ALPHA_EXT 0x8598\n#define GL_OPERAND1_ALPHA_EXT 0x8599\n#define GL_OPERAND2_ALPHA_EXT 0x859A\n#define GL_FOG_DISTANCE_MODE_NV 0x855A\n#define GL_EYE_RADIAL_NV 0x855B\n#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C\n#define GL_EYE_PLANE 0x2502\n#define GL_ASYNC_HISTOGRAM_SGIX 0x832C\n#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D\n#define GL_MAX_SHININESS_NV 0x8504\n#define GL_MAX_SPOT_EXPONENT_NV 0x8505\n#define GL_COMBINE4_NV 0x8503\n#define GL_SOURCE3_RGB_NV 0x8583\n#define GL_SOURCE3_ALPHA_NV 0x858B\n#define GL_OPERAND3_RGB_NV 0x8593\n#define GL_OPERAND3_ALPHA_NV 0x859B\n#define GL_COMBINE_ARB 0x8570\n#define GL_COMBINE_RGB_ARB 0x8571\n#define GL_COMBINE_ALPHA_ARB 0x8572\n#define GL_SOURCE0_RGB_ARB 0x8580\n#define GL_SOURCE1_RGB_ARB 0x8581\n#define GL_SOURCE2_RGB_ARB 0x8582\n#define GL_SOURCE0_ALPHA_ARB 0x8588\n#define GL_SOURCE1_ALPHA_ARB 0x8589\n#define GL_SOURCE2_ALPHA_ARB 0x858A\n#define GL_OPERAND0_RGB_ARB 0x8590\n#define GL_OPERAND1_RGB_ARB 0x8591\n#define GL_OPERAND2_RGB_ARB 0x8592\n#define GL_OPERAND0_ALPHA_ARB 0x8598\n#define GL_OPERAND1_ALPHA_ARB 0x8599\n#define GL_OPERAND2_ALPHA_ARB 0x859A\n#define GL_RGB_SCALE_ARB 0x8573\n#define GL_ADD_SIGNED_ARB 0x8574\n#define GL_INTERPOLATE_ARB 0x8575\n#define GL_SUBTRACT_ARB 0x84E7\n#define GL_CONSTANT_ARB 0x8576\n#define GL_PRIMARY_COLOR_ARB 0x8577\n#define GL_PREVIOUS_ARB 0x8578\n#define GL_CONVOLUTION_1D_EXT 0x8010\n#define GL_CONVOLUTION_2D_EXT 0x8011\n#define GL_SEPARABLE_2D_EXT 0x8012\n#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013\n#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014\n#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015\n#define GL_REDUCE_EXT 0x8016\n#define GL_CONVOLUTION_FORMAT_EXT 0x8017\n#define GL_CONVOLUTION_WIDTH_EXT 0x8018\n#define GL_CONVOLUTION_HEIGHT_EXT 0x8019\n#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A\n#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B\n#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C\n#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D\n#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E\n#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F\n#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020\n#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021\n#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022\n#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023\n#define GL_COMPUTE_PROGRAM_NV 0x90FB\n#define GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC\n#define GL_INT64_NV 0x140E\n#define GL_UNSIGNED_INT64_NV 0x140F\n#define GL_COLOR_INDEX1_EXT 0x80E2\n#define GL_COLOR_INDEX2_EXT 0x80E3\n#define GL_COLOR_INDEX4_EXT 0x80E4\n#define GL_COLOR_INDEX8_EXT 0x80E5\n#define GL_COLOR_INDEX12_EXT 0x80E6\n#define GL_COLOR_INDEX16_EXT 0x80E7\n#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED\n#define GL_TEXTURE_BUFFER_ARB 0x8C2A\n#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B\n#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C\n#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D\n#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E\n#define GL_PN_TRIANGLES_ATI 0x87F0\n#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1\n#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2\n#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3\n#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4\n#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5\n#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6\n#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7\n#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8\n#define GL_PACK_RESAMPLE_SGIX 0x842C\n#define GL_UNPACK_RESAMPLE_SGIX 0x842D\n#define GL_RESAMPLE_REPLICATE_SGIX 0x842E\n#define GL_RESAMPLE_ZERO_FILL_SGIX 0x842F\n#define GL_RESAMPLE_DECIMATE_SGIX 0x8430\n#define GL_FRAGMENT_MATERIAL_EXT 0x8349\n#define GL_FRAGMENT_NORMAL_EXT 0x834A\n#define GL_FRAGMENT_COLOR_EXT 0x834C\n#define GL_ATTENUATION_EXT 0x834D\n#define GL_SHADOW_ATTENUATION_EXT 0x834E\n#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F\n#define GL_TEXTURE_LIGHT_EXT 0x8350\n#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351\n#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352\n#define GL_FRAGMENT_DEPTH_EXT 0x8452\n#define GL_POINT_SPRITE_ARB 0x8861\n#define GL_COORD_REPLACE_ARB 0x8862\n#define GL_HALF_FLOAT_ARB 0x140B\n#define GL_MAX_PROGRAM_PATCH_ATTRIBS_NV 0x86D8\n#define GL_TESS_CONTROL_PROGRAM_NV 0x891E\n#define GL_TESS_EVALUATION_PROGRAM_NV 0x891F\n#define GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV 0x8C74\n#define GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV 0x8C75\n#define GL_SCREEN_COORDINATES_REND 0x8490\n#define GL_INVERTED_SCREEN_W_REND 0x8491\n#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB\n#define GL_R11F_G11F_B10F_EXT 0x8C3A\n#define GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B\n#define GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C\n#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982\n#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983\n#define GL_VERTEX_PRECLIP_SGIX 0x83EE\n#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF\n#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179\n#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A\n#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B\n#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C\n#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18\n#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19\n#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A\n#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B\n#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C\n#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D\n#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF\n#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4\n#define GL_EMBOSS_LIGHT_NV 0x855D\n#define GL_EMBOSS_CONSTANT_NV 0x855E\n#define GL_EMBOSS_MAP_NV 0x855F\n#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42\n#define GL_TEXTURE_SWIZZLE_G_EXT 0x8E43\n#define GL_TEXTURE_SWIZZLE_B_EXT 0x8E44\n#define GL_TEXTURE_SWIZZLE_A_EXT 0x8E45\n#define GL_TEXTURE_SWIZZLE_RGBA_EXT 0x8E46\n#define GL_FRAGMENT_SHADER_ARB 0x8B30\n#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49\n#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B\n#define GL_ACTIVE_PROGRAM_EXT 0x8B8D\n#define GL_VERTEX_SHADER_BIT_EXT 0x00000001\n#define GL_FRAGMENT_SHADER_BIT_EXT 0x00000002\n#define GL_ALL_SHADER_BITS_EXT 0xFFFFFFFF\n#define GL_PROGRAM_SEPARABLE_EXT 0x8258\n#define GL_PROGRAM_PIPELINE_BINDING_EXT 0x825A\n#define GL_TEXTURE_COVERAGE_SAMPLES_NV 0x9045\n#define GL_TEXTURE_COLOR_SAMPLES_NV 0x9046\n#define GL_PROGRAM_OBJECT_ARB 0x8B40\n#define GL_SHADER_OBJECT_ARB 0x8B48\n#define GL_OBJECT_TYPE_ARB 0x8B4E\n#define GL_OBJECT_SUBTYPE_ARB 0x8B4F\n#define GL_FLOAT_VEC2_ARB 0x8B50\n#define GL_FLOAT_VEC3_ARB 0x8B51\n#define GL_FLOAT_VEC4_ARB 0x8B52\n#define GL_INT_VEC2_ARB 0x8B53\n#define GL_INT_VEC3_ARB 0x8B54\n#define GL_INT_VEC4_ARB 0x8B55\n#define GL_BOOL_ARB 0x8B56\n#define GL_BOOL_VEC2_ARB 0x8B57\n#define GL_BOOL_VEC3_ARB 0x8B58\n#define GL_BOOL_VEC4_ARB 0x8B59\n#define GL_FLOAT_MAT2_ARB 0x8B5A\n#define GL_FLOAT_MAT3_ARB 0x8B5B\n#define GL_FLOAT_MAT4_ARB 0x8B5C\n#define GL_SAMPLER_1D_ARB 0x8B5D\n#define GL_SAMPLER_2D_ARB 0x8B5E\n#define GL_SAMPLER_3D_ARB 0x8B5F\n#define GL_SAMPLER_CUBE_ARB 0x8B60\n#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61\n#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62\n#define GL_SAMPLER_2D_RECT_ARB 0x8B63\n#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64\n#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80\n#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81\n#define GL_OBJECT_LINK_STATUS_ARB 0x8B82\n#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83\n#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84\n#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85\n#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86\n#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87\n#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88\n#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14\n#define GL_TEXTURE_INTENSITY_TYPE 0x8C15\n#define GL_BUMP_ROT_MATRIX_ATI 0x8775\n#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776\n#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777\n#define GL_BUMP_TEX_UNITS_ATI 0x8778\n#define GL_DUDV_ATI 0x8779\n#define GL_DU8DV8_ATI 0x877A\n#define GL_BUMP_ENVMAP_ATI 0x877B\n#define GL_BUMP_TARGET_ATI 0x877C\n#define GL_TEXTURE_RECTANGLE_NV 0x84F5\n#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6\n#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7\n#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8\n#define GL_TEXTURE_RECTANGLE_ARB 0x84F5\n#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6\n#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7\n#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8\n#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC\n#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD\n#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004\n#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252\n#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253\n#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254\n#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255\n#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256\n#define GL_NO_RESET_NOTIFICATION_ARB 0x8261\n#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878\n#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879\n#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A\n#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B\n#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C\n#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D\n#define GL_READ_FRAMEBUFFER_EXT 0x8CA8\n#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9\n#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6\n#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA\n#define GL_DEPTH_COMPONENT16_SGIX 0x81A5\n#define GL_DEPTH_COMPONENT24_SGIX 0x81A6\n#define GL_DEPTH_COMPONENT32_SGIX 0x81A7\n#define GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3\n#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502\n#define GL_MODELVIEW0_MATRIX_EXT 0x0BA6\n#define GL_MODELVIEW1_MATRIX_EXT 0x8506\n#define GL_VERTEX_WEIGHTING_EXT 0x8509\n#define GL_MODELVIEW0_EXT 0x1700\n#define GL_MODELVIEW1_EXT 0x850A\n#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B\n#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C\n#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D\n#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E\n#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F\n#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510\n#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C\n#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D\n#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E\n#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F\n#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB\n#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC\n#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD\n#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE\n#define GL_EVAL_2D_NV 0x86C0\n#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1\n#define GL_MAP_TESSELLATION_NV 0x86C2\n#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3\n#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4\n#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5\n#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6\n#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7\n#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8\n#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9\n#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA\n#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB\n#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC\n#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD\n#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE\n#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF\n#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0\n#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1\n#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2\n#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3\n#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4\n#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5\n#define GL_MAX_MAP_TESSELLATION_NV 0x86D6\n#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7\n#define GL_FILTER4_SGIS 0x8146\n#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147\n#define GL_COUNTER_TYPE_AMD 0x8BC0\n#define GL_COUNTER_RANGE_AMD 0x8BC1\n#define GL_UNSIGNED_INT64_AMD 0x8BC2\n#define GL_PERCENTAGE_AMD 0x8BC3\n#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4\n#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5\n#define GL_PERFMON_RESULT_AMD 0x8BC6\n#define GL_STENCIL_TAG_BITS_EXT 0x88F2\n#define GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3\n#define GL_FRAME_NV 0x8E26\n#define GL_FIELDS_NV 0x8E27\n#define GL_CURRENT_TIME_NV 0x8E28\n#define GL_NUM_FILL_STREAMS_NV 0x8E29\n#define GL_PRESENT_TIME_NV 0x8E2A\n#define GL_PRESENT_DURATION_NV 0x8E2B\n#define GL_IGNORE_BORDER_HP 0x8150\n#define GL_CONSTANT_BORDER_HP 0x8151\n#define GL_REPLICATE_BORDER_HP 0x8153\n#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154\n#define GL_FOG_OFFSET_SGIX 0x8198\n#define GL_FOG_OFFSET_VALUE_SGIX 0x8199\n#define GL_INTERLACE_READ_INGR 0x8568\n#define GL_BACK_PRIMARY_COLOR_NV 0x8C77\n#define GL_BACK_SECONDARY_COLOR_NV 0x8C78\n#define GL_TEXTURE_COORD_NV 0x8C79\n#define GL_CLIP_DISTANCE_NV 0x8C7A\n#define GL_VERTEX_ID_NV 0x8C7B\n#define GL_PRIMITIVE_ID_NV 0x8C7C\n#define GL_GENERIC_ATTRIB_NV 0x8C7D\n#define GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E\n#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80\n#define GL_ACTIVE_VARYINGS_NV 0x8C81\n#define GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82\n#define GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83\n#define GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84\n#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85\n#define GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86\n#define GL_PRIMITIVES_GENERATED_NV 0x8C87\n#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88\n#define GL_RASTERIZER_DISCARD_NV 0x8C89\n#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV 0x8C8A\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B\n#define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C\n#define GL_SEPARATE_ATTRIBS_NV 0x8C8D\n#define GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E\n#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F\n#define GL_LAYER_NV 0x8DAA\n#define GL_NEXT_BUFFER_NV -2\n#define GL_SKIP_COMPONENTS4_NV -3\n#define GL_SKIP_COMPONENTS3_NV -4\n#define GL_SKIP_COMPONENTS2_NV -5\n#define GL_SKIP_COMPONENTS1_NV -6\n#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868\n#define GL_FRAGMENT_PROGRAM_NV 0x8870\n#define GL_MAX_TEXTURE_COORDS_NV 0x8871\n#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872\n#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873\n#define GL_PROGRAM_ERROR_STRING_NV 0x8874\n#define GL_SET_AMD 0x874A\n#define GL_REPLACE_VALUE_AMD 0x874B\n#define GL_STENCIL_OP_VALUE_AMD 0x874C\n#define GL_STENCIL_BACK_OP_VALUE_AMD 0x874D\n#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE\n#define GL_POLYGON_OFFSET_EXT 0x8037\n#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038\n#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039\n#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533\n#define GL_VIRTUAL_PAGE_SIZE_X_AMD 0x9195\n#define GL_VIRTUAL_PAGE_SIZE_Y_AMD 0x9196\n#define GL_VIRTUAL_PAGE_SIZE_Z_AMD 0x9197\n#define GL_MAX_SPARSE_TEXTURE_SIZE_AMD 0x9198\n#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD 0x9199\n#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS 0x919A\n#define GL_MIN_SPARSE_LEVEL_AMD 0x919B\n#define GL_MIN_LOD_WARNING_AMD 0x919C\n#define GL_TEXTURE_STORAGE_SPARSE_BIT_AMD 0x00000001\n#define GL_ALL_COMPLETED_NV 0x84F2\n#define GL_FENCE_STATUS_NV 0x84F3\n#define GL_FENCE_CONDITION_NV 0x84F4\n#define GL_QUAD_MESH_SUN 0x8614\n#define GL_TRIANGLE_MESH_SUN 0x8615\n#define GL_SYNC_CL_EVENT_ARB 0x8240\n#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241\n#define GL_DEPTH_STENCIL_NV 0x84F9\n#define GL_UNSIGNED_INT_24_8_NV 0x84FA\n#define GL_PRIMITIVE_RESTART_NV 0x8558\n#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559\n#define GL_GLOBAL_ALPHA_SUN 0x81D9\n#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA\n#define GL_TEXTURE_PRIORITY_EXT 0x8066\n#define GL_TEXTURE_RESIDENT_EXT 0x8067\n#define GL_TEXTURE_1D_BINDING_EXT 0x8068\n#define GL_TEXTURE_2D_BINDING_EXT 0x8069\n#define GL_TEXTURE_3D_BINDING_EXT 0x806A\n#define GL_DATA_BUFFER_AMD 0x9151\n#define GL_PERFORMANCE_MONITOR_AMD 0x9152\n#define GL_QUERY_OBJECT_AMD 0x9153\n#define GL_VERTEX_ARRAY_OBJECT_AMD 0x9154\n#define GL_SAMPLER_OBJECT_AMD 0x9155\n#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850\n#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851\n#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852\n#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853\n#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854\n#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855\n#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856\n#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857\n#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858\n#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859\n#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A\n#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B\n#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C\n#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D\n#define GL_HILO8_NV 0x885E\n#define GL_SIGNED_HILO8_NV 0x885F\n#define GL_FORCE_BLUE_TO_ONE_NV 0x8860\n#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF\n#define GL_ALPHA4_EXT 0x803B\n#define GL_ALPHA8_EXT 0x803C\n#define GL_ALPHA12_EXT 0x803D\n#define GL_ALPHA16_EXT 0x803E\n#define GL_LUMINANCE4_EXT 0x803F\n#define GL_LUMINANCE8_EXT 0x8040\n#define GL_LUMINANCE12_EXT 0x8041\n#define GL_LUMINANCE16_EXT 0x8042\n#define GL_LUMINANCE4_ALPHA4_EXT 0x8043\n#define GL_LUMINANCE6_ALPHA2_EXT 0x8044\n#define GL_LUMINANCE8_ALPHA8_EXT 0x8045\n#define GL_LUMINANCE12_ALPHA4_EXT 0x8046\n#define GL_LUMINANCE12_ALPHA12_EXT 0x8047\n#define GL_LUMINANCE16_ALPHA16_EXT 0x8048\n#define GL_INTENSITY_EXT 0x8049\n#define GL_INTENSITY4_EXT 0x804A\n#define GL_INTENSITY8_EXT 0x804B\n#define GL_INTENSITY12_EXT 0x804C\n#define GL_INTENSITY16_EXT 0x804D\n#define GL_RGB2_EXT 0x804E\n#define GL_RGB4_EXT 0x804F\n#define GL_RGB5_EXT 0x8050\n#define GL_RGB8_EXT 0x8051\n#define GL_RGB10_EXT 0x8052\n#define GL_RGB12_EXT 0x8053\n#define GL_RGB16_EXT 0x8054\n#define GL_RGBA2_EXT 0x8055\n#define GL_RGBA4_EXT 0x8056\n#define GL_RGB5_A1_EXT 0x8057\n#define GL_RGBA8_EXT 0x8058\n#define GL_RGB10_A2_EXT 0x8059\n#define GL_RGBA12_EXT 0x805A\n#define GL_RGBA16_EXT 0x805B\n#define GL_TEXTURE_RED_SIZE_EXT 0x805C\n#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D\n#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E\n#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F\n#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060\n#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061\n#define GL_REPLACE_EXT 0x8062\n#define GL_PROXY_TEXTURE_1D_EXT 0x8063\n#define GL_PROXY_TEXTURE_2D_EXT 0x8064\n#define GL_TEXTURE_TOO_LARGE_EXT 0x8065\n#define GL_VERTEX_ATTRIB_MAP1_APPLE 0x8A00\n#define GL_VERTEX_ATTRIB_MAP2_APPLE 0x8A01\n#define GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE 0x8A02\n#define GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE 0x8A03\n#define GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE 0x8A04\n#define GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE 0x8A05\n#define GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE 0x8A06\n#define GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE 0x8A07\n#define GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE 0x8A08\n#define GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE 0x8A09\n#define GL_SHADOW_AMBIENT_SGIX 0x80BF\n#define GL_NORMAL_MAP_ARB 0x8511\n#define GL_REFLECTION_MAP_ARB 0x8512\n#define GL_TEXTURE_CUBE_MAP_ARB 0x8513\n#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518\n#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519\n#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A\n#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B\n#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C\n#define GL_LIST_PRIORITY_SGIX 0x8182\n#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E\n#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F\n#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20\n#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21\n#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22\n#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23\n#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24\n#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25\n#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26\n#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27\n#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28\n#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29\n#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A\n#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B\n#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C\n#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D\n#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E\n#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F\n#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30\n#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31\n#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32\n#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33\n#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40\n#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41\n#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42\n#define GL_DOT3_RGB_EXT 0x8740\n#define GL_DOT3_RGBA_EXT 0x8741\n#define GL_MODULATE_ADD_ATI 0x8744\n#define GL_MODULATE_SIGNED_ADD_ATI 0x8745\n#define GL_MODULATE_SUBTRACT_ATI 0x8746\n#define GL_BLEND_OVERLAP_NV 0x9281\n#define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280\n#define GL_BLUE_NV 0x1905\n#define GL_COLORBURN_NV 0x929A\n#define GL_COLORDODGE_NV 0x9299\n#define GL_CONJOINT_NV 0x9284\n#define GL_CONTRAST_NV 0x92A1\n#define GL_DARKEN_NV 0x9297\n#define GL_DIFFERENCE_NV 0x929E\n#define GL_DISJOINT_NV 0x9283\n#define GL_DST_ATOP_NV 0x928F\n#define GL_DST_IN_NV 0x928B\n#define GL_DST_NV 0x9287\n#define GL_DST_OUT_NV 0x928D\n#define GL_DST_OVER_NV 0x9289\n#define GL_EXCLUSION_NV 0x92A0\n#define GL_GREEN_NV 0x1904\n#define GL_HARDLIGHT_NV 0x929B\n#define GL_HARDMIX_NV 0x92A9\n#define GL_HSL_COLOR_NV 0x92AF\n#define GL_HSL_HUE_NV 0x92AD\n#define GL_HSL_LUMINOSITY_NV 0x92B0\n#define GL_HSL_SATURATION_NV 0x92AE\n#define GL_INVERT_OVG_NV 0x92B4\n#define GL_INVERT_RGB_NV 0x92A3\n#define GL_LIGHTEN_NV 0x9298\n#define GL_LINEARBURN_NV 0x92A5\n#define GL_LINEARDODGE_NV 0x92A4\n#define GL_LINEARLIGHT_NV 0x92A7\n#define GL_MINUS_CLAMPED_NV 0x92B3\n#define GL_MINUS_NV 0x929F\n#define GL_MULTIPLY_NV 0x9294\n#define GL_OVERLAY_NV 0x9296\n#define GL_PINLIGHT_NV 0x92A8\n#define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2\n#define GL_PLUS_CLAMPED_NV 0x92B1\n#define GL_PLUS_DARKER_NV 0x9292\n#define GL_PLUS_NV 0x9291\n#define GL_RED_NV 0x1903\n#define GL_SCREEN_NV 0x9295\n#define GL_SOFTLIGHT_NV 0x929C\n#define GL_SRC_ATOP_NV 0x928E\n#define GL_SRC_IN_NV 0x928A\n#define GL_SRC_NV 0x9286\n#define GL_SRC_OUT_NV 0x928C\n#define GL_SRC_OVER_NV 0x9288\n#define GL_UNCORRELATED_NV 0x9282\n#define GL_VIVIDLIGHT_NV 0x92A6\n#define GL_XOR_NV 0x1506\n#define GL_LINEAR_SHARPEN_SGIS 0x80AD\n#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE\n#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF\n#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0\n#define GL_COLOR_SUM_ARB 0x8458\n#define GL_VERTEX_PROGRAM_ARB 0x8620\n#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622\n#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623\n#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624\n#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625\n#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626\n#define GL_PROGRAM_LENGTH_ARB 0x8627\n#define GL_PROGRAM_STRING_ARB 0x8628\n#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E\n#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F\n#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640\n#define GL_CURRENT_MATRIX_ARB 0x8641\n#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642\n#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643\n#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645\n#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B\n#define GL_PROGRAM_BINDING_ARB 0x8677\n#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869\n#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A\n#define GL_PROGRAM_ERROR_STRING_ARB 0x8874\n#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875\n#define GL_PROGRAM_FORMAT_ARB 0x8876\n#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0\n#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1\n#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2\n#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3\n#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4\n#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5\n#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6\n#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7\n#define GL_PROGRAM_PARAMETERS_ARB 0x88A8\n#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9\n#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA\n#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB\n#define GL_PROGRAM_ATTRIBS_ARB 0x88AC\n#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD\n#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE\n#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF\n#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0\n#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1\n#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2\n#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3\n#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4\n#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5\n#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6\n#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7\n#define GL_MATRIX0_ARB 0x88C0\n#define GL_MATRIX1_ARB 0x88C1\n#define GL_MATRIX2_ARB 0x88C2\n#define GL_MATRIX3_ARB 0x88C3\n#define GL_MATRIX4_ARB 0x88C4\n#define GL_MATRIX5_ARB 0x88C5\n#define GL_MATRIX6_ARB 0x88C6\n#define GL_MATRIX7_ARB 0x88C7\n#define GL_MATRIX8_ARB 0x88C8\n#define GL_MATRIX9_ARB 0x88C9\n#define GL_MATRIX10_ARB 0x88CA\n#define GL_MATRIX11_ARB 0x88CB\n#define GL_MATRIX12_ARB 0x88CC\n#define GL_MATRIX13_ARB 0x88CD\n#define GL_MATRIX14_ARB 0x88CE\n#define GL_MATRIX15_ARB 0x88CF\n#define GL_MATRIX16_ARB 0x88D0\n#define GL_MATRIX17_ARB 0x88D1\n#define GL_MATRIX18_ARB 0x88D2\n#define GL_MATRIX19_ARB 0x88D3\n#define GL_MATRIX20_ARB 0x88D4\n#define GL_MATRIX21_ARB 0x88D5\n#define GL_MATRIX22_ARB 0x88D6\n#define GL_MATRIX23_ARB 0x88D7\n#define GL_MATRIX24_ARB 0x88D8\n#define GL_MATRIX25_ARB 0x88D9\n#define GL_MATRIX26_ARB 0x88DA\n#define GL_MATRIX27_ARB 0x88DB\n#define GL_MATRIX28_ARB 0x88DC\n#define GL_MATRIX29_ARB 0x88DD\n#define GL_MATRIX30_ARB 0x88DE\n#define GL_MATRIX31_ARB 0x88DF\n#define GL_INTERLACE_OML 0x8980\n#define GL_INTERLACE_READ_OML 0x8981\n#define GL_RGBA_FLOAT_MODE_ATI 0x8820\n#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835\n#define GL_BUFFER_SIZE_ARB 0x8764\n#define GL_BUFFER_USAGE_ARB 0x8765\n#define GL_ARRAY_BUFFER_ARB 0x8892\n#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893\n#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894\n#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895\n#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896\n#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897\n#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898\n#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899\n#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A\n#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B\n#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C\n#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D\n#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E\n#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F\n#define GL_READ_ONLY_ARB 0x88B8\n#define GL_WRITE_ONLY_ARB 0x88B9\n#define GL_READ_WRITE_ARB 0x88BA\n#define GL_BUFFER_ACCESS_ARB 0x88BB\n#define GL_BUFFER_MAPPED_ARB 0x88BC\n#define GL_BUFFER_MAP_POINTER_ARB 0x88BD\n#define GL_STREAM_DRAW_ARB 0x88E0\n#define GL_STREAM_READ_ARB 0x88E1\n#define GL_STREAM_COPY_ARB 0x88E2\n#define GL_STATIC_DRAW_ARB 0x88E4\n#define GL_STATIC_READ_ARB 0x88E5\n#define GL_STATIC_COPY_ARB 0x88E6\n#define GL_DYNAMIC_DRAW_ARB 0x88E8\n#define GL_DYNAMIC_READ_ARB 0x88E9\n#define GL_DYNAMIC_COPY_ARB 0x88EA\n#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200\n#define GL_VERTEX_ARRAY_RANGE_NV 0x851D\n#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E\n#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F\n#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520\n#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521\n#define GL_FRAGMENT_LIGHTING_SGIX 0x8400\n#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401\n#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402\n#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403\n#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404\n#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405\n#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406\n#define GL_LIGHT_ENV_MODE_SGIX 0x8407\n#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408\n#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409\n#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A\n#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B\n#define GL_FRAGMENT_LIGHT0_SGIX 0x840C\n#define GL_FRAGMENT_LIGHT1_SGIX 0x840D\n#define GL_FRAGMENT_LIGHT2_SGIX 0x840E\n#define GL_FRAGMENT_LIGHT3_SGIX 0x840F\n#define GL_FRAGMENT_LIGHT4_SGIX 0x8410\n#define GL_FRAGMENT_LIGHT5_SGIX 0x8411\n#define GL_FRAGMENT_LIGHT6_SGIX 0x8412\n#define GL_FRAGMENT_LIGHT7_SGIX 0x8413\n#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F\n#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB\n#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10\n#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11\n#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12\n#define GL_TIME_ELAPSED_EXT 0x88BF\n#define GL_VERTEX_ARRAY 0x8074\n#define GL_STACK_OVERFLOW 0x0503\n#define GL_STACK_UNDERFLOW 0x0504\n#define GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR 0x8242\n#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR 0x8243\n#define GL_DEBUG_CALLBACK_FUNCTION_KHR 0x8244\n#define GL_DEBUG_CALLBACK_USER_PARAM_KHR 0x8245\n#define GL_DEBUG_SOURCE_API_KHR 0x8246\n#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR 0x8247\n#define GL_DEBUG_SOURCE_SHADER_COMPILER_KHR 0x8248\n#define GL_DEBUG_SOURCE_THIRD_PARTY_KHR 0x8249\n#define GL_DEBUG_SOURCE_APPLICATION_KHR 0x824A\n#define GL_DEBUG_SOURCE_OTHER_KHR 0x824B\n#define GL_DEBUG_TYPE_ERROR_KHR 0x824C\n#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR 0x824D\n#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR 0x824E\n#define GL_DEBUG_TYPE_PORTABILITY_KHR 0x824F\n#define GL_DEBUG_TYPE_PERFORMANCE_KHR 0x8250\n#define GL_DEBUG_TYPE_OTHER_KHR 0x8251\n#define GL_DEBUG_TYPE_MARKER_KHR 0x8268\n#define GL_DEBUG_TYPE_PUSH_GROUP_KHR 0x8269\n#define GL_DEBUG_TYPE_POP_GROUP_KHR 0x826A\n#define GL_DEBUG_SEVERITY_NOTIFICATION_KHR 0x826B\n#define GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR 0x826C\n#define GL_DEBUG_GROUP_STACK_DEPTH_KHR 0x826D\n#define GL_BUFFER_KHR 0x82E0\n#define GL_SHADER_KHR 0x82E1\n#define GL_PROGRAM_KHR 0x82E2\n#define GL_VERTEX_ARRAY_KHR 0x8074\n#define GL_QUERY_KHR 0x82E3\n#define GL_SAMPLER_KHR 0x82E6\n#define GL_MAX_LABEL_LENGTH_KHR 0x82E8\n#define GL_MAX_DEBUG_MESSAGE_LENGTH_KHR 0x9143\n#define GL_MAX_DEBUG_LOGGED_MESSAGES_KHR 0x9144\n#define GL_DEBUG_LOGGED_MESSAGES_KHR 0x9145\n#define GL_DEBUG_SEVERITY_HIGH_KHR 0x9146\n#define GL_DEBUG_SEVERITY_MEDIUM_KHR 0x9147\n#define GL_DEBUG_SEVERITY_LOW_KHR 0x9148\n#define GL_DEBUG_OUTPUT_KHR 0x92E0\n#define GL_CONTEXT_FLAG_DEBUG_BIT_KHR 0x00000002\n#define GL_STACK_OVERFLOW_KHR 0x0503\n#define GL_STACK_UNDERFLOW_KHR 0x0504\n#define GL_CLAMP_TO_BORDER_SGIS 0x812D\n#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170\n#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171\n#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172\n#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173\n#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174\n#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175\n#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176\n#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177\n#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178\n#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D\n#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E\n#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F\n#define GL_GEOMETRY_SHADER_EXT 0x8DD9\n#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA\n#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB\n#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC\n#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29\n#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD\n#define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE\n#define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B\n#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF\n#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0\n#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1\n#define GL_LINES_ADJACENCY_EXT 0x000A\n#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B\n#define GL_TRIANGLES_ADJACENCY_EXT 0x000C\n#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D\n#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8\n#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9\n#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7\n#define GL_PROGRAM_POINT_SIZE_EXT 0x8642\n#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA\n#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB\n#define GL_YCBCR_MESA 0x8757\n#define GL_TEXTURE_1D_STACK_MESAX 0x8759\n#define GL_TEXTURE_2D_STACK_MESAX 0x875A\n#define GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B\n#define GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C\n#define GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D\n#define GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E\n#define GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2\n#define GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3\n#define GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4\n#define GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED\n#define GL_UNIFORM_BUFFER_EXT 0x8DEE\n#define GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF\n#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0\n#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1\n#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2\n#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3\n#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4\n#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5\n#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6\n#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7\n#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8\n#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9\n#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA\n#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB\n#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC\n#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC\n#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD\n#define GL_ELEMENT_ARRAY_ATI 0x8768\n#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769\n#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A\n#define GL_REFERENCE_PLANE_SGIX 0x817D\n#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E\n#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910\n#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911\n#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E\n#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F\n#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190\n#define GL_SAMPLE_POSITION_NV 0x8E50\n#define GL_SAMPLE_MASK_NV 0x8E51\n#define GL_SAMPLE_MASK_VALUE_NV 0x8E52\n#define GL_TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53\n#define GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54\n#define GL_TEXTURE_RENDERBUFFER_NV 0x8E55\n#define GL_SAMPLER_RENDERBUFFER_NV 0x8E56\n#define GL_INT_SAMPLER_RENDERBUFFER_NV 0x8E57\n#define GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58\n#define GL_MAX_SAMPLE_MASK_WORDS_NV 0x8E59\n#define GL_ALL_STATIC_DATA_IBM 103060\n#define GL_STATIC_VERTEX_ARRAY_IBM 103061\n#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0\n#define GL_PERTURB_EXT 0x85AE\n#define GL_TEXTURE_NORMAL_EXT 0x85AF\n#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6\n#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7\n#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8\n#define GL_POINT_SIZE_MIN_EXT 0x8126\n#define GL_POINT_SIZE_MAX_EXT 0x8127\n#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128\n#define GL_DISTANCE_ATTENUATION_EXT 0x8129\n#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8\n#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD\n#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE\n#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202\n#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203\n#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204\n#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C\n#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D\n#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E\n#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F\n#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210\n#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211\n#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216\n#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217\n#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218\n#define GL_FULL_STIPPLE_HINT_PGI 0x1A219\n#define GL_CLIP_NEAR_HINT_PGI 0x1A220\n#define GL_CLIP_FAR_HINT_PGI 0x1A221\n#define GL_WIDE_LINE_HINT_PGI 0x1A222\n#define GL_BACK_NORMALS_HINT_PGI 0x1A223\n#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0\n#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1\n#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2\n#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3\n#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4\n#define GL_VERTEX_SHADER_ARB 0x8B31\n#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A\n#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B\n#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C\n#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D\n#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89\n#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A\n#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872\n#define GL_MAX_TEXTURE_COORDS_ARB 0x8871\n#define GL_DUAL_ALPHA4_SGIS 0x8110\n#define GL_DUAL_ALPHA8_SGIS 0x8111\n#define GL_DUAL_ALPHA12_SGIS 0x8112\n#define GL_DUAL_ALPHA16_SGIS 0x8113\n#define GL_DUAL_LUMINANCE4_SGIS 0x8114\n#define GL_DUAL_LUMINANCE8_SGIS 0x8115\n#define GL_DUAL_LUMINANCE12_SGIS 0x8116\n#define GL_DUAL_LUMINANCE16_SGIS 0x8117\n#define GL_DUAL_INTENSITY4_SGIS 0x8118\n#define GL_DUAL_INTENSITY8_SGIS 0x8119\n#define GL_DUAL_INTENSITY12_SGIS 0x811A\n#define GL_DUAL_INTENSITY16_SGIS 0x811B\n#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C\n#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D\n#define GL_QUAD_ALPHA4_SGIS 0x811E\n#define GL_QUAD_ALPHA8_SGIS 0x811F\n#define GL_QUAD_LUMINANCE4_SGIS 0x8120\n#define GL_QUAD_LUMINANCE8_SGIS 0x8121\n#define GL_QUAD_INTENSITY4_SGIS 0x8122\n#define GL_QUAD_INTENSITY8_SGIS 0x8123\n#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124\n#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125\n#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C\n#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D\n#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E\n#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9\n#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA\n#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB\n#define GL_DSDT_MAG_INTENSITY_NV 0x86DC\n#define GL_SHADER_CONSISTENT_NV 0x86DD\n#define GL_TEXTURE_SHADER_NV 0x86DE\n#define GL_SHADER_OPERATION_NV 0x86DF\n#define GL_CULL_MODES_NV 0x86E0\n#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1\n#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2\n#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3\n#define GL_OFFSET_TEXTURE_2D_MATRIX_NV 0x86E1\n#define GL_OFFSET_TEXTURE_2D_SCALE_NV 0x86E2\n#define GL_OFFSET_TEXTURE_2D_BIAS_NV 0x86E3\n#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4\n#define GL_CONST_EYE_NV 0x86E5\n#define GL_PASS_THROUGH_NV 0x86E6\n#define GL_CULL_FRAGMENT_NV 0x86E7\n#define GL_OFFSET_TEXTURE_2D_NV 0x86E8\n#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9\n#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA\n#define GL_DOT_PRODUCT_NV 0x86EC\n#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED\n#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE\n#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0\n#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1\n#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2\n#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3\n#define GL_HILO_NV 0x86F4\n#define GL_DSDT_NV 0x86F5\n#define GL_DSDT_MAG_NV 0x86F6\n#define GL_DSDT_MAG_VIB_NV 0x86F7\n#define GL_HILO16_NV 0x86F8\n#define GL_SIGNED_HILO_NV 0x86F9\n#define GL_SIGNED_HILO16_NV 0x86FA\n#define GL_SIGNED_RGBA_NV 0x86FB\n#define GL_SIGNED_RGBA8_NV 0x86FC\n#define GL_SIGNED_RGB_NV 0x86FE\n#define GL_SIGNED_RGB8_NV 0x86FF\n#define GL_SIGNED_LUMINANCE_NV 0x8701\n#define GL_SIGNED_LUMINANCE8_NV 0x8702\n#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703\n#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704\n#define GL_SIGNED_ALPHA_NV 0x8705\n#define GL_SIGNED_ALPHA8_NV 0x8706\n#define GL_SIGNED_INTENSITY_NV 0x8707\n#define GL_SIGNED_INTENSITY8_NV 0x8708\n#define GL_DSDT8_NV 0x8709\n#define GL_DSDT8_MAG8_NV 0x870A\n#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B\n#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C\n#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D\n#define GL_HI_SCALE_NV 0x870E\n#define GL_LO_SCALE_NV 0x870F\n#define GL_DS_SCALE_NV 0x8710\n#define GL_DT_SCALE_NV 0x8711\n#define GL_MAGNITUDE_SCALE_NV 0x8712\n#define GL_VIBRANCE_SCALE_NV 0x8713\n#define GL_HI_BIAS_NV 0x8714\n#define GL_LO_BIAS_NV 0x8715\n#define GL_DS_BIAS_NV 0x8716\n#define GL_DT_BIAS_NV 0x8717\n#define GL_MAGNITUDE_BIAS_NV 0x8718\n#define GL_VIBRANCE_BIAS_NV 0x8719\n#define GL_TEXTURE_BORDER_VALUES_NV 0x871A\n#define GL_TEXTURE_HI_SIZE_NV 0x871B\n#define GL_TEXTURE_LO_SIZE_NV 0x871C\n#define GL_TEXTURE_DS_SIZE_NV 0x871D\n#define GL_TEXTURE_DT_SIZE_NV 0x871E\n#define GL_TEXTURE_MAG_SIZE_NV 0x871F\n#define GL_QUADS 0x0007\n#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC\n#define GL_VERTEX_ELEMENT_SWIZZLE_AMD 0x91A4\n#define GL_VERTEX_ID_SWIZZLE_AMD 0x91A5\n#define GL_FRAGMENT_PROGRAM_ARB 0x8804\n#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805\n#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806\n#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807\n#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808\n#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809\n#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A\n#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B\n#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C\n#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D\n#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E\n#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F\n#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810\n#define GL_PACK_RESAMPLE_OML 0x8984\n#define GL_UNPACK_RESAMPLE_OML 0x8985\n#define GL_RESAMPLE_REPLICATE_OML 0x8986\n#define GL_RESAMPLE_ZERO_FILL_OML 0x8987\n#define GL_RESAMPLE_AVERAGE_OML 0x8988\n#define GL_RESAMPLE_DECIMATE_OML 0x8989\n#define GL_YCBCR_422_APPLE 0x85B9\n#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA\n#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB\n#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE\n#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF\n#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB\n#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC\n#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED\n#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF\n#define GL_PIXEL_TEXTURE_SGIS 0x8353\n#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354\n#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355\n#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356\n#define GL_GENERATE_MIPMAP_SGIS 0x8191\n#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192\n#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180\n#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181\n#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167\n#define GL_TEXTURE_POST_SPECULAR_HP 0x8168\n#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169\n#define GL_MIN_EXT 0x8007\n#define GL_MAX_EXT 0x8008\n#define GL_FUNC_ADD_EXT 0x8006\n#define GL_BLEND_EQUATION_EXT 0x8009\n#define GL_PACK_INVERT_MESA 0x8758\n#define GL_CONVOLUTION_HINT_SGIX 0x8316\n#define GL_VERTEX_DATA_HINT_PGI 0x1A22A\n#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B\n#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C\n#define GL_MAX_VERTEX_HINT_PGI 0x1A22D\n#define GL_COLOR3_BIT_PGI 0x00010000\n#define GL_COLOR4_BIT_PGI 0x00020000\n#define GL_EDGEFLAG_BIT_PGI 0x00040000\n#define GL_INDEX_BIT_PGI 0x00080000\n#define GL_MAT_AMBIENT_BIT_PGI 0x00100000\n#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000\n#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000\n#define GL_MAT_EMISSION_BIT_PGI 0x00800000\n#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000\n#define GL_MAT_SHININESS_BIT_PGI 0x02000000\n#define GL_MAT_SPECULAR_BIT_PGI 0x04000000\n#define GL_NORMAL_BIT_PGI 0x08000000\n#define GL_TEXCOORD1_BIT_PGI 0x10000000\n#define GL_TEXCOORD2_BIT_PGI 0x20000000\n#define GL_TEXCOORD3_BIT_PGI 0x40000000\n#define GL_TEXCOORD4_BIT_PGI 0x80000000\n#define GL_VERTEX23_BIT_PGI 0x00000004\n#define GL_VERTEX4_BIT_PGI 0x00000008\n#define GL_RGBA32UI_EXT 0x8D70\n#define GL_RGB32UI_EXT 0x8D71\n#define GL_ALPHA32UI_EXT 0x8D72\n#define GL_INTENSITY32UI_EXT 0x8D73\n#define GL_LUMINANCE32UI_EXT 0x8D74\n#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75\n#define GL_RGBA16UI_EXT 0x8D76\n#define GL_RGB16UI_EXT 0x8D77\n#define GL_ALPHA16UI_EXT 0x8D78\n#define GL_INTENSITY16UI_EXT 0x8D79\n#define GL_LUMINANCE16UI_EXT 0x8D7A\n#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B\n#define GL_RGBA8UI_EXT 0x8D7C\n#define GL_RGB8UI_EXT 0x8D7D\n#define GL_ALPHA8UI_EXT 0x8D7E\n#define GL_INTENSITY8UI_EXT 0x8D7F\n#define GL_LUMINANCE8UI_EXT 0x8D80\n#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81\n#define GL_RGBA32I_EXT 0x8D82\n#define GL_RGB32I_EXT 0x8D83\n#define GL_ALPHA32I_EXT 0x8D84\n#define GL_INTENSITY32I_EXT 0x8D85\n#define GL_LUMINANCE32I_EXT 0x8D86\n#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87\n#define GL_RGBA16I_EXT 0x8D88\n#define GL_RGB16I_EXT 0x8D89\n#define GL_ALPHA16I_EXT 0x8D8A\n#define GL_INTENSITY16I_EXT 0x8D8B\n#define GL_LUMINANCE16I_EXT 0x8D8C\n#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D\n#define GL_RGBA8I_EXT 0x8D8E\n#define GL_RGB8I_EXT 0x8D8F\n#define GL_ALPHA8I_EXT 0x8D90\n#define GL_INTENSITY8I_EXT 0x8D91\n#define GL_LUMINANCE8I_EXT 0x8D92\n#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93\n#define GL_RED_INTEGER_EXT 0x8D94\n#define GL_GREEN_INTEGER_EXT 0x8D95\n#define GL_BLUE_INTEGER_EXT 0x8D96\n#define GL_ALPHA_INTEGER_EXT 0x8D97\n#define GL_RGB_INTEGER_EXT 0x8D98\n#define GL_RGBA_INTEGER_EXT 0x8D99\n#define GL_BGR_INTEGER_EXT 0x8D9A\n#define GL_BGRA_INTEGER_EXT 0x8D9B\n#define GL_LUMINANCE_INTEGER_EXT 0x8D9C\n#define GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D\n#define GL_RGBA_INTEGER_MODE_EXT 0x8D9E\n#define GL_RGB_S3TC 0x83A0\n#define GL_RGB4_S3TC 0x83A1\n#define GL_RGBA_S3TC 0x83A2\n#define GL_RGBA4_S3TC 0x83A3\n#define GL_RGBA_DXT5_S3TC 0x83A4\n#define GL_RGBA4_DXT5_S3TC 0x83A5\n#define GL_SAMPLER_BUFFER_AMD 0x9001\n#define GL_INT_SAMPLER_BUFFER_AMD 0x9002\n#define GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003\n#define GL_TESSELLATION_MODE_AMD 0x9004\n#define GL_TESSELLATION_FACTOR_AMD 0x9005\n#define GL_DISCRETE_AMD 0x9006\n#define GL_CONTINUOUS_AMD 0x9007\n#define GL_INDEX_MATERIAL_EXT 0x81B8\n#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9\n#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA\n#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285\n#define GL_PARALLEL_ARRAYS_INTEL 0x83F4\n#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5\n#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6\n#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7\n#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8\n#define GL_MAX_DRAW_BUFFERS_ATI 0x8824\n#define GL_DRAW_BUFFER0_ATI 0x8825\n#define GL_DRAW_BUFFER1_ATI 0x8826\n#define GL_DRAW_BUFFER2_ATI 0x8827\n#define GL_DRAW_BUFFER3_ATI 0x8828\n#define GL_DRAW_BUFFER4_ATI 0x8829\n#define GL_DRAW_BUFFER5_ATI 0x882A\n#define GL_DRAW_BUFFER6_ATI 0x882B\n#define GL_DRAW_BUFFER7_ATI 0x882C\n#define GL_DRAW_BUFFER8_ATI 0x882D\n#define GL_DRAW_BUFFER9_ATI 0x882E\n#define GL_DRAW_BUFFER10_ATI 0x882F\n#define GL_DRAW_BUFFER11_ATI 0x8830\n#define GL_DRAW_BUFFER12_ATI 0x8831\n#define GL_DRAW_BUFFER13_ATI 0x8832\n#define GL_DRAW_BUFFER14_ATI 0x8833\n#define GL_DRAW_BUFFER15_ATI 0x8834\n#define GL_CMYK_EXT 0x800C\n#define GL_CMYKA_EXT 0x800D\n#define GL_PACK_CMYK_HINT_EXT 0x800E\n#define GL_UNPACK_CMYK_HINT_EXT 0x800F\n#define GL_PIXEL_TEX_GEN_SGIX 0x8139\n#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B\n#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0\n#define GL_INTERLACE_SGIX 0x8094\n#define GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0\n#define GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1\n#define GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2\n#define GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3\n#define GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4\n#define GL_RESCALE_NORMAL_EXT 0x803A\n#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB\n#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC\n#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED\n#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF\n#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C\n#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32\n#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45\n#define GL_TRANSFORM_FEEDBACK_NV 0x8E22\n#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV 0x8E23\n#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV 0x8E24\n#define GL_TRANSFORM_FEEDBACK_BINDING_NV 0x8E25\n#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C\n#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D\n#define GL_ASYNC_READ_PIXELS_SGIX 0x835E\n#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F\n#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360\n#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361\n#define GL_CONSTANT_COLOR_EXT 0x8001\n#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002\n#define GL_CONSTANT_ALPHA_EXT 0x8003\n#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004\n#define GL_BLEND_COLOR_EXT 0x8005\n#define GL_WARP_SIZE_NV 0x9339\n#define GL_WARPS_PER_SM_NV 0x933A\n#define GL_SM_COUNT_NV 0x933B\n#define GL_INCR_WRAP_EXT 0x8507\n#define GL_DECR_WRAP_EXT 0x8508\n#define GL_IUI_V2F_EXT 0x81AD\n#define GL_IUI_V3F_EXT 0x81AE\n#define GL_IUI_N3F_V2F_EXT 0x81AF\n#define GL_IUI_N3F_V3F_EXT 0x81B0\n#define GL_T2F_IUI_V2F_EXT 0x81B1\n#define GL_T2F_IUI_V3F_EXT 0x81B2\n#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3\n#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4\n#define GL_HISTOGRAM_EXT 0x8024\n#define GL_PROXY_HISTOGRAM_EXT 0x8025\n#define GL_HISTOGRAM_WIDTH_EXT 0x8026\n#define GL_HISTOGRAM_FORMAT_EXT 0x8027\n#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028\n#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029\n#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A\n#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B\n#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C\n#define GL_HISTOGRAM_SINK_EXT 0x802D\n#define GL_MINMAX_EXT 0x802E\n#define GL_MINMAX_FORMAT_EXT 0x802F\n#define GL_MINMAX_SINK_EXT 0x8030\n#define GL_TABLE_TOO_LARGE_EXT 0x8031\n#define GL_POINT_SIZE_MIN_SGIS 0x8126\n#define GL_POINT_SIZE_MAX_SGIS 0x8127\n#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128\n#define GL_DISTANCE_ATTENUATION_SGIS 0x8129\n#define GL_PROGRAM_MATRIX_EXT 0x8E2D\n#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E\n#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F\n#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F\n#define GL_VERTEX_PROGRAM_NV 0x8620\n#define GL_VERTEX_STATE_PROGRAM_NV 0x8621\n#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623\n#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624\n#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625\n#define GL_CURRENT_ATTRIB_NV 0x8626\n#define GL_PROGRAM_LENGTH_NV 0x8627\n#define GL_PROGRAM_STRING_NV 0x8628\n#define GL_MODELVIEW_PROJECTION_NV 0x8629\n#define GL_IDENTITY_NV 0x862A\n#define GL_INVERSE_NV 0x862B\n#define GL_TRANSPOSE_NV 0x862C\n#define GL_INVERSE_TRANSPOSE_NV 0x862D\n#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E\n#define GL_MAX_TRACK_MATRICES_NV 0x862F\n#define GL_MATRIX0_NV 0x8630\n#define GL_MATRIX1_NV 0x8631\n#define GL_MATRIX2_NV 0x8632\n#define GL_MATRIX3_NV 0x8633\n#define GL_MATRIX4_NV 0x8634\n#define GL_MATRIX5_NV 0x8635\n#define GL_MATRIX6_NV 0x8636\n#define GL_MATRIX7_NV 0x8637\n#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640\n#define GL_CURRENT_MATRIX_NV 0x8641\n#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642\n#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643\n#define GL_PROGRAM_PARAMETER_NV 0x8644\n#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645\n#define GL_PROGRAM_TARGET_NV 0x8646\n#define GL_PROGRAM_RESIDENT_NV 0x8647\n#define GL_TRACK_MATRIX_NV 0x8648\n#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649\n#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A\n#define GL_PROGRAM_ERROR_POSITION_NV 0x864B\n#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650\n#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651\n#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652\n#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653\n#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654\n#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655\n#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656\n#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657\n#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658\n#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659\n#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A\n#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B\n#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C\n#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D\n#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E\n#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F\n#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660\n#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661\n#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662\n#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663\n#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664\n#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665\n#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666\n#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667\n#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668\n#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669\n#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A\n#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B\n#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C\n#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D\n#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E\n#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F\n#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670\n#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671\n#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672\n#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673\n#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674\n#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675\n#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676\n#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677\n#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678\n#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679\n#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A\n#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B\n#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C\n#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D\n#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E\n#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F\n#define GL_VERTEX_SHADER_EXT 0x8780\n#define GL_VERTEX_SHADER_BINDING_EXT 0x8781\n#define GL_OP_INDEX_EXT 0x8782\n#define GL_OP_NEGATE_EXT 0x8783\n#define GL_OP_DOT3_EXT 0x8784\n#define GL_OP_DOT4_EXT 0x8785\n#define GL_OP_MUL_EXT 0x8786\n#define GL_OP_ADD_EXT 0x8787\n#define GL_OP_MADD_EXT 0x8788\n#define GL_OP_FRAC_EXT 0x8789\n#define GL_OP_MAX_EXT 0x878A\n#define GL_OP_MIN_EXT 0x878B\n#define GL_OP_SET_GE_EXT 0x878C\n#define GL_OP_SET_LT_EXT 0x878D\n#define GL_OP_CLAMP_EXT 0x878E\n#define GL_OP_FLOOR_EXT 0x878F\n#define GL_OP_ROUND_EXT 0x8790\n#define GL_OP_EXP_BASE_2_EXT 0x8791\n#define GL_OP_LOG_BASE_2_EXT 0x8792\n#define GL_OP_POWER_EXT 0x8793\n#define GL_OP_RECIP_EXT 0x8794\n#define GL_OP_RECIP_SQRT_EXT 0x8795\n#define GL_OP_SUB_EXT 0x8796\n#define GL_OP_CROSS_PRODUCT_EXT 0x8797\n#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798\n#define GL_OP_MOV_EXT 0x8799\n#define GL_OUTPUT_VERTEX_EXT 0x879A\n#define GL_OUTPUT_COLOR0_EXT 0x879B\n#define GL_OUTPUT_COLOR1_EXT 0x879C\n#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D\n#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E\n#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F\n#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0\n#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1\n#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2\n#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3\n#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4\n#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5\n#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6\n#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7\n#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8\n#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9\n#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA\n#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB\n#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC\n#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD\n#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE\n#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF\n#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0\n#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1\n#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2\n#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3\n#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4\n#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5\n#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6\n#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7\n#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8\n#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9\n#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA\n#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB\n#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC\n#define GL_OUTPUT_FOG_EXT 0x87BD\n#define GL_SCALAR_EXT 0x87BE\n#define GL_VECTOR_EXT 0x87BF\n#define GL_MATRIX_EXT 0x87C0\n#define GL_VARIANT_EXT 0x87C1\n#define GL_INVARIANT_EXT 0x87C2\n#define GL_LOCAL_CONSTANT_EXT 0x87C3\n#define GL_LOCAL_EXT 0x87C4\n#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5\n#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6\n#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7\n#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8\n#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9\n#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA\n#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB\n#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC\n#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD\n#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE\n#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF\n#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0\n#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1\n#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2\n#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3\n#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4\n#define GL_X_EXT 0x87D5\n#define GL_Y_EXT 0x87D6\n#define GL_Z_EXT 0x87D7\n#define GL_W_EXT 0x87D8\n#define GL_NEGATIVE_X_EXT 0x87D9\n#define GL_NEGATIVE_Y_EXT 0x87DA\n#define GL_NEGATIVE_Z_EXT 0x87DB\n#define GL_NEGATIVE_W_EXT 0x87DC\n#define GL_ZERO_EXT 0x87DD\n#define GL_ONE_EXT 0x87DE\n#define GL_NEGATIVE_ONE_EXT 0x87DF\n#define GL_NORMALIZED_RANGE_EXT 0x87E0\n#define GL_FULL_RANGE_EXT 0x87E1\n#define GL_CURRENT_VERTEX_EXT 0x87E2\n#define GL_MVP_MATRIX_EXT 0x87E3\n#define GL_VARIANT_VALUE_EXT 0x87E4\n#define GL_VARIANT_DATATYPE_EXT 0x87E5\n#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6\n#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7\n#define GL_VARIANT_ARRAY_EXT 0x87E8\n#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9\n#define GL_INVARIANT_VALUE_EXT 0x87EA\n#define GL_INVARIANT_DATATYPE_EXT 0x87EB\n#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC\n#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED\n#define GL_BLEND_DST_RGB_EXT 0x80C8\n#define GL_BLEND_SRC_RGB_EXT 0x80C9\n#define GL_BLEND_DST_ALPHA_EXT 0x80CA\n#define GL_BLEND_SRC_ALPHA_EXT 0x80CB\n#define GL_DRAW_PIXELS_APPLE 0x8A0A\n#define GL_FENCE_APPLE 0x8A0B\n#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3\n#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4\n#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5\n#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6\n#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450\n#define GL_FOG_COORDINATE_EXT 0x8451\n#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453\n#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454\n#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455\n#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456\n#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457\n#define GL_VERTEX_ARRAY_EXT 0x8074\n#define GL_NORMAL_ARRAY_EXT 0x8075\n#define GL_COLOR_ARRAY_EXT 0x8076\n#define GL_INDEX_ARRAY_EXT 0x8077\n#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078\n#define GL_EDGE_FLAG_ARRAY_EXT 0x8079\n#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A\n#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B\n#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C\n#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D\n#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E\n#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F\n#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080\n#define GL_COLOR_ARRAY_SIZE_EXT 0x8081\n#define GL_COLOR_ARRAY_TYPE_EXT 0x8082\n#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083\n#define GL_COLOR_ARRAY_COUNT_EXT 0x8084\n#define GL_INDEX_ARRAY_TYPE_EXT 0x8085\n#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086\n#define GL_INDEX_ARRAY_COUNT_EXT 0x8087\n#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088\n#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089\n#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A\n#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B\n#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C\n#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D\n#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E\n#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F\n#define GL_COLOR_ARRAY_POINTER_EXT 0x8090\n#define GL_INDEX_ARRAY_POINTER_EXT 0x8091\n#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092\n#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093\n#define GL_BLEND_EQUATION_RGB_EXT 0x8009\n#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D\n#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23\n#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24\n#define GL_YCRCB_SGIX 0x8318\n#define GL_YCRCBA_SGIX 0x8319\n#define GL_BGR_EXT 0x80E0\n#define GL_BGRA_EXT 0x80E1\n#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0\n#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1\n#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2\n#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3\n#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330\n#define GL_PIXEL_MAG_FILTER_EXT 0x8331\n#define GL_PIXEL_MIN_FILTER_EXT 0x8332\n#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333\n#define GL_CUBIC_EXT 0x8334\n#define GL_AVERAGE_EXT 0x8335\n#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336\n#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337\n#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338\n#define GL_FRAGMENT_SHADER_ATI 0x8920\n#define GL_REG_0_ATI 0x8921\n#define GL_REG_1_ATI 0x8922\n#define GL_REG_2_ATI 0x8923\n#define GL_REG_3_ATI 0x8924\n#define GL_REG_4_ATI 0x8925\n#define GL_REG_5_ATI 0x8926\n#define GL_REG_6_ATI 0x8927\n#define GL_REG_7_ATI 0x8928\n#define GL_REG_8_ATI 0x8929\n#define GL_REG_9_ATI 0x892A\n#define GL_REG_10_ATI 0x892B\n#define GL_REG_11_ATI 0x892C\n#define GL_REG_12_ATI 0x892D\n#define GL_REG_13_ATI 0x892E\n#define GL_REG_14_ATI 0x892F\n#define GL_REG_15_ATI 0x8930\n#define GL_REG_16_ATI 0x8931\n#define GL_REG_17_ATI 0x8932\n#define GL_REG_18_ATI 0x8933\n#define GL_REG_19_ATI 0x8934\n#define GL_REG_20_ATI 0x8935\n#define GL_REG_21_ATI 0x8936\n#define GL_REG_22_ATI 0x8937\n#define GL_REG_23_ATI 0x8938\n#define GL_REG_24_ATI 0x8939\n#define GL_REG_25_ATI 0x893A\n#define GL_REG_26_ATI 0x893B\n#define GL_REG_27_ATI 0x893C\n#define GL_REG_28_ATI 0x893D\n#define GL_REG_29_ATI 0x893E\n#define GL_REG_30_ATI 0x893F\n#define GL_REG_31_ATI 0x8940\n#define GL_CON_0_ATI 0x8941\n#define GL_CON_1_ATI 0x8942\n#define GL_CON_2_ATI 0x8943\n#define GL_CON_3_ATI 0x8944\n#define GL_CON_4_ATI 0x8945\n#define GL_CON_5_ATI 0x8946\n#define GL_CON_6_ATI 0x8947\n#define GL_CON_7_ATI 0x8948\n#define GL_CON_8_ATI 0x8949\n#define GL_CON_9_ATI 0x894A\n#define GL_CON_10_ATI 0x894B\n#define GL_CON_11_ATI 0x894C\n#define GL_CON_12_ATI 0x894D\n#define GL_CON_13_ATI 0x894E\n#define GL_CON_14_ATI 0x894F\n#define GL_CON_15_ATI 0x8950\n#define GL_CON_16_ATI 0x8951\n#define GL_CON_17_ATI 0x8952\n#define GL_CON_18_ATI 0x8953\n#define GL_CON_19_ATI 0x8954\n#define GL_CON_20_ATI 0x8955\n#define GL_CON_21_ATI 0x8956\n#define GL_CON_22_ATI 0x8957\n#define GL_CON_23_ATI 0x8958\n#define GL_CON_24_ATI 0x8959\n#define GL_CON_25_ATI 0x895A\n#define GL_CON_26_ATI 0x895B\n#define GL_CON_27_ATI 0x895C\n#define GL_CON_28_ATI 0x895D\n#define GL_CON_29_ATI 0x895E\n#define GL_CON_30_ATI 0x895F\n#define GL_CON_31_ATI 0x8960\n#define GL_MOV_ATI 0x8961\n#define GL_ADD_ATI 0x8963\n#define GL_MUL_ATI 0x8964\n#define GL_SUB_ATI 0x8965\n#define GL_DOT3_ATI 0x8966\n#define GL_DOT4_ATI 0x8967\n#define GL_MAD_ATI 0x8968\n#define GL_LERP_ATI 0x8969\n#define GL_CND_ATI 0x896A\n#define GL_CND0_ATI 0x896B\n#define GL_DOT2_ADD_ATI 0x896C\n#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D\n#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E\n#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F\n#define GL_NUM_PASSES_ATI 0x8970\n#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971\n#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972\n#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973\n#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974\n#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975\n#define GL_SWIZZLE_STR_ATI 0x8976\n#define GL_SWIZZLE_STQ_ATI 0x8977\n#define GL_SWIZZLE_STR_DR_ATI 0x8978\n#define GL_SWIZZLE_STQ_DQ_ATI 0x8979\n#define GL_SWIZZLE_STRQ_ATI 0x897A\n#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B\n#define GL_RED_BIT_ATI 0x00000001\n#define GL_GREEN_BIT_ATI 0x00000002\n#define GL_BLUE_BIT_ATI 0x00000004\n#define GL_2X_BIT_ATI 0x00000001\n#define GL_4X_BIT_ATI 0x00000002\n#define GL_8X_BIT_ATI 0x00000004\n#define GL_HALF_BIT_ATI 0x00000008\n#define GL_QUARTER_BIT_ATI 0x00000010\n#define GL_EIGHTH_BIT_ATI 0x00000020\n#define GL_SATURATE_BIT_ATI 0x00000040\n#define GL_COMP_BIT_ATI 0x00000002\n#define GL_NEGATE_BIT_ATI 0x00000004\n#define GL_BIAS_BIT_ATI 0x00000008\n#define GL_RESTART_SUN 0x0001\n#define GL_REPLACE_MIDDLE_SUN 0x0002\n#define GL_REPLACE_OLDEST_SUN 0x0003\n#define GL_TRIANGLE_LIST_SUN 0x81D7\n#define GL_REPLACEMENT_CODE_SUN 0x81D8\n#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0\n#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1\n#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2\n#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3\n#define GL_R1UI_V3F_SUN 0x85C4\n#define GL_R1UI_C4UB_V3F_SUN 0x85C5\n#define GL_R1UI_C3F_V3F_SUN 0x85C6\n#define GL_R1UI_N3F_V3F_SUN 0x85C7\n#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8\n#define GL_R1UI_T2F_V3F_SUN 0x85C9\n#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA\n#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB\n#define GL_DEPTH_STENCIL_EXT 0x84F9\n#define GL_UNSIGNED_INT_24_8_EXT 0x84FA\n#define GL_DEPTH24_STENCIL8_EXT 0x88F0\n#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1\n#define GL_MIRROR_CLAMP_EXT 0x8742\n#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743\n#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912\n#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534\n#define GL_HALF_APPLE 0x140B\n#define GL_RGBA_FLOAT32_APPLE 0x8814\n#define GL_RGB_FLOAT32_APPLE 0x8815\n#define GL_ALPHA_FLOAT32_APPLE 0x8816\n#define GL_INTENSITY_FLOAT32_APPLE 0x8817\n#define GL_LUMINANCE_FLOAT32_APPLE 0x8818\n#define GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819\n#define GL_RGBA_FLOAT16_APPLE 0x881A\n#define GL_RGB_FLOAT16_APPLE 0x881B\n#define GL_ALPHA_FLOAT16_APPLE 0x881C\n#define GL_INTENSITY_FLOAT16_APPLE 0x881D\n#define GL_LUMINANCE_FLOAT16_APPLE 0x881E\n#define GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F\n#define GL_COLOR_FLOAT_APPLE 0x8A0F\n#define GL_ASYNC_MARKER_SGIX 0x8329\n#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70\n#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71\n#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72\n#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73\n#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C\n#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000\n#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001\n#define GL_PERFQUERY_WAIT_INTEL 0x83FB\n#define GL_PERFQUERY_FLUSH_INTEL 0x83FA\n#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9\n#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0\n#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1\n#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2\n#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3\n#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4\n#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5\n#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8\n#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9\n#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA\n#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB\n#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC\n#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD\n#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE\n#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF\n#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500\n#define GL_INT8_NV 0x8FE0\n#define GL_INT8_VEC2_NV 0x8FE1\n#define GL_INT8_VEC3_NV 0x8FE2\n#define GL_INT8_VEC4_NV 0x8FE3\n#define GL_INT16_NV 0x8FE4\n#define GL_INT16_VEC2_NV 0x8FE5\n#define GL_INT16_VEC3_NV 0x8FE6\n#define GL_INT16_VEC4_NV 0x8FE7\n#define GL_INT64_VEC2_NV 0x8FE9\n#define GL_INT64_VEC3_NV 0x8FEA\n#define GL_INT64_VEC4_NV 0x8FEB\n#define GL_UNSIGNED_INT8_NV 0x8FEC\n#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED\n#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE\n#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF\n#define GL_UNSIGNED_INT16_NV 0x8FF0\n#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1\n#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2\n#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3\n#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5\n#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6\n#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7\n#define GL_FLOAT16_NV 0x8FF8\n#define GL_FLOAT16_VEC2_NV 0x8FF9\n#define GL_FLOAT16_VEC3_NV 0x8FFA\n#define GL_FLOAT16_VEC4_NV 0x8FFB\n#define GL_PARAMETER_BUFFER_ARB 0x80EE\n#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF\n#define GL_HALF_FLOAT_NV 0x140B\n#define GL_TANGENT_ARRAY_EXT 0x8439\n#define GL_BINORMAL_ARRAY_EXT 0x843A\n#define GL_CURRENT_TANGENT_EXT 0x843B\n#define GL_CURRENT_BINORMAL_EXT 0x843C\n#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E\n#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F\n#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440\n#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441\n#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442\n#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443\n#define GL_MAP1_TANGENT_EXT 0x8444\n#define GL_MAP2_TANGENT_EXT 0x8445\n#define GL_MAP1_BINORMAL_EXT 0x8446\n#define GL_MAP2_BINORMAL_EXT 0x8447\n#define GL_MIRROR_CLAMP_ATI 0x8742\n#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743\n#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262\n#define GL_TEXTURE_COMPARE_SGIX 0x819A\n#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B\n#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C\n#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D\n#define GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV 0x90D0\n#define GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV 0x90D1\n#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183\n#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8\n#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9\n#define GL_DEPTH_COMPONENT32F_NV 0x8DAB\n#define GL_DEPTH32F_STENCIL8_NV 0x8DAC\n#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD\n#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF\n#define GL_PIXEL_COUNTER_BITS_NV 0x8864\n#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865\n#define GL_PIXEL_COUNT_NV 0x8866\n#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867\n#define GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12\n#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13\n#define GL_BLEND_COLOR 0x8005\n#define GL_BLEND_EQUATION 0x8009\n#define GL_CONVOLUTION_1D 0x8010\n#define GL_CONVOLUTION_2D 0x8011\n#define GL_SEPARABLE_2D 0x8012\n#define GL_CONVOLUTION_BORDER_MODE 0x8013\n#define GL_CONVOLUTION_FILTER_SCALE 0x8014\n#define GL_CONVOLUTION_FILTER_BIAS 0x8015\n#define GL_REDUCE 0x8016\n#define GL_CONVOLUTION_FORMAT 0x8017\n#define GL_CONVOLUTION_WIDTH 0x8018\n#define GL_CONVOLUTION_HEIGHT 0x8019\n#define GL_MAX_CONVOLUTION_WIDTH 0x801A\n#define GL_MAX_CONVOLUTION_HEIGHT 0x801B\n#define GL_POST_CONVOLUTION_RED_SCALE 0x801C\n#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D\n#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E\n#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F\n#define GL_POST_CONVOLUTION_RED_BIAS 0x8020\n#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021\n#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022\n#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023\n#define GL_HISTOGRAM 0x8024\n#define GL_PROXY_HISTOGRAM 0x8025\n#define GL_HISTOGRAM_WIDTH 0x8026\n#define GL_HISTOGRAM_FORMAT 0x8027\n#define GL_HISTOGRAM_RED_SIZE 0x8028\n#define GL_HISTOGRAM_GREEN_SIZE 0x8029\n#define GL_HISTOGRAM_BLUE_SIZE 0x802A\n#define GL_HISTOGRAM_ALPHA_SIZE 0x802B\n#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C\n#define GL_HISTOGRAM_SINK 0x802D\n#define GL_MINMAX 0x802E\n#define GL_MINMAX_FORMAT 0x802F\n#define GL_MINMAX_SINK 0x8030\n#define GL_TABLE_TOO_LARGE 0x8031\n#define GL_COLOR_MATRIX 0x80B1\n#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2\n#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3\n#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4\n#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5\n#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6\n#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7\n#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8\n#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9\n#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA\n#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB\n#define GL_COLOR_TABLE 0x80D0\n#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1\n#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2\n#define GL_PROXY_COLOR_TABLE 0x80D3\n#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4\n#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5\n#define GL_COLOR_TABLE_SCALE 0x80D6\n#define GL_COLOR_TABLE_BIAS 0x80D7\n#define GL_COLOR_TABLE_FORMAT 0x80D8\n#define GL_COLOR_TABLE_WIDTH 0x80D9\n#define GL_COLOR_TABLE_RED_SIZE 0x80DA\n#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB\n#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC\n#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD\n#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE\n#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF\n#define GL_CONSTANT_BORDER 0x8151\n#define GL_REPLICATE_BORDER 0x8153\n#define GL_CONVOLUTION_BORDER_COLOR 0x8154\n#define GL_FACTOR_MIN_AMD 0x901C\n#define GL_FACTOR_MAX_AMD 0x901D\n#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48\n#define GL_DECODE_EXT 0x8A49\n#define GL_SKIP_DECODE_EXT 0x8A4A\n#define GL_VBO_FREE_MEMORY_ATI 0x87FB\n#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC\n#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD\n#define GL_ABGR_EXT 0x8000\n#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160\n#define GL_ALPHA_SNORM 0x9010\n#define GL_LUMINANCE_SNORM 0x9011\n#define GL_LUMINANCE_ALPHA_SNORM 0x9012\n#define GL_INTENSITY_SNORM 0x9013\n#define GL_ALPHA8_SNORM 0x9014\n#define GL_LUMINANCE8_SNORM 0x9015\n#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016\n#define GL_INTENSITY8_SNORM 0x9017\n#define GL_ALPHA16_SNORM 0x9018\n#define GL_LUMINANCE16_SNORM 0x9019\n#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A\n#define GL_INTENSITY16_SNORM 0x901B\n#define GL_RED_SNORM 0x8F90\n#define GL_RG_SNORM 0x8F91\n#define GL_RGB_SNORM 0x8F92\n#define GL_RGBA_SNORM 0x8F93\n#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369\n#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A\n#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B\n#define GL_MULTISAMPLE_ARB 0x809D\n#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E\n#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F\n#define GL_SAMPLE_COVERAGE_ARB 0x80A0\n#define GL_SAMPLE_BUFFERS_ARB 0x80A8\n#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA\n#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB\n#define GL_MULTISAMPLE_BIT_ARB 0x20000000\n#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F\n#define GL_PROGRAM_OBJECT_EXT 0x8B40\n#define GL_SHADER_OBJECT_EXT 0x8B48\n#define GL_BUFFER_OBJECT_EXT 0x9151\n#define GL_QUERY_OBJECT_EXT 0x9153\n#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154\n#define GL_SAMPLE_SHADING_ARB 0x8C36\n#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37\n#define GL_TEXTURE_MEMORY_LAYOUT_INTEL 0x83FF\n#define GL_LAYOUT_DEFAULT_INTEL 0\n#define GL_LAYOUT_LINEAR_INTEL 1\n#define GL_LAYOUT_LINEAR_CPU_CACHED_INTEL 2\n#define GL_422_EXT 0x80CC\n#define GL_422_REV_EXT 0x80CD\n#define GL_422_AVERAGE_EXT 0x80CE\n#define GL_422_REV_AVERAGE_EXT 0x80CF\n#define GL_COMPUTE_SHADER_BIT 0x00000020\n#define GL_CULL_VERTEX_IBM 103050\n#define GL_VERTEX_ARRAY_LIST_IBM 103070\n#define GL_NORMAL_ARRAY_LIST_IBM 103071\n#define GL_COLOR_ARRAY_LIST_IBM 103072\n#define GL_INDEX_ARRAY_LIST_IBM 103073\n#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074\n#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075\n#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076\n#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077\n#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080\n#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081\n#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082\n#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083\n#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084\n#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085\n#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086\n#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087\n#define GL_RGBA_FLOAT_MODE_ARB 0x8820\n#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A\n#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B\n#define GL_CLAMP_READ_COLOR_ARB 0x891C\n#define GL_FIXED_ONLY_ARB 0x891D\n#define GL_UNSIGNED_INT64_ARB 0x140F\n#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C\n#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D\n#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E\n#define GL_MIRRORED_REPEAT_ARB 0x8370\n#define GL_MAX_IMAGE_UNITS_EXT 0x8F38\n#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT 0x8F39\n#define GL_IMAGE_BINDING_NAME_EXT 0x8F3A\n#define GL_IMAGE_BINDING_LEVEL_EXT 0x8F3B\n#define GL_IMAGE_BINDING_LAYERED_EXT 0x8F3C\n#define GL_IMAGE_BINDING_LAYER_EXT 0x8F3D\n#define GL_IMAGE_BINDING_ACCESS_EXT 0x8F3E\n#define GL_IMAGE_1D_EXT 0x904C\n#define GL_IMAGE_2D_EXT 0x904D\n#define GL_IMAGE_3D_EXT 0x904E\n#define GL_IMAGE_2D_RECT_EXT 0x904F\n#define GL_IMAGE_CUBE_EXT 0x9050\n#define GL_IMAGE_BUFFER_EXT 0x9051\n#define GL_IMAGE_1D_ARRAY_EXT 0x9052\n#define GL_IMAGE_2D_ARRAY_EXT 0x9053\n#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054\n#define GL_IMAGE_2D_MULTISAMPLE_EXT 0x9055\n#define GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9056\n#define GL_INT_IMAGE_1D_EXT 0x9057\n#define GL_INT_IMAGE_2D_EXT 0x9058\n#define GL_INT_IMAGE_3D_EXT 0x9059\n#define GL_INT_IMAGE_2D_RECT_EXT 0x905A\n#define GL_INT_IMAGE_CUBE_EXT 0x905B\n#define GL_INT_IMAGE_BUFFER_EXT 0x905C\n#define GL_INT_IMAGE_1D_ARRAY_EXT 0x905D\n#define GL_INT_IMAGE_2D_ARRAY_EXT 0x905E\n#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F\n#define GL_INT_IMAGE_2D_MULTISAMPLE_EXT 0x9060\n#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9061\n#define GL_UNSIGNED_INT_IMAGE_1D_EXT 0x9062\n#define GL_UNSIGNED_INT_IMAGE_2D_EXT 0x9063\n#define GL_UNSIGNED_INT_IMAGE_3D_EXT 0x9064\n#define GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT 0x9065\n#define GL_UNSIGNED_INT_IMAGE_CUBE_EXT 0x9066\n#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067\n#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT 0x9068\n#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT 0x9069\n#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A\n#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT 0x906B\n#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x906C\n#define GL_MAX_IMAGE_SAMPLES_EXT 0x906D\n#define GL_IMAGE_BINDING_FORMAT_EXT 0x906E\n#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT 0x00000001\n#define GL_ELEMENT_ARRAY_BARRIER_BIT_EXT 0x00000002\n#define GL_UNIFORM_BARRIER_BIT_EXT 0x00000004\n#define GL_TEXTURE_FETCH_BARRIER_BIT_EXT 0x00000008\n#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT 0x00000020\n#define GL_COMMAND_BARRIER_BIT_EXT 0x00000040\n#define GL_PIXEL_BUFFER_BARRIER_BIT_EXT 0x00000080\n#define GL_TEXTURE_UPDATE_BARRIER_BIT_EXT 0x00000100\n#define GL_BUFFER_UPDATE_BARRIER_BIT_EXT 0x00000200\n#define GL_FRAMEBUFFER_BARRIER_BIT_EXT 0x00000400\n#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT 0x00000800\n#define GL_ATOMIC_COUNTER_BARRIER_BIT_EXT 0x00001000\n#define GL_ALL_BARRIER_BITS_EXT 0xFFFFFFFF\n#define GL_PER_STAGE_CONSTANTS_NV 0x8535\n#define GL_IR_INSTRUMENT1_SGIX 0x817F\n#define GL_RGB9_E5_EXT 0x8C3D\n#define GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E\n#define GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F\n#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E\n#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F\n#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890\n#define GL_DEPTH_BOUNDS_EXT 0x8891\n#define GL_IMAGE_SCALE_X_HP 0x8155\n#define GL_IMAGE_SCALE_Y_HP 0x8156\n#define GL_IMAGE_TRANSLATE_X_HP 0x8157\n#define GL_IMAGE_TRANSLATE_Y_HP 0x8158\n#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159\n#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A\n#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B\n#define GL_IMAGE_MAG_FILTER_HP 0x815C\n#define GL_IMAGE_MIN_FILTER_HP 0x815D\n#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E\n#define GL_CUBIC_HP 0x815F\n#define GL_AVERAGE_HP 0x8160\n#define GL_IMAGE_TRANSFORM_2D_HP 0x8161\n#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162\n#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163\n#define GL_VIDEO_BUFFER_NV 0x9020\n#define GL_VIDEO_BUFFER_BINDING_NV 0x9021\n#define GL_FIELD_UPPER_NV 0x9022\n#define GL_FIELD_LOWER_NV 0x9023\n#define GL_NUM_VIDEO_CAPTURE_STREAMS_NV 0x9024\n#define GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV 0x9025\n#define GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV 0x9026\n#define GL_LAST_VIDEO_CAPTURE_STATUS_NV 0x9027\n#define GL_VIDEO_BUFFER_PITCH_NV 0x9028\n#define GL_VIDEO_COLOR_CONVERSION_MATRIX_NV 0x9029\n#define GL_VIDEO_COLOR_CONVERSION_MAX_NV 0x902A\n#define GL_VIDEO_COLOR_CONVERSION_MIN_NV 0x902B\n#define GL_VIDEO_COLOR_CONVERSION_OFFSET_NV 0x902C\n#define GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV 0x902D\n#define GL_PARTIAL_SUCCESS_NV 0x902E\n#define GL_SUCCESS_NV 0x902F\n#define GL_FAILURE_NV 0x9030\n#define GL_YCBYCR8_422_NV 0x9031\n#define GL_YCBAYCR8A_4224_NV 0x9032\n#define GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV 0x9033\n#define GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV 0x9034\n#define GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV 0x9035\n#define GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV 0x9036\n#define GL_Z4Y12Z4CB12Z4CR12_444_NV 0x9037\n#define GL_VIDEO_CAPTURE_FRAME_WIDTH_NV 0x9038\n#define GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV 0x9039\n#define GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV 0x903A\n#define GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV 0x903B\n#define GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV 0x903C\n#define GL_MATRIX_PALETTE_ARB 0x8840\n#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841\n#define GL_MAX_PALETTE_MATRICES_ARB 0x8842\n#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843\n#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844\n#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845\n#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846\n#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847\n#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848\n#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849\n#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF\n#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032\n#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033\n#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034\n#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035\n#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036\n#define GL_COMPRESSED_ALPHA_ARB 0x84E9\n#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA\n#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB\n#define GL_COMPRESSED_INTENSITY_ARB 0x84EC\n#define GL_COMPRESSED_RGB_ARB 0x84ED\n#define GL_COMPRESSED_RGBA_ARB 0x84EE\n#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF\n#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0\n#define GL_TEXTURE_COMPRESSED_ARB 0x86A1\n#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2\n#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3\n#define GL_AUX_DEPTH_STENCIL_APPLE 0x8A14\n#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9\n#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA\n#define GL_DOUBLE_VEC2_EXT 0x8FFC\n#define GL_DOUBLE_VEC3_EXT 0x8FFD\n#define GL_DOUBLE_VEC4_EXT 0x8FFE\n#define GL_DOUBLE_MAT2_EXT 0x8F46\n#define GL_DOUBLE_MAT3_EXT 0x8F47\n#define GL_DOUBLE_MAT4_EXT 0x8F48\n#define GL_DOUBLE_MAT2x3_EXT 0x8F49\n#define GL_DOUBLE_MAT2x4_EXT 0x8F4A\n#define GL_DOUBLE_MAT3x2_EXT 0x8F4B\n#define GL_DOUBLE_MAT3x4_EXT 0x8F4C\n#define GL_DOUBLE_MAT4x2_EXT 0x8F4D\n#define GL_DOUBLE_MAT4x3_EXT 0x8F4E\n#define GL_DEPTH_COMPONENT16_ARB 0x81A5\n#define GL_DEPTH_COMPONENT24_ARB 0x81A6\n#define GL_DEPTH_COMPONENT32_ARB 0x81A7\n#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A\n#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B\n#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010\n#define GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7\n#define GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8\n#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC\n#define GL_STORAGE_PRIVATE_APPLE 0x85BD\n#define GL_STORAGE_CACHED_APPLE 0x85BE\n#define GL_STORAGE_SHARED_APPLE 0x85BF\n#define GL_COPY_READ_BUFFER_BINDING 0x8F36\n#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37\n#define GL_BUFFER_OBJECT_APPLE 0x85B3\n#define GL_RELEASED_APPLE 0x8A19\n#define GL_VOLATILE_APPLE 0x8A1A\n#define GL_RETAINED_APPLE 0x8A1B\n#define GL_UNDEFINED_APPLE 0x8A1C\n#define GL_PURGEABLE_APPLE 0x8A1D\n#define GL_QUERY_COUNTER_BITS_ARB 0x8864\n#define GL_CURRENT_QUERY_ARB 0x8865\n#define GL_QUERY_RESULT_ARB 0x8866\n#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867\n#define GL_SAMPLES_PASSED_ARB 0x8914\n#define GL_RED_MIN_CLAMP_INGR 0x8560\n#define GL_GREEN_MIN_CLAMP_INGR 0x8561\n#define GL_BLUE_MIN_CLAMP_INGR 0x8562\n#define GL_ALPHA_MIN_CLAMP_INGR 0x8563\n#define GL_RED_MAX_CLAMP_INGR 0x8564\n#define GL_GREEN_MAX_CLAMP_INGR 0x8565\n#define GL_BLUE_MAX_CLAMP_INGR 0x8566\n#define GL_ALPHA_MAX_CLAMP_INGR 0x8567\n#define GL_COLOR_TABLE_SGI 0x80D0\n#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1\n#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2\n#define GL_PROXY_COLOR_TABLE_SGI 0x80D3\n#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4\n#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5\n#define GL_COLOR_TABLE_SCALE_SGI 0x80D6\n#define GL_COLOR_TABLE_BIAS_SGI 0x80D7\n#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8\n#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9\n#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA\n#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB\n#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC\n#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD\n#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE\n#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF\n#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009\n#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A\n#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B\n#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C\n#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D\n#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E\n#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F\n#define GL_SCALEBIAS_HINT_SGIX 0x8322\n#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD\n#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0\n#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1\n#define GL_SAMPLER_BUFFER_EXT 0x8DC2\n#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3\n#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4\n#define GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5\n#define GL_UNSIGNED_INT_VEC2_EXT 0x8DC6\n#define GL_UNSIGNED_INT_VEC3_EXT 0x8DC7\n#define GL_UNSIGNED_INT_VEC4_EXT 0x8DC8\n#define GL_INT_SAMPLER_1D_EXT 0x8DC9\n#define GL_INT_SAMPLER_2D_EXT 0x8DCA\n#define GL_INT_SAMPLER_3D_EXT 0x8DCB\n#define GL_INT_SAMPLER_CUBE_EXT 0x8DCC\n#define GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD\n#define GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE\n#define GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF\n#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0\n#define GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1\n#define GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2\n#define GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3\n#define GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4\n#define GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5\n#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6\n#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7\n#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8\n#define GL_MIN_PROGRAM_TEXEL_OFFSET_EXT 0x8904\n#define GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905\n#define GL_GEOMETRY_PROGRAM_NV 0x8C26\n#define GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27\n#define GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28\n#define GL_SCALED_RESOLVE_FASTEST_EXT 0x90BA\n#define GL_SCALED_RESOLVE_NICEST_EXT 0x90BB\n#define GL_MAX_DEBUG_MESSAGE_LENGTH_AMD 0x9143\n#define GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144\n#define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145\n#define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146\n#define GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147\n#define GL_DEBUG_SEVERITY_LOW_AMD 0x9148\n#define GL_DEBUG_CATEGORY_API_ERROR_AMD 0x9149\n#define GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD 0x914A\n#define GL_DEBUG_CATEGORY_DEPRECATION_AMD 0x914B\n#define GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD 0x914C\n#define GL_DEBUG_CATEGORY_PERFORMANCE_AMD 0x914D\n#define GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD 0x914E\n#define GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914F\n#define GL_DEBUG_CATEGORY_OTHER_AMD 0x9150\n#define GL_CLAMP_TO_BORDER_ARB 0x812D\n#define GL_TEXTURE0_ARB 0x84C0\n#define GL_TEXTURE1_ARB 0x84C1\n#define GL_TEXTURE2_ARB 0x84C2\n#define GL_TEXTURE3_ARB 0x84C3\n#define GL_TEXTURE4_ARB 0x84C4\n#define GL_TEXTURE5_ARB 0x84C5\n#define GL_TEXTURE6_ARB 0x84C6\n#define GL_TEXTURE7_ARB 0x84C7\n#define GL_TEXTURE8_ARB 0x84C8\n#define GL_TEXTURE9_ARB 0x84C9\n#define GL_TEXTURE10_ARB 0x84CA\n#define GL_TEXTURE11_ARB 0x84CB\n#define GL_TEXTURE12_ARB 0x84CC\n#define GL_TEXTURE13_ARB 0x84CD\n#define GL_TEXTURE14_ARB 0x84CE\n#define GL_TEXTURE15_ARB 0x84CF\n#define GL_TEXTURE16_ARB 0x84D0\n#define GL_TEXTURE17_ARB 0x84D1\n#define GL_TEXTURE18_ARB 0x84D2\n#define GL_TEXTURE19_ARB 0x84D3\n#define GL_TEXTURE20_ARB 0x84D4\n#define GL_TEXTURE21_ARB 0x84D5\n#define GL_TEXTURE22_ARB 0x84D6\n#define GL_TEXTURE23_ARB 0x84D7\n#define GL_TEXTURE24_ARB 0x84D8\n#define GL_TEXTURE25_ARB 0x84D9\n#define GL_TEXTURE26_ARB 0x84DA\n#define GL_TEXTURE27_ARB 0x84DB\n#define GL_TEXTURE28_ARB 0x84DC\n#define GL_TEXTURE29_ARB 0x84DD\n#define GL_TEXTURE30_ARB 0x84DE\n#define GL_TEXTURE31_ARB 0x84DF\n#define GL_ACTIVE_TEXTURE_ARB 0x84E0\n#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1\n#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2\n#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001\n#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002\n#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194\n#define GL_TEXTURE_DEFORMATION_SGIX 0x8195\n#define GL_DEFORMATIONS_MASK_SGIX 0x8196\n#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197\n#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT 0x8E4C\n#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D\n#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E\n#define GL_PROVOKING_VERTEX_EXT 0x8E4F\n#define GL_POINT_SIZE_MIN_ARB 0x8126\n#define GL_POINT_SIZE_MAX_ARB 0x8127\n#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128\n#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129\n#define GL_OCCLUSION_TEST_HP 0x8165\n#define GL_OCCLUSION_TEST_RESULT_HP 0x8166\n#define GL_FRAMEZOOM_SGIX 0x818B\n#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C\n#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D\n#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E\n#define GL_TRANSFORM_FEEDBACK_BUFFER_EXT 0x8C8E\n#define GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT 0x8C84\n#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT 0x8C85\n#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT 0x8C8F\n#define GL_INTERLEAVED_ATTRIBS_EXT 0x8C8C\n#define GL_SEPARATE_ATTRIBS_EXT 0x8C8D\n#define GL_PRIMITIVES_GENERATED_EXT 0x8C87\n#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT 0x8C88\n#define GL_RASTERIZER_DISCARD_EXT 0x8C89\n#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B\n#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80\n#define GL_TRANSFORM_FEEDBACK_VARYINGS_EXT 0x8C83\n#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT 0x8C7F\n#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT 0x8C76\n#define GL_MULTISAMPLE_3DFX 0x86B2\n#define GL_SAMPLE_BUFFERS_3DFX 0x86B3\n#define GL_SAMPLES_3DFX 0x86B4\n#define GL_MULTISAMPLE_BIT_3DFX 0x20000000\n#define GL_DOT3_RGB_ARB 0x86AE\n#define GL_DOT3_RGBA_ARB 0x86AF\n#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904\n#define GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905\n#define GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906\n#define GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907\n#define GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908\n#define GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909\n#define GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5\n#define GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6\n#define GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV 0x8E5A\n#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5B\n#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5C\n#define GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV 0x8E5D\n#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5E\n#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5F\n#define GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV 0x8F44\n#define GL_MAX_PROGRAM_SUBROUTINE_NUM_NV 0x8F45\n#define GL_FLOAT_R_NV 0x8880\n#define GL_FLOAT_RG_NV 0x8881\n#define GL_FLOAT_RGB_NV 0x8882\n#define GL_FLOAT_RGBA_NV 0x8883\n#define GL_FLOAT_R16_NV 0x8884\n#define GL_FLOAT_R32_NV 0x8885\n#define GL_FLOAT_RG16_NV 0x8886\n#define GL_FLOAT_RG32_NV 0x8887\n#define GL_FLOAT_RGB16_NV 0x8888\n#define GL_FLOAT_RGB32_NV 0x8889\n#define GL_FLOAT_RGBA16_NV 0x888A\n#define GL_FLOAT_RGBA32_NV 0x888B\n#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C\n#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D\n#define GL_FLOAT_RGBA_MODE_NV 0x888E\n#define GL_CLAMP_TO_EDGE_SGIS 0x812F\n#define GL_SLICE_ACCUM_SUN 0x85CC\n#define GL_LINES_ADJACENCY_ARB 0x000A\n#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B\n#define GL_TRIANGLES_ADJACENCY_ARB 0x000C\n#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D\n#define GL_PROGRAM_POINT_SIZE_ARB 0x8642\n#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29\n#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7\n#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8\n#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9\n#define GL_GEOMETRY_SHADER_ARB 0x8DD9\n#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA\n#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB\n#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC\n#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD\n#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE\n#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF\n#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0\n#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1\n#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8\n#define GL_SINGLE_COLOR_EXT 0x81F9\n#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA\n#define GL_DEPTH_CLAMP_NEAR_AMD 0x901E\n#define GL_DEPTH_CLAMP_FAR_AMD 0x901F\n#define GL_WRAP_BORDER_SUN 0x81D4\n#define GL_SPRITE_SGIX 0x8148\n#define GL_SPRITE_MODE_SGIX 0x8149\n#define GL_SPRITE_AXIS_SGIX 0x814A\n#define GL_SPRITE_TRANSLATION_SGIX 0x814B\n#define GL_SPRITE_AXIAL_SGIX 0x814C\n#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D\n#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E\n#define GL_OCCLUSION_QUERY_EVENT_MASK_AMD 0x874F\n#define GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD 0x00000001\n#define GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD 0x00000002\n#define GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD 0x00000004\n#define GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD 0x00000008\n#define GL_QUERY_ALL_EVENT_BITS_AMD 0xFFFFFFFF\n#define GL_MULTISAMPLE_SGIS 0x809D\n#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E\n#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F\n#define GL_SAMPLE_MASK_SGIS 0x80A0\n#define GL_1PASS_SGIS 0x80A1\n#define GL_2PASS_0_SGIS 0x80A2\n#define GL_2PASS_1_SGIS 0x80A3\n#define GL_4PASS_0_SGIS 0x80A4\n#define GL_4PASS_1_SGIS 0x80A5\n#define GL_4PASS_2_SGIS 0x80A6\n#define GL_4PASS_3_SGIS 0x80A7\n#define GL_SAMPLE_BUFFERS_SGIS 0x80A8\n#define GL_SAMPLES_SGIS 0x80A9\n#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA\n#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB\n#define GL_SAMPLE_PATTERN_SGIS 0x80AC\n#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506\n#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8\n#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6\n#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0\n#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3\n#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4\n#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5\n#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6\n#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7\n#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9\n#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA\n#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB\n#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC\n#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD\n#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF\n#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0\n#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1\n#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2\n#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3\n#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4\n#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5\n#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6\n#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7\n#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8\n#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9\n#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA\n#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB\n#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC\n#define GL_COLOR_ATTACHMENT13_EXT 0x8CED\n#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE\n#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF\n#define GL_DEPTH_ATTACHMENT_EXT 0x8D00\n#define GL_STENCIL_ATTACHMENT_EXT 0x8D20\n#define GL_FRAMEBUFFER_EXT 0x8D40\n#define GL_RENDERBUFFER_EXT 0x8D41\n#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42\n#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43\n#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44\n#define GL_STENCIL_INDEX1_EXT 0x8D46\n#define GL_STENCIL_INDEX4_EXT 0x8D47\n#define GL_STENCIL_INDEX8_EXT 0x8D48\n#define GL_STENCIL_INDEX16_EXT 0x8D49\n#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50\n#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51\n#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52\n#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53\n#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54\n#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55\n#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D\n#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E\n#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F\n#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521\n#define GL_STORAGE_CLIENT_APPLE 0x85B4\n#define GL_QUERY_BUFFER_AMD 0x9192\n#define GL_QUERY_BUFFER_BINDING_AMD 0x9193\n#define GL_QUERY_RESULT_NO_WAIT_AMD 0x9194\n#define GL_REGISTER_COMBINERS_NV 0x8522\n#define GL_VARIABLE_A_NV 0x8523\n#define GL_VARIABLE_B_NV 0x8524\n#define GL_VARIABLE_C_NV 0x8525\n#define GL_VARIABLE_D_NV 0x8526\n#define GL_VARIABLE_E_NV 0x8527\n#define GL_VARIABLE_F_NV 0x8528\n#define GL_VARIABLE_G_NV 0x8529\n#define GL_CONSTANT_COLOR0_NV 0x852A\n#define GL_CONSTANT_COLOR1_NV 0x852B\n#define GL_PRIMARY_COLOR_NV 0x852C\n#define GL_SECONDARY_COLOR_NV 0x852D\n#define GL_SPARE0_NV 0x852E\n#define GL_SPARE1_NV 0x852F\n#define GL_DISCARD_NV 0x8530\n#define GL_E_TIMES_F_NV 0x8531\n#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532\n#define GL_UNSIGNED_IDENTITY_NV 0x8536\n#define GL_UNSIGNED_INVERT_NV 0x8537\n#define GL_EXPAND_NORMAL_NV 0x8538\n#define GL_EXPAND_NEGATE_NV 0x8539\n#define GL_HALF_BIAS_NORMAL_NV 0x853A\n#define GL_HALF_BIAS_NEGATE_NV 0x853B\n#define GL_SIGNED_IDENTITY_NV 0x853C\n#define GL_SIGNED_NEGATE_NV 0x853D\n#define GL_SCALE_BY_TWO_NV 0x853E\n#define GL_SCALE_BY_FOUR_NV 0x853F\n#define GL_SCALE_BY_ONE_HALF_NV 0x8540\n#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541\n#define GL_COMBINER_INPUT_NV 0x8542\n#define GL_COMBINER_MAPPING_NV 0x8543\n#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544\n#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545\n#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546\n#define GL_COMBINER_MUX_SUM_NV 0x8547\n#define GL_COMBINER_SCALE_NV 0x8548\n#define GL_COMBINER_BIAS_NV 0x8549\n#define GL_COMBINER_AB_OUTPUT_NV 0x854A\n#define GL_COMBINER_CD_OUTPUT_NV 0x854B\n#define GL_COMBINER_SUM_OUTPUT_NV 0x854C\n#define GL_MAX_GENERAL_COMBINERS_NV 0x854D\n#define GL_NUM_GENERAL_COMBINERS_NV 0x854E\n#define GL_COLOR_SUM_CLAMP_NV 0x854F\n#define GL_COMBINER0_NV 0x8550\n#define GL_COMBINER1_NV 0x8551\n#define GL_COMBINER2_NV 0x8552\n#define GL_COMBINER3_NV 0x8553\n#define GL_COMBINER4_NV 0x8554\n#define GL_COMBINER5_NV 0x8555\n#define GL_COMBINER6_NV 0x8556\n#define GL_COMBINER7_NV 0x8557\n#define GL_FOG 0x0B60\n#define GL_MAX_DRAW_BUFFERS_ARB 0x8824\n#define GL_DRAW_BUFFER0_ARB 0x8825\n#define GL_DRAW_BUFFER1_ARB 0x8826\n#define GL_DRAW_BUFFER2_ARB 0x8827\n#define GL_DRAW_BUFFER3_ARB 0x8828\n#define GL_DRAW_BUFFER4_ARB 0x8829\n#define GL_DRAW_BUFFER5_ARB 0x882A\n#define GL_DRAW_BUFFER6_ARB 0x882B\n#define GL_DRAW_BUFFER7_ARB 0x882C\n#define GL_DRAW_BUFFER8_ARB 0x882D\n#define GL_DRAW_BUFFER9_ARB 0x882E\n#define GL_DRAW_BUFFER10_ARB 0x882F\n#define GL_DRAW_BUFFER11_ARB 0x8830\n#define GL_DRAW_BUFFER12_ARB 0x8831\n#define GL_DRAW_BUFFER13_ARB 0x8832\n#define GL_DRAW_BUFFER14_ARB 0x8833\n#define GL_DRAW_BUFFER15_ARB 0x8834\n#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242\n#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243\n#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244\n#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245\n#define GL_DEBUG_SOURCE_API_ARB 0x8246\n#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247\n#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248\n#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249\n#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A\n#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B\n#define GL_DEBUG_TYPE_ERROR_ARB 0x824C\n#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D\n#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E\n#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F\n#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250\n#define GL_DEBUG_TYPE_OTHER_ARB 0x8251\n#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143\n#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144\n#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145\n#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146\n#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147\n#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148\n#define GL_COLOR_MATRIX_SGI 0x80B1\n#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2\n#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3\n#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4\n#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5\n#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6\n#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7\n#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8\n#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9\n#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA\n#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB\n#define GL_CULL_VERTEX_EXT 0x81AA\n#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB\n#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC\n#define GL_SRGB_EXT 0x8C40\n#define GL_SRGB8_EXT 0x8C41\n#define GL_SRGB_ALPHA_EXT 0x8C42\n#define GL_SRGB8_ALPHA8_EXT 0x8C43\n#define GL_SLUMINANCE_ALPHA_EXT 0x8C44\n#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45\n#define GL_SLUMINANCE_EXT 0x8C46\n#define GL_SLUMINANCE8_EXT 0x8C47\n#define GL_COMPRESSED_SRGB_EXT 0x8C48\n#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49\n#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A\n#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B\n#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C\n#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D\n#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E\n#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F\n#define GL_PACK_ROW_BYTES_APPLE 0x8A15\n#define GL_UNPACK_ROW_BYTES_APPLE 0x8A16\n#define GL_NORMAL_MAP_NV 0x8511\n#define GL_REFLECTION_MAP_NV 0x8512\n#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5\n#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0\n#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1\n#define GL_YCRCB_422_SGIX 0x81BB\n#define GL_YCRCB_444_SGIX 0x81BC\n#define GL_TEXTURE_RED_TYPE_ARB 0x8C10\n#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11\n#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12\n#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13\n#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14\n#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15\n#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16\n#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17\n#define GL_RGBA32F_ARB 0x8814\n#define GL_RGB32F_ARB 0x8815\n#define GL_ALPHA32F_ARB 0x8816\n#define GL_INTENSITY32F_ARB 0x8817\n#define GL_LUMINANCE32F_ARB 0x8818\n#define GL_LUMINANCE_ALPHA32F_ARB 0x8819\n#define GL_RGBA16F_ARB 0x881A\n#define GL_RGB16F_ARB 0x881B\n#define GL_ALPHA16F_ARB 0x881C\n#define GL_INTENSITY16F_ARB 0x881D\n#define GL_LUMINANCE16F_ARB 0x881E\n#define GL_LUMINANCE_ALPHA16F_ARB 0x881F\n#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095\n#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096\n#define GL_LINEAR_DETAIL_SGIS 0x8097\n#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098\n#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099\n#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A\n#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B\n#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C\n#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A\n#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B\n#define GL_RGBA_FLOAT32_ATI 0x8814\n#define GL_RGB_FLOAT32_ATI 0x8815\n#define GL_ALPHA_FLOAT32_ATI 0x8816\n#define GL_INTENSITY_FLOAT32_ATI 0x8817\n#define GL_LUMINANCE_FLOAT32_ATI 0x8818\n#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819\n#define GL_RGBA_FLOAT16_ATI 0x881A\n#define GL_RGB_FLOAT16_ATI 0x881B\n#define GL_ALPHA_FLOAT16_ATI 0x881C\n#define GL_INTENSITY_FLOAT16_ATI 0x881D\n#define GL_LUMINANCE_FLOAT16_ATI 0x881E\n#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F\n#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E\n#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F\n#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F\n#define GL_SHADER_INCLUDE_ARB 0x8DAE\n#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9\n#define GL_NAMED_STRING_TYPE_ARB 0x8DEA\n#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2\n#define GL_PHONG_WIN 0x80EA\n#define GL_PHONG_HINT_WIN 0x80EB\n#define GL_PATH_FORMAT_SVG_NV 0x9070\n#define GL_PATH_FORMAT_PS_NV 0x9071\n#define GL_STANDARD_FONT_NAME_NV 0x9072\n#define GL_SYSTEM_FONT_NAME_NV 0x9073\n#define GL_FILE_NAME_NV 0x9074\n#define GL_PATH_STROKE_WIDTH_NV 0x9075\n#define GL_PATH_END_CAPS_NV 0x9076\n#define GL_PATH_INITIAL_END_CAP_NV 0x9077\n#define GL_PATH_TERMINAL_END_CAP_NV 0x9078\n#define GL_PATH_JOIN_STYLE_NV 0x9079\n#define GL_PATH_MITER_LIMIT_NV 0x907A\n#define GL_PATH_DASH_CAPS_NV 0x907B\n#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C\n#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D\n#define GL_PATH_DASH_OFFSET_NV 0x907E\n#define GL_PATH_CLIENT_LENGTH_NV 0x907F\n#define GL_PATH_FILL_MODE_NV 0x9080\n#define GL_PATH_FILL_MASK_NV 0x9081\n#define GL_PATH_FILL_COVER_MODE_NV 0x9082\n#define GL_PATH_STROKE_COVER_MODE_NV 0x9083\n#define GL_PATH_STROKE_MASK_NV 0x9084\n#define GL_COUNT_UP_NV 0x9088\n#define GL_COUNT_DOWN_NV 0x9089\n#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A\n#define GL_CONVEX_HULL_NV 0x908B\n#define GL_BOUNDING_BOX_NV 0x908D\n#define GL_TRANSLATE_X_NV 0x908E\n#define GL_TRANSLATE_Y_NV 0x908F\n#define GL_TRANSLATE_2D_NV 0x9090\n#define GL_TRANSLATE_3D_NV 0x9091\n#define GL_AFFINE_2D_NV 0x9092\n#define GL_AFFINE_3D_NV 0x9094\n#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096\n#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098\n#define GL_UTF8_NV 0x909A\n#define GL_UTF16_NV 0x909B\n#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C\n#define GL_PATH_COMMAND_COUNT_NV 0x909D\n#define GL_PATH_COORD_COUNT_NV 0x909E\n#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F\n#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0\n#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1\n#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2\n#define GL_SQUARE_NV 0x90A3\n#define GL_ROUND_NV 0x90A4\n#define GL_TRIANGULAR_NV 0x90A5\n#define GL_BEVEL_NV 0x90A6\n#define GL_MITER_REVERT_NV 0x90A7\n#define GL_MITER_TRUNCATE_NV 0x90A8\n#define GL_SKIP_MISSING_GLYPH_NV 0x90A9\n#define GL_USE_MISSING_GLYPH_NV 0x90AA\n#define GL_PATH_ERROR_POSITION_NV 0x90AB\n#define GL_PATH_FOG_GEN_MODE_NV 0x90AC\n#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD\n#define GL_ADJACENT_PAIRS_NV 0x90AE\n#define GL_FIRST_TO_REST_NV 0x90AF\n#define GL_PATH_GEN_MODE_NV 0x90B0\n#define GL_PATH_GEN_COEFF_NV 0x90B1\n#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2\n#define GL_PATH_GEN_COMPONENTS_NV 0x90B3\n#define GL_PATH_STENCIL_FUNC_NV 0x90B7\n#define GL_PATH_STENCIL_REF_NV 0x90B8\n#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9\n#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD\n#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE\n#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF\n#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4\n#define GL_MOVE_TO_RESETS_NV 0x90B5\n#define GL_MOVE_TO_CONTINUES_NV 0x90B6\n#define GL_CLOSE_PATH_NV 0x00\n#define GL_MOVE_TO_NV 0x02\n#define GL_RELATIVE_MOVE_TO_NV 0x03\n#define GL_LINE_TO_NV 0x04\n#define GL_RELATIVE_LINE_TO_NV 0x05\n#define GL_HORIZONTAL_LINE_TO_NV 0x06\n#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07\n#define GL_VERTICAL_LINE_TO_NV 0x08\n#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09\n#define GL_QUADRATIC_CURVE_TO_NV 0x0A\n#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B\n#define GL_CUBIC_CURVE_TO_NV 0x0C\n#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D\n#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E\n#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F\n#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10\n#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11\n#define GL_SMALL_CCW_ARC_TO_NV 0x12\n#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13\n#define GL_SMALL_CW_ARC_TO_NV 0x14\n#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15\n#define GL_LARGE_CCW_ARC_TO_NV 0x16\n#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17\n#define GL_LARGE_CW_ARC_TO_NV 0x18\n#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19\n#define GL_RESTART_PATH_NV 0xF0\n#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2\n#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4\n#define GL_RECT_NV 0xF6\n#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8\n#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA\n#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC\n#define GL_ARC_TO_NV 0xFE\n#define GL_RELATIVE_ARC_TO_NV 0xFF\n#define GL_BOLD_BIT_NV 0x01\n#define GL_ITALIC_BIT_NV 0x02\n#define GL_GLYPH_WIDTH_BIT_NV 0x01\n#define GL_GLYPH_HEIGHT_BIT_NV 0x02\n#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04\n#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08\n#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10\n#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20\n#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40\n#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80\n#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100\n#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000\n#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000\n#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000\n#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000\n#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000\n#define GL_FONT_ASCENDER_BIT_NV 0x00200000\n#define GL_FONT_DESCENDER_BIT_NV 0x00400000\n#define GL_FONT_HEIGHT_BIT_NV 0x00800000\n#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000\n#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000\n#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000\n#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000\n#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000\n#define GL_PRIMARY_COLOR 0x8577\n#define GL_MAX_VERTEX_STREAMS_ATI 0x876B\n#define GL_VERTEX_STREAM0_ATI 0x876C\n#define GL_VERTEX_STREAM1_ATI 0x876D\n#define GL_VERTEX_STREAM2_ATI 0x876E\n#define GL_VERTEX_STREAM3_ATI 0x876F\n#define GL_VERTEX_STREAM4_ATI 0x8770\n#define GL_VERTEX_STREAM5_ATI 0x8771\n#define GL_VERTEX_STREAM6_ATI 0x8772\n#define GL_VERTEX_STREAM7_ATI 0x8773\n#define GL_VERTEX_SOURCE_ATI 0x8774\n#define GL_RGB_422_APPLE 0x8A1F\n#define GL_RGB_RAW_422_APPLE 0x8A51\n#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD\n#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500\n#define GL_TEXTURE_LOD_BIAS_EXT 0x8501\n#define GL_SURFACE_STATE_NV 0x86EB\n#define GL_SURFACE_REGISTERED_NV 0x86FD\n#define GL_SURFACE_MAPPED_NV 0x8700\n#define GL_WRITE_DISCARD_NV 0x88BE\n#define GL_SRGB_DECODE_ARB 0x8299\n#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE\n#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF\n#define GL_TEXTURE_MIN_LOD_SGIS 0x813A\n#define GL_TEXTURE_MAX_LOD_SGIS 0x813B\n#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C\n#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D\n#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD\n#define GL_FOG_FUNC_SGIS 0x812A\n#define GL_FOG_FUNC_POINTS_SGIS 0x812B\n#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C\n#define GL_SYNC_X11_FENCE_EXT 0x90E1\n#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344\n#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB\n#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345\n#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF\n#define GL_FIXED_OES 0x140C\n#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB\n#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56\n#define GL_MAX_SAMPLES_EXT 0x8D57\n#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130\n#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131\n#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132\n#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133\n#define GL_TEXTURE_4D_SGIS 0x8134\n#define GL_PROXY_TEXTURE_4D_SGIS 0x8135\n#define GL_TEXTURE_4DSIZE_SGIS 0x8136\n#define GL_TEXTURE_WRAP_Q_SGIS 0x8137\n#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138\n#define GL_TEXTURE_4D_BINDING_SGIS 0x814F\n#define GL_PACK_SKIP_IMAGES_EXT 0x806B\n#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C\n#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D\n#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E\n#define GL_TEXTURE_3D_EXT 0x806F\n#define GL_PROXY_TEXTURE_3D_EXT 0x8070\n#define GL_TEXTURE_DEPTH_EXT 0x8071\n#define GL_TEXTURE_WRAP_R_EXT 0x8072\n#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073\n#define GL_MULTISAMPLE_EXT 0x809D\n#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E\n#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F\n#define GL_SAMPLE_MASK_EXT 0x80A0\n#define GL_1PASS_EXT 0x80A1\n#define GL_2PASS_0_EXT 0x80A2\n#define GL_2PASS_1_EXT 0x80A3\n#define GL_4PASS_0_EXT 0x80A4\n#define GL_4PASS_1_EXT 0x80A5\n#define GL_4PASS_2_EXT 0x80A6\n#define GL_4PASS_3_EXT 0x80A7\n#define GL_SAMPLE_BUFFERS_EXT 0x80A8\n#define GL_SAMPLES_EXT 0x80A9\n#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA\n#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB\n#define GL_SAMPLE_PATTERN_EXT 0x80AC\n#define GL_MULTISAMPLE_BIT_EXT 0x20000000\n#define GL_COLOR_SUM_EXT 0x8458\n#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459\n#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A\n#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B\n#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C\n#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D\n#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E\n#define GL_STATIC_ATI 0x8760\n#define GL_DYNAMIC_ATI 0x8761\n#define GL_PRESERVE_ATI 0x8762\n#define GL_DISCARD_ATI 0x8763\n#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764\n#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765\n#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766\n#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767\n#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047\n#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048\n#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049\n#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A\n#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B\n#define GL_TEXTURE_SPARSE_ARB 0x91A6\n#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7\n#define GL_MIN_SPARSE_LEVEL_ARB 0x919B\n#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8\n#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195\n#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196\n#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197\n#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198\n#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199\n#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A\n#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9\n#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0\n#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1\n#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2\n#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3\n#define GL_EYE_POINT_SGIS 0x81F4\n#define GL_OBJECT_POINT_SGIS 0x81F5\n#define GL_EYE_LINE_SGIS 0x81F6\n#define GL_OBJECT_LINE_SGIS 0x81F7\n#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8\n#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9\n#define GL_ALPHA_MIN_SGIX 0x8320\n#define GL_ALPHA_MAX_SGIX 0x8321\n#ifndef GL_SGIX_pixel_tiles\n#define GL_SGIX_pixel_tiles 1\nGLAPI int GLAD_GL_SGIX_pixel_tiles;\n#endif\n#ifndef GL_NV_point_sprite\n#define GL_NV_point_sprite 1\nGLAPI int GLAD_GL_NV_point_sprite;\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC)(GLenum, GLint);\nGLAPI PFNGLPOINTPARAMETERINVPROC glad_glPointParameteriNV;\n#define glPointParameteriNV glad_glPointParameteriNV\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC)(GLenum, const GLint*);\nGLAPI PFNGLPOINTPARAMETERIVNVPROC glad_glPointParameterivNV;\n#define glPointParameterivNV glad_glPointParameterivNV\n#endif\n#ifndef GL_APPLE_element_array\n#define GL_APPLE_element_array 1\nGLAPI int GLAD_GL_APPLE_element_array;\ntypedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC)(GLenum, const void*);\nGLAPI PFNGLELEMENTPOINTERAPPLEPROC glad_glElementPointerAPPLE;\n#define glElementPointerAPPLE glad_glElementPointerAPPLE\ntypedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC)(GLenum, GLint, GLsizei);\nGLAPI PFNGLDRAWELEMENTARRAYAPPLEPROC glad_glDrawElementArrayAPPLE;\n#define glDrawElementArrayAPPLE glad_glDrawElementArrayAPPLE\ntypedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC)(GLenum, GLuint, GLuint, GLint, GLsizei);\nGLAPI PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC glad_glDrawRangeElementArrayAPPLE;\n#define glDrawRangeElementArrayAPPLE glad_glDrawRangeElementArrayAPPLE\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC)(GLenum, const GLint*, const GLsizei*, GLsizei);\nGLAPI PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC glad_glMultiDrawElementArrayAPPLE;\n#define glMultiDrawElementArrayAPPLE glad_glMultiDrawElementArrayAPPLE\ntypedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC)(GLenum, GLuint, GLuint, const GLint*, const GLsizei*, GLsizei);\nGLAPI PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC glad_glMultiDrawRangeElementArrayAPPLE;\n#define glMultiDrawRangeElementArrayAPPLE glad_glMultiDrawRangeElementArrayAPPLE\n#endif\n#ifndef GL_AMD_multi_draw_indirect\n#define GL_AMD_multi_draw_indirect 1\nGLAPI int GLAD_GL_AMD_multi_draw_indirect;\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC)(GLenum, const void*, GLsizei, GLsizei);\nGLAPI PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC glad_glMultiDrawArraysIndirectAMD;\n#define glMultiDrawArraysIndirectAMD glad_glMultiDrawArraysIndirectAMD\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC)(GLenum, GLenum, const void*, GLsizei, GLsizei);\nGLAPI PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC glad_glMultiDrawElementsIndirectAMD;\n#define glMultiDrawElementsIndirectAMD glad_glMultiDrawElementsIndirectAMD\n#endif\n#ifndef GL_EXT_blend_subtract\n#define GL_EXT_blend_subtract 1\nGLAPI int GLAD_GL_EXT_blend_subtract;\n#endif\n#ifndef GL_SGIX_tag_sample_buffer\n#define GL_SGIX_tag_sample_buffer 1\nGLAPI int GLAD_GL_SGIX_tag_sample_buffer;\ntypedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC)();\nGLAPI PFNGLTAGSAMPLEBUFFERSGIXPROC glad_glTagSampleBufferSGIX;\n#define glTagSampleBufferSGIX glad_glTagSampleBufferSGIX\n#endif\n#ifndef GL_IBM_texture_mirrored_repeat\n#define GL_IBM_texture_mirrored_repeat 1\nGLAPI int GLAD_GL_IBM_texture_mirrored_repeat;\n#endif\n#ifndef GL_APPLE_transform_hint\n#define GL_APPLE_transform_hint 1\nGLAPI int GLAD_GL_APPLE_transform_hint;\n#endif\n#ifndef GL_ATI_separate_stencil\n#define GL_ATI_separate_stencil 1\nGLAPI int GLAD_GL_ATI_separate_stencil;\ntypedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC)(GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLSTENCILOPSEPARATEATIPROC glad_glStencilOpSeparateATI;\n#define glStencilOpSeparateATI glad_glStencilOpSeparateATI\ntypedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC)(GLenum, GLenum, GLint, GLuint);\nGLAPI PFNGLSTENCILFUNCSEPARATEATIPROC glad_glStencilFuncSeparateATI;\n#define glStencilFuncSeparateATI glad_glStencilFuncSeparateATI\n#endif\n#ifndef GL_NV_vertex_program2_option\n#define GL_NV_vertex_program2_option 1\nGLAPI int GLAD_GL_NV_vertex_program2_option;\n#endif\n#ifndef GL_EXT_texture_buffer_object\n#define GL_EXT_texture_buffer_object 1\nGLAPI int GLAD_GL_EXT_texture_buffer_object;\ntypedef void (APIENTRYP PFNGLTEXBUFFEREXTPROC)(GLenum, GLenum, GLuint);\nGLAPI PFNGLTEXBUFFEREXTPROC glad_glTexBufferEXT;\n#define glTexBufferEXT glad_glTexBufferEXT\n#endif\n#ifndef GL_ARB_vertex_blend\n#define GL_ARB_vertex_blend 1\nGLAPI int GLAD_GL_ARB_vertex_blend;\ntypedef void (APIENTRYP PFNGLWEIGHTBVARBPROC)(GLint, const GLbyte*);\nGLAPI PFNGLWEIGHTBVARBPROC glad_glWeightbvARB;\n#define glWeightbvARB glad_glWeightbvARB\ntypedef void (APIENTRYP PFNGLWEIGHTSVARBPROC)(GLint, const GLshort*);\nGLAPI PFNGLWEIGHTSVARBPROC glad_glWeightsvARB;\n#define glWeightsvARB glad_glWeightsvARB\ntypedef void (APIENTRYP PFNGLWEIGHTIVARBPROC)(GLint, const GLint*);\nGLAPI PFNGLWEIGHTIVARBPROC glad_glWeightivARB;\n#define glWeightivARB glad_glWeightivARB\ntypedef void (APIENTRYP PFNGLWEIGHTFVARBPROC)(GLint, const GLfloat*);\nGLAPI PFNGLWEIGHTFVARBPROC glad_glWeightfvARB;\n#define glWeightfvARB glad_glWeightfvARB\ntypedef void (APIENTRYP PFNGLWEIGHTDVARBPROC)(GLint, const GLdouble*);\nGLAPI PFNGLWEIGHTDVARBPROC glad_glWeightdvARB;\n#define glWeightdvARB glad_glWeightdvARB\ntypedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC)(GLint, const GLubyte*);\nGLAPI PFNGLWEIGHTUBVARBPROC glad_glWeightubvARB;\n#define glWeightubvARB glad_glWeightubvARB\ntypedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC)(GLint, const GLushort*);\nGLAPI PFNGLWEIGHTUSVARBPROC glad_glWeightusvARB;\n#define glWeightusvARB glad_glWeightusvARB\ntypedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC)(GLint, const GLuint*);\nGLAPI PFNGLWEIGHTUIVARBPROC glad_glWeightuivARB;\n#define glWeightuivARB glad_glWeightuivARB\ntypedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC)(GLint, GLenum, GLsizei, const void*);\nGLAPI PFNGLWEIGHTPOINTERARBPROC glad_glWeightPointerARB;\n#define glWeightPointerARB glad_glWeightPointerARB\ntypedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC)(GLint);\nGLAPI PFNGLVERTEXBLENDARBPROC glad_glVertexBlendARB;\n#define glVertexBlendARB glad_glVertexBlendARB\n#endif\n#ifndef GL_NV_vertex_program2\n#define GL_NV_vertex_program2 1\nGLAPI int GLAD_GL_NV_vertex_program2;\n#endif\n#ifndef GL_ARB_program_interface_query\n#define GL_ARB_program_interface_query 1\nGLAPI int GLAD_GL_ARB_program_interface_query;\n#endif\n#ifndef GL_EXT_misc_attribute\n#define GL_EXT_misc_attribute 1\nGLAPI int GLAD_GL_EXT_misc_attribute;\n#endif\n#ifndef GL_NV_multisample_coverage\n#define GL_NV_multisample_coverage 1\nGLAPI int GLAD_GL_NV_multisample_coverage;\n#endif\n#ifndef GL_ARB_shading_language_packing\n#define GL_ARB_shading_language_packing 1\nGLAPI int GLAD_GL_ARB_shading_language_packing;\n#endif\n#ifndef GL_EXT_texture_cube_map\n#define GL_EXT_texture_cube_map 1\nGLAPI int GLAD_GL_EXT_texture_cube_map;\n#endif\n#ifndef GL_ARB_texture_stencil8\n#define GL_ARB_texture_stencil8 1\nGLAPI int GLAD_GL_ARB_texture_stencil8;\n#endif\n#ifndef GL_EXT_index_func\n#define GL_EXT_index_func 1\nGLAPI int GLAD_GL_EXT_index_func;\ntypedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC)(GLenum, GLclampf);\nGLAPI PFNGLINDEXFUNCEXTPROC glad_glIndexFuncEXT;\n#define glIndexFuncEXT glad_glIndexFuncEXT\n#endif\n#ifndef GL_OES_compressed_paletted_texture\n#define GL_OES_compressed_paletted_texture 1\nGLAPI int GLAD_GL_OES_compressed_paletted_texture;\n#endif\n#ifndef GL_NV_depth_clamp\n#define GL_NV_depth_clamp 1\nGLAPI int GLAD_GL_NV_depth_clamp;\n#endif\n#ifndef GL_NV_shader_buffer_load\n#define GL_NV_shader_buffer_load 1\nGLAPI int GLAD_GL_NV_shader_buffer_load;\ntypedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC)(GLenum, GLenum);\nGLAPI PFNGLMAKEBUFFERRESIDENTNVPROC glad_glMakeBufferResidentNV;\n#define glMakeBufferResidentNV glad_glMakeBufferResidentNV\ntypedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC)(GLenum);\nGLAPI PFNGLMAKEBUFFERNONRESIDENTNVPROC glad_glMakeBufferNonResidentNV;\n#define glMakeBufferNonResidentNV glad_glMakeBufferNonResidentNV\ntypedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC)(GLenum);\nGLAPI PFNGLISBUFFERRESIDENTNVPROC glad_glIsBufferResidentNV;\n#define glIsBufferResidentNV glad_glIsBufferResidentNV\ntypedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC)(GLuint, GLenum);\nGLAPI PFNGLMAKENAMEDBUFFERRESIDENTNVPROC glad_glMakeNamedBufferResidentNV;\n#define glMakeNamedBufferResidentNV glad_glMakeNamedBufferResidentNV\ntypedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC)(GLuint);\nGLAPI PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC glad_glMakeNamedBufferNonResidentNV;\n#define glMakeNamedBufferNonResidentNV glad_glMakeNamedBufferNonResidentNV\ntypedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC)(GLuint);\nGLAPI PFNGLISNAMEDBUFFERRESIDENTNVPROC glad_glIsNamedBufferResidentNV;\n#define glIsNamedBufferResidentNV glad_glIsNamedBufferResidentNV\ntypedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC)(GLenum, GLenum, GLuint64EXT*);\nGLAPI PFNGLGETBUFFERPARAMETERUI64VNVPROC glad_glGetBufferParameterui64vNV;\n#define glGetBufferParameterui64vNV glad_glGetBufferParameterui64vNV\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC)(GLuint, GLenum, GLuint64EXT*);\nGLAPI PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC glad_glGetNamedBufferParameterui64vNV;\n#define glGetNamedBufferParameterui64vNV glad_glGetNamedBufferParameterui64vNV\ntypedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC)(GLenum, GLuint64EXT*);\nGLAPI PFNGLGETINTEGERUI64VNVPROC glad_glGetIntegerui64vNV;\n#define glGetIntegerui64vNV glad_glGetIntegerui64vNV\ntypedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC)(GLint, GLuint64EXT);\nGLAPI PFNGLUNIFORMUI64NVPROC glad_glUniformui64NV;\n#define glUniformui64NV glad_glUniformui64NV\ntypedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC)(GLint, GLsizei, const GLuint64EXT*);\nGLAPI PFNGLUNIFORMUI64VNVPROC glad_glUniformui64vNV;\n#define glUniformui64vNV glad_glUniformui64vNV\ntypedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC)(GLuint, GLint, GLuint64EXT*);\nGLAPI PFNGLGETUNIFORMUI64VNVPROC glad_glGetUniformui64vNV;\n#define glGetUniformui64vNV glad_glGetUniformui64vNV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC)(GLuint, GLint, GLuint64EXT);\nGLAPI PFNGLPROGRAMUNIFORMUI64NVPROC glad_glProgramUniformui64NV;\n#define glProgramUniformui64NV glad_glProgramUniformui64NV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC)(GLuint, GLint, GLsizei, const GLuint64EXT*);\nGLAPI PFNGLPROGRAMUNIFORMUI64VNVPROC glad_glProgramUniformui64vNV;\n#define glProgramUniformui64vNV glad_glProgramUniformui64vNV\n#endif\n#ifndef GL_EXT_color_subtable\n#define GL_EXT_color_subtable 1\nGLAPI int GLAD_GL_EXT_color_subtable;\ntypedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC)(GLenum, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLCOLORSUBTABLEEXTPROC glad_glColorSubTableEXT;\n#define glColorSubTableEXT glad_glColorSubTableEXT\ntypedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC)(GLenum, GLsizei, GLint, GLint, GLsizei);\nGLAPI PFNGLCOPYCOLORSUBTABLEEXTPROC glad_glCopyColorSubTableEXT;\n#define glCopyColorSubTableEXT glad_glCopyColorSubTableEXT\n#endif\n#ifndef GL_SUNX_constant_data\n#define GL_SUNX_constant_data 1\nGLAPI int GLAD_GL_SUNX_constant_data;\ntypedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC)();\nGLAPI PFNGLFINISHTEXTURESUNXPROC glad_glFinishTextureSUNX;\n#define glFinishTextureSUNX glad_glFinishTextureSUNX\n#endif\n#ifndef GL_EXT_multi_draw_arrays\n#define GL_EXT_multi_draw_arrays 1\nGLAPI int GLAD_GL_EXT_multi_draw_arrays;\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC)(GLenum, const GLint*, const GLsizei*, GLsizei);\nGLAPI PFNGLMULTIDRAWARRAYSEXTPROC glad_glMultiDrawArraysEXT;\n#define glMultiDrawArraysEXT glad_glMultiDrawArraysEXT\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC)(GLenum, const GLsizei*, GLenum, const void**, GLsizei);\nGLAPI PFNGLMULTIDRAWELEMENTSEXTPROC glad_glMultiDrawElementsEXT;\n#define glMultiDrawElementsEXT glad_glMultiDrawElementsEXT\n#endif\n#ifndef GL_ARB_shader_atomic_counters\n#define GL_ARB_shader_atomic_counters 1\nGLAPI int GLAD_GL_ARB_shader_atomic_counters;\n#endif\n#ifndef GL_ARB_arrays_of_arrays\n#define GL_ARB_arrays_of_arrays 1\nGLAPI int GLAD_GL_ARB_arrays_of_arrays;\n#endif\n#ifndef GL_NV_conditional_render\n#define GL_NV_conditional_render 1\nGLAPI int GLAD_GL_NV_conditional_render;\ntypedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC)(GLuint, GLenum);\nGLAPI PFNGLBEGINCONDITIONALRENDERNVPROC glad_glBeginConditionalRenderNV;\n#define glBeginConditionalRenderNV glad_glBeginConditionalRenderNV\ntypedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC)();\nGLAPI PFNGLENDCONDITIONALRENDERNVPROC glad_glEndConditionalRenderNV;\n#define glEndConditionalRenderNV glad_glEndConditionalRenderNV\n#endif\n#ifndef GL_EXT_texture_env_combine\n#define GL_EXT_texture_env_combine 1\nGLAPI int GLAD_GL_EXT_texture_env_combine;\n#endif\n#ifndef GL_NV_fog_distance\n#define GL_NV_fog_distance 1\nGLAPI int GLAD_GL_NV_fog_distance;\n#endif\n#ifndef GL_SGIX_async_histogram\n#define GL_SGIX_async_histogram 1\nGLAPI int GLAD_GL_SGIX_async_histogram;\n#endif\n#ifndef GL_MESA_resize_buffers\n#define GL_MESA_resize_buffers 1\nGLAPI int GLAD_GL_MESA_resize_buffers;\ntypedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC)();\nGLAPI PFNGLRESIZEBUFFERSMESAPROC glad_glResizeBuffersMESA;\n#define glResizeBuffersMESA glad_glResizeBuffersMESA\n#endif\n#ifndef GL_NV_light_max_exponent\n#define GL_NV_light_max_exponent 1\nGLAPI int GLAD_GL_NV_light_max_exponent;\n#endif\n#ifndef GL_NV_texture_env_combine4\n#define GL_NV_texture_env_combine4 1\nGLAPI int GLAD_GL_NV_texture_env_combine4;\n#endif\n#ifndef GL_ARB_texture_view\n#define GL_ARB_texture_view 1\nGLAPI int GLAD_GL_ARB_texture_view;\n#endif\n#ifndef GL_ARB_texture_env_combine\n#define GL_ARB_texture_env_combine 1\nGLAPI int GLAD_GL_ARB_texture_env_combine;\n#endif\n#ifndef GL_ARB_map_buffer_range\n#define GL_ARB_map_buffer_range 1\nGLAPI int GLAD_GL_ARB_map_buffer_range;\n#endif\n#ifndef GL_EXT_convolution\n#define GL_EXT_convolution 1\nGLAPI int GLAD_GL_EXT_convolution;\ntypedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC)(GLenum, GLenum, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLCONVOLUTIONFILTER1DEXTPROC glad_glConvolutionFilter1DEXT;\n#define glConvolutionFilter1DEXT glad_glConvolutionFilter1DEXT\ntypedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC)(GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLCONVOLUTIONFILTER2DEXTPROC glad_glConvolutionFilter2DEXT;\n#define glConvolutionFilter2DEXT glad_glConvolutionFilter2DEXT\ntypedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC)(GLenum, GLenum, GLfloat);\nGLAPI PFNGLCONVOLUTIONPARAMETERFEXTPROC glad_glConvolutionParameterfEXT;\n#define glConvolutionParameterfEXT glad_glConvolutionParameterfEXT\ntypedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLCONVOLUTIONPARAMETERFVEXTPROC glad_glConvolutionParameterfvEXT;\n#define glConvolutionParameterfvEXT glad_glConvolutionParameterfvEXT\ntypedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC)(GLenum, GLenum, GLint);\nGLAPI PFNGLCONVOLUTIONPARAMETERIEXTPROC glad_glConvolutionParameteriEXT;\n#define glConvolutionParameteriEXT glad_glConvolutionParameteriEXT\ntypedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLCONVOLUTIONPARAMETERIVEXTPROC glad_glConvolutionParameterivEXT;\n#define glConvolutionParameterivEXT glad_glConvolutionParameterivEXT\ntypedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC)(GLenum, GLenum, GLint, GLint, GLsizei);\nGLAPI PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC glad_glCopyConvolutionFilter1DEXT;\n#define glCopyConvolutionFilter1DEXT glad_glCopyConvolutionFilter1DEXT\ntypedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC)(GLenum, GLenum, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC glad_glCopyConvolutionFilter2DEXT;\n#define glCopyConvolutionFilter2DEXT glad_glCopyConvolutionFilter2DEXT\ntypedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC)(GLenum, GLenum, GLenum, void*);\nGLAPI PFNGLGETCONVOLUTIONFILTEREXTPROC glad_glGetConvolutionFilterEXT;\n#define glGetConvolutionFilterEXT glad_glGetConvolutionFilterEXT\ntypedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC glad_glGetConvolutionParameterfvEXT;\n#define glGetConvolutionParameterfvEXT glad_glGetConvolutionParameterfvEXT\ntypedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC glad_glGetConvolutionParameterivEXT;\n#define glGetConvolutionParameterivEXT glad_glGetConvolutionParameterivEXT\ntypedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC)(GLenum, GLenum, GLenum, void*, void*, void*);\nGLAPI PFNGLGETSEPARABLEFILTEREXTPROC glad_glGetSeparableFilterEXT;\n#define glGetSeparableFilterEXT glad_glGetSeparableFilterEXT\ntypedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC)(GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const void*, const void*);\nGLAPI PFNGLSEPARABLEFILTER2DEXTPROC glad_glSeparableFilter2DEXT;\n#define glSeparableFilter2DEXT glad_glSeparableFilter2DEXT\n#endif\n#ifndef GL_NV_compute_program5\n#define GL_NV_compute_program5 1\nGLAPI int GLAD_GL_NV_compute_program5;\n#endif\n#ifndef GL_NV_vertex_attrib_integer_64bit\n#define GL_NV_vertex_attrib_integer_64bit 1\nGLAPI int GLAD_GL_NV_vertex_attrib_integer_64bit;\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC)(GLuint, GLint64EXT);\nGLAPI PFNGLVERTEXATTRIBL1I64NVPROC glad_glVertexAttribL1i64NV;\n#define glVertexAttribL1i64NV glad_glVertexAttribL1i64NV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC)(GLuint, GLint64EXT, GLint64EXT);\nGLAPI PFNGLVERTEXATTRIBL2I64NVPROC glad_glVertexAttribL2i64NV;\n#define glVertexAttribL2i64NV glad_glVertexAttribL2i64NV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC)(GLuint, GLint64EXT, GLint64EXT, GLint64EXT);\nGLAPI PFNGLVERTEXATTRIBL3I64NVPROC glad_glVertexAttribL3i64NV;\n#define glVertexAttribL3i64NV glad_glVertexAttribL3i64NV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC)(GLuint, GLint64EXT, GLint64EXT, GLint64EXT, GLint64EXT);\nGLAPI PFNGLVERTEXATTRIBL4I64NVPROC glad_glVertexAttribL4i64NV;\n#define glVertexAttribL4i64NV glad_glVertexAttribL4i64NV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC)(GLuint, const GLint64EXT*);\nGLAPI PFNGLVERTEXATTRIBL1I64VNVPROC glad_glVertexAttribL1i64vNV;\n#define glVertexAttribL1i64vNV glad_glVertexAttribL1i64vNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC)(GLuint, const GLint64EXT*);\nGLAPI PFNGLVERTEXATTRIBL2I64VNVPROC glad_glVertexAttribL2i64vNV;\n#define glVertexAttribL2i64vNV glad_glVertexAttribL2i64vNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC)(GLuint, const GLint64EXT*);\nGLAPI PFNGLVERTEXATTRIBL3I64VNVPROC glad_glVertexAttribL3i64vNV;\n#define glVertexAttribL3i64vNV glad_glVertexAttribL3i64vNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC)(GLuint, const GLint64EXT*);\nGLAPI PFNGLVERTEXATTRIBL4I64VNVPROC glad_glVertexAttribL4i64vNV;\n#define glVertexAttribL4i64vNV glad_glVertexAttribL4i64vNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC)(GLuint, GLuint64EXT);\nGLAPI PFNGLVERTEXATTRIBL1UI64NVPROC glad_glVertexAttribL1ui64NV;\n#define glVertexAttribL1ui64NV glad_glVertexAttribL1ui64NV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC)(GLuint, GLuint64EXT, GLuint64EXT);\nGLAPI PFNGLVERTEXATTRIBL2UI64NVPROC glad_glVertexAttribL2ui64NV;\n#define glVertexAttribL2ui64NV glad_glVertexAttribL2ui64NV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC)(GLuint, GLuint64EXT, GLuint64EXT, GLuint64EXT);\nGLAPI PFNGLVERTEXATTRIBL3UI64NVPROC glad_glVertexAttribL3ui64NV;\n#define glVertexAttribL3ui64NV glad_glVertexAttribL3ui64NV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC)(GLuint, GLuint64EXT, GLuint64EXT, GLuint64EXT, GLuint64EXT);\nGLAPI PFNGLVERTEXATTRIBL4UI64NVPROC glad_glVertexAttribL4ui64NV;\n#define glVertexAttribL4ui64NV glad_glVertexAttribL4ui64NV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC)(GLuint, const GLuint64EXT*);\nGLAPI PFNGLVERTEXATTRIBL1UI64VNVPROC glad_glVertexAttribL1ui64vNV;\n#define glVertexAttribL1ui64vNV glad_glVertexAttribL1ui64vNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC)(GLuint, const GLuint64EXT*);\nGLAPI PFNGLVERTEXATTRIBL2UI64VNVPROC glad_glVertexAttribL2ui64vNV;\n#define glVertexAttribL2ui64vNV glad_glVertexAttribL2ui64vNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC)(GLuint, const GLuint64EXT*);\nGLAPI PFNGLVERTEXATTRIBL3UI64VNVPROC glad_glVertexAttribL3ui64vNV;\n#define glVertexAttribL3ui64vNV glad_glVertexAttribL3ui64vNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC)(GLuint, const GLuint64EXT*);\nGLAPI PFNGLVERTEXATTRIBL4UI64VNVPROC glad_glVertexAttribL4ui64vNV;\n#define glVertexAttribL4ui64vNV glad_glVertexAttribL4ui64vNV\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC)(GLuint, GLenum, GLint64EXT*);\nGLAPI PFNGLGETVERTEXATTRIBLI64VNVPROC glad_glGetVertexAttribLi64vNV;\n#define glGetVertexAttribLi64vNV glad_glGetVertexAttribLi64vNV\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC)(GLuint, GLenum, GLuint64EXT*);\nGLAPI PFNGLGETVERTEXATTRIBLUI64VNVPROC glad_glGetVertexAttribLui64vNV;\n#define glGetVertexAttribLui64vNV glad_glGetVertexAttribLui64vNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC)(GLuint, GLint, GLenum, GLsizei);\nGLAPI PFNGLVERTEXATTRIBLFORMATNVPROC glad_glVertexAttribLFormatNV;\n#define glVertexAttribLFormatNV glad_glVertexAttribLFormatNV\n#endif\n#ifndef GL_EXT_paletted_texture\n#define GL_EXT_paletted_texture 1\nGLAPI int GLAD_GL_EXT_paletted_texture;\ntypedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC)(GLenum, GLenum, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLCOLORTABLEEXTPROC glad_glColorTableEXT;\n#define glColorTableEXT glad_glColorTableEXT\ntypedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC)(GLenum, GLenum, GLenum, void*);\nGLAPI PFNGLGETCOLORTABLEEXTPROC glad_glGetColorTableEXT;\n#define glGetColorTableEXT glad_glGetColorTableEXT\ntypedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETCOLORTABLEPARAMETERIVEXTPROC glad_glGetColorTableParameterivEXT;\n#define glGetColorTableParameterivEXT glad_glGetColorTableParameterivEXT\ntypedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETCOLORTABLEPARAMETERFVEXTPROC glad_glGetColorTableParameterfvEXT;\n#define glGetColorTableParameterfvEXT glad_glGetColorTableParameterfvEXT\n#endif\n#ifndef GL_ARB_texture_buffer_object\n#define GL_ARB_texture_buffer_object 1\nGLAPI int GLAD_GL_ARB_texture_buffer_object;\ntypedef void (APIENTRYP PFNGLTEXBUFFERARBPROC)(GLenum, GLenum, GLuint);\nGLAPI PFNGLTEXBUFFERARBPROC glad_glTexBufferARB;\n#define glTexBufferARB glad_glTexBufferARB\n#endif\n#ifndef GL_ATI_pn_triangles\n#define GL_ATI_pn_triangles 1\nGLAPI int GLAD_GL_ATI_pn_triangles;\ntypedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC)(GLenum, GLint);\nGLAPI PFNGLPNTRIANGLESIATIPROC glad_glPNTrianglesiATI;\n#define glPNTrianglesiATI glad_glPNTrianglesiATI\ntypedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC)(GLenum, GLfloat);\nGLAPI PFNGLPNTRIANGLESFATIPROC glad_glPNTrianglesfATI;\n#define glPNTrianglesfATI glad_glPNTrianglesfATI\n#endif\n#ifndef GL_SGIX_resample\n#define GL_SGIX_resample 1\nGLAPI int GLAD_GL_SGIX_resample;\n#endif\n#ifndef GL_SGIX_flush_raster\n#define GL_SGIX_flush_raster 1\nGLAPI int GLAD_GL_SGIX_flush_raster;\ntypedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC)();\nGLAPI PFNGLFLUSHRASTERSGIXPROC glad_glFlushRasterSGIX;\n#define glFlushRasterSGIX glad_glFlushRasterSGIX\n#endif\n#ifndef GL_EXT_light_texture\n#define GL_EXT_light_texture 1\nGLAPI int GLAD_GL_EXT_light_texture;\ntypedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC)(GLenum);\nGLAPI PFNGLAPPLYTEXTUREEXTPROC glad_glApplyTextureEXT;\n#define glApplyTextureEXT glad_glApplyTextureEXT\ntypedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC)(GLenum);\nGLAPI PFNGLTEXTURELIGHTEXTPROC glad_glTextureLightEXT;\n#define glTextureLightEXT glad_glTextureLightEXT\ntypedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC)(GLenum, GLenum);\nGLAPI PFNGLTEXTUREMATERIALEXTPROC glad_glTextureMaterialEXT;\n#define glTextureMaterialEXT glad_glTextureMaterialEXT\n#endif\n#ifndef GL_ARB_point_sprite\n#define GL_ARB_point_sprite 1\nGLAPI int GLAD_GL_ARB_point_sprite;\n#endif\n#ifndef GL_ARB_half_float_pixel\n#define GL_ARB_half_float_pixel 1\nGLAPI int GLAD_GL_ARB_half_float_pixel;\n#endif\n#ifndef GL_NV_tessellation_program5\n#define GL_NV_tessellation_program5 1\nGLAPI int GLAD_GL_NV_tessellation_program5;\n#endif\n#ifndef GL_REND_screen_coordinates\n#define GL_REND_screen_coordinates 1\nGLAPI int GLAD_GL_REND_screen_coordinates;\n#endif\n#ifndef GL_EXT_shared_texture_palette\n#define GL_EXT_shared_texture_palette 1\nGLAPI int GLAD_GL_EXT_shared_texture_palette;\n#endif\n#ifndef GL_EXT_packed_float\n#define GL_EXT_packed_float 1\nGLAPI int GLAD_GL_EXT_packed_float;\n#endif\n#ifndef GL_OML_subsample\n#define GL_OML_subsample 1\nGLAPI int GLAD_GL_OML_subsample;\n#endif\n#ifndef GL_SGIX_vertex_preclip\n#define GL_SGIX_vertex_preclip 1\nGLAPI int GLAD_GL_SGIX_vertex_preclip;\n#endif\n#ifndef GL_SGIX_texture_scale_bias\n#define GL_SGIX_texture_scale_bias 1\nGLAPI int GLAD_GL_SGIX_texture_scale_bias;\n#endif\n#ifndef GL_AMD_draw_buffers_blend\n#define GL_AMD_draw_buffers_blend 1\nGLAPI int GLAD_GL_AMD_draw_buffers_blend;\ntypedef void (APIENTRYP PFNGLBLENDFUNCINDEXEDAMDPROC)(GLuint, GLenum, GLenum);\nGLAPI PFNGLBLENDFUNCINDEXEDAMDPROC glad_glBlendFuncIndexedAMD;\n#define glBlendFuncIndexedAMD glad_glBlendFuncIndexedAMD\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC)(GLuint, GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC glad_glBlendFuncSeparateIndexedAMD;\n#define glBlendFuncSeparateIndexedAMD glad_glBlendFuncSeparateIndexedAMD\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONINDEXEDAMDPROC)(GLuint, GLenum);\nGLAPI PFNGLBLENDEQUATIONINDEXEDAMDPROC glad_glBlendEquationIndexedAMD;\n#define glBlendEquationIndexedAMD glad_glBlendEquationIndexedAMD\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC)(GLuint, GLenum, GLenum);\nGLAPI PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC glad_glBlendEquationSeparateIndexedAMD;\n#define glBlendEquationSeparateIndexedAMD glad_glBlendEquationSeparateIndexedAMD\n#endif\n#ifndef GL_MESA_window_pos\n#define GL_MESA_window_pos 1\nGLAPI int GLAD_GL_MESA_window_pos;\ntypedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC)(GLdouble, GLdouble);\nGLAPI PFNGLWINDOWPOS2DMESAPROC glad_glWindowPos2dMESA;\n#define glWindowPos2dMESA glad_glWindowPos2dMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC)(const GLdouble*);\nGLAPI PFNGLWINDOWPOS2DVMESAPROC glad_glWindowPos2dvMESA;\n#define glWindowPos2dvMESA glad_glWindowPos2dvMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC)(GLfloat, GLfloat);\nGLAPI PFNGLWINDOWPOS2FMESAPROC glad_glWindowPos2fMESA;\n#define glWindowPos2fMESA glad_glWindowPos2fMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC)(const GLfloat*);\nGLAPI PFNGLWINDOWPOS2FVMESAPROC glad_glWindowPos2fvMESA;\n#define glWindowPos2fvMESA glad_glWindowPos2fvMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC)(GLint, GLint);\nGLAPI PFNGLWINDOWPOS2IMESAPROC glad_glWindowPos2iMESA;\n#define glWindowPos2iMESA glad_glWindowPos2iMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC)(const GLint*);\nGLAPI PFNGLWINDOWPOS2IVMESAPROC glad_glWindowPos2ivMESA;\n#define glWindowPos2ivMESA glad_glWindowPos2ivMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC)(GLshort, GLshort);\nGLAPI PFNGLWINDOWPOS2SMESAPROC glad_glWindowPos2sMESA;\n#define glWindowPos2sMESA glad_glWindowPos2sMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC)(const GLshort*);\nGLAPI PFNGLWINDOWPOS2SVMESAPROC glad_glWindowPos2svMESA;\n#define glWindowPos2svMESA glad_glWindowPos2svMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC)(GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLWINDOWPOS3DMESAPROC glad_glWindowPos3dMESA;\n#define glWindowPos3dMESA glad_glWindowPos3dMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC)(const GLdouble*);\nGLAPI PFNGLWINDOWPOS3DVMESAPROC glad_glWindowPos3dvMESA;\n#define glWindowPos3dvMESA glad_glWindowPos3dvMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC)(GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLWINDOWPOS3FMESAPROC glad_glWindowPos3fMESA;\n#define glWindowPos3fMESA glad_glWindowPos3fMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC)(const GLfloat*);\nGLAPI PFNGLWINDOWPOS3FVMESAPROC glad_glWindowPos3fvMESA;\n#define glWindowPos3fvMESA glad_glWindowPos3fvMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC)(GLint, GLint, GLint);\nGLAPI PFNGLWINDOWPOS3IMESAPROC glad_glWindowPos3iMESA;\n#define glWindowPos3iMESA glad_glWindowPos3iMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC)(const GLint*);\nGLAPI PFNGLWINDOWPOS3IVMESAPROC glad_glWindowPos3ivMESA;\n#define glWindowPos3ivMESA glad_glWindowPos3ivMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC)(GLshort, GLshort, GLshort);\nGLAPI PFNGLWINDOWPOS3SMESAPROC glad_glWindowPos3sMESA;\n#define glWindowPos3sMESA glad_glWindowPos3sMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC)(const GLshort*);\nGLAPI PFNGLWINDOWPOS3SVMESAPROC glad_glWindowPos3svMESA;\n#define glWindowPos3svMESA glad_glWindowPos3svMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC)(GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLWINDOWPOS4DMESAPROC glad_glWindowPos4dMESA;\n#define glWindowPos4dMESA glad_glWindowPos4dMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC)(const GLdouble*);\nGLAPI PFNGLWINDOWPOS4DVMESAPROC glad_glWindowPos4dvMESA;\n#define glWindowPos4dvMESA glad_glWindowPos4dvMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC)(GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLWINDOWPOS4FMESAPROC glad_glWindowPos4fMESA;\n#define glWindowPos4fMESA glad_glWindowPos4fMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC)(const GLfloat*);\nGLAPI PFNGLWINDOWPOS4FVMESAPROC glad_glWindowPos4fvMESA;\n#define glWindowPos4fvMESA glad_glWindowPos4fvMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC)(GLint, GLint, GLint, GLint);\nGLAPI PFNGLWINDOWPOS4IMESAPROC glad_glWindowPos4iMESA;\n#define glWindowPos4iMESA glad_glWindowPos4iMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC)(const GLint*);\nGLAPI PFNGLWINDOWPOS4IVMESAPROC glad_glWindowPos4ivMESA;\n#define glWindowPos4ivMESA glad_glWindowPos4ivMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC)(GLshort, GLshort, GLshort, GLshort);\nGLAPI PFNGLWINDOWPOS4SMESAPROC glad_glWindowPos4sMESA;\n#define glWindowPos4sMESA glad_glWindowPos4sMESA\ntypedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC)(const GLshort*);\nGLAPI PFNGLWINDOWPOS4SVMESAPROC glad_glWindowPos4svMESA;\n#define glWindowPos4svMESA glad_glWindowPos4svMESA\n#endif\n#ifndef GL_EXT_texture_array\n#define GL_EXT_texture_array 1\nGLAPI int GLAD_GL_EXT_texture_array;\n#endif\n#ifndef GL_NV_texture_barrier\n#define GL_NV_texture_barrier 1\nGLAPI int GLAD_GL_NV_texture_barrier;\ntypedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC)();\nGLAPI PFNGLTEXTUREBARRIERNVPROC glad_glTextureBarrierNV;\n#define glTextureBarrierNV glad_glTextureBarrierNV\n#endif\n#ifndef GL_ARB_texture_query_levels\n#define GL_ARB_texture_query_levels 1\nGLAPI int GLAD_GL_ARB_texture_query_levels;\n#endif\n#ifndef GL_NV_texgen_emboss\n#define GL_NV_texgen_emboss 1\nGLAPI int GLAD_GL_NV_texgen_emboss;\n#endif\n#ifndef GL_EXT_texture_swizzle\n#define GL_EXT_texture_swizzle 1\nGLAPI int GLAD_GL_EXT_texture_swizzle;\n#endif\n#ifndef GL_ARB_texture_rg\n#define GL_ARB_texture_rg 1\nGLAPI int GLAD_GL_ARB_texture_rg;\n#endif\n#ifndef GL_ARB_vertex_type_2_10_10_10_rev\n#define GL_ARB_vertex_type_2_10_10_10_rev 1\nGLAPI int GLAD_GL_ARB_vertex_type_2_10_10_10_rev;\n#endif\n#ifndef GL_ARB_fragment_shader\n#define GL_ARB_fragment_shader 1\nGLAPI int GLAD_GL_ARB_fragment_shader;\n#endif\n#ifndef GL_3DFX_tbuffer\n#define GL_3DFX_tbuffer 1\nGLAPI int GLAD_GL_3DFX_tbuffer;\ntypedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC)(GLuint);\nGLAPI PFNGLTBUFFERMASK3DFXPROC glad_glTbufferMask3DFX;\n#define glTbufferMask3DFX glad_glTbufferMask3DFX\n#endif\n#ifndef GL_GREMEDY_frame_terminator\n#define GL_GREMEDY_frame_terminator 1\nGLAPI int GLAD_GL_GREMEDY_frame_terminator;\ntypedef void (APIENTRYP PFNGLFRAMETERMINATORGREMEDYPROC)();\nGLAPI PFNGLFRAMETERMINATORGREMEDYPROC glad_glFrameTerminatorGREMEDY;\n#define glFrameTerminatorGREMEDY glad_glFrameTerminatorGREMEDY\n#endif\n#ifndef GL_ARB_blend_func_extended\n#define GL_ARB_blend_func_extended 1\nGLAPI int GLAD_GL_ARB_blend_func_extended;\n#endif\n#ifndef GL_EXT_separate_shader_objects\n#define GL_EXT_separate_shader_objects 1\nGLAPI int GLAD_GL_EXT_separate_shader_objects;\ntypedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC)(GLenum, GLuint);\nGLAPI PFNGLUSESHADERPROGRAMEXTPROC glad_glUseShaderProgramEXT;\n#define glUseShaderProgramEXT glad_glUseShaderProgramEXT\ntypedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC)(GLuint);\nGLAPI PFNGLACTIVEPROGRAMEXTPROC glad_glActiveProgramEXT;\n#define glActiveProgramEXT glad_glActiveProgramEXT\ntypedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC)(GLenum, const GLchar*);\nGLAPI PFNGLCREATESHADERPROGRAMEXTPROC glad_glCreateShaderProgramEXT;\n#define glCreateShaderProgramEXT glad_glCreateShaderProgramEXT\ntypedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMEXTPROC)(GLuint, GLuint);\nGLAPI PFNGLACTIVESHADERPROGRAMEXTPROC glad_glActiveShaderProgramEXT;\n#define glActiveShaderProgramEXT glad_glActiveShaderProgramEXT\ntypedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEEXTPROC)(GLuint);\nGLAPI PFNGLBINDPROGRAMPIPELINEEXTPROC glad_glBindProgramPipelineEXT;\n#define glBindProgramPipelineEXT glad_glBindProgramPipelineEXT\ntypedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVEXTPROC)(GLenum, GLsizei, const GLchar**);\nGLAPI PFNGLCREATESHADERPROGRAMVEXTPROC glad_glCreateShaderProgramvEXT;\n#define glCreateShaderProgramvEXT glad_glCreateShaderProgramvEXT\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESEXTPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEPROGRAMPIPELINESEXTPROC glad_glDeleteProgramPipelinesEXT;\n#define glDeleteProgramPipelinesEXT glad_glDeleteProgramPipelinesEXT\ntypedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESEXTPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENPROGRAMPIPELINESEXTPROC glad_glGenProgramPipelinesEXT;\n#define glGenProgramPipelinesEXT glad_glGenProgramPipelinesEXT\ntypedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC)(GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC glad_glGetProgramPipelineInfoLogEXT;\n#define glGetProgramPipelineInfoLogEXT glad_glGetProgramPipelineInfoLogEXT\ntypedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVEXTPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETPROGRAMPIPELINEIVEXTPROC glad_glGetProgramPipelineivEXT;\n#define glGetProgramPipelineivEXT glad_glGetProgramPipelineivEXT\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEEXTPROC)(GLuint);\nGLAPI PFNGLISPROGRAMPIPELINEEXTPROC glad_glIsProgramPipelineEXT;\n#define glIsProgramPipelineEXT glad_glIsProgramPipelineEXT\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC)(GLuint, GLenum, GLint);\nGLAPI PFNGLPROGRAMPARAMETERIEXTPROC glad_glProgramParameteriEXT;\n#define glProgramParameteriEXT glad_glProgramParameteriEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC)(GLuint, GLint, GLfloat);\nGLAPI PFNGLPROGRAMUNIFORM1FEXTPROC glad_glProgramUniform1fEXT;\n#define glProgramUniform1fEXT glad_glProgramUniform1fEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC)(GLuint, GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORM1FVEXTPROC glad_glProgramUniform1fvEXT;\n#define glProgramUniform1fvEXT glad_glProgramUniform1fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC)(GLuint, GLint, GLint);\nGLAPI PFNGLPROGRAMUNIFORM1IEXTPROC glad_glProgramUniform1iEXT;\n#define glProgramUniform1iEXT glad_glProgramUniform1iEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC)(GLuint, GLint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMUNIFORM1IVEXTPROC glad_glProgramUniform1ivEXT;\n#define glProgramUniform1ivEXT glad_glProgramUniform1ivEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC)(GLuint, GLint, GLfloat, GLfloat);\nGLAPI PFNGLPROGRAMUNIFORM2FEXTPROC glad_glProgramUniform2fEXT;\n#define glProgramUniform2fEXT glad_glProgramUniform2fEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC)(GLuint, GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORM2FVEXTPROC glad_glProgramUniform2fvEXT;\n#define glProgramUniform2fvEXT glad_glProgramUniform2fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC)(GLuint, GLint, GLint, GLint);\nGLAPI PFNGLPROGRAMUNIFORM2IEXTPROC glad_glProgramUniform2iEXT;\n#define glProgramUniform2iEXT glad_glProgramUniform2iEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC)(GLuint, GLint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMUNIFORM2IVEXTPROC glad_glProgramUniform2ivEXT;\n#define glProgramUniform2ivEXT glad_glProgramUniform2ivEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC)(GLuint, GLint, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLPROGRAMUNIFORM3FEXTPROC glad_glProgramUniform3fEXT;\n#define glProgramUniform3fEXT glad_glProgramUniform3fEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC)(GLuint, GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORM3FVEXTPROC glad_glProgramUniform3fvEXT;\n#define glProgramUniform3fvEXT glad_glProgramUniform3fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC)(GLuint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLPROGRAMUNIFORM3IEXTPROC glad_glProgramUniform3iEXT;\n#define glProgramUniform3iEXT glad_glProgramUniform3iEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC)(GLuint, GLint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMUNIFORM3IVEXTPROC glad_glProgramUniform3ivEXT;\n#define glProgramUniform3ivEXT glad_glProgramUniform3ivEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC)(GLuint, GLint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLPROGRAMUNIFORM4FEXTPROC glad_glProgramUniform4fEXT;\n#define glProgramUniform4fEXT glad_glProgramUniform4fEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC)(GLuint, GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORM4FVEXTPROC glad_glProgramUniform4fvEXT;\n#define glProgramUniform4fvEXT glad_glProgramUniform4fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC)(GLuint, GLint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLPROGRAMUNIFORM4IEXTPROC glad_glProgramUniform4iEXT;\n#define glProgramUniform4iEXT glad_glProgramUniform4iEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC)(GLuint, GLint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMUNIFORM4IVEXTPROC glad_glProgramUniform4ivEXT;\n#define glProgramUniform4ivEXT glad_glProgramUniform4ivEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC glad_glProgramUniformMatrix2fvEXT;\n#define glProgramUniformMatrix2fvEXT glad_glProgramUniformMatrix2fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC glad_glProgramUniformMatrix3fvEXT;\n#define glProgramUniformMatrix3fvEXT glad_glProgramUniformMatrix3fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC glad_glProgramUniformMatrix4fvEXT;\n#define glProgramUniformMatrix4fvEXT glad_glProgramUniformMatrix4fvEXT\ntypedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESEXTPROC)(GLuint, GLbitfield, GLuint);\nGLAPI PFNGLUSEPROGRAMSTAGESEXTPROC glad_glUseProgramStagesEXT;\n#define glUseProgramStagesEXT glad_glUseProgramStagesEXT\ntypedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEEXTPROC)(GLuint);\nGLAPI PFNGLVALIDATEPROGRAMPIPELINEEXTPROC glad_glValidateProgramPipelineEXT;\n#define glValidateProgramPipelineEXT glad_glValidateProgramPipelineEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC)(GLuint, GLint, GLuint);\nGLAPI PFNGLPROGRAMUNIFORM1UIEXTPROC glad_glProgramUniform1uiEXT;\n#define glProgramUniform1uiEXT glad_glProgramUniform1uiEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC)(GLuint, GLint, GLuint, GLuint);\nGLAPI PFNGLPROGRAMUNIFORM2UIEXTPROC glad_glProgramUniform2uiEXT;\n#define glProgramUniform2uiEXT glad_glProgramUniform2uiEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC)(GLuint, GLint, GLuint, GLuint, GLuint);\nGLAPI PFNGLPROGRAMUNIFORM3UIEXTPROC glad_glProgramUniform3uiEXT;\n#define glProgramUniform3uiEXT glad_glProgramUniform3uiEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC)(GLuint, GLint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLPROGRAMUNIFORM4UIEXTPROC glad_glProgramUniform4uiEXT;\n#define glProgramUniform4uiEXT glad_glProgramUniform4uiEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC)(GLuint, GLint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMUNIFORM1UIVEXTPROC glad_glProgramUniform1uivEXT;\n#define glProgramUniform1uivEXT glad_glProgramUniform1uivEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC)(GLuint, GLint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMUNIFORM2UIVEXTPROC glad_glProgramUniform2uivEXT;\n#define glProgramUniform2uivEXT glad_glProgramUniform2uivEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC)(GLuint, GLint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMUNIFORM3UIVEXTPROC glad_glProgramUniform3uivEXT;\n#define glProgramUniform3uivEXT glad_glProgramUniform3uivEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC)(GLuint, GLint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMUNIFORM4UIVEXTPROC glad_glProgramUniform4uivEXT;\n#define glProgramUniform4uivEXT glad_glProgramUniform4uivEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC glad_glProgramUniformMatrix2x3fvEXT;\n#define glProgramUniformMatrix2x3fvEXT glad_glProgramUniformMatrix2x3fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC glad_glProgramUniformMatrix3x2fvEXT;\n#define glProgramUniformMatrix3x2fvEXT glad_glProgramUniformMatrix3x2fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC glad_glProgramUniformMatrix2x4fvEXT;\n#define glProgramUniformMatrix2x4fvEXT glad_glProgramUniformMatrix2x4fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC glad_glProgramUniformMatrix4x2fvEXT;\n#define glProgramUniformMatrix4x2fvEXT glad_glProgramUniformMatrix4x2fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC glad_glProgramUniformMatrix3x4fvEXT;\n#define glProgramUniformMatrix3x4fvEXT glad_glProgramUniformMatrix3x4fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC glad_glProgramUniformMatrix4x3fvEXT;\n#define glProgramUniformMatrix4x3fvEXT glad_glProgramUniformMatrix4x3fvEXT\n#endif\n#ifndef GL_NV_texture_multisample\n#define GL_NV_texture_multisample 1\nGLAPI int GLAD_GL_NV_texture_multisample;\ntypedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC)(GLenum, GLsizei, GLsizei, GLint, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC glad_glTexImage2DMultisampleCoverageNV;\n#define glTexImage2DMultisampleCoverageNV glad_glTexImage2DMultisampleCoverageNV\ntypedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC)(GLenum, GLsizei, GLsizei, GLint, GLsizei, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC glad_glTexImage3DMultisampleCoverageNV;\n#define glTexImage3DMultisampleCoverageNV glad_glTexImage3DMultisampleCoverageNV\ntypedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC)(GLuint, GLenum, GLsizei, GLint, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC glad_glTextureImage2DMultisampleNV;\n#define glTextureImage2DMultisampleNV glad_glTextureImage2DMultisampleNV\ntypedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC)(GLuint, GLenum, GLsizei, GLint, GLsizei, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC glad_glTextureImage3DMultisampleNV;\n#define glTextureImage3DMultisampleNV glad_glTextureImage3DMultisampleNV\ntypedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC)(GLuint, GLenum, GLsizei, GLsizei, GLint, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC glad_glTextureImage2DMultisampleCoverageNV;\n#define glTextureImage2DMultisampleCoverageNV glad_glTextureImage2DMultisampleCoverageNV\ntypedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC)(GLuint, GLenum, GLsizei, GLsizei, GLint, GLsizei, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC glad_glTextureImage3DMultisampleCoverageNV;\n#define glTextureImage3DMultisampleCoverageNV glad_glTextureImage3DMultisampleCoverageNV\n#endif\n#ifndef GL_ARB_shader_objects\n#define GL_ARB_shader_objects 1\nGLAPI int GLAD_GL_ARB_shader_objects;\ntypedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC)(GLhandleARB);\nGLAPI PFNGLDELETEOBJECTARBPROC glad_glDeleteObjectARB;\n#define glDeleteObjectARB glad_glDeleteObjectARB\ntypedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC)(GLenum);\nGLAPI PFNGLGETHANDLEARBPROC glad_glGetHandleARB;\n#define glGetHandleARB glad_glGetHandleARB\ntypedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC)(GLhandleARB, GLhandleARB);\nGLAPI PFNGLDETACHOBJECTARBPROC glad_glDetachObjectARB;\n#define glDetachObjectARB glad_glDetachObjectARB\ntypedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC)(GLenum);\nGLAPI PFNGLCREATESHADEROBJECTARBPROC glad_glCreateShaderObjectARB;\n#define glCreateShaderObjectARB glad_glCreateShaderObjectARB\ntypedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC)(GLhandleARB, GLsizei, const GLcharARB**, const GLint*);\nGLAPI PFNGLSHADERSOURCEARBPROC glad_glShaderSourceARB;\n#define glShaderSourceARB glad_glShaderSourceARB\ntypedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC)(GLhandleARB);\nGLAPI PFNGLCOMPILESHADERARBPROC glad_glCompileShaderARB;\n#define glCompileShaderARB glad_glCompileShaderARB\ntypedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC)();\nGLAPI PFNGLCREATEPROGRAMOBJECTARBPROC glad_glCreateProgramObjectARB;\n#define glCreateProgramObjectARB glad_glCreateProgramObjectARB\ntypedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC)(GLhandleARB, GLhandleARB);\nGLAPI PFNGLATTACHOBJECTARBPROC glad_glAttachObjectARB;\n#define glAttachObjectARB glad_glAttachObjectARB\ntypedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC)(GLhandleARB);\nGLAPI PFNGLLINKPROGRAMARBPROC glad_glLinkProgramARB;\n#define glLinkProgramARB glad_glLinkProgramARB\ntypedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC)(GLhandleARB);\nGLAPI PFNGLUSEPROGRAMOBJECTARBPROC glad_glUseProgramObjectARB;\n#define glUseProgramObjectARB glad_glUseProgramObjectARB\ntypedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC)(GLhandleARB);\nGLAPI PFNGLVALIDATEPROGRAMARBPROC glad_glValidateProgramARB;\n#define glValidateProgramARB glad_glValidateProgramARB\ntypedef void (APIENTRYP PFNGLUNIFORM1FARBPROC)(GLint, GLfloat);\nGLAPI PFNGLUNIFORM1FARBPROC glad_glUniform1fARB;\n#define glUniform1fARB glad_glUniform1fARB\ntypedef void (APIENTRYP PFNGLUNIFORM2FARBPROC)(GLint, GLfloat, GLfloat);\nGLAPI PFNGLUNIFORM2FARBPROC glad_glUniform2fARB;\n#define glUniform2fARB glad_glUniform2fARB\ntypedef void (APIENTRYP PFNGLUNIFORM3FARBPROC)(GLint, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLUNIFORM3FARBPROC glad_glUniform3fARB;\n#define glUniform3fARB glad_glUniform3fARB\ntypedef void (APIENTRYP PFNGLUNIFORM4FARBPROC)(GLint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLUNIFORM4FARBPROC glad_glUniform4fARB;\n#define glUniform4fARB glad_glUniform4fARB\ntypedef void (APIENTRYP PFNGLUNIFORM1IARBPROC)(GLint, GLint);\nGLAPI PFNGLUNIFORM1IARBPROC glad_glUniform1iARB;\n#define glUniform1iARB glad_glUniform1iARB\ntypedef void (APIENTRYP PFNGLUNIFORM2IARBPROC)(GLint, GLint, GLint);\nGLAPI PFNGLUNIFORM2IARBPROC glad_glUniform2iARB;\n#define glUniform2iARB glad_glUniform2iARB\ntypedef void (APIENTRYP PFNGLUNIFORM3IARBPROC)(GLint, GLint, GLint, GLint);\nGLAPI PFNGLUNIFORM3IARBPROC glad_glUniform3iARB;\n#define glUniform3iARB glad_glUniform3iARB\ntypedef void (APIENTRYP PFNGLUNIFORM4IARBPROC)(GLint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLUNIFORM4IARBPROC glad_glUniform4iARB;\n#define glUniform4iARB glad_glUniform4iARB\ntypedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC)(GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLUNIFORM1FVARBPROC glad_glUniform1fvARB;\n#define glUniform1fvARB glad_glUniform1fvARB\ntypedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC)(GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLUNIFORM2FVARBPROC glad_glUniform2fvARB;\n#define glUniform2fvARB glad_glUniform2fvARB\ntypedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC)(GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLUNIFORM3FVARBPROC glad_glUniform3fvARB;\n#define glUniform3fvARB glad_glUniform3fvARB\ntypedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC)(GLint, GLsizei, const GLfloat*);\nGLAPI PFNGLUNIFORM4FVARBPROC glad_glUniform4fvARB;\n#define glUniform4fvARB glad_glUniform4fvARB\ntypedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC)(GLint, GLsizei, const GLint*);\nGLAPI PFNGLUNIFORM1IVARBPROC glad_glUniform1ivARB;\n#define glUniform1ivARB glad_glUniform1ivARB\ntypedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC)(GLint, GLsizei, const GLint*);\nGLAPI PFNGLUNIFORM2IVARBPROC glad_glUniform2ivARB;\n#define glUniform2ivARB glad_glUniform2ivARB\ntypedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC)(GLint, GLsizei, const GLint*);\nGLAPI PFNGLUNIFORM3IVARBPROC glad_glUniform3ivARB;\n#define glUniform3ivARB glad_glUniform3ivARB\ntypedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC)(GLint, GLsizei, const GLint*);\nGLAPI PFNGLUNIFORM4IVARBPROC glad_glUniform4ivARB;\n#define glUniform4ivARB glad_glUniform4ivARB\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX2FVARBPROC glad_glUniformMatrix2fvARB;\n#define glUniformMatrix2fvARB glad_glUniformMatrix2fvARB\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX3FVARBPROC glad_glUniformMatrix3fvARB;\n#define glUniformMatrix3fvARB glad_glUniformMatrix3fvARB\ntypedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC)(GLint, GLsizei, GLboolean, const GLfloat*);\nGLAPI PFNGLUNIFORMMATRIX4FVARBPROC glad_glUniformMatrix4fvARB;\n#define glUniformMatrix4fvARB glad_glUniformMatrix4fvARB\ntypedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC)(GLhandleARB, GLenum, GLfloat*);\nGLAPI PFNGLGETOBJECTPARAMETERFVARBPROC glad_glGetObjectParameterfvARB;\n#define glGetObjectParameterfvARB glad_glGetObjectParameterfvARB\ntypedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC)(GLhandleARB, GLenum, GLint*);\nGLAPI PFNGLGETOBJECTPARAMETERIVARBPROC glad_glGetObjectParameterivARB;\n#define glGetObjectParameterivARB glad_glGetObjectParameterivARB\ntypedef void (APIENTRYP PFNGLGETINFOLOGARBPROC)(GLhandleARB, GLsizei, GLsizei*, GLcharARB*);\nGLAPI PFNGLGETINFOLOGARBPROC glad_glGetInfoLogARB;\n#define glGetInfoLogARB glad_glGetInfoLogARB\ntypedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC)(GLhandleARB, GLsizei, GLsizei*, GLhandleARB*);\nGLAPI PFNGLGETATTACHEDOBJECTSARBPROC glad_glGetAttachedObjectsARB;\n#define glGetAttachedObjectsARB glad_glGetAttachedObjectsARB\ntypedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC)(GLhandleARB, const GLcharARB*);\nGLAPI PFNGLGETUNIFORMLOCATIONARBPROC glad_glGetUniformLocationARB;\n#define glGetUniformLocationARB glad_glGetUniformLocationARB\ntypedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC)(GLhandleARB, GLuint, GLsizei, GLsizei*, GLint*, GLenum*, GLcharARB*);\nGLAPI PFNGLGETACTIVEUNIFORMARBPROC glad_glGetActiveUniformARB;\n#define glGetActiveUniformARB glad_glGetActiveUniformARB\ntypedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC)(GLhandleARB, GLint, GLfloat*);\nGLAPI PFNGLGETUNIFORMFVARBPROC glad_glGetUniformfvARB;\n#define glGetUniformfvARB glad_glGetUniformfvARB\ntypedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC)(GLhandleARB, GLint, GLint*);\nGLAPI PFNGLGETUNIFORMIVARBPROC glad_glGetUniformivARB;\n#define glGetUniformivARB glad_glGetUniformivARB\ntypedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC)(GLhandleARB, GLsizei, GLsizei*, GLcharARB*);\nGLAPI PFNGLGETSHADERSOURCEARBPROC glad_glGetShaderSourceARB;\n#define glGetShaderSourceARB glad_glGetShaderSourceARB\n#endif\n#ifndef GL_ARB_framebuffer_object\n#define GL_ARB_framebuffer_object 1\nGLAPI int GLAD_GL_ARB_framebuffer_object;\n#endif\n#ifndef GL_ATI_envmap_bumpmap\n#define GL_ATI_envmap_bumpmap 1\nGLAPI int GLAD_GL_ATI_envmap_bumpmap;\ntypedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC)(GLenum, const GLint*);\nGLAPI PFNGLTEXBUMPPARAMETERIVATIPROC glad_glTexBumpParameterivATI;\n#define glTexBumpParameterivATI glad_glTexBumpParameterivATI\ntypedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLTEXBUMPPARAMETERFVATIPROC glad_glTexBumpParameterfvATI;\n#define glTexBumpParameterfvATI glad_glTexBumpParameterfvATI\ntypedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC)(GLenum, GLint*);\nGLAPI PFNGLGETTEXBUMPPARAMETERIVATIPROC glad_glGetTexBumpParameterivATI;\n#define glGetTexBumpParameterivATI glad_glGetTexBumpParameterivATI\ntypedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC)(GLenum, GLfloat*);\nGLAPI PFNGLGETTEXBUMPPARAMETERFVATIPROC glad_glGetTexBumpParameterfvATI;\n#define glGetTexBumpParameterfvATI glad_glGetTexBumpParameterfvATI\n#endif\n#ifndef GL_ARB_robust_buffer_access_behavior\n#define GL_ARB_robust_buffer_access_behavior 1\nGLAPI int GLAD_GL_ARB_robust_buffer_access_behavior;\n#endif\n#ifndef GL_ARB_shader_stencil_export\n#define GL_ARB_shader_stencil_export 1\nGLAPI int GLAD_GL_ARB_shader_stencil_export;\n#endif\n#ifndef GL_NV_texture_rectangle\n#define GL_NV_texture_rectangle 1\nGLAPI int GLAD_GL_NV_texture_rectangle;\n#endif\n#ifndef GL_ARB_enhanced_layouts\n#define GL_ARB_enhanced_layouts 1\nGLAPI int GLAD_GL_ARB_enhanced_layouts;\n#endif\n#ifndef GL_ARB_texture_rectangle\n#define GL_ARB_texture_rectangle 1\nGLAPI int GLAD_GL_ARB_texture_rectangle;\n#endif\n#ifndef GL_SGI_texture_color_table\n#define GL_SGI_texture_color_table 1\nGLAPI int GLAD_GL_SGI_texture_color_table;\n#endif\n#ifndef GL_ATI_map_object_buffer\n#define GL_ATI_map_object_buffer 1\nGLAPI int GLAD_GL_ATI_map_object_buffer;\ntypedef void* (APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC)(GLuint);\nGLAPI PFNGLMAPOBJECTBUFFERATIPROC glad_glMapObjectBufferATI;\n#define glMapObjectBufferATI glad_glMapObjectBufferATI\ntypedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC)(GLuint);\nGLAPI PFNGLUNMAPOBJECTBUFFERATIPROC glad_glUnmapObjectBufferATI;\n#define glUnmapObjectBufferATI glad_glUnmapObjectBufferATI\n#endif\n#ifndef GL_ARB_robustness\n#define GL_ARB_robustness 1\nGLAPI int GLAD_GL_ARB_robustness;\ntypedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC)();\nGLAPI PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB;\n#define glGetGraphicsResetStatusARB glad_glGetGraphicsResetStatusARB\ntypedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC)(GLenum, GLint, GLenum, GLenum, GLsizei, void*);\nGLAPI PFNGLGETNTEXIMAGEARBPROC glad_glGetnTexImageARB;\n#define glGetnTexImageARB glad_glGetnTexImageARB\ntypedef void (APIENTRYP PFNGLREADNPIXELSARBPROC)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLsizei, void*);\nGLAPI PFNGLREADNPIXELSARBPROC glad_glReadnPixelsARB;\n#define glReadnPixelsARB glad_glReadnPixelsARB\ntypedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC)(GLenum, GLint, GLsizei, void*);\nGLAPI PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_glGetnCompressedTexImageARB;\n#define glGetnCompressedTexImageARB glad_glGetnCompressedTexImageARB\ntypedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC)(GLuint, GLint, GLsizei, GLfloat*);\nGLAPI PFNGLGETNUNIFORMFVARBPROC glad_glGetnUniformfvARB;\n#define glGetnUniformfvARB glad_glGetnUniformfvARB\ntypedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC)(GLuint, GLint, GLsizei, GLint*);\nGLAPI PFNGLGETNUNIFORMIVARBPROC glad_glGetnUniformivARB;\n#define glGetnUniformivARB glad_glGetnUniformivARB\ntypedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC)(GLuint, GLint, GLsizei, GLuint*);\nGLAPI PFNGLGETNUNIFORMUIVARBPROC glad_glGetnUniformuivARB;\n#define glGetnUniformuivARB glad_glGetnUniformuivARB\ntypedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC)(GLuint, GLint, GLsizei, GLdouble*);\nGLAPI PFNGLGETNUNIFORMDVARBPROC glad_glGetnUniformdvARB;\n#define glGetnUniformdvARB glad_glGetnUniformdvARB\ntypedef void (APIENTRYP PFNGLGETNMAPDVARBPROC)(GLenum, GLenum, GLsizei, GLdouble*);\nGLAPI PFNGLGETNMAPDVARBPROC glad_glGetnMapdvARB;\n#define glGetnMapdvARB glad_glGetnMapdvARB\ntypedef void (APIENTRYP PFNGLGETNMAPFVARBPROC)(GLenum, GLenum, GLsizei, GLfloat*);\nGLAPI PFNGLGETNMAPFVARBPROC glad_glGetnMapfvARB;\n#define glGetnMapfvARB glad_glGetnMapfvARB\ntypedef void (APIENTRYP PFNGLGETNMAPIVARBPROC)(GLenum, GLenum, GLsizei, GLint*);\nGLAPI PFNGLGETNMAPIVARBPROC glad_glGetnMapivARB;\n#define glGetnMapivARB glad_glGetnMapivARB\ntypedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC)(GLenum, GLsizei, GLfloat*);\nGLAPI PFNGLGETNPIXELMAPFVARBPROC glad_glGetnPixelMapfvARB;\n#define glGetnPixelMapfvARB glad_glGetnPixelMapfvARB\ntypedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC)(GLenum, GLsizei, GLuint*);\nGLAPI PFNGLGETNPIXELMAPUIVARBPROC glad_glGetnPixelMapuivARB;\n#define glGetnPixelMapuivARB glad_glGetnPixelMapuivARB\ntypedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC)(GLenum, GLsizei, GLushort*);\nGLAPI PFNGLGETNPIXELMAPUSVARBPROC glad_glGetnPixelMapusvARB;\n#define glGetnPixelMapusvARB glad_glGetnPixelMapusvARB\ntypedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC)(GLsizei, GLubyte*);\nGLAPI PFNGLGETNPOLYGONSTIPPLEARBPROC glad_glGetnPolygonStippleARB;\n#define glGetnPolygonStippleARB glad_glGetnPolygonStippleARB\ntypedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC)(GLenum, GLenum, GLenum, GLsizei, void*);\nGLAPI PFNGLGETNCOLORTABLEARBPROC glad_glGetnColorTableARB;\n#define glGetnColorTableARB glad_glGetnColorTableARB\ntypedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC)(GLenum, GLenum, GLenum, GLsizei, void*);\nGLAPI PFNGLGETNCONVOLUTIONFILTERARBPROC glad_glGetnConvolutionFilterARB;\n#define glGetnConvolutionFilterARB glad_glGetnConvolutionFilterARB\ntypedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC)(GLenum, GLenum, GLenum, GLsizei, void*, GLsizei, void*, void*);\nGLAPI PFNGLGETNSEPARABLEFILTERARBPROC glad_glGetnSeparableFilterARB;\n#define glGetnSeparableFilterARB glad_glGetnSeparableFilterARB\ntypedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC)(GLenum, GLboolean, GLenum, GLenum, GLsizei, void*);\nGLAPI PFNGLGETNHISTOGRAMARBPROC glad_glGetnHistogramARB;\n#define glGetnHistogramARB glad_glGetnHistogramARB\ntypedef void (APIENTRYP PFNGLGETNMINMAXARBPROC)(GLenum, GLboolean, GLenum, GLenum, GLsizei, void*);\nGLAPI PFNGLGETNMINMAXARBPROC glad_glGetnMinmaxARB;\n#define glGetnMinmaxARB glad_glGetnMinmaxARB\n#endif\n#ifndef GL_NV_pixel_data_range\n#define GL_NV_pixel_data_range 1\nGLAPI int GLAD_GL_NV_pixel_data_range;\ntypedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC)(GLenum, GLsizei, const void*);\nGLAPI PFNGLPIXELDATARANGENVPROC glad_glPixelDataRangeNV;\n#define glPixelDataRangeNV glad_glPixelDataRangeNV\ntypedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC)(GLenum);\nGLAPI PFNGLFLUSHPIXELDATARANGENVPROC glad_glFlushPixelDataRangeNV;\n#define glFlushPixelDataRangeNV glad_glFlushPixelDataRangeNV\n#endif\n#ifndef GL_EXT_framebuffer_blit\n#define GL_EXT_framebuffer_blit 1\nGLAPI int GLAD_GL_EXT_framebuffer_blit;\ntypedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC)(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum);\nGLAPI PFNGLBLITFRAMEBUFFEREXTPROC glad_glBlitFramebufferEXT;\n#define glBlitFramebufferEXT glad_glBlitFramebufferEXT\n#endif\n#ifndef GL_ARB_gpu_shader_fp64\n#define GL_ARB_gpu_shader_fp64 1\nGLAPI int GLAD_GL_ARB_gpu_shader_fp64;\n#endif\n#ifndef GL_SGIX_depth_texture\n#define GL_SGIX_depth_texture 1\nGLAPI int GLAD_GL_SGIX_depth_texture;\n#endif\n#ifndef GL_EXT_vertex_weighting\n#define GL_EXT_vertex_weighting 1\nGLAPI int GLAD_GL_EXT_vertex_weighting;\ntypedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC)(GLfloat);\nGLAPI PFNGLVERTEXWEIGHTFEXTPROC glad_glVertexWeightfEXT;\n#define glVertexWeightfEXT glad_glVertexWeightfEXT\ntypedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC)(const GLfloat*);\nGLAPI PFNGLVERTEXWEIGHTFVEXTPROC glad_glVertexWeightfvEXT;\n#define glVertexWeightfvEXT glad_glVertexWeightfvEXT\ntypedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC)(GLint, GLenum, GLsizei, const void*);\nGLAPI PFNGLVERTEXWEIGHTPOINTEREXTPROC glad_glVertexWeightPointerEXT;\n#define glVertexWeightPointerEXT glad_glVertexWeightPointerEXT\n#endif\n#ifndef GL_GREMEDY_string_marker\n#define GL_GREMEDY_string_marker 1\nGLAPI int GLAD_GL_GREMEDY_string_marker;\ntypedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC)(GLsizei, const void*);\nGLAPI PFNGLSTRINGMARKERGREMEDYPROC glad_glStringMarkerGREMEDY;\n#define glStringMarkerGREMEDY glad_glStringMarkerGREMEDY\n#endif\n#ifndef GL_ARB_texture_compression_bptc\n#define GL_ARB_texture_compression_bptc 1\nGLAPI int GLAD_GL_ARB_texture_compression_bptc;\n#endif\n#ifndef GL_EXT_subtexture\n#define GL_EXT_subtexture 1\nGLAPI int GLAD_GL_EXT_subtexture;\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC)(GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXSUBIMAGE1DEXTPROC glad_glTexSubImage1DEXT;\n#define glTexSubImage1DEXT glad_glTexSubImage1DEXT\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXSUBIMAGE2DEXTPROC glad_glTexSubImage2DEXT;\n#define glTexSubImage2DEXT glad_glTexSubImage2DEXT\n#endif\n#ifndef GL_EXT_pixel_transform_color_table\n#define GL_EXT_pixel_transform_color_table 1\nGLAPI int GLAD_GL_EXT_pixel_transform_color_table;\n#endif\n#ifndef GL_EXT_texture_compression_rgtc\n#define GL_EXT_texture_compression_rgtc 1\nGLAPI int GLAD_GL_EXT_texture_compression_rgtc;\n#endif\n#ifndef GL_SGIX_depth_pass_instrument\n#define GL_SGIX_depth_pass_instrument 1\nGLAPI int GLAD_GL_SGIX_depth_pass_instrument;\n#endif\n#ifndef GL_ARB_shader_precision\n#define GL_ARB_shader_precision 1\nGLAPI int GLAD_GL_ARB_shader_precision;\n#endif\n#ifndef GL_NV_evaluators\n#define GL_NV_evaluators 1\nGLAPI int GLAD_GL_NV_evaluators;\ntypedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC)(GLenum, GLuint, GLenum, GLsizei, GLsizei, GLint, GLint, GLboolean, const void*);\nGLAPI PFNGLMAPCONTROLPOINTSNVPROC glad_glMapControlPointsNV;\n#define glMapControlPointsNV glad_glMapControlPointsNV\ntypedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLMAPPARAMETERIVNVPROC glad_glMapParameterivNV;\n#define glMapParameterivNV glad_glMapParameterivNV\ntypedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLMAPPARAMETERFVNVPROC glad_glMapParameterfvNV;\n#define glMapParameterfvNV glad_glMapParameterfvNV\ntypedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC)(GLenum, GLuint, GLenum, GLsizei, GLsizei, GLboolean, void*);\nGLAPI PFNGLGETMAPCONTROLPOINTSNVPROC glad_glGetMapControlPointsNV;\n#define glGetMapControlPointsNV glad_glGetMapControlPointsNV\ntypedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETMAPPARAMETERIVNVPROC glad_glGetMapParameterivNV;\n#define glGetMapParameterivNV glad_glGetMapParameterivNV\ntypedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETMAPPARAMETERFVNVPROC glad_glGetMapParameterfvNV;\n#define glGetMapParameterfvNV glad_glGetMapParameterfvNV\ntypedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC)(GLenum, GLuint, GLenum, GLint*);\nGLAPI PFNGLGETMAPATTRIBPARAMETERIVNVPROC glad_glGetMapAttribParameterivNV;\n#define glGetMapAttribParameterivNV glad_glGetMapAttribParameterivNV\ntypedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC)(GLenum, GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETMAPATTRIBPARAMETERFVNVPROC glad_glGetMapAttribParameterfvNV;\n#define glGetMapAttribParameterfvNV glad_glGetMapAttribParameterfvNV\ntypedef void (APIENTRYP PFNGLEVALMAPSNVPROC)(GLenum, GLenum);\nGLAPI PFNGLEVALMAPSNVPROC glad_glEvalMapsNV;\n#define glEvalMapsNV glad_glEvalMapsNV\n#endif\n#ifndef GL_SGIS_texture_filter4\n#define GL_SGIS_texture_filter4 1\nGLAPI int GLAD_GL_SGIS_texture_filter4;\ntypedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETTEXFILTERFUNCSGISPROC glad_glGetTexFilterFuncSGIS;\n#define glGetTexFilterFuncSGIS glad_glGetTexFilterFuncSGIS\ntypedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC)(GLenum, GLenum, GLsizei, const GLfloat*);\nGLAPI PFNGLTEXFILTERFUNCSGISPROC glad_glTexFilterFuncSGIS;\n#define glTexFilterFuncSGIS glad_glTexFilterFuncSGIS\n#endif\n#ifndef GL_AMD_performance_monitor\n#define GL_AMD_performance_monitor 1\nGLAPI int GLAD_GL_AMD_performance_monitor;\ntypedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC)(GLint*, GLsizei, GLuint*);\nGLAPI PFNGLGETPERFMONITORGROUPSAMDPROC glad_glGetPerfMonitorGroupsAMD;\n#define glGetPerfMonitorGroupsAMD glad_glGetPerfMonitorGroupsAMD\ntypedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC)(GLuint, GLint*, GLint*, GLsizei, GLuint*);\nGLAPI PFNGLGETPERFMONITORCOUNTERSAMDPROC glad_glGetPerfMonitorCountersAMD;\n#define glGetPerfMonitorCountersAMD glad_glGetPerfMonitorCountersAMD\ntypedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC)(GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETPERFMONITORGROUPSTRINGAMDPROC glad_glGetPerfMonitorGroupStringAMD;\n#define glGetPerfMonitorGroupStringAMD glad_glGetPerfMonitorGroupStringAMD\ntypedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC glad_glGetPerfMonitorCounterStringAMD;\n#define glGetPerfMonitorCounterStringAMD glad_glGetPerfMonitorCounterStringAMD\ntypedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC)(GLuint, GLuint, GLenum, void*);\nGLAPI PFNGLGETPERFMONITORCOUNTERINFOAMDPROC glad_glGetPerfMonitorCounterInfoAMD;\n#define glGetPerfMonitorCounterInfoAMD glad_glGetPerfMonitorCounterInfoAMD\ntypedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENPERFMONITORSAMDPROC glad_glGenPerfMonitorsAMD;\n#define glGenPerfMonitorsAMD glad_glGenPerfMonitorsAMD\ntypedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC)(GLsizei, GLuint*);\nGLAPI PFNGLDELETEPERFMONITORSAMDPROC glad_glDeletePerfMonitorsAMD;\n#define glDeletePerfMonitorsAMD glad_glDeletePerfMonitorsAMD\ntypedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC)(GLuint, GLboolean, GLuint, GLint, GLuint*);\nGLAPI PFNGLSELECTPERFMONITORCOUNTERSAMDPROC glad_glSelectPerfMonitorCountersAMD;\n#define glSelectPerfMonitorCountersAMD glad_glSelectPerfMonitorCountersAMD\ntypedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC)(GLuint);\nGLAPI PFNGLBEGINPERFMONITORAMDPROC glad_glBeginPerfMonitorAMD;\n#define glBeginPerfMonitorAMD glad_glBeginPerfMonitorAMD\ntypedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC)(GLuint);\nGLAPI PFNGLENDPERFMONITORAMDPROC glad_glEndPerfMonitorAMD;\n#define glEndPerfMonitorAMD glad_glEndPerfMonitorAMD\ntypedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC)(GLuint, GLenum, GLsizei, GLuint*, GLint*);\nGLAPI PFNGLGETPERFMONITORCOUNTERDATAAMDPROC glad_glGetPerfMonitorCounterDataAMD;\n#define glGetPerfMonitorCounterDataAMD glad_glGetPerfMonitorCounterDataAMD\n#endif\n#ifndef GL_NV_geometry_shader4\n#define GL_NV_geometry_shader4 1\nGLAPI int GLAD_GL_NV_geometry_shader4;\n#endif\n#ifndef GL_EXT_stencil_clear_tag\n#define GL_EXT_stencil_clear_tag 1\nGLAPI int GLAD_GL_EXT_stencil_clear_tag;\ntypedef void (APIENTRYP PFNGLSTENCILCLEARTAGEXTPROC)(GLsizei, GLuint);\nGLAPI PFNGLSTENCILCLEARTAGEXTPROC glad_glStencilClearTagEXT;\n#define glStencilClearTagEXT glad_glStencilClearTagEXT\n#endif\n#ifndef GL_NV_vertex_program1_1\n#define GL_NV_vertex_program1_1 1\nGLAPI int GLAD_GL_NV_vertex_program1_1;\n#endif\n#ifndef GL_NV_present_video\n#define GL_NV_present_video 1\nGLAPI int GLAD_GL_NV_present_video;\ntypedef void (APIENTRYP PFNGLPRESENTFRAMEKEYEDNVPROC)(GLuint, GLuint64EXT, GLuint, GLuint, GLenum, GLenum, GLuint, GLuint, GLenum, GLuint, GLuint);\nGLAPI PFNGLPRESENTFRAMEKEYEDNVPROC glad_glPresentFrameKeyedNV;\n#define glPresentFrameKeyedNV glad_glPresentFrameKeyedNV\ntypedef void (APIENTRYP PFNGLPRESENTFRAMEDUALFILLNVPROC)(GLuint, GLuint64EXT, GLuint, GLuint, GLenum, GLenum, GLuint, GLenum, GLuint, GLenum, GLuint, GLenum, GLuint);\nGLAPI PFNGLPRESENTFRAMEDUALFILLNVPROC glad_glPresentFrameDualFillNV;\n#define glPresentFrameDualFillNV glad_glPresentFrameDualFillNV\ntypedef void (APIENTRYP PFNGLGETVIDEOIVNVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVIDEOIVNVPROC glad_glGetVideoivNV;\n#define glGetVideoivNV glad_glGetVideoivNV\ntypedef void (APIENTRYP PFNGLGETVIDEOUIVNVPROC)(GLuint, GLenum, GLuint*);\nGLAPI PFNGLGETVIDEOUIVNVPROC glad_glGetVideouivNV;\n#define glGetVideouivNV glad_glGetVideouivNV\ntypedef void (APIENTRYP PFNGLGETVIDEOI64VNVPROC)(GLuint, GLenum, GLint64EXT*);\nGLAPI PFNGLGETVIDEOI64VNVPROC glad_glGetVideoi64vNV;\n#define glGetVideoi64vNV glad_glGetVideoi64vNV\ntypedef void (APIENTRYP PFNGLGETVIDEOUI64VNVPROC)(GLuint, GLenum, GLuint64EXT*);\nGLAPI PFNGLGETVIDEOUI64VNVPROC glad_glGetVideoui64vNV;\n#define glGetVideoui64vNV glad_glGetVideoui64vNV\n#endif\n#ifndef GL_ARB_texture_compression_rgtc\n#define GL_ARB_texture_compression_rgtc 1\nGLAPI int GLAD_GL_ARB_texture_compression_rgtc;\n#endif\n#ifndef GL_HP_convolution_border_modes\n#define GL_HP_convolution_border_modes 1\nGLAPI int GLAD_GL_HP_convolution_border_modes;\n#endif\n#ifndef GL_EXT_gpu_program_parameters\n#define GL_EXT_gpu_program_parameters 1\nGLAPI int GLAD_GL_EXT_gpu_program_parameters;\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC)(GLenum, GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMENVPARAMETERS4FVEXTPROC glad_glProgramEnvParameters4fvEXT;\n#define glProgramEnvParameters4fvEXT glad_glProgramEnvParameters4fvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC)(GLenum, GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC glad_glProgramLocalParameters4fvEXT;\n#define glProgramLocalParameters4fvEXT glad_glProgramLocalParameters4fvEXT\n#endif\n#ifndef GL_EXT_shader_integer_mix\n#define GL_EXT_shader_integer_mix 1\nGLAPI int GLAD_GL_EXT_shader_integer_mix;\n#endif\n#ifndef GL_ARB_stencil_texturing\n#define GL_ARB_stencil_texturing 1\nGLAPI int GLAD_GL_ARB_stencil_texturing;\n#endif\n#ifndef GL_SGIX_fog_offset\n#define GL_SGIX_fog_offset 1\nGLAPI int GLAD_GL_SGIX_fog_offset;\n#endif\n#ifndef GL_ARB_draw_elements_base_vertex\n#define GL_ARB_draw_elements_base_vertex 1\nGLAPI int GLAD_GL_ARB_draw_elements_base_vertex;\n#endif\n#ifndef GL_INGR_interlace_read\n#define GL_INGR_interlace_read 1\nGLAPI int GLAD_GL_INGR_interlace_read;\n#endif\n#ifndef GL_NV_transform_feedback\n#define GL_NV_transform_feedback 1\nGLAPI int GLAD_GL_NV_transform_feedback;\ntypedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKNVPROC)(GLenum);\nGLAPI PFNGLBEGINTRANSFORMFEEDBACKNVPROC glad_glBeginTransformFeedbackNV;\n#define glBeginTransformFeedbackNV glad_glBeginTransformFeedbackNV\ntypedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKNVPROC)();\nGLAPI PFNGLENDTRANSFORMFEEDBACKNVPROC glad_glEndTransformFeedbackNV;\n#define glEndTransformFeedbackNV glad_glEndTransformFeedbackNV\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC)(GLuint, const GLint*, GLenum);\nGLAPI PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC glad_glTransformFeedbackAttribsNV;\n#define glTransformFeedbackAttribsNV glad_glTransformFeedbackAttribsNV\ntypedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC)(GLenum, GLuint, GLuint, GLintptr, GLsizeiptr);\nGLAPI PFNGLBINDBUFFERRANGENVPROC glad_glBindBufferRangeNV;\n#define glBindBufferRangeNV glad_glBindBufferRangeNV\ntypedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC)(GLenum, GLuint, GLuint, GLintptr);\nGLAPI PFNGLBINDBUFFEROFFSETNVPROC glad_glBindBufferOffsetNV;\n#define glBindBufferOffsetNV glad_glBindBufferOffsetNV\ntypedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC)(GLenum, GLuint, GLuint);\nGLAPI PFNGLBINDBUFFERBASENVPROC glad_glBindBufferBaseNV;\n#define glBindBufferBaseNV glad_glBindBufferBaseNV\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC)(GLuint, GLsizei, const GLint*, GLenum);\nGLAPI PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC glad_glTransformFeedbackVaryingsNV;\n#define glTransformFeedbackVaryingsNV glad_glTransformFeedbackVaryingsNV\ntypedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC)(GLuint, const GLchar*);\nGLAPI PFNGLACTIVEVARYINGNVPROC glad_glActiveVaryingNV;\n#define glActiveVaryingNV glad_glActiveVaryingNV\ntypedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC)(GLuint, const GLchar*);\nGLAPI PFNGLGETVARYINGLOCATIONNVPROC glad_glGetVaryingLocationNV;\n#define glGetVaryingLocationNV glad_glGetVaryingLocationNV\ntypedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLsizei*, GLenum*, GLchar*);\nGLAPI PFNGLGETACTIVEVARYINGNVPROC glad_glGetActiveVaryingNV;\n#define glGetActiveVaryingNV glad_glGetActiveVaryingNV\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC)(GLuint, GLuint, GLint*);\nGLAPI PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC glad_glGetTransformFeedbackVaryingNV;\n#define glGetTransformFeedbackVaryingNV glad_glGetTransformFeedbackVaryingNV\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC)(GLsizei, const GLint*, GLsizei, const GLint*, GLenum);\nGLAPI PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC glad_glTransformFeedbackStreamAttribsNV;\n#define glTransformFeedbackStreamAttribsNV glad_glTransformFeedbackStreamAttribsNV\n#endif\n#ifndef GL_NV_fragment_program\n#define GL_NV_fragment_program 1\nGLAPI int GLAD_GL_NV_fragment_program;\ntypedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC)(GLuint, GLsizei, const GLubyte*, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLPROGRAMNAMEDPARAMETER4FNVPROC glad_glProgramNamedParameter4fNV;\n#define glProgramNamedParameter4fNV glad_glProgramNamedParameter4fNV\ntypedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC)(GLuint, GLsizei, const GLubyte*, const GLfloat*);\nGLAPI PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC glad_glProgramNamedParameter4fvNV;\n#define glProgramNamedParameter4fvNV glad_glProgramNamedParameter4fvNV\ntypedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC)(GLuint, GLsizei, const GLubyte*, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLPROGRAMNAMEDPARAMETER4DNVPROC glad_glProgramNamedParameter4dNV;\n#define glProgramNamedParameter4dNV glad_glProgramNamedParameter4dNV\ntypedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC)(GLuint, GLsizei, const GLubyte*, const GLdouble*);\nGLAPI PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC glad_glProgramNamedParameter4dvNV;\n#define glProgramNamedParameter4dvNV glad_glProgramNamedParameter4dvNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC)(GLuint, GLsizei, const GLubyte*, GLfloat*);\nGLAPI PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC glad_glGetProgramNamedParameterfvNV;\n#define glGetProgramNamedParameterfvNV glad_glGetProgramNamedParameterfvNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC)(GLuint, GLsizei, const GLubyte*, GLdouble*);\nGLAPI PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC glad_glGetProgramNamedParameterdvNV;\n#define glGetProgramNamedParameterdvNV glad_glGetProgramNamedParameterdvNV\n#endif\n#ifndef GL_AMD_stencil_operation_extended\n#define GL_AMD_stencil_operation_extended 1\nGLAPI int GLAD_GL_AMD_stencil_operation_extended;\ntypedef void (APIENTRYP PFNGLSTENCILOPVALUEAMDPROC)(GLenum, GLuint);\nGLAPI PFNGLSTENCILOPVALUEAMDPROC glad_glStencilOpValueAMD;\n#define glStencilOpValueAMD glad_glStencilOpValueAMD\n#endif\n#ifndef GL_ARB_seamless_cubemap_per_texture\n#define GL_ARB_seamless_cubemap_per_texture 1\nGLAPI int GLAD_GL_ARB_seamless_cubemap_per_texture;\n#endif\n#ifndef GL_ARB_instanced_arrays\n#define GL_ARB_instanced_arrays 1\nGLAPI int GLAD_GL_ARB_instanced_arrays;\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC)(GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBDIVISORARBPROC glad_glVertexAttribDivisorARB;\n#define glVertexAttribDivisorARB glad_glVertexAttribDivisorARB\n#endif\n#ifndef GL_EXT_polygon_offset\n#define GL_EXT_polygon_offset 1\nGLAPI int GLAD_GL_EXT_polygon_offset;\ntypedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC)(GLfloat, GLfloat);\nGLAPI PFNGLPOLYGONOFFSETEXTPROC glad_glPolygonOffsetEXT;\n#define glPolygonOffsetEXT glad_glPolygonOffsetEXT\n#endif\n#ifndef GL_NV_vertex_array_range2\n#define GL_NV_vertex_array_range2 1\nGLAPI int GLAD_GL_NV_vertex_array_range2;\n#endif\n#ifndef GL_AMD_sparse_texture\n#define GL_AMD_sparse_texture 1\nGLAPI int GLAD_GL_AMD_sparse_texture;\ntypedef void (APIENTRYP PFNGLTEXSTORAGESPARSEAMDPROC)(GLenum, GLenum, GLsizei, GLsizei, GLsizei, GLsizei, GLbitfield);\nGLAPI PFNGLTEXSTORAGESPARSEAMDPROC glad_glTexStorageSparseAMD;\n#define glTexStorageSparseAMD glad_glTexStorageSparseAMD\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGESPARSEAMDPROC)(GLuint, GLenum, GLenum, GLsizei, GLsizei, GLsizei, GLsizei, GLbitfield);\nGLAPI PFNGLTEXTURESTORAGESPARSEAMDPROC glad_glTextureStorageSparseAMD;\n#define glTextureStorageSparseAMD glad_glTextureStorageSparseAMD\n#endif\n#ifndef GL_NV_fence\n#define GL_NV_fence 1\nGLAPI int GLAD_GL_NV_fence;\ntypedef void (APIENTRYP PFNGLDELETEFENCESNVPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEFENCESNVPROC glad_glDeleteFencesNV;\n#define glDeleteFencesNV glad_glDeleteFencesNV\ntypedef void (APIENTRYP PFNGLGENFENCESNVPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENFENCESNVPROC glad_glGenFencesNV;\n#define glGenFencesNV glad_glGenFencesNV\ntypedef GLboolean (APIENTRYP PFNGLISFENCENVPROC)(GLuint);\nGLAPI PFNGLISFENCENVPROC glad_glIsFenceNV;\n#define glIsFenceNV glad_glIsFenceNV\ntypedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC)(GLuint);\nGLAPI PFNGLTESTFENCENVPROC glad_glTestFenceNV;\n#define glTestFenceNV glad_glTestFenceNV\ntypedef void (APIENTRYP PFNGLGETFENCEIVNVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETFENCEIVNVPROC glad_glGetFenceivNV;\n#define glGetFenceivNV glad_glGetFenceivNV\ntypedef void (APIENTRYP PFNGLFINISHFENCENVPROC)(GLuint);\nGLAPI PFNGLFINISHFENCENVPROC glad_glFinishFenceNV;\n#define glFinishFenceNV glad_glFinishFenceNV\ntypedef void (APIENTRYP PFNGLSETFENCENVPROC)(GLuint, GLenum);\nGLAPI PFNGLSETFENCENVPROC glad_glSetFenceNV;\n#define glSetFenceNV glad_glSetFenceNV\n#endif\n#ifndef GL_ARB_texture_buffer_range\n#define GL_ARB_texture_buffer_range 1\nGLAPI int GLAD_GL_ARB_texture_buffer_range;\n#endif\n#ifndef GL_SUN_mesh_array\n#define GL_SUN_mesh_array 1\nGLAPI int GLAD_GL_SUN_mesh_array;\ntypedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC)(GLenum, GLint, GLsizei, GLsizei);\nGLAPI PFNGLDRAWMESHARRAYSSUNPROC glad_glDrawMeshArraysSUN;\n#define glDrawMeshArraysSUN glad_glDrawMeshArraysSUN\n#endif\n#ifndef GL_ARB_vertex_attrib_binding\n#define GL_ARB_vertex_attrib_binding 1\nGLAPI int GLAD_GL_ARB_vertex_attrib_binding;\n#endif\n#ifndef GL_ARB_framebuffer_no_attachments\n#define GL_ARB_framebuffer_no_attachments 1\nGLAPI int GLAD_GL_ARB_framebuffer_no_attachments;\n#endif\n#ifndef GL_ARB_cl_event\n#define GL_ARB_cl_event 1\nGLAPI int GLAD_GL_ARB_cl_event;\ntypedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC)(struct _cl_context*, struct _cl_event*, GLbitfield);\nGLAPI PFNGLCREATESYNCFROMCLEVENTARBPROC glad_glCreateSyncFromCLeventARB;\n#define glCreateSyncFromCLeventARB glad_glCreateSyncFromCLeventARB\n#endif\n#ifndef GL_NV_packed_depth_stencil\n#define GL_NV_packed_depth_stencil 1\nGLAPI int GLAD_GL_NV_packed_depth_stencil;\n#endif\n#ifndef GL_OES_single_precision\n#define GL_OES_single_precision 1\nGLAPI int GLAD_GL_OES_single_precision;\ntypedef void (APIENTRYP PFNGLCLEARDEPTHFOESPROC)(GLclampf);\nGLAPI PFNGLCLEARDEPTHFOESPROC glad_glClearDepthfOES;\n#define glClearDepthfOES glad_glClearDepthfOES\ntypedef void (APIENTRYP PFNGLCLIPPLANEFOESPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLCLIPPLANEFOESPROC glad_glClipPlanefOES;\n#define glClipPlanefOES glad_glClipPlanefOES\ntypedef void (APIENTRYP PFNGLDEPTHRANGEFOESPROC)(GLclampf, GLclampf);\nGLAPI PFNGLDEPTHRANGEFOESPROC glad_glDepthRangefOES;\n#define glDepthRangefOES glad_glDepthRangefOES\ntypedef void (APIENTRYP PFNGLFRUSTUMFOESPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLFRUSTUMFOESPROC glad_glFrustumfOES;\n#define glFrustumfOES glad_glFrustumfOES\ntypedef void (APIENTRYP PFNGLGETCLIPPLANEFOESPROC)(GLenum, GLfloat*);\nGLAPI PFNGLGETCLIPPLANEFOESPROC glad_glGetClipPlanefOES;\n#define glGetClipPlanefOES glad_glGetClipPlanefOES\ntypedef void (APIENTRYP PFNGLORTHOFOESPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLORTHOFOESPROC glad_glOrthofOES;\n#define glOrthofOES glad_glOrthofOES\n#endif\n#ifndef GL_NV_primitive_restart\n#define GL_NV_primitive_restart 1\nGLAPI int GLAD_GL_NV_primitive_restart;\ntypedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC)();\nGLAPI PFNGLPRIMITIVERESTARTNVPROC glad_glPrimitiveRestartNV;\n#define glPrimitiveRestartNV glad_glPrimitiveRestartNV\ntypedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC)(GLuint);\nGLAPI PFNGLPRIMITIVERESTARTINDEXNVPROC glad_glPrimitiveRestartIndexNV;\n#define glPrimitiveRestartIndexNV glad_glPrimitiveRestartIndexNV\n#endif\n#ifndef GL_SUN_global_alpha\n#define GL_SUN_global_alpha 1\nGLAPI int GLAD_GL_SUN_global_alpha;\ntypedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC)(GLbyte);\nGLAPI PFNGLGLOBALALPHAFACTORBSUNPROC glad_glGlobalAlphaFactorbSUN;\n#define glGlobalAlphaFactorbSUN glad_glGlobalAlphaFactorbSUN\ntypedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC)(GLshort);\nGLAPI PFNGLGLOBALALPHAFACTORSSUNPROC glad_glGlobalAlphaFactorsSUN;\n#define glGlobalAlphaFactorsSUN glad_glGlobalAlphaFactorsSUN\ntypedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC)(GLint);\nGLAPI PFNGLGLOBALALPHAFACTORISUNPROC glad_glGlobalAlphaFactoriSUN;\n#define glGlobalAlphaFactoriSUN glad_glGlobalAlphaFactoriSUN\ntypedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC)(GLfloat);\nGLAPI PFNGLGLOBALALPHAFACTORFSUNPROC glad_glGlobalAlphaFactorfSUN;\n#define glGlobalAlphaFactorfSUN glad_glGlobalAlphaFactorfSUN\ntypedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC)(GLdouble);\nGLAPI PFNGLGLOBALALPHAFACTORDSUNPROC glad_glGlobalAlphaFactordSUN;\n#define glGlobalAlphaFactordSUN glad_glGlobalAlphaFactordSUN\ntypedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC)(GLubyte);\nGLAPI PFNGLGLOBALALPHAFACTORUBSUNPROC glad_glGlobalAlphaFactorubSUN;\n#define glGlobalAlphaFactorubSUN glad_glGlobalAlphaFactorubSUN\ntypedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC)(GLushort);\nGLAPI PFNGLGLOBALALPHAFACTORUSSUNPROC glad_glGlobalAlphaFactorusSUN;\n#define glGlobalAlphaFactorusSUN glad_glGlobalAlphaFactorusSUN\ntypedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC)(GLuint);\nGLAPI PFNGLGLOBALALPHAFACTORUISUNPROC glad_glGlobalAlphaFactoruiSUN;\n#define glGlobalAlphaFactoruiSUN glad_glGlobalAlphaFactoruiSUN\n#endif\n#ifndef GL_EXT_texture_object\n#define GL_EXT_texture_object 1\nGLAPI int GLAD_GL_EXT_texture_object;\ntypedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC)(GLsizei, const GLuint*, GLboolean*);\nGLAPI PFNGLARETEXTURESRESIDENTEXTPROC glad_glAreTexturesResidentEXT;\n#define glAreTexturesResidentEXT glad_glAreTexturesResidentEXT\ntypedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDTEXTUREEXTPROC glad_glBindTextureEXT;\n#define glBindTextureEXT glad_glBindTextureEXT\ntypedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETETEXTURESEXTPROC glad_glDeleteTexturesEXT;\n#define glDeleteTexturesEXT glad_glDeleteTexturesEXT\ntypedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENTEXTURESEXTPROC glad_glGenTexturesEXT;\n#define glGenTexturesEXT glad_glGenTexturesEXT\ntypedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC)(GLuint);\nGLAPI PFNGLISTEXTUREEXTPROC glad_glIsTextureEXT;\n#define glIsTextureEXT glad_glIsTextureEXT\ntypedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC)(GLsizei, const GLuint*, const GLclampf*);\nGLAPI PFNGLPRIORITIZETEXTURESEXTPROC glad_glPrioritizeTexturesEXT;\n#define glPrioritizeTexturesEXT glad_glPrioritizeTexturesEXT\n#endif\n#ifndef GL_AMD_name_gen_delete\n#define GL_AMD_name_gen_delete 1\nGLAPI int GLAD_GL_AMD_name_gen_delete;\ntypedef void (APIENTRYP PFNGLGENNAMESAMDPROC)(GLenum, GLuint, GLuint*);\nGLAPI PFNGLGENNAMESAMDPROC glad_glGenNamesAMD;\n#define glGenNamesAMD glad_glGenNamesAMD\ntypedef void (APIENTRYP PFNGLDELETENAMESAMDPROC)(GLenum, GLuint, const GLuint*);\nGLAPI PFNGLDELETENAMESAMDPROC glad_glDeleteNamesAMD;\n#define glDeleteNamesAMD glad_glDeleteNamesAMD\ntypedef GLboolean (APIENTRYP PFNGLISNAMEAMDPROC)(GLenum, GLuint);\nGLAPI PFNGLISNAMEAMDPROC glad_glIsNameAMD;\n#define glIsNameAMD glad_glIsNameAMD\n#endif\n#ifndef GL_NV_texture_compression_vtc\n#define GL_NV_texture_compression_vtc 1\nGLAPI int GLAD_GL_NV_texture_compression_vtc;\n#endif\n#ifndef GL_SGIX_ycrcb_subsample\n#define GL_SGIX_ycrcb_subsample 1\nGLAPI int GLAD_GL_SGIX_ycrcb_subsample;\n#endif\n#ifndef GL_NV_texture_shader3\n#define GL_NV_texture_shader3 1\nGLAPI int GLAD_GL_NV_texture_shader3;\n#endif\n#ifndef GL_NV_texture_shader2\n#define GL_NV_texture_shader2 1\nGLAPI int GLAD_GL_NV_texture_shader2;\n#endif\n#ifndef GL_EXT_texture\n#define GL_EXT_texture 1\nGLAPI int GLAD_GL_EXT_texture;\n#endif\n#ifndef GL_ARB_buffer_storage\n#define GL_ARB_buffer_storage 1\nGLAPI int GLAD_GL_ARB_buffer_storage;\n#endif\n#ifndef GL_AMD_shader_atomic_counter_ops\n#define GL_AMD_shader_atomic_counter_ops 1\nGLAPI int GLAD_GL_AMD_shader_atomic_counter_ops;\n#endif\n#ifndef GL_APPLE_vertex_program_evaluators\n#define GL_APPLE_vertex_program_evaluators 1\nGLAPI int GLAD_GL_APPLE_vertex_program_evaluators;\ntypedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBAPPLEPROC)(GLuint, GLenum);\nGLAPI PFNGLENABLEVERTEXATTRIBAPPLEPROC glad_glEnableVertexAttribAPPLE;\n#define glEnableVertexAttribAPPLE glad_glEnableVertexAttribAPPLE\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBAPPLEPROC)(GLuint, GLenum);\nGLAPI PFNGLDISABLEVERTEXATTRIBAPPLEPROC glad_glDisableVertexAttribAPPLE;\n#define glDisableVertexAttribAPPLE glad_glDisableVertexAttribAPPLE\ntypedef GLboolean (APIENTRYP PFNGLISVERTEXATTRIBENABLEDAPPLEPROC)(GLuint, GLenum);\nGLAPI PFNGLISVERTEXATTRIBENABLEDAPPLEPROC glad_glIsVertexAttribEnabledAPPLE;\n#define glIsVertexAttribEnabledAPPLE glad_glIsVertexAttribEnabledAPPLE\ntypedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1DAPPLEPROC)(GLuint, GLuint, GLdouble, GLdouble, GLint, GLint, const GLdouble*);\nGLAPI PFNGLMAPVERTEXATTRIB1DAPPLEPROC glad_glMapVertexAttrib1dAPPLE;\n#define glMapVertexAttrib1dAPPLE glad_glMapVertexAttrib1dAPPLE\ntypedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1FAPPLEPROC)(GLuint, GLuint, GLfloat, GLfloat, GLint, GLint, const GLfloat*);\nGLAPI PFNGLMAPVERTEXATTRIB1FAPPLEPROC glad_glMapVertexAttrib1fAPPLE;\n#define glMapVertexAttrib1fAPPLE glad_glMapVertexAttrib1fAPPLE\ntypedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2DAPPLEPROC)(GLuint, GLuint, GLdouble, GLdouble, GLint, GLint, GLdouble, GLdouble, GLint, GLint, const GLdouble*);\nGLAPI PFNGLMAPVERTEXATTRIB2DAPPLEPROC glad_glMapVertexAttrib2dAPPLE;\n#define glMapVertexAttrib2dAPPLE glad_glMapVertexAttrib2dAPPLE\ntypedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2FAPPLEPROC)(GLuint, GLuint, GLfloat, GLfloat, GLint, GLint, GLfloat, GLfloat, GLint, GLint, const GLfloat*);\nGLAPI PFNGLMAPVERTEXATTRIB2FAPPLEPROC glad_glMapVertexAttrib2fAPPLE;\n#define glMapVertexAttrib2fAPPLE glad_glMapVertexAttrib2fAPPLE\n#endif\n#ifndef GL_ARB_multi_bind\n#define GL_ARB_multi_bind 1\nGLAPI int GLAD_GL_ARB_multi_bind;\n#endif\n#ifndef GL_ARB_explicit_uniform_location\n#define GL_ARB_explicit_uniform_location 1\nGLAPI int GLAD_GL_ARB_explicit_uniform_location;\n#endif\n#ifndef GL_ARB_depth_buffer_float\n#define GL_ARB_depth_buffer_float 1\nGLAPI int GLAD_GL_ARB_depth_buffer_float;\n#endif\n#ifndef GL_SGIX_shadow_ambient\n#define GL_SGIX_shadow_ambient 1\nGLAPI int GLAD_GL_SGIX_shadow_ambient;\n#endif\n#ifndef GL_ARB_texture_cube_map\n#define GL_ARB_texture_cube_map 1\nGLAPI int GLAD_GL_ARB_texture_cube_map;\n#endif\n#ifndef GL_AMD_vertex_shader_viewport_index\n#define GL_AMD_vertex_shader_viewport_index 1\nGLAPI int GLAD_GL_AMD_vertex_shader_viewport_index;\n#endif\n#ifndef GL_SGIX_list_priority\n#define GL_SGIX_list_priority 1\nGLAPI int GLAD_GL_SGIX_list_priority;\ntypedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETLISTPARAMETERFVSGIXPROC glad_glGetListParameterfvSGIX;\n#define glGetListParameterfvSGIX glad_glGetListParameterfvSGIX\ntypedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETLISTPARAMETERIVSGIXPROC glad_glGetListParameterivSGIX;\n#define glGetListParameterivSGIX glad_glGetListParameterivSGIX\ntypedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC)(GLuint, GLenum, GLfloat);\nGLAPI PFNGLLISTPARAMETERFSGIXPROC glad_glListParameterfSGIX;\n#define glListParameterfSGIX glad_glListParameterfSGIX\ntypedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC)(GLuint, GLenum, const GLfloat*);\nGLAPI PFNGLLISTPARAMETERFVSGIXPROC glad_glListParameterfvSGIX;\n#define glListParameterfvSGIX glad_glListParameterfvSGIX\ntypedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC)(GLuint, GLenum, GLint);\nGLAPI PFNGLLISTPARAMETERISGIXPROC glad_glListParameteriSGIX;\n#define glListParameteriSGIX glad_glListParameteriSGIX\ntypedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC)(GLuint, GLenum, const GLint*);\nGLAPI PFNGLLISTPARAMETERIVSGIXPROC glad_glListParameterivSGIX;\n#define glListParameterivSGIX glad_glListParameterivSGIX\n#endif\n#ifndef GL_NV_vertex_buffer_unified_memory\n#define GL_NV_vertex_buffer_unified_memory 1\nGLAPI int GLAD_GL_NV_vertex_buffer_unified_memory;\ntypedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC)(GLenum, GLuint, GLuint64EXT, GLsizeiptr);\nGLAPI PFNGLBUFFERADDRESSRANGENVPROC glad_glBufferAddressRangeNV;\n#define glBufferAddressRangeNV glad_glBufferAddressRangeNV\ntypedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC)(GLint, GLenum, GLsizei);\nGLAPI PFNGLVERTEXFORMATNVPROC glad_glVertexFormatNV;\n#define glVertexFormatNV glad_glVertexFormatNV\ntypedef void (APIENTRYP PFNGLNORMALFORMATNVPROC)(GLenum, GLsizei);\nGLAPI PFNGLNORMALFORMATNVPROC glad_glNormalFormatNV;\n#define glNormalFormatNV glad_glNormalFormatNV\ntypedef void (APIENTRYP PFNGLCOLORFORMATNVPROC)(GLint, GLenum, GLsizei);\nGLAPI PFNGLCOLORFORMATNVPROC glad_glColorFormatNV;\n#define glColorFormatNV glad_glColorFormatNV\ntypedef void (APIENTRYP PFNGLINDEXFORMATNVPROC)(GLenum, GLsizei);\nGLAPI PFNGLINDEXFORMATNVPROC glad_glIndexFormatNV;\n#define glIndexFormatNV glad_glIndexFormatNV\ntypedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC)(GLint, GLenum, GLsizei);\nGLAPI PFNGLTEXCOORDFORMATNVPROC glad_glTexCoordFormatNV;\n#define glTexCoordFormatNV glad_glTexCoordFormatNV\ntypedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC)(GLsizei);\nGLAPI PFNGLEDGEFLAGFORMATNVPROC glad_glEdgeFlagFormatNV;\n#define glEdgeFlagFormatNV glad_glEdgeFlagFormatNV\ntypedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC)(GLint, GLenum, GLsizei);\nGLAPI PFNGLSECONDARYCOLORFORMATNVPROC glad_glSecondaryColorFormatNV;\n#define glSecondaryColorFormatNV glad_glSecondaryColorFormatNV\ntypedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC)(GLenum, GLsizei);\nGLAPI PFNGLFOGCOORDFORMATNVPROC glad_glFogCoordFormatNV;\n#define glFogCoordFormatNV glad_glFogCoordFormatNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC)(GLuint, GLint, GLenum, GLboolean, GLsizei);\nGLAPI PFNGLVERTEXATTRIBFORMATNVPROC glad_glVertexAttribFormatNV;\n#define glVertexAttribFormatNV glad_glVertexAttribFormatNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC)(GLuint, GLint, GLenum, GLsizei);\nGLAPI PFNGLVERTEXATTRIBIFORMATNVPROC glad_glVertexAttribIFormatNV;\n#define glVertexAttribIFormatNV glad_glVertexAttribIFormatNV\ntypedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC)(GLenum, GLuint, GLuint64EXT*);\nGLAPI PFNGLGETINTEGERUI64I_VNVPROC glad_glGetIntegerui64i_vNV;\n#define glGetIntegerui64i_vNV glad_glGetIntegerui64i_vNV\n#endif\n#ifndef GL_EXT_texture_env_dot3\n#define GL_EXT_texture_env_dot3 1\nGLAPI int GLAD_GL_EXT_texture_env_dot3;\n#endif\n#ifndef GL_ATI_texture_env_combine3\n#define GL_ATI_texture_env_combine3 1\nGLAPI int GLAD_GL_ATI_texture_env_combine3;\n#endif\n#ifndef GL_ARB_map_buffer_alignment\n#define GL_ARB_map_buffer_alignment 1\nGLAPI int GLAD_GL_ARB_map_buffer_alignment;\n#endif\n#ifndef GL_NV_blend_equation_advanced\n#define GL_NV_blend_equation_advanced 1\nGLAPI int GLAD_GL_NV_blend_equation_advanced;\ntypedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC)(GLenum, GLint);\nGLAPI PFNGLBLENDPARAMETERINVPROC glad_glBlendParameteriNV;\n#define glBlendParameteriNV glad_glBlendParameteriNV\ntypedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC)();\nGLAPI PFNGLBLENDBARRIERNVPROC glad_glBlendBarrierNV;\n#define glBlendBarrierNV glad_glBlendBarrierNV\n#endif\n#ifndef GL_SGIS_sharpen_texture\n#define GL_SGIS_sharpen_texture 1\nGLAPI int GLAD_GL_SGIS_sharpen_texture;\ntypedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC)(GLenum, GLsizei, const GLfloat*);\nGLAPI PFNGLSHARPENTEXFUNCSGISPROC glad_glSharpenTexFuncSGIS;\n#define glSharpenTexFuncSGIS glad_glSharpenTexFuncSGIS\ntypedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC)(GLenum, GLfloat*);\nGLAPI PFNGLGETSHARPENTEXFUNCSGISPROC glad_glGetSharpenTexFuncSGIS;\n#define glGetSharpenTexFuncSGIS glad_glGetSharpenTexFuncSGIS\n#endif\n#ifndef GL_ARB_vertex_program\n#define GL_ARB_vertex_program 1\nGLAPI int GLAD_GL_ARB_vertex_program;\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC)(GLuint, GLdouble);\nGLAPI PFNGLVERTEXATTRIB1DARBPROC glad_glVertexAttrib1dARB;\n#define glVertexAttrib1dARB glad_glVertexAttrib1dARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB1DVARBPROC glad_glVertexAttrib1dvARB;\n#define glVertexAttrib1dvARB glad_glVertexAttrib1dvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC)(GLuint, GLfloat);\nGLAPI PFNGLVERTEXATTRIB1FARBPROC glad_glVertexAttrib1fARB;\n#define glVertexAttrib1fARB glad_glVertexAttrib1fARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB1FVARBPROC glad_glVertexAttrib1fvARB;\n#define glVertexAttrib1fvARB glad_glVertexAttrib1fvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC)(GLuint, GLshort);\nGLAPI PFNGLVERTEXATTRIB1SARBPROC glad_glVertexAttrib1sARB;\n#define glVertexAttrib1sARB glad_glVertexAttrib1sARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB1SVARBPROC glad_glVertexAttrib1svARB;\n#define glVertexAttrib1svARB glad_glVertexAttrib1svARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC)(GLuint, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIB2DARBPROC glad_glVertexAttrib2dARB;\n#define glVertexAttrib2dARB glad_glVertexAttrib2dARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB2DVARBPROC glad_glVertexAttrib2dvARB;\n#define glVertexAttrib2dvARB glad_glVertexAttrib2dvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC)(GLuint, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXATTRIB2FARBPROC glad_glVertexAttrib2fARB;\n#define glVertexAttrib2fARB glad_glVertexAttrib2fARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB2FVARBPROC glad_glVertexAttrib2fvARB;\n#define glVertexAttrib2fvARB glad_glVertexAttrib2fvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC)(GLuint, GLshort, GLshort);\nGLAPI PFNGLVERTEXATTRIB2SARBPROC glad_glVertexAttrib2sARB;\n#define glVertexAttrib2sARB glad_glVertexAttrib2sARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB2SVARBPROC glad_glVertexAttrib2svARB;\n#define glVertexAttrib2svARB glad_glVertexAttrib2svARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC)(GLuint, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIB3DARBPROC glad_glVertexAttrib3dARB;\n#define glVertexAttrib3dARB glad_glVertexAttrib3dARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB3DVARBPROC glad_glVertexAttrib3dvARB;\n#define glVertexAttrib3dvARB glad_glVertexAttrib3dvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC)(GLuint, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXATTRIB3FARBPROC glad_glVertexAttrib3fARB;\n#define glVertexAttrib3fARB glad_glVertexAttrib3fARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB3FVARBPROC glad_glVertexAttrib3fvARB;\n#define glVertexAttrib3fvARB glad_glVertexAttrib3fvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC)(GLuint, GLshort, GLshort, GLshort);\nGLAPI PFNGLVERTEXATTRIB3SARBPROC glad_glVertexAttrib3sARB;\n#define glVertexAttrib3sARB glad_glVertexAttrib3sARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB3SVARBPROC glad_glVertexAttrib3svARB;\n#define glVertexAttrib3svARB glad_glVertexAttrib3svARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC)(GLuint, const GLbyte*);\nGLAPI PFNGLVERTEXATTRIB4NBVARBPROC glad_glVertexAttrib4NbvARB;\n#define glVertexAttrib4NbvARB glad_glVertexAttrib4NbvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIB4NIVARBPROC glad_glVertexAttrib4NivARB;\n#define glVertexAttrib4NivARB glad_glVertexAttrib4NivARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB4NSVARBPROC glad_glVertexAttrib4NsvARB;\n#define glVertexAttrib4NsvARB glad_glVertexAttrib4NsvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC)(GLuint, GLubyte, GLubyte, GLubyte, GLubyte);\nGLAPI PFNGLVERTEXATTRIB4NUBARBPROC glad_glVertexAttrib4NubARB;\n#define glVertexAttrib4NubARB glad_glVertexAttrib4NubARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC)(GLuint, const GLubyte*);\nGLAPI PFNGLVERTEXATTRIB4NUBVARBPROC glad_glVertexAttrib4NubvARB;\n#define glVertexAttrib4NubvARB glad_glVertexAttrib4NubvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIB4NUIVARBPROC glad_glVertexAttrib4NuivARB;\n#define glVertexAttrib4NuivARB glad_glVertexAttrib4NuivARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC)(GLuint, const GLushort*);\nGLAPI PFNGLVERTEXATTRIB4NUSVARBPROC glad_glVertexAttrib4NusvARB;\n#define glVertexAttrib4NusvARB glad_glVertexAttrib4NusvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC)(GLuint, const GLbyte*);\nGLAPI PFNGLVERTEXATTRIB4BVARBPROC glad_glVertexAttrib4bvARB;\n#define glVertexAttrib4bvARB glad_glVertexAttrib4bvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC)(GLuint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIB4DARBPROC glad_glVertexAttrib4dARB;\n#define glVertexAttrib4dARB glad_glVertexAttrib4dARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB4DVARBPROC glad_glVertexAttrib4dvARB;\n#define glVertexAttrib4dvARB glad_glVertexAttrib4dvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXATTRIB4FARBPROC glad_glVertexAttrib4fARB;\n#define glVertexAttrib4fARB glad_glVertexAttrib4fARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB4FVARBPROC glad_glVertexAttrib4fvARB;\n#define glVertexAttrib4fvARB glad_glVertexAttrib4fvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIB4IVARBPROC glad_glVertexAttrib4ivARB;\n#define glVertexAttrib4ivARB glad_glVertexAttrib4ivARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC)(GLuint, GLshort, GLshort, GLshort, GLshort);\nGLAPI PFNGLVERTEXATTRIB4SARBPROC glad_glVertexAttrib4sARB;\n#define glVertexAttrib4sARB glad_glVertexAttrib4sARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB4SVARBPROC glad_glVertexAttrib4svARB;\n#define glVertexAttrib4svARB glad_glVertexAttrib4svARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC)(GLuint, const GLubyte*);\nGLAPI PFNGLVERTEXATTRIB4UBVARBPROC glad_glVertexAttrib4ubvARB;\n#define glVertexAttrib4ubvARB glad_glVertexAttrib4ubvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIB4UIVARBPROC glad_glVertexAttrib4uivARB;\n#define glVertexAttrib4uivARB glad_glVertexAttrib4uivARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC)(GLuint, const GLushort*);\nGLAPI PFNGLVERTEXATTRIB4USVARBPROC glad_glVertexAttrib4usvARB;\n#define glVertexAttrib4usvARB glad_glVertexAttrib4usvARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC)(GLuint, GLint, GLenum, GLboolean, GLsizei, const void*);\nGLAPI PFNGLVERTEXATTRIBPOINTERARBPROC glad_glVertexAttribPointerARB;\n#define glVertexAttribPointerARB glad_glVertexAttribPointerARB\ntypedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC)(GLuint);\nGLAPI PFNGLENABLEVERTEXATTRIBARRAYARBPROC glad_glEnableVertexAttribArrayARB;\n#define glEnableVertexAttribArrayARB glad_glEnableVertexAttribArrayARB\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC)(GLuint);\nGLAPI PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glad_glDisableVertexAttribArrayARB;\n#define glDisableVertexAttribArrayARB glad_glDisableVertexAttribArrayARB\ntypedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC)(GLenum, GLenum, GLsizei, const void*);\nGLAPI PFNGLPROGRAMSTRINGARBPROC glad_glProgramStringARB;\n#define glProgramStringARB glad_glProgramStringARB\ntypedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDPROGRAMARBPROC glad_glBindProgramARB;\n#define glBindProgramARB glad_glBindProgramARB\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEPROGRAMSARBPROC glad_glDeleteProgramsARB;\n#define glDeleteProgramsARB glad_glDeleteProgramsARB\ntypedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENPROGRAMSARBPROC glad_glGenProgramsARB;\n#define glGenProgramsARB glad_glGenProgramsARB\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC)(GLenum, GLuint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLPROGRAMENVPARAMETER4DARBPROC glad_glProgramEnvParameter4dARB;\n#define glProgramEnvParameter4dARB glad_glProgramEnvParameter4dARB\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC)(GLenum, GLuint, const GLdouble*);\nGLAPI PFNGLPROGRAMENVPARAMETER4DVARBPROC glad_glProgramEnvParameter4dvARB;\n#define glProgramEnvParameter4dvARB glad_glProgramEnvParameter4dvARB\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC)(GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLPROGRAMENVPARAMETER4FARBPROC glad_glProgramEnvParameter4fARB;\n#define glProgramEnvParameter4fARB glad_glProgramEnvParameter4fARB\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC)(GLenum, GLuint, const GLfloat*);\nGLAPI PFNGLPROGRAMENVPARAMETER4FVARBPROC glad_glProgramEnvParameter4fvARB;\n#define glProgramEnvParameter4fvARB glad_glProgramEnvParameter4fvARB\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC)(GLenum, GLuint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLPROGRAMLOCALPARAMETER4DARBPROC glad_glProgramLocalParameter4dARB;\n#define glProgramLocalParameter4dARB glad_glProgramLocalParameter4dARB\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC)(GLenum, GLuint, const GLdouble*);\nGLAPI PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glad_glProgramLocalParameter4dvARB;\n#define glProgramLocalParameter4dvARB glad_glProgramLocalParameter4dvARB\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC)(GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLPROGRAMLOCALPARAMETER4FARBPROC glad_glProgramLocalParameter4fARB;\n#define glProgramLocalParameter4fARB glad_glProgramLocalParameter4fARB\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)(GLenum, GLuint, const GLfloat*);\nGLAPI PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glad_glProgramLocalParameter4fvARB;\n#define glProgramLocalParameter4fvARB glad_glProgramLocalParameter4fvARB\ntypedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC)(GLenum, GLuint, GLdouble*);\nGLAPI PFNGLGETPROGRAMENVPARAMETERDVARBPROC glad_glGetProgramEnvParameterdvARB;\n#define glGetProgramEnvParameterdvARB glad_glGetProgramEnvParameterdvARB\ntypedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC)(GLenum, GLuint, GLfloat*);\nGLAPI PFNGLGETPROGRAMENVPARAMETERFVARBPROC glad_glGetProgramEnvParameterfvARB;\n#define glGetProgramEnvParameterfvARB glad_glGetProgramEnvParameterfvARB\ntypedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC)(GLenum, GLuint, GLdouble*);\nGLAPI PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glad_glGetProgramLocalParameterdvARB;\n#define glGetProgramLocalParameterdvARB glad_glGetProgramLocalParameterdvARB\ntypedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC)(GLenum, GLuint, GLfloat*);\nGLAPI PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glad_glGetProgramLocalParameterfvARB;\n#define glGetProgramLocalParameterfvARB glad_glGetProgramLocalParameterfvARB\ntypedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETPROGRAMIVARBPROC glad_glGetProgramivARB;\n#define glGetProgramivARB glad_glGetProgramivARB\ntypedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC)(GLenum, GLenum, void*);\nGLAPI PFNGLGETPROGRAMSTRINGARBPROC glad_glGetProgramStringARB;\n#define glGetProgramStringARB glad_glGetProgramStringARB\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC)(GLuint, GLenum, GLdouble*);\nGLAPI PFNGLGETVERTEXATTRIBDVARBPROC glad_glGetVertexAttribdvARB;\n#define glGetVertexAttribdvARB glad_glGetVertexAttribdvARB\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETVERTEXATTRIBFVARBPROC glad_glGetVertexAttribfvARB;\n#define glGetVertexAttribfvARB glad_glGetVertexAttribfvARB\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVERTEXATTRIBIVARBPROC glad_glGetVertexAttribivARB;\n#define glGetVertexAttribivARB glad_glGetVertexAttribivARB\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC)(GLuint, GLenum, void**);\nGLAPI PFNGLGETVERTEXATTRIBPOINTERVARBPROC glad_glGetVertexAttribPointervARB;\n#define glGetVertexAttribPointervARB glad_glGetVertexAttribPointervARB\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC)(GLuint);\nGLAPI PFNGLISPROGRAMARBPROC glad_glIsProgramARB;\n#define glIsProgramARB glad_glIsProgramARB\n#endif\n#ifndef GL_ARB_texture_rgb10_a2ui\n#define GL_ARB_texture_rgb10_a2ui 1\nGLAPI int GLAD_GL_ARB_texture_rgb10_a2ui;\n#endif\n#ifndef GL_OML_interlace\n#define GL_OML_interlace 1\nGLAPI int GLAD_GL_OML_interlace;\n#endif\n#ifndef GL_ATI_pixel_format_float\n#define GL_ATI_pixel_format_float 1\nGLAPI int GLAD_GL_ATI_pixel_format_float;\n#endif\n#ifndef GL_ARB_vertex_buffer_object\n#define GL_ARB_vertex_buffer_object 1\nGLAPI int GLAD_GL_ARB_vertex_buffer_object;\ntypedef void (APIENTRYP PFNGLBINDBUFFERARBPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDBUFFERARBPROC glad_glBindBufferARB;\n#define glBindBufferARB glad_glBindBufferARB\ntypedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEBUFFERSARBPROC glad_glDeleteBuffersARB;\n#define glDeleteBuffersARB glad_glDeleteBuffersARB\ntypedef void (APIENTRYP PFNGLGENBUFFERSARBPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENBUFFERSARBPROC glad_glGenBuffersARB;\n#define glGenBuffersARB glad_glGenBuffersARB\ntypedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC)(GLuint);\nGLAPI PFNGLISBUFFERARBPROC glad_glIsBufferARB;\n#define glIsBufferARB glad_glIsBufferARB\ntypedef void (APIENTRYP PFNGLBUFFERDATAARBPROC)(GLenum, GLsizeiptrARB, const void*, GLenum);\nGLAPI PFNGLBUFFERDATAARBPROC glad_glBufferDataARB;\n#define glBufferDataARB glad_glBufferDataARB\ntypedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC)(GLenum, GLintptrARB, GLsizeiptrARB, const void*);\nGLAPI PFNGLBUFFERSUBDATAARBPROC glad_glBufferSubDataARB;\n#define glBufferSubDataARB glad_glBufferSubDataARB\ntypedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC)(GLenum, GLintptrARB, GLsizeiptrARB, void*);\nGLAPI PFNGLGETBUFFERSUBDATAARBPROC glad_glGetBufferSubDataARB;\n#define glGetBufferSubDataARB glad_glGetBufferSubDataARB\ntypedef void* (APIENTRYP PFNGLMAPBUFFERARBPROC)(GLenum, GLenum);\nGLAPI PFNGLMAPBUFFERARBPROC glad_glMapBufferARB;\n#define glMapBufferARB glad_glMapBufferARB\ntypedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC)(GLenum);\nGLAPI PFNGLUNMAPBUFFERARBPROC glad_glUnmapBufferARB;\n#define glUnmapBufferARB glad_glUnmapBufferARB\ntypedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETBUFFERPARAMETERIVARBPROC glad_glGetBufferParameterivARB;\n#define glGetBufferParameterivARB glad_glGetBufferParameterivARB\ntypedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC)(GLenum, GLenum, void**);\nGLAPI PFNGLGETBUFFERPOINTERVARBPROC glad_glGetBufferPointervARB;\n#define glGetBufferPointervARB glad_glGetBufferPointervARB\n#endif\n#ifndef GL_EXT_shadow_funcs\n#define GL_EXT_shadow_funcs 1\nGLAPI int GLAD_GL_EXT_shadow_funcs;\n#endif\n#ifndef GL_ATI_text_fragment_shader\n#define GL_ATI_text_fragment_shader 1\nGLAPI int GLAD_GL_ATI_text_fragment_shader;\n#endif\n#ifndef GL_NV_vertex_array_range\n#define GL_NV_vertex_array_range 1\nGLAPI int GLAD_GL_NV_vertex_array_range;\ntypedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC)();\nGLAPI PFNGLFLUSHVERTEXARRAYRANGENVPROC glad_glFlushVertexArrayRangeNV;\n#define glFlushVertexArrayRangeNV glad_glFlushVertexArrayRangeNV\ntypedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC)(GLsizei, const void*);\nGLAPI PFNGLVERTEXARRAYRANGENVPROC glad_glVertexArrayRangeNV;\n#define glVertexArrayRangeNV glad_glVertexArrayRangeNV\n#endif\n#ifndef GL_SGIX_fragment_lighting\n#define GL_SGIX_fragment_lighting 1\nGLAPI int GLAD_GL_SGIX_fragment_lighting;\ntypedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC)(GLenum, GLenum);\nGLAPI PFNGLFRAGMENTCOLORMATERIALSGIXPROC glad_glFragmentColorMaterialSGIX;\n#define glFragmentColorMaterialSGIX glad_glFragmentColorMaterialSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC)(GLenum, GLenum, GLfloat);\nGLAPI PFNGLFRAGMENTLIGHTFSGIXPROC glad_glFragmentLightfSGIX;\n#define glFragmentLightfSGIX glad_glFragmentLightfSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLFRAGMENTLIGHTFVSGIXPROC glad_glFragmentLightfvSGIX;\n#define glFragmentLightfvSGIX glad_glFragmentLightfvSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC)(GLenum, GLenum, GLint);\nGLAPI PFNGLFRAGMENTLIGHTISGIXPROC glad_glFragmentLightiSGIX;\n#define glFragmentLightiSGIX glad_glFragmentLightiSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLFRAGMENTLIGHTIVSGIXPROC glad_glFragmentLightivSGIX;\n#define glFragmentLightivSGIX glad_glFragmentLightivSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC)(GLenum, GLfloat);\nGLAPI PFNGLFRAGMENTLIGHTMODELFSGIXPROC glad_glFragmentLightModelfSGIX;\n#define glFragmentLightModelfSGIX glad_glFragmentLightModelfSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLFRAGMENTLIGHTMODELFVSGIXPROC glad_glFragmentLightModelfvSGIX;\n#define glFragmentLightModelfvSGIX glad_glFragmentLightModelfvSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC)(GLenum, GLint);\nGLAPI PFNGLFRAGMENTLIGHTMODELISGIXPROC glad_glFragmentLightModeliSGIX;\n#define glFragmentLightModeliSGIX glad_glFragmentLightModeliSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC)(GLenum, const GLint*);\nGLAPI PFNGLFRAGMENTLIGHTMODELIVSGIXPROC glad_glFragmentLightModelivSGIX;\n#define glFragmentLightModelivSGIX glad_glFragmentLightModelivSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC)(GLenum, GLenum, GLfloat);\nGLAPI PFNGLFRAGMENTMATERIALFSGIXPROC glad_glFragmentMaterialfSGIX;\n#define glFragmentMaterialfSGIX glad_glFragmentMaterialfSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLFRAGMENTMATERIALFVSGIXPROC glad_glFragmentMaterialfvSGIX;\n#define glFragmentMaterialfvSGIX glad_glFragmentMaterialfvSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC)(GLenum, GLenum, GLint);\nGLAPI PFNGLFRAGMENTMATERIALISGIXPROC glad_glFragmentMaterialiSGIX;\n#define glFragmentMaterialiSGIX glad_glFragmentMaterialiSGIX\ntypedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLFRAGMENTMATERIALIVSGIXPROC glad_glFragmentMaterialivSGIX;\n#define glFragmentMaterialivSGIX glad_glFragmentMaterialivSGIX\ntypedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETFRAGMENTLIGHTFVSGIXPROC glad_glGetFragmentLightfvSGIX;\n#define glGetFragmentLightfvSGIX glad_glGetFragmentLightfvSGIX\ntypedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETFRAGMENTLIGHTIVSGIXPROC glad_glGetFragmentLightivSGIX;\n#define glGetFragmentLightivSGIX glad_glGetFragmentLightivSGIX\ntypedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETFRAGMENTMATERIALFVSGIXPROC glad_glGetFragmentMaterialfvSGIX;\n#define glGetFragmentMaterialfvSGIX glad_glGetFragmentMaterialfvSGIX\ntypedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETFRAGMENTMATERIALIVSGIXPROC glad_glGetFragmentMaterialivSGIX;\n#define glGetFragmentMaterialivSGIX glad_glGetFragmentMaterialivSGIX\ntypedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC)(GLenum, GLint);\nGLAPI PFNGLLIGHTENVISGIXPROC glad_glLightEnviSGIX;\n#define glLightEnviSGIX glad_glLightEnviSGIX\n#endif\n#ifndef GL_NV_texture_expand_normal\n#define GL_NV_texture_expand_normal 1\nGLAPI int GLAD_GL_NV_texture_expand_normal;\n#endif\n#ifndef GL_NV_framebuffer_multisample_coverage\n#define GL_NV_framebuffer_multisample_coverage 1\nGLAPI int GLAD_GL_NV_framebuffer_multisample_coverage;\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC)(GLenum, GLsizei, GLsizei, GLenum, GLsizei, GLsizei);\nGLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC glad_glRenderbufferStorageMultisampleCoverageNV;\n#define glRenderbufferStorageMultisampleCoverageNV glad_glRenderbufferStorageMultisampleCoverageNV\n#endif\n#ifndef GL_EXT_timer_query\n#define GL_EXT_timer_query 1\nGLAPI int GLAD_GL_EXT_timer_query;\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC)(GLuint, GLenum, GLint64*);\nGLAPI PFNGLGETQUERYOBJECTI64VEXTPROC glad_glGetQueryObjecti64vEXT;\n#define glGetQueryObjecti64vEXT glad_glGetQueryObjecti64vEXT\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC)(GLuint, GLenum, GLuint64*);\nGLAPI PFNGLGETQUERYOBJECTUI64VEXTPROC glad_glGetQueryObjectui64vEXT;\n#define glGetQueryObjectui64vEXT glad_glGetQueryObjectui64vEXT\n#endif\n#ifndef GL_EXT_vertex_array_bgra\n#define GL_EXT_vertex_array_bgra 1\nGLAPI int GLAD_GL_EXT_vertex_array_bgra;\n#endif\n#ifndef GL_NV_bindless_texture\n#define GL_NV_bindless_texture 1\nGLAPI int GLAD_GL_NV_bindless_texture;\ntypedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC)(GLuint);\nGLAPI PFNGLGETTEXTUREHANDLENVPROC glad_glGetTextureHandleNV;\n#define glGetTextureHandleNV glad_glGetTextureHandleNV\ntypedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC)(GLuint, GLuint);\nGLAPI PFNGLGETTEXTURESAMPLERHANDLENVPROC glad_glGetTextureSamplerHandleNV;\n#define glGetTextureSamplerHandleNV glad_glGetTextureSamplerHandleNV\ntypedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC)(GLuint64);\nGLAPI PFNGLMAKETEXTUREHANDLERESIDENTNVPROC glad_glMakeTextureHandleResidentNV;\n#define glMakeTextureHandleResidentNV glad_glMakeTextureHandleResidentNV\ntypedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC)(GLuint64);\nGLAPI PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC glad_glMakeTextureHandleNonResidentNV;\n#define glMakeTextureHandleNonResidentNV glad_glMakeTextureHandleNonResidentNV\ntypedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC)(GLuint, GLint, GLboolean, GLint, GLenum);\nGLAPI PFNGLGETIMAGEHANDLENVPROC glad_glGetImageHandleNV;\n#define glGetImageHandleNV glad_glGetImageHandleNV\ntypedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC)(GLuint64, GLenum);\nGLAPI PFNGLMAKEIMAGEHANDLERESIDENTNVPROC glad_glMakeImageHandleResidentNV;\n#define glMakeImageHandleResidentNV glad_glMakeImageHandleResidentNV\ntypedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC)(GLuint64);\nGLAPI PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC glad_glMakeImageHandleNonResidentNV;\n#define glMakeImageHandleNonResidentNV glad_glMakeImageHandleNonResidentNV\ntypedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC)(GLint, GLuint64);\nGLAPI PFNGLUNIFORMHANDLEUI64NVPROC glad_glUniformHandleui64NV;\n#define glUniformHandleui64NV glad_glUniformHandleui64NV\ntypedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC)(GLint, GLsizei, const GLuint64*);\nGLAPI PFNGLUNIFORMHANDLEUI64VNVPROC glad_glUniformHandleui64vNV;\n#define glUniformHandleui64vNV glad_glUniformHandleui64vNV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC)(GLuint, GLint, GLuint64);\nGLAPI PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC glad_glProgramUniformHandleui64NV;\n#define glProgramUniformHandleui64NV glad_glProgramUniformHandleui64NV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC)(GLuint, GLint, GLsizei, const GLuint64*);\nGLAPI PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC glad_glProgramUniformHandleui64vNV;\n#define glProgramUniformHandleui64vNV glad_glProgramUniformHandleui64vNV\ntypedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC)(GLuint64);\nGLAPI PFNGLISTEXTUREHANDLERESIDENTNVPROC glad_glIsTextureHandleResidentNV;\n#define glIsTextureHandleResidentNV glad_glIsTextureHandleResidentNV\ntypedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC)(GLuint64);\nGLAPI PFNGLISIMAGEHANDLERESIDENTNVPROC glad_glIsImageHandleResidentNV;\n#define glIsImageHandleResidentNV glad_glIsImageHandleResidentNV\n#endif\n#ifndef GL_KHR_debug\n#define GL_KHR_debug 1\nGLAPI int GLAD_GL_KHR_debug;\ntypedef void (APIENTRYP PFNGLGETPOINTERVPROC)(GLenum, void**);\nGLAPI PFNGLGETPOINTERVPROC glad_glGetPointerv;\n#define glGetPointerv glad_glGetPointerv\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLKHRPROC)(GLenum, GLenum, GLenum, GLsizei, const GLuint*, GLboolean);\nGLAPI PFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR;\n#define glDebugMessageControlKHR glad_glDebugMessageControlKHR\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTKHRPROC)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*);\nGLAPI PFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR;\n#define glDebugMessageInsertKHR glad_glDebugMessageInsertKHR\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKKHRPROC)(GLDEBUGPROCKHR, const void*);\nGLAPI PFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR;\n#define glDebugMessageCallbackKHR glad_glDebugMessageCallbackKHR\ntypedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGKHRPROC)(GLuint, GLsizei, GLenum*, GLenum*, GLuint*, GLenum*, GLsizei*, GLchar*);\nGLAPI PFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR;\n#define glGetDebugMessageLogKHR glad_glGetDebugMessageLogKHR\ntypedef void (APIENTRYP PFNGLPUSHDEBUGGROUPKHRPROC)(GLenum, GLuint, GLsizei, const GLchar*);\nGLAPI PFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR;\n#define glPushDebugGroupKHR glad_glPushDebugGroupKHR\ntypedef void (APIENTRYP PFNGLPOPDEBUGGROUPKHRPROC)();\nGLAPI PFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR;\n#define glPopDebugGroupKHR glad_glPopDebugGroupKHR\ntypedef void (APIENTRYP PFNGLOBJECTLABELKHRPROC)(GLenum, GLuint, GLsizei, const GLchar*);\nGLAPI PFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR;\n#define glObjectLabelKHR glad_glObjectLabelKHR\ntypedef void (APIENTRYP PFNGLGETOBJECTLABELKHRPROC)(GLenum, GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR;\n#define glGetObjectLabelKHR glad_glGetObjectLabelKHR\ntypedef void (APIENTRYP PFNGLOBJECTPTRLABELKHRPROC)(const void*, GLsizei, const GLchar*);\nGLAPI PFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR;\n#define glObjectPtrLabelKHR glad_glObjectPtrLabelKHR\ntypedef void (APIENTRYP PFNGLGETOBJECTPTRLABELKHRPROC)(const void*, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR;\n#define glGetObjectPtrLabelKHR glad_glGetObjectPtrLabelKHR\ntypedef void (APIENTRYP PFNGLGETPOINTERVKHRPROC)(GLenum, void**);\nGLAPI PFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR;\n#define glGetPointervKHR glad_glGetPointervKHR\n#endif\n#ifndef GL_SGIS_texture_border_clamp\n#define GL_SGIS_texture_border_clamp 1\nGLAPI int GLAD_GL_SGIS_texture_border_clamp;\n#endif\n#ifndef GL_ATI_vertex_attrib_array_object\n#define GL_ATI_vertex_attrib_array_object 1\nGLAPI int GLAD_GL_ATI_vertex_attrib_array_object;\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC)(GLuint, GLint, GLenum, GLboolean, GLsizei, GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBARRAYOBJECTATIPROC glad_glVertexAttribArrayObjectATI;\n#define glVertexAttribArrayObjectATI glad_glVertexAttribArrayObjectATI\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC glad_glGetVertexAttribArrayObjectfvATI;\n#define glGetVertexAttribArrayObjectfvATI glad_glGetVertexAttribArrayObjectfvATI\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC glad_glGetVertexAttribArrayObjectivATI;\n#define glGetVertexAttribArrayObjectivATI glad_glGetVertexAttribArrayObjectivATI\n#endif\n#ifndef GL_SGIX_clipmap\n#define GL_SGIX_clipmap 1\nGLAPI int GLAD_GL_SGIX_clipmap;\n#endif\n#ifndef GL_EXT_geometry_shader4\n#define GL_EXT_geometry_shader4 1\nGLAPI int GLAD_GL_EXT_geometry_shader4;\n#endif\n#ifndef GL_MESA_ycbcr_texture\n#define GL_MESA_ycbcr_texture 1\nGLAPI int GLAD_GL_MESA_ycbcr_texture;\n#endif\n#ifndef GL_MESAX_texture_stack\n#define GL_MESAX_texture_stack 1\nGLAPI int GLAD_GL_MESAX_texture_stack;\n#endif\n#ifndef GL_AMD_seamless_cubemap_per_texture\n#define GL_AMD_seamless_cubemap_per_texture 1\nGLAPI int GLAD_GL_AMD_seamless_cubemap_per_texture;\n#endif\n#ifndef GL_EXT_bindable_uniform\n#define GL_EXT_bindable_uniform 1\nGLAPI int GLAD_GL_EXT_bindable_uniform;\ntypedef void (APIENTRYP PFNGLUNIFORMBUFFEREXTPROC)(GLuint, GLint, GLuint);\nGLAPI PFNGLUNIFORMBUFFEREXTPROC glad_glUniformBufferEXT;\n#define glUniformBufferEXT glad_glUniformBufferEXT\ntypedef GLint (APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC)(GLuint, GLint);\nGLAPI PFNGLGETUNIFORMBUFFERSIZEEXTPROC glad_glGetUniformBufferSizeEXT;\n#define glGetUniformBufferSizeEXT glad_glGetUniformBufferSizeEXT\ntypedef GLintptr (APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC)(GLuint, GLint);\nGLAPI PFNGLGETUNIFORMOFFSETEXTPROC glad_glGetUniformOffsetEXT;\n#define glGetUniformOffsetEXT glad_glGetUniformOffsetEXT\n#endif\n#ifndef GL_KHR_texture_compression_astc_hdr\n#define GL_KHR_texture_compression_astc_hdr 1\nGLAPI int GLAD_GL_KHR_texture_compression_astc_hdr;\n#endif\n#ifndef GL_ARB_fragment_program_shadow\n#define GL_ARB_fragment_program_shadow 1\nGLAPI int GLAD_GL_ARB_fragment_program_shadow;\n#endif\n#ifndef GL_ATI_element_array\n#define GL_ATI_element_array 1\nGLAPI int GLAD_GL_ATI_element_array;\ntypedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC)(GLenum, const void*);\nGLAPI PFNGLELEMENTPOINTERATIPROC glad_glElementPointerATI;\n#define glElementPointerATI glad_glElementPointerATI\ntypedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC)(GLenum, GLsizei);\nGLAPI PFNGLDRAWELEMENTARRAYATIPROC glad_glDrawElementArrayATI;\n#define glDrawElementArrayATI glad_glDrawElementArrayATI\ntypedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC)(GLenum, GLuint, GLuint, GLsizei);\nGLAPI PFNGLDRAWRANGEELEMENTARRAYATIPROC glad_glDrawRangeElementArrayATI;\n#define glDrawRangeElementArrayATI glad_glDrawRangeElementArrayATI\n#endif\n#ifndef GL_AMD_texture_texture4\n#define GL_AMD_texture_texture4 1\nGLAPI int GLAD_GL_AMD_texture_texture4;\n#endif\n#ifndef GL_SGIX_reference_plane\n#define GL_SGIX_reference_plane 1\nGLAPI int GLAD_GL_SGIX_reference_plane;\ntypedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC)(const GLdouble*);\nGLAPI PFNGLREFERENCEPLANESGIXPROC glad_glReferencePlaneSGIX;\n#define glReferencePlaneSGIX glad_glReferencePlaneSGIX\n#endif\n#ifndef GL_EXT_stencil_two_side\n#define GL_EXT_stencil_two_side 1\nGLAPI int GLAD_GL_EXT_stencil_two_side;\ntypedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC)(GLenum);\nGLAPI PFNGLACTIVESTENCILFACEEXTPROC glad_glActiveStencilFaceEXT;\n#define glActiveStencilFaceEXT glad_glActiveStencilFaceEXT\n#endif\n#ifndef GL_SGIX_texture_lod_bias\n#define GL_SGIX_texture_lod_bias 1\nGLAPI int GLAD_GL_SGIX_texture_lod_bias;\n#endif\n#ifndef GL_NV_explicit_multisample\n#define GL_NV_explicit_multisample 1\nGLAPI int GLAD_GL_NV_explicit_multisample;\ntypedef void (APIENTRYP PFNGLGETMULTISAMPLEFVNVPROC)(GLenum, GLuint, GLfloat*);\nGLAPI PFNGLGETMULTISAMPLEFVNVPROC glad_glGetMultisamplefvNV;\n#define glGetMultisamplefvNV glad_glGetMultisamplefvNV\ntypedef void (APIENTRYP PFNGLSAMPLEMASKINDEXEDNVPROC)(GLuint, GLbitfield);\nGLAPI PFNGLSAMPLEMASKINDEXEDNVPROC glad_glSampleMaskIndexedNV;\n#define glSampleMaskIndexedNV glad_glSampleMaskIndexedNV\ntypedef void (APIENTRYP PFNGLTEXRENDERBUFFERNVPROC)(GLenum, GLuint);\nGLAPI PFNGLTEXRENDERBUFFERNVPROC glad_glTexRenderbufferNV;\n#define glTexRenderbufferNV glad_glTexRenderbufferNV\n#endif\n#ifndef GL_IBM_static_data\n#define GL_IBM_static_data 1\nGLAPI int GLAD_GL_IBM_static_data;\ntypedef void (APIENTRYP PFNGLFLUSHSTATICDATAIBMPROC)(GLenum);\nGLAPI PFNGLFLUSHSTATICDATAIBMPROC glad_glFlushStaticDataIBM;\n#define glFlushStaticDataIBM glad_glFlushStaticDataIBM\n#endif\n#ifndef GL_EXT_clip_volume_hint\n#define GL_EXT_clip_volume_hint 1\nGLAPI int GLAD_GL_EXT_clip_volume_hint;\n#endif\n#ifndef GL_EXT_texture_perturb_normal\n#define GL_EXT_texture_perturb_normal 1\nGLAPI int GLAD_GL_EXT_texture_perturb_normal;\ntypedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC)(GLenum);\nGLAPI PFNGLTEXTURENORMALEXTPROC glad_glTextureNormalEXT;\n#define glTextureNormalEXT glad_glTextureNormalEXT\n#endif\n#ifndef GL_NV_fragment_program2\n#define GL_NV_fragment_program2 1\nGLAPI int GLAD_GL_NV_fragment_program2;\n#endif\n#ifndef GL_NV_fragment_program4\n#define GL_NV_fragment_program4 1\nGLAPI int GLAD_GL_NV_fragment_program4;\n#endif\n#ifndef GL_EXT_point_parameters\n#define GL_EXT_point_parameters 1\nGLAPI int GLAD_GL_EXT_point_parameters;\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC)(GLenum, GLfloat);\nGLAPI PFNGLPOINTPARAMETERFEXTPROC glad_glPointParameterfEXT;\n#define glPointParameterfEXT glad_glPointParameterfEXT\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLPOINTPARAMETERFVEXTPROC glad_glPointParameterfvEXT;\n#define glPointParameterfvEXT glad_glPointParameterfvEXT\n#endif\n#ifndef GL_PGI_misc_hints\n#define GL_PGI_misc_hints 1\nGLAPI int GLAD_GL_PGI_misc_hints;\ntypedef void (APIENTRYP PFNGLHINTPGIPROC)(GLenum, GLint);\nGLAPI PFNGLHINTPGIPROC glad_glHintPGI;\n#define glHintPGI glad_glHintPGI\n#endif\n#ifndef GL_SGIX_subsample\n#define GL_SGIX_subsample 1\nGLAPI int GLAD_GL_SGIX_subsample;\n#endif\n#ifndef GL_AMD_shader_stencil_export\n#define GL_AMD_shader_stencil_export 1\nGLAPI int GLAD_GL_AMD_shader_stencil_export;\n#endif\n#ifndef GL_ARB_shader_texture_lod\n#define GL_ARB_shader_texture_lod 1\nGLAPI int GLAD_GL_ARB_shader_texture_lod;\n#endif\n#ifndef GL_ARB_vertex_shader\n#define GL_ARB_vertex_shader 1\nGLAPI int GLAD_GL_ARB_vertex_shader;\ntypedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC)(GLhandleARB, GLuint, const GLcharARB*);\nGLAPI PFNGLBINDATTRIBLOCATIONARBPROC glad_glBindAttribLocationARB;\n#define glBindAttribLocationARB glad_glBindAttribLocationARB\ntypedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC)(GLhandleARB, GLuint, GLsizei, GLsizei*, GLint*, GLenum*, GLcharARB*);\nGLAPI PFNGLGETACTIVEATTRIBARBPROC glad_glGetActiveAttribARB;\n#define glGetActiveAttribARB glad_glGetActiveAttribARB\ntypedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC)(GLhandleARB, const GLcharARB*);\nGLAPI PFNGLGETATTRIBLOCATIONARBPROC glad_glGetAttribLocationARB;\n#define glGetAttribLocationARB glad_glGetAttribLocationARB\n#endif\n#ifndef GL_ARB_depth_clamp\n#define GL_ARB_depth_clamp 1\nGLAPI int GLAD_GL_ARB_depth_clamp;\n#endif\n#ifndef GL_SGIS_texture_select\n#define GL_SGIS_texture_select 1\nGLAPI int GLAD_GL_SGIS_texture_select;\n#endif\n#ifndef GL_NV_texture_shader\n#define GL_NV_texture_shader 1\nGLAPI int GLAD_GL_NV_texture_shader;\n#endif\n#ifndef GL_ARB_tessellation_shader\n#define GL_ARB_tessellation_shader 1\nGLAPI int GLAD_GL_ARB_tessellation_shader;\n#endif\n#ifndef GL_EXT_draw_buffers2\n#define GL_EXT_draw_buffers2 1\nGLAPI int GLAD_GL_EXT_draw_buffers2;\ntypedef void (APIENTRYP PFNGLCOLORMASKINDEXEDEXTPROC)(GLuint, GLboolean, GLboolean, GLboolean, GLboolean);\nGLAPI PFNGLCOLORMASKINDEXEDEXTPROC glad_glColorMaskIndexedEXT;\n#define glColorMaskIndexedEXT glad_glColorMaskIndexedEXT\ntypedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC)(GLenum, GLuint, GLboolean*);\nGLAPI PFNGLGETBOOLEANINDEXEDVEXTPROC glad_glGetBooleanIndexedvEXT;\n#define glGetBooleanIndexedvEXT glad_glGetBooleanIndexedvEXT\ntypedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC)(GLenum, GLuint, GLint*);\nGLAPI PFNGLGETINTEGERINDEXEDVEXTPROC glad_glGetIntegerIndexedvEXT;\n#define glGetIntegerIndexedvEXT glad_glGetIntegerIndexedvEXT\ntypedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC)(GLenum, GLuint);\nGLAPI PFNGLENABLEINDEXEDEXTPROC glad_glEnableIndexedEXT;\n#define glEnableIndexedEXT glad_glEnableIndexedEXT\ntypedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC)(GLenum, GLuint);\nGLAPI PFNGLDISABLEINDEXEDEXTPROC glad_glDisableIndexedEXT;\n#define glDisableIndexedEXT glad_glDisableIndexedEXT\ntypedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC)(GLenum, GLuint);\nGLAPI PFNGLISENABLEDINDEXEDEXTPROC glad_glIsEnabledIndexedEXT;\n#define glIsEnabledIndexedEXT glad_glIsEnabledIndexedEXT\n#endif\n#ifndef GL_ARB_vertex_attrib_64bit\n#define GL_ARB_vertex_attrib_64bit 1\nGLAPI int GLAD_GL_ARB_vertex_attrib_64bit;\n#endif\n#ifndef GL_WIN_specular_fog\n#define GL_WIN_specular_fog 1\nGLAPI int GLAD_GL_WIN_specular_fog;\n#endif\n#ifndef GL_AMD_interleaved_elements\n#define GL_AMD_interleaved_elements 1\nGLAPI int GLAD_GL_AMD_interleaved_elements;\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBPARAMETERIAMDPROC)(GLuint, GLenum, GLint);\nGLAPI PFNGLVERTEXATTRIBPARAMETERIAMDPROC glad_glVertexAttribParameteriAMD;\n#define glVertexAttribParameteriAMD glad_glVertexAttribParameteriAMD\n#endif\n#ifndef GL_ARB_fragment_program\n#define GL_ARB_fragment_program 1\nGLAPI int GLAD_GL_ARB_fragment_program;\n#endif\n#ifndef GL_OML_resample\n#define GL_OML_resample 1\nGLAPI int GLAD_GL_OML_resample;\n#endif\n#ifndef GL_APPLE_ycbcr_422\n#define GL_APPLE_ycbcr_422 1\nGLAPI int GLAD_GL_APPLE_ycbcr_422;\n#endif\n#ifndef GL_SGIX_texture_add_env\n#define GL_SGIX_texture_add_env 1\nGLAPI int GLAD_GL_SGIX_texture_add_env;\n#endif\n#ifndef GL_ARB_shadow_ambient\n#define GL_ARB_shadow_ambient 1\nGLAPI int GLAD_GL_ARB_shadow_ambient;\n#endif\n#ifndef GL_ARB_texture_storage\n#define GL_ARB_texture_storage 1\nGLAPI int GLAD_GL_ARB_texture_storage;\n#endif\n#ifndef GL_EXT_pixel_buffer_object\n#define GL_EXT_pixel_buffer_object 1\nGLAPI int GLAD_GL_EXT_pixel_buffer_object;\n#endif\n#ifndef GL_ARB_copy_image\n#define GL_ARB_copy_image 1\nGLAPI int GLAD_GL_ARB_copy_image;\n#endif\n#ifndef GL_SGIS_pixel_texture\n#define GL_SGIS_pixel_texture 1\nGLAPI int GLAD_GL_SGIS_pixel_texture;\ntypedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC)(GLenum, GLint);\nGLAPI PFNGLPIXELTEXGENPARAMETERISGISPROC glad_glPixelTexGenParameteriSGIS;\n#define glPixelTexGenParameteriSGIS glad_glPixelTexGenParameteriSGIS\ntypedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC)(GLenum, const GLint*);\nGLAPI PFNGLPIXELTEXGENPARAMETERIVSGISPROC glad_glPixelTexGenParameterivSGIS;\n#define glPixelTexGenParameterivSGIS glad_glPixelTexGenParameterivSGIS\ntypedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC)(GLenum, GLfloat);\nGLAPI PFNGLPIXELTEXGENPARAMETERFSGISPROC glad_glPixelTexGenParameterfSGIS;\n#define glPixelTexGenParameterfSGIS glad_glPixelTexGenParameterfSGIS\ntypedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLPIXELTEXGENPARAMETERFVSGISPROC glad_glPixelTexGenParameterfvSGIS;\n#define glPixelTexGenParameterfvSGIS glad_glPixelTexGenParameterfvSGIS\ntypedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC)(GLenum, GLint*);\nGLAPI PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC glad_glGetPixelTexGenParameterivSGIS;\n#define glGetPixelTexGenParameterivSGIS glad_glGetPixelTexGenParameterivSGIS\ntypedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC)(GLenum, GLfloat*);\nGLAPI PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC glad_glGetPixelTexGenParameterfvSGIS;\n#define glGetPixelTexGenParameterfvSGIS glad_glGetPixelTexGenParameterfvSGIS\n#endif\n#ifndef GL_SGIS_generate_mipmap\n#define GL_SGIS_generate_mipmap 1\nGLAPI int GLAD_GL_SGIS_generate_mipmap;\n#endif\n#ifndef GL_SGIX_instruments\n#define GL_SGIX_instruments 1\nGLAPI int GLAD_GL_SGIX_instruments;\ntypedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC)();\nGLAPI PFNGLGETINSTRUMENTSSGIXPROC glad_glGetInstrumentsSGIX;\n#define glGetInstrumentsSGIX glad_glGetInstrumentsSGIX\ntypedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC)(GLsizei, GLint*);\nGLAPI PFNGLINSTRUMENTSBUFFERSGIXPROC glad_glInstrumentsBufferSGIX;\n#define glInstrumentsBufferSGIX glad_glInstrumentsBufferSGIX\ntypedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC)(GLint*);\nGLAPI PFNGLPOLLINSTRUMENTSSGIXPROC glad_glPollInstrumentsSGIX;\n#define glPollInstrumentsSGIX glad_glPollInstrumentsSGIX\ntypedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC)(GLint);\nGLAPI PFNGLREADINSTRUMENTSSGIXPROC glad_glReadInstrumentsSGIX;\n#define glReadInstrumentsSGIX glad_glReadInstrumentsSGIX\ntypedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC)();\nGLAPI PFNGLSTARTINSTRUMENTSSGIXPROC glad_glStartInstrumentsSGIX;\n#define glStartInstrumentsSGIX glad_glStartInstrumentsSGIX\ntypedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC)(GLint);\nGLAPI PFNGLSTOPINSTRUMENTSSGIXPROC glad_glStopInstrumentsSGIX;\n#define glStopInstrumentsSGIX glad_glStopInstrumentsSGIX\n#endif\n#ifndef GL_HP_texture_lighting\n#define GL_HP_texture_lighting 1\nGLAPI int GLAD_GL_HP_texture_lighting;\n#endif\n#ifndef GL_ARB_shader_storage_buffer_object\n#define GL_ARB_shader_storage_buffer_object 1\nGLAPI int GLAD_GL_ARB_shader_storage_buffer_object;\n#endif\n#ifndef GL_EXT_blend_minmax\n#define GL_EXT_blend_minmax 1\nGLAPI int GLAD_GL_EXT_blend_minmax;\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC)(GLenum);\nGLAPI PFNGLBLENDEQUATIONEXTPROC glad_glBlendEquationEXT;\n#define glBlendEquationEXT glad_glBlendEquationEXT\n#endif\n#ifndef GL_MESA_pack_invert\n#define GL_MESA_pack_invert 1\nGLAPI int GLAD_GL_MESA_pack_invert;\n#endif\n#ifndef GL_ARB_base_instance\n#define GL_ARB_base_instance 1\nGLAPI int GLAD_GL_ARB_base_instance;\n#endif\n#ifndef GL_SGIX_convolution_accuracy\n#define GL_SGIX_convolution_accuracy 1\nGLAPI int GLAD_GL_SGIX_convolution_accuracy;\n#endif\n#ifndef GL_PGI_vertex_hints\n#define GL_PGI_vertex_hints 1\nGLAPI int GLAD_GL_PGI_vertex_hints;\n#endif\n#ifndef GL_EXT_texture_integer\n#define GL_EXT_texture_integer 1\nGLAPI int GLAD_GL_EXT_texture_integer;\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLTEXPARAMETERIIVEXTPROC glad_glTexParameterIivEXT;\n#define glTexParameterIivEXT glad_glTexParameterIivEXT\ntypedef void (APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC)(GLenum, GLenum, const GLuint*);\nGLAPI PFNGLTEXPARAMETERIUIVEXTPROC glad_glTexParameterIuivEXT;\n#define glTexParameterIuivEXT glad_glTexParameterIuivEXT\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETTEXPARAMETERIIVEXTPROC glad_glGetTexParameterIivEXT;\n#define glGetTexParameterIivEXT glad_glGetTexParameterIivEXT\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC)(GLenum, GLenum, GLuint*);\nGLAPI PFNGLGETTEXPARAMETERIUIVEXTPROC glad_glGetTexParameterIuivEXT;\n#define glGetTexParameterIuivEXT glad_glGetTexParameterIuivEXT\ntypedef void (APIENTRYP PFNGLCLEARCOLORIIEXTPROC)(GLint, GLint, GLint, GLint);\nGLAPI PFNGLCLEARCOLORIIEXTPROC glad_glClearColorIiEXT;\n#define glClearColorIiEXT glad_glClearColorIiEXT\ntypedef void (APIENTRYP PFNGLCLEARCOLORIUIEXTPROC)(GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLCLEARCOLORIUIEXTPROC glad_glClearColorIuiEXT;\n#define glClearColorIuiEXT glad_glClearColorIuiEXT\n#endif\n#ifndef GL_ARB_texture_multisample\n#define GL_ARB_texture_multisample 1\nGLAPI int GLAD_GL_ARB_texture_multisample;\n#endif\n#ifndef GL_S3_s3tc\n#define GL_S3_s3tc 1\nGLAPI int GLAD_GL_S3_s3tc;\n#endif\n#ifndef GL_ARB_query_buffer_object\n#define GL_ARB_query_buffer_object 1\nGLAPI int GLAD_GL_ARB_query_buffer_object;\n#endif\n#ifndef GL_AMD_vertex_shader_tessellator\n#define GL_AMD_vertex_shader_tessellator 1\nGLAPI int GLAD_GL_AMD_vertex_shader_tessellator;\ntypedef void (APIENTRYP PFNGLTESSELLATIONFACTORAMDPROC)(GLfloat);\nGLAPI PFNGLTESSELLATIONFACTORAMDPROC glad_glTessellationFactorAMD;\n#define glTessellationFactorAMD glad_glTessellationFactorAMD\ntypedef void (APIENTRYP PFNGLTESSELLATIONMODEAMDPROC)(GLenum);\nGLAPI PFNGLTESSELLATIONMODEAMDPROC glad_glTessellationModeAMD;\n#define glTessellationModeAMD glad_glTessellationModeAMD\n#endif\n#ifndef GL_ARB_invalidate_subdata\n#define GL_ARB_invalidate_subdata 1\nGLAPI int GLAD_GL_ARB_invalidate_subdata;\n#endif\n#ifndef GL_EXT_index_material\n#define GL_EXT_index_material 1\nGLAPI int GLAD_GL_EXT_index_material;\ntypedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC)(GLenum, GLenum);\nGLAPI PFNGLINDEXMATERIALEXTPROC glad_glIndexMaterialEXT;\n#define glIndexMaterialEXT glad_glIndexMaterialEXT\n#endif\n#ifndef GL_NV_blend_equation_advanced_coherent\n#define GL_NV_blend_equation_advanced_coherent 1\nGLAPI int GLAD_GL_NV_blend_equation_advanced_coherent;\n#endif\n#ifndef GL_INTEL_parallel_arrays\n#define GL_INTEL_parallel_arrays 1\nGLAPI int GLAD_GL_INTEL_parallel_arrays;\ntypedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC)(GLint, GLenum, const void**);\nGLAPI PFNGLVERTEXPOINTERVINTELPROC glad_glVertexPointervINTEL;\n#define glVertexPointervINTEL glad_glVertexPointervINTEL\ntypedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC)(GLenum, const void**);\nGLAPI PFNGLNORMALPOINTERVINTELPROC glad_glNormalPointervINTEL;\n#define glNormalPointervINTEL glad_glNormalPointervINTEL\ntypedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC)(GLint, GLenum, const void**);\nGLAPI PFNGLCOLORPOINTERVINTELPROC glad_glColorPointervINTEL;\n#define glColorPointervINTEL glad_glColorPointervINTEL\ntypedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC)(GLint, GLenum, const void**);\nGLAPI PFNGLTEXCOORDPOINTERVINTELPROC glad_glTexCoordPointervINTEL;\n#define glTexCoordPointervINTEL glad_glTexCoordPointervINTEL\n#endif\n#ifndef GL_ATI_draw_buffers\n#define GL_ATI_draw_buffers 1\nGLAPI int GLAD_GL_ATI_draw_buffers;\ntypedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC)(GLsizei, const GLenum*);\nGLAPI PFNGLDRAWBUFFERSATIPROC glad_glDrawBuffersATI;\n#define glDrawBuffersATI glad_glDrawBuffersATI\n#endif\n#ifndef GL_EXT_cmyka\n#define GL_EXT_cmyka 1\nGLAPI int GLAD_GL_EXT_cmyka;\n#endif\n#ifndef GL_SGIX_pixel_texture\n#define GL_SGIX_pixel_texture 1\nGLAPI int GLAD_GL_SGIX_pixel_texture;\ntypedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC)(GLenum);\nGLAPI PFNGLPIXELTEXGENSGIXPROC glad_glPixelTexGenSGIX;\n#define glPixelTexGenSGIX glad_glPixelTexGenSGIX\n#endif\n#ifndef GL_APPLE_specular_vector\n#define GL_APPLE_specular_vector 1\nGLAPI int GLAD_GL_APPLE_specular_vector;\n#endif\n#ifndef GL_ARB_compatibility\n#define GL_ARB_compatibility 1\nGLAPI int GLAD_GL_ARB_compatibility;\n#endif\n#ifndef GL_ARB_timer_query\n#define GL_ARB_timer_query 1\nGLAPI int GLAD_GL_ARB_timer_query;\n#endif\n#ifndef GL_SGIX_interlace\n#define GL_SGIX_interlace 1\nGLAPI int GLAD_GL_SGIX_interlace;\n#endif\n#ifndef GL_NV_parameter_buffer_object\n#define GL_NV_parameter_buffer_object 1\nGLAPI int GLAD_GL_NV_parameter_buffer_object;\ntypedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC)(GLenum, GLuint, GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC glad_glProgramBufferParametersfvNV;\n#define glProgramBufferParametersfvNV glad_glProgramBufferParametersfvNV\ntypedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC)(GLenum, GLuint, GLuint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC glad_glProgramBufferParametersIivNV;\n#define glProgramBufferParametersIivNV glad_glProgramBufferParametersIivNV\ntypedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC)(GLenum, GLuint, GLuint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC glad_glProgramBufferParametersIuivNV;\n#define glProgramBufferParametersIuivNV glad_glProgramBufferParametersIuivNV\n#endif\n#ifndef GL_AMD_shader_trinary_minmax\n#define GL_AMD_shader_trinary_minmax 1\nGLAPI int GLAD_GL_AMD_shader_trinary_minmax;\n#endif\n#ifndef GL_EXT_rescale_normal\n#define GL_EXT_rescale_normal 1\nGLAPI int GLAD_GL_EXT_rescale_normal;\n#endif\n#ifndef GL_ARB_pixel_buffer_object\n#define GL_ARB_pixel_buffer_object 1\nGLAPI int GLAD_GL_ARB_pixel_buffer_object;\n#endif\n#ifndef GL_ARB_uniform_buffer_object\n#define GL_ARB_uniform_buffer_object 1\nGLAPI int GLAD_GL_ARB_uniform_buffer_object;\n#endif\n#ifndef GL_ARB_vertex_type_10f_11f_11f_rev\n#define GL_ARB_vertex_type_10f_11f_11f_rev 1\nGLAPI int GLAD_GL_ARB_vertex_type_10f_11f_11f_rev;\n#endif\n#ifndef GL_ARB_texture_swizzle\n#define GL_ARB_texture_swizzle 1\nGLAPI int GLAD_GL_ARB_texture_swizzle;\n#endif\n#ifndef GL_NV_transform_feedback2\n#define GL_NV_transform_feedback2 1\nGLAPI int GLAD_GL_NV_transform_feedback2;\ntypedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKNVPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDTRANSFORMFEEDBACKNVPROC glad_glBindTransformFeedbackNV;\n#define glBindTransformFeedbackNV glad_glBindTransformFeedbackNV\ntypedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSNVPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETETRANSFORMFEEDBACKSNVPROC glad_glDeleteTransformFeedbacksNV;\n#define glDeleteTransformFeedbacksNV glad_glDeleteTransformFeedbacksNV\ntypedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSNVPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENTRANSFORMFEEDBACKSNVPROC glad_glGenTransformFeedbacksNV;\n#define glGenTransformFeedbacksNV glad_glGenTransformFeedbacksNV\ntypedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKNVPROC)(GLuint);\nGLAPI PFNGLISTRANSFORMFEEDBACKNVPROC glad_glIsTransformFeedbackNV;\n#define glIsTransformFeedbackNV glad_glIsTransformFeedbackNV\ntypedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKNVPROC)();\nGLAPI PFNGLPAUSETRANSFORMFEEDBACKNVPROC glad_glPauseTransformFeedbackNV;\n#define glPauseTransformFeedbackNV glad_glPauseTransformFeedbackNV\ntypedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKNVPROC)();\nGLAPI PFNGLRESUMETRANSFORMFEEDBACKNVPROC glad_glResumeTransformFeedbackNV;\n#define glResumeTransformFeedbackNV glad_glResumeTransformFeedbackNV\ntypedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKNVPROC)(GLenum, GLuint);\nGLAPI PFNGLDRAWTRANSFORMFEEDBACKNVPROC glad_glDrawTransformFeedbackNV;\n#define glDrawTransformFeedbackNV glad_glDrawTransformFeedbackNV\n#endif\n#ifndef GL_SGIX_async_pixel\n#define GL_SGIX_async_pixel 1\nGLAPI int GLAD_GL_SGIX_async_pixel;\n#endif\n#ifndef GL_NV_fragment_program_option\n#define GL_NV_fragment_program_option 1\nGLAPI int GLAD_GL_NV_fragment_program_option;\n#endif\n#ifndef GL_ARB_explicit_attrib_location\n#define GL_ARB_explicit_attrib_location 1\nGLAPI int GLAD_GL_ARB_explicit_attrib_location;\n#endif\n#ifndef GL_EXT_blend_color\n#define GL_EXT_blend_color 1\nGLAPI int GLAD_GL_EXT_blend_color;\ntypedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC)(GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLBLENDCOLOREXTPROC glad_glBlendColorEXT;\n#define glBlendColorEXT glad_glBlendColorEXT\n#endif\n#ifndef GL_NV_shader_thread_group\n#define GL_NV_shader_thread_group 1\nGLAPI int GLAD_GL_NV_shader_thread_group;\n#endif\n#ifndef GL_EXT_stencil_wrap\n#define GL_EXT_stencil_wrap 1\nGLAPI int GLAD_GL_EXT_stencil_wrap;\n#endif\n#ifndef GL_EXT_index_array_formats\n#define GL_EXT_index_array_formats 1\nGLAPI int GLAD_GL_EXT_index_array_formats;\n#endif\n#ifndef GL_EXT_histogram\n#define GL_EXT_histogram 1\nGLAPI int GLAD_GL_EXT_histogram;\ntypedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC)(GLenum, GLboolean, GLenum, GLenum, void*);\nGLAPI PFNGLGETHISTOGRAMEXTPROC glad_glGetHistogramEXT;\n#define glGetHistogramEXT glad_glGetHistogramEXT\ntypedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETHISTOGRAMPARAMETERFVEXTPROC glad_glGetHistogramParameterfvEXT;\n#define glGetHistogramParameterfvEXT glad_glGetHistogramParameterfvEXT\ntypedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETHISTOGRAMPARAMETERIVEXTPROC glad_glGetHistogramParameterivEXT;\n#define glGetHistogramParameterivEXT glad_glGetHistogramParameterivEXT\ntypedef void (APIENTRYP PFNGLGETMINMAXEXTPROC)(GLenum, GLboolean, GLenum, GLenum, void*);\nGLAPI PFNGLGETMINMAXEXTPROC glad_glGetMinmaxEXT;\n#define glGetMinmaxEXT glad_glGetMinmaxEXT\ntypedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETMINMAXPARAMETERFVEXTPROC glad_glGetMinmaxParameterfvEXT;\n#define glGetMinmaxParameterfvEXT glad_glGetMinmaxParameterfvEXT\ntypedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETMINMAXPARAMETERIVEXTPROC glad_glGetMinmaxParameterivEXT;\n#define glGetMinmaxParameterivEXT glad_glGetMinmaxParameterivEXT\ntypedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC)(GLenum, GLsizei, GLenum, GLboolean);\nGLAPI PFNGLHISTOGRAMEXTPROC glad_glHistogramEXT;\n#define glHistogramEXT glad_glHistogramEXT\ntypedef void (APIENTRYP PFNGLMINMAXEXTPROC)(GLenum, GLenum, GLboolean);\nGLAPI PFNGLMINMAXEXTPROC glad_glMinmaxEXT;\n#define glMinmaxEXT glad_glMinmaxEXT\ntypedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC)(GLenum);\nGLAPI PFNGLRESETHISTOGRAMEXTPROC glad_glResetHistogramEXT;\n#define glResetHistogramEXT glad_glResetHistogramEXT\ntypedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC)(GLenum);\nGLAPI PFNGLRESETMINMAXEXTPROC glad_glResetMinmaxEXT;\n#define glResetMinmaxEXT glad_glResetMinmaxEXT\n#endif\n#ifndef GL_SGIS_point_parameters\n#define GL_SGIS_point_parameters 1\nGLAPI int GLAD_GL_SGIS_point_parameters;\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC)(GLenum, GLfloat);\nGLAPI PFNGLPOINTPARAMETERFSGISPROC glad_glPointParameterfSGIS;\n#define glPointParameterfSGIS glad_glPointParameterfSGIS\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLPOINTPARAMETERFVSGISPROC glad_glPointParameterfvSGIS;\n#define glPointParameterfvSGIS glad_glPointParameterfvSGIS\n#endif\n#ifndef GL_EXT_direct_state_access\n#define GL_EXT_direct_state_access 1\nGLAPI int GLAD_GL_EXT_direct_state_access;\ntypedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLMATRIXLOADFEXTPROC glad_glMatrixLoadfEXT;\n#define glMatrixLoadfEXT glad_glMatrixLoadfEXT\ntypedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLMATRIXLOADDEXTPROC glad_glMatrixLoaddEXT;\n#define glMatrixLoaddEXT glad_glMatrixLoaddEXT\ntypedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLMATRIXMULTFEXTPROC glad_glMatrixMultfEXT;\n#define glMatrixMultfEXT glad_glMatrixMultfEXT\ntypedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLMATRIXMULTDEXTPROC glad_glMatrixMultdEXT;\n#define glMatrixMultdEXT glad_glMatrixMultdEXT\ntypedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC)(GLenum);\nGLAPI PFNGLMATRIXLOADIDENTITYEXTPROC glad_glMatrixLoadIdentityEXT;\n#define glMatrixLoadIdentityEXT glad_glMatrixLoadIdentityEXT\ntypedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC)(GLenum, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLMATRIXROTATEFEXTPROC glad_glMatrixRotatefEXT;\n#define glMatrixRotatefEXT glad_glMatrixRotatefEXT\ntypedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC)(GLenum, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLMATRIXROTATEDEXTPROC glad_glMatrixRotatedEXT;\n#define glMatrixRotatedEXT glad_glMatrixRotatedEXT\ntypedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC)(GLenum, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLMATRIXSCALEFEXTPROC glad_glMatrixScalefEXT;\n#define glMatrixScalefEXT glad_glMatrixScalefEXT\ntypedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC)(GLenum, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLMATRIXSCALEDEXTPROC glad_glMatrixScaledEXT;\n#define glMatrixScaledEXT glad_glMatrixScaledEXT\ntypedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC)(GLenum, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLMATRIXTRANSLATEFEXTPROC glad_glMatrixTranslatefEXT;\n#define glMatrixTranslatefEXT glad_glMatrixTranslatefEXT\ntypedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC)(GLenum, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLMATRIXTRANSLATEDEXTPROC glad_glMatrixTranslatedEXT;\n#define glMatrixTranslatedEXT glad_glMatrixTranslatedEXT\ntypedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC)(GLenum, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLMATRIXFRUSTUMEXTPROC glad_glMatrixFrustumEXT;\n#define glMatrixFrustumEXT glad_glMatrixFrustumEXT\ntypedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC)(GLenum, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLMATRIXORTHOEXTPROC glad_glMatrixOrthoEXT;\n#define glMatrixOrthoEXT glad_glMatrixOrthoEXT\ntypedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC)(GLenum);\nGLAPI PFNGLMATRIXPOPEXTPROC glad_glMatrixPopEXT;\n#define glMatrixPopEXT glad_glMatrixPopEXT\ntypedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC)(GLenum);\nGLAPI PFNGLMATRIXPUSHEXTPROC glad_glMatrixPushEXT;\n#define glMatrixPushEXT glad_glMatrixPushEXT\ntypedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC)(GLbitfield);\nGLAPI PFNGLCLIENTATTRIBDEFAULTEXTPROC glad_glClientAttribDefaultEXT;\n#define glClientAttribDefaultEXT glad_glClientAttribDefaultEXT\ntypedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC)(GLbitfield);\nGLAPI PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC glad_glPushClientAttribDefaultEXT;\n#define glPushClientAttribDefaultEXT glad_glPushClientAttribDefaultEXT\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC)(GLuint, GLenum, GLenum, GLfloat);\nGLAPI PFNGLTEXTUREPARAMETERFEXTPROC glad_glTextureParameterfEXT;\n#define glTextureParameterfEXT glad_glTextureParameterfEXT\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC)(GLuint, GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLTEXTUREPARAMETERFVEXTPROC glad_glTextureParameterfvEXT;\n#define glTextureParameterfvEXT glad_glTextureParameterfvEXT\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC)(GLuint, GLenum, GLenum, GLint);\nGLAPI PFNGLTEXTUREPARAMETERIEXTPROC glad_glTextureParameteriEXT;\n#define glTextureParameteriEXT glad_glTextureParameteriEXT\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC)(GLuint, GLenum, GLenum, const GLint*);\nGLAPI PFNGLTEXTUREPARAMETERIVEXTPROC glad_glTextureParameterivEXT;\n#define glTextureParameterivEXT glad_glTextureParameterivEXT\ntypedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC)(GLuint, GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXTUREIMAGE1DEXTPROC glad_glTextureImage1DEXT;\n#define glTextureImage1DEXT glad_glTextureImage1DEXT\ntypedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC)(GLuint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXTUREIMAGE2DEXTPROC glad_glTextureImage2DEXT;\n#define glTextureImage2DEXT glad_glTextureImage2DEXT\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC)(GLuint, GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXTURESUBIMAGE1DEXTPROC glad_glTextureSubImage1DEXT;\n#define glTextureSubImage1DEXT glad_glTextureSubImage1DEXT\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC)(GLuint, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXTURESUBIMAGE2DEXTPROC glad_glTextureSubImage2DEXT;\n#define glTextureSubImage2DEXT glad_glTextureSubImage2DEXT\ntypedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC)(GLuint, GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLint);\nGLAPI PFNGLCOPYTEXTUREIMAGE1DEXTPROC glad_glCopyTextureImage1DEXT;\n#define glCopyTextureImage1DEXT glad_glCopyTextureImage1DEXT\ntypedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC)(GLuint, GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint);\nGLAPI PFNGLCOPYTEXTUREIMAGE2DEXTPROC glad_glCopyTextureImage2DEXT;\n#define glCopyTextureImage2DEXT glad_glCopyTextureImage2DEXT\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC)(GLuint, GLenum, GLint, GLint, GLint, GLint, GLsizei);\nGLAPI PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC glad_glCopyTextureSubImage1DEXT;\n#define glCopyTextureSubImage1DEXT glad_glCopyTextureSubImage1DEXT\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC)(GLuint, GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC glad_glCopyTextureSubImage2DEXT;\n#define glCopyTextureSubImage2DEXT glad_glCopyTextureSubImage2DEXT\ntypedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC)(GLuint, GLenum, GLint, GLenum, GLenum, void*);\nGLAPI PFNGLGETTEXTUREIMAGEEXTPROC glad_glGetTextureImageEXT;\n#define glGetTextureImageEXT glad_glGetTextureImageEXT\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC)(GLuint, GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETTEXTUREPARAMETERFVEXTPROC glad_glGetTextureParameterfvEXT;\n#define glGetTextureParameterfvEXT glad_glGetTextureParameterfvEXT\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC)(GLuint, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETTEXTUREPARAMETERIVEXTPROC glad_glGetTextureParameterivEXT;\n#define glGetTextureParameterivEXT glad_glGetTextureParameterivEXT\ntypedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC)(GLuint, GLenum, GLint, GLenum, GLfloat*);\nGLAPI PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC glad_glGetTextureLevelParameterfvEXT;\n#define glGetTextureLevelParameterfvEXT glad_glGetTextureLevelParameterfvEXT\ntypedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC)(GLuint, GLenum, GLint, GLenum, GLint*);\nGLAPI PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC glad_glGetTextureLevelParameterivEXT;\n#define glGetTextureLevelParameterivEXT glad_glGetTextureLevelParameterivEXT\ntypedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC)(GLuint, GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXTUREIMAGE3DEXTPROC glad_glTextureImage3DEXT;\n#define glTextureImage3DEXT glad_glTextureImage3DEXT\ntypedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC)(GLuint, GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXTURESUBIMAGE3DEXTPROC glad_glTextureSubImage3DEXT;\n#define glTextureSubImage3DEXT glad_glTextureSubImage3DEXT\ntypedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC)(GLuint, GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC glad_glCopyTextureSubImage3DEXT;\n#define glCopyTextureSubImage3DEXT glad_glCopyTextureSubImage3DEXT\ntypedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC)(GLenum, GLenum, GLuint);\nGLAPI PFNGLBINDMULTITEXTUREEXTPROC glad_glBindMultiTextureEXT;\n#define glBindMultiTextureEXT glad_glBindMultiTextureEXT\ntypedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC)(GLenum, GLint, GLenum, GLsizei, const void*);\nGLAPI PFNGLMULTITEXCOORDPOINTEREXTPROC glad_glMultiTexCoordPointerEXT;\n#define glMultiTexCoordPointerEXT glad_glMultiTexCoordPointerEXT\ntypedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC)(GLenum, GLenum, GLenum, GLfloat);\nGLAPI PFNGLMULTITEXENVFEXTPROC glad_glMultiTexEnvfEXT;\n#define glMultiTexEnvfEXT glad_glMultiTexEnvfEXT\ntypedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC)(GLenum, GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLMULTITEXENVFVEXTPROC glad_glMultiTexEnvfvEXT;\n#define glMultiTexEnvfvEXT glad_glMultiTexEnvfvEXT\ntypedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC)(GLenum, GLenum, GLenum, GLint);\nGLAPI PFNGLMULTITEXENVIEXTPROC glad_glMultiTexEnviEXT;\n#define glMultiTexEnviEXT glad_glMultiTexEnviEXT\ntypedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC)(GLenum, GLenum, GLenum, const GLint*);\nGLAPI PFNGLMULTITEXENVIVEXTPROC glad_glMultiTexEnvivEXT;\n#define glMultiTexEnvivEXT glad_glMultiTexEnvivEXT\ntypedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC)(GLenum, GLenum, GLenum, GLdouble);\nGLAPI PFNGLMULTITEXGENDEXTPROC glad_glMultiTexGendEXT;\n#define glMultiTexGendEXT glad_glMultiTexGendEXT\ntypedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC)(GLenum, GLenum, GLenum, const GLdouble*);\nGLAPI PFNGLMULTITEXGENDVEXTPROC glad_glMultiTexGendvEXT;\n#define glMultiTexGendvEXT glad_glMultiTexGendvEXT\ntypedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC)(GLenum, GLenum, GLenum, GLfloat);\nGLAPI PFNGLMULTITEXGENFEXTPROC glad_glMultiTexGenfEXT;\n#define glMultiTexGenfEXT glad_glMultiTexGenfEXT\ntypedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC)(GLenum, GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLMULTITEXGENFVEXTPROC glad_glMultiTexGenfvEXT;\n#define glMultiTexGenfvEXT glad_glMultiTexGenfvEXT\ntypedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC)(GLenum, GLenum, GLenum, GLint);\nGLAPI PFNGLMULTITEXGENIEXTPROC glad_glMultiTexGeniEXT;\n#define glMultiTexGeniEXT glad_glMultiTexGeniEXT\ntypedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC)(GLenum, GLenum, GLenum, const GLint*);\nGLAPI PFNGLMULTITEXGENIVEXTPROC glad_glMultiTexGenivEXT;\n#define glMultiTexGenivEXT glad_glMultiTexGenivEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC)(GLenum, GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETMULTITEXENVFVEXTPROC glad_glGetMultiTexEnvfvEXT;\n#define glGetMultiTexEnvfvEXT glad_glGetMultiTexEnvfvEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC)(GLenum, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETMULTITEXENVIVEXTPROC glad_glGetMultiTexEnvivEXT;\n#define glGetMultiTexEnvivEXT glad_glGetMultiTexEnvivEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC)(GLenum, GLenum, GLenum, GLdouble*);\nGLAPI PFNGLGETMULTITEXGENDVEXTPROC glad_glGetMultiTexGendvEXT;\n#define glGetMultiTexGendvEXT glad_glGetMultiTexGendvEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC)(GLenum, GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETMULTITEXGENFVEXTPROC glad_glGetMultiTexGenfvEXT;\n#define glGetMultiTexGenfvEXT glad_glGetMultiTexGenfvEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC)(GLenum, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETMULTITEXGENIVEXTPROC glad_glGetMultiTexGenivEXT;\n#define glGetMultiTexGenivEXT glad_glGetMultiTexGenivEXT\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC)(GLenum, GLenum, GLenum, GLint);\nGLAPI PFNGLMULTITEXPARAMETERIEXTPROC glad_glMultiTexParameteriEXT;\n#define glMultiTexParameteriEXT glad_glMultiTexParameteriEXT\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC)(GLenum, GLenum, GLenum, const GLint*);\nGLAPI PFNGLMULTITEXPARAMETERIVEXTPROC glad_glMultiTexParameterivEXT;\n#define glMultiTexParameterivEXT glad_glMultiTexParameterivEXT\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC)(GLenum, GLenum, GLenum, GLfloat);\nGLAPI PFNGLMULTITEXPARAMETERFEXTPROC glad_glMultiTexParameterfEXT;\n#define glMultiTexParameterfEXT glad_glMultiTexParameterfEXT\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC)(GLenum, GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLMULTITEXPARAMETERFVEXTPROC glad_glMultiTexParameterfvEXT;\n#define glMultiTexParameterfvEXT glad_glMultiTexParameterfvEXT\ntypedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC)(GLenum, GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLMULTITEXIMAGE1DEXTPROC glad_glMultiTexImage1DEXT;\n#define glMultiTexImage1DEXT glad_glMultiTexImage1DEXT\ntypedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC)(GLenum, GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLMULTITEXIMAGE2DEXTPROC glad_glMultiTexImage2DEXT;\n#define glMultiTexImage2DEXT glad_glMultiTexImage2DEXT\ntypedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC)(GLenum, GLenum, GLint, GLint, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLMULTITEXSUBIMAGE1DEXTPROC glad_glMultiTexSubImage1DEXT;\n#define glMultiTexSubImage1DEXT glad_glMultiTexSubImage1DEXT\ntypedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC)(GLenum, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLMULTITEXSUBIMAGE2DEXTPROC glad_glMultiTexSubImage2DEXT;\n#define glMultiTexSubImage2DEXT glad_glMultiTexSubImage2DEXT\ntypedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC)(GLenum, GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLint);\nGLAPI PFNGLCOPYMULTITEXIMAGE1DEXTPROC glad_glCopyMultiTexImage1DEXT;\n#define glCopyMultiTexImage1DEXT glad_glCopyMultiTexImage1DEXT\ntypedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC)(GLenum, GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint);\nGLAPI PFNGLCOPYMULTITEXIMAGE2DEXTPROC glad_glCopyMultiTexImage2DEXT;\n#define glCopyMultiTexImage2DEXT glad_glCopyMultiTexImage2DEXT\ntypedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC)(GLenum, GLenum, GLint, GLint, GLint, GLint, GLsizei);\nGLAPI PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC glad_glCopyMultiTexSubImage1DEXT;\n#define glCopyMultiTexSubImage1DEXT glad_glCopyMultiTexSubImage1DEXT\ntypedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC)(GLenum, GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC glad_glCopyMultiTexSubImage2DEXT;\n#define glCopyMultiTexSubImage2DEXT glad_glCopyMultiTexSubImage2DEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC)(GLenum, GLenum, GLint, GLenum, GLenum, void*);\nGLAPI PFNGLGETMULTITEXIMAGEEXTPROC glad_glGetMultiTexImageEXT;\n#define glGetMultiTexImageEXT glad_glGetMultiTexImageEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC)(GLenum, GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETMULTITEXPARAMETERFVEXTPROC glad_glGetMultiTexParameterfvEXT;\n#define glGetMultiTexParameterfvEXT glad_glGetMultiTexParameterfvEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC)(GLenum, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETMULTITEXPARAMETERIVEXTPROC glad_glGetMultiTexParameterivEXT;\n#define glGetMultiTexParameterivEXT glad_glGetMultiTexParameterivEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC)(GLenum, GLenum, GLint, GLenum, GLfloat*);\nGLAPI PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC glad_glGetMultiTexLevelParameterfvEXT;\n#define glGetMultiTexLevelParameterfvEXT glad_glGetMultiTexLevelParameterfvEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC)(GLenum, GLenum, GLint, GLenum, GLint*);\nGLAPI PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC glad_glGetMultiTexLevelParameterivEXT;\n#define glGetMultiTexLevelParameterivEXT glad_glGetMultiTexLevelParameterivEXT\ntypedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC)(GLenum, GLenum, GLint, GLint, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLMULTITEXIMAGE3DEXTPROC glad_glMultiTexImage3DEXT;\n#define glMultiTexImage3DEXT glad_glMultiTexImage3DEXT\ntypedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC)(GLenum, GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLMULTITEXSUBIMAGE3DEXTPROC glad_glMultiTexSubImage3DEXT;\n#define glMultiTexSubImage3DEXT glad_glMultiTexSubImage3DEXT\ntypedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC)(GLenum, GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC glad_glCopyMultiTexSubImage3DEXT;\n#define glCopyMultiTexSubImage3DEXT glad_glCopyMultiTexSubImage3DEXT\ntypedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC)(GLenum, GLuint);\nGLAPI PFNGLENABLECLIENTSTATEINDEXEDEXTPROC glad_glEnableClientStateIndexedEXT;\n#define glEnableClientStateIndexedEXT glad_glEnableClientStateIndexedEXT\ntypedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC)(GLenum, GLuint);\nGLAPI PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC glad_glDisableClientStateIndexedEXT;\n#define glDisableClientStateIndexedEXT glad_glDisableClientStateIndexedEXT\ntypedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC)(GLenum, GLuint, GLfloat*);\nGLAPI PFNGLGETFLOATINDEXEDVEXTPROC glad_glGetFloatIndexedvEXT;\n#define glGetFloatIndexedvEXT glad_glGetFloatIndexedvEXT\ntypedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC)(GLenum, GLuint, GLdouble*);\nGLAPI PFNGLGETDOUBLEINDEXEDVEXTPROC glad_glGetDoubleIndexedvEXT;\n#define glGetDoubleIndexedvEXT glad_glGetDoubleIndexedvEXT\ntypedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC)(GLenum, GLuint, void**);\nGLAPI PFNGLGETPOINTERINDEXEDVEXTPROC glad_glGetPointerIndexedvEXT;\n#define glGetPointerIndexedvEXT glad_glGetPointerIndexedvEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC)(GLuint, GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC glad_glCompressedTextureImage3DEXT;\n#define glCompressedTextureImage3DEXT glad_glCompressedTextureImage3DEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC)(GLuint, GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC glad_glCompressedTextureImage2DEXT;\n#define glCompressedTextureImage2DEXT glad_glCompressedTextureImage2DEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC)(GLuint, GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC glad_glCompressedTextureImage1DEXT;\n#define glCompressedTextureImage1DEXT glad_glCompressedTextureImage1DEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC)(GLuint, GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC glad_glCompressedTextureSubImage3DEXT;\n#define glCompressedTextureSubImage3DEXT glad_glCompressedTextureSubImage3DEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC)(GLuint, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC glad_glCompressedTextureSubImage2DEXT;\n#define glCompressedTextureSubImage2DEXT glad_glCompressedTextureSubImage2DEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC)(GLuint, GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC glad_glCompressedTextureSubImage1DEXT;\n#define glCompressedTextureSubImage1DEXT glad_glCompressedTextureSubImage1DEXT\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC)(GLuint, GLenum, GLint, void*);\nGLAPI PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC glad_glGetCompressedTextureImageEXT;\n#define glGetCompressedTextureImageEXT glad_glGetCompressedTextureImageEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC)(GLenum, GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC glad_glCompressedMultiTexImage3DEXT;\n#define glCompressedMultiTexImage3DEXT glad_glCompressedMultiTexImage3DEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC)(GLenum, GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC glad_glCompressedMultiTexImage2DEXT;\n#define glCompressedMultiTexImage2DEXT glad_glCompressedMultiTexImage2DEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC)(GLenum, GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC glad_glCompressedMultiTexImage1DEXT;\n#define glCompressedMultiTexImage1DEXT glad_glCompressedMultiTexImage1DEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC)(GLenum, GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC glad_glCompressedMultiTexSubImage3DEXT;\n#define glCompressedMultiTexSubImage3DEXT glad_glCompressedMultiTexSubImage3DEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC)(GLenum, GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC glad_glCompressedMultiTexSubImage2DEXT;\n#define glCompressedMultiTexSubImage2DEXT glad_glCompressedMultiTexSubImage2DEXT\ntypedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC)(GLenum, GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC glad_glCompressedMultiTexSubImage1DEXT;\n#define glCompressedMultiTexSubImage1DEXT glad_glCompressedMultiTexSubImage1DEXT\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC)(GLenum, GLenum, GLint, void*);\nGLAPI PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC glad_glGetCompressedMultiTexImageEXT;\n#define glGetCompressedMultiTexImageEXT glad_glGetCompressedMultiTexImageEXT\ntypedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLMATRIXLOADTRANSPOSEFEXTPROC glad_glMatrixLoadTransposefEXT;\n#define glMatrixLoadTransposefEXT glad_glMatrixLoadTransposefEXT\ntypedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLMATRIXLOADTRANSPOSEDEXTPROC glad_glMatrixLoadTransposedEXT;\n#define glMatrixLoadTransposedEXT glad_glMatrixLoadTransposedEXT\ntypedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLMATRIXMULTTRANSPOSEFEXTPROC glad_glMatrixMultTransposefEXT;\n#define glMatrixMultTransposefEXT glad_glMatrixMultTransposefEXT\ntypedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLMATRIXMULTTRANSPOSEDEXTPROC glad_glMatrixMultTransposedEXT;\n#define glMatrixMultTransposedEXT glad_glMatrixMultTransposedEXT\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC)(GLuint, GLsizeiptr, const void*, GLenum);\nGLAPI PFNGLNAMEDBUFFERDATAEXTPROC glad_glNamedBufferDataEXT;\n#define glNamedBufferDataEXT glad_glNamedBufferDataEXT\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC)(GLuint, GLintptr, GLsizeiptr, const void*);\nGLAPI PFNGLNAMEDBUFFERSUBDATAEXTPROC glad_glNamedBufferSubDataEXT;\n#define glNamedBufferSubDataEXT glad_glNamedBufferSubDataEXT\ntypedef void* (APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC)(GLuint, GLenum);\nGLAPI PFNGLMAPNAMEDBUFFEREXTPROC glad_glMapNamedBufferEXT;\n#define glMapNamedBufferEXT glad_glMapNamedBufferEXT\ntypedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC)(GLuint);\nGLAPI PFNGLUNMAPNAMEDBUFFEREXTPROC glad_glUnmapNamedBufferEXT;\n#define glUnmapNamedBufferEXT glad_glUnmapNamedBufferEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC glad_glGetNamedBufferParameterivEXT;\n#define glGetNamedBufferParameterivEXT glad_glGetNamedBufferParameterivEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC)(GLuint, GLenum, void**);\nGLAPI PFNGLGETNAMEDBUFFERPOINTERVEXTPROC glad_glGetNamedBufferPointervEXT;\n#define glGetNamedBufferPointervEXT glad_glGetNamedBufferPointervEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC)(GLuint, GLintptr, GLsizeiptr, void*);\nGLAPI PFNGLGETNAMEDBUFFERSUBDATAEXTPROC glad_glGetNamedBufferSubDataEXT;\n#define glGetNamedBufferSubDataEXT glad_glGetNamedBufferSubDataEXT\ntypedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC)(GLuint, GLenum, GLenum, GLuint);\nGLAPI PFNGLTEXTUREBUFFEREXTPROC glad_glTextureBufferEXT;\n#define glTextureBufferEXT glad_glTextureBufferEXT\ntypedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC)(GLenum, GLenum, GLenum, GLuint);\nGLAPI PFNGLMULTITEXBUFFEREXTPROC glad_glMultiTexBufferEXT;\n#define glMultiTexBufferEXT glad_glMultiTexBufferEXT\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC)(GLuint, GLenum, GLenum, const GLint*);\nGLAPI PFNGLTEXTUREPARAMETERIIVEXTPROC glad_glTextureParameterIivEXT;\n#define glTextureParameterIivEXT glad_glTextureParameterIivEXT\ntypedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC)(GLuint, GLenum, GLenum, const GLuint*);\nGLAPI PFNGLTEXTUREPARAMETERIUIVEXTPROC glad_glTextureParameterIuivEXT;\n#define glTextureParameterIuivEXT glad_glTextureParameterIuivEXT\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC)(GLuint, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETTEXTUREPARAMETERIIVEXTPROC glad_glGetTextureParameterIivEXT;\n#define glGetTextureParameterIivEXT glad_glGetTextureParameterIivEXT\ntypedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC)(GLuint, GLenum, GLenum, GLuint*);\nGLAPI PFNGLGETTEXTUREPARAMETERIUIVEXTPROC glad_glGetTextureParameterIuivEXT;\n#define glGetTextureParameterIuivEXT glad_glGetTextureParameterIuivEXT\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC)(GLenum, GLenum, GLenum, const GLint*);\nGLAPI PFNGLMULTITEXPARAMETERIIVEXTPROC glad_glMultiTexParameterIivEXT;\n#define glMultiTexParameterIivEXT glad_glMultiTexParameterIivEXT\ntypedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC)(GLenum, GLenum, GLenum, const GLuint*);\nGLAPI PFNGLMULTITEXPARAMETERIUIVEXTPROC glad_glMultiTexParameterIuivEXT;\n#define glMultiTexParameterIuivEXT glad_glMultiTexParameterIuivEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC)(GLenum, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETMULTITEXPARAMETERIIVEXTPROC glad_glGetMultiTexParameterIivEXT;\n#define glGetMultiTexParameterIivEXT glad_glGetMultiTexParameterIivEXT\ntypedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC)(GLenum, GLenum, GLenum, GLuint*);\nGLAPI PFNGLGETMULTITEXPARAMETERIUIVEXTPROC glad_glGetMultiTexParameterIuivEXT;\n#define glGetMultiTexParameterIuivEXT glad_glGetMultiTexParameterIuivEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC)(GLuint, GLenum, GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC glad_glNamedProgramLocalParameters4fvEXT;\n#define glNamedProgramLocalParameters4fvEXT glad_glNamedProgramLocalParameters4fvEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC)(GLuint, GLenum, GLuint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC glad_glNamedProgramLocalParameterI4iEXT;\n#define glNamedProgramLocalParameterI4iEXT glad_glNamedProgramLocalParameterI4iEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC)(GLuint, GLenum, GLuint, const GLint*);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC glad_glNamedProgramLocalParameterI4ivEXT;\n#define glNamedProgramLocalParameterI4ivEXT glad_glNamedProgramLocalParameterI4ivEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC)(GLuint, GLenum, GLuint, GLsizei, const GLint*);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC glad_glNamedProgramLocalParametersI4ivEXT;\n#define glNamedProgramLocalParametersI4ivEXT glad_glNamedProgramLocalParametersI4ivEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC)(GLuint, GLenum, GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC glad_glNamedProgramLocalParameterI4uiEXT;\n#define glNamedProgramLocalParameterI4uiEXT glad_glNamedProgramLocalParameterI4uiEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC)(GLuint, GLenum, GLuint, const GLuint*);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC glad_glNamedProgramLocalParameterI4uivEXT;\n#define glNamedProgramLocalParameterI4uivEXT glad_glNamedProgramLocalParameterI4uivEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC)(GLuint, GLenum, GLuint, GLsizei, const GLuint*);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC glad_glNamedProgramLocalParametersI4uivEXT;\n#define glNamedProgramLocalParametersI4uivEXT glad_glNamedProgramLocalParametersI4uivEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC)(GLuint, GLenum, GLuint, GLint*);\nGLAPI PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC glad_glGetNamedProgramLocalParameterIivEXT;\n#define glGetNamedProgramLocalParameterIivEXT glad_glGetNamedProgramLocalParameterIivEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC)(GLuint, GLenum, GLuint, GLuint*);\nGLAPI PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC glad_glGetNamedProgramLocalParameterIuivEXT;\n#define glGetNamedProgramLocalParameterIuivEXT glad_glGetNamedProgramLocalParameterIuivEXT\ntypedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC)(GLenum, GLuint);\nGLAPI PFNGLENABLECLIENTSTATEIEXTPROC glad_glEnableClientStateiEXT;\n#define glEnableClientStateiEXT glad_glEnableClientStateiEXT\ntypedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC)(GLenum, GLuint);\nGLAPI PFNGLDISABLECLIENTSTATEIEXTPROC glad_glDisableClientStateiEXT;\n#define glDisableClientStateiEXT glad_glDisableClientStateiEXT\ntypedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC)(GLenum, GLuint, GLfloat*);\nGLAPI PFNGLGETFLOATI_VEXTPROC glad_glGetFloati_vEXT;\n#define glGetFloati_vEXT glad_glGetFloati_vEXT\ntypedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC)(GLenum, GLuint, GLdouble*);\nGLAPI PFNGLGETDOUBLEI_VEXTPROC glad_glGetDoublei_vEXT;\n#define glGetDoublei_vEXT glad_glGetDoublei_vEXT\ntypedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC)(GLenum, GLuint, void**);\nGLAPI PFNGLGETPOINTERI_VEXTPROC glad_glGetPointeri_vEXT;\n#define glGetPointeri_vEXT glad_glGetPointeri_vEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC)(GLuint, GLenum, GLenum, GLsizei, const void*);\nGLAPI PFNGLNAMEDPROGRAMSTRINGEXTPROC glad_glNamedProgramStringEXT;\n#define glNamedProgramStringEXT glad_glNamedProgramStringEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC)(GLuint, GLenum, GLuint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC glad_glNamedProgramLocalParameter4dEXT;\n#define glNamedProgramLocalParameter4dEXT glad_glNamedProgramLocalParameter4dEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC)(GLuint, GLenum, GLuint, const GLdouble*);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC glad_glNamedProgramLocalParameter4dvEXT;\n#define glNamedProgramLocalParameter4dvEXT glad_glNamedProgramLocalParameter4dvEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC)(GLuint, GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC glad_glNamedProgramLocalParameter4fEXT;\n#define glNamedProgramLocalParameter4fEXT glad_glNamedProgramLocalParameter4fEXT\ntypedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC)(GLuint, GLenum, GLuint, const GLfloat*);\nGLAPI PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC glad_glNamedProgramLocalParameter4fvEXT;\n#define glNamedProgramLocalParameter4fvEXT glad_glNamedProgramLocalParameter4fvEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC)(GLuint, GLenum, GLuint, GLdouble*);\nGLAPI PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC glad_glGetNamedProgramLocalParameterdvEXT;\n#define glGetNamedProgramLocalParameterdvEXT glad_glGetNamedProgramLocalParameterdvEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC)(GLuint, GLenum, GLuint, GLfloat*);\nGLAPI PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC glad_glGetNamedProgramLocalParameterfvEXT;\n#define glGetNamedProgramLocalParameterfvEXT glad_glGetNamedProgramLocalParameterfvEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC)(GLuint, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETNAMEDPROGRAMIVEXTPROC glad_glGetNamedProgramivEXT;\n#define glGetNamedProgramivEXT glad_glGetNamedProgramivEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC)(GLuint, GLenum, GLenum, void*);\nGLAPI PFNGLGETNAMEDPROGRAMSTRINGEXTPROC glad_glGetNamedProgramStringEXT;\n#define glGetNamedProgramStringEXT glad_glGetNamedProgramStringEXT\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC)(GLuint, GLenum, GLsizei, GLsizei);\nGLAPI PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC glad_glNamedRenderbufferStorageEXT;\n#define glNamedRenderbufferStorageEXT glad_glNamedRenderbufferStorageEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC glad_glGetNamedRenderbufferParameterivEXT;\n#define glGetNamedRenderbufferParameterivEXT glad_glGetNamedRenderbufferParameterivEXT\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)(GLuint, GLsizei, GLenum, GLsizei, GLsizei);\nGLAPI PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glNamedRenderbufferStorageMultisampleEXT;\n#define glNamedRenderbufferStorageMultisampleEXT glad_glNamedRenderbufferStorageMultisampleEXT\ntypedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC)(GLuint, GLsizei, GLsizei, GLenum, GLsizei, GLsizei);\nGLAPI PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC glad_glNamedRenderbufferStorageMultisampleCoverageEXT;\n#define glNamedRenderbufferStorageMultisampleCoverageEXT glad_glNamedRenderbufferStorageMultisampleCoverageEXT\ntypedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC)(GLuint, GLenum);\nGLAPI PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC glad_glCheckNamedFramebufferStatusEXT;\n#define glCheckNamedFramebufferStatusEXT glad_glCheckNamedFramebufferStatusEXT\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC)(GLuint, GLenum, GLenum, GLuint, GLint);\nGLAPI PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC glad_glNamedFramebufferTexture1DEXT;\n#define glNamedFramebufferTexture1DEXT glad_glNamedFramebufferTexture1DEXT\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC)(GLuint, GLenum, GLenum, GLuint, GLint);\nGLAPI PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC glad_glNamedFramebufferTexture2DEXT;\n#define glNamedFramebufferTexture2DEXT glad_glNamedFramebufferTexture2DEXT\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC)(GLuint, GLenum, GLenum, GLuint, GLint, GLint);\nGLAPI PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC glad_glNamedFramebufferTexture3DEXT;\n#define glNamedFramebufferTexture3DEXT glad_glNamedFramebufferTexture3DEXT\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC)(GLuint, GLenum, GLenum, GLuint);\nGLAPI PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC glad_glNamedFramebufferRenderbufferEXT;\n#define glNamedFramebufferRenderbufferEXT glad_glNamedFramebufferRenderbufferEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)(GLuint, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glad_glGetNamedFramebufferAttachmentParameterivEXT;\n#define glGetNamedFramebufferAttachmentParameterivEXT glad_glGetNamedFramebufferAttachmentParameterivEXT\ntypedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC)(GLuint, GLenum);\nGLAPI PFNGLGENERATETEXTUREMIPMAPEXTPROC glad_glGenerateTextureMipmapEXT;\n#define glGenerateTextureMipmapEXT glad_glGenerateTextureMipmapEXT\ntypedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC)(GLenum, GLenum);\nGLAPI PFNGLGENERATEMULTITEXMIPMAPEXTPROC glad_glGenerateMultiTexMipmapEXT;\n#define glGenerateMultiTexMipmapEXT glad_glGenerateMultiTexMipmapEXT\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC)(GLuint, GLenum);\nGLAPI PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC glad_glFramebufferDrawBufferEXT;\n#define glFramebufferDrawBufferEXT glad_glFramebufferDrawBufferEXT\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC)(GLuint, GLsizei, const GLenum*);\nGLAPI PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC glad_glFramebufferDrawBuffersEXT;\n#define glFramebufferDrawBuffersEXT glad_glFramebufferDrawBuffersEXT\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC)(GLuint, GLenum);\nGLAPI PFNGLFRAMEBUFFERREADBUFFEREXTPROC glad_glFramebufferReadBufferEXT;\n#define glFramebufferReadBufferEXT glad_glFramebufferReadBufferEXT\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC glad_glGetFramebufferParameterivEXT;\n#define glGetFramebufferParameterivEXT glad_glGetFramebufferParameterivEXT\ntypedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC)(GLuint, GLuint, GLintptr, GLintptr, GLsizeiptr);\nGLAPI PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC glad_glNamedCopyBufferSubDataEXT;\n#define glNamedCopyBufferSubDataEXT glad_glNamedCopyBufferSubDataEXT\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC)(GLuint, GLenum, GLuint, GLint);\nGLAPI PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC glad_glNamedFramebufferTextureEXT;\n#define glNamedFramebufferTextureEXT glad_glNamedFramebufferTextureEXT\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC)(GLuint, GLenum, GLuint, GLint, GLint);\nGLAPI PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC glad_glNamedFramebufferTextureLayerEXT;\n#define glNamedFramebufferTextureLayerEXT glad_glNamedFramebufferTextureLayerEXT\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC)(GLuint, GLenum, GLuint, GLint, GLenum);\nGLAPI PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC glad_glNamedFramebufferTextureFaceEXT;\n#define glNamedFramebufferTextureFaceEXT glad_glNamedFramebufferTextureFaceEXT\ntypedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC)(GLuint, GLenum, GLuint);\nGLAPI PFNGLTEXTURERENDERBUFFEREXTPROC glad_glTextureRenderbufferEXT;\n#define glTextureRenderbufferEXT glad_glTextureRenderbufferEXT\ntypedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC)(GLenum, GLenum, GLuint);\nGLAPI PFNGLMULTITEXRENDERBUFFEREXTPROC glad_glMultiTexRenderbufferEXT;\n#define glMultiTexRenderbufferEXT glad_glMultiTexRenderbufferEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC)(GLuint, GLuint, GLint, GLenum, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC glad_glVertexArrayVertexOffsetEXT;\n#define glVertexArrayVertexOffsetEXT glad_glVertexArrayVertexOffsetEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC)(GLuint, GLuint, GLint, GLenum, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYCOLOROFFSETEXTPROC glad_glVertexArrayColorOffsetEXT;\n#define glVertexArrayColorOffsetEXT glad_glVertexArrayColorOffsetEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC)(GLuint, GLuint, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC glad_glVertexArrayEdgeFlagOffsetEXT;\n#define glVertexArrayEdgeFlagOffsetEXT glad_glVertexArrayEdgeFlagOffsetEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC)(GLuint, GLuint, GLenum, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYINDEXOFFSETEXTPROC glad_glVertexArrayIndexOffsetEXT;\n#define glVertexArrayIndexOffsetEXT glad_glVertexArrayIndexOffsetEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC)(GLuint, GLuint, GLenum, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYNORMALOFFSETEXTPROC glad_glVertexArrayNormalOffsetEXT;\n#define glVertexArrayNormalOffsetEXT glad_glVertexArrayNormalOffsetEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC)(GLuint, GLuint, GLint, GLenum, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC glad_glVertexArrayTexCoordOffsetEXT;\n#define glVertexArrayTexCoordOffsetEXT glad_glVertexArrayTexCoordOffsetEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC)(GLuint, GLuint, GLenum, GLint, GLenum, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC glad_glVertexArrayMultiTexCoordOffsetEXT;\n#define glVertexArrayMultiTexCoordOffsetEXT glad_glVertexArrayMultiTexCoordOffsetEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC)(GLuint, GLuint, GLenum, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC glad_glVertexArrayFogCoordOffsetEXT;\n#define glVertexArrayFogCoordOffsetEXT glad_glVertexArrayFogCoordOffsetEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC)(GLuint, GLuint, GLint, GLenum, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC glad_glVertexArraySecondaryColorOffsetEXT;\n#define glVertexArraySecondaryColorOffsetEXT glad_glVertexArraySecondaryColorOffsetEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC)(GLuint, GLuint, GLuint, GLint, GLenum, GLboolean, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC glad_glVertexArrayVertexAttribOffsetEXT;\n#define glVertexArrayVertexAttribOffsetEXT glad_glVertexArrayVertexAttribOffsetEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC)(GLuint, GLuint, GLuint, GLint, GLenum, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC glad_glVertexArrayVertexAttribIOffsetEXT;\n#define glVertexArrayVertexAttribIOffsetEXT glad_glVertexArrayVertexAttribIOffsetEXT\ntypedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC)(GLuint, GLenum);\nGLAPI PFNGLENABLEVERTEXARRAYEXTPROC glad_glEnableVertexArrayEXT;\n#define glEnableVertexArrayEXT glad_glEnableVertexArrayEXT\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC)(GLuint, GLenum);\nGLAPI PFNGLDISABLEVERTEXARRAYEXTPROC glad_glDisableVertexArrayEXT;\n#define glDisableVertexArrayEXT glad_glDisableVertexArrayEXT\ntypedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC)(GLuint, GLuint);\nGLAPI PFNGLENABLEVERTEXARRAYATTRIBEXTPROC glad_glEnableVertexArrayAttribEXT;\n#define glEnableVertexArrayAttribEXT glad_glEnableVertexArrayAttribEXT\ntypedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC)(GLuint, GLuint);\nGLAPI PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC glad_glDisableVertexArrayAttribEXT;\n#define glDisableVertexArrayAttribEXT glad_glDisableVertexArrayAttribEXT\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVERTEXARRAYINTEGERVEXTPROC glad_glGetVertexArrayIntegervEXT;\n#define glGetVertexArrayIntegervEXT glad_glGetVertexArrayIntegervEXT\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC)(GLuint, GLenum, void**);\nGLAPI PFNGLGETVERTEXARRAYPOINTERVEXTPROC glad_glGetVertexArrayPointervEXT;\n#define glGetVertexArrayPointervEXT glad_glGetVertexArrayPointervEXT\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC)(GLuint, GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC glad_glGetVertexArrayIntegeri_vEXT;\n#define glGetVertexArrayIntegeri_vEXT glad_glGetVertexArrayIntegeri_vEXT\ntypedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC)(GLuint, GLuint, GLenum, void**);\nGLAPI PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC glad_glGetVertexArrayPointeri_vEXT;\n#define glGetVertexArrayPointeri_vEXT glad_glGetVertexArrayPointeri_vEXT\ntypedef void* (APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC)(GLuint, GLintptr, GLsizeiptr, GLbitfield);\nGLAPI PFNGLMAPNAMEDBUFFERRANGEEXTPROC glad_glMapNamedBufferRangeEXT;\n#define glMapNamedBufferRangeEXT glad_glMapNamedBufferRangeEXT\ntypedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC)(GLuint, GLintptr, GLsizeiptr);\nGLAPI PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC glad_glFlushMappedNamedBufferRangeEXT;\n#define glFlushMappedNamedBufferRangeEXT glad_glFlushMappedNamedBufferRangeEXT\ntypedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC)(GLuint, GLsizeiptr, const void*, GLbitfield);\nGLAPI PFNGLNAMEDBUFFERSTORAGEEXTPROC glad_glNamedBufferStorageEXT;\n#define glNamedBufferStorageEXT glad_glNamedBufferStorageEXT\ntypedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC)(GLuint, GLenum, GLenum, GLenum, const void*);\nGLAPI PFNGLCLEARNAMEDBUFFERDATAEXTPROC glad_glClearNamedBufferDataEXT;\n#define glClearNamedBufferDataEXT glad_glClearNamedBufferDataEXT\ntypedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC)(GLuint, GLenum, GLsizeiptr, GLsizeiptr, GLenum, GLenum, const void*);\nGLAPI PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC glad_glClearNamedBufferSubDataEXT;\n#define glClearNamedBufferSubDataEXT glad_glClearNamedBufferSubDataEXT\ntypedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC)(GLuint, GLenum, GLint);\nGLAPI PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC glad_glNamedFramebufferParameteriEXT;\n#define glNamedFramebufferParameteriEXT glad_glNamedFramebufferParameteriEXT\ntypedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC glad_glGetNamedFramebufferParameterivEXT;\n#define glGetNamedFramebufferParameterivEXT glad_glGetNamedFramebufferParameterivEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC)(GLuint, GLint, GLdouble);\nGLAPI PFNGLPROGRAMUNIFORM1DEXTPROC glad_glProgramUniform1dEXT;\n#define glProgramUniform1dEXT glad_glProgramUniform1dEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC)(GLuint, GLint, GLdouble, GLdouble);\nGLAPI PFNGLPROGRAMUNIFORM2DEXTPROC glad_glProgramUniform2dEXT;\n#define glProgramUniform2dEXT glad_glProgramUniform2dEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC)(GLuint, GLint, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLPROGRAMUNIFORM3DEXTPROC glad_glProgramUniform3dEXT;\n#define glProgramUniform3dEXT glad_glProgramUniform3dEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC)(GLuint, GLint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLPROGRAMUNIFORM4DEXTPROC glad_glProgramUniform4dEXT;\n#define glProgramUniform4dEXT glad_glProgramUniform4dEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC)(GLuint, GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORM1DVEXTPROC glad_glProgramUniform1dvEXT;\n#define glProgramUniform1dvEXT glad_glProgramUniform1dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC)(GLuint, GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORM2DVEXTPROC glad_glProgramUniform2dvEXT;\n#define glProgramUniform2dvEXT glad_glProgramUniform2dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC)(GLuint, GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORM3DVEXTPROC glad_glProgramUniform3dvEXT;\n#define glProgramUniform3dvEXT glad_glProgramUniform3dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC)(GLuint, GLint, GLsizei, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORM4DVEXTPROC glad_glProgramUniform4dvEXT;\n#define glProgramUniform4dvEXT glad_glProgramUniform4dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC glad_glProgramUniformMatrix2dvEXT;\n#define glProgramUniformMatrix2dvEXT glad_glProgramUniformMatrix2dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC glad_glProgramUniformMatrix3dvEXT;\n#define glProgramUniformMatrix3dvEXT glad_glProgramUniformMatrix3dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC glad_glProgramUniformMatrix4dvEXT;\n#define glProgramUniformMatrix4dvEXT glad_glProgramUniformMatrix4dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC glad_glProgramUniformMatrix2x3dvEXT;\n#define glProgramUniformMatrix2x3dvEXT glad_glProgramUniformMatrix2x3dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC glad_glProgramUniformMatrix2x4dvEXT;\n#define glProgramUniformMatrix2x4dvEXT glad_glProgramUniformMatrix2x4dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC glad_glProgramUniformMatrix3x2dvEXT;\n#define glProgramUniformMatrix3x2dvEXT glad_glProgramUniformMatrix3x2dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC glad_glProgramUniformMatrix3x4dvEXT;\n#define glProgramUniformMatrix3x4dvEXT glad_glProgramUniformMatrix3x4dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC glad_glProgramUniformMatrix4x2dvEXT;\n#define glProgramUniformMatrix4x2dvEXT glad_glProgramUniformMatrix4x2dvEXT\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC)(GLuint, GLint, GLsizei, GLboolean, const GLdouble*);\nGLAPI PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC glad_glProgramUniformMatrix4x3dvEXT;\n#define glProgramUniformMatrix4x3dvEXT glad_glProgramUniformMatrix4x3dvEXT\ntypedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC)(GLuint, GLenum, GLenum, GLuint, GLintptr, GLsizeiptr);\nGLAPI PFNGLTEXTUREBUFFERRANGEEXTPROC glad_glTextureBufferRangeEXT;\n#define glTextureBufferRangeEXT glad_glTextureBufferRangeEXT\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC)(GLuint, GLenum, GLsizei, GLenum, GLsizei);\nGLAPI PFNGLTEXTURESTORAGE1DEXTPROC glad_glTextureStorage1DEXT;\n#define glTextureStorage1DEXT glad_glTextureStorage1DEXT\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC)(GLuint, GLenum, GLsizei, GLenum, GLsizei, GLsizei);\nGLAPI PFNGLTEXTURESTORAGE2DEXTPROC glad_glTextureStorage2DEXT;\n#define glTextureStorage2DEXT glad_glTextureStorage2DEXT\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC)(GLuint, GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei);\nGLAPI PFNGLTEXTURESTORAGE3DEXTPROC glad_glTextureStorage3DEXT;\n#define glTextureStorage3DEXT glad_glTextureStorage3DEXT\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC)(GLuint, GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC glad_glTextureStorage2DMultisampleEXT;\n#define glTextureStorage2DMultisampleEXT glad_glTextureStorage2DMultisampleEXT\ntypedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC)(GLuint, GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC glad_glTextureStorage3DMultisampleEXT;\n#define glTextureStorage3DMultisampleEXT glad_glTextureStorage3DMultisampleEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC)(GLuint, GLuint, GLuint, GLintptr, GLsizei);\nGLAPI PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC glad_glVertexArrayBindVertexBufferEXT;\n#define glVertexArrayBindVertexBufferEXT glad_glVertexArrayBindVertexBufferEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC)(GLuint, GLuint, GLint, GLenum, GLboolean, GLuint);\nGLAPI PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC glad_glVertexArrayVertexAttribFormatEXT;\n#define glVertexArrayVertexAttribFormatEXT glad_glVertexArrayVertexAttribFormatEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC)(GLuint, GLuint, GLint, GLenum, GLuint);\nGLAPI PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC glad_glVertexArrayVertexAttribIFormatEXT;\n#define glVertexArrayVertexAttribIFormatEXT glad_glVertexArrayVertexAttribIFormatEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC)(GLuint, GLuint, GLint, GLenum, GLuint);\nGLAPI PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC glad_glVertexArrayVertexAttribLFormatEXT;\n#define glVertexArrayVertexAttribLFormatEXT glad_glVertexArrayVertexAttribLFormatEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC glad_glVertexArrayVertexAttribBindingEXT;\n#define glVertexArrayVertexAttribBindingEXT glad_glVertexArrayVertexAttribBindingEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC glad_glVertexArrayVertexBindingDivisorEXT;\n#define glVertexArrayVertexBindingDivisorEXT glad_glVertexArrayVertexBindingDivisorEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC)(GLuint, GLuint, GLuint, GLint, GLenum, GLsizei, GLintptr);\nGLAPI PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC glad_glVertexArrayVertexAttribLOffsetEXT;\n#define glVertexArrayVertexAttribLOffsetEXT glad_glVertexArrayVertexAttribLOffsetEXT\ntypedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC)(GLuint, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXTUREPAGECOMMITMENTEXTPROC glad_glTexturePageCommitmentEXT;\n#define glTexturePageCommitmentEXT glad_glTexturePageCommitmentEXT\ntypedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC glad_glVertexArrayVertexAttribDivisorEXT;\n#define glVertexArrayVertexAttribDivisorEXT glad_glVertexArrayVertexAttribDivisorEXT\n#endif\n#ifndef GL_AMD_sample_positions\n#define GL_AMD_sample_positions 1\nGLAPI int GLAD_GL_AMD_sample_positions;\ntypedef void (APIENTRYP PFNGLSETMULTISAMPLEFVAMDPROC)(GLenum, GLuint, const GLfloat*);\nGLAPI PFNGLSETMULTISAMPLEFVAMDPROC glad_glSetMultisamplefvAMD;\n#define glSetMultisamplefvAMD glad_glSetMultisamplefvAMD\n#endif\n#ifndef GL_NV_vertex_program\n#define GL_NV_vertex_program 1\nGLAPI int GLAD_GL_NV_vertex_program;\ntypedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC)(GLsizei, const GLuint*, GLboolean*);\nGLAPI PFNGLAREPROGRAMSRESIDENTNVPROC glad_glAreProgramsResidentNV;\n#define glAreProgramsResidentNV glad_glAreProgramsResidentNV\ntypedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDPROGRAMNVPROC glad_glBindProgramNV;\n#define glBindProgramNV glad_glBindProgramNV\ntypedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEPROGRAMSNVPROC glad_glDeleteProgramsNV;\n#define glDeleteProgramsNV glad_glDeleteProgramsNV\ntypedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC)(GLenum, GLuint, const GLfloat*);\nGLAPI PFNGLEXECUTEPROGRAMNVPROC glad_glExecuteProgramNV;\n#define glExecuteProgramNV glad_glExecuteProgramNV\ntypedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENPROGRAMSNVPROC glad_glGenProgramsNV;\n#define glGenProgramsNV glad_glGenProgramsNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC)(GLenum, GLuint, GLenum, GLdouble*);\nGLAPI PFNGLGETPROGRAMPARAMETERDVNVPROC glad_glGetProgramParameterdvNV;\n#define glGetProgramParameterdvNV glad_glGetProgramParameterdvNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC)(GLenum, GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETPROGRAMPARAMETERFVNVPROC glad_glGetProgramParameterfvNV;\n#define glGetProgramParameterfvNV glad_glGetProgramParameterfvNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETPROGRAMIVNVPROC glad_glGetProgramivNV;\n#define glGetProgramivNV glad_glGetProgramivNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC)(GLuint, GLenum, GLubyte*);\nGLAPI PFNGLGETPROGRAMSTRINGNVPROC glad_glGetProgramStringNV;\n#define glGetProgramStringNV glad_glGetProgramStringNV\ntypedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC)(GLenum, GLuint, GLenum, GLint*);\nGLAPI PFNGLGETTRACKMATRIXIVNVPROC glad_glGetTrackMatrixivNV;\n#define glGetTrackMatrixivNV glad_glGetTrackMatrixivNV\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC)(GLuint, GLenum, GLdouble*);\nGLAPI PFNGLGETVERTEXATTRIBDVNVPROC glad_glGetVertexAttribdvNV;\n#define glGetVertexAttribdvNV glad_glGetVertexAttribdvNV\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETVERTEXATTRIBFVNVPROC glad_glGetVertexAttribfvNV;\n#define glGetVertexAttribfvNV glad_glGetVertexAttribfvNV\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVERTEXATTRIBIVNVPROC glad_glGetVertexAttribivNV;\n#define glGetVertexAttribivNV glad_glGetVertexAttribivNV\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC)(GLuint, GLenum, void**);\nGLAPI PFNGLGETVERTEXATTRIBPOINTERVNVPROC glad_glGetVertexAttribPointervNV;\n#define glGetVertexAttribPointervNV glad_glGetVertexAttribPointervNV\ntypedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC)(GLuint);\nGLAPI PFNGLISPROGRAMNVPROC glad_glIsProgramNV;\n#define glIsProgramNV glad_glIsProgramNV\ntypedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC)(GLenum, GLuint, GLsizei, const GLubyte*);\nGLAPI PFNGLLOADPROGRAMNVPROC glad_glLoadProgramNV;\n#define glLoadProgramNV glad_glLoadProgramNV\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC)(GLenum, GLuint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLPROGRAMPARAMETER4DNVPROC glad_glProgramParameter4dNV;\n#define glProgramParameter4dNV glad_glProgramParameter4dNV\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC)(GLenum, GLuint, const GLdouble*);\nGLAPI PFNGLPROGRAMPARAMETER4DVNVPROC glad_glProgramParameter4dvNV;\n#define glProgramParameter4dvNV glad_glProgramParameter4dvNV\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC)(GLenum, GLuint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLPROGRAMPARAMETER4FNVPROC glad_glProgramParameter4fNV;\n#define glProgramParameter4fNV glad_glProgramParameter4fNV\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC)(GLenum, GLuint, const GLfloat*);\nGLAPI PFNGLPROGRAMPARAMETER4FVNVPROC glad_glProgramParameter4fvNV;\n#define glProgramParameter4fvNV glad_glProgramParameter4fvNV\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC)(GLenum, GLuint, GLsizei, const GLdouble*);\nGLAPI PFNGLPROGRAMPARAMETERS4DVNVPROC glad_glProgramParameters4dvNV;\n#define glProgramParameters4dvNV glad_glProgramParameters4dvNV\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC)(GLenum, GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLPROGRAMPARAMETERS4FVNVPROC glad_glProgramParameters4fvNV;\n#define glProgramParameters4fvNV glad_glProgramParameters4fvNV\ntypedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLREQUESTRESIDENTPROGRAMSNVPROC glad_glRequestResidentProgramsNV;\n#define glRequestResidentProgramsNV glad_glRequestResidentProgramsNV\ntypedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC)(GLenum, GLuint, GLenum, GLenum);\nGLAPI PFNGLTRACKMATRIXNVPROC glad_glTrackMatrixNV;\n#define glTrackMatrixNV glad_glTrackMatrixNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC)(GLuint, GLint, GLenum, GLsizei, const void*);\nGLAPI PFNGLVERTEXATTRIBPOINTERNVPROC glad_glVertexAttribPointerNV;\n#define glVertexAttribPointerNV glad_glVertexAttribPointerNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC)(GLuint, GLdouble);\nGLAPI PFNGLVERTEXATTRIB1DNVPROC glad_glVertexAttrib1dNV;\n#define glVertexAttrib1dNV glad_glVertexAttrib1dNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB1DVNVPROC glad_glVertexAttrib1dvNV;\n#define glVertexAttrib1dvNV glad_glVertexAttrib1dvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC)(GLuint, GLfloat);\nGLAPI PFNGLVERTEXATTRIB1FNVPROC glad_glVertexAttrib1fNV;\n#define glVertexAttrib1fNV glad_glVertexAttrib1fNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB1FVNVPROC glad_glVertexAttrib1fvNV;\n#define glVertexAttrib1fvNV glad_glVertexAttrib1fvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC)(GLuint, GLshort);\nGLAPI PFNGLVERTEXATTRIB1SNVPROC glad_glVertexAttrib1sNV;\n#define glVertexAttrib1sNV glad_glVertexAttrib1sNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB1SVNVPROC glad_glVertexAttrib1svNV;\n#define glVertexAttrib1svNV glad_glVertexAttrib1svNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC)(GLuint, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIB2DNVPROC glad_glVertexAttrib2dNV;\n#define glVertexAttrib2dNV glad_glVertexAttrib2dNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB2DVNVPROC glad_glVertexAttrib2dvNV;\n#define glVertexAttrib2dvNV glad_glVertexAttrib2dvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC)(GLuint, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXATTRIB2FNVPROC glad_glVertexAttrib2fNV;\n#define glVertexAttrib2fNV glad_glVertexAttrib2fNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB2FVNVPROC glad_glVertexAttrib2fvNV;\n#define glVertexAttrib2fvNV glad_glVertexAttrib2fvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC)(GLuint, GLshort, GLshort);\nGLAPI PFNGLVERTEXATTRIB2SNVPROC glad_glVertexAttrib2sNV;\n#define glVertexAttrib2sNV glad_glVertexAttrib2sNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB2SVNVPROC glad_glVertexAttrib2svNV;\n#define glVertexAttrib2svNV glad_glVertexAttrib2svNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC)(GLuint, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIB3DNVPROC glad_glVertexAttrib3dNV;\n#define glVertexAttrib3dNV glad_glVertexAttrib3dNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB3DVNVPROC glad_glVertexAttrib3dvNV;\n#define glVertexAttrib3dvNV glad_glVertexAttrib3dvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC)(GLuint, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXATTRIB3FNVPROC glad_glVertexAttrib3fNV;\n#define glVertexAttrib3fNV glad_glVertexAttrib3fNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB3FVNVPROC glad_glVertexAttrib3fvNV;\n#define glVertexAttrib3fvNV glad_glVertexAttrib3fvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC)(GLuint, GLshort, GLshort, GLshort);\nGLAPI PFNGLVERTEXATTRIB3SNVPROC glad_glVertexAttrib3sNV;\n#define glVertexAttrib3sNV glad_glVertexAttrib3sNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB3SVNVPROC glad_glVertexAttrib3svNV;\n#define glVertexAttrib3svNV glad_glVertexAttrib3svNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC)(GLuint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIB4DNVPROC glad_glVertexAttrib4dNV;\n#define glVertexAttrib4dNV glad_glVertexAttrib4dNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIB4DVNVPROC glad_glVertexAttrib4dvNV;\n#define glVertexAttrib4dvNV glad_glVertexAttrib4dvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXATTRIB4FNVPROC glad_glVertexAttrib4fNV;\n#define glVertexAttrib4fNV glad_glVertexAttrib4fNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIB4FVNVPROC glad_glVertexAttrib4fvNV;\n#define glVertexAttrib4fvNV glad_glVertexAttrib4fvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC)(GLuint, GLshort, GLshort, GLshort, GLshort);\nGLAPI PFNGLVERTEXATTRIB4SNVPROC glad_glVertexAttrib4sNV;\n#define glVertexAttrib4sNV glad_glVertexAttrib4sNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIB4SVNVPROC glad_glVertexAttrib4svNV;\n#define glVertexAttrib4svNV glad_glVertexAttrib4svNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC)(GLuint, GLubyte, GLubyte, GLubyte, GLubyte);\nGLAPI PFNGLVERTEXATTRIB4UBNVPROC glad_glVertexAttrib4ubNV;\n#define glVertexAttrib4ubNV glad_glVertexAttrib4ubNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC)(GLuint, const GLubyte*);\nGLAPI PFNGLVERTEXATTRIB4UBVNVPROC glad_glVertexAttrib4ubvNV;\n#define glVertexAttrib4ubvNV glad_glVertexAttrib4ubvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC)(GLuint, GLsizei, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBS1DVNVPROC glad_glVertexAttribs1dvNV;\n#define glVertexAttribs1dvNV glad_glVertexAttribs1dvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC)(GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIBS1FVNVPROC glad_glVertexAttribs1fvNV;\n#define glVertexAttribs1fvNV glad_glVertexAttribs1fvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC)(GLuint, GLsizei, const GLshort*);\nGLAPI PFNGLVERTEXATTRIBS1SVNVPROC glad_glVertexAttribs1svNV;\n#define glVertexAttribs1svNV glad_glVertexAttribs1svNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC)(GLuint, GLsizei, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBS2DVNVPROC glad_glVertexAttribs2dvNV;\n#define glVertexAttribs2dvNV glad_glVertexAttribs2dvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC)(GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIBS2FVNVPROC glad_glVertexAttribs2fvNV;\n#define glVertexAttribs2fvNV glad_glVertexAttribs2fvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC)(GLuint, GLsizei, const GLshort*);\nGLAPI PFNGLVERTEXATTRIBS2SVNVPROC glad_glVertexAttribs2svNV;\n#define glVertexAttribs2svNV glad_glVertexAttribs2svNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC)(GLuint, GLsizei, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBS3DVNVPROC glad_glVertexAttribs3dvNV;\n#define glVertexAttribs3dvNV glad_glVertexAttribs3dvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC)(GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIBS3FVNVPROC glad_glVertexAttribs3fvNV;\n#define glVertexAttribs3fvNV glad_glVertexAttribs3fvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC)(GLuint, GLsizei, const GLshort*);\nGLAPI PFNGLVERTEXATTRIBS3SVNVPROC glad_glVertexAttribs3svNV;\n#define glVertexAttribs3svNV glad_glVertexAttribs3svNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC)(GLuint, GLsizei, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBS4DVNVPROC glad_glVertexAttribs4dvNV;\n#define glVertexAttribs4dvNV glad_glVertexAttribs4dvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC)(GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLVERTEXATTRIBS4FVNVPROC glad_glVertexAttribs4fvNV;\n#define glVertexAttribs4fvNV glad_glVertexAttribs4fvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC)(GLuint, GLsizei, const GLshort*);\nGLAPI PFNGLVERTEXATTRIBS4SVNVPROC glad_glVertexAttribs4svNV;\n#define glVertexAttribs4svNV glad_glVertexAttribs4svNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC)(GLuint, GLsizei, const GLubyte*);\nGLAPI PFNGLVERTEXATTRIBS4UBVNVPROC glad_glVertexAttribs4ubvNV;\n#define glVertexAttribs4ubvNV glad_glVertexAttribs4ubvNV\n#endif\n#ifndef GL_NV_shader_thread_shuffle\n#define GL_NV_shader_thread_shuffle 1\nGLAPI int GLAD_GL_NV_shader_thread_shuffle;\n#endif\n#ifndef GL_NVX_conditional_render\n#define GL_NVX_conditional_render 1\nGLAPI int GLAD_GL_NVX_conditional_render;\ntypedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVXPROC)(GLuint);\nGLAPI PFNGLBEGINCONDITIONALRENDERNVXPROC glad_glBeginConditionalRenderNVX;\n#define glBeginConditionalRenderNVX glad_glBeginConditionalRenderNVX\ntypedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVXPROC)();\nGLAPI PFNGLENDCONDITIONALRENDERNVXPROC glad_glEndConditionalRenderNVX;\n#define glEndConditionalRenderNVX glad_glEndConditionalRenderNVX\n#endif\n#ifndef GL_EXT_vertex_shader\n#define GL_EXT_vertex_shader 1\nGLAPI int GLAD_GL_EXT_vertex_shader;\ntypedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC)();\nGLAPI PFNGLBEGINVERTEXSHADEREXTPROC glad_glBeginVertexShaderEXT;\n#define glBeginVertexShaderEXT glad_glBeginVertexShaderEXT\ntypedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC)();\nGLAPI PFNGLENDVERTEXSHADEREXTPROC glad_glEndVertexShaderEXT;\n#define glEndVertexShaderEXT glad_glEndVertexShaderEXT\ntypedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC)(GLuint);\nGLAPI PFNGLBINDVERTEXSHADEREXTPROC glad_glBindVertexShaderEXT;\n#define glBindVertexShaderEXT glad_glBindVertexShaderEXT\ntypedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC)(GLuint);\nGLAPI PFNGLGENVERTEXSHADERSEXTPROC glad_glGenVertexShadersEXT;\n#define glGenVertexShadersEXT glad_glGenVertexShadersEXT\ntypedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC)(GLuint);\nGLAPI PFNGLDELETEVERTEXSHADEREXTPROC glad_glDeleteVertexShaderEXT;\n#define glDeleteVertexShaderEXT glad_glDeleteVertexShaderEXT\ntypedef void (APIENTRYP PFNGLSHADEROP1EXTPROC)(GLenum, GLuint, GLuint);\nGLAPI PFNGLSHADEROP1EXTPROC glad_glShaderOp1EXT;\n#define glShaderOp1EXT glad_glShaderOp1EXT\ntypedef void (APIENTRYP PFNGLSHADEROP2EXTPROC)(GLenum, GLuint, GLuint, GLuint);\nGLAPI PFNGLSHADEROP2EXTPROC glad_glShaderOp2EXT;\n#define glShaderOp2EXT glad_glShaderOp2EXT\ntypedef void (APIENTRYP PFNGLSHADEROP3EXTPROC)(GLenum, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLSHADEROP3EXTPROC glad_glShaderOp3EXT;\n#define glShaderOp3EXT glad_glShaderOp3EXT\ntypedef void (APIENTRYP PFNGLSWIZZLEEXTPROC)(GLuint, GLuint, GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLSWIZZLEEXTPROC glad_glSwizzleEXT;\n#define glSwizzleEXT glad_glSwizzleEXT\ntypedef void (APIENTRYP PFNGLWRITEMASKEXTPROC)(GLuint, GLuint, GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLWRITEMASKEXTPROC glad_glWriteMaskEXT;\n#define glWriteMaskEXT glad_glWriteMaskEXT\ntypedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLINSERTCOMPONENTEXTPROC glad_glInsertComponentEXT;\n#define glInsertComponentEXT glad_glInsertComponentEXT\ntypedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLEXTRACTCOMPONENTEXTPROC glad_glExtractComponentEXT;\n#define glExtractComponentEXT glad_glExtractComponentEXT\ntypedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC)(GLenum, GLenum, GLenum, GLuint);\nGLAPI PFNGLGENSYMBOLSEXTPROC glad_glGenSymbolsEXT;\n#define glGenSymbolsEXT glad_glGenSymbolsEXT\ntypedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC)(GLuint, GLenum, const void*);\nGLAPI PFNGLSETINVARIANTEXTPROC glad_glSetInvariantEXT;\n#define glSetInvariantEXT glad_glSetInvariantEXT\ntypedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC)(GLuint, GLenum, const void*);\nGLAPI PFNGLSETLOCALCONSTANTEXTPROC glad_glSetLocalConstantEXT;\n#define glSetLocalConstantEXT glad_glSetLocalConstantEXT\ntypedef void (APIENTRYP PFNGLVARIANTBVEXTPROC)(GLuint, const GLbyte*);\nGLAPI PFNGLVARIANTBVEXTPROC glad_glVariantbvEXT;\n#define glVariantbvEXT glad_glVariantbvEXT\ntypedef void (APIENTRYP PFNGLVARIANTSVEXTPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVARIANTSVEXTPROC glad_glVariantsvEXT;\n#define glVariantsvEXT glad_glVariantsvEXT\ntypedef void (APIENTRYP PFNGLVARIANTIVEXTPROC)(GLuint, const GLint*);\nGLAPI PFNGLVARIANTIVEXTPROC glad_glVariantivEXT;\n#define glVariantivEXT glad_glVariantivEXT\ntypedef void (APIENTRYP PFNGLVARIANTFVEXTPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLVARIANTFVEXTPROC glad_glVariantfvEXT;\n#define glVariantfvEXT glad_glVariantfvEXT\ntypedef void (APIENTRYP PFNGLVARIANTDVEXTPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVARIANTDVEXTPROC glad_glVariantdvEXT;\n#define glVariantdvEXT glad_glVariantdvEXT\ntypedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC)(GLuint, const GLubyte*);\nGLAPI PFNGLVARIANTUBVEXTPROC glad_glVariantubvEXT;\n#define glVariantubvEXT glad_glVariantubvEXT\ntypedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC)(GLuint, const GLushort*);\nGLAPI PFNGLVARIANTUSVEXTPROC glad_glVariantusvEXT;\n#define glVariantusvEXT glad_glVariantusvEXT\ntypedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVARIANTUIVEXTPROC glad_glVariantuivEXT;\n#define glVariantuivEXT glad_glVariantuivEXT\ntypedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC)(GLuint, GLenum, GLuint, const void*);\nGLAPI PFNGLVARIANTPOINTEREXTPROC glad_glVariantPointerEXT;\n#define glVariantPointerEXT glad_glVariantPointerEXT\ntypedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC)(GLuint);\nGLAPI PFNGLENABLEVARIANTCLIENTSTATEEXTPROC glad_glEnableVariantClientStateEXT;\n#define glEnableVariantClientStateEXT glad_glEnableVariantClientStateEXT\ntypedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC)(GLuint);\nGLAPI PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC glad_glDisableVariantClientStateEXT;\n#define glDisableVariantClientStateEXT glad_glDisableVariantClientStateEXT\ntypedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC)(GLenum, GLenum);\nGLAPI PFNGLBINDLIGHTPARAMETEREXTPROC glad_glBindLightParameterEXT;\n#define glBindLightParameterEXT glad_glBindLightParameterEXT\ntypedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC)(GLenum, GLenum);\nGLAPI PFNGLBINDMATERIALPARAMETEREXTPROC glad_glBindMaterialParameterEXT;\n#define glBindMaterialParameterEXT glad_glBindMaterialParameterEXT\ntypedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC)(GLenum, GLenum, GLenum);\nGLAPI PFNGLBINDTEXGENPARAMETEREXTPROC glad_glBindTexGenParameterEXT;\n#define glBindTexGenParameterEXT glad_glBindTexGenParameterEXT\ntypedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC)(GLenum, GLenum);\nGLAPI PFNGLBINDTEXTUREUNITPARAMETEREXTPROC glad_glBindTextureUnitParameterEXT;\n#define glBindTextureUnitParameterEXT glad_glBindTextureUnitParameterEXT\ntypedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC)(GLenum);\nGLAPI PFNGLBINDPARAMETEREXTPROC glad_glBindParameterEXT;\n#define glBindParameterEXT glad_glBindParameterEXT\ntypedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC)(GLuint, GLenum);\nGLAPI PFNGLISVARIANTENABLEDEXTPROC glad_glIsVariantEnabledEXT;\n#define glIsVariantEnabledEXT glad_glIsVariantEnabledEXT\ntypedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC)(GLuint, GLenum, GLboolean*);\nGLAPI PFNGLGETVARIANTBOOLEANVEXTPROC glad_glGetVariantBooleanvEXT;\n#define glGetVariantBooleanvEXT glad_glGetVariantBooleanvEXT\ntypedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVARIANTINTEGERVEXTPROC glad_glGetVariantIntegervEXT;\n#define glGetVariantIntegervEXT glad_glGetVariantIntegervEXT\ntypedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETVARIANTFLOATVEXTPROC glad_glGetVariantFloatvEXT;\n#define glGetVariantFloatvEXT glad_glGetVariantFloatvEXT\ntypedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC)(GLuint, GLenum, void**);\nGLAPI PFNGLGETVARIANTPOINTERVEXTPROC glad_glGetVariantPointervEXT;\n#define glGetVariantPointervEXT glad_glGetVariantPointervEXT\ntypedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC)(GLuint, GLenum, GLboolean*);\nGLAPI PFNGLGETINVARIANTBOOLEANVEXTPROC glad_glGetInvariantBooleanvEXT;\n#define glGetInvariantBooleanvEXT glad_glGetInvariantBooleanvEXT\ntypedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETINVARIANTINTEGERVEXTPROC glad_glGetInvariantIntegervEXT;\n#define glGetInvariantIntegervEXT glad_glGetInvariantIntegervEXT\ntypedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETINVARIANTFLOATVEXTPROC glad_glGetInvariantFloatvEXT;\n#define glGetInvariantFloatvEXT glad_glGetInvariantFloatvEXT\ntypedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC)(GLuint, GLenum, GLboolean*);\nGLAPI PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC glad_glGetLocalConstantBooleanvEXT;\n#define glGetLocalConstantBooleanvEXT glad_glGetLocalConstantBooleanvEXT\ntypedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETLOCALCONSTANTINTEGERVEXTPROC glad_glGetLocalConstantIntegervEXT;\n#define glGetLocalConstantIntegervEXT glad_glGetLocalConstantIntegervEXT\ntypedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETLOCALCONSTANTFLOATVEXTPROC glad_glGetLocalConstantFloatvEXT;\n#define glGetLocalConstantFloatvEXT glad_glGetLocalConstantFloatvEXT\n#endif\n#ifndef GL_EXT_blend_func_separate\n#define GL_EXT_blend_func_separate 1\nGLAPI int GLAD_GL_EXT_blend_func_separate;\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC)(GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLBLENDFUNCSEPARATEEXTPROC glad_glBlendFuncSeparateEXT;\n#define glBlendFuncSeparateEXT glad_glBlendFuncSeparateEXT\n#endif\n#ifndef GL_APPLE_fence\n#define GL_APPLE_fence 1\nGLAPI int GLAD_GL_APPLE_fence;\ntypedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENFENCESAPPLEPROC glad_glGenFencesAPPLE;\n#define glGenFencesAPPLE glad_glGenFencesAPPLE\ntypedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEFENCESAPPLEPROC glad_glDeleteFencesAPPLE;\n#define glDeleteFencesAPPLE glad_glDeleteFencesAPPLE\ntypedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC)(GLuint);\nGLAPI PFNGLSETFENCEAPPLEPROC glad_glSetFenceAPPLE;\n#define glSetFenceAPPLE glad_glSetFenceAPPLE\ntypedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC)(GLuint);\nGLAPI PFNGLISFENCEAPPLEPROC glad_glIsFenceAPPLE;\n#define glIsFenceAPPLE glad_glIsFenceAPPLE\ntypedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC)(GLuint);\nGLAPI PFNGLTESTFENCEAPPLEPROC glad_glTestFenceAPPLE;\n#define glTestFenceAPPLE glad_glTestFenceAPPLE\ntypedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC)(GLuint);\nGLAPI PFNGLFINISHFENCEAPPLEPROC glad_glFinishFenceAPPLE;\n#define glFinishFenceAPPLE glad_glFinishFenceAPPLE\ntypedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC)(GLenum, GLuint);\nGLAPI PFNGLTESTOBJECTAPPLEPROC glad_glTestObjectAPPLE;\n#define glTestObjectAPPLE glad_glTestObjectAPPLE\ntypedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC)(GLenum, GLint);\nGLAPI PFNGLFINISHOBJECTAPPLEPROC glad_glFinishObjectAPPLE;\n#define glFinishObjectAPPLE glad_glFinishObjectAPPLE\n#endif\n#ifndef GL_OES_byte_coordinates\n#define GL_OES_byte_coordinates 1\nGLAPI int GLAD_GL_OES_byte_coordinates;\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1BOESPROC)(GLenum, GLbyte);\nGLAPI PFNGLMULTITEXCOORD1BOESPROC glad_glMultiTexCoord1bOES;\n#define glMultiTexCoord1bOES glad_glMultiTexCoord1bOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1BVOESPROC)(GLenum, const GLbyte*);\nGLAPI PFNGLMULTITEXCOORD1BVOESPROC glad_glMultiTexCoord1bvOES;\n#define glMultiTexCoord1bvOES glad_glMultiTexCoord1bvOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2BOESPROC)(GLenum, GLbyte, GLbyte);\nGLAPI PFNGLMULTITEXCOORD2BOESPROC glad_glMultiTexCoord2bOES;\n#define glMultiTexCoord2bOES glad_glMultiTexCoord2bOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2BVOESPROC)(GLenum, const GLbyte*);\nGLAPI PFNGLMULTITEXCOORD2BVOESPROC glad_glMultiTexCoord2bvOES;\n#define glMultiTexCoord2bvOES glad_glMultiTexCoord2bvOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3BOESPROC)(GLenum, GLbyte, GLbyte, GLbyte);\nGLAPI PFNGLMULTITEXCOORD3BOESPROC glad_glMultiTexCoord3bOES;\n#define glMultiTexCoord3bOES glad_glMultiTexCoord3bOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3BVOESPROC)(GLenum, const GLbyte*);\nGLAPI PFNGLMULTITEXCOORD3BVOESPROC glad_glMultiTexCoord3bvOES;\n#define glMultiTexCoord3bvOES glad_glMultiTexCoord3bvOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4BOESPROC)(GLenum, GLbyte, GLbyte, GLbyte, GLbyte);\nGLAPI PFNGLMULTITEXCOORD4BOESPROC glad_glMultiTexCoord4bOES;\n#define glMultiTexCoord4bOES glad_glMultiTexCoord4bOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4BVOESPROC)(GLenum, const GLbyte*);\nGLAPI PFNGLMULTITEXCOORD4BVOESPROC glad_glMultiTexCoord4bvOES;\n#define glMultiTexCoord4bvOES glad_glMultiTexCoord4bvOES\ntypedef void (APIENTRYP PFNGLTEXCOORD1BOESPROC)(GLbyte);\nGLAPI PFNGLTEXCOORD1BOESPROC glad_glTexCoord1bOES;\n#define glTexCoord1bOES glad_glTexCoord1bOES\ntypedef void (APIENTRYP PFNGLTEXCOORD1BVOESPROC)(const GLbyte*);\nGLAPI PFNGLTEXCOORD1BVOESPROC glad_glTexCoord1bvOES;\n#define glTexCoord1bvOES glad_glTexCoord1bvOES\ntypedef void (APIENTRYP PFNGLTEXCOORD2BOESPROC)(GLbyte, GLbyte);\nGLAPI PFNGLTEXCOORD2BOESPROC glad_glTexCoord2bOES;\n#define glTexCoord2bOES glad_glTexCoord2bOES\ntypedef void (APIENTRYP PFNGLTEXCOORD2BVOESPROC)(const GLbyte*);\nGLAPI PFNGLTEXCOORD2BVOESPROC glad_glTexCoord2bvOES;\n#define glTexCoord2bvOES glad_glTexCoord2bvOES\ntypedef void (APIENTRYP PFNGLTEXCOORD3BOESPROC)(GLbyte, GLbyte, GLbyte);\nGLAPI PFNGLTEXCOORD3BOESPROC glad_glTexCoord3bOES;\n#define glTexCoord3bOES glad_glTexCoord3bOES\ntypedef void (APIENTRYP PFNGLTEXCOORD3BVOESPROC)(const GLbyte*);\nGLAPI PFNGLTEXCOORD3BVOESPROC glad_glTexCoord3bvOES;\n#define glTexCoord3bvOES glad_glTexCoord3bvOES\ntypedef void (APIENTRYP PFNGLTEXCOORD4BOESPROC)(GLbyte, GLbyte, GLbyte, GLbyte);\nGLAPI PFNGLTEXCOORD4BOESPROC glad_glTexCoord4bOES;\n#define glTexCoord4bOES glad_glTexCoord4bOES\ntypedef void (APIENTRYP PFNGLTEXCOORD4BVOESPROC)(const GLbyte*);\nGLAPI PFNGLTEXCOORD4BVOESPROC glad_glTexCoord4bvOES;\n#define glTexCoord4bvOES glad_glTexCoord4bvOES\ntypedef void (APIENTRYP PFNGLVERTEX2BOESPROC)(GLbyte);\nGLAPI PFNGLVERTEX2BOESPROC glad_glVertex2bOES;\n#define glVertex2bOES glad_glVertex2bOES\ntypedef void (APIENTRYP PFNGLVERTEX2BVOESPROC)(const GLbyte*);\nGLAPI PFNGLVERTEX2BVOESPROC glad_glVertex2bvOES;\n#define glVertex2bvOES glad_glVertex2bvOES\ntypedef void (APIENTRYP PFNGLVERTEX3BOESPROC)(GLbyte, GLbyte);\nGLAPI PFNGLVERTEX3BOESPROC glad_glVertex3bOES;\n#define glVertex3bOES glad_glVertex3bOES\ntypedef void (APIENTRYP PFNGLVERTEX3BVOESPROC)(const GLbyte*);\nGLAPI PFNGLVERTEX3BVOESPROC glad_glVertex3bvOES;\n#define glVertex3bvOES glad_glVertex3bvOES\ntypedef void (APIENTRYP PFNGLVERTEX4BOESPROC)(GLbyte, GLbyte, GLbyte);\nGLAPI PFNGLVERTEX4BOESPROC glad_glVertex4bOES;\n#define glVertex4bOES glad_glVertex4bOES\ntypedef void (APIENTRYP PFNGLVERTEX4BVOESPROC)(const GLbyte*);\nGLAPI PFNGLVERTEX4BVOESPROC glad_glVertex4bvOES;\n#define glVertex4bvOES glad_glVertex4bvOES\n#endif\n#ifndef GL_ARB_transpose_matrix\n#define GL_ARB_transpose_matrix 1\nGLAPI int GLAD_GL_ARB_transpose_matrix;\ntypedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC)(const GLfloat*);\nGLAPI PFNGLLOADTRANSPOSEMATRIXFARBPROC glad_glLoadTransposeMatrixfARB;\n#define glLoadTransposeMatrixfARB glad_glLoadTransposeMatrixfARB\ntypedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC)(const GLdouble*);\nGLAPI PFNGLLOADTRANSPOSEMATRIXDARBPROC glad_glLoadTransposeMatrixdARB;\n#define glLoadTransposeMatrixdARB glad_glLoadTransposeMatrixdARB\ntypedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC)(const GLfloat*);\nGLAPI PFNGLMULTTRANSPOSEMATRIXFARBPROC glad_glMultTransposeMatrixfARB;\n#define glMultTransposeMatrixfARB glad_glMultTransposeMatrixfARB\ntypedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC)(const GLdouble*);\nGLAPI PFNGLMULTTRANSPOSEMATRIXDARBPROC glad_glMultTransposeMatrixdARB;\n#define glMultTransposeMatrixdARB glad_glMultTransposeMatrixdARB\n#endif\n#ifndef GL_ARB_provoking_vertex\n#define GL_ARB_provoking_vertex 1\nGLAPI int GLAD_GL_ARB_provoking_vertex;\n#endif\n#ifndef GL_EXT_fog_coord\n#define GL_EXT_fog_coord 1\nGLAPI int GLAD_GL_EXT_fog_coord;\ntypedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC)(GLfloat);\nGLAPI PFNGLFOGCOORDFEXTPROC glad_glFogCoordfEXT;\n#define glFogCoordfEXT glad_glFogCoordfEXT\ntypedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC)(const GLfloat*);\nGLAPI PFNGLFOGCOORDFVEXTPROC glad_glFogCoordfvEXT;\n#define glFogCoordfvEXT glad_glFogCoordfvEXT\ntypedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC)(GLdouble);\nGLAPI PFNGLFOGCOORDDEXTPROC glad_glFogCoorddEXT;\n#define glFogCoorddEXT glad_glFogCoorddEXT\ntypedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC)(const GLdouble*);\nGLAPI PFNGLFOGCOORDDVEXTPROC glad_glFogCoorddvEXT;\n#define glFogCoorddvEXT glad_glFogCoorddvEXT\ntypedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC)(GLenum, GLsizei, const void*);\nGLAPI PFNGLFOGCOORDPOINTEREXTPROC glad_glFogCoordPointerEXT;\n#define glFogCoordPointerEXT glad_glFogCoordPointerEXT\n#endif\n#ifndef GL_EXT_vertex_array\n#define GL_EXT_vertex_array 1\nGLAPI int GLAD_GL_EXT_vertex_array;\ntypedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC)(GLint);\nGLAPI PFNGLARRAYELEMENTEXTPROC glad_glArrayElementEXT;\n#define glArrayElementEXT glad_glArrayElementEXT\ntypedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC)(GLint, GLenum, GLsizei, GLsizei, const void*);\nGLAPI PFNGLCOLORPOINTEREXTPROC glad_glColorPointerEXT;\n#define glColorPointerEXT glad_glColorPointerEXT\ntypedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC)(GLenum, GLint, GLsizei);\nGLAPI PFNGLDRAWARRAYSEXTPROC glad_glDrawArraysEXT;\n#define glDrawArraysEXT glad_glDrawArraysEXT\ntypedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC)(GLsizei, GLsizei, const GLboolean*);\nGLAPI PFNGLEDGEFLAGPOINTEREXTPROC glad_glEdgeFlagPointerEXT;\n#define glEdgeFlagPointerEXT glad_glEdgeFlagPointerEXT\ntypedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC)(GLenum, void**);\nGLAPI PFNGLGETPOINTERVEXTPROC glad_glGetPointervEXT;\n#define glGetPointervEXT glad_glGetPointervEXT\ntypedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC)(GLenum, GLsizei, GLsizei, const void*);\nGLAPI PFNGLINDEXPOINTEREXTPROC glad_glIndexPointerEXT;\n#define glIndexPointerEXT glad_glIndexPointerEXT\ntypedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC)(GLenum, GLsizei, GLsizei, const void*);\nGLAPI PFNGLNORMALPOINTEREXTPROC glad_glNormalPointerEXT;\n#define glNormalPointerEXT glad_glNormalPointerEXT\ntypedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC)(GLint, GLenum, GLsizei, GLsizei, const void*);\nGLAPI PFNGLTEXCOORDPOINTEREXTPROC glad_glTexCoordPointerEXT;\n#define glTexCoordPointerEXT glad_glTexCoordPointerEXT\ntypedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC)(GLint, GLenum, GLsizei, GLsizei, const void*);\nGLAPI PFNGLVERTEXPOINTEREXTPROC glad_glVertexPointerEXT;\n#define glVertexPointerEXT glad_glVertexPointerEXT\n#endif\n#ifndef GL_ARB_half_float_vertex\n#define GL_ARB_half_float_vertex 1\nGLAPI int GLAD_GL_ARB_half_float_vertex;\n#endif\n#ifndef GL_EXT_blend_equation_separate\n#define GL_EXT_blend_equation_separate 1\nGLAPI int GLAD_GL_EXT_blend_equation_separate;\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC)(GLenum, GLenum);\nGLAPI PFNGLBLENDEQUATIONSEPARATEEXTPROC glad_glBlendEquationSeparateEXT;\n#define glBlendEquationSeparateEXT glad_glBlendEquationSeparateEXT\n#endif\n#ifndef GL_ARB_multi_draw_indirect\n#define GL_ARB_multi_draw_indirect 1\nGLAPI int GLAD_GL_ARB_multi_draw_indirect;\n#endif\n#ifndef GL_NV_copy_image\n#define GL_NV_copy_image 1\nGLAPI int GLAD_GL_NV_copy_image;\ntypedef void (APIENTRYP PFNGLCOPYIMAGESUBDATANVPROC)(GLuint, GLenum, GLint, GLint, GLint, GLint, GLuint, GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei);\nGLAPI PFNGLCOPYIMAGESUBDATANVPROC glad_glCopyImageSubDataNV;\n#define glCopyImageSubDataNV glad_glCopyImageSubDataNV\n#endif\n#ifndef GL_ARB_fragment_layer_viewport\n#define GL_ARB_fragment_layer_viewport 1\nGLAPI int GLAD_GL_ARB_fragment_layer_viewport;\n#endif\n#ifndef GL_ARB_transform_feedback2\n#define GL_ARB_transform_feedback2 1\nGLAPI int GLAD_GL_ARB_transform_feedback2;\n#endif\n#ifndef GL_ARB_transform_feedback3\n#define GL_ARB_transform_feedback3 1\nGLAPI int GLAD_GL_ARB_transform_feedback3;\n#endif\n#ifndef GL_SGIX_ycrcba\n#define GL_SGIX_ycrcba 1\nGLAPI int GLAD_GL_SGIX_ycrcba;\n#endif\n#ifndef GL_EXT_debug_marker\n#define GL_EXT_debug_marker 1\nGLAPI int GLAD_GL_EXT_debug_marker;\ntypedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC)(GLsizei, const GLchar*);\nGLAPI PFNGLINSERTEVENTMARKEREXTPROC glad_glInsertEventMarkerEXT;\n#define glInsertEventMarkerEXT glad_glInsertEventMarkerEXT\ntypedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC)(GLsizei, const GLchar*);\nGLAPI PFNGLPUSHGROUPMARKEREXTPROC glad_glPushGroupMarkerEXT;\n#define glPushGroupMarkerEXT glad_glPushGroupMarkerEXT\ntypedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC)();\nGLAPI PFNGLPOPGROUPMARKEREXTPROC glad_glPopGroupMarkerEXT;\n#define glPopGroupMarkerEXT glad_glPopGroupMarkerEXT\n#endif\n#ifndef GL_EXT_bgra\n#define GL_EXT_bgra 1\nGLAPI int GLAD_GL_EXT_bgra;\n#endif\n#ifndef GL_EXT_texture_compression_s3tc\n#define GL_EXT_texture_compression_s3tc 1\nGLAPI int GLAD_GL_EXT_texture_compression_s3tc;\n#endif\n#ifndef GL_EXT_pixel_transform\n#define GL_EXT_pixel_transform 1\nGLAPI int GLAD_GL_EXT_pixel_transform;\ntypedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC)(GLenum, GLenum, GLint);\nGLAPI PFNGLPIXELTRANSFORMPARAMETERIEXTPROC glad_glPixelTransformParameteriEXT;\n#define glPixelTransformParameteriEXT glad_glPixelTransformParameteriEXT\ntypedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC)(GLenum, GLenum, GLfloat);\nGLAPI PFNGLPIXELTRANSFORMPARAMETERFEXTPROC glad_glPixelTransformParameterfEXT;\n#define glPixelTransformParameterfEXT glad_glPixelTransformParameterfEXT\ntypedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC glad_glPixelTransformParameterivEXT;\n#define glPixelTransformParameterivEXT glad_glPixelTransformParameterivEXT\ntypedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC glad_glPixelTransformParameterfvEXT;\n#define glPixelTransformParameterfvEXT glad_glPixelTransformParameterfvEXT\ntypedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC glad_glGetPixelTransformParameterivEXT;\n#define glGetPixelTransformParameterivEXT glad_glGetPixelTransformParameterivEXT\ntypedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC glad_glGetPixelTransformParameterfvEXT;\n#define glGetPixelTransformParameterfvEXT glad_glGetPixelTransformParameterfvEXT\n#endif\n#ifndef GL_ARB_conservative_depth\n#define GL_ARB_conservative_depth 1\nGLAPI int GLAD_GL_ARB_conservative_depth;\n#endif\n#ifndef GL_ATI_fragment_shader\n#define GL_ATI_fragment_shader 1\nGLAPI int GLAD_GL_ATI_fragment_shader;\ntypedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC)(GLuint);\nGLAPI PFNGLGENFRAGMENTSHADERSATIPROC glad_glGenFragmentShadersATI;\n#define glGenFragmentShadersATI glad_glGenFragmentShadersATI\ntypedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC)(GLuint);\nGLAPI PFNGLBINDFRAGMENTSHADERATIPROC glad_glBindFragmentShaderATI;\n#define glBindFragmentShaderATI glad_glBindFragmentShaderATI\ntypedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC)(GLuint);\nGLAPI PFNGLDELETEFRAGMENTSHADERATIPROC glad_glDeleteFragmentShaderATI;\n#define glDeleteFragmentShaderATI glad_glDeleteFragmentShaderATI\ntypedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC)();\nGLAPI PFNGLBEGINFRAGMENTSHADERATIPROC glad_glBeginFragmentShaderATI;\n#define glBeginFragmentShaderATI glad_glBeginFragmentShaderATI\ntypedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC)();\nGLAPI PFNGLENDFRAGMENTSHADERATIPROC glad_glEndFragmentShaderATI;\n#define glEndFragmentShaderATI glad_glEndFragmentShaderATI\ntypedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC)(GLuint, GLuint, GLenum);\nGLAPI PFNGLPASSTEXCOORDATIPROC glad_glPassTexCoordATI;\n#define glPassTexCoordATI glad_glPassTexCoordATI\ntypedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC)(GLuint, GLuint, GLenum);\nGLAPI PFNGLSAMPLEMAPATIPROC glad_glSampleMapATI;\n#define glSampleMapATI glad_glSampleMapATI\ntypedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC)(GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLCOLORFRAGMENTOP1ATIPROC glad_glColorFragmentOp1ATI;\n#define glColorFragmentOp1ATI glad_glColorFragmentOp1ATI\ntypedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC)(GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLCOLORFRAGMENTOP2ATIPROC glad_glColorFragmentOp2ATI;\n#define glColorFragmentOp2ATI glad_glColorFragmentOp2ATI\ntypedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC)(GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLCOLORFRAGMENTOP3ATIPROC glad_glColorFragmentOp3ATI;\n#define glColorFragmentOp3ATI glad_glColorFragmentOp3ATI\ntypedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC)(GLenum, GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLALPHAFRAGMENTOP1ATIPROC glad_glAlphaFragmentOp1ATI;\n#define glAlphaFragmentOp1ATI glad_glAlphaFragmentOp1ATI\ntypedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC)(GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLALPHAFRAGMENTOP2ATIPROC glad_glAlphaFragmentOp2ATI;\n#define glAlphaFragmentOp2ATI glad_glAlphaFragmentOp2ATI\ntypedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC)(GLenum, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLALPHAFRAGMENTOP3ATIPROC glad_glAlphaFragmentOp3ATI;\n#define glAlphaFragmentOp3ATI glad_glAlphaFragmentOp3ATI\ntypedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC)(GLuint, const GLfloat*);\nGLAPI PFNGLSETFRAGMENTSHADERCONSTANTATIPROC glad_glSetFragmentShaderConstantATI;\n#define glSetFragmentShaderConstantATI glad_glSetFragmentShaderConstantATI\n#endif\n#ifndef GL_ARB_vertex_array_object\n#define GL_ARB_vertex_array_object 1\nGLAPI int GLAD_GL_ARB_vertex_array_object;\n#endif\n#ifndef GL_SUN_triangle_list\n#define GL_SUN_triangle_list 1\nGLAPI int GLAD_GL_SUN_triangle_list;\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC)(GLuint);\nGLAPI PFNGLREPLACEMENTCODEUISUNPROC glad_glReplacementCodeuiSUN;\n#define glReplacementCodeuiSUN glad_glReplacementCodeuiSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC)(GLushort);\nGLAPI PFNGLREPLACEMENTCODEUSSUNPROC glad_glReplacementCodeusSUN;\n#define glReplacementCodeusSUN glad_glReplacementCodeusSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC)(GLubyte);\nGLAPI PFNGLREPLACEMENTCODEUBSUNPROC glad_glReplacementCodeubSUN;\n#define glReplacementCodeubSUN glad_glReplacementCodeubSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC)(const GLuint*);\nGLAPI PFNGLREPLACEMENTCODEUIVSUNPROC glad_glReplacementCodeuivSUN;\n#define glReplacementCodeuivSUN glad_glReplacementCodeuivSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC)(const GLushort*);\nGLAPI PFNGLREPLACEMENTCODEUSVSUNPROC glad_glReplacementCodeusvSUN;\n#define glReplacementCodeusvSUN glad_glReplacementCodeusvSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC)(const GLubyte*);\nGLAPI PFNGLREPLACEMENTCODEUBVSUNPROC glad_glReplacementCodeubvSUN;\n#define glReplacementCodeubvSUN glad_glReplacementCodeubvSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC)(GLenum, GLsizei, const void**);\nGLAPI PFNGLREPLACEMENTCODEPOINTERSUNPROC glad_glReplacementCodePointerSUN;\n#define glReplacementCodePointerSUN glad_glReplacementCodePointerSUN\n#endif\n#ifndef GL_EXT_texture_env_add\n#define GL_EXT_texture_env_add 1\nGLAPI int GLAD_GL_EXT_texture_env_add;\n#endif\n#ifndef GL_EXT_packed_depth_stencil\n#define GL_EXT_packed_depth_stencil 1\nGLAPI int GLAD_GL_EXT_packed_depth_stencil;\n#endif\n#ifndef GL_EXT_texture_mirror_clamp\n#define GL_EXT_texture_mirror_clamp 1\nGLAPI int GLAD_GL_EXT_texture_mirror_clamp;\n#endif\n#ifndef GL_NV_multisample_filter_hint\n#define GL_NV_multisample_filter_hint 1\nGLAPI int GLAD_GL_NV_multisample_filter_hint;\n#endif\n#ifndef GL_APPLE_float_pixels\n#define GL_APPLE_float_pixels 1\nGLAPI int GLAD_GL_APPLE_float_pixels;\n#endif\n#ifndef GL_ARB_transform_feedback_instanced\n#define GL_ARB_transform_feedback_instanced 1\nGLAPI int GLAD_GL_ARB_transform_feedback_instanced;\n#endif\n#ifndef GL_SGIX_async\n#define GL_SGIX_async 1\nGLAPI int GLAD_GL_SGIX_async;\ntypedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC)(GLuint);\nGLAPI PFNGLASYNCMARKERSGIXPROC glad_glAsyncMarkerSGIX;\n#define glAsyncMarkerSGIX glad_glAsyncMarkerSGIX\ntypedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC)(GLuint*);\nGLAPI PFNGLFINISHASYNCSGIXPROC glad_glFinishAsyncSGIX;\n#define glFinishAsyncSGIX glad_glFinishAsyncSGIX\ntypedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC)(GLuint*);\nGLAPI PFNGLPOLLASYNCSGIXPROC glad_glPollAsyncSGIX;\n#define glPollAsyncSGIX glad_glPollAsyncSGIX\ntypedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC)(GLsizei);\nGLAPI PFNGLGENASYNCMARKERSSGIXPROC glad_glGenAsyncMarkersSGIX;\n#define glGenAsyncMarkersSGIX glad_glGenAsyncMarkersSGIX\ntypedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC)(GLuint, GLsizei);\nGLAPI PFNGLDELETEASYNCMARKERSSGIXPROC glad_glDeleteAsyncMarkersSGIX;\n#define glDeleteAsyncMarkersSGIX glad_glDeleteAsyncMarkersSGIX\ntypedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC)(GLuint);\nGLAPI PFNGLISASYNCMARKERSGIXPROC glad_glIsAsyncMarkerSGIX;\n#define glIsAsyncMarkerSGIX glad_glIsAsyncMarkerSGIX\n#endif\n#ifndef GL_EXT_texture_compression_latc\n#define GL_EXT_texture_compression_latc 1\nGLAPI int GLAD_GL_EXT_texture_compression_latc;\n#endif\n#ifndef GL_NV_shader_atomic_float\n#define GL_NV_shader_atomic_float 1\nGLAPI int GLAD_GL_NV_shader_atomic_float;\n#endif\n#ifndef GL_ARB_shading_language_100\n#define GL_ARB_shading_language_100 1\nGLAPI int GLAD_GL_ARB_shading_language_100;\n#endif\n#ifndef GL_INTEL_performance_query\n#define GL_INTEL_performance_query 1\nGLAPI int GLAD_GL_INTEL_performance_query;\ntypedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC)(GLuint);\nGLAPI PFNGLBEGINPERFQUERYINTELPROC glad_glBeginPerfQueryINTEL;\n#define glBeginPerfQueryINTEL glad_glBeginPerfQueryINTEL\ntypedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC)(GLuint, GLuint*);\nGLAPI PFNGLCREATEPERFQUERYINTELPROC glad_glCreatePerfQueryINTEL;\n#define glCreatePerfQueryINTEL glad_glCreatePerfQueryINTEL\ntypedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC)(GLuint);\nGLAPI PFNGLDELETEPERFQUERYINTELPROC glad_glDeletePerfQueryINTEL;\n#define glDeletePerfQueryINTEL glad_glDeletePerfQueryINTEL\ntypedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC)(GLuint);\nGLAPI PFNGLENDPERFQUERYINTELPROC glad_glEndPerfQueryINTEL;\n#define glEndPerfQueryINTEL glad_glEndPerfQueryINTEL\ntypedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC)(GLuint*);\nGLAPI PFNGLGETFIRSTPERFQUERYIDINTELPROC glad_glGetFirstPerfQueryIdINTEL;\n#define glGetFirstPerfQueryIdINTEL glad_glGetFirstPerfQueryIdINTEL\ntypedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC)(GLuint, GLuint*);\nGLAPI PFNGLGETNEXTPERFQUERYIDINTELPROC glad_glGetNextPerfQueryIdINTEL;\n#define glGetNextPerfQueryIdINTEL glad_glGetNextPerfQueryIdINTEL\ntypedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC)(GLuint, GLuint, GLuint, GLchar*, GLuint, GLchar*, GLuint*, GLuint*, GLuint*, GLuint*, GLuint64*);\nGLAPI PFNGLGETPERFCOUNTERINFOINTELPROC glad_glGetPerfCounterInfoINTEL;\n#define glGetPerfCounterInfoINTEL glad_glGetPerfCounterInfoINTEL\ntypedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC)(GLuint, GLuint, GLsizei, GLvoid*, GLuint*);\nGLAPI PFNGLGETPERFQUERYDATAINTELPROC glad_glGetPerfQueryDataINTEL;\n#define glGetPerfQueryDataINTEL glad_glGetPerfQueryDataINTEL\ntypedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC)(GLchar*, GLuint*);\nGLAPI PFNGLGETPERFQUERYIDBYNAMEINTELPROC glad_glGetPerfQueryIdByNameINTEL;\n#define glGetPerfQueryIdByNameINTEL glad_glGetPerfQueryIdByNameINTEL\ntypedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC)(GLuint, GLuint, GLchar*, GLuint*, GLuint*, GLuint*, GLuint*);\nGLAPI PFNGLGETPERFQUERYINFOINTELPROC glad_glGetPerfQueryInfoINTEL;\n#define glGetPerfQueryInfoINTEL glad_glGetPerfQueryInfoINTEL\n#endif\n#ifndef GL_ARB_texture_mirror_clamp_to_edge\n#define GL_ARB_texture_mirror_clamp_to_edge 1\nGLAPI int GLAD_GL_ARB_texture_mirror_clamp_to_edge;\n#endif\n#ifndef GL_NV_gpu_shader5\n#define GL_NV_gpu_shader5 1\nGLAPI int GLAD_GL_NV_gpu_shader5;\ntypedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC)(GLint, GLint64EXT);\nGLAPI PFNGLUNIFORM1I64NVPROC glad_glUniform1i64NV;\n#define glUniform1i64NV glad_glUniform1i64NV\ntypedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC)(GLint, GLint64EXT, GLint64EXT);\nGLAPI PFNGLUNIFORM2I64NVPROC glad_glUniform2i64NV;\n#define glUniform2i64NV glad_glUniform2i64NV\ntypedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC)(GLint, GLint64EXT, GLint64EXT, GLint64EXT);\nGLAPI PFNGLUNIFORM3I64NVPROC glad_glUniform3i64NV;\n#define glUniform3i64NV glad_glUniform3i64NV\ntypedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC)(GLint, GLint64EXT, GLint64EXT, GLint64EXT, GLint64EXT);\nGLAPI PFNGLUNIFORM4I64NVPROC glad_glUniform4i64NV;\n#define glUniform4i64NV glad_glUniform4i64NV\ntypedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC)(GLint, GLsizei, const GLint64EXT*);\nGLAPI PFNGLUNIFORM1I64VNVPROC glad_glUniform1i64vNV;\n#define glUniform1i64vNV glad_glUniform1i64vNV\ntypedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC)(GLint, GLsizei, const GLint64EXT*);\nGLAPI PFNGLUNIFORM2I64VNVPROC glad_glUniform2i64vNV;\n#define glUniform2i64vNV glad_glUniform2i64vNV\ntypedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC)(GLint, GLsizei, const GLint64EXT*);\nGLAPI PFNGLUNIFORM3I64VNVPROC glad_glUniform3i64vNV;\n#define glUniform3i64vNV glad_glUniform3i64vNV\ntypedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC)(GLint, GLsizei, const GLint64EXT*);\nGLAPI PFNGLUNIFORM4I64VNVPROC glad_glUniform4i64vNV;\n#define glUniform4i64vNV glad_glUniform4i64vNV\ntypedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC)(GLint, GLuint64EXT);\nGLAPI PFNGLUNIFORM1UI64NVPROC glad_glUniform1ui64NV;\n#define glUniform1ui64NV glad_glUniform1ui64NV\ntypedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC)(GLint, GLuint64EXT, GLuint64EXT);\nGLAPI PFNGLUNIFORM2UI64NVPROC glad_glUniform2ui64NV;\n#define glUniform2ui64NV glad_glUniform2ui64NV\ntypedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC)(GLint, GLuint64EXT, GLuint64EXT, GLuint64EXT);\nGLAPI PFNGLUNIFORM3UI64NVPROC glad_glUniform3ui64NV;\n#define glUniform3ui64NV glad_glUniform3ui64NV\ntypedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC)(GLint, GLuint64EXT, GLuint64EXT, GLuint64EXT, GLuint64EXT);\nGLAPI PFNGLUNIFORM4UI64NVPROC glad_glUniform4ui64NV;\n#define glUniform4ui64NV glad_glUniform4ui64NV\ntypedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC)(GLint, GLsizei, const GLuint64EXT*);\nGLAPI PFNGLUNIFORM1UI64VNVPROC glad_glUniform1ui64vNV;\n#define glUniform1ui64vNV glad_glUniform1ui64vNV\ntypedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC)(GLint, GLsizei, const GLuint64EXT*);\nGLAPI PFNGLUNIFORM2UI64VNVPROC glad_glUniform2ui64vNV;\n#define glUniform2ui64vNV glad_glUniform2ui64vNV\ntypedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC)(GLint, GLsizei, const GLuint64EXT*);\nGLAPI PFNGLUNIFORM3UI64VNVPROC glad_glUniform3ui64vNV;\n#define glUniform3ui64vNV glad_glUniform3ui64vNV\ntypedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC)(GLint, GLsizei, const GLuint64EXT*);\nGLAPI PFNGLUNIFORM4UI64VNVPROC glad_glUniform4ui64vNV;\n#define glUniform4ui64vNV glad_glUniform4ui64vNV\ntypedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC)(GLuint, GLint, GLint64EXT*);\nGLAPI PFNGLGETUNIFORMI64VNVPROC glad_glGetUniformi64vNV;\n#define glGetUniformi64vNV glad_glGetUniformi64vNV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC)(GLuint, GLint, GLint64EXT);\nGLAPI PFNGLPROGRAMUNIFORM1I64NVPROC glad_glProgramUniform1i64NV;\n#define glProgramUniform1i64NV glad_glProgramUniform1i64NV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC)(GLuint, GLint, GLint64EXT, GLint64EXT);\nGLAPI PFNGLPROGRAMUNIFORM2I64NVPROC glad_glProgramUniform2i64NV;\n#define glProgramUniform2i64NV glad_glProgramUniform2i64NV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC)(GLuint, GLint, GLint64EXT, GLint64EXT, GLint64EXT);\nGLAPI PFNGLPROGRAMUNIFORM3I64NVPROC glad_glProgramUniform3i64NV;\n#define glProgramUniform3i64NV glad_glProgramUniform3i64NV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC)(GLuint, GLint, GLint64EXT, GLint64EXT, GLint64EXT, GLint64EXT);\nGLAPI PFNGLPROGRAMUNIFORM4I64NVPROC glad_glProgramUniform4i64NV;\n#define glProgramUniform4i64NV glad_glProgramUniform4i64NV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC)(GLuint, GLint, GLsizei, const GLint64EXT*);\nGLAPI PFNGLPROGRAMUNIFORM1I64VNVPROC glad_glProgramUniform1i64vNV;\n#define glProgramUniform1i64vNV glad_glProgramUniform1i64vNV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC)(GLuint, GLint, GLsizei, const GLint64EXT*);\nGLAPI PFNGLPROGRAMUNIFORM2I64VNVPROC glad_glProgramUniform2i64vNV;\n#define glProgramUniform2i64vNV glad_glProgramUniform2i64vNV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC)(GLuint, GLint, GLsizei, const GLint64EXT*);\nGLAPI PFNGLPROGRAMUNIFORM3I64VNVPROC glad_glProgramUniform3i64vNV;\n#define glProgramUniform3i64vNV glad_glProgramUniform3i64vNV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC)(GLuint, GLint, GLsizei, const GLint64EXT*);\nGLAPI PFNGLPROGRAMUNIFORM4I64VNVPROC glad_glProgramUniform4i64vNV;\n#define glProgramUniform4i64vNV glad_glProgramUniform4i64vNV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC)(GLuint, GLint, GLuint64EXT);\nGLAPI PFNGLPROGRAMUNIFORM1UI64NVPROC glad_glProgramUniform1ui64NV;\n#define glProgramUniform1ui64NV glad_glProgramUniform1ui64NV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC)(GLuint, GLint, GLuint64EXT, GLuint64EXT);\nGLAPI PFNGLPROGRAMUNIFORM2UI64NVPROC glad_glProgramUniform2ui64NV;\n#define glProgramUniform2ui64NV glad_glProgramUniform2ui64NV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC)(GLuint, GLint, GLuint64EXT, GLuint64EXT, GLuint64EXT);\nGLAPI PFNGLPROGRAMUNIFORM3UI64NVPROC glad_glProgramUniform3ui64NV;\n#define glProgramUniform3ui64NV glad_glProgramUniform3ui64NV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC)(GLuint, GLint, GLuint64EXT, GLuint64EXT, GLuint64EXT, GLuint64EXT);\nGLAPI PFNGLPROGRAMUNIFORM4UI64NVPROC glad_glProgramUniform4ui64NV;\n#define glProgramUniform4ui64NV glad_glProgramUniform4ui64NV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC)(GLuint, GLint, GLsizei, const GLuint64EXT*);\nGLAPI PFNGLPROGRAMUNIFORM1UI64VNVPROC glad_glProgramUniform1ui64vNV;\n#define glProgramUniform1ui64vNV glad_glProgramUniform1ui64vNV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC)(GLuint, GLint, GLsizei, const GLuint64EXT*);\nGLAPI PFNGLPROGRAMUNIFORM2UI64VNVPROC glad_glProgramUniform2ui64vNV;\n#define glProgramUniform2ui64vNV glad_glProgramUniform2ui64vNV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC)(GLuint, GLint, GLsizei, const GLuint64EXT*);\nGLAPI PFNGLPROGRAMUNIFORM3UI64VNVPROC glad_glProgramUniform3ui64vNV;\n#define glProgramUniform3ui64vNV glad_glProgramUniform3ui64vNV\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC)(GLuint, GLint, GLsizei, const GLuint64EXT*);\nGLAPI PFNGLPROGRAMUNIFORM4UI64VNVPROC glad_glProgramUniform4ui64vNV;\n#define glProgramUniform4ui64vNV glad_glProgramUniform4ui64vNV\n#endif\n#ifndef GL_ARB_ES2_compatibility\n#define GL_ARB_ES2_compatibility 1\nGLAPI int GLAD_GL_ARB_ES2_compatibility;\n#endif\n#ifndef GL_ARB_indirect_parameters\n#define GL_ARB_indirect_parameters 1\nGLAPI int GLAD_GL_ARB_indirect_parameters;\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC)(GLenum, GLintptr, GLintptr, GLsizei, GLsizei);\nGLAPI PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC glad_glMultiDrawArraysIndirectCountARB;\n#define glMultiDrawArraysIndirectCountARB glad_glMultiDrawArraysIndirectCountARB\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC)(GLenum, GLenum, GLintptr, GLintptr, GLsizei, GLsizei);\nGLAPI PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC glad_glMultiDrawElementsIndirectCountARB;\n#define glMultiDrawElementsIndirectCountARB glad_glMultiDrawElementsIndirectCountARB\n#endif\n#ifndef GL_NV_half_float\n#define GL_NV_half_float 1\nGLAPI int GLAD_GL_NV_half_float;\ntypedef void (APIENTRYP PFNGLVERTEX2HNVPROC)(GLhalfNV, GLhalfNV);\nGLAPI PFNGLVERTEX2HNVPROC glad_glVertex2hNV;\n#define glVertex2hNV glad_glVertex2hNV\ntypedef void (APIENTRYP PFNGLVERTEX2HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLVERTEX2HVNVPROC glad_glVertex2hvNV;\n#define glVertex2hvNV glad_glVertex2hvNV\ntypedef void (APIENTRYP PFNGLVERTEX3HNVPROC)(GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLVERTEX3HNVPROC glad_glVertex3hNV;\n#define glVertex3hNV glad_glVertex3hNV\ntypedef void (APIENTRYP PFNGLVERTEX3HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLVERTEX3HVNVPROC glad_glVertex3hvNV;\n#define glVertex3hvNV glad_glVertex3hvNV\ntypedef void (APIENTRYP PFNGLVERTEX4HNVPROC)(GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLVERTEX4HNVPROC glad_glVertex4hNV;\n#define glVertex4hNV glad_glVertex4hNV\ntypedef void (APIENTRYP PFNGLVERTEX4HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLVERTEX4HVNVPROC glad_glVertex4hvNV;\n#define glVertex4hvNV glad_glVertex4hvNV\ntypedef void (APIENTRYP PFNGLNORMAL3HNVPROC)(GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLNORMAL3HNVPROC glad_glNormal3hNV;\n#define glNormal3hNV glad_glNormal3hNV\ntypedef void (APIENTRYP PFNGLNORMAL3HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLNORMAL3HVNVPROC glad_glNormal3hvNV;\n#define glNormal3hvNV glad_glNormal3hvNV\ntypedef void (APIENTRYP PFNGLCOLOR3HNVPROC)(GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLCOLOR3HNVPROC glad_glColor3hNV;\n#define glColor3hNV glad_glColor3hNV\ntypedef void (APIENTRYP PFNGLCOLOR3HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLCOLOR3HVNVPROC glad_glColor3hvNV;\n#define glColor3hvNV glad_glColor3hvNV\ntypedef void (APIENTRYP PFNGLCOLOR4HNVPROC)(GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLCOLOR4HNVPROC glad_glColor4hNV;\n#define glColor4hNV glad_glColor4hNV\ntypedef void (APIENTRYP PFNGLCOLOR4HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLCOLOR4HVNVPROC glad_glColor4hvNV;\n#define glColor4hvNV glad_glColor4hvNV\ntypedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC)(GLhalfNV);\nGLAPI PFNGLTEXCOORD1HNVPROC glad_glTexCoord1hNV;\n#define glTexCoord1hNV glad_glTexCoord1hNV\ntypedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLTEXCOORD1HVNVPROC glad_glTexCoord1hvNV;\n#define glTexCoord1hvNV glad_glTexCoord1hvNV\ntypedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC)(GLhalfNV, GLhalfNV);\nGLAPI PFNGLTEXCOORD2HNVPROC glad_glTexCoord2hNV;\n#define glTexCoord2hNV glad_glTexCoord2hNV\ntypedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLTEXCOORD2HVNVPROC glad_glTexCoord2hvNV;\n#define glTexCoord2hvNV glad_glTexCoord2hvNV\ntypedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC)(GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLTEXCOORD3HNVPROC glad_glTexCoord3hNV;\n#define glTexCoord3hNV glad_glTexCoord3hNV\ntypedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLTEXCOORD3HVNVPROC glad_glTexCoord3hvNV;\n#define glTexCoord3hvNV glad_glTexCoord3hvNV\ntypedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC)(GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLTEXCOORD4HNVPROC glad_glTexCoord4hNV;\n#define glTexCoord4hNV glad_glTexCoord4hNV\ntypedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLTEXCOORD4HVNVPROC glad_glTexCoord4hvNV;\n#define glTexCoord4hvNV glad_glTexCoord4hvNV\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC)(GLenum, GLhalfNV);\nGLAPI PFNGLMULTITEXCOORD1HNVPROC glad_glMultiTexCoord1hNV;\n#define glMultiTexCoord1hNV glad_glMultiTexCoord1hNV\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC)(GLenum, const GLhalfNV*);\nGLAPI PFNGLMULTITEXCOORD1HVNVPROC glad_glMultiTexCoord1hvNV;\n#define glMultiTexCoord1hvNV glad_glMultiTexCoord1hvNV\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC)(GLenum, GLhalfNV, GLhalfNV);\nGLAPI PFNGLMULTITEXCOORD2HNVPROC glad_glMultiTexCoord2hNV;\n#define glMultiTexCoord2hNV glad_glMultiTexCoord2hNV\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC)(GLenum, const GLhalfNV*);\nGLAPI PFNGLMULTITEXCOORD2HVNVPROC glad_glMultiTexCoord2hvNV;\n#define glMultiTexCoord2hvNV glad_glMultiTexCoord2hvNV\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC)(GLenum, GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLMULTITEXCOORD3HNVPROC glad_glMultiTexCoord3hNV;\n#define glMultiTexCoord3hNV glad_glMultiTexCoord3hNV\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC)(GLenum, const GLhalfNV*);\nGLAPI PFNGLMULTITEXCOORD3HVNVPROC glad_glMultiTexCoord3hvNV;\n#define glMultiTexCoord3hvNV glad_glMultiTexCoord3hvNV\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC)(GLenum, GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLMULTITEXCOORD4HNVPROC glad_glMultiTexCoord4hNV;\n#define glMultiTexCoord4hNV glad_glMultiTexCoord4hNV\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC)(GLenum, const GLhalfNV*);\nGLAPI PFNGLMULTITEXCOORD4HVNVPROC glad_glMultiTexCoord4hvNV;\n#define glMultiTexCoord4hvNV glad_glMultiTexCoord4hvNV\ntypedef void (APIENTRYP PFNGLFOGCOORDHNVPROC)(GLhalfNV);\nGLAPI PFNGLFOGCOORDHNVPROC glad_glFogCoordhNV;\n#define glFogCoordhNV glad_glFogCoordhNV\ntypedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLFOGCOORDHVNVPROC glad_glFogCoordhvNV;\n#define glFogCoordhvNV glad_glFogCoordhvNV\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC)(GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLSECONDARYCOLOR3HNVPROC glad_glSecondaryColor3hNV;\n#define glSecondaryColor3hNV glad_glSecondaryColor3hNV\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLSECONDARYCOLOR3HVNVPROC glad_glSecondaryColor3hvNV;\n#define glSecondaryColor3hvNV glad_glSecondaryColor3hvNV\ntypedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC)(GLhalfNV);\nGLAPI PFNGLVERTEXWEIGHTHNVPROC glad_glVertexWeighthNV;\n#define glVertexWeighthNV glad_glVertexWeighthNV\ntypedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC)(const GLhalfNV*);\nGLAPI PFNGLVERTEXWEIGHTHVNVPROC glad_glVertexWeighthvNV;\n#define glVertexWeighthvNV glad_glVertexWeighthvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC)(GLuint, GLhalfNV);\nGLAPI PFNGLVERTEXATTRIB1HNVPROC glad_glVertexAttrib1hNV;\n#define glVertexAttrib1hNV glad_glVertexAttrib1hNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC)(GLuint, const GLhalfNV*);\nGLAPI PFNGLVERTEXATTRIB1HVNVPROC glad_glVertexAttrib1hvNV;\n#define glVertexAttrib1hvNV glad_glVertexAttrib1hvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC)(GLuint, GLhalfNV, GLhalfNV);\nGLAPI PFNGLVERTEXATTRIB2HNVPROC glad_glVertexAttrib2hNV;\n#define glVertexAttrib2hNV glad_glVertexAttrib2hNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC)(GLuint, const GLhalfNV*);\nGLAPI PFNGLVERTEXATTRIB2HVNVPROC glad_glVertexAttrib2hvNV;\n#define glVertexAttrib2hvNV glad_glVertexAttrib2hvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC)(GLuint, GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLVERTEXATTRIB3HNVPROC glad_glVertexAttrib3hNV;\n#define glVertexAttrib3hNV glad_glVertexAttrib3hNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC)(GLuint, const GLhalfNV*);\nGLAPI PFNGLVERTEXATTRIB3HVNVPROC glad_glVertexAttrib3hvNV;\n#define glVertexAttrib3hvNV glad_glVertexAttrib3hvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC)(GLuint, GLhalfNV, GLhalfNV, GLhalfNV, GLhalfNV);\nGLAPI PFNGLVERTEXATTRIB4HNVPROC glad_glVertexAttrib4hNV;\n#define glVertexAttrib4hNV glad_glVertexAttrib4hNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC)(GLuint, const GLhalfNV*);\nGLAPI PFNGLVERTEXATTRIB4HVNVPROC glad_glVertexAttrib4hvNV;\n#define glVertexAttrib4hvNV glad_glVertexAttrib4hvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC)(GLuint, GLsizei, const GLhalfNV*);\nGLAPI PFNGLVERTEXATTRIBS1HVNVPROC glad_glVertexAttribs1hvNV;\n#define glVertexAttribs1hvNV glad_glVertexAttribs1hvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC)(GLuint, GLsizei, const GLhalfNV*);\nGLAPI PFNGLVERTEXATTRIBS2HVNVPROC glad_glVertexAttribs2hvNV;\n#define glVertexAttribs2hvNV glad_glVertexAttribs2hvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC)(GLuint, GLsizei, const GLhalfNV*);\nGLAPI PFNGLVERTEXATTRIBS3HVNVPROC glad_glVertexAttribs3hvNV;\n#define glVertexAttribs3hvNV glad_glVertexAttribs3hvNV\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC)(GLuint, GLsizei, const GLhalfNV*);\nGLAPI PFNGLVERTEXATTRIBS4HVNVPROC glad_glVertexAttribs4hvNV;\n#define glVertexAttribs4hvNV glad_glVertexAttribs4hvNV\n#endif\n#ifndef GL_EXT_coordinate_frame\n#define GL_EXT_coordinate_frame 1\nGLAPI int GLAD_GL_EXT_coordinate_frame;\ntypedef void (APIENTRYP PFNGLTANGENT3BEXTPROC)(GLbyte, GLbyte, GLbyte);\nGLAPI PFNGLTANGENT3BEXTPROC glad_glTangent3bEXT;\n#define glTangent3bEXT glad_glTangent3bEXT\ntypedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC)(const GLbyte*);\nGLAPI PFNGLTANGENT3BVEXTPROC glad_glTangent3bvEXT;\n#define glTangent3bvEXT glad_glTangent3bvEXT\ntypedef void (APIENTRYP PFNGLTANGENT3DEXTPROC)(GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLTANGENT3DEXTPROC glad_glTangent3dEXT;\n#define glTangent3dEXT glad_glTangent3dEXT\ntypedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC)(const GLdouble*);\nGLAPI PFNGLTANGENT3DVEXTPROC glad_glTangent3dvEXT;\n#define glTangent3dvEXT glad_glTangent3dvEXT\ntypedef void (APIENTRYP PFNGLTANGENT3FEXTPROC)(GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLTANGENT3FEXTPROC glad_glTangent3fEXT;\n#define glTangent3fEXT glad_glTangent3fEXT\ntypedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC)(const GLfloat*);\nGLAPI PFNGLTANGENT3FVEXTPROC glad_glTangent3fvEXT;\n#define glTangent3fvEXT glad_glTangent3fvEXT\ntypedef void (APIENTRYP PFNGLTANGENT3IEXTPROC)(GLint, GLint, GLint);\nGLAPI PFNGLTANGENT3IEXTPROC glad_glTangent3iEXT;\n#define glTangent3iEXT glad_glTangent3iEXT\ntypedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC)(const GLint*);\nGLAPI PFNGLTANGENT3IVEXTPROC glad_glTangent3ivEXT;\n#define glTangent3ivEXT glad_glTangent3ivEXT\ntypedef void (APIENTRYP PFNGLTANGENT3SEXTPROC)(GLshort, GLshort, GLshort);\nGLAPI PFNGLTANGENT3SEXTPROC glad_glTangent3sEXT;\n#define glTangent3sEXT glad_glTangent3sEXT\ntypedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC)(const GLshort*);\nGLAPI PFNGLTANGENT3SVEXTPROC glad_glTangent3svEXT;\n#define glTangent3svEXT glad_glTangent3svEXT\ntypedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC)(GLbyte, GLbyte, GLbyte);\nGLAPI PFNGLBINORMAL3BEXTPROC glad_glBinormal3bEXT;\n#define glBinormal3bEXT glad_glBinormal3bEXT\ntypedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC)(const GLbyte*);\nGLAPI PFNGLBINORMAL3BVEXTPROC glad_glBinormal3bvEXT;\n#define glBinormal3bvEXT glad_glBinormal3bvEXT\ntypedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC)(GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLBINORMAL3DEXTPROC glad_glBinormal3dEXT;\n#define glBinormal3dEXT glad_glBinormal3dEXT\ntypedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC)(const GLdouble*);\nGLAPI PFNGLBINORMAL3DVEXTPROC glad_glBinormal3dvEXT;\n#define glBinormal3dvEXT glad_glBinormal3dvEXT\ntypedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC)(GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLBINORMAL3FEXTPROC glad_glBinormal3fEXT;\n#define glBinormal3fEXT glad_glBinormal3fEXT\ntypedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC)(const GLfloat*);\nGLAPI PFNGLBINORMAL3FVEXTPROC glad_glBinormal3fvEXT;\n#define glBinormal3fvEXT glad_glBinormal3fvEXT\ntypedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC)(GLint, GLint, GLint);\nGLAPI PFNGLBINORMAL3IEXTPROC glad_glBinormal3iEXT;\n#define glBinormal3iEXT glad_glBinormal3iEXT\ntypedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC)(const GLint*);\nGLAPI PFNGLBINORMAL3IVEXTPROC glad_glBinormal3ivEXT;\n#define glBinormal3ivEXT glad_glBinormal3ivEXT\ntypedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC)(GLshort, GLshort, GLshort);\nGLAPI PFNGLBINORMAL3SEXTPROC glad_glBinormal3sEXT;\n#define glBinormal3sEXT glad_glBinormal3sEXT\ntypedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC)(const GLshort*);\nGLAPI PFNGLBINORMAL3SVEXTPROC glad_glBinormal3svEXT;\n#define glBinormal3svEXT glad_glBinormal3svEXT\ntypedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC)(GLenum, GLsizei, const void*);\nGLAPI PFNGLTANGENTPOINTEREXTPROC glad_glTangentPointerEXT;\n#define glTangentPointerEXT glad_glTangentPointerEXT\ntypedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC)(GLenum, GLsizei, const void*);\nGLAPI PFNGLBINORMALPOINTEREXTPROC glad_glBinormalPointerEXT;\n#define glBinormalPointerEXT glad_glBinormalPointerEXT\n#endif\n#ifndef GL_ATI_texture_mirror_once\n#define GL_ATI_texture_mirror_once 1\nGLAPI int GLAD_GL_ATI_texture_mirror_once;\n#endif\n#ifndef GL_IBM_rasterpos_clip\n#define GL_IBM_rasterpos_clip 1\nGLAPI int GLAD_GL_IBM_rasterpos_clip;\n#endif\n#ifndef GL_SGIX_shadow\n#define GL_SGIX_shadow 1\nGLAPI int GLAD_GL_SGIX_shadow;\n#endif\n#ifndef GL_NV_deep_texture3D\n#define GL_NV_deep_texture3D 1\nGLAPI int GLAD_GL_NV_deep_texture3D;\n#endif\n#ifndef GL_ARB_shader_draw_parameters\n#define GL_ARB_shader_draw_parameters 1\nGLAPI int GLAD_GL_ARB_shader_draw_parameters;\n#endif\n#ifndef GL_SGIX_calligraphic_fragment\n#define GL_SGIX_calligraphic_fragment 1\nGLAPI int GLAD_GL_SGIX_calligraphic_fragment;\n#endif\n#ifndef GL_ARB_shader_bit_encoding\n#define GL_ARB_shader_bit_encoding 1\nGLAPI int GLAD_GL_ARB_shader_bit_encoding;\n#endif\n#ifndef GL_EXT_compiled_vertex_array\n#define GL_EXT_compiled_vertex_array 1\nGLAPI int GLAD_GL_EXT_compiled_vertex_array;\ntypedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC)(GLint, GLsizei);\nGLAPI PFNGLLOCKARRAYSEXTPROC glad_glLockArraysEXT;\n#define glLockArraysEXT glad_glLockArraysEXT\ntypedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC)();\nGLAPI PFNGLUNLOCKARRAYSEXTPROC glad_glUnlockArraysEXT;\n#define glUnlockArraysEXT glad_glUnlockArraysEXT\n#endif\n#ifndef GL_NV_depth_buffer_float\n#define GL_NV_depth_buffer_float 1\nGLAPI int GLAD_GL_NV_depth_buffer_float;\ntypedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC)(GLdouble, GLdouble);\nGLAPI PFNGLDEPTHRANGEDNVPROC glad_glDepthRangedNV;\n#define glDepthRangedNV glad_glDepthRangedNV\ntypedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC)(GLdouble);\nGLAPI PFNGLCLEARDEPTHDNVPROC glad_glClearDepthdNV;\n#define glClearDepthdNV glad_glClearDepthdNV\ntypedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC)(GLdouble, GLdouble);\nGLAPI PFNGLDEPTHBOUNDSDNVPROC glad_glDepthBoundsdNV;\n#define glDepthBoundsdNV glad_glDepthBoundsdNV\n#endif\n#ifndef GL_NV_occlusion_query\n#define GL_NV_occlusion_query 1\nGLAPI int GLAD_GL_NV_occlusion_query;\ntypedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENOCCLUSIONQUERIESNVPROC glad_glGenOcclusionQueriesNV;\n#define glGenOcclusionQueriesNV glad_glGenOcclusionQueriesNV\ntypedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEOCCLUSIONQUERIESNVPROC glad_glDeleteOcclusionQueriesNV;\n#define glDeleteOcclusionQueriesNV glad_glDeleteOcclusionQueriesNV\ntypedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC)(GLuint);\nGLAPI PFNGLISOCCLUSIONQUERYNVPROC glad_glIsOcclusionQueryNV;\n#define glIsOcclusionQueryNV glad_glIsOcclusionQueryNV\ntypedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC)(GLuint);\nGLAPI PFNGLBEGINOCCLUSIONQUERYNVPROC glad_glBeginOcclusionQueryNV;\n#define glBeginOcclusionQueryNV glad_glBeginOcclusionQueryNV\ntypedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC)();\nGLAPI PFNGLENDOCCLUSIONQUERYNVPROC glad_glEndOcclusionQueryNV;\n#define glEndOcclusionQueryNV glad_glEndOcclusionQueryNV\ntypedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETOCCLUSIONQUERYIVNVPROC glad_glGetOcclusionQueryivNV;\n#define glGetOcclusionQueryivNV glad_glGetOcclusionQueryivNV\ntypedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC)(GLuint, GLenum, GLuint*);\nGLAPI PFNGLGETOCCLUSIONQUERYUIVNVPROC glad_glGetOcclusionQueryuivNV;\n#define glGetOcclusionQueryuivNV glad_glGetOcclusionQueryuivNV\n#endif\n#ifndef GL_APPLE_flush_buffer_range\n#define GL_APPLE_flush_buffer_range 1\nGLAPI int GLAD_GL_APPLE_flush_buffer_range;\ntypedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC)(GLenum, GLenum, GLint);\nGLAPI PFNGLBUFFERPARAMETERIAPPLEPROC glad_glBufferParameteriAPPLE;\n#define glBufferParameteriAPPLE glad_glBufferParameteriAPPLE\ntypedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC)(GLenum, GLintptr, GLsizeiptr);\nGLAPI PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC glad_glFlushMappedBufferRangeAPPLE;\n#define glFlushMappedBufferRangeAPPLE glad_glFlushMappedBufferRangeAPPLE\n#endif\n#ifndef GL_ARB_imaging\n#define GL_ARB_imaging 1\nGLAPI int GLAD_GL_ARB_imaging;\ntypedef void (APIENTRYP PFNGLCOLORTABLEPROC)(GLenum, GLenum, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLCOLORTABLEPROC glad_glColorTable;\n#define glColorTable glad_glColorTable\ntypedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLCOLORTABLEPARAMETERFVPROC glad_glColorTableParameterfv;\n#define glColorTableParameterfv glad_glColorTableParameterfv\ntypedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLCOLORTABLEPARAMETERIVPROC glad_glColorTableParameteriv;\n#define glColorTableParameteriv glad_glColorTableParameteriv\ntypedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC)(GLenum, GLenum, GLint, GLint, GLsizei);\nGLAPI PFNGLCOPYCOLORTABLEPROC glad_glCopyColorTable;\n#define glCopyColorTable glad_glCopyColorTable\ntypedef void (APIENTRYP PFNGLGETCOLORTABLEPROC)(GLenum, GLenum, GLenum, void*);\nGLAPI PFNGLGETCOLORTABLEPROC glad_glGetColorTable;\n#define glGetColorTable glad_glGetColorTable\ntypedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETCOLORTABLEPARAMETERFVPROC glad_glGetColorTableParameterfv;\n#define glGetColorTableParameterfv glad_glGetColorTableParameterfv\ntypedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETCOLORTABLEPARAMETERIVPROC glad_glGetColorTableParameteriv;\n#define glGetColorTableParameteriv glad_glGetColorTableParameteriv\ntypedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC)(GLenum, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLCOLORSUBTABLEPROC glad_glColorSubTable;\n#define glColorSubTable glad_glColorSubTable\ntypedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC)(GLenum, GLsizei, GLint, GLint, GLsizei);\nGLAPI PFNGLCOPYCOLORSUBTABLEPROC glad_glCopyColorSubTable;\n#define glCopyColorSubTable glad_glCopyColorSubTable\ntypedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC)(GLenum, GLenum, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLCONVOLUTIONFILTER1DPROC glad_glConvolutionFilter1D;\n#define glConvolutionFilter1D glad_glConvolutionFilter1D\ntypedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC)(GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLCONVOLUTIONFILTER2DPROC glad_glConvolutionFilter2D;\n#define glConvolutionFilter2D glad_glConvolutionFilter2D\ntypedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC)(GLenum, GLenum, GLfloat);\nGLAPI PFNGLCONVOLUTIONPARAMETERFPROC glad_glConvolutionParameterf;\n#define glConvolutionParameterf glad_glConvolutionParameterf\ntypedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLCONVOLUTIONPARAMETERFVPROC glad_glConvolutionParameterfv;\n#define glConvolutionParameterfv glad_glConvolutionParameterfv\ntypedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC)(GLenum, GLenum, GLint);\nGLAPI PFNGLCONVOLUTIONPARAMETERIPROC glad_glConvolutionParameteri;\n#define glConvolutionParameteri glad_glConvolutionParameteri\ntypedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLCONVOLUTIONPARAMETERIVPROC glad_glConvolutionParameteriv;\n#define glConvolutionParameteriv glad_glConvolutionParameteriv\ntypedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC)(GLenum, GLenum, GLint, GLint, GLsizei);\nGLAPI PFNGLCOPYCONVOLUTIONFILTER1DPROC glad_glCopyConvolutionFilter1D;\n#define glCopyConvolutionFilter1D glad_glCopyConvolutionFilter1D\ntypedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC)(GLenum, GLenum, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLCOPYCONVOLUTIONFILTER2DPROC glad_glCopyConvolutionFilter2D;\n#define glCopyConvolutionFilter2D glad_glCopyConvolutionFilter2D\ntypedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC)(GLenum, GLenum, GLenum, void*);\nGLAPI PFNGLGETCONVOLUTIONFILTERPROC glad_glGetConvolutionFilter;\n#define glGetConvolutionFilter glad_glGetConvolutionFilter\ntypedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETCONVOLUTIONPARAMETERFVPROC glad_glGetConvolutionParameterfv;\n#define glGetConvolutionParameterfv glad_glGetConvolutionParameterfv\ntypedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETCONVOLUTIONPARAMETERIVPROC glad_glGetConvolutionParameteriv;\n#define glGetConvolutionParameteriv glad_glGetConvolutionParameteriv\ntypedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC)(GLenum, GLenum, GLenum, void*, void*, void*);\nGLAPI PFNGLGETSEPARABLEFILTERPROC glad_glGetSeparableFilter;\n#define glGetSeparableFilter glad_glGetSeparableFilter\ntypedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC)(GLenum, GLenum, GLsizei, GLsizei, GLenum, GLenum, const void*, const void*);\nGLAPI PFNGLSEPARABLEFILTER2DPROC glad_glSeparableFilter2D;\n#define glSeparableFilter2D glad_glSeparableFilter2D\ntypedef void (APIENTRYP PFNGLGETHISTOGRAMPROC)(GLenum, GLboolean, GLenum, GLenum, void*);\nGLAPI PFNGLGETHISTOGRAMPROC glad_glGetHistogram;\n#define glGetHistogram glad_glGetHistogram\ntypedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETHISTOGRAMPARAMETERFVPROC glad_glGetHistogramParameterfv;\n#define glGetHistogramParameterfv glad_glGetHistogramParameterfv\ntypedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETHISTOGRAMPARAMETERIVPROC glad_glGetHistogramParameteriv;\n#define glGetHistogramParameteriv glad_glGetHistogramParameteriv\ntypedef void (APIENTRYP PFNGLGETMINMAXPROC)(GLenum, GLboolean, GLenum, GLenum, void*);\nGLAPI PFNGLGETMINMAXPROC glad_glGetMinmax;\n#define glGetMinmax glad_glGetMinmax\ntypedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETMINMAXPARAMETERFVPROC glad_glGetMinmaxParameterfv;\n#define glGetMinmaxParameterfv glad_glGetMinmaxParameterfv\ntypedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETMINMAXPARAMETERIVPROC glad_glGetMinmaxParameteriv;\n#define glGetMinmaxParameteriv glad_glGetMinmaxParameteriv\ntypedef void (APIENTRYP PFNGLHISTOGRAMPROC)(GLenum, GLsizei, GLenum, GLboolean);\nGLAPI PFNGLHISTOGRAMPROC glad_glHistogram;\n#define glHistogram glad_glHistogram\ntypedef void (APIENTRYP PFNGLMINMAXPROC)(GLenum, GLenum, GLboolean);\nGLAPI PFNGLMINMAXPROC glad_glMinmax;\n#define glMinmax glad_glMinmax\ntypedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC)(GLenum);\nGLAPI PFNGLRESETHISTOGRAMPROC glad_glResetHistogram;\n#define glResetHistogram glad_glResetHistogram\ntypedef void (APIENTRYP PFNGLRESETMINMAXPROC)(GLenum);\nGLAPI PFNGLRESETMINMAXPROC glad_glResetMinmax;\n#define glResetMinmax glad_glResetMinmax\n#endif\n#ifndef GL_ARB_draw_buffers_blend\n#define GL_ARB_draw_buffers_blend 1\nGLAPI int GLAD_GL_ARB_draw_buffers_blend;\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC)(GLuint, GLenum);\nGLAPI PFNGLBLENDEQUATIONIARBPROC glad_glBlendEquationiARB;\n#define glBlendEquationiARB glad_glBlendEquationiARB\ntypedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC)(GLuint, GLenum, GLenum);\nGLAPI PFNGLBLENDEQUATIONSEPARATEIARBPROC glad_glBlendEquationSeparateiARB;\n#define glBlendEquationSeparateiARB glad_glBlendEquationSeparateiARB\ntypedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC)(GLuint, GLenum, GLenum);\nGLAPI PFNGLBLENDFUNCIARBPROC glad_glBlendFunciARB;\n#define glBlendFunciARB glad_glBlendFunciARB\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC)(GLuint, GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLBLENDFUNCSEPARATEIARBPROC glad_glBlendFuncSeparateiARB;\n#define glBlendFuncSeparateiARB glad_glBlendFuncSeparateiARB\n#endif\n#ifndef GL_NV_blend_square\n#define GL_NV_blend_square 1\nGLAPI int GLAD_GL_NV_blend_square;\n#endif\n#ifndef GL_AMD_blend_minmax_factor\n#define GL_AMD_blend_minmax_factor 1\nGLAPI int GLAD_GL_AMD_blend_minmax_factor;\n#endif\n#ifndef GL_EXT_texture_sRGB_decode\n#define GL_EXT_texture_sRGB_decode 1\nGLAPI int GLAD_GL_EXT_texture_sRGB_decode;\n#endif\n#ifndef GL_ARB_shading_language_420pack\n#define GL_ARB_shading_language_420pack 1\nGLAPI int GLAD_GL_ARB_shading_language_420pack;\n#endif\n#ifndef GL_ATI_meminfo\n#define GL_ATI_meminfo 1\nGLAPI int GLAD_GL_ATI_meminfo;\n#endif\n#ifndef GL_EXT_abgr\n#define GL_EXT_abgr 1\nGLAPI int GLAD_GL_EXT_abgr;\n#endif\n#ifndef GL_AMD_pinned_memory\n#define GL_AMD_pinned_memory 1\nGLAPI int GLAD_GL_AMD_pinned_memory;\n#endif\n#ifndef GL_EXT_texture_snorm\n#define GL_EXT_texture_snorm 1\nGLAPI int GLAD_GL_EXT_texture_snorm;\n#endif\n#ifndef GL_SGIX_texture_coordinate_clamp\n#define GL_SGIX_texture_coordinate_clamp 1\nGLAPI int GLAD_GL_SGIX_texture_coordinate_clamp;\n#endif\n#ifndef GL_ARB_clear_buffer_object\n#define GL_ARB_clear_buffer_object 1\nGLAPI int GLAD_GL_ARB_clear_buffer_object;\n#endif\n#ifndef GL_ARB_multisample\n#define GL_ARB_multisample 1\nGLAPI int GLAD_GL_ARB_multisample;\ntypedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC)(GLfloat, GLboolean);\nGLAPI PFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB;\n#define glSampleCoverageARB glad_glSampleCoverageARB\n#endif\n#ifndef GL_EXT_debug_label\n#define GL_EXT_debug_label 1\nGLAPI int GLAD_GL_EXT_debug_label;\ntypedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC)(GLenum, GLuint, GLsizei, const GLchar*);\nGLAPI PFNGLLABELOBJECTEXTPROC glad_glLabelObjectEXT;\n#define glLabelObjectEXT glad_glLabelObjectEXT\ntypedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC)(GLenum, GLuint, GLsizei, GLsizei*, GLchar*);\nGLAPI PFNGLGETOBJECTLABELEXTPROC glad_glGetObjectLabelEXT;\n#define glGetObjectLabelEXT glad_glGetObjectLabelEXT\n#endif\n#ifndef GL_ARB_sample_shading\n#define GL_ARB_sample_shading 1\nGLAPI int GLAD_GL_ARB_sample_shading;\ntypedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC)(GLfloat);\nGLAPI PFNGLMINSAMPLESHADINGARBPROC glad_glMinSampleShadingARB;\n#define glMinSampleShadingARB glad_glMinSampleShadingARB\n#endif\n#ifndef GL_INTEL_map_texture\n#define GL_INTEL_map_texture 1\nGLAPI int GLAD_GL_INTEL_map_texture;\ntypedef void (APIENTRYP PFNGLSYNCTEXTUREINTELPROC)(GLuint);\nGLAPI PFNGLSYNCTEXTUREINTELPROC glad_glSyncTextureINTEL;\n#define glSyncTextureINTEL glad_glSyncTextureINTEL\ntypedef void (APIENTRYP PFNGLUNMAPTEXTURE2DINTELPROC)(GLuint, GLint);\nGLAPI PFNGLUNMAPTEXTURE2DINTELPROC glad_glUnmapTexture2DINTEL;\n#define glUnmapTexture2DINTEL glad_glUnmapTexture2DINTEL\ntypedef void* (APIENTRYP PFNGLMAPTEXTURE2DINTELPROC)(GLuint, GLint, GLbitfield, GLint*, GLenum*);\nGLAPI PFNGLMAPTEXTURE2DINTELPROC glad_glMapTexture2DINTEL;\n#define glMapTexture2DINTEL glad_glMapTexture2DINTEL\n#endif\n#ifndef GL_ARB_texture_env_crossbar\n#define GL_ARB_texture_env_crossbar 1\nGLAPI int GLAD_GL_ARB_texture_env_crossbar;\n#endif\n#ifndef GL_EXT_422_pixels\n#define GL_EXT_422_pixels 1\nGLAPI int GLAD_GL_EXT_422_pixels;\n#endif\n#ifndef GL_ARB_compute_shader\n#define GL_ARB_compute_shader 1\nGLAPI int GLAD_GL_ARB_compute_shader;\n#endif\n#ifndef GL_EXT_blend_logic_op\n#define GL_EXT_blend_logic_op 1\nGLAPI int GLAD_GL_EXT_blend_logic_op;\n#endif\n#ifndef GL_IBM_cull_vertex\n#define GL_IBM_cull_vertex 1\nGLAPI int GLAD_GL_IBM_cull_vertex;\n#endif\n#ifndef GL_IBM_vertex_array_lists\n#define GL_IBM_vertex_array_lists 1\nGLAPI int GLAD_GL_IBM_vertex_array_lists;\ntypedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC)(GLint, GLenum, GLint, const void**, GLint);\nGLAPI PFNGLCOLORPOINTERLISTIBMPROC glad_glColorPointerListIBM;\n#define glColorPointerListIBM glad_glColorPointerListIBM\ntypedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC)(GLint, GLenum, GLint, const void**, GLint);\nGLAPI PFNGLSECONDARYCOLORPOINTERLISTIBMPROC glad_glSecondaryColorPointerListIBM;\n#define glSecondaryColorPointerListIBM glad_glSecondaryColorPointerListIBM\ntypedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC)(GLint, const GLboolean**, GLint);\nGLAPI PFNGLEDGEFLAGPOINTERLISTIBMPROC glad_glEdgeFlagPointerListIBM;\n#define glEdgeFlagPointerListIBM glad_glEdgeFlagPointerListIBM\ntypedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC)(GLenum, GLint, const void**, GLint);\nGLAPI PFNGLFOGCOORDPOINTERLISTIBMPROC glad_glFogCoordPointerListIBM;\n#define glFogCoordPointerListIBM glad_glFogCoordPointerListIBM\ntypedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC)(GLenum, GLint, const void**, GLint);\nGLAPI PFNGLINDEXPOINTERLISTIBMPROC glad_glIndexPointerListIBM;\n#define glIndexPointerListIBM glad_glIndexPointerListIBM\ntypedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC)(GLenum, GLint, const void**, GLint);\nGLAPI PFNGLNORMALPOINTERLISTIBMPROC glad_glNormalPointerListIBM;\n#define glNormalPointerListIBM glad_glNormalPointerListIBM\ntypedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC)(GLint, GLenum, GLint, const void**, GLint);\nGLAPI PFNGLTEXCOORDPOINTERLISTIBMPROC glad_glTexCoordPointerListIBM;\n#define glTexCoordPointerListIBM glad_glTexCoordPointerListIBM\ntypedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC)(GLint, GLenum, GLint, const void**, GLint);\nGLAPI PFNGLVERTEXPOINTERLISTIBMPROC glad_glVertexPointerListIBM;\n#define glVertexPointerListIBM glad_glVertexPointerListIBM\n#endif\n#ifndef GL_ARB_color_buffer_float\n#define GL_ARB_color_buffer_float 1\nGLAPI int GLAD_GL_ARB_color_buffer_float;\ntypedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC)(GLenum, GLenum);\nGLAPI PFNGLCLAMPCOLORARBPROC glad_glClampColorARB;\n#define glClampColorARB glad_glClampColorARB\n#endif\n#ifndef GL_ARB_bindless_texture\n#define GL_ARB_bindless_texture 1\nGLAPI int GLAD_GL_ARB_bindless_texture;\ntypedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC)(GLuint);\nGLAPI PFNGLGETTEXTUREHANDLEARBPROC glad_glGetTextureHandleARB;\n#define glGetTextureHandleARB glad_glGetTextureHandleARB\ntypedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC)(GLuint, GLuint);\nGLAPI PFNGLGETTEXTURESAMPLERHANDLEARBPROC glad_glGetTextureSamplerHandleARB;\n#define glGetTextureSamplerHandleARB glad_glGetTextureSamplerHandleARB\ntypedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC)(GLuint64);\nGLAPI PFNGLMAKETEXTUREHANDLERESIDENTARBPROC glad_glMakeTextureHandleResidentARB;\n#define glMakeTextureHandleResidentARB glad_glMakeTextureHandleResidentARB\ntypedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC)(GLuint64);\nGLAPI PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC glad_glMakeTextureHandleNonResidentARB;\n#define glMakeTextureHandleNonResidentARB glad_glMakeTextureHandleNonResidentARB\ntypedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC)(GLuint, GLint, GLboolean, GLint, GLenum);\nGLAPI PFNGLGETIMAGEHANDLEARBPROC glad_glGetImageHandleARB;\n#define glGetImageHandleARB glad_glGetImageHandleARB\ntypedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC)(GLuint64, GLenum);\nGLAPI PFNGLMAKEIMAGEHANDLERESIDENTARBPROC glad_glMakeImageHandleResidentARB;\n#define glMakeImageHandleResidentARB glad_glMakeImageHandleResidentARB\ntypedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC)(GLuint64);\nGLAPI PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC glad_glMakeImageHandleNonResidentARB;\n#define glMakeImageHandleNonResidentARB glad_glMakeImageHandleNonResidentARB\ntypedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC)(GLint, GLuint64);\nGLAPI PFNGLUNIFORMHANDLEUI64ARBPROC glad_glUniformHandleui64ARB;\n#define glUniformHandleui64ARB glad_glUniformHandleui64ARB\ntypedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC)(GLint, GLsizei, const GLuint64*);\nGLAPI PFNGLUNIFORMHANDLEUI64VARBPROC glad_glUniformHandleui64vARB;\n#define glUniformHandleui64vARB glad_glUniformHandleui64vARB\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC)(GLuint, GLint, GLuint64);\nGLAPI PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC glad_glProgramUniformHandleui64ARB;\n#define glProgramUniformHandleui64ARB glad_glProgramUniformHandleui64ARB\ntypedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC)(GLuint, GLint, GLsizei, const GLuint64*);\nGLAPI PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC glad_glProgramUniformHandleui64vARB;\n#define glProgramUniformHandleui64vARB glad_glProgramUniformHandleui64vARB\ntypedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC)(GLuint64);\nGLAPI PFNGLISTEXTUREHANDLERESIDENTARBPROC glad_glIsTextureHandleResidentARB;\n#define glIsTextureHandleResidentARB glad_glIsTextureHandleResidentARB\ntypedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC)(GLuint64);\nGLAPI PFNGLISIMAGEHANDLERESIDENTARBPROC glad_glIsImageHandleResidentARB;\n#define glIsImageHandleResidentARB glad_glIsImageHandleResidentARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC)(GLuint, GLuint64EXT);\nGLAPI PFNGLVERTEXATTRIBL1UI64ARBPROC glad_glVertexAttribL1ui64ARB;\n#define glVertexAttribL1ui64ARB glad_glVertexAttribL1ui64ARB\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC)(GLuint, const GLuint64EXT*);\nGLAPI PFNGLVERTEXATTRIBL1UI64VARBPROC glad_glVertexAttribL1ui64vARB;\n#define glVertexAttribL1ui64vARB glad_glVertexAttribL1ui64vARB\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC)(GLuint, GLenum, GLuint64EXT*);\nGLAPI PFNGLGETVERTEXATTRIBLUI64VARBPROC glad_glGetVertexAttribLui64vARB;\n#define glGetVertexAttribLui64vARB glad_glGetVertexAttribLui64vARB\n#endif\n#ifndef GL_ARB_window_pos\n#define GL_ARB_window_pos 1\nGLAPI int GLAD_GL_ARB_window_pos;\ntypedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC)(GLdouble, GLdouble);\nGLAPI PFNGLWINDOWPOS2DARBPROC glad_glWindowPos2dARB;\n#define glWindowPos2dARB glad_glWindowPos2dARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC)(const GLdouble*);\nGLAPI PFNGLWINDOWPOS2DVARBPROC glad_glWindowPos2dvARB;\n#define glWindowPos2dvARB glad_glWindowPos2dvARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC)(GLfloat, GLfloat);\nGLAPI PFNGLWINDOWPOS2FARBPROC glad_glWindowPos2fARB;\n#define glWindowPos2fARB glad_glWindowPos2fARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC)(const GLfloat*);\nGLAPI PFNGLWINDOWPOS2FVARBPROC glad_glWindowPos2fvARB;\n#define glWindowPos2fvARB glad_glWindowPos2fvARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC)(GLint, GLint);\nGLAPI PFNGLWINDOWPOS2IARBPROC glad_glWindowPos2iARB;\n#define glWindowPos2iARB glad_glWindowPos2iARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC)(const GLint*);\nGLAPI PFNGLWINDOWPOS2IVARBPROC glad_glWindowPos2ivARB;\n#define glWindowPos2ivARB glad_glWindowPos2ivARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC)(GLshort, GLshort);\nGLAPI PFNGLWINDOWPOS2SARBPROC glad_glWindowPos2sARB;\n#define glWindowPos2sARB glad_glWindowPos2sARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC)(const GLshort*);\nGLAPI PFNGLWINDOWPOS2SVARBPROC glad_glWindowPos2svARB;\n#define glWindowPos2svARB glad_glWindowPos2svARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC)(GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLWINDOWPOS3DARBPROC glad_glWindowPos3dARB;\n#define glWindowPos3dARB glad_glWindowPos3dARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC)(const GLdouble*);\nGLAPI PFNGLWINDOWPOS3DVARBPROC glad_glWindowPos3dvARB;\n#define glWindowPos3dvARB glad_glWindowPos3dvARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC)(GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLWINDOWPOS3FARBPROC glad_glWindowPos3fARB;\n#define glWindowPos3fARB glad_glWindowPos3fARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC)(const GLfloat*);\nGLAPI PFNGLWINDOWPOS3FVARBPROC glad_glWindowPos3fvARB;\n#define glWindowPos3fvARB glad_glWindowPos3fvARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC)(GLint, GLint, GLint);\nGLAPI PFNGLWINDOWPOS3IARBPROC glad_glWindowPos3iARB;\n#define glWindowPos3iARB glad_glWindowPos3iARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC)(const GLint*);\nGLAPI PFNGLWINDOWPOS3IVARBPROC glad_glWindowPos3ivARB;\n#define glWindowPos3ivARB glad_glWindowPos3ivARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC)(GLshort, GLshort, GLshort);\nGLAPI PFNGLWINDOWPOS3SARBPROC glad_glWindowPos3sARB;\n#define glWindowPos3sARB glad_glWindowPos3sARB\ntypedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC)(const GLshort*);\nGLAPI PFNGLWINDOWPOS3SVARBPROC glad_glWindowPos3svARB;\n#define glWindowPos3svARB glad_glWindowPos3svARB\n#endif\n#ifndef GL_ARB_internalformat_query\n#define GL_ARB_internalformat_query 1\nGLAPI int GLAD_GL_ARB_internalformat_query;\n#endif\n#ifndef GL_ARB_shadow\n#define GL_ARB_shadow 1\nGLAPI int GLAD_GL_ARB_shadow;\n#endif\n#ifndef GL_ARB_texture_mirrored_repeat\n#define GL_ARB_texture_mirrored_repeat 1\nGLAPI int GLAD_GL_ARB_texture_mirrored_repeat;\n#endif\n#ifndef GL_EXT_shader_image_load_store\n#define GL_EXT_shader_image_load_store 1\nGLAPI int GLAD_GL_EXT_shader_image_load_store;\ntypedef void (APIENTRYP PFNGLBINDIMAGETEXTUREEXTPROC)(GLuint, GLuint, GLint, GLboolean, GLint, GLenum, GLint);\nGLAPI PFNGLBINDIMAGETEXTUREEXTPROC glad_glBindImageTextureEXT;\n#define glBindImageTextureEXT glad_glBindImageTextureEXT\ntypedef void (APIENTRYP PFNGLMEMORYBARRIEREXTPROC)(GLbitfield);\nGLAPI PFNGLMEMORYBARRIEREXTPROC glad_glMemoryBarrierEXT;\n#define glMemoryBarrierEXT glad_glMemoryBarrierEXT\n#endif\n#ifndef GL_EXT_copy_texture\n#define GL_EXT_copy_texture 1\nGLAPI int GLAD_GL_EXT_copy_texture;\ntypedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC)(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLint);\nGLAPI PFNGLCOPYTEXIMAGE1DEXTPROC glad_glCopyTexImage1DEXT;\n#define glCopyTexImage1DEXT glad_glCopyTexImage1DEXT\ntypedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC)(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint);\nGLAPI PFNGLCOPYTEXIMAGE2DEXTPROC glad_glCopyTexImage2DEXT;\n#define glCopyTexImage2DEXT glad_glCopyTexImage2DEXT\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC)(GLenum, GLint, GLint, GLint, GLint, GLsizei);\nGLAPI PFNGLCOPYTEXSUBIMAGE1DEXTPROC glad_glCopyTexSubImage1DEXT;\n#define glCopyTexSubImage1DEXT glad_glCopyTexSubImage1DEXT\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC)(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLCOPYTEXSUBIMAGE2DEXTPROC glad_glCopyTexSubImage2DEXT;\n#define glCopyTexSubImage2DEXT glad_glCopyTexSubImage2DEXT\ntypedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC)(GLenum, GLint, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei);\nGLAPI PFNGLCOPYTEXSUBIMAGE3DEXTPROC glad_glCopyTexSubImage3DEXT;\n#define glCopyTexSubImage3DEXT glad_glCopyTexSubImage3DEXT\n#endif\n#ifndef GL_NV_register_combiners2\n#define GL_NV_register_combiners2 1\nGLAPI int GLAD_GL_NV_register_combiners2;\ntypedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLCOMBINERSTAGEPARAMETERFVNVPROC glad_glCombinerStageParameterfvNV;\n#define glCombinerStageParameterfvNV glad_glCombinerStageParameterfvNV\ntypedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC glad_glGetCombinerStageParameterfvNV;\n#define glGetCombinerStageParameterfvNV glad_glGetCombinerStageParameterfvNV\n#endif\n#ifndef GL_SGIX_ir_instrument1\n#define GL_SGIX_ir_instrument1 1\nGLAPI int GLAD_GL_SGIX_ir_instrument1;\n#endif\n#ifndef GL_NV_draw_texture\n#define GL_NV_draw_texture 1\nGLAPI int GLAD_GL_NV_draw_texture;\ntypedef void (APIENTRYP PFNGLDRAWTEXTURENVPROC)(GLuint, GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLDRAWTEXTURENVPROC glad_glDrawTextureNV;\n#define glDrawTextureNV glad_glDrawTextureNV\n#endif\n#ifndef GL_EXT_texture_shared_exponent\n#define GL_EXT_texture_shared_exponent 1\nGLAPI int GLAD_GL_EXT_texture_shared_exponent;\n#endif\n#ifndef GL_EXT_draw_instanced\n#define GL_EXT_draw_instanced 1\nGLAPI int GLAD_GL_EXT_draw_instanced;\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC)(GLenum, GLint, GLsizei, GLsizei);\nGLAPI PFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT;\n#define glDrawArraysInstancedEXT glad_glDrawArraysInstancedEXT\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC)(GLenum, GLsizei, GLenum, const void*, GLsizei);\nGLAPI PFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT;\n#define glDrawElementsInstancedEXT glad_glDrawElementsInstancedEXT\n#endif\n#ifndef GL_NV_copy_depth_to_color\n#define GL_NV_copy_depth_to_color 1\nGLAPI int GLAD_GL_NV_copy_depth_to_color;\n#endif\n#ifndef GL_ARB_viewport_array\n#define GL_ARB_viewport_array 1\nGLAPI int GLAD_GL_ARB_viewport_array;\n#endif\n#ifndef GL_ARB_separate_shader_objects\n#define GL_ARB_separate_shader_objects 1\nGLAPI int GLAD_GL_ARB_separate_shader_objects;\n#endif\n#ifndef GL_EXT_depth_bounds_test\n#define GL_EXT_depth_bounds_test 1\nGLAPI int GLAD_GL_EXT_depth_bounds_test;\ntypedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC)(GLclampd, GLclampd);\nGLAPI PFNGLDEPTHBOUNDSEXTPROC glad_glDepthBoundsEXT;\n#define glDepthBoundsEXT glad_glDepthBoundsEXT\n#endif\n#ifndef GL_HP_image_transform\n#define GL_HP_image_transform 1\nGLAPI int GLAD_GL_HP_image_transform;\ntypedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC)(GLenum, GLenum, GLint);\nGLAPI PFNGLIMAGETRANSFORMPARAMETERIHPPROC glad_glImageTransformParameteriHP;\n#define glImageTransformParameteriHP glad_glImageTransformParameteriHP\ntypedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC)(GLenum, GLenum, GLfloat);\nGLAPI PFNGLIMAGETRANSFORMPARAMETERFHPPROC glad_glImageTransformParameterfHP;\n#define glImageTransformParameterfHP glad_glImageTransformParameterfHP\ntypedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLIMAGETRANSFORMPARAMETERIVHPPROC glad_glImageTransformParameterivHP;\n#define glImageTransformParameterivHP glad_glImageTransformParameterivHP\ntypedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLIMAGETRANSFORMPARAMETERFVHPPROC glad_glImageTransformParameterfvHP;\n#define glImageTransformParameterfvHP glad_glImageTransformParameterfvHP\ntypedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC glad_glGetImageTransformParameterivHP;\n#define glGetImageTransformParameterivHP glad_glGetImageTransformParameterivHP\ntypedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC glad_glGetImageTransformParameterfvHP;\n#define glGetImageTransformParameterfvHP glad_glGetImageTransformParameterfvHP\n#endif\n#ifndef GL_ARB_texture_env_add\n#define GL_ARB_texture_env_add 1\nGLAPI int GLAD_GL_ARB_texture_env_add;\n#endif\n#ifndef GL_NV_video_capture\n#define GL_NV_video_capture 1\nGLAPI int GLAD_GL_NV_video_capture;\ntypedef void (APIENTRYP PFNGLBEGINVIDEOCAPTURENVPROC)(GLuint);\nGLAPI PFNGLBEGINVIDEOCAPTURENVPROC glad_glBeginVideoCaptureNV;\n#define glBeginVideoCaptureNV glad_glBeginVideoCaptureNV\ntypedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC)(GLuint, GLuint, GLenum, GLintptrARB);\nGLAPI PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC glad_glBindVideoCaptureStreamBufferNV;\n#define glBindVideoCaptureStreamBufferNV glad_glBindVideoCaptureStreamBufferNV\ntypedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC)(GLuint, GLuint, GLenum, GLenum, GLuint);\nGLAPI PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC glad_glBindVideoCaptureStreamTextureNV;\n#define glBindVideoCaptureStreamTextureNV glad_glBindVideoCaptureStreamTextureNV\ntypedef void (APIENTRYP PFNGLENDVIDEOCAPTURENVPROC)(GLuint);\nGLAPI PFNGLENDVIDEOCAPTURENVPROC glad_glEndVideoCaptureNV;\n#define glEndVideoCaptureNV glad_glEndVideoCaptureNV\ntypedef void (APIENTRYP PFNGLGETVIDEOCAPTUREIVNVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVIDEOCAPTUREIVNVPROC glad_glGetVideoCaptureivNV;\n#define glGetVideoCaptureivNV glad_glGetVideoCaptureivNV\ntypedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMIVNVPROC)(GLuint, GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVIDEOCAPTURESTREAMIVNVPROC glad_glGetVideoCaptureStreamivNV;\n#define glGetVideoCaptureStreamivNV glad_glGetVideoCaptureStreamivNV\ntypedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMFVNVPROC)(GLuint, GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETVIDEOCAPTURESTREAMFVNVPROC glad_glGetVideoCaptureStreamfvNV;\n#define glGetVideoCaptureStreamfvNV glad_glGetVideoCaptureStreamfvNV\ntypedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMDVNVPROC)(GLuint, GLuint, GLenum, GLdouble*);\nGLAPI PFNGLGETVIDEOCAPTURESTREAMDVNVPROC glad_glGetVideoCaptureStreamdvNV;\n#define glGetVideoCaptureStreamdvNV glad_glGetVideoCaptureStreamdvNV\ntypedef GLenum (APIENTRYP PFNGLVIDEOCAPTURENVPROC)(GLuint, GLuint*, GLuint64EXT*);\nGLAPI PFNGLVIDEOCAPTURENVPROC glad_glVideoCaptureNV;\n#define glVideoCaptureNV glad_glVideoCaptureNV\ntypedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC)(GLuint, GLuint, GLenum, const GLint*);\nGLAPI PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC glad_glVideoCaptureStreamParameterivNV;\n#define glVideoCaptureStreamParameterivNV glad_glVideoCaptureStreamParameterivNV\ntypedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC)(GLuint, GLuint, GLenum, const GLfloat*);\nGLAPI PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC glad_glVideoCaptureStreamParameterfvNV;\n#define glVideoCaptureStreamParameterfvNV glad_glVideoCaptureStreamParameterfvNV\ntypedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC)(GLuint, GLuint, GLenum, const GLdouble*);\nGLAPI PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC glad_glVideoCaptureStreamParameterdvNV;\n#define glVideoCaptureStreamParameterdvNV glad_glVideoCaptureStreamParameterdvNV\n#endif\n#ifndef GL_ARB_sampler_objects\n#define GL_ARB_sampler_objects 1\nGLAPI int GLAD_GL_ARB_sampler_objects;\n#endif\n#ifndef GL_ARB_matrix_palette\n#define GL_ARB_matrix_palette 1\nGLAPI int GLAD_GL_ARB_matrix_palette;\ntypedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC)(GLint);\nGLAPI PFNGLCURRENTPALETTEMATRIXARBPROC glad_glCurrentPaletteMatrixARB;\n#define glCurrentPaletteMatrixARB glad_glCurrentPaletteMatrixARB\ntypedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC)(GLint, const GLubyte*);\nGLAPI PFNGLMATRIXINDEXUBVARBPROC glad_glMatrixIndexubvARB;\n#define glMatrixIndexubvARB glad_glMatrixIndexubvARB\ntypedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC)(GLint, const GLushort*);\nGLAPI PFNGLMATRIXINDEXUSVARBPROC glad_glMatrixIndexusvARB;\n#define glMatrixIndexusvARB glad_glMatrixIndexusvARB\ntypedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC)(GLint, const GLuint*);\nGLAPI PFNGLMATRIXINDEXUIVARBPROC glad_glMatrixIndexuivARB;\n#define glMatrixIndexuivARB glad_glMatrixIndexuivARB\ntypedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC)(GLint, GLenum, GLsizei, const void*);\nGLAPI PFNGLMATRIXINDEXPOINTERARBPROC glad_glMatrixIndexPointerARB;\n#define glMatrixIndexPointerARB glad_glMatrixIndexPointerARB\n#endif\n#ifndef GL_SGIS_texture_color_mask\n#define GL_SGIS_texture_color_mask 1\nGLAPI int GLAD_GL_SGIS_texture_color_mask;\ntypedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC)(GLboolean, GLboolean, GLboolean, GLboolean);\nGLAPI PFNGLTEXTURECOLORMASKSGISPROC glad_glTextureColorMaskSGIS;\n#define glTextureColorMaskSGIS glad_glTextureColorMaskSGIS\n#endif\n#ifndef GL_EXT_packed_pixels\n#define GL_EXT_packed_pixels 1\nGLAPI int GLAD_GL_EXT_packed_pixels;\n#endif\n#ifndef GL_ARB_texture_compression\n#define GL_ARB_texture_compression 1\nGLAPI int GLAD_GL_ARB_texture_compression;\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glad_glCompressedTexImage3DARB;\n#define glCompressedTexImage3DARB glad_glCompressedTexImage3DARB\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glad_glCompressedTexImage2DARB;\n#define glCompressedTexImage2DARB glad_glCompressedTexImage2DARB\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC)(GLenum, GLint, GLenum, GLsizei, GLint, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXIMAGE1DARBPROC glad_glCompressedTexImage1DARB;\n#define glCompressedTexImage1DARB glad_glCompressedTexImage1DARB\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glad_glCompressedTexSubImage3DARB;\n#define glCompressedTexSubImage3DARB glad_glCompressedTexSubImage3DARB\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glad_glCompressedTexSubImage2DARB;\n#define glCompressedTexSubImage2DARB glad_glCompressedTexSubImage2DARB\ntypedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC)(GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const void*);\nGLAPI PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glad_glCompressedTexSubImage1DARB;\n#define glCompressedTexSubImage1DARB glad_glCompressedTexSubImage1DARB\ntypedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC)(GLenum, GLint, void*);\nGLAPI PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glad_glGetCompressedTexImageARB;\n#define glGetCompressedTexImageARB glad_glGetCompressedTexImageARB\n#endif\n#ifndef GL_APPLE_aux_depth_stencil\n#define GL_APPLE_aux_depth_stencil 1\nGLAPI int GLAD_GL_APPLE_aux_depth_stencil;\n#endif\n#ifndef GL_ARB_shader_subroutine\n#define GL_ARB_shader_subroutine 1\nGLAPI int GLAD_GL_ARB_shader_subroutine;\n#endif\n#ifndef GL_EXT_framebuffer_sRGB\n#define GL_EXT_framebuffer_sRGB 1\nGLAPI int GLAD_GL_EXT_framebuffer_sRGB;\n#endif\n#ifndef GL_ARB_texture_storage_multisample\n#define GL_ARB_texture_storage_multisample 1\nGLAPI int GLAD_GL_ARB_texture_storage_multisample;\n#endif\n#ifndef GL_EXT_vertex_attrib_64bit\n#define GL_EXT_vertex_attrib_64bit 1\nGLAPI int GLAD_GL_EXT_vertex_attrib_64bit;\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1DEXTPROC)(GLuint, GLdouble);\nGLAPI PFNGLVERTEXATTRIBL1DEXTPROC glad_glVertexAttribL1dEXT;\n#define glVertexAttribL1dEXT glad_glVertexAttribL1dEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2DEXTPROC)(GLuint, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIBL2DEXTPROC glad_glVertexAttribL2dEXT;\n#define glVertexAttribL2dEXT glad_glVertexAttribL2dEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3DEXTPROC)(GLuint, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIBL3DEXTPROC glad_glVertexAttribL3dEXT;\n#define glVertexAttribL3dEXT glad_glVertexAttribL3dEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4DEXTPROC)(GLuint, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXATTRIBL4DEXTPROC glad_glVertexAttribL4dEXT;\n#define glVertexAttribL4dEXT glad_glVertexAttribL4dEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVEXTPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBL1DVEXTPROC glad_glVertexAttribL1dvEXT;\n#define glVertexAttribL1dvEXT glad_glVertexAttribL1dvEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVEXTPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBL2DVEXTPROC glad_glVertexAttribL2dvEXT;\n#define glVertexAttribL2dvEXT glad_glVertexAttribL2dvEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVEXTPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBL3DVEXTPROC glad_glVertexAttribL3dvEXT;\n#define glVertexAttribL3dvEXT glad_glVertexAttribL3dvEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVEXTPROC)(GLuint, const GLdouble*);\nGLAPI PFNGLVERTEXATTRIBL4DVEXTPROC glad_glVertexAttribL4dvEXT;\n#define glVertexAttribL4dvEXT glad_glVertexAttribL4dvEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTEREXTPROC)(GLuint, GLint, GLenum, GLsizei, const void*);\nGLAPI PFNGLVERTEXATTRIBLPOINTEREXTPROC glad_glVertexAttribLPointerEXT;\n#define glVertexAttribLPointerEXT glad_glVertexAttribLPointerEXT\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVEXTPROC)(GLuint, GLenum, GLdouble*);\nGLAPI PFNGLGETVERTEXATTRIBLDVEXTPROC glad_glGetVertexAttribLdvEXT;\n#define glGetVertexAttribLdvEXT glad_glGetVertexAttribLdvEXT\n#endif\n#ifndef GL_ARB_depth_texture\n#define GL_ARB_depth_texture 1\nGLAPI int GLAD_GL_ARB_depth_texture;\n#endif\n#ifndef GL_NV_shader_buffer_store\n#define GL_NV_shader_buffer_store 1\nGLAPI int GLAD_GL_NV_shader_buffer_store;\n#endif\n#ifndef GL_OES_query_matrix\n#define GL_OES_query_matrix 1\nGLAPI int GLAD_GL_OES_query_matrix;\ntypedef GLbitfield (APIENTRYP PFNGLQUERYMATRIXXOESPROC)(GLfixed*, GLint*);\nGLAPI PFNGLQUERYMATRIXXOESPROC glad_glQueryMatrixxOES;\n#define glQueryMatrixxOES glad_glQueryMatrixxOES\n#endif\n#ifndef GL_APPLE_texture_range\n#define GL_APPLE_texture_range 1\nGLAPI int GLAD_GL_APPLE_texture_range;\ntypedef void (APIENTRYP PFNGLTEXTURERANGEAPPLEPROC)(GLenum, GLsizei, const void*);\nGLAPI PFNGLTEXTURERANGEAPPLEPROC glad_glTextureRangeAPPLE;\n#define glTextureRangeAPPLE glad_glTextureRangeAPPLE\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC)(GLenum, GLenum, void**);\nGLAPI PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC glad_glGetTexParameterPointervAPPLE;\n#define glGetTexParameterPointervAPPLE glad_glGetTexParameterPointervAPPLE\n#endif\n#ifndef GL_NV_shader_storage_buffer_object\n#define GL_NV_shader_storage_buffer_object 1\nGLAPI int GLAD_GL_NV_shader_storage_buffer_object;\n#endif\n#ifndef GL_ARB_texture_query_lod\n#define GL_ARB_texture_query_lod 1\nGLAPI int GLAD_GL_ARB_texture_query_lod;\n#endif\n#ifndef GL_ARB_copy_buffer\n#define GL_ARB_copy_buffer 1\nGLAPI int GLAD_GL_ARB_copy_buffer;\n#endif\n#ifndef GL_ARB_shader_image_size\n#define GL_ARB_shader_image_size 1\nGLAPI int GLAD_GL_ARB_shader_image_size;\n#endif\n#ifndef GL_NV_shader_atomic_counters\n#define GL_NV_shader_atomic_counters 1\nGLAPI int GLAD_GL_NV_shader_atomic_counters;\n#endif\n#ifndef GL_APPLE_object_purgeable\n#define GL_APPLE_object_purgeable 1\nGLAPI int GLAD_GL_APPLE_object_purgeable;\ntypedef GLenum (APIENTRYP PFNGLOBJECTPURGEABLEAPPLEPROC)(GLenum, GLuint, GLenum);\nGLAPI PFNGLOBJECTPURGEABLEAPPLEPROC glad_glObjectPurgeableAPPLE;\n#define glObjectPurgeableAPPLE glad_glObjectPurgeableAPPLE\ntypedef GLenum (APIENTRYP PFNGLOBJECTUNPURGEABLEAPPLEPROC)(GLenum, GLuint, GLenum);\nGLAPI PFNGLOBJECTUNPURGEABLEAPPLEPROC glad_glObjectUnpurgeableAPPLE;\n#define glObjectUnpurgeableAPPLE glad_glObjectUnpurgeableAPPLE\ntypedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVAPPLEPROC)(GLenum, GLuint, GLenum, GLint*);\nGLAPI PFNGLGETOBJECTPARAMETERIVAPPLEPROC glad_glGetObjectParameterivAPPLE;\n#define glGetObjectParameterivAPPLE glad_glGetObjectParameterivAPPLE\n#endif\n#ifndef GL_ARB_occlusion_query\n#define GL_ARB_occlusion_query 1\nGLAPI int GLAD_GL_ARB_occlusion_query;\ntypedef void (APIENTRYP PFNGLGENQUERIESARBPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENQUERIESARBPROC glad_glGenQueriesARB;\n#define glGenQueriesARB glad_glGenQueriesARB\ntypedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEQUERIESARBPROC glad_glDeleteQueriesARB;\n#define glDeleteQueriesARB glad_glDeleteQueriesARB\ntypedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC)(GLuint);\nGLAPI PFNGLISQUERYARBPROC glad_glIsQueryARB;\n#define glIsQueryARB glad_glIsQueryARB\ntypedef void (APIENTRYP PFNGLBEGINQUERYARBPROC)(GLenum, GLuint);\nGLAPI PFNGLBEGINQUERYARBPROC glad_glBeginQueryARB;\n#define glBeginQueryARB glad_glBeginQueryARB\ntypedef void (APIENTRYP PFNGLENDQUERYARBPROC)(GLenum);\nGLAPI PFNGLENDQUERYARBPROC glad_glEndQueryARB;\n#define glEndQueryARB glad_glEndQueryARB\ntypedef void (APIENTRYP PFNGLGETQUERYIVARBPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETQUERYIVARBPROC glad_glGetQueryivARB;\n#define glGetQueryivARB glad_glGetQueryivARB\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETQUERYOBJECTIVARBPROC glad_glGetQueryObjectivARB;\n#define glGetQueryObjectivARB glad_glGetQueryObjectivARB\ntypedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC)(GLuint, GLenum, GLuint*);\nGLAPI PFNGLGETQUERYOBJECTUIVARBPROC glad_glGetQueryObjectuivARB;\n#define glGetQueryObjectuivARB glad_glGetQueryObjectuivARB\n#endif\n#ifndef GL_INGR_color_clamp\n#define GL_INGR_color_clamp 1\nGLAPI int GLAD_GL_INGR_color_clamp;\n#endif\n#ifndef GL_SGI_color_table\n#define GL_SGI_color_table 1\nGLAPI int GLAD_GL_SGI_color_table;\ntypedef void (APIENTRYP PFNGLCOLORTABLESGIPROC)(GLenum, GLenum, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLCOLORTABLESGIPROC glad_glColorTableSGI;\n#define glColorTableSGI glad_glColorTableSGI\ntypedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC)(GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLCOLORTABLEPARAMETERFVSGIPROC glad_glColorTableParameterfvSGI;\n#define glColorTableParameterfvSGI glad_glColorTableParameterfvSGI\ntypedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC)(GLenum, GLenum, const GLint*);\nGLAPI PFNGLCOLORTABLEPARAMETERIVSGIPROC glad_glColorTableParameterivSGI;\n#define glColorTableParameterivSGI glad_glColorTableParameterivSGI\ntypedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC)(GLenum, GLenum, GLint, GLint, GLsizei);\nGLAPI PFNGLCOPYCOLORTABLESGIPROC glad_glCopyColorTableSGI;\n#define glCopyColorTableSGI glad_glCopyColorTableSGI\ntypedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC)(GLenum, GLenum, GLenum, void*);\nGLAPI PFNGLGETCOLORTABLESGIPROC glad_glGetColorTableSGI;\n#define glGetColorTableSGI glad_glGetColorTableSGI\ntypedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETCOLORTABLEPARAMETERFVSGIPROC glad_glGetColorTableParameterfvSGI;\n#define glGetColorTableParameterfvSGI glad_glGetColorTableParameterfvSGI\ntypedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETCOLORTABLEPARAMETERIVSGIPROC glad_glGetColorTableParameterivSGI;\n#define glGetColorTableParameterivSGI glad_glGetColorTableParameterivSGI\n#endif\n#ifndef GL_NV_gpu_program5_mem_extended\n#define GL_NV_gpu_program5_mem_extended 1\nGLAPI int GLAD_GL_NV_gpu_program5_mem_extended;\n#endif\n#ifndef GL_ARB_texture_cube_map_array\n#define GL_ARB_texture_cube_map_array 1\nGLAPI int GLAD_GL_ARB_texture_cube_map_array;\n#endif\n#ifndef GL_SGIX_scalebias_hint\n#define GL_SGIX_scalebias_hint 1\nGLAPI int GLAD_GL_SGIX_scalebias_hint;\n#endif\n#ifndef GL_EXT_gpu_shader4\n#define GL_EXT_gpu_shader4 1\nGLAPI int GLAD_GL_EXT_gpu_shader4;\ntypedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC)(GLuint, GLint, GLuint*);\nGLAPI PFNGLGETUNIFORMUIVEXTPROC glad_glGetUniformuivEXT;\n#define glGetUniformuivEXT glad_glGetUniformuivEXT\ntypedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC)(GLuint, GLuint, const GLchar*);\nGLAPI PFNGLBINDFRAGDATALOCATIONEXTPROC glad_glBindFragDataLocationEXT;\n#define glBindFragDataLocationEXT glad_glBindFragDataLocationEXT\ntypedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC)(GLuint, const GLchar*);\nGLAPI PFNGLGETFRAGDATALOCATIONEXTPROC glad_glGetFragDataLocationEXT;\n#define glGetFragDataLocationEXT glad_glGetFragDataLocationEXT\ntypedef void (APIENTRYP PFNGLUNIFORM1UIEXTPROC)(GLint, GLuint);\nGLAPI PFNGLUNIFORM1UIEXTPROC glad_glUniform1uiEXT;\n#define glUniform1uiEXT glad_glUniform1uiEXT\ntypedef void (APIENTRYP PFNGLUNIFORM2UIEXTPROC)(GLint, GLuint, GLuint);\nGLAPI PFNGLUNIFORM2UIEXTPROC glad_glUniform2uiEXT;\n#define glUniform2uiEXT glad_glUniform2uiEXT\ntypedef void (APIENTRYP PFNGLUNIFORM3UIEXTPROC)(GLint, GLuint, GLuint, GLuint);\nGLAPI PFNGLUNIFORM3UIEXTPROC glad_glUniform3uiEXT;\n#define glUniform3uiEXT glad_glUniform3uiEXT\ntypedef void (APIENTRYP PFNGLUNIFORM4UIEXTPROC)(GLint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLUNIFORM4UIEXTPROC glad_glUniform4uiEXT;\n#define glUniform4uiEXT glad_glUniform4uiEXT\ntypedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC)(GLint, GLsizei, const GLuint*);\nGLAPI PFNGLUNIFORM1UIVEXTPROC glad_glUniform1uivEXT;\n#define glUniform1uivEXT glad_glUniform1uivEXT\ntypedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC)(GLint, GLsizei, const GLuint*);\nGLAPI PFNGLUNIFORM2UIVEXTPROC glad_glUniform2uivEXT;\n#define glUniform2uivEXT glad_glUniform2uivEXT\ntypedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC)(GLint, GLsizei, const GLuint*);\nGLAPI PFNGLUNIFORM3UIVEXTPROC glad_glUniform3uivEXT;\n#define glUniform3uivEXT glad_glUniform3uivEXT\ntypedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC)(GLint, GLsizei, const GLuint*);\nGLAPI PFNGLUNIFORM4UIVEXTPROC glad_glUniform4uivEXT;\n#define glUniform4uivEXT glad_glUniform4uivEXT\n#endif\n#ifndef GL_NV_geometry_program4\n#define GL_NV_geometry_program4 1\nGLAPI int GLAD_GL_NV_geometry_program4;\ntypedef void (APIENTRYP PFNGLPROGRAMVERTEXLIMITNVPROC)(GLenum, GLint);\nGLAPI PFNGLPROGRAMVERTEXLIMITNVPROC glad_glProgramVertexLimitNV;\n#define glProgramVertexLimitNV glad_glProgramVertexLimitNV\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC)(GLenum, GLenum, GLuint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTUREEXTPROC glad_glFramebufferTextureEXT;\n#define glFramebufferTextureEXT glad_glFramebufferTextureEXT\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC)(GLenum, GLenum, GLuint, GLint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC glad_glFramebufferTextureLayerEXT;\n#define glFramebufferTextureLayerEXT glad_glFramebufferTextureLayerEXT\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC)(GLenum, GLenum, GLuint, GLint, GLenum);\nGLAPI PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC glad_glFramebufferTextureFaceEXT;\n#define glFramebufferTextureFaceEXT glad_glFramebufferTextureFaceEXT\n#endif\n#ifndef GL_EXT_framebuffer_multisample_blit_scaled\n#define GL_EXT_framebuffer_multisample_blit_scaled 1\nGLAPI int GLAD_GL_EXT_framebuffer_multisample_blit_scaled;\n#endif\n#ifndef GL_AMD_debug_output\n#define GL_AMD_debug_output 1\nGLAPI int GLAD_GL_AMD_debug_output;\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGEENABLEAMDPROC)(GLenum, GLenum, GLsizei, const GLuint*, GLboolean);\nGLAPI PFNGLDEBUGMESSAGEENABLEAMDPROC glad_glDebugMessageEnableAMD;\n#define glDebugMessageEnableAMD glad_glDebugMessageEnableAMD\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTAMDPROC)(GLenum, GLenum, GLuint, GLsizei, const GLchar*);\nGLAPI PFNGLDEBUGMESSAGEINSERTAMDPROC glad_glDebugMessageInsertAMD;\n#define glDebugMessageInsertAMD glad_glDebugMessageInsertAMD\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKAMDPROC)(GLDEBUGPROCAMD, void*);\nGLAPI PFNGLDEBUGMESSAGECALLBACKAMDPROC glad_glDebugMessageCallbackAMD;\n#define glDebugMessageCallbackAMD glad_glDebugMessageCallbackAMD\ntypedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC)(GLuint, GLsizei, GLenum*, GLuint*, GLuint*, GLsizei*, GLchar*);\nGLAPI PFNGLGETDEBUGMESSAGELOGAMDPROC glad_glGetDebugMessageLogAMD;\n#define glGetDebugMessageLogAMD glad_glGetDebugMessageLogAMD\n#endif\n#ifndef GL_ARB_texture_border_clamp\n#define GL_ARB_texture_border_clamp 1\nGLAPI int GLAD_GL_ARB_texture_border_clamp;\n#endif\n#ifndef GL_ARB_fragment_coord_conventions\n#define GL_ARB_fragment_coord_conventions 1\nGLAPI int GLAD_GL_ARB_fragment_coord_conventions;\n#endif\n#ifndef GL_ARB_multitexture\n#define GL_ARB_multitexture 1\nGLAPI int GLAD_GL_ARB_multitexture;\ntypedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC)(GLenum);\nGLAPI PFNGLACTIVETEXTUREARBPROC glad_glActiveTextureARB;\n#define glActiveTextureARB glad_glActiveTextureARB\ntypedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC)(GLenum);\nGLAPI PFNGLCLIENTACTIVETEXTUREARBPROC glad_glClientActiveTextureARB;\n#define glClientActiveTextureARB glad_glClientActiveTextureARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC)(GLenum, GLdouble);\nGLAPI PFNGLMULTITEXCOORD1DARBPROC glad_glMultiTexCoord1dARB;\n#define glMultiTexCoord1dARB glad_glMultiTexCoord1dARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLMULTITEXCOORD1DVARBPROC glad_glMultiTexCoord1dvARB;\n#define glMultiTexCoord1dvARB glad_glMultiTexCoord1dvARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC)(GLenum, GLfloat);\nGLAPI PFNGLMULTITEXCOORD1FARBPROC glad_glMultiTexCoord1fARB;\n#define glMultiTexCoord1fARB glad_glMultiTexCoord1fARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLMULTITEXCOORD1FVARBPROC glad_glMultiTexCoord1fvARB;\n#define glMultiTexCoord1fvARB glad_glMultiTexCoord1fvARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC)(GLenum, GLint);\nGLAPI PFNGLMULTITEXCOORD1IARBPROC glad_glMultiTexCoord1iARB;\n#define glMultiTexCoord1iARB glad_glMultiTexCoord1iARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC)(GLenum, const GLint*);\nGLAPI PFNGLMULTITEXCOORD1IVARBPROC glad_glMultiTexCoord1ivARB;\n#define glMultiTexCoord1ivARB glad_glMultiTexCoord1ivARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC)(GLenum, GLshort);\nGLAPI PFNGLMULTITEXCOORD1SARBPROC glad_glMultiTexCoord1sARB;\n#define glMultiTexCoord1sARB glad_glMultiTexCoord1sARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC)(GLenum, const GLshort*);\nGLAPI PFNGLMULTITEXCOORD1SVARBPROC glad_glMultiTexCoord1svARB;\n#define glMultiTexCoord1svARB glad_glMultiTexCoord1svARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC)(GLenum, GLdouble, GLdouble);\nGLAPI PFNGLMULTITEXCOORD2DARBPROC glad_glMultiTexCoord2dARB;\n#define glMultiTexCoord2dARB glad_glMultiTexCoord2dARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLMULTITEXCOORD2DVARBPROC glad_glMultiTexCoord2dvARB;\n#define glMultiTexCoord2dvARB glad_glMultiTexCoord2dvARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC)(GLenum, GLfloat, GLfloat);\nGLAPI PFNGLMULTITEXCOORD2FARBPROC glad_glMultiTexCoord2fARB;\n#define glMultiTexCoord2fARB glad_glMultiTexCoord2fARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLMULTITEXCOORD2FVARBPROC glad_glMultiTexCoord2fvARB;\n#define glMultiTexCoord2fvARB glad_glMultiTexCoord2fvARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC)(GLenum, GLint, GLint);\nGLAPI PFNGLMULTITEXCOORD2IARBPROC glad_glMultiTexCoord2iARB;\n#define glMultiTexCoord2iARB glad_glMultiTexCoord2iARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC)(GLenum, const GLint*);\nGLAPI PFNGLMULTITEXCOORD2IVARBPROC glad_glMultiTexCoord2ivARB;\n#define glMultiTexCoord2ivARB glad_glMultiTexCoord2ivARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC)(GLenum, GLshort, GLshort);\nGLAPI PFNGLMULTITEXCOORD2SARBPROC glad_glMultiTexCoord2sARB;\n#define glMultiTexCoord2sARB glad_glMultiTexCoord2sARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC)(GLenum, const GLshort*);\nGLAPI PFNGLMULTITEXCOORD2SVARBPROC glad_glMultiTexCoord2svARB;\n#define glMultiTexCoord2svARB glad_glMultiTexCoord2svARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC)(GLenum, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLMULTITEXCOORD3DARBPROC glad_glMultiTexCoord3dARB;\n#define glMultiTexCoord3dARB glad_glMultiTexCoord3dARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLMULTITEXCOORD3DVARBPROC glad_glMultiTexCoord3dvARB;\n#define glMultiTexCoord3dvARB glad_glMultiTexCoord3dvARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC)(GLenum, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLMULTITEXCOORD3FARBPROC glad_glMultiTexCoord3fARB;\n#define glMultiTexCoord3fARB glad_glMultiTexCoord3fARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLMULTITEXCOORD3FVARBPROC glad_glMultiTexCoord3fvARB;\n#define glMultiTexCoord3fvARB glad_glMultiTexCoord3fvARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC)(GLenum, GLint, GLint, GLint);\nGLAPI PFNGLMULTITEXCOORD3IARBPROC glad_glMultiTexCoord3iARB;\n#define glMultiTexCoord3iARB glad_glMultiTexCoord3iARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC)(GLenum, const GLint*);\nGLAPI PFNGLMULTITEXCOORD3IVARBPROC glad_glMultiTexCoord3ivARB;\n#define glMultiTexCoord3ivARB glad_glMultiTexCoord3ivARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC)(GLenum, GLshort, GLshort, GLshort);\nGLAPI PFNGLMULTITEXCOORD3SARBPROC glad_glMultiTexCoord3sARB;\n#define glMultiTexCoord3sARB glad_glMultiTexCoord3sARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC)(GLenum, const GLshort*);\nGLAPI PFNGLMULTITEXCOORD3SVARBPROC glad_glMultiTexCoord3svARB;\n#define glMultiTexCoord3svARB glad_glMultiTexCoord3svARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC)(GLenum, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLMULTITEXCOORD4DARBPROC glad_glMultiTexCoord4dARB;\n#define glMultiTexCoord4dARB glad_glMultiTexCoord4dARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLMULTITEXCOORD4DVARBPROC glad_glMultiTexCoord4dvARB;\n#define glMultiTexCoord4dvARB glad_glMultiTexCoord4dvARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC)(GLenum, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLMULTITEXCOORD4FARBPROC glad_glMultiTexCoord4fARB;\n#define glMultiTexCoord4fARB glad_glMultiTexCoord4fARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLMULTITEXCOORD4FVARBPROC glad_glMultiTexCoord4fvARB;\n#define glMultiTexCoord4fvARB glad_glMultiTexCoord4fvARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC)(GLenum, GLint, GLint, GLint, GLint);\nGLAPI PFNGLMULTITEXCOORD4IARBPROC glad_glMultiTexCoord4iARB;\n#define glMultiTexCoord4iARB glad_glMultiTexCoord4iARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC)(GLenum, const GLint*);\nGLAPI PFNGLMULTITEXCOORD4IVARBPROC glad_glMultiTexCoord4ivARB;\n#define glMultiTexCoord4ivARB glad_glMultiTexCoord4ivARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC)(GLenum, GLshort, GLshort, GLshort, GLshort);\nGLAPI PFNGLMULTITEXCOORD4SARBPROC glad_glMultiTexCoord4sARB;\n#define glMultiTexCoord4sARB glad_glMultiTexCoord4sARB\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC)(GLenum, const GLshort*);\nGLAPI PFNGLMULTITEXCOORD4SVARBPROC glad_glMultiTexCoord4svARB;\n#define glMultiTexCoord4svARB glad_glMultiTexCoord4svARB\n#endif\n#ifndef GL_SGIX_polynomial_ffd\n#define GL_SGIX_polynomial_ffd 1\nGLAPI int GLAD_GL_SGIX_polynomial_ffd;\ntypedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC)(GLenum, GLdouble, GLdouble, GLint, GLint, GLdouble, GLdouble, GLint, GLint, GLdouble, GLdouble, GLint, GLint, const GLdouble*);\nGLAPI PFNGLDEFORMATIONMAP3DSGIXPROC glad_glDeformationMap3dSGIX;\n#define glDeformationMap3dSGIX glad_glDeformationMap3dSGIX\ntypedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC)(GLenum, GLfloat, GLfloat, GLint, GLint, GLfloat, GLfloat, GLint, GLint, GLfloat, GLfloat, GLint, GLint, const GLfloat*);\nGLAPI PFNGLDEFORMATIONMAP3FSGIXPROC glad_glDeformationMap3fSGIX;\n#define glDeformationMap3fSGIX glad_glDeformationMap3fSGIX\ntypedef void (APIENTRYP PFNGLDEFORMSGIXPROC)(GLbitfield);\nGLAPI PFNGLDEFORMSGIXPROC glad_glDeformSGIX;\n#define glDeformSGIX glad_glDeformSGIX\ntypedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC)(GLbitfield);\nGLAPI PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC glad_glLoadIdentityDeformationMapSGIX;\n#define glLoadIdentityDeformationMapSGIX glad_glLoadIdentityDeformationMapSGIX\n#endif\n#ifndef GL_EXT_provoking_vertex\n#define GL_EXT_provoking_vertex 1\nGLAPI int GLAD_GL_EXT_provoking_vertex;\ntypedef void (APIENTRYP PFNGLPROVOKINGVERTEXEXTPROC)(GLenum);\nGLAPI PFNGLPROVOKINGVERTEXEXTPROC glad_glProvokingVertexEXT;\n#define glProvokingVertexEXT glad_glProvokingVertexEXT\n#endif\n#ifndef GL_ARB_point_parameters\n#define GL_ARB_point_parameters 1\nGLAPI int GLAD_GL_ARB_point_parameters;\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC)(GLenum, GLfloat);\nGLAPI PFNGLPOINTPARAMETERFARBPROC glad_glPointParameterfARB;\n#define glPointParameterfARB glad_glPointParameterfARB\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLPOINTPARAMETERFVARBPROC glad_glPointParameterfvARB;\n#define glPointParameterfvARB glad_glPointParameterfvARB\n#endif\n#ifndef GL_ARB_shader_image_load_store\n#define GL_ARB_shader_image_load_store 1\nGLAPI int GLAD_GL_ARB_shader_image_load_store;\n#endif\n#ifndef GL_HP_occlusion_test\n#define GL_HP_occlusion_test 1\nGLAPI int GLAD_GL_HP_occlusion_test;\n#endif\n#ifndef GL_ARB_ES3_compatibility\n#define GL_ARB_ES3_compatibility 1\nGLAPI int GLAD_GL_ARB_ES3_compatibility;\n#endif\n#ifndef GL_SGIX_framezoom\n#define GL_SGIX_framezoom 1\nGLAPI int GLAD_GL_SGIX_framezoom;\ntypedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC)(GLint);\nGLAPI PFNGLFRAMEZOOMSGIXPROC glad_glFrameZoomSGIX;\n#define glFrameZoomSGIX glad_glFrameZoomSGIX\n#endif\n#ifndef GL_ARB_texture_buffer_object_rgb32\n#define GL_ARB_texture_buffer_object_rgb32 1\nGLAPI int GLAD_GL_ARB_texture_buffer_object_rgb32;\n#endif\n#ifndef GL_NV_bindless_multi_draw_indirect\n#define GL_NV_bindless_multi_draw_indirect 1\nGLAPI int GLAD_GL_NV_bindless_multi_draw_indirect;\ntypedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC)(GLenum, const void*, GLsizei, GLsizei, GLint);\nGLAPI PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC glad_glMultiDrawArraysIndirectBindlessNV;\n#define glMultiDrawArraysIndirectBindlessNV glad_glMultiDrawArraysIndirectBindlessNV\ntypedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC)(GLenum, GLenum, const void*, GLsizei, GLsizei, GLint);\nGLAPI PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC glad_glMultiDrawElementsIndirectBindlessNV;\n#define glMultiDrawElementsIndirectBindlessNV glad_glMultiDrawElementsIndirectBindlessNV\n#endif\n#ifndef GL_SGIX_texture_multi_buffer\n#define GL_SGIX_texture_multi_buffer 1\nGLAPI int GLAD_GL_SGIX_texture_multi_buffer;\n#endif\n#ifndef GL_EXT_transform_feedback\n#define GL_EXT_transform_feedback 1\nGLAPI int GLAD_GL_EXT_transform_feedback;\ntypedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKEXTPROC)(GLenum);\nGLAPI PFNGLBEGINTRANSFORMFEEDBACKEXTPROC glad_glBeginTransformFeedbackEXT;\n#define glBeginTransformFeedbackEXT glad_glBeginTransformFeedbackEXT\ntypedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKEXTPROC)();\nGLAPI PFNGLENDTRANSFORMFEEDBACKEXTPROC glad_glEndTransformFeedbackEXT;\n#define glEndTransformFeedbackEXT glad_glEndTransformFeedbackEXT\ntypedef void (APIENTRYP PFNGLBINDBUFFERRANGEEXTPROC)(GLenum, GLuint, GLuint, GLintptr, GLsizeiptr);\nGLAPI PFNGLBINDBUFFERRANGEEXTPROC glad_glBindBufferRangeEXT;\n#define glBindBufferRangeEXT glad_glBindBufferRangeEXT\ntypedef void (APIENTRYP PFNGLBINDBUFFEROFFSETEXTPROC)(GLenum, GLuint, GLuint, GLintptr);\nGLAPI PFNGLBINDBUFFEROFFSETEXTPROC glad_glBindBufferOffsetEXT;\n#define glBindBufferOffsetEXT glad_glBindBufferOffsetEXT\ntypedef void (APIENTRYP PFNGLBINDBUFFERBASEEXTPROC)(GLenum, GLuint, GLuint);\nGLAPI PFNGLBINDBUFFERBASEEXTPROC glad_glBindBufferBaseEXT;\n#define glBindBufferBaseEXT glad_glBindBufferBaseEXT\ntypedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC)(GLuint, GLsizei, const GLchar**, GLenum);\nGLAPI PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC glad_glTransformFeedbackVaryingsEXT;\n#define glTransformFeedbackVaryingsEXT glad_glTransformFeedbackVaryingsEXT\ntypedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC)(GLuint, GLuint, GLsizei, GLsizei*, GLsizei*, GLenum*, GLchar*);\nGLAPI PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC glad_glGetTransformFeedbackVaryingEXT;\n#define glGetTransformFeedbackVaryingEXT glad_glGetTransformFeedbackVaryingEXT\n#endif\n#ifndef GL_KHR_texture_compression_astc_ldr\n#define GL_KHR_texture_compression_astc_ldr 1\nGLAPI int GLAD_GL_KHR_texture_compression_astc_ldr;\n#endif\n#ifndef GL_3DFX_multisample\n#define GL_3DFX_multisample 1\nGLAPI int GLAD_GL_3DFX_multisample;\n#endif\n#ifndef GL_INTEL_fragment_shader_ordering\n#define GL_INTEL_fragment_shader_ordering 1\nGLAPI int GLAD_GL_INTEL_fragment_shader_ordering;\n#endif\n#ifndef GL_ARB_texture_env_dot3\n#define GL_ARB_texture_env_dot3 1\nGLAPI int GLAD_GL_ARB_texture_env_dot3;\n#endif\n#ifndef GL_NV_gpu_program4\n#define GL_NV_gpu_program4 1\nGLAPI int GLAD_GL_NV_gpu_program4;\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4INVPROC)(GLenum, GLuint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLPROGRAMLOCALPARAMETERI4INVPROC glad_glProgramLocalParameterI4iNV;\n#define glProgramLocalParameterI4iNV glad_glProgramLocalParameterI4iNV\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC)(GLenum, GLuint, const GLint*);\nGLAPI PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC glad_glProgramLocalParameterI4ivNV;\n#define glProgramLocalParameterI4ivNV glad_glProgramLocalParameterI4ivNV\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC)(GLenum, GLuint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC glad_glProgramLocalParametersI4ivNV;\n#define glProgramLocalParametersI4ivNV glad_glProgramLocalParametersI4ivNV\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UINVPROC)(GLenum, GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLPROGRAMLOCALPARAMETERI4UINVPROC glad_glProgramLocalParameterI4uiNV;\n#define glProgramLocalParameterI4uiNV glad_glProgramLocalParameterI4uiNV\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC)(GLenum, GLuint, const GLuint*);\nGLAPI PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC glad_glProgramLocalParameterI4uivNV;\n#define glProgramLocalParameterI4uivNV glad_glProgramLocalParameterI4uivNV\ntypedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC)(GLenum, GLuint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC glad_glProgramLocalParametersI4uivNV;\n#define glProgramLocalParametersI4uivNV glad_glProgramLocalParametersI4uivNV\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4INVPROC)(GLenum, GLuint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLPROGRAMENVPARAMETERI4INVPROC glad_glProgramEnvParameterI4iNV;\n#define glProgramEnvParameterI4iNV glad_glProgramEnvParameterI4iNV\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4IVNVPROC)(GLenum, GLuint, const GLint*);\nGLAPI PFNGLPROGRAMENVPARAMETERI4IVNVPROC glad_glProgramEnvParameterI4ivNV;\n#define glProgramEnvParameterI4ivNV glad_glProgramEnvParameterI4ivNV\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4IVNVPROC)(GLenum, GLuint, GLsizei, const GLint*);\nGLAPI PFNGLPROGRAMENVPARAMETERSI4IVNVPROC glad_glProgramEnvParametersI4ivNV;\n#define glProgramEnvParametersI4ivNV glad_glProgramEnvParametersI4ivNV\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UINVPROC)(GLenum, GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLPROGRAMENVPARAMETERI4UINVPROC glad_glProgramEnvParameterI4uiNV;\n#define glProgramEnvParameterI4uiNV glad_glProgramEnvParameterI4uiNV\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UIVNVPROC)(GLenum, GLuint, const GLuint*);\nGLAPI PFNGLPROGRAMENVPARAMETERI4UIVNVPROC glad_glProgramEnvParameterI4uivNV;\n#define glProgramEnvParameterI4uivNV glad_glProgramEnvParameterI4uivNV\ntypedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC)(GLenum, GLuint, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC glad_glProgramEnvParametersI4uivNV;\n#define glProgramEnvParametersI4uivNV glad_glProgramEnvParametersI4uivNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC)(GLenum, GLuint, GLint*);\nGLAPI PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC glad_glGetProgramLocalParameterIivNV;\n#define glGetProgramLocalParameterIivNV glad_glGetProgramLocalParameterIivNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC)(GLenum, GLuint, GLuint*);\nGLAPI PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC glad_glGetProgramLocalParameterIuivNV;\n#define glGetProgramLocalParameterIuivNV glad_glGetProgramLocalParameterIuivNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIIVNVPROC)(GLenum, GLuint, GLint*);\nGLAPI PFNGLGETPROGRAMENVPARAMETERIIVNVPROC glad_glGetProgramEnvParameterIivNV;\n#define glGetProgramEnvParameterIivNV glad_glGetProgramEnvParameterIivNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC)(GLenum, GLuint, GLuint*);\nGLAPI PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC glad_glGetProgramEnvParameterIuivNV;\n#define glGetProgramEnvParameterIuivNV glad_glGetProgramEnvParameterIuivNV\n#endif\n#ifndef GL_NV_gpu_program5\n#define GL_NV_gpu_program5 1\nGLAPI int GLAD_GL_NV_gpu_program5;\ntypedef void (APIENTRYP PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC)(GLenum, GLsizei, const GLuint*);\nGLAPI PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC glad_glProgramSubroutineParametersuivNV;\n#define glProgramSubroutineParametersuivNV glad_glProgramSubroutineParametersuivNV\ntypedef void (APIENTRYP PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC)(GLenum, GLuint, GLuint*);\nGLAPI PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC glad_glGetProgramSubroutineParameteruivNV;\n#define glGetProgramSubroutineParameteruivNV glad_glGetProgramSubroutineParameteruivNV\n#endif\n#ifndef GL_NV_float_buffer\n#define GL_NV_float_buffer 1\nGLAPI int GLAD_GL_NV_float_buffer;\n#endif\n#ifndef GL_SGIS_texture_edge_clamp\n#define GL_SGIS_texture_edge_clamp 1\nGLAPI int GLAD_GL_SGIS_texture_edge_clamp;\n#endif\n#ifndef GL_ARB_framebuffer_sRGB\n#define GL_ARB_framebuffer_sRGB 1\nGLAPI int GLAD_GL_ARB_framebuffer_sRGB;\n#endif\n#ifndef GL_SUN_slice_accum\n#define GL_SUN_slice_accum 1\nGLAPI int GLAD_GL_SUN_slice_accum;\n#endif\n#ifndef GL_EXT_index_texture\n#define GL_EXT_index_texture 1\nGLAPI int GLAD_GL_EXT_index_texture;\n#endif\n#ifndef GL_EXT_shader_image_load_formatted\n#define GL_EXT_shader_image_load_formatted 1\nGLAPI int GLAD_GL_EXT_shader_image_load_formatted;\n#endif\n#ifndef GL_ARB_geometry_shader4\n#define GL_ARB_geometry_shader4 1\nGLAPI int GLAD_GL_ARB_geometry_shader4;\ntypedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC)(GLuint, GLenum, GLint);\nGLAPI PFNGLPROGRAMPARAMETERIARBPROC glad_glProgramParameteriARB;\n#define glProgramParameteriARB glad_glProgramParameteriARB\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC)(GLenum, GLenum, GLuint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTUREARBPROC glad_glFramebufferTextureARB;\n#define glFramebufferTextureARB glad_glFramebufferTextureARB\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC)(GLenum, GLenum, GLuint, GLint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTURELAYERARBPROC glad_glFramebufferTextureLayerARB;\n#define glFramebufferTextureLayerARB glad_glFramebufferTextureLayerARB\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC)(GLenum, GLenum, GLuint, GLint, GLenum);\nGLAPI PFNGLFRAMEBUFFERTEXTUREFACEARBPROC glad_glFramebufferTextureFaceARB;\n#define glFramebufferTextureFaceARB glad_glFramebufferTextureFaceARB\n#endif\n#ifndef GL_EXT_separate_specular_color\n#define GL_EXT_separate_specular_color 1\nGLAPI int GLAD_GL_EXT_separate_specular_color;\n#endif\n#ifndef GL_AMD_depth_clamp_separate\n#define GL_AMD_depth_clamp_separate 1\nGLAPI int GLAD_GL_AMD_depth_clamp_separate;\n#endif\n#ifndef GL_SUN_convolution_border_modes\n#define GL_SUN_convolution_border_modes 1\nGLAPI int GLAD_GL_SUN_convolution_border_modes;\n#endif\n#ifndef GL_SGIX_sprite\n#define GL_SGIX_sprite 1\nGLAPI int GLAD_GL_SGIX_sprite;\ntypedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC)(GLenum, GLfloat);\nGLAPI PFNGLSPRITEPARAMETERFSGIXPROC glad_glSpriteParameterfSGIX;\n#define glSpriteParameterfSGIX glad_glSpriteParameterfSGIX\ntypedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLSPRITEPARAMETERFVSGIXPROC glad_glSpriteParameterfvSGIX;\n#define glSpriteParameterfvSGIX glad_glSpriteParameterfvSGIX\ntypedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC)(GLenum, GLint);\nGLAPI PFNGLSPRITEPARAMETERISGIXPROC glad_glSpriteParameteriSGIX;\n#define glSpriteParameteriSGIX glad_glSpriteParameteriSGIX\ntypedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC)(GLenum, const GLint*);\nGLAPI PFNGLSPRITEPARAMETERIVSGIXPROC glad_glSpriteParameterivSGIX;\n#define glSpriteParameterivSGIX glad_glSpriteParameterivSGIX\n#endif\n#ifndef GL_ARB_get_program_binary\n#define GL_ARB_get_program_binary 1\nGLAPI int GLAD_GL_ARB_get_program_binary;\n#endif\n#ifndef GL_AMD_occlusion_query_event\n#define GL_AMD_occlusion_query_event 1\nGLAPI int GLAD_GL_AMD_occlusion_query_event;\ntypedef void (APIENTRYP PFNGLQUERYOBJECTPARAMETERUIAMDPROC)(GLenum, GLuint, GLenum, GLuint);\nGLAPI PFNGLQUERYOBJECTPARAMETERUIAMDPROC glad_glQueryObjectParameteruiAMD;\n#define glQueryObjectParameteruiAMD glad_glQueryObjectParameteruiAMD\n#endif\n#ifndef GL_SGIS_multisample\n#define GL_SGIS_multisample 1\nGLAPI int GLAD_GL_SGIS_multisample;\ntypedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC)(GLclampf, GLboolean);\nGLAPI PFNGLSAMPLEMASKSGISPROC glad_glSampleMaskSGIS;\n#define glSampleMaskSGIS glad_glSampleMaskSGIS\ntypedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC)(GLenum);\nGLAPI PFNGLSAMPLEPATTERNSGISPROC glad_glSamplePatternSGIS;\n#define glSamplePatternSGIS glad_glSamplePatternSGIS\n#endif\n#ifndef GL_EXT_framebuffer_object\n#define GL_EXT_framebuffer_object 1\nGLAPI int GLAD_GL_EXT_framebuffer_object;\ntypedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC)(GLuint);\nGLAPI PFNGLISRENDERBUFFEREXTPROC glad_glIsRenderbufferEXT;\n#define glIsRenderbufferEXT glad_glIsRenderbufferEXT\ntypedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDRENDERBUFFEREXTPROC glad_glBindRenderbufferEXT;\n#define glBindRenderbufferEXT glad_glBindRenderbufferEXT\ntypedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETERENDERBUFFERSEXTPROC glad_glDeleteRenderbuffersEXT;\n#define glDeleteRenderbuffersEXT glad_glDeleteRenderbuffersEXT\ntypedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENRENDERBUFFERSEXTPROC glad_glGenRenderbuffersEXT;\n#define glGenRenderbuffersEXT glad_glGenRenderbuffersEXT\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC)(GLenum, GLenum, GLsizei, GLsizei);\nGLAPI PFNGLRENDERBUFFERSTORAGEEXTPROC glad_glRenderbufferStorageEXT;\n#define glRenderbufferStorageEXT glad_glRenderbufferStorageEXT\ntypedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glad_glGetRenderbufferParameterivEXT;\n#define glGetRenderbufferParameterivEXT glad_glGetRenderbufferParameterivEXT\ntypedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC)(GLuint);\nGLAPI PFNGLISFRAMEBUFFEREXTPROC glad_glIsFramebufferEXT;\n#define glIsFramebufferEXT glad_glIsFramebufferEXT\ntypedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC)(GLenum, GLuint);\nGLAPI PFNGLBINDFRAMEBUFFEREXTPROC glad_glBindFramebufferEXT;\n#define glBindFramebufferEXT glad_glBindFramebufferEXT\ntypedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEFRAMEBUFFERSEXTPROC glad_glDeleteFramebuffersEXT;\n#define glDeleteFramebuffersEXT glad_glDeleteFramebuffersEXT\ntypedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENFRAMEBUFFERSEXTPROC glad_glGenFramebuffersEXT;\n#define glGenFramebuffersEXT glad_glGenFramebuffersEXT\ntypedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)(GLenum);\nGLAPI PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glad_glCheckFramebufferStatusEXT;\n#define glCheckFramebufferStatusEXT glad_glCheckFramebufferStatusEXT\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTURE1DEXTPROC glad_glFramebufferTexture1DEXT;\n#define glFramebufferTexture1DEXT glad_glFramebufferTexture1DEXT\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTURE2DEXTPROC glad_glFramebufferTexture2DEXT;\n#define glFramebufferTexture2DEXT glad_glFramebufferTexture2DEXT\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLint);\nGLAPI PFNGLFRAMEBUFFERTEXTURE3DEXTPROC glad_glFramebufferTexture3DEXT;\n#define glFramebufferTexture3DEXT glad_glFramebufferTexture3DEXT\ntypedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)(GLenum, GLenum, GLenum, GLuint);\nGLAPI PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glad_glFramebufferRenderbufferEXT;\n#define glFramebufferRenderbufferEXT glad_glFramebufferRenderbufferEXT\ntypedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)(GLenum, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glad_glGetFramebufferAttachmentParameterivEXT;\n#define glGetFramebufferAttachmentParameterivEXT glad_glGetFramebufferAttachmentParameterivEXT\ntypedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC)(GLenum);\nGLAPI PFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT;\n#define glGenerateMipmapEXT glad_glGenerateMipmapEXT\n#endif\n#ifndef GL_ARB_robustness_isolation\n#define GL_ARB_robustness_isolation 1\nGLAPI int GLAD_GL_ARB_robustness_isolation;\n#endif\n#ifndef GL_ARB_vertex_array_bgra\n#define GL_ARB_vertex_array_bgra 1\nGLAPI int GLAD_GL_ARB_vertex_array_bgra;\n#endif\n#ifndef GL_APPLE_vertex_array_range\n#define GL_APPLE_vertex_array_range 1\nGLAPI int GLAD_GL_APPLE_vertex_array_range;\ntypedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC)(GLsizei, void*);\nGLAPI PFNGLVERTEXARRAYRANGEAPPLEPROC glad_glVertexArrayRangeAPPLE;\n#define glVertexArrayRangeAPPLE glad_glVertexArrayRangeAPPLE\ntypedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC)(GLsizei, void*);\nGLAPI PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC glad_glFlushVertexArrayRangeAPPLE;\n#define glFlushVertexArrayRangeAPPLE glad_glFlushVertexArrayRangeAPPLE\ntypedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC)(GLenum, GLint);\nGLAPI PFNGLVERTEXARRAYPARAMETERIAPPLEPROC glad_glVertexArrayParameteriAPPLE;\n#define glVertexArrayParameteriAPPLE glad_glVertexArrayParameteriAPPLE\n#endif\n#ifndef GL_AMD_query_buffer_object\n#define GL_AMD_query_buffer_object 1\nGLAPI int GLAD_GL_AMD_query_buffer_object;\n#endif\n#ifndef GL_NV_register_combiners\n#define GL_NV_register_combiners 1\nGLAPI int GLAD_GL_NV_register_combiners;\ntypedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLCOMBINERPARAMETERFVNVPROC glad_glCombinerParameterfvNV;\n#define glCombinerParameterfvNV glad_glCombinerParameterfvNV\ntypedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC)(GLenum, GLfloat);\nGLAPI PFNGLCOMBINERPARAMETERFNVPROC glad_glCombinerParameterfNV;\n#define glCombinerParameterfNV glad_glCombinerParameterfNV\ntypedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC)(GLenum, const GLint*);\nGLAPI PFNGLCOMBINERPARAMETERIVNVPROC glad_glCombinerParameterivNV;\n#define glCombinerParameterivNV glad_glCombinerParameterivNV\ntypedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC)(GLenum, GLint);\nGLAPI PFNGLCOMBINERPARAMETERINVPROC glad_glCombinerParameteriNV;\n#define glCombinerParameteriNV glad_glCombinerParameteriNV\ntypedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC)(GLenum, GLenum, GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLCOMBINERINPUTNVPROC glad_glCombinerInputNV;\n#define glCombinerInputNV glad_glCombinerInputNV\ntypedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC)(GLenum, GLenum, GLenum, GLenum, GLenum, GLenum, GLenum, GLboolean, GLboolean, GLboolean);\nGLAPI PFNGLCOMBINEROUTPUTNVPROC glad_glCombinerOutputNV;\n#define glCombinerOutputNV glad_glCombinerOutputNV\ntypedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC)(GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLFINALCOMBINERINPUTNVPROC glad_glFinalCombinerInputNV;\n#define glFinalCombinerInputNV glad_glFinalCombinerInputNV\ntypedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC)(GLenum, GLenum, GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC glad_glGetCombinerInputParameterfvNV;\n#define glGetCombinerInputParameterfvNV glad_glGetCombinerInputParameterfvNV\ntypedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC)(GLenum, GLenum, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC glad_glGetCombinerInputParameterivNV;\n#define glGetCombinerInputParameterivNV glad_glGetCombinerInputParameterivNV\ntypedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC)(GLenum, GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC glad_glGetCombinerOutputParameterfvNV;\n#define glGetCombinerOutputParameterfvNV glad_glGetCombinerOutputParameterfvNV\ntypedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC)(GLenum, GLenum, GLenum, GLint*);\nGLAPI PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC glad_glGetCombinerOutputParameterivNV;\n#define glGetCombinerOutputParameterivNV glad_glGetCombinerOutputParameterivNV\ntypedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC glad_glGetFinalCombinerInputParameterfvNV;\n#define glGetFinalCombinerInputParameterfvNV glad_glGetFinalCombinerInputParameterfvNV\ntypedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC glad_glGetFinalCombinerInputParameterivNV;\n#define glGetFinalCombinerInputParameterivNV glad_glGetFinalCombinerInputParameterivNV\n#endif\n#ifndef GL_ARB_draw_buffers\n#define GL_ARB_draw_buffers 1\nGLAPI int GLAD_GL_ARB_draw_buffers;\ntypedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC)(GLsizei, const GLenum*);\nGLAPI PFNGLDRAWBUFFERSARBPROC glad_glDrawBuffersARB;\n#define glDrawBuffersARB glad_glDrawBuffersARB\n#endif\n#ifndef GL_ARB_clear_texture\n#define GL_ARB_clear_texture 1\nGLAPI int GLAD_GL_ARB_clear_texture;\n#endif\n#ifndef GL_ARB_debug_output\n#define GL_ARB_debug_output 1\nGLAPI int GLAD_GL_ARB_debug_output;\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC)(GLenum, GLenum, GLenum, GLsizei, const GLuint*, GLboolean);\nGLAPI PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB;\n#define glDebugMessageControlARB glad_glDebugMessageControlARB\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC)(GLenum, GLenum, GLuint, GLenum, GLsizei, const GLchar*);\nGLAPI PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB;\n#define glDebugMessageInsertARB glad_glDebugMessageInsertARB\ntypedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC)(GLDEBUGPROCARB, const void*);\nGLAPI PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB;\n#define glDebugMessageCallbackARB glad_glDebugMessageCallbackARB\ntypedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC)(GLuint, GLsizei, GLenum*, GLenum*, GLuint*, GLenum*, GLsizei*, GLchar*);\nGLAPI PFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB;\n#define glGetDebugMessageLogARB glad_glGetDebugMessageLogARB\n#endif\n#ifndef GL_SGI_color_matrix\n#define GL_SGI_color_matrix 1\nGLAPI int GLAD_GL_SGI_color_matrix;\n#endif\n#ifndef GL_EXT_cull_vertex\n#define GL_EXT_cull_vertex 1\nGLAPI int GLAD_GL_EXT_cull_vertex;\ntypedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC)(GLenum, GLdouble*);\nGLAPI PFNGLCULLPARAMETERDVEXTPROC glad_glCullParameterdvEXT;\n#define glCullParameterdvEXT glad_glCullParameterdvEXT\ntypedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC)(GLenum, GLfloat*);\nGLAPI PFNGLCULLPARAMETERFVEXTPROC glad_glCullParameterfvEXT;\n#define glCullParameterfvEXT glad_glCullParameterfvEXT\n#endif\n#ifndef GL_EXT_texture_sRGB\n#define GL_EXT_texture_sRGB 1\nGLAPI int GLAD_GL_EXT_texture_sRGB;\n#endif\n#ifndef GL_APPLE_row_bytes\n#define GL_APPLE_row_bytes 1\nGLAPI int GLAD_GL_APPLE_row_bytes;\n#endif\n#ifndef GL_NV_texgen_reflection\n#define GL_NV_texgen_reflection 1\nGLAPI int GLAD_GL_NV_texgen_reflection;\n#endif\n#ifndef GL_IBM_multimode_draw_arrays\n#define GL_IBM_multimode_draw_arrays 1\nGLAPI int GLAD_GL_IBM_multimode_draw_arrays;\ntypedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC)(const GLenum*, const GLint*, const GLsizei*, GLsizei, GLint);\nGLAPI PFNGLMULTIMODEDRAWARRAYSIBMPROC glad_glMultiModeDrawArraysIBM;\n#define glMultiModeDrawArraysIBM glad_glMultiModeDrawArraysIBM\ntypedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC)(const GLenum*, const GLsizei*, GLenum, const void**, GLsizei, GLint);\nGLAPI PFNGLMULTIMODEDRAWELEMENTSIBMPROC glad_glMultiModeDrawElementsIBM;\n#define glMultiModeDrawElementsIBM glad_glMultiModeDrawElementsIBM\n#endif\n#ifndef GL_APPLE_vertex_array_object\n#define GL_APPLE_vertex_array_object 1\nGLAPI int GLAD_GL_APPLE_vertex_array_object;\ntypedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC)(GLuint);\nGLAPI PFNGLBINDVERTEXARRAYAPPLEPROC glad_glBindVertexArrayAPPLE;\n#define glBindVertexArrayAPPLE glad_glBindVertexArrayAPPLE\ntypedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC)(GLsizei, const GLuint*);\nGLAPI PFNGLDELETEVERTEXARRAYSAPPLEPROC glad_glDeleteVertexArraysAPPLE;\n#define glDeleteVertexArraysAPPLE glad_glDeleteVertexArraysAPPLE\ntypedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC)(GLsizei, GLuint*);\nGLAPI PFNGLGENVERTEXARRAYSAPPLEPROC glad_glGenVertexArraysAPPLE;\n#define glGenVertexArraysAPPLE glad_glGenVertexArraysAPPLE\ntypedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC)(GLuint);\nGLAPI PFNGLISVERTEXARRAYAPPLEPROC glad_glIsVertexArrayAPPLE;\n#define glIsVertexArrayAPPLE glad_glIsVertexArrayAPPLE\n#endif\n#ifndef GL_3DFX_texture_compression_FXT1\n#define GL_3DFX_texture_compression_FXT1 1\nGLAPI int GLAD_GL_3DFX_texture_compression_FXT1;\n#endif\n#ifndef GL_SGIX_ycrcb\n#define GL_SGIX_ycrcb 1\nGLAPI int GLAD_GL_SGIX_ycrcb;\n#endif\n#ifndef GL_AMD_conservative_depth\n#define GL_AMD_conservative_depth 1\nGLAPI int GLAD_GL_AMD_conservative_depth;\n#endif\n#ifndef GL_ARB_texture_float\n#define GL_ARB_texture_float 1\nGLAPI int GLAD_GL_ARB_texture_float;\n#endif\n#ifndef GL_ARB_compressed_texture_pixel_storage\n#define GL_ARB_compressed_texture_pixel_storage 1\nGLAPI int GLAD_GL_ARB_compressed_texture_pixel_storage;\n#endif\n#ifndef GL_SGIS_detail_texture\n#define GL_SGIS_detail_texture 1\nGLAPI int GLAD_GL_SGIS_detail_texture;\ntypedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC)(GLenum, GLsizei, const GLfloat*);\nGLAPI PFNGLDETAILTEXFUNCSGISPROC glad_glDetailTexFuncSGIS;\n#define glDetailTexFuncSGIS glad_glDetailTexFuncSGIS\ntypedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC)(GLenum, GLfloat*);\nGLAPI PFNGLGETDETAILTEXFUNCSGISPROC glad_glGetDetailTexFuncSGIS;\n#define glGetDetailTexFuncSGIS glad_glGetDetailTexFuncSGIS\n#endif\n#ifndef GL_ARB_draw_instanced\n#define GL_ARB_draw_instanced 1\nGLAPI int GLAD_GL_ARB_draw_instanced;\ntypedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC)(GLenum, GLint, GLsizei, GLsizei);\nGLAPI PFNGLDRAWARRAYSINSTANCEDARBPROC glad_glDrawArraysInstancedARB;\n#define glDrawArraysInstancedARB glad_glDrawArraysInstancedARB\ntypedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC)(GLenum, GLsizei, GLenum, const void*, GLsizei);\nGLAPI PFNGLDRAWELEMENTSINSTANCEDARBPROC glad_glDrawElementsInstancedARB;\n#define glDrawElementsInstancedARB glad_glDrawElementsInstancedARB\n#endif\n#ifndef GL_OES_read_format\n#define GL_OES_read_format 1\nGLAPI int GLAD_GL_OES_read_format;\n#endif\n#ifndef GL_ATI_texture_float\n#define GL_ATI_texture_float 1\nGLAPI int GLAD_GL_ATI_texture_float;\n#endif\n#ifndef GL_ARB_texture_gather\n#define GL_ARB_texture_gather 1\nGLAPI int GLAD_GL_ARB_texture_gather;\n#endif\n#ifndef GL_AMD_vertex_shader_layer\n#define GL_AMD_vertex_shader_layer 1\nGLAPI int GLAD_GL_AMD_vertex_shader_layer;\n#endif\n#ifndef GL_ARB_shading_language_include\n#define GL_ARB_shading_language_include 1\nGLAPI int GLAD_GL_ARB_shading_language_include;\ntypedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC)(GLenum, GLint, const GLchar*, GLint, const GLchar*);\nGLAPI PFNGLNAMEDSTRINGARBPROC glad_glNamedStringARB;\n#define glNamedStringARB glad_glNamedStringARB\ntypedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC)(GLint, const GLchar*);\nGLAPI PFNGLDELETENAMEDSTRINGARBPROC glad_glDeleteNamedStringARB;\n#define glDeleteNamedStringARB glad_glDeleteNamedStringARB\ntypedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC)(GLuint, GLsizei, const GLchar**, const GLint*);\nGLAPI PFNGLCOMPILESHADERINCLUDEARBPROC glad_glCompileShaderIncludeARB;\n#define glCompileShaderIncludeARB glad_glCompileShaderIncludeARB\ntypedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC)(GLint, const GLchar*);\nGLAPI PFNGLISNAMEDSTRINGARBPROC glad_glIsNamedStringARB;\n#define glIsNamedStringARB glad_glIsNamedStringARB\ntypedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC)(GLint, const GLchar*, GLsizei, GLint*, GLchar*);\nGLAPI PFNGLGETNAMEDSTRINGARBPROC glad_glGetNamedStringARB;\n#define glGetNamedStringARB glad_glGetNamedStringARB\ntypedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC)(GLint, const GLchar*, GLenum, GLint*);\nGLAPI PFNGLGETNAMEDSTRINGIVARBPROC glad_glGetNamedStringivARB;\n#define glGetNamedStringivARB glad_glGetNamedStringivARB\n#endif\n#ifndef GL_APPLE_client_storage\n#define GL_APPLE_client_storage 1\nGLAPI int GLAD_GL_APPLE_client_storage;\n#endif\n#ifndef GL_WIN_phong_shading\n#define GL_WIN_phong_shading 1\nGLAPI int GLAD_GL_WIN_phong_shading;\n#endif\n#ifndef GL_INGR_blend_func_separate\n#define GL_INGR_blend_func_separate 1\nGLAPI int GLAD_GL_INGR_blend_func_separate;\ntypedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC)(GLenum, GLenum, GLenum, GLenum);\nGLAPI PFNGLBLENDFUNCSEPARATEINGRPROC glad_glBlendFuncSeparateINGR;\n#define glBlendFuncSeparateINGR glad_glBlendFuncSeparateINGR\n#endif\n#ifndef GL_NV_path_rendering\n#define GL_NV_path_rendering 1\nGLAPI int GLAD_GL_NV_path_rendering;\ntypedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC)(GLsizei);\nGLAPI PFNGLGENPATHSNVPROC glad_glGenPathsNV;\n#define glGenPathsNV glad_glGenPathsNV\ntypedef void (APIENTRYP PFNGLDELETEPATHSNVPROC)(GLuint, GLsizei);\nGLAPI PFNGLDELETEPATHSNVPROC glad_glDeletePathsNV;\n#define glDeletePathsNV glad_glDeletePathsNV\ntypedef GLboolean (APIENTRYP PFNGLISPATHNVPROC)(GLuint);\nGLAPI PFNGLISPATHNVPROC glad_glIsPathNV;\n#define glIsPathNV glad_glIsPathNV\ntypedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC)(GLuint, GLsizei, const GLubyte*, GLsizei, GLenum, const void*);\nGLAPI PFNGLPATHCOMMANDSNVPROC glad_glPathCommandsNV;\n#define glPathCommandsNV glad_glPathCommandsNV\ntypedef void (APIENTRYP PFNGLPATHCOORDSNVPROC)(GLuint, GLsizei, GLenum, const void*);\nGLAPI PFNGLPATHCOORDSNVPROC glad_glPathCoordsNV;\n#define glPathCoordsNV glad_glPathCoordsNV\ntypedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC)(GLuint, GLsizei, GLsizei, GLsizei, const GLubyte*, GLsizei, GLenum, const void*);\nGLAPI PFNGLPATHSUBCOMMANDSNVPROC glad_glPathSubCommandsNV;\n#define glPathSubCommandsNV glad_glPathSubCommandsNV\ntypedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC)(GLuint, GLsizei, GLsizei, GLenum, const void*);\nGLAPI PFNGLPATHSUBCOORDSNVPROC glad_glPathSubCoordsNV;\n#define glPathSubCoordsNV glad_glPathSubCoordsNV\ntypedef void (APIENTRYP PFNGLPATHSTRINGNVPROC)(GLuint, GLenum, GLsizei, const void*);\nGLAPI PFNGLPATHSTRINGNVPROC glad_glPathStringNV;\n#define glPathStringNV glad_glPathStringNV\ntypedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC)(GLuint, GLenum, const void*, GLbitfield, GLsizei, GLenum, const void*, GLenum, GLuint, GLfloat);\nGLAPI PFNGLPATHGLYPHSNVPROC glad_glPathGlyphsNV;\n#define glPathGlyphsNV glad_glPathGlyphsNV\ntypedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC)(GLuint, GLenum, const void*, GLbitfield, GLuint, GLsizei, GLenum, GLuint, GLfloat);\nGLAPI PFNGLPATHGLYPHRANGENVPROC glad_glPathGlyphRangeNV;\n#define glPathGlyphRangeNV glad_glPathGlyphRangeNV\ntypedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC)(GLuint, GLsizei, const GLuint*, const GLfloat*);\nGLAPI PFNGLWEIGHTPATHSNVPROC glad_glWeightPathsNV;\n#define glWeightPathsNV glad_glWeightPathsNV\ntypedef void (APIENTRYP PFNGLCOPYPATHNVPROC)(GLuint, GLuint);\nGLAPI PFNGLCOPYPATHNVPROC glad_glCopyPathNV;\n#define glCopyPathNV glad_glCopyPathNV\ntypedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC)(GLuint, GLuint, GLuint, GLfloat);\nGLAPI PFNGLINTERPOLATEPATHSNVPROC glad_glInterpolatePathsNV;\n#define glInterpolatePathsNV glad_glInterpolatePathsNV\ntypedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC)(GLuint, GLuint, GLenum, const GLfloat*);\nGLAPI PFNGLTRANSFORMPATHNVPROC glad_glTransformPathNV;\n#define glTransformPathNV glad_glTransformPathNV\ntypedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC)(GLuint, GLenum, const GLint*);\nGLAPI PFNGLPATHPARAMETERIVNVPROC glad_glPathParameterivNV;\n#define glPathParameterivNV glad_glPathParameterivNV\ntypedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC)(GLuint, GLenum, GLint);\nGLAPI PFNGLPATHPARAMETERINVPROC glad_glPathParameteriNV;\n#define glPathParameteriNV glad_glPathParameteriNV\ntypedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC)(GLuint, GLenum, const GLfloat*);\nGLAPI PFNGLPATHPARAMETERFVNVPROC glad_glPathParameterfvNV;\n#define glPathParameterfvNV glad_glPathParameterfvNV\ntypedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC)(GLuint, GLenum, GLfloat);\nGLAPI PFNGLPATHPARAMETERFNVPROC glad_glPathParameterfNV;\n#define glPathParameterfNV glad_glPathParameterfNV\ntypedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC)(GLuint, GLsizei, const GLfloat*);\nGLAPI PFNGLPATHDASHARRAYNVPROC glad_glPathDashArrayNV;\n#define glPathDashArrayNV glad_glPathDashArrayNV\ntypedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC)(GLenum, GLint, GLuint);\nGLAPI PFNGLPATHSTENCILFUNCNVPROC glad_glPathStencilFuncNV;\n#define glPathStencilFuncNV glad_glPathStencilFuncNV\ntypedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC)(GLfloat, GLfloat);\nGLAPI PFNGLPATHSTENCILDEPTHOFFSETNVPROC glad_glPathStencilDepthOffsetNV;\n#define glPathStencilDepthOffsetNV glad_glPathStencilDepthOffsetNV\ntypedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC)(GLuint, GLenum, GLuint);\nGLAPI PFNGLSTENCILFILLPATHNVPROC glad_glStencilFillPathNV;\n#define glStencilFillPathNV glad_glStencilFillPathNV\ntypedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC)(GLuint, GLint, GLuint);\nGLAPI PFNGLSTENCILSTROKEPATHNVPROC glad_glStencilStrokePathNV;\n#define glStencilStrokePathNV glad_glStencilStrokePathNV\ntypedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC)(GLsizei, GLenum, const void*, GLuint, GLenum, GLuint, GLenum, const GLfloat*);\nGLAPI PFNGLSTENCILFILLPATHINSTANCEDNVPROC glad_glStencilFillPathInstancedNV;\n#define glStencilFillPathInstancedNV glad_glStencilFillPathInstancedNV\ntypedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC)(GLsizei, GLenum, const void*, GLuint, GLint, GLuint, GLenum, const GLfloat*);\nGLAPI PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC glad_glStencilStrokePathInstancedNV;\n#define glStencilStrokePathInstancedNV glad_glStencilStrokePathInstancedNV\ntypedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC)(GLenum);\nGLAPI PFNGLPATHCOVERDEPTHFUNCNVPROC glad_glPathCoverDepthFuncNV;\n#define glPathCoverDepthFuncNV glad_glPathCoverDepthFuncNV\ntypedef void (APIENTRYP PFNGLPATHCOLORGENNVPROC)(GLenum, GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLPATHCOLORGENNVPROC glad_glPathColorGenNV;\n#define glPathColorGenNV glad_glPathColorGenNV\ntypedef void (APIENTRYP PFNGLPATHTEXGENNVPROC)(GLenum, GLenum, GLint, const GLfloat*);\nGLAPI PFNGLPATHTEXGENNVPROC glad_glPathTexGenNV;\n#define glPathTexGenNV glad_glPathTexGenNV\ntypedef void (APIENTRYP PFNGLPATHFOGGENNVPROC)(GLenum);\nGLAPI PFNGLPATHFOGGENNVPROC glad_glPathFogGenNV;\n#define glPathFogGenNV glad_glPathFogGenNV\ntypedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC)(GLuint, GLenum);\nGLAPI PFNGLCOVERFILLPATHNVPROC glad_glCoverFillPathNV;\n#define glCoverFillPathNV glad_glCoverFillPathNV\ntypedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC)(GLuint, GLenum);\nGLAPI PFNGLCOVERSTROKEPATHNVPROC glad_glCoverStrokePathNV;\n#define glCoverStrokePathNV glad_glCoverStrokePathNV\ntypedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC)(GLsizei, GLenum, const void*, GLuint, GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLCOVERFILLPATHINSTANCEDNVPROC glad_glCoverFillPathInstancedNV;\n#define glCoverFillPathInstancedNV glad_glCoverFillPathInstancedNV\ntypedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC)(GLsizei, GLenum, const void*, GLuint, GLenum, GLenum, const GLfloat*);\nGLAPI PFNGLCOVERSTROKEPATHINSTANCEDNVPROC glad_glCoverStrokePathInstancedNV;\n#define glCoverStrokePathInstancedNV glad_glCoverStrokePathInstancedNV\ntypedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETPATHPARAMETERIVNVPROC glad_glGetPathParameterivNV;\n#define glGetPathParameterivNV glad_glGetPathParameterivNV\ntypedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETPATHPARAMETERFVNVPROC glad_glGetPathParameterfvNV;\n#define glGetPathParameterfvNV glad_glGetPathParameterfvNV\ntypedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC)(GLuint, GLubyte*);\nGLAPI PFNGLGETPATHCOMMANDSNVPROC glad_glGetPathCommandsNV;\n#define glGetPathCommandsNV glad_glGetPathCommandsNV\ntypedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC)(GLuint, GLfloat*);\nGLAPI PFNGLGETPATHCOORDSNVPROC glad_glGetPathCoordsNV;\n#define glGetPathCoordsNV glad_glGetPathCoordsNV\ntypedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC)(GLuint, GLfloat*);\nGLAPI PFNGLGETPATHDASHARRAYNVPROC glad_glGetPathDashArrayNV;\n#define glGetPathDashArrayNV glad_glGetPathDashArrayNV\ntypedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC)(GLbitfield, GLsizei, GLenum, const void*, GLuint, GLsizei, GLfloat*);\nGLAPI PFNGLGETPATHMETRICSNVPROC glad_glGetPathMetricsNV;\n#define glGetPathMetricsNV glad_glGetPathMetricsNV\ntypedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC)(GLbitfield, GLuint, GLsizei, GLsizei, GLfloat*);\nGLAPI PFNGLGETPATHMETRICRANGENVPROC glad_glGetPathMetricRangeNV;\n#define glGetPathMetricRangeNV glad_glGetPathMetricRangeNV\ntypedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC)(GLenum, GLsizei, GLenum, const void*, GLuint, GLfloat, GLfloat, GLenum, GLfloat*);\nGLAPI PFNGLGETPATHSPACINGNVPROC glad_glGetPathSpacingNV;\n#define glGetPathSpacingNV glad_glGetPathSpacingNV\ntypedef void (APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETPATHCOLORGENIVNVPROC glad_glGetPathColorGenivNV;\n#define glGetPathColorGenivNV glad_glGetPathColorGenivNV\ntypedef void (APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETPATHCOLORGENFVNVPROC glad_glGetPathColorGenfvNV;\n#define glGetPathColorGenfvNV glad_glGetPathColorGenfvNV\ntypedef void (APIENTRYP PFNGLGETPATHTEXGENIVNVPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETPATHTEXGENIVNVPROC glad_glGetPathTexGenivNV;\n#define glGetPathTexGenivNV glad_glGetPathTexGenivNV\ntypedef void (APIENTRYP PFNGLGETPATHTEXGENFVNVPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETPATHTEXGENFVNVPROC glad_glGetPathTexGenfvNV;\n#define glGetPathTexGenfvNV glad_glGetPathTexGenfvNV\ntypedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC)(GLuint, GLuint, GLfloat, GLfloat);\nGLAPI PFNGLISPOINTINFILLPATHNVPROC glad_glIsPointInFillPathNV;\n#define glIsPointInFillPathNV glad_glIsPointInFillPathNV\ntypedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC)(GLuint, GLfloat, GLfloat);\nGLAPI PFNGLISPOINTINSTROKEPATHNVPROC glad_glIsPointInStrokePathNV;\n#define glIsPointInStrokePathNV glad_glIsPointInStrokePathNV\ntypedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC)(GLuint, GLsizei, GLsizei);\nGLAPI PFNGLGETPATHLENGTHNVPROC glad_glGetPathLengthNV;\n#define glGetPathLengthNV glad_glGetPathLengthNV\ntypedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC)(GLuint, GLsizei, GLsizei, GLfloat, GLfloat*, GLfloat*, GLfloat*, GLfloat*);\nGLAPI PFNGLPOINTALONGPATHNVPROC glad_glPointAlongPathNV;\n#define glPointAlongPathNV glad_glPointAlongPathNV\n#endif\n#ifndef GL_ATI_vertex_streams\n#define GL_ATI_vertex_streams 1\nGLAPI int GLAD_GL_ATI_vertex_streams;\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC)(GLenum, GLshort);\nGLAPI PFNGLVERTEXSTREAM1SATIPROC glad_glVertexStream1sATI;\n#define glVertexStream1sATI glad_glVertexStream1sATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC)(GLenum, const GLshort*);\nGLAPI PFNGLVERTEXSTREAM1SVATIPROC glad_glVertexStream1svATI;\n#define glVertexStream1svATI glad_glVertexStream1svATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC)(GLenum, GLint);\nGLAPI PFNGLVERTEXSTREAM1IATIPROC glad_glVertexStream1iATI;\n#define glVertexStream1iATI glad_glVertexStream1iATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC)(GLenum, const GLint*);\nGLAPI PFNGLVERTEXSTREAM1IVATIPROC glad_glVertexStream1ivATI;\n#define glVertexStream1ivATI glad_glVertexStream1ivATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC)(GLenum, GLfloat);\nGLAPI PFNGLVERTEXSTREAM1FATIPROC glad_glVertexStream1fATI;\n#define glVertexStream1fATI glad_glVertexStream1fATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLVERTEXSTREAM1FVATIPROC glad_glVertexStream1fvATI;\n#define glVertexStream1fvATI glad_glVertexStream1fvATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC)(GLenum, GLdouble);\nGLAPI PFNGLVERTEXSTREAM1DATIPROC glad_glVertexStream1dATI;\n#define glVertexStream1dATI glad_glVertexStream1dATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLVERTEXSTREAM1DVATIPROC glad_glVertexStream1dvATI;\n#define glVertexStream1dvATI glad_glVertexStream1dvATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC)(GLenum, GLshort, GLshort);\nGLAPI PFNGLVERTEXSTREAM2SATIPROC glad_glVertexStream2sATI;\n#define glVertexStream2sATI glad_glVertexStream2sATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC)(GLenum, const GLshort*);\nGLAPI PFNGLVERTEXSTREAM2SVATIPROC glad_glVertexStream2svATI;\n#define glVertexStream2svATI glad_glVertexStream2svATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC)(GLenum, GLint, GLint);\nGLAPI PFNGLVERTEXSTREAM2IATIPROC glad_glVertexStream2iATI;\n#define glVertexStream2iATI glad_glVertexStream2iATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC)(GLenum, const GLint*);\nGLAPI PFNGLVERTEXSTREAM2IVATIPROC glad_glVertexStream2ivATI;\n#define glVertexStream2ivATI glad_glVertexStream2ivATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC)(GLenum, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXSTREAM2FATIPROC glad_glVertexStream2fATI;\n#define glVertexStream2fATI glad_glVertexStream2fATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLVERTEXSTREAM2FVATIPROC glad_glVertexStream2fvATI;\n#define glVertexStream2fvATI glad_glVertexStream2fvATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC)(GLenum, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXSTREAM2DATIPROC glad_glVertexStream2dATI;\n#define glVertexStream2dATI glad_glVertexStream2dATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLVERTEXSTREAM2DVATIPROC glad_glVertexStream2dvATI;\n#define glVertexStream2dvATI glad_glVertexStream2dvATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC)(GLenum, GLshort, GLshort, GLshort);\nGLAPI PFNGLVERTEXSTREAM3SATIPROC glad_glVertexStream3sATI;\n#define glVertexStream3sATI glad_glVertexStream3sATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC)(GLenum, const GLshort*);\nGLAPI PFNGLVERTEXSTREAM3SVATIPROC glad_glVertexStream3svATI;\n#define glVertexStream3svATI glad_glVertexStream3svATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC)(GLenum, GLint, GLint, GLint);\nGLAPI PFNGLVERTEXSTREAM3IATIPROC glad_glVertexStream3iATI;\n#define glVertexStream3iATI glad_glVertexStream3iATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC)(GLenum, const GLint*);\nGLAPI PFNGLVERTEXSTREAM3IVATIPROC glad_glVertexStream3ivATI;\n#define glVertexStream3ivATI glad_glVertexStream3ivATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC)(GLenum, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXSTREAM3FATIPROC glad_glVertexStream3fATI;\n#define glVertexStream3fATI glad_glVertexStream3fATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLVERTEXSTREAM3FVATIPROC glad_glVertexStream3fvATI;\n#define glVertexStream3fvATI glad_glVertexStream3fvATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC)(GLenum, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXSTREAM3DATIPROC glad_glVertexStream3dATI;\n#define glVertexStream3dATI glad_glVertexStream3dATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLVERTEXSTREAM3DVATIPROC glad_glVertexStream3dvATI;\n#define glVertexStream3dvATI glad_glVertexStream3dvATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC)(GLenum, GLshort, GLshort, GLshort, GLshort);\nGLAPI PFNGLVERTEXSTREAM4SATIPROC glad_glVertexStream4sATI;\n#define glVertexStream4sATI glad_glVertexStream4sATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC)(GLenum, const GLshort*);\nGLAPI PFNGLVERTEXSTREAM4SVATIPROC glad_glVertexStream4svATI;\n#define glVertexStream4svATI glad_glVertexStream4svATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC)(GLenum, GLint, GLint, GLint, GLint);\nGLAPI PFNGLVERTEXSTREAM4IATIPROC glad_glVertexStream4iATI;\n#define glVertexStream4iATI glad_glVertexStream4iATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC)(GLenum, const GLint*);\nGLAPI PFNGLVERTEXSTREAM4IVATIPROC glad_glVertexStream4ivATI;\n#define glVertexStream4ivATI glad_glVertexStream4ivATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC)(GLenum, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLVERTEXSTREAM4FATIPROC glad_glVertexStream4fATI;\n#define glVertexStream4fATI glad_glVertexStream4fATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLVERTEXSTREAM4FVATIPROC glad_glVertexStream4fvATI;\n#define glVertexStream4fvATI glad_glVertexStream4fvATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC)(GLenum, GLdouble, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLVERTEXSTREAM4DATIPROC glad_glVertexStream4dATI;\n#define glVertexStream4dATI glad_glVertexStream4dATI\ntypedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLVERTEXSTREAM4DVATIPROC glad_glVertexStream4dvATI;\n#define glVertexStream4dvATI glad_glVertexStream4dvATI\ntypedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC)(GLenum, GLbyte, GLbyte, GLbyte);\nGLAPI PFNGLNORMALSTREAM3BATIPROC glad_glNormalStream3bATI;\n#define glNormalStream3bATI glad_glNormalStream3bATI\ntypedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC)(GLenum, const GLbyte*);\nGLAPI PFNGLNORMALSTREAM3BVATIPROC glad_glNormalStream3bvATI;\n#define glNormalStream3bvATI glad_glNormalStream3bvATI\ntypedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC)(GLenum, GLshort, GLshort, GLshort);\nGLAPI PFNGLNORMALSTREAM3SATIPROC glad_glNormalStream3sATI;\n#define glNormalStream3sATI glad_glNormalStream3sATI\ntypedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC)(GLenum, const GLshort*);\nGLAPI PFNGLNORMALSTREAM3SVATIPROC glad_glNormalStream3svATI;\n#define glNormalStream3svATI glad_glNormalStream3svATI\ntypedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC)(GLenum, GLint, GLint, GLint);\nGLAPI PFNGLNORMALSTREAM3IATIPROC glad_glNormalStream3iATI;\n#define glNormalStream3iATI glad_glNormalStream3iATI\ntypedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC)(GLenum, const GLint*);\nGLAPI PFNGLNORMALSTREAM3IVATIPROC glad_glNormalStream3ivATI;\n#define glNormalStream3ivATI glad_glNormalStream3ivATI\ntypedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC)(GLenum, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLNORMALSTREAM3FATIPROC glad_glNormalStream3fATI;\n#define glNormalStream3fATI glad_glNormalStream3fATI\ntypedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC)(GLenum, const GLfloat*);\nGLAPI PFNGLNORMALSTREAM3FVATIPROC glad_glNormalStream3fvATI;\n#define glNormalStream3fvATI glad_glNormalStream3fvATI\ntypedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC)(GLenum, GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLNORMALSTREAM3DATIPROC glad_glNormalStream3dATI;\n#define glNormalStream3dATI glad_glNormalStream3dATI\ntypedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC)(GLenum, const GLdouble*);\nGLAPI PFNGLNORMALSTREAM3DVATIPROC glad_glNormalStream3dvATI;\n#define glNormalStream3dvATI glad_glNormalStream3dvATI\ntypedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC)(GLenum);\nGLAPI PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC glad_glClientActiveVertexStreamATI;\n#define glClientActiveVertexStreamATI glad_glClientActiveVertexStreamATI\ntypedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC)(GLenum, GLint);\nGLAPI PFNGLVERTEXBLENDENVIATIPROC glad_glVertexBlendEnviATI;\n#define glVertexBlendEnviATI glad_glVertexBlendEnviATI\ntypedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC)(GLenum, GLfloat);\nGLAPI PFNGLVERTEXBLENDENVFATIPROC glad_glVertexBlendEnvfATI;\n#define glVertexBlendEnvfATI glad_glVertexBlendEnvfATI\n#endif\n#ifndef GL_ARB_texture_non_power_of_two\n#define GL_ARB_texture_non_power_of_two 1\nGLAPI int GLAD_GL_ARB_texture_non_power_of_two;\n#endif\n#ifndef GL_APPLE_rgb_422\n#define GL_APPLE_rgb_422 1\nGLAPI int GLAD_GL_APPLE_rgb_422;\n#endif\n#ifndef GL_EXT_texture_lod_bias\n#define GL_EXT_texture_lod_bias 1\nGLAPI int GLAD_GL_EXT_texture_lod_bias;\n#endif\n#ifndef GL_ARB_seamless_cube_map\n#define GL_ARB_seamless_cube_map 1\nGLAPI int GLAD_GL_ARB_seamless_cube_map;\n#endif\n#ifndef GL_ARB_shader_group_vote\n#define GL_ARB_shader_group_vote 1\nGLAPI int GLAD_GL_ARB_shader_group_vote;\n#endif\n#ifndef GL_NV_vdpau_interop\n#define GL_NV_vdpau_interop 1\nGLAPI int GLAD_GL_NV_vdpau_interop;\ntypedef void (APIENTRYP PFNGLVDPAUINITNVPROC)(const void*, const void*);\nGLAPI PFNGLVDPAUINITNVPROC glad_glVDPAUInitNV;\n#define glVDPAUInitNV glad_glVDPAUInitNV\ntypedef void (APIENTRYP PFNGLVDPAUFININVPROC)();\nGLAPI PFNGLVDPAUFININVPROC glad_glVDPAUFiniNV;\n#define glVDPAUFiniNV glad_glVDPAUFiniNV\ntypedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC)(const void*, GLenum, GLsizei, const GLuint*);\nGLAPI PFNGLVDPAUREGISTERVIDEOSURFACENVPROC glad_glVDPAURegisterVideoSurfaceNV;\n#define glVDPAURegisterVideoSurfaceNV glad_glVDPAURegisterVideoSurfaceNV\ntypedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC)(const void*, GLenum, GLsizei, const GLuint*);\nGLAPI PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC glad_glVDPAURegisterOutputSurfaceNV;\n#define glVDPAURegisterOutputSurfaceNV glad_glVDPAURegisterOutputSurfaceNV\ntypedef GLboolean (APIENTRYP PFNGLVDPAUISSURFACENVPROC)(GLvdpauSurfaceNV);\nGLAPI PFNGLVDPAUISSURFACENVPROC glad_glVDPAUIsSurfaceNV;\n#define glVDPAUIsSurfaceNV glad_glVDPAUIsSurfaceNV\ntypedef void (APIENTRYP PFNGLVDPAUUNREGISTERSURFACENVPROC)(GLvdpauSurfaceNV);\nGLAPI PFNGLVDPAUUNREGISTERSURFACENVPROC glad_glVDPAUUnregisterSurfaceNV;\n#define glVDPAUUnregisterSurfaceNV glad_glVDPAUUnregisterSurfaceNV\ntypedef void (APIENTRYP PFNGLVDPAUGETSURFACEIVNVPROC)(GLvdpauSurfaceNV, GLenum, GLsizei, GLsizei*, GLint*);\nGLAPI PFNGLVDPAUGETSURFACEIVNVPROC glad_glVDPAUGetSurfaceivNV;\n#define glVDPAUGetSurfaceivNV glad_glVDPAUGetSurfaceivNV\ntypedef void (APIENTRYP PFNGLVDPAUSURFACEACCESSNVPROC)(GLvdpauSurfaceNV, GLenum);\nGLAPI PFNGLVDPAUSURFACEACCESSNVPROC glad_glVDPAUSurfaceAccessNV;\n#define glVDPAUSurfaceAccessNV glad_glVDPAUSurfaceAccessNV\ntypedef void (APIENTRYP PFNGLVDPAUMAPSURFACESNVPROC)(GLsizei, const GLvdpauSurfaceNV*);\nGLAPI PFNGLVDPAUMAPSURFACESNVPROC glad_glVDPAUMapSurfacesNV;\n#define glVDPAUMapSurfacesNV glad_glVDPAUMapSurfacesNV\ntypedef void (APIENTRYP PFNGLVDPAUUNMAPSURFACESNVPROC)(GLsizei, const GLvdpauSurfaceNV*);\nGLAPI PFNGLVDPAUUNMAPSURFACESNVPROC glad_glVDPAUUnmapSurfacesNV;\n#define glVDPAUUnmapSurfacesNV glad_glVDPAUUnmapSurfacesNV\n#endif\n#ifndef GL_ARB_occlusion_query2\n#define GL_ARB_occlusion_query2 1\nGLAPI int GLAD_GL_ARB_occlusion_query2;\n#endif\n#ifndef GL_ARB_internalformat_query2\n#define GL_ARB_internalformat_query2 1\nGLAPI int GLAD_GL_ARB_internalformat_query2;\n#endif\n#ifndef GL_EXT_texture_filter_anisotropic\n#define GL_EXT_texture_filter_anisotropic 1\nGLAPI int GLAD_GL_EXT_texture_filter_anisotropic;\n#endif\n#ifndef GL_SUN_vertex\n#define GL_SUN_vertex 1\nGLAPI int GLAD_GL_SUN_vertex;\ntypedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC)(GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat);\nGLAPI PFNGLCOLOR4UBVERTEX2FSUNPROC glad_glColor4ubVertex2fSUN;\n#define glColor4ubVertex2fSUN glad_glColor4ubVertex2fSUN\ntypedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC)(const GLubyte*, const GLfloat*);\nGLAPI PFNGLCOLOR4UBVERTEX2FVSUNPROC glad_glColor4ubVertex2fvSUN;\n#define glColor4ubVertex2fvSUN glad_glColor4ubVertex2fvSUN\ntypedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC)(GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLCOLOR4UBVERTEX3FSUNPROC glad_glColor4ubVertex3fSUN;\n#define glColor4ubVertex3fSUN glad_glColor4ubVertex3fSUN\ntypedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC)(const GLubyte*, const GLfloat*);\nGLAPI PFNGLCOLOR4UBVERTEX3FVSUNPROC glad_glColor4ubVertex3fvSUN;\n#define glColor4ubVertex3fvSUN glad_glColor4ubVertex3fvSUN\ntypedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLCOLOR3FVERTEX3FSUNPROC glad_glColor3fVertex3fSUN;\n#define glColor3fVertex3fSUN glad_glColor3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC)(const GLfloat*, const GLfloat*);\nGLAPI PFNGLCOLOR3FVERTEX3FVSUNPROC glad_glColor3fVertex3fvSUN;\n#define glColor3fVertex3fvSUN glad_glColor3fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLNORMAL3FVERTEX3FSUNPROC glad_glNormal3fVertex3fSUN;\n#define glNormal3fVertex3fSUN glad_glNormal3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC)(const GLfloat*, const GLfloat*);\nGLAPI PFNGLNORMAL3FVERTEX3FVSUNPROC glad_glNormal3fVertex3fvSUN;\n#define glNormal3fVertex3fvSUN glad_glNormal3fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC glad_glColor4fNormal3fVertex3fSUN;\n#define glColor4fNormal3fVertex3fSUN glad_glColor4fNormal3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC)(const GLfloat*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC glad_glColor4fNormal3fVertex3fvSUN;\n#define glColor4fNormal3fVertex3fvSUN glad_glColor4fNormal3fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLTEXCOORD2FVERTEX3FSUNPROC glad_glTexCoord2fVertex3fSUN;\n#define glTexCoord2fVertex3fSUN glad_glTexCoord2fVertex3fSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC)(const GLfloat*, const GLfloat*);\nGLAPI PFNGLTEXCOORD2FVERTEX3FVSUNPROC glad_glTexCoord2fVertex3fvSUN;\n#define glTexCoord2fVertex3fvSUN glad_glTexCoord2fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLTEXCOORD4FVERTEX4FSUNPROC glad_glTexCoord4fVertex4fSUN;\n#define glTexCoord4fVertex4fSUN glad_glTexCoord4fVertex4fSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC)(const GLfloat*, const GLfloat*);\nGLAPI PFNGLTEXCOORD4FVERTEX4FVSUNPROC glad_glTexCoord4fVertex4fvSUN;\n#define glTexCoord4fVertex4fvSUN glad_glTexCoord4fVertex4fvSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC)(GLfloat, GLfloat, GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC glad_glTexCoord2fColor4ubVertex3fSUN;\n#define glTexCoord2fColor4ubVertex3fSUN glad_glTexCoord2fColor4ubVertex3fSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC)(const GLfloat*, const GLubyte*, const GLfloat*);\nGLAPI PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC glad_glTexCoord2fColor4ubVertex3fvSUN;\n#define glTexCoord2fColor4ubVertex3fvSUN glad_glTexCoord2fColor4ubVertex3fvSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC glad_glTexCoord2fColor3fVertex3fSUN;\n#define glTexCoord2fColor3fVertex3fSUN glad_glTexCoord2fColor3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC)(const GLfloat*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC glad_glTexCoord2fColor3fVertex3fvSUN;\n#define glTexCoord2fColor3fVertex3fvSUN glad_glTexCoord2fColor3fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC glad_glTexCoord2fNormal3fVertex3fSUN;\n#define glTexCoord2fNormal3fVertex3fSUN glad_glTexCoord2fNormal3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC)(const GLfloat*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC glad_glTexCoord2fNormal3fVertex3fvSUN;\n#define glTexCoord2fNormal3fVertex3fvSUN glad_glTexCoord2fNormal3fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC glad_glTexCoord2fColor4fNormal3fVertex3fSUN;\n#define glTexCoord2fColor4fNormal3fVertex3fSUN glad_glTexCoord2fColor4fNormal3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC)(const GLfloat*, const GLfloat*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC glad_glTexCoord2fColor4fNormal3fVertex3fvSUN;\n#define glTexCoord2fColor4fNormal3fVertex3fvSUN glad_glTexCoord2fColor4fNormal3fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC)(GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC glad_glTexCoord4fColor4fNormal3fVertex4fSUN;\n#define glTexCoord4fColor4fNormal3fVertex4fSUN glad_glTexCoord4fColor4fNormal3fVertex4fSUN\ntypedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC)(const GLfloat*, const GLfloat*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC glad_glTexCoord4fColor4fNormal3fVertex4fvSUN;\n#define glTexCoord4fColor4fNormal3fVertex4fvSUN glad_glTexCoord4fColor4fNormal3fVertex4fvSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC)(GLuint, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC glad_glReplacementCodeuiVertex3fSUN;\n#define glReplacementCodeuiVertex3fSUN glad_glReplacementCodeuiVertex3fSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC)(const GLuint*, const GLfloat*);\nGLAPI PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC glad_glReplacementCodeuiVertex3fvSUN;\n#define glReplacementCodeuiVertex3fvSUN glad_glReplacementCodeuiVertex3fvSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC)(GLuint, GLubyte, GLubyte, GLubyte, GLubyte, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC glad_glReplacementCodeuiColor4ubVertex3fSUN;\n#define glReplacementCodeuiColor4ubVertex3fSUN glad_glReplacementCodeuiColor4ubVertex3fSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC)(const GLuint*, const GLubyte*, const GLfloat*);\nGLAPI PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC glad_glReplacementCodeuiColor4ubVertex3fvSUN;\n#define glReplacementCodeuiColor4ubVertex3fvSUN glad_glReplacementCodeuiColor4ubVertex3fvSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC glad_glReplacementCodeuiColor3fVertex3fSUN;\n#define glReplacementCodeuiColor3fVertex3fSUN glad_glReplacementCodeuiColor3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC)(const GLuint*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC glad_glReplacementCodeuiColor3fVertex3fvSUN;\n#define glReplacementCodeuiColor3fVertex3fvSUN glad_glReplacementCodeuiColor3fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC glad_glReplacementCodeuiNormal3fVertex3fSUN;\n#define glReplacementCodeuiNormal3fVertex3fSUN glad_glReplacementCodeuiNormal3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC)(const GLuint*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC glad_glReplacementCodeuiNormal3fVertex3fvSUN;\n#define glReplacementCodeuiNormal3fVertex3fvSUN glad_glReplacementCodeuiNormal3fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC glad_glReplacementCodeuiColor4fNormal3fVertex3fSUN;\n#define glReplacementCodeuiColor4fNormal3fVertex3fSUN glad_glReplacementCodeuiColor4fNormal3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC)(const GLuint*, const GLfloat*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC glad_glReplacementCodeuiColor4fNormal3fVertex3fvSUN;\n#define glReplacementCodeuiColor4fNormal3fVertex3fvSUN glad_glReplacementCodeuiColor4fNormal3fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC glad_glReplacementCodeuiTexCoord2fVertex3fSUN;\n#define glReplacementCodeuiTexCoord2fVertex3fSUN glad_glReplacementCodeuiTexCoord2fVertex3fSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC)(const GLuint*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC glad_glReplacementCodeuiTexCoord2fVertex3fvSUN;\n#define glReplacementCodeuiTexCoord2fVertex3fvSUN glad_glReplacementCodeuiTexCoord2fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC glad_glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN;\n#define glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN glad_glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC)(const GLuint*, const GLfloat*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC glad_glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN;\n#define glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN glad_glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC)(GLuint, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC glad_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN;\n#define glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN glad_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN\ntypedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC)(const GLuint*, const GLfloat*, const GLfloat*, const GLfloat*, const GLfloat*);\nGLAPI PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC glad_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN;\n#define glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN glad_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN\n#endif\n#ifndef GL_SGIX_igloo_interface\n#define GL_SGIX_igloo_interface 1\nGLAPI int GLAD_GL_SGIX_igloo_interface;\ntypedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC)(GLenum, const void*);\nGLAPI PFNGLIGLOOINTERFACESGIXPROC glad_glIglooInterfaceSGIX;\n#define glIglooInterfaceSGIX glad_glIglooInterfaceSGIX\n#endif\n#ifndef GL_SGIS_texture_lod\n#define GL_SGIS_texture_lod 1\nGLAPI int GLAD_GL_SGIS_texture_lod;\n#endif\n#ifndef GL_NV_vertex_program3\n#define GL_NV_vertex_program3 1\nGLAPI int GLAD_GL_NV_vertex_program3;\n#endif\n#ifndef GL_ARB_draw_indirect\n#define GL_ARB_draw_indirect 1\nGLAPI int GLAD_GL_ARB_draw_indirect;\n#endif\n#ifndef GL_NV_vertex_program4\n#define GL_NV_vertex_program4 1\nGLAPI int GLAD_GL_NV_vertex_program4;\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC)(GLuint, GLint);\nGLAPI PFNGLVERTEXATTRIBI1IEXTPROC glad_glVertexAttribI1iEXT;\n#define glVertexAttribI1iEXT glad_glVertexAttribI1iEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC)(GLuint, GLint, GLint);\nGLAPI PFNGLVERTEXATTRIBI2IEXTPROC glad_glVertexAttribI2iEXT;\n#define glVertexAttribI2iEXT glad_glVertexAttribI2iEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC)(GLuint, GLint, GLint, GLint);\nGLAPI PFNGLVERTEXATTRIBI3IEXTPROC glad_glVertexAttribI3iEXT;\n#define glVertexAttribI3iEXT glad_glVertexAttribI3iEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC)(GLuint, GLint, GLint, GLint, GLint);\nGLAPI PFNGLVERTEXATTRIBI4IEXTPROC glad_glVertexAttribI4iEXT;\n#define glVertexAttribI4iEXT glad_glVertexAttribI4iEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC)(GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBI1UIEXTPROC glad_glVertexAttribI1uiEXT;\n#define glVertexAttribI1uiEXT glad_glVertexAttribI1uiEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBI2UIEXTPROC glad_glVertexAttribI2uiEXT;\n#define glVertexAttribI2uiEXT glad_glVertexAttribI2uiEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC)(GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBI3UIEXTPROC glad_glVertexAttribI3uiEXT;\n#define glVertexAttribI3uiEXT glad_glVertexAttribI3uiEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC)(GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLVERTEXATTRIBI4UIEXTPROC glad_glVertexAttribI4uiEXT;\n#define glVertexAttribI4uiEXT glad_glVertexAttribI4uiEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIBI1IVEXTPROC glad_glVertexAttribI1ivEXT;\n#define glVertexAttribI1ivEXT glad_glVertexAttribI1ivEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIBI2IVEXTPROC glad_glVertexAttribI2ivEXT;\n#define glVertexAttribI2ivEXT glad_glVertexAttribI2ivEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIBI3IVEXTPROC glad_glVertexAttribI3ivEXT;\n#define glVertexAttribI3ivEXT glad_glVertexAttribI3ivEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC)(GLuint, const GLint*);\nGLAPI PFNGLVERTEXATTRIBI4IVEXTPROC glad_glVertexAttribI4ivEXT;\n#define glVertexAttribI4ivEXT glad_glVertexAttribI4ivEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBI1UIVEXTPROC glad_glVertexAttribI1uivEXT;\n#define glVertexAttribI1uivEXT glad_glVertexAttribI1uivEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBI2UIVEXTPROC glad_glVertexAttribI2uivEXT;\n#define glVertexAttribI2uivEXT glad_glVertexAttribI2uivEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBI3UIVEXTPROC glad_glVertexAttribI3uivEXT;\n#define glVertexAttribI3uivEXT glad_glVertexAttribI3uivEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC)(GLuint, const GLuint*);\nGLAPI PFNGLVERTEXATTRIBI4UIVEXTPROC glad_glVertexAttribI4uivEXT;\n#define glVertexAttribI4uivEXT glad_glVertexAttribI4uivEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC)(GLuint, const GLbyte*);\nGLAPI PFNGLVERTEXATTRIBI4BVEXTPROC glad_glVertexAttribI4bvEXT;\n#define glVertexAttribI4bvEXT glad_glVertexAttribI4bvEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC)(GLuint, const GLshort*);\nGLAPI PFNGLVERTEXATTRIBI4SVEXTPROC glad_glVertexAttribI4svEXT;\n#define glVertexAttribI4svEXT glad_glVertexAttribI4svEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC)(GLuint, const GLubyte*);\nGLAPI PFNGLVERTEXATTRIBI4UBVEXTPROC glad_glVertexAttribI4ubvEXT;\n#define glVertexAttribI4ubvEXT glad_glVertexAttribI4ubvEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC)(GLuint, const GLushort*);\nGLAPI PFNGLVERTEXATTRIBI4USVEXTPROC glad_glVertexAttribI4usvEXT;\n#define glVertexAttribI4usvEXT glad_glVertexAttribI4usvEXT\ntypedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC)(GLuint, GLint, GLenum, GLsizei, const void*);\nGLAPI PFNGLVERTEXATTRIBIPOINTEREXTPROC glad_glVertexAttribIPointerEXT;\n#define glVertexAttribIPointerEXT glad_glVertexAttribIPointerEXT\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVERTEXATTRIBIIVEXTPROC glad_glGetVertexAttribIivEXT;\n#define glGetVertexAttribIivEXT glad_glGetVertexAttribIivEXT\ntypedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC)(GLuint, GLenum, GLuint*);\nGLAPI PFNGLGETVERTEXATTRIBIUIVEXTPROC glad_glGetVertexAttribIuivEXT;\n#define glGetVertexAttribIuivEXT glad_glGetVertexAttribIuivEXT\n#endif\n#ifndef GL_AMD_transform_feedback3_lines_triangles\n#define GL_AMD_transform_feedback3_lines_triangles 1\nGLAPI int GLAD_GL_AMD_transform_feedback3_lines_triangles;\n#endif\n#ifndef GL_SGIS_fog_function\n#define GL_SGIS_fog_function 1\nGLAPI int GLAD_GL_SGIS_fog_function;\ntypedef void (APIENTRYP PFNGLFOGFUNCSGISPROC)(GLsizei, const GLfloat*);\nGLAPI PFNGLFOGFUNCSGISPROC glad_glFogFuncSGIS;\n#define glFogFuncSGIS glad_glFogFuncSGIS\ntypedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC)(GLfloat*);\nGLAPI PFNGLGETFOGFUNCSGISPROC glad_glGetFogFuncSGIS;\n#define glGetFogFuncSGIS glad_glGetFogFuncSGIS\n#endif\n#ifndef GL_EXT_x11_sync_object\n#define GL_EXT_x11_sync_object 1\nGLAPI int GLAD_GL_EXT_x11_sync_object;\ntypedef GLsync (APIENTRYP PFNGLIMPORTSYNCEXTPROC)(GLenum, GLintptr, GLbitfield);\nGLAPI PFNGLIMPORTSYNCEXTPROC glad_glImportSyncEXT;\n#define glImportSyncEXT glad_glImportSyncEXT\n#endif\n#ifndef GL_ARB_sync\n#define GL_ARB_sync 1\nGLAPI int GLAD_GL_ARB_sync;\n#endif\n#ifndef GL_ARB_compute_variable_group_size\n#define GL_ARB_compute_variable_group_size 1\nGLAPI int GLAD_GL_ARB_compute_variable_group_size;\ntypedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC)(GLuint, GLuint, GLuint, GLuint, GLuint, GLuint);\nGLAPI PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC glad_glDispatchComputeGroupSizeARB;\n#define glDispatchComputeGroupSizeARB glad_glDispatchComputeGroupSizeARB\n#endif\n#ifndef GL_OES_fixed_point\n#define GL_OES_fixed_point 1\nGLAPI int GLAD_GL_OES_fixed_point;\ntypedef void (APIENTRYP PFNGLALPHAFUNCXOESPROC)(GLenum, GLfixed);\nGLAPI PFNGLALPHAFUNCXOESPROC glad_glAlphaFuncxOES;\n#define glAlphaFuncxOES glad_glAlphaFuncxOES\ntypedef void (APIENTRYP PFNGLCLEARCOLORXOESPROC)(GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLCLEARCOLORXOESPROC glad_glClearColorxOES;\n#define glClearColorxOES glad_glClearColorxOES\ntypedef void (APIENTRYP PFNGLCLEARDEPTHXOESPROC)(GLfixed);\nGLAPI PFNGLCLEARDEPTHXOESPROC glad_glClearDepthxOES;\n#define glClearDepthxOES glad_glClearDepthxOES\ntypedef void (APIENTRYP PFNGLCLIPPLANEXOESPROC)(GLenum, const GLfixed*);\nGLAPI PFNGLCLIPPLANEXOESPROC glad_glClipPlanexOES;\n#define glClipPlanexOES glad_glClipPlanexOES\ntypedef void (APIENTRYP PFNGLCOLOR4XOESPROC)(GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLCOLOR4XOESPROC glad_glColor4xOES;\n#define glColor4xOES glad_glColor4xOES\ntypedef void (APIENTRYP PFNGLDEPTHRANGEXOESPROC)(GLfixed, GLfixed);\nGLAPI PFNGLDEPTHRANGEXOESPROC glad_glDepthRangexOES;\n#define glDepthRangexOES glad_glDepthRangexOES\ntypedef void (APIENTRYP PFNGLFOGXOESPROC)(GLenum, GLfixed);\nGLAPI PFNGLFOGXOESPROC glad_glFogxOES;\n#define glFogxOES glad_glFogxOES\ntypedef void (APIENTRYP PFNGLFOGXVOESPROC)(GLenum, const GLfixed*);\nGLAPI PFNGLFOGXVOESPROC glad_glFogxvOES;\n#define glFogxvOES glad_glFogxvOES\ntypedef void (APIENTRYP PFNGLFRUSTUMXOESPROC)(GLfixed, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLFRUSTUMXOESPROC glad_glFrustumxOES;\n#define glFrustumxOES glad_glFrustumxOES\ntypedef void (APIENTRYP PFNGLGETCLIPPLANEXOESPROC)(GLenum, GLfixed*);\nGLAPI PFNGLGETCLIPPLANEXOESPROC glad_glGetClipPlanexOES;\n#define glGetClipPlanexOES glad_glGetClipPlanexOES\ntypedef void (APIENTRYP PFNGLGETFIXEDVOESPROC)(GLenum, GLfixed*);\nGLAPI PFNGLGETFIXEDVOESPROC glad_glGetFixedvOES;\n#define glGetFixedvOES glad_glGetFixedvOES\ntypedef void (APIENTRYP PFNGLGETTEXENVXVOESPROC)(GLenum, GLenum, GLfixed*);\nGLAPI PFNGLGETTEXENVXVOESPROC glad_glGetTexEnvxvOES;\n#define glGetTexEnvxvOES glad_glGetTexEnvxvOES\ntypedef void (APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC)(GLenum, GLenum, GLfixed*);\nGLAPI PFNGLGETTEXPARAMETERXVOESPROC glad_glGetTexParameterxvOES;\n#define glGetTexParameterxvOES glad_glGetTexParameterxvOES\ntypedef void (APIENTRYP PFNGLLIGHTMODELXOESPROC)(GLenum, GLfixed);\nGLAPI PFNGLLIGHTMODELXOESPROC glad_glLightModelxOES;\n#define glLightModelxOES glad_glLightModelxOES\ntypedef void (APIENTRYP PFNGLLIGHTMODELXVOESPROC)(GLenum, const GLfixed*);\nGLAPI PFNGLLIGHTMODELXVOESPROC glad_glLightModelxvOES;\n#define glLightModelxvOES glad_glLightModelxvOES\ntypedef void (APIENTRYP PFNGLLIGHTXOESPROC)(GLenum, GLenum, GLfixed);\nGLAPI PFNGLLIGHTXOESPROC glad_glLightxOES;\n#define glLightxOES glad_glLightxOES\ntypedef void (APIENTRYP PFNGLLIGHTXVOESPROC)(GLenum, GLenum, const GLfixed*);\nGLAPI PFNGLLIGHTXVOESPROC glad_glLightxvOES;\n#define glLightxvOES glad_glLightxvOES\ntypedef void (APIENTRYP PFNGLLINEWIDTHXOESPROC)(GLfixed);\nGLAPI PFNGLLINEWIDTHXOESPROC glad_glLineWidthxOES;\n#define glLineWidthxOES glad_glLineWidthxOES\ntypedef void (APIENTRYP PFNGLLOADMATRIXXOESPROC)(const GLfixed*);\nGLAPI PFNGLLOADMATRIXXOESPROC glad_glLoadMatrixxOES;\n#define glLoadMatrixxOES glad_glLoadMatrixxOES\ntypedef void (APIENTRYP PFNGLMATERIALXOESPROC)(GLenum, GLenum, GLfixed);\nGLAPI PFNGLMATERIALXOESPROC glad_glMaterialxOES;\n#define glMaterialxOES glad_glMaterialxOES\ntypedef void (APIENTRYP PFNGLMATERIALXVOESPROC)(GLenum, GLenum, const GLfixed*);\nGLAPI PFNGLMATERIALXVOESPROC glad_glMaterialxvOES;\n#define glMaterialxvOES glad_glMaterialxvOES\ntypedef void (APIENTRYP PFNGLMULTMATRIXXOESPROC)(const GLfixed*);\nGLAPI PFNGLMULTMATRIXXOESPROC glad_glMultMatrixxOES;\n#define glMultMatrixxOES glad_glMultMatrixxOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4XOESPROC)(GLenum, GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLMULTITEXCOORD4XOESPROC glad_glMultiTexCoord4xOES;\n#define glMultiTexCoord4xOES glad_glMultiTexCoord4xOES\ntypedef void (APIENTRYP PFNGLNORMAL3XOESPROC)(GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLNORMAL3XOESPROC glad_glNormal3xOES;\n#define glNormal3xOES glad_glNormal3xOES\ntypedef void (APIENTRYP PFNGLORTHOXOESPROC)(GLfixed, GLfixed, GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLORTHOXOESPROC glad_glOrthoxOES;\n#define glOrthoxOES glad_glOrthoxOES\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERXVOESPROC)(GLenum, const GLfixed*);\nGLAPI PFNGLPOINTPARAMETERXVOESPROC glad_glPointParameterxvOES;\n#define glPointParameterxvOES glad_glPointParameterxvOES\ntypedef void (APIENTRYP PFNGLPOINTSIZEXOESPROC)(GLfixed);\nGLAPI PFNGLPOINTSIZEXOESPROC glad_glPointSizexOES;\n#define glPointSizexOES glad_glPointSizexOES\ntypedef void (APIENTRYP PFNGLPOLYGONOFFSETXOESPROC)(GLfixed, GLfixed);\nGLAPI PFNGLPOLYGONOFFSETXOESPROC glad_glPolygonOffsetxOES;\n#define glPolygonOffsetxOES glad_glPolygonOffsetxOES\ntypedef void (APIENTRYP PFNGLROTATEXOESPROC)(GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLROTATEXOESPROC glad_glRotatexOES;\n#define glRotatexOES glad_glRotatexOES\ntypedef void (APIENTRYP PFNGLSAMPLECOVERAGEOESPROC)(GLfixed, GLboolean);\nGLAPI PFNGLSAMPLECOVERAGEOESPROC glad_glSampleCoverageOES;\n#define glSampleCoverageOES glad_glSampleCoverageOES\ntypedef void (APIENTRYP PFNGLSCALEXOESPROC)(GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLSCALEXOESPROC glad_glScalexOES;\n#define glScalexOES glad_glScalexOES\ntypedef void (APIENTRYP PFNGLTEXENVXOESPROC)(GLenum, GLenum, GLfixed);\nGLAPI PFNGLTEXENVXOESPROC glad_glTexEnvxOES;\n#define glTexEnvxOES glad_glTexEnvxOES\ntypedef void (APIENTRYP PFNGLTEXENVXVOESPROC)(GLenum, GLenum, const GLfixed*);\nGLAPI PFNGLTEXENVXVOESPROC glad_glTexEnvxvOES;\n#define glTexEnvxvOES glad_glTexEnvxvOES\ntypedef void (APIENTRYP PFNGLTEXPARAMETERXOESPROC)(GLenum, GLenum, GLfixed);\nGLAPI PFNGLTEXPARAMETERXOESPROC glad_glTexParameterxOES;\n#define glTexParameterxOES glad_glTexParameterxOES\ntypedef void (APIENTRYP PFNGLTEXPARAMETERXVOESPROC)(GLenum, GLenum, const GLfixed*);\nGLAPI PFNGLTEXPARAMETERXVOESPROC glad_glTexParameterxvOES;\n#define glTexParameterxvOES glad_glTexParameterxvOES\ntypedef void (APIENTRYP PFNGLTRANSLATEXOESPROC)(GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLTRANSLATEXOESPROC glad_glTranslatexOES;\n#define glTranslatexOES glad_glTranslatexOES\ntypedef void (APIENTRYP PFNGLGETLIGHTXVOESPROC)(GLenum, GLenum, GLfixed*);\nGLAPI PFNGLGETLIGHTXVOESPROC glad_glGetLightxvOES;\n#define glGetLightxvOES glad_glGetLightxvOES\ntypedef void (APIENTRYP PFNGLGETMATERIALXVOESPROC)(GLenum, GLenum, GLfixed*);\nGLAPI PFNGLGETMATERIALXVOESPROC glad_glGetMaterialxvOES;\n#define glGetMaterialxvOES glad_glGetMaterialxvOES\ntypedef void (APIENTRYP PFNGLPOINTPARAMETERXOESPROC)(GLenum, GLfixed);\nGLAPI PFNGLPOINTPARAMETERXOESPROC glad_glPointParameterxOES;\n#define glPointParameterxOES glad_glPointParameterxOES\ntypedef void (APIENTRYP PFNGLSAMPLECOVERAGEXOESPROC)(GLclampx, GLboolean);\nGLAPI PFNGLSAMPLECOVERAGEXOESPROC glad_glSampleCoveragexOES;\n#define glSampleCoveragexOES glad_glSampleCoveragexOES\ntypedef void (APIENTRYP PFNGLACCUMXOESPROC)(GLenum, GLfixed);\nGLAPI PFNGLACCUMXOESPROC glad_glAccumxOES;\n#define glAccumxOES glad_glAccumxOES\ntypedef void (APIENTRYP PFNGLBITMAPXOESPROC)(GLsizei, GLsizei, GLfixed, GLfixed, GLfixed, GLfixed, const GLubyte*);\nGLAPI PFNGLBITMAPXOESPROC glad_glBitmapxOES;\n#define glBitmapxOES glad_glBitmapxOES\ntypedef void (APIENTRYP PFNGLBLENDCOLORXOESPROC)(GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLBLENDCOLORXOESPROC glad_glBlendColorxOES;\n#define glBlendColorxOES glad_glBlendColorxOES\ntypedef void (APIENTRYP PFNGLCLEARACCUMXOESPROC)(GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLCLEARACCUMXOESPROC glad_glClearAccumxOES;\n#define glClearAccumxOES glad_glClearAccumxOES\ntypedef void (APIENTRYP PFNGLCOLOR3XOESPROC)(GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLCOLOR3XOESPROC glad_glColor3xOES;\n#define glColor3xOES glad_glColor3xOES\ntypedef void (APIENTRYP PFNGLCOLOR3XVOESPROC)(const GLfixed*);\nGLAPI PFNGLCOLOR3XVOESPROC glad_glColor3xvOES;\n#define glColor3xvOES glad_glColor3xvOES\ntypedef void (APIENTRYP PFNGLCOLOR4XVOESPROC)(const GLfixed*);\nGLAPI PFNGLCOLOR4XVOESPROC glad_glColor4xvOES;\n#define glColor4xvOES glad_glColor4xvOES\ntypedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXOESPROC)(GLenum, GLenum, GLfixed);\nGLAPI PFNGLCONVOLUTIONPARAMETERXOESPROC glad_glConvolutionParameterxOES;\n#define glConvolutionParameterxOES glad_glConvolutionParameterxOES\ntypedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXVOESPROC)(GLenum, GLenum, const GLfixed*);\nGLAPI PFNGLCONVOLUTIONPARAMETERXVOESPROC glad_glConvolutionParameterxvOES;\n#define glConvolutionParameterxvOES glad_glConvolutionParameterxvOES\ntypedef void (APIENTRYP PFNGLEVALCOORD1XOESPROC)(GLfixed);\nGLAPI PFNGLEVALCOORD1XOESPROC glad_glEvalCoord1xOES;\n#define glEvalCoord1xOES glad_glEvalCoord1xOES\ntypedef void (APIENTRYP PFNGLEVALCOORD1XVOESPROC)(const GLfixed*);\nGLAPI PFNGLEVALCOORD1XVOESPROC glad_glEvalCoord1xvOES;\n#define glEvalCoord1xvOES glad_glEvalCoord1xvOES\ntypedef void (APIENTRYP PFNGLEVALCOORD2XOESPROC)(GLfixed, GLfixed);\nGLAPI PFNGLEVALCOORD2XOESPROC glad_glEvalCoord2xOES;\n#define glEvalCoord2xOES glad_glEvalCoord2xOES\ntypedef void (APIENTRYP PFNGLEVALCOORD2XVOESPROC)(const GLfixed*);\nGLAPI PFNGLEVALCOORD2XVOESPROC glad_glEvalCoord2xvOES;\n#define glEvalCoord2xvOES glad_glEvalCoord2xvOES\ntypedef void (APIENTRYP PFNGLFEEDBACKBUFFERXOESPROC)(GLsizei, GLenum, const GLfixed*);\nGLAPI PFNGLFEEDBACKBUFFERXOESPROC glad_glFeedbackBufferxOES;\n#define glFeedbackBufferxOES glad_glFeedbackBufferxOES\ntypedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERXVOESPROC)(GLenum, GLenum, GLfixed*);\nGLAPI PFNGLGETCONVOLUTIONPARAMETERXVOESPROC glad_glGetConvolutionParameterxvOES;\n#define glGetConvolutionParameterxvOES glad_glGetConvolutionParameterxvOES\ntypedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERXVOESPROC)(GLenum, GLenum, GLfixed*);\nGLAPI PFNGLGETHISTOGRAMPARAMETERXVOESPROC glad_glGetHistogramParameterxvOES;\n#define glGetHistogramParameterxvOES glad_glGetHistogramParameterxvOES\ntypedef void (APIENTRYP PFNGLGETLIGHTXOESPROC)(GLenum, GLenum, GLfixed*);\nGLAPI PFNGLGETLIGHTXOESPROC glad_glGetLightxOES;\n#define glGetLightxOES glad_glGetLightxOES\ntypedef void (APIENTRYP PFNGLGETMAPXVOESPROC)(GLenum, GLenum, GLfixed*);\nGLAPI PFNGLGETMAPXVOESPROC glad_glGetMapxvOES;\n#define glGetMapxvOES glad_glGetMapxvOES\ntypedef void (APIENTRYP PFNGLGETMATERIALXOESPROC)(GLenum, GLenum, GLfixed);\nGLAPI PFNGLGETMATERIALXOESPROC glad_glGetMaterialxOES;\n#define glGetMaterialxOES glad_glGetMaterialxOES\ntypedef void (APIENTRYP PFNGLGETPIXELMAPXVPROC)(GLenum, GLint, GLfixed*);\nGLAPI PFNGLGETPIXELMAPXVPROC glad_glGetPixelMapxv;\n#define glGetPixelMapxv glad_glGetPixelMapxv\ntypedef void (APIENTRYP PFNGLGETTEXGENXVOESPROC)(GLenum, GLenum, GLfixed*);\nGLAPI PFNGLGETTEXGENXVOESPROC glad_glGetTexGenxvOES;\n#define glGetTexGenxvOES glad_glGetTexGenxvOES\ntypedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERXVOESPROC)(GLenum, GLint, GLenum, GLfixed*);\nGLAPI PFNGLGETTEXLEVELPARAMETERXVOESPROC glad_glGetTexLevelParameterxvOES;\n#define glGetTexLevelParameterxvOES glad_glGetTexLevelParameterxvOES\ntypedef void (APIENTRYP PFNGLINDEXXOESPROC)(GLfixed);\nGLAPI PFNGLINDEXXOESPROC glad_glIndexxOES;\n#define glIndexxOES glad_glIndexxOES\ntypedef void (APIENTRYP PFNGLINDEXXVOESPROC)(const GLfixed*);\nGLAPI PFNGLINDEXXVOESPROC glad_glIndexxvOES;\n#define glIndexxvOES glad_glIndexxvOES\ntypedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXXOESPROC)(const GLfixed*);\nGLAPI PFNGLLOADTRANSPOSEMATRIXXOESPROC glad_glLoadTransposeMatrixxOES;\n#define glLoadTransposeMatrixxOES glad_glLoadTransposeMatrixxOES\ntypedef void (APIENTRYP PFNGLMAP1XOESPROC)(GLenum, GLfixed, GLfixed, GLint, GLint, GLfixed);\nGLAPI PFNGLMAP1XOESPROC glad_glMap1xOES;\n#define glMap1xOES glad_glMap1xOES\ntypedef void (APIENTRYP PFNGLMAP2XOESPROC)(GLenum, GLfixed, GLfixed, GLint, GLint, GLfixed, GLfixed, GLint, GLint, GLfixed);\nGLAPI PFNGLMAP2XOESPROC glad_glMap2xOES;\n#define glMap2xOES glad_glMap2xOES\ntypedef void (APIENTRYP PFNGLMAPGRID1XOESPROC)(GLint, GLfixed, GLfixed);\nGLAPI PFNGLMAPGRID1XOESPROC glad_glMapGrid1xOES;\n#define glMapGrid1xOES glad_glMapGrid1xOES\ntypedef void (APIENTRYP PFNGLMAPGRID2XOESPROC)(GLint, GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLMAPGRID2XOESPROC glad_glMapGrid2xOES;\n#define glMapGrid2xOES glad_glMapGrid2xOES\ntypedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXXOESPROC)(const GLfixed*);\nGLAPI PFNGLMULTTRANSPOSEMATRIXXOESPROC glad_glMultTransposeMatrixxOES;\n#define glMultTransposeMatrixxOES glad_glMultTransposeMatrixxOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1XOESPROC)(GLenum, GLfixed);\nGLAPI PFNGLMULTITEXCOORD1XOESPROC glad_glMultiTexCoord1xOES;\n#define glMultiTexCoord1xOES glad_glMultiTexCoord1xOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD1XVOESPROC)(GLenum, const GLfixed*);\nGLAPI PFNGLMULTITEXCOORD1XVOESPROC glad_glMultiTexCoord1xvOES;\n#define glMultiTexCoord1xvOES glad_glMultiTexCoord1xvOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2XOESPROC)(GLenum, GLfixed, GLfixed);\nGLAPI PFNGLMULTITEXCOORD2XOESPROC glad_glMultiTexCoord2xOES;\n#define glMultiTexCoord2xOES glad_glMultiTexCoord2xOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD2XVOESPROC)(GLenum, const GLfixed*);\nGLAPI PFNGLMULTITEXCOORD2XVOESPROC glad_glMultiTexCoord2xvOES;\n#define glMultiTexCoord2xvOES glad_glMultiTexCoord2xvOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3XOESPROC)(GLenum, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLMULTITEXCOORD3XOESPROC glad_glMultiTexCoord3xOES;\n#define glMultiTexCoord3xOES glad_glMultiTexCoord3xOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD3XVOESPROC)(GLenum, const GLfixed*);\nGLAPI PFNGLMULTITEXCOORD3XVOESPROC glad_glMultiTexCoord3xvOES;\n#define glMultiTexCoord3xvOES glad_glMultiTexCoord3xvOES\ntypedef void (APIENTRYP PFNGLMULTITEXCOORD4XVOESPROC)(GLenum, const GLfixed*);\nGLAPI PFNGLMULTITEXCOORD4XVOESPROC glad_glMultiTexCoord4xvOES;\n#define glMultiTexCoord4xvOES glad_glMultiTexCoord4xvOES\ntypedef void (APIENTRYP PFNGLNORMAL3XVOESPROC)(const GLfixed*);\nGLAPI PFNGLNORMAL3XVOESPROC glad_glNormal3xvOES;\n#define glNormal3xvOES glad_glNormal3xvOES\ntypedef void (APIENTRYP PFNGLPASSTHROUGHXOESPROC)(GLfixed);\nGLAPI PFNGLPASSTHROUGHXOESPROC glad_glPassThroughxOES;\n#define glPassThroughxOES glad_glPassThroughxOES\ntypedef void (APIENTRYP PFNGLPIXELMAPXPROC)(GLenum, GLint, const GLfixed*);\nGLAPI PFNGLPIXELMAPXPROC glad_glPixelMapx;\n#define glPixelMapx glad_glPixelMapx\ntypedef void (APIENTRYP PFNGLPIXELSTOREXPROC)(GLenum, GLfixed);\nGLAPI PFNGLPIXELSTOREXPROC glad_glPixelStorex;\n#define glPixelStorex glad_glPixelStorex\ntypedef void (APIENTRYP PFNGLPIXELTRANSFERXOESPROC)(GLenum, GLfixed);\nGLAPI PFNGLPIXELTRANSFERXOESPROC glad_glPixelTransferxOES;\n#define glPixelTransferxOES glad_glPixelTransferxOES\ntypedef void (APIENTRYP PFNGLPIXELZOOMXOESPROC)(GLfixed, GLfixed);\nGLAPI PFNGLPIXELZOOMXOESPROC glad_glPixelZoomxOES;\n#define glPixelZoomxOES glad_glPixelZoomxOES\ntypedef void (APIENTRYP PFNGLPRIORITIZETEXTURESXOESPROC)(GLsizei, const GLuint*, const GLfixed*);\nGLAPI PFNGLPRIORITIZETEXTURESXOESPROC glad_glPrioritizeTexturesxOES;\n#define glPrioritizeTexturesxOES glad_glPrioritizeTexturesxOES\ntypedef void (APIENTRYP PFNGLRASTERPOS2XOESPROC)(GLfixed, GLfixed);\nGLAPI PFNGLRASTERPOS2XOESPROC glad_glRasterPos2xOES;\n#define glRasterPos2xOES glad_glRasterPos2xOES\ntypedef void (APIENTRYP PFNGLRASTERPOS2XVOESPROC)(const GLfixed*);\nGLAPI PFNGLRASTERPOS2XVOESPROC glad_glRasterPos2xvOES;\n#define glRasterPos2xvOES glad_glRasterPos2xvOES\ntypedef void (APIENTRYP PFNGLRASTERPOS3XOESPROC)(GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLRASTERPOS3XOESPROC glad_glRasterPos3xOES;\n#define glRasterPos3xOES glad_glRasterPos3xOES\ntypedef void (APIENTRYP PFNGLRASTERPOS3XVOESPROC)(const GLfixed*);\nGLAPI PFNGLRASTERPOS3XVOESPROC glad_glRasterPos3xvOES;\n#define glRasterPos3xvOES glad_glRasterPos3xvOES\ntypedef void (APIENTRYP PFNGLRASTERPOS4XOESPROC)(GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLRASTERPOS4XOESPROC glad_glRasterPos4xOES;\n#define glRasterPos4xOES glad_glRasterPos4xOES\ntypedef void (APIENTRYP PFNGLRASTERPOS4XVOESPROC)(const GLfixed*);\nGLAPI PFNGLRASTERPOS4XVOESPROC glad_glRasterPos4xvOES;\n#define glRasterPos4xvOES glad_glRasterPos4xvOES\ntypedef void (APIENTRYP PFNGLRECTXOESPROC)(GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLRECTXOESPROC glad_glRectxOES;\n#define glRectxOES glad_glRectxOES\ntypedef void (APIENTRYP PFNGLRECTXVOESPROC)(const GLfixed*, const GLfixed*);\nGLAPI PFNGLRECTXVOESPROC glad_glRectxvOES;\n#define glRectxvOES glad_glRectxvOES\ntypedef void (APIENTRYP PFNGLTEXCOORD1XOESPROC)(GLfixed);\nGLAPI PFNGLTEXCOORD1XOESPROC glad_glTexCoord1xOES;\n#define glTexCoord1xOES glad_glTexCoord1xOES\ntypedef void (APIENTRYP PFNGLTEXCOORD1XVOESPROC)(const GLfixed*);\nGLAPI PFNGLTEXCOORD1XVOESPROC glad_glTexCoord1xvOES;\n#define glTexCoord1xvOES glad_glTexCoord1xvOES\ntypedef void (APIENTRYP PFNGLTEXCOORD2XOESPROC)(GLfixed, GLfixed);\nGLAPI PFNGLTEXCOORD2XOESPROC glad_glTexCoord2xOES;\n#define glTexCoord2xOES glad_glTexCoord2xOES\ntypedef void (APIENTRYP PFNGLTEXCOORD2XVOESPROC)(const GLfixed*);\nGLAPI PFNGLTEXCOORD2XVOESPROC glad_glTexCoord2xvOES;\n#define glTexCoord2xvOES glad_glTexCoord2xvOES\ntypedef void (APIENTRYP PFNGLTEXCOORD3XOESPROC)(GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLTEXCOORD3XOESPROC glad_glTexCoord3xOES;\n#define glTexCoord3xOES glad_glTexCoord3xOES\ntypedef void (APIENTRYP PFNGLTEXCOORD3XVOESPROC)(const GLfixed*);\nGLAPI PFNGLTEXCOORD3XVOESPROC glad_glTexCoord3xvOES;\n#define glTexCoord3xvOES glad_glTexCoord3xvOES\ntypedef void (APIENTRYP PFNGLTEXCOORD4XOESPROC)(GLfixed, GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLTEXCOORD4XOESPROC glad_glTexCoord4xOES;\n#define glTexCoord4xOES glad_glTexCoord4xOES\ntypedef void (APIENTRYP PFNGLTEXCOORD4XVOESPROC)(const GLfixed*);\nGLAPI PFNGLTEXCOORD4XVOESPROC glad_glTexCoord4xvOES;\n#define glTexCoord4xvOES glad_glTexCoord4xvOES\ntypedef void (APIENTRYP PFNGLTEXGENXOESPROC)(GLenum, GLenum, GLfixed);\nGLAPI PFNGLTEXGENXOESPROC glad_glTexGenxOES;\n#define glTexGenxOES glad_glTexGenxOES\ntypedef void (APIENTRYP PFNGLTEXGENXVOESPROC)(GLenum, GLenum, const GLfixed*);\nGLAPI PFNGLTEXGENXVOESPROC glad_glTexGenxvOES;\n#define glTexGenxvOES glad_glTexGenxvOES\ntypedef void (APIENTRYP PFNGLVERTEX2XOESPROC)(GLfixed);\nGLAPI PFNGLVERTEX2XOESPROC glad_glVertex2xOES;\n#define glVertex2xOES glad_glVertex2xOES\ntypedef void (APIENTRYP PFNGLVERTEX2XVOESPROC)(const GLfixed*);\nGLAPI PFNGLVERTEX2XVOESPROC glad_glVertex2xvOES;\n#define glVertex2xvOES glad_glVertex2xvOES\ntypedef void (APIENTRYP PFNGLVERTEX3XOESPROC)(GLfixed, GLfixed);\nGLAPI PFNGLVERTEX3XOESPROC glad_glVertex3xOES;\n#define glVertex3xOES glad_glVertex3xOES\ntypedef void (APIENTRYP PFNGLVERTEX3XVOESPROC)(const GLfixed*);\nGLAPI PFNGLVERTEX3XVOESPROC glad_glVertex3xvOES;\n#define glVertex3xvOES glad_glVertex3xvOES\ntypedef void (APIENTRYP PFNGLVERTEX4XOESPROC)(GLfixed, GLfixed, GLfixed);\nGLAPI PFNGLVERTEX4XOESPROC glad_glVertex4xOES;\n#define glVertex4xOES glad_glVertex4xOES\ntypedef void (APIENTRYP PFNGLVERTEX4XVOESPROC)(const GLfixed*);\nGLAPI PFNGLVERTEX4XVOESPROC glad_glVertex4xvOES;\n#define glVertex4xvOES glad_glVertex4xvOES\n#endif\n#ifndef GL_EXT_framebuffer_multisample\n#define GL_EXT_framebuffer_multisample 1\nGLAPI int GLAD_GL_EXT_framebuffer_multisample;\ntypedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei);\nGLAPI PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT;\n#define glRenderbufferStorageMultisampleEXT glad_glRenderbufferStorageMultisampleEXT\n#endif\n#ifndef GL_ARB_gpu_shader5\n#define GL_ARB_gpu_shader5 1\nGLAPI int GLAD_GL_ARB_gpu_shader5;\n#endif\n#ifndef GL_SGIS_texture4D\n#define GL_SGIS_texture4D 1\nGLAPI int GLAD_GL_SGIS_texture4D;\ntypedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXIMAGE4DSGISPROC glad_glTexImage4DSGIS;\n#define glTexImage4DSGIS glad_glTexImage4DSGIS\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC)(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXSUBIMAGE4DSGISPROC glad_glTexSubImage4DSGIS;\n#define glTexSubImage4DSGIS glad_glTexSubImage4DSGIS\n#endif\n#ifndef GL_EXT_texture3D\n#define GL_EXT_texture3D 1\nGLAPI int GLAD_GL_EXT_texture3D;\ntypedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, GLint, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXIMAGE3DEXTPROC glad_glTexImage3DEXT;\n#define glTexImage3DEXT glad_glTexImage3DEXT\ntypedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLenum, GLenum, const void*);\nGLAPI PFNGLTEXSUBIMAGE3DEXTPROC glad_glTexSubImage3DEXT;\n#define glTexSubImage3DEXT glad_glTexSubImage3DEXT\n#endif\n#ifndef GL_EXT_multisample\n#define GL_EXT_multisample 1\nGLAPI int GLAD_GL_EXT_multisample;\ntypedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC)(GLclampf, GLboolean);\nGLAPI PFNGLSAMPLEMASKEXTPROC glad_glSampleMaskEXT;\n#define glSampleMaskEXT glad_glSampleMaskEXT\ntypedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC)(GLenum);\nGLAPI PFNGLSAMPLEPATTERNEXTPROC glad_glSamplePatternEXT;\n#define glSamplePatternEXT glad_glSamplePatternEXT\n#endif\n#ifndef GL_EXT_secondary_color\n#define GL_EXT_secondary_color 1\nGLAPI int GLAD_GL_EXT_secondary_color;\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC)(GLbyte, GLbyte, GLbyte);\nGLAPI PFNGLSECONDARYCOLOR3BEXTPROC glad_glSecondaryColor3bEXT;\n#define glSecondaryColor3bEXT glad_glSecondaryColor3bEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC)(const GLbyte*);\nGLAPI PFNGLSECONDARYCOLOR3BVEXTPROC glad_glSecondaryColor3bvEXT;\n#define glSecondaryColor3bvEXT glad_glSecondaryColor3bvEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC)(GLdouble, GLdouble, GLdouble);\nGLAPI PFNGLSECONDARYCOLOR3DEXTPROC glad_glSecondaryColor3dEXT;\n#define glSecondaryColor3dEXT glad_glSecondaryColor3dEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC)(const GLdouble*);\nGLAPI PFNGLSECONDARYCOLOR3DVEXTPROC glad_glSecondaryColor3dvEXT;\n#define glSecondaryColor3dvEXT glad_glSecondaryColor3dvEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC)(GLfloat, GLfloat, GLfloat);\nGLAPI PFNGLSECONDARYCOLOR3FEXTPROC glad_glSecondaryColor3fEXT;\n#define glSecondaryColor3fEXT glad_glSecondaryColor3fEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC)(const GLfloat*);\nGLAPI PFNGLSECONDARYCOLOR3FVEXTPROC glad_glSecondaryColor3fvEXT;\n#define glSecondaryColor3fvEXT glad_glSecondaryColor3fvEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC)(GLint, GLint, GLint);\nGLAPI PFNGLSECONDARYCOLOR3IEXTPROC glad_glSecondaryColor3iEXT;\n#define glSecondaryColor3iEXT glad_glSecondaryColor3iEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC)(const GLint*);\nGLAPI PFNGLSECONDARYCOLOR3IVEXTPROC glad_glSecondaryColor3ivEXT;\n#define glSecondaryColor3ivEXT glad_glSecondaryColor3ivEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC)(GLshort, GLshort, GLshort);\nGLAPI PFNGLSECONDARYCOLOR3SEXTPROC glad_glSecondaryColor3sEXT;\n#define glSecondaryColor3sEXT glad_glSecondaryColor3sEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC)(const GLshort*);\nGLAPI PFNGLSECONDARYCOLOR3SVEXTPROC glad_glSecondaryColor3svEXT;\n#define glSecondaryColor3svEXT glad_glSecondaryColor3svEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC)(GLubyte, GLubyte, GLubyte);\nGLAPI PFNGLSECONDARYCOLOR3UBEXTPROC glad_glSecondaryColor3ubEXT;\n#define glSecondaryColor3ubEXT glad_glSecondaryColor3ubEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC)(const GLubyte*);\nGLAPI PFNGLSECONDARYCOLOR3UBVEXTPROC glad_glSecondaryColor3ubvEXT;\n#define glSecondaryColor3ubvEXT glad_glSecondaryColor3ubvEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC)(GLuint, GLuint, GLuint);\nGLAPI PFNGLSECONDARYCOLOR3UIEXTPROC glad_glSecondaryColor3uiEXT;\n#define glSecondaryColor3uiEXT glad_glSecondaryColor3uiEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC)(const GLuint*);\nGLAPI PFNGLSECONDARYCOLOR3UIVEXTPROC glad_glSecondaryColor3uivEXT;\n#define glSecondaryColor3uivEXT glad_glSecondaryColor3uivEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC)(GLushort, GLushort, GLushort);\nGLAPI PFNGLSECONDARYCOLOR3USEXTPROC glad_glSecondaryColor3usEXT;\n#define glSecondaryColor3usEXT glad_glSecondaryColor3usEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC)(const GLushort*);\nGLAPI PFNGLSECONDARYCOLOR3USVEXTPROC glad_glSecondaryColor3usvEXT;\n#define glSecondaryColor3usvEXT glad_glSecondaryColor3usvEXT\ntypedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC)(GLint, GLenum, GLsizei, const void*);\nGLAPI PFNGLSECONDARYCOLORPOINTEREXTPROC glad_glSecondaryColorPointerEXT;\n#define glSecondaryColorPointerEXT glad_glSecondaryColorPointerEXT\n#endif\n#ifndef GL_NV_parameter_buffer_object2\n#define GL_NV_parameter_buffer_object2 1\nGLAPI int GLAD_GL_NV_parameter_buffer_object2;\n#endif\n#ifndef GL_ATI_vertex_array_object\n#define GL_ATI_vertex_array_object 1\nGLAPI int GLAD_GL_ATI_vertex_array_object;\ntypedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC)(GLsizei, const void*, GLenum);\nGLAPI PFNGLNEWOBJECTBUFFERATIPROC glad_glNewObjectBufferATI;\n#define glNewObjectBufferATI glad_glNewObjectBufferATI\ntypedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC)(GLuint);\nGLAPI PFNGLISOBJECTBUFFERATIPROC glad_glIsObjectBufferATI;\n#define glIsObjectBufferATI glad_glIsObjectBufferATI\ntypedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC)(GLuint, GLuint, GLsizei, const void*, GLenum);\nGLAPI PFNGLUPDATEOBJECTBUFFERATIPROC glad_glUpdateObjectBufferATI;\n#define glUpdateObjectBufferATI glad_glUpdateObjectBufferATI\ntypedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETOBJECTBUFFERFVATIPROC glad_glGetObjectBufferfvATI;\n#define glGetObjectBufferfvATI glad_glGetObjectBufferfvATI\ntypedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETOBJECTBUFFERIVATIPROC glad_glGetObjectBufferivATI;\n#define glGetObjectBufferivATI glad_glGetObjectBufferivATI\ntypedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC)(GLuint);\nGLAPI PFNGLFREEOBJECTBUFFERATIPROC glad_glFreeObjectBufferATI;\n#define glFreeObjectBufferATI glad_glFreeObjectBufferATI\ntypedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC)(GLenum, GLint, GLenum, GLsizei, GLuint, GLuint);\nGLAPI PFNGLARRAYOBJECTATIPROC glad_glArrayObjectATI;\n#define glArrayObjectATI glad_glArrayObjectATI\ntypedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC)(GLenum, GLenum, GLfloat*);\nGLAPI PFNGLGETARRAYOBJECTFVATIPROC glad_glGetArrayObjectfvATI;\n#define glGetArrayObjectfvATI glad_glGetArrayObjectfvATI\ntypedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC)(GLenum, GLenum, GLint*);\nGLAPI PFNGLGETARRAYOBJECTIVATIPROC glad_glGetArrayObjectivATI;\n#define glGetArrayObjectivATI glad_glGetArrayObjectivATI\ntypedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC)(GLuint, GLenum, GLsizei, GLuint, GLuint);\nGLAPI PFNGLVARIANTARRAYOBJECTATIPROC glad_glVariantArrayObjectATI;\n#define glVariantArrayObjectATI glad_glVariantArrayObjectATI\ntypedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC)(GLuint, GLenum, GLfloat*);\nGLAPI PFNGLGETVARIANTARRAYOBJECTFVATIPROC glad_glGetVariantArrayObjectfvATI;\n#define glGetVariantArrayObjectfvATI glad_glGetVariantArrayObjectfvATI\ntypedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC)(GLuint, GLenum, GLint*);\nGLAPI PFNGLGETVARIANTARRAYOBJECTIVATIPROC glad_glGetVariantArrayObjectivATI;\n#define glGetVariantArrayObjectivATI glad_glGetVariantArrayObjectivATI\n#endif\n#ifndef GL_NVX_gpu_memory_info\n#define GL_NVX_gpu_memory_info 1\nGLAPI int GLAD_GL_NVX_gpu_memory_info;\n#endif\n#ifndef GL_ARB_sparse_texture\n#define GL_ARB_sparse_texture 1\nGLAPI int GLAD_GL_ARB_sparse_texture;\ntypedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC)(GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei, GLboolean);\nGLAPI PFNGLTEXPAGECOMMITMENTARBPROC glad_glTexPageCommitmentARB;\n#define glTexPageCommitmentARB glad_glTexPageCommitmentARB\n#endif\n#ifndef GL_SGIS_point_line_texgen\n#define GL_SGIS_point_line_texgen 1\nGLAPI int GLAD_GL_SGIS_point_line_texgen;\n#endif\n#ifndef GL_EXT_draw_range_elements\n#define GL_EXT_draw_range_elements 1\nGLAPI int GLAD_GL_EXT_draw_range_elements;\ntypedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC)(GLenum, GLuint, GLuint, GLsizei, GLenum, const void*);\nGLAPI PFNGLDRAWRANGEELEMENTSEXTPROC glad_glDrawRangeElementsEXT;\n#define glDrawRangeElementsEXT glad_glDrawRangeElementsEXT\n#endif\n#ifndef GL_SGIX_blend_alpha_minmax\n#define GL_SGIX_blend_alpha_minmax 1\nGLAPI int GLAD_GL_SGIX_blend_alpha_minmax;\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "deps/glad/include/glad/glad_egl.h",
    "content": "/*\n\n    EGL loader generated by glad 0.1.36 on Tue Oct 22 13:32:48 2024.\n\n    Language/Generator: C/C++\n    Specification: egl\n    APIs: egl=1.5\n    Profile: -\n    Extensions:\n        EGL_ANDROID_native_fence_sync,\n        EGL_EXT_device_base,\n        EGL_EXT_device_drm_render_node,\n        EGL_EXT_image_dma_buf_import,\n        EGL_EXT_image_dma_buf_import_modifiers,\n        EGL_EXT_platform_base,\n        EGL_EXT_platform_wayland,\n        EGL_EXT_platform_x11,\n        EGL_EXT_platform_xcb,\n        EGL_KHR_create_context,\n        EGL_KHR_image_base,\n        EGL_KHR_image_pixmap,\n        EGL_KHR_platform_wayland,\n        EGL_KHR_platform_x11,\n        EGL_MESA_image_dma_buf_export\n    Loader: True\n    Local files: False\n    Omit khrplatform: False\n    Reproducible: False\n\n    Commandline:\n        --api=\"egl=1.5\" --generator=\"c\" --spec=\"egl\" --extensions=\"EGL_ANDROID_native_fence_sync,EGL_EXT_device_base,EGL_EXT_device_drm_render_node,EGL_EXT_image_dma_buf_import,EGL_EXT_image_dma_buf_import_modifiers,EGL_EXT_platform_base,EGL_EXT_platform_wayland,EGL_EXT_platform_x11,EGL_EXT_platform_xcb,EGL_KHR_create_context,EGL_KHR_image_base,EGL_KHR_image_pixmap,EGL_KHR_platform_wayland,EGL_KHR_platform_x11,EGL_MESA_image_dma_buf_export\"\n    Online:\n        https://glad.dav1d.de/#language=c&specification=egl&loader=on&api=egl%3D1.5&extensions=EGL_ANDROID_native_fence_sync&extensions=EGL_EXT_device_base&extensions=EGL_EXT_device_drm_render_node&extensions=EGL_EXT_image_dma_buf_import&extensions=EGL_EXT_image_dma_buf_import_modifiers&extensions=EGL_EXT_platform_base&extensions=EGL_EXT_platform_wayland&extensions=EGL_EXT_platform_x11&extensions=EGL_EXT_platform_xcb&extensions=EGL_KHR_create_context&extensions=EGL_KHR_image_base&extensions=EGL_KHR_image_pixmap&extensions=EGL_KHR_platform_wayland&extensions=EGL_KHR_platform_x11&extensions=EGL_MESA_image_dma_buf_export\n*/\n\n\n#ifndef __glad_egl_h_\n\n#ifdef __egl_h_\n#error EGL header already included, remove this include, glad already provides it\n#endif\n\n#define __glad_egl_h_\n#define __egl_h_\n\n#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)\n#define APIENTRY __stdcall\n#endif\n\n#ifndef APIENTRY\n#define APIENTRY\n#endif\n#ifndef APIENTRYP\n#define APIENTRYP APIENTRY *\n#endif\n#ifndef GLAPI\n#define GLAPI extern\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void* (* GLADloadproc)(const char *name);\n\nGLAPI int gladLoadEGL(void);\nGLAPI int gladLoadEGLLoader(GLADloadproc);\n\n#include <KHR/khrplatform.h>\n#include <EGL/eglplatform.h>\nstruct AHardwareBuffer;\nstruct wl_buffer;\nstruct wl_display;\nstruct wl_resource;\ntypedef unsigned int EGLBoolean;\ntypedef unsigned int EGLenum;\ntypedef intptr_t EGLAttribKHR;\ntypedef intptr_t EGLAttrib;\ntypedef void *EGLClientBuffer;\ntypedef void *EGLConfig;\ntypedef void *EGLContext;\ntypedef void *EGLDeviceEXT;\ntypedef void *EGLDisplay;\ntypedef void *EGLImage;\ntypedef void *EGLImageKHR;\ntypedef void *EGLLabelKHR;\ntypedef void *EGLObjectKHR;\ntypedef void *EGLOutputLayerEXT;\ntypedef void *EGLOutputPortEXT;\ntypedef void *EGLStreamKHR;\ntypedef void *EGLSurface;\ntypedef void *EGLSync;\ntypedef void *EGLSyncKHR;\ntypedef void *EGLSyncNV;\ntypedef void (*__eglMustCastToProperFunctionPointerType)(void);\ntypedef khronos_utime_nanoseconds_t EGLTimeKHR;\ntypedef khronos_utime_nanoseconds_t EGLTime;\ntypedef khronos_utime_nanoseconds_t EGLTimeNV;\ntypedef khronos_utime_nanoseconds_t EGLuint64NV;\ntypedef khronos_uint64_t EGLuint64KHR;\ntypedef khronos_stime_nanoseconds_t EGLnsecsANDROID;\ntypedef int EGLNativeFileDescriptorKHR;\ntypedef khronos_ssize_t EGLsizeiANDROID;\ntypedef void (*EGLSetBlobFuncANDROID) (const void *key, EGLsizeiANDROID keySize, const void *value, EGLsizeiANDROID valueSize);\ntypedef EGLsizeiANDROID (*EGLGetBlobFuncANDROID) (const void *key, EGLsizeiANDROID keySize, void *value, EGLsizeiANDROID valueSize);\nstruct EGLClientPixmapHI {\n    void  *pData;\n    EGLint iWidth;\n    EGLint iHeight;\n    EGLint iStride;\n};\ntypedef void (APIENTRY *EGLDEBUGPROCKHR)(EGLenum error,const char *command,EGLint messageType,EGLLabelKHR threadLabel,EGLLabelKHR objectLabel,const char* message);\n#define PFNEGLBINDWAYLANDDISPLAYWL PFNEGLBINDWAYLANDDISPLAYWLPROC\n#define PFNEGLUNBINDWAYLANDDISPLAYWL PFNEGLUNBINDWAYLANDDISPLAYWLPROC\n#define PFNEGLQUERYWAYLANDBUFFERWL PFNEGLQUERYWAYLANDBUFFERWLPROC\n#define PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWL PFNEGLCREATEWAYLANDBUFFERFROMIMAGEWLPROC\n#define EGL_ALPHA_SIZE 0x3021\n#define EGL_BAD_ACCESS 0x3002\n#define EGL_BAD_ALLOC 0x3003\n#define EGL_BAD_ATTRIBUTE 0x3004\n#define EGL_BAD_CONFIG 0x3005\n#define EGL_BAD_CONTEXT 0x3006\n#define EGL_BAD_CURRENT_SURFACE 0x3007\n#define EGL_BAD_DISPLAY 0x3008\n#define EGL_BAD_MATCH 0x3009\n#define EGL_BAD_NATIVE_PIXMAP 0x300A\n#define EGL_BAD_NATIVE_WINDOW 0x300B\n#define EGL_BAD_PARAMETER 0x300C\n#define EGL_BAD_SURFACE 0x300D\n#define EGL_BLUE_SIZE 0x3022\n#define EGL_BUFFER_SIZE 0x3020\n#define EGL_CONFIG_CAVEAT 0x3027\n#define EGL_CONFIG_ID 0x3028\n#define EGL_CORE_NATIVE_ENGINE 0x305B\n#define EGL_DEPTH_SIZE 0x3025\n#define EGL_DONT_CARE EGL_CAST(EGLint,-1)\n#define EGL_DRAW 0x3059\n#define EGL_EXTENSIONS 0x3055\n#define EGL_FALSE 0\n#define EGL_GREEN_SIZE 0x3023\n#define EGL_HEIGHT 0x3056\n#define EGL_LARGEST_PBUFFER 0x3058\n#define EGL_LEVEL 0x3029\n#define EGL_MAX_PBUFFER_HEIGHT 0x302A\n#define EGL_MAX_PBUFFER_PIXELS 0x302B\n#define EGL_MAX_PBUFFER_WIDTH 0x302C\n#define EGL_NATIVE_RENDERABLE 0x302D\n#define EGL_NATIVE_VISUAL_ID 0x302E\n#define EGL_NATIVE_VISUAL_TYPE 0x302F\n#define EGL_NONE 0x3038\n#define EGL_NON_CONFORMANT_CONFIG 0x3051\n#define EGL_NOT_INITIALIZED 0x3001\n#define EGL_NO_CONTEXT EGL_CAST(EGLContext,0)\n#define EGL_NO_DISPLAY EGL_CAST(EGLDisplay,0)\n#define EGL_NO_SURFACE EGL_CAST(EGLSurface,0)\n#define EGL_PBUFFER_BIT 0x0001\n#define EGL_PIXMAP_BIT 0x0002\n#define EGL_READ 0x305A\n#define EGL_RED_SIZE 0x3024\n#define EGL_SAMPLES 0x3031\n#define EGL_SAMPLE_BUFFERS 0x3032\n#define EGL_SLOW_CONFIG 0x3050\n#define EGL_STENCIL_SIZE 0x3026\n#define EGL_SUCCESS 0x3000\n#define EGL_SURFACE_TYPE 0x3033\n#define EGL_TRANSPARENT_BLUE_VALUE 0x3035\n#define EGL_TRANSPARENT_GREEN_VALUE 0x3036\n#define EGL_TRANSPARENT_RED_VALUE 0x3037\n#define EGL_TRANSPARENT_RGB 0x3052\n#define EGL_TRANSPARENT_TYPE 0x3034\n#define EGL_TRUE 1\n#define EGL_VENDOR 0x3053\n#define EGL_VERSION 0x3054\n#define EGL_WIDTH 0x3057\n#define EGL_WINDOW_BIT 0x0004\n#define EGL_BACK_BUFFER 0x3084\n#define EGL_BIND_TO_TEXTURE_RGB 0x3039\n#define EGL_BIND_TO_TEXTURE_RGBA 0x303A\n#define EGL_CONTEXT_LOST 0x300E\n#define EGL_MIN_SWAP_INTERVAL 0x303B\n#define EGL_MAX_SWAP_INTERVAL 0x303C\n#define EGL_MIPMAP_TEXTURE 0x3082\n#define EGL_MIPMAP_LEVEL 0x3083\n#define EGL_NO_TEXTURE 0x305C\n#define EGL_TEXTURE_2D 0x305F\n#define EGL_TEXTURE_FORMAT 0x3080\n#define EGL_TEXTURE_RGB 0x305D\n#define EGL_TEXTURE_RGBA 0x305E\n#define EGL_TEXTURE_TARGET 0x3081\n#define EGL_ALPHA_FORMAT 0x3088\n#define EGL_ALPHA_FORMAT_NONPRE 0x308B\n#define EGL_ALPHA_FORMAT_PRE 0x308C\n#define EGL_ALPHA_MASK_SIZE 0x303E\n#define EGL_BUFFER_PRESERVED 0x3094\n#define EGL_BUFFER_DESTROYED 0x3095\n#define EGL_CLIENT_APIS 0x308D\n#define EGL_COLORSPACE 0x3087\n#define EGL_COLORSPACE_sRGB 0x3089\n#define EGL_COLORSPACE_LINEAR 0x308A\n#define EGL_COLOR_BUFFER_TYPE 0x303F\n#define EGL_CONTEXT_CLIENT_TYPE 0x3097\n#define EGL_DISPLAY_SCALING 10000\n#define EGL_HORIZONTAL_RESOLUTION 0x3090\n#define EGL_LUMINANCE_BUFFER 0x308F\n#define EGL_LUMINANCE_SIZE 0x303D\n#define EGL_OPENGL_ES_BIT 0x0001\n#define EGL_OPENVG_BIT 0x0002\n#define EGL_OPENGL_ES_API 0x30A0\n#define EGL_OPENVG_API 0x30A1\n#define EGL_OPENVG_IMAGE 0x3096\n#define EGL_PIXEL_ASPECT_RATIO 0x3092\n#define EGL_RENDERABLE_TYPE 0x3040\n#define EGL_RENDER_BUFFER 0x3086\n#define EGL_RGB_BUFFER 0x308E\n#define EGL_SINGLE_BUFFER 0x3085\n#define EGL_SWAP_BEHAVIOR 0x3093\n#define EGL_UNKNOWN EGL_CAST(EGLint,-1)\n#define EGL_VERTICAL_RESOLUTION 0x3091\n#define EGL_CONFORMANT 0x3042\n#define EGL_CONTEXT_CLIENT_VERSION 0x3098\n#define EGL_MATCH_NATIVE_PIXMAP 0x3041\n#define EGL_OPENGL_ES2_BIT 0x0004\n#define EGL_VG_ALPHA_FORMAT 0x3088\n#define EGL_VG_ALPHA_FORMAT_NONPRE 0x308B\n#define EGL_VG_ALPHA_FORMAT_PRE 0x308C\n#define EGL_VG_ALPHA_FORMAT_PRE_BIT 0x0040\n#define EGL_VG_COLORSPACE 0x3087\n#define EGL_VG_COLORSPACE_sRGB 0x3089\n#define EGL_VG_COLORSPACE_LINEAR 0x308A\n#define EGL_VG_COLORSPACE_LINEAR_BIT 0x0020\n#define EGL_DEFAULT_DISPLAY EGL_CAST(EGLNativeDisplayType,0)\n#define EGL_MULTISAMPLE_RESOLVE_BOX_BIT 0x0200\n#define EGL_MULTISAMPLE_RESOLVE 0x3099\n#define EGL_MULTISAMPLE_RESOLVE_DEFAULT 0x309A\n#define EGL_MULTISAMPLE_RESOLVE_BOX 0x309B\n#define EGL_OPENGL_API 0x30A2\n#define EGL_OPENGL_BIT 0x0008\n#define EGL_SWAP_BEHAVIOR_PRESERVED_BIT 0x0400\n#define EGL_CONTEXT_MAJOR_VERSION 0x3098\n#define EGL_CONTEXT_MINOR_VERSION 0x30FB\n#define EGL_CONTEXT_OPENGL_PROFILE_MASK 0x30FD\n#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY 0x31BD\n#define EGL_NO_RESET_NOTIFICATION 0x31BE\n#define EGL_LOSE_CONTEXT_ON_RESET 0x31BF\n#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT 0x00000001\n#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT 0x00000002\n#define EGL_CONTEXT_OPENGL_DEBUG 0x31B0\n#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE 0x31B1\n#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS 0x31B2\n#define EGL_OPENGL_ES3_BIT 0x00000040\n#define EGL_CL_EVENT_HANDLE 0x309C\n#define EGL_SYNC_CL_EVENT 0x30FE\n#define EGL_SYNC_CL_EVENT_COMPLETE 0x30FF\n#define EGL_SYNC_PRIOR_COMMANDS_COMPLETE 0x30F0\n#define EGL_SYNC_TYPE 0x30F7\n#define EGL_SYNC_STATUS 0x30F1\n#define EGL_SYNC_CONDITION 0x30F8\n#define EGL_SIGNALED 0x30F2\n#define EGL_UNSIGNALED 0x30F3\n#define EGL_SYNC_FLUSH_COMMANDS_BIT 0x0001\n#define EGL_FOREVER 0xFFFFFFFFFFFFFFFF\n#define EGL_TIMEOUT_EXPIRED 0x30F5\n#define EGL_CONDITION_SATISFIED 0x30F6\n#define EGL_NO_SYNC EGL_CAST(EGLSync,0)\n#define EGL_SYNC_FENCE 0x30F9\n#define EGL_GL_COLORSPACE 0x309D\n#define EGL_GL_COLORSPACE_SRGB 0x3089\n#define EGL_GL_COLORSPACE_LINEAR 0x308A\n#define EGL_GL_RENDERBUFFER 0x30B9\n#define EGL_GL_TEXTURE_2D 0x30B1\n#define EGL_GL_TEXTURE_LEVEL 0x30BC\n#define EGL_GL_TEXTURE_3D 0x30B2\n#define EGL_GL_TEXTURE_ZOFFSET 0x30BD\n#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x30B3\n#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x30B4\n#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x30B5\n#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x30B6\n#define EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x30B7\n#define EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x30B8\n#define EGL_IMAGE_PRESERVED 0x30D2\n#define EGL_NO_IMAGE EGL_CAST(EGLImage,0)\nEGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);\nEGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target);\nEGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);\nEGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list);\nEGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list);\nEGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list);\nEGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx);\nEGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface);\nEGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);\nEGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);\nEGLDisplay eglGetCurrentDisplay(void);\nEGLSurface eglGetCurrentSurface(EGLint readdraw);\nEGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);\nEGLint eglGetError(void);\n__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname);\nEGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);\nEGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);\nEGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value);\nconst char *eglQueryString(EGLDisplay dpy, EGLint name);\nEGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint *value);\nEGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface);\nEGLBoolean eglTerminate(EGLDisplay dpy);\nEGLBoolean eglWaitGL(void);\nEGLBoolean eglWaitNative(EGLint engine);\nEGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);\nEGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer);\nEGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value);\nEGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval);\nEGLBoolean eglBindAPI(EGLenum api);\nEGLenum eglQueryAPI(void);\nEGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list);\nEGLBoolean eglReleaseThread(void);\nEGLBoolean eglWaitClient(void);\nEGLContext eglGetCurrentContext(void);\nEGLSync eglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);\nEGLBoolean eglDestroySync(EGLDisplay dpy, EGLSync sync);\nEGLint eglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);\nEGLBoolean eglGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLAttrib *value);\nEGLImage eglCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLAttrib *attrib_list);\nEGLBoolean eglDestroyImage(EGLDisplay dpy, EGLImage image);\nEGLDisplay eglGetPlatformDisplay(EGLenum platform, void *native_display, const EGLAttrib *attrib_list);\nEGLSurface eglCreatePlatformWindowSurface(EGLDisplay dpy, EGLConfig config, void *native_window, const EGLAttrib *attrib_list);\nEGLSurface eglCreatePlatformPixmapSurface(EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLAttrib *attrib_list);\nEGLBoolean eglWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags);\n#define EGL_SYNC_NATIVE_FENCE_ANDROID 0x3144\n#define EGL_SYNC_NATIVE_FENCE_FD_ANDROID 0x3145\n#define EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID 0x3146\n#define EGL_NO_NATIVE_FENCE_FD_ANDROID -1\n#define EGL_NO_DEVICE_EXT EGL_CAST(EGLDeviceEXT,0)\n#define EGL_BAD_DEVICE_EXT 0x322B\n#define EGL_DEVICE_EXT 0x322C\n#define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377\n#define EGL_LINUX_DMA_BUF_EXT 0x3270\n#define EGL_LINUX_DRM_FOURCC_EXT 0x3271\n#define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272\n#define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273\n#define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274\n#define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275\n#define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276\n#define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277\n#define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278\n#define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279\n#define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A\n#define EGL_YUV_COLOR_SPACE_HINT_EXT 0x327B\n#define EGL_SAMPLE_RANGE_HINT_EXT 0x327C\n#define EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT 0x327D\n#define EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT 0x327E\n#define EGL_ITU_REC601_EXT 0x327F\n#define EGL_ITU_REC709_EXT 0x3280\n#define EGL_ITU_REC2020_EXT 0x3281\n#define EGL_YUV_FULL_RANGE_EXT 0x3282\n#define EGL_YUV_NARROW_RANGE_EXT 0x3283\n#define EGL_YUV_CHROMA_SITING_0_EXT 0x3284\n#define EGL_YUV_CHROMA_SITING_0_5_EXT 0x3285\n#define EGL_DMA_BUF_PLANE3_FD_EXT 0x3440\n#define EGL_DMA_BUF_PLANE3_OFFSET_EXT 0x3441\n#define EGL_DMA_BUF_PLANE3_PITCH_EXT 0x3442\n#define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443\n#define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444\n#define EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 0x3445\n#define EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT 0x3446\n#define EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT 0x3447\n#define EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT 0x3448\n#define EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT 0x3449\n#define EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT 0x344A\n#define EGL_PLATFORM_WAYLAND_EXT 0x31D8\n#define EGL_PLATFORM_X11_EXT 0x31D5\n#define EGL_PLATFORM_X11_SCREEN_EXT 0x31D6\n#define EGL_PLATFORM_XCB_EXT 0x31DC\n#define EGL_PLATFORM_XCB_SCREEN_EXT 0x31DE\n#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098\n#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB\n#define EGL_CONTEXT_FLAGS_KHR 0x30FC\n#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD\n#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD\n#define EGL_NO_RESET_NOTIFICATION_KHR 0x31BE\n#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF\n#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001\n#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002\n#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004\n#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001\n#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002\n#define EGL_OPENGL_ES3_BIT_KHR 0x00000040\n#define EGL_IMAGE_PRESERVED_KHR 0x30D2\n#define EGL_NO_IMAGE_KHR EGL_CAST(EGLImageKHR,0)\n#define EGL_NATIVE_PIXMAP_KHR 0x30B0\n#define EGL_PLATFORM_WAYLAND_KHR 0x31D8\n#define EGL_PLATFORM_X11_KHR 0x31D5\n#define EGL_PLATFORM_X11_SCREEN_KHR 0x31D6\n#ifndef EGL_ANDROID_native_fence_sync\n#define EGL_ANDROID_native_fence_sync 1\ntypedef EGLint (APIENTRYP PFNEGLDUPNATIVEFENCEFDANDROIDPROC)(EGLDisplay dpy, EGLSyncKHR sync);\nGLAPI PFNEGLDUPNATIVEFENCEFDANDROIDPROC glad_eglDupNativeFenceFDANDROID;\n#define eglDupNativeFenceFDANDROID glad_eglDupNativeFenceFDANDROID\n#endif\n#ifndef EGL_EXT_device_base\n#define EGL_EXT_device_base 1\ntypedef EGLBoolean (APIENTRYP PFNEGLQUERYDEVICEATTRIBEXTPROC)(EGLDeviceEXT device, EGLint attribute, EGLAttrib *value);\nGLAPI PFNEGLQUERYDEVICEATTRIBEXTPROC glad_eglQueryDeviceAttribEXT;\n#define eglQueryDeviceAttribEXT glad_eglQueryDeviceAttribEXT\ntypedef const char * (APIENTRYP PFNEGLQUERYDEVICESTRINGEXTPROC)(EGLDeviceEXT device, EGLint name);\nGLAPI PFNEGLQUERYDEVICESTRINGEXTPROC glad_eglQueryDeviceStringEXT;\n#define eglQueryDeviceStringEXT glad_eglQueryDeviceStringEXT\ntypedef EGLBoolean (APIENTRYP PFNEGLQUERYDEVICESEXTPROC)(EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices);\nGLAPI PFNEGLQUERYDEVICESEXTPROC glad_eglQueryDevicesEXT;\n#define eglQueryDevicesEXT glad_eglQueryDevicesEXT\ntypedef EGLBoolean (APIENTRYP PFNEGLQUERYDISPLAYATTRIBEXTPROC)(EGLDisplay dpy, EGLint attribute, EGLAttrib *value);\nGLAPI PFNEGLQUERYDISPLAYATTRIBEXTPROC glad_eglQueryDisplayAttribEXT;\n#define eglQueryDisplayAttribEXT glad_eglQueryDisplayAttribEXT\n#endif\n#ifndef EGL_EXT_device_drm_render_node\n#define EGL_EXT_device_drm_render_node 1\n#endif\n#ifndef EGL_EXT_image_dma_buf_import\n#define EGL_EXT_image_dma_buf_import 1\n#endif\n#ifndef EGL_EXT_image_dma_buf_import_modifiers\n#define EGL_EXT_image_dma_buf_import_modifiers 1\ntypedef EGLBoolean (APIENTRYP PFNEGLQUERYDMABUFFORMATSEXTPROC)(EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats);\nGLAPI PFNEGLQUERYDMABUFFORMATSEXTPROC glad_eglQueryDmaBufFormatsEXT;\n#define eglQueryDmaBufFormatsEXT glad_eglQueryDmaBufFormatsEXT\ntypedef EGLBoolean (APIENTRYP PFNEGLQUERYDMABUFMODIFIERSEXTPROC)(EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers);\nGLAPI PFNEGLQUERYDMABUFMODIFIERSEXTPROC glad_eglQueryDmaBufModifiersEXT;\n#define eglQueryDmaBufModifiersEXT glad_eglQueryDmaBufModifiersEXT\n#endif\n#ifndef EGL_EXT_platform_base\n#define EGL_EXT_platform_base 1\ntypedef EGLDisplay (APIENTRYP PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum platform, void *native_display, const EGLint *attrib_list);\nGLAPI PFNEGLGETPLATFORMDISPLAYEXTPROC glad_eglGetPlatformDisplayEXT;\n#define eglGetPlatformDisplayEXT glad_eglGetPlatformDisplayEXT\ntypedef EGLSurface (APIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)(EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list);\nGLAPI PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC glad_eglCreatePlatformWindowSurfaceEXT;\n#define eglCreatePlatformWindowSurfaceEXT glad_eglCreatePlatformWindowSurfaceEXT\ntypedef EGLSurface (APIENTRYP PFNEGLCREATEPLATFORMPIXMAPSURFACEEXTPROC)(EGLDisplay dpy, EGLConfig config, void *native_pixmap, const EGLint *attrib_list);\nGLAPI PFNEGLCREATEPLATFORMPIXMAPSURFACEEXTPROC glad_eglCreatePlatformPixmapSurfaceEXT;\n#define eglCreatePlatformPixmapSurfaceEXT glad_eglCreatePlatformPixmapSurfaceEXT\n#endif\n#ifndef EGL_EXT_platform_wayland\n#define EGL_EXT_platform_wayland 1\n#endif\n#ifndef EGL_EXT_platform_x11\n#define EGL_EXT_platform_x11 1\n#endif\n#ifndef EGL_EXT_platform_xcb\n#define EGL_EXT_platform_xcb 1\n#endif\n#ifndef EGL_KHR_create_context\n#define EGL_KHR_create_context 1\n#endif\n#ifndef EGL_KHR_image_base\n#define EGL_KHR_image_base 1\ntypedef EGLImageKHR (APIENTRYP PFNEGLCREATEIMAGEKHRPROC)(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);\nGLAPI PFNEGLCREATEIMAGEKHRPROC glad_eglCreateImageKHR;\n#define eglCreateImageKHR glad_eglCreateImageKHR\ntypedef EGLBoolean (APIENTRYP PFNEGLDESTROYIMAGEKHRPROC)(EGLDisplay dpy, EGLImageKHR image);\nGLAPI PFNEGLDESTROYIMAGEKHRPROC glad_eglDestroyImageKHR;\n#define eglDestroyImageKHR glad_eglDestroyImageKHR\n#endif\n#ifndef EGL_KHR_image_pixmap\n#define EGL_KHR_image_pixmap 1\n#endif\n#ifndef EGL_KHR_platform_wayland\n#define EGL_KHR_platform_wayland 1\n#endif\n#ifndef EGL_KHR_platform_x11\n#define EGL_KHR_platform_x11 1\n#endif\n#ifndef EGL_MESA_image_dma_buf_export\n#define EGL_MESA_image_dma_buf_export 1\ntypedef EGLBoolean (APIENTRYP PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC)(EGLDisplay dpy, EGLImageKHR image, int *fourcc, int *num_planes, EGLuint64KHR *modifiers);\nGLAPI PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC glad_eglExportDMABUFImageQueryMESA;\n#define eglExportDMABUFImageQueryMESA glad_eglExportDMABUFImageQueryMESA\ntypedef EGLBoolean (APIENTRYP PFNEGLEXPORTDMABUFIMAGEMESAPROC)(EGLDisplay dpy, EGLImageKHR image, int *fds, EGLint *strides, EGLint *offsets);\nGLAPI PFNEGLEXPORTDMABUFIMAGEMESAPROC glad_eglExportDMABUFImageMESA;\n#define eglExportDMABUFImageMESA glad_eglExportDMABUFImageMESA\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "deps/glad/include/glad/glad_wgl.h",
    "content": "\n#ifndef WINAPI\n# ifndef WIN32_LEAN_AND_MEAN\n#  define WIN32_LEAN_AND_MEAN 1\n# endif\n# include <windows.h>\n#endif\n\n#include <glad/glad.h>\n\n#ifndef __glad_wglext_h_\n\n#ifdef __wglext_h_\n#error WGL header already included, remove this include, glad already provides it\n#endif\n\n#define __glad_wglext_h_\n#define __wglext_h_\n\n#ifndef APIENTRY\n#define APIENTRY\n#endif\n#ifndef APIENTRYP\n#define APIENTRYP APIENTRY *\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void* (* GLADloadproc)(const char *name);\n\n#ifndef GLAPI\n# if defined(GLAD_GLAPI_EXPORT)\n#  if defined(WIN32) || defined(__CYGWIN__)\n#   if defined(GLAD_GLAPI_EXPORT_BUILD)\n#    if defined(__GNUC__)\n#     define GLAPI __attribute__ ((dllexport)) extern\n#    else\n#     define GLAPI __declspec(dllexport) extern\n#    endif\n#   else\n#    if defined(__GNUC__)\n#     define GLAPI __attribute__ ((dllimport)) extern\n#    else\n#     define GLAPI __declspec(dllimport) extern\n#    endif\n#   endif\n#  elif defined(__GNUC__) && defined(GLAD_GLAPI_EXPORT_BUILD)\n#   define GLAPI __attribute__ ((visibility (\"default\"))) extern\n#  else\n#   define GLAPI extern\n#  endif\n# else\n#  define GLAPI extern\n# endif\n#endif\n\nGLAPI int gladLoadWGL(HDC hdc);\n\nGLAPI void gladLoadWGLLoader(GLADloadproc, HDC hdc);\n\nstruct _GPU_DEVICE {\n    DWORD  cb;\n    CHAR   DeviceName[32];\n    CHAR   DeviceString[128];\n    DWORD  Flags;\n    RECT   rcVirtualScreen;\n};\nDECLARE_HANDLE(HPBUFFERARB);\nDECLARE_HANDLE(HPBUFFEREXT);\nDECLARE_HANDLE(HVIDEOOUTPUTDEVICENV);\nDECLARE_HANDLE(HPVIDEODEV);\nDECLARE_HANDLE(HPGPUNV);\nDECLARE_HANDLE(HGPUNV);\nDECLARE_HANDLE(HVIDEOINPUTDEVICENV);\ntypedef struct _GPU_DEVICE GPU_DEVICE;\ntypedef struct _GPU_DEVICE *PGPU_DEVICE;\n#define WGL_COVERAGE_SAMPLES_NV 0x2042\n#define WGL_COLOR_SAMPLES_NV 0x20B9\n#define WGL_IMAGE_BUFFER_MIN_ACCESS_I3D 0x00000001\n#define WGL_IMAGE_BUFFER_LOCK_I3D 0x00000002\n#define WGL_FLOAT_COMPONENTS_NV 0x20B0\n#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_R_NV 0x20B1\n#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RG_NV 0x20B2\n#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV 0x20B3\n#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV 0x20B4\n#define WGL_TEXTURE_FLOAT_R_NV 0x20B5\n#define WGL_TEXTURE_FLOAT_RG_NV 0x20B6\n#define WGL_TEXTURE_FLOAT_RGB_NV 0x20B7\n#define WGL_TEXTURE_FLOAT_RGBA_NV 0x20B8\n#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0\n#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001\n#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002\n#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091\n#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092\n#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093\n#define WGL_CONTEXT_FLAGS_ARB 0x2094\n#define ERROR_INVALID_VERSION_ARB 0x2095\n#define ERROR_INCOMPATIBLE_AFFINITY_MASKS_NV 0x20D0\n#define ERROR_MISSING_AFFINITY_MASK_NV 0x20D1\n#define WGL_NUMBER_PIXEL_FORMATS_EXT 0x2000\n#define WGL_DRAW_TO_WINDOW_EXT 0x2001\n#define WGL_DRAW_TO_BITMAP_EXT 0x2002\n#define WGL_ACCELERATION_EXT 0x2003\n#define WGL_NEED_PALETTE_EXT 0x2004\n#define WGL_NEED_SYSTEM_PALETTE_EXT 0x2005\n#define WGL_SWAP_LAYER_BUFFERS_EXT 0x2006\n#define WGL_SWAP_METHOD_EXT 0x2007\n#define WGL_NUMBER_OVERLAYS_EXT 0x2008\n#define WGL_NUMBER_UNDERLAYS_EXT 0x2009\n#define WGL_TRANSPARENT_EXT 0x200A\n#define WGL_TRANSPARENT_VALUE_EXT 0x200B\n#define WGL_SHARE_DEPTH_EXT 0x200C\n#define WGL_SHARE_STENCIL_EXT 0x200D\n#define WGL_SHARE_ACCUM_EXT 0x200E\n#define WGL_SUPPORT_GDI_EXT 0x200F\n#define WGL_SUPPORT_OPENGL_EXT 0x2010\n#define WGL_DOUBLE_BUFFER_EXT 0x2011\n#define WGL_STEREO_EXT 0x2012\n#define WGL_PIXEL_TYPE_EXT 0x2013\n#define WGL_COLOR_BITS_EXT 0x2014\n#define WGL_RED_BITS_EXT 0x2015\n#define WGL_RED_SHIFT_EXT 0x2016\n#define WGL_GREEN_BITS_EXT 0x2017\n#define WGL_GREEN_SHIFT_EXT 0x2018\n#define WGL_BLUE_BITS_EXT 0x2019\n#define WGL_BLUE_SHIFT_EXT 0x201A\n#define WGL_ALPHA_BITS_EXT 0x201B\n#define WGL_ALPHA_SHIFT_EXT 0x201C\n#define WGL_ACCUM_BITS_EXT 0x201D\n#define WGL_ACCUM_RED_BITS_EXT 0x201E\n#define WGL_ACCUM_GREEN_BITS_EXT 0x201F\n#define WGL_ACCUM_BLUE_BITS_EXT 0x2020\n#define WGL_ACCUM_ALPHA_BITS_EXT 0x2021\n#define WGL_DEPTH_BITS_EXT 0x2022\n#define WGL_STENCIL_BITS_EXT 0x2023\n#define WGL_AUX_BUFFERS_EXT 0x2024\n#define WGL_NO_ACCELERATION_EXT 0x2025\n#define WGL_GENERIC_ACCELERATION_EXT 0x2026\n#define WGL_FULL_ACCELERATION_EXT 0x2027\n#define WGL_SWAP_EXCHANGE_EXT 0x2028\n#define WGL_SWAP_COPY_EXT 0x2029\n#define WGL_SWAP_UNDEFINED_EXT 0x202A\n#define WGL_TYPE_RGBA_EXT 0x202B\n#define WGL_TYPE_COLORINDEX_EXT 0x202C\n#define WGL_UNIQUE_ID_NV 0x20CE\n#define WGL_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF\n#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV 0x20A0\n#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV 0x20A1\n#define WGL_TEXTURE_RECTANGLE_NV 0x20A2\n#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004\n#define WGL_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008\n#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070\n#define WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071\n#define WGL_TEXTURE_FORMAT_ARB 0x2072\n#define WGL_TEXTURE_TARGET_ARB 0x2073\n#define WGL_MIPMAP_TEXTURE_ARB 0x2074\n#define WGL_TEXTURE_RGB_ARB 0x2075\n#define WGL_TEXTURE_RGBA_ARB 0x2076\n#define WGL_NO_TEXTURE_ARB 0x2077\n#define WGL_TEXTURE_CUBE_MAP_ARB 0x2078\n#define WGL_TEXTURE_1D_ARB 0x2079\n#define WGL_TEXTURE_2D_ARB 0x207A\n#define WGL_MIPMAP_LEVEL_ARB 0x207B\n#define WGL_CUBE_MAP_FACE_ARB 0x207C\n#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D\n#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E\n#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F\n#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080\n#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081\n#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082\n#define WGL_FRONT_LEFT_ARB 0x2083\n#define WGL_FRONT_RIGHT_ARB 0x2084\n#define WGL_BACK_LEFT_ARB 0x2085\n#define WGL_BACK_RIGHT_ARB 0x2086\n#define WGL_AUX0_ARB 0x2087\n#define WGL_AUX1_ARB 0x2088\n#define WGL_AUX2_ARB 0x2089\n#define WGL_AUX3_ARB 0x208A\n#define WGL_AUX4_ARB 0x208B\n#define WGL_AUX5_ARB 0x208C\n#define WGL_AUX6_ARB 0x208D\n#define WGL_AUX7_ARB 0x208E\n#define WGL_AUX8_ARB 0x208F\n#define WGL_AUX9_ARB 0x2090\n#define WGL_DEPTH_FLOAT_EXT 0x2040\n#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000\n#define WGL_DRAW_TO_WINDOW_ARB 0x2001\n#define WGL_DRAW_TO_BITMAP_ARB 0x2002\n#define WGL_ACCELERATION_ARB 0x2003\n#define WGL_NEED_PALETTE_ARB 0x2004\n#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005\n#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006\n#define WGL_SWAP_METHOD_ARB 0x2007\n#define WGL_NUMBER_OVERLAYS_ARB 0x2008\n#define WGL_NUMBER_UNDERLAYS_ARB 0x2009\n#define WGL_TRANSPARENT_ARB 0x200A\n#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037\n#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038\n#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039\n#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A\n#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B\n#define WGL_SHARE_DEPTH_ARB 0x200C\n#define WGL_SHARE_STENCIL_ARB 0x200D\n#define WGL_SHARE_ACCUM_ARB 0x200E\n#define WGL_SUPPORT_GDI_ARB 0x200F\n#define WGL_SUPPORT_OPENGL_ARB 0x2010\n#define WGL_DOUBLE_BUFFER_ARB 0x2011\n#define WGL_STEREO_ARB 0x2012\n#define WGL_PIXEL_TYPE_ARB 0x2013\n#define WGL_COLOR_BITS_ARB 0x2014\n#define WGL_RED_BITS_ARB 0x2015\n#define WGL_RED_SHIFT_ARB 0x2016\n#define WGL_GREEN_BITS_ARB 0x2017\n#define WGL_GREEN_SHIFT_ARB 0x2018\n#define WGL_BLUE_BITS_ARB 0x2019\n#define WGL_BLUE_SHIFT_ARB 0x201A\n#define WGL_ALPHA_BITS_ARB 0x201B\n#define WGL_ALPHA_SHIFT_ARB 0x201C\n#define WGL_ACCUM_BITS_ARB 0x201D\n#define WGL_ACCUM_RED_BITS_ARB 0x201E\n#define WGL_ACCUM_GREEN_BITS_ARB 0x201F\n#define WGL_ACCUM_BLUE_BITS_ARB 0x2020\n#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021\n#define WGL_DEPTH_BITS_ARB 0x2022\n#define WGL_STENCIL_BITS_ARB 0x2023\n#define WGL_AUX_BUFFERS_ARB 0x2024\n#define WGL_NO_ACCELERATION_ARB 0x2025\n#define WGL_GENERIC_ACCELERATION_ARB 0x2026\n#define WGL_FULL_ACCELERATION_ARB 0x2027\n#define WGL_SWAP_EXCHANGE_ARB 0x2028\n#define WGL_SWAP_COPY_ARB 0x2029\n#define WGL_SWAP_UNDEFINED_ARB 0x202A\n#define WGL_TYPE_RGBA_ARB 0x202B\n#define WGL_TYPE_COLORINDEX_ARB 0x202C\n#define WGL_SAMPLE_BUFFERS_ARB 0x2041\n#define WGL_SAMPLES_ARB 0x2042\n#define WGL_GENLOCK_SOURCE_MULTIVIEW_I3D 0x2044\n#define WGL_GENLOCK_SOURCE_EXTERNAL_SYNC_I3D 0x2045\n#define WGL_GENLOCK_SOURCE_EXTERNAL_FIELD_I3D 0x2046\n#define WGL_GENLOCK_SOURCE_EXTERNAL_TTL_I3D 0x2047\n#define WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D 0x2048\n#define WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D 0x2049\n#define WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D 0x204A\n#define WGL_GENLOCK_SOURCE_EDGE_RISING_I3D 0x204B\n#define WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D 0x204C\n#define WGL_ACCESS_READ_ONLY_NV 0x00000000\n#define WGL_ACCESS_READ_WRITE_NV 0x00000001\n#define WGL_ACCESS_WRITE_DISCARD_NV 0x00000002\n#define WGL_STEREO_EMITTER_ENABLE_3DL 0x2055\n#define WGL_STEREO_EMITTER_DISABLE_3DL 0x2056\n#define WGL_STEREO_POLARITY_NORMAL_3DL 0x2057\n#define WGL_STEREO_POLARITY_INVERT_3DL 0x2058\n#define WGL_DRAW_TO_PBUFFER_EXT 0x202D\n#define WGL_MAX_PBUFFER_PIXELS_EXT 0x202E\n#define WGL_MAX_PBUFFER_WIDTH_EXT 0x202F\n#define WGL_MAX_PBUFFER_HEIGHT_EXT 0x2030\n#define WGL_OPTIMAL_PBUFFER_WIDTH_EXT 0x2031\n#define WGL_OPTIMAL_PBUFFER_HEIGHT_EXT 0x2032\n#define WGL_PBUFFER_LARGEST_EXT 0x2033\n#define WGL_PBUFFER_WIDTH_EXT 0x2034\n#define WGL_PBUFFER_HEIGHT_EXT 0x2035\n#define WGL_BIND_TO_VIDEO_RGB_NV 0x20C0\n#define WGL_BIND_TO_VIDEO_RGBA_NV 0x20C1\n#define WGL_BIND_TO_VIDEO_RGB_AND_DEPTH_NV 0x20C2\n#define WGL_VIDEO_OUT_COLOR_NV 0x20C3\n#define WGL_VIDEO_OUT_ALPHA_NV 0x20C4\n#define WGL_VIDEO_OUT_DEPTH_NV 0x20C5\n#define WGL_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6\n#define WGL_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7\n#define WGL_VIDEO_OUT_FRAME 0x20C8\n#define WGL_VIDEO_OUT_FIELD_1 0x20C9\n#define WGL_VIDEO_OUT_FIELD_2 0x20CA\n#define WGL_VIDEO_OUT_STACKED_FIELDS_1_2 0x20CB\n#define WGL_VIDEO_OUT_STACKED_FIELDS_2_1 0x20CC\n#define WGL_SAMPLE_BUFFERS_3DFX 0x2060\n#define WGL_SAMPLES_3DFX 0x2061\n#define WGL_GAMMA_TABLE_SIZE_I3D 0x204E\n#define WGL_GAMMA_EXCLUDE_DESKTOP_I3D 0x204F\n#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9\n#define WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20A9\n#define WGL_NUM_VIDEO_SLOTS_NV 0x20F0\n#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004\n#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004\n#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252\n#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256\n#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261\n#define ERROR_INVALID_PIXEL_TYPE_ARB 0x2043\n#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054\n#define WGL_SAMPLE_BUFFERS_EXT 0x2041\n#define WGL_SAMPLES_EXT 0x2042\n#define WGL_BIND_TO_TEXTURE_DEPTH_NV 0x20A3\n#define WGL_BIND_TO_TEXTURE_RECTANGLE_DEPTH_NV 0x20A4\n#define WGL_DEPTH_TEXTURE_FORMAT_NV 0x20A5\n#define WGL_TEXTURE_DEPTH_COMPONENT_NV 0x20A6\n#define WGL_DEPTH_COMPONENT_NV 0x20A7\n#define WGL_TYPE_RGBA_FLOAT_ATI 0x21A0\n#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126\n#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001\n#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002\n#define ERROR_INVALID_PROFILE_ARB 0x2096\n#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D 0x2050\n#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D 0x2051\n#define WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D 0x2052\n#define WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D 0x2053\n#define WGL_DRAW_TO_PBUFFER_ARB 0x202D\n#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E\n#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F\n#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030\n#define WGL_PBUFFER_LARGEST_ARB 0x2033\n#define WGL_PBUFFER_WIDTH_ARB 0x2034\n#define WGL_PBUFFER_HEIGHT_ARB 0x2035\n#define WGL_PBUFFER_LOST_ARB 0x2036\n#define WGL_GPU_VENDOR_AMD 0x1F00\n#define WGL_GPU_RENDERER_STRING_AMD 0x1F01\n#define WGL_GPU_OPENGL_VERSION_STRING_AMD 0x1F02\n#define WGL_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2\n#define WGL_GPU_RAM_AMD 0x21A3\n#define WGL_GPU_CLOCK_AMD 0x21A4\n#define WGL_GPU_NUM_PIPES_AMD 0x21A5\n#define WGL_GPU_NUM_SIMD_AMD 0x21A6\n#define WGL_GPU_NUM_RB_AMD 0x21A7\n#define WGL_GPU_NUM_SPI_AMD 0x21A8\n#define WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT 0x20A8\n#define ERROR_INVALID_PIXEL_TYPE_EXT 0x2043\n#define WGL_FRONT_COLOR_BUFFER_BIT_ARB 0x00000001\n#define WGL_BACK_COLOR_BUFFER_BIT_ARB 0x00000002\n#define WGL_DEPTH_BUFFER_BIT_ARB 0x00000004\n#define WGL_STENCIL_BUFFER_BIT_ARB 0x00000008\n#ifndef WGL_NV_multisample_coverage\n#define WGL_NV_multisample_coverage 1\nGLAPI int GLAD_WGL_NV_multisample_coverage;\n#endif\n#ifndef WGL_I3D_image_buffer\n#define WGL_I3D_image_buffer 1\nGLAPI int GLAD_WGL_I3D_image_buffer;\ntypedef LPVOID (APIENTRYP PFNWGLCREATEIMAGEBUFFERI3DPROC)(HDC, DWORD, UINT);\nGLAPI PFNWGLCREATEIMAGEBUFFERI3DPROC glad_wglCreateImageBufferI3D;\n#define wglCreateImageBufferI3D glad_wglCreateImageBufferI3D\ntypedef BOOL (APIENTRYP PFNWGLDESTROYIMAGEBUFFERI3DPROC)(HDC, LPVOID);\nGLAPI PFNWGLDESTROYIMAGEBUFFERI3DPROC glad_wglDestroyImageBufferI3D;\n#define wglDestroyImageBufferI3D glad_wglDestroyImageBufferI3D\ntypedef BOOL (APIENTRYP PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC)(HDC, const HANDLE*, const LPVOID*, const DWORD*, UINT);\nGLAPI PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC glad_wglAssociateImageBufferEventsI3D;\n#define wglAssociateImageBufferEventsI3D glad_wglAssociateImageBufferEventsI3D\ntypedef BOOL (APIENTRYP PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC)(HDC, const LPVOID*, UINT);\nGLAPI PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC glad_wglReleaseImageBufferEventsI3D;\n#define wglReleaseImageBufferEventsI3D glad_wglReleaseImageBufferEventsI3D\n#endif\n#ifndef WGL_I3D_swap_frame_usage\n#define WGL_I3D_swap_frame_usage 1\nGLAPI int GLAD_WGL_I3D_swap_frame_usage;\ntypedef BOOL (APIENTRYP PFNWGLGETFRAMEUSAGEI3DPROC)(float*);\nGLAPI PFNWGLGETFRAMEUSAGEI3DPROC glad_wglGetFrameUsageI3D;\n#define wglGetFrameUsageI3D glad_wglGetFrameUsageI3D\ntypedef BOOL (APIENTRYP PFNWGLBEGINFRAMETRACKINGI3DPROC)();\nGLAPI PFNWGLBEGINFRAMETRACKINGI3DPROC glad_wglBeginFrameTrackingI3D;\n#define wglBeginFrameTrackingI3D glad_wglBeginFrameTrackingI3D\ntypedef BOOL (APIENTRYP PFNWGLENDFRAMETRACKINGI3DPROC)();\nGLAPI PFNWGLENDFRAMETRACKINGI3DPROC glad_wglEndFrameTrackingI3D;\n#define wglEndFrameTrackingI3D glad_wglEndFrameTrackingI3D\ntypedef BOOL (APIENTRYP PFNWGLQUERYFRAMETRACKINGI3DPROC)(DWORD*, DWORD*, float*);\nGLAPI PFNWGLQUERYFRAMETRACKINGI3DPROC glad_wglQueryFrameTrackingI3D;\n#define wglQueryFrameTrackingI3D glad_wglQueryFrameTrackingI3D\n#endif\n#ifndef WGL_NV_DX_interop2\n#define WGL_NV_DX_interop2 1\nGLAPI int GLAD_WGL_NV_DX_interop2;\n#endif\n#ifndef WGL_NV_float_buffer\n#define WGL_NV_float_buffer 1\nGLAPI int GLAD_WGL_NV_float_buffer;\n#endif\n#ifndef WGL_NV_delay_before_swap\n#define WGL_NV_delay_before_swap 1\nGLAPI int GLAD_WGL_NV_delay_before_swap;\ntypedef BOOL (APIENTRYP PFNWGLDELAYBEFORESWAPNVPROC)(HDC, GLfloat);\nGLAPI PFNWGLDELAYBEFORESWAPNVPROC glad_wglDelayBeforeSwapNV;\n#define wglDelayBeforeSwapNV glad_wglDelayBeforeSwapNV\n#endif\n#ifndef WGL_OML_sync_control\n#define WGL_OML_sync_control 1\nGLAPI int GLAD_WGL_OML_sync_control;\ntypedef BOOL (APIENTRYP PFNWGLGETSYNCVALUESOMLPROC)(HDC, INT64*, INT64*, INT64*);\nGLAPI PFNWGLGETSYNCVALUESOMLPROC glad_wglGetSyncValuesOML;\n#define wglGetSyncValuesOML glad_wglGetSyncValuesOML\ntypedef BOOL (APIENTRYP PFNWGLGETMSCRATEOMLPROC)(HDC, INT32*, INT32*);\nGLAPI PFNWGLGETMSCRATEOMLPROC glad_wglGetMscRateOML;\n#define wglGetMscRateOML glad_wglGetMscRateOML\ntypedef INT64 (APIENTRYP PFNWGLSWAPBUFFERSMSCOMLPROC)(HDC, INT64, INT64, INT64);\nGLAPI PFNWGLSWAPBUFFERSMSCOMLPROC glad_wglSwapBuffersMscOML;\n#define wglSwapBuffersMscOML glad_wglSwapBuffersMscOML\ntypedef INT64 (APIENTRYP PFNWGLSWAPLAYERBUFFERSMSCOMLPROC)(HDC, int, INT64, INT64, INT64);\nGLAPI PFNWGLSWAPLAYERBUFFERSMSCOMLPROC glad_wglSwapLayerBuffersMscOML;\n#define wglSwapLayerBuffersMscOML glad_wglSwapLayerBuffersMscOML\ntypedef BOOL (APIENTRYP PFNWGLWAITFORMSCOMLPROC)(HDC, INT64, INT64, INT64, INT64*, INT64*, INT64*);\nGLAPI PFNWGLWAITFORMSCOMLPROC glad_wglWaitForMscOML;\n#define wglWaitForMscOML glad_wglWaitForMscOML\ntypedef BOOL (APIENTRYP PFNWGLWAITFORSBCOMLPROC)(HDC, INT64, INT64*, INT64*, INT64*);\nGLAPI PFNWGLWAITFORSBCOMLPROC glad_wglWaitForSbcOML;\n#define wglWaitForSbcOML glad_wglWaitForSbcOML\n#endif\n#ifndef WGL_ARB_pixel_format_float\n#define WGL_ARB_pixel_format_float 1\nGLAPI int GLAD_WGL_ARB_pixel_format_float;\n#endif\n#ifndef WGL_ARB_create_context\n#define WGL_ARB_create_context 1\nGLAPI int GLAD_WGL_ARB_create_context;\ntypedef HGLRC (APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int*);\nGLAPI PFNWGLCREATECONTEXTATTRIBSARBPROC glad_wglCreateContextAttribsARB;\n#define wglCreateContextAttribsARB glad_wglCreateContextAttribsARB\n#endif\n#ifndef WGL_NV_swap_group\n#define WGL_NV_swap_group 1\nGLAPI int GLAD_WGL_NV_swap_group;\ntypedef BOOL (APIENTRYP PFNWGLJOINSWAPGROUPNVPROC)(HDC, GLuint);\nGLAPI PFNWGLJOINSWAPGROUPNVPROC glad_wglJoinSwapGroupNV;\n#define wglJoinSwapGroupNV glad_wglJoinSwapGroupNV\ntypedef BOOL (APIENTRYP PFNWGLBINDSWAPBARRIERNVPROC)(GLuint, GLuint);\nGLAPI PFNWGLBINDSWAPBARRIERNVPROC glad_wglBindSwapBarrierNV;\n#define wglBindSwapBarrierNV glad_wglBindSwapBarrierNV\ntypedef BOOL (APIENTRYP PFNWGLQUERYSWAPGROUPNVPROC)(HDC, GLuint*, GLuint*);\nGLAPI PFNWGLQUERYSWAPGROUPNVPROC glad_wglQuerySwapGroupNV;\n#define wglQuerySwapGroupNV glad_wglQuerySwapGroupNV\ntypedef BOOL (APIENTRYP PFNWGLQUERYMAXSWAPGROUPSNVPROC)(HDC, GLuint*, GLuint*);\nGLAPI PFNWGLQUERYMAXSWAPGROUPSNVPROC glad_wglQueryMaxSwapGroupsNV;\n#define wglQueryMaxSwapGroupsNV glad_wglQueryMaxSwapGroupsNV\ntypedef BOOL (APIENTRYP PFNWGLQUERYFRAMECOUNTNVPROC)(HDC, GLuint*);\nGLAPI PFNWGLQUERYFRAMECOUNTNVPROC glad_wglQueryFrameCountNV;\n#define wglQueryFrameCountNV glad_wglQueryFrameCountNV\ntypedef BOOL (APIENTRYP PFNWGLRESETFRAMECOUNTNVPROC)(HDC);\nGLAPI PFNWGLRESETFRAMECOUNTNVPROC glad_wglResetFrameCountNV;\n#define wglResetFrameCountNV glad_wglResetFrameCountNV\n#endif\n#ifndef WGL_NV_gpu_affinity\n#define WGL_NV_gpu_affinity 1\nGLAPI int GLAD_WGL_NV_gpu_affinity;\ntypedef BOOL (APIENTRYP PFNWGLENUMGPUSNVPROC)(UINT, HGPUNV*);\nGLAPI PFNWGLENUMGPUSNVPROC glad_wglEnumGpusNV;\n#define wglEnumGpusNV glad_wglEnumGpusNV\ntypedef BOOL (APIENTRYP PFNWGLENUMGPUDEVICESNVPROC)(HGPUNV, UINT, PGPU_DEVICE);\nGLAPI PFNWGLENUMGPUDEVICESNVPROC glad_wglEnumGpuDevicesNV;\n#define wglEnumGpuDevicesNV glad_wglEnumGpuDevicesNV\ntypedef HDC (APIENTRYP PFNWGLCREATEAFFINITYDCNVPROC)(const HGPUNV*);\nGLAPI PFNWGLCREATEAFFINITYDCNVPROC glad_wglCreateAffinityDCNV;\n#define wglCreateAffinityDCNV glad_wglCreateAffinityDCNV\ntypedef BOOL (APIENTRYP PFNWGLENUMGPUSFROMAFFINITYDCNVPROC)(HDC, UINT, HGPUNV*);\nGLAPI PFNWGLENUMGPUSFROMAFFINITYDCNVPROC glad_wglEnumGpusFromAffinityDCNV;\n#define wglEnumGpusFromAffinityDCNV glad_wglEnumGpusFromAffinityDCNV\ntypedef BOOL (APIENTRYP PFNWGLDELETEDCNVPROC)(HDC);\nGLAPI PFNWGLDELETEDCNVPROC glad_wglDeleteDCNV;\n#define wglDeleteDCNV glad_wglDeleteDCNV\n#endif\n#ifndef WGL_EXT_pixel_format\n#define WGL_EXT_pixel_format 1\nGLAPI int GLAD_WGL_EXT_pixel_format;\ntypedef BOOL (APIENTRYP PFNWGLGETPIXELFORMATATTRIBIVEXTPROC)(HDC, int, int, UINT, int*, int*);\nGLAPI PFNWGLGETPIXELFORMATATTRIBIVEXTPROC glad_wglGetPixelFormatAttribivEXT;\n#define wglGetPixelFormatAttribivEXT glad_wglGetPixelFormatAttribivEXT\ntypedef BOOL (APIENTRYP PFNWGLGETPIXELFORMATATTRIBFVEXTPROC)(HDC, int, int, UINT, int*, FLOAT*);\nGLAPI PFNWGLGETPIXELFORMATATTRIBFVEXTPROC glad_wglGetPixelFormatAttribfvEXT;\n#define wglGetPixelFormatAttribfvEXT glad_wglGetPixelFormatAttribfvEXT\ntypedef BOOL (APIENTRYP PFNWGLCHOOSEPIXELFORMATEXTPROC)(HDC, const int*, const FLOAT*, UINT, int*, UINT*);\nGLAPI PFNWGLCHOOSEPIXELFORMATEXTPROC glad_wglChoosePixelFormatEXT;\n#define wglChoosePixelFormatEXT glad_wglChoosePixelFormatEXT\n#endif\n#ifndef WGL_ARB_extensions_string\n#define WGL_ARB_extensions_string 1\nGLAPI int GLAD_WGL_ARB_extensions_string;\ntypedef const char* (APIENTRYP PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC);\nGLAPI PFNWGLGETEXTENSIONSSTRINGARBPROC glad_wglGetExtensionsStringARB;\n#define wglGetExtensionsStringARB glad_wglGetExtensionsStringARB\n#endif\n#ifndef WGL_NV_video_capture\n#define WGL_NV_video_capture 1\nGLAPI int GLAD_WGL_NV_video_capture;\ntypedef BOOL (APIENTRYP PFNWGLBINDVIDEOCAPTUREDEVICENVPROC)(UINT, HVIDEOINPUTDEVICENV);\nGLAPI PFNWGLBINDVIDEOCAPTUREDEVICENVPROC glad_wglBindVideoCaptureDeviceNV;\n#define wglBindVideoCaptureDeviceNV glad_wglBindVideoCaptureDeviceNV\ntypedef UINT (APIENTRYP PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC)(HDC, HVIDEOINPUTDEVICENV*);\nGLAPI PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC glad_wglEnumerateVideoCaptureDevicesNV;\n#define wglEnumerateVideoCaptureDevicesNV glad_wglEnumerateVideoCaptureDevicesNV\ntypedef BOOL (APIENTRYP PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC)(HDC, HVIDEOINPUTDEVICENV);\nGLAPI PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC glad_wglLockVideoCaptureDeviceNV;\n#define wglLockVideoCaptureDeviceNV glad_wglLockVideoCaptureDeviceNV\ntypedef BOOL (APIENTRYP PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC)(HDC, HVIDEOINPUTDEVICENV, int, int*);\nGLAPI PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC glad_wglQueryVideoCaptureDeviceNV;\n#define wglQueryVideoCaptureDeviceNV glad_wglQueryVideoCaptureDeviceNV\ntypedef BOOL (APIENTRYP PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC)(HDC, HVIDEOINPUTDEVICENV);\nGLAPI PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC glad_wglReleaseVideoCaptureDeviceNV;\n#define wglReleaseVideoCaptureDeviceNV glad_wglReleaseVideoCaptureDeviceNV\n#endif\n#ifndef WGL_NV_render_texture_rectangle\n#define WGL_NV_render_texture_rectangle 1\nGLAPI int GLAD_WGL_NV_render_texture_rectangle;\n#endif\n#ifndef WGL_EXT_create_context_es_profile\n#define WGL_EXT_create_context_es_profile 1\nGLAPI int GLAD_WGL_EXT_create_context_es_profile;\n#endif\n#ifndef WGL_ARB_robustness_share_group_isolation\n#define WGL_ARB_robustness_share_group_isolation 1\nGLAPI int GLAD_WGL_ARB_robustness_share_group_isolation;\n#endif\n#ifndef WGL_ARB_render_texture\n#define WGL_ARB_render_texture 1\nGLAPI int GLAD_WGL_ARB_render_texture;\ntypedef BOOL (APIENTRYP PFNWGLBINDTEXIMAGEARBPROC)(HPBUFFERARB, int);\nGLAPI PFNWGLBINDTEXIMAGEARBPROC glad_wglBindTexImageARB;\n#define wglBindTexImageARB glad_wglBindTexImageARB\ntypedef BOOL (APIENTRYP PFNWGLRELEASETEXIMAGEARBPROC)(HPBUFFERARB, int);\nGLAPI PFNWGLRELEASETEXIMAGEARBPROC glad_wglReleaseTexImageARB;\n#define wglReleaseTexImageARB glad_wglReleaseTexImageARB\ntypedef BOOL (APIENTRYP PFNWGLSETPBUFFERATTRIBARBPROC)(HPBUFFERARB, const int*);\nGLAPI PFNWGLSETPBUFFERATTRIBARBPROC glad_wglSetPbufferAttribARB;\n#define wglSetPbufferAttribARB glad_wglSetPbufferAttribARB\n#endif\n#ifndef WGL_EXT_depth_float\n#define WGL_EXT_depth_float 1\nGLAPI int GLAD_WGL_EXT_depth_float;\n#endif\n#ifndef WGL_EXT_swap_control_tear\n#define WGL_EXT_swap_control_tear 1\nGLAPI int GLAD_WGL_EXT_swap_control_tear;\n#endif\n#ifndef WGL_ARB_pixel_format\n#define WGL_ARB_pixel_format 1\nGLAPI int GLAD_WGL_ARB_pixel_format;\ntypedef BOOL (APIENTRYP PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC, int, int, UINT, const int*, int*);\nGLAPI PFNWGLGETPIXELFORMATATTRIBIVARBPROC glad_wglGetPixelFormatAttribivARB;\n#define wglGetPixelFormatAttribivARB glad_wglGetPixelFormatAttribivARB\ntypedef BOOL (APIENTRYP PFNWGLGETPIXELFORMATATTRIBFVARBPROC)(HDC, int, int, UINT, const int*, FLOAT*);\nGLAPI PFNWGLGETPIXELFORMATATTRIBFVARBPROC glad_wglGetPixelFormatAttribfvARB;\n#define wglGetPixelFormatAttribfvARB glad_wglGetPixelFormatAttribfvARB\ntypedef BOOL (APIENTRYP PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC, const int*, const FLOAT*, UINT, int*, UINT*);\nGLAPI PFNWGLCHOOSEPIXELFORMATARBPROC glad_wglChoosePixelFormatARB;\n#define wglChoosePixelFormatARB glad_wglChoosePixelFormatARB\n#endif\n#ifndef WGL_ARB_multisample\n#define WGL_ARB_multisample 1\nGLAPI int GLAD_WGL_ARB_multisample;\n#endif\n#ifndef WGL_I3D_genlock\n#define WGL_I3D_genlock 1\nGLAPI int GLAD_WGL_I3D_genlock;\ntypedef BOOL (APIENTRYP PFNWGLENABLEGENLOCKI3DPROC)(HDC);\nGLAPI PFNWGLENABLEGENLOCKI3DPROC glad_wglEnableGenlockI3D;\n#define wglEnableGenlockI3D glad_wglEnableGenlockI3D\ntypedef BOOL (APIENTRYP PFNWGLDISABLEGENLOCKI3DPROC)(HDC);\nGLAPI PFNWGLDISABLEGENLOCKI3DPROC glad_wglDisableGenlockI3D;\n#define wglDisableGenlockI3D glad_wglDisableGenlockI3D\ntypedef BOOL (APIENTRYP PFNWGLISENABLEDGENLOCKI3DPROC)(HDC, BOOL*);\nGLAPI PFNWGLISENABLEDGENLOCKI3DPROC glad_wglIsEnabledGenlockI3D;\n#define wglIsEnabledGenlockI3D glad_wglIsEnabledGenlockI3D\ntypedef BOOL (APIENTRYP PFNWGLGENLOCKSOURCEI3DPROC)(HDC, UINT);\nGLAPI PFNWGLGENLOCKSOURCEI3DPROC glad_wglGenlockSourceI3D;\n#define wglGenlockSourceI3D glad_wglGenlockSourceI3D\ntypedef BOOL (APIENTRYP PFNWGLGETGENLOCKSOURCEI3DPROC)(HDC, UINT*);\nGLAPI PFNWGLGETGENLOCKSOURCEI3DPROC glad_wglGetGenlockSourceI3D;\n#define wglGetGenlockSourceI3D glad_wglGetGenlockSourceI3D\ntypedef BOOL (APIENTRYP PFNWGLGENLOCKSOURCEEDGEI3DPROC)(HDC, UINT);\nGLAPI PFNWGLGENLOCKSOURCEEDGEI3DPROC glad_wglGenlockSourceEdgeI3D;\n#define wglGenlockSourceEdgeI3D glad_wglGenlockSourceEdgeI3D\ntypedef BOOL (APIENTRYP PFNWGLGETGENLOCKSOURCEEDGEI3DPROC)(HDC, UINT*);\nGLAPI PFNWGLGETGENLOCKSOURCEEDGEI3DPROC glad_wglGetGenlockSourceEdgeI3D;\n#define wglGetGenlockSourceEdgeI3D glad_wglGetGenlockSourceEdgeI3D\ntypedef BOOL (APIENTRYP PFNWGLGENLOCKSAMPLERATEI3DPROC)(HDC, UINT);\nGLAPI PFNWGLGENLOCKSAMPLERATEI3DPROC glad_wglGenlockSampleRateI3D;\n#define wglGenlockSampleRateI3D glad_wglGenlockSampleRateI3D\ntypedef BOOL (APIENTRYP PFNWGLGETGENLOCKSAMPLERATEI3DPROC)(HDC, UINT*);\nGLAPI PFNWGLGETGENLOCKSAMPLERATEI3DPROC glad_wglGetGenlockSampleRateI3D;\n#define wglGetGenlockSampleRateI3D glad_wglGetGenlockSampleRateI3D\ntypedef BOOL (APIENTRYP PFNWGLGENLOCKSOURCEDELAYI3DPROC)(HDC, UINT);\nGLAPI PFNWGLGENLOCKSOURCEDELAYI3DPROC glad_wglGenlockSourceDelayI3D;\n#define wglGenlockSourceDelayI3D glad_wglGenlockSourceDelayI3D\ntypedef BOOL (APIENTRYP PFNWGLGETGENLOCKSOURCEDELAYI3DPROC)(HDC, UINT*);\nGLAPI PFNWGLGETGENLOCKSOURCEDELAYI3DPROC glad_wglGetGenlockSourceDelayI3D;\n#define wglGetGenlockSourceDelayI3D glad_wglGetGenlockSourceDelayI3D\ntypedef BOOL (APIENTRYP PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC)(HDC, UINT*, UINT*);\nGLAPI PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC glad_wglQueryGenlockMaxSourceDelayI3D;\n#define wglQueryGenlockMaxSourceDelayI3D glad_wglQueryGenlockMaxSourceDelayI3D\n#endif\n#ifndef WGL_NV_DX_interop\n#define WGL_NV_DX_interop 1\nGLAPI int GLAD_WGL_NV_DX_interop;\ntypedef BOOL (APIENTRYP PFNWGLDXSETRESOURCESHAREHANDLENVPROC)(void*, HANDLE);\nGLAPI PFNWGLDXSETRESOURCESHAREHANDLENVPROC glad_wglDXSetResourceShareHandleNV;\n#define wglDXSetResourceShareHandleNV glad_wglDXSetResourceShareHandleNV\ntypedef HANDLE (APIENTRYP PFNWGLDXOPENDEVICENVPROC)(void*);\nGLAPI PFNWGLDXOPENDEVICENVPROC glad_wglDXOpenDeviceNV;\n#define wglDXOpenDeviceNV glad_wglDXOpenDeviceNV\ntypedef BOOL (APIENTRYP PFNWGLDXCLOSEDEVICENVPROC)(HANDLE);\nGLAPI PFNWGLDXCLOSEDEVICENVPROC glad_wglDXCloseDeviceNV;\n#define wglDXCloseDeviceNV glad_wglDXCloseDeviceNV\ntypedef HANDLE (APIENTRYP PFNWGLDXREGISTEROBJECTNVPROC)(HANDLE, void*, GLuint, GLenum, GLenum);\nGLAPI PFNWGLDXREGISTEROBJECTNVPROC glad_wglDXRegisterObjectNV;\n#define wglDXRegisterObjectNV glad_wglDXRegisterObjectNV\ntypedef BOOL (APIENTRYP PFNWGLDXUNREGISTEROBJECTNVPROC)(HANDLE, HANDLE);\nGLAPI PFNWGLDXUNREGISTEROBJECTNVPROC glad_wglDXUnregisterObjectNV;\n#define wglDXUnregisterObjectNV glad_wglDXUnregisterObjectNV\ntypedef BOOL (APIENTRYP PFNWGLDXOBJECTACCESSNVPROC)(HANDLE, GLenum);\nGLAPI PFNWGLDXOBJECTACCESSNVPROC glad_wglDXObjectAccessNV;\n#define wglDXObjectAccessNV glad_wglDXObjectAccessNV\ntypedef BOOL (APIENTRYP PFNWGLDXLOCKOBJECTSNVPROC)(HANDLE, GLint, HANDLE*);\nGLAPI PFNWGLDXLOCKOBJECTSNVPROC glad_wglDXLockObjectsNV;\n#define wglDXLockObjectsNV glad_wglDXLockObjectsNV\ntypedef BOOL (APIENTRYP PFNWGLDXUNLOCKOBJECTSNVPROC)(HANDLE, GLint, HANDLE*);\nGLAPI PFNWGLDXUNLOCKOBJECTSNVPROC glad_wglDXUnlockObjectsNV;\n#define wglDXUnlockObjectsNV glad_wglDXUnlockObjectsNV\n#endif\n#ifndef WGL_3DL_stereo_control\n#define WGL_3DL_stereo_control 1\nGLAPI int GLAD_WGL_3DL_stereo_control;\ntypedef BOOL (APIENTRYP PFNWGLSETSTEREOEMITTERSTATE3DLPROC)(HDC, UINT);\nGLAPI PFNWGLSETSTEREOEMITTERSTATE3DLPROC glad_wglSetStereoEmitterState3DL;\n#define wglSetStereoEmitterState3DL glad_wglSetStereoEmitterState3DL\n#endif\n#ifndef WGL_EXT_pbuffer\n#define WGL_EXT_pbuffer 1\nGLAPI int GLAD_WGL_EXT_pbuffer;\ntypedef HPBUFFEREXT (APIENTRYP PFNWGLCREATEPBUFFEREXTPROC)(HDC, int, int, int, const int*);\nGLAPI PFNWGLCREATEPBUFFEREXTPROC glad_wglCreatePbufferEXT;\n#define wglCreatePbufferEXT glad_wglCreatePbufferEXT\ntypedef HDC (APIENTRYP PFNWGLGETPBUFFERDCEXTPROC)(HPBUFFEREXT);\nGLAPI PFNWGLGETPBUFFERDCEXTPROC glad_wglGetPbufferDCEXT;\n#define wglGetPbufferDCEXT glad_wglGetPbufferDCEXT\ntypedef int (APIENTRYP PFNWGLRELEASEPBUFFERDCEXTPROC)(HPBUFFEREXT, HDC);\nGLAPI PFNWGLRELEASEPBUFFERDCEXTPROC glad_wglReleasePbufferDCEXT;\n#define wglReleasePbufferDCEXT glad_wglReleasePbufferDCEXT\ntypedef BOOL (APIENTRYP PFNWGLDESTROYPBUFFEREXTPROC)(HPBUFFEREXT);\nGLAPI PFNWGLDESTROYPBUFFEREXTPROC glad_wglDestroyPbufferEXT;\n#define wglDestroyPbufferEXT glad_wglDestroyPbufferEXT\ntypedef BOOL (APIENTRYP PFNWGLQUERYPBUFFEREXTPROC)(HPBUFFEREXT, int, int*);\nGLAPI PFNWGLQUERYPBUFFEREXTPROC glad_wglQueryPbufferEXT;\n#define wglQueryPbufferEXT glad_wglQueryPbufferEXT\n#endif\n#ifndef WGL_EXT_display_color_table\n#define WGL_EXT_display_color_table 1\nGLAPI int GLAD_WGL_EXT_display_color_table;\ntypedef GLboolean (APIENTRYP PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC)(GLushort);\nGLAPI PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC glad_wglCreateDisplayColorTableEXT;\n#define wglCreateDisplayColorTableEXT glad_wglCreateDisplayColorTableEXT\ntypedef GLboolean (APIENTRYP PFNWGLLOADDISPLAYCOLORTABLEEXTPROC)(const GLushort*, GLuint);\nGLAPI PFNWGLLOADDISPLAYCOLORTABLEEXTPROC glad_wglLoadDisplayColorTableEXT;\n#define wglLoadDisplayColorTableEXT glad_wglLoadDisplayColorTableEXT\ntypedef GLboolean (APIENTRYP PFNWGLBINDDISPLAYCOLORTABLEEXTPROC)(GLushort);\nGLAPI PFNWGLBINDDISPLAYCOLORTABLEEXTPROC glad_wglBindDisplayColorTableEXT;\n#define wglBindDisplayColorTableEXT glad_wglBindDisplayColorTableEXT\ntypedef VOID (APIENTRYP PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC)(GLushort);\nGLAPI PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC glad_wglDestroyDisplayColorTableEXT;\n#define wglDestroyDisplayColorTableEXT glad_wglDestroyDisplayColorTableEXT\n#endif\n#ifndef WGL_NV_video_output\n#define WGL_NV_video_output 1\nGLAPI int GLAD_WGL_NV_video_output;\ntypedef BOOL (APIENTRYP PFNWGLGETVIDEODEVICENVPROC)(HDC, int, HPVIDEODEV*);\nGLAPI PFNWGLGETVIDEODEVICENVPROC glad_wglGetVideoDeviceNV;\n#define wglGetVideoDeviceNV glad_wglGetVideoDeviceNV\ntypedef BOOL (APIENTRYP PFNWGLRELEASEVIDEODEVICENVPROC)(HPVIDEODEV);\nGLAPI PFNWGLRELEASEVIDEODEVICENVPROC glad_wglReleaseVideoDeviceNV;\n#define wglReleaseVideoDeviceNV glad_wglReleaseVideoDeviceNV\ntypedef BOOL (APIENTRYP PFNWGLBINDVIDEOIMAGENVPROC)(HPVIDEODEV, HPBUFFERARB, int);\nGLAPI PFNWGLBINDVIDEOIMAGENVPROC glad_wglBindVideoImageNV;\n#define wglBindVideoImageNV glad_wglBindVideoImageNV\ntypedef BOOL (APIENTRYP PFNWGLRELEASEVIDEOIMAGENVPROC)(HPBUFFERARB, int);\nGLAPI PFNWGLRELEASEVIDEOIMAGENVPROC glad_wglReleaseVideoImageNV;\n#define wglReleaseVideoImageNV glad_wglReleaseVideoImageNV\ntypedef BOOL (APIENTRYP PFNWGLSENDPBUFFERTOVIDEONVPROC)(HPBUFFERARB, int, unsigned long*, BOOL);\nGLAPI PFNWGLSENDPBUFFERTOVIDEONVPROC glad_wglSendPbufferToVideoNV;\n#define wglSendPbufferToVideoNV glad_wglSendPbufferToVideoNV\ntypedef BOOL (APIENTRYP PFNWGLGETVIDEOINFONVPROC)(HPVIDEODEV, unsigned long*, unsigned long*);\nGLAPI PFNWGLGETVIDEOINFONVPROC glad_wglGetVideoInfoNV;\n#define wglGetVideoInfoNV glad_wglGetVideoInfoNV\n#endif\n#ifndef WGL_ARB_robustness_application_isolation\n#define WGL_ARB_robustness_application_isolation 1\nGLAPI int GLAD_WGL_ARB_robustness_application_isolation;\n#endif\n#ifndef WGL_3DFX_multisample\n#define WGL_3DFX_multisample 1\nGLAPI int GLAD_WGL_3DFX_multisample;\n#endif\n#ifndef WGL_I3D_gamma\n#define WGL_I3D_gamma 1\nGLAPI int GLAD_WGL_I3D_gamma;\ntypedef BOOL (APIENTRYP PFNWGLGETGAMMATABLEPARAMETERSI3DPROC)(HDC, int, int*);\nGLAPI PFNWGLGETGAMMATABLEPARAMETERSI3DPROC glad_wglGetGammaTableParametersI3D;\n#define wglGetGammaTableParametersI3D glad_wglGetGammaTableParametersI3D\ntypedef BOOL (APIENTRYP PFNWGLSETGAMMATABLEPARAMETERSI3DPROC)(HDC, int, const int*);\nGLAPI PFNWGLSETGAMMATABLEPARAMETERSI3DPROC glad_wglSetGammaTableParametersI3D;\n#define wglSetGammaTableParametersI3D glad_wglSetGammaTableParametersI3D\ntypedef BOOL (APIENTRYP PFNWGLGETGAMMATABLEI3DPROC)(HDC, int, USHORT*, USHORT*, USHORT*);\nGLAPI PFNWGLGETGAMMATABLEI3DPROC glad_wglGetGammaTableI3D;\n#define wglGetGammaTableI3D glad_wglGetGammaTableI3D\ntypedef BOOL (APIENTRYP PFNWGLSETGAMMATABLEI3DPROC)(HDC, int, const USHORT*, const USHORT*, const USHORT*);\nGLAPI PFNWGLSETGAMMATABLEI3DPROC glad_wglSetGammaTableI3D;\n#define wglSetGammaTableI3D glad_wglSetGammaTableI3D\n#endif\n#ifndef WGL_ARB_framebuffer_sRGB\n#define WGL_ARB_framebuffer_sRGB 1\nGLAPI int GLAD_WGL_ARB_framebuffer_sRGB;\n#endif\n#ifndef WGL_NV_copy_image\n#define WGL_NV_copy_image 1\nGLAPI int GLAD_WGL_NV_copy_image;\ntypedef BOOL (APIENTRYP PFNWGLCOPYIMAGESUBDATANVPROC)(HGLRC, GLuint, GLenum, GLint, GLint, GLint, GLint, HGLRC, GLuint, GLenum, GLint, GLint, GLint, GLint, GLsizei, GLsizei, GLsizei);\nGLAPI PFNWGLCOPYIMAGESUBDATANVPROC glad_wglCopyImageSubDataNV;\n#define wglCopyImageSubDataNV glad_wglCopyImageSubDataNV\n#endif\n#ifndef WGL_EXT_framebuffer_sRGB\n#define WGL_EXT_framebuffer_sRGB 1\nGLAPI int GLAD_WGL_EXT_framebuffer_sRGB;\n#endif\n#ifndef WGL_NV_present_video\n#define WGL_NV_present_video 1\nGLAPI int GLAD_WGL_NV_present_video;\ntypedef int (APIENTRYP PFNWGLENUMERATEVIDEODEVICESNVPROC)(HDC, HVIDEOOUTPUTDEVICENV*);\nGLAPI PFNWGLENUMERATEVIDEODEVICESNVPROC glad_wglEnumerateVideoDevicesNV;\n#define wglEnumerateVideoDevicesNV glad_wglEnumerateVideoDevicesNV\ntypedef BOOL (APIENTRYP PFNWGLBINDVIDEODEVICENVPROC)(HDC, unsigned int, HVIDEOOUTPUTDEVICENV, const int*);\nGLAPI PFNWGLBINDVIDEODEVICENVPROC glad_wglBindVideoDeviceNV;\n#define wglBindVideoDeviceNV glad_wglBindVideoDeviceNV\ntypedef BOOL (APIENTRYP PFNWGLQUERYCURRENTCONTEXTNVPROC)(int, int*);\nGLAPI PFNWGLQUERYCURRENTCONTEXTNVPROC glad_wglQueryCurrentContextNV;\n#define wglQueryCurrentContextNV glad_wglQueryCurrentContextNV\n#endif\n#ifndef WGL_EXT_create_context_es2_profile\n#define WGL_EXT_create_context_es2_profile 1\nGLAPI int GLAD_WGL_EXT_create_context_es2_profile;\n#endif\n#ifndef WGL_ARB_create_context_robustness\n#define WGL_ARB_create_context_robustness 1\nGLAPI int GLAD_WGL_ARB_create_context_robustness;\n#endif\n#ifndef WGL_ARB_make_current_read\n#define WGL_ARB_make_current_read 1\nGLAPI int GLAD_WGL_ARB_make_current_read;\ntypedef BOOL (APIENTRYP PFNWGLMAKECONTEXTCURRENTARBPROC)(HDC, HDC, HGLRC);\nGLAPI PFNWGLMAKECONTEXTCURRENTARBPROC glad_wglMakeContextCurrentARB;\n#define wglMakeContextCurrentARB glad_wglMakeContextCurrentARB\ntypedef HDC (APIENTRYP PFNWGLGETCURRENTREADDCARBPROC)();\nGLAPI PFNWGLGETCURRENTREADDCARBPROC glad_wglGetCurrentReadDCARB;\n#define wglGetCurrentReadDCARB glad_wglGetCurrentReadDCARB\n#endif\n#ifndef WGL_EXT_multisample\n#define WGL_EXT_multisample 1\nGLAPI int GLAD_WGL_EXT_multisample;\n#endif\n#ifndef WGL_EXT_extensions_string\n#define WGL_EXT_extensions_string 1\nGLAPI int GLAD_WGL_EXT_extensions_string;\ntypedef const char* (APIENTRYP PFNWGLGETEXTENSIONSSTRINGEXTPROC)();\nGLAPI PFNWGLGETEXTENSIONSSTRINGEXTPROC glad_wglGetExtensionsStringEXT;\n#define wglGetExtensionsStringEXT glad_wglGetExtensionsStringEXT\n#endif\n#ifndef WGL_NV_render_depth_texture\n#define WGL_NV_render_depth_texture 1\nGLAPI int GLAD_WGL_NV_render_depth_texture;\n#endif\n#ifndef WGL_ATI_pixel_format_float\n#define WGL_ATI_pixel_format_float 1\nGLAPI int GLAD_WGL_ATI_pixel_format_float;\n#endif\n#ifndef WGL_ARB_create_context_profile\n#define WGL_ARB_create_context_profile 1\nGLAPI int GLAD_WGL_ARB_create_context_profile;\n#endif\n#ifndef WGL_EXT_swap_control\n#define WGL_EXT_swap_control 1\nGLAPI int GLAD_WGL_EXT_swap_control;\ntypedef BOOL (APIENTRYP PFNWGLSWAPINTERVALEXTPROC)(int);\nGLAPI PFNWGLSWAPINTERVALEXTPROC glad_wglSwapIntervalEXT;\n#define wglSwapIntervalEXT glad_wglSwapIntervalEXT\ntypedef int (APIENTRYP PFNWGLGETSWAPINTERVALEXTPROC)();\nGLAPI PFNWGLGETSWAPINTERVALEXTPROC glad_wglGetSwapIntervalEXT;\n#define wglGetSwapIntervalEXT glad_wglGetSwapIntervalEXT\n#endif\n#ifndef WGL_I3D_digital_video_control\n#define WGL_I3D_digital_video_control 1\nGLAPI int GLAD_WGL_I3D_digital_video_control;\ntypedef BOOL (APIENTRYP PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC)(HDC, int, int*);\nGLAPI PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC glad_wglGetDigitalVideoParametersI3D;\n#define wglGetDigitalVideoParametersI3D glad_wglGetDigitalVideoParametersI3D\ntypedef BOOL (APIENTRYP PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC)(HDC, int, const int*);\nGLAPI PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC glad_wglSetDigitalVideoParametersI3D;\n#define wglSetDigitalVideoParametersI3D glad_wglSetDigitalVideoParametersI3D\n#endif\n#ifndef WGL_ARB_pbuffer\n#define WGL_ARB_pbuffer 1\nGLAPI int GLAD_WGL_ARB_pbuffer;\ntypedef HPBUFFERARB (APIENTRYP PFNWGLCREATEPBUFFERARBPROC)(HDC, int, int, int, const int*);\nGLAPI PFNWGLCREATEPBUFFERARBPROC glad_wglCreatePbufferARB;\n#define wglCreatePbufferARB glad_wglCreatePbufferARB\ntypedef HDC (APIENTRYP PFNWGLGETPBUFFERDCARBPROC)(HPBUFFERARB);\nGLAPI PFNWGLGETPBUFFERDCARBPROC glad_wglGetPbufferDCARB;\n#define wglGetPbufferDCARB glad_wglGetPbufferDCARB\ntypedef int (APIENTRYP PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB, HDC);\nGLAPI PFNWGLRELEASEPBUFFERDCARBPROC glad_wglReleasePbufferDCARB;\n#define wglReleasePbufferDCARB glad_wglReleasePbufferDCARB\ntypedef BOOL (APIENTRYP PFNWGLDESTROYPBUFFERARBPROC)(HPBUFFERARB);\nGLAPI PFNWGLDESTROYPBUFFERARBPROC glad_wglDestroyPbufferARB;\n#define wglDestroyPbufferARB glad_wglDestroyPbufferARB\ntypedef BOOL (APIENTRYP PFNWGLQUERYPBUFFERARBPROC)(HPBUFFERARB, int, int*);\nGLAPI PFNWGLQUERYPBUFFERARBPROC glad_wglQueryPbufferARB;\n#define wglQueryPbufferARB glad_wglQueryPbufferARB\n#endif\n#ifndef WGL_NV_vertex_array_range\n#define WGL_NV_vertex_array_range 1\nGLAPI int GLAD_WGL_NV_vertex_array_range;\ntypedef void* (APIENTRYP PFNWGLALLOCATEMEMORYNVPROC)(GLsizei, GLfloat, GLfloat, GLfloat);\nGLAPI PFNWGLALLOCATEMEMORYNVPROC glad_wglAllocateMemoryNV;\n#define wglAllocateMemoryNV glad_wglAllocateMemoryNV\ntypedef void (APIENTRYP PFNWGLFREEMEMORYNVPROC)(void*);\nGLAPI PFNWGLFREEMEMORYNVPROC glad_wglFreeMemoryNV;\n#define wglFreeMemoryNV glad_wglFreeMemoryNV\n#endif\n#ifndef WGL_AMD_gpu_association\n#define WGL_AMD_gpu_association 1\nGLAPI int GLAD_WGL_AMD_gpu_association;\ntypedef UINT (APIENTRYP PFNWGLGETGPUIDSAMDPROC)(UINT, UINT*);\nGLAPI PFNWGLGETGPUIDSAMDPROC glad_wglGetGPUIDsAMD;\n#define wglGetGPUIDsAMD glad_wglGetGPUIDsAMD\ntypedef INT (APIENTRYP PFNWGLGETGPUINFOAMDPROC)(UINT, int, GLenum, UINT, void*);\nGLAPI PFNWGLGETGPUINFOAMDPROC glad_wglGetGPUInfoAMD;\n#define wglGetGPUInfoAMD glad_wglGetGPUInfoAMD\ntypedef UINT (APIENTRYP PFNWGLGETCONTEXTGPUIDAMDPROC)(HGLRC);\nGLAPI PFNWGLGETCONTEXTGPUIDAMDPROC glad_wglGetContextGPUIDAMD;\n#define wglGetContextGPUIDAMD glad_wglGetContextGPUIDAMD\ntypedef HGLRC (APIENTRYP PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC)(UINT);\nGLAPI PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC glad_wglCreateAssociatedContextAMD;\n#define wglCreateAssociatedContextAMD glad_wglCreateAssociatedContextAMD\ntypedef HGLRC (APIENTRYP PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC)(UINT, HGLRC, const int*);\nGLAPI PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC glad_wglCreateAssociatedContextAttribsAMD;\n#define wglCreateAssociatedContextAttribsAMD glad_wglCreateAssociatedContextAttribsAMD\ntypedef BOOL (APIENTRYP PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC)(HGLRC);\nGLAPI PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC glad_wglDeleteAssociatedContextAMD;\n#define wglDeleteAssociatedContextAMD glad_wglDeleteAssociatedContextAMD\ntypedef BOOL (APIENTRYP PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC)(HGLRC);\nGLAPI PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC glad_wglMakeAssociatedContextCurrentAMD;\n#define wglMakeAssociatedContextCurrentAMD glad_wglMakeAssociatedContextCurrentAMD\ntypedef HGLRC (APIENTRYP PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC)();\nGLAPI PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC glad_wglGetCurrentAssociatedContextAMD;\n#define wglGetCurrentAssociatedContextAMD glad_wglGetCurrentAssociatedContextAMD\ntypedef VOID (APIENTRYP PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC)(HGLRC, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum);\nGLAPI PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC glad_wglBlitContextFramebufferAMD;\n#define wglBlitContextFramebufferAMD glad_wglBlitContextFramebufferAMD\n#endif\n#ifndef WGL_EXT_pixel_format_packed_float\n#define WGL_EXT_pixel_format_packed_float 1\nGLAPI int GLAD_WGL_EXT_pixel_format_packed_float;\n#endif\n#ifndef WGL_EXT_make_current_read\n#define WGL_EXT_make_current_read 1\nGLAPI int GLAD_WGL_EXT_make_current_read;\ntypedef BOOL (APIENTRYP PFNWGLMAKECONTEXTCURRENTEXTPROC)(HDC, HDC, HGLRC);\nGLAPI PFNWGLMAKECONTEXTCURRENTEXTPROC glad_wglMakeContextCurrentEXT;\n#define wglMakeContextCurrentEXT glad_wglMakeContextCurrentEXT\ntypedef HDC (APIENTRYP PFNWGLGETCURRENTREADDCEXTPROC)();\nGLAPI PFNWGLGETCURRENTREADDCEXTPROC glad_wglGetCurrentReadDCEXT;\n#define wglGetCurrentReadDCEXT glad_wglGetCurrentReadDCEXT\n#endif\n#ifndef WGL_I3D_swap_frame_lock\n#define WGL_I3D_swap_frame_lock 1\nGLAPI int GLAD_WGL_I3D_swap_frame_lock;\ntypedef BOOL (APIENTRYP PFNWGLENABLEFRAMELOCKI3DPROC)();\nGLAPI PFNWGLENABLEFRAMELOCKI3DPROC glad_wglEnableFrameLockI3D;\n#define wglEnableFrameLockI3D glad_wglEnableFrameLockI3D\ntypedef BOOL (APIENTRYP PFNWGLDISABLEFRAMELOCKI3DPROC)();\nGLAPI PFNWGLDISABLEFRAMELOCKI3DPROC glad_wglDisableFrameLockI3D;\n#define wglDisableFrameLockI3D glad_wglDisableFrameLockI3D\ntypedef BOOL (APIENTRYP PFNWGLISENABLEDFRAMELOCKI3DPROC)(BOOL*);\nGLAPI PFNWGLISENABLEDFRAMELOCKI3DPROC glad_wglIsEnabledFrameLockI3D;\n#define wglIsEnabledFrameLockI3D glad_wglIsEnabledFrameLockI3D\ntypedef BOOL (APIENTRYP PFNWGLQUERYFRAMELOCKMASTERI3DPROC)(BOOL*);\nGLAPI PFNWGLQUERYFRAMELOCKMASTERI3DPROC glad_wglQueryFrameLockMasterI3D;\n#define wglQueryFrameLockMasterI3D glad_wglQueryFrameLockMasterI3D\n#endif\n#ifndef WGL_ARB_buffer_region\n#define WGL_ARB_buffer_region 1\nGLAPI int GLAD_WGL_ARB_buffer_region;\ntypedef HANDLE (APIENTRYP PFNWGLCREATEBUFFERREGIONARBPROC)(HDC, int, UINT);\nGLAPI PFNWGLCREATEBUFFERREGIONARBPROC glad_wglCreateBufferRegionARB;\n#define wglCreateBufferRegionARB glad_wglCreateBufferRegionARB\ntypedef VOID (APIENTRYP PFNWGLDELETEBUFFERREGIONARBPROC)(HANDLE);\nGLAPI PFNWGLDELETEBUFFERREGIONARBPROC glad_wglDeleteBufferRegionARB;\n#define wglDeleteBufferRegionARB glad_wglDeleteBufferRegionARB\ntypedef BOOL (APIENTRYP PFNWGLSAVEBUFFERREGIONARBPROC)(HANDLE, int, int, int, int);\nGLAPI PFNWGLSAVEBUFFERREGIONARBPROC glad_wglSaveBufferRegionARB;\n#define wglSaveBufferRegionARB glad_wglSaveBufferRegionARB\ntypedef BOOL (APIENTRYP PFNWGLRESTOREBUFFERREGIONARBPROC)(HANDLE, int, int, int, int, int, int);\nGLAPI PFNWGLRESTOREBUFFERREGIONARBPROC glad_wglRestoreBufferRegionARB;\n#define wglRestoreBufferRegionARB glad_wglRestoreBufferRegionARB\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "deps/glad/src/glad.c",
    "content": "#include <string.h>\n#include <glad/glad.h>\n\nstatic void* get_proc(const char *namez);\n\n#ifdef _WIN32\n#include <windows.h>\nstatic HMODULE libGL;\n\ntypedef void* (APIENTRYP PFNWGLGETPROCADDRESSPROC_PRIVATE)(const char*);\nPFNWGLGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;\n\nstatic\nint open_gl(void) {\n    libGL = LoadLibraryA(\"opengl32.dll\");\n    if(libGL != NULL) {\n        gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE)GetProcAddress(\n                libGL, \"wglGetProcAddress\");\n        return gladGetProcAddressPtr != NULL;\n    }\n\n    return 0;\n}\n\nstatic\nvoid close_gl(void) {\n    if(libGL != NULL) {\n        FreeLibrary(libGL);\n        libGL = NULL;\n    }\n}\n#else\n#include <dlfcn.h>\nstatic void* libGL;\n\n#ifndef __APPLE__\ntypedef void* (APIENTRYP PFNGLXGETPROCADDRESSPROC_PRIVATE)(const char*);\nPFNGLXGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;\n#endif\n\nstatic\nint open_gl(void) {\n#ifdef __APPLE__\n    static const char *NAMES[] = {\n        \"../Frameworks/OpenGL.framework/OpenGL\",\n        \"/Library/Frameworks/OpenGL.framework/OpenGL\",\n        \"/System/Library/Frameworks/OpenGL.framework/OpenGL\",\n        \"/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL\"\n    };\n#else\n    static const char *NAMES[] = {\"libGL.so.1\", \"libGL.so\"};\n#endif\n\n    unsigned int index = 0;\n    for(index = 0; index < (sizeof(NAMES) / sizeof(NAMES[0])); index++) {\n        libGL = dlopen(NAMES[index], RTLD_NOW | RTLD_GLOBAL);\n\n        if(libGL != NULL) {\n#ifdef __APPLE__\n            return 1;\n#else\n            gladGetProcAddressPtr = (PFNGLXGETPROCADDRESSPROC_PRIVATE)dlsym(libGL,\n                \"glXGetProcAddressARB\");\n            return gladGetProcAddressPtr != NULL;\n#endif\n        }\n    }\n\n    return 0;\n}\n\nstatic\nvoid close_gl() {\n    if(libGL != NULL) {\n        dlclose(libGL);\n        libGL = NULL;\n    }\n}\n#endif\n\nstatic\nvoid* get_proc(const char *namez) {\n    void* result = NULL;\n    if(libGL == NULL) return NULL;\n\n#ifndef __APPLE__\n    if(gladGetProcAddressPtr != NULL) {\n        result = gladGetProcAddressPtr(namez);\n    }\n#endif\n    if(result == NULL) {\n#ifdef _WIN32\n        result = (void*)GetProcAddress(libGL, namez);\n#else\n        result = dlsym(libGL, namez);\n#endif\n    }\n\n    return result;\n}\n\nint gladLoadGL(void) {\n    if(open_gl()) {\n        gladLoadGLLoader(&get_proc);\n        close_gl();\n        return 1;\n    }\n    return 0;\n}\n\nstruct gladGLversionStruct GLVersion;\n\n#if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0)\n#define _GLAD_IS_SOME_NEW_VERSION 1\n#endif\n\nstatic int has_ext(const char *ext) {\n#ifdef _GLAD_IS_SOME_NEW_VERSION\n    if(GLVersion.major < 3) {\n#endif\n        const char *extensions;\n        const char *loc;\n        const char *terminator;\n        extensions = (const char *)glGetString(GL_EXTENSIONS);\n        if(extensions == NULL || ext == NULL) {\n            return 0;\n        }\n\n        while(1) {\n            loc = strstr(extensions, ext);\n            if(loc == NULL) {\n                return 0;\n            }\n\n            terminator = loc + strlen(ext);\n            if((loc == extensions || *(loc - 1) == ' ') &&\n                (*terminator == ' ' || *terminator == '\\0')) {\n                return 1;\n            }\n            extensions = terminator;\n        }\n#ifdef _GLAD_IS_SOME_NEW_VERSION\n    } else {\n        int num;\n        glGetIntegerv(GL_NUM_EXTENSIONS, &num);\n\n        int index;\n        for(index = 0; index < num; index++) {\n            const char *e = (const char*)glGetStringi(GL_EXTENSIONS, index);\n            if(strcmp(e, ext) == 0) {\n                return 1;\n            }\n        }\n    }\n#endif\n\n    return 0;\n}\nint GLAD_GL_VERSION_1_0;\nint GLAD_GL_VERSION_1_1;\nint GLAD_GL_VERSION_1_2;\nint GLAD_GL_VERSION_1_3;\nint GLAD_GL_VERSION_1_4;\nint GLAD_GL_VERSION_1_5;\nint GLAD_GL_VERSION_2_0;\nint GLAD_GL_VERSION_2_1;\nint GLAD_GL_VERSION_3_0;\nint GLAD_GL_VERSION_3_1;\nint GLAD_GL_VERSION_3_2;\nint GLAD_GL_VERSION_3_3;\nint GLAD_GL_VERSION_4_0;\nint GLAD_GL_VERSION_4_1;\nint GLAD_GL_VERSION_4_2;\nint GLAD_GL_VERSION_4_3;\nint GLAD_GL_VERSION_4_4;\nPFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D;\nPFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui;\nPFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate;\nPFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading;\nPFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer;\nPFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv;\nPFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D;\nPFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv;\nPFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v;\nPFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv;\nPFNGLBINDSAMPLERPROC glad_glBindSampler;\nPFNGLLINEWIDTHPROC glad_glLineWidth;\nPFNGLCOLORP3UIVPROC glad_glColorP3uiv;\nPFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v;\nPFNGLCOMPILESHADERPROC glad_glCompileShader;\nPFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying;\nPFNGLDEPTHRANGEFPROC glad_glDepthRangef;\nPFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer;\nPFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui;\nPFNGLVERTEXP4UIPROC glad_glVertexP4ui;\nPFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glad_glDrawElementsInstancedBaseInstance;\nPFNGLENABLEIPROC glad_glEnablei;\nPFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui;\nPFNGLCREATESHADERPROC glad_glCreateShader;\nPFNGLISBUFFERPROC glad_glIsBuffer;\nPFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv;\nPFNGLPROGRAMUNIFORMMATRIX2DVPROC glad_glProgramUniformMatrix2dv;\nPFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers;\nPFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D;\nPFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D;\nPFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f;\nPFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate;\nPFNGLPROGRAMUNIFORMMATRIX4FVPROC glad_glProgramUniformMatrix4fv;\nPFNGLHINTPROC glad_glHint;\nPFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s;\nPFNGLSAMPLEMASKIPROC glad_glSampleMaski;\nPFNGLVERTEXP2UIPROC glad_glVertexP2ui;\nPFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv;\nPFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl;\nPFNGLPOINTSIZEPROC glad_glPointSize;\nPFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv;\nPFNGLDELETEPROGRAMPROC glad_glDeleteProgram;\nPFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv;\nPFNGLTEXSTORAGE2DPROC glad_glTexStorage2D;\nPFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage;\nPFNGLWAITSYNCPROC glad_glWaitSync;\nPFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv;\nPFNGLUNIFORM3IPROC glad_glUniform3i;\nPFNGLCLEARBUFFERFVPROC glad_glClearBufferfv;\nPFNGLPROGRAMUNIFORM1UIPROC glad_glProgramUniform1ui;\nPFNGLBLENDEQUATIONSEPARATEIPROC glad_glBlendEquationSeparatei;\nPFNGLUNIFORM3DPROC glad_glUniform3d;\nPFNGLUNIFORM3FPROC glad_glUniform3f;\nPFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv;\nPFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv;\nPFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui;\nPFNGLCOLORMASKIPROC glad_glColorMaski;\nPFNGLCLEARBUFFERFIPROC glad_glClearBufferfi;\nPFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect;\nPFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays;\nPFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback;\nPFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui;\nPFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glad_glProgramUniformMatrix3x2dv;\nPFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glad_glProgramUniformMatrix3x2fv;\nPFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv;\nPFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex;\nPFNGLVERTEXATTRIBL4DPROC glad_glVertexAttribL4d;\nPFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture;\nPFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv;\nPFNGLACTIVESHADERPROGRAMPROC glad_glActiveShaderProgram;\nPFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv;\nPFNGLUNIFORMMATRIX3DVPROC glad_glUniformMatrix3dv;\nPFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glad_glProgramUniformMatrix3x4dv;\nPFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData;\nPFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback;\nPFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui;\nPFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glad_glProgramUniformMatrix4x3fv;\nPFNGLVIEWPORTARRAYVPROC glad_glViewportArrayv;\nPFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers;\nPFNGLDRAWARRAYSPROC glad_glDrawArrays;\nPFNGLUNIFORM1UIPROC glad_glUniform1ui;\nPFNGLPROGRAMUNIFORM2UIVPROC glad_glProgramUniform2uiv;\nPFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i;\nPFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui;\nPFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d;\nPFNGLCLEARPROC glad_glClear;\nPFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri;\nPFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName;\nPFNGLMEMORYBARRIERPROC glad_glMemoryBarrier;\nPFNGLISENABLEDPROC glad_glIsEnabled;\nPFNGLSTENCILOPPROC glad_glStencilOp;\nPFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D;\nPFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv;\nPFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub;\nPFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation;\nPFNGLTEXIMAGE1DPROC glad_glTexImage1D;\nPFNGLTEXPARAMETERIVPROC glad_glTexParameteriv;\nPFNGLGETTEXIMAGEPROC glad_glGetTexImage;\nPFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v;\nPFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers;\nPFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage;\nPFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders;\nPFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer;\nPFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays;\nPFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers;\nPFNGLPROGRAMUNIFORM1UIVPROC glad_glProgramUniform1uiv;\nPFNGLISVERTEXARRAYPROC glad_glIsVertexArray;\nPFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray;\nPFNGLPROGRAMUNIFORM2IVPROC glad_glProgramUniform2iv;\nPFNGLGETQUERYIVPROC glad_glGetQueryiv;\nPFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv;\nPFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding;\nPFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glad_glProgramUniformMatrix4x2dv;\nPFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices;\nPFNGLISSHADERPROC glad_glIsShader;\nPFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv;\nPFNGLBEGINQUERYINDEXEDPROC glad_glBeginQueryIndexed;\nPFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv;\nPFNGLENABLEPROC glad_glEnable;\nPFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv;\nPFNGLBLENDEQUATIONIPROC glad_glBlendEquationi;\nPFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation;\nPFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv;\nPFNGLGETPROGRAMINTERFACEIVPROC glad_glGetProgramInterfaceiv;\nPFNGLUNIFORM2DVPROC glad_glUniform2dv;\nPFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv;\nPFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui;\nPFNGLVERTEXATTRIBL1DVPROC glad_glVertexAttribL1dv;\nPFNGLPROGRAMUNIFORM3UIPROC glad_glProgramUniform3ui;\nPFNGLVERTEXBINDINGDIVISORPROC glad_glVertexBindingDivisor;\nPFNGLGETUNIFORMFVPROC glad_glGetUniformfv;\nPFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv;\nPFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glad_glProgramUniformMatrix2x3fv;\nPFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv;\nPFNGLDRAWBUFFERPROC glad_glDrawBuffer;\nPFNGLENDQUERYINDEXEDPROC glad_glEndQueryIndexed;\nPFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv;\nPFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced;\nPFNGLPROGRAMUNIFORM1IPROC glad_glProgramUniform1i;\nPFNGLPATCHPARAMETERIPROC glad_glPatchParameteri;\nPFNGLPROGRAMUNIFORM1DPROC glad_glProgramUniform1d;\nPFNGLPROGRAMUNIFORM1FPROC glad_glProgramUniform1f;\nPFNGLFLUSHPROC glad_glFlush;\nPFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv;\nPFNGLPROGRAMUNIFORM3IVPROC glad_glProgramUniform3iv;\nPFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog;\nPFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv;\nPFNGLFENCESYNCPROC glad_glFenceSync;\nPFNGLCOLORP3UIPROC glad_glColorP3ui;\nPFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance;\nPFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv;\nPFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender;\nPFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline;\nPFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv;\nPFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv;\nPFNGLTEXSTORAGE3DMULTISAMPLEPROC glad_glTexStorage3DMultisample;\nPFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate;\nPFNGLGENSAMPLERSPROC glad_glGenSamplers;\nPFNGLCLAMPCOLORPROC glad_glClampColor;\nPFNGLUNIFORM4IVPROC glad_glUniform4iv;\nPFNGLCLEARSTENCILPROC glad_glClearStencil;\nPFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv;\nPFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glad_glDrawTransformFeedbackInstanced;\nPFNGLGENTEXTURESPROC glad_glGenTextures;\nPFNGLDRAWTRANSFORMFEEDBACKPROC glad_glDrawTransformFeedback;\nPFNGLUNIFORM1DVPROC glad_glUniform1dv;\nPFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv;\nPFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv;\nPFNGLISSYNCPROC glad_glIsSync;\nPFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName;\nPFNGLUNIFORM2IPROC glad_glUniform2i;\nPFNGLUNIFORM2FPROC glad_glUniform2f;\nPFNGLUNIFORM2DPROC glad_glUniform2d;\nPFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui;\nPFNGLGETPROGRAMIVPROC glad_glGetProgramiv;\nPFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer;\nPFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer;\nPFNGLPROGRAMUNIFORM4FVPROC glad_glProgramUniform4fv;\nPFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel;\nPFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange;\nPFNGLPROGRAMUNIFORM2FVPROC glad_glProgramUniform2fv;\nPFNGLUNIFORMMATRIX2X3DVPROC glad_glUniformMatrix2x3dv;\nPFNGLPROGRAMUNIFORMMATRIX4DVPROC glad_glProgramUniformMatrix4dv;\nPFNGLVERTEXATTRIBL3DPROC glad_glVertexAttribL3d;\nPFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glad_glProgramUniformMatrix2x4dv;\nPFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute;\nPFNGLGENQUERIESPROC glad_glGenQueries;\nPFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui;\nPFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D;\nPFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v;\nPFNGLDELETESAMPLERSPROC glad_glDeleteSamplers;\nPFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D;\nPFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer;\nPFNGLISENABLEDIPROC glad_glIsEnabledi;\nPFNGLBINDBUFFERSRANGEPROC glad_glBindBuffersRange;\nPFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui;\nPFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed;\nPFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData;\nPFNGLUNIFORM2IVPROC glad_glUniform2iv;\nPFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv;\nPFNGLUNIFORM4UIVPROC glad_glUniform4uiv;\nPFNGLPROGRAMUNIFORM2DVPROC glad_glProgramUniform2dv;\nPFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D;\nPFNGLGETSHADERIVPROC glad_glGetShaderiv;\nPFNGLPROGRAMUNIFORMMATRIX3FVPROC glad_glProgramUniformMatrix3fv;\nPFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel;\nPFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer;\nPFNGLBINDTEXTURESPROC glad_glBindTextures;\nPFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation;\nPFNGLSCISSORARRAYVPROC glad_glScissorArrayv;\nPFNGLPOLYGONOFFSETPROC glad_glPolygonOffset;\nPFNGLGETDOUBLEVPROC glad_glGetDoublev;\nPFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d;\nPFNGLUNIFORM4DVPROC glad_glUniform4dv;\nPFNGLPROGRAMUNIFORM3DVPROC glad_glProgramUniform3dv;\nPFNGLGETUNIFORMIVPROC glad_glGetUniformiv;\nPFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData;\nPFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv;\nPFNGLUNIFORM3FVPROC glad_glUniform3fv;\nPFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect;\nPFNGLDEPTHRANGEPROC glad_glDepthRange;\nPFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer;\nPFNGLMAPBUFFERPROC glad_glMapBuffer;\nPFNGLCLEARTEXIMAGEPROC glad_glClearTexImage;\nPFNGLVERTEXATTRIBLFORMATPROC glad_glVertexAttribLFormat;\nPFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D;\nPFNGLDELETESYNCPROC glad_glDeleteSync;\nPFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D;\nPFNGLUNIFORMMATRIX4DVPROC glad_glUniformMatrix4dv;\nPFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv;\nPFNGLUNIFORMMATRIX4X2DVPROC glad_glUniformMatrix4x2dv;\nPFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements;\nPFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv;\nPFNGLUNIFORM3IVPROC glad_glUniform3iv;\nPFNGLPOLYGONMODEPROC glad_glPolygonMode;\nPFNGLDRAWBUFFERSPROC glad_glDrawBuffers;\nPFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv;\nPFNGLPROGRAMUNIFORM4IVPROC glad_glProgramUniform4iv;\nPFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary;\nPFNGLUSEPROGRAMPROC glad_glUseProgram;\nPFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog;\nPFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback;\nPFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray;\nPFNGLDELETEBUFFERSPROC glad_glDeleteBuffers;\nPFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv;\nPFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex;\nPFNGLUNIFORM2UIVPROC glad_glUniform2uiv;\nPFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D;\nPFNGLFINISHPROC glad_glFinish;\nPFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed;\nPFNGLDELETESHADERPROC glad_glDeleteShader;\nPFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v;\nPFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup;\nPFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv;\nPFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glad_glGetProgramResourceLocationIndex;\nPFNGLVIEWPORTPROC glad_glViewport;\nPFNGLUNIFORM1UIVPROC glad_glUniform1uiv;\nPFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings;\nPFNGLUNIFORM2UIPROC glad_glUniform2ui;\nPFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback;\nPFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i;\nPFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage;\nPFNGLVERTEXATTRIBFORMATPROC glad_glVertexAttribFormat;\nPFNGLCLEARDEPTHPROC glad_glClearDepth;\nPFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv;\nPFNGLTEXPARAMETERFPROC glad_glTexParameterf;\nPFNGLVERTEXATTRIBBINDINGPROC glad_glVertexAttribBinding;\nPFNGLTEXPARAMETERIPROC glad_glTexParameteri;\nPFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glad_glGetActiveSubroutineUniformiv;\nPFNGLGETSHADERSOURCEPROC glad_glGetShaderSource;\nPFNGLTEXBUFFERPROC glad_glTexBuffer;\nPFNGLPIXELSTOREIPROC glad_glPixelStorei;\nPFNGLVALIDATEPROGRAMPROC glad_glValidateProgram;\nPFNGLPIXELSTOREFPROC glad_glPixelStoref;\nPFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v;\nPFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv;\nPFNGLGENPROGRAMPIPELINESPROC glad_glGenProgramPipelines;\nPFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ;\nPFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv;\nPFNGLLINKPROGRAMPROC glad_glLinkProgram;\nPFNGLBINDTEXTUREPROC glad_glBindTexture;\nPFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect;\nPFNGLGETOBJECTLABELPROC glad_glGetObjectLabel;\nPFNGLGETPROGRAMPIPELINEINFOLOGPROC glad_glGetProgramPipelineInfoLog;\nPFNGLGETSTRINGPROC glad_glGetString;\nPFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv;\nPFNGLDETACHSHADERPROC glad_glDetachShader;\nPFNGLPROGRAMUNIFORM3IPROC glad_glProgramUniform3i;\nPFNGLUNIFORMMATRIX3X4DVPROC glad_glUniformMatrix3x4dv;\nPFNGLENDQUERYPROC glad_glEndQuery;\nPFNGLNORMALP3UIPROC glad_glNormalP3ui;\nPFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri;\nPFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName;\nPFNGLUNIFORMMATRIX4X3DVPROC glad_glUniformMatrix4x3dv;\nPFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv;\nPFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui;\nPFNGLGETPROGRAMRESOURCELOCATIONPROC glad_glGetProgramResourceLocation;\nPFNGLDELETETEXTURESPROC glad_glDeleteTextures;\nPFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv;\nPFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate;\nPFNGLDELETEQUERIESPROC glad_glDeleteQueries;\nPFNGLNORMALP3UIVPROC glad_glNormalP3uiv;\nPFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f;\nPFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d;\nPFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv;\nPFNGLBINDBUFFERSBASEPROC glad_glBindBuffersBase;\nPFNGLVERTEXATTRIBL4DVPROC glad_glVertexAttribL4dv;\nPFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv;\nPFNGLPROGRAMUNIFORM1DVPROC glad_glProgramUniform1dv;\nPFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s;\nPFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex;\nPFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage;\nPFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri;\nPFNGLCLEARBUFFERSUBDATAPROC glad_glClearBufferSubData;\nPFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf;\nPFNGLTEXSTORAGE1DPROC glad_glTexStorage1D;\nPFNGLUNIFORM1FPROC glad_glUniform1f;\nPFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv;\nPFNGLUNIFORM1DPROC glad_glUniform1d;\nPFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage;\nPFNGLUNIFORM1IPROC glad_glUniform1i;\nPFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib;\nPFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D;\nPFNGLDISABLEPROC glad_glDisable;\nPFNGLLOGICOPPROC glad_glLogicOp;\nPFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glad_glProgramUniformMatrix3x4fv;\nPFNGLPROGRAMUNIFORM4UIVPROC glad_glProgramUniform4uiv;\nPFNGLUNIFORM4UIPROC glad_glUniform4ui;\nPFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer;\nPFNGLCULLFACEPROC glad_glCullFace;\nPFNGLPROGRAMUNIFORM4IPROC glad_glProgramUniform4i;\nPFNGLPROGRAMUNIFORM4FPROC glad_glProgramUniform4f;\nPFNGLVIEWPORTINDEXEDFPROC glad_glViewportIndexedf;\nPFNGLPROGRAMUNIFORM4DPROC glad_glProgramUniform4d;\nPFNGLGETSTRINGIPROC glad_glGetStringi;\nPFNGLSCISSORINDEXEDPROC glad_glScissorIndexed;\nPFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream;\nPFNGLATTACHSHADERPROC glad_glAttachShader;\nPFNGLQUERYCOUNTERPROC glad_glQueryCounter;\nPFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex;\nPFNGLSHADERBINARYPROC glad_glShaderBinary;\nPFNGLDRAWELEMENTSPROC glad_glDrawElements;\nPFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv;\nPFNGLUNIFORM1IVPROC glad_glUniform1iv;\nPFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv;\nPFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv;\nPFNGLREADBUFFERPROC glad_glReadBuffer;\nPFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv;\nPFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced;\nPFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap;\nPFNGLPROGRAMUNIFORMMATRIX2FVPROC glad_glProgramUniformMatrix2fv;\nPFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv;\nPFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f;\nPFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv;\nPFNGLPOINTPARAMETERIPROC glad_glPointParameteri;\nPFNGLBLENDCOLORPROC glad_glBlendColor;\nPFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv;\nPFNGLUNMAPBUFFERPROC glad_glUnmapBuffer;\nPFNGLPOINTPARAMETERFPROC glad_glPointParameterf;\nPFNGLPROGRAMUNIFORM1IVPROC glad_glProgramUniform1iv;\nPFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s;\nPFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer;\nPFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv;\nPFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv;\nPFNGLISPROGRAMPROC glad_glIsProgram;\nPFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv;\nPFNGLUNIFORMMATRIX3X2DVPROC glad_glUniformMatrix3x2dv;\nPFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv;\nPFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glad_glProgramUniformMatrix2x3dv;\nPFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback;\nPFNGLUNIFORM4IPROC glad_glUniform4i;\nPFNGLACTIVETEXTUREPROC glad_glActiveTexture;\nPFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray;\nPFNGLISPROGRAMPIPELINEPROC glad_glIsProgramPipeline;\nPFNGLREADPIXELSPROC glad_glReadPixels;\nPFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv;\nPFNGLUNIFORM4FPROC glad_glUniform4f;\nPFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample;\nPFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv;\nPFNGLVERTEXATTRIBLPOINTERPROC glad_glVertexAttribLPointer;\nPFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex;\nPFNGLVERTEXATTRIBL2DVPROC glad_glVertexAttribL2dv;\nPFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced;\nPFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName;\nPFNGLVERTEXATTRIBL2DPROC glad_glVertexAttribL2d;\nPFNGLSTENCILFUNCPROC glad_glStencilFunc;\nPFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup;\nPFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding;\nPFNGLCOLORP4UIPROC glad_glColorP4ui;\nPFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages;\nPFNGLPROGRAMUNIFORM3FPROC glad_glProgramUniform3f;\nPFNGLPROGRAMUNIFORM3DPROC glad_glProgramUniform3d;\nPFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv;\nPFNGLGETPROGRAMPIPELINEIVPROC glad_glGetProgramPipelineiv;\nPFNGLTEXSTORAGE3DPROC glad_glTexStorage3D;\nPFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv;\nPFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog;\nPFNGLOBJECTLABELPROC glad_glObjectLabel;\nPFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i;\nPFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData;\nPFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv;\nPFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate;\nPFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui;\nPFNGLGENBUFFERSPROC glad_glGenBuffers;\nPFNGLGETSUBROUTINEINDEXPROC glad_glGetSubroutineIndex;\nPFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv;\nPFNGLBLENDFUNCPROC glad_glBlendFunc;\nPFNGLCREATEPROGRAMPROC glad_glCreateProgram;\nPFNGLTEXIMAGE3DPROC glad_glTexImage3D;\nPFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer;\nPFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex;\nPFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage;\nPFNGLBINDIMAGETEXTURESPROC glad_glBindImageTextures;\nPFNGLGETINTEGER64VPROC glad_glGetInteger64v;\nPFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline;\nPFNGLSCISSORPROC glad_glScissor;\nPFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv;\nPFNGLGETBOOLEANVPROC glad_glGetBooleanv;\nPFNGLVERTEXP2UIVPROC glad_glVertexP2uiv;\nPFNGLUNIFORM3UIVPROC glad_glUniform3uiv;\nPFNGLCLEARCOLORPROC glad_glClearColor;\nPFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv;\nPFNGLCLEARBUFFERIVPROC glad_glClearBufferiv;\nPFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v;\nPFNGLPROGRAMUNIFORM4DVPROC glad_glProgramUniform4dv;\nPFNGLCOLORP4UIVPROC glad_glColorP4uiv;\nPFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv;\nPFNGLUNIFORM3UIPROC glad_glUniform3ui;\nPFNGLPROGRAMUNIFORM3UIVPROC glad_glProgramUniform3uiv;\nPFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv;\nPFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv;\nPFNGLUNIFORM2FVPROC glad_glUniform2fv;\nPFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glad_glGetActiveSubroutineUniformName;\nPFNGLGETPROGRAMRESOURCEINDEXPROC glad_glGetProgramResourceIndex;\nPFNGLDRAWELEMENTSINDIRECTPROC glad_glDrawElementsIndirect;\nPFNGLDISPATCHCOMPUTEINDIRECTPROC glad_glDispatchComputeIndirect;\nPFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv;\nPFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange;\nPFNGLVERTEXATTRIBL3DVPROC glad_glVertexAttribL3dv;\nPFNGLGETUNIFORMDVPROC glad_glGetUniformdv;\nPFNGLCLEARDEPTHFPROC glad_glClearDepthf;\nPFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv;\nPFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks;\nPFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv;\nPFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv;\nPFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glad_glProgramUniformMatrix4x3dv;\nPFNGLDEPTHFUNCPROC glad_glDepthFunc;\nPFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D;\nPFNGLPROGRAMBINARYPROC glad_glProgramBinary;\nPFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv;\nPFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv;\nPFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui;\nPFNGLBUFFERSTORAGEPROC glad_glBufferStorage;\nPFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync;\nPFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui;\nPFNGLGETFLOATI_VPROC glad_glGetFloati_v;\nPFNGLCOLORMASKPROC glad_glColorMask;\nPFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv;\nPFNGLBLENDEQUATIONPROC glad_glBlendEquation;\nPFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation;\nPFNGLUNIFORMMATRIX2X4DVPROC glad_glUniformMatrix2x4dv;\nPFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback;\nPFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv;\nPFNGLGETUNIFORMSUBROUTINEUIVPROC glad_glGetUniformSubroutineuiv;\nPFNGLUNIFORM4FVPROC glad_glUniform4fv;\nPFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer;\nPFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert;\nPFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv;\nPFNGLCLEARBUFFERDATAPROC glad_glClearBufferData;\nPFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback;\nPFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv;\nPFNGLISSAMPLERPROC glad_glIsSampler;\nPFNGLVERTEXP3UIPROC glad_glVertexP3ui;\nPFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor;\nPFNGLBINDSAMPLERSPROC glad_glBindSamplers;\nPFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D;\nPFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks;\nPFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D;\nPFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex;\nPFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus;\nPFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender;\nPFNGLVERTEXP3UIVPROC glad_glVertexP3uiv;\nPFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation;\nPFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv;\nPFNGLUNIFORMMATRIX2DVPROC glad_glUniformMatrix2dv;\nPFNGLBLENDFUNCIPROC glad_glBlendFunci;\nPFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv;\nPFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements;\nPFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv;\nPFNGLBINDBUFFERBASEPROC glad_glBindBufferBase;\nPFNGLBUFFERSUBDATAPROC glad_glBufferSubData;\nPFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv;\nPFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange;\nPFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture;\nPFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei;\nPFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glad_glProgramUniformMatrix4x2fv;\nPFNGLVERTEXATTRIBL1DPROC glad_glVertexAttribL1d;\nPFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays;\nPFNGLVERTEXP4UIVPROC glad_glVertexP4uiv;\nPFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv;\nPFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat;\nPFNGLTEXTUREVIEWPROC glad_glTextureView;\nPFNGLDISABLEIPROC glad_glDisablei;\nPFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glad_glProgramUniformMatrix2x4fv;\nPFNGLSHADERSOURCEPROC glad_glShaderSource;\nPFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers;\nPFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv;\nPFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler;\nPFNGLVERTEXATTRIBIFORMATPROC glad_glVertexAttribIFormat;\nPFNGLGETSYNCIVPROC glad_glGetSynciv;\nPFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv;\nPFNGLPATCHPARAMETERFVPROC glad_glPatchParameterfv;\nPFNGLPROGRAMUNIFORM2IPROC glad_glProgramUniform2i;\nPFNGLBEGINQUERYPROC glad_glBeginQuery;\nPFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv;\nPFNGLBINDBUFFERPROC glad_glBindBuffer;\nPFNGLTEXSTORAGE2DMULTISAMPLEPROC glad_glTexStorage2DMultisample;\nPFNGLPROGRAMUNIFORM2DPROC glad_glProgramUniform2d;\nPFNGLPROGRAMUNIFORM2FPROC glad_glProgramUniform2f;\nPFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv;\nPFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv;\nPFNGLBUFFERDATAPROC glad_glBufferData;\nPFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv;\nPFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui;\nPFNGLGETERRORPROC glad_glGetError;\nPFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui;\nPFNGLGETFLOATVPROC glad_glGetFloatv;\nPFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D;\nPFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv;\nPFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv;\nPFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i;\nPFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv;\nPFNGLUNIFORM4DPROC glad_glUniform4d;\nPFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv;\nPFNGLGETINTEGERVPROC glad_glGetIntegerv;\nPFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv;\nPFNGLPROGRAMUNIFORMMATRIX3DVPROC glad_glProgramUniformMatrix3dv;\nPFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D;\nPFNGLISQUERYPROC glad_glIsQuery;\nPFNGLPROGRAMUNIFORM2UIPROC glad_glProgramUniform2ui;\nPFNGLPROGRAMUNIFORM4UIPROC glad_glProgramUniform4ui;\nPFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv;\nPFNGLTEXIMAGE2DPROC glad_glTexImage2D;\nPFNGLSTENCILMASKPROC glad_glStencilMask;\nPFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv;\nPFNGLISTEXTUREPROC glad_glIsTexture;\nPFNGLUNIFORM1FVPROC glad_glUniform1fv;\nPFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv;\nPFNGLTEXPARAMETERFVPROC glad_glTexParameterfv;\nPFNGLSCISSORINDEXEDVPROC glad_glScissorIndexedv;\nPFNGLUNIFORM3DVPROC glad_glUniform3dv;\nPFNGLPROGRAMUNIFORM3FVPROC glad_glProgramUniform3fv;\nPFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glad_glGetSubroutineUniformLocation;\nPFNGLGETFRAMEBUFFERPARAMETERIVPROC glad_glGetFramebufferParameteriv;\nPFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv;\nPFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData;\nPFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv;\nPFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d;\nPFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f;\nPFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv;\nPFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v;\nPFNGLDEPTHMASKPROC glad_glDepthMask;\nPFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s;\nPFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample;\nPFNGLPROGRAMUNIFORM1FVPROC glad_glProgramUniform1fv;\nPFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex;\nPFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample;\nPFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform;\nPFNGLFRONTFACEPROC glad_glFrontFace;\nPFNGLTEXBUFFERRANGEPROC glad_glTexBufferRange;\nPFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance;\nPFNGLDELETEPROGRAMPIPELINESPROC glad_glDeleteProgramPipelines;\nint GLAD_GL_SGIX_pixel_tiles;\nint GLAD_GL_NV_point_sprite;\nint GLAD_GL_APPLE_element_array;\nint GLAD_GL_AMD_multi_draw_indirect;\nint GLAD_GL_EXT_blend_subtract;\nint GLAD_GL_SGIX_tag_sample_buffer;\nint GLAD_GL_IBM_texture_mirrored_repeat;\nint GLAD_GL_APPLE_transform_hint;\nint GLAD_GL_ATI_separate_stencil;\nint GLAD_GL_NV_vertex_program2_option;\nint GLAD_GL_EXT_texture_buffer_object;\nint GLAD_GL_ARB_vertex_blend;\nint GLAD_GL_NV_vertex_program2;\nint GLAD_GL_ARB_program_interface_query;\nint GLAD_GL_EXT_misc_attribute;\nint GLAD_GL_NV_multisample_coverage;\nint GLAD_GL_ARB_shading_language_packing;\nint GLAD_GL_EXT_texture_cube_map;\nint GLAD_GL_ARB_texture_stencil8;\nint GLAD_GL_EXT_index_func;\nint GLAD_GL_OES_compressed_paletted_texture;\nint GLAD_GL_NV_depth_clamp;\nint GLAD_GL_NV_shader_buffer_load;\nint GLAD_GL_EXT_color_subtable;\nint GLAD_GL_SUNX_constant_data;\nint GLAD_GL_EXT_multi_draw_arrays;\nint GLAD_GL_ARB_shader_atomic_counters;\nint GLAD_GL_ARB_arrays_of_arrays;\nint GLAD_GL_NV_conditional_render;\nint GLAD_GL_EXT_texture_env_combine;\nint GLAD_GL_NV_fog_distance;\nint GLAD_GL_SGIX_async_histogram;\nint GLAD_GL_MESA_resize_buffers;\nint GLAD_GL_NV_light_max_exponent;\nint GLAD_GL_NV_texture_env_combine4;\nint GLAD_GL_ARB_texture_view;\nint GLAD_GL_ARB_texture_env_combine;\nint GLAD_GL_ARB_map_buffer_range;\nint GLAD_GL_EXT_convolution;\nint GLAD_GL_NV_compute_program5;\nint GLAD_GL_NV_vertex_attrib_integer_64bit;\nint GLAD_GL_EXT_paletted_texture;\nint GLAD_GL_ARB_texture_buffer_object;\nint GLAD_GL_ATI_pn_triangles;\nint GLAD_GL_SGIX_resample;\nint GLAD_GL_SGIX_flush_raster;\nint GLAD_GL_EXT_light_texture;\nint GLAD_GL_ARB_point_sprite;\nint GLAD_GL_ARB_half_float_pixel;\nint GLAD_GL_NV_tessellation_program5;\nint GLAD_GL_REND_screen_coordinates;\nint GLAD_GL_EXT_shared_texture_palette;\nint GLAD_GL_EXT_packed_float;\nint GLAD_GL_OML_subsample;\nint GLAD_GL_SGIX_vertex_preclip;\nint GLAD_GL_SGIX_texture_scale_bias;\nint GLAD_GL_AMD_draw_buffers_blend;\nint GLAD_GL_MESA_window_pos;\nint GLAD_GL_EXT_texture_array;\nint GLAD_GL_NV_texture_barrier;\nint GLAD_GL_ARB_texture_query_levels;\nint GLAD_GL_NV_texgen_emboss;\nint GLAD_GL_EXT_texture_swizzle;\nint GLAD_GL_ARB_texture_rg;\nint GLAD_GL_ARB_vertex_type_2_10_10_10_rev;\nint GLAD_GL_ARB_fragment_shader;\nint GLAD_GL_3DFX_tbuffer;\nint GLAD_GL_GREMEDY_frame_terminator;\nint GLAD_GL_ARB_blend_func_extended;\nint GLAD_GL_EXT_separate_shader_objects;\nint GLAD_GL_NV_texture_multisample;\nint GLAD_GL_ARB_shader_objects;\nint GLAD_GL_ARB_framebuffer_object;\nint GLAD_GL_ATI_envmap_bumpmap;\nint GLAD_GL_ARB_robust_buffer_access_behavior;\nint GLAD_GL_ARB_shader_stencil_export;\nint GLAD_GL_NV_texture_rectangle;\nint GLAD_GL_ARB_enhanced_layouts;\nint GLAD_GL_ARB_texture_rectangle;\nint GLAD_GL_SGI_texture_color_table;\nint GLAD_GL_ATI_map_object_buffer;\nint GLAD_GL_ARB_robustness;\nint GLAD_GL_NV_pixel_data_range;\nint GLAD_GL_EXT_framebuffer_blit;\nint GLAD_GL_ARB_gpu_shader_fp64;\nint GLAD_GL_SGIX_depth_texture;\nint GLAD_GL_EXT_vertex_weighting;\nint GLAD_GL_GREMEDY_string_marker;\nint GLAD_GL_ARB_texture_compression_bptc;\nint GLAD_GL_EXT_subtexture;\nint GLAD_GL_EXT_pixel_transform_color_table;\nint GLAD_GL_EXT_texture_compression_rgtc;\nint GLAD_GL_SGIX_depth_pass_instrument;\nint GLAD_GL_ARB_shader_precision;\nint GLAD_GL_NV_evaluators;\nint GLAD_GL_SGIS_texture_filter4;\nint GLAD_GL_AMD_performance_monitor;\nint GLAD_GL_NV_geometry_shader4;\nint GLAD_GL_EXT_stencil_clear_tag;\nint GLAD_GL_NV_vertex_program1_1;\nint GLAD_GL_NV_present_video;\nint GLAD_GL_ARB_texture_compression_rgtc;\nint GLAD_GL_HP_convolution_border_modes;\nint GLAD_GL_EXT_gpu_program_parameters;\nint GLAD_GL_EXT_shader_integer_mix;\nint GLAD_GL_ARB_stencil_texturing;\nint GLAD_GL_SGIX_fog_offset;\nint GLAD_GL_ARB_draw_elements_base_vertex;\nint GLAD_GL_INGR_interlace_read;\nint GLAD_GL_NV_transform_feedback;\nint GLAD_GL_NV_fragment_program;\nint GLAD_GL_AMD_stencil_operation_extended;\nint GLAD_GL_ARB_seamless_cubemap_per_texture;\nint GLAD_GL_ARB_instanced_arrays;\nint GLAD_GL_EXT_polygon_offset;\nint GLAD_GL_NV_vertex_array_range2;\nint GLAD_GL_AMD_sparse_texture;\nint GLAD_GL_NV_fence;\nint GLAD_GL_ARB_texture_buffer_range;\nint GLAD_GL_SUN_mesh_array;\nint GLAD_GL_ARB_vertex_attrib_binding;\nint GLAD_GL_ARB_framebuffer_no_attachments;\nint GLAD_GL_ARB_cl_event;\nint GLAD_GL_NV_packed_depth_stencil;\nint GLAD_GL_OES_single_precision;\nint GLAD_GL_NV_primitive_restart;\nint GLAD_GL_SUN_global_alpha;\nint GLAD_GL_EXT_texture_object;\nint GLAD_GL_AMD_name_gen_delete;\nint GLAD_GL_NV_texture_compression_vtc;\nint GLAD_GL_SGIX_ycrcb_subsample;\nint GLAD_GL_NV_texture_shader3;\nint GLAD_GL_NV_texture_shader2;\nint GLAD_GL_EXT_texture;\nint GLAD_GL_ARB_buffer_storage;\nint GLAD_GL_AMD_shader_atomic_counter_ops;\nint GLAD_GL_APPLE_vertex_program_evaluators;\nint GLAD_GL_ARB_multi_bind;\nint GLAD_GL_ARB_explicit_uniform_location;\nint GLAD_GL_ARB_depth_buffer_float;\nint GLAD_GL_SGIX_shadow_ambient;\nint GLAD_GL_ARB_texture_cube_map;\nint GLAD_GL_AMD_vertex_shader_viewport_index;\nint GLAD_GL_SGIX_list_priority;\nint GLAD_GL_NV_vertex_buffer_unified_memory;\nint GLAD_GL_EXT_texture_env_dot3;\nint GLAD_GL_ATI_texture_env_combine3;\nint GLAD_GL_ARB_map_buffer_alignment;\nint GLAD_GL_NV_blend_equation_advanced;\nint GLAD_GL_SGIS_sharpen_texture;\nint GLAD_GL_ARB_vertex_program;\nint GLAD_GL_ARB_texture_rgb10_a2ui;\nint GLAD_GL_OML_interlace;\nint GLAD_GL_ATI_pixel_format_float;\nint GLAD_GL_ARB_vertex_buffer_object;\nint GLAD_GL_EXT_shadow_funcs;\nint GLAD_GL_ATI_text_fragment_shader;\nint GLAD_GL_NV_vertex_array_range;\nint GLAD_GL_SGIX_fragment_lighting;\nint GLAD_GL_NV_texture_expand_normal;\nint GLAD_GL_NV_framebuffer_multisample_coverage;\nint GLAD_GL_EXT_timer_query;\nint GLAD_GL_EXT_vertex_array_bgra;\nint GLAD_GL_NV_bindless_texture;\nint GLAD_GL_KHR_debug;\nint GLAD_GL_SGIS_texture_border_clamp;\nint GLAD_GL_ATI_vertex_attrib_array_object;\nint GLAD_GL_SGIX_clipmap;\nint GLAD_GL_EXT_geometry_shader4;\nint GLAD_GL_MESA_ycbcr_texture;\nint GLAD_GL_MESAX_texture_stack;\nint GLAD_GL_AMD_seamless_cubemap_per_texture;\nint GLAD_GL_EXT_bindable_uniform;\nint GLAD_GL_KHR_texture_compression_astc_hdr;\nint GLAD_GL_ARB_fragment_program_shadow;\nint GLAD_GL_ATI_element_array;\nint GLAD_GL_AMD_texture_texture4;\nint GLAD_GL_SGIX_reference_plane;\nint GLAD_GL_EXT_stencil_two_side;\nint GLAD_GL_SGIX_texture_lod_bias;\nint GLAD_GL_NV_explicit_multisample;\nint GLAD_GL_IBM_static_data;\nint GLAD_GL_EXT_clip_volume_hint;\nint GLAD_GL_EXT_texture_perturb_normal;\nint GLAD_GL_NV_fragment_program2;\nint GLAD_GL_NV_fragment_program4;\nint GLAD_GL_EXT_point_parameters;\nint GLAD_GL_PGI_misc_hints;\nint GLAD_GL_SGIX_subsample;\nint GLAD_GL_AMD_shader_stencil_export;\nint GLAD_GL_ARB_shader_texture_lod;\nint GLAD_GL_ARB_vertex_shader;\nint GLAD_GL_ARB_depth_clamp;\nint GLAD_GL_SGIS_texture_select;\nint GLAD_GL_NV_texture_shader;\nint GLAD_GL_ARB_tessellation_shader;\nint GLAD_GL_EXT_draw_buffers2;\nint GLAD_GL_ARB_vertex_attrib_64bit;\nint GLAD_GL_WIN_specular_fog;\nint GLAD_GL_AMD_interleaved_elements;\nint GLAD_GL_ARB_fragment_program;\nint GLAD_GL_OML_resample;\nint GLAD_GL_APPLE_ycbcr_422;\nint GLAD_GL_SGIX_texture_add_env;\nint GLAD_GL_ARB_shadow_ambient;\nint GLAD_GL_ARB_texture_storage;\nint GLAD_GL_EXT_pixel_buffer_object;\nint GLAD_GL_ARB_copy_image;\nint GLAD_GL_SGIS_pixel_texture;\nint GLAD_GL_SGIS_generate_mipmap;\nint GLAD_GL_SGIX_instruments;\nint GLAD_GL_HP_texture_lighting;\nint GLAD_GL_ARB_shader_storage_buffer_object;\nint GLAD_GL_EXT_blend_minmax;\nint GLAD_GL_MESA_pack_invert;\nint GLAD_GL_ARB_base_instance;\nint GLAD_GL_SGIX_convolution_accuracy;\nint GLAD_GL_PGI_vertex_hints;\nint GLAD_GL_EXT_texture_integer;\nint GLAD_GL_ARB_texture_multisample;\nint GLAD_GL_S3_s3tc;\nint GLAD_GL_ARB_query_buffer_object;\nint GLAD_GL_AMD_vertex_shader_tessellator;\nint GLAD_GL_ARB_invalidate_subdata;\nint GLAD_GL_EXT_index_material;\nint GLAD_GL_NV_blend_equation_advanced_coherent;\nint GLAD_GL_INTEL_parallel_arrays;\nint GLAD_GL_ATI_draw_buffers;\nint GLAD_GL_EXT_cmyka;\nint GLAD_GL_SGIX_pixel_texture;\nint GLAD_GL_APPLE_specular_vector;\nint GLAD_GL_ARB_compatibility;\nint GLAD_GL_ARB_timer_query;\nint GLAD_GL_SGIX_interlace;\nint GLAD_GL_NV_parameter_buffer_object;\nint GLAD_GL_AMD_shader_trinary_minmax;\nint GLAD_GL_EXT_rescale_normal;\nint GLAD_GL_ARB_pixel_buffer_object;\nint GLAD_GL_ARB_uniform_buffer_object;\nint GLAD_GL_ARB_vertex_type_10f_11f_11f_rev;\nint GLAD_GL_ARB_texture_swizzle;\nint GLAD_GL_NV_transform_feedback2;\nint GLAD_GL_SGIX_async_pixel;\nint GLAD_GL_NV_fragment_program_option;\nint GLAD_GL_ARB_explicit_attrib_location;\nint GLAD_GL_EXT_blend_color;\nint GLAD_GL_NV_shader_thread_group;\nint GLAD_GL_EXT_stencil_wrap;\nint GLAD_GL_EXT_index_array_formats;\nint GLAD_GL_EXT_histogram;\nint GLAD_GL_SGIS_point_parameters;\nint GLAD_GL_EXT_direct_state_access;\nint GLAD_GL_AMD_sample_positions;\nint GLAD_GL_NV_vertex_program;\nint GLAD_GL_NV_shader_thread_shuffle;\nint GLAD_GL_NVX_conditional_render;\nint GLAD_GL_EXT_vertex_shader;\nint GLAD_GL_EXT_blend_func_separate;\nint GLAD_GL_APPLE_fence;\nint GLAD_GL_OES_byte_coordinates;\nint GLAD_GL_ARB_transpose_matrix;\nint GLAD_GL_ARB_provoking_vertex;\nint GLAD_GL_EXT_fog_coord;\nint GLAD_GL_EXT_vertex_array;\nint GLAD_GL_ARB_half_float_vertex;\nint GLAD_GL_EXT_blend_equation_separate;\nint GLAD_GL_ARB_multi_draw_indirect;\nint GLAD_GL_NV_copy_image;\nint GLAD_GL_ARB_fragment_layer_viewport;\nint GLAD_GL_ARB_transform_feedback2;\nint GLAD_GL_ARB_transform_feedback3;\nint GLAD_GL_SGIX_ycrcba;\nint GLAD_GL_EXT_debug_marker;\nint GLAD_GL_EXT_bgra;\nint GLAD_GL_EXT_texture_compression_s3tc;\nint GLAD_GL_EXT_pixel_transform;\nint GLAD_GL_ARB_conservative_depth;\nint GLAD_GL_ATI_fragment_shader;\nint GLAD_GL_ARB_vertex_array_object;\nint GLAD_GL_SUN_triangle_list;\nint GLAD_GL_EXT_texture_env_add;\nint GLAD_GL_EXT_packed_depth_stencil;\nint GLAD_GL_EXT_texture_mirror_clamp;\nint GLAD_GL_NV_multisample_filter_hint;\nint GLAD_GL_APPLE_float_pixels;\nint GLAD_GL_ARB_transform_feedback_instanced;\nint GLAD_GL_SGIX_async;\nint GLAD_GL_EXT_texture_compression_latc;\nint GLAD_GL_NV_shader_atomic_float;\nint GLAD_GL_ARB_shading_language_100;\nint GLAD_GL_INTEL_performance_query;\nint GLAD_GL_ARB_texture_mirror_clamp_to_edge;\nint GLAD_GL_NV_gpu_shader5;\nint GLAD_GL_ARB_ES2_compatibility;\nint GLAD_GL_ARB_indirect_parameters;\nint GLAD_GL_NV_half_float;\nint GLAD_GL_EXT_coordinate_frame;\nint GLAD_GL_ATI_texture_mirror_once;\nint GLAD_GL_IBM_rasterpos_clip;\nint GLAD_GL_SGIX_shadow;\nint GLAD_GL_NV_deep_texture3D;\nint GLAD_GL_ARB_shader_draw_parameters;\nint GLAD_GL_SGIX_calligraphic_fragment;\nint GLAD_GL_ARB_shader_bit_encoding;\nint GLAD_GL_EXT_compiled_vertex_array;\nint GLAD_GL_NV_depth_buffer_float;\nint GLAD_GL_NV_occlusion_query;\nint GLAD_GL_APPLE_flush_buffer_range;\nint GLAD_GL_ARB_imaging;\nint GLAD_GL_ARB_draw_buffers_blend;\nint GLAD_GL_NV_blend_square;\nint GLAD_GL_AMD_blend_minmax_factor;\nint GLAD_GL_EXT_texture_sRGB_decode;\nint GLAD_GL_ARB_shading_language_420pack;\nint GLAD_GL_ATI_meminfo;\nint GLAD_GL_EXT_abgr;\nint GLAD_GL_AMD_pinned_memory;\nint GLAD_GL_EXT_texture_snorm;\nint GLAD_GL_SGIX_texture_coordinate_clamp;\nint GLAD_GL_ARB_clear_buffer_object;\nint GLAD_GL_ARB_multisample;\nint GLAD_GL_EXT_debug_label;\nint GLAD_GL_ARB_sample_shading;\nint GLAD_GL_INTEL_map_texture;\nint GLAD_GL_ARB_texture_env_crossbar;\nint GLAD_GL_EXT_422_pixels;\nint GLAD_GL_ARB_compute_shader;\nint GLAD_GL_EXT_blend_logic_op;\nint GLAD_GL_IBM_cull_vertex;\nint GLAD_GL_IBM_vertex_array_lists;\nint GLAD_GL_ARB_color_buffer_float;\nint GLAD_GL_ARB_bindless_texture;\nint GLAD_GL_ARB_window_pos;\nint GLAD_GL_ARB_internalformat_query;\nint GLAD_GL_ARB_shadow;\nint GLAD_GL_ARB_texture_mirrored_repeat;\nint GLAD_GL_EXT_shader_image_load_store;\nint GLAD_GL_EXT_copy_texture;\nint GLAD_GL_NV_register_combiners2;\nint GLAD_GL_SGIX_ir_instrument1;\nint GLAD_GL_NV_draw_texture;\nint GLAD_GL_EXT_texture_shared_exponent;\nint GLAD_GL_EXT_draw_instanced;\nint GLAD_GL_NV_copy_depth_to_color;\nint GLAD_GL_ARB_viewport_array;\nint GLAD_GL_ARB_separate_shader_objects;\nint GLAD_GL_EXT_depth_bounds_test;\nint GLAD_GL_HP_image_transform;\nint GLAD_GL_ARB_texture_env_add;\nint GLAD_GL_NV_video_capture;\nint GLAD_GL_ARB_sampler_objects;\nint GLAD_GL_ARB_matrix_palette;\nint GLAD_GL_SGIS_texture_color_mask;\nint GLAD_GL_EXT_packed_pixels;\nint GLAD_GL_ARB_texture_compression;\nint GLAD_GL_APPLE_aux_depth_stencil;\nint GLAD_GL_ARB_shader_subroutine;\nint GLAD_GL_EXT_framebuffer_sRGB;\nint GLAD_GL_ARB_texture_storage_multisample;\nint GLAD_GL_EXT_vertex_attrib_64bit;\nint GLAD_GL_ARB_depth_texture;\nint GLAD_GL_NV_shader_buffer_store;\nint GLAD_GL_OES_query_matrix;\nint GLAD_GL_APPLE_texture_range;\nint GLAD_GL_NV_shader_storage_buffer_object;\nint GLAD_GL_ARB_texture_query_lod;\nint GLAD_GL_ARB_copy_buffer;\nint GLAD_GL_ARB_shader_image_size;\nint GLAD_GL_NV_shader_atomic_counters;\nint GLAD_GL_APPLE_object_purgeable;\nint GLAD_GL_ARB_occlusion_query;\nint GLAD_GL_INGR_color_clamp;\nint GLAD_GL_SGI_color_table;\nint GLAD_GL_NV_gpu_program5_mem_extended;\nint GLAD_GL_ARB_texture_cube_map_array;\nint GLAD_GL_SGIX_scalebias_hint;\nint GLAD_GL_EXT_gpu_shader4;\nint GLAD_GL_NV_geometry_program4;\nint GLAD_GL_EXT_framebuffer_multisample_blit_scaled;\nint GLAD_GL_AMD_debug_output;\nint GLAD_GL_ARB_texture_border_clamp;\nint GLAD_GL_ARB_fragment_coord_conventions;\nint GLAD_GL_ARB_multitexture;\nint GLAD_GL_SGIX_polynomial_ffd;\nint GLAD_GL_EXT_provoking_vertex;\nint GLAD_GL_ARB_point_parameters;\nint GLAD_GL_ARB_shader_image_load_store;\nint GLAD_GL_HP_occlusion_test;\nint GLAD_GL_ARB_ES3_compatibility;\nint GLAD_GL_SGIX_framezoom;\nint GLAD_GL_ARB_texture_buffer_object_rgb32;\nint GLAD_GL_NV_bindless_multi_draw_indirect;\nint GLAD_GL_SGIX_texture_multi_buffer;\nint GLAD_GL_EXT_transform_feedback;\nint GLAD_GL_KHR_texture_compression_astc_ldr;\nint GLAD_GL_3DFX_multisample;\nint GLAD_GL_INTEL_fragment_shader_ordering;\nint GLAD_GL_ARB_texture_env_dot3;\nint GLAD_GL_NV_gpu_program4;\nint GLAD_GL_NV_gpu_program5;\nint GLAD_GL_NV_float_buffer;\nint GLAD_GL_SGIS_texture_edge_clamp;\nint GLAD_GL_ARB_framebuffer_sRGB;\nint GLAD_GL_SUN_slice_accum;\nint GLAD_GL_EXT_index_texture;\nint GLAD_GL_EXT_shader_image_load_formatted;\nint GLAD_GL_ARB_geometry_shader4;\nint GLAD_GL_EXT_separate_specular_color;\nint GLAD_GL_AMD_depth_clamp_separate;\nint GLAD_GL_SUN_convolution_border_modes;\nint GLAD_GL_SGIX_sprite;\nint GLAD_GL_ARB_get_program_binary;\nint GLAD_GL_AMD_occlusion_query_event;\nint GLAD_GL_SGIS_multisample;\nint GLAD_GL_EXT_framebuffer_object;\nint GLAD_GL_ARB_robustness_isolation;\nint GLAD_GL_ARB_vertex_array_bgra;\nint GLAD_GL_APPLE_vertex_array_range;\nint GLAD_GL_AMD_query_buffer_object;\nint GLAD_GL_NV_register_combiners;\nint GLAD_GL_ARB_draw_buffers;\nint GLAD_GL_ARB_clear_texture;\nint GLAD_GL_ARB_debug_output;\nint GLAD_GL_SGI_color_matrix;\nint GLAD_GL_EXT_cull_vertex;\nint GLAD_GL_EXT_texture_sRGB;\nint GLAD_GL_APPLE_row_bytes;\nint GLAD_GL_NV_texgen_reflection;\nint GLAD_GL_IBM_multimode_draw_arrays;\nint GLAD_GL_APPLE_vertex_array_object;\nint GLAD_GL_3DFX_texture_compression_FXT1;\nint GLAD_GL_SGIX_ycrcb;\nint GLAD_GL_AMD_conservative_depth;\nint GLAD_GL_ARB_texture_float;\nint GLAD_GL_ARB_compressed_texture_pixel_storage;\nint GLAD_GL_SGIS_detail_texture;\nint GLAD_GL_ARB_draw_instanced;\nint GLAD_GL_OES_read_format;\nint GLAD_GL_ATI_texture_float;\nint GLAD_GL_ARB_texture_gather;\nint GLAD_GL_AMD_vertex_shader_layer;\nint GLAD_GL_ARB_shading_language_include;\nint GLAD_GL_APPLE_client_storage;\nint GLAD_GL_WIN_phong_shading;\nint GLAD_GL_INGR_blend_func_separate;\nint GLAD_GL_NV_path_rendering;\nint GLAD_GL_ATI_vertex_streams;\nint GLAD_GL_ARB_texture_non_power_of_two;\nint GLAD_GL_APPLE_rgb_422;\nint GLAD_GL_EXT_texture_lod_bias;\nint GLAD_GL_ARB_seamless_cube_map;\nint GLAD_GL_ARB_shader_group_vote;\nint GLAD_GL_NV_vdpau_interop;\nint GLAD_GL_ARB_occlusion_query2;\nint GLAD_GL_ARB_internalformat_query2;\nint GLAD_GL_EXT_texture_filter_anisotropic;\nint GLAD_GL_SUN_vertex;\nint GLAD_GL_SGIX_igloo_interface;\nint GLAD_GL_SGIS_texture_lod;\nint GLAD_GL_NV_vertex_program3;\nint GLAD_GL_ARB_draw_indirect;\nint GLAD_GL_NV_vertex_program4;\nint GLAD_GL_AMD_transform_feedback3_lines_triangles;\nint GLAD_GL_SGIS_fog_function;\nint GLAD_GL_EXT_x11_sync_object;\nint GLAD_GL_ARB_sync;\nint GLAD_GL_ARB_compute_variable_group_size;\nint GLAD_GL_OES_fixed_point;\nint GLAD_GL_EXT_framebuffer_multisample;\nint GLAD_GL_ARB_gpu_shader5;\nint GLAD_GL_SGIS_texture4D;\nint GLAD_GL_EXT_texture3D;\nint GLAD_GL_EXT_multisample;\nint GLAD_GL_EXT_secondary_color;\nint GLAD_GL_NV_parameter_buffer_object2;\nint GLAD_GL_ATI_vertex_array_object;\nint GLAD_GL_NVX_gpu_memory_info;\nint GLAD_GL_ARB_sparse_texture;\nint GLAD_GL_SGIS_point_line_texgen;\nint GLAD_GL_EXT_draw_range_elements;\nint GLAD_GL_SGIX_blend_alpha_minmax;\nPFNGLPOINTPARAMETERINVPROC glad_glPointParameteriNV;\nPFNGLPOINTPARAMETERIVNVPROC glad_glPointParameterivNV;\nPFNGLELEMENTPOINTERAPPLEPROC glad_glElementPointerAPPLE;\nPFNGLDRAWELEMENTARRAYAPPLEPROC glad_glDrawElementArrayAPPLE;\nPFNGLDRAWRANGEELEMENTARRAYAPPLEPROC glad_glDrawRangeElementArrayAPPLE;\nPFNGLMULTIDRAWELEMENTARRAYAPPLEPROC glad_glMultiDrawElementArrayAPPLE;\nPFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC glad_glMultiDrawRangeElementArrayAPPLE;\nPFNGLMULTIDRAWARRAYSINDIRECTAMDPROC glad_glMultiDrawArraysIndirectAMD;\nPFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC glad_glMultiDrawElementsIndirectAMD;\nPFNGLTAGSAMPLEBUFFERSGIXPROC glad_glTagSampleBufferSGIX;\nPFNGLSTENCILOPSEPARATEATIPROC glad_glStencilOpSeparateATI;\nPFNGLSTENCILFUNCSEPARATEATIPROC glad_glStencilFuncSeparateATI;\nPFNGLTEXBUFFEREXTPROC glad_glTexBufferEXT;\nPFNGLWEIGHTBVARBPROC glad_glWeightbvARB;\nPFNGLWEIGHTSVARBPROC glad_glWeightsvARB;\nPFNGLWEIGHTIVARBPROC glad_glWeightivARB;\nPFNGLWEIGHTFVARBPROC glad_glWeightfvARB;\nPFNGLWEIGHTDVARBPROC glad_glWeightdvARB;\nPFNGLWEIGHTUBVARBPROC glad_glWeightubvARB;\nPFNGLWEIGHTUSVARBPROC glad_glWeightusvARB;\nPFNGLWEIGHTUIVARBPROC glad_glWeightuivARB;\nPFNGLWEIGHTPOINTERARBPROC glad_glWeightPointerARB;\nPFNGLVERTEXBLENDARBPROC glad_glVertexBlendARB;\nPFNGLINDEXFUNCEXTPROC glad_glIndexFuncEXT;\nPFNGLMAKEBUFFERRESIDENTNVPROC glad_glMakeBufferResidentNV;\nPFNGLMAKEBUFFERNONRESIDENTNVPROC glad_glMakeBufferNonResidentNV;\nPFNGLISBUFFERRESIDENTNVPROC glad_glIsBufferResidentNV;\nPFNGLMAKENAMEDBUFFERRESIDENTNVPROC glad_glMakeNamedBufferResidentNV;\nPFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC glad_glMakeNamedBufferNonResidentNV;\nPFNGLISNAMEDBUFFERRESIDENTNVPROC glad_glIsNamedBufferResidentNV;\nPFNGLGETBUFFERPARAMETERUI64VNVPROC glad_glGetBufferParameterui64vNV;\nPFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC glad_glGetNamedBufferParameterui64vNV;\nPFNGLGETINTEGERUI64VNVPROC glad_glGetIntegerui64vNV;\nPFNGLUNIFORMUI64NVPROC glad_glUniformui64NV;\nPFNGLUNIFORMUI64VNVPROC glad_glUniformui64vNV;\nPFNGLGETUNIFORMUI64VNVPROC glad_glGetUniformui64vNV;\nPFNGLPROGRAMUNIFORMUI64NVPROC glad_glProgramUniformui64NV;\nPFNGLPROGRAMUNIFORMUI64VNVPROC glad_glProgramUniformui64vNV;\nPFNGLCOLORSUBTABLEEXTPROC glad_glColorSubTableEXT;\nPFNGLCOPYCOLORSUBTABLEEXTPROC glad_glCopyColorSubTableEXT;\nPFNGLFINISHTEXTURESUNXPROC glad_glFinishTextureSUNX;\nPFNGLMULTIDRAWARRAYSEXTPROC glad_glMultiDrawArraysEXT;\nPFNGLMULTIDRAWELEMENTSEXTPROC glad_glMultiDrawElementsEXT;\nPFNGLBEGINCONDITIONALRENDERNVPROC glad_glBeginConditionalRenderNV;\nPFNGLENDCONDITIONALRENDERNVPROC glad_glEndConditionalRenderNV;\nPFNGLRESIZEBUFFERSMESAPROC glad_glResizeBuffersMESA;\nPFNGLCONVOLUTIONFILTER1DEXTPROC glad_glConvolutionFilter1DEXT;\nPFNGLCONVOLUTIONFILTER2DEXTPROC glad_glConvolutionFilter2DEXT;\nPFNGLCONVOLUTIONPARAMETERFEXTPROC glad_glConvolutionParameterfEXT;\nPFNGLCONVOLUTIONPARAMETERFVEXTPROC glad_glConvolutionParameterfvEXT;\nPFNGLCONVOLUTIONPARAMETERIEXTPROC glad_glConvolutionParameteriEXT;\nPFNGLCONVOLUTIONPARAMETERIVEXTPROC glad_glConvolutionParameterivEXT;\nPFNGLCOPYCONVOLUTIONFILTER1DEXTPROC glad_glCopyConvolutionFilter1DEXT;\nPFNGLCOPYCONVOLUTIONFILTER2DEXTPROC glad_glCopyConvolutionFilter2DEXT;\nPFNGLGETCONVOLUTIONFILTEREXTPROC glad_glGetConvolutionFilterEXT;\nPFNGLGETCONVOLUTIONPARAMETERFVEXTPROC glad_glGetConvolutionParameterfvEXT;\nPFNGLGETCONVOLUTIONPARAMETERIVEXTPROC glad_glGetConvolutionParameterivEXT;\nPFNGLGETSEPARABLEFILTEREXTPROC glad_glGetSeparableFilterEXT;\nPFNGLSEPARABLEFILTER2DEXTPROC glad_glSeparableFilter2DEXT;\nPFNGLVERTEXATTRIBL1I64NVPROC glad_glVertexAttribL1i64NV;\nPFNGLVERTEXATTRIBL2I64NVPROC glad_glVertexAttribL2i64NV;\nPFNGLVERTEXATTRIBL3I64NVPROC glad_glVertexAttribL3i64NV;\nPFNGLVERTEXATTRIBL4I64NVPROC glad_glVertexAttribL4i64NV;\nPFNGLVERTEXATTRIBL1I64VNVPROC glad_glVertexAttribL1i64vNV;\nPFNGLVERTEXATTRIBL2I64VNVPROC glad_glVertexAttribL2i64vNV;\nPFNGLVERTEXATTRIBL3I64VNVPROC glad_glVertexAttribL3i64vNV;\nPFNGLVERTEXATTRIBL4I64VNVPROC glad_glVertexAttribL4i64vNV;\nPFNGLVERTEXATTRIBL1UI64NVPROC glad_glVertexAttribL1ui64NV;\nPFNGLVERTEXATTRIBL2UI64NVPROC glad_glVertexAttribL2ui64NV;\nPFNGLVERTEXATTRIBL3UI64NVPROC glad_glVertexAttribL3ui64NV;\nPFNGLVERTEXATTRIBL4UI64NVPROC glad_glVertexAttribL4ui64NV;\nPFNGLVERTEXATTRIBL1UI64VNVPROC glad_glVertexAttribL1ui64vNV;\nPFNGLVERTEXATTRIBL2UI64VNVPROC glad_glVertexAttribL2ui64vNV;\nPFNGLVERTEXATTRIBL3UI64VNVPROC glad_glVertexAttribL3ui64vNV;\nPFNGLVERTEXATTRIBL4UI64VNVPROC glad_glVertexAttribL4ui64vNV;\nPFNGLGETVERTEXATTRIBLI64VNVPROC glad_glGetVertexAttribLi64vNV;\nPFNGLGETVERTEXATTRIBLUI64VNVPROC glad_glGetVertexAttribLui64vNV;\nPFNGLVERTEXATTRIBLFORMATNVPROC glad_glVertexAttribLFormatNV;\nPFNGLCOLORTABLEEXTPROC glad_glColorTableEXT;\nPFNGLGETCOLORTABLEEXTPROC glad_glGetColorTableEXT;\nPFNGLGETCOLORTABLEPARAMETERIVEXTPROC glad_glGetColorTableParameterivEXT;\nPFNGLGETCOLORTABLEPARAMETERFVEXTPROC glad_glGetColorTableParameterfvEXT;\nPFNGLTEXBUFFERARBPROC glad_glTexBufferARB;\nPFNGLPNTRIANGLESIATIPROC glad_glPNTrianglesiATI;\nPFNGLPNTRIANGLESFATIPROC glad_glPNTrianglesfATI;\nPFNGLFLUSHRASTERSGIXPROC glad_glFlushRasterSGIX;\nPFNGLAPPLYTEXTUREEXTPROC glad_glApplyTextureEXT;\nPFNGLTEXTURELIGHTEXTPROC glad_glTextureLightEXT;\nPFNGLTEXTUREMATERIALEXTPROC glad_glTextureMaterialEXT;\nPFNGLBLENDFUNCINDEXEDAMDPROC glad_glBlendFuncIndexedAMD;\nPFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC glad_glBlendFuncSeparateIndexedAMD;\nPFNGLBLENDEQUATIONINDEXEDAMDPROC glad_glBlendEquationIndexedAMD;\nPFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC glad_glBlendEquationSeparateIndexedAMD;\nPFNGLWINDOWPOS2DMESAPROC glad_glWindowPos2dMESA;\nPFNGLWINDOWPOS2DVMESAPROC glad_glWindowPos2dvMESA;\nPFNGLWINDOWPOS2FMESAPROC glad_glWindowPos2fMESA;\nPFNGLWINDOWPOS2FVMESAPROC glad_glWindowPos2fvMESA;\nPFNGLWINDOWPOS2IMESAPROC glad_glWindowPos2iMESA;\nPFNGLWINDOWPOS2IVMESAPROC glad_glWindowPos2ivMESA;\nPFNGLWINDOWPOS2SMESAPROC glad_glWindowPos2sMESA;\nPFNGLWINDOWPOS2SVMESAPROC glad_glWindowPos2svMESA;\nPFNGLWINDOWPOS3DMESAPROC glad_glWindowPos3dMESA;\nPFNGLWINDOWPOS3DVMESAPROC glad_glWindowPos3dvMESA;\nPFNGLWINDOWPOS3FMESAPROC glad_glWindowPos3fMESA;\nPFNGLWINDOWPOS3FVMESAPROC glad_glWindowPos3fvMESA;\nPFNGLWINDOWPOS3IMESAPROC glad_glWindowPos3iMESA;\nPFNGLWINDOWPOS3IVMESAPROC glad_glWindowPos3ivMESA;\nPFNGLWINDOWPOS3SMESAPROC glad_glWindowPos3sMESA;\nPFNGLWINDOWPOS3SVMESAPROC glad_glWindowPos3svMESA;\nPFNGLWINDOWPOS4DMESAPROC glad_glWindowPos4dMESA;\nPFNGLWINDOWPOS4DVMESAPROC glad_glWindowPos4dvMESA;\nPFNGLWINDOWPOS4FMESAPROC glad_glWindowPos4fMESA;\nPFNGLWINDOWPOS4FVMESAPROC glad_glWindowPos4fvMESA;\nPFNGLWINDOWPOS4IMESAPROC glad_glWindowPos4iMESA;\nPFNGLWINDOWPOS4IVMESAPROC glad_glWindowPos4ivMESA;\nPFNGLWINDOWPOS4SMESAPROC glad_glWindowPos4sMESA;\nPFNGLWINDOWPOS4SVMESAPROC glad_glWindowPos4svMESA;\nPFNGLTEXTUREBARRIERNVPROC glad_glTextureBarrierNV;\nPFNGLTBUFFERMASK3DFXPROC glad_glTbufferMask3DFX;\nPFNGLFRAMETERMINATORGREMEDYPROC glad_glFrameTerminatorGREMEDY;\nPFNGLUSESHADERPROGRAMEXTPROC glad_glUseShaderProgramEXT;\nPFNGLACTIVEPROGRAMEXTPROC glad_glActiveProgramEXT;\nPFNGLCREATESHADERPROGRAMEXTPROC glad_glCreateShaderProgramEXT;\nPFNGLACTIVESHADERPROGRAMEXTPROC glad_glActiveShaderProgramEXT;\nPFNGLBINDPROGRAMPIPELINEEXTPROC glad_glBindProgramPipelineEXT;\nPFNGLCREATESHADERPROGRAMVEXTPROC glad_glCreateShaderProgramvEXT;\nPFNGLDELETEPROGRAMPIPELINESEXTPROC glad_glDeleteProgramPipelinesEXT;\nPFNGLGENPROGRAMPIPELINESEXTPROC glad_glGenProgramPipelinesEXT;\nPFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC glad_glGetProgramPipelineInfoLogEXT;\nPFNGLGETPROGRAMPIPELINEIVEXTPROC glad_glGetProgramPipelineivEXT;\nPFNGLISPROGRAMPIPELINEEXTPROC glad_glIsProgramPipelineEXT;\nPFNGLPROGRAMPARAMETERIEXTPROC glad_glProgramParameteriEXT;\nPFNGLPROGRAMUNIFORM1FEXTPROC glad_glProgramUniform1fEXT;\nPFNGLPROGRAMUNIFORM1FVEXTPROC glad_glProgramUniform1fvEXT;\nPFNGLPROGRAMUNIFORM1IEXTPROC glad_glProgramUniform1iEXT;\nPFNGLPROGRAMUNIFORM1IVEXTPROC glad_glProgramUniform1ivEXT;\nPFNGLPROGRAMUNIFORM2FEXTPROC glad_glProgramUniform2fEXT;\nPFNGLPROGRAMUNIFORM2FVEXTPROC glad_glProgramUniform2fvEXT;\nPFNGLPROGRAMUNIFORM2IEXTPROC glad_glProgramUniform2iEXT;\nPFNGLPROGRAMUNIFORM2IVEXTPROC glad_glProgramUniform2ivEXT;\nPFNGLPROGRAMUNIFORM3FEXTPROC glad_glProgramUniform3fEXT;\nPFNGLPROGRAMUNIFORM3FVEXTPROC glad_glProgramUniform3fvEXT;\nPFNGLPROGRAMUNIFORM3IEXTPROC glad_glProgramUniform3iEXT;\nPFNGLPROGRAMUNIFORM3IVEXTPROC glad_glProgramUniform3ivEXT;\nPFNGLPROGRAMUNIFORM4FEXTPROC glad_glProgramUniform4fEXT;\nPFNGLPROGRAMUNIFORM4FVEXTPROC glad_glProgramUniform4fvEXT;\nPFNGLPROGRAMUNIFORM4IEXTPROC glad_glProgramUniform4iEXT;\nPFNGLPROGRAMUNIFORM4IVEXTPROC glad_glProgramUniform4ivEXT;\nPFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC glad_glProgramUniformMatrix2fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC glad_glProgramUniformMatrix3fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC glad_glProgramUniformMatrix4fvEXT;\nPFNGLUSEPROGRAMSTAGESEXTPROC glad_glUseProgramStagesEXT;\nPFNGLVALIDATEPROGRAMPIPELINEEXTPROC glad_glValidateProgramPipelineEXT;\nPFNGLPROGRAMUNIFORM1UIEXTPROC glad_glProgramUniform1uiEXT;\nPFNGLPROGRAMUNIFORM2UIEXTPROC glad_glProgramUniform2uiEXT;\nPFNGLPROGRAMUNIFORM3UIEXTPROC glad_glProgramUniform3uiEXT;\nPFNGLPROGRAMUNIFORM4UIEXTPROC glad_glProgramUniform4uiEXT;\nPFNGLPROGRAMUNIFORM1UIVEXTPROC glad_glProgramUniform1uivEXT;\nPFNGLPROGRAMUNIFORM2UIVEXTPROC glad_glProgramUniform2uivEXT;\nPFNGLPROGRAMUNIFORM3UIVEXTPROC glad_glProgramUniform3uivEXT;\nPFNGLPROGRAMUNIFORM4UIVEXTPROC glad_glProgramUniform4uivEXT;\nPFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC glad_glProgramUniformMatrix4fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC glad_glProgramUniformMatrix2x3fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC glad_glProgramUniformMatrix3x2fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC glad_glProgramUniformMatrix2x4fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC glad_glProgramUniformMatrix4x2fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC glad_glProgramUniformMatrix3x4fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC glad_glProgramUniformMatrix4x3fvEXT;\nPFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC glad_glTexImage2DMultisampleCoverageNV;\nPFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC glad_glTexImage3DMultisampleCoverageNV;\nPFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC glad_glTextureImage2DMultisampleNV;\nPFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC glad_glTextureImage3DMultisampleNV;\nPFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC glad_glTextureImage2DMultisampleCoverageNV;\nPFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC glad_glTextureImage3DMultisampleCoverageNV;\nPFNGLDELETEOBJECTARBPROC glad_glDeleteObjectARB;\nPFNGLGETHANDLEARBPROC glad_glGetHandleARB;\nPFNGLDETACHOBJECTARBPROC glad_glDetachObjectARB;\nPFNGLCREATESHADEROBJECTARBPROC glad_glCreateShaderObjectARB;\nPFNGLSHADERSOURCEARBPROC glad_glShaderSourceARB;\nPFNGLCOMPILESHADERARBPROC glad_glCompileShaderARB;\nPFNGLCREATEPROGRAMOBJECTARBPROC glad_glCreateProgramObjectARB;\nPFNGLATTACHOBJECTARBPROC glad_glAttachObjectARB;\nPFNGLLINKPROGRAMARBPROC glad_glLinkProgramARB;\nPFNGLUSEPROGRAMOBJECTARBPROC glad_glUseProgramObjectARB;\nPFNGLVALIDATEPROGRAMARBPROC glad_glValidateProgramARB;\nPFNGLUNIFORM1FARBPROC glad_glUniform1fARB;\nPFNGLUNIFORM2FARBPROC glad_glUniform2fARB;\nPFNGLUNIFORM3FARBPROC glad_glUniform3fARB;\nPFNGLUNIFORM4FARBPROC glad_glUniform4fARB;\nPFNGLUNIFORM1IARBPROC glad_glUniform1iARB;\nPFNGLUNIFORM2IARBPROC glad_glUniform2iARB;\nPFNGLUNIFORM3IARBPROC glad_glUniform3iARB;\nPFNGLUNIFORM4IARBPROC glad_glUniform4iARB;\nPFNGLUNIFORM1FVARBPROC glad_glUniform1fvARB;\nPFNGLUNIFORM2FVARBPROC glad_glUniform2fvARB;\nPFNGLUNIFORM3FVARBPROC glad_glUniform3fvARB;\nPFNGLUNIFORM4FVARBPROC glad_glUniform4fvARB;\nPFNGLUNIFORM1IVARBPROC glad_glUniform1ivARB;\nPFNGLUNIFORM2IVARBPROC glad_glUniform2ivARB;\nPFNGLUNIFORM3IVARBPROC glad_glUniform3ivARB;\nPFNGLUNIFORM4IVARBPROC glad_glUniform4ivARB;\nPFNGLUNIFORMMATRIX2FVARBPROC glad_glUniformMatrix2fvARB;\nPFNGLUNIFORMMATRIX3FVARBPROC glad_glUniformMatrix3fvARB;\nPFNGLUNIFORMMATRIX4FVARBPROC glad_glUniformMatrix4fvARB;\nPFNGLGETOBJECTPARAMETERFVARBPROC glad_glGetObjectParameterfvARB;\nPFNGLGETOBJECTPARAMETERIVARBPROC glad_glGetObjectParameterivARB;\nPFNGLGETINFOLOGARBPROC glad_glGetInfoLogARB;\nPFNGLGETATTACHEDOBJECTSARBPROC glad_glGetAttachedObjectsARB;\nPFNGLGETUNIFORMLOCATIONARBPROC glad_glGetUniformLocationARB;\nPFNGLGETACTIVEUNIFORMARBPROC glad_glGetActiveUniformARB;\nPFNGLGETUNIFORMFVARBPROC glad_glGetUniformfvARB;\nPFNGLGETUNIFORMIVARBPROC glad_glGetUniformivARB;\nPFNGLGETSHADERSOURCEARBPROC glad_glGetShaderSourceARB;\nPFNGLTEXBUMPPARAMETERIVATIPROC glad_glTexBumpParameterivATI;\nPFNGLTEXBUMPPARAMETERFVATIPROC glad_glTexBumpParameterfvATI;\nPFNGLGETTEXBUMPPARAMETERIVATIPROC glad_glGetTexBumpParameterivATI;\nPFNGLGETTEXBUMPPARAMETERFVATIPROC glad_glGetTexBumpParameterfvATI;\nPFNGLMAPOBJECTBUFFERATIPROC glad_glMapObjectBufferATI;\nPFNGLUNMAPOBJECTBUFFERATIPROC glad_glUnmapObjectBufferATI;\nPFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB;\nPFNGLGETNTEXIMAGEARBPROC glad_glGetnTexImageARB;\nPFNGLREADNPIXELSARBPROC glad_glReadnPixelsARB;\nPFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_glGetnCompressedTexImageARB;\nPFNGLGETNUNIFORMFVARBPROC glad_glGetnUniformfvARB;\nPFNGLGETNUNIFORMIVARBPROC glad_glGetnUniformivARB;\nPFNGLGETNUNIFORMUIVARBPROC glad_glGetnUniformuivARB;\nPFNGLGETNUNIFORMDVARBPROC glad_glGetnUniformdvARB;\nPFNGLGETNMAPDVARBPROC glad_glGetnMapdvARB;\nPFNGLGETNMAPFVARBPROC glad_glGetnMapfvARB;\nPFNGLGETNMAPIVARBPROC glad_glGetnMapivARB;\nPFNGLGETNPIXELMAPFVARBPROC glad_glGetnPixelMapfvARB;\nPFNGLGETNPIXELMAPUIVARBPROC glad_glGetnPixelMapuivARB;\nPFNGLGETNPIXELMAPUSVARBPROC glad_glGetnPixelMapusvARB;\nPFNGLGETNPOLYGONSTIPPLEARBPROC glad_glGetnPolygonStippleARB;\nPFNGLGETNCOLORTABLEARBPROC glad_glGetnColorTableARB;\nPFNGLGETNCONVOLUTIONFILTERARBPROC glad_glGetnConvolutionFilterARB;\nPFNGLGETNSEPARABLEFILTERARBPROC glad_glGetnSeparableFilterARB;\nPFNGLGETNHISTOGRAMARBPROC glad_glGetnHistogramARB;\nPFNGLGETNMINMAXARBPROC glad_glGetnMinmaxARB;\nPFNGLPIXELDATARANGENVPROC glad_glPixelDataRangeNV;\nPFNGLFLUSHPIXELDATARANGENVPROC glad_glFlushPixelDataRangeNV;\nPFNGLBLITFRAMEBUFFEREXTPROC glad_glBlitFramebufferEXT;\nPFNGLVERTEXWEIGHTFEXTPROC glad_glVertexWeightfEXT;\nPFNGLVERTEXWEIGHTFVEXTPROC glad_glVertexWeightfvEXT;\nPFNGLVERTEXWEIGHTPOINTEREXTPROC glad_glVertexWeightPointerEXT;\nPFNGLSTRINGMARKERGREMEDYPROC glad_glStringMarkerGREMEDY;\nPFNGLTEXSUBIMAGE1DEXTPROC glad_glTexSubImage1DEXT;\nPFNGLTEXSUBIMAGE2DEXTPROC glad_glTexSubImage2DEXT;\nPFNGLMAPCONTROLPOINTSNVPROC glad_glMapControlPointsNV;\nPFNGLMAPPARAMETERIVNVPROC glad_glMapParameterivNV;\nPFNGLMAPPARAMETERFVNVPROC glad_glMapParameterfvNV;\nPFNGLGETMAPCONTROLPOINTSNVPROC glad_glGetMapControlPointsNV;\nPFNGLGETMAPPARAMETERIVNVPROC glad_glGetMapParameterivNV;\nPFNGLGETMAPPARAMETERFVNVPROC glad_glGetMapParameterfvNV;\nPFNGLGETMAPATTRIBPARAMETERIVNVPROC glad_glGetMapAttribParameterivNV;\nPFNGLGETMAPATTRIBPARAMETERFVNVPROC glad_glGetMapAttribParameterfvNV;\nPFNGLEVALMAPSNVPROC glad_glEvalMapsNV;\nPFNGLGETTEXFILTERFUNCSGISPROC glad_glGetTexFilterFuncSGIS;\nPFNGLTEXFILTERFUNCSGISPROC glad_glTexFilterFuncSGIS;\nPFNGLGETPERFMONITORGROUPSAMDPROC glad_glGetPerfMonitorGroupsAMD;\nPFNGLGETPERFMONITORCOUNTERSAMDPROC glad_glGetPerfMonitorCountersAMD;\nPFNGLGETPERFMONITORGROUPSTRINGAMDPROC glad_glGetPerfMonitorGroupStringAMD;\nPFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC glad_glGetPerfMonitorCounterStringAMD;\nPFNGLGETPERFMONITORCOUNTERINFOAMDPROC glad_glGetPerfMonitorCounterInfoAMD;\nPFNGLGENPERFMONITORSAMDPROC glad_glGenPerfMonitorsAMD;\nPFNGLDELETEPERFMONITORSAMDPROC glad_glDeletePerfMonitorsAMD;\nPFNGLSELECTPERFMONITORCOUNTERSAMDPROC glad_glSelectPerfMonitorCountersAMD;\nPFNGLBEGINPERFMONITORAMDPROC glad_glBeginPerfMonitorAMD;\nPFNGLENDPERFMONITORAMDPROC glad_glEndPerfMonitorAMD;\nPFNGLGETPERFMONITORCOUNTERDATAAMDPROC glad_glGetPerfMonitorCounterDataAMD;\nPFNGLSTENCILCLEARTAGEXTPROC glad_glStencilClearTagEXT;\nPFNGLPRESENTFRAMEKEYEDNVPROC glad_glPresentFrameKeyedNV;\nPFNGLPRESENTFRAMEDUALFILLNVPROC glad_glPresentFrameDualFillNV;\nPFNGLGETVIDEOIVNVPROC glad_glGetVideoivNV;\nPFNGLGETVIDEOUIVNVPROC glad_glGetVideouivNV;\nPFNGLGETVIDEOI64VNVPROC glad_glGetVideoi64vNV;\nPFNGLGETVIDEOUI64VNVPROC glad_glGetVideoui64vNV;\nPFNGLPROGRAMENVPARAMETERS4FVEXTPROC glad_glProgramEnvParameters4fvEXT;\nPFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC glad_glProgramLocalParameters4fvEXT;\nPFNGLBEGINTRANSFORMFEEDBACKNVPROC glad_glBeginTransformFeedbackNV;\nPFNGLENDTRANSFORMFEEDBACKNVPROC glad_glEndTransformFeedbackNV;\nPFNGLTRANSFORMFEEDBACKATTRIBSNVPROC glad_glTransformFeedbackAttribsNV;\nPFNGLBINDBUFFERRANGENVPROC glad_glBindBufferRangeNV;\nPFNGLBINDBUFFEROFFSETNVPROC glad_glBindBufferOffsetNV;\nPFNGLBINDBUFFERBASENVPROC glad_glBindBufferBaseNV;\nPFNGLTRANSFORMFEEDBACKVARYINGSNVPROC glad_glTransformFeedbackVaryingsNV;\nPFNGLACTIVEVARYINGNVPROC glad_glActiveVaryingNV;\nPFNGLGETVARYINGLOCATIONNVPROC glad_glGetVaryingLocationNV;\nPFNGLGETACTIVEVARYINGNVPROC glad_glGetActiveVaryingNV;\nPFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC glad_glGetTransformFeedbackVaryingNV;\nPFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC glad_glTransformFeedbackStreamAttribsNV;\nPFNGLPROGRAMNAMEDPARAMETER4FNVPROC glad_glProgramNamedParameter4fNV;\nPFNGLPROGRAMNAMEDPARAMETER4FVNVPROC glad_glProgramNamedParameter4fvNV;\nPFNGLPROGRAMNAMEDPARAMETER4DNVPROC glad_glProgramNamedParameter4dNV;\nPFNGLPROGRAMNAMEDPARAMETER4DVNVPROC glad_glProgramNamedParameter4dvNV;\nPFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC glad_glGetProgramNamedParameterfvNV;\nPFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC glad_glGetProgramNamedParameterdvNV;\nPFNGLSTENCILOPVALUEAMDPROC glad_glStencilOpValueAMD;\nPFNGLVERTEXATTRIBDIVISORARBPROC glad_glVertexAttribDivisorARB;\nPFNGLPOLYGONOFFSETEXTPROC glad_glPolygonOffsetEXT;\nPFNGLTEXSTORAGESPARSEAMDPROC glad_glTexStorageSparseAMD;\nPFNGLTEXTURESTORAGESPARSEAMDPROC glad_glTextureStorageSparseAMD;\nPFNGLDELETEFENCESNVPROC glad_glDeleteFencesNV;\nPFNGLGENFENCESNVPROC glad_glGenFencesNV;\nPFNGLISFENCENVPROC glad_glIsFenceNV;\nPFNGLTESTFENCENVPROC glad_glTestFenceNV;\nPFNGLGETFENCEIVNVPROC glad_glGetFenceivNV;\nPFNGLFINISHFENCENVPROC glad_glFinishFenceNV;\nPFNGLSETFENCENVPROC glad_glSetFenceNV;\nPFNGLDRAWMESHARRAYSSUNPROC glad_glDrawMeshArraysSUN;\nPFNGLCREATESYNCFROMCLEVENTARBPROC glad_glCreateSyncFromCLeventARB;\nPFNGLCLEARDEPTHFOESPROC glad_glClearDepthfOES;\nPFNGLCLIPPLANEFOESPROC glad_glClipPlanefOES;\nPFNGLDEPTHRANGEFOESPROC glad_glDepthRangefOES;\nPFNGLFRUSTUMFOESPROC glad_glFrustumfOES;\nPFNGLGETCLIPPLANEFOESPROC glad_glGetClipPlanefOES;\nPFNGLORTHOFOESPROC glad_glOrthofOES;\nPFNGLPRIMITIVERESTARTNVPROC glad_glPrimitiveRestartNV;\nPFNGLPRIMITIVERESTARTINDEXNVPROC glad_glPrimitiveRestartIndexNV;\nPFNGLGLOBALALPHAFACTORBSUNPROC glad_glGlobalAlphaFactorbSUN;\nPFNGLGLOBALALPHAFACTORSSUNPROC glad_glGlobalAlphaFactorsSUN;\nPFNGLGLOBALALPHAFACTORISUNPROC glad_glGlobalAlphaFactoriSUN;\nPFNGLGLOBALALPHAFACTORFSUNPROC glad_glGlobalAlphaFactorfSUN;\nPFNGLGLOBALALPHAFACTORDSUNPROC glad_glGlobalAlphaFactordSUN;\nPFNGLGLOBALALPHAFACTORUBSUNPROC glad_glGlobalAlphaFactorubSUN;\nPFNGLGLOBALALPHAFACTORUSSUNPROC glad_glGlobalAlphaFactorusSUN;\nPFNGLGLOBALALPHAFACTORUISUNPROC glad_glGlobalAlphaFactoruiSUN;\nPFNGLARETEXTURESRESIDENTEXTPROC glad_glAreTexturesResidentEXT;\nPFNGLBINDTEXTUREEXTPROC glad_glBindTextureEXT;\nPFNGLDELETETEXTURESEXTPROC glad_glDeleteTexturesEXT;\nPFNGLGENTEXTURESEXTPROC glad_glGenTexturesEXT;\nPFNGLISTEXTUREEXTPROC glad_glIsTextureEXT;\nPFNGLPRIORITIZETEXTURESEXTPROC glad_glPrioritizeTexturesEXT;\nPFNGLGENNAMESAMDPROC glad_glGenNamesAMD;\nPFNGLDELETENAMESAMDPROC glad_glDeleteNamesAMD;\nPFNGLISNAMEAMDPROC glad_glIsNameAMD;\nPFNGLENABLEVERTEXATTRIBAPPLEPROC glad_glEnableVertexAttribAPPLE;\nPFNGLDISABLEVERTEXATTRIBAPPLEPROC glad_glDisableVertexAttribAPPLE;\nPFNGLISVERTEXATTRIBENABLEDAPPLEPROC glad_glIsVertexAttribEnabledAPPLE;\nPFNGLMAPVERTEXATTRIB1DAPPLEPROC glad_glMapVertexAttrib1dAPPLE;\nPFNGLMAPVERTEXATTRIB1FAPPLEPROC glad_glMapVertexAttrib1fAPPLE;\nPFNGLMAPVERTEXATTRIB2DAPPLEPROC glad_glMapVertexAttrib2dAPPLE;\nPFNGLMAPVERTEXATTRIB2FAPPLEPROC glad_glMapVertexAttrib2fAPPLE;\nPFNGLGETLISTPARAMETERFVSGIXPROC glad_glGetListParameterfvSGIX;\nPFNGLGETLISTPARAMETERIVSGIXPROC glad_glGetListParameterivSGIX;\nPFNGLLISTPARAMETERFSGIXPROC glad_glListParameterfSGIX;\nPFNGLLISTPARAMETERFVSGIXPROC glad_glListParameterfvSGIX;\nPFNGLLISTPARAMETERISGIXPROC glad_glListParameteriSGIX;\nPFNGLLISTPARAMETERIVSGIXPROC glad_glListParameterivSGIX;\nPFNGLBUFFERADDRESSRANGENVPROC glad_glBufferAddressRangeNV;\nPFNGLVERTEXFORMATNVPROC glad_glVertexFormatNV;\nPFNGLNORMALFORMATNVPROC glad_glNormalFormatNV;\nPFNGLCOLORFORMATNVPROC glad_glColorFormatNV;\nPFNGLINDEXFORMATNVPROC glad_glIndexFormatNV;\nPFNGLTEXCOORDFORMATNVPROC glad_glTexCoordFormatNV;\nPFNGLEDGEFLAGFORMATNVPROC glad_glEdgeFlagFormatNV;\nPFNGLSECONDARYCOLORFORMATNVPROC glad_glSecondaryColorFormatNV;\nPFNGLFOGCOORDFORMATNVPROC glad_glFogCoordFormatNV;\nPFNGLVERTEXATTRIBFORMATNVPROC glad_glVertexAttribFormatNV;\nPFNGLVERTEXATTRIBIFORMATNVPROC glad_glVertexAttribIFormatNV;\nPFNGLGETINTEGERUI64I_VNVPROC glad_glGetIntegerui64i_vNV;\nPFNGLBLENDPARAMETERINVPROC glad_glBlendParameteriNV;\nPFNGLBLENDBARRIERNVPROC glad_glBlendBarrierNV;\nPFNGLSHARPENTEXFUNCSGISPROC glad_glSharpenTexFuncSGIS;\nPFNGLGETSHARPENTEXFUNCSGISPROC glad_glGetSharpenTexFuncSGIS;\nPFNGLVERTEXATTRIB1DARBPROC glad_glVertexAttrib1dARB;\nPFNGLVERTEXATTRIB1DVARBPROC glad_glVertexAttrib1dvARB;\nPFNGLVERTEXATTRIB1FARBPROC glad_glVertexAttrib1fARB;\nPFNGLVERTEXATTRIB1FVARBPROC glad_glVertexAttrib1fvARB;\nPFNGLVERTEXATTRIB1SARBPROC glad_glVertexAttrib1sARB;\nPFNGLVERTEXATTRIB1SVARBPROC glad_glVertexAttrib1svARB;\nPFNGLVERTEXATTRIB2DARBPROC glad_glVertexAttrib2dARB;\nPFNGLVERTEXATTRIB2DVARBPROC glad_glVertexAttrib2dvARB;\nPFNGLVERTEXATTRIB2FARBPROC glad_glVertexAttrib2fARB;\nPFNGLVERTEXATTRIB2FVARBPROC glad_glVertexAttrib2fvARB;\nPFNGLVERTEXATTRIB2SARBPROC glad_glVertexAttrib2sARB;\nPFNGLVERTEXATTRIB2SVARBPROC glad_glVertexAttrib2svARB;\nPFNGLVERTEXATTRIB3DARBPROC glad_glVertexAttrib3dARB;\nPFNGLVERTEXATTRIB3DVARBPROC glad_glVertexAttrib3dvARB;\nPFNGLVERTEXATTRIB3FARBPROC glad_glVertexAttrib3fARB;\nPFNGLVERTEXATTRIB3FVARBPROC glad_glVertexAttrib3fvARB;\nPFNGLVERTEXATTRIB3SARBPROC glad_glVertexAttrib3sARB;\nPFNGLVERTEXATTRIB3SVARBPROC glad_glVertexAttrib3svARB;\nPFNGLVERTEXATTRIB4NBVARBPROC glad_glVertexAttrib4NbvARB;\nPFNGLVERTEXATTRIB4NIVARBPROC glad_glVertexAttrib4NivARB;\nPFNGLVERTEXATTRIB4NSVARBPROC glad_glVertexAttrib4NsvARB;\nPFNGLVERTEXATTRIB4NUBARBPROC glad_glVertexAttrib4NubARB;\nPFNGLVERTEXATTRIB4NUBVARBPROC glad_glVertexAttrib4NubvARB;\nPFNGLVERTEXATTRIB4NUIVARBPROC glad_glVertexAttrib4NuivARB;\nPFNGLVERTEXATTRIB4NUSVARBPROC glad_glVertexAttrib4NusvARB;\nPFNGLVERTEXATTRIB4BVARBPROC glad_glVertexAttrib4bvARB;\nPFNGLVERTEXATTRIB4DARBPROC glad_glVertexAttrib4dARB;\nPFNGLVERTEXATTRIB4DVARBPROC glad_glVertexAttrib4dvARB;\nPFNGLVERTEXATTRIB4FARBPROC glad_glVertexAttrib4fARB;\nPFNGLVERTEXATTRIB4FVARBPROC glad_glVertexAttrib4fvARB;\nPFNGLVERTEXATTRIB4IVARBPROC glad_glVertexAttrib4ivARB;\nPFNGLVERTEXATTRIB4SARBPROC glad_glVertexAttrib4sARB;\nPFNGLVERTEXATTRIB4SVARBPROC glad_glVertexAttrib4svARB;\nPFNGLVERTEXATTRIB4UBVARBPROC glad_glVertexAttrib4ubvARB;\nPFNGLVERTEXATTRIB4UIVARBPROC glad_glVertexAttrib4uivARB;\nPFNGLVERTEXATTRIB4USVARBPROC glad_glVertexAttrib4usvARB;\nPFNGLVERTEXATTRIBPOINTERARBPROC glad_glVertexAttribPointerARB;\nPFNGLENABLEVERTEXATTRIBARRAYARBPROC glad_glEnableVertexAttribArrayARB;\nPFNGLDISABLEVERTEXATTRIBARRAYARBPROC glad_glDisableVertexAttribArrayARB;\nPFNGLPROGRAMSTRINGARBPROC glad_glProgramStringARB;\nPFNGLBINDPROGRAMARBPROC glad_glBindProgramARB;\nPFNGLDELETEPROGRAMSARBPROC glad_glDeleteProgramsARB;\nPFNGLGENPROGRAMSARBPROC glad_glGenProgramsARB;\nPFNGLPROGRAMENVPARAMETER4DARBPROC glad_glProgramEnvParameter4dARB;\nPFNGLPROGRAMENVPARAMETER4DVARBPROC glad_glProgramEnvParameter4dvARB;\nPFNGLPROGRAMENVPARAMETER4FARBPROC glad_glProgramEnvParameter4fARB;\nPFNGLPROGRAMENVPARAMETER4FVARBPROC glad_glProgramEnvParameter4fvARB;\nPFNGLPROGRAMLOCALPARAMETER4DARBPROC glad_glProgramLocalParameter4dARB;\nPFNGLPROGRAMLOCALPARAMETER4DVARBPROC glad_glProgramLocalParameter4dvARB;\nPFNGLPROGRAMLOCALPARAMETER4FARBPROC glad_glProgramLocalParameter4fARB;\nPFNGLPROGRAMLOCALPARAMETER4FVARBPROC glad_glProgramLocalParameter4fvARB;\nPFNGLGETPROGRAMENVPARAMETERDVARBPROC glad_glGetProgramEnvParameterdvARB;\nPFNGLGETPROGRAMENVPARAMETERFVARBPROC glad_glGetProgramEnvParameterfvARB;\nPFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glad_glGetProgramLocalParameterdvARB;\nPFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glad_glGetProgramLocalParameterfvARB;\nPFNGLGETPROGRAMIVARBPROC glad_glGetProgramivARB;\nPFNGLGETPROGRAMSTRINGARBPROC glad_glGetProgramStringARB;\nPFNGLGETVERTEXATTRIBDVARBPROC glad_glGetVertexAttribdvARB;\nPFNGLGETVERTEXATTRIBFVARBPROC glad_glGetVertexAttribfvARB;\nPFNGLGETVERTEXATTRIBIVARBPROC glad_glGetVertexAttribivARB;\nPFNGLGETVERTEXATTRIBPOINTERVARBPROC glad_glGetVertexAttribPointervARB;\nPFNGLISPROGRAMARBPROC glad_glIsProgramARB;\nPFNGLBINDBUFFERARBPROC glad_glBindBufferARB;\nPFNGLDELETEBUFFERSARBPROC glad_glDeleteBuffersARB;\nPFNGLGENBUFFERSARBPROC glad_glGenBuffersARB;\nPFNGLISBUFFERARBPROC glad_glIsBufferARB;\nPFNGLBUFFERDATAARBPROC glad_glBufferDataARB;\nPFNGLBUFFERSUBDATAARBPROC glad_glBufferSubDataARB;\nPFNGLGETBUFFERSUBDATAARBPROC glad_glGetBufferSubDataARB;\nPFNGLMAPBUFFERARBPROC glad_glMapBufferARB;\nPFNGLUNMAPBUFFERARBPROC glad_glUnmapBufferARB;\nPFNGLGETBUFFERPARAMETERIVARBPROC glad_glGetBufferParameterivARB;\nPFNGLGETBUFFERPOINTERVARBPROC glad_glGetBufferPointervARB;\nPFNGLFLUSHVERTEXARRAYRANGENVPROC glad_glFlushVertexArrayRangeNV;\nPFNGLVERTEXARRAYRANGENVPROC glad_glVertexArrayRangeNV;\nPFNGLFRAGMENTCOLORMATERIALSGIXPROC glad_glFragmentColorMaterialSGIX;\nPFNGLFRAGMENTLIGHTFSGIXPROC glad_glFragmentLightfSGIX;\nPFNGLFRAGMENTLIGHTFVSGIXPROC glad_glFragmentLightfvSGIX;\nPFNGLFRAGMENTLIGHTISGIXPROC glad_glFragmentLightiSGIX;\nPFNGLFRAGMENTLIGHTIVSGIXPROC glad_glFragmentLightivSGIX;\nPFNGLFRAGMENTLIGHTMODELFSGIXPROC glad_glFragmentLightModelfSGIX;\nPFNGLFRAGMENTLIGHTMODELFVSGIXPROC glad_glFragmentLightModelfvSGIX;\nPFNGLFRAGMENTLIGHTMODELISGIXPROC glad_glFragmentLightModeliSGIX;\nPFNGLFRAGMENTLIGHTMODELIVSGIXPROC glad_glFragmentLightModelivSGIX;\nPFNGLFRAGMENTMATERIALFSGIXPROC glad_glFragmentMaterialfSGIX;\nPFNGLFRAGMENTMATERIALFVSGIXPROC glad_glFragmentMaterialfvSGIX;\nPFNGLFRAGMENTMATERIALISGIXPROC glad_glFragmentMaterialiSGIX;\nPFNGLFRAGMENTMATERIALIVSGIXPROC glad_glFragmentMaterialivSGIX;\nPFNGLGETFRAGMENTLIGHTFVSGIXPROC glad_glGetFragmentLightfvSGIX;\nPFNGLGETFRAGMENTLIGHTIVSGIXPROC glad_glGetFragmentLightivSGIX;\nPFNGLGETFRAGMENTMATERIALFVSGIXPROC glad_glGetFragmentMaterialfvSGIX;\nPFNGLGETFRAGMENTMATERIALIVSGIXPROC glad_glGetFragmentMaterialivSGIX;\nPFNGLLIGHTENVISGIXPROC glad_glLightEnviSGIX;\nPFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC glad_glRenderbufferStorageMultisampleCoverageNV;\nPFNGLGETQUERYOBJECTI64VEXTPROC glad_glGetQueryObjecti64vEXT;\nPFNGLGETQUERYOBJECTUI64VEXTPROC glad_glGetQueryObjectui64vEXT;\nPFNGLGETTEXTUREHANDLENVPROC glad_glGetTextureHandleNV;\nPFNGLGETTEXTURESAMPLERHANDLENVPROC glad_glGetTextureSamplerHandleNV;\nPFNGLMAKETEXTUREHANDLERESIDENTNVPROC glad_glMakeTextureHandleResidentNV;\nPFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC glad_glMakeTextureHandleNonResidentNV;\nPFNGLGETIMAGEHANDLENVPROC glad_glGetImageHandleNV;\nPFNGLMAKEIMAGEHANDLERESIDENTNVPROC glad_glMakeImageHandleResidentNV;\nPFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC glad_glMakeImageHandleNonResidentNV;\nPFNGLUNIFORMHANDLEUI64NVPROC glad_glUniformHandleui64NV;\nPFNGLUNIFORMHANDLEUI64VNVPROC glad_glUniformHandleui64vNV;\nPFNGLPROGRAMUNIFORMHANDLEUI64NVPROC glad_glProgramUniformHandleui64NV;\nPFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC glad_glProgramUniformHandleui64vNV;\nPFNGLISTEXTUREHANDLERESIDENTNVPROC glad_glIsTextureHandleResidentNV;\nPFNGLISIMAGEHANDLERESIDENTNVPROC glad_glIsImageHandleResidentNV;\nPFNGLGETPOINTERVPROC glad_glGetPointerv;\nPFNGLDEBUGMESSAGECONTROLKHRPROC glad_glDebugMessageControlKHR;\nPFNGLDEBUGMESSAGEINSERTKHRPROC glad_glDebugMessageInsertKHR;\nPFNGLDEBUGMESSAGECALLBACKKHRPROC glad_glDebugMessageCallbackKHR;\nPFNGLGETDEBUGMESSAGELOGKHRPROC glad_glGetDebugMessageLogKHR;\nPFNGLPUSHDEBUGGROUPKHRPROC glad_glPushDebugGroupKHR;\nPFNGLPOPDEBUGGROUPKHRPROC glad_glPopDebugGroupKHR;\nPFNGLOBJECTLABELKHRPROC glad_glObjectLabelKHR;\nPFNGLGETOBJECTLABELKHRPROC glad_glGetObjectLabelKHR;\nPFNGLOBJECTPTRLABELKHRPROC glad_glObjectPtrLabelKHR;\nPFNGLGETOBJECTPTRLABELKHRPROC glad_glGetObjectPtrLabelKHR;\nPFNGLGETPOINTERVKHRPROC glad_glGetPointervKHR;\nPFNGLVERTEXATTRIBARRAYOBJECTATIPROC glad_glVertexAttribArrayObjectATI;\nPFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC glad_glGetVertexAttribArrayObjectfvATI;\nPFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC glad_glGetVertexAttribArrayObjectivATI;\nPFNGLPROGRAMPARAMETERIEXTPROC glad_glProgramParameteriEXT;\nPFNGLUNIFORMBUFFEREXTPROC glad_glUniformBufferEXT;\nPFNGLGETUNIFORMBUFFERSIZEEXTPROC glad_glGetUniformBufferSizeEXT;\nPFNGLGETUNIFORMOFFSETEXTPROC glad_glGetUniformOffsetEXT;\nPFNGLELEMENTPOINTERATIPROC glad_glElementPointerATI;\nPFNGLDRAWELEMENTARRAYATIPROC glad_glDrawElementArrayATI;\nPFNGLDRAWRANGEELEMENTARRAYATIPROC glad_glDrawRangeElementArrayATI;\nPFNGLREFERENCEPLANESGIXPROC glad_glReferencePlaneSGIX;\nPFNGLACTIVESTENCILFACEEXTPROC glad_glActiveStencilFaceEXT;\nPFNGLGETMULTISAMPLEFVNVPROC glad_glGetMultisamplefvNV;\nPFNGLSAMPLEMASKINDEXEDNVPROC glad_glSampleMaskIndexedNV;\nPFNGLTEXRENDERBUFFERNVPROC glad_glTexRenderbufferNV;\nPFNGLFLUSHSTATICDATAIBMPROC glad_glFlushStaticDataIBM;\nPFNGLTEXTURENORMALEXTPROC glad_glTextureNormalEXT;\nPFNGLPOINTPARAMETERFEXTPROC glad_glPointParameterfEXT;\nPFNGLPOINTPARAMETERFVEXTPROC glad_glPointParameterfvEXT;\nPFNGLHINTPGIPROC glad_glHintPGI;\nPFNGLVERTEXATTRIB1FARBPROC glad_glVertexAttrib1fARB;\nPFNGLVERTEXATTRIB1SARBPROC glad_glVertexAttrib1sARB;\nPFNGLVERTEXATTRIB1DARBPROC glad_glVertexAttrib1dARB;\nPFNGLVERTEXATTRIB2FARBPROC glad_glVertexAttrib2fARB;\nPFNGLVERTEXATTRIB2SARBPROC glad_glVertexAttrib2sARB;\nPFNGLVERTEXATTRIB2DARBPROC glad_glVertexAttrib2dARB;\nPFNGLVERTEXATTRIB3FARBPROC glad_glVertexAttrib3fARB;\nPFNGLVERTEXATTRIB3SARBPROC glad_glVertexAttrib3sARB;\nPFNGLVERTEXATTRIB3DARBPROC glad_glVertexAttrib3dARB;\nPFNGLVERTEXATTRIB4FARBPROC glad_glVertexAttrib4fARB;\nPFNGLVERTEXATTRIB4SARBPROC glad_glVertexAttrib4sARB;\nPFNGLVERTEXATTRIB4DARBPROC glad_glVertexAttrib4dARB;\nPFNGLVERTEXATTRIB4NUBARBPROC glad_glVertexAttrib4NubARB;\nPFNGLVERTEXATTRIB1FVARBPROC glad_glVertexAttrib1fvARB;\nPFNGLVERTEXATTRIB1SVARBPROC glad_glVertexAttrib1svARB;\nPFNGLVERTEXATTRIB1DVARBPROC glad_glVertexAttrib1dvARB;\nPFNGLVERTEXATTRIB2FVARBPROC glad_glVertexAttrib2fvARB;\nPFNGLVERTEXATTRIB2SVARBPROC glad_glVertexAttrib2svARB;\nPFNGLVERTEXATTRIB2DVARBPROC glad_glVertexAttrib2dvARB;\nPFNGLVERTEXATTRIB3FVARBPROC glad_glVertexAttrib3fvARB;\nPFNGLVERTEXATTRIB3SVARBPROC glad_glVertexAttrib3svARB;\nPFNGLVERTEXATTRIB3DVARBPROC glad_glVertexAttrib3dvARB;\nPFNGLVERTEXATTRIB4FVARBPROC glad_glVertexAttrib4fvARB;\nPFNGLVERTEXATTRIB4SVARBPROC glad_glVertexAttrib4svARB;\nPFNGLVERTEXATTRIB4DVARBPROC glad_glVertexAttrib4dvARB;\nPFNGLVERTEXATTRIB4IVARBPROC glad_glVertexAttrib4ivARB;\nPFNGLVERTEXATTRIB4BVARBPROC glad_glVertexAttrib4bvARB;\nPFNGLVERTEXATTRIB4UBVARBPROC glad_glVertexAttrib4ubvARB;\nPFNGLVERTEXATTRIB4USVARBPROC glad_glVertexAttrib4usvARB;\nPFNGLVERTEXATTRIB4UIVARBPROC glad_glVertexAttrib4uivARB;\nPFNGLVERTEXATTRIB4NBVARBPROC glad_glVertexAttrib4NbvARB;\nPFNGLVERTEXATTRIB4NSVARBPROC glad_glVertexAttrib4NsvARB;\nPFNGLVERTEXATTRIB4NIVARBPROC glad_glVertexAttrib4NivARB;\nPFNGLVERTEXATTRIB4NUBVARBPROC glad_glVertexAttrib4NubvARB;\nPFNGLVERTEXATTRIB4NUSVARBPROC glad_glVertexAttrib4NusvARB;\nPFNGLVERTEXATTRIB4NUIVARBPROC glad_glVertexAttrib4NuivARB;\nPFNGLVERTEXATTRIBPOINTERARBPROC glad_glVertexAttribPointerARB;\nPFNGLENABLEVERTEXATTRIBARRAYARBPROC glad_glEnableVertexAttribArrayARB;\nPFNGLDISABLEVERTEXATTRIBARRAYARBPROC glad_glDisableVertexAttribArrayARB;\nPFNGLBINDATTRIBLOCATIONARBPROC glad_glBindAttribLocationARB;\nPFNGLGETACTIVEATTRIBARBPROC glad_glGetActiveAttribARB;\nPFNGLGETATTRIBLOCATIONARBPROC glad_glGetAttribLocationARB;\nPFNGLGETVERTEXATTRIBDVARBPROC glad_glGetVertexAttribdvARB;\nPFNGLGETVERTEXATTRIBFVARBPROC glad_glGetVertexAttribfvARB;\nPFNGLGETVERTEXATTRIBIVARBPROC glad_glGetVertexAttribivARB;\nPFNGLGETVERTEXATTRIBPOINTERVARBPROC glad_glGetVertexAttribPointervARB;\nPFNGLCOLORMASKINDEXEDEXTPROC glad_glColorMaskIndexedEXT;\nPFNGLGETBOOLEANINDEXEDVEXTPROC glad_glGetBooleanIndexedvEXT;\nPFNGLGETINTEGERINDEXEDVEXTPROC glad_glGetIntegerIndexedvEXT;\nPFNGLENABLEINDEXEDEXTPROC glad_glEnableIndexedEXT;\nPFNGLDISABLEINDEXEDEXTPROC glad_glDisableIndexedEXT;\nPFNGLISENABLEDINDEXEDEXTPROC glad_glIsEnabledIndexedEXT;\nPFNGLVERTEXATTRIBPARAMETERIAMDPROC glad_glVertexAttribParameteriAMD;\nPFNGLPROGRAMSTRINGARBPROC glad_glProgramStringARB;\nPFNGLBINDPROGRAMARBPROC glad_glBindProgramARB;\nPFNGLDELETEPROGRAMSARBPROC glad_glDeleteProgramsARB;\nPFNGLGENPROGRAMSARBPROC glad_glGenProgramsARB;\nPFNGLPROGRAMENVPARAMETER4DARBPROC glad_glProgramEnvParameter4dARB;\nPFNGLPROGRAMENVPARAMETER4DVARBPROC glad_glProgramEnvParameter4dvARB;\nPFNGLPROGRAMENVPARAMETER4FARBPROC glad_glProgramEnvParameter4fARB;\nPFNGLPROGRAMENVPARAMETER4FVARBPROC glad_glProgramEnvParameter4fvARB;\nPFNGLPROGRAMLOCALPARAMETER4DARBPROC glad_glProgramLocalParameter4dARB;\nPFNGLPROGRAMLOCALPARAMETER4DVARBPROC glad_glProgramLocalParameter4dvARB;\nPFNGLPROGRAMLOCALPARAMETER4FARBPROC glad_glProgramLocalParameter4fARB;\nPFNGLPROGRAMLOCALPARAMETER4FVARBPROC glad_glProgramLocalParameter4fvARB;\nPFNGLGETPROGRAMENVPARAMETERDVARBPROC glad_glGetProgramEnvParameterdvARB;\nPFNGLGETPROGRAMENVPARAMETERFVARBPROC glad_glGetProgramEnvParameterfvARB;\nPFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glad_glGetProgramLocalParameterdvARB;\nPFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glad_glGetProgramLocalParameterfvARB;\nPFNGLGETPROGRAMIVARBPROC glad_glGetProgramivARB;\nPFNGLGETPROGRAMSTRINGARBPROC glad_glGetProgramStringARB;\nPFNGLISPROGRAMARBPROC glad_glIsProgramARB;\nPFNGLPIXELTEXGENPARAMETERISGISPROC glad_glPixelTexGenParameteriSGIS;\nPFNGLPIXELTEXGENPARAMETERIVSGISPROC glad_glPixelTexGenParameterivSGIS;\nPFNGLPIXELTEXGENPARAMETERFSGISPROC glad_glPixelTexGenParameterfSGIS;\nPFNGLPIXELTEXGENPARAMETERFVSGISPROC glad_glPixelTexGenParameterfvSGIS;\nPFNGLGETPIXELTEXGENPARAMETERIVSGISPROC glad_glGetPixelTexGenParameterivSGIS;\nPFNGLGETPIXELTEXGENPARAMETERFVSGISPROC glad_glGetPixelTexGenParameterfvSGIS;\nPFNGLGETINSTRUMENTSSGIXPROC glad_glGetInstrumentsSGIX;\nPFNGLINSTRUMENTSBUFFERSGIXPROC glad_glInstrumentsBufferSGIX;\nPFNGLPOLLINSTRUMENTSSGIXPROC glad_glPollInstrumentsSGIX;\nPFNGLREADINSTRUMENTSSGIXPROC glad_glReadInstrumentsSGIX;\nPFNGLSTARTINSTRUMENTSSGIXPROC glad_glStartInstrumentsSGIX;\nPFNGLSTOPINSTRUMENTSSGIXPROC glad_glStopInstrumentsSGIX;\nPFNGLBLENDEQUATIONEXTPROC glad_glBlendEquationEXT;\nPFNGLTEXPARAMETERIIVEXTPROC glad_glTexParameterIivEXT;\nPFNGLTEXPARAMETERIUIVEXTPROC glad_glTexParameterIuivEXT;\nPFNGLGETTEXPARAMETERIIVEXTPROC glad_glGetTexParameterIivEXT;\nPFNGLGETTEXPARAMETERIUIVEXTPROC glad_glGetTexParameterIuivEXT;\nPFNGLCLEARCOLORIIEXTPROC glad_glClearColorIiEXT;\nPFNGLCLEARCOLORIUIEXTPROC glad_glClearColorIuiEXT;\nPFNGLTESSELLATIONFACTORAMDPROC glad_glTessellationFactorAMD;\nPFNGLTESSELLATIONMODEAMDPROC glad_glTessellationModeAMD;\nPFNGLINDEXMATERIALEXTPROC glad_glIndexMaterialEXT;\nPFNGLVERTEXPOINTERVINTELPROC glad_glVertexPointervINTEL;\nPFNGLNORMALPOINTERVINTELPROC glad_glNormalPointervINTEL;\nPFNGLCOLORPOINTERVINTELPROC glad_glColorPointervINTEL;\nPFNGLTEXCOORDPOINTERVINTELPROC glad_glTexCoordPointervINTEL;\nPFNGLDRAWBUFFERSATIPROC glad_glDrawBuffersATI;\nPFNGLPIXELTEXGENSGIXPROC glad_glPixelTexGenSGIX;\nPFNGLPROGRAMBUFFERPARAMETERSFVNVPROC glad_glProgramBufferParametersfvNV;\nPFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC glad_glProgramBufferParametersIivNV;\nPFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC glad_glProgramBufferParametersIuivNV;\nPFNGLBINDTRANSFORMFEEDBACKNVPROC glad_glBindTransformFeedbackNV;\nPFNGLDELETETRANSFORMFEEDBACKSNVPROC glad_glDeleteTransformFeedbacksNV;\nPFNGLGENTRANSFORMFEEDBACKSNVPROC glad_glGenTransformFeedbacksNV;\nPFNGLISTRANSFORMFEEDBACKNVPROC glad_glIsTransformFeedbackNV;\nPFNGLPAUSETRANSFORMFEEDBACKNVPROC glad_glPauseTransformFeedbackNV;\nPFNGLRESUMETRANSFORMFEEDBACKNVPROC glad_glResumeTransformFeedbackNV;\nPFNGLDRAWTRANSFORMFEEDBACKNVPROC glad_glDrawTransformFeedbackNV;\nPFNGLBLENDCOLOREXTPROC glad_glBlendColorEXT;\nPFNGLGETHISTOGRAMEXTPROC glad_glGetHistogramEXT;\nPFNGLGETHISTOGRAMPARAMETERFVEXTPROC glad_glGetHistogramParameterfvEXT;\nPFNGLGETHISTOGRAMPARAMETERIVEXTPROC glad_glGetHistogramParameterivEXT;\nPFNGLGETMINMAXEXTPROC glad_glGetMinmaxEXT;\nPFNGLGETMINMAXPARAMETERFVEXTPROC glad_glGetMinmaxParameterfvEXT;\nPFNGLGETMINMAXPARAMETERIVEXTPROC glad_glGetMinmaxParameterivEXT;\nPFNGLHISTOGRAMEXTPROC glad_glHistogramEXT;\nPFNGLMINMAXEXTPROC glad_glMinmaxEXT;\nPFNGLRESETHISTOGRAMEXTPROC glad_glResetHistogramEXT;\nPFNGLRESETMINMAXEXTPROC glad_glResetMinmaxEXT;\nPFNGLPOINTPARAMETERFSGISPROC glad_glPointParameterfSGIS;\nPFNGLPOINTPARAMETERFVSGISPROC glad_glPointParameterfvSGIS;\nPFNGLMATRIXLOADFEXTPROC glad_glMatrixLoadfEXT;\nPFNGLMATRIXLOADDEXTPROC glad_glMatrixLoaddEXT;\nPFNGLMATRIXMULTFEXTPROC glad_glMatrixMultfEXT;\nPFNGLMATRIXMULTDEXTPROC glad_glMatrixMultdEXT;\nPFNGLMATRIXLOADIDENTITYEXTPROC glad_glMatrixLoadIdentityEXT;\nPFNGLMATRIXROTATEFEXTPROC glad_glMatrixRotatefEXT;\nPFNGLMATRIXROTATEDEXTPROC glad_glMatrixRotatedEXT;\nPFNGLMATRIXSCALEFEXTPROC glad_glMatrixScalefEXT;\nPFNGLMATRIXSCALEDEXTPROC glad_glMatrixScaledEXT;\nPFNGLMATRIXTRANSLATEFEXTPROC glad_glMatrixTranslatefEXT;\nPFNGLMATRIXTRANSLATEDEXTPROC glad_glMatrixTranslatedEXT;\nPFNGLMATRIXFRUSTUMEXTPROC glad_glMatrixFrustumEXT;\nPFNGLMATRIXORTHOEXTPROC glad_glMatrixOrthoEXT;\nPFNGLMATRIXPOPEXTPROC glad_glMatrixPopEXT;\nPFNGLMATRIXPUSHEXTPROC glad_glMatrixPushEXT;\nPFNGLCLIENTATTRIBDEFAULTEXTPROC glad_glClientAttribDefaultEXT;\nPFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC glad_glPushClientAttribDefaultEXT;\nPFNGLTEXTUREPARAMETERFEXTPROC glad_glTextureParameterfEXT;\nPFNGLTEXTUREPARAMETERFVEXTPROC glad_glTextureParameterfvEXT;\nPFNGLTEXTUREPARAMETERIEXTPROC glad_glTextureParameteriEXT;\nPFNGLTEXTUREPARAMETERIVEXTPROC glad_glTextureParameterivEXT;\nPFNGLTEXTUREIMAGE1DEXTPROC glad_glTextureImage1DEXT;\nPFNGLTEXTUREIMAGE2DEXTPROC glad_glTextureImage2DEXT;\nPFNGLTEXTURESUBIMAGE1DEXTPROC glad_glTextureSubImage1DEXT;\nPFNGLTEXTURESUBIMAGE2DEXTPROC glad_glTextureSubImage2DEXT;\nPFNGLCOPYTEXTUREIMAGE1DEXTPROC glad_glCopyTextureImage1DEXT;\nPFNGLCOPYTEXTUREIMAGE2DEXTPROC glad_glCopyTextureImage2DEXT;\nPFNGLCOPYTEXTURESUBIMAGE1DEXTPROC glad_glCopyTextureSubImage1DEXT;\nPFNGLCOPYTEXTURESUBIMAGE2DEXTPROC glad_glCopyTextureSubImage2DEXT;\nPFNGLGETTEXTUREIMAGEEXTPROC glad_glGetTextureImageEXT;\nPFNGLGETTEXTUREPARAMETERFVEXTPROC glad_glGetTextureParameterfvEXT;\nPFNGLGETTEXTUREPARAMETERIVEXTPROC glad_glGetTextureParameterivEXT;\nPFNGLGETTEXTURELEVELPARAMETERFVEXTPROC glad_glGetTextureLevelParameterfvEXT;\nPFNGLGETTEXTURELEVELPARAMETERIVEXTPROC glad_glGetTextureLevelParameterivEXT;\nPFNGLTEXTUREIMAGE3DEXTPROC glad_glTextureImage3DEXT;\nPFNGLTEXTURESUBIMAGE3DEXTPROC glad_glTextureSubImage3DEXT;\nPFNGLCOPYTEXTURESUBIMAGE3DEXTPROC glad_glCopyTextureSubImage3DEXT;\nPFNGLBINDMULTITEXTUREEXTPROC glad_glBindMultiTextureEXT;\nPFNGLMULTITEXCOORDPOINTEREXTPROC glad_glMultiTexCoordPointerEXT;\nPFNGLMULTITEXENVFEXTPROC glad_glMultiTexEnvfEXT;\nPFNGLMULTITEXENVFVEXTPROC glad_glMultiTexEnvfvEXT;\nPFNGLMULTITEXENVIEXTPROC glad_glMultiTexEnviEXT;\nPFNGLMULTITEXENVIVEXTPROC glad_glMultiTexEnvivEXT;\nPFNGLMULTITEXGENDEXTPROC glad_glMultiTexGendEXT;\nPFNGLMULTITEXGENDVEXTPROC glad_glMultiTexGendvEXT;\nPFNGLMULTITEXGENFEXTPROC glad_glMultiTexGenfEXT;\nPFNGLMULTITEXGENFVEXTPROC glad_glMultiTexGenfvEXT;\nPFNGLMULTITEXGENIEXTPROC glad_glMultiTexGeniEXT;\nPFNGLMULTITEXGENIVEXTPROC glad_glMultiTexGenivEXT;\nPFNGLGETMULTITEXENVFVEXTPROC glad_glGetMultiTexEnvfvEXT;\nPFNGLGETMULTITEXENVIVEXTPROC glad_glGetMultiTexEnvivEXT;\nPFNGLGETMULTITEXGENDVEXTPROC glad_glGetMultiTexGendvEXT;\nPFNGLGETMULTITEXGENFVEXTPROC glad_glGetMultiTexGenfvEXT;\nPFNGLGETMULTITEXGENIVEXTPROC glad_glGetMultiTexGenivEXT;\nPFNGLMULTITEXPARAMETERIEXTPROC glad_glMultiTexParameteriEXT;\nPFNGLMULTITEXPARAMETERIVEXTPROC glad_glMultiTexParameterivEXT;\nPFNGLMULTITEXPARAMETERFEXTPROC glad_glMultiTexParameterfEXT;\nPFNGLMULTITEXPARAMETERFVEXTPROC glad_glMultiTexParameterfvEXT;\nPFNGLMULTITEXIMAGE1DEXTPROC glad_glMultiTexImage1DEXT;\nPFNGLMULTITEXIMAGE2DEXTPROC glad_glMultiTexImage2DEXT;\nPFNGLMULTITEXSUBIMAGE1DEXTPROC glad_glMultiTexSubImage1DEXT;\nPFNGLMULTITEXSUBIMAGE2DEXTPROC glad_glMultiTexSubImage2DEXT;\nPFNGLCOPYMULTITEXIMAGE1DEXTPROC glad_glCopyMultiTexImage1DEXT;\nPFNGLCOPYMULTITEXIMAGE2DEXTPROC glad_glCopyMultiTexImage2DEXT;\nPFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC glad_glCopyMultiTexSubImage1DEXT;\nPFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC glad_glCopyMultiTexSubImage2DEXT;\nPFNGLGETMULTITEXIMAGEEXTPROC glad_glGetMultiTexImageEXT;\nPFNGLGETMULTITEXPARAMETERFVEXTPROC glad_glGetMultiTexParameterfvEXT;\nPFNGLGETMULTITEXPARAMETERIVEXTPROC glad_glGetMultiTexParameterivEXT;\nPFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC glad_glGetMultiTexLevelParameterfvEXT;\nPFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC glad_glGetMultiTexLevelParameterivEXT;\nPFNGLMULTITEXIMAGE3DEXTPROC glad_glMultiTexImage3DEXT;\nPFNGLMULTITEXSUBIMAGE3DEXTPROC glad_glMultiTexSubImage3DEXT;\nPFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC glad_glCopyMultiTexSubImage3DEXT;\nPFNGLENABLECLIENTSTATEINDEXEDEXTPROC glad_glEnableClientStateIndexedEXT;\nPFNGLDISABLECLIENTSTATEINDEXEDEXTPROC glad_glDisableClientStateIndexedEXT;\nPFNGLGETFLOATINDEXEDVEXTPROC glad_glGetFloatIndexedvEXT;\nPFNGLGETDOUBLEINDEXEDVEXTPROC glad_glGetDoubleIndexedvEXT;\nPFNGLGETPOINTERINDEXEDVEXTPROC glad_glGetPointerIndexedvEXT;\nPFNGLENABLEINDEXEDEXTPROC glad_glEnableIndexedEXT;\nPFNGLDISABLEINDEXEDEXTPROC glad_glDisableIndexedEXT;\nPFNGLISENABLEDINDEXEDEXTPROC glad_glIsEnabledIndexedEXT;\nPFNGLGETINTEGERINDEXEDVEXTPROC glad_glGetIntegerIndexedvEXT;\nPFNGLGETBOOLEANINDEXEDVEXTPROC glad_glGetBooleanIndexedvEXT;\nPFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC glad_glCompressedTextureImage3DEXT;\nPFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC glad_glCompressedTextureImage2DEXT;\nPFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC glad_glCompressedTextureImage1DEXT;\nPFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC glad_glCompressedTextureSubImage3DEXT;\nPFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC glad_glCompressedTextureSubImage2DEXT;\nPFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC glad_glCompressedTextureSubImage1DEXT;\nPFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC glad_glGetCompressedTextureImageEXT;\nPFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC glad_glCompressedMultiTexImage3DEXT;\nPFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC glad_glCompressedMultiTexImage2DEXT;\nPFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC glad_glCompressedMultiTexImage1DEXT;\nPFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC glad_glCompressedMultiTexSubImage3DEXT;\nPFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC glad_glCompressedMultiTexSubImage2DEXT;\nPFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC glad_glCompressedMultiTexSubImage1DEXT;\nPFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC glad_glGetCompressedMultiTexImageEXT;\nPFNGLMATRIXLOADTRANSPOSEFEXTPROC glad_glMatrixLoadTransposefEXT;\nPFNGLMATRIXLOADTRANSPOSEDEXTPROC glad_glMatrixLoadTransposedEXT;\nPFNGLMATRIXMULTTRANSPOSEFEXTPROC glad_glMatrixMultTransposefEXT;\nPFNGLMATRIXMULTTRANSPOSEDEXTPROC glad_glMatrixMultTransposedEXT;\nPFNGLNAMEDBUFFERDATAEXTPROC glad_glNamedBufferDataEXT;\nPFNGLNAMEDBUFFERSUBDATAEXTPROC glad_glNamedBufferSubDataEXT;\nPFNGLMAPNAMEDBUFFEREXTPROC glad_glMapNamedBufferEXT;\nPFNGLUNMAPNAMEDBUFFEREXTPROC glad_glUnmapNamedBufferEXT;\nPFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC glad_glGetNamedBufferParameterivEXT;\nPFNGLGETNAMEDBUFFERPOINTERVEXTPROC glad_glGetNamedBufferPointervEXT;\nPFNGLGETNAMEDBUFFERSUBDATAEXTPROC glad_glGetNamedBufferSubDataEXT;\nPFNGLPROGRAMUNIFORM1FEXTPROC glad_glProgramUniform1fEXT;\nPFNGLPROGRAMUNIFORM2FEXTPROC glad_glProgramUniform2fEXT;\nPFNGLPROGRAMUNIFORM3FEXTPROC glad_glProgramUniform3fEXT;\nPFNGLPROGRAMUNIFORM4FEXTPROC glad_glProgramUniform4fEXT;\nPFNGLPROGRAMUNIFORM1IEXTPROC glad_glProgramUniform1iEXT;\nPFNGLPROGRAMUNIFORM2IEXTPROC glad_glProgramUniform2iEXT;\nPFNGLPROGRAMUNIFORM3IEXTPROC glad_glProgramUniform3iEXT;\nPFNGLPROGRAMUNIFORM4IEXTPROC glad_glProgramUniform4iEXT;\nPFNGLPROGRAMUNIFORM1FVEXTPROC glad_glProgramUniform1fvEXT;\nPFNGLPROGRAMUNIFORM2FVEXTPROC glad_glProgramUniform2fvEXT;\nPFNGLPROGRAMUNIFORM3FVEXTPROC glad_glProgramUniform3fvEXT;\nPFNGLPROGRAMUNIFORM4FVEXTPROC glad_glProgramUniform4fvEXT;\nPFNGLPROGRAMUNIFORM1IVEXTPROC glad_glProgramUniform1ivEXT;\nPFNGLPROGRAMUNIFORM2IVEXTPROC glad_glProgramUniform2ivEXT;\nPFNGLPROGRAMUNIFORM3IVEXTPROC glad_glProgramUniform3ivEXT;\nPFNGLPROGRAMUNIFORM4IVEXTPROC glad_glProgramUniform4ivEXT;\nPFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC glad_glProgramUniformMatrix2fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC glad_glProgramUniformMatrix3fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC glad_glProgramUniformMatrix4fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC glad_glProgramUniformMatrix2x3fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC glad_glProgramUniformMatrix3x2fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC glad_glProgramUniformMatrix2x4fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC glad_glProgramUniformMatrix4x2fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC glad_glProgramUniformMatrix3x4fvEXT;\nPFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC glad_glProgramUniformMatrix4x3fvEXT;\nPFNGLTEXTUREBUFFEREXTPROC glad_glTextureBufferEXT;\nPFNGLMULTITEXBUFFEREXTPROC glad_glMultiTexBufferEXT;\nPFNGLTEXTUREPARAMETERIIVEXTPROC glad_glTextureParameterIivEXT;\nPFNGLTEXTUREPARAMETERIUIVEXTPROC glad_glTextureParameterIuivEXT;\nPFNGLGETTEXTUREPARAMETERIIVEXTPROC glad_glGetTextureParameterIivEXT;\nPFNGLGETTEXTUREPARAMETERIUIVEXTPROC glad_glGetTextureParameterIuivEXT;\nPFNGLMULTITEXPARAMETERIIVEXTPROC glad_glMultiTexParameterIivEXT;\nPFNGLMULTITEXPARAMETERIUIVEXTPROC glad_glMultiTexParameterIuivEXT;\nPFNGLGETMULTITEXPARAMETERIIVEXTPROC glad_glGetMultiTexParameterIivEXT;\nPFNGLGETMULTITEXPARAMETERIUIVEXTPROC glad_glGetMultiTexParameterIuivEXT;\nPFNGLPROGRAMUNIFORM1UIEXTPROC glad_glProgramUniform1uiEXT;\nPFNGLPROGRAMUNIFORM2UIEXTPROC glad_glProgramUniform2uiEXT;\nPFNGLPROGRAMUNIFORM3UIEXTPROC glad_glProgramUniform3uiEXT;\nPFNGLPROGRAMUNIFORM4UIEXTPROC glad_glProgramUniform4uiEXT;\nPFNGLPROGRAMUNIFORM1UIVEXTPROC glad_glProgramUniform1uivEXT;\nPFNGLPROGRAMUNIFORM2UIVEXTPROC glad_glProgramUniform2uivEXT;\nPFNGLPROGRAMUNIFORM3UIVEXTPROC glad_glProgramUniform3uivEXT;\nPFNGLPROGRAMUNIFORM4UIVEXTPROC glad_glProgramUniform4uivEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC glad_glNamedProgramLocalParameters4fvEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC glad_glNamedProgramLocalParameterI4iEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC glad_glNamedProgramLocalParameterI4ivEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC glad_glNamedProgramLocalParametersI4ivEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC glad_glNamedProgramLocalParameterI4uiEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC glad_glNamedProgramLocalParameterI4uivEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC glad_glNamedProgramLocalParametersI4uivEXT;\nPFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC glad_glGetNamedProgramLocalParameterIivEXT;\nPFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC glad_glGetNamedProgramLocalParameterIuivEXT;\nPFNGLENABLECLIENTSTATEIEXTPROC glad_glEnableClientStateiEXT;\nPFNGLDISABLECLIENTSTATEIEXTPROC glad_glDisableClientStateiEXT;\nPFNGLGETFLOATI_VEXTPROC glad_glGetFloati_vEXT;\nPFNGLGETDOUBLEI_VEXTPROC glad_glGetDoublei_vEXT;\nPFNGLGETPOINTERI_VEXTPROC glad_glGetPointeri_vEXT;\nPFNGLNAMEDPROGRAMSTRINGEXTPROC glad_glNamedProgramStringEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC glad_glNamedProgramLocalParameter4dEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC glad_glNamedProgramLocalParameter4dvEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC glad_glNamedProgramLocalParameter4fEXT;\nPFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC glad_glNamedProgramLocalParameter4fvEXT;\nPFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC glad_glGetNamedProgramLocalParameterdvEXT;\nPFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC glad_glGetNamedProgramLocalParameterfvEXT;\nPFNGLGETNAMEDPROGRAMIVEXTPROC glad_glGetNamedProgramivEXT;\nPFNGLGETNAMEDPROGRAMSTRINGEXTPROC glad_glGetNamedProgramStringEXT;\nPFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC glad_glNamedRenderbufferStorageEXT;\nPFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC glad_glGetNamedRenderbufferParameterivEXT;\nPFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glNamedRenderbufferStorageMultisampleEXT;\nPFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC glad_glNamedRenderbufferStorageMultisampleCoverageEXT;\nPFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC glad_glCheckNamedFramebufferStatusEXT;\nPFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC glad_glNamedFramebufferTexture1DEXT;\nPFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC glad_glNamedFramebufferTexture2DEXT;\nPFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC glad_glNamedFramebufferTexture3DEXT;\nPFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC glad_glNamedFramebufferRenderbufferEXT;\nPFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glad_glGetNamedFramebufferAttachmentParameterivEXT;\nPFNGLGENERATETEXTUREMIPMAPEXTPROC glad_glGenerateTextureMipmapEXT;\nPFNGLGENERATEMULTITEXMIPMAPEXTPROC glad_glGenerateMultiTexMipmapEXT;\nPFNGLFRAMEBUFFERDRAWBUFFEREXTPROC glad_glFramebufferDrawBufferEXT;\nPFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC glad_glFramebufferDrawBuffersEXT;\nPFNGLFRAMEBUFFERREADBUFFEREXTPROC glad_glFramebufferReadBufferEXT;\nPFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC glad_glGetFramebufferParameterivEXT;\nPFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC glad_glNamedCopyBufferSubDataEXT;\nPFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC glad_glNamedFramebufferTextureEXT;\nPFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC glad_glNamedFramebufferTextureLayerEXT;\nPFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC glad_glNamedFramebufferTextureFaceEXT;\nPFNGLTEXTURERENDERBUFFEREXTPROC glad_glTextureRenderbufferEXT;\nPFNGLMULTITEXRENDERBUFFEREXTPROC glad_glMultiTexRenderbufferEXT;\nPFNGLVERTEXARRAYVERTEXOFFSETEXTPROC glad_glVertexArrayVertexOffsetEXT;\nPFNGLVERTEXARRAYCOLOROFFSETEXTPROC glad_glVertexArrayColorOffsetEXT;\nPFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC glad_glVertexArrayEdgeFlagOffsetEXT;\nPFNGLVERTEXARRAYINDEXOFFSETEXTPROC glad_glVertexArrayIndexOffsetEXT;\nPFNGLVERTEXARRAYNORMALOFFSETEXTPROC glad_glVertexArrayNormalOffsetEXT;\nPFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC glad_glVertexArrayTexCoordOffsetEXT;\nPFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC glad_glVertexArrayMultiTexCoordOffsetEXT;\nPFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC glad_glVertexArrayFogCoordOffsetEXT;\nPFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC glad_glVertexArraySecondaryColorOffsetEXT;\nPFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC glad_glVertexArrayVertexAttribOffsetEXT;\nPFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC glad_glVertexArrayVertexAttribIOffsetEXT;\nPFNGLENABLEVERTEXARRAYEXTPROC glad_glEnableVertexArrayEXT;\nPFNGLDISABLEVERTEXARRAYEXTPROC glad_glDisableVertexArrayEXT;\nPFNGLENABLEVERTEXARRAYATTRIBEXTPROC glad_glEnableVertexArrayAttribEXT;\nPFNGLDISABLEVERTEXARRAYATTRIBEXTPROC glad_glDisableVertexArrayAttribEXT;\nPFNGLGETVERTEXARRAYINTEGERVEXTPROC glad_glGetVertexArrayIntegervEXT;\nPFNGLGETVERTEXARRAYPOINTERVEXTPROC glad_glGetVertexArrayPointervEXT;\nPFNGLGETVERTEXARRAYINTEGERI_VEXTPROC glad_glGetVertexArrayIntegeri_vEXT;\nPFNGLGETVERTEXARRAYPOINTERI_VEXTPROC glad_glGetVertexArrayPointeri_vEXT;\nPFNGLMAPNAMEDBUFFERRANGEEXTPROC glad_glMapNamedBufferRangeEXT;\nPFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC glad_glFlushMappedNamedBufferRangeEXT;\nPFNGLNAMEDBUFFERSTORAGEEXTPROC glad_glNamedBufferStorageEXT;\nPFNGLCLEARNAMEDBUFFERDATAEXTPROC glad_glClearNamedBufferDataEXT;\nPFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC glad_glClearNamedBufferSubDataEXT;\nPFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC glad_glNamedFramebufferParameteriEXT;\nPFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC glad_glGetNamedFramebufferParameterivEXT;\nPFNGLPROGRAMUNIFORM1DEXTPROC glad_glProgramUniform1dEXT;\nPFNGLPROGRAMUNIFORM2DEXTPROC glad_glProgramUniform2dEXT;\nPFNGLPROGRAMUNIFORM3DEXTPROC glad_glProgramUniform3dEXT;\nPFNGLPROGRAMUNIFORM4DEXTPROC glad_glProgramUniform4dEXT;\nPFNGLPROGRAMUNIFORM1DVEXTPROC glad_glProgramUniform1dvEXT;\nPFNGLPROGRAMUNIFORM2DVEXTPROC glad_glProgramUniform2dvEXT;\nPFNGLPROGRAMUNIFORM3DVEXTPROC glad_glProgramUniform3dvEXT;\nPFNGLPROGRAMUNIFORM4DVEXTPROC glad_glProgramUniform4dvEXT;\nPFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC glad_glProgramUniformMatrix2dvEXT;\nPFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC glad_glProgramUniformMatrix3dvEXT;\nPFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC glad_glProgramUniformMatrix4dvEXT;\nPFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC glad_glProgramUniformMatrix2x3dvEXT;\nPFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC glad_glProgramUniformMatrix2x4dvEXT;\nPFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC glad_glProgramUniformMatrix3x2dvEXT;\nPFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC glad_glProgramUniformMatrix3x4dvEXT;\nPFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC glad_glProgramUniformMatrix4x2dvEXT;\nPFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC glad_glProgramUniformMatrix4x3dvEXT;\nPFNGLTEXTUREBUFFERRANGEEXTPROC glad_glTextureBufferRangeEXT;\nPFNGLTEXTURESTORAGE1DEXTPROC glad_glTextureStorage1DEXT;\nPFNGLTEXTURESTORAGE2DEXTPROC glad_glTextureStorage2DEXT;\nPFNGLTEXTURESTORAGE3DEXTPROC glad_glTextureStorage3DEXT;\nPFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC glad_glTextureStorage2DMultisampleEXT;\nPFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC glad_glTextureStorage3DMultisampleEXT;\nPFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC glad_glVertexArrayBindVertexBufferEXT;\nPFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC glad_glVertexArrayVertexAttribFormatEXT;\nPFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC glad_glVertexArrayVertexAttribIFormatEXT;\nPFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC glad_glVertexArrayVertexAttribLFormatEXT;\nPFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC glad_glVertexArrayVertexAttribBindingEXT;\nPFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC glad_glVertexArrayVertexBindingDivisorEXT;\nPFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC glad_glVertexArrayVertexAttribLOffsetEXT;\nPFNGLTEXTUREPAGECOMMITMENTEXTPROC glad_glTexturePageCommitmentEXT;\nPFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC glad_glVertexArrayVertexAttribDivisorEXT;\nPFNGLSETMULTISAMPLEFVAMDPROC glad_glSetMultisamplefvAMD;\nPFNGLAREPROGRAMSRESIDENTNVPROC glad_glAreProgramsResidentNV;\nPFNGLBINDPROGRAMNVPROC glad_glBindProgramNV;\nPFNGLDELETEPROGRAMSNVPROC glad_glDeleteProgramsNV;\nPFNGLEXECUTEPROGRAMNVPROC glad_glExecuteProgramNV;\nPFNGLGENPROGRAMSNVPROC glad_glGenProgramsNV;\nPFNGLGETPROGRAMPARAMETERDVNVPROC glad_glGetProgramParameterdvNV;\nPFNGLGETPROGRAMPARAMETERFVNVPROC glad_glGetProgramParameterfvNV;\nPFNGLGETPROGRAMIVNVPROC glad_glGetProgramivNV;\nPFNGLGETPROGRAMSTRINGNVPROC glad_glGetProgramStringNV;\nPFNGLGETTRACKMATRIXIVNVPROC glad_glGetTrackMatrixivNV;\nPFNGLGETVERTEXATTRIBDVNVPROC glad_glGetVertexAttribdvNV;\nPFNGLGETVERTEXATTRIBFVNVPROC glad_glGetVertexAttribfvNV;\nPFNGLGETVERTEXATTRIBIVNVPROC glad_glGetVertexAttribivNV;\nPFNGLGETVERTEXATTRIBPOINTERVNVPROC glad_glGetVertexAttribPointervNV;\nPFNGLISPROGRAMNVPROC glad_glIsProgramNV;\nPFNGLLOADPROGRAMNVPROC glad_glLoadProgramNV;\nPFNGLPROGRAMPARAMETER4DNVPROC glad_glProgramParameter4dNV;\nPFNGLPROGRAMPARAMETER4DVNVPROC glad_glProgramParameter4dvNV;\nPFNGLPROGRAMPARAMETER4FNVPROC glad_glProgramParameter4fNV;\nPFNGLPROGRAMPARAMETER4FVNVPROC glad_glProgramParameter4fvNV;\nPFNGLPROGRAMPARAMETERS4DVNVPROC glad_glProgramParameters4dvNV;\nPFNGLPROGRAMPARAMETERS4FVNVPROC glad_glProgramParameters4fvNV;\nPFNGLREQUESTRESIDENTPROGRAMSNVPROC glad_glRequestResidentProgramsNV;\nPFNGLTRACKMATRIXNVPROC glad_glTrackMatrixNV;\nPFNGLVERTEXATTRIBPOINTERNVPROC glad_glVertexAttribPointerNV;\nPFNGLVERTEXATTRIB1DNVPROC glad_glVertexAttrib1dNV;\nPFNGLVERTEXATTRIB1DVNVPROC glad_glVertexAttrib1dvNV;\nPFNGLVERTEXATTRIB1FNVPROC glad_glVertexAttrib1fNV;\nPFNGLVERTEXATTRIB1FVNVPROC glad_glVertexAttrib1fvNV;\nPFNGLVERTEXATTRIB1SNVPROC glad_glVertexAttrib1sNV;\nPFNGLVERTEXATTRIB1SVNVPROC glad_glVertexAttrib1svNV;\nPFNGLVERTEXATTRIB2DNVPROC glad_glVertexAttrib2dNV;\nPFNGLVERTEXATTRIB2DVNVPROC glad_glVertexAttrib2dvNV;\nPFNGLVERTEXATTRIB2FNVPROC glad_glVertexAttrib2fNV;\nPFNGLVERTEXATTRIB2FVNVPROC glad_glVertexAttrib2fvNV;\nPFNGLVERTEXATTRIB2SNVPROC glad_glVertexAttrib2sNV;\nPFNGLVERTEXATTRIB2SVNVPROC glad_glVertexAttrib2svNV;\nPFNGLVERTEXATTRIB3DNVPROC glad_glVertexAttrib3dNV;\nPFNGLVERTEXATTRIB3DVNVPROC glad_glVertexAttrib3dvNV;\nPFNGLVERTEXATTRIB3FNVPROC glad_glVertexAttrib3fNV;\nPFNGLVERTEXATTRIB3FVNVPROC glad_glVertexAttrib3fvNV;\nPFNGLVERTEXATTRIB3SNVPROC glad_glVertexAttrib3sNV;\nPFNGLVERTEXATTRIB3SVNVPROC glad_glVertexAttrib3svNV;\nPFNGLVERTEXATTRIB4DNVPROC glad_glVertexAttrib4dNV;\nPFNGLVERTEXATTRIB4DVNVPROC glad_glVertexAttrib4dvNV;\nPFNGLVERTEXATTRIB4FNVPROC glad_glVertexAttrib4fNV;\nPFNGLVERTEXATTRIB4FVNVPROC glad_glVertexAttrib4fvNV;\nPFNGLVERTEXATTRIB4SNVPROC glad_glVertexAttrib4sNV;\nPFNGLVERTEXATTRIB4SVNVPROC glad_glVertexAttrib4svNV;\nPFNGLVERTEXATTRIB4UBNVPROC glad_glVertexAttrib4ubNV;\nPFNGLVERTEXATTRIB4UBVNVPROC glad_glVertexAttrib4ubvNV;\nPFNGLVERTEXATTRIBS1DVNVPROC glad_glVertexAttribs1dvNV;\nPFNGLVERTEXATTRIBS1FVNVPROC glad_glVertexAttribs1fvNV;\nPFNGLVERTEXATTRIBS1SVNVPROC glad_glVertexAttribs1svNV;\nPFNGLVERTEXATTRIBS2DVNVPROC glad_glVertexAttribs2dvNV;\nPFNGLVERTEXATTRIBS2FVNVPROC glad_glVertexAttribs2fvNV;\nPFNGLVERTEXATTRIBS2SVNVPROC glad_glVertexAttribs2svNV;\nPFNGLVERTEXATTRIBS3DVNVPROC glad_glVertexAttribs3dvNV;\nPFNGLVERTEXATTRIBS3FVNVPROC glad_glVertexAttribs3fvNV;\nPFNGLVERTEXATTRIBS3SVNVPROC glad_glVertexAttribs3svNV;\nPFNGLVERTEXATTRIBS4DVNVPROC glad_glVertexAttribs4dvNV;\nPFNGLVERTEXATTRIBS4FVNVPROC glad_glVertexAttribs4fvNV;\nPFNGLVERTEXATTRIBS4SVNVPROC glad_glVertexAttribs4svNV;\nPFNGLVERTEXATTRIBS4UBVNVPROC glad_glVertexAttribs4ubvNV;\nPFNGLBEGINCONDITIONALRENDERNVXPROC glad_glBeginConditionalRenderNVX;\nPFNGLENDCONDITIONALRENDERNVXPROC glad_glEndConditionalRenderNVX;\nPFNGLBEGINVERTEXSHADEREXTPROC glad_glBeginVertexShaderEXT;\nPFNGLENDVERTEXSHADEREXTPROC glad_glEndVertexShaderEXT;\nPFNGLBINDVERTEXSHADEREXTPROC glad_glBindVertexShaderEXT;\nPFNGLGENVERTEXSHADERSEXTPROC glad_glGenVertexShadersEXT;\nPFNGLDELETEVERTEXSHADEREXTPROC glad_glDeleteVertexShaderEXT;\nPFNGLSHADEROP1EXTPROC glad_glShaderOp1EXT;\nPFNGLSHADEROP2EXTPROC glad_glShaderOp2EXT;\nPFNGLSHADEROP3EXTPROC glad_glShaderOp3EXT;\nPFNGLSWIZZLEEXTPROC glad_glSwizzleEXT;\nPFNGLWRITEMASKEXTPROC glad_glWriteMaskEXT;\nPFNGLINSERTCOMPONENTEXTPROC glad_glInsertComponentEXT;\nPFNGLEXTRACTCOMPONENTEXTPROC glad_glExtractComponentEXT;\nPFNGLGENSYMBOLSEXTPROC glad_glGenSymbolsEXT;\nPFNGLSETINVARIANTEXTPROC glad_glSetInvariantEXT;\nPFNGLSETLOCALCONSTANTEXTPROC glad_glSetLocalConstantEXT;\nPFNGLVARIANTBVEXTPROC glad_glVariantbvEXT;\nPFNGLVARIANTSVEXTPROC glad_glVariantsvEXT;\nPFNGLVARIANTIVEXTPROC glad_glVariantivEXT;\nPFNGLVARIANTFVEXTPROC glad_glVariantfvEXT;\nPFNGLVARIANTDVEXTPROC glad_glVariantdvEXT;\nPFNGLVARIANTUBVEXTPROC glad_glVariantubvEXT;\nPFNGLVARIANTUSVEXTPROC glad_glVariantusvEXT;\nPFNGLVARIANTUIVEXTPROC glad_glVariantuivEXT;\nPFNGLVARIANTPOINTEREXTPROC glad_glVariantPointerEXT;\nPFNGLENABLEVARIANTCLIENTSTATEEXTPROC glad_glEnableVariantClientStateEXT;\nPFNGLDISABLEVARIANTCLIENTSTATEEXTPROC glad_glDisableVariantClientStateEXT;\nPFNGLBINDLIGHTPARAMETEREXTPROC glad_glBindLightParameterEXT;\nPFNGLBINDMATERIALPARAMETEREXTPROC glad_glBindMaterialParameterEXT;\nPFNGLBINDTEXGENPARAMETEREXTPROC glad_glBindTexGenParameterEXT;\nPFNGLBINDTEXTUREUNITPARAMETEREXTPROC glad_glBindTextureUnitParameterEXT;\nPFNGLBINDPARAMETEREXTPROC glad_glBindParameterEXT;\nPFNGLISVARIANTENABLEDEXTPROC glad_glIsVariantEnabledEXT;\nPFNGLGETVARIANTBOOLEANVEXTPROC glad_glGetVariantBooleanvEXT;\nPFNGLGETVARIANTINTEGERVEXTPROC glad_glGetVariantIntegervEXT;\nPFNGLGETVARIANTFLOATVEXTPROC glad_glGetVariantFloatvEXT;\nPFNGLGETVARIANTPOINTERVEXTPROC glad_glGetVariantPointervEXT;\nPFNGLGETINVARIANTBOOLEANVEXTPROC glad_glGetInvariantBooleanvEXT;\nPFNGLGETINVARIANTINTEGERVEXTPROC glad_glGetInvariantIntegervEXT;\nPFNGLGETINVARIANTFLOATVEXTPROC glad_glGetInvariantFloatvEXT;\nPFNGLGETLOCALCONSTANTBOOLEANVEXTPROC glad_glGetLocalConstantBooleanvEXT;\nPFNGLGETLOCALCONSTANTINTEGERVEXTPROC glad_glGetLocalConstantIntegervEXT;\nPFNGLGETLOCALCONSTANTFLOATVEXTPROC glad_glGetLocalConstantFloatvEXT;\nPFNGLBLENDFUNCSEPARATEEXTPROC glad_glBlendFuncSeparateEXT;\nPFNGLGENFENCESAPPLEPROC glad_glGenFencesAPPLE;\nPFNGLDELETEFENCESAPPLEPROC glad_glDeleteFencesAPPLE;\nPFNGLSETFENCEAPPLEPROC glad_glSetFenceAPPLE;\nPFNGLISFENCEAPPLEPROC glad_glIsFenceAPPLE;\nPFNGLTESTFENCEAPPLEPROC glad_glTestFenceAPPLE;\nPFNGLFINISHFENCEAPPLEPROC glad_glFinishFenceAPPLE;\nPFNGLTESTOBJECTAPPLEPROC glad_glTestObjectAPPLE;\nPFNGLFINISHOBJECTAPPLEPROC glad_glFinishObjectAPPLE;\nPFNGLMULTITEXCOORD1BOESPROC glad_glMultiTexCoord1bOES;\nPFNGLMULTITEXCOORD1BVOESPROC glad_glMultiTexCoord1bvOES;\nPFNGLMULTITEXCOORD2BOESPROC glad_glMultiTexCoord2bOES;\nPFNGLMULTITEXCOORD2BVOESPROC glad_glMultiTexCoord2bvOES;\nPFNGLMULTITEXCOORD3BOESPROC glad_glMultiTexCoord3bOES;\nPFNGLMULTITEXCOORD3BVOESPROC glad_glMultiTexCoord3bvOES;\nPFNGLMULTITEXCOORD4BOESPROC glad_glMultiTexCoord4bOES;\nPFNGLMULTITEXCOORD4BVOESPROC glad_glMultiTexCoord4bvOES;\nPFNGLTEXCOORD1BOESPROC glad_glTexCoord1bOES;\nPFNGLTEXCOORD1BVOESPROC glad_glTexCoord1bvOES;\nPFNGLTEXCOORD2BOESPROC glad_glTexCoord2bOES;\nPFNGLTEXCOORD2BVOESPROC glad_glTexCoord2bvOES;\nPFNGLTEXCOORD3BOESPROC glad_glTexCoord3bOES;\nPFNGLTEXCOORD3BVOESPROC glad_glTexCoord3bvOES;\nPFNGLTEXCOORD4BOESPROC glad_glTexCoord4bOES;\nPFNGLTEXCOORD4BVOESPROC glad_glTexCoord4bvOES;\nPFNGLVERTEX2BOESPROC glad_glVertex2bOES;\nPFNGLVERTEX2BVOESPROC glad_glVertex2bvOES;\nPFNGLVERTEX3BOESPROC glad_glVertex3bOES;\nPFNGLVERTEX3BVOESPROC glad_glVertex3bvOES;\nPFNGLVERTEX4BOESPROC glad_glVertex4bOES;\nPFNGLVERTEX4BVOESPROC glad_glVertex4bvOES;\nPFNGLLOADTRANSPOSEMATRIXFARBPROC glad_glLoadTransposeMatrixfARB;\nPFNGLLOADTRANSPOSEMATRIXDARBPROC glad_glLoadTransposeMatrixdARB;\nPFNGLMULTTRANSPOSEMATRIXFARBPROC glad_glMultTransposeMatrixfARB;\nPFNGLMULTTRANSPOSEMATRIXDARBPROC glad_glMultTransposeMatrixdARB;\nPFNGLFOGCOORDFEXTPROC glad_glFogCoordfEXT;\nPFNGLFOGCOORDFVEXTPROC glad_glFogCoordfvEXT;\nPFNGLFOGCOORDDEXTPROC glad_glFogCoorddEXT;\nPFNGLFOGCOORDDVEXTPROC glad_glFogCoorddvEXT;\nPFNGLFOGCOORDPOINTEREXTPROC glad_glFogCoordPointerEXT;\nPFNGLARRAYELEMENTEXTPROC glad_glArrayElementEXT;\nPFNGLCOLORPOINTEREXTPROC glad_glColorPointerEXT;\nPFNGLDRAWARRAYSEXTPROC glad_glDrawArraysEXT;\nPFNGLEDGEFLAGPOINTEREXTPROC glad_glEdgeFlagPointerEXT;\nPFNGLGETPOINTERVEXTPROC glad_glGetPointervEXT;\nPFNGLINDEXPOINTEREXTPROC glad_glIndexPointerEXT;\nPFNGLNORMALPOINTEREXTPROC glad_glNormalPointerEXT;\nPFNGLTEXCOORDPOINTEREXTPROC glad_glTexCoordPointerEXT;\nPFNGLVERTEXPOINTEREXTPROC glad_glVertexPointerEXT;\nPFNGLBLENDEQUATIONSEPARATEEXTPROC glad_glBlendEquationSeparateEXT;\nPFNGLCOPYIMAGESUBDATANVPROC glad_glCopyImageSubDataNV;\nPFNGLINSERTEVENTMARKEREXTPROC glad_glInsertEventMarkerEXT;\nPFNGLPUSHGROUPMARKEREXTPROC glad_glPushGroupMarkerEXT;\nPFNGLPOPGROUPMARKEREXTPROC glad_glPopGroupMarkerEXT;\nPFNGLPIXELTRANSFORMPARAMETERIEXTPROC glad_glPixelTransformParameteriEXT;\nPFNGLPIXELTRANSFORMPARAMETERFEXTPROC glad_glPixelTransformParameterfEXT;\nPFNGLPIXELTRANSFORMPARAMETERIVEXTPROC glad_glPixelTransformParameterivEXT;\nPFNGLPIXELTRANSFORMPARAMETERFVEXTPROC glad_glPixelTransformParameterfvEXT;\nPFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC glad_glGetPixelTransformParameterivEXT;\nPFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC glad_glGetPixelTransformParameterfvEXT;\nPFNGLGENFRAGMENTSHADERSATIPROC glad_glGenFragmentShadersATI;\nPFNGLBINDFRAGMENTSHADERATIPROC glad_glBindFragmentShaderATI;\nPFNGLDELETEFRAGMENTSHADERATIPROC glad_glDeleteFragmentShaderATI;\nPFNGLBEGINFRAGMENTSHADERATIPROC glad_glBeginFragmentShaderATI;\nPFNGLENDFRAGMENTSHADERATIPROC glad_glEndFragmentShaderATI;\nPFNGLPASSTEXCOORDATIPROC glad_glPassTexCoordATI;\nPFNGLSAMPLEMAPATIPROC glad_glSampleMapATI;\nPFNGLCOLORFRAGMENTOP1ATIPROC glad_glColorFragmentOp1ATI;\nPFNGLCOLORFRAGMENTOP2ATIPROC glad_glColorFragmentOp2ATI;\nPFNGLCOLORFRAGMENTOP3ATIPROC glad_glColorFragmentOp3ATI;\nPFNGLALPHAFRAGMENTOP1ATIPROC glad_glAlphaFragmentOp1ATI;\nPFNGLALPHAFRAGMENTOP2ATIPROC glad_glAlphaFragmentOp2ATI;\nPFNGLALPHAFRAGMENTOP3ATIPROC glad_glAlphaFragmentOp3ATI;\nPFNGLSETFRAGMENTSHADERCONSTANTATIPROC glad_glSetFragmentShaderConstantATI;\nPFNGLREPLACEMENTCODEUISUNPROC glad_glReplacementCodeuiSUN;\nPFNGLREPLACEMENTCODEUSSUNPROC glad_glReplacementCodeusSUN;\nPFNGLREPLACEMENTCODEUBSUNPROC glad_glReplacementCodeubSUN;\nPFNGLREPLACEMENTCODEUIVSUNPROC glad_glReplacementCodeuivSUN;\nPFNGLREPLACEMENTCODEUSVSUNPROC glad_glReplacementCodeusvSUN;\nPFNGLREPLACEMENTCODEUBVSUNPROC glad_glReplacementCodeubvSUN;\nPFNGLREPLACEMENTCODEPOINTERSUNPROC glad_glReplacementCodePointerSUN;\nPFNGLASYNCMARKERSGIXPROC glad_glAsyncMarkerSGIX;\nPFNGLFINISHASYNCSGIXPROC glad_glFinishAsyncSGIX;\nPFNGLPOLLASYNCSGIXPROC glad_glPollAsyncSGIX;\nPFNGLGENASYNCMARKERSSGIXPROC glad_glGenAsyncMarkersSGIX;\nPFNGLDELETEASYNCMARKERSSGIXPROC glad_glDeleteAsyncMarkersSGIX;\nPFNGLISASYNCMARKERSGIXPROC glad_glIsAsyncMarkerSGIX;\nPFNGLBEGINPERFQUERYINTELPROC glad_glBeginPerfQueryINTEL;\nPFNGLCREATEPERFQUERYINTELPROC glad_glCreatePerfQueryINTEL;\nPFNGLDELETEPERFQUERYINTELPROC glad_glDeletePerfQueryINTEL;\nPFNGLENDPERFQUERYINTELPROC glad_glEndPerfQueryINTEL;\nPFNGLGETFIRSTPERFQUERYIDINTELPROC glad_glGetFirstPerfQueryIdINTEL;\nPFNGLGETNEXTPERFQUERYIDINTELPROC glad_glGetNextPerfQueryIdINTEL;\nPFNGLGETPERFCOUNTERINFOINTELPROC glad_glGetPerfCounterInfoINTEL;\nPFNGLGETPERFQUERYDATAINTELPROC glad_glGetPerfQueryDataINTEL;\nPFNGLGETPERFQUERYIDBYNAMEINTELPROC glad_glGetPerfQueryIdByNameINTEL;\nPFNGLGETPERFQUERYINFOINTELPROC glad_glGetPerfQueryInfoINTEL;\nPFNGLUNIFORM1I64NVPROC glad_glUniform1i64NV;\nPFNGLUNIFORM2I64NVPROC glad_glUniform2i64NV;\nPFNGLUNIFORM3I64NVPROC glad_glUniform3i64NV;\nPFNGLUNIFORM4I64NVPROC glad_glUniform4i64NV;\nPFNGLUNIFORM1I64VNVPROC glad_glUniform1i64vNV;\nPFNGLUNIFORM2I64VNVPROC glad_glUniform2i64vNV;\nPFNGLUNIFORM3I64VNVPROC glad_glUniform3i64vNV;\nPFNGLUNIFORM4I64VNVPROC glad_glUniform4i64vNV;\nPFNGLUNIFORM1UI64NVPROC glad_glUniform1ui64NV;\nPFNGLUNIFORM2UI64NVPROC glad_glUniform2ui64NV;\nPFNGLUNIFORM3UI64NVPROC glad_glUniform3ui64NV;\nPFNGLUNIFORM4UI64NVPROC glad_glUniform4ui64NV;\nPFNGLUNIFORM1UI64VNVPROC glad_glUniform1ui64vNV;\nPFNGLUNIFORM2UI64VNVPROC glad_glUniform2ui64vNV;\nPFNGLUNIFORM3UI64VNVPROC glad_glUniform3ui64vNV;\nPFNGLUNIFORM4UI64VNVPROC glad_glUniform4ui64vNV;\nPFNGLGETUNIFORMI64VNVPROC glad_glGetUniformi64vNV;\nPFNGLPROGRAMUNIFORM1I64NVPROC glad_glProgramUniform1i64NV;\nPFNGLPROGRAMUNIFORM2I64NVPROC glad_glProgramUniform2i64NV;\nPFNGLPROGRAMUNIFORM3I64NVPROC glad_glProgramUniform3i64NV;\nPFNGLPROGRAMUNIFORM4I64NVPROC glad_glProgramUniform4i64NV;\nPFNGLPROGRAMUNIFORM1I64VNVPROC glad_glProgramUniform1i64vNV;\nPFNGLPROGRAMUNIFORM2I64VNVPROC glad_glProgramUniform2i64vNV;\nPFNGLPROGRAMUNIFORM3I64VNVPROC glad_glProgramUniform3i64vNV;\nPFNGLPROGRAMUNIFORM4I64VNVPROC glad_glProgramUniform4i64vNV;\nPFNGLPROGRAMUNIFORM1UI64NVPROC glad_glProgramUniform1ui64NV;\nPFNGLPROGRAMUNIFORM2UI64NVPROC glad_glProgramUniform2ui64NV;\nPFNGLPROGRAMUNIFORM3UI64NVPROC glad_glProgramUniform3ui64NV;\nPFNGLPROGRAMUNIFORM4UI64NVPROC glad_glProgramUniform4ui64NV;\nPFNGLPROGRAMUNIFORM1UI64VNVPROC glad_glProgramUniform1ui64vNV;\nPFNGLPROGRAMUNIFORM2UI64VNVPROC glad_glProgramUniform2ui64vNV;\nPFNGLPROGRAMUNIFORM3UI64VNVPROC glad_glProgramUniform3ui64vNV;\nPFNGLPROGRAMUNIFORM4UI64VNVPROC glad_glProgramUniform4ui64vNV;\nPFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC glad_glMultiDrawArraysIndirectCountARB;\nPFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC glad_glMultiDrawElementsIndirectCountARB;\nPFNGLVERTEX2HNVPROC glad_glVertex2hNV;\nPFNGLVERTEX2HVNVPROC glad_glVertex2hvNV;\nPFNGLVERTEX3HNVPROC glad_glVertex3hNV;\nPFNGLVERTEX3HVNVPROC glad_glVertex3hvNV;\nPFNGLVERTEX4HNVPROC glad_glVertex4hNV;\nPFNGLVERTEX4HVNVPROC glad_glVertex4hvNV;\nPFNGLNORMAL3HNVPROC glad_glNormal3hNV;\nPFNGLNORMAL3HVNVPROC glad_glNormal3hvNV;\nPFNGLCOLOR3HNVPROC glad_glColor3hNV;\nPFNGLCOLOR3HVNVPROC glad_glColor3hvNV;\nPFNGLCOLOR4HNVPROC glad_glColor4hNV;\nPFNGLCOLOR4HVNVPROC glad_glColor4hvNV;\nPFNGLTEXCOORD1HNVPROC glad_glTexCoord1hNV;\nPFNGLTEXCOORD1HVNVPROC glad_glTexCoord1hvNV;\nPFNGLTEXCOORD2HNVPROC glad_glTexCoord2hNV;\nPFNGLTEXCOORD2HVNVPROC glad_glTexCoord2hvNV;\nPFNGLTEXCOORD3HNVPROC glad_glTexCoord3hNV;\nPFNGLTEXCOORD3HVNVPROC glad_glTexCoord3hvNV;\nPFNGLTEXCOORD4HNVPROC glad_glTexCoord4hNV;\nPFNGLTEXCOORD4HVNVPROC glad_glTexCoord4hvNV;\nPFNGLMULTITEXCOORD1HNVPROC glad_glMultiTexCoord1hNV;\nPFNGLMULTITEXCOORD1HVNVPROC glad_glMultiTexCoord1hvNV;\nPFNGLMULTITEXCOORD2HNVPROC glad_glMultiTexCoord2hNV;\nPFNGLMULTITEXCOORD2HVNVPROC glad_glMultiTexCoord2hvNV;\nPFNGLMULTITEXCOORD3HNVPROC glad_glMultiTexCoord3hNV;\nPFNGLMULTITEXCOORD3HVNVPROC glad_glMultiTexCoord3hvNV;\nPFNGLMULTITEXCOORD4HNVPROC glad_glMultiTexCoord4hNV;\nPFNGLMULTITEXCOORD4HVNVPROC glad_glMultiTexCoord4hvNV;\nPFNGLFOGCOORDHNVPROC glad_glFogCoordhNV;\nPFNGLFOGCOORDHVNVPROC glad_glFogCoordhvNV;\nPFNGLSECONDARYCOLOR3HNVPROC glad_glSecondaryColor3hNV;\nPFNGLSECONDARYCOLOR3HVNVPROC glad_glSecondaryColor3hvNV;\nPFNGLVERTEXWEIGHTHNVPROC glad_glVertexWeighthNV;\nPFNGLVERTEXWEIGHTHVNVPROC glad_glVertexWeighthvNV;\nPFNGLVERTEXATTRIB1HNVPROC glad_glVertexAttrib1hNV;\nPFNGLVERTEXATTRIB1HVNVPROC glad_glVertexAttrib1hvNV;\nPFNGLVERTEXATTRIB2HNVPROC glad_glVertexAttrib2hNV;\nPFNGLVERTEXATTRIB2HVNVPROC glad_glVertexAttrib2hvNV;\nPFNGLVERTEXATTRIB3HNVPROC glad_glVertexAttrib3hNV;\nPFNGLVERTEXATTRIB3HVNVPROC glad_glVertexAttrib3hvNV;\nPFNGLVERTEXATTRIB4HNVPROC glad_glVertexAttrib4hNV;\nPFNGLVERTEXATTRIB4HVNVPROC glad_glVertexAttrib4hvNV;\nPFNGLVERTEXATTRIBS1HVNVPROC glad_glVertexAttribs1hvNV;\nPFNGLVERTEXATTRIBS2HVNVPROC glad_glVertexAttribs2hvNV;\nPFNGLVERTEXATTRIBS3HVNVPROC glad_glVertexAttribs3hvNV;\nPFNGLVERTEXATTRIBS4HVNVPROC glad_glVertexAttribs4hvNV;\nPFNGLTANGENT3BEXTPROC glad_glTangent3bEXT;\nPFNGLTANGENT3BVEXTPROC glad_glTangent3bvEXT;\nPFNGLTANGENT3DEXTPROC glad_glTangent3dEXT;\nPFNGLTANGENT3DVEXTPROC glad_glTangent3dvEXT;\nPFNGLTANGENT3FEXTPROC glad_glTangent3fEXT;\nPFNGLTANGENT3FVEXTPROC glad_glTangent3fvEXT;\nPFNGLTANGENT3IEXTPROC glad_glTangent3iEXT;\nPFNGLTANGENT3IVEXTPROC glad_glTangent3ivEXT;\nPFNGLTANGENT3SEXTPROC glad_glTangent3sEXT;\nPFNGLTANGENT3SVEXTPROC glad_glTangent3svEXT;\nPFNGLBINORMAL3BEXTPROC glad_glBinormal3bEXT;\nPFNGLBINORMAL3BVEXTPROC glad_glBinormal3bvEXT;\nPFNGLBINORMAL3DEXTPROC glad_glBinormal3dEXT;\nPFNGLBINORMAL3DVEXTPROC glad_glBinormal3dvEXT;\nPFNGLBINORMAL3FEXTPROC glad_glBinormal3fEXT;\nPFNGLBINORMAL3FVEXTPROC glad_glBinormal3fvEXT;\nPFNGLBINORMAL3IEXTPROC glad_glBinormal3iEXT;\nPFNGLBINORMAL3IVEXTPROC glad_glBinormal3ivEXT;\nPFNGLBINORMAL3SEXTPROC glad_glBinormal3sEXT;\nPFNGLBINORMAL3SVEXTPROC glad_glBinormal3svEXT;\nPFNGLTANGENTPOINTEREXTPROC glad_glTangentPointerEXT;\nPFNGLBINORMALPOINTEREXTPROC glad_glBinormalPointerEXT;\nPFNGLLOCKARRAYSEXTPROC glad_glLockArraysEXT;\nPFNGLUNLOCKARRAYSEXTPROC glad_glUnlockArraysEXT;\nPFNGLDEPTHRANGEDNVPROC glad_glDepthRangedNV;\nPFNGLCLEARDEPTHDNVPROC glad_glClearDepthdNV;\nPFNGLDEPTHBOUNDSDNVPROC glad_glDepthBoundsdNV;\nPFNGLGENOCCLUSIONQUERIESNVPROC glad_glGenOcclusionQueriesNV;\nPFNGLDELETEOCCLUSIONQUERIESNVPROC glad_glDeleteOcclusionQueriesNV;\nPFNGLISOCCLUSIONQUERYNVPROC glad_glIsOcclusionQueryNV;\nPFNGLBEGINOCCLUSIONQUERYNVPROC glad_glBeginOcclusionQueryNV;\nPFNGLENDOCCLUSIONQUERYNVPROC glad_glEndOcclusionQueryNV;\nPFNGLGETOCCLUSIONQUERYIVNVPROC glad_glGetOcclusionQueryivNV;\nPFNGLGETOCCLUSIONQUERYUIVNVPROC glad_glGetOcclusionQueryuivNV;\nPFNGLBUFFERPARAMETERIAPPLEPROC glad_glBufferParameteriAPPLE;\nPFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC glad_glFlushMappedBufferRangeAPPLE;\nPFNGLCOLORTABLEPROC glad_glColorTable;\nPFNGLCOLORTABLEPARAMETERFVPROC glad_glColorTableParameterfv;\nPFNGLCOLORTABLEPARAMETERIVPROC glad_glColorTableParameteriv;\nPFNGLCOPYCOLORTABLEPROC glad_glCopyColorTable;\nPFNGLGETCOLORTABLEPROC glad_glGetColorTable;\nPFNGLGETCOLORTABLEPARAMETERFVPROC glad_glGetColorTableParameterfv;\nPFNGLGETCOLORTABLEPARAMETERIVPROC glad_glGetColorTableParameteriv;\nPFNGLCOLORSUBTABLEPROC glad_glColorSubTable;\nPFNGLCOPYCOLORSUBTABLEPROC glad_glCopyColorSubTable;\nPFNGLCONVOLUTIONFILTER1DPROC glad_glConvolutionFilter1D;\nPFNGLCONVOLUTIONFILTER2DPROC glad_glConvolutionFilter2D;\nPFNGLCONVOLUTIONPARAMETERFPROC glad_glConvolutionParameterf;\nPFNGLCONVOLUTIONPARAMETERFVPROC glad_glConvolutionParameterfv;\nPFNGLCONVOLUTIONPARAMETERIPROC glad_glConvolutionParameteri;\nPFNGLCONVOLUTIONPARAMETERIVPROC glad_glConvolutionParameteriv;\nPFNGLCOPYCONVOLUTIONFILTER1DPROC glad_glCopyConvolutionFilter1D;\nPFNGLCOPYCONVOLUTIONFILTER2DPROC glad_glCopyConvolutionFilter2D;\nPFNGLGETCONVOLUTIONFILTERPROC glad_glGetConvolutionFilter;\nPFNGLGETCONVOLUTIONPARAMETERFVPROC glad_glGetConvolutionParameterfv;\nPFNGLGETCONVOLUTIONPARAMETERIVPROC glad_glGetConvolutionParameteriv;\nPFNGLGETSEPARABLEFILTERPROC glad_glGetSeparableFilter;\nPFNGLSEPARABLEFILTER2DPROC glad_glSeparableFilter2D;\nPFNGLGETHISTOGRAMPROC glad_glGetHistogram;\nPFNGLGETHISTOGRAMPARAMETERFVPROC glad_glGetHistogramParameterfv;\nPFNGLGETHISTOGRAMPARAMETERIVPROC glad_glGetHistogramParameteriv;\nPFNGLGETMINMAXPROC glad_glGetMinmax;\nPFNGLGETMINMAXPARAMETERFVPROC glad_glGetMinmaxParameterfv;\nPFNGLGETMINMAXPARAMETERIVPROC glad_glGetMinmaxParameteriv;\nPFNGLHISTOGRAMPROC glad_glHistogram;\nPFNGLMINMAXPROC glad_glMinmax;\nPFNGLRESETHISTOGRAMPROC glad_glResetHistogram;\nPFNGLRESETMINMAXPROC glad_glResetMinmax;\nPFNGLBLENDEQUATIONIARBPROC glad_glBlendEquationiARB;\nPFNGLBLENDEQUATIONSEPARATEIARBPROC glad_glBlendEquationSeparateiARB;\nPFNGLBLENDFUNCIARBPROC glad_glBlendFunciARB;\nPFNGLBLENDFUNCSEPARATEIARBPROC glad_glBlendFuncSeparateiARB;\nPFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB;\nPFNGLLABELOBJECTEXTPROC glad_glLabelObjectEXT;\nPFNGLGETOBJECTLABELEXTPROC glad_glGetObjectLabelEXT;\nPFNGLMINSAMPLESHADINGARBPROC glad_glMinSampleShadingARB;\nPFNGLSYNCTEXTUREINTELPROC glad_glSyncTextureINTEL;\nPFNGLUNMAPTEXTURE2DINTELPROC glad_glUnmapTexture2DINTEL;\nPFNGLMAPTEXTURE2DINTELPROC glad_glMapTexture2DINTEL;\nPFNGLCOLORPOINTERLISTIBMPROC glad_glColorPointerListIBM;\nPFNGLSECONDARYCOLORPOINTERLISTIBMPROC glad_glSecondaryColorPointerListIBM;\nPFNGLEDGEFLAGPOINTERLISTIBMPROC glad_glEdgeFlagPointerListIBM;\nPFNGLFOGCOORDPOINTERLISTIBMPROC glad_glFogCoordPointerListIBM;\nPFNGLINDEXPOINTERLISTIBMPROC glad_glIndexPointerListIBM;\nPFNGLNORMALPOINTERLISTIBMPROC glad_glNormalPointerListIBM;\nPFNGLTEXCOORDPOINTERLISTIBMPROC glad_glTexCoordPointerListIBM;\nPFNGLVERTEXPOINTERLISTIBMPROC glad_glVertexPointerListIBM;\nPFNGLCLAMPCOLORARBPROC glad_glClampColorARB;\nPFNGLGETTEXTUREHANDLEARBPROC glad_glGetTextureHandleARB;\nPFNGLGETTEXTURESAMPLERHANDLEARBPROC glad_glGetTextureSamplerHandleARB;\nPFNGLMAKETEXTUREHANDLERESIDENTARBPROC glad_glMakeTextureHandleResidentARB;\nPFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC glad_glMakeTextureHandleNonResidentARB;\nPFNGLGETIMAGEHANDLEARBPROC glad_glGetImageHandleARB;\nPFNGLMAKEIMAGEHANDLERESIDENTARBPROC glad_glMakeImageHandleResidentARB;\nPFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC glad_glMakeImageHandleNonResidentARB;\nPFNGLUNIFORMHANDLEUI64ARBPROC glad_glUniformHandleui64ARB;\nPFNGLUNIFORMHANDLEUI64VARBPROC glad_glUniformHandleui64vARB;\nPFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC glad_glProgramUniformHandleui64ARB;\nPFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC glad_glProgramUniformHandleui64vARB;\nPFNGLISTEXTUREHANDLERESIDENTARBPROC glad_glIsTextureHandleResidentARB;\nPFNGLISIMAGEHANDLERESIDENTARBPROC glad_glIsImageHandleResidentARB;\nPFNGLVERTEXATTRIBL1UI64ARBPROC glad_glVertexAttribL1ui64ARB;\nPFNGLVERTEXATTRIBL1UI64VARBPROC glad_glVertexAttribL1ui64vARB;\nPFNGLGETVERTEXATTRIBLUI64VARBPROC glad_glGetVertexAttribLui64vARB;\nPFNGLWINDOWPOS2DARBPROC glad_glWindowPos2dARB;\nPFNGLWINDOWPOS2DVARBPROC glad_glWindowPos2dvARB;\nPFNGLWINDOWPOS2FARBPROC glad_glWindowPos2fARB;\nPFNGLWINDOWPOS2FVARBPROC glad_glWindowPos2fvARB;\nPFNGLWINDOWPOS2IARBPROC glad_glWindowPos2iARB;\nPFNGLWINDOWPOS2IVARBPROC glad_glWindowPos2ivARB;\nPFNGLWINDOWPOS2SARBPROC glad_glWindowPos2sARB;\nPFNGLWINDOWPOS2SVARBPROC glad_glWindowPos2svARB;\nPFNGLWINDOWPOS3DARBPROC glad_glWindowPos3dARB;\nPFNGLWINDOWPOS3DVARBPROC glad_glWindowPos3dvARB;\nPFNGLWINDOWPOS3FARBPROC glad_glWindowPos3fARB;\nPFNGLWINDOWPOS3FVARBPROC glad_glWindowPos3fvARB;\nPFNGLWINDOWPOS3IARBPROC glad_glWindowPos3iARB;\nPFNGLWINDOWPOS3IVARBPROC glad_glWindowPos3ivARB;\nPFNGLWINDOWPOS3SARBPROC glad_glWindowPos3sARB;\nPFNGLWINDOWPOS3SVARBPROC glad_glWindowPos3svARB;\nPFNGLBINDIMAGETEXTUREEXTPROC glad_glBindImageTextureEXT;\nPFNGLMEMORYBARRIEREXTPROC glad_glMemoryBarrierEXT;\nPFNGLCOPYTEXIMAGE1DEXTPROC glad_glCopyTexImage1DEXT;\nPFNGLCOPYTEXIMAGE2DEXTPROC glad_glCopyTexImage2DEXT;\nPFNGLCOPYTEXSUBIMAGE1DEXTPROC glad_glCopyTexSubImage1DEXT;\nPFNGLCOPYTEXSUBIMAGE2DEXTPROC glad_glCopyTexSubImage2DEXT;\nPFNGLCOPYTEXSUBIMAGE3DEXTPROC glad_glCopyTexSubImage3DEXT;\nPFNGLCOMBINERSTAGEPARAMETERFVNVPROC glad_glCombinerStageParameterfvNV;\nPFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC glad_glGetCombinerStageParameterfvNV;\nPFNGLDRAWTEXTURENVPROC glad_glDrawTextureNV;\nPFNGLDRAWARRAYSINSTANCEDEXTPROC glad_glDrawArraysInstancedEXT;\nPFNGLDRAWELEMENTSINSTANCEDEXTPROC glad_glDrawElementsInstancedEXT;\nPFNGLDEPTHBOUNDSEXTPROC glad_glDepthBoundsEXT;\nPFNGLIMAGETRANSFORMPARAMETERIHPPROC glad_glImageTransformParameteriHP;\nPFNGLIMAGETRANSFORMPARAMETERFHPPROC glad_glImageTransformParameterfHP;\nPFNGLIMAGETRANSFORMPARAMETERIVHPPROC glad_glImageTransformParameterivHP;\nPFNGLIMAGETRANSFORMPARAMETERFVHPPROC glad_glImageTransformParameterfvHP;\nPFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC glad_glGetImageTransformParameterivHP;\nPFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC glad_glGetImageTransformParameterfvHP;\nPFNGLBEGINVIDEOCAPTURENVPROC glad_glBeginVideoCaptureNV;\nPFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC glad_glBindVideoCaptureStreamBufferNV;\nPFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC glad_glBindVideoCaptureStreamTextureNV;\nPFNGLENDVIDEOCAPTURENVPROC glad_glEndVideoCaptureNV;\nPFNGLGETVIDEOCAPTUREIVNVPROC glad_glGetVideoCaptureivNV;\nPFNGLGETVIDEOCAPTURESTREAMIVNVPROC glad_glGetVideoCaptureStreamivNV;\nPFNGLGETVIDEOCAPTURESTREAMFVNVPROC glad_glGetVideoCaptureStreamfvNV;\nPFNGLGETVIDEOCAPTURESTREAMDVNVPROC glad_glGetVideoCaptureStreamdvNV;\nPFNGLVIDEOCAPTURENVPROC glad_glVideoCaptureNV;\nPFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC glad_glVideoCaptureStreamParameterivNV;\nPFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC glad_glVideoCaptureStreamParameterfvNV;\nPFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC glad_glVideoCaptureStreamParameterdvNV;\nPFNGLCURRENTPALETTEMATRIXARBPROC glad_glCurrentPaletteMatrixARB;\nPFNGLMATRIXINDEXUBVARBPROC glad_glMatrixIndexubvARB;\nPFNGLMATRIXINDEXUSVARBPROC glad_glMatrixIndexusvARB;\nPFNGLMATRIXINDEXUIVARBPROC glad_glMatrixIndexuivARB;\nPFNGLMATRIXINDEXPOINTERARBPROC glad_glMatrixIndexPointerARB;\nPFNGLTEXTURECOLORMASKSGISPROC glad_glTextureColorMaskSGIS;\nPFNGLCOMPRESSEDTEXIMAGE3DARBPROC glad_glCompressedTexImage3DARB;\nPFNGLCOMPRESSEDTEXIMAGE2DARBPROC glad_glCompressedTexImage2DARB;\nPFNGLCOMPRESSEDTEXIMAGE1DARBPROC glad_glCompressedTexImage1DARB;\nPFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glad_glCompressedTexSubImage3DARB;\nPFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glad_glCompressedTexSubImage2DARB;\nPFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glad_glCompressedTexSubImage1DARB;\nPFNGLGETCOMPRESSEDTEXIMAGEARBPROC glad_glGetCompressedTexImageARB;\nPFNGLVERTEXATTRIBL1DEXTPROC glad_glVertexAttribL1dEXT;\nPFNGLVERTEXATTRIBL2DEXTPROC glad_glVertexAttribL2dEXT;\nPFNGLVERTEXATTRIBL3DEXTPROC glad_glVertexAttribL3dEXT;\nPFNGLVERTEXATTRIBL4DEXTPROC glad_glVertexAttribL4dEXT;\nPFNGLVERTEXATTRIBL1DVEXTPROC glad_glVertexAttribL1dvEXT;\nPFNGLVERTEXATTRIBL2DVEXTPROC glad_glVertexAttribL2dvEXT;\nPFNGLVERTEXATTRIBL3DVEXTPROC glad_glVertexAttribL3dvEXT;\nPFNGLVERTEXATTRIBL4DVEXTPROC glad_glVertexAttribL4dvEXT;\nPFNGLVERTEXATTRIBLPOINTEREXTPROC glad_glVertexAttribLPointerEXT;\nPFNGLGETVERTEXATTRIBLDVEXTPROC glad_glGetVertexAttribLdvEXT;\nPFNGLQUERYMATRIXXOESPROC glad_glQueryMatrixxOES;\nPFNGLTEXTURERANGEAPPLEPROC glad_glTextureRangeAPPLE;\nPFNGLGETTEXPARAMETERPOINTERVAPPLEPROC glad_glGetTexParameterPointervAPPLE;\nPFNGLOBJECTPURGEABLEAPPLEPROC glad_glObjectPurgeableAPPLE;\nPFNGLOBJECTUNPURGEABLEAPPLEPROC glad_glObjectUnpurgeableAPPLE;\nPFNGLGETOBJECTPARAMETERIVAPPLEPROC glad_glGetObjectParameterivAPPLE;\nPFNGLGENQUERIESARBPROC glad_glGenQueriesARB;\nPFNGLDELETEQUERIESARBPROC glad_glDeleteQueriesARB;\nPFNGLISQUERYARBPROC glad_glIsQueryARB;\nPFNGLBEGINQUERYARBPROC glad_glBeginQueryARB;\nPFNGLENDQUERYARBPROC glad_glEndQueryARB;\nPFNGLGETQUERYIVARBPROC glad_glGetQueryivARB;\nPFNGLGETQUERYOBJECTIVARBPROC glad_glGetQueryObjectivARB;\nPFNGLGETQUERYOBJECTUIVARBPROC glad_glGetQueryObjectuivARB;\nPFNGLCOLORTABLESGIPROC glad_glColorTableSGI;\nPFNGLCOLORTABLEPARAMETERFVSGIPROC glad_glColorTableParameterfvSGI;\nPFNGLCOLORTABLEPARAMETERIVSGIPROC glad_glColorTableParameterivSGI;\nPFNGLCOPYCOLORTABLESGIPROC glad_glCopyColorTableSGI;\nPFNGLGETCOLORTABLESGIPROC glad_glGetColorTableSGI;\nPFNGLGETCOLORTABLEPARAMETERFVSGIPROC glad_glGetColorTableParameterfvSGI;\nPFNGLGETCOLORTABLEPARAMETERIVSGIPROC glad_glGetColorTableParameterivSGI;\nPFNGLGETUNIFORMUIVEXTPROC glad_glGetUniformuivEXT;\nPFNGLBINDFRAGDATALOCATIONEXTPROC glad_glBindFragDataLocationEXT;\nPFNGLGETFRAGDATALOCATIONEXTPROC glad_glGetFragDataLocationEXT;\nPFNGLUNIFORM1UIEXTPROC glad_glUniform1uiEXT;\nPFNGLUNIFORM2UIEXTPROC glad_glUniform2uiEXT;\nPFNGLUNIFORM3UIEXTPROC glad_glUniform3uiEXT;\nPFNGLUNIFORM4UIEXTPROC glad_glUniform4uiEXT;\nPFNGLUNIFORM1UIVEXTPROC glad_glUniform1uivEXT;\nPFNGLUNIFORM2UIVEXTPROC glad_glUniform2uivEXT;\nPFNGLUNIFORM3UIVEXTPROC glad_glUniform3uivEXT;\nPFNGLUNIFORM4UIVEXTPROC glad_glUniform4uivEXT;\nPFNGLPROGRAMVERTEXLIMITNVPROC glad_glProgramVertexLimitNV;\nPFNGLFRAMEBUFFERTEXTUREEXTPROC glad_glFramebufferTextureEXT;\nPFNGLFRAMEBUFFERTEXTURELAYEREXTPROC glad_glFramebufferTextureLayerEXT;\nPFNGLFRAMEBUFFERTEXTUREFACEEXTPROC glad_glFramebufferTextureFaceEXT;\nPFNGLDEBUGMESSAGEENABLEAMDPROC glad_glDebugMessageEnableAMD;\nPFNGLDEBUGMESSAGEINSERTAMDPROC glad_glDebugMessageInsertAMD;\nPFNGLDEBUGMESSAGECALLBACKAMDPROC glad_glDebugMessageCallbackAMD;\nPFNGLGETDEBUGMESSAGELOGAMDPROC glad_glGetDebugMessageLogAMD;\nPFNGLACTIVETEXTUREARBPROC glad_glActiveTextureARB;\nPFNGLCLIENTACTIVETEXTUREARBPROC glad_glClientActiveTextureARB;\nPFNGLMULTITEXCOORD1DARBPROC glad_glMultiTexCoord1dARB;\nPFNGLMULTITEXCOORD1DVARBPROC glad_glMultiTexCoord1dvARB;\nPFNGLMULTITEXCOORD1FARBPROC glad_glMultiTexCoord1fARB;\nPFNGLMULTITEXCOORD1FVARBPROC glad_glMultiTexCoord1fvARB;\nPFNGLMULTITEXCOORD1IARBPROC glad_glMultiTexCoord1iARB;\nPFNGLMULTITEXCOORD1IVARBPROC glad_glMultiTexCoord1ivARB;\nPFNGLMULTITEXCOORD1SARBPROC glad_glMultiTexCoord1sARB;\nPFNGLMULTITEXCOORD1SVARBPROC glad_glMultiTexCoord1svARB;\nPFNGLMULTITEXCOORD2DARBPROC glad_glMultiTexCoord2dARB;\nPFNGLMULTITEXCOORD2DVARBPROC glad_glMultiTexCoord2dvARB;\nPFNGLMULTITEXCOORD2FARBPROC glad_glMultiTexCoord2fARB;\nPFNGLMULTITEXCOORD2FVARBPROC glad_glMultiTexCoord2fvARB;\nPFNGLMULTITEXCOORD2IARBPROC glad_glMultiTexCoord2iARB;\nPFNGLMULTITEXCOORD2IVARBPROC glad_glMultiTexCoord2ivARB;\nPFNGLMULTITEXCOORD2SARBPROC glad_glMultiTexCoord2sARB;\nPFNGLMULTITEXCOORD2SVARBPROC glad_glMultiTexCoord2svARB;\nPFNGLMULTITEXCOORD3DARBPROC glad_glMultiTexCoord3dARB;\nPFNGLMULTITEXCOORD3DVARBPROC glad_glMultiTexCoord3dvARB;\nPFNGLMULTITEXCOORD3FARBPROC glad_glMultiTexCoord3fARB;\nPFNGLMULTITEXCOORD3FVARBPROC glad_glMultiTexCoord3fvARB;\nPFNGLMULTITEXCOORD3IARBPROC glad_glMultiTexCoord3iARB;\nPFNGLMULTITEXCOORD3IVARBPROC glad_glMultiTexCoord3ivARB;\nPFNGLMULTITEXCOORD3SARBPROC glad_glMultiTexCoord3sARB;\nPFNGLMULTITEXCOORD3SVARBPROC glad_glMultiTexCoord3svARB;\nPFNGLMULTITEXCOORD4DARBPROC glad_glMultiTexCoord4dARB;\nPFNGLMULTITEXCOORD4DVARBPROC glad_glMultiTexCoord4dvARB;\nPFNGLMULTITEXCOORD4FARBPROC glad_glMultiTexCoord4fARB;\nPFNGLMULTITEXCOORD4FVARBPROC glad_glMultiTexCoord4fvARB;\nPFNGLMULTITEXCOORD4IARBPROC glad_glMultiTexCoord4iARB;\nPFNGLMULTITEXCOORD4IVARBPROC glad_glMultiTexCoord4ivARB;\nPFNGLMULTITEXCOORD4SARBPROC glad_glMultiTexCoord4sARB;\nPFNGLMULTITEXCOORD4SVARBPROC glad_glMultiTexCoord4svARB;\nPFNGLDEFORMATIONMAP3DSGIXPROC glad_glDeformationMap3dSGIX;\nPFNGLDEFORMATIONMAP3FSGIXPROC glad_glDeformationMap3fSGIX;\nPFNGLDEFORMSGIXPROC glad_glDeformSGIX;\nPFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC glad_glLoadIdentityDeformationMapSGIX;\nPFNGLPROVOKINGVERTEXEXTPROC glad_glProvokingVertexEXT;\nPFNGLPOINTPARAMETERFARBPROC glad_glPointParameterfARB;\nPFNGLPOINTPARAMETERFVARBPROC glad_glPointParameterfvARB;\nPFNGLFRAMEZOOMSGIXPROC glad_glFrameZoomSGIX;\nPFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC glad_glMultiDrawArraysIndirectBindlessNV;\nPFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC glad_glMultiDrawElementsIndirectBindlessNV;\nPFNGLBEGINTRANSFORMFEEDBACKEXTPROC glad_glBeginTransformFeedbackEXT;\nPFNGLENDTRANSFORMFEEDBACKEXTPROC glad_glEndTransformFeedbackEXT;\nPFNGLBINDBUFFERRANGEEXTPROC glad_glBindBufferRangeEXT;\nPFNGLBINDBUFFEROFFSETEXTPROC glad_glBindBufferOffsetEXT;\nPFNGLBINDBUFFERBASEEXTPROC glad_glBindBufferBaseEXT;\nPFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC glad_glTransformFeedbackVaryingsEXT;\nPFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC glad_glGetTransformFeedbackVaryingEXT;\nPFNGLPROGRAMLOCALPARAMETERI4INVPROC glad_glProgramLocalParameterI4iNV;\nPFNGLPROGRAMLOCALPARAMETERI4IVNVPROC glad_glProgramLocalParameterI4ivNV;\nPFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC glad_glProgramLocalParametersI4ivNV;\nPFNGLPROGRAMLOCALPARAMETERI4UINVPROC glad_glProgramLocalParameterI4uiNV;\nPFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC glad_glProgramLocalParameterI4uivNV;\nPFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC glad_glProgramLocalParametersI4uivNV;\nPFNGLPROGRAMENVPARAMETERI4INVPROC glad_glProgramEnvParameterI4iNV;\nPFNGLPROGRAMENVPARAMETERI4IVNVPROC glad_glProgramEnvParameterI4ivNV;\nPFNGLPROGRAMENVPARAMETERSI4IVNVPROC glad_glProgramEnvParametersI4ivNV;\nPFNGLPROGRAMENVPARAMETERI4UINVPROC glad_glProgramEnvParameterI4uiNV;\nPFNGLPROGRAMENVPARAMETERI4UIVNVPROC glad_glProgramEnvParameterI4uivNV;\nPFNGLPROGRAMENVPARAMETERSI4UIVNVPROC glad_glProgramEnvParametersI4uivNV;\nPFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC glad_glGetProgramLocalParameterIivNV;\nPFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC glad_glGetProgramLocalParameterIuivNV;\nPFNGLGETPROGRAMENVPARAMETERIIVNVPROC glad_glGetProgramEnvParameterIivNV;\nPFNGLGETPROGRAMENVPARAMETERIUIVNVPROC glad_glGetProgramEnvParameterIuivNV;\nPFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC glad_glProgramSubroutineParametersuivNV;\nPFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC glad_glGetProgramSubroutineParameteruivNV;\nPFNGLPROGRAMPARAMETERIARBPROC glad_glProgramParameteriARB;\nPFNGLFRAMEBUFFERTEXTUREARBPROC glad_glFramebufferTextureARB;\nPFNGLFRAMEBUFFERTEXTURELAYERARBPROC glad_glFramebufferTextureLayerARB;\nPFNGLFRAMEBUFFERTEXTUREFACEARBPROC glad_glFramebufferTextureFaceARB;\nPFNGLSPRITEPARAMETERFSGIXPROC glad_glSpriteParameterfSGIX;\nPFNGLSPRITEPARAMETERFVSGIXPROC glad_glSpriteParameterfvSGIX;\nPFNGLSPRITEPARAMETERISGIXPROC glad_glSpriteParameteriSGIX;\nPFNGLSPRITEPARAMETERIVSGIXPROC glad_glSpriteParameterivSGIX;\nPFNGLQUERYOBJECTPARAMETERUIAMDPROC glad_glQueryObjectParameteruiAMD;\nPFNGLSAMPLEMASKSGISPROC glad_glSampleMaskSGIS;\nPFNGLSAMPLEPATTERNSGISPROC glad_glSamplePatternSGIS;\nPFNGLISRENDERBUFFEREXTPROC glad_glIsRenderbufferEXT;\nPFNGLBINDRENDERBUFFEREXTPROC glad_glBindRenderbufferEXT;\nPFNGLDELETERENDERBUFFERSEXTPROC glad_glDeleteRenderbuffersEXT;\nPFNGLGENRENDERBUFFERSEXTPROC glad_glGenRenderbuffersEXT;\nPFNGLRENDERBUFFERSTORAGEEXTPROC glad_glRenderbufferStorageEXT;\nPFNGLGETRENDERBUFFERPARAMETERIVEXTPROC glad_glGetRenderbufferParameterivEXT;\nPFNGLISFRAMEBUFFEREXTPROC glad_glIsFramebufferEXT;\nPFNGLBINDFRAMEBUFFEREXTPROC glad_glBindFramebufferEXT;\nPFNGLDELETEFRAMEBUFFERSEXTPROC glad_glDeleteFramebuffersEXT;\nPFNGLGENFRAMEBUFFERSEXTPROC glad_glGenFramebuffersEXT;\nPFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glad_glCheckFramebufferStatusEXT;\nPFNGLFRAMEBUFFERTEXTURE1DEXTPROC glad_glFramebufferTexture1DEXT;\nPFNGLFRAMEBUFFERTEXTURE2DEXTPROC glad_glFramebufferTexture2DEXT;\nPFNGLFRAMEBUFFERTEXTURE3DEXTPROC glad_glFramebufferTexture3DEXT;\nPFNGLFRAMEBUFFERRENDERBUFFEREXTPROC glad_glFramebufferRenderbufferEXT;\nPFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC glad_glGetFramebufferAttachmentParameterivEXT;\nPFNGLGENERATEMIPMAPEXTPROC glad_glGenerateMipmapEXT;\nPFNGLVERTEXARRAYRANGEAPPLEPROC glad_glVertexArrayRangeAPPLE;\nPFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC glad_glFlushVertexArrayRangeAPPLE;\nPFNGLVERTEXARRAYPARAMETERIAPPLEPROC glad_glVertexArrayParameteriAPPLE;\nPFNGLCOMBINERPARAMETERFVNVPROC glad_glCombinerParameterfvNV;\nPFNGLCOMBINERPARAMETERFNVPROC glad_glCombinerParameterfNV;\nPFNGLCOMBINERPARAMETERIVNVPROC glad_glCombinerParameterivNV;\nPFNGLCOMBINERPARAMETERINVPROC glad_glCombinerParameteriNV;\nPFNGLCOMBINERINPUTNVPROC glad_glCombinerInputNV;\nPFNGLCOMBINEROUTPUTNVPROC glad_glCombinerOutputNV;\nPFNGLFINALCOMBINERINPUTNVPROC glad_glFinalCombinerInputNV;\nPFNGLGETCOMBINERINPUTPARAMETERFVNVPROC glad_glGetCombinerInputParameterfvNV;\nPFNGLGETCOMBINERINPUTPARAMETERIVNVPROC glad_glGetCombinerInputParameterivNV;\nPFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC glad_glGetCombinerOutputParameterfvNV;\nPFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC glad_glGetCombinerOutputParameterivNV;\nPFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC glad_glGetFinalCombinerInputParameterfvNV;\nPFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC glad_glGetFinalCombinerInputParameterivNV;\nPFNGLDRAWBUFFERSARBPROC glad_glDrawBuffersARB;\nPFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB;\nPFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB;\nPFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB;\nPFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB;\nPFNGLCULLPARAMETERDVEXTPROC glad_glCullParameterdvEXT;\nPFNGLCULLPARAMETERFVEXTPROC glad_glCullParameterfvEXT;\nPFNGLMULTIMODEDRAWARRAYSIBMPROC glad_glMultiModeDrawArraysIBM;\nPFNGLMULTIMODEDRAWELEMENTSIBMPROC glad_glMultiModeDrawElementsIBM;\nPFNGLBINDVERTEXARRAYAPPLEPROC glad_glBindVertexArrayAPPLE;\nPFNGLDELETEVERTEXARRAYSAPPLEPROC glad_glDeleteVertexArraysAPPLE;\nPFNGLGENVERTEXARRAYSAPPLEPROC glad_glGenVertexArraysAPPLE;\nPFNGLISVERTEXARRAYAPPLEPROC glad_glIsVertexArrayAPPLE;\nPFNGLDETAILTEXFUNCSGISPROC glad_glDetailTexFuncSGIS;\nPFNGLGETDETAILTEXFUNCSGISPROC glad_glGetDetailTexFuncSGIS;\nPFNGLDRAWARRAYSINSTANCEDARBPROC glad_glDrawArraysInstancedARB;\nPFNGLDRAWELEMENTSINSTANCEDARBPROC glad_glDrawElementsInstancedARB;\nPFNGLNAMEDSTRINGARBPROC glad_glNamedStringARB;\nPFNGLDELETENAMEDSTRINGARBPROC glad_glDeleteNamedStringARB;\nPFNGLCOMPILESHADERINCLUDEARBPROC glad_glCompileShaderIncludeARB;\nPFNGLISNAMEDSTRINGARBPROC glad_glIsNamedStringARB;\nPFNGLGETNAMEDSTRINGARBPROC glad_glGetNamedStringARB;\nPFNGLGETNAMEDSTRINGIVARBPROC glad_glGetNamedStringivARB;\nPFNGLBLENDFUNCSEPARATEINGRPROC glad_glBlendFuncSeparateINGR;\nPFNGLGENPATHSNVPROC glad_glGenPathsNV;\nPFNGLDELETEPATHSNVPROC glad_glDeletePathsNV;\nPFNGLISPATHNVPROC glad_glIsPathNV;\nPFNGLPATHCOMMANDSNVPROC glad_glPathCommandsNV;\nPFNGLPATHCOORDSNVPROC glad_glPathCoordsNV;\nPFNGLPATHSUBCOMMANDSNVPROC glad_glPathSubCommandsNV;\nPFNGLPATHSUBCOORDSNVPROC glad_glPathSubCoordsNV;\nPFNGLPATHSTRINGNVPROC glad_glPathStringNV;\nPFNGLPATHGLYPHSNVPROC glad_glPathGlyphsNV;\nPFNGLPATHGLYPHRANGENVPROC glad_glPathGlyphRangeNV;\nPFNGLWEIGHTPATHSNVPROC glad_glWeightPathsNV;\nPFNGLCOPYPATHNVPROC glad_glCopyPathNV;\nPFNGLINTERPOLATEPATHSNVPROC glad_glInterpolatePathsNV;\nPFNGLTRANSFORMPATHNVPROC glad_glTransformPathNV;\nPFNGLPATHPARAMETERIVNVPROC glad_glPathParameterivNV;\nPFNGLPATHPARAMETERINVPROC glad_glPathParameteriNV;\nPFNGLPATHPARAMETERFVNVPROC glad_glPathParameterfvNV;\nPFNGLPATHPARAMETERFNVPROC glad_glPathParameterfNV;\nPFNGLPATHDASHARRAYNVPROC glad_glPathDashArrayNV;\nPFNGLPATHSTENCILFUNCNVPROC glad_glPathStencilFuncNV;\nPFNGLPATHSTENCILDEPTHOFFSETNVPROC glad_glPathStencilDepthOffsetNV;\nPFNGLSTENCILFILLPATHNVPROC glad_glStencilFillPathNV;\nPFNGLSTENCILSTROKEPATHNVPROC glad_glStencilStrokePathNV;\nPFNGLSTENCILFILLPATHINSTANCEDNVPROC glad_glStencilFillPathInstancedNV;\nPFNGLSTENCILSTROKEPATHINSTANCEDNVPROC glad_glStencilStrokePathInstancedNV;\nPFNGLPATHCOVERDEPTHFUNCNVPROC glad_glPathCoverDepthFuncNV;\nPFNGLPATHCOLORGENNVPROC glad_glPathColorGenNV;\nPFNGLPATHTEXGENNVPROC glad_glPathTexGenNV;\nPFNGLPATHFOGGENNVPROC glad_glPathFogGenNV;\nPFNGLCOVERFILLPATHNVPROC glad_glCoverFillPathNV;\nPFNGLCOVERSTROKEPATHNVPROC glad_glCoverStrokePathNV;\nPFNGLCOVERFILLPATHINSTANCEDNVPROC glad_glCoverFillPathInstancedNV;\nPFNGLCOVERSTROKEPATHINSTANCEDNVPROC glad_glCoverStrokePathInstancedNV;\nPFNGLGETPATHPARAMETERIVNVPROC glad_glGetPathParameterivNV;\nPFNGLGETPATHPARAMETERFVNVPROC glad_glGetPathParameterfvNV;\nPFNGLGETPATHCOMMANDSNVPROC glad_glGetPathCommandsNV;\nPFNGLGETPATHCOORDSNVPROC glad_glGetPathCoordsNV;\nPFNGLGETPATHDASHARRAYNVPROC glad_glGetPathDashArrayNV;\nPFNGLGETPATHMETRICSNVPROC glad_glGetPathMetricsNV;\nPFNGLGETPATHMETRICRANGENVPROC glad_glGetPathMetricRangeNV;\nPFNGLGETPATHSPACINGNVPROC glad_glGetPathSpacingNV;\nPFNGLGETPATHCOLORGENIVNVPROC glad_glGetPathColorGenivNV;\nPFNGLGETPATHCOLORGENFVNVPROC glad_glGetPathColorGenfvNV;\nPFNGLGETPATHTEXGENIVNVPROC glad_glGetPathTexGenivNV;\nPFNGLGETPATHTEXGENFVNVPROC glad_glGetPathTexGenfvNV;\nPFNGLISPOINTINFILLPATHNVPROC glad_glIsPointInFillPathNV;\nPFNGLISPOINTINSTROKEPATHNVPROC glad_glIsPointInStrokePathNV;\nPFNGLGETPATHLENGTHNVPROC glad_glGetPathLengthNV;\nPFNGLPOINTALONGPATHNVPROC glad_glPointAlongPathNV;\nPFNGLVERTEXSTREAM1SATIPROC glad_glVertexStream1sATI;\nPFNGLVERTEXSTREAM1SVATIPROC glad_glVertexStream1svATI;\nPFNGLVERTEXSTREAM1IATIPROC glad_glVertexStream1iATI;\nPFNGLVERTEXSTREAM1IVATIPROC glad_glVertexStream1ivATI;\nPFNGLVERTEXSTREAM1FATIPROC glad_glVertexStream1fATI;\nPFNGLVERTEXSTREAM1FVATIPROC glad_glVertexStream1fvATI;\nPFNGLVERTEXSTREAM1DATIPROC glad_glVertexStream1dATI;\nPFNGLVERTEXSTREAM1DVATIPROC glad_glVertexStream1dvATI;\nPFNGLVERTEXSTREAM2SATIPROC glad_glVertexStream2sATI;\nPFNGLVERTEXSTREAM2SVATIPROC glad_glVertexStream2svATI;\nPFNGLVERTEXSTREAM2IATIPROC glad_glVertexStream2iATI;\nPFNGLVERTEXSTREAM2IVATIPROC glad_glVertexStream2ivATI;\nPFNGLVERTEXSTREAM2FATIPROC glad_glVertexStream2fATI;\nPFNGLVERTEXSTREAM2FVATIPROC glad_glVertexStream2fvATI;\nPFNGLVERTEXSTREAM2DATIPROC glad_glVertexStream2dATI;\nPFNGLVERTEXSTREAM2DVATIPROC glad_glVertexStream2dvATI;\nPFNGLVERTEXSTREAM3SATIPROC glad_glVertexStream3sATI;\nPFNGLVERTEXSTREAM3SVATIPROC glad_glVertexStream3svATI;\nPFNGLVERTEXSTREAM3IATIPROC glad_glVertexStream3iATI;\nPFNGLVERTEXSTREAM3IVATIPROC glad_glVertexStream3ivATI;\nPFNGLVERTEXSTREAM3FATIPROC glad_glVertexStream3fATI;\nPFNGLVERTEXSTREAM3FVATIPROC glad_glVertexStream3fvATI;\nPFNGLVERTEXSTREAM3DATIPROC glad_glVertexStream3dATI;\nPFNGLVERTEXSTREAM3DVATIPROC glad_glVertexStream3dvATI;\nPFNGLVERTEXSTREAM4SATIPROC glad_glVertexStream4sATI;\nPFNGLVERTEXSTREAM4SVATIPROC glad_glVertexStream4svATI;\nPFNGLVERTEXSTREAM4IATIPROC glad_glVertexStream4iATI;\nPFNGLVERTEXSTREAM4IVATIPROC glad_glVertexStream4ivATI;\nPFNGLVERTEXSTREAM4FATIPROC glad_glVertexStream4fATI;\nPFNGLVERTEXSTREAM4FVATIPROC glad_glVertexStream4fvATI;\nPFNGLVERTEXSTREAM4DATIPROC glad_glVertexStream4dATI;\nPFNGLVERTEXSTREAM4DVATIPROC glad_glVertexStream4dvATI;\nPFNGLNORMALSTREAM3BATIPROC glad_glNormalStream3bATI;\nPFNGLNORMALSTREAM3BVATIPROC glad_glNormalStream3bvATI;\nPFNGLNORMALSTREAM3SATIPROC glad_glNormalStream3sATI;\nPFNGLNORMALSTREAM3SVATIPROC glad_glNormalStream3svATI;\nPFNGLNORMALSTREAM3IATIPROC glad_glNormalStream3iATI;\nPFNGLNORMALSTREAM3IVATIPROC glad_glNormalStream3ivATI;\nPFNGLNORMALSTREAM3FATIPROC glad_glNormalStream3fATI;\nPFNGLNORMALSTREAM3FVATIPROC glad_glNormalStream3fvATI;\nPFNGLNORMALSTREAM3DATIPROC glad_glNormalStream3dATI;\nPFNGLNORMALSTREAM3DVATIPROC glad_glNormalStream3dvATI;\nPFNGLCLIENTACTIVEVERTEXSTREAMATIPROC glad_glClientActiveVertexStreamATI;\nPFNGLVERTEXBLENDENVIATIPROC glad_glVertexBlendEnviATI;\nPFNGLVERTEXBLENDENVFATIPROC glad_glVertexBlendEnvfATI;\nPFNGLVDPAUINITNVPROC glad_glVDPAUInitNV;\nPFNGLVDPAUFININVPROC glad_glVDPAUFiniNV;\nPFNGLVDPAUREGISTERVIDEOSURFACENVPROC glad_glVDPAURegisterVideoSurfaceNV;\nPFNGLVDPAUREGISTEROUTPUTSURFACENVPROC glad_glVDPAURegisterOutputSurfaceNV;\nPFNGLVDPAUISSURFACENVPROC glad_glVDPAUIsSurfaceNV;\nPFNGLVDPAUUNREGISTERSURFACENVPROC glad_glVDPAUUnregisterSurfaceNV;\nPFNGLVDPAUGETSURFACEIVNVPROC glad_glVDPAUGetSurfaceivNV;\nPFNGLVDPAUSURFACEACCESSNVPROC glad_glVDPAUSurfaceAccessNV;\nPFNGLVDPAUMAPSURFACESNVPROC glad_glVDPAUMapSurfacesNV;\nPFNGLVDPAUUNMAPSURFACESNVPROC glad_glVDPAUUnmapSurfacesNV;\nPFNGLCOLOR4UBVERTEX2FSUNPROC glad_glColor4ubVertex2fSUN;\nPFNGLCOLOR4UBVERTEX2FVSUNPROC glad_glColor4ubVertex2fvSUN;\nPFNGLCOLOR4UBVERTEX3FSUNPROC glad_glColor4ubVertex3fSUN;\nPFNGLCOLOR4UBVERTEX3FVSUNPROC glad_glColor4ubVertex3fvSUN;\nPFNGLCOLOR3FVERTEX3FSUNPROC glad_glColor3fVertex3fSUN;\nPFNGLCOLOR3FVERTEX3FVSUNPROC glad_glColor3fVertex3fvSUN;\nPFNGLNORMAL3FVERTEX3FSUNPROC glad_glNormal3fVertex3fSUN;\nPFNGLNORMAL3FVERTEX3FVSUNPROC glad_glNormal3fVertex3fvSUN;\nPFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC glad_glColor4fNormal3fVertex3fSUN;\nPFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC glad_glColor4fNormal3fVertex3fvSUN;\nPFNGLTEXCOORD2FVERTEX3FSUNPROC glad_glTexCoord2fVertex3fSUN;\nPFNGLTEXCOORD2FVERTEX3FVSUNPROC glad_glTexCoord2fVertex3fvSUN;\nPFNGLTEXCOORD4FVERTEX4FSUNPROC glad_glTexCoord4fVertex4fSUN;\nPFNGLTEXCOORD4FVERTEX4FVSUNPROC glad_glTexCoord4fVertex4fvSUN;\nPFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC glad_glTexCoord2fColor4ubVertex3fSUN;\nPFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC glad_glTexCoord2fColor4ubVertex3fvSUN;\nPFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC glad_glTexCoord2fColor3fVertex3fSUN;\nPFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC glad_glTexCoord2fColor3fVertex3fvSUN;\nPFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC glad_glTexCoord2fNormal3fVertex3fSUN;\nPFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC glad_glTexCoord2fNormal3fVertex3fvSUN;\nPFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC glad_glTexCoord2fColor4fNormal3fVertex3fSUN;\nPFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC glad_glTexCoord2fColor4fNormal3fVertex3fvSUN;\nPFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC glad_glTexCoord4fColor4fNormal3fVertex4fSUN;\nPFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC glad_glTexCoord4fColor4fNormal3fVertex4fvSUN;\nPFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC glad_glReplacementCodeuiVertex3fSUN;\nPFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC glad_glReplacementCodeuiVertex3fvSUN;\nPFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC glad_glReplacementCodeuiColor4ubVertex3fSUN;\nPFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC glad_glReplacementCodeuiColor4ubVertex3fvSUN;\nPFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC glad_glReplacementCodeuiColor3fVertex3fSUN;\nPFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC glad_glReplacementCodeuiColor3fVertex3fvSUN;\nPFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC glad_glReplacementCodeuiNormal3fVertex3fSUN;\nPFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC glad_glReplacementCodeuiNormal3fVertex3fvSUN;\nPFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC glad_glReplacementCodeuiColor4fNormal3fVertex3fSUN;\nPFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC glad_glReplacementCodeuiColor4fNormal3fVertex3fvSUN;\nPFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC glad_glReplacementCodeuiTexCoord2fVertex3fSUN;\nPFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC glad_glReplacementCodeuiTexCoord2fVertex3fvSUN;\nPFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC glad_glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN;\nPFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC glad_glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN;\nPFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC glad_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN;\nPFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC glad_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN;\nPFNGLIGLOOINTERFACESGIXPROC glad_glIglooInterfaceSGIX;\nPFNGLVERTEXATTRIBI1IEXTPROC glad_glVertexAttribI1iEXT;\nPFNGLVERTEXATTRIBI2IEXTPROC glad_glVertexAttribI2iEXT;\nPFNGLVERTEXATTRIBI3IEXTPROC glad_glVertexAttribI3iEXT;\nPFNGLVERTEXATTRIBI4IEXTPROC glad_glVertexAttribI4iEXT;\nPFNGLVERTEXATTRIBI1UIEXTPROC glad_glVertexAttribI1uiEXT;\nPFNGLVERTEXATTRIBI2UIEXTPROC glad_glVertexAttribI2uiEXT;\nPFNGLVERTEXATTRIBI3UIEXTPROC glad_glVertexAttribI3uiEXT;\nPFNGLVERTEXATTRIBI4UIEXTPROC glad_glVertexAttribI4uiEXT;\nPFNGLVERTEXATTRIBI1IVEXTPROC glad_glVertexAttribI1ivEXT;\nPFNGLVERTEXATTRIBI2IVEXTPROC glad_glVertexAttribI2ivEXT;\nPFNGLVERTEXATTRIBI3IVEXTPROC glad_glVertexAttribI3ivEXT;\nPFNGLVERTEXATTRIBI4IVEXTPROC glad_glVertexAttribI4ivEXT;\nPFNGLVERTEXATTRIBI1UIVEXTPROC glad_glVertexAttribI1uivEXT;\nPFNGLVERTEXATTRIBI2UIVEXTPROC glad_glVertexAttribI2uivEXT;\nPFNGLVERTEXATTRIBI3UIVEXTPROC glad_glVertexAttribI3uivEXT;\nPFNGLVERTEXATTRIBI4UIVEXTPROC glad_glVertexAttribI4uivEXT;\nPFNGLVERTEXATTRIBI4BVEXTPROC glad_glVertexAttribI4bvEXT;\nPFNGLVERTEXATTRIBI4SVEXTPROC glad_glVertexAttribI4svEXT;\nPFNGLVERTEXATTRIBI4UBVEXTPROC glad_glVertexAttribI4ubvEXT;\nPFNGLVERTEXATTRIBI4USVEXTPROC glad_glVertexAttribI4usvEXT;\nPFNGLVERTEXATTRIBIPOINTEREXTPROC glad_glVertexAttribIPointerEXT;\nPFNGLGETVERTEXATTRIBIIVEXTPROC glad_glGetVertexAttribIivEXT;\nPFNGLGETVERTEXATTRIBIUIVEXTPROC glad_glGetVertexAttribIuivEXT;\nPFNGLFOGFUNCSGISPROC glad_glFogFuncSGIS;\nPFNGLGETFOGFUNCSGISPROC glad_glGetFogFuncSGIS;\nPFNGLIMPORTSYNCEXTPROC glad_glImportSyncEXT;\nPFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC glad_glDispatchComputeGroupSizeARB;\nPFNGLALPHAFUNCXOESPROC glad_glAlphaFuncxOES;\nPFNGLCLEARCOLORXOESPROC glad_glClearColorxOES;\nPFNGLCLEARDEPTHXOESPROC glad_glClearDepthxOES;\nPFNGLCLIPPLANEXOESPROC glad_glClipPlanexOES;\nPFNGLCOLOR4XOESPROC glad_glColor4xOES;\nPFNGLDEPTHRANGEXOESPROC glad_glDepthRangexOES;\nPFNGLFOGXOESPROC glad_glFogxOES;\nPFNGLFOGXVOESPROC glad_glFogxvOES;\nPFNGLFRUSTUMXOESPROC glad_glFrustumxOES;\nPFNGLGETCLIPPLANEXOESPROC glad_glGetClipPlanexOES;\nPFNGLGETFIXEDVOESPROC glad_glGetFixedvOES;\nPFNGLGETTEXENVXVOESPROC glad_glGetTexEnvxvOES;\nPFNGLGETTEXPARAMETERXVOESPROC glad_glGetTexParameterxvOES;\nPFNGLLIGHTMODELXOESPROC glad_glLightModelxOES;\nPFNGLLIGHTMODELXVOESPROC glad_glLightModelxvOES;\nPFNGLLIGHTXOESPROC glad_glLightxOES;\nPFNGLLIGHTXVOESPROC glad_glLightxvOES;\nPFNGLLINEWIDTHXOESPROC glad_glLineWidthxOES;\nPFNGLLOADMATRIXXOESPROC glad_glLoadMatrixxOES;\nPFNGLMATERIALXOESPROC glad_glMaterialxOES;\nPFNGLMATERIALXVOESPROC glad_glMaterialxvOES;\nPFNGLMULTMATRIXXOESPROC glad_glMultMatrixxOES;\nPFNGLMULTITEXCOORD4XOESPROC glad_glMultiTexCoord4xOES;\nPFNGLNORMAL3XOESPROC glad_glNormal3xOES;\nPFNGLORTHOXOESPROC glad_glOrthoxOES;\nPFNGLPOINTPARAMETERXVOESPROC glad_glPointParameterxvOES;\nPFNGLPOINTSIZEXOESPROC glad_glPointSizexOES;\nPFNGLPOLYGONOFFSETXOESPROC glad_glPolygonOffsetxOES;\nPFNGLROTATEXOESPROC glad_glRotatexOES;\nPFNGLSAMPLECOVERAGEOESPROC glad_glSampleCoverageOES;\nPFNGLSCALEXOESPROC glad_glScalexOES;\nPFNGLTEXENVXOESPROC glad_glTexEnvxOES;\nPFNGLTEXENVXVOESPROC glad_glTexEnvxvOES;\nPFNGLTEXPARAMETERXOESPROC glad_glTexParameterxOES;\nPFNGLTEXPARAMETERXVOESPROC glad_glTexParameterxvOES;\nPFNGLTRANSLATEXOESPROC glad_glTranslatexOES;\nPFNGLGETLIGHTXVOESPROC glad_glGetLightxvOES;\nPFNGLGETMATERIALXVOESPROC glad_glGetMaterialxvOES;\nPFNGLPOINTPARAMETERXOESPROC glad_glPointParameterxOES;\nPFNGLSAMPLECOVERAGEXOESPROC glad_glSampleCoveragexOES;\nPFNGLACCUMXOESPROC glad_glAccumxOES;\nPFNGLBITMAPXOESPROC glad_glBitmapxOES;\nPFNGLBLENDCOLORXOESPROC glad_glBlendColorxOES;\nPFNGLCLEARACCUMXOESPROC glad_glClearAccumxOES;\nPFNGLCOLOR3XOESPROC glad_glColor3xOES;\nPFNGLCOLOR3XVOESPROC glad_glColor3xvOES;\nPFNGLCOLOR4XVOESPROC glad_glColor4xvOES;\nPFNGLCONVOLUTIONPARAMETERXOESPROC glad_glConvolutionParameterxOES;\nPFNGLCONVOLUTIONPARAMETERXVOESPROC glad_glConvolutionParameterxvOES;\nPFNGLEVALCOORD1XOESPROC glad_glEvalCoord1xOES;\nPFNGLEVALCOORD1XVOESPROC glad_glEvalCoord1xvOES;\nPFNGLEVALCOORD2XOESPROC glad_glEvalCoord2xOES;\nPFNGLEVALCOORD2XVOESPROC glad_glEvalCoord2xvOES;\nPFNGLFEEDBACKBUFFERXOESPROC glad_glFeedbackBufferxOES;\nPFNGLGETCONVOLUTIONPARAMETERXVOESPROC glad_glGetConvolutionParameterxvOES;\nPFNGLGETHISTOGRAMPARAMETERXVOESPROC glad_glGetHistogramParameterxvOES;\nPFNGLGETLIGHTXOESPROC glad_glGetLightxOES;\nPFNGLGETMAPXVOESPROC glad_glGetMapxvOES;\nPFNGLGETMATERIALXOESPROC glad_glGetMaterialxOES;\nPFNGLGETPIXELMAPXVPROC glad_glGetPixelMapxv;\nPFNGLGETTEXGENXVOESPROC glad_glGetTexGenxvOES;\nPFNGLGETTEXLEVELPARAMETERXVOESPROC glad_glGetTexLevelParameterxvOES;\nPFNGLINDEXXOESPROC glad_glIndexxOES;\nPFNGLINDEXXVOESPROC glad_glIndexxvOES;\nPFNGLLOADTRANSPOSEMATRIXXOESPROC glad_glLoadTransposeMatrixxOES;\nPFNGLMAP1XOESPROC glad_glMap1xOES;\nPFNGLMAP2XOESPROC glad_glMap2xOES;\nPFNGLMAPGRID1XOESPROC glad_glMapGrid1xOES;\nPFNGLMAPGRID2XOESPROC glad_glMapGrid2xOES;\nPFNGLMULTTRANSPOSEMATRIXXOESPROC glad_glMultTransposeMatrixxOES;\nPFNGLMULTITEXCOORD1XOESPROC glad_glMultiTexCoord1xOES;\nPFNGLMULTITEXCOORD1XVOESPROC glad_glMultiTexCoord1xvOES;\nPFNGLMULTITEXCOORD2XOESPROC glad_glMultiTexCoord2xOES;\nPFNGLMULTITEXCOORD2XVOESPROC glad_glMultiTexCoord2xvOES;\nPFNGLMULTITEXCOORD3XOESPROC glad_glMultiTexCoord3xOES;\nPFNGLMULTITEXCOORD3XVOESPROC glad_glMultiTexCoord3xvOES;\nPFNGLMULTITEXCOORD4XVOESPROC glad_glMultiTexCoord4xvOES;\nPFNGLNORMAL3XVOESPROC glad_glNormal3xvOES;\nPFNGLPASSTHROUGHXOESPROC glad_glPassThroughxOES;\nPFNGLPIXELMAPXPROC glad_glPixelMapx;\nPFNGLPIXELSTOREXPROC glad_glPixelStorex;\nPFNGLPIXELTRANSFERXOESPROC glad_glPixelTransferxOES;\nPFNGLPIXELZOOMXOESPROC glad_glPixelZoomxOES;\nPFNGLPRIORITIZETEXTURESXOESPROC glad_glPrioritizeTexturesxOES;\nPFNGLRASTERPOS2XOESPROC glad_glRasterPos2xOES;\nPFNGLRASTERPOS2XVOESPROC glad_glRasterPos2xvOES;\nPFNGLRASTERPOS3XOESPROC glad_glRasterPos3xOES;\nPFNGLRASTERPOS3XVOESPROC glad_glRasterPos3xvOES;\nPFNGLRASTERPOS4XOESPROC glad_glRasterPos4xOES;\nPFNGLRASTERPOS4XVOESPROC glad_glRasterPos4xvOES;\nPFNGLRECTXOESPROC glad_glRectxOES;\nPFNGLRECTXVOESPROC glad_glRectxvOES;\nPFNGLTEXCOORD1XOESPROC glad_glTexCoord1xOES;\nPFNGLTEXCOORD1XVOESPROC glad_glTexCoord1xvOES;\nPFNGLTEXCOORD2XOESPROC glad_glTexCoord2xOES;\nPFNGLTEXCOORD2XVOESPROC glad_glTexCoord2xvOES;\nPFNGLTEXCOORD3XOESPROC glad_glTexCoord3xOES;\nPFNGLTEXCOORD3XVOESPROC glad_glTexCoord3xvOES;\nPFNGLTEXCOORD4XOESPROC glad_glTexCoord4xOES;\nPFNGLTEXCOORD4XVOESPROC glad_glTexCoord4xvOES;\nPFNGLTEXGENXOESPROC glad_glTexGenxOES;\nPFNGLTEXGENXVOESPROC glad_glTexGenxvOES;\nPFNGLVERTEX2XOESPROC glad_glVertex2xOES;\nPFNGLVERTEX2XVOESPROC glad_glVertex2xvOES;\nPFNGLVERTEX3XOESPROC glad_glVertex3xOES;\nPFNGLVERTEX3XVOESPROC glad_glVertex3xvOES;\nPFNGLVERTEX4XOESPROC glad_glVertex4xOES;\nPFNGLVERTEX4XVOESPROC glad_glVertex4xvOES;\nPFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT;\nPFNGLTEXIMAGE4DSGISPROC glad_glTexImage4DSGIS;\nPFNGLTEXSUBIMAGE4DSGISPROC glad_glTexSubImage4DSGIS;\nPFNGLTEXIMAGE3DEXTPROC glad_glTexImage3DEXT;\nPFNGLTEXSUBIMAGE3DEXTPROC glad_glTexSubImage3DEXT;\nPFNGLSAMPLEMASKEXTPROC glad_glSampleMaskEXT;\nPFNGLSAMPLEPATTERNEXTPROC glad_glSamplePatternEXT;\nPFNGLSECONDARYCOLOR3BEXTPROC glad_glSecondaryColor3bEXT;\nPFNGLSECONDARYCOLOR3BVEXTPROC glad_glSecondaryColor3bvEXT;\nPFNGLSECONDARYCOLOR3DEXTPROC glad_glSecondaryColor3dEXT;\nPFNGLSECONDARYCOLOR3DVEXTPROC glad_glSecondaryColor3dvEXT;\nPFNGLSECONDARYCOLOR3FEXTPROC glad_glSecondaryColor3fEXT;\nPFNGLSECONDARYCOLOR3FVEXTPROC glad_glSecondaryColor3fvEXT;\nPFNGLSECONDARYCOLOR3IEXTPROC glad_glSecondaryColor3iEXT;\nPFNGLSECONDARYCOLOR3IVEXTPROC glad_glSecondaryColor3ivEXT;\nPFNGLSECONDARYCOLOR3SEXTPROC glad_glSecondaryColor3sEXT;\nPFNGLSECONDARYCOLOR3SVEXTPROC glad_glSecondaryColor3svEXT;\nPFNGLSECONDARYCOLOR3UBEXTPROC glad_glSecondaryColor3ubEXT;\nPFNGLSECONDARYCOLOR3UBVEXTPROC glad_glSecondaryColor3ubvEXT;\nPFNGLSECONDARYCOLOR3UIEXTPROC glad_glSecondaryColor3uiEXT;\nPFNGLSECONDARYCOLOR3UIVEXTPROC glad_glSecondaryColor3uivEXT;\nPFNGLSECONDARYCOLOR3USEXTPROC glad_glSecondaryColor3usEXT;\nPFNGLSECONDARYCOLOR3USVEXTPROC glad_glSecondaryColor3usvEXT;\nPFNGLSECONDARYCOLORPOINTEREXTPROC glad_glSecondaryColorPointerEXT;\nPFNGLNEWOBJECTBUFFERATIPROC glad_glNewObjectBufferATI;\nPFNGLISOBJECTBUFFERATIPROC glad_glIsObjectBufferATI;\nPFNGLUPDATEOBJECTBUFFERATIPROC glad_glUpdateObjectBufferATI;\nPFNGLGETOBJECTBUFFERFVATIPROC glad_glGetObjectBufferfvATI;\nPFNGLGETOBJECTBUFFERIVATIPROC glad_glGetObjectBufferivATI;\nPFNGLFREEOBJECTBUFFERATIPROC glad_glFreeObjectBufferATI;\nPFNGLARRAYOBJECTATIPROC glad_glArrayObjectATI;\nPFNGLGETARRAYOBJECTFVATIPROC glad_glGetArrayObjectfvATI;\nPFNGLGETARRAYOBJECTIVATIPROC glad_glGetArrayObjectivATI;\nPFNGLVARIANTARRAYOBJECTATIPROC glad_glVariantArrayObjectATI;\nPFNGLGETVARIANTARRAYOBJECTFVATIPROC glad_glGetVariantArrayObjectfvATI;\nPFNGLGETVARIANTARRAYOBJECTIVATIPROC glad_glGetVariantArrayObjectivATI;\nPFNGLTEXPAGECOMMITMENTARBPROC glad_glTexPageCommitmentARB;\nPFNGLDRAWRANGEELEMENTSEXTPROC glad_glDrawRangeElementsEXT;\nstatic void load_GL_VERSION_1_0(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_0) return;\n\tglad_glCullFace = (PFNGLCULLFACEPROC)load(\"glCullFace\");\n\tglad_glFrontFace = (PFNGLFRONTFACEPROC)load(\"glFrontFace\");\n\tglad_glHint = (PFNGLHINTPROC)load(\"glHint\");\n\tglad_glLineWidth = (PFNGLLINEWIDTHPROC)load(\"glLineWidth\");\n\tglad_glPointSize = (PFNGLPOINTSIZEPROC)load(\"glPointSize\");\n\tglad_glPolygonMode = (PFNGLPOLYGONMODEPROC)load(\"glPolygonMode\");\n\tglad_glScissor = (PFNGLSCISSORPROC)load(\"glScissor\");\n\tglad_glTexParameterf = (PFNGLTEXPARAMETERFPROC)load(\"glTexParameterf\");\n\tglad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC)load(\"glTexParameterfv\");\n\tglad_glTexParameteri = (PFNGLTEXPARAMETERIPROC)load(\"glTexParameteri\");\n\tglad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC)load(\"glTexParameteriv\");\n\tglad_glTexImage1D = (PFNGLTEXIMAGE1DPROC)load(\"glTexImage1D\");\n\tglad_glTexImage2D = (PFNGLTEXIMAGE2DPROC)load(\"glTexImage2D\");\n\tglad_glDrawBuffer = (PFNGLDRAWBUFFERPROC)load(\"glDrawBuffer\");\n\tglad_glClear = (PFNGLCLEARPROC)load(\"glClear\");\n\tglad_glClearColor = (PFNGLCLEARCOLORPROC)load(\"glClearColor\");\n\tglad_glClearStencil = (PFNGLCLEARSTENCILPROC)load(\"glClearStencil\");\n\tglad_glClearDepth = (PFNGLCLEARDEPTHPROC)load(\"glClearDepth\");\n\tglad_glStencilMask = (PFNGLSTENCILMASKPROC)load(\"glStencilMask\");\n\tglad_glColorMask = (PFNGLCOLORMASKPROC)load(\"glColorMask\");\n\tglad_glDepthMask = (PFNGLDEPTHMASKPROC)load(\"glDepthMask\");\n\tglad_glDisable = (PFNGLDISABLEPROC)load(\"glDisable\");\n\tglad_glEnable = (PFNGLENABLEPROC)load(\"glEnable\");\n\tglad_glFinish = (PFNGLFINISHPROC)load(\"glFinish\");\n\tglad_glFlush = (PFNGLFLUSHPROC)load(\"glFlush\");\n\tglad_glBlendFunc = (PFNGLBLENDFUNCPROC)load(\"glBlendFunc\");\n\tglad_glLogicOp = (PFNGLLOGICOPPROC)load(\"glLogicOp\");\n\tglad_glStencilFunc = (PFNGLSTENCILFUNCPROC)load(\"glStencilFunc\");\n\tglad_glStencilOp = (PFNGLSTENCILOPPROC)load(\"glStencilOp\");\n\tglad_glDepthFunc = (PFNGLDEPTHFUNCPROC)load(\"glDepthFunc\");\n\tglad_glPixelStoref = (PFNGLPIXELSTOREFPROC)load(\"glPixelStoref\");\n\tglad_glPixelStorei = (PFNGLPIXELSTOREIPROC)load(\"glPixelStorei\");\n\tglad_glReadBuffer = (PFNGLREADBUFFERPROC)load(\"glReadBuffer\");\n\tglad_glReadPixels = (PFNGLREADPIXELSPROC)load(\"glReadPixels\");\n\tglad_glGetBooleanv = (PFNGLGETBOOLEANVPROC)load(\"glGetBooleanv\");\n\tglad_glGetDoublev = (PFNGLGETDOUBLEVPROC)load(\"glGetDoublev\");\n\tglad_glGetError = (PFNGLGETERRORPROC)load(\"glGetError\");\n\tglad_glGetFloatv = (PFNGLGETFLOATVPROC)load(\"glGetFloatv\");\n\tglad_glGetIntegerv = (PFNGLGETINTEGERVPROC)load(\"glGetIntegerv\");\n\tglad_glGetString = (PFNGLGETSTRINGPROC)load(\"glGetString\");\n\tglad_glGetTexImage = (PFNGLGETTEXIMAGEPROC)load(\"glGetTexImage\");\n\tglad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC)load(\"glGetTexParameterfv\");\n\tglad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC)load(\"glGetTexParameteriv\");\n\tglad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC)load(\"glGetTexLevelParameterfv\");\n\tglad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC)load(\"glGetTexLevelParameteriv\");\n\tglad_glIsEnabled = (PFNGLISENABLEDPROC)load(\"glIsEnabled\");\n\tglad_glDepthRange = (PFNGLDEPTHRANGEPROC)load(\"glDepthRange\");\n\tglad_glViewport = (PFNGLVIEWPORTPROC)load(\"glViewport\");\n}\nstatic void load_GL_VERSION_1_1(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_1) return;\n\tglad_glDrawArrays = (PFNGLDRAWARRAYSPROC)load(\"glDrawArrays\");\n\tglad_glDrawElements = (PFNGLDRAWELEMENTSPROC)load(\"glDrawElements\");\n\tglad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC)load(\"glPolygonOffset\");\n\tglad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC)load(\"glCopyTexImage1D\");\n\tglad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC)load(\"glCopyTexImage2D\");\n\tglad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC)load(\"glCopyTexSubImage1D\");\n\tglad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC)load(\"glCopyTexSubImage2D\");\n\tglad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC)load(\"glTexSubImage1D\");\n\tglad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC)load(\"glTexSubImage2D\");\n\tglad_glBindTexture = (PFNGLBINDTEXTUREPROC)load(\"glBindTexture\");\n\tglad_glDeleteTextures = (PFNGLDELETETEXTURESPROC)load(\"glDeleteTextures\");\n\tglad_glGenTextures = (PFNGLGENTEXTURESPROC)load(\"glGenTextures\");\n\tglad_glIsTexture = (PFNGLISTEXTUREPROC)load(\"glIsTexture\");\n}\nstatic void load_GL_VERSION_1_2(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_2) return;\n\tglad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC)load(\"glDrawRangeElements\");\n\tglad_glTexImage3D = (PFNGLTEXIMAGE3DPROC)load(\"glTexImage3D\");\n\tglad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)load(\"glTexSubImage3D\");\n\tglad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC)load(\"glCopyTexSubImage3D\");\n}\nstatic void load_GL_VERSION_1_3(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_3) return;\n\tglad_glActiveTexture = (PFNGLACTIVETEXTUREPROC)load(\"glActiveTexture\");\n\tglad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC)load(\"glSampleCoverage\");\n\tglad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)load(\"glCompressedTexImage3D\");\n\tglad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)load(\"glCompressedTexImage2D\");\n\tglad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)load(\"glCompressedTexImage1D\");\n\tglad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)load(\"glCompressedTexSubImage3D\");\n\tglad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)load(\"glCompressedTexSubImage2D\");\n\tglad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)load(\"glCompressedTexSubImage1D\");\n\tglad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)load(\"glGetCompressedTexImage\");\n}\nstatic void load_GL_VERSION_1_4(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_4) return;\n\tglad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)load(\"glBlendFuncSeparate\");\n\tglad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC)load(\"glMultiDrawArrays\");\n\tglad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC)load(\"glMultiDrawElements\");\n\tglad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC)load(\"glPointParameterf\");\n\tglad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC)load(\"glPointParameterfv\");\n\tglad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC)load(\"glPointParameteri\");\n\tglad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC)load(\"glPointParameteriv\");\n\tglad_glBlendColor = (PFNGLBLENDCOLORPROC)load(\"glBlendColor\");\n\tglad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load(\"glBlendEquation\");\n}\nstatic void load_GL_VERSION_1_5(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_1_5) return;\n\tglad_glGenQueries = (PFNGLGENQUERIESPROC)load(\"glGenQueries\");\n\tglad_glDeleteQueries = (PFNGLDELETEQUERIESPROC)load(\"glDeleteQueries\");\n\tglad_glIsQuery = (PFNGLISQUERYPROC)load(\"glIsQuery\");\n\tglad_glBeginQuery = (PFNGLBEGINQUERYPROC)load(\"glBeginQuery\");\n\tglad_glEndQuery = (PFNGLENDQUERYPROC)load(\"glEndQuery\");\n\tglad_glGetQueryiv = (PFNGLGETQUERYIVPROC)load(\"glGetQueryiv\");\n\tglad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)load(\"glGetQueryObjectiv\");\n\tglad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)load(\"glGetQueryObjectuiv\");\n\tglad_glBindBuffer = (PFNGLBINDBUFFERPROC)load(\"glBindBuffer\");\n\tglad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)load(\"glDeleteBuffers\");\n\tglad_glGenBuffers = (PFNGLGENBUFFERSPROC)load(\"glGenBuffers\");\n\tglad_glIsBuffer = (PFNGLISBUFFERPROC)load(\"glIsBuffer\");\n\tglad_glBufferData = (PFNGLBUFFERDATAPROC)load(\"glBufferData\");\n\tglad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC)load(\"glBufferSubData\");\n\tglad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC)load(\"glGetBufferSubData\");\n\tglad_glMapBuffer = (PFNGLMAPBUFFERPROC)load(\"glMapBuffer\");\n\tglad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)load(\"glUnmapBuffer\");\n\tglad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC)load(\"glGetBufferParameteriv\");\n\tglad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC)load(\"glGetBufferPointerv\");\n}\nstatic void load_GL_VERSION_2_0(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_2_0) return;\n\tglad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC)load(\"glBlendEquationSeparate\");\n\tglad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC)load(\"glDrawBuffers\");\n\tglad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC)load(\"glStencilOpSeparate\");\n\tglad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC)load(\"glStencilFuncSeparate\");\n\tglad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC)load(\"glStencilMaskSeparate\");\n\tglad_glAttachShader = (PFNGLATTACHSHADERPROC)load(\"glAttachShader\");\n\tglad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)load(\"glBindAttribLocation\");\n\tglad_glCompileShader = (PFNGLCOMPILESHADERPROC)load(\"glCompileShader\");\n\tglad_glCreateProgram = (PFNGLCREATEPROGRAMPROC)load(\"glCreateProgram\");\n\tglad_glCreateShader = (PFNGLCREATESHADERPROC)load(\"glCreateShader\");\n\tglad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC)load(\"glDeleteProgram\");\n\tglad_glDeleteShader = (PFNGLDELETESHADERPROC)load(\"glDeleteShader\");\n\tglad_glDetachShader = (PFNGLDETACHSHADERPROC)load(\"glDetachShader\");\n\tglad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)load(\"glDisableVertexAttribArray\");\n\tglad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)load(\"glEnableVertexAttribArray\");\n\tglad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)load(\"glGetActiveAttrib\");\n\tglad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)load(\"glGetActiveUniform\");\n\tglad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC)load(\"glGetAttachedShaders\");\n\tglad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)load(\"glGetAttribLocation\");\n\tglad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC)load(\"glGetProgramiv\");\n\tglad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)load(\"glGetProgramInfoLog\");\n\tglad_glGetShaderiv = (PFNGLGETSHADERIVPROC)load(\"glGetShaderiv\");\n\tglad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)load(\"glGetShaderInfoLog\");\n\tglad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC)load(\"glGetShaderSource\");\n\tglad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)load(\"glGetUniformLocation\");\n\tglad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC)load(\"glGetUniformfv\");\n\tglad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC)load(\"glGetUniformiv\");\n\tglad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC)load(\"glGetVertexAttribdv\");\n\tglad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC)load(\"glGetVertexAttribfv\");\n\tglad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC)load(\"glGetVertexAttribiv\");\n\tglad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC)load(\"glGetVertexAttribPointerv\");\n\tglad_glIsProgram = (PFNGLISPROGRAMPROC)load(\"glIsProgram\");\n\tglad_glIsShader = (PFNGLISSHADERPROC)load(\"glIsShader\");\n\tglad_glLinkProgram = (PFNGLLINKPROGRAMPROC)load(\"glLinkProgram\");\n\tglad_glShaderSource = (PFNGLSHADERSOURCEPROC)load(\"glShaderSource\");\n\tglad_glUseProgram = (PFNGLUSEPROGRAMPROC)load(\"glUseProgram\");\n\tglad_glUniform1f = (PFNGLUNIFORM1FPROC)load(\"glUniform1f\");\n\tglad_glUniform2f = (PFNGLUNIFORM2FPROC)load(\"glUniform2f\");\n\tglad_glUniform3f = (PFNGLUNIFORM3FPROC)load(\"glUniform3f\");\n\tglad_glUniform4f = (PFNGLUNIFORM4FPROC)load(\"glUniform4f\");\n\tglad_glUniform1i = (PFNGLUNIFORM1IPROC)load(\"glUniform1i\");\n\tglad_glUniform2i = (PFNGLUNIFORM2IPROC)load(\"glUniform2i\");\n\tglad_glUniform3i = (PFNGLUNIFORM3IPROC)load(\"glUniform3i\");\n\tglad_glUniform4i = (PFNGLUNIFORM4IPROC)load(\"glUniform4i\");\n\tglad_glUniform1fv = (PFNGLUNIFORM1FVPROC)load(\"glUniform1fv\");\n\tglad_glUniform2fv = (PFNGLUNIFORM2FVPROC)load(\"glUniform2fv\");\n\tglad_glUniform3fv = (PFNGLUNIFORM3FVPROC)load(\"glUniform3fv\");\n\tglad_glUniform4fv = (PFNGLUNIFORM4FVPROC)load(\"glUniform4fv\");\n\tglad_glUniform1iv = (PFNGLUNIFORM1IVPROC)load(\"glUniform1iv\");\n\tglad_glUniform2iv = (PFNGLUNIFORM2IVPROC)load(\"glUniform2iv\");\n\tglad_glUniform3iv = (PFNGLUNIFORM3IVPROC)load(\"glUniform3iv\");\n\tglad_glUniform4iv = (PFNGLUNIFORM4IVPROC)load(\"glUniform4iv\");\n\tglad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)load(\"glUniformMatrix2fv\");\n\tglad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)load(\"glUniformMatrix3fv\");\n\tglad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)load(\"glUniformMatrix4fv\");\n\tglad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)load(\"glValidateProgram\");\n\tglad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC)load(\"glVertexAttrib1d\");\n\tglad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC)load(\"glVertexAttrib1dv\");\n\tglad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)load(\"glVertexAttrib1f\");\n\tglad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC)load(\"glVertexAttrib1fv\");\n\tglad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC)load(\"glVertexAttrib1s\");\n\tglad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC)load(\"glVertexAttrib1sv\");\n\tglad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC)load(\"glVertexAttrib2d\");\n\tglad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC)load(\"glVertexAttrib2dv\");\n\tglad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)load(\"glVertexAttrib2f\");\n\tglad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)load(\"glVertexAttrib2fv\");\n\tglad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC)load(\"glVertexAttrib2s\");\n\tglad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC)load(\"glVertexAttrib2sv\");\n\tglad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC)load(\"glVertexAttrib3d\");\n\tglad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC)load(\"glVertexAttrib3dv\");\n\tglad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)load(\"glVertexAttrib3f\");\n\tglad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)load(\"glVertexAttrib3fv\");\n\tglad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC)load(\"glVertexAttrib3s\");\n\tglad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC)load(\"glVertexAttrib3sv\");\n\tglad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC)load(\"glVertexAttrib4Nbv\");\n\tglad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC)load(\"glVertexAttrib4Niv\");\n\tglad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC)load(\"glVertexAttrib4Nsv\");\n\tglad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC)load(\"glVertexAttrib4Nub\");\n\tglad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC)load(\"glVertexAttrib4Nubv\");\n\tglad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC)load(\"glVertexAttrib4Nuiv\");\n\tglad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC)load(\"glVertexAttrib4Nusv\");\n\tglad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC)load(\"glVertexAttrib4bv\");\n\tglad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC)load(\"glVertexAttrib4d\");\n\tglad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC)load(\"glVertexAttrib4dv\");\n\tglad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)load(\"glVertexAttrib4f\");\n\tglad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)load(\"glVertexAttrib4fv\");\n\tglad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC)load(\"glVertexAttrib4iv\");\n\tglad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC)load(\"glVertexAttrib4s\");\n\tglad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC)load(\"glVertexAttrib4sv\");\n\tglad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC)load(\"glVertexAttrib4ubv\");\n\tglad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC)load(\"glVertexAttrib4uiv\");\n\tglad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC)load(\"glVertexAttrib4usv\");\n\tglad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)load(\"glVertexAttribPointer\");\n}\nstatic void load_GL_VERSION_2_1(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_2_1) return;\n\tglad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)load(\"glUniformMatrix2x3fv\");\n\tglad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)load(\"glUniformMatrix3x2fv\");\n\tglad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)load(\"glUniformMatrix2x4fv\");\n\tglad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)load(\"glUniformMatrix4x2fv\");\n\tglad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)load(\"glUniformMatrix3x4fv\");\n\tglad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)load(\"glUniformMatrix4x3fv\");\n}\nstatic void load_GL_VERSION_3_0(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_3_0) return;\n\tglad_glColorMaski = (PFNGLCOLORMASKIPROC)load(\"glColorMaski\");\n\tglad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC)load(\"glGetBooleani_v\");\n\tglad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC)load(\"glGetIntegeri_v\");\n\tglad_glEnablei = (PFNGLENABLEIPROC)load(\"glEnablei\");\n\tglad_glDisablei = (PFNGLDISABLEIPROC)load(\"glDisablei\");\n\tglad_glIsEnabledi = (PFNGLISENABLEDIPROC)load(\"glIsEnabledi\");\n\tglad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)load(\"glBeginTransformFeedback\");\n\tglad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)load(\"glEndTransformFeedback\");\n\tglad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)load(\"glBindBufferRange\");\n\tglad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)load(\"glBindBufferBase\");\n\tglad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)load(\"glTransformFeedbackVaryings\");\n\tglad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)load(\"glGetTransformFeedbackVarying\");\n\tglad_glClampColor = (PFNGLCLAMPCOLORPROC)load(\"glClampColor\");\n\tglad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC)load(\"glBeginConditionalRender\");\n\tglad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC)load(\"glEndConditionalRender\");\n\tglad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)load(\"glVertexAttribIPointer\");\n\tglad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC)load(\"glGetVertexAttribIiv\");\n\tglad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC)load(\"glGetVertexAttribIuiv\");\n\tglad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC)load(\"glVertexAttribI1i\");\n\tglad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC)load(\"glVertexAttribI2i\");\n\tglad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC)load(\"glVertexAttribI3i\");\n\tglad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC)load(\"glVertexAttribI4i\");\n\tglad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC)load(\"glVertexAttribI1ui\");\n\tglad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC)load(\"glVertexAttribI2ui\");\n\tglad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC)load(\"glVertexAttribI3ui\");\n\tglad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC)load(\"glVertexAttribI4ui\");\n\tglad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC)load(\"glVertexAttribI1iv\");\n\tglad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC)load(\"glVertexAttribI2iv\");\n\tglad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC)load(\"glVertexAttribI3iv\");\n\tglad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC)load(\"glVertexAttribI4iv\");\n\tglad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC)load(\"glVertexAttribI1uiv\");\n\tglad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC)load(\"glVertexAttribI2uiv\");\n\tglad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC)load(\"glVertexAttribI3uiv\");\n\tglad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC)load(\"glVertexAttribI4uiv\");\n\tglad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC)load(\"glVertexAttribI4bv\");\n\tglad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC)load(\"glVertexAttribI4sv\");\n\tglad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC)load(\"glVertexAttribI4ubv\");\n\tglad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC)load(\"glVertexAttribI4usv\");\n\tglad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC)load(\"glGetUniformuiv\");\n\tglad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC)load(\"glBindFragDataLocation\");\n\tglad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC)load(\"glGetFragDataLocation\");\n\tglad_glUniform1ui = (PFNGLUNIFORM1UIPROC)load(\"glUniform1ui\");\n\tglad_glUniform2ui = (PFNGLUNIFORM2UIPROC)load(\"glUniform2ui\");\n\tglad_glUniform3ui = (PFNGLUNIFORM3UIPROC)load(\"glUniform3ui\");\n\tglad_glUniform4ui = (PFNGLUNIFORM4UIPROC)load(\"glUniform4ui\");\n\tglad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC)load(\"glUniform1uiv\");\n\tglad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC)load(\"glUniform2uiv\");\n\tglad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC)load(\"glUniform3uiv\");\n\tglad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC)load(\"glUniform4uiv\");\n\tglad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC)load(\"glTexParameterIiv\");\n\tglad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC)load(\"glTexParameterIuiv\");\n\tglad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC)load(\"glGetTexParameterIiv\");\n\tglad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC)load(\"glGetTexParameterIuiv\");\n\tglad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC)load(\"glClearBufferiv\");\n\tglad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC)load(\"glClearBufferuiv\");\n\tglad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC)load(\"glClearBufferfv\");\n\tglad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC)load(\"glClearBufferfi\");\n\tglad_glGetStringi = (PFNGLGETSTRINGIPROC)load(\"glGetStringi\");\n\tglad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load(\"glIsRenderbuffer\");\n\tglad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load(\"glBindRenderbuffer\");\n\tglad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load(\"glDeleteRenderbuffers\");\n\tglad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load(\"glGenRenderbuffers\");\n\tglad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load(\"glRenderbufferStorage\");\n\tglad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load(\"glGetRenderbufferParameteriv\");\n\tglad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load(\"glIsFramebuffer\");\n\tglad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load(\"glBindFramebuffer\");\n\tglad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load(\"glDeleteFramebuffers\");\n\tglad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load(\"glGenFramebuffers\");\n\tglad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load(\"glCheckFramebufferStatus\");\n\tglad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)load(\"glFramebufferTexture1D\");\n\tglad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load(\"glFramebufferTexture2D\");\n\tglad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)load(\"glFramebufferTexture3D\");\n\tglad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load(\"glFramebufferRenderbuffer\");\n\tglad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load(\"glGetFramebufferAttachmentParameteriv\");\n\tglad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load(\"glGenerateMipmap\");\n\tglad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)load(\"glBlitFramebuffer\");\n\tglad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load(\"glRenderbufferStorageMultisample\");\n\tglad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load(\"glFramebufferTextureLayer\");\n\tglad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)load(\"glMapBufferRange\");\n\tglad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)load(\"glFlushMappedBufferRange\");\n\tglad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)load(\"glBindVertexArray\");\n\tglad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)load(\"glDeleteVertexArrays\");\n\tglad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)load(\"glGenVertexArrays\");\n\tglad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC)load(\"glIsVertexArray\");\n}\nstatic void load_GL_VERSION_3_1(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_3_1) return;\n\tglad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)load(\"glDrawArraysInstanced\");\n\tglad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)load(\"glDrawElementsInstanced\");\n\tglad_glTexBuffer = (PFNGLTEXBUFFERPROC)load(\"glTexBuffer\");\n\tglad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC)load(\"glPrimitiveRestartIndex\");\n\tglad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load(\"glCopyBufferSubData\");\n\tglad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load(\"glGetUniformIndices\");\n\tglad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load(\"glGetActiveUniformsiv\");\n\tglad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)load(\"glGetActiveUniformName\");\n\tglad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load(\"glGetUniformBlockIndex\");\n\tglad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load(\"glGetActiveUniformBlockiv\");\n\tglad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load(\"glGetActiveUniformBlockName\");\n\tglad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load(\"glUniformBlockBinding\");\n}\nstatic void load_GL_VERSION_3_2(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_3_2) return;\n\tglad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)load(\"glDrawElementsBaseVertex\");\n\tglad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)load(\"glDrawRangeElementsBaseVertex\");\n\tglad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)load(\"glDrawElementsInstancedBaseVertex\");\n\tglad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)load(\"glMultiDrawElementsBaseVertex\");\n\tglad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC)load(\"glProvokingVertex\");\n\tglad_glFenceSync = (PFNGLFENCESYNCPROC)load(\"glFenceSync\");\n\tglad_glIsSync = (PFNGLISSYNCPROC)load(\"glIsSync\");\n\tglad_glDeleteSync = (PFNGLDELETESYNCPROC)load(\"glDeleteSync\");\n\tglad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)load(\"glClientWaitSync\");\n\tglad_glWaitSync = (PFNGLWAITSYNCPROC)load(\"glWaitSync\");\n\tglad_glGetInteger64v = (PFNGLGETINTEGER64VPROC)load(\"glGetInteger64v\");\n\tglad_glGetSynciv = (PFNGLGETSYNCIVPROC)load(\"glGetSynciv\");\n\tglad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC)load(\"glGetInteger64i_v\");\n\tglad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC)load(\"glGetBufferParameteri64v\");\n\tglad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)load(\"glFramebufferTexture\");\n\tglad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)load(\"glTexImage2DMultisample\");\n\tglad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC)load(\"glTexImage3DMultisample\");\n\tglad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)load(\"glGetMultisamplefv\");\n\tglad_glSampleMaski = (PFNGLSAMPLEMASKIPROC)load(\"glSampleMaski\");\n}\nstatic void load_GL_VERSION_3_3(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_3_3) return;\n\tglad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)load(\"glBindFragDataLocationIndexed\");\n\tglad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)load(\"glGetFragDataIndex\");\n\tglad_glGenSamplers = (PFNGLGENSAMPLERSPROC)load(\"glGenSamplers\");\n\tglad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)load(\"glDeleteSamplers\");\n\tglad_glIsSampler = (PFNGLISSAMPLERPROC)load(\"glIsSampler\");\n\tglad_glBindSampler = (PFNGLBINDSAMPLERPROC)load(\"glBindSampler\");\n\tglad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)load(\"glSamplerParameteri\");\n\tglad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)load(\"glSamplerParameteriv\");\n\tglad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)load(\"glSamplerParameterf\");\n\tglad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)load(\"glSamplerParameterfv\");\n\tglad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)load(\"glSamplerParameterIiv\");\n\tglad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)load(\"glSamplerParameterIuiv\");\n\tglad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)load(\"glGetSamplerParameteriv\");\n\tglad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)load(\"glGetSamplerParameterIiv\");\n\tglad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)load(\"glGetSamplerParameterfv\");\n\tglad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)load(\"glGetSamplerParameterIuiv\");\n\tglad_glQueryCounter = (PFNGLQUERYCOUNTERPROC)load(\"glQueryCounter\");\n\tglad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)load(\"glGetQueryObjecti64v\");\n\tglad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)load(\"glGetQueryObjectui64v\");\n\tglad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)load(\"glVertexAttribDivisor\");\n\tglad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC)load(\"glVertexAttribP1ui\");\n\tglad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC)load(\"glVertexAttribP1uiv\");\n\tglad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC)load(\"glVertexAttribP2ui\");\n\tglad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC)load(\"glVertexAttribP2uiv\");\n\tglad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC)load(\"glVertexAttribP3ui\");\n\tglad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC)load(\"glVertexAttribP3uiv\");\n\tglad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC)load(\"glVertexAttribP4ui\");\n\tglad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC)load(\"glVertexAttribP4uiv\");\n\tglad_glVertexP2ui = (PFNGLVERTEXP2UIPROC)load(\"glVertexP2ui\");\n\tglad_glVertexP2uiv = (PFNGLVERTEXP2UIVPROC)load(\"glVertexP2uiv\");\n\tglad_glVertexP3ui = (PFNGLVERTEXP3UIPROC)load(\"glVertexP3ui\");\n\tglad_glVertexP3uiv = (PFNGLVERTEXP3UIVPROC)load(\"glVertexP3uiv\");\n\tglad_glVertexP4ui = (PFNGLVERTEXP4UIPROC)load(\"glVertexP4ui\");\n\tglad_glVertexP4uiv = (PFNGLVERTEXP4UIVPROC)load(\"glVertexP4uiv\");\n\tglad_glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC)load(\"glTexCoordP1ui\");\n\tglad_glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC)load(\"glTexCoordP1uiv\");\n\tglad_glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC)load(\"glTexCoordP2ui\");\n\tglad_glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC)load(\"glTexCoordP2uiv\");\n\tglad_glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC)load(\"glTexCoordP3ui\");\n\tglad_glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC)load(\"glTexCoordP3uiv\");\n\tglad_glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC)load(\"glTexCoordP4ui\");\n\tglad_glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC)load(\"glTexCoordP4uiv\");\n\tglad_glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC)load(\"glMultiTexCoordP1ui\");\n\tglad_glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC)load(\"glMultiTexCoordP1uiv\");\n\tglad_glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC)load(\"glMultiTexCoordP2ui\");\n\tglad_glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC)load(\"glMultiTexCoordP2uiv\");\n\tglad_glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC)load(\"glMultiTexCoordP3ui\");\n\tglad_glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC)load(\"glMultiTexCoordP3uiv\");\n\tglad_glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC)load(\"glMultiTexCoordP4ui\");\n\tglad_glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC)load(\"glMultiTexCoordP4uiv\");\n\tglad_glNormalP3ui = (PFNGLNORMALP3UIPROC)load(\"glNormalP3ui\");\n\tglad_glNormalP3uiv = (PFNGLNORMALP3UIVPROC)load(\"glNormalP3uiv\");\n\tglad_glColorP3ui = (PFNGLCOLORP3UIPROC)load(\"glColorP3ui\");\n\tglad_glColorP3uiv = (PFNGLCOLORP3UIVPROC)load(\"glColorP3uiv\");\n\tglad_glColorP4ui = (PFNGLCOLORP4UIPROC)load(\"glColorP4ui\");\n\tglad_glColorP4uiv = (PFNGLCOLORP4UIVPROC)load(\"glColorP4uiv\");\n\tglad_glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC)load(\"glSecondaryColorP3ui\");\n\tglad_glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)load(\"glSecondaryColorP3uiv\");\n}\nstatic void load_GL_VERSION_4_0(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_0) return;\n\tglad_glMinSampleShading = (PFNGLMINSAMPLESHADINGPROC)load(\"glMinSampleShading\");\n\tglad_glBlendEquationi = (PFNGLBLENDEQUATIONIPROC)load(\"glBlendEquationi\");\n\tglad_glBlendEquationSeparatei = (PFNGLBLENDEQUATIONSEPARATEIPROC)load(\"glBlendEquationSeparatei\");\n\tglad_glBlendFunci = (PFNGLBLENDFUNCIPROC)load(\"glBlendFunci\");\n\tglad_glBlendFuncSeparatei = (PFNGLBLENDFUNCSEPARATEIPROC)load(\"glBlendFuncSeparatei\");\n\tglad_glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC)load(\"glDrawArraysIndirect\");\n\tglad_glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC)load(\"glDrawElementsIndirect\");\n\tglad_glUniform1d = (PFNGLUNIFORM1DPROC)load(\"glUniform1d\");\n\tglad_glUniform2d = (PFNGLUNIFORM2DPROC)load(\"glUniform2d\");\n\tglad_glUniform3d = (PFNGLUNIFORM3DPROC)load(\"glUniform3d\");\n\tglad_glUniform4d = (PFNGLUNIFORM4DPROC)load(\"glUniform4d\");\n\tglad_glUniform1dv = (PFNGLUNIFORM1DVPROC)load(\"glUniform1dv\");\n\tglad_glUniform2dv = (PFNGLUNIFORM2DVPROC)load(\"glUniform2dv\");\n\tglad_glUniform3dv = (PFNGLUNIFORM3DVPROC)load(\"glUniform3dv\");\n\tglad_glUniform4dv = (PFNGLUNIFORM4DVPROC)load(\"glUniform4dv\");\n\tglad_glUniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC)load(\"glUniformMatrix2dv\");\n\tglad_glUniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC)load(\"glUniformMatrix3dv\");\n\tglad_glUniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC)load(\"glUniformMatrix4dv\");\n\tglad_glUniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC)load(\"glUniformMatrix2x3dv\");\n\tglad_glUniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC)load(\"glUniformMatrix2x4dv\");\n\tglad_glUniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC)load(\"glUniformMatrix3x2dv\");\n\tglad_glUniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC)load(\"glUniformMatrix3x4dv\");\n\tglad_glUniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC)load(\"glUniformMatrix4x2dv\");\n\tglad_glUniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC)load(\"glUniformMatrix4x3dv\");\n\tglad_glGetUniformdv = (PFNGLGETUNIFORMDVPROC)load(\"glGetUniformdv\");\n\tglad_glGetSubroutineUniformLocation = (PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)load(\"glGetSubroutineUniformLocation\");\n\tglad_glGetSubroutineIndex = (PFNGLGETSUBROUTINEINDEXPROC)load(\"glGetSubroutineIndex\");\n\tglad_glGetActiveSubroutineUniformiv = (PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)load(\"glGetActiveSubroutineUniformiv\");\n\tglad_glGetActiveSubroutineUniformName = (PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)load(\"glGetActiveSubroutineUniformName\");\n\tglad_glGetActiveSubroutineName = (PFNGLGETACTIVESUBROUTINENAMEPROC)load(\"glGetActiveSubroutineName\");\n\tglad_glUniformSubroutinesuiv = (PFNGLUNIFORMSUBROUTINESUIVPROC)load(\"glUniformSubroutinesuiv\");\n\tglad_glGetUniformSubroutineuiv = (PFNGLGETUNIFORMSUBROUTINEUIVPROC)load(\"glGetUniformSubroutineuiv\");\n\tglad_glGetProgramStageiv = (PFNGLGETPROGRAMSTAGEIVPROC)load(\"glGetProgramStageiv\");\n\tglad_glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)load(\"glPatchParameteri\");\n\tglad_glPatchParameterfv = (PFNGLPATCHPARAMETERFVPROC)load(\"glPatchParameterfv\");\n\tglad_glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)load(\"glBindTransformFeedback\");\n\tglad_glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC)load(\"glDeleteTransformFeedbacks\");\n\tglad_glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)load(\"glGenTransformFeedbacks\");\n\tglad_glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC)load(\"glIsTransformFeedback\");\n\tglad_glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC)load(\"glPauseTransformFeedback\");\n\tglad_glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC)load(\"glResumeTransformFeedback\");\n\tglad_glDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC)load(\"glDrawTransformFeedback\");\n\tglad_glDrawTransformFeedbackStream = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)load(\"glDrawTransformFeedbackStream\");\n\tglad_glBeginQueryIndexed = (PFNGLBEGINQUERYINDEXEDPROC)load(\"glBeginQueryIndexed\");\n\tglad_glEndQueryIndexed = (PFNGLENDQUERYINDEXEDPROC)load(\"glEndQueryIndexed\");\n\tglad_glGetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC)load(\"glGetQueryIndexediv\");\n}\nstatic void load_GL_VERSION_4_1(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_1) return;\n\tglad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)load(\"glReleaseShaderCompiler\");\n\tglad_glShaderBinary = (PFNGLSHADERBINARYPROC)load(\"glShaderBinary\");\n\tglad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)load(\"glGetShaderPrecisionFormat\");\n\tglad_glDepthRangef = (PFNGLDEPTHRANGEFPROC)load(\"glDepthRangef\");\n\tglad_glClearDepthf = (PFNGLCLEARDEPTHFPROC)load(\"glClearDepthf\");\n\tglad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)load(\"glGetProgramBinary\");\n\tglad_glProgramBinary = (PFNGLPROGRAMBINARYPROC)load(\"glProgramBinary\");\n\tglad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)load(\"glProgramParameteri\");\n\tglad_glUseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC)load(\"glUseProgramStages\");\n\tglad_glActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC)load(\"glActiveShaderProgram\");\n\tglad_glCreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC)load(\"glCreateShaderProgramv\");\n\tglad_glBindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC)load(\"glBindProgramPipeline\");\n\tglad_glDeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC)load(\"glDeleteProgramPipelines\");\n\tglad_glGenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC)load(\"glGenProgramPipelines\");\n\tglad_glIsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC)load(\"glIsProgramPipeline\");\n\tglad_glGetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC)load(\"glGetProgramPipelineiv\");\n\tglad_glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC)load(\"glProgramUniform1i\");\n\tglad_glProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC)load(\"glProgramUniform1iv\");\n\tglad_glProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC)load(\"glProgramUniform1f\");\n\tglad_glProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC)load(\"glProgramUniform1fv\");\n\tglad_glProgramUniform1d = (PFNGLPROGRAMUNIFORM1DPROC)load(\"glProgramUniform1d\");\n\tglad_glProgramUniform1dv = (PFNGLPROGRAMUNIFORM1DVPROC)load(\"glProgramUniform1dv\");\n\tglad_glProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC)load(\"glProgramUniform1ui\");\n\tglad_glProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC)load(\"glProgramUniform1uiv\");\n\tglad_glProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC)load(\"glProgramUniform2i\");\n\tglad_glProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC)load(\"glProgramUniform2iv\");\n\tglad_glProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC)load(\"glProgramUniform2f\");\n\tglad_glProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC)load(\"glProgramUniform2fv\");\n\tglad_glProgramUniform2d = (PFNGLPROGRAMUNIFORM2DPROC)load(\"glProgramUniform2d\");\n\tglad_glProgramUniform2dv = (PFNGLPROGRAMUNIFORM2DVPROC)load(\"glProgramUniform2dv\");\n\tglad_glProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC)load(\"glProgramUniform2ui\");\n\tglad_glProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC)load(\"glProgramUniform2uiv\");\n\tglad_glProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC)load(\"glProgramUniform3i\");\n\tglad_glProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC)load(\"glProgramUniform3iv\");\n\tglad_glProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC)load(\"glProgramUniform3f\");\n\tglad_glProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC)load(\"glProgramUniform3fv\");\n\tglad_glProgramUniform3d = (PFNGLPROGRAMUNIFORM3DPROC)load(\"glProgramUniform3d\");\n\tglad_glProgramUniform3dv = (PFNGLPROGRAMUNIFORM3DVPROC)load(\"glProgramUniform3dv\");\n\tglad_glProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC)load(\"glProgramUniform3ui\");\n\tglad_glProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC)load(\"glProgramUniform3uiv\");\n\tglad_glProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC)load(\"glProgramUniform4i\");\n\tglad_glProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC)load(\"glProgramUniform4iv\");\n\tglad_glProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC)load(\"glProgramUniform4f\");\n\tglad_glProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC)load(\"glProgramUniform4fv\");\n\tglad_glProgramUniform4d = (PFNGLPROGRAMUNIFORM4DPROC)load(\"glProgramUniform4d\");\n\tglad_glProgramUniform4dv = (PFNGLPROGRAMUNIFORM4DVPROC)load(\"glProgramUniform4dv\");\n\tglad_glProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC)load(\"glProgramUniform4ui\");\n\tglad_glProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC)load(\"glProgramUniform4uiv\");\n\tglad_glProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC)load(\"glProgramUniformMatrix2fv\");\n\tglad_glProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC)load(\"glProgramUniformMatrix3fv\");\n\tglad_glProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC)load(\"glProgramUniformMatrix4fv\");\n\tglad_glProgramUniformMatrix2dv = (PFNGLPROGRAMUNIFORMMATRIX2DVPROC)load(\"glProgramUniformMatrix2dv\");\n\tglad_glProgramUniformMatrix3dv = (PFNGLPROGRAMUNIFORMMATRIX3DVPROC)load(\"glProgramUniformMatrix3dv\");\n\tglad_glProgramUniformMatrix4dv = (PFNGLPROGRAMUNIFORMMATRIX4DVPROC)load(\"glProgramUniformMatrix4dv\");\n\tglad_glProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)load(\"glProgramUniformMatrix2x3fv\");\n\tglad_glProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)load(\"glProgramUniformMatrix3x2fv\");\n\tglad_glProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)load(\"glProgramUniformMatrix2x4fv\");\n\tglad_glProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)load(\"glProgramUniformMatrix4x2fv\");\n\tglad_glProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)load(\"glProgramUniformMatrix3x4fv\");\n\tglad_glProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)load(\"glProgramUniformMatrix4x3fv\");\n\tglad_glProgramUniformMatrix2x3dv = (PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)load(\"glProgramUniformMatrix2x3dv\");\n\tglad_glProgramUniformMatrix3x2dv = (PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)load(\"glProgramUniformMatrix3x2dv\");\n\tglad_glProgramUniformMatrix2x4dv = (PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)load(\"glProgramUniformMatrix2x4dv\");\n\tglad_glProgramUniformMatrix4x2dv = (PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)load(\"glProgramUniformMatrix4x2dv\");\n\tglad_glProgramUniformMatrix3x4dv = (PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)load(\"glProgramUniformMatrix3x4dv\");\n\tglad_glProgramUniformMatrix4x3dv = (PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)load(\"glProgramUniformMatrix4x3dv\");\n\tglad_glValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC)load(\"glValidateProgramPipeline\");\n\tglad_glGetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC)load(\"glGetProgramPipelineInfoLog\");\n\tglad_glVertexAttribL1d = (PFNGLVERTEXATTRIBL1DPROC)load(\"glVertexAttribL1d\");\n\tglad_glVertexAttribL2d = (PFNGLVERTEXATTRIBL2DPROC)load(\"glVertexAttribL2d\");\n\tglad_glVertexAttribL3d = (PFNGLVERTEXATTRIBL3DPROC)load(\"glVertexAttribL3d\");\n\tglad_glVertexAttribL4d = (PFNGLVERTEXATTRIBL4DPROC)load(\"glVertexAttribL4d\");\n\tglad_glVertexAttribL1dv = (PFNGLVERTEXATTRIBL1DVPROC)load(\"glVertexAttribL1dv\");\n\tglad_glVertexAttribL2dv = (PFNGLVERTEXATTRIBL2DVPROC)load(\"glVertexAttribL2dv\");\n\tglad_glVertexAttribL3dv = (PFNGLVERTEXATTRIBL3DVPROC)load(\"glVertexAttribL3dv\");\n\tglad_glVertexAttribL4dv = (PFNGLVERTEXATTRIBL4DVPROC)load(\"glVertexAttribL4dv\");\n\tglad_glVertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC)load(\"glVertexAttribLPointer\");\n\tglad_glGetVertexAttribLdv = (PFNGLGETVERTEXATTRIBLDVPROC)load(\"glGetVertexAttribLdv\");\n\tglad_glViewportArrayv = (PFNGLVIEWPORTARRAYVPROC)load(\"glViewportArrayv\");\n\tglad_glViewportIndexedf = (PFNGLVIEWPORTINDEXEDFPROC)load(\"glViewportIndexedf\");\n\tglad_glViewportIndexedfv = (PFNGLVIEWPORTINDEXEDFVPROC)load(\"glViewportIndexedfv\");\n\tglad_glScissorArrayv = (PFNGLSCISSORARRAYVPROC)load(\"glScissorArrayv\");\n\tglad_glScissorIndexed = (PFNGLSCISSORINDEXEDPROC)load(\"glScissorIndexed\");\n\tglad_glScissorIndexedv = (PFNGLSCISSORINDEXEDVPROC)load(\"glScissorIndexedv\");\n\tglad_glDepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC)load(\"glDepthRangeArrayv\");\n\tglad_glDepthRangeIndexed = (PFNGLDEPTHRANGEINDEXEDPROC)load(\"glDepthRangeIndexed\");\n\tglad_glGetFloati_v = (PFNGLGETFLOATI_VPROC)load(\"glGetFloati_v\");\n\tglad_glGetDoublei_v = (PFNGLGETDOUBLEI_VPROC)load(\"glGetDoublei_v\");\n}\nstatic void load_GL_VERSION_4_2(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_2) return;\n\tglad_glDrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)load(\"glDrawArraysInstancedBaseInstance\");\n\tglad_glDrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)load(\"glDrawElementsInstancedBaseInstance\");\n\tglad_glDrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)load(\"glDrawElementsInstancedBaseVertexBaseInstance\");\n\tglad_glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC)load(\"glGetInternalformativ\");\n\tglad_glGetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)load(\"glGetActiveAtomicCounterBufferiv\");\n\tglad_glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)load(\"glBindImageTexture\");\n\tglad_glMemoryBarrier = (PFNGLMEMORYBARRIERPROC)load(\"glMemoryBarrier\");\n\tglad_glTexStorage1D = (PFNGLTEXSTORAGE1DPROC)load(\"glTexStorage1D\");\n\tglad_glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)load(\"glTexStorage2D\");\n\tglad_glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)load(\"glTexStorage3D\");\n\tglad_glDrawTransformFeedbackInstanced = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)load(\"glDrawTransformFeedbackInstanced\");\n\tglad_glDrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)load(\"glDrawTransformFeedbackStreamInstanced\");\n}\nstatic void load_GL_VERSION_4_3(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_3) return;\n\tglad_glClearBufferData = (PFNGLCLEARBUFFERDATAPROC)load(\"glClearBufferData\");\n\tglad_glClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC)load(\"glClearBufferSubData\");\n\tglad_glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)load(\"glDispatchCompute\");\n\tglad_glDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC)load(\"glDispatchComputeIndirect\");\n\tglad_glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC)load(\"glCopyImageSubData\");\n\tglad_glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC)load(\"glFramebufferParameteri\");\n\tglad_glGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC)load(\"glGetFramebufferParameteriv\");\n\tglad_glGetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC)load(\"glGetInternalformati64v\");\n\tglad_glInvalidateTexSubImage = (PFNGLINVALIDATETEXSUBIMAGEPROC)load(\"glInvalidateTexSubImage\");\n\tglad_glInvalidateTexImage = (PFNGLINVALIDATETEXIMAGEPROC)load(\"glInvalidateTexImage\");\n\tglad_glInvalidateBufferSubData = (PFNGLINVALIDATEBUFFERSUBDATAPROC)load(\"glInvalidateBufferSubData\");\n\tglad_glInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC)load(\"glInvalidateBufferData\");\n\tglad_glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC)load(\"glInvalidateFramebuffer\");\n\tglad_glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC)load(\"glInvalidateSubFramebuffer\");\n\tglad_glMultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC)load(\"glMultiDrawArraysIndirect\");\n\tglad_glMultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC)load(\"glMultiDrawElementsIndirect\");\n\tglad_glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)load(\"glGetProgramInterfaceiv\");\n\tglad_glGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC)load(\"glGetProgramResourceIndex\");\n\tglad_glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)load(\"glGetProgramResourceName\");\n\tglad_glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)load(\"glGetProgramResourceiv\");\n\tglad_glGetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC)load(\"glGetProgramResourceLocation\");\n\tglad_glGetProgramResourceLocationIndex = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)load(\"glGetProgramResourceLocationIndex\");\n\tglad_glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC)load(\"glShaderStorageBlockBinding\");\n\tglad_glTexBufferRange = (PFNGLTEXBUFFERRANGEPROC)load(\"glTexBufferRange\");\n\tglad_glTexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC)load(\"glTexStorage2DMultisample\");\n\tglad_glTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)load(\"glTexStorage3DMultisample\");\n\tglad_glTextureView = (PFNGLTEXTUREVIEWPROC)load(\"glTextureView\");\n\tglad_glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC)load(\"glBindVertexBuffer\");\n\tglad_glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC)load(\"glVertexAttribFormat\");\n\tglad_glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC)load(\"glVertexAttribIFormat\");\n\tglad_glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC)load(\"glVertexAttribLFormat\");\n\tglad_glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC)load(\"glVertexAttribBinding\");\n\tglad_glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC)load(\"glVertexBindingDivisor\");\n\tglad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)load(\"glDebugMessageControl\");\n\tglad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)load(\"glDebugMessageInsert\");\n\tglad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)load(\"glDebugMessageCallback\");\n\tglad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)load(\"glGetDebugMessageLog\");\n\tglad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)load(\"glPushDebugGroup\");\n\tglad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)load(\"glPopDebugGroup\");\n\tglad_glObjectLabel = (PFNGLOBJECTLABELPROC)load(\"glObjectLabel\");\n\tglad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)load(\"glGetObjectLabel\");\n\tglad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)load(\"glObjectPtrLabel\");\n\tglad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load(\"glGetObjectPtrLabel\");\n}\nstatic void load_GL_VERSION_4_4(GLADloadproc load) {\n\tif(!GLAD_GL_VERSION_4_4) return;\n\tglad_glBufferStorage = (PFNGLBUFFERSTORAGEPROC)load(\"glBufferStorage\");\n\tglad_glClearTexImage = (PFNGLCLEARTEXIMAGEPROC)load(\"glClearTexImage\");\n\tglad_glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC)load(\"glClearTexSubImage\");\n\tglad_glBindBuffersBase = (PFNGLBINDBUFFERSBASEPROC)load(\"glBindBuffersBase\");\n\tglad_glBindBuffersRange = (PFNGLBINDBUFFERSRANGEPROC)load(\"glBindBuffersRange\");\n\tglad_glBindTextures = (PFNGLBINDTEXTURESPROC)load(\"glBindTextures\");\n\tglad_glBindSamplers = (PFNGLBINDSAMPLERSPROC)load(\"glBindSamplers\");\n\tglad_glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)load(\"glBindImageTextures\");\n\tglad_glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)load(\"glBindVertexBuffers\");\n}\nstatic void load_GL_NV_point_sprite(GLADloadproc load) {\n\tif(!GLAD_GL_NV_point_sprite) return;\n\tglad_glPointParameteriNV = (PFNGLPOINTPARAMETERINVPROC)load(\"glPointParameteriNV\");\n\tglad_glPointParameterivNV = (PFNGLPOINTPARAMETERIVNVPROC)load(\"glPointParameterivNV\");\n}\nstatic void load_GL_APPLE_element_array(GLADloadproc load) {\n\tif(!GLAD_GL_APPLE_element_array) return;\n\tglad_glElementPointerAPPLE = (PFNGLELEMENTPOINTERAPPLEPROC)load(\"glElementPointerAPPLE\");\n\tglad_glDrawElementArrayAPPLE = (PFNGLDRAWELEMENTARRAYAPPLEPROC)load(\"glDrawElementArrayAPPLE\");\n\tglad_glDrawRangeElementArrayAPPLE = (PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC)load(\"glDrawRangeElementArrayAPPLE\");\n\tglad_glMultiDrawElementArrayAPPLE = (PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC)load(\"glMultiDrawElementArrayAPPLE\");\n\tglad_glMultiDrawRangeElementArrayAPPLE = (PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC)load(\"glMultiDrawRangeElementArrayAPPLE\");\n}\nstatic void load_GL_AMD_multi_draw_indirect(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_multi_draw_indirect) return;\n\tglad_glMultiDrawArraysIndirectAMD = (PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC)load(\"glMultiDrawArraysIndirectAMD\");\n\tglad_glMultiDrawElementsIndirectAMD = (PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC)load(\"glMultiDrawElementsIndirectAMD\");\n}\nstatic void load_GL_SGIX_tag_sample_buffer(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_tag_sample_buffer) return;\n\tglad_glTagSampleBufferSGIX = (PFNGLTAGSAMPLEBUFFERSGIXPROC)load(\"glTagSampleBufferSGIX\");\n}\nstatic void load_GL_ATI_separate_stencil(GLADloadproc load) {\n\tif(!GLAD_GL_ATI_separate_stencil) return;\n\tglad_glStencilOpSeparateATI = (PFNGLSTENCILOPSEPARATEATIPROC)load(\"glStencilOpSeparateATI\");\n\tglad_glStencilFuncSeparateATI = (PFNGLSTENCILFUNCSEPARATEATIPROC)load(\"glStencilFuncSeparateATI\");\n}\nstatic void load_GL_EXT_texture_buffer_object(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_texture_buffer_object) return;\n\tglad_glTexBufferEXT = (PFNGLTEXBUFFEREXTPROC)load(\"glTexBufferEXT\");\n}\nstatic void load_GL_ARB_vertex_blend(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_vertex_blend) return;\n\tglad_glWeightbvARB = (PFNGLWEIGHTBVARBPROC)load(\"glWeightbvARB\");\n\tglad_glWeightsvARB = (PFNGLWEIGHTSVARBPROC)load(\"glWeightsvARB\");\n\tglad_glWeightivARB = (PFNGLWEIGHTIVARBPROC)load(\"glWeightivARB\");\n\tglad_glWeightfvARB = (PFNGLWEIGHTFVARBPROC)load(\"glWeightfvARB\");\n\tglad_glWeightdvARB = (PFNGLWEIGHTDVARBPROC)load(\"glWeightdvARB\");\n\tglad_glWeightubvARB = (PFNGLWEIGHTUBVARBPROC)load(\"glWeightubvARB\");\n\tglad_glWeightusvARB = (PFNGLWEIGHTUSVARBPROC)load(\"glWeightusvARB\");\n\tglad_glWeightuivARB = (PFNGLWEIGHTUIVARBPROC)load(\"glWeightuivARB\");\n\tglad_glWeightPointerARB = (PFNGLWEIGHTPOINTERARBPROC)load(\"glWeightPointerARB\");\n\tglad_glVertexBlendARB = (PFNGLVERTEXBLENDARBPROC)load(\"glVertexBlendARB\");\n}\nstatic void load_GL_ARB_program_interface_query(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_program_interface_query) return;\n\tglad_glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)load(\"glGetProgramInterfaceiv\");\n\tglad_glGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC)load(\"glGetProgramResourceIndex\");\n\tglad_glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)load(\"glGetProgramResourceName\");\n\tglad_glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)load(\"glGetProgramResourceiv\");\n\tglad_glGetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC)load(\"glGetProgramResourceLocation\");\n\tglad_glGetProgramResourceLocationIndex = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)load(\"glGetProgramResourceLocationIndex\");\n}\nstatic void load_GL_EXT_index_func(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_index_func) return;\n\tglad_glIndexFuncEXT = (PFNGLINDEXFUNCEXTPROC)load(\"glIndexFuncEXT\");\n}\nstatic void load_GL_NV_shader_buffer_load(GLADloadproc load) {\n\tif(!GLAD_GL_NV_shader_buffer_load) return;\n\tglad_glMakeBufferResidentNV = (PFNGLMAKEBUFFERRESIDENTNVPROC)load(\"glMakeBufferResidentNV\");\n\tglad_glMakeBufferNonResidentNV = (PFNGLMAKEBUFFERNONRESIDENTNVPROC)load(\"glMakeBufferNonResidentNV\");\n\tglad_glIsBufferResidentNV = (PFNGLISBUFFERRESIDENTNVPROC)load(\"glIsBufferResidentNV\");\n\tglad_glMakeNamedBufferResidentNV = (PFNGLMAKENAMEDBUFFERRESIDENTNVPROC)load(\"glMakeNamedBufferResidentNV\");\n\tglad_glMakeNamedBufferNonResidentNV = (PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC)load(\"glMakeNamedBufferNonResidentNV\");\n\tglad_glIsNamedBufferResidentNV = (PFNGLISNAMEDBUFFERRESIDENTNVPROC)load(\"glIsNamedBufferResidentNV\");\n\tglad_glGetBufferParameterui64vNV = (PFNGLGETBUFFERPARAMETERUI64VNVPROC)load(\"glGetBufferParameterui64vNV\");\n\tglad_glGetNamedBufferParameterui64vNV = (PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC)load(\"glGetNamedBufferParameterui64vNV\");\n\tglad_glGetIntegerui64vNV = (PFNGLGETINTEGERUI64VNVPROC)load(\"glGetIntegerui64vNV\");\n\tglad_glUniformui64NV = (PFNGLUNIFORMUI64NVPROC)load(\"glUniformui64NV\");\n\tglad_glUniformui64vNV = (PFNGLUNIFORMUI64VNVPROC)load(\"glUniformui64vNV\");\n\tglad_glGetUniformui64vNV = (PFNGLGETUNIFORMUI64VNVPROC)load(\"glGetUniformui64vNV\");\n\tglad_glProgramUniformui64NV = (PFNGLPROGRAMUNIFORMUI64NVPROC)load(\"glProgramUniformui64NV\");\n\tglad_glProgramUniformui64vNV = (PFNGLPROGRAMUNIFORMUI64VNVPROC)load(\"glProgramUniformui64vNV\");\n}\nstatic void load_GL_EXT_color_subtable(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_color_subtable) return;\n\tglad_glColorSubTableEXT = (PFNGLCOLORSUBTABLEEXTPROC)load(\"glColorSubTableEXT\");\n\tglad_glCopyColorSubTableEXT = (PFNGLCOPYCOLORSUBTABLEEXTPROC)load(\"glCopyColorSubTableEXT\");\n}\nstatic void load_GL_SUNX_constant_data(GLADloadproc load) {\n\tif(!GLAD_GL_SUNX_constant_data) return;\n\tglad_glFinishTextureSUNX = (PFNGLFINISHTEXTURESUNXPROC)load(\"glFinishTextureSUNX\");\n}\nstatic void load_GL_EXT_multi_draw_arrays(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_multi_draw_arrays) return;\n\tglad_glMultiDrawArraysEXT = (PFNGLMULTIDRAWARRAYSEXTPROC)load(\"glMultiDrawArraysEXT\");\n\tglad_glMultiDrawElementsEXT = (PFNGLMULTIDRAWELEMENTSEXTPROC)load(\"glMultiDrawElementsEXT\");\n}\nstatic void load_GL_ARB_shader_atomic_counters(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_shader_atomic_counters) return;\n\tglad_glGetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)load(\"glGetActiveAtomicCounterBufferiv\");\n}\nstatic void load_GL_NV_conditional_render(GLADloadproc load) {\n\tif(!GLAD_GL_NV_conditional_render) return;\n\tglad_glBeginConditionalRenderNV = (PFNGLBEGINCONDITIONALRENDERNVPROC)load(\"glBeginConditionalRenderNV\");\n\tglad_glEndConditionalRenderNV = (PFNGLENDCONDITIONALRENDERNVPROC)load(\"glEndConditionalRenderNV\");\n}\nstatic void load_GL_MESA_resize_buffers(GLADloadproc load) {\n\tif(!GLAD_GL_MESA_resize_buffers) return;\n\tglad_glResizeBuffersMESA = (PFNGLRESIZEBUFFERSMESAPROC)load(\"glResizeBuffersMESA\");\n}\nstatic void load_GL_ARB_texture_view(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_texture_view) return;\n\tglad_glTextureView = (PFNGLTEXTUREVIEWPROC)load(\"glTextureView\");\n}\nstatic void load_GL_ARB_map_buffer_range(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_map_buffer_range) return;\n\tglad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)load(\"glMapBufferRange\");\n\tglad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC)load(\"glFlushMappedBufferRange\");\n}\nstatic void load_GL_EXT_convolution(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_convolution) return;\n\tglad_glConvolutionFilter1DEXT = (PFNGLCONVOLUTIONFILTER1DEXTPROC)load(\"glConvolutionFilter1DEXT\");\n\tglad_glConvolutionFilter2DEXT = (PFNGLCONVOLUTIONFILTER2DEXTPROC)load(\"glConvolutionFilter2DEXT\");\n\tglad_glConvolutionParameterfEXT = (PFNGLCONVOLUTIONPARAMETERFEXTPROC)load(\"glConvolutionParameterfEXT\");\n\tglad_glConvolutionParameterfvEXT = (PFNGLCONVOLUTIONPARAMETERFVEXTPROC)load(\"glConvolutionParameterfvEXT\");\n\tglad_glConvolutionParameteriEXT = (PFNGLCONVOLUTIONPARAMETERIEXTPROC)load(\"glConvolutionParameteriEXT\");\n\tglad_glConvolutionParameterivEXT = (PFNGLCONVOLUTIONPARAMETERIVEXTPROC)load(\"glConvolutionParameterivEXT\");\n\tglad_glCopyConvolutionFilter1DEXT = (PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC)load(\"glCopyConvolutionFilter1DEXT\");\n\tglad_glCopyConvolutionFilter2DEXT = (PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC)load(\"glCopyConvolutionFilter2DEXT\");\n\tglad_glGetConvolutionFilterEXT = (PFNGLGETCONVOLUTIONFILTEREXTPROC)load(\"glGetConvolutionFilterEXT\");\n\tglad_glGetConvolutionParameterfvEXT = (PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC)load(\"glGetConvolutionParameterfvEXT\");\n\tglad_glGetConvolutionParameterivEXT = (PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC)load(\"glGetConvolutionParameterivEXT\");\n\tglad_glGetSeparableFilterEXT = (PFNGLGETSEPARABLEFILTEREXTPROC)load(\"glGetSeparableFilterEXT\");\n\tglad_glSeparableFilter2DEXT = (PFNGLSEPARABLEFILTER2DEXTPROC)load(\"glSeparableFilter2DEXT\");\n}\nstatic void load_GL_NV_vertex_attrib_integer_64bit(GLADloadproc load) {\n\tif(!GLAD_GL_NV_vertex_attrib_integer_64bit) return;\n\tglad_glVertexAttribL1i64NV = (PFNGLVERTEXATTRIBL1I64NVPROC)load(\"glVertexAttribL1i64NV\");\n\tglad_glVertexAttribL2i64NV = (PFNGLVERTEXATTRIBL2I64NVPROC)load(\"glVertexAttribL2i64NV\");\n\tglad_glVertexAttribL3i64NV = (PFNGLVERTEXATTRIBL3I64NVPROC)load(\"glVertexAttribL3i64NV\");\n\tglad_glVertexAttribL4i64NV = (PFNGLVERTEXATTRIBL4I64NVPROC)load(\"glVertexAttribL4i64NV\");\n\tglad_glVertexAttribL1i64vNV = (PFNGLVERTEXATTRIBL1I64VNVPROC)load(\"glVertexAttribL1i64vNV\");\n\tglad_glVertexAttribL2i64vNV = (PFNGLVERTEXATTRIBL2I64VNVPROC)load(\"glVertexAttribL2i64vNV\");\n\tglad_glVertexAttribL3i64vNV = (PFNGLVERTEXATTRIBL3I64VNVPROC)load(\"glVertexAttribL3i64vNV\");\n\tglad_glVertexAttribL4i64vNV = (PFNGLVERTEXATTRIBL4I64VNVPROC)load(\"glVertexAttribL4i64vNV\");\n\tglad_glVertexAttribL1ui64NV = (PFNGLVERTEXATTRIBL1UI64NVPROC)load(\"glVertexAttribL1ui64NV\");\n\tglad_glVertexAttribL2ui64NV = (PFNGLVERTEXATTRIBL2UI64NVPROC)load(\"glVertexAttribL2ui64NV\");\n\tglad_glVertexAttribL3ui64NV = (PFNGLVERTEXATTRIBL3UI64NVPROC)load(\"glVertexAttribL3ui64NV\");\n\tglad_glVertexAttribL4ui64NV = (PFNGLVERTEXATTRIBL4UI64NVPROC)load(\"glVertexAttribL4ui64NV\");\n\tglad_glVertexAttribL1ui64vNV = (PFNGLVERTEXATTRIBL1UI64VNVPROC)load(\"glVertexAttribL1ui64vNV\");\n\tglad_glVertexAttribL2ui64vNV = (PFNGLVERTEXATTRIBL2UI64VNVPROC)load(\"glVertexAttribL2ui64vNV\");\n\tglad_glVertexAttribL3ui64vNV = (PFNGLVERTEXATTRIBL3UI64VNVPROC)load(\"glVertexAttribL3ui64vNV\");\n\tglad_glVertexAttribL4ui64vNV = (PFNGLVERTEXATTRIBL4UI64VNVPROC)load(\"glVertexAttribL4ui64vNV\");\n\tglad_glGetVertexAttribLi64vNV = (PFNGLGETVERTEXATTRIBLI64VNVPROC)load(\"glGetVertexAttribLi64vNV\");\n\tglad_glGetVertexAttribLui64vNV = (PFNGLGETVERTEXATTRIBLUI64VNVPROC)load(\"glGetVertexAttribLui64vNV\");\n\tglad_glVertexAttribLFormatNV = (PFNGLVERTEXATTRIBLFORMATNVPROC)load(\"glVertexAttribLFormatNV\");\n}\nstatic void load_GL_EXT_paletted_texture(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_paletted_texture) return;\n\tglad_glColorTableEXT = (PFNGLCOLORTABLEEXTPROC)load(\"glColorTableEXT\");\n\tglad_glGetColorTableEXT = (PFNGLGETCOLORTABLEEXTPROC)load(\"glGetColorTableEXT\");\n\tglad_glGetColorTableParameterivEXT = (PFNGLGETCOLORTABLEPARAMETERIVEXTPROC)load(\"glGetColorTableParameterivEXT\");\n\tglad_glGetColorTableParameterfvEXT = (PFNGLGETCOLORTABLEPARAMETERFVEXTPROC)load(\"glGetColorTableParameterfvEXT\");\n}\nstatic void load_GL_ARB_texture_buffer_object(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_texture_buffer_object) return;\n\tglad_glTexBufferARB = (PFNGLTEXBUFFERARBPROC)load(\"glTexBufferARB\");\n}\nstatic void load_GL_ATI_pn_triangles(GLADloadproc load) {\n\tif(!GLAD_GL_ATI_pn_triangles) return;\n\tglad_glPNTrianglesiATI = (PFNGLPNTRIANGLESIATIPROC)load(\"glPNTrianglesiATI\");\n\tglad_glPNTrianglesfATI = (PFNGLPNTRIANGLESFATIPROC)load(\"glPNTrianglesfATI\");\n}\nstatic void load_GL_SGIX_flush_raster(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_flush_raster) return;\n\tglad_glFlushRasterSGIX = (PFNGLFLUSHRASTERSGIXPROC)load(\"glFlushRasterSGIX\");\n}\nstatic void load_GL_EXT_light_texture(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_light_texture) return;\n\tglad_glApplyTextureEXT = (PFNGLAPPLYTEXTUREEXTPROC)load(\"glApplyTextureEXT\");\n\tglad_glTextureLightEXT = (PFNGLTEXTURELIGHTEXTPROC)load(\"glTextureLightEXT\");\n\tglad_glTextureMaterialEXT = (PFNGLTEXTUREMATERIALEXTPROC)load(\"glTextureMaterialEXT\");\n}\nstatic void load_GL_AMD_draw_buffers_blend(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_draw_buffers_blend) return;\n\tglad_glBlendFuncIndexedAMD = (PFNGLBLENDFUNCINDEXEDAMDPROC)load(\"glBlendFuncIndexedAMD\");\n\tglad_glBlendFuncSeparateIndexedAMD = (PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC)load(\"glBlendFuncSeparateIndexedAMD\");\n\tglad_glBlendEquationIndexedAMD = (PFNGLBLENDEQUATIONINDEXEDAMDPROC)load(\"glBlendEquationIndexedAMD\");\n\tglad_glBlendEquationSeparateIndexedAMD = (PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC)load(\"glBlendEquationSeparateIndexedAMD\");\n}\nstatic void load_GL_MESA_window_pos(GLADloadproc load) {\n\tif(!GLAD_GL_MESA_window_pos) return;\n\tglad_glWindowPos2dMESA = (PFNGLWINDOWPOS2DMESAPROC)load(\"glWindowPos2dMESA\");\n\tglad_glWindowPos2dvMESA = (PFNGLWINDOWPOS2DVMESAPROC)load(\"glWindowPos2dvMESA\");\n\tglad_glWindowPos2fMESA = (PFNGLWINDOWPOS2FMESAPROC)load(\"glWindowPos2fMESA\");\n\tglad_glWindowPos2fvMESA = (PFNGLWINDOWPOS2FVMESAPROC)load(\"glWindowPos2fvMESA\");\n\tglad_glWindowPos2iMESA = (PFNGLWINDOWPOS2IMESAPROC)load(\"glWindowPos2iMESA\");\n\tglad_glWindowPos2ivMESA = (PFNGLWINDOWPOS2IVMESAPROC)load(\"glWindowPos2ivMESA\");\n\tglad_glWindowPos2sMESA = (PFNGLWINDOWPOS2SMESAPROC)load(\"glWindowPos2sMESA\");\n\tglad_glWindowPos2svMESA = (PFNGLWINDOWPOS2SVMESAPROC)load(\"glWindowPos2svMESA\");\n\tglad_glWindowPos3dMESA = (PFNGLWINDOWPOS3DMESAPROC)load(\"glWindowPos3dMESA\");\n\tglad_glWindowPos3dvMESA = (PFNGLWINDOWPOS3DVMESAPROC)load(\"glWindowPos3dvMESA\");\n\tglad_glWindowPos3fMESA = (PFNGLWINDOWPOS3FMESAPROC)load(\"glWindowPos3fMESA\");\n\tglad_glWindowPos3fvMESA = (PFNGLWINDOWPOS3FVMESAPROC)load(\"glWindowPos3fvMESA\");\n\tglad_glWindowPos3iMESA = (PFNGLWINDOWPOS3IMESAPROC)load(\"glWindowPos3iMESA\");\n\tglad_glWindowPos3ivMESA = (PFNGLWINDOWPOS3IVMESAPROC)load(\"glWindowPos3ivMESA\");\n\tglad_glWindowPos3sMESA = (PFNGLWINDOWPOS3SMESAPROC)load(\"glWindowPos3sMESA\");\n\tglad_glWindowPos3svMESA = (PFNGLWINDOWPOS3SVMESAPROC)load(\"glWindowPos3svMESA\");\n\tglad_glWindowPos4dMESA = (PFNGLWINDOWPOS4DMESAPROC)load(\"glWindowPos4dMESA\");\n\tglad_glWindowPos4dvMESA = (PFNGLWINDOWPOS4DVMESAPROC)load(\"glWindowPos4dvMESA\");\n\tglad_glWindowPos4fMESA = (PFNGLWINDOWPOS4FMESAPROC)load(\"glWindowPos4fMESA\");\n\tglad_glWindowPos4fvMESA = (PFNGLWINDOWPOS4FVMESAPROC)load(\"glWindowPos4fvMESA\");\n\tglad_glWindowPos4iMESA = (PFNGLWINDOWPOS4IMESAPROC)load(\"glWindowPos4iMESA\");\n\tglad_glWindowPos4ivMESA = (PFNGLWINDOWPOS4IVMESAPROC)load(\"glWindowPos4ivMESA\");\n\tglad_glWindowPos4sMESA = (PFNGLWINDOWPOS4SMESAPROC)load(\"glWindowPos4sMESA\");\n\tglad_glWindowPos4svMESA = (PFNGLWINDOWPOS4SVMESAPROC)load(\"glWindowPos4svMESA\");\n}\nstatic void load_GL_NV_texture_barrier(GLADloadproc load) {\n\tif(!GLAD_GL_NV_texture_barrier) return;\n\tglad_glTextureBarrierNV = (PFNGLTEXTUREBARRIERNVPROC)load(\"glTextureBarrierNV\");\n}\nstatic void load_GL_ARB_vertex_type_2_10_10_10_rev(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_vertex_type_2_10_10_10_rev) return;\n\tglad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC)load(\"glVertexAttribP1ui\");\n\tglad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC)load(\"glVertexAttribP1uiv\");\n\tglad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC)load(\"glVertexAttribP2ui\");\n\tglad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC)load(\"glVertexAttribP2uiv\");\n\tglad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC)load(\"glVertexAttribP3ui\");\n\tglad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC)load(\"glVertexAttribP3uiv\");\n\tglad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC)load(\"glVertexAttribP4ui\");\n\tglad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC)load(\"glVertexAttribP4uiv\");\n\tglad_glVertexP2ui = (PFNGLVERTEXP2UIPROC)load(\"glVertexP2ui\");\n\tglad_glVertexP2uiv = (PFNGLVERTEXP2UIVPROC)load(\"glVertexP2uiv\");\n\tglad_glVertexP3ui = (PFNGLVERTEXP3UIPROC)load(\"glVertexP3ui\");\n\tglad_glVertexP3uiv = (PFNGLVERTEXP3UIVPROC)load(\"glVertexP3uiv\");\n\tglad_glVertexP4ui = (PFNGLVERTEXP4UIPROC)load(\"glVertexP4ui\");\n\tglad_glVertexP4uiv = (PFNGLVERTEXP4UIVPROC)load(\"glVertexP4uiv\");\n\tglad_glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC)load(\"glTexCoordP1ui\");\n\tglad_glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC)load(\"glTexCoordP1uiv\");\n\tglad_glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC)load(\"glTexCoordP2ui\");\n\tglad_glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC)load(\"glTexCoordP2uiv\");\n\tglad_glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC)load(\"glTexCoordP3ui\");\n\tglad_glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC)load(\"glTexCoordP3uiv\");\n\tglad_glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC)load(\"glTexCoordP4ui\");\n\tglad_glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC)load(\"glTexCoordP4uiv\");\n\tglad_glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC)load(\"glMultiTexCoordP1ui\");\n\tglad_glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC)load(\"glMultiTexCoordP1uiv\");\n\tglad_glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC)load(\"glMultiTexCoordP2ui\");\n\tglad_glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC)load(\"glMultiTexCoordP2uiv\");\n\tglad_glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC)load(\"glMultiTexCoordP3ui\");\n\tglad_glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC)load(\"glMultiTexCoordP3uiv\");\n\tglad_glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC)load(\"glMultiTexCoordP4ui\");\n\tglad_glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC)load(\"glMultiTexCoordP4uiv\");\n\tglad_glNormalP3ui = (PFNGLNORMALP3UIPROC)load(\"glNormalP3ui\");\n\tglad_glNormalP3uiv = (PFNGLNORMALP3UIVPROC)load(\"glNormalP3uiv\");\n\tglad_glColorP3ui = (PFNGLCOLORP3UIPROC)load(\"glColorP3ui\");\n\tglad_glColorP3uiv = (PFNGLCOLORP3UIVPROC)load(\"glColorP3uiv\");\n\tglad_glColorP4ui = (PFNGLCOLORP4UIPROC)load(\"glColorP4ui\");\n\tglad_glColorP4uiv = (PFNGLCOLORP4UIVPROC)load(\"glColorP4uiv\");\n\tglad_glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC)load(\"glSecondaryColorP3ui\");\n\tglad_glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)load(\"glSecondaryColorP3uiv\");\n}\nstatic void load_GL_3DFX_tbuffer(GLADloadproc load) {\n\tif(!GLAD_GL_3DFX_tbuffer) return;\n\tglad_glTbufferMask3DFX = (PFNGLTBUFFERMASK3DFXPROC)load(\"glTbufferMask3DFX\");\n}\nstatic void load_GL_GREMEDY_frame_terminator(GLADloadproc load) {\n\tif(!GLAD_GL_GREMEDY_frame_terminator) return;\n\tglad_glFrameTerminatorGREMEDY = (PFNGLFRAMETERMINATORGREMEDYPROC)load(\"glFrameTerminatorGREMEDY\");\n}\nstatic void load_GL_ARB_blend_func_extended(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_blend_func_extended) return;\n\tglad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)load(\"glBindFragDataLocationIndexed\");\n\tglad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)load(\"glGetFragDataIndex\");\n}\nstatic void load_GL_EXT_separate_shader_objects(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_separate_shader_objects) return;\n\tglad_glUseShaderProgramEXT = (PFNGLUSESHADERPROGRAMEXTPROC)load(\"glUseShaderProgramEXT\");\n\tglad_glActiveProgramEXT = (PFNGLACTIVEPROGRAMEXTPROC)load(\"glActiveProgramEXT\");\n\tglad_glCreateShaderProgramEXT = (PFNGLCREATESHADERPROGRAMEXTPROC)load(\"glCreateShaderProgramEXT\");\n\tglad_glActiveShaderProgramEXT = (PFNGLACTIVESHADERPROGRAMEXTPROC)load(\"glActiveShaderProgramEXT\");\n\tglad_glBindProgramPipelineEXT = (PFNGLBINDPROGRAMPIPELINEEXTPROC)load(\"glBindProgramPipelineEXT\");\n\tglad_glCreateShaderProgramvEXT = (PFNGLCREATESHADERPROGRAMVEXTPROC)load(\"glCreateShaderProgramvEXT\");\n\tglad_glDeleteProgramPipelinesEXT = (PFNGLDELETEPROGRAMPIPELINESEXTPROC)load(\"glDeleteProgramPipelinesEXT\");\n\tglad_glGenProgramPipelinesEXT = (PFNGLGENPROGRAMPIPELINESEXTPROC)load(\"glGenProgramPipelinesEXT\");\n\tglad_glGetProgramPipelineInfoLogEXT = (PFNGLGETPROGRAMPIPELINEINFOLOGEXTPROC)load(\"glGetProgramPipelineInfoLogEXT\");\n\tglad_glGetProgramPipelineivEXT = (PFNGLGETPROGRAMPIPELINEIVEXTPROC)load(\"glGetProgramPipelineivEXT\");\n\tglad_glIsProgramPipelineEXT = (PFNGLISPROGRAMPIPELINEEXTPROC)load(\"glIsProgramPipelineEXT\");\n\tglad_glProgramParameteriEXT = (PFNGLPROGRAMPARAMETERIEXTPROC)load(\"glProgramParameteriEXT\");\n\tglad_glProgramUniform1fEXT = (PFNGLPROGRAMUNIFORM1FEXTPROC)load(\"glProgramUniform1fEXT\");\n\tglad_glProgramUniform1fvEXT = (PFNGLPROGRAMUNIFORM1FVEXTPROC)load(\"glProgramUniform1fvEXT\");\n\tglad_glProgramUniform1iEXT = (PFNGLPROGRAMUNIFORM1IEXTPROC)load(\"glProgramUniform1iEXT\");\n\tglad_glProgramUniform1ivEXT = (PFNGLPROGRAMUNIFORM1IVEXTPROC)load(\"glProgramUniform1ivEXT\");\n\tglad_glProgramUniform2fEXT = (PFNGLPROGRAMUNIFORM2FEXTPROC)load(\"glProgramUniform2fEXT\");\n\tglad_glProgramUniform2fvEXT = (PFNGLPROGRAMUNIFORM2FVEXTPROC)load(\"glProgramUniform2fvEXT\");\n\tglad_glProgramUniform2iEXT = (PFNGLPROGRAMUNIFORM2IEXTPROC)load(\"glProgramUniform2iEXT\");\n\tglad_glProgramUniform2ivEXT = (PFNGLPROGRAMUNIFORM2IVEXTPROC)load(\"glProgramUniform2ivEXT\");\n\tglad_glProgramUniform3fEXT = (PFNGLPROGRAMUNIFORM3FEXTPROC)load(\"glProgramUniform3fEXT\");\n\tglad_glProgramUniform3fvEXT = (PFNGLPROGRAMUNIFORM3FVEXTPROC)load(\"glProgramUniform3fvEXT\");\n\tglad_glProgramUniform3iEXT = (PFNGLPROGRAMUNIFORM3IEXTPROC)load(\"glProgramUniform3iEXT\");\n\tglad_glProgramUniform3ivEXT = (PFNGLPROGRAMUNIFORM3IVEXTPROC)load(\"glProgramUniform3ivEXT\");\n\tglad_glProgramUniform4fEXT = (PFNGLPROGRAMUNIFORM4FEXTPROC)load(\"glProgramUniform4fEXT\");\n\tglad_glProgramUniform4fvEXT = (PFNGLPROGRAMUNIFORM4FVEXTPROC)load(\"glProgramUniform4fvEXT\");\n\tglad_glProgramUniform4iEXT = (PFNGLPROGRAMUNIFORM4IEXTPROC)load(\"glProgramUniform4iEXT\");\n\tglad_glProgramUniform4ivEXT = (PFNGLPROGRAMUNIFORM4IVEXTPROC)load(\"glProgramUniform4ivEXT\");\n\tglad_glProgramUniformMatrix2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC)load(\"glProgramUniformMatrix2fvEXT\");\n\tglad_glProgramUniformMatrix3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC)load(\"glProgramUniformMatrix3fvEXT\");\n\tglad_glProgramUniformMatrix4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC)load(\"glProgramUniformMatrix4fvEXT\");\n\tglad_glUseProgramStagesEXT = (PFNGLUSEPROGRAMSTAGESEXTPROC)load(\"glUseProgramStagesEXT\");\n\tglad_glValidateProgramPipelineEXT = (PFNGLVALIDATEPROGRAMPIPELINEEXTPROC)load(\"glValidateProgramPipelineEXT\");\n\tglad_glProgramUniform1uiEXT = (PFNGLPROGRAMUNIFORM1UIEXTPROC)load(\"glProgramUniform1uiEXT\");\n\tglad_glProgramUniform2uiEXT = (PFNGLPROGRAMUNIFORM2UIEXTPROC)load(\"glProgramUniform2uiEXT\");\n\tglad_glProgramUniform3uiEXT = (PFNGLPROGRAMUNIFORM3UIEXTPROC)load(\"glProgramUniform3uiEXT\");\n\tglad_glProgramUniform4uiEXT = (PFNGLPROGRAMUNIFORM4UIEXTPROC)load(\"glProgramUniform4uiEXT\");\n\tglad_glProgramUniform1uivEXT = (PFNGLPROGRAMUNIFORM1UIVEXTPROC)load(\"glProgramUniform1uivEXT\");\n\tglad_glProgramUniform2uivEXT = (PFNGLPROGRAMUNIFORM2UIVEXTPROC)load(\"glProgramUniform2uivEXT\");\n\tglad_glProgramUniform3uivEXT = (PFNGLPROGRAMUNIFORM3UIVEXTPROC)load(\"glProgramUniform3uivEXT\");\n\tglad_glProgramUniform4uivEXT = (PFNGLPROGRAMUNIFORM4UIVEXTPROC)load(\"glProgramUniform4uivEXT\");\n\tglad_glProgramUniformMatrix4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC)load(\"glProgramUniformMatrix4fvEXT\");\n\tglad_glProgramUniformMatrix2x3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC)load(\"glProgramUniformMatrix2x3fvEXT\");\n\tglad_glProgramUniformMatrix3x2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC)load(\"glProgramUniformMatrix3x2fvEXT\");\n\tglad_glProgramUniformMatrix2x4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC)load(\"glProgramUniformMatrix2x4fvEXT\");\n\tglad_glProgramUniformMatrix4x2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC)load(\"glProgramUniformMatrix4x2fvEXT\");\n\tglad_glProgramUniformMatrix3x4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC)load(\"glProgramUniformMatrix3x4fvEXT\");\n\tglad_glProgramUniformMatrix4x3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC)load(\"glProgramUniformMatrix4x3fvEXT\");\n}\nstatic void load_GL_NV_texture_multisample(GLADloadproc load) {\n\tif(!GLAD_GL_NV_texture_multisample) return;\n\tglad_glTexImage2DMultisampleCoverageNV = (PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC)load(\"glTexImage2DMultisampleCoverageNV\");\n\tglad_glTexImage3DMultisampleCoverageNV = (PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC)load(\"glTexImage3DMultisampleCoverageNV\");\n\tglad_glTextureImage2DMultisampleNV = (PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC)load(\"glTextureImage2DMultisampleNV\");\n\tglad_glTextureImage3DMultisampleNV = (PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC)load(\"glTextureImage3DMultisampleNV\");\n\tglad_glTextureImage2DMultisampleCoverageNV = (PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC)load(\"glTextureImage2DMultisampleCoverageNV\");\n\tglad_glTextureImage3DMultisampleCoverageNV = (PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC)load(\"glTextureImage3DMultisampleCoverageNV\");\n}\nstatic void load_GL_ARB_shader_objects(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_shader_objects) return;\n\tglad_glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)load(\"glDeleteObjectARB\");\n\tglad_glGetHandleARB = (PFNGLGETHANDLEARBPROC)load(\"glGetHandleARB\");\n\tglad_glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC)load(\"glDetachObjectARB\");\n\tglad_glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)load(\"glCreateShaderObjectARB\");\n\tglad_glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)load(\"glShaderSourceARB\");\n\tglad_glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)load(\"glCompileShaderARB\");\n\tglad_glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)load(\"glCreateProgramObjectARB\");\n\tglad_glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)load(\"glAttachObjectARB\");\n\tglad_glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)load(\"glLinkProgramARB\");\n\tglad_glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)load(\"glUseProgramObjectARB\");\n\tglad_glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC)load(\"glValidateProgramARB\");\n\tglad_glUniform1fARB = (PFNGLUNIFORM1FARBPROC)load(\"glUniform1fARB\");\n\tglad_glUniform2fARB = (PFNGLUNIFORM2FARBPROC)load(\"glUniform2fARB\");\n\tglad_glUniform3fARB = (PFNGLUNIFORM3FARBPROC)load(\"glUniform3fARB\");\n\tglad_glUniform4fARB = (PFNGLUNIFORM4FARBPROC)load(\"glUniform4fARB\");\n\tglad_glUniform1iARB = (PFNGLUNIFORM1IARBPROC)load(\"glUniform1iARB\");\n\tglad_glUniform2iARB = (PFNGLUNIFORM2IARBPROC)load(\"glUniform2iARB\");\n\tglad_glUniform3iARB = (PFNGLUNIFORM3IARBPROC)load(\"glUniform3iARB\");\n\tglad_glUniform4iARB = (PFNGLUNIFORM4IARBPROC)load(\"glUniform4iARB\");\n\tglad_glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC)load(\"glUniform1fvARB\");\n\tglad_glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC)load(\"glUniform2fvARB\");\n\tglad_glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC)load(\"glUniform3fvARB\");\n\tglad_glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC)load(\"glUniform4fvARB\");\n\tglad_glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC)load(\"glUniform1ivARB\");\n\tglad_glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC)load(\"glUniform2ivARB\");\n\tglad_glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC)load(\"glUniform3ivARB\");\n\tglad_glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC)load(\"glUniform4ivARB\");\n\tglad_glUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC)load(\"glUniformMatrix2fvARB\");\n\tglad_glUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC)load(\"glUniformMatrix3fvARB\");\n\tglad_glUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC)load(\"glUniformMatrix4fvARB\");\n\tglad_glGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC)load(\"glGetObjectParameterfvARB\");\n\tglad_glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)load(\"glGetObjectParameterivARB\");\n\tglad_glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)load(\"glGetInfoLogARB\");\n\tglad_glGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC)load(\"glGetAttachedObjectsARB\");\n\tglad_glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)load(\"glGetUniformLocationARB\");\n\tglad_glGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC)load(\"glGetActiveUniformARB\");\n\tglad_glGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC)load(\"glGetUniformfvARB\");\n\tglad_glGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC)load(\"glGetUniformivARB\");\n\tglad_glGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC)load(\"glGetShaderSourceARB\");\n}\nstatic void load_GL_ARB_framebuffer_object(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_framebuffer_object) return;\n\tglad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)load(\"glIsRenderbuffer\");\n\tglad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)load(\"glBindRenderbuffer\");\n\tglad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)load(\"glDeleteRenderbuffers\");\n\tglad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)load(\"glGenRenderbuffers\");\n\tglad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)load(\"glRenderbufferStorage\");\n\tglad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)load(\"glGetRenderbufferParameteriv\");\n\tglad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)load(\"glIsFramebuffer\");\n\tglad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)load(\"glBindFramebuffer\");\n\tglad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)load(\"glDeleteFramebuffers\");\n\tglad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)load(\"glGenFramebuffers\");\n\tglad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)load(\"glCheckFramebufferStatus\");\n\tglad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)load(\"glFramebufferTexture1D\");\n\tglad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)load(\"glFramebufferTexture2D\");\n\tglad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)load(\"glFramebufferTexture3D\");\n\tglad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)load(\"glFramebufferRenderbuffer\");\n\tglad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)load(\"glGetFramebufferAttachmentParameteriv\");\n\tglad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)load(\"glGenerateMipmap\");\n\tglad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)load(\"glBlitFramebuffer\");\n\tglad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)load(\"glRenderbufferStorageMultisample\");\n\tglad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)load(\"glFramebufferTextureLayer\");\n}\nstatic void load_GL_ATI_envmap_bumpmap(GLADloadproc load) {\n\tif(!GLAD_GL_ATI_envmap_bumpmap) return;\n\tglad_glTexBumpParameterivATI = (PFNGLTEXBUMPPARAMETERIVATIPROC)load(\"glTexBumpParameterivATI\");\n\tglad_glTexBumpParameterfvATI = (PFNGLTEXBUMPPARAMETERFVATIPROC)load(\"glTexBumpParameterfvATI\");\n\tglad_glGetTexBumpParameterivATI = (PFNGLGETTEXBUMPPARAMETERIVATIPROC)load(\"glGetTexBumpParameterivATI\");\n\tglad_glGetTexBumpParameterfvATI = (PFNGLGETTEXBUMPPARAMETERFVATIPROC)load(\"glGetTexBumpParameterfvATI\");\n}\nstatic void load_GL_ATI_map_object_buffer(GLADloadproc load) {\n\tif(!GLAD_GL_ATI_map_object_buffer) return;\n\tglad_glMapObjectBufferATI = (PFNGLMAPOBJECTBUFFERATIPROC)load(\"glMapObjectBufferATI\");\n\tglad_glUnmapObjectBufferATI = (PFNGLUNMAPOBJECTBUFFERATIPROC)load(\"glUnmapObjectBufferATI\");\n}\nstatic void load_GL_ARB_robustness(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_robustness) return;\n\tglad_glGetGraphicsResetStatusARB = (PFNGLGETGRAPHICSRESETSTATUSARBPROC)load(\"glGetGraphicsResetStatusARB\");\n\tglad_glGetnTexImageARB = (PFNGLGETNTEXIMAGEARBPROC)load(\"glGetnTexImageARB\");\n\tglad_glReadnPixelsARB = (PFNGLREADNPIXELSARBPROC)load(\"glReadnPixelsARB\");\n\tglad_glGetnCompressedTexImageARB = (PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC)load(\"glGetnCompressedTexImageARB\");\n\tglad_glGetnUniformfvARB = (PFNGLGETNUNIFORMFVARBPROC)load(\"glGetnUniformfvARB\");\n\tglad_glGetnUniformivARB = (PFNGLGETNUNIFORMIVARBPROC)load(\"glGetnUniformivARB\");\n\tglad_glGetnUniformuivARB = (PFNGLGETNUNIFORMUIVARBPROC)load(\"glGetnUniformuivARB\");\n\tglad_glGetnUniformdvARB = (PFNGLGETNUNIFORMDVARBPROC)load(\"glGetnUniformdvARB\");\n\tglad_glGetnMapdvARB = (PFNGLGETNMAPDVARBPROC)load(\"glGetnMapdvARB\");\n\tglad_glGetnMapfvARB = (PFNGLGETNMAPFVARBPROC)load(\"glGetnMapfvARB\");\n\tglad_glGetnMapivARB = (PFNGLGETNMAPIVARBPROC)load(\"glGetnMapivARB\");\n\tglad_glGetnPixelMapfvARB = (PFNGLGETNPIXELMAPFVARBPROC)load(\"glGetnPixelMapfvARB\");\n\tglad_glGetnPixelMapuivARB = (PFNGLGETNPIXELMAPUIVARBPROC)load(\"glGetnPixelMapuivARB\");\n\tglad_glGetnPixelMapusvARB = (PFNGLGETNPIXELMAPUSVARBPROC)load(\"glGetnPixelMapusvARB\");\n\tglad_glGetnPolygonStippleARB = (PFNGLGETNPOLYGONSTIPPLEARBPROC)load(\"glGetnPolygonStippleARB\");\n\tglad_glGetnColorTableARB = (PFNGLGETNCOLORTABLEARBPROC)load(\"glGetnColorTableARB\");\n\tglad_glGetnConvolutionFilterARB = (PFNGLGETNCONVOLUTIONFILTERARBPROC)load(\"glGetnConvolutionFilterARB\");\n\tglad_glGetnSeparableFilterARB = (PFNGLGETNSEPARABLEFILTERARBPROC)load(\"glGetnSeparableFilterARB\");\n\tglad_glGetnHistogramARB = (PFNGLGETNHISTOGRAMARBPROC)load(\"glGetnHistogramARB\");\n\tglad_glGetnMinmaxARB = (PFNGLGETNMINMAXARBPROC)load(\"glGetnMinmaxARB\");\n}\nstatic void load_GL_NV_pixel_data_range(GLADloadproc load) {\n\tif(!GLAD_GL_NV_pixel_data_range) return;\n\tglad_glPixelDataRangeNV = (PFNGLPIXELDATARANGENVPROC)load(\"glPixelDataRangeNV\");\n\tglad_glFlushPixelDataRangeNV = (PFNGLFLUSHPIXELDATARANGENVPROC)load(\"glFlushPixelDataRangeNV\");\n}\nstatic void load_GL_EXT_framebuffer_blit(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_framebuffer_blit) return;\n\tglad_glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXTPROC)load(\"glBlitFramebufferEXT\");\n}\nstatic void load_GL_ARB_gpu_shader_fp64(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_gpu_shader_fp64) return;\n\tglad_glUniform1d = (PFNGLUNIFORM1DPROC)load(\"glUniform1d\");\n\tglad_glUniform2d = (PFNGLUNIFORM2DPROC)load(\"glUniform2d\");\n\tglad_glUniform3d = (PFNGLUNIFORM3DPROC)load(\"glUniform3d\");\n\tglad_glUniform4d = (PFNGLUNIFORM4DPROC)load(\"glUniform4d\");\n\tglad_glUniform1dv = (PFNGLUNIFORM1DVPROC)load(\"glUniform1dv\");\n\tglad_glUniform2dv = (PFNGLUNIFORM2DVPROC)load(\"glUniform2dv\");\n\tglad_glUniform3dv = (PFNGLUNIFORM3DVPROC)load(\"glUniform3dv\");\n\tglad_glUniform4dv = (PFNGLUNIFORM4DVPROC)load(\"glUniform4dv\");\n\tglad_glUniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC)load(\"glUniformMatrix2dv\");\n\tglad_glUniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC)load(\"glUniformMatrix3dv\");\n\tglad_glUniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC)load(\"glUniformMatrix4dv\");\n\tglad_glUniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC)load(\"glUniformMatrix2x3dv\");\n\tglad_glUniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC)load(\"glUniformMatrix2x4dv\");\n\tglad_glUniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC)load(\"glUniformMatrix3x2dv\");\n\tglad_glUniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC)load(\"glUniformMatrix3x4dv\");\n\tglad_glUniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC)load(\"glUniformMatrix4x2dv\");\n\tglad_glUniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC)load(\"glUniformMatrix4x3dv\");\n\tglad_glGetUniformdv = (PFNGLGETUNIFORMDVPROC)load(\"glGetUniformdv\");\n}\nstatic void load_GL_EXT_vertex_weighting(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_vertex_weighting) return;\n\tglad_glVertexWeightfEXT = (PFNGLVERTEXWEIGHTFEXTPROC)load(\"glVertexWeightfEXT\");\n\tglad_glVertexWeightfvEXT = (PFNGLVERTEXWEIGHTFVEXTPROC)load(\"glVertexWeightfvEXT\");\n\tglad_glVertexWeightPointerEXT = (PFNGLVERTEXWEIGHTPOINTEREXTPROC)load(\"glVertexWeightPointerEXT\");\n}\nstatic void load_GL_GREMEDY_string_marker(GLADloadproc load) {\n\tif(!GLAD_GL_GREMEDY_string_marker) return;\n\tglad_glStringMarkerGREMEDY = (PFNGLSTRINGMARKERGREMEDYPROC)load(\"glStringMarkerGREMEDY\");\n}\nstatic void load_GL_EXT_subtexture(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_subtexture) return;\n\tglad_glTexSubImage1DEXT = (PFNGLTEXSUBIMAGE1DEXTPROC)load(\"glTexSubImage1DEXT\");\n\tglad_glTexSubImage2DEXT = (PFNGLTEXSUBIMAGE2DEXTPROC)load(\"glTexSubImage2DEXT\");\n}\nstatic void load_GL_NV_evaluators(GLADloadproc load) {\n\tif(!GLAD_GL_NV_evaluators) return;\n\tglad_glMapControlPointsNV = (PFNGLMAPCONTROLPOINTSNVPROC)load(\"glMapControlPointsNV\");\n\tglad_glMapParameterivNV = (PFNGLMAPPARAMETERIVNVPROC)load(\"glMapParameterivNV\");\n\tglad_glMapParameterfvNV = (PFNGLMAPPARAMETERFVNVPROC)load(\"glMapParameterfvNV\");\n\tglad_glGetMapControlPointsNV = (PFNGLGETMAPCONTROLPOINTSNVPROC)load(\"glGetMapControlPointsNV\");\n\tglad_glGetMapParameterivNV = (PFNGLGETMAPPARAMETERIVNVPROC)load(\"glGetMapParameterivNV\");\n\tglad_glGetMapParameterfvNV = (PFNGLGETMAPPARAMETERFVNVPROC)load(\"glGetMapParameterfvNV\");\n\tglad_glGetMapAttribParameterivNV = (PFNGLGETMAPATTRIBPARAMETERIVNVPROC)load(\"glGetMapAttribParameterivNV\");\n\tglad_glGetMapAttribParameterfvNV = (PFNGLGETMAPATTRIBPARAMETERFVNVPROC)load(\"glGetMapAttribParameterfvNV\");\n\tglad_glEvalMapsNV = (PFNGLEVALMAPSNVPROC)load(\"glEvalMapsNV\");\n}\nstatic void load_GL_SGIS_texture_filter4(GLADloadproc load) {\n\tif(!GLAD_GL_SGIS_texture_filter4) return;\n\tglad_glGetTexFilterFuncSGIS = (PFNGLGETTEXFILTERFUNCSGISPROC)load(\"glGetTexFilterFuncSGIS\");\n\tglad_glTexFilterFuncSGIS = (PFNGLTEXFILTERFUNCSGISPROC)load(\"glTexFilterFuncSGIS\");\n}\nstatic void load_GL_AMD_performance_monitor(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_performance_monitor) return;\n\tglad_glGetPerfMonitorGroupsAMD = (PFNGLGETPERFMONITORGROUPSAMDPROC)load(\"glGetPerfMonitorGroupsAMD\");\n\tglad_glGetPerfMonitorCountersAMD = (PFNGLGETPERFMONITORCOUNTERSAMDPROC)load(\"glGetPerfMonitorCountersAMD\");\n\tglad_glGetPerfMonitorGroupStringAMD = (PFNGLGETPERFMONITORGROUPSTRINGAMDPROC)load(\"glGetPerfMonitorGroupStringAMD\");\n\tglad_glGetPerfMonitorCounterStringAMD = (PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC)load(\"glGetPerfMonitorCounterStringAMD\");\n\tglad_glGetPerfMonitorCounterInfoAMD = (PFNGLGETPERFMONITORCOUNTERINFOAMDPROC)load(\"glGetPerfMonitorCounterInfoAMD\");\n\tglad_glGenPerfMonitorsAMD = (PFNGLGENPERFMONITORSAMDPROC)load(\"glGenPerfMonitorsAMD\");\n\tglad_glDeletePerfMonitorsAMD = (PFNGLDELETEPERFMONITORSAMDPROC)load(\"glDeletePerfMonitorsAMD\");\n\tglad_glSelectPerfMonitorCountersAMD = (PFNGLSELECTPERFMONITORCOUNTERSAMDPROC)load(\"glSelectPerfMonitorCountersAMD\");\n\tglad_glBeginPerfMonitorAMD = (PFNGLBEGINPERFMONITORAMDPROC)load(\"glBeginPerfMonitorAMD\");\n\tglad_glEndPerfMonitorAMD = (PFNGLENDPERFMONITORAMDPROC)load(\"glEndPerfMonitorAMD\");\n\tglad_glGetPerfMonitorCounterDataAMD = (PFNGLGETPERFMONITORCOUNTERDATAAMDPROC)load(\"glGetPerfMonitorCounterDataAMD\");\n}\nstatic void load_GL_EXT_stencil_clear_tag(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_stencil_clear_tag) return;\n\tglad_glStencilClearTagEXT = (PFNGLSTENCILCLEARTAGEXTPROC)load(\"glStencilClearTagEXT\");\n}\nstatic void load_GL_NV_present_video(GLADloadproc load) {\n\tif(!GLAD_GL_NV_present_video) return;\n\tglad_glPresentFrameKeyedNV = (PFNGLPRESENTFRAMEKEYEDNVPROC)load(\"glPresentFrameKeyedNV\");\n\tglad_glPresentFrameDualFillNV = (PFNGLPRESENTFRAMEDUALFILLNVPROC)load(\"glPresentFrameDualFillNV\");\n\tglad_glGetVideoivNV = (PFNGLGETVIDEOIVNVPROC)load(\"glGetVideoivNV\");\n\tglad_glGetVideouivNV = (PFNGLGETVIDEOUIVNVPROC)load(\"glGetVideouivNV\");\n\tglad_glGetVideoi64vNV = (PFNGLGETVIDEOI64VNVPROC)load(\"glGetVideoi64vNV\");\n\tglad_glGetVideoui64vNV = (PFNGLGETVIDEOUI64VNVPROC)load(\"glGetVideoui64vNV\");\n}\nstatic void load_GL_EXT_gpu_program_parameters(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_gpu_program_parameters) return;\n\tglad_glProgramEnvParameters4fvEXT = (PFNGLPROGRAMENVPARAMETERS4FVEXTPROC)load(\"glProgramEnvParameters4fvEXT\");\n\tglad_glProgramLocalParameters4fvEXT = (PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC)load(\"glProgramLocalParameters4fvEXT\");\n}\nstatic void load_GL_ARB_draw_elements_base_vertex(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_draw_elements_base_vertex) return;\n\tglad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)load(\"glDrawElementsBaseVertex\");\n\tglad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)load(\"glDrawRangeElementsBaseVertex\");\n\tglad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)load(\"glDrawElementsInstancedBaseVertex\");\n\tglad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)load(\"glMultiDrawElementsBaseVertex\");\n}\nstatic void load_GL_NV_transform_feedback(GLADloadproc load) {\n\tif(!GLAD_GL_NV_transform_feedback) return;\n\tglad_glBeginTransformFeedbackNV = (PFNGLBEGINTRANSFORMFEEDBACKNVPROC)load(\"glBeginTransformFeedbackNV\");\n\tglad_glEndTransformFeedbackNV = (PFNGLENDTRANSFORMFEEDBACKNVPROC)load(\"glEndTransformFeedbackNV\");\n\tglad_glTransformFeedbackAttribsNV = (PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC)load(\"glTransformFeedbackAttribsNV\");\n\tglad_glBindBufferRangeNV = (PFNGLBINDBUFFERRANGENVPROC)load(\"glBindBufferRangeNV\");\n\tglad_glBindBufferOffsetNV = (PFNGLBINDBUFFEROFFSETNVPROC)load(\"glBindBufferOffsetNV\");\n\tglad_glBindBufferBaseNV = (PFNGLBINDBUFFERBASENVPROC)load(\"glBindBufferBaseNV\");\n\tglad_glTransformFeedbackVaryingsNV = (PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC)load(\"glTransformFeedbackVaryingsNV\");\n\tglad_glActiveVaryingNV = (PFNGLACTIVEVARYINGNVPROC)load(\"glActiveVaryingNV\");\n\tglad_glGetVaryingLocationNV = (PFNGLGETVARYINGLOCATIONNVPROC)load(\"glGetVaryingLocationNV\");\n\tglad_glGetActiveVaryingNV = (PFNGLGETACTIVEVARYINGNVPROC)load(\"glGetActiveVaryingNV\");\n\tglad_glGetTransformFeedbackVaryingNV = (PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC)load(\"glGetTransformFeedbackVaryingNV\");\n\tglad_glTransformFeedbackStreamAttribsNV = (PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC)load(\"glTransformFeedbackStreamAttribsNV\");\n}\nstatic void load_GL_NV_fragment_program(GLADloadproc load) {\n\tif(!GLAD_GL_NV_fragment_program) return;\n\tglad_glProgramNamedParameter4fNV = (PFNGLPROGRAMNAMEDPARAMETER4FNVPROC)load(\"glProgramNamedParameter4fNV\");\n\tglad_glProgramNamedParameter4fvNV = (PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC)load(\"glProgramNamedParameter4fvNV\");\n\tglad_glProgramNamedParameter4dNV = (PFNGLPROGRAMNAMEDPARAMETER4DNVPROC)load(\"glProgramNamedParameter4dNV\");\n\tglad_glProgramNamedParameter4dvNV = (PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC)load(\"glProgramNamedParameter4dvNV\");\n\tglad_glGetProgramNamedParameterfvNV = (PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC)load(\"glGetProgramNamedParameterfvNV\");\n\tglad_glGetProgramNamedParameterdvNV = (PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC)load(\"glGetProgramNamedParameterdvNV\");\n}\nstatic void load_GL_AMD_stencil_operation_extended(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_stencil_operation_extended) return;\n\tglad_glStencilOpValueAMD = (PFNGLSTENCILOPVALUEAMDPROC)load(\"glStencilOpValueAMD\");\n}\nstatic void load_GL_ARB_instanced_arrays(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_instanced_arrays) return;\n\tglad_glVertexAttribDivisorARB = (PFNGLVERTEXATTRIBDIVISORARBPROC)load(\"glVertexAttribDivisorARB\");\n}\nstatic void load_GL_EXT_polygon_offset(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_polygon_offset) return;\n\tglad_glPolygonOffsetEXT = (PFNGLPOLYGONOFFSETEXTPROC)load(\"glPolygonOffsetEXT\");\n}\nstatic void load_GL_AMD_sparse_texture(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_sparse_texture) return;\n\tglad_glTexStorageSparseAMD = (PFNGLTEXSTORAGESPARSEAMDPROC)load(\"glTexStorageSparseAMD\");\n\tglad_glTextureStorageSparseAMD = (PFNGLTEXTURESTORAGESPARSEAMDPROC)load(\"glTextureStorageSparseAMD\");\n}\nstatic void load_GL_NV_fence(GLADloadproc load) {\n\tif(!GLAD_GL_NV_fence) return;\n\tglad_glDeleteFencesNV = (PFNGLDELETEFENCESNVPROC)load(\"glDeleteFencesNV\");\n\tglad_glGenFencesNV = (PFNGLGENFENCESNVPROC)load(\"glGenFencesNV\");\n\tglad_glIsFenceNV = (PFNGLISFENCENVPROC)load(\"glIsFenceNV\");\n\tglad_glTestFenceNV = (PFNGLTESTFENCENVPROC)load(\"glTestFenceNV\");\n\tglad_glGetFenceivNV = (PFNGLGETFENCEIVNVPROC)load(\"glGetFenceivNV\");\n\tglad_glFinishFenceNV = (PFNGLFINISHFENCENVPROC)load(\"glFinishFenceNV\");\n\tglad_glSetFenceNV = (PFNGLSETFENCENVPROC)load(\"glSetFenceNV\");\n}\nstatic void load_GL_ARB_texture_buffer_range(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_texture_buffer_range) return;\n\tglad_glTexBufferRange = (PFNGLTEXBUFFERRANGEPROC)load(\"glTexBufferRange\");\n}\nstatic void load_GL_SUN_mesh_array(GLADloadproc load) {\n\tif(!GLAD_GL_SUN_mesh_array) return;\n\tglad_glDrawMeshArraysSUN = (PFNGLDRAWMESHARRAYSSUNPROC)load(\"glDrawMeshArraysSUN\");\n}\nstatic void load_GL_ARB_vertex_attrib_binding(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_vertex_attrib_binding) return;\n\tglad_glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC)load(\"glBindVertexBuffer\");\n\tglad_glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC)load(\"glVertexAttribFormat\");\n\tglad_glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC)load(\"glVertexAttribIFormat\");\n\tglad_glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC)load(\"glVertexAttribLFormat\");\n\tglad_glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC)load(\"glVertexAttribBinding\");\n\tglad_glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC)load(\"glVertexBindingDivisor\");\n}\nstatic void load_GL_ARB_framebuffer_no_attachments(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_framebuffer_no_attachments) return;\n\tglad_glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC)load(\"glFramebufferParameteri\");\n\tglad_glGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC)load(\"glGetFramebufferParameteriv\");\n}\nstatic void load_GL_ARB_cl_event(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_cl_event) return;\n\tglad_glCreateSyncFromCLeventARB = (PFNGLCREATESYNCFROMCLEVENTARBPROC)load(\"glCreateSyncFromCLeventARB\");\n}\nstatic void load_GL_OES_single_precision(GLADloadproc load) {\n\tif(!GLAD_GL_OES_single_precision) return;\n\tglad_glClearDepthfOES = (PFNGLCLEARDEPTHFOESPROC)load(\"glClearDepthfOES\");\n\tglad_glClipPlanefOES = (PFNGLCLIPPLANEFOESPROC)load(\"glClipPlanefOES\");\n\tglad_glDepthRangefOES = (PFNGLDEPTHRANGEFOESPROC)load(\"glDepthRangefOES\");\n\tglad_glFrustumfOES = (PFNGLFRUSTUMFOESPROC)load(\"glFrustumfOES\");\n\tglad_glGetClipPlanefOES = (PFNGLGETCLIPPLANEFOESPROC)load(\"glGetClipPlanefOES\");\n\tglad_glOrthofOES = (PFNGLORTHOFOESPROC)load(\"glOrthofOES\");\n}\nstatic void load_GL_NV_primitive_restart(GLADloadproc load) {\n\tif(!GLAD_GL_NV_primitive_restart) return;\n\tglad_glPrimitiveRestartNV = (PFNGLPRIMITIVERESTARTNVPROC)load(\"glPrimitiveRestartNV\");\n\tglad_glPrimitiveRestartIndexNV = (PFNGLPRIMITIVERESTARTINDEXNVPROC)load(\"glPrimitiveRestartIndexNV\");\n}\nstatic void load_GL_SUN_global_alpha(GLADloadproc load) {\n\tif(!GLAD_GL_SUN_global_alpha) return;\n\tglad_glGlobalAlphaFactorbSUN = (PFNGLGLOBALALPHAFACTORBSUNPROC)load(\"glGlobalAlphaFactorbSUN\");\n\tglad_glGlobalAlphaFactorsSUN = (PFNGLGLOBALALPHAFACTORSSUNPROC)load(\"glGlobalAlphaFactorsSUN\");\n\tglad_glGlobalAlphaFactoriSUN = (PFNGLGLOBALALPHAFACTORISUNPROC)load(\"glGlobalAlphaFactoriSUN\");\n\tglad_glGlobalAlphaFactorfSUN = (PFNGLGLOBALALPHAFACTORFSUNPROC)load(\"glGlobalAlphaFactorfSUN\");\n\tglad_glGlobalAlphaFactordSUN = (PFNGLGLOBALALPHAFACTORDSUNPROC)load(\"glGlobalAlphaFactordSUN\");\n\tglad_glGlobalAlphaFactorubSUN = (PFNGLGLOBALALPHAFACTORUBSUNPROC)load(\"glGlobalAlphaFactorubSUN\");\n\tglad_glGlobalAlphaFactorusSUN = (PFNGLGLOBALALPHAFACTORUSSUNPROC)load(\"glGlobalAlphaFactorusSUN\");\n\tglad_glGlobalAlphaFactoruiSUN = (PFNGLGLOBALALPHAFACTORUISUNPROC)load(\"glGlobalAlphaFactoruiSUN\");\n}\nstatic void load_GL_EXT_texture_object(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_texture_object) return;\n\tglad_glAreTexturesResidentEXT = (PFNGLARETEXTURESRESIDENTEXTPROC)load(\"glAreTexturesResidentEXT\");\n\tglad_glBindTextureEXT = (PFNGLBINDTEXTUREEXTPROC)load(\"glBindTextureEXT\");\n\tglad_glDeleteTexturesEXT = (PFNGLDELETETEXTURESEXTPROC)load(\"glDeleteTexturesEXT\");\n\tglad_glGenTexturesEXT = (PFNGLGENTEXTURESEXTPROC)load(\"glGenTexturesEXT\");\n\tglad_glIsTextureEXT = (PFNGLISTEXTUREEXTPROC)load(\"glIsTextureEXT\");\n\tglad_glPrioritizeTexturesEXT = (PFNGLPRIORITIZETEXTURESEXTPROC)load(\"glPrioritizeTexturesEXT\");\n}\nstatic void load_GL_AMD_name_gen_delete(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_name_gen_delete) return;\n\tglad_glGenNamesAMD = (PFNGLGENNAMESAMDPROC)load(\"glGenNamesAMD\");\n\tglad_glDeleteNamesAMD = (PFNGLDELETENAMESAMDPROC)load(\"glDeleteNamesAMD\");\n\tglad_glIsNameAMD = (PFNGLISNAMEAMDPROC)load(\"glIsNameAMD\");\n}\nstatic void load_GL_ARB_buffer_storage(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_buffer_storage) return;\n\tglad_glBufferStorage = (PFNGLBUFFERSTORAGEPROC)load(\"glBufferStorage\");\n}\nstatic void load_GL_APPLE_vertex_program_evaluators(GLADloadproc load) {\n\tif(!GLAD_GL_APPLE_vertex_program_evaluators) return;\n\tglad_glEnableVertexAttribAPPLE = (PFNGLENABLEVERTEXATTRIBAPPLEPROC)load(\"glEnableVertexAttribAPPLE\");\n\tglad_glDisableVertexAttribAPPLE = (PFNGLDISABLEVERTEXATTRIBAPPLEPROC)load(\"glDisableVertexAttribAPPLE\");\n\tglad_glIsVertexAttribEnabledAPPLE = (PFNGLISVERTEXATTRIBENABLEDAPPLEPROC)load(\"glIsVertexAttribEnabledAPPLE\");\n\tglad_glMapVertexAttrib1dAPPLE = (PFNGLMAPVERTEXATTRIB1DAPPLEPROC)load(\"glMapVertexAttrib1dAPPLE\");\n\tglad_glMapVertexAttrib1fAPPLE = (PFNGLMAPVERTEXATTRIB1FAPPLEPROC)load(\"glMapVertexAttrib1fAPPLE\");\n\tglad_glMapVertexAttrib2dAPPLE = (PFNGLMAPVERTEXATTRIB2DAPPLEPROC)load(\"glMapVertexAttrib2dAPPLE\");\n\tglad_glMapVertexAttrib2fAPPLE = (PFNGLMAPVERTEXATTRIB2FAPPLEPROC)load(\"glMapVertexAttrib2fAPPLE\");\n}\nstatic void load_GL_ARB_multi_bind(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_multi_bind) return;\n\tglad_glBindBuffersBase = (PFNGLBINDBUFFERSBASEPROC)load(\"glBindBuffersBase\");\n\tglad_glBindBuffersRange = (PFNGLBINDBUFFERSRANGEPROC)load(\"glBindBuffersRange\");\n\tglad_glBindTextures = (PFNGLBINDTEXTURESPROC)load(\"glBindTextures\");\n\tglad_glBindSamplers = (PFNGLBINDSAMPLERSPROC)load(\"glBindSamplers\");\n\tglad_glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC)load(\"glBindImageTextures\");\n\tglad_glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC)load(\"glBindVertexBuffers\");\n}\nstatic void load_GL_SGIX_list_priority(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_list_priority) return;\n\tglad_glGetListParameterfvSGIX = (PFNGLGETLISTPARAMETERFVSGIXPROC)load(\"glGetListParameterfvSGIX\");\n\tglad_glGetListParameterivSGIX = (PFNGLGETLISTPARAMETERIVSGIXPROC)load(\"glGetListParameterivSGIX\");\n\tglad_glListParameterfSGIX = (PFNGLLISTPARAMETERFSGIXPROC)load(\"glListParameterfSGIX\");\n\tglad_glListParameterfvSGIX = (PFNGLLISTPARAMETERFVSGIXPROC)load(\"glListParameterfvSGIX\");\n\tglad_glListParameteriSGIX = (PFNGLLISTPARAMETERISGIXPROC)load(\"glListParameteriSGIX\");\n\tglad_glListParameterivSGIX = (PFNGLLISTPARAMETERIVSGIXPROC)load(\"glListParameterivSGIX\");\n}\nstatic void load_GL_NV_vertex_buffer_unified_memory(GLADloadproc load) {\n\tif(!GLAD_GL_NV_vertex_buffer_unified_memory) return;\n\tglad_glBufferAddressRangeNV = (PFNGLBUFFERADDRESSRANGENVPROC)load(\"glBufferAddressRangeNV\");\n\tglad_glVertexFormatNV = (PFNGLVERTEXFORMATNVPROC)load(\"glVertexFormatNV\");\n\tglad_glNormalFormatNV = (PFNGLNORMALFORMATNVPROC)load(\"glNormalFormatNV\");\n\tglad_glColorFormatNV = (PFNGLCOLORFORMATNVPROC)load(\"glColorFormatNV\");\n\tglad_glIndexFormatNV = (PFNGLINDEXFORMATNVPROC)load(\"glIndexFormatNV\");\n\tglad_glTexCoordFormatNV = (PFNGLTEXCOORDFORMATNVPROC)load(\"glTexCoordFormatNV\");\n\tglad_glEdgeFlagFormatNV = (PFNGLEDGEFLAGFORMATNVPROC)load(\"glEdgeFlagFormatNV\");\n\tglad_glSecondaryColorFormatNV = (PFNGLSECONDARYCOLORFORMATNVPROC)load(\"glSecondaryColorFormatNV\");\n\tglad_glFogCoordFormatNV = (PFNGLFOGCOORDFORMATNVPROC)load(\"glFogCoordFormatNV\");\n\tglad_glVertexAttribFormatNV = (PFNGLVERTEXATTRIBFORMATNVPROC)load(\"glVertexAttribFormatNV\");\n\tglad_glVertexAttribIFormatNV = (PFNGLVERTEXATTRIBIFORMATNVPROC)load(\"glVertexAttribIFormatNV\");\n\tglad_glGetIntegerui64i_vNV = (PFNGLGETINTEGERUI64I_VNVPROC)load(\"glGetIntegerui64i_vNV\");\n}\nstatic void load_GL_NV_blend_equation_advanced(GLADloadproc load) {\n\tif(!GLAD_GL_NV_blend_equation_advanced) return;\n\tglad_glBlendParameteriNV = (PFNGLBLENDPARAMETERINVPROC)load(\"glBlendParameteriNV\");\n\tglad_glBlendBarrierNV = (PFNGLBLENDBARRIERNVPROC)load(\"glBlendBarrierNV\");\n}\nstatic void load_GL_SGIS_sharpen_texture(GLADloadproc load) {\n\tif(!GLAD_GL_SGIS_sharpen_texture) return;\n\tglad_glSharpenTexFuncSGIS = (PFNGLSHARPENTEXFUNCSGISPROC)load(\"glSharpenTexFuncSGIS\");\n\tglad_glGetSharpenTexFuncSGIS = (PFNGLGETSHARPENTEXFUNCSGISPROC)load(\"glGetSharpenTexFuncSGIS\");\n}\nstatic void load_GL_ARB_vertex_program(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_vertex_program) return;\n\tglad_glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC)load(\"glVertexAttrib1dARB\");\n\tglad_glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC)load(\"glVertexAttrib1dvARB\");\n\tglad_glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC)load(\"glVertexAttrib1fARB\");\n\tglad_glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC)load(\"glVertexAttrib1fvARB\");\n\tglad_glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC)load(\"glVertexAttrib1sARB\");\n\tglad_glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC)load(\"glVertexAttrib1svARB\");\n\tglad_glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC)load(\"glVertexAttrib2dARB\");\n\tglad_glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC)load(\"glVertexAttrib2dvARB\");\n\tglad_glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC)load(\"glVertexAttrib2fARB\");\n\tglad_glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC)load(\"glVertexAttrib2fvARB\");\n\tglad_glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC)load(\"glVertexAttrib2sARB\");\n\tglad_glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC)load(\"glVertexAttrib2svARB\");\n\tglad_glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC)load(\"glVertexAttrib3dARB\");\n\tglad_glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC)load(\"glVertexAttrib3dvARB\");\n\tglad_glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC)load(\"glVertexAttrib3fARB\");\n\tglad_glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC)load(\"glVertexAttrib3fvARB\");\n\tglad_glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC)load(\"glVertexAttrib3sARB\");\n\tglad_glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC)load(\"glVertexAttrib3svARB\");\n\tglad_glVertexAttrib4NbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC)load(\"glVertexAttrib4NbvARB\");\n\tglad_glVertexAttrib4NivARB = (PFNGLVERTEXATTRIB4NIVARBPROC)load(\"glVertexAttrib4NivARB\");\n\tglad_glVertexAttrib4NsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC)load(\"glVertexAttrib4NsvARB\");\n\tglad_glVertexAttrib4NubARB = (PFNGLVERTEXATTRIB4NUBARBPROC)load(\"glVertexAttrib4NubARB\");\n\tglad_glVertexAttrib4NubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC)load(\"glVertexAttrib4NubvARB\");\n\tglad_glVertexAttrib4NuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC)load(\"glVertexAttrib4NuivARB\");\n\tglad_glVertexAttrib4NusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC)load(\"glVertexAttrib4NusvARB\");\n\tglad_glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC)load(\"glVertexAttrib4bvARB\");\n\tglad_glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC)load(\"glVertexAttrib4dARB\");\n\tglad_glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC)load(\"glVertexAttrib4dvARB\");\n\tglad_glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC)load(\"glVertexAttrib4fARB\");\n\tglad_glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC)load(\"glVertexAttrib4fvARB\");\n\tglad_glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC)load(\"glVertexAttrib4ivARB\");\n\tglad_glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC)load(\"glVertexAttrib4sARB\");\n\tglad_glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC)load(\"glVertexAttrib4svARB\");\n\tglad_glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC)load(\"glVertexAttrib4ubvARB\");\n\tglad_glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC)load(\"glVertexAttrib4uivARB\");\n\tglad_glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC)load(\"glVertexAttrib4usvARB\");\n\tglad_glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC)load(\"glVertexAttribPointerARB\");\n\tglad_glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC)load(\"glEnableVertexAttribArrayARB\");\n\tglad_glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC)load(\"glDisableVertexAttribArrayARB\");\n\tglad_glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC)load(\"glProgramStringARB\");\n\tglad_glBindProgramARB = (PFNGLBINDPROGRAMARBPROC)load(\"glBindProgramARB\");\n\tglad_glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC)load(\"glDeleteProgramsARB\");\n\tglad_glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC)load(\"glGenProgramsARB\");\n\tglad_glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC)load(\"glProgramEnvParameter4dARB\");\n\tglad_glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC)load(\"glProgramEnvParameter4dvARB\");\n\tglad_glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC)load(\"glProgramEnvParameter4fARB\");\n\tglad_glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC)load(\"glProgramEnvParameter4fvARB\");\n\tglad_glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC)load(\"glProgramLocalParameter4dARB\");\n\tglad_glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC)load(\"glProgramLocalParameter4dvARB\");\n\tglad_glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC)load(\"glProgramLocalParameter4fARB\");\n\tglad_glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)load(\"glProgramLocalParameter4fvARB\");\n\tglad_glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC)load(\"glGetProgramEnvParameterdvARB\");\n\tglad_glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC)load(\"glGetProgramEnvParameterfvARB\");\n\tglad_glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC)load(\"glGetProgramLocalParameterdvARB\");\n\tglad_glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC)load(\"glGetProgramLocalParameterfvARB\");\n\tglad_glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC)load(\"glGetProgramivARB\");\n\tglad_glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC)load(\"glGetProgramStringARB\");\n\tglad_glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC)load(\"glGetVertexAttribdvARB\");\n\tglad_glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC)load(\"glGetVertexAttribfvARB\");\n\tglad_glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC)load(\"glGetVertexAttribivARB\");\n\tglad_glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC)load(\"glGetVertexAttribPointervARB\");\n\tglad_glIsProgramARB = (PFNGLISPROGRAMARBPROC)load(\"glIsProgramARB\");\n}\nstatic void load_GL_ARB_vertex_buffer_object(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_vertex_buffer_object) return;\n\tglad_glBindBufferARB = (PFNGLBINDBUFFERARBPROC)load(\"glBindBufferARB\");\n\tglad_glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)load(\"glDeleteBuffersARB\");\n\tglad_glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)load(\"glGenBuffersARB\");\n\tglad_glIsBufferARB = (PFNGLISBUFFERARBPROC)load(\"glIsBufferARB\");\n\tglad_glBufferDataARB = (PFNGLBUFFERDATAARBPROC)load(\"glBufferDataARB\");\n\tglad_glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)load(\"glBufferSubDataARB\");\n\tglad_glGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC)load(\"glGetBufferSubDataARB\");\n\tglad_glMapBufferARB = (PFNGLMAPBUFFERARBPROC)load(\"glMapBufferARB\");\n\tglad_glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)load(\"glUnmapBufferARB\");\n\tglad_glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)load(\"glGetBufferParameterivARB\");\n\tglad_glGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC)load(\"glGetBufferPointervARB\");\n}\nstatic void load_GL_NV_vertex_array_range(GLADloadproc load) {\n\tif(!GLAD_GL_NV_vertex_array_range) return;\n\tglad_glFlushVertexArrayRangeNV = (PFNGLFLUSHVERTEXARRAYRANGENVPROC)load(\"glFlushVertexArrayRangeNV\");\n\tglad_glVertexArrayRangeNV = (PFNGLVERTEXARRAYRANGENVPROC)load(\"glVertexArrayRangeNV\");\n}\nstatic void load_GL_SGIX_fragment_lighting(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_fragment_lighting) return;\n\tglad_glFragmentColorMaterialSGIX = (PFNGLFRAGMENTCOLORMATERIALSGIXPROC)load(\"glFragmentColorMaterialSGIX\");\n\tglad_glFragmentLightfSGIX = (PFNGLFRAGMENTLIGHTFSGIXPROC)load(\"glFragmentLightfSGIX\");\n\tglad_glFragmentLightfvSGIX = (PFNGLFRAGMENTLIGHTFVSGIXPROC)load(\"glFragmentLightfvSGIX\");\n\tglad_glFragmentLightiSGIX = (PFNGLFRAGMENTLIGHTISGIXPROC)load(\"glFragmentLightiSGIX\");\n\tglad_glFragmentLightivSGIX = (PFNGLFRAGMENTLIGHTIVSGIXPROC)load(\"glFragmentLightivSGIX\");\n\tglad_glFragmentLightModelfSGIX = (PFNGLFRAGMENTLIGHTMODELFSGIXPROC)load(\"glFragmentLightModelfSGIX\");\n\tglad_glFragmentLightModelfvSGIX = (PFNGLFRAGMENTLIGHTMODELFVSGIXPROC)load(\"glFragmentLightModelfvSGIX\");\n\tglad_glFragmentLightModeliSGIX = (PFNGLFRAGMENTLIGHTMODELISGIXPROC)load(\"glFragmentLightModeliSGIX\");\n\tglad_glFragmentLightModelivSGIX = (PFNGLFRAGMENTLIGHTMODELIVSGIXPROC)load(\"glFragmentLightModelivSGIX\");\n\tglad_glFragmentMaterialfSGIX = (PFNGLFRAGMENTMATERIALFSGIXPROC)load(\"glFragmentMaterialfSGIX\");\n\tglad_glFragmentMaterialfvSGIX = (PFNGLFRAGMENTMATERIALFVSGIXPROC)load(\"glFragmentMaterialfvSGIX\");\n\tglad_glFragmentMaterialiSGIX = (PFNGLFRAGMENTMATERIALISGIXPROC)load(\"glFragmentMaterialiSGIX\");\n\tglad_glFragmentMaterialivSGIX = (PFNGLFRAGMENTMATERIALIVSGIXPROC)load(\"glFragmentMaterialivSGIX\");\n\tglad_glGetFragmentLightfvSGIX = (PFNGLGETFRAGMENTLIGHTFVSGIXPROC)load(\"glGetFragmentLightfvSGIX\");\n\tglad_glGetFragmentLightivSGIX = (PFNGLGETFRAGMENTLIGHTIVSGIXPROC)load(\"glGetFragmentLightivSGIX\");\n\tglad_glGetFragmentMaterialfvSGIX = (PFNGLGETFRAGMENTMATERIALFVSGIXPROC)load(\"glGetFragmentMaterialfvSGIX\");\n\tglad_glGetFragmentMaterialivSGIX = (PFNGLGETFRAGMENTMATERIALIVSGIXPROC)load(\"glGetFragmentMaterialivSGIX\");\n\tglad_glLightEnviSGIX = (PFNGLLIGHTENVISGIXPROC)load(\"glLightEnviSGIX\");\n}\nstatic void load_GL_NV_framebuffer_multisample_coverage(GLADloadproc load) {\n\tif(!GLAD_GL_NV_framebuffer_multisample_coverage) return;\n\tglad_glRenderbufferStorageMultisampleCoverageNV = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC)load(\"glRenderbufferStorageMultisampleCoverageNV\");\n}\nstatic void load_GL_EXT_timer_query(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_timer_query) return;\n\tglad_glGetQueryObjecti64vEXT = (PFNGLGETQUERYOBJECTI64VEXTPROC)load(\"glGetQueryObjecti64vEXT\");\n\tglad_glGetQueryObjectui64vEXT = (PFNGLGETQUERYOBJECTUI64VEXTPROC)load(\"glGetQueryObjectui64vEXT\");\n}\nstatic void load_GL_NV_bindless_texture(GLADloadproc load) {\n\tif(!GLAD_GL_NV_bindless_texture) return;\n\tglad_glGetTextureHandleNV = (PFNGLGETTEXTUREHANDLENVPROC)load(\"glGetTextureHandleNV\");\n\tglad_glGetTextureSamplerHandleNV = (PFNGLGETTEXTURESAMPLERHANDLENVPROC)load(\"glGetTextureSamplerHandleNV\");\n\tglad_glMakeTextureHandleResidentNV = (PFNGLMAKETEXTUREHANDLERESIDENTNVPROC)load(\"glMakeTextureHandleResidentNV\");\n\tglad_glMakeTextureHandleNonResidentNV = (PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC)load(\"glMakeTextureHandleNonResidentNV\");\n\tglad_glGetImageHandleNV = (PFNGLGETIMAGEHANDLENVPROC)load(\"glGetImageHandleNV\");\n\tglad_glMakeImageHandleResidentNV = (PFNGLMAKEIMAGEHANDLERESIDENTNVPROC)load(\"glMakeImageHandleResidentNV\");\n\tglad_glMakeImageHandleNonResidentNV = (PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC)load(\"glMakeImageHandleNonResidentNV\");\n\tglad_glUniformHandleui64NV = (PFNGLUNIFORMHANDLEUI64NVPROC)load(\"glUniformHandleui64NV\");\n\tglad_glUniformHandleui64vNV = (PFNGLUNIFORMHANDLEUI64VNVPROC)load(\"glUniformHandleui64vNV\");\n\tglad_glProgramUniformHandleui64NV = (PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC)load(\"glProgramUniformHandleui64NV\");\n\tglad_glProgramUniformHandleui64vNV = (PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC)load(\"glProgramUniformHandleui64vNV\");\n\tglad_glIsTextureHandleResidentNV = (PFNGLISTEXTUREHANDLERESIDENTNVPROC)load(\"glIsTextureHandleResidentNV\");\n\tglad_glIsImageHandleResidentNV = (PFNGLISIMAGEHANDLERESIDENTNVPROC)load(\"glIsImageHandleResidentNV\");\n}\nstatic void load_GL_KHR_debug(GLADloadproc load) {\n\tif(!GLAD_GL_KHR_debug) return;\n\tglad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)load(\"glDebugMessageControl\");\n\tglad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC)load(\"glDebugMessageInsert\");\n\tglad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)load(\"glDebugMessageCallback\");\n\tglad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC)load(\"glGetDebugMessageLog\");\n\tglad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC)load(\"glPushDebugGroup\");\n\tglad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC)load(\"glPopDebugGroup\");\n\tglad_glObjectLabel = (PFNGLOBJECTLABELPROC)load(\"glObjectLabel\");\n\tglad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC)load(\"glGetObjectLabel\");\n\tglad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC)load(\"glObjectPtrLabel\");\n\tglad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC)load(\"glGetObjectPtrLabel\");\n\tglad_glGetPointerv = (PFNGLGETPOINTERVPROC)load(\"glGetPointerv\");\n\tglad_glDebugMessageControlKHR = (PFNGLDEBUGMESSAGECONTROLKHRPROC)load(\"glDebugMessageControlKHR\");\n\tglad_glDebugMessageInsertKHR = (PFNGLDEBUGMESSAGEINSERTKHRPROC)load(\"glDebugMessageInsertKHR\");\n\tglad_glDebugMessageCallbackKHR = (PFNGLDEBUGMESSAGECALLBACKKHRPROC)load(\"glDebugMessageCallbackKHR\");\n\tglad_glGetDebugMessageLogKHR = (PFNGLGETDEBUGMESSAGELOGKHRPROC)load(\"glGetDebugMessageLogKHR\");\n\tglad_glPushDebugGroupKHR = (PFNGLPUSHDEBUGGROUPKHRPROC)load(\"glPushDebugGroupKHR\");\n\tglad_glPopDebugGroupKHR = (PFNGLPOPDEBUGGROUPKHRPROC)load(\"glPopDebugGroupKHR\");\n\tglad_glObjectLabelKHR = (PFNGLOBJECTLABELKHRPROC)load(\"glObjectLabelKHR\");\n\tglad_glGetObjectLabelKHR = (PFNGLGETOBJECTLABELKHRPROC)load(\"glGetObjectLabelKHR\");\n\tglad_glObjectPtrLabelKHR = (PFNGLOBJECTPTRLABELKHRPROC)load(\"glObjectPtrLabelKHR\");\n\tglad_glGetObjectPtrLabelKHR = (PFNGLGETOBJECTPTRLABELKHRPROC)load(\"glGetObjectPtrLabelKHR\");\n\tglad_glGetPointervKHR = (PFNGLGETPOINTERVKHRPROC)load(\"glGetPointervKHR\");\n}\nstatic void load_GL_ATI_vertex_attrib_array_object(GLADloadproc load) {\n\tif(!GLAD_GL_ATI_vertex_attrib_array_object) return;\n\tglad_glVertexAttribArrayObjectATI = (PFNGLVERTEXATTRIBARRAYOBJECTATIPROC)load(\"glVertexAttribArrayObjectATI\");\n\tglad_glGetVertexAttribArrayObjectfvATI = (PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC)load(\"glGetVertexAttribArrayObjectfvATI\");\n\tglad_glGetVertexAttribArrayObjectivATI = (PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC)load(\"glGetVertexAttribArrayObjectivATI\");\n}\nstatic void load_GL_EXT_geometry_shader4(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_geometry_shader4) return;\n\tglad_glProgramParameteriEXT = (PFNGLPROGRAMPARAMETERIEXTPROC)load(\"glProgramParameteriEXT\");\n}\nstatic void load_GL_EXT_bindable_uniform(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_bindable_uniform) return;\n\tglad_glUniformBufferEXT = (PFNGLUNIFORMBUFFEREXTPROC)load(\"glUniformBufferEXT\");\n\tglad_glGetUniformBufferSizeEXT = (PFNGLGETUNIFORMBUFFERSIZEEXTPROC)load(\"glGetUniformBufferSizeEXT\");\n\tglad_glGetUniformOffsetEXT = (PFNGLGETUNIFORMOFFSETEXTPROC)load(\"glGetUniformOffsetEXT\");\n}\nstatic void load_GL_ATI_element_array(GLADloadproc load) {\n\tif(!GLAD_GL_ATI_element_array) return;\n\tglad_glElementPointerATI = (PFNGLELEMENTPOINTERATIPROC)load(\"glElementPointerATI\");\n\tglad_glDrawElementArrayATI = (PFNGLDRAWELEMENTARRAYATIPROC)load(\"glDrawElementArrayATI\");\n\tglad_glDrawRangeElementArrayATI = (PFNGLDRAWRANGEELEMENTARRAYATIPROC)load(\"glDrawRangeElementArrayATI\");\n}\nstatic void load_GL_SGIX_reference_plane(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_reference_plane) return;\n\tglad_glReferencePlaneSGIX = (PFNGLREFERENCEPLANESGIXPROC)load(\"glReferencePlaneSGIX\");\n}\nstatic void load_GL_EXT_stencil_two_side(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_stencil_two_side) return;\n\tglad_glActiveStencilFaceEXT = (PFNGLACTIVESTENCILFACEEXTPROC)load(\"glActiveStencilFaceEXT\");\n}\nstatic void load_GL_NV_explicit_multisample(GLADloadproc load) {\n\tif(!GLAD_GL_NV_explicit_multisample) return;\n\tglad_glGetMultisamplefvNV = (PFNGLGETMULTISAMPLEFVNVPROC)load(\"glGetMultisamplefvNV\");\n\tglad_glSampleMaskIndexedNV = (PFNGLSAMPLEMASKINDEXEDNVPROC)load(\"glSampleMaskIndexedNV\");\n\tglad_glTexRenderbufferNV = (PFNGLTEXRENDERBUFFERNVPROC)load(\"glTexRenderbufferNV\");\n}\nstatic void load_GL_IBM_static_data(GLADloadproc load) {\n\tif(!GLAD_GL_IBM_static_data) return;\n\tglad_glFlushStaticDataIBM = (PFNGLFLUSHSTATICDATAIBMPROC)load(\"glFlushStaticDataIBM\");\n}\nstatic void load_GL_EXT_texture_perturb_normal(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_texture_perturb_normal) return;\n\tglad_glTextureNormalEXT = (PFNGLTEXTURENORMALEXTPROC)load(\"glTextureNormalEXT\");\n}\nstatic void load_GL_EXT_point_parameters(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_point_parameters) return;\n\tglad_glPointParameterfEXT = (PFNGLPOINTPARAMETERFEXTPROC)load(\"glPointParameterfEXT\");\n\tglad_glPointParameterfvEXT = (PFNGLPOINTPARAMETERFVEXTPROC)load(\"glPointParameterfvEXT\");\n}\nstatic void load_GL_PGI_misc_hints(GLADloadproc load) {\n\tif(!GLAD_GL_PGI_misc_hints) return;\n\tglad_glHintPGI = (PFNGLHINTPGIPROC)load(\"glHintPGI\");\n}\nstatic void load_GL_ARB_vertex_shader(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_vertex_shader) return;\n\tglad_glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC)load(\"glVertexAttrib1fARB\");\n\tglad_glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC)load(\"glVertexAttrib1sARB\");\n\tglad_glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC)load(\"glVertexAttrib1dARB\");\n\tglad_glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC)load(\"glVertexAttrib2fARB\");\n\tglad_glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC)load(\"glVertexAttrib2sARB\");\n\tglad_glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC)load(\"glVertexAttrib2dARB\");\n\tglad_glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC)load(\"glVertexAttrib3fARB\");\n\tglad_glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC)load(\"glVertexAttrib3sARB\");\n\tglad_glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC)load(\"glVertexAttrib3dARB\");\n\tglad_glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC)load(\"glVertexAttrib4fARB\");\n\tglad_glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC)load(\"glVertexAttrib4sARB\");\n\tglad_glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC)load(\"glVertexAttrib4dARB\");\n\tglad_glVertexAttrib4NubARB = (PFNGLVERTEXATTRIB4NUBARBPROC)load(\"glVertexAttrib4NubARB\");\n\tglad_glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC)load(\"glVertexAttrib1fvARB\");\n\tglad_glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC)load(\"glVertexAttrib1svARB\");\n\tglad_glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC)load(\"glVertexAttrib1dvARB\");\n\tglad_glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC)load(\"glVertexAttrib2fvARB\");\n\tglad_glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC)load(\"glVertexAttrib2svARB\");\n\tglad_glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC)load(\"glVertexAttrib2dvARB\");\n\tglad_glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC)load(\"glVertexAttrib3fvARB\");\n\tglad_glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC)load(\"glVertexAttrib3svARB\");\n\tglad_glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC)load(\"glVertexAttrib3dvARB\");\n\tglad_glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC)load(\"glVertexAttrib4fvARB\");\n\tglad_glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC)load(\"glVertexAttrib4svARB\");\n\tglad_glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC)load(\"glVertexAttrib4dvARB\");\n\tglad_glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC)load(\"glVertexAttrib4ivARB\");\n\tglad_glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC)load(\"glVertexAttrib4bvARB\");\n\tglad_glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC)load(\"glVertexAttrib4ubvARB\");\n\tglad_glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC)load(\"glVertexAttrib4usvARB\");\n\tglad_glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC)load(\"glVertexAttrib4uivARB\");\n\tglad_glVertexAttrib4NbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC)load(\"glVertexAttrib4NbvARB\");\n\tglad_glVertexAttrib4NsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC)load(\"glVertexAttrib4NsvARB\");\n\tglad_glVertexAttrib4NivARB = (PFNGLVERTEXATTRIB4NIVARBPROC)load(\"glVertexAttrib4NivARB\");\n\tglad_glVertexAttrib4NubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC)load(\"glVertexAttrib4NubvARB\");\n\tglad_glVertexAttrib4NusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC)load(\"glVertexAttrib4NusvARB\");\n\tglad_glVertexAttrib4NuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC)load(\"glVertexAttrib4NuivARB\");\n\tglad_glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC)load(\"glVertexAttribPointerARB\");\n\tglad_glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC)load(\"glEnableVertexAttribArrayARB\");\n\tglad_glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC)load(\"glDisableVertexAttribArrayARB\");\n\tglad_glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC)load(\"glBindAttribLocationARB\");\n\tglad_glGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC)load(\"glGetActiveAttribARB\");\n\tglad_glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC)load(\"glGetAttribLocationARB\");\n\tglad_glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC)load(\"glGetVertexAttribdvARB\");\n\tglad_glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC)load(\"glGetVertexAttribfvARB\");\n\tglad_glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC)load(\"glGetVertexAttribivARB\");\n\tglad_glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC)load(\"glGetVertexAttribPointervARB\");\n}\nstatic void load_GL_ARB_tessellation_shader(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_tessellation_shader) return;\n\tglad_glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)load(\"glPatchParameteri\");\n\tglad_glPatchParameterfv = (PFNGLPATCHPARAMETERFVPROC)load(\"glPatchParameterfv\");\n}\nstatic void load_GL_EXT_draw_buffers2(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_draw_buffers2) return;\n\tglad_glColorMaskIndexedEXT = (PFNGLCOLORMASKINDEXEDEXTPROC)load(\"glColorMaskIndexedEXT\");\n\tglad_glGetBooleanIndexedvEXT = (PFNGLGETBOOLEANINDEXEDVEXTPROC)load(\"glGetBooleanIndexedvEXT\");\n\tglad_glGetIntegerIndexedvEXT = (PFNGLGETINTEGERINDEXEDVEXTPROC)load(\"glGetIntegerIndexedvEXT\");\n\tglad_glEnableIndexedEXT = (PFNGLENABLEINDEXEDEXTPROC)load(\"glEnableIndexedEXT\");\n\tglad_glDisableIndexedEXT = (PFNGLDISABLEINDEXEDEXTPROC)load(\"glDisableIndexedEXT\");\n\tglad_glIsEnabledIndexedEXT = (PFNGLISENABLEDINDEXEDEXTPROC)load(\"glIsEnabledIndexedEXT\");\n}\nstatic void load_GL_ARB_vertex_attrib_64bit(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_vertex_attrib_64bit) return;\n\tglad_glVertexAttribL1d = (PFNGLVERTEXATTRIBL1DPROC)load(\"glVertexAttribL1d\");\n\tglad_glVertexAttribL2d = (PFNGLVERTEXATTRIBL2DPROC)load(\"glVertexAttribL2d\");\n\tglad_glVertexAttribL3d = (PFNGLVERTEXATTRIBL3DPROC)load(\"glVertexAttribL3d\");\n\tglad_glVertexAttribL4d = (PFNGLVERTEXATTRIBL4DPROC)load(\"glVertexAttribL4d\");\n\tglad_glVertexAttribL1dv = (PFNGLVERTEXATTRIBL1DVPROC)load(\"glVertexAttribL1dv\");\n\tglad_glVertexAttribL2dv = (PFNGLVERTEXATTRIBL2DVPROC)load(\"glVertexAttribL2dv\");\n\tglad_glVertexAttribL3dv = (PFNGLVERTEXATTRIBL3DVPROC)load(\"glVertexAttribL3dv\");\n\tglad_glVertexAttribL4dv = (PFNGLVERTEXATTRIBL4DVPROC)load(\"glVertexAttribL4dv\");\n\tglad_glVertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC)load(\"glVertexAttribLPointer\");\n\tglad_glGetVertexAttribLdv = (PFNGLGETVERTEXATTRIBLDVPROC)load(\"glGetVertexAttribLdv\");\n}\nstatic void load_GL_AMD_interleaved_elements(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_interleaved_elements) return;\n\tglad_glVertexAttribParameteriAMD = (PFNGLVERTEXATTRIBPARAMETERIAMDPROC)load(\"glVertexAttribParameteriAMD\");\n}\nstatic void load_GL_ARB_fragment_program(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_fragment_program) return;\n\tglad_glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC)load(\"glProgramStringARB\");\n\tglad_glBindProgramARB = (PFNGLBINDPROGRAMARBPROC)load(\"glBindProgramARB\");\n\tglad_glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC)load(\"glDeleteProgramsARB\");\n\tglad_glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC)load(\"glGenProgramsARB\");\n\tglad_glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC)load(\"glProgramEnvParameter4dARB\");\n\tglad_glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC)load(\"glProgramEnvParameter4dvARB\");\n\tglad_glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC)load(\"glProgramEnvParameter4fARB\");\n\tglad_glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC)load(\"glProgramEnvParameter4fvARB\");\n\tglad_glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC)load(\"glProgramLocalParameter4dARB\");\n\tglad_glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC)load(\"glProgramLocalParameter4dvARB\");\n\tglad_glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC)load(\"glProgramLocalParameter4fARB\");\n\tglad_glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)load(\"glProgramLocalParameter4fvARB\");\n\tglad_glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC)load(\"glGetProgramEnvParameterdvARB\");\n\tglad_glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC)load(\"glGetProgramEnvParameterfvARB\");\n\tglad_glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC)load(\"glGetProgramLocalParameterdvARB\");\n\tglad_glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC)load(\"glGetProgramLocalParameterfvARB\");\n\tglad_glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC)load(\"glGetProgramivARB\");\n\tglad_glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC)load(\"glGetProgramStringARB\");\n\tglad_glIsProgramARB = (PFNGLISPROGRAMARBPROC)load(\"glIsProgramARB\");\n}\nstatic void load_GL_ARB_texture_storage(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_texture_storage) return;\n\tglad_glTexStorage1D = (PFNGLTEXSTORAGE1DPROC)load(\"glTexStorage1D\");\n\tglad_glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)load(\"glTexStorage2D\");\n\tglad_glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)load(\"glTexStorage3D\");\n}\nstatic void load_GL_ARB_copy_image(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_copy_image) return;\n\tglad_glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC)load(\"glCopyImageSubData\");\n}\nstatic void load_GL_SGIS_pixel_texture(GLADloadproc load) {\n\tif(!GLAD_GL_SGIS_pixel_texture) return;\n\tglad_glPixelTexGenParameteriSGIS = (PFNGLPIXELTEXGENPARAMETERISGISPROC)load(\"glPixelTexGenParameteriSGIS\");\n\tglad_glPixelTexGenParameterivSGIS = (PFNGLPIXELTEXGENPARAMETERIVSGISPROC)load(\"glPixelTexGenParameterivSGIS\");\n\tglad_glPixelTexGenParameterfSGIS = (PFNGLPIXELTEXGENPARAMETERFSGISPROC)load(\"glPixelTexGenParameterfSGIS\");\n\tglad_glPixelTexGenParameterfvSGIS = (PFNGLPIXELTEXGENPARAMETERFVSGISPROC)load(\"glPixelTexGenParameterfvSGIS\");\n\tglad_glGetPixelTexGenParameterivSGIS = (PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC)load(\"glGetPixelTexGenParameterivSGIS\");\n\tglad_glGetPixelTexGenParameterfvSGIS = (PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC)load(\"glGetPixelTexGenParameterfvSGIS\");\n}\nstatic void load_GL_SGIX_instruments(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_instruments) return;\n\tglad_glGetInstrumentsSGIX = (PFNGLGETINSTRUMENTSSGIXPROC)load(\"glGetInstrumentsSGIX\");\n\tglad_glInstrumentsBufferSGIX = (PFNGLINSTRUMENTSBUFFERSGIXPROC)load(\"glInstrumentsBufferSGIX\");\n\tglad_glPollInstrumentsSGIX = (PFNGLPOLLINSTRUMENTSSGIXPROC)load(\"glPollInstrumentsSGIX\");\n\tglad_glReadInstrumentsSGIX = (PFNGLREADINSTRUMENTSSGIXPROC)load(\"glReadInstrumentsSGIX\");\n\tglad_glStartInstrumentsSGIX = (PFNGLSTARTINSTRUMENTSSGIXPROC)load(\"glStartInstrumentsSGIX\");\n\tglad_glStopInstrumentsSGIX = (PFNGLSTOPINSTRUMENTSSGIXPROC)load(\"glStopInstrumentsSGIX\");\n}\nstatic void load_GL_ARB_shader_storage_buffer_object(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_shader_storage_buffer_object) return;\n\tglad_glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC)load(\"glShaderStorageBlockBinding\");\n}\nstatic void load_GL_EXT_blend_minmax(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_blend_minmax) return;\n\tglad_glBlendEquationEXT = (PFNGLBLENDEQUATIONEXTPROC)load(\"glBlendEquationEXT\");\n}\nstatic void load_GL_ARB_base_instance(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_base_instance) return;\n\tglad_glDrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)load(\"glDrawArraysInstancedBaseInstance\");\n\tglad_glDrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)load(\"glDrawElementsInstancedBaseInstance\");\n\tglad_glDrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)load(\"glDrawElementsInstancedBaseVertexBaseInstance\");\n}\nstatic void load_GL_EXT_texture_integer(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_texture_integer) return;\n\tglad_glTexParameterIivEXT = (PFNGLTEXPARAMETERIIVEXTPROC)load(\"glTexParameterIivEXT\");\n\tglad_glTexParameterIuivEXT = (PFNGLTEXPARAMETERIUIVEXTPROC)load(\"glTexParameterIuivEXT\");\n\tglad_glGetTexParameterIivEXT = (PFNGLGETTEXPARAMETERIIVEXTPROC)load(\"glGetTexParameterIivEXT\");\n\tglad_glGetTexParameterIuivEXT = (PFNGLGETTEXPARAMETERIUIVEXTPROC)load(\"glGetTexParameterIuivEXT\");\n\tglad_glClearColorIiEXT = (PFNGLCLEARCOLORIIEXTPROC)load(\"glClearColorIiEXT\");\n\tglad_glClearColorIuiEXT = (PFNGLCLEARCOLORIUIEXTPROC)load(\"glClearColorIuiEXT\");\n}\nstatic void load_GL_ARB_texture_multisample(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_texture_multisample) return;\n\tglad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)load(\"glTexImage2DMultisample\");\n\tglad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC)load(\"glTexImage3DMultisample\");\n\tglad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)load(\"glGetMultisamplefv\");\n\tglad_glSampleMaski = (PFNGLSAMPLEMASKIPROC)load(\"glSampleMaski\");\n}\nstatic void load_GL_AMD_vertex_shader_tessellator(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_vertex_shader_tessellator) return;\n\tglad_glTessellationFactorAMD = (PFNGLTESSELLATIONFACTORAMDPROC)load(\"glTessellationFactorAMD\");\n\tglad_glTessellationModeAMD = (PFNGLTESSELLATIONMODEAMDPROC)load(\"glTessellationModeAMD\");\n}\nstatic void load_GL_ARB_invalidate_subdata(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_invalidate_subdata) return;\n\tglad_glInvalidateTexSubImage = (PFNGLINVALIDATETEXSUBIMAGEPROC)load(\"glInvalidateTexSubImage\");\n\tglad_glInvalidateTexImage = (PFNGLINVALIDATETEXIMAGEPROC)load(\"glInvalidateTexImage\");\n\tglad_glInvalidateBufferSubData = (PFNGLINVALIDATEBUFFERSUBDATAPROC)load(\"glInvalidateBufferSubData\");\n\tglad_glInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC)load(\"glInvalidateBufferData\");\n\tglad_glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC)load(\"glInvalidateFramebuffer\");\n\tglad_glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC)load(\"glInvalidateSubFramebuffer\");\n}\nstatic void load_GL_EXT_index_material(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_index_material) return;\n\tglad_glIndexMaterialEXT = (PFNGLINDEXMATERIALEXTPROC)load(\"glIndexMaterialEXT\");\n}\nstatic void load_GL_INTEL_parallel_arrays(GLADloadproc load) {\n\tif(!GLAD_GL_INTEL_parallel_arrays) return;\n\tglad_glVertexPointervINTEL = (PFNGLVERTEXPOINTERVINTELPROC)load(\"glVertexPointervINTEL\");\n\tglad_glNormalPointervINTEL = (PFNGLNORMALPOINTERVINTELPROC)load(\"glNormalPointervINTEL\");\n\tglad_glColorPointervINTEL = (PFNGLCOLORPOINTERVINTELPROC)load(\"glColorPointervINTEL\");\n\tglad_glTexCoordPointervINTEL = (PFNGLTEXCOORDPOINTERVINTELPROC)load(\"glTexCoordPointervINTEL\");\n}\nstatic void load_GL_ATI_draw_buffers(GLADloadproc load) {\n\tif(!GLAD_GL_ATI_draw_buffers) return;\n\tglad_glDrawBuffersATI = (PFNGLDRAWBUFFERSATIPROC)load(\"glDrawBuffersATI\");\n}\nstatic void load_GL_SGIX_pixel_texture(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_pixel_texture) return;\n\tglad_glPixelTexGenSGIX = (PFNGLPIXELTEXGENSGIXPROC)load(\"glPixelTexGenSGIX\");\n}\nstatic void load_GL_ARB_timer_query(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_timer_query) return;\n\tglad_glQueryCounter = (PFNGLQUERYCOUNTERPROC)load(\"glQueryCounter\");\n\tglad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)load(\"glGetQueryObjecti64v\");\n\tglad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)load(\"glGetQueryObjectui64v\");\n}\nstatic void load_GL_NV_parameter_buffer_object(GLADloadproc load) {\n\tif(!GLAD_GL_NV_parameter_buffer_object) return;\n\tglad_glProgramBufferParametersfvNV = (PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC)load(\"glProgramBufferParametersfvNV\");\n\tglad_glProgramBufferParametersIivNV = (PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC)load(\"glProgramBufferParametersIivNV\");\n\tglad_glProgramBufferParametersIuivNV = (PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC)load(\"glProgramBufferParametersIuivNV\");\n}\nstatic void load_GL_ARB_uniform_buffer_object(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_uniform_buffer_object) return;\n\tglad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)load(\"glGetUniformIndices\");\n\tglad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)load(\"glGetActiveUniformsiv\");\n\tglad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)load(\"glGetActiveUniformName\");\n\tglad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)load(\"glGetUniformBlockIndex\");\n\tglad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)load(\"glGetActiveUniformBlockiv\");\n\tglad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)load(\"glGetActiveUniformBlockName\");\n\tglad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)load(\"glUniformBlockBinding\");\n}\nstatic void load_GL_NV_transform_feedback2(GLADloadproc load) {\n\tif(!GLAD_GL_NV_transform_feedback2) return;\n\tglad_glBindTransformFeedbackNV = (PFNGLBINDTRANSFORMFEEDBACKNVPROC)load(\"glBindTransformFeedbackNV\");\n\tglad_glDeleteTransformFeedbacksNV = (PFNGLDELETETRANSFORMFEEDBACKSNVPROC)load(\"glDeleteTransformFeedbacksNV\");\n\tglad_glGenTransformFeedbacksNV = (PFNGLGENTRANSFORMFEEDBACKSNVPROC)load(\"glGenTransformFeedbacksNV\");\n\tglad_glIsTransformFeedbackNV = (PFNGLISTRANSFORMFEEDBACKNVPROC)load(\"glIsTransformFeedbackNV\");\n\tglad_glPauseTransformFeedbackNV = (PFNGLPAUSETRANSFORMFEEDBACKNVPROC)load(\"glPauseTransformFeedbackNV\");\n\tglad_glResumeTransformFeedbackNV = (PFNGLRESUMETRANSFORMFEEDBACKNVPROC)load(\"glResumeTransformFeedbackNV\");\n\tglad_glDrawTransformFeedbackNV = (PFNGLDRAWTRANSFORMFEEDBACKNVPROC)load(\"glDrawTransformFeedbackNV\");\n}\nstatic void load_GL_EXT_blend_color(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_blend_color) return;\n\tglad_glBlendColorEXT = (PFNGLBLENDCOLOREXTPROC)load(\"glBlendColorEXT\");\n}\nstatic void load_GL_EXT_histogram(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_histogram) return;\n\tglad_glGetHistogramEXT = (PFNGLGETHISTOGRAMEXTPROC)load(\"glGetHistogramEXT\");\n\tglad_glGetHistogramParameterfvEXT = (PFNGLGETHISTOGRAMPARAMETERFVEXTPROC)load(\"glGetHistogramParameterfvEXT\");\n\tglad_glGetHistogramParameterivEXT = (PFNGLGETHISTOGRAMPARAMETERIVEXTPROC)load(\"glGetHistogramParameterivEXT\");\n\tglad_glGetMinmaxEXT = (PFNGLGETMINMAXEXTPROC)load(\"glGetMinmaxEXT\");\n\tglad_glGetMinmaxParameterfvEXT = (PFNGLGETMINMAXPARAMETERFVEXTPROC)load(\"glGetMinmaxParameterfvEXT\");\n\tglad_glGetMinmaxParameterivEXT = (PFNGLGETMINMAXPARAMETERIVEXTPROC)load(\"glGetMinmaxParameterivEXT\");\n\tglad_glHistogramEXT = (PFNGLHISTOGRAMEXTPROC)load(\"glHistogramEXT\");\n\tglad_glMinmaxEXT = (PFNGLMINMAXEXTPROC)load(\"glMinmaxEXT\");\n\tglad_glResetHistogramEXT = (PFNGLRESETHISTOGRAMEXTPROC)load(\"glResetHistogramEXT\");\n\tglad_glResetMinmaxEXT = (PFNGLRESETMINMAXEXTPROC)load(\"glResetMinmaxEXT\");\n}\nstatic void load_GL_SGIS_point_parameters(GLADloadproc load) {\n\tif(!GLAD_GL_SGIS_point_parameters) return;\n\tglad_glPointParameterfSGIS = (PFNGLPOINTPARAMETERFSGISPROC)load(\"glPointParameterfSGIS\");\n\tglad_glPointParameterfvSGIS = (PFNGLPOINTPARAMETERFVSGISPROC)load(\"glPointParameterfvSGIS\");\n}\nstatic void load_GL_EXT_direct_state_access(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_direct_state_access) return;\n\tglad_glMatrixLoadfEXT = (PFNGLMATRIXLOADFEXTPROC)load(\"glMatrixLoadfEXT\");\n\tglad_glMatrixLoaddEXT = (PFNGLMATRIXLOADDEXTPROC)load(\"glMatrixLoaddEXT\");\n\tglad_glMatrixMultfEXT = (PFNGLMATRIXMULTFEXTPROC)load(\"glMatrixMultfEXT\");\n\tglad_glMatrixMultdEXT = (PFNGLMATRIXMULTDEXTPROC)load(\"glMatrixMultdEXT\");\n\tglad_glMatrixLoadIdentityEXT = (PFNGLMATRIXLOADIDENTITYEXTPROC)load(\"glMatrixLoadIdentityEXT\");\n\tglad_glMatrixRotatefEXT = (PFNGLMATRIXROTATEFEXTPROC)load(\"glMatrixRotatefEXT\");\n\tglad_glMatrixRotatedEXT = (PFNGLMATRIXROTATEDEXTPROC)load(\"glMatrixRotatedEXT\");\n\tglad_glMatrixScalefEXT = (PFNGLMATRIXSCALEFEXTPROC)load(\"glMatrixScalefEXT\");\n\tglad_glMatrixScaledEXT = (PFNGLMATRIXSCALEDEXTPROC)load(\"glMatrixScaledEXT\");\n\tglad_glMatrixTranslatefEXT = (PFNGLMATRIXTRANSLATEFEXTPROC)load(\"glMatrixTranslatefEXT\");\n\tglad_glMatrixTranslatedEXT = (PFNGLMATRIXTRANSLATEDEXTPROC)load(\"glMatrixTranslatedEXT\");\n\tglad_glMatrixFrustumEXT = (PFNGLMATRIXFRUSTUMEXTPROC)load(\"glMatrixFrustumEXT\");\n\tglad_glMatrixOrthoEXT = (PFNGLMATRIXORTHOEXTPROC)load(\"glMatrixOrthoEXT\");\n\tglad_glMatrixPopEXT = (PFNGLMATRIXPOPEXTPROC)load(\"glMatrixPopEXT\");\n\tglad_glMatrixPushEXT = (PFNGLMATRIXPUSHEXTPROC)load(\"glMatrixPushEXT\");\n\tglad_glClientAttribDefaultEXT = (PFNGLCLIENTATTRIBDEFAULTEXTPROC)load(\"glClientAttribDefaultEXT\");\n\tglad_glPushClientAttribDefaultEXT = (PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC)load(\"glPushClientAttribDefaultEXT\");\n\tglad_glTextureParameterfEXT = (PFNGLTEXTUREPARAMETERFEXTPROC)load(\"glTextureParameterfEXT\");\n\tglad_glTextureParameterfvEXT = (PFNGLTEXTUREPARAMETERFVEXTPROC)load(\"glTextureParameterfvEXT\");\n\tglad_glTextureParameteriEXT = (PFNGLTEXTUREPARAMETERIEXTPROC)load(\"glTextureParameteriEXT\");\n\tglad_glTextureParameterivEXT = (PFNGLTEXTUREPARAMETERIVEXTPROC)load(\"glTextureParameterivEXT\");\n\tglad_glTextureImage1DEXT = (PFNGLTEXTUREIMAGE1DEXTPROC)load(\"glTextureImage1DEXT\");\n\tglad_glTextureImage2DEXT = (PFNGLTEXTUREIMAGE2DEXTPROC)load(\"glTextureImage2DEXT\");\n\tglad_glTextureSubImage1DEXT = (PFNGLTEXTURESUBIMAGE1DEXTPROC)load(\"glTextureSubImage1DEXT\");\n\tglad_glTextureSubImage2DEXT = (PFNGLTEXTURESUBIMAGE2DEXTPROC)load(\"glTextureSubImage2DEXT\");\n\tglad_glCopyTextureImage1DEXT = (PFNGLCOPYTEXTUREIMAGE1DEXTPROC)load(\"glCopyTextureImage1DEXT\");\n\tglad_glCopyTextureImage2DEXT = (PFNGLCOPYTEXTUREIMAGE2DEXTPROC)load(\"glCopyTextureImage2DEXT\");\n\tglad_glCopyTextureSubImage1DEXT = (PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC)load(\"glCopyTextureSubImage1DEXT\");\n\tglad_glCopyTextureSubImage2DEXT = (PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC)load(\"glCopyTextureSubImage2DEXT\");\n\tglad_glGetTextureImageEXT = (PFNGLGETTEXTUREIMAGEEXTPROC)load(\"glGetTextureImageEXT\");\n\tglad_glGetTextureParameterfvEXT = (PFNGLGETTEXTUREPARAMETERFVEXTPROC)load(\"glGetTextureParameterfvEXT\");\n\tglad_glGetTextureParameterivEXT = (PFNGLGETTEXTUREPARAMETERIVEXTPROC)load(\"glGetTextureParameterivEXT\");\n\tglad_glGetTextureLevelParameterfvEXT = (PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC)load(\"glGetTextureLevelParameterfvEXT\");\n\tglad_glGetTextureLevelParameterivEXT = (PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC)load(\"glGetTextureLevelParameterivEXT\");\n\tglad_glTextureImage3DEXT = (PFNGLTEXTUREIMAGE3DEXTPROC)load(\"glTextureImage3DEXT\");\n\tglad_glTextureSubImage3DEXT = (PFNGLTEXTURESUBIMAGE3DEXTPROC)load(\"glTextureSubImage3DEXT\");\n\tglad_glCopyTextureSubImage3DEXT = (PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC)load(\"glCopyTextureSubImage3DEXT\");\n\tglad_glBindMultiTextureEXT = (PFNGLBINDMULTITEXTUREEXTPROC)load(\"glBindMultiTextureEXT\");\n\tglad_glMultiTexCoordPointerEXT = (PFNGLMULTITEXCOORDPOINTEREXTPROC)load(\"glMultiTexCoordPointerEXT\");\n\tglad_glMultiTexEnvfEXT = (PFNGLMULTITEXENVFEXTPROC)load(\"glMultiTexEnvfEXT\");\n\tglad_glMultiTexEnvfvEXT = (PFNGLMULTITEXENVFVEXTPROC)load(\"glMultiTexEnvfvEXT\");\n\tglad_glMultiTexEnviEXT = (PFNGLMULTITEXENVIEXTPROC)load(\"glMultiTexEnviEXT\");\n\tglad_glMultiTexEnvivEXT = (PFNGLMULTITEXENVIVEXTPROC)load(\"glMultiTexEnvivEXT\");\n\tglad_glMultiTexGendEXT = (PFNGLMULTITEXGENDEXTPROC)load(\"glMultiTexGendEXT\");\n\tglad_glMultiTexGendvEXT = (PFNGLMULTITEXGENDVEXTPROC)load(\"glMultiTexGendvEXT\");\n\tglad_glMultiTexGenfEXT = (PFNGLMULTITEXGENFEXTPROC)load(\"glMultiTexGenfEXT\");\n\tglad_glMultiTexGenfvEXT = (PFNGLMULTITEXGENFVEXTPROC)load(\"glMultiTexGenfvEXT\");\n\tglad_glMultiTexGeniEXT = (PFNGLMULTITEXGENIEXTPROC)load(\"glMultiTexGeniEXT\");\n\tglad_glMultiTexGenivEXT = (PFNGLMULTITEXGENIVEXTPROC)load(\"glMultiTexGenivEXT\");\n\tglad_glGetMultiTexEnvfvEXT = (PFNGLGETMULTITEXENVFVEXTPROC)load(\"glGetMultiTexEnvfvEXT\");\n\tglad_glGetMultiTexEnvivEXT = (PFNGLGETMULTITEXENVIVEXTPROC)load(\"glGetMultiTexEnvivEXT\");\n\tglad_glGetMultiTexGendvEXT = (PFNGLGETMULTITEXGENDVEXTPROC)load(\"glGetMultiTexGendvEXT\");\n\tglad_glGetMultiTexGenfvEXT = (PFNGLGETMULTITEXGENFVEXTPROC)load(\"glGetMultiTexGenfvEXT\");\n\tglad_glGetMultiTexGenivEXT = (PFNGLGETMULTITEXGENIVEXTPROC)load(\"glGetMultiTexGenivEXT\");\n\tglad_glMultiTexParameteriEXT = (PFNGLMULTITEXPARAMETERIEXTPROC)load(\"glMultiTexParameteriEXT\");\n\tglad_glMultiTexParameterivEXT = (PFNGLMULTITEXPARAMETERIVEXTPROC)load(\"glMultiTexParameterivEXT\");\n\tglad_glMultiTexParameterfEXT = (PFNGLMULTITEXPARAMETERFEXTPROC)load(\"glMultiTexParameterfEXT\");\n\tglad_glMultiTexParameterfvEXT = (PFNGLMULTITEXPARAMETERFVEXTPROC)load(\"glMultiTexParameterfvEXT\");\n\tglad_glMultiTexImage1DEXT = (PFNGLMULTITEXIMAGE1DEXTPROC)load(\"glMultiTexImage1DEXT\");\n\tglad_glMultiTexImage2DEXT = (PFNGLMULTITEXIMAGE2DEXTPROC)load(\"glMultiTexImage2DEXT\");\n\tglad_glMultiTexSubImage1DEXT = (PFNGLMULTITEXSUBIMAGE1DEXTPROC)load(\"glMultiTexSubImage1DEXT\");\n\tglad_glMultiTexSubImage2DEXT = (PFNGLMULTITEXSUBIMAGE2DEXTPROC)load(\"glMultiTexSubImage2DEXT\");\n\tglad_glCopyMultiTexImage1DEXT = (PFNGLCOPYMULTITEXIMAGE1DEXTPROC)load(\"glCopyMultiTexImage1DEXT\");\n\tglad_glCopyMultiTexImage2DEXT = (PFNGLCOPYMULTITEXIMAGE2DEXTPROC)load(\"glCopyMultiTexImage2DEXT\");\n\tglad_glCopyMultiTexSubImage1DEXT = (PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC)load(\"glCopyMultiTexSubImage1DEXT\");\n\tglad_glCopyMultiTexSubImage2DEXT = (PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC)load(\"glCopyMultiTexSubImage2DEXT\");\n\tglad_glGetMultiTexImageEXT = (PFNGLGETMULTITEXIMAGEEXTPROC)load(\"glGetMultiTexImageEXT\");\n\tglad_glGetMultiTexParameterfvEXT = (PFNGLGETMULTITEXPARAMETERFVEXTPROC)load(\"glGetMultiTexParameterfvEXT\");\n\tglad_glGetMultiTexParameterivEXT = (PFNGLGETMULTITEXPARAMETERIVEXTPROC)load(\"glGetMultiTexParameterivEXT\");\n\tglad_glGetMultiTexLevelParameterfvEXT = (PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC)load(\"glGetMultiTexLevelParameterfvEXT\");\n\tglad_glGetMultiTexLevelParameterivEXT = (PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC)load(\"glGetMultiTexLevelParameterivEXT\");\n\tglad_glMultiTexImage3DEXT = (PFNGLMULTITEXIMAGE3DEXTPROC)load(\"glMultiTexImage3DEXT\");\n\tglad_glMultiTexSubImage3DEXT = (PFNGLMULTITEXSUBIMAGE3DEXTPROC)load(\"glMultiTexSubImage3DEXT\");\n\tglad_glCopyMultiTexSubImage3DEXT = (PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC)load(\"glCopyMultiTexSubImage3DEXT\");\n\tglad_glEnableClientStateIndexedEXT = (PFNGLENABLECLIENTSTATEINDEXEDEXTPROC)load(\"glEnableClientStateIndexedEXT\");\n\tglad_glDisableClientStateIndexedEXT = (PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC)load(\"glDisableClientStateIndexedEXT\");\n\tglad_glGetFloatIndexedvEXT = (PFNGLGETFLOATINDEXEDVEXTPROC)load(\"glGetFloatIndexedvEXT\");\n\tglad_glGetDoubleIndexedvEXT = (PFNGLGETDOUBLEINDEXEDVEXTPROC)load(\"glGetDoubleIndexedvEXT\");\n\tglad_glGetPointerIndexedvEXT = (PFNGLGETPOINTERINDEXEDVEXTPROC)load(\"glGetPointerIndexedvEXT\");\n\tglad_glEnableIndexedEXT = (PFNGLENABLEINDEXEDEXTPROC)load(\"glEnableIndexedEXT\");\n\tglad_glDisableIndexedEXT = (PFNGLDISABLEINDEXEDEXTPROC)load(\"glDisableIndexedEXT\");\n\tglad_glIsEnabledIndexedEXT = (PFNGLISENABLEDINDEXEDEXTPROC)load(\"glIsEnabledIndexedEXT\");\n\tglad_glGetIntegerIndexedvEXT = (PFNGLGETINTEGERINDEXEDVEXTPROC)load(\"glGetIntegerIndexedvEXT\");\n\tglad_glGetBooleanIndexedvEXT = (PFNGLGETBOOLEANINDEXEDVEXTPROC)load(\"glGetBooleanIndexedvEXT\");\n\tglad_glCompressedTextureImage3DEXT = (PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC)load(\"glCompressedTextureImage3DEXT\");\n\tglad_glCompressedTextureImage2DEXT = (PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC)load(\"glCompressedTextureImage2DEXT\");\n\tglad_glCompressedTextureImage1DEXT = (PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC)load(\"glCompressedTextureImage1DEXT\");\n\tglad_glCompressedTextureSubImage3DEXT = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC)load(\"glCompressedTextureSubImage3DEXT\");\n\tglad_glCompressedTextureSubImage2DEXT = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC)load(\"glCompressedTextureSubImage2DEXT\");\n\tglad_glCompressedTextureSubImage1DEXT = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC)load(\"glCompressedTextureSubImage1DEXT\");\n\tglad_glGetCompressedTextureImageEXT = (PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC)load(\"glGetCompressedTextureImageEXT\");\n\tglad_glCompressedMultiTexImage3DEXT = (PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC)load(\"glCompressedMultiTexImage3DEXT\");\n\tglad_glCompressedMultiTexImage2DEXT = (PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC)load(\"glCompressedMultiTexImage2DEXT\");\n\tglad_glCompressedMultiTexImage1DEXT = (PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC)load(\"glCompressedMultiTexImage1DEXT\");\n\tglad_glCompressedMultiTexSubImage3DEXT = (PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC)load(\"glCompressedMultiTexSubImage3DEXT\");\n\tglad_glCompressedMultiTexSubImage2DEXT = (PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC)load(\"glCompressedMultiTexSubImage2DEXT\");\n\tglad_glCompressedMultiTexSubImage1DEXT = (PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC)load(\"glCompressedMultiTexSubImage1DEXT\");\n\tglad_glGetCompressedMultiTexImageEXT = (PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC)load(\"glGetCompressedMultiTexImageEXT\");\n\tglad_glMatrixLoadTransposefEXT = (PFNGLMATRIXLOADTRANSPOSEFEXTPROC)load(\"glMatrixLoadTransposefEXT\");\n\tglad_glMatrixLoadTransposedEXT = (PFNGLMATRIXLOADTRANSPOSEDEXTPROC)load(\"glMatrixLoadTransposedEXT\");\n\tglad_glMatrixMultTransposefEXT = (PFNGLMATRIXMULTTRANSPOSEFEXTPROC)load(\"glMatrixMultTransposefEXT\");\n\tglad_glMatrixMultTransposedEXT = (PFNGLMATRIXMULTTRANSPOSEDEXTPROC)load(\"glMatrixMultTransposedEXT\");\n\tglad_glNamedBufferDataEXT = (PFNGLNAMEDBUFFERDATAEXTPROC)load(\"glNamedBufferDataEXT\");\n\tglad_glNamedBufferSubDataEXT = (PFNGLNAMEDBUFFERSUBDATAEXTPROC)load(\"glNamedBufferSubDataEXT\");\n\tglad_glMapNamedBufferEXT = (PFNGLMAPNAMEDBUFFEREXTPROC)load(\"glMapNamedBufferEXT\");\n\tglad_glUnmapNamedBufferEXT = (PFNGLUNMAPNAMEDBUFFEREXTPROC)load(\"glUnmapNamedBufferEXT\");\n\tglad_glGetNamedBufferParameterivEXT = (PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC)load(\"glGetNamedBufferParameterivEXT\");\n\tglad_glGetNamedBufferPointervEXT = (PFNGLGETNAMEDBUFFERPOINTERVEXTPROC)load(\"glGetNamedBufferPointervEXT\");\n\tglad_glGetNamedBufferSubDataEXT = (PFNGLGETNAMEDBUFFERSUBDATAEXTPROC)load(\"glGetNamedBufferSubDataEXT\");\n\tglad_glProgramUniform1fEXT = (PFNGLPROGRAMUNIFORM1FEXTPROC)load(\"glProgramUniform1fEXT\");\n\tglad_glProgramUniform2fEXT = (PFNGLPROGRAMUNIFORM2FEXTPROC)load(\"glProgramUniform2fEXT\");\n\tglad_glProgramUniform3fEXT = (PFNGLPROGRAMUNIFORM3FEXTPROC)load(\"glProgramUniform3fEXT\");\n\tglad_glProgramUniform4fEXT = (PFNGLPROGRAMUNIFORM4FEXTPROC)load(\"glProgramUniform4fEXT\");\n\tglad_glProgramUniform1iEXT = (PFNGLPROGRAMUNIFORM1IEXTPROC)load(\"glProgramUniform1iEXT\");\n\tglad_glProgramUniform2iEXT = (PFNGLPROGRAMUNIFORM2IEXTPROC)load(\"glProgramUniform2iEXT\");\n\tglad_glProgramUniform3iEXT = (PFNGLPROGRAMUNIFORM3IEXTPROC)load(\"glProgramUniform3iEXT\");\n\tglad_glProgramUniform4iEXT = (PFNGLPROGRAMUNIFORM4IEXTPROC)load(\"glProgramUniform4iEXT\");\n\tglad_glProgramUniform1fvEXT = (PFNGLPROGRAMUNIFORM1FVEXTPROC)load(\"glProgramUniform1fvEXT\");\n\tglad_glProgramUniform2fvEXT = (PFNGLPROGRAMUNIFORM2FVEXTPROC)load(\"glProgramUniform2fvEXT\");\n\tglad_glProgramUniform3fvEXT = (PFNGLPROGRAMUNIFORM3FVEXTPROC)load(\"glProgramUniform3fvEXT\");\n\tglad_glProgramUniform4fvEXT = (PFNGLPROGRAMUNIFORM4FVEXTPROC)load(\"glProgramUniform4fvEXT\");\n\tglad_glProgramUniform1ivEXT = (PFNGLPROGRAMUNIFORM1IVEXTPROC)load(\"glProgramUniform1ivEXT\");\n\tglad_glProgramUniform2ivEXT = (PFNGLPROGRAMUNIFORM2IVEXTPROC)load(\"glProgramUniform2ivEXT\");\n\tglad_glProgramUniform3ivEXT = (PFNGLPROGRAMUNIFORM3IVEXTPROC)load(\"glProgramUniform3ivEXT\");\n\tglad_glProgramUniform4ivEXT = (PFNGLPROGRAMUNIFORM4IVEXTPROC)load(\"glProgramUniform4ivEXT\");\n\tglad_glProgramUniformMatrix2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC)load(\"glProgramUniformMatrix2fvEXT\");\n\tglad_glProgramUniformMatrix3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC)load(\"glProgramUniformMatrix3fvEXT\");\n\tglad_glProgramUniformMatrix4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC)load(\"glProgramUniformMatrix4fvEXT\");\n\tglad_glProgramUniformMatrix2x3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC)load(\"glProgramUniformMatrix2x3fvEXT\");\n\tglad_glProgramUniformMatrix3x2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC)load(\"glProgramUniformMatrix3x2fvEXT\");\n\tglad_glProgramUniformMatrix2x4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC)load(\"glProgramUniformMatrix2x4fvEXT\");\n\tglad_glProgramUniformMatrix4x2fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC)load(\"glProgramUniformMatrix4x2fvEXT\");\n\tglad_glProgramUniformMatrix3x4fvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC)load(\"glProgramUniformMatrix3x4fvEXT\");\n\tglad_glProgramUniformMatrix4x3fvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC)load(\"glProgramUniformMatrix4x3fvEXT\");\n\tglad_glTextureBufferEXT = (PFNGLTEXTUREBUFFEREXTPROC)load(\"glTextureBufferEXT\");\n\tglad_glMultiTexBufferEXT = (PFNGLMULTITEXBUFFEREXTPROC)load(\"glMultiTexBufferEXT\");\n\tglad_glTextureParameterIivEXT = (PFNGLTEXTUREPARAMETERIIVEXTPROC)load(\"glTextureParameterIivEXT\");\n\tglad_glTextureParameterIuivEXT = (PFNGLTEXTUREPARAMETERIUIVEXTPROC)load(\"glTextureParameterIuivEXT\");\n\tglad_glGetTextureParameterIivEXT = (PFNGLGETTEXTUREPARAMETERIIVEXTPROC)load(\"glGetTextureParameterIivEXT\");\n\tglad_glGetTextureParameterIuivEXT = (PFNGLGETTEXTUREPARAMETERIUIVEXTPROC)load(\"glGetTextureParameterIuivEXT\");\n\tglad_glMultiTexParameterIivEXT = (PFNGLMULTITEXPARAMETERIIVEXTPROC)load(\"glMultiTexParameterIivEXT\");\n\tglad_glMultiTexParameterIuivEXT = (PFNGLMULTITEXPARAMETERIUIVEXTPROC)load(\"glMultiTexParameterIuivEXT\");\n\tglad_glGetMultiTexParameterIivEXT = (PFNGLGETMULTITEXPARAMETERIIVEXTPROC)load(\"glGetMultiTexParameterIivEXT\");\n\tglad_glGetMultiTexParameterIuivEXT = (PFNGLGETMULTITEXPARAMETERIUIVEXTPROC)load(\"glGetMultiTexParameterIuivEXT\");\n\tglad_glProgramUniform1uiEXT = (PFNGLPROGRAMUNIFORM1UIEXTPROC)load(\"glProgramUniform1uiEXT\");\n\tglad_glProgramUniform2uiEXT = (PFNGLPROGRAMUNIFORM2UIEXTPROC)load(\"glProgramUniform2uiEXT\");\n\tglad_glProgramUniform3uiEXT = (PFNGLPROGRAMUNIFORM3UIEXTPROC)load(\"glProgramUniform3uiEXT\");\n\tglad_glProgramUniform4uiEXT = (PFNGLPROGRAMUNIFORM4UIEXTPROC)load(\"glProgramUniform4uiEXT\");\n\tglad_glProgramUniform1uivEXT = (PFNGLPROGRAMUNIFORM1UIVEXTPROC)load(\"glProgramUniform1uivEXT\");\n\tglad_glProgramUniform2uivEXT = (PFNGLPROGRAMUNIFORM2UIVEXTPROC)load(\"glProgramUniform2uivEXT\");\n\tglad_glProgramUniform3uivEXT = (PFNGLPROGRAMUNIFORM3UIVEXTPROC)load(\"glProgramUniform3uivEXT\");\n\tglad_glProgramUniform4uivEXT = (PFNGLPROGRAMUNIFORM4UIVEXTPROC)load(\"glProgramUniform4uivEXT\");\n\tglad_glNamedProgramLocalParameters4fvEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC)load(\"glNamedProgramLocalParameters4fvEXT\");\n\tglad_glNamedProgramLocalParameterI4iEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC)load(\"glNamedProgramLocalParameterI4iEXT\");\n\tglad_glNamedProgramLocalParameterI4ivEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC)load(\"glNamedProgramLocalParameterI4ivEXT\");\n\tglad_glNamedProgramLocalParametersI4ivEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC)load(\"glNamedProgramLocalParametersI4ivEXT\");\n\tglad_glNamedProgramLocalParameterI4uiEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC)load(\"glNamedProgramLocalParameterI4uiEXT\");\n\tglad_glNamedProgramLocalParameterI4uivEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC)load(\"glNamedProgramLocalParameterI4uivEXT\");\n\tglad_glNamedProgramLocalParametersI4uivEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC)load(\"glNamedProgramLocalParametersI4uivEXT\");\n\tglad_glGetNamedProgramLocalParameterIivEXT = (PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC)load(\"glGetNamedProgramLocalParameterIivEXT\");\n\tglad_glGetNamedProgramLocalParameterIuivEXT = (PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC)load(\"glGetNamedProgramLocalParameterIuivEXT\");\n\tglad_glEnableClientStateiEXT = (PFNGLENABLECLIENTSTATEIEXTPROC)load(\"glEnableClientStateiEXT\");\n\tglad_glDisableClientStateiEXT = (PFNGLDISABLECLIENTSTATEIEXTPROC)load(\"glDisableClientStateiEXT\");\n\tglad_glGetFloati_vEXT = (PFNGLGETFLOATI_VEXTPROC)load(\"glGetFloati_vEXT\");\n\tglad_glGetDoublei_vEXT = (PFNGLGETDOUBLEI_VEXTPROC)load(\"glGetDoublei_vEXT\");\n\tglad_glGetPointeri_vEXT = (PFNGLGETPOINTERI_VEXTPROC)load(\"glGetPointeri_vEXT\");\n\tglad_glNamedProgramStringEXT = (PFNGLNAMEDPROGRAMSTRINGEXTPROC)load(\"glNamedProgramStringEXT\");\n\tglad_glNamedProgramLocalParameter4dEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC)load(\"glNamedProgramLocalParameter4dEXT\");\n\tglad_glNamedProgramLocalParameter4dvEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC)load(\"glNamedProgramLocalParameter4dvEXT\");\n\tglad_glNamedProgramLocalParameter4fEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC)load(\"glNamedProgramLocalParameter4fEXT\");\n\tglad_glNamedProgramLocalParameter4fvEXT = (PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC)load(\"glNamedProgramLocalParameter4fvEXT\");\n\tglad_glGetNamedProgramLocalParameterdvEXT = (PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC)load(\"glGetNamedProgramLocalParameterdvEXT\");\n\tglad_glGetNamedProgramLocalParameterfvEXT = (PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC)load(\"glGetNamedProgramLocalParameterfvEXT\");\n\tglad_glGetNamedProgramivEXT = (PFNGLGETNAMEDPROGRAMIVEXTPROC)load(\"glGetNamedProgramivEXT\");\n\tglad_glGetNamedProgramStringEXT = (PFNGLGETNAMEDPROGRAMSTRINGEXTPROC)load(\"glGetNamedProgramStringEXT\");\n\tglad_glNamedRenderbufferStorageEXT = (PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC)load(\"glNamedRenderbufferStorageEXT\");\n\tglad_glGetNamedRenderbufferParameterivEXT = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC)load(\"glGetNamedRenderbufferParameterivEXT\");\n\tglad_glNamedRenderbufferStorageMultisampleEXT = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)load(\"glNamedRenderbufferStorageMultisampleEXT\");\n\tglad_glNamedRenderbufferStorageMultisampleCoverageEXT = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC)load(\"glNamedRenderbufferStorageMultisampleCoverageEXT\");\n\tglad_glCheckNamedFramebufferStatusEXT = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC)load(\"glCheckNamedFramebufferStatusEXT\");\n\tglad_glNamedFramebufferTexture1DEXT = (PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC)load(\"glNamedFramebufferTexture1DEXT\");\n\tglad_glNamedFramebufferTexture2DEXT = (PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC)load(\"glNamedFramebufferTexture2DEXT\");\n\tglad_glNamedFramebufferTexture3DEXT = (PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC)load(\"glNamedFramebufferTexture3DEXT\");\n\tglad_glNamedFramebufferRenderbufferEXT = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC)load(\"glNamedFramebufferRenderbufferEXT\");\n\tglad_glGetNamedFramebufferAttachmentParameterivEXT = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)load(\"glGetNamedFramebufferAttachmentParameterivEXT\");\n\tglad_glGenerateTextureMipmapEXT = (PFNGLGENERATETEXTUREMIPMAPEXTPROC)load(\"glGenerateTextureMipmapEXT\");\n\tglad_glGenerateMultiTexMipmapEXT = (PFNGLGENERATEMULTITEXMIPMAPEXTPROC)load(\"glGenerateMultiTexMipmapEXT\");\n\tglad_glFramebufferDrawBufferEXT = (PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC)load(\"glFramebufferDrawBufferEXT\");\n\tglad_glFramebufferDrawBuffersEXT = (PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC)load(\"glFramebufferDrawBuffersEXT\");\n\tglad_glFramebufferReadBufferEXT = (PFNGLFRAMEBUFFERREADBUFFEREXTPROC)load(\"glFramebufferReadBufferEXT\");\n\tglad_glGetFramebufferParameterivEXT = (PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC)load(\"glGetFramebufferParameterivEXT\");\n\tglad_glNamedCopyBufferSubDataEXT = (PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC)load(\"glNamedCopyBufferSubDataEXT\");\n\tglad_glNamedFramebufferTextureEXT = (PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC)load(\"glNamedFramebufferTextureEXT\");\n\tglad_glNamedFramebufferTextureLayerEXT = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC)load(\"glNamedFramebufferTextureLayerEXT\");\n\tglad_glNamedFramebufferTextureFaceEXT = (PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC)load(\"glNamedFramebufferTextureFaceEXT\");\n\tglad_glTextureRenderbufferEXT = (PFNGLTEXTURERENDERBUFFEREXTPROC)load(\"glTextureRenderbufferEXT\");\n\tglad_glMultiTexRenderbufferEXT = (PFNGLMULTITEXRENDERBUFFEREXTPROC)load(\"glMultiTexRenderbufferEXT\");\n\tglad_glVertexArrayVertexOffsetEXT = (PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC)load(\"glVertexArrayVertexOffsetEXT\");\n\tglad_glVertexArrayColorOffsetEXT = (PFNGLVERTEXARRAYCOLOROFFSETEXTPROC)load(\"glVertexArrayColorOffsetEXT\");\n\tglad_glVertexArrayEdgeFlagOffsetEXT = (PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC)load(\"glVertexArrayEdgeFlagOffsetEXT\");\n\tglad_glVertexArrayIndexOffsetEXT = (PFNGLVERTEXARRAYINDEXOFFSETEXTPROC)load(\"glVertexArrayIndexOffsetEXT\");\n\tglad_glVertexArrayNormalOffsetEXT = (PFNGLVERTEXARRAYNORMALOFFSETEXTPROC)load(\"glVertexArrayNormalOffsetEXT\");\n\tglad_glVertexArrayTexCoordOffsetEXT = (PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC)load(\"glVertexArrayTexCoordOffsetEXT\");\n\tglad_glVertexArrayMultiTexCoordOffsetEXT = (PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC)load(\"glVertexArrayMultiTexCoordOffsetEXT\");\n\tglad_glVertexArrayFogCoordOffsetEXT = (PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC)load(\"glVertexArrayFogCoordOffsetEXT\");\n\tglad_glVertexArraySecondaryColorOffsetEXT = (PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC)load(\"glVertexArraySecondaryColorOffsetEXT\");\n\tglad_glVertexArrayVertexAttribOffsetEXT = (PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC)load(\"glVertexArrayVertexAttribOffsetEXT\");\n\tglad_glVertexArrayVertexAttribIOffsetEXT = (PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC)load(\"glVertexArrayVertexAttribIOffsetEXT\");\n\tglad_glEnableVertexArrayEXT = (PFNGLENABLEVERTEXARRAYEXTPROC)load(\"glEnableVertexArrayEXT\");\n\tglad_glDisableVertexArrayEXT = (PFNGLDISABLEVERTEXARRAYEXTPROC)load(\"glDisableVertexArrayEXT\");\n\tglad_glEnableVertexArrayAttribEXT = (PFNGLENABLEVERTEXARRAYATTRIBEXTPROC)load(\"glEnableVertexArrayAttribEXT\");\n\tglad_glDisableVertexArrayAttribEXT = (PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC)load(\"glDisableVertexArrayAttribEXT\");\n\tglad_glGetVertexArrayIntegervEXT = (PFNGLGETVERTEXARRAYINTEGERVEXTPROC)load(\"glGetVertexArrayIntegervEXT\");\n\tglad_glGetVertexArrayPointervEXT = (PFNGLGETVERTEXARRAYPOINTERVEXTPROC)load(\"glGetVertexArrayPointervEXT\");\n\tglad_glGetVertexArrayIntegeri_vEXT = (PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC)load(\"glGetVertexArrayIntegeri_vEXT\");\n\tglad_glGetVertexArrayPointeri_vEXT = (PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC)load(\"glGetVertexArrayPointeri_vEXT\");\n\tglad_glMapNamedBufferRangeEXT = (PFNGLMAPNAMEDBUFFERRANGEEXTPROC)load(\"glMapNamedBufferRangeEXT\");\n\tglad_glFlushMappedNamedBufferRangeEXT = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC)load(\"glFlushMappedNamedBufferRangeEXT\");\n\tglad_glNamedBufferStorageEXT = (PFNGLNAMEDBUFFERSTORAGEEXTPROC)load(\"glNamedBufferStorageEXT\");\n\tglad_glClearNamedBufferDataEXT = (PFNGLCLEARNAMEDBUFFERDATAEXTPROC)load(\"glClearNamedBufferDataEXT\");\n\tglad_glClearNamedBufferSubDataEXT = (PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC)load(\"glClearNamedBufferSubDataEXT\");\n\tglad_glNamedFramebufferParameteriEXT = (PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC)load(\"glNamedFramebufferParameteriEXT\");\n\tglad_glGetNamedFramebufferParameterivEXT = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC)load(\"glGetNamedFramebufferParameterivEXT\");\n\tglad_glProgramUniform1dEXT = (PFNGLPROGRAMUNIFORM1DEXTPROC)load(\"glProgramUniform1dEXT\");\n\tglad_glProgramUniform2dEXT = (PFNGLPROGRAMUNIFORM2DEXTPROC)load(\"glProgramUniform2dEXT\");\n\tglad_glProgramUniform3dEXT = (PFNGLPROGRAMUNIFORM3DEXTPROC)load(\"glProgramUniform3dEXT\");\n\tglad_glProgramUniform4dEXT = (PFNGLPROGRAMUNIFORM4DEXTPROC)load(\"glProgramUniform4dEXT\");\n\tglad_glProgramUniform1dvEXT = (PFNGLPROGRAMUNIFORM1DVEXTPROC)load(\"glProgramUniform1dvEXT\");\n\tglad_glProgramUniform2dvEXT = (PFNGLPROGRAMUNIFORM2DVEXTPROC)load(\"glProgramUniform2dvEXT\");\n\tglad_glProgramUniform3dvEXT = (PFNGLPROGRAMUNIFORM3DVEXTPROC)load(\"glProgramUniform3dvEXT\");\n\tglad_glProgramUniform4dvEXT = (PFNGLPROGRAMUNIFORM4DVEXTPROC)load(\"glProgramUniform4dvEXT\");\n\tglad_glProgramUniformMatrix2dvEXT = (PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC)load(\"glProgramUniformMatrix2dvEXT\");\n\tglad_glProgramUniformMatrix3dvEXT = (PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC)load(\"glProgramUniformMatrix3dvEXT\");\n\tglad_glProgramUniformMatrix4dvEXT = (PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC)load(\"glProgramUniformMatrix4dvEXT\");\n\tglad_glProgramUniformMatrix2x3dvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC)load(\"glProgramUniformMatrix2x3dvEXT\");\n\tglad_glProgramUniformMatrix2x4dvEXT = (PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC)load(\"glProgramUniformMatrix2x4dvEXT\");\n\tglad_glProgramUniformMatrix3x2dvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC)load(\"glProgramUniformMatrix3x2dvEXT\");\n\tglad_glProgramUniformMatrix3x4dvEXT = (PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC)load(\"glProgramUniformMatrix3x4dvEXT\");\n\tglad_glProgramUniformMatrix4x2dvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC)load(\"glProgramUniformMatrix4x2dvEXT\");\n\tglad_glProgramUniformMatrix4x3dvEXT = (PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC)load(\"glProgramUniformMatrix4x3dvEXT\");\n\tglad_glTextureBufferRangeEXT = (PFNGLTEXTUREBUFFERRANGEEXTPROC)load(\"glTextureBufferRangeEXT\");\n\tglad_glTextureStorage1DEXT = (PFNGLTEXTURESTORAGE1DEXTPROC)load(\"glTextureStorage1DEXT\");\n\tglad_glTextureStorage2DEXT = (PFNGLTEXTURESTORAGE2DEXTPROC)load(\"glTextureStorage2DEXT\");\n\tglad_glTextureStorage3DEXT = (PFNGLTEXTURESTORAGE3DEXTPROC)load(\"glTextureStorage3DEXT\");\n\tglad_glTextureStorage2DMultisampleEXT = (PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC)load(\"glTextureStorage2DMultisampleEXT\");\n\tglad_glTextureStorage3DMultisampleEXT = (PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC)load(\"glTextureStorage3DMultisampleEXT\");\n\tglad_glVertexArrayBindVertexBufferEXT = (PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC)load(\"glVertexArrayBindVertexBufferEXT\");\n\tglad_glVertexArrayVertexAttribFormatEXT = (PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC)load(\"glVertexArrayVertexAttribFormatEXT\");\n\tglad_glVertexArrayVertexAttribIFormatEXT = (PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC)load(\"glVertexArrayVertexAttribIFormatEXT\");\n\tglad_glVertexArrayVertexAttribLFormatEXT = (PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC)load(\"glVertexArrayVertexAttribLFormatEXT\");\n\tglad_glVertexArrayVertexAttribBindingEXT = (PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC)load(\"glVertexArrayVertexAttribBindingEXT\");\n\tglad_glVertexArrayVertexBindingDivisorEXT = (PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC)load(\"glVertexArrayVertexBindingDivisorEXT\");\n\tglad_glVertexArrayVertexAttribLOffsetEXT = (PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC)load(\"glVertexArrayVertexAttribLOffsetEXT\");\n\tglad_glTexturePageCommitmentEXT = (PFNGLTEXTUREPAGECOMMITMENTEXTPROC)load(\"glTexturePageCommitmentEXT\");\n\tglad_glVertexArrayVertexAttribDivisorEXT = (PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC)load(\"glVertexArrayVertexAttribDivisorEXT\");\n}\nstatic void load_GL_AMD_sample_positions(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_sample_positions) return;\n\tglad_glSetMultisamplefvAMD = (PFNGLSETMULTISAMPLEFVAMDPROC)load(\"glSetMultisamplefvAMD\");\n}\nstatic void load_GL_NV_vertex_program(GLADloadproc load) {\n\tif(!GLAD_GL_NV_vertex_program) return;\n\tglad_glAreProgramsResidentNV = (PFNGLAREPROGRAMSRESIDENTNVPROC)load(\"glAreProgramsResidentNV\");\n\tglad_glBindProgramNV = (PFNGLBINDPROGRAMNVPROC)load(\"glBindProgramNV\");\n\tglad_glDeleteProgramsNV = (PFNGLDELETEPROGRAMSNVPROC)load(\"glDeleteProgramsNV\");\n\tglad_glExecuteProgramNV = (PFNGLEXECUTEPROGRAMNVPROC)load(\"glExecuteProgramNV\");\n\tglad_glGenProgramsNV = (PFNGLGENPROGRAMSNVPROC)load(\"glGenProgramsNV\");\n\tglad_glGetProgramParameterdvNV = (PFNGLGETPROGRAMPARAMETERDVNVPROC)load(\"glGetProgramParameterdvNV\");\n\tglad_glGetProgramParameterfvNV = (PFNGLGETPROGRAMPARAMETERFVNVPROC)load(\"glGetProgramParameterfvNV\");\n\tglad_glGetProgramivNV = (PFNGLGETPROGRAMIVNVPROC)load(\"glGetProgramivNV\");\n\tglad_glGetProgramStringNV = (PFNGLGETPROGRAMSTRINGNVPROC)load(\"glGetProgramStringNV\");\n\tglad_glGetTrackMatrixivNV = (PFNGLGETTRACKMATRIXIVNVPROC)load(\"glGetTrackMatrixivNV\");\n\tglad_glGetVertexAttribdvNV = (PFNGLGETVERTEXATTRIBDVNVPROC)load(\"glGetVertexAttribdvNV\");\n\tglad_glGetVertexAttribfvNV = (PFNGLGETVERTEXATTRIBFVNVPROC)load(\"glGetVertexAttribfvNV\");\n\tglad_glGetVertexAttribivNV = (PFNGLGETVERTEXATTRIBIVNVPROC)load(\"glGetVertexAttribivNV\");\n\tglad_glGetVertexAttribPointervNV = (PFNGLGETVERTEXATTRIBPOINTERVNVPROC)load(\"glGetVertexAttribPointervNV\");\n\tglad_glIsProgramNV = (PFNGLISPROGRAMNVPROC)load(\"glIsProgramNV\");\n\tglad_glLoadProgramNV = (PFNGLLOADPROGRAMNVPROC)load(\"glLoadProgramNV\");\n\tglad_glProgramParameter4dNV = (PFNGLPROGRAMPARAMETER4DNVPROC)load(\"glProgramParameter4dNV\");\n\tglad_glProgramParameter4dvNV = (PFNGLPROGRAMPARAMETER4DVNVPROC)load(\"glProgramParameter4dvNV\");\n\tglad_glProgramParameter4fNV = (PFNGLPROGRAMPARAMETER4FNVPROC)load(\"glProgramParameter4fNV\");\n\tglad_glProgramParameter4fvNV = (PFNGLPROGRAMPARAMETER4FVNVPROC)load(\"glProgramParameter4fvNV\");\n\tglad_glProgramParameters4dvNV = (PFNGLPROGRAMPARAMETERS4DVNVPROC)load(\"glProgramParameters4dvNV\");\n\tglad_glProgramParameters4fvNV = (PFNGLPROGRAMPARAMETERS4FVNVPROC)load(\"glProgramParameters4fvNV\");\n\tglad_glRequestResidentProgramsNV = (PFNGLREQUESTRESIDENTPROGRAMSNVPROC)load(\"glRequestResidentProgramsNV\");\n\tglad_glTrackMatrixNV = (PFNGLTRACKMATRIXNVPROC)load(\"glTrackMatrixNV\");\n\tglad_glVertexAttribPointerNV = (PFNGLVERTEXATTRIBPOINTERNVPROC)load(\"glVertexAttribPointerNV\");\n\tglad_glVertexAttrib1dNV = (PFNGLVERTEXATTRIB1DNVPROC)load(\"glVertexAttrib1dNV\");\n\tglad_glVertexAttrib1dvNV = (PFNGLVERTEXATTRIB1DVNVPROC)load(\"glVertexAttrib1dvNV\");\n\tglad_glVertexAttrib1fNV = (PFNGLVERTEXATTRIB1FNVPROC)load(\"glVertexAttrib1fNV\");\n\tglad_glVertexAttrib1fvNV = (PFNGLVERTEXATTRIB1FVNVPROC)load(\"glVertexAttrib1fvNV\");\n\tglad_glVertexAttrib1sNV = (PFNGLVERTEXATTRIB1SNVPROC)load(\"glVertexAttrib1sNV\");\n\tglad_glVertexAttrib1svNV = (PFNGLVERTEXATTRIB1SVNVPROC)load(\"glVertexAttrib1svNV\");\n\tglad_glVertexAttrib2dNV = (PFNGLVERTEXATTRIB2DNVPROC)load(\"glVertexAttrib2dNV\");\n\tglad_glVertexAttrib2dvNV = (PFNGLVERTEXATTRIB2DVNVPROC)load(\"glVertexAttrib2dvNV\");\n\tglad_glVertexAttrib2fNV = (PFNGLVERTEXATTRIB2FNVPROC)load(\"glVertexAttrib2fNV\");\n\tglad_glVertexAttrib2fvNV = (PFNGLVERTEXATTRIB2FVNVPROC)load(\"glVertexAttrib2fvNV\");\n\tglad_glVertexAttrib2sNV = (PFNGLVERTEXATTRIB2SNVPROC)load(\"glVertexAttrib2sNV\");\n\tglad_glVertexAttrib2svNV = (PFNGLVERTEXATTRIB2SVNVPROC)load(\"glVertexAttrib2svNV\");\n\tglad_glVertexAttrib3dNV = (PFNGLVERTEXATTRIB3DNVPROC)load(\"glVertexAttrib3dNV\");\n\tglad_glVertexAttrib3dvNV = (PFNGLVERTEXATTRIB3DVNVPROC)load(\"glVertexAttrib3dvNV\");\n\tglad_glVertexAttrib3fNV = (PFNGLVERTEXATTRIB3FNVPROC)load(\"glVertexAttrib3fNV\");\n\tglad_glVertexAttrib3fvNV = (PFNGLVERTEXATTRIB3FVNVPROC)load(\"glVertexAttrib3fvNV\");\n\tglad_glVertexAttrib3sNV = (PFNGLVERTEXATTRIB3SNVPROC)load(\"glVertexAttrib3sNV\");\n\tglad_glVertexAttrib3svNV = (PFNGLVERTEXATTRIB3SVNVPROC)load(\"glVertexAttrib3svNV\");\n\tglad_glVertexAttrib4dNV = (PFNGLVERTEXATTRIB4DNVPROC)load(\"glVertexAttrib4dNV\");\n\tglad_glVertexAttrib4dvNV = (PFNGLVERTEXATTRIB4DVNVPROC)load(\"glVertexAttrib4dvNV\");\n\tglad_glVertexAttrib4fNV = (PFNGLVERTEXATTRIB4FNVPROC)load(\"glVertexAttrib4fNV\");\n\tglad_glVertexAttrib4fvNV = (PFNGLVERTEXATTRIB4FVNVPROC)load(\"glVertexAttrib4fvNV\");\n\tglad_glVertexAttrib4sNV = (PFNGLVERTEXATTRIB4SNVPROC)load(\"glVertexAttrib4sNV\");\n\tglad_glVertexAttrib4svNV = (PFNGLVERTEXATTRIB4SVNVPROC)load(\"glVertexAttrib4svNV\");\n\tglad_glVertexAttrib4ubNV = (PFNGLVERTEXATTRIB4UBNVPROC)load(\"glVertexAttrib4ubNV\");\n\tglad_glVertexAttrib4ubvNV = (PFNGLVERTEXATTRIB4UBVNVPROC)load(\"glVertexAttrib4ubvNV\");\n\tglad_glVertexAttribs1dvNV = (PFNGLVERTEXATTRIBS1DVNVPROC)load(\"glVertexAttribs1dvNV\");\n\tglad_glVertexAttribs1fvNV = (PFNGLVERTEXATTRIBS1FVNVPROC)load(\"glVertexAttribs1fvNV\");\n\tglad_glVertexAttribs1svNV = (PFNGLVERTEXATTRIBS1SVNVPROC)load(\"glVertexAttribs1svNV\");\n\tglad_glVertexAttribs2dvNV = (PFNGLVERTEXATTRIBS2DVNVPROC)load(\"glVertexAttribs2dvNV\");\n\tglad_glVertexAttribs2fvNV = (PFNGLVERTEXATTRIBS2FVNVPROC)load(\"glVertexAttribs2fvNV\");\n\tglad_glVertexAttribs2svNV = (PFNGLVERTEXATTRIBS2SVNVPROC)load(\"glVertexAttribs2svNV\");\n\tglad_glVertexAttribs3dvNV = (PFNGLVERTEXATTRIBS3DVNVPROC)load(\"glVertexAttribs3dvNV\");\n\tglad_glVertexAttribs3fvNV = (PFNGLVERTEXATTRIBS3FVNVPROC)load(\"glVertexAttribs3fvNV\");\n\tglad_glVertexAttribs3svNV = (PFNGLVERTEXATTRIBS3SVNVPROC)load(\"glVertexAttribs3svNV\");\n\tglad_glVertexAttribs4dvNV = (PFNGLVERTEXATTRIBS4DVNVPROC)load(\"glVertexAttribs4dvNV\");\n\tglad_glVertexAttribs4fvNV = (PFNGLVERTEXATTRIBS4FVNVPROC)load(\"glVertexAttribs4fvNV\");\n\tglad_glVertexAttribs4svNV = (PFNGLVERTEXATTRIBS4SVNVPROC)load(\"glVertexAttribs4svNV\");\n\tglad_glVertexAttribs4ubvNV = (PFNGLVERTEXATTRIBS4UBVNVPROC)load(\"glVertexAttribs4ubvNV\");\n}\nstatic void load_GL_NVX_conditional_render(GLADloadproc load) {\n\tif(!GLAD_GL_NVX_conditional_render) return;\n\tglad_glBeginConditionalRenderNVX = (PFNGLBEGINCONDITIONALRENDERNVXPROC)load(\"glBeginConditionalRenderNVX\");\n\tglad_glEndConditionalRenderNVX = (PFNGLENDCONDITIONALRENDERNVXPROC)load(\"glEndConditionalRenderNVX\");\n}\nstatic void load_GL_EXT_vertex_shader(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_vertex_shader) return;\n\tglad_glBeginVertexShaderEXT = (PFNGLBEGINVERTEXSHADEREXTPROC)load(\"glBeginVertexShaderEXT\");\n\tglad_glEndVertexShaderEXT = (PFNGLENDVERTEXSHADEREXTPROC)load(\"glEndVertexShaderEXT\");\n\tglad_glBindVertexShaderEXT = (PFNGLBINDVERTEXSHADEREXTPROC)load(\"glBindVertexShaderEXT\");\n\tglad_glGenVertexShadersEXT = (PFNGLGENVERTEXSHADERSEXTPROC)load(\"glGenVertexShadersEXT\");\n\tglad_glDeleteVertexShaderEXT = (PFNGLDELETEVERTEXSHADEREXTPROC)load(\"glDeleteVertexShaderEXT\");\n\tglad_glShaderOp1EXT = (PFNGLSHADEROP1EXTPROC)load(\"glShaderOp1EXT\");\n\tglad_glShaderOp2EXT = (PFNGLSHADEROP2EXTPROC)load(\"glShaderOp2EXT\");\n\tglad_glShaderOp3EXT = (PFNGLSHADEROP3EXTPROC)load(\"glShaderOp3EXT\");\n\tglad_glSwizzleEXT = (PFNGLSWIZZLEEXTPROC)load(\"glSwizzleEXT\");\n\tglad_glWriteMaskEXT = (PFNGLWRITEMASKEXTPROC)load(\"glWriteMaskEXT\");\n\tglad_glInsertComponentEXT = (PFNGLINSERTCOMPONENTEXTPROC)load(\"glInsertComponentEXT\");\n\tglad_glExtractComponentEXT = (PFNGLEXTRACTCOMPONENTEXTPROC)load(\"glExtractComponentEXT\");\n\tglad_glGenSymbolsEXT = (PFNGLGENSYMBOLSEXTPROC)load(\"glGenSymbolsEXT\");\n\tglad_glSetInvariantEXT = (PFNGLSETINVARIANTEXTPROC)load(\"glSetInvariantEXT\");\n\tglad_glSetLocalConstantEXT = (PFNGLSETLOCALCONSTANTEXTPROC)load(\"glSetLocalConstantEXT\");\n\tglad_glVariantbvEXT = (PFNGLVARIANTBVEXTPROC)load(\"glVariantbvEXT\");\n\tglad_glVariantsvEXT = (PFNGLVARIANTSVEXTPROC)load(\"glVariantsvEXT\");\n\tglad_glVariantivEXT = (PFNGLVARIANTIVEXTPROC)load(\"glVariantivEXT\");\n\tglad_glVariantfvEXT = (PFNGLVARIANTFVEXTPROC)load(\"glVariantfvEXT\");\n\tglad_glVariantdvEXT = (PFNGLVARIANTDVEXTPROC)load(\"glVariantdvEXT\");\n\tglad_glVariantubvEXT = (PFNGLVARIANTUBVEXTPROC)load(\"glVariantubvEXT\");\n\tglad_glVariantusvEXT = (PFNGLVARIANTUSVEXTPROC)load(\"glVariantusvEXT\");\n\tglad_glVariantuivEXT = (PFNGLVARIANTUIVEXTPROC)load(\"glVariantuivEXT\");\n\tglad_glVariantPointerEXT = (PFNGLVARIANTPOINTEREXTPROC)load(\"glVariantPointerEXT\");\n\tglad_glEnableVariantClientStateEXT = (PFNGLENABLEVARIANTCLIENTSTATEEXTPROC)load(\"glEnableVariantClientStateEXT\");\n\tglad_glDisableVariantClientStateEXT = (PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC)load(\"glDisableVariantClientStateEXT\");\n\tglad_glBindLightParameterEXT = (PFNGLBINDLIGHTPARAMETEREXTPROC)load(\"glBindLightParameterEXT\");\n\tglad_glBindMaterialParameterEXT = (PFNGLBINDMATERIALPARAMETEREXTPROC)load(\"glBindMaterialParameterEXT\");\n\tglad_glBindTexGenParameterEXT = (PFNGLBINDTEXGENPARAMETEREXTPROC)load(\"glBindTexGenParameterEXT\");\n\tglad_glBindTextureUnitParameterEXT = (PFNGLBINDTEXTUREUNITPARAMETEREXTPROC)load(\"glBindTextureUnitParameterEXT\");\n\tglad_glBindParameterEXT = (PFNGLBINDPARAMETEREXTPROC)load(\"glBindParameterEXT\");\n\tglad_glIsVariantEnabledEXT = (PFNGLISVARIANTENABLEDEXTPROC)load(\"glIsVariantEnabledEXT\");\n\tglad_glGetVariantBooleanvEXT = (PFNGLGETVARIANTBOOLEANVEXTPROC)load(\"glGetVariantBooleanvEXT\");\n\tglad_glGetVariantIntegervEXT = (PFNGLGETVARIANTINTEGERVEXTPROC)load(\"glGetVariantIntegervEXT\");\n\tglad_glGetVariantFloatvEXT = (PFNGLGETVARIANTFLOATVEXTPROC)load(\"glGetVariantFloatvEXT\");\n\tglad_glGetVariantPointervEXT = (PFNGLGETVARIANTPOINTERVEXTPROC)load(\"glGetVariantPointervEXT\");\n\tglad_glGetInvariantBooleanvEXT = (PFNGLGETINVARIANTBOOLEANVEXTPROC)load(\"glGetInvariantBooleanvEXT\");\n\tglad_glGetInvariantIntegervEXT = (PFNGLGETINVARIANTINTEGERVEXTPROC)load(\"glGetInvariantIntegervEXT\");\n\tglad_glGetInvariantFloatvEXT = (PFNGLGETINVARIANTFLOATVEXTPROC)load(\"glGetInvariantFloatvEXT\");\n\tglad_glGetLocalConstantBooleanvEXT = (PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC)load(\"glGetLocalConstantBooleanvEXT\");\n\tglad_glGetLocalConstantIntegervEXT = (PFNGLGETLOCALCONSTANTINTEGERVEXTPROC)load(\"glGetLocalConstantIntegervEXT\");\n\tglad_glGetLocalConstantFloatvEXT = (PFNGLGETLOCALCONSTANTFLOATVEXTPROC)load(\"glGetLocalConstantFloatvEXT\");\n}\nstatic void load_GL_EXT_blend_func_separate(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_blend_func_separate) return;\n\tglad_glBlendFuncSeparateEXT = (PFNGLBLENDFUNCSEPARATEEXTPROC)load(\"glBlendFuncSeparateEXT\");\n}\nstatic void load_GL_APPLE_fence(GLADloadproc load) {\n\tif(!GLAD_GL_APPLE_fence) return;\n\tglad_glGenFencesAPPLE = (PFNGLGENFENCESAPPLEPROC)load(\"glGenFencesAPPLE\");\n\tglad_glDeleteFencesAPPLE = (PFNGLDELETEFENCESAPPLEPROC)load(\"glDeleteFencesAPPLE\");\n\tglad_glSetFenceAPPLE = (PFNGLSETFENCEAPPLEPROC)load(\"glSetFenceAPPLE\");\n\tglad_glIsFenceAPPLE = (PFNGLISFENCEAPPLEPROC)load(\"glIsFenceAPPLE\");\n\tglad_glTestFenceAPPLE = (PFNGLTESTFENCEAPPLEPROC)load(\"glTestFenceAPPLE\");\n\tglad_glFinishFenceAPPLE = (PFNGLFINISHFENCEAPPLEPROC)load(\"glFinishFenceAPPLE\");\n\tglad_glTestObjectAPPLE = (PFNGLTESTOBJECTAPPLEPROC)load(\"glTestObjectAPPLE\");\n\tglad_glFinishObjectAPPLE = (PFNGLFINISHOBJECTAPPLEPROC)load(\"glFinishObjectAPPLE\");\n}\nstatic void load_GL_OES_byte_coordinates(GLADloadproc load) {\n\tif(!GLAD_GL_OES_byte_coordinates) return;\n\tglad_glMultiTexCoord1bOES = (PFNGLMULTITEXCOORD1BOESPROC)load(\"glMultiTexCoord1bOES\");\n\tglad_glMultiTexCoord1bvOES = (PFNGLMULTITEXCOORD1BVOESPROC)load(\"glMultiTexCoord1bvOES\");\n\tglad_glMultiTexCoord2bOES = (PFNGLMULTITEXCOORD2BOESPROC)load(\"glMultiTexCoord2bOES\");\n\tglad_glMultiTexCoord2bvOES = (PFNGLMULTITEXCOORD2BVOESPROC)load(\"glMultiTexCoord2bvOES\");\n\tglad_glMultiTexCoord3bOES = (PFNGLMULTITEXCOORD3BOESPROC)load(\"glMultiTexCoord3bOES\");\n\tglad_glMultiTexCoord3bvOES = (PFNGLMULTITEXCOORD3BVOESPROC)load(\"glMultiTexCoord3bvOES\");\n\tglad_glMultiTexCoord4bOES = (PFNGLMULTITEXCOORD4BOESPROC)load(\"glMultiTexCoord4bOES\");\n\tglad_glMultiTexCoord4bvOES = (PFNGLMULTITEXCOORD4BVOESPROC)load(\"glMultiTexCoord4bvOES\");\n\tglad_glTexCoord1bOES = (PFNGLTEXCOORD1BOESPROC)load(\"glTexCoord1bOES\");\n\tglad_glTexCoord1bvOES = (PFNGLTEXCOORD1BVOESPROC)load(\"glTexCoord1bvOES\");\n\tglad_glTexCoord2bOES = (PFNGLTEXCOORD2BOESPROC)load(\"glTexCoord2bOES\");\n\tglad_glTexCoord2bvOES = (PFNGLTEXCOORD2BVOESPROC)load(\"glTexCoord2bvOES\");\n\tglad_glTexCoord3bOES = (PFNGLTEXCOORD3BOESPROC)load(\"glTexCoord3bOES\");\n\tglad_glTexCoord3bvOES = (PFNGLTEXCOORD3BVOESPROC)load(\"glTexCoord3bvOES\");\n\tglad_glTexCoord4bOES = (PFNGLTEXCOORD4BOESPROC)load(\"glTexCoord4bOES\");\n\tglad_glTexCoord4bvOES = (PFNGLTEXCOORD4BVOESPROC)load(\"glTexCoord4bvOES\");\n\tglad_glVertex2bOES = (PFNGLVERTEX2BOESPROC)load(\"glVertex2bOES\");\n\tglad_glVertex2bvOES = (PFNGLVERTEX2BVOESPROC)load(\"glVertex2bvOES\");\n\tglad_glVertex3bOES = (PFNGLVERTEX3BOESPROC)load(\"glVertex3bOES\");\n\tglad_glVertex3bvOES = (PFNGLVERTEX3BVOESPROC)load(\"glVertex3bvOES\");\n\tglad_glVertex4bOES = (PFNGLVERTEX4BOESPROC)load(\"glVertex4bOES\");\n\tglad_glVertex4bvOES = (PFNGLVERTEX4BVOESPROC)load(\"glVertex4bvOES\");\n}\nstatic void load_GL_ARB_transpose_matrix(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_transpose_matrix) return;\n\tglad_glLoadTransposeMatrixfARB = (PFNGLLOADTRANSPOSEMATRIXFARBPROC)load(\"glLoadTransposeMatrixfARB\");\n\tglad_glLoadTransposeMatrixdARB = (PFNGLLOADTRANSPOSEMATRIXDARBPROC)load(\"glLoadTransposeMatrixdARB\");\n\tglad_glMultTransposeMatrixfARB = (PFNGLMULTTRANSPOSEMATRIXFARBPROC)load(\"glMultTransposeMatrixfARB\");\n\tglad_glMultTransposeMatrixdARB = (PFNGLMULTTRANSPOSEMATRIXDARBPROC)load(\"glMultTransposeMatrixdARB\");\n}\nstatic void load_GL_ARB_provoking_vertex(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_provoking_vertex) return;\n\tglad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC)load(\"glProvokingVertex\");\n}\nstatic void load_GL_EXT_fog_coord(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_fog_coord) return;\n\tglad_glFogCoordfEXT = (PFNGLFOGCOORDFEXTPROC)load(\"glFogCoordfEXT\");\n\tglad_glFogCoordfvEXT = (PFNGLFOGCOORDFVEXTPROC)load(\"glFogCoordfvEXT\");\n\tglad_glFogCoorddEXT = (PFNGLFOGCOORDDEXTPROC)load(\"glFogCoorddEXT\");\n\tglad_glFogCoorddvEXT = (PFNGLFOGCOORDDVEXTPROC)load(\"glFogCoorddvEXT\");\n\tglad_glFogCoordPointerEXT = (PFNGLFOGCOORDPOINTEREXTPROC)load(\"glFogCoordPointerEXT\");\n}\nstatic void load_GL_EXT_vertex_array(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_vertex_array) return;\n\tglad_glArrayElementEXT = (PFNGLARRAYELEMENTEXTPROC)load(\"glArrayElementEXT\");\n\tglad_glColorPointerEXT = (PFNGLCOLORPOINTEREXTPROC)load(\"glColorPointerEXT\");\n\tglad_glDrawArraysEXT = (PFNGLDRAWARRAYSEXTPROC)load(\"glDrawArraysEXT\");\n\tglad_glEdgeFlagPointerEXT = (PFNGLEDGEFLAGPOINTEREXTPROC)load(\"glEdgeFlagPointerEXT\");\n\tglad_glGetPointervEXT = (PFNGLGETPOINTERVEXTPROC)load(\"glGetPointervEXT\");\n\tglad_glIndexPointerEXT = (PFNGLINDEXPOINTEREXTPROC)load(\"glIndexPointerEXT\");\n\tglad_glNormalPointerEXT = (PFNGLNORMALPOINTEREXTPROC)load(\"glNormalPointerEXT\");\n\tglad_glTexCoordPointerEXT = (PFNGLTEXCOORDPOINTEREXTPROC)load(\"glTexCoordPointerEXT\");\n\tglad_glVertexPointerEXT = (PFNGLVERTEXPOINTEREXTPROC)load(\"glVertexPointerEXT\");\n}\nstatic void load_GL_EXT_blend_equation_separate(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_blend_equation_separate) return;\n\tglad_glBlendEquationSeparateEXT = (PFNGLBLENDEQUATIONSEPARATEEXTPROC)load(\"glBlendEquationSeparateEXT\");\n}\nstatic void load_GL_ARB_multi_draw_indirect(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_multi_draw_indirect) return;\n\tglad_glMultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC)load(\"glMultiDrawArraysIndirect\");\n\tglad_glMultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC)load(\"glMultiDrawElementsIndirect\");\n}\nstatic void load_GL_NV_copy_image(GLADloadproc load) {\n\tif(!GLAD_GL_NV_copy_image) return;\n\tglad_glCopyImageSubDataNV = (PFNGLCOPYIMAGESUBDATANVPROC)load(\"glCopyImageSubDataNV\");\n}\nstatic void load_GL_ARB_transform_feedback2(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_transform_feedback2) return;\n\tglad_glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)load(\"glBindTransformFeedback\");\n\tglad_glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC)load(\"glDeleteTransformFeedbacks\");\n\tglad_glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)load(\"glGenTransformFeedbacks\");\n\tglad_glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC)load(\"glIsTransformFeedback\");\n\tglad_glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC)load(\"glPauseTransformFeedback\");\n\tglad_glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC)load(\"glResumeTransformFeedback\");\n\tglad_glDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC)load(\"glDrawTransformFeedback\");\n}\nstatic void load_GL_ARB_transform_feedback3(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_transform_feedback3) return;\n\tglad_glDrawTransformFeedbackStream = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)load(\"glDrawTransformFeedbackStream\");\n\tglad_glBeginQueryIndexed = (PFNGLBEGINQUERYINDEXEDPROC)load(\"glBeginQueryIndexed\");\n\tglad_glEndQueryIndexed = (PFNGLENDQUERYINDEXEDPROC)load(\"glEndQueryIndexed\");\n\tglad_glGetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC)load(\"glGetQueryIndexediv\");\n}\nstatic void load_GL_EXT_debug_marker(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_debug_marker) return;\n\tglad_glInsertEventMarkerEXT = (PFNGLINSERTEVENTMARKEREXTPROC)load(\"glInsertEventMarkerEXT\");\n\tglad_glPushGroupMarkerEXT = (PFNGLPUSHGROUPMARKEREXTPROC)load(\"glPushGroupMarkerEXT\");\n\tglad_glPopGroupMarkerEXT = (PFNGLPOPGROUPMARKEREXTPROC)load(\"glPopGroupMarkerEXT\");\n}\nstatic void load_GL_EXT_pixel_transform(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_pixel_transform) return;\n\tglad_glPixelTransformParameteriEXT = (PFNGLPIXELTRANSFORMPARAMETERIEXTPROC)load(\"glPixelTransformParameteriEXT\");\n\tglad_glPixelTransformParameterfEXT = (PFNGLPIXELTRANSFORMPARAMETERFEXTPROC)load(\"glPixelTransformParameterfEXT\");\n\tglad_glPixelTransformParameterivEXT = (PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC)load(\"glPixelTransformParameterivEXT\");\n\tglad_glPixelTransformParameterfvEXT = (PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC)load(\"glPixelTransformParameterfvEXT\");\n\tglad_glGetPixelTransformParameterivEXT = (PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC)load(\"glGetPixelTransformParameterivEXT\");\n\tglad_glGetPixelTransformParameterfvEXT = (PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC)load(\"glGetPixelTransformParameterfvEXT\");\n}\nstatic void load_GL_ATI_fragment_shader(GLADloadproc load) {\n\tif(!GLAD_GL_ATI_fragment_shader) return;\n\tglad_glGenFragmentShadersATI = (PFNGLGENFRAGMENTSHADERSATIPROC)load(\"glGenFragmentShadersATI\");\n\tglad_glBindFragmentShaderATI = (PFNGLBINDFRAGMENTSHADERATIPROC)load(\"glBindFragmentShaderATI\");\n\tglad_glDeleteFragmentShaderATI = (PFNGLDELETEFRAGMENTSHADERATIPROC)load(\"glDeleteFragmentShaderATI\");\n\tglad_glBeginFragmentShaderATI = (PFNGLBEGINFRAGMENTSHADERATIPROC)load(\"glBeginFragmentShaderATI\");\n\tglad_glEndFragmentShaderATI = (PFNGLENDFRAGMENTSHADERATIPROC)load(\"glEndFragmentShaderATI\");\n\tglad_glPassTexCoordATI = (PFNGLPASSTEXCOORDATIPROC)load(\"glPassTexCoordATI\");\n\tglad_glSampleMapATI = (PFNGLSAMPLEMAPATIPROC)load(\"glSampleMapATI\");\n\tglad_glColorFragmentOp1ATI = (PFNGLCOLORFRAGMENTOP1ATIPROC)load(\"glColorFragmentOp1ATI\");\n\tglad_glColorFragmentOp2ATI = (PFNGLCOLORFRAGMENTOP2ATIPROC)load(\"glColorFragmentOp2ATI\");\n\tglad_glColorFragmentOp3ATI = (PFNGLCOLORFRAGMENTOP3ATIPROC)load(\"glColorFragmentOp3ATI\");\n\tglad_glAlphaFragmentOp1ATI = (PFNGLALPHAFRAGMENTOP1ATIPROC)load(\"glAlphaFragmentOp1ATI\");\n\tglad_glAlphaFragmentOp2ATI = (PFNGLALPHAFRAGMENTOP2ATIPROC)load(\"glAlphaFragmentOp2ATI\");\n\tglad_glAlphaFragmentOp3ATI = (PFNGLALPHAFRAGMENTOP3ATIPROC)load(\"glAlphaFragmentOp3ATI\");\n\tglad_glSetFragmentShaderConstantATI = (PFNGLSETFRAGMENTSHADERCONSTANTATIPROC)load(\"glSetFragmentShaderConstantATI\");\n}\nstatic void load_GL_ARB_vertex_array_object(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_vertex_array_object) return;\n\tglad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)load(\"glBindVertexArray\");\n\tglad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)load(\"glDeleteVertexArrays\");\n\tglad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)load(\"glGenVertexArrays\");\n\tglad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC)load(\"glIsVertexArray\");\n}\nstatic void load_GL_SUN_triangle_list(GLADloadproc load) {\n\tif(!GLAD_GL_SUN_triangle_list) return;\n\tglad_glReplacementCodeuiSUN = (PFNGLREPLACEMENTCODEUISUNPROC)load(\"glReplacementCodeuiSUN\");\n\tglad_glReplacementCodeusSUN = (PFNGLREPLACEMENTCODEUSSUNPROC)load(\"glReplacementCodeusSUN\");\n\tglad_glReplacementCodeubSUN = (PFNGLREPLACEMENTCODEUBSUNPROC)load(\"glReplacementCodeubSUN\");\n\tglad_glReplacementCodeuivSUN = (PFNGLREPLACEMENTCODEUIVSUNPROC)load(\"glReplacementCodeuivSUN\");\n\tglad_glReplacementCodeusvSUN = (PFNGLREPLACEMENTCODEUSVSUNPROC)load(\"glReplacementCodeusvSUN\");\n\tglad_glReplacementCodeubvSUN = (PFNGLREPLACEMENTCODEUBVSUNPROC)load(\"glReplacementCodeubvSUN\");\n\tglad_glReplacementCodePointerSUN = (PFNGLREPLACEMENTCODEPOINTERSUNPROC)load(\"glReplacementCodePointerSUN\");\n}\nstatic void load_GL_ARB_transform_feedback_instanced(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_transform_feedback_instanced) return;\n\tglad_glDrawTransformFeedbackInstanced = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)load(\"glDrawTransformFeedbackInstanced\");\n\tglad_glDrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)load(\"glDrawTransformFeedbackStreamInstanced\");\n}\nstatic void load_GL_SGIX_async(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_async) return;\n\tglad_glAsyncMarkerSGIX = (PFNGLASYNCMARKERSGIXPROC)load(\"glAsyncMarkerSGIX\");\n\tglad_glFinishAsyncSGIX = (PFNGLFINISHASYNCSGIXPROC)load(\"glFinishAsyncSGIX\");\n\tglad_glPollAsyncSGIX = (PFNGLPOLLASYNCSGIXPROC)load(\"glPollAsyncSGIX\");\n\tglad_glGenAsyncMarkersSGIX = (PFNGLGENASYNCMARKERSSGIXPROC)load(\"glGenAsyncMarkersSGIX\");\n\tglad_glDeleteAsyncMarkersSGIX = (PFNGLDELETEASYNCMARKERSSGIXPROC)load(\"glDeleteAsyncMarkersSGIX\");\n\tglad_glIsAsyncMarkerSGIX = (PFNGLISASYNCMARKERSGIXPROC)load(\"glIsAsyncMarkerSGIX\");\n}\nstatic void load_GL_INTEL_performance_query(GLADloadproc load) {\n\tif(!GLAD_GL_INTEL_performance_query) return;\n\tglad_glBeginPerfQueryINTEL = (PFNGLBEGINPERFQUERYINTELPROC)load(\"glBeginPerfQueryINTEL\");\n\tglad_glCreatePerfQueryINTEL = (PFNGLCREATEPERFQUERYINTELPROC)load(\"glCreatePerfQueryINTEL\");\n\tglad_glDeletePerfQueryINTEL = (PFNGLDELETEPERFQUERYINTELPROC)load(\"glDeletePerfQueryINTEL\");\n\tglad_glEndPerfQueryINTEL = (PFNGLENDPERFQUERYINTELPROC)load(\"glEndPerfQueryINTEL\");\n\tglad_glGetFirstPerfQueryIdINTEL = (PFNGLGETFIRSTPERFQUERYIDINTELPROC)load(\"glGetFirstPerfQueryIdINTEL\");\n\tglad_glGetNextPerfQueryIdINTEL = (PFNGLGETNEXTPERFQUERYIDINTELPROC)load(\"glGetNextPerfQueryIdINTEL\");\n\tglad_glGetPerfCounterInfoINTEL = (PFNGLGETPERFCOUNTERINFOINTELPROC)load(\"glGetPerfCounterInfoINTEL\");\n\tglad_glGetPerfQueryDataINTEL = (PFNGLGETPERFQUERYDATAINTELPROC)load(\"glGetPerfQueryDataINTEL\");\n\tglad_glGetPerfQueryIdByNameINTEL = (PFNGLGETPERFQUERYIDBYNAMEINTELPROC)load(\"glGetPerfQueryIdByNameINTEL\");\n\tglad_glGetPerfQueryInfoINTEL = (PFNGLGETPERFQUERYINFOINTELPROC)load(\"glGetPerfQueryInfoINTEL\");\n}\nstatic void load_GL_NV_gpu_shader5(GLADloadproc load) {\n\tif(!GLAD_GL_NV_gpu_shader5) return;\n\tglad_glUniform1i64NV = (PFNGLUNIFORM1I64NVPROC)load(\"glUniform1i64NV\");\n\tglad_glUniform2i64NV = (PFNGLUNIFORM2I64NVPROC)load(\"glUniform2i64NV\");\n\tglad_glUniform3i64NV = (PFNGLUNIFORM3I64NVPROC)load(\"glUniform3i64NV\");\n\tglad_glUniform4i64NV = (PFNGLUNIFORM4I64NVPROC)load(\"glUniform4i64NV\");\n\tglad_glUniform1i64vNV = (PFNGLUNIFORM1I64VNVPROC)load(\"glUniform1i64vNV\");\n\tglad_glUniform2i64vNV = (PFNGLUNIFORM2I64VNVPROC)load(\"glUniform2i64vNV\");\n\tglad_glUniform3i64vNV = (PFNGLUNIFORM3I64VNVPROC)load(\"glUniform3i64vNV\");\n\tglad_glUniform4i64vNV = (PFNGLUNIFORM4I64VNVPROC)load(\"glUniform4i64vNV\");\n\tglad_glUniform1ui64NV = (PFNGLUNIFORM1UI64NVPROC)load(\"glUniform1ui64NV\");\n\tglad_glUniform2ui64NV = (PFNGLUNIFORM2UI64NVPROC)load(\"glUniform2ui64NV\");\n\tglad_glUniform3ui64NV = (PFNGLUNIFORM3UI64NVPROC)load(\"glUniform3ui64NV\");\n\tglad_glUniform4ui64NV = (PFNGLUNIFORM4UI64NVPROC)load(\"glUniform4ui64NV\");\n\tglad_glUniform1ui64vNV = (PFNGLUNIFORM1UI64VNVPROC)load(\"glUniform1ui64vNV\");\n\tglad_glUniform2ui64vNV = (PFNGLUNIFORM2UI64VNVPROC)load(\"glUniform2ui64vNV\");\n\tglad_glUniform3ui64vNV = (PFNGLUNIFORM3UI64VNVPROC)load(\"glUniform3ui64vNV\");\n\tglad_glUniform4ui64vNV = (PFNGLUNIFORM4UI64VNVPROC)load(\"glUniform4ui64vNV\");\n\tglad_glGetUniformi64vNV = (PFNGLGETUNIFORMI64VNVPROC)load(\"glGetUniformi64vNV\");\n\tglad_glProgramUniform1i64NV = (PFNGLPROGRAMUNIFORM1I64NVPROC)load(\"glProgramUniform1i64NV\");\n\tglad_glProgramUniform2i64NV = (PFNGLPROGRAMUNIFORM2I64NVPROC)load(\"glProgramUniform2i64NV\");\n\tglad_glProgramUniform3i64NV = (PFNGLPROGRAMUNIFORM3I64NVPROC)load(\"glProgramUniform3i64NV\");\n\tglad_glProgramUniform4i64NV = (PFNGLPROGRAMUNIFORM4I64NVPROC)load(\"glProgramUniform4i64NV\");\n\tglad_glProgramUniform1i64vNV = (PFNGLPROGRAMUNIFORM1I64VNVPROC)load(\"glProgramUniform1i64vNV\");\n\tglad_glProgramUniform2i64vNV = (PFNGLPROGRAMUNIFORM2I64VNVPROC)load(\"glProgramUniform2i64vNV\");\n\tglad_glProgramUniform3i64vNV = (PFNGLPROGRAMUNIFORM3I64VNVPROC)load(\"glProgramUniform3i64vNV\");\n\tglad_glProgramUniform4i64vNV = (PFNGLPROGRAMUNIFORM4I64VNVPROC)load(\"glProgramUniform4i64vNV\");\n\tglad_glProgramUniform1ui64NV = (PFNGLPROGRAMUNIFORM1UI64NVPROC)load(\"glProgramUniform1ui64NV\");\n\tglad_glProgramUniform2ui64NV = (PFNGLPROGRAMUNIFORM2UI64NVPROC)load(\"glProgramUniform2ui64NV\");\n\tglad_glProgramUniform3ui64NV = (PFNGLPROGRAMUNIFORM3UI64NVPROC)load(\"glProgramUniform3ui64NV\");\n\tglad_glProgramUniform4ui64NV = (PFNGLPROGRAMUNIFORM4UI64NVPROC)load(\"glProgramUniform4ui64NV\");\n\tglad_glProgramUniform1ui64vNV = (PFNGLPROGRAMUNIFORM1UI64VNVPROC)load(\"glProgramUniform1ui64vNV\");\n\tglad_glProgramUniform2ui64vNV = (PFNGLPROGRAMUNIFORM2UI64VNVPROC)load(\"glProgramUniform2ui64vNV\");\n\tglad_glProgramUniform3ui64vNV = (PFNGLPROGRAMUNIFORM3UI64VNVPROC)load(\"glProgramUniform3ui64vNV\");\n\tglad_glProgramUniform4ui64vNV = (PFNGLPROGRAMUNIFORM4UI64VNVPROC)load(\"glProgramUniform4ui64vNV\");\n}\nstatic void load_GL_ARB_ES2_compatibility(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_ES2_compatibility) return;\n\tglad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC)load(\"glReleaseShaderCompiler\");\n\tglad_glShaderBinary = (PFNGLSHADERBINARYPROC)load(\"glShaderBinary\");\n\tglad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC)load(\"glGetShaderPrecisionFormat\");\n\tglad_glDepthRangef = (PFNGLDEPTHRANGEFPROC)load(\"glDepthRangef\");\n\tglad_glClearDepthf = (PFNGLCLEARDEPTHFPROC)load(\"glClearDepthf\");\n}\nstatic void load_GL_ARB_indirect_parameters(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_indirect_parameters) return;\n\tglad_glMultiDrawArraysIndirectCountARB = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC)load(\"glMultiDrawArraysIndirectCountARB\");\n\tglad_glMultiDrawElementsIndirectCountARB = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC)load(\"glMultiDrawElementsIndirectCountARB\");\n}\nstatic void load_GL_NV_half_float(GLADloadproc load) {\n\tif(!GLAD_GL_NV_half_float) return;\n\tglad_glVertex2hNV = (PFNGLVERTEX2HNVPROC)load(\"glVertex2hNV\");\n\tglad_glVertex2hvNV = (PFNGLVERTEX2HVNVPROC)load(\"glVertex2hvNV\");\n\tglad_glVertex3hNV = (PFNGLVERTEX3HNVPROC)load(\"glVertex3hNV\");\n\tglad_glVertex3hvNV = (PFNGLVERTEX3HVNVPROC)load(\"glVertex3hvNV\");\n\tglad_glVertex4hNV = (PFNGLVERTEX4HNVPROC)load(\"glVertex4hNV\");\n\tglad_glVertex4hvNV = (PFNGLVERTEX4HVNVPROC)load(\"glVertex4hvNV\");\n\tglad_glNormal3hNV = (PFNGLNORMAL3HNVPROC)load(\"glNormal3hNV\");\n\tglad_glNormal3hvNV = (PFNGLNORMAL3HVNVPROC)load(\"glNormal3hvNV\");\n\tglad_glColor3hNV = (PFNGLCOLOR3HNVPROC)load(\"glColor3hNV\");\n\tglad_glColor3hvNV = (PFNGLCOLOR3HVNVPROC)load(\"glColor3hvNV\");\n\tglad_glColor4hNV = (PFNGLCOLOR4HNVPROC)load(\"glColor4hNV\");\n\tglad_glColor4hvNV = (PFNGLCOLOR4HVNVPROC)load(\"glColor4hvNV\");\n\tglad_glTexCoord1hNV = (PFNGLTEXCOORD1HNVPROC)load(\"glTexCoord1hNV\");\n\tglad_glTexCoord1hvNV = (PFNGLTEXCOORD1HVNVPROC)load(\"glTexCoord1hvNV\");\n\tglad_glTexCoord2hNV = (PFNGLTEXCOORD2HNVPROC)load(\"glTexCoord2hNV\");\n\tglad_glTexCoord2hvNV = (PFNGLTEXCOORD2HVNVPROC)load(\"glTexCoord2hvNV\");\n\tglad_glTexCoord3hNV = (PFNGLTEXCOORD3HNVPROC)load(\"glTexCoord3hNV\");\n\tglad_glTexCoord3hvNV = (PFNGLTEXCOORD3HVNVPROC)load(\"glTexCoord3hvNV\");\n\tglad_glTexCoord4hNV = (PFNGLTEXCOORD4HNVPROC)load(\"glTexCoord4hNV\");\n\tglad_glTexCoord4hvNV = (PFNGLTEXCOORD4HVNVPROC)load(\"glTexCoord4hvNV\");\n\tglad_glMultiTexCoord1hNV = (PFNGLMULTITEXCOORD1HNVPROC)load(\"glMultiTexCoord1hNV\");\n\tglad_glMultiTexCoord1hvNV = (PFNGLMULTITEXCOORD1HVNVPROC)load(\"glMultiTexCoord1hvNV\");\n\tglad_glMultiTexCoord2hNV = (PFNGLMULTITEXCOORD2HNVPROC)load(\"glMultiTexCoord2hNV\");\n\tglad_glMultiTexCoord2hvNV = (PFNGLMULTITEXCOORD2HVNVPROC)load(\"glMultiTexCoord2hvNV\");\n\tglad_glMultiTexCoord3hNV = (PFNGLMULTITEXCOORD3HNVPROC)load(\"glMultiTexCoord3hNV\");\n\tglad_glMultiTexCoord3hvNV = (PFNGLMULTITEXCOORD3HVNVPROC)load(\"glMultiTexCoord3hvNV\");\n\tglad_glMultiTexCoord4hNV = (PFNGLMULTITEXCOORD4HNVPROC)load(\"glMultiTexCoord4hNV\");\n\tglad_glMultiTexCoord4hvNV = (PFNGLMULTITEXCOORD4HVNVPROC)load(\"glMultiTexCoord4hvNV\");\n\tglad_glFogCoordhNV = (PFNGLFOGCOORDHNVPROC)load(\"glFogCoordhNV\");\n\tglad_glFogCoordhvNV = (PFNGLFOGCOORDHVNVPROC)load(\"glFogCoordhvNV\");\n\tglad_glSecondaryColor3hNV = (PFNGLSECONDARYCOLOR3HNVPROC)load(\"glSecondaryColor3hNV\");\n\tglad_glSecondaryColor3hvNV = (PFNGLSECONDARYCOLOR3HVNVPROC)load(\"glSecondaryColor3hvNV\");\n\tglad_glVertexWeighthNV = (PFNGLVERTEXWEIGHTHNVPROC)load(\"glVertexWeighthNV\");\n\tglad_glVertexWeighthvNV = (PFNGLVERTEXWEIGHTHVNVPROC)load(\"glVertexWeighthvNV\");\n\tglad_glVertexAttrib1hNV = (PFNGLVERTEXATTRIB1HNVPROC)load(\"glVertexAttrib1hNV\");\n\tglad_glVertexAttrib1hvNV = (PFNGLVERTEXATTRIB1HVNVPROC)load(\"glVertexAttrib1hvNV\");\n\tglad_glVertexAttrib2hNV = (PFNGLVERTEXATTRIB2HNVPROC)load(\"glVertexAttrib2hNV\");\n\tglad_glVertexAttrib2hvNV = (PFNGLVERTEXATTRIB2HVNVPROC)load(\"glVertexAttrib2hvNV\");\n\tglad_glVertexAttrib3hNV = (PFNGLVERTEXATTRIB3HNVPROC)load(\"glVertexAttrib3hNV\");\n\tglad_glVertexAttrib3hvNV = (PFNGLVERTEXATTRIB3HVNVPROC)load(\"glVertexAttrib3hvNV\");\n\tglad_glVertexAttrib4hNV = (PFNGLVERTEXATTRIB4HNVPROC)load(\"glVertexAttrib4hNV\");\n\tglad_glVertexAttrib4hvNV = (PFNGLVERTEXATTRIB4HVNVPROC)load(\"glVertexAttrib4hvNV\");\n\tglad_glVertexAttribs1hvNV = (PFNGLVERTEXATTRIBS1HVNVPROC)load(\"glVertexAttribs1hvNV\");\n\tglad_glVertexAttribs2hvNV = (PFNGLVERTEXATTRIBS2HVNVPROC)load(\"glVertexAttribs2hvNV\");\n\tglad_glVertexAttribs3hvNV = (PFNGLVERTEXATTRIBS3HVNVPROC)load(\"glVertexAttribs3hvNV\");\n\tglad_glVertexAttribs4hvNV = (PFNGLVERTEXATTRIBS4HVNVPROC)load(\"glVertexAttribs4hvNV\");\n}\nstatic void load_GL_EXT_coordinate_frame(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_coordinate_frame) return;\n\tglad_glTangent3bEXT = (PFNGLTANGENT3BEXTPROC)load(\"glTangent3bEXT\");\n\tglad_glTangent3bvEXT = (PFNGLTANGENT3BVEXTPROC)load(\"glTangent3bvEXT\");\n\tglad_glTangent3dEXT = (PFNGLTANGENT3DEXTPROC)load(\"glTangent3dEXT\");\n\tglad_glTangent3dvEXT = (PFNGLTANGENT3DVEXTPROC)load(\"glTangent3dvEXT\");\n\tglad_glTangent3fEXT = (PFNGLTANGENT3FEXTPROC)load(\"glTangent3fEXT\");\n\tglad_glTangent3fvEXT = (PFNGLTANGENT3FVEXTPROC)load(\"glTangent3fvEXT\");\n\tglad_glTangent3iEXT = (PFNGLTANGENT3IEXTPROC)load(\"glTangent3iEXT\");\n\tglad_glTangent3ivEXT = (PFNGLTANGENT3IVEXTPROC)load(\"glTangent3ivEXT\");\n\tglad_glTangent3sEXT = (PFNGLTANGENT3SEXTPROC)load(\"glTangent3sEXT\");\n\tglad_glTangent3svEXT = (PFNGLTANGENT3SVEXTPROC)load(\"glTangent3svEXT\");\n\tglad_glBinormal3bEXT = (PFNGLBINORMAL3BEXTPROC)load(\"glBinormal3bEXT\");\n\tglad_glBinormal3bvEXT = (PFNGLBINORMAL3BVEXTPROC)load(\"glBinormal3bvEXT\");\n\tglad_glBinormal3dEXT = (PFNGLBINORMAL3DEXTPROC)load(\"glBinormal3dEXT\");\n\tglad_glBinormal3dvEXT = (PFNGLBINORMAL3DVEXTPROC)load(\"glBinormal3dvEXT\");\n\tglad_glBinormal3fEXT = (PFNGLBINORMAL3FEXTPROC)load(\"glBinormal3fEXT\");\n\tglad_glBinormal3fvEXT = (PFNGLBINORMAL3FVEXTPROC)load(\"glBinormal3fvEXT\");\n\tglad_glBinormal3iEXT = (PFNGLBINORMAL3IEXTPROC)load(\"glBinormal3iEXT\");\n\tglad_glBinormal3ivEXT = (PFNGLBINORMAL3IVEXTPROC)load(\"glBinormal3ivEXT\");\n\tglad_glBinormal3sEXT = (PFNGLBINORMAL3SEXTPROC)load(\"glBinormal3sEXT\");\n\tglad_glBinormal3svEXT = (PFNGLBINORMAL3SVEXTPROC)load(\"glBinormal3svEXT\");\n\tglad_glTangentPointerEXT = (PFNGLTANGENTPOINTEREXTPROC)load(\"glTangentPointerEXT\");\n\tglad_glBinormalPointerEXT = (PFNGLBINORMALPOINTEREXTPROC)load(\"glBinormalPointerEXT\");\n}\nstatic void load_GL_EXT_compiled_vertex_array(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_compiled_vertex_array) return;\n\tglad_glLockArraysEXT = (PFNGLLOCKARRAYSEXTPROC)load(\"glLockArraysEXT\");\n\tglad_glUnlockArraysEXT = (PFNGLUNLOCKARRAYSEXTPROC)load(\"glUnlockArraysEXT\");\n}\nstatic void load_GL_NV_depth_buffer_float(GLADloadproc load) {\n\tif(!GLAD_GL_NV_depth_buffer_float) return;\n\tglad_glDepthRangedNV = (PFNGLDEPTHRANGEDNVPROC)load(\"glDepthRangedNV\");\n\tglad_glClearDepthdNV = (PFNGLCLEARDEPTHDNVPROC)load(\"glClearDepthdNV\");\n\tglad_glDepthBoundsdNV = (PFNGLDEPTHBOUNDSDNVPROC)load(\"glDepthBoundsdNV\");\n}\nstatic void load_GL_NV_occlusion_query(GLADloadproc load) {\n\tif(!GLAD_GL_NV_occlusion_query) return;\n\tglad_glGenOcclusionQueriesNV = (PFNGLGENOCCLUSIONQUERIESNVPROC)load(\"glGenOcclusionQueriesNV\");\n\tglad_glDeleteOcclusionQueriesNV = (PFNGLDELETEOCCLUSIONQUERIESNVPROC)load(\"glDeleteOcclusionQueriesNV\");\n\tglad_glIsOcclusionQueryNV = (PFNGLISOCCLUSIONQUERYNVPROC)load(\"glIsOcclusionQueryNV\");\n\tglad_glBeginOcclusionQueryNV = (PFNGLBEGINOCCLUSIONQUERYNVPROC)load(\"glBeginOcclusionQueryNV\");\n\tglad_glEndOcclusionQueryNV = (PFNGLENDOCCLUSIONQUERYNVPROC)load(\"glEndOcclusionQueryNV\");\n\tglad_glGetOcclusionQueryivNV = (PFNGLGETOCCLUSIONQUERYIVNVPROC)load(\"glGetOcclusionQueryivNV\");\n\tglad_glGetOcclusionQueryuivNV = (PFNGLGETOCCLUSIONQUERYUIVNVPROC)load(\"glGetOcclusionQueryuivNV\");\n}\nstatic void load_GL_APPLE_flush_buffer_range(GLADloadproc load) {\n\tif(!GLAD_GL_APPLE_flush_buffer_range) return;\n\tglad_glBufferParameteriAPPLE = (PFNGLBUFFERPARAMETERIAPPLEPROC)load(\"glBufferParameteriAPPLE\");\n\tglad_glFlushMappedBufferRangeAPPLE = (PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC)load(\"glFlushMappedBufferRangeAPPLE\");\n}\nstatic void load_GL_ARB_imaging(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_imaging) return;\n\tglad_glBlendColor = (PFNGLBLENDCOLORPROC)load(\"glBlendColor\");\n\tglad_glBlendEquation = (PFNGLBLENDEQUATIONPROC)load(\"glBlendEquation\");\n\tglad_glColorTable = (PFNGLCOLORTABLEPROC)load(\"glColorTable\");\n\tglad_glColorTableParameterfv = (PFNGLCOLORTABLEPARAMETERFVPROC)load(\"glColorTableParameterfv\");\n\tglad_glColorTableParameteriv = (PFNGLCOLORTABLEPARAMETERIVPROC)load(\"glColorTableParameteriv\");\n\tglad_glCopyColorTable = (PFNGLCOPYCOLORTABLEPROC)load(\"glCopyColorTable\");\n\tglad_glGetColorTable = (PFNGLGETCOLORTABLEPROC)load(\"glGetColorTable\");\n\tglad_glGetColorTableParameterfv = (PFNGLGETCOLORTABLEPARAMETERFVPROC)load(\"glGetColorTableParameterfv\");\n\tglad_glGetColorTableParameteriv = (PFNGLGETCOLORTABLEPARAMETERIVPROC)load(\"glGetColorTableParameteriv\");\n\tglad_glColorSubTable = (PFNGLCOLORSUBTABLEPROC)load(\"glColorSubTable\");\n\tglad_glCopyColorSubTable = (PFNGLCOPYCOLORSUBTABLEPROC)load(\"glCopyColorSubTable\");\n\tglad_glConvolutionFilter1D = (PFNGLCONVOLUTIONFILTER1DPROC)load(\"glConvolutionFilter1D\");\n\tglad_glConvolutionFilter2D = (PFNGLCONVOLUTIONFILTER2DPROC)load(\"glConvolutionFilter2D\");\n\tglad_glConvolutionParameterf = (PFNGLCONVOLUTIONPARAMETERFPROC)load(\"glConvolutionParameterf\");\n\tglad_glConvolutionParameterfv = (PFNGLCONVOLUTIONPARAMETERFVPROC)load(\"glConvolutionParameterfv\");\n\tglad_glConvolutionParameteri = (PFNGLCONVOLUTIONPARAMETERIPROC)load(\"glConvolutionParameteri\");\n\tglad_glConvolutionParameteriv = (PFNGLCONVOLUTIONPARAMETERIVPROC)load(\"glConvolutionParameteriv\");\n\tglad_glCopyConvolutionFilter1D = (PFNGLCOPYCONVOLUTIONFILTER1DPROC)load(\"glCopyConvolutionFilter1D\");\n\tglad_glCopyConvolutionFilter2D = (PFNGLCOPYCONVOLUTIONFILTER2DPROC)load(\"glCopyConvolutionFilter2D\");\n\tglad_glGetConvolutionFilter = (PFNGLGETCONVOLUTIONFILTERPROC)load(\"glGetConvolutionFilter\");\n\tglad_glGetConvolutionParameterfv = (PFNGLGETCONVOLUTIONPARAMETERFVPROC)load(\"glGetConvolutionParameterfv\");\n\tglad_glGetConvolutionParameteriv = (PFNGLGETCONVOLUTIONPARAMETERIVPROC)load(\"glGetConvolutionParameteriv\");\n\tglad_glGetSeparableFilter = (PFNGLGETSEPARABLEFILTERPROC)load(\"glGetSeparableFilter\");\n\tglad_glSeparableFilter2D = (PFNGLSEPARABLEFILTER2DPROC)load(\"glSeparableFilter2D\");\n\tglad_glGetHistogram = (PFNGLGETHISTOGRAMPROC)load(\"glGetHistogram\");\n\tglad_glGetHistogramParameterfv = (PFNGLGETHISTOGRAMPARAMETERFVPROC)load(\"glGetHistogramParameterfv\");\n\tglad_glGetHistogramParameteriv = (PFNGLGETHISTOGRAMPARAMETERIVPROC)load(\"glGetHistogramParameteriv\");\n\tglad_glGetMinmax = (PFNGLGETMINMAXPROC)load(\"glGetMinmax\");\n\tglad_glGetMinmaxParameterfv = (PFNGLGETMINMAXPARAMETERFVPROC)load(\"glGetMinmaxParameterfv\");\n\tglad_glGetMinmaxParameteriv = (PFNGLGETMINMAXPARAMETERIVPROC)load(\"glGetMinmaxParameteriv\");\n\tglad_glHistogram = (PFNGLHISTOGRAMPROC)load(\"glHistogram\");\n\tglad_glMinmax = (PFNGLMINMAXPROC)load(\"glMinmax\");\n\tglad_glResetHistogram = (PFNGLRESETHISTOGRAMPROC)load(\"glResetHistogram\");\n\tglad_glResetMinmax = (PFNGLRESETMINMAXPROC)load(\"glResetMinmax\");\n}\nstatic void load_GL_ARB_draw_buffers_blend(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_draw_buffers_blend) return;\n\tglad_glBlendEquationiARB = (PFNGLBLENDEQUATIONIARBPROC)load(\"glBlendEquationiARB\");\n\tglad_glBlendEquationSeparateiARB = (PFNGLBLENDEQUATIONSEPARATEIARBPROC)load(\"glBlendEquationSeparateiARB\");\n\tglad_glBlendFunciARB = (PFNGLBLENDFUNCIARBPROC)load(\"glBlendFunciARB\");\n\tglad_glBlendFuncSeparateiARB = (PFNGLBLENDFUNCSEPARATEIARBPROC)load(\"glBlendFuncSeparateiARB\");\n}\nstatic void load_GL_ARB_clear_buffer_object(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_clear_buffer_object) return;\n\tglad_glClearBufferData = (PFNGLCLEARBUFFERDATAPROC)load(\"glClearBufferData\");\n\tglad_glClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC)load(\"glClearBufferSubData\");\n}\nstatic void load_GL_ARB_multisample(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_multisample) return;\n\tglad_glSampleCoverageARB = (PFNGLSAMPLECOVERAGEARBPROC)load(\"glSampleCoverageARB\");\n}\nstatic void load_GL_EXT_debug_label(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_debug_label) return;\n\tglad_glLabelObjectEXT = (PFNGLLABELOBJECTEXTPROC)load(\"glLabelObjectEXT\");\n\tglad_glGetObjectLabelEXT = (PFNGLGETOBJECTLABELEXTPROC)load(\"glGetObjectLabelEXT\");\n}\nstatic void load_GL_ARB_sample_shading(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_sample_shading) return;\n\tglad_glMinSampleShadingARB = (PFNGLMINSAMPLESHADINGARBPROC)load(\"glMinSampleShadingARB\");\n}\nstatic void load_GL_INTEL_map_texture(GLADloadproc load) {\n\tif(!GLAD_GL_INTEL_map_texture) return;\n\tglad_glSyncTextureINTEL = (PFNGLSYNCTEXTUREINTELPROC)load(\"glSyncTextureINTEL\");\n\tglad_glUnmapTexture2DINTEL = (PFNGLUNMAPTEXTURE2DINTELPROC)load(\"glUnmapTexture2DINTEL\");\n\tglad_glMapTexture2DINTEL = (PFNGLMAPTEXTURE2DINTELPROC)load(\"glMapTexture2DINTEL\");\n}\nstatic void load_GL_ARB_compute_shader(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_compute_shader) return;\n\tglad_glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)load(\"glDispatchCompute\");\n\tglad_glDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC)load(\"glDispatchComputeIndirect\");\n}\nstatic void load_GL_IBM_vertex_array_lists(GLADloadproc load) {\n\tif(!GLAD_GL_IBM_vertex_array_lists) return;\n\tglad_glColorPointerListIBM = (PFNGLCOLORPOINTERLISTIBMPROC)load(\"glColorPointerListIBM\");\n\tglad_glSecondaryColorPointerListIBM = (PFNGLSECONDARYCOLORPOINTERLISTIBMPROC)load(\"glSecondaryColorPointerListIBM\");\n\tglad_glEdgeFlagPointerListIBM = (PFNGLEDGEFLAGPOINTERLISTIBMPROC)load(\"glEdgeFlagPointerListIBM\");\n\tglad_glFogCoordPointerListIBM = (PFNGLFOGCOORDPOINTERLISTIBMPROC)load(\"glFogCoordPointerListIBM\");\n\tglad_glIndexPointerListIBM = (PFNGLINDEXPOINTERLISTIBMPROC)load(\"glIndexPointerListIBM\");\n\tglad_glNormalPointerListIBM = (PFNGLNORMALPOINTERLISTIBMPROC)load(\"glNormalPointerListIBM\");\n\tglad_glTexCoordPointerListIBM = (PFNGLTEXCOORDPOINTERLISTIBMPROC)load(\"glTexCoordPointerListIBM\");\n\tglad_glVertexPointerListIBM = (PFNGLVERTEXPOINTERLISTIBMPROC)load(\"glVertexPointerListIBM\");\n}\nstatic void load_GL_ARB_color_buffer_float(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_color_buffer_float) return;\n\tglad_glClampColorARB = (PFNGLCLAMPCOLORARBPROC)load(\"glClampColorARB\");\n}\nstatic void load_GL_ARB_bindless_texture(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_bindless_texture) return;\n\tglad_glGetTextureHandleARB = (PFNGLGETTEXTUREHANDLEARBPROC)load(\"glGetTextureHandleARB\");\n\tglad_glGetTextureSamplerHandleARB = (PFNGLGETTEXTURESAMPLERHANDLEARBPROC)load(\"glGetTextureSamplerHandleARB\");\n\tglad_glMakeTextureHandleResidentARB = (PFNGLMAKETEXTUREHANDLERESIDENTARBPROC)load(\"glMakeTextureHandleResidentARB\");\n\tglad_glMakeTextureHandleNonResidentARB = (PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC)load(\"glMakeTextureHandleNonResidentARB\");\n\tglad_glGetImageHandleARB = (PFNGLGETIMAGEHANDLEARBPROC)load(\"glGetImageHandleARB\");\n\tglad_glMakeImageHandleResidentARB = (PFNGLMAKEIMAGEHANDLERESIDENTARBPROC)load(\"glMakeImageHandleResidentARB\");\n\tglad_glMakeImageHandleNonResidentARB = (PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC)load(\"glMakeImageHandleNonResidentARB\");\n\tglad_glUniformHandleui64ARB = (PFNGLUNIFORMHANDLEUI64ARBPROC)load(\"glUniformHandleui64ARB\");\n\tglad_glUniformHandleui64vARB = (PFNGLUNIFORMHANDLEUI64VARBPROC)load(\"glUniformHandleui64vARB\");\n\tglad_glProgramUniformHandleui64ARB = (PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC)load(\"glProgramUniformHandleui64ARB\");\n\tglad_glProgramUniformHandleui64vARB = (PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC)load(\"glProgramUniformHandleui64vARB\");\n\tglad_glIsTextureHandleResidentARB = (PFNGLISTEXTUREHANDLERESIDENTARBPROC)load(\"glIsTextureHandleResidentARB\");\n\tglad_glIsImageHandleResidentARB = (PFNGLISIMAGEHANDLERESIDENTARBPROC)load(\"glIsImageHandleResidentARB\");\n\tglad_glVertexAttribL1ui64ARB = (PFNGLVERTEXATTRIBL1UI64ARBPROC)load(\"glVertexAttribL1ui64ARB\");\n\tglad_glVertexAttribL1ui64vARB = (PFNGLVERTEXATTRIBL1UI64VARBPROC)load(\"glVertexAttribL1ui64vARB\");\n\tglad_glGetVertexAttribLui64vARB = (PFNGLGETVERTEXATTRIBLUI64VARBPROC)load(\"glGetVertexAttribLui64vARB\");\n}\nstatic void load_GL_ARB_window_pos(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_window_pos) return;\n\tglad_glWindowPos2dARB = (PFNGLWINDOWPOS2DARBPROC)load(\"glWindowPos2dARB\");\n\tglad_glWindowPos2dvARB = (PFNGLWINDOWPOS2DVARBPROC)load(\"glWindowPos2dvARB\");\n\tglad_glWindowPos2fARB = (PFNGLWINDOWPOS2FARBPROC)load(\"glWindowPos2fARB\");\n\tglad_glWindowPos2fvARB = (PFNGLWINDOWPOS2FVARBPROC)load(\"glWindowPos2fvARB\");\n\tglad_glWindowPos2iARB = (PFNGLWINDOWPOS2IARBPROC)load(\"glWindowPos2iARB\");\n\tglad_glWindowPos2ivARB = (PFNGLWINDOWPOS2IVARBPROC)load(\"glWindowPos2ivARB\");\n\tglad_glWindowPos2sARB = (PFNGLWINDOWPOS2SARBPROC)load(\"glWindowPos2sARB\");\n\tglad_glWindowPos2svARB = (PFNGLWINDOWPOS2SVARBPROC)load(\"glWindowPos2svARB\");\n\tglad_glWindowPos3dARB = (PFNGLWINDOWPOS3DARBPROC)load(\"glWindowPos3dARB\");\n\tglad_glWindowPos3dvARB = (PFNGLWINDOWPOS3DVARBPROC)load(\"glWindowPos3dvARB\");\n\tglad_glWindowPos3fARB = (PFNGLWINDOWPOS3FARBPROC)load(\"glWindowPos3fARB\");\n\tglad_glWindowPos3fvARB = (PFNGLWINDOWPOS3FVARBPROC)load(\"glWindowPos3fvARB\");\n\tglad_glWindowPos3iARB = (PFNGLWINDOWPOS3IARBPROC)load(\"glWindowPos3iARB\");\n\tglad_glWindowPos3ivARB = (PFNGLWINDOWPOS3IVARBPROC)load(\"glWindowPos3ivARB\");\n\tglad_glWindowPos3sARB = (PFNGLWINDOWPOS3SARBPROC)load(\"glWindowPos3sARB\");\n\tglad_glWindowPos3svARB = (PFNGLWINDOWPOS3SVARBPROC)load(\"glWindowPos3svARB\");\n}\nstatic void load_GL_ARB_internalformat_query(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_internalformat_query) return;\n\tglad_glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC)load(\"glGetInternalformativ\");\n}\nstatic void load_GL_EXT_shader_image_load_store(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_shader_image_load_store) return;\n\tglad_glBindImageTextureEXT = (PFNGLBINDIMAGETEXTUREEXTPROC)load(\"glBindImageTextureEXT\");\n\tglad_glMemoryBarrierEXT = (PFNGLMEMORYBARRIEREXTPROC)load(\"glMemoryBarrierEXT\");\n}\nstatic void load_GL_EXT_copy_texture(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_copy_texture) return;\n\tglad_glCopyTexImage1DEXT = (PFNGLCOPYTEXIMAGE1DEXTPROC)load(\"glCopyTexImage1DEXT\");\n\tglad_glCopyTexImage2DEXT = (PFNGLCOPYTEXIMAGE2DEXTPROC)load(\"glCopyTexImage2DEXT\");\n\tglad_glCopyTexSubImage1DEXT = (PFNGLCOPYTEXSUBIMAGE1DEXTPROC)load(\"glCopyTexSubImage1DEXT\");\n\tglad_glCopyTexSubImage2DEXT = (PFNGLCOPYTEXSUBIMAGE2DEXTPROC)load(\"glCopyTexSubImage2DEXT\");\n\tglad_glCopyTexSubImage3DEXT = (PFNGLCOPYTEXSUBIMAGE3DEXTPROC)load(\"glCopyTexSubImage3DEXT\");\n}\nstatic void load_GL_NV_register_combiners2(GLADloadproc load) {\n\tif(!GLAD_GL_NV_register_combiners2) return;\n\tglad_glCombinerStageParameterfvNV = (PFNGLCOMBINERSTAGEPARAMETERFVNVPROC)load(\"glCombinerStageParameterfvNV\");\n\tglad_glGetCombinerStageParameterfvNV = (PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC)load(\"glGetCombinerStageParameterfvNV\");\n}\nstatic void load_GL_NV_draw_texture(GLADloadproc load) {\n\tif(!GLAD_GL_NV_draw_texture) return;\n\tglad_glDrawTextureNV = (PFNGLDRAWTEXTURENVPROC)load(\"glDrawTextureNV\");\n}\nstatic void load_GL_EXT_draw_instanced(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_draw_instanced) return;\n\tglad_glDrawArraysInstancedEXT = (PFNGLDRAWARRAYSINSTANCEDEXTPROC)load(\"glDrawArraysInstancedEXT\");\n\tglad_glDrawElementsInstancedEXT = (PFNGLDRAWELEMENTSINSTANCEDEXTPROC)load(\"glDrawElementsInstancedEXT\");\n}\nstatic void load_GL_ARB_viewport_array(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_viewport_array) return;\n\tglad_glViewportArrayv = (PFNGLVIEWPORTARRAYVPROC)load(\"glViewportArrayv\");\n\tglad_glViewportIndexedf = (PFNGLVIEWPORTINDEXEDFPROC)load(\"glViewportIndexedf\");\n\tglad_glViewportIndexedfv = (PFNGLVIEWPORTINDEXEDFVPROC)load(\"glViewportIndexedfv\");\n\tglad_glScissorArrayv = (PFNGLSCISSORARRAYVPROC)load(\"glScissorArrayv\");\n\tglad_glScissorIndexed = (PFNGLSCISSORINDEXEDPROC)load(\"glScissorIndexed\");\n\tglad_glScissorIndexedv = (PFNGLSCISSORINDEXEDVPROC)load(\"glScissorIndexedv\");\n\tglad_glDepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC)load(\"glDepthRangeArrayv\");\n\tglad_glDepthRangeIndexed = (PFNGLDEPTHRANGEINDEXEDPROC)load(\"glDepthRangeIndexed\");\n\tglad_glGetFloati_v = (PFNGLGETFLOATI_VPROC)load(\"glGetFloati_v\");\n\tglad_glGetDoublei_v = (PFNGLGETDOUBLEI_VPROC)load(\"glGetDoublei_v\");\n}\nstatic void load_GL_ARB_separate_shader_objects(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_separate_shader_objects) return;\n\tglad_glUseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC)load(\"glUseProgramStages\");\n\tglad_glActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC)load(\"glActiveShaderProgram\");\n\tglad_glCreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC)load(\"glCreateShaderProgramv\");\n\tglad_glBindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC)load(\"glBindProgramPipeline\");\n\tglad_glDeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC)load(\"glDeleteProgramPipelines\");\n\tglad_glGenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC)load(\"glGenProgramPipelines\");\n\tglad_glIsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC)load(\"glIsProgramPipeline\");\n\tglad_glGetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC)load(\"glGetProgramPipelineiv\");\n\tglad_glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC)load(\"glProgramUniform1i\");\n\tglad_glProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC)load(\"glProgramUniform1iv\");\n\tglad_glProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC)load(\"glProgramUniform1f\");\n\tglad_glProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC)load(\"glProgramUniform1fv\");\n\tglad_glProgramUniform1d = (PFNGLPROGRAMUNIFORM1DPROC)load(\"glProgramUniform1d\");\n\tglad_glProgramUniform1dv = (PFNGLPROGRAMUNIFORM1DVPROC)load(\"glProgramUniform1dv\");\n\tglad_glProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC)load(\"glProgramUniform1ui\");\n\tglad_glProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC)load(\"glProgramUniform1uiv\");\n\tglad_glProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC)load(\"glProgramUniform2i\");\n\tglad_glProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC)load(\"glProgramUniform2iv\");\n\tglad_glProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC)load(\"glProgramUniform2f\");\n\tglad_glProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC)load(\"glProgramUniform2fv\");\n\tglad_glProgramUniform2d = (PFNGLPROGRAMUNIFORM2DPROC)load(\"glProgramUniform2d\");\n\tglad_glProgramUniform2dv = (PFNGLPROGRAMUNIFORM2DVPROC)load(\"glProgramUniform2dv\");\n\tglad_glProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC)load(\"glProgramUniform2ui\");\n\tglad_glProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC)load(\"glProgramUniform2uiv\");\n\tglad_glProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC)load(\"glProgramUniform3i\");\n\tglad_glProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC)load(\"glProgramUniform3iv\");\n\tglad_glProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC)load(\"glProgramUniform3f\");\n\tglad_glProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC)load(\"glProgramUniform3fv\");\n\tglad_glProgramUniform3d = (PFNGLPROGRAMUNIFORM3DPROC)load(\"glProgramUniform3d\");\n\tglad_glProgramUniform3dv = (PFNGLPROGRAMUNIFORM3DVPROC)load(\"glProgramUniform3dv\");\n\tglad_glProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC)load(\"glProgramUniform3ui\");\n\tglad_glProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC)load(\"glProgramUniform3uiv\");\n\tglad_glProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC)load(\"glProgramUniform4i\");\n\tglad_glProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC)load(\"glProgramUniform4iv\");\n\tglad_glProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC)load(\"glProgramUniform4f\");\n\tglad_glProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC)load(\"glProgramUniform4fv\");\n\tglad_glProgramUniform4d = (PFNGLPROGRAMUNIFORM4DPROC)load(\"glProgramUniform4d\");\n\tglad_glProgramUniform4dv = (PFNGLPROGRAMUNIFORM4DVPROC)load(\"glProgramUniform4dv\");\n\tglad_glProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC)load(\"glProgramUniform4ui\");\n\tglad_glProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC)load(\"glProgramUniform4uiv\");\n\tglad_glProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC)load(\"glProgramUniformMatrix2fv\");\n\tglad_glProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC)load(\"glProgramUniformMatrix3fv\");\n\tglad_glProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC)load(\"glProgramUniformMatrix4fv\");\n\tglad_glProgramUniformMatrix2dv = (PFNGLPROGRAMUNIFORMMATRIX2DVPROC)load(\"glProgramUniformMatrix2dv\");\n\tglad_glProgramUniformMatrix3dv = (PFNGLPROGRAMUNIFORMMATRIX3DVPROC)load(\"glProgramUniformMatrix3dv\");\n\tglad_glProgramUniformMatrix4dv = (PFNGLPROGRAMUNIFORMMATRIX4DVPROC)load(\"glProgramUniformMatrix4dv\");\n\tglad_glProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)load(\"glProgramUniformMatrix2x3fv\");\n\tglad_glProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)load(\"glProgramUniformMatrix3x2fv\");\n\tglad_glProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)load(\"glProgramUniformMatrix2x4fv\");\n\tglad_glProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)load(\"glProgramUniformMatrix4x2fv\");\n\tglad_glProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)load(\"glProgramUniformMatrix3x4fv\");\n\tglad_glProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)load(\"glProgramUniformMatrix4x3fv\");\n\tglad_glProgramUniformMatrix2x3dv = (PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)load(\"glProgramUniformMatrix2x3dv\");\n\tglad_glProgramUniformMatrix3x2dv = (PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)load(\"glProgramUniformMatrix3x2dv\");\n\tglad_glProgramUniformMatrix2x4dv = (PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)load(\"glProgramUniformMatrix2x4dv\");\n\tglad_glProgramUniformMatrix4x2dv = (PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)load(\"glProgramUniformMatrix4x2dv\");\n\tglad_glProgramUniformMatrix3x4dv = (PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)load(\"glProgramUniformMatrix3x4dv\");\n\tglad_glProgramUniformMatrix4x3dv = (PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)load(\"glProgramUniformMatrix4x3dv\");\n\tglad_glValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC)load(\"glValidateProgramPipeline\");\n\tglad_glGetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC)load(\"glGetProgramPipelineInfoLog\");\n}\nstatic void load_GL_EXT_depth_bounds_test(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_depth_bounds_test) return;\n\tglad_glDepthBoundsEXT = (PFNGLDEPTHBOUNDSEXTPROC)load(\"glDepthBoundsEXT\");\n}\nstatic void load_GL_HP_image_transform(GLADloadproc load) {\n\tif(!GLAD_GL_HP_image_transform) return;\n\tglad_glImageTransformParameteriHP = (PFNGLIMAGETRANSFORMPARAMETERIHPPROC)load(\"glImageTransformParameteriHP\");\n\tglad_glImageTransformParameterfHP = (PFNGLIMAGETRANSFORMPARAMETERFHPPROC)load(\"glImageTransformParameterfHP\");\n\tglad_glImageTransformParameterivHP = (PFNGLIMAGETRANSFORMPARAMETERIVHPPROC)load(\"glImageTransformParameterivHP\");\n\tglad_glImageTransformParameterfvHP = (PFNGLIMAGETRANSFORMPARAMETERFVHPPROC)load(\"glImageTransformParameterfvHP\");\n\tglad_glGetImageTransformParameterivHP = (PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC)load(\"glGetImageTransformParameterivHP\");\n\tglad_glGetImageTransformParameterfvHP = (PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC)load(\"glGetImageTransformParameterfvHP\");\n}\nstatic void load_GL_NV_video_capture(GLADloadproc load) {\n\tif(!GLAD_GL_NV_video_capture) return;\n\tglad_glBeginVideoCaptureNV = (PFNGLBEGINVIDEOCAPTURENVPROC)load(\"glBeginVideoCaptureNV\");\n\tglad_glBindVideoCaptureStreamBufferNV = (PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC)load(\"glBindVideoCaptureStreamBufferNV\");\n\tglad_glBindVideoCaptureStreamTextureNV = (PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC)load(\"glBindVideoCaptureStreamTextureNV\");\n\tglad_glEndVideoCaptureNV = (PFNGLENDVIDEOCAPTURENVPROC)load(\"glEndVideoCaptureNV\");\n\tglad_glGetVideoCaptureivNV = (PFNGLGETVIDEOCAPTUREIVNVPROC)load(\"glGetVideoCaptureivNV\");\n\tglad_glGetVideoCaptureStreamivNV = (PFNGLGETVIDEOCAPTURESTREAMIVNVPROC)load(\"glGetVideoCaptureStreamivNV\");\n\tglad_glGetVideoCaptureStreamfvNV = (PFNGLGETVIDEOCAPTURESTREAMFVNVPROC)load(\"glGetVideoCaptureStreamfvNV\");\n\tglad_glGetVideoCaptureStreamdvNV = (PFNGLGETVIDEOCAPTURESTREAMDVNVPROC)load(\"glGetVideoCaptureStreamdvNV\");\n\tglad_glVideoCaptureNV = (PFNGLVIDEOCAPTURENVPROC)load(\"glVideoCaptureNV\");\n\tglad_glVideoCaptureStreamParameterivNV = (PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC)load(\"glVideoCaptureStreamParameterivNV\");\n\tglad_glVideoCaptureStreamParameterfvNV = (PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC)load(\"glVideoCaptureStreamParameterfvNV\");\n\tglad_glVideoCaptureStreamParameterdvNV = (PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC)load(\"glVideoCaptureStreamParameterdvNV\");\n}\nstatic void load_GL_ARB_sampler_objects(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_sampler_objects) return;\n\tglad_glGenSamplers = (PFNGLGENSAMPLERSPROC)load(\"glGenSamplers\");\n\tglad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)load(\"glDeleteSamplers\");\n\tglad_glIsSampler = (PFNGLISSAMPLERPROC)load(\"glIsSampler\");\n\tglad_glBindSampler = (PFNGLBINDSAMPLERPROC)load(\"glBindSampler\");\n\tglad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)load(\"glSamplerParameteri\");\n\tglad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)load(\"glSamplerParameteriv\");\n\tglad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)load(\"glSamplerParameterf\");\n\tglad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)load(\"glSamplerParameterfv\");\n\tglad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)load(\"glSamplerParameterIiv\");\n\tglad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)load(\"glSamplerParameterIuiv\");\n\tglad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)load(\"glGetSamplerParameteriv\");\n\tglad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)load(\"glGetSamplerParameterIiv\");\n\tglad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)load(\"glGetSamplerParameterfv\");\n\tglad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)load(\"glGetSamplerParameterIuiv\");\n}\nstatic void load_GL_ARB_matrix_palette(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_matrix_palette) return;\n\tglad_glCurrentPaletteMatrixARB = (PFNGLCURRENTPALETTEMATRIXARBPROC)load(\"glCurrentPaletteMatrixARB\");\n\tglad_glMatrixIndexubvARB = (PFNGLMATRIXINDEXUBVARBPROC)load(\"glMatrixIndexubvARB\");\n\tglad_glMatrixIndexusvARB = (PFNGLMATRIXINDEXUSVARBPROC)load(\"glMatrixIndexusvARB\");\n\tglad_glMatrixIndexuivARB = (PFNGLMATRIXINDEXUIVARBPROC)load(\"glMatrixIndexuivARB\");\n\tglad_glMatrixIndexPointerARB = (PFNGLMATRIXINDEXPOINTERARBPROC)load(\"glMatrixIndexPointerARB\");\n}\nstatic void load_GL_SGIS_texture_color_mask(GLADloadproc load) {\n\tif(!GLAD_GL_SGIS_texture_color_mask) return;\n\tglad_glTextureColorMaskSGIS = (PFNGLTEXTURECOLORMASKSGISPROC)load(\"glTextureColorMaskSGIS\");\n}\nstatic void load_GL_ARB_texture_compression(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_texture_compression) return;\n\tglad_glCompressedTexImage3DARB = (PFNGLCOMPRESSEDTEXIMAGE3DARBPROC)load(\"glCompressedTexImage3DARB\");\n\tglad_glCompressedTexImage2DARB = (PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)load(\"glCompressedTexImage2DARB\");\n\tglad_glCompressedTexImage1DARB = (PFNGLCOMPRESSEDTEXIMAGE1DARBPROC)load(\"glCompressedTexImage1DARB\");\n\tglad_glCompressedTexSubImage3DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC)load(\"glCompressedTexSubImage3DARB\");\n\tglad_glCompressedTexSubImage2DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC)load(\"glCompressedTexSubImage2DARB\");\n\tglad_glCompressedTexSubImage1DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC)load(\"glCompressedTexSubImage1DARB\");\n\tglad_glGetCompressedTexImageARB = (PFNGLGETCOMPRESSEDTEXIMAGEARBPROC)load(\"glGetCompressedTexImageARB\");\n}\nstatic void load_GL_ARB_shader_subroutine(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_shader_subroutine) return;\n\tglad_glGetSubroutineUniformLocation = (PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)load(\"glGetSubroutineUniformLocation\");\n\tglad_glGetSubroutineIndex = (PFNGLGETSUBROUTINEINDEXPROC)load(\"glGetSubroutineIndex\");\n\tglad_glGetActiveSubroutineUniformiv = (PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)load(\"glGetActiveSubroutineUniformiv\");\n\tglad_glGetActiveSubroutineUniformName = (PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)load(\"glGetActiveSubroutineUniformName\");\n\tglad_glGetActiveSubroutineName = (PFNGLGETACTIVESUBROUTINENAMEPROC)load(\"glGetActiveSubroutineName\");\n\tglad_glUniformSubroutinesuiv = (PFNGLUNIFORMSUBROUTINESUIVPROC)load(\"glUniformSubroutinesuiv\");\n\tglad_glGetUniformSubroutineuiv = (PFNGLGETUNIFORMSUBROUTINEUIVPROC)load(\"glGetUniformSubroutineuiv\");\n\tglad_glGetProgramStageiv = (PFNGLGETPROGRAMSTAGEIVPROC)load(\"glGetProgramStageiv\");\n}\nstatic void load_GL_ARB_texture_storage_multisample(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_texture_storage_multisample) return;\n\tglad_glTexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC)load(\"glTexStorage2DMultisample\");\n\tglad_glTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)load(\"glTexStorage3DMultisample\");\n}\nstatic void load_GL_EXT_vertex_attrib_64bit(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_vertex_attrib_64bit) return;\n\tglad_glVertexAttribL1dEXT = (PFNGLVERTEXATTRIBL1DEXTPROC)load(\"glVertexAttribL1dEXT\");\n\tglad_glVertexAttribL2dEXT = (PFNGLVERTEXATTRIBL2DEXTPROC)load(\"glVertexAttribL2dEXT\");\n\tglad_glVertexAttribL3dEXT = (PFNGLVERTEXATTRIBL3DEXTPROC)load(\"glVertexAttribL3dEXT\");\n\tglad_glVertexAttribL4dEXT = (PFNGLVERTEXATTRIBL4DEXTPROC)load(\"glVertexAttribL4dEXT\");\n\tglad_glVertexAttribL1dvEXT = (PFNGLVERTEXATTRIBL1DVEXTPROC)load(\"glVertexAttribL1dvEXT\");\n\tglad_glVertexAttribL2dvEXT = (PFNGLVERTEXATTRIBL2DVEXTPROC)load(\"glVertexAttribL2dvEXT\");\n\tglad_glVertexAttribL3dvEXT = (PFNGLVERTEXATTRIBL3DVEXTPROC)load(\"glVertexAttribL3dvEXT\");\n\tglad_glVertexAttribL4dvEXT = (PFNGLVERTEXATTRIBL4DVEXTPROC)load(\"glVertexAttribL4dvEXT\");\n\tglad_glVertexAttribLPointerEXT = (PFNGLVERTEXATTRIBLPOINTEREXTPROC)load(\"glVertexAttribLPointerEXT\");\n\tglad_glGetVertexAttribLdvEXT = (PFNGLGETVERTEXATTRIBLDVEXTPROC)load(\"glGetVertexAttribLdvEXT\");\n}\nstatic void load_GL_OES_query_matrix(GLADloadproc load) {\n\tif(!GLAD_GL_OES_query_matrix) return;\n\tglad_glQueryMatrixxOES = (PFNGLQUERYMATRIXXOESPROC)load(\"glQueryMatrixxOES\");\n}\nstatic void load_GL_APPLE_texture_range(GLADloadproc load) {\n\tif(!GLAD_GL_APPLE_texture_range) return;\n\tglad_glTextureRangeAPPLE = (PFNGLTEXTURERANGEAPPLEPROC)load(\"glTextureRangeAPPLE\");\n\tglad_glGetTexParameterPointervAPPLE = (PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC)load(\"glGetTexParameterPointervAPPLE\");\n}\nstatic void load_GL_ARB_copy_buffer(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_copy_buffer) return;\n\tglad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC)load(\"glCopyBufferSubData\");\n}\nstatic void load_GL_APPLE_object_purgeable(GLADloadproc load) {\n\tif(!GLAD_GL_APPLE_object_purgeable) return;\n\tglad_glObjectPurgeableAPPLE = (PFNGLOBJECTPURGEABLEAPPLEPROC)load(\"glObjectPurgeableAPPLE\");\n\tglad_glObjectUnpurgeableAPPLE = (PFNGLOBJECTUNPURGEABLEAPPLEPROC)load(\"glObjectUnpurgeableAPPLE\");\n\tglad_glGetObjectParameterivAPPLE = (PFNGLGETOBJECTPARAMETERIVAPPLEPROC)load(\"glGetObjectParameterivAPPLE\");\n}\nstatic void load_GL_ARB_occlusion_query(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_occlusion_query) return;\n\tglad_glGenQueriesARB = (PFNGLGENQUERIESARBPROC)load(\"glGenQueriesARB\");\n\tglad_glDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC)load(\"glDeleteQueriesARB\");\n\tglad_glIsQueryARB = (PFNGLISQUERYARBPROC)load(\"glIsQueryARB\");\n\tglad_glBeginQueryARB = (PFNGLBEGINQUERYARBPROC)load(\"glBeginQueryARB\");\n\tglad_glEndQueryARB = (PFNGLENDQUERYARBPROC)load(\"glEndQueryARB\");\n\tglad_glGetQueryivARB = (PFNGLGETQUERYIVARBPROC)load(\"glGetQueryivARB\");\n\tglad_glGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC)load(\"glGetQueryObjectivARB\");\n\tglad_glGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC)load(\"glGetQueryObjectuivARB\");\n}\nstatic void load_GL_SGI_color_table(GLADloadproc load) {\n\tif(!GLAD_GL_SGI_color_table) return;\n\tglad_glColorTableSGI = (PFNGLCOLORTABLESGIPROC)load(\"glColorTableSGI\");\n\tglad_glColorTableParameterfvSGI = (PFNGLCOLORTABLEPARAMETERFVSGIPROC)load(\"glColorTableParameterfvSGI\");\n\tglad_glColorTableParameterivSGI = (PFNGLCOLORTABLEPARAMETERIVSGIPROC)load(\"glColorTableParameterivSGI\");\n\tglad_glCopyColorTableSGI = (PFNGLCOPYCOLORTABLESGIPROC)load(\"glCopyColorTableSGI\");\n\tglad_glGetColorTableSGI = (PFNGLGETCOLORTABLESGIPROC)load(\"glGetColorTableSGI\");\n\tglad_glGetColorTableParameterfvSGI = (PFNGLGETCOLORTABLEPARAMETERFVSGIPROC)load(\"glGetColorTableParameterfvSGI\");\n\tglad_glGetColorTableParameterivSGI = (PFNGLGETCOLORTABLEPARAMETERIVSGIPROC)load(\"glGetColorTableParameterivSGI\");\n}\nstatic void load_GL_EXT_gpu_shader4(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_gpu_shader4) return;\n\tglad_glGetUniformuivEXT = (PFNGLGETUNIFORMUIVEXTPROC)load(\"glGetUniformuivEXT\");\n\tglad_glBindFragDataLocationEXT = (PFNGLBINDFRAGDATALOCATIONEXTPROC)load(\"glBindFragDataLocationEXT\");\n\tglad_glGetFragDataLocationEXT = (PFNGLGETFRAGDATALOCATIONEXTPROC)load(\"glGetFragDataLocationEXT\");\n\tglad_glUniform1uiEXT = (PFNGLUNIFORM1UIEXTPROC)load(\"glUniform1uiEXT\");\n\tglad_glUniform2uiEXT = (PFNGLUNIFORM2UIEXTPROC)load(\"glUniform2uiEXT\");\n\tglad_glUniform3uiEXT = (PFNGLUNIFORM3UIEXTPROC)load(\"glUniform3uiEXT\");\n\tglad_glUniform4uiEXT = (PFNGLUNIFORM4UIEXTPROC)load(\"glUniform4uiEXT\");\n\tglad_glUniform1uivEXT = (PFNGLUNIFORM1UIVEXTPROC)load(\"glUniform1uivEXT\");\n\tglad_glUniform2uivEXT = (PFNGLUNIFORM2UIVEXTPROC)load(\"glUniform2uivEXT\");\n\tglad_glUniform3uivEXT = (PFNGLUNIFORM3UIVEXTPROC)load(\"glUniform3uivEXT\");\n\tglad_glUniform4uivEXT = (PFNGLUNIFORM4UIVEXTPROC)load(\"glUniform4uivEXT\");\n}\nstatic void load_GL_NV_geometry_program4(GLADloadproc load) {\n\tif(!GLAD_GL_NV_geometry_program4) return;\n\tglad_glProgramVertexLimitNV = (PFNGLPROGRAMVERTEXLIMITNVPROC)load(\"glProgramVertexLimitNV\");\n\tglad_glFramebufferTextureEXT = (PFNGLFRAMEBUFFERTEXTUREEXTPROC)load(\"glFramebufferTextureEXT\");\n\tglad_glFramebufferTextureLayerEXT = (PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC)load(\"glFramebufferTextureLayerEXT\");\n\tglad_glFramebufferTextureFaceEXT = (PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC)load(\"glFramebufferTextureFaceEXT\");\n}\nstatic void load_GL_AMD_debug_output(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_debug_output) return;\n\tglad_glDebugMessageEnableAMD = (PFNGLDEBUGMESSAGEENABLEAMDPROC)load(\"glDebugMessageEnableAMD\");\n\tglad_glDebugMessageInsertAMD = (PFNGLDEBUGMESSAGEINSERTAMDPROC)load(\"glDebugMessageInsertAMD\");\n\tglad_glDebugMessageCallbackAMD = (PFNGLDEBUGMESSAGECALLBACKAMDPROC)load(\"glDebugMessageCallbackAMD\");\n\tglad_glGetDebugMessageLogAMD = (PFNGLGETDEBUGMESSAGELOGAMDPROC)load(\"glGetDebugMessageLogAMD\");\n}\nstatic void load_GL_ARB_multitexture(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_multitexture) return;\n\tglad_glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)load(\"glActiveTextureARB\");\n\tglad_glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC)load(\"glClientActiveTextureARB\");\n\tglad_glMultiTexCoord1dARB = (PFNGLMULTITEXCOORD1DARBPROC)load(\"glMultiTexCoord1dARB\");\n\tglad_glMultiTexCoord1dvARB = (PFNGLMULTITEXCOORD1DVARBPROC)load(\"glMultiTexCoord1dvARB\");\n\tglad_glMultiTexCoord1fARB = (PFNGLMULTITEXCOORD1FARBPROC)load(\"glMultiTexCoord1fARB\");\n\tglad_glMultiTexCoord1fvARB = (PFNGLMULTITEXCOORD1FVARBPROC)load(\"glMultiTexCoord1fvARB\");\n\tglad_glMultiTexCoord1iARB = (PFNGLMULTITEXCOORD1IARBPROC)load(\"glMultiTexCoord1iARB\");\n\tglad_glMultiTexCoord1ivARB = (PFNGLMULTITEXCOORD1IVARBPROC)load(\"glMultiTexCoord1ivARB\");\n\tglad_glMultiTexCoord1sARB = (PFNGLMULTITEXCOORD1SARBPROC)load(\"glMultiTexCoord1sARB\");\n\tglad_glMultiTexCoord1svARB = (PFNGLMULTITEXCOORD1SVARBPROC)load(\"glMultiTexCoord1svARB\");\n\tglad_glMultiTexCoord2dARB = (PFNGLMULTITEXCOORD2DARBPROC)load(\"glMultiTexCoord2dARB\");\n\tglad_glMultiTexCoord2dvARB = (PFNGLMULTITEXCOORD2DVARBPROC)load(\"glMultiTexCoord2dvARB\");\n\tglad_glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC)load(\"glMultiTexCoord2fARB\");\n\tglad_glMultiTexCoord2fvARB = (PFNGLMULTITEXCOORD2FVARBPROC)load(\"glMultiTexCoord2fvARB\");\n\tglad_glMultiTexCoord2iARB = (PFNGLMULTITEXCOORD2IARBPROC)load(\"glMultiTexCoord2iARB\");\n\tglad_glMultiTexCoord2ivARB = (PFNGLMULTITEXCOORD2IVARBPROC)load(\"glMultiTexCoord2ivARB\");\n\tglad_glMultiTexCoord2sARB = (PFNGLMULTITEXCOORD2SARBPROC)load(\"glMultiTexCoord2sARB\");\n\tglad_glMultiTexCoord2svARB = (PFNGLMULTITEXCOORD2SVARBPROC)load(\"glMultiTexCoord2svARB\");\n\tglad_glMultiTexCoord3dARB = (PFNGLMULTITEXCOORD3DARBPROC)load(\"glMultiTexCoord3dARB\");\n\tglad_glMultiTexCoord3dvARB = (PFNGLMULTITEXCOORD3DVARBPROC)load(\"glMultiTexCoord3dvARB\");\n\tglad_glMultiTexCoord3fARB = (PFNGLMULTITEXCOORD3FARBPROC)load(\"glMultiTexCoord3fARB\");\n\tglad_glMultiTexCoord3fvARB = (PFNGLMULTITEXCOORD3FVARBPROC)load(\"glMultiTexCoord3fvARB\");\n\tglad_glMultiTexCoord3iARB = (PFNGLMULTITEXCOORD3IARBPROC)load(\"glMultiTexCoord3iARB\");\n\tglad_glMultiTexCoord3ivARB = (PFNGLMULTITEXCOORD3IVARBPROC)load(\"glMultiTexCoord3ivARB\");\n\tglad_glMultiTexCoord3sARB = (PFNGLMULTITEXCOORD3SARBPROC)load(\"glMultiTexCoord3sARB\");\n\tglad_glMultiTexCoord3svARB = (PFNGLMULTITEXCOORD3SVARBPROC)load(\"glMultiTexCoord3svARB\");\n\tglad_glMultiTexCoord4dARB = (PFNGLMULTITEXCOORD4DARBPROC)load(\"glMultiTexCoord4dARB\");\n\tglad_glMultiTexCoord4dvARB = (PFNGLMULTITEXCOORD4DVARBPROC)load(\"glMultiTexCoord4dvARB\");\n\tglad_glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC)load(\"glMultiTexCoord4fARB\");\n\tglad_glMultiTexCoord4fvARB = (PFNGLMULTITEXCOORD4FVARBPROC)load(\"glMultiTexCoord4fvARB\");\n\tglad_glMultiTexCoord4iARB = (PFNGLMULTITEXCOORD4IARBPROC)load(\"glMultiTexCoord4iARB\");\n\tglad_glMultiTexCoord4ivARB = (PFNGLMULTITEXCOORD4IVARBPROC)load(\"glMultiTexCoord4ivARB\");\n\tglad_glMultiTexCoord4sARB = (PFNGLMULTITEXCOORD4SARBPROC)load(\"glMultiTexCoord4sARB\");\n\tglad_glMultiTexCoord4svARB = (PFNGLMULTITEXCOORD4SVARBPROC)load(\"glMultiTexCoord4svARB\");\n}\nstatic void load_GL_SGIX_polynomial_ffd(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_polynomial_ffd) return;\n\tglad_glDeformationMap3dSGIX = (PFNGLDEFORMATIONMAP3DSGIXPROC)load(\"glDeformationMap3dSGIX\");\n\tglad_glDeformationMap3fSGIX = (PFNGLDEFORMATIONMAP3FSGIXPROC)load(\"glDeformationMap3fSGIX\");\n\tglad_glDeformSGIX = (PFNGLDEFORMSGIXPROC)load(\"glDeformSGIX\");\n\tglad_glLoadIdentityDeformationMapSGIX = (PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC)load(\"glLoadIdentityDeformationMapSGIX\");\n}\nstatic void load_GL_EXT_provoking_vertex(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_provoking_vertex) return;\n\tglad_glProvokingVertexEXT = (PFNGLPROVOKINGVERTEXEXTPROC)load(\"glProvokingVertexEXT\");\n}\nstatic void load_GL_ARB_point_parameters(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_point_parameters) return;\n\tglad_glPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC)load(\"glPointParameterfARB\");\n\tglad_glPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC)load(\"glPointParameterfvARB\");\n}\nstatic void load_GL_ARB_shader_image_load_store(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_shader_image_load_store) return;\n\tglad_glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)load(\"glBindImageTexture\");\n\tglad_glMemoryBarrier = (PFNGLMEMORYBARRIERPROC)load(\"glMemoryBarrier\");\n}\nstatic void load_GL_SGIX_framezoom(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_framezoom) return;\n\tglad_glFrameZoomSGIX = (PFNGLFRAMEZOOMSGIXPROC)load(\"glFrameZoomSGIX\");\n}\nstatic void load_GL_NV_bindless_multi_draw_indirect(GLADloadproc load) {\n\tif(!GLAD_GL_NV_bindless_multi_draw_indirect) return;\n\tglad_glMultiDrawArraysIndirectBindlessNV = (PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC)load(\"glMultiDrawArraysIndirectBindlessNV\");\n\tglad_glMultiDrawElementsIndirectBindlessNV = (PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC)load(\"glMultiDrawElementsIndirectBindlessNV\");\n}\nstatic void load_GL_EXT_transform_feedback(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_transform_feedback) return;\n\tglad_glBeginTransformFeedbackEXT = (PFNGLBEGINTRANSFORMFEEDBACKEXTPROC)load(\"glBeginTransformFeedbackEXT\");\n\tglad_glEndTransformFeedbackEXT = (PFNGLENDTRANSFORMFEEDBACKEXTPROC)load(\"glEndTransformFeedbackEXT\");\n\tglad_glBindBufferRangeEXT = (PFNGLBINDBUFFERRANGEEXTPROC)load(\"glBindBufferRangeEXT\");\n\tglad_glBindBufferOffsetEXT = (PFNGLBINDBUFFEROFFSETEXTPROC)load(\"glBindBufferOffsetEXT\");\n\tglad_glBindBufferBaseEXT = (PFNGLBINDBUFFERBASEEXTPROC)load(\"glBindBufferBaseEXT\");\n\tglad_glTransformFeedbackVaryingsEXT = (PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC)load(\"glTransformFeedbackVaryingsEXT\");\n\tglad_glGetTransformFeedbackVaryingEXT = (PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC)load(\"glGetTransformFeedbackVaryingEXT\");\n}\nstatic void load_GL_NV_gpu_program4(GLADloadproc load) {\n\tif(!GLAD_GL_NV_gpu_program4) return;\n\tglad_glProgramLocalParameterI4iNV = (PFNGLPROGRAMLOCALPARAMETERI4INVPROC)load(\"glProgramLocalParameterI4iNV\");\n\tglad_glProgramLocalParameterI4ivNV = (PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC)load(\"glProgramLocalParameterI4ivNV\");\n\tglad_glProgramLocalParametersI4ivNV = (PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC)load(\"glProgramLocalParametersI4ivNV\");\n\tglad_glProgramLocalParameterI4uiNV = (PFNGLPROGRAMLOCALPARAMETERI4UINVPROC)load(\"glProgramLocalParameterI4uiNV\");\n\tglad_glProgramLocalParameterI4uivNV = (PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC)load(\"glProgramLocalParameterI4uivNV\");\n\tglad_glProgramLocalParametersI4uivNV = (PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC)load(\"glProgramLocalParametersI4uivNV\");\n\tglad_glProgramEnvParameterI4iNV = (PFNGLPROGRAMENVPARAMETERI4INVPROC)load(\"glProgramEnvParameterI4iNV\");\n\tglad_glProgramEnvParameterI4ivNV = (PFNGLPROGRAMENVPARAMETERI4IVNVPROC)load(\"glProgramEnvParameterI4ivNV\");\n\tglad_glProgramEnvParametersI4ivNV = (PFNGLPROGRAMENVPARAMETERSI4IVNVPROC)load(\"glProgramEnvParametersI4ivNV\");\n\tglad_glProgramEnvParameterI4uiNV = (PFNGLPROGRAMENVPARAMETERI4UINVPROC)load(\"glProgramEnvParameterI4uiNV\");\n\tglad_glProgramEnvParameterI4uivNV = (PFNGLPROGRAMENVPARAMETERI4UIVNVPROC)load(\"glProgramEnvParameterI4uivNV\");\n\tglad_glProgramEnvParametersI4uivNV = (PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC)load(\"glProgramEnvParametersI4uivNV\");\n\tglad_glGetProgramLocalParameterIivNV = (PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC)load(\"glGetProgramLocalParameterIivNV\");\n\tglad_glGetProgramLocalParameterIuivNV = (PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC)load(\"glGetProgramLocalParameterIuivNV\");\n\tglad_glGetProgramEnvParameterIivNV = (PFNGLGETPROGRAMENVPARAMETERIIVNVPROC)load(\"glGetProgramEnvParameterIivNV\");\n\tglad_glGetProgramEnvParameterIuivNV = (PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC)load(\"glGetProgramEnvParameterIuivNV\");\n}\nstatic void load_GL_NV_gpu_program5(GLADloadproc load) {\n\tif(!GLAD_GL_NV_gpu_program5) return;\n\tglad_glProgramSubroutineParametersuivNV = (PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC)load(\"glProgramSubroutineParametersuivNV\");\n\tglad_glGetProgramSubroutineParameteruivNV = (PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC)load(\"glGetProgramSubroutineParameteruivNV\");\n}\nstatic void load_GL_ARB_geometry_shader4(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_geometry_shader4) return;\n\tglad_glProgramParameteriARB = (PFNGLPROGRAMPARAMETERIARBPROC)load(\"glProgramParameteriARB\");\n\tglad_glFramebufferTextureARB = (PFNGLFRAMEBUFFERTEXTUREARBPROC)load(\"glFramebufferTextureARB\");\n\tglad_glFramebufferTextureLayerARB = (PFNGLFRAMEBUFFERTEXTURELAYERARBPROC)load(\"glFramebufferTextureLayerARB\");\n\tglad_glFramebufferTextureFaceARB = (PFNGLFRAMEBUFFERTEXTUREFACEARBPROC)load(\"glFramebufferTextureFaceARB\");\n}\nstatic void load_GL_SGIX_sprite(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_sprite) return;\n\tglad_glSpriteParameterfSGIX = (PFNGLSPRITEPARAMETERFSGIXPROC)load(\"glSpriteParameterfSGIX\");\n\tglad_glSpriteParameterfvSGIX = (PFNGLSPRITEPARAMETERFVSGIXPROC)load(\"glSpriteParameterfvSGIX\");\n\tglad_glSpriteParameteriSGIX = (PFNGLSPRITEPARAMETERISGIXPROC)load(\"glSpriteParameteriSGIX\");\n\tglad_glSpriteParameterivSGIX = (PFNGLSPRITEPARAMETERIVSGIXPROC)load(\"glSpriteParameterivSGIX\");\n}\nstatic void load_GL_ARB_get_program_binary(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_get_program_binary) return;\n\tglad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC)load(\"glGetProgramBinary\");\n\tglad_glProgramBinary = (PFNGLPROGRAMBINARYPROC)load(\"glProgramBinary\");\n\tglad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)load(\"glProgramParameteri\");\n}\nstatic void load_GL_AMD_occlusion_query_event(GLADloadproc load) {\n\tif(!GLAD_GL_AMD_occlusion_query_event) return;\n\tglad_glQueryObjectParameteruiAMD = (PFNGLQUERYOBJECTPARAMETERUIAMDPROC)load(\"glQueryObjectParameteruiAMD\");\n}\nstatic void load_GL_SGIS_multisample(GLADloadproc load) {\n\tif(!GLAD_GL_SGIS_multisample) return;\n\tglad_glSampleMaskSGIS = (PFNGLSAMPLEMASKSGISPROC)load(\"glSampleMaskSGIS\");\n\tglad_glSamplePatternSGIS = (PFNGLSAMPLEPATTERNSGISPROC)load(\"glSamplePatternSGIS\");\n}\nstatic void load_GL_EXT_framebuffer_object(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_framebuffer_object) return;\n\tglad_glIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC)load(\"glIsRenderbufferEXT\");\n\tglad_glBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC)load(\"glBindRenderbufferEXT\");\n\tglad_glDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC)load(\"glDeleteRenderbuffersEXT\");\n\tglad_glGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC)load(\"glGenRenderbuffersEXT\");\n\tglad_glRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC)load(\"glRenderbufferStorageEXT\");\n\tglad_glGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC)load(\"glGetRenderbufferParameterivEXT\");\n\tglad_glIsFramebufferEXT = (PFNGLISFRAMEBUFFEREXTPROC)load(\"glIsFramebufferEXT\");\n\tglad_glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC)load(\"glBindFramebufferEXT\");\n\tglad_glDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC)load(\"glDeleteFramebuffersEXT\");\n\tglad_glGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC)load(\"glGenFramebuffersEXT\");\n\tglad_glCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)load(\"glCheckFramebufferStatusEXT\");\n\tglad_glFramebufferTexture1DEXT = (PFNGLFRAMEBUFFERTEXTURE1DEXTPROC)load(\"glFramebufferTexture1DEXT\");\n\tglad_glFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC)load(\"glFramebufferTexture2DEXT\");\n\tglad_glFramebufferTexture3DEXT = (PFNGLFRAMEBUFFERTEXTURE3DEXTPROC)load(\"glFramebufferTexture3DEXT\");\n\tglad_glFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC)load(\"glFramebufferRenderbufferEXT\");\n\tglad_glGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC)load(\"glGetFramebufferAttachmentParameterivEXT\");\n\tglad_glGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC)load(\"glGenerateMipmapEXT\");\n}\nstatic void load_GL_APPLE_vertex_array_range(GLADloadproc load) {\n\tif(!GLAD_GL_APPLE_vertex_array_range) return;\n\tglad_glVertexArrayRangeAPPLE = (PFNGLVERTEXARRAYRANGEAPPLEPROC)load(\"glVertexArrayRangeAPPLE\");\n\tglad_glFlushVertexArrayRangeAPPLE = (PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC)load(\"glFlushVertexArrayRangeAPPLE\");\n\tglad_glVertexArrayParameteriAPPLE = (PFNGLVERTEXARRAYPARAMETERIAPPLEPROC)load(\"glVertexArrayParameteriAPPLE\");\n}\nstatic void load_GL_NV_register_combiners(GLADloadproc load) {\n\tif(!GLAD_GL_NV_register_combiners) return;\n\tglad_glCombinerParameterfvNV = (PFNGLCOMBINERPARAMETERFVNVPROC)load(\"glCombinerParameterfvNV\");\n\tglad_glCombinerParameterfNV = (PFNGLCOMBINERPARAMETERFNVPROC)load(\"glCombinerParameterfNV\");\n\tglad_glCombinerParameterivNV = (PFNGLCOMBINERPARAMETERIVNVPROC)load(\"glCombinerParameterivNV\");\n\tglad_glCombinerParameteriNV = (PFNGLCOMBINERPARAMETERINVPROC)load(\"glCombinerParameteriNV\");\n\tglad_glCombinerInputNV = (PFNGLCOMBINERINPUTNVPROC)load(\"glCombinerInputNV\");\n\tglad_glCombinerOutputNV = (PFNGLCOMBINEROUTPUTNVPROC)load(\"glCombinerOutputNV\");\n\tglad_glFinalCombinerInputNV = (PFNGLFINALCOMBINERINPUTNVPROC)load(\"glFinalCombinerInputNV\");\n\tglad_glGetCombinerInputParameterfvNV = (PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC)load(\"glGetCombinerInputParameterfvNV\");\n\tglad_glGetCombinerInputParameterivNV = (PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC)load(\"glGetCombinerInputParameterivNV\");\n\tglad_glGetCombinerOutputParameterfvNV = (PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC)load(\"glGetCombinerOutputParameterfvNV\");\n\tglad_glGetCombinerOutputParameterivNV = (PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC)load(\"glGetCombinerOutputParameterivNV\");\n\tglad_glGetFinalCombinerInputParameterfvNV = (PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC)load(\"glGetFinalCombinerInputParameterfvNV\");\n\tglad_glGetFinalCombinerInputParameterivNV = (PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC)load(\"glGetFinalCombinerInputParameterivNV\");\n}\nstatic void load_GL_ARB_draw_buffers(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_draw_buffers) return;\n\tglad_glDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC)load(\"glDrawBuffersARB\");\n}\nstatic void load_GL_ARB_clear_texture(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_clear_texture) return;\n\tglad_glClearTexImage = (PFNGLCLEARTEXIMAGEPROC)load(\"glClearTexImage\");\n\tglad_glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC)load(\"glClearTexSubImage\");\n}\nstatic void load_GL_ARB_debug_output(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_debug_output) return;\n\tglad_glDebugMessageControlARB = (PFNGLDEBUGMESSAGECONTROLARBPROC)load(\"glDebugMessageControlARB\");\n\tglad_glDebugMessageInsertARB = (PFNGLDEBUGMESSAGEINSERTARBPROC)load(\"glDebugMessageInsertARB\");\n\tglad_glDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC)load(\"glDebugMessageCallbackARB\");\n\tglad_glGetDebugMessageLogARB = (PFNGLGETDEBUGMESSAGELOGARBPROC)load(\"glGetDebugMessageLogARB\");\n}\nstatic void load_GL_EXT_cull_vertex(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_cull_vertex) return;\n\tglad_glCullParameterdvEXT = (PFNGLCULLPARAMETERDVEXTPROC)load(\"glCullParameterdvEXT\");\n\tglad_glCullParameterfvEXT = (PFNGLCULLPARAMETERFVEXTPROC)load(\"glCullParameterfvEXT\");\n}\nstatic void load_GL_IBM_multimode_draw_arrays(GLADloadproc load) {\n\tif(!GLAD_GL_IBM_multimode_draw_arrays) return;\n\tglad_glMultiModeDrawArraysIBM = (PFNGLMULTIMODEDRAWARRAYSIBMPROC)load(\"glMultiModeDrawArraysIBM\");\n\tglad_glMultiModeDrawElementsIBM = (PFNGLMULTIMODEDRAWELEMENTSIBMPROC)load(\"glMultiModeDrawElementsIBM\");\n}\nstatic void load_GL_APPLE_vertex_array_object(GLADloadproc load) {\n\tif(!GLAD_GL_APPLE_vertex_array_object) return;\n\tglad_glBindVertexArrayAPPLE = (PFNGLBINDVERTEXARRAYAPPLEPROC)load(\"glBindVertexArrayAPPLE\");\n\tglad_glDeleteVertexArraysAPPLE = (PFNGLDELETEVERTEXARRAYSAPPLEPROC)load(\"glDeleteVertexArraysAPPLE\");\n\tglad_glGenVertexArraysAPPLE = (PFNGLGENVERTEXARRAYSAPPLEPROC)load(\"glGenVertexArraysAPPLE\");\n\tglad_glIsVertexArrayAPPLE = (PFNGLISVERTEXARRAYAPPLEPROC)load(\"glIsVertexArrayAPPLE\");\n}\nstatic void load_GL_SGIS_detail_texture(GLADloadproc load) {\n\tif(!GLAD_GL_SGIS_detail_texture) return;\n\tglad_glDetailTexFuncSGIS = (PFNGLDETAILTEXFUNCSGISPROC)load(\"glDetailTexFuncSGIS\");\n\tglad_glGetDetailTexFuncSGIS = (PFNGLGETDETAILTEXFUNCSGISPROC)load(\"glGetDetailTexFuncSGIS\");\n}\nstatic void load_GL_ARB_draw_instanced(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_draw_instanced) return;\n\tglad_glDrawArraysInstancedARB = (PFNGLDRAWARRAYSINSTANCEDARBPROC)load(\"glDrawArraysInstancedARB\");\n\tglad_glDrawElementsInstancedARB = (PFNGLDRAWELEMENTSINSTANCEDARBPROC)load(\"glDrawElementsInstancedARB\");\n}\nstatic void load_GL_ARB_shading_language_include(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_shading_language_include) return;\n\tglad_glNamedStringARB = (PFNGLNAMEDSTRINGARBPROC)load(\"glNamedStringARB\");\n\tglad_glDeleteNamedStringARB = (PFNGLDELETENAMEDSTRINGARBPROC)load(\"glDeleteNamedStringARB\");\n\tglad_glCompileShaderIncludeARB = (PFNGLCOMPILESHADERINCLUDEARBPROC)load(\"glCompileShaderIncludeARB\");\n\tglad_glIsNamedStringARB = (PFNGLISNAMEDSTRINGARBPROC)load(\"glIsNamedStringARB\");\n\tglad_glGetNamedStringARB = (PFNGLGETNAMEDSTRINGARBPROC)load(\"glGetNamedStringARB\");\n\tglad_glGetNamedStringivARB = (PFNGLGETNAMEDSTRINGIVARBPROC)load(\"glGetNamedStringivARB\");\n}\nstatic void load_GL_INGR_blend_func_separate(GLADloadproc load) {\n\tif(!GLAD_GL_INGR_blend_func_separate) return;\n\tglad_glBlendFuncSeparateINGR = (PFNGLBLENDFUNCSEPARATEINGRPROC)load(\"glBlendFuncSeparateINGR\");\n}\nstatic void load_GL_NV_path_rendering(GLADloadproc load) {\n\tif(!GLAD_GL_NV_path_rendering) return;\n\tglad_glGenPathsNV = (PFNGLGENPATHSNVPROC)load(\"glGenPathsNV\");\n\tglad_glDeletePathsNV = (PFNGLDELETEPATHSNVPROC)load(\"glDeletePathsNV\");\n\tglad_glIsPathNV = (PFNGLISPATHNVPROC)load(\"glIsPathNV\");\n\tglad_glPathCommandsNV = (PFNGLPATHCOMMANDSNVPROC)load(\"glPathCommandsNV\");\n\tglad_glPathCoordsNV = (PFNGLPATHCOORDSNVPROC)load(\"glPathCoordsNV\");\n\tglad_glPathSubCommandsNV = (PFNGLPATHSUBCOMMANDSNVPROC)load(\"glPathSubCommandsNV\");\n\tglad_glPathSubCoordsNV = (PFNGLPATHSUBCOORDSNVPROC)load(\"glPathSubCoordsNV\");\n\tglad_glPathStringNV = (PFNGLPATHSTRINGNVPROC)load(\"glPathStringNV\");\n\tglad_glPathGlyphsNV = (PFNGLPATHGLYPHSNVPROC)load(\"glPathGlyphsNV\");\n\tglad_glPathGlyphRangeNV = (PFNGLPATHGLYPHRANGENVPROC)load(\"glPathGlyphRangeNV\");\n\tglad_glWeightPathsNV = (PFNGLWEIGHTPATHSNVPROC)load(\"glWeightPathsNV\");\n\tglad_glCopyPathNV = (PFNGLCOPYPATHNVPROC)load(\"glCopyPathNV\");\n\tglad_glInterpolatePathsNV = (PFNGLINTERPOLATEPATHSNVPROC)load(\"glInterpolatePathsNV\");\n\tglad_glTransformPathNV = (PFNGLTRANSFORMPATHNVPROC)load(\"glTransformPathNV\");\n\tglad_glPathParameterivNV = (PFNGLPATHPARAMETERIVNVPROC)load(\"glPathParameterivNV\");\n\tglad_glPathParameteriNV = (PFNGLPATHPARAMETERINVPROC)load(\"glPathParameteriNV\");\n\tglad_glPathParameterfvNV = (PFNGLPATHPARAMETERFVNVPROC)load(\"glPathParameterfvNV\");\n\tglad_glPathParameterfNV = (PFNGLPATHPARAMETERFNVPROC)load(\"glPathParameterfNV\");\n\tglad_glPathDashArrayNV = (PFNGLPATHDASHARRAYNVPROC)load(\"glPathDashArrayNV\");\n\tglad_glPathStencilFuncNV = (PFNGLPATHSTENCILFUNCNVPROC)load(\"glPathStencilFuncNV\");\n\tglad_glPathStencilDepthOffsetNV = (PFNGLPATHSTENCILDEPTHOFFSETNVPROC)load(\"glPathStencilDepthOffsetNV\");\n\tglad_glStencilFillPathNV = (PFNGLSTENCILFILLPATHNVPROC)load(\"glStencilFillPathNV\");\n\tglad_glStencilStrokePathNV = (PFNGLSTENCILSTROKEPATHNVPROC)load(\"glStencilStrokePathNV\");\n\tglad_glStencilFillPathInstancedNV = (PFNGLSTENCILFILLPATHINSTANCEDNVPROC)load(\"glStencilFillPathInstancedNV\");\n\tglad_glStencilStrokePathInstancedNV = (PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC)load(\"glStencilStrokePathInstancedNV\");\n\tglad_glPathCoverDepthFuncNV = (PFNGLPATHCOVERDEPTHFUNCNVPROC)load(\"glPathCoverDepthFuncNV\");\n\tglad_glPathColorGenNV = (PFNGLPATHCOLORGENNVPROC)load(\"glPathColorGenNV\");\n\tglad_glPathTexGenNV = (PFNGLPATHTEXGENNVPROC)load(\"glPathTexGenNV\");\n\tglad_glPathFogGenNV = (PFNGLPATHFOGGENNVPROC)load(\"glPathFogGenNV\");\n\tglad_glCoverFillPathNV = (PFNGLCOVERFILLPATHNVPROC)load(\"glCoverFillPathNV\");\n\tglad_glCoverStrokePathNV = (PFNGLCOVERSTROKEPATHNVPROC)load(\"glCoverStrokePathNV\");\n\tglad_glCoverFillPathInstancedNV = (PFNGLCOVERFILLPATHINSTANCEDNVPROC)load(\"glCoverFillPathInstancedNV\");\n\tglad_glCoverStrokePathInstancedNV = (PFNGLCOVERSTROKEPATHINSTANCEDNVPROC)load(\"glCoverStrokePathInstancedNV\");\n\tglad_glGetPathParameterivNV = (PFNGLGETPATHPARAMETERIVNVPROC)load(\"glGetPathParameterivNV\");\n\tglad_glGetPathParameterfvNV = (PFNGLGETPATHPARAMETERFVNVPROC)load(\"glGetPathParameterfvNV\");\n\tglad_glGetPathCommandsNV = (PFNGLGETPATHCOMMANDSNVPROC)load(\"glGetPathCommandsNV\");\n\tglad_glGetPathCoordsNV = (PFNGLGETPATHCOORDSNVPROC)load(\"glGetPathCoordsNV\");\n\tglad_glGetPathDashArrayNV = (PFNGLGETPATHDASHARRAYNVPROC)load(\"glGetPathDashArrayNV\");\n\tglad_glGetPathMetricsNV = (PFNGLGETPATHMETRICSNVPROC)load(\"glGetPathMetricsNV\");\n\tglad_glGetPathMetricRangeNV = (PFNGLGETPATHMETRICRANGENVPROC)load(\"glGetPathMetricRangeNV\");\n\tglad_glGetPathSpacingNV = (PFNGLGETPATHSPACINGNVPROC)load(\"glGetPathSpacingNV\");\n\tglad_glGetPathColorGenivNV = (PFNGLGETPATHCOLORGENIVNVPROC)load(\"glGetPathColorGenivNV\");\n\tglad_glGetPathColorGenfvNV = (PFNGLGETPATHCOLORGENFVNVPROC)load(\"glGetPathColorGenfvNV\");\n\tglad_glGetPathTexGenivNV = (PFNGLGETPATHTEXGENIVNVPROC)load(\"glGetPathTexGenivNV\");\n\tglad_glGetPathTexGenfvNV = (PFNGLGETPATHTEXGENFVNVPROC)load(\"glGetPathTexGenfvNV\");\n\tglad_glIsPointInFillPathNV = (PFNGLISPOINTINFILLPATHNVPROC)load(\"glIsPointInFillPathNV\");\n\tglad_glIsPointInStrokePathNV = (PFNGLISPOINTINSTROKEPATHNVPROC)load(\"glIsPointInStrokePathNV\");\n\tglad_glGetPathLengthNV = (PFNGLGETPATHLENGTHNVPROC)load(\"glGetPathLengthNV\");\n\tglad_glPointAlongPathNV = (PFNGLPOINTALONGPATHNVPROC)load(\"glPointAlongPathNV\");\n}\nstatic void load_GL_ATI_vertex_streams(GLADloadproc load) {\n\tif(!GLAD_GL_ATI_vertex_streams) return;\n\tglad_glVertexStream1sATI = (PFNGLVERTEXSTREAM1SATIPROC)load(\"glVertexStream1sATI\");\n\tglad_glVertexStream1svATI = (PFNGLVERTEXSTREAM1SVATIPROC)load(\"glVertexStream1svATI\");\n\tglad_glVertexStream1iATI = (PFNGLVERTEXSTREAM1IATIPROC)load(\"glVertexStream1iATI\");\n\tglad_glVertexStream1ivATI = (PFNGLVERTEXSTREAM1IVATIPROC)load(\"glVertexStream1ivATI\");\n\tglad_glVertexStream1fATI = (PFNGLVERTEXSTREAM1FATIPROC)load(\"glVertexStream1fATI\");\n\tglad_glVertexStream1fvATI = (PFNGLVERTEXSTREAM1FVATIPROC)load(\"glVertexStream1fvATI\");\n\tglad_glVertexStream1dATI = (PFNGLVERTEXSTREAM1DATIPROC)load(\"glVertexStream1dATI\");\n\tglad_glVertexStream1dvATI = (PFNGLVERTEXSTREAM1DVATIPROC)load(\"glVertexStream1dvATI\");\n\tglad_glVertexStream2sATI = (PFNGLVERTEXSTREAM2SATIPROC)load(\"glVertexStream2sATI\");\n\tglad_glVertexStream2svATI = (PFNGLVERTEXSTREAM2SVATIPROC)load(\"glVertexStream2svATI\");\n\tglad_glVertexStream2iATI = (PFNGLVERTEXSTREAM2IATIPROC)load(\"glVertexStream2iATI\");\n\tglad_glVertexStream2ivATI = (PFNGLVERTEXSTREAM2IVATIPROC)load(\"glVertexStream2ivATI\");\n\tglad_glVertexStream2fATI = (PFNGLVERTEXSTREAM2FATIPROC)load(\"glVertexStream2fATI\");\n\tglad_glVertexStream2fvATI = (PFNGLVERTEXSTREAM2FVATIPROC)load(\"glVertexStream2fvATI\");\n\tglad_glVertexStream2dATI = (PFNGLVERTEXSTREAM2DATIPROC)load(\"glVertexStream2dATI\");\n\tglad_glVertexStream2dvATI = (PFNGLVERTEXSTREAM2DVATIPROC)load(\"glVertexStream2dvATI\");\n\tglad_glVertexStream3sATI = (PFNGLVERTEXSTREAM3SATIPROC)load(\"glVertexStream3sATI\");\n\tglad_glVertexStream3svATI = (PFNGLVERTEXSTREAM3SVATIPROC)load(\"glVertexStream3svATI\");\n\tglad_glVertexStream3iATI = (PFNGLVERTEXSTREAM3IATIPROC)load(\"glVertexStream3iATI\");\n\tglad_glVertexStream3ivATI = (PFNGLVERTEXSTREAM3IVATIPROC)load(\"glVertexStream3ivATI\");\n\tglad_glVertexStream3fATI = (PFNGLVERTEXSTREAM3FATIPROC)load(\"glVertexStream3fATI\");\n\tglad_glVertexStream3fvATI = (PFNGLVERTEXSTREAM3FVATIPROC)load(\"glVertexStream3fvATI\");\n\tglad_glVertexStream3dATI = (PFNGLVERTEXSTREAM3DATIPROC)load(\"glVertexStream3dATI\");\n\tglad_glVertexStream3dvATI = (PFNGLVERTEXSTREAM3DVATIPROC)load(\"glVertexStream3dvATI\");\n\tglad_glVertexStream4sATI = (PFNGLVERTEXSTREAM4SATIPROC)load(\"glVertexStream4sATI\");\n\tglad_glVertexStream4svATI = (PFNGLVERTEXSTREAM4SVATIPROC)load(\"glVertexStream4svATI\");\n\tglad_glVertexStream4iATI = (PFNGLVERTEXSTREAM4IATIPROC)load(\"glVertexStream4iATI\");\n\tglad_glVertexStream4ivATI = (PFNGLVERTEXSTREAM4IVATIPROC)load(\"glVertexStream4ivATI\");\n\tglad_glVertexStream4fATI = (PFNGLVERTEXSTREAM4FATIPROC)load(\"glVertexStream4fATI\");\n\tglad_glVertexStream4fvATI = (PFNGLVERTEXSTREAM4FVATIPROC)load(\"glVertexStream4fvATI\");\n\tglad_glVertexStream4dATI = (PFNGLVERTEXSTREAM4DATIPROC)load(\"glVertexStream4dATI\");\n\tglad_glVertexStream4dvATI = (PFNGLVERTEXSTREAM4DVATIPROC)load(\"glVertexStream4dvATI\");\n\tglad_glNormalStream3bATI = (PFNGLNORMALSTREAM3BATIPROC)load(\"glNormalStream3bATI\");\n\tglad_glNormalStream3bvATI = (PFNGLNORMALSTREAM3BVATIPROC)load(\"glNormalStream3bvATI\");\n\tglad_glNormalStream3sATI = (PFNGLNORMALSTREAM3SATIPROC)load(\"glNormalStream3sATI\");\n\tglad_glNormalStream3svATI = (PFNGLNORMALSTREAM3SVATIPROC)load(\"glNormalStream3svATI\");\n\tglad_glNormalStream3iATI = (PFNGLNORMALSTREAM3IATIPROC)load(\"glNormalStream3iATI\");\n\tglad_glNormalStream3ivATI = (PFNGLNORMALSTREAM3IVATIPROC)load(\"glNormalStream3ivATI\");\n\tglad_glNormalStream3fATI = (PFNGLNORMALSTREAM3FATIPROC)load(\"glNormalStream3fATI\");\n\tglad_glNormalStream3fvATI = (PFNGLNORMALSTREAM3FVATIPROC)load(\"glNormalStream3fvATI\");\n\tglad_glNormalStream3dATI = (PFNGLNORMALSTREAM3DATIPROC)load(\"glNormalStream3dATI\");\n\tglad_glNormalStream3dvATI = (PFNGLNORMALSTREAM3DVATIPROC)load(\"glNormalStream3dvATI\");\n\tglad_glClientActiveVertexStreamATI = (PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC)load(\"glClientActiveVertexStreamATI\");\n\tglad_glVertexBlendEnviATI = (PFNGLVERTEXBLENDENVIATIPROC)load(\"glVertexBlendEnviATI\");\n\tglad_glVertexBlendEnvfATI = (PFNGLVERTEXBLENDENVFATIPROC)load(\"glVertexBlendEnvfATI\");\n}\nstatic void load_GL_NV_vdpau_interop(GLADloadproc load) {\n\tif(!GLAD_GL_NV_vdpau_interop) return;\n\tglad_glVDPAUInitNV = (PFNGLVDPAUINITNVPROC)load(\"glVDPAUInitNV\");\n\tglad_glVDPAUFiniNV = (PFNGLVDPAUFININVPROC)load(\"glVDPAUFiniNV\");\n\tglad_glVDPAURegisterVideoSurfaceNV = (PFNGLVDPAUREGISTERVIDEOSURFACENVPROC)load(\"glVDPAURegisterVideoSurfaceNV\");\n\tglad_glVDPAURegisterOutputSurfaceNV = (PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC)load(\"glVDPAURegisterOutputSurfaceNV\");\n\tglad_glVDPAUIsSurfaceNV = (PFNGLVDPAUISSURFACENVPROC)load(\"glVDPAUIsSurfaceNV\");\n\tglad_glVDPAUUnregisterSurfaceNV = (PFNGLVDPAUUNREGISTERSURFACENVPROC)load(\"glVDPAUUnregisterSurfaceNV\");\n\tglad_glVDPAUGetSurfaceivNV = (PFNGLVDPAUGETSURFACEIVNVPROC)load(\"glVDPAUGetSurfaceivNV\");\n\tglad_glVDPAUSurfaceAccessNV = (PFNGLVDPAUSURFACEACCESSNVPROC)load(\"glVDPAUSurfaceAccessNV\");\n\tglad_glVDPAUMapSurfacesNV = (PFNGLVDPAUMAPSURFACESNVPROC)load(\"glVDPAUMapSurfacesNV\");\n\tglad_glVDPAUUnmapSurfacesNV = (PFNGLVDPAUUNMAPSURFACESNVPROC)load(\"glVDPAUUnmapSurfacesNV\");\n}\nstatic void load_GL_ARB_internalformat_query2(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_internalformat_query2) return;\n\tglad_glGetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC)load(\"glGetInternalformati64v\");\n}\nstatic void load_GL_SUN_vertex(GLADloadproc load) {\n\tif(!GLAD_GL_SUN_vertex) return;\n\tglad_glColor4ubVertex2fSUN = (PFNGLCOLOR4UBVERTEX2FSUNPROC)load(\"glColor4ubVertex2fSUN\");\n\tglad_glColor4ubVertex2fvSUN = (PFNGLCOLOR4UBVERTEX2FVSUNPROC)load(\"glColor4ubVertex2fvSUN\");\n\tglad_glColor4ubVertex3fSUN = (PFNGLCOLOR4UBVERTEX3FSUNPROC)load(\"glColor4ubVertex3fSUN\");\n\tglad_glColor4ubVertex3fvSUN = (PFNGLCOLOR4UBVERTEX3FVSUNPROC)load(\"glColor4ubVertex3fvSUN\");\n\tglad_glColor3fVertex3fSUN = (PFNGLCOLOR3FVERTEX3FSUNPROC)load(\"glColor3fVertex3fSUN\");\n\tglad_glColor3fVertex3fvSUN = (PFNGLCOLOR3FVERTEX3FVSUNPROC)load(\"glColor3fVertex3fvSUN\");\n\tglad_glNormal3fVertex3fSUN = (PFNGLNORMAL3FVERTEX3FSUNPROC)load(\"glNormal3fVertex3fSUN\");\n\tglad_glNormal3fVertex3fvSUN = (PFNGLNORMAL3FVERTEX3FVSUNPROC)load(\"glNormal3fVertex3fvSUN\");\n\tglad_glColor4fNormal3fVertex3fSUN = (PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC)load(\"glColor4fNormal3fVertex3fSUN\");\n\tglad_glColor4fNormal3fVertex3fvSUN = (PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC)load(\"glColor4fNormal3fVertex3fvSUN\");\n\tglad_glTexCoord2fVertex3fSUN = (PFNGLTEXCOORD2FVERTEX3FSUNPROC)load(\"glTexCoord2fVertex3fSUN\");\n\tglad_glTexCoord2fVertex3fvSUN = (PFNGLTEXCOORD2FVERTEX3FVSUNPROC)load(\"glTexCoord2fVertex3fvSUN\");\n\tglad_glTexCoord4fVertex4fSUN = (PFNGLTEXCOORD4FVERTEX4FSUNPROC)load(\"glTexCoord4fVertex4fSUN\");\n\tglad_glTexCoord4fVertex4fvSUN = (PFNGLTEXCOORD4FVERTEX4FVSUNPROC)load(\"glTexCoord4fVertex4fvSUN\");\n\tglad_glTexCoord2fColor4ubVertex3fSUN = (PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC)load(\"glTexCoord2fColor4ubVertex3fSUN\");\n\tglad_glTexCoord2fColor4ubVertex3fvSUN = (PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC)load(\"glTexCoord2fColor4ubVertex3fvSUN\");\n\tglad_glTexCoord2fColor3fVertex3fSUN = (PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC)load(\"glTexCoord2fColor3fVertex3fSUN\");\n\tglad_glTexCoord2fColor3fVertex3fvSUN = (PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC)load(\"glTexCoord2fColor3fVertex3fvSUN\");\n\tglad_glTexCoord2fNormal3fVertex3fSUN = (PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC)load(\"glTexCoord2fNormal3fVertex3fSUN\");\n\tglad_glTexCoord2fNormal3fVertex3fvSUN = (PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC)load(\"glTexCoord2fNormal3fVertex3fvSUN\");\n\tglad_glTexCoord2fColor4fNormal3fVertex3fSUN = (PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC)load(\"glTexCoord2fColor4fNormal3fVertex3fSUN\");\n\tglad_glTexCoord2fColor4fNormal3fVertex3fvSUN = (PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC)load(\"glTexCoord2fColor4fNormal3fVertex3fvSUN\");\n\tglad_glTexCoord4fColor4fNormal3fVertex4fSUN = (PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC)load(\"glTexCoord4fColor4fNormal3fVertex4fSUN\");\n\tglad_glTexCoord4fColor4fNormal3fVertex4fvSUN = (PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC)load(\"glTexCoord4fColor4fNormal3fVertex4fvSUN\");\n\tglad_glReplacementCodeuiVertex3fSUN = (PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC)load(\"glReplacementCodeuiVertex3fSUN\");\n\tglad_glReplacementCodeuiVertex3fvSUN = (PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC)load(\"glReplacementCodeuiVertex3fvSUN\");\n\tglad_glReplacementCodeuiColor4ubVertex3fSUN = (PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC)load(\"glReplacementCodeuiColor4ubVertex3fSUN\");\n\tglad_glReplacementCodeuiColor4ubVertex3fvSUN = (PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC)load(\"glReplacementCodeuiColor4ubVertex3fvSUN\");\n\tglad_glReplacementCodeuiColor3fVertex3fSUN = (PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC)load(\"glReplacementCodeuiColor3fVertex3fSUN\");\n\tglad_glReplacementCodeuiColor3fVertex3fvSUN = (PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC)load(\"glReplacementCodeuiColor3fVertex3fvSUN\");\n\tglad_glReplacementCodeuiNormal3fVertex3fSUN = (PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC)load(\"glReplacementCodeuiNormal3fVertex3fSUN\");\n\tglad_glReplacementCodeuiNormal3fVertex3fvSUN = (PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC)load(\"glReplacementCodeuiNormal3fVertex3fvSUN\");\n\tglad_glReplacementCodeuiColor4fNormal3fVertex3fSUN = (PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC)load(\"glReplacementCodeuiColor4fNormal3fVertex3fSUN\");\n\tglad_glReplacementCodeuiColor4fNormal3fVertex3fvSUN = (PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC)load(\"glReplacementCodeuiColor4fNormal3fVertex3fvSUN\");\n\tglad_glReplacementCodeuiTexCoord2fVertex3fSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC)load(\"glReplacementCodeuiTexCoord2fVertex3fSUN\");\n\tglad_glReplacementCodeuiTexCoord2fVertex3fvSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC)load(\"glReplacementCodeuiTexCoord2fVertex3fvSUN\");\n\tglad_glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC)load(\"glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN\");\n\tglad_glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC)load(\"glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN\");\n\tglad_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC)load(\"glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN\");\n\tglad_glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN = (PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC)load(\"glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN\");\n}\nstatic void load_GL_SGIX_igloo_interface(GLADloadproc load) {\n\tif(!GLAD_GL_SGIX_igloo_interface) return;\n\tglad_glIglooInterfaceSGIX = (PFNGLIGLOOINTERFACESGIXPROC)load(\"glIglooInterfaceSGIX\");\n}\nstatic void load_GL_ARB_draw_indirect(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_draw_indirect) return;\n\tglad_glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC)load(\"glDrawArraysIndirect\");\n\tglad_glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC)load(\"glDrawElementsIndirect\");\n}\nstatic void load_GL_NV_vertex_program4(GLADloadproc load) {\n\tif(!GLAD_GL_NV_vertex_program4) return;\n\tglad_glVertexAttribI1iEXT = (PFNGLVERTEXATTRIBI1IEXTPROC)load(\"glVertexAttribI1iEXT\");\n\tglad_glVertexAttribI2iEXT = (PFNGLVERTEXATTRIBI2IEXTPROC)load(\"glVertexAttribI2iEXT\");\n\tglad_glVertexAttribI3iEXT = (PFNGLVERTEXATTRIBI3IEXTPROC)load(\"glVertexAttribI3iEXT\");\n\tglad_glVertexAttribI4iEXT = (PFNGLVERTEXATTRIBI4IEXTPROC)load(\"glVertexAttribI4iEXT\");\n\tglad_glVertexAttribI1uiEXT = (PFNGLVERTEXATTRIBI1UIEXTPROC)load(\"glVertexAttribI1uiEXT\");\n\tglad_glVertexAttribI2uiEXT = (PFNGLVERTEXATTRIBI2UIEXTPROC)load(\"glVertexAttribI2uiEXT\");\n\tglad_glVertexAttribI3uiEXT = (PFNGLVERTEXATTRIBI3UIEXTPROC)load(\"glVertexAttribI3uiEXT\");\n\tglad_glVertexAttribI4uiEXT = (PFNGLVERTEXATTRIBI4UIEXTPROC)load(\"glVertexAttribI4uiEXT\");\n\tglad_glVertexAttribI1ivEXT = (PFNGLVERTEXATTRIBI1IVEXTPROC)load(\"glVertexAttribI1ivEXT\");\n\tglad_glVertexAttribI2ivEXT = (PFNGLVERTEXATTRIBI2IVEXTPROC)load(\"glVertexAttribI2ivEXT\");\n\tglad_glVertexAttribI3ivEXT = (PFNGLVERTEXATTRIBI3IVEXTPROC)load(\"glVertexAttribI3ivEXT\");\n\tglad_glVertexAttribI4ivEXT = (PFNGLVERTEXATTRIBI4IVEXTPROC)load(\"glVertexAttribI4ivEXT\");\n\tglad_glVertexAttribI1uivEXT = (PFNGLVERTEXATTRIBI1UIVEXTPROC)load(\"glVertexAttribI1uivEXT\");\n\tglad_glVertexAttribI2uivEXT = (PFNGLVERTEXATTRIBI2UIVEXTPROC)load(\"glVertexAttribI2uivEXT\");\n\tglad_glVertexAttribI3uivEXT = (PFNGLVERTEXATTRIBI3UIVEXTPROC)load(\"glVertexAttribI3uivEXT\");\n\tglad_glVertexAttribI4uivEXT = (PFNGLVERTEXATTRIBI4UIVEXTPROC)load(\"glVertexAttribI4uivEXT\");\n\tglad_glVertexAttribI4bvEXT = (PFNGLVERTEXATTRIBI4BVEXTPROC)load(\"glVertexAttribI4bvEXT\");\n\tglad_glVertexAttribI4svEXT = (PFNGLVERTEXATTRIBI4SVEXTPROC)load(\"glVertexAttribI4svEXT\");\n\tglad_glVertexAttribI4ubvEXT = (PFNGLVERTEXATTRIBI4UBVEXTPROC)load(\"glVertexAttribI4ubvEXT\");\n\tglad_glVertexAttribI4usvEXT = (PFNGLVERTEXATTRIBI4USVEXTPROC)load(\"glVertexAttribI4usvEXT\");\n\tglad_glVertexAttribIPointerEXT = (PFNGLVERTEXATTRIBIPOINTEREXTPROC)load(\"glVertexAttribIPointerEXT\");\n\tglad_glGetVertexAttribIivEXT = (PFNGLGETVERTEXATTRIBIIVEXTPROC)load(\"glGetVertexAttribIivEXT\");\n\tglad_glGetVertexAttribIuivEXT = (PFNGLGETVERTEXATTRIBIUIVEXTPROC)load(\"glGetVertexAttribIuivEXT\");\n}\nstatic void load_GL_SGIS_fog_function(GLADloadproc load) {\n\tif(!GLAD_GL_SGIS_fog_function) return;\n\tglad_glFogFuncSGIS = (PFNGLFOGFUNCSGISPROC)load(\"glFogFuncSGIS\");\n\tglad_glGetFogFuncSGIS = (PFNGLGETFOGFUNCSGISPROC)load(\"glGetFogFuncSGIS\");\n}\nstatic void load_GL_EXT_x11_sync_object(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_x11_sync_object) return;\n\tglad_glImportSyncEXT = (PFNGLIMPORTSYNCEXTPROC)load(\"glImportSyncEXT\");\n}\nstatic void load_GL_ARB_sync(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_sync) return;\n\tglad_glFenceSync = (PFNGLFENCESYNCPROC)load(\"glFenceSync\");\n\tglad_glIsSync = (PFNGLISSYNCPROC)load(\"glIsSync\");\n\tglad_glDeleteSync = (PFNGLDELETESYNCPROC)load(\"glDeleteSync\");\n\tglad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)load(\"glClientWaitSync\");\n\tglad_glWaitSync = (PFNGLWAITSYNCPROC)load(\"glWaitSync\");\n\tglad_glGetInteger64v = (PFNGLGETINTEGER64VPROC)load(\"glGetInteger64v\");\n\tglad_glGetSynciv = (PFNGLGETSYNCIVPROC)load(\"glGetSynciv\");\n}\nstatic void load_GL_ARB_compute_variable_group_size(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_compute_variable_group_size) return;\n\tglad_glDispatchComputeGroupSizeARB = (PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC)load(\"glDispatchComputeGroupSizeARB\");\n}\nstatic void load_GL_OES_fixed_point(GLADloadproc load) {\n\tif(!GLAD_GL_OES_fixed_point) return;\n\tglad_glAlphaFuncxOES = (PFNGLALPHAFUNCXOESPROC)load(\"glAlphaFuncxOES\");\n\tglad_glClearColorxOES = (PFNGLCLEARCOLORXOESPROC)load(\"glClearColorxOES\");\n\tglad_glClearDepthxOES = (PFNGLCLEARDEPTHXOESPROC)load(\"glClearDepthxOES\");\n\tglad_glClipPlanexOES = (PFNGLCLIPPLANEXOESPROC)load(\"glClipPlanexOES\");\n\tglad_glColor4xOES = (PFNGLCOLOR4XOESPROC)load(\"glColor4xOES\");\n\tglad_glDepthRangexOES = (PFNGLDEPTHRANGEXOESPROC)load(\"glDepthRangexOES\");\n\tglad_glFogxOES = (PFNGLFOGXOESPROC)load(\"glFogxOES\");\n\tglad_glFogxvOES = (PFNGLFOGXVOESPROC)load(\"glFogxvOES\");\n\tglad_glFrustumxOES = (PFNGLFRUSTUMXOESPROC)load(\"glFrustumxOES\");\n\tglad_glGetClipPlanexOES = (PFNGLGETCLIPPLANEXOESPROC)load(\"glGetClipPlanexOES\");\n\tglad_glGetFixedvOES = (PFNGLGETFIXEDVOESPROC)load(\"glGetFixedvOES\");\n\tglad_glGetTexEnvxvOES = (PFNGLGETTEXENVXVOESPROC)load(\"glGetTexEnvxvOES\");\n\tglad_glGetTexParameterxvOES = (PFNGLGETTEXPARAMETERXVOESPROC)load(\"glGetTexParameterxvOES\");\n\tglad_glLightModelxOES = (PFNGLLIGHTMODELXOESPROC)load(\"glLightModelxOES\");\n\tglad_glLightModelxvOES = (PFNGLLIGHTMODELXVOESPROC)load(\"glLightModelxvOES\");\n\tglad_glLightxOES = (PFNGLLIGHTXOESPROC)load(\"glLightxOES\");\n\tglad_glLightxvOES = (PFNGLLIGHTXVOESPROC)load(\"glLightxvOES\");\n\tglad_glLineWidthxOES = (PFNGLLINEWIDTHXOESPROC)load(\"glLineWidthxOES\");\n\tglad_glLoadMatrixxOES = (PFNGLLOADMATRIXXOESPROC)load(\"glLoadMatrixxOES\");\n\tglad_glMaterialxOES = (PFNGLMATERIALXOESPROC)load(\"glMaterialxOES\");\n\tglad_glMaterialxvOES = (PFNGLMATERIALXVOESPROC)load(\"glMaterialxvOES\");\n\tglad_glMultMatrixxOES = (PFNGLMULTMATRIXXOESPROC)load(\"glMultMatrixxOES\");\n\tglad_glMultiTexCoord4xOES = (PFNGLMULTITEXCOORD4XOESPROC)load(\"glMultiTexCoord4xOES\");\n\tglad_glNormal3xOES = (PFNGLNORMAL3XOESPROC)load(\"glNormal3xOES\");\n\tglad_glOrthoxOES = (PFNGLORTHOXOESPROC)load(\"glOrthoxOES\");\n\tglad_glPointParameterxvOES = (PFNGLPOINTPARAMETERXVOESPROC)load(\"glPointParameterxvOES\");\n\tglad_glPointSizexOES = (PFNGLPOINTSIZEXOESPROC)load(\"glPointSizexOES\");\n\tglad_glPolygonOffsetxOES = (PFNGLPOLYGONOFFSETXOESPROC)load(\"glPolygonOffsetxOES\");\n\tglad_glRotatexOES = (PFNGLROTATEXOESPROC)load(\"glRotatexOES\");\n\tglad_glSampleCoverageOES = (PFNGLSAMPLECOVERAGEOESPROC)load(\"glSampleCoverageOES\");\n\tglad_glScalexOES = (PFNGLSCALEXOESPROC)load(\"glScalexOES\");\n\tglad_glTexEnvxOES = (PFNGLTEXENVXOESPROC)load(\"glTexEnvxOES\");\n\tglad_glTexEnvxvOES = (PFNGLTEXENVXVOESPROC)load(\"glTexEnvxvOES\");\n\tglad_glTexParameterxOES = (PFNGLTEXPARAMETERXOESPROC)load(\"glTexParameterxOES\");\n\tglad_glTexParameterxvOES = (PFNGLTEXPARAMETERXVOESPROC)load(\"glTexParameterxvOES\");\n\tglad_glTranslatexOES = (PFNGLTRANSLATEXOESPROC)load(\"glTranslatexOES\");\n\tglad_glGetLightxvOES = (PFNGLGETLIGHTXVOESPROC)load(\"glGetLightxvOES\");\n\tglad_glGetMaterialxvOES = (PFNGLGETMATERIALXVOESPROC)load(\"glGetMaterialxvOES\");\n\tglad_glPointParameterxOES = (PFNGLPOINTPARAMETERXOESPROC)load(\"glPointParameterxOES\");\n\tglad_glSampleCoveragexOES = (PFNGLSAMPLECOVERAGEXOESPROC)load(\"glSampleCoveragexOES\");\n\tglad_glAccumxOES = (PFNGLACCUMXOESPROC)load(\"glAccumxOES\");\n\tglad_glBitmapxOES = (PFNGLBITMAPXOESPROC)load(\"glBitmapxOES\");\n\tglad_glBlendColorxOES = (PFNGLBLENDCOLORXOESPROC)load(\"glBlendColorxOES\");\n\tglad_glClearAccumxOES = (PFNGLCLEARACCUMXOESPROC)load(\"glClearAccumxOES\");\n\tglad_glColor3xOES = (PFNGLCOLOR3XOESPROC)load(\"glColor3xOES\");\n\tglad_glColor3xvOES = (PFNGLCOLOR3XVOESPROC)load(\"glColor3xvOES\");\n\tglad_glColor4xvOES = (PFNGLCOLOR4XVOESPROC)load(\"glColor4xvOES\");\n\tglad_glConvolutionParameterxOES = (PFNGLCONVOLUTIONPARAMETERXOESPROC)load(\"glConvolutionParameterxOES\");\n\tglad_glConvolutionParameterxvOES = (PFNGLCONVOLUTIONPARAMETERXVOESPROC)load(\"glConvolutionParameterxvOES\");\n\tglad_glEvalCoord1xOES = (PFNGLEVALCOORD1XOESPROC)load(\"glEvalCoord1xOES\");\n\tglad_glEvalCoord1xvOES = (PFNGLEVALCOORD1XVOESPROC)load(\"glEvalCoord1xvOES\");\n\tglad_glEvalCoord2xOES = (PFNGLEVALCOORD2XOESPROC)load(\"glEvalCoord2xOES\");\n\tglad_glEvalCoord2xvOES = (PFNGLEVALCOORD2XVOESPROC)load(\"glEvalCoord2xvOES\");\n\tglad_glFeedbackBufferxOES = (PFNGLFEEDBACKBUFFERXOESPROC)load(\"glFeedbackBufferxOES\");\n\tglad_glGetConvolutionParameterxvOES = (PFNGLGETCONVOLUTIONPARAMETERXVOESPROC)load(\"glGetConvolutionParameterxvOES\");\n\tglad_glGetHistogramParameterxvOES = (PFNGLGETHISTOGRAMPARAMETERXVOESPROC)load(\"glGetHistogramParameterxvOES\");\n\tglad_glGetLightxOES = (PFNGLGETLIGHTXOESPROC)load(\"glGetLightxOES\");\n\tglad_glGetMapxvOES = (PFNGLGETMAPXVOESPROC)load(\"glGetMapxvOES\");\n\tglad_glGetMaterialxOES = (PFNGLGETMATERIALXOESPROC)load(\"glGetMaterialxOES\");\n\tglad_glGetPixelMapxv = (PFNGLGETPIXELMAPXVPROC)load(\"glGetPixelMapxv\");\n\tglad_glGetTexGenxvOES = (PFNGLGETTEXGENXVOESPROC)load(\"glGetTexGenxvOES\");\n\tglad_glGetTexLevelParameterxvOES = (PFNGLGETTEXLEVELPARAMETERXVOESPROC)load(\"glGetTexLevelParameterxvOES\");\n\tglad_glIndexxOES = (PFNGLINDEXXOESPROC)load(\"glIndexxOES\");\n\tglad_glIndexxvOES = (PFNGLINDEXXVOESPROC)load(\"glIndexxvOES\");\n\tglad_glLoadTransposeMatrixxOES = (PFNGLLOADTRANSPOSEMATRIXXOESPROC)load(\"glLoadTransposeMatrixxOES\");\n\tglad_glMap1xOES = (PFNGLMAP1XOESPROC)load(\"glMap1xOES\");\n\tglad_glMap2xOES = (PFNGLMAP2XOESPROC)load(\"glMap2xOES\");\n\tglad_glMapGrid1xOES = (PFNGLMAPGRID1XOESPROC)load(\"glMapGrid1xOES\");\n\tglad_glMapGrid2xOES = (PFNGLMAPGRID2XOESPROC)load(\"glMapGrid2xOES\");\n\tglad_glMultTransposeMatrixxOES = (PFNGLMULTTRANSPOSEMATRIXXOESPROC)load(\"glMultTransposeMatrixxOES\");\n\tglad_glMultiTexCoord1xOES = (PFNGLMULTITEXCOORD1XOESPROC)load(\"glMultiTexCoord1xOES\");\n\tglad_glMultiTexCoord1xvOES = (PFNGLMULTITEXCOORD1XVOESPROC)load(\"glMultiTexCoord1xvOES\");\n\tglad_glMultiTexCoord2xOES = (PFNGLMULTITEXCOORD2XOESPROC)load(\"glMultiTexCoord2xOES\");\n\tglad_glMultiTexCoord2xvOES = (PFNGLMULTITEXCOORD2XVOESPROC)load(\"glMultiTexCoord2xvOES\");\n\tglad_glMultiTexCoord3xOES = (PFNGLMULTITEXCOORD3XOESPROC)load(\"glMultiTexCoord3xOES\");\n\tglad_glMultiTexCoord3xvOES = (PFNGLMULTITEXCOORD3XVOESPROC)load(\"glMultiTexCoord3xvOES\");\n\tglad_glMultiTexCoord4xvOES = (PFNGLMULTITEXCOORD4XVOESPROC)load(\"glMultiTexCoord4xvOES\");\n\tglad_glNormal3xvOES = (PFNGLNORMAL3XVOESPROC)load(\"glNormal3xvOES\");\n\tglad_glPassThroughxOES = (PFNGLPASSTHROUGHXOESPROC)load(\"glPassThroughxOES\");\n\tglad_glPixelMapx = (PFNGLPIXELMAPXPROC)load(\"glPixelMapx\");\n\tglad_glPixelStorex = (PFNGLPIXELSTOREXPROC)load(\"glPixelStorex\");\n\tglad_glPixelTransferxOES = (PFNGLPIXELTRANSFERXOESPROC)load(\"glPixelTransferxOES\");\n\tglad_glPixelZoomxOES = (PFNGLPIXELZOOMXOESPROC)load(\"glPixelZoomxOES\");\n\tglad_glPrioritizeTexturesxOES = (PFNGLPRIORITIZETEXTURESXOESPROC)load(\"glPrioritizeTexturesxOES\");\n\tglad_glRasterPos2xOES = (PFNGLRASTERPOS2XOESPROC)load(\"glRasterPos2xOES\");\n\tglad_glRasterPos2xvOES = (PFNGLRASTERPOS2XVOESPROC)load(\"glRasterPos2xvOES\");\n\tglad_glRasterPos3xOES = (PFNGLRASTERPOS3XOESPROC)load(\"glRasterPos3xOES\");\n\tglad_glRasterPos3xvOES = (PFNGLRASTERPOS3XVOESPROC)load(\"glRasterPos3xvOES\");\n\tglad_glRasterPos4xOES = (PFNGLRASTERPOS4XOESPROC)load(\"glRasterPos4xOES\");\n\tglad_glRasterPos4xvOES = (PFNGLRASTERPOS4XVOESPROC)load(\"glRasterPos4xvOES\");\n\tglad_glRectxOES = (PFNGLRECTXOESPROC)load(\"glRectxOES\");\n\tglad_glRectxvOES = (PFNGLRECTXVOESPROC)load(\"glRectxvOES\");\n\tglad_glTexCoord1xOES = (PFNGLTEXCOORD1XOESPROC)load(\"glTexCoord1xOES\");\n\tglad_glTexCoord1xvOES = (PFNGLTEXCOORD1XVOESPROC)load(\"glTexCoord1xvOES\");\n\tglad_glTexCoord2xOES = (PFNGLTEXCOORD2XOESPROC)load(\"glTexCoord2xOES\");\n\tglad_glTexCoord2xvOES = (PFNGLTEXCOORD2XVOESPROC)load(\"glTexCoord2xvOES\");\n\tglad_glTexCoord3xOES = (PFNGLTEXCOORD3XOESPROC)load(\"glTexCoord3xOES\");\n\tglad_glTexCoord3xvOES = (PFNGLTEXCOORD3XVOESPROC)load(\"glTexCoord3xvOES\");\n\tglad_glTexCoord4xOES = (PFNGLTEXCOORD4XOESPROC)load(\"glTexCoord4xOES\");\n\tglad_glTexCoord4xvOES = (PFNGLTEXCOORD4XVOESPROC)load(\"glTexCoord4xvOES\");\n\tglad_glTexGenxOES = (PFNGLTEXGENXOESPROC)load(\"glTexGenxOES\");\n\tglad_glTexGenxvOES = (PFNGLTEXGENXVOESPROC)load(\"glTexGenxvOES\");\n\tglad_glVertex2xOES = (PFNGLVERTEX2XOESPROC)load(\"glVertex2xOES\");\n\tglad_glVertex2xvOES = (PFNGLVERTEX2XVOESPROC)load(\"glVertex2xvOES\");\n\tglad_glVertex3xOES = (PFNGLVERTEX3XOESPROC)load(\"glVertex3xOES\");\n\tglad_glVertex3xvOES = (PFNGLVERTEX3XVOESPROC)load(\"glVertex3xvOES\");\n\tglad_glVertex4xOES = (PFNGLVERTEX4XOESPROC)load(\"glVertex4xOES\");\n\tglad_glVertex4xvOES = (PFNGLVERTEX4XVOESPROC)load(\"glVertex4xvOES\");\n}\nstatic void load_GL_EXT_framebuffer_multisample(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_framebuffer_multisample) return;\n\tglad_glRenderbufferStorageMultisampleEXT = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)load(\"glRenderbufferStorageMultisampleEXT\");\n}\nstatic void load_GL_SGIS_texture4D(GLADloadproc load) {\n\tif(!GLAD_GL_SGIS_texture4D) return;\n\tglad_glTexImage4DSGIS = (PFNGLTEXIMAGE4DSGISPROC)load(\"glTexImage4DSGIS\");\n\tglad_glTexSubImage4DSGIS = (PFNGLTEXSUBIMAGE4DSGISPROC)load(\"glTexSubImage4DSGIS\");\n}\nstatic void load_GL_EXT_texture3D(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_texture3D) return;\n\tglad_glTexImage3DEXT = (PFNGLTEXIMAGE3DEXTPROC)load(\"glTexImage3DEXT\");\n\tglad_glTexSubImage3DEXT = (PFNGLTEXSUBIMAGE3DEXTPROC)load(\"glTexSubImage3DEXT\");\n}\nstatic void load_GL_EXT_multisample(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_multisample) return;\n\tglad_glSampleMaskEXT = (PFNGLSAMPLEMASKEXTPROC)load(\"glSampleMaskEXT\");\n\tglad_glSamplePatternEXT = (PFNGLSAMPLEPATTERNEXTPROC)load(\"glSamplePatternEXT\");\n}\nstatic void load_GL_EXT_secondary_color(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_secondary_color) return;\n\tglad_glSecondaryColor3bEXT = (PFNGLSECONDARYCOLOR3BEXTPROC)load(\"glSecondaryColor3bEXT\");\n\tglad_glSecondaryColor3bvEXT = (PFNGLSECONDARYCOLOR3BVEXTPROC)load(\"glSecondaryColor3bvEXT\");\n\tglad_glSecondaryColor3dEXT = (PFNGLSECONDARYCOLOR3DEXTPROC)load(\"glSecondaryColor3dEXT\");\n\tglad_glSecondaryColor3dvEXT = (PFNGLSECONDARYCOLOR3DVEXTPROC)load(\"glSecondaryColor3dvEXT\");\n\tglad_glSecondaryColor3fEXT = (PFNGLSECONDARYCOLOR3FEXTPROC)load(\"glSecondaryColor3fEXT\");\n\tglad_glSecondaryColor3fvEXT = (PFNGLSECONDARYCOLOR3FVEXTPROC)load(\"glSecondaryColor3fvEXT\");\n\tglad_glSecondaryColor3iEXT = (PFNGLSECONDARYCOLOR3IEXTPROC)load(\"glSecondaryColor3iEXT\");\n\tglad_glSecondaryColor3ivEXT = (PFNGLSECONDARYCOLOR3IVEXTPROC)load(\"glSecondaryColor3ivEXT\");\n\tglad_glSecondaryColor3sEXT = (PFNGLSECONDARYCOLOR3SEXTPROC)load(\"glSecondaryColor3sEXT\");\n\tglad_glSecondaryColor3svEXT = (PFNGLSECONDARYCOLOR3SVEXTPROC)load(\"glSecondaryColor3svEXT\");\n\tglad_glSecondaryColor3ubEXT = (PFNGLSECONDARYCOLOR3UBEXTPROC)load(\"glSecondaryColor3ubEXT\");\n\tglad_glSecondaryColor3ubvEXT = (PFNGLSECONDARYCOLOR3UBVEXTPROC)load(\"glSecondaryColor3ubvEXT\");\n\tglad_glSecondaryColor3uiEXT = (PFNGLSECONDARYCOLOR3UIEXTPROC)load(\"glSecondaryColor3uiEXT\");\n\tglad_glSecondaryColor3uivEXT = (PFNGLSECONDARYCOLOR3UIVEXTPROC)load(\"glSecondaryColor3uivEXT\");\n\tglad_glSecondaryColor3usEXT = (PFNGLSECONDARYCOLOR3USEXTPROC)load(\"glSecondaryColor3usEXT\");\n\tglad_glSecondaryColor3usvEXT = (PFNGLSECONDARYCOLOR3USVEXTPROC)load(\"glSecondaryColor3usvEXT\");\n\tglad_glSecondaryColorPointerEXT = (PFNGLSECONDARYCOLORPOINTEREXTPROC)load(\"glSecondaryColorPointerEXT\");\n}\nstatic void load_GL_ATI_vertex_array_object(GLADloadproc load) {\n\tif(!GLAD_GL_ATI_vertex_array_object) return;\n\tglad_glNewObjectBufferATI = (PFNGLNEWOBJECTBUFFERATIPROC)load(\"glNewObjectBufferATI\");\n\tglad_glIsObjectBufferATI = (PFNGLISOBJECTBUFFERATIPROC)load(\"glIsObjectBufferATI\");\n\tglad_glUpdateObjectBufferATI = (PFNGLUPDATEOBJECTBUFFERATIPROC)load(\"glUpdateObjectBufferATI\");\n\tglad_glGetObjectBufferfvATI = (PFNGLGETOBJECTBUFFERFVATIPROC)load(\"glGetObjectBufferfvATI\");\n\tglad_glGetObjectBufferivATI = (PFNGLGETOBJECTBUFFERIVATIPROC)load(\"glGetObjectBufferivATI\");\n\tglad_glFreeObjectBufferATI = (PFNGLFREEOBJECTBUFFERATIPROC)load(\"glFreeObjectBufferATI\");\n\tglad_glArrayObjectATI = (PFNGLARRAYOBJECTATIPROC)load(\"glArrayObjectATI\");\n\tglad_glGetArrayObjectfvATI = (PFNGLGETARRAYOBJECTFVATIPROC)load(\"glGetArrayObjectfvATI\");\n\tglad_glGetArrayObjectivATI = (PFNGLGETARRAYOBJECTIVATIPROC)load(\"glGetArrayObjectivATI\");\n\tglad_glVariantArrayObjectATI = (PFNGLVARIANTARRAYOBJECTATIPROC)load(\"glVariantArrayObjectATI\");\n\tglad_glGetVariantArrayObjectfvATI = (PFNGLGETVARIANTARRAYOBJECTFVATIPROC)load(\"glGetVariantArrayObjectfvATI\");\n\tglad_glGetVariantArrayObjectivATI = (PFNGLGETVARIANTARRAYOBJECTIVATIPROC)load(\"glGetVariantArrayObjectivATI\");\n}\nstatic void load_GL_ARB_sparse_texture(GLADloadproc load) {\n\tif(!GLAD_GL_ARB_sparse_texture) return;\n\tglad_glTexPageCommitmentARB = (PFNGLTEXPAGECOMMITMENTARBPROC)load(\"glTexPageCommitmentARB\");\n}\nstatic void load_GL_EXT_draw_range_elements(GLADloadproc load) {\n\tif(!GLAD_GL_EXT_draw_range_elements) return;\n\tglad_glDrawRangeElementsEXT = (PFNGLDRAWRANGEELEMENTSEXTPROC)load(\"glDrawRangeElementsEXT\");\n}\nstatic void find_extensionsGL(void) {\n\tGLAD_GL_SGIX_pixel_tiles = has_ext(\"GL_SGIX_pixel_tiles\");\n\tGLAD_GL_NV_point_sprite = has_ext(\"GL_NV_point_sprite\");\n\tGLAD_GL_APPLE_element_array = has_ext(\"GL_APPLE_element_array\");\n\tGLAD_GL_AMD_multi_draw_indirect = has_ext(\"GL_AMD_multi_draw_indirect\");\n\tGLAD_GL_EXT_blend_subtract = has_ext(\"GL_EXT_blend_subtract\");\n\tGLAD_GL_SGIX_tag_sample_buffer = has_ext(\"GL_SGIX_tag_sample_buffer\");\n\tGLAD_GL_IBM_texture_mirrored_repeat = has_ext(\"GL_IBM_texture_mirrored_repeat\");\n\tGLAD_GL_APPLE_transform_hint = has_ext(\"GL_APPLE_transform_hint\");\n\tGLAD_GL_ATI_separate_stencil = has_ext(\"GL_ATI_separate_stencil\");\n\tGLAD_GL_NV_vertex_program2_option = has_ext(\"GL_NV_vertex_program2_option\");\n\tGLAD_GL_EXT_texture_buffer_object = has_ext(\"GL_EXT_texture_buffer_object\");\n\tGLAD_GL_ARB_vertex_blend = has_ext(\"GL_ARB_vertex_blend\");\n\tGLAD_GL_NV_vertex_program2 = has_ext(\"GL_NV_vertex_program2\");\n\tGLAD_GL_ARB_program_interface_query = has_ext(\"GL_ARB_program_interface_query\");\n\tGLAD_GL_EXT_misc_attribute = has_ext(\"GL_EXT_misc_attribute\");\n\tGLAD_GL_NV_multisample_coverage = has_ext(\"GL_NV_multisample_coverage\");\n\tGLAD_GL_ARB_shading_language_packing = has_ext(\"GL_ARB_shading_language_packing\");\n\tGLAD_GL_EXT_texture_cube_map = has_ext(\"GL_EXT_texture_cube_map\");\n\tGLAD_GL_ARB_texture_stencil8 = has_ext(\"GL_ARB_texture_stencil8\");\n\tGLAD_GL_EXT_index_func = has_ext(\"GL_EXT_index_func\");\n\tGLAD_GL_OES_compressed_paletted_texture = has_ext(\"GL_OES_compressed_paletted_texture\");\n\tGLAD_GL_NV_depth_clamp = has_ext(\"GL_NV_depth_clamp\");\n\tGLAD_GL_NV_shader_buffer_load = has_ext(\"GL_NV_shader_buffer_load\");\n\tGLAD_GL_EXT_color_subtable = has_ext(\"GL_EXT_color_subtable\");\n\tGLAD_GL_SUNX_constant_data = has_ext(\"GL_SUNX_constant_data\");\n\tGLAD_GL_EXT_multi_draw_arrays = has_ext(\"GL_EXT_multi_draw_arrays\");\n\tGLAD_GL_ARB_shader_atomic_counters = has_ext(\"GL_ARB_shader_atomic_counters\");\n\tGLAD_GL_ARB_arrays_of_arrays = has_ext(\"GL_ARB_arrays_of_arrays\");\n\tGLAD_GL_NV_conditional_render = has_ext(\"GL_NV_conditional_render\");\n\tGLAD_GL_EXT_texture_env_combine = has_ext(\"GL_EXT_texture_env_combine\");\n\tGLAD_GL_NV_fog_distance = has_ext(\"GL_NV_fog_distance\");\n\tGLAD_GL_SGIX_async_histogram = has_ext(\"GL_SGIX_async_histogram\");\n\tGLAD_GL_MESA_resize_buffers = has_ext(\"GL_MESA_resize_buffers\");\n\tGLAD_GL_NV_light_max_exponent = has_ext(\"GL_NV_light_max_exponent\");\n\tGLAD_GL_NV_texture_env_combine4 = has_ext(\"GL_NV_texture_env_combine4\");\n\tGLAD_GL_ARB_texture_view = has_ext(\"GL_ARB_texture_view\");\n\tGLAD_GL_ARB_texture_env_combine = has_ext(\"GL_ARB_texture_env_combine\");\n\tGLAD_GL_ARB_map_buffer_range = has_ext(\"GL_ARB_map_buffer_range\");\n\tGLAD_GL_EXT_convolution = has_ext(\"GL_EXT_convolution\");\n\tGLAD_GL_NV_compute_program5 = has_ext(\"GL_NV_compute_program5\");\n\tGLAD_GL_NV_vertex_attrib_integer_64bit = has_ext(\"GL_NV_vertex_attrib_integer_64bit\");\n\tGLAD_GL_EXT_paletted_texture = has_ext(\"GL_EXT_paletted_texture\");\n\tGLAD_GL_ARB_texture_buffer_object = has_ext(\"GL_ARB_texture_buffer_object\");\n\tGLAD_GL_ATI_pn_triangles = has_ext(\"GL_ATI_pn_triangles\");\n\tGLAD_GL_SGIX_resample = has_ext(\"GL_SGIX_resample\");\n\tGLAD_GL_SGIX_flush_raster = has_ext(\"GL_SGIX_flush_raster\");\n\tGLAD_GL_EXT_light_texture = has_ext(\"GL_EXT_light_texture\");\n\tGLAD_GL_ARB_point_sprite = has_ext(\"GL_ARB_point_sprite\");\n\tGLAD_GL_ARB_half_float_pixel = has_ext(\"GL_ARB_half_float_pixel\");\n\tGLAD_GL_NV_tessellation_program5 = has_ext(\"GL_NV_tessellation_program5\");\n\tGLAD_GL_REND_screen_coordinates = has_ext(\"GL_REND_screen_coordinates\");\n\tGLAD_GL_EXT_shared_texture_palette = has_ext(\"GL_EXT_shared_texture_palette\");\n\tGLAD_GL_EXT_packed_float = has_ext(\"GL_EXT_packed_float\");\n\tGLAD_GL_OML_subsample = has_ext(\"GL_OML_subsample\");\n\tGLAD_GL_SGIX_vertex_preclip = has_ext(\"GL_SGIX_vertex_preclip\");\n\tGLAD_GL_SGIX_texture_scale_bias = has_ext(\"GL_SGIX_texture_scale_bias\");\n\tGLAD_GL_AMD_draw_buffers_blend = has_ext(\"GL_AMD_draw_buffers_blend\");\n\tGLAD_GL_MESA_window_pos = has_ext(\"GL_MESA_window_pos\");\n\tGLAD_GL_EXT_texture_array = has_ext(\"GL_EXT_texture_array\");\n\tGLAD_GL_NV_texture_barrier = has_ext(\"GL_NV_texture_barrier\");\n\tGLAD_GL_ARB_texture_query_levels = has_ext(\"GL_ARB_texture_query_levels\");\n\tGLAD_GL_NV_texgen_emboss = has_ext(\"GL_NV_texgen_emboss\");\n\tGLAD_GL_EXT_texture_swizzle = has_ext(\"GL_EXT_texture_swizzle\");\n\tGLAD_GL_ARB_texture_rg = has_ext(\"GL_ARB_texture_rg\");\n\tGLAD_GL_ARB_vertex_type_2_10_10_10_rev = has_ext(\"GL_ARB_vertex_type_2_10_10_10_rev\");\n\tGLAD_GL_ARB_fragment_shader = has_ext(\"GL_ARB_fragment_shader\");\n\tGLAD_GL_3DFX_tbuffer = has_ext(\"GL_3DFX_tbuffer\");\n\tGLAD_GL_GREMEDY_frame_terminator = has_ext(\"GL_GREMEDY_frame_terminator\");\n\tGLAD_GL_ARB_blend_func_extended = has_ext(\"GL_ARB_blend_func_extended\");\n\tGLAD_GL_EXT_separate_shader_objects = has_ext(\"GL_EXT_separate_shader_objects\");\n\tGLAD_GL_NV_texture_multisample = has_ext(\"GL_NV_texture_multisample\");\n\tGLAD_GL_ARB_shader_objects = has_ext(\"GL_ARB_shader_objects\");\n\tGLAD_GL_ARB_framebuffer_object = has_ext(\"GL_ARB_framebuffer_object\");\n\tGLAD_GL_ATI_envmap_bumpmap = has_ext(\"GL_ATI_envmap_bumpmap\");\n\tGLAD_GL_ARB_robust_buffer_access_behavior = has_ext(\"GL_ARB_robust_buffer_access_behavior\");\n\tGLAD_GL_ARB_shader_stencil_export = has_ext(\"GL_ARB_shader_stencil_export\");\n\tGLAD_GL_NV_texture_rectangle = has_ext(\"GL_NV_texture_rectangle\");\n\tGLAD_GL_ARB_enhanced_layouts = has_ext(\"GL_ARB_enhanced_layouts\");\n\tGLAD_GL_ARB_texture_rectangle = has_ext(\"GL_ARB_texture_rectangle\");\n\tGLAD_GL_SGI_texture_color_table = has_ext(\"GL_SGI_texture_color_table\");\n\tGLAD_GL_ATI_map_object_buffer = has_ext(\"GL_ATI_map_object_buffer\");\n\tGLAD_GL_ARB_robustness = has_ext(\"GL_ARB_robustness\");\n\tGLAD_GL_NV_pixel_data_range = has_ext(\"GL_NV_pixel_data_range\");\n\tGLAD_GL_EXT_framebuffer_blit = has_ext(\"GL_EXT_framebuffer_blit\");\n\tGLAD_GL_ARB_gpu_shader_fp64 = has_ext(\"GL_ARB_gpu_shader_fp64\");\n\tGLAD_GL_SGIX_depth_texture = has_ext(\"GL_SGIX_depth_texture\");\n\tGLAD_GL_EXT_vertex_weighting = has_ext(\"GL_EXT_vertex_weighting\");\n\tGLAD_GL_GREMEDY_string_marker = has_ext(\"GL_GREMEDY_string_marker\");\n\tGLAD_GL_ARB_texture_compression_bptc = has_ext(\"GL_ARB_texture_compression_bptc\");\n\tGLAD_GL_EXT_subtexture = has_ext(\"GL_EXT_subtexture\");\n\tGLAD_GL_EXT_pixel_transform_color_table = has_ext(\"GL_EXT_pixel_transform_color_table\");\n\tGLAD_GL_EXT_texture_compression_rgtc = has_ext(\"GL_EXT_texture_compression_rgtc\");\n\tGLAD_GL_SGIX_depth_pass_instrument = has_ext(\"GL_SGIX_depth_pass_instrument\");\n\tGLAD_GL_ARB_shader_precision = has_ext(\"GL_ARB_shader_precision\");\n\tGLAD_GL_NV_evaluators = has_ext(\"GL_NV_evaluators\");\n\tGLAD_GL_SGIS_texture_filter4 = has_ext(\"GL_SGIS_texture_filter4\");\n\tGLAD_GL_AMD_performance_monitor = has_ext(\"GL_AMD_performance_monitor\");\n\tGLAD_GL_NV_geometry_shader4 = has_ext(\"GL_NV_geometry_shader4\");\n\tGLAD_GL_EXT_stencil_clear_tag = has_ext(\"GL_EXT_stencil_clear_tag\");\n\tGLAD_GL_NV_vertex_program1_1 = has_ext(\"GL_NV_vertex_program1_1\");\n\tGLAD_GL_NV_present_video = has_ext(\"GL_NV_present_video\");\n\tGLAD_GL_ARB_texture_compression_rgtc = has_ext(\"GL_ARB_texture_compression_rgtc\");\n\tGLAD_GL_HP_convolution_border_modes = has_ext(\"GL_HP_convolution_border_modes\");\n\tGLAD_GL_EXT_gpu_program_parameters = has_ext(\"GL_EXT_gpu_program_parameters\");\n\tGLAD_GL_EXT_shader_integer_mix = has_ext(\"GL_EXT_shader_integer_mix\");\n\tGLAD_GL_ARB_stencil_texturing = has_ext(\"GL_ARB_stencil_texturing\");\n\tGLAD_GL_SGIX_fog_offset = has_ext(\"GL_SGIX_fog_offset\");\n\tGLAD_GL_ARB_draw_elements_base_vertex = has_ext(\"GL_ARB_draw_elements_base_vertex\");\n\tGLAD_GL_INGR_interlace_read = has_ext(\"GL_INGR_interlace_read\");\n\tGLAD_GL_NV_transform_feedback = has_ext(\"GL_NV_transform_feedback\");\n\tGLAD_GL_NV_fragment_program = has_ext(\"GL_NV_fragment_program\");\n\tGLAD_GL_AMD_stencil_operation_extended = has_ext(\"GL_AMD_stencil_operation_extended\");\n\tGLAD_GL_ARB_seamless_cubemap_per_texture = has_ext(\"GL_ARB_seamless_cubemap_per_texture\");\n\tGLAD_GL_ARB_instanced_arrays = has_ext(\"GL_ARB_instanced_arrays\");\n\tGLAD_GL_EXT_polygon_offset = has_ext(\"GL_EXT_polygon_offset\");\n\tGLAD_GL_NV_vertex_array_range2 = has_ext(\"GL_NV_vertex_array_range2\");\n\tGLAD_GL_AMD_sparse_texture = has_ext(\"GL_AMD_sparse_texture\");\n\tGLAD_GL_NV_fence = has_ext(\"GL_NV_fence\");\n\tGLAD_GL_ARB_texture_buffer_range = has_ext(\"GL_ARB_texture_buffer_range\");\n\tGLAD_GL_SUN_mesh_array = has_ext(\"GL_SUN_mesh_array\");\n\tGLAD_GL_ARB_vertex_attrib_binding = has_ext(\"GL_ARB_vertex_attrib_binding\");\n\tGLAD_GL_ARB_framebuffer_no_attachments = has_ext(\"GL_ARB_framebuffer_no_attachments\");\n\tGLAD_GL_ARB_cl_event = has_ext(\"GL_ARB_cl_event\");\n\tGLAD_GL_NV_packed_depth_stencil = has_ext(\"GL_NV_packed_depth_stencil\");\n\tGLAD_GL_OES_single_precision = has_ext(\"GL_OES_single_precision\");\n\tGLAD_GL_NV_primitive_restart = has_ext(\"GL_NV_primitive_restart\");\n\tGLAD_GL_SUN_global_alpha = has_ext(\"GL_SUN_global_alpha\");\n\tGLAD_GL_EXT_texture_object = has_ext(\"GL_EXT_texture_object\");\n\tGLAD_GL_AMD_name_gen_delete = has_ext(\"GL_AMD_name_gen_delete\");\n\tGLAD_GL_NV_texture_compression_vtc = has_ext(\"GL_NV_texture_compression_vtc\");\n\tGLAD_GL_SGIX_ycrcb_subsample = has_ext(\"GL_SGIX_ycrcb_subsample\");\n\tGLAD_GL_NV_texture_shader3 = has_ext(\"GL_NV_texture_shader3\");\n\tGLAD_GL_NV_texture_shader2 = has_ext(\"GL_NV_texture_shader2\");\n\tGLAD_GL_EXT_texture = has_ext(\"GL_EXT_texture\");\n\tGLAD_GL_ARB_buffer_storage = has_ext(\"GL_ARB_buffer_storage\");\n\tGLAD_GL_AMD_shader_atomic_counter_ops = has_ext(\"GL_AMD_shader_atomic_counter_ops\");\n\tGLAD_GL_APPLE_vertex_program_evaluators = has_ext(\"GL_APPLE_vertex_program_evaluators\");\n\tGLAD_GL_ARB_multi_bind = has_ext(\"GL_ARB_multi_bind\");\n\tGLAD_GL_ARB_explicit_uniform_location = has_ext(\"GL_ARB_explicit_uniform_location\");\n\tGLAD_GL_ARB_depth_buffer_float = has_ext(\"GL_ARB_depth_buffer_float\");\n\tGLAD_GL_SGIX_shadow_ambient = has_ext(\"GL_SGIX_shadow_ambient\");\n\tGLAD_GL_ARB_texture_cube_map = has_ext(\"GL_ARB_texture_cube_map\");\n\tGLAD_GL_AMD_vertex_shader_viewport_index = has_ext(\"GL_AMD_vertex_shader_viewport_index\");\n\tGLAD_GL_SGIX_list_priority = has_ext(\"GL_SGIX_list_priority\");\n\tGLAD_GL_NV_vertex_buffer_unified_memory = has_ext(\"GL_NV_vertex_buffer_unified_memory\");\n\tGLAD_GL_EXT_texture_env_dot3 = has_ext(\"GL_EXT_texture_env_dot3\");\n\tGLAD_GL_ATI_texture_env_combine3 = has_ext(\"GL_ATI_texture_env_combine3\");\n\tGLAD_GL_ARB_map_buffer_alignment = has_ext(\"GL_ARB_map_buffer_alignment\");\n\tGLAD_GL_NV_blend_equation_advanced = has_ext(\"GL_NV_blend_equation_advanced\");\n\tGLAD_GL_SGIS_sharpen_texture = has_ext(\"GL_SGIS_sharpen_texture\");\n\tGLAD_GL_ARB_vertex_program = has_ext(\"GL_ARB_vertex_program\");\n\tGLAD_GL_ARB_texture_rgb10_a2ui = has_ext(\"GL_ARB_texture_rgb10_a2ui\");\n\tGLAD_GL_OML_interlace = has_ext(\"GL_OML_interlace\");\n\tGLAD_GL_ATI_pixel_format_float = has_ext(\"GL_ATI_pixel_format_float\");\n\tGLAD_GL_ARB_vertex_buffer_object = has_ext(\"GL_ARB_vertex_buffer_object\");\n\tGLAD_GL_EXT_shadow_funcs = has_ext(\"GL_EXT_shadow_funcs\");\n\tGLAD_GL_ATI_text_fragment_shader = has_ext(\"GL_ATI_text_fragment_shader\");\n\tGLAD_GL_NV_vertex_array_range = has_ext(\"GL_NV_vertex_array_range\");\n\tGLAD_GL_SGIX_fragment_lighting = has_ext(\"GL_SGIX_fragment_lighting\");\n\tGLAD_GL_NV_texture_expand_normal = has_ext(\"GL_NV_texture_expand_normal\");\n\tGLAD_GL_NV_framebuffer_multisample_coverage = has_ext(\"GL_NV_framebuffer_multisample_coverage\");\n\tGLAD_GL_EXT_timer_query = has_ext(\"GL_EXT_timer_query\");\n\tGLAD_GL_EXT_vertex_array_bgra = has_ext(\"GL_EXT_vertex_array_bgra\");\n\tGLAD_GL_NV_bindless_texture = has_ext(\"GL_NV_bindless_texture\");\n\tGLAD_GL_KHR_debug = has_ext(\"GL_KHR_debug\");\n\tGLAD_GL_SGIS_texture_border_clamp = has_ext(\"GL_SGIS_texture_border_clamp\");\n\tGLAD_GL_ATI_vertex_attrib_array_object = has_ext(\"GL_ATI_vertex_attrib_array_object\");\n\tGLAD_GL_SGIX_clipmap = has_ext(\"GL_SGIX_clipmap\");\n\tGLAD_GL_EXT_geometry_shader4 = has_ext(\"GL_EXT_geometry_shader4\");\n\tGLAD_GL_MESA_ycbcr_texture = has_ext(\"GL_MESA_ycbcr_texture\");\n\tGLAD_GL_MESAX_texture_stack = has_ext(\"GL_MESAX_texture_stack\");\n\tGLAD_GL_AMD_seamless_cubemap_per_texture = has_ext(\"GL_AMD_seamless_cubemap_per_texture\");\n\tGLAD_GL_EXT_bindable_uniform = has_ext(\"GL_EXT_bindable_uniform\");\n\tGLAD_GL_KHR_texture_compression_astc_hdr = has_ext(\"GL_KHR_texture_compression_astc_hdr\");\n\tGLAD_GL_ARB_fragment_program_shadow = has_ext(\"GL_ARB_fragment_program_shadow\");\n\tGLAD_GL_ATI_element_array = has_ext(\"GL_ATI_element_array\");\n\tGLAD_GL_AMD_texture_texture4 = has_ext(\"GL_AMD_texture_texture4\");\n\tGLAD_GL_SGIX_reference_plane = has_ext(\"GL_SGIX_reference_plane\");\n\tGLAD_GL_EXT_stencil_two_side = has_ext(\"GL_EXT_stencil_two_side\");\n\tGLAD_GL_SGIX_texture_lod_bias = has_ext(\"GL_SGIX_texture_lod_bias\");\n\tGLAD_GL_NV_explicit_multisample = has_ext(\"GL_NV_explicit_multisample\");\n\tGLAD_GL_IBM_static_data = has_ext(\"GL_IBM_static_data\");\n\tGLAD_GL_EXT_clip_volume_hint = has_ext(\"GL_EXT_clip_volume_hint\");\n\tGLAD_GL_EXT_texture_perturb_normal = has_ext(\"GL_EXT_texture_perturb_normal\");\n\tGLAD_GL_NV_fragment_program2 = has_ext(\"GL_NV_fragment_program2\");\n\tGLAD_GL_NV_fragment_program4 = has_ext(\"GL_NV_fragment_program4\");\n\tGLAD_GL_EXT_point_parameters = has_ext(\"GL_EXT_point_parameters\");\n\tGLAD_GL_PGI_misc_hints = has_ext(\"GL_PGI_misc_hints\");\n\tGLAD_GL_SGIX_subsample = has_ext(\"GL_SGIX_subsample\");\n\tGLAD_GL_AMD_shader_stencil_export = has_ext(\"GL_AMD_shader_stencil_export\");\n\tGLAD_GL_ARB_shader_texture_lod = has_ext(\"GL_ARB_shader_texture_lod\");\n\tGLAD_GL_ARB_vertex_shader = has_ext(\"GL_ARB_vertex_shader\");\n\tGLAD_GL_ARB_depth_clamp = has_ext(\"GL_ARB_depth_clamp\");\n\tGLAD_GL_SGIS_texture_select = has_ext(\"GL_SGIS_texture_select\");\n\tGLAD_GL_NV_texture_shader = has_ext(\"GL_NV_texture_shader\");\n\tGLAD_GL_ARB_tessellation_shader = has_ext(\"GL_ARB_tessellation_shader\");\n\tGLAD_GL_EXT_draw_buffers2 = has_ext(\"GL_EXT_draw_buffers2\");\n\tGLAD_GL_ARB_vertex_attrib_64bit = has_ext(\"GL_ARB_vertex_attrib_64bit\");\n\tGLAD_GL_WIN_specular_fog = has_ext(\"GL_WIN_specular_fog\");\n\tGLAD_GL_AMD_interleaved_elements = has_ext(\"GL_AMD_interleaved_elements\");\n\tGLAD_GL_ARB_fragment_program = has_ext(\"GL_ARB_fragment_program\");\n\tGLAD_GL_OML_resample = has_ext(\"GL_OML_resample\");\n\tGLAD_GL_APPLE_ycbcr_422 = has_ext(\"GL_APPLE_ycbcr_422\");\n\tGLAD_GL_SGIX_texture_add_env = has_ext(\"GL_SGIX_texture_add_env\");\n\tGLAD_GL_ARB_shadow_ambient = has_ext(\"GL_ARB_shadow_ambient\");\n\tGLAD_GL_ARB_texture_storage = has_ext(\"GL_ARB_texture_storage\");\n\tGLAD_GL_EXT_pixel_buffer_object = has_ext(\"GL_EXT_pixel_buffer_object\");\n\tGLAD_GL_ARB_copy_image = has_ext(\"GL_ARB_copy_image\");\n\tGLAD_GL_SGIS_pixel_texture = has_ext(\"GL_SGIS_pixel_texture\");\n\tGLAD_GL_SGIS_generate_mipmap = has_ext(\"GL_SGIS_generate_mipmap\");\n\tGLAD_GL_SGIX_instruments = has_ext(\"GL_SGIX_instruments\");\n\tGLAD_GL_HP_texture_lighting = has_ext(\"GL_HP_texture_lighting\");\n\tGLAD_GL_ARB_shader_storage_buffer_object = has_ext(\"GL_ARB_shader_storage_buffer_object\");\n\tGLAD_GL_EXT_blend_minmax = has_ext(\"GL_EXT_blend_minmax\");\n\tGLAD_GL_MESA_pack_invert = has_ext(\"GL_MESA_pack_invert\");\n\tGLAD_GL_ARB_base_instance = has_ext(\"GL_ARB_base_instance\");\n\tGLAD_GL_SGIX_convolution_accuracy = has_ext(\"GL_SGIX_convolution_accuracy\");\n\tGLAD_GL_PGI_vertex_hints = has_ext(\"GL_PGI_vertex_hints\");\n\tGLAD_GL_EXT_texture_integer = has_ext(\"GL_EXT_texture_integer\");\n\tGLAD_GL_ARB_texture_multisample = has_ext(\"GL_ARB_texture_multisample\");\n\tGLAD_GL_S3_s3tc = has_ext(\"GL_S3_s3tc\");\n\tGLAD_GL_ARB_query_buffer_object = has_ext(\"GL_ARB_query_buffer_object\");\n\tGLAD_GL_AMD_vertex_shader_tessellator = has_ext(\"GL_AMD_vertex_shader_tessellator\");\n\tGLAD_GL_ARB_invalidate_subdata = has_ext(\"GL_ARB_invalidate_subdata\");\n\tGLAD_GL_EXT_index_material = has_ext(\"GL_EXT_index_material\");\n\tGLAD_GL_NV_blend_equation_advanced_coherent = has_ext(\"GL_NV_blend_equation_advanced_coherent\");\n\tGLAD_GL_INTEL_parallel_arrays = has_ext(\"GL_INTEL_parallel_arrays\");\n\tGLAD_GL_ATI_draw_buffers = has_ext(\"GL_ATI_draw_buffers\");\n\tGLAD_GL_EXT_cmyka = has_ext(\"GL_EXT_cmyka\");\n\tGLAD_GL_SGIX_pixel_texture = has_ext(\"GL_SGIX_pixel_texture\");\n\tGLAD_GL_APPLE_specular_vector = has_ext(\"GL_APPLE_specular_vector\");\n\tGLAD_GL_ARB_compatibility = has_ext(\"GL_ARB_compatibility\");\n\tGLAD_GL_ARB_timer_query = has_ext(\"GL_ARB_timer_query\");\n\tGLAD_GL_SGIX_interlace = has_ext(\"GL_SGIX_interlace\");\n\tGLAD_GL_NV_parameter_buffer_object = has_ext(\"GL_NV_parameter_buffer_object\");\n\tGLAD_GL_AMD_shader_trinary_minmax = has_ext(\"GL_AMD_shader_trinary_minmax\");\n\tGLAD_GL_EXT_rescale_normal = has_ext(\"GL_EXT_rescale_normal\");\n\tGLAD_GL_ARB_pixel_buffer_object = has_ext(\"GL_ARB_pixel_buffer_object\");\n\tGLAD_GL_ARB_uniform_buffer_object = has_ext(\"GL_ARB_uniform_buffer_object\");\n\tGLAD_GL_ARB_vertex_type_10f_11f_11f_rev = has_ext(\"GL_ARB_vertex_type_10f_11f_11f_rev\");\n\tGLAD_GL_ARB_texture_swizzle = has_ext(\"GL_ARB_texture_swizzle\");\n\tGLAD_GL_NV_transform_feedback2 = has_ext(\"GL_NV_transform_feedback2\");\n\tGLAD_GL_SGIX_async_pixel = has_ext(\"GL_SGIX_async_pixel\");\n\tGLAD_GL_NV_fragment_program_option = has_ext(\"GL_NV_fragment_program_option\");\n\tGLAD_GL_ARB_explicit_attrib_location = has_ext(\"GL_ARB_explicit_attrib_location\");\n\tGLAD_GL_EXT_blend_color = has_ext(\"GL_EXT_blend_color\");\n\tGLAD_GL_NV_shader_thread_group = has_ext(\"GL_NV_shader_thread_group\");\n\tGLAD_GL_EXT_stencil_wrap = has_ext(\"GL_EXT_stencil_wrap\");\n\tGLAD_GL_EXT_index_array_formats = has_ext(\"GL_EXT_index_array_formats\");\n\tGLAD_GL_EXT_histogram = has_ext(\"GL_EXT_histogram\");\n\tGLAD_GL_SGIS_point_parameters = has_ext(\"GL_SGIS_point_parameters\");\n\tGLAD_GL_EXT_direct_state_access = has_ext(\"GL_EXT_direct_state_access\");\n\tGLAD_GL_AMD_sample_positions = has_ext(\"GL_AMD_sample_positions\");\n\tGLAD_GL_NV_vertex_program = has_ext(\"GL_NV_vertex_program\");\n\tGLAD_GL_NV_shader_thread_shuffle = has_ext(\"GL_NV_shader_thread_shuffle\");\n\tGLAD_GL_NVX_conditional_render = has_ext(\"GL_NVX_conditional_render\");\n\tGLAD_GL_EXT_vertex_shader = has_ext(\"GL_EXT_vertex_shader\");\n\tGLAD_GL_EXT_blend_func_separate = has_ext(\"GL_EXT_blend_func_separate\");\n\tGLAD_GL_APPLE_fence = has_ext(\"GL_APPLE_fence\");\n\tGLAD_GL_OES_byte_coordinates = has_ext(\"GL_OES_byte_coordinates\");\n\tGLAD_GL_ARB_transpose_matrix = has_ext(\"GL_ARB_transpose_matrix\");\n\tGLAD_GL_ARB_provoking_vertex = has_ext(\"GL_ARB_provoking_vertex\");\n\tGLAD_GL_EXT_fog_coord = has_ext(\"GL_EXT_fog_coord\");\n\tGLAD_GL_EXT_vertex_array = has_ext(\"GL_EXT_vertex_array\");\n\tGLAD_GL_ARB_half_float_vertex = has_ext(\"GL_ARB_half_float_vertex\");\n\tGLAD_GL_EXT_blend_equation_separate = has_ext(\"GL_EXT_blend_equation_separate\");\n\tGLAD_GL_ARB_multi_draw_indirect = has_ext(\"GL_ARB_multi_draw_indirect\");\n\tGLAD_GL_NV_copy_image = has_ext(\"GL_NV_copy_image\");\n\tGLAD_GL_ARB_fragment_layer_viewport = has_ext(\"GL_ARB_fragment_layer_viewport\");\n\tGLAD_GL_ARB_transform_feedback2 = has_ext(\"GL_ARB_transform_feedback2\");\n\tGLAD_GL_ARB_transform_feedback3 = has_ext(\"GL_ARB_transform_feedback3\");\n\tGLAD_GL_SGIX_ycrcba = has_ext(\"GL_SGIX_ycrcba\");\n\tGLAD_GL_EXT_debug_marker = has_ext(\"GL_EXT_debug_marker\");\n\tGLAD_GL_EXT_bgra = has_ext(\"GL_EXT_bgra\");\n\tGLAD_GL_EXT_texture_compression_s3tc = has_ext(\"GL_EXT_texture_compression_s3tc\");\n\tGLAD_GL_EXT_pixel_transform = has_ext(\"GL_EXT_pixel_transform\");\n\tGLAD_GL_ARB_conservative_depth = has_ext(\"GL_ARB_conservative_depth\");\n\tGLAD_GL_ATI_fragment_shader = has_ext(\"GL_ATI_fragment_shader\");\n\tGLAD_GL_ARB_vertex_array_object = has_ext(\"GL_ARB_vertex_array_object\");\n\tGLAD_GL_SUN_triangle_list = has_ext(\"GL_SUN_triangle_list\");\n\tGLAD_GL_EXT_texture_env_add = has_ext(\"GL_EXT_texture_env_add\");\n\tGLAD_GL_EXT_packed_depth_stencil = has_ext(\"GL_EXT_packed_depth_stencil\");\n\tGLAD_GL_EXT_texture_mirror_clamp = has_ext(\"GL_EXT_texture_mirror_clamp\");\n\tGLAD_GL_NV_multisample_filter_hint = has_ext(\"GL_NV_multisample_filter_hint\");\n\tGLAD_GL_APPLE_float_pixels = has_ext(\"GL_APPLE_float_pixels\");\n\tGLAD_GL_ARB_transform_feedback_instanced = has_ext(\"GL_ARB_transform_feedback_instanced\");\n\tGLAD_GL_SGIX_async = has_ext(\"GL_SGIX_async\");\n\tGLAD_GL_EXT_texture_compression_latc = has_ext(\"GL_EXT_texture_compression_latc\");\n\tGLAD_GL_NV_shader_atomic_float = has_ext(\"GL_NV_shader_atomic_float\");\n\tGLAD_GL_ARB_shading_language_100 = has_ext(\"GL_ARB_shading_language_100\");\n\tGLAD_GL_INTEL_performance_query = has_ext(\"GL_INTEL_performance_query\");\n\tGLAD_GL_ARB_texture_mirror_clamp_to_edge = has_ext(\"GL_ARB_texture_mirror_clamp_to_edge\");\n\tGLAD_GL_NV_gpu_shader5 = has_ext(\"GL_NV_gpu_shader5\");\n\tGLAD_GL_ARB_ES2_compatibility = has_ext(\"GL_ARB_ES2_compatibility\");\n\tGLAD_GL_ARB_indirect_parameters = has_ext(\"GL_ARB_indirect_parameters\");\n\tGLAD_GL_NV_half_float = has_ext(\"GL_NV_half_float\");\n\tGLAD_GL_EXT_coordinate_frame = has_ext(\"GL_EXT_coordinate_frame\");\n\tGLAD_GL_ATI_texture_mirror_once = has_ext(\"GL_ATI_texture_mirror_once\");\n\tGLAD_GL_IBM_rasterpos_clip = has_ext(\"GL_IBM_rasterpos_clip\");\n\tGLAD_GL_SGIX_shadow = has_ext(\"GL_SGIX_shadow\");\n\tGLAD_GL_NV_deep_texture3D = has_ext(\"GL_NV_deep_texture3D\");\n\tGLAD_GL_ARB_shader_draw_parameters = has_ext(\"GL_ARB_shader_draw_parameters\");\n\tGLAD_GL_SGIX_calligraphic_fragment = has_ext(\"GL_SGIX_calligraphic_fragment\");\n\tGLAD_GL_ARB_shader_bit_encoding = has_ext(\"GL_ARB_shader_bit_encoding\");\n\tGLAD_GL_EXT_compiled_vertex_array = has_ext(\"GL_EXT_compiled_vertex_array\");\n\tGLAD_GL_NV_depth_buffer_float = has_ext(\"GL_NV_depth_buffer_float\");\n\tGLAD_GL_NV_occlusion_query = has_ext(\"GL_NV_occlusion_query\");\n\tGLAD_GL_APPLE_flush_buffer_range = has_ext(\"GL_APPLE_flush_buffer_range\");\n\tGLAD_GL_ARB_imaging = has_ext(\"GL_ARB_imaging\");\n\tGLAD_GL_ARB_draw_buffers_blend = has_ext(\"GL_ARB_draw_buffers_blend\");\n\tGLAD_GL_NV_blend_square = has_ext(\"GL_NV_blend_square\");\n\tGLAD_GL_AMD_blend_minmax_factor = has_ext(\"GL_AMD_blend_minmax_factor\");\n\tGLAD_GL_EXT_texture_sRGB_decode = has_ext(\"GL_EXT_texture_sRGB_decode\");\n\tGLAD_GL_ARB_shading_language_420pack = has_ext(\"GL_ARB_shading_language_420pack\");\n\tGLAD_GL_ATI_meminfo = has_ext(\"GL_ATI_meminfo\");\n\tGLAD_GL_EXT_abgr = has_ext(\"GL_EXT_abgr\");\n\tGLAD_GL_AMD_pinned_memory = has_ext(\"GL_AMD_pinned_memory\");\n\tGLAD_GL_EXT_texture_snorm = has_ext(\"GL_EXT_texture_snorm\");\n\tGLAD_GL_SGIX_texture_coordinate_clamp = has_ext(\"GL_SGIX_texture_coordinate_clamp\");\n\tGLAD_GL_ARB_clear_buffer_object = has_ext(\"GL_ARB_clear_buffer_object\");\n\tGLAD_GL_ARB_multisample = has_ext(\"GL_ARB_multisample\");\n\tGLAD_GL_EXT_debug_label = has_ext(\"GL_EXT_debug_label\");\n\tGLAD_GL_ARB_sample_shading = has_ext(\"GL_ARB_sample_shading\");\n\tGLAD_GL_INTEL_map_texture = has_ext(\"GL_INTEL_map_texture\");\n\tGLAD_GL_ARB_texture_env_crossbar = has_ext(\"GL_ARB_texture_env_crossbar\");\n\tGLAD_GL_EXT_422_pixels = has_ext(\"GL_EXT_422_pixels\");\n\tGLAD_GL_ARB_compute_shader = has_ext(\"GL_ARB_compute_shader\");\n\tGLAD_GL_EXT_blend_logic_op = has_ext(\"GL_EXT_blend_logic_op\");\n\tGLAD_GL_IBM_cull_vertex = has_ext(\"GL_IBM_cull_vertex\");\n\tGLAD_GL_IBM_vertex_array_lists = has_ext(\"GL_IBM_vertex_array_lists\");\n\tGLAD_GL_ARB_color_buffer_float = has_ext(\"GL_ARB_color_buffer_float\");\n\tGLAD_GL_ARB_bindless_texture = has_ext(\"GL_ARB_bindless_texture\");\n\tGLAD_GL_ARB_window_pos = has_ext(\"GL_ARB_window_pos\");\n\tGLAD_GL_ARB_internalformat_query = has_ext(\"GL_ARB_internalformat_query\");\n\tGLAD_GL_ARB_shadow = has_ext(\"GL_ARB_shadow\");\n\tGLAD_GL_ARB_texture_mirrored_repeat = has_ext(\"GL_ARB_texture_mirrored_repeat\");\n\tGLAD_GL_EXT_shader_image_load_store = has_ext(\"GL_EXT_shader_image_load_store\");\n\tGLAD_GL_EXT_copy_texture = has_ext(\"GL_EXT_copy_texture\");\n\tGLAD_GL_NV_register_combiners2 = has_ext(\"GL_NV_register_combiners2\");\n\tGLAD_GL_SGIX_ir_instrument1 = has_ext(\"GL_SGIX_ir_instrument1\");\n\tGLAD_GL_NV_draw_texture = has_ext(\"GL_NV_draw_texture\");\n\tGLAD_GL_EXT_texture_shared_exponent = has_ext(\"GL_EXT_texture_shared_exponent\");\n\tGLAD_GL_EXT_draw_instanced = has_ext(\"GL_EXT_draw_instanced\");\n\tGLAD_GL_NV_copy_depth_to_color = has_ext(\"GL_NV_copy_depth_to_color\");\n\tGLAD_GL_ARB_viewport_array = has_ext(\"GL_ARB_viewport_array\");\n\tGLAD_GL_ARB_separate_shader_objects = has_ext(\"GL_ARB_separate_shader_objects\");\n\tGLAD_GL_EXT_depth_bounds_test = has_ext(\"GL_EXT_depth_bounds_test\");\n\tGLAD_GL_HP_image_transform = has_ext(\"GL_HP_image_transform\");\n\tGLAD_GL_ARB_texture_env_add = has_ext(\"GL_ARB_texture_env_add\");\n\tGLAD_GL_NV_video_capture = has_ext(\"GL_NV_video_capture\");\n\tGLAD_GL_ARB_sampler_objects = has_ext(\"GL_ARB_sampler_objects\");\n\tGLAD_GL_ARB_matrix_palette = has_ext(\"GL_ARB_matrix_palette\");\n\tGLAD_GL_SGIS_texture_color_mask = has_ext(\"GL_SGIS_texture_color_mask\");\n\tGLAD_GL_EXT_packed_pixels = has_ext(\"GL_EXT_packed_pixels\");\n\tGLAD_GL_ARB_texture_compression = has_ext(\"GL_ARB_texture_compression\");\n\tGLAD_GL_APPLE_aux_depth_stencil = has_ext(\"GL_APPLE_aux_depth_stencil\");\n\tGLAD_GL_ARB_shader_subroutine = has_ext(\"GL_ARB_shader_subroutine\");\n\tGLAD_GL_EXT_framebuffer_sRGB = has_ext(\"GL_EXT_framebuffer_sRGB\");\n\tGLAD_GL_ARB_texture_storage_multisample = has_ext(\"GL_ARB_texture_storage_multisample\");\n\tGLAD_GL_EXT_vertex_attrib_64bit = has_ext(\"GL_EXT_vertex_attrib_64bit\");\n\tGLAD_GL_ARB_depth_texture = has_ext(\"GL_ARB_depth_texture\");\n\tGLAD_GL_NV_shader_buffer_store = has_ext(\"GL_NV_shader_buffer_store\");\n\tGLAD_GL_OES_query_matrix = has_ext(\"GL_OES_query_matrix\");\n\tGLAD_GL_APPLE_texture_range = has_ext(\"GL_APPLE_texture_range\");\n\tGLAD_GL_NV_shader_storage_buffer_object = has_ext(\"GL_NV_shader_storage_buffer_object\");\n\tGLAD_GL_ARB_texture_query_lod = has_ext(\"GL_ARB_texture_query_lod\");\n\tGLAD_GL_ARB_copy_buffer = has_ext(\"GL_ARB_copy_buffer\");\n\tGLAD_GL_ARB_shader_image_size = has_ext(\"GL_ARB_shader_image_size\");\n\tGLAD_GL_NV_shader_atomic_counters = has_ext(\"GL_NV_shader_atomic_counters\");\n\tGLAD_GL_APPLE_object_purgeable = has_ext(\"GL_APPLE_object_purgeable\");\n\tGLAD_GL_ARB_occlusion_query = has_ext(\"GL_ARB_occlusion_query\");\n\tGLAD_GL_INGR_color_clamp = has_ext(\"GL_INGR_color_clamp\");\n\tGLAD_GL_SGI_color_table = has_ext(\"GL_SGI_color_table\");\n\tGLAD_GL_NV_gpu_program5_mem_extended = has_ext(\"GL_NV_gpu_program5_mem_extended\");\n\tGLAD_GL_ARB_texture_cube_map_array = has_ext(\"GL_ARB_texture_cube_map_array\");\n\tGLAD_GL_SGIX_scalebias_hint = has_ext(\"GL_SGIX_scalebias_hint\");\n\tGLAD_GL_EXT_gpu_shader4 = has_ext(\"GL_EXT_gpu_shader4\");\n\tGLAD_GL_NV_geometry_program4 = has_ext(\"GL_NV_geometry_program4\");\n\tGLAD_GL_EXT_framebuffer_multisample_blit_scaled = has_ext(\"GL_EXT_framebuffer_multisample_blit_scaled\");\n\tGLAD_GL_AMD_debug_output = has_ext(\"GL_AMD_debug_output\");\n\tGLAD_GL_ARB_texture_border_clamp = has_ext(\"GL_ARB_texture_border_clamp\");\n\tGLAD_GL_ARB_fragment_coord_conventions = has_ext(\"GL_ARB_fragment_coord_conventions\");\n\tGLAD_GL_ARB_multitexture = has_ext(\"GL_ARB_multitexture\");\n\tGLAD_GL_SGIX_polynomial_ffd = has_ext(\"GL_SGIX_polynomial_ffd\");\n\tGLAD_GL_EXT_provoking_vertex = has_ext(\"GL_EXT_provoking_vertex\");\n\tGLAD_GL_ARB_point_parameters = has_ext(\"GL_ARB_point_parameters\");\n\tGLAD_GL_ARB_shader_image_load_store = has_ext(\"GL_ARB_shader_image_load_store\");\n\tGLAD_GL_HP_occlusion_test = has_ext(\"GL_HP_occlusion_test\");\n\tGLAD_GL_ARB_ES3_compatibility = has_ext(\"GL_ARB_ES3_compatibility\");\n\tGLAD_GL_SGIX_framezoom = has_ext(\"GL_SGIX_framezoom\");\n\tGLAD_GL_ARB_texture_buffer_object_rgb32 = has_ext(\"GL_ARB_texture_buffer_object_rgb32\");\n\tGLAD_GL_NV_bindless_multi_draw_indirect = has_ext(\"GL_NV_bindless_multi_draw_indirect\");\n\tGLAD_GL_SGIX_texture_multi_buffer = has_ext(\"GL_SGIX_texture_multi_buffer\");\n\tGLAD_GL_EXT_transform_feedback = has_ext(\"GL_EXT_transform_feedback\");\n\tGLAD_GL_KHR_texture_compression_astc_ldr = has_ext(\"GL_KHR_texture_compression_astc_ldr\");\n\tGLAD_GL_3DFX_multisample = has_ext(\"GL_3DFX_multisample\");\n\tGLAD_GL_INTEL_fragment_shader_ordering = has_ext(\"GL_INTEL_fragment_shader_ordering\");\n\tGLAD_GL_ARB_texture_env_dot3 = has_ext(\"GL_ARB_texture_env_dot3\");\n\tGLAD_GL_NV_gpu_program4 = has_ext(\"GL_NV_gpu_program4\");\n\tGLAD_GL_NV_gpu_program5 = has_ext(\"GL_NV_gpu_program5\");\n\tGLAD_GL_NV_float_buffer = has_ext(\"GL_NV_float_buffer\");\n\tGLAD_GL_SGIS_texture_edge_clamp = has_ext(\"GL_SGIS_texture_edge_clamp\");\n\tGLAD_GL_ARB_framebuffer_sRGB = has_ext(\"GL_ARB_framebuffer_sRGB\");\n\tGLAD_GL_SUN_slice_accum = has_ext(\"GL_SUN_slice_accum\");\n\tGLAD_GL_EXT_index_texture = has_ext(\"GL_EXT_index_texture\");\n\tGLAD_GL_EXT_shader_image_load_formatted = has_ext(\"GL_EXT_shader_image_load_formatted\");\n\tGLAD_GL_ARB_geometry_shader4 = has_ext(\"GL_ARB_geometry_shader4\");\n\tGLAD_GL_EXT_separate_specular_color = has_ext(\"GL_EXT_separate_specular_color\");\n\tGLAD_GL_AMD_depth_clamp_separate = has_ext(\"GL_AMD_depth_clamp_separate\");\n\tGLAD_GL_SUN_convolution_border_modes = has_ext(\"GL_SUN_convolution_border_modes\");\n\tGLAD_GL_SGIX_sprite = has_ext(\"GL_SGIX_sprite\");\n\tGLAD_GL_ARB_get_program_binary = has_ext(\"GL_ARB_get_program_binary\");\n\tGLAD_GL_AMD_occlusion_query_event = has_ext(\"GL_AMD_occlusion_query_event\");\n\tGLAD_GL_SGIS_multisample = has_ext(\"GL_SGIS_multisample\");\n\tGLAD_GL_EXT_framebuffer_object = has_ext(\"GL_EXT_framebuffer_object\");\n\tGLAD_GL_ARB_robustness_isolation = has_ext(\"GL_ARB_robustness_isolation\");\n\tGLAD_GL_ARB_vertex_array_bgra = has_ext(\"GL_ARB_vertex_array_bgra\");\n\tGLAD_GL_APPLE_vertex_array_range = has_ext(\"GL_APPLE_vertex_array_range\");\n\tGLAD_GL_AMD_query_buffer_object = has_ext(\"GL_AMD_query_buffer_object\");\n\tGLAD_GL_NV_register_combiners = has_ext(\"GL_NV_register_combiners\");\n\tGLAD_GL_ARB_draw_buffers = has_ext(\"GL_ARB_draw_buffers\");\n\tGLAD_GL_ARB_clear_texture = has_ext(\"GL_ARB_clear_texture\");\n\tGLAD_GL_ARB_debug_output = has_ext(\"GL_ARB_debug_output\");\n\tGLAD_GL_SGI_color_matrix = has_ext(\"GL_SGI_color_matrix\");\n\tGLAD_GL_EXT_cull_vertex = has_ext(\"GL_EXT_cull_vertex\");\n\tGLAD_GL_EXT_texture_sRGB = has_ext(\"GL_EXT_texture_sRGB\");\n\tGLAD_GL_APPLE_row_bytes = has_ext(\"GL_APPLE_row_bytes\");\n\tGLAD_GL_NV_texgen_reflection = has_ext(\"GL_NV_texgen_reflection\");\n\tGLAD_GL_IBM_multimode_draw_arrays = has_ext(\"GL_IBM_multimode_draw_arrays\");\n\tGLAD_GL_APPLE_vertex_array_object = has_ext(\"GL_APPLE_vertex_array_object\");\n\tGLAD_GL_3DFX_texture_compression_FXT1 = has_ext(\"GL_3DFX_texture_compression_FXT1\");\n\tGLAD_GL_SGIX_ycrcb = has_ext(\"GL_SGIX_ycrcb\");\n\tGLAD_GL_AMD_conservative_depth = has_ext(\"GL_AMD_conservative_depth\");\n\tGLAD_GL_ARB_texture_float = has_ext(\"GL_ARB_texture_float\");\n\tGLAD_GL_ARB_compressed_texture_pixel_storage = has_ext(\"GL_ARB_compressed_texture_pixel_storage\");\n\tGLAD_GL_SGIS_detail_texture = has_ext(\"GL_SGIS_detail_texture\");\n\tGLAD_GL_ARB_draw_instanced = has_ext(\"GL_ARB_draw_instanced\");\n\tGLAD_GL_OES_read_format = has_ext(\"GL_OES_read_format\");\n\tGLAD_GL_ATI_texture_float = has_ext(\"GL_ATI_texture_float\");\n\tGLAD_GL_ARB_texture_gather = has_ext(\"GL_ARB_texture_gather\");\n\tGLAD_GL_AMD_vertex_shader_layer = has_ext(\"GL_AMD_vertex_shader_layer\");\n\tGLAD_GL_ARB_shading_language_include = has_ext(\"GL_ARB_shading_language_include\");\n\tGLAD_GL_APPLE_client_storage = has_ext(\"GL_APPLE_client_storage\");\n\tGLAD_GL_WIN_phong_shading = has_ext(\"GL_WIN_phong_shading\");\n\tGLAD_GL_INGR_blend_func_separate = has_ext(\"GL_INGR_blend_func_separate\");\n\tGLAD_GL_NV_path_rendering = has_ext(\"GL_NV_path_rendering\");\n\tGLAD_GL_ATI_vertex_streams = has_ext(\"GL_ATI_vertex_streams\");\n\tGLAD_GL_ARB_texture_non_power_of_two = has_ext(\"GL_ARB_texture_non_power_of_two\");\n\tGLAD_GL_APPLE_rgb_422 = has_ext(\"GL_APPLE_rgb_422\");\n\tGLAD_GL_EXT_texture_lod_bias = has_ext(\"GL_EXT_texture_lod_bias\");\n\tGLAD_GL_ARB_seamless_cube_map = has_ext(\"GL_ARB_seamless_cube_map\");\n\tGLAD_GL_ARB_shader_group_vote = has_ext(\"GL_ARB_shader_group_vote\");\n\tGLAD_GL_NV_vdpau_interop = has_ext(\"GL_NV_vdpau_interop\");\n\tGLAD_GL_ARB_occlusion_query2 = has_ext(\"GL_ARB_occlusion_query2\");\n\tGLAD_GL_ARB_internalformat_query2 = has_ext(\"GL_ARB_internalformat_query2\");\n\tGLAD_GL_EXT_texture_filter_anisotropic = has_ext(\"GL_EXT_texture_filter_anisotropic\");\n\tGLAD_GL_SUN_vertex = has_ext(\"GL_SUN_vertex\");\n\tGLAD_GL_SGIX_igloo_interface = has_ext(\"GL_SGIX_igloo_interface\");\n\tGLAD_GL_SGIS_texture_lod = has_ext(\"GL_SGIS_texture_lod\");\n\tGLAD_GL_NV_vertex_program3 = has_ext(\"GL_NV_vertex_program3\");\n\tGLAD_GL_ARB_draw_indirect = has_ext(\"GL_ARB_draw_indirect\");\n\tGLAD_GL_NV_vertex_program4 = has_ext(\"GL_NV_vertex_program4\");\n\tGLAD_GL_AMD_transform_feedback3_lines_triangles = has_ext(\"GL_AMD_transform_feedback3_lines_triangles\");\n\tGLAD_GL_SGIS_fog_function = has_ext(\"GL_SGIS_fog_function\");\n\tGLAD_GL_EXT_x11_sync_object = has_ext(\"GL_EXT_x11_sync_object\");\n\tGLAD_GL_ARB_sync = has_ext(\"GL_ARB_sync\");\n\tGLAD_GL_ARB_compute_variable_group_size = has_ext(\"GL_ARB_compute_variable_group_size\");\n\tGLAD_GL_OES_fixed_point = has_ext(\"GL_OES_fixed_point\");\n\tGLAD_GL_EXT_framebuffer_multisample = has_ext(\"GL_EXT_framebuffer_multisample\");\n\tGLAD_GL_ARB_gpu_shader5 = has_ext(\"GL_ARB_gpu_shader5\");\n\tGLAD_GL_SGIS_texture4D = has_ext(\"GL_SGIS_texture4D\");\n\tGLAD_GL_EXT_texture3D = has_ext(\"GL_EXT_texture3D\");\n\tGLAD_GL_EXT_multisample = has_ext(\"GL_EXT_multisample\");\n\tGLAD_GL_EXT_secondary_color = has_ext(\"GL_EXT_secondary_color\");\n\tGLAD_GL_NV_parameter_buffer_object2 = has_ext(\"GL_NV_parameter_buffer_object2\");\n\tGLAD_GL_ATI_vertex_array_object = has_ext(\"GL_ATI_vertex_array_object\");\n\tGLAD_GL_NVX_gpu_memory_info = has_ext(\"GL_NVX_gpu_memory_info\");\n\tGLAD_GL_ARB_sparse_texture = has_ext(\"GL_ARB_sparse_texture\");\n\tGLAD_GL_SGIS_point_line_texgen = has_ext(\"GL_SGIS_point_line_texgen\");\n\tGLAD_GL_EXT_draw_range_elements = has_ext(\"GL_EXT_draw_range_elements\");\n\tGLAD_GL_SGIX_blend_alpha_minmax = has_ext(\"GL_SGIX_blend_alpha_minmax\");\n}\n\nstatic void find_coreGL(void) {\n\tconst char *v = (const char *)glGetString(GL_VERSION);\n\tint major = v[0] - '0';\n\tint minor = v[2] - '0';\n\tGLVersion.major = major; GLVersion.minor = minor;\n\tGLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1;\n\tGLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1;\n\tGLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1;\n\tGLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1;\n\tGLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1;\n\tGLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1;\n\tGLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2;\n\tGLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2;\n\tGLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3;\n\tGLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3;\n\tGLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3;\n\tGLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3;\n\tGLAD_GL_VERSION_4_0 = (major == 4 && minor >= 0) || major > 4;\n\tGLAD_GL_VERSION_4_1 = (major == 4 && minor >= 1) || major > 4;\n\tGLAD_GL_VERSION_4_2 = (major == 4 && minor >= 2) || major > 4;\n\tGLAD_GL_VERSION_4_3 = (major == 4 && minor >= 3) || major > 4;\n\tGLAD_GL_VERSION_4_4 = (major == 4 && minor >= 4) || major > 4;\n}\n\nvoid gladLoadGLLoader(GLADloadproc load) {\n\tGLVersion.major = 0; GLVersion.minor = 0;\n\tglGetString = (PFNGLGETSTRINGPROC)load(\"glGetString\");\n\tif(glGetString == NULL) return;\n\tfind_coreGL();\n\tload_GL_VERSION_1_0(load);\n\tload_GL_VERSION_1_1(load);\n\tload_GL_VERSION_1_2(load);\n\tload_GL_VERSION_1_3(load);\n\tload_GL_VERSION_1_4(load);\n\tload_GL_VERSION_1_5(load);\n\tload_GL_VERSION_2_0(load);\n\tload_GL_VERSION_2_1(load);\n\tload_GL_VERSION_3_0(load);\n\tload_GL_VERSION_3_1(load);\n\tload_GL_VERSION_3_2(load);\n\tload_GL_VERSION_3_3(load);\n\tload_GL_VERSION_4_0(load);\n\tload_GL_VERSION_4_1(load);\n\tload_GL_VERSION_4_2(load);\n\tload_GL_VERSION_4_3(load);\n\tload_GL_VERSION_4_4(load);\n\n\tfind_extensionsGL();\n\tload_GL_NV_point_sprite(load);\n\tload_GL_APPLE_element_array(load);\n\tload_GL_AMD_multi_draw_indirect(load);\n\tload_GL_SGIX_tag_sample_buffer(load);\n\tload_GL_ATI_separate_stencil(load);\n\tload_GL_EXT_texture_buffer_object(load);\n\tload_GL_ARB_vertex_blend(load);\n\tload_GL_ARB_program_interface_query(load);\n\tload_GL_EXT_index_func(load);\n\tload_GL_NV_shader_buffer_load(load);\n\tload_GL_EXT_color_subtable(load);\n\tload_GL_SUNX_constant_data(load);\n\tload_GL_EXT_multi_draw_arrays(load);\n\tload_GL_ARB_shader_atomic_counters(load);\n\tload_GL_NV_conditional_render(load);\n\tload_GL_MESA_resize_buffers(load);\n\tload_GL_ARB_texture_view(load);\n\tload_GL_ARB_map_buffer_range(load);\n\tload_GL_EXT_convolution(load);\n\tload_GL_NV_vertex_attrib_integer_64bit(load);\n\tload_GL_EXT_paletted_texture(load);\n\tload_GL_ARB_texture_buffer_object(load);\n\tload_GL_ATI_pn_triangles(load);\n\tload_GL_SGIX_flush_raster(load);\n\tload_GL_EXT_light_texture(load);\n\tload_GL_AMD_draw_buffers_blend(load);\n\tload_GL_MESA_window_pos(load);\n\tload_GL_NV_texture_barrier(load);\n\tload_GL_ARB_vertex_type_2_10_10_10_rev(load);\n\tload_GL_3DFX_tbuffer(load);\n\tload_GL_GREMEDY_frame_terminator(load);\n\tload_GL_ARB_blend_func_extended(load);\n\tload_GL_EXT_separate_shader_objects(load);\n\tload_GL_NV_texture_multisample(load);\n\tload_GL_ARB_shader_objects(load);\n\tload_GL_ARB_framebuffer_object(load);\n\tload_GL_ATI_envmap_bumpmap(load);\n\tload_GL_ATI_map_object_buffer(load);\n\tload_GL_ARB_robustness(load);\n\tload_GL_NV_pixel_data_range(load);\n\tload_GL_EXT_framebuffer_blit(load);\n\tload_GL_ARB_gpu_shader_fp64(load);\n\tload_GL_EXT_vertex_weighting(load);\n\tload_GL_GREMEDY_string_marker(load);\n\tload_GL_EXT_subtexture(load);\n\tload_GL_NV_evaluators(load);\n\tload_GL_SGIS_texture_filter4(load);\n\tload_GL_AMD_performance_monitor(load);\n\tload_GL_EXT_stencil_clear_tag(load);\n\tload_GL_NV_present_video(load);\n\tload_GL_EXT_gpu_program_parameters(load);\n\tload_GL_ARB_draw_elements_base_vertex(load);\n\tload_GL_NV_transform_feedback(load);\n\tload_GL_NV_fragment_program(load);\n\tload_GL_AMD_stencil_operation_extended(load);\n\tload_GL_ARB_instanced_arrays(load);\n\tload_GL_EXT_polygon_offset(load);\n\tload_GL_AMD_sparse_texture(load);\n\tload_GL_NV_fence(load);\n\tload_GL_ARB_texture_buffer_range(load);\n\tload_GL_SUN_mesh_array(load);\n\tload_GL_ARB_vertex_attrib_binding(load);\n\tload_GL_ARB_framebuffer_no_attachments(load);\n\tload_GL_ARB_cl_event(load);\n\tload_GL_OES_single_precision(load);\n\tload_GL_NV_primitive_restart(load);\n\tload_GL_SUN_global_alpha(load);\n\tload_GL_EXT_texture_object(load);\n\tload_GL_AMD_name_gen_delete(load);\n\tload_GL_ARB_buffer_storage(load);\n\tload_GL_APPLE_vertex_program_evaluators(load);\n\tload_GL_ARB_multi_bind(load);\n\tload_GL_SGIX_list_priority(load);\n\tload_GL_NV_vertex_buffer_unified_memory(load);\n\tload_GL_NV_blend_equation_advanced(load);\n\tload_GL_SGIS_sharpen_texture(load);\n\tload_GL_ARB_vertex_program(load);\n\tload_GL_ARB_vertex_buffer_object(load);\n\tload_GL_NV_vertex_array_range(load);\n\tload_GL_SGIX_fragment_lighting(load);\n\tload_GL_NV_framebuffer_multisample_coverage(load);\n\tload_GL_EXT_timer_query(load);\n\tload_GL_NV_bindless_texture(load);\n\tload_GL_KHR_debug(load);\n\tload_GL_ATI_vertex_attrib_array_object(load);\n\tload_GL_EXT_geometry_shader4(load);\n\tload_GL_EXT_bindable_uniform(load);\n\tload_GL_ATI_element_array(load);\n\tload_GL_SGIX_reference_plane(load);\n\tload_GL_EXT_stencil_two_side(load);\n\tload_GL_NV_explicit_multisample(load);\n\tload_GL_IBM_static_data(load);\n\tload_GL_EXT_texture_perturb_normal(load);\n\tload_GL_EXT_point_parameters(load);\n\tload_GL_PGI_misc_hints(load);\n\tload_GL_ARB_vertex_shader(load);\n\tload_GL_ARB_tessellation_shader(load);\n\tload_GL_EXT_draw_buffers2(load);\n\tload_GL_ARB_vertex_attrib_64bit(load);\n\tload_GL_AMD_interleaved_elements(load);\n\tload_GL_ARB_fragment_program(load);\n\tload_GL_ARB_texture_storage(load);\n\tload_GL_ARB_copy_image(load);\n\tload_GL_SGIS_pixel_texture(load);\n\tload_GL_SGIX_instruments(load);\n\tload_GL_ARB_shader_storage_buffer_object(load);\n\tload_GL_EXT_blend_minmax(load);\n\tload_GL_ARB_base_instance(load);\n\tload_GL_EXT_texture_integer(load);\n\tload_GL_ARB_texture_multisample(load);\n\tload_GL_AMD_vertex_shader_tessellator(load);\n\tload_GL_ARB_invalidate_subdata(load);\n\tload_GL_EXT_index_material(load);\n\tload_GL_INTEL_parallel_arrays(load);\n\tload_GL_ATI_draw_buffers(load);\n\tload_GL_SGIX_pixel_texture(load);\n\tload_GL_ARB_timer_query(load);\n\tload_GL_NV_parameter_buffer_object(load);\n\tload_GL_ARB_uniform_buffer_object(load);\n\tload_GL_NV_transform_feedback2(load);\n\tload_GL_EXT_blend_color(load);\n\tload_GL_EXT_histogram(load);\n\tload_GL_SGIS_point_parameters(load);\n\tload_GL_EXT_direct_state_access(load);\n\tload_GL_AMD_sample_positions(load);\n\tload_GL_NV_vertex_program(load);\n\tload_GL_NVX_conditional_render(load);\n\tload_GL_EXT_vertex_shader(load);\n\tload_GL_EXT_blend_func_separate(load);\n\tload_GL_APPLE_fence(load);\n\tload_GL_OES_byte_coordinates(load);\n\tload_GL_ARB_transpose_matrix(load);\n\tload_GL_ARB_provoking_vertex(load);\n\tload_GL_EXT_fog_coord(load);\n\tload_GL_EXT_vertex_array(load);\n\tload_GL_EXT_blend_equation_separate(load);\n\tload_GL_ARB_multi_draw_indirect(load);\n\tload_GL_NV_copy_image(load);\n\tload_GL_ARB_transform_feedback2(load);\n\tload_GL_ARB_transform_feedback3(load);\n\tload_GL_EXT_debug_marker(load);\n\tload_GL_EXT_pixel_transform(load);\n\tload_GL_ATI_fragment_shader(load);\n\tload_GL_ARB_vertex_array_object(load);\n\tload_GL_SUN_triangle_list(load);\n\tload_GL_ARB_transform_feedback_instanced(load);\n\tload_GL_SGIX_async(load);\n\tload_GL_INTEL_performance_query(load);\n\tload_GL_NV_gpu_shader5(load);\n\tload_GL_ARB_ES2_compatibility(load);\n\tload_GL_ARB_indirect_parameters(load);\n\tload_GL_NV_half_float(load);\n\tload_GL_EXT_coordinate_frame(load);\n\tload_GL_EXT_compiled_vertex_array(load);\n\tload_GL_NV_depth_buffer_float(load);\n\tload_GL_NV_occlusion_query(load);\n\tload_GL_APPLE_flush_buffer_range(load);\n\tload_GL_ARB_imaging(load);\n\tload_GL_ARB_draw_buffers_blend(load);\n\tload_GL_ARB_clear_buffer_object(load);\n\tload_GL_ARB_multisample(load);\n\tload_GL_EXT_debug_label(load);\n\tload_GL_ARB_sample_shading(load);\n\tload_GL_INTEL_map_texture(load);\n\tload_GL_ARB_compute_shader(load);\n\tload_GL_IBM_vertex_array_lists(load);\n\tload_GL_ARB_color_buffer_float(load);\n\tload_GL_ARB_bindless_texture(load);\n\tload_GL_ARB_window_pos(load);\n\tload_GL_ARB_internalformat_query(load);\n\tload_GL_EXT_shader_image_load_store(load);\n\tload_GL_EXT_copy_texture(load);\n\tload_GL_NV_register_combiners2(load);\n\tload_GL_NV_draw_texture(load);\n\tload_GL_EXT_draw_instanced(load);\n\tload_GL_ARB_viewport_array(load);\n\tload_GL_ARB_separate_shader_objects(load);\n\tload_GL_EXT_depth_bounds_test(load);\n\tload_GL_HP_image_transform(load);\n\tload_GL_NV_video_capture(load);\n\tload_GL_ARB_sampler_objects(load);\n\tload_GL_ARB_matrix_palette(load);\n\tload_GL_SGIS_texture_color_mask(load);\n\tload_GL_ARB_texture_compression(load);\n\tload_GL_ARB_shader_subroutine(load);\n\tload_GL_ARB_texture_storage_multisample(load);\n\tload_GL_EXT_vertex_attrib_64bit(load);\n\tload_GL_OES_query_matrix(load);\n\tload_GL_APPLE_texture_range(load);\n\tload_GL_ARB_copy_buffer(load);\n\tload_GL_APPLE_object_purgeable(load);\n\tload_GL_ARB_occlusion_query(load);\n\tload_GL_SGI_color_table(load);\n\tload_GL_EXT_gpu_shader4(load);\n\tload_GL_NV_geometry_program4(load);\n\tload_GL_AMD_debug_output(load);\n\tload_GL_ARB_multitexture(load);\n\tload_GL_SGIX_polynomial_ffd(load);\n\tload_GL_EXT_provoking_vertex(load);\n\tload_GL_ARB_point_parameters(load);\n\tload_GL_ARB_shader_image_load_store(load);\n\tload_GL_SGIX_framezoom(load);\n\tload_GL_NV_bindless_multi_draw_indirect(load);\n\tload_GL_EXT_transform_feedback(load);\n\tload_GL_NV_gpu_program4(load);\n\tload_GL_NV_gpu_program5(load);\n\tload_GL_ARB_geometry_shader4(load);\n\tload_GL_SGIX_sprite(load);\n\tload_GL_ARB_get_program_binary(load);\n\tload_GL_AMD_occlusion_query_event(load);\n\tload_GL_SGIS_multisample(load);\n\tload_GL_EXT_framebuffer_object(load);\n\tload_GL_APPLE_vertex_array_range(load);\n\tload_GL_NV_register_combiners(load);\n\tload_GL_ARB_draw_buffers(load);\n\tload_GL_ARB_clear_texture(load);\n\tload_GL_ARB_debug_output(load);\n\tload_GL_EXT_cull_vertex(load);\n\tload_GL_IBM_multimode_draw_arrays(load);\n\tload_GL_APPLE_vertex_array_object(load);\n\tload_GL_SGIS_detail_texture(load);\n\tload_GL_ARB_draw_instanced(load);\n\tload_GL_ARB_shading_language_include(load);\n\tload_GL_INGR_blend_func_separate(load);\n\tload_GL_NV_path_rendering(load);\n\tload_GL_ATI_vertex_streams(load);\n\tload_GL_NV_vdpau_interop(load);\n\tload_GL_ARB_internalformat_query2(load);\n\tload_GL_SUN_vertex(load);\n\tload_GL_SGIX_igloo_interface(load);\n\tload_GL_ARB_draw_indirect(load);\n\tload_GL_NV_vertex_program4(load);\n\tload_GL_SGIS_fog_function(load);\n\tload_GL_EXT_x11_sync_object(load);\n\tload_GL_ARB_sync(load);\n\tload_GL_ARB_compute_variable_group_size(load);\n\tload_GL_OES_fixed_point(load);\n\tload_GL_EXT_framebuffer_multisample(load);\n\tload_GL_SGIS_texture4D(load);\n\tload_GL_EXT_texture3D(load);\n\tload_GL_EXT_multisample(load);\n\tload_GL_EXT_secondary_color(load);\n\tload_GL_ATI_vertex_array_object(load);\n\tload_GL_ARB_sparse_texture(load);\n\tload_GL_EXT_draw_range_elements(load);\n\n\treturn;\n}\n\n"
  },
  {
    "path": "deps/glad/src/glad_egl.c",
    "content": "/*\n\n    EGL loader generated by glad 0.1.36 on Tue Oct 22 13:32:48 2024.\n\n    Language/Generator: C/C++\n    Specification: egl\n    APIs: egl=1.5\n    Profile: -\n    Extensions:\n        EGL_ANDROID_native_fence_sync,\n        EGL_EXT_device_base,\n        EGL_EXT_device_drm_render_node,\n        EGL_EXT_image_dma_buf_import,\n        EGL_EXT_image_dma_buf_import_modifiers,\n        EGL_EXT_platform_base,\n        EGL_EXT_platform_wayland,\n        EGL_EXT_platform_x11,\n        EGL_EXT_platform_xcb,\n        EGL_KHR_create_context,\n        EGL_KHR_image_base,\n        EGL_KHR_image_pixmap,\n        EGL_KHR_platform_wayland,\n        EGL_KHR_platform_x11,\n        EGL_MESA_image_dma_buf_export\n    Loader: True\n    Local files: False\n    Omit khrplatform: False\n    Reproducible: False\n\n    Commandline:\n        --api=\"egl=1.5\" --generator=\"c\" --spec=\"egl\" --extensions=\"EGL_ANDROID_native_fence_sync,EGL_EXT_device_base,EGL_EXT_device_drm_render_node,EGL_EXT_image_dma_buf_import,EGL_EXT_image_dma_buf_import_modifiers,EGL_EXT_platform_base,EGL_EXT_platform_wayland,EGL_EXT_platform_x11,EGL_EXT_platform_xcb,EGL_KHR_create_context,EGL_KHR_image_base,EGL_KHR_image_pixmap,EGL_KHR_platform_wayland,EGL_KHR_platform_x11,EGL_MESA_image_dma_buf_export\"\n    Online:\n        https://glad.dav1d.de/#language=c&specification=egl&loader=on&api=egl%3D1.5&extensions=EGL_ANDROID_native_fence_sync&extensions=EGL_EXT_device_base&extensions=EGL_EXT_device_drm_render_node&extensions=EGL_EXT_image_dma_buf_import&extensions=EGL_EXT_image_dma_buf_import_modifiers&extensions=EGL_EXT_platform_base&extensions=EGL_EXT_platform_wayland&extensions=EGL_EXT_platform_x11&extensions=EGL_EXT_platform_xcb&extensions=EGL_KHR_create_context&extensions=EGL_KHR_image_base&extensions=EGL_KHR_image_pixmap&extensions=EGL_KHR_platform_wayland&extensions=EGL_KHR_platform_x11&extensions=EGL_MESA_image_dma_buf_export\n*/\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <glad/glad_egl.h>\n\nint gladLoadEGL(void) {\n    return gladLoadEGLLoader((GLADloadproc)eglGetProcAddress);\n}\n\nPFNEGLDUPNATIVEFENCEFDANDROIDPROC glad_eglDupNativeFenceFDANDROID = NULL;\nPFNEGLQUERYDEVICEATTRIBEXTPROC glad_eglQueryDeviceAttribEXT = NULL;\nPFNEGLQUERYDEVICESTRINGEXTPROC glad_eglQueryDeviceStringEXT = NULL;\nPFNEGLQUERYDEVICESEXTPROC glad_eglQueryDevicesEXT = NULL;\nPFNEGLQUERYDISPLAYATTRIBEXTPROC glad_eglQueryDisplayAttribEXT = NULL;\nPFNEGLQUERYDMABUFFORMATSEXTPROC glad_eglQueryDmaBufFormatsEXT = NULL;\nPFNEGLQUERYDMABUFMODIFIERSEXTPROC glad_eglQueryDmaBufModifiersEXT = NULL;\nPFNEGLGETPLATFORMDISPLAYEXTPROC glad_eglGetPlatformDisplayEXT = NULL;\nPFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC glad_eglCreatePlatformWindowSurfaceEXT = NULL;\nPFNEGLCREATEPLATFORMPIXMAPSURFACEEXTPROC glad_eglCreatePlatformPixmapSurfaceEXT = NULL;\nPFNEGLCREATEIMAGEKHRPROC glad_eglCreateImageKHR = NULL;\nPFNEGLDESTROYIMAGEKHRPROC glad_eglDestroyImageKHR = NULL;\nPFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC glad_eglExportDMABUFImageQueryMESA = NULL;\nPFNEGLEXPORTDMABUFIMAGEMESAPROC glad_eglExportDMABUFImageMESA = NULL;\nstatic void load_EGL_ANDROID_native_fence_sync(GLADloadproc load) {\n\tglad_eglDupNativeFenceFDANDROID = (PFNEGLDUPNATIVEFENCEFDANDROIDPROC)load(\"eglDupNativeFenceFDANDROID\");\n}\nstatic void load_EGL_EXT_device_base(GLADloadproc load) {\n\tglad_eglQueryDeviceAttribEXT = (PFNEGLQUERYDEVICEATTRIBEXTPROC)load(\"eglQueryDeviceAttribEXT\");\n\tglad_eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)load(\"eglQueryDeviceStringEXT\");\n\tglad_eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)load(\"eglQueryDevicesEXT\");\n\tglad_eglQueryDisplayAttribEXT = (PFNEGLQUERYDISPLAYATTRIBEXTPROC)load(\"eglQueryDisplayAttribEXT\");\n}\nstatic void load_EGL_EXT_image_dma_buf_import_modifiers(GLADloadproc load) {\n\tglad_eglQueryDmaBufFormatsEXT = (PFNEGLQUERYDMABUFFORMATSEXTPROC)load(\"eglQueryDmaBufFormatsEXT\");\n\tglad_eglQueryDmaBufModifiersEXT = (PFNEGLQUERYDMABUFMODIFIERSEXTPROC)load(\"eglQueryDmaBufModifiersEXT\");\n}\nstatic void load_EGL_EXT_platform_base(GLADloadproc load) {\n\tglad_eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)load(\"eglGetPlatformDisplayEXT\");\n\tglad_eglCreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)load(\"eglCreatePlatformWindowSurfaceEXT\");\n\tglad_eglCreatePlatformPixmapSurfaceEXT = (PFNEGLCREATEPLATFORMPIXMAPSURFACEEXTPROC)load(\"eglCreatePlatformPixmapSurfaceEXT\");\n}\nstatic void load_EGL_KHR_image_base(GLADloadproc load) {\n\tglad_eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)load(\"eglCreateImageKHR\");\n\tglad_eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)load(\"eglDestroyImageKHR\");\n}\nstatic void load_EGL_MESA_image_dma_buf_export(GLADloadproc load) {\n\tglad_eglExportDMABUFImageQueryMESA = (PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC)load(\"eglExportDMABUFImageQueryMESA\");\n\tglad_eglExportDMABUFImageMESA = (PFNEGLEXPORTDMABUFIMAGEMESAPROC)load(\"eglExportDMABUFImageMESA\");\n}\nstatic int find_extensionsEGL(void) {\n\treturn 1;\n}\n\nstatic void find_coreEGL(void) {\n}\n\nint gladLoadEGLLoader(GLADloadproc load) {\n\t(void) load;\n\tfind_coreEGL();\n\n\tif (!find_extensionsEGL()) return 0;\n\tload_EGL_ANDROID_native_fence_sync(load);\n\tload_EGL_EXT_device_base(load);\n\tload_EGL_EXT_image_dma_buf_import_modifiers(load);\n\tload_EGL_EXT_platform_base(load);\n\tload_EGL_KHR_image_base(load);\n\tload_EGL_MESA_image_dma_buf_export(load);\n\treturn 1;\n}\n\n"
  },
  {
    "path": "deps/glad/src/glad_wgl.c",
    "content": "#include <string.h>\n#include <glad/glad_wgl.h>\n\nstatic void* get_proc(const char *namez);\n\n#ifdef _WIN32\n#include <windows.h>\nstatic HMODULE libGL;\n\ntypedef void* (APIENTRYP PFNWGLGETPROCADDRESSPROC_PRIVATE)(const char*);\nPFNWGLGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;\n\nstatic\nint open_gl(void) {\n    libGL = LoadLibraryA(\"opengl32.dll\");\n    if(libGL != NULL) {\n        gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE)GetProcAddress(\n                libGL, \"wglGetProcAddress\");\n        return gladGetProcAddressPtr != NULL;\n    }\n\n    return 0;\n}\n\nstatic\nvoid close_gl(void) {\n    if(libGL != NULL) {\n        FreeLibrary(libGL);\n        libGL = NULL;\n    }\n}\n#else\n#include <dlfcn.h>\nstatic void* libGL;\n\n#ifndef __APPLE__\ntypedef void* (APIENTRYP PFNGLXGETPROCADDRESSPROC_PRIVATE)(const char*);\nPFNGLXGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;\n#endif\n\nstatic\nint open_gl(void) {\n#ifdef __APPLE__\n    static const char *NAMES[] = {\n        \"../Frameworks/OpenGL.framework/OpenGL\",\n        \"/Library/Frameworks/OpenGL.framework/OpenGL\",\n        \"/System/Library/Frameworks/OpenGL.framework/OpenGL\",\n        \"/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL\"\n    };\n#else\n    static const char *NAMES[] = {\"libGL.so.1\", \"libGL.so\"};\n#endif\n\n    unsigned int index = 0;\n    for(index = 0; index < (sizeof(NAMES) / sizeof(NAMES[0])); index++) {\n        libGL = dlopen(NAMES[index], RTLD_NOW | RTLD_GLOBAL);\n\n        if(libGL != NULL) {\n#ifdef __APPLE__\n            return 1;\n#else\n            gladGetProcAddressPtr = (PFNGLXGETPROCADDRESSPROC_PRIVATE)dlsym(libGL,\n                \"glXGetProcAddressARB\");\n            return gladGetProcAddressPtr != NULL;\n#endif\n        }\n    }\n\n    return 0;\n}\n\nstatic\nvoid close_gl() {\n    if(libGL != NULL) {\n        dlclose(libGL);\n        libGL = NULL;\n    }\n}\n#endif\n\nstatic\nvoid* get_proc(const char *namez) {\n    void* result = NULL;\n    if(libGL == NULL) return NULL;\n\n#ifndef __APPLE__\n    if(gladGetProcAddressPtr != NULL) {\n        result = gladGetProcAddressPtr(namez);\n    }\n#endif\n    if(result == NULL) {\n#ifdef _WIN32\n        result = (void*)GetProcAddress(libGL, namez);\n#else\n        result = dlsym(libGL, namez);\n#endif\n    }\n\n    return result;\n}\n\nint gladLoadWGL(HDC hdc) {\n    if(open_gl()) {\n        gladLoadWGLLoader((GLADloadproc)get_proc, hdc);\n        close_gl();\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic HDC GLADWGLhdc = INVALID_HANDLE_VALUE;\n\nstatic int has_ext(const char *ext) {\n    const char *terminator;\n    const char *loc;\n    const char *extensions;\n\n    if(wglGetExtensionsStringEXT == NULL && wglGetExtensionsStringARB == NULL)\n        return 0;\n\n    if(wglGetExtensionsStringARB == NULL || GLADWGLhdc == INVALID_HANDLE_VALUE)\n        extensions = wglGetExtensionsStringEXT();\n    else\n        extensions = wglGetExtensionsStringARB(GLADWGLhdc);\n\n    if(extensions == NULL || ext == NULL)\n        return 0;\n\n    while(1) {\n        loc = strstr(extensions, ext);\n        if(loc == NULL)\n            break;\n\n        terminator = loc + strlen(ext);\n        if((loc == extensions || *(loc - 1) == ' ') &&\n            (*terminator == ' ' || *terminator == '\\0'))\n        {\n            return 1;\n        }\n        extensions = terminator;\n    }\n\n    return 0;\n}\nint GLAD_WGL_VERSION_1_0;\nint GLAD_WGL_NV_multisample_coverage;\nint GLAD_WGL_I3D_image_buffer;\nint GLAD_WGL_I3D_swap_frame_usage;\nint GLAD_WGL_NV_DX_interop2;\nint GLAD_WGL_NV_float_buffer;\nint GLAD_WGL_NV_delay_before_swap;\nint GLAD_WGL_OML_sync_control;\nint GLAD_WGL_ARB_pixel_format_float;\nint GLAD_WGL_ARB_create_context;\nint GLAD_WGL_NV_swap_group;\nint GLAD_WGL_NV_gpu_affinity;\nint GLAD_WGL_EXT_pixel_format;\nint GLAD_WGL_ARB_extensions_string;\nint GLAD_WGL_NV_video_capture;\nint GLAD_WGL_NV_render_texture_rectangle;\nint GLAD_WGL_EXT_create_context_es_profile;\nint GLAD_WGL_ARB_robustness_share_group_isolation;\nint GLAD_WGL_ARB_render_texture;\nint GLAD_WGL_EXT_depth_float;\nint GLAD_WGL_EXT_swap_control_tear;\nint GLAD_WGL_ARB_pixel_format;\nint GLAD_WGL_ARB_multisample;\nint GLAD_WGL_I3D_genlock;\nint GLAD_WGL_NV_DX_interop;\nint GLAD_WGL_3DL_stereo_control;\nint GLAD_WGL_EXT_pbuffer;\nint GLAD_WGL_EXT_display_color_table;\nint GLAD_WGL_NV_video_output;\nint GLAD_WGL_ARB_robustness_application_isolation;\nint GLAD_WGL_3DFX_multisample;\nint GLAD_WGL_I3D_gamma;\nint GLAD_WGL_ARB_framebuffer_sRGB;\nint GLAD_WGL_NV_copy_image;\nint GLAD_WGL_EXT_framebuffer_sRGB;\nint GLAD_WGL_NV_present_video;\nint GLAD_WGL_EXT_create_context_es2_profile;\nint GLAD_WGL_ARB_create_context_robustness;\nint GLAD_WGL_ARB_make_current_read;\nint GLAD_WGL_EXT_multisample;\nint GLAD_WGL_EXT_extensions_string;\nint GLAD_WGL_NV_render_depth_texture;\nint GLAD_WGL_ATI_pixel_format_float;\nint GLAD_WGL_ARB_create_context_profile;\nint GLAD_WGL_EXT_swap_control;\nint GLAD_WGL_I3D_digital_video_control;\nint GLAD_WGL_ARB_pbuffer;\nint GLAD_WGL_NV_vertex_array_range;\nint GLAD_WGL_AMD_gpu_association;\nint GLAD_WGL_EXT_pixel_format_packed_float;\nint GLAD_WGL_EXT_make_current_read;\nint GLAD_WGL_I3D_swap_frame_lock;\nint GLAD_WGL_ARB_buffer_region;\nPFNWGLCREATEIMAGEBUFFERI3DPROC glad_wglCreateImageBufferI3D;\nPFNWGLDESTROYIMAGEBUFFERI3DPROC glad_wglDestroyImageBufferI3D;\nPFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC glad_wglAssociateImageBufferEventsI3D;\nPFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC glad_wglReleaseImageBufferEventsI3D;\nPFNWGLGETFRAMEUSAGEI3DPROC glad_wglGetFrameUsageI3D;\nPFNWGLBEGINFRAMETRACKINGI3DPROC glad_wglBeginFrameTrackingI3D;\nPFNWGLENDFRAMETRACKINGI3DPROC glad_wglEndFrameTrackingI3D;\nPFNWGLQUERYFRAMETRACKINGI3DPROC glad_wglQueryFrameTrackingI3D;\nPFNWGLDELAYBEFORESWAPNVPROC glad_wglDelayBeforeSwapNV;\nPFNWGLGETSYNCVALUESOMLPROC glad_wglGetSyncValuesOML;\nPFNWGLGETMSCRATEOMLPROC glad_wglGetMscRateOML;\nPFNWGLSWAPBUFFERSMSCOMLPROC glad_wglSwapBuffersMscOML;\nPFNWGLSWAPLAYERBUFFERSMSCOMLPROC glad_wglSwapLayerBuffersMscOML;\nPFNWGLWAITFORMSCOMLPROC glad_wglWaitForMscOML;\nPFNWGLWAITFORSBCOMLPROC glad_wglWaitForSbcOML;\nPFNWGLCREATECONTEXTATTRIBSARBPROC glad_wglCreateContextAttribsARB;\nPFNWGLJOINSWAPGROUPNVPROC glad_wglJoinSwapGroupNV;\nPFNWGLBINDSWAPBARRIERNVPROC glad_wglBindSwapBarrierNV;\nPFNWGLQUERYSWAPGROUPNVPROC glad_wglQuerySwapGroupNV;\nPFNWGLQUERYMAXSWAPGROUPSNVPROC glad_wglQueryMaxSwapGroupsNV;\nPFNWGLQUERYFRAMECOUNTNVPROC glad_wglQueryFrameCountNV;\nPFNWGLRESETFRAMECOUNTNVPROC glad_wglResetFrameCountNV;\nPFNWGLENUMGPUSNVPROC glad_wglEnumGpusNV;\nPFNWGLENUMGPUDEVICESNVPROC glad_wglEnumGpuDevicesNV;\nPFNWGLCREATEAFFINITYDCNVPROC glad_wglCreateAffinityDCNV;\nPFNWGLENUMGPUSFROMAFFINITYDCNVPROC glad_wglEnumGpusFromAffinityDCNV;\nPFNWGLDELETEDCNVPROC glad_wglDeleteDCNV;\nPFNWGLGETPIXELFORMATATTRIBIVEXTPROC glad_wglGetPixelFormatAttribivEXT;\nPFNWGLGETPIXELFORMATATTRIBFVEXTPROC glad_wglGetPixelFormatAttribfvEXT;\nPFNWGLCHOOSEPIXELFORMATEXTPROC glad_wglChoosePixelFormatEXT;\nPFNWGLGETEXTENSIONSSTRINGARBPROC glad_wglGetExtensionsStringARB;\nPFNWGLBINDVIDEOCAPTUREDEVICENVPROC glad_wglBindVideoCaptureDeviceNV;\nPFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC glad_wglEnumerateVideoCaptureDevicesNV;\nPFNWGLLOCKVIDEOCAPTUREDEVICENVPROC glad_wglLockVideoCaptureDeviceNV;\nPFNWGLQUERYVIDEOCAPTUREDEVICENVPROC glad_wglQueryVideoCaptureDeviceNV;\nPFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC glad_wglReleaseVideoCaptureDeviceNV;\nPFNWGLBINDTEXIMAGEARBPROC glad_wglBindTexImageARB;\nPFNWGLRELEASETEXIMAGEARBPROC glad_wglReleaseTexImageARB;\nPFNWGLSETPBUFFERATTRIBARBPROC glad_wglSetPbufferAttribARB;\nPFNWGLGETPIXELFORMATATTRIBIVARBPROC glad_wglGetPixelFormatAttribivARB;\nPFNWGLGETPIXELFORMATATTRIBFVARBPROC glad_wglGetPixelFormatAttribfvARB;\nPFNWGLCHOOSEPIXELFORMATARBPROC glad_wglChoosePixelFormatARB;\nPFNWGLENABLEGENLOCKI3DPROC glad_wglEnableGenlockI3D;\nPFNWGLDISABLEGENLOCKI3DPROC glad_wglDisableGenlockI3D;\nPFNWGLISENABLEDGENLOCKI3DPROC glad_wglIsEnabledGenlockI3D;\nPFNWGLGENLOCKSOURCEI3DPROC glad_wglGenlockSourceI3D;\nPFNWGLGETGENLOCKSOURCEI3DPROC glad_wglGetGenlockSourceI3D;\nPFNWGLGENLOCKSOURCEEDGEI3DPROC glad_wglGenlockSourceEdgeI3D;\nPFNWGLGETGENLOCKSOURCEEDGEI3DPROC glad_wglGetGenlockSourceEdgeI3D;\nPFNWGLGENLOCKSAMPLERATEI3DPROC glad_wglGenlockSampleRateI3D;\nPFNWGLGETGENLOCKSAMPLERATEI3DPROC glad_wglGetGenlockSampleRateI3D;\nPFNWGLGENLOCKSOURCEDELAYI3DPROC glad_wglGenlockSourceDelayI3D;\nPFNWGLGETGENLOCKSOURCEDELAYI3DPROC glad_wglGetGenlockSourceDelayI3D;\nPFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC glad_wglQueryGenlockMaxSourceDelayI3D;\nPFNWGLDXSETRESOURCESHAREHANDLENVPROC glad_wglDXSetResourceShareHandleNV;\nPFNWGLDXOPENDEVICENVPROC glad_wglDXOpenDeviceNV;\nPFNWGLDXCLOSEDEVICENVPROC glad_wglDXCloseDeviceNV;\nPFNWGLDXREGISTEROBJECTNVPROC glad_wglDXRegisterObjectNV;\nPFNWGLDXUNREGISTEROBJECTNVPROC glad_wglDXUnregisterObjectNV;\nPFNWGLDXOBJECTACCESSNVPROC glad_wglDXObjectAccessNV;\nPFNWGLDXLOCKOBJECTSNVPROC glad_wglDXLockObjectsNV;\nPFNWGLDXUNLOCKOBJECTSNVPROC glad_wglDXUnlockObjectsNV;\nPFNWGLSETSTEREOEMITTERSTATE3DLPROC glad_wglSetStereoEmitterState3DL;\nPFNWGLCREATEPBUFFEREXTPROC glad_wglCreatePbufferEXT;\nPFNWGLGETPBUFFERDCEXTPROC glad_wglGetPbufferDCEXT;\nPFNWGLRELEASEPBUFFERDCEXTPROC glad_wglReleasePbufferDCEXT;\nPFNWGLDESTROYPBUFFEREXTPROC glad_wglDestroyPbufferEXT;\nPFNWGLQUERYPBUFFEREXTPROC glad_wglQueryPbufferEXT;\nPFNWGLCREATEDISPLAYCOLORTABLEEXTPROC glad_wglCreateDisplayColorTableEXT;\nPFNWGLLOADDISPLAYCOLORTABLEEXTPROC glad_wglLoadDisplayColorTableEXT;\nPFNWGLBINDDISPLAYCOLORTABLEEXTPROC glad_wglBindDisplayColorTableEXT;\nPFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC glad_wglDestroyDisplayColorTableEXT;\nPFNWGLGETVIDEODEVICENVPROC glad_wglGetVideoDeviceNV;\nPFNWGLRELEASEVIDEODEVICENVPROC glad_wglReleaseVideoDeviceNV;\nPFNWGLBINDVIDEOIMAGENVPROC glad_wglBindVideoImageNV;\nPFNWGLRELEASEVIDEOIMAGENVPROC glad_wglReleaseVideoImageNV;\nPFNWGLSENDPBUFFERTOVIDEONVPROC glad_wglSendPbufferToVideoNV;\nPFNWGLGETVIDEOINFONVPROC glad_wglGetVideoInfoNV;\nPFNWGLGETGAMMATABLEPARAMETERSI3DPROC glad_wglGetGammaTableParametersI3D;\nPFNWGLSETGAMMATABLEPARAMETERSI3DPROC glad_wglSetGammaTableParametersI3D;\nPFNWGLGETGAMMATABLEI3DPROC glad_wglGetGammaTableI3D;\nPFNWGLSETGAMMATABLEI3DPROC glad_wglSetGammaTableI3D;\nPFNWGLCOPYIMAGESUBDATANVPROC glad_wglCopyImageSubDataNV;\nPFNWGLENUMERATEVIDEODEVICESNVPROC glad_wglEnumerateVideoDevicesNV;\nPFNWGLBINDVIDEODEVICENVPROC glad_wglBindVideoDeviceNV;\nPFNWGLQUERYCURRENTCONTEXTNVPROC glad_wglQueryCurrentContextNV;\nPFNWGLMAKECONTEXTCURRENTARBPROC glad_wglMakeContextCurrentARB;\nPFNWGLGETCURRENTREADDCARBPROC glad_wglGetCurrentReadDCARB;\nPFNWGLGETEXTENSIONSSTRINGEXTPROC glad_wglGetExtensionsStringEXT;\nPFNWGLSWAPINTERVALEXTPROC glad_wglSwapIntervalEXT;\nPFNWGLGETSWAPINTERVALEXTPROC glad_wglGetSwapIntervalEXT;\nPFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC glad_wglGetDigitalVideoParametersI3D;\nPFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC glad_wglSetDigitalVideoParametersI3D;\nPFNWGLCREATEPBUFFERARBPROC glad_wglCreatePbufferARB;\nPFNWGLGETPBUFFERDCARBPROC glad_wglGetPbufferDCARB;\nPFNWGLRELEASEPBUFFERDCARBPROC glad_wglReleasePbufferDCARB;\nPFNWGLDESTROYPBUFFERARBPROC glad_wglDestroyPbufferARB;\nPFNWGLQUERYPBUFFERARBPROC glad_wglQueryPbufferARB;\nPFNWGLALLOCATEMEMORYNVPROC glad_wglAllocateMemoryNV;\nPFNWGLFREEMEMORYNVPROC glad_wglFreeMemoryNV;\nPFNWGLGETGPUIDSAMDPROC glad_wglGetGPUIDsAMD;\nPFNWGLGETGPUINFOAMDPROC glad_wglGetGPUInfoAMD;\nPFNWGLGETCONTEXTGPUIDAMDPROC glad_wglGetContextGPUIDAMD;\nPFNWGLCREATEASSOCIATEDCONTEXTAMDPROC glad_wglCreateAssociatedContextAMD;\nPFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC glad_wglCreateAssociatedContextAttribsAMD;\nPFNWGLDELETEASSOCIATEDCONTEXTAMDPROC glad_wglDeleteAssociatedContextAMD;\nPFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC glad_wglMakeAssociatedContextCurrentAMD;\nPFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC glad_wglGetCurrentAssociatedContextAMD;\nPFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC glad_wglBlitContextFramebufferAMD;\nPFNWGLMAKECONTEXTCURRENTEXTPROC glad_wglMakeContextCurrentEXT;\nPFNWGLGETCURRENTREADDCEXTPROC glad_wglGetCurrentReadDCEXT;\nPFNWGLENABLEFRAMELOCKI3DPROC glad_wglEnableFrameLockI3D;\nPFNWGLDISABLEFRAMELOCKI3DPROC glad_wglDisableFrameLockI3D;\nPFNWGLISENABLEDFRAMELOCKI3DPROC glad_wglIsEnabledFrameLockI3D;\nPFNWGLQUERYFRAMELOCKMASTERI3DPROC glad_wglQueryFrameLockMasterI3D;\nPFNWGLCREATEBUFFERREGIONARBPROC glad_wglCreateBufferRegionARB;\nPFNWGLDELETEBUFFERREGIONARBPROC glad_wglDeleteBufferRegionARB;\nPFNWGLSAVEBUFFERREGIONARBPROC glad_wglSaveBufferRegionARB;\nPFNWGLRESTOREBUFFERREGIONARBPROC glad_wglRestoreBufferRegionARB;\nstatic void load_WGL_I3D_image_buffer(GLADloadproc load) {\n\tif(!GLAD_WGL_I3D_image_buffer) return;\n\tglad_wglCreateImageBufferI3D = (PFNWGLCREATEIMAGEBUFFERI3DPROC)load(\"wglCreateImageBufferI3D\");\n\tglad_wglDestroyImageBufferI3D = (PFNWGLDESTROYIMAGEBUFFERI3DPROC)load(\"wglDestroyImageBufferI3D\");\n\tglad_wglAssociateImageBufferEventsI3D = (PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC)load(\"wglAssociateImageBufferEventsI3D\");\n\tglad_wglReleaseImageBufferEventsI3D = (PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC)load(\"wglReleaseImageBufferEventsI3D\");\n}\nstatic void load_WGL_I3D_swap_frame_usage(GLADloadproc load) {\n\tif(!GLAD_WGL_I3D_swap_frame_usage) return;\n\tglad_wglGetFrameUsageI3D = (PFNWGLGETFRAMEUSAGEI3DPROC)load(\"wglGetFrameUsageI3D\");\n\tglad_wglBeginFrameTrackingI3D = (PFNWGLBEGINFRAMETRACKINGI3DPROC)load(\"wglBeginFrameTrackingI3D\");\n\tglad_wglEndFrameTrackingI3D = (PFNWGLENDFRAMETRACKINGI3DPROC)load(\"wglEndFrameTrackingI3D\");\n\tglad_wglQueryFrameTrackingI3D = (PFNWGLQUERYFRAMETRACKINGI3DPROC)load(\"wglQueryFrameTrackingI3D\");\n}\nstatic void load_WGL_NV_delay_before_swap(GLADloadproc load) {\n\tif(!GLAD_WGL_NV_delay_before_swap) return;\n\tglad_wglDelayBeforeSwapNV = (PFNWGLDELAYBEFORESWAPNVPROC)load(\"wglDelayBeforeSwapNV\");\n}\nstatic void load_WGL_OML_sync_control(GLADloadproc load) {\n\tif(!GLAD_WGL_OML_sync_control) return;\n\tglad_wglGetSyncValuesOML = (PFNWGLGETSYNCVALUESOMLPROC)load(\"wglGetSyncValuesOML\");\n\tglad_wglGetMscRateOML = (PFNWGLGETMSCRATEOMLPROC)load(\"wglGetMscRateOML\");\n\tglad_wglSwapBuffersMscOML = (PFNWGLSWAPBUFFERSMSCOMLPROC)load(\"wglSwapBuffersMscOML\");\n\tglad_wglSwapLayerBuffersMscOML = (PFNWGLSWAPLAYERBUFFERSMSCOMLPROC)load(\"wglSwapLayerBuffersMscOML\");\n\tglad_wglWaitForMscOML = (PFNWGLWAITFORMSCOMLPROC)load(\"wglWaitForMscOML\");\n\tglad_wglWaitForSbcOML = (PFNWGLWAITFORSBCOMLPROC)load(\"wglWaitForSbcOML\");\n}\nstatic void load_WGL_ARB_create_context(GLADloadproc load) {\n\tif(!GLAD_WGL_ARB_create_context) return;\n\tglad_wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)load(\"wglCreateContextAttribsARB\");\n}\nstatic void load_WGL_NV_swap_group(GLADloadproc load) {\n\tif(!GLAD_WGL_NV_swap_group) return;\n\tglad_wglJoinSwapGroupNV = (PFNWGLJOINSWAPGROUPNVPROC)load(\"wglJoinSwapGroupNV\");\n\tglad_wglBindSwapBarrierNV = (PFNWGLBINDSWAPBARRIERNVPROC)load(\"wglBindSwapBarrierNV\");\n\tglad_wglQuerySwapGroupNV = (PFNWGLQUERYSWAPGROUPNVPROC)load(\"wglQuerySwapGroupNV\");\n\tglad_wglQueryMaxSwapGroupsNV = (PFNWGLQUERYMAXSWAPGROUPSNVPROC)load(\"wglQueryMaxSwapGroupsNV\");\n\tglad_wglQueryFrameCountNV = (PFNWGLQUERYFRAMECOUNTNVPROC)load(\"wglQueryFrameCountNV\");\n\tglad_wglResetFrameCountNV = (PFNWGLRESETFRAMECOUNTNVPROC)load(\"wglResetFrameCountNV\");\n}\nstatic void load_WGL_NV_gpu_affinity(GLADloadproc load) {\n\tif(!GLAD_WGL_NV_gpu_affinity) return;\n\tglad_wglEnumGpusNV = (PFNWGLENUMGPUSNVPROC)load(\"wglEnumGpusNV\");\n\tglad_wglEnumGpuDevicesNV = (PFNWGLENUMGPUDEVICESNVPROC)load(\"wglEnumGpuDevicesNV\");\n\tglad_wglCreateAffinityDCNV = (PFNWGLCREATEAFFINITYDCNVPROC)load(\"wglCreateAffinityDCNV\");\n\tglad_wglEnumGpusFromAffinityDCNV = (PFNWGLENUMGPUSFROMAFFINITYDCNVPROC)load(\"wglEnumGpusFromAffinityDCNV\");\n\tglad_wglDeleteDCNV = (PFNWGLDELETEDCNVPROC)load(\"wglDeleteDCNV\");\n}\nstatic void load_WGL_EXT_pixel_format(GLADloadproc load) {\n\tif(!GLAD_WGL_EXT_pixel_format) return;\n\tglad_wglGetPixelFormatAttribivEXT = (PFNWGLGETPIXELFORMATATTRIBIVEXTPROC)load(\"wglGetPixelFormatAttribivEXT\");\n\tglad_wglGetPixelFormatAttribfvEXT = (PFNWGLGETPIXELFORMATATTRIBFVEXTPROC)load(\"wglGetPixelFormatAttribfvEXT\");\n\tglad_wglChoosePixelFormatEXT = (PFNWGLCHOOSEPIXELFORMATEXTPROC)load(\"wglChoosePixelFormatEXT\");\n}\nstatic void load_WGL_ARB_extensions_string(GLADloadproc load) {\n\tif(!GLAD_WGL_ARB_extensions_string) return;\n\tglad_wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)load(\"wglGetExtensionsStringARB\");\n}\nstatic void load_WGL_NV_video_capture(GLADloadproc load) {\n\tif(!GLAD_WGL_NV_video_capture) return;\n\tglad_wglBindVideoCaptureDeviceNV = (PFNWGLBINDVIDEOCAPTUREDEVICENVPROC)load(\"wglBindVideoCaptureDeviceNV\");\n\tglad_wglEnumerateVideoCaptureDevicesNV = (PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC)load(\"wglEnumerateVideoCaptureDevicesNV\");\n\tglad_wglLockVideoCaptureDeviceNV = (PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC)load(\"wglLockVideoCaptureDeviceNV\");\n\tglad_wglQueryVideoCaptureDeviceNV = (PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC)load(\"wglQueryVideoCaptureDeviceNV\");\n\tglad_wglReleaseVideoCaptureDeviceNV = (PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC)load(\"wglReleaseVideoCaptureDeviceNV\");\n}\nstatic void load_WGL_ARB_render_texture(GLADloadproc load) {\n\tif(!GLAD_WGL_ARB_render_texture) return;\n\tglad_wglBindTexImageARB = (PFNWGLBINDTEXIMAGEARBPROC)load(\"wglBindTexImageARB\");\n\tglad_wglReleaseTexImageARB = (PFNWGLRELEASETEXIMAGEARBPROC)load(\"wglReleaseTexImageARB\");\n\tglad_wglSetPbufferAttribARB = (PFNWGLSETPBUFFERATTRIBARBPROC)load(\"wglSetPbufferAttribARB\");\n}\nstatic void load_WGL_ARB_pixel_format(GLADloadproc load) {\n\tif(!GLAD_WGL_ARB_pixel_format) return;\n\tglad_wglGetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)load(\"wglGetPixelFormatAttribivARB\");\n\tglad_wglGetPixelFormatAttribfvARB = (PFNWGLGETPIXELFORMATATTRIBFVARBPROC)load(\"wglGetPixelFormatAttribfvARB\");\n\tglad_wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)load(\"wglChoosePixelFormatARB\");\n}\nstatic void load_WGL_I3D_genlock(GLADloadproc load) {\n\tif(!GLAD_WGL_I3D_genlock) return;\n\tglad_wglEnableGenlockI3D = (PFNWGLENABLEGENLOCKI3DPROC)load(\"wglEnableGenlockI3D\");\n\tglad_wglDisableGenlockI3D = (PFNWGLDISABLEGENLOCKI3DPROC)load(\"wglDisableGenlockI3D\");\n\tglad_wglIsEnabledGenlockI3D = (PFNWGLISENABLEDGENLOCKI3DPROC)load(\"wglIsEnabledGenlockI3D\");\n\tglad_wglGenlockSourceI3D = (PFNWGLGENLOCKSOURCEI3DPROC)load(\"wglGenlockSourceI3D\");\n\tglad_wglGetGenlockSourceI3D = (PFNWGLGETGENLOCKSOURCEI3DPROC)load(\"wglGetGenlockSourceI3D\");\n\tglad_wglGenlockSourceEdgeI3D = (PFNWGLGENLOCKSOURCEEDGEI3DPROC)load(\"wglGenlockSourceEdgeI3D\");\n\tglad_wglGetGenlockSourceEdgeI3D = (PFNWGLGETGENLOCKSOURCEEDGEI3DPROC)load(\"wglGetGenlockSourceEdgeI3D\");\n\tglad_wglGenlockSampleRateI3D = (PFNWGLGENLOCKSAMPLERATEI3DPROC)load(\"wglGenlockSampleRateI3D\");\n\tglad_wglGetGenlockSampleRateI3D = (PFNWGLGETGENLOCKSAMPLERATEI3DPROC)load(\"wglGetGenlockSampleRateI3D\");\n\tglad_wglGenlockSourceDelayI3D = (PFNWGLGENLOCKSOURCEDELAYI3DPROC)load(\"wglGenlockSourceDelayI3D\");\n\tglad_wglGetGenlockSourceDelayI3D = (PFNWGLGETGENLOCKSOURCEDELAYI3DPROC)load(\"wglGetGenlockSourceDelayI3D\");\n\tglad_wglQueryGenlockMaxSourceDelayI3D = (PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC)load(\"wglQueryGenlockMaxSourceDelayI3D\");\n}\nstatic void load_WGL_NV_DX_interop(GLADloadproc load) {\n\tif(!GLAD_WGL_NV_DX_interop) return;\n\tglad_wglDXSetResourceShareHandleNV = (PFNWGLDXSETRESOURCESHAREHANDLENVPROC)load(\"wglDXSetResourceShareHandleNV\");\n\tglad_wglDXOpenDeviceNV = (PFNWGLDXOPENDEVICENVPROC)load(\"wglDXOpenDeviceNV\");\n\tglad_wglDXCloseDeviceNV = (PFNWGLDXCLOSEDEVICENVPROC)load(\"wglDXCloseDeviceNV\");\n\tglad_wglDXRegisterObjectNV = (PFNWGLDXREGISTEROBJECTNVPROC)load(\"wglDXRegisterObjectNV\");\n\tglad_wglDXUnregisterObjectNV = (PFNWGLDXUNREGISTEROBJECTNVPROC)load(\"wglDXUnregisterObjectNV\");\n\tglad_wglDXObjectAccessNV = (PFNWGLDXOBJECTACCESSNVPROC)load(\"wglDXObjectAccessNV\");\n\tglad_wglDXLockObjectsNV = (PFNWGLDXLOCKOBJECTSNVPROC)load(\"wglDXLockObjectsNV\");\n\tglad_wglDXUnlockObjectsNV = (PFNWGLDXUNLOCKOBJECTSNVPROC)load(\"wglDXUnlockObjectsNV\");\n}\nstatic void load_WGL_3DL_stereo_control(GLADloadproc load) {\n\tif(!GLAD_WGL_3DL_stereo_control) return;\n\tglad_wglSetStereoEmitterState3DL = (PFNWGLSETSTEREOEMITTERSTATE3DLPROC)load(\"wglSetStereoEmitterState3DL\");\n}\nstatic void load_WGL_EXT_pbuffer(GLADloadproc load) {\n\tif(!GLAD_WGL_EXT_pbuffer) return;\n\tglad_wglCreatePbufferEXT = (PFNWGLCREATEPBUFFEREXTPROC)load(\"wglCreatePbufferEXT\");\n\tglad_wglGetPbufferDCEXT = (PFNWGLGETPBUFFERDCEXTPROC)load(\"wglGetPbufferDCEXT\");\n\tglad_wglReleasePbufferDCEXT = (PFNWGLRELEASEPBUFFERDCEXTPROC)load(\"wglReleasePbufferDCEXT\");\n\tglad_wglDestroyPbufferEXT = (PFNWGLDESTROYPBUFFEREXTPROC)load(\"wglDestroyPbufferEXT\");\n\tglad_wglQueryPbufferEXT = (PFNWGLQUERYPBUFFEREXTPROC)load(\"wglQueryPbufferEXT\");\n}\nstatic void load_WGL_EXT_display_color_table(GLADloadproc load) {\n\tif(!GLAD_WGL_EXT_display_color_table) return;\n\tglad_wglCreateDisplayColorTableEXT = (PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC)load(\"wglCreateDisplayColorTableEXT\");\n\tglad_wglLoadDisplayColorTableEXT = (PFNWGLLOADDISPLAYCOLORTABLEEXTPROC)load(\"wglLoadDisplayColorTableEXT\");\n\tglad_wglBindDisplayColorTableEXT = (PFNWGLBINDDISPLAYCOLORTABLEEXTPROC)load(\"wglBindDisplayColorTableEXT\");\n\tglad_wglDestroyDisplayColorTableEXT = (PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC)load(\"wglDestroyDisplayColorTableEXT\");\n}\nstatic void load_WGL_NV_video_output(GLADloadproc load) {\n\tif(!GLAD_WGL_NV_video_output) return;\n\tglad_wglGetVideoDeviceNV = (PFNWGLGETVIDEODEVICENVPROC)load(\"wglGetVideoDeviceNV\");\n\tglad_wglReleaseVideoDeviceNV = (PFNWGLRELEASEVIDEODEVICENVPROC)load(\"wglReleaseVideoDeviceNV\");\n\tglad_wglBindVideoImageNV = (PFNWGLBINDVIDEOIMAGENVPROC)load(\"wglBindVideoImageNV\");\n\tglad_wglReleaseVideoImageNV = (PFNWGLRELEASEVIDEOIMAGENVPROC)load(\"wglReleaseVideoImageNV\");\n\tglad_wglSendPbufferToVideoNV = (PFNWGLSENDPBUFFERTOVIDEONVPROC)load(\"wglSendPbufferToVideoNV\");\n\tglad_wglGetVideoInfoNV = (PFNWGLGETVIDEOINFONVPROC)load(\"wglGetVideoInfoNV\");\n}\nstatic void load_WGL_I3D_gamma(GLADloadproc load) {\n\tif(!GLAD_WGL_I3D_gamma) return;\n\tglad_wglGetGammaTableParametersI3D = (PFNWGLGETGAMMATABLEPARAMETERSI3DPROC)load(\"wglGetGammaTableParametersI3D\");\n\tglad_wglSetGammaTableParametersI3D = (PFNWGLSETGAMMATABLEPARAMETERSI3DPROC)load(\"wglSetGammaTableParametersI3D\");\n\tglad_wglGetGammaTableI3D = (PFNWGLGETGAMMATABLEI3DPROC)load(\"wglGetGammaTableI3D\");\n\tglad_wglSetGammaTableI3D = (PFNWGLSETGAMMATABLEI3DPROC)load(\"wglSetGammaTableI3D\");\n}\nstatic void load_WGL_NV_copy_image(GLADloadproc load) {\n\tif(!GLAD_WGL_NV_copy_image) return;\n\tglad_wglCopyImageSubDataNV = (PFNWGLCOPYIMAGESUBDATANVPROC)load(\"wglCopyImageSubDataNV\");\n}\nstatic void load_WGL_NV_present_video(GLADloadproc load) {\n\tif(!GLAD_WGL_NV_present_video) return;\n\tglad_wglEnumerateVideoDevicesNV = (PFNWGLENUMERATEVIDEODEVICESNVPROC)load(\"wglEnumerateVideoDevicesNV\");\n\tglad_wglBindVideoDeviceNV = (PFNWGLBINDVIDEODEVICENVPROC)load(\"wglBindVideoDeviceNV\");\n\tglad_wglQueryCurrentContextNV = (PFNWGLQUERYCURRENTCONTEXTNVPROC)load(\"wglQueryCurrentContextNV\");\n}\nstatic void load_WGL_ARB_make_current_read(GLADloadproc load) {\n\tif(!GLAD_WGL_ARB_make_current_read) return;\n\tglad_wglMakeContextCurrentARB = (PFNWGLMAKECONTEXTCURRENTARBPROC)load(\"wglMakeContextCurrentARB\");\n\tglad_wglGetCurrentReadDCARB = (PFNWGLGETCURRENTREADDCARBPROC)load(\"wglGetCurrentReadDCARB\");\n}\nstatic void load_WGL_EXT_extensions_string(GLADloadproc load) {\n\tif(!GLAD_WGL_EXT_extensions_string) return;\n\tglad_wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)load(\"wglGetExtensionsStringEXT\");\n}\nstatic void load_WGL_EXT_swap_control(GLADloadproc load) {\n\tif(!GLAD_WGL_EXT_swap_control) return;\n\tglad_wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)load(\"wglSwapIntervalEXT\");\n\tglad_wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)load(\"wglGetSwapIntervalEXT\");\n}\nstatic void load_WGL_I3D_digital_video_control(GLADloadproc load) {\n\tif(!GLAD_WGL_I3D_digital_video_control) return;\n\tglad_wglGetDigitalVideoParametersI3D = (PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC)load(\"wglGetDigitalVideoParametersI3D\");\n\tglad_wglSetDigitalVideoParametersI3D = (PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC)load(\"wglSetDigitalVideoParametersI3D\");\n}\nstatic void load_WGL_ARB_pbuffer(GLADloadproc load) {\n\tif(!GLAD_WGL_ARB_pbuffer) return;\n\tglad_wglCreatePbufferARB = (PFNWGLCREATEPBUFFERARBPROC)load(\"wglCreatePbufferARB\");\n\tglad_wglGetPbufferDCARB = (PFNWGLGETPBUFFERDCARBPROC)load(\"wglGetPbufferDCARB\");\n\tglad_wglReleasePbufferDCARB = (PFNWGLRELEASEPBUFFERDCARBPROC)load(\"wglReleasePbufferDCARB\");\n\tglad_wglDestroyPbufferARB = (PFNWGLDESTROYPBUFFERARBPROC)load(\"wglDestroyPbufferARB\");\n\tglad_wglQueryPbufferARB = (PFNWGLQUERYPBUFFERARBPROC)load(\"wglQueryPbufferARB\");\n}\nstatic void load_WGL_NV_vertex_array_range(GLADloadproc load) {\n\tif(!GLAD_WGL_NV_vertex_array_range) return;\n\tglad_wglAllocateMemoryNV = (PFNWGLALLOCATEMEMORYNVPROC)load(\"wglAllocateMemoryNV\");\n\tglad_wglFreeMemoryNV = (PFNWGLFREEMEMORYNVPROC)load(\"wglFreeMemoryNV\");\n}\nstatic void load_WGL_AMD_gpu_association(GLADloadproc load) {\n\tif(!GLAD_WGL_AMD_gpu_association) return;\n\tglad_wglGetGPUIDsAMD = (PFNWGLGETGPUIDSAMDPROC)load(\"wglGetGPUIDsAMD\");\n\tglad_wglGetGPUInfoAMD = (PFNWGLGETGPUINFOAMDPROC)load(\"wglGetGPUInfoAMD\");\n\tglad_wglGetContextGPUIDAMD = (PFNWGLGETCONTEXTGPUIDAMDPROC)load(\"wglGetContextGPUIDAMD\");\n\tglad_wglCreateAssociatedContextAMD = (PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC)load(\"wglCreateAssociatedContextAMD\");\n\tglad_wglCreateAssociatedContextAttribsAMD = (PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC)load(\"wglCreateAssociatedContextAttribsAMD\");\n\tglad_wglDeleteAssociatedContextAMD = (PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC)load(\"wglDeleteAssociatedContextAMD\");\n\tglad_wglMakeAssociatedContextCurrentAMD = (PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC)load(\"wglMakeAssociatedContextCurrentAMD\");\n\tglad_wglGetCurrentAssociatedContextAMD = (PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC)load(\"wglGetCurrentAssociatedContextAMD\");\n\tglad_wglBlitContextFramebufferAMD = (PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC)load(\"wglBlitContextFramebufferAMD\");\n}\nstatic void load_WGL_EXT_make_current_read(GLADloadproc load) {\n\tif(!GLAD_WGL_EXT_make_current_read) return;\n\tglad_wglMakeContextCurrentEXT = (PFNWGLMAKECONTEXTCURRENTEXTPROC)load(\"wglMakeContextCurrentEXT\");\n\tglad_wglGetCurrentReadDCEXT = (PFNWGLGETCURRENTREADDCEXTPROC)load(\"wglGetCurrentReadDCEXT\");\n}\nstatic void load_WGL_I3D_swap_frame_lock(GLADloadproc load) {\n\tif(!GLAD_WGL_I3D_swap_frame_lock) return;\n\tglad_wglEnableFrameLockI3D = (PFNWGLENABLEFRAMELOCKI3DPROC)load(\"wglEnableFrameLockI3D\");\n\tglad_wglDisableFrameLockI3D = (PFNWGLDISABLEFRAMELOCKI3DPROC)load(\"wglDisableFrameLockI3D\");\n\tglad_wglIsEnabledFrameLockI3D = (PFNWGLISENABLEDFRAMELOCKI3DPROC)load(\"wglIsEnabledFrameLockI3D\");\n\tglad_wglQueryFrameLockMasterI3D = (PFNWGLQUERYFRAMELOCKMASTERI3DPROC)load(\"wglQueryFrameLockMasterI3D\");\n}\nstatic void load_WGL_ARB_buffer_region(GLADloadproc load) {\n\tif(!GLAD_WGL_ARB_buffer_region) return;\n\tglad_wglCreateBufferRegionARB = (PFNWGLCREATEBUFFERREGIONARBPROC)load(\"wglCreateBufferRegionARB\");\n\tglad_wglDeleteBufferRegionARB = (PFNWGLDELETEBUFFERREGIONARBPROC)load(\"wglDeleteBufferRegionARB\");\n\tglad_wglSaveBufferRegionARB = (PFNWGLSAVEBUFFERREGIONARBPROC)load(\"wglSaveBufferRegionARB\");\n\tglad_wglRestoreBufferRegionARB = (PFNWGLRESTOREBUFFERREGIONARBPROC)load(\"wglRestoreBufferRegionARB\");\n}\nstatic void find_extensionsWGL(void) {\n\tGLAD_WGL_NV_multisample_coverage = has_ext(\"WGL_NV_multisample_coverage\");\n\tGLAD_WGL_I3D_image_buffer = has_ext(\"WGL_I3D_image_buffer\");\n\tGLAD_WGL_I3D_swap_frame_usage = has_ext(\"WGL_I3D_swap_frame_usage\");\n\tGLAD_WGL_NV_DX_interop2 = has_ext(\"WGL_NV_DX_interop2\");\n\tGLAD_WGL_NV_float_buffer = has_ext(\"WGL_NV_float_buffer\");\n\tGLAD_WGL_NV_delay_before_swap = has_ext(\"WGL_NV_delay_before_swap\");\n\tGLAD_WGL_OML_sync_control = has_ext(\"WGL_OML_sync_control\");\n\tGLAD_WGL_ARB_pixel_format_float = has_ext(\"WGL_ARB_pixel_format_float\");\n\tGLAD_WGL_ARB_create_context = has_ext(\"WGL_ARB_create_context\");\n\tGLAD_WGL_NV_swap_group = has_ext(\"WGL_NV_swap_group\");\n\tGLAD_WGL_NV_gpu_affinity = has_ext(\"WGL_NV_gpu_affinity\");\n\tGLAD_WGL_EXT_pixel_format = has_ext(\"WGL_EXT_pixel_format\");\n\tGLAD_WGL_ARB_extensions_string = has_ext(\"WGL_ARB_extensions_string\");\n\tGLAD_WGL_NV_video_capture = has_ext(\"WGL_NV_video_capture\");\n\tGLAD_WGL_NV_render_texture_rectangle = has_ext(\"WGL_NV_render_texture_rectangle\");\n\tGLAD_WGL_EXT_create_context_es_profile = has_ext(\"WGL_EXT_create_context_es_profile\");\n\tGLAD_WGL_ARB_robustness_share_group_isolation = has_ext(\"WGL_ARB_robustness_share_group_isolation\");\n\tGLAD_WGL_ARB_render_texture = has_ext(\"WGL_ARB_render_texture\");\n\tGLAD_WGL_EXT_depth_float = has_ext(\"WGL_EXT_depth_float\");\n\tGLAD_WGL_EXT_swap_control_tear = has_ext(\"WGL_EXT_swap_control_tear\");\n\tGLAD_WGL_ARB_pixel_format = has_ext(\"WGL_ARB_pixel_format\");\n\tGLAD_WGL_ARB_multisample = has_ext(\"WGL_ARB_multisample\");\n\tGLAD_WGL_I3D_genlock = has_ext(\"WGL_I3D_genlock\");\n\tGLAD_WGL_NV_DX_interop = has_ext(\"WGL_NV_DX_interop\");\n\tGLAD_WGL_3DL_stereo_control = has_ext(\"WGL_3DL_stereo_control\");\n\tGLAD_WGL_EXT_pbuffer = has_ext(\"WGL_EXT_pbuffer\");\n\tGLAD_WGL_EXT_display_color_table = has_ext(\"WGL_EXT_display_color_table\");\n\tGLAD_WGL_NV_video_output = has_ext(\"WGL_NV_video_output\");\n\tGLAD_WGL_ARB_robustness_application_isolation = has_ext(\"WGL_ARB_robustness_application_isolation\");\n\tGLAD_WGL_3DFX_multisample = has_ext(\"WGL_3DFX_multisample\");\n\tGLAD_WGL_I3D_gamma = has_ext(\"WGL_I3D_gamma\");\n\tGLAD_WGL_ARB_framebuffer_sRGB = has_ext(\"WGL_ARB_framebuffer_sRGB\");\n\tGLAD_WGL_NV_copy_image = has_ext(\"WGL_NV_copy_image\");\n\tGLAD_WGL_EXT_framebuffer_sRGB = has_ext(\"WGL_EXT_framebuffer_sRGB\");\n\tGLAD_WGL_NV_present_video = has_ext(\"WGL_NV_present_video\");\n\tGLAD_WGL_EXT_create_context_es2_profile = has_ext(\"WGL_EXT_create_context_es2_profile\");\n\tGLAD_WGL_ARB_create_context_robustness = has_ext(\"WGL_ARB_create_context_robustness\");\n\tGLAD_WGL_ARB_make_current_read = has_ext(\"WGL_ARB_make_current_read\");\n\tGLAD_WGL_EXT_multisample = has_ext(\"WGL_EXT_multisample\");\n\tGLAD_WGL_EXT_extensions_string = has_ext(\"WGL_EXT_extensions_string\");\n\tGLAD_WGL_NV_render_depth_texture = has_ext(\"WGL_NV_render_depth_texture\");\n\tGLAD_WGL_ATI_pixel_format_float = has_ext(\"WGL_ATI_pixel_format_float\");\n\tGLAD_WGL_ARB_create_context_profile = has_ext(\"WGL_ARB_create_context_profile\");\n\tGLAD_WGL_EXT_swap_control = has_ext(\"WGL_EXT_swap_control\");\n\tGLAD_WGL_I3D_digital_video_control = has_ext(\"WGL_I3D_digital_video_control\");\n\tGLAD_WGL_ARB_pbuffer = has_ext(\"WGL_ARB_pbuffer\");\n\tGLAD_WGL_NV_vertex_array_range = has_ext(\"WGL_NV_vertex_array_range\");\n\tGLAD_WGL_AMD_gpu_association = has_ext(\"WGL_AMD_gpu_association\");\n\tGLAD_WGL_EXT_pixel_format_packed_float = has_ext(\"WGL_EXT_pixel_format_packed_float\");\n\tGLAD_WGL_EXT_make_current_read = has_ext(\"WGL_EXT_make_current_read\");\n\tGLAD_WGL_I3D_swap_frame_lock = has_ext(\"WGL_I3D_swap_frame_lock\");\n\tGLAD_WGL_ARB_buffer_region = has_ext(\"WGL_ARB_buffer_region\");\n}\n\nstatic void find_coreWGL(HDC hdc) {\n\t//int major = 9;\n\t//int minor = 9;\n\tGLADWGLhdc = hdc;\n}\n\nvoid gladLoadWGLLoader(GLADloadproc load, HDC hdc) {\n\twglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)load(\"wglGetExtensionsStringARB\");\n\twglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)load(\"wglGetExtensionsStringEXT\");\n\tif(wglGetExtensionsStringARB == NULL && wglGetExtensionsStringEXT == NULL) return;\n\tfind_coreWGL(hdc);\n\n\tfind_extensionsWGL();\n\tload_WGL_I3D_image_buffer(load);\n\tload_WGL_I3D_swap_frame_usage(load);\n\tload_WGL_NV_delay_before_swap(load);\n\tload_WGL_OML_sync_control(load);\n\tload_WGL_ARB_create_context(load);\n\tload_WGL_NV_swap_group(load);\n\tload_WGL_NV_gpu_affinity(load);\n\tload_WGL_EXT_pixel_format(load);\n\tload_WGL_ARB_extensions_string(load);\n\tload_WGL_NV_video_capture(load);\n\tload_WGL_ARB_render_texture(load);\n\tload_WGL_ARB_pixel_format(load);\n\tload_WGL_I3D_genlock(load);\n\tload_WGL_NV_DX_interop(load);\n\tload_WGL_3DL_stereo_control(load);\n\tload_WGL_EXT_pbuffer(load);\n\tload_WGL_EXT_display_color_table(load);\n\tload_WGL_NV_video_output(load);\n\tload_WGL_I3D_gamma(load);\n\tload_WGL_NV_copy_image(load);\n\tload_WGL_NV_present_video(load);\n\tload_WGL_ARB_make_current_read(load);\n\tload_WGL_EXT_extensions_string(load);\n\tload_WGL_EXT_swap_control(load);\n\tload_WGL_I3D_digital_video_control(load);\n\tload_WGL_ARB_pbuffer(load);\n\tload_WGL_NV_vertex_array_range(load);\n\tload_WGL_AMD_gpu_association(load);\n\tload_WGL_EXT_make_current_read(load);\n\tload_WGL_I3D_swap_frame_lock(load);\n\tload_WGL_ARB_buffer_region(load);\n\n\treturn;\n}\n\n"
  },
  {
    "path": "deps/json11/.clang-format",
    "content": "Language: Cpp\nSortIncludes: false\nDisableFormat: true\n"
  },
  {
    "path": "deps/json11/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nadd_library(json11 OBJECT)\nadd_library(OBS::json11 ALIAS json11)\n\ntarget_sources(json11 PRIVATE json11.cpp PUBLIC json11.hpp)\n\ntarget_include_directories(json11 PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}\")\n\nif(\n  (CMAKE_CXX_COMPILER_ID STREQUAL AppleClang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14)\n  OR (CMAKE_CXX_COMPILER_ID STREQUAL Clang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15)\n)\n  target_compile_options(json11 PUBLIC -Wno-unqualified-std-cast-call)\nendif()\n\nset_target_properties(json11 PROPERTIES FOLDER deps)\n"
  },
  {
    "path": "deps/json11/LICENSE.txt",
    "content": "Copyright (c) 2013 Dropbox, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "deps/json11/json11.cpp",
    "content": "/* Copyright (c) 2013 Dropbox, Inc.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#include \"json11.hpp\"\n#include <cassert>\n#include <cmath>\n#include <cstdint>\n#include <cstdlib>\n#include <cstdio>\n#include <limits>\n\nnamespace json11 {\n\nstatic const int max_depth = 200;\n\nusing std::string;\nusing std::vector;\nusing std::map;\nusing std::make_shared;\nusing std::initializer_list;\nusing std::move;\n\n/* Helper for representing null - just a do-nothing struct, plus comparison\n * operators so the helpers in JsonValue work. We can't use nullptr_t because\n * it may not be orderable.\n */\nstruct NullStruct {\n    bool operator==(NullStruct) const { return true; }\n    bool operator<(NullStruct) const { return false; }\n};\n\n/* * * * * * * * * * * * * * * * * * * *\n * Serialization\n */\n\nstatic void dump(NullStruct, string &out) {\n    out += \"null\";\n}\n\nstatic void dump(double value, string &out) {\n    if (std::isfinite(value)) {\n        char buf[32];\n        snprintf(buf, sizeof buf, \"%.17g\", value);\n        out += buf;\n    } else {\n        out += \"null\";\n    }\n}\n\nstatic void dump(int value, string &out) {\n    char buf[32];\n    snprintf(buf, sizeof buf, \"%d\", value);\n    out += buf;\n}\n\nstatic void dump(bool value, string &out) {\n    out += value ? \"true\" : \"false\";\n}\n\nstatic void dump(const string &value, string &out) {\n    out += '\"';\n    for (size_t i = 0; i < value.length(); i++) {\n        const char ch = value[i];\n        if (ch == '\\\\') {\n            out += \"\\\\\\\\\";\n        } else if (ch == '\"') {\n            out += \"\\\\\\\"\";\n        } else if (ch == '\\b') {\n            out += \"\\\\b\";\n        } else if (ch == '\\f') {\n            out += \"\\\\f\";\n        } else if (ch == '\\n') {\n            out += \"\\\\n\";\n        } else if (ch == '\\r') {\n            out += \"\\\\r\";\n        } else if (ch == '\\t') {\n            out += \"\\\\t\";\n        } else if (static_cast<uint8_t>(ch) <= 0x1f) {\n            char buf[8];\n            snprintf(buf, sizeof buf, \"\\\\u%04x\", ch);\n            out += buf;\n        } else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80\n                   && static_cast<uint8_t>(value[i+2]) == 0xa8) {\n            out += \"\\\\u2028\";\n            i += 2;\n        } else if (static_cast<uint8_t>(ch) == 0xe2 && static_cast<uint8_t>(value[i+1]) == 0x80\n                   && static_cast<uint8_t>(value[i+2]) == 0xa9) {\n            out += \"\\\\u2029\";\n            i += 2;\n        } else {\n            out += ch;\n        }\n    }\n    out += '\"';\n}\n\nstatic void dump(const Json::array &values, string &out) {\n    bool first = true;\n    out += \"[\";\n    for (const auto &value : values) {\n        if (!first)\n            out += \", \";\n        value.dump(out);\n        first = false;\n    }\n    out += \"]\";\n}\n\nstatic void dump(const Json::object &values, string &out) {\n    bool first = true;\n    out += \"{\";\n    for (const auto &kv : values) {\n        if (!first)\n            out += \", \";\n        dump(kv.first, out);\n        out += \": \";\n        kv.second.dump(out);\n        first = false;\n    }\n    out += \"}\";\n}\n\nvoid Json::dump(string &out) const {\n    m_ptr->dump(out);\n}\n\n/* * * * * * * * * * * * * * * * * * * *\n * Value wrappers\n */\n\ntemplate <Json::Type tag, typename T>\nclass Value : public JsonValue {\nprotected:\n\n    // Constructors\n    explicit Value(const T &value) : m_value(value) {}\n    explicit Value(T &&value)      : m_value(move(value)) {}\n\n    // Get type tag\n    Json::Type type() const override {\n        return tag;\n    }\n\n    // Comparisons\n    bool equals(const JsonValue * other) const override {\n        return m_value == static_cast<const Value<tag, T> *>(other)->m_value;\n    }\n    bool less(const JsonValue * other) const override {\n        return m_value < static_cast<const Value<tag, T> *>(other)->m_value;\n    }\n\n    const T m_value;\n    void dump(string &out) const override { json11::dump(m_value, out); }\n};\n\nclass JsonDouble final : public Value<Json::NUMBER, double> {\n    double number_value() const override { return m_value; }\n    int int_value() const override { return static_cast<int>(m_value); }\n    bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }\n    bool less(const JsonValue * other)   const override { return m_value <  other->number_value(); }\npublic:\n    explicit JsonDouble(double value) : Value(value) {}\n};\n\nclass JsonInt final : public Value<Json::NUMBER, int> {\n    double number_value() const override { return m_value; }\n    int int_value() const override { return m_value; }\n    bool equals(const JsonValue * other) const override { return m_value == other->number_value(); }\n    bool less(const JsonValue * other)   const override { return m_value <  other->number_value(); }\npublic:\n    explicit JsonInt(int value) : Value(value) {}\n};\n\nclass JsonBoolean final : public Value<Json::BOOL, bool> {\n    bool bool_value() const override { return m_value; }\npublic:\n    explicit JsonBoolean(bool value) : Value(value) {}\n};\n\nclass JsonString final : public Value<Json::STRING, string> {\n    const string &string_value() const override { return m_value; }\npublic:\n    explicit JsonString(const string &value) : Value(value) {}\n    explicit JsonString(string &&value)      : Value(move(value)) {}\n};\n\nclass JsonArray final : public Value<Json::ARRAY, Json::array> {\n    const Json::array &array_items() const override { return m_value; }\n    const Json & operator[](size_t i) const override;\npublic:\n    explicit JsonArray(const Json::array &value) : Value(value) {}\n    explicit JsonArray(Json::array &&value)      : Value(move(value)) {}\n};\n\nclass JsonObject final : public Value<Json::OBJECT, Json::object> {\n    const Json::object &object_items() const override { return m_value; }\n    const Json & operator[](const string &key) const override;\npublic:\n    explicit JsonObject(const Json::object &value) : Value(value) {}\n    explicit JsonObject(Json::object &&value)      : Value(move(value)) {}\n};\n\nclass JsonNull final : public Value<Json::NUL, NullStruct> {\npublic:\n    JsonNull() : Value({}) {}\n};\n\n/* * * * * * * * * * * * * * * * * * * *\n * Static globals - static-init-safe\n */\nstruct Statics {\n    const std::shared_ptr<JsonValue> null = make_shared<JsonNull>();\n    const std::shared_ptr<JsonValue> t = make_shared<JsonBoolean>(true);\n    const std::shared_ptr<JsonValue> f = make_shared<JsonBoolean>(false);\n    const string empty_string;\n    const vector<Json> empty_vector;\n    const map<string, Json> empty_map;\n    Statics() {}\n};\n\nstatic const Statics & statics() {\n    static const Statics s {};\n    return s;\n}\n\nstatic const Json & static_null() {\n    // This has to be separate, not in Statics, because Json() accesses statics().null.\n    static const Json json_null;\n    return json_null;\n}\n\n/* * * * * * * * * * * * * * * * * * * *\n * Constructors\n */\n\nJson::Json() noexcept                  : m_ptr(statics().null) {}\nJson::Json(std::nullptr_t) noexcept    : m_ptr(statics().null) {}\nJson::Json(double value)               : m_ptr(make_shared<JsonDouble>(value)) {}\nJson::Json(int value)                  : m_ptr(make_shared<JsonInt>(value)) {}\nJson::Json(bool value)                 : m_ptr(value ? statics().t : statics().f) {}\nJson::Json(const string &value)        : m_ptr(make_shared<JsonString>(value)) {}\nJson::Json(string &&value)             : m_ptr(make_shared<JsonString>(move(value))) {}\nJson::Json(const char * value)         : m_ptr(make_shared<JsonString>(value)) {}\nJson::Json(const Json::array &values)  : m_ptr(make_shared<JsonArray>(values)) {}\nJson::Json(Json::array &&values)       : m_ptr(make_shared<JsonArray>(move(values))) {}\nJson::Json(const Json::object &values) : m_ptr(make_shared<JsonObject>(values)) {}\nJson::Json(Json::object &&values)      : m_ptr(make_shared<JsonObject>(move(values))) {}\n\n/* * * * * * * * * * * * * * * * * * * *\n * Accessors\n */\n\nJson::Type Json::type()                           const { return m_ptr->type();         }\ndouble Json::number_value()                       const { return m_ptr->number_value(); }\nint Json::int_value()                             const { return m_ptr->int_value();    }\nbool Json::bool_value()                           const { return m_ptr->bool_value();   }\nconst string & Json::string_value()               const { return m_ptr->string_value(); }\nconst vector<Json> & Json::array_items()          const { return m_ptr->array_items();  }\nconst map<string, Json> & Json::object_items()    const { return m_ptr->object_items(); }\nconst Json & Json::operator[] (size_t i)          const { return (*m_ptr)[i];           }\nconst Json & Json::operator[] (const string &key) const { return (*m_ptr)[key];         }\n\ndouble                    JsonValue::number_value()              const { return 0; }\nint                       JsonValue::int_value()                 const { return 0; }\nbool                      JsonValue::bool_value()                const { return false; }\nconst string &            JsonValue::string_value()              const { return statics().empty_string; }\nconst vector<Json> &      JsonValue::array_items()               const { return statics().empty_vector; }\nconst map<string, Json> & JsonValue::object_items()              const { return statics().empty_map; }\nconst Json &              JsonValue::operator[] (size_t)         const { return static_null(); }\nconst Json &              JsonValue::operator[] (const string &) const { return static_null(); }\n\nconst Json & JsonObject::operator[] (const string &key) const {\n    auto iter = m_value.find(key);\n    return (iter == m_value.end()) ? static_null() : iter->second;\n}\nconst Json & JsonArray::operator[] (size_t i) const {\n    if (i >= m_value.size()) return static_null();\n    else return m_value[i];\n}\n\n/* * * * * * * * * * * * * * * * * * * *\n * Comparison\n */\n\nbool Json::operator== (const Json &other) const {\n    if (m_ptr == other.m_ptr)\n        return true;\n    if (m_ptr->type() != other.m_ptr->type())\n        return false;\n\n    return m_ptr->equals(other.m_ptr.get());\n}\n\nbool Json::operator< (const Json &other) const {\n    if (m_ptr == other.m_ptr)\n        return false;\n    if (m_ptr->type() != other.m_ptr->type())\n        return m_ptr->type() < other.m_ptr->type();\n\n    return m_ptr->less(other.m_ptr.get());\n}\n\n/* * * * * * * * * * * * * * * * * * * *\n * Parsing\n */\n\n/* esc(c)\n *\n * Format char c suitable for printing in an error message.\n */\nstatic inline string esc(char c) {\n    char buf[12];\n    if (static_cast<uint8_t>(c) >= 0x20 && static_cast<uint8_t>(c) <= 0x7f) {\n        snprintf(buf, sizeof buf, \"'%c' (%d)\", c, c);\n    } else {\n        snprintf(buf, sizeof buf, \"(%d)\", c);\n    }\n    return string(buf);\n}\n\nstatic inline bool in_range(long x, long lower, long upper) {\n    return (x >= lower && x <= upper);\n}\n\nnamespace {\n/* JsonParser\n *\n * Object that tracks all state of an in-progress parse.\n */\nstruct JsonParser final {\n\n    /* State\n     */\n    const string &str;\n    size_t i;\n    string &err;\n    bool failed;\n    const JsonParse strategy;\n\n    /* fail(msg, err_ret = Json())\n     *\n     * Mark this parse as failed.\n     */\n    Json fail(string &&msg) {\n        return fail(move(msg), Json());\n    }\n\n    template <typename T>\n    T fail(string &&msg, const T err_ret) {\n        if (!failed)\n            err = std::move(msg);\n        failed = true;\n        return err_ret;\n    }\n\n    /* consume_whitespace()\n     *\n     * Advance until the current character is non-whitespace.\n     */\n    void consume_whitespace() {\n        while (str[i] == ' ' || str[i] == '\\r' || str[i] == '\\n' || str[i] == '\\t')\n            i++;\n    }\n\n    /* consume_comment()\n     *\n     * Advance comments (c-style inline and multiline).\n     */\n    bool consume_comment() {\n      bool comment_found = false;\n      if (str[i] == '/') {\n        i++;\n        if (i == str.size())\n          return fail(\"unexpected end of input after start of comment\", false);\n        if (str[i] == '/') { // inline comment\n          i++;\n          // advance until next line, or end of input\n          while (i < str.size() && str[i] != '\\n') {\n            i++;\n          }\n          comment_found = true;\n        }\n        else if (str[i] == '*') { // multiline comment\n          i++;\n          if (i > str.size()-2)\n            return fail(\"unexpected end of input inside multi-line comment\", false);\n          // advance until closing tokens\n          while (!(str[i] == '*' && str[i+1] == '/')) {\n            i++;\n            if (i > str.size()-2)\n              return fail(\n                \"unexpected end of input inside multi-line comment\", false);\n          }\n          i += 2;\n          comment_found = true;\n        }\n        else\n          return fail(\"malformed comment\", false);\n      }\n      return comment_found;\n    }\n\n    /* consume_garbage()\n     *\n     * Advance until the current character is non-whitespace and non-comment.\n     */\n    void consume_garbage() {\n      consume_whitespace();\n      if(strategy == JsonParse::COMMENTS) {\n        bool comment_found = false;\n        do {\n          comment_found = consume_comment();\n          if (failed) return;\n          consume_whitespace();\n        }\n        while(comment_found);\n      }\n    }\n\n    /* get_next_token()\n     *\n     * Return the next non-whitespace character. If the end of the input is reached,\n     * flag an error and return 0.\n     */\n    char get_next_token() {\n        consume_garbage();\n        if (failed) return static_cast<char>(0);\n        if (i == str.size())\n            return fail(\"unexpected end of input\", static_cast<char>(0));\n\n        return str[i++];\n    }\n\n    /* encode_utf8(pt, out)\n     *\n     * Encode pt as UTF-8 and add it to out.\n     */\n    void encode_utf8(long pt, string & out) {\n        if (pt < 0)\n            return;\n\n        if (pt < 0x80) {\n            out += static_cast<char>(pt);\n        } else if (pt < 0x800) {\n            out += static_cast<char>((pt >> 6) | 0xC0);\n            out += static_cast<char>((pt & 0x3F) | 0x80);\n        } else if (pt < 0x10000) {\n            out += static_cast<char>((pt >> 12) | 0xE0);\n            out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);\n            out += static_cast<char>((pt & 0x3F) | 0x80);\n        } else {\n            out += static_cast<char>((pt >> 18) | 0xF0);\n            out += static_cast<char>(((pt >> 12) & 0x3F) | 0x80);\n            out += static_cast<char>(((pt >> 6) & 0x3F) | 0x80);\n            out += static_cast<char>((pt & 0x3F) | 0x80);\n        }\n    }\n\n    /* parse_string()\n     *\n     * Parse a string, starting at the current position.\n     */\n    string parse_string() {\n        string out;\n        long last_escaped_codepoint = -1;\n        while (true) {\n            if (i == str.size())\n                return fail(\"unexpected end of input in string\", \"\");\n\n            char ch = str[i++];\n\n            if (ch == '\"') {\n                encode_utf8(last_escaped_codepoint, out);\n                return out;\n            }\n\n            if (in_range(ch, 0, 0x1f))\n                return fail(\"unescaped \" + esc(ch) + \" in string\", \"\");\n\n            // The usual case: non-escaped characters\n            if (ch != '\\\\') {\n                encode_utf8(last_escaped_codepoint, out);\n                last_escaped_codepoint = -1;\n                out += ch;\n                continue;\n            }\n\n            // Handle escapes\n            if (i == str.size())\n                return fail(\"unexpected end of input in string\", \"\");\n\n            ch = str[i++];\n\n            if (ch == 'u') {\n                // Extract 4-byte escape sequence\n                string esc = str.substr(i, 4);\n                // Explicitly check length of the substring. The following loop\n                // relies on std::string returning the terminating NUL when\n                // accessing str[length]. Checking here reduces brittleness.\n                if (esc.length() < 4) {\n                    return fail(\"bad \\\\u escape: \" + esc, \"\");\n                }\n                for (size_t j = 0; j < 4; j++) {\n                    if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F')\n                            && !in_range(esc[j], '0', '9'))\n                        return fail(\"bad \\\\u escape: \" + esc, \"\");\n                }\n\n                long codepoint = strtol(esc.data(), nullptr, 16);\n\n                // JSON specifies that characters outside the BMP shall be encoded as a pair\n                // of 4-hex-digit \\u escapes encoding their surrogate pair components. Check\n                // whether we're in the middle of such a beast: the previous codepoint was an\n                // escaped lead (high) surrogate, and this is a trail (low) surrogate.\n                if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF)\n                        && in_range(codepoint, 0xDC00, 0xDFFF)) {\n                    // Reassemble the two surrogate pairs into one astral-plane character, per\n                    // the UTF-16 algorithm.\n                    encode_utf8((((last_escaped_codepoint - 0xD800) << 10)\n                                 | (codepoint - 0xDC00)) + 0x10000, out);\n                    last_escaped_codepoint = -1;\n                } else {\n                    encode_utf8(last_escaped_codepoint, out);\n                    last_escaped_codepoint = codepoint;\n                }\n\n                i += 4;\n                continue;\n            }\n\n            encode_utf8(last_escaped_codepoint, out);\n            last_escaped_codepoint = -1;\n\n            if (ch == 'b') {\n                out += '\\b';\n            } else if (ch == 'f') {\n                out += '\\f';\n            } else if (ch == 'n') {\n                out += '\\n';\n            } else if (ch == 'r') {\n                out += '\\r';\n            } else if (ch == 't') {\n                out += '\\t';\n            } else if (ch == '\"' || ch == '\\\\' || ch == '/') {\n                out += ch;\n            } else {\n                return fail(\"invalid escape character \" + esc(ch), \"\");\n            }\n        }\n    }\n\n    /* parse_number()\n     *\n     * Parse a double.\n     */\n    Json parse_number() {\n        size_t start_pos = i;\n\n        if (str[i] == '-')\n            i++;\n\n        // Integer part\n        if (str[i] == '0') {\n            i++;\n            if (in_range(str[i], '0', '9'))\n                return fail(\"leading 0s not permitted in numbers\");\n        } else if (in_range(str[i], '1', '9')) {\n            i++;\n            while (in_range(str[i], '0', '9'))\n                i++;\n        } else {\n            return fail(\"invalid \" + esc(str[i]) + \" in number\");\n        }\n\n        if (str[i] != '.' && str[i] != 'e' && str[i] != 'E'\n                && (i - start_pos) <= static_cast<size_t>(std::numeric_limits<int>::digits10)) {\n            return std::atoi(str.c_str() + start_pos);\n        }\n\n        // Decimal part\n        if (str[i] == '.') {\n            i++;\n            if (!in_range(str[i], '0', '9'))\n                return fail(\"at least one digit required in fractional part\");\n\n            while (in_range(str[i], '0', '9'))\n                i++;\n        }\n\n        // Exponent part\n        if (str[i] == 'e' || str[i] == 'E') {\n            i++;\n\n            if (str[i] == '+' || str[i] == '-')\n                i++;\n\n            if (!in_range(str[i], '0', '9'))\n                return fail(\"at least one digit required in exponent\");\n\n            while (in_range(str[i], '0', '9'))\n                i++;\n        }\n\n        return std::strtod(str.c_str() + start_pos, nullptr);\n    }\n\n    /* expect(str, res)\n     *\n     * Expect that 'str' starts at the character that was just read. If it does, advance\n     * the input and return res. If not, flag an error.\n     */\n    Json expect(const string &expected, Json res) {\n        assert(i != 0);\n        i--;\n        if (str.compare(i, expected.length(), expected) == 0) {\n            i += expected.length();\n            return res;\n        } else {\n            return fail(\"parse error: expected \" + expected + \", got \" + str.substr(i, expected.length()));\n        }\n    }\n\n    /* parse_json()\n     *\n     * Parse a JSON object.\n     */\n    Json parse_json(int depth) {\n        if (depth > max_depth) {\n            return fail(\"exceeded maximum nesting depth\");\n        }\n\n        char ch = get_next_token();\n        if (failed)\n            return Json();\n\n        if (ch == '-' || (ch >= '0' && ch <= '9')) {\n            i--;\n            return parse_number();\n        }\n\n        if (ch == 't')\n            return expect(\"true\", true);\n\n        if (ch == 'f')\n            return expect(\"false\", false);\n\n        if (ch == 'n')\n            return expect(\"null\", Json());\n\n        if (ch == '\"')\n            return parse_string();\n\n        if (ch == '{') {\n            map<string, Json> data;\n            ch = get_next_token();\n            if (ch == '}')\n                return data;\n\n            while (1) {\n                if (ch != '\"')\n                    return fail(\"expected '\\\"' in object, got \" + esc(ch));\n\n                string key = parse_string();\n                if (failed)\n                    return Json();\n\n                ch = get_next_token();\n                if (ch != ':')\n                    return fail(\"expected ':' in object, got \" + esc(ch));\n\n                data[std::move(key)] = parse_json(depth + 1);\n                if (failed)\n                    return Json();\n\n                ch = get_next_token();\n                if (ch == '}')\n                    break;\n                if (ch != ',')\n                    return fail(\"expected ',' in object, got \" + esc(ch));\n\n                ch = get_next_token();\n            }\n            return data;\n        }\n\n        if (ch == '[') {\n            vector<Json> data;\n            ch = get_next_token();\n            if (ch == ']')\n                return data;\n\n            while (1) {\n                i--;\n                data.push_back(parse_json(depth + 1));\n                if (failed)\n                    return Json();\n\n                ch = get_next_token();\n                if (ch == ']')\n                    break;\n                if (ch != ',')\n                    return fail(\"expected ',' in list, got \" + esc(ch));\n\n                ch = get_next_token();\n                (void)ch;\n            }\n            return data;\n        }\n\n        return fail(\"expected value, got \" + esc(ch));\n    }\n};\n}//namespace {\n\nJson Json::parse(const string &in, string &err, JsonParse strategy) {\n    JsonParser parser { in, 0, err, false, strategy };\n    Json result = parser.parse_json(0);\n\n    // Check for any trailing garbage\n    parser.consume_garbage();\n    if (parser.failed)\n        return Json();\n    if (parser.i != in.size())\n        return parser.fail(\"unexpected trailing \" + esc(in[parser.i]));\n\n    return result;\n}\n\n// Documented in json11.hpp\nvector<Json> Json::parse_multi(const string &in,\n                               std::string::size_type &parser_stop_pos,\n                               string &err,\n                               JsonParse strategy) {\n    JsonParser parser { in, 0, err, false, strategy };\n    parser_stop_pos = 0;\n    vector<Json> json_vec;\n    while (parser.i != in.size() && !parser.failed) {\n        json_vec.push_back(parser.parse_json(0));\n        if (parser.failed)\n            break;\n\n        // Check for another object\n        parser.consume_garbage();\n        if (parser.failed)\n            break;\n        parser_stop_pos = parser.i;\n    }\n    return json_vec;\n}\n\n/* * * * * * * * * * * * * * * * * * * *\n * Shape-checking\n */\n\nbool Json::has_shape(const shape & types, string & err) const {\n    if (!is_object()) {\n        err = \"expected JSON object, got \" + dump();\n        return false;\n    }\n\n    const auto& obj_items = object_items();\n    for (auto & item : types) {\n        const auto it = obj_items.find(item.first);\n        if (it == obj_items.cend() || it->second.type() != item.second) {\n            err = \"bad type for \" + item.first + \" in \" + dump();\n            return false;\n        }\n    }\n\n    return true;\n}\n\n} // namespace json11\n"
  },
  {
    "path": "deps/json11/json11.hpp",
    "content": "/* json11\n *\n * json11 is a tiny JSON library for C++11, providing JSON parsing and serialization.\n *\n * The core object provided by the library is json11::Json. A Json object represents any JSON\n * value: null, bool, number (int or double), string (std::string), array (std::vector), or\n * object (std::map).\n *\n * Json objects act like values: they can be assigned, copied, moved, compared for equality or\n * order, etc. There are also helper methods Json::dump, to serialize a Json to a string, and\n * Json::parse (static) to parse a std::string as a Json object.\n *\n * Internally, the various types of Json object are represented by the JsonValue class\n * hierarchy.\n *\n * A note on numbers - JSON specifies the syntax of number formatting but not its semantics,\n * so some JSON implementations distinguish between integers and floating-point numbers, while\n * some don't. In json11, we choose the latter. Because some JSON implementations (namely\n * Javascript itself) treat all numbers as the same type, distinguishing the two leads\n * to JSON that will be *silently* changed by a round-trip through those implementations.\n * Dangerous! To avoid that risk, json11 stores all numbers as double internally, but also\n * provides integer helpers.\n *\n * Fortunately, double-precision IEEE754 ('double') can precisely store any integer in the\n * range +/-2^53, which includes every 'int' on most systems. (Timestamps often use int64\n * or long long to avoid the Y2038K problem; a double storing microseconds since some epoch\n * will be exact for +/- 275 years.)\n */\n\n/* Copyright (c) 2013 Dropbox, Inc.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n#include <map>\n#include <memory>\n#include <initializer_list>\n\n#ifdef _MSC_VER\n    #if _MSC_VER <= 1800 // VS 2013\n        #ifndef noexcept\n            #define noexcept throw()\n        #endif\n\n        #ifndef snprintf\n            #define snprintf _snprintf_s\n        #endif\n    #endif\n#endif\n\nnamespace json11 {\n\nenum JsonParse {\n    STANDARD, COMMENTS\n};\n\nclass JsonValue;\n\nclass Json final {\npublic:\n    // Types\n    enum Type {\n        NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT\n    };\n\n    // Array and object typedefs\n    typedef std::vector<Json> array;\n    typedef std::map<std::string, Json> object;\n\n    // Constructors for the various types of JSON value.\n    Json() noexcept;                // NUL\n    Json(std::nullptr_t) noexcept;  // NUL\n    Json(double value);             // NUMBER\n    Json(int value);                // NUMBER\n    Json(bool value);               // BOOL\n    Json(const std::string &value); // STRING\n    Json(std::string &&value);      // STRING\n    Json(const char * value);       // STRING\n    Json(const array &values);      // ARRAY\n    Json(array &&values);           // ARRAY\n    Json(const object &values);     // OBJECT\n    Json(object &&values);          // OBJECT\n\n    // Implicit constructor: anything with a to_json() function.\n    template <class T, class = decltype(&T::to_json)>\n    Json(const T & t) : Json(t.to_json()) {}\n\n    // Implicit constructor: map-like objects (std::map, std::unordered_map, etc)\n    template <class M, typename std::enable_if<\n        std::is_constructible<std::string, decltype(std::declval<M>().begin()->first)>::value\n        && std::is_constructible<Json, decltype(std::declval<M>().begin()->second)>::value,\n            int>::type = 0>\n    Json(const M & m) : Json(object(m.begin(), m.end())) {}\n\n    // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)\n    template <class V, typename std::enable_if<\n        std::is_constructible<Json, decltype(*std::declval<V>().begin())>::value,\n            int>::type = 0>\n    Json(const V & v) : Json(array(v.begin(), v.end())) {}\n\n    // This prevents Json(some_pointer) from accidentally producing a bool. Use\n    // Json(bool(some_pointer)) if that behavior is desired.\n    Json(void *) = delete;\n\n    // Accessors\n    Type type() const;\n\n    bool is_null()   const { return type() == NUL; }\n    bool is_number() const { return type() == NUMBER; }\n    bool is_bool()   const { return type() == BOOL; }\n    bool is_string() const { return type() == STRING; }\n    bool is_array()  const { return type() == ARRAY; }\n    bool is_object() const { return type() == OBJECT; }\n\n    // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not\n    // distinguish between integer and non-integer numbers - number_value() and int_value()\n    // can both be applied to a NUMBER-typed object.\n    double number_value() const;\n    int int_value() const;\n\n    // Return the enclosed value if this is a boolean, false otherwise.\n    bool bool_value() const;\n    // Return the enclosed string if this is a string, \"\" otherwise.\n    const std::string &string_value() const;\n    // Return the enclosed std::vector if this is an array, or an empty vector otherwise.\n    const array &array_items() const;\n    // Return the enclosed std::map if this is an object, or an empty map otherwise.\n    const object &object_items() const;\n\n    // Return a reference to arr[i] if this is an array, Json() otherwise.\n    const Json & operator[](size_t i) const;\n    // Return a reference to obj[key] if this is an object, Json() otherwise.\n    const Json & operator[](const std::string &key) const;\n\n    // Serialize.\n    void dump(std::string &out) const;\n    std::string dump() const {\n        std::string out;\n        dump(out);\n        return out;\n    }\n\n    // Parse. If parse fails, return Json() and assign an error message to err.\n    static Json parse(const std::string & in,\n                      std::string & err,\n                      JsonParse strategy = JsonParse::STANDARD);\n    static Json parse(const char * in,\n                      std::string & err,\n                      JsonParse strategy = JsonParse::STANDARD) {\n        if (in) {\n            return parse(std::string(in), err, strategy);\n        } else {\n            err = \"null input\";\n            return nullptr;\n        }\n    }\n    // Parse multiple objects, concatenated or separated by whitespace\n    static std::vector<Json> parse_multi(\n        const std::string & in,\n        std::string::size_type & parser_stop_pos,\n        std::string & err,\n        JsonParse strategy = JsonParse::STANDARD);\n\n    static inline std::vector<Json> parse_multi(\n        const std::string & in,\n        std::string & err,\n        JsonParse strategy = JsonParse::STANDARD) {\n        std::string::size_type parser_stop_pos;\n        return parse_multi(in, parser_stop_pos, err, strategy);\n    }\n\n    bool operator== (const Json &rhs) const;\n    bool operator<  (const Json &rhs) const;\n    bool operator!= (const Json &rhs) const { return !(*this == rhs); }\n    bool operator<= (const Json &rhs) const { return !(rhs < *this); }\n    bool operator>  (const Json &rhs) const { return  (rhs < *this); }\n    bool operator>= (const Json &rhs) const { return !(*this < rhs); }\n\n    /* has_shape(types, err)\n     *\n     * Return true if this is a JSON object and, for each item in types, has a field of\n     * the given type. If not, return false and set err to a descriptive message.\n     */\n    typedef std::initializer_list<std::pair<std::string, Type>> shape;\n    bool has_shape(const shape & types, std::string & err) const;\n\nprivate:\n    std::shared_ptr<JsonValue> m_ptr;\n};\n\n// Internal class hierarchy - JsonValue objects are not exposed to users of this API.\nclass JsonValue {\nprotected:\n    friend class Json;\n    friend class JsonInt;\n    friend class JsonDouble;\n    virtual Json::Type type() const = 0;\n    virtual bool equals(const JsonValue * other) const = 0;\n    virtual bool less(const JsonValue * other) const = 0;\n    virtual void dump(std::string &out) const = 0;\n    virtual double number_value() const;\n    virtual int int_value() const;\n    virtual bool bool_value() const;\n    virtual const std::string &string_value() const;\n    virtual const Json::array &array_items() const;\n    virtual const Json &operator[](size_t i) const;\n    virtual const Json::object &object_items() const;\n    virtual const Json &operator[](const std::string &key) const;\n    virtual ~JsonValue() {}\n};\n\n} // namespace json11\n"
  },
  {
    "path": "deps/libcaption/.clang-format",
    "content": "Language: Cpp\nSortIncludes: false\nDisableFormat: true\n"
  },
  {
    "path": "deps/libcaption/.gitignore",
    "content": "# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n\n# Shared objects (inc. Windows DLLs)\n*.dll\n*.so\n*.so.*\n*.dylib\n\n# Executables\n*.exe\n*.out\n*.app\n*.i*86\n*.x86_64\n*.hex\n\n# Debug files\n*.dSYM/\n\n# cmake files\nMakefile\nCMakeFiles\nCMakeCache.txt\ncmake_install.cmake\n\n# Bin\nscc2srt\nsrt2vtt\nflv2srt\nts2srt\nflv+srt\nsrtdump\nparty\nrtmpspit\n\n# Autogenerated files\n# src/eia608.c\n\n# Mac\n.DS_Store\n\n#Doc\nDoxyfile\n"
  },
  {
    "path": "deps/libcaption/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nadd_library(caption STATIC EXCLUDE_FROM_ALL)\nadd_library(OBS::caption ALIAS caption)\n\ntarget_sources(\n  caption\n  PRIVATE\n    caption/cea708.h\n    caption/eia608.h\n    caption/eia608_charmap.h\n    caption/mpeg.h\n    caption/scc.h\n    caption/utf8.h\n    src/caption.c\n    src/cea708.c\n    src/eia608.c\n    src/eia608_charmap.c\n    src/eia608_from_utf8.c\n    src/mpeg.c\n    src/scc.c\n    src/srt.c\n    src/utf8.c\n    src/xds.c\n  PUBLIC caption/caption.h\n)\n\ntarget_include_directories(caption PRIVATE caption PUBLIC \"${CMAKE_CURRENT_SOURCE_DIR}\")\n\ntarget_compile_definitions(caption PRIVATE __STDC_CONSTANT_MACROS)\n\ntarget_compile_options(\n  caption\n  PRIVATE\n    $<$<COMPILE_LANG_AND_ID:C,Clang,AppleClang,GNU>:-Wno-unused-but-set-parameter>\n    $<$<COMPILE_LANG_AND_ID:C,Clang,AppleClang,GNU>:-Wno-strict-prototypes>\n    $<$<COMPILE_LANG_AND_ID:C,Clang,AppleClang>:-Wno-comma>\n)\n\nset_target_properties(caption PROPERTIES FOLDER deps POSITION_INDEPENDENT_CODE TRUE)\n"
  },
  {
    "path": "deps/libcaption/Doxyfile.in",
    "content": "# Doxyfile 1.8.11\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for a project.\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\n# This tag specifies the encoding used for all characters in the config file\n# that follow. The default is UTF-8 which is also the encoding used for all text\n# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv\n# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv\n# for the list of possible encodings.\n# The default value is: UTF-8.\n\nDOXYFILE_ENCODING      = UTF-8\n\n# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by\n# double-quotes, unless you are using Doxywizard) that should identify the\n# project for which the documentation is generated. This name is used in the\n# title of most generated pages and in a few other places.\n# The default value is: My Project.\n\nPROJECT_NAME           = \"libcaption\"\n\n# The PROJECT_NUMBER tag can be used to enter a project or revision number. This\n# could be handy for archiving the generated documentation or if some version\n# control system is used.\n\nPROJECT_NUMBER         =\n\n# Using the PROJECT_BRIEF tag one can provide an optional one line description\n# for a project that appears at the top of each page and should give viewer a\n# quick idea about the purpose of the project. Keep the description short.\n\nPROJECT_BRIEF          =\n\n# With the PROJECT_LOGO tag one can specify a logo or an icon that is included\n# in the documentation. The maximum height of the logo should not exceed 55\n# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy\n# the logo to the output directory.\n\nPROJECT_LOGO           =\n\n# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path\n# into which the generated documentation will be written. If a relative path is\n# entered, it will be relative to the location where doxygen was started. If\n# left blank the current directory will be used.\n\nOUTPUT_DIRECTORY       = ./docs\n\n# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-\n# directories (in 2 levels) under the output directory of each output format and\n# will distribute the generated files over these directories. Enabling this\n# option can be useful when feeding doxygen a huge amount of source files, where\n# putting all generated files in the same directory would otherwise causes\n# performance problems for the file system.\n# The default value is: NO.\n\nCREATE_SUBDIRS         = YES\n\n# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII\n# characters to appear in the names of generated files. If set to NO, non-ASCII\n# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode\n# U+3044.\n# The default value is: NO.\n\nALLOW_UNICODE_NAMES    = NO\n\n# The OUTPUT_LANGUAGE tag is used to specify the language in which all\n# documentation generated by doxygen is written. Doxygen will use this\n# information to generate all constant output in the proper language.\n# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,\n# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),\n# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,\n# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),\n# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,\n# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,\n# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,\n# Ukrainian and Vietnamese.\n# The default value is: English.\n\nOUTPUT_LANGUAGE        = English\n\n# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member\n# descriptions after the members that are listed in the file and class\n# documentation (similar to Javadoc). Set to NO to disable this.\n# The default value is: YES.\n\nBRIEF_MEMBER_DESC      = YES\n\n# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief\n# description of a member or function before the detailed description\n#\n# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the\n# brief descriptions will be completely suppressed.\n# The default value is: YES.\n\nREPEAT_BRIEF           = YES\n\n# This tag implements a quasi-intelligent brief description abbreviator that is\n# used to form the text in various listings. Each string in this list, if found\n# as the leading text of the brief description, will be stripped from the text\n# and the result, after processing the whole list, is used as the annotated\n# text. Otherwise, the brief description is used as-is. If left blank, the\n# following values are used ($name is automatically replaced with the name of\n# the entity):The $name class, The $name widget, The $name file, is, provides,\n# specifies, contains, represents, a, an and the.\n\nABBREVIATE_BRIEF       =\n\n# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then\n# doxygen will generate a detailed section even if there is only a brief\n# description.\n# The default value is: NO.\n\nALWAYS_DETAILED_SEC    = NO\n\n# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all\n# inherited members of a class in the documentation of that class as if those\n# members were ordinary class members. Constructors, destructors and assignment\n# operators of the base classes will not be shown.\n# The default value is: NO.\n\nINLINE_INHERITED_MEMB  = NO\n\n# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path\n# before files name in the file list and in the header files. If set to NO the\n# shortest path that makes the file name unique will be used\n# The default value is: YES.\n\nFULL_PATH_NAMES        = YES\n\n# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.\n# Stripping is only done if one of the specified strings matches the left-hand\n# part of the path. The tag can be used to show relative paths in the file list.\n# If left blank the directory from which doxygen is run is used as the path to\n# strip.\n#\n# Note that you can specify absolute paths here, but also relative paths, which\n# will be relative from the directory where doxygen is started.\n# This tag requires that the tag FULL_PATH_NAMES is set to YES.\n\nSTRIP_FROM_PATH        =\n\n# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the\n# path mentioned in the documentation of a class, which tells the reader which\n# header file to include in order to use a class. If left blank only the name of\n# the header file containing the class definition is used. Otherwise one should\n# specify the list of include paths that are normally passed to the compiler\n# using the -I flag.\n\nSTRIP_FROM_INC_PATH    =\n\n# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but\n# less readable) file names. This can be useful is your file systems doesn't\n# support long names like on DOS, Mac, or CD-ROM.\n# The default value is: NO.\n\nSHORT_NAMES            = NO\n\n# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the\n# first line (until the first dot) of a Javadoc-style comment as the brief\n# description. If set to NO, the Javadoc-style will behave just like regular Qt-\n# style comments (thus requiring an explicit @brief command for a brief\n# description.)\n# The default value is: NO.\n\nJAVADOC_AUTOBRIEF      = NO\n\n# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first\n# line (until the first dot) of a Qt-style comment as the brief description. If\n# set to NO, the Qt-style will behave just like regular Qt-style comments (thus\n# requiring an explicit \\brief command for a brief description.)\n# The default value is: NO.\n\nQT_AUTOBRIEF           = NO\n\n# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a\n# multi-line C++ special comment block (i.e. a block of //! or /// comments) as\n# a brief description. This used to be the default behavior. The new default is\n# to treat a multi-line C++ comment block as a detailed description. Set this\n# tag to YES if you prefer the old behavior instead.\n#\n# Note that setting this tag to YES also means that rational rose comments are\n# not recognized any more.\n# The default value is: NO.\n\nMULTILINE_CPP_IS_BRIEF = NO\n\n# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the\n# documentation from any documented member that it re-implements.\n# The default value is: YES.\n\nINHERIT_DOCS           = YES\n\n# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new\n# page for each member. If set to NO, the documentation of a member will be part\n# of the file/class/namespace that contains it.\n# The default value is: NO.\n\nSEPARATE_MEMBER_PAGES  = NO\n\n# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen\n# uses this value to replace tabs by spaces in code fragments.\n# Minimum value: 1, maximum value: 16, default value: 4.\n\nTAB_SIZE               = 4\n\n# This tag can be used to specify a number of aliases that act as commands in\n# the documentation. An alias has the form:\n# name=value\n# For example adding\n# \"sideeffect=@par Side Effects:\\n\"\n# will allow you to put the command \\sideeffect (or @sideeffect) in the\n# documentation, which will result in a user-defined paragraph with heading\n# \"Side Effects:\". You can put \\n's in the value part of an alias to insert\n# newlines.\n\nALIASES                =\n\n# This tag can be used to specify a number of word-keyword mappings (TCL only).\n# A mapping has the form \"name=value\". For example adding \"class=itcl::class\"\n# will allow you to use the command class in the itcl::class meaning.\n\nTCL_SUBST              =\n\n# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources\n# only. Doxygen will then generate output that is more tailored for C. For\n# instance, some of the names that are used will be different. The list of all\n# members will be omitted, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_FOR_C  = NO\n\n# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or\n# Python sources only. Doxygen will then generate output that is more tailored\n# for that language. For instance, namespaces will be presented as packages,\n# qualified scopes will look different, etc.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_JAVA   = NO\n\n# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran\n# sources. Doxygen will then generate output that is tailored for Fortran.\n# The default value is: NO.\n\nOPTIMIZE_FOR_FORTRAN   = NO\n\n# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL\n# sources. Doxygen will then generate output that is tailored for VHDL.\n# The default value is: NO.\n\nOPTIMIZE_OUTPUT_VHDL   = NO\n\n# Doxygen selects the parser to use depending on the extension of the files it\n# parses. With this tag you can assign which parser to use for a given\n# extension. Doxygen has a built-in mapping, but you can override or extend it\n# using this tag. The format is ext=language, where ext is a file extension, and\n# language is one of the parsers supported by doxygen: IDL, Java, Javascript,\n# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:\n# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:\n# Fortran. In the later case the parser tries to guess whether the code is fixed\n# or free formatted code, this is the default for Fortran type files), VHDL. For\n# instance to make doxygen treat .inc files as Fortran files (default is PHP),\n# and .f files as C (default is Fortran), use: inc=Fortran f=C.\n#\n# Note: For files without extension you can use no_extension as a placeholder.\n#\n# Note that for custom extensions you also need to set FILE_PATTERNS otherwise\n# the files are not read by doxygen.\n\nEXTENSION_MAPPING      =\n\n# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments\n# according to the Markdown format, which allows for more readable\n# documentation. See http://daringfireball.net/projects/markdown/ for details.\n# The output of markdown processing is further processed by doxygen, so you can\n# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in\n# case of backward compatibilities issues.\n# The default value is: YES.\n\nMARKDOWN_SUPPORT       = YES\n\n# When enabled doxygen tries to link words that correspond to documented\n# classes, or namespaces to their corresponding documentation. Such a link can\n# be prevented in individual cases by putting a % sign in front of the word or\n# globally by setting AUTOLINK_SUPPORT to NO.\n# The default value is: YES.\n\nAUTOLINK_SUPPORT       = YES\n\n# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want\n# to include (a tag file for) the STL sources as input, then you should set this\n# tag to YES in order to let doxygen match functions declarations and\n# definitions whose arguments contain STL classes (e.g. func(std::string);\n# versus func(std::string) {}). This also make the inheritance and collaboration\n# diagrams that involve STL classes more complete and accurate.\n# The default value is: NO.\n\nBUILTIN_STL_SUPPORT    = NO\n\n# If you use Microsoft's C++/CLI language, you should set this option to YES to\n# enable parsing support.\n# The default value is: NO.\n\nCPP_CLI_SUPPORT        = NO\n\n# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:\n# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen\n# will parse them like normal C++ but will assume all classes use public instead\n# of private inheritance when no explicit protection keyword is present.\n# The default value is: NO.\n\nSIP_SUPPORT            = NO\n\n# For Microsoft's IDL there are propget and propput attributes to indicate\n# getter and setter methods for a property. Setting this option to YES will make\n# doxygen to replace the get and set methods by a property in the documentation.\n# This will only work if the methods are indeed getting or setting a simple\n# type. If this is not the case, or you want to show the methods anyway, you\n# should set this option to NO.\n# The default value is: YES.\n\nIDL_PROPERTY_SUPPORT   = YES\n\n# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC\n# tag is set to YES then doxygen will reuse the documentation of the first\n# member in the group (if any) for the other members of the group. By default\n# all members of a group must be documented explicitly.\n# The default value is: NO.\n\nDISTRIBUTE_GROUP_DOC   = NO\n\n# If one adds a struct or class to a group and this option is enabled, then also\n# any nested class or struct is added to the same group. By default this option\n# is disabled and one has to add nested compounds explicitly via \\ingroup.\n# The default value is: NO.\n\nGROUP_NESTED_COMPOUNDS = NO\n\n# Set the SUBGROUPING tag to YES to allow class member groups of the same type\n# (for instance a group of public functions) to be put as a subgroup of that\n# type (e.g. under the Public Functions section). Set it to NO to prevent\n# subgrouping. Alternatively, this can be done per class using the\n# \\nosubgrouping command.\n# The default value is: YES.\n\nSUBGROUPING            = YES\n\n# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions\n# are shown inside the group in which they are included (e.g. using \\ingroup)\n# instead of on a separate page (for HTML and Man pages) or section (for LaTeX\n# and RTF).\n#\n# Note that this feature does not work in combination with\n# SEPARATE_MEMBER_PAGES.\n# The default value is: NO.\n\nINLINE_GROUPED_CLASSES = NO\n\n# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions\n# with only public data fields or simple typedef fields will be shown inline in\n# the documentation of the scope in which they are defined (i.e. file,\n# namespace, or group documentation), provided this scope is documented. If set\n# to NO, structs, classes, and unions are shown on a separate page (for HTML and\n# Man pages) or section (for LaTeX and RTF).\n# The default value is: NO.\n\nINLINE_SIMPLE_STRUCTS  = NO\n\n# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or\n# enum is documented as struct, union, or enum with the name of the typedef. So\n# typedef struct TypeS {} TypeT, will appear in the documentation as a struct\n# with name TypeT. When disabled the typedef will appear as a member of a file,\n# namespace, or class. And the struct will be named TypeS. This can typically be\n# useful for C code in case the coding convention dictates that all compound\n# types are typedef'ed and only the typedef is referenced, never the tag name.\n# The default value is: NO.\n\nTYPEDEF_HIDES_STRUCT   = NO\n\n# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This\n# cache is used to resolve symbols given their name and scope. Since this can be\n# an expensive process and often the same symbol appears multiple times in the\n# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small\n# doxygen will become slower. If the cache is too large, memory is wasted. The\n# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range\n# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536\n# symbols. At the end of a run doxygen will report the cache usage and suggest\n# the optimal cache size from a speed point of view.\n# Minimum value: 0, maximum value: 9, default value: 0.\n\nLOOKUP_CACHE_SIZE      = 0\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\n# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in\n# documentation are documented, even if no documentation was available. Private\n# class members and static file members will be hidden unless the\n# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.\n# Note: This will also disable the warnings about undocumented members that are\n# normally produced when WARNINGS is set to YES.\n# The default value is: NO.\n\nEXTRACT_ALL            = YES\n\n# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will\n# be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PRIVATE        = NO\n\n# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal\n# scope will be included in the documentation.\n# The default value is: NO.\n\nEXTRACT_PACKAGE        = NO\n\n# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be\n# included in the documentation.\n# The default value is: NO.\n\nEXTRACT_STATIC         = YES\n\n# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined\n# locally in source files will be included in the documentation. If set to NO,\n# only classes defined in header files are included. Does not have any effect\n# for Java sources.\n# The default value is: YES.\n\nEXTRACT_LOCAL_CLASSES  = YES\n\n# This flag is only useful for Objective-C code. If set to YES, local methods,\n# which are defined in the implementation section but not in the interface are\n# included in the documentation. If set to NO, only methods in the interface are\n# included.\n# The default value is: NO.\n\nEXTRACT_LOCAL_METHODS  = NO\n\n# If this flag is set to YES, the members of anonymous namespaces will be\n# extracted and appear in the documentation as a namespace called\n# 'anonymous_namespace{file}', where file will be replaced with the base name of\n# the file that contains the anonymous namespace. By default anonymous namespace\n# are hidden.\n# The default value is: NO.\n\nEXTRACT_ANON_NSPACES   = NO\n\n# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all\n# undocumented members inside documented classes or files. If set to NO these\n# members will be included in the various overviews, but no documentation\n# section is generated. This option has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_MEMBERS     = NO\n\n# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all\n# undocumented classes that are normally visible in the class hierarchy. If set\n# to NO, these classes will be included in the various overviews. This option\n# has no effect if EXTRACT_ALL is enabled.\n# The default value is: NO.\n\nHIDE_UNDOC_CLASSES     = NO\n\n# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend\n# (class|struct|union) declarations. If set to NO, these declarations will be\n# included in the documentation.\n# The default value is: NO.\n\nHIDE_FRIEND_COMPOUNDS  = NO\n\n# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any\n# documentation blocks found inside the body of a function. If set to NO, these\n# blocks will be appended to the function's detailed documentation block.\n# The default value is: NO.\n\nHIDE_IN_BODY_DOCS      = NO\n\n# The INTERNAL_DOCS tag determines if documentation that is typed after a\n# \\internal command is included. If the tag is set to NO then the documentation\n# will be excluded. Set it to YES to include the internal documentation.\n# The default value is: NO.\n\nINTERNAL_DOCS          = NO\n\n# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file\n# names in lower-case letters. If set to YES, upper-case letters are also\n# allowed. This is useful if you have classes or files whose names only differ\n# in case and if your file system supports case sensitive file names. Windows\n# and Mac users are advised to set this option to NO.\n# The default value is: system dependent.\n\nCASE_SENSE_NAMES       = NO\n\n# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with\n# their full class and namespace scopes in the documentation. If set to YES, the\n# scope will be hidden.\n# The default value is: NO.\n\nHIDE_SCOPE_NAMES       = NO\n\n# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will\n# append additional text to a page's title, such as Class Reference. If set to\n# YES the compound reference will be hidden.\n# The default value is: NO.\n\nHIDE_COMPOUND_REFERENCE= NO\n\n# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of\n# the files that are included by a file in the documentation of that file.\n# The default value is: YES.\n\nSHOW_INCLUDE_FILES     = YES\n\n# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each\n# grouped member an include statement to the documentation, telling the reader\n# which file to include in order to use the member.\n# The default value is: NO.\n\nSHOW_GROUPED_MEMB_INC  = NO\n\n# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include\n# files with double quotes in the documentation rather than with sharp brackets.\n# The default value is: NO.\n\nFORCE_LOCAL_INCLUDES   = NO\n\n# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the\n# documentation for inline members.\n# The default value is: YES.\n\nINLINE_INFO            = YES\n\n# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the\n# (detailed) documentation of file and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order.\n# The default value is: YES.\n\nSORT_MEMBER_DOCS       = YES\n\n# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief\n# descriptions of file, namespace and class members alphabetically by member\n# name. If set to NO, the members will appear in declaration order. Note that\n# this will also influence the order of the classes in the class list.\n# The default value is: NO.\n\nSORT_BRIEF_DOCS        = NO\n\n# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the\n# (brief and detailed) documentation of class members so that constructors and\n# destructors are listed first. If set to NO the constructors will appear in the\n# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.\n# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief\n# member documentation.\n# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting\n# detailed member documentation.\n# The default value is: NO.\n\nSORT_MEMBERS_CTORS_1ST = NO\n\n# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy\n# of group names into alphabetical order. If set to NO the group names will\n# appear in their defined order.\n# The default value is: NO.\n\nSORT_GROUP_NAMES       = NO\n\n# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by\n# fully-qualified names, including namespaces. If set to NO, the class list will\n# be sorted only by class name, not including the namespace part.\n# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.\n# Note: This option applies only to the class list, not to the alphabetical\n# list.\n# The default value is: NO.\n\nSORT_BY_SCOPE_NAME     = NO\n\n# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper\n# type resolution of all parameters of a function it will reject a match between\n# the prototype and the implementation of a member function even if there is\n# only one candidate or it is obvious which candidate to choose by doing a\n# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still\n# accept a match between prototype and implementation in such cases.\n# The default value is: NO.\n\nSTRICT_PROTO_MATCHING  = NO\n\n# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo\n# list. This list is created by putting \\todo commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TODOLIST      = YES\n\n# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test\n# list. This list is created by putting \\test commands in the documentation.\n# The default value is: YES.\n\nGENERATE_TESTLIST      = YES\n\n# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug\n# list. This list is created by putting \\bug commands in the documentation.\n# The default value is: YES.\n\nGENERATE_BUGLIST       = YES\n\n# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)\n# the deprecated list. This list is created by putting \\deprecated commands in\n# the documentation.\n# The default value is: YES.\n\nGENERATE_DEPRECATEDLIST= YES\n\n# The ENABLED_SECTIONS tag can be used to enable conditional documentation\n# sections, marked by \\if <section_label> ... \\endif and \\cond <section_label>\n# ... \\endcond blocks.\n\nENABLED_SECTIONS       =\n\n# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the\n# initial value of a variable or macro / define can have for it to appear in the\n# documentation. If the initializer consists of more lines than specified here\n# it will be hidden. Use a value of 0 to hide initializers completely. The\n# appearance of the value of individual variables and macros / defines can be\n# controlled using \\showinitializer or \\hideinitializer command in the\n# documentation regardless of this setting.\n# Minimum value: 0, maximum value: 10000, default value: 30.\n\nMAX_INITIALIZER_LINES  = 30\n\n# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at\n# the bottom of the documentation of classes and structs. If set to YES, the\n# list will mention the files that were used to generate the documentation.\n# The default value is: YES.\n\nSHOW_USED_FILES        = YES\n\n# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This\n# will remove the Files entry from the Quick Index and from the Folder Tree View\n# (if specified).\n# The default value is: YES.\n\nSHOW_FILES             = YES\n\n# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces\n# page. This will remove the Namespaces entry from the Quick Index and from the\n# Folder Tree View (if specified).\n# The default value is: YES.\n\nSHOW_NAMESPACES        = YES\n\n# The FILE_VERSION_FILTER tag can be used to specify a program or script that\n# doxygen should invoke to get the current version for each file (typically from\n# the version control system). Doxygen will invoke the program by executing (via\n# popen()) the command command input-file, where command is the value of the\n# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided\n# by doxygen. Whatever the program writes to standard output is used as the file\n# version. For an example see the documentation.\n\nFILE_VERSION_FILTER    =\n\n# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed\n# by doxygen. The layout file controls the global structure of the generated\n# output files in an output format independent way. To create the layout file\n# that represents doxygen's defaults, run doxygen with the -l option. You can\n# optionally specify a file name after the option, if omitted DoxygenLayout.xml\n# will be used as the name of the layout file.\n#\n# Note that if you run doxygen from a directory containing a file called\n# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE\n# tag is left empty.\n\nLAYOUT_FILE            =\n\n# The CITE_BIB_FILES tag can be used to specify one or more bib files containing\n# the reference definitions. This must be a list of .bib files. The .bib\n# extension is automatically appended if omitted. This requires the bibtex tool\n# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.\n# For LaTeX the style of the bibliography can be controlled using\n# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the\n# search path. See also \\cite for info how to create references.\n\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\n# The QUIET tag can be used to turn on/off the messages that are generated to\n# standard output by doxygen. If QUIET is set to YES this implies that the\n# messages are off.\n# The default value is: NO.\n\nQUIET                  = NO\n\n# The WARNINGS tag can be used to turn on/off the warning messages that are\n# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES\n# this implies that the warnings are on.\n#\n# Tip: Turn warnings on while writing the documentation.\n# The default value is: YES.\n\nWARNINGS               = YES\n\n# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate\n# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag\n# will automatically be disabled.\n# The default value is: YES.\n\nWARN_IF_UNDOCUMENTED   = YES\n\n# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for\n# potential errors in the documentation, such as not documenting some parameters\n# in a documented function, or documenting parameters that don't exist or using\n# markup commands wrongly.\n# The default value is: YES.\n\nWARN_IF_DOC_ERROR      = YES\n\n# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that\n# are documented, but have no documentation for their parameters or return\n# value. If set to NO, doxygen will only warn about wrong or incomplete\n# parameter documentation, but not about the absence of documentation.\n# The default value is: NO.\n\nWARN_NO_PARAMDOC       = NO\n\n# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when\n# a warning is encountered.\n# The default value is: NO.\n\nWARN_AS_ERROR          = NO\n\n# The WARN_FORMAT tag determines the format of the warning messages that doxygen\n# can produce. The string should contain the $file, $line, and $text tags, which\n# will be replaced by the file and line number from which the warning originated\n# and the warning text. Optionally the format may contain $version, which will\n# be replaced by the version of the file (if it could be obtained via\n# FILE_VERSION_FILTER)\n# The default value is: $file:$line: $text.\n\nWARN_FORMAT            = \"$file:$line: $text\"\n\n# The WARN_LOGFILE tag can be used to specify a file to which warning and error\n# messages should be written. If left blank the output is written to standard\n# error (stderr).\n\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\n# The INPUT tag is used to specify the files and/or directories that contain\n# documented source files. You may enter file names like myfile.cpp or\n# directories like /usr/src/myproject. Separate the files or directories with\n# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING\n# Note: If this tag is empty the current directory is searched.\n\nINPUT                  = @CMAKE_CURRENT_SOURCE_DIR@/include @CMAKE_CURRENT_SOURCE_DIR@/examples\n\n# This tag can be used to specify the character encoding of the source files\n# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses\n# libiconv (or the iconv built into libc) for the transcoding. See the libiconv\n# documentation (see: http://www.gnu.org/software/libiconv) for the list of\n# possible encodings.\n# The default value is: UTF-8.\n\nINPUT_ENCODING         = UTF-8\n\n# If the value of the INPUT tag contains directories, you can use the\n# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and\n# *.h) to filter out the source-files in the directories.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# read by doxygen.\n#\n# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,\n# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,\n# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,\n# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl,\n# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js.\n\nFILE_PATTERNS          = *.h *.c *.re2c\n\n# The RECURSIVE tag can be used to specify whether or not subdirectories should\n# be searched for input files as well.\n# The default value is: NO.\n\nRECURSIVE              = YES\n\n# The EXCLUDE tag can be used to specify files and/or directories that should be\n# excluded from the INPUT source files. This way you can easily exclude a\n# subdirectory from a directory tree whose root is specified with the INPUT tag.\n#\n# Note that relative paths are relative to the directory from which doxygen is\n# run.\n\nEXCLUDE                =\n\n# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or\n# directories that are symbolic links (a Unix file system feature) are excluded\n# from the input.\n# The default value is: NO.\n\nEXCLUDE_SYMLINKS       = NO\n\n# If the value of the INPUT tag contains directories, you can use the\n# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude\n# certain files from those directories.\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories for example use the pattern */test/*\n\nEXCLUDE_PATTERNS       =\n\n# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names\n# (namespaces, classes, functions, etc.) that should be excluded from the\n# output. The symbol name can be a fully qualified name, a word, or if the\n# wildcard * is used, a substring. Examples: ANamespace, AClass,\n# AClass::ANamespace, ANamespace::*Test\n#\n# Note that the wildcards are matched against the file with absolute path, so to\n# exclude all test directories use the pattern */test/*\n\nEXCLUDE_SYMBOLS        =\n\n# The EXAMPLE_PATH tag can be used to specify one or more files or directories\n# that contain example code fragments that are included (see the \\include\n# command).\n\nEXAMPLE_PATH           =\n\n# If the value of the EXAMPLE_PATH tag contains directories, you can use the\n# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and\n# *.h) to filter out the source-files in the directories. If left blank all\n# files are included.\n\nEXAMPLE_PATTERNS       =\n\n# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be\n# searched for input files to be used with the \\include or \\dontinclude commands\n# irrespective of the value of the RECURSIVE tag.\n# The default value is: NO.\n\nEXAMPLE_RECURSIVE      = NO\n\n# The IMAGE_PATH tag can be used to specify one or more files or directories\n# that contain images that are to be included in the documentation (see the\n# \\image command).\n\nIMAGE_PATH             =\n\n# The INPUT_FILTER tag can be used to specify a program that doxygen should\n# invoke to filter for each input file. Doxygen will invoke the filter program\n# by executing (via popen()) the command:\n#\n# <filter> <input-file>\n#\n# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the\n# name of an input file. Doxygen will then use the output that the filter\n# program writes to standard output. If FILTER_PATTERNS is specified, this tag\n# will be ignored.\n#\n# Note that the filter must not add or remove lines; it is applied before the\n# code is scanned, but not when the output code is generated. If lines are added\n# or removed, the anchors will not be placed correctly.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nINPUT_FILTER           =\n\n# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern\n# basis. Doxygen will compare the file name with each pattern and apply the\n# filter if there is a match. The filters are a list of the form: pattern=filter\n# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how\n# filters are used. If the FILTER_PATTERNS tag is empty or if none of the\n# patterns match the file name, INPUT_FILTER is applied.\n#\n# Note that for custom extensions or not directly supported extensions you also\n# need to set EXTENSION_MAPPING for the extension otherwise the files are not\n# properly processed by doxygen.\n\nFILTER_PATTERNS        =\n\n# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using\n# INPUT_FILTER) will also be used to filter the input files that are used for\n# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).\n# The default value is: NO.\n\nFILTER_SOURCE_FILES    = NO\n\n# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file\n# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and\n# it is also possible to disable source filtering for a specific pattern using\n# *.ext= (so without naming a filter).\n# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.\n\nFILTER_SOURCE_PATTERNS =\n\n# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that\n# is part of the input, its contents will be placed on the main page\n# (index.html). This can be useful if you have a project on for instance GitHub\n# and want to reuse the introduction page also for the doxygen output.\n\nUSE_MDFILE_AS_MAINPAGE =\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\n# If the SOURCE_BROWSER tag is set to YES then a list of source files will be\n# generated. Documented entities will be cross-referenced with these sources.\n#\n# Note: To get rid of all source code in the generated output, make sure that\n# also VERBATIM_HEADERS is set to NO.\n# The default value is: NO.\n\nSOURCE_BROWSER         = NO\n\n# Setting the INLINE_SOURCES tag to YES will include the body of functions,\n# classes and enums directly into the documentation.\n# The default value is: NO.\n\nINLINE_SOURCES         = NO\n\n# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any\n# special comment blocks from generated source code fragments. Normal C, C++ and\n# Fortran comments will always remain visible.\n# The default value is: YES.\n\nSTRIP_CODE_COMMENTS    = YES\n\n# If the REFERENCED_BY_RELATION tag is set to YES then for each documented\n# function all documented functions referencing it will be listed.\n# The default value is: NO.\n\nREFERENCED_BY_RELATION = NO\n\n# If the REFERENCES_RELATION tag is set to YES then for each documented function\n# all documented entities called/used by that function will be listed.\n# The default value is: NO.\n\nREFERENCES_RELATION    = NO\n\n# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set\n# to YES then the hyperlinks from functions in REFERENCES_RELATION and\n# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will\n# link to the documentation.\n# The default value is: YES.\n\nREFERENCES_LINK_SOURCE = YES\n\n# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the\n# source code will show a tooltip with additional information such as prototype,\n# brief description and links to the definition and documentation. Since this\n# will make the HTML file larger and loading of large files a bit slower, you\n# can opt to disable this feature.\n# The default value is: YES.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nSOURCE_TOOLTIPS        = YES\n\n# If the USE_HTAGS tag is set to YES then the references to source code will\n# point to the HTML generated by the htags(1) tool instead of doxygen built-in\n# source browser. The htags tool is part of GNU's global source tagging system\n# (see http://www.gnu.org/software/global/global.html). You will need version\n# 4.8.6 or higher.\n#\n# To use it do the following:\n# - Install the latest version of global\n# - Enable SOURCE_BROWSER and USE_HTAGS in the config file\n# - Make sure the INPUT points to the root of the source tree\n# - Run doxygen as normal\n#\n# Doxygen will invoke htags (and that will in turn invoke gtags), so these\n# tools must be available from the command line (i.e. in the search path).\n#\n# The result: instead of the source browser generated by doxygen, the links to\n# source code will now point to the output of htags.\n# The default value is: NO.\n# This tag requires that the tag SOURCE_BROWSER is set to YES.\n\nUSE_HTAGS              = NO\n\n# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a\n# verbatim copy of the header file for each class for which an include is\n# specified. Set to NO to disable this.\n# See also: Section \\class.\n# The default value is: YES.\n\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\n# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all\n# compounds will be generated. Enable this if the project contains a lot of\n# classes, structs, unions or interfaces.\n# The default value is: YES.\n\nALPHABETICAL_INDEX     = YES\n\n# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in\n# which the alphabetical index list will be split.\n# Minimum value: 1, maximum value: 20, default value: 5.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nCOLS_IN_ALPHA_INDEX    = 5\n\n# In case all classes in a project start with a common prefix, all classes will\n# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag\n# can be used to specify a prefix (or a list of prefixes) that should be ignored\n# while generating the index headers.\n# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.\n\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the HTML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output\n# The default value is: YES.\n\nGENERATE_HTML          = YES\n\n# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_OUTPUT            = html\n\n# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each\n# generated HTML page (for example: .htm, .php, .asp).\n# The default value is: .html.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FILE_EXTENSION    = .html\n\n# The HTML_HEADER tag can be used to specify a user-defined HTML header file for\n# each generated HTML page. If the tag is left blank doxygen will generate a\n# standard header.\n#\n# To get valid HTML the header file that includes any scripts and style sheets\n# that doxygen needs, which is dependent on the configuration options used (e.g.\n# the setting GENERATE_TREEVIEW). It is highly recommended to start with a\n# default header using\n# doxygen -w html new_header.html new_footer.html new_stylesheet.css\n# YourConfigFile\n# and then modify the file new_header.html. See also section \"Doxygen usage\"\n# for information on how to generate the default header that doxygen normally\n# uses.\n# Note: The header is subject to change so you typically have to regenerate the\n# default header when upgrading to a newer version of doxygen. For a description\n# of the possible markers and block names see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_HEADER            =\n\n# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each\n# generated HTML page. If the tag is left blank doxygen will generate a standard\n# footer. See HTML_HEADER for more information on how to generate a default\n# footer and what special commands can be used inside the footer. See also\n# section \"Doxygen usage\" for information on how to generate the default footer\n# that doxygen normally uses.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_FOOTER            =\n\n# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style\n# sheet that is used by each HTML page. It can be used to fine-tune the look of\n# the HTML output. If left blank doxygen will generate a default style sheet.\n# See also section \"Doxygen usage\" for information on how to generate the style\n# sheet that doxygen normally uses.\n# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as\n# it is more robust and this tag (HTML_STYLESHEET) will in the future become\n# obsolete.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_STYLESHEET        =\n\n# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# cascading style sheets that are included after the standard style sheets\n# created by doxygen. Using this option one can overrule certain style aspects.\n# This is preferred over using HTML_STYLESHEET since it does not replace the\n# standard style sheet and is therefore more robust against future updates.\n# Doxygen will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list). For an example see the documentation.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_STYLESHEET  =\n\n# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the HTML output directory. Note\n# that these files will be copied to the base HTML output directory. Use the\n# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these\n# files. In the HTML_STYLESHEET file, use the file name only. Also note that the\n# files will be copied as-is; there are no commands or markers available.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_EXTRA_FILES       =\n\n# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen\n# will adjust the colors in the style sheet and background images according to\n# this color. Hue is specified as an angle on a colorwheel, see\n# http://en.wikipedia.org/wiki/Hue for more information. For instance the value\n# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300\n# purple, and 360 is red again.\n# Minimum value: 0, maximum value: 359, default value: 220.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_HUE    = 220\n\n# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors\n# in the HTML output. For a value of 0 the output will use grayscales only. A\n# value of 255 will produce the most vivid colors.\n# Minimum value: 0, maximum value: 255, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_SAT    = 100\n\n# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the\n# luminance component of the colors in the HTML output. Values below 100\n# gradually make the output lighter, whereas values above 100 make the output\n# darker. The value divided by 100 is the actual gamma applied, so 80 represents\n# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not\n# change the gamma.\n# Minimum value: 40, maximum value: 240, default value: 80.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_COLORSTYLE_GAMMA  = 80\n\n# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML\n# page will contain the date and time when the page was generated. Setting this\n# to YES can help to show when doxygen was last run and thus if the\n# documentation is up to date.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_TIMESTAMP         = NO\n\n# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML\n# documentation will contain sections that can be hidden and shown after the\n# page has loaded.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_DYNAMIC_SECTIONS  = NO\n\n# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries\n# shown in the various tree structured indices initially; the user can expand\n# and collapse entries dynamically later on. Doxygen will expand the tree to\n# such a level that at most the specified number of entries are visible (unless\n# a fully collapsed tree already exceeds this amount). So setting the number of\n# entries 1 will produce a full collapsed tree by default. 0 is a special value\n# representing an infinite number of entries and will result in a full expanded\n# tree by default.\n# Minimum value: 0, maximum value: 9999, default value: 100.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nHTML_INDEX_NUM_ENTRIES = 100\n\n# If the GENERATE_DOCSET tag is set to YES, additional index files will be\n# generated that can be used as input for Apple's Xcode 3 integrated development\n# environment (see: http://developer.apple.com/tools/xcode/), introduced with\n# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a\n# Makefile in the HTML output directory. Running make will produce the docset in\n# that directory and running make install will install the docset in\n# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at\n# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html\n# for more information.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_DOCSET        = NO\n\n# This tag determines the name of the docset feed. A documentation feed provides\n# an umbrella under which multiple documentation sets from a single provider\n# (such as a company or product suite) can be grouped.\n# The default value is: Doxygen generated docs.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_FEEDNAME        = \"Doxygen generated docs\"\n\n# This tag specifies a string that should uniquely identify the documentation\n# set bundle. This should be a reverse domain-name style string, e.g.\n# com.mycompany.MyDocSet. Doxygen will append .docset to the name.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_BUNDLE_ID       = org.doxygen.Project\n\n# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify\n# the documentation publisher. This should be a reverse domain-name style\n# string, e.g. com.mycompany.MyDocSet.documentation.\n# The default value is: org.doxygen.Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_ID    = org.doxygen.Publisher\n\n# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.\n# The default value is: Publisher.\n# This tag requires that the tag GENERATE_DOCSET is set to YES.\n\nDOCSET_PUBLISHER_NAME  = Publisher\n\n# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three\n# additional HTML index files: index.hhp, index.hhc, and index.hhk. The\n# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop\n# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on\n# Windows.\n#\n# The HTML Help Workshop contains a compiler that can convert all HTML output\n# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML\n# files are now used as the Windows 98 help format, and will replace the old\n# Windows help format (.hlp) on all Windows platforms in the future. Compressed\n# HTML files also contain an index, a table of contents, and you can search for\n# words in the documentation. The HTML workshop also contains a viewer for\n# compressed HTML files.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_HTMLHELP      = NO\n\n# The CHM_FILE tag can be used to specify the file name of the resulting .chm\n# file. You can add a path in front of the file if the result should not be\n# written to the html output directory.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_FILE               =\n\n# The HHC_LOCATION tag can be used to specify the location (absolute path\n# including file name) of the HTML help compiler (hhc.exe). If non-empty,\n# doxygen will try to run the HTML help compiler on the generated index.hhp.\n# The file has to be specified with full path.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nHHC_LOCATION           =\n\n# The GENERATE_CHI flag controls if a separate .chi index file is generated\n# (YES) or that it should be included in the master .chm file (NO).\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nGENERATE_CHI           = NO\n\n# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)\n# and project file content.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nCHM_INDEX_ENCODING     =\n\n# The BINARY_TOC flag controls whether a binary table of contents is generated\n# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it\n# enables the Previous and Next buttons.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nBINARY_TOC             = NO\n\n# The TOC_EXPAND flag can be set to YES to add extra items for group members to\n# the table of contents of the HTML help documentation and to the tree view.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTMLHELP is set to YES.\n\nTOC_EXPAND             = NO\n\n# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and\n# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that\n# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help\n# (.qch) of the generated HTML documentation.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_QHP           = NO\n\n# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify\n# the file name of the resulting .qch file. The path specified is relative to\n# the HTML output folder.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQCH_FILE               =\n\n# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help\n# Project output. For more information please see Qt Help Project / Namespace\n# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_NAMESPACE          = org.doxygen.Project\n\n# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt\n# Help Project output. For more information please see Qt Help Project / Virtual\n# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-\n# folders).\n# The default value is: doc.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_VIRTUAL_FOLDER     = doc\n\n# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom\n# filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_NAME   =\n\n# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the\n# custom filter to add. For more information please see Qt Help Project / Custom\n# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-\n# filters).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_CUST_FILTER_ATTRS  =\n\n# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this\n# project's filter section matches. Qt Help Project / Filter Attributes (see:\n# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHP_SECT_FILTER_ATTRS  =\n\n# The QHG_LOCATION tag can be used to specify the location of Qt's\n# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the\n# generated .qhp file.\n# This tag requires that the tag GENERATE_QHP is set to YES.\n\nQHG_LOCATION           =\n\n# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be\n# generated, together with the HTML files, they form an Eclipse help plugin. To\n# install this plugin and make it available under the help contents menu in\n# Eclipse, the contents of the directory containing the HTML and XML files needs\n# to be copied into the plugins directory of eclipse. The name of the directory\n# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.\n# After copying Eclipse needs to be restarted before the help appears.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_ECLIPSEHELP   = NO\n\n# A unique identifier for the Eclipse help plugin. When installing the plugin\n# the directory name containing the HTML and XML files should also have this\n# name. Each documentation set should have its own identifier.\n# The default value is: org.doxygen.Project.\n# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.\n\nECLIPSE_DOC_ID         = org.doxygen.Project\n\n# If you want full control over the layout of the generated HTML pages it might\n# be necessary to disable the index and replace it with your own. The\n# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top\n# of each HTML page. A value of NO enables the index and the value YES disables\n# it. Since the tabs in the index contain the same information as the navigation\n# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nDISABLE_INDEX          = NO\n\n# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index\n# structure should be generated to display hierarchical information. If the tag\n# value is set to YES, a side panel will be generated containing a tree-like\n# index structure (just like the one that is generated for HTML Help). For this\n# to work a browser that supports JavaScript, DHTML, CSS and frames is required\n# (i.e. any modern browser). Windows users are probably better off using the\n# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can\n# further fine-tune the look of the index. As an example, the default style\n# sheet generated by doxygen has an example that shows how to put an image at\n# the root of the tree instead of the PROJECT_NAME. Since the tree basically has\n# the same information as the tab index, you could consider setting\n# DISABLE_INDEX to YES when enabling this option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nGENERATE_TREEVIEW      = NO\n\n# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that\n# doxygen will group on one line in the generated HTML documentation.\n#\n# Note that a value of 0 will completely suppress the enum values from appearing\n# in the overview section.\n# Minimum value: 0, maximum value: 20, default value: 4.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nENUM_VALUES_PER_LINE   = 4\n\n# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used\n# to set the initial width (in pixels) of the frame in which the tree is shown.\n# Minimum value: 0, maximum value: 1500, default value: 250.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nTREEVIEW_WIDTH         = 250\n\n# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to\n# external symbols imported via tag files in a separate window.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nEXT_LINKS_IN_WINDOW    = NO\n\n# Use this tag to change the font size of LaTeX formulas included as images in\n# the HTML documentation. When you change the font size after a successful\n# doxygen run you need to manually remove any form_*.png images from the HTML\n# output directory to force them to be regenerated.\n# Minimum value: 8, maximum value: 50, default value: 10.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_FONTSIZE       = 10\n\n# Use the FORMULA_TRANPARENT tag to determine whether or not the images\n# generated for formulas are transparent PNGs. Transparent PNGs are not\n# supported properly for IE 6.0, but are supported on all modern browsers.\n#\n# Note that when changing this option you need to delete any form_*.png files in\n# the HTML output directory before the changes have effect.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nFORMULA_TRANSPARENT    = YES\n\n# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see\n# http://www.mathjax.org) which uses client side Javascript for the rendering\n# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX\n# installed or if you want to formulas look prettier in the HTML output. When\n# enabled you may also need to install MathJax separately and configure the path\n# to it using the MATHJAX_RELPATH option.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nUSE_MATHJAX            = NO\n\n# When MathJax is enabled you can set the default output format to be used for\n# the MathJax output. See the MathJax site (see:\n# http://docs.mathjax.org/en/latest/output.html) for more details.\n# Possible values are: HTML-CSS (which is slower, but has the best\n# compatibility), NativeMML (i.e. MathML) and SVG.\n# The default value is: HTML-CSS.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_FORMAT         = HTML-CSS\n\n# When MathJax is enabled you need to specify the location relative to the HTML\n# output directory using the MATHJAX_RELPATH option. The destination directory\n# should contain the MathJax.js script. For instance, if the mathjax directory\n# is located at the same level as the HTML output directory, then\n# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax\n# Content Delivery Network so you can quickly see the result without installing\n# MathJax. However, it is strongly recommended to install a local copy of\n# MathJax from http://www.mathjax.org before deployment.\n# The default value is: http://cdn.mathjax.org/mathjax/latest.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest\n\n# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax\n# extension names that should be enabled during MathJax rendering. For example\n# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_EXTENSIONS     =\n\n# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces\n# of code that will be used on startup of the MathJax code. See the MathJax site\n# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an\n# example see the documentation.\n# This tag requires that the tag USE_MATHJAX is set to YES.\n\nMATHJAX_CODEFILE       =\n\n# When the SEARCHENGINE tag is enabled doxygen will generate a search box for\n# the HTML output. The underlying search engine uses javascript and DHTML and\n# should work on any modern browser. Note that when using HTML help\n# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)\n# there is already a search function so this one should typically be disabled.\n# For large projects the javascript based search engine can be slow, then\n# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to\n# search using the keyboard; to jump to the search box use <access key> + S\n# (what the <access key> is depends on the OS and browser, but it is typically\n# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down\n# key> to jump into the search results window, the results can be navigated\n# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel\n# the search. The filter options can be selected when the cursor is inside the\n# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>\n# to select a filter and <Enter> or <escape> to activate or cancel the filter\n# option.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_HTML is set to YES.\n\nSEARCHENGINE           = YES\n\n# When the SERVER_BASED_SEARCH tag is enabled the search engine will be\n# implemented using a web server instead of a web client using Javascript. There\n# are two flavors of web server based searching depending on the EXTERNAL_SEARCH\n# setting. When disabled, doxygen will generate a PHP script for searching and\n# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing\n# and searching needs to be provided by external tools. See the section\n# \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSERVER_BASED_SEARCH    = NO\n\n# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP\n# script for searching. Instead the search results are written to an XML file\n# which needs to be processed by an external indexer. Doxygen will invoke an\n# external search engine pointed to by the SEARCHENGINE_URL option to obtain the\n# search results.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: http://xapian.org/).\n#\n# See the section \"External Indexing and Searching\" for details.\n# The default value is: NO.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH        = NO\n\n# The SEARCHENGINE_URL should point to a search engine hosted by a web server\n# which will return the search results when EXTERNAL_SEARCH is enabled.\n#\n# Doxygen ships with an example indexer (doxyindexer) and search engine\n# (doxysearch.cgi) which are based on the open source search engine library\n# Xapian (see: http://xapian.org/). See the section \"External Indexing and\n# Searching\" for details.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHENGINE_URL       =\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed\n# search data is written to a file for indexing by an external tool. With the\n# SEARCHDATA_FILE tag the name of this file can be specified.\n# The default file is: searchdata.xml.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nSEARCHDATA_FILE        = searchdata.xml\n\n# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the\n# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is\n# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple\n# projects and redirect the results back to the right project.\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTERNAL_SEARCH_ID     =\n\n# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen\n# projects other than the one defined by this configuration file, but that are\n# all added to the same external search index. Each project needs to have a\n# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of\n# to a relative location where the documentation can be found. The format is:\n# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...\n# This tag requires that the tag SEARCHENGINE is set to YES.\n\nEXTRA_SEARCH_MAPPINGS  =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the LaTeX output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.\n# The default value is: YES.\n\nGENERATE_LATEX         = YES\n\n# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_OUTPUT           = latex\n\n# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be\n# invoked.\n#\n# Note that when enabling USE_PDFLATEX this option is only used for generating\n# bitmaps for formulas in the HTML output, but not in the Makefile that is\n# written to the output directory.\n# The default file is: latex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_CMD_NAME         = latex\n\n# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate\n# index for LaTeX.\n# The default file is: makeindex.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nMAKEINDEX_CMD_NAME     = makeindex\n\n# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nCOMPACT_LATEX          = NO\n\n# The PAPER_TYPE tag can be used to set the paper type that is used by the\n# printer.\n# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x\n# 14 inches) and executive (7.25 x 10.5 inches).\n# The default value is: a4.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPAPER_TYPE             = a4\n\n# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names\n# that should be included in the LaTeX output. The package can be specified just\n# by its name or with the correct syntax as to be used with the LaTeX\n# \\usepackage command. To get the times font for instance you can specify :\n# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}\n# To use the option intlimits with the amsmath package you can specify:\n# EXTRA_PACKAGES=[intlimits]{amsmath}\n# If left blank no extra packages will be included.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nEXTRA_PACKAGES         =\n\n# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the\n# generated LaTeX document. The header should contain everything until the first\n# chapter. If it is left blank doxygen will generate a standard header. See\n# section \"Doxygen usage\" for information on how to let doxygen write the\n# default header to a separate file.\n#\n# Note: Only use a user-defined header if you know what you are doing! The\n# following commands have a special meaning inside the header: $title,\n# $datetime, $date, $doxygenversion, $projectname, $projectnumber,\n# $projectbrief, $projectlogo. Doxygen will replace $title with the empty\n# string, for the replacement values of the other commands the user is referred\n# to HTML_HEADER.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HEADER           =\n\n# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the\n# generated LaTeX document. The footer should contain everything after the last\n# chapter. If it is left blank doxygen will generate a standard footer. See\n# LATEX_HEADER for more information on how to generate a default footer and what\n# special commands can be used inside the footer.\n#\n# Note: Only use a user-defined footer if you know what you are doing!\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_FOOTER           =\n\n# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined\n# LaTeX style sheets that are included after the standard style sheets created\n# by doxygen. Using this option one can overrule certain style aspects. Doxygen\n# will copy the style sheet files to the output directory.\n# Note: The order of the extra style sheet files is of importance (e.g. the last\n# style sheet in the list overrules the setting of the previous ones in the\n# list).\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_STYLESHEET =\n\n# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or\n# other source files which should be copied to the LATEX_OUTPUT output\n# directory. Note that the files will be copied as-is; there are no commands or\n# markers available.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_EXTRA_FILES      =\n\n# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is\n# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will\n# contain links (just like the HTML output) instead of page references. This\n# makes the output suitable for online browsing using a PDF viewer.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nPDF_HYPERLINKS         = YES\n\n# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate\n# the PDF file directly from the LaTeX files. Set this option to YES, to get a\n# higher quality PDF documentation.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nUSE_PDFLATEX           = YES\n\n# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode\n# command to the generated LaTeX files. This will instruct LaTeX to keep running\n# if errors occur, instead of asking the user for help. This option is also used\n# when generating formulas in HTML.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BATCHMODE        = NO\n\n# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the\n# index chapters (such as File Index, Compound Index, etc.) in the output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_HIDE_INDICES     = NO\n\n# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source\n# code with syntax highlighting in the LaTeX output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_SOURCE_CODE      = NO\n\n# The LATEX_BIB_STYLE tag can be used to specify the style to use for the\n# bibliography, e.g. plainnat, or ieeetr. See\n# http://en.wikipedia.org/wiki/BibTeX and \\cite for more info.\n# The default value is: plain.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_BIB_STYLE        = plain\n\n# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated\n# page will contain the date and time when the page was generated. Setting this\n# to NO can help when comparing the output of multiple runs.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_LATEX is set to YES.\n\nLATEX_TIMESTAMP        = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the RTF output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The\n# RTF output is optimized for Word 97 and may not look too pretty with other RTF\n# readers/editors.\n# The default value is: NO.\n\nGENERATE_RTF           = NO\n\n# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: rtf.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_OUTPUT             = rtf\n\n# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF\n# documents. This may be useful for small projects and may help to save some\n# trees in general.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nCOMPACT_RTF            = NO\n\n# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will\n# contain hyperlink fields. The RTF file will contain links (just like the HTML\n# output) instead of page references. This makes the output suitable for online\n# browsing using Word or some other Word compatible readers that support those\n# fields.\n#\n# Note: WordPad (write) and others do not support links.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_HYPERLINKS         = NO\n\n# Load stylesheet definitions from file. Syntax is similar to doxygen's config\n# file, i.e. a series of assignments. You only have to provide replacements,\n# missing definitions are set to their default value.\n#\n# See also section \"Doxygen usage\" for information on how to generate the\n# default style sheet that doxygen normally uses.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_STYLESHEET_FILE    =\n\n# Set optional variables used in the generation of an RTF document. Syntax is\n# similar to doxygen's config file. A template extensions file can be generated\n# using doxygen -e rtf extensionFile.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_EXTENSIONS_FILE    =\n\n# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code\n# with syntax highlighting in the RTF output.\n#\n# Note that which sources are shown also depends on other settings such as\n# SOURCE_BROWSER.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_RTF is set to YES.\n\nRTF_SOURCE_CODE        = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the man page output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for\n# classes and files.\n# The default value is: NO.\n\nGENERATE_MAN           = NO\n\n# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it. A directory man3 will be created inside the directory specified by\n# MAN_OUTPUT.\n# The default directory is: man.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_OUTPUT             = man\n\n# The MAN_EXTENSION tag determines the extension that is added to the generated\n# man pages. In case the manual section does not start with a number, the number\n# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is\n# optional.\n# The default value is: .3.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_EXTENSION          = .3\n\n# The MAN_SUBDIR tag determines the name of the directory created within\n# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by\n# MAN_EXTENSION with the initial . removed.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_SUBDIR             =\n\n# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it\n# will generate one additional man file for each entity documented in the real\n# man page(s). These additional files only source the real man page, but without\n# them the man command would be unable to find the correct page.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_MAN is set to YES.\n\nMAN_LINKS              = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that\n# captures the structure of the code including all documentation.\n# The default value is: NO.\n\nGENERATE_XML           = NO\n\n# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a\n# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of\n# it.\n# The default directory is: xml.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_OUTPUT             = xml\n\n# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program\n# listings (including syntax highlighting and cross-referencing information) to\n# the XML output. Note that enabling this will significantly increase the size\n# of the XML output.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_XML is set to YES.\n\nXML_PROGRAMLISTING     = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the DOCBOOK output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files\n# that can be used to generate PDF.\n# The default value is: NO.\n\nGENERATE_DOCBOOK       = NO\n\n# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.\n# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in\n# front of it.\n# The default directory is: docbook.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_OUTPUT         = docbook\n\n# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the\n# program listings (including syntax highlighting and cross-referencing\n# information) to the DOCBOOK output. Note that enabling this will significantly\n# increase the size of the DOCBOOK output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_DOCBOOK is set to YES.\n\nDOCBOOK_PROGRAMLISTING = NO\n\n#---------------------------------------------------------------------------\n# Configuration options for the AutoGen Definitions output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an\n# AutoGen Definitions (see http://autogen.sf.net) file that captures the\n# structure of the code including all documentation. Note that this feature is\n# still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_AUTOGEN_DEF   = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the Perl module output\n#---------------------------------------------------------------------------\n\n# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module\n# file that captures the structure of the code including all documentation.\n#\n# Note that this feature is still experimental and incomplete at the moment.\n# The default value is: NO.\n\nGENERATE_PERLMOD       = NO\n\n# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary\n# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI\n# output from the Perl module output.\n# The default value is: NO.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_LATEX          = NO\n\n# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely\n# formatted so it can be parsed by a human reader. This is useful if you want to\n# understand what is going on. On the other hand, if this tag is set to NO, the\n# size of the Perl module output will be much smaller and Perl will parse it\n# just the same.\n# The default value is: YES.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_PRETTY         = YES\n\n# The names of the make variables in the generated doxyrules.make file are\n# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful\n# so different doxyrules.make files included by the same Makefile don't\n# overwrite each other's variables.\n# This tag requires that the tag GENERATE_PERLMOD is set to YES.\n\nPERLMOD_MAKEVAR_PREFIX =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\n# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all\n# C-preprocessor directives found in the sources and include files.\n# The default value is: YES.\n\nENABLE_PREPROCESSING   = YES\n\n# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names\n# in the source code. If set to NO, only conditional compilation will be\n# performed. Macro expansion can be done in a controlled way by setting\n# EXPAND_ONLY_PREDEF to YES.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nMACRO_EXPANSION        = NO\n\n# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then\n# the macro expansion is limited to the macros specified with the PREDEFINED and\n# EXPAND_AS_DEFINED tags.\n# The default value is: NO.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_ONLY_PREDEF     = NO\n\n# If the SEARCH_INCLUDES tag is set to YES, the include files in the\n# INCLUDE_PATH will be searched if a #include is found.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSEARCH_INCLUDES        = YES\n\n# The INCLUDE_PATH tag can be used to specify one or more directories that\n# contain include files that are not input files but should be processed by the\n# preprocessor.\n# This tag requires that the tag SEARCH_INCLUDES is set to YES.\n\nINCLUDE_PATH           =\n\n# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard\n# patterns (like *.h and *.hpp) to filter out the header-files in the\n# directories. If left blank, the patterns specified with FILE_PATTERNS will be\n# used.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nINCLUDE_FILE_PATTERNS  =\n\n# The PREDEFINED tag can be used to specify one or more macro names that are\n# defined before the preprocessor is started (similar to the -D option of e.g.\n# gcc). The argument of the tag is a list of macros of the form: name or\n# name=definition (no spaces). If the definition and the \"=\" are omitted, \"=1\"\n# is assumed. To prevent a macro definition from being undefined via #undef or\n# recursively expanded use the := operator instead of the = operator.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nPREDEFINED             =\n\n# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this\n# tag can be used to specify a list of macro names that should be expanded. The\n# macro definition that is found in the sources will be used. Use the PREDEFINED\n# tag if you want to use a different macro definition that overrules the\n# definition found in the source code.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nEXPAND_AS_DEFINED      =\n\n# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will\n# remove all references to function-like macros that are alone on a line, have\n# an all uppercase name, and do not end with a semicolon. Such function macros\n# are typically used for boiler-plate code, and will confuse the parser if not\n# removed.\n# The default value is: YES.\n# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.\n\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\n# The TAGFILES tag can be used to specify one or more tag files. For each tag\n# file the location of the external documentation should be added. The format of\n# a tag file without this location is as follows:\n# TAGFILES = file1 file2 ...\n# Adding location for the tag files is done as follows:\n# TAGFILES = file1=loc1 \"file2 = loc2\" ...\n# where loc1 and loc2 can be relative or absolute paths or URLs. See the\n# section \"Linking to external documentation\" for more information about the use\n# of tag files.\n# Note: Each tag file must have a unique name (where the name does NOT include\n# the path). If a tag file is not located in the directory in which doxygen is\n# run, you must also specify the path to the tagfile here.\n\nTAGFILES               =\n\n# When a file name is specified after GENERATE_TAGFILE, doxygen will create a\n# tag file that is based on the input files it reads. See section \"Linking to\n# external documentation\" for more information about the usage of tag files.\n\nGENERATE_TAGFILE       =\n\n# If the ALLEXTERNALS tag is set to YES, all external class will be listed in\n# the class index. If set to NO, only the inherited external classes will be\n# listed.\n# The default value is: NO.\n\nALLEXTERNALS           = NO\n\n# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed\n# in the modules index. If set to NO, only the current project's groups will be\n# listed.\n# The default value is: YES.\n\nEXTERNAL_GROUPS        = YES\n\n# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in\n# the related pages index. If set to NO, only the current project's pages will\n# be listed.\n# The default value is: YES.\n\nEXTERNAL_PAGES         = YES\n\n# The PERL_PATH should be the absolute path and name of the perl script\n# interpreter (i.e. the result of 'which perl').\n# The default file (with absolute path) is: /usr/bin/perl.\n\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\n# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram\n# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to\n# NO turns the diagrams off. Note that this option also works with HAVE_DOT\n# disabled, but it is recommended to install and use dot, since it yields more\n# powerful graphs.\n# The default value is: YES.\n\nCLASS_DIAGRAMS         = YES\n\n# You can define message sequence charts within doxygen comments using the \\msc\n# command. Doxygen will then run the mscgen tool (see:\n# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the\n# documentation. The MSCGEN_PATH tag allows you to specify the directory where\n# the mscgen tool resides. If left empty the tool is assumed to be found in the\n# default search path.\n\nMSCGEN_PATH            =\n\n# You can include diagrams made with dia in doxygen documentation. Doxygen will\n# then run dia to produce the diagram and insert it in the documentation. The\n# DIA_PATH tag allows you to specify the directory where the dia binary resides.\n# If left empty dia is assumed to be found in the default search path.\n\nDIA_PATH               =\n\n# If set to YES the inheritance and collaboration graphs will hide inheritance\n# and usage relations if the target is undocumented or is not a class.\n# The default value is: YES.\n\nHIDE_UNDOC_RELATIONS   = YES\n\n# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is\n# available from the path. This tool is part of Graphviz (see:\n# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent\n# Bell Labs. The other options in this section have no effect if this option is\n# set to NO\n# The default value is: NO.\n\nHAVE_DOT               = NO\n\n# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed\n# to run in parallel. When set to 0 doxygen will base this on the number of\n# processors available in the system. You can set it explicitly to a value\n# larger than 0 to get control over the balance between CPU load and processing\n# speed.\n# Minimum value: 0, maximum value: 32, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_NUM_THREADS        = 0\n\n# When you want a differently looking font in the dot files that doxygen\n# generates you can specify the font name using DOT_FONTNAME. You need to make\n# sure dot is able to find the font, which can be done by putting it in a\n# standard location or by setting the DOTFONTPATH environment variable or by\n# setting DOT_FONTPATH to the directory containing the font.\n# The default value is: Helvetica.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTNAME           = Helvetica\n\n# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of\n# dot graphs.\n# Minimum value: 4, maximum value: 24, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTSIZE           = 10\n\n# By default doxygen will tell dot to use the default font as specified with\n# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set\n# the path where dot can find it using this tag.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_FONTPATH           =\n\n# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for\n# each documented class showing the direct and indirect inheritance relations.\n# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCLASS_GRAPH            = YES\n\n# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a\n# graph for each documented class showing the direct and indirect implementation\n# dependencies (inheritance, containment, and class references variables) of the\n# class with other documented classes.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCOLLABORATION_GRAPH    = YES\n\n# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for\n# groups, showing the direct groups dependencies.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGROUP_GRAPHS           = YES\n\n# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and\n# collaboration diagrams in a style similar to the OMG's Unified Modeling\n# Language.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LOOK               = NO\n\n# If the UML_LOOK tag is enabled, the fields and methods are shown inside the\n# class node. If there are many fields or methods and many nodes the graph may\n# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the\n# number of items for each type to make the size more manageable. Set this to 0\n# for no limit. Note that the threshold may be exceeded by 50% before the limit\n# is enforced. So when you set the threshold to 10, up to 15 fields may appear,\n# but if the number exceeds 15, the total amount of fields shown is limited to\n# 10.\n# Minimum value: 0, maximum value: 100, default value: 10.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nUML_LIMIT_NUM_FIELDS   = 10\n\n# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and\n# collaboration graphs will show the relations between templates and their\n# instances.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nTEMPLATE_RELATIONS     = NO\n\n# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to\n# YES then doxygen will generate a graph for each documented file showing the\n# direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDE_GRAPH          = YES\n\n# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are\n# set to YES then doxygen will generate a graph for each documented file showing\n# the direct and indirect include dependencies of the file with other documented\n# files.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINCLUDED_BY_GRAPH      = YES\n\n# If the CALL_GRAPH tag is set to YES then doxygen will generate a call\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable call graphs for selected\n# functions only using the \\callgraph command. Disabling a call graph can be\n# accomplished by means of the command \\hidecallgraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALL_GRAPH             = NO\n\n# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller\n# dependency graph for every global function or class method.\n#\n# Note that enabling this option will significantly increase the time of a run.\n# So in most cases it will be better to enable caller graphs for selected\n# functions only using the \\callergraph command. Disabling a caller graph can be\n# accomplished by means of the command \\hidecallergraph.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nCALLER_GRAPH           = NO\n\n# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical\n# hierarchy of all classes instead of a textual one.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGRAPHICAL_HIERARCHY    = YES\n\n# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the\n# dependencies a directory has on other directories in a graphical way. The\n# dependency relations are determined by the #include relations between the\n# files in the directories.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDIRECTORY_GRAPH        = YES\n\n# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images\n# generated by dot. For an explanation of the image formats see the section\n# output formats in the documentation of the dot tool (Graphviz (see:\n# http://www.graphviz.org/)).\n# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order\n# to make the SVG files visible in IE 9+ (other browsers do not have this\n# requirement).\n# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,\n# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and\n# png:gdiplus:gdiplus.\n# The default value is: png.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_IMAGE_FORMAT       = png\n\n# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to\n# enable generation of interactive SVG images that allow zooming and panning.\n#\n# Note that this requires a modern browser other than Internet Explorer. Tested\n# and working are Firefox, Chrome, Safari, and Opera.\n# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make\n# the SVG files visible. Older versions of IE do not have SVG support.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nINTERACTIVE_SVG        = NO\n\n# The DOT_PATH tag can be used to specify the path where the dot tool can be\n# found. If left blank, it is assumed the dot tool can be found in the path.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_PATH               =\n\n# The DOTFILE_DIRS tag can be used to specify one or more directories that\n# contain dot files that are included in the documentation (see the \\dotfile\n# command).\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOTFILE_DIRS           =\n\n# The MSCFILE_DIRS tag can be used to specify one or more directories that\n# contain msc files that are included in the documentation (see the \\mscfile\n# command).\n\nMSCFILE_DIRS           =\n\n# The DIAFILE_DIRS tag can be used to specify one or more directories that\n# contain dia files that are included in the documentation (see the \\diafile\n# command).\n\nDIAFILE_DIRS           =\n\n# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the\n# path where java can find the plantuml.jar file. If left blank, it is assumed\n# PlantUML is not used or called during a preprocessing step. Doxygen will\n# generate a warning when it encounters a \\startuml command in this case and\n# will not generate output for the diagram.\n\nPLANTUML_JAR_PATH      =\n\n# When using plantuml, the specified paths are searched for files specified by\n# the !include statement in a plantuml block.\n\nPLANTUML_INCLUDE_PATH  =\n\n# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes\n# that will be shown in the graph. If the number of nodes in a graph becomes\n# larger than this value, doxygen will truncate the graph, which is visualized\n# by representing a node as a red box. Note that doxygen if the number of direct\n# children of the root node in a graph is already larger than\n# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that\n# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.\n# Minimum value: 0, maximum value: 10000, default value: 50.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_GRAPH_MAX_NODES    = 50\n\n# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs\n# generated by dot. A depth value of 3 means that only nodes reachable from the\n# root by following a path via at most 3 edges will be shown. Nodes that lay\n# further from the root node will be omitted. Note that setting this option to 1\n# or 2 may greatly reduce the computation time needed for large code bases. Also\n# note that the size of a graph can be further restricted by\n# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.\n# Minimum value: 0, maximum value: 1000, default value: 0.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nMAX_DOT_GRAPH_DEPTH    = 0\n\n# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent\n# background. This is disabled by default, because dot on Windows does not seem\n# to support this out of the box.\n#\n# Warning: Depending on the platform used, enabling this option may lead to\n# badly anti-aliased labels on the edges of a graph (i.e. they become hard to\n# read).\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_TRANSPARENT        = NO\n\n# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output\n# files in one run (i.e. multiple -o and -T options on the command line). This\n# makes dot run faster, but since only newer versions of dot (>1.8.10) support\n# this, this feature is disabled by default.\n# The default value is: NO.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_MULTI_TARGETS      = NO\n\n# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page\n# explaining the meaning of the various boxes and arrows in the dot generated\n# graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nGENERATE_LEGEND        = YES\n\n# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot\n# files that are used to generate the various graphs.\n# The default value is: YES.\n# This tag requires that the tag HAVE_DOT is set to YES.\n\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "deps/libcaption/LICENSE.txt",
    "content": "The MIT License\n\nCopyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "deps/libcaption/README.md",
    "content": "# version\nv0.8\nMatthew Szatmary m3u8@twitch.tv / matt@szatmary.org\n\n# libcaption\n\nlibcaption is a library written in C to aid in the creating and parsing of closed caption data, open sourced under the MIT license to use within community developed broadcast tools. To maintain consistency across platforms libcaption aims to implement a subset of EIA608, CEA708 as supported by the Apple iOS platform.\n\n608 support is currently limited to encoding and decoding the necessary control and preamble codes as well as support for the Basic North American, Special North American and Extended Western European character sets.\n\n708 support is limited to encoding the 608 data in NTSC field 1 user data type structure.\n\nIn addition, utility functions to create h.264 SEI (Supplementary enhancement information) NALUs (Network Abstraction Layer Unit) for inclusion into an h.264 elementary stream are provided.\n\nH.264 utility functions are limited to wrapping the 708 payload into a SEI NALU. This is accomplished by prepending the 708 payload with 3 bytes (nal_unit_type = 6, payloadType = 4, and PayloadSize = variable), and appending a stop bit encoded into a full byte (with a value of 127). In addition if the 708 payload contains an emulated start code, a three byte sequence equaling 0,0,1 an emulation prevention byte (3) is inserted. Functions to reverse this operation are also provided.\n\n## Characters\n| | | | | | | | | | | | | | | | | |\n|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|\n|BNA| |!|\"|#|$|%|&|’|(|)|á|+|,|-|.|/|\n|BNA|0|1|2|3|4|5|6|7|8|9|:|;|<|=|>|?|\n|BNA|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|\n|BNA|P|Q|R|S|T|U|V|W|X|Y|Z|[|é|]|í|ó|\n|BNA|ú|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|\n|BNA|p|q|r|s|t|u|v|w|x|y|z|ç|÷|Ñ|ñ|█|\n|SNA|®|°|½|¿|™|¢|£|♪|à|&nbsp;|è|â|ê|î|ô|û|\n|WES|Á|É|Ó|Ú|Ü|ü|‘|¡|*|'|—|©|℠|•|“|”|\n|WEF|À|Â|Ç|È|Ê|Ë|ë|Î|Ï|ï|Ô|Ù|ù|Û|«|»|\n|WEP|Ã|ã|Í|Ì|ì|Ò|ò|Õ|õ|{|}|&#92;|^|_|&#124;|~|\n|WEG|Ä|ä|Ö|ö|ß|¥|¤|¦|Å|å|Ø|ø|┌|┐|└|┘|\n\n* BNA = Basic North American character set\n* SNA = Special North American character set\n* WES = Extended Western European character set : Extended Spanish/Miscellaneous\n* WEF = Extended Western European character set : Extended French\n* WEP = Extended Western European character set : Portuguese\n* WEG = Extended Western European character set : German/Danish\n\n## Limitations\nCurrent B-frame support for caption creation is minimal. libcaption ensures no re-ordering of captions is required\non playback.\n\n## Build Directions\n# Mac Os/Linux\nInstall build dependencies (git, cmake, a compiler such as xcode, gcc or clang and optionally re2c and ffmpeg)\n* run `cmake . && make`\n* or to compile without re2c `cmake -DENABLE_RE2C=OFF . && make`\n* finally `sudo make install` to install\n# Windows\nI have never tested libcaption in windows. It is written in pure C with no dependencies,\nso there is no reason it would not work.\n"
  },
  {
    "path": "deps/libcaption/caption/caption.h",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#ifndef LIBCAPTION_H\n#define LIBCAPTION_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"eia608.h\"\n#include \"utf8.h\"\n#include \"xds.h\"\n\n// ssize_t is POSIX and does not exist on Windows\n#if defined(_MSC_VER)\n#if defined(_WIN64)\ntypedef signed long ssize_t;\n#else\ntypedef signed int ssize_t;\n#endif\n#endif\n\ntypedef enum {\n    LIBCAPTION_ERROR = 0,\n    LIBCAPTION_OK = 1,\n    LIBCAPTION_READY = 2\n} libcaption_stauts_t;\n\nstatic inline libcaption_stauts_t libcaption_status_update(libcaption_stauts_t old_stat, libcaption_stauts_t new_stat)\n{\n    return (LIBCAPTION_ERROR == old_stat || LIBCAPTION_ERROR == new_stat) ? LIBCAPTION_ERROR : (LIBCAPTION_READY == old_stat) ? LIBCAPTION_READY : new_stat;\n}\n\n#define SCREEN_ROWS 15\n#define SCREEN_COLS 32\n\ntypedef struct {\n    unsigned int uln : 1; //< underline\n    unsigned int sty : 3; //< style\n    utf8_char_t data[5]; //< 4 byte utf8 values plus null term\n} caption_frame_cell_t;\n\ntypedef struct {\n    caption_frame_cell_t cell[SCREEN_ROWS][SCREEN_COLS];\n} caption_frame_buffer_t;\n\ntypedef struct {\n    unsigned int uln : 1; //< underline\n    unsigned int sty : 3; //< style\n    unsigned int rup : 2; //< roll-up line count minus 1\n    int8_t row, col;\n    uint16_t cc_data;\n} caption_frame_state_t;\n\n// timestamp and duration are in seconds\ntypedef struct {\n    double timestamp;\n    xds_t xds;\n    caption_frame_state_t state;\n    caption_frame_buffer_t front;\n    caption_frame_buffer_t back;\n    caption_frame_buffer_t* write;\n    libcaption_stauts_t status;\n} caption_frame_t;\n\n/*!\n    \\brief Initializes an allocated caption_frame_t instance\n    \\param frame Pointer to prealocated caption_frame_t object\n*/\nvoid caption_frame_init(caption_frame_t* frame);\n/*! \\brief\n    \\param\n*/\nstatic inline int caption_frame_popon(caption_frame_t* frame) { return (frame->write == &frame->back) ? 1 : 0; }\n/*! \\brief\n    \\param\n*/\nstatic inline int caption_frame_painton(caption_frame_t* frame) { return (frame->write == &frame->front) ? 1 : 0; }\n/*! \\brief\n    \\param\n*/\nstatic const int _caption_frame_rollup[] = { 0, 2, 3, 4 };\nstatic inline int caption_frame_rollup(caption_frame_t* frame) { return _caption_frame_rollup[frame->state.rup]; }\n/*! \\brief\n    \\param\n*/\nstatic inline double caption_frame_timestamp(caption_frame_t* frame) { return frame->timestamp; }\n/*! \\brief Writes a single charcter to a caption_frame_t object\n    \\param frame A pointer to an allocted and initialized caption_frame_t object\n    \\param row Row position to write charcter, must be between 0 and SCREEN_ROWS-1\n    \\param col Column position to write charcter, must be between 0 and SCREEN_ROWS-1\n    \\param style Style to apply to charcter\n    \\param underline Set underline attribute, 0 = off any other value = on\n    \\param c pointer to a single valid utf8 charcter. Bytes are automatically determined, and a NULL terminator is not required\n*/\nint caption_frame_write_char(caption_frame_t* frame, int row, int col, eia608_style_t style, int underline, const utf8_char_t* c);\n/*! \\brief\n    \\param\n*/\nconst utf8_char_t* caption_frame_read_char(caption_frame_t* frame, int row, int col, eia608_style_t* style, int* underline);\n/*! \\brief\n    \\param\n*/\nlibcaption_stauts_t caption_frame_decode(caption_frame_t* frame, uint16_t cc_data, double timestamp);\n/*! \\brief\n    \\param\n*/\nint caption_frame_from_text(caption_frame_t* frame, const utf8_char_t* data);\n/*! \\brief\n    \\param\n*/\n#define CAPTION_FRAME_TEXT_BYTES (4 * ((SCREEN_COLS + 2) * SCREEN_ROWS) + 1)\nsize_t caption_frame_to_text(caption_frame_t* frame, utf8_char_t* data);\n/*! \\brief\n    \\param\n*/\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/caption/cea708.h",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#ifndef LIBCAPTION_CEA708_H\n#define LIBCAPTION_CEA708_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"caption.h\"\n#define CEA608_MAX_SIZE (255)\n\n////////////////////////////////////////////////////////////////////////////////\ntypedef enum {\n    cc_type_ntsc_cc_field_1 = 0,\n    cc_type_ntsc_cc_field_2 = 1,\n    cc_type_dtvcc_packet_data = 2,\n    cc_type_dtvcc_packet_start = 3,\n} cea708_cc_type_t;\n\ntypedef struct {\n    unsigned int marker_bits : 5;\n    unsigned int cc_valid : 1;\n    unsigned int cc_type : 2; // castable to cea708_cc_type_t\n    unsigned int cc_data : 16;\n} cc_data_t;\n\ntypedef struct {\n    unsigned int process_em_data_flag : 1;\n    unsigned int process_cc_data_flag : 1;\n    unsigned int additional_data_flag : 1;\n    unsigned int cc_count : 5;\n    unsigned int em_data : 8;\n    cc_data_t cc_data[32];\n} user_data_t;\n\n/*! \\brief\n    \\param\n*/\ncc_data_t cea708_encode_cc_data(int cc_valid, cea708_cc_type_t type, uint16_t cc_data);\n/*! \\brief\n    \\param\n*/\nint cea708_cc_count(user_data_t* data);\n/*! \\brief\n    \\param\n*/\nuint16_t cea708_cc_data(user_data_t* data, int index, int* valid, cea708_cc_type_t* type);\n////////////////////////////////////////////////////////////////////////////////\n\ntypedef enum {\n    country_united_states = 181,\n} itu_t_t35_country_code_t;\n\ntypedef enum {\n    t35_provider_direct_tv = 47,\n    t35_provider_atsc = 49,\n} itu_t_t35_provider_code_t;\n\ntypedef struct {\n    itu_t_t35_country_code_t country;\n    itu_t_t35_provider_code_t provider;\n    uint32_t user_identifier;\n    uint8_t user_data_type_code;\n    uint8_t directv_user_data_length;\n    user_data_t user_data;\n    double timestamp;\n} cea708_t;\n\nstatic const uint32_t GA94 = (('G' << 24) | ('A' << 16) | ('9' << 8) | '4');\nstatic const uint32_t DTG1 = (('D' << 24) | ('T' << 16) | ('G' << 8) | '1');\n\n/*! \\brief\n    \\param\n*/\nint cea708_init(cea708_t* cea708, double timestamp); // will confgure using HLS compatiable defaults\n/*! \\brief\n    \\param\n*/\nlibcaption_stauts_t cea708_parse_h264(const uint8_t* data, size_t size, cea708_t* cea708);\n/*! \\brief\n    \\param\n*/\nlibcaption_stauts_t cea708_parse_h262(const uint8_t* data, size_t size, cea708_t* cea708);\n/*! \\brief\n    \\param\n*/\nlibcaption_stauts_t cea708_to_caption_frame(caption_frame_t* frame, cea708_t* cea708);\n/*! \\brief\n    \\param\n*/\nint cea708_add_cc_data(cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data);\n/*! \\brief\n    \\param\n*/\nint cea708_render(cea708_t* cea708, uint8_t* data, size_t size);\n/*! \\brief\n    \\param\n*/\nvoid cea708_dump(cea708_t* cea708);\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/caption/dvtcc.h",
    "content": "#ifndef LIBCAPTION_CEA708_H\n#define LIBCAPTION_CEA708_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n////////////////////////////////////////////////////////////////////////////////\nstruct dtvcc_packet_t {\n    unsigned int sequence_number;\n    unsigned int packet_size;\n    unsigned int serice_number;\n};\n\n#defing DVTCC_SERVICE_NUMBER_UNKNOWN\n\n// static inline size_t dvtvcc_packet_size_bytes(const struct dtvcc_packet_t *dvtcc) { return dvtcc->packet_size*2-1;}\n\n////////////////////////////////////////////////////////////////////////////////\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/caption/eia608.h",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#ifndef LIBCAPTION_EIA608_H\n#define LIBCAPTION_EIA608_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"eia608_charmap.h\"\n#include \"utf8.h\"\n////////////////////////////////////////////////////////////////////////////////\n// Parity\n#define EIA608_BX(B, X) (((B) << (X)) & 0x80)\n#define EIA608_BP(B) ((B)&0x7F) | (0x80 ^ EIA608_BX((B), 1) ^ EIA608_BX((B), 2) ^ EIA608_BX((B), 3) ^ EIA608_BX((B), 4) ^ EIA608_BX((B), 5) ^ EIA608_BX((B), 6) ^ EIA608_BX((B), 7))\n#define EIA608_B2(B) EIA608_BP((B) + 0), EIA608_BP((B) + 1), EIA608_BP((B) + 2), EIA608_BP((B) + 3), EIA608_BP((B) + 4), EIA608_BP((B) + 5), EIA608_BP((B) + 6), EIA608_BP((B) + 7)\n#define EIA608_B1(B) EIA608_B2((B) + 0), EIA608_B2((B) + 8), EIA608_B2((B) + 16), EIA608_B2((B) + 24), EIA608_B2((B) + 32), EIA608_B2((B) + 40), EIA608_B2((B) + 48), EIA608_B2((B) + 56)\n\nstatic const uint8_t eia608_parity_table[] = { EIA608_B1(0), EIA608_B1(64) };\nextern const char* eia608_style_map[];\n\n#ifdef _MSC_VER\n#ifndef inline\n#define inline __inline\n#endif\n#endif\n\n/*! \\brief\n    \\param\n*/\nstatic inline uint8_t eia608_parity_byte(uint8_t cc_data) { return eia608_parity_table[0x7F & cc_data]; }\n/*! \\brief\n    \\param\n*/\nstatic inline uint16_t eia608_parity_word(uint16_t cc_data) { return (uint16_t)((eia608_parity_byte((uint8_t)(cc_data >> 8)) << 8) | eia608_parity_byte((uint8_t)cc_data)); }\n/*! \\brief\n    \\param\n*/\nstatic inline uint16_t eia608_parity(uint16_t cc_data) { return eia608_parity_word(cc_data); }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_parity_varify(uint16_t cc_data) { return eia608_parity_word(cc_data) == cc_data ? 1 : 0; }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_parity_strip(uint16_t cc_data) { return cc_data & 0x7F7F; }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_test_second_channel_bit(uint16_t cc_data) { return (cc_data & 0x0800); }\n\n////////////////////////////////////////////////////////////////////////////////\n// cc_data types\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_is_basicna(uint16_t cc_data) { return 0x0000 != (0x6000 & cc_data); /*&& 0x1F00 < (0x7F00 & cc_data);*/ }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_is_preamble(uint16_t cc_data) { return 0x1040 == (0x7040 & cc_data); }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_is_midrowchange(uint16_t cc_data) { return 0x1120 == (0x7770 & cc_data); }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_is_specialna(uint16_t cc_data) { return 0x1130 == (0x7770 & cc_data); }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_is_xds(uint16_t cc_data) { return 0x0000 == (0x7070 & cc_data) && 0x0000 != (0x0F0F & cc_data); }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_is_westeu(uint16_t cc_data) { return 0x1220 == (0x7660 & cc_data); }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_is_control(uint16_t cc_data) { return 0x1420 == (0x7670 & cc_data) || 0x1720 == (0x7770 & cc_data); }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_is_norpak(uint16_t cc_data) { return 0x1724 == (0x777C & cc_data) || 0x1728 == (0x777C & cc_data); }\n/*! \\brief\n    \\param\n*/\nstatic inline int eia608_is_padding(uint16_t cc_data) { return 0x8080 == cc_data; }\n\n////////////////////////////////////////////////////////////////////////////////\n// preamble\ntypedef enum {\n    eia608_style_white = 0,\n    eia608_style_green = 1,\n    eia608_style_blue = 2,\n    eia608_style_cyan = 3,\n    eia608_style_red = 4,\n    eia608_style_yellow = 5,\n    eia608_style_magenta = 6,\n    eia608_style_italics = 7,\n} eia608_style_t;\n\n/*! \\brief\n    \\param\n*/\nint eia608_parse_preamble(uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline);\n/*! \\brief\n    \\param\n*/\nint eia608_parse_midrowchange(uint16_t cc_data, int* chan, eia608_style_t* style, int* underline);\n/*! \\brief\n    \\param\n*/\nuint16_t eia608_row_column_pramble(int row, int col, int chan, int underline);\n/*! \\brief\n    \\param\n*/\nuint16_t eia608_row_style_pramble(int row, int chan, eia608_style_t style, int underline);\n/*! \\brief\n    \\param\n*/\nuint16_t eia608_midrow_change(int chan, eia608_style_t style, int underline);\n////////////////////////////////////////////////////////////////////////////////\n// control command\ntypedef enum {\n    eia608_tab_offset_0 = 0x1720,\n    eia608_tab_offset_1 = 0x1721,\n    eia608_tab_offset_2 = 0x1722,\n    eia608_tab_offset_3 = 0x1723,\n    eia608_control_resume_caption_loading = 0x1420,\n    eia608_control_backspace = 0x1421,\n    eia608_control_alarm_off = 0x1422,\n    eia608_control_alarm_on = 0x1423,\n    eia608_control_delete_to_end_of_row = 0x1424,\n    eia608_control_roll_up_2 = 0x1425,\n    eia608_control_roll_up_3 = 0x1426,\n    eia608_control_roll_up_4 = 0x1427,\n    eia608_control_resume_direct_captioning = 0x1429,\n    eia608_control_text_restart = 0x142A,\n    eia608_control_text_resume_text_display = 0x142B,\n    eia608_control_erase_display_memory = 0x142C,\n    eia608_control_carriage_return = 0x142D,\n    eia608_control_erase_non_displayed_memory = 0x142E,\n    eia608_control_end_of_caption = 0x142F,\n} eia608_control_t;\n\n/*! \\brief\n    \\param\n*/\nuint16_t eia608_control_command(eia608_control_t cmd, int cc);\n/*! \\brief\n    \\param\n*/\nstatic inline uint16_t eia608_tab(int size, int cc) { return eia608_control_command((eia608_control_t)(eia608_tab_offset_0 | (size & 0x0F)), cc); }\n/*! \\brief\n    \\param\n*/\neia608_control_t eia608_parse_control(uint16_t cc_data, int* cc);\n////////////////////////////////////////////////////////////////////////////////\n// text\n/*! \\brief\n    \\param c\n*/\nuint16_t eia608_from_utf8_1(const utf8_char_t* c, int chan);\n/*! \\brief\n    \\param\n*/\nuint16_t eia608_from_utf8_2(const utf8_char_t* c1, const utf8_char_t* c2);\n/*! \\brief\n    \\param\n*/\nuint16_t eia608_from_basicna(uint16_t bna1, uint16_t bna2);\n/*! \\brief\n    \\param\n*/\nint eia608_to_utf8(uint16_t c, int* chan, utf8_char_t* char1, utf8_char_t* char2);\n////////////////////////////////////////////////////////////////////////////////\n/*! \\brief\n    \\param\n*/\nvoid eia608_dump(uint16_t cc_data);\n////////////////////////////////////////////////////////////////////////////////\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/caption/eia608_charmap.h",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#ifndef LIBCAPTION_EIA608_CHARMAP_H\n#define LIBCAPTION_EIA608_CHARMAP_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define EIA608_CHAR_COUNT 176\nextern const char* eia608_char_map[EIA608_CHAR_COUNT];\n\n// Helper char\n#define EIA608_CHAR_NULL \"\"\n// Basic North American character set\n#define EIA608_CHAR_SPACE \"\\x20\"\n#define EIA608_CHAR_EXCLAMATION_MARK \"\\x21\"\n#define EIA608_CHAR_QUOTATION_MARK \"\\x22\"\n#define EIA608_CHAR_NUMBER_SIGN \"\\x23\"\n#define EIA608_CHAR_DOLLAR_SIGN \"\\x24\"\n#define EIA608_CHAR_PERCENT_SIGN \"\\x25\"\n#define EIA608_CHAR_AMPERSAND \"\\x26\"\n#define EIA608_CHAR_LEFT_SINGLE_QUOTATION_MARK \"\\xE2\\x80\\x98\"\n#define EIA608_CHAR_LEFT_PARENTHESIS \"\\x28\"\n#define EIA608_CHAR_RIGHT_PARENTHESIS \"\\x29\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_ACUTE \"\\xC3\\xA1\"\n#define EIA608_CHAR_PLUS_SIGN \"\\x2B\"\n#define EIA608_CHAR_COMMA \"\\x2C\"\n#define EIA608_CHAR_HYPHEN_MINUS \"\\x2D\"\n#define EIA608_CHAR_FULL_STOP \"\\x2E\"\n#define EIA608_CHAR_SOLIDUS \"\\x2F\"\n\n// Basic North American character set\n#define EIA608_CHAR_DIGIT_ZERO \"\\x30\"\n#define EIA608_CHAR_DIGIT_ONE \"\\x31\"\n#define EIA608_CHAR_DIGIT_TWO \"\\x32\"\n#define EIA608_CHAR_DIGIT_THREE \"\\x33\"\n#define EIA608_CHAR_DIGIT_FOUR \"\\x34\"\n#define EIA608_CHAR_DIGIT_FIVE \"\\x35\"\n#define EIA608_CHAR_DIGIT_SIX \"\\x36\"\n#define EIA608_CHAR_DIGIT_SEVEN \"\\x37\"\n#define EIA608_CHAR_DIGIT_EIGHT \"\\x38\"\n#define EIA608_CHAR_DIGIT_NINE \"\\x39\"\n#define EIA608_CHAR_COLON \"\\x3A\"\n#define EIA608_CHAR_SEMICOLON \"\\x3B\"\n#define EIA608_CHAR_LESS_THAN_SIGN \"\\x3C\"\n#define EIA608_CHAR_EQUALS_SIGN \"\\x3D\"\n#define EIA608_CHAR_GREATER_THAN_SIGN \"\\x3E\"\n#define EIA608_CHAR_QUESTION_MARK \"\\x3F\"\n\n// Basic North American character set\n#define EIA608_CHAR_COMMERCIAL_AT \"\\x40\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A \"\\x41\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_B \"\\x42\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_C \"\\x43\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_D \"\\x44\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E \"\\x45\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_F \"\\x46\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_G \"\\x47\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_H \"\\x48\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I \"\\x49\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_J \"\\x4A\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_K \"\\x4B\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_L \"\\x4C\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_M \"\\x4D\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_N \"\\x4E\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O \"\\x4F\"\n\n// Basic North American character set\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_P \"\\x50\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_Q \"\\x51\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_R \"\\x52\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_S \"\\x53\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_T \"\\x54\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U \"\\x55\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_V \"\\x56\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_W \"\\x57\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_X \"\\x58\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_Y \"\\x59\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_Z \"\\x5A\"\n#define EIA608_CHAR_LEFT_SQUARE_BRACKET \"\\x5B\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_ACUTE \"\\xC3\\xA9\"\n#define EIA608_CHAR_RIGHT_SQUARE_BRACKET \"\\x5D\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_ACUTE \"\\xC3\\xAD\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_ACUTE \"\\xC3\\xB3\"\n\n// Basic North American character set\n#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_ACUTE \"\\xC3\\xBA\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_A \"\\x61\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_B \"\\x62\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_C \"\\x63\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_D \"\\x64\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_E \"\\x65\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_F \"\\x66\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_G \"\\x67\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_H \"\\x68\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_I \"\\x69\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_J \"\\x6A\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_K \"\\x6B\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_L \"\\x6C\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_M \"\\x6D\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_N \"\\x6E\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_O \"\\x6F\"\n\n// Basic North American character set\n#define EIA608_CHAR_LATIN_SMALL_LETTER_P \"\\x70\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_Q \"\\x71\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_R \"\\x72\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_S \"\\x73\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_T \"\\x74\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_U \"\\x75\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_V \"\\x76\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_W \"\\x77\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_X \"\\x78\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_Y \"\\x79\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_Z \"\\x7A\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_C_WITH_CEDILLA \"\\xC3\\xA7\"\n#define EIA608_CHAR_DIVISION_SIGN \"\\xC3\\xB7\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_N_WITH_TILDE \"\\xC3\\x91\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_N_WITH_TILDE \"\\xC3\\xB1\"\n#define EIA608_CHAR_FULL_BLOCK \"\\xE2\\x96\\x88\"\n\n// Special North American character set[edit]\n#define EIA608_CHAR_REGISTERED_SIGN \"\\xC2\\xAE\"\n#define EIA608_CHAR_DEGREE_SIGN \"\\xC2\\xB0\"\n#define EIA608_CHAR_VULGAR_FRACTION_ONE_HALF \"\\xC2\\xBD\"\n#define EIA608_CHAR_INVERTED_QUESTION_MARK \"\\xC2\\xBF\"\n#define EIA608_CHAR_TRADE_MARK_SIGN \"\\xE2\\x84\\xA2\"\n#define EIA608_CHAR_CENT_SIGN \"\\xC2\\xA2\"\n#define EIA608_CHAR_POUND_SIGN \"\\xC2\\xA3\"\n#define EIA608_CHAR_EIGHTH_NOTE \"\\xE2\\x99\\xAA\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_GRAVE \"\\xC3\\xA0\"\n#define EIA608_CHAR_NO_BREAK_SPACE \"\\xC2\\xA0\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_GRAVE \"\\xC3\\xA8\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX \"\\xC3\\xA2\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX \"\\xC3\\xAA\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX \"\\xC3\\xAE\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX \"\\xC3\\xB4\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX \"\\xC3\\xBB\"\n\n// Extended Western European character set : Extended Spanish/Miscellaneous\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_ACUTE \"\\xC3\\x81\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_ACUTE \"\\xC3\\x89\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_ACUTE \"\\xC3\\x93\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_ACUTE \"\\xC3\\x9A\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS \"\\xC3\\x9C\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_DIAERESIS \"\\xC3\\xBC\"\n#define EIA608_CHAR_RIGHT_SINGLE_QUOTATION_MARK \"\\xE2\\x80\\x99\"\n#define EIA608_CHAR_INVERTED_EXCLAMATION_MARK \"\\xC2\\xA1\"\n#define EIA608_CHAR_ASTERISK \"\\x2A\"\n#define EIA608_CHAR_APOSTROPHE \"\\x27\"\n#define EIA608_CHAR_EM_DASH \"\\xE2\\x80\\x94\"\n#define EIA608_CHAR_COPYRIGHT_SIGN \"\\xC2\\xA9\"\n#define EIA608_CHAR_SERVICE_MARK \"\\xE2\\x84\\xA0\"\n#define EIA608_CHAR_BULLET \"\\xE2\\x80\\xA2\"\n#define EIA608_CHAR_LEFT_DOUBLE_QUOTATION_MARK \"\\xE2\\x80\\x9C\"\n#define EIA608_CHAR_RIGHT_DOUBLE_QUOTATION_MARK \"\\xE2\\x80\\x9D\"\n\n// Extended Western European character set : Extended French\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_GRAVE \"\\xC3\\x80\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX \"\\xC3\\x82\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA \"\\xC3\\x87\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_GRAVE \"\\xC3\\x88\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX \"\\xC3\\x8A\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS \"\\xC3\\x8B\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_DIAERESIS \"\\xC3\\xAB\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX \"\\xC3\\x8E\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS \"\\xC3\\x8F\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_DIAERESIS \"\\xC3\\xAF\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX \"\\xC3\\x94\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_GRAVE \"\\xC3\\x99\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_GRAVE \"\\xC3\\xB9\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX \"\\xC3\\x9B\"\n#define EIA608_CHAR_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK \"\\xC2\\xAB\"\n#define EIA608_CHAR_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK \"\\xC2\\xBB\"\n\n// Extended Western European character set : Portuguese\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_TILDE \"\\xC3\\x83\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_TILDE \"\\xC3\\xA3\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_ACUTE \"\\xC3\\x8D\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_GRAVE \"\\xC3\\x8C\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_GRAVE \"\\xC3\\xAC\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_GRAVE \"\\xC3\\x92\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_GRAVE \"\\xC3\\xB2\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_TILDE \"\\xC3\\x95\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_TILDE \"\\xC3\\xB5\"\n#define EIA608_CHAR_LEFT_CURLY_BRACKET \"\\x7B\"\n#define EIA608_CHAR_RIGHT_CURLY_BRACKET \"\\x7D\"\n#define EIA608_CHAR_REVERSE_SOLIDUS \"\\x5C\"\n#define EIA608_CHAR_CIRCUMFLEX_ACCENT \"\\x5E\"\n#define EIA608_CHAR_LOW_LINE \"\\x5F\"\n#define EIA608_CHAR_VERTICAL_LINE \"\\x7C\"\n#define EIA608_CHAR_TILDE \"\\x7E\"\n\n// Extended Western European character set : German/Danish\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS \"\\xC3\\x84\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_DIAERESIS \"\\xC3\\xA4\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS \"\\xC3\\x96\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_DIAERESIS \"\\xC3\\xB6\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_SHARP_S \"\\xC3\\x9F\"\n#define EIA608_CHAR_YEN_SIGN \"\\xC2\\xA5\"\n#define EIA608_CHAR_CURRENCY_SIGN \"\\xC2\\xA4\"\n#define EIA608_CHAR_BROKEN_BAR \"\\xC2\\xA6\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE \"\\xC3\\x85\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE \"\\xC3\\xA5\"\n#define EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_STROKE \"\\xC3\\x98\"\n#define EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_STROKE \"\\xC3\\xB8\"\n#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT \"\\xE2\\x94\\x8C\" // top left\n#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT \"\\xE2\\x94\\x90\" // top right\n#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT \"\\xE2\\x94\\x94\" // lower left\n#define EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT \"\\xE2\\x94\\x98\" // bottom right\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/caption/mpeg.h",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#ifndef LIBCAPTION_MPEG_H\n#define LIBCAPTION_MPEG_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"caption.h\"\n#include \"cea708.h\"\n#include \"scc.h\"\n#include <float.h>\n#include <stddef.h>\n////////////////////////////////////////////////////////////////////////////////\n#define STREAM_TYPE_H262 0x02\n#define STREAM_TYPE_H264 0x1B\n#define STREAM_TYPE_H265 0x24\n#define H262_SEI_PACKET 0xB2\n#define H264_SEI_PACKET 0x06\n#define H265_SEI_PACKET 0x27 // There is also 0x28\n#define MAX_NALU_SIZE (6 * 1024 * 1024)\n#define MAX_REFRENCE_FRAMES 64\ntypedef struct {\n    size_t size;\n    uint8_t data[MAX_NALU_SIZE + 1];\n    double dts, cts;\n    libcaption_stauts_t status;\n    // Priority queue for out of order frame processing\n    // Should probablly be a linked list\n    size_t front;\n    size_t latent;\n    cea708_t cea708[MAX_REFRENCE_FRAMES];\n} mpeg_bitstream_t;\n\nvoid mpeg_bitstream_init(mpeg_bitstream_t* packet);\n////////////////////////////////////////////////////////////////////////////////\n// TODO make convenience functions for flv/mp4\n/*! \\brief\n    \\param\n*/\nsize_t mpeg_bitstream_parse(mpeg_bitstream_t* packet, caption_frame_t* frame, const uint8_t* data, size_t size, unsigned stream_type, double dts, double cts);\n/*! \\brief\n    \\param\n*/\nstatic inline libcaption_stauts_t mpeg_bitstream_status(mpeg_bitstream_t* packet) { return packet->status; }\n/*! \\brief\n        Flushes latent packets caused by out or order frames.\n        Returns number of latent frames remaining, 0 when complete;\n    \\param\n*/\nsize_t mpeg_bitstream_flush(mpeg_bitstream_t* packet, caption_frame_t* frame);\n////////////////////////////////////////////////////////////////////////////////\ntypedef enum {\n    sei_type_buffering_period = 0,\n    sei_type_pic_timing = 1,\n    sei_type_pan_scan_rect = 2,\n    sei_type_filler_payload = 3,\n    sei_type_user_data_registered_itu_t_t35 = 4,\n    sei_type_user_data_unregistered = 5,\n    sei_type_recovery_point = 6,\n    sei_type_dec_ref_pic_marking_repetition = 7,\n    sei_type_spare_pic = 8,\n    sei_type_scene_info = 9,\n    sei_type_sub_seq_info = 10,\n    sei_type_sub_seq_layer_characteristics = 11,\n    sei_type_sub_seq_characteristics = 12,\n    sei_type_full_frame_freeze = 13,\n    sei_type_full_frame_freeze_release = 14,\n    sei_type_full_frame_snapshot = 15,\n    sei_type_progressive_refinement_segment_start = 16,\n    sei_type_progressive_refinement_segment_end = 17,\n    sei_type_motion_constrained_slice_group_set = 18,\n    sei_type_film_grain_characteristics = 19,\n    sei_type_deblocking_filter_display_preference = 20,\n    sei_type_stereo_video_info = 21,\n} sei_msgtype_t;\n////////////////////////////////////////////////////////////////////////////////\ntypedef struct _sei_message_t {\n    size_t size;\n    sei_msgtype_t type;\n    struct _sei_message_t* next;\n} sei_message_t;\n\ntypedef struct {\n    double timestamp;\n    sei_message_t* head;\n    sei_message_t* tail;\n} sei_t;\n\n/*! \\brief\n    \\param\n*/\nvoid sei_init(sei_t* sei, double timestamp);\n/*! \\brief\n    \\param\n*/\nvoid sei_free(sei_t* sei);\n/*! \\brief\n    \\param\n*/\nvoid sei_cat(sei_t* to, sei_t* from, int itu_t_t35);\n/*! \\brief\n    \\param\n*/\nvoid sei_message_append(sei_t* sei, sei_message_t* msg);\n/*! \\brief\n    \\param\n*/\nlibcaption_stauts_t sei_parse(sei_t* sei, const uint8_t* data, size_t size, double timestamp);\n/*! \\brief\n    \\param\n*/\nstatic inline sei_message_t* sei_message_head(sei_t* sei) { return sei->head; }\n/*! \\brief\n    \\param\n*/\nstatic inline sei_message_t* sei_message_tail(sei_t* sei) { return sei->tail; }\n/*! \\brief\n    \\param\n*/\nsei_message_t* sei_message_next(sei_message_t* msg);\n/*! \\brief\n    \\param\n*/\nsei_msgtype_t sei_message_type(sei_message_t* msg);\n/*! \\brief\n    \\param\n*/\nsize_t sei_message_size(sei_message_t* msg);\n/*! \\brief\n    \\param\n*/\nuint8_t* sei_message_data(sei_message_t* msg);\n/*! \\brief\n    \\param\n*/\nsei_message_t* sei_message_new(sei_msgtype_t type, uint8_t* data, size_t size);\n/*! \\brief\n    \\param\n*/\nstatic inline sei_message_t* sei_message_copy(sei_message_t* msg)\n{\n    return sei_message_new(sei_message_type(msg), sei_message_data(msg), sei_message_size(msg));\n}\n/**\nFree message and all accoiated data. Messaged added to sei_t by using sei_append_message MUST NOT be freed\nThese messages will be freed by calling sei_free()\n*/\n/*! \\brief\n    \\param\n*/\nvoid sei_message_free(sei_message_t* msg);\n////////////////////////////////////////////////////////////////////////////////\n/*! \\brief\n    \\param\n*/\nsize_t sei_render_size(sei_t* sei);\n/*! \\brief\n    \\param\n*/\nsize_t sei_render(sei_t* sei, uint8_t* data);\n/*! \\brief\n    \\param\n*/\nvoid sei_dump(sei_t* sei);\n/*! \\brief\n    \\param\n*/\nvoid sei_dump_messages(sei_message_t* head, double timestamp);\n////////////////////////////////////////////////////////////////////////////////\n/*! \\brief\n    \\param\n*/\nlibcaption_stauts_t sei_from_scc(sei_t* sei, const scc_t* scc);\n/*! \\brief\n    \\param\n*/\nlibcaption_stauts_t sei_from_caption_frame(sei_t* sei, caption_frame_t* frame);\n/*! \\brief\n    \\param\n*/\nlibcaption_stauts_t sei_from_caption_clear(sei_t* sei);\n/*! \\brief\n    \\param\n*/\nlibcaption_stauts_t sei_to_caption_frame(sei_t* sei, caption_frame_t* frame);\n////////////////////////////////////////////////////////////////////////////////\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/caption/scc.h",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#ifndef LIBCAPTION_SCC_H\n#define LIBCAPTION_SCC_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"eia608.h\"\n\ntypedef struct _scc_t {\n    double timestamp;\n    unsigned int cc_aloc;\n    unsigned int cc_size;\n    uint16_t cc_data[];\n} scc_t;\n\nscc_t* scc_new(int cc_count);\nscc_t* scc_free(scc_t* scc);\n\nsize_t scc_to_608(scc_t** scc, const utf8_char_t* data);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/caption/srt.h",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#ifndef LIBCAPTION_SRT_H\n#define LIBCAPTION_SRT_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"caption.h\"\n#include \"eia608.h\"\n#include \"vtt.h\"\n\n// timestamp and duration are in seconds\ntypedef vtt_t srt_t;\ntypedef vtt_block_t srt_cue_t;\n\n/*! \\brief\n    \\param\n*/\nsrt_t* srt_new();\n/*! \\brief\n    \\param\n*/\nsrt_t* srt_free_head(srt_t* head);\n// returns the head of the link list. must bee freed when done\n/*! \\brief\n    \\param\n*/\nsrt_t* srt_parse(const utf8_char_t* data, size_t size);\n/*! \\brief\n    \\param\n*/\nvoid srt_free(srt_t* srt);\n\n/*! \\brief\n    \\param\n*/\nstatic inline vtt_block_t* srt_next(vtt_block_t* srt) { return srt->next; }\n\n/*! \\brief\n    \\param\n*/\nstatic inline utf8_char_t* srt_cue_data(srt_cue_t* cue) { return vtt_block_data(cue); }\n\n/*! \\brief\n    \\param\n*/\nstatic inline srt_cue_t* srt_cue_from_caption_frame(caption_frame_t* frame, srt_t* srt) { return vtt_cue_from_caption_frame(frame, srt); };\n\n/*! \\brief\n    \\param\n*/\nstatic inline void srt_cue_free_head(srt_t* srt) { vtt_cue_free_head(srt); };\n\n/*! \\brief\n    \\param\n*/\nstatic inline srt_cue_t* srt_cue_new(srt_t* srt, const utf8_char_t* data, size_t size) { return vtt_block_new(srt, data, size, VTT_CUE); };\n\n/*! \\brief\n    \\param\n*/\nstatic inline int srt_cue_to_caption_frame(srt_cue_t* cue, caption_frame_t* frame) { return vtt_cue_to_caption_frame(cue, frame); };\n\nvoid srt_dump(srt_t* srt);\n/*! \\brief\n    \\param\n*/\nvoid vtt_dump(srt_t* srt);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/caption/utf8.h",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#ifndef LIBCAPTION_UTF8_H\n#define LIBCAPTION_UTF8_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <inttypes.h>\n#include <stddef.h>\n\n// These types exist to make the code more self dcoumenting\n// utf8_char_t point is a null teminate string of utf8 encodecd chars\n//\n// utf8_size_t is the length of a string in chars\n// size_t is bytes\ntypedef char utf8_char_t;\ntypedef size_t utf8_size_t;\n/*! \\brief\n    \\param\n\n    Skiped continuation bytes\n*/\n\nconst utf8_char_t* utf8_char_next(const utf8_char_t* c);\n/*! \\brief\n    \\param\n\n    returnes the length of the char in bytes\n*/\nsize_t utf8_char_length(const utf8_char_t* c);\n\n/*! \\brief\n    \\param\n\n    returns 1 if first charcter is white space\n*/\nint utf8_char_whitespace(const utf8_char_t* c);\n\n/*! \\brief\n    \\param\n\n    returns length of the string in bytes\n    size is number of charcter to count (0 to count until NULL term)\n*/\nsize_t utf8_string_length(const utf8_char_t* data, utf8_size_t size);\n/*! \\brief\n    \\param\n*/\nsize_t utf8_char_copy(utf8_char_t* dst, const utf8_char_t* src);\n\n/*! \\brief\n    \\param\n\n    returnes the number of utf8 charcters in a string givne the numbe of bytes\n    to coutn until the a null terminator, pass 0 for size\n*/\nutf8_size_t utf8_char_count(const char* data, size_t size);\n/*! \\brief\n    \\param\n\n    returnes the length of the line in bytes triming not printable characters at the end\n*/\nutf8_size_t utf8_trimmed_length(const utf8_char_t* data, utf8_size_t charcters);\n/*! \\brief\n    \\param\n\n    returns the length in bytes of the line including the new line charcter(s)\n    auto detects between windows(CRLF), unix(LF), mac(CR) and riscos (LFCR) line endings\n*/\nsize_t utf8_line_length(const utf8_char_t* data);\n/*! \\brief\n    \\param\n\n    returns number of chars to include before split\n*/\nutf8_size_t utf8_wrap_length(const utf8_char_t* data, utf8_size_t size);\n\n/*! \\brief\n    \\param\n\n    returns number of new lines in the string\n*/\nint utf8_line_count(const utf8_char_t* data);\n\n/*! \\brief\n    \\param\n    size in/out. In the the max seize, out is the size read;\n    returns number of new lins in teh string\n*/\n#define UFTF_DEFAULT_MAX_FILE_SIZE = (50 * 1024 * 1024);\n\nutf8_char_t* utf8_load_text_file(const char* path, size_t* size);\n\n/*! \\brief\n    \\param\n\n    Compares 2 strings up to max len\n*/\n#ifndef strnstr\nchar* strnstr(const char* string1, const char* string2, size_t len);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/caption/vtt.h",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#ifndef LIBCAPTION_VTT_H\n#define LIBCAPTION_VTT_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"caption.h\"\n#include \"eia608.h\"\n\nenum VTT_BLOCK_TYPE {\n    VTT_REGION = 0,\n    VTT_STYLE = 1,\n    VTT_NOTE = 2,\n    VTT_CUE = 3\n};\n\n// CUE represents a block of caption text\ntypedef struct _vtt_block_t {\n    struct _vtt_block_t* next;\n    enum VTT_BLOCK_TYPE type;\n    // CUE-Only\n    double timestamp;\n    double duration; // -1.0 for no duration\n    char* cue_settings;\n    char* cue_id;\n    // Standard block data\n    size_t text_size;\n    char* block_text;\n} vtt_block_t;\n\n// VTT files are a collection of REGION, STYLE and CUE blocks.\n// XXX: Comments (NOTE blocks) are ignored\ntypedef struct _vtt_t {\n    vtt_block_t* region_head;\n    vtt_block_t* region_tail;\n    vtt_block_t* style_head;\n    vtt_block_t* style_tail;\n    vtt_block_t* cue_head;\n    vtt_block_t* cue_tail;\n} vtt_t;\n\n/*! \\brief\n    \\param\n*/\nvtt_t* vtt_new();\n/*! \\brief\n    \\param\n*/\nvoid vtt_free(vtt_t* vtt);\n\n/*! \\brief\n    \\param\n*/\nvtt_block_t* vtt_block_new(vtt_t* vtt, const utf8_char_t* data, size_t size, enum VTT_BLOCK_TYPE type);\n\n/*! \\brief\n    \\param\n*/\nvoid vtt_cue_free_head(vtt_t* vtt);\n\n/*! \\brief\n    \\param\n*/\nvoid vtt_style_free_head(vtt_t* vtt);\n\n/*! \\brief\n    \\param\n*/\nvoid vtt_region_free_head(vtt_t* vtt);\n\n// returns a vtt_t, containing linked lists of blocks. must be freed when done\n/*! \\brief\n    \\param\n*/\nvtt_t* vtt_parse(const utf8_char_t* data, size_t size);\n\n/*! \\brief\n    \\param\n*/\nvtt_t* _vtt_parse(const utf8_char_t* data, size_t size, int srt_mode);\n\n/*! \\brief\n    \\param\n*/\nstatic inline vtt_block_t* vtt_cue_next(vtt_block_t* block) { return block->next; }\n\n/*! \\brief\n    \\param\n*/\nstatic inline utf8_char_t* vtt_block_data(vtt_block_t* block) { return (utf8_char_t*)(block) + sizeof(vtt_block_t); }\n\n/*! \\brief\n    \\param\n*/\nstatic inline void vtt_crack_time(double tt, int* hh, int* mm, int* ss, int* ms)\n{\n    (*ms) = (int)((int64_t)(tt * 1000) % 1000);\n    (*ss) = (int)((int64_t)(tt) % 60);\n    (*mm) = (int)((int64_t)(tt / (60)) % 60);\n    (*hh) = (int)((int64_t)(tt / (60 * 60)));\n}\n\n// This only converts the current CUE, it does not walk the list\n/*! \\brief\n    \\param\n*/\nint vtt_cue_to_caption_frame(vtt_block_t* cue, caption_frame_t* frame);\n\n// returns the new cue\n/*! \\brief\n    \\param\n*/\nvtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t* vtt);\n/*! \\brief\n    \\param\n*/\nvoid vtt_dump(vtt_t* vtt);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/caption/xds.h",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#ifndef LIBCAPTION_XDS_H\n#define LIBCAPTION_XDS_H\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <inttypes.h>\n#include <stddef.h>\n\ntypedef struct {\n    int state;\n    uint8_t class_code;\n    uint8_t type;\n    uint32_t size;\n    uint8_t content[32];\n    uint8_t checksum;\n} xds_t;\n\nvoid xds_init(xds_t* xds);\nint xds_decode(xds_t* xds, uint16_t cc);\n\n#ifdef __cplusplus\n}\n#endif\n#endif\n"
  },
  {
    "path": "deps/libcaption/format.sh",
    "content": "#!/bin/bash\ncd \"$(dirname \"$0\")\"\nfind . \\( -name '*.cpp' -o -name '*.c' -o -name '*.h' -o -name '*.hpp' -o -name '*.re2c' \\) -exec astyle --style=stroustrup --attach-extern-c --break-blocks --pad-header --pad-paren-out --unpad-paren --add-brackets --keep-one-line-blocks --keep-one-line-statements --convert-tabs --align-pointer=type --align-reference=type --suffix=none --lineend=linux --max-code-length=180 {} \\;\n"
  },
  {
    "path": "deps/libcaption/src/caption.c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#include \"caption.h\"\n#include \"eia608.h\"\n#include \"utf8.h\"\n#include \"xds.h\"\n#include <stdio.h>\n#include <string.h>\n////////////////////////////////////////////////////////////////////////////////\nvoid caption_frame_buffer_clear(caption_frame_buffer_t* buff)\n{\n    memset(buff, 0, sizeof(caption_frame_buffer_t));\n}\n\nvoid caption_frame_state_clear(caption_frame_t* frame)\n{\n    frame->write = 0;\n    frame->timestamp = -1;\n    frame->state = (caption_frame_state_t){ 0, 0, 0, SCREEN_ROWS - 1, 0, 0 }; // clear global state\n}\n\nvoid caption_frame_init(caption_frame_t* frame)\n{\n    xds_init(&frame->xds);\n    caption_frame_state_clear(frame);\n    caption_frame_buffer_clear(&frame->back);\n    caption_frame_buffer_clear(&frame->front);\n}\n////////////////////////////////////////////////////////////////////////////////\n// Helpers\nstatic caption_frame_cell_t* frame_buffer_cell(caption_frame_buffer_t* buff, int row, int col)\n{\n    if (!buff || 0 > row || SCREEN_ROWS <= row || 0 > col || SCREEN_COLS <= col) {\n        return 0;\n    }\n\n    return &buff->cell[row][col];\n}\n\nuint16_t _eia608_from_utf8(const char* s); // function is in eia608.c.re2c\nint caption_frame_write_char(caption_frame_t* frame, int row, int col, eia608_style_t style, int underline, const char* c)\n{\n    if (!frame->write || !_eia608_from_utf8(c)) {\n        return 0;\n    }\n\n    caption_frame_cell_t* cell = frame_buffer_cell(frame->write, row, col);\n\n    if (cell && utf8_char_copy(&cell->data[0], c)) {\n        cell->uln = underline;\n        cell->sty = style;\n        return 1;\n    }\n\n    return 0;\n}\n\nconst utf8_char_t* caption_frame_read_char(caption_frame_t* frame, int row, int col, eia608_style_t* style, int* underline)\n{\n    // always read from front\n    caption_frame_cell_t* cell = frame_buffer_cell(&frame->front, row, col);\n\n    if (!cell) {\n        if (style) {\n            (*style) = eia608_style_white;\n        }\n\n        if (underline) {\n            (*underline) = 0;\n        }\n\n        return EIA608_CHAR_NULL;\n    }\n\n    if (style) {\n        (*style) = cell->sty;\n    }\n\n    if (underline) {\n        (*underline) = cell->uln;\n    }\n\n    return &cell->data[0];\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Parsing\nlibcaption_stauts_t caption_frame_carriage_return(caption_frame_t* frame)\n{\n    if (0 > frame->state.row || SCREEN_ROWS <= frame->state.row) {\n        return LIBCAPTION_ERROR;\n    }\n\n    int r = frame->state.row - (frame->state.rup - 1);\n\n    if (0 >= r || !caption_frame_rollup(frame)) {\n        return LIBCAPTION_OK;\n    }\n\n    for (; r < SCREEN_ROWS; ++r) {\n        uint8_t* dst = (uint8_t*)frame_buffer_cell(frame->write, r - 1, 0);\n        uint8_t* src = (uint8_t*)frame_buffer_cell(frame->write, r - 0, 0);\n        memcpy(dst, src, sizeof(caption_frame_cell_t) * SCREEN_COLS);\n    }\n\n    frame->state.col = 0;\n    caption_frame_cell_t* cell = frame_buffer_cell(frame->write, SCREEN_ROWS - 1, 0);\n    memset(cell, 0, sizeof(caption_frame_cell_t) * SCREEN_COLS);\n    return LIBCAPTION_OK;\n}\n////////////////////////////////////////////////////////////////////////////////\nlibcaption_stauts_t eia608_write_char(caption_frame_t* frame, char* c)\n{\n    if (0 == c || 0 == c[0] || SCREEN_ROWS <= frame->state.row || 0 > frame->state.row || SCREEN_COLS <= frame->state.col || 0 > frame->state.col) {\n        // NO-OP\n    } else if (caption_frame_write_char(frame, frame->state.row, frame->state.col, frame->state.sty, frame->state.uln, c)) {\n        frame->state.col += 1;\n    }\n\n    return LIBCAPTION_OK;\n}\n\nlibcaption_stauts_t caption_frame_end(caption_frame_t* frame)\n{\n    memcpy(&frame->front, &frame->back, sizeof(caption_frame_buffer_t));\n    caption_frame_buffer_clear(&frame->back); // This is required\n    return LIBCAPTION_READY;\n}\n\nlibcaption_stauts_t caption_frame_decode_preamble(caption_frame_t* frame, uint16_t cc_data)\n{\n    eia608_style_t sty;\n    int row, col, chn, uln;\n\n    if (eia608_parse_preamble(cc_data, &row, &col, &sty, &chn, &uln)) {\n        frame->state.row = row;\n        frame->state.col = col;\n        frame->state.sty = sty;\n        frame->state.uln = uln;\n    }\n\n    return LIBCAPTION_OK;\n}\n\nlibcaption_stauts_t caption_frame_decode_midrowchange(caption_frame_t* frame, uint16_t cc_data)\n{\n    eia608_style_t sty;\n    int chn, unl;\n\n    if (eia608_parse_midrowchange(cc_data, &chn, &sty, &unl)) {\n        frame->state.sty = sty;\n        frame->state.uln = unl;\n    }\n\n    return LIBCAPTION_OK;\n}\n\nlibcaption_stauts_t caption_frame_backspace(caption_frame_t* frame)\n{\n    // do not reverse wrap (tw 28:20)\n    frame->state.col = (0 < frame->state.col) ? (frame->state.col - 1) : 0;\n    caption_frame_write_char(frame, frame->state.row, frame->state.col, eia608_style_white, 0, EIA608_CHAR_NULL);\n    return LIBCAPTION_READY;\n}\n\nlibcaption_stauts_t caption_frame_delete_to_end_of_row(caption_frame_t* frame)\n{\n    int c;\n    if (frame->write) {\n        for (c = frame->state.col; c < SCREEN_COLS; ++c) {\n            caption_frame_write_char(frame, frame->state.row, c, eia608_style_white, 0, EIA608_CHAR_NULL);\n        }\n    }\n\n    // TODO test this and replace loop\n    //  uint8_t* dst = (uint8_t*)frame_buffer_cell(frame->write, frame->state.row, frame->state.col);\n    //  memset(dst,0,sizeof(caption_frame_cell_t) * (SCREEN_COLS - frame->state.col - 1))\n\n    return LIBCAPTION_READY;\n}\n\nlibcaption_stauts_t caption_frame_decode_control(caption_frame_t* frame, uint16_t cc_data)\n{\n    int cc;\n    eia608_control_t cmd = eia608_parse_control(cc_data, &cc);\n\n    switch (cmd) {\n    // PAINT ON\n    case eia608_control_resume_direct_captioning:\n        frame->state.rup = 0;\n        frame->write = &frame->front;\n        return LIBCAPTION_OK;\n\n    case eia608_control_erase_display_memory:\n        caption_frame_buffer_clear(&frame->front);\n        return LIBCAPTION_READY;\n\n    // ROLL-UP\n    case eia608_control_roll_up_2:\n        frame->state.rup = 1;\n        frame->write = &frame->front;\n        return LIBCAPTION_OK;\n\n    case eia608_control_roll_up_3:\n        frame->state.rup = 2;\n        frame->write = &frame->front;\n        return LIBCAPTION_OK;\n\n    case eia608_control_roll_up_4:\n        frame->state.rup = 3;\n        frame->write = &frame->front;\n        return LIBCAPTION_OK;\n\n    case eia608_control_carriage_return:\n        return caption_frame_carriage_return(frame);\n\n    // Corrections (Is this only valid as part of paint on?)\n    case eia608_control_backspace:\n        return caption_frame_backspace(frame);\n    case eia608_control_delete_to_end_of_row:\n        return caption_frame_delete_to_end_of_row(frame);\n\n    // POP ON\n    case eia608_control_resume_caption_loading:\n        frame->state.rup = 0;\n        frame->write = &frame->back;\n        return LIBCAPTION_OK;\n\n    case eia608_control_erase_non_displayed_memory:\n        caption_frame_buffer_clear(&frame->back);\n        return LIBCAPTION_OK;\n\n    case eia608_control_end_of_caption:\n        return caption_frame_end(frame);\n\n    // cursor positioning\n    case eia608_tab_offset_0:\n    case eia608_tab_offset_1:\n    case eia608_tab_offset_2:\n    case eia608_tab_offset_3:\n        frame->state.col += (cmd - eia608_tab_offset_0);\n        return LIBCAPTION_OK;\n\n    // Unhandled\n    default:\n    case eia608_control_alarm_off:\n    case eia608_control_alarm_on:\n    case eia608_control_text_restart:\n    case eia608_control_text_resume_text_display:\n        return LIBCAPTION_OK;\n    }\n}\n\nlibcaption_stauts_t caption_frame_decode_text(caption_frame_t* frame, uint16_t cc_data)\n{\n    int chan;\n    char char1[5], char2[5];\n    size_t chars = eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]);\n\n    if (eia608_is_westeu(cc_data)) {\n        // Extended charcters replace the previous charcter for back compatibility\n        caption_frame_backspace(frame);\n    }\n\n    if (0 < chars) {\n        eia608_write_char(frame, char1);\n    }\n\n    if (1 < chars) {\n        eia608_write_char(frame, char2);\n    }\n\n    return LIBCAPTION_OK;\n}\n\nlibcaption_stauts_t caption_frame_decode(caption_frame_t* frame, uint16_t cc_data, double timestamp)\n{\n    if (!eia608_parity_varify(cc_data)) {\n        frame->status = LIBCAPTION_ERROR;\n        return frame->status;\n    }\n\n    if (eia608_is_padding(cc_data)) {\n        frame->status = LIBCAPTION_OK;\n        return frame->status;\n    }\n\n    if (0 > frame->timestamp || LIBCAPTION_READY == frame->status) {\n        frame->timestamp = timestamp;\n    }\n\n    // skip duplicate controll commands. We also skip duplicate specialna to match the behaviour of iOS/vlc\n    if ((eia608_is_specialna(cc_data) || eia608_is_control(cc_data)) && cc_data == frame->state.cc_data) {\n        frame->status = LIBCAPTION_OK;\n        return frame->status;\n    }\n\n    frame->state.cc_data = cc_data;\n\n    if (frame->xds.state) {\n        frame->status = xds_decode(&frame->xds, cc_data);\n    } else if (eia608_is_xds(cc_data)) {\n        frame->status = xds_decode(&frame->xds, cc_data);\n    } else if (eia608_is_control(cc_data)) {\n        frame->status = caption_frame_decode_control(frame, cc_data);\n    } else if (eia608_is_basicna(cc_data) || eia608_is_specialna(cc_data) || eia608_is_westeu(cc_data)) {\n\n        // Don't decode text if we dont know what mode we are in.\n        if (!frame->write) {\n            frame->status = LIBCAPTION_OK;\n            return frame->status;\n        }\n\n        frame->status = caption_frame_decode_text(frame, cc_data);\n\n        // If we are in paint on mode, display immiditally\n        if (LIBCAPTION_OK == frame->status && caption_frame_painton(frame)) {\n            frame->status = LIBCAPTION_READY;\n        }\n    } else if (eia608_is_preamble(cc_data)) {\n        frame->status = caption_frame_decode_preamble(frame, cc_data);\n    } else if (eia608_is_midrowchange(cc_data)) {\n        frame->status = caption_frame_decode_midrowchange(frame, cc_data);\n    }\n\n    return frame->status;\n}\n\n////////////////////////////////////////////////////////////////////////////////\nint caption_frame_from_text(caption_frame_t* frame, const utf8_char_t* data)\n{\n    ssize_t size = (ssize_t)strlen(data);\n    caption_frame_init(frame);\n    frame->write = &frame->back;\n\n    for (size_t r = 0; (*data) && size && r < SCREEN_ROWS;) {\n        // skip whitespace at start of line\n        while (size && utf8_char_whitespace(data)) {\n            size_t s = utf8_char_length(data);\n            data += s, size -= (ssize_t)s;\n        }\n\n        // get charcter count for wrap (or orest of line)\n        utf8_size_t char_count = utf8_wrap_length(data, SCREEN_COLS);\n        // write to caption frame\n        for (size_t c = 0; c < char_count; ++c) {\n            size_t char_length = utf8_char_length(data);\n            caption_frame_write_char(frame, (int)r, (int)c, eia608_style_white, 0, data);\n            data += char_length, size -= (ssize_t)char_length;\n        }\n\n        r += char_count ? 1 : 0; // Update row num only if not blank\n    }\n\n    caption_frame_end(frame);\n    return 0;\n}\n////////////////////////////////////////////////////////////////////////////////\nsize_t caption_frame_to_text(caption_frame_t* frame, utf8_char_t* data)\n{\n    int r, c, uln, crlf = 0, count = 0;\n    size_t s, size = 0;\n    eia608_style_t sty;\n    (*data) = '\\0';\n\n    for (r = 0; r < SCREEN_ROWS; ++r) {\n        crlf += count, count = 0;\n        for (c = 0; c < SCREEN_COLS; ++c) {\n            const utf8_char_t* chr = caption_frame_read_char(frame, r, c, &sty, &uln);\n            // dont start a new line until we encounter at least one printable character\n            if (0 < utf8_char_length(chr) && (0 < count || !utf8_char_whitespace(chr))) {\n                if (0 < crlf) {\n                    memcpy(data, \"\\r\\n\\0\", 3);\n                    data += 2, size += 2, crlf = 0;\n                }\n\n                s = utf8_char_copy(data, chr);\n                data += s, size += s, ++count;\n            }\n        }\n    }\n\n    return size;\n}\n"
  },
  {
    "path": "deps/libcaption/src/cea708.c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#include \"cea708.h\"\n#include <float.h>\n#include <memory.h>\n#include <stdio.h>\n\nint cea708_cc_count(user_data_t* data)\n{\n    return data->cc_count;\n}\n\nuint16_t cea708_cc_data(user_data_t* data, int index, int* valid, cea708_cc_type_t* type)\n{\n    (*valid) = data->cc_data[index].cc_valid;\n    (*type) = data->cc_data[index].cc_type;\n    return data->cc_data[index].cc_data;\n}\n\nint cea708_init(cea708_t* cea708, double timestamp)\n{\n    memset(cea708, 0, sizeof(cea708_t));\n    cea708->country = country_united_states;\n    cea708->provider = t35_provider_atsc;\n    cea708->user_identifier = GA94;\n    cea708->user_data_type_code = 3;\n    cea708->directv_user_data_length = 0;\n    cea708->user_data.process_em_data_flag = 0;\n    cea708->user_data.process_cc_data_flag = 1;\n    cea708->user_data.additional_data_flag = 0;\n    cea708->user_data.em_data = 0xFF;\n    cea708->user_data.cc_count = 0;\n    cea708->timestamp = timestamp;\n    return 1;\n}\n\nvoid cea708_parse_user_data_type_strcture(const uint8_t* data, size_t size, user_data_t* user_data)\n{\n    memset(user_data, 0, sizeof(user_data_t));\n    user_data->process_em_data_flag = !!(data[0] & 0x80);\n    user_data->process_cc_data_flag = !!(data[0] & 0x40);\n    user_data->additional_data_flag = !!(data[0] & 0x20);\n    user_data->cc_count = (data[0] & 0x1F);\n    user_data->em_data = data[1];\n    data += 2, size -= 2;\n\n    for (int i = 0; 3 < size && i < (int)user_data->cc_count; ++i, data += 3, size -= 3) {\n        user_data->cc_data[i].marker_bits = data[0] >> 3;\n        user_data->cc_data[i].cc_valid = data[0] >> 2;\n        user_data->cc_data[i].cc_type = data[0] >> 0;\n        user_data->cc_data[i].cc_data = data[1] << 8 | data[2];\n    }\n}\n\n// 00 00 00  06 C1  FF FC 34 B9 FF : onCaptionInfo.\nlibcaption_stauts_t cea708_parse_h264(const uint8_t* data, size_t size, cea708_t* cea708)\n{\n    if (3 > size) {\n        goto error;\n    }\n\n    // I think the first few bytes need to be handled in mpeg\n    cea708->country = (itu_t_t35_country_code_t)(data[0]);\n    cea708->provider = (itu_t_t35_provider_code_t)((data[1] << 8) | data[2]);\n    cea708->user_identifier = 0;\n    cea708->user_data_type_code = 0;\n    data += 3, size -= 3;\n\n    if (t35_provider_atsc == cea708->provider) {\n        if (4 > size) {\n            goto error;\n        }\n\n        cea708->user_identifier = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);\n        data += 4, size -= 4;\n    }\n\n    // where country and provider are zero\n    // Im not sure what this extra byte is. It sonly seesm to come up in onCaptionInfo\n    // h264 spec seems to describe this\n    if (0 == cea708->provider && 0 == cea708->country) {\n        if (1 > size) {\n            goto error;\n        }\n\n        data += 1, size -= 1;\n    } else if (t35_provider_atsc == cea708->provider || t35_provider_direct_tv == cea708->provider) {\n        if (1 > size) {\n            goto error;\n        }\n\n        cea708->user_data_type_code = data[0];\n        data += 1, size -= 1;\n    }\n\n    if (t35_provider_direct_tv == cea708->provider) {\n        if (1 > size) {\n            goto error;\n        }\n\n        cea708->directv_user_data_length = data[0];\n        data += 1, size -= 1;\n    }\n\n    if (3 == cea708->user_data_type_code && 2 <= size) {\n        cea708_parse_user_data_type_strcture(data, size, &cea708->user_data);\n    } else if (4 == cea708->user_data_type_code) {\n        // additional_CEA_608_data\n    } else if (5 == cea708->user_data_type_code) {\n        // luma_PAM_data\n    } else {\n        // ATSC_reserved_user_data\n    }\n\n    return LIBCAPTION_OK;\nerror:\n    return LIBCAPTION_ERROR;\n}\n\nlibcaption_stauts_t cea708_parse_h262(const uint8_t* data, size_t size, cea708_t* cea708)\n{\n    if (!data || 7 > size) {\n        return LIBCAPTION_ERROR;\n    }\n\n    cea708->user_identifier = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);\n    cea708->user_data_type_code = data[4];\n    if (3 == cea708->user_data_type_code) {\n        cea708_parse_user_data_type_strcture(data + 5, size - 5, &cea708->user_data);\n    }\n\n    return LIBCAPTION_OK;\n}\n\nint cea708_add_cc_data(cea708_t* cea708, int valid, cea708_cc_type_t type, uint16_t cc_data)\n{\n    if (31 <= cea708->user_data.cc_count) {\n        return 0;\n    }\n\n    cea708->user_data.cc_data[cea708->user_data.cc_count].marker_bits = 0x1F;\n    cea708->user_data.cc_data[cea708->user_data.cc_count].cc_valid = valid;\n    cea708->user_data.cc_data[cea708->user_data.cc_count].cc_type = type;\n    cea708->user_data.cc_data[cea708->user_data.cc_count].cc_data = cc_data;\n    ++cea708->user_data.cc_count;\n    return 1;\n}\n\nint cea708_render(cea708_t* cea708, uint8_t* data, size_t size)\n{\n    int i;\n    size_t total = 0;\n    data[0] = cea708->country;\n    data[1] = cea708->provider >> 8;\n    data[2] = cea708->provider >> 0;\n    total += 3;\n    data += 3;\n    size -= 3;\n\n    if (t35_provider_atsc == cea708->provider) {\n\n        data[0] = cea708->user_identifier >> 24;\n        data[1] = cea708->user_identifier >> 16;\n        data[2] = cea708->user_identifier >> 8;\n        data[3] = cea708->user_identifier >> 0;\n        total += 4;\n        data += 4;\n        size -= 4;\n    }\n\n    if (t35_provider_atsc == cea708->provider || t35_provider_direct_tv == cea708->provider) {\n        data[0] = cea708->user_data_type_code;\n        total += 1;\n        data += 1;\n        size -= 1;\n    }\n\n    if (t35_provider_direct_tv == cea708->provider) {\n        data[0] = cea708->directv_user_data_length;\n        total += 1;\n        data += 1;\n        size -= 1;\n    }\n\n    data[1] = cea708->user_data.em_data;\n    data[0] = (cea708->user_data.process_em_data_flag ? 0x80 : 0x00)\n        | (cea708->user_data.process_cc_data_flag ? 0x40 : 0x00)\n        | (cea708->user_data.additional_data_flag ? 0x20 : 0x00)\n        | (cea708->user_data.cc_count & 0x1F);\n\n    total += 2;\n    data += 2;\n    size -= 2;\n\n    for (i = 0; i < (int)cea708->user_data.cc_count; ++i) {\n        data[0] = (cea708->user_data.cc_data[i].marker_bits << 3) | (cea708->user_data.cc_data[i].cc_valid << 2) | cea708->user_data.cc_data[i].cc_type;\n        data[1] = cea708->user_data.cc_data[i].cc_data >> 8;\n        data[2] = cea708->user_data.cc_data[i].cc_data >> 0;\n        total += 3;\n        data += 3;\n        size -= 3;\n    }\n\n    data[0] = 0xFF; //marker bits\n    return (int)(total + 1);\n}\n\ncc_data_t cea708_encode_cc_data(int cc_valid, cea708_cc_type_t type, uint16_t cc_data)\n{\n    cc_data_t data = { 0x1F, cc_valid, type, cc_data };\n    return data;\n}\n\nvoid cea708_dump(cea708_t* cea708)\n{\n    int i;\n\n    fprintf(stderr, \"itu_t_t35_country_code_t %d\\n\", cea708->country);\n    fprintf(stderr, \"itu_t_t35_provider_code_t %d\\n\", cea708->provider);\n    fprintf(stderr, \"user_identifier %c%c%c%c\\n\",\n        (cea708->user_identifier >> 24) & 0xFF, (cea708->user_identifier >> 16) & 0xFF,\n        (cea708->user_identifier >> 8) & 0xFF, cea708->user_identifier & 0xFF);\n    fprintf(stderr, \"user_data_type_code %d\\n\", cea708->user_data_type_code);\n    fprintf(stderr, \"directv_user_data_length %d\\n\", cea708->directv_user_data_length);\n    fprintf(stderr, \"user_data.process_em_data_flag %d\\n\", cea708->user_data.process_em_data_flag);\n    fprintf(stderr, \"user_data.process_cc_data_flag %d\\n\", cea708->user_data.process_cc_data_flag);\n    fprintf(stderr, \"user_data.additional_data_flag %d\\n\", cea708->user_data.additional_data_flag);\n    fprintf(stderr, \"user_data.cc_count %d\\n\", cea708->user_data.cc_count);\n    fprintf(stderr, \"user_data.em_data %d\\n\", cea708->user_data.em_data);\n\n    for (i = 0; i < (int)cea708->user_data.cc_count; ++i) {\n        int valid;\n        cea708_cc_type_t type;\n        uint16_t cc_data = cea708_cc_data(&cea708->user_data, i, &valid, &type);\n\n        if (valid && cc_type_ntsc_cc_field_1 == type) {\n            eia608_dump(cc_data);\n        } else {\n            fprintf(stderr, \"user_data.cc_data[%d] cc_valid: %s, cc_type: %d, cc_data: %04x\\n\", i, cea708->user_data.cc_data[i].cc_valid ? \"true\" : \"false\", cea708->user_data.cc_data[i].cc_type, cea708->user_data.cc_data[i].cc_data);\n        }\n    }\n}\n\nlibcaption_stauts_t cea708_to_caption_frame(caption_frame_t* frame, cea708_t* cea708)\n{\n    int i, count = cea708_cc_count(&cea708->user_data);\n    libcaption_stauts_t status = LIBCAPTION_OK;\n\n    if (GA94 == cea708->user_identifier) {\n        for (i = 0; i < count; ++i) {\n            int valid;\n            cea708_cc_type_t type;\n            uint16_t cc_data = cea708_cc_data(&cea708->user_data, i, &valid, &type);\n\n            if (valid && cc_type_ntsc_cc_field_1 == type) {\n                status = libcaption_status_update(status, caption_frame_decode(frame, cc_data, cea708->timestamp));\n            }\n        }\n    }\n\n    return status;\n}\n"
  },
  {
    "path": "deps/libcaption/src/dvtcc.c",
    "content": "\n\nstruct dtvcc_packet_t* dtvcc_packet_start(uint8_t cc_data1, uint8_t cc_data2)\n{\n    unsigned int packet_size = cc_data1 & 0x3F;\n    packet_size = (0 == packet_size) ? 64 * 8 - 1 : (packet_size * 8 - 1)\n\n                                                        unsigned int packet_size_bytes\n        = dtvcc_packet_t* dvtcc = malloc(sizeof(dtvcc_packet_t) + packet_size * 2 - 1);\n    dvtcc->service_number = (cc_data1 0xC0) >> 6;\n    dvtcc->packet_size = packet_size;\n    dvtcc->service_number = DVTCC_SERVICE_NUMBER_UNKNOWN;\n}\n\nvoid dtvcc_packet_data(struct dtvcc_packet_t* dvtcc, uint8_t cc_data1, uint8_t cc_data2)\n{\n    if (dvtcc->service_number) {\n        if (7 == dvtcc->service_number) {\n            dvtcc->service_number\n        }\n    }\n}\n"
  },
  {
    "path": "deps/libcaption/src/eia608.c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#include \"eia608.h\"\n#include <stdio.h>\n#include <string.h>\n\n////////////////////////////////////////////////////////////////////////////////\nint eia608_row_map[] = { 10, -1, 0, 1, 2, 3, 11, 12, 13, 14, 4, 5, 6, 7, 8, 9 };\nint eia608_reverse_row_map[] = { 2, 3, 4, 5, 10, 11, 12, 13, 14, 15, 0, 6, 7, 8, 9, 1 };\n\nconst char* eia608_style_map[] = {\n    \"white\",\n    \"green\",\n    \"blue\",\n    \"cyan\",\n    \"red\",\n    \"yellow\",\n    \"magenta\",\n    \"italics\",\n};\n\nstatic inline uint16_t eia608_row_pramble(int row, int chan, int x, int underline)\n{\n    row = eia608_reverse_row_map[row & 0x0F];\n    return eia608_parity(0x1040 | (chan ? 0x0800 : 0x0000) | ((row << 7) & 0x0700) | ((row << 5) & 0x0020)) | ((x << 1) & 0x001E) | (underline ? 0x0001 : 0x0000);\n}\n\nuint16_t eia608_row_column_pramble(int row, int col, int chan, int underline) { return eia608_row_pramble(row, chan, 0x10 | (col / 4), underline); }\nuint16_t eia608_row_style_pramble(int row, int chan, eia608_style_t style, int underline) { return eia608_row_pramble(row, chan, style, underline); }\nuint16_t eia608_midrow_change(int chan, eia608_style_t style, int underline) { return eia608_parity(0x1120 | ((chan << 11) & 0x0800) | ((style << 1) & 0x000E) | (underline & 0x0001)); }\n\nint eia608_parse_preamble(uint16_t cc_data, int* row, int* col, eia608_style_t* style, int* chan, int* underline)\n{\n    (*row) = eia608_row_map[((0x0700 & cc_data) >> 7) | ((0x0020 & cc_data) >> 5)];\n    (*chan) = !!(0x0800 & cc_data);\n    (*underline) = 0x0001 & cc_data;\n\n    if (0x0010 & cc_data) {\n        (*style) = eia608_style_white;\n        (*col) = 4 * ((0x000E & cc_data) >> 1);\n    } else {\n        (*style) = (0x000E & cc_data) >> 1;\n        (*col) = 0;\n    }\n\n    return 1;\n}\n\nint eia608_parse_midrowchange(uint16_t cc_data, int* chan, eia608_style_t* style, int* underline)\n{\n    (*chan) = !!(0x0800 & cc_data);\n\n    if (0x1120 == (0x7770 & cc_data)) {\n        (*style) = (0x000E & cc_data) >> 1;\n        (*underline) = 0x0001 & cc_data;\n    }\n\n    return 1;\n}\n////////////////////////////////////////////////////////////////////////////////\n// control command\neia608_control_t eia608_parse_control(uint16_t cc_data, int* cc)\n{\n    if (0x0200 & cc_data) {\n        (*cc) = (cc_data & 0x0800 ? 0x01 : 0x00);\n        return (eia608_control_t)(0x177F & cc_data);\n    } else {\n        (*cc) = (cc_data & 0x0800 ? 0x01 : 0x00) | (cc_data & 0x0100 ? 0x02 : 0x00);\n        return (eia608_control_t)(0x167F & cc_data);\n    }\n}\n\nuint16_t eia608_control_command(eia608_control_t cmd, int cc)\n{\n    uint16_t c = (cc & 0x01) ? 0x0800 : 0x0000;\n    uint16_t f = (cc & 0x02) ? 0x0100 : 0x0000;\n\n    if (eia608_tab_offset_0 == (eia608_control_t)(cmd & 0xFFC0)) {\n        return (eia608_control_t)eia608_parity(cmd | c);\n    } else {\n        return (eia608_control_t)eia608_parity(cmd | c | f);\n    }\n}\n////////////////////////////////////////////////////////////////////////////////\n// text\nstatic const char* utf8_from_index(int idx) { return (0 <= idx && EIA608_CHAR_COUNT > idx) ? eia608_char_map[idx] : \"\"; }\nstatic int eia608_to_index(uint16_t cc_data, int* chan, int* c1, int* c2)\n{\n    (*c1) = (*c2) = -1;\n    (*chan) = 0;\n    cc_data &= 0x7F7F; // strip off parity bits\n\n    // Handle Basic NA BEFORE we strip the channel bit\n    if (eia608_is_basicna(cc_data)) {\n        // we got first char, yes. But what about second char?\n        (*c1) = (cc_data >> 8) - 0x20;\n        cc_data &= 0x00FF;\n\n        if (0x0020 <= cc_data && 0x0080 > cc_data) {\n            (*c2) = cc_data - 0x20;\n            return 2;\n        }\n\n        return 1;\n    }\n\n    // Check then strip second channel toggle\n    (*chan) = cc_data & 0x0800;\n    cc_data = cc_data & 0xF7FF;\n\n    if (eia608_is_specialna(cc_data)) {\n        // Special North American character\n        (*c1) = cc_data - 0x1130 + 0x60;\n        return 1;\n    }\n\n    if (0x1220 <= cc_data && 0x1240 > cc_data) {\n        // Extended Western European character set, Spanish/Miscellaneous/French\n        (*c1) = cc_data - 0x1220 + 0x70;\n        return 1;\n    }\n\n    if (0x1320 <= cc_data && 0x1340 > cc_data) {\n        // Extended Western European character set, Portuguese/German/Danish\n        (*c1) = cc_data - 0x1320 + 0x90;\n        return 1;\n    }\n\n    return 0;\n}\n\nint eia608_to_utf8(uint16_t c, int* chan, char* str1, char* str2)\n{\n    int c1, c2;\n    int size = (int)eia608_to_index(c, chan, &c1, &c2);\n    utf8_char_copy(str1, utf8_from_index(c1));\n    utf8_char_copy(str2, utf8_from_index(c2));\n    return size;\n}\n\nuint16_t eia608_from_basicna(uint16_t bna1, uint16_t bna2)\n{\n    if (!eia608_is_basicna(bna1) || !eia608_is_basicna(bna2)) {\n        return 0;\n    }\n\n    return eia608_parity((0xFF00 & bna1) | ((0xFF00 & bna2) >> 8));\n}\n\n// prototype for re2c generated function\nuint16_t _eia608_from_utf8(const utf8_char_t* s);\nuint16_t eia608_from_utf8_1(const utf8_char_t* c, int chan)\n{\n    uint16_t cc_data = _eia608_from_utf8(c);\n\n    if (0 == cc_data) {\n        return cc_data;\n    }\n\n    if (chan && !eia608_is_basicna(cc_data)) {\n        cc_data |= 0x0800;\n    }\n\n    return eia608_parity(cc_data);\n}\n\nuint16_t eia608_from_utf8_2(const utf8_char_t* c1, const utf8_char_t* c2)\n{\n    uint16_t cc1 = _eia608_from_utf8(c1);\n    uint16_t cc2 = _eia608_from_utf8(c2);\n    return eia608_from_basicna(cc1, cc2);\n}\n////////////////////////////////////////////////////////////////////////////////\nvoid eia608_dump(uint16_t cc_data)\n{\n    eia608_style_t style;\n    const char* text = 0;\n    char char1[5], char2[5];\n    char1[0] = char2[0] = 0;\n    int row, col, chan, underline;\n\n    if (!eia608_parity_varify(cc_data)) {\n        text = \"parity failed\";\n    } else if (0 == eia608_parity_strip(cc_data)) {\n        text = \"pad\";\n    } else if (eia608_is_basicna(cc_data)) {\n        text = \"basicna\";\n        eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]);\n    } else if (eia608_is_specialna(cc_data)) {\n        text = \"specialna\";\n        eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]);\n    } else if (eia608_is_westeu(cc_data)) {\n        text = \"westeu\";\n        eia608_to_utf8(cc_data, &chan, &char1[0], &char2[0]);\n    } else if (eia608_is_xds(cc_data)) {\n        text = \"xds\";\n    } else if (eia608_is_midrowchange(cc_data)) {\n        text = \"midrowchange\";\n    } else if (eia608_is_norpak(cc_data)) {\n        text = \"norpak\";\n    } else if (eia608_is_preamble(cc_data)) {\n        text = \"preamble\";\n        eia608_parse_preamble(cc_data, &row, &col, &style, &chan, &underline);\n        fprintf(stderr, \"preamble %d %d %d %d %d\\n\", row, col, style, chan, underline);\n\n    } else if (eia608_is_control(cc_data)) {\n        switch (eia608_parse_control(cc_data, &chan)) {\n\n        default:\n            text = \"unknown_control\";\n            break;\n\n        case eia608_tab_offset_0:\n            text = \"eia608_tab_offset_0\";\n            break;\n\n        case eia608_tab_offset_1:\n            text = \"eia608_tab_offset_1\";\n            break;\n\n        case eia608_tab_offset_2:\n            text = \"eia608_tab_offset_2\";\n            break;\n\n        case eia608_tab_offset_3:\n            text = \"eia608_tab_offset_3\";\n            break;\n\n        case eia608_control_resume_caption_loading:\n            text = \"eia608_control_resume_caption_loading\";\n            break;\n\n        case eia608_control_backspace:\n            text = \"eia608_control_backspace\";\n            break;\n\n        case eia608_control_alarm_off:\n            text = \"eia608_control_alarm_off\";\n            break;\n\n        case eia608_control_alarm_on:\n            text = \"eia608_control_alarm_on\";\n            break;\n\n        case eia608_control_delete_to_end_of_row:\n            text = \"eia608_control_delete_to_end_of_row\";\n            break;\n\n        case eia608_control_roll_up_2:\n            text = \"eia608_control_roll_up_2\";\n            break;\n\n        case eia608_control_roll_up_3:\n            text = \"eia608_control_roll_up_3\";\n            break;\n\n        case eia608_control_roll_up_4:\n            text = \"eia608_control_roll_up_4\";\n            break;\n\n        case eia608_control_resume_direct_captioning:\n            text = \"eia608_control_resume_direct_captioning\";\n            break;\n\n        case eia608_control_text_restart:\n            text = \"eia608_control_text_restart\";\n            break;\n\n        case eia608_control_text_resume_text_display:\n            text = \"eia608_control_text_resume_text_display\";\n            break;\n\n        case eia608_control_erase_display_memory:\n            text = \"eia608_control_erase_display_memory\";\n            break;\n\n        case eia608_control_carriage_return:\n            text = \"eia608_control_carriage_return\";\n            break;\n\n        case eia608_control_erase_non_displayed_memory:\n            text = \"eia608_control_erase_non_displayed_memory\";\n            break;\n\n        case eia608_control_end_of_caption:\n            text = \"eia608_control_end_of_caption\";\n            break;\n        }\n    } else {\n        text = \"unhandled\";\n    }\n\n    fprintf(stderr, \"cc %04X (%04X) '%s' '%s' (%s)\\n\", cc_data, eia608_parity_strip(cc_data), char1, char2, text);\n}\n"
  },
  {
    "path": "deps/libcaption/src/eia608_charmap.c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#include \"eia608_charmap.h\"\n// 0 - 95: Basic North American character set\n// 96 - 111: Special North American character\n// 112 - 127: Extended Western European character set : Extended Spanish/Miscellaneous\n// 128 - 143: Extended Western European character set : Extended French\n// 144 - 159: Extended Western European character set : Portuguese\n// 160 - 175: Extended Western European character set : German/Danish\nconst char* eia608_char_map[] = {\n    EIA608_CHAR_SPACE,\n    EIA608_CHAR_EXCLAMATION_MARK,\n    EIA608_CHAR_QUOTATION_MARK,\n    EIA608_CHAR_NUMBER_SIGN,\n    EIA608_CHAR_DOLLAR_SIGN,\n    EIA608_CHAR_PERCENT_SIGN,\n    EIA608_CHAR_AMPERSAND,\n    EIA608_CHAR_RIGHT_SINGLE_QUOTATION_MARK,\n    EIA608_CHAR_LEFT_PARENTHESIS,\n    EIA608_CHAR_RIGHT_PARENTHESIS,\n    EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_ACUTE,\n    EIA608_CHAR_PLUS_SIGN,\n    EIA608_CHAR_COMMA,\n    EIA608_CHAR_HYPHEN_MINUS,\n    EIA608_CHAR_FULL_STOP,\n    EIA608_CHAR_SOLIDUS,\n    EIA608_CHAR_DIGIT_ZERO,\n    EIA608_CHAR_DIGIT_ONE,\n    EIA608_CHAR_DIGIT_TWO,\n    EIA608_CHAR_DIGIT_THREE,\n    EIA608_CHAR_DIGIT_FOUR,\n    EIA608_CHAR_DIGIT_FIVE,\n    EIA608_CHAR_DIGIT_SIX,\n    EIA608_CHAR_DIGIT_SEVEN,\n    EIA608_CHAR_DIGIT_EIGHT,\n    EIA608_CHAR_DIGIT_NINE,\n    EIA608_CHAR_COLON,\n    EIA608_CHAR_SEMICOLON,\n    EIA608_CHAR_LESS_THAN_SIGN,\n    EIA608_CHAR_EQUALS_SIGN,\n    EIA608_CHAR_GREATER_THAN_SIGN,\n    EIA608_CHAR_QUESTION_MARK,\n    EIA608_CHAR_COMMERCIAL_AT,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_A,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_B,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_C,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_D,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_E,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_F,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_G,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_H,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_I,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_J,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_K,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_L,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_M,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_N,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_O,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_P,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_Q,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_R,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_S,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_T,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_U,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_V,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_W,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_X,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_Y,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_Z,\n    EIA608_CHAR_LEFT_SQUARE_BRACKET,\n    EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_ACUTE,\n    EIA608_CHAR_RIGHT_SQUARE_BRACKET,\n    EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_ACUTE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_ACUTE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_ACUTE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_A,\n    EIA608_CHAR_LATIN_SMALL_LETTER_B,\n    EIA608_CHAR_LATIN_SMALL_LETTER_C,\n    EIA608_CHAR_LATIN_SMALL_LETTER_D,\n    EIA608_CHAR_LATIN_SMALL_LETTER_E,\n    EIA608_CHAR_LATIN_SMALL_LETTER_F,\n    EIA608_CHAR_LATIN_SMALL_LETTER_G,\n    EIA608_CHAR_LATIN_SMALL_LETTER_H,\n    EIA608_CHAR_LATIN_SMALL_LETTER_I,\n    EIA608_CHAR_LATIN_SMALL_LETTER_J,\n    EIA608_CHAR_LATIN_SMALL_LETTER_K,\n    EIA608_CHAR_LATIN_SMALL_LETTER_L,\n    EIA608_CHAR_LATIN_SMALL_LETTER_M,\n    EIA608_CHAR_LATIN_SMALL_LETTER_N,\n    EIA608_CHAR_LATIN_SMALL_LETTER_O,\n    EIA608_CHAR_LATIN_SMALL_LETTER_P,\n    EIA608_CHAR_LATIN_SMALL_LETTER_Q,\n    EIA608_CHAR_LATIN_SMALL_LETTER_R,\n    EIA608_CHAR_LATIN_SMALL_LETTER_S,\n    EIA608_CHAR_LATIN_SMALL_LETTER_T,\n    EIA608_CHAR_LATIN_SMALL_LETTER_U,\n    EIA608_CHAR_LATIN_SMALL_LETTER_V,\n    EIA608_CHAR_LATIN_SMALL_LETTER_W,\n    EIA608_CHAR_LATIN_SMALL_LETTER_X,\n    EIA608_CHAR_LATIN_SMALL_LETTER_Y,\n    EIA608_CHAR_LATIN_SMALL_LETTER_Z,\n    EIA608_CHAR_LATIN_SMALL_LETTER_C_WITH_CEDILLA,\n    EIA608_CHAR_DIVISION_SIGN,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_N_WITH_TILDE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_N_WITH_TILDE,\n    EIA608_CHAR_FULL_BLOCK,\n    EIA608_CHAR_REGISTERED_SIGN,\n    EIA608_CHAR_DEGREE_SIGN,\n    EIA608_CHAR_VULGAR_FRACTION_ONE_HALF,\n    EIA608_CHAR_INVERTED_QUESTION_MARK,\n    EIA608_CHAR_TRADE_MARK_SIGN,\n    EIA608_CHAR_CENT_SIGN,\n    EIA608_CHAR_POUND_SIGN,\n    EIA608_CHAR_EIGHTH_NOTE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_GRAVE,\n    EIA608_CHAR_NO_BREAK_SPACE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_GRAVE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX,\n    EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX,\n    EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX,\n    EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX,\n    EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_ACUTE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_ACUTE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_ACUTE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_ACUTE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS,\n    EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_DIAERESIS,\n    EIA608_CHAR_LEFT_SINGLE_QUOTATION_MARK,\n    EIA608_CHAR_INVERTED_EXCLAMATION_MARK,\n    EIA608_CHAR_ASTERISK,\n    EIA608_CHAR_APOSTROPHE,\n    EIA608_CHAR_EM_DASH,\n    EIA608_CHAR_COPYRIGHT_SIGN,\n    EIA608_CHAR_SERVICE_MARK,\n    EIA608_CHAR_BULLET,\n    EIA608_CHAR_LEFT_DOUBLE_QUOTATION_MARK,\n    EIA608_CHAR_RIGHT_DOUBLE_QUOTATION_MARK,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_GRAVE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_C_WITH_CEDILLA,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_GRAVE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS,\n    EIA608_CHAR_LATIN_SMALL_LETTER_E_WITH_DIAERESIS,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS,\n    EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_DIAERESIS,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_GRAVE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_U_WITH_GRAVE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX,\n    EIA608_CHAR_LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK,\n    EIA608_CHAR_RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_TILDE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_TILDE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_ACUTE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_I_WITH_GRAVE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_I_WITH_GRAVE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_GRAVE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_GRAVE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_TILDE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_TILDE,\n    EIA608_CHAR_LEFT_CURLY_BRACKET,\n    EIA608_CHAR_RIGHT_CURLY_BRACKET,\n    EIA608_CHAR_REVERSE_SOLIDUS,\n    EIA608_CHAR_CIRCUMFLEX_ACCENT,\n    EIA608_CHAR_LOW_LINE,\n    EIA608_CHAR_VERTICAL_LINE,\n    EIA608_CHAR_TILDE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS,\n    EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_DIAERESIS,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS,\n    EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_DIAERESIS,\n    EIA608_CHAR_LATIN_SMALL_LETTER_SHARP_S,\n    EIA608_CHAR_YEN_SIGN,\n    EIA608_CHAR_CURRENCY_SIGN,\n    EIA608_CHAR_BROKEN_BAR,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_A_WITH_RING_ABOVE,\n    EIA608_CHAR_LATIN_CAPITAL_LETTER_O_WITH_STROKE,\n    EIA608_CHAR_LATIN_SMALL_LETTER_O_WITH_STROKE,\n    EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT,\n    EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT,\n    EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT,\n    EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT,\n};\n"
  },
  {
    "path": "deps/libcaption/src/eia608_from_utf8.c",
    "content": "/* Generated by re2c 1.0.3 on Tue Jun 19 17:18:11 2018 */\n/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#include \"utf8.h\"\n#include <ctype.h>\n#include <stdint.h>\n\nuint16_t _eia608_from_utf8 (const utf8_char_t* s)\n{\n    const unsigned char* YYMARKER = 0;\n    const unsigned char* YYCURSOR = (const unsigned char*) s;\n\n    if (0==s) { return 0x0000;}\n\n    \n{\n    unsigned char yych;\n    yych = *YYCURSOR;\n    if (yych <= '`') {\n        if (yych <= '*') {\n            if (yych <= '&') {\n                if (yych <= 0x00) goto yy2;\n                if (yych <= 0x1F) goto yy4;\n                goto yy6;\n            } else {\n                if (yych <= '\\'') goto yy8;\n                if (yych <= ')') goto yy6;\n                goto yy10;\n            }\n        } else {\n            if (yych <= ']') {\n                if (yych == '\\\\') goto yy12;\n                goto yy6;\n            } else {\n                if (yych <= '^') goto yy14;\n                if (yych <= '_') goto yy16;\n                goto yy18;\n            }\n        }\n    } else {\n        if (yych <= 0x7F) {\n            if (yych <= '|') {\n                if (yych <= 'z') goto yy6;\n                if (yych <= '{') goto yy20;\n                goto yy22;\n            } else {\n                if (yych <= '}') goto yy24;\n                if (yych <= '~') goto yy26;\n                goto yy28;\n            }\n        } else {\n            if (yych <= 0xC3) {\n                if (yych <= 0xC1) goto yy4;\n                if (yych <= 0xC2) goto yy30;\n                goto yy31;\n            } else {\n                if (yych == 0xE2) goto yy32;\n                goto yy4;\n            }\n        }\n    }\nyy2:\n    ++YYCURSOR;\n    { /*NULL*/ return 0x0000; }\nyy4:\n    ++YYCURSOR;\nyy5:\n    { /*DEFAULT_RULE*/ return 0x0000; }\nyy6:\n    ++YYCURSOR;\n    { /*ASCII range*/ return (s[0]<<8) &0xFF00; }\nyy8:\n    ++YYCURSOR;\n    { /*APOSTROPHE -> RIGHT_SINGLE_QUOTATION_MARK*/ return 0x1229; }\nyy10:\n    ++YYCURSOR;\n    { /*ASTERISK*/ return 0x1228; }\nyy12:\n    ++YYCURSOR;\n    { /*REVERSE_SOLIDUS*/ return 0x132B; }\nyy14:\n    ++YYCURSOR;\n    { /*CIRCUMFLEX_ACCENT*/ return 0x132C; }\nyy16:\n    ++YYCURSOR;\n    { /*LOW_LINE*/ return 0x132D; }\nyy18:\n    ++YYCURSOR;\n    { /*GRAVE_ACCENT -> LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; }\nyy20:\n    ++YYCURSOR;\n    { /*LEFT_CURLY_BRACKET*/ return 0x1329; }\nyy22:\n    ++YYCURSOR;\n    { /*VERTICAL_LINE*/ return 0x132E; }\nyy24:\n    ++YYCURSOR;\n    { /*RIGHT_CURLY_BRACKET*/ return 0x132A; }\nyy26:\n    ++YYCURSOR;\n    { /*TILDE*/ return 0x132F; }\nyy28:\n    ++YYCURSOR;\n    { /*DEL/BACKSPACE. Need to set bits 9 and 12! return 0x1421;*/ return 0x0000; }\nyy30:\n    yych = *++YYCURSOR;\n    switch (yych) {\n    case 0xA0:    goto yy33;\n    case 0xA1:    goto yy35;\n    case 0xA2:    goto yy37;\n    case 0xA3:    goto yy39;\n    case 0xA4:    goto yy41;\n    case 0xA5:    goto yy43;\n    case 0xA6:    goto yy45;\n    case 0xA9:    goto yy47;\n    case 0xAB:    goto yy49;\n    case 0xAE:    goto yy51;\n    case 0xB0:    goto yy53;\n    case 0xBB:    goto yy55;\n    case 0xBD:    goto yy57;\n    case 0xBF:    goto yy59;\n    default:    goto yy5;\n    }\nyy31:\n    yych = *++YYCURSOR;\n    switch (yych) {\n    case 0x80:    goto yy61;\n    case 0x81:    goto yy63;\n    case 0x82:    goto yy65;\n    case 0x83:    goto yy67;\n    case 0x84:    goto yy69;\n    case 0x85:    goto yy71;\n    case 0x87:    goto yy73;\n    case 0x88:    goto yy75;\n    case 0x89:    goto yy77;\n    case 0x8A:    goto yy79;\n    case 0x8B:    goto yy81;\n    case 0x8C:    goto yy83;\n    case 0x8D:    goto yy85;\n    case 0x8E:    goto yy87;\n    case 0x8F:    goto yy89;\n    case 0x91:    goto yy91;\n    case 0x92:    goto yy93;\n    case 0x93:    goto yy95;\n    case 0x94:    goto yy97;\n    case 0x95:    goto yy99;\n    case 0x96:    goto yy101;\n    case 0x98:    goto yy103;\n    case 0x99:    goto yy105;\n    case 0x9A:    goto yy107;\n    case 0x9B:    goto yy109;\n    case 0x9C:    goto yy111;\n    case 0x9F:    goto yy113;\n    case 0xA0:    goto yy115;\n    case 0xA1:    goto yy117;\n    case 0xA2:    goto yy119;\n    case 0xA3:    goto yy121;\n    case 0xA4:    goto yy123;\n    case 0xA5:    goto yy125;\n    case 0xA7:    goto yy127;\n    case 0xA8:    goto yy129;\n    case 0xA9:    goto yy131;\n    case 0xAA:    goto yy133;\n    case 0xAB:    goto yy135;\n    case 0xAC:    goto yy137;\n    case 0xAD:    goto yy139;\n    case 0xAE:    goto yy141;\n    case 0xAF:    goto yy143;\n    case 0xB1:    goto yy145;\n    case 0xB2:    goto yy147;\n    case 0xB3:    goto yy149;\n    case 0xB4:    goto yy151;\n    case 0xB5:    goto yy153;\n    case 0xB6:    goto yy155;\n    case 0xB7:    goto yy157;\n    case 0xB8:    goto yy159;\n    case 0xB9:    goto yy161;\n    case 0xBA:    goto yy163;\n    case 0xBB:    goto yy165;\n    case 0xBC:    goto yy167;\n    default:    goto yy5;\n    }\nyy32:\n    yych = *(YYMARKER = ++YYCURSOR);\n    switch (yych) {\n    case 0x80:    goto yy169;\n    case 0x84:    goto yy171;\n    case 0x94:    goto yy172;\n    case 0x96:    goto yy173;\n    case 0x99:    goto yy174;\n    default:    goto yy5;\n    }\nyy33:\n    ++YYCURSOR;\n    { /*NO_BREAK_SPACE*/ return 0x1139; }\nyy35:\n    ++YYCURSOR;\n    { /*INVERTED_EXCLAMATION_MARK*/ return 0x1227; }\nyy37:\n    ++YYCURSOR;\n    { /*CENT_SIGN*/ return 0x1135; }\nyy39:\n    ++YYCURSOR;\n    { /*POUND_SIGN*/ return 0x1136; }\nyy41:\n    ++YYCURSOR;\n    { /*CURRENCY_SIGN*/ return 0x1336; }\nyy43:\n    ++YYCURSOR;\n    { /*YEN_SIGN*/ return 0x1335; }\nyy45:\n    ++YYCURSOR;\n    { /*BROKEN_BAR*/ return 0x1337; }\nyy47:\n    ++YYCURSOR;\n    { /*COPYRIGHT_SIGN*/ return 0x122B; }\nyy49:\n    ++YYCURSOR;\n    { /*LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK*/ return 0x123E; }\nyy51:\n    ++YYCURSOR;\n    { /*REGISTERED_SIGN*/ return 0x1130; }\nyy53:\n    ++YYCURSOR;\n    { /*DEGREE_SIGN*/ return 0x1131; }\nyy55:\n    ++YYCURSOR;\n    { /*RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK*/ return 0x123F; }\nyy57:\n    ++YYCURSOR;\n    { /*VULGAR_FRACTION_ONE_HALF*/ return 0x1132; }\nyy59:\n    ++YYCURSOR;\n    { /*INVERTED_QUESTION_MARK*/ return 0x1133; }\nyy61:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_A_WITH_GRAVE*/ return 0x1230; }\nyy63:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_A_WITH_ACUTE*/ return 0x1220; }\nyy65:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX*/ return 0x1231; }\nyy67:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_A_WITH_TILDE*/ return 0x1320; }\nyy69:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS*/ return 0x1330; }\nyy71:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE*/ return 0x1338; }\nyy73:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_C_WITH_CEDILLA*/ return 0x1232; }\nyy75:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_E_WITH_GRAVE*/ return 0x1233; }\nyy77:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_E_WITH_ACUTE*/ return 0x1221; }\nyy79:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX*/ return 0x1234; }\nyy81:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS*/ return 0x1235; }\nyy83:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_I_WITH_GRAVE*/ return 0x1323; }\nyy85:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_I_WITH_ACUTE*/ return 0x1322; }\nyy87:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX*/ return 0x1237; }\nyy89:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS*/ return 0x1238; }\nyy91:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_N_WITH_TILDE*/ return 0x7D00; }\nyy93:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_O_WITH_GRAVE*/ return 0x1325; }\nyy95:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_O_WITH_ACUTE*/ return 0x1222; }\nyy97:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX*/ return 0x123A; }\nyy99:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_O_WITH_TILDE*/ return 0x1327; }\nyy101:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS*/ return 0x1332; }\nyy103:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_O_WITH_STROKE*/ return 0x133A; }\nyy105:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_U_WITH_GRAVE*/ return 0x123B; }\nyy107:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_U_WITH_ACUTE*/ return 0x1223; }\nyy109:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX*/ return 0x123D; }\nyy111:\n    ++YYCURSOR;\n    { /*LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS*/ return 0x1224; }\nyy113:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_SHARP_S*/ return 0x1334; }\nyy115:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_A_WITH_GRAVE*/ return 0x1138; }\nyy117:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_A_WITH_ACUTE*/ return 0x2A00; }\nyy119:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX*/ return 0x113B; }\nyy121:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_A_WITH_TILDE*/ return 0x1321; }\nyy123:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_A_WITH_DIAERESIS*/ return 0x1331; }\nyy125:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_A_WITH_RING_ABOVE*/ return 0x1339; }\nyy127:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_C_WITH_CEDILLA*/ return 0x7B00; }\nyy129:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_E_WITH_GRAVE*/ return 0x113A; }\nyy131:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_E_WITH_ACUTE*/ return 0x5C00; }\nyy133:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX*/ return 0x113C; }\nyy135:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_E_WITH_DIAERESIS*/ return 0x1236; }\nyy137:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_I_WITH_GRAVE*/ return 0x1324; }\nyy139:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_I_WITH_ACUTE*/ return 0x5E00; }\nyy141:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX*/ return 0x113D; }\nyy143:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_I_WITH_DIAERESIS*/ return 0x1239; }\nyy145:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_N_WITH_TILDE*/ return 0x7E00; }\nyy147:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_O_WITH_GRAVE*/ return 0x1326; }\nyy149:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_O_WITH_ACUTE*/ return 0x5F00; }\nyy151:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX*/ return 0x113E; }\nyy153:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_O_WITH_TILDE*/ return 0x1328; }\nyy155:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_O_WITH_DIAERESIS*/ return 0x1333; }\nyy157:\n    ++YYCURSOR;\n    { /*DIVISION_SIGN*/ return 0x7C00; }\nyy159:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_O_WITH_STROKE*/ return 0x133B; }\nyy161:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_U_WITH_GRAVE*/ return 0x123C; }\nyy163:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_U_WITH_ACUTE*/ return 0x6000; }\nyy165:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX*/ return 0x113F; }\nyy167:\n    ++YYCURSOR;\n    { /*LATIN_SMALL_LETTER_U_WITH_DIAERESIS*/ return 0x1225; }\nyy169:\n    yych = *++YYCURSOR;\n    switch (yych) {\n    case 0x94:    goto yy175;\n    case 0x98:    goto yy177;\n    case 0x99:    goto yy179;\n    case 0x9C:    goto yy181;\n    case 0x9D:    goto yy183;\n    case 0xA2:    goto yy185;\n    default:    goto yy170;\n    }\nyy170:\n    YYCURSOR = YYMARKER;\n    goto yy5;\nyy171:\n    yych = *++YYCURSOR;\n    if (yych == 0xA0) goto yy187;\n    if (yych == 0xA2) goto yy189;\n    goto yy170;\nyy172:\n    yych = *++YYCURSOR;\n    switch (yych) {\n    case 0x8C:    goto yy191;\n    case 0x90:    goto yy193;\n    case 0x94:    goto yy195;\n    case 0x98:    goto yy197;\n    default:    goto yy170;\n    }\nyy173:\n    yych = *++YYCURSOR;\n    if (yych == 0x88) goto yy199;\n    goto yy170;\nyy174:\n    yych = *++YYCURSOR;\n    if (yych == 0xAA) goto yy201;\n    goto yy170;\nyy175:\n    ++YYCURSOR;\n    { /*EM_DASH*/ return 0x122A; }\nyy177:\n    ++YYCURSOR;\n    { /*LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; }\nyy179:\n    ++YYCURSOR;\n    { /*RIGHT_SINGLE_QUOTATION_MARK -> APOSTROPHE*/ return 0x2700; }\nyy181:\n    ++YYCURSOR;\n    { /*LEFT_DOUBLE_QUOTATION_MARK*/ return 0x122E; }\nyy183:\n    ++YYCURSOR;\n    { /*RIGHT_DOUBLE_QUOTATION_MARK*/ return 0x122F; }\nyy185:\n    ++YYCURSOR;\n    { /*BULLET*/ return 0x122D; }\nyy187:\n    ++YYCURSOR;\n    { /*SERVICE_MARK*/ return 0x122C; }\nyy189:\n    ++YYCURSOR;\n    { /*TRADE_MARK_SIGN*/ return 0x1134; }\nyy191:\n    ++YYCURSOR;\n    { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT*/ return 0x133C; }\nyy193:\n    ++YYCURSOR;\n    { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT*/ return 0x133D; }\nyy195:\n    ++YYCURSOR;\n    { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT*/ return 0x133E; }\nyy197:\n    ++YYCURSOR;\n    { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT*/ return 0x133F; }\nyy199:\n    ++YYCURSOR;\n    { /*FULL_BLOCK*/ return 0x7F00; }\nyy201:\n    ++YYCURSOR;\n    { /*EIGHTH_NOTE*/ return 0x1137; }\n}\n\n}\n"
  },
  {
    "path": "deps/libcaption/src/eia608_from_utf8.re2c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#include \"utf8.h\"\n#include <ctype.h>\n#include <stdint.h>\n\nuint16_t _eia608_from_utf8 (const utf8_char_t* s)\n{\n    const unsigned char* YYMARKER = 0;\n    const unsigned char* YYCURSOR = (const unsigned char*) s;\n\n    if (0==s) { return 0x0000;}\n\n    /*!re2c\n    re2c:yyfill:enable = 0;\n    re2c:indent:string = \"    \";\n    re2c:define:YYCTYPE = \"unsigned char\";\n\n    /*Ascii Exceptions*/\n    \"\\x00\" { /*NULL*/ return 0x0000; }\n    \"\\x27\" { /*APOSTROPHE -> RIGHT_SINGLE_QUOTATION_MARK*/ return 0x1229; }\n    \"\\x2A\" { /*ASTERISK*/ return 0x1228; }\n    \"\\x5C\" { /*REVERSE_SOLIDUS*/ return 0x132B; }\n    \"\\x5E\" { /*CIRCUMFLEX_ACCENT*/ return 0x132C; }\n    \"\\x5F\" { /*LOW_LINE*/ return 0x132D; }\n    /*Map GRAVE_ACCENT to a LEFT_SINGLE_QUOTATION_MARK so we have a cc_data for every printable ASCII value*/\n    \"\\x60\" { /*GRAVE_ACCENT -> LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; }\n    \"\\x7B\" { /*LEFT_CURLY_BRACKET*/ return 0x1329; }\n    \"\\x7C\" { /*VERTICAL_LINE*/ return 0x132E; }\n    \"\\x7D\" { /*RIGHT_CURLY_BRACKET*/ return 0x132A; }\n    \"\\x7E\" { /*TILDE*/ return 0x132F; }\n    /*There is a control equivalent. Haven't decided if we want to handle that here or not*/\n    \"\\x7F\" { /*DEL/BACKSPACE. Need to set bits 9 and 12! return 0x1421;*/ return 0x0000; }\n\n    /* Rules are processed top to bottom. So All single byte chars MUST be above this line!*/\n    [\\x20-\\x7F] { /*ASCII range*/ return (s[0]<<8) &0xFF00; } /* Should we use yych instead of s[0]?*/\n\n    /*This is the second half of the ascii exceptions*/\n    \"\\xC3\\xA1\" { /*LATIN_SMALL_LETTER_A_WITH_ACUTE*/ return 0x2A00; }\n    \"\\xC3\\xA9\" { /*LATIN_SMALL_LETTER_E_WITH_ACUTE*/ return 0x5C00; }\n    \"\\xC3\\xAD\" { /*LATIN_SMALL_LETTER_I_WITH_ACUTE*/ return 0x5E00; }\n    \"\\xC3\\xB3\" { /*LATIN_SMALL_LETTER_O_WITH_ACUTE*/ return 0x5F00; }\n    \"\\xC3\\xBA\" { /*LATIN_SMALL_LETTER_U_WITH_ACUTE*/ return 0x6000; }\n    \"\\xC3\\xA7\" { /*LATIN_SMALL_LETTER_C_WITH_CEDILLA*/ return 0x7B00; }\n    \"\\xC3\\xB7\" { /*DIVISION_SIGN*/ return 0x7C00; }\n    \"\\xC3\\x91\" { /*LATIN_CAPITAL_LETTER_N_WITH_TILDE*/ return 0x7D00; }\n    \"\\xC3\\xB1\" { /*LATIN_SMALL_LETTER_N_WITH_TILDE*/ return 0x7E00; }\n    \"\\xE2\\x96\\x88\" { /*FULL_BLOCK*/ return 0x7F00; }\n\n    /*Special North American character set*/\n    \"\\xC2\\xAE\" { /*REGISTERED_SIGN*/ return 0x1130; }\n    \"\\xC2\\xB0\" { /*DEGREE_SIGN*/ return 0x1131; }\n    \"\\xC2\\xBD\" { /*VULGAR_FRACTION_ONE_HALF*/ return 0x1132; }\n    \"\\xC2\\xBF\" { /*INVERTED_QUESTION_MARK*/ return 0x1133; }\n    \"\\xE2\\x84\\xA2\" { /*TRADE_MARK_SIGN*/ return 0x1134; }\n    \"\\xC2\\xA2\" { /*CENT_SIGN*/ return 0x1135; }\n    \"\\xC2\\xA3\" { /*POUND_SIGN*/ return 0x1136; }\n    \"\\xE2\\x99\\xAA\" { /*EIGHTH_NOTE*/ return 0x1137; }\n    \"\\xC3\\xA0\" { /*LATIN_SMALL_LETTER_A_WITH_GRAVE*/ return 0x1138; }\n    \"\\xC2\\xA0\" { /*NO_BREAK_SPACE*/ return 0x1139; }\n    \"\\xC3\\xA8\" { /*LATIN_SMALL_LETTER_E_WITH_GRAVE*/ return 0x113A; }\n    \"\\xC3\\xA2\" { /*LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX*/ return 0x113B; }\n    \"\\xC3\\xAA\" { /*LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX*/ return 0x113C; }\n    \"\\xC3\\xAE\" { /*LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX*/ return 0x113D; }\n    \"\\xC3\\xB4\" { /*LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX*/ return 0x113E; }\n    \"\\xC3\\xBB\" { /*LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX*/ return 0x113F; }\n\n    /*Extended Spanish/Miscellaneous*/\n    \"\\xC3\\x81\" { /*LATIN_CAPITAL_LETTER_A_WITH_ACUTE*/ return 0x1220; }\n    \"\\xC3\\x89\" { /*LATIN_CAPITAL_LETTER_E_WITH_ACUTE*/ return 0x1221; }\n    \"\\xC3\\x93\" { /*LATIN_CAPITAL_LETTER_O_WITH_ACUTE*/ return 0x1222; }\n    \"\\xC3\\x9A\" { /*LATIN_CAPITAL_LETTER_U_WITH_ACUTE*/ return 0x1223; }\n    \"\\xC3\\x9C\" { /*LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS*/ return 0x1224; }\n    \"\\xC3\\xBC\" { /*LATIN_SMALL_LETTER_U_WITH_DIAERESIS*/ return 0x1225; }\n    \"\\xE2\\x80\\x98\" { /*LEFT_SINGLE_QUOTATION_MARK*/ return 0x1226; }\n    \"\\xC2\\xA1\" { /*INVERTED_EXCLAMATION_MARK*/ return 0x1227; }\n    /*ASTERISK handled in ASCII mapping*/\n    \"\\xE2\\x80\\x99\" { /*RIGHT_SINGLE_QUOTATION_MARK -> APOSTROPHE*/ return 0x2700; }\n    \"\\xE2\\x80\\x94\" { /*EM_DASH*/ return 0x122A; }\n    \"\\xC2\\xA9\" { /*COPYRIGHT_SIGN*/ return 0x122B; }\n    \"\\xE2\\x84\\xA0\" { /*SERVICE_MARK*/ return 0x122C; }\n    \"\\xE2\\x80\\xA2\" { /*BULLET*/ return 0x122D; }\n    \"\\xE2\\x80\\x9C\" { /*LEFT_DOUBLE_QUOTATION_MARK*/ return 0x122E; }\n    \"\\xE2\\x80\\x9D\" { /*RIGHT_DOUBLE_QUOTATION_MARK*/ return 0x122F; }\n\n    /*Extended French*/\n    \"\\xC3\\x80\" { /*LATIN_CAPITAL_LETTER_A_WITH_GRAVE*/ return 0x1230; }\n    \"\\xC3\\x82\" { /*LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX*/ return 0x1231; }\n    \"\\xC3\\x87\" { /*LATIN_CAPITAL_LETTER_C_WITH_CEDILLA*/ return 0x1232; }\n    \"\\xC3\\x88\" { /*LATIN_CAPITAL_LETTER_E_WITH_GRAVE*/ return 0x1233; }\n    \"\\xC3\\x8A\" { /*LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX*/ return 0x1234; }\n    \"\\xC3\\x8B\" { /*LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS*/ return 0x1235; }\n    \"\\xC3\\xAB\" { /*LATIN_SMALL_LETTER_E_WITH_DIAERESIS*/ return 0x1236; }\n    \"\\xC3\\x8E\" { /*LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX*/ return 0x1237; }\n    \"\\xC3\\x8F\" { /*LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS*/ return 0x1238; }\n    \"\\xC3\\xAF\" { /*LATIN_SMALL_LETTER_I_WITH_DIAERESIS*/ return 0x1239; }\n    \"\\xC3\\x94\" { /*LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX*/ return 0x123A; }\n    \"\\xC3\\x99\" { /*LATIN_CAPITAL_LETTER_U_WITH_GRAVE*/ return 0x123B; }\n    \"\\xC3\\xB9\" { /*LATIN_SMALL_LETTER_U_WITH_GRAVE*/ return 0x123C; }\n    \"\\xC3\\x9B\" { /*LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX*/ return 0x123D; }\n    \"\\xC2\\xAB\" { /*LEFT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK*/ return 0x123E; }\n    \"\\xC2\\xBB\" { /*RIGHT_POINTING_DOUBLE_ANGLE_QUOTATION_MARK*/ return 0x123F; }\n\n    /*Portuguese*/\n    \"\\xC3\\x83\" { /*LATIN_CAPITAL_LETTER_A_WITH_TILDE*/ return 0x1320; }\n    \"\\xC3\\xA3\" { /*LATIN_SMALL_LETTER_A_WITH_TILDE*/ return 0x1321; }\n    \"\\xC3\\x8D\" { /*LATIN_CAPITAL_LETTER_I_WITH_ACUTE*/ return 0x1322; }\n    \"\\xC3\\x8C\" { /*LATIN_CAPITAL_LETTER_I_WITH_GRAVE*/ return 0x1323; }\n    \"\\xC3\\xAC\" { /*LATIN_SMALL_LETTER_I_WITH_GRAVE*/ return 0x1324; }\n    \"\\xC3\\x92\" { /*LATIN_CAPITAL_LETTER_O_WITH_GRAVE*/ return 0x1325; }\n    \"\\xC3\\xB2\" { /*LATIN_SMALL_LETTER_O_WITH_GRAVE*/ return 0x1326; }\n    \"\\xC3\\x95\" { /*LATIN_CAPITAL_LETTER_O_WITH_TILDE*/ return 0x1327; }\n    \"\\xC3\\xB5\" { /*LATIN_SMALL_LETTER_O_WITH_TILDE*/ return 0x1328; }\n    /*LEFT_CURLY_BRACKET handled in ASCII mapping*/\n    /*RIGHT_CURLY_BRACKET handled in ASCII mapping*/\n    /*REVERSE_SOLIDUS handled in ASCII mapping*/\n    /*CIRCUMFLEX_ACCENT handled in ASCII mapping*/\n    /*LOW_LINE handled in ASCII mapping*/\n    /*VERTICAL_LINE handled in ASCII mapping*/\n    /*TILDE handled in ASCII mapping*/\n\n    /*German/Danish*/\n    \"\\xC3\\x84\" { /*LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS*/ return 0x1330; }\n    \"\\xC3\\xA4\" { /*LATIN_SMALL_LETTER_A_WITH_DIAERESIS*/ return 0x1331; }\n    \"\\xC3\\x96\" { /*LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS*/ return 0x1332; }\n    \"\\xC3\\xB6\" { /*LATIN_SMALL_LETTER_O_WITH_DIAERESIS*/ return 0x1333; }\n    \"\\xC3\\x9F\" { /*LATIN_SMALL_LETTER_SHARP_S*/ return 0x1334; }\n    \"\\xC2\\xA5\" { /*YEN_SIGN*/ return 0x1335; }\n    \"\\xC2\\xA4\" { /*CURRENCY_SIGN*/ return 0x1336; }\n    \"\\xC2\\xA6\" { /*BROKEN_BAR*/ return 0x1337; }\n    \"\\xC3\\x85\" { /*LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE*/ return 0x1338; }\n    \"\\xC3\\xA5\" { /*LATIN_SMALL_LETTER_A_WITH_RING_ABOVE*/ return 0x1339; }\n    \"\\xC3\\x98\" { /*LATIN_CAPITAL_LETTER_O_WITH_STROKE*/ return 0x133A; }\n    \"\\xC3\\xB8\" { /*LATIN_SMALL_LETTER_O_WITH_STROKE*/ return 0x133B; }\n    \"\\xE2\\x94\\x8C\" { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT*/ return 0x133C; }\n    \"\\xE2\\x94\\x90\" { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT*/ return 0x133D; }\n    \"\\xE2\\x94\\x94\" { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT*/ return 0x133E; }\n    \"\\xE2\\x94\\x98\" { /*EIA608_CHAR_BOX_DRAWINGS_LIGHT_UP_AND_LEFT*/ return 0x133F; }\n\n    /*Default rule*/\n    * { /*DEFAULT_RULE*/ return 0x0000; }\n    */\n}\n"
  },
  {
    "path": "deps/libcaption/src/mpeg.c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n\n#include \"mpeg.h\"\n#include <assert.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n////////////////////////////////////////////////////////////////////////////////\n// AVC RBSP Methods\n//  TODO move the to a avcutils file\nstatic size_t _find_emulation_prevention_byte(const uint8_t* data, size_t size)\n{\n    size_t offset = 2;\n\n    while (offset < size) {\n        if (0 == data[offset]) {\n            // 0 0 X 3 //; we know X is zero\n            offset += 1;\n        } else if (3 != data[offset]) {\n            // 0 0 X 0 0 3; we know X is not 0 and not 3\n            offset += 3;\n        } else if (0 != data[offset - 1]) {\n            // 0 X 0 0 3\n            offset += 2;\n        } else if (0 != data[offset - 2]) {\n            // X 0 0 3\n            offset += 1;\n        } else {\n            // 0 0 3\n            return offset;\n        }\n    }\n\n    return size;\n}\n\nstatic size_t _copy_to_rbsp(uint8_t* destData, size_t destSize, const uint8_t* sorcData, size_t sorcSize)\n{\n    size_t toCopy, totlSize = 0;\n\n    for (;;) {\n        if (destSize >= sorcSize) {\n            return 0;\n        }\n\n        // The following line IS correct! We want to look in sorcData up to destSize bytes\n        // We know destSize is smaller than sorcSize because of the previous line\n        toCopy = _find_emulation_prevention_byte(sorcData, destSize);\n        memcpy(destData, sorcData, toCopy);\n        totlSize += toCopy;\n        destData += toCopy;\n        destSize -= toCopy;\n\n        if (0 == destSize) {\n            return totlSize;\n        }\n\n        // skip the emulation prevention byte\n        totlSize += 1;\n        sorcData += toCopy + 1;\n        sorcSize -= toCopy + 1;\n    }\n\n    return 0;\n}\n////////////////////////////////////////////////////////////////////////////////\nstatic inline size_t _find_emulated(uint8_t* data, size_t size)\n{\n    size_t offset = 2;\n\n    while (offset < size) {\n        if (3 < data[offset]) {\n            // 0 0 X; we know X is not 0, 1, 2 or 3\n            offset += 3;\n        } else if (0 != data[offset - 1]) {\n            // 0 X 0 0 1\n            offset += 2;\n        } else if (0 != data[offset - 2]) {\n            // X 0 0 1\n            offset += 1;\n        } else {\n            // 0 0 0, 0 0 1\n            return offset;\n        }\n    }\n\n    return size;\n}\n\nsize_t _copy_from_rbsp(uint8_t* data, uint8_t* payloadData, size_t payloadSize)\n{\n    size_t total = 0;\n\n    while (payloadSize) {\n        size_t bytes = _find_emulated(payloadData, payloadSize);\n\n        if (bytes > payloadSize) {\n            return 0;\n        }\n\n        memcpy(data, payloadData, bytes);\n\n        if (bytes == payloadSize) {\n            return total + bytes;\n        }\n\n        data[bytes] = 3; // insert emulation prevention byte\n        data += bytes + 1;\n        total += bytes + 1;\n        payloadData += bytes;\n        payloadSize -= bytes;\n    }\n\n    return total;\n}\n////////////////////////////////////////////////////////////////////////////////\nsei_message_t* sei_message_next(sei_message_t* msg) { return ((struct _sei_message_t*)msg)->next; }\nsei_msgtype_t sei_message_type(sei_message_t* msg) { return ((struct _sei_message_t*)msg)->type; }\nsize_t sei_message_size(sei_message_t* msg) { return ((struct _sei_message_t*)msg)->size; }\nuint8_t* sei_message_data(sei_message_t* msg) { return ((uint8_t*)msg) + sizeof(struct _sei_message_t); }\nvoid sei_message_free(sei_message_t* msg)\n{\n    if (msg) {\n        free(msg);\n    }\n}\n\nsei_message_t* sei_message_new(sei_msgtype_t type, uint8_t* data, size_t size)\n{\n    struct _sei_message_t* msg = (struct _sei_message_t*)malloc(sizeof(struct _sei_message_t) + size);\n    msg->next = 0;\n    msg->type = type;\n    msg->size = size;\n\n    if (data) {\n        memcpy(sei_message_data(msg), data, size);\n    } else {\n        memset(sei_message_data(msg), 0, size);\n    }\n\n    return (sei_message_t*)msg;\n}\n////////////////////////////////////////////////////////////////////////////////\nvoid sei_init(sei_t* sei, double timestamp)\n{\n    sei->head = 0;\n    sei->tail = 0;\n    sei->timestamp = timestamp;\n}\n\nvoid sei_message_append(sei_t* sei, sei_message_t* msg)\n{\n    if (0 == sei->head) {\n        sei->head = msg;\n        sei->tail = msg;\n    } else {\n        sei->tail->next = msg;\n        sei->tail = msg;\n    }\n}\n\nvoid sei_cat(sei_t* to, sei_t* from, int itu_t_t35)\n{\n    if (!to || !from) {\n        return;\n    }\n\n    sei_message_t* msg = NULL;\n    for (msg = sei_message_head(from); msg; msg = sei_message_next(msg)) {\n        if (itu_t_t35 || sei_type_user_data_registered_itu_t_t35 != msg->type) {\n            sei_message_append(to, sei_message_copy(msg));\n        }\n    }\n}\n\nvoid sei_free(sei_t* sei)\n{\n    sei_message_t* tail;\n\n    while (sei->head) {\n        tail = sei->head->next;\n        free(sei->head);\n        sei->head = tail;\n    }\n\n    sei_init(sei, 0);\n}\n\nvoid sei_dump(sei_t* sei)\n{\n    fprintf(stderr, \"SEI %p\\n\", sei);\n    sei_dump_messages(sei->head, sei->timestamp);\n}\n\nvoid sei_dump_messages(sei_message_t* head, double timestamp)\n{\n    cea708_t cea708;\n    sei_message_t* msg;\n    cea708_init(&cea708, timestamp);\n\n    for (msg = head; msg; msg = sei_message_next(msg)) {\n        uint8_t* data = sei_message_data(msg);\n        size_t size = sei_message_size(msg);\n        fprintf(stderr, \"-- Message %p\\n-- Message Type: %d\\n-- Message Size: %d\\n\", data, sei_message_type(msg), (int)size);\n\n        while (size) {\n            fprintf(stderr, \"%02X \", *data);\n            ++data;\n            --size;\n        }\n\n        fprintf(stderr, \"\\n\");\n\n        if (sei_type_user_data_registered_itu_t_t35 == sei_message_type(msg)) {\n            if (LIBCAPTION_OK != cea708_parse_h262(sei_message_data(msg), sei_message_size(msg), &cea708)) {\n                fprintf(stderr, \"cea708_parse error\\n\");\n            } else {\n                cea708_dump(&cea708);\n            }\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////\nsize_t sei_render_size(sei_t* sei)\n{\n    if (!sei || !sei->head) {\n        return 0;\n    }\n\n    size_t size = 2; // nalu_type + stop bit\n    sei_message_t* msg;\n    for (msg = sei_message_head(sei); msg; msg = sei_message_next(msg)) {\n        size += 1 + (msg->type / 255);\n        size += 1 + (msg->size / 255);\n        size += 1 + (msg->size * 4 / 3);\n    }\n\n    return size;\n}\n\n// we can safely assume sei_render_size() bytes have been allocated for data\nsize_t sei_render(sei_t* sei, uint8_t* data)\n{\n    if (!sei || !sei->head) {\n        return 0;\n    }\n\n    size_t escaped_size, size = 2; // nalu_type + stop bit\n    sei_message_t* msg;\n    (*data) = 6;\n    ++data;\n\n    for (msg = sei_message_head(sei); msg; msg = sei_message_next(msg)) {\n        int payloadType = sei_message_type(msg);\n        int payloadSize = (int)sei_message_size(msg);\n        uint8_t* payloadData = sei_message_data(msg);\n\n        while (255 <= payloadType) {\n            (*data) = 255;\n            ++data;\n            ++size;\n            payloadType -= 255;\n        }\n\n        (*data) = payloadType;\n        ++data;\n        ++size;\n\n        while (255 <= payloadSize) {\n            (*data) = 255;\n            ++data;\n            ++size;\n            payloadSize -= 255;\n        }\n\n        (*data) = payloadSize;\n        ++data;\n        ++size;\n\n        if (0 >= (escaped_size = _copy_from_rbsp(data, payloadData, payloadSize))) {\n            return 0;\n        }\n\n        data += escaped_size;\n        size += escaped_size;\n    }\n\n    // write stop bit and return\n    (*data) = 0x80;\n    return size;\n}\n\nuint8_t* sei_render_alloc(sei_t* sei, size_t* size)\n{\n    size_t aloc = sei_render_size(sei);\n    uint8_t* data = malloc(aloc);\n    (*size) = sei_render(sei, data);\n    return data;\n}\n\n////////////////////////////////////////////////////////////////////////////////\nlibcaption_stauts_t sei_parse(sei_t* sei, const uint8_t* data, size_t size, double timestamp)\n{\n    sei_init(sei, timestamp);\n    int ret = 0;\n\n    // SEI may contain more than one payload\n    while (1 < size) {\n        size_t payloadType = 0;\n        size_t payloadSize = 0;\n\n        while (0 < size && 255 == (*data)) {\n            payloadType += 255;\n            ++data, --size;\n        }\n\n        if (0 == size) {\n            return LIBCAPTION_ERROR;\n        }\n\n        payloadType += (*data);\n        ++data, --size;\n\n        while (0 < size && 255 == (*data)) {\n            payloadSize += 255;\n            ++data, --size;\n        }\n\n        if (0 == size) {\n            return LIBCAPTION_ERROR;\n        }\n\n        payloadSize += (*data);\n        ++data, --size;\n\n        if (payloadSize) {\n            sei_message_t* msg = sei_message_new((sei_msgtype_t)payloadType, 0, payloadSize);\n            uint8_t* payloadData = sei_message_data(msg);\n            size_t bytes = _copy_to_rbsp(payloadData, payloadSize, data, size);\n            sei_message_append(sei, msg);\n\n            if (bytes < payloadSize) {\n                return LIBCAPTION_ERROR;\n            }\n\n            data += bytes;\n            size -= bytes;\n            ++ret;\n        }\n    }\n\n    // There should be one trailing byte, 0x80. But really, we can just ignore that fact.\n    return LIBCAPTION_OK;\n}\n////////////////////////////////////////////////////////////////////////////////\nlibcaption_stauts_t sei_to_caption_frame(sei_t* sei, caption_frame_t* frame)\n{\n    cea708_t cea708;\n    sei_message_t* msg;\n    libcaption_stauts_t status = LIBCAPTION_OK;\n\n    cea708_init(&cea708, frame->timestamp);\n\n    for (msg = sei_message_head(sei); msg; msg = sei_message_next(msg)) {\n        if (sei_type_user_data_registered_itu_t_t35 == sei_message_type(msg)) {\n            cea708_parse_h264(sei_message_data(msg), sei_message_size(msg), &cea708);\n            status = libcaption_status_update(status, cea708_to_caption_frame(frame, &cea708));\n        }\n    }\n\n    if (LIBCAPTION_READY == status) {\n        frame->timestamp = sei->timestamp;\n    }\n\n    return status;\n}\n////////////////////////////////////////////////////////////////////////////////\n#define DEFAULT_CHANNEL 0\n\nvoid sei_append_708(sei_t* sei, cea708_t* cea708)\n{\n    sei_message_t* msg = sei_message_new(sei_type_user_data_registered_itu_t_t35, 0, CEA608_MAX_SIZE);\n    msg->size = cea708_render(cea708, sei_message_data(msg), sei_message_size(msg));\n    sei_message_append(sei, msg);\n    cea708_init(cea708, sei->timestamp); // will confgure using HLS compatiable defaults\n}\n\n// This should be moved to 708.c\n// This works for popon, but bad for paint on and roll up\n// Please understand this function before you try to use it, setting null values have different effects than you may assume\nvoid sei_encode_eia608(sei_t* sei, cea708_t* cea708, uint16_t cc_data)\n{\n    // This one is full, flush and init a new one\n    // shoudl this be 32? I cant remember\n    if (31 == cea708->user_data.cc_count) {\n        sei_append_708(sei, cea708);\n    }\n\n    if (0 == cea708->user_data.cc_count) { // This is a new 708 header, but a continuation of a 608 stream\n        cea708_add_cc_data(cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_resume_caption_loading, DEFAULT_CHANNEL));\n        cea708_add_cc_data(cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_resume_caption_loading, DEFAULT_CHANNEL));\n    }\n\n    if (0 == cc_data) { // Finished\n        sei_encode_eia608(sei, cea708, eia608_control_command(eia608_control_end_of_caption, DEFAULT_CHANNEL));\n        sei_encode_eia608(sei, cea708, eia608_control_command(eia608_control_end_of_caption, DEFAULT_CHANNEL));\n        sei_append_708(sei, cea708);\n        return;\n    }\n\n    cea708_add_cc_data(cea708, 1, cc_type_ntsc_cc_field_1, cc_data);\n}\n////////////////////////////////////////////////////////////////////////////////\n// TODO move this out of sei\nlibcaption_stauts_t sei_from_caption_frame(sei_t* sei, caption_frame_t* frame)\n{\n    int r, c;\n    int unl, prev_unl;\n    cea708_t cea708;\n    const char* data;\n    uint16_t prev_cc_data;\n    eia608_style_t styl, prev_styl;\n\n    sei_init(sei, frame->timestamp);\n    cea708_init(&cea708, frame->timestamp); // set up a new popon frame\n    cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_non_displayed_memory, DEFAULT_CHANNEL));\n    cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_resume_caption_loading, DEFAULT_CHANNEL));\n\n    for (r = 0; r < SCREEN_ROWS; ++r) {\n        prev_unl = 0, prev_styl = eia608_style_white;\n        // Calculate preamble\n        for (c = 0; c < SCREEN_COLS && 0 == *caption_frame_read_char(frame, r, c, &styl, &unl); ++c) {\n        }\n\n        // This row is blank\n        if (SCREEN_COLS == c) {\n            continue;\n        }\n\n        // Write preamble\n        if (0 < c || (0 == unl && eia608_style_white == styl)) {\n            int tab = c % 4;\n            sei_encode_eia608(sei, &cea708, eia608_row_column_pramble(r, c, DEFAULT_CHANNEL, 0));\n            if (tab) {\n                sei_encode_eia608(sei, &cea708, eia608_tab(tab, DEFAULT_CHANNEL));\n            }\n        } else {\n            sei_encode_eia608(sei, &cea708, eia608_row_style_pramble(r, DEFAULT_CHANNEL, styl, unl));\n            prev_unl = unl, prev_styl = styl;\n        }\n\n        // Write the row\n        for (prev_cc_data = 0, data = caption_frame_read_char(frame, r, c, 0, 0);\n             (*data) && c < SCREEN_COLS; ++c, data = caption_frame_read_char(frame, r, c, &styl, &unl)) {\n            uint16_t cc_data = eia608_from_utf8_1(data, DEFAULT_CHANNEL);\n\n            if (unl != prev_unl || styl != prev_styl) {\n                sei_encode_eia608(sei, &cea708, eia608_midrow_change(DEFAULT_CHANNEL, styl, unl));\n                prev_unl = unl, prev_styl = styl;\n            }\n\n            if (!cc_data) {\n                // We do't want to write bad data, so just ignore it.\n            } else if (eia608_is_basicna(prev_cc_data)) {\n                if (eia608_is_basicna(cc_data)) {\n                    // previous and current chars are both basicna, combine them into current\n                    sei_encode_eia608(sei, &cea708, eia608_from_basicna(prev_cc_data, cc_data));\n                } else if (eia608_is_westeu(cc_data)) {\n                    // extended charcters overwrite the previous charcter, so insert a dummy char thren write the extended char\n                    sei_encode_eia608(sei, &cea708, eia608_from_basicna(prev_cc_data, eia608_from_utf8_1(EIA608_CHAR_SPACE, DEFAULT_CHANNEL)));\n                    sei_encode_eia608(sei, &cea708, cc_data);\n                } else {\n                    // previous was basic na, but current isnt; write previous and current\n                    sei_encode_eia608(sei, &cea708, prev_cc_data);\n                    sei_encode_eia608(sei, &cea708, cc_data);\n                }\n\n                prev_cc_data = 0; // previous is handled, we can forget it now\n            } else if (eia608_is_westeu(cc_data)) {\n                // extended chars overwrite the previous chars, so insert a dummy char\n                // TODO create a map of alternamt chars for eia608_is_westeu instead of using space\n                sei_encode_eia608(sei, &cea708, eia608_from_utf8_1(EIA608_CHAR_SPACE, DEFAULT_CHANNEL));\n                sei_encode_eia608(sei, &cea708, cc_data);\n            } else if (eia608_is_basicna(cc_data)) {\n                prev_cc_data = cc_data;\n            } else {\n                sei_encode_eia608(sei, &cea708, cc_data);\n            }\n\n            if (eia608_is_specialna(cc_data)) {\n                // specialna are treated as control charcters. Duplicated control charcters are discarded\n                // So we write a resume after a specialna as a noop to break repetition detection\n                // TODO only do this if the same charcter is repeated\n                sei_encode_eia608(sei, &cea708, eia608_control_command(eia608_control_resume_caption_loading, DEFAULT_CHANNEL));\n            }\n        }\n\n        if (0 != prev_cc_data) {\n            sei_encode_eia608(sei, &cea708, prev_cc_data);\n        }\n    }\n\n    sei_encode_eia608(sei, &cea708, 0); // flush\n    sei->timestamp = frame->timestamp; // assumes in order frames\n    // sei_dump (sei);\n    return LIBCAPTION_OK;\n}\n\nlibcaption_stauts_t sei_from_scc(sei_t* sei, const scc_t* scc)\n{\n    unsigned int i;\n    cea708_t cea708;\n    cea708_init(&cea708, sei->timestamp); // set up a new popon frame\n\n    for (i = 0; i < scc->cc_size; ++i) {\n        if (31 == cea708.user_data.cc_count) {\n            sei_append_708(sei, &cea708);\n        }\n\n        cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, scc->cc_data[i]);\n    }\n\n    if (0 != cea708.user_data.cc_count) {\n        sei_append_708(sei, &cea708);\n    }\n\n    return LIBCAPTION_OK;\n}\n\nlibcaption_stauts_t sei_from_caption_clear(sei_t* sei)\n{\n    cea708_t cea708;\n    cea708_init(&cea708, sei->timestamp); // set up a new popon frame\n    cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_end_of_caption, DEFAULT_CHANNEL));\n    cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_end_of_caption, DEFAULT_CHANNEL));\n    cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_non_displayed_memory, DEFAULT_CHANNEL));\n    cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_non_displayed_memory, DEFAULT_CHANNEL));\n    cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_display_memory, DEFAULT_CHANNEL));\n    cea708_add_cc_data(&cea708, 1, cc_type_ntsc_cc_field_1, eia608_control_command(eia608_control_erase_display_memory, DEFAULT_CHANNEL));\n    sei_append_708(sei, &cea708);\n    return LIBCAPTION_OK;\n}\n////////////////////////////////////////////////////////////////////////////////\n// bitstream\nvoid mpeg_bitstream_init(mpeg_bitstream_t* packet)\n{\n    packet->dts = 0;\n    packet->cts = 0;\n    packet->size = 0;\n    packet->front = 0;\n    packet->latent = 0;\n    packet->status = LIBCAPTION_OK;\n}\n\nuint8_t mpeg_bitstream_packet_type(mpeg_bitstream_t* packet, unsigned stream_type)\n{\n    if (4 > packet->size) {\n        return 0;\n    }\n    switch (stream_type) {\n    case STREAM_TYPE_H262:\n        return packet->data[3];\n    case STREAM_TYPE_H264:\n        return packet->data[3] & 0x1F;\n    case STREAM_TYPE_H265:\n        return (packet->data[3] >> 1) & 0x3F;\n    default:\n        return 0;\n    }\n}\n\n// TODO optomize\n// static size_t find_start_code_increnental(const uint8_t* data, size_t size, size_t prev_size)\n// {\n//     // prev_size MUST be at least 4\n//     assert(3 < prev_size);\n//     uint32_t start_code = 0xffffffff;\n//     for (size_t i = prev_size - 3; i < size; ++i) {\n//         start_code = (start_code << 8) | data[i];\n//         if (0x00000100 == (start_code & 0xffffff00)) {\n//             return i - 3;\n//         }\n//     }\n//     return 0;\n// }\n\nstatic size_t find_start_code(const uint8_t* data, size_t size)\n{\n    uint32_t start_code = 0xffffffff;\n    for (size_t i = 1; i < size; ++i) {\n        start_code = (start_code << 8) | data[i];\n        if (0x00000100 == (start_code & 0xffffff00)) {\n            return i - 3;\n        }\n    }\n    return 0;\n}\n\n// WILL wrap around if larger than MAX_REFRENCE_FRAMES for memory saftey\ncea708_t* _mpeg_bitstream_cea708_at(mpeg_bitstream_t* packet, size_t pos) { return &packet->cea708[(packet->front + pos) % MAX_REFRENCE_FRAMES]; }\ncea708_t* _mpeg_bitstream_cea708_front(mpeg_bitstream_t* packet) { return _mpeg_bitstream_cea708_at(packet, 0); }\ncea708_t* _mpeg_bitstream_cea708_back(mpeg_bitstream_t* packet) { return _mpeg_bitstream_cea708_at(packet, packet->latent - 1); }\ncea708_t* _mpeg_bitstream_cea708_emplace_back(mpeg_bitstream_t* packet, double timestamp)\n{\n    ++packet->latent;\n    cea708_t* cea708 = _mpeg_bitstream_cea708_back(packet);\n    cea708_init(cea708, timestamp);\n    return cea708;\n}\n\nvoid _mpeg_bitstream_cea708_sort(mpeg_bitstream_t* packet)\n{\n    // TODO better sort? (for small nearly sorted lists bubble is difficult to beat)\n    // This must be stable, decending sort\nagain:\n    for (size_t i = 1; i < packet->latent; ++i) {\n        cea708_t c;\n        cea708_t* a = _mpeg_bitstream_cea708_at(packet, i - 1);\n        cea708_t* b = _mpeg_bitstream_cea708_at(packet, i);\n        if (a->timestamp > b->timestamp) {\n            memcpy(&c, a, sizeof(cea708_t));\n            memcpy(a, b, sizeof(cea708_t));\n            memcpy(b, &c, sizeof(cea708_t));\n            goto again;\n        }\n    }\n}\n\n// Removes items from front\nsize_t mpeg_bitstream_flush(mpeg_bitstream_t* packet, caption_frame_t* frame)\n{\n    if (packet->latent) {\n        cea708_t* cea708 = _mpeg_bitstream_cea708_front(packet);\n        packet->status = libcaption_status_update(LIBCAPTION_OK, cea708_to_caption_frame(frame, cea708));\n        packet->front = (packet->front + 1) % MAX_REFRENCE_FRAMES;\n        --packet->latent;\n    }\n\n    return packet->latent;\n}\n\nvoid _mpeg_bitstream_cea708_sort_flush(mpeg_bitstream_t* packet, caption_frame_t* frame, double dts)\n{\n    _mpeg_bitstream_cea708_sort(packet);\n    // Loop will terminate on LIBCAPTION_READY\n    while (packet->latent && packet->status == LIBCAPTION_OK && _mpeg_bitstream_cea708_front(packet)->timestamp < dts) {\n        mpeg_bitstream_flush(packet, frame);\n    }\n}\n\nsize_t mpeg_bitstream_parse(mpeg_bitstream_t* packet, caption_frame_t* frame, const uint8_t* data, size_t size, unsigned stream_type, double dts, double cts)\n{\n    if (MAX_NALU_SIZE <= packet->size) {\n        packet->status = LIBCAPTION_ERROR;\n        // fprintf(stderr, \"LIBCAPTION_ERROR\\n\");\n        return 0;\n    }\n\n    // consume upto MAX_NALU_SIZE bytes\n    if (MAX_NALU_SIZE <= packet->size + size) {\n        size = MAX_NALU_SIZE - packet->size;\n    }\n\n    sei_t sei;\n    size_t header_size, scpos;\n    packet->status = LIBCAPTION_OK;\n    memcpy(&packet->data[packet->size], data, size);\n    packet->size += size;\n\n    while (packet->status == LIBCAPTION_OK && 0 < (scpos = find_start_code(&packet->data[0], packet->size))) {\n        switch (mpeg_bitstream_packet_type(packet, stream_type)) {\n        default:\n            break;\n        case H262_SEI_PACKET:\n            header_size = 4;\n            if (STREAM_TYPE_H262 == stream_type && scpos > header_size) {\n                cea708_t* cea708 = _mpeg_bitstream_cea708_emplace_back(packet, dts + cts);\n                packet->status = libcaption_status_update(packet->status, cea708_parse_h262(&packet->data[header_size], scpos - header_size, cea708));\n                _mpeg_bitstream_cea708_sort_flush(packet, frame, dts);\n            }\n            break;\n        case H264_SEI_PACKET:\n        case H265_SEI_PACKET:\n            header_size = STREAM_TYPE_H264 == stream_type ? 4 : STREAM_TYPE_H265 == stream_type ? 5 : 0;\n            if (header_size && scpos > header_size) {\n                packet->status = libcaption_status_update(packet->status, sei_parse(&sei, &packet->data[header_size], scpos - header_size, dts + cts));\n                for (sei_message_t* msg = sei_message_head(&sei); msg; msg = sei_message_next(msg)) {\n                    if (sei_type_user_data_registered_itu_t_t35 == sei_message_type(msg)) {\n                        cea708_t* cea708 = _mpeg_bitstream_cea708_emplace_back(packet, dts + cts);\n                        packet->status = libcaption_status_update(packet->status, cea708_parse_h264(sei_message_data(msg), sei_message_size(msg), cea708));\n                        _mpeg_bitstream_cea708_sort_flush(packet, frame, dts);\n                    }\n                }\n            }\n            break;\n        }\n\n        packet->size -= scpos;\n        memmove(&packet->data[0], &packet->data[scpos], packet->size);\n    }\n\n    return size;\n}\n////////////////////////////////////////////////////////////////////////////////\n// // h262\n// libcaption_stauts_t h262_user_data_to_caption_frame(caption_frame_t* frame, mpeg_bitstream_t* packet, double dts, double cts)\n// {\n//     cea708_t cea708;\n//     libcaption_stauts_t status = LIBCAPTION_OK;\n\n//     cea708_init(&cea708);\n//     size_t size = mpeg_bitstream_size(packet, STREAM_TYPE_H262);\n//     const uint8_t* data = mpeg_bitstream_data(packet, STREAM_TYPE_H262);\n//     status = cea708_parse_h262(data, size, &cea708);\n//     // cea708_dump(&cea708);\n//     status = libcaption_status_update(status, cea708_to_caption_frame(frame, &cea708, dts + cts));\n\n//     if (LIBCAPTION_READY == status) {\n//         frame->timestamp = dts + cts;\n//     }\n\n//     return status;\n// }\n"
  },
  {
    "path": "deps/libcaption/src/scc.c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#include \"scc.h\"\n#include \"utf8.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nstatic scc_t* scc_relloc(scc_t* scc, unsigned int cc_count)\n{\n    if (0 == scc || scc->cc_aloc < cc_count) {\n        // alloc 1.5 time what is asked for.\n        scc = (scc_t*)realloc(scc, sizeof(scc_t) + ((cc_count * 15 / 10) * sizeof(uint16_t)));\n        scc->cc_aloc = cc_count;\n    }\n\n    return scc;\n}\n\nscc_t* scc_new(int cc_count)\n{\n    scc_t* scc = scc_relloc(0, cc_count);\n    scc->timestamp = 0.0;\n    scc->cc_size = 0;\n    return scc;\n}\n\nscc_t* scc_free(scc_t* scc)\n{\n    free(scc);\n    return NULL;\n}\n\ndouble scc_time_to_timestamp(int hh, int mm, int ss, int ff)\n{\n    return (hh * 3600.0) + (mm * 60.0) + ss + (ff / 29.97);\n}\n\n// 00:00:25:16  9420 9440 aeae ae79 ef75 2068 6176 e520 79ef 75f2 20f2 ef62 eff4 e9e3 732c 2061 6e64 2049 94fe 9723 ea75 73f4 20f7 616e f420 f4ef 2062 e520 61f7 e573 ef6d e520 e96e 2073 7061 e3e5 ae80 942c 8080 8080 942f\nsize_t scc_to_608(scc_t** scc, const utf8_char_t* data)\n{\n    size_t llen, size = 0;\n    int v1 = 0, v2 = 0, hh = 0, mm = 0, ss = 0, ff = 0, cc_data = 0;\n\n    if (0 == data) {\n        return 0;\n    }\n\n    if ((*scc)) {\n        (*scc)->cc_size = 0;\n    }\n\n    // skip 'Scenarist_SCC V1.0' header\n    if (2 == sscanf(data, \"Scenarist_SCC V%1d.%1d\", &v1, &v2)) {\n        data += 18, size += 18;\n\n        if (1 != v1 || 0 != v2) {\n            return 0;\n        }\n    }\n\n    // Skip blank lines\n    for (;;) {\n        llen = utf8_line_length(data);\n\n        if (0 == llen || 0 != utf8_trimmed_length(data, llen)) {\n            break;\n        }\n\n        data += llen;\n        size += llen;\n    }\n\n    if (4 == sscanf(data, \"%2d:%2d:%2d%*1[:;]%2d\", &hh, &mm, &ss, &ff)) {\n        data += 12, size += 12;\n        // Get length of the remaining charcters\n        llen = utf8_line_length(data);\n        llen = utf8_trimmed_length(data, llen);\n        unsigned int max_cc_count = 1 + ((unsigned int)llen / 5);\n        (*scc) = scc_relloc((*scc), max_cc_count * 15 / 10);\n        (*scc)->timestamp = scc_time_to_timestamp(hh, mm, ss, ff);\n        (*scc)->cc_size = 0;\n\n        while ((*scc)->cc_size < max_cc_count && 1 == sscanf(data, \"%04x\", &cc_data)) {\n            (*scc)->cc_data[(*scc)->cc_size] = (uint16_t)cc_data;\n            (*scc)->cc_size += 1;\n            data += 5, size += 5;\n        }\n    }\n\n    return size;\n}\n"
  },
  {
    "path": "deps/libcaption/src/srt.c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#include \"srt.h\"\n#include \"utf8.h\"\n#include \"vtt.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nsrt_t* srt_new()\n{\n    return vtt_new();\n}\n\nvoid srt_free(srt_t* srt)\n{\n    vtt_free(srt);\n}\n\nvtt_t* srt_parse(const utf8_char_t* data, size_t size)\n{\n    return _vtt_parse(data, size, 1);\n}\n\nvoid srt_dump(srt_t* srt)\n{\n    int i;\n    vtt_block_t* block;\n\n    for (block = srt->cue_head, i = 1; block; block = block->next, ++i) {\n        int hh1, hh2, mm1, mm2, ss1, ss2, ms1, ms2;\n        vtt_crack_time(block->timestamp, &hh1, &mm1, &ss1, &ms1);\n        vtt_crack_time(block->timestamp + block->duration, &hh2, &mm2, &ss2, &ms2);\n\n        printf(\"%02d\\r\\n%d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d\\r\\n%s\\r\\n\", i,\n            hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, vtt_block_data(block));\n    }\n}\n"
  },
  {
    "path": "deps/libcaption/src/utf8.c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n\n#include \"utf8.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nconst utf8_char_t* utf8_char_next(const utf8_char_t* c)\n{\n    const utf8_char_t* n = c + utf8_char_length(c);\n    return n == c ? 0 : n;\n}\n\n// returnes the length of the char in bytes\nsize_t utf8_char_length(const utf8_char_t* c)\n{\n    // count null term as zero size\n    if (!c || 0x00 == c[0]) {\n        return 0;\n    }\n\n    static const size_t _utf8_char_length[] = {\n        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0\n    };\n\n    return _utf8_char_length[(c[0] >> 3) & 0x1F];\n}\n\nint utf8_char_whitespace(const utf8_char_t* c)\n{\n    // 0x7F is DEL\n    if (!c || (unsigned char)c[0] <= ' ' || c[0] == 0x7F) {\n        return 1;\n    }\n\n    // EIA608_CHAR_NO_BREAK_SPACE TODO other utf8 spaces\n    if (0xC2 == (unsigned char)c[0] && 0xA0 == (unsigned char)c[1]) {\n        return 1;\n    }\n\n    return 0;\n}\n\n// returns length of the string in bytes\n// size is number of charcter to count (0 to count until NULL term)\nsize_t utf8_string_length(const utf8_char_t* data, utf8_size_t size)\n{\n    size_t char_length, byts = 0;\n\n    if (0 == size) {\n        size = utf8_char_count(data, 0);\n    }\n\n    for (; 0 < size; --size) {\n        if (0 == (char_length = utf8_char_length(data))) {\n            break;\n        }\n\n        data += char_length;\n        byts += char_length;\n    }\n\n    return byts;\n}\n\nsize_t utf8_char_copy(utf8_char_t* dst, const utf8_char_t* src)\n{\n    size_t bytes = utf8_char_length(src);\n\n    if (bytes && dst) {\n        memcpy(dst, src, bytes);\n        dst[bytes] = '\\0';\n    }\n\n    return bytes;\n}\n\n// returnes the number of utf8 charcters in a string given the number of bytes\n// to count until the a null terminator, pass 0 for size\nutf8_size_t utf8_char_count(const char* data, size_t size)\n{\n    size_t i, bytes = 0;\n    utf8_size_t count = 0;\n\n    if (0 == size) {\n        size = strlen(data);\n    }\n\n    for (i = 0; i < size; ++count, i += bytes) {\n        if (0 == (bytes = utf8_char_length(&data[i]))) {\n            break;\n        }\n    }\n\n    return count;\n}\n\n// returnes the length of the line in bytes triming not printable charcters at the end\nsize_t utf8_trimmed_length(const utf8_char_t* data, utf8_size_t charcters)\n{\n    size_t l, t = 0, split_at = 0;\n    for (size_t c = 0; (*data) && c < charcters; ++c) {\n        l = utf8_char_length(data);\n        t += l, data += l;\n        if (!utf8_char_whitespace(data)) {\n            split_at = t;\n        }\n    }\n\n    return split_at;\n}\n\nsize_t _utf8_newline(const utf8_char_t* data)\n{\n    if ('\\r' == data[0]) {\n        return '\\n' == data[1] ? 2 : 1; // windows/unix\n    } else if ('\\n' == data[0]) {\n        return '\\r' == data[1] ? 2 : 1; // riscos/macos\n    } else {\n        return 0;\n    }\n}\n// returns the length in bytes of the line including the new line charcter(s)\n// auto detects between windows(CRLF), unix(LF), mac(CR) and riscos (LFCR) line endings\nsize_t utf8_line_length(const utf8_char_t* data)\n{\n    size_t n, len = 0;\n\n    for (len = 0; 0 != data[len]; ++len) {\n        if (0 < (n = _utf8_newline(data))) {\n            return len + n;\n        }\n    }\n\n    return len;\n}\n\n// returns number of chars to include before split\nutf8_size_t utf8_wrap_length(const utf8_char_t* data, utf8_size_t size)\n{\n    // Set split_at to size, so if a split point cna not be found, retuns the size passed in\n    size_t char_length, char_count, split_at = size;\n\n    for (char_count = 0; char_count <= size; ++char_count) {\n        if (_utf8_newline(data)) {\n            return char_count;\n        } else if (utf8_char_whitespace(data)) {\n            split_at = char_count;\n        }\n\n        char_length = utf8_char_length(data);\n        data += char_length;\n    }\n\n    return split_at;\n}\n\nint utf8_line_count(const utf8_char_t* data)\n{\n    size_t len = 0;\n    int count = 0;\n\n    do {\n        len = utf8_line_length(data);\n        data += len;\n        ++count;\n    } while (0 < len);\n\n    return count - 1;\n}\n\nutf8_char_t* utf8_load_text_file(const char* path, size_t* size)\n{\n    utf8_char_t* data = NULL;\n    FILE* file = fopen(path, \"r\");\n\n    if (file) {\n        fseek(file, 0, SEEK_END);\n        size_t file_size = ftell(file);\n        fseek(file, 0, SEEK_SET);\n\n        if (0 == (*size) || file_size <= (*size)) {\n            (*size) = 0;\n            data = (utf8_char_t*)malloc(1 + file_size);\n            memset(data, '\\0', file_size);\n\n            if (data) {\n                utf8_char_t* pos = data;\n                size_t bytes_read = 0;\n\n                while (0 < (bytes_read = fread(pos, 1, file_size - (*size), file))) {\n                    pos += bytes_read;\n                    (*size) += bytes_read;\n                }\n            }\n\n            fclose(file);\n        }\n    }\n\n    if (data) {\n        data[*size] = 0;\n    }\n    \n    return data;\n}\n\n#ifndef strnstr\nchar* strnstr(const char* string1, const char* string2, size_t len)\n{\n    size_t length2;\n\n    length2 = strlen(string2);\n    if (!length2) {\n        return (char*)string1;\n    }\n\n    while (len >= length2) {\n        len--;\n        if (!memcmp(string1, string2, length2))\n            return (char*)string1;\n        string1++;\n    }\n    return NULL;\n}\n#endif\n"
  },
  {
    "path": "deps/libcaption/src/vtt.c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n#include \"vtt.h\"\n#include \"utf8.h\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nvtt_block_t* vtt_block_free_head(vtt_block_t* head);\n\nvtt_t* vtt_new()\n{\n    vtt_t* vtt = malloc(sizeof(vtt_t));\n    memset(vtt, 0, sizeof(vtt_t));\n    return vtt;\n}\n\nvoid vtt_free(vtt_t* vtt)\n{\n    while (vtt->region_head != NULL) {\n        vtt->region_head = vtt_block_free_head(vtt->region_head);\n    }\n    while (vtt->style_head != NULL) {\n        vtt->style_head = vtt_block_free_head(vtt->style_head);\n    }\n    while (vtt->cue_head != NULL) {\n        vtt->cue_head = vtt_block_free_head(vtt->cue_head);\n    }\n    free(vtt);\n}\n\nvtt_block_t* vtt_block_new(vtt_t* vtt, const utf8_char_t* data, size_t size, enum VTT_BLOCK_TYPE type)\n{\n    vtt_block_t* block = malloc(sizeof(vtt_block_t) + size + 1);\n    block->next = NULL;\n    block->type = type;\n    block->timestamp = 0.0;\n    block->duration = 0.0;\n    block->cue_settings = NULL;\n    block->cue_id = NULL;\n    block->text_size = size;\n\n    switch (type) {\n    case VTT_REGION:\n        if (vtt->region_head == NULL) {\n            vtt->region_head = block;\n        } else {\n            vtt->region_tail->next = block;\n        }\n        vtt->region_tail = block;\n        break;\n    case VTT_STYLE:\n        if (vtt->style_head == NULL) {\n            vtt->style_head = block;\n        } else {\n            vtt->style_tail->next = block;\n        }\n        vtt->style_tail = block;\n        break;\n    case VTT_CUE:\n        if (vtt->cue_head == NULL) {\n            vtt->cue_head = block;\n        } else {\n            vtt->cue_tail->next = block;\n        }\n        vtt->cue_tail = block;\n        break;\n    case VTT_NOTE:\n        break;\n    }\n\n    utf8_char_t* dest = (utf8_char_t*)vtt_block_data(block);\n    if (data) {\n        memcpy(dest, data, size);\n    } else {\n        memset(dest, 0, size);\n    }\n\n    dest[size] = '\\0';\n    return block;\n}\n\nvtt_block_t* vtt_block_free_head(vtt_block_t* head)\n{\n    vtt_block_t* next = head->next;\n    if (head->cue_id != NULL) {\n        free(head->cue_id);\n    }\n    if (head->cue_settings != NULL) {\n        free(head->cue_settings);\n    }\n    free(head);\n    return next;\n}\n\nvoid vtt_cue_free_head(vtt_t* vtt)\n{\n    vtt->cue_head = vtt_block_free_head(vtt->cue_head);\n    if (vtt->cue_head == NULL) {\n        vtt->cue_tail = NULL;\n    }\n}\n\nvoid vtt_style_free_head(vtt_t* vtt)\n{\n    vtt->style_head = vtt_block_free_head(vtt->style_head);\n    if (vtt->style_head == NULL) {\n        vtt->style_tail = NULL;\n    }\n}\n\nvoid vtt_region_free_head(vtt_t* vtt)\n{\n    vtt->region_head = vtt_block_free_head(vtt->region_head);\n    if (vtt->region_head == NULL) {\n        vtt->region_tail = NULL;\n    }\n}\n\n#define VTTTIME2SECONDS(HH, MM, SS, MS) ((HH * 3600.0) + (MM * 60.0) + SS + (MS / 1000.0))\ndouble parse_timestamp(const utf8_char_t* line)\n{\n    int hh, mm, ss, ms;\n    if (sscanf(line, \"%d:%2d:%2d%*1[,.]%3d\", &hh, &mm, &ss, &ms) == 4) {\n        return VTTTIME2SECONDS(hh, mm, ss, ms);\n    }\n    if (sscanf(line, \"%2d:%2d%*1[,.]%3d\", &mm, &ss, &ms) == 3) {\n        return VTTTIME2SECONDS(0.0, mm, ss, ms);\n    }\n    return -1.0;\n}\n\nvoid parse_timestamps(const utf8_char_t* line, double* start_pts, double* end_pts, char** cue_settings)\n{\n    char start_str[32];\n    char end_str[32];\n    char cue_str[1024];\n\n    int matches = sscanf(line, \" %31s --> %31s%1023[^\\n\\r]\", start_str, end_str, cue_str);\n    *start_pts = -1;\n    *cue_settings = NULL;\n\n    printf(\"Matches: %d\\n\", matches);\n\n    if (matches >= 1) {\n        *start_pts = parse_timestamp(start_str);\n        printf(\"Start pts: %f\\n\", *start_pts);\n    }\n    if (matches >= 2) {\n        *end_pts = parse_timestamp(end_str);\n    }\n    if ((matches == 3) && (strlen(cue_str) > 0)) {\n        int cue_size = strlen(cue_str);\n        *cue_settings = malloc(cue_size + 1);\n        strncpy(*cue_settings, cue_str, cue_size);\n        (*cue_settings)[cue_size] = '\\0';\n    }\n}\n\nvtt_t* vtt_parse(const utf8_char_t* data, size_t size)\n{\n    return _vtt_parse(data, size, 0);\n}\n\nvtt_t* _vtt_parse(const utf8_char_t* data, size_t size, int srt_mode)\n{\n    vtt_t* vtt = NULL;\n    double str_pts = 0, end_pts = 0;\n    size_t line_length = 0, trimmed_length = 0;\n    char* cue_settings;\n    enum VTT_BLOCK_TYPE block_type;\n    size_t cue_id_length = 0;\n    const utf8_char_t* cue_id = NULL;\n\n    if (!data || !size || size < 6) {\n        return NULL;\n    }\n\n    // TODO: Support UTF-8 BOM?\n    if (!srt_mode && strncmp(data, \"WEBVTT\", 6) != 0) {\n        // WebVTT files must start with WEBVTT\n        fprintf(stderr, \"Invalid webvtt header: %.*s\\n\", 6, data);\n        return NULL;\n    }\n\n    data += 6;\n    size -= 6;\n\n    vtt = vtt_new();\n\n    for (;;) {\n        line_length = 0;\n\n        do {\n            data += line_length;\n            size -= line_length;\n            line_length = utf8_line_length(data); // Line length\n            trimmed_length = utf8_trimmed_length(data, line_length);\n            // Skip empty lines\n        } while (0 < line_length && 0 == trimmed_length);\n\n        // line length only zero at EOF\n        if (0 == line_length) {\n            break;\n        }\n\n        if (strnstr(data, \"REGION\", line_length) != NULL) {\n            block_type = VTT_REGION;\n        } else if (strnstr(data, \"STYLE\", line_length) != NULL) {\n            block_type = VTT_STYLE;\n        } else if (strnstr(data, \"NOTE\", line_length) != NULL) {\n            block_type = VTT_NOTE;\n        } else if (strnstr(data, \"-->\", line_length) != NULL) {\n            block_type = VTT_CUE;\n            parse_timestamps(data, &str_pts, &end_pts, &cue_settings);\n            if (str_pts == -1) {\n                // Failed to parse timestamps\n                fprintf(stderr, \"Bad timestamp: %.*s\\n\", (int)line_length, data);\n                return NULL;\n            }\n        } else {\n            if (cue_id != NULL) {\n                // Invalid text found\n                fprintf(stderr, \"ERR: Unrecognized block\\n\");\n                return NULL;\n            }\n\n            cue_id = data;\n            cue_id_length = line_length;\n\n            data += line_length;\n            size -= line_length;\n            continue;\n        }\n\n        data += line_length;\n        size -= line_length;\n\n        // Caption text starts here\n        const utf8_char_t* text = data;\n        size_t text_size = 0;\n\n        line_length = 0;\n\n        do {\n            text_size += line_length;\n            line_length = utf8_line_length(data);\n            trimmed_length = utf8_trimmed_length(data, line_length);\n            // printf (\"cap (%d): '%.*s'\\n\", line_length, (int) trimmed_length, data);\n            data += line_length;\n            size -= line_length;\n        } while (trimmed_length);\n\n        // should we trim here?\n\n        vtt_block_t* block = vtt_block_new(vtt, text, text_size, block_type);\n\n        if (block_type == VTT_CUE) {\n            block->timestamp = str_pts;\n            block->duration = end_pts - str_pts;\n            block->cue_settings = cue_settings;\n            if (cue_id != NULL) {\n                block->cue_id = malloc(cue_id_length + 1);\n                memcpy(block->cue_id, cue_id, cue_id_length);\n                block->cue_id[cue_id_length] = '\\0';\n            }\n        }\n\n        cue_id = NULL;\n    }\n\n    return vtt;\n}\n\nint vtt_cue_to_caption_frame(vtt_block_t* cue, caption_frame_t* frame)\n{\n    const char* data = vtt_block_data(cue);\n    return caption_frame_from_text(frame, data);\n}\n\nvtt_block_t* vtt_cue_from_caption_frame(caption_frame_t* frame, vtt_t* vtt)\n{\n    if (vtt->cue_tail && 0 >= vtt->cue_tail->duration) {\n        vtt->cue_tail->duration = frame->timestamp - vtt->cue_tail->timestamp;\n    }\n\n    // CRLF per row, plus an extra at the end\n    vtt_block_t* cue = vtt_block_new(vtt, NULL, 2 + CAPTION_FRAME_TEXT_BYTES, VTT_CUE);\n    utf8_char_t* data = vtt_block_data(cue);\n\n    caption_frame_to_text(frame, data);\n    cue->timestamp = frame->timestamp;\n    // vtt requires an extra new line\n    strcat((char*)data, \"\\r\\n\");\n    return cue;\n}\n\nstatic void _dump(vtt_t* vtt)\n{\n    vtt_block_t* block;\n    printf(\"WEBVTT\\r\\n\\r\\n\");\n\n    block = vtt->region_head;\n    while (block != NULL) {\n        printf(\"REGION\\r\\n%s\\r\\n\", vtt_block_data(block));\n        block = block->next;\n    }\n\n    block = vtt->style_head;\n    while (block != NULL) {\n        printf(\"STYLE\\r\\n%s\\r\\n\", vtt_block_data(block));\n        block = block->next;\n    }\n\n    block = vtt->cue_head;\n    while (block != NULL) {\n        int hh1, hh2, mm1, mm2, ss1, ss2, ms1, ms2;\n        vtt_crack_time(block->timestamp, &hh1, &mm1, &ss1, &ms1);\n        vtt_crack_time(block->timestamp + block->duration, &hh2, &mm2, &ss2, &ms2);\n\n        if (block->cue_id != NULL) {\n            printf(\"%s\\n\", block->cue_id);\n        }\n\n        printf(\"%02d:%02d:%02d.%03d --> %02d:%02d:%02d.%03d\",\n            hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2);\n\n        if (block->cue_settings != NULL) {\n            printf(\" %s\", block->cue_settings);\n        }\n\n        printf(\"\\r\\n%s\\r\\n\", vtt_block_data(block));\n    }\n}\n\nvoid vtt_dump(vtt_t* head) { _dump(head); }\n"
  },
  {
    "path": "deps/libcaption/src/xds.c",
    "content": "/**********************************************************************************************/\n/* The MIT License                                                                            */\n/*                                                                                            */\n/* Copyright 2016-2017 Twitch Interactive, Inc. or its affiliates. All Rights Reserved.       */\n/*                                                                                            */\n/* Permission is hereby granted, free of charge, to any person obtaining a copy               */\n/* of this software and associated documentation files (the \"Software\"), to deal              */\n/* in the Software without restriction, including without limitation the rights               */\n/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell                  */\n/* copies of the Software, and to permit persons to whom the Software is                      */\n/* furnished to do so, subject to the following conditions:                                   */\n/*                                                                                            */\n/* The above copyright notice and this permission notice shall be included in                 */\n/* all copies or substantial portions of the Software.                                        */\n/*                                                                                            */\n/* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR                 */\n/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,                   */\n/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE                */\n/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER                     */\n/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,              */\n/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN                  */\n/* THE SOFTWARE.                                                                              */\n/**********************************************************************************************/\n// http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/CC_XDS.HTML#PR\n#include \"xds.h\"\n#include \"caption.h\"\n#include <string.h>\n\nvoid xds_init(xds_t* xds)\n{\n    memset(xds, 0, sizeof(xds_t));\n}\n\nint xds_decode(xds_t* xds, uint16_t cc)\n{\n    switch (xds->state) {\n    default:\n    case 0:\n        xds_init(xds);\n        xds->class_code = (cc & 0x0F00) >> 8;\n        xds->type = (cc & 0x000F);\n        xds->state = 1;\n        return LIBCAPTION_OK;\n\n    case 1:\n        if (0x8F00 == (cc & 0xFF00)) {\n            xds->checksum = (cc & 0x007F);\n            xds->state = 0;\n            return LIBCAPTION_READY;\n        }\n\n        if (xds->size < 32) {\n            xds->content[xds->size + 0] = (cc & 0x7F00) >> 8;\n            xds->content[xds->size + 1] = (cc & 0x007F);\n            xds->size += 2;\n            return LIBCAPTION_OK;\n        }\n    }\n\n    xds->state = 0;\n    return LIBCAPTION_ERROR;\n}\n"
  },
  {
    "path": "deps/libdshowcapture/CMakeLists.txt",
    "content": "add_library(libdshowcapture INTERFACE)\nadd_library(OBS::libdshowcapture ALIAS libdshowcapture)\n\ntarget_sources(\n  libdshowcapture\n  INTERFACE src/dshowcapture.hpp\n            src/source/capture-filter.cpp\n            src/source/capture-filter.hpp\n            src/source/device-vendor.cpp\n            src/source/device.cpp\n            src/source/device.hpp\n            src/source/dshow-base.cpp\n            src/source/dshow-base.hpp\n            src/source/dshow-demux.cpp\n            src/source/dshow-demux.hpp\n            src/source/dshow-device-defs.hpp\n            src/source/dshow-encoded-device.cpp\n            src/source/dshow-enum.cpp\n            src/source/dshow-enum.hpp\n            src/source/dshow-formats.cpp\n            src/source/dshow-formats.hpp\n            src/source/dshow-media-type.cpp\n            src/source/dshow-media-type.hpp\n            src/source/dshowcapture.cpp\n            src/source/dshowencode.cpp\n            src/source/encoder.cpp\n            src/source/encoder.hpp\n            src/source/external/IVideoCaptureFilter.h\n            src/source/log.cpp\n            src/source/log.hpp\n            src/source/output-filter.cpp\n            src/source/output-filter.hpp\n            src/external/capture-device-support/Library/EGAVResult.cpp\n            src/external/capture-device-support/Library/ElgatoUVCDevice.cpp\n            src/external/capture-device-support/Library/win/EGAVHIDImplementation.cpp\n            src/external/capture-device-support/SampleCode/DriverInterface.cpp)\n\ntarget_include_directories(\n  libdshowcapture INTERFACE \"${CMAKE_CURRENT_SOURCE_DIR}/src\"\n                            \"${CMAKE_CURRENT_SOURCE_DIR}/src/external/capture-device-support/Library\")\n\ntarget_compile_definitions(libdshowcapture INTERFACE _UP_WINDOWS=1)\ntarget_compile_options(libdshowcapture INTERFACE /wd4005 /wd4018)\n"
  },
  {
    "path": "deps/w32-pthreads/.clang-format",
    "content": "Language: Cpp\nSortIncludes: false\nDisableFormat: true\n"
  },
  {
    "path": "deps/w32-pthreads/ANNOUNCE",
    "content": "PTHREADS-WIN32 RELEASE 2.9.0 (2012-05-25)\n-----------------------------------------\nWeb Site: http://sourceware.org/pthreads-win32/\nFTP Site: ftp://sourceware.org/pub/pthreads-win32\nMaintainer: Ross Johnson <ross.johnson@loungebythelake.net>\n\n\nWe are pleased to announce the availability of a new release of\nPthreads-win32, an Open Source Software implementation of the\nThreads component of the POSIX 1003.1 2001 Standard for Microsoft's\nWin32 environment. Some functions from other sections of POSIX\n1003.1 2001 are also supported including semaphores and scheduling\nfunctions.\n\nSome common non-portable functions are also implemented for\nadditional compatibility, as are a few functions specific\nto pthreads-win32 for easier integration with Win32 applications.\n\nPthreads-win32 is free software, distributed under the GNU Lesser\nGeneral Public License (LGPL).\n\n\nAcknowledgements\n----------------\nThis library is based originally on a Win32 pthreads\nimplementation contributed by John Bossom.\n\nThe implementation of Condition Variables uses algorithms developed\nby Alexander Terekhov and Louis Thomas.\n\nThe implementation of POSIX mutexes has been improved by Thomas Pfaff\nand later by Alexander Terekhov.\n\nThe implementation of Spinlocks and Barriers was contributed\nby Ross Johnson.\n\nThe implementation of read/write locks was contributed by\nAurelio Medina and improved by Alexander Terekhov.\n\nMany others have contributed significant time and effort to solve crutial\nproblems in order to make the library workable, robust and reliable.\n\nThanks to Xavier Leroy for granting permission to use and modify his\nLinuxThreads manual pages.\n\nThanks to The Open Group for making the Single Unix Specification\npublicly available - many of the manual pages included in the package\nwere extracted from it.\n\nThere is also a separate CONTRIBUTORS file. This file and others are\non the web site:\n\n\thttp://sourceware.org/pthreads-win32\n\nAs much as possible, the ChangeLog file acknowledges contributions to the\ncode base in more detail.\n\n\nChanges since the last release\n------------------------------\nThese are now documented in the NEWS file.\nSee the ChangeLog file also.\n\n\nKnown Bugs\n----------\nThese are now documented in the BUGS file.\n\n\nLevel of standards conformance\n------------------------------\n\nThe following POSIX 1003.1 2001 options are defined and set to 200112L:\n\n      _POSIX_THREADS\n      _POSIX_THREAD_SAFE_FUNCTIONS\n      _POSIX_THREAD_ATTR_STACKSIZE\n      _POSIX_THREAD_PRIORITY_SCHEDULING\n      _POSIX_SEMAPHORES\n      _POSIX_READER_WRITER_LOCKS\n      _POSIX_SPIN_LOCKS\n      _POSIX_BARRIERS\n\n\nThe following POSIX 1003.1 2001 options are defined and set to -1:\n\n      _POSIX_THREAD_ATTR_STACKADDR\n      _POSIX_THREAD_PRIO_INHERIT\n      _POSIX_THREAD_PRIO_PROTECT\n      _POSIX_THREAD_PROCESS_SHARED\n\n\nThe following POSIX 1003.1 2001 limits are defined and set:\n\n      _POSIX_THREAD_THREADS_MAX\n      _POSIX_SEM_VALUE_MAX\n      _POSIX_SEM_NSEMS_MAX\n      _POSIX_THREAD_KEYS_MAX\n      _POSIX_THREAD_DESTRUCTOR_ITERATIONS\n      PTHREAD_STACK_MIN\n      PTHREAD_THREADS_MAX\n      SEM_VALUE_MAX\n      SEM_NSEMS_MAX\n      PTHREAD_KEYS_MAX\n      PTHREAD_DESTRUCTOR_ITERATIONS\n\n\nThe following functions are implemented:\n\n      ---------------------------\n      PThreads\n      ---------------------------\n      pthread_attr_init\n      pthread_attr_destroy\n      pthread_attr_getdetachstate\n      pthread_attr_getstackaddr\n      pthread_attr_getstacksize\n      pthread_attr_setdetachstate\n      pthread_attr_setstackaddr\n      pthread_attr_setstacksize\n\n      pthread_create\n      pthread_detach\n      pthread_equal\n      pthread_exit\n      pthread_join\n      pthread_once\n      pthread_self\n\n      pthread_cancel\n      pthread_cleanup_pop\n      pthread_cleanup_push\n      pthread_setcancelstate\n      pthread_setcanceltype\n      pthread_testcancel\n\n      ---------------------------\n      Thread Specific Data\n      ---------------------------\n      pthread_key_create\n      pthread_key_delete\n      pthread_setspecific\n      pthread_getspecific\n\n      ---------------------------\n      Mutexes\n      ---------------------------\n      pthread_mutexattr_init\n      pthread_mutexattr_destroy\n      pthread_mutexattr_getpshared\n      pthread_mutexattr_setpshared\n      pthread_mutexattr_gettype\n      pthread_mutexattr_settype (types: PTHREAD_MUTEX_DEFAULT\n\t\t\t\t\tPTHREAD_MUTEX_NORMAL\n\t\t\t\t\tPTHREAD_MUTEX_ERRORCHECK\n\t\t\t\t\tPTHREAD_MUTEX_RECURSIVE  )\n      pthread_mutexattr_getrobust\n      pthread_mutexattr_setrobust (values: PTHREAD_MUTEX_STALLED\n                                           PTHREAD_MUTEX_ROBUST)\n      pthread_mutex_init\n      pthread_mutex_destroy\n      pthread_mutex_lock\n      pthread_mutex_trylock\n      pthread_mutex_timedlock\n      pthread_mutex_unlock\n      pthread_mutex_consistent\n\n      ---------------------------\n      Condition Variables\n      ---------------------------\n      pthread_condattr_init\n      pthread_condattr_destroy\n      pthread_condattr_getpshared\n      pthread_condattr_setpshared\n\n      pthread_cond_init\n      pthread_cond_destroy\n      pthread_cond_wait\n      pthread_cond_timedwait\n      pthread_cond_signal\n      pthread_cond_broadcast\n\n      ---------------------------\n      Read/Write Locks\n      ---------------------------\n      pthread_rwlock_init\n      pthread_rwlock_destroy\n      pthread_rwlock_tryrdlock\n      pthread_rwlock_trywrlock\n      pthread_rwlock_rdlock\n      pthread_rwlock_timedrdlock\n      pthread_rwlock_rwlock\n      pthread_rwlock_timedwrlock\n      pthread_rwlock_unlock\n      pthread_rwlockattr_init\n      pthread_rwlockattr_destroy\n      pthread_rwlockattr_getpshared\n      pthread_rwlockattr_setpshared\n\n      ---------------------------\n      Spin Locks\n      ---------------------------\n      pthread_spin_init\n      pthread_spin_destroy\n      pthread_spin_lock\n      pthread_spin_unlock\n      pthread_spin_trylock\n\n      ---------------------------\n      Barriers\n      ---------------------------\n      pthread_barrier_init\n      pthread_barrier_destroy\n      pthread_barrier_wait\n      pthread_barrierattr_init\n      pthread_barrierattr_destroy\n      pthread_barrierattr_getpshared\n      pthread_barrierattr_setpshared\n\n      ---------------------------\n      Semaphores\n      ---------------------------\n      sem_init\n      sem_destroy\n      sem_post\n      sem_wait\n      sem_trywait\n      sem_timedwait\n      sem_getvalue\t     (# free if +ve, # of waiters if -ve)\n      sem_open\t\t     (returns an error ENOSYS)\n      sem_close \t     (returns an error ENOSYS)\n      sem_unlink\t     (returns an error ENOSYS)\n\n      ---------------------------\n      RealTime Scheduling\n      ---------------------------\n      pthread_attr_getschedparam\n      pthread_attr_setschedparam\n      pthread_attr_getinheritsched\n      pthread_attr_setinheritsched\n      pthread_attr_getschedpolicy (only supports SCHED_OTHER)\n      pthread_attr_setschedpolicy (only supports SCHED_OTHER)\n      pthread_getschedparam\n      pthread_setschedparam\n      pthread_getconcurrency\n      pthread_setconcurrency\n      pthread_attr_getscope\n      pthread_attr_setscope  (only supports PTHREAD_SCOPE_SYSTEM)\n      sched_get_priority_max\n      sched_get_priority_min\n      sched_rr_get_interval  (returns an error ENOTSUP)\n      sched_setscheduler     (only supports SCHED_OTHER)\n      sched_getscheduler     (only supports SCHED_OTHER)\n      sched_yield\n\n      ---------------------------\n      Signals\n      ---------------------------\n      pthread_sigmask\n      pthread_kill           (only supports zero sig value,\n                              for thread validity checking)\n\n      ---------------------------\n      Non-portable routines (see the README.NONPORTABLE file for usage)\n      ---------------------------\n      pthread_getw32threadhandle_np\n      pthread_timechange_handler_np\n      pthread_delay_np\n      pthread_getunique_np\n      pthread_mutexattr_getkind_np\n      pthread_mutexattr_setkind_np\t(types: PTHREAD_MUTEX_FAST_NP,\n\t\t\t\t\t\tPTHREAD_MUTEX_ERRORCHECK_NP,\n\t\t\t\t\t\tPTHREAD_MUTEX_RECURSIVE_NP,\n\t\t\t\t\t\tPTHREAD_MUTEX_ADAPTIVE_NP,\n\t\t\t\t\t\tPTHREAD_MUTEX_TIMED_NP)\n      pthread_num_processors_np\n      (The following four routines may be required when linking statically.\n       The process_* routines should not be needed for MSVC or GCC.)\n      pthread_win32_process_attach_np\n      pthread_win32_process_detach_np\n      (The following routines should only be needed to manage implicit\n       POSIX handles i.e. when Win native threads call POSIX thread routines\n       (other than pthread_create))\n      pthread_win32_thread_attach_np\n      pthread_win32_thread_detach_np\n\n      ---------------------------\n      Static Initializers\n      ---------------------------\n      PTHREAD_ONCE_INIT\n      PTHREAD_MUTEX_INITIALIZER\n      PTHREAD_RECURSIVE_MUTEX_INITIALIZER\n      PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP\n      PTHREAD_ERRORCHECK_MUTEX_INITIALIZER\n      PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP\n      PTHREAD_COND_INITIALIZER\n      PTHREAD_RWLOCK_INITIALIZER\n      PTHREAD_SPINLOCK_INITIALIZER\n\n\nThe library includes two non-API functions for creating cancellation\npoints in applications and libraries:\n      \n      pthreadCancelableWait\n      pthreadCancelableTimedWait\n\n      \nThe following functions are not implemented:\n\n      ---------------------------\n      RealTime Scheduling\n      ---------------------------\n      pthread_mutex_getprioceiling\n      pthread_mutex_setprioceiling\n      pthread_mutex_attr_getprioceiling\n      pthread_mutex_attr_getprotocol\n      pthread_mutex_attr_setprioceiling\n      pthread_mutex_attr_setprotocol\n\n      ---------------------------\n      Fork Handlers\n      ---------------------------\n      pthread_atfork\n\n      ---------------------------\n      Stdio\n      --------------------------- \n      flockfile\n      ftrylockfile\n      funlockfile\n      getc_unlocked\n      getchar_unlocked\t\n      putc_unlocked\n      putchar_unlocked\n\n      ---------------------------\n      Thread-Safe C Runtime Library\n      ---------------------------\n      readdir_r\n      getgrgid_r\n      getgrnam_r\n      getpwuid_r\n      getpwnam_r\n      \n      ---------------------------\n      Signals\n      ---------------------------\n      sigtimedwait\n      sigwait\n      sigwaitinfo\n\n      ---------------------------\n      General\n      ---------------------------\n      sysconf      \n\n      ---------------------------\n      Thread-Safe C Runtime Library (macros)\n      ---------------------------\n      strtok_r\n      asctime_r\n      ctime_r\n      gmtime_r\n      localtime_r\n      rand_r\n\n\nAvailability\n------------ \n\nThe prebuilt DLL, export libs (for both MSVC and Mingw32), and the header\nfiles (pthread.h, semaphore.h, sched.h) are available along with the\ncomplete source code.\n\nThe source code can be found at:\n\n\tftp://sources.redhat.com/pub/pthreads-win32\n\nand as individual source code files at\n\n\tftp://sources.redhat.com/pub/pthreads-win32/source\n\nThe pre-built DLL, export libraries and include files can be found at:\n\n\tftp://sources.redhat.com/pub/pthreads-win32/dll-latest\n\n\n      \nMailing List \n------------  \n      \nThere is a mailing list for discussing pthreads on Win32. To join,\nsend email to:\n\n\tpthreads-win32-subscribe@sourceware.cygnus.com\n      \n\nApplication Development Environments\n------------------------------------\n\nSee the README file for more information.\n      \nMSVC:\nMSVC using SEH works. Distribute pthreadVSE.dll with your application.\nMSVC using C++ EH works. Distribute pthreadVCE.dll with your application.\nMSVC using C setjmp/longjmp works. Distribute pthreadVC.dll with your application.\n\n\nMingw32:\nSee the FAQ, Questions 6 and 10.\n\nMingw using C++ EH works. Distribute pthreadGCE.dll with your application.\nMingw using C setjmp/longjmp works. Distribute pthreadGC.dll with your application.\n\n\nCygwin: (http://sourceware.cygnus.com/cygwin/)\nDevelopers using Cygwin do not need pthreads-win32 since it has POSIX threads\nsupport. Refer to its documentation for details and extent.\n\n\nUWIN:\nUWIN is a complete Unix-like environment for Windows from AT&T. Pthreads-win32\ndoesn't currently support UWIN (and vice versa), but that may change in the\nfuture.\n\nGenerally:\nFor convenience, the following pre-built files are available on the FTP site\n(see Availability above):\n\n\tpthread.h\t- for POSIX threads\n\tsemaphore.h\t- for POSIX semaphores\n\tsched.h \t- for POSIX scheduling\n\tpthreadVCE.dll\t- built with MSVC++ compiler using C++ EH\n\tpthreadVCE.lib\n\tpthreadVC.dll\t- built with MSVC compiler using C setjmp/longjmp\n\tpthreadVC.lib\n\tpthreadVSE.dll\t- built with MSVC compiler using SEH\n\tpthreadVSE.lib\n\tpthreadGCE.dll\t- built with Mingw32 G++ 2.95.2-1\n\tpthreadGC.dll\t- built with Mingw32 GCC 2.95.2-1 using setjmp/longjmp\n\tlibpthreadGCE.a - derived from pthreadGCE.dll\n\tlibpthreadGC.a\t- derived from pthreadGC.dll\n\tgcc.dll \t- needed if distributing applications that use\n\t\t\t  pthreadGCE.dll (but see the FAQ Q 10 for the latest\n\t\t\t  related information)\n\nThese are the only files you need in order to build POSIX threads\napplications for Win32 using either MSVC or Mingw32.\n\nSee the FAQ file in the source tree for additional information.\n\n\nDocumentation\n-------------\n\nFor the authoritative reference, see the online POSIX\nstandard reference at:\n\n       http://www.OpenGroup.org\n\nFor POSIX Thread API programming, several reference books are\navailable:\n\n       Programming with POSIX Threads\n       David R. Butenhof\n       Addison-Wesley (pub)\n\n       Pthreads Programming\n       By Bradford Nichols, Dick Buttlar & Jacqueline Proulx Farrell\n       O'Reilly (pub)\n\nOn the web: see the links at the bottom of the pthreads-win32 site:\n\n       http://sources.redhat.com/pthreads-win32/\n\n       Currently, there is no documentation included in the package apart\n       from the copious comments in the source code.\n\n\n\nEnjoy!\n\nRoss Johnson\n"
  },
  {
    "path": "deps/w32-pthreads/BUGS",
    "content": "----------\nKnown bugs\n----------\n\n1. Not strictly a bug, more of a gotcha.\n\n   Under MS VC++ (only tested with version 6.0), a term_func\n   set via the standard C++ set_terminate() function causes the\n   application to abort.\n\n   Notes from the MSVC++ manual:\n         1) A term_func() should call exit(), otherwise\n            abort() will be called on return to the caller.\n            A call to abort() raises SIGABRT and the default signal handler\n            for all signals terminates the calling program with\n            exit code 3.\n         2) A term_func() must not throw an exception. Therefore\n            term_func() should not call pthread_exit(), which\n            works by throwing an exception (pthreadVCE or pthreadVSE)\n            or by calling longjmp (pthreadVC).\n\n   Workaround: avoid using pthread_exit() in C++ applications. Exit\n   threads by dropping through the end of the thread routine.\n\n2. Cancellation problems in C++ builds\n   - Milan Gardian\n\n   [Note: It's not clear if this problem isn't simply due to the context\n   switch in pthread_cancel() which occurs unless the QueueUserAPCEx\n   library and driver are installed and used. Just like setjmp/longjmp,\n   this is probably not going to work well in C++. In any case, unless for\n   some very unusual reason you really must use the C++ build then please\n   use the C build pthreadVC2.dll or pthreadGC2.dll, i.e. for C++\n   applications.]\n\n   This is suspected to be a compiler bug in VC6.0, and also seen in\n   VC7.0 and VS .NET 2003. The GNU C++ compiler does not have a problem\n   with this, and it has been reported that the Intel C++ 8.1 compiler\n   and Visual C++ 2005 Express Edition Beta2 pass tests\\semaphore4.c\n   (which exposes the bug).\n\n   Workaround [rpj - 2 Feb 2002]\n   -----------------------------\n   [Please note: this workaround did not solve a similar problem in\n   snapshot-2004-11-03 or later, even though similar symptoms were seen.\n   tests\\semaphore4.c fails in that snapshot for the VCE version of the\n   DLL.]\n\n   The problem disappears when /Ob0 is used, i.e. /O2 /Ob0 works OK,\n   but if you want to use inlining optimisation you can be much more\n   specific about where it's switched off and on by using a pragma.\n\n   So the inlining optimisation is interfering with the way that cleanup\n   handlers are run. It appears to relate to auto-inlining of class methods\n   since this is the only auto inlining that is performed at /O1 optimisation\n   (functions with the \"inline\" qualifier are also inlined, but the problem\n   doesn't appear to involve any such functions in the library or testsuite).\n\n   In order to confirm the inlining culprit, the following use of pragmas\n   eliminate the problem but I don't know how to make it transparent, putting\n   it in, say, pthread.h where pthread_cleanup_push defined as a macro.\n\n   #pragma inline_depth(0)\n     pthread_cleanup_push(handlerFunc, (void *) &arg);\n\n        /* ... */\n\n     pthread_cleanup_pop(0);\n   #pragma inline_depth()\n\n   Note the empty () pragma value after the pop macro. This resets depth to the\n   default. Or you can specify a non-zero depth here.\n\n   The pragma is also needed (and now used) within the library itself wherever\n   cleanup handlers are used (condvar.c and rwlock.c).\n\n   Use of these pragmas allows compiler optimisations /O1 and /O2 to be\n   used for either or both the library and applications.\n\n   Experimenting further, I found that wrapping the actual cleanup handler\n   function with #pragma auto_inline(off|on) does NOT work.\n\n   MSVC6.0 doesn't appear to support the C99 standard's _Pragma directive,\n   however, later versions may. This form is embeddable inside #define\n   macros, which would be ideal because it would mean that it could be added\n   to the push/pop macro definitions in pthread.h and hidden from the\n   application programmer.\n\n   [/rpj]\n\n   Original problem description\n   ----------------------------\n\n   The cancellation (actually, cleanup-after-cancel) tests fail when using VC\n   (professional) optimisation switches (/O1 or /O2) in pthreads library. I\n   have not investigated which concrete optimisation technique causes this\n   problem (/Og, /Oi, /Ot, /Oy, /Ob1, /Gs, /Gf, /Gy, etc.), but here is a\n   summary of builds and corresponding failures:\n\n     * pthreads VSE (optimised tests): OK\n     * pthreads VCE (optimised tests): Failed \"cleanup1\" test (runtime)\n\n     * pthreads VSE (DLL in CRT, optimised tests): OK\n     * pthreads VCE (DLL in CRT, optimised tests): Failed \"cleanup1\" test\n   (runtime)\n\n   Please note that while in VSE version of the pthreads library the\n   optimisation does not really have any impact on the tests (they pass OK), in\n   VCE version addition of optimisation (/O2 in this case) causes the tests to\n   fail uniformly - either in \"cleanup0\" or \"cleanup1\" test cases.\n\n   Please note that all the tests above use default pthreads DLL (no\n   optimisations, linked with either static or DLL CRT, based on test type).\n   Therefore the problem lies not within the pthreads DLL but within the\n   compiled client code (the application using pthreads -> involvement of\n   \"pthread.h\").\n\n   I think the message of this section is that usage of VCE version of pthreads\n   in applications relying on cancellation/cleanup AND using optimisations for\n   creation of production code is highly unreliable for the current version of\n   the pthreads library.\n\n3. The Borland Builder 5.5 version of the library produces memory read exceptions\nin some tests.\n\n4. pthread_barrier_wait() can deadlock if the number of potential calling\nthreads for a particular barrier is greater than the barrier count parameter\ngiven to pthread_barrier_init() for that barrier.\n\nThis is due to the very lightweight implementation of pthread-win32 barriers.\nTo cope with more than \"count\" possible waiters, barriers must effectively\nimplement all the same safeguards as condition variables, making them much\n\"heavier\" than at present.\n\nThe workaround is to ensure that no more than \"count\" threads attempt to wait\nat the barrier.\n\n5. Canceling a thread blocked on pthread_once appears not to work in the MSVC++\nversion of the library \"pthreadVCE.dll\". The test case \"once3.c\" hangs. I have no\nclues on this at present. All other versions pass this test ok - pthreadsVC.dll,\npthreadsVSE.dll, pthreadsGC.dll and pthreadsGCE.dll. \n"
  },
  {
    "path": "deps/w32-pthreads/Bmakefile",
    "content": "# This makefile is compatible with BCB make.  Use \"make -fBMakefile\" to compile.\n# \n# The variables $DLLDEST and $LIBDEST hold the destination directories for the\n# dll and the lib, respectively. Probably all that needs to change is $DEVROOT.\n#\n# Currently only the recommended pthreadBC.dll is built by this makefile.\n#\n\n\nDLL_VER\t= 2\n\nDEVROOT\t= .\n\nDLLDEST\t= $(DEVROOT)\\DLL\nLIBDEST\t= $(DEVROOT)\\DLL\n\nDLLS\t= pthreadBC$(DLL_VER).dll\n\nOPTIM\t= /O2\n\nRC\t= brcc32\nRCFLAGS\t= -i.\n\nCFLAGS\t= /q /I. /D_WIN32_WINNT=0x400 /DHAVE_PTW32_CONFIG_H=1 /4 /tWD /tWM \\\n\t  /w-aus /w-asc /w-par\n\n#C cleanup code\nBCFLAGS\t= $(PTW32_FLAGS) $(CFLAGS)\n\n# Agregate modules for inlinability\nDLL_OBJS\t= \\\n\t\tattr.obj \\\n\t\tbarrier.obj \\\n\t\tcancel.obj \\\n\t\tcleanup.obj \\\n\t\tcondvar.obj \\\n\t\tcreate.obj \\\n\t\tdll.obj \\\n\t\terrno.obj \\\n\t\texit.obj \\\n\t\tfork.obj \\\n\t\tglobal.obj \\\n\t\tmisc.obj \\\n\t\tmutex.obj \\\n\t\tnonportable.obj \\\n\t\tprivate.obj \\\n\t\trwlock.obj \\\n\t\tsched.obj \\\n\t\tsemaphore.obj \\\n\t\tsignal.obj \\\n\t\tspin.obj \\\n\t\tsync.obj \\\n\t\ttsd.obj\n\nINCL\t= config.h implement.h semaphore.h pthread.h need_errno.h\n\nATTR_SRCS\t= \\\n\t\tpthread_attr_init.c \\\n\t\tpthread_attr_destroy.c \\\n\t\tpthread_attr_getdetachstate.c \\\n\t\tpthread_attr_setdetachstate.c \\\n\t\tpthread_attr_getstackaddr.c \\\n\t\tpthread_attr_setstackaddr.c \\\n\t\tpthread_attr_getstacksize.c \\\n\t\tpthread_attr_setstacksize.c \\\n\t\tpthread_attr_getscope.c \\\n\t\tpthread_attr_setscope.c\n\nBARRIER_SRCS = \\\n\t\tpthread_barrier_init.c \\\n\t\tpthread_barrier_destroy.c \\\n\t\tpthread_barrier_wait.c \\\n\t\tpthread_barrierattr_init.c \\\n\t\tpthread_barrierattr_destroy.c \\\n\t\tpthread_barrierattr_setpshared.c \\\n\t\tpthread_barrierattr_getpshared.c\n\nCANCEL_SRCS\t= \\\n\t\tpthread_setcancelstate.c \\\n\t\tpthread_setcanceltype.c \\\n\t\tpthread_testcancel.c \\\n\t\tpthread_cancel.c \n\nCONDVAR_SRCS\t= \\\n\t\tptw32_cond_check_need_init.c \\\n\t\tpthread_condattr_destroy.c \\\n\t\tpthread_condattr_getpshared.c \\\n\t\tpthread_condattr_init.c \\\n\t\tpthread_condattr_setpshared.c \\\n\t\tpthread_cond_destroy.c \\\n\t\tpthread_cond_init.c \\\n\t\tpthread_cond_signal.c \\\n\t\tpthread_cond_wait.c\n\nEXIT_SRCS\t= \\\n\t\tpthread_exit.c\n\nMISC_SRCS\t= \\\n\t\tpthread_equal.c \\\n\t\tpthread_getconcurrency.c \\\n\t\tpthread_once.c \\\n\t\tpthread_self.c \\\n\t\tpthread_setconcurrency.c \\\n\t\tptw32_calloc.c \\\n\t\tptw32_MCS_lock.c \\\n\t\tptw32_new.c \\\n\t\tw32_CancelableWait.c\n\nMUTEX_SRCS\t= \\\n\t\tptw32_mutex_check_need_init.c \\\n\t\tpthread_mutex_init.c \\\n\t\tpthread_mutex_destroy.c \\\n\t\tpthread_mutexattr_init.c \\\n\t\tpthread_mutexattr_destroy.c \\\n\t\tpthread_mutexattr_getpshared.c \\\n\t\tpthread_mutexattr_setpshared.c \\\n\t\tpthread_mutexattr_settype.c \\\n\t\tpthread_mutexattr_gettype.c \\\n\t\tpthread_mutexattr_setrobust.c \\\n\t\tpthread_mutexattr_getrobust.c \\\n\t\tpthread_mutex_lock.c \\\n\t\tpthread_mutex_timedlock.c \\\n\t\tpthread_mutex_unlock.c \\\n\t\tpthread_mutex_trylock.c \\\n\t\tpthread_mutex_consistent.c\n\nNONPORTABLE_SRCS = \\\n\t\tpthread_mutexattr_setkind_np.c \\\n\t\tpthread_mutexattr_getkind_np.c \\\n\t\tpthread_getw32threadhandle_np.c \\\n\t\tpthread_delay_np.c \\\n\t\tpthread_num_processors_np.c \\\n\t\tpthread_win32_attach_detach_np.c \\\n\t\tpthread_timechange_handler_np.c \n\nPRIVATE_SRCS\t= \\\n\t\tptw32_is_attr.c \\\n\t\tptw32_processInitialize.c \\\n\t\tptw32_processTerminate.c \\\n\t\tptw32_threadStart.c \\\n\t\tptw32_threadDestroy.c \\\n\t\tptw32_tkAssocCreate.c \\\n\t\tptw32_tkAssocDestroy.c \\\n\t\tptw32_callUserDestroyRoutines.c \\\n\t\tptw32_timespec.c \\\n\t\tptw32_relmillisecs.c \\\n\t\tptw32_throw.c \\\n\t\tptw32_getprocessors.c\n\nRWLOCK_SRCS\t= \\\n\t\tptw32_rwlock_check_need_init.c \\\n\t\tptw32_rwlock_cancelwrwait.c \\\n\t\tpthread_rwlock_init.c \\\n\t\tpthread_rwlock_destroy.c \\\n\t\tpthread_rwlockattr_init.c \\\n\t\tpthread_rwlockattr_destroy.c \\\n\t\tpthread_rwlockattr_getpshared.c \\\n\t\tpthread_rwlockattr_setpshared.c \\\n\t\tpthread_rwlock_rdlock.c \\\n\t\tpthread_rwlock_timedrdlock.c \\\n\t\tpthread_rwlock_wrlock.c \\\n\t\tpthread_rwlock_timedwrlock.c \\\n\t\tpthread_rwlock_unlock.c \\\n\t\tpthread_rwlock_tryrdlock.c \\\n\t\tpthread_rwlock_trywrlock.c\n\nSCHED_SRCS\t= \\\n\t\tpthread_attr_setschedpolicy.c \\\n\t\tpthread_attr_getschedpolicy.c \\\n\t\tpthread_attr_setschedparam.c \\\n\t\tpthread_attr_getschedparam.c \\\n\t\tpthread_attr_setinheritsched.c \\\n\t\tpthread_attr_getinheritsched.c \\\n\t\tpthread_setschedparam.c \\\n\t\tpthread_getschedparam.c \\\n\t\tsched_get_priority_max.c \\\n\t\tsched_get_priority_min.c \\\n\t\tsched_setscheduler.c \\\n\t\tsched_getscheduler.c \\\n\t\tsched_yield.c\n\nSEMAPHORE_SRCS = \\\n\t\tsem_init.c \\\n\t\tsem_destroy.c \\\n\t\tsem_trywait.c \\\n\t\tsem_timedwait.c \\\n\t\tsem_wait.c \\\n\t\tsem_post.c \\\n\t\tsem_post_multiple.c \\\n\t\tsem_getvalue.c \\\n\t\tsem_open.c \\\n\t\tsem_close.c \\\n\t\tsem_unlink.c\n\nSPIN_SRCS\t= \\\n\t\tptw32_spinlock_check_need_init.c \\\n\t\tpthread_spin_init.c \\\n\t\tpthread_spin_destroy.c \\\n\t\tpthread_spin_lock.c \\\n\t\tpthread_spin_unlock.c \\\n\t\tpthread_spin_trylock.c\n\nSYNC_SRCS\t= \\\n\t\tpthread_detach.c \\\n\t\tpthread_join.c\n\nTSD_SRCS\t= \\\n\t\tpthread_key_create.c \\\n\t\tpthread_key_delete.c \\\n\t\tpthread_setspecific.c \\\n\t\tpthread_getspecific.c\n\n\nall: clean $(DLLS)\n\nrealclean: clean\n\tif exist pthread*.dll del pthread*.dll\n\tif exist pthread*.lib del pthread*.lib\n\tif exist *.stamp del *.stamp\n\nclean:\n\tif exist *.obj del *.obj\n\tif exist *.ilk del *.ilk\n\tif exist *.ilc del *.ilc\n\tif exist *.ild del *.ild\n\tif exist *.ilf del *.ilf\n\tif exist *.ils del *.ils\n\tif exist *.tds del *.tds\n\tif exist *.pdb del *.pdb\n\tif exist *.exp del *.exp\n\tif exist *.map del *.map\n\tif exist *.o del *.o\n\tif exist *.i del *.i\n\tif exist *.res del *.res\n\n\ninstall: $(DLLS)\n\tcopy pthread*.dll $(DLLDEST)\n\tcopy pthread*.lib $(LIBDEST)\n\n$(DLLS): $(DLL_OBJS) version.res\n\tilink32 /Tpd /Gi c0d32x.obj $(DLL_OBJS), \\\n\t\t$@, ,\\\n\t\tcw32mti.lib import32.lib, ,\\\n\t\tversion.res\n\n.c.obj:\n\t$(CC) $(OPTIM) $(BCFLAGS) -c $<\n\n.rc.res:\n\t$(RC) $(RCFLAGS) $<\n\nattr.obj:\tattr.c $(ATTR_SRCS) $(INCL)\nbarrier.obj:\tbarrier.c $(BARRIER_SRCS) $(INCL)\ncancel.obj:\tcancel.c $(CANCEL_SRCS) $(INCL)\ncondvar.obj:\tcondvar.c $(CONDVAR_SRCS) $(INCL)\nexit.obj:\texit.c $(EXIT_SRCS) $(INCL)\nmisc.obj:\tmisc.c $(MISC_SRCS) $(INCL)\nmutex.obj:\tmutex.c $(MUTEX_SRCS) $(INCL)\nnonportable.obj:\tnonportable.c $(NONPORTABLE_SRCS) $(INCL)\nprivate.obj:\tprivate.c $(PRIVATE_SRCS) $(INCL)\nrwlock.obj:\trwlock.c $(RWLOCK_SRCS) $(INCL)\nsched.obj:\tsched.c $(SCHED_SRCS) $(INCL)\nsemaphore.obj:\tsemaphore.c $(SEMAPHORE_SRCS) $(INCL)\nspin.obj:\tspin.c $(SPIN_SRCS) $(INCL)\nsync.obj:\tsync.c $(SYNC_SRCS) $(INCL)\ntsd.obj:\ttsd.c $(TSD_SRCS) $(INCL)\nversion.res:\tversion.rc $(INCL)\n"
  },
  {
    "path": "deps/w32-pthreads/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nadd_library(w32-pthreads SHARED EXCLUDE_FROM_ALL)\nadd_library(OBS::w32-pthreads ALIAS w32-pthreads)\n\ntarget_sources(\n  w32-pthreads\n  PRIVATE implement.h pthread.c pthread.h sched.h semaphore.h w32-pthreads.rc\n)\n\ntarget_compile_definitions(w32-pthreads PRIVATE __CLEANUP_C PTW32_BUILD)\n\ntarget_include_directories(w32-pthreads PUBLIC \"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\")\n\nconfigure_file(cmake/windows/obs-module.rc.in w32-pthreads.rc)\n\nset_target_properties_obs(w32-pthreads PROPERTIES FOLDER deps)\n\nset_property(TARGET w32-pthreads APPEND PROPERTY PUBLIC_HEADER pthread.h sched.h)\n\ntarget_export(w32-pthreads)\n"
  },
  {
    "path": "deps/w32-pthreads/CONTRIBUTORS",
    "content": "Contributors (in approximate order of appearance)\n\n[See also the ChangeLog file where individuals are\nattributed in log entries. Likewise in the FAQ file.]\n\nBen Elliston\t\tbje at cygnus dot com\n\t\t\t\t\tInitiated the project;\n\t\t\t\t\tsetup the project infrastructure (CVS, web page, etc.);\n\t\t\t\t\tearly prototype routines.\nRoss Johnson\t\tRoss dot Johnson at  dot homemail dot com dot au\n\t\t\t\t\tearly prototype routines;\n\t\t\t\t\tongoing project coordination/maintenance;\n\t\t\t\t\timplementation of spin locks and barriers;\n\t\t\t\t\tvarious enhancements;\n\t\t\t\t\tbug fixes;\n\t\t\t\t\tdocumentation;\n\t\t\t\t\ttestsuite.\nRobert Colquhoun\trjc at trump dot net dot au\n\t\t\t\t\tEarly bug fixes.\nJohn E. Bossom\t\tJohn dot Bossom at cognos dot com\n\t\t\t\t\tContributed substantial original working implementation;\n\t\t\t\t\tbug fixes;\n\t\t\t\t\tongoing guidance and standards interpretation.\nAnders Norlander\tanorland at hem2 dot passagen dot se\n\t\t\t\t\tEarly enhancements and runtime checking for supported\n\t\t\t\t\tWin32 routines.\nTor Lillqvist\t\ttml at iki dot fi\n\t\t\t\t\tGeneral enhancements;\n\t\t\t\t\tearly bug fixes to condition variables.\nScott Lightner\t\tscott at curriculum dot com\n\t\t\t\t\tBug fix.\nKevin Ruland\t\tKevin dot Ruland at anheuser-busch dot com\n\t\t\t\t\tVarious bug fixes.\nMike Russo\t\tmiker at eai dot com\n\t\t\t\t\tBug fix.\nMark E. Armstrong\tavail at pacbell dot net\n\t\t\t\t\tBug fixes.\nLorin Hochstein \tlmh at xiphos dot ca\n\t\t\t\t\tgeneral bug fixes; bug fixes to condition variables.\nPeter Slacik\t\tPeter dot Slacik at tatramed dot sk\n\t\t\t\t\tBug fixes.\nMumit Khan\t\tkhan at xraylith dot wisc dot edu\n\t\t\t\t\tFixes to work with Mingw32.\nMilan Gardian\t\tmg at tatramed dot sk\n\t\t\t\t\tBug fixes and reports/analyses of obscure problems.\nAurelio Medina\t\taureliom at crt dot com\n\t\t\t\t\tFirst implementation of read-write locks.\nGraham Dumpleton\tGraham dot Dumpleton at ra dot pad dot otc dot telstra dot com dot au\n\t\t\t\t\tBug fix in condition variables.\nTristan Savatier\ttristan at mpegtv dot com\n\t\t\t\t\tWinCE port.\nErik Hensema\t\terik at hensema dot xs4all dot nl\n\t\t\t\t\tBug fixes.\nRich Peters\t\trpeters at micro-magic dot com\nTodd Owen\t\ttowen at lucidcalm dot dropbear dot id dot au\n\t\t\t\t\tBug fixes to dll loading.\nJason Nye\t\tjnye at nbnet dot nb dot ca\n\t\t\t\t\tImplementation of async cancelation.\nFred Forester\t\tfforest at eticomm dot net\nKevin D. Clark\t\tkclark at cabletron dot com\nDavid Baggett\t\tdmb at itasoftware dot com\n\t\t\t\t\tBug fixes.\nPaul Redondo\t\tpaul at matchvision dot com\nScott McCaskill \tscott at 3dfx dot com\n\t\t\t\t\tBug fixes.\nJef Gearhart\t\tjgearhart at tpssys dot com\n\t\t\t\t\tBug fix.\nArthur Kantor\t\takantor at bexusa dot com\n\t\t\t\t\tMutex enhancements.\nSteven Reddie\t\tsmr at essemer dot com dot au\n\t\t\t\t\tBug fix.\nAlexander Terekhov\tTEREKHOV at de dot ibm dot com\n\t\t\t\t\tRe-implemented and improved read-write locks;\n\t\t\t\t\t(with Louis Thomas) re-implemented and improved\n\t\t\t\t\tcondition variables;\n\t\t\t\t\tenhancements to semaphores;\n\t\t\t\t\tenhancements to mutexes;\n\t\t\t\t\tnew mutex implementation in 'futex' style;\n\t\t\t\t\tsuggested a robust implementation of pthread_once\n\t\t\t\t\tsimilar to that implemented by V.Kliathcko;\n\t\t\t\t\tsystem clock change handling re CV timeouts;\n\t\t\t\t\tbug fixes.\nThomas Pfaff\t\ttpfaff at gmx dot net\n\t\t\t\t\tChanges to make C version usable with C++ applications;\n\t\t\t\t\tre-implemented mutex routines to avoid Win32 mutexes\n\t\t\t\t\tand TryEnterCriticalSection;\n\t\t\t\t\tprocedure to fix Mingw32 thread-safety issues.\nFranco Bez\t\tfranco dot bez at gmx dot de\n\t\t\t\t\tprocedure to fix Mingw32 thread-safety issues.\nLouis Thomas\t\tlthomas at arbitrade dot com\n\t\t\t\t\t(with Alexander Terekhov) re-implemented and improved\n\t\t\t\t\tcondition variables.\nDavid Korn\t\tdgk at research dot att dot com\n\t\t\t\t\tPorted to UWIN.\nPhil Frisbie, Jr.\tphil at hawksoft dot com\n\t\t\t\t\tBug fix.\nRalf Brese\t\tRalf dot Brese at pdb4 dot siemens dot de\n\t\t\t\t\tBug fix.\nprionx at juno dot com \tprionx at juno dot com\n\t\t\t\t\tBug fixes.\nMax Woodbury\t\tmtew at cds dot duke dot edu\n\t\t\t\t\tPOSIX versioning conditionals;\n\t\t\t\t\treduced namespace pollution;\n\t\t\t\t\tidea to separate routines to reduce statically\n\t\t\t\t\tlinked image sizes.\nRob Fanner\t\trfanner at stonethree dot com\n\t\t\t\t\tBug fix.\nMichael Johnson \tmichaelj at maine dot rr dot com\n\t\t\t\t\tBug fix.\nNicolas Barry\t\tboozai at yahoo dot com\n\t\t\t\t\tBug fixes.\nPiet van Bruggen\tpietvb at newbridges dot nl\n\t\t\t\t\tBug fix.\nMakoto Kato\t\traven at oldskool dot jp\n\t\t\t\t\tAMD64 port.\nPanagiotis E. Hadjidoukas\tpeh at hpclab dot ceid dot upatras dot gr\n                                phadjido at cs dot uoi dot gr\n\t\t\t\t\tContributed the QueueUserAPCEx package which\n\t\t\t\t\tmakes preemptive async cancelation possible.\nWill Bryant\t\twill dot bryant at ecosm dot com\n\t\t\t\t\tBorland compiler patch and makefile.\nAnuj Goyal\t\tanuj dot goyal at gmail dot com\n\t\t\t\t\tPort to Digital Mars compiler.\nGottlob Frege\t\tgottlobfrege at  gmail dot com\n\t\t\t\t\tre-implemented pthread_once (version 2)\n\t\t\t\t\t(pthread_once cancellation added by rpj).\nVladimir Kliatchko\tvladimir at kliatchko dot com\n\t\t\t\t\treimplemented pthread_once with the same form\n\t\t\t\t\tas described by A.Terekhov (later version 2);\n\t\t\t\t\timplementation of MCS (Mellor-Crummey/Scott) locks.\nRamiro Polla\t\tramiro.polla at gmail dot com\n\t\t\t\t\tstatic library auto init/cleanup on application\n\t\t\t\t\tstart/exit via RT hooks (MSC and GCC compilers only).\nDaniel Richard G.\t\t\tskunk at iSKUNK dot org\n\t\t\t\t\tPatches and cleanups for x86 and x64, particularly\n\t\t\t\t\tacross a range of MS build environments.\nJohn Kamp\t\tjohn dot kamp at globalgraphics dot com\n\t\t\t\t\tPatches to fix various problems on x64; brutal testing\n\t\t\t\t\tparticularly using high memory run environments.\n\n"
  },
  {
    "path": "deps/w32-pthreads/COPYING",
    "content": "\tpthreads-win32 - a POSIX threads library for Microsoft Windows\n\n\nThis file is Copyrighted\n------------------------\n\n    This file is covered under the following Copyright:\n\n\tCopyright (C) 2001,2006 Ross P. Johnson\n\tAll rights reserved.\n\n\tEveryone is permitted to copy and distribute verbatim copies\n\tof this license document, but changing it is not allowed.\n\nPthreads-win32 is covered by the GNU Lesser General Public License\n------------------------------------------------------------------\n\n    Pthreads-win32 is open software; you can redistribute it and/or\n    modify it under the terms of the GNU Lesser General Public License\n    as published by the Free Software Foundation version 2.1 of the\n    License.\n\n    Pthreads-win32 is several binary link libraries, several modules,\n    associated interface definition files and scripts used to control\n    its compilation and installation.\n\n    Pthreads-win32 is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Lesser General Public License for more details.\n\n    A copy of the GNU Lesser General Public License is distributed with\n    pthreads-win32 under the filename:\n\n\t    COPYING.LIB\n\n    You should have received a copy of the version 2.1 GNU Lesser General\n    Public License with pthreads-win32; if not, write to:\n\n\t    Free Software Foundation, Inc.\n\t    51 Franklin Street\n\t    Fifth Floor\n\t    Boston, MA\t02110-1301\n\t    USA\n\n    The contact addresses for pthreads-win32 is as follows:\n\n        Web:\thttp://sources.redhat.com/pthreads-win32\n        Email:  Ross Johnson\n                Please use: Firstname.Lastname@homemail.com.au\n\n\n\nPthreads-win32 copyrights and exception files\n---------------------------------------------\n\n    With the exception of the files listed below, Pthreads-win32\n    is covered under the following GNU Lesser General Public License\n    Copyrights:\n\n\tPthreads-win32 - POSIX Threads Library for Win32\n\tCopyright(C) 1998 John E. Bossom\n\tCopyright(C) 1999,2006 Pthreads-win32 contributors\n\n\tThe current list of contributors is contained\n        in the file CONTRIBUTORS included with the source\n\tcode distribution. The current list of CONTRIBUTORS\n\tcan also be seen at the following WWW location:\n        http://sources.redhat.com/pthreads-win32/contributors.html\n\n    Contact Email: Ross Johnson\n                   Please use: Firstname.Lastname@homemail.com.au\n\n    These files are not covered under one of the Copyrights listed above:\n\n            COPYING\n\t    COPYING.LIB\n            tests/rwlock7.c\n\n    This file, COPYING, is distributed under the Copyright found at the\n    top of this file.  It is important to note that you may distribute\n    verbatim copies of this file but you may not modify this file.\n\n    The file COPYING.LIB, which contains a copy of the version 2.1\n    GNU Lesser General Public License, is itself copyrighted by the\n    Free Software Foundation, Inc.  Please note that the Free Software\n    Foundation, Inc. does NOT have a copyright over Pthreads-win32,\n    only the COPYING.LIB that is supplied with pthreads-win32.\n\n    The file tests/rwlock7.c is derived from code written by\n    Dave Butenhof for his book 'Programming With POSIX(R) Threads'.\n    The original code was obtained by free download from his website\n    http://home.earthlink.net/~anneart/family/Threads/source.html\n    and did not contain a copyright or author notice. It is assumed to\n    be freely distributable.\n\n    In all cases one may use and distribute these exception files freely.\n    And because one may freely distribute the LGPL covered files, the\n    entire pthreads-win32 source may be freely used and distributed.\n\n\n\nGeneral Copyleft and License info\n---------------------------------\n\n    For general information on Copylefts, see:\n\n\thttp://www.gnu.org/copyleft/\n\n    For information on GNU Lesser General Public Licenses, see:\n\n\thttp://www.gnu.org/copyleft/lesser.html\n\thttp://www.gnu.org/copyleft/lesser.txt\n\n\nWhy pthreads-win32 did not use the GNU General Public License\n-------------------------------------------------------------\n\n    The goal of the pthreads-win32 project has been to\n    provide a quality and complete implementation of the POSIX\n    threads API for Microsoft Windows within the limits imposed\n    by virtue of it being a stand-alone library and not\n    linked directly to other POSIX compliant libraries. For\n    example, some functions and features, such as those based\n    on POSIX signals, are missing.\n\n    Pthreads-win32 is a library, available in several different\n    versions depending on supported compilers, and may be used\n    as a dynamically linked module or a statically linked set of\n    binary modules. It is not an application on it's own.\n\n    It was fully intended that pthreads-win32 be usable with\n    commercial software not covered by either the GPL or the LGPL\n    licenses. Pthreads-win32 has many contributors to it's\n    code base, many of whom have done so because they have\n    used the library in commercial or proprietry software\n    projects.\n\n    Releasing pthreads-win32 under the LGPL ensures that the\n    library can be used widely, while at the same time ensures\n    that bug fixes and improvements to the pthreads-win32 code\n    itself is returned to benefit all current and future users\n    of the library.\n\n    Although pthreads-win32 makes it possible for applications\n    that use POSIX threads to be ported to Win32 platforms, the\n    broader goal of the project is to encourage the use of open\n    standards, and in particular, to make it just a little easier\n    for developers writing Win32 applications to consider\n    widening the potential market for their products.\n"
  },
  {
    "path": "deps/w32-pthreads/COPYING.LIB",
    "content": "\t\t  GNU LESSER GENERAL PUBLIC LICENSE\n\t\t       Version 2.1, February 1999\n\n Copyright (C) 1991, 1999 Free Software Foundation, Inc.\n 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n[This is the first released version of the Lesser GPL.  It also counts\n as the successor of the GNU Library Public License, version 2, hence\n the version number 2.1.]\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicenses are intended to guarantee your freedom to share and change\nfree software--to make sure the software is free for all its users.\n\n  This license, the Lesser General Public License, applies to some\nspecially designated software packages--typically libraries--of the\nFree Software Foundation and other authors who decide to use it.  You\ncan use it too, but we suggest you first think carefully about whether\nthis license or the ordinary General Public License is the better\nstrategy to use in any particular case, based on the explanations below.\n\n  When we speak of free software, we are referring to freedom of use,\nnot price.  Our General Public Licenses are designed to make sure that\nyou have the freedom to distribute copies of free software (and charge\nfor this service if you wish); that you receive source code or can get\nit if you want it; that you can change the software and use pieces of\nit in new free programs; and that you are informed that you can do\nthese things.\n\n  To protect your rights, we need to make restrictions that forbid\ndistributors to deny you these rights or to ask you to surrender these\nrights.  These restrictions translate to certain responsibilities for\nyou if you distribute copies of the library or if you modify it.\n\n  For example, if you distribute copies of the library, whether gratis\nor for a fee, you must give the recipients all the rights that we gave\nyou.  You must make sure that they, too, receive or can get the source\ncode.  If you link other code with the library, you must provide\ncomplete object files to the recipients, so that they can relink them\nwith the library after making changes to the library and recompiling\nit.  And you must show them these terms so they know their rights.\n\n  We protect your rights with a two-step method: (1) we copyright the\nlibrary, and (2) we offer you this license, which gives you legal\npermission to copy, distribute and/or modify the library.\n\n  To protect each distributor, we want to make it very clear that\nthere is no warranty for the free library.  Also, if the library is\nmodified by someone else and passed on, the recipients should know\nthat what they have is not the original version, so that the original\nauthor's reputation will not be affected by problems that might be\nintroduced by others.\n\f\n  Finally, software patents pose a constant threat to the existence of\nany free program.  We wish to make sure that a company cannot\neffectively restrict the users of a free program by obtaining a\nrestrictive license from a patent holder.  Therefore, we insist that\nany patent license obtained for a version of the library must be\nconsistent with the full freedom of use specified in this license.\n\n  Most GNU software, including some libraries, is covered by the\nordinary GNU General Public License.  This license, the GNU Lesser\nGeneral Public License, applies to certain designated libraries, and\nis quite different from the ordinary General Public License.  We use\nthis license for certain libraries in order to permit linking those\nlibraries into non-free programs.\n\n  When a program is linked with a library, whether statically or using\na shared library, the combination of the two is legally speaking a\ncombined work, a derivative of the original library.  The ordinary\nGeneral Public License therefore permits such linking only if the\nentire combination fits its criteria of freedom.  The Lesser General\nPublic License permits more lax criteria for linking other code with\nthe library.\n\n  We call this license the \"Lesser\" General Public License because it\ndoes Less to protect the user's freedom than the ordinary General\nPublic License.  It also provides other free software developers Less\nof an advantage over competing non-free programs.  These disadvantages\nare the reason we use the ordinary General Public License for many\nlibraries.  However, the Lesser license provides advantages in certain\nspecial circumstances.\n\n  For example, on rare occasions, there may be a special need to\nencourage the widest possible use of a certain library, so that it becomes\na de-facto standard.  To achieve this, non-free programs must be\nallowed to use the library.  A more frequent case is that a free\nlibrary does the same job as widely used non-free libraries.  In this\ncase, there is little to gain by limiting the free library to free\nsoftware only, so we use the Lesser General Public License.\n\n  In other cases, permission to use a particular library in non-free\nprograms enables a greater number of people to use a large body of\nfree software.  For example, permission to use the GNU C Library in\nnon-free programs enables many more people to use the whole GNU\noperating system, as well as its variant, the GNU/Linux operating\nsystem.\n\n  Although the Lesser General Public License is Less protective of the\nusers' freedom, it does ensure that the user of a program that is\nlinked with the Library has the freedom and the wherewithal to run\nthat program using a modified version of the Library.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.  Pay close attention to the difference between a\n\"work based on the library\" and a \"work that uses the library\".  The\nformer contains code derived from the library, whereas the latter must\nbe combined with the library in order to run.\n\f\n\t\t  GNU LESSER GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License Agreement applies to any software library or other\nprogram which contains a notice placed by the copyright holder or\nother authorized party saying it may be distributed under the terms of\nthis Lesser General Public License (also called \"this License\").\nEach licensee is addressed as \"you\".\n\n  A \"library\" means a collection of software functions and/or data\nprepared so as to be conveniently linked with application programs\n(which use some of those functions and data) to form executables.\n\n  The \"Library\", below, refers to any such software library or work\nwhich has been distributed under these terms.  A \"work based on the\nLibrary\" means either the Library or any derivative work under\ncopyright law: that is to say, a work containing the Library or a\nportion of it, either verbatim or with modifications and/or translated\nstraightforwardly into another language.  (Hereinafter, translation is\nincluded without limitation in the term \"modification\".)\n\n  \"Source code\" for a work means the preferred form of the work for\nmaking modifications to it.  For a library, complete source code means\nall the source code for all modules it contains, plus any associated\ninterface definition files, plus the scripts used to control compilation\nand installation of the library.\n\n  Activities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning a program using the Library is not restricted, and output from\nsuch a program is covered only if its contents constitute a work based\non the Library (independent of the use of the Library in a tool for\nwriting it).  Whether that is true depends on what the Library does\nand what the program that uses the Library does.\n  \n  1. You may copy and distribute verbatim copies of the Library's\ncomplete source code as you receive it, in any medium, provided that\nyou conspicuously and appropriately publish on each copy an\nappropriate copyright notice and disclaimer of warranty; keep intact\nall the notices that refer to this License and to the absence of any\nwarranty; and distribute a copy of this License along with the\nLibrary.\n\n  You may charge a fee for the physical act of transferring a copy,\nand you may at your option offer warranty protection in exchange for a\nfee.\n\f\n  2. You may modify your copy or copies of the Library or any portion\nof it, thus forming a work based on the Library, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) The modified work must itself be a software library.\n\n    b) You must cause the files modified to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    c) You must cause the whole of the work to be licensed at no\n    charge to all third parties under the terms of this License.\n\n    d) If a facility in the modified Library refers to a function or a\n    table of data to be supplied by an application program that uses\n    the facility, other than as an argument passed when the facility\n    is invoked, then you must make a good faith effort to ensure that,\n    in the event an application does not supply such function or\n    table, the facility still operates, and performs whatever part of\n    its purpose remains meaningful.\n\n    (For example, a function in a library to compute square roots has\n    a purpose that is entirely well-defined independent of the\n    application.  Therefore, Subsection 2d requires that any\n    application-supplied function or table used by this function must\n    be optional: if the application does not supply it, the square\n    root function must still compute square roots.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Library,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Library, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote\nit.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Library.\n\nIn addition, mere aggregation of another work not based on the Library\nwith the Library (or with a work based on the Library) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may opt to apply the terms of the ordinary GNU General Public\nLicense instead of this License to a given copy of the Library.  To do\nthis, you must alter all the notices that refer to this License, so\nthat they refer to the ordinary GNU General Public License, version 2,\ninstead of to this License.  (If a newer version than version 2 of the\nordinary GNU General Public License has appeared, then you can specify\nthat version instead if you wish.)  Do not make any other change in\nthese notices.\n\f\n  Once this change is made in a given copy, it is irreversible for\nthat copy, so the ordinary GNU General Public License applies to all\nsubsequent copies and derivative works made from that copy.\n\n  This option is useful when you wish to copy part of the code of\nthe Library into a program that is not a library.\n\n  4. You may copy and distribute the Library (or a portion or\nderivative of it, under Section 2) in object code or executable form\nunder the terms of Sections 1 and 2 above provided that you accompany\nit with the complete corresponding machine-readable source code, which\nmust be distributed under the terms of Sections 1 and 2 above on a\nmedium customarily used for software interchange.\n\n  If distribution of object code is made by offering access to copy\nfrom a designated place, then offering equivalent access to copy the\nsource code from the same place satisfies the requirement to\ndistribute the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  5. A program that contains no derivative of any portion of the\nLibrary, but is designed to work with the Library by being compiled or\nlinked with it, is called a \"work that uses the Library\".  Such a\nwork, in isolation, is not a derivative work of the Library, and\ntherefore falls outside the scope of this License.\n\n  However, linking a \"work that uses the Library\" with the Library\ncreates an executable that is a derivative of the Library (because it\ncontains portions of the Library), rather than a \"work that uses the\nlibrary\".  The executable is therefore covered by this License.\nSection 6 states terms for distribution of such executables.\n\n  When a \"work that uses the Library\" uses material from a header file\nthat is part of the Library, the object code for the work may be a\nderivative work of the Library even though the source code is not.\nWhether this is true is especially significant if the work can be\nlinked without the Library, or if the work is itself a library.  The\nthreshold for this to be true is not precisely defined by law.\n\n  If such an object file uses only numerical parameters, data\nstructure layouts and accessors, and small macros and small inline\nfunctions (ten lines or less in length), then the use of the object\nfile is unrestricted, regardless of whether it is legally a derivative\nwork.  (Executables containing this object code plus portions of the\nLibrary will still fall under Section 6.)\n\n  Otherwise, if the work is a derivative of the Library, you may\ndistribute the object code for the work under the terms of Section 6.\nAny executables containing that work also fall under Section 6,\nwhether or not they are linked directly with the Library itself.\n\f\n  6. As an exception to the Sections above, you may also combine or\nlink a \"work that uses the Library\" with the Library to produce a\nwork containing portions of the Library, and distribute that work\nunder terms of your choice, provided that the terms permit\nmodification of the work for the customer's own use and reverse\nengineering for debugging such modifications.\n\n  You must give prominent notice with each copy of the work that the\nLibrary is used in it and that the Library and its use are covered by\nthis License.  You must supply a copy of this License.  If the work\nduring execution displays copyright notices, you must include the\ncopyright notice for the Library among them, as well as a reference\ndirecting the user to the copy of this License.  Also, you must do one\nof these things:\n\n    a) Accompany the work with the complete corresponding\n    machine-readable source code for the Library including whatever\n    changes were used in the work (which must be distributed under\n    Sections 1 and 2 above); and, if the work is an executable linked\n    with the Library, with the complete machine-readable \"work that\n    uses the Library\", as object code and/or source code, so that the\n    user can modify the Library and then relink to produce a modified\n    executable containing the modified Library.  (It is understood\n    that the user who changes the contents of definitions files in the\n    Library will not necessarily be able to recompile the application\n    to use the modified definitions.)\n\n    b) Use a suitable shared library mechanism for linking with the\n    Library.  A suitable mechanism is one that (1) uses at run time a\n    copy of the library already present on the user's computer system,\n    rather than copying library functions into the executable, and (2)\n    will operate properly with a modified version of the library, if\n    the user installs one, as long as the modified version is\n    interface-compatible with the version that the work was made with.\n\n    c) Accompany the work with a written offer, valid for at\n    least three years, to give the same user the materials\n    specified in Subsection 6a, above, for a charge no more\n    than the cost of performing this distribution.\n\n    d) If distribution of the work is made by offering access to copy\n    from a designated place, offer equivalent access to copy the above\n    specified materials from the same place.\n\n    e) Verify that the user has already received a copy of these\n    materials or that you have already sent this user a copy.\n\n  For an executable, the required form of the \"work that uses the\nLibrary\" must include any data and utility programs needed for\nreproducing the executable from it.  However, as a special exception,\nthe materials to be distributed need not include anything that is\nnormally distributed (in either source or binary form) with the major\ncomponents (compiler, kernel, and so on) of the operating system on\nwhich the executable runs, unless that component itself accompanies\nthe executable.\n\n  It may happen that this requirement contradicts the license\nrestrictions of other proprietary libraries that do not normally\naccompany the operating system.  Such a contradiction means you cannot\nuse both them and the Library together in an executable that you\ndistribute.\n\f\n  7. You may place library facilities that are a work based on the\nLibrary side-by-side in a single library together with other library\nfacilities not covered by this License, and distribute such a combined\nlibrary, provided that the separate distribution of the work based on\nthe Library and of the other library facilities is otherwise\npermitted, and provided that you do these two things:\n\n    a) Accompany the combined library with a copy of the same work\n    based on the Library, uncombined with any other library\n    facilities.  This must be distributed under the terms of the\n    Sections above.\n\n    b) Give prominent notice with the combined library of the fact\n    that part of it is a work based on the Library, and explaining\n    where to find the accompanying uncombined form of the same work.\n\n  8. You may not copy, modify, sublicense, link with, or distribute\nthe Library except as expressly provided under this License.  Any\nattempt otherwise to copy, modify, sublicense, link with, or\ndistribute the Library is void, and will automatically terminate your\nrights under this License.  However, parties who have received copies,\nor rights, from you under this License will not have their licenses\nterminated so long as such parties remain in full compliance.\n\n  9. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Library or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Library (or any work based on the\nLibrary), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Library or works based on it.\n\n  10. Each time you redistribute the Library (or any work based on the\nLibrary), the recipient automatically receives a license from the\noriginal licensor to copy, distribute, link with or modify the Library\nsubject to these terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties with\nthis License.\n\f\n  11. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Library at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Library by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Library.\n\nIf any portion of this section is held invalid or unenforceable under any\nparticular circumstance, the balance of the section is intended to apply,\nand the section as a whole is intended to apply in other circumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  12. If the distribution and/or use of the Library is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Library under this License may add\nan explicit geographical distribution limitation excluding those countries,\nso that distribution is permitted only in or among countries not thus\nexcluded.  In such case, this License incorporates the limitation as if\nwritten in the body of this License.\n\n  13. The Free Software Foundation may publish revised and/or new\nversions of the Lesser General Public License from time to time.\nSuch new versions will be similar in spirit to the present version,\nbut may differ in detail to address new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Library\nspecifies a version number of this License which applies to it and\n\"any later version\", you have the option of following the terms and\nconditions either of that version or of any later version published by\nthe Free Software Foundation.  If the Library does not specify a\nlicense version number, you may choose any version ever published by\nthe Free Software Foundation.\n\f\n  14. If you wish to incorporate parts of the Library into other free\nprograms whose distribution conditions are incompatible with these,\nwrite to the author to ask for permission.  For software which is\ncopyrighted by the Free Software Foundation, write to the Free\nSoftware Foundation; we sometimes make exceptions for this.  Our\ndecision will be guided by the two goals of preserving the free status\nof all derivatives of our free software and of promoting the sharing\nand reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO\nWARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.\nEXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR\nOTHER PARTIES PROVIDE THE LIBRARY \"AS IS\" WITHOUT WARRANTY OF ANY\nKIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE\nLIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME\nTHE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN\nWRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY\nAND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU\nFOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR\nCONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE\nLIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING\nRENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A\nFAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF\nSUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\nDAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n\f\n           How to Apply These Terms to Your New Libraries\n\n  If you develop a new library, and you want it to be of the greatest\npossible use to the public, we recommend making it free software that\neveryone can redistribute and change.  You can do so by permitting\nredistribution under these terms (or, alternatively, under the terms of the\nordinary General Public License).\n\n  To apply these terms, attach the following notices to the library.  It is\nsafest to attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least the\n\"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the library's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This library is free software; you can redistribute it and/or\n    modify it under the terms of the GNU Lesser General Public\n    License as published by the Free Software Foundation; either\n    version 2.1 of the License, or (at your option) any later version.\n\n    This library is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n    Lesser General Public License for more details.\n\n    You should have received a copy of the GNU Lesser General Public\n    License along with this library; if not, write to the Free Software\n    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\n\nAlso add information on how to contact you by electronic and paper mail.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the library, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the\n  library `Frob' (a library for tweaking knobs) written by James Random Hacker.\n\n  <signature of Ty Coon>, 1 April 1990\n  Ty Coon, President of Vice\n\nThat's all there is to it!\n\n\n"
  },
  {
    "path": "deps/w32-pthreads/ChangeLog",
    "content": "2012-03-18  Ross Johnson <ross dot johnson at homemail dot com dot au>\n\n\t* create.c (pthread_create): add __cdecl attribute to thread routine\n\targ\n\t* implement.h (pthread_key_t): add __cdecl attribute to destructor\n\telement\n\t(ThreadParms): likewise for start element\n\t* pthread.h (pthread_create): add __cdecl to prototype start arg\n\t(pthread_once): likewise for init_routine arg\n\t(pthread_key_create): likewise for destructor arg\n\t(ptw32_cleanup_push): replace type of routine arg with previously\n\tdefined ptw32_cleanup_callback_t\n\t* pthread_key_create.c: add __cdecl attribute to destructor arg\n\t* pthread_once.c: add __cdecl attribute to init_routine arg\n\t* ptw32_threadStart.c (start): add __cdecl to start variable type\n\n\n2011-07-06  Ross Johnson <ross dot johnson at homemail dot com dot au>\n\n\t* pthread_cond_wait.c (pragma inline_depth): this is almost redundant\n\tnow nevertheless fixed thei controlling MSC_VER from \"< 800\" to\n\t\"< 1400\" (i.e. any prior to VC++ 8.0).\n\t* pthread_once.ci (pragma inline_depth): Likewise.\n\t* pthread_rwlock_timedwrlock.ci (pragma inline_depth): Likewise.\n\t* pthread_rwlock_wrlock.ci (pragma inline_depth): Likewise.\n\t* sem_timedwait.ci (pragma inline_depth): Likewise.\n\t* sem_wait.ci (pragma inline_depth): Likewise.\n\n2011-07-05  Ross Johnson <ross dot johnson at homemail dot com dot au>\n\n\t* pthread_win32_attach_detach_np.c: Use strncat_s if available\n\tto removei a compile warning; MingW supports this routine but we\n\tcontinue to use strncat anyway there because it is secure if\n\tgiven the correct parameters; fix strncat param 3 to avoid\n\tbuffer overrun exploitation potential.\n\n2011-07-03  Ross Johnson <ross dot johnson at homemail dot com dot au>\n\n\t* pthread_spin_unlock.c (EPERM): Return success if unlocking a lock\n\tthat is not locked, because single CPU machines wrap a\n\tPTHREAD_MUTEX_NORMAL mutex, which returns success in this case.\n\t* pthread_win32_attach_detach_np.c (QUSEREX.DLL): Load from an\n\tabsolute path only which must be the Windows System folder.\n\n2011-07-03 Daniel Richard G. <skunk at iskunk dot org>\n\n\t* Makefile (_WIN32_WINNT): Removed; duplicate definition in\n\timplement.h; more cleanup and enhancements.\n\n2011-07-02 Daniel Richard G. <skunk at iskunk dot org>\n\n\t* Makefile: Cleanups and implovements.\n\t* ptw32_MCS_locks.c: Casting fixes.\n\t* implement.h: Interlocked call and argument casting macro fixes\n\tto support older and newer build environments.\n\n2011-07-01  Ross Johnson <ross dot johnson at homemail dot com dot au>\n\n\t* *.[ch] (PTW32_INTERLOCKED_*): Redo 23 and 64 bit versions of these\n\tmacros and re-apply in code to undo the incorrect changes from\n\t2011-06-29; remove some size_t casts which should not be required\n\tand may be problematic.a\n\tThere are now two sets of macros:\n\tPTW32_INTERLOCKED_*_LONG which work only on 32 bit integer variables;\n\tPTW32_INTERLOCKED_*_SIZE which work on size_t integer variables, i.e.\n\tLONG for 32 bit systems and LONGLONG for 64 bit systems.\n\t* implement.h (MCS locks): nextFlag and waitFlag are now HANDLE type.\n\t* ptw32_MCS_locks.c: Likewise.\n\t* pthread.h (#include <setjmp.h>): Removed.\n\t* ptw32_throw.c (#include <setjmp.h>): Added.\n\t* ptw32_threadStart.c (#include <setjmp.h>): Added.\n\t* implement.h (#include <setjmp.h>): Added.\n\n2011-06-30  Ross Johnson <ross dot johnson at homemail dot com dot au>\n\n\t* pthread_once.c: Tighten 'if' statement casting; fix interlocked\n\tpointer cast for 64 bit compatibility (missed yesterday); remove\n\tthe superfluous static cleanup routine and call the release routine\n\tdirectly if popped.\n\t* create.c (stackSize): Now type size_t.\n\t* pthread.h (struct ptw32_thread_t_): Rearrange to fix element alignments.\n\n2011-06-29 Daniel Richard G. <skunk at iskunk dot org>\n\n\t* ptw32_relmillisecs.c (ftime):\n\t  _ftime64_s() is only available in MSVC 2005 or later;\n\t  _ftime64() is available in MinGW or MSVC 2002 or later;\n\t  _ftime() is always available.\n\t* pthread.h (long long): Not defined in older MSVC 6.\n\t* implement.h (long long): Likewise.\n\t* pthread_getunique_np.c (long long): Likewise.\n\n2011-06-29  Ross Johnson <ross dot johnson at homemail dot com dot au>\n\n\t* *.[ch] (PTW32_INTERLOCKED_*): These macros should now work for\n\tboth 32 and 64 bit builds. The MingW versions are all inlined asm\n\twhile the MSVC versions expand to their Interlocked* or Interlocked*64\n\tcounterparts appropriately. The argument type have also been changed\n\tto cast to the appropriate value or pointer size for the architecture.\n\n2011-05-29  Ross Johnson <ross dot johnson at homemail dot com dot au>\n\n\t* *.[ch] (#ifdef): Extended cleanup to whole project.\n\n2011-05-29 Daniel Richard G. <skunk at iskunk dot org>\n\n\t* Makefile (CC): Define  CC to allow use of other compatible\n\tcompilers such as the Intel compilter icl.\n\t* implement.h (#if): Fix forms like #if HAVE_SOMETHING.\n\t* pthread.h: Likewise.\n\t* sched.h: Likewise; PTW32_LEVEL_* becomes PTW32_SCHED_LEVEL_*.\n\t* semaphore.h: Likewise.\n\n2011-05-11  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* ptw32_callUserDestroyRoutines.c (terminate): Altered includes\n\tto match ptw32_threadStart.c.\n\t* GNUmakefile (GCE-inlined-debug, DOPT): Fixed.\n\n2011-04-31  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* (robust mutexes): Added this API. The API is not\n\tmandatory for implementations that don't support PROCESS_SHARED\n\tmutexes, nevertheless it was considered useful both functionally\n\tand for source-level compatibility.\n\t\n2011-03-26  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* pthread_getunique_np.c: New non-POSIX interface for compatibility\n\twith some other implementations; returns a 64 bit sequence number\n\tthat is unique to each thread in the process.\n\t* pthread.h (pthread_getunique_np): Added.\n\t* global.c: Add global sequence counter for above.\n\t* implement.h: Likewise.\n\n2011-03-25  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* (cancelLock): Convert to an MCS lock and rename to stateLock.\n\t* (threadLock): Likewise.\n\t* (keyLock): Likewise.\n\t* pthread_mutex*.c: First working robust mutexes.\n\n2011-03-11  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* implement.h (PTW32_INTERLOCKED_*CREMENT macros): increment/decrement\n\tusing ++/-- instead of add/subtract 1.\n\t* ptw32_MCS_lock.c: Make casts consistent.\n\n2011-03-09  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* implement.h (ptw32_thread_t_): Add process unique sequence number.\n\t* global.c: Replace global Critical Section objects with MCS\n\tqueue locks.\n\t* implement.h: Likewise.\n\t* pthread_cond_destroy.c: Likewise.\n\t* pthread_cond_init.c: Likewise.\n\t* pthread_detach.c: Likewise.\n\t* pthread_join.c: Likewise.\n\t* pthread_kill.c: Likewise.\n\t* pthread_mutex_destroy.c: Likewise.\n\t* pthread_rwlock_destroy.c: Likewise.\n\t* pthread_spin_destroy.c: Likewise.\n\t* pthread_timechange_handler_np.c: Likewise.\n\t* ptw32_cond_check_need_init.c: Likewise.\n\t* ptw32_mutex_check_need_init.c: Likewise.\n\t* ptw32_processInitialize.c: Likewise.\n\t* ptw32_processTerminate.c: Likewise.\n\t* ptw32_reuse.c: Likewise.\n\t* ptw32_rwlock_check_need_init.c: Likewise.\n\t* ptw32_spinlock_check_need_init.c: Likewise.\n\n2011-03-06  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* several (MINGW64): Cast and call fixups for 64 bit compatibility;\n\tclean build via x86_64-w64-mingw32 cross toolchain on Linux i686\n\ttargeting x86_64 win64.\n\t* ptw32_threadStart.c (ptw32_threadStart): Routine no longer attempts\n\tto pass [unexpected C++] exceptions out of scope but ends the thread\n\tnormally setting EINTR as the exit status.\n\t* ptw32_throw.c: Fix C++ exception throwing warnings; ignore\n\tinformational warning.\n\t* implement.h: Likewise with the corresponding header definition.\n\n2011-03-04  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* implement.h (PTW32_INTERLOCKED_*): Mingw32 does not provide\n\tthe __sync_* intrinsics so implemented them here as macro\n\tassembler routines. MSVS Interlocked* are emmitted as intrinsics\n\twherever possible, so we want mingw to match it; Extended to\n\tinclude all interlocked routines used by the library; implemented\n\tx86_64 versions also.\n\t* ptw32_InterlockedCompareExchange.c: No code remaining here.\n\t* ptw32_MCS_lock.c: Converted interlocked calls to use new macros.\n\t* pthread_barrier_wait.c: Likewise.\n\t* pthread_once.c: Likewise.\n\t* ptw32_MCS_lock.c (ptw32_mcs_node_substitute): Name changed to\n\tptw32_mcs_node_transfer.\n\n2011-02-28  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* ptw32_relmillisecs.c: If possible, use _ftime64_s or _ftime64\n\tbefore resorting to _ftime.\n\n2011-02-27  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* sched_setscheduler.c: Ensure the handle is closed after use.\n\t* sched_getscheduler.c: Likewise.\n\t* pthread.h: Remove POSIX compatibility macros; don't define\n\ttimespec if already defined.\n\t* context.h: Changes for 64 bit.\n\t* pthread_cancel.c: Likewise.\n\t* pthread_exit.c: Likewise.\n\t* pthread_spin_destroy.c: Likewise.\n\t* pthread_timechange_handler_np.c: Likewise.\n\t* ptw32_MCS_lock.c: Likewise; some of these changes may\n\tnot be compatible with pre Windows 2000 systems; reverse the order of\n\tthe includes.\n\t* ptw32_threadStart.c: Likewise.\n\t* ptw32_throw.c: Likewise.\n\n2011-02-13  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* pthread_self: Add comment re returning 'nil' value to\n\tindicate failure only to win32 threads that call us.\n\t* pthread_attr_setstackaddr: Fix comments; note this\n\tfunction and it's compliment are now removed from SUSv4.\n\n2011-02-12  Ross Johnson <ross.johnson at homemail.com.au>\n\n\tREADME.NONPORTABLE: Record a description of an obvious\n\tmethod for nulling/comparing/hashing pthread_t using a\n\tunion; plus and investigation of a change of type for\n\tpthread_t (to a union) to neutralise any padding bits and\n\tbytes if they occur in pthread_t (the current pthread_t struct\n\tdoes not contain padding AFAIK, but porting the library to a\n\tfuture architecture may introduce them). Padding affects\n\tbyte-by-byte copies and compare operations.\n\n2010-11-16  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* ChangeLog: Add this entry ;-)\n\tRestore entries from 2007 through 2009 that went missing\n\tat the last update.\n\n2010-06-19  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* ptw32_MCS_lock.c (ptw32_mcs_node_substitute): Fix variable\n\tnames to avoid using C++ keyword (\"new\").\n\t* implement.h (ptw32_mcs_node_substitute): Likewise.\n\t* pthread_barrier_wait.c: Fix signed/unsigned comparison warning.\n\n2010-06-18  Ramiro Polla  <ramiro.polla at gmail.com >\n\n\t* autostatic.c: New file; call pthread_win32_process_*()\n\tlibary init/cleanup routines automatically on application start\n\twhen statically linked.\n\t* pthread.c (autostatic.c): Included.\n\t* pthread.h (declspec): Remove import/export defines if compiler\n\tis MINGW.\n\t* sched.h (declspec): Likewise.\n\t* semaphore.h (declspec): Likewise.\n\t* need_errno.h (declspec): Likewise.\n\t* Makefile (autostatic.obj): Add for small static builds.\n\t* GNUmakefile (autostatic.o): Likewise.\n\t* NEWS (Version 2.9.0): Add changes.\n\t* README.NONPORTABLE (pthread_win32_process_*): Update\n\tdescription.\n\n2010-06-15  Ramiro Polla  <ramiro.polla at gmail.com >\n\n\t* Makefile: Remove linkage with the winsock library by default.\n\t* GNUmakefile: Likewise.\n\t* pthread_getspecific.c: Likewise by removing calls to WSA\n\tfunctions.\n\t* config.h (RETAIN_WSALASTERROR): Can be defined if necessary.\n\n2010-01-26  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* ptw32_MCS_lock.c (ptw32_mcs_node_substitute): New routine\n\tto allow relocating the lock owners thread-local node to somewhere\n\telse, e.g. to global space so that another thread can release the\n\tlock. Used in pthread_barrier_wait.\n\t(ptw32_mcs_lock_try_acquire): New routine.\n\t* pthread_barrier_init: Only one semaphore is used now.\n\t* pthread_barrier_wait: Added an MCS guard lock with the last thread\n\tto leave the barrier releasing the lock. This removes a deadlock bug\n\tobserved when there are greater than barrier-count threads\n\tattempting to cross.\n\t* pthread_barrier_destroy: Added an MCS guard lock.\n        \n2009-03-03  Stephan O'Farrill <stephan dot ofarrill at gmail dot com>\n\n\t* pthread_attr_getschedpolicy.c: Add \"const\" to function parameter\n\tin accordance with SUSv3 (POSIX).\n\t* pthread_attr_getinheritsched.c: Likewise.\n\t* pthread_mutexattr_gettype.c: Likewise.\n\n2008-06-06  Robert Kindred  <RKindred at SwRI dot edu>\n\n\t* ptw32_throw.c (ptw32_throw): Remove possible reference to NULL\n\tpointer. (At the same time made the switch block conditionally\n\tincluded only if exitCode is needed - RPJ.)\n\t* pthread_testcancel.c (pthread_testcancel): Remove duplicate and\n\tmisplaced pthread_mutex_unlock().\n\n2008-02-21  Sebastian Gottschalk  <seppig_relay at gmx dot de>\n\n\t* pthread_attr_getdetachstate.c (pthread_attr_getdetachstate):\n\tRemove potential and superfluous null pointer assignment.\n\n2007-11-22  Ivan Pizhenko  <ivanp4 at ua dot fm>\n\n\t* pthread.h (gmtime_r): gmtime returns 0 if tm represents a time\n\tprior to 1/1/1970. Notice this to prevent raising an exception.\n\t* pthread.h (localtime_r): Likewise for localtime.\n\n2007-07-14  Marcel Ruff  <mr at marcelruff dot info>\n\n\t* errno.c (_errno): Fix test for pthread_self() success.\n\t* need_errno.h: Remove unintentional line wrap from #if line.\n\n2007-07-14  Mike Romanchuk  <mromanchuk at empirix dot com>\n\n\t* pthread.h (timespec): Fix tv_sec type.\n\n2007-01-07  Sinan Kaya <sinan.kaya at siemens dot com>\n\n\t* need_errno.h: Fix declaration of _errno - the local version of\n\t_errno() is used, e.g. by WinCE.\n\n2007-01-06  Ross Johnson <ross.johnson at homemail dot com dot au>\n\n\t* ptw32_semwait.c: Add check for invalid sem_t after acquiring the\n\tsem_t state guard mutex and before affecting changes to sema state.\n                \n2007-01-06  Marcel Ruff <mr at marcelruff dot info>\n\n\t* error.c: Fix reference to pthread handle exitStatus member for\n\tbuilds that use NEED_ERRNO (i.e. WINCE).\n\t* context.h: Add support for ARM processor (WinCE).\n\t* mutex.c (process.h): Exclude for WINCE.\n\t* create.c: Likewise.\n\t* exit.c: Likewise.\n\t* implement.h: Likewise.\n\t* pthread_detach.c (signal.h): Exclude for WINCE.\n\t* pthread_join.c: Likewise.\n\t* pthread_kill.c: Likewise.\n\t* pthread_rwlock_init.c (errno.h): Remove - included by pthread.h.\n\t* pthread_rwlock_destroy.c: Likewise.\n\t* pthread_rwlock_rdlock.c: Likewise.\n\t* pthread_rwlock_timedrdlock.c: Likewise.\n\t* pthread_rwlock_timedwrlock.c: Likewise.\n\t* pthread_rwlock_tryrdlock.c: Likewise.\n\t* pthread_rwlock_trywrlock.c: likewise.\n\t* pthread_rwlock_unlock.c: Likewise.\n\t* pthread_rwlock_wrlock.c: Likewise.\n\t* pthread_rwlockattr_destroy.c: Likewise.\n\t* pthread_rwlockattr_getpshared.c: Likewise.\n\t* pthread_rwlockattr_init.c: Likewise.\n\t* pthread_rwlockattr_setpshared.c: Likewise.\n\n2007-01-06  Romano Paolo Tenca <rotenca at telvia dot it>\n\n\t* pthread_cond_destroy.c: Replace sem_wait() with non-cancelable\n\tptw32_semwait() since pthread_cond_destroy() is not a cancelation\n\tpoint.\n\t* implement.h (ptw32_spinlock_check_need_init): Add prototype.\n\t* ptw32_MCS_lock.c: Reverse order of includes.\n\n2007-01-06  Eric Berge <eric dot berge at quantum dot com>\n\n\t* pthread_cond_destroy.c: Add LeaveCriticalSection before returning\n\tafter errors.\n\n2007-01-04  Ross Johnson <ross.johnson at homemail dot com dot au>\n\n\t* ptw32_InterlockedCompareExchange.c: Conditionally skip for\n\tWin64 as not required.\n\t* pthread_win32_attach_detach_np.c (pthread_win32_process_attach_np):\n\tTest for InterlockedCompareExchange is not required for Win64.\n\t* context.h: New file. Included by pthread_cancel.h and any tests\n\tthat need it (e.g. context1.c).\n\t* pthread_cancel.c: Architecture-dependent context macros moved\n\tto context.h.\n\n2007-01-04  Kip Streithorst <KSTREITH at ball dot com>\n\n\t* implement.h (PTW32_INTERLOCKED_COMPARE_EXCHANGE): Add Win64\n\tsupport.\n\n2006-12-20  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* sem_destroy.c: Fix the race involving invalidation of the sema;\n\tfix incorrect return of EBUSY resulting from the mutex trylock\n\ton the private mutex guard.\n\t* sem_wait.c: Add check for invalid sem_t after acquiring the\n\tsem_t state guard mutex and before affecting changes to sema state.\n\t* sem_trywait.c: Likewise.\n\t* sem_timedwait.c: Likewise.\n\t* sem_getvalue.c: Likewise.\n\t* sem_post.c: Similar.\n\t* sem_post_multiple.c: Likewise.\n\t* sem_init.c: Set max Win32 semaphore count to SEM_VALUE_MAX (was\n\t_POSIX_SEM_VALUE_MAX, which is a lower value - the minimum).\n\n\t* pthread_win32_attach_detach_np.c (pthread_win32_process_attach_np):\n\tLoad COREDLL.DLL under WINCE to check existence of\n\tInterlockedCompareExchange() routine. This used to be done to test\n\tfor TryEnterCriticalSection() but was removed when this was no\n\tlonger needed.\n\n2006-01-25  Prashant Thakre <prashant.thakre at gmail.com>\n\n\t* pthread_cancel.c: Added _M_IA64 register context support.\n\n2005-05-13  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* pthread_kill.c (pthread_kill): Remove check for Win32 thread\n\tpriority (to confirm HANDLE validity). Useless since thread HANDLEs\n\ta not recycle-unique.\n\n2005-05-30  Vladimir Kliatchko  <vladimir at kliatchko.com>\n\n\t* pthread_once.c: Re-implement using an MCS queue-based lock. The form\n\tof pthread_once is as proposed by Alexander Terekhov (see entry of\n\t2005-03-13). The MCS lock implementation does not require a unique\n\t'name' to identify the lock between threads. Attempts to get the Event\n\tor Semaphore based versions of pthread_once to a satisfactory level\n\tof robustness have thus far failed. The last problem (avoiding races\n\tinvolving non recycle-unique Win32 HANDLEs) was giving everyone\n\tgrey hair trying to solve it.\n\n\t* ptw32_MCS_lock.c: New MCS queue-based lock implementation. These\n\tlocks are efficient: they have very low overhead in the uncontended case;\n\tare efficient in contention and minimise cache-coherence updates in\n\tmanaging the user level FIFO queue; do not require an ABI change in the\n\tlibrary.\n\n2005-05-27  Alexander Gottwald <alexander.gottwald at s1999.tu-chemnitz.de>\n\n\t* pthread.h: Some things, like HANDLE, were only defined if\n\tPTW32_LEVEL was >= 3. They should always be defined.\n\n2005-05-25  Vladimir Kliatchko  <vladimir at kliatchko.com>\n\n\t* pthread_once.c: Eliminate all priority operations and other\n\tcomplexity by replacing the event with a semaphore. The advantage\n\tof the change is the ability to release just one waiter if the\n\tinit_routine thread is cancelled yet still release all waiters when\n\tdone. Simplify once_control state checks to improve efficiency\n\tfurther.\n\n2005-05-24  Mikael Magnusson  <mikaelmagnusson at glocalnet.net>\n\n\t* GNUmakefile: Patched to allow cross-compile with mingw32 on Linux.\n\tIt uses macros instead of referencing dlltool, gcc and g++ directly;\n\tadded a call to ranlib. For example the GC static library can be\n\tbuilt with:\n\tmake CC=i586-mingw32msvc-gcc RC=i586-mingw32msvc-windres \\\n\tRANLIB=i586-mingw32msvc-ranlib clean GC-static\n\n2005-05-13  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* pthread_win32_attach_detach_np.c (pthread_win32_thread_detach_np):\n\tMove on-exit-only stuff from ptw32_threadDestroy() to here.\n\t* ptw32_threadDestroy.c: It's purpose is now only to reclaim thread\n\tresources for detached threads, or via pthread_join() or\n\tpthread_detach() on joinable threads.\n\t* ptw32_threadStart.c: Calling user destruct routines has moved to\n\tpthread_win32_thread_detach_np(); call pthread_win32_thread_detach_np()\n\tdirectly if statically linking, otherwise do so via dllMain; store\n\tthread return value in thread struct for all cases, including\n\tcancellation and exception exits; thread abnormal exits\tgo via\n\tpthread_win32_thread_detach_np.\n\t* pthread_join.c (pthread_join): Don't try to get return code from\n\tWin32 thread - always get it from he thread struct.\n\t* pthread_detach.c (pthread_detach): reduce extent of the thread\n\texistence check since we now don't care if the Win32 thread HANDLE has\n\tbeen closed; reclaim thread resources if the thread has exited already.\n\t* ptw32_throw.c (ptw32_throw): For Win32 threads that are not implicit,\n\tonly Call thread cleanup if statically linking, otherwise leave it to\n\tdllMain.\n\t* sem_post.c (_POSIX_SEM_VALUE_MAX): Change to SEM_VALUE_MAX.\n\t* sem_post_multiple.c: Likewise.\n\t* sem_init.c: Likewise.\n\n2005-05-10  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* pthread_join.c (pthread_join): Add missing check for thread ID\n\treference count in thread existence test; reduce extent of the\n\texistence test since we don't care if the Win32 thread HANDLE has\n\tbeen closed.\n\n2005-05-09  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* ptw32_callUserDestroyRoutines.c: Run destructor process (i.e.\n\tloop over all keys calling destructors) up to\n\tPTHREAD_DESTRUCTOR_ITERATIONS times if TSD value isn't NULL yet;\n\tmodify assoc management.\n\t* pthread_key_delete.c: Modify assoc management.\n\t* ptw32_tkAssocDestroy.c: Fix error in assoc removal from chains.\n\t* pthread.h\n\t(_POSIX_THREAD_DESTRUCTOR_ITERATIONS): Define to value specified by\n\tPOSIX.\n\t(_POSIX_THREAD_KEYS_MAX): Define to value specified by POSIX.\n\t(PTHREAD_KEYS_MAX): Redefine [upward] to minimum required by POSIX.\n\t(SEM_NSEMS_MAX): Define to implementation value.\n\t(SEM_VALUE_MAX): Define to implementation value.\n\t(_POSIX_SEM_NSEMS_MAX): Redefine to value specified by POSIX.\n\t(_POSIX_SEM_VALUE_MAX): Redefine to value specified by POSIX.\n\n2005-05-06  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* signal.c (sigwait): Add a cancellation point to this otherwise\n\tno-op.\n\t* sem_init.c (sem_init): Check for and return ERANGE error.\n\t* sem_post.c (sem_post): Likewise.\n\t* sem_post_multiple.c (sem_post_multiple): Likewise.\n\t* manual (directory): Added; see ChangeLog inside.\n\n2005-05-02  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* implement.h (struct pthread_key_t_): Change threadsLock to keyLock\n\tso as not to be confused with the per thread lock 'threadlock';\n\tchange all references to it.\n\t* implement.h (struct ThreadKeyAssoc): Remove lock; add prevKey\n\tand prevThread pointers; re-implemented all routines that use this\n\tstruct. The effect of this is to save one handle per association,\n\twhich could potentially equal the number of keys multiplied by the\n\tnumber of threads, accumulating over time - and to free the\n\tassociation memory as soon as it is no longer referenced by either\n\tthe key or the thread. Previously, the handle and memory were\n\treleased only after BOTH key and thread no longer referenced the\n\tassociation. That is, often no association resources were released\n\tuntil the process itself exited. In addition, at least one race\n\tcondition has been removed - where two threads could attempt to\n\trelease the association resources simultaneously - one via\n\tptw32_callUserDestroyRoutines and the other via\n\tpthread_key_delete.\n\t- thanks to Richard Hughes at Aculab for discovering the problem.\n\t* pthread_key_create.c: See above.\n\t* pthread_key_delete.c: See above.\n\t* pthread_setspecific.c: See above.\n\t* ptw32_callUserDestroyRoutines.c: See above.\n\t* ptw32_tkAssocCreate.c: See above.\n\t* ptw32_tkAssocDestroy.c: See above.\n\n2005-04-27  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* sem_wait.c (ptw32_sem_wait_cleanup): after cancellation re-attempt\n\tto acquire the semaphore to avoid a race with a late sem_post.\n\t* sem_timedwait.c: Modify comments.\n\n2005-04-25  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* ptw32_relmillisecs.c: New module; converts future abstime to \n\tmilliseconds relative to 'now'.\n\t* pthread_mutex_timedlock.c: Use new ptw32_relmillisecs routine in\n\tplace of internal code; remove the NEED_SEM code - this routine is now\n\timplemented for builds that define NEED_SEM (WinCE etc)\n\t* sem_timedwait.c: Likewise; after timeout or cancellation,\n\tre-attempt to acquire the semaphore in case one has been posted since\n\tthe timeout/cancel occurred. Thanks to Stefan Mueller.\n\t* Makefile: Add ptw32_relmillisecs.c module; remove\n\tptw32_{in,de}crease_semaphore.c modules.\n\t* GNUmakefile: Likewise.\n\t* Bmakefile: Likewise.\n\n\t* sem_init.c: Re-write the NEED_SEM code to be consistent with the\n\tnon-NEED_SEM code, but retaining use of an event in place of the w32 sema\n\tfor w32 systems that don't include semaphores (WinCE);\n\tthe NEED_SEM versions of semaphores has been broken for a long time but is\n\tnow fixed and supports all of the same routines as the non-NEED_SEM case.\n\t* sem_destroy.c: Likewise.\n\t* sem_wait.c: Likewise.\n\t* sem_post.c: Likewise.\n\t* sem_post_multple.c: Likewise.\n\t* implement.h: Likewise.\n\t* sem_timedwait.c: Likewise; this routine is now\n\timplemented for builds that define NEED_SEM (WinCE etc).\n\t* sem_trywait.c: Likewise.\n\t* sem_getvalue.c: Likewise.\n\n\t* pthread_once.c: Yet more changes, reverting closer to Gottlob Frege's\n\tfirst design, but retaining cancellation, priority boosting, and adding\n\tpreservation of W32 error codes to make pthread_once transparent to\n\tGetLastError.\n\n2005-04-11  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* pthread_once.c (pthread_once): Added priority boosting to\n\tsolve starvation problem after once_routine cancellation.\n\tSee notes in file.\n\n2005-04-06  Kevin Lussier <Kevin at codegreennetworks.com>\n\n\t* Makefile: Added debug targets for all versions of the library.\n\n2005-04-01  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* GNUmakefile: Add target to build libpthreadGC1.a as a static link\n\tlibrary.\n\t* Makefile: Likewise for pthreadGC1.lib.\n\n2005-04-01  Kevin Lussier <Kevin at codegreennetworks.com>\n\n\t* sem_timedwait.c (sem_timedwait): Increase size of temp variables to\n\tavoid int overflows for large timeout values.\n\t* implement.h (int64_t): Include or define.\n\n2005-03-31   Dimitar Panayotov <develop at mail.bg>^M\n\n\t* pthread.h: Fix conditional defines for static linking.\n\t* sched.h: Liekwise.\n\t* semaphore.h: Likewise.\n\t* dll.c (PTW32_STATIC_LIB): Module is conditionally included\n\tin the build.\n\n2005-03-16  Ross Johnson  <ross at callisto.canberra.edu.au>^M\n\n\t* pthread_setcancelstate.c: Undo the last change.\n\n2005-03-16  Ross Johnson  <ross at callisto.canberra.edu.au>^M\n\n\t* pthread_setcancelstate.c: Don't check for an async cancel event\n\tif the library is using alertable async cancel..\n\n2005-03-14  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* pthread_once.c (pthread_once): Downgrade interlocked operations to simple\n\tmemory operations where these are protected by the critical section; edit\n\tcomments.\n\n2005-03-13  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_once.c (pthread_once): Completely redesigned; a change was\n\trequired to the ABI (pthread_once_t_), and resulting in a version\n\tcompatibility index increment.\n\n\tNOTES:\n\tThe design (based on pseudo code contributed by Gottlob Frege) avoids\n\tcreating a kernel object if there is no contention. See URL for details:-\n\thttp://sources.redhat.com/ml/pthreads-win32/2005/msg00029.html\n\tThis uses late initialisation similar to the technique already used for\n\tpthreads-win32 mutexes and semaphores (from Alexander Terekhov).\n\n\tThe subsequent cancelation cleanup additions (by rpj) could not be implemented\n\twithout sacrificing some of the efficiency in Gottlob's design. In particular,\n\talthough each once_control uses it's own event to block on, a global CS is\n\trequired to manage it - since the event must be either re-usable or\n\tre-creatable under cancelation. This is not needed in the non-cancelable\n\tdesign because it is able to mark the event as closed (forever).\n\n\tWhen uncontested, a CS operation is equivalent to an Interlocked operation\n\tin speed. So, in the final design with cancelability, an uncontested\n\tonce_control operation involves a minimum of five interlocked operations\n\t(including the LeaveCS operation).\n\t\n\tALTERNATIVES:\n\tAn alternative design from Alexander Terekhov proposed using a named mutex,\n\tas sketched below:-\n\n\t  if (!once_control) { // May be in TLS\n\t    named_mutex::guard guard(&once_control2);\n\t      if (!once_control2) {\n\t         <init>\n\t         once_control2 = true;\n\t      }\n\t    once_control = true;\n\t  }\n\t\n\tA more detailed description of this can be found here:-\n\thttp://groups.yahoo.com/group/boost/message/15442\n\n\t[Although the definition of a suitable PTHREAD_ONCE_INIT precludes use of the\n\tTLS located flag, this is not critical.]\n\t\n\tThere are three primary concerns though:-\n\t1) The [named] mutex is 'created' even in the uncontended case.\n\t2) A system wide unique name must be generated.\n\t3) Win32 mutexes are VERY slow even in the uncontended \tcase. An uncontested\n\tWin32 mutex lock operation can be 50 (or more) times slower than an\n\tuncontested EnterCS operation.\n\n\tUltimately, the named mutex trick is making use of the global locks maintained\n\tby the kernel.\n\n\t* pthread.h (pthread_once_t_): One flag and an event HANDLE added.\n\t(PTHREAD_ONCE_INIT): Additional values included.\n\n2005-03-08  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_once.c (pthread_once): Redesigned to elliminate potential\n\tstarvation problem.\n\t- reported by Gottlob Frege  <gottlobfrege at gmail.com>\n\n\t* ptw32_threadDestroy.c (ptw32_threadDestroy): Implicit threads were\n\tnot closing their Win32 thread duplicate handle.\n\t- reported by Dmitrii Semii <bogolt at gmail.com>\n\n2005-01-25  Ralf Kubis  <RKubis at mc.com>\n\n\t* Attempted acquisition of recursive mutex was causing waiting\n\tthreads to not be woken when the mutex is released.\n\n\t* GNUmakefile (GCE): Generate correct version resource comments.\n\n2005-01-01  Konstantin Voronkov  <beowinkle at yahoo.com>\n\n\t* pthread_mutex_lock.c (pthread_mutex_lock): The new atomic exchange\n\tmutex algorithm is known to allow a thread to steal the lock off\n\tFIFO waiting threads. The next waiting FIFO thread gets a spurious\n\twake-up and must attempt to re-acquire the lock. The woken thread\n\twas setting itself as the mutex's owner before the re-acquisition.\n\n2004-11-22  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_cond_wait.c (ptw32_cond_wait_cleanup): Undo change\n\tfrom 2004-11-02.\n\t* Makefile (DLL_VER): Added for DLL naming suffix - see README.\n\t* GNUmakefile (DLL_VER): Likewise.\n\t* Wmakefile (DLL_VER): Likewise.\n\t* Bmakefile (DLL_VER): Likewise.\n\t* pthread.dsw (version.rc): Added to MSVS workspace.\n\n2004-11-20  Boudewijn Dekker  <b.dekker at ellipsis.nl>\n\n\t* pthread_getspecific.c (pthread_getspecific): Check for\n\tinvalid (NULL) key argument.\n\n2004-11-19  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* config.h (PTW32_THREAD_ID_REUSE_INCREMENT): Added to allow\n\tbuilding the library for either unique thread IDs like Solaris\n\tor non-unique thread IDs like Linux; allows application developers\n\tto override the library's default insensitivity to some apps\n\tthat may not be strictly POSIX compliant.\n\t* version.rc: New resource module to encode version information\n\twithin the DLL.\n\t* pthread.h: Added PTW32_VERSION* defines and grouped sections\n\trequired by resource compiler together; bulk of file is skipped\n\tif RC_INVOKED. Defined some error numbers and other names for\n\tBorland compiler.\n\n2004-11-02  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_cond_wait.c (ptw32_cond_wait_cleanup): Lock CV mutex at\n\tstart of cleanup handler rather than at the end.\n\t* implement.h (PTW32_THREAD_REUSE_EMPTY): Renamed from *_BOTTOM.\n\t(ptw32_threadReuseBottom): New global variable.\n\t* global.c (ptw32_threadReuseBottom): Declare new variable.\n\t* ptw32_reuse.c (ptw32_reuse): Change reuse LIFO stack to LILO queue\n\tto more evenly distribute use of reusable thread IDs; use renamed\n\tPTW32_THREAD_REUSE_EMPTY.\n\t* ptw32_processTerminate.c (ptw2_processTerminate): Use renamed\n\tPTW32_THREAD_REUSE_EMPTY.\n\n2004-10-31  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* implement.h (PThreadState): Add new state value\n\t'PThreadStateCancelPending'.\n\t* pthread_testcancel.c (pthread_testcancel): Use new thread\n\t'PThreadStateCancelPending' state as short cut to avoid entering\n\tkernel space via WaitForSingleObject() call. This was obviated\n\tby user space sema acquisition in sem_wait() and sem_timedwait(),\n\twhich are also cancelation points. A call to pthread_testcancel()\n\twas required, which introduced a kernel call, effectively nullifying\n\tany gains made by the user space sem acquisition checks.\n\t* pthread_cancel.c (pthread_cancel): Set new thread\n\t'PThreadStateCancelPending' state.\n\n2004-10-29  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* implement.h (pthread_t): Renamed to ptw32_thread_t; struct contains\n\tall thread state.\n\t* pthread.h (ptw32_handle_t): New general purpose struct to serve\n\tas a handle for various reusable object IDs - currently only used\n\tby pthread_t; contains a pointer to ptw32_thread_t (thread state)\n\tand a general purpose uint for use as a reuse counter or flags etc.\n\t(pthread_t): typedef'ed to ptw32_handle_t; the uint is the reuse\n\tcounter that allows the library to maintain unique POSIX thread IDs.\n\tWhen the pthread struct reuse stack was introduced, threads would\n\toften acquire an identical ID to a previously destroyed thread. The\n\tsame was true for the pre-reuse stack library, by virtue of pthread_t\n\tbeing the address of the thread struct. The new pthread_t retains\n\tthe reuse stack but provides virtually unique thread IDs.\n\t* sem_wait.c (ptw32_sem_wait_cleanup): New routine used for\n\tcancelation cleanup.\n\t* sem_timedwait.c (ptw32_sem_timedwait_cleanup): Likewise.\n\n2004-10-22  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* sem_init.c (sem_init): Introduce a 'lock' element in order to\n\treplace the interlocked operations with conventional serialisation.\n\tThis is needed in order to be able to atomically modify the sema\n\tvalue and perform Win32 sema release operations. Win32 semaphores are\n\tused instead of events in order to support efficient multiple posting.\n\tIf the whole modify/release isn't atomic, a race between\n\tsem_timedwait() and sem_post() could result in a release when there is\n\tno waiting semaphore, which would cause too many threads to proceed.\n\t* sem_wait.c (sem_wait): Use new 'lock'element.\n\t* sem_timedwait.c (sem_timedwait): Likewise.\n\t* sem_trywait.c (sem_trywait): Likewise.\n\t* sem_post.c (sem_post): Likewise.\n\t* sem_post_multiple.c (sem_post_multiple): Likewise.\n\t* sem_getvalue.c (sem_getvalue): Likewise.\n\t* ptw32_semwait.c (ptw32_semwait): Likewise.\n\t* sem_destroy.c (sem_destroy): Likewise; also tightened the conditions\n\tfor semaphore destruction; in particular, a semaphore will not be\n\tdestroyed if it has waiters.\n\t* sem_timedwait.c (sem_timedwait): Added cancel cleanup handler to\n\trestore sema value when cancelled.\n\t* sem_wait.c (sem_wait): Likewise.\n\n2004-10-21  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_mutex_unlock.c (pthread_mutex_unlock): Must use PulseEvent()\n\trather than SetEvent() to reset the event if there are no waiters.\n\n2004-10-19  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* sem_init.c (sem_init): New semaphore model based on the same idea\n\tas mutexes, i.e. user space interlocked check to avoid \n\tunnecessarily entering kernel space. Wraps the Win32 semaphore and\n\tkeeps it's own counter. Although the motivation to do this has existed\n\tfor a long time, credit goes to Alexander Terekhov for providing\n\tthe logic. I have deviated slightly from AT's logic to add the waiters\n\tcount, which has made the code more complicated by adding cancelation\n\tcleanup. This also appears to have broken the VCE (C++ EH) version of\n\tthe library (the same problem as previously reported - see BUGS #2),\n\tonly apparently not fixable using the usual workaround, nor by turning\n\tall optimisation off. The GCE version works fine, so it is presumed to\n\tbe a bug in MSVC++ 6.0. The cancelation exception is thrown and caught\n\tcorrectly, but the cleanup class destructor is never called. The failing\n\ttest is tests\\semaphore4.c.\n\t* sem_wait.c (sem_wait): Implemented user space check model.\n\t* sem_post.c (sem_post): Likewise.\n\t* sem_trywait.c (sem_trywait): Likewise.\n\t* sem_timedwait.c (sem_timedwait): Likewise.\n\t* sem_post_multiple.c (sem_post_multiple): Likewise.\n\t* sem_getvalue.c (sem_getvalue): Likewise.\n\t* ptw32_semwait.c (ptw32_semwait): Likewise.\n\t* implement.h (sem_t_): Add counter element.\n\n2004-10-15  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* implement.h (pthread_mutex_t_): Use an event in place of\n\tthe POSIX semaphore.\n\t* pthread_mutex_init.c: Create the event; remove semaphore init.\n\t* pthread_mutex_destroy.c: Delete the event.\n\t* pthread_mutex_lock.c: Replace the semaphore wait with the event wait.\n\t* pthread_mutex_trylock.c: Likewise.\n\t* pthread_mutex_timedlock.c: Likewise.\n\t* pthread_mutex_unlock.c: Set the event.\n\t\n2004-10-14  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_mutex_lock.c (pthread_mutex_lock): New algorithm using\n\tTerekhov's xchg based variation of Drepper's cmpxchg model.\n\tTheoretically, xchg uses fewer clock cycles than cmpxchg (using IA-32\n\tas a reference), however, in my opinion bus locking dominates the\n\tequation on smp systems, so the model with the least number of bus\n\tlock operations in the execution path should win, which is Terekhov's\n\tvariant. On IA-32 uni-processor systems, it's faster to use the\n\tCMPXCHG instruction without locking the bus than to use the XCHG\n\tinstruction, which always locks the bus. This makes the two variants\n\tequal for the non-contended lock (fast lane) execution path on up\n\tIA-32. Testing shows that the xchg variant is faster on up IA-32 as\n\twell if the test forces higher lock contention frequency, even though\n\tkernel calls should be dominating the times (on up IA-32, both\n\tvariants used CMPXCHG instructions and neither locked the bus).\n\t* pthread_mutex_timedlock.c pthread_mutex_timedlock(): Similarly.\n\t* pthread_mutex_trylock.c (pthread_mutex_trylock): Similarly.\n\t* pthread_mutex_unlock.c (pthread_mutex_unlock): Similarly.\n\t* ptw32_InterlockedCompareExchange.c (ptw32_InterlockExchange): New\n\tfunction.\n\t(PTW32_INTERLOCKED_EXCHANGE): Sets up macro to use inlined\n\tptw32_InterlockedExchange.\n\t* implement.h (PTW32_INTERLOCKED_EXCHANGE): Set default to\n\tInterlockedExchange().\n\t* Makefile: Building using /Ob2 so that asm sections within inline\n\tfunctions are inlined.\n\n2004-10-08  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_mutex_destroy.c (pthread_mutex_destroy): Critical Section\n\telement is no longer required.\n\t* pthread_mutex_init.c (pthread_mutex_init): Likewise.\n\t* pthread_mutex_lock.c (pthread_mutex_lock): New algorithm following\n\tDrepper's paper at http://people.redhat.com/drepper/futex.pdf, but\n\tusing the existing semaphore in place of the futex described in the\n\tpaper. Idea suggested by Alexander Terekhov - see:\n\thttp://sources.redhat.com/ml/pthreads-win32/2003/msg00108.html\n\t* pthread_mutex_timedlock.c pthread_mutex_timedlock(): Similarly.\n\t* pthread_mutex_trylock.c (pthread_mutex_trylock): Similarly.\n\t* pthread_mutex_unlock.c (pthread_mutex_unlock): Similarly.\n\t* pthread_barrier_wait.c (pthread_barrier_wait): Use inlined version\n\tof InterlockedCompareExchange() if possible - determined at\n\tbuild-time.\n\t* pthread_spin_destroy.c pthread_spin_destroy(): Likewise.\n\t* pthread_spin_lock.c pthread_spin_lock():Likewise.\n\t* pthread_spin_trylock.c (pthread_spin_trylock):Likewise.\n\t* pthread_spin_unlock.c (pthread_spin_unlock):Likewise.\n\t* ptw32_InterlockedCompareExchange.c: Sets up macro for inlined use.\n\t* implement.h (pthread_mutex_t_): Remove Critical Section element.\n\t(PTW32_INTERLOCKED_COMPARE_EXCHANGE): Set to default non-inlined\n\tversion of InterlockedCompareExchange().\n\t* private.c: Include ptw32_InterlockedCompareExchange.c first for\n\tinlining.\n\t* GNUmakefile: Add commandline option to use inlined\n\tInterlockedCompareExchange().\n\t* Makefile: Likewise.\n\n2004-09-27  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_mutex_lock.c (pthread_mutex_lock): Separate\n\tPTHREAD_MUTEX_NORMAL logic since we do not need to keep or check some\n\tstate required by other mutex types; do not check mutex pointer arg\n\tfor validity - leave this to the system since we are only checking\n\tfor NULL pointers. This should improve speed of NORMAL mutexes and\n\tmarginally improve speed of other type.\n\t* pthread_mutex_trylock.c (pthread_mutex_trylock): Likewise.\n\t* pthread_mutex_unlock.c (pthread_mutex_unlock): Likewise; also avoid\n\tentering the critical section for the no-waiters case, with approx.\n\t30% reduction in lock/unlock overhead for this case.\n\t* pthread_mutex_timedlock.c (pthread_mutex_timedlock): Likewise; also\n\tno longer keeps mutex if post-timeout second attempt succeeds - this\n\twill assist applications that wish to impose strict lock deadlines,\n\trather than simply to escape from frozen locks.\n\n2004-09-09  Tristan Savatier  <tristan at mpegtv.com>\n\t* pthread.h (struct pthread_once_t_): Qualify the 'done' element\n\tas 'volatile'.\n\t* pthread_once.c: Concerned about possible race condition,\n\tspecifically on MPU systems re concurrent access to multibyte types.\n\t[Maintainer's note: the race condition is harmless on SPU systems\n\tand only a problem on MPU systems if concurrent access results in an\n\texception (presumably generated by a hardware interrupt). There are\n\tother instances of similar harmless race conditions that have not\n\tbeen identified as issues.]\n\n2004-09-09  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread.h: Declare additional types as volatile.\n\n2004-08-27  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_barrier_wait.c (pthread_barrier_wait): Remove excessive code\n\tby substituting the internal non-cancelable version of sem_wait\n\t(ptw32_semwait).\n\n2004-08-25  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_join.c (pthread_join): Rewrite and re-order the conditional\n\ttests in an attempt to improve efficiency and remove a race\n\tcondition.\n\n2004-08-23  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* create.c (pthread_create): Don't create a thread if the thread\n\tid pointer location (first arg) is inaccessible. A memory\n\tprotection fault will result if the thread id arg isn't an accessible\n\tlocation. This is consistent with GNU/Linux but different to\n\tSolaris or MKS (and possibly others), which accept NULL as meaning\n\t'don't return the created thread's ID'. Applications that run\n\tusing pthreads-win32 will run on all other POSIX threads\n\timplementations, at least w.r.t. this feature.\n\n\tIt was decided not to copy the Solaris et al behaviour because,\n\talthough it would have simplified some application porting (but only\n\tfrom Solaris to Windows), the feature is not technically necessary,\n\tand the alternative segfault behaviour helps avoid buggy application\n\tcode.\n\n2004-07-01  Anuj Goyal  <anuj.goyal at gmail.com>\n\n\t* builddmc.bat: New; Windows bat file to build the library.\n\t* config.h (__DMC__): Support for Digital Mars compiler.\n\t* create.c (__DMC__): Likewise.\n\t* pthread_exit.c (__DMC__): Likewise.\n\t* pthread_join.c (__DMC__): Likewise.\n\t* ptw32_threadDestroy.c (__DMC__): Likewise.\n\t* ptw32_threadStart.c (__DMC__): Likewise.\n\t* ptw32_throw.c (__DMC__): Likewise.\n\n2004-06-29  Anuj Goyal  <anuj.goyal at gmail.com>\n\n\t* pthread.h (__DMC__): Initial support for Digital Mars compiler.\n\n2004-06-29  Will Bryant  <will.bryant at ecosm.com>\n\n\t* README.Borland: New; description of Borland changes.\n\t* Bmakefile: New makefile for the Borland make utility.\n\t* ptw32_InterlockedCompareExchange.c:\n\tAdd Borland compatible asm code.\n\n2004-06-26  Jason Bard  <BardJA at Npt.NUWC.Navy.Mil>\n\n\t* pthread.h (HAVE_STRUCT_TIMESPEC): If undefined, define it\n\tto avoid timespec struct redefined errors elsewhere in an\n\tapplication.\n\n2004-06-21  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread.h (PTHREAD_RECURSIVE_MUTEX_INITIALIZER): Mutex\n\tinitialiser added for compatibility with Linux threads and\n\tothers; currently not included in SUSV3.\n\t* pthread.h (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER): Likewise.\n\t* pthread.h (PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP): Likewise.\n\t* pthread.h (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP): Likewise.\n\n\t* ptw32_mutex_check_need_init.c (ptw32_mutex_check_need_init): \n\tAdd new initialisers.\n\n\t* pthread_mutex_lock.c (pthread_mutex_lock): Check for new\n\tinitialisers.\n\t* pthread_mutex_trylock.c (pthread_mutex_trylock): Likewise.\n\t* pthread_mutex_timedlock.c (pthread_mutex_timedlock): Likewise.\n\t* pthread_mutex_unlock.c (pthread_mutex_unlock): Likewise.\n\t* pthread_mutex_destroy.c (pthread_mutex_destroy): Likewise.\n\n2004-05-20  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* README.NONPORTABLE: Document pthread_win32_test_features_np().\n\t* FAQ: Update various answers.\n\n2004-05-19  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* Makefile: Don't define _WIN32_WINNT on compiler command line.\n\t* GNUmakefile: Likewise.\n\n2004-05-16  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_cancel.c (pthread_cancel): Adapted to use auto-detected\n\tQueueUserAPCEx features at run-time.\n\t(ptw32_RegisterCancelation): Drop in replacement for QueueUserAPCEx()\n\tif it can't be used. Provides older style non-preemptive async\n\tcancelation.\n\t* pthread_win32_attach_detach_np.c (pthread_win32_attach_np):\n\tAuto-detect quserex.dll and the availability of alertdrv.sys;\n\tinitialise and close on process attach/detach.\n\t* global.c (ptw32_register_cancelation): Pointer to either\n\tQueueUserAPCEx() or ptw32_RegisterCancelation() depending on\n\tavailability. QueueUserAPCEx makes pre-emptive async cancelation\n\tpossible.\n\t* implement.h: Add definitions and prototypes related to QueueUserAPC.\n\n2004-05-16  Panagiotis E. Hadjidoukas <peh at hpclab.ceid.upatras.gr>\n\n\t* QueueUserAPCEx (separate contributed package): Provides preemptive\n\tAPC feature.\n\t* pthread_cancel.c (pthread_cancel): Initial integration of\n\tQueueUserAPCEx into pthreads-win32 to provide true pre-emptive\n\tasync cancelation of threads, including blocked threads.\n\n2004-05-06  Makoto Kato  <raven at oldskool.jp>\n\n\t* pthread.h (DWORD_PTR): Define typedef for older MSVC.\n\t* pthread_cancel.c (AMD64): Add architecture specific Context register.\n\t* ptw32_getprocessors.c: Use correct types (DWORD_PTR) for mask\n\tvariables.\n\n2004-04-06  P. van Bruggen  <pietvb at newbridges.nl>\n\n\t* ptw32_threadDestroy.c: Destroy threadLock mutex to\n\tclose a memory leak.\n\n2004-02-13  Gustav Hallberg  <gustav at virtutech.com>\n\n\t* pthread_equal.c: Remove redundant equality logic.\n\n2003-12-10  Philippe Di Cristo  <philipped at voicebox.com>\n\n\t* sem_timedwait.c (sem_timedwait): Fix timeout calculations.\n\n2003-10-20  Alexander Terekhov  <TEREKHOV at de.ibm.com>\n\n\t* pthread_mutex_timedlock.c (ptw32_semwait): Move to individual module.\n\t* ptw32_semwait.c: New module.\n\t* pthread_cond_wait.c (ptw32_cond_wait_cleanup): Replace cancelable\n\tsem_wait() call with non-cancelable ptw32_semwait() call.\n\t* pthread.c (private.c): Re-order for inlining. GNU C warned that\n\tfunction ptw32_semwait() was defined 'inline' after it was called.\n\t* pthread_cond_signal.c (ptw32_cond_unblock): Likewise.\n\t* pthread_delay_np.c: Disable Watcom warning with comment.\n\t* *.c (process.h): Remove include from .c files. This is conditionally\n\tincluded by the common project include files.\n\n2003-10-20  James Ewing  <james.ewing at sveasoft.com>\n\n\t* ptw32_getprocessors.c: Some Win32 environments don't have\n\tGetProcessAffinityMask(), so always return CPU count = 1 for them.\n\t* config.h (NEED_PROCESSOR_AFFINITY_MASK): Define for WinCE.\n\t\n2003-10-15  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* Re-indented all .c files using default GNU style to remove assorted\n\teditor ugliness (used GNU indent utility in default style).\n\n2003-10-15  Alex Blanco  <Alex.Blanco at motorola.com>\n\n\t* sem_init.c (sem_init): Would call CreateSemaphore even if the sema\n\tstruct calloc failed; was not freeing calloced memory if either\n\tCreateSemaphore or CreateEvent failed.\n\n2003-10-14  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* pthread.h: Add Watcom compiler compatibility. Esssentially just add\n\tthe cdecl attribute to all exposed function prototypes so that Watcom\n\tgenerates function call code compatible with non-Watcom built libraries.\n\tBy default, Watcom uses registers to pass function args if possible rather\n\tthan pushing to stack.\n\t* semaphore.h: Likewise.\n\t* sched.h: Likewise.\n\t* pthread_cond_wait.c (ptw32_cond_wait_cleanup): Define with cdecl attribute\n\tfor Watcom compatibility. This routine is called via pthread_cleanup_push so\n\tit had to match function arg definition.\n\t* Wmakefile: New makefile for Watcom builds.\n\n2003-09-14  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_setschedparam.c (pthread_setschedparam): Attempt to map\n\tall priority levels between max and min (as returned by\n\tsched_get_priority_min/max) to reasonable Win32 priority levels - i.e.\n\tlevels between THREAD_PRIORITY_LOWEST/IDLE to THREAD_PRIORITY_LOWEST and\n\tbetween THREAD_PRIORITY_HIGHEST/TIME_CRITICAL to THREAD_PRIORITY_HIGHEST\n\twhile others remain unchanged; record specified thread priority level\n\tfor return by pthread_getschedparam.\n\n\tNote that, previously, specified levels not matching Win32 priority levels\n\twould silently leave the current thread priority unaltered.\n\n\t* pthread_getschedparam.c (pthread_getschedparam): Return the priority\n\tlevel specified by the latest pthread_setschedparam or pthread_create rather\n\tthan the actual running thread priority as returned by GetThreadPriority - as\n\trequired by POSIX. I.e. temporary or adjusted actual priority levels are not\n\treturned by this routine.\n\n\t* pthread_create.c (pthread_create): For priority levels specified via\n\tpthread attributes, attempt to map all priority levels between max and\n\tmin (as returned by sched_get_priority_min/max) to reasonable Win32\n\tpriority levels; record priority level given via attributes, or\n\tinherited from parent thread, for later return by pthread_getschedparam.\n\n\t* ptw32_new.c (ptw32_new): Initialise pthread_t_ sched_priority element.\n\n\t* pthread_self.c (pthread_self): Set newly created implicit POSIX thread\n\tsched_priority to Win32 thread's current actual priority. Temporarily\n\taltered priorities can't be avoided in this case.\n\n\t* implement.h (struct pthread_t_): Add new sched_priority element.\n\n2003-09-12  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* sched_get_priority_min.c (sched_get_priority_min): On error should return -1\n\twith errno set.\n\t* sched_get_priority_max.c (sched_get_priority_max): Likewise.\n\n2003-09-03  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* w32_cancelableWait.c (ptw32_cancelable_wait): Allow cancelation\n\tof implicit POSIX threads as well.\n\n2003-09-02  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_win32_attach_detach_np.c (pthread_win32_thread_detach_np):\n\tAdd comment.\n\n\t* pthread_exit.c (pthread_exit): Fix to recycle the POSIX thread handle in\n\taddition to calling user TSD destructors. Move the implicit POSIX thread exit\n\thandling to ptw32_throw to centralise the logic.\n\n\t* ptw32_throw.c (ptw32_throw): Implicit POSIX threads have no point\n\tto jump or throw to, so cleanup and exit the thread here in this case. For\n\tprocesses using the C runtime, the exit code will be set to the POSIX\n\treason for the throw (i.e. PTHREAD_CANCEL or the value given to pthread_exit).\n\tNote that pthread_exit() already had similar logic, which has been moved to\n\there.\n\n\t* ptw32_threadDestroy.c (ptw32_threadDestroy): Don't close the Win32 handle\n\tof implicit POSIX threads - expect this to be done by Win32?\n\n2003-09-01  Ross Johnson  <rpj at callisto.canberra.edu.au>\n\n\t* pthread_self.c (pthread_self): The newly aquired pthread_t must be\n\tassigned to the reuse stack, not freed, if the routine fails somehow.\n\n2003-08-13  Ross Johnson  <rpj at ise.canberra.edu.au>\n\n\t* pthread_getschedparam.c (pthread_getschedparam): An invalid thread ID\n\tparameter was returning an incorrect error value; now uses a more exhaustive\n\tcheck for validity.\n\n\t* pthread_setschedparam.c (pthread_setschedparam): Likewise.\n\n\t* pthread_join.c (pthread_join): Now uses a more exhaustive\n\tcheck for validity.\n\n\t* pthread_detach.c (pthread_detach): Likewise.\n\n\t* pthread_cancel.c (pthread_cancel): Likewise.\n\n\t* ptw32_threadDestroy.c (ptw32_threadDestroy): pthread_t structs are\n\tnever freed - push them onto a stack for reuse.\n\n\t* ptw32_new.c (ptw32_new): Check for reusable pthread_t before dynamically\n\tallocating new memory for the struct.\n\n\t* pthread_kill.c (pthread_kill): New file; new routine; takes only a zero\n\tsignal arg so that applications can check the thread arg for validity; checks\n\tthat the underlying Win32 thread HANDLE is valid.\n\n\t* pthread.h (pthread_kill): Add prototype.\n\n\t* ptw32_reuse.c (ptw32_threadReusePop): New file; new routine; pop a\n\tpthread_t off the reuse stack. pthread_t_ structs that have been destroyed, i.e.\n\thave exited detached or have been joined, are cleaned up and put onto a reuse\n\tstack. Consequently, thread IDs are no longer freed once calloced. The library\n\twill attempt to get a struct off this stack before asking the system to alloc\n\tnew memory when creating threads. The stack is guarded by a global mutex.\n\t(ptw32_threadReusePush): New routine; push a pthread_t onto the reuse stack.\n\n\t* implement.h (ptw32_threadReusePush): Add new prototype.\n\t(ptw32_threadReusePop): Likewise.\n\t(pthread_t): Add new element.\n\n\t* ptw32_processTerminate.c (ptw32_processTerminate): Delete the thread\n\treuse lock; free all thread ID structs on the thread reuse stack.\n\n\t* ptw32_processInitialize.c (ptw32_processInitialize): Initialise the\n\tthread reuse lock.\n\n2003-07-19  Ross Johnson  <rpj at ise.canberra.edu.au>\n\n\t* GNUmakefile: modified to work under MsysDTK environment.\n\t* pthread_spin_lock.c (pthread_spin_lock): Check for NULL arg.\n\t* pthread_spin_unlock.c (pthread_spin_unlock): Likewise.\n\t* pthread_spin_trylock.c (pthread_spin_trylock): Likewise;\n\tfix incorrect pointer value if lock is dynamically initialised by\n\tthis function.\n\t* sem_init.c (sem_init): Initialise sem_t value to quell compiler warning.\n\t* sem_destroy.c (sem_destroy): Likewise.\n\t* ptw32_threadStart.c (non-MSVC code sections): Include <exception> rather\n\tthan old-style <new.h>; fix all std:: namespace entities such as\n\tstd::terminate_handler instances and associated methods.\n\t* ptw32_callUserDestroyRoutines.c (non-MSVC code sections): Likewise.\n\n2003-06-24  Piet van Bruggen  <pietvb at newbridges.nl>\n\n\t* pthread_spin_destroy.c (pthread_spin_destroy): Was not freeing the\n\tspinlock struct.\n\n2003-06-22  Nicolas Barry  <boozai at yahoo.com>\n\n\t* pthread_mutex_destroy.c (pthread_mutex_destroy): When called\n\twith a recursive mutex that was locked by the current thread, the\n\tfunction was failing with a success return code.\n\n2003-05-15  Steven Reddie  <Steven.Reddie at ca.com>\n\n\t* pthread_win32_attach_detach_np.c (pthread_win32_process_detach_np):\n\tNULLify ptw32_selfThreadKey after the thread is destroyed, otherwise\n\tdestructors calling pthreads routines might resurrect it again, creating\n\tmemory leaks. Call the underlying Win32 Tls routine directly rather than\n\tpthread_setspecific().\n\t(pthread_win32_thread_detach_np): Likewise.\n\n2003-05-14  Viv  <vcotirlea at hotmail.com>\n\n\t* pthread.dsp: Change /MT compile flag to /MD.\n\n2003-03-04  Alexander Terekhov  <TEREKHOV at de.ibm.com>\n\n\t* pthread_mutex_timedlock.c (pthread_mutex_timedlock): Fix failure to\n\tset ownership of mutex on second grab after abstime timeout.\n\t- bug reported by Robert Strycek <strycek at posam.sk>\n\n2002-12-17  Thomas Pfaff  <tpfaff at gmx.net>\n\n\t* pthread_mutex_lock.c (ptw32_semwait): New static routine to provide\n\ta non-cancelable sem_wait() function. This is consistent with the\n\tway that pthread_mutex_timedlock.c does it.\n\t(pthread_mutex_lock): Use ptw32_semwait() instead of sem_wait().\n\n2002-12-11  Thomas Pfaff  <tpfaff at gmx.net>\n\n\t* pthread_mutex_trylock.c: Should return EBUSY rather than EDEADLK.\n\t* pthread_mutex_destroy.c: Remove redundant ownership test (the\n\ttrylock call does this for us); do not destroy a recursively locked\n\tmutex.\n\n2002-09-20  Michael Johnson  <michaelj at maine.rr.com>\n\n\t* pthread_cond_destroy.c (pthread_cond_destroy): \n\tWhen two different threads exist, and one is attempting to\n\tdestroy a condition variable while the other is attempting to\n\tinitialize a condition variable that was created with\n\tPTHREAD_COND_INITIALIZER, a deadlock can occur. Shrink\n\tthe ptw32_cond_list_lock critical section to fix it.\n\n2002-07-31  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* ptw32_threadStart.c (ptw32_threadStart): Thread cancelLock\n\tdestruction moved to ptw32_threadDestroy().\n\n\t* ptw32_threadDestroy.c (ptw32_threadDestroy):  Destroy\n\tthe thread's cancelLock. Moved here from ptw32_threadStart.c\n\tto cleanup implicit threads as well.\n\n2002-07-30  Alexander Terekhov  <TEREKHOV at de.ibm.com>\n\n\t* pthread_cond_wait.c (ptw32_cond_wait_cleanup): \n\tRemove code designed to avoid/prevent spurious wakeup\n\tproblems. It is believed that the sem_timedwait() call\n\tis consuming a CV signal that it shouldn't and this is\n\tbreaking the avoidance logic.\n\n2002-07-30  Ross Johnson  <rpj at ise.canberra.edu.au>\n\n\t* sem_timedwait.c (sem_timedwait): Tighten checks for\n\tunreasonable abstime values - that would result in\n\tunexpected timeout values.\n\n\t* w32_CancelableWait.c (ptw32_cancelable_wait):\n\tTighten up return value checking and add comments.\n\n\n2002-06-08  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* sem_getvalue.c (sem_getvalue): Now returns a value for the\n\tNEED_SEM version (i.e. earlier versions of WinCE).\n\n\n2002-06-04  Rob Fanner  <rfanner at stonethree.com>\n\n\t* sem_getvalue.c (sem_getvalue): The Johnson M. Hart\n\tapproach didn't work - we are forced to take an\n\tintrusive approach. We try to decrement the sema\n\tand then immediately release it again to get the\n\tvalue. There is a small probability that this may\n\tblock other threads, but only momentarily.\n\n2002-06-03  Ross Johnson  <rpj at ise.canberra.edu.au>\n\n\t* sem_init.c (sem_init): Initialise Win32 semaphores\n\tto _POSIX_SEM_VALUE_MAX (which this implementation\n\tdefines in pthread.h) so that sem_getvalue() can use\n\tthe trick described in the comments in sem_getvalue().\n\t* pthread.h (_POSIX_SEM_VALUE_MAX): Defined.\n\t(_POSIX_SEM_NSEMS_MAX): Defined - not used but may be\n\tuseful for source code portability.\n\n2002-06-03  Rob Fanner  <rfanner at stonethree.com>\n\n\t* sem_getvalue.c (sem_getvalue): Did not work on NT.\n\tUse approach suggested by Johnson M. Hart in his book\n\t\"Win32 System Programming\".\n\n2002-02-28  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* errno.c: Compiler directive was incorrectly including code.\n\t* pthread.h: Conditionally added some #defines from config.h\n\tneeded when not building the library. e.g. NEED_ERRNO, NEED_SEM.\n\t(PTW32_DLLPORT): Now only defined if _DLL defined.\n\t(_errno): Compiler directive was incorrectly including prototype.\n\t* sched.h: Conditionally added some #defines from config.h\n\tneeded when not building the library.\n\t* semaphore.h: Replace an instance of NEED_SEM that should\n\thave been NEED_ERRNO. This change currently has nil effect.\n\n\t* GNUmakefile: Correct some recent changes.\n\n\t* Makefile: Add rule to generate pre-processor output.\n\n2002-02-23  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* pthread_rwlock_timedrdlock.c: New - untested.\n\t* pthread_rwlock_timedwrlock.c: New - untested.\n\t\n\t* Testsuite passed (except known MSVC++ problems)\n\n\t* pthread_cond_destroy.c: Expand the time change\n\tcritical section to solve deadlock problem.\n\n\t* pthread.c: Add all remaining C modules.\n\t* pthread.h: Use dllexport/dllimport attributes on functions\n\tto avoid using pthread.def.\n\t* sched.h: Likewise.\n\t* semaphore.h: Likewise.\n\t* GNUmakefile: Add new targets for single translation\n\tunit build to maximise inlining potential; generate\n\tpthread.def automatically.\n\t* Makefile: Likewise, but no longer uses pthread.def.\n\n2002-02-20  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* pthread_cond_destroy.c (pthread_cond_destroy):\n\tEnter the time change critical section earlier.\n\n2002-02-17  Ross Johnson  <rpj at setup1.ise.canberra.edu.au\n\n\t* Testsuite passed.\n\n\t* pthread_timechange_handler_np.c: New; following\n\ta suggestion from Alexander Terekhov that CVs should\n\tbe broadcast so that they all re-evaluate their\n\tcondition variables and reset a new timeout if\n\trequired, whenever an application receives a\n\tWM_TIMECHANGE message. This message indicates that\n\tthe system time has been changed. Therefore, CVs\n\twaiting for a timeout set as an abs_time will possibly\n\tnot wake up at the expected time. Some applications\n\tmay not be tolerant of this.\n\t* pthread_cond_init.c: Add CV to linked list.\n\t* pthread_cond_destroy.c: Remove CV from linked list.\n\t* global.c (ptw32_cond_list_head): New variable.\n\t(ptw32_cond_list_tail): New variable.\n\t(ptw32_cond_list_cs): New critical section.\n\t* ptw32_processInitialize (ptw32_cond_list_cs): Initialize.\n\t* ptw32_processTerminate (ptw32_cond_list_cs): Delete.\n\n\n\t* Reduce executable size.\n\t  -----------------------\n\tWhen linking with the static library, only those\n\troutines actually called, either directly or indirectly\n\tshould be included.\n\n\t[Gcc has the -ffunction-segments option to do this but MSVC\n\tdoesn't have this feature as far as I can determine. Other\n\tcompilers are undetermined as well. - rpj]\n\n\t* spin.c: Split file into function segments.\n\t* ptw32_spinlock_check_need_init.c: Separated routine from spin.c.\n\t* pthread_spin_init.c: Likewise.\n\t* pthread_spin_destroy.c: Likewise.\n\t* pthread_spin_lock.c: Likewise.\n\t* pthread_spin_unlock.c: Likewise.\n\t* pthread_spin_trylock.c: Likewise.\n\n\t* sync.c: Split file into function segments.\n\t* pthread_detach.c: Separated routine from sync.c.\n\t* pthread_join.c: Likewise.\n\n\t* tsd.c: Split file into function segments.\n\t* pthread_key_create.c: Separated routine from tsd.c.\n\t* pthread_key_delete.c: Likewise.\n\t* pthread_setspecific.c: Likewise.\n\t* pthread_getspecific.c: Likewise.\n\n\t* sched.c: Split file into function segments.\n\t* pthread_attr_setschedpolicy.c: Separated routine from sched.c.\n\t* pthread_attr_getschedpolicy.c: Likewise.\n\t* pthread_attr_setschedparam.c: Likewise.\n\t* pthread_attr_getschedparam.c: Likewise.\n\t* pthread_attr_setinheritsched.c: Likewise.\n\t* pthread_attr_getinheritsched.c: Likewise.\n\t* pthread_setschedparam.c: Likewise.\n\t* pthread_getschedparam.c: Likewise.\n\t* sched_get_priority_max.c: Likewise.\n\t* sched_get_priority_min.c: Likewise.\n\t* sched_setscheduler.c: Likewise.\n\t* sched_getscheduler.c: Likewise.\n\t* sched_yield.c: Likewise.\n\n\n2002-02-16  Ross Johnson  <rpj at setup1.ise.canberra.edu.au\n\n\tReduce executable size.\n\t-----------------------\n\tWhen linking with the static library, only those\n\troutines actually called, either directly or indirectly\n\tshould be included.\n\n\t[Gcc has the -ffunction-segments option to do this but MSVC\n\tdoesn't have this feature as far as I can determine. Other\n\tcompilers are undetermined as well. - rpj]\n\n\t* mutex.c: Split file into function segments.\n\t* pthread_mutexattr_destroy.c: Separated routine from mutex.c\n\t* pthread_mutexattr_getpshared.c: Likewise.\n\t* pthread_mutexattr_gettype.c: Likewise.\n\t* pthread_mutexattr_init.c: Likewise.\n\t* pthread_mutexattr_setpshared.c: Likewise.\n\t* pthread_mutexattr_settype.c: Likewise.\n\t* ptw32_mutex_check_need_init.c: Likewise.\n\t* pthread_mutex_destroy.c: Likewise.\n\t* pthread_mutex_init.c: Likewise.\n\t* pthread_mutex_lock.c: Likewise.\n\t* pthread_mutex_timedlock.c: Likewise.\n\t* pthread_mutex_trylock.c: Likewise.\n\t* pthread_mutex_unlock.c: Likewise.\n\t\n\t* private.c: Split file into function segments.\n\t* ptw32_InterlockedCompareExchange.c: Separated routine from private.c\n\t* ptw32_callUserDestroyRoutines.c: Likewise.\n\t* ptw32_getprocessors.c: Likewise.\n\t* ptw32_processInitialize.c: Likewise.\n\t* ptw32_processTerminate.c: Likewise.\n\t* ptw32_threadDestroy.c: Likewise.\n\t* ptw32_threadStart.c: Likewise.\n\t* ptw32_throw.c: Likewise.\n\t* ptw32_timespec.c: Likewise.\n\t* ptw32_tkAssocCreate.c: Likewise.\n\t* ptw32_tkAssocDestroy.c: Likewise.\n\n\t* rwlock.c: Split file into function segments.\n\t* pthread_rwlockattr_destroy.c: Separated routine from rwlock.c\n\t* pthread_rwlockattr_getpshared.c: Likewise.\n\t* pthread_rwlockattr_init.c: Likewise.\n\t* pthread_rwlockattr_setpshared.c: Likewise.\n\t* ptw32_rwlock_check_need_init.c: Likewise.\n\t* pthread_rwlock_destroy.c: Likewise.\n\t* pthread_rwlock_init.c: Likewise.\n\t* pthread_rwlock_rdlock.c: Likewise.\n\t* pthread_rwlock_tryrdlock.c: Likewise.\n\t* pthread_rwlock_trywrlock.c: Likewise.\n\t* pthread_rwlock_unlock.c: Likewise.\n\t* pthread_rwlock_wrlock.c: Likewise.\n\n2002-02-10  Ross Johnson  <rpj at setup1.ise.canberra.edu.au\n\n\tReduce executable size.\n\t-----------------------\n\tWhen linking with the static library, only those\n\troutines actually called, either directly or indirectly\n\tshould be included.\n\n\t[Gcc has the -ffunction-segments option to do this but MSVC\n\tdoesn't have this feature as far as I can determine. Other\n\tcompilers are undetermined as well. - rpj]\n\n\t* nonportable.c: Split file into function segments.\n\t* np_delay.c: Separated routine from nonportable.c\n\t* np_getw32threadhandle.c: Likewise.\n\t* np_mutexattr_setkind.c: Likewise.\n\t* np_mutexattr_getkind.c: Likewise.\n\t* np_num_processors.c: Likewise.\n\t* np_win32_attach_detach.c: Likewise.\n\n\t* misc.c: Split file into function segments.\n\t* pthread_equal.c: Separated routine from nonportable.c.\n\t* pthread_getconcurrency.c: Likewise.\n\t* pthread_once.c: Likewise.\n\t* pthread_self.c: Likewise.\n\t* pthread_setconcurrency.c: Likewise.\n\t* ptw32_calloc.c: Likewise.\n\t* ptw32_new.c: Likewise.\n\t* w32_CancelableWait.c: Likewise.\n\t\n2002-02-09  Ross Johnson  <rpj at setup1.ise.canberra.edu.au\n\n\tReduce executable size.\n\t-----------------------\n\tWhen linking with the static library, only those\n\troutines actually called, either directly or indirectly\n\tshould be included.\n\n\t[Gcc has the -ffunction-segments option to do this but MSVC\n\tdoesn't have this feature as far as I can determine. Other\n\tcompilers are undetermined as well. - rpj]\n\n\t* condvar.c: Split file into function segments.\n\t* pthread_condattr_destroy.c: Separated routine from condvar.c.\n\t* pthread_condattr_getpshared.c: Likewise.\n\t* pthread_condattr_init.c: Likewise.\n\t* pthread_condattr_setpshared.c: Likewise.\n\t* ptw32_cond_check_need_init.c: Likewise.\n\t* pthread_cond_destroy.c: Likewise.\n\t* pthread_cond_init.c: Likewise.\n\t* pthread_cond_signal.c: Likewise.\n\t* pthread_cond_wait.c: Likewise.\n\t\n2002-02-07  Alexander Terekhov<TEREKHOV at de.ibm.com>\n\n\t* nonportable.c (pthread_delay_np): Make a true\n\tcancelation point. Deferred cancels will interrupt the\n\twait.\n\n2002-02-07  Ross Johnson  <rpj at setup1.ise.canberra.edu.au\n\n\t* misc.c (ptw32_new): Add creation of cancelEvent so that\n\timplicit POSIX threads (Win32 threads with a POSIX face)\n\tare cancelable; mainly so that pthread_delay_np doesn't fail\n\tif called from the main thread.\n\t* create.c (pthread_create): Remove creation of cancelEvent\n\tfrom here; now in ptw32_new().\n\n\tReduce executable size.\n\t-----------------------\n\tWhen linking with the static library, only those\n\troutines actually called, either directly or indirectly\n\tshould be included.\n\n\t[Gcc has the -ffunction-segments option to do this but MSVC\n\tdoesn't have this feature as far as I can determine. Other\n\tcompilers are undetermined as well. - rpj]\n\n\t* barrier.c: All routines are now in separate compilation units;\n\tThis file is used to congregate the separate modules for\n\tpotential inline optimisation and backward build compatibility.\n\t* cancel.c: Likewise.\n\t* pthread_barrierattr_destroy.c: Separated routine from cancel.c.\n\t* pthread_barrierattr_getpshared.c: Likewise.\n\t* pthread_barrierattr_init.c: Likewise.\n\t* pthread_barrierattr_setpshared.c: Likewise.\n\t* pthread_barrier_destroy.c: Likewise.\n\t* pthread_barrier_init.c: Likewise.\n\t* pthread_barrier_wait.c: Likewise.\n\t* pthread_cancel.c: Likewise.\n\t* pthread_setcancelstate.c: Likewise.\n\t* pthread_setcanceltype.c: Likewise.\n\t* pthread_testcancel.c: Likewise.\n\n2002-02-04  Max Woodbury <mtew at cds.duke.edu>\n\n\tReduced name space pollution.\n\t-----------------------------\n\tWhen the appropriate symbols are defined, the headers\n\twill restrict the definitions of new names. In particular,\n\tit must be possible to NOT include the <windows.h>\n\theader and related definitions with some combination\n\tof symbol definitions. Secondly, it should be possible\n\tthat additional definitions should be limited to POSIX \n\tcompliant symbols by the definition of appropriate symbols.\n\n\t* pthread.h: POSIX conditionals.\n\t* sched.h: POSIX conditionals.\n\t* semaphore.h: POSIX conditionals.\n\n\t* semaphore.c: Included <limits.h>.\n\t(sem_init): Changed magic 0x7FFFFFFFL to INT_MAX.\n\t(sem_getvalue): Trial version.\n\n\tReduce executable size.\n\t-----------------------\n\tWhen linking with the static library, only those\n\troutines actually called, either directly or indirectly\n\tshould be included.\n\n\t[Gcc has the -ffunction-segments option to do this but MSVC\n\tdoesn't have this feature as far as I can determine. Other\n\tcompilers are undetermined as well. - rpj]\n\n\t* semaphore.c: All routines are now in separate compilation units;\n\tThis file is used to congregate the separate modules for\n\tpotential inline optimisation and backward build compatibility.\n\t* sem_close.c: Separated routine from semaphore.c.\n\t* ptw32_decrease_semaphore.c: Likewise.\n\t* sem_destroy.c: Likewise.\n\t* sem_getvalue.c: Likewise.\n\t* ptw32_increase_semaphore.c: Likewise.\n\t* sem_init.c: Likewise.\n\t* sem_open.c: Likewise.\n\t* sem_post.c: Likewise.\n\t* sem_post_multiple.c: Likewise.\n\t* sem_timedwait.c: Likewise.\n\t* sem_trywait.c: Likewise.\n\t* sem_unlink.c: Likewise.\n\t* sem_wait.c: Likewise.\n\n2002-02-04  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\tThe following extends the idea above to the rest of pthreads-win32 - rpj\n\t\n\t* attr.c: All routines are now in separate compilation units;\n\tThis file is used to congregate the separate modules for\n\tpotential inline optimisation and backward build compatibility.\n\t* pthread_attr_destroy.c: Separated routine from attr.c.\n\t* pthread_attr_getdetachstate.c: Likewise.\n\t* pthread_attr_getscope.c: Likewise.\n\t* pthread_attr_getstackaddr.c: Likewise.\n\t* pthread_attr_getstacksize.c: Likewise.\n\t* pthread_attr_init.c: Likewise.\n\t* pthread_attr_is_attr.c: Likewise.\n\t* pthread_attr_setdetachstate.c: Likewise.\n\t* pthread_attr_setscope.c: Likewise.\n\t* pthread_attr_setstackaddr.c: Likewise.\n\t* pthread_attr_setstacksize.c: Likewise.\n\n\t* pthread.c: Agregation of agregate modules for super-inlineability.\n\n2002-02-02  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* cancel.c: Rearranged some code and introduced checks\n\tto disable cancelation at the start of a thread's cancelation\n\trun to prevent double cancelation. The main problem\n\tarises if a thread is canceling and then receives a subsequent\n\tasync cancel request.\n\t* private.c: Likewise.\n\t* condvar.c: Place pragmas around cleanup_push/pop to turn\n\toff inline optimisation (/Obn where n>0 - MSVC only). Various\n\toptimisation switches in MSVC turn this on, which interferes with\n\tthe way that cleanup handlers are run in C++ EH and SEH\n\tcode. Application code compiled with inline optimisation must\n\talso wrap cleanup_push/pop blocks with the pragmas, e.g.\n\t  #pragma inline_depth(0)\n\t  pthread_cleanup_push(...)\n\t    ...\n\t  pthread_cleanup_pop(...)\n\t  #pragma inline_depth(8)\n\t* rwlock.c: Likewise.\n\t* mutex.c: Remove attempts to inline some functions.\n\t* signal.c: Modify misleading comment.\n\n2002-02-01  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* semaphore.c (sem_trywait): Fix missing errno return\n\tfor systems that define NEED_SEM (e.g. early WinCE).\n\t* mutex.c (pthread_mutex_timedlock): Return ENOTSUP\n\tfor systems that define NEED_SEM since they don't\n\thave sem_trywait().\n\n2002-01-27  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* mutex.c (pthread_mutex_timedlock): New function suggested by\n\tAlexander Terekhov. The logic required to implement this\n\tproperly came from Alexander, with some collaboration\n\twith Thomas Pfaff.\n\t(pthread_mutex_unlock): Wrap the waiters check and sema\n\tpost in a critical section to prevent a race with\n\tpthread_mutex_timedlock.\n\t(ptw32_timed_semwait): New function;\n\treturns a special result if the absolute timeout parameter\n\trepresents a time already passed when called; used by\n\tpthread_mutex_timedwait(). Have deliberately not reused\n\tthe name \"ptw32_sem_timedwait\" because they are not the same\n\troutine.\n\t* condvar.c (ptw32_cond_timedwait): Use the new sem_timedwait()\n\tinstead of ptw32_sem_timedwait(), which now has a different\n\tfunction. See previous.\n\t* implement.h: Remove prototype for ptw32_sem_timedwait.\n\tSee next.\n\t(pthread_mutex_t_): Add critical section element for access\n\tto lock_idx during mutex post-timeout processing.\n\t* semaphore.h (sem_timedwait): See next.\n\t* semaphore.c (sem_timedwait): See next.\n\t* private.c (ptw32_sem_timedwait): Move to semaphore.c\n\tand rename as sem_timedwait().\n\n2002-01-18  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* sync.c (pthread_join): Was getting the exit code from the\n\tcalling thread rather than the joined thread if\n\tdefined(__MINGW32__) && !defined(__MSVCRT__).\n\n2002-01-15  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* pthread.h: Unless the build explicitly defines __CLEANUP_SEH,\n\t__CLEANUP_CXX, or __CLEANUP_C, then the build defaults to\n\t__CLEANUP_C style cleanup. This style uses setjmp/longjmp\n\tin the cancelation and thread exit implementations and therefore\n\twon't do stack unwinding if linked to applications that have it\n\t(e.g. C++ apps). This is currently consistent with most/all\n\tcommercial Unix POSIX threads implementations.\n\n\t* spin.c (pthread_spin_init): Edit renamed function call.\n\t* nonportable.c (pthread_num_processors_np): New.\n\t(pthread_getprocessors_np): Renamed to ptw32_getprocessors\n\tand moved to private.c.\n\t* private.c (pthread_getprocessors): Moved here from\n\tnonportable.c.\n\t* pthread.def (pthread_getprocessors_np): Removed\n\tfrom export list.\n\n\t* rwlock.c (pthread_rwlockattr_init): New.\n\t(pthread_rwlockattr_destroy): New.\n\t(pthread_rwlockattr_getpshared): New.\n\t(pthread_rwlockattr_setpshared): New.\n\n2002-01-14  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* attr.c (pthread_attr_setscope): Fix struct pointer\n\tindirection error introduced 2002-01-04.\n\t(pthread_attr_getscope): Likewise.\n\n2002-01-12  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* pthread.dsp (SOURCE): Add missing source files.\n\n2002-01-08  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* mutex.c (pthread_mutex_trylock): use\n\tptw32_interlocked_compare_exchange function pointer\n\trather than ptw32_InterlockedCompareExchange() directly\n\tto retain portability to non-iX86 processors,\n\te.g. WinCE etc. The pointer will point to the native\n\tOS version of InterlockedCompareExchange() if the\n\tOS supports it (see ChangeLog entry of 2001-10-17).\n\n2002-01-07  Thomas Pfaff <tpfaff at gmx.net>, Alexander Terekhov <TEREKHOV at de.ibm.com>\n\n\t* mutex.c (pthread_mutex_init): Remove critical\n\tsection calls.\n\t(pthread_mutex_destroy): Likewise.\n\t(pthread_mutex_unlock): Likewise.\n\t(pthread_mutex_trylock): Likewise; uses\n\tptw32_InterlockedCompareExchange() to avoid need for\n\tcritical section; library is no longer i386 compatible;\n\trecursive mutexes now increment the lock count rather\n\tthan return EBUSY; errorcheck mutexes return EDEADLCK\n\trather than EBUSY. This behaviour is consistent with the\n\tSolaris pthreads implementation.\n\t* implement.h (pthread_mutex_t_): Remove critical\n\tsection element - no longer needed.\n\t\n\n2002-01-04  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* attr.c (pthread_attr_setscope): Add more error\n\tchecking and actually store the scope value even\n\tthough it's not really necessary.\n\t(pthread_attr_getscope): Return stored value.\n\t* implement.h (pthread_attr_t_): Add new scope element.\n\t* ANNOUNCE: Fix out of date comment next to\n\tpthread_attr_setscope in conformance section.\n\n2001-12-21  Alexander Terekhov <TEREKHOV at de.ibm.com>\n\n\t* mutex.c (pthread_mutex_lock): Decrementing lock_idx was\n\tnot thread-safe.\n\t(pthread_mutex_trylock): Likewise.\n\n2001-10-26  prionx@juno.com\n\n\t* semaphore.c (sem_init): Fix typo and missing bracket\n\tin conditionally compiled code. Only older versions of\n\tWinCE require this code, hence it doesn't normally get\n\ttested; somehow when sem_t reverted to an opaque struct\n\tthe calloc NULL check was left in the conditionally included\n\tsection.\n\t(sem_destroy): Likewise, the calloced sem_t wasn't being freed.\n\n2001-10-25  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* GNUmakefile (libwsock32): Add to linker flags for\n\tWSAGetLastError() and WSASetLastError().\n\t* Makefile (wsock32.lib): Likewise.\n\t* create.c: Minor mostly inert changes.\n\t* implement.h (PTW32_MAX): Move into here and renamed\n\tfrom sched.h.\n\t(PTW32_MIN): Likewise.\n\t* GNUmakefile (TEST_ICE): Define if testing internal\n\timplementation of InterlockedCompareExchange.\n\t* Makefile (TEST_ICE): Likewise.\n\t* private.c (TEST_ICE): Likewise.\n\t\n2001-10-24  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* attr.c (pthread_attr_setstacksize): Quell warning\n\tfrom LCC by conditionally compiling the stacksize\n\tvalidity check. LCC correctly warns that the condition\n\t(stacksize < PTHREAD_STACK_MIN) is suspicious\n\tbecause STACK_MIN is 0 and stacksize is of type\n\tsize_t (or unsigned int).\n\n2001-10-17  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* barrier.c: Move _LONG and _LPLONG defines into\n\timplement.h; rename to PTW32_INTERLOCKED_LONG and\n\tPTW32_INTERLOCKED_LPLONG respectively.\n\t* spin.c: Likewise; ptw32_interlocked_compare_exchange used\n\tin place of InterlockedCompareExchange directly.\n\t* global.c (ptw32_interlocked_compare_exchange): Add\n\tprototype for this new routine pointer to be used when\n\tInterlockedCompareExchange isn't supported by Windows.\n\t* nonportable.c (pthread_win32_process_attach_np): Check for\n\tsupport of InterlockedCompareExchange in kernel32 and assign its\n\taddress to ptw32_interlocked_compare_exchange if it exists, or\n\tour own ix86 specific implementation ptw32_InterlockedCompareExchange.\n\t*private.c (ptw32_InterlockedCompareExchange): An\n\timplementation of InterlockedCompareExchange() which is\n\tspecific to ix86; written directly in assembler for either\n\tMSVC or GNU C; needed because Windows 95 doesn't support\n\tInterlockedCompareExchange().\n\n\t* sched.c (sched_get_priority_min): Extend to return\n\tTHREAD_PRIORITY_IDLE.\n\t(sched_get_priority_max): Extend to return\n\tTHREAD_PRIORITY_CRITICAL.\n\n2001-10-15  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* spin.c (pthread_spin_lock): PTHREAD_SPINLOCK_INITIALIZER\n\twas causing a program fault.\n\t(pthread_spin_init): Could have alloced memory\n\twithout freeing under some error conditions.\n\n\t* mutex.c (pthread_mutex_init): Move memory\n\tallocation of mutex struct after checking for\n\tPROCESS_SHARED.\n\n2001-10-12  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* spin.c (pthread_spin_unlock): Was not returning\n\tEPERM if the spinlock was not locked, for multi CPU\n\tmachines.\n\n2001-10-08  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* spin.c (pthread_spin_trylock): Was not returning\n\tEBUSY for multi CPU machines.\n\n2001-08-24  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* condvar.c (pthread_cond_destroy): Remove cv element\n\tthat is no longer used.\n\t* implement.h: Likewise.\n\n2001-08-23  Alexander Terekhov <TEREKHOV at de.ibm.com>\n\n\t* condvar.c (pthread_cond_destroy): fix bug with\n\trespect to deadlock in the case of concurrent\n\t_destroy/_unblock; a condition variable can be destroyed\n\timmediately after all the threads that are blocked on\n\tit are awakened.\n\n2001-08-23  Phil Frisbie, Jr. <phil at hawksoft.com>\n\n\t* tsd.c (pthread_getspecific): Preserve the last\n\twinsock error [from WSAGetLastError()].\n\n2001-07-18  Scott McCaskill <scott at magruder.org>\n\n\t* mutex.c (pthread_mutexattr_init): Return ENOMEM\n\timmediately and don't dereference the NULL pointer\n\tif calloc fails.\n\t(pthread_mutexattr_getpshared): Don't dereference\n\ta pointer that is possibly NULL.\n\t* barrier.c (pthread_barrierattr_init): Likewise\n\t(pthread_barrierattr_getpshared): Don't dereference\n\ta pointer that is possibly NULL.\n\t* condvar.c (pthread_condattr_getpshared): Don't dereference\n\ta pointer that is possibly NULL.\n\n2001-07-15  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* rwlock.c (pthread_rwlock_wrlock): Is allowed to be\n\ta cancelation point; re-enable deferred cancelability\n\taround the CV call.\n\n2001-07-10  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* barrier.c: Still more revamping. The exclusive access\n\tmutex isn't really needed so it has been removed and replaced\n\tby an InterlockedDecrement(). nSerial has been removed.\n\tiStep is now dual-purpose. The process shared attribute\n\tis now stored in the barrier struct.\n\t* implement.h (pthread_barrier_t_): Lost some/gained one\n\telements.\n\t* private.c (ptw32_threadStart): Removed some comments.\n\n2001-07-10  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* barrier.c: Revamped to fix the race condition. Two alternating\n\tsemaphores are used instead of the PulseEvent. Also improved\n\toverall throughput by returning PTHREAD_BARRIER_SERIAL_THREAD\n\tto the first waking thread.\n\t* implement.h (pthread_barrier_t_): Revamped.\n\n2001-07-09  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* barrier.c: Fix several bugs in all routines. Now passes\n\ttests/barrier5.c which is fairly rigorous. There is still\n\ta non-optimal work-around for a race condition between\n\tthe barrier breeched event signal and event wait. Basically\n\tthe last (signalling) thread to hit the barrier yields\n\tto allow any other threads, which may have lost the race,\n\tto complete.\n\n2001-07-07  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* barrier.c: Changed synchronisation mechanism to a\n\tWin32 manual reset Event and use PulseEvent to signal\n\twaiting threads. If the implementation continued to use\n\ta semaphore it would require a second semaphore and\n\tsome management to use them alternately as barriers. A\n\tsingle semaphore allows threads to cascade from one barrier\n\tthrough the next, leaving some threads blocked at the first.\n\t* implement.h (pthread_barrier_t_): As per above.\n\t* general: Made a number of other routines inlinable.\n\n2001-07-07  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* spin.c: Revamped and working; included static initialiser.\n\tNow beta level.\n\t* barrier.c: Likewise.\n\t* condvar.c: Macro constant change; inline auto init routine.\n\t* mutex.c: Likewise.\n\t* rwlock.c: Likewise.\n\t* private.c: Add support for spinlock initialiser.\n\t* global.c: Likewise.\n\t* implement.h: Likewise.\n\t* pthread.h (PTHREAD_SPINLOCK_INITIALIZER): Fix typo.\n\n2001-07-05  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* barrier.c: Remove static initialisation - irrelevent\n\tfor this object.\n\t* pthread.h (PTHREAD_BARRIER_INITIALIZER): Removed.\n\t* rwlock.c (pthread_rwlock_wrlock): This routine is\n\tnot a cancelation point - disable deferred\n\tcancelation around call to pthread_cond_wait().\n\n2001-07-05  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* spin.c: New module implementing spin locks.\n\t* barrier.c: New module implementing barriers.\n\t* pthread.h (_POSIX_SPIN_LOCKS): defined.\n\t(_POSIX_BARRIERS): Defined.\n\t(pthread_spin_*): Defined.\n\t(pthread_barrier*): Defined.\n\t(PTHREAD_BARRIER_SERIAL_THREAD): Defined.\n\t* implement.h (pthread_spinlock_t_): Defined.\n\t(pthread_barrier_t_): Defined.\n\t(pthread_barrierattr_t_): Defined.\n\n\t* mutex.c (pthread_mutex_lock): Return with the error\n\tif an auto-initialiser initialisation fails.\n\n\t* nonportable.c (pthread_getprocessors_np): New; gets the\n\tnumber of available processors for the current process.\n\n2001-07-03  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* pthread.h (_POSIX_READER_WRITER_LOCKS): Define it\n\tif not already defined.\n\n2001-07-01  Alexander Terekhov <TEREKHOV at de.ibm.com>\n\n\t* condvar.c: Fixed lost signal bug reported by Timur Aydin\n\t(taydin@snet.net).\n\t[RPJ (me) didn't translate the original algorithm\n\tcorrectly.]\n\t* semaphore.c: Added sem_post_multiple; this is a useful\n\troutine, but it doesn't appear to be standard. For now it's\n\tnot an exported function.\n\t\n2001-06-25  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* create.c (pthread_create): Add priority inheritance\n\tattributes.\n\t* mutex.c (pthread_mutex_lock): Remove some overhead for\n\tPTHREAD_MUTEX_NORMAL mutex types. Specifically, avoid\n\tcalling pthread_self() and pthread_equal() to check/set\n\tthe mutex owner. Introduce a new pseudo owner for this\n\ttype. Test results suggest increases in speed of up to\n\t90% for non-blocking locks.\n\tThis is the default type of mutex used internally by other\n\tsynchronising objects, ie. condition variables and\n\tread-write locks. The test rwlock7.c shows about a\n\t30-35% speed increase over snapshot 2001-06-06. The\n\tprice of this is that the application developer\n\tmust ensure correct behaviour, or explicitly set the\n\tmutex to a safer type such as PTHREAD_MUTEX_ERRORCHECK.\n\tFor example, PTHREAD_MUTEX_NORMAL (or PTHREAD_MUTEX_DEFAULT)\n\ttype mutexes will not return an error if a thread which is not\n\tthe owner calls pthread_mutex_unlock. The call will succeed\n\tin unlocking the mutex if it is currently locked, but a\n\tsubsequent unlock by the true owner will then fail with EPERM.\n\tThis is however consistent with some other implementations.\n\t(pthread_mutex_unlock): Likewise.\n\t(pthread_mutex_trylock): Likewise.\n\t(pthread_mutex_destroy): Likewise.\n\t* attr.c (pthread_attr_init): PTHREAD_EXPLICIT_SCHED is the\n\tdefault inheritance attribute; THREAD_PRIORITY_NORMAL is\n\tthe default priority for new threads.\n\t* sched.c (pthread_attr_setschedpolicy): Added routine.\n\t(pthread_attr_getschedpolicy): Added routine.\n\t(pthread_attr_setinheritsched): Added routine.\n\t(pthread_attr_getinheritsched): Added routine.\n\t* pthread.h (sched_rr_set_interval): Added as a macro;\n\treturns -1 with errno set to ENOSYS.\n\n2001-06-23  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t*sched.c (pthread_attr_setschedparam): Add priority range\n\tcheck.\n\t(sched_setscheduler): New function; checks for a valid\n\tpid and policy; checks for permission to set information\n\tin the target process; expects pid to be a Win32 process ID,\n\tnot a process handle; the only scheduler policy allowed is\n\tSCHED_OTHER.\n\t(sched_getscheduler): Likewise, but checks for permission\n\tto query.\n\t* pthread.h (SCHED_*): Moved to sched.h as defined in the\n\tPOSIX standard.\n\t* sched.h (SCHED_*): Moved from pthread.h.\n\t(pid_t): Defined if necessary.\n\t(sched_setscheduler): Defined.\n\t(sched_getscheduler): Defined.\n\t* pthread.def (sched_setscheduler): Exported.\n\t(sched_getscheduler): Likewise.\n\n2001-06-23  Ralf Brese <Ralf.Brese at pdb4.siemens.de>\n\n\t* create.c (pthread_create): Set thread priority from\n\tthread attributes.\n\n2001-06-18  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* Made organisational-only changes to UWIN additions.\n\t* dll.c (dllMain): Moved UWIN process attach code\n\tto pthread_win32_process_attach_np(); moved\n\tinstance of pthread_count to global.c.\n\t* global.c (pthread_count): Moved from dll.c.\n\t* nonportable.c (pthread_win32_process_attach_np):\n\tMoved _UWIN code to here from dll.c.\n\t* implement.h (pthread_count): Define extern int.\n\t* create.c (pthread_count): Remove extern int.\n\t* private.c (pthread_count): Likewise.\n\t* exit.c (pthread_count): Likewise.\n\n2001-06-18  David Korn <dgk at research.att.com>\n\n\t* dll.c: Added changes necessary to work with UWIN.\n\t* create.c: Likewise.\n\t* pthread.h: Likewise.\n\t* misc.c: Likewise.\n\t* exit.c: Likewise.\n\t* private.c: Likewise.\n\t* implement.h: Likewise.\n\tThere is some room at the start of struct pthread_t_\n\tto implement the signal semantics in UWIN's posix.dll\n\talthough this is not yet complete.\n\t* Nmakefile: Compatible with UWIN's Nmake utility.\n\t* Nmakefile.tests: Likewise - for running the tests.\n\n2001-06-08  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* semaphore.h (sem_t): Fixed for compile and test.\n\t* implement.h (sem_t_): Likewise.\n\t* semaphore.c: Likewise.\n\t* private.c (ptw32_sem_timedwait): Updated to use new\n\topaque sem_t.\n\n2001-06-06  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* semaphore.h (sem_t): Is now an opaque pointer;\n\tmoved actual definition to implement.h.\n\t* implement.h (sem_t_): Move here from semaphore.h;\n\twas the definition of sem_t.\n\t* semaphore.c: Wherever necessary, changed use of sem\n\tfrom that of a pointer to a pointer-pointer; added\n\textra checks for a valid sem_t; NULL sem_t when\n\tit is destroyed; added extra checks when creating\n\tand destroying sem_t elements in the NEED_SEM\n\tcode branches; changed from using a pthread_mutex_t\n\t((*sem)->mutex) to CRITICAL_SECTION ((*sem)->sem_lock_cs)\n\tin NEED_SEM branches for access serialisation.\n\n2001-06-06  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* mutex.c (pthread_mutexattr_init): Remove \n\tptw32_mutex_default_kind.\n\t\n2001-06-05  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* nonportable.c (pthread_mutex_setdefaultkind_np):\n\tRemove - should not have been included in the first place.\n\t(pthread_mutex_getdefaultkind_np): Likewise.\n\t* global.c (ptw32_mutex_default_kind): Likewise.\n\t* mutex.c (pthread_mutex_init): Remove use of\n\tptw32_mutex_default_kind.\n\t* pthread.h (pthread_mutex_setdefaultkind_np): Likewise.\n\t(pthread_mutex_getdefaultkind_np): Likewise.\n\t* pthread.def (pthread_mutexattr_setkind_np): Added.\n\t(pthread_mutexattr_getkind_np): Likewise.\n\n\t* README: Many changes that should have gone in before\n\tthe last snapshot.\n\t* README.NONPORTABLE: New - referred to by ANNOUNCE\n\tbut never created; documents the non-portable routines\n\tincluded in the library - moved from README with new\n\troutines added.\n\t* ANNOUNCE (pthread_mutexattr_setkind_np): Added to\n\tcompliance list.\n\t(pthread_mutexattr_getkind_np): Likewise.\n\n2001-06-04  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* condvar.c: Add original description of the algorithm as\n\tdeveloped by Terekhov and Thomas, plus reference to\n\tREADME.CV.\n\n2001-06-03  Alexander Terekhov <TEREKHOV at de.ibm.com>, Louis Thomas <lthomas at arbitrade.com>\n\n\t* condvar.c (pthread_cond_init): Completely revamped.\n\t(pthread_cond_destroy): Likewise.\n\t(ptw32_cond_wait_cleanup): Likewise.\n\t(ptw32_cond_timedwait): Likewise.\n\t(ptw32_cond_unblock): New general signaling routine.\n\t(pthread_cond_signal): Now calls ptw32_cond_unblock.\n\t(pthread_cond_broadcast): Likewise.\n\t* implement.h (pthread_cond_t_): Revamped.\n\t* README.CV: New; explanation of the above changes.\n\n2001-05-30  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* pthread.h (rand_r): Fake using _seed argument to quell\n\tcompiler warning (compiler should optimise this away later).\n\n\t* GNUmakefile (OPT): Leave symbolic information out of the library\n\tand increase optimisation level - for smaller faster prebuilt\n\tdlls.\n\t\n2001-05-29  Milan Gardian <Milan.Gardian at LEIBINGER.com>\n\n\t* Makefile: fix typo.\n\t* pthreads.h: Fix problems with stdcall/cdecl conventions, in particular\n\tremove the need for PT_STDCALL everywhere; remove warning supression.\n\t* (errno): Fix the longstanding \"inconsistent dll linkage\" problem\n\twith errno; now also works with /MD debugging libs - \n\twarnings emerged when compiling pthreads library with /MD (or /MDd)\n\tcompiler switch, instead of /MT (or /MTd) (i.e. when compiling pthreads\n\tusing Multithreaded DLL CRT instead of Multithreaded statically linked\n\tCRT).\n\t* create.c (pthread_create): Likewise; fix typo.\n\t* private.c (ptw32_threadStart): Eliminate use of terminate() which doesn't\n\tthrow exceptions.\n\t* Remove unnecessary #includes from a number of modules -\n\t[I had to #include malloc.h in implement.h for gcc - rpj].\n\n2001-05-29  Thomas Pfaff <tpfaff at gmx.net>\n\n\t* pthread.h (PTHREAD_MUTEX_DEFAULT): New; equivalent to\n\tPTHREAD_MUTEX_DEFAULT_NP.\n\t* (PTHREAD_MUTEX_NORMAL): Similarly.\n\t* (PTHREAD_MUTEX_ERRORCHECK): Similarly.\n\t* (PTHREAD_MUTEX_RECURSIVE): Similarly.\n\t* (pthread_mutex_setdefaultkind_np): New; Linux compatibility stub\n\tfor pthread_mutexattr_settype.\n\t* (pthread_mutexattr_getkind_np): New; Linux compatibility stub\n\tfor pthread_mutexattr_gettype.\n\t* mutex.c (pthread_mutexattr_settype): New; allow\n\tthe following types of mutex:\n\t  PTHREAD_MUTEX_DEFAULT_NP\n\t  PTHREAD_MUTEX_NORMAL_NP\n\t  PTHREAD_MUTEX_ERRORCHECK_NP\n\t  PTHREAD_MUTEX_RECURSIVE_NP\n\t* Note that PTHREAD_MUTEX_DEFAULT is equivalent to\n\tPTHREAD_MUTEX_NORMAL - ie. mutexes should no longer\n\tbe recursive by default, and a thread will deadlock if it\n\ttries to relock a mutex it already owns. This is inline with\n\tother pthreads implementations.\n\t* (pthread_mutex_lock): Process the lock request\n\taccording to the mutex type.\n\t* (pthread_mutex_init): Eliminate use of Win32 mutexes as the\n\tbasis of POSIX mutexes - instead, a combination of one critical section\n\tand one semaphore are used in conjunction with Win32 Interlocked* routines.\n\t* (pthread_mutex_destroy): Likewise.\n\t* (pthread_mutex_lock): Likewise.\n\t* (pthread_mutex_trylock): Likewise.\n\t* (pthread_mutex_unlock): Likewise.\n\t* Use longjmp/setjmp to implement cancelation when building the library\n\tusing a C compiler which doesn't support exceptions, e.g. gcc -x c (note\n\tthat gcc -x c++ uses exceptions).\n\t* Also fixed some of the same typos and eliminated PT_STDCALL as\n\tMilan Gardian's patches above.\n\n2001-02-07  Alexander Terekhov <TEREKHOV at de.ibm.com>\n\n\t* rwlock.c: Revamped.\n\t* implement.h (pthread_rwlock_t_): Redefined.\n\tThis implementation does not have reader/writer starvation problem.\n\tRwlock attempts to behave more like a normal mutex with\n\traces and scheduling policy determining who is more important;\n\tIt also supports recursive locking,\n\thas less synchronization overhead (no broadcasts at all,\n\treaders are not blocked on any condition variable) and seem to\n\tbe faster than the current implementation [W98 appears to be\n\tapproximately 15 percent faster at least - on top of speed increase\n\tfrom Thomas Pfaff's changes to mutex.c - rpj].\n\n2000-12-29  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* Makefile: Back-out \"for\" loops which don't work.\n\n\t* GNUmakefile: Remove the fake.a target; add the \"realclean\"\n\ttarget; don't remove built libs under the \"clean\" target.\n\n\t* config.h: Add a guard against multiple inclusion.\n\n\t* semaphore.h: Add some defines from config.h to make\n\tsemaphore.h independent of config.h when building apps.\n\n\t* pthread.h (_errno): Back-out previous fix until we know how to\n\tfix it properly.\n\n\t* implement.h (lockCount): Add missing element to pthread_mutex_t_.\n\n\t* sync.c (pthread_join): Spelling fix in comment.\n\n\t* private.c (ptw32_threadStart): Reset original termination\n\tfunction (C++).\n\t(ptw32_threadStart): Cleanup detached threads early in case\n\tthe library is statically linked.\n\t(ptw32_callUserDestroyRoutines): Remove [SEH] __try block from\n\tdestructor call so that unhandled exceptions will be passed through\n\tto the \tsystem; call terminate() from [C++] try block for the same\n\treason.\n\n\t* tsd.c (pthread_getspecific): Add comment.\n\n\t* mutex.c (pthread_mutex_init): Initialise new elements in\n\tpthread_mutex_t.\n\t(pthread_mutex_unlock): Invert \"pthread_equal()\" test.\n\n2000-12-28  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* semaphore.c (mode_t): Use ifndef HAVE_MODE_T to include definition.\n\n\t* config.h.in (HAVE_MODE_T): Added.\n\t(_UWIN): Start adding defines for the UWIN package.\n\n\t* private.c (ptw32_threadStart): Unhandled exceptions are\n\tnow passed through to the system to deal with. This is consistent\n\twith normal Windows behaviour. C++ applications may use\n\tset_terminate() to override the default behaviour which is\n\tto call ptw32_terminate(). Ptw32_terminate() cleans up some\n\tPOSIX thread stuff before calling the system default function\n\twhich calls abort(). The users termination function should conform\n\tto standard C++ semantics which is to not return. It should\n\texit the thread (call pthread_exit()) or exit the application.\n\t* private.c (ptw32_terminate): Added as the default set_terminate()\n\tfunction. It calls the system default function after cleaning up\n\tsome POSIX thread stuff.\n\n\t* implement.h (ptw32_try_enter_critical_section): Move\n\tdeclaration.\n\t* global.c (ptw32_try_enter_critical_section): Moved\n\tfrom dll.c.\n\t* dll.c: Move process and thread attach/detach code into\n\tfunctions in nonportable.c.\n\t* nonportable.c (pthread_win32_process_attach_np): Process\n\tattach code from dll.c is now available to static linked\n\tapplications.\n\t* nonportable.c (pthread_win32_process_detach_np): Likewise.\n\t* nonportable.c (pthread_win32_thread_attach_np): Likewise.\n\t* nonportable.c (pthread_win32_thread_detach_np): Likewise.\n\n\t* pthread.h: Add new non-portable prototypes for static\n\tlinked applications.\n\n\t* GNUmakefile (OPT): Increase optimisation flag and remove\n\tdebug info flag.\n\n\t* pthread.def: Add new non-portable exports for static\n\tlinked applications.\n\n2000-12-11  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* FAQ: Update Answer 6 re getting a fully working\n\tMingw32 built library.\n\n2000-10-10  Steven Reddie <smr at essemer.com.au>\n \n        * misc.c (pthread_self): Restore Win32 \"last error\"\n        cleared by TlsGetValue() call in\n        pthread_getspecific()\n \n2000-09-20  Arthur Kantor <akantor at bexusa.com>\n \n        * mutex.c (pthread_mutex_lock): Record the owner\n        of the mutex. This requires also keeping count of\n        recursive locks ourselves rather than leaving it\n        to Win32 since we need to know when to NULL the\n        thread owner when the mutex is unlocked.\n        (pthread_mutex_trylock): Likewise.\n        (pthread_mutex_unlock): Check that the calling\n        thread owns the mutex, decrement the recursive\n        lock count, and NULL the owner if zero. Return\n        EPERM if the mutex is owned by another thread.\n        * implement.h (pthread_mutex_t_): Add ownerThread\n        and lockCount members.\n\n2000-09-13  Jef Gearhart <jgearhart at tpssys.com>\n\n\t* mutex.c (pthread_mutex_init): Call\n\tTryEnterCriticalSection through the pointer\n\trather than directly so that the dll can load\n\ton Windows versions that can't resolve the\n\tfunction, eg. Windows 95\n\n2000-09-09  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* pthread.h (ctime_r): Fix arg.\n\n2000-09-08  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* GNUmakefile(_WIN32_WINNT=0x400): Define in CFLAGS;\n\tdoesn't seem to be needed though.\n\n\t* cancel.c (pthread_cancel): Must get \"self\" through\n\tcalling pthread_self() which will ensure a POSIX thread\n\tstruct is built for non-POSIX threads; return an error\n\tif this fails\n\t- Ollie Leahy <ollie at mpt.ie>\n\t(pthread_setcancelstate): Likewise.\n\t(pthread_setcanceltype): Likewise.\n\t* misc.c (ptw32_cancelable_wait): Likewise.\n\n\t* private.c (ptw32_tkAssocCreate): Remove unused #if 0\n\twrapped code.\n\n\t* pthread.h (ptw32_get_exception_services_code):\n\tNeeded to be forward declared unconditionally.\n\n2000-09-06  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* cancel.c (pthread_cancel): If called from the main\n\tthread \"self\" would be NULL; get \"self\" via pthread_self()\n\tinstead of directly from TLS so that an implicit\n\tpthread object is created.\n\n\t* misc.c (pthread_equal): Strengthen test for NULLs.\n\n2000-09-02  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* condvar.c (ptw32_cond_wait_cleanup): Ensure that all\n\twaking threads check if they are the last, and notify\n\tthe broadcaster if so - even if an error occurs in the\n\twaiter.\n\n\t* semaphore.c (_decrease_semaphore): Should be\n\ta call to ptw32_decrease_semaphore.\n\t(_increase_semaphore): Should be a call to\n\tptw32_increase_semaphore.\n\n\t* misc.c (ptw32_cancelable_wait): Renamed from\n\tCancelableWait.\n\t* rwlock.c (_rwlock_check*): Renamed to\n\tptw32_rwlock_check*.\n\t* mutex.c (_mutex_check*): Renamed to ptw32_mutex_check*.\n\t* condvar.c (cond_timed*): Renamed to ptw32_cond_timed*.\n\t(_cond_check*): Renamed to ptw32_cond_check*.\n\t(cond_wait_cleanup*): Rename to ptw32_cond_wait_cleanup*.\n\t(ptw32_cond_timedwait): Add comments.\n\n2000-08-22  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* private.c (ptw32_throw): Fix exception test;\n\tmove exceptionInformation declaration.\n\n\t* tsd.c (pthread_key_create): newkey wrongly declared.\n\n\t* pthread.h: Fix comment block.\n\n2000-08-18  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* mutex.c (pthread_mutex_destroy): Check that the mutex isn't\n\theld; invalidate the mutex as early as possible to avoid\n\tcontention; not perfect - FIXME!\n\n\t* rwlock.c (pthread_rwlock_init): Remove redundant assignment\n\tto \"rw\".\n\t(pthread_rwlock_destroy): Invalidate the rwlock before\n\tfreeing up any of it's resources - to avoid contention.\n\n\t* private.c (ptw32_tkAssocCreate): Change assoc->lock\n\tto use a dynamically initialised mutex - only consumes\n\ta W32 mutex or critical section when first used,\n\tnot before.\n\n\t* mutex.c (pthread_mutex_init): Remove redundant assignment\n\tto \"mx\".\n\t(pthread_mutexattr_destroy): Set attribute to NULL\n\tbefore freeing it's memory - to avoid contention.\n\n\t* implement.h (PTW32_EPS_CANCEL/PTW32_EPS_EXIT):\n\tMust be defined for all compilers - used as generic\n\texception selectors by ptw32_throw().\n\n\t* Several: Fix typos from scripted edit session\n\tyesterday.\n\n\t* nonportable.c (pthread_mutexattr_setforcecs_np):\n\tMoved this function from mutex.c.\n\t(pthread_getw32threadhandle_np): New function to\n\treturn the win32 thread handle that the POSIX\n\tthread is using.\n\t* mutex.c (pthread_mutexattr_setforcecs_np):\n\tMoved to new file \"nonportable.c\".\n\n\t* pthread.h (PTW32_BUILD): Only\tredefine __except\n\tand catch compiler keywords if we aren't building\n\tthe library (ie. PTW32_BUILD is not defined) - \n\tthis is safer than defining and then undefining\n\tif not building the library.\n\t* implement.h: Remove __except and catch undefines.\n\t* Makefile (CFLAGS): Define PTW32_BUILD.\n\t* GNUmakefile (CFLAGS): Define PTW32_BUILD.\n\n\t* All appropriate: Change Pthread_exception* to\n\tptw32_exception* to be consistent with internal\n\tidentifier naming.\n\n\t* private.c (ptw32_throw): New function to provide\n\ta generic exception throw for all internal\n\texceptions and EH schemes.\n\t(ptw32_threadStart): pthread_exit() value is now\n\treturned via the thread structure exitStatus\n\telement.\n\t* exit.c (pthread_exit): pthread_exit() value is now\n\treturned via the thread structure exitStatus\n\telement.\n\t* cancel.c (ptw32_cancel_self): Now uses ptw32_throw.\n\t(pthread_setcancelstate): Ditto.\n\t(pthread_setcanceltype): Ditto.\n\t(pthread_testcancel): Ditto.\n\t(pthread_cancel): Ditto.\n\t* misc.c (CancelableWait): Ditto.\n\t* exit.c (pthread_exit): Ditto.\n\t* All applicable: Change PTW32_ prefix to\n\tPTW32_ prefix to remove leading underscores\n\tfrom private library identifiers.\n\n2000-08-17  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* All applicable: Change _pthread_ prefix to\n\tptw32_ prefix to remove leading underscores\n\tfrom private library identifiers (single\n\tand double leading underscores are reserved in the\n\tANSI C standard for compiler implementations).\n\n\t* tsd.c (pthread_create_key): Initialise temporary\n\tkey before returning it's address to avoid race\n\tconditions.\n\n2000-08-13  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* errno.c: Add _MD precompile condition; thus far\n\thad no effect when using /MD compile option but I\n\tthnk it should be there.\n\n\t* exit.c: Add __cplusplus to various #if lines;\n\twas compiling SEH code even when VC++ had\n\tC++ compile options.\n\n\t* private.c: ditto.\n\n\t* create.c (pthread_create): Add PT_STDCALL macro to\n\tfunction pointer arg in _beginthread().\n\n\t* pthread.h: PT_STDCALL really does need to be defined\n\tin both this and impliment.h; don't set it to __cdecl\n\t- this macro is only used to extend function pointer\n\tcasting for functions that will be passed as parameters.\n\t(~PThreadCleanup): add cast and group expression.\n\t(_errno): Add _MD compile conditional.\n\t(PtW32NoCatchWarn): Change pragma message.\n\n\t* implement.h: Move and change PT_STDCALL define.\n\n\t* need_errno.h: Add _MD to compilation conditional.\n\n\t* GNUmakefile: Substantial rewrite for new naming\n\tconvention; set for nil optimisation (turn it up\n\twhen we have a working library build; add target\n\t\"fake.a\" to build a libpthreadw32.a from the VC++\n\tbuilt DLL pthreadVCE.dll.\n\n\t* pthread.def (LIBRARY): Don't specify in the .def\n\tfile - it is specified on the linker command line\n\tsince we now use the same .def file for variously\n\tnamed .dlls.\n\n\t* Makefile: Substantial rewrite for new naming\n\tconvention; default nmake target only issues a\n\thelp message; run nmake with specific target\n\tcorresponding to the EH scheme being used.\n\n\t* README: Update information; add naming convention\n\texplanation.\n\n\t* ANNOUNCE: Update information.\n\n2000-08-12  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* pthread.h: Add compile-time message when using\n\tMSC_VER compiler and C++ EH to warn application\n\tprogrammers to use PtW32Catch instead of catch(...)\n\tif they want cancelation and pthread_exit to work.\n\n\t* implement.h: Remove #include <semaphore.h>; we\n\tuse our own local semaphore.h.\n\n2000-08-10  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* cleanup.c (pthread_pop_cleanup): Remove _pthread\n\tprefix from __except and catch keywords; implement.h\n\tnow simply undefines ptw32__except and\n\tptw32_catch if defined; VC++ was not textually\n\tsubstituting ptw32_catch etc back to catch as\n\tit was redefined; the reason for using the prefixed\n\tversion was to make it clear that it was not using\n\tthe pthread.h redefined catch keyword.\n\n\t* private.c (ptw32_threadStart): Ditto.\n\t(ptw32_callUserDestroyRoutines): Ditto.\n\n\t* implement.h (ptw32__except): Remove #define.\n\t(ptw32_catch): Remove #define.\n\n\t* GNUmakefile (pthread.a): New target to build\n\tlibpthread32.a from pthread.dll using dlltool.\n\n\t* buildlib.bat: Duplicate cl commands with args to\n\tbuild C++ EH version of pthread.dll; use of .bat\n\tfiles is redundant now that nmake compatible\n\tMakefile is included; used as a kludge only now.\n\n\t* Makefile: Localise some macros and fix up the clean:\n\ttarget to extend it and work properly.\n\n\t* CONTRIBUTORS: Add contributors.\n\n\t* ANNOUNCE: Updated.\n\n\t* README: Updated.\n\n2000-08-06  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* pthread.h: Remove #warning - VC++ doesn't accept it.\n\n2000-08-05  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* pthread.h (PtW32CatchAll): Add macro. When compiling\n\tapplications using VC++ with C++ EH rather than SEH\n\t'PtW32CatchAll' must be used in place of any 'catch( ... )'\n\tif the application wants pthread cancelation or\n\tpthread_exit() to work.\n\n2000-08-03  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* pthread.h: Add a base class ptw32_exception for\n\tlibrary internal exceptions and change the \"catch\"\n\tre-define macro to use it.\n\n2000-08-02  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* GNUmakefile (CFLAGS): Add -mthreads.\n\tAdd new targets to generate cpp and asm output.\n\n\t* sync.c (pthread_join): Remove dead code.\n\n2000-07-25  Tristan Savatier <tristan at mpegtv.com>\n\n\t* sched.c (sched_get_priority_max): Handle different WinCE and\n\tWin32 priority values together.\n\t(sched_get_priority_min): Ditto.\n\n2000-07-25  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* create.c (pthread_create): Force new threads to wait until\n\tpthread_create has the new thread's handle; we also retain\n\ta local copy of the handle for internal use until\n\tpthread_create returns.\n\n\t* private.c (ptw32_threadStart): Initialise ei[].\n\t(ptw32_threadStart): When beginthread is used to start the\n\tthread, force waiting until the creator thread had the \n\tthread handle.\n\n\t* cancel.c (ptw32_cancel_thread): Include context switch\n\tcode for defined(_X86_) environments in addition to _M_IX86.\n\n\t* rwlock.c (pthread_rwlock_destroy): Assignment changed\n\tto avoid compiler warning.\n\n\t* private.c (ptw32_get_exception_services_code): Cast\n\tNULL return value to avoid compiler warning.\n\n\t* cleanup.c (pthread_pop_cleanup): Initialise \"cleanup\" variable\n\tto avoid compiler warnings.\n\n\t* misc.c (ptw32_new): Change \"new\" variable to \"t\" to avoid\n\tconfusion with the C++ keyword of the same name.\n\n\t* condvar.c (cond_wait_cleanup): Initialise lastWaiter variable.\n\t(cond_timedwait): Remove unused local variables. to avoid\n\tcompiler warnings.\n\n\t* dll.c (dllMain): Remove 2000-07-21 change - problem\n\tappears to be in pthread_create().\n\n2000-07-22  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* tsd.c (pthread_key_create): If a destructor was given\n\tand the pthread_mutex_init failed, then would try to\n\treference a NULL pointer (*key); eliminate this section of\n\tcode by using a dynamically initialised mutex\n\t(PTHREAD_MUTEX_INITIALIZER).\n\n\t* tsd.c (pthread_setspecific): Return an error if\n\tunable to set the value; simplify cryptic conditional.\n\n\t* tsd.c (pthread_key_delete): Locking threadsLock relied\n\ton mutex_lock returning an error if the key has no destructor.\n\tThreadsLock is only initialised if the key has a destructor.\n\tMaking this mutex a static could reduce the number of mutexes\n\tused by an application since it is actually created only at\n\tfirst use and it's often destroyed soon after.\n\t\n2000-07-22  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* FAQ: Added Q5 and Q6.\n\n2000-07-21  David Baggett <dmb at itasoftware.com>\n\n\t* dll.c: Include resource leakage work-around. This is a\n\tpartial FIXME which doesn't stop all leakage. The real\n\tproblem needs to be found and fixed.\n\n2000-07-21  Ross Johnson  <rpj at setup1.ise.canberra.edu.au>\n\n\t* create.c (pthread_create): Set threadH to 0 (zero)\n\teverywhere. Some assignments were using NULL. Maybe\n\tit should be NULL everywhere - need to check. (I know\n\tthey are nearly always the same thing - but not by\n\tdefinition.)\n\n\t* misc.c (pthread_self): Try to catch NULL thread handles\n\tat the point where they might be generated, even though\n\tthey should always be valid at this point.\n\n\t* tsd.c (pthread_setspecific): return an error value if\n\tpthread_self() returns NULL.\n\n\t* sync.c (pthread_join): return an error value if\n\tpthread_self() returns NULL.\n\n\t* signal.c (pthread_sigmask): return an error value if\n\tpthread_self() returns NULL.\n\n2000-03-02  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* attr.c (pthread_attr_init): Set default stacksize to zero (0)\n\trather than PTHREAD_STACK_MIN even though these are now the same.\n\n\t* pthread.h (PTHREAD_STACK_MIN): Lowered to 0.\n\n2000-01-28  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* mutex.c (pthread_mutex_init): Free mutex if it has been alloced;\n\tif critical sections can be used instead of Win32 mutexes, test\n\tthat the critical section works and return an error if not.\n\n2000-01-07  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* cleanup.c (pthread_pop_cleanup): Include SEH code only if MSC is not\n\tcompiling as C++.\n\t(pthread_push_cleanup): Include SEH code only if MSC is not\n\tcompiling as C++.\n\n\t* pthread.h: Include SEH code only if MSC is not\n\tcompiling as C++.\n\n\t* implement.h: Include SEH code only if MSC is not\n\tcompiling as C++.\n\n\t* cancel.c (ptw32_cancel_thread): Add _M_IX86 check.\n\t(pthread_testcancel): Include SEH code only if MSC is not\n\tcompiling as C++.\n\t(ptw32_cancel_self): Include SEH code only if MSC is not\n\tcompiling as C++.\n\n2000-01-06  Erik Hensema <erik.hensema at group2000.nl>\n\n\t* Makefile: Remove inconsistencies in 'cl' args\n\n2000-01-04  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* private.c (ptw32_get_exception_services_code): New; returns\n\tvalue of EXCEPTION_PTW32_SERVICES.\n\t(ptw32_processInitialize): Remove initialisation of\n\tptw32_exception_services which is no longer needed.\n\n\t* pthread.h (ptw32_exception_services): Remove extern.\n\t(ptw32_get_exception_services_code): Add function prototype;\n\tuse this to return EXCEPTION_PTW32_SERVICES value instead of\n\tusing the ptw32_exception_services variable which I had\n\ttrouble exporting through pthread.def.\n\n\t* global.c (ptw32_exception_services): Remove declaration.\n\n1999-11-22  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* implement.h: Forward declare ptw32_new();\n\n\t* misc.c (ptw32_new): New; alloc and initialise a new pthread_t.\n\t(pthread_self): New thread struct is generated \tby new routine\n\tptw32_new().\n\n\t* create.c (pthread_create): New thread struct is generated\n\tby new routine ptw32_new().\n\n1999-11-21  Ross Johnson  <rpj at special.ise.canberra.edu.au>\n\n\t* global.c (ptw32_exception_services): Declare new variable. \n\n\t* private.c (ptw32_threadStart): Destroy thread's\n\tcancelLock mutex; make 'catch' and '__except' usageimmune to\n\tredfinitions in pthread.h.\n\t(ptw32_processInitialize): Init new constant ptw32_exception_services.\n\n\t* create.c (pthread_create): Initialise thread's cancelLock\n\tmutex.\n\n\t* cleanup.c (pthread_pop_cleanup): Make 'catch' and '__except'\n\tusage immune to redfinition s in pthread.h.\n\n\t* private.c: Ditto.\n\n\t* pthread.h (catch): Redefine 'catch' so that C++ applications\n\twon't catch our internal exceptions.\n\t(__except): ditto for __except.\n\n\t* implement.h (ptw32_catch): Define internal version\n\tof 'catch' because 'catch' is redefined by pthread.h.\n\t(__except): ditto for __except.\n\t(struct pthread_t_): Add cancelLock mutex for async cancel\n\tsafety.\n\n1999-11-21  Jason Nye <jnye at nbnet.nb.ca>, Erik Hensema <erik.hensema at group2000.nl>\n\n\t* cancel.c (ptw32_cancel_self): New; part of the async\n\tcancellation implementation.\n\t(ptw32_cancel_thread): Ditto; this function is X86\n\tprocessor specific.\n\t(pthread_setcancelstate): Add check for pending async\n\tcancel request and cancel the calling thread if\n\trequired; add async-cancel safety lock.\n\t(pthread_setcanceltype): Ditto.\n\n1999-11-13  Erik Hensema <erik.hensema at group2000.nl>\n\n\t* configure.in (AC_OUTPUT): Put generated output into GNUmakefile\n\trather than Makefile. Makefile will become the MSC nmake compatible\n\tversion\n\n1999-11-13  John Bossom (John.Bossom@cognos.com>\n\n\t* misc.c (pthread_self): Add a note about GetCurrentThread\n\treturning a pseudo-handle\n\n1999-11-10  Todd Owen <towen at lucidcalm.dropbear.id.au>\n\n\t* dll.c (dllMain): Free kernel32 ASAP.\n\tIf TryEnterCriticalSection is not being used, then free\n\tthe kernel32.dll handle now, rather than leaving it until\n\tDLL_PROCESS_DETACH.\n\n\tNote: this is not a pedantic exercise in freeing unused\n\tresources!  It is a work-around for a bug in Windows 95\n\t(see microsoft knowledge base article, Q187684) which\n\tdoes Bad Things when FreeLibrary is called within\n\tthe DLL_PROCESS_DETACH code, in certain situations.\n\tSince w95 just happens to be a platform which does not\n\tprovide TryEnterCriticalSection, the bug will be\n\teffortlessly avoided.\n\n1999-11-10  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* sync.c (pthread_join): Make it a deferred cancelation point.\n\n\t* misc.c (pthread_self): Explicitly initialise implicitly\n\tcreated thread state to default values.\n\n1999-11-05  Tristan Savatier <tristan at mpegtv.com>\n\n\t* pthread.h (winsock.h): Include unconditionally.\n\t(ETIMEDOUT): Change fallback value to that defined by winsock.h.\n\t\n\t* general: Patched for portability to WinCE. The details are\n\tdescribed in the file WinCE-PORT. Follow the instructions\n\tin README.WinCE to make the appropriate changes in config.h.\n\n1999-10-30  Erik Hensema <erik.hensema at group2000.nl>\n\n\t* create.c (pthread_create): Explicitly initialise thread state to\n\tdefault values.\n\n\t* cancel.c (pthread_setcancelstate): Check for NULL 'oldstate'\n\tfor compatibility with Solaris pthreads;\n\t(pthread_setcanceltype): ditto:\n\n1999-10-23  Erik Hensema <erik.hensema at group2000.nl>\n\n\t* pthread.h (ctime_r): Fix incorrect argument \"_tm\"\n\n1999-10-21  Aurelio Medina <aureliom at crt.com>\n\n\t* pthread.h (_POSIX_THREADS): Only define it if it isn't\n\talready defined. Projects may need to define this on\n\tthe CC command line under Win32 as it doesn't have unistd.h\n\n1999-10-17  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* rwlock.c (pthread_rwlock_destroy): Add cast to remove compile\n\twarning.\n\n\t* condvar.c (pthread_cond_broadcast): Only release semaphores\n\tif there are waiting threads.\n\n1999-10-15  Lorin Hochstein <lmh at xiphos.ca>, Peter Slacik <Peter.Slacik at tatramed.sk>\n\n\t* condvar.c (cond_wait_cleanup): New static cleanup handler for\n\tcond_timedwait;\n\t(cond_timedwait): pthread_cleanup_push args changed;\n\tcanceling a thread while it's in pthread_cond_wait\n\twill now decrement the waiters count and cleanup if it's the\n\tlast waiter.\n\n1999-10-15  Graham Dumpleton <Graham.Dumpleton at ra.pad.otc.telstra.com.au>\n\n\t* condvar.c (cond_wait_cleanup): the last waiter will now reset the CV's\n\twasBroadcast flag\n\nThu Sep 16 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* rwlock.c (pthread_rwlock_destroy): Add serialisation.\n\t(_rwlock_check_need_init): Check for detroyed rwlock.\n\t* rwlock.c: Check return codes from _rwlock_check_need_init();\n\tmodify comments; serialise access to rwlock objects during\n\toperations; rename rw_mutex to rw_lock.\n\t* implement.h: Rename rw_mutex to rw_lock.\n\t* mutex.c (pthread_mutex_destroy): Add serialisation.\n\t(_mutex_check_need_init): Check for detroyed mutex.\n\t* condvar.c (pthread_cond_destroy): Add serialisation.\n\t(_cond_check_need_init): Check for detroyed condvar.\n\t* mutex.c: Modify comments.\n\t* condvar.c: Modify comments.\n\n1999-08-10  Aurelio Medina  <aureliom at crt.com>\n\n\t* implement.h (pthread_rwlock_t_): Add.\n\t* pthread.h (pthread_rwlock_t): Add.\n\t(PTHREAD_RWLOCK_INITIALIZER): Add.\n\tAdd rwlock function prototypes.\n\t* rwlock.c: New module.\n\t* pthread.def: Add new rwlock functions.\n\t* private.c (ptw32_processInitialize): initialise\n\tptw32_rwlock_test_init_lock critical section.\n\t* global.c (ptw32_rwlock_test_init_lock): Add.\n\n\t* mutex.c (pthread_mutex_destroy): Don't free mutex memory\n\tif mutex is PTHREAD_MUTEX_INITIALIZER and has not been\n\tinitialised yet.\n\n1999-08-08 Milan Gardian <mg at tatramed.sk>\n\n\t* mutex.c (pthread_mutex_destroy): Free mutex memory.\n\n1999-08-22  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* exit.c (pthread_exit): Fix reference to potentially\n\tuninitialised pointer.\n\n1999-08-21  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* private.c (ptw32_threadStart): Apply fix of 1999-08-19\n\tthis time to C++ and non-trapped C versions. Ommitted to\n\tdo this the first time through.\n\n1999-08-19  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* private.c (ptw32_threadStart): Return exit status from\n\tthe application thread startup routine.\n\t- Milan Gardian <mg at tatramed.sk>\n\n1999-08-18  John Bossom <john.Bossom at cognos.com>\n\n\t* exit.c (pthread_exit): Put status into pthread_t->exitStatus\n\t* private.c (ptw32_threadStart): Set pthread->exitStatus\n\ton exit of try{} block.\n\t* sync.c (pthread_join): use pthread_exitStatus value if the\n\tthread exit doesn't return a value (for Mingw32 CRTDLL\n\twhich uses endthread instead of _endthreadex).\n\nTue Aug 17 20:17:58 CDT 1999  Mumit Khan  <khan at xraylith.wisc.edu>\n\n        * create.c (pthread_create): Add CRTDLL suppport.\n        * exit.c (pthread_exit): Likewise.\n        * private.c (ptw32_threadStart): Likewise.\n        (ptw32_threadDestroy): Likewise.\n        * sync.c (pthread_join): Likewise.\n        * tests/join1.c (main): Warn about partial support for CRTDLL.\n\nTue Aug 17 20:00:08 1999  Mumit Khan  <khan at xraylith.wisc.edu>\n\n        * Makefile.in (LD): Delete entry point.\n        * acconfig.h (STDCALL): Delete unused macro.\n        * configure.in: Remove test for STDCALL.\n        * config.h.in: Regenerate.\n        * errno.c (_errno): Fix self type.\n        * pthread.h (PT_STDCALL): Move from here to\n        * implement.h (PT_STDCALL): here.\n        (ptw32_threadStart): Fix prototype.\n        * private.c (ptw32_threadStart): Likewise.\n\n1999-08-14  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* exit.c (pthread_exit): Don't call pthread_self() but\n\tget thread handle directly from TSD for efficiency.\n\t\n1999-08-12  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* private.c (ptw32_threadStart): ei[] only declared if _MSC_VER.\n\n\t* exit.c (pthread_exit): Check for implicitly created threads\n\tto avoid raising an unhandled exception.\n\t\n1999-07-12  Peter Slacik <Peter.Slacik at tatramed.sk>\n\n\t* condvar.c (pthread_cond_destroy): Add critical section.\n\t(cond_timedwait): Add critical section; check for timeout\n\twaiting on semaphore.\n\t(pthread_cond_broadcast): Add critical section.\n\n1999-07-09  Lorin Hochstein <lmh at xiphos.ca>, John Bossom <John.Bossom at Cognos.COM>\n\n\tThe problem was that cleanup handlers were not executed when\n\tpthread_exit() was called.\n\n\t* implement.h (pthread_t_): Add exceptionInformation element for\n\tC++ per-thread exception information.\n\t(general): Define and rename exceptions.\n\n1999-07-09  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* misc.c (CancelableWait):  PTW32_EPS_CANCEL (SEH) and\n\tptw32_exception_cancel (C++) used to identify the exception.\n\n\t* cancel.c (pthread_testcancel): PTW32_EPS_CANCEL (SEH) and\n\tptw32_exception_cancel (C++) used to identify the exception.\n\n\t* exit.c (pthread_exit): throw/raise an exception to return to\n\tptw32_threadStart() to exit the thread. PTW32_EPS_EXIT (SEH)\n\tand ptw32_exception_exit (C++) used to identify the exception.\n\n\t* private.c (ptw32_threadStart): Add pthread_exit exception trap;\n\tclean up and exit the thread directly rather than via pthread_exit().\n\nSun May 30 00:25:02 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* semaphore.h (mode_t): Conditionally typedef it.\n\nFri May 28 13:33:05 1999  Mark E. Armstrong <avail at pacbell.net>\n\n\t* condvar.c (pthread_cond_broadcast): Fix possible memory fault\n\t\nThu May 27 13:08:46 1999  Peter Slacik <Peter.Slacik at tatramed.sk>\n\n\t* condvar.c (pthread_cond_broadcast): Fix logic bug\n\nThu May 27 13:08:46 1999  Bossom, John <John.Bossom at Cognos.COM>\n\n\t* condvar.c (pthread_cond_broadcast): optimise sem_post loop\n\nFri May 14 12:13:18 1999  Mike Russo <miker at eai.com>\n\n\t* attr.c (pthread_attr_setdetachstate): Fix logic bug\n\nSat May  8 09:42:30 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.def (sem_open): Add.\n\t(sem_close): Add.\n\t(sem_unlink): Add.\n\t(sem_getvalue): Add.\n\n\t* FAQ (Question 3): Add.\n\nThu Apr  8 01:16:23 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* semaphore.c (sem_open): New function; returns an error (ENOSYS).\n\t(sem_close): ditto.\n\t(sem_unlink): ditto.\n\t(sem_getvalue): ditto.\n\n\t* semaphore.h (_POSIX_SEMAPHORES): define.\n\t\nWed Apr  7 14:09:52 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* errno.c (_REENTRANT || _MT): Invert condition.\n\n\t* pthread.h (_errno): Conditionally include prototype.\n\nWed Apr  7 09:37:00 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* *.c (comments): Remove individual attributions - these are\n\tdocumented sufficiently elsewhere.\n\n\t* implement.h (pthread.h): Remove extraneous include.\n\nSun Apr  4 11:05:57 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* sched.c (sched.h): Include.\n\n\t* sched.h: New file for POSIX 1b scheduling.\n\n\t* pthread.h: Move opaque structures to implement.h; move sched_*\n\tprototypes out and into sched.h.\n\n\t* implement.h: Add opaque structures from pthread.h.\n\n\t* sched.c (sched_yield): New function.\n\n\t* condvar.c (ptw32_sem_*): Rename to sem_*; except for\n\tptw32_sem_timedwait which is an private function.\n\nSat Apr  3 23:28:00 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* Makefile.in (OBJS): Add errno.o.\n\nFri Apr  2 11:08:50 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h (ptw32_sem_*): Remove prototypes now defined in\n\tsemaphore.h.\n\n\t* pthread.h (sempahore.h): Include.\n\n\t* semaphore.h: New file for POSIX 1b semaphores.\n\n\t* semaphore.c (ptw32_sem_timedwait): Moved to private.c.\n\n\t* pthread.h (ptw32_sem_t): Change to sem_t. \n\n\t* private.c (ptw32_sem_timedwait): Moved from semaphore.c;\n\tset errno on error.\n\n\t* pthread.h (pthread_t_): Add per-thread errno element.\n\nFri Apr  2 11:08:50 1999  John Bossom <jebossom at cognos.com>\n\n\t* semaphore.c (ptw32_sem_*): Change to sem_*; these functions\n\twill be exported from the library; set errno on error.\n\n\t* errno.c (_errno): New file. New function.\n\nFri Mar 26 14:11:45 1999  Tor Lillqvist <tml at iki.fi>\n\n\t* semaphore.c (ptw32_sem_timedwait): Check for negative\n\tmilliseconds.\n\nWed Mar 24 11:32:07 1999  John Bossom <jebossom at cognos.com>\n\n\t* misc.c (CancelableWait): Initialise exceptionInformation[2].\n\t(pthread_self): Get a real Win32 thread handle for implicit threads.\n\n\t* cancel.c (pthread_testcancel): Initialise exceptionInformation[2].\n\n\t* implement.h (SE_INFORMATION): Fix values.\n\n\t* private.c (ptw32_threadDestroy): Close the thread handle.\n\nFri Mar 19 12:57:27 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* cancel.c (comments): Update and cleanup.\n\nFri Mar 19 09:12:59 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* private.c (ptw32_threadStart): status returns PTHREAD_CANCELED.\n\n\t* pthread.h (PTHREAD_CANCELED): defined.\n\nTue Mar 16  1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* all: Add GNU LGPL and Copyright and Warranty.\n\t\nMon Mar 15 00:20:13 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* condvar.c (pthread_cond_init): fix possible uninitialised use\n\tof cv.\n\nSun Mar 14 21:01:59 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* condvar.c (pthread_cond_destroy): don't do full cleanup if\n\tstatic initialised cv has never been used.\n\t(cond_timedwait): check result of auto-initialisation.\n\nThu Mar 11 09:01:48 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.h (pthread_mutex_t): revert to (pthread_mutex_t *);\n\tdefine a value to serve as PTHREAD_MUTEX_INITIALIZER.\n\t(pthread_mutex_t_): remove staticinit and valid elements.\n\t(pthread_cond_t): revert to (pthread_cond_t_ *);\n\tdefine a value to serve as PTHREAD_COND_INITIALIZER.\n\t(pthread_cond_t_): remove staticinit and valid elements.\n\n\t* mutex.c (pthread_mutex_t args): adjust indirection of references.\n\t(all functions): check for PTHREAD_MUTEX_INITIALIZER value;\n\tcheck for NULL (invalid).\n\n\t* condvar.c (pthread_cond_t args): adjust indirection of references.\n\t(all functions): check for PTHREAD_COND_INITIALIZER value;\n\tcheck for NULL (invalid).\n\nWed Mar 10 17:18:12 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* misc.c (CancelableWait): Undo changes from Mar 8 and 7.\n\nMon Mar  8 11:18:59 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* misc.c (CancelableWait): Ensure cancelEvent handle is the lowest\n\tindexed element in the handles array. Enhance test for abandoned\n\tobjects.\n\n\t* pthread.h (PTHREAD_MUTEX_INITIALIZER): Trailing elements not\n\tinitialised are set to zero by the compiler. This avoids the\n\tproblem of initialising the opaque critical section element in it.\n\t(PTHREAD_COND_INITIALIZER): Ditto.\n\n\t* semaphore.c (ptw32_sem_timedwait): Check sem == NULL earlier.\n\nSun Mar  7 12:31:14 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* condvar.c (pthread_cond_init): set semaphore initial value\n\tto 0, not 1. cond_timedwait was returning signaled immediately.\n\n\t* misc.c (CancelableWait): Place the cancel event handle first\n\tin the handle table for WaitForMultipleObjects. This ensures that\n\tthe cancel event is recognised and acted apon if both objects\n\thappen to be signaled together.\n\n\t* private.c (ptw32_cond_test_init_lock): Initialise and destroy.\n\n\t* implement.h (ptw32_cond_test_init_lock): Add extern.\n\n\t* global.c (ptw32_cond_test_init_lock): Add declaration. \n\n\t* condvar.c (pthread_cond_destroy): check for valid initialised CV;\n\tflag destroyed CVs as invalid.\n\t(pthread_cond_init): pthread_cond_t is no longer just a pointer.\n\tThis is because PTHREAD_COND_INITIALIZER needs state info to reside\n\tin pthread_cond_t so that it can initialise on first use. Will work on\n\tmaking pthread_cond_t (and other objects like it) opaque again, if\n\tpossible, later.\n\t(cond_timedwait): add check for statically initialisation of\n\tCV; initialise on first use.\n\t(pthread_cond_signal): check for valid CV.\n\t(pthread_cond_broadcast): check for valid CV.\n\t(_cond_check_need_init): Add.\n\n\t* pthread.h (PTHREAD_COND_INITIALIZER): Fix.\n\t(pthread_cond_t): no longer a pointer to pthread_cond_t_.\n\t(pthread_cond_t_): add 'staticinit' and 'valid' elements.\n\nSat Mar 6 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h: Undate comments.\n\nSun Feb 21 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.h (PTHREAD_MUTEX_INITIALIZER): missing braces around\n\tcs element initialiser.\n\n1999-02-21  Ben Elliston  <bje at cygnus.com>\n\n\t* pthread.h (pthread_exit): The return type of this function is\n\tvoid, not int.\n\n\t* exit.c (pthread_exit): Do not return 0.\n\nSat Feb 20 16:03:30 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* dll.c (DLLMain): Expand TryEnterCriticalSection support test.\n\n\t* mutex.c (pthread_mutex_trylock): The check for\n\tptw32_try_enter_critical_section == NULL should have been\n\tremoved long ago.\n\nFri Feb 19 16:03:30 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* sync.c (pthread_join): Fix pthread_equal() test.\n\n\t* mutex.c (pthread_mutex_trylock): Check mutex != NULL before\n\tusing it.\n\nThu Feb 18 16:17:30 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* misc.c (pthread_equal): Fix inverted result.\n\n\t* Makefile.in: Use libpthread32.a as the name of the DLL export\n\tlibrary instead of pthread.lib.\n\n\t* condvar.c (pthread_cond_init): cv could have been used unitialised;\n\tinitialise.\n\n\t* create.c (pthread_create): parms could have been used unitialised;\n\tinitialise.\n\n\t* pthread.h (struct pthread_once_t_): Remove redefinition.\n\nSat Feb 13 03:03:30 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.h (struct pthread_once_t_): Replaced.\n\n\t* misc.c (pthread_once): Replace with John Bossom's version;\n\thas lighter weight serialisation; fixes problem of not holding\n\tcompeting threads until after the init_routine completes.\n\nThu Feb 11 13:34:14 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* misc.c (CancelableWait): Change C++ exception throw.\n\n\t* sync.c (pthread_join): Change FIXME comment - issue resolved.\n\nWed Feb 10 12:49:11 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* configure: Various temporary changes.\n\t- Kevin Ruland <Kevin.Ruland at anheuser-busch.com>\n\n\t* README: Update.\n\n\t* pthread.def (pthread_attr_getstackaddr): uncomment\n\t(pthread_attr_setstackaddr): uncomment\n\nFri Feb  5 13:42:30 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* semaphore.c: Comment format changes.\n\nThu Feb  4 10:07:28 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* global.c: Remove ptw32_exception instantiation.\n\n\t* cancel.c (pthread_testcancel): Change C++ exception throw.\n\n\t* implement.h: Remove extern declaration.\n\nWed Feb  3 13:04:44 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* cleanup.c: Rename ptw32_*_cleanup() to pthread_*_cleanup().\n\n\t* pthread.def: Ditto.\n\t\n\t* pthread.h: Ditto.\n\n\t* pthread.def (pthread_cleanup_push): Remove from export list;\n\tthe function is defined as a macro under all compilers.\n\t(pthread_cleanup_pop): Ditto.\n\n\t* pthread.h: Remove #if defined().\n\nWed Feb  3 10:13:48 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* sync.c (pthread_join): Check for NULL value_ptr arg;\n\tcheck for detached threads.\n\nTue Feb  2 18:07:43 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* implement.h: Add #include <pthread.h>.\n\tChange sem_t to ptw32_sem_t.\n\nTue Feb  2 18:07:43 1999  Kevin Ruland <Kevin.Ruland at anheuser-busch.com>\n\n\t* signal.c (pthread_sigmask): Add and modify casts.\n\tReverse LHS/RHS bitwise assignments.\n\n\t* pthread.h: Remove #include <semaphore.h>.\n\t(PTW32_ATTR_VALID): Add cast.\n\t(struct pthread_t_): Add sigmask element.\n\n\t* dll.c: Add \"extern C\" for DLLMain.\n\t(DllMain): Add cast.\n\n\t* create.c (pthread_create): Set sigmask in thread.\n\n\t* condvar.c: Remove #include. Change sem_* to ptw32_sem_*.\n\n\t* attr.c: Changed #include.\n\n\t* Makefile.in: Additional targets and changes to build the library\n\tas a DLL.\n\nFri Jan 29 11:56:28 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* Makefile.in (OBJS): Add semaphore.o to list.\n\n\t* semaphore.c (ptw32_sem_timedwait): Move from private.c.\n\tRename sem_* to ptw32_sem_*.\n\n\t* pthread.h (pthread_cond_t): Change type of sem_t.\n\t_POSIX_SEMAPHORES no longer defined.\n\n\t* semaphore.h: Contents moved to implement.h.\n\tRemoved from source tree.\n\n\t* implement.h: Add semaphore function prototypes and rename all\n\tfunctions to prepend 'ptw32_'. They are\n\tnow private to the pthreads-win32 implementation.\n\n\t* private.c: Change #warning.\n\tMove ptw32_sem_timedwait() to semaphore.c.\n\n\t* cleanup.c: Change #warning.\n\n\t* misc.c: Remove #include <errno.h>\n\n\t* pthread.def: Cleanup CVS merge conflicts.\n\n\t* global.c: Ditto.\n\n\t* ChangeLog: Ditto.\n\n\t* cleanup.c: Ditto.\n\nSun Jan 24 01:34:52 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* semaphore.c (sem_wait): Remove second arg to \n\tpthreadCancelableWait() call.\n\nSat Jan 23 17:36:40 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.def: Add new functions to export list.\n\n\t* pthread.h (PTHREAD_MUTEX_AUTO_CS_NP): New.\n\t(PTHREAD_MUTEX_FORCE_CS_NP): New.\n\n\t* README: Updated.\n\nFri Jan 22 14:31:59 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* Makefile.in (CFLAGS): Remove -fhandle-exceptions. Not needed\n\twith egcs. Add -g for debugging.\n\n\t* create.c (pthread_create): Replace __stdcall with PT_STDCALL\n\tmacro. This is a hack and must be fixed.\n\n\t* misc.c (CancelableWait): Remove redundant statement.\n\n\t* mutex.c (pthread_mutexattr_init): Cast calloc return value.\n\n\t* misc.c (CancelableWait): Add cast.\n\t(pthread_self): Add cast.\n\n\t* exit.c (pthread_exit): Add cast.\n\n\t* condvar.c (pthread_condattr_init): Cast calloc return value.\n\n\t* cleanup.c: Reorganise conditional compilation.\n\n\t* attr.c (pthread_attr_init): Remove unused 'result'.\n\tCast malloc return value.\n\n\t* private.c (ptw32_callUserDestroyRoutines): Redo conditional\n\tcompilation.\n\n\t* misc.c (CancelableWait): C++ version uses 'throw'.\n\n\t* cancel.c (pthread_testcancel): Ditto.\n\n\t* implement.h (class ptw32_exception): Define for C++.\n\n\t* pthread.h: Fix C, C++, and Win32 SEH condition compilation\n\tmayhem around pthread_cleanup_* defines. C++ version now uses John\n\tBossom's cleanup handlers.\n\t(pthread_attr_t): Make 'valid' unsigned.\n\tDefine '_timeb' as 'timeb' for Ming32.\n\tDefine PT_STDCALL as nothing for Mingw32. May be temporary.\n\n\t* cancel.c (pthread_testcancel): Cast return value.\n\nWed Jan 20 09:31:28 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.h (pthread_mutexattr_t): Changed to a pointer.\n\n\t* mutex.c (pthread_mutex_init): Conditionally create Win32 mutex\n\t- from John Bossom's implementation.\n\t(pthread_mutex_destroy): Conditionally close Win32 mutex\n\t- from John Bossom's implementation.\n\t(pthread_mutexattr_init): Replaced by John Bossom's version.\n\t(pthread_mutexattr_destroy): Ditto.\n\t(pthread_mutexattr_getpshared): New function from John Bossom's\n\timplementation.\n\t(pthread_mutexattr_setpshared): New function from John Bossom's\n\timplementation.\n\nTue Jan 19 18:27:42 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* pthread.h (pthreadCancelableTimedWait): New prototype.\n\t(pthreadCancelableWait): Remove second argument.\n\n\t* misc.c (CancelableWait): New static function is \n\tpthreadCancelableWait() renamed.\n\t(pthreadCancelableWait): Now just calls CancelableWait() with\n\tINFINITE timeout.\n\t(pthreadCancelableTimedWait): Just calls CancelableWait()\n\twith passed in timeout.\n\nTue Jan 19 18:27:42 1999  Scott Lightner <scott at curriculum.com>\n\n\t* private.c (ptw32_sem_timedwait): 'abstime' arg really is\n\tabsolute time. Calculate relative time to wait from current\n\ttime before passing timeout to new routine \n\tpthreadCancelableTimedWait().\n\nTue Jan 19 10:27:39 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.h (pthread_mutexattr_setforcecs_np): New prototype.\n\t\n\t* mutex.c (pthread_mutexattr_init): Init 'pshared' and 'forcecs'\n\tattributes to 0.\n\t(pthread_mutexattr_setforcecs_np): New function (not portable).\n\n\t* pthread.h (pthread_mutex_t): \n\tAdd 'mutex' element. Set to NULL in PTHREAD_MUTEX_INITIALIZER.\n\tThe pthread_mutex_*() routines will try to optimise performance\n\tby choosing either mutexes or critical sections as the basis\n\tfor pthread mutexes for each indevidual mutex.\n\t(pthread_mutexattr_t_): Add 'forcecs' element.\n\tSome applications may choose to force use of critical sections\n\tif they know that:-\n\t     the mutex is PROCESS_PRIVATE and, \n\t         either the OS supports TryEnterCriticalSection() or\n\t         pthread_mutex_trylock() will never be called on the mutex.\n\tThis attribute will be setable via a non-portable routine.\n\n\tNote: We don't yet support PROCESS_SHARED mutexes, so the\n\timplementation as it stands will default to Win32 mutexes only if\n\tthe OS doesn't support TryEnterCriticalSection. On Win9x, and early\n\tversions of NT 'forcecs' will need to be set in order to get\n\tcritical section based mutexes.\n\nSun Jan 17 12:01:26 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.h (PTHREAD_MUTEX_INITIALIZER): Init new 'staticinit'\n\tvalue to '1' and existing 'valid' value to '1'.\n\n\t* global.c (ptw32_mutex_test_init_lock): Add.\n\n\t* implement.h (ptw32_mutex_test_init_lock.): Add extern.\n\n\t* private.c (ptw32_processInitialize): Init critical section for\n\tglobal lock used by _mutex_check_need_init().\n\t(ptw32_processTerminate): Ditto (:s/Init/Destroy/).\n\n\t* dll.c (dllMain): Move call to FreeLibrary() so that it is only\n\tcalled once when the process detaches.\n\n\t* mutex.c (_mutex_check_need_init): New static function to test\n\tand init PTHREAD_MUTEX_INITIALIZER mutexes. Provides serialised\n\taccess to the internal state of the uninitialised static mutex. \n\tCalled from pthread_mutex_trylock() and pthread_mutex_lock() which\n\tdo a quick unguarded test to check if _mutex_check_need_init()\n\tneeds to be called. This is safe as the test is conservative\n \tand is repeated inside the guarded section of \n\t_mutex_check_need_init(). Thus in all calls except the first\n\tcalls to lock static mutexes, the additional overhead to lock any\n\tmutex is a single memory fetch and test for zero.\n\n\t* pthread.h (pthread_mutex_t_): Add 'staticinit' member. Mutexes\n\tinitialised by PTHREAD_MUTEX_INITIALIZER aren't really initialised\n\tuntil the first attempt to lock it. Using the 'valid'\n\tflag (which flags the mutex as destroyed or not) to record this\n\tinformation would be messy. It is possible for a statically\n\tinitialised mutex such as this to be destroyed before ever being\n\tused.\n\n\t* mutex.c (pthread_mutex_trylock): Call _mutex_check_need_init()\n\tto test/init PTHREAD_MUTEX_INITIALIZER mutexes.\n\t(pthread_mutex_lock): Ditto.\n\t(pthread_mutex_unlock): Add check to ensure we don't try to unlock\n\tan unitialised static mutex.\n\t(pthread_mutex_destroy): Add check to ensure we don't try to delete\n\ta critical section that we never created. Allows us to destroy\n\ta static mutex that has never been locked (and hence initialised).\n\t(pthread_mutex_init): Set 'staticinit' flag to 0 for the new mutex.\n\nSun Jan 17 12:01:26 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* private.c (ptw32_sem_timedwait): Move from semaphore.c.\n\n\t* semaphore.c : Remove redundant #includes.\n\t(ptw32_sem_timedwait): Move to private.c.\n\t(sem_wait): Add missing abstime arg to pthreadCancelableWait() call.\n\nFri Jan 15 23:38:05 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* condvar.c (cond_timedwait): Remove comment.\n\nFri Jan 15 15:41:28 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* pthread.h: Add new 'abstime' arg to pthreadCancelableWait()\n\tprototype.\n\n\t* condvar.c (cond_timedwait): New generalised function called by\n\tboth pthread_cond_wait() and pthread_cond_timedwait(). This is\n\tessentially pthread_cond_wait() renamed and modified to add the\n\t'abstime' arg and call the new ptw32_sem_timedwait() instead of\n\tsem_wait().\n\t(pthread_cond_wait): Now just calls the internal static\n\tfunction cond_timedwait() with an INFINITE wait.\n\t(pthread_cond_timedwait): Now implemented. Calls the internal\n\tstatic function cond_timedwait().\n\n\t* implement.h (ptw32_sem_timedwait): New internal function\n\tprototype.\n\n\t* misc.c (pthreadCancelableWait): Added new 'abstime' argument\n\tto allow shorter than INFINITE wait.\n\n\t* semaphore.c (ptw32_sem_timedwait): New function for internal\n\tuse.  This is essentially sem_wait() modified to add the\n        'abstime' arg and call the modified (see above)\n        pthreadCancelableWait().\n\nThu Jan 14 14:27:13 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* cleanup.c: Correct _cplusplus to __cplusplus wherever used.\n\n\t* Makefile.in: Add CC=g++ and add -fhandle-exceptions to CFLAGS.\n\tThe derived Makefile will compile all units of the package as C++\n\tso that those which include try/catch exception handling should work\n\tproperly. The package should compile ok if CC=gcc, however, exception\n\thandling will not be included and thus thread cancellation, for\n \texample, will not work.\n\n\t* cleanup.c (ptw32_pop_cleanup): Add #warning to compile this\n \tfile as C++ if using a cygwin32 environment. Perhaps the whole package\n\tshould be compiled using g++ under cygwin.\n\n\t* private.c (ptw32_threadStart): Change #error directive\n\tinto #warning and bracket for __CYGWIN__ and derivative compilers.\n\nWed Jan 13 09:34:52 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* build.bat: Delete old binaries before compiling/linking.\n\nTue Jan 12 09:58:38 1999  Tor Lillqvist <tml at iki.fi>\n\n\t* dll.c: The Microsoft compiler pragmas probably are more\n\tappropriately protected by _MSC_VER than by _WIN32.\n\n\t* pthread.h: Define ETIMEDOUT. This should be returned by\n\tpthread_cond_timedwait which is not implemented yet as of\n\tsnapshot-1999-01-04-1305. It was implemented in the older version.\n\tThe Microsoft compiler pragmas probably are more appropriately\n\tprotected by _MSC_VER than by _WIN32.\n\n\t* pthread.def: pthread_mutex_destroy was missing from the def file\n\n\t* condvar.c (pthread_cond_broadcast): Ensure we only wait on threads\n\tif there were any waiting on the condition.\n\tI think pthread_cond_broadcast should do the WaitForSingleObject\n\tonly if cv->waiters > 0? Otherwise it seems to hang, at least in the\n\ttestg thread program from glib.\n\nTue Jan 12 09:58:38 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* condvar.c (pthread_cond_timedwait): Fix function description\n\tcomments.\n\n\t* semaphore.c (sem_post): Correct typo in comment.\n\nMon Jan 11 20:33:19 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.h: Re-arrange conditional compile of pthread_cleanup-*\n\tmacros.\n\n\t* cleanup.c (ptw32_push_cleanup): Provide conditional \n\tcompile of cleanup->prev.\n\n1999-01-11  Tor Lillqvist <tml at iki.fi>\n\n\t* condvar.c (pthread_cond_init): Invert logic when testing the\n\treturn value from calloc().\n\nSat Jan  9 14:32:08 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h: Compile-time switch for CYGWIN derived environments\n\tto use CreateThread instead of _beginthreadex. Ditto for ExitThread.\n\tPatch provided by Anders Norlander  <anorland at hem2.passagen.se>.\n\nTue Jan  5 16:33:04 1999  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* cleanup.c (ptw32_pop_cleanup): Add C++ version of __try/__except\n\tblock. Move trailing \"}\" out of #ifdef _WIN32 block left there by\n\t(rpj's) mistake.\n\n\t* private.c: Remove #include <errno.h> which is included by pthread.h.\n\n1998-12-11  Ben Elliston  <bje at toilet.to.cygnus.com>\n\n\t* README: Update info about subscribing to the mailing list.\n\nMon Jan  4 11:23:40 1999  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* all: No code changes, just cleanup.\n\t- remove #if 0 /* Pre Bossom */ enclosed code.\n\t- Remove some redundant #includes.\n\t* pthread.h: Update implemented/unimplemented routines list.\n\t* Tag the bossom merge branch getting ready to merge back to main\n\ttrunk.\n\nTue Dec 29 13:11:16 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h: Move the following struct definitions to pthread.h:\n\tpthread_t_, pthread_attr_t_, pthread_mutex_t_, pthread_mutex_t_,\n\tpthread_mutexattr_t_, pthread_key_t_, pthread_cond_t_,\n\tpthread_condattr_t_, pthread_once_t_.\n\n\t* pthread.h: Add \"_\" prefix to pthread_push_cleanup and \n\tpthread_pop_cleanup internal routines, and associated struct and\n\ttypedefs.\n\n\t* buildlib.bat: Add compile command for semaphore.c\n\n\t* pthread.def: Comment out pthread_atfork routine name. \n\tNow unimplemented.\n\n\t* tsd.c (pthread_setspecific): Rename tkAssocCreate to\n\tptw32_tkAssocCreate.\n\t(pthread_key_delete): Rename tkAssocDestroy to\n\tptw32_tkAssocDestroy.\n\n\t* sync.c (pthread_join): Rename threadDestroy to ptw32_threadDestroy\n\n\t* sched.c (is_attr): attr is now **attr (was *attr), so add extra\n\tNULL pointer test.\n\t(pthread_attr_setschedparam): Increase redirection for attr which is\n\tnow a **.\n\t(pthread_attr_getschedparam): Ditto.\n\t(pthread_setschedparam): Change thread validation and rename \"thread\"\n \tWin32 thread Handle element name to match John Bossom's version.\n\t(pthread_getschedparam): Ditto.\n\n\t* private.c (ptw32_threadDestroy): Rename call to\n\tcallUserDestroyRoutines() as ptw32_callUserDestroyRoutines()\n\n\t* misc.c: Add #include \"implement.h\".\n\n\t* dll.c: Remove defined(KLUDGE) wrapped code.\n\n\t* fork.c: Remove redefinition of ENOMEM.\n\tRemove pthread_atfork() and fork() with #if 0/#endif.\n\n\t* create.c (pthread_create): Rename threadStart and threadDestroy calls\n\tto ptw32_threadStart and ptw32_threadDestroy.\n\n\t* implement.h: Rename \"detachedstate\" to \"detachstate\".\n\n\t* attr.c: Rename \"detachedstate\" to \"detachstate\".\n\nMon Dec 28 09:54:39 1998  John Bossom\n\n\t* semaphore.c: Initial version.\n\t* semaphore.h: Initial version.\n\nMon Dec 28 09:54:39 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.h (pthread_attr_t_): Change to *pthread_attr_t.\n\nMon Dec 28 09:54:39 1998  John Bossom, Ben Elliston\n\n\t* attr.c (pthread_attr_setstacksize): Merge with John's version.\n\t(pthread_attr_getstacksize): Merge with John's version.\n\t(pthread_attr_setstackaddr): Merge with John's version.\n\t(pthread_attr_getstackaddr): Merge with John's version.\n\t(pthread_attr_init): Merge with John's version.\n\t(pthread_attr_destroy): Merge with John's version.\n\t(pthread_attr_getdetachstate): Merge with John's version.\n\t(pthread_attr_setdetachstate): Merge with John's version.\n\t(is_attr): attr is now **attr (was *attr), so add extra NULL pointer\n\ttest.\n\nMon Dec 28 09:54:39 1998  Ross Johnson\n\n\t* implement.h (pthread_attr_t_): Add and rename elements in JEB's\n\tversion to correspond to original, so that it can be used with\n\toriginal attr routines.\n\n\t* pthread.h: Add #endif at end which was truncated in merging.\n\nSun Dec 20 14:51:58 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* misc.c (pthreadCancelableWait): New function by John Bossom. Non-standard\n\tbut provides a hook that can be used to implement cancellation points in\n\tapplications that use this library.\n\n\t* pthread.h (pthread_cleanup_pop): C++ (non-WIN32) version uses\n\ttry/catch to emulate John Bossom's WIN32 __try/__finally behaviour.\n\tIn the WIN32 version __finally block, add a test for AbnormalTermination otherwise\n\tcleanup is only run if the cleanup_pop execute arg is non-zero. Cancellation\n\tshould cause the cleanup to run irrespective of the execute arg.\n\n\t* condvar.c (pthread_condattr_init): Replaced by John Bossom's version.\n\t(pthread_condattr_destroy): Replaced by John Bossom's version.\n\t(pthread_condattr_getpshared): Replaced by John Bossom's version.\n\t(pthread_condattr_setpshared): Replaced by John Bossom's version.\n\t(pthread_cond_init): Replaced by John Bossom's version.\n\tFix comment (refered to mutex rather than condition variable).\n\t(pthread_cond_destroy): Replaced by John Bossom's version.\n\t(pthread_cond_wait): Replaced by John Bossom's version.\n\t(pthread_cond_timedwait): Replaced by John Bossom's version.\n\t(pthread_cond_signal): Replaced by John Bossom's version.\n\t(pthread_cond_broadcast): Replaced by John Bossom's version.\n\nThu Dec 17 19:10:46 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* tsd.c (pthread_key_create): Replaced by John Bossom's version.\n\t(pthread_key_delete): Replaced by John Bossom's version.\n\t(pthread_setspecific): Replaced by John Bossom's version.\n\t(pthread_getspecific): Replaced by John Bossom's version.\n\nMon Dec  7 09:44:40 1998  John Bossom\n\n\t* cancel.c (pthread_setcancelstate): Replaced.\n\t(pthread_setcanceltype): Replaced.\n\t(pthread_testcancel): Replaced.\n\t(pthread_cancel): Replaced.\n\t\n\t* exit.c (pthread_exit): Replaced.\n\n\t* misc.c (pthread_self): Replaced.\n\t(pthread_equal): Replaced.\n\n\t* sync.c (pthread_detach): Replaced.\n\t(pthread_join): Replaced.\n\n\t* create.c (pthread_create): Replaced.\n\n\t* private.c (ptw32_processInitialize): New.\n\t(ptw32_processTerminate): New.\n\t(ptw32_threadStart): New.\n \t(ptw32_threadDestroy): New.\n\t(ptw32_cleanupStack): New.\n\t(ptw32_tkAssocCreate): New.\n\t(ptw32_tkAssocDestroy): New.\n\t(ptw32_callUserDestroyRoutines): New.\n\n\t* implement.h: Added non-API structures and declarations.\n\n\t* dll.c (PthreadsEntryPoint): Cast return value of GetProcAddress\n\tto resolve compile warning from MSVC.\n\n\t* dll.c (DLLmain): Replaced.\n\t* dll.c (PthreadsEntryPoint):\n\tRe-applied Anders Norlander's patch:-\n\tInitialize ptw32_try_enter_critical_section at startup\n\tand release kernel32 handle when DLL is being unloaded.\n\nSun Dec  6 21:54:35 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* buildlib.bat: Fix args to CL when building the .DLL\n\n\t* cleanup.c (ptw32_destructor_run_all): Fix TSD key management.\n\tThis is a tidy-up before TSD and Thread management is completely\n\treplaced by John Bossom's code.\n\n\t* tsd.c (pthread_key_create): Fix TSD key management.\n\n\t* global.c (ptw32_key_virgin_next): Initialise.\n\n\t* build.bat: New DOS script to compile and link a pthreads app\n\tusing Microsoft's CL compiler linker.\n\t* buildlib.bat: New DOS script to compile all the object files\n\tand create pthread.lib and pthread.dll using Microsoft's CL\n\tcompiler linker.\n\n1998-12-05  Anders Norlander  <anorland at hem2.passagen.se>\n\n\t* implement.h (ptw32_try_enter_critical_section): New extern\n\t* dll.c (ptw32_try_enter_critical_section): New pointer to\n\tTryEnterCriticalSection if it exists; otherwise NULL.\n\t* dll.c (PthreadsEntryPoint):\n\tInitialize ptw32_try_enter_critical_section at startup\n\tand release kernel32 handle when DLL is being unloaded.\n\t* mutex.c (pthread_mutex_trylock): Replaced check for NT with\n\ta check if ptw32_try_enter_critical_section is valid\n\tpointer to a function. Call ptw32_try_enter_critical_section\n\tinstead of TryEnterCriticalSection to avoid errors on Win95.\n\nThu Dec 3 13:32:00 1998  Ross Johnson  <rpj at ise.canberra.edu.au>\n\n\t* README: Correct cygwin32 compatibility statement.\n\nSun Nov 15 21:24:06 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* cleanup.c (ptw32_destructor_run_all): Declare missing void * arg.\n\tFixup CVS merge conflicts.\n\n1998-10-30  Ben Elliston  <bje at cygnus.com>\n\n\t* condvar.c (cond_wait): Fix semantic error. Test for equality\n\tinstead of making an assignment.\n\nFri Oct 30 15:15:50 1998  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* cleanup.c (ptw32_handler_push): Fixed bug appending new\n\thandler to list reported by Peter Slacik\n\t<Peter.Slacik at leibinger.freinet.de>.\n\t(new_thread): Rename poorly named local variable to\n\t\"new_handler\".\n\nSat Oct 24 18:34:59 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* global.c: Add TSD key management array and index declarations.\n\n\t* implement.h: Ditto for externs.\n\nFri Oct 23 00:08:09 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h (PTW32_TSD_KEY_REUSE): Add enum.\n\n\t* private.c (ptw32_delete_thread): Add call to\n\tptw32_destructor_run_all() to clean up the threads keys.\n\n\t* cleanup.c (ptw32_destructor_run_all): Check for no more dirty\n\tkeys to run destructors on. Assume that the destructor call always\n\tsucceeds and set the key value to NULL.\n\nThu Oct 22 21:44:44 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* tsd.c (pthread_setspecific): Add key management code.\n\t(pthread_key_create): Ditto.\n\t(pthread_key_delete): Ditto.\n\n\t* implement.h (struct ptw32_tsd_key): Add status member.\n\n\t* tsd.c: Add description of pthread_key_delete() from the\n\tstandard as a comment.\n\nFri Oct 16 17:38:47 1998  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* cleanup.c (ptw32_destructor_run_all): Fix and improve\n\tstepping through the key table.\n\nThu Oct 15 14:05:01 1998  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* private.c (ptw32_new_thread): Remove init of destructorstack.\n\tNo longer an element of pthread_t.\n\n\t* tsd.c (pthread_setspecific): Fix type declaration and cast.\n\t(pthread_getspecific): Ditto.\n\t(pthread_getspecific): Change error return value to NULL if key\n\tis not in use.\n\nThu Oct 15 11:53:21 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* global.c (ptw32_tsd_key_table): Fix declaration.\n\n\t* implement.h(ptw32_TSD_keys_TlsIndex): Add missing extern.\n\t(ptw32_tsd_mutex): Ditto.\n\n\t* create.c (ptw32_start_call): Fix \"keys\" array declaration.\n\tAdd comment.\n\n\t* tsd.c (pthread_setspecific): Fix type declaration and cast.\n\t(pthread_getspecific): Ditto.\n\n\t* cleanup.c (ptw32_destructor_run_all): Declare missing loop\n\tcounter.\n\nWed Oct 14 21:09:24 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* private.c (ptw32_new_thread): Increment ptw32_threads_count.\n\t(ptw32_delete_thread): Decrement ptw32_threads_count.\n\tRemove some comments.\n\n\t* exit.c (ptw32_exit): : Fix two pthread_mutex_lock() calls that\n \tshould have been pthread_mutex_unlock() calls.\n\t(ptw32_vacuum): Remove call to ptw32_destructor_pop_all().\n\n\t* create.c (pthread_create): Fix two pthread_mutex_lock() calls that\n \tshould have been pthread_mutex_unlock() calls.\n\n\t* global.c (ptw32_tsd_mutex): Add mutex for TSD operations.\n\n\t* tsd.c (pthread_key_create): Add critical section.\n\t(pthread_setspecific): Ditto.\n\t(pthread_getspecific): Ditto.\n\t(pthread_key_delete): Ditto.\n\n\t* sync.c (pthread_join): Fix two pthread_mutex_lock() calls that\n \tshould have been pthread_mutex_unlock() calls.\n\nMon Oct 12 00:00:44 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h (ptw32_tsd_key_table): New.\n\n\t* create.c (ptw32_start_call): Initialise per-thread TSD keys\n\tto NULL.\n\n\t* misc.c (pthread_once): Correct typo in comment.\n\n\t* implement.h (ptw32_destructor_push): Remove.\n\t(ptw32_destructor_pop): Remove.\n\t(ptw32_destructor_run_all): Rename from ptw32_destructor_pop_all.\n\t(PTW32_TSD_KEY_DELETED): Add enum.\n\t(PTW32_TSD_KEY_INUSE): Add enum.\n\n\t* cleanup.c (ptw32_destructor_push): Remove.\n\t(ptw32_destructor_pop): Remove.\n\t(ptw32_destructor_run_all): Totally revamped TSD.\n\n\t* dll.c (ptw32_TSD_keys_TlsIndex): Initialise.\n\n\t* tsd.c (pthread_setspecific): Totally revamped TSD.\n\t(pthread_getspecific): Ditto.\n\t(pthread_create): Ditto.\n\t(pthread_delete): Ditto.\n\nSun Oct 11 22:44:55 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* global.c (ptw32_tsd_key_table): Add new global.\n\n\t* implement.h (ptw32_tsd_key_t and struct ptw32_tsd_key):\n\tAdd.\n\t(struct _pthread): Remove destructorstack.\n\n\t* cleanup.c (ptw32_destructor_run_all): Rename from\n \tptw32_destructor_pop_all. The key destructor stack was made\n \tglobal rather than per-thread. No longer removes destructor nodes\n\tfrom the stack. Comments updated.\n\n1998-10-06  Ben Elliston  <bje at cygnus.com>\n\n\t* condvar.c (cond_wait): Use POSIX, not Win32 mutex calls.\n\t(pthread_cond_broadcast): Likewise.\n\t(pthread_cond_signal): Likewise.\n\n1998-10-05  Ben Elliston  <bje at cygnus.com>\n\n\t* pthread.def: Update. Some functions aren't available yet, others\n\tare macros in <pthread.h>.\n\n\t* tests/join.c: Remove; useless.\n\nMon Oct  5 14:25:08 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* pthread.def: New file for building the DLL.\n\n1998-10-05  Ben Elliston  <bje at cygnus.com>\n\n\t* misc.c (pthread_equal): Correct inverted logic bug.\n\t(pthread_once): Use the POSIX mutex primitives, not Win32. Remove\n\tirrelevant FIXME comment.\n\n\t* global.c (PTHREAD_MUTEX_INITIALIZER): Move to pthread.h.\n\n\t* pthread.h (PTHREAD_MUTEX_INITIALIZER): Define.\n\t(pthread_mutex_t): Reimplement as a struct containing a valid\n\tflag. If the flag is ever down upon entry to a mutex operation,\n\twe call pthread_mutex_create() to initialise the object. This\n\tfixes the problem of how to handle statically initialised objects\n\tthat can't call InitializeCriticalSection() due to their context.\n\t(PTHREAD_ONCE_INIT): Define.\n\n\t* mutex.c (pthread_mutex_init): Set valid flag.\n\t(pthread_mutex_destroy): Clear valid flag.\n\t(pthread_mutex_lock): Check and handle the valid flag.\n\t(pthread_mutex_unlock): Likewise.\n\t(pthread_mutex_trylock): Likewise.\n\n\t* tests/mutex3.c: New file; test for the static initialisation\n\tmacro. Passes.\n\n\t* tests/create1.c: New file; test pthread_create(). Passes.\n\t\n\t* tests/equal.c: Poor test; remove.\n\t\n\t* tests/equal1.c New file; test pthread_equal(). Passes.\n\n\t* tests/once1.c: New file; test for pthread_once(). Passes.\n\n\t* tests/self.c: Remove; rename to self1.c.\n\n\t* tests/self1.c: This is the old self.c.\n\n\t* tests/self2.c: New file. Test pthread_self() with a single\n\tthread. Passes.\n\n\t* tests/self3.c: New file. Test pthread_self() with a couple of\n\tthreads to ensure their thread IDs differ. Passes.\n\t\n1998-10-04  Ben Elliston  <bje at cygnus.com>\n\n\t* tests/mutex2.c: Test pthread_mutex_trylock(). Passes.\n\n\t* tests/mutex1.c: New basic test for mutex functions (it passes).\n\t(main): Eliminate warning.\n\n\t* configure.in: Test for __stdcall, not _stdcall. Typo.\n\n\t* configure: Regenerate.\n\n\t* attr.c (pthread_attr_setstackaddr): Remove FIXME comment. Win32\n\tdoes know about ENOSYS after all.\n\t(pthread_attr_setstackaddr): Likewise.\n\n1998-10-03  Ben Elliston  <bje at cygnus.com>\n\n\t* configure.in: Test for the `_stdcall' keyword.  Define `STDCALL'\n\tto `_stdcall' if we have it, null otherwise.\n\n\t* configure: Regenerate.\n\n\t* acconfig.h (STDCALL): New define.\n\n\t* config.h.in: Regenerate.\n\n\t* create.c (ptw32_start_call): Add STDCALL prefix.\n\t\n\t* mutex.c (pthread_mutex_init): Correct function signature.\n\n\t* attr.c (pthread_attr_init): Only zero out the `sigmask' member\n\tif we have the sigset_t type.\n\n\t* pthread.h: No need to include <unistd.h>.  It doesn't even exist\n\ton Win32! Again, an artifact of cross-compilation.\t\n\t(pthread_sigmask): Only provide if we have the sigset_t type.\n\n\t* process.h: Remove. This was a stand-in before we started doing\n\tnative compilation under Win32.\n\n\t* pthread.h (pthread_mutex_init): Make `attr' argument const.\n\n1998-10-02  Ben Elliston  <bje at cygnus.com>\n\n\t* COPYING: Remove.\n\n\t* COPYING.LIB: Add. This library is under the LGPL.\n\n1998-09-13  Ben Elliston  <bje at cygnus.com>\n\n\t* configure.in: Test for required system features.\n\n\t* configure: Generate. \n\n\t* acconfig.h: New file.\n\n\t* config.h.in: Generate.\n\n\t* Makefile.in: Renamed from Makefile.\n\n\t* COPYING: Import from a recent GNU package.\n\n\t* config.guess: Likewise.\n\n\t* config.sub: Likewise.\n\n\t* install-sh: Likewise.\n\n\t* config.h: Remove.  \n\n\t* Makefile: Likewise.\n\n1998-09-12  Ben Elliston  <bje at cygnus.com>\n\n\t* windows.h: No longer needed; remove.\n\n\t* windows.c: Likewise.\n\nSat Sep 12 20:09:24 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* windows.h: Remove error number definitions. These are in <errno.h>\n\t\n\t* tsd.c: Add comment explaining rationale for not building\n\tPOSIX TSD on top of Win32 TLS.\n\n1998-09-12  Ben Elliston  <bje at cygnus.com>\n\n\t* {most}.c: Include <errno.h> to get POSIX error values.\n\n\t* signal.c (pthread_sigmask): Only provide if HAVE_SIGSET_T is\n\tdefined.\n \n\t* config.h: #undef features, don't #define them.  This will be\n\tgenerated by autoconf very soon.\n\t\n1998-08-11  Ben Elliston  <bje at cygnus.com>\n\n\t* Makefile (LIB): Define.\n\t(clean): Define target.\n\t(all): Build a library not just the object files.\n\n\t* pthread.h: Provide a definition for struct timespec if we don't\n\talready have one.\n\n\t* windows.c (TlsGetValue): Bug fix.\n\t\nThu Aug  6 15:19:22 1998  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* misc.c (pthread_once): Fix arg 1 of EnterCriticalSection()\n \tand LeaveCriticalSection() calls to pass address-of lock.\n\n\t* fork.c (pthread_atfork): Typecast (void (*)(void *)) funcptr\n\tin each ptw32_handler_push() call.\n\n\t* exit.c (ptw32_exit): Fix attr arg in \n\tpthread_attr_getdetachstate() call.\n\n\t* private.c (ptw32_new_thread): Typecast (HANDLE) NULL.\n\t(ptw32_delete_thread): Ditto.\n\n\t* implement.h: (PTW32_MAX_THREADS): Add define. This keeps\n\tchanging in an attempt to make thread administration data types\n\topaque and cleanup DLL startup.\n\n\t* dll.c (PthreadsEntryPoint): \n\t(ptw32_virgins): Remove malloc() and free() calls.\n\t(ptw32_reuse): Ditto.\n\t(ptw32_win32handle_map): Ditto.\n\t(ptw32_threads_mutex_table): Ditto.\n\n\t* global.c (_POSIX_THREAD_THREADS_MAX): Initialise with \n\tPTW32_MAX_THREADS.\n\t(ptw32_virgins): Ditto.\n\t(ptw32_reuse): Ditto.\n\t(ptw32_win32handle_map): Ditto.\n\t(ptw32_threads_mutex_table): Ditto.\n\n\t* create.c (pthread_create): Typecast (HANDLE) NULL.\n\tTypecast (unsigned (*)(void *)) start_routine.\n\n\t* condvar.c (pthread_cond_init): Add address-of operator & to\n\targ 1 of pthread_mutex_init() call.\n\t(pthread_cond_destroy): Add address-of operator & to\n\targ 1 of pthread_mutex_destroy() call. \n\n\t* cleanup.c (ptw32_destructor_pop_all): Add (int) cast to \n\tpthread_getspecific() arg.\n\t(ptw32_destructor_pop): Add (void *) cast to \"if\" conditional.\n\t(ptw32_destructor_push): Add (void *) cast to\n\tptw32_handler_push() \"key\" arg.\n\t(malloc.h): Add include.\n\n\t* implement.h (ptw32_destructor_pop): Add prototype.\n\n\t* tsd.c (implement.h): Add include.\n\n\t* sync.c (pthread_join): Remove target_thread_mutex and it's\n\tinitialisation. Rename getdetachedstate to getdetachstate.\n\tRemove unused variable \"exitcode\".\n\t(pthread_detach): Remove target_thread_mutex and it's\n\tinitialisation. Rename getdetachedstate to getdetachstate.\n\tRename setdetachedstate to setdetachstate.\n\n\t* signal.c (pthread_sigmask): Rename SIG_SET to SIG_SETMASK.\n\tCast \"set\" to (long *) in assignment to passify compiler warning.\n\tAdd address-of operator & to thread->attr.sigmask in memcpy() call\n\tand assignment.\n\t(pthread_sigmask): Add address-of operator & to thread->attr.sigmask\n\tin memcpy() call and assignment.\n\n\t* windows.h (THREAD_PRIORITY_ERROR_RETURN): Add.\n\t(THREAD_PRIORITY_LOWEST): Add.\n\t(THREAD_PRIORITY_HIGHEST): Add.\n\n\t* sched.c (is_attr): Add function.\n\t(implement.h): Add include.\n\t(pthread_setschedparam): Rename all instances of \"sched_policy\"\n\tto \"sched_priority\".\n\t(pthread_getschedparam): Ditto.\n\nTue Aug  4 16:57:58 1998  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* private.c (ptw32_delete_thread): Fix typo. Add missing ';'.\n\n\t* global.c (ptw32_virgins): Change types from pointer to \n\tarray pointer.\n\t(ptw32_reuse): Ditto.\n\t(ptw32_win32handle_map): Ditto.\n\t(ptw32_threads_mutex_table): Ditto.\n\n\t* implement.h(ptw32_virgins): Change types from pointer to \n\tarray pointer.\n\t(ptw32_reuse): Ditto.\n\t(ptw32_win32handle_map): Ditto.\n\t(ptw32_threads_mutex_table): Ditto.\n\n\t* private.c (ptw32_delete_thread): Fix \"entry\" should be \"thread\".\n\n\t* misc.c (pthread_self): Add extern for ptw32_threadID_TlsIndex.\n\n\t* global.c: Add comment.\n\n\t* misc.c (pthread_once): Fix member -> dereferences.\n\tChange ptw32_once_flag to once_control->flag in \"if\" test.\n\nTue Aug  4 00:09:30 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h(ptw32_virgins): Add extern.\n\t(ptw32_virgin_next): Ditto.\n\t(ptw32_reuse): Ditto.\n\t(ptw32_reuse_top): Ditto.\n\t(ptw32_win32handle_map): Ditto.\n\t(ptw32_threads_mutex_table): Ditto.\n\n\t* global.c (ptw32_virgins): Changed from array to pointer.\n\tStorage allocation for the array moved into dll.c.\n\t(ptw32_reuse): Ditto.\n\t(ptw32_win32handle_map): Ditto.\n\t(ptw32_threads_mutex_table): Ditto.\n\n\t* dll.c (PthreadsEntryPoint): Set up thread admin storage when\n\tDLL is loaded.\n\n\t* fork.c (pthread_atfork): Fix function pointer arg to all\n\tptw32_handler_push() calls. Change \"arg\" arg to NULL in child push.\n\n\t* exit.c: Add windows.h and process.h includes.\n\t(ptw32_exit): Add local detachstate declaration.\n\t(ptw32_exit): Fix incorrect name for pthread_attr_getdetachstate().\n\n\t* pthread.h (_POSIX_THREAD_ATTR_STACKSIZE): Move from global.c\n\t(_POSIX_THREAD_ATTR_STACKADDR): Ditto.\n\n\t* create.c (pthread_create): Fix #if should be #ifdef.\n\t(ptw32_start_call): Remove usused variables.\n\n\t* process.h: Create.\n\n\t* windows.h: Move _beginthreadex and _endthreadex into\n\tprocess.h\n\nMon Aug  3 21:19:57 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* condvar.c (pthread_cond_init): Add NULL attr to\n\tpthread_mutex_init() call - default attributes will be used.\n\t(cond_wait): Fix typo.\n\t(cond_wait): Fix typo - cv was ev.\n\t(pthread_cond_broadcast): Fix two identical typos.\n\n\t* cleanup.c (ptw32_destructor_pop_all): Remove _ prefix from\n\tPTHREAD_DESTRUCTOR_ITERATIONS.\n\n\t* pthread.h: Move _POSIX_* values into posix.h\n\n\t* pthread.h: Fix typo in pthread_mutex_init() prototype.\n\n\t* attr.c (pthread_attr_init): Fix error in priority member init.\n\n\t* windows.h (THREAD_PRIORITY_NORMAL): Add.\n\n\t* pthread.h (sched_param): Add missing ';' to struct definition. \n\n\t* attr.c (pthread_attr_init): Remove obsolete pthread_attr_t\n\tmember initialisation - cancelstate, canceltype, cancel_pending.\n\t(is_attr): Make arg \"attr\" a const.\n\n\t* implement.h (PTW32_HANDLER_POP_LIFO): Remove definition.\n\t(PTW32_HANDLER_POP_FIFO): Ditto.\n\t(PTW32_VALID): Add missing newline escape (\\).\n\t(ptw32_handler_node): Make element \"next\" a pointer.\n\n1998-08-02  Ben Elliston  <bje at cygnus.com>\n\n\t* windows.h: Remove duplicate TlsSetValue() prototype.  Add \n\tTlsGetValue() prototype.\n\t(FALSE): Define.\n\t(TRUE): Likewise.\n\tAdd forgotten errno values.  Guard against multiple #includes.\n\n\t* windows.c: New file.  Implement stubs for Win32 functions.\n\n\t* Makefile (SRCS): Remove.  Not explicitly needed.\n\t(CFLAGS): Add -Wall for all warnings with GCC.\n\nSun Aug  2 19:03:42 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* config.h: Create. This is a temporary stand-in for autoconf yet\n\tto be done.\n \t(HAVE_SIGNAL_H): Add.\n\n\t* pthread.h: Minor rearrangement for temporary config.h.\n\nFri Jul 31 14:00:29 1998  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* cleanup.c (ptw32_destructor_pop): Implement. Removes\n\tdestructors associated with a key without executing them.\n\t(ptw32_destructor_pop_all): Add FIXME comment.\n\n\t* tsd.c (pthread_key_delete): Add call to ptw32_destructor_pop().\n\nFri Jul 31 00:05:45 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* tsd.c (pthread_key_create): Update to properly associate\n\tthe destructor routine with the key.\n\t(pthread_key_delete): Add FIXME comment.\n\n\t* exit.c (ptw32_vacuum): Add call to\n\tptw32_destructor_pop_all().\n\n\t* implement.h (ptw32_handler_pop_all): Add prototype.\n\t(ptw32_destructor_pop_all): Ditto.\n\n\t* cleanup.c (ptw32_destructor_push): Implement. This is just a\n\tcall to ptw32_handler_push().\n\t(ptw32_destructor_pop_all): Implement. This is significantly\n\tdifferent to ptw32_handler_pop_all().\n\n\t* Makefile (SRCS): Create. Preliminary.\n\n\t* windows.h: Create. Contains Win32 definitions for compile\n\ttesting. This is just a standin for the real one.\n\n\t* pthread.h (SIG_UNBLOCK): Fix typo. Was SIG_BLOCK.\n\t(windows.h): Add include. Required for CRITICAL_SECTION.\n\t(pthread_cond_t): Move enum declaration outside of struct\n\tdefinition.\n\t(unistd.h): Add include - may be temporary.\n\n\t* condvar.c (windows.h): Add include.\n\n\t* implement.h (PTW32_THIS): Remove - no longer required.\n\t(PTW32_STACK): Use pthread_self() instead of PTW32_THIS.\n\nThu Jul 30 23:12:45 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h: Remove ptw32_find_entry() prototype.\n\n\t* private.c: Extend comments.\n\tRemove ptw32_find_entry() - no longer needed.\n\n\t* create.c (ptw32_start_call): Add call to TlsSetValue() to\n\tstore the thread ID.\n\n\t* dll.c (PthreadsEntryPoint): Implement. This is called\n\twhenever a process loads the DLL. Used to initialise thread\n\tlocal storage.\n\n\t* implement.h: Add ptw32_threadID_TlsIndex.\n\tAdd ()s around PTW32_VALID expression.\n\n\t* misc.c (pthread_self): Re-implement using Win32 TLS to store\n\tthe threads own ID.\n\nWed Jul 29 11:39:03 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* private.c: Corrections in comments.\n\t(ptw32_new_thread): Alter \"if\" flow to be more natural.\n\n\t* cleanup.c (ptw32_handler_push): Same as below.\n\n\t* create.c (pthread_create): Same as below.\n\n\t* private.c (ptw32_new_thread): Rename \"new\" to \"new_thread\".\n\tSince when has a C programmer been required to know C++?\n\nTue Jul 28 14:04:29 1998  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* implement.h: Add PTW32_VALID macro.\n\n\t* sync.c (pthread_join): Modify to use the new thread\n\ttype and ptw32_delete_thread(). Rename \"target\" to \"thread\".\n\tRemove extra local variable \"target\".\n\t(pthread_detach): Ditto.\n\n\t* signal.c (pthread_sigmask): Move init of \"us\" out of inner block.\n\tFix instance of \"this\" should have been \"us\". Rename \"us\" to \"thread\".\n\n\t* sched.c (pthread_setschedparam): Modify to use the new thread\n\ttype.\n\t(pthread_getschedparam): Ditto.\n\n\t* private.c (ptw32_find_thread): Fix return type and arg.\n\n\t* implement.h: Remove PTW32_YES and PTW32_NO.\n\t(ptw32_new_thread): Add prototype.\n\t(ptw32_find_thread): Ditto.\n\t(ptw32_delete_thread): Ditto.\n\t(ptw32_new_thread_entry): Remove prototype.\n\t(ptw32_find_thread_entry): Ditto.\n\t(ptw32_delete_thread_entry): Ditto.\n\t(  PTW32_NEW, PTW32_INUSE, PTW32_EXITED, PTW32_REUSE):\n\tAdd.\n\n\n\t* create.c (pthread_create): Minor rename \"us\" to \"new\" (I need\n\tthese cues but it doesn't stop me coming out with some major bugs\n\tat times).\n\tLoad start_routine and arg into the thread so the wrapper can\n\tcall it.\n\n\t* exit.c (pthread_exit): Fix pthread_this should be pthread_self.\n\n\t* cancel.c (pthread_setcancelstate): Change\n \tptw32_threads_thread_t * to pthread_t and init with\n \tpthread_this().\n\t(pthread_setcanceltype): Ditto.\n\n\t* exit.c (ptw32_exit): Add new pthread_t arg.\n\tRename ptw32_delete_thread_entry to ptw32_delete_thread.\n\tRename \"us\" to \"thread\".\n\t(pthread_exit): Call ptw32_exit with added thread arg.\n\n\t* create.c (ptw32_start_call): Insert missing \")\".\n\tAdd \"us\" arg to ptw32_exit() call.\n\t(pthread_create): Modify to use new thread allocation scheme.\n\n\t* private.c: Added detailed explanation of the new thread\n\tallocation scheme.\n\t(ptw32_new_thread): Totally rewritten to use\n\tnew thread allocation scheme.\n\t(ptw32_delete_thread): Ditto.\n\t(ptw32_find_thread): Obsolete.\n\nMon Jul 27 17:46:37 1998  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* create.c (pthread_create): Start of rewrite. Not completed yet.\n\n\t* private.c (ptw32_new_thread_entry): Start of rewrite. Not\n\tcomplete.\n\n\t* implement.h (ptw32_threads_thread): Rename, remove thread\n\tmember, add win32handle and ptstatus members.\n\t(ptw32_t): Add.\n\n\t* pthread.h: pthread_t is no longer mapped directly to a Win32\n\tHANDLE type. This is so we can let the Win32 thread terminate and\n\treuse the HANDLE while pthreads holds it's own thread ID until\n\tthe last waiting join exits.\n\nMon Jul 27 00:20:37 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* private.c (ptw32_delete_thread_entry): Destroy the thread\n \tentry attribute object before deleting the thread entry itself.\n\n\t* attr.c (pthread_attr_init): Initialise cancel_pending = FALSE.\n\t(pthread_attr_setdetachstate): Rename \"detached\" to \"detachedstate\".\n\t(pthread_attr_getdetachstate): Ditto.\n\n\t* exit.c (ptw32_exit): Fix incorrect check for detachedstate.\n\n\t* implement.h (ptw32_call_t): Remove env member. \n\nSun Jul 26 13:06:12 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h (ptw32_new_thread_entry): Fix prototype.\n\t(ptw32_find_thread_entry): Ditto.\n\t(ptw32_delete_thread_entry): Ditto.\n\t(ptw32_exit): Add prototype.\n\n\t* exit.c (ptw32_exit): New function. Called from pthread_exit()\n\tand ptw32_start_call() to exit the thread. It allows an extra\n\targument which is the return code passed to _endthreadex().\n\t(ptw32_exit): Move thread entry delete call from ptw32_vacuum()\n\tinto here. Add more explanation of thread entry deletion.\n\t(ptw32_exit): Clarify comment.\n\n\t* create.c (ptw32_start_call): Change pthread_exit() call to\n\tptw32_exit() call.\n\n\t* exit.c (ptw32_vacuum): Add thread entry deletion code\n\tmoved from ptw32_start_call(). See next item.\n\t(pthread_exit): Remove longjmp(). Add mutex lock around thread table\n\tmanipulation code. This routine now calls _enthreadex().\n\n\t* create.c (ptw32_start_call): Remove setjmp() call and move\n\tcleanup code out. Call pthread_exit(NULL) to terminate the thread.\n\n1998-07-26  Ben Elliston  <bje at cygnus.com>\n\n\t* tsd.c (pthread_getspecific): Update comments.\n\n\t* mutex.c (pthread_mutexattr_setpshared): Not supported; remove.\n\t(pthread_mutexattr_getpshared): Likewise.\n\n\t* pthread.h (pthread_mutexattr_setpshared): Remove prototype.\n\t(pthread_mutexattr_getpshared): Likewise.\n\nSun Jul 26 00:09:59 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* sync.c: Rename all instances of ptw32_count_mutex to\n\tptw32_table_mutex.\n\n\t* implement.h: Rename ptw32_count_mutex to\n\tptw32_table_mutex.\n\n\t* global.c: Rename ptw32_count_mutex to\n\tptw32_table_mutex.\n\n\t* create.c (pthread_create): Add critical sections.\n\t(ptw32_start_call): Rename ptw32_count_mutex to\n\tptw32_table_mutex.\n\n\t* cancel.c (pthread_setcancelstate): Fix indirection bug and rename\n\t\"this\" to \"us\".\n\n\t* signal.c (pthread_sigmask): Rename \"this\" to \"us\" and fix some\n\tminor syntax errors. Declare \"us\" and initialise it.\n\n\t* sync.c (pthread_detach): Rename \"this\" to \"target\".\n\n\t* pthread.h: Converting PTHREAD_* defines to alias the (const int)\n\tvalues in global.c.\n\n\t* global.c: Started converting PTHREAD_* defines to (const int) as\n \ta part of making the eventual pthreads DLL binary compatible\n \tthrough version changes.\n\n\t* condvar.c (cond_wait): Add cancelation point. This applies the\n\tpoint to both pthread_cond_wait() and pthread_cond_timedwait().\n\n\t* exit.c (pthread_exit): Rename \"this\" to \"us\".\n\n\t* implement.h: Add comment.\n\n\t* sync.c (pthread_join): I've satisfied myself that pthread_detach()\n\tdoes set the detached attribute in the thread entry attributes\n\tto PTHREAD_CREATE_DETACHED. \"if\" conditions were changed to test\n\tthat attribute instead of a separate flag.\n\n\t* create.c (pthread_create): Rename \"this\" to \"us\".\n\t(pthread_create): cancelstate and canceltype are not attributes\n\tso the copy to thread entry attribute storage was removed.\n\tOnly the thread itself can change it's cancelstate or canceltype,\n\tie. the thread must exist already.\n\n\t* private.c (ptw32_delete_thread_entry): Mutex locks removed.\n\tMutexes must be applied at the caller level.\n\t(ptw32_new_thread_entry): Ditto.\n\t(ptw32_new_thread_entry): Init cancelstate, canceltype, and\n\tcancel_pending to default values.\n\t(ptw32_new_thread_entry): Rename \"this\" to \"new\".\n\t(ptw32_find_thread_entry): Rename \"this\" to \"entry\".\n\t(ptw32_delete_thread_entry): Rename \"thread_entry\" to \"entry\".\n\n\t* create.c (ptw32_start_call): Mutexes changed to\n\tptw32_count_mutex. All access to the threads table entries is\n\tunder the one mutex. Otherwise chaos reigns.\n\nSat Jul 25 23:16:51 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h (ptw32_threads_thread): Move cancelstate and\n \tcanceltype members out of pthread_attr_t into here.\n\n\t* fork.c (fork): Add comment.\n\n1998-07-25  Ben Elliston  <bje at cygnus.com>\n\n\t* fork.c (fork): Autoconfiscate.\n\nSat Jul 25 00:00:13 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* create.c (ptw32_start_call): Set thread priority.  Ensure our\n \tthread entry is removed from the thread table but only if\n \tpthread_detach() was called and there are no waiting joins.\n\t(pthread_create): Set detach flag in thread entry if the \n\tthread is created PTHREAD_CREATE_DETACHED.\n\n\t* pthread.h (pthread_attr_t): Rename member \"detachedstate\".\n\n\t* attr.c (pthread_attr_init): Rename attr members.\n\n\t* exit.c (pthread_exit): Fix indirection mistake.\n\n\t* implement.h (PTW32_THREADS_TABLE_INDEX): Add.\n\n\t* exit.c (ptw32_vacuum): Fix incorrect args to\n\tptw32_handler_pop_all() calls.\n\tMake thread entry removal conditional.\n\n\t* sync.c (pthread_join): Add multiple join and async detach handling.\n\n\t* implement.h (PTW32_THREADS_TABLE_INDEX): Add.\n\n\t* global.c (ptw32_threads_mutex_table): Add.\n\n\t* implement.h (ptw32_once_flag): Remove.\n\t(ptw32_once_lock): Ditto.\n\t(ptw32_threads_mutex_table): Add.\n\n\t* global.c (ptw32_once_flag): Remove.\n\t(ptw32_once_lock): Ditto.\n\n\t* sync.c (pthread_join): Fix tests involving new return value\n\tfrom ptw32_find_thread_entry().\n\t(pthread_detach): Ditto.\n\n\t* private.c (ptw32_find_thread_entry): Failure return code\n\tchanged from -1 to NULL.\n\nFri Jul 24 23:09:33 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* create.c (pthread_create): Change . to -> in sigmask memcpy() args.\n\n\t* pthread.h: (pthread_cancel): Add function prototype.\n\t(pthread_testcancel): Ditto.\n\n1998-07-24  Ben Elliston  <bje at cygnus.com>\n\n\t* pthread.h (pthread_condattr_t): Rename dummy structure member.\n\t(pthread_mutexattr_t): Likewise.\n\nFri Jul 24 21:13:55 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* cancel.c (pthread_cancel): Implement.\n\t(pthread_testcancel): Implement.\n\n\t* exit.c (pthread_exit): Add comment explaining the longjmp().\n\n\t* implement.h (ptw32_threads_thread_t): New member cancelthread.\n\t(PTW32_YES): Define.\n\t(PTW32_NO): Define.\n\t(RND_SIZEOF): Remove.\n\n\t* create.c (pthread_create): Rename cancelability to cancelstate.\n\n\t* pthread.h (pthread_attr_t): Rename cancelability to cancelstate.\n\t(PTHREAD_CANCELED): Define.\n\n1998-07-24  Ben Elliston  <bje at cygnus.com>\n\n\t* pthread.h (SIG_BLOCK): Define if not already defined.\n\t(SIG_UNBLOCK): Likewise.\n\t(SIG_SETMASK): Likewise.\n\t(pthread_attr_t): Add signal mask member.\n\t(pthread_sigmask): Add function prototype.\n\n\t* signal.c (pthread_sigmask): Implement.\n\n\t* create.c: #include <string.h> to get a prototype for memcpy().\n\t(pthread_create): New threads inherit their creator's signal\n\tmask.  Copy the signal mask to the new thread structure if we know\n\tabout signals.\n\t\nFri Jul 24 16:33:17 1998  Ross Johnson  <rpj at swan.canberra.edu.au>\n\n\t* fork.c (pthread_atfork): Add all the necessary push calls.\n\tLocal implementation semantics:\n\tIf we get an ENOMEM at any time then ALL handlers\n\t(including those from previous pthread_atfork() calls) will be\n\tpopped off each of the three atfork stacks before we return.\n\t(fork): Add all the necessary pop calls. Add the thread cancellation\n\tand join calls to the child fork.\n\tAdd #includes.\n\n\t* implement.h: (ptw32_handler_push): Fix return type and stack arg\n\ttype in prototype.\n\t(ptw32_handler_pop): Fix stack arg type in prototype.\n\t(ptw32_handler_pop_all): Fix stack arg type in prototype.\n\n\t* cleanup.c (ptw32_handler_push): Change return type to int and\n\treturn ENOMEM if malloc() fails.\n\n\t* sync.c (pthread_detach): Use equality test, not assignment.\n\n\t* create.c (ptw32_start_call): Add call to Win32 CloseHandle()\n\tif thread is detached.\n\n1998-07-24  Ben Elliston  <bje at cygnus.com>\n\n\t* sync.c (pthread_detach): Close the Win32 thread handle to\n\temulate detached (or daemon) threads.\n\nFri Jul 24 03:00:25 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* sync.c (pthread_join): Save valueptr arg in joinvalueptr for\n\tpthread_exit() to use.\n\n\t* private.c (ptw32_new_thread_entry): Initialise joinvalueptr to\n\tNULL.\n\n\t* create.c (ptw32_start_call): Rewrite to facilitate joins.\n\tpthread_exit() will do a longjmp() back to here. Does appropriate\n\tcleanup and exit/return from the thread.\n\t(pthread_create): _beginthreadex() now passes a pointer to our\n\tthread table entry instead of just the call member of that entry.\n\n\t* implement.h (ptw32_threads_thread): New member \n\tvoid ** joinvalueptr.\n\t(ptw32_call_t): New member jmpbuf env.\n\n\t* exit.c (pthread_exit): Major rewrite to handle joins and handing\n\tvalue pointer to joining thread. Uses longjmp() back to \n\tptw32_start_call().\n\n\t* create.c (pthread_create): Ensure values of new attribute members\n\tare copied to the thread attribute object.\n\n\t* attr.c (pthread_attr_destroy):  Fix merge conflicts.\n\t(pthread_attr_getdetachstate):  Fix merge conflicts.\n\t(pthread_attr_setdetachstate):  Fix merge conflicts.\n\n\t* pthread.h:  Fix merge conflicts.\n\n\t* sync.c (pthread_join): Fix merge conflicts.\n\nFri Jul 24 00:21:21 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* sync.c (pthread_join): Add check for valid and joinable\n\tthread.\n\t(pthread_detach): Implement. After checking for a valid and joinable\n\tthread, it's still a no-op.\n\n\t* private.c (ptw32_find_thread_entry): Bug prevented returning\n\tan error value in some cases.\n\n\t* attr.c (pthread_attr_setdetachedstate): Implement.\n\t(pthread_attr_getdetachedstate): Implement.\n\n\t* implement.h: Move more hidden definitions into here from\n\tpthread.h.\n\n1998-07-24  Ben Elliston  <bje at cygnus.com>\n\n\t* pthread.h (PTHREAD_CREATE_JOINABLE): Define.\n\t(PTHREAD_CREATE_DETACHED): Likewise.\n\t(pthread_attr_t): Add new structure member `detached'.\n\t(pthread_attr_getdetachstate): Add function prototype.\n\t(pthread_attr_setdetachstate): Likewise.\n\n\t* sync.c (pthread_join): Return if the target thread is detached.\n\n\t* attr.c (pthread_attr_init): Initialise cancelability and\n\tcanceltype structure members.\n\t(pthread_attr_getdetachstate): Implement.\n\t(pthread_attr_setdetachstate): Likewise.\n\n\t* implement.h (PTW32_CANCEL_DEFAULTS): Remove.  Bit fields\n\tproved to be too cumbersome.  Set the defaults in attr.c using the\n\tpublic PTHREAD_CANCEL_* constants.\n\n\t* cancel.c: New file.\n\n\t* pthread.h (sched_param): Define this type.\n\t(pthread_attr_getschedparam): Add function prototype.\n\t(pthread_attr_setschedparam): Likewise.\n\t(pthread_setcancelstate): Likewise.\n\t(pthread_setcanceltype): Likewise.\n\t(sched_get_priority_min): Likewise.\n\t(sched_get_priority_max): Likewise.\n\t(pthread_mutexattr_setprotocol): Remove; not supported.\n\t(pthread_mutexattr_getprotocol): Likewise.\n\t(pthread_mutexattr_setprioceiling): Likewise.\n\t(pthread_mutexattr_getprioceiling): Likewise.\n\t(pthread_attr_t): Add canceltype member.  Update comments.\n\t(SCHED_OTHER): Define this scheduling policy constant.\n\t(SCHED_FIFO): Likewise.\n\t(SCHED_RR): Likewise.\n\t(SCHED_MIN): Define the lowest possible value for this constant.\n\t(SCHED_MAX): Likewise, the maximum possible value.\n\t(PTHREAD_CANCEL_ASYNCHRONOUS): Redefine.\n\t(PTHREAD_CANCEL_DEFERRED): Likewise.\n\t\n\t* sched.c: New file.\n\t(pthread_setschedparam): Implement.\n\t(pthread_getschedparam): Implement.\n\t(sched_get_priority_max): Validate policy argument.\n\t(sched_get_priority_min): Likewise.\n\n\t* mutex.c (pthread_mutexattr_setprotocol): Remove; not supported.\n\t(pthread_mutexattr_getprotocol): Likewise.\n\t(pthread_mutexattr_setprioceiling): Likewise.\n\t(pthread_mutexattr_getprioceiling): Likewise.\n\nFri Jul 24 00:21:21 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* create.c (pthread_create): Arg to ptw32_new_thread_entry()\n\tchanged. See next entry. Move mutex locks out. Changes made yesterday\n\tand today allow us to start the new thread running rather than\n\ttemporarily suspended.\n\n\t* private.c (ptw32_new_thread_entry): ptw32_thread_table\n\twas changed back to a table of thread structures rather than pointers.\n\tAs such we're trading storage for increaded speed. This routine\n\twas modified to work with the new table. Mutex lock put in around\n\tglobal data accesses.\n\t(ptw32_find_thread_entry): Ditto\n\t(ptw32_delete_thread_entry): Ditto\n\nThu Jul 23 23:25:30 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* global.c: New. Global data objects declared here. These moved from\n\tpthread.h.\n\n\t* pthread.h: Move implementation hidden definitions into\n\timplement.h.\n\n\t* implement.h: Move implementation hidden definitions from\n\tpthread.h. Add constants to index into the different handler stacks.\n\n\t* cleanup.c (ptw32_handler_push): Simplify args. Restructure.\n\t(ptw32_handler_pop): Simplify args. Restructure.\n\t(ptw32_handler_pop_all): Simplify args. Restructure.\n\nWed Jul 22 00:16:22 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* attr.c, implement.h, pthread.h, ChangeLog: Resolve CVS merge\n\tconflicts.\n\n\t* private.c (ptw32_find_thread_entry): Changes to return type\n\tto support leaner ptw32_threads_table[] which now only stores\n\tptw32_thread_thread_t *.\n\t(ptw32_new_thread_entry): Internal changes.\n\t(ptw32_delete_thread_entry): Internal changes to avoid contention.\n \tCalling routines changed accordingly.\n\n\t* pthread.h: Modified cleanup macros to use new generic push and pop.\n\tAdded destructor and atfork stacks to ptw32_threads_thread_t.\n\n\t* cleanup.c (ptw32_handler_push, ptw32_handler_pop,\n\tptw32_handler_pop_all): Renamed cleanup push and pop routines\n\tand made generic to handle destructors and atfork handlers as\n\twell.\n\n\t* create.c (ptw32_start_call): New function is a wrapper for\n\tall new threads. It allows us to do some cleanup when the thread\n\treturns, ie. that is otherwise only done if the thread is cancelled.\n\n\t* exit.c (ptw32_vacuum): New function contains code from \n\tpthread_exit() that we need in the new ptw32_start_call()\n\tas well.\n\n\t* implement.h: Various additions and minor changes.\n\n\t* pthread.h: Various additions and minor changes.\n\tChange cleanup handler macros to use generic handler push and pop\n\tfunctions.\n\n\t* attr.c: Minor mods to all functions.\n\t(is_attr): Implemented missing function.\n\n\t* create.c (pthread_create): More clean up.\n\n\t* private.c (ptw32_find_thread_entry): Implement.\n\t(ptw32_delete_thread_entry): Implement.\n\t(ptw32_new_thread_entry): Implement.\n\tThese functions manipulate the implementations internal thread\n\ttable and are part of general code cleanup and modularisation.\n\tThey replace ptw32_getthreadindex() which was removed.\n\n\t* exit.c (pthread_exit): Changed to use the new code above.\n\n\t* pthread.h: Add cancelability constants. Update comments.\n\n1998-07-22  Ben Elliston  <bje at cygnus.com>\n\n\t* attr.c (pthread_setstacksize): Update test of attr argument.\n\t(pthread_getstacksize): Likewise.\n\t(pthread_setstackaddr): Likewise.\n\t(pthread_getstackaddr): Likewise.\n\t(pthread_attr_init): No need to allocate any storage.\n\t(pthread_attr_destroy): No need to free any storage.\n\n\t* mutex.c (is_attr): Not likely to be needed; remove.\n\t(remove_attr): Likewise.\n\t(insert_attr): Likewise.\n\n\t* implement.h (ptw32_mutexattr_t): Moved to a public definition\n\tin pthread.h.  There was little gain in hiding these details.\n\t(ptw32_condattr_t): Likewise.\n\t(ptw32_attr_t): Likewise.\n\n\t* pthread.h (pthread_atfork): Add function prototype.\n\t(pthread_attr_t): Moved here from implement.h.\n\n\t* fork.c (pthread_atfork): Preliminary implementation.\n\t(ptw32_fork): Likewise.\n\nWed Jul 22 00:16:22 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* cleanup.c (ptw32_cleanup_push): Implement.\n\t(ptw32_cleanup_pop): Implement.\n\t(ptw32_do_cancellation): Implement.\n\tThese are private to the implementation. The real cleanup functions\n\tare macros. See below.\n\n\t* pthread.h (pthread_cleanup_push): Implement as a macro.\n\t(pthread_cleanup_pop): Implement as a macro.\n\tBecause these are macros which start and end a block, the POSIX scoping\n\trequirement is observed. See the comment in the file.\n\n\t* exit.c (pthread_exit): Refine the code.\n\n\t* create.c (pthread_create): Code cleanup.\n\n\t* implement.h (RND_SIZEOF): Add RND_SIZEOF(T) to round sizeof(T)\n\tup to multiple of DWORD.\n\tAdd function prototypes.\n\n\t* private.c (ptw32_getthreadindex): \"*thread\" should have been \n\t\"thread\". Detect empty slot fail condition.\n\n1998-07-20  Ben Elliston  <bje at cygnus.com>\n\n\t* misc.c (pthread_once): Implement.  Don't use a per-application\n\tflag and mutex--make `pthread_once_t' contain these elements in\n\ttheir structure.  The earlier version had incorrect semantics.\n\t\n\t* pthread.h (ptw32_once_flag): Add new variable.  Remove.\n\t(ptw32_once_lock): Add new mutex lock to ensure integrity of\n\taccess to ptw32_once_flag.  Remove.\n\t(pthread_once): Add function prototype.\n\t(pthread_once_t): Define this type.\n\t\nMon Jul 20 02:31:05 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* private.c (ptw32_getthreadindex): Implement.\n\n\t* pthread.h: Add application static data dependent on\n\t_PTHREADS_BUILD_DLL define. This is needed to avoid allocating\n\tnon-sharable static data within the pthread DLL.\n\n\t* implement.h: Add ptw32_cleanup_stack_t, ptw32_cleanup_node_t\n\tand PTW32_HASH_INDEX.\n\n\t* exit.c (pthread_exit): Begin work on cleanup and de-allocate\n\tthread-private storage.\n\n\t* create.c (pthread_create): Add thread to thread table.\n\tKeep a thread-private copy of the attributes with default values\n\tfilled in when necessary. Same for the cleanup stack. Make \n\tpthread_create C run-time library friendly by using _beginthreadex()\n\tinstead of CreateThread(). Fix error returns.\n\nSun Jul 19 16:26:23 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h: Rename pthreads_thread_count to ptw32_threads_count.\n\tCreate ptw32_threads_thread_t struct to keep thread specific data.\n\n\t* create.c: Rename pthreads_thread_count to ptw32_threads_count.\n\t(pthread_create): Handle errors from CreateThread().\n\n1998-07-19  Ben Elliston  <bje at cygnus.com>\n\n\t* condvar.c (pthread_cond_wait): Generalise.  Moved from here ..\n\t(cond_wait): To here.\n\t(pthread_cond_timedwait): Implement; use generalised cond_wait().\n\n\t* pthread.h (pthread_key_t): Define this type.\n\t(pthread_key_create): Add function prototype.\n\t(pthread_setspecific): Likewise.\n\t(pthread_getspecific): Likwise.\n\t(pthread_key_delete): Likewise.\n\n\t* tsd.c (pthread_key_create): Implement.\n\t(pthread_setspecific): Likewise.\n\t(pthread_getspecific): Likewise.\n\t(pthread_key_delete): Likewise.\n\n\t* mutex.c (pthread_mutex_trylock): Return ENOSYS if this function\n\tis called on a Win32 platform which is not Windows NT.\n\n1998-07-18  Ben Elliston  <bje at cygnus.com>\n\n\t* condvar.c (pthread_condattr_init): Do not attempt to malloc any\n\tstorage; none is needed now that condattr_t is an empty struct.\n\t(pthread_condattr_destory): Likewise; do not free storage.\n\t(pthread_condattr_setpshared): No longer supported; return ENOSYS.\n\t(pthread_condattr_getpshared): Likewise.\n\t(pthread_cond_init): Implement with help from Douglas Schmidt.\n\tRemember to initialise the cv's internal mutex.\n\t(pthread_cond_wait): Likewise.\n\t(pthread_cond_signal): Likewise.\n\t(pthread_cond_broadcast): Likewise.\n\t(pthread_cond_timedwait): Preliminary implementation, but I need\n\tto see some API documentation for `WaitForMultipleObject'.\n\t(pthread_destory): Implement.\n\n\t* pthread.h (pthread_cond_init): Add function protoype.\n\t(pthread_cond_broadcast): Likewise.\n\t(pthread_cond_signal): Likewise.\n\t(pthread_cond_timedwait): Likewise.\n\t(pthread_cond_wait): Likewise.\n\t(pthread_cond_destroy): Likewise.\n\t(pthread_cond_t): Define this type.  Fix for u_int.  Do not assume\n\tthat the mutex contained withing the pthread_cond_t structure will\n\tbe a critical section.  Use our new POSIX type!\n\n\t* implement.h (ptw32_condattr_t): Remove shared attribute.\n\n1998-07-17  Ben Elliston  <bje at cygnus.com>\n\n\t* pthread.h (PTHREADS_PROCESS_PRIVATE): Remove.\n\t(PTHREAD_PROCESS_SHARED): Likewise.  No support for mutexes shared\n\tacross processes for now.\n\t(pthread_mutex_t): Use a Win32 CRITICAL_SECTION type for better\n\tperformance.\n\t\n\t* implement.h (ptw32_mutexattr_t): Remove shared attribute.\n\t\n\t* mutex.c (pthread_mutexattr_setpshared): This optional function\n\tis no longer supported, since we want to implement POSIX mutex\n\tvariables using the much more efficient Win32 critical section\n\tprimitives.  Critical section objects in Win32 cannot be shared\n\tbetween processes.\n\t(pthread_mutexattr_getpshared): Likewise.\n\t(pthread_mutexattr_init): No need to malloc any storage; the\n\tattributes structure is now empty.\n\t(pthread_mutexattr_destroy): This is now a nop.\n\t(pthread_mutex_init): Use InitializeCriticalSection().\n\t(pthread_mutex_destroy): Use DeleteCriticalSection().\n\t(pthread_mutex_lock): Use EnterCriticalSection().\n\t(pthread_mutex_trylock): Use TryEnterCriticalSection().  This is\n\tnot supported by Windows 9x, but trylock is a hack anyway, IMHO.\n\t(pthread_mutex_unlock): Use LeaveCriticalSection().\n\n1998-07-14  Ben Elliston  <bje at cygnus.com>\n\n\t* attr.c (pthread_attr_setstacksize): Implement.\n\t(pthread_attr_getstacksize): Likewise.\n\t(pthread_attr_setstackaddr): Likewise.\n\t(pthread_attr_getstackaddr): Likewise.\n\t(pthread_attr_init): Likewise.\n\t(pthread_attr_destroy): Likewise.\n\t\n\t* condvar.c (pthread_condattr_init): Add `_cond' to function name.\n\n\t* mutex.c (pthread_mutex_lock): Add `_mutex' to function name.\n\t(pthread_mutex_trylock): Likewise.\n\t(pthread_mutex_unlock): Likewise.\n\n\t* pthread.h (pthread_condattr_setpshared): Fix typo.\n\t(pthread_attr_init): Add function prototype.\n\t(pthread_attr_destroy): Likewise.\n\t(pthread_attr_setstacksize): Likewise.\n\t(pthread_attr_getstacksize): Likewise.\n\t(pthread_attr_setstackaddr): Likewise.\n\t(pthread_attr_getstackaddr): Likewise.\n\t\nMon Jul 13 01:09:55 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h: Wrap in #ifndef _IMPLEMENT_H\n\n\t* create.c (pthread_create): Map stacksize attr to Win32.\n\n\t* mutex.c: Include implement.h\n\n1998-07-13  Ben Elliston  <bje at cygnus.com>\n\n\t* condvar.c (pthread_condattr_init): Implement.\n\t(pthread_condattr_destroy): Likewise.\n\t(pthread_condattr_setpshared): Likewise.\n\t(pthread_condattr_getpshared): Likewise.\n\t\n\t* implement.h (PTHREAD_THREADS_MAX): Remove trailing semicolon.\n\t(PTHREAD_STACK_MIN): Specify; needs confirming.\n\t(ptw32_attr_t): Define this type.\n\t(ptw32_condattr_t): Likewise.\n\n\t* pthread.h (pthread_mutex_t): Define this type.\n\t(pthread_condattr_t): Likewise.\n\t(pthread_mutex_destroy): Add function prototype.\n\t(pthread_lock): Likewise.\n\t(pthread_trylock): Likewise.\n\t(pthread_unlock): Likewise.\n\t(pthread_condattr_init): Likewise.\n\t(pthread_condattr_destroy): Likewise.\n\t(pthread_condattr_setpshared): Likewise.\n\t(pthread_condattr_getpshared): Likewise.\n\n\t* mutex.c (pthread_mutex_init): Implement.\n\t(pthread_mutex_destroy): Likewise.\n\t(pthread_lock): Likewise.\n\t(pthread_trylock): Likewise.\n\t(pthread_unlock): Likewise.\n\n1998-07-12  Ben Elliston  <bje at cygnus.com>\n\n\t* implement.h (ptw32_mutexattr_t): Define this implementation\n\tinternal type.  Application programmers only see a mutex attribute\n\tobject as a void pointer.\n\n\t* pthread.h (pthread_mutexattr_t): Define this type.\n\t(pthread_mutexattr_init): Add function prototype.\n\t(pthread_mutexattr_destroy): Likewise.\n\t(pthread_mutexattr_setpshared): Likewise.\n\t(pthread_mutexattr_getpshared): Likewise.\n\t(pthread_mutexattr_setprotocol): Likewise.\n\t(pthread_mutexattr_getprotocol): Likewise.\n\t(pthread_mutexattr_setprioceiling): Likewise.\n\t(pthread_mutexattr_getprioceiling): Likewise.\n\t(PTHREAD_PROCESS_PRIVATE): Define.\n\t(PTHREAD_PROCESS_SHARED): Define.\n\n\t* mutex.c (pthread_mutexattr_init): Implement.\n\t(pthread_mutexattr_destroy): Implement.\n\t(pthread_mutexattr_setprotocol): Implement.\n\t(pthread_mutexattr_getprotocol): Likewise.\n\t(pthread_mutexattr_setprioceiling): Likewise.\n\t(pthread_mutexattr_getprioceiling): Likewise.\n\t(pthread_mutexattr_setpshared): Likewise.\n\t(pthread_mutexattr_getpshared): Likewise.\n\t(insert_attr): New function; very preliminary implementation!\n\t(is_attr): Likewise.\n\t(remove_attr): Likewise.\n\t\nSat Jul 11 14:48:54 1998  Ross Johnson  <rpj at ixobrychus.canberra.edu.au>\n\n\t* implement.h: Preliminary implementation specific defines.\n\n\t* create.c (pthread_create): Preliminary implementation.\n\n1998-07-11  Ben Elliston  <bje at cygnus.com>\n\n\t* sync.c (pthread_join): Implement.\n\n\t* misc.c (pthread_equal): Likewise.\n\t\n\t* pthread.h (pthread_join): Add function prototype.\n\t(pthread_equal): Likewise.\n\t\n1998-07-10  Ben Elliston  <bje at cygnus.com>\n\n\t* misc.c (pthread_self): Implement.\n\n\t* exit.c (pthread_exit): Implement.\n\n\t* pthread.h (pthread_exit): Add function prototype.\n\t(pthread_self): Likewise.\n\t(pthread_t): Define this type.\n\n1998-07-09  Ben Elliston  <bje at cygnus.com>\n\n\t* create.c (pthread_create): A dummy stub right now.\n\n\t* pthread.h (pthread_create): Add function prototype.\n"
  },
  {
    "path": "deps/w32-pthreads/FAQ",
    "content": "\t\t  =========================================\n\t\t  PTHREADS-WIN32 Frequently Asked Questions\n\t\t  =========================================\n\nINDEX\n-----\n\nQ 1\tWhat is it?\n\nQ 2\tWhich of the several dll versions do I use?\n\tor,\n\tWhat are all these pthread*.dll and pthread*.lib files?\n\nQ 3\tWhat is the library naming convention?\n\nQ 4\tCleanup code default style or: it used to work when I built\n\tthe library myself, but now it doesn't - why?\n\nQ 5\tWhy is the default library version now less exception-friendly?\n\nQ 6\tShould I use Cygwin or Mingw32 as a development environment?\n\nQ 7\tNow that pthreads-win32 builds under Mingw32, why do I get\n\tmemory access violations (segfaults)?\n\nQ 8\tHow do I use pthread.dll for Win32 (Visual C++ 5.0)\n\nQ 9\tCancelation doesn't work for me, why?\n\nQ 10\tHow do I generate pthreadGCE.dll and libpthreadw32.a for use\n\twith Mingw32?\n\nQ 11    Why isn't pthread_t defined as a scalar (e.g. pointer or int)\n        like it is for other POSIX threads implementations?\n\n=============================================================================\n\nQ 1\tWhat is it?\n---\n\nPthreads-win32 is an Open Source Software implementation of the\nThreads component of the POSIX 1003.1c 1995 Standard for Microsoft's\nWin32 environment. Some functions from POSIX 1003.1b are also\nsupported including semaphores. Other related functions include\nthe set of read-write lock functions. The library also supports\nsome of the functionality of the Open Group's Single Unix\nspecification, version 2, namely mutex types.\n\nSee the file \"ANNOUNCE\" for more information including standards\nconformance details and list of supported routines.\n\n\n------------------------------------------------------------------------------\n\nQ 2\tWhich of the several dll versions do I use?\n---\tor,\n\tWhat are all these pthread*.dll and pthread*.lib files?\n\nSimply, you only use one of them, but you need to choose carefully.\n\nThe most important choice you need to make is whether to use a\nversion that uses exceptions internally, or not (there are versions\nof the library that use exceptions as part of the thread\ncancelation and cleanup implementation, and one that uses\nsetjmp/longjmp instead).\n\nThere is some contension amongst POSIX threads experts as\nto how POSIX threads cancelation and exit should work\nwith languages that include exceptions and handlers, e.g.\nC++ and even C (Microsoft's Structured Exceptions).\n\nThe issue is: should cancelation of a thread in, say,\na C++ application cause object destructors and C++ exception\nhandlers to be invoked as the stack unwinds during thread\nexit, or not?\n\nThere seems to be more opinion in favour of using the\nstandard C version of the library (no EH) with C++ applications\nsince this appears to be the assumption commercial pthreads\nimplementations make. Therefore, if you use an EH version\nof pthreads-win32 then you may be under the illusion that\nyour application will be portable, when in fact it is likely to\nbehave very differently linked with other pthreads libraries.\n\nNow you may be asking: why have you kept the EH versions of\nthe library?\n\nThere are a couple of reasons:\n- there is division amongst the experts and so the code may\n  be needed in the future. (Yes, it's in the repository and we\n  can get it out anytime in the future, but ...)\n- pthreads-win32 is one of the few implementations, and possibly\n  the only freely available one, that has EH versions. It may be\n  useful to people who want to play with or study application\n  behaviour under these conditions.\n\n\n------------------------------------------------------------------------------\n\nQ 3\tWhat is the library naming convention?\n---\n\nBecause the library is being built using various exception\nhandling schemes and compilers - and because the library\nmay not work reliably if these are mixed in an application,\neach different version of the library has it's own name.\n\nNote 1: the incompatibility is really between EH implementations\nof the different compilers. It should be possible to use the\nstandard C version from either compiler with C++ applications\nbuilt with a different compiler. If you use an EH version of\nthe library, then you must use the same compiler for the\napplication. This is another complication and dependency that\ncan be avoided by using only the standard C library version.\n\nNote 2: if you use a standard C pthread*.dll with a C++\napplication, then any functions that you define that are\nintended to be called via pthread_cleanup_push() must be\n__cdecl.\n\nNote 3: the intention is to also name either the VC or GC\nversion (it should be arbitrary) as pthread.dll, including\npthread.lib and libpthread.a as appropriate.\n\nIn general:\n\tpthread[VG]{SE,CE,C}.dll\n\tpthread[VG]{SE,CE,C}.lib\n\nwhere:\n\t[VG] indicates the compiler\n\tV\t- MS VC\n\tG\t- GNU C\n\n\t{SE,CE,C} indicates the exception handling scheme\n\tSE\t- Structured EH\n\tCE\t- C++ EH\n\tC\t- no exceptions - uses setjmp/longjmp\n\nFor example:\n\tpthreadVSE.dll\t(MSVC/SEH)\n\tpthreadGCE.dll\t(GNUC/C++ EH)\n\tpthreadGC.dll\t(GNUC/not dependent on exceptions)\n\nThe GNU library archive file names have changed to:\n\n\tlibpthreadGCE.a\n\tlibpthreadGC.a\n\n\n------------------------------------------------------------------------------\n\nQ 4\tCleanup code default style or: it used to work when I built\n---\tthe library myself, but now it doesn't - why?\n\nUp to and including snapshot 2001-07-12, if not defined, the cleanup\nstyle was determined automatically from the compiler used, and one\nof the following was defined accordingly:\n\n\t__CLEANUP_SEH\tMSVC only\n\t__CLEANUP_CXX\tC++, including MSVC++, GNU G++\n\t__CLEANUP_C\t\tC, including GNU GCC, not MSVC\n\nThese defines determine the style of cleanup (see pthread.h) and,\nmost importantly, the way that cancelation and thread exit (via\npthread_exit) is performed (see the routine ptw32_throw() in private.c).\n\nIn short, the exceptions versions of the library throw an exception\nwhen a thread is canceled or exits (via pthread_exit()), which is\ncaught by a handler in the thread startup routine, so that the\nthe correct stack unwinding occurs regardless of where the thread\nis when it's canceled or exits via pthread_exit().\n\nAfter snapshot 2001-07-12, unless your build explicitly defines (e.g.\nvia a compiler option) __CLEANUP_SEH, __CLEANUP_CXX, or __CLEANUP_C, then\nthe build now ALWAYS defaults to __CLEANUP_C style cleanup. This style\nuses setjmp/longjmp in the cancelation and pthread_exit implementations,\nand therefore won't do stack unwinding even when linked to applications\nthat have it (e.g. C++ apps). This is for consistency with most/all\ncommercial Unix POSIX threads implementations.\n\nAlthough it was not clearly documented before, it is still necessary to\nbuild your application using the same __CLEANUP_* define as was\nused for the version of the library that you link with, so that the\ncorrect parts of pthread.h are included. That is, the possible\ndefines require the following library versions:\n\n\t__CLEANUP_SEH\tpthreadVSE.dll\n\t__CLEANUP_CXX\tpthreadVCE.dll or pthreadGCE.dll\n\t__CLEANUP_C\t\tpthreadVC.dll or pthreadGC.dll\n\nTHE POINT OF ALL THIS IS: if you have not been defining one of these\nexplicitly, then the defaults have been set according to the compiler\nand language you are using, as described at the top of this\nsection.\n\nTHIS NOW CHANGES, as has been explained above. For example:\n\nIf you were building your application with MSVC++ i.e. using C++\nexceptions (rather than SEH) and not explicitly defining one of\n__CLEANUP_*, then __CLEANUP_C++ was defined for you in pthread.h.\nYou should have been linking with pthreadVCE.dll, which does\nstack unwinding.\n\nIf you now build your application as you had before, pthread.h will now\nset __CLEANUP_C as the default style, and you will need to link\nwith pthreadVC.dll. Stack unwinding will now NOT occur when a\nthread is canceled, nor when the thread calls pthread_exit().\n\nYour application will now most likely behave differently to previous\nversions, and in non-obvious ways. Most likely is that local\nobjects may not be destroyed or cleaned up after a thread\nis canceled.\n\nIf you want the same behaviour as before, then you must now define\n__CLEANUP_C++ explicitly using a compiler option and link with\npthreadVCE.dll as you did before.\n\n\n------------------------------------------------------------------------------\n\nQ 5\tWhy is the default library version now less exception-friendly?\n---\n\nBecause most commercial Unix POSIX threads implementations don't allow you to\nchoose to have stack unwinding. (Compaq's TRU64 Unix is possibly an exception.)\n\nTherefore, providing it in pthread-win32 as a default could be dangerous\nand non-portable. We still provide the choice but you must now consciously\nmake it.\n\nWHY NOT REMOVE THE EXCEPTIONS VERSIONS OF THE LIBRARY ALTOGETHER?\nThere are a few reasons:\n- because there are well respected POSIX threads people who believe\n  that POSIX threads implementations should be exceptions-aware and\n  do the expected thing in that context. (There are equally respected\n  people who believe it should not be easily accessible, if it's there\n  at all.)\n- because pthreads-win32 is one of the few implementations that has\n  the choice, perhaps the only freely available one, and so offers\n  a laboratory to people who may want to explore the effects;\n- although the code will always be around somewhere for anyone who\n  wants it, once it's removed from the current version it will not be\n  nearly as visible to people who may have a use for it.\n\n\n------------------------------------------------------------------------------\n\nQ 6\tShould I use Cygwin or Mingw32 as a development environment?\n---\n\nImportant: see Q7 also.\n\nUse Mingw32 with the MSVCRT library to build applications that use\nthe pthreads DLL.\n\nCygwin's own internal support for POSIX threads is growing.\nConsult that project's documentation for more information.\n\n------------------------------------------------------------------------------\n\nQ 7\tNow that pthreads-win32 builds under Mingw32, why do I get\n---\tmemory access violations (segfaults)?\n\nThe latest Mingw32 package has thread-safe exception handling (see Q10).\nAlso, see Q6 above.\n\n------------------------------------------------------------------------------\n\nQ 8\tHow do I use pthread.dll for Win32 (Visual C++ 5.0)\n---\t\n\n>\n> I'm a \"rookie\" when it comes to your pthread implementation.\tI'm currently\n> desperately trying to install the prebuilt .dll file into my MSVC compiler.\n> Could you please provide me with explicit instructions on how to do this (or\n> direct me to a resource(s) where I can acquire such information)?\n>\n> Thank you,\n>\n\nYou should have a .dll, .lib, .def, and three .h files. It is recommended\nthat you use pthreadVC.dll, rather than pthreadVCE.dll or pthreadVSE.dll\n(see Q2 above).\n\nThe .dll can go in any directory listed in your PATH environment\nvariable, so putting it into C:\\WINDOWS should work.\n\nThe .lib file can go in any directory listed in your LIB environment\nvariable.\n\nThe .h files can go in any directory listed in your INCLUDE\nenvironment variable.\n\nOr you might prefer to put the .lib and .h files into a new directory\nand add its path to LIB and INCLUDE. You can probably do this easiest\nby editing the file:-\n\nC:\\Program Files\\DevStudio\\vc\\bin\\vcvars32.bat\n\nThe .def file isn't used by anything in the pre-compiled version but \nis included for information.\n\nCheers.\nRoss\n\n------------------------------------------------------------------------------\n\nQ 9\tCancelation doesn't work for me, why?\n---\n\n> I'm investigating a problem regarding thread cancelation. The thread I want\n> to cancel has PTHREAD_CANCEL_ASYNCHRONOUS, however, this piece of code\n> blocks on the join():\n>\n>\t\tif ((retv = Pthread_cancel( recvThread )) == 0)\n>\t\t{\n>\t\t\tretv = Pthread_join( recvThread, 0 );\n>\t\t}\n>\n> Pthread_* are just macro's; they call pthread_*.\n>\n> The thread recvThread seems to block on a select() call. It doesn't get\n> cancelled.\n>\n> Two questions:\n>\n> 1) is this normal behaviour?\n>\n> 2) if not, how does the cancel mechanism work? I'm not very familliar to\n> win32 programming, so I don't really understand how the *Event() family of\n> calls work.\n\nThe answer to your first question is, normal POSIX behaviour would  \nbe to asynchronously cancel the thread. However, even that doesn't\nguarantee cancelation as the standard only says it should be\ncancelled as soon as possible.\n\nSnapshot 99-11-02 or earlier only partially supports asynchronous cancellation.\nSnapshots since then simulate async cancelation by poking the address of\na cancelation routine into the PC of the threads context. This requires\nthe thread to be resumed in some way for the cancelation to actually\nproceed. This is not true async cancelation, but it is as close as we've\nbeen able to get to it.\n\nIf the thread you're trying to cancel is blocked (for instance, it could be\nwaiting for data from the network), it will only get cancelled when it unblocks\n(when the data arrives). For true pre-emptive cancelation in these cases,\npthreads-win32 from snapshot 2004-05-16 can automatically recognise and use the\nQueueUserAPCEx package by Panagiotis E. Hadjidoukas. This package is available\nfrom the pthreads-win32 ftp site and is included in the pthreads-win32\nself-unpacking zip from 2004-05-16 onwards.\n\nUsing deferred cancelation would normally be the way to go, however,\neven though the POSIX threads standard lists a number of C library\nfunctions that are defined as deferred cancelation points, there is\nno hookup between those which are provided by Windows and the\npthreads-win32 library.\n\nIncidently, it's worth noting for code portability that the older POSIX\nthreads standards cancelation point lists didn't include \"select\" because\n(as I read in Butenhof) it wasn't part of POSIX. However, it does appear in\nthe SUSV3.\n\nEffectively, the only mandatory cancelation points that pthreads-win32\nrecognises are those the library implements itself, ie.\n\t\n\tpthread_testcancel\n\tpthread_cond_wait\n\tpthread_cond_timedwait\n\tpthread_join\n\tsem_wait\n\tsem_timedwait\n\tpthread_delay_np\n\nThe following routines from the non-mandatory list in SUSV3 are\ncancelation points in pthreads-win32:\n\n\tpthread_rwlock_wrlock\n\tpthread_rwlock_timedwrlock\n\nThe following routines from the non-mandatory list in SUSV3 are not\ncancelation points in pthreads-win32:\n\n\tpthread_rwlock_rdlock\n\tpthread_rwlock_timedrdlock\n\nPthreads-win32 also provides two functions that allow you to create\ncancelation points within your application, but only for cases where\na thread is going to block on a Win32 handle. These are:\n\n\tpthreadCancelableWait(HANDLE waitHandle) /* Infinite wait */\n \n\tpthreadCancelableTimedWait(HANDLE waitHandle, DWORD timeout)\n\n------------------------------------------------------------------------------\n \n\nQ 10\tHow do I create thread-safe applications using\n----\tpthreadGCE.dll, libpthreadw32.a and Mingw32?\n\nThis should not be a problem with recent versions of MinGW32.\n\nFor early versions, see Thomas Pfaff's email at:\nhttp://sources.redhat.com/ml/pthreads-win32/2002/msg00000.html\n------------------------------------------------------------------------------\n\nQ 11    Why isn't pthread_t defined as a scalar (e.g. pointer or int)\n        like it is for other POSIX threads implementations?\n----\n\nOriginally pthread_t was defined as a pointer (to the opaque pthread_t_\nstruct) and later it was changed to a struct containing the original\npointer plus a sequence counter. This is allowed under both the original\nPOSIX Threads Standard and the current Single Unix Specification.\n\nWhen pthread_t is a simple pointer to a struct some very difficult to\ndebug problems arise from the process of freeing and later allocing\nthread structs because new pthread_t handles can acquire the identity of\npreviously detached threads. The change to a struct was made, along with\nsome changes to their internal managment, in order to guarantee (for\npractical applications) that the pthread_t handle will be unique over the\nlife of the running process.\n\nWhere application code attempts to compare one pthread_t against another\ndirectly, a compiler error will be emitted because structs can't be\ncompared at that level. This should signal a potentially serious problem\nin the code design, which would go undetected if pthread_t was a scalar.\n\nThe POSIX Threading API provides a function named pthread_equal() to\ncompare pthread_t thread handles.\n\nOther pthreads implementations, such as Sun's, use an int as the handle\nbut do guarantee uniqueness within the process scope. Win32 scalar typed\nthread handles also guarantee uniqueness in system scope. It wasn't clear\nhow well the internal management of these handles would scale as the\nnumber of threads and the fragmentation of the sequence numbering\nincreased for applications where thousands or millions of threads are\ncreated and detached over time. The current management of threads within\npthreads-win32 using structs for pthread_t, and reusing without ever\nfreeing them, reduces the management time overheads to a constant, which\ncould be important given that pthreads-win32 threads are built on top of\nWin32 threads and will therefore include that management overhead on top\nof their own. The cost is that the memory resources used for thread\nhandles will remain at the peak level until the process exits.\n\nWhile it may be inconvenient for developers to be forced away from making\nassumptions about the internals of pthread_t, the advantage for the\nfuture development of pthread-win32, as well as those applications that\nuse it and other pthread implementations, is that the library is free to\nchange pthread_t internals and management as better methods arise.\n\n"
  },
  {
    "path": "deps/w32-pthreads/GNUmakefile",
    "content": "#\n# --------------------------------------------------------------------------\n#\n#      Pthreads-win32 - POSIX Threads Library for Win32\n#      Copyright(C) 1998 John E. Bossom\n#      Copyright(C) 1999,2005 Pthreads-win32 contributors\n# \n#      Contact Email: rpj@callisto.canberra.edu.au\n# \n#      The current list of contributors is contained\n#      in the file CONTRIBUTORS included with the source\n#      code distribution. The list can also be seen at the\n#      following World Wide Web location:\n#      http://sources.redhat.com/pthreads-win32/contributors.html\n# \n#      This library is free software; you can redistribute it and/or\n#      modify it under the terms of the GNU Lesser General Public\n#      License as published by the Free Software Foundation; either\n#      version 2 of the License, or (at your option) any later version.\n# \n#      This library is distributed in the hope that it will be useful,\n#      but WITHOUT ANY WARRANTY; without even the implied warranty of\n#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n#      Lesser General Public License for more details.\n# \n#      You should have received a copy of the GNU Lesser General Public\n#      License along with this library in the file COPYING.LIB;\n#      if not, write to the Free Software Foundation, Inc.,\n#      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n#\n\nDLL_VER\t= 2\nDLL_VERD= $(DLL_VER)d\n\nDEVROOT\t= C:\\PTHREADS\n\nDLLDEST\t= $(DEVROOT)\\DLL\nLIBDEST\t= $(DEVROOT)\\DLL\n\n# If Running MsysDTK\nRM\t= rm -f\nMV\t= mv -f\nCP\t= cp -f\n\n# If not.\n#RM\t= erase\n#MV\t= rename\n#CP\t= copy\n\n# For cross compiling use e.g.\n# make CROSS=x86_64-w64-mingw32- clean GC-inlined\nCROSS\t= \n\nAR\t= $(CROSS)ar\nDLLTOOL = $(CROSS)dlltool\nCC      = $(CROSS)gcc\nCXX     = $(CROSS)g++\nRANLIB  = $(CROSS)ranlib\nRC\t= $(CROSS)windres\n\nOPT\t= $(CLEANUP) -O3 # -finline-functions -findirect-inlining\nXOPT\t=\n\nRCFLAGS\t\t= --include-dir=.\n# Uncomment this if config.h defines RETAIN_WSALASTERROR\n#LFLAGS\t\t= -lws2_32\n\n# ----------------------------------------------------------------------\n# The library can be built with some alternative behaviour to\n# facilitate development of applications on Win32 that will be ported\n# to other POSIX systems. Nothing definable here will make the library\n# non-compliant, but applications that make assumptions that POSIX\n# does not garrantee may fail or misbehave under some settings.\n#\n# PTW32_THREAD_ID_REUSE_INCREMENT\n# Purpose:\n# POSIX says that applications should assume that thread IDs can be\n# recycled. However, Solaris and some other systems use a [very large]\n# sequence number as the thread ID, which provides virtual uniqueness.\n# Pthreads-win32 provides pseudo-unique IDs when the default increment\n# (1) is used, but pthread_t is not a scalar type like Solaris's.\n#\n# Usage:\n# Set to any value in the range: 0 <= value <= 2^wordsize\n#\n# Examples:\n# Set to 0 to emulate non recycle-unique behaviour like Linux or *BSD.\n# Set to 1 for recycle-unique thread IDs (this is the default).\n# Set to some other +ve value to emulate smaller word size types\n# (i.e. will wrap sooner).\n#\n#PTW32_FLAGS\t= \"-DPTW32_THREAD_ID_REUSE_INCREMENT=0\"\n#\n# ----------------------------------------------------------------------\n\nGC_CFLAGS\t= $(PTW32_FLAGS) \nGCE_CFLAGS\t= $(PTW32_FLAGS) -mthreads\n\n## Mingw\nMAKE\t\t?= make\nCFLAGS\t= $(OPT) $(XOPT) -I. -DHAVE_PTW32_CONFIG_H -Wall\n\nDLL_INLINED_OBJS\t= \\\n\t\tpthread.o \\\n\t\tversion.o\n\n# Agregate modules for inlinability\nDLL_OBJS\t= \\\n\t\tattr.o \\\n\t\tbarrier.o \\\n\t\tcancel.o \\\n\t\tcleanup.o \\\n\t\tcondvar.o \\\n\t\tcreate.o \\\n\t\tdll.o \\\n\t\terrno.o \\\n\t\texit.o \\\n\t\tfork.o \\\n\t\tglobal.o \\\n\t\tmisc.o \\\n\t\tmutex.o \\\n\t\tnonportable.o \\\n\t\tprivate.o \\\n\t\trwlock.o \\\n\t\tsched.o \\\n\t\tsemaphore.o \\\n\t\tsignal.o \\\n\t\tspin.o \\\n\t\tsync.o \\\n\t\ttsd.o \\\n\t\tversion.o\n\n# Separate modules for minimum size statically linked images\nSMALL_STATIC_OBJS\t= \\\n\t\tpthread_attr_init.o \\\n\t\tpthread_attr_destroy.o \\\n\t\tpthread_attr_getdetachstate.o \\\n\t\tpthread_attr_setdetachstate.o \\\n\t\tpthread_attr_getstackaddr.o \\\n\t\tpthread_attr_setstackaddr.o \\\n\t\tpthread_attr_getstacksize.o \\\n\t\tpthread_attr_setstacksize.o \\\n\t\tpthread_attr_getscope.o \\\n\t\tpthread_attr_setscope.o \\\n\t\tpthread_attr_setschedpolicy.o \\\n\t\tpthread_attr_getschedpolicy.o \\\n\t\tpthread_attr_setschedparam.o \\\n\t\tpthread_attr_getschedparam.o \\\n\t\tpthread_attr_setinheritsched.o \\\n\t\tpthread_attr_getinheritsched.o \\\n\t\tpthread_barrier_init.o \\\n\t\tpthread_barrier_destroy.o \\\n\t\tpthread_barrier_wait.o \\\n\t\tpthread_barrierattr_init.o \\\n\t\tpthread_barrierattr_destroy.o \\\n\t\tpthread_barrierattr_setpshared.o \\\n\t\tpthread_barrierattr_getpshared.o \\\n\t\tpthread_setcancelstate.o \\\n\t\tpthread_setcanceltype.o \\\n\t\tpthread_testcancel.o \\\n\t\tpthread_cancel.o \\\n\t\tcleanup.o \\\n\t\tpthread_condattr_destroy.o \\\n\t\tpthread_condattr_getpshared.o \\\n\t\tpthread_condattr_init.o \\\n\t\tpthread_condattr_setpshared.o \\\n\t\tpthread_cond_destroy.o \\\n\t\tpthread_cond_init.o \\\n\t\tpthread_cond_signal.o \\\n\t\tpthread_cond_wait.o \\\n\t\tcreate.o \\\n\t\tdll.o \\\n\t\tautostatic.o \\\n\t\terrno.o \\\n\t\tpthread_exit.o \\\n\t\tfork.o \\\n\t\tglobal.o \\\n\t\tpthread_mutex_init.o \\\n\t\tpthread_mutex_destroy.o \\\n\t\tpthread_mutexattr_init.o \\\n\t\tpthread_mutexattr_destroy.o \\\n\t\tpthread_mutexattr_getpshared.o \\\n\t\tpthread_mutexattr_setpshared.o \\\n\t\tpthread_mutexattr_settype.o \\\n\t\tpthread_mutexattr_gettype.o \\\n\t\tpthread_mutexattr_setrobust.o \\\n\t\tpthread_mutexattr_getrobust.o \\\n\t\tpthread_mutex_lock.o \\\n\t\tpthread_mutex_timedlock.o \\\n\t\tpthread_mutex_unlock.o \\\n\t\tpthread_mutex_trylock.o \\\n\t\tpthread_mutex_consistent.o \\\n\t\tpthread_mutexattr_setkind_np.o \\\n\t\tpthread_mutexattr_getkind_np.o \\\n\t\tpthread_getw32threadhandle_np.o \\\n\t\tpthread_getunique_np.o \\\n\t\tpthread_delay_np.o \\\n\t\tpthread_num_processors_np.o \\\n\t\tpthread_win32_attach_detach_np.o \\\n\t\tpthread_equal.o \\\n\t\tpthread_getconcurrency.o \\\n\t\tpthread_once.o \\\n\t\tpthread_self.o \\\n\t\tpthread_setconcurrency.o \\\n\t\tpthread_rwlock_init.o \\\n\t\tpthread_rwlock_destroy.o \\\n\t\tpthread_rwlockattr_init.o \\\n\t\tpthread_rwlockattr_destroy.o \\\n\t\tpthread_rwlockattr_getpshared.o \\\n\t\tpthread_rwlockattr_setpshared.o \\\n\t\tpthread_rwlock_rdlock.o \\\n\t\tpthread_rwlock_wrlock.o \\\n\t\tpthread_rwlock_unlock.o \\\n\t\tpthread_rwlock_tryrdlock.o \\\n\t\tpthread_rwlock_trywrlock.o \\\n\t\tpthread_setschedparam.o \\\n\t\tpthread_getschedparam.o \\\n\t\tpthread_timechange_handler_np.o \\\n\t\tptw32_is_attr.o \\\n\t\tptw32_cond_check_need_init.o \\\n\t\tptw32_MCS_lock.o \\\n\t\tptw32_mutex_check_need_init.o \\\n\t\tptw32_processInitialize.o \\\n\t\tptw32_processTerminate.o \\\n\t\tptw32_threadStart.o \\\n\t\tptw32_threadDestroy.o \\\n\t\tptw32_tkAssocCreate.o \\\n\t\tptw32_tkAssocDestroy.o \\\n\t\tptw32_callUserDestroyRoutines.o \\\n\t\tptw32_timespec.o \\\n\t\tptw32_throw.o \\\n\t\tptw32_getprocessors.o \\\n\t\tptw32_calloc.o \\\n\t\tptw32_new.o \\\n\t\tptw32_reuse.o \\\n\t\tptw32_semwait.o \\\n\t\tptw32_relmillisecs.o \\\n\t\tptw32_rwlock_check_need_init.o \\\n\t\tsched_get_priority_max.o \\\n\t\tsched_get_priority_min.o \\\n\t\tsched_setscheduler.o \\\n\t\tsched_getscheduler.o \\\n\t\tsched_yield.o \\\n\t\tsem_init.o \\\n\t\tsem_destroy.o \\\n\t\tsem_trywait.o \\\n\t\tsem_timedwait.o \\\n\t\tsem_wait.o \\\n\t\tsem_post.o \\\n\t\tsem_post_multiple.o \\\n\t\tsem_getvalue.o \\\n\t\tsem_open.o \\\n\t\tsem_close.o \\\n\t\tsem_unlink.o \\\n\t\tsignal.o \\\n\t\tpthread_kill.o \\\n\t\tptw32_spinlock_check_need_init.o \\\n\t\tpthread_spin_init.o \\\n\t\tpthread_spin_destroy.o \\\n\t\tpthread_spin_lock.o \\\n\t\tpthread_spin_unlock.o \\\n\t\tpthread_spin_trylock.o \\\n\t\tpthread_detach.o \\\n\t\tpthread_join.o \\\n\t\tpthread_key_create.o \\\n\t\tpthread_key_delete.o \\\n\t\tpthread_setspecific.o \\\n\t\tpthread_getspecific.o \\\n\t\tw32_CancelableWait.o \\\n\t\tversion.o\n\nINCL\t= \\\n\t\tconfig.h \\\n\t\timplement.h \\\n\t\tsemaphore.h \\\n\t\tpthread.h \\\n\t\tneed_errno.h\n\nATTR_SRCS\t= \\\n\t\tpthread_attr_init.c \\\n\t\tpthread_attr_destroy.c \\\n\t\tpthread_attr_getdetachstate.c \\\n\t\tpthread_attr_setdetachstate.c \\\n\t\tpthread_attr_getstackaddr.c \\\n\t\tpthread_attr_setstackaddr.c \\\n\t\tpthread_attr_getstacksize.c \\\n\t\tpthread_attr_setstacksize.c \\\n\t\tpthread_attr_getscope.c \\\n\t\tpthread_attr_setscope.c\n\nBARRIER_SRCS = \\\n\t\tpthread_barrier_init.c \\\n\t\tpthread_barrier_destroy.c \\\n\t\tpthread_barrier_wait.c \\\n\t\tpthread_barrierattr_init.c \\\n\t\tpthread_barrierattr_destroy.c \\\n\t\tpthread_barrierattr_setpshared.c \\\n\t\tpthread_barrierattr_getpshared.c\n\nCANCEL_SRCS\t= \\\n\t\tpthread_setcancelstate.c \\\n\t\tpthread_setcanceltype.c \\\n\t\tpthread_testcancel.c \\\n\t\tpthread_cancel.c \n\nCONDVAR_SRCS\t= \\\n\t\tptw32_cond_check_need_init.c \\\n\t\tpthread_condattr_destroy.c \\\n\t\tpthread_condattr_getpshared.c \\\n\t\tpthread_condattr_init.c \\\n\t\tpthread_condattr_setpshared.c \\\n\t\tpthread_cond_destroy.c \\\n\t\tpthread_cond_init.c \\\n\t\tpthread_cond_signal.c \\\n\t\tpthread_cond_wait.c\n\nEXIT_SRCS\t= \\\n\t\tpthread_exit.c\n\nMISC_SRCS\t= \\\n\t\tpthread_equal.c \\\n\t\tpthread_getconcurrency.c \\\n\t\tpthread_kill.c \\\n\t\tpthread_once.c \\\n\t\tpthread_self.c \\\n\t\tpthread_setconcurrency.c \\\n\t\tptw32_calloc.c \\\n\t\tptw32_MCS_lock.c \\\n\t\tptw32_new.c \\\n\t\tptw32_reuse.c \\\n\t\tw32_CancelableWait.c\n\nMUTEX_SRCS\t= \\\n\t\tptw32_mutex_check_need_init.c \\\n\t\tpthread_mutex_init.c \\\n\t\tpthread_mutex_destroy.c \\\n\t\tpthread_mutexattr_init.c \\\n\t\tpthread_mutexattr_destroy.c \\\n\t\tpthread_mutexattr_getpshared.c \\\n\t\tpthread_mutexattr_setpshared.c \\\n\t\tpthread_mutexattr_settype.c \\\n\t\tpthread_mutexattr_gettype.c \\\n\t\tpthread_mutexattr_setrobust.c \\\n\t\tpthread_mutexattr_getrobust.c \\\n\t\tpthread_mutex_lock.c \\\n\t\tpthread_mutex_timedlock.c \\\n\t\tpthread_mutex_unlock.c \\\n\t\tpthread_mutex_trylock.c \\\n\t\tpthread_mutex_consistent.c\n\nNONPORTABLE_SRCS = \\\n\t\tpthread_mutexattr_setkind_np.c \\\n\t\tpthread_mutexattr_getkind_np.c \\\n\t\tpthread_getw32threadhandle_np.c \\\n                pthread_getunique_np.c \\\n\t\tpthread_delay_np.c \\\n\t\tpthread_num_processors_np.c \\\n\t\tpthread_win32_attach_detach_np.c \\\n\t\tpthread_timechange_handler_np.c \n\nPRIVATE_SRCS\t= \\\n\t\tptw32_is_attr.c \\\n\t\tptw32_processInitialize.c \\\n\t\tptw32_processTerminate.c \\\n\t\tptw32_threadStart.c \\\n\t\tptw32_threadDestroy.c \\\n\t\tptw32_tkAssocCreate.c \\\n\t\tptw32_tkAssocDestroy.c \\\n\t\tptw32_callUserDestroyRoutines.c \\\n\t\tptw32_semwait.c \\\n\t\tptw32_relmillisecs.c \\\n\t\tptw32_timespec.c \\\n\t\tptw32_throw.c \\\n\t\tptw32_getprocessors.c\n\nRWLOCK_SRCS\t= \\\n\t\tptw32_rwlock_check_need_init.c \\\n\t\tptw32_rwlock_cancelwrwait.c \\\n\t\tpthread_rwlock_init.c \\\n\t\tpthread_rwlock_destroy.c \\\n\t\tpthread_rwlockattr_init.c \\\n\t\tpthread_rwlockattr_destroy.c \\\n\t\tpthread_rwlockattr_getpshared.c \\\n\t\tpthread_rwlockattr_setpshared.c \\\n\t\tpthread_rwlock_rdlock.c \\\n\t\tpthread_rwlock_timedrdlock.c \\\n\t\tpthread_rwlock_wrlock.c \\\n\t\tpthread_rwlock_timedwrlock.c \\\n\t\tpthread_rwlock_unlock.c \\\n\t\tpthread_rwlock_tryrdlock.c \\\n\t\tpthread_rwlock_trywrlock.c\n\nSCHED_SRCS\t= \\\n\t\tpthread_attr_setschedpolicy.c \\\n\t\tpthread_attr_getschedpolicy.c \\\n\t\tpthread_attr_setschedparam.c \\\n\t\tpthread_attr_getschedparam.c \\\n\t\tpthread_attr_setinheritsched.c \\\n\t\tpthread_attr_getinheritsched.c \\\n\t\tpthread_setschedparam.c \\\n\t\tpthread_getschedparam.c \\\n\t\tsched_get_priority_max.c \\\n\t\tsched_get_priority_min.c \\\n\t\tsched_setscheduler.c \\\n\t\tsched_getscheduler.c \\\n\t\tsched_yield.c\n\nSEMAPHORE_SRCS = \\\n\t\tsem_init.c \\\n\t\tsem_destroy.c \\\n\t\tsem_trywait.c \\\n\t\tsem_timedwait.c \\\n\t\tsem_wait.c \\\n\t\tsem_post.c \\\n\t\tsem_post_multiple.c \\\n\t\tsem_getvalue.c \\\n\t\tsem_open.c \\\n\t\tsem_close.c \\\n\t\tsem_unlink.c\n\nSPIN_SRCS\t= \\\n\t\tptw32_spinlock_check_need_init.c \\\n\t\tpthread_spin_init.c \\\n\t\tpthread_spin_destroy.c \\\n\t\tpthread_spin_lock.c \\\n\t\tpthread_spin_unlock.c \\\n\t\tpthread_spin_trylock.c\n\nSYNC_SRCS\t= \\\n\t\tpthread_detach.c \\\n\t\tpthread_join.c\n\nTSD_SRCS\t= \\\n\t\tpthread_key_create.c \\\n\t\tpthread_key_delete.c \\\n\t\tpthread_setspecific.c \\\n\t\tpthread_getspecific.c\n\n\nGCE_DLL\t= pthreadGCE$(DLL_VER).dll\nGCED_DLL= pthreadGCE$(DLL_VERD).dll\nGCE_LIB\t= libpthreadGCE$(DLL_VER).a\nGCED_LIB= libpthreadGCE$(DLL_VERD).a\nGCE_INLINED_STAMP = pthreadGCE$(DLL_VER).stamp\nGCED_INLINED_STAMP = pthreadGCE$(DLL_VERD).stamp\nGCE_STATIC_STAMP = libpthreadGCE$(DLL_VER).stamp\nGCED_STATIC_STAMP = libpthreadGCE$(DLL_VERD).stamp\n\nGC_DLL \t= pthreadGC$(DLL_VER).dll\nGCD_DLL\t= pthreadGC$(DLL_VERD).dll\nGC_LIB\t= libpthreadGC$(DLL_VER).a\nGCD_LIB\t= libpthreadGC$(DLL_VERD).a\nGC_INLINED_STAMP = pthreadGC$(DLL_VER).stamp\nGCD_INLINED_STAMP = pthreadGC$(DLL_VERD).stamp\nGC_STATIC_STAMP = libpthreadGC$(DLL_VER).stamp\nGCD_STATIC_STAMP = libpthreadGC$(DLL_VERD).stamp\n\nPTHREAD_DEF\t= pthread.def\n\nhelp:\n\t@ echo \"Run one of the following command lines:\"\n\t@ echo \"make clean GC            (to build the GNU C dll with C cleanup code)\"\n\t@ echo \"make clean GCE           (to build the GNU C dll with C++ exception handling)\"\n\t@ echo \"make clean GC-inlined    (to build the GNU C inlined dll with C cleanup code)\"\n\t@ echo \"make clean GCE-inlined   (to build the GNU C inlined dll with C++ exception handling)\"\n\t@ echo \"make clean GC-static     (to build the GNU C inlined static lib with C cleanup code)\"\n\t@ echo \"make clean GC-debug      (to build the GNU C debug dll with C cleanup code)\"\n\t@ echo \"make clean GCE-debug     (to build the GNU C debug dll with C++ exception handling)\"\n\t@ echo \"make clean GC-inlined-debug    (to build the GNU C inlined debug dll with C cleanup code)\"\n\t@ echo \"make clean GCE-inlined-debug   (to build the GNU C inlined debug dll with C++ exception handling)\"\n\t@ echo \"make clean GC-static-debug     (to build the GNU C inlined static debug lib with C cleanup code)\"\n\nall:\n\t@ $(MAKE) clean GCE\n\t@ $(MAKE) clean GC\n\nGC:\n\t\t$(MAKE) CLEANUP=-D__CLEANUP_C XC_FLAGS=\"$(GC_CFLAGS)\" OBJ=\"$(DLL_OBJS)\" $(GC_DLL)\n\nGC-debug:\n\t\t$(MAKE) CLEANUP=-D__CLEANUP_C XC_FLAGS=\"$(GC_CFLAGS)\" OBJ=\"$(DLL_OBJS)\" DLL_VER=$(DLL_VERD) OPT=\"-D__CLEANUP_C -g -O0\" $(GCD_DLL)\n\nGCE:\n\t\t$(MAKE) CC=$(CXX) CLEANUP=-D__CLEANUP_CXX XC_FLAGS=\"$(GCE_CFLAGS)\" OBJ=\"$(DLL_OBJS)\" $(GCE_DLL)\n\nGCE-debug:\n\t\t$(MAKE) CC=$(CXX) CLEANUP=-D__CLEANUP_CXX XC_FLAGS=\"$(GCE_CFLAGS)\" OBJ=\"$(DLL_OBJS)\" DLL_VER=$(DLL_VERD) OPT=\"-D__CLEANUP_CXX -g -O0\" $(GCED_DLL)\n\nGC-inlined:\n\t\t$(MAKE) XOPT=\"-DPTW32_BUILD_INLINED\" CLEANUP=-D__CLEANUP_C XC_FLAGS=\"$(GC_CFLAGS)\" OBJ=\"$(DLL_INLINED_OBJS)\" $(GC_INLINED_STAMP)\n\nGC-inlined-debug:\n\t\t$(MAKE) XOPT=\"-DPTW32_BUILD_INLINED\" CLEANUP=-D__CLEANUP_C XC_FLAGS=\"$(GC_CFLAGS)\" OBJ=\"$(DLL_INLINED_OBJS)\" DLL_VER=$(DLL_VERD) OPT=\"-D__CLEANUP_C -g -O0\" $(GCD_INLINED_STAMP)\n\nGCE-inlined:\n\t\t$(MAKE) CC=$(CXX) XOPT=\"-DPTW32_BUILD_INLINED\" CLEANUP=-D__CLEANUP_CXX XC_FLAGS=\"$(GCE_CFLAGS)\" OBJ=\"$(DLL_INLINED_OBJS)\" $(GCE_INLINED_STAMP)\n\nGCE-inlined-debug:\n\t\t$(MAKE) CC=$(CXX) XOPT=\"-DPTW32_BUILD_INLINED\" CLEANUP=-D__CLEANUP_CXX XC_FLAGS=\"$(GCE_CFLAGS)\" OBJ=\"$(DLL_INLINED_OBJS)\" DLL_VER=$(DLL_VERD) OPT=\"-D__CLEANUP_CXX -g -O0\" $(GCED_INLINED_STAMP)\n\nGC-static:\n\t\t$(MAKE) XOPT=\"-DPTW32_BUILD_INLINED -DPTW32_STATIC_LIB\" CLEANUP=-D__CLEANUP_C XC_FLAGS=\"$(GC_CFLAGS)\" OBJ=\"$(DLL_INLINED_OBJS)\" $(GC_STATIC_STAMP)\n\nGC-static-debug:\n\t\t$(MAKE) XOPT=\"-DPTW32_BUILD_INLINED -DPTW32_STATIC_LIB\" CLEANUP=-D__CLEANUP_C XC_FLAGS=\"$(GC_CFLAGS)\" OBJ=\"$(DLL_INLINED_OBJS)\" DLL_VER=$(DLL_VERD) OPT=\"-D__CLEANUP_C -g -O0\" $(GCD_STATIC_STAMP)\n\ntests:\n\t@ cd tests\n\t@ $(MAKE) auto\n\n%.pre: %.c\n\t$(CC) -E -o $@ $(CFLAGS) $^\n\n%.s: %.c\n\t$(CC) -c $(CFLAGS) -DPTW32_BUILD_INLINED -Wa,-ahl $^ > $@\n\n%.o: %.rc\n\t$(RC) $(RCFLAGS) $(CLEANUP) -o $@ -i $<\n\n.SUFFIXES: .dll .rc .c .o\n\n.c.o:;\t\t $(CC) -c -o $@ $(CFLAGS) $(XC_FLAGS) $<\n\n\n$(GC_DLL) $(GCD_DLL): $(DLL_OBJS)\n\t$(CC) $(OPT) -shared -o $(GC_DLL) $(DLL_OBJS) $(LFLAGS)\n\t$(DLLTOOL) -z pthread.def $(DLL_OBJS)\n\t$(DLLTOOL) -k --dllname $@ --output-lib $(GC_LIB) --def $(PTHREAD_DEF)\n\n$(GCE_DLL): $(DLL_OBJS)\n\t$(CC) $(OPT) -mthreads -shared -o $(GCE_DLL) $(DLL_OBJS) $(LFLAGS)\n\t$(DLLTOOL) -z pthread.def $(DLL_OBJS)\n\t$(DLLTOOL) -k --dllname $@ --output-lib $(GCE_LIB) --def $(PTHREAD_DEF)\n\n$(GC_INLINED_STAMP) $(GCD_INLINED_STAMP): $(DLL_INLINED_OBJS)\n\t$(CC) $(OPT) $(XOPT) -shared -o $(GC_DLL) $(DLL_INLINED_OBJS) $(LFLAGS)\n\t$(DLLTOOL) -z pthread.def $(DLL_INLINED_OBJS)\n\t$(DLLTOOL) -k --dllname $(GC_DLL) --output-lib $(GC_LIB) --def $(PTHREAD_DEF)\n\techo touched > $(GC_INLINED_STAMP)\n\n$(GCE_INLINED_STAMP) $(GCED_INLINED_STAMP): $(DLL_INLINED_OBJS)\n\t$(CC) $(OPT) $(XOPT) -mthreads -shared -o $(GCE_DLL) $(DLL_INLINED_OBJS)  $(LFLAGS)\n\t$(DLLTOOL) -z pthread.def $(DLL_INLINED_OBJS)\n\t$(DLLTOOL) -k --dllname $(GCE_DLL) --output-lib $(GCE_LIB) --def $(PTHREAD_DEF)\n\techo touched > $(GCE_INLINED_STAMP)\n\n$(GC_STATIC_STAMP) $(GCD_STATIC_STAMP): $(DLL_INLINED_OBJS)\n\t$(RM) $(GC_LIB)\n\t$(AR) -rv $(GC_LIB) $(DLL_INLINED_OBJS)\n\t$(RANLIB) $(GC_LIB)\n\techo touched > $(GC_STATIC_STAMP)\n\nclean:\n\t-$(RM) *~\n\t-$(RM) *.i\n\t-$(RM) *.s\n\t-$(RM) *.o\n\t-$(RM) *.obj\n\t-$(RM) *.exe\n\t-$(RM) $(PTHREAD_DEF)\n\nrealclean: clean\n\t-$(RM) $(GC_LIB)\n\t-$(RM) $(GCE_LIB)\n\t-$(RM) $(GC_DLL)\n\t-$(RM) $(GCE_DLL)\n\t-$(RM) $(GC_INLINED_STAMP)\n\t-$(RM) $(GCE_INLINED_STAMP)\n\t-$(RM) $(GC_STATIC_STAMP)\n\t-$(RM) $(GCD_LIB)\n\t-$(RM) $(GCED_LIB)\n\t-$(RM) $(GCD_DLL)\n\t-$(RM) $(GCED_DLL)\n\t-$(RM) $(GCD_INLINED_STAMP)\n\t-$(RM) $(GCED_INLINED_STAMP)\n\t-$(RM) $(GCD_STATIC_STAMP)\n\nattr.o:\t\tattr.c $(ATTR_SRCS) $(INCL)\nbarrier.o:\tbarrier.c $(BARRIER_SRCS) $(INCL)\ncancel.o:\tcancel.c $(CANCEL_SRCS) $(INCL)\ncondvar.o:\tcondvar.c $(CONDVAR_SRCS) $(INCL)\nexit.o:\t\texit.c $(EXIT_SRCS) $(INCL)\nmisc.o:\t\tmisc.c $(MISC_SRCS) $(INCL)\nmutex.o:\tmutex.c $(MUTEX_SRCS) $(INCL)\nnonportable.o:\tnonportable.c $(NONPORTABLE_SRCS) $(INCL)\nprivate.o:\tprivate.c $(PRIVATE_SRCS) $(INCL)\nrwlock.o:\trwlock.c $(RWLOCK_SRCS) $(INCL)\nsched.o:\tsched.c $(SCHED_SRCS) $(INCL)\nsemaphore.o:\tsemaphore.c $(SEMAPHORE_SRCS) $(INCL)\nspin.o:\t\tspin.c $(SPIN_SRCS) $(INCL)\nsync.o:\t\tsync.c $(SYNC_SRCS) $(INCL)\ntsd.o:\t\ttsd.c $(TSD_SRCS) $(INCL)\nversion.o:\tversion.rc $(INCL)\n"
  },
  {
    "path": "deps/w32-pthreads/MAINTAINERS",
    "content": "CVS Repository maintainers\n\nRoss Johnson\t\trpj@ise.canberra.edu.au\nBen Elliston\t\tbje@cygnus.com\n"
  },
  {
    "path": "deps/w32-pthreads/Makefile",
    "content": "# This makefile is compatible with MS nmake and can be used as a\n# replacement for buildlib.bat. I've changed the target from an ordinary dll\n# (/LD) to a debugging dll (/LDd).\n# \n# The variables $DLLDEST and $LIBDEST hold the destination directories for the\n# dll and the lib, respectively. Probably all that needs to change is $DEVROOT.\n\n\n# DLL_VER:\n# See pthread.h and README - This number is computed as 'current - age'\nDLL_VER\t= 2\nDLL_VERD= $(DLL_VER)d\n\nDEVROOT\t= C:\\pthreads\n\nDLLDEST\t= $(DEVROOT)\\dll\nLIBDEST\t= $(DEVROOT)\\lib\nHDRDEST\t= $(DEVROOT)\\include\n\nDLLS\t= pthreadVCE$(DLL_VER).dll pthreadVSE$(DLL_VER).dll pthreadVC$(DLL_VER).dll \\\n\t\t  pthreadVCE$(DLL_VERD).dll pthreadVSE$(DLL_VERD).dll pthreadVC$(DLL_VERD).dll\nINLINED_STAMPS\t= pthreadVCE$(DLL_VER).stamp pthreadVSE$(DLL_VER).stamp pthreadVC$(DLL_VER).stamp \\\n\t\t\t\t  pthreadVCE$(DLL_VERD).stamp pthreadVSE$(DLL_VERD).stamp pthreadVC$(DLL_VERD).stamp\nSTATIC_STAMPS\t= pthreadVCE$(DLL_VER).static pthreadVSE$(DLL_VER).static pthreadVC$(DLL_VER).static \\\n\t\t\t\t  pthreadVCE$(DLL_VERD).static pthreadVSE$(DLL_VERD).static pthreadVC$(DLL_VERD).static\n\nCC\t= cl\nCPPFLAGS = /I. /DHAVE_PTW32_CONFIG_H\nXCFLAGS = /W3 /MD /nologo\nCFLAGS\t= /O2 /Ob2 $(XCFLAGS)\nCFLAGSD\t= /Z7 $(XCFLAGS)\n\n# Uncomment this if config.h defines RETAIN_WSALASTERROR\n#XLIBS = wsock32.lib\n\n# Default cleanup style\nCLEANUP\t= __CLEANUP_C\n\n# C++ Exceptions\nVCEFLAGS\t= /EHsc /TP $(CPPFLAGS) $(CFLAGS)\nVCEFLAGSD\t= /EHsc /TP $(CPPFLAGS) $(CFLAGSD)\n#Structured Exceptions\nVSEFLAGS\t= $(CPPFLAGS) $(CFLAGS)\nVSEFLAGSD\t= $(CPPFLAGS) $(CFLAGSD)\n#C cleanup code\nVCFLAGS\t\t= $(CPPFLAGS) $(CFLAGS)\nVCFLAGSD\t= $(CPPFLAGS) $(CFLAGSD)\n\nDLL_INLINED_OBJS = \\\n\t\tpthread.obj \\\n\t\tversion.res\n\n# Aggregate modules for inlinability\nDLL_OBJS\t= \\\n\t\tattr.obj \\\n\t\tbarrier.obj \\\n\t\tcancel.obj \\\n\t\tcleanup.obj \\\n\t\tcondvar.obj \\\n\t\tcreate.obj \\\n\t\tdll.obj \\\n\t\tautostatic.obj \\\n\t\terrno.obj \\\n\t\texit.obj \\\n\t\tfork.obj \\\n\t\tglobal.obj \\\n\t\tmisc.obj \\\n\t\tmutex.obj \\\n\t\tnonportable.obj \\\n\t\tprivate.obj \\\n\t\trwlock.obj \\\n\t\tsched.obj \\\n\t\tsemaphore.obj \\\n\t\tsignal.obj \\\n\t\tspin.obj \\\n\t\tsync.obj \\\n\t\ttsd.obj \\\n\t\tversion.res\n\n# Separate modules for minimising the size of statically linked images\nSMALL_STATIC_OBJS\t= \\\n\t\tpthread_attr_init.obj \\\n\t\tpthread_attr_destroy.obj \\\n\t\tpthread_attr_getdetachstate.obj \\\n\t\tpthread_attr_setdetachstate.obj \\\n\t\tpthread_attr_getstackaddr.obj \\\n\t\tpthread_attr_setstackaddr.obj \\\n\t\tpthread_attr_getstacksize.obj \\\n\t\tpthread_attr_setstacksize.obj \\\n\t\tpthread_attr_getscope.obj \\\n\t\tpthread_attr_setscope.obj \\\n\t\tpthread_attr_setschedpolicy.obj \\\n\t\tpthread_attr_getschedpolicy.obj \\\n\t\tpthread_attr_setschedparam.obj \\\n\t\tpthread_attr_getschedparam.obj \\\n\t\tpthread_attr_setinheritsched.obj \\\n\t\tpthread_attr_getinheritsched.obj \\\n\t\tpthread_barrier_init.obj \\\n\t\tpthread_barrier_destroy.obj \\\n\t\tpthread_barrier_wait.obj \\\n\t\tpthread_barrierattr_init.obj \\\n\t\tpthread_barrierattr_destroy.obj \\\n\t\tpthread_barrierattr_setpshared.obj \\\n\t\tpthread_barrierattr_getpshared.obj \\\n\t\tpthread_setcancelstate.obj \\\n\t\tpthread_setcanceltype.obj \\\n\t\tpthread_testcancel.obj \\\n\t\tpthread_cancel.obj \\\n\t\tcleanup.obj \\\n\t\tpthread_condattr_destroy.obj \\\n\t\tpthread_condattr_getpshared.obj \\\n\t\tpthread_condattr_init.obj \\\n\t\tpthread_condattr_setpshared.obj \\\n\t\tpthread_cond_destroy.obj \\\n\t\tpthread_cond_init.obj \\\n\t\tpthread_cond_signal.obj \\\n\t\tpthread_cond_wait.obj \\\n\t\tcreate.obj \\\n\t\tdll.obj \\\n\t\tautostatic.obj \\\n\t\terrno.obj \\\n\t\tpthread_exit.obj \\\n\t\tfork.obj \\\n\t\tglobal.obj \\\n\t\tpthread_mutex_init.obj \\\n\t\tpthread_mutex_destroy.obj \\\n\t\tpthread_mutexattr_init.obj \\\n\t\tpthread_mutexattr_destroy.obj \\\n\t\tpthread_mutexattr_getpshared.obj \\\n\t\tpthread_mutexattr_setpshared.obj \\\n\t\tpthread_mutexattr_settype.obj \\\n\t\tpthread_mutexattr_gettype.obj \\\n\t\tpthread_mutexattr_setrobust.obj \\\n\t\tpthread_mutexattr_getrobust.obj \\\n\t\tpthread_mutex_lock.obj \\\n\t\tpthread_mutex_timedlock.obj \\\n\t\tpthread_mutex_unlock.obj \\\n\t\tpthread_mutex_trylock.obj \\\n\t\tpthread_mutex_consistent.obj \\\n\t\tpthread_mutexattr_setkind_np.obj \\\n\t\tpthread_mutexattr_getkind_np.obj \\\n\t\tpthread_getw32threadhandle_np.obj \\\n\t\tpthread_getunique_np.obj \\\n\t\tpthread_delay_np.obj \\\n\t\tpthread_num_processors_np.obj \\\n\t\tpthread_win32_attach_detach_np.obj \\\n\t\tpthread_equal.obj \\\n\t\tpthread_getconcurrency.obj \\\n\t\tpthread_once.obj \\\n\t\tpthread_self.obj \\\n\t\tpthread_setconcurrency.obj \\\n\t\tpthread_rwlock_init.obj \\\n\t\tpthread_rwlock_destroy.obj \\\n\t\tpthread_rwlockattr_init.obj \\\n\t\tpthread_rwlockattr_destroy.obj \\\n\t\tpthread_rwlockattr_getpshared.obj \\\n\t\tpthread_rwlockattr_setpshared.obj \\\n\t\tpthread_rwlock_rdlock.obj \\\n\t\tpthread_rwlock_wrlock.obj \\\n\t\tpthread_rwlock_unlock.obj \\\n\t\tpthread_rwlock_tryrdlock.obj \\\n\t\tpthread_rwlock_trywrlock.obj \\\n\t\tpthread_setschedparam.obj \\\n\t\tpthread_getschedparam.obj \\\n\t\tpthread_timechange_handler_np.obj \\\n\t\tptw32_is_attr.obj \\\n\t\tptw32_processInitialize.obj \\\n\t\tptw32_processTerminate.obj \\\n\t\tptw32_threadStart.obj \\\n\t\tptw32_threadDestroy.obj \\\n\t\tptw32_tkAssocCreate.obj \\\n\t\tptw32_tkAssocDestroy.obj \\\n\t\tptw32_callUserDestroyRoutines.obj \\\n\t\tptw32_timespec.obj \\\n\t\tptw32_throw.obj \\\n\t\tptw32_getprocessors.obj \\\n\t\tptw32_calloc.obj \\\n\t\tptw32_new.obj \\\n\t\tptw32_reuse.obj \\\n\t\tptw32_rwlock_check_need_init.obj \\\n\t\tptw32_cond_check_need_init.obj \\\n\t\tptw32_mutex_check_need_init.obj \\\n\t\tptw32_semwait.obj \\\n\t\tptw32_relmillisecs.obj \\\n\t\tptw32_MCS_lock.obj \\\n\t\tsched_get_priority_max.obj \\\n\t\tsched_get_priority_min.obj \\\n\t\tsched_setscheduler.obj \\\n\t\tsched_getscheduler.obj \\\n\t\tsched_yield.obj \\\n\t\tsem_init.obj \\\n\t\tsem_destroy.obj \\\n\t\tsem_trywait.obj \\\n\t\tsem_timedwait.obj \\\n\t\tsem_wait.obj \\\n\t\tsem_post.obj \\\n\t\tsem_post_multiple.obj \\\n\t\tsem_getvalue.obj \\\n\t\tsem_open.obj \\\n\t\tsem_close.obj \\\n\t\tsem_unlink.obj \\\n\t\tsignal.obj \\\n\t\tpthread_kill.obj \\\n\t\tptw32_spinlock_check_need_init.obj \\\n\t\tpthread_spin_init.obj \\\n\t\tpthread_spin_destroy.obj \\\n\t\tpthread_spin_lock.obj \\\n\t\tpthread_spin_unlock.obj \\\n\t\tpthread_spin_trylock.obj \\\n\t\tpthread_detach.obj \\\n\t\tpthread_join.obj \\\n\t\tpthread_key_create.obj \\\n\t\tpthread_key_delete.obj \\\n\t\tpthread_setspecific.obj \\\n\t\tpthread_getspecific.obj \\\n\t\tw32_CancelableWait.obj \\\n\t\tversion.res\n\nINCL\t= config.h implement.h semaphore.h pthread.h need_errno.h\n\nATTR_SRCS\t= \\\n\t\tpthread_attr_init.c \\\n\t\tpthread_attr_destroy.c \\\n\t\tpthread_attr_getdetachstate.c \\\n\t\tpthread_attr_setdetachstate.c \\\n\t\tpthread_attr_getstackaddr.c \\\n\t\tpthread_attr_setstackaddr.c \\\n\t\tpthread_attr_getstacksize.c \\\n\t\tpthread_attr_setstacksize.c \\\n\t\tpthread_attr_getscope.c \\\n\t\tpthread_attr_setscope.c\n\nBARRIER_SRCS = \\\n\t\tpthread_barrier_init.c \\\n\t\tpthread_barrier_destroy.c \\\n\t\tpthread_barrier_wait.c \\\n\t\tpthread_barrierattr_init.c \\\n\t\tpthread_barrierattr_destroy.c \\\n\t\tpthread_barrierattr_setpshared.c \\\n\t\tpthread_barrierattr_getpshared.c\n\nCANCEL_SRCS\t= \\\n\t\tpthread_setcancelstate.c \\\n\t\tpthread_setcanceltype.c \\\n\t\tpthread_testcancel.c \\\n\t\tpthread_cancel.c \n\nCONDVAR_SRCS\t= \\\n\t\tptw32_cond_check_need_init.c \\\n\t\tpthread_condattr_destroy.c \\\n\t\tpthread_condattr_getpshared.c \\\n\t\tpthread_condattr_init.c \\\n\t\tpthread_condattr_setpshared.c \\\n\t\tpthread_cond_destroy.c \\\n\t\tpthread_cond_init.c \\\n\t\tpthread_cond_signal.c \\\n\t\tpthread_cond_wait.c\n\nEXIT_SRCS\t= \\\n\t\tpthread_exit.c\n\nMISC_SRCS\t= \\\n\t\tpthread_equal.c \\\n\t\tpthread_getconcurrency.c \\\n\t\tpthread_kill.c \\\n\t\tpthread_once.c \\\n\t\tpthread_self.c \\\n\t\tpthread_setconcurrency.c \\\n\t\tptw32_calloc.c \\\n\t\tptw32_MCS_lock.c \\\n\t\tptw32_new.c \\\n\t\tptw32_reuse.c \\\n\t\tptw32_relmillisecs.c \\\n\t\tw32_CancelableWait.c\n\nMUTEX_SRCS\t= \\\n\t\tptw32_mutex_check_need_init.c \\\n\t\tpthread_mutex_init.c \\\n\t\tpthread_mutex_destroy.c \\\n\t\tpthread_mutexattr_init.c \\\n\t\tpthread_mutexattr_destroy.c \\\n\t\tpthread_mutexattr_getpshared.c \\\n\t\tpthread_mutexattr_setpshared.c \\\n\t\tpthread_mutexattr_settype.c \\\n\t\tpthread_mutexattr_gettype.c \\\n\t\tpthread_mutexattr_setrobust.c \\\n\t\tpthread_mutexattr_getrobust.c \\\n\t\tpthread_mutex_lock.c \\\n\t\tpthread_mutex_timedlock.c \\\n\t\tpthread_mutex_unlock.c \\\n\t\tpthread_mutex_trylock.c \\\n\t\tpthread_mutex_consistent.c\n\nNONPORTABLE_SRCS = \\\n\t\tpthread_mutexattr_setkind_np.c \\\n\t\tpthread_mutexattr_getkind_np.c \\\n\t\tpthread_getw32threadhandle_np.c \\\n\t\tpthread_getunique_np.c \\\n\t\tpthread_delay_np.c \\\n\t\tpthread_num_processors_np.c \\\n\t\tpthread_win32_attach_detach_np.c \\\n\t\tpthread_timechange_handler_np.c \n\nPRIVATE_SRCS\t= \\\n\t\tptw32_is_attr.c \\\n\t\tptw32_processInitialize.c \\\n\t\tptw32_processTerminate.c \\\n\t\tptw32_threadStart.c \\\n\t\tptw32_threadDestroy.c \\\n\t\tptw32_tkAssocCreate.c \\\n\t\tptw32_tkAssocDestroy.c \\\n\t\tptw32_callUserDestroyRoutines.c \\\n\t\tptw32_semwait.c \\\n\t\tptw32_timespec.c \\\n\t\tptw32_throw.c \\\n\t\tptw32_getprocessors.c\n\nRWLOCK_SRCS\t= \\\n\t\tptw32_rwlock_check_need_init.c \\\n\t\tptw32_rwlock_cancelwrwait.c \\\n\t\tpthread_rwlock_init.c \\\n\t\tpthread_rwlock_destroy.c \\\n\t\tpthread_rwlockattr_init.c \\\n\t\tpthread_rwlockattr_destroy.c \\\n\t\tpthread_rwlockattr_getpshared.c \\\n\t\tpthread_rwlockattr_setpshared.c \\\n\t\tpthread_rwlock_rdlock.c \\\n\t\tpthread_rwlock_timedrdlock.c \\\n\t\tpthread_rwlock_wrlock.c \\\n\t\tpthread_rwlock_timedwrlock.c \\\n\t\tpthread_rwlock_unlock.c \\\n\t\tpthread_rwlock_tryrdlock.c \\\n\t\tpthread_rwlock_trywrlock.c\n\nSCHED_SRCS\t= \\\n\t\tpthread_attr_setschedpolicy.c \\\n\t\tpthread_attr_getschedpolicy.c \\\n\t\tpthread_attr_setschedparam.c \\\n\t\tpthread_attr_getschedparam.c \\\n\t\tpthread_attr_setinheritsched.c \\\n\t\tpthread_attr_getinheritsched.c \\\n\t\tpthread_setschedparam.c \\\n\t\tpthread_getschedparam.c \\\n\t\tsched_get_priority_max.c \\\n\t\tsched_get_priority_min.c \\\n\t\tsched_setscheduler.c \\\n\t\tsched_getscheduler.c \\\n\t\tsched_yield.c\n\nSEMAPHORE_SRCS = \\\n\t\tsem_init.c \\\n\t\tsem_destroy.c \\\n\t\tsem_trywait.c \\\n\t\tsem_timedwait.c \\\n\t\tsem_wait.c \\\n\t\tsem_post.c \\\n\t\tsem_post_multiple.c \\\n\t\tsem_getvalue.c \\\n\t\tsem_open.c \\\n\t\tsem_close.c \\\n\t\tsem_unlink.c\n\nSPIN_SRCS\t= \\\n\t\tptw32_spinlock_check_need_init.c \\\n\t\tpthread_spin_init.c \\\n\t\tpthread_spin_destroy.c \\\n\t\tpthread_spin_lock.c \\\n\t\tpthread_spin_unlock.c \\\n\t\tpthread_spin_trylock.c\n\nSYNC_SRCS\t= \\\n\t\tpthread_detach.c \\\n\t\tpthread_join.c\n\nTSD_SRCS\t= \\\n\t\tpthread_key_create.c \\\n\t\tpthread_key_delete.c \\\n\t\tpthread_setspecific.c \\\n\t\tpthread_getspecific.c\n\n\nhelp:\n\t@ echo Run one of the following command lines:\n\t@ echo nmake clean VCE   (to build the MSVC dll with C++ exception handling)\n\t@ echo nmake clean VSE   (to build the MSVC dll with structured exception handling)\n\t@ echo nmake clean VC    (to build the MSVC dll with C cleanup code)\n\t@ echo nmake clean VCE-inlined   (to build the MSVC inlined dll with C++ exception handling)\n\t@ echo nmake clean VSE-inlined   (to build the MSVC inlined dll with structured exception handling)\n\t@ echo nmake clean VC-inlined    (to build the MSVC inlined dll with C cleanup code)\n\t@ echo nmake clean VC-static     (to build the MSVC static lib with C cleanup code)\n\t@ echo nmake clean VCE-debug   (to build the debug MSVC dll with C++ exception handling)\n\t@ echo nmake clean VSE-debug   (to build the debug MSVC dll with structured exception handling)\n\t@ echo nmake clean VC-debug    (to build the debug MSVC dll with C cleanup code)\n\t@ echo nmake clean VCE-inlined-debug   (to build the debug MSVC inlined dll with C++ exception handling)\n\t@ echo nmake clean VSE-inlined-debug   (to build the debug MSVC inlined dll with structured exception handling)\n\t@ echo nmake clean VC-inlined-debug    (to build the debug MSVC inlined dll with C cleanup code)\n\t@ echo nmake clean VC-static-debug     (to build the debug MSVC static lib with C cleanup code)\n\nall:\n\t@ $(MAKE) /E clean VCE-inlined\n\t@ $(MAKE) /E clean VSE-inlined\n\t@ $(MAKE) /E clean VC-inlined\n\t@ $(MAKE) /E clean VCE-inlined-debug\n\t@ $(MAKE) /E clean VSE-inlined-debug\n\t@ $(MAKE) /E clean VC-inlined-debug\n\nVCE:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VCEFLAGS)\" CLEANUP=__CLEANUP_CXX pthreadVCE$(DLL_VER).dll\n\nVCE-debug:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VCEFLAGSD)\" CLEANUP=__CLEANUP_CXX pthreadVCE$(DLL_VERD).dll\n\nVSE:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VSEFLAGS)\" CLEANUP=__CLEANUP_SEH pthreadVSE$(DLL_VER).dll\n\nVSE-debug:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VSEFLAGSD)\" CLEANUP=__CLEANUP_SEH pthreadVSE$(DLL_VERD).dll\n\nVC:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VCFLAGS)\" CLEANUP=__CLEANUP_C pthreadVC$(DLL_VER).dll\n\nVC-debug:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VCFLAGSD)\" CLEANUP=__CLEANUP_C pthreadVC$(DLL_VERD).dll\n\n#\n# The so-called inlined DLL is just a single translation unit with\n# inlining optimisation turned on.\n#\nVCE-inlined:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VCEFLAGS) /DPTW32_BUILD_INLINED\" CLEANUP=__CLEANUP_CXX pthreadVCE$(DLL_VER).stamp\n\nVCE-inlined-debug:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VCEFLAGSD) /DPTW32_BUILD_INLINED\" CLEANUP=__CLEANUP_CXX pthreadVCE$(DLL_VERD).stamp\n\nVSE-inlined:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VSEFLAGS) /DPTW32_BUILD_INLINED\" CLEANUP=__CLEANUP_SEH pthreadVSE$(DLL_VER).stamp\n\nVSE-inlined-debug:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VSEFLAGSD) /DPTW32_BUILD_INLINED\" CLEANUP=__CLEANUP_SEH pthreadVSE$(DLL_VERD).stamp\n\nVC-inlined:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VCFLAGS) /DPTW32_BUILD_INLINED\" CLEANUP=__CLEANUP_C pthreadVC$(DLL_VER).stamp\n\nVC-inlined-debug:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VCFLAGSD) /DPTW32_BUILD_INLINED\" CLEANUP=__CLEANUP_C pthreadVC$(DLL_VERD).stamp\n\nVC-static:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VCFLAGS) /DPTW32_BUILD_INLINED /DPTW32_STATIC_LIB\" CLEANUP=__CLEANUP_C pthreadVC$(DLL_VER).static\n\nVC-static-debug:\n\t@ $(MAKE) /E /nologo EHFLAGS=\"$(VCFLAGSD) /DPTW32_BUILD_INLINED /DPTW32_STATIC_LIB\" CLEANUP=__CLEANUP_C pthreadVC$(DLL_VERD).static\n\nrealclean: clean\n\tif exist pthread*.dll del pthread*.dll\n\tif exist pthread*.lib del pthread*.lib\n\tif exist *.manifest del *.manifest\n\tif exist *.stamp del *.stamp\n\nclean:\n\tif exist *.obj del *.obj\n\tif exist *.def del *.def\n\tif exist *.ilk del *.ilk\n\tif exist *.pdb del *.pdb\n\tif exist *.exp del *.exp\n\tif exist *.map del *.map\n\tif exist *.o del *.o\n\tif exist *.i del *.i\n\tif exist *.res del *.res\n\n\ninstall:\n\tcopy pthread*.dll $(DLLDEST)\n\tcopy pthread*.lib $(LIBDEST)\n\tcopy pthread.h $(HDRDEST)\n\tcopy sched.h $(HDRDEST)\n\tcopy semaphore.h $(HDRDEST)\n\n$(DLLS): $(DLL_OBJS)\n\t$(CC) /LDd /Zi /nologo $(DLL_OBJS) /link /implib:$*.lib $(XLIBS) /out:$@\n\n$(INLINED_STAMPS): $(DLL_INLINED_OBJS)\n\t$(CC) /LDd /Zi /nologo $(DLL_INLINED_OBJS) /link /implib:$*.lib $(XLIBS) /out:$*.dll\n\n$(STATIC_STAMPS): $(DLL_INLINED_OBJS)\n\tif exist $*.lib del $*.lib\n\tlib $(DLL_INLINED_OBJS) /out:$*.lib\n\n.c.obj:\n\t$(CC) $(EHFLAGS) /D$(CLEANUP) -c $<\n\n# TARGET_CPU is an environment variable set by Visual Studio Command Prompt\n# as provided by the SDK\n.rc.res:\n\trc /dPTW32_ARCH$(TARGET_CPU) /dPTW32_RC_MSC /d$(CLEANUP) $<\n\n.c.i:\n\t$(CC) /P /O2 /Ob1 $(VCFLAGS) $<\n\nattr.obj:\tattr.c $(ATTR_SRCS) $(INCL)\nbarrier.obj:\tbarrier.c $(BARRIER_SRCS) $(INCL)\ncancel.obj:\tcancel.c $(CANCEL_SRCS) $(INCL)\ncondvar.obj:\tcondvar.c $(CONDVAR_SRCS) $(INCL)\nexit.obj:\texit.c $(EXIT_SRCS) $(INCL)\nmisc.obj:\tmisc.c $(MISC_SRCS) $(INCL)\nmutex.obj:\tmutex.c $(MUTEX_SRCS) $(INCL)\nnonportable.obj:\tnonportable.c $(NONPORTABLE_SRCS) $(INCL)\nprivate.obj:\tprivate.c $(PRIVATE_SRCS) $(INCL)\nrwlock.obj:\trwlock.c $(RWLOCK_SRCS) $(INCL)\nsched.obj:\tsched.c $(SCHED_SRCS) $(INCL)\nsemaphore.obj:\tsemaphore.c $(SEMAPHORE_SRCS) $(INCL)\nspin.obj:\tspin.c $(SPIN_SRCS) $(INCL)\nsync.obj:\tsync.c $(SYNC_SRCS) $(INCL)\ntsd.obj:\ttsd.c $(TSD_SRCS) $(INCL)\nversion.res:\tversion.rc $(INCL)\n"
  },
  {
    "path": "deps/w32-pthreads/NEWS",
    "content": "RELEASE 2.9.0\n-------------\n(2012-05-25)\n\nGeneral\n-------\nNew bug fixes in this release since 2.8.0 have NOT been applied to the\n1.x.x series.\n\nSome changes post 2011-02-26 in CVS may not be compatible with pre\nWindows 2000 systems.\n\nUse of other than the \"C\" version of the library is now discouraged.\nThat is, the \"C++\" version fails some tests and does not provide any\nadditional functionality.\n\nTesting and verification\n------------------------\nThis version has been tested on SMP architecture (Intel x64 Hex Core)\nby completing the included test suite, stress and bench tests.\n\nNew Features\n------------\nDLL properties now properly includes the target architecture, i.e.\nright-click on the file pthreadVC2.dll in explorer and choose the Detail\ntab will show the compiler and architecture in the description field, e.g.\n\"MS C x64\" or \"MS C x86\".\n- Ross Johnson\n\n(MSC and GNU builds) The statically linked library now automatically\ninitialises and cleans up on program start/exit, i.e. statically linked\napplications need not call the routines pthread_win32_process_attach_np()\nand pthread_win32_process_detach_np() explicitly. The per-thread routine\npthread_win32_thread_detach_np() is also called at program exit to cleanup\nPOSIX resources acquired by the primary Windows native thread, if I (RJ)\nunderstand the process correctly. Other Windows native threads that call\nPOSIX API routines may need to call the thread detach routine on thread\nexit if the application depends on reclaimed POSIX resources or running\nPOSIX TSD (TLS) destructors.\nSee README.NONPORTABLE for descriptions of these routines.\n- Ramiro Polla\n\nRobust mutexes are implemented within the PROCESS_PRIVATE scope. NOTE that\npthread_mutex_* functions may return different error codes for robust\nmutexes than they otherwise do in normal usage, e.g.  pthread_mutex_unlock\nis required to check ownership for all mutex types when the mutex is\nrobust, whereas this does not occur for the \"normal\" non-robust mutex type.\n- Ross Johnson\n\npthread_getunique_np is implemented for source level compatibility\nwith some other implementations. This routine returns a 64 bit\nsequence number that is uniquely associated with a thread. It can be\nused by applications to order or hash POSIX thread handles.\n- Ross Johnson\n\nBug fixes\n---------\nMany more changes for 64 bit systems.\n- Kai Tietz\n\nVarious modifications and fixes to build and test for WinCE.\n- Marcel Ruff, Sinan Kaya\n\nFix pthread_cond_destroy() - should not be a cancellation point. Other\nminor build problems fixed.\n- Romano Paolo Tenca\n\nRemove potential deadlock condition from pthread_cond_destroy().\n- Eric Berge\n\nVarious modifications to build and test for Win64.\n- Kip Streithorst\n\nVarious fixes to the QueueUserAPCEx async cancellation helper DLL\n(this is a separate download) and pthreads code cleanups.\n- Sebastian Gottschalk\n\nRemoved potential NULL pointer reference.\n- Robert Kindred\n\nRemoved the requirement that applications restrict the number of threads\ncalling pthread_barrier_wait to just the barrier count. Also reduced the\ncontention between barrier_wait and barrier_destroy. This change will have\nslowed barriers down slightly but halves the number of semaphores consumed\nper barrier to one.\n- Ross Johnson\n\nFixed a handle leak in sched_[gs]etscheduler.\n- Mark Pizzolato\n\nRemoved all of the POSIX re-entrant function compatibility macros from pthread.h.\nSome were simply not semanticly correct.\n- Igor Lubashev\n\nThreads no longer attempt to pass uncaught exceptions out of thread scope (C++\nand SEH builds only). Uncaught exceptions now cause the thread to exit with\nthe return code PTHREAD_CANCELED.\n- Ross Johnson\n\nLots of casting fixes particularly for x64, Interlocked fixes and reworking\nfor x64.\n- Daniel Richard G., John Kamp\n\nOther changes\n-------------\nDependence on the winsock library is now discretionary via\n#define RETAIN_WSALASTERROR in config.h. It is undefined by default unless\nWINCE is defined (because RJ is unsure of the dependency there).\n- Ramiro Polla\n\nSeveral static POSIX mutexes used for internal management were replaced by\nMCS queue-based locks to reduce resource consumption, in particular use of Win32\nobjects.\n- Ross Johnson\n\nFor security, the QuserEx.dll if used must now be installed in the Windows System\nfolder.\n- Ross Johnson\n\nNew tests\n---------\nrobust[1-5].c - Robust mutexes\nsequence1.c - per-thread unique sequence numbers\n\nModified tests and benchtests\n-----------------------------\nAll mutex*.c tests wherever appropriate have been modified to also test\nrobust mutexes under the same conditions.\nAdded robust mutex benchtests to benchtest*.c wherever appropriate.\n\n\nRELEASE 2.8.0\n-------------\n(2006-12-22)\n\nGeneral\n-------\nNew bug fixes in this release since 2.7.0 have not been applied to the\nversion 1.x.x series. It is probably time to drop version 1.\n\nTesting and verification\n------------------------\nThis release has not yet been tested on SMP architechtures. All tests pass\non a uni-processor system.\n\nBug fixes\n---------\nSem_destroy could return EBUSY even though no threads were waiting on the \nsemaphore. Other races around invalidating semaphore structs (internally)\nhave been removed as well.\n\nNew tests\n---------\nsemaphore5.c - tests the bug fix referred to above.\n\n\nRELEASE 2.7.0\n-------------\n(2005-06-04)\n\nGeneral\n-------\nAll new features in this release have been back-ported in release 1.11.0,\nincluding the incorporation of MCS locks in pthread_once, however, versions\n1 and 2 remain incompatible even though they are now identical in\nperformance and functionality.\n\nTesting and verification\n------------------------\nThis release has been tested (passed the test suite) on both uni-processor\nand multi-processor systems.\n- Tim Theisen\n\nBug fixes\n---------\nPthread_once has been re-implemented to remove priority boosting and other\ncomplexity to improve robustness. Races for Win32 handles that are not\nrecycle-unique have been removed. The general form of pthread_once is now\nthe same as that suggested earlier by Alexander Terekhov, but instead of the\n'named mutex', a queue-based lock has been implemented which has the required\nproperties of dynamic self initialisation and destruction. This lock is also\nefficient. The ABI is unaffected in as much as the size of pthread_once_t has\nnot changed and PTHREAD_ONCE_INIT has not changed, however, applications that\npeek inside pthread_once_t, which is supposed to be opaque, will break.\n- Vladimir Kliatchko\n\nNew features\n------------\n* Support for Mingw cross development tools added to GNUmakefile.\nMingw cross tools allow building the libraries on Linux.\n- Mikael Magnusson\n\n\nRELEASE 2.6.0\n-------------\n(2005-05-19)\n\nGeneral\n-------\nAll of the bug fixes and new features in this release have been\nback-ported in release 1.10.0.\n\nTesting and verification\n------------------------\nThis release has been tested (passed the test suite) on both uni-processor\nand multi-processor systems. Thanks to Tim Theisen at TomoTherapy for\nexhaustively running the MP tests and for providing crutial observations\nand data when faults are detected.\n\nBugs fixed\n----------\n\n* pthread_detach() now reclaims remaining thread resources if called after\nthe target thread has terminated. Previously, this routine did nothing in\nthis case.\n\nNew tests\n---------\n\n* detach1.c - tests that pthread_detach properly invalidates the target\nthread, which indicates that the thread resources have been reclaimed.\n\n\nRELEASE 2.5.0\n-------------\n(2005-05-09)\n\nGeneral\n-------\n\nThe package now includes a reference documentation set consisting of\nHTML formatted Unix-style manual pages that have been edited for\nconsistency with Pthreads-w32. The set can also be read online at:\nhttp://sources.redhat.com/pthreads-win32/manual/index.html\n\nThanks again to Tim Theisen for running the test suite pre-release\non an MP system.\n\nAll of the bug fixes and new features in this release have been\nback-ported in release 1.9.0.\n\nBugs fixed\n----------\n\n* Thread Specific Data (TSD) key management has been ammended to\neliminate a source of (what was effectively) resource leakage (a HANDLE\nplus memory for each key destruct routine/thread association). This was\nnot a true leak because these resources were eventually reclaimed when\npthread_key_delete was run AND each thread referencing the key had exited.\nThe problem was that these two conditions are often not met until very\nlate, and often not until the process is about to exit.\n\nThe ammended implementation avoids the need for the problematic HANDLE\nand reclaims the memory as soon as either the key is deleted OR the\nthread exits, whichever is first.\n\nThanks to Richard Hughes at Aculab for identifying and locating the leak.\n\n* TSD key destructors are now processed up to PTHREAD_DESTRUCTOR_ITERATIONS\ntimes instead of just once. PTHREAD_DESTRUCTOR_ITERATIONS has been\ndefined in pthread.h for some time but not used.\n\n* Fix a semaphore accounting race between sem_post/sem_post_multiple\nand sem_wait cancellation. This is the same issue as with\nsem_timedwait that was fixed in the last release.\n\n* sem_init, sem_post, and sem_post_multiple now check that the\nsemaphore count never exceeds _POSIX_SEM_VALUE_MAX.\n\n* Although sigwait() is nothing more than a no-op, it should at least\nbe a cancellation point to be consistent with the standard.\n\nNew tests\n---------\n\n* stress1.c - attempts to expose problems in condition variable\nand semaphore timed wait logic. This test was inspired by Stephan\nMueller's sample test code used to identify the sem_timedwait bug\nfrom the last release. It's not a part of the regular test suite\nbecause it can take awhile to run. To run it:\nnmake clean VC-stress\n\n* tsd2.c - tests that key destructors are re-run if the tsd key value is\nnot NULL after the destructor routine has run. Also tests that\npthread_setspecific() and pthread_getspecific() are callable from\ndestructors.\n\n\nRELEASE 2.4.0\n-------------\n(2005-04-26)\n\nGeneral\n-------\n\nThere is now no plan to release a version 3.0.0 to fix problems in\npthread_once(). Other possible implementations of pthread_once\nwill still be investigated for a possible future release in an attempt\nto reduce the current implementation's complexity.\n\nAll of the bug fixes and new features in this release have been\nback-ported for release 1.8.0.\n\nBugs fixed\n----------\n\n* Fixed pthread_once race (failures on an MP system). Thanks to\nTim Theisen for running exhaustive pre-release testing on his MP system\nusing a range of compilers:\n  VC++ 6\n  VC++ 7.1\n  Intel C++ version 8.0\nAll tests passed.\nSome minor speed improvements were also done.\n\n* Fix integer overrun error in pthread_mutex_timedlock() - missed when\nsem_timedwait() was fixed in release 2.2.0. This routine no longer returns\nENOTSUP when NEED_SEM is defined - it is supported (NEED_SEM is only\nrequired for WinCE versions prior to 3.0).\n\n* Fix timeout bug in sem_timedwait().\n- Thanks to Stephan Mueller for reporting, providing diagnostic output\nand test code.\n\n* Fix several problems in the NEED_SEM conditionally included code.\nNEED_SEM included code is provided for systems that don't implement W32\nsemaphores, such as WinCE prior to version 3.0. An alternate implementation\nof POSIX semaphores is built using W32 events for these systems when\nNEED_SEM is defined. This code has been completely rewritten in this\nrelease to reuse most of the default POSIX semaphore code, and particularly,\nto implement all of the sem_* routines supported by pthreads-win32. Tim\nTheisen also run the test suite over the NEED_SEM code on his MP system. All\ntests passed.\n\n* The library now builds without errors for the Borland Builder 5.5 compiler.\n\nNew features\n------------\n\n* pthread_mutex_timedlock() and all sem_* routines provided by\npthreads-win32 are now implemented for WinCE versions prior to 3.0. Those\nversions did not implement W32 semaphores. Define NEED_SEM in config.h when\nbuilding the library for these systems.\n\nKnown issues in this release\n----------------------------\n\n* pthread_once is too complicated - but it works as far as testing can\ndetermine..\n\n* The Borland version of the dll fails some of the tests with a memory read\nexception. The cause is not yet known but a compiler bug has not been ruled\nout.\n\n\nRELEASE 2.3.0\n-------------\n(2005-04-12)\n\nGeneral\n-------\n\nRelease 1.7.0 is a backport of features and bug fixes new in\nthis release. See earlier notes under Release 2.0.0/General.\n\nBugs fixed\n----------\n\n* Fixed pthread_once potential for post once_routine cancellation\nhanging due to starvation. See comments in pthread_once.c.\nMomentary priority boosting is used to ensure that, after a\nonce_routine is cancelled, the thread that will run the\nonce_routine is not starved by higher priority waiting threads at\ncritical times. Priority boosting occurs only AFTER a once_routine \ncancellation, and is applied only to that once_control. The\nonce_routine is run at the thread's normal base priority.\n\nNew tests\n---------\n\n* once4.c: Aggressively tests pthread_once() under realtime\nconditions using threads with varying priorities. Windows'\nrandom priority boosting does not occur for threads with realtime\npriority levels.\n\n\nRELEASE 2.2.0\n-------------\n(2005-04-04)\n\nGeneral\n-------\n\n* Added makefile targets to build static link versions of the library.\nBoth MinGW and MSVC. Please note that this does not imply any change\nto the LGPL licensing, which still imposes psecific conditions on\ndistributing software that has been statically linked with this library.\n\n* There is a known bug in pthread_once(). Cancellation of the init_routine\nexposes a potential starvation (i.e. deadlock) problem if a waiting thread\nhas a higher priority than the initting thread. This problem will be fixed\nin version 3.0.0 of the library.\n\nBugs fixed\n----------\n\n* Fix integer overrun error in sem_timedwait().\nKevin Lussier\n\n* Fix preprocessor directives for static linking.\nDimitar Panayotov\n\n\nRELEASE 2.1.0\n-------------\n(2005-03-16)\n\nBugs fixed\n----------\n\n* Reverse change to pthread_setcancelstate() in 2.0.0.\n\n\nRELEASE 2.0.0\n-------------\n(2005-03-16)\n\nGeneral\n-------\n\nThis release represents an ABI change and the DLL version naming has\nincremented from 1 to 2, e.g. pthreadVC2.dll.\n\nVersion 1.4.0 back-ports the new functionality included in this\nrelease. Please distribute DLLs built from that version with updates\nto applications built on pthreads-win32 version 1.x.x.\n\nThe package naming has changed, replacing the snapshot date with \nthe version number + descriptive information. E.g. this\nrelease is \"pthreads-w32-2-0-0-release\".\n\nBugs fixed\n----------\n\n* pthread_setcancelstate() no longer checks for a pending\nasync cancel event if the library is using alertable async\ncancel. See the README file (Prerequisites section) for info\non adding alertable async cancelation.\n\nNew features\n------------\n\n* pthread_once() now supports init_routine cancellability.\n\nNew tests\n---------\n\n* Agressively test pthread_once() init_routine cancellability.\n\n\nSNAPSHOT 2005-03-08\n-------------------\nVersion 1.3.0\n\nBug reports (fixed)\n-------------------\n\n* Implicitly created threads leave Win32 handles behind after exiting.\n- Dmitrii Semii\n\n* pthread_once() starvation problem.\n- Gottlob Frege\n\nNew tests\n---------\n\n* More intense testing of pthread_once().\n\n\nSNAPSHOT 2005-01-25\n-------------------\nVersion 1.2.0\n\nBug fixes\n---------\n\n* Attempted acquisition of a recursive mutex could cause waiting threads\nto not be woken when the mutex was released.\n- Ralf Kubis  <RKubis at mc.com>\n\n* Various package omissions have been fixed.\n\n\nSNAPSHOT 2005-01-03\n-------------------\nVersion 1.1.0\n\nBug fixes\n---------\n\n* Unlocking recursive or errorcheck mutexes would sometimes\nunexpectedly return an EPERM error (bug introduced in\nsnapshot-2004-11-03).\n- Konstantin Voronkov  <beowinkle at yahoo.com>\n\n\nSNAPSHOT 2004-11-22\n-------------------\nVersion 1.0.0\n\nThis snapshot primarily fixes the condvar bug introduced in\nsnapshot-2004-11-03. DLL versioning has also been included to allow\napplications to runtime check the Microsoft compatible DLL version\ninformation, and to extend the DLL naming system for ABI and major\n(non-backward compatible) API changes. See the README file for details.\n\nBug fixes\n---------\n\n* Condition variables no longer deadlock (bug introduced in\nsnapshot-2004-11-03).\n- Alexander Kotliarov and Nicolas at saintmac\n\n* DLL naming extended to avoid 'DLL hell' in the future, and to\naccommodate the ABI change introduced in snapshot-2004-11-03. Snapshot\n2004-11-03 will be removed from FTP sites.\n\nNew features\n------------\n\n* A Microsoft-style version resource has been added to the DLL for\napplications that wish to check DLL compatibility at runtime.\n\n* Pthreads-win32 DLL naming has been extended to allow incompatible DLL\nversions to co-exist in the same filesystem. See the README file for details,\nbut briefly: while the version information inside the DLL will change with\neach release from now on, the DLL version names will only change if the new\nDLL is not backward compatible with older applications.\n\nThe versioning scheme has been borrowed from GNU Libtool, and the DLL\nnaming scheme is from Cygwin. Provided the Libtool-style numbering rules are\nhonoured, the Cygwin DLL naming scheme automatcally ensures that DLL name\nchanges are minimal and that applications will not load an incompatible\npthreads-win32 DLL.\n\nThose who use the pre-built DLLs will find that the DLL/LIB names have a new\nsuffix (1) in this snapshot. E.g. pthreadVC1.dll etc.\n\n* The POSIX thread ID reuse uniqueness feature introduced in the last snapshot\nhas been kept as default, but the behaviour can now be controlled when the DLL\nis built to effectively switch it off. This makes the library much more\nsensitive to applications that assume that POSIX thread IDs are unique, i.e.\nare not strictly compliant with POSIX. See the PTW32_THREAD_ID_REUSE_INCREMENT\nmacro comments in config.h for details.\n\nOther changes\n-------------\nCertain POSIX macros have changed.\n\nThese changes are intended to conform to the Single Unix Specification version 3,\nwhich states that, if set to 0 (zero) or not defined, then applications may use\nsysconf() to determine their values at runtime. Pthreads-win32 does not\nimplement sysconf().\n\nThe following macros are no longer undefined, but defined and set to -1\n(not implemented):\n\n      _POSIX_THREAD_ATTR_STACKADDR\n      _POSIX_THREAD_PRIO_INHERIT\n      _POSIX_THREAD_PRIO_PROTECT\n      _POSIX_THREAD_PROCESS_SHARED\n\nThe following macros are defined and set to 200112L (implemented):\n\n      _POSIX_THREADS\n      _POSIX_THREAD_SAFE_FUNCTIONS\n      _POSIX_THREAD_ATTR_STACKSIZE\n      _POSIX_THREAD_PRIORITY_SCHEDULING\n      _POSIX_SEMAPHORES\n      _POSIX_READER_WRITER_LOCKS\n      _POSIX_SPIN_LOCKS\n      _POSIX_BARRIERS\n\nThe following macros are defined and set to appropriate values:\n\n      _POSIX_THREAD_THREADS_MAX\n      _POSIX_SEM_VALUE_MAX\n      _POSIX_SEM_NSEMS_MAX\n      PTHREAD_DESTRUCTOR_ITERATIONS\n      PTHREAD_KEYS_MAX\n      PTHREAD_STACK_MIN\n      PTHREAD_THREADS_MAX\n\n\nSNAPSHOT 2004-11-03\n-------------------\n\nDLLs produced from this snapshot cannot be used with older applications without\nrecompiling the application, due to a change to pthread_t to provide unique POSIX\nthread IDs.\n\nAlthough this snapshot passes the extended test suite, many of the changes are\nfairly major, and some applications may show different behaviour than previously,\nso adopt with care. Hopefully, any changed behaviour will be due to the library\nbeing better at it's job, not worse.\n\nBug fixes\n---------\n\n* pthread_create() no longer accepts NULL as the thread reference arg.\nA segfault (memory access fault) will result, and no thread will be\ncreated.\n\n* pthread_barrier_wait() no longer acts as a cancelation point.\n\n* Fix potential race condition in pthread_once()\n- Tristan Savatier  <tristan at mpegtv.com>\n\n* Changes to pthread_cond_destroy() exposed some coding weaknesses in several\ntest suite mini-apps because pthread_cond_destroy() now returns EBUSY if the CV\nis still in use.\n\nNew features\n------------\n\n* Added for compatibility:\nPTHREAD_RECURSIVE_MUTEX_INITIALIZER,\nPTHREAD_ERRORCHECK_MUTEX_INITIALIZER,\nPTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,\nPTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP\n\n* Initial support for Digital Mars compiler\n- Anuj Goyal  <anuj.goyal at gmail.com>\n\n* Faster Mutexes. These have been been rewritten following a model provided by\nAlexander Terekhov that reduces kernel space checks, and eliminates some additional\ncritical sections used to manage a race between timedlock expiration and unlock.\nPlease be aware that the new mutexes do not enforce strict absolute FIFO scheduling\nof mutexes, however any out-of-order lock acquisition should be very rare.\n\n* Faster semaphores. Following a similar model to mutexes above, these have been\nrewritten to use preliminary users space checks.\n\n* sem_getvalue() now returns the number of waiters.\n\n* The POSIX thread ID now has much stronger uniqueness characteristics. The library\ngarrantees not to reuse the same thread ID for at least 2^(wordsize) thread\ndestruction/creation cycles.\n\nNew tests\n---------\n\n* semaphore4.c: Tests cancelation of the new sem_wait().\n\n* semaphore4t.c: Likewise for sem_timedwait().\n\n* rwlock8.c: Tests and times the slow execution paths of r/w locks, and the CVs,\nmutexes, and semaphores that they're built on.\n\n\nSNAPSHOT 2004-05-16\n-------------------\n\nAttempt to add Watcom to the list of compilers that can build the library.\nThis failed in the end due to it's non-thread-aware errno. The library\nbuilds but the test suite fails. See README.Watcom for more details.\n\nBug fixes\n---------\n* Bug and memory leak in sem_init()\n- Alex Blanco  <Alex.Blanco at motorola.com>\n\n* ptw32_getprocessors() now returns CPU count of 1 for WinCE.\n- James Ewing  <james.ewing at sveasoft.com>\n\n* pthread_cond_wait() could be canceled at a point where it should not\nbe cancelable. Fixed.\n- Alexander Terekhov  <TEREKHOV at de.ibm.com>\n\n* sem_timedwait() had an incorrect timeout calculation.\n- Philippe Di Cristo  <philipped at voicebox.com>\n\n* Fix a memory leak left behind after threads are destroyed.\n- P. van Bruggen  <pietvb at newbridges.nl>\n\nNew features\n------------\n* Ported to AMD64.\n- Makoto Kato  <raven at oldskool.jp>\n\n* True pre-emptive asynchronous cancelation of threads. This is optional\nand requires that Panagiotis E. Hadjidoukas's QueueUserAPCEx package be\ninstalled. This package is included in the pthreads-win32 self-unpacking\nZip archive starting from this snapshot. See the README.txt file inside\nthe package for installation details.\n\nNote: If you don't use async cancelation in your application, or don't need\nto cancel threads that are blocked on system resources such as network I/O,\nthen the default non-preemptive async cancelation is probably good enough.\nHowever, pthreads-win32 auto-detects the availability of these components\nat run-time, so you don't need to rebuild the library from source if you\nchange your mind later.\n\nAll of the advice available in books and elsewhere on the undesirability\nof using async cancelation in any application still stands, but this\nfeature is a welcome addition with respect to the library's conformance to\nthe POSIX standard.\n\nSNAPSHOT 2003-09-18\n-------------------\n\nCleanup of thread priority management. In particular, setting of thread\npriority now attempts to map invalid Win32 values within the range returned\nby sched_get_priority_min/max() to useful values. See README.NONPORTABLE\nunder \"Thread priority\".\n\nBug fixes\n---------\n* pthread_getschedparam() now returns the priority given by the most recent\ncall to pthread_setschedparam() or established by pthread_create(), as\nrequired by the standard. Previously, pthread_getschedparam() incorrectly\nreturned the running thread priority at the time of the call, which may have\nbeen adjusted or temporarily promoted/demoted.\n\n* sched_get_priority_min() and sched_get_priority_max() now return -1 on error\nand set errno. Previously, they incorrectly returned the error value directly.\n\n\nSNAPSHOT 2003-09-04\n-------------------\n\nBug fixes\n---------\n* ptw32_cancelableWait() now allows cancelation of waiting implicit POSIX\nthreads.\n\nNew test\n--------\n* cancel8.c tests cancelation of Win32 threads waiting at a POSIX cancelation\npoint.\n\n\nSNAPSHOT 2003-09-03\n-------------------\n\nBug fixes\n---------\n* pthread_self() would free the newly created implicit POSIX thread handle if\nDuplicateHandle failed instead of recycle it (very unlikely).\n\n* pthread_exit() was neither freeing nor recycling the POSIX thread struct\nfor implicit POSIX threads.\n\nNew feature - Cancelation of/by Win32 (non-POSIX) threads\n---------------------------------------------------------\nSince John Bossom's original implementation, the library has allowed non-POSIX\ninitialised threads (Win32 threads) to call pthreads-win32 routines and\ntherefore interact with POSIX threads. This is done by creating an on-the-fly\nPOSIX thread ID for the Win32 thread that, once created, allows fully\nreciprical interaction. This did not extend to thread cancelation (async or\ndeferred). Now it does.\n\nAny thread can be canceled by any other thread (Win32 or POSIX) if the former\nthread's POSIX pthread_t value is known. It's TSD destructors and POSIX\ncleanup handlers will be run before the thread exits with an exit code of\nPTHREAD_CANCELED (retrieved with GetExitCodeThread()).\n\nThis allows a Win32 thread to, for example, call POSIX CV routines in the same way\nthat POSIX threads would/should, with pthread_cond_wait() cancelability and\ncleanup handlers (pthread_cond_wait() is a POSIX cancelation point).\n\nBy adding cancelation, Win32 threads should now be able to call all POSIX\nthreads routines that make sense including semaphores, mutexes, condition\nvariables, read/write locks, barriers, spinlocks, tsd, cleanup push/pop,\ncancelation, pthread_exit, scheduling, etc.\n\nNote that these on-the-fly 'implicit' POSIX thread IDs are initialised as detached\n(not joinable) with deferred cancelation type. The POSIX thread ID will be created\nautomatically by any POSIX routines that need a POSIX handle (unless the routine\nneeds a pthread_t as a parameter of course). A Win32 thread can discover it's own\nPOSIX thread ID by calling pthread_self(), which will create the handle if\nnecessary and return the pthread_t value.\n\nNew tests\n---------\nTest the above new feature.\n\n\nSNAPSHOT 2003-08-19\n-------------------\n\nThis snapshot fixes some accidental corruption to new test case sources.\nThere are no changes to the library source code.\n\n\nSNAPSHOT 2003-08-15\n-------------------\n\nBug fixes\n---------\n\n* pthread.dsp now uses correct compile flags (/MD).\n- Viv  <vcotirlea@hotmail.com>\n\n* pthread_win32_process_detach_np() fixed memory leak.\n- Steven Reddie  <Steven.Reddie@ca.com>\n\n* pthread_mutex_destroy() fixed incorrect return code.\n- Nicolas Barry  <boozai@yahoo.com>\n\n* pthread_spin_destroy() fixed memory leak.\n- Piet van Bruggen  <pietvb@newbridges.nl>\n\n* Various changes to tighten arg checking, and to work with later versions of\nMinGW32 and MsysDTK.\n\n* pthread_getschedparam() etc, fixed dangerous thread validity checking.\n- Nicolas Barry  <boozai@yahoo.com>\n\n* POSIX thread handles are now reused and their memory is not freed on thread exit.\nThis allows for stronger thread validity checking.\n\nNew standard routine\n--------------------\n\n* pthread_kill() added to provide thread validity checking to applications.\nIt does not accept any non zero values for the signal arg.\n\nNew test cases\n--------------\n\n* New test cases to confirm validity checking, pthread_kill(), and thread reuse.\n\n\nSNAPSHOT 2003-05-10\n-------------------\n\nBug fixes\n---------\n\n* pthread_mutex_trylock() now returns correct error values.\npthread_mutex_destroy() will no longer destroy a recursively locked mutex.\npthread_mutex_lock() is no longer inadvertantly behaving as a cancelation point.\n- Thomas Pfaff  <tpfaff@gmx.net>\n\n* pthread_mutex_timedlock() no longer occasionally sets incorrect mutex\nownership, causing deadlocks in some applications.\n- Robert Strycek <strycek@posam.sk> and Alexander Terekhov  <TEREKHOV@de.ibm.com>\n\n\nSNAPSHOT 2002-11-04\n-------------------\n\nBug fixes\n---------\n\n* sem_getvalue() now returns the correct value under Win NT and WinCE.\n- Rob Fanner  <rfanner@stonethree.com>\n\n* sem_timedwait() now uses tighter checks for unreasonable\nabstime values - that would result in unexpected timeout values.\n\n* ptw32_cond_wait_cleanup() no longer mysteriously consumes\nCV signals but may produce more spurious wakeups. It is believed\nthat the sem_timedwait() call is consuming a CV signal that it\nshouldn't.\n- Alexander Terekhov  <TEREKHOV@de.ibm.com>\n\n* Fixed a memory leak in ptw32_threadDestroy() for implicit threads.\n\n* Fixed potential for deadlock in pthread_cond_destroy().\nA deadlock could occur for statically declared CVs (PTHREAD_COND_INITIALIZER),\nwhen one thread is attempting to destroy the condition variable while another\nis attempting to dynamically initialize it.\n- Michael Johnson  <michaelj@maine.rr.com>\n\n\nSNAPSHOT 2002-03-02\n-------------------\n\nCleanup code default style. (IMPORTANT)\n----------------------------------------------------------------------\nPreviously, if not defined, the cleanup style was determined automatically\nfrom the compiler/language, and one of the following was defined accordingly:\n\n        __CLEANUP_SEH   MSVC only\n        __CLEANUP_CXX   C++, including MSVC++, GNU G++\n        __CLEANUP_C             C, including GNU GCC, not MSVC\n\nThese defines determine the style of cleanup (see pthread.h) and,\nmost importantly, the way that cancelation and thread exit (via\npthread_exit) is performed (see the routine ptw32_throw() in private.c).\n\nIn short, the exceptions versions of the library throw an exception\nwhen a thread is canceled or exits (via pthread_exit()), which is\ncaught by a handler in the thread startup routine, so that the\nthe correct stack unwinding occurs regardless of where the thread\nis when it's canceled or exits via pthread_exit().\n\nIn this and future snapshots, unless the build explicitly defines (e.g.\nvia a compiler option) __CLEANUP_SEH, __CLEANUP_CXX, or __CLEANUP_C, then\nthe build NOW always defaults to __CLEANUP_C style cleanup. This style\nuses setjmp/longjmp in the cancelation and pthread_exit implementations,\nand therefore won't do stack unwinding even when linked to applications\nthat have it (e.g. C++ apps). This is for consistency with most\ncurrent commercial Unix POSIX threads implementations. Compaq's TRU64\nmay be an exception (no pun intended) and possible future trend.\n\nAlthough it was not clearly documented before, it is still necessary to\nbuild your application using the same __CLEANUP_* define as was\nused for the version of the library that you link with, so that the\ncorrect parts of pthread.h are included. That is, the possible\ndefines require the following library versions:\n\n        __CLEANUP_SEH   pthreadVSE.dll\n        __CLEANUP_CXX   pthreadVCE.dll or pthreadGCE.dll\n        __CLEANUP_C     pthreadVC.dll or pthreadGC.dll\n\nE.g. regardless of whether your app is C or C++, if you link with\npthreadVC.lib or libpthreadGC.a, then you must define __CLEANUP_C.\n\n\nTHE POINT OF ALL THIS IS: if you have not been defining one of these\nexplicitly, then the defaults as described at the top of this\nsection were being used.\n\nTHIS NOW CHANGES, as has been explained above, but to try to make this\nclearer here's an example:\n\nIf you were building your application with MSVC++ i.e. using C++\nexceptions and not explicitly defining one of __CLEANUP_*, then\n__CLEANUP_C++ was automatically defined for you in pthread.h.\nYou should have been linking with pthreadVCE.dll, which does\nstack unwinding.\n\nIf you now build your application as you had before, pthread.h will now\nautomatically set __CLEANUP_C as the default style, and you will need to\nlink with pthreadVC.dll. Stack unwinding will now NOT occur when a thread\nis canceled, or the thread calls pthread_exit().\n\nYour application will now most likely behave differently to previous\nversions, and in non-obvious ways. Most likely is that locally\ninstantiated objects may not be destroyed or cleaned up after a thread\nis canceled.\n\nIf you want the same behaviour as before, then you must now define\n__CLEANUP_C++ explicitly using a compiler option and link with\npthreadVCE.dll as you did before.\n\n\nWHY ARE WE MAKING THE DEFAULT STYLE LESS EXCEPTION-FRIENDLY?\nBecause no commercial Unix POSIX threads implementation allows you to\nchoose to have stack unwinding. Therefore, providing it in pthread-win32\nas a default is dangerous. We still provide the choice but unless\nyou consciously choose to do otherwise, your pthreads applications will\nnow run or crash in similar ways irrespective of the threads platform\nyou use. Or at least this is the hope.\n\n\nWHY NOT REMOVE THE EXCEPTIONS VERSIONS OF THE LIBRARY ALTOGETHER?\nThere are a few reasons:\n- because there are well respected POSIX threads people who believe\n  that POSIX threads implementations should be exceptions aware and\n  do the expected thing in that context. (There are equally respected\n  people who believe it should not be easily accessible, if it's there\n  at all, for unconditional conformity to other implementations.)\n- because pthreads-win32 is one of the few implementations that has\n  the choice, perhaps the only freely available one, and so offers\n  a laboratory to people who may want to explore the effects;\n- although the code will always be around somewhere for anyone who\n  wants it, once it's removed from the current version it will not be\n  nearly as visible to people who may have a use for it.\n\n\nSource module splitting\n-----------------------\nIn order to enable smaller image sizes to be generated\nfor applications that link statically with the library,\nmost routines have been separated out into individual\nsource code files.\n\nThis is being done in such a way as to be backward compatible.\nThe old source files are reused to congregate the individual\nroutine files into larger translation units (via a bunch of\n# includes) so that the compiler can still optimise wherever\npossible, e.g. through inlining, which can only be done\nwithin the same translation unit.\n\nIt is also possible to build the entire library by compiling\nthe single file named \"pthread.c\", which just #includes all\nthe secondary congregation source files. The compiler\nmay be able to use this to do more inlining of routines.\n\nAlthough the GNU compiler is able to produce libraries with\nthe necessary separation (the -ffunction-segments switch),\nAFAIK, the MSVC and other compilers don't have this feature.\n\nFinally, since I use makefiles and command-line compilation,\nI don't know what havoc this reorganisation may wreak amongst\nIDE project file users. You should be able to continue\nusing your existing project files without modification.\n\n\nNew non-portable functions\n--------------------------\npthread_num_processors_np():\n  Returns the number of processors in the system that are\n  available to the process, as determined from the processor\n  affinity mask.\n\npthread_timechange_handler_np():\n  To improve tolerance against operator or time service initiated\n  system clock changes.\n\n  This routine can be called by an application when it\n  receives a WM_TIMECHANGE message from the system. At present\n  it broadcasts all condition variables so that waiting threads\n  can wake up and re-evaluate their conditions and restart\n  their timed waits if required.\n  - Suggested by Alexander Terekhov\n\n\nPlatform dependence\n-------------------\nAs Win95 doesn't provide one, the library now contains\nit's own InterlockedCompareExchange() routine, which is used\nwhenever Windows doesn't provide it. InterlockedCompareExchange()\nis used to implement spinlocks and barriers, and also in mutexes.\nThis routine relies on the CMPXCHG machine instruction which\nis not available on i386 CPUs. This library (from snapshot\n20010712 onwards) is therefore no longer supported on i386\nprocessor platforms.\n\n\nNew standard routines\n---------------------\nFor source code portability only - rwlocks cannot be process shared yet.\n\n        pthread_rwlockattr_init()\n        pthread_rwlockattr_destroy()\n        pthread_rwlockattr_setpshared()\n        pthread_rwlockattr_getpshared()\n\nAs defined in the new POSIX standard, and the Single Unix Spec version 3:\n\n        sem_timedwait()\n        pthread_mutex_timedlock()    - Alexander Terekhov and Thomas Pfaff\n        pthread_rwlock_timedrdlock() - adapted from pthread_rwlock_rdlock()\n        pthread_rwlock_timedwrlock() - adapted from pthread_rwlock_wrlock()\n\n\npthread.h no longer includes windows.h\n--------------------------------------\n[Not yet for G++]\n\nThis was done to prevent conflicts.\n\nHANDLE, DWORD, and NULL are temporarily defined within pthread.h if\nthey are not already.\n\n\npthread.h, sched.h and semaphore.h now use dllexport/dllimport\n--------------------------------------------------------------\nNot only to avoid the need for the pthread.def file, but to\nimprove performance. Apparently, declaring functions with dllimport\ngenerates a direct call to the function and avoids the overhead\nof a stub function call.\n\nBug fixes\n---------\n* Fixed potential NULL pointer dereferences in pthread_mutexattr_init,\npthread_mutexattr_getpshared, pthread_barrierattr_init,\npthread_barrierattr_getpshared, and pthread_condattr_getpshared.\n- Scott McCaskill <scott@magruder.org>\n\n* Removed potential race condition in pthread_mutex_trylock and\npthread_mutex_lock;\n- Alexander Terekhov <TEREKHOV@de.ibm.com>\n\n* The behaviour of pthread_mutex_trylock in relation to\nrecursive mutexes was inconsistent with commercial implementations.\nTrylock would return EBUSY if the lock was owned already by the\ncalling thread regardless of mutex type. Trylock now increments the\nrecursion count and returns 0 for RECURSIVE mutexes, and will\nreturn EDEADLK rather than EBUSY for ERRORCHECK mutexes. This is\nconsistent with Solaris.\n- Thomas Pfaff <tpfaff@gmx.net>\n\n* Found a fix for the library and workaround for applications for\nthe known bug #2, i.e. where __CLEANUP_CXX or __CLEANUP_SEH is defined.\nSee the \"Known Bugs in this snapshot\" section below.\n\nThis could be made transparent to applications by replacing the macros that\ndefine the current C++ and SEH versions of pthread_cleanup_push/pop\nwith the C version, but AFAIK cleanup handlers would not then run in the\ncorrect sequence with destructors and exception cleanup handlers when\nan exception occurs.\n\n* Cancelation once started in a thread cannot now be inadvertantly\ndouble canceled. That is, once a thread begins it's cancelation run,\ncancelation is disabled and a subsequent cancel request will\nreturn an error (ESRCH).\n\n* errno: An incorrect compiler directive caused a local version\nof errno to be used instead of the Win32 errno. Both instances are\nthread-safe but applications checking errno after a pthreads-win32\ncall would be wrong. Fixing this also fixed a bad compiler\noption in the testsuite (/MT should have been /MD) which is\nneeded to link with the correct library MSVCRT.LIB.\n\n\nSNAPSHOT 2001-07-12\n-------------------\n\nTo be added\n\n\nSNAPSHOT 2001-07-03\n-------------------\n\nTo be added\n\n\nSNAPSHOT 2000-08-13\n-------------------\n\nNew:\n-       Renamed DLL and LIB files:\n                pthreadVSE.dll  (MS VC++/Structured EH)\n                pthreadVSE.lib\n                pthreadVCE.dll  (MS VC++/C++ EH)\n                pthreadVCE.lib\n                pthreadGCE.dll  (GNU G++/C++ EH)\n                libpthreadw32.a\n\n        Both your application and the pthread dll should use the\n        same exception handling scheme.\n\nBugs fixed:\n-       MSVC++ C++ exception handling.\n\nSome new tests have been added.\n\n\nSNAPSHOT 2000-08-10\n-------------------\n\nNew:\n-       asynchronous cancelation on X86 (Jason Nye)\n-       Makefile compatible with MS nmake to replace\n        buildlib.bat\n-       GNUmakefile for Mingw32\n-       tests/Makefile for MS nmake replaces runall.bat\n-       tests/GNUmakefile for Mingw32\n\nBugs fixed:\n-       kernel32 load/free problem\n-       attempt to hide internel exceptions from application\n        exception handlers (__try/__except and try/catch blocks)\n-       Win32 thread handle leakage bug\n        (David Baggett/Paul Redondo/Eyal Lebedinsky)\n\nSome new tests have been added.\n\n\nSNAPSHOT 1999-11-02\n-------------------\n\nBugs fixed:\n-       ctime_r macro had an incorrect argument (Erik Hensema),\n-       threads were not being created \n        PTHREAD_CANCEL_DEFERRED. This should have\n        had little effect as deferred is the only\n        supported type. (Ross Johnson).\n\nSome compatibility improvements added, eg.\n-       pthread_setcancelstate accepts NULL pointer\n        for the previous value argument. Ditto for\n        pthread_setcanceltype. This is compatible\n        with Solaris but should not affect\n        standard applications (Erik Hensema)\n\nSome new tests have been added.\n\n\nSNAPSHOT 1999-10-17\n-------------------\n\nBug fix - Cancelation of threads waiting on condition variables\nnow works properly (Lorin Hochstein and Peter Slacik)\n\n\nSNAPSHOT 1999-08-12\n-------------------\n\nFixed exception stack cleanup if calling pthread_exit()\n- (Lorin Hochstein and John Bossom).\n\nFixed bugs in condition variables - (Peter Slacik):\n        - additional contention checks\n        - properly adjust number of waiting threads after timed\n          condvar timeout.\n\n\nSNAPSHOT 1999-05-30\n-------------------\n\nSome minor bugs have been fixed. See the ChangeLog file for details.\n\nSome more POSIX 1b functions are now included but ony return an\nerror (ENOSYS) if called. They are:\n\n        sem_open\n        sem_close\n        sem_unlink\n        sem_getvalue\n\n\nSNAPSHOT 1999-04-07\n-------------------\n\nSome POSIX 1b functions which were internally supported are now\navailable as exported functions:\n\n        sem_init\n        sem_destroy\n        sem_wait\n        sem_trywait\n        sem_post\n        sched_yield\n        sched_get_priority_min\n        sched_get_priority_max\n\nSome minor bugs have been fixed. See the ChangeLog file for details.\n\n\nSNAPSHOT 1999-03-16\n-------------------\n\nInitial release.\n\n"
  },
  {
    "path": "deps/w32-pthreads/Nmakefile",
    "content": "/*\n * nmake file for uwin pthread library\n */\n\nVERSION \t\t= -\nCCFLAGS \t\t= -V -g $(CC.DLL)\nHAVE_PTW32_CONFIG_H\t== 1\n_MT\t\t\t== 1\n_timeb\t\t== timeb\n_ftime\t\t== ftime\n_errno\t\t== _ast_errno\n\n$(INCLUDEDIR)\t:INSTALLDIR:\tpthread.h sched.h\n\npthread $(VERSION) :LIBRARY: attr.c barrier.c cancel.c cleanup.c condvar.c \\\n\tcreate.c dll.c exit.c fork.c global.c misc.c mutex.c private.c \\\n\trwlock.c sched.c semaphore.c spin.c sync.c tsd.c nonportable.c\n\n:: ANNOUNCE CONTRIBUTORS COPYING.LIB ChangeLog FAQ GNUmakefile MAINTAINERS \\\n\tMakefile Makefile.in Makefile.vc NEWS PROGRESS README README.WinCE \\\n\tTODO WinCE-PORT install-sh errno.c tests tests.mk acconfig.h \\\n\tconfig.guess config.h.in config.sub configure configure.in signal.c \\\n\tREADME.CV README.NONPORTABLE pthread.dsp pthread.dsw\n\n"
  },
  {
    "path": "deps/w32-pthreads/Nmakefile.tests",
    "content": "/* for running tests */\nCCFLAGS \t= -g \n_MT\t\t== 1\n_timeb\t== timeb\n_ftime\t== ftime \n\n.SOURCE:\ttests\n/*\n:PACKAGE:\tpthread\n*/\n\nset keepgoing\n\n\":test:\" : .MAKE .OPERATOR\n\tlocal I\n\t$(<:D:B:S=.pass) : .IMPLICIT $(>:D:B:S=.pass)\n\tfor I $(<) $(>)\n\t\t$(I:D:B:S=.pass) : .VIRTUAL .FORCE $(I)\n\t\t\t$(>)\n\tend\nsizes::\t\tsizes.c\nloadfree::\tloadfree.c\nmutex1::\tmutex1.c\nmutex1e::\tmutex1e.c\nmutex1n::\tmutex1n.c\nmutex1r::\tmutex1r.c\nmutex2::\tmutex2.c\nmutex2r::\tmutex2r.c\nmutex2e::\tmutex2e.c\nexit1::\texit1.c\ncondvar1::\tcondvar1.c\ncondvar1_1::\tcondvar1_1.c\ncondvar1_2::\tcondvar1_2.c\nself1::\t\tself1.c\ncondvar2::\tcondvar2.c\ncondvar2_1::\tcondvar2_1.c\ncondvar3_1::\tcondvar3_1.c\ncondvar3_2::\tcondvar3_2.c\ncondvar3_3::\tcondvar3_3.c\ncreate1.::\tcreate1.c\ncreate2.::\tcreate2.c\ncancel1::\tcancel1.c\ncancel2::\tcancel2.c\nmutex3::\tmutex3.c\nmutex3r::\tmutex3r.c\nmutex3e::\tmutex3e.c\nmutex4::\tmutex4.c\nmutex5::\tmutex5.c\nmutex6::\tmutex6.c\nmutex6e::\tmutex6e.c\nmutex6n::\tmutex6n.c\nmutex6r::\tmutex6r.c\nmutex7::\tmutex7.c\nmutex6s::\tmutex6s.c\nmutex6rs::\tmutex6rs.c\nmutex6es::\tmutex6es.c\nmutex7e::\tmutex7e.c\nmutex7n::\tmutex7n.c\nmutex7r::\tmutex7r.c\nmutex8::\tmutex8.c\nmutex8e::\tmutex8e.c\nmutex8n::\tmutex8n.c\nmutex8r::\tmutex8r.c\nequal1::\tequal1.c\nexit2::\t\texit2.c\nexit3::\t\texit3.c\nexit4::\t\texit4.c\nexit5::\t\texit5.c\njoin0::\t\tjoin0.c\njoin1::\t\tjoin1.c\njoin2::\t\tjoin2.c\njoin3::\t\tjoin3.c\nkill1::\t\tkill1.c\ncount1::\tcount1.c\nonce1::\t\tonce1.c\ntsd1::\t\ttsd1.c\nself2::\t\tself2.c\neyal1::\t\teyal1.c\ncondvar3::\tcondvar3.c\ncondvar4::\tcondvar4.c\ncondvar5::\tcondvar5.c\ncondvar6::\tcondvar6.c\ncondvar7::\tcondvar7.c\ncondvar8::\tcondvar8.c\ncondvar9::\tcondvar9.c\nerrno1::\terrno1.c\nreuse1.::\treuse1.c\nreuse2.::\treuse2.c\nrwlock1::\trwlock1.c\nrwlock2::\trwlock2.c\nrwlock3::\trwlock3.c\nrwlock4::\trwlock4.c\nrwlock5::\trwlock5.c\nrwlock6::\trwlock6.c\nrwlock7::\trwlock7.c\nrwlock8::\trwlock8.c\nrwlock2_t::\trwlock2_t.c\nrwlock3_t::\trwlock3_t.c\nrwlock4_t::\trwlock4_t.c\nrwlock5_t::\trwlock5_t.c\nrwlock6_t::\trwlock6_t.c\nrwlock6_t2::\trwlock6_t2.c\nsemaphore1::\tsemaphore1.c\nsemaphore2::\tsemaphore2.c\nsemaphore3::\tsemaphore3.c\ncontext1::\tcontext1.c\ncancel3::\tcancel3.c\ncancel4::\tcancel4.c\ncancel5::\tcancel5.c\ncancel6a::\tcancel6a.c\ncancel6d::\tcancel6d.c\ncancel7::\tcancel7.c\ncleanup0::\tcleanup0.c\ncleanup1::\tcleanup1.c\ncleanup2::\tcleanup2.c\ncleanup3::\tcleanup3.c\npriority1::     priority1.c\npriority2::     priority2.c\ninherit1::      inherit1.c\nspin1::         spin1.c\nspin2::         spin2.c\nspin3::         spin3.c\nspin4::         spin4.c\nbarrier1::      barrier1.c\nbarrier2::      barrier2.c\nbarrier3::      barrier3.c\nbarrier4::      barrier4.c\nbarrier5::      barrier5.c\nexception1::\texception1.c\nexception2::\texception2.c\nexception3::\texception3.c\nbenchtest1::    benchtest1.c\nbenchtest2::    benchtest2.c\nbenchtest3::    benchtest3.c\nbenchtest4::    benchtest4.c\nbenchtest5::    benchtest5.c\nvalid1::\tvalid1.c\nvalid2::\tvalid2.c\ncancel9::\tcancel9.c\n\nsizes:\t\t:test:\tsizes\nloadfree:\t:test:\nmutex5\t\t:test:\tloadfree\nmutex1\t\t:test:\tloadfree\nmutex1n\t\t:test:\tloadfree\nmutex1r\t\t:test:\tloadfree\nmutex1e\t\t:test:\tloadfree\nsemaphore1\t:test:\tloadfree\nsemaphore2\t:test:\tloadfree\nsemaphore3\t:test:\tloadfree\nmutex2\t\t:test:\tloadfree\nmutex2r\t\t:test:\tloadfree\nmutex2e\t\t:test:\tloadfree\nexit1\t\t:test:\tloadfree\ncondvar1\t:test:\tloadfree\nkill1\t\t:test:\tloadfree\ncondvar1_1\t:test:\tcondvar1\ncondvar1_2\t:test:\tjoin2\nself1\t\t:test:\tloadfree\ncondvar2\t:test:\tcondvar1\ncondvar2_1\t:test:\tcondvar2\ncreate1 \t:test:\tmutex2\ncreate2 \t:test:\tcreate1\nreuse1 \t\t:test:\tcreate2\nreuse2 \t\t:test:\treuse1\ncancel1\t\t:test:\tcreate1\ncancel2\t\t:test:\tcancel1\nmutex3\t\t:test:\tcreate1\nmutex3r\t\t:test:\tcreate1\nmutex3e\t\t:test:\tcreate1\nmutex4\t\t:test:\tmutex3\nmutex6\t\t:test:\tmutex4\nmutex6n\t\t:test:\tmutex4\nmutex6e\t\t:test:\tmutex4\nmutex6r\t\t:test:\tmutex4\nmutex6s\t\t:test:\tmutex6\nmutex6rs\t:test:\tmutex6r\nmutex6es\t:test:\tmutex6e\nmutex7\t\t:test:\tmutex6\nmutex7n\t\t:test:\tmutex6n\nmutex7e\t\t:test:\tmutex6e\nmutex7r\t\t:test:\tmutex6r\nmutex8\t\t:test:\tmutex7\nmutex8n\t\t:test:\tmutex7n\nmutex8e\t\t:test:\tmutex7e\nmutex8r\t\t:test:\tmutex7r\nequal1\t\t:test:\tcreate1\nexit2\t\t:test:\tcreate1\nexit3\t\t:test:\tcreate1\nexit4\t\t:test:\tkill1\nexit5\t\t:test:\texit4\njoin0\t\t:test:\tcreate1\njoin1\t\t:test:\tcreate1\njoin2\t\t:test:\tcreate1\njoin3\t\t:test:\tjoin2\ncount1\t\t:test:\tjoin1\nonce1\t\t:test:\tcreate1\ntsd1\t\t:test:\tjoin1\nself2\t\t:test:\tcreate1\neyal1\t\t:test:\ttsd1\ncondvar3\t:test:\tcreate1\ncondvar3_1\t:test:\tcondvar3\ncondvar3_2\t:test:\tcondvar3_1\ncondvar3_3\t:test:\tcondvar3_2\ncondvar4\t:test:\tcreate1\ncondvar5\t:test:\tcondvar4\ncondvar6\t:test:\tcondvar5\ncondvar7\t:test:\tcondvar6\tcleanup1\ncondvar8\t:test:\tcondvar7\ncondvar9\t:test:\tcondvar8\nerrno1\t\t:test:\tmutex3\nrwlock1\t\t:test:\tcondvar6\nrwlock2\t\t:test:\trwlock1\nrwlock3\t\t:test:\trwlock2\nrwlock4\t\t:test:\trwlock3\nrwlock5\t\t:test:\trwlock4\nrwlock6\t\t:test:\trwlock5\nrwlock7\t\t:test:\trwlock6\nrwlock8\t\t:test:\trwlock7\nrwlock2_t\t:test:\trwlock2\nrwlock3_t\t:test:\trwlock2_t\nrwlock4_t\t:test:\trwlock3_t\nrwlock5_t\t:test:\trwlock4_t\nrwlock6_t\t:test:\trwlock5_t\nrwlock6_t2\t:test:\trwlock6_t\ncontext1\t:test:\tcancel2\ncancel3\t\t:test:\tcontext1\ncancel4\t\t:test:\tcancel3\ncancel5\t\t:test:\tcancel3\ncancel6a\t:test:\tcancel3\ncancel6d\t:test:\tcancel3\ncancel7\t\t:test:\tkill1\ncleanup0\t:test:\tcancel5\ncleanup1\t:test:\tcleanup0\ncleanup2\t:test:\tcleanup1\ncleanup3\t:test:\tcleanup2\npriority1       :test:  join1\npriority2       :test:  priority1\ninherit1        :test:  join1\nspin1           :test:\nspin2           :test:  spin1.c\nspin3           :test:  spin2.c\nspin4           :test:  spin3.c\nbarrier1        :test:\nbarrier2        :test:  barrier1.c\nbarrier3        :test:  barrier2.c\nbarrier4        :test:  barrier3.c\nbarrier5        :test:  barrier4.c\nbenchtest1      :test:  mutex3\nbenchtest2      :test:  benchtest1\nbenchtest3      :test:  benchtest2\nbenchtest4      :test:  benchtest3\nbenchtest5      :test:  benchtest4\nexception1\t:test:\tcancel4\nexception2\t:test:\texception1\nexception3\t:test:\texception2\nexit4\t\t:test:\texit3\nvalid1\t\t:test:\tjoin1\nvalid2\t\t:test:\tvalid1\ncancel9\t\t:test:\tcancel8\n"
  },
  {
    "path": "deps/w32-pthreads/PROGRESS",
    "content": "Please see the ANNOUNCE file \"Level of Standards Conformance\"\nor the web page:\n\nhttp://sources.redhat.com/pthreads-win32/conformance.html\n"
  },
  {
    "path": "deps/w32-pthreads/README",
    "content": "PTHREADS-WIN32\n==============\n\nPthreads-win32 is free software, distributed under the GNU Lesser\nGeneral Public License (LGPL). See the file 'COPYING.LIB' for terms\nand conditions. Also see the file 'COPYING' for information\nspecific to pthreads-win32, copyrights and the LGPL.\n\n\nWhat is it?\n-----------\n\nPthreads-win32 is an Open Source Software implementation of the\nThreads component of the POSIX 1003.1c 1995 Standard (or later)\nfor Microsoft's Win32 environment. Some functions from POSIX\n1003.1b are also supported including semaphores. Other related\nfunctions include the set of read-write lock functions. The\nlibrary also supports some of the functionality of the Open\nGroup's Single Unix specification, version 2, namely mutex types,\nplus some common and pthreads-win32 specific non-portable\nroutines (see README.NONPORTABLE).\n\nSee the file \"ANNOUNCE\" for more information including standards\nconformance details and the list of supported and unsupported\nroutines.\n\n\nPrerequisites\n-------------\nMSVC or GNU C (MinGW32 MSys development kit)\n\tTo build from source.\n\nQueueUserAPCEx by Panagiotis E. Hadjidoukas\n\tTo support any thread cancelation in C++ library builds or\n\tto support cancelation of blocked threads in any build.\n\tThis library is not required otherwise.\n\n\tFor true async cancelation of threads (including blocked threads).\n\tThis is a DLL and Windows driver that provides pre-emptive APC\n\tby forcing threads into an alertable state when the APC is queued.\n\tBoth the DLL and driver are provided with the pthreads-win32.exe\n\tself-unpacking ZIP, and on the pthreads-win32 FTP site  (in source\n\tand pre-built forms). Currently this is a separate LGPL package to\n\tpthreads-win32. See the README in the QueueUserAPCEx folder for\n\tinstallation instructions.\n\n\tPthreads-win32 will automatically detect if the QueueUserAPCEx DLL\n\tQuserEx.DLL is available and whether the driver AlertDrv.sys is\n\tloaded. If it is not available, pthreads-win32 will simulate async\n\tcancelation, which means that it can async cancel only threads that\n\tare runnable. The simulated async cancellation cannot cancel blocked\n\tthreads.\n\n        [FOR SECURITY] To be found Quserex.dll MUST be installed in the\n\tWindows System Folder. This is not an unreasonable constraint given a\n\tdriver must also be installed and loaded at system startup.\n\n\nLibrary naming\n--------------\n\nBecause the library is being built using various exception\nhandling schemes and compilers - and because the library\nmay not work reliably if these are mixed in an application,\neach different version of the library has it's own name.\n\nNote 1: the incompatibility is really between EH implementations\nof the different compilers. It should be possible to use the\nstandard C version from either compiler with C++ applications\nbuilt with a different compiler. If you use an EH version of\nthe library, then you must use the same compiler for the\napplication. This is another complication and dependency that\ncan be avoided by using only the standard C library version.\n\nNote 2: if you use a standard C pthread*.dll with a C++\napplication, then any functions that you define that are\nintended to be called via pthread_cleanup_push() must be\n__cdecl.\n\nNote 3: the intention was to also name either the VC or GC\nversion (it should be arbitrary) as pthread.dll, including\npthread.lib and libpthread.a as appropriate. This is no longer\nlikely to happen.\n\nNote 4: the compatibility number was added so that applications\ncan differentiate between binary incompatible versions of the\nlibs and dlls.\n\nIn general:\n\tpthread[VG]{SE,CE,C}[c].dll\n\tpthread[VG]{SE,CE,C}[c].lib\n\nwhere:\n\t[VG] indicates the compiler\n\tV\t- MS VC, or\n\tG\t- GNU C\n\n\t{SE,CE,C} indicates the exception handling scheme\n\tSE\t- Structured EH, or\n\tCE\t- C++ EH, or\n\tC\t- no exceptions - uses setjmp/longjmp\n\n\tc\t- DLL compatibility number indicating ABI and API\n\t\t  compatibility with applications built against\n\t\t  a snapshot with the same compatibility number.\n\t\t  See 'Version numbering' below.\n\nThe name may also be suffixed by a 'd' to indicate a debugging version\nof the library. E.g. pthreadVC2d.lib. Debugging versions contain\nadditional information for debugging (symbols etc) and are often not\noptimised in any way (compiled with optimisation turned off).\n\nExamples:\n\tpthreadVSE.dll\t(MSVC/SEH)\n\tpthreadGCE.dll\t(GNUC/C++ EH)\n\tpthreadGC.dll\t(GNUC/not dependent on exceptions)\n\tpthreadVC1.dll\t(MSVC/not dependent on exceptions - not binary\n\t\t\tcompatible with pthreadVC.dll)\n\tpthreadVC2.dll\t(MSVC/not dependent on exceptions - not binary\n\t\t\tcompatible with pthreadVC1.dll or pthreadVC.dll)\n\nThe GNU library archive file names have correspondingly changed to:\n\n\tlibpthreadGCEc.a\n\tlibpthreadGCc.a\n\n\nVersioning numbering\n--------------------\n\nVersion numbering is separate from the snapshot dating system, and\nis the canonical version identification system embedded within the\nDLL using the Microsoft version resource system. The versioning\nsystem chosen follows the GNU Libtool system. See\nhttp://www.gnu.org/software/libtool/manual.html section 6.2.\n\nSee the resource file 'version.rc'.\n\nMicrosoft version numbers use 4 integers:\n\n\t0.0.0.0\n\nPthreads-win32 uses the first 3 following the Libtool convention.\nThe fourth is commonly used for the build number, but will be reserved\nfor future use.\n\n\tcurrent.revision.age.0\n\nThe numbers are changed as follows:\n\n1. If the library source code has changed at all since the last update,\n   then increment revision (`c:r:a' becomes `c:r+1:a').\n2. If any interfaces have been added, removed, or changed since the last\n   update, increment current, and set revision to 0.\n3. If any interfaces have been added since the last public release, then\n   increment age.\n4. If any interfaces have been removed or changed since the last public\n   release, then set age to 0.\n\n\nDLL compatibility numbering is an attempt to ensure that applications\nalways load a compatible pthreads-win32 DLL by using a DLL naming system\nthat is consistent with the version numbering system. It also allows\nolder and newer DLLs to coexist in the same filesystem so that older\napplications can continue to be used. For pre .NET Windows systems,\nthis inevitably requires incompatible versions of the same DLLs to have\ndifferent names.\n\nPthreads-win32 has adopted the Cygwin convention of appending a single\ninteger number to the DLL name. The number used is based on the library\nversion number and is computed as 'current' - 'age'.\n\n(See http://home.att.net/~perlspinr/libversioning.html for a nicely\ndetailed explanation.)\n\nUsing this method, DLL name/s will only change when the DLL's\nbackwards compatibility changes. Note that the addition of new\n'interfaces' will not of itself change the DLL's compatibility for older\napplications.\n\n\nWhich of the several dll versions to use?\n-----------------------------------------\nor,\n---\nWhat are all these pthread*.dll and pthread*.lib files?\n-------------------------------------------------------\n\nSimple, use either pthreadGCv.* if you use GCC, or pthreadVCv.* if you\nuse MSVC - where 'v' is the DLL versioning (compatibility) number.\n\nOtherwise, you need to choose carefully and know WHY.\n\nThe most important choice you need to make is whether to use a\nversion that uses exceptions internally, or not. There are versions\nof the library that use exceptions as part of the thread\ncancelation and exit implementation. The default version uses\nsetjmp/longjmp.\n\nThere is some contension amongst POSIX threads experts as\nto how POSIX threads cancelation and exit should work\nwith languages that use exceptions, e.g. C++ and even C\n(Microsoft's Structured Exceptions).\n\nThe issue is: should cancelation of a thread in, say,\na C++ application cause object destructors and C++ exception\nhandlers to be invoked as the stack unwinds during thread\nexit, or not?\n\nThere seems to be more opinion in favour of using the\nstandard C version of the library (no EH) with C++ applications\nfor the reason that this appears to be the assumption commercial\npthreads implementations make. Therefore, if you use an EH version\nof pthreads-win32 then you may be under the illusion that\nyour application will be portable, when in fact it is likely to\nbehave differently when linked with other pthreads libraries.\n\nNow you may be asking: then why have you kept the EH versions of\nthe library?\n\nThere are a couple of reasons:\n- there is division amongst the experts and so the code may\n  be needed in the future. Yes, it's in the repository and we\n  can get it out anytime in the future, but it would be difficult\n  to find.\n- pthreads-win32 is one of the few implementations, and possibly\n  the only freely available one, that has EH versions. It may be\n  useful to people who want to play with or study application\n  behaviour under these conditions.\n\nNotes:\n\n[If you use either pthreadVCE or pthreadGCE]\n\n1. [See also the discussion in the FAQ file - Q2, Q4, and Q5]\n\nIf your application contains catch(...) blocks in your POSIX\nthreads then you will need to replace the \"catch(...)\" with the macro\n\"PtW32Catch\", eg.\n\n\t#ifdef PtW32Catch\n\t\tPtW32Catch {\n\t\t\t...\n\t\t}\n\t#else\n\t\tcatch(...) {\n\t\t\t...\n\t\t}\n\t#endif\n\nOtherwise neither pthreads cancelation nor pthread_exit() will work\nreliably when using versions of the library that use C++ exceptions\nfor cancelation and thread exit.\n\nThis is due to what is believed to be a C++ compliance error in VC++\nwhereby you may not have multiple handlers for the same exception in\nthe same try/catch block. GNU G++ doesn't have this restriction.\n\n\nOther name changes\n------------------\n\nAll snapshots prior to and including snapshot 2000-08-13\nused \"_pthread_\" as the prefix to library internal\nfunctions, and \"_PTHREAD_\" to many library internal\nmacros. These have now been changed to \"ptw32_\" and \"PTW32_\"\nrespectively so as to not conflict with the ANSI standard's\nreservation of identifiers beginning with \"_\" and \"__\" for\nuse by compiler implementations only.\n\nIf you have written any applications and you are linking\nstatically with the pthreads-win32 library then you may have\nincluded a call to _pthread_processInitialize. You will\nnow have to change that to ptw32_processInitialize.\n\n\nCleanup code default style\n--------------------------\n\nPreviously, if not defined, the cleanup style was determined automatically\nfrom the compiler used, and one of the following was defined accordingly:\n\n\t__CLEANUP_SEH\tMSVC only\n\t__CLEANUP_CXX\tC++, including MSVC++, GNU G++\n\t__CLEANUP_C\tC, including GNU GCC, not MSVC\n\nThese defines determine the style of cleanup (see pthread.h) and,\nmost importantly, the way that cancelation and thread exit (via\npthread_exit) is performed (see the routine ptw32_throw()).\n\nIn short, the exceptions versions of the library throw an exception\nwhen a thread is canceled, or exits via pthread_exit(). This exception is\ncaught by a handler in the thread startup routine, so that the\nthe correct stack unwinding occurs regardless of where the thread\nis when it's canceled or exits via pthread_exit().\n\nIn this snapshot, unless the build explicitly defines (e.g. via a\ncompiler option) __CLEANUP_SEH, __CLEANUP_CXX, or __CLEANUP_C, then\nthe build NOW always defaults to __CLEANUP_C style cleanup. This style\nuses setjmp/longjmp in the cancelation and pthread_exit implementations,\nand therefore won't do stack unwinding even when linked to applications\nthat have it (e.g. C++ apps). This is for consistency with most/all\ncommercial Unix POSIX threads implementations.\n\nAlthough it was not clearly documented before, it is still necessary to\nbuild your application using the same __CLEANUP_* define as was\nused for the version of the library that you link with, so that the\ncorrect parts of pthread.h are included. That is, the possible\ndefines require the following library versions:\n\n\t__CLEANUP_SEH\tpthreadVSE.dll\n\t__CLEANUP_CXX\tpthreadVCE.dll or pthreadGCE.dll\n\t__CLEANUP_C\tpthreadVC.dll or pthreadGC.dll\n\nIt is recommended that you let pthread.h use it's default __CLEANUP_C\nfor both library and application builds. That is, don't define any of\nthe above, and then link with pthreadVC.lib (MSVC or MSVC++) and\nlibpthreadGC.a (MinGW GCC or G++). The reason is explained below, but\nanother reason is that the prebuilt pthreadVCE.dll is currently broken.\nVersions built with MSVC++ later than version 6 may not be broken, but I\ncan't verify this yet.\n\nWHY ARE WE MAKING THE DEFAULT STYLE LESS EXCEPTION-FRIENDLY?\nBecause no commercial Unix POSIX threads implementation allows you to\nchoose to have stack unwinding. Therefore, providing it in pthread-win32\nas a default is dangerous. We still provide the choice but unless\nyou consciously choose to do otherwise, your pthreads applications will\nnow run or crash in similar ways irrespective of the pthreads platform\nyou use. Or at least this is the hope.\n\n\nBuilding under VC++ using C++ EH, Structured EH, or just C\n----------------------------------------------------------\n\nFrom the source directory run nmake without any arguments to list\nhelp information. E.g.\n\n$ nmake\n\nMicrosoft (R) Program Maintenance Utility   Version 6.00.8168.0\nCopyright (C) Microsoft Corp 1988-1998. All rights reserved.\n\nRun one of the following command lines:\nnmake clean VCE (to build the MSVC dll with C++ exception handling)\nnmake clean VSE (to build the MSVC dll with structured exception handling)\nnmake clean VC (to build the MSVC dll with C cleanup code)\nnmake clean VCE-inlined (to build the MSVC inlined dll with C++ exception handling)\nnmake clean VSE-inlined (to build the MSVC inlined dll with structured exception handling)\nnmake clean VC-inlined (to build the MSVC inlined dll with C cleanup code)\nnmake clean VC-static (to build the MSVC static lib with C cleanup code)\nnmake clean VCE-debug (to build the debug MSVC dll with C++ exception handling)\nnmake clean VSE-debug (to build the debug MSVC dll with structured exception handling)\nnmake clean VC-debug (to build the debug MSVC dll with C cleanup code)\nnmake clean VCE-inlined-debug (to build the debug MSVC inlined dll with C++ exception handling)\nnmake clean VSE-inlined-debug (to build the debug MSVC inlined dll with structured exception handling)\nnmake clean VC-inlined-debug (to build the debug MSVC inlined dll with C cleanup code)\nnmake clean VC-static-debug (to build the debug MSVC static lib with C cleanup code)\n\n\nThe pre-built dlls are normally built using the *-inlined targets.\n\nYou can run the testsuite by changing to the \"tests\" directory and\nrunning nmake. E.g.:\n\n$ cd tests\n$ nmake\n\nMicrosoft (R) Program Maintenance Utility   Version 6.00.8168.0\nCopyright (C) Microsoft Corp 1988-1998. All rights reserved.\n\nRun one of the following command lines:\nnmake clean VC (to test using VC dll with VC (no EH) applications)\nnmake clean VCX (to test using VC dll with VC++ (EH) applications)\nnmake clean VCE (to test using the VCE dll with VC++ EH applications)\nnmake clean VSE (to test using VSE dll with VC (SEH) applications)\nnmake clean VC-bench (to benchtest using VC dll with C bench app)\nnmake clean VCX-bench (to benchtest using VC dll with C++ bench app)\nnmake clean VCE-bench (to benchtest using VCE dll with C++ bench app)\nnmake clean VSE-bench (to benchtest using VSE dll with SEH bench app)\nnmake clean VC-static (to test using VC static lib with VC (no EH) applications)\n\n\nBuilding under Mingw32\n----------------------\n\nThe dll can be built easily with recent versions of Mingw32.\n(The distributed versions are built using Mingw32 and MsysDTK\nfrom www.mingw32.org.)\n\nFrom the source directory, run make for help information. E.g.:\n\n$ make\nRun one of the following command lines:\nmake clean GC            (to build the GNU C dll with C cleanup code)\nmake clean GCE           (to build the GNU C dll with C++ exception handling)\nmake clean GC-inlined    (to build the GNU C inlined dll with C cleanup code)\nmake clean GCE-inlined   (to build the GNU C inlined dll with C++ exception handling)\nmake clean GC-static     (to build the GNU C inlined static lib with C cleanup code)\nmake clean GC-debug      (to build the GNU C debug dll with C cleanup code)\nmake clean GCE-debug     (to build the GNU C debug dll with C++ exception handling)\nmake clean GC-inlined-debug    (to build the GNU C inlined debug dll with C cleanup code)\nmake clean GCE-inlined-debug   (to build the GNU C inlined debug dll with C++ exception handling)\nmake clean GC-static-debug     (to build the GNU C inlined static debug lib with C cleanup code)\n\n\nThe pre-built dlls are normally built using the *-inlined targets.\n\nYou can run the testsuite by changing to the \"tests\" directory and\nrunning make for help information. E.g.:\n\n$ cd tests\n$ make\nRun one of the following command lines:\nmake clean GC    (to test using GC dll with C (no EH) applications)\nmake clean GCX   (to test using GC dll with C++ (EH) applications)\nmake clean GCE   (to test using GCE dll with C++ (EH) applications)\nmake clean GC-bench       (to benchtest using GNU C dll with C cleanup code)\nmake clean GCE-bench   (to benchtest using GNU C dll with C++ exception handling)\nmake clean GC-static   (to test using GC static lib with C (no EH) applications)\n\n\nBuilding under Linux using the Mingw32 cross development tools\n--------------------------------------------------------------\n\nYou can build the library without leaving Linux by using the Mingw32 cross\ndevelopment toolchain. See http://www.libsdl.org/extras/win32/cross/ for\ntools and info. The GNUmakefile contains some support for this, for example:\n\nmake CROSS=i386-mingw32msvc- clean GC-inlined\n\nwill build pthreadGCn.dll and libpthreadGCn.a (n=version#), provided your\ncross-tools/bin directory is in your PATH (or use the cross-make.sh script\nat the URL above).\n\n\nBuilding the library as a statically linkable library\n-----------------------------------------------------\n\nGeneral: PTW32_STATIC_LIB must be defined for both the library build and the\napplication build. The makefiles supplied and used by the following 'make'\ncommand lines will define this for you.\n\nMSVC (creates pthreadVCn.lib as a static link lib):\n\nnmake clean VC-static\n\n\nMinGW32 (creates libpthreadGCn.a as a static link lib):\n\nmake clean GC-static\n\n\nDefine PTW32_STATIC_LIB when building your application. Also, your\napplication must call a two non-portable routines to initialise the\nsome state on startup and cleanup before exit. One other routine needs\nto be called to cleanup after any Win32 threads have called POSIX API\nroutines. See README.NONPORTABLE or the html reference manual pages for\ndetails on these routines:\n\nBOOL pthread_win32_process_attach_np (void);\nBOOL pthread_win32_process_detach_np (void);\nBOOL pthread_win32_thread_attach_np (void); // Currently a no-op\nBOOL pthread_win32_thread_detach_np (void);\n\n\nThe tests makefiles have the same targets but only check that the\nstatic library is statically linkable. They don't run the full\ntestsuite. To run the full testsuite, build the dlls and run the\ndll test targets.\n\n\nBuilding the library under Cygwin\n---------------------------------\n\nCygwin is implementing it's own POSIX threads routines and these\nwill be the ones to use if you develop using Cygwin.\n\n\nReady to run binaries\n---------------------\n\nFor convenience, the following ready-to-run files can be downloaded\nfrom the FTP site (see under \"Availability\" below):\n\n\tpthread.h\n\tsemaphore.h\n\tsched.h\n\tpthreadVC.dll\t- built with MSVC compiler using C setjmp/longjmp\n\tpthreadVC.lib\n\tpthreadVCE.dll\t- built with MSVC++ compiler using C++ EH\n\tpthreadVCE.lib\n\tpthreadVSE.dll\t- built with MSVC compiler using SEH\n\tpthreadVSE.lib\n\tpthreadGC.dll\t- built with Mingw32 GCC\n\tlibpthreadGC.a\t- derived from pthreadGC.dll\n\tpthreadGCE.dll\t- built with Mingw32 G++\n\tlibpthreadGCE.a\t- derived from pthreadGCE.dll\n\nAs of August 2003 pthreads-win32 pthreadG* versions are built and tested\nusing the MinGW + MsysDTK environment current as of that date or later.\nThe following file MAY be needed for older MinGW environments.\n\n\tgcc.dll \t- needed to build and run applications that use\n\t\t\t  pthreadGCE.dll.\n\n\nBuilding applications with GNU compilers\n----------------------------------------\n\nIf you're using pthreadGC.dll:\n\nWith the three header files, pthreadGC.dll and libpthreadGC.a in the\nsame directory as your application myapp.c, you could compile, link\nand run myapp.c under Mingw32 as follows:\n\n\tgcc -o myapp.exe myapp.c -I. -L. -lpthreadGC\n\tmyapp\n\nOr put pthreadGC.dll in an appropriate directory in your PATH,\nput libpthreadGC.a in your system lib directory, and\nput the three header files in your system include directory,\nthen use:\n\n\tgcc -o myapp.exe myapp.c -lpthreadGC\n\tmyapp\n\n\nIf you're using pthreadGCE.dll:\n\nWith the three header files, pthreadGCE.dll, gcc.dll and libpthreadGCE.a\nin the same directory as your application myapp.c, you could compile,\nlink and run myapp.c under Mingw32 as follows:\n\n\tgcc -x c++ -o myapp.exe myapp.c -I. -L. -lpthreadGCE\n\tmyapp\n\nOr put pthreadGCE.dll and gcc.dll in an appropriate directory in\nyour PATH, put libpthreadGCE.a in your system lib directory, and\nput the three header files in your system include directory,\nthen use:\n\n\tgcc -x c++ -o myapp.exe myapp.c -lpthreadGCE\n\tmyapp\n\n\nAvailability\n------------\n\nThe complete source code in either unbundled, self-extracting\nZip file, or tar/gzipped format can be found at:\n\n\tftp://sources.redhat.com/pub/pthreads-win32\n\nThe pre-built DLL, export libraries and matching pthread.h can\nbe found at:\n\n\tftp://sources.redhat.com/pub/pthreads-win32/dll-latest\n\nHome page:\n\n\thttp://sources.redhat.com/pthreads-win32/\n\n\nMailing list\n------------\n\nThere is a mailing list for discussing pthreads on Win32.\nTo join, send email to:\n\n\tpthreads-win32-subscribe@sources.redhat.com\n\nUnsubscribe by sending mail to:\n\n\tpthreads-win32-unsubscribe@sources.redhat.com\n\n\nAcknowledgements\n----------------\n\nSee the ANNOUNCE file for acknowledgements.\nSee the 'CONTRIBUTORS' file for the list of contributors.\n\nAs much as possible, the ChangeLog file attributes\ncontributions and patches that have been incorporated\nin the library to the individuals responsible.\n\nFinally, thanks to all those who work on and contribute to the\nPOSIX and Single Unix Specification standards. The maturity of an\nindustry can be measured by it's open standards.\n\n----\nRoss Johnson\n<rpj@callisto.canberra.edu.au>\n\n\n\n\n\n\n\n\n"
  },
  {
    "path": "deps/w32-pthreads/README.Borland",
    "content": "In ptw32_InterlockedCompareExchange.c, I've added a section for\nBorland's compiler; it's identical to that for the MS compiler except\nthat it uses /* ... */ comments instead of ; comments.\n\n[RPJ: need to define HAVE_TASM32 in config.h to use the above.]\n\n\nThe other file is a makefile suitable for use with Borland's compiler\n(run \"make -fBmakefile\" in the directory).  It builds a single version\nof the library, pthreadBC.dll and the corresponding pthreadBC.lib\nimport library, which is comparable to the pthreadVC version; I can't\npersonally see any demand for the versions that include structured or\nC++ exception cancellation handling so I haven't attempted to build\nthose versions of the library.  (I imagine a static version might be\nof use to some, but we can't legally use that on my commercial\nprojects so I can't try that out, unfortunately.)\n\n[RPJ: Added tests\\Bmakefile as well.]\n\nBorland C++ doesn't define the ENOSYS constant used by pthreads-win32;\nrather than make more extensive patches to the pthreads-win32 source I\nhave a mostly-arbitrary constant for it in the makefile.  However this\ndoesn't make it visible to the application using the library, so if\nanyone actually wants to use this constant in their apps (why?)\nsomeone might like to make a seperate NEED_BCC_something define to add\nthis stuff.\n\nThe makefile also #defines EDEADLK as EDEADLOCK, _timeb as timeb, and\n_ftime as ftime, to deal with the minor differences between the two\nRTLs' naming conventions, and sets the compiler flags as required to\nget a normal compile of the library.\n\n[RPJ: Moved errno values and _timeb etc to pthread.h, so apps will also\nuse them.]\n\n(While I'm on the subject, the reason Borland users should recompile\nthe library, rather than using the impdef/implib technique suggested\npreviously on the mailing list, is that a) the errno constants are\ndifferent, so the results returned by the pthread_* functions can be\nmeaningless, and b) the errno variable/pseudo-variable itself is\ndifferent in the MS & BCC runtimes, so you can't access the\npthreadVC's errno from a Borland C++-compiled host application\ncorrectly - I imagine there are other potential problems from the RTL\nmismatch too.)\n\n[RPJ: Make sure you use the same RTL in both dll and application builds.\nThe dll and tests Bmakefiles use cw32mti.lib. Having some trouble with\nmemory read exceptions running the test suite using BCC55.]\n\nBest regards,\nWill\n\n-- \nWill Bryant\nSystems Architect, eCOSM Limited\nCell +64 21 655 443, office +64 3 365 4176\nhttp://www.ecosm.com/\n"
  },
  {
    "path": "deps/w32-pthreads/README.CV",
    "content": "README.CV -- Condition Variables\n--------------------------------\n\nThe original implementation of condition variables in\npthreads-win32 was based on a discussion paper:\n\n\"Strategies for Implementing POSIX Condition Variables\non Win32\": http://www.cs.wustl.edu/~schmidt/win32-cv-1.html\n\nThe changes suggested below were made on Feb 6 2001. This\nfile is included in the package for the benefit of anyone\ninterested in understanding the pthreads-win32 implementation\nof condition variables and the (sometimes subtle) issues that\nit attempts to resolve.\n\nThanks go to the individuals whose names appear throughout\nthe following text.\n\nRoss Johnson\n\n--------------------------------------------------------------------\n\nfyi.. (more detailed problem description/demos + possible fix/patch)\n\nregards,\nalexander.\n\n\nAlexander Terekhov\n31.01.2001 17:43\n\nTo:   ace-bugs@cs.wustl.edu\ncc:\nFrom: Alexander Terekhov/Germany/IBM@IBMDE\nSubject:  Implementation of POSIX CVs: spur.wakeups/lost\n      signals/deadlocks/unfairness\n\n\n\n    ACE VERSION:\n\n        5.1.12 (pthread-win32 snapshot 2000-12-29)\n\n    HOST MACHINE and OPERATING SYSTEM:\n\n        IBM IntelliStation Z Pro, 2 x XEON 1GHz, Win2K\n\n    TARGET MACHINE and OPERATING SYSTEM, if different from HOST:\n    COMPILER NAME AND VERSION (AND PATCHLEVEL):\n\n        Microsoft Visual C++ 6.0\n\n    AREA/CLASS/EXAMPLE AFFECTED:\n\n        Implementation of POSIX condition variables - OS.cpp/.h\n\n    DOES THE PROBLEM AFFECT:\n\n        EXECUTION? YES!\n\n    SYNOPSIS:\n\n        a) spurious wakeups (minor problem)\n        b) lost signals\n        c) broadcast deadlock\n        d) unfairness (minor problem)\n\n    DESCRIPTION:\n\n        Please see attached copy of discussion thread\n        from comp.programming.threads for more details on\n        some reported problems. (i've also posted a \"fyi\"\n        message to ace-users a week or two ago but\n        unfortunately did not get any response so far).\n\n        It seems that current implementation suffers from\n        two essential problems:\n\n        1) cond.waiters_count does not accurately reflect\n           number of waiters blocked on semaphore - w/o\n           proper synchronisation that could result (in the\n           time window when counter is not accurate)\n           in spurious wakeups organised by subsequent\n           _signals  and _broadcasts.\n\n        2) Always having (with no e.g. copy_and_clear/..)\n           the same queue in use (semaphore+counter)\n           neither signal nor broadcast provide 'atomic'\n           behaviour with respect to other threads/subsequent\n           calls to signal/broadcast/wait.\n\n        Each problem and combination of both could produce\n        various nasty things:\n\n        a) spurious wakeups (minor problem)\n\n             it is possible that waiter(s) which was already\n             unblocked even so is still counted as blocked\n             waiter. signal and broadcast will release\n             semaphore which will produce a spurious wakeup\n             for a 'real' waiter coming later.\n\n        b) lost signals\n\n             signalling thread ends up consuming its own\n             signal. please see demo/discussion below.\n\n        c) broadcast deadlock\n\n             last_waiter processing code does not correctly\n             handle the case with multiple threads\n             waiting for the end of broadcast.\n             please see demo/discussion below.\n\n        d) unfairness (minor problem)\n\n             without SignalObjectAndWait some waiter(s)\n             may end up consuming broadcasted signals\n             multiple times (spurious wakeups) because waiter\n             thread(s) can be preempted before they call\n             semaphore wait (but after count++ and mtx.unlock).\n\n    REPEAT BY:\n\n        See below... run problem demos programs (tennis.cpp and\n        tennisb.cpp) number of times concurrently (on multiprocessor)\n        and in multiple sessions or just add a couple of \"Sleep\"s\n        as described in the attached copy of discussion thread\n        from comp.programming.threads\n\n    SAMPLE FIX/WORKAROUND:\n\n        See attached patch to pthread-win32.. well, I can not\n        claim that it is completely bug free but at least my\n        test and tests provided by pthreads-win32 seem to work.\n        Perhaps that will help.\n\n        regards,\n        alexander.\n\n\n>> Forum: comp.programming.threads\n>> Thread: pthread_cond_* implementation questions\n.\n.\n.\nDavid Schwartz <davids@webmaster.com> wrote:\n\n> terekhov@my-deja.com wrote:\n>\n>> BTW, could you please also share your view on other perceived\n>> \"problems\" such as nested broadcast deadlock, spurious wakeups\n>> and (the latest one) lost signals??\n>\n>I'm not sure what you mean. The standard allows an implementation\n>to do almost whatever it likes. In fact, you could implement\n>pthread_cond_wait by releasing the mutex, sleeping a random\n>amount of time, and then reacquiring the mutex. Of course,\n>this would be a pretty poor implementation, but any code that\n>didn't work under that implementation wouldn't be strictly\n>compliant.\n\nThe implementation you suggested is indeed correct\none (yes, now I see it :). However it requires from\nsignal/broadcast nothing more than to \"{ return 0; }\"\nThat is not the case for pthread-win32 and ACE\nimplementations. I do think that these implementations\n(basically the same implementation) have some serious\nproblems with wait/signal/broadcast calls. I am looking\nfor help to clarify whether these problems are real\nor not. I think that I can demonstrate what I mean\nusing one or two small sample programs.\n.\n.\n.\n==========\ntennis.cpp\n==========\n\n#include \"ace/Synch.h\"\n#include \"ace/Thread.h\"\n\nenum GAME_STATE {\n\n  START_GAME,\n  PLAYER_A,     // Player A playes the ball\n  PLAYER_B,     // Player B playes the ball\n  GAME_OVER,\n  ONE_PLAYER_GONE,\n  BOTH_PLAYERS_GONE\n\n};\n\nenum GAME_STATE             eGameState;\nACE_Mutex*                  pmtxGameStateLock;\nACE_Condition< ACE_Mutex >* pcndGameStateChange;\n\nvoid*\n  playerA(\n    void* pParm\n  )\n{\n\n  // For access to game state variable\n  pmtxGameStateLock->acquire();\n\n  // Play loop\n  while ( eGameState < GAME_OVER ) {\n\n    // Play the ball\n    cout << endl << \"PLAYER-A\" << endl;\n\n    // Now its PLAYER-B's turn\n    eGameState = PLAYER_B;\n\n    // Signal to PLAYER-B that now it is his turn\n    pcndGameStateChange->signal();\n\n    // Wait until PLAYER-B finishes playing the ball\n    do {\n\n      pcndGameStateChange->wait();\n\n      if ( PLAYER_B == eGameState )\n        cout << endl << \"----PLAYER-A: SPURIOUS WAKEUP!!!\" << endl;\n\n    } while ( PLAYER_B == eGameState );\n\n  }\n\n  // PLAYER-A gone\n  eGameState = (GAME_STATE)(eGameState+1);\n  cout << endl << \"PLAYER-A GONE\" << endl;\n\n  // No more access to state variable needed\n  pmtxGameStateLock->release();\n\n  // Signal PLAYER-A gone event\n  pcndGameStateChange->broadcast();\n\n  return 0;\n\n}\n\nvoid*\n  playerB(\n    void* pParm\n  )\n{\n\n  // For access to game state variable\n  pmtxGameStateLock->acquire();\n\n  // Play loop\n  while ( eGameState < GAME_OVER ) {\n\n    // Play the ball\n    cout << endl << \"PLAYER-B\" << endl;\n\n    // Now its PLAYER-A's turn\n    eGameState = PLAYER_A;\n\n    // Signal to PLAYER-A that now it is his turn\n    pcndGameStateChange->signal();\n\n    // Wait until PLAYER-A finishes playing the ball\n    do {\n\n      pcndGameStateChange->wait();\n\n      if ( PLAYER_A == eGameState )\n        cout << endl << \"----PLAYER-B: SPURIOUS WAKEUP!!!\" << endl;\n\n    } while ( PLAYER_A == eGameState );\n\n  }\n\n  // PLAYER-B gone\n  eGameState = (GAME_STATE)(eGameState+1);\n  cout << endl << \"PLAYER-B GONE\" << endl;\n\n  // No more access to state variable needed\n  pmtxGameStateLock->release();\n\n  // Signal PLAYER-B gone event\n  pcndGameStateChange->broadcast();\n\n  return 0;\n\n}\n\n\nint\nmain (int, ACE_TCHAR *[])\n{\n\n  pmtxGameStateLock   = new ACE_Mutex();\n  pcndGameStateChange = new ACE_Condition< ACE_Mutex >( *pmtxGameStateLock\n);\n\n  // Set initial state\n  eGameState = START_GAME;\n\n  // Create players\n  ACE_Thread::spawn( playerA );\n  ACE_Thread::spawn( playerB );\n\n  // Give them 5 sec. to play\n  Sleep( 5000 );//sleep( 5 );\n\n  // Set game over state\n  pmtxGameStateLock->acquire();\n  eGameState = GAME_OVER;\n\n  // Let them know\n  pcndGameStateChange->broadcast();\n\n  // Wait for players to stop\n  do {\n\n    pcndGameStateChange->wait();\n\n  } while ( eGameState < BOTH_PLAYERS_GONE );\n\n  // Cleanup\n  cout << endl << \"GAME OVER\" << endl;\n  pmtxGameStateLock->release();\n  delete pcndGameStateChange;\n  delete pmtxGameStateLock;\n\n  return 0;\n\n}\n\n===========\ntennisb.cpp\n===========\n#include \"ace/Synch.h\"\n#include \"ace/Thread.h\"\n\nenum GAME_STATE {\n\n  START_GAME,\n  PLAYER_A,     // Player A playes the ball\n  PLAYER_B,     // Player B playes the ball\n  GAME_OVER,\n  ONE_PLAYER_GONE,\n  BOTH_PLAYERS_GONE\n\n};\n\nenum GAME_STATE             eGameState;\nACE_Mutex*                  pmtxGameStateLock;\nACE_Condition< ACE_Mutex >* pcndGameStateChange;\n\nvoid*\n  playerA(\n    void* pParm\n  )\n{\n\n  // For access to game state variable\n  pmtxGameStateLock->acquire();\n\n  // Play loop\n  while ( eGameState < GAME_OVER ) {\n\n    // Play the ball\n    cout << endl << \"PLAYER-A\" << endl;\n\n    // Now its PLAYER-B's turn\n    eGameState = PLAYER_B;\n\n    // Signal to PLAYER-B that now it is his turn\n    pcndGameStateChange->broadcast();\n\n    // Wait until PLAYER-B finishes playing the ball\n    do {\n\n      pcndGameStateChange->wait();\n\n      if ( PLAYER_B == eGameState )\n        cout << endl << \"----PLAYER-A: SPURIOUS WAKEUP!!!\" << endl;\n\n    } while ( PLAYER_B == eGameState );\n\n  }\n\n  // PLAYER-A gone\n  eGameState = (GAME_STATE)(eGameState+1);\n  cout << endl << \"PLAYER-A GONE\" << endl;\n\n  // No more access to state variable needed\n  pmtxGameStateLock->release();\n\n  // Signal PLAYER-A gone event\n  pcndGameStateChange->broadcast();\n\n  return 0;\n\n}\n\nvoid*\n  playerB(\n    void* pParm\n  )\n{\n\n  // For access to game state variable\n  pmtxGameStateLock->acquire();\n\n  // Play loop\n  while ( eGameState < GAME_OVER ) {\n\n    // Play the ball\n    cout << endl << \"PLAYER-B\" << endl;\n\n    // Now its PLAYER-A's turn\n    eGameState = PLAYER_A;\n\n    // Signal to PLAYER-A that now it is his turn\n    pcndGameStateChange->broadcast();\n\n    // Wait until PLAYER-A finishes playing the ball\n    do {\n\n      pcndGameStateChange->wait();\n\n      if ( PLAYER_A == eGameState )\n        cout << endl << \"----PLAYER-B: SPURIOUS WAKEUP!!!\" << endl;\n\n    } while ( PLAYER_A == eGameState );\n\n  }\n\n  // PLAYER-B gone\n  eGameState = (GAME_STATE)(eGameState+1);\n  cout << endl << \"PLAYER-B GONE\" << endl;\n\n  // No more access to state variable needed\n  pmtxGameStateLock->release();\n\n  // Signal PLAYER-B gone event\n  pcndGameStateChange->broadcast();\n\n  return 0;\n\n}\n\n\nint\nmain (int, ACE_TCHAR *[])\n{\n\n  pmtxGameStateLock   = new ACE_Mutex();\n  pcndGameStateChange = new ACE_Condition< ACE_Mutex >( *pmtxGameStateLock\n);\n\n  // Set initial state\n  eGameState = START_GAME;\n\n  // Create players\n  ACE_Thread::spawn( playerA );\n  ACE_Thread::spawn( playerB );\n\n  // Give them 5 sec. to play\n  Sleep( 5000 );//sleep( 5 );\n\n  // Make some noise\n  pmtxGameStateLock->acquire();\n  cout << endl << \"---Noise ON...\" << endl;\n  pmtxGameStateLock->release();\n  for ( int i = 0; i < 100000; i++ )\n    pcndGameStateChange->broadcast();\n  cout << endl << \"---Noise OFF\" << endl;\n\n  // Set game over state\n  pmtxGameStateLock->acquire();\n  eGameState = GAME_OVER;\n  cout << endl << \"---Stopping the game...\" << endl;\n\n  // Let them know\n  pcndGameStateChange->broadcast();\n\n  // Wait for players to stop\n  do {\n\n    pcndGameStateChange->wait();\n\n  } while ( eGameState < BOTH_PLAYERS_GONE );\n\n  // Cleanup\n  cout << endl << \"GAME OVER\" << endl;\n  pmtxGameStateLock->release();\n  delete pcndGameStateChange;\n  delete pmtxGameStateLock;\n\n  return 0;\n\n}\n.\n.\n.\nDavid Schwartz <davids@webmaster.com> wrote:\n>> > It's compliant\n>>\n>> That is really good.\n>\n>> Tomorrow (I have to go urgently now) I will try to\n>> demonstrate the lost-signal \"problem\" of current\n>> pthread-win32 and ACE-(variant w/o SingleObjectAndWait)\n>> implementations: players start suddenly drop their balls :-)\n>> (with no change in source code).\n>\n>Signals aren't lost, they're going to the main thread,\n>which isn't coded correctly to handle them. Try this:\n>\n>  // Wait for players to stop\n>  do {\n>\n>    pthread_cond_wait( &cndGameStateChange,&mtxGameStateLock );\n>printf(\"Main thread stole a signal\\n\");\n>\n>  } while ( eGameState < BOTH_PLAYERS_GONE );\n>\n>I bet everytime you thing a signal is lost, you'll see that printf.\n>The signal isn't lost, it was stolen by another thread.\n\nwell, you can probably loose your bet.. it was indeed stolen\nby \"another\" thread but not the one you seem to think of.\n\nI think that what actually happens is the following:\n\nH:\\SA\\UXX\\pt\\PTHREADS\\TESTS>tennis3.exe\n\nPLAYER-A\n\nPLAYER-B\n\n----PLAYER-B: SPURIOUS WAKEUP!!!\n\nPLAYER-A GONE\n\nPLAYER-B GONE\n\nGAME OVER\n\nH:\\SA\\UXX\\pt\\PTHREADS\\TESTS>\n\nhere you can see that PLAYER-B after playing his first\nball (which came via signal from PLAYER-A) just dropped\nit down. What happened is that his signal to player A\nwas consumed as spurious wakeup by himself (player B).\n\nThe implementation has a problem:\n\n================\nwaiting threads:\n================\n\n{ /** Critical Section\n\n  inc cond.waiters_count\n\n}\n\n  /*\n  /* Atomic only if using Win32 SignalObjectAndWait\n  /*\n  cond.mtx.release\n\n  /*** ^^-- A THREAD WHICH DID SIGNAL MAY ACQUIRE THE MUTEX,\n  /***      GO INTO WAIT ON THE SAME CONDITION AND OVERTAKE\n  /***      ORIGINAL WAITER(S) CONSUMING ITS OWN SIGNAL!\n\n  cond.sem.wait\n\nPlayer-A after playing game's initial ball went into\nwait (called _wait) but was pre-empted before reaching\nwait semaphore. He was counted as waiter but was not\nactually waiting/blocked yet.\n\n===============\nsignal threads:\n===============\n\n{ /** Critical Section\n\n  waiters_count = cond.waiters_count\n\n}\n\n  if ( waiters_count != 0 )\n\n    sem.post 1\n\n  endif\n\nPlayer-B after he received signal/ball from Player A\ncalled _signal. The _signal did see that there was\none waiter blocked on the condition (Player-A) and\nreleased the semaphore.. (but it did not unblock\nPlayer-A because he was not actually blocked).\nPlayer-B thread continued its execution, called _wait,\nwas counted as second waiter BUT was allowed to slip\nthrough opened semaphore gate (which was opened for\nPlayer-B) and received his own signal. Player B remained\nblocked followed by Player A. Deadlock happened which\nlasted until main thread came in and said game over.\n\nIt seems to me that the implementation fails to\ncorrectly implement the following statement\nfrom specification:\n\nhttp://www.opengroup.org/\nonlinepubs/007908799/xsh/pthread_cond_wait.html\n\n\"These functions atomically release mutex and cause\nthe calling thread to block on the condition variable\ncond; atomically here means \"atomically with respect\nto access by another thread to the mutex and then the\ncondition variable\". That is, if another thread is\nable to acquire the mutex after the about-to-block\nthread has released it, then a subsequent call to\npthread_cond_signal() or pthread_cond_broadcast()\nin that thread behaves as if it were issued after\nthe about-to-block thread has blocked.\"\n\nQuestion: Am I right?\n\n(I produced the program output above by simply\nadding ?Sleep( 1 )?:\n\n================\nwaiting threads:\n================\n\n{ /** Critical Section\n\n  inc cond.waiters_count\n\n}\n\n  /*\n  /* Atomic only if using Win32 SignalObjectAndWait\n  /*\n  cond.mtx.release\n\nSleep( 1 ); // Win32\n\n  /*** ^^-- A THREAD WHICH DID SIGNAL MAY ACQUIRE THE MUTEX,\n  /***      GO INTO WAIT ON THE SAME CONDITION AND OVERTAKE\n  /***      ORIGINAL WAITER(S) CONSUMING ITS OWN SIGNAL!\n\n  cond.sem.wait\n\nto the source code of pthread-win32 implementation:\n\nhttp://sources.redhat.com/cgi-bin/cvsweb.cgi/pthreads/\ncondvar.c?rev=1.36&content-type=text/\nx-cvsweb-markup&cvsroot=pthreads-win32\n\n\n  /*\n  * We keep the lock held just long enough to increment the count of\n  * waiters by one (above).\n  * Note that we can't keep it held across the\n  * call to sem_wait since that will deadlock other calls\n  * to pthread_cond_signal\n  */\n  cleanup_args.mutexPtr = mutex;\n  cleanup_args.cv = cv;\n  cleanup_args.resultPtr = &result;\n\n  pthread_cleanup_push (ptw32_cond_wait_cleanup, (void *)\n&cleanup_args);\n\n  if ((result = pthread_mutex_unlock (mutex)) == 0)\n    {((result\nSleep( 1 ); // @AT\n\n      /*\n      * Wait to be awakened by\n      *              pthread_cond_signal, or\n      *              pthread_cond_broadcast, or\n      *              a timeout\n      *\n      * Note:\n      *      ptw32_sem_timedwait is a cancelation point,\n      *      hence providing the\n      *      mechanism for making pthread_cond_wait a cancelation\n      *      point. We use the cleanup mechanism to ensure we\n      *      re-lock the mutex and decrement the waiters count\n      *      if we are canceled.\n      */\n      if (ptw32_sem_timedwait (&(cv->sema), abstime) == -1)         {\n          result = errno;\n        }\n    }\n\n  pthread_cleanup_pop (1);  /* Always cleanup */\n\n\nBTW, on my system (2 CPUs) I can manage to get\nsignals lost even without any source code modification\nif I run the tennis program many times in different\nshell sessions.\n.\n.\n.\nDavid Schwartz <davids@webmaster.com> wrote:\n>terekhov@my-deja.com wrote:\n>\n>> well, it might be that the program is in fact buggy.\n>> but you did not show me any bug.\n>\n>You're right. I was close but not dead on. I was correct, however,\n>that the code is buggy because it uses 'pthread_cond_signal' even\n>though not any thread waiting on the condition variable can do the\n>job. I was wrong in which thread could be waiting on the cv but\n>unable to do the job.\n\nOkay, lets change 'pthread_cond_signal' to 'pthread_cond_broadcast'\nbut also add some noise from main() right before declaring the game\nto be over (I need it in order to demonstrate another problem of\npthread-win32/ACE implementations - broadcast deadlock)...\n.\n.\n.\nIt is my understanding of POSIX conditions,\nthat on correct implementation added noise\nin form of unnecessary broadcasts from main,\nshould not break the tennis program. The\nonly 'side effect' of added noise on correct\nimplementation would be 'spurious wakeups' of\nplayers (in fact they are not spurious,\nplayers just see them as spurious) unblocked,\nnot by another player but by main before\nanother player had a chance to acquire the\nmutex and change the game state variable:\n.\n.\n.\n\nPLAYER-B\n\nPLAYER-A\n\n---Noise ON...\n\nPLAYER-B\n\nPLAYER-A\n\n.\n.\n.\n\nPLAYER-B\n\nPLAYER-A\n\n----PLAYER-A: SPURIOUS WAKEUP!!!\n\nPLAYER-B\n\nPLAYER-A\n\n---Noise OFF\n\nPLAYER-B\n\n---Stopping the game...\n\nPLAYER-A GONE\n\nPLAYER-B GONE\n\nGAME OVER\n\nH:\\SA\\UXX\\pt\\PTHREADS\\TESTS>\n\nOn pthread-win32/ACE implementations the\nprogram could stall:\n\n.\n.\n.\n\nPLAYER-A\n\nPLAYER-B\n\nPLAYER-A\n\nPLAYER-B\n\nPLAYER-A\n\nPLAYER-B\n\nPLAYER-A\n\nPLAYER-B\n\n---Noise ON...\n\nPLAYER-A\n\n---Noise OFF\n^C\nH:\\SA\\UXX\\pt\\PTHREADS\\TESTS>\n\n\nThe implementation has problems:\n\n================\nwaiting threads:\n================\n\n{ /** Critical Section\n\n  inc cond.waiters_count\n\n}\n\n  /*\n  /* Atomic only if using Win32 SignalObjectAndWait\n  /*\n  cond.mtx.release\n  cond.sem.wait\n\n  /*** ^^-- WAITER CAN BE PREEMPTED AFTER BEING UNBLOCKED...\n\n{ /** Critical Section\n\n  dec cond.waiters_count\n\n  /*** ^^- ...AND BEFORE DECREMENTING THE COUNT (1)\n\n  last_waiter = ( cond.was_broadcast &&\n                    cond.waiters_count == 0 )\n\n  if ( last_waiter )\n\n    cond.was_broadcast = FALSE\n\n  endif\n\n}\n\n  if ( last_waiter )\n\n    /*\n    /* Atomic only if using Win32 SignalObjectAndWait\n    /*\n    cond.auto_reset_event_or_sem.post /* Event for Win32\n    cond.mtx.acquire\n\n  /*** ^^-- ...AND BEFORE CALL TO mtx.acquire (2)\n\n  /*** ^^-- NESTED BROADCASTS RESULT IN A DEADLOCK\n\n\n  else\n\n    cond.mtx.acquire\n\n  /*** ^^-- ...AND BEFORE CALL TO mtx.acquire (3)\n\n  endif\n\n\n==================\nbroadcast threads:\n==================\n\n{ /** Critical Section\n\n  waiters_count = cond.waiters_count\n\n  if ( waiters_count != 0 )\n\n    cond.was_broadcast = TRUE\n\n  endif\n\n}\n\nif ( waiters_count != 0 )\n\n  cond.sem.post waiters_count\n\n  /*** ^^^^^--- SPURIOUS WAKEUPS DUE TO (1)\n\n  cond.auto_reset_event_or_sem.wait /* Event for Win32\n\n  /*** ^^^^^--- DEADLOCK FOR FURTHER BROADCASTS IF THEY\n                HAPPEN TO GO INTO WAIT WHILE PREVIOUS\n                BROADCAST IS STILL IN PROGRESS/WAITING\n\nendif\n\na) cond.waiters_count does not accurately reflect\nnumber of waiters blocked on semaphore - that could\nresult (in the time window when counter is not accurate)\nin spurios wakeups organised by subsequent _signals\nand _broadcasts. From standard compliance point of view\nthat is OK but that could be a real problem from\nperformance/efficiency point of view.\n\nb) If subsequent broadcast happen to go into wait on\ncond.auto_reset_event_or_sem before previous\nbroadcast was unblocked from cond.auto_reset_event_or_sem\nby its last waiter, one of two blocked threads will\nremain blocked because last_waiter processing code\nfails to unblock both threads.\n\nIn the situation with tennisb.c the Player-B was put\nin a deadlock by noise (broadcast) coming from main\nthread. And since Player-B holds the game state\nmutex when it calls broadcast, the whole program\nstalled: Player-A was deadlocked on mutex and\nmain thread after finishing with producing the noise\nwas deadlocked on mutex too (needed to declare the\ngame over)\n\n(I produced the program output above by simply\nadding ?Sleep( 1 )?:\n\n==================\nbroadcast threads:\n==================\n\n{ /** Critical Section\n\n  waiters_count = cond.waiters_count\n\n  if ( waiters_count != 0 )\n\n    cond.was_broadcast = TRUE\n\n  endif\n\n}\n\nif ( waiters_count != 0 )\n\nSleep( 1 ); //Win32\n\n  cond.sem.post waiters_count\n\n  /*** ^^^^^--- SPURIOUS WAKEUPS DUE TO (1)\n\n  cond.auto_reset_event_or_sem.wait /* Event for Win32\n\n  /*** ^^^^^--- DEADLOCK FOR FURTHER BROADCASTS IF THEY\n                HAPPEN TO GO INTO WAIT WHILE PREVIOUS\n                BROADCAST IS STILL IN PROGRESS/WAITING\n\nendif\n\nto the source code of pthread-win32 implementation:\n\nhttp://sources.redhat.com/cgi-bin/cvsweb.cgi/pthreads/\ncondvar.c?rev=1.36&content-type=text/\nx-cvsweb-markup&cvsroot=pthreads-win32\n\n  if (wereWaiters)\n    {(wereWaiters)sroot=pthreads-win32eb.cgi/pthreads/Yem...m\n      /*\n      * Wake up all waiters\n      */\n\nSleep( 1 ); //@AT\n\n#ifdef NEED_SEM\n\n      result = (ptw32_increase_semaphore( &cv->sema, cv->waiters )\n                 ? 0\n                : EINVAL);\n\n#else /* NEED_SEM */\n\n      result = (ReleaseSemaphore( cv->sema, cv->waiters, NULL )\n                 ? 0\n                : EINVAL);\n\n#endif /* NEED_SEM */\n\n    }\n\n  (void) pthread_mutex_unlock(&(cv->waitersLock));\n\n  if (wereWaiters && result == 0)\n    {(wereWaiters\n      /*\n       * Wait for all the awakened threads to acquire their part of\n       * the counting semaphore\n       */\n\n      if (WaitForSingleObject (cv->waitersDone, INFINITE)\n          == WAIT_OBJECT_0)\n        {\n          result = 0;\n        }\n      else\n        {\n          result = EINVAL;\n        }\n\n    }\n\n  return (result);\n\n}\n\nBTW, on my system (2 CPUs) I can manage to get\nthe program stalled even without any source code\nmodification if I run the tennisb program many\ntimes in different shell sessions.\n\n===================\npthread-win32 patch\n===================\nstruct pthread_cond_t_ {\n  long            nWaitersBlocked;   /* Number of threads blocked\n*/\n  long            nWaitersUnblocked; /* Number of threads unblocked\n*/\n  long            nWaitersToUnblock; /* Number of threads to unblock\n*/\n  sem_t           semBlockQueue;     /* Queue up threads waiting for the\n*/\n                                     /*   condition to become signalled\n*/\n  sem_t           semBlockLock;      /* Semaphore that guards access to\n*/\n                                     /* | waiters blocked count/block queue\n*/\n                                     /* +-> Mandatory Sync.LEVEL-1\n*/\n  pthread_mutex_t mtxUnblockLock;    /* Mutex that guards access to\n*/\n                                     /* | waiters (to)unblock(ed) counts\n*/\n                                     /* +-> Optional* Sync.LEVEL-2\n*/\n};                                   /* Opt*) for _timedwait and\ncancellation*/\n\nint\npthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)\n  int result = EAGAIN;\n  pthread_cond_t cv = NULL;\n\n  if (cond == NULL)\n    {(cond\n      return EINVAL;\n    }\n\n  if ((attr != NULL && *attr != NULL) &&\n      ((*attr)->pshared == PTHREAD_PROCESS_SHARED))\n    {\n      /*\n       * Creating condition variable that can be shared between\n       * processes.\n       */\n      result = ENOSYS;\n\n      goto FAIL0;\n    }\n\n  cv = (pthread_cond_t) calloc (1, sizeof (*cv));\n\n  if (cv == NULL)\n    {(cv\n      result = ENOMEM;\n      goto FAIL0;\n    }\n\n  cv->nWaitersBlocked   = 0;\n  cv->nWaitersUnblocked = 0;\n  cv->nWaitersToUnblock = 0;\n\n  if (sem_init (&(cv->semBlockLock), 0, 1) != 0)\n    {(sem_init\n      goto FAIL0;\n    }\n\n  if (sem_init (&(cv->semBlockQueue), 0, 0) != 0)\n    {(sem_init\n      goto FAIL1;\n    }\n\n  if (pthread_mutex_init (&(cv->mtxUnblockLock), 0) != 0)\n    {(pthread_mutex_init\n      goto FAIL2;\n    }\n\n\n  result = 0;\n\n  goto DONE;\n\n  /*\n   * -------------\n   * Failed...\n   * -------------\n   */\nFAIL2:\n  (void) sem_destroy (&(cv->semBlockQueue));\n\nFAIL1:\n  (void) sem_destroy (&(cv->semBlockLock));\n\nFAIL0:\nDONE:\n  *cond = cv;\n\n  return (result);\n\n}                               /* pthread_cond_init */\n\nint\npthread_cond_destroy (pthread_cond_t * cond)\n{\n  int result = 0;\n  pthread_cond_t cv;\n\n  /*\n   * Assuming any race condition here is harmless.\n   */\n  if (cond == NULL\n      || *cond == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (*cond != (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)\n    {(*cond\n      cv = *cond;\n\n      /*\n       * Synchronize access to waiters blocked count (LEVEL-1)\n       */\n      if (sem_wait(&(cv->semBlockLock)) != 0)\n        {(sem_wait(&(cv->semBlockLock))\n          return errno;\n        }\n\n      /*\n       * Synchronize access to waiters (to)unblock(ed) counts (LEVEL-2)\n       */\n      if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0)\n        {((result\n          (void) sem_post(&(cv->semBlockLock));\n          return result;\n        }\n\n      /*\n       * Check whether cv is still busy (still has waiters blocked)\n       */\n      if (cv->nWaitersBlocked - cv->nWaitersUnblocked > 0)\n        {(cv->nWaitersBlocked\n          (void) sem_post(&(cv->semBlockLock));\n          (void) pthread_mutex_unlock(&(cv->mtxUnblockLock));\n          return EBUSY;\n        }\n\n      /*\n       * Now it is safe to destroy\n       */\n      (void) sem_destroy (&(cv->semBlockLock));\n      (void) sem_destroy (&(cv->semBlockQueue));\n      (void) pthread_mutex_unlock (&(cv->mtxUnblockLock));\n      (void) pthread_mutex_destroy (&(cv->mtxUnblockLock));\n\n      free(cv);\n      *cond = NULL;\n    }\n  else\n    {\n      /*\n       * See notes in ptw32_cond_check_need_init() above also.\n       */\n      EnterCriticalSection(&ptw32_cond_test_init_lock);\n\n      /*\n       * Check again.\n       */\n      if (*cond == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)\n        {(*cond\n          /*\n           * This is all we need to do to destroy a statically\n           * initialised cond that has not yet been used (initialised).\n           * If we get to here, another thread\n           * waiting to initialise this cond will get an EINVAL.\n           */\n          *cond = NULL;\n        }\n      else\n        {\n          /*\n           * The cv has been initialised while we were waiting\n           * so assume it's in use.\n           */\n          result = EBUSY;\n        }\n\n      LeaveCriticalSection(&ptw32_cond_test_init_lock);\n    }\n\n  return (result);\n}\n\n/*\n * Arguments for cond_wait_cleanup, since we can only pass a\n * single void * to it.\n */\ntypedef struct {\n  pthread_mutex_t * mutexPtr;\n  pthread_cond_t cv;\n  int * resultPtr;\n} ptw32_cond_wait_cleanup_args_t;\n\nstatic void\nptw32_cond_wait_cleanup(void * args)\n{\n  ptw32_cond_wait_cleanup_args_t * cleanup_args =\n(ptw32_cond_wait_cleanup_args_t *) args;\n  pthread_cond_t cv = cleanup_args->cv;\n  int * resultPtr = cleanup_args->resultPtr;\n  int eLastSignal; /* enum: 1=yes 0=no -1=cancelled/timedout w/o signal(s)\n*/\n  int result;\n\n  /*\n   * Whether we got here as a result of signal/broadcast or because of\n   * timeout on wait or thread cancellation we indicate that we are no\n   * longer waiting. The waiter is responsible for adjusting waiters\n   * (to)unblock(ed) counts (protected by unblock lock).\n   * Unblock lock/Sync.LEVEL-2 supports _timedwait and cancellation.\n   */\n  if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0)\n    {((result\n      *resultPtr = result;\n      return;\n    }\n\n  cv->nWaitersUnblocked++;\n\n  eLastSignal = (cv->nWaitersToUnblock == 0) ?\n                   -1 : (--cv->nWaitersToUnblock == 0);\n\n  /*\n   * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed\n   */\n  if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0)\n    {((result\n      *resultPtr = result;\n      return;\n    }\n\n  /*\n   * If last signal...\n   */\n  if (eLastSignal == 1)\n    {(eLastSignal\n     /*\n      * ...it means that we have end of 'atomic' signal/broadcast\n      */\n      if (sem_post(&(cv->semBlockLock)) != 0)\n        {(sem_post(&(cv->semBlockLock))\n          *resultPtr = errno;\n          return;\n        }\n    }\n  /*\n   * If not last signal and not timed out/cancelled wait w/o signal...\n   */\n  else if (eLastSignal == 0)\n    {\n     /*\n      * ...it means that next waiter can go through semaphore\n      */\n      if (sem_post(&(cv->semBlockQueue)) != 0)\n        {(sem_post(&(cv->semBlockQueue))\n          *resultPtr = errno;\n          return;\n        }\n    }\n\n  /*\n   * XSH: Upon successful return, the mutex has been locked and is owned\n   * by the calling thread\n   */\n  if ((result = pthread_mutex_lock(cleanup_args->mutexPtr)) != 0)\n    {((result\n      *resultPtr = result;\n    }\n\n}                               /* ptw32_cond_wait_cleanup */\n\nstatic int\nptw32_cond_timedwait (pthread_cond_t * cond,\n                      pthread_mutex_t * mutex,\n                      const struct timespec *abstime)\n{\n  int result = 0;\n  pthread_cond_t cv;\n  ptw32_cond_wait_cleanup_args_t cleanup_args;\n\n  if (cond == NULL || *cond == NULL)\n    {(cond\n      return EINVAL;\n    }\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static condition variable. We check\n   * again inside the guarded section of ptw32_cond_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*cond == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)\n    {(*cond\n      result = ptw32_cond_check_need_init(cond);\n    }\n\n  if (result != 0 && result != EBUSY)\n    {(result\n      return result;\n    }\n\n  cv = *cond;\n\n  /*\n   * Synchronize access to waiters blocked count (LEVEL-1)\n   */\n  if (sem_wait(&(cv->semBlockLock)) != 0)\n    {(sem_wait(&(cv->semBlockLock))\n      return errno;\n    }\n\n  cv->nWaitersBlocked++;\n\n  /*\n   * Thats it. Counted means waiting, no more access needed\n   */\n  if (sem_post(&(cv->semBlockLock)) != 0)\n    {(sem_post(&(cv->semBlockLock))\n      return errno;\n    }\n\n  /*\n   * Setup this waiter cleanup handler\n   */\n  cleanup_args.mutexPtr = mutex;\n  cleanup_args.cv = cv;\n  cleanup_args.resultPtr = &result;\n\n  pthread_cleanup_push (ptw32_cond_wait_cleanup, (void *) &cleanup_args);\n\n  /*\n   * Now we can release 'mutex' and...\n   */\n  if ((result = pthread_mutex_unlock (mutex)) == 0)\n    {((result\n\n      /*\n       * ...wait to be awakened by\n       *              pthread_cond_signal, or\n       *              pthread_cond_broadcast, or\n       *              timeout, or\n       *              thread cancellation\n       *\n       * Note:\n       *\n       *      ptw32_sem_timedwait is a cancellation point,\n       *      hence providing the mechanism for making\n       *      pthread_cond_wait a cancellation point.\n       *      We use the cleanup mechanism to ensure we\n       *      re-lock the mutex and adjust (to)unblock(ed) waiters\n       *      counts if we are cancelled, timed out or signalled.\n       */\n      if (ptw32_sem_timedwait (&(cv->semBlockQueue), abstime) != 0)\n        {(ptw32_sem_timedwait\n          result = errno;\n        }\n    }\n\n  /*\n   * Always cleanup\n   */\n  pthread_cleanup_pop (1);\n\n\n  /*\n   * \"result\" can be modified by the cleanup handler.\n   */\n  return (result);\n\n}                               /* ptw32_cond_timedwait */\n\n\nstatic int\nptw32_cond_unblock (pthread_cond_t * cond,\n                    int unblockAll)\n{\n  int result;\n  pthread_cond_t cv;\n\n  if (cond == NULL || *cond == NULL)\n    {(cond\n      return EINVAL;\n    }\n\n  cv = *cond;\n\n  /*\n   * No-op if the CV is static and hasn't been initialised yet.\n   * Assuming that any race condition is harmless.\n   */\n  if (cv == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT)\n    {(cv\n      return 0;\n    }\n\n  /*\n   * Synchronize access to waiters blocked count (LEVEL-1)\n   */\n  if (sem_wait(&(cv->semBlockLock)) != 0)\n    {(sem_wait(&(cv->semBlockLock))\n      return errno;\n    }\n\n  /*\n   * Synchronize access to waiters (to)unblock(ed) counts (LEVEL-2)\n   * This sync.level supports _timedwait and cancellation\n   */\n  if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0)\n    {((result\n      return result;\n    }\n\n  /*\n   * Adjust waiters blocked and unblocked counts (collect garbage)\n   */\n  if (cv->nWaitersUnblocked != 0)\n    {(cv->nWaitersUnblocked\n      cv->nWaitersBlocked  -= cv->nWaitersUnblocked;\n      cv->nWaitersUnblocked = 0;\n    }\n\n  /*\n   * If (after adjustment) there are still some waiters blocked counted...\n   */\n  if ( cv->nWaitersBlocked > 0)\n    {(\n      /*\n       * We will unblock first waiter and leave semBlockLock/LEVEL-1 locked\n       * LEVEL-1 access is left disabled until last signal/unblock\ncompletes\n       */\n      cv->nWaitersToUnblock = (unblockAll) ? cv->nWaitersBlocked : 1;\n\n      /*\n       * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed\n       * This sync.level supports _timedwait and cancellation\n       */\n      if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0)\n        {((result\n          return result;\n        }\n\n\n      /*\n       * Now, with LEVEL-2 lock released let first waiter go through\nsemaphore\n       */\n      if (sem_post(&(cv->semBlockQueue)) != 0)\n        {(sem_post(&(cv->semBlockQueue))\n          return errno;\n        }\n    }\n  /*\n   * No waiter blocked - no more LEVEL-1 access to blocked count needed...\n   */\n  else if (sem_post(&(cv->semBlockLock)) != 0)\n    {\n      return errno;\n    }\n  /*\n   * ...and no more LEVEL-2 access to waiters (to)unblock(ed) counts needed\ntoo\n   * This sync.level supports _timedwait and cancellation\n   */\n  else\n    {\n      result = pthread_mutex_unlock(&(cv->mtxUnblockLock));\n    }\n\n  return(result);\n\n}                               /* ptw32_cond_unblock */\n\nint\npthread_cond_wait (pthread_cond_t * cond,\n                   pthread_mutex_t * mutex)\n{\n  /* The NULL abstime arg means INFINITE waiting. */\n  return(ptw32_cond_timedwait(cond, mutex, NULL));\n}                               /* pthread_cond_wait */\n\n\nint\npthread_cond_timedwait (pthread_cond_t * cond,\n                        pthread_mutex_t * mutex,\n                        const struct timespec *abstime)\n{\n  if (abstime == NULL)\n    {(abstime\n      return EINVAL;\n    }\n\n  return(ptw32_cond_timedwait(cond, mutex, abstime));\n}                               /* pthread_cond_timedwait */\n\n\nint\npthread_cond_signal (pthread_cond_t * cond)\n{\n  /* The '0'(FALSE) unblockAll arg means unblock ONE waiter. */\n  return(ptw32_cond_unblock(cond, 0));\n}                               /* pthread_cond_signal */\n\nint\npthread_cond_broadcast (pthread_cond_t * cond)\n{\n  /* The '1'(TRUE) unblockAll arg means unblock ALL waiters. */\n  return(ptw32_cond_unblock(cond, 1));\n}                               /* pthread_cond_broadcast */\n\n\n\n\nTEREKHOV@de.ibm.com on 17.01.2001 01:00:57\n\nPlease respond to TEREKHOV@de.ibm.com\n\nTo:   pthreads-win32@sourceware.cygnus.com\ncc:   schmidt@uci.edu\nSubject:  win32 conditions: sem+counter+event = broadcast_deadlock +\n      spur.wakeup/unfairness/incorrectness ??\n\n\n\n\n\n\n\nHi,\n\nProblem 1: broadcast_deadlock\n\nIt seems that current implementation does not provide \"atomic\"\nbroadcasts. That may lead to \"nested\" broadcasts... and it seems\nthat nested case is not handled correctly -> producing a broadcast\nDEADLOCK as a result.\n\nScenario:\n\nN (>1) waiting threads W1..N are blocked (in _wait) on condition's\nsemaphore.\n\nThread B1 calls pthread_cond_broadcast, which results in \"releasing\" N\nW threads via incrementing semaphore counter by N (stored in\ncv->waiters) BUT cv->waiters counter does not change!! The caller\nthread B1 remains blocked on cv->waitersDone event (auto-reset!!) BUT\ncondition is not protected from starting another broadcast (when called\non another thread) while still waiting for the \"old\" broadcast to\ncomplete on thread B1.\n\nM (>=0, <N) W threads are fast enough to go thru their _wait call and\ndecrement cv->waiters counter.\n\nL (N-M) \"late\" waiter W threads are a) still blocked/not returned from\ntheir semaphore wait call or b) were preempted after sem_wait but before\nlock( &cv->waitersLock ) or c) are blocked on cv->waitersLock.\n\ncv->waiters is still > 0 (= L).\n\nAnother thread B2 (or some W thread from M group) calls\npthread_cond_broadcast and gains access to counter... neither a) nor b)\nprevent thread B2 in pthread_cond_broadcast from gaining access to\ncounter and starting another broadcast ( for c) - it depends on\ncv->waitersLock scheduling rules: FIFO=OK, PRTY=PROBLEM,... )\n\nThat call to pthread_cond_broadcast (on thread B2) will result in\nincrementing semaphore by cv->waiters (=L) which is INCORRECT (all\nW1..N were in fact already released by thread B1) and waiting on\n_auto-reset_ event cv->waitersDone which is DEADLY WRONG (produces a\ndeadlock)...\n\nAll late W1..L threads now have a chance to complete their _wait call.\nLast W_L thread sets an auto-reselt event cv->waitersDone which will\nrelease either B1 or B2 leaving one of B threads in a deadlock.\n\nProblem 2: spur.wakeup/unfairness/incorrectness\n\nIt seems that:\n\na) because of the same problem with counter which does not reflect the\nactual number of NOT RELEASED waiters, the signal call may increment\na semaphore counter w/o having a waiter blocked on it. That will result\nin (best case) spurious wake ups - performance degradation due to\nunnecessary context switches and predicate re-checks and (in worth case)\nunfairness/incorrectness problem - see b)\n\nb) neither signal nor broadcast prevent other threads - \"new waiters\"\n(and in the case of signal, the caller thread as well) from going into\n_wait and overtaking \"old\" waiters (already released but still not returned\nfrom sem_wait on condition's semaphore). Win semaphore just [API DOC]:\n\"Maintains a count between zero and some maximum value, limiting the number\nof threads that are simultaneously accessing a shared resource.\" Calling\nReleaseSemaphore does not imply (at least not documented) that on return\nfrom ReleaseSemaphore all waiters will in fact become released (returned\nfrom their Wait... call) and/or that new waiters calling Wait... afterwards\nwill become less importance. It is NOT documented to be an atomic release\nof\nwaiters... And even if it would be there is still a problem with a thread\nbeing preempted after Wait on semaphore and before Wait on cv->waitersLock\nand scheduling rules for cv->waitersLock itself\n(??WaitForMultipleObjects??)\nThat may result in unfairness/incorrectness problem as described\nfor SetEvent impl. in \"Strategies for Implementing POSIX Condition\nVariables\non Win32\": http://www.cs.wustl.edu/~schmidt/win32-cv-1.html\n\nUnfairness -- The semantics of the POSIX pthread_cond_broadcast function is\nto wake up all threads currently blocked in wait calls on the condition\nvariable. The awakened threads then compete for the external_mutex. To\nensure\nfairness, all of these threads should be released from their\npthread_cond_wait calls and allowed to recheck their condition expressions\nbefore other threads can successfully complete a wait on the condition\nvariable.\n\nUnfortunately, the SetEvent implementation above does not guarantee that\nall\nthreads sleeping on the condition variable when cond_broadcast is called\nwill\nacquire the external_mutex and check their condition expressions. Although\nthe Pthreads specification does not mandate this degree of fairness, the\nlack of fairness can cause starvation.\n\nTo illustrate the unfairness problem, imagine there are 2 threads, C1 and\nC2,\nthat are blocked in pthread_cond_wait on condition variable not_empty_ that\nis guarding a thread-safe message queue. Another thread, P1 then places two\nmessages onto the queue and calls pthread_cond_broadcast. If C1 returns\nfrom\npthread_cond_wait, dequeues and processes the message, and immediately\nwaits\nagain then it and only it may end up acquiring both messages. Thus, C2 will\nnever get a chance to dequeue a message and run.\n\nThe following illustrates the sequence of events:\n\n1.   Thread C1 attempts to dequeue and waits on CV non_empty_\n2.   Thread C2 attempts to dequeue and waits on CV non_empty_\n3.   Thread P1 enqueues 2 messages and broadcasts to CV not_empty_\n4.   Thread P1 exits\n5.   Thread C1 wakes up from CV not_empty_, dequeues a message and runs\n6.   Thread C1 waits again on CV not_empty_, immediately dequeues the 2nd\n        message and runs\n7.   Thread C1 exits\n8.   Thread C2 is the only thread left and blocks forever since\n        not_empty_ will never be signaled\n\nDepending on the algorithm being implemented, this lack of fairness may\nyield\nconcurrent programs that have subtle bugs. Of course, application\ndevelopers\nshould not rely on the fairness semantics of pthread_cond_broadcast.\nHowever,\nthere are many cases where fair implementations of condition variables can\nsimplify application code.\n\nIncorrectness -- A variation on the unfairness problem described above\noccurs\nwhen a third consumer thread, C3, is allowed to slip through even though it\nwas not waiting on condition variable not_empty_ when a broadcast occurred.\n\nTo illustrate this, we will use the same scenario as above: 2 threads, C1\nand\nC2, are blocked dequeuing messages from the message queue. Another thread,\nP1\nthen places two messages onto the queue and calls pthread_cond_broadcast.\nC1\nreturns from pthread_cond_wait, dequeues and processes the message. At this\ntime, C3 acquires the external_mutex, calls pthread_cond_wait and waits on\nthe events in WaitForMultipleObjects. Since C2 has not had a chance to run\nyet, the BROADCAST event is still signaled. C3 then returns from\nWaitForMultipleObjects, and dequeues and processes the message in the\nqueue.\nThus, C2 will never get a chance to dequeue a message and run.\n\nThe following illustrates the sequence of events:\n\n1.   Thread C1 attempts to dequeue and waits on CV non_empty_\n2.   Thread C2 attempts to dequeue and waits on CV non_empty_\n3.   Thread P1 enqueues 2 messages and broadcasts to CV not_empty_\n4.   Thread P1 exits\n5.   Thread C1 wakes up from CV not_empty_, dequeues a message and runs\n6.   Thread C1 exits\n7.   Thread C3 waits on CV not_empty_, immediately dequeues the 2nd\n        message and runs\n8.   Thread C3 exits\n9.   Thread C2 is the only thread left and blocks forever since\n        not_empty_ will never be signaled\n\nIn the above case, a thread that was not waiting on the condition variable\nwhen a broadcast occurred was allowed to proceed. This leads to incorrect\nsemantics for a condition variable.\n\n\nCOMMENTS???\n\nregards,\nalexander.\n\n-----------------------------------------------------------------------------\n\nSubject: RE: FYI/comp.programming.threads/Re: pthread_cond_*\n     implementation questions\nDate: Wed, 21 Feb 2001 11:54:47 +0100\nFrom: TEREKHOV@de.ibm.com\nTo: lthomas@arbitrade.com\nCC: rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>,\n     Nanbor Wang <nanbor@cs.wustl.edu>\n\nHi Louis,\n\ngeneration number 8..\n\nhad some time to revisit timeouts/spurious wakeup problem..\nfound some bugs (in 7.b/c/d) and something to improve\n(7a - using IPC semaphores but it should speedup Win32\nversion as well).\n\nregards,\nalexander.\n\n---------- Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL ------\ngiven:\nsemBlockLock - bin.semaphore\nsemBlockQueue - semaphore\nmtxExternal - mutex or CS\nmtxUnblockLock - mutex or CS\nnWaitersGone - int\nnWaitersBlocked - int\nnWaitersToUnblock - int\n\nwait( timeout ) {\n\n  [auto: register int result          ]     // error checking omitted\n  [auto: register int nSignalsWasLeft ]\n  [auto: register int nWaitersWasGone ]\n\n  sem_wait( semBlockLock );\n  nWaitersBlocked++;\n  sem_post( semBlockLock );\n\n  unlock( mtxExternal );\n  bTimedOut = sem_wait( semBlockQueue,timeout );\n\n  lock( mtxUnblockLock );\n  if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {\n    if ( bTimeout ) {                       // timeout (or canceled)\n      if ( 0 != nWaitersBlocked ) {\n        nWaitersBlocked--;\n      }\n      else {\n        nWaitersGone++;                     // count spurious wakeups\n      }\n    }\n    if ( 0 == --nWaitersToUnblock ) {\n      if ( 0 != nWaitersBlocked ) {\n        sem_post( semBlockLock );           // open the gate\n        nSignalsWasLeft = 0;                // do not open the gate below\nagain\n      }\n      else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {\n        nWaitersGone = 0;\n      }\n    }\n  }\n  else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious\nsemaphore :-)\n    sem_wait( semBlockLock );\n    nWaitersBlocked -= nWaitersGone;        // something is going on here -\ntest of timeouts? :-)\n    sem_post( semBlockLock );\n    nWaitersGone = 0;\n  }\n  unlock( mtxUnblockLock );\n\n  if ( 1 == nSignalsWasLeft ) {\n    if ( 0 != nWaitersWasGone ) {\n      // sem_adjust( -nWaitersWasGone );\n      while ( nWaitersWasGone-- ) {\n        sem_wait( semBlockLock );          // better now than spurious\nlater\n      }\n    }\n    sem_post( semBlockLock );              // open the gate\n  }\n\n  lock( mtxExternal );\n\n  return ( bTimedOut ) ? ETIMEOUT : 0;\n}\n\nsignal(bAll) {\n\n  [auto: register int result         ]\n  [auto: register int nSignalsToIssue]\n\n  lock( mtxUnblockLock );\n\n  if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!\n    if ( 0 == nWaitersBlocked ) { // NO-OP\n      return unlock( mtxUnblockLock );\n    }\n    if (bAll) {\n      nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;\n      nWaitersBlocked = 0;\n    }\n    else {\n      nSignalsToIssue = 1;\n      nWaitersToUnblock++;\n      nWaitersBlocked--;\n    }\n  }\n  else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!\n    sem_wait( semBlockLock ); // close the gate\n    if ( 0 != nWaitersGone ) {\n      nWaitersBlocked -= nWaitersGone;\n      nWaitersGone = 0;\n    }\n    if (bAll) {\n      nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;\n      nWaitersBlocked = 0;\n    }\n    else {\n      nSignalsToIssue = nWaitersToUnblock = 1;\n      nWaitersBlocked--;\n    }\n  }\n  else { // NO-OP\n    return unlock( mtxUnblockLock );\n  }\n\n  unlock( mtxUnblockLock );\n  sem_post( semBlockQueue,nSignalsToIssue );\n  return result;\n}\n\n---------- Algorithm 8b / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ONEBYONE\n------\ngiven:\nsemBlockLock - bin.semaphore\nsemBlockQueue - bin.semaphore\nmtxExternal - mutex or CS\nmtxUnblockLock - mutex or CS\nnWaitersGone - int\nnWaitersBlocked - int\nnWaitersToUnblock - int\n\nwait( timeout ) {\n\n  [auto: register int result          ]     // error checking omitted\n  [auto: register int nWaitersWasGone ]\n  [auto: register int nSignalsWasLeft ]\n\n  sem_wait( semBlockLock );\n  nWaitersBlocked++;\n  sem_post( semBlockLock );\n\n  unlock( mtxExternal );\n  bTimedOut = sem_wait( semBlockQueue,timeout );\n\n  lock( mtxUnblockLock );\n  if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {\n    if ( bTimeout ) {                       // timeout (or canceled)\n      if ( 0 != nWaitersBlocked ) {\n        nWaitersBlocked--;\n        nSignalsWasLeft = 0;                // do not unblock next waiter\nbelow (already unblocked)\n      }\n      else {\n        nWaitersGone = 1;                   // spurious wakeup pending!!\n      }\n    }\n    if ( 0 == --nWaitersToUnblock &&\n      if ( 0 != nWaitersBlocked ) {\n        sem_post( semBlockLock );           // open the gate\n        nSignalsWasLeft = 0;                // do not open the gate below\nagain\n      }\n      else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {\n        nWaitersGone = 0;\n      }\n    }\n  }\n  else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious\nsemaphore :-)\n    sem_wait( semBlockLock );\n    nWaitersBlocked -= nWaitersGone;        // something is going on here -\ntest of timeouts? :-)\n    sem_post( semBlockLock );\n    nWaitersGone = 0;\n  }\n  unlock( mtxUnblockLock );\n\n  if ( 1 == nSignalsWasLeft ) {\n    if ( 0 != nWaitersWasGone ) {\n      // sem_adjust( -1 );\n      sem_wait( semBlockQueue );           // better now than spurious\nlater\n    }\n    sem_post( semBlockLock );              // open the gate\n  }\n  else if ( 0 != nSignalsWasLeft ) {\n    sem_post( semBlockQueue );             // unblock next waiter\n  }\n\n  lock( mtxExternal );\n\n  return ( bTimedOut ) ? ETIMEOUT : 0;\n}\n\nsignal(bAll) {\n\n  [auto: register int result ]\n\n  lock( mtxUnblockLock );\n\n  if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!\n    if ( 0 == nWaitersBlocked ) { // NO-OP\n      return unlock( mtxUnblockLock );\n    }\n    if (bAll) {\n      nWaitersToUnblock += nWaitersBlocked;\n      nWaitersBlocked = 0;\n    }\n    else {\n      nWaitersToUnblock++;\n      nWaitersBlocked--;\n    }\n    unlock( mtxUnblockLock );\n  }\n  else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!\n    sem_wait( semBlockLock ); // close the gate\n    if ( 0 != nWaitersGone ) {\n      nWaitersBlocked -= nWaitersGone;\n      nWaitersGone = 0;\n    }\n    if (bAll) {\n      nWaitersToUnblock = nWaitersBlocked;\n      nWaitersBlocked = 0;\n    }\n    else {\n      nWaitersToUnblock = 1;\n      nWaitersBlocked--;\n    }\n    unlock( mtxUnblockLock );\n    sem_post( semBlockQueue );\n  }\n  else { // NO-OP\n    unlock( mtxUnblockLock );\n  }\n\n  return result;\n}\n\n---------- Algorithm 8c / IMPL_EVENT,UNBLOCK_STRATEGY == UNBLOCK_ONEBYONE\n---------\ngiven:\nhevBlockLock - auto-reset event\nhevBlockQueue - auto-reset event\nmtxExternal - mutex or CS\nmtxUnblockLock - mutex or CS\nnWaitersGone - int\nnWaitersBlocked - int\nnWaitersToUnblock - int\n\nwait( timeout ) {\n\n  [auto: register int result          ]     // error checking omitted\n  [auto: register int nSignalsWasLeft ]\n  [auto: register int nWaitersWasGone ]\n\n  wait( hevBlockLock,INFINITE );\n  nWaitersBlocked++;\n  set_event( hevBlockLock );\n\n  unlock( mtxExternal );\n  bTimedOut = wait( hevBlockQueue,timeout );\n\n  lock( mtxUnblockLock );\n  if ( 0 != (SignalsWasLeft = nWaitersToUnblock) ) {\n    if ( bTimeout ) {                       // timeout (or canceled)\n      if ( 0 != nWaitersBlocked ) {\n        nWaitersBlocked--;\n        nSignalsWasLeft = 0;                // do not unblock next waiter\nbelow (already unblocked)\n      }\n      else {\n        nWaitersGone = 1;                   // spurious wakeup pending!!\n      }\n    }\n    if ( 0 == --nWaitersToUnblock )\n      if ( 0 != nWaitersBlocked ) {\n        set_event( hevBlockLock );          // open the gate\n        nSignalsWasLeft = 0;                // do not open the gate below\nagain\n      }\n      else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {\n        nWaitersGone = 0;\n      }\n    }\n  }\n  else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious\nevent :-)\n    wait( hevBlockLock,INFINITE );\n    nWaitersBlocked -= nWaitersGone;        // something is going on here -\ntest of timeouts? :-)\n    set_event( hevBlockLock );\n    nWaitersGone = 0;\n  }\n  unlock( mtxUnblockLock );\n\n  if ( 1 == nSignalsWasLeft ) {\n    if ( 0 != nWaitersWasGone ) {\n      reset_event( hevBlockQueue );         // better now than spurious\nlater\n    }\n    set_event( hevBlockLock );              // open the gate\n  }\n  else if ( 0 != nSignalsWasLeft ) {\n    set_event( hevBlockQueue );             // unblock next waiter\n  }\n\n  lock( mtxExternal );\n\n  return ( bTimedOut ) ? ETIMEOUT : 0;\n}\n\nsignal(bAll) {\n\n  [auto: register int result ]\n\n  lock( mtxUnblockLock );\n\n  if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!\n    if ( 0 == nWaitersBlocked ) { // NO-OP\n      return unlock( mtxUnblockLock );\n    }\n    if (bAll) {\n      nWaitersToUnblock += nWaitersBlocked;\n      nWaitersBlocked = 0;\n    }\n    else {\n      nWaitersToUnblock++;\n      nWaitersBlocked--;\n    }\n    unlock( mtxUnblockLock );\n  }\n  else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!\n    wait( hevBlockLock,INFINITE ); // close the gate\n    if ( 0 != nWaitersGone ) {\n      nWaitersBlocked -= nWaitersGone;\n      nWaitersGone = 0;\n    }\n    if (bAll) {\n      nWaitersToUnblock = nWaitersBlocked;\n      nWaitersBlocked = 0;\n    }\n    else {\n      nWaitersToUnblock = 1;\n      nWaitersBlocked--;\n    }\n    unlock( mtxUnblockLock );\n    set_event( hevBlockQueue );\n  }\n  else { // NO-OP\n    unlock( mtxUnblockLock );\n  }\n\n  return result;\n}\n\n---------- Algorithm 8d / IMPL_EVENT,UNBLOCK_STRATEGY == UNBLOCK_ALL ------\ngiven:\nhevBlockLock - auto-reset event\nhevBlockQueueS - auto-reset event  // for signals\nhevBlockQueueB - manual-reset even // for broadcasts\nmtxExternal - mutex or CS\nmtxUnblockLock - mutex or CS\neBroadcast - int                   // 0: no broadcast, 1: broadcast, 2:\nbroadcast after signal(s)\nnWaitersGone - int\nnWaitersBlocked - int\nnWaitersToUnblock - int\n\nwait( timeout ) {\n\n  [auto: register int result          ]     // error checking omitted\n  [auto: register int eWasBroadcast   ]\n  [auto: register int nSignalsWasLeft ]\n  [auto: register int nWaitersWasGone ]\n\n  wait( hevBlockLock,INFINITE );\n  nWaitersBlocked++;\n  set_event( hevBlockLock );\n\n  unlock( mtxExternal );\n  bTimedOut = waitformultiple( hevBlockQueueS,hevBlockQueueB,timeout,ONE );\n\n  lock( mtxUnblockLock );\n  if ( 0 != (SignalsWasLeft = nWaitersToUnblock) ) {\n    if ( bTimeout ) {                       // timeout (or canceled)\n      if ( 0 != nWaitersBlocked ) {\n        nWaitersBlocked--;\n        nSignalsWasLeft = 0;                // do not unblock next waiter\nbelow (already unblocked)\n      }\n      else if ( 1 != eBroadcast ) {\n        nWaitersGone = 1;\n      }\n    }\n    if ( 0 == --nWaitersToUnblock ) {\n      if ( 0 != nWaitersBlocked ) {\n        set_event( hevBlockLock );           // open the gate\n        nSignalsWasLeft = 0;                 // do not open the gate below\nagain\n      }\n      else {\n        if ( 0 != (eWasBroadcast = eBroadcast) ) {\n          eBroadcast = 0;\n        }\n        if ( 0 != (nWaitersWasGone = nWaitersGone ) {\n          nWaitersGone = 0;\n        }\n      }\n    }\n    else if ( 0 != eBroadcast ) {\n      nSignalsWasLeft = 0;                  // do not unblock next waiter\nbelow (already unblocked)\n    }\n  }\n  else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious\nevent :-)\n    wait( hevBlockLock,INFINITE );\n    nWaitersBlocked -= nWaitersGone;        // something is going on here -\ntest of timeouts? :-)\n    set_event( hevBlockLock );\n    nWaitersGone = 0;\n  }\n  unlock( mtxUnblockLock );\n\n  if ( 1 == nSignalsWasLeft ) {\n    if ( 0 != eWasBroadcast ) {\n      reset_event( hevBlockQueueB );\n    }\n    if ( 0 != nWaitersWasGone ) {\n      reset_event( hevBlockQueueS );        // better now than spurious\nlater\n    }\n    set_event( hevBlockLock );              // open the gate\n  }\n  else if ( 0 != nSignalsWasLeft ) {\n    set_event( hevBlockQueueS );            // unblock next waiter\n  }\n\n  lock( mtxExternal );\n\n  return ( bTimedOut ) ? ETIMEOUT : 0;\n}\n\nsignal(bAll) {\n\n  [auto: register int    result        ]\n  [auto: register HANDLE hevBlockQueue ]\n\n  lock( mtxUnblockLock );\n\n  if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!\n    if ( 0 == nWaitersBlocked ) { // NO-OP\n      return unlock( mtxUnblockLock );\n    }\n    if (bAll) {\n      nWaitersToUnblock += nWaitersBlocked;\n      nWaitersBlocked = 0;\n      eBroadcast = 2;\n      hevBlockQueue = hevBlockQueueB;\n    }\n    else {\n      nWaitersToUnblock++;\n      nWaitersBlocked--;\n      return unlock( mtxUnblockLock );\n    }\n  }\n  else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!\n    wait( hevBlockLock,INFINITE ); // close the gate\n    if ( 0 != nWaitersGone ) {\n      nWaitersBlocked -= nWaitersGone;\n      nWaitersGone = 0;\n    }\n    if (bAll) {\n      nWaitersToUnblock = nWaitersBlocked;\n      nWaitersBlocked = 0;\n      eBroadcast = 1;\n      hevBlockQueue = hevBlockQueueB;\n    }\n    else {\n      nWaitersToUnblock = 1;\n      nWaitersBlocked--;\n      hevBlockQueue = hevBlockQueueS;\n    }\n  }\n  else { // NO-OP\n    return unlock( mtxUnblockLock );\n  }\n\n  unlock( mtxUnblockLock );\n  set_event( hevBlockQueue );\n  return result;\n}\n---------------------- Forwarded by Alexander Terekhov/Germany/IBM on\n02/21/2001 09:13 AM ---------------------------\n\nAlexander Terekhov\n02/20/2001 04:33 PM\n\nTo:   Louis Thomas <lthomas@arbitrade.com>\ncc:\n\nFrom: Alexander Terekhov/Germany/IBM@IBMDE\nSubject:  RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio\n      n questions\nImportance:    Normal\n\n>Sorry, gotta take a break and work on something else for a while.\n>Real work\n>calls, unfortunately. I'll get back to you in two or three days.\n\nok. no problem. here is some more stuff for pauses you might have\nin between :)\n\n---------- Algorithm 7d / IMPL_EVENT,UNBLOCK_STRATEGY == UNBLOCK_ALL ------\ngiven:\nhevBlockLock - auto-reset event\nhevBlockQueueS - auto-reset event  // for signals\nhevBlockQueueB - manual-reset even // for broadcasts\nmtxExternal - mutex or CS\nmtxUnblockLock - mutex or CS\nbBroadcast - int\nnWaitersGone - int\nnWaitersBlocked - int\nnWaitersToUnblock - int\n\nwait( timeout ) {\n\n  [auto: register int result          ]     // error checking omitted\n  [auto: register int bWasBroadcast   ]\n  [auto: register int nSignalsWasLeft ]\n\n  wait( hevBlockLock,INFINITE );\n  nWaitersBlocked++;\n  set_event( hevBlockLock );\n\n  unlock( mtxExternal );\n  bTimedOut = waitformultiple( hevBlockQueueS,hevBlockQueueB,timeout,ONE );\n\n  lock( mtxUnblockLock );\n  if ( 0 != (SignalsWasLeft = nWaitersToUnblock) ) {\n    if ( bTimeout ) {                       // timeout (or canceled)\n      if ( 0 != nWaitersBlocked ) {\n        nWaitersBlocked--;\n        nSignalsWasLeft = 0;                // do not unblock next waiter\nbelow (already unblocked)\n      }\n      else if ( !bBroadcast ) {\n        wait( hevBlockQueueS,INFINITE );    // better now than spurious\nlater\n      }\n    }\n    if ( 0 == --nWaitersToUnblock ) {\n      if ( 0 != nWaitersBlocked ) {\n        if ( bBroadcast ) {\n          reset_event( hevBlockQueueB );\n          bBroadcast = false;\n        }\n        set_event( hevBlockLock );           // open the gate\n        nSignalsWasLeft = 0;                 // do not open the gate below\nagain\n      }\n      else if ( false != (bWasBroadcast = bBroadcast) ) {\n        bBroadcast = false;\n      }\n    }\n    else {\n      bWasBroadcast = bBroadcast;\n    }\n  }\n  else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or spurious\nevent :-)\n    wait( hevBlockLock,INFINITE );\n    nWaitersBlocked -= nWaitersGone;        // something is going on here -\ntest of timeouts? :-)\n    set_event( hevBlockLock );\n    nWaitersGone = 0;\n  }\n  unlock( mtxUnblockLock );\n\n  if ( 1 == nSignalsWasLeft ) {\n    if ( bWasBroadcast ) {\n      reset_event( hevBlockQueueB );\n    }\n    set_event( hevBlockLock );              // open the gate\n  }\n  else if ( 0 != nSignalsWasLeft && !bWasBroadcast ) {\n    set_event( hevBlockQueueS );            // unblock next waiter\n  }\n\n  lock( mtxExternal );\n\n  return ( bTimedOut ) ? ETIMEOUT : 0;\n}\n\nsignal(bAll) {\n\n  [auto: register int    result        ]\n  [auto: register HANDLE hevBlockQueue ]\n\n  lock( mtxUnblockLock );\n\n  if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!\n    if ( 0 == nWaitersBlocked ) { // NO-OP\n      return unlock( mtxUnblockLock );\n    }\n    if (bAll) {\n      nWaitersToUnblock += nWaitersBlocked;\n      nWaitersBlocked = 0;\n      bBroadcast = true;\n      hevBlockQueue = hevBlockQueueB;\n    }\n    else {\n      nWaitersToUnblock++;\n      nWaitersBlocked--;\n      return unlock( mtxUnblockLock );\n    }\n  }\n  else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!\n    wait( hevBlockLock,INFINITE ); // close the gate\n    if ( 0 != nWaitersGone ) {\n      nWaitersBlocked -= nWaitersGone;\n      nWaitersGone = 0;\n    }\n    if (bAll) {\n      nWaitersToUnblock = nWaitersBlocked;\n      nWaitersBlocked = 0;\n      bBroadcast = true;\n      hevBlockQueue = hevBlockQueueB;\n    }\n    else {\n      nWaitersToUnblock = 1;\n      nWaitersBlocked--;\n      hevBlockQueue = hevBlockQueueS;\n    }\n  }\n  else { // NO-OP\n    return unlock( mtxUnblockLock );\n  }\n\n  unlock( mtxUnblockLock );\n  set_event( hevBlockQueue );\n  return result;\n}\n\n\n----------------------------------------------------------------------------\n\nSubject: RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio\n     n questions\nDate: Mon, 26 Feb 2001 22:20:12 -0600\nFrom: Louis Thomas <lthomas@arbitrade.com>\nTo: \"'TEREKHOV@de.ibm.com'\" <TEREKHOV@de.ibm.com>\nCC: rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>,\n     Nanbor Wang\n     <nanbor@cs.wustl.edu>\n\nSorry all. Busy week.\n\n> this insures the fairness\n> which POSIX does not (e.g. two subsequent broadcasts - the gate does\ninsure\n> that first wave waiters will start the race for the mutex before waiters\n> from the second wave - Linux pthreads process/unblock both waves\n> concurrently...)\n\nI'm not sure how we are any more fair about this than Linux. We certainly\ndon't guarantee that the threads released by the first broadcast will get\nthe external mutex before the threads of the second wave. In fact, it is\npossible that those threads will never get the external mutex if there is\nenough contention for it.\n\n> e.g. i was thinking about implementation with a pool of\n> N semaphores/counters [...]\n\nI considered that too. The problem is as you mentioned in a). You really\nneed to assign threads to semaphores once you know how you want to wake them\nup, not when they first begin waiting which is the only time you can assign\nthem.\n\n> well, i am not quite sure that i've fully understood your scenario,\n\nHmm. Well, it think it's an important example, so I'll try again. First, we\nhave thread A which we KNOW is waiting on a condition. As soon as it becomes\nunblocked for any reason, we will know because it will set a flag. Since the\nflag is not set, we are 100% confident that thread A is waiting on the\ncondition. We have another thread, thread B, which has acquired the mutex\nand is about to wait on the condition. Thus it is pretty clear that at any\npoint, either just A is waiting, or A and B are waiting. Now thread C comes\nalong. C is about to do a broadcast on the condition. A broadcast is\nguaranteed to unblock all threads currently waiting on a condition, right?\nAgain, we said that either just A is waiting, or A and B are both waiting.\nSo, when C does its broadcast, depending upon whether B has started waiting\nor not, thread C will unblock A or unblock A and B. Either way, C must\nunblock A, right?\n\nNow, you said anything that happens is correct so long as a) \"a signal is\nnot lost between unlocking the mutex and waiting on the condition\" and b) \"a\nthread must not steal a signal it sent\", correct? Requirement b) is easy to\nsatisfy: in this scenario, thread C will never wait on the condition, so it\nwon't steal any signals.  Requirement a) is not hard either. The only way we\ncould fail to meet requirement a) in this scenario is if thread B was\nstarted waiting but didn't wake up because a signal was lost. This will not\nhappen.\n\nNow, here is what happens. Assume thread C beats thread B. Thread C looks to\nsee how many threads are waiting on the condition. Thread C sees just one\nthread, thread A, waiting. It does a broadcast waking up just one thread\nbecause just one thread is waiting. Next, before A can become unblocked,\nthread B begins waiting. Now there are two threads waiting, but only one\nwill be unblocked. Suppose B wins. B will become unblocked. A will not\nbecome unblocked, because C only unblocked one thread (sema_post cond, 1).\nSo at the end, B finishes and A remains blocked.\n\nWe have met both of your requirements, so by your rules, this is an\nacceptable outcome. However, I think that the spec says this is an\nunacceptable outcome! We know for certain that A was waiting and that C did\na broadcast, but A did not become unblocked! Yet, the spec says that a\nbroadcast wakes up all waiting threads. This did not happen. Do you agree\nthat this shows your rules are not strict enough?\n\n> and what about N2? :) this one does allow almost everything.\n\nDon't get me started about rule #2. I'll NEVER advocate an algorithm that\nuses rule 2 as an excuse to suck!\n\n> but it is done (decrement)under mutex protection - this is not a subject\n> of a race condition.\n\nYou are correct. My mistake.\n\n> i would remove \"_bTimedOut=false\".. after all, it was a real timeout..\n\nI disagree. A thread that can't successfully retract its waiter status can't\nreally have timed out. If a thread can't return without executing extra code\nto deal with the fact that someone tried to unblock it, I think it is a poor\nidea to pretend we\ndidn't realize someone was trying to signal us. After all, a signal is more\nimportant than a time out.\n\n> when nSignaled != 0, it is possible to update nWaiters (--) and do not\n> touch nGone\n\nI realize this, but I was thinking that writing it the other ways saves\nanother if statement.\n\n> adjust only if nGone != 0 and save one cache memory write - probably much\nslower than 'if'\n\nHmm. You are probably right.\n\n> well, in a strange (e.g. timeout test) program you may (theoretically)\n> have an overflow of nWaiters/nGone counters (with waiters repeatedly\ntiming\n> out and no signals at all).\n\nAlso true. Not only that, but you also have the possibility that one could\noverflow the number of waiters as well! However, considering the limit you\nhave chosen for nWaitersGone, I suppose it is unlikely that anyone would be\nable to get INT_MAX/2 threads waiting on a single condition. :)\n\nAnalysis of 8a:\n\nIt looks correct to me.\n\nWhat are IPC semaphores?\n\nIn the line where you state, \"else if ( nWaitersBlocked > nWaitersGone ) {\n// HARMLESS RACE CONDITION!\" there is no race condition for nWaitersGone\nbecause nWaitersGone is never modified without holding mtxUnblockLock. You\nare correct that there is a harmless race on nWaitersBlocked, which can\nincrease and make the condition become true just after we check it. If this\nhappens, we interpret it as the wait starting after the signal.\n\nI like your optimization of this. You could improve Alg. 6 as follows:\n---------- Algorithm 6b ----------\nsignal(bAll) {\n  _nSig=0\n  lock counters\n  // this is safe because nWaiting can only be decremented by a thread that\n  // owns counters and nGone can only be changed by a thread that owns\ncounters.\n  if (nWaiting>nGone) {\n    if (0==nSignaled) {\n      sema_wait gate // close gate if not already closed\n    }\n    if (nGone>0) {\n      nWaiting-=nGone\n      nGone=0\n    }\n    _nSig=bAll?nWaiting:1\n    nSignaled+=_nSig\n    nWaiting-=_nSig\n  }\n  unlock counters\n  if (0!=_nSig) {\n    sema_post queue, _nSig\n  }\n}\n---------- ---------- ----------\nI guess this wouldn't apply to Alg 8a because nWaitersGone changes meanings\ndepending upon whether the gate is open or closed.\n\nIn the loop \"while ( nWaitersWasGone-- ) {\" you do a sema_wait on\nsemBlockLock. Perhaps waiting on semBlockQueue would be a better idea.\n\nWhat have you gained by making the last thread to be signaled do the waits\nfor all the timed out threads, besides added complexity? It took me a long\ntime to figure out what your objective was with this, to realize you were\nusing nWaitersGone to mean two different things, and to verify that you\nhadn't introduced any bug by doing this. Even now I'm not 100% sure.\n\nWhat has all this playing about with nWaitersGone really gained us besides a\nlot of complexity (it is much harder to verify that this solution is\ncorrect), execution overhead (we now have a lot more if statements to\nevaluate), and space overhead (more space for the extra code, and another\ninteger in our data)? We did manage to save a lock/unlock pair in an\nuncommon case (when a time out occurs) at the above mentioned expenses in\nthe common cases.\n\nAs for 8b, c, and d, they look ok though I haven't studied them thoroughly.\nWhat would you use them for?\n\n    Later,\n        -Louis! :)\n\n-----------------------------------------------------------------------------\n\nSubject: RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio\n     n questions\nDate: Tue, 27 Feb 2001 15:51:28 +0100\nFrom: TEREKHOV@de.ibm.com\nTo: Louis Thomas <lthomas@arbitrade.com>\nCC: rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>,\n     Nanbor Wang <nanbor@cs.wustl.edu>\n\nHi Louis,\n\n>> that first wave waiters will start the race for the mutex before waiters\n>> from the second wave - Linux pthreads process/unblock both waves\n>> concurrently...)\n>\n>I'm not sure how we are any more fair about this than Linux. We certainly\n>don't guarantee that the threads released by the first broadcast will get\n>the external mutex before the threads of the second wave. In fact, it is\n>possible that those threads will never get the external mutex if there is\n>enough contention for it.\n\ncorrect. but gate is nevertheless more fair than Linux because of the\nbarrier it establishes between two races (1st and 2nd wave waiters) for\nthe mutex which under 'normal' circumstances (e.g. all threads of equal\npriorities,..) will 'probably' result in fair behaviour with respect to\nmutex ownership.\n\n>> well, i am not quite sure that i've fully understood your scenario,\n>\n>Hmm. Well, it think it's an important example, so I'll try again. ...\n\nok. now i seem to understand this example. well, now it seems to me\nthat the only meaningful rule is just:\n\na) \"a signal is not lost between unlocking the mutex and waiting on the\ncondition\"\n\nand that the rule\n\nb) \"a thread must not steal a signal it sent\"\n\nis not needed at all because a thread which violates b) also violates a).\n\ni'll try to explain..\n\ni think that the most important thing is how POSIX defines waiter's\nvisibility:\n\n\"if another thread is able to acquire the mutex after the about-to-block\nthread\nhas released it, then a subsequent call to pthread_cond_signal() or\npthread_cond_broadcast() in that thread behaves as if it were issued after\nthe about-to-block thread has blocked. \"\n\nmy understanding is the following:\n\n1) there is no guarantees whatsoever with respect to whether\nsignal/broadcast\nwill actually unblock any 'waiter' if it is done w/o acquiring the mutex\nfirst\n(note that a thread may release it before signal/broadcast - it does not\nmatter).\n\n2) it is guaranteed that waiters become 'visible' - eligible for unblock as\nsoon\nas signalling thread acquires the mutex (but not before!!)\n\nso..\n\n>So, when C does its broadcast, depending upon whether B has started\nwaiting\n>or not, thread C will unblock A or unblock A and B. Either way, C must\n>unblock A, right?\n\nright. but only if C did acquire the mutex prior to broadcast (it may\nrelease it before broadcast as well).\n\nimplementation will violate waiters visibility rule (signal will become\nlost)\nif C will not unblock A.\n\n>Now, here is what happens. Assume thread C beats thread B. Thread C looks\nto\n>see how many threads are waiting on the condition. Thread C sees just one\n>thread, thread A, waiting. It does a broadcast waking up just one thread\n>because just one thread is waiting. Next, before A can become unblocked,\n>thread B begins waiting. Now there are two threads waiting, but only one\n>will be unblocked. Suppose B wins. B will become unblocked. A will not\n>become unblocked, because C only unblocked one thread (sema_post cond, 1).\n>So at the end, B finishes and A remains blocked.\n\nthread C did acquire the mutex (\"Thread C sees just one thread, thread A,\nwaiting\"). beginning from that moment it is guaranteed that subsequent\nbroadcast will unblock A. Otherwise we will have a lost signal with respect\nto A. I do think that it does not matter whether the signal was physically\n(completely) lost or was just stolen by another thread (B) - in both cases\nit was simply lost with respect to A.\n\n>..Do you agree that this shows your rules are not strict enough?\n\nprobably the opposite.. :-) i think that it shows that the only meaningful\nrule is\n\na) \"a signal is not lost between unlocking the mutex and waiting on the\ncondition\"\n\nwith clarification of waiters visibility as defined by POSIX above.\n\n>> i would remove \"_bTimedOut=false\".. after all, it was a real timeout..\n>\n>I disagree. A thread that can't successfully retract its waiter status\ncan't\n>really have timed out. If a thread can't return without executing extra\ncode\n>to deal with the fact that someone tried to unblock it, I think it is a\npoor\n>idea to pretend we\n>didn't realize someone was trying to signal us. After all, a signal is\nmore\n>important than a time out.\n\na) POSIX does allow timed out thread to consume a signal (cancelled is\nnot).\nb) ETIMEDOUT status just says that: \"The time specified by abstime to\npthread_cond_timedwait() has passed.\"\nc) it seem to me that hiding timeouts would violate \"The\npthread_cond_timedwait()\nfunction is the same as pthread_cond_wait() except that an error is\nreturned if\nthe absolute time specified by abstime passes (that is, system time equals\nor\nexceeds abstime) before the condition cond is signaled or broadcasted\"\nbecause\nthe abs. time did really pass before cond was signaled (waiter was\nreleased via semaphore). however, if it really matters, i could imaging\nthat we\ncan save an abs. time of signal/broadcast and compare it with timeout after\nunblock to find out whether it was a 'real' timeout or not. absent this\ncheck\ni do think that hiding timeouts would result in technical violation of\nspecification.. but i think that this check is not important and we can\nsimply\ntrust timeout error code provided by wait since we are not trying to make\n'hard' realtime implementation.\n\n>What are IPC semaphores?\n\n<sys/sem.h>\nint   semctl(int, int, int, ...);\nint   semget(key_t, int, int);\nint   semop(int, struct sembuf *, size_t);\n\nthey support adjustment of semaphore counter (semvalue)\nin one single call - imaging Win32 ReleaseSemaphore( hsem,-N )\n\n>In the line where you state, \"else if ( nWaitersBlocked > nWaitersGone ) {\n>// HARMLESS RACE CONDITION!\" there is no race condition for nWaitersGone\n>because nWaitersGone is never modified without holding mtxUnblockLock. You\n>are correct that there is a harmless race on nWaitersBlocked, which can\n>increase and make the condition become true just after we check it. If\nthis\n>happens, we interpret it as the wait starting after the signal.\n\nwell, the reason why i've asked on comp.programming.threads whether this\nrace\ncondition is harmless or not is that in order to be harmless it should not\nviolate the waiters visibility rule (see above). Fortunately, we increment\nthe counter under protection of external mutex.. so that any (signalling)\nthread which will acquire the mutex next, should see the updated counter\n(in signal) according to POSIX memory visibility rules and mutexes\n(memory barriers). But i am not so sure how it actually works on\nWin32/INTEL\nwhich does not explicitly define any memory visibility rules :(\n\n>I like your optimization of this. You could improve Alg. 6 as follows:\n>---------- Algorithm 6b ----------\n>signal(bAll) {\n>  _nSig=0\n>  lock counters\n>  // this is safe because nWaiting can only be decremented by a thread\nthat\n>  // owns counters and nGone can only be changed by a thread that owns\n>counters.\n>  if (nWaiting>nGone) {\n>    if (0==nSignaled) {\n>      sema_wait gate // close gate if not already closed\n>    }\n>    if (nGone>0) {\n>      nWaiting-=nGone\n>      nGone=0\n>    }\n>    _nSig=bAll?nWaiting:1\n>    nSignaled+=_nSig\n>    nWaiting-=_nSig\n>  }\n>  unlock counters\n>  if (0!=_nSig) {\n>    sema_post queue, _nSig\n>  }\n>}\n>---------- ---------- ----------\n>I guess this wouldn't apply to Alg 8a because nWaitersGone changes\nmeanings\n>depending upon whether the gate is open or closed.\n\nagree.\n\n>In the loop \"while ( nWaitersWasGone-- ) {\" you do a sema_wait on\n>semBlockLock. Perhaps waiting on semBlockQueue would be a better idea.\n\nyou are correct. my mistake.\n\n>What have you gained by making the last thread to be signaled do the waits\n>for all the timed out threads, besides added complexity? It took me a long\n>time to figure out what your objective was with this, to realize you were\n>using nWaitersGone to mean two different things, and to verify that you\n>hadn't introduced any bug by doing this. Even now I'm not 100% sure.\n>\n>What has all this playing about with nWaitersGone really gained us besides\na\n>lot of complexity (it is much harder to verify that this solution is\n>correct), execution overhead (we now have a lot more if statements to\n>evaluate), and space overhead (more space for the extra code, and another\n>integer in our data)? We did manage to save a lock/unlock pair in an\n>uncommon case (when a time out occurs) at the above mentioned expenses in\n>the common cases.\n\nwell, please consider the following:\n\n1) with multiple waiters unblocked (but some timed out) the trick with\ncounter\nseem to ensure potentially higher level of concurrency by not delaying\nmost of unblocked waiters for semaphore cleanup - only the last one\nwill be delayed but all others would already contend/acquire/release\nthe external mutex - the critical section protected by mtxUnblockLock is\nmade smaller (increment + couple of IFs is faster than system/kernel call)\nwhich i think is good in general. however, you are right, this is done\nat expense of 'normal' waiters..\n\n2) some semaphore APIs (e.g. POSIX IPC sems) do allow to adjust the\nsemaphore counter in one call => less system/kernel calls.. imagine:\n\nif ( 1 == nSignalsWasLeft ) {\n    if ( 0 != nWaitersWasGone ) {\n      ReleaseSemaphore( semBlockQueue,-nWaitersWasGone );  // better now\nthan spurious later\n    }\n    sem_post( semBlockLock );              // open the gate\n  }\n\n3) even on win32 a single thread doing multiple cleanup calls (to wait)\nwill probably result in faster execution (because of processor caching)\nthan multiple threads each doing a single call to wait.\n\n>As for 8b, c, and d, they look ok though I haven't studied them\nthoroughly.\n>What would you use them for?\n\n8b) for semaphores which do not allow to unblock multiple waiters\nin a single call to post/release (e.g. POSIX realtime semaphores -\n<semaphore.h>)\n\n8c/8d) for WinCE prior to 3.0 (WinCE 3.0 does have semaphores)\n\nok. so, which one is the 'final' algorithm(s) which we should use in\npthreads-win32??\n\nregards,\nalexander.\n\n----------------------------------------------------------------------------\n\nLouis Thomas <lthomas@arbitrade.com> on 02/27/2001 05:20:12 AM\n\nPlease respond to Louis Thomas <lthomas@arbitrade.com>\n\nTo:   Alexander Terekhov/Germany/IBM@IBMDE\ncc:   rpj@ise.canberra.edu.au, Thomas Pfaff <tpfaff@gmx.net>, Nanbor Wang\n      <nanbor@cs.wustl.edu>\nSubject:  RE: FYI/comp.programming.threads/Re: pthread_cond_* implementatio\n      n questions\n\nSorry all. Busy week.\n\n> this insures the fairness\n> which POSIX does not (e.g. two subsequent broadcasts - the gate does\ninsure\n> that first wave waiters will start the race for the mutex before waiters\n> from the second wave - Linux pthreads process/unblock both waves\n> concurrently...)\n\nI'm not sure how we are any more fair about this than Linux. We certainly\ndon't guarantee that the threads released by the first broadcast will get\nthe external mutex before the threads of the second wave. In fact, it is\npossible that those threads will never get the external mutex if there is\nenough contention for it.\n\n> e.g. i was thinking about implementation with a pool of\n> N semaphores/counters [...]\n\nI considered that too. The problem is as you mentioned in a). You really\nneed to assign threads to semaphores once you know how you want to wake\nthem\nup, not when they first begin waiting which is the only time you can assign\nthem.\n\n> well, i am not quite sure that i've fully understood your scenario,\n\nHmm. Well, it think it's an important example, so I'll try again. First, we\nhave thread A which we KNOW is waiting on a condition. As soon as it\nbecomes\nunblocked for any reason, we will know because it will set a flag. Since\nthe\nflag is not set, we are 100% confident that thread A is waiting on the\ncondition. We have another thread, thread B, which has acquired the mutex\nand is about to wait on the condition. Thus it is pretty clear that at any\npoint, either just A is waiting, or A and B are waiting. Now thread C comes\nalong. C is about to do a broadcast on the condition. A broadcast is\nguaranteed to unblock all threads currently waiting on a condition, right?\nAgain, we said that either just A is waiting, or A and B are both waiting.\nSo, when C does its broadcast, depending upon whether B has started waiting\nor not, thread C will unblock A or unblock A and B. Either way, C must\nunblock A, right?\n\nNow, you said anything that happens is correct so long as a) \"a signal is\nnot lost between unlocking the mutex and waiting on the condition\" and b)\n\"a\nthread must not steal a signal it sent\", correct? Requirement b) is easy to\nsatisfy: in this scenario, thread C will never wait on the condition, so it\nwon't steal any signals.  Requirement a) is not hard either. The only way\nwe\ncould fail to meet requirement a) in this scenario is if thread B was\nstarted waiting but didn't wake up because a signal was lost. This will not\nhappen.\n\nNow, here is what happens. Assume thread C beats thread B. Thread C looks\nto\nsee how many threads are waiting on the condition. Thread C sees just one\nthread, thread A, waiting. It does a broadcast waking up just one thread\nbecause just one thread is waiting. Next, before A can become unblocked,\nthread B begins waiting. Now there are two threads waiting, but only one\nwill be unblocked. Suppose B wins. B will become unblocked. A will not\nbecome unblocked, because C only unblocked one thread (sema_post cond, 1).\nSo at the end, B finishes and A remains blocked.\n\nWe have met both of your requirements, so by your rules, this is an\nacceptable outcome. However, I think that the spec says this is an\nunacceptable outcome! We know for certain that A was waiting and that C did\na broadcast, but A did not become unblocked! Yet, the spec says that a\nbroadcast wakes up all waiting threads. This did not happen. Do you agree\nthat this shows your rules are not strict enough?\n\n> and what about N2? :) this one does allow almost everything.\n\nDon't get me started about rule #2. I'll NEVER advocate an algorithm that\nuses rule 2 as an excuse to suck!\n\n> but it is done (decrement)under mutex protection - this is not a subject\n> of a race condition.\n\nYou are correct. My mistake.\n\n> i would remove \"_bTimedOut=false\".. after all, it was a real timeout..\n\nI disagree. A thread that can't successfully retract its waiter status\ncan't\nreally have timed out. If a thread can't return without executing extra\ncode\nto deal with the fact that someone tried to unblock it, I think it is a\npoor\nidea to pretend we\ndidn't realize someone was trying to signal us. After all, a signal is more\nimportant than a time out.\n\n> when nSignaled != 0, it is possible to update nWaiters (--) and do not\n> touch nGone\n\nI realize this, but I was thinking that writing it the other ways saves\nanother if statement.\n\n> adjust only if nGone != 0 and save one cache memory write - probably much\nslower than 'if'\n\nHmm. You are probably right.\n\n> well, in a strange (e.g. timeout test) program you may (theoretically)\n> have an overflow of nWaiters/nGone counters (with waiters repeatedly\ntiming\n> out and no signals at all).\n\nAlso true. Not only that, but you also have the possibility that one could\noverflow the number of waiters as well! However, considering the limit you\nhave chosen for nWaitersGone, I suppose it is unlikely that anyone would be\nable to get INT_MAX/2 threads waiting on a single condition. :)\n\nAnalysis of 8a:\n\nIt looks correct to me.\n\nWhat are IPC semaphores?\n\nIn the line where you state, \"else if ( nWaitersBlocked > nWaitersGone ) {\n// HARMLESS RACE CONDITION!\" there is no race condition for nWaitersGone\nbecause nWaitersGone is never modified without holding mtxUnblockLock. You\nare correct that there is a harmless race on nWaitersBlocked, which can\nincrease and make the condition become true just after we check it. If this\nhappens, we interpret it as the wait starting after the signal.\n\nI like your optimization of this. You could improve Alg. 6 as follows:\n---------- Algorithm 6b ----------\nsignal(bAll) {\n  _nSig=0\n  lock counters\n  // this is safe because nWaiting can only be decremented by a thread that\n  // owns counters and nGone can only be changed by a thread that owns\ncounters.\n  if (nWaiting>nGone) {\n    if (0==nSignaled) {\n      sema_wait gate // close gate if not already closed\n    }\n    if (nGone>0) {\n      nWaiting-=nGone\n      nGone=0\n    }\n    _nSig=bAll?nWaiting:1\n    nSignaled+=_nSig\n    nWaiting-=_nSig\n  }\n  unlock counters\n  if (0!=_nSig) {\n    sema_post queue, _nSig\n  }\n}\n---------- ---------- ----------\nI guess this wouldn't apply to Alg 8a because nWaitersGone changes meanings\ndepending upon whether the gate is open or closed.\n\nIn the loop \"while ( nWaitersWasGone-- ) {\" you do a sema_wait on\nsemBlockLock. Perhaps waiting on semBlockQueue would be a better idea.\n\nWhat have you gained by making the last thread to be signaled do the waits\nfor all the timed out threads, besides added complexity? It took me a long\ntime to figure out what your objective was with this, to realize you were\nusing nWaitersGone to mean two different things, and to verify that you\nhadn't introduced any bug by doing this. Even now I'm not 100% sure.\n\nWhat has all this playing about with nWaitersGone really gained us besides\na\nlot of complexity (it is much harder to verify that this solution is\ncorrect), execution overhead (we now have a lot more if statements to\nevaluate), and space overhead (more space for the extra code, and another\ninteger in our data)? We did manage to save a lock/unlock pair in an\nuncommon case (when a time out occurs) at the above mentioned expenses in\nthe common cases.\n\nAs for 8b, c, and d, they look ok though I haven't studied them thoroughly.\nWhat would you use them for?\n\n    Later,\n        -Louis! :)\n\n"
  },
  {
    "path": "deps/w32-pthreads/README.NONPORTABLE",
    "content": "This file documents non-portable functions and other issues.\n\nNon-portable functions included in pthreads-win32\n-------------------------------------------------\n\nBOOL\npthread_win32_test_features_np(int mask)\n\n\tThis routine allows an application to check which\n\trun-time auto-detected features are available within\n\tthe library.\n\n\tThe possible features are:\n\n\t\tPTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE\n\t\t\tReturn TRUE if the native version of\n\t\t\tInterlockedCompareExchange() is being used.\n\t\t\tThis feature is not meaningful in recent\n\t\t\tlibrary versions as MSVC builds only support\n\t\t\tsystem implemented ICE. Note that all Mingw\n\t\t\tbuilds use inlined asm versions of all the\n\t\t\tInterlocked routines.\n\t\tPTW32_ALERTABLE_ASYNC_CANCEL\n\t\t\tReturn TRUE is the QueueUserAPCEx package\n\t\t\tQUSEREX.DLL is available and the AlertDrv.sys\n\t\t\tdriver is loaded into Windows, providing\n\t\t\talertable (pre-emptive) asyncronous threads\n\t\t\tcancelation. If this feature returns FALSE\n\t\t\tthen the default async cancel scheme is in\n\t\t\tuse, which cannot cancel blocked threads.\n\n\tFeatures may be Or'ed into the mask parameter, in which case\n\tthe routine returns TRUE if any of the Or'ed features would\n\treturn TRUE. At this stage it doesn't make sense to Or features\n\tbut it may some day.\n\n\nvoid *\npthread_timechange_handler_np(void *)\n\n        To improve tolerance against operator or time service\n        initiated system clock changes.\n\n        This routine can be called by an application when it\n        receives a WM_TIMECHANGE message from the system. At\n        present it broadcasts all condition variables so that\n        waiting threads can wake up and re-evaluate their\n        conditions and restart their timed waits if required.\n\n        It has the same return type and argument type as a\n        thread routine so that it may be called directly\n        through pthread_create(), i.e. as a separate thread.\n\n        Parameters\n\n        Although a parameter must be supplied, it is ignored.\n        The value NULL can be used.\n\n        Return values\n\n        It can return an error EAGAIN to indicate that not\n        all condition variables were broadcast for some reason.\n        Otherwise, 0 is returned.\n\n        If run as a thread, the return value is returned\n        through pthread_join().\n\n        The return value should be cast to an integer.\n\n\nHANDLE\npthread_getw32threadhandle_np(pthread_t thread);\n\n\tReturns the win32 thread handle that the POSIX\n\tthread \"thread\" is running as.\n\n\tApplications can use the win32 handle to set\n\twin32 specific attributes of the thread.\n\nDWORD\npthread_getw32threadid_np (pthread_t thread)\n\n\tReturns the Windows native thread ID that the POSIX\n\tthread \"thread\" is running as.\n\n        Only valid when the library is built where\n        ! (defined(__MINGW64__) || defined(__MINGW32__)) || defined (__MSVCRT__) || defined (__DMC__)\n        and otherwise returns 0.\n\n\nint\npthread_mutexattr_setkind_np(pthread_mutexattr_t * attr, int kind)\n\nint\npthread_mutexattr_getkind_np(pthread_mutexattr_t * attr, int *kind)\n\n        These two routines are included for Linux compatibility\n        and are direct equivalents to the standard routines\n                pthread_mutexattr_settype\n                pthread_mutexattr_gettype\n\n        pthread_mutexattr_setkind_np accepts the following\n        mutex kinds:\n                PTHREAD_MUTEX_FAST_NP\n                PTHREAD_MUTEX_ERRORCHECK_NP\n                PTHREAD_MUTEX_RECURSIVE_NP\n\n        These are really just equivalent to (respectively):\n                PTHREAD_MUTEX_NORMAL\n                PTHREAD_MUTEX_ERRORCHECK\n                PTHREAD_MUTEX_RECURSIVE\n\nint\npthread_delay_np (const struct timespec *interval);\n\n        This routine causes a thread to delay execution for a specific period of time.\n        This period ends at the current time plus the specified interval. The routine\n        will not return before the end of the period is reached, but may return an\n        arbitrary amount of time after the period has gone by. This can be due to\n        system load, thread priorities, and system timer granularity.\n\n        Specifying an interval of zero (0) seconds and zero (0) nanoseconds is\n        allowed and can be used to force the thread to give up the processor or to\n        deliver a pending cancelation request.\n\n        This routine is a cancelation point.\n\n        The timespec structure contains the following two fields:\n\n                tv_sec is an integer number of seconds.\n                tv_nsec is an integer number of nanoseconds. \n\n        Return Values\n\n        If an error condition occurs, this routine returns an integer value\n        indicating the type of error. Possible return values are as follows:\n\n        0          Successful completion. \n        [EINVAL]   The value specified by interval is invalid. \n\nint\npthread_num_processors_np (void)\n\n        This routine (found on HPUX systems) returns the number of processors\n        in the system. This implementation actually returns the number of\n        processors available to the process, which can be a lower number\n        than the system's number, depending on the process's affinity mask.\n\nBOOL\npthread_win32_process_attach_np (void);\n\nBOOL\npthread_win32_process_detach_np (void);\n\nBOOL\npthread_win32_thread_attach_np (void);\n\nBOOL\npthread_win32_thread_detach_np (void);\n\n\tThese functions contain the code normally run via dllMain\n\twhen the library is used as a dll but which need to be\n\tcalled explicitly by an application when the library\n\tis statically linked. As of version 2.9.0 of the library, static\n\tbuilds using either MSC or GCC will call pthread_win32_process_*\n\tautomatically at application startup and exit respectively.\n\n\tOtherwise, you will need to call pthread_win32_process_attach_np()\n\tbefore you can call any pthread routines when statically linking.\n\tYou should call pthread_win32_process_detach_np() before\n\texiting your application to clean up.\n\n\tpthread_win32_thread_attach_np() is currently a no-op, but\n\tpthread_win32_thread_detach_np() is needed to clean up\n\tthe implicit pthread handle that is allocated to a Win32 thread if\n\tit calls any pthreads routines. Call this routine when the\n\tWin32 thread exits.\n\n\tThreads created through pthread_create() do not\tneed to call\n\tpthread_win32_thread_detach_np().\n\n\tThese functions invariably return TRUE except for\n\tpthread_win32_process_attach_np() which will return FALSE\n\tif pthreads-win32 initialisation fails.\n\nint\npthreadCancelableWait (HANDLE waitHandle);\n\nint\npthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout);\n\n\tThese two functions provide hooks into the pthread_cancel\n\tmechanism that will allow you to wait on a Windows handle\n\tand make it a cancellation point. Both functions block\n\tuntil either the given w32 handle is signaled, or\n\tpthread_cancel has been called. It is implemented using\n\tWaitForMultipleObjects on 'waitHandle' and a manually\n\treset w32 event used to implement pthread_cancel.\n\n\nNon-portable issues\n-------------------\n\nThread priority\n\n\tPOSIX defines a single contiguous range of numbers that determine a\n\tthread's priority. Win32 defines priority classes and priority\n\tlevels relative to these classes. Classes are simply priority base\n\tlevels that the defined priority levels are relative to such that,\n\tchanging a process's priority class will change the priority of all\n\tof it's threads, while the threads retain the same relativity to each\n\tother.\n\n\tA Win32 system defines a single contiguous monotonic range of values\n\tthat define system priority levels, just like POSIX. However, Win32\n\trestricts individual threads to a subset of this range on a\n\tper-process basis.\n\n\tThe following table shows the base priority levels for combinations\n\tof priority class and priority value in Win32.\n\t\n\t Process Priority Class               Thread Priority Level\n\t -----------------------------------------------------------------\n\t 1 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_IDLE\n\t 1 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_IDLE\n\t 1 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_IDLE\n\t 1 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_IDLE\n\t 1 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_IDLE\n\t 2 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_LOWEST\n\t 3 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_BELOW_NORMAL\n\t 4 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_NORMAL\n\t 4 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_LOWEST\n\t 5 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_ABOVE_NORMAL\n\t 5 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_BELOW_NORMAL\n\t 5 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_LOWEST\n\t 6 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_HIGHEST\n\t 6 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_NORMAL\n\t 6 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_BELOW_NORMAL\n\t 7 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_ABOVE_NORMAL\n\t 7 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_NORMAL\n\t 7 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_LOWEST\n \t 8 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_HIGHEST\n\t 8 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_ABOVE_NORMAL\n\t 8 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_BELOW_NORMAL\n\t 8 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_LOWEST\n\t 9 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_HIGHEST\n\t 9 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_NORMAL\n\t 9 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_BELOW_NORMAL\n\t10 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_ABOVE_NORMAL\n\t10 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_NORMAL\n\t11 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_HIGHEST\n\t11 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_ABOVE_NORMAL\n\t11 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_LOWEST\n\t12 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_HIGHEST\n\t12 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_BELOW_NORMAL\n\t13 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_NORMAL\n\t14 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_ABOVE_NORMAL\n\t15 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_HIGHEST\n\t15 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_TIME_CRITICAL\n\t15 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_TIME_CRITICAL\n\t15 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_TIME_CRITICAL\n\t15 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_TIME_CRITICAL\n\t15 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_TIME_CRITICAL\n\t16 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_IDLE\n\t17 REALTIME_PRIORITY_CLASS            -7\n\t18 REALTIME_PRIORITY_CLASS            -6\n\t19 REALTIME_PRIORITY_CLASS            -5\n\t20 REALTIME_PRIORITY_CLASS            -4\n\t21 REALTIME_PRIORITY_CLASS            -3\n\t22 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_LOWEST\n\t23 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_BELOW_NORMAL\n\t24 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_NORMAL\n\t25 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_ABOVE_NORMAL\n\t26 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_HIGHEST\n\t27 REALTIME_PRIORITY_CLASS             3\n\t28 REALTIME_PRIORITY_CLASS             4\n\t29 REALTIME_PRIORITY_CLASS             5\n\t30 REALTIME_PRIORITY_CLASS             6\n\t31 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_TIME_CRITICAL\n\t\n\tWindows NT:  Values -7, -6, -5, -4, -3, 3, 4, 5, and 6 are not supported.\n\n\n\tAs you can see, the real priority levels available to any individual\n\tWin32 thread are non-contiguous.\n\n\tAn application using pthreads-win32 should not make assumptions about\n\tthe numbers used to represent thread priority levels, except that they\n\tare monotonic between the values returned by sched_get_priority_min()\n\tand sched_get_priority_max(). E.g. Windows 95, 98, NT, 2000, XP make\n\tavailable a non-contiguous range of numbers between -15 and 15, while\n\tat least one version of WinCE (3.0) defines the minimum priority\n\t(THREAD_PRIORITY_LOWEST) as 5, and the maximum priority\n\t(THREAD_PRIORITY_HIGHEST) as 1.\n\n\tInternally, pthreads-win32 maps any priority levels between\n\tTHREAD_PRIORITY_IDLE and THREAD_PRIORITY_LOWEST to THREAD_PRIORITY_LOWEST,\n\tor between THREAD_PRIORITY_TIME_CRITICAL and THREAD_PRIORITY_HIGHEST to\n\tTHREAD_PRIORITY_HIGHEST. Currently, this also applies to\n\tREALTIME_PRIORITY_CLASSi even if levels -7, -6, -5, -4, -3, 3, 4, 5, and 6\n\tare supported.\n\n\tIf it wishes, a Win32 application using pthreads-win32 can use the Win32\n\tdefined priority macros THREAD_PRIORITY_IDLE through\n\tTHREAD_PRIORITY_TIME_CRITICAL.\n\n\nThe opacity of the pthread_t datatype\n-------------------------------------\nand possible solutions for portable null/compare/hash, etc\n----------------------------------------------------------\n\nBecause pthread_t is an opague datatype an implementation is permitted to define\npthread_t in any way it wishes. That includes defining some bits, if it is\nscalar, or members, if it is an aggregate, to store information that may be\nextra to the unique identifying value of the ID. As a result, pthread_t values\nmay not be directly comparable.\n\nIf you want your code to be portable you must adhere to the following contraints:\n\n1) Don't assume it is a scalar data type, e.g. an integer or pointer value. There\nare several other implementations where pthread_t is also a struct. See our FAQ\nQuestion 11 for our reasons for defining pthread_t as a struct.\n\n2) You must not compare them using relational or equality operators. You must use\nthe API function pthread_equal() to test for equality.\n\n3) Never attempt to reference individual members.\n\n\nThe problem\n\nCertain applications would like to be able to access only the 'pure' pthread_t\nid values, primarily to use as keys into data structures to manage threads or\nthread-related data, but this is not possible in a maximally portable and\nstandards compliant way for current POSIX threads implementations.\n\nFor implementations that define pthread_t as a scalar, programmers often employ\ndirect relational and equality operators on pthread_t. This code will break when\nported to an implementation that defines pthread_t as an aggregate type.\n\nFor implementations that define pthread_t as an aggregate, e.g. a struct,\nprogrammers can use memcmp etc., but then face the prospect that the struct may\ninclude alignment padding bytes or bits as well as extra implementation-specific\nmembers that are not part of the unique identifying value.\n\n[While this is not currently the case for pthreads-win32, opacity also\nmeans that an implementation is free to change the definition, which should\ngenerally only require that applications be recompiled and relinked, not\nrewritten.]\n\n\nDoesn't the compiler take care of padding?\n\nThe C89 and later standards only effectively guarrantee element-by-element\nequivalence following an assignment or pass by value of a struct or union,\ntherefore undefined areas of any two otherwise equivalent pthread_t instances\ncan still compare differently, e.g. attempting to compare two such pthread_t\nvariables byte-by-byte, e.g. memcmp(&t1, &t2, sizeof(pthread_t) may give an\nincorrect result. In practice I'm reasonably confident that compilers routinely\nalso copy the padding bytes, mainly because assignment of unions would be far\ntoo complicated otherwise. But it just isn't guarranteed by the standard.\n\nIllustration:\n\nWe have two thread IDs t1 and t2\n\npthread_t t1, t2;\n\nIn an application we create the threads and intend to store the thread IDs in an\nordered data structure (linked list, tree, etc) so we need to be able to compare\nthem in order to insert them initially and also to traverse.\n\nSuppose pthread_t contains undefined padding bits and our compiler copies our\npthread_t [struct] element-by-element, then for the assignment:\n\npthread_t temp = t1;\n\ntemp and t1 will be equivalent and correct but a byte-for-byte comparison such as\nmemcmp(&temp, &t1, sizeof(pthread_t)) == 0 may not return true as we expect because\nthe undefined bits may not have the same values in the two variable instances.\n\nSimilarly if passing by value under the same conditions.\n\nIf, on the other hand, the undefined bits are at least constant through every\nassignment and pass-by-value then the byte-for-byte comparison\nmemcmp(&temp, &t1, sizeof(pthread_t)) == 0 will always return the expected result.\nHow can we force the behaviour we need?\n\n\nSolutions\n\nAdding new functions to the standard API or as non-portable extentions is\nthe only reliable and portable way to provide the necessary operations.\nRemember also that POSIX is not tied to the C language. The most common\nfunctions that have been suggested are:\n\npthread_null()\npthread_compare()\npthread_hash()\n\nA single more general purpose function could also be defined as a\nbasis for at least the last two of the above functions.\n\nFirst we need to list the freedoms and constraints with restpect\nto pthread_t so that we can be sure our solution is compatible with the\nstandard.\n\nWhat is known or may be deduced from the standard:\n1) pthread_t must be able to be passed by value, so it must be a single object.\n2) from (1) it must be copyable so cannot embed thread-state information, locks\nor other volatile objects required to manage the thread it associates with.\n3) pthread_t may carry additional information, e.g. for debugging or to manage\nitself.\n4) there is an implicit requirement that the size of pthread_t is determinable\nat compile-time and size-invariant, because it must be able to copy the object\n(i.e. through assignment and pass-by-value). Such copies must be genuine\nduplicates, not merely a copy of a pointer to a common instance such as\nwould be the case if pthread_t were defined as an array.\n\n\nSuppose we define the following function:\n\n/* This function shall return it's argument */\npthread_t* pthread_normalize(pthread_t* thread);\n\nFor scalar or aggregate pthread_t types this function would simply zero any bits\nwithin the pthread_t that don't uniquely identify the thread, including padding,\nsuch that client code can return consistent results from operations done on the\nresult. If the additional bits are a pointer to an associate structure then\nthis function would ensure that the memory used to store that associate\nstructure does not leak. After normalization the following compare would be\nvalid and repeatable:\n\nmemcmp(pthread_normalize(&t1),pthread_normalize(&t2),sizeof(pthread_t))\n\nNote 1: such comparisons are intended merely to order and sort pthread_t values\nand allow them to index various data structures. They are not intended to reveal\nanything about the relationships between threads, like startup order.\n\nNote 2: the normalized pthread_t is also a valid pthread_t that uniquely\nidentifies the same thread.\n\nAdvantages:\n1) In most existing implementations this function would reduce to a no-op that\nemits no additional instructions, i.e after in-lining or optimisation, or if\ndefined as a macro:\n#define pthread_normalise(tptr) (tptr)\n\n2) This single function allows an application to portably derive\napplication-level versions of any of the other required functions.\n\n3) It is a generic function that could enable unanticipated uses.\n\nDisadvantages:\n1) Less efficient than dedicated compare or hash functions for implementations\nthat include significant extra non-id elements in pthread_t.\n\n2) Still need to be concerned about padding if copying normalized pthread_t.\nSee the later section on defining pthread_t to neutralise padding issues.\n\nGenerally a pthread_t may need to be normalized every time it is used,\nwhich could have a significant impact. However, this is a design decision\nfor the implementor in a competitive environment. An implementation is free\nto define a pthread_t in a way that minimises or eliminates padding or\nrenders this function a no-op.\n\nHazards:\n1) Pass-by-reference directly modifies 'thread' so the application must\nsynchronise access or ensure that the pointer refers to a copy. The alternative\nof pass-by-value/return-by-value was considered but then this requires two copy\noperations, disadvantaging implementations where this function is not a no-op\nin terms of speed of execution. This function is intended to be used in high\nfrequency situations and needs to be efficient, or at least not unnecessarily\ninefficient. The alternative also sits awkwardly with functions like memcmp.\n\n2) [Non-compliant] code that uses relational and equality operators on\narithmetic or pointer style pthread_t types would need to be rewritten, but it\nshould be rewritten anyway.\n\n\nC implementation of null/compare/hash functions using pthread_normalize():\n\n/* In pthread.h */\npthread_t* pthread_normalize(pthread_t* thread);\n\n/* In user code */\n/* User-level bitclear function - clear bits in loc corresponding to mask */\nvoid* bitclear (void* loc, void* mask, size_t count);\n\ntypedef unsigned int hash_t;\n\n/* User-level hash function */\nhash_t hash(void* ptr, size_t count);\n\n/*\n * User-level pthr_null function - modifies the origin thread handle.\n * The concept of a null pthread_t is highly implementation dependent\n * and this design may be far from the mark. For example, in an\n * implementation \"null\" may mean setting a special value inside one\n * element of pthread_t to mean \"INVALID\". However, if that value was zero and\n * formed part of the id component then we may get away with this design.\n */\npthread_t* pthr_null(pthread_t* tp)\n{\n  /* \n   * This should have the same effect as memset(tp, 0, sizeof(pthread_t))\n   * We're just showing that we can do it.\n   */\n  void* p = (void*) pthread_normalize(tp);\n  return (pthread_t*) bitclear(p, p, sizeof(pthread_t));\n}\n\n/*\n * Safe user-level pthr_compare function - modifies temporary thread handle copies\n */\nint pthr_compare_safe(pthread_t thread1, pthread_t thread2)\n{\n  return memcmp(pthread_normalize(&thread1), pthread_normalize(&thread2), sizeof(pthread_t));\n}\n\n/*\n * Fast user-level pthr_compare function - modifies origin thread handles\n */\nint pthr_compare_fast(pthread_t* thread1, pthread_t* thread2)\n{\n  return memcmp(pthread_normalize(&thread1), pthread_normalize(&thread2), sizeof(pthread_t));\n}\n\n/*\n * Safe user-level pthr_hash function - modifies temporary thread handle copy\n */\nhash_t pthr_hash_safe(pthread_t thread)\n{\n  return hash((void *) pthread_normalize(&thread), sizeof(pthread_t));\n}\n\n/*\n * Fast user-level pthr_hash function - modifies origin thread handle\n */\nhash_t pthr_hash_fast(pthread_t thread)\n{\n  return hash((void *) pthread_normalize(&thread), sizeof(pthread_t));\n}\n\n/* User-level bitclear function - modifies the origin array */\nvoid* bitclear(void* loc, void* mask, size_t count)\n{\n  int i;\n  for (i=0; i < count; i++) {\n    (unsigned char) *loc++ &= ~((unsigned char) *mask++);\n  }\n}\n\n/* Donald Knuth hash */\nhash_t hash(void* str, size_t count)\n{\n   hash_t hash = (hash_t) count;\n   unsigned int i = 0;\n\n   for(i = 0; i < len; str++, i++)\n   {\n      hash = ((hash << 5) ^ (hash >> 27)) ^ (*str);\n   }\n   return hash;\n}\n\n/* Example of advantage point (3) - split a thread handle into its id and non-id values */\npthread_t id = thread, non-id = thread;\nbitclear((void*) &non-id, (void*) pthread_normalize(&id), sizeof(pthread_t));\n\n\nA pthread_t type change proposal to neutralise the effects of padding\n\nEven if pthread_nornalize() is available, padding is still a problem because\nthe standard only garrantees element-by-element equivalence through\ncopy operations (assignment and pass-by-value). So padding bit values can\nstill change randomly after calls to pthread_normalize().\n\n[I suspect that most compilers take the easy path and always byte-copy anyway,\npartly because it becomes too complex to do (e.g. unions that contain sub-aggregates)\nbut also because programmers can easily design their aggregates to minimise and\noften eliminate padding].\n\nHow can we eliminate the problem of padding bytes in structs? Could\ndefining pthread_t as a union rather than a struct provide a solution?\n\nIn fact, the Linux pthread.h defines most of it's pthread_*_t objects (but not\npthread_t itself) as unions, possibly for this and/or other reasons. We'll\nborrow some element naming from there but the ideas themselves are well known\n- the __align element used to force alignment of the union comes from K&R's\nstorage allocator example.\n\n/* Essentially our current pthread_t renamed */\ntypedef struct {\n  struct thread_state_t * __p;\n  long __x; /* sequence counter */\n} thread_id_t;\n\nEnsuring that the last element in the above struct is a long ensures that the\noverall struct size is a multiple of sizeof(long), so there should be no trailing\npadding in this struct or the union we define below.\n(Later we'll see that we can handle internal but not trailing padding.)\n\n/* New pthread_t */\ntypedef union {\n  char __size[sizeof(thread_id_t)]; /* array as the first element */\n  thread_id_t __tid;\n  long __align;  /* Ensure that the union starts on long boundary */\n} pthread_t;\n\nThis guarrantees that, during an assignment or pass-by-value, the compiler copies\nevery byte in our thread_id_t because the compiler guarrantees that the __size\narray, which we have ensured is the equal-largest element in the union, retains\nequivalence.\n\nThis means that pthread_t values stored, assigned and passed by value will at least\ncarry the value of any undefined padding bytes along and therefore ensure that\nthose values remain consistent. Our comparisons will return consistent results and\nour hashes of [zero initialised] pthread_t values will also return consistent\nresults.\n\nWe have also removed the need for a pthread_null() function; we can initialise\nat declaration time or easily create our own const pthread_t to use in assignments\nlater:\n\nconst pthread_t null_tid = {0}; /* braces are required */\n\npthread_t t;\n...\nt = null_tid;\n\n\nNote that we don't have to explicitly make use of the __size array at all. It's\nthere just to force the compiler behaviour we want.\n\n\nPartial solutions without a pthread_normalize function\n\n\nAn application-level pthread_null and pthread_compare proposal\n(and pthread_hash proposal by extention)\n\nIn order to deal with the problem of scalar/aggregate pthread_t type disparity in\nportable code I suggest using an old-fashioned union, e.g.:\n\nContraints:\n- there is no padding, or padding values are preserved through assignment and\n  pass-by-value (see above);\n- there are no extra non-id values in the pthread_t.\n\n\nExample 1: A null initialiser for pthread_t variables...\n\ntypedef union {\n    unsigned char b[sizeof(pthread_t)];\n    pthread_t t;\n} init_t;\n\nconst init_t initial = {0};\n\npthread_t tid = initial.t; /* init tid to all zeroes */\n\n\nExample 2: A comparison function for pthread_t values\n\ntypedef union {\n   unsigned char b[sizeof(pthread_t)];\n   pthread_t t;\n} pthcmp_t;\n\nint pthcmp(pthread_t left, pthread_t right)\n{\n  /*\n  * Compare two pthread handles in a way that imposes a repeatable but arbitrary\n  * ordering on them.\n  * I.e. given the same set of pthread_t handles the ordering should be the same\n  * each time but the order has no particular meaning other than that. E.g.\n  * the ordering does not imply the thread start sequence, or any other\n  * relationship between threads.\n  *\n  * Return values are:\n  * 1 : left is greater than right\n  * 0 : left is equal to right\n  * -1 : left is less than right\n  */\n  int i;\n  pthcmp_t L, R;\n  L.t = left;\n  R.t = right;\n  for (i = 0; i < sizeof(pthread_t); i++)\n  {\n    if (L.b[i] > R.b[i])\n      return 1;\n    else if (L.b[i] < R.b[i])\n      return -1;\n  }\n  return 0;\n}\n\nIt has been pointed out that the C99 standard allows for the possibility that\ninteger types also may include padding bits, which could invalidate the above\nmethod. This addition to C99 was specifically included after it was pointed\nout that there was one, presumably not particularly well known, architecture\nthat included a padding bit in it's 32 bit integer type. See section 6.2.6.2\nof both the standard and the rationale, specifically the paragraph starting at\nline 16 on page 43 of the rationale.\n\n\nAn aside\n\nCertain compilers, e.g. gcc and one of the IBM compilers, include a feature\nextention: provided the union contains a member of the same type as the\nobject then the object may be cast to the union itself.\n\nWe could use this feature to speed up the pthrcmp() function from example 2\nabove by casting rather than assigning the pthread_t arguments to the union, e.g.:\n\nint pthcmp(pthread_t left, pthread_t right)\n{\n  /*\n  * Compare two pthread handles in a way that imposes a repeatable but arbitrary\n  * ordering on them.\n  * I.e. given the same set of pthread_t handles the ordering should be the same\n  * each time but the order has no particular meaning other than that. E.g.\n  * the ordering does not imply the thread start sequence, or any other\n  * relationship between threads.\n  *\n  * Return values are:\n  * 1 : left is greater than right\n  * 0 : left is equal to right\n  * -1 : left is less than right\n  */\n  int i;\n  for (i = 0; i < sizeof(pthread_t); i++)\n  {\n    if (((pthcmp_t)left).b[i] > ((pthcmp_t)right).b[i])\n      return 1;\n    else if (((pthcmp_t)left).b[i] < ((pthcmp_t)right).b[i])\n      return -1;\n  }\n  return 0;\n}\n\n\nResult thus far\n\nWe can't remove undefined bits if they are there in pthread_t already, but we have\nattempted to render them inert for comparison and hashing functions by making them\nconsistent through assignment, copy and pass-by-value.\n\nNote: Hashing pthread_t values requires that all pthread_t variables be initialised\nto the same value (usually all zeros) before being assigned a proper thread ID, i.e.\nto ensure that any padding bits are zero, or at least the same value for all\npthread_t. Since all pthread_t values are generated by the library in the first\ninstance this need not be an application-level operation.\n\n\nConclusion\n\nI've attempted to resolve the multiple issues of type opacity and the possible\npresence of undefined bits and bytes in pthread_t values, which prevent\napplications from comparing or hashing pthread handles.\n\nTwo complimentary partial solutions have been proposed, one an application-level\nscheme to handle both scalar and aggregate pthread_t types equally, plus a\ndefinition of pthread_t itself that neutralises padding bits and bytes by\ncoercing semantics out of the compiler to eliminate variations in the values of\npadding bits.\n\nI have not provided any solution to the problem of handling extra values embedded\nin pthread_t, e.g. debugging or trap information that an implementation is entitled\nto include. Therefore none of this replaces the portability and flexibility of API\nfunctions but what functions are needed? The threads standard is unlikely to\ninclude that can be implemented by a combination of existing features and more\ngeneric functions (several references in the threads rationale suggest this.\nTherefore I propose that the following function could replace the several functions\nthat have been suggested in conversations:\n\npthread_t * pthread_normalize(pthread_t * handle);\n\nFor most existing pthreads implementations this function, or macro, would reduce to\na no-op with zero call overhead.\n"
  },
  {
    "path": "deps/w32-pthreads/README.Watcom",
    "content": "Watcom compiler notes\n=====================\n\nStatus\n------\nNot yet usable. Although the library builds under Watcom it\nsubstantially fails the test suite.\n\nThere is a working Wmakefile for wmake for the library build.\n\ninvoke as any of:\nwmake -f Wmakefile clean WC\nwmake -f Wmakefile clean WC-inlined\nwmake -f Wmakefile clean WCE\nwmake -f Wmakefile clean WCE-inlined\n\nThese build pthreadWC.dll and pthreadWCE.dll.\n\nThere is a working Wmakefile for wmake for the test suite.\n\ninvoke as any of:\nwmake -f Wmakefile clean WC\nwmake -f Wmakefile clean WCX\nwmake -f Wmakefile clean WCE\nwmake -f Wmakefile clean WC-bench\nwmake -f Wmakefile clean WCX-bench\nwmake -f Wmakefile clean WCE-bench\n\n\nCurrent known problems\n----------------------\n\nLibrary build:\nThe Watcom compiler uses a different default call convention to MS C or GNU C and so\napplications are not compatible with pthreadVC.dll etc using pre 2003-10-14 versions\nof pthread.h, sched.h, or semaphore.h. The cdecl attribute can be used on exposed\nfunction prototypes to force compatibility with MS C built DLLs.\n\nHowever, there appear to be other incompatibilities. Errno.h, for example, defines\ndifferent values for the standard C and POSIX errors to those defined by the MS C\nerrno.h. It may be that references to Watcom's threads compatible 'errno' do set\nand return translated numbers consistently, but I have not verified this.\n\nWatcom defines errno as a dereferenced pointer returned by the function\n_get_errno_ptr(). This is similar to both the MS and GNU C environments for\nmultithreaded use. However, the Watcom version appears to have a number of problems:\n\n- different threads return the same pointer value. Compare with the MS and GNU C\nversions which correctly return different values (since each thread must maintain\na thread specific errno value).\n\n- an errno value set within the DLL appears as zero in the application even though\nboth share the same thread.\n\nTherefore applications built using the Watcom compiler may need to use\na Watcom built version of the library (pthreadWC.dll). If this is the case, then\nthe cdecl function attribute should not be required.\n\nApplication builds:\nThe test suite fails with the Watcom compiler.\n\nTest semaphore1.c fails for pthreadWC.dll because errno returns 0 instead of EAGAIN.\n"
  },
  {
    "path": "deps/w32-pthreads/README.WinCE",
    "content": "WinCE port\n----------\n(See the file WinCE-PORT for a detailed explanation.)\n\nMake sure you define \"WINCE\" amongst your compiler flags (eg. -DWINCE).\nThe config.h file will define all the necessary defines for you.\n"
  },
  {
    "path": "deps/w32-pthreads/TODO",
    "content": "                   Things that aren't done yet\n                   ---------------------------\n\n1. Implement PTHREAD_PROCESS_SHARED for semaphores, mutexes,\n   condition variables, read/write locks, barriers.\n\n\n"
  },
  {
    "path": "deps/w32-pthreads/WinCE-PORT",
    "content": "NOTE: The comments in this file relate to the original WinCE port\ndone by Tristan Savatier. The semaphore routines have been \ncompletely rewritten since (2005-04-25), having been progressively\nbroken more and more by changes to the library. All of the semaphore\nroutines implemented for W9x/WNT/2000 and up should now also work for\nWinCE. Also, pthread_mutex_timedlock should now work.\n\nAdditional WinCE updates have been applied since this as well. Check the\nChangeLog file and search for WINCE for example. (2007-01-07)\n\n[RPJ]\n\n----\n\nSome interesting news:\n\nI have been able to port pthread-win32 to Windows-CE,\nwhich uses a subset of the WIN32 API.\n\nSince we intend to keep using pthread-win32 for our\nCommercial WinCE developments, I would be very interested\nif WinCE support could be added to the main source tree\nof pthread-win32.  Also, I would like to be credited\nfor this port :-)\n\nNow, here is the story...\n\nThe port was performed and tested on a Casio \"Cassiopeia\"\nPalmSize PC, which runs a MIP processor.  The OS in the\nCasio is WinCE version 2.11, but I used VC++ 6.0 with\nthe WinCE SDK for version 2.01.\n\nI used pthread-win32 to port a heavily multithreaded\ncommercial application (real-time MPEG video player)\nfrom Linux to WinCE.  I consider the changes that\nI have done to be quite well tested.\n\nOverall the modifications that we had to do are minor.\n\nThe WinCE port were based on pthread-win32-snap-1999-05-30,\nbut I am certain that they can be integrated very easiely\nto more recent versions of the source.\n\nI have attached the modified source code:\npthread-win32-snap-1999-05-30-WinCE.\n\nAll the changes do not affect the code compiled on non-WinCE\nenvironment, provided that the macros used for WinCE compilation\nare not used, of course!\n\nOverall description of the WinCE port:\n-------------------------------------\n\nMost of the changes had to be made in areas where\npthread-win32 was relying on some standard-C librairies\n(e.g. _ftime, calloc, errno), which are not available\non WinCE. We have changed the code to use native Win32\nAPI instead (or in some cases we made wrappers).\n\nThe Win32 Semaphores are not available,\nso we had to re-implement Semaphores using mutexes\nand events.\n\nLimitations / known problems of the WinCE port:\n----------------------------------------------\n\nNot all the semaphore routines have been ported\n(semaphores are defined by Posix but are not part\npf pthread).  I have just done enough to make\npthread routines (that rely internally on semaphores)\nwork, like signal conditions.\n\nI noticed that the Win32 threads work slightly\ndifferently on WinCE.  This may have some impact\non some tricky parts of pthread-win32, but I have\nnot really investigated.  For example, on WinCE,\nthe process is killed if the main thread falls off\nthe bottom (or calls pthread_exit), regardless\nof the existence of any other detached thread.\nMicrosoft manual indicates that this behavior is\ndeffirent from that of Windows Threads for other\nWin32 platforms.\n\n\nDetailed descriptions of the changes and rationals:\n\n------------------------------------\n- use a new macro NEED_ERRNO.\n\nIf defined, the code in errno.c that defines a reentrant errno\nis compiled, regardless of _MT and _REENTRANT.\n\nRational: On WinCE, there is no support for <stdio.h>, <errno.h> or\nany other standard C library, i.e. even if _MT or _REENTRANT\nis defined, errno is not provided by any library.  NEED_ERRNO\nmust be set to compile for WinCE.\n\n------------------------------------\n- In implement.h, change #include <semaphore.h> to #include \"semaphore.h\".\n\nRational: semaphore.h is provided in pthread-win32 and should not\nbe searched in the systems standard include.  would not compile.\nThis change does not seem to create problems on \"classic\" win32\n(e.g. win95).\n\n------------------------------------\n- use a new macro NEED_CALLOC.\n\nIf defined, some code in misc.c will provide a replacement\nfor calloc, which is not available on Win32.\n\n\n------------------------------------\n- use a new macro NEED_CREATETHREAD.\n\nIf defined, implement.h defines the macro _beginthreadex\nand _endthreadex.\n\nRational: On WinCE, the wrappers _beginthreadex and _endthreadex\ndo not exist. The native Win32 routines must be used.\n\n------------------------------------\n- in misc.c:\n\n#ifdef NEED_DUPLICATEHANDLE\n\t  /* DuplicateHandle does not exist on WinCE */\n\t  self->threadH = GetCurrentThread();\n#else\n\t  if( !DuplicateHandle(\n\t\t\t       GetCurrentProcess(),\n\t\t\t       GetCurrentThread(),\n\t\t\t       GetCurrentProcess(),\n\t\t\t       &self->threadH,\n\t\t\t       0,\n\t\t\t       FALSE,\n\t\t\t       DUPLICATE_SAME_ACCESS ) )\n\t    {\n\t      free( self );\n\t      return (NULL);\n\t    }\n#endif\n\nRational: On WinCE, DuplicateHandle does not exist.  I could not understand\nwhy DuplicateHandle must be used.  It seems to me that getting the current\nthread handle with GetCurrentThread() is sufficient, and it seems to work\nperfectly fine, so maybe DuplicateHandle was just plain useless to begin with ?\n\n------------------------------------\n- In private.c, added some code at the beginning of ptw32_processInitialize\nto detect the case of multiple calls to ptw32_processInitialize.\n\nRational: In order to debug pthread-win32, it is easier to compile\nit as a regular library (it is not possible to debug DLL's on winCE).\nIn that case, the application must call ptw32_rocessInitialize()\nexplicitely, to initialize pthread-win32.  It is safer in this circumstance\nto handle the case where ptw32_processInitialize() is called on\nan already initialized library:\n\nint\nptw32_processInitialize (void)\n{\n\tif (ptw32_processInitialized) {\n\t\t/* \n\t\t * ignore if already initialized. this is useful for \n\t\t * programs that uses a non-dll pthread\n\t\t * library. such programs must call ptw32_processInitialize() explicitely,\n\t\t * since this initialization routine is automatically called only when\n\t\t * the dll is loaded.\n\t\t */\n\t\treturn TRUE;\n\t}\n    ptw32_processInitialized = TRUE;\n  \t[...]\n}\n\n------------------------------------\n- in private.c, if macro NEED_FTIME is defined, add routines to\nconvert timespec_to_filetime and filetime_to_timespec, and modified\ncode that was using _ftime() to use Win32 API instead.\n\nRational: _ftime is not available on WinCE.  It is necessary to use\nthe native Win32 time API instead.\n\nNote: the routine timespec_to_filetime is provided as a convenience and a mean\nto test that filetime_to_timespec works, but it is not used by the library.\n\n------------------------------------\n- in semaphore.c, if macro NEED_SEM is defined, add code for the routines\n_increase_semaphore and _decrease_semaphore, and modify significantly\nthe implementation of the semaphores so that it does not use CreateSemaphore.\n\nRational: CreateSemaphore is not available on WinCE.  I had to re-implement\nsemaphores using mutexes and Events.\n\nNote: Only the semaphore routines that are used by pthread are implemented\n(i.e. signal conditions rely on a subset of the semaphores routines, and\nthis subset works). Some other semaphore routines (e.g. sem_trywait) are\nnot yet supported on my WinCE port (and since I don't need them, I am not\nplanning to do anything about them).\n\n------------------------------------\n- in tsd.c, changed the code that defines TLS_OUT_OF_INDEXES\n\n/* TLS_OUT_OF_INDEXES not defined on WinCE */\n#ifndef TLS_OUT_OF_INDEXES\n#define TLS_OUT_OF_INDEXES 0xffffffff\n#endif\n\nRational: TLS_OUT_OF_INDEXES is not defined in any standard include file\non WinCE.\n\n------------------------------------\n- added file need_errno.h\n\nRational: On WinCE, there is no errno.h file. need_errno.h is just a\ncopy of windows version of errno.h, with minor modifications due to the fact\nthat some of the error codes are defined by the WinCE socket library.\nIn pthread.h, if NEED_ERRNO is defined, the file need_errno.h is\nincluded (instead of <errno.h>).\n\n\n-- eof\n"
  },
  {
    "path": "deps/w32-pthreads/attr.c",
    "content": "/*\n * attr.c\n *\n * Description:\n * This translation unit agregates operations on thread attribute objects.\n * It is used for inline optimisation.\n *\n * The included modules are used separately when static executable sizes\n * must be minimised.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n#include \"pthread_attr_init.c\"\n#include \"pthread_attr_destroy.c\"\n#include \"pthread_attr_getdetachstate.c\"\n#include \"pthread_attr_setdetachstate.c\"\n#include \"pthread_attr_getstackaddr.c\"\n#include \"pthread_attr_setstackaddr.c\"\n#include \"pthread_attr_getstacksize.c\"\n#include \"pthread_attr_setstacksize.c\"\n#include \"pthread_attr_getscope.c\"\n#include \"pthread_attr_setscope.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/autostatic.c",
    "content": "/*\n * autostatic.c\n *\n * Description:\n * This translation unit implements static auto-init and auto-exit logic.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n *\n *      Contact Email: rpj@callisto.canberra.edu.au\n *\n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n *\n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n *\n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n *\n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#if defined(PTW32_STATIC_LIB)\n\n#if defined(__MINGW64__) || defined(__MINGW32__) || defined(_MSC_VER)\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nstatic void on_process_init(void)\n{\n    pthread_win32_process_attach_np ();\n}\n\nstatic void on_process_exit(void)\n{\n    pthread_win32_thread_detach_np  ();\n    pthread_win32_process_detach_np ();\n}\n\n#if defined(__MINGW64__) || defined(__MINGW32__)\n# define attribute_section(a) __attribute__((section(a)))\n#elif defined(_MSC_VER)\n# define attribute_section(a) __pragma(section(a,long,read)); __declspec(allocate(a))\n#endif\n\nattribute_section(\".ctors\") void *gcc_ctor = on_process_init;\nattribute_section(\".dtors\") void *gcc_dtor = on_process_exit;\n\nattribute_section(\".CRT$XCU\") void *msc_ctor = on_process_init;\nattribute_section(\".CRT$XPU\") void *msc_dtor = on_process_exit;\n\n#endif /* defined(__MINGW64__) || defined(__MINGW32__) || defined(_MSC_VER) */\n\n#endif /* PTW32_STATIC_LIB */\n"
  },
  {
    "path": "deps/w32-pthreads/barrier.c",
    "content": "/*\n * barrier.c\n *\n * Description:\n * This translation unit implements barrier primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n#include \"pthread_barrier_init.c\"\n#include \"pthread_barrier_destroy.c\"\n#include \"pthread_barrier_wait.c\"\n#include \"pthread_barrierattr_init.c\"\n#include \"pthread_barrierattr_destroy.c\"\n#include \"pthread_barrierattr_getpshared.c\"\n#include \"pthread_barrierattr_setpshared.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/builddmc.bat",
    "content": "; Build the pthreads library with the Digital Mars Compiler\n;\nset DMCDIR=c:\\dm\n\n;   RELEASE\n%DMCDIR%\\bin\\dmc -D_WIN32_WINNT -D_MT -DHAVE_PTW32_CONFIG_H -I.;c:\\dm\\include -o+all -WD pthread.c user32.lib+kernel32.lib+wsock32.lib -L/impl -L/NODEBUG -L/SU:WINDOWS\n\n;   DEBUG\n%DMCDIR%\\bin\\dmc -g -D_WIN32_WINNT -D_MT -DHAVE_PTW32_CONFIG_H -I.;c:\\dm\\include -o+all -WD pthread.c user32.lib+kernel32.lib+wsock32.lib -L/impl -L/SU:WINDOWS\n"
  },
  {
    "path": "deps/w32-pthreads/cancel.c",
    "content": "/*\n * cancel.c\n *\n * Description:\n * POSIX thread functions related to thread cancellation.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n#include \"pthread_setcancelstate.c\"\n#include \"pthread_setcanceltype.c\"\n#include \"pthread_testcancel.c\"\n#include \"pthread_cancel.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/cleanup.c",
    "content": "/*\n * cleanup.c\n *\n * Description:\n * This translation unit implements routines associated\n * with cleaning up threads.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n/*\n * The functions ptw32_pop_cleanup and ptw32_push_cleanup\n * are implemented here for applications written in C with no\n * SEH or C++ destructor support. \n */\n\nptw32_cleanup_t *\nptw32_pop_cleanup (int execute)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function pops the most recently pushed cleanup\n      *      handler. If execute is nonzero, then the cleanup handler\n      *      is executed if non-null.\n      *\n      * PARAMETERS\n      *      execute\n      *              if nonzero, execute the cleanup handler\n      *\n      *\n      * DESCRIPTION\n      *      This function pops the most recently pushed cleanup\n      *      handler. If execute is nonzero, then the cleanup handler\n      *      is executed if non-null.\n      *      NOTE: specify 'execute' as nonzero to avoid duplication\n      *                of common cleanup code.\n      *\n      * RESULTS\n      *              N/A\n      *\n      * ------------------------------------------------------\n      */\n{\n  ptw32_cleanup_t *cleanup;\n\n  cleanup = (ptw32_cleanup_t *) pthread_getspecific (ptw32_cleanupKey);\n\n  if (cleanup != NULL)\n    {\n      if (execute && (cleanup->routine != NULL))\n\t{\n\n\t  (*cleanup->routine) (cleanup->arg);\n\n\t}\n\n      pthread_setspecific (ptw32_cleanupKey, (void *) cleanup->prev);\n\n    }\n\n  return (cleanup);\n\n}\t\t\t\t/* ptw32_pop_cleanup */\n\n\nvoid\nptw32_push_cleanup (ptw32_cleanup_t * cleanup,\n\t\t    ptw32_cleanup_callback_t routine, void *arg)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function pushes a new cleanup handler onto the thread's stack\n      *      of cleanup handlers. Each cleanup handler pushed onto the stack is\n      *      popped and invoked with the argument 'arg' when\n      *              a) the thread exits by calling 'pthread_exit',\n      *              b) when the thread acts on a cancellation request,\n      *              c) or when the thread calls pthread_cleanup_pop with a nonzero\n      *                 'execute' argument\n      *\n      * PARAMETERS\n      *      cleanup\n      *              a pointer to an instance of pthread_cleanup_t,\n      *\n      *      routine\n      *              pointer to a cleanup handler,\n      *\n      *      arg\n      *              parameter to be passed to the cleanup handler\n      *\n      *\n      * DESCRIPTION\n      *      This function pushes a new cleanup handler onto the thread's stack\n      *      of cleanup handlers. Each cleanup handler pushed onto the stack is\n      *      popped and invoked with the argument 'arg' when\n      *              a) the thread exits by calling 'pthread_exit',\n      *              b) when the thread acts on a cancellation request,\n      *              c) or when the thrad calls pthread_cleanup_pop with a nonzero\n      *                 'execute' argument\n      *      NOTE: pthread_push_cleanup, ptw32_pop_cleanup must be paired\n      *                in the same lexical scope.\n      *\n      * RESULTS\n      *              pthread_cleanup_t *\n      *                              pointer to the previous cleanup\n      *\n      * ------------------------------------------------------\n      */\n{\n  cleanup->routine = routine;\n  cleanup->arg = arg;\n\n  cleanup->prev = (ptw32_cleanup_t *) pthread_getspecific (ptw32_cleanupKey);\n\n  pthread_setspecific (ptw32_cleanupKey, (void *) cleanup);\n\n}\t\t\t\t/* ptw32_push_cleanup */\n"
  },
  {
    "path": "deps/w32-pthreads/cmake/w32-pthreadsConfig.cmake.in",
    "content": "@PACKAGE_INIT@\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake\")\ncheck_required_components(\"w32-pthreads\")\n"
  },
  {
    "path": "deps/w32-pthreads/cmake/windows/obs-module.rc.in",
    "content": "1 VERSIONINFO\nFILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"040904B0\"\n    BEGIN\n      VALUE \"CompanyName\", \"${OBS_COMPANY_NAME}\"\n      VALUE \"FileDescription\", \"POSIX Threads for Windows\"\n      VALUE \"FileVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"ProductName\", \"${OBS_PRODUCT_NAME}\"\n      VALUE \"ProductVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"Comments\", \"${OBS_COMMENTS}\"\n      VALUE \"LegalCopyright\", \"${OBS_LEGAL_COPYRIGHT}\"\n      VALUE \"InternalName\", \"w32-pthreads\"\n      VALUE \"OriginalFilename\", \"w32-pthreads\"\n    END\n  END\n\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0409, 0x04B0\n  END\nEND\n"
  },
  {
    "path": "deps/w32-pthreads/condvar.c",
    "content": "/*\n * condvar.c\n *\n * Description:\n * This translation unit implements condition variables and their primitives.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n#include \"ptw32_cond_check_need_init.c\"\n#include \"pthread_condattr_init.c\"\n#include \"pthread_condattr_destroy.c\"\n#include \"pthread_condattr_getpshared.c\"\n#include \"pthread_condattr_setpshared.c\"\n#include \"pthread_cond_init.c\"\n#include \"pthread_cond_destroy.c\"\n#include \"pthread_cond_wait.c\"\n#include \"pthread_cond_signal.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/config.h",
    "content": "/* config.h  */\n\n#ifndef PTW32_CONFIG_H\n#define PTW32_CONFIG_H\n\n/*********************************************************************\n * Defaults: see target specific redefinitions below.\n *********************************************************************/\n\n/* We're building the pthreads-win32 library */\n#define PTW32_BUILD\n\n/* Do we know about the C type sigset_t? */\n#undef HAVE_SIGSET_T\n\n/* Define if you have the <signal.h> header file.  */\n#undef HAVE_SIGNAL_H\n\n/* Define if you have the Borland TASM32 or compatible assembler.  */\n#undef HAVE_TASM32\n\n/* Define if you don't have Win32 DuplicateHandle. (eg. WinCE) */\n#undef NEED_DUPLICATEHANDLE\n\n/* Define if you don't have Win32 _beginthreadex. (eg. WinCE) */\n#undef NEED_CREATETHREAD\n\n/* Define if you don't have Win32 errno. (eg. WinCE) */\n#undef NEED_ERRNO\n\n/* Define if you don't have Win32 calloc. (eg. WinCE)  */\n#undef NEED_CALLOC\n\n/* Define if you don't have Win32 ftime. (eg. WinCE)  */\n#undef NEED_FTIME\n\n/* Define if you don't have Win32 semaphores. (eg. WinCE 2.1 or earlier)  */\n#undef NEED_SEM\n\n/* Define if you need to convert string parameters to unicode. (eg. WinCE)  */\n#undef NEED_UNICODE_CONSTS\n\n/* Define if your C (not C++) compiler supports \"inline\" functions. */\n#undef HAVE_C_INLINE\n\n/* Do we know about type mode_t? */\n#undef HAVE_MODE_T\n\n/* \n * Define if GCC has atomic builtins, i.e. __sync_* intrinsics\n * __sync_lock_* is implemented in mingw32 gcc 4.5.2 at least\n * so this define does not turn those on or off. If you get an\n * error from __sync_lock* then consider upgrading your gcc.\n */\n#undef HAVE_GCC_ATOMIC_BUILTINS\n\n/* Define if you have the timespec struct */\n#undef HAVE_STRUCT_TIMESPEC\n\n/* Define if you don't have the GetProcessAffinityMask() */\n#undef NEED_PROCESS_AFFINITY_MASK\n\n/* Define if your version of Windows TLSGetValue() clears WSALastError\n * and calling SetLastError() isn't enough restore it. You'll also need to\n * link against wsock32.lib (or libwsock32.a for MinGW).\n */\n#undef RETAIN_WSALASTERROR\n\n/*\n# ----------------------------------------------------------------------\n# The library can be built with some alternative behaviour to better\n# facilitate development of applications on Win32 that will be ported\n# to other POSIX systems.\n#\n# Nothing described here will make the library non-compliant and strictly\n# compliant applications will not be affected in any way, but\n# applications that make assumptions that POSIX does not guarantee are\n# not strictly compliant and may fail or misbehave with some settings.\n#\n# PTW32_THREAD_ID_REUSE_INCREMENT\n# Purpose:\n# POSIX says that applications should assume that thread IDs can be\n# recycled. However, Solaris (and some other systems) use a [very large]\n# sequence number as the thread ID, which provides virtual uniqueness.\n# This provides a very high but finite level of safety for applications\n# that are not meticulous in tracking thread lifecycles e.g. applications\n# that call functions which target detached threads without some form of\n# thread exit synchronisation.\n#\n# Usage:\n# Set to any value in the range: 0 <= value < 2^wordsize.\n# Set to 0 to emulate reusable thread ID behaviour like Linux or *BSD.\n# Set to 1 for unique thread IDs like Solaris (this is the default).\n# Set to some factor of 2^wordsize to emulate smaller word size types\n# (i.e. will wrap sooner). This might be useful to emulate some embedded\n# systems.\n#\n# define PTW32_THREAD_ID_REUSE_INCREMENT 0\n#\n# ----------------------------------------------------------------------\n */\n#undef PTW32_THREAD_ID_REUSE_INCREMENT\n\n\n/*********************************************************************\n * Target specific groups\n *\n * If you find that these are incorrect or incomplete please report it\n * to the pthreads-win32 maintainer. Thanks.\n *********************************************************************/\n#if defined(WINCE)\n#define NEED_DUPLICATEHANDLE\n#define NEED_CREATETHREAD\n#define NEED_ERRNO\n#define NEED_CALLOC\n#define NEED_FTIME\n/* #define NEED_SEM */\n#define NEED_UNICODE_CONSTS\n#define NEED_PROCESS_AFFINITY_MASK\n/* This may not be needed */\n#define RETAIN_WSALASTERROR\n#endif\n\n#if defined(_UWIN)\n#define HAVE_MODE_T\n#define HAVE_STRUCT_TIMESPEC\n#endif\n\n#if defined(__GNUC__)\n#define HAVE_C_INLINE\n#endif\n\n#if defined(__MINGW64__)\n#define HAVE_MODE_T\n#define HAVE_STRUCT_TIMESPEC\n#elif defined(__MINGW32__)\n#define HAVE_MODE_T\n#endif\n\n#if defined(__BORLANDC__)\n#endif\n\n#if defined(__WATCOMC__)\n#endif\n\n#if defined(__DMC__)\n#define HAVE_SIGNAL_H\n#define HAVE_C_INLINE\n#endif\n\n\n\n#endif\n"
  },
  {
    "path": "deps/w32-pthreads/context.h",
    "content": "/*\n * context.h\n *\n * Description:\n * POSIX thread macros related to thread cancellation.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#ifndef PTW32_CONTEXT_H\n#define PTW32_CONTEXT_H\n\n#undef PTW32_PROGCTR\n\n#if defined(_M_IX86) || (defined(_X86_) && !defined(__amd64__))\n#define PTW32_PROGCTR(Context)  ((Context).Eip)\n#endif\n\n#if defined (_M_IA64) || defined(_IA64)\n#define PTW32_PROGCTR(Context)  ((Context).StIIP)\n#endif\n\n#if defined(_MIPS_) || defined(MIPS)\n#define PTW32_PROGCTR(Context)  ((Context).Fir)\n#endif\n\n#if defined(_ALPHA_)\n#define PTW32_PROGCTR(Context)  ((Context).Fir)\n#endif\n\n#if defined(_PPC_)\n#define PTW32_PROGCTR(Context)  ((Context).Iar)\n#endif\n\n#if defined(_AMD64_) || defined(__amd64__)\n#define PTW32_PROGCTR(Context)  ((Context).Rip)\n#endif\n\n#if defined(_ARM_) || defined(ARM) || defined(_M_ARM) || defined(_M_ARM64)\n#define PTW32_PROGCTR(Context)  ((Context).Pc)\n#endif\n\n#if !defined(PTW32_PROGCTR)\n#error Module contains CPU-specific code; modify and recompile.\n#endif\n\n#endif\n"
  },
  {
    "path": "deps/w32-pthreads/create.c",
    "content": "/*\n * create.c\n *\n * Description:\n * This translation unit implements routines associated with spawning a new\n * thread.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#if ! defined(_UWIN) && ! defined(WINCE)\n#include <process.h>\n#endif\n\nint\npthread_create (pthread_t * tid,\n\t\tconst pthread_attr_t * attr,\n\t\tvoid *(PTW32_CDECL *start) (void *), void *arg)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function creates a thread running the start function,\n      *      passing it the parameter value, 'arg'. The 'attr'\n      *      argument specifies optional creation attributes.\n      *      The identity of the new thread is returned\n      *      via 'tid', which should not be NULL.\n      *\n      * PARAMETERS\n      *      tid\n      *              pointer to an instance of pthread_t\n      *\n      *      attr\n      *              optional pointer to an instance of pthread_attr_t\n      *\n      *      start\n      *              pointer to the starting routine for the new thread\n      *\n      *      arg\n      *              optional parameter passed to 'start'\n      *\n      *\n      * DESCRIPTION\n      *      This function creates a thread running the start function,\n      *      passing it the parameter value, 'arg'. The 'attr'\n      *      argument specifies optional creation attributes.\n      *      The identity of the new thread is returned\n      *      via 'tid', which should not be the NULL pointer.\n      *\n      * RESULTS\n      *              0               successfully created thread,\n      *              EINVAL          attr invalid,\n      *              EAGAIN          insufficient resources.\n      *\n      * ------------------------------------------------------\n      */\n{\n  pthread_t thread;\n  ptw32_thread_t * tp;\n  register pthread_attr_t a;\n  HANDLE threadH = 0;\n  int result = EAGAIN;\n  int run = PTW32_TRUE;\n  ThreadParms *parms = NULL;\n  unsigned int stackSize;\n  int priority;\n  pthread_t self;\n\n  /*\n   * Before doing anything, check that tid can be stored through\n   * without invoking a memory protection error (segfault).\n   * Make sure that the assignment below can't be optimised out by the compiler.\n   * This is assured by conditionally assigning *tid again at the end.\n   */\n  tid->x = 0;\n\n  if (attr != NULL)\n    {\n      a = *attr;\n    }\n  else\n    {\n      a = NULL;\n    }\n\n  if ((thread = ptw32_new ()).p == NULL)\n    {\n      goto FAIL0;\n    }\n\n  tp = (ptw32_thread_t *) thread.p;\n\n  priority = tp->sched_priority;\n\n  if ((parms = (ThreadParms *) malloc (sizeof (*parms))) == NULL)\n    {\n      goto FAIL0;\n    }\n\n  parms->tid = thread;\n  parms->start = start;\n  parms->arg = arg;\n\n#if defined(HAVE_SIGSET_T)\n\n  /*\n   * Threads inherit their initial sigmask from their creator thread.\n   */\n  self = pthread_self();\n  tp->sigmask = ((ptw32_thread_t *)self.p)->sigmask;\n\n#endif /* HAVE_SIGSET_T */\n\n\n  if (a != NULL)\n    {\n      stackSize = (unsigned int)a->stacksize;\n      tp->detachState = a->detachstate;\n      priority = a->param.sched_priority;\n\n#if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL)\n      /* WinCE */\n#else\n      /* Everything else */\n\n      /*\n       * Thread priority must be set to a valid system level\n       * without altering the value set by pthread_attr_setschedparam().\n       */\n\n      /*\n       * PTHREAD_EXPLICIT_SCHED is the default because Win32 threads\n       * don't inherit their creator's priority. They are started with\n       * THREAD_PRIORITY_NORMAL (win32 value). The result of not supplying\n       * an 'attr' arg to pthread_create() is equivalent to defaulting to\n       * PTHREAD_EXPLICIT_SCHED and priority THREAD_PRIORITY_NORMAL.\n       */\n      if (PTHREAD_INHERIT_SCHED == a->inheritsched)\n\t{\n\t  /*\n\t   * If the thread that called pthread_create() is a Win32 thread\n\t   * then the inherited priority could be the result of a temporary\n\t   * system adjustment. This is not the case for POSIX threads.\n\t   */\n#if ! defined(HAVE_SIGSET_T)\n\t  self = pthread_self ();\n#endif\n\t  priority = ((ptw32_thread_t *) self.p)->sched_priority;\n\t}\n\n#endif\n\n    }\n  else\n    {\n      /*\n       * Default stackSize\n       */\n      stackSize = PTHREAD_STACK_MIN;\n    }\n\n  tp->state = run ? PThreadStateInitial : PThreadStateSuspended;\n\n  tp->keys = NULL;\n\n  /*\n   * Threads must be started in suspended mode and resumed if necessary\n   * after _beginthreadex returns us the handle. Otherwise we set up a\n   * race condition between the creating and the created threads.\n   * Note that we also retain a local copy of the handle for use\n   * by us in case thread.p->threadH gets NULLed later but before we've\n   * finished with it here.\n   */\n\n#if ! (defined (__MINGW64__) || defined(__MINGW32__)) || defined (__MSVCRT__) || defined (__DMC__) \n\n  tp->threadH =\n    threadH =\n    (HANDLE) _beginthreadex ((void *) NULL,\t/* No security info             */\n\t\t\t     stackSize,\t\t/* default stack size   */\n\t\t\t     ptw32_threadStart,\n\t\t\t     parms,\n\t\t\t     (unsigned)\n\t\t\t     CREATE_SUSPENDED,\n\t\t\t     (unsigned *) &(tp->thread));\n\n  if (threadH != 0)\n    {\n      if (a != NULL)\n\t{\n\t  (void) ptw32_setthreadpriority (thread, SCHED_OTHER, priority);\n\t}\n\n      if (run)\n\t{\n\t  ResumeThread (threadH);\n\t}\n    }\n\n#else\n\n  {\n    ptw32_mcs_local_node_t stateLock;\n\n    /*\n     * This lock will force pthread_threadStart() to wait until we have\n     * the thread handle and have set the priority.\n     */\n    ptw32_mcs_lock_acquire(&tp->stateLock, &stateLock);\n\n    tp->threadH =\n      threadH =\n      (HANDLE) _beginthread (ptw32_threadStart, stackSize,\t/* default stack size   */\n\t\t\t     parms);\n\n    /*\n     * Make the return code match _beginthreadex's.\n     */\n    if (threadH == (HANDLE) - 1L)\n      {\n        tp->threadH = threadH = 0;\n      }\n    else\n      {\n        if (!run)\n\t  {\n\t    /* \n\t     * beginthread does not allow for create flags, so we do it now.\n\t     * Note that beginthread itself creates the thread in SUSPENDED\n\t     * mode, and then calls ResumeThread to start it.\n\t     */\n\t    SuspendThread (threadH);\n\t  }\n  \n        if (a != NULL)\n\t  {\n\t    (void) ptw32_setthreadpriority (thread, SCHED_OTHER, priority);\n\t  }\n      }\n\n    ptw32_mcs_lock_release (&stateLock);\n  }\n#endif\n\n  result = (threadH != 0) ? 0 : EAGAIN;\n\n  /*\n   * Fall Through Intentionally\n   */\n\n  /*\n   * ------------\n   * Failure Code\n   * ------------\n   */\n\nFAIL0:\n  if (result != 0)\n    {\n\n      ptw32_threadDestroy (thread);\n      tp = NULL;\n\n      if (parms != NULL)\n\t{\n\t  free (parms);\n\t}\n    }\n  else\n    {\n      *tid = thread;\n    }\n\n#if defined(_UWIN)\n  if (result == 0)\n    pthread_count++;\n#endif\n  return (result);\n\n}\t\t\t\t/* pthread_create */\n"
  },
  {
    "path": "deps/w32-pthreads/dll.c",
    "content": "/*\n * dll.c\n *\n * Description:\n * This translation unit implements DLL initialisation.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#if !defined(PTW32_STATIC_LIB)\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n#if defined(_MSC_VER)\n/* \n * lpvReserved yields an unreferenced formal parameter;\n * ignore it\n */\n#pragma warning( disable : 4100 )\n#endif\n\n#if defined(__cplusplus)\n/*\n * Dear c++: Please don't mangle this name. -thanks\n */\nextern \"C\"\n#endif\t\t\t\t/* __cplusplus */\n  BOOL WINAPI\nDllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved)\n{\n  BOOL result = PTW32_TRUE;\n\n  switch (fdwReason)\n    {\n\n    case DLL_PROCESS_ATTACH:\n      result = pthread_win32_process_attach_np ();\n      break;\n\n    case DLL_THREAD_ATTACH:\n      /*\n       * A thread is being created\n       */\n      result = pthread_win32_thread_attach_np ();\n      break;\n\n    case DLL_THREAD_DETACH:\n      /*\n       * A thread is exiting cleanly\n       */\n      result = pthread_win32_thread_detach_np ();\n      break;\n\n    case DLL_PROCESS_DETACH:\n      (void) pthread_win32_thread_detach_np ();\n      result = pthread_win32_process_detach_np ();\n      break;\n    }\n\n  return (result);\n\n}\t\t\t\t/* DllMain */\n\n#endif /* PTW32_STATIC_LIB */\n"
  },
  {
    "path": "deps/w32-pthreads/errno.c",
    "content": "/*\n * errno.c\n *\n * Description:\n * This translation unit implements routines associated with spawning a new\n * thread.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#if defined(NEED_ERRNO)\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nstatic int reallyBad = ENOMEM;\n\n/*\n * Re-entrant errno.\n *\n * Each thread has it's own errno variable in pthread_t.\n *\n * The benefit of using the pthread_t structure\n * instead of another TSD key is TSD keys are limited\n * on Win32 to 64 per process. Secondly, to implement\n * it properly without using pthread_t you'd need\n * to dynamically allocate an int on starting the thread\n * and store it manually into TLS and then ensure that you free\n * it on thread termination. We get all that for free\n * by simply storing the errno on the pthread_t structure.\n *\n * MSVC and Mingw32 already have their own thread-safe errno.\n *\n * #if defined( _REENTRANT ) || defined( _MT )\n * #define errno *_errno()\n *\n * int *_errno( void );\n * #else\n * extern int errno;\n * #endif\n *\n */\n\nint *\n_errno (void)\n{\n  pthread_t self;\n  int *result;\n\n  if ((self = pthread_self ()).p == NULL)\n    {\n      /*\n       * Yikes! unable to allocate a thread!\n       * Throw an exception? return an error?\n       */\n      result = &reallyBad;\n    }\n  else\n    {\n      result = (int *)(&self.p->exitStatus);\n    }\n\n  return (result);\n\n}\t\t\t\t/* _errno */\n\n#endif /* (NEED_ERRNO) */\n"
  },
  {
    "path": "deps/w32-pthreads/exit.c",
    "content": "/*\n * exit.c\n *\n * Description:\n * This translation unit implements routines associated with exiting from\n * a thread.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#if ! defined(_UWIN) && ! defined(WINCE)\n#   include <process.h>\n#endif\n\n#include \"pthread_exit.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/fork.c",
    "content": "/*\n * fork.c\n *\n * Description:\n * Implementation of fork() for POSIX threads.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n\n#include \"pthread.h\"\n#include \"implement.h\"\n"
  },
  {
    "path": "deps/w32-pthreads/global.c",
    "content": "/*\n * global.c\n *\n * Description:\n * This translation unit instantiates data associated with the implementation\n * as a whole.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint ptw32_processInitialized = PTW32_FALSE;\nptw32_thread_t * ptw32_threadReuseTop = PTW32_THREAD_REUSE_EMPTY;\nptw32_thread_t * ptw32_threadReuseBottom = PTW32_THREAD_REUSE_EMPTY;\npthread_key_t ptw32_selfThreadKey = NULL;\npthread_key_t ptw32_cleanupKey = NULL;\npthread_cond_t ptw32_cond_list_head = NULL;\npthread_cond_t ptw32_cond_list_tail = NULL;\n\nint ptw32_concurrency = 0;\n\n/* What features have been auto-detected */\nint ptw32_features = 0;\n\n/*\n * Global [process wide] thread sequence Number\n */\nunsigned __int64 ptw32_threadSeqNumber = 0;\n\n/* \n * Function pointer to QueueUserAPCEx if it exists, otherwise\n * it will be set at runtime to a substitute routine which cannot unblock\n * blocked threads.\n */\nDWORD (*ptw32_register_cancelation) (PAPCFUNC, HANDLE, DWORD) = NULL;\n\n/*\n * Global lock for managing pthread_t struct reuse.\n */\nptw32_mcs_lock_t ptw32_thread_reuse_lock = 0;\n\n/*\n * Global lock for testing internal state of statically declared mutexes.\n */\nptw32_mcs_lock_t ptw32_mutex_test_init_lock = 0;\n\n/*\n * Global lock for testing internal state of PTHREAD_COND_INITIALIZER\n * created condition variables.\n */\nptw32_mcs_lock_t ptw32_cond_test_init_lock = 0;\n\n/*\n * Global lock for testing internal state of PTHREAD_RWLOCK_INITIALIZER\n * created read/write locks.\n */\nptw32_mcs_lock_t ptw32_rwlock_test_init_lock = 0;\n\n/*\n * Global lock for testing internal state of PTHREAD_SPINLOCK_INITIALIZER\n * created spin locks.\n */\nptw32_mcs_lock_t ptw32_spinlock_test_init_lock = 0;\n\n/*\n * Global lock for condition variable linked list. The list exists\n * to wake up CVs when a WM_TIMECHANGE message arrives. See\n * w32_TimeChangeHandler.c.\n */\nptw32_mcs_lock_t ptw32_cond_list_lock = 0;\n\n#if defined(_UWIN)\n/*\n * Keep a count of the number of threads.\n */\nint pthread_count = 0;\n#endif\n"
  },
  {
    "path": "deps/w32-pthreads/implement.h",
    "content": "/*\n * implement.h\n *\n * Definitions that don't need to be public.\n *\n * Keeps all the internals out of pthread.h\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: Ross.Johnson@homemail.com.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#if !defined(_IMPLEMENT_H)\n#define _IMPLEMENT_H\n\n#if !defined(_WIN32_WINNT)\n#define _WIN32_WINNT 0x0400\n#endif\n\n#include <windows.h>\n\n/*\n * In case windows.h doesn't define it (e.g. WinCE perhaps)\n */\n#if defined(WINCE)\ntypedef VOID (APIENTRY *PAPCFUNC)(DWORD dwParam);\n#endif\n\n/*\n * note: ETIMEDOUT is correctly defined in winsock.h\n */\n#include <winsock.h>\n\n/*\n * In case ETIMEDOUT hasn't been defined above somehow.\n */\n#if !defined(ETIMEDOUT)\n#  define ETIMEDOUT 10060\t/* This is the value in winsock.h. */\n#endif\n\n#if !defined(malloc)\n#include <malloc.h>\n#endif\n\n#if defined(__CLEANUP_C)\n# include <setjmp.h>\n#endif\n\n#if !defined(INT_MAX)\n#include <limits.h>\n#endif\n\n/* use local include files during development */\n#include \"semaphore.h\"\n#include \"sched.h\"\n\n#if defined(HAVE_C_INLINE) || defined(__cplusplus)\n#define INLINE inline\n#else\n#define INLINE\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER < 1300\n/*\n * MSVC 6 does not use the \"volatile\" qualifier\n */\n#define PTW32_INTERLOCKED_VOLATILE\n#else\n#define PTW32_INTERLOCKED_VOLATILE volatile\n#endif\n#define PTW32_INTERLOCKED_LONG long\n#define PTW32_INTERLOCKED_SIZE size_t\n#define PTW32_INTERLOCKED_PVOID PVOID\n#define PTW32_INTERLOCKED_LONGPTR PTW32_INTERLOCKED_VOLATILE long*\n#define PTW32_INTERLOCKED_SIZEPTR PTW32_INTERLOCKED_VOLATILE size_t*\n#define PTW32_INTERLOCKED_PVOID_PTR PTW32_INTERLOCKED_VOLATILE PVOID*\n\n#if defined(__MINGW64__) || defined(__MINGW32__)\n#  include <stdint.h>\n#elif defined(__BORLANDC__)\n#  define int64_t ULONGLONG\n#else\n#  define int64_t _int64\n#  if defined(_MSC_VER) && _MSC_VER < 1300\n     typedef long intptr_t;\n#  endif\n#endif\n\ntypedef enum\n{\n  /*\n   * This enumeration represents the state of the thread;\n   * The thread is still \"alive\" if the numeric value of the\n   * state is greater or equal \"PThreadStateRunning\".\n   */\n  PThreadStateInitial = 0,\t/* Thread not running                   */\n  PThreadStateRunning,\t\t/* Thread alive & kicking               */\n  PThreadStateSuspended,\t/* Thread alive but suspended           */\n  PThreadStateCancelPending,\t/* Thread alive but                     */\n                                /* has cancelation pending.             */\n  PThreadStateCanceling,\t/* Thread alive but is                  */\n                                /* in the process of terminating        */\n                                /* due to a cancellation request        */\n  PThreadStateExiting,\t\t/* Thread alive but exiting             */\n                                /* due to an exception                  */\n  PThreadStateLast,             /* All handlers have been run and now   */\n                                /* final cleanup can be done.           */\n  PThreadStateReuse             /* In reuse pool.                       */\n}\nPThreadState;\n\ntypedef struct ptw32_mcs_node_t_     ptw32_mcs_local_node_t;\ntypedef struct ptw32_mcs_node_t_*    ptw32_mcs_lock_t;\ntypedef struct ptw32_robust_node_t_  ptw32_robust_node_t;\ntypedef struct ptw32_thread_t_       ptw32_thread_t;\n\n\nstruct ptw32_thread_t_\n{\n  unsigned __int64 seqNumber;\t/* Process-unique thread sequence number */\n  HANDLE threadH;\t\t/* Win32 thread handle - POSIX thread is invalid if threadH == 0 */\n  pthread_t ptHandle;\t\t/* This thread's permanent pthread_t handle */\n  ptw32_thread_t * prevReuse;\t/* Links threads on reuse stack */\n  volatile PThreadState state;\n  ptw32_mcs_lock_t threadLock;\t/* Used for serialised access to public thread state */\n  ptw32_mcs_lock_t stateLock;\t/* Used for async-cancel safety */\n  HANDLE cancelEvent;\n  void *exitStatus;\n  void *parms;\n  void *keys;\n  void *nextAssoc;\n#if defined(__CLEANUP_C)\n  jmp_buf start_mark;\t\t/* Jump buffer follows void* so should be aligned */\n#endif\t\t\t\t/* __CLEANUP_C */\n#if defined(HAVE_SIGSET_T)\n  sigset_t sigmask;\n#endif\t\t\t\t/* HAVE_SIGSET_T */\n  ptw32_mcs_lock_t\n              robustMxListLock; /* robustMxList lock */\n  ptw32_robust_node_t*\n                  robustMxList; /* List of currenty held robust mutexes */\n  int ptErrno;\n  int detachState;\n  int sched_priority;\t\t/* As set, not as currently is */\n  int cancelState;\n  int cancelType;\n  int implicit:1;\n  DWORD thread;\t\t\t/* Win32 thread ID */\n#if defined(_UWIN)\n  DWORD dummy[5];\n#endif\n  size_t align;\t\t\t/* Force alignment if this struct is packed */\n};\n\n\n/* \n * Special value to mark attribute objects as valid.\n */\n#define PTW32_ATTR_VALID ((unsigned long) 0xC4C0FFEE)\n\nstruct pthread_attr_t_\n{\n  unsigned long valid;\n  void *stackaddr;\n  size_t stacksize;\n  int detachstate;\n  struct sched_param param;\n  int inheritsched;\n  int contentionscope;\n#if defined(HAVE_SIGSET_T)\n  sigset_t sigmask;\n#endif\t\t\t\t/* HAVE_SIGSET_T */\n};\n\n\n/*\n * ====================\n * ====================\n * Semaphores, Mutexes and Condition Variables\n * ====================\n * ====================\n */\n\nstruct sem_t_\n{\n  int value;\n  pthread_mutex_t lock;\n  HANDLE sem;\n#if defined(NEED_SEM)\n  int leftToUnblock;\n#endif\n};\n\n#define PTW32_OBJECT_AUTO_INIT ((void *)(size_t) -1)\n#define PTW32_OBJECT_INVALID   NULL\n\nstruct pthread_mutex_t_\n{\n  LONG lock_idx;\t\t/* Provides exclusive access to mutex state\n\t\t\t\t   via the Interlocked* mechanism.\n\t\t\t\t    0: unlocked/free.\n\t\t\t\t    1: locked - no other waiters.\n\t\t\t\t   -1: locked - with possible other waiters.\n\t\t\t\t*/\n  int recursive_count;\t\t/* Number of unlocks a thread needs to perform\n\t\t\t\t   before the lock is released (recursive\n\t\t\t\t   mutexes only). */\n  int kind;\t\t\t/* Mutex type. */\n  pthread_t ownerThread;\n  HANDLE event;\t\t\t/* Mutex release notification to waiting\n\t\t\t\t   threads. */\n  ptw32_robust_node_t*\n                    robustNode; /* Extra state for robust mutexes  */\n};\n\nenum ptw32_robust_state_t_\n{\n  PTW32_ROBUST_CONSISTENT,\n  PTW32_ROBUST_INCONSISTENT,\n  PTW32_ROBUST_NOTRECOVERABLE\n};\n\ntypedef enum ptw32_robust_state_t_   ptw32_robust_state_t;\n\n/*\n * Node used to manage per-thread lists of currently-held robust mutexes.\n */\nstruct ptw32_robust_node_t_\n{\n  pthread_mutex_t mx;\n  ptw32_robust_state_t stateInconsistent;\n  ptw32_robust_node_t* prev;\n  ptw32_robust_node_t* next;\n};\n\nstruct pthread_mutexattr_t_\n{\n  int pshared;\n  int kind;\n  int robustness;\n};\n\n/*\n * Possible values, other than PTW32_OBJECT_INVALID,\n * for the \"interlock\" element in a spinlock.\n *\n * In this implementation, when a spinlock is initialised,\n * the number of cpus available to the process is checked.\n * If there is only one cpu then \"interlock\" is set equal to\n * PTW32_SPIN_USE_MUTEX and u.mutex is an initialised mutex.\n * If the number of cpus is greater than 1 then \"interlock\"\n * is set equal to PTW32_SPIN_UNLOCKED and the number is\n * stored in u.cpus. This arrangement allows the spinlock\n * routines to attempt an InterlockedCompareExchange on \"interlock\"\n * immediately and, if that fails, to try the inferior mutex.\n *\n * \"u.cpus\" isn't used for anything yet, but could be used at\n * some point to optimise spinlock behaviour.\n */\n#define PTW32_SPIN_INVALID     (0)\n#define PTW32_SPIN_UNLOCKED    (1)\n#define PTW32_SPIN_LOCKED      (2)\n#define PTW32_SPIN_USE_MUTEX   (3)\n\nstruct pthread_spinlock_t_\n{\n  long interlock;\t\t/* Locking element for multi-cpus. */\n  union\n  {\n    int cpus;\t\t\t/* No. of cpus if multi cpus, or   */\n    pthread_mutex_t mutex;\t/* mutex if single cpu.            */\n  } u;\n};\n\n/*\n * MCS lock queue node - see ptw32_MCS_lock.c\n */\nstruct ptw32_mcs_node_t_\n{\n  struct ptw32_mcs_node_t_ **lock;        /* ptr to tail of queue */\n  struct ptw32_mcs_node_t_  *next;        /* ptr to successor in queue */\n  HANDLE                     readyFlag;   /* set after lock is released by\n                                             predecessor */\n  HANDLE                     nextFlag;    /* set after 'next' ptr is set by\n                                             successor */\n};\n\n\nstruct pthread_barrier_t_\n{\n  unsigned int nCurrentBarrierHeight;\n  unsigned int nInitialBarrierHeight;\n  int pshared;\n  sem_t semBarrierBreeched;\n  ptw32_mcs_lock_t lock;\n  ptw32_mcs_local_node_t proxynode;\n};\n\nstruct pthread_barrierattr_t_\n{\n  int pshared;\n};\n\nstruct pthread_key_t_\n{\n  DWORD key;\n  void (PTW32_CDECL *destructor) (void *);\n  ptw32_mcs_lock_t keyLock;\n  void *threads;\n};\n\n\ntypedef struct ThreadParms ThreadParms;\n\nstruct ThreadParms\n{\n  pthread_t tid;\n  void *(PTW32_CDECL *start) (void *);\n  void *arg;\n};\n\n\nstruct pthread_cond_t_\n{\n  long nWaitersBlocked;\t\t/* Number of threads blocked            */\n  long nWaitersGone;\t\t/* Number of threads timed out          */\n  long nWaitersToUnblock;\t/* Number of threads to unblock         */\n  sem_t semBlockQueue;\t\t/* Queue up threads waiting for the     */\n  /*   condition to become signalled      */\n  sem_t semBlockLock;\t\t/* Semaphore that guards access to      */\n  /* | waiters blocked count/block queue  */\n  /* +-> Mandatory Sync.LEVEL-1           */\n  pthread_mutex_t mtxUnblockLock;\t/* Mutex that guards access to          */\n  /* | waiters (to)unblock(ed) counts     */\n  /* +-> Optional* Sync.LEVEL-2           */\n  pthread_cond_t next;\t\t/* Doubly linked list                   */\n  pthread_cond_t prev;\n};\n\n\nstruct pthread_condattr_t_\n{\n  int pshared;\n};\n\n#define PTW32_RWLOCK_MAGIC 0xfacade2\n\nstruct pthread_rwlock_t_\n{\n  pthread_mutex_t mtxExclusiveAccess;\n  pthread_mutex_t mtxSharedAccessCompleted;\n  pthread_cond_t cndSharedAccessCompleted;\n  int nSharedAccessCount;\n  int nExclusiveAccessCount;\n  int nCompletedSharedAccessCount;\n  int nMagic;\n};\n\nstruct pthread_rwlockattr_t_\n{\n  int pshared;\n};\n\ntypedef struct ThreadKeyAssoc ThreadKeyAssoc;\n\nstruct ThreadKeyAssoc\n{\n  /*\n   * Purpose:\n   *      This structure creates an association between a thread and a key.\n   *      It is used to implement the implicit invocation of a user defined\n   *      destroy routine for thread specific data registered by a user upon\n   *      exiting a thread.\n   *\n   *      Graphically, the arrangement is as follows, where:\n   *\n   *         K - Key with destructor\n   *            (head of chain is key->threads)\n   *         T - Thread that has called pthread_setspecific(Kn)\n   *            (head of chain is thread->keys)\n   *         A - Association. Each association is a node at the\n   *             intersection of two doubly-linked lists.\n   *\n   *                 T1    T2    T3\n   *                 |     |     |\n   *                 |     |     |\n   *         K1 -----+-----A-----A----->\n   *                 |     |     |\n   *                 |     |     |\n   *         K2 -----A-----A-----+----->\n   *                 |     |     |\n   *                 |     |     |\n   *         K3 -----A-----+-----A----->\n   *                 |     |     |\n   *                 |     |     |\n   *                 V     V     V\n   *\n   *      Access to the association is guarded by two locks: the key's\n   *      general lock (guarding the row) and the thread's general\n   *      lock (guarding the column). This avoids the need for a\n   *      dedicated lock for each association, which not only consumes\n   *      more handles but requires that the lock resources persist\n   *      until both the key is deleted and the thread has called the\n   *      destructor. The two-lock arrangement allows those resources\n   *      to be freed as soon as either thread or key is concluded.\n   *\n   *      To avoid deadlock, whenever both locks are required both the\n   *      key and thread locks are acquired consistently in the order\n   *      \"key lock then thread lock\". An exception to this exists\n   *      when a thread calls the destructors, however, this is done\n   *      carefully (but inelegantly) to avoid deadlock.\n   *\n   *      An association is created when a thread first calls\n   *      pthread_setspecific() on a key that has a specified\n   *      destructor.\n   *\n   *      An association is destroyed either immediately after the\n   *      thread calls the key destructor function on thread exit, or\n   *      when the key is deleted.\n   *\n   * Attributes:\n   *      thread\n   *              reference to the thread that owns the\n   *              association. This is actually the pointer to the\n   *              thread struct itself. Since the association is\n   *              destroyed before the thread exits, this can never\n   *              point to a different logical thread to the one that\n   *              created the assoc, i.e. after thread struct reuse.\n   *\n   *      key\n   *              reference to the key that owns the association.\n   *\n   *      nextKey\n   *              The pthread_t->keys attribute is the head of a\n   *              chain of associations that runs through the nextKey\n   *              link. This chain provides the 1 to many relationship\n   *              between a pthread_t and all pthread_key_t on which\n   *              it called pthread_setspecific.\n   *\n   *      prevKey\n   *              Similarly.\n   *\n   *      nextThread\n   *              The pthread_key_t->threads attribute is the head of\n   *              a chain of associations that runs through the\n   *              nextThreads link. This chain provides the 1 to many\n   *              relationship between a pthread_key_t and all the \n   *              PThreads that have called pthread_setspecific for\n   *              this pthread_key_t.\n   *\n   *      prevThread\n   *              Similarly.\n   *\n   * Notes:\n   *      1)      As soon as either the key or the thread is no longer\n   *              referencing the association, it can be destroyed. The\n   *              association will be removed from both chains.\n   *\n   *      2)      Under WIN32, an association is only created by\n   *              pthread_setspecific if the user provided a\n   *              destroyRoutine when they created the key.\n   *\n   *\n   */\n  ptw32_thread_t * thread;\n  pthread_key_t key;\n  ThreadKeyAssoc *nextKey;\n  ThreadKeyAssoc *nextThread;\n  ThreadKeyAssoc *prevKey;\n  ThreadKeyAssoc *prevThread;\n};\n\n\n#if defined(__CLEANUP_SEH)\n/*\n * --------------------------------------------------------------\n * MAKE_SOFTWARE_EXCEPTION\n *      This macro constructs a software exception code following\n *      the same format as the standard Win32 error codes as defined\n *      in WINERROR.H\n *  Values are 32 bit values laid out as follows:\n *\n *   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0\n *  +---+-+-+-----------------------+-------------------------------+\n *  |Sev|C|R|     Facility          |               Code            |\n *  +---+-+-+-----------------------+-------------------------------+\n *\n * Severity Values:\n */\n#define SE_SUCCESS              0x00\n#define SE_INFORMATION          0x01\n#define SE_WARNING              0x02\n#define SE_ERROR                0x03\n\n#define MAKE_SOFTWARE_EXCEPTION( _severity, _facility, _exception ) \\\n( (DWORD) ( ( (_severity) << 30 ) |     /* Severity code        */ \\\n            ( 1 << 29 ) |               /* MS=0, User=1         */ \\\n            ( 0 << 28 ) |               /* Reserved             */ \\\n            ( (_facility) << 16 ) |     /* Facility Code        */ \\\n            ( (_exception) <<  0 )      /* Exception Code       */ \\\n            ) )\n\n/*\n * We choose one specific Facility/Error code combination to\n * identify our software exceptions vs. WIN32 exceptions.\n * We store our actual component and error code within\n * the optional information array.\n */\n#define EXCEPTION_PTW32_SERVICES        \\\n     MAKE_SOFTWARE_EXCEPTION( SE_ERROR, \\\n                              PTW32_SERVICES_FACILITY, \\\n                              PTW32_SERVICES_ERROR )\n\n#define PTW32_SERVICES_FACILITY         0xBAD\n#define PTW32_SERVICES_ERROR            0xDEED\n\n#endif /* __CLEANUP_SEH */\n\n/*\n * Services available through EXCEPTION_PTW32_SERVICES\n * and also used [as parameters to ptw32_throw()] as\n * generic exception selectors.\n */\n\n#define PTW32_EPS_EXIT                  (1)\n#define PTW32_EPS_CANCEL                (2)\n\n\n/* Useful macros */\n#define PTW32_MAX(a,b)  ((a)<(b)?(b):(a))\n#define PTW32_MIN(a,b)  ((a)>(b)?(b):(a))\n\n\n/* Declared in pthread_cancel.c */\nextern DWORD (*ptw32_register_cancelation) (PAPCFUNC, HANDLE, DWORD);\n\n/* Thread Reuse stack bottom marker. Must not be NULL or any valid pointer to memory. */\n#define PTW32_THREAD_REUSE_EMPTY ((ptw32_thread_t *)(size_t) 1)\n\nextern int ptw32_processInitialized;\nextern ptw32_thread_t * ptw32_threadReuseTop;\nextern ptw32_thread_t * ptw32_threadReuseBottom;\nextern pthread_key_t ptw32_selfThreadKey;\nextern pthread_key_t ptw32_cleanupKey;\nextern pthread_cond_t ptw32_cond_list_head;\nextern pthread_cond_t ptw32_cond_list_tail;\n\nextern int ptw32_mutex_default_kind;\n\nextern unsigned __int64 ptw32_threadSeqNumber;\n\nextern int ptw32_concurrency;\n\nextern int ptw32_features;\n\nextern ptw32_mcs_lock_t ptw32_thread_reuse_lock;\nextern ptw32_mcs_lock_t ptw32_mutex_test_init_lock;\nextern ptw32_mcs_lock_t ptw32_cond_list_lock;\nextern ptw32_mcs_lock_t ptw32_cond_test_init_lock;\nextern ptw32_mcs_lock_t ptw32_rwlock_test_init_lock;\nextern ptw32_mcs_lock_t ptw32_spinlock_test_init_lock;\n\n#if defined(_UWIN)\nextern int pthread_count;\n#endif\n\n#if defined(__cplusplus)\nextern \"C\"\n{\n#endif\t\t\t\t/* __cplusplus */\n\n/*\n * =====================\n * =====================\n * Forward Declarations\n * =====================\n * =====================\n */\n\n  int ptw32_is_attr (const pthread_attr_t * attr);\n\n  int ptw32_cond_check_need_init (pthread_cond_t * cond);\n  int ptw32_mutex_check_need_init (pthread_mutex_t * mutex);\n  int ptw32_rwlock_check_need_init (pthread_rwlock_t * rwlock);\n\n  int ptw32_robust_mutex_inherit(pthread_mutex_t * mutex);\n  void ptw32_robust_mutex_add(pthread_mutex_t* mutex, pthread_t self);\n  void ptw32_robust_mutex_remove(pthread_mutex_t* mutex, ptw32_thread_t* otp);\n\n  DWORD\n    ptw32_RegisterCancelation (PAPCFUNC callback,\n\t\t\t       HANDLE threadH, DWORD callback_arg);\n\n  int ptw32_processInitialize (void);\n\n  void ptw32_processTerminate (void);\n\n  void ptw32_threadDestroy (pthread_t tid);\n\n  void ptw32_pop_cleanup_all (int execute);\n\n  pthread_t ptw32_new (void);\n\n  pthread_t ptw32_threadReusePop (void);\n\n  void ptw32_threadReusePush (pthread_t thread);\n\n  int ptw32_getprocessors (int *count);\n\n  int ptw32_setthreadpriority (pthread_t thread, int policy, int priority);\n\n  void ptw32_rwlock_cancelwrwait (void *arg);\n\n#if ! (defined (__MINGW64__) || defined(__MINGW32__)) || (defined(__MSVCRT__) && ! defined(__DMC__))\n  unsigned __stdcall\n#else\n  void\n#endif\n    ptw32_threadStart (void *vthreadParms);\n\n  void ptw32_callUserDestroyRoutines (pthread_t thread);\n\n  int ptw32_tkAssocCreate (ptw32_thread_t * thread, pthread_key_t key);\n\n  void ptw32_tkAssocDestroy (ThreadKeyAssoc * assoc);\n\n  int ptw32_semwait (sem_t * sem);\n\n  DWORD ptw32_relmillisecs (const struct timespec * abstime);\n\n  void ptw32_mcs_lock_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node);\n\n  int ptw32_mcs_lock_try_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node);\n\n  void ptw32_mcs_lock_release (ptw32_mcs_local_node_t * node);\n\n  void ptw32_mcs_node_transfer (ptw32_mcs_local_node_t * new_node, ptw32_mcs_local_node_t * old_node);\n\n#if defined(NEED_FTIME)\n  void ptw32_timespec_to_filetime (const struct timespec *ts, FILETIME * ft);\n  void ptw32_filetime_to_timespec (const FILETIME * ft, struct timespec *ts);\n#endif\n\n/* Declared in misc.c */\n#if defined(NEED_CALLOC)\n#define calloc(n, s) ptw32_calloc(n, s)\n  void *ptw32_calloc (size_t n, size_t s);\n#endif\n\n/* Declared in private.c */\n#if defined(_MSC_VER)\n/*\n * Ignore the warning:\n * \"C++ exception specification ignored except to indicate that\n * the function is not __declspec(nothrow).\"\n */\n#pragma warning(disable:4290)\n#endif\n  void ptw32_throw (DWORD exception)\n#if defined(__CLEANUP_CXX)\n    throw(ptw32_exception_cancel,ptw32_exception_exit)\n#endif\n;\n\n#if defined(__cplusplus)\n}\n#endif\t\t\t\t/* __cplusplus */\n\n\n#if defined(_UWIN_)\n#   if defined(_MT)\n#       if defined(__cplusplus)\nextern \"C\"\n{\n#       endif\n  _CRTIMP unsigned long __cdecl _beginthread (void (__cdecl *) (void *),\n\t\t\t\t\t      unsigned, void *);\n  _CRTIMP void __cdecl _endthread (void);\n  _CRTIMP unsigned long __cdecl _beginthreadex (void *, unsigned,\n\t\t\t\t\t\tunsigned (__stdcall *) (void *),\n\t\t\t\t\t\tvoid *, unsigned, unsigned *);\n  _CRTIMP void __cdecl _endthreadex (unsigned);\n#       if defined(__cplusplus)\n}\n#       endif\n#   endif\n#else\n#       include <process.h>\n#   endif\n\n\n/*\n * Use intrinsic versions wherever possible. VC will do this\n * automatically where possible and GCC define these if available:\n * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1\n * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2\n * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4\n * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_8\n * __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16\n *\n * The full set of Interlocked intrinsics in GCC are (check versions):\n * type __sync_fetch_and_add (type *ptr, type value, ...)\n * type __sync_fetch_and_sub (type *ptr, type value, ...)\n * type __sync_fetch_and_or (type *ptr, type value, ...)\n * type __sync_fetch_and_and (type *ptr, type value, ...)\n * type __sync_fetch_and_xor (type *ptr, type value, ...)\n * type __sync_fetch_and_nand (type *ptr, type value, ...)\n * type __sync_add_and_fetch (type *ptr, type value, ...)\n * type __sync_sub_and_fetch (type *ptr, type value, ...)\n * type __sync_or_and_fetch (type *ptr, type value, ...)\n * type __sync_and_and_fetch (type *ptr, type value, ...)\n * type __sync_xor_and_fetch (type *ptr, type value, ...)\n * type __sync_nand_and_fetch (type *ptr, type value, ...)\n * bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)\n * type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)\n * __sync_synchronize (...) // Full memory barrier\n * type __sync_lock_test_and_set (type *ptr, type value, ...) // Acquire barrier\n * void __sync_lock_release (type *ptr, ...) // Release barrier\n *\n * These are all overloaded and take 1,2,4,8 byte scalar or pointer types.\n *\n * The above aren't available in Mingw32 as of gcc 4.5.2 so define our own.\n */\n#if defined(__GNUC__)\n# if defined(_WIN64)\n# define PTW32_INTERLOCKED_COMPARE_EXCHANGE_64(location, value, comparand)    \\\n    ({                                                                     \\\n      __typeof (value) _result;                                            \\\n      __asm__ __volatile__                                                 \\\n      (                                                                    \\\n        \"lock\\n\\t\"                                                         \\\n        \"cmpxchgq      %2,(%1)\"                                            \\\n        :\"=a\" (_result)                                                    \\\n        :\"r\"  (location), \"r\" (value), \"a\" (comparand)                     \\\n        :\"memory\", \"cc\");                                                  \\\n      _result;                                                             \\\n    })\n# define PTW32_INTERLOCKED_EXCHANGE_64(location, value)                    \\\n    ({                                                                     \\\n      __typeof (value) _result;                                            \\\n      __asm__ __volatile__                                                 \\\n      (                                                                    \\\n        \"xchgq\t %0,(%1)\"                                                  \\\n        :\"=r\" (_result)                                                    \\\n        :\"r\" (location), \"0\" (value)                                       \\\n        :\"memory\", \"cc\");                                                  \\\n      _result;                                                             \\\n    })\n# define PTW32_INTERLOCKED_EXCHANGE_ADD_64(location, value)                \\\n    ({                                                                     \\\n      __typeof (value) _result;                                            \\\n      __asm__ __volatile__                                                 \\\n      (                                                                    \\\n        \"lock\\n\\t\"                                                         \\\n        \"xaddq\t %0,(%1)\"                                                  \\\n        :\"=r\" (_result)                                                    \\\n        :\"r\" (location), \"0\" (value)                                       \\\n        :\"memory\", \"cc\");                                                  \\\n      _result;                                                             \\\n    })\n# define PTW32_INTERLOCKED_INCREMENT_64(location)                          \\\n    ({                                                                     \\\n      PTW32_INTERLOCKED_LONG _temp = 1;                                   \\\n      __asm__ __volatile__                                                 \\\n      (                                                                    \\\n        \"lock\\n\\t\"                                                         \\\n        \"xaddq\t %0,(%1)\"                                                  \\\n        :\"+r\" (_temp)                                                      \\\n        :\"r\" (location)                                                    \\\n        :\"memory\", \"cc\");                                                  \\\n      ++_temp;                                                             \\\n    })\n# define PTW32_INTERLOCKED_DECREMENT_64(location)                          \\\n    ({                                                                     \\\n      PTW32_INTERLOCKED_LONG _temp = -1;                                  \\\n      __asm__ __volatile__                                                 \\\n      (                                                                    \\\n        \"lock\\n\\t\"                                                         \\\n        \"xaddq\t %2,(%1)\"                                                  \\\n        :\"+r\" (_temp)                                                      \\\n        :\"r\" (location)                                                    \\\n        :\"memory\", \"cc\");                                                  \\\n      --_temp;                                                             \\\n    })\n#endif\n# define PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG(location, value, comparand)    \\\n    ({                                                                     \\\n      __typeof (value) _result;                                            \\\n      __asm__ __volatile__                                                 \\\n      (                                                                    \\\n        \"lock\\n\\t\"                                                         \\\n        \"cmpxchgl       %2,(%1)\"                                           \\\n        :\"=a\" (_result)                                                    \\\n        :\"r\"  (location), \"r\" (value), \"a\" (comparand)                     \\\n        :\"memory\", \"cc\");                                                  \\\n      _result;                                                             \\\n    })\n# define PTW32_INTERLOCKED_EXCHANGE_LONG(location, value)                  \\\n    ({                                                                     \\\n      __typeof (value) _result;                                            \\\n      __asm__ __volatile__                                                 \\\n      (                                                                    \\\n        \"xchgl\t %0,(%1)\"                                                  \\\n        :\"=r\" (_result)                                                    \\\n        :\"r\" (location), \"0\" (value)                                       \\\n        :\"memory\", \"cc\");                                                  \\\n      _result;                                                             \\\n    })\n# define PTW32_INTERLOCKED_EXCHANGE_ADD_LONG(location, value)              \\\n    ({                                                                     \\\n      __typeof (value) _result;                                            \\\n      __asm__ __volatile__                                                 \\\n      (                                                                    \\\n        \"lock\\n\\t\"                                                         \\\n        \"xaddl\t %0,(%1)\"                                                  \\\n        :\"=r\" (_result)                                                    \\\n        :\"r\" (location), \"0\" (value)                                       \\\n        :\"memory\", \"cc\");                                                  \\\n      _result;                                                             \\\n    })\n# define PTW32_INTERLOCKED_INCREMENT_LONG(location)                        \\\n    ({                                                                     \\\n      PTW32_INTERLOCKED_LONG _temp = 1;                                   \\\n      __asm__ __volatile__                                                 \\\n      (                                                                    \\\n        \"lock\\n\\t\"                                                         \\\n        \"xaddl\t %0,(%1)\"                                                  \\\n        :\"+r\" (_temp)                                                      \\\n        :\"r\" (location)                                                    \\\n        :\"memory\", \"cc\");                                                  \\\n      ++_temp;                                                             \\\n    })\n# define PTW32_INTERLOCKED_DECREMENT_LONG(location)                        \\\n    ({                                                                     \\\n      PTW32_INTERLOCKED_LONG _temp = -1;                                  \\\n      __asm__ __volatile__                                                 \\\n      (                                                                    \\\n        \"lock\\n\\t\"                                                         \\\n        \"xaddl\t %0,(%1)\"                                                  \\\n        :\"+r\" (_temp)                                                      \\\n        :\"r\" (location)                                                    \\\n        :\"memory\", \"cc\");                                                  \\\n      --_temp;                                                             \\\n    })\n# define PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR(location, value, comparand) \\\n    PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE((PTW32_INTERLOCKED_SIZEPTR)location, \\\n                                            (PTW32_INTERLOCKED_SIZE)value, \\\n                                            (PTW32_INTERLOCKED_SIZE)comparand)\n# define PTW32_INTERLOCKED_EXCHANGE_PTR(location, value) \\\n    PTW32_INTERLOCKED_EXCHANGE_SIZE((PTW32_INTERLOCKED_SIZEPTR)location, \\\n                                    (PTW32_INTERLOCKED_SIZE)value)\n#else\n# if defined(_WIN64)\n#   define PTW32_INTERLOCKED_COMPARE_EXCHANGE_64 InterlockedCompareExchange64\n#   define PTW32_INTERLOCKED_EXCHANGE_64 InterlockedExchange64\n#   define PTW32_INTERLOCKED_EXCHANGE_ADD_64 InterlockedExchangeAdd64\n#   define PTW32_INTERLOCKED_INCREMENT_64 InterlockedIncrement64\n#   define PTW32_INTERLOCKED_DECREMENT_64 InterlockedDecrement64\n# endif\n# if defined(_MSC_VER) && _MSC_VER < 1300 && !defined(_WIN64) /* MSVC 6 */\n#  define PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG(location, value, comparand) \\\n      ((LONG)InterlockedCompareExchange((PVOID *)(location), (PVOID)(value), (PVOID)(comparand)))\n# else\n#  define PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG InterlockedCompareExchange\n# endif\n# define PTW32_INTERLOCKED_EXCHANGE_LONG InterlockedExchange\n# define PTW32_INTERLOCKED_EXCHANGE_ADD_LONG InterlockedExchangeAdd\n# define PTW32_INTERLOCKED_INCREMENT_LONG InterlockedIncrement\n# define PTW32_INTERLOCKED_DECREMENT_LONG InterlockedDecrement\n# if defined(_MSC_VER) && _MSC_VER < 1300 && !defined(_WIN64) /* MSVC 6 */\n#  define PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR InterlockedCompareExchange\n#  define PTW32_INTERLOCKED_EXCHANGE_PTR(location, value) \\\n    ((PVOID)InterlockedExchange((LPLONG)(location), (LONG)(value)))\n# else\n#  define PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR InterlockedCompareExchangePointer\n#  define PTW32_INTERLOCKED_EXCHANGE_PTR InterlockedExchangePointer\n# endif\n#endif\n#if defined(_WIN64)\n#   define PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE PTW32_INTERLOCKED_COMPARE_EXCHANGE_64\n#   define PTW32_INTERLOCKED_EXCHANGE_SIZE PTW32_INTERLOCKED_EXCHANGE_64\n#   define PTW32_INTERLOCKED_EXCHANGE_ADD_SIZE PTW32_INTERLOCKED_EXCHANGE_ADD_64\n#   define PTW32_INTERLOCKED_INCREMENT_SIZE PTW32_INTERLOCKED_INCREMENT_64\n#   define PTW32_INTERLOCKED_DECREMENT_SIZE PTW32_INTERLOCKED_DECREMENT_64\n#else\n#   define PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG\n#   define PTW32_INTERLOCKED_EXCHANGE_SIZE PTW32_INTERLOCKED_EXCHANGE_LONG\n#   define PTW32_INTERLOCKED_EXCHANGE_ADD_SIZE PTW32_INTERLOCKED_EXCHANGE_ADD_LONG\n#   define PTW32_INTERLOCKED_INCREMENT_SIZE PTW32_INTERLOCKED_INCREMENT_LONG\n#   define PTW32_INTERLOCKED_DECREMENT_SIZE PTW32_INTERLOCKED_DECREMENT_LONG\n#endif\n\n#if defined(NEED_CREATETHREAD)\n\n/* \n * Macro uses args so we can cast start_proc to LPTHREAD_START_ROUTINE\n * in order to avoid warnings because of return type\n */\n\n#define _beginthreadex(security, \\\n                       stack_size, \\\n                       start_proc, \\\n                       arg, \\\n                       flags, \\\n                       pid) \\\n        CreateThread(security, \\\n                     stack_size, \\\n                     (LPTHREAD_START_ROUTINE) start_proc, \\\n                     arg, \\\n                     flags, \\\n                     pid)\n\n#define _endthreadex ExitThread\n\n#endif\t\t\t\t/* NEED_CREATETHREAD */\n\n\n#endif\t\t\t\t/* _IMPLEMENT_H */\n"
  },
  {
    "path": "deps/w32-pthreads/libpthreadGC2.stamp",
    "content": "touched\n"
  },
  {
    "path": "deps/w32-pthreads/manual/ChangeLog",
    "content": "2011-03-26  Ross Johnson  <ross at homemail dot com dot au>\n\n\t* pthread_nutex_init.html (robust mutexes): Added\n\tdescriptions for newly implemented interface.\n\t* pthread_mutexattr_init.html (robust mutexes): Likewise.\n\t* pthread_getsequence_np.html: New.\n\t* index.html: Updated.\n\n2008-06-30  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* pthread_setschedparam.html: Fix \"see also\" links.\n\n2005-05-06  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* PortabilityIssues.html: Was nonPortableIssues.html.\n\t* index.html: Updated; add table of contents at top.\n\t* *.html: Add Pthreads-win32 header info; add link back to the\n\tindex page 'index.html'.\n\n2005-05-06  Ross Johnson  <ross at callisto.canberra.edu.au>\n\n\t* index.html: New.\n\t* nonPortableIssues.html: New.\n\t* pthread_attr_init.html: New.\n\t* pthread_attr_setstackaddr.html: New.\n\t* pthread_attr_setstacksize.html: New.\n\t* pthread_barrierattr_init.html: New.\n\t* pthread_barrierattr_setpshared.html: New.\n\t* pthread_barrier_init.html: New.\n\t* pthread_barrier_wait.html: New.\n\t* pthreadCancelableWait.html: New.\n\t* pthread_cancel.html: New.\n\t* pthread_cleanup_push.html: New.\n\t* pthread_condattr_init.html: New.\n\t* pthread_condattr_setpshared.html: New.\n\t* pthread_cond_init.html: New.\n\t* pthread_create.html: New.\n\t* pthread_delay_np.html: New.\n\t* pthread_detach.html: New.\n\t* pthread_equal.html: New.\n\t* pthread_exit.html: New.\n\t* pthread_getw32threadhandle_np.html: New.\n\t* pthread_join.html: New.\n\t* pthread_key_create.html: New.\n\t* pthread_kill.html: New.\n\t* pthread_mutexattr_init.html: New.\n\t* pthread_mutexattr_setpshared.html: New.\n\t* pthread_mutex_init.html: New.\n\t* pthread_num_processors_np.html: New.\n\t* pthread_once.html: New.\n\t* pthread_rwlockattr_init.html: New.\n\t* pthread_rwlockattr_setpshared.html: New.\n\t* pthread_rwlock_init.html: New.\n\t* pthread_rwlock_rdlock.html: New.\n\t* pthread_rwlock_timedrdlock.html: New.\n\t* pthread_rwlock_timedwrlock.html: New.\n\t* pthread_rwlock_unlock.html: New.\n\t* pthread_rwlock_wrlock.html: New.\n\t* pthread_self.html: New.\n\t* pthread_setcancelstate.html: New.\n\t* pthread_setcanceltype.html: New.\n\t* pthread_setconcurrency.html: New.\n\t* pthread_setschedparam.html: New.\n\t* pthread_spin_init.html: New.\n\t* pthread_spin_lock.html: New.\n\t* pthread_spin_unlock.html: New.\n\t* pthread_timechange_handler_np.html: New.\n\t* pthread_win32_attach_detach_np.html: New.\n\t* pthread_win32_test_features_np.html: New.\n\t* sched_get_priority_max.html: New.\n\t* sched_getscheduler.html: New.\n\t* sched_setscheduler.html: New.\n\t* sched_yield.html: New.\n\t* sem_init.html: New.\n"
  },
  {
    "path": "deps/w32-pthreads/manual/PortabilityIssues.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PORTABILITYISSUES manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;322600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050506;11580000\">\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<H4><A HREF=\"#toc\">Table of Contents</A></H4>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P STYLE=\"font-weight: medium\">Portability issues</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>Thread priority</B></P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<H3>Thread priority</H3>\n<P STYLE=\"margin-left: 2cm\">POSIX defines a single contiguous range\nof numbers that determine a thread's priority. Win32 defines priority\nclasses - and priority levels relative to these classes. Classes are\nsimply priority base levels that the defined priority levels are\nrelative to such that, changing a process's priority class will\nchange the priority of all of it's threads, while the threads retain\nthe same relativity to each other.</P>\n<P STYLE=\"margin-left: 2cm\">A Win32 system defines a single\ncontiguous monotonic range of values that define system priority\nlevels, just like POSIX. However, Win32 restricts individual threads\nto a subset of this range on a per-process basis.</P>\n<P STYLE=\"margin-left: 2cm\">The following table shows the base\npriority levels for combinations of priority class and priority value\nin Win32.</P>\n<DL>\n\t<DL>\n\t\t<DD>\n\t\t<TABLE WIDTH=742 BORDER=0 CELLPADDING=0 CELLSPACING=0 STYLE=\"page-break-inside: avoid\">\n\t\t\t<COL WIDTH=50>\n\t\t\t<COL WIDTH=356>\n\t\t\t<COL WIDTH=336>\n\t\t\t<THEAD>\n\t\t\t\t<TR VALIGN=TOP>\n\t\t\t\t\t<TD WIDTH=50>\n\t\t\t\t\t\t<P ALIGN=CENTER><BR>\n\t\t\t\t\t\t</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356>\n\t\t\t\t\t\t<P ALIGN=LEFT><B>Process Priority Class</B></P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336>\n\t\t\t\t\t\t<P ALIGN=LEFT><B>Thread Priority Level</B></P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t</THEAD>\n\t\t\t<TBODY>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"1\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>1</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>IDLE_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_IDLE</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"1\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>1</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>BELOW_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_IDLE</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"1\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>1</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_IDLE</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"1\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>1</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>ABOVE_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_IDLE</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"1\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>1</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>HIGH_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_IDLE</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"2\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>2</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>IDLE_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_LOWEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"3\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>3</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>IDLE_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_BELOW_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"4\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>4</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>IDLE_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"4\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>4</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>BELOW_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_LOWEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"5\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>5</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>IDLE_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_ABOVE_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"5\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>5</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>BELOW_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_BELOW_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"5\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>5</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>Background NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_LOWEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"6\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>6</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>IDLE_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_HIGHEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"6\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>6</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>BELOW_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"6\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>6</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>Background NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_BELOW_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"7\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>7</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>BELOW_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_ABOVE_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"7\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>7</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>Background NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"7\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>7</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>Foreground NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_LOWEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"8\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>8</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>BELOW_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_HIGHEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"8\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>8</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_ABOVE_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"8\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>8</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>Foreground NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_BELOW_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"8\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>8</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>ABOVE_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_LOWEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"9\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>9</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_HIGHEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"9\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>9</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>Foreground NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"9\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>9</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>ABOVE_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_BELOW_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"10\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>10</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>Foreground NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_ABOVE_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"10\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>10</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>ABOVE_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"11\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>11</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>Foreground NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_HIGHEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"11\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>11</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>ABOVE_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_ABOVE_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"11\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>11</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>HIGH_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_LOWEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"12\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>12</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>ABOVE_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_HIGHEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"12\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>12</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>HIGH_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_BELOW_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"13\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>13</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>HIGH_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"14\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>14</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>HIGH_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_ABOVE_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"15\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>15</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>HIGH_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_HIGHEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"15\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>15</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>HIGH_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_TIME_CRITICAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"15\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>15</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>IDLE_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_TIME_CRITICAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"15\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>15</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>BELOW_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_TIME_CRITICAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"15\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>15</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_TIME_CRITICAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"15\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>15</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>ABOVE_NORMAL_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_TIME_CRITICAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"16\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>16</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_IDLE</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"17\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>17</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=BOTTOM SDVAL=\"-7\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=LEFT>-7</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"18\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>18</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=BOTTOM SDVAL=\"-6\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=LEFT>-6</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"19\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>19</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=BOTTOM SDVAL=\"-5\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=LEFT>-5</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"20\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>20</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=BOTTOM SDVAL=\"-4\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=LEFT>-4</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"21\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>21</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=BOTTOM SDVAL=\"-3\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=LEFT>-3</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"22\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>22</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_LOWEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"23\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>23</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_BELOW_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"24\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>24</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"25\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>25</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_ABOVE_NORMAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"26\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>26</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_HIGHEST</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"27\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>27</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=BOTTOM SDVAL=\"3\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=LEFT>3</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"28\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>28</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=BOTTOM SDVAL=\"4\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=LEFT>4</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"29\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>29</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=BOTTOM SDVAL=\"5\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=LEFT>5</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"30\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>30</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=BOTTOM SDVAL=\"6\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=LEFT>6</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t\t<TR>\n\t\t\t\t\t<TD WIDTH=50 VALIGN=BOTTOM SDVAL=\"31\" SDNUM=\"3081;\">\n\t\t\t\t\t\t<P ALIGN=CENTER>31</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=356 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>REALTIME_PRIORITY_CLASS</P>\n\t\t\t\t\t</TD>\n\t\t\t\t\t<TD WIDTH=336 VALIGN=TOP>\n\t\t\t\t\t\t<P ALIGN=LEFT>THREAD_PRIORITY_TIME_CRITICAL</P>\n\t\t\t\t\t</TD>\n\t\t\t\t</TR>\n\t\t\t</TBODY>\n\t\t</TABLE>\n\t</DL>\n</DL>\n<P STYLE=\"margin-left: 2cm\">Windows NT: Values -7, -6, -5, -4, -3, 3,\n4, 5, and 6 are not supported.</P>\n<P STYLE=\"margin-left: 2cm\">As you can see, the real priority levels\navailable to any individual Win32 thread are non-contiguous.</P>\n<P STYLE=\"margin-left: 2cm\">An application using Pthreads-w32 should\nnot make assumptions about the numbers used to represent thread\npriority levels, except that they are monotonic between the values\nreturned by sched_get_priority_min() and sched_get_priority_max().\nE.g. Windows 95, 98, NT, 2000, XP make available a non-contiguous\nrange of numbers between -15 and 15, while at least one version of\nWinCE (3.0) defines the minimum priority (THREAD_PRIORITY_LOWEST) as\n5, and the maximum priority (THREAD_PRIORITY_HIGHEST) as 1.</P>\n<P STYLE=\"margin-left: 2cm\">Internally, pthreads-win32 maps any\npriority levels between THREAD_PRIORITY_IDLE and\nTHREAD_PRIORITY_LOWEST to THREAD_PRIORITY_LOWEST, or between\nTHREAD_PRIORITY_TIME_CRITICAL and THREAD_PRIORITY_HIGHEST to\nTHREAD_PRIORITY_HIGHEST. Currently, this also applies to\nREALTIME_PRIORITY_CLASS even if levels -7, -6, -5, -4, -3, 3, 4, 5,\nand 6 are supported.</P>\n<P STYLE=\"margin-left: 2cm\">If it wishes, a Win32 application using\npthreads-w32 can use the Win32 defined priority macros\nTHREAD_PRIORITY_IDLE through THREAD_PRIORITY_TIME_CRITICAL.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Author</A></H2>\n<P>Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">See also</A></H2>\n<P><BR><BR>\n</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Author</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect4\" NAME=\"toc4\">See also</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/index.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE></TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 3.2  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;17350500\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20110326;18352700\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<STYLE TYPE=\"text/css\">\n\t<!--\n\t\tH4.cjk { font-family: \"AR PL UMing CN\" }\n\t\tH4.ctl { font-family: \"Lohit Devanagari\" }\n\t\tH3.cjk { font-family: \"AR PL UMing CN\" }\n\t\tH3.ctl { font-family: \"Lohit Devanagari\" }\n\t\tH2.cjk { font-family: \"AR PL UMing CN\" }\n\t\tH2.ctl { font-family: \"Lohit Devanagari\" }\n\t-->\n\t</STYLE>\n</HEAD>\n<BODY LANG=\"en-GB\" DIR=\"LTR\">\n<H4 CLASS=\"western\">POSIX Threads for Windows – REFERENCE -\n<A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<H3 CLASS=\"western\">Table of Contents</H3>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"#sect1\" NAME=\"toc1\">POSIX\nthreads API reference</A><BR><A HREF=\"#sect2\" NAME=\"toc2\">Miscellaneous\nPOSIX thread safe routines provided by Pthreads-w32</A><BR><A HREF=\"#sect3\" NAME=\"toc3\">Non-portable\nPthreads-w32 routines</A><BR><A HREF=\"#sect4\" NAME=\"toc4\">Other</A></P>\n<H2 CLASS=\"western\"><A HREF=\"#toc1\" NAME=\"sect1\">POSIX threads API\nreference</A></H2>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_getdetachstate</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_getinheritsched</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_getschedparam</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_getschedpolicy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_getscope</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_setstackaddr.html\"><B>pthread_attr_getstackaddr</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_setstacksize.html\"><B>pthread_attr_getstacksize</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_setdetachstate</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_setinheritsched</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_setschedparam</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_setschedpolicy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_setscope</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_setstackaddr.html\"><B>pthread_attr_setstackaddr</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_attr_setstacksize.html\"><B>pthread_attr_setstacksize</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_barrierattr_init.html\"><B>pthread_barrierattr_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_barrierattr_setpshared.html\"><B>pthread_barrierattr_getpshared</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_barrierattr_init.html\"><B>pthread_barrierattr_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_barrierattr_setpshared.html\"><B>pthread_barrierattr_setpshared</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_barrier_init.html\"><B>pthread_barrier_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_barrier_init.html\"><B>pthread_barrier_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_barrier_wait.html\"><B>pthread_barrier_wait</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cancel.html\"><B>pthread_cancel</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cleanup_push.html\"><B>pthread_cleanup_pop</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cleanup_push.html\"><B>pthread_cleanup_push</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_condattr_init.html\"><B>pthread_condattr_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_condattr_setpshared.html\"><B>pthread_condattr_getpshared</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_condattr_init.html\"><B>pthread_condattr_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_condattr_setpshared.html\"><B>pthread_condattr_setpshared</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cond_init.html\"><B>pthread_cond_broadcast</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cond_init.html\"><B>pthread_cond_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cond_init.html\"><B>pthread_cond_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cond_init.html\"><B>pthread_cond_signal</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cond_init.html\"><B>pthread_cond_timedwait</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cond_init.html\"><B>pthread_cond_wait</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_create.html\"><B>pthread_create</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_detach.html\"><B>pthread_detach</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_equal.html\"><B>pthread_equal</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_exit.html\"><B>pthread_exit</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_setconcurrency.html\"><B>pthread_getconcurrency</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_setschedparam.html\"><B>pthread_getschedparam</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_getunique_np.html\"><B>pthread_getunique_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_key_create.html\"><B>pthread_getspecific</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_join.html\"><B>pthread_join</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_key_create.html\"><B>pthread_key_create</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_key_create.html\"><B>pthread_key_delete</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_kill.html\"><B>pthread_kill</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_getkind_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutexattr_setpshared.html\"><B>pthread_mutexattr_getpshared</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_getrobust</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_gettype</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_setkind_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutexattr_setpshared.html\"><B>pthread_mutexattr_setpshared</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_setrobust</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_settype</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_consistent</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_lock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_timedlock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_trylock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_unlock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_once.html\"><B>pthread_once</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlockattr_init.html\"><B>pthread_rwlockattr_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlockattr_setpshared.html\"><B>pthread_rwlockattr_getpshared</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlockattr_init.html\"><B>pthread_rwlockattr_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlockattr_setpshared.html\"><B>pthread_rwlockattr_setpshared</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlock_init.html\"><B>pthread_rwlock_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlock_init.html\"><B>pthread_rwlock_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_rdlock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlock_timedrdlock.html\"><B>pthread_rwlock_timedrdlock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlock_timedwrlock.html\"><B>pthread_rwlock_timedwrlock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_tryrdlock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_trywrlock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlock_unlock.html\"><B>pthread_rwlock_unlock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_wrlock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_self.html\"><B>pthread_self</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cancel.html\"><B>pthread_setcancelstate</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cancel.html\"><B>pthread_setcanceltype</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_setconcurrency.html\"><B>pthread_setconcurrency</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_setschedparam.html\"><B>pthread_setschedparam</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_key_create.html\"><B>pthread_setspecific</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_kill.html\"><B>pthread_sigmask</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_spin_init.html\"><B>pthread_spin_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_spin_init.html\"><B>pthread_spin_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_spin_lock.html\"><B>pthread_spin_lock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_spin_lock.html\"><B>pthread_spin_trylock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_spin_unlock.html\"><B>pthread_spin_unlock</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_cancel.html\"><B>pthread_testcancel</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sched_get_priority_max.html\"><B>sched_get_priority_max</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sched_get_priority_max.html\"><B>sched_get_priority_min</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sched_getscheduler.html\"><B>sched_getscheduler</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sched_setscheduler.html\"><B>sched_setscheduler</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sched_yield.html\"><B>sched_yield</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><B>sem_close</B></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sem_init.html\"><B>sem_destroy</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sem_init.html\"><B>sem_getvalue</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sem_init.html\"><B>sem_init</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><B>sem_open</B></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sem_init.html\"><B>sem_post</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sem_init.html\"><B>sem_post_multiple</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sem_init.html\"><B>sem_timedwait</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sem_init.html\"><B>sem_trywait</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><B>sem_unlink</B></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"sem_init.html\"><B>sem_wait</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_kill.html\"><B>sigwait</B></A></P>\n<H2 CLASS=\"western\"><A HREF=\"#toc3\" NAME=\"sect3\">Non-portable\nPthreads-w32 routines</A></H2>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthreadCancelableWait.html\"><B>pthreadCancelableTimedWait</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthreadCancelableWait.html\"><B>pthreadCancelableWait</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_delay_np.html\"><B>pthread_delay_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_getw32threadhandle_np.html\"><B>pthread_getw32threadhandle_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_num_processors_np.html\"><B>pthread_num_processors_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_win32_test_features_np.html\"><B>pthread_win32_test_features_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_timechange_handler_np.html\"><B>pthread_timechange_handler_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_win32_attach_detach_np.html\"><B>pthread_win32_process_attach_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_win32_attach_detach_np.html\"><B>pthread_win32_process_detach_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_win32_attach_detach_np.html\"><B>pthread_win32_thread_attach_np</B></A></P>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"pthread_win32_attach_detach_np.html\"><B>pthread_win32_thread_detach_np</B></A></P>\n<H2 CLASS=\"western\"><A HREF=\"#toc4\" NAME=\"sect4\">Other</A></H2>\n<P STYLE=\"margin-left: 0.79in\"><A HREF=\"PortabilityIssues.html\"><B>Portability\nissues</B></A></P>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/pthreadCancelableWait.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREADCANCELLABLEWAIT manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;322600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;23242300\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P STYLE=\"font-weight: medium\">pthreadCancelableTimedWait,\npthreadCancelableWait – provide cancellation hooks for user Win32\nroutines</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthreadCancelableTimedWait (HANDLE </B><I>waitHandle</I><B>,\nDWORD </B><I>timeout</I><B>);</B></P>\n<P><B>int pthreadCancelableWait (HANDLE </B><I>waitHandle</I><B>);</B></P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>These two functions provide hooks into the <A HREF=\"pthread_cancel.html\"><B>pthread_cancel</B></A>()\nmechanism that will allow you to wait on a Windows handle and make it\na cancellation point. Both functions block until either the given\nWin32 <B>HANDLE</B> is signalled, or <A HREF=\"pthread_cancel.html\"><B>pthread_cancel</B></A>()\nhas been called. They are implemented using <B>WaitForMultipleObjects</B>\non <I>waitHandle</I> and the manually reset Win32 event handle that\nis the target of <A HREF=\"pthread_cancel.html\"><B>pthread_cancel</B></A>().\nThese routines may be called from Win32 native threads but\n<A HREF=\"pthread_cancel.html\"><B>pthread_cancel</B></A>() will\nrequire that thread's POSIX thread ID that the thread must retrieve\nusing <A HREF=\"pthread_self.html\"><B>pthread_self</B></A>().</P>\n<P><B>pthreadCancelableTimedWait</B> is the timed version that will\nreturn with the code <B>ETIMEDOUT</B> if the interval <I>timeout</I>\nmilliseconds elapses before <I>waitHandle</I> is signalled.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P>These routines allow routines that block on Win32 HANDLEs to be\ncancellable via <A HREF=\"pthread_cancel.html\"><B>pthread_cancel</B></A>().</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Return Value</A></H2>\n<P><BR><BR>\n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<P>The <B>pthreadCancelableTimedWait</B> function returns the\nfollowing error code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ETIMEDOUT</B>\n\t\t\t\t</DT></DL>\n</DL>\n<P STYLE=\"margin-left: 2cm\">\nThe interval <I>timeout</I> milliseconds elapsed before <I>waitHandle</I>\nwas signalled.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">See also</A></H2>\n<P><A HREF=\"pthread_cancel.html\"><B>pthread_cancel()</B></A>,\n<A HREF=\"pthread_self.html\"><B>pthread_self()</B></A></P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\"></A><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A><A HREF=\"#sect4\" NAME=\"toc4\"></A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Author</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect7\" NAME=\"toc7\">See also</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_attr_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_ATTR_INIT(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;10092900\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;16540200\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_attr_init, pthread_attr_destroy,\npthread_attr_setdetachstate, pthread_attr_getdetachstate,\npthread_attr_setschedparam, pthread_attr_getschedparam,\npthread_attr_setschedpolicy, pthread_attr_getschedpolicy,\npthread_attr_setinheritsched, pthread_attr_getinheritsched,\npthread_attr_setscope, pthread_attr_getscope - thread creation\nattributes \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_attr_init(pthread_attr_t *</B><I>attr</I><B>);</B> \n</P>\n<P><B>int pthread_attr_destroy(pthread_attr_t *</B><I>attr</I><B>);</B>\n</P>\n<P><B>int pthread_attr_setdetachstate(pthread_attr_t *</B><I>attr</I><B>,\nint </B><I>detachstate</I><B>);</B> \n</P>\n<P><B>int pthread_attr_getdetachstate(const pthread_attr_t *</B><I>attr</I><B>,\nint *</B><I>detachstate</I><B>);</B> \n</P>\n<P><B>int pthread_attr_setschedpolicy(pthread_attr_t *</B><I>attr</I><B>,\nint </B><I>policy</I><B>);</B> \n</P>\n<P><B>int pthread_attr_getschedpolicy(const pthread_attr_t *</B><I>attr</I><B>,\nint *</B><I>policy</I><B>);</B> \n</P>\n<P><B>int pthread_attr_setschedparam(pthread_attr_t *</B><I>attr</I><B>,\nconst struct sched_param *</B><I>param</I><B>);</B> \n</P>\n<P><B>int pthread_attr_getschedparam(const pthread_attr_t *</B><I>attr</I><B>,\nstruct sched_param *</B><I>param</I><B>);</B> \n</P>\n<P><B>int pthread_attr_setinheritsched(pthread_attr_t *</B><I>attr</I><B>,\nint </B><I>inherit</I><B>);</B> \n</P>\n<P><B>int pthread_attr_getinheritsched(const pthread_attr_t *</B><I>attr</I><B>,\nint *</B><I>inherit</I><B>);</B> \n</P>\n<P><B>int pthread_attr_setscope(pthread_attr_t *</B><I>attr</I><B>,\nint </B><I>scope</I><B>);</B> \n</P>\n<P><B>int pthread_attr_getscope(const pthread_attr_t *</B><I>attr</I><B>,\nint *</B><I>scope</I><B>);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Setting attributes for threads is achieved by filling a thread\nattribute object <I>attr</I> of type <B>pthread_attr_t</B>, then\npassing it as second argument to <A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A>\n. Passing <B>NULL</B> is equivalent to passing a thread attribute\nobject with all attributes set to their default values. \n</P>\n<P><B>pthread_attr_init</B> initializes the thread attribute object\n<I>attr</I> and fills it with default values for the attributes. (The\ndefault values are listed below for each attribute.) \n</P>\n<P>Each attribute <I>attrname</I> (see below for a list of all\nattributes) can be individually set using the function\n<B>pthread_attr_set</B><I>attrname</I> and retrieved using the\nfunction <B>pthread_attr_get</B><I>attrname.</I> \n</P>\n<P><B>pthread_attr_destroy</B> destroys a thread attribute object,\nwhich must not then be reused until it is reinitialized. \n</P>\n<P>Attribute objects are consulted only when creating a new thread.\nThe same attribute object can be used for creating several threads.\nModifying an attribute object after a call to <B>pthread_create</B>\ndoes not change the attributes of the thread previously created. \n</P>\n<P>The following thread attributes are supported: \n</P>\n<H3><A HREF=\"#toc3\" NAME=\"sect3\">detachstate</A></H3>\n<P>Control whether the thread is created in the joinable state (value\n<B>PTHREAD_CREATE_JOINABLE</B>) or in the detached state (\n<B>PTHREAD_CREATE_DETACHED</B>). \n</P>\n<P>Default value: <B>PTHREAD_CREATE_JOINABLE</B>. \n</P>\n<P>In the joinable state, another thread can synchronize on the\nthread termination and recover its termination code using\n<A HREF=\"pthread_join.html\"><B>pthread_join</B>(3)</A> . When a\njoinable thread terminates, some of the thread resources are kept\nallocated, and released only when another thread performs\n<A HREF=\"pthread_join.html\"><B>pthread_join</B>(3)</A> on that\nthread. \n</P>\n<P>In the detached state, the thread's resources are released\nimmediately when it terminates. <A HREF=\"pthread_join.html\"><B>pthread_join</B>(3)</A>\ncannot be used to synchronize on the thread termination. \n</P>\n<P>A thread created in the joinable state can later be put in the\ndetached thread using <A HREF=\"pthread_detach.html\"><B>pthread_detach</B>(3)</A>\n. \n</P>\n<H3><A HREF=\"#toc4\" NAME=\"sect4\">schedpolicy</A></H3>\n<P>Select the scheduling policy for the thread: one of <B>SCHED_OTHER</B>\n(regular, non-real-time scheduling), <B>SCHED_RR</B> (real-time,\nround-robin) or <B>SCHED_FIFO</B> (real-time, first-in first-out). \n</P>\n<P><B>Pthreads-w32</B> only supports <B>SCHED_OTHER</B> - attempting\nto set one of the other policies will return an error ENOTSUP.</P>\n<P>Default value: <B>SCHED_OTHER</B>. \n</P>\n<P><B>Pthreads-w32</B> only supports <B>SCHED_OTHER</B> - attempting\nto set one of the other policies will return an error ENOTSUP.</P>\n<P>The scheduling policy of a thread can be changed after creation\nwith <A HREF=\"pthread_setschedparam.html\"><B>pthread_setschedparam</B>(3)</A>\n. \n</P>\n<H3><A HREF=\"#toc5\" NAME=\"sect5\">schedparam</A></H3>\n<P>Contain the scheduling parameters (essentially, the scheduling\npriority) for the thread.</P>\n<P><B>Pthreads-w32</B> supports the priority levels defined by the\nWindows system it is running on. Under Windows, thread priorities are\nrelative to the process priority class, which must be set via the\nWindows W32 API.</P>\n<P>Default value: priority is 0 (Win32 level <B>THREAD_PRIORITY_NORMAL</B>).\n</P>\n<P>The scheduling priority of a thread can be changed after creation\nwith <A HREF=\"pthread_setschedparam.html\"><B>pthread_setschedparam</B>(3)</A>\n. \n</P>\n<H3><A HREF=\"#toc6\" NAME=\"sect6\">inheritsched</A></H3>\n<P>Indicate whether the scheduling policy and scheduling parameters\nfor the newly created thread are determined by the values of the\n<I>schedpolicy</I> and <I>schedparam</I> attributes (value\n<B>PTHREAD_EXPLICIT_SCHED</B>) or are inherited from the parent\nthread (value <B>PTHREAD_INHERIT_SCHED</B>). \n</P>\n<P>Default value: <B>PTHREAD_EXPLICIT_SCHED</B>. \n</P>\n<H3><A HREF=\"#toc7\" NAME=\"sect7\">scope</A></H3>\n<P>Define the scheduling contention scope for the created thread. The\nonly value supported in the <B>Pthreads-w32</B> implementation is\n<B>PTHREAD_SCOPE_SYSTEM</B>, meaning that the threads contend for CPU\ntime with all processes running on the machine. The other value\nspecified by the standard, <B>PTHREAD_SCOPE_PROCESS</B>, means that\nscheduling contention occurs only between the threads of the running\nprocess.</P>\n<P><B>Pthreads-w32</B> only supports <B>PTHREAD_SCOPE_SYSTEM</B>.</P>\n<P>Default value: <B>PTHREAD_SCOPE_SYSTEM</B>. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Return Value</A></H2>\n<P>All functions return 0 on success and a non-zero error code on\nerror. On success, the <B>pthread_attr_get</B><I>attrname</I>\nfunctions also store the current value of the attribute <I>attrname</I>\nin the location pointed to by their second argument. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">Errors</A></H2>\n<P>The <B>pthread_attr_setdetachstate</B> function returns the\nfollowing error codes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe specified <I>detachstate</I> is not one of\n\t\t<B>PTHREAD_CREATE_JOINABLE</B> or <B>PTHREAD_CREATE_DETACHED</B>. \n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>pthread_attr_setschedparam</B> function returns the following\nerror codes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe priority specified in <I>param</I> is outside the range of\n\t\tallowed priorities for the scheduling policy currently in <I>attr</I>\n\t\t(1 to 99 for <B>SCHED_FIFO</B> and <B>SCHED_RR</B>; 0 for\n\t\t<B>SCHED_OTHER</B>). \n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>pthread_attr_setschedpolicy</B> function returns the following\nerror codes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe specified <I>policy</I> is not one of <B>SCHED_OTHER</B>,\n\t\t<B>SCHED_FIFO</B>, or <B>SCHED_RR</B>. \n\t\t</DD><DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<B>ENOTSUP</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<I>policy</I> is not <B>SCHED_OTHER</B>, the only value supported\n\t\tby <B>Pthreads-w32</B>.</DD></DL>\n</DL>\n<P>\nThe <B>pthread_attr_setinheritsched</B> function returns the\nfollowing error codes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe specified <I>inherit</I> is not one of <B>PTHREAD_INHERIT_SCHED</B>\n\t\tor <B>PTHREAD_EXPLICIT_SCHED</B>. \n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>pthread_attr_setscope</B> function returns the following error\ncodes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe specified <I>scope</I> is not one of <B>PTHREAD_SCOPE_SYSTEM</B>\n\t\tor <B>PTHREAD_SCOPE_PROCESS</B>. \n\t\t</DD><DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<B>ENOTSUP</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe specified <I>scope</I> is <B>PTHREAD_SCOPE_PROCESS</B> (not\n\t\tsupported by <B>Pthreads-w32</B>). \n\t\t</DD></DL>\n</DL>\n<H2>\n<A HREF=\"#toc10\" NAME=\"sect10\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc11\" NAME=\"sect11\">See Also</A></H2>\n<P><A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A> ,\n<A HREF=\"pthread_join.html\"><B>pthread_join</B>(3)</A> ,\n<A HREF=\"pthread_detach.html\"><B>pthread_detach</B>(3)</A> ,\n<A HREF=\"pthread_setschedparam.html\"><B>pthread_setschedparam</B>(3)</A>\n. \n</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<UL>\n\t\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">detachstate</A>\n\t\t\t\t</P>\n\t\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">schedpolicy</A>\n\t\t\t\t</P>\n\t\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">schedparam</A>\n\t\t\t\t</P>\n\t\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">inheritsched</A>\n\t\t\t\t</P>\n\t\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">scope</A>\n\t\t\t\t</P>\n\t</UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect10\" NAME=\"toc10\">Author</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect11\" NAME=\"toc11\">See Also</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_attr_setstackaddr.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_ATTR_GETSTACKADDR&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;11025500\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;17571400\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_attr_getstackaddr, pthread_attr_setstackaddr - get and set\nthe stackaddr attribute \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_attr_getstackaddr(const pthread_attr_t *restrict</B>\n<I>attr</I><B>, void **restrict</B> <I>stackaddr</I><B>); <BR>int\npthread_attr_setstackaddr(pthread_attr_t *</B><I>attr</I><B>, void\n*</B><I>stackaddr</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_attr_getstackaddr</B> and <B>pthread_attr_setstackaddr</B>\nfunctions, respectively, shall get and set the thread creation\n<I>stackaddr</I> attribute in the <I>attr</I> object. \n</P>\n<P>The <I>stackaddr</I> attribute specifies the location of storage\nto be used for the created thread’s stack. The size of the storage\nshall be at least {PTHREAD_STACK_MIN}. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_THREAD_ATTR_STACKADDR</B> in\npthread.h as -1 to indicate that these routines are implemented but\ncannot used to set or get the stack address. These routines always\nreturn the error ENOSYS when called.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, <B>pthread_attr_getstackaddr</B> and\n<B>pthread_attr_setstackaddr</B> shall return a value of 0;\notherwise, an error number shall be returned to indicate the error. \n</P>\n<P>The <B>pthread_attr_getstackaddr</B> function stores the <I>stackaddr</I>\nattribute value in <I>stackaddr</I> if successful. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_attr_setstackaddr</B> function always returns the\nfollowing error code: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ENOSYS</B></DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tThe function is not supported. \n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>pthread_attr_getstackaddr</B> function always returns the\nfollowing error code: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ENOSYS</B></DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tThe function is not supported. \n\t\t</DD></DL>\n</DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>The specification of the <I>stackaddr</I> attribute presents\nseveral ambiguities that make portable use of these interfaces\nimpossible. The description of the single address parameter as a\n&quot;stack&quot; does not specify a particular relationship between\nthe address and the &quot;stack&quot; implied by that address. For\nexample, the address may be taken as the low memory address of a\nbuffer intended for use as a stack, or it may be taken as the address\nto be used as the initial stack pointer register value for the new\nthread. These two are not the same except for a machine on which the\nstack grows &quot;up&quot; from low memory to high, and on which a\n&quot;push&quot; operation first stores the value in memory and then\nincrements the stack pointer register. Further, on a machine where\nthe stack grows &quot;down&quot; from high memory to low,\ninterpretation of the address as the &quot;low memory&quot; address\nrequires a determination of the intended size of the stack.\nIEEE&nbsp;Std&nbsp;1003.1-2001 has introduced the new interfaces\n<A HREF=\"pthread_attr_setstack.html\"><B>pthread_attr_setstack</B>(3)</A>\nand <A HREF=\"pthread_attr_getstack.html\"><B>pthread_attr_getstack</B>(3)</A>\nto resolve these ambiguities. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_destroy</B>(3)</A>\n, <A HREF=\"pthread_attr_init.html\"><B>pthread_attr_getdetachstate</B>(3)</A>\n, <A HREF=\"pthread_attr_getstack.html\"><B>pthread_attr_getstack</B>(3)</A>\n, <A HREF=\"pthread_attr_getstacksize.html\"><B>pthread_attr_getstacksize</B>(3)</A>\n, <A HREF=\"pthread_attr_setstack.html\"><B>pthread_attr_setstack</B>(3)</A>\n, <A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A> , the\nBase Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;limits.h&gt;</I>, <I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_attr_setstacksize.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_ATTR_GETSTACKSIZE&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;11224900\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;18003200\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_attr_getstacksize, pthread_attr_setstacksize - get and set\nthe stacksize attribute \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_attr_getstacksize(const pthread_attr_t *restrict</B>\n<I>attr</I><B>, size_t *restrict</B> <I>stacksize</I><B>); <BR>int\npthread_attr_setstacksize(pthread_attr_t *</B><I>attr</I><B>, size_t</B>\n<I>stacksize</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_attr_getstacksize</B> and <B>pthread_attr_setstacksize</B>\nfunctions, respectively, shall get and set the thread creation\n<I>stacksize</I> attribute in the <I>attr</I> object. \n</P>\n<P>The <I>stacksize</I> attribute shall define the minimum stack size\n(in bytes) allocated for the created threads stack. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_THREAD_ATTR_STACKSIZE</B> in\npthread.h to indicate that these routines are implemented and may be\nused to set or get the stack size.</P>\n<P>Default value: 0 (in Pthreads-w32 a value of 0 means the stack\nwill grow as required)</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, <B>pthread_attr_getstacksize</B> and\n<B>pthread_attr_setstacksize</B> shall return a value of 0;\notherwise, an error number shall be returned to indicate the error. \n</P>\n<P>The <B>pthread_attr_getstacksize</B> function stores the <I>stacksize</I>\nattribute value in <I>stacksize</I> if successful. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_attr_setstacksize</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value of <I>stacksize</I> is less than {PTHREAD_STACK_MIN} or\n\texceeds a system-imposed limit. \n\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_attr_init.html\"><B>pthread_attr_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_attr_setstackaddr.html\"><B>pthread_attr_getstackaddr</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_attr_init.html\"><B>pthread_attr_getdetachstate</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A> <B>,</B>\nthe Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;limits.h&gt;</I>, <I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_barrier_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_BARRIER_DESTROY&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 2.0  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;11372800\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20060408;9450100\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_barrier_destroy, pthread_barrier_init - destroy and\ninitialize a barrier object (<B>ADVANCED REALTIME THREADS</B>) \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_barrier_destroy(pthread_barrier_t *</B><I>barrier</I><B>);\n<BR>int pthread_barrier_init(pthread_barrier_t *restrict</B> <I>barrier</I><B>,\nconst pthread_barrierattr_t *restrict</B> <I>attr</I><B>, unsigned</B>\n<I>count</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_barrier_destroy</B> function shall destroy the\nbarrier referenced by <I>barrier</I> and release any resources used\nby the barrier. The effect of subsequent use of the barrier is\nundefined until the barrier is reinitialized by another call to\n<B>pthread_barrier_init</B> . An implementation may use this function\nto set <I>barrier</I> to an invalid value. An error code is returned if <B>pthread_barrier_destroy</B> is called when any thread is\nblocked on the barrier, or if this function is called with an\nuninitialized barrier. \n</P>\n<P>The <B>pthread_barrier_init</B> function shall allocate any\nresources required to use the barrier referenced by <I>barrier</I>\nand shall initialize the barrier with attributes referenced by <I>attr</I>.\nIf <I>attr</I> is NULL, the default barrier attributes shall be used;\nthe effect is the same as passing the address of a default barrier\nattributes object. The results are undefined if <B>pthread_barrier_init</B>\nis called when any thread is blocked on the barrier (that is, has not\nreturned from the <A HREF=\"pthread_barrier_wait.html\"><B>pthread_barrier_wait</B>(3)</A>\ncall). The results are undefined if a barrier is used without first\nbeing initialized. The results are undefined if <B>pthread_barrier_init</B>\nis called specifying an already initialized barrier. \n</P>\n<P>The <I>count</I> argument specifies the number of threads that\nmust call <A HREF=\"pthread_barrier_wait.html\"><B>pthread_barrier_wait</B>(3)</A>\nbefore any of them successfully return from the call. The value\nspecified by <I>count</I> must be greater than zero. \n</P>\n<P>If the <B>pthread_barrier_init</B> function fails, the barrier\nshall not be initialized and the contents of <I>barrier</I> are\nundefined. \n</P>\n<P>Only the object referenced by <I>barrier</I> may be used for\nperforming synchronization. The result of referring to copies of that\nobject in calls to <B>pthread_barrier_destroy</B> <B>or</B>\n<A HREF=\"pthread_barrier_wait.html\"><B>pthread_barrier_wait</B>(3)</A>\nis undefined.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, these functions shall return zero;\notherwise, an error number shall be returned to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_barrier_destroy</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EBUSY</B> \n\t</DT><DD>\n\tThe implementation has detected an attempt to destroy a barrier\n\twhile it is in use (for example, while being used in a\n\t<A HREF=\"pthread_barrier_wait.html\"><B>pthread_barrier_wait</B>(3)</A>\n\tcall) by another thread. \n\t</DD><DT>\n\t<B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>barrier</I> is invalid. \n\t</DD></DL>\n<P>\nThe <B>pthread_barrier_init</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>EAGAIN</B> \n\t</DT><DD>\n\tThe system lacks the necessary resources to initialize another\n\tbarrier. \n\t</DD><DT>\n\t<B>EINVAL</B> \n\t</DT><DD>\n\tThe value specified by <I>count</I> is equal to zero. \n\t</DD><DT>\n\t<B>ENOMEM</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tInsufficient memory exists to initialize the barrier. \n\t</DD></DL>\n<P>\nThe <B>pthread_barrier_init</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EBUSY</B> \n\t</DT><DD>\n\tThe implementation has detected an attempt to reinitialize a barrier\n\twhile it is in use (for example, while being used in a\n\t<A HREF=\"pthread_barrier_wait.html\"><B>pthread_barrier_wait</B>(3)</A>\n\tcall) by another thread. \n\t</DD><DT>\n\t<B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>attr</I> is invalid. \n\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>The <B>pthread_barrier_destroy</B> and <B>pthread_barrier_init</B>\nfunctions are part of the Barriers option and need not be provided on\nall implementations. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_BARRIERS</B> to indicate\nthat these routines are implemented and may be used.</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc11\" NAME=\"sect11\">Known Bugs</A></H2>\n<DL>\n\t<DD STYLE=\"margin-left: 0cm; margin-bottom: 0.5cm\">In\n\t<B><SPAN LANG=\"en-GB\"><SPAN LANG=\"en-GB\">pthreads-win32</SPAN></SPAN></B>,\n\tthe behaviour of threads which enter <A HREF=\"pthread_barrier_wait.html\"><B>pthread_barrier_wait</B>(3)</A>\n\twhile the barrier is being destroyed is undefined. \n\t</DD></DL>\n<H2>\n<A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_barrier_wait.html\"><B>pthread_barrier_wait</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect11\" NAME=\"toc11\">Known\n\tBugs</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_barrier_wait.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_BARRIER_WAIT&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 2.0  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;11484200\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20060408;9504600\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_barrier_wait - synchronize at a barrier (<B>ADVANCED\nREALTIME THREADS</B>) \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_barrier_wait(pthread_barrier_t *</B><I>barrier</I><B>);\n</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_barrier_wait</B> function shall synchronize\nparticipating threads at the barrier referenced by <I>barrier</I>.\nThe calling thread shall block until the required number of threads\nhave called <B>pthread_barrier_wait</B> specifying the barrier. \n</P>\n<P>When the required number of threads have called\n<B>pthread_barrier_wait</B> specifying the barrier, the constant\n<B>PTHREAD_BARRIER_SERIAL_THREAD</B> shall be returned to one\nunspecified thread and zero shall be returned to each of the\nremaining threads. At this point, the barrier shall be reset to the\nstate it had as a result of the most recent <A HREF=\"pthread_barrier_init.html\"><B>pthread_barrier_init</B>(3)</A>\nfunction that referenced it. \n</P>\n<P>The constant <B>PTHREAD_BARRIER_SERIAL_THREAD</B> is defined in\n<I>&lt;pthread.h&gt;</I> and its value shall be distinct from any\nother value returned by <B>pthread_barrier_wait</B> . \n</P>\n<P>The results are undefined if this function is called with an\nuninitialized barrier. \n</P>\n<P>If a signal is delivered to a thread blocked on a barrier, upon\nreturn from the signal handler the thread shall resume waiting at the\nbarrier if the barrier wait has not completed (that is, if the\nrequired number of threads have not arrived at the barrier during the\nexecution of the signal handler); otherwise, the thread shall\ncontinue as normal from the completed barrier wait. Until the thread\nin the signal handler returns from it, it is unspecified whether\nother threads may proceed past the barrier once they have all reached\nit. \n</P>\n<P>A thread that has blocked on a barrier shall not prevent any\nunblocked thread that is eligible to use the same processing\nresources from eventually making forward progress in its execution.\nEligibility for processing resources shall be determined by the\nscheduling policy. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, the <B>pthread_barrier_wait</B>\nfunction shall return <B>PTHREAD_BARRIER_SERIAL_THREAD</B> for a\nsingle (arbitrary) thread synchronized at the barrier and zero for\neach of the other threads. Otherwise, an error number shall be\nreturned to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_barrier_wait</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>barrier</I> does not refer to an\n\tinitialized barrier object. \n\t</DD></DL>\n<P>\nThis function shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>Applications using this function may be subject to priority\ninversion, as discussed in the Base Definitions volume of\nIEEE&nbsp;Std&nbsp;1003.1-2001, Section 3.285, Priority Inversion. \n</P>\n<P>The <B>pthread_barrier_wait</B> function is part of the Barriers\noption and need not be provided on all implementations. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_BARRIERS</B> to indicate\nthat this routine is implemented and may be used.</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc11\" NAME=\"sect11\">Known Bugs</A></H2>\n<DL>\n\tNone.</DL>\n<H2>\n<A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_barrier_init.html\"><B>pthread_barrier_destroy</B>(3)</A>,\n<A HREF=\"pthread_barrier_init.html\"><B>pthread_barrier_init(3)</B></A>,\nthe Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect11\" NAME=\"toc11\">Known\n\tBugs</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_barrierattr_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_BARRIERATTR_DESTROY&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;11502600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;18032300\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_barrierattr_destroy, pthread_barrierattr_init - destroy\nand initialize the barrier attributes object (<B>ADVANCED REALTIME\nTHREADS</B>) \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P>i<B>nt pthread_barrierattr_destroy(pthread_barrierattr_t *</B><I>attr</I><B>);\n<BR>int pthread_barrierattr_init(pthread_barrierattr_t *</B><I>attr</I><B>);\n</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_barrierattr_destroy</B> function shall destroy a\nbarrier attributes object. A destroyed <I>attr</I> attributes object\ncan be reinitialized using <B>pthread_barrierattr_init</B> ; the\nresults of otherwise referencing the object after it has been\ndestroyed are undefined. An implementation may cause\n<B>pthread_barrierattr_destroy</B> to set the object referenced by\n<I>attr</I> to an invalid value. \n</P>\n<P>The <B>pthread_barrierattr_init</B> function shall initialize a\nbarrier attributes object <I>attr</I> with the default value for all\nof the attributes defined by the implementation. \n</P>\n<P>Results are undefined if <B>pthread_barrierattr_init</B> is called\nspecifying an already initialized <I>attr</I> attributes object. \n</P>\n<P>After a barrier attributes object has been used to initialize one\nor more barriers, any function affecting the attributes object\n(including destruction) shall not affect any previously initialized\nbarrier. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>If successful, the <B>pthread_barrierattr_destroy</B> and\n<B>pthread_barrierattr_init</B> functions shall return zero;\notherwise, an error number shall be returned to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_barrierattr_destroy</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>attr</I> is invalid. \n\t</DD></DL>\n<P>\nThe <B>pthread_barrierattr_init</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>ENOMEM</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tInsufficient memory exists to initialize the barrier attributes\n\tobject. \n\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>The <B>pthread_barrierattr_destroy</B> and\n<B>pthread_barrierattr_init</B> functions are part of the Barriers\noption and need not be provided on all implementations. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_BARRIERS</B> to indicate\nthat these routines are implemented and may be used.</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_barrierattr_setpshared.html\"><B>pthread_barrierattr_getpshared</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_barrierattr_setpshared.html\"><B>pthread_barrierattr_setpshared</B>(3)</A>\n, the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I>. \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_barrierattr_setpshared.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_BARRIERATTR_GETPSHARED&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;11552100\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;18080400\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_barrierattr_getpshared, pthread_barrierattr_setpshared -\nget and set the process-shared attribute of the barrier attributes\nobject (<B>ADVANCED REALTIME THREADS</B>) \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_barrierattr_getpshared(const pthread_barrierattr_t\n* restrict</B> <I>attr</I><B>, int *restrict</B> <I>pshared</I><B>);\n<BR>int pthread_barrierattr_setpshared(pthread_barrierattr_t *</B><I>attr</I><B>,\nint</B> <I>pshared</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_barrierattr_getpshared</B> function shall obtain\nthe value of the <I>process-shared</I> attribute from the attributes\nobject referenced by <I>attr</I>. The <B>pthread_barrierattr_setpshared</B>\nfunction shall set the <I>process-shared</I> attribute in an\ninitialized attributes object referenced by <I>attr</I>. \n</P>\n<P>The <I>process-shared</I> attribute is set to\nPTHREAD_PROCESS_SHARED to permit a barrier to be operated upon by any\nthread that has access to the memory where the barrier is allocated.\nIf the <I>process-shared</I> attribute is PTHREAD_PROCESS_PRIVATE,\nthe barrier shall only be operated upon by threads created within the\nsame process as the thread that initialized the barrier; if threads\nof different processes attempt to operate on such a barrier, the\nbehavior is undefined. The default value of the attribute shall be\nPTHREAD_PROCESS_PRIVATE. Both constants PTHREAD_PROCESS_SHARED and\nPTHREAD_PROCESS_PRIVATE are defined in <I>&lt;pthread.h&gt;</I>. \n</P>\n<P><B>Pthreads-w32</B> defines _<B>POSIX_THREAD_PROCESS_SHARED</B> in\npthread.h as -1 to indicate that these routines are implemented but\nthat the process shared attribute is not supported.</P>\n<P>Additional attributes, their default values, and the names of the\nassociated functions to get and set those attribute values are\nimplementation-defined. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>If successful, the <B>pthread_barrierattr_getpshared</B> function\nshall return zero and store the value of the <I>process-shared</I>\nattribute of <I>attr</I> into the object referenced by the <I>pshared</I>\nparameter. Otherwise, an error number shall be returned to indicate\nthe error. \n</P>\n<P>If successful, the <B>pthread_barrierattr_setpshared</B> function\nshall return zero; otherwise, an error number shall be returned to\nindicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>These functions may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>attr</I> is invalid. \n\t</DD><DT>\n\tThe <B>pthread_barrierattr_setpshared</B> function may fail if: \n\t</DT><DT>\n\t<B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe new value specified for the <I>process-shared</I> attribute is\n\tnot one of the legal values <B>PTHREAD_PROCESS_SHARED</B> or\n\t<B>PTHREAD_PROCESS_PRIVATE</B>. \n\t</DD><DT>\n\t<B>ENOSYS</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>attr</I> was <B>PTHREAD_PROCESS_SHARED</B>\n\t(Pthreads-w32).</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>The <B>pthread_barrierattr_getpshared</B> and\n<B>pthread_barrierattr_setpshared</B> functions are part of the\nBarriers option and need not be provided on all implementations. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_BARRIERS</B> and\n<B>_POSIX_THREAD_PROCESS_SHARED</B> in pthread.h as -1 to indicate\nthat these routines are implemented and may be used, but do not\nsupport the process shared option.</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_barrier_init.html\"><B>pthread_barrier_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_barrierattr_init.html\"><B>pthread_barrierattr_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_barrierattr_init.html\"><B>pthread_barrierattr_init</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_cancel.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_CANCEL(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;12090500\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;18220000\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_cancel, pthread_setcancelstate, pthread_setcanceltype,\npthread_testcancel - thread cancellation \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_cancel(pthread_t </B><I>thread</I><B>);</B> \n</P>\n<P><B>int pthread_setcancelstate(int </B><I>state</I><B>, int\n*</B><I>oldstate</I><B>);</B> \n</P>\n<P><B>int pthread_setcanceltype(int </B><I>type</I><B>, int\n*</B><I>oldtype</I><B>);</B> \n</P>\n<P><B>void pthread_testcancel(void);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Cancellation is the mechanism by which a thread can terminate the\nexecution of another thread. More precisely, a thread can send a\ncancellation request to another thread. Depending on its settings,\nthe target thread can then either ignore the request, honor it\nimmediately, or defer it until it reaches a cancellation point. \n</P>\n<P>When a thread eventually honors a cancellation request, it\nperforms as if <B>pthread_exit(PTHREAD_CANCELED)</B> has been called\nat that point: all cleanup handlers are executed in reverse order,\ndestructor functions for thread-specific data are called, and finally\nthe thread stops executing with the return value <B>PTHREAD_CANCELED</B>.\nSee <A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> for more\ninformation. \n</P>\n<P><B>pthread_cancel</B> sends a cancellation request to the thread\ndenoted by the <I>thread</I> argument. \n</P>\n<P><B>pthread_setcancelstate</B> changes the cancellation state for\nthe calling thread -- that is, whether cancellation requests are\nignored or not. The <I>state</I> argument is the new cancellation\nstate: either <B>PTHREAD_CANCEL_ENABLE</B> to enable cancellation, or\n<B>PTHREAD_CANCEL_DISABLE</B> to disable cancellation (cancellation\nrequests are ignored). If <I>oldstate</I> is not <B>NULL</B>, the\nprevious cancellation state is stored in the location pointed to by\n<I>oldstate</I>, and can thus be restored later by another call to\n<B>pthread_setcancelstate</B>. \n</P>\n<P><B>pthread_setcanceltype</B> changes the type of responses to\ncancellation requests for the calling thread: asynchronous\n(immediate) or deferred. The <I>type</I> argument is the new\ncancellation type: either <B>PTHREAD_CANCEL_ASYNCHRONOUS</B> to\ncancel the calling thread as soon as the cancellation request is\nreceived, or <B>PTHREAD_CANCEL_DEFERRED</B> to keep the cancellation\nrequest pending until the next cancellation point. If <I>oldtype</I>\nis not <B>NULL</B>, the previous cancellation state is stored in the\nlocation pointed to by <I>oldtype</I>, and can thus be restored later\nby another call to <B>pthread_setcanceltype</B>. \n</P>\n<P><B>Pthreads-w32</B> provides two levels of support for\n<B>PTHREAD_CANCEL_ASYNCHRONOUS</B>: full and partial. Full support\nrequires an additional DLL and driver be installed on the Windows\nsystem (see the See Also section below) that allows blocked threads\nto be cancelled immediately. Partial support means that the target\nthread will not cancel until it resumes execution naturally. Partial\nsupport is provided if either the DLL or the driver are not\nautomatically detected by the pthreads-w32 library at run-time.</P>\n<P>Threads are always created by <A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A>\nwith cancellation enabled and deferred. That is, the initial\ncancellation state is <B>PTHREAD_CANCEL_ENABLE</B> and the initial\ntype is <B>PTHREAD_CANCEL_DEFERRED</B>. \n</P>\n<P>Cancellation points are those points in the program execution\nwhere a test for pending cancellation requests is performed and\ncancellation is executed if positive. The following POSIX threads\nfunctions are cancellation points: \n</P>\n<P><A HREF=\"pthread_join.html\"><B>pthread_join</B>(3)</A>\n<BR><A HREF=\"pthread_cond_init.html\"><B>pthread_cond_wait</B>(3)</A>\n<BR><A HREF=\"pthread_cond_init.html\"><B>pthread_cond_timedwait</B>(3)</A>\n<BR><A HREF=\"\"><B>pthread_testcancel</B>(3)</A> <BR><A HREF=\"sem_init.html\"><B>sem_wait</B>(3)</A>\n<BR><A HREF=\"sem_init.html\"><B>sem_timedwait</B>(3)</A> <BR><A HREF=\"pthread_kill.html\"><B>sigwait</B>(3)</A></P>\n<P><B>Pthreads-w32</B> provides two functions to enable additional\ncancellation points to be created in user functions that block on\nWin32 HANDLEs:</P>\n<P><A HREF=\"pthreadCancelableWait.html\">pthreadCancelableWait()</A>\n<BR><A HREF=\"pthreadCancelableTimedWait.html\">pthreadCancelableTimedWait()</A></P>\n<P>All other POSIX threads functions are guaranteed not to be\ncancellation points. That is, they never perform cancellation in\ndeferred cancellation mode. \n</P>\n<P><B>pthread_testcancel</B> does nothing except testing for pending\ncancellation and executing it. Its purpose is to introduce explicit\nchecks for cancellation in long sequences of code that do not call\ncancellation point functions otherwise. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P><B>pthread_cancel</B>, <B>pthread_setcancelstate</B> and\n<B>pthread_setcanceltype</B> return 0 on success and a non-zero error\ncode on error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P><B>pthread_cancel</B> returns the following error code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ESRCH</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tno thread could be found corresponding to that specified by the\n\t\t<I>thread</I> ID. \n\t\t</DD></DL>\n</DL>\n<P>\n<B>pthread_setcancelstate</B> returns the following error code on\nerror: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe <I>state</I> argument is not \n\t\t</DD></DL>\n</DL>\n<BLOCKQUOTE>\n<B>PTHREAD_CANCEL_ENABLE</B> nor <B>PTHREAD_CANCEL_DISABLE</B> \n</BLOCKQUOTE>\n<P><B>pthread_setcanceltype</B> returns the following error code on\nerror: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe <I>type</I> argument is not \n\t\t</DD></DL>\n</DL>\n<BLOCKQUOTE>\n<B>PTHREAD_CANCEL_DEFERRED</B> nor <B>PTHREAD_CANCEL_ASYNCHRONOUS</B>\n</BLOCKQUOTE>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">See Also</A></H2>\n<P><A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> ,\n<A HREF=\"pthread_cleanup_push.html\"><B>pthread_cleanup_push</B>(3)</A>\n, <A HREF=\"pthread_cleanup_pop.html\"><B>pthread_cleanup_pop</B>(3)</A>\n, Pthreads-w32 package README file 'Prerequisites' section. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Bugs</A></H2>\n<P>POSIX specifies that a number of system calls (basically, all\nsystem calls that may block, such as <A HREF=\"read.html\"><B>read</B>(2)</A>\n, <A HREF=\"write.html\"><B>write</B>(2)</A> , <A HREF=\"wait.html\"><B>wait</B>(2)</A>\n, etc.) and library functions that may call these system calls (e.g.\n<A HREF=\"fprintf.html\"><B>fprintf</B>(3)</A> ) are cancellation\npoints. <B>Pthreads-win32</B> is not integrated enough with the C\nlibrary to implement this, and thus none of the C library functions\nis a cancellation point. \n</P>\n<P>A workaround for these calls is to temporarily switch to\nasynchronous cancellation (assuming full asynchronous cancellation\nsupport is installed). So, checking for cancellation during a <B>read</B>\nsystem call, for instance, can be achieved as follows: \n</P>\n<BLOCKQUOTE><BR><BR>\n</BLOCKQUOTE>\n<PRE STYLE=\"margin-left: 1cm; margin-right: 1cm\">pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &amp;oldCancelType);\nread(fd, buffer, length);\npthread_setcanceltype(oldCancelType, NULL);</PRE>\n<HR>\n<BLOCKQUOTE><A NAME=\"toc\"></A><B>Table of Contents</B></BLOCKQUOTE>\n<UL>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Author</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">See\n\tAlso</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE><A HREF=\"#sect7\" NAME=\"toc7\">Bugs</A> \n\t</BLOCKQUOTE>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_cleanup_push.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_CLEANUP(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;20152200\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;18252600\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_cleanup_push, pthread_cleanup_pop - install and remove\ncleanup handlers \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>void pthread_cleanup_push(void (*</B><I>routine</I><B>) (void\n*), void *</B><I>arg</I><B>);</B> \n</P>\n<P><B>void pthread_cleanup_pop(int </B><I>execute</I><B>);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Cleanup handlers are functions that get called when a thread\nterminates, either by calling <A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A>\nor because of cancellation. Cleanup handlers are installed and\nremoved following a stack-like discipline. \n</P>\n<P>The purpose of cleanup handlers is to free the resources that a\nthread may hold at the time it terminates. In particular, if a thread\nexits or is cancelled while it owns a locked mutex, the mutex will\nremain locked forever and prevent other threads from executing\nnormally. The best way to avoid this is, just before locking the\nmutex, to install a cleanup handler whose effect is to unlock the\nmutex. Cleanup handlers can be used similarly to free blocks\nallocated with <A HREF=\"malloc.html\"><B>malloc</B>(3)</A> or close\nfile descriptors on thread termination. \n</P>\n<P><B>pthread_cleanup_push</B> installs the <I>routine</I> function\nwith argument <I>arg</I> as a cleanup handler. From this point on to\nthe matching <B>pthread_cleanup_pop</B>, the function <I>routine</I>\nwill be called with arguments <I>arg</I> when the thread terminates,\neither through <A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A>\nor by cancellation. If several cleanup handlers are active at that\npoint, they are called in LIFO order: the most recently installed\nhandler is called first. \n</P>\n<P><B>pthread_cleanup_pop</B> removes the most recently installed\ncleanup handler. If the <I>execute</I> argument is not 0, it also\nexecutes the handler, by calling the <I>routine</I> function with\narguments <I>arg</I>. If the <I>execute</I> argument is 0, the\nhandler is only removed but not executed. \n</P>\n<P>Matching pairs of <B>pthread_cleanup_push</B> and\n<B>pthread_cleanup_pop</B> must occur in the same function, at the\nsame level of block nesting. Actually, <B>pthread_cleanup_push</B>\nand <B>pthread_cleanup_pop</B> are macros, and the expansion of\n<B>pthread_cleanup_push</B> introduces an open brace <B>{</B> with\nthe matching closing brace <B>}</B> being introduced by the expansion\nof the matching <B>pthread_cleanup_pop</B>. \n</P>\n<H2 STYLE=\"margin-top: 0cm\"><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\">None. \n</BLOCKQUOTE>\n<H2 STYLE=\"margin-top: 0cm\"><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\">None. \n</BLOCKQUOTE>\n<H2 STYLE=\"margin-top: 0cm\"><A HREF=\"#toc5\" NAME=\"sect5\">Author</A></H2>\n<BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\">Xavier Leroy\n&lt;Xavier.Leroy@inria.fr&gt; \n</BLOCKQUOTE>\n<BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\">Modified by\nRoss Johnson for use with Pthreads-w32.</BLOCKQUOTE>\n<H2 STYLE=\"margin-top: 0cm\"><A HREF=\"#toc6\" NAME=\"sect6\">See Also</A></H2>\n<BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\"><A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A>\n, <A HREF=\"pthread_cancel.html\"><B>pthread_cancel</B>(3)</A> ,\n<A HREF=\"pthread_cancel.html\"><B>pthread_setcanceltype</B>(3)</A> . \n</BLOCKQUOTE>\n<H2 STYLE=\"margin-top: 0cm\"><A HREF=\"#toc7\" NAME=\"sect7\">Example</A></H2>\n<BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\">Here is how\nto lock a mutex <I>mut</I> in such a way that it will be unlocked if\nthe thread is canceled while <I>mut</I> is locked: \n</BLOCKQUOTE>\n<PRE>pthread_cleanup_push(pthread_mutex_unlock, (void *) &amp;mut);\npthread_mutex_lock(&amp;mut);\n/* do some work */\npthread_mutex_unlock(&amp;mut);\npthread_cleanup_pop(0);</PRE><BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\">\nEquivalently, the last two lines can be replaced by \n</BLOCKQUOTE>\n<PRE STYLE=\"margin-bottom: 0.5cm\">pthread_cleanup_pop(1);</PRE><BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\">\nNotice that the code above is safe only in deferred cancellation mode\n(see <A HREF=\"pthread_cancel.html\"><B>pthread_setcanceltype</B>(3)</A>\n). In asynchronous cancellation mode, a cancellation can occur\nbetween <B>pthread_cleanup_push</B> and <B>pthread_mutex_lock</B>, or\nbetween <B>pthread_mutex_unlock</B> and <B>pthread_cleanup_pop</B>,\nresulting in both cases in the thread trying to unlock a mutex not\nlocked by the current thread. This is the main reason why\nasynchronous cancellation is difficult to use. \n</BLOCKQUOTE>\n<BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\">If the code\nabove must also work in asynchronous cancellation mode, then it must\nswitch to deferred mode for locking and unlocking the mutex: \n</BLOCKQUOTE>\n<PRE>pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &amp;oldtype);\npthread_cleanup_push(pthread_mutex_unlock, (void *) &amp;mut);\npthread_mutex_lock(&amp;mut);\n/* do some work */\npthread_cleanup_pop(1);\npthread_setcanceltype(oldtype, NULL);</PRE>\n<HR>\n<BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\"><A NAME=\"toc\"></A>\n<B>Table of Contents</B></BLOCKQUOTE>\n<UL>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Author</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">See\n\tAlso</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Example</A>\n\t\t</BLOCKQUOTE>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_cond_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_COND(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;16454400\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;19004700\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_cond_init, pthread_cond_destroy, pthread_cond_signal,\npthread_cond_broadcast, pthread_cond_wait, pthread_cond_timedwait -\noperations on conditions \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>pthread_cond_t </B><I>cond</I> <B>= PTHREAD_COND_INITIALIZER;</B>\n</P>\n<P><B>int pthread_cond_init(pthread_cond_t *</B><I>cond</I><B>,\npthread_condattr_t *</B><I>cond_attr</I><B>);</B> \n</P>\n<P><B>int pthread_cond_signal(pthread_cond_t *</B><I>cond</I><B>);</B>\n</P>\n<P><B>int pthread_cond_broadcast(pthread_cond_t *</B><I>cond</I><B>);</B>\n</P>\n<P><B>int pthread_cond_wait(pthread_cond_t *</B><I>cond</I><B>,\npthread_mutex_t *</B><I>mutex</I><B>);</B> \n</P>\n<P><B>int pthread_cond_timedwait(pthread_cond_t *</B><I>cond</I><B>,\npthread_mutex_t *</B><I>mutex</I><B>, const struct timespec\n*</B><I>abstime</I><B>);</B> \n</P>\n<P><B>int pthread_cond_destroy(pthread_cond_t *</B><I>cond</I><B>);</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>A condition (short for ‘‘condition variable’’) is a\nsynchronization device that allows threads to suspend execution and\nrelinquish the processors until some predicate on shared data is\nsatisfied. The basic operations on conditions are: signal the\ncondition (when the predicate becomes true), and wait for the\ncondition, suspending the thread execution until another thread\nsignals the condition. \n</P>\n<P>A condition variable must always be associated with a mutex, to\navoid the race condition where a thread prepares to wait on a\ncondition variable and another thread signals the condition just\nbefore the first thread actually waits on it. \n</P>\n<P><B>pthread_cond_init</B> initializes the condition variable <I>cond</I>,\nusing the condition attributes specified in <I>cond_attr</I>, or\ndefault attributes if <I>cond_attr</I> is <B>NULL</B>. \n</P>\n<P>Variables of type <B>pthread_cond_t</B> can also be initialized\nstatically, using the constant <B>PTHREAD_COND_INITIALIZER</B>. In\nthe <B>Pthreads-w32</B> implementation, an application should still\ncall <B>pthread_cond_destroy</B> at some point to ensure that any\nresources consumed by the condition variable are released.</P>\n<P><B>pthread_cond_signal</B> restarts one of the threads that are\nwaiting on the condition variable <I>cond</I>. If no threads are\nwaiting on <I>cond</I>, nothing happens. If several threads are\nwaiting on <I>cond</I>, exactly one is restarted, but it is not\nspecified which. \n</P>\n<P><B>pthread_cond_broadcast</B> restarts all the threads that are\nwaiting on the condition variable <I>cond</I>. Nothing happens if no\nthreads are waiting on <I>cond</I>. \n</P>\n<P><B>pthread_cond_wait</B> atomically unlocks the <I>mutex</I> (as\nper <B>pthread_unlock_mutex</B>) and waits for the condition variable\n<I>cond</I> to be signalled. The thread execution is suspended and\ndoes not consume any CPU time until the condition variable is\nsignalled. The <I>mutex</I> must be locked by the calling thread on\nentrance to <B>pthread_cond_wait</B>. Before returning to the calling\nthread, <B>pthread_cond_wait</B> re-acquires <I>mutex</I> (as per\n<B>pthread_lock_mutex</B>). \n</P>\n<P>Unlocking the mutex and suspending on the condition variable is\ndone atomically. Thus, if all threads always acquire the mutex before\nsignalling the condition, this guarantees that the condition cannot\nbe signalled (and thus ignored) between the time a thread locks the\nmutex and the time it waits on the condition variable. \n</P>\n<P><B>pthread_cond_timedwait</B> atomically unlocks <I>mutex</I> and\nwaits on <I>cond</I>, as <B>pthread_cond_wait</B> does, but it also\nbounds the duration of the wait. If <I>cond</I> has not been\nsignalled within the amount of time specified by <I>abstime</I>, the\nmutex <I>mutex</I> is re-acquired and <B>pthread_cond_timedwait</B>\nreturns the error <B>ETIMEDOUT</B>. The <I>abstime</I> parameter\nspecifies an absolute time, with the same origin as <A HREF=\"time.html\"><B>time</B>(2)</A>\nand <A HREF=\"gettimeofday.html\"><B>gettimeofday</B>(2)</A>. \n</P>\n<P><B>pthread_cond_destroy</B> destroys a condition variable, freeing\nthe resources it might hold. No threads must be waiting on the\ncondition variable on entrance to <B>pthread_cond_destroy</B>.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P><B>pthread_cond_wait</B> and <B>pthread_cond_timedwait</B> are\ncancellation points. If a thread is cancelled while suspended in one\nof these functions, the thread immediately resumes execution, then\nlocks again the <I>mutex</I> argument to <B>pthread_cond_wait</B> and\n<B>pthread_cond_timedwait</B>, and finally executes the cancellation.\nConsequently, cleanup handlers are assured that <I>mutex</I> is\nlocked when they are called. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Async-signal Safety</A></H2>\n<P>The condition functions are not async-signal safe, and should not\nbe called from a signal handler. In particular, calling\n<B>pthread_cond_signal</B> or <B>pthread_cond_broadcast</B> from a\nsignal handler may deadlock the calling thread. \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Return Value</A></H2>\n<P>All condition variable functions return 0 on success and a\nnon-zero error code on error. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Errors</A></H2>\n<P><B>pthread_cond_init</B>, <B>pthread_cond_signal</B>,\n<B>pthread_cond_broadcast</B>, and <B>pthread_cond_wait</B> never\nreturn an error code. \n</P>\n<P>The <B>pthread_cond_init</B> function returns the following error\ncodes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tThe <I>cond</I> argument is invalid. \n\t\t</DD><DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<B>ENOMEM</B> \n\t\t</DT></DL>\n</DL>\n<BLOCKQUOTE STYLE=\"margin-left: 4cm\">\nThere was not enough memory to allocate the condition variable. \n</BLOCKQUOTE>\n<P>The <B>pthread_cond_signal</B> function returns the following\nerror codes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tThe <I>cond</I> argument is invalid. \n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>pthread_cond_broadcast</B> function returns the following\nerror codes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tThe <I>cond</I> argument is invalid. \n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>pthread_cond_wait</B> function returns the following error\ncodes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tThe <I>cond</I> argument is invalid. \n\t\t</DD><DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<B>ENOMEM</B> \n\t\t</DT></DL>\n</DL>\n<BLOCKQUOTE STYLE=\"margin-left: 4cm\">\nThere was not enough memory to allocate the statically initialised\ncondition variable. Statically initialised condition variables are\ndynamically allocated by the first thread to wait on them.</BLOCKQUOTE>\n<P>The <B>pthread_cond_timedwait</B> function returns the following\nerror codes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT></DL>\n</DL>\n<P STYLE=\"margin-left: 2cm\">\nThe <I>cond</I> argument is invalid. \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ETIMEDOUT</B>\n\t\t\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tThe condition variable was not signalled before the timeout\n\t\tspecified by <I>abstime</I> \n\t\t</DD><DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<B>ENOMEM</B> \n\t\t</DT></DL>\n</DL>\n<BLOCKQUOTE STYLE=\"margin-left: 4cm\">\nThere was not enough memory to allocate the statically initialised\ncondition variable. Statically initialised condition variables are\ndynamically allocated by the first thread to wait on them. \n</BLOCKQUOTE>\n<P>The <B>pthread_cond_destroy</B> function returns the following\nerror code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT></DL>\n</DL>\n<P STYLE=\"margin-left: 2cm; margin-right: 1cm\">\nThe <I>cond</I> argument is invalid. \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EBUSY</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tSome threads are currently waiting on <I>cond</I>. \n\t\t</DD></DL>\n</DL>\n<H2>\n<A HREF=\"#toc7\" NAME=\"sect7\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">See Also</A></H2>\n<P><A HREF=\"pthread_condattr_init.html\"><B>pthread_condattr_init</B>(3)</A>\n, <A HREF=\"pthread_mutex_lock.html\"><B>pthread_mutex_lock</B>(3)</A>\n, <A HREF=\"pthread_mutex_unlock.html\"><B>pthread_mutex_unlock</B>(3)</A>\n, <A HREF=\"pthread_cancel.html\"><B>pthread_cancel(3)</B></A>. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">Example</A></H2>\n<P>Consider two shared variables <I>x</I> and <I>y</I>, protected by\nthe mutex <I>mut</I>, and a condition variable <I>cond</I> that is to\nbe signaled whenever <I>x</I> becomes greater than <I>y</I>. \n</P>\n<PRE STYLE=\"margin-left: 1cm; margin-right: 1cm\">int x,y;\npthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;\npthread_cond_t cond = PTHREAD_COND_INITIALIZER;</PRE><BLOCKQUOTE>\nWaiting until <I>x</I> is greater than <I>y</I> is performed as\nfollows: \n</BLOCKQUOTE>\n<PRE STYLE=\"margin-left: 1.01cm\">pthread_mutex_lock(&amp;mut);\nwhile (x &lt;= y) {\n        pthread_cond_wait(&amp;cond, &amp;mut);\n}\n/* operate on x and y */\npthread_mutex_unlock(&amp;mut);</PRE><BLOCKQUOTE STYLE=\"margin-left: 3.01cm\">\nModifications on <I>x</I> and <I>y</I> that may cause <I>x</I> to\nbecome greater than <I>y</I> should signal the condition if needed: \n</BLOCKQUOTE>\n<PRE STYLE=\"margin-left: 1.01cm\">pthread_mutex_lock(&amp;mut);\n/* modify x and y */\nif (x &gt; y) pthread_cond_broadcast(&amp;cond);\npthread_mutex_unlock(&amp;mut);</PRE><BLOCKQUOTE STYLE=\"margin-left: 3.01cm\">\nIf it can be proved that at most one waiting thread needs to be waken\nup (for instance, if there are only two threads communicating through\n<I>x</I> and <I>y</I>), <B>pthread_cond_signal</B> can be used as a\nslightly more efficient alternative to <B>pthread_cond_broadcast</B>.\nIf in doubt, use <B>pthread_cond_broadcast</B>. \n</BLOCKQUOTE>\n<BLOCKQUOTE STYLE=\"margin-left: 3.01cm\">To wait for <I>x</I> to\nbecome greater than <I>y</I> with a timeout of 5 seconds, do: \n</BLOCKQUOTE>\n<PRE STYLE=\"margin-left: 1.01cm\">struct timeval now;\nstruct timespec timeout;\nint retcode;\npthread_mutex_lock(&amp;mut);\ngettimeofday(&amp;now);\ntimeout.tv_sec = now.tv_sec + 5;\ntimeout.tv_nsec = now.tv_usec * 1000;\nretcode = 0;\nwhile (x &lt;= y &amp;&amp; retcode != ETIMEDOUT) {\n        retcode = pthread_cond_timedwait(&amp;cond, &amp;mut, &amp;timeout);\n}\nif (retcode == ETIMEDOUT) {\n        /* timeout occurred */\n} else {\n        /* operate on x and y */\n}\npthread_mutex_unlock(&amp;mut);</PRE>\n<HR>\n<BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\"><A NAME=\"toc\"></A>\n<B>Table of Contents</B></BLOCKQUOTE>\n<UL>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Async-signal\n\tSafety</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Return\n\tValue</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Errors</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Author</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm; margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">See\n\tAlso</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">Example</A>\n\t\t</BLOCKQUOTE>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_condattr_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_CONDATTR(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;16375600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050504;16570300\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_condattr_init, pthread_condattr_destroy - condition\ncreation \n</P>\n<P>attributes \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_condattr_init(pthread_condattr_t *</B><I>attr</I><B>);</B>\n</P>\n<P><B>int pthread_condattr_destroy(pthread_condattr_t *</B><I>attr</I><B>);</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Condition attributes can be specified at condition creation time,\nby passing a condition attribute object as second argument to\n<A HREF=\"pthread_cond_init.html\"><B>pthread_cond_init</B>(3)</A> .\nPassing <B>NULL</B> is equivalent to passing a condition attribute\nobject with all attributes set to their default values. \n</P>\n<P><B>pthread_condattr_init</B> initializes the condition attribute\nobject <I>attr</I> and fills it with default values for the\nattributes. <B>pthread_condattr_destroy</B> destroys a condition\nattribute object, which must not be reused until it is reinitialized.</P>\n<P><B>Pthreads-w32</B> defines _<B>POSIX_THREAD_PROCESS_SHARED</B> in\npthread.h as -1 to indicate that the attribute routines are\nimplemented but that the process shared attribute is not supported.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>All condition variable functions return 0 on success and a\nnon-zero error code on error.</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_condattr_init</B> function returns the following\nerror code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ENOMEM</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tThe was insufficient memory to create the attribute.<SPAN STYLE=\"font-weight: medium\">\n\t\t</SPAN>\n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>pthread_condattr_destroy</B> function returns the following\nerror code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tThe <I>attr</I> argument is not valid.<SPAN STYLE=\"font-weight: medium\">\n\t\t</SPAN>\n\t\t</DD></DL>\n</DL>\n<H2>\n<A HREF=\"#toc5\" NAME=\"sect5\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">See Also</A></H2>\n<P><A HREF=\"pthread_cond_init.html\"><B>pthread_cond_init</B>(3)</A> .\n</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Author</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">See Also</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_condattr_setpshared.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_CONDATTR_GETPSHARED&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;17542300\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;18293100\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_condattr_getpshared, pthread_condattr_setpshared - get and\nset the process-shared condition variable attributes \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_condattr_getpshared(const pthread_condattr_t\n*restrict</B> <I>attr</I><B>, int *restrict</B> <I>pshared</I><B>);\n<BR>int pthread_condattr_setpshared(pthread_condattr_t *</B><I>attr</I><B>,\nint</B> <I>pshared</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_condattr_getpshared</B> function shall obtain the\nvalue of the <I>process-shared</I> attribute from the attributes\nobject referenced by <I>attr</I>. The <B>pthread_condattr_setpshared</B>\nfunction shall set the <I>process-shared</I> attribute in an\ninitialized attributes object referenced by <I>attr</I>. \n</P>\n<P>The <I>process-shared</I> attribute is set to\n<B>PTHREAD_PROCESS_SHARED</B> to permit a condition variable to be\noperated upon by any thread that has access to the memory where the\ncondition variable is allocated, even if the condition variable is\nallocated in memory that is shared by multiple processes. If the\n<I>process-shared</I> attribute is <B>PTHREAD_PROCESS_PRIVATE</B>,\nthe condition variable shall only be operated upon by threads created\nwithin the same process as the thread that initialized the condition\nvariable; if threads of differing processes attempt to operate on\nsuch a condition variable, the behavior is undefined. The default\nvalue of the attribute is <B>PTHREAD_PROCESS_PRIVATE</B>. \n</P>\n<P><B>Pthreads-w32</B> defines _<B>POSIX_THREAD_PROCESS_SHARED</B> in\npthread.h as -1 to indicate that these routines are implemented but\nthat the process shared attribute is not supported.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>If successful, the <B>pthread_condattr_setpshared</B> function\nshall return zero; otherwise, an error number shall be returned to\nindicate the error. \n</P>\n<P>If successful, the <B>pthread_condattr_getpshared</B> function\nshall return zero and store the value of the <I>process-shared</I>\nattribute of <I>attr</I> into the object referenced by the <I>pshared</I>\nparameter. Otherwise, an error number shall be returned to indicate\nthe error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_condattr_getpshared</B> and\n<B>pthread_condattr_setpshared</B> functions may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>attr</I> is invalid. \n\t</DD></DL>\n<P>\nThe <B>pthread_condattr_setpshared</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe new value specified for the attribute is outside the range of\n\tlegal values for that attribute. \n\t</DD><DT>\n\t<B>ENOSYS</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>attr</I> was <B>PTHREAD_PROCESS_SHARED</B>\n\t(Pthreads-w32).</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_THREAD_PROCESS_SHARED</B> in\npthread.h as -1 to indicate that these routines are implemented and\nmay be used, but do not support the process shared option.</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A> <B>,</B>\n<A HREF=\"pthread_cond_init.html\"><B>pthread_cond_destroy</B>(3)</A> <B>,</B>\n<A HREF=\"pthread_condattr_init.html\"><B>pthread_condattr_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_destroy</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_create.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_CREATE(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;19394700\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050504;20140200\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_create - create a new thread \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_create(pthread_t * </B><I>thread</I><B>,\npthread_attr_t * </B><I>attr</I><B>, void * (*</B><I>start_routine</I><B>)(void\n*), void * </B><I>arg</I><B>);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P><B>pthread_create</B> creates a new thread of control that\nexecutes concurrently with the calling thread. The new thread applies\nthe function <I>start_routine</I> passing it <I>arg</I> as first\nargument. The new thread terminates either explicitly, by calling\n<A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> , or\nimplicitly, by returning from the <I>start_routine</I> function. The\nlatter case is equivalent to calling <A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A>\nwith the result returned by <I>start_routine</I> as exit code. \n</P>\n<P>The initial signal state of the new thread is inherited from it's\ncreating thread and there are no pending signals. <B>Pthreads-w32</B>\ndoes not yet implement signals.</P>\n<P>The <I>attr</I> argument specifies thread attributes to be applied\nto the new thread. See <A HREF=\"pthread_attr_init.html\"><B>pthread_attr_init</B>(3)</A>\nfor a complete list of thread attributes. The <I>attr</I> argument\ncan also be <B>NULL</B>, in which case default attributes are used:\nthe created thread is joinable (not detached) and has default (non\nreal-time) scheduling policy. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>On success, the identifier of the newly created thread is stored\nin the location pointed by the <I>thread</I> argument, and a 0 is\nreturned. On error, a non-zero error code is returned. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<DL>\n\t<DT><B>EAGAIN</B> \n\t</DT><DL>\n\t\t<DL>\n\t\t\t<DT>\n\t\t\tNot enough system resources to create a process for the new\n\t\t\tthread, or<BR>more than <B>PTHREAD_THREADS_MAX</B> threads are\n\t\t\talready active. \n\t\t\t</DT></DL>\n\t</DL>\n</DL>\n<H2>\n<A HREF=\"#toc5\" NAME=\"sect5\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">See Also</A></H2>\n<P><A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> ,\n<A HREF=\"pthread_join.html\"><B>pthread_join</B>(3)</A> ,\n<A HREF=\"pthread_detach.html\"><B>pthread_detach</B>(3)</A> ,\n<A HREF=\"pthread_attr_init.html\"><B>pthread_attr_init</B>(3)</A> . \n</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Author</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">See Also</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_delay_np.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_DELAY_NP manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;322600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;21371500\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P STYLE=\"font-weight: medium\">pthread_delay_np – suspend the\nthread for a specified period</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_delay_np (const struct timespec *</B>interval<B>);</B></P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P><B>pthread_delay_np</B> causes a thread to delay execution for a\nspecific period of time. This period ends at the current time plus\nthe specified interval. The routine will not return before the end of\nthe period is reached, but may return an arbitrary amount of time\nafter the period has gone by. This can be due to system load, thread\npriorities, and system timer granularity.</P>\n<P>Specifying an interval of zero (0) seconds and zero (0)\nnanoseconds is allowed and can be used to force the thread to give up\nthe processor or to deliver a pending cancellation request.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P><B>pthread_delay_np </B>is a cancellation point.</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Return Value</A></H2>\n<P>If an error condition occurs, <B>pthread_delay_np</B> returns an\ninteger value indicating the type of error.</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<P>The <B>pthread_delay_np</B> function returns the following error\ncode on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT></DL>\n</DL>\n<P STYLE=\"margin-left: 2cm\">\nThe value specified by interval is invalid.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">Author</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_detach.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_DETACH(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 2.0  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;18010700\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20060408;9255600\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_detach - put a running thread in the detached state \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_detach(pthread_t </B><I>th</I><B>);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P><B>pthread_detach</B> puts the thread <I>th</I> in the detached\nstate. This guarantees that the resources consumed by <I>th</I> will\nbe freed immediately when <I>th</I> terminates. However, this\nprevents other threads from synchronizing on the termination of <I>th</I>\nusing <B>pthread_join</B>. If, when <B>pthread_detach</B> is called,\n<I>th</I> has already terminated, all of <I>th</I>'s remaining\nresources will be freed.</P>\n<P>A thread can be created initially in the detached state, using the\n<B>detachstate</B> attribute to <A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A>\n. In contrast, <B>pthread_detach</B> applies to threads created in\nthe joinable state, and which need to be put in the detached state\nlater. \n</P>\n<P>After <B>pthread_detach</B> completes, subsequent attempts to\nperform <B>pthread_join</B> on <I>th</I> will fail. If another thread\nis already joining the thread <I>th</I> at the time <B>pthread_detach</B>\nis called, <I>th</I> will be detached and <B>pthread_join</B> will\neventually return when <I>th</I> terminates but may not return with\n<I>th</I>'s correct return code. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>On success, 0 is returned. On error, a non-zero error code is\nreturned. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<DL>\n\t<DT><B>ESRCH</B> \n\t</DT><DD>\n\tNo thread could be found corresponding to that specified by <I>th</I>\n\t\t</DD><DT>\n\t<B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tthe thread <I>th</I> is already in the detached state \n\t</DD></DL>\n<H2>\n<A HREF=\"#toc5\" NAME=\"sect5\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with Pthreads-w32.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">See Also</A></H2>\n<P><A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A> ,\n<A HREF=\"pthread_join.html\"><B>pthread_join</B>(3)</A> ,\n<A HREF=\"pthread_attr_init.html\"><B>pthread_attr_setdetachstate</B>(3)</A>\n</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Author</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">See Also</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_equal.html",
    "content": "<!-- manual page source format generated by PolyglotMan v3.2, -->\n<!-- available at http://polyglotman.sourceforge.net/ -->\n\n<html>\n<head>\n<title>PTHREAD_EQUAL(3) manual page</title>\n</head>\n<body bgcolor='white'>\n<a href='#toc'>Table of Contents</a><p>\n\n<p> \n<h2><a name='sect0' href='#toc0'>Name</a></h2>\npthread_equal - compare two thread identifiers \n<p> \n<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>\n<b>#include &lt;pthread.h&gt;</b>\n\n<p> <b>int pthread_equal(pthread_t </b><i>thread1</i><b>, pthread_t </b><i>thread2</i><b>);</b> \n<p> \n<h2><a name='sect2' href='#toc2'>Description</a></h2>\n<b>pthread_equal</b>\ndetermines if two thread identifiers refer to the same thread. \n<p> \n<h2><a name='sect3' href='#toc3'>Return Value</a></h2>\nA\nnon-zero value is returned if  <i>thread1</i> and  <i>thread2</i> refer to the same thread.\nOtherwise, 0 is returned. \n<p> \n<h2><a name='sect4' href='#toc4'>Author</a></h2>\nXavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n<p> \n<h2><a name='sect5' href='#toc5'>See Also</a></h2>\n<a href='file:pthread_self.html'><b>pthread_self</b>(3)</a>\n.\n<p>\n\n<hr><p>\n<a name='toc'><b>Table of Contents</b></a><p>\n<ul>\n<li><a name='toc0' href='#sect0'>Name</a></li>\n<li><a name='toc1' href='#sect1'>Synopsis</a></li>\n<li><a name='toc2' href='#sect2'>Description</a></li>\n<li><a name='toc3' href='#sect3'>Return Value</a></li>\n<li><a name='toc4' href='#sect4'>Author</a></li>\n<li><a name='toc5' href='#sect5'>See Also</a></li>\n</ul>\n</body>\n</html>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_exit.html",
    "content": "<!-- manual page source format generated by PolyglotMan v3.2, -->\n<!-- available at http://polyglotman.sourceforge.net/ -->\n\n<html>\n<head>\n<title>PTHREAD_EXIT(3) manual page</title>\n</head>\n<body bgcolor='white'>\n<a href='#toc'>Table of Contents</a><p>\n\n<p> \n<h2><a name='sect0' href='#toc0'>Name</a></h2>\npthread_exit - terminate the calling thread \n<p> \n<h2><a name='sect1' href='#toc1'>Synopsis</a></h2>\n<b>#include &lt;pthread.h&gt;</b>\n\n<p> <b>void pthread_exit(void *</b><i>retval</i><b>);</b> \n<p> \n<h2><a name='sect2' href='#toc2'>Description</a></h2>\n<b>pthread_exit</b> terminates the\nexecution of the calling thread. All cleanup handlers that have been set\nfor the calling thread with <a href='file:pthread_cleanup_push.html'><b>pthread_cleanup_push</b>(3)</a>\n are executed in reverse\norder (the most recently pushed handler is executed first). Finalization\nfunctions for thread-specific data are then called for all keys that have\nnon- <b>NULL</b> values associated with them in the calling thread (see <a href='file:pthread_key_create.html'><b>pthread_key_create</b>(3)</a>\n).\nFinally, execution of the calling thread is stopped. \n<p> The  <i>retval</i> argument\nis the return value of the thread. It can be consulted from another thread\nusing  <a href='file:pthread_join.html'><b>pthread_join</b>(3)</a>\n. \n<p> \n<h2><a name='sect3' href='#toc3'>Return Value</a></h2>\nThe  <b>pthread_exit</b> function never returns.\n\n<p> \n<h2><a name='sect4' href='#toc4'>Author</a></h2>\nXavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n<p> \n<h2><a name='sect5' href='#toc5'>See Also</a></h2>\n<a href='file:pthread_create.html'><b>pthread_create</b>(3)</a>\n, <a href='file:pthread_join.html'><b>pthread_join</b>(3)</a>\n.\n<p>\n\n<hr><p>\n<a name='toc'><b>Table of Contents</b></a><p>\n<ul>\n<li><a name='toc0' href='#sect0'>Name</a></li>\n<li><a name='toc1' href='#sect1'>Synopsis</a></li>\n<li><a name='toc2' href='#sect2'>Description</a></li>\n<li><a name='toc3' href='#sect3'>Return Value</a></li>\n<li><a name='toc4' href='#sect4'>Author</a></li>\n<li><a name='toc5' href='#sect5'>See Also</a></li>\n</ul>\n</body>\n</html>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_getunique_np.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_GETW32THREADHANDLE_NP manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 3.2  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;322600\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20110326;18290500\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n\t<STYLE TYPE=\"text/css\">\n\t<!--\n\t\tH4.cjk { font-family: \"AR PL UMing CN\" }\n\t\tH4.ctl { font-family: \"Lohit Devanagari\" }\n\t\tH2.cjk { font-family: \"AR PL UMing CN\" }\n\t\tH2.ctl { font-family: \"Lohit Devanagari\" }\n\t-->\n\t</STYLE>\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4 CLASS=\"western\">POSIX Threads for Windows – REFERENCE -\n<A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2 CLASS=\"western\"><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P STYLE=\"font-weight: normal\">pthread_getunique_np – get the\nunique sequence number associated with a thread</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>unsigned long long pthread_getunique_np(pthread_t</B> <I>thread</I><B>);</B></P>\n<H2 CLASS=\"western\"><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Returns the <B>unique </B><SPAN STYLE=\"font-weight: normal\">64 bit\nsequence number</SPAN> assigned to <I>thread</I>.</P>\n<P>In <B>Pthreads-win32:</B></P>\n<UL>\n\t<LI><P>the value returned is not reused after the thread terminates\n\tso it is unique for the life of the process</P>\n\t<LI><P>Windows native threads may obtain their own POSIX thread\n\tsequence number by first retrieving their <B>pthread_t</B> handle\n\tvia <B>pthread_self</B> to use as the <I>thread</I> argument.</P>\n</UL>\n<P>This function was added for source code compatibility with some\nother POSIX threads implementations.</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P>None.</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc4\" NAME=\"sect4\">Return Value</A></H2>\n<P><B>pthread_getunique_np</B> returns the unique sequence number for\n<I>thread</I>.</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<P>None.</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">Author</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_getw32threadhandle_np.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_GETW32THREADHANDLE_NP manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;322600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;21523500\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P STYLE=\"font-weight: medium\">pthread_getw32threadhandle_np – get\nthe Win32 thread handle associated with a thread</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>HANDLE pthread_getw32threadhandle_np(pthread_t</B> <I>thread</I><B>);</B></P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Returns the Win32 native thread <B>HANDLE</B> that the POSIX\nthread <I>thread</I> is running as.</P>\n<P>Applications can use the Win32 handle to set Win32 specific\nattributes of the thread.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P>None.</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Return Value</A></H2>\n<P><B>pthread_getw32threadhandle_np</B> returns the Win32 native\nthread <B>HANDLE</B> for the specified POSIX thread <I>thread</I>.</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<P>None.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">Author</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_join.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_JOIN(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;18232700\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050504;18421400\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_join - wait for termination of another thread \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_join(pthread_t </B><I>th</I><B>, void\n**</B><I>thread_return</I><B>);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P><B>pthread_join</B> suspends the execution of the calling thread\nuntil the thread identified by <I>th</I> terminates, either by\ncalling <A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> or by\nbeing cancelled. \n</P>\n<P>If <I>thread_return</I> is not <B>NULL</B>, the return value of <I>th</I>\nis stored in the location pointed to by <I>thread_return</I>. The\nreturn value of <I>th</I> is either the argument it gave to\n<A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> , or\n<B>PTHREAD_CANCELED</B> if <I>th</I> was cancelled. \n</P>\n<P>The joined thread <B>th</B> must be in the joinable state: it must\nnot have been detached using <A HREF=\"pthread_detach.html\"><B>pthread_detach</B>(3)</A>\nor the <B>PTHREAD_CREATE_DETACHED</B> attribute to <A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A>\n. \n</P>\n<P>When a joinable thread terminates, its memory resources (thread\ndescriptor and stack) are not deallocated until another thread\nperforms <B>pthread_join</B> on it. Therefore, <B>pthread_join</B>\nmust be called once for each joinable thread created to avoid memory\nleaks. \n</P>\n<P>At most one thread can wait for the termination of a given thread.\nCalling <B>pthread_join</B> on a thread <I>th</I> on which another\nthread is already waiting for termination returns an error. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P><B>pthread_join</B> is a cancellation point. If a thread is\ncancelled while suspended in <B>pthread_join</B>, the thread\nexecution resumes immediately and the cancellation is executed\nwithout waiting for the <I>th</I> thread to terminate. If\ncancellation occurs during <B>pthread_join</B>, the <I>th</I> thread\nremains not joined. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Return Value</A></H2>\n<P>On success, the return value of <I>th</I> is stored in the\nlocation pointed to by <I>thread_return</I>, and 0 is returned. On\nerror, a non-zero error code is returned. \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<DL>\n\t<DT><B>ESRCH</B> \n\t</DT><DD>\n\tNo thread could be found corresponding to that specified by <I>th</I>.\n\t\t</DD><DT>\n\t<B>EINVAL</B> \n\t</DT><DD>\n\tThe <I>th</I> thread has been detached. \n\t</DD><DT>\n\t<B>EINVAL</B> \n\t</DT><DD>\n\tAnother thread is already waiting on termination of <I>th</I>. \n\t</DD><DT>\n\t<B>EDEADLK</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe <I>th</I> argument refers to the calling thread. \n\t</DD></DL>\n<H2>\n<A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">See Also</A></H2>\n<P><A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> ,\n<A HREF=\"pthread_detach.html\"><B>pthread_detach</B>(3)</A> ,\n<A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A> ,\n<A HREF=\"pthread_attr_setdetachstate.html\"><B>pthread_attr_setdetachstate</B>(3)</A>\n, <A HREF=\"pthread_cleanup_push.html\"><B>pthread_cleanup_push</B>(3)</A>\n, <A HREF=\"pthread_key_create.html\"><B>pthread_key_create</B>(3)</A>\n. \n</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Author</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect7\" NAME=\"toc7\">See Also</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_key_create.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_SPECIFIC(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;18425400\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050509;18220200\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_key_create, pthread_key_delete, pthread_setspecific,\npthread_getspecific - management of thread-specific data \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_key_create(pthread_key_t *</B><I>key</I><B>, void\n(*</B><I>destr_function</I><B>) (void *));</B> \n</P>\n<P><B>int pthread_key_delete(pthread_key_t </B><I>key</I><B>);</B> \n</P>\n<P><B>int pthread_setspecific(pthread_key_t </B><I>key</I><B>, const\nvoid *</B><I>pointer</I><B>);</B> \n</P>\n<P><B>void * pthread_getspecific(pthread_key_t </B><I>key</I><B>);</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Programs often need global or static variables that have different\nvalues in different threads. Since threads share one memory space,\nthis cannot be achieved with regular variables. Thread-specific data\nis the POSIX threads answer to this need. \n</P>\n<P>Each thread possesses a private memory block, the thread-specific\ndata area, or TSD area for short. This area is indexed by TSD keys.\nThe TSD area associates values of type <B>void *</B> to TSD keys. TSD\nkeys are common to all threads, but the value associated with a given\nTSD key can be different in each thread. \n</P>\n<P>For concreteness, the TSD areas can be viewed as arrays of <B>void\n*</B> pointers, TSD keys as integer indices into these arrays, and\nthe value of a TSD key as the value of the corresponding array\nelement in the calling thread. \n</P>\n<P>When a thread is created, its TSD area initially associates <B>NULL</B>\nwith all keys. \n</P>\n<P><B>pthread_key_create</B> allocates a new TSD key. The key is\nstored in the location pointed to by <I>key</I>. There is a limit of\n<B>PTHREAD_KEYS_MAX</B> on the number of keys allocated at a given\ntime. The value initially associated with the returned key is <B>NULL</B>\nin all currently executing threads. \n</P>\n<P>The <I>destr_function</I> argument, if not <B>NULL</B>, specifies\na destructor function associated with the key. When a thread\nterminates via <B>pthread_exit</B> or by cancellation, <I>destr_function</I>\nis called with arguments the value associated with the key in that\nthread. The <I>destr_function</I> is not called if that value is <B>NULL</B><SPAN STYLE=\"font-weight: medium\">\nor the key has been deleted</SPAN>. The order in which destructor\nfunctions are called at thread termination time is unspecified. \n</P>\n<P>Before the destructor function is called, the <B>NULL</B> value is\nassociated with the key in the current thread. A destructor function\nmight, however, re-associate non- <B>NULL</B> values to that key or\nsome other key. To deal with this, if after all the destructors have\nbeen called for all non- <B>NULL</B> values, there are still some\nnon- <B>NULL</B> values with associated destructors, then the process\nis repeated.</P>\n<P><B>pthread_key_delete</B> deallocates a TSD key. It does not check\nwhether non- <B>NULL</B> values are associated with that key in the\ncurrently executing threads, nor call the destructor function\nassociated with the key. \n</P>\n<P><B>pthread_setspecific</B> changes the value associated with <I>key</I>\nin the calling thread, storing the given <I>pointer</I> instead. \n</P>\n<P><B>pthread_getspecific</B> returns the value currently associated\nwith <I>key</I> in the calling thread. \n</P>\n<P>The routines <B>pthread_setspecific</B>, <B>pthread_getspecific</B>,\nand <B>pthread_key_delete</B> can be called from <I>destr_function</I>\ntargeting any valid key including the key on which <I>destr_function</I>\nis currently operating. If <B>pthread_getspecific</B> is called on\nthe key whose thread specific data is being destroyed, the value NULL\nis returned, unless <B>pthread_setspecific</B> was called previously\non that key from within <I>destr_function</I> to set the value to\nnon-NULL. For some implementations the effect of calling\n<B>pthread_setspecific</B> from within <I>destr_function</I> can be\neither memory leakage or infinite loops if <I>destr_function</I> has\nalready been called at least <B>PTHREAD_DESTRUCTOR_ITERATIONS</B>\ntimes.</P>\n<P STYLE=\"font-weight: medium\"><B>Pthreads-w32</B> stops running key\n<I>destr_function</I> routines after <B>PTHREAD_DESTRUCTOR_ITERATIONS</B>\niterations, even if some non- <B>NULL</B> values with associated\ndescriptors remain. If memory is allocated and associated with a key\nfrom within <I>destr_function</I>, that memory may not be reclaimed\nbecause that key's <I>destr_function</I>, may not run again.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P><B>pthread_key_create</B>, <B>pthread_key_delete</B>, and\n<B>pthread_setspecific</B> return 0 on success and a non-zero error\ncode on failure. If successful, <B>pthread_key_create</B> stores the\nnewly allocated key in the location pointed to by its <I>key</I>\nargument. \n</P>\n<P><B>pthread_getspecific</B> returns the value associated with <I>key</I>\non success, and <B>NULL</B> on error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P><B>pthread_key_create</B> returns the following error code on\nerror: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EAGAIN</B> \n\t\t</DT></DL>\n</DL>\n<BLOCKQUOTE STYLE=\"margin-left: 5cm\">\n<B>PTHREAD_KEYS_MAX</B> keys are already allocated \n</BLOCKQUOTE>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ENOMEM</B> \n\t\t</DT></DL>\n</DL>\n<BLOCKQUOTE STYLE=\"margin-left: 5cm\">\nInsufficient memory to allocate the key. \n</BLOCKQUOTE>\n<P><B>pthread_key_delete</B> and <B>pthread_setspecific</B> return\nthe following error code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<I>key</I> is not a valid, allocated TSD key \n\t\t</DD></DL>\n</DL>\n<P>\n<B>pthread_getspecific</B> returns <B>NULL</B> if <I>key</I> is not a\nvalid, allocated TSD key. \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">See Also</A></H2>\n<P><A HREF=\"pthread_create.html\">pthread_create(3)</A> ,\n<A HREF=\"pthread_exit.html\">pthread_exit(3)</A> ,\n<A HREF=\"pthread_cancel.html\">pthread_testcancel(3)</A> . \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Example</A></H2>\n<P>The following code fragment allocates a thread-specific array of\n100 characters, with automatic reclamation at thread exit: \n</P>\n<BLOCKQUOTE><BR><BR>\n</BLOCKQUOTE>\n<PRE STYLE=\"margin-left: 1cm; margin-right: 1cm\">/* Key for the thread-specific buffer */\nstatic pthread_key_t buffer_key;\n/* Once-only initialisation of the key */\nstatic pthread_once_t buffer_key_once = PTHREAD_ONCE_INIT;\n/* Allocate the thread-specific buffer */\nvoid buffer_alloc(void)\n{\n  pthread_once(&amp;buffer_key_once, buffer_key_alloc);\n  pthread_setspecific(buffer_key, malloc(100));\n}\n/* Return the thread-specific buffer */\nchar * get_buffer(void)\n{\n  return (char *) pthread_getspecific(buffer_key);\n}\n/* Allocate the key */\nstatic void buffer_key_alloc()\n{\n  pthread_key_create(&amp;buffer_key, buffer_destroy);\n}\n/* Free the thread-specific buffer */\nstatic void buffer_destroy(void * buf)\n{\n  free(buf);\n}</PRE>\n<HR>\n<BLOCKQUOTE STYLE=\"margin-left: 0cm; margin-right: 0cm\"><A NAME=\"toc\"></A>\n<B>Table of Contents</B></BLOCKQUOTE>\n<UL>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Author</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">See\n\tAlso</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE><A HREF=\"#sect7\" NAME=\"toc7\">Example</A> \n\t</BLOCKQUOTE>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_kill.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_SIGNAL(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;18500100\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050504;23005800\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_sigmask, pthread_kill, sigwait - handling of signals in\nthreads \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> <BR><B>#include &lt;signal.h&gt;</B>\n</P>\n<P><B>int pthread_sigmask(int </B><I>how</I><B>, const sigset_t\n*</B><I>newmask</I><B>, sigset_t *</B><I>oldmask</I><B>);</B> \n</P>\n<P><B>int pthread_kill(pthread_t </B><I>thread</I><B>, int </B><I>signo</I><B>);</B>\n</P>\n<P><B>int sigwait(const sigset_t *</B>set, <B>int</B> *sig);</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P><B>pthread_sigmask</B> changes the signal mask for the calling\nthread as described by the <I>how</I> and <I>newmask</I> arguments.\nIf <I>oldmask</I> is not <B>NULL</B>, the previous signal mask is\nstored in the location pointed to by <I>oldmask</I>. <B>Pthreads-w32</B>\nimplements this function but no other function uses the signal mask\nyet.</P>\n<P>The meaning of the <I>how</I> and <I>newmask</I> arguments is the\nsame as for <B><SPAN STYLE=\"font-style: normal\">sigprocmask</SPAN></B>(2).\nIf <I>how</I> is <B>SIG_SETMASK</B>, the signal mask is set to\n<I>newmask</I>. If <I>how</I> is <B>SIG_BLOCK</B>, the signals\nspecified to <I>newmask</I> are added to the current signal mask. If\n<I>how</I> is <B>SIG_UNBLOCK</B>, the signals specified to <I>newmask</I>\nare removed from the current signal mask. \n</P>\n<P>Recall that signal masks are set on a per-thread basis, but signal\nactions and signal handlers, as set with <B>sigaction</B>(2), are\nshared between all threads. \n</P>\n<P><B>pthread_kill</B> send signal number <I>signo</I> to the thread\n<I>thread</I>. <B>Pthreads-w32</B> only supports signal number 0,\nwhich does not send any signal but causes <B>pthread_kill</B> to\nreturn an error if <I>thread</I> is not valid.</P>\n<P><B>sigwait</B> suspends the calling thread until one of the\nsignals in <I>set</I> is delivered to the calling thread. It then\nstores the number of the signal received in the location pointed to\nby <I>sig</I> and returns. The signals in <I>set</I> must be blocked\nand not ignored on entrance to <B>sigwait</B>. If the delivered\nsignal has a signal handler function attached, that function is <I>not</I>\ncalled. <B>Pthreads-w32</B> implements this function as a\ncancellation point only - it does not wait for any signals and does\nnot change the location pointed to by <I>sig</I>.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P><B>sigwait</B> is a cancellation point. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Return Value</A></H2>\n<P>On success, 0 is returned. On failure, a non-zero error code is\nreturned. \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<P>The <B>pthread_sigmask</B> function returns the following error\ncodes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<I>how</I> is not one of <B>SIG_SETMASK</B>, <B>SIG_BLOCK</B>, or\n\t\t<B>SIG_UNBLOCK</B> \n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>pthread_kill</B> function returns the following error codes on\nerror: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<I>signo</I> is not a valid signal number or is unsupported.</DD><DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<B>ESRCH</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe thread <I>thread</I> does not exist (e.g. it has already\n\t\tterminated) \n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>sigwait</B> function never returns an error. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">See Also</A></H2>\n<P> \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Notes</A></H2>\n<P>In any implementation, for <B>sigwait</B> to work reliably, the\nsignals being waited for must be blocked in all threads, not only in\nthe calling thread, since otherwise the POSIX semantics for signal\ndelivery do not guarantee that it’s the thread doing the <B>sigwait</B>\nthat will receive the signal. The best way to achieve this is to\nblock those signals before any threads are created, and never unblock\nthem in the program other than by calling <B>sigwait</B>. This works\nbecause all threads inherit their initial sigmask from their creating\nthread.</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">Bugs</A></H2>\n<P><B>Pthreads-w32</B> does not implement signals yet and so these\nroutines have almost no use except to prevent the compiler or linker\nfrom complaining. <B>pthread_kill</B> is useful in determining if the\nthread is a valid thread, but since many threads implementations\nreuse thread IDs, the valid thread may no longer be the thread you\nthink it is, and so this method of determining thread validity is not\nportable, and very risky. <B>Pthreads-w32</B> from version 1.0.0\nonwards implements pseudo-unique thread IDs, so applications that use\nthis technique (but really shouldn't) have some protection.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Author</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">See\n\tAlso</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Notes</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect9\" NAME=\"toc9\">Bugs</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_mutex_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_MUTEX(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 3.2  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;5000\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20110326;15072100\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n\t<STYLE TYPE=\"text/css\">\n\t<!--\n\t\tH4.cjk { font-family: \"AR PL UMing CN\" }\n\t\tH4.ctl { font-family: \"Lohit Devanagari\" }\n\t\tH2.cjk { font-family: \"AR PL UMing CN\" }\n\t\tH2.ctl { font-family: \"Lohit Devanagari\" }\n\t\tPRE.cjk { font-family: \"AR PL UMing CN\", monospace }\n\t\tPRE.ctl { font-family: \"Lohit Devanagari\", monospace }\n\t-->\n\t</STYLE>\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4 CLASS=\"western\">POSIX Threads for Windows – REFERENCE -\n<A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2 CLASS=\"western\"><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_mutex_init, pthread_mutex_lock, pthread_mutex_trylock,\npthread_mutex_timedlock, pthread_mutex_unlock,\npthread_mutex_consistent, pthread_mutex_destroy - operations on\nmutexes \n</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>#include &lt;time.h&gt;</B></P>\n<P><B>pthread_mutex_t </B><I>fastmutex</I> <B>=\nPTHREAD_MUTEX_INITIALIZER;</B> \n</P>\n<P><B>pthread_mutex_t </B><I>recmutex</I> <B>=\nPTHREAD_RECURSIVE_MUTEX_INITIALIZER;</B> \n</P>\n<P><B>pthread_mutex_t </B><I>errchkmutex</I> <B>=\nPTHREAD_ERRORCHECK_MUTEX_INITIALIZER;</B> \n</P>\n<P><B>pthread_mutex_t </B><I>recmutex</I> <B>=\nPTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;</B> \n</P>\n<P><B>pthread_mutex_t </B><I>errchkmutex</I> <B>=\nPTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;</B> \n</P>\n<P><B>int pthread_mutex_init(pthread_mutex_t *</B><I>mutex</I><B>,\nconst pthread_mutexattr_t *</B><I>mutexattr</I><B>);</B> \n</P>\n<P><B>int pthread_mutex_lock(pthread_mutex_t *</B><I>mutex</I><B>);</B>\n</P>\n<P><B>int pthread_mutex_trylock(pthread_mutex_t *</B><I>mutex</I><B>);</B>\n</P>\n<P><B>int pthread_mutex_timedlock(pthread_mutex_t *</B><I>mutex,\n</I><B>const struct timespec *</B><I>abs_timeout</I><B>);</B> \n</P>\n<P><B>int pthread_mutex_unlock(pthread_mutex_t *</B><I>mutex</I><B>);</B>\n</P>\n<P><B>int pthread_mutex_consistent(pthread_mutex_t *</B><I>mutex</I><B>);</B>\n</P>\n<P><B>int pthread_mutex_destroy(pthread_mutex_t *</B><I>mutex</I><B>);</B>\n</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>A mutex is a MUTual EXclusion device, and is useful for protecting\nshared data structures from concurrent modifications, and\nimplementing critical sections and monitors. \n</P>\n<P>A mutex has two possible states: unlocked (not owned by any\nthread), and locked (owned by one thread). A mutex can never be owned\nby two different threads simultaneously. A thread attempting to lock\na mutex that is already locked by another thread is suspended until\nthe owning thread unlocks the mutex first. \n</P>\n<P><B>pthread_mutex_init</B> initializes the mutex object pointed to\nby <I>mutex</I> according to the mutex attributes specified in\n<I>mutexattr</I>. If <I>mutexattr</I> is <B>NULL</B>, default\nattributes are used instead. \n</P>\n<P>The type of a mutex determines whether it can be locked again by a\nthread that already owns it. The default type is “normal”. See\n<A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_init</B>(3)</A>\nfor more information on mutex attributes. \n</P>\n<P>Variables of type <B>pthread_mutex_t</B> can also be initialized\nstatically, using the constants <B>PTHREAD_MUTEX_INITIALIZER</B> (for\nnormal “fast” mutexes), <B>PTHREAD_RECURSIVE_MUTEX_INITIALIZER</B>\n(for recursive mutexes), and <B>PTHREAD_ERRORCHECK_MUTEX_INITIALIZER</B>\n(for error checking mutexes). <SPAN STYLE=\"font-weight: normal\">In\nthe </SPAN><B>Pthreads-w32</B> <SPAN STYLE=\"font-weight: normal\">implementation,\nan application should still call </SPAN><B>pthread_mutex_destroy</B>\n<SPAN STYLE=\"font-weight: normal\">at some point to ensure that any\nresources consumed by the mutex are released.</SPAN></P>\n<P><SPAN STYLE=\"font-weight: normal\">Any mutex type can be\ninitialized as a </SPAN><B>robust mutex</B><SPAN STYLE=\"font-weight: normal\">.\nSee </SPAN><A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_init</B><SPAN STYLE=\"font-weight: normal\">(3)</SPAN></A>\n<SPAN STYLE=\"font-weight: normal\">for more information as well as the\nsection </SPAN><I><U><SPAN STYLE=\"font-weight: normal\">Robust Mutexes</SPAN></U></I>\n<SPAN STYLE=\"font-weight: normal\">below.</SPAN></P>\n<P><B>pthread_mutex_lock</B> locks the given mutex. If the mutex is\ncurrently unlocked, it becomes locked and owned by the calling\nthread, and <B>pthread_mutex_lock</B> returns immediately. If the\nmutex is already locked by another thread, <B>pthread_mutex_lock</B>\nsuspends the calling thread until the mutex is unlocked.</P>\n<P>If the mutex is already locked by the calling thread, the behavior\nof <B>pthread_mutex_lock</B> depends on the type of the mutex. If the\nmutex is of the “normal” type, the calling thread is suspended\nuntil the mutex is unlocked, thus effectively causing the calling\nthread to deadlock. If the mutex is of the ‘‘error checking’’\ntype, <B>pthread_mutex_lock</B> returns immediately with the error\ncode <B>EDEADLK</B>. If the mutex is of the ‘‘recursive’’\ntype, <B>pthread_mutex_lock</B> succeeds and returns immediately,\nrecording the number of times the calling thread has locked the\nmutex. An equal number of <B>pthread_mutex_unlock</B> operations must\nbe performed before the mutex returns to the unlocked state. \n</P>\n<P><B>pthread_mutex_trylock</B> behaves identically to\n<B>pthread_mutex_lock</B>, except that it does not block the calling\nthread if the mutex is already locked by another thread (or by the\ncalling thread in the case of a “normal” or “<SPAN STYLE=\"font-style: normal\">errorcheck</SPAN>”\nmutex). Instead, <B>pthread_mutex_trylock</B> returns immediately\nwith the error code <B>EBUSY</B>. \n</P>\n<P><B>pthread_mutex_timedlock</B> behaves identically to\n<B>pthread_mutex_lock</B>, except that if it cannot acquire the lock\nbefore the <I>abs_timeout</I> time, the call returns with the error\ncode <B>ETIMEDOUT</B>. If the mutex can be locked immediately it is,\nand the <B>abs_timeout</B> parameter is ignored.</P>\n<P><B>pthread_mutex_consistent</B> may only be called for\n<B>PTHREAD_MUTEX_ROBUST</B> mutexes. It simply marks the mutex as\nconsistent. See <I><U>Robust Mutexes</U></I> below.</P>\n<P><B>pthread_mutex_unlock</B> unlocks the given mutex. The mutex is\nassumed to be locked and owned by the calling thread on entrance to\n<B>pthread_mutex_unlock</B>. If the mutex is of the “normal”\ntype, <B>pthread_mutex_unlock</B> always returns it to the unlocked\nstate. If it is of the ‘‘recursive’’ type, it decrements the\nlocking count of the mutex (number of <B>pthread_mutex_lock</B>\noperations performed on it by the calling thread), and only when this\ncount reaches zero is the mutex actually unlocked. In <B>Pthreads-win32</B>,\nnon-robust normal or default mutex types do not check the owner of\nthe mutex. For all types of robust mutexes the owner is checked and\nan error code is returned if the calling thread does not own the\nmutex.</P>\n<P>On ‘‘error checking’’ mutexes, <B>pthread_mutex_unlock</B>\nactually checks at run-time that the mutex is locked on entrance, and\nthat it was locked by the same thread that is now calling\n<B>pthread_mutex_unlock</B>. If these conditions are not met, an\nerror code is returned and the mutex remains unchanged. ‘‘Normal’’\n[non-robust] mutexes perform no such checks, thus allowing a locked\nmutex to be unlocked by a thread other than its owner. This is\nnon-portable behavior and is not meant to be used as a feature.</P>\n<P><B>pthread_mutex_destroy</B> destroys a mutex object, freeing the\nresources it might hold. The mutex must be unlocked on entrance.</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc10\" NAME=\"sect10\">Robust Mutexes</A></H2>\n<P>If the mutex is <B>PTHREAD_MUTEX_ROBUST</B> and the owning thread\nterminates without unlocking the mutex the implementation will wake\none waiting thread, if any. The next thread to acquire the mutex will\nreceive the error code <B>EOWNERDEAD</B><SPAN STYLE=\"font-weight: normal\">,\nin which case that thread should if possible ensure that the state\nprotected by the mutex is consistent and then call\n</SPAN><B>pthread_mutex_consistent</B> <SPAN STYLE=\"font-weight: normal\">before\nunlocking. The mutex may then be used normally from then on.</SPAN></P>\n<P><SPAN STYLE=\"font-weight: normal\">If the thread cannot recover the\nstate then it must call </SPAN><B>pthread_mutex_unlock</B><SPAN STYLE=\"font-weight: normal\">\nwithout calling </SPAN><B>pthread_mutex_consistent</B><SPAN STYLE=\"font-weight: normal\">.\nThis will mark the mutex as unusable and wake all currently waiting\nthreads with the return code </SPAN><B>ENOTRECOVERABLE</B><SPAN STYLE=\"font-weight: normal\">.\nThe error indicates that the mutex is no longer usable and any\nthreads that receive this error code from any lock operation have not\nacquired the mutex. The mutex can be made consistent by calling\n</SPAN><B>pthread_mutex_destroy</B> <SPAN STYLE=\"font-weight: normal\">to\nuninitialize the mutex, and calling </SPAN><B>pthread_mutex_int</B>\n<SPAN STYLE=\"font-weight: normal\">to reinitialize the mutex. However,\nthe state that was protected by the mutex remains inconsistent and\nsome form of application recovery is required.</SPAN></P>\n<P><SPAN STYLE=\"font-weight: normal\">If a thread that receives the\n</SPAN><B>EOWNERDEAD</B> <SPAN STYLE=\"font-weight: normal\">error code\nitself terminates without unlocking the mutex then this behaviour\nrepeats for the next acquiring thread.</SPAN></P>\n<P><SPAN STYLE=\"font-weight: normal\">Applications must ensure that\nthey check the return values from all calls targeting robust mutexes.</SPAN></P>\n<P STYLE=\"font-weight: normal\">Robust mutexes are slower because they\nrequire some additional overhead, however they are not very much\nslower than the non-robust recursive type.</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P>None of the mutex functions is a cancellation point, not even\n<B>pthread_mutex_lock</B>, in spite of the fact that it can suspend a\nthread for arbitrary durations. This way, the status of mutexes at\ncancellation points is predictable, allowing cancellation handlers to\nunlock precisely those mutexes that need to be unlocked before the\nthread stops executing. Consequently, threads using deferred\ncancellation should never hold a mutex for extended periods of time. \n</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc4\" NAME=\"sect4\">Async-signal Safety</A></H2>\n<P>The mutex functions are not async-signal safe. What this means is\nthat they should not be called from a signal handler. In particular,\ncalling <B>pthread_mutex_lock</B> or <B>pthread_mutex_unlock</B> from\na signal handler may deadlock the calling thread. \n</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc5\" NAME=\"sect5\">Return Value</A></H2>\n<P><B>pthread_mutex_init</B> always returns 0. The other mutex\nfunctions return 0 on success and a non-zero error code on error. \n</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc6\" NAME=\"sect6\">Errors</A></H2>\n<P>The <B>pthread_mutex_lock</B> function returns the following error\ncode on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\"><B>EINVAL</B></DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe mutex has not been properly initialized. \n\t\t</DD><DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>EDEADLK</B></DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe mutex is already locked by the calling thread (‘‘error\n\t\tchecking’’ mutexes only). \n\t\t</DD><DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>EOWNERDEAD</B></DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe robust mutex is now locked by the calling thread after the\n\t\tprevious owner terminated without unlocking it.</DD><DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>ENOTRECOVERABLE</B></DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe robust mutex is not locked and is no longer usable after the\n\t\tprevious owner unlocked it without calling\n\t\tpthread_mutex_consistent.</DD></DL>\n\t<DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\tThe <B>pthread_mutex_trylock</B> function returns the following\n\terror codes on error: \n\t</DD><DL>\n\t\t<DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>EBUSY</B> \n\t\t</DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe mutex could not be acquired because it was currently locked. \n\t\t</DD><DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe mutex has not been properly initialized. \n\t\t</DD><DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>EOWNERDEAD</B></DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe robust mutex is now locked by the calling thread after the\n\t\tprevious owner terminated without unlocking it.</DD><DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>ENOTRECOVERABLE</B></DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe robust mutex is not locked and is no longer usable after the\n\t\tprevious owner unlocked it without calling\n\t\tpthread_mutex_consistent.</DD></DL>\n</DL>\n<P>\nThe <B>pthread_mutex_timedlock</B> function returns the following\nerror codes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\"><B>ETIMEDOUT</B>\n\t\t\t\t</DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe mutex could not be acquired before the <I>abs_timeout</I> time\n\t\tarrived. \n\t\t</DD><DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe mutex has not been properly initialized. \n\t\t</DD><DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>EOWNERDEAD</B></DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe robust mutex is now locked by the calling thread after the\n\t\tprevious owner terminated without unlocking it.</DD><DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>ENOTRECOVERABLE</B></DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe robust mutex is not locked and is no longer usable after the\n\t\tprevious owner unlocked it without calling\n\t\tpthread_mutex_consistent.</DD></DL>\n</DL>\n<P>\nThe <B>pthread_mutex_unlock</B> function returns the following error\ncode on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\"><B>EINVAL</B>\n\t\t\t\t</DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe mutex has not been properly initialized. \n\t\t</DD><DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\t<B>EPERM</B> \n\t\t</DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe calling thread does not own the mutex (‘‘error checking’’\n\t\tmutexes only). \n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>pthread_mutex_destroy</B> function returns the following error\ncode on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\"><B>EBUSY</B>\n\t\t\t\t</DT><DD STYLE=\"margin-right: 0.39in; margin-bottom: 0.2in\">\n\t\tthe mutex is currently locked. \n\t\t</DD></DL>\n</DL>\n<H2 CLASS=\"western\">\n<A HREF=\"#toc7\" NAME=\"sect7\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc8\" NAME=\"sect8\">See Also</A></H2>\n<P><A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_init</B>(3)</A>\n, <A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_settype</B>(3)</A>\n, <A HREF=\"pthread_cancel.html\"><B>pthread_cancel</B>(3)</A> . \n</P>\n<H2 CLASS=\"western\"><A HREF=\"#toc9\" NAME=\"sect9\">Example</A></H2>\n<P>A shared global variable <I>x</I> can be protected by a mutex as\nfollows: \n</P>\n<PRE CLASS=\"western\" STYLE=\"margin-left: 0.39in; margin-right: 0.39in\">int x;\npthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;</PRE><BLOCKQUOTE STYLE=\"margin-left: 0in; margin-right: 0in\">\nAll accesses and modifications to <I>x</I> should be bracketed by\ncalls to <B>pthread_mutex_lock</B> and <B>pthread_mutex_unlock</B> as\nfollows: \n</BLOCKQUOTE>\n<PRE CLASS=\"western\" STYLE=\"margin-left: 0.41in; margin-right: 0.79in\">pthread_mutex_lock(&amp;mut);\n/* operate on x */\npthread_mutex_unlock(&amp;mut);</PRE>\n<HR>\n<BLOCKQUOTE STYLE=\"margin-right: 2.75in\"><A NAME=\"toc\"></A><B>Table\nof Contents</B></BLOCKQUOTE>\n<UL>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in; margin-bottom: 0in\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in; margin-bottom: 0in\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in; margin-bottom: 0in\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in; margin-bottom: 0in\"><A HREF=\"#sect10\" NAME=\"toc10\">Robust\n\tMutexes</A></BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in; margin-bottom: 0in\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in; margin-bottom: 0in\"><A HREF=\"#sect4\" NAME=\"toc4\">Async-signal\n\tSafety</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in; margin-bottom: 0in\"><A HREF=\"#sect5\" NAME=\"toc5\">Return\n\tValue</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in; margin-bottom: 0in\"><A HREF=\"#sect6\" NAME=\"toc6\">Errors</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in; margin-bottom: 0in\"><A HREF=\"#sect7\" NAME=\"toc7\">Author</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in; margin-bottom: 0in\"><A HREF=\"#sect8\" NAME=\"toc8\">See\n\tAlso</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-right: 2.75in\"><A HREF=\"#sect9\" NAME=\"toc9\">Example</A>\n\t\t</BLOCKQUOTE>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_mutexattr_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\r\n<HTML>\r\n<HEAD>\r\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\r\n\t<TITLE>PTHREAD_MUTEXATTR(3) manual page</TITLE>\r\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 3.2  (Linux)\">\r\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;23040500\">\r\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\r\n\t<META NAME=\"CHANGED\" CONTENT=\"20110326;13190500\">\r\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\r\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\r\n\t<STYLE TYPE=\"text/css\">\r\n\t<!--\r\n\t\tH4.cjk { font-family: \"AR PL UMing CN\" }\r\n\t\tH4.ctl { font-family: \"Lohit Devanagari\" }\r\n\t\tH2.cjk { font-family: \"AR PL UMing CN\" }\r\n\t\tH2.ctl { font-family: \"Lohit Devanagari\" }\r\n\t-->\r\n\t</STYLE>\r\n</HEAD>\r\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\r\n<H4 CLASS=\"western\">POSIX Threads for Windows – REFERENCE -\r\n<A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\r\n<P><A HREF=\"index.html\">Reference Index</A></P>\r\n<P><A HREF=\"#toc\">Table of Contents</A></P>\r\n<H2 CLASS=\"western\"><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\r\n<P>pthread_mutexattr_init, pthread_mutexattr_destroy,\r\npthread_mutexattr_settype, pthread_mutexattr_gettype - mutex creation\r\nattributes \r\n</P>\r\n<H2 CLASS=\"western\"><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\r\n<P><B>#include &lt;pthread.h&gt;</B> \r\n</P>\r\n<P><B>int pthread_mutexattr_init(pthread_mutexattr_t *</B><I>attr</I><B>);</B>\r\n</P>\r\n<P><B>int pthread_mutexattr_destroy(pthread_mutexattr_t *</B><I>attr</I><B>);</B>\r\n</P>\r\n<P><B>int pthread_mutexattr_settype(pthread_mutexattr_t *</B><I>attr</I><B>,\r\nint </B><I>type</I><B>);</B> \r\n</P>\r\n<P><B>int pthread_mutexattr_gettype(const pthread_mutexattr_t *</B><I>attr</I><B>,\r\nint *</B><I>type</I><B>);</B> \r\n</P>\r\n<P><B>int pthread_mutexattr_setkind_np(pthread_mutexattr_t *</B><I>attr</I><B>,\r\nint </B><I>type</I><B>);</B> \r\n</P>\r\n<P><B>int pthread_mutexattr_getkind_np(const pthread_mutexattr_t\r\n*</B><I>attr</I><B>, int *</B><I>type</I><B>);</B> \r\n</P>\r\n<P><B>int pthread_mutexattr_setrobust(pthread_mutexattr_t *</B><I>attr</I><B>,\r\nint</B><SPAN STYLE=\"font-weight: normal\"> </SPAN><I><SPAN STYLE=\"font-weight: normal\">robust</SPAN></I><B>);</B>\r\n</P>\r\n<P><B>int pthread_mutexattr_getrobust(pthread_mutexattr_t *</B><I>attr</I><B>,\r\nint</B><SPAN STYLE=\"font-weight: normal\"> </SPAN><B>*</B><I><SPAN STYLE=\"font-weight: normal\">robust</SPAN></I><B>);</B>\r\n</P>\r\n<H2 CLASS=\"western\"><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\r\n<P>Mutex attributes can be specified at mutex creation time, by\r\npassing a mutex attribute object as second argument to\r\n<A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_init</B>(3)</A> .\r\nPassing <B>NULL</B> is equivalent to passing a mutex attribute object\r\nwith all attributes set to their default values. \r\n</P>\r\n<P><B>pthread_mutexattr_init</B> initializes the mutex attribute\r\nobject <I>attr</I> and fills it with default values for the\r\nattributes. \r\n</P>\r\n<P><B>pthread_mutexattr_destroy</B> destroys a mutex attribute\r\nobject, which must not be reused until it is reinitialized.</P>\r\n<P><B>pthread_mutexattr_settype</B> sets the mutex type attribute in\r\n<I>attr</I> to the value specified by <I>type</I>. \r\n</P>\r\n<P><B>pthread_mutexattr_gettype</B> retrieves the current value of\r\nthe mutex kind attribute in <I>attr</I> and stores it in the location\r\npointed to by <I>type</I>. \r\n</P>\r\n<P><B>Pthreads-w32</B> also recognises the following equivalent\r\nfunctions that are used in Linux:</P>\r\n<P><B>pthread_mutexattr_setkind_np</B> is an alias for\r\n<B>pthread_mutexattr_settype</B>. \r\n</P>\r\n<P STYLE=\"font-weight: normal\"><B>pthread_mutexattr_getkind_np</B> is\r\nan alias for <B>pthread_mutexattr_gettype</B>. \r\n</P>\r\n<P>The following mutex types are supported:</P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>PTHREAD_MUTEX_NORMAL</B> - for\r\n‘‘fast’’ mutexes.</P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>PTHREAD_MUTEX_RECURSIVE</B> - for\r\n‘‘recursive’’ mutexes.</P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>PTHREAD_MUTEX_ERRORCHECK</B> - for\r\n‘‘error checking’’ mutexes.</P>\r\n<P>The mutex type determines what happens if a thread attempts to\r\nlock a mutex it already owns with <A HREF=\"pthread_mutex_lock.html\"><B>pthread_mutex_lock</B>(3)</A>\r\n. If the mutex is of the “normal” or “fast” type,\r\n<A HREF=\"pthread_mutex_lock.html\"><B>pthread_mutex_lock</B>(3)</A>\r\nsimply suspends the calling thread forever. If the mutex is of the\r\n‘‘error checking’’ type, <A HREF=\"pthread_mutex_lock.html\"><B>pthread_mutex_lock</B>(3)</A>\r\nreturns immediately with the error code <B>EDEADLK</B>. If the mutex\r\nis of the ‘‘recursive’’ type, the call to\r\n<A HREF=\"pthread_mutex_lock.html\"><B>pthread_mutex_lock</B>(3)</A>\r\nreturns immediately with a success return code. The number of times\r\nthe thread owning the mutex has locked it is recorded in the mutex.\r\nThe owning thread must call <A HREF=\"pthread_mutex_unlock.html\"><B>pthread_mutex_unlock</B>(3)</A>\r\nthe same number of times before the mutex returns to the unlocked\r\nstate. \r\n</P>\r\n<P>The default mutex type is <B>PTHREAD_MUTEX_NORMAL</B></P>\r\n<P><B>Pthreads-w32</B> also recognises the following equivalent types\r\nthat are used by Linux:</P>\r\n<P STYLE=\"margin-left: 0.79in; font-weight: normal\"><B>PTHREAD_MUTEX_FAST_NP</B>\r\n– equivalent to <B>PTHREAD_MUTEX_NORMAL</B></P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>PTHREAD_MUTEX_RECURSIVE_NP</B></P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>PTHREAD_MUTEX_ERRORCHECK_NP</B></P>\r\n<P><B>pthread_mutexattr_setrobust</B><SPAN STYLE=\"font-weight: normal\">\r\nsets the robustness attribute to the value given by </SPAN><I><SPAN STYLE=\"font-weight: normal\">robust</SPAN></I><SPAN STYLE=\"font-weight: normal\">.</SPAN></P>\r\n<P><B>pthread_mutexattr_getrobust</B><SPAN STYLE=\"font-weight: normal\">\r\nreturns the current robustness value to the location given by\r\n*</SPAN><I><SPAN STYLE=\"font-weight: normal\">robust</SPAN></I><SPAN STYLE=\"font-weight: normal\">.</SPAN></P>\r\n<P><SPAN STYLE=\"font-weight: normal\">The possible values for </SPAN><I><SPAN STYLE=\"font-weight: normal\">robust</SPAN></I><SPAN STYLE=\"font-weight: normal\">\r\nare:</SPAN></P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>PTHREAD_MUTEX_STALLED</B><SPAN STYLE=\"font-weight: normal\">\r\n- when the owner of the mutex terminates without unlocking the mutex,\r\nall subsequent calls\r to pthread_mutex_*lock() are blocked from\r\nprogress in an unspecified manner.</SPAN></P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>PTHREAD_MUTEX_ROBUST</B><SPAN STYLE=\"font-weight: normal\">\r\n- when the owner of the mutex terminates without unlocking the mutex,\r\nthe mutex is\r unlocked. The next owner of this mutex acquires the\r\nmutex with an error return of\rEOWNERDEAD.</SPAN></P>\r\n<H2 CLASS=\"western\"><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\r\n<P><SPAN STYLE=\"font-weight: normal\">On success all functions return\r\n0, otherwise they return an error code as follows:</SPAN></P>\r\n<P><B>pthread_mutexattr_init</B></P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>ENOMEM</B><SPAN STYLE=\"font-weight: normal\">\r\n- insufficient memory for </SPAN><I><SPAN STYLE=\"font-weight: normal\">attr</SPAN></I><SPAN STYLE=\"font-weight: normal\">.</SPAN></P>\r\n<P><B>pthread_mutexattr_destroy</B></P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>EINVAL</B><SPAN STYLE=\"font-weight: normal\">\r\n- </SPAN><I><SPAN STYLE=\"font-weight: normal\">attr</SPAN></I><SPAN STYLE=\"font-weight: normal\">\r\nis invalid.</SPAN></P>\r\n<P><B>pthread_mutexattr_gettype</B></P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>EINVAL</B><SPAN STYLE=\"font-weight: normal\">\r\n- </SPAN><I><SPAN STYLE=\"font-weight: normal\">attr</SPAN></I><SPAN STYLE=\"font-weight: normal\">\r\nis invalid.</SPAN></P>\r\n<P><B>pthread_mutexattr_settype</B></P>\r\n<DL>\r\n\t<DL>\r\n\t\t<DL>\r\n\t\t\t<DT><B>EINVAL</B><SPAN STYLE=\"font-weight: normal\"> - </SPAN><I><SPAN STYLE=\"font-weight: normal\">attr</SPAN></I><SPAN STYLE=\"font-weight: normal\">\r\n\t\t\tis invalid or </SPAN><I><SPAN STYLE=\"font-weight: normal\">type</SPAN></I><SPAN STYLE=\"font-weight: normal\">\r\n\t\t\tis none of:</SPAN></DT><DL>\r\n\t\t\t\t<DL>\r\n\t\t\t\t\t<DT>\r\n\t\t\t\t\t<B>PTHREAD_MUTEX_NORMAL<BR>PTHREAD_MUTEX_FAST_NP<BR>PTHREAD_MUTEX_RECURSIVE<BR>PTHREAD_MUTEX_RECURSIVE_NP<BR>PTHREAD_MUTEX_ERRORCHECK<BR>PTHREAD_MUTEX_ERRORCHECK_NP</B></DT></DL>\r\n\t\t\t</DL>\r\n\t\t</DL>\r\n\t</DL>\r\n\t<DD STYLE=\"margin-left: 0in\">\r\n\t<BR>\r\n\t</DD></DL>\r\n<P>\r\n<B>pthread_mutexattr_getrobust</B></P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>EINVAL</B><SPAN STYLE=\"font-weight: normal\">\r\n– </SPAN><I><SPAN STYLE=\"font-weight: normal\">attr</SPAN></I><SPAN STYLE=\"font-weight: normal\">\r\nor </SPAN><I><SPAN STYLE=\"font-weight: normal\">robust</SPAN></I><SPAN STYLE=\"font-weight: normal\">\r\nis invalid.</SPAN></P>\r\n<P><B>pthread_mutexattr_setrobust</B></P>\r\n<P STYLE=\"margin-left: 0.79in\"><B>EINVAL</B><SPAN STYLE=\"font-weight: normal\">\r\n– </SPAN><I><SPAN STYLE=\"font-weight: normal\">attr</SPAN></I><SPAN STYLE=\"font-weight: normal\">\r\nor </SPAN><I><SPAN STYLE=\"font-weight: normal\">robust</SPAN></I><SPAN STYLE=\"font-weight: normal\">\r\nis invalid.</SPAN></P>\r\n<H2 CLASS=\"western\"><A HREF=\"#toc5\" NAME=\"sect5\">Author</A></H2>\r\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \r\n</P>\r\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\r\n<H2 CLASS=\"western\"><A HREF=\"#toc6\" NAME=\"sect6\">See Also</A></H2>\r\n<P><A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_init</B>(3)</A>\r\n, <A HREF=\"pthread_mutex_lock.html\"><B>pthread_mutex_lock</B>(3)</A>\r\n, <A HREF=\"pthread_mutex_unlock.html\"><B>pthread_mutex_unlock</B>(3)</A>\r\n. \r\n</P>\r\n<H2 CLASS=\"western\"><A HREF=\"#toc7\" NAME=\"sect7\"><FONT COLOR=\"#000080\"><U>Notes</U></FONT></A></H2>\r\n<P>For speed, <B>Pthreads-w32</B> never checks the thread ownership\r\nof non-robust mutexes of type <B>PTHREAD_MUTEX_NORMAL</B> (or\r\n<B>PTHREAD_MUTEX_FAST_NP</B>) when performing operations on the\r\nmutex. It is therefore possible for one thread to lock such a mutex\r\nand another to unlock it.</P>\r\n<P STYLE=\"font-weight: normal\">When developing code, it is a common\r\nprecaution to substitute the error checking type, then drop in the\r\nnormal type for release if the extra performance is required.</P>\r\n<HR>\r\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\r\n<UL>\r\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\r\n\t\t</P>\r\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\r\n\t\t</P>\r\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\r\n\t\t</P>\r\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\r\n\tValue</A> \r\n\t</P>\r\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect5\" NAME=\"toc5\">Author</A>\r\n\t\t</P>\r\n\t<LI><P STYLE=\"margin-bottom: 0in\"><A HREF=\"#sect6\" NAME=\"toc6\">See\r\n\tAlso</A></P>\r\n\t<LI><P><A HREF=\"#sect7\" NAME=\"toc7\">Notes</A></P>\r\n</UL>\r\n</BODY>\r\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_mutexattr_setpshared.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_MUTEXATTR_GETPSHARED&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;23480600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;18381800\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_mutexattr_getpshared, pthread_mutexattr_setpshared - get\nand set the process-shared attribute \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_mutexattr_getpshared(const pthread_mutexattr_t *\nrestrict</B> <I>attr</I><B>, int *restrict</B> <I>pshared</I><B>);\n<BR>int pthread_mutexattr_setpshared(pthread_mutexattr_t *</B><I>attr</I><B>,\nint</B> <I>pshared</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_mutexattr_getpshared</B> function shall obtain the\nvalue of the <I>process-shared</I> attribute from the attributes\nobject referenced by <I>attr</I>. The <B>pthread_mutexattr_setpshared</B>\nfunction shall set the <I>process-shared</I> attribute in an\ninitialized attributes object referenced by <I>attr</I>. \n</P>\n<P>The <I>process-shared</I> attribute is set to\n<B>PTHREAD_PROCESS_SHARED</B> to permit a mutex to be operated upon\nby any thread that has access to the memory where the mutex is\nallocated, even if the mutex is allocated in memory that is shared by\nmultiple processes. If the <I>process-shared</I> attribute is\n<B>PTHREAD_PROCESS_PRIVATE</B>, the mutex shall only be operated upon\nby threads created within the same process as the thread that\ninitialized the mutex; if threads of differing processes attempt to\noperate on such a mutex, the behavior is undefined. The default value\nof the attribute shall be <B>PTHREAD_PROCESS_PRIVATE</B>. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_THREAD_PROCESS_SHARED</B> in\npthread.h as -1 to indicate that these routines are implemented but\nthe process shared option is not supported.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, <B>pthread_mutexattr_setpshared</B>\nshall return zero; otherwise, an error number shall be returned to\nindicate the error. \n</P>\n<P>Upon successful completion, <B>pthread_mutexattr_getpshared</B>\nshall return zero and store the value of the <I>process-shared</I>\nattribute of <I>attr</I> into the object referenced by the <I>pshared</I>\nparameter. Otherwise, an error number shall be returned to indicate\nthe error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_mutexattr_getpshared</B> and\n<B>pthread_mutexattr_setpshared</B> functions may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>attr</I> is invalid. \n\t</DD></DL>\n<P>\nThe <B>pthread_mutexattr_setpshared</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe new value specified for the attribute is outside the range of\n\tlegal values for that attribute. \n\t</DD><DT>\n\t<B>ENOTSUP</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe new value specified for the attribute is PTHREAD_PROCESS_SHARED.\n\t\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_cond_init.html\"><B>pthread_cond_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A> <B>,</B>\n<A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_mutexattr_init.html\"><B>pthread_mutexattr_destroy</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_num_processors_np.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_NUM_PROCESSORS_NP manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;322600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;21514100\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P STYLE=\"font-weight: medium\">pthread_num_processors_np – get the\nnumber of processors (CPUs) in use by the process</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_num_processors_np(void);</B></P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P><B>pthread_num_processors_np </B>returns the number of processors\nin the system. This implementation actually returns the number of\nprocessors available to the process, which can be a lower number than\nthe system's number, depending on the process's affinity mask.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P>None.</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\"><FONT COLOR=\"#000080\">Return Value</FONT></A></H2>\n<P><B>pthread_num_processors_np</B> returns the number of processors\ncurrently available to the process.</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<P>None.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">Author</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_once.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_ONCE(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;322600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050603;463100\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_once - once-only initialization \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>pthread_once_t </B><I>once_control</I> <B>= PTHREAD_ONCE_INIT;</B>\n</P>\n<P><B>int pthread_once(pthread_once_t *</B><I>once_control</I><B>,\nvoid (*</B><I>init_routine</I><B>) (void));</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The purpose of <B>pthread_once</B> is to ensure that a piece of\ninitialization code is executed at most once. The <I>once_control</I>\nargument points to a static or extern variable statically initialized\nto <B>PTHREAD_ONCE_INIT</B>. \n</P>\n<P>The first time <B>pthread_once</B> is called with a given\n<I>once_control</I> argument, it calls <I>init_routine</I> with no\nargument and changes the value of the <I>once_control</I> variable to\nrecord that initialization has been performed. Subsequent calls to\n<B>pthread_once</B> with the same <B>once_control</B> argument do\nnothing. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P>While <B>pthread_once</B> is not a cancellation point,\n<I>init_routine</I> can be. The effect on <I>once_control</I> of a\ncancellation inside the <I>init_routine</I> is to leave it as if\n<B>pthread_once</B> had not been called by the cancelled thread.</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Return Value</A></H2>\n<P STYLE=\"text-decoration: none\"><FONT COLOR=\"#000000\"><B>pthread_once</B>\nreturns 0 on success, or an error code on failure.</FONT></P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<P>The <B>pthread_once</B> function returns the following error code\non error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT></DL>\n</DL>\n<P STYLE=\"margin-left: 2cm\">\nThe once_control or init_routine parameter is NULL.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">Author</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_rwlock_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_RWLOCK_DESTROY&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;10211800\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;18563400\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_rwlock_destroy, pthread_rwlock_init - destroy and\ninitialize a read-write lock object \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>pthread_wrlock_t </B><I>rwlock</I> <B>=\nPTHREAD_RWLOCK_INITIALIZER;</B></P>\n<P><B>int pthread_rwlock_destroy(pthread_rwlock_t *</B><I>rwlock</I><B>);\n<BR>int pthread_rwlock_init(pthread_rwlock_t *restrict</B> <I>rwlock</I><B>,\nconst pthread_rwlockattr_t *restrict</B> <I>attr</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_rwlock_destroy</B> function shall destroy the\nread-write lock object referenced by <I>rwlock</I> and release any\nresources used by the lock. The effect of subsequent use of the lock\nis undefined until the lock is reinitialized by another call to\n<B>pthread_rwlock_init</B>. An implementation may cause\n<B>pthread_rwlock_destroy</B> to set the object referenced by <I>rwlock</I>\nto an invalid value. Results are undefined if <B>pthread_rwlock_destroy</B>\nis called when any thread holds <I>rwlock</I>. Attempting to destroy\nan uninitialized read-write lock results in undefined behavior. \n</P>\n<P>The <B>pthread_rwlock_init</B> function shall allocate any\nresources required to use the read-write lock referenced by <I>rwlock</I>\nand initializes the lock to an unlocked state with attributes\nreferenced by <I>attr</I>. If <I>attr</I> is NULL, the default\nread-write lock attributes shall be used; the effect is the same as\npassing the address of a default read-write lock attributes object.\nOnce initialized, the lock can be used any number of times without\nbeing reinitialized. Results are undefined if <B>pthread_rwlock_init</B>\nis called specifying an already initialized read-write lock. Results\nare undefined if a read-write lock is used without first being\ninitialized. \n</P>\n<P>If the <B>pthread_rwlock_init</B> function fails, <I>rwlock</I>\nshall not be initialized and the contents of <I>rwlock</I> are\nundefined. \n</P>\n<P><B>Pthreads-w32</B> supports statically initialized <I>rwlock</I>\nobjects using <B>PTHREAD_RWLOCK_INITIALIZER</B>. <SPAN STYLE=\"font-weight: medium\">\nAn application should still call <B>pthread_rwlock_destroy</B> at\nsome point to ensure that any resources consumed by the read/write\nlock are released.</SPAN></P>\n<P>Only the object referenced by <I>rwlock</I> may be used for\nperforming synchronization. The result of referring to copies of that\nobject in calls to <B>pthread_rwlock_destroy</B> ,\n<B>pthread_rwlock_rdlock</B> , <B>pthread_rwlock_timedrdlock</B> ,\n<B>pthread_rwlock_timedwrlock</B> , <B>pthread_rwlock_tryrdlock</B> ,\n<B>pthread_rwlock_trywrlock</B> , <B>pthread_rwlock_unlock</B> , or\n<B>pthread_rwlock_wrlock</B> is undefined. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_READER_WRITER_LOCKS</B> in\npthread.h as 200112L to indicate that the reader/writer routines are\nimplemented and may be used.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>If successful, the <B>pthread_rwlock_destroy</B> and\n<B>pthread_rwlock_init</B> functions shall return zero; otherwise, an\nerror number shall be returned to indicate the error. \n</P>\n<P>The [EBUSY] and [EINVAL] error checks, if implemented, act as if\nthey were performed immediately at the beginning of processing for\nthe function and caused an error return prior to modifying the state\nof the read-write lock specified by <I>rwlock</I>. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_rwlock_destroy</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EBUSY</B> \n\t</DT><DD>\n\tThe implementation has detected an attempt to destroy the object\n\treferenced by <I>rwlock</I> while it is locked. \n\t</DD><DT>\n\t<B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>rwlock</I> is invalid. \n\t</DD></DL>\n<P>\nThe <B>pthread_rwlock_init</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>EAGAIN</B> \n\t</DT><DD>\n\tThe system lacked the necessary resources (other than memory) to\n\tinitialize another read-write lock. \n\t</DD><DT>\n\t<B>ENOMEM</B> \n\t</DT><DD>\n\tInsufficient memory exists to initialize the read-write lock. \n\t</DD><DD STYLE=\"margin-left: 0cm; margin-bottom: 0.5cm\">\n\t<BR><BR>\n\t</DD></DL>\n<P>\nThe <B>pthread_rwlock_init</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>attr</I> is invalid. \n\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>Applications using these and related read-write lock functions may\nbe subject to priority inversion, as discussed in the Base\nDefinitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001, Section 3.285,\nPriority Inversion. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_rdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_timedrdlock.html\"><B>pthread_rwlock_timedrdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_timedwrlock.html\"><B>pthread_rwlock_timedwrlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_tryrdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_trywrlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_unlock.html\"><B>pthread_rwlock_unlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_wrlock</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_rwlock_rdlock.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_RWLOCK_RDLOCK&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;10352600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;12554200\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_rwlock_rdlock, pthread_rwlock_tryrdlock - lock a\nread-write lock object for reading \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_rwlock_rdlock(pthread_rwlock_t <I>*</I></B><I>rwlock</I><B>);\n<BR></B>i<B>nt pthread_rwlock_tryrdlock(pthread_rwlock_t <I>*</I></B><I>rwlock</I><B>);\n</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_rwlock_rdlock</B> function shall apply a read lock\nto the read-write lock referenced by <I>rwlock</I>. The calling\nthread acquires the read lock if a writer does not hold the lock and\nthere are no writers blocked on the lock. \n</P>\n<P><B>Pthreads-win32</B> does not prefer either writers or readers in\nacquiring the lock – all threads enter a single prioritised FIFO\nqueue. While this may not be optimally efficient for some\napplications, it does ensure that one type does not starve the other.</P>\n<P>A thread may hold multiple concurrent read locks on <I>rwlock</I>\n(that is, successfully call the <B>pthread_rwlock_rdlock</B> function\n<I>n</I> times). If so, the application shall ensure that the thread\nperforms matching unlocks (that is, it calls the\n<A HREF=\"pthread_rwlock_unlock.html\"><B>pthread_rwlock_unlock</B>(3)</A>\nfunction <I>n</I> times). \n</P>\n<P>The <B>pthread_rwlock_tryrdlock</B> function shall apply a read\nlock as in the <B>pthread_rwlock_rdlock</B> function, with the\nexception that the function shall fail if the equivalent\n<B>pthread_rwlock_rdlock</B> call would have blocked the calling\nthread. In no case shall the <B>pthread_rwlock_tryrdlock</B> function\never block; it always either acquires the lock or fails and returns\nimmediately. \n</P>\n<P>Results are undefined if any of these functions are called with an\nuninitialized read-write lock. \n</P>\n<P><B>Pthreads-w32</B> does not detect deadlock if the thread already\nowns the lock for writing.</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_READER_WRITER_LOCKS</B> in\npthread.h as  200112L to indicate that the reader/writer routines are\nimplemented and may be used.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>If successful, the <B>pthread_rwlock_rdlock</B> function shall\nreturn zero; otherwise, an error number shall be returned to indicate\nthe error. \n</P>\n<P>The <B>pthread_rwlock_tryrdlock</B> function shall return zero if\nthe lock for reading on the read-write lock object referenced by\n<I>rwlock</I> is acquired. Otherwise, an error number shall be\nreturned to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_rwlock_tryrdlock</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>EBUSY</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe read-write lock could not be acquired for reading because a\n\twriter holds the lock or a writer with the appropriate priority was\n\tblocked on it. \n\t</DD></DL>\n<P>\nThe <B>pthread_rwlock_rdlock</B> and <B>pthread_rwlock_tryrdlock</B>\nfunctions may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD>\n\tThe value specified by <I>rwlock</I> does not refer to an\n\tinitialized read-write lock object. \n\t</DD><DT>\n\t<B>EAGAIN</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe read lock could not be acquired because the maximum number of\n\tread locks for <I>rwlock</I> has been exceeded. \n\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>Applications using these functions may be subject to priority\ninversion, as discussed in the Base Definitions volume of\nIEEE&nbsp;Std&nbsp;1003.1-2001, Section 3.285, Priority Inversion. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_rwlock_init.html\"><B>pthread_rwlock_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_timedrdlock.html\"><B>pthread_rwlock_timedrdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_timedwrlock.html\"><B>pthread_rwlock_timedwrlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_trywrlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_unlock.html\"><B>pthread_rwlock_unlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_wrlock</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_rwlock_timedrdlock.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_RWLOCK_TIMEDRDLOCK&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;11101700\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;12560000\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_rwlock_timedrdlock - lock a read-write lock for reading \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; <BR></B>#include &lt;time.h&gt; \n</P>\n<P><B>int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict</B>\n<I>rwlock</I><B>, const struct timespec *restrict</B> <I>abs_timeout</I><B>);\n</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_rwlock_timedrdlock</B> function shall apply a read\nlock to the read-write lock referenced by <I>rwlock</I> as in the\n<A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_rdlock</B>(3)</A>\nfunction. However, if the lock cannot be acquired without waiting for\nother threads to unlock the lock, this wait shall be terminated when\nthe specified timeout expires. The timeout shall expire when the\nabsolute time specified by <I>abs_timeout</I> passes, as measured by\nthe clock on which timeouts are based (that is, when the value of\nthat clock equals or exceeds <I>abs_timeout</I>), or if the absolute\ntime specified by <I>abs_timeout</I> has already been passed at the\ntime of the call. \n</P>\n<P>The <B>timespec</B> data type is defined in the <I>&lt;time.h&gt;</I>\nheader. Under no circumstances shall the function fail with a timeout\nif the lock can be acquired immediately. The validity of the\n<I>abs_timeout</I> parameter need not be checked if the lock can be\nimmediately acquired. \n</P>\n<P>The calling thread may deadlock if at the time the call is made it\nholds a write lock on <I>rwlock</I>. The results are undefined if\nthis function is called with an uninitialized read-write lock. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_READER_WRITER_LOCKS</B> in\npthread.h as  200112L to indicate that the reader/writer routines are\nimplemented and may be used.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>The <B>pthread_rwlock_timedrdlock</B> function shall return zero\nif the lock for reading on the read-write lock object referenced by\n<I>rwlock</I> is acquired. Otherwise, an error number shall be\nreturned to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_rwlock_timedrdlock</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>ETIMEDOUT</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe lock could not be acquired before the specified timeout expired.\n\t\t</DD></DL>\n<P>\nThe <B>pthread_rwlock_timedrdlock</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EAGAIN</B> \n\t</DT><DD>\n\tThe read lock could not be acquired because the maximum number of\n\tread locks for lock would be exceeded. \n\t</DD><DT>\n\t<B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>rwlock</I> does not refer to an\n\tinitialized read-write lock object, or the <I>abs_timeout</I>\n\tnanosecond value is less than zero or greater than or equal to 1000\n\tmillion. \n\t</DD></DL>\n<P>\nThis function shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>Applications using this function may be subject to priority\ninversion, as discussed in the Base Definitions volume of\nIEEE&nbsp;Std&nbsp;1003.1-2001, Section 3.285, Priority Inversion. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_rwlock_init.html\"><B>pthread_rwlock_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_rdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_timedwrlock.html\"><B>pthread_rwlock_timedwrlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_tryrdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_trywrlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_unlock.html\"><B>pthread_rwlock_unlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_wrlock</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I>, <I>&lt;time.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_rwlock_timedwrlock.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_RWLOCK_TIMEDWRLOCK&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;11172800\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;12561600\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_rwlock_timedwrlock - lock a read-write lock for writing \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; <BR></B>#include &lt;time.h&gt; \n</P>\n<P><B>int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict</B>\n<I>rwlock</I><B>, const struct timespec *restrict</B> <I>abs_timeout</I><B>);\n</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_rwlock_timedwrlock</B> function shall apply a write\nlock to the read-write lock referenced by <I>rwlock</I> as in the\n<A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_wrlock</B>(3)</A>\nfunction. However, if the lock cannot be acquired without waiting for\nother threads to unlock the lock, this wait shall be terminated when\nthe specified timeout expires. The timeout shall expire when the\nabsolute time specified by <I>abs_timeout</I> passes, as measured by\nthe clock on which timeouts are based (that is, when the value of\nthat clock equals or exceeds <I>abs_timeout</I>), or if the absolute\ntime specified by <I>abs_timeout</I> has already been passed at the\ntime of the call. \n</P>\n<P>The <B>timespec</B> data type is defined in the <I>&lt;time.h&gt;</I>\nheader. Under no circumstances shall the function fail with a timeout\nif the lock can be acquired immediately. The validity of the\n<I>abs_timeout</I> parameter need not be checked if the lock can be\nimmediately acquired. \n</P>\n<P>The calling thread may deadlock if at the time the call is made it\nholds the read-write lock. The results are undefined if this function\nis called with an uninitialized read-write lock. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_READER_WRITER_LOCKS</B> in\npthread.h as  200112L to indicate that the reader/writer routines are\nimplemented and may be used.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>The <B>pthread_rwlock_timedwrlock</B> function shall return zero\nif the lock for writing on the read-write lock object referenced by\n<I>rwlock</I> is acquired. Otherwise, an error number shall be\nreturned to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_rwlock_timedwrlock</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>ETIMEDOUT</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe lock could not be acquired before the specified timeout expired.\n\t\t</DD></DL>\n<P>\nThe <B>pthread_rwlock_timedwrlock</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by rwlock does not refer to an initialized\n\tread-write lock object, or the <I>abs_timeout</I> nanosecond value\n\tis less than zero or greater than or equal to 1000 million. \n\t</DD></DL>\n<P>\nThis function shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>Applications using this function may be subject to priority\ninversion, as discussed in the Base Definitions volume of\nIEEE&nbsp;Std&nbsp;1003.1-2001, Section 3.285, Priority Inversion. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_rwlock_init.html\"><B>pthread_rwlock_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_rdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_timedrdlock.html\"><B>pthread_rwlock_timedrdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_tryrdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_trywrlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_unlock.html\"><B>pthread_rwlock_unlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_wrlock</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I>, <I>&lt;time.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_rwlock_unlock.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_RWLOCK_UNLOCK&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;11291100\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;12562700\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_rwlock_unlock - unlock a read-write lock object \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P>in<B>t pthread_rwlock_unlock(pthread_rwlock_t <I>*</I></B><I>rwlock</I><B>);\n</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_rwlock_unlock</B> function shall release a lock\nheld on the read-write lock object referenced by <I>rwlock</I>.\nResults are undefined if the read-write lock <I>rwlock</I> is not\nheld by the calling thread. \n</P>\n<P>If this function is called to release a read lock from the\nread-write lock object and there are other read locks currently held\non this read-write lock object, the read-write lock object remains in\nthe read locked state. If this function releases the last read lock\nfor this read-write lock object, the read-write lock object shall be\nput in the unlocked state with no owners. \n</P>\n<P>If this function is called to release a write lock for this\nread-write lock object, the read-write lock object shall be put in\nthe unlocked state. \n</P>\n<P><B>Pthreads-win32</B> does not prefer either writers or readers in\nacquiring the lock – all threads enter a single prioritised FIFO\nqueue. While this may not be optimally efficient for some\napplications, it does ensure that one type does not starve the other.</P>\n<P>Results are undefined if any of these functions are called with an\nuninitialized read-write lock. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_READER_WRITER_LOCKS</B> in\npthread.h as  200112L to indicate that the reader/writer routines are\nimplemented and may be used.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>If successful, the <B>pthread_rwlock_unlock</B> function shall\nreturn zero; otherwise, an error number shall be returned to indicate\nthe error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_rwlock_unlock</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD>\n\tThe value specified by <I>rwlock</I> does not refer to an\n\tinitialized read-write lock object. \n\t</DD><DD STYLE=\"margin-left: 0cm; margin-bottom: 0.5cm\">\n\t<BR><BR>\n\t</DD></DL>\n<P>\nThe <B>pthread_rwlock_unlock</B> function shall not return an error\ncode of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_rwlock_init.html\"><B>pthread_rwlock_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_rdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_timedrdlock.html\"><B>pthread_rwlock_timedrdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_timedwrlock.html\"><B>pthread_rwlock_timedwrlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_tryrdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_trywrlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_wrlock.html\"><B>pthread_rwlock_wrlock</B>(3)</A>\n, the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_rwlock_wrlock.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_RWLOCK_TRYWRLOCK&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;11221900\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;12563800\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_rwlock_trywrlock, pthread_rwlock_wrlock - lock a\nread-write lock object for writing \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_rwlock_trywrlock(pthread_rwlock_t <I>*</I></B><I>rwlock</I><B>);\n<BR>int pthread_rwlock_wrlock(pthread_rwlock_t <I>*</I></B><I>rwlock</I><B>);\n</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_rwlock_trywrlock</B> function shall apply a write\nlock like the <B>pthread_rwlock_wrlock</B> function, with the\nexception that the function shall fail if any thread currently holds\n<I>rwlock</I> (for reading or writing). \n</P>\n<P>The <B>pthread_rwlock_wrlock</B> function shall apply a write lock\nto the read-write lock referenced by <I>rwlock</I>. The calling\nthread acquires the write lock if no other thread (reader or writer)\nholds the read-write lock <I>rwlock</I>. Otherwise, the thread shall\nblock until it can acquire the lock. The calling thread may deadlock\nif at the time the call is made it holds the read-write lock (whether\na read or write lock). \n</P>\n<P><B>Pthreads-win32</B> does not prefer either writers or readers in\nacquiring the lock – all threads enter a single prioritised FIFO\nqueue. While this may not be optimally efficient for some\napplications, it does ensure that one type does not starve the other.</P>\n<P>Results are undefined if any of these functions are called with an\nuninitialized read-write lock. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_READER_WRITER_LOCKS</B> in\npthread.h as  200112L to indicate that the reader/writer routines are\nimplemented and may be used.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>The <B>pthread_rwlock_trywrlock</B> function shall return zero if\nthe lock for writing on the read-write lock object referenced by\n<I>rwlock</I> is acquired. Otherwise, an error number shall be\nreturned to indicate the error. \n</P>\n<P>If successful, the <B>pthread_rwlock_wrlock</B> function shall\nreturn zero; otherwise, an error number shall be returned to indicate\nthe error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_rwlock_trywrlock</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>EBUSY</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe read-write lock could not be acquired for writing because it was\n\talready locked for reading or writing. \n\t</DD></DL>\n<P>\nThe <B>pthread_rwlock_trywrlock</B> and <B>pthread_rwlock_wrlock</B>\nfunctions may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>rwlock</I> does not refer to an\n\tinitialized read-write lock object. \n\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>Applications using these functions may be subject to priority\ninversion, as discussed in the Base Definitions volume of\nIEEE&nbsp;Std&nbsp;1003.1-2001, Section 3.285, Priority Inversion. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_rwlock_init.html\"><B>pthread_rwlock_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_rdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_timedrdlock.html\"><B>pthread_rwlock_timedrdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_timedwrlock.html\"><B>pthread_rwlock_timedwrlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_rdlock.html\"><B>pthread_rwlock_tryrdlock</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlock_unlock.html\"><B>pthread_rwlock_unlock</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_rwlockattr_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_RWLOCKATTR_DESTROY&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;504000\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;18474200\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_rwlockattr_destroy, pthread_rwlockattr_init - destroy and\ninitialize the read-write lock attributes object \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_rwlockattr_destroy(pthread_rwlockattr_t <I>*</I></B><I>attr</I><B>);\n<BR>int pthread_rwlockattr_init(pthread_rwlockattr_t <I>*</I></B><I>attr</I><B>);\n</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_rwlockattr_destroy</B> function shall destroy a\nread-write lock attributes object. A destroyed <I>attr</I> attributes\nobject can be reinitialized using <B>pthread_rwlockattr_init</B> ;\nthe results of otherwise referencing the object after it has been\ndestroyed are undefined. An implementation may cause\n<B>pthread_rwlockattr_destroy</B> to set the object referenced by\n<I>attr</I> to an invalid value. \n</P>\n<P>The <B>pthread_rwlockattr_init</B> function shall initialize a\nread-write lock attributes object <I>attr</I> with the default value\nfor all of the attributes defined by the implementation. \n</P>\n<P>Results are undefined if <B>pthread_rwlockattr_init</B> is called\nspecifying an already initialized <I>attr</I> attributes object. \n</P>\n<P>After a read-write lock attributes object has been used to\ninitialize one or more read-write locks, any function affecting the\nattributes object (including destruction) shall not affect any\npreviously initialized read-write locks. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_READER_WRITER_LOCKS</B> in\npthread.h as 200112L to indicate that the reader/writer routines are\nimplemented and may be used.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>If successful, the <B>pthread_rwlockattr_destroy</B> and\n<B>pthread_rwlockattr_init</B> functions shall return zero;\notherwise, an error number shall be returned to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_rwlockattr_destroy</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>attr</I> is invalid. \n\t</DD></DL>\n<P>\nThe <B>pthread_rwlockattr_init</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>ENOMEM</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tInsufficient memory exists to initialize the read-write lock\n\tattributes object. \n\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_rwlock_init.html\"><B>pthread_rwlock_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlockattr_setpshared.html\"><B>pthread_rwlockattr_getpshared</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlockattr_setpshared.html\"><B>pthread_rwlockattr_setpshared</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_rwlockattr_setpshared.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_RWLOCKATTR_GETPSHARED&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;10124400\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;12572100\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_rwlockattr_getpshared, pthread_rwlockattr_setpshared - get\nand set the process-shared attribute of the read-write lock\nattributes object \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *\nrestrict </B><I>attr</I><B>, int *restrict</B> <I>pshared</I><B>);\n<BR>int pthread_rwlockattr_setpshared(pthread_rwlockattr_t <I>*</I></B><I>attr</I><B>,\nint</B> <I>pshared</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_rwlockattr_getpshared</B> function shall obtain the\nvalue of the <I>process-shared</I> attribute from the initialized\nattributes object referenced by <I>attr</I>. The\n<B>pthread_rwlockattr_setpshared</B> function shall set the\n<I>process-shared</I> attribute in an initialized attributes object\nreferenced by <I>attr</I>. \n</P>\n<P>The <I>process-shared</I> attribute shall be set to\n<B>PTHREAD_PROCESS_SHARED</B> to permit a read-write lock to be\noperated upon by any thread that has access to the memory where the\nread-write lock is allocated, even if the read-write lock is\nallocated in memory that is shared by multiple processes. If the\n<I>process-shared</I> attribute is <B>PTHREAD_PROCESS_PRIVATE</B>,\nthe read-write lock shall only be operated upon by threads created\nwithin the same process as the thread that initialized the read-write\nlock; if threads of differing processes attempt to operate on such a\nread-write lock, the behavior is undefined. The default value of the\n<I>process-shared</I> attribute shall be <B>PTHREAD_PROCESS_PRIVATE</B>.\n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_THREAD_PROCESS_SHARED</B> in\npthread.h as -1 to indicate that these routines are implemented but\nthey do not support the process shared option.</P>\n<P>Additional attributes, their default values, and the names of the\nassociated functions to get and set those attribute values are\nimplementation-defined. \n</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_READER_WRITER_LOCKS</B> in\npthread.h as  200112L to indicate that the reader/writer routines are\nimplemented and may be used.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, the <B>pthread_rwlockattr_getpshared</B>\nfunction shall return zero and store the value of the <I>process-shared</I>\nattribute of <I>attr</I> into the object referenced by the <I>pshared</I>\nparameter. Otherwise, an error number shall be returned to indicate\nthe error. \n</P>\n<P>If successful, the <B>pthread_rwlockattr_setpshared</B> function\nshall return zero; otherwise, an error number shall be returned to\nindicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_rwlockattr_getpshared</B> and\n<B>pthread_rwlockattr_setpshared</B> functions may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>attr</I> is invalid. \n\t</DD></DL>\n<P>\nThe <B>pthread_rwlockattr_setpshared</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe new value specified for the attribute is outside the range of\n\tlegal values for that attribute. \n\t</DD><DT>\n\t<B>ENOTSUP</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe new value specified for the attribute is <B>PTHREAD_PROCESS_SHARED</B>.\n\t\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_rwlock_init.html\"><B>pthread_rwlock_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlockattr_init.html\"><B>pthread_rwlockattr_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_rwlockattr_init.html\"><B>pthread_rwlockattr_init</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_self.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_SELF(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;11361600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;11575700\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_self - return identifier of current thread \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>pthread_t pthread_self(void);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P><B>pthread_self</B> return the thread identifier for the calling\nthread. \n</P>\n<P><B>Pthreads-w32</B> also provides support for <B>Win32</B> native\nthreads to interact with POSIX threads through the pthreads API.\nWhereas all threads created via a call to pthread_create have a POSIX\nthread ID and thread state, the library ensures that any Win32 native\nthreads that interact through the Pthreads API also generate a POSIX\nthread ID and thread state when and if necessary. This provides full\n<SPAN LANG=\"en-GB\">reciprocity</SPAN> between Win32 and POSIX\nthreads. Win32 native threads that generate a POSIX thread ID and\nstate are treated by the library as having been created with the\n<B>PTHREAD_CREATE_DETACHED</B> attribute.</P>\n<P>Any Win32 native thread may call <B>pthread_self</B> directly to\nreturn it's POSIX thread identifier. The ID and state will be\ngenerated if it does not already exist. Win32 native threads do not\nneed to call <B>pthread_self</B> before calling Pthreads-w32 routines\nunless that routine requires a pthread_t parameter.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">See Also</A></H2>\n<P><A HREF=\"pthread_equal.html\"><B>pthread_equal</B>(3)</A> ,\n<A HREF=\"pthread_join.html\"><B>pthread_join</B>(3)</A> ,\n<A HREF=\"pthread_detach.html\"><B>pthread_detach</B>(3)</A> ,\n<A HREF=\"pthread_setschedparam.html\"><B>pthread_setschedparam</B>(3)</A>\n, <A HREF=\"pthread_setschedparam.html\"><B>pthread_getschedparam</B>(3)</A>\n. \n</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Author</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect4\" NAME=\"toc4\">See Also</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_setcancelstate.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_CANCEL(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;12090500\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050504;16361300\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_cancel, pthread_setcancelstate, pthread_setcanceltype,\npthread_testcancel - thread cancellation \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_cancel(pthread_t </B><I>thread</I><B>);</B> \n</P>\n<P><B>int pthread_setcancelstate(int </B><I>state</I><B>, int\n*</B><I>oldstate</I><B>);</B> \n</P>\n<P><B>int pthread_setcanceltype(int </B><I>type</I><B>, int\n*</B><I>oldtype</I><B>);</B> \n</P>\n<P><B>void pthread_testcancel(void);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Cancellation is the mechanism by which a thread can terminate the\nexecution of another thread. More precisely, a thread can send a\ncancellation request to another thread. Depending on its settings,\nthe target thread can then either ignore the request, honor it\nimmediately, or defer it until it reaches a cancellation point. \n</P>\n<P>When a thread eventually honors a cancellation request, it\nperforms as if <B>pthread_exit(PTHREAD_CANCELED)</B> has been called\nat that point: all cleanup handlers are executed in reverse order,\ndestructor functions for thread-specific data are called, and finally\nthe thread stops executing with the return value <B>PTHREAD_CANCELED</B>.\nSee <A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> for more\ninformation. \n</P>\n<P><B>pthread_cancel</B> sends a cancellation request to the thread\ndenoted by the <I>thread</I> argument. \n</P>\n<P><B>pthread_setcancelstate</B> changes the cancellation state for\nthe calling thread -- that is, whether cancellation requests are\nignored or not. The <I>state</I> argument is the new cancellation\nstate: either <B>PTHREAD_CANCEL_ENABLE</B> to enable cancellation, or\n<B>PTHREAD_CANCEL_DISABLE</B> to disable cancellation (cancellation\nrequests are ignored). If <I>oldstate</I> is not <B>NULL</B>, the\nprevious cancellation state is stored in the location pointed to by\n<I>oldstate</I>, and can thus be restored later by another call to\n<B>pthread_setcancelstate</B>. \n</P>\n<P><B>pthread_setcanceltype</B> changes the type of responses to\ncancellation requests for the calling thread: asynchronous\n(immediate) or deferred. The <I>type</I> argument is the new\ncancellation type: either <B>PTHREAD_CANCEL_ASYNCHRONOUS</B> to\ncancel the calling thread as soon as the cancellation request is\nreceived, or <B>PTHREAD_CANCEL_DEFERRED</B> to keep the cancellation\nrequest pending until the next cancellation point. If <I>oldtype</I>\nis not <B>NULL</B>, the previous cancellation state is stored in the\nlocation pointed to by <I>oldtype</I>, and can thus be restored later\nby another call to <B>pthread_setcanceltype</B>. \n</P>\n<P><B>Pthreads-w32</B> provides two levels of support for\n<B>PTHREAD_CANCEL_ASYNCHRONOUS</B>: full and partial. Full support\nrequires an additional DLL and driver be installed on the Windows\nsystem (see the See Also section below) that allows blocked threads\nto be cancelled immediately. Partial support means that the target\nthread will not cancel until it resumes execution naturally. Partial\nsupport is provided if either the DLL or the driver are not\nautomatically detected by the pthreads-w32 library at run-time.</P>\n<P>Threads are always created by <A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A>\nwith cancellation enabled and deferred. That is, the initial\ncancellation state is <B>PTHREAD_CANCEL_ENABLE</B> and the initial\ntype is <B>PTHREAD_CANCEL_DEFERRED</B>. \n</P>\n<P>Cancellation points are those points in the program execution\nwhere a test for pending cancellation requests is performed and\ncancellation is executed if positive. The following POSIX threads\nfunctions are cancellation points: \n</P>\n<P><A HREF=\"pthread_join.html\"><B>pthread_join</B>(3)</A>\n<BR><A HREF=\"pthread_cond_wait.html\"><B>pthread_cond_wait</B>(3)</A>\n<BR><A HREF=\"pthread_cond_timedwait.html\"><B>pthread_cond_timedwait</B>(3)</A>\n<BR><A HREF=\"pthread_testcancel.html\"><B>pthread_testcancel</B>(3)</A>\n<BR><A HREF=\"sem_wait.html\"><B>sem_wait</B>(3)</A> <BR><A HREF=\"sem_timedwait.html\"><B>sem_timedwait</B>(3)</A>\n<BR><A HREF=\"sigwait.html\"><B>sigwait</B>(3)</A> (not supported under\n<B>Pthreads-w32</B>)</P>\n<P><B>Pthreads-w32</B> provides two functions to enable additional\ncancellation points to be created in user functions that block on\nWin32 HANDLEs:</P>\n<P><A HREF=\"pthreadCancelableWait.html\">pthreadCancelableWait()</A>\n<BR><A HREF=\"pthreadCancelableTimedWait.html\">pthreadCancelableTimedWait()</A></P>\n<P>All other POSIX threads functions are guaranteed not to be\ncancellation points. That is, they never perform cancellation in\ndeferred cancellation mode. \n</P>\n<P><B>pthread_testcancel</B> does nothing except testing for pending\ncancellation and executing it. Its purpose is to introduce explicit\nchecks for cancellation in long sequences of code that do not call\ncancellation point functions otherwise. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P><B>pthread_cancel</B>, <B>pthread_setcancelstate</B> and\n<B>pthread_setcanceltype</B> return 0 on success and a non-zero error\ncode on error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P><B>pthread_cancel</B> returns the following error code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ESRCH</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tno thread could be found corresponding to that specified by the\n\t\t<I>thread</I> ID. \n\t\t</DD></DL>\n</DL>\n<P>\n<B>pthread_setcancelstate</B> returns the following error code on\nerror: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe <I>state</I> argument is not \n\t\t</DD></DL>\n</DL>\n<BLOCKQUOTE>\n<B>PTHREAD_CANCEL_ENABLE</B> nor <B>PTHREAD_CANCEL_DISABLE</B> \n</BLOCKQUOTE>\n<P><B>pthread_setcanceltype</B> returns the following error code on\nerror: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe <I>type</I> argument is not \n\t\t</DD></DL>\n</DL>\n<BLOCKQUOTE>\n<B>PTHREAD_CANCEL_DEFERRED</B> nor <B>PTHREAD_CANCEL_ASYNCHRONOUS</B>\n</BLOCKQUOTE>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">See Also</A></H2>\n<P><A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> ,\n<A HREF=\"pthread_cleanup_push.html\"><B>pthread_cleanup_push</B>(3)</A>\n, <A HREF=\"pthread_cleanup_pop.html\"><B>pthread_cleanup_pop</B>(3)</A>\n, Pthreads-w32 package README file 'Prerequisites' section. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Bugs</A></H2>\n<P>POSIX specifies that a number of system calls (basically, all\nsystem calls that may block, such as <A HREF=\"read.html\"><B>read</B>(2)</A>\n, <A HREF=\"write.html\"><B>write</B>(2)</A> , <A HREF=\"wait.html\"><B>wait</B>(2)</A>\n, etc.) and library functions that may call these system calls (e.g.\n<A HREF=\"fprintf.html\"><B>fprintf</B>(3)</A> ) are cancellation\npoints. <B>Pthreads-win32</B> is not integrated enough with the C\nlibrary to implement this, and thus none of the C library functions\nis a cancellation point. \n</P>\n<P>A workaround for these calls is to temporarily switch to\nasynchronous cancellation (assuming full asynchronous cancellation\nsupport is installed). So, checking for cancellation during a <B>read</B>\nsystem call, for instance, can be achieved as follows: \n</P>\n<BLOCKQUOTE><BR><BR>\n</BLOCKQUOTE>\n<PRE STYLE=\"margin-left: 1cm; margin-right: 1cm\">pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &amp;oldCancelType);\nread(fd, buffer, length);\npthread_setcanceltype(oldCancelType, NULL);</PRE>\n<HR>\n<BLOCKQUOTE><A NAME=\"toc\"></A><B>Table of Contents</B></BLOCKQUOTE>\n<UL>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Author</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">See\n\tAlso</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE><A HREF=\"#sect7\" NAME=\"toc7\">Bugs</A> \n\t</BLOCKQUOTE>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_setcanceltype.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_CANCEL(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;12090500\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050504;16362800\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_cancel, pthread_setcancelstate, pthread_setcanceltype,\npthread_testcancel - thread cancellation \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_cancel(pthread_t </B><I>thread</I><B>);</B> \n</P>\n<P><B>int pthread_setcancelstate(int </B><I>state</I><B>, int\n*</B><I>oldstate</I><B>);</B> \n</P>\n<P><B>int pthread_setcanceltype(int </B><I>type</I><B>, int\n*</B><I>oldtype</I><B>);</B> \n</P>\n<P><B>void pthread_testcancel(void);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Cancellation is the mechanism by which a thread can terminate the\nexecution of another thread. More precisely, a thread can send a\ncancellation request to another thread. Depending on its settings,\nthe target thread can then either ignore the request, honor it\nimmediately, or defer it until it reaches a cancellation point. \n</P>\n<P>When a thread eventually honors a cancellation request, it\nperforms as if <B>pthread_exit(PTHREAD_CANCELED)</B> has been called\nat that point: all cleanup handlers are executed in reverse order,\ndestructor functions for thread-specific data are called, and finally\nthe thread stops executing with the return value <B>PTHREAD_CANCELED</B>.\nSee <A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> for more\ninformation. \n</P>\n<P><B>pthread_cancel</B> sends a cancellation request to the thread\ndenoted by the <I>thread</I> argument. \n</P>\n<P><B>pthread_setcancelstate</B> changes the cancellation state for\nthe calling thread -- that is, whether cancellation requests are\nignored or not. The <I>state</I> argument is the new cancellation\nstate: either <B>PTHREAD_CANCEL_ENABLE</B> to enable cancellation, or\n<B>PTHREAD_CANCEL_DISABLE</B> to disable cancellation (cancellation\nrequests are ignored). If <I>oldstate</I> is not <B>NULL</B>, the\nprevious cancellation state is stored in the location pointed to by\n<I>oldstate</I>, and can thus be restored later by another call to\n<B>pthread_setcancelstate</B>. \n</P>\n<P><B>pthread_setcanceltype</B> changes the type of responses to\ncancellation requests for the calling thread: asynchronous\n(immediate) or deferred. The <I>type</I> argument is the new\ncancellation type: either <B>PTHREAD_CANCEL_ASYNCHRONOUS</B> to\ncancel the calling thread as soon as the cancellation request is\nreceived, or <B>PTHREAD_CANCEL_DEFERRED</B> to keep the cancellation\nrequest pending until the next cancellation point. If <I>oldtype</I>\nis not <B>NULL</B>, the previous cancellation state is stored in the\nlocation pointed to by <I>oldtype</I>, and can thus be restored later\nby another call to <B>pthread_setcanceltype</B>. \n</P>\n<P><B>Pthreads-w32</B> provides two levels of support for\n<B>PTHREAD_CANCEL_ASYNCHRONOUS</B>: full and partial. Full support\nrequires an additional DLL and driver be installed on the Windows\nsystem (see the See Also section below) that allows blocked threads\nto be cancelled immediately. Partial support means that the target\nthread will not cancel until it resumes execution naturally. Partial\nsupport is provided if either the DLL or the driver are not\nautomatically detected by the pthreads-w32 library at run-time.</P>\n<P>Threads are always created by <A HREF=\"pthread_create.html\"><B>pthread_create</B>(3)</A>\nwith cancellation enabled and deferred. That is, the initial\ncancellation state is <B>PTHREAD_CANCEL_ENABLE</B> and the initial\ntype is <B>PTHREAD_CANCEL_DEFERRED</B>. \n</P>\n<P>Cancellation points are those points in the program execution\nwhere a test for pending cancellation requests is performed and\ncancellation is executed if positive. The following POSIX threads\nfunctions are cancellation points: \n</P>\n<P><A HREF=\"pthread_join.html\"><B>pthread_join</B>(3)</A>\n<BR><A HREF=\"pthread_cond_wait.html\"><B>pthread_cond_wait</B>(3)</A>\n<BR><A HREF=\"pthread_cond_timedwait.html\"><B>pthread_cond_timedwait</B>(3)</A>\n<BR><A HREF=\"pthread_testcancel.html\"><B>pthread_testcancel</B>(3)</A>\n<BR><A HREF=\"sem_wait.html\"><B>sem_wait</B>(3)</A> <BR><A HREF=\"sem_timedwait.html\"><B>sem_timedwait</B>(3)</A>\n<BR><A HREF=\"sigwait.html\"><B>sigwait</B>(3)</A> (not supported under\n<B>Pthreads-w32</B>)</P>\n<P><B>Pthreads-w32</B> provides two functions to enable additional\ncancellation points to be created in user functions that block on\nWin32 HANDLEs:</P>\n<P><A HREF=\"pthreadCancelableWait.html\">pthreadCancelableWait()</A>\n<BR><A HREF=\"pthreadCancelableTimedWait.html\">pthreadCancelableTimedWait()</A></P>\n<P>All other POSIX threads functions are guaranteed not to be\ncancellation points. That is, they never perform cancellation in\ndeferred cancellation mode. \n</P>\n<P><B>pthread_testcancel</B> does nothing except testing for pending\ncancellation and executing it. Its purpose is to introduce explicit\nchecks for cancellation in long sequences of code that do not call\ncancellation point functions otherwise. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P><B>pthread_cancel</B>, <B>pthread_setcancelstate</B> and\n<B>pthread_setcanceltype</B> return 0 on success and a non-zero error\ncode on error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P><B>pthread_cancel</B> returns the following error code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ESRCH</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tno thread could be found corresponding to that specified by the\n\t\t<I>thread</I> ID. \n\t\t</DD></DL>\n</DL>\n<P>\n<B>pthread_setcancelstate</B> returns the following error code on\nerror: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe <I>state</I> argument is not \n\t\t</DD></DL>\n</DL>\n<BLOCKQUOTE>\n<B>PTHREAD_CANCEL_ENABLE</B> nor <B>PTHREAD_CANCEL_DISABLE</B> \n</BLOCKQUOTE>\n<P><B>pthread_setcanceltype</B> returns the following error code on\nerror: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe <I>type</I> argument is not \n\t\t</DD></DL>\n</DL>\n<BLOCKQUOTE>\n<B>PTHREAD_CANCEL_DEFERRED</B> nor <B>PTHREAD_CANCEL_ASYNCHRONOUS</B>\n</BLOCKQUOTE>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">See Also</A></H2>\n<P><A HREF=\"pthread_exit.html\"><B>pthread_exit</B>(3)</A> ,\n<A HREF=\"pthread_cleanup_push.html\"><B>pthread_cleanup_push</B>(3)</A>\n, <A HREF=\"pthread_cleanup_pop.html\"><B>pthread_cleanup_pop</B>(3)</A>\n, Pthreads-w32 package README file 'Prerequisites' section. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Bugs</A></H2>\n<P>POSIX specifies that a number of system calls (basically, all\nsystem calls that may block, such as <A HREF=\"read.html\"><B>read</B>(2)</A>\n, <A HREF=\"write.html\"><B>write</B>(2)</A> , <A HREF=\"wait.html\"><B>wait</B>(2)</A>\n, etc.) and library functions that may call these system calls (e.g.\n<A HREF=\"fprintf.html\"><B>fprintf</B>(3)</A> ) are cancellation\npoints. <B>Pthreads-win32</B> is not integrated enough with the C\nlibrary to implement this, and thus none of the C library functions\nis a cancellation point. \n</P>\n<P>A workaround for these calls is to temporarily switch to\nasynchronous cancellation (assuming full asynchronous cancellation\nsupport is installed). So, checking for cancellation during a <B>read</B>\nsystem call, for instance, can be achieved as follows: \n</P>\n<BLOCKQUOTE><BR><BR>\n</BLOCKQUOTE>\n<PRE STYLE=\"margin-left: 1cm; margin-right: 1cm\">pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &amp;oldCancelType);\nread(fd, buffer, length);\npthread_setcanceltype(oldCancelType, NULL);</PRE>\n<HR>\n<BLOCKQUOTE><A NAME=\"toc\"></A><B>Table of Contents</B></BLOCKQUOTE>\n<UL>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Author</A>\n\t\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">See\n\tAlso</A> \n\t</BLOCKQUOTE>\n\t<LI><BLOCKQUOTE><A HREF=\"#sect7\" NAME=\"toc7\">Bugs</A> \n\t</BLOCKQUOTE>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_setconcurrency.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_GETCONCURRENCY&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;19162600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;19223800\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_getconcurrency, pthread_setconcurrency - get and set the\nlevel of concurrency \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_getconcurrency(void);</B> <BR><B>int\npthread_setconcurrency(int</B> <I>new_level</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Unbound threads in a process may or may not be required to be\nsimultaneously active. By default, the threads implementation ensures\nthat a sufficient number of threads are active so that the process\ncan continue to make progress. While this conserves system resources,\nit may not produce the most effective level of concurrency. \n</P>\n<P>The <B>pthread_setconcurrency</B> function allows an application\nto inform the threads implementation of its desired concurrency\nlevel, <I>new_level</I>. The actual level of concurrency provided by\nthe implementation as a result of this function call is unspecified. \n</P>\n<P>If <I>new_level</I> is zero, it causes the implementation to\nmaintain the concurrency level at its discretion as if\n<B>pthread_setconcurrency</B> had never been called. \n</P>\n<P>The <B>pthread_getconcurrency</B> function shall return the value\nset by a previous call to the <B>pthread_setconcurrency</B> function.\nIf the <B>pthread_setconcurrency</B> function was not previously\ncalled, this function shall return zero to indicate that the\nimplementation is maintaining the concurrency level. \n</P>\n<P>A call to <B>pthread_setconcurrency</B> shall inform the\nimplementation of its desired concurrency level. The implementation\nshall use this as a hint, not a requirement. \n</P>\n<P>If an implementation does not support multiplexing of user threads\non top of several kernel-scheduled entities, the\n<B>pthread_setconcurrency</B> and <B>pthread_getconcurrency</B>\nfunctions are provided for source code compatibility but they shall\nhave no effect when called. To maintain the function semantics, the\n<I>new_level</I> parameter is saved when <B>pthread_setconcurrency</B>\nis called so that a subsequent call to <B>pthread_getconcurrency</B>\nshall return the same value. \n</P>\n<P><B>Pthreads-w32</B> provides these routines for source code\ncompatibility only, as described in the previous paragraph.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>If successful, the <B>pthread_setconcurrency</B> function shall\nreturn zero; otherwise, an error number shall be returned to indicate\nthe error. \n</P>\n<P>The <B>pthread_getconcurrency</B> function shall always return the\nconcurrency level set by a previous call to <B>pthread_setconcurrency</B>\n. If the <B>pthread_setconcurrency</B> function has never been\ncalled, <B>pthread_getconcurrency</B> shall return zero. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_setconcurrency</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD>\n\tThe value specified by <I>new_level</I> is negative. \n\t</DD><DT>\n\t<B>EAGAIN</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specific by <I>new_level</I> would cause a system resource\n\tto be exceeded. \n\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>Use of these functions changes the state of the underlying\nconcurrency upon which the application depends. Library developers\nare advised to not use the <B>pthread_getconcurrency</B> and\n<B>pthread_setconcurrency</B> functions since their use may conflict\nwith an applications use of these functions. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P>The Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_setschedparam.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_SETSCHEDPARAM(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 2.4  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050504;18110100\">\n\t<META NAME=\"CHANGEDBY\" CONTENT=\"Ross Johnson\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20080630;22330400\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_setschedparam, pthread_getschedparam - control thread\nscheduling \n</P>\n<P>parameters \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>int pthread_setschedparam(pthread_t </B><I>target_thread</I><B>,\nint </B><I>policy</I><B>, const struct sched_param *</B><I>param</I><B>);</B>\n</P>\n<P><B>int pthread_getschedparam(pthread_t </B><I>target_thread</I><B>,\nint *</B><I>policy</I><B>, struct sched_param *</B><I>param</I><B>);</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P><B>pthread_setschedparam</B> sets the scheduling parameters for\nthe thread <I>target_thread</I> as indicated by <I>policy</I> and\n<I>param</I>. <I>policy</I> can be either <B>SCHED_OTHER</B>\n(regular, non-real-time scheduling), <B>SCHED_RR</B> (real-time,\nround-robin) or <B>SCHED_FIFO</B> (real-time, first-in first-out).\n<I>param</I> specifies the scheduling priority for the two real-time\npolicies.</P>\n<P><B>Pthreads-w32</B> only supports SCHED_OTHER and does not support\nthe real-time scheduling policies <B>SCHED_RR</B> and <B>SCHED_FIFO.</B>\n</P>\n<P><B>pthread_getschedparam</B> retrieves the scheduling policy and\nscheduling parameters for the thread <I>target_thread</I> and stores\nthem in the locations pointed to by <I>policy</I> and <I>param</I>,\nrespectively. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P><B>pthread_setschedparam</B> and <B>pthread_getschedparam</B>\nreturn 0 on success and a non-zero error code on error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>On error, <B>pthread_setschedparam</B> returns the following error\ncodes: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ENOTSUP</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<I>policy</I> is not <B>SCHED_OTHER.</B></DD><DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tOne of the arguments is invalid, or the priority value specified by\n\t\t<I>param</I> is not valid for the specified policy.</DD><DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<B>ESRCH</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tThe <I>target_thread</I> is invalid or has already terminated \n\t\t</DD></DL>\n</DL>\n<P>\nOn error, <B>pthread_getschedparam</B> returns the following error\ncodes: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ESRCH</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tthe <I>target_thread</I> is invalid or has already terminated \n\t\t</DD></DL>\n</DL>\n<H2>\n<A HREF=\"#toc5\" NAME=\"sect5\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">See Also</A></H2>\n<P><A HREF=\"sched_setscheduler.html\"><B>sched_setscheduler</B>(2)</A>\n, <A HREF=\"sched_getscheduler.html\"><B>sched_getscheduler</B>(2)</A>\n, <A HREF=\"sched_getparam.html\"><B>sched_getparam</B>(2)</A> ,\n<A HREF=\"pthread_attr_init.html\"><B>pthread_attr_setschedpolicy</B>(3)</A>\n, <A HREF=\"pthread_attr_init.html\"><B>pthread_attr_setschedparam</B>(3)</A>\n. \n</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Author</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">See Also</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_spin_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_SPIN_DESTROY&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;12074100\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;19014200\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_spin_destroy, pthread_spin_init - destroy or initialize a\nspin lock object (<B>ADVANCED REALTIME THREADS</B>) \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>pthread_spinlock_t</B> <I>lock</I> <B>=\nPTHREAD_SPINLOCK_INITIALIZER;</B></P>\n<P><B>int pthread_spin_destroy(pthread_spinlock_t *</B><I>lock</I><B>);\n<BR>int pthread_spin_init(pthread_spinlock_t *</B><I>lock</I><B>, int</B>\n<I>pshared</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_spin_destroy</B> function shall destroy the spin\nlock referenced by <I>lock</I> and release any resources used by the\nlock. The effect of subsequent use of the lock is undefined until the\nlock is reinitialized by another call to <B>pthread_spin_init</B> .\nThe results are undefined if <B>pthread_spin_destroy</B> is called\nwhen a thread holds the lock, or if this function is called with an\nuninitialized thread spin lock. \n</P>\n<P>The <B>pthread_spin_init</B> function shall allocate any resources\nrequired to use the spin lock referenced by <I>lock</I> and\ninitialize the lock to an unlocked state. \n</P>\n<P><B>Pthreads-w32</B> supports single and multiple processor systems\nas well as process CPU affinity masking by checking the mask when the\nspin lock is initialized. If the process is using only a single\nprocessor at the time <B>pthread_spin_init</B> is called then the\nspin lock is initialized as a PTHREAD_MUTEX_NORMAL mutex object. A\nthread that calls <A HREF=\"pthread_spin_lock.html\"><B>pthread_spin_lock(3)</B></A>\nwill block rather than spin in this case. If the process CPU affinity\nmask is altered after the spin lock has been initialised, the spin\nlock is not modified, and may no longer be optimal for the number of\nCPUs available.</P>\n<P><B>Pthreads-w32</B> defines <B>_POSIX_THREAD_PROCESS_SHARED</B> in\npthread.h as -1 to indicate that these routines do not support the\n<B>PTHREAD_PROCESS_SHARED</B> attribute. <B>pthread_spin_init</B>\nwill return the error <B>ENOTSUP</B> if the value of <I>pshared</I>\nis not <B>PTHREAD_PROCESS_PRIVATE</B>.</P>\n<P>The results are undefined if <B>pthread_spin_init</B> is called\nspecifying an already initialized spin lock. The results are\nundefined if a spin lock is used without first being initialized. \n</P>\n<P>If the <B>pthread_spin_init</B> function fails, the lock is not\ninitialized and the contents of <I>lock</I> are undefined. \n</P>\n<P>Only the object referenced by <I>lock</I> may be used for\nperforming synchronization. \n</P>\n<P>The result of referring to copies of that object in calls to\n<B>pthread_spin_destroy</B> , <A HREF=\"pthread_spin_lock.html\"><B>pthread_spin_lock</B>(3)</A>\n, <A HREF=\"pthread_spin_lock.html\"><B>pthread_spin_trylock</B>(3)</A>,\nor <A HREF=\"pthread_spin_unlock.html\"><B>pthread_spin_unlock</B>(3)</A>\nis undefined. \n</P>\n<P><B>Pthreads-w32</B> supports statically initialized spin locks\nusing <B>PTHREAD_SPINLOCK_INITIALIZER</B>. An application should\nstill call <B>pthread_spin_destroy</B> at some point to ensure that\nany resources consumed by the spin lock are released.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, these functions shall return zero;\notherwise, an error number shall be returned to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>These functions may fail if: \n</P>\n<DL>\n\t<DT><B>EBUSY</B> \n\t</DT><DD>\n\tThe implementation has detected an attempt to initialize or destroy\n\ta spin lock while it is in use (for example, while being used in a\n\t<A HREF=\"pthread_spin_lock.html\"><B>pthread_spin_lock</B>(3)</A>\n\tcall) by another thread. \n\t</DD><DT>\n\t<B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>lock</I> is invalid. \n\t</DD></DL>\n<P>\nThe <B>pthread_spin_init</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>ENOTSUP</B> \n\t</DT><DD>\n\tThe value of <I>pshared</I> is not <B>PTHREAD_PROCESS_PRIVATE</B>.</DD><DT>\n\t<B>ENOMEM</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tInsufficient memory exists to initialize the lock. \n\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>The <B>pthread_spin_destroy</B> and <B>pthread_spin_init</B>\nfunctions are part of the Spin Locks option and need not be provided\non all implementations. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_spin_lock.html\"><B>pthread_spin_lock</B>(3)</A> <B>,</B>\n<A HREF=\"pthread_spin_unlock.html\"><B>pthread_spin_unlock</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_spin_lock.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_SPIN_LOCK&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;12202300\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;19302300\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_spin_lock, pthread_spin_trylock - lock a spin lock object\n(<B>ADVANCED REALTIME THREADS</B>) \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_spin_lock(pthread_spinlock_t *</B><I>lock</I><B>);\n<BR>int pthread_spin_trylock(pthread_spinlock_t *</B><I>lock</I><B>);\n</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_spin_lock</B> function shall lock the spin lock\nreferenced by <I>lock</I>. The calling thread shall acquire the lock\nif it is not held by another thread. Otherwise, the thread shall spin\n(that is, shall not return from the <B>pthread_spin_lock</B> call)\nuntil the lock becomes available. The results are undefined if the\ncalling thread holds the lock at the time the call is made.</P>\n<P><B>Pthreads-w32</B> supports single and multiple processor systems\nas well as process CPU affinity masking by checking the mask when the\nspin lock is initialized. If the process is using only a single\nprocessor at the time <A HREF=\"pthread_spin_init.html\"><B>pthread_spin_init</B>(3)</A>\nis called then the spin lock is initialized as a PTHREAD_MUTEX_NORMAL\nmutex object. A thread that calls <B>pthread_spin_lock</B> will block\nrather than spin in this case. If the process CPU affinity mask is\naltered after the spin lock has been initialised, the spin lock is\nnot modified, and may no longer be optimal for the number of CPUs\navailable.</P>\n<P>The <B>pthread_spin_trylock</B> function shall lock the spin lock\nreferenced by <I>lock</I> if it is not held by any thread. Otherwise,\nthe function shall fail. \n</P>\n<P>The results are undefined if any of these functions is called with\nan uninitialized spin lock. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, these functions shall return zero;\notherwise, an error number shall be returned to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>These functions may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value specified by <I>lock</I> does not refer to an initialized\n\tspin lock object. \n\t</DD></DL>\n<P>\nThe <B>pthread_spin_trylock</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>EBUSY</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tA thread currently holds the lock. \n\t</DD></DL>\n<P>\nThese functions shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>Applications using this function may be subject to priority\ninversion, as discussed in the Base Definitions volume of\nIEEE&nbsp;Std&nbsp;1003.1-2001, Section 3.285, Priority Inversion. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_spin_init.html\"><B>pthread_spin_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_spin_unlock.html\"><B>pthread_spin_unlock</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_spin_unlock.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;PTHREAD_SPIN_UNLOCK&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;12401400\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;12462400\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>pthread_spin_unlock - unlock a spin lock object (<B>ADVANCED\nREALTIME THREADS</B>) \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt; </B>\n</P>\n<P><B>int pthread_spin_unlock(pthread_spinlock_t *</B><I>lock</I><B>);\n</B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>pthread_spin_unlock</B> function shall release the spin\nlock referenced by <I>lock</I> which was locked via the\n<A HREF=\"pthread_spin_lock.html\"><B>pthread_spin_lock</B>(3)</A> or\n<A HREF=\"pthread_spin_lock.html\"><B>pthread_spin_trylock</B>(3)</A>\nfunctions. If there are threads spinning on the lock when\n<B>pthread_spin_unlock</B> is called, the lock becomes available and\nan unspecified spinning thread shall acquire the lock. \n</P>\n<P><B>Pthreads-w32</B> does not check ownership of the lock and it is\ntherefore possible for a thread other than the locker to unlock the\nspin lock. This is not a feature that should be exploited.</P>\n<P>The results are undefined if this function is called with an\nuninitialized thread spin lock. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, the <B>pthread_spin_unlock</B>\nfunction shall return zero; otherwise, an error number shall be\nreturned to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>pthread_spin_unlock</B> function may fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD>\n\tAn invalid argument was specified. \n\t</DD><DD STYLE=\"margin-left: -2cm\">\n\t<BR>\n\t</DD></DL>\n<P>\nThis function shall not return an error code of [EINTR]. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P><B>Pthreads-w32</B> does not check ownership of the lock and it is\ntherefore possible for a thread other than the locker to unlock the\nspin lock. This is not a feature that should be exploited.</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"pthread_spin_init.html\"><B>pthread_spin_destroy</B>(3)</A>\n<B>,</B> <A HREF=\"pthread_spin_lock.html\"><B>pthread_spin_lock</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;pthread.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_timechange_handler_np.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_TIMECHANGE_HANDLER_NP manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;322600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;22110600\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P STYLE=\"font-weight: medium\">pthread_timechange_handler_np –\nalert timed waiting condition variables to system time changes.</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>void * pthread_timechange_handler_np(void * </B><I>dummy</I><B>);</B></P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>To improve tolerance against operator or time service initiated\nsystem clock changes.</P>\n<P><B>pthread_timechange_handler_np </B>can be called by an\napplication when it receives a WM_TIMECHANGE message from the system.\nAt present it broadcasts all condition variables so that waiting\nthreads can wake up and re-evaluate their conditions and restart\ntheir timed waits if required.</P>\n<P><B>pthread_timechange_handler_np </B>has the same return type and\nargument type as a thread routine so that it may be called directly\nthrough pthread_create(), i.e. as a separate thread. If run as a\nthread, the return code must be retrieved through <A HREF=\"pthread_join.html\"><B>pthread_join</B>()</A>.</P>\n<P>Although the <I>dummy</I> parameter is required it is not used and\nany value including NULL can be given.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P>None.</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\"><FONT COLOR=\"#000080\">Return Value</FONT></A></H2>\n<P><B>pthread_timechange_handler_np</B> returns 0 on success, or an\nerror code.</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<P>The <B>pthread_timechange_handler_np</B> function returns the\nfollowing error code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EAGAIN</B> \n\t\t</DT></DL>\n</DL>\n<P STYLE=\"margin-left: 2cm; margin-bottom: 0cm\">\nTo indicate that not all condition variables were signalled for some\nreason.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">Author</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_win32_attach_detach_np.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_WIN32_ATTACH_DETACH_NP manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;322600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;22415600\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P STYLE=\"font-weight: medium\">pthread_win32_process_attach_np,\npthread_win32_process_detach_np, pthread_win32_thread_attach_np,\npthread_win32_thread_detach_np – exposed versions of the\npthreads-w32 DLL dllMain() switch functionality for use when\nstatically linking the library.</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>BOOL pthread_win32_process_attach_np (void);</B></P>\n<P><B>BOOL pthread_win32_process_detach_np (void);</B></P>\n<P><B>BOOL pthread_win32_thread_attach_np (void);</B></P>\n<P><B>BOOL pthread_win32_thread_detach_np (void);</B></P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>These functions contain the code normally run via <b>dllMain</b>\nwhen the library is used as a dll but which need to be called\nexplicitly by an application when the library is statically linked. As of version 2.9.0,  the  static library built using either MSC or GCC includes RT hooks which will call the pthread_win32_process_*_np routines automatically on  start/exit of the application.</P>\n<P>You will need to call <B>pthread_win32_process_attach_np</B>\nbefore you can call any pthread routines when statically linking. You\nshould call <B>pthread_win32_process_detach_np</B> before exiting\nyour application to clean up.</P>\n<P><B>pthread_win32_thread_attach_np</B> is currently a no-op, but\n<B>pthread_win32_thread_detach_np</B> is needed to clean up the\nimplicit  pthread handle that is allocated to a Win32 thread if it\ncalls certain pthreads routines. Call this routine when the Win32\nthread exits.</P>\n<P>These functions invariably return TRUE except for\n<B>pthread_win32_process_attach_np</B> which will return FALSE if\npthreads-w32 initialisation fails.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P>None.</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\"><FONT COLOR=\"#000080\">Return Value</FONT></A></H2>\n<P>These routines return TRUE (non-zero) on success, or FALSE (0) if\nthey fail.</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<P>None.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">Author</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/pthread_win32_test_features_np.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>PTHREAD_WIN32_TEST_FEATURES_NP manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;322600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050510;17405600\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P STYLE=\"font-weight: medium\">pthread_win32_test_features_np –\nfind out what features were detected at process attach time.</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;pthread.h&gt;</B> \n</P>\n<P><B>BOOL pthread_win32_test_features_np(int</B> <I>mask</I><B>);</B></P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P><B>pthread_win32_test_features_np</B> allows an application to\ncheck which run-time auto-detected features are available within the\nlibrary.</P>\n<P>The possible features are:</P>\n<P><B>PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE</B></P>\n<P STYLE=\"margin-left: 2cm\">Return TRUE if the Win32 version of\nInterlockedCompareExchange() is being used. On IA32 systems the\nlibrary can use optimised and inlinable assembler versions of\nInterlockedExchange() and InterlockedCompareExchange().</P>\n<P><B>PTW32_ALERTABLE_ASYNC_CANCEL</B></P>\n<P STYLE=\"margin-left: 2cm\">Return TRUE if the QueueUserAPCEx package\nQUSEREX.DLL and the AlertDrv.sys driver was detected. This package\nprovides alertable (pre-emptive) asynchronous threads cancellation.\nIf this feature returns FALSE then the default async cancel scheme is\nin use, which cannot cancel blocked threads.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P>None.</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\"><FONT COLOR=\"#000080\">Return Value</FONT></A></H2>\n<P><B>pthread_win32_test_features_np</B> returns TRUE (non-zero) if\nthe specified feature is present, and FALSE (0) otherwise.</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Errors</A></H2>\n<P>None.</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Author</A></H2>\n<P>Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Errors</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect6\" NAME=\"toc6\">Author</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/sched_get_priority_max.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;SCHED_GET_PRIORITY_MAX&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;12575100\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050510;12533300\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>sched_get_priority_max, sched_get_priority_min - get priority\nlimits (<B>REALTIME</B>) \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;sched.h&gt; </B>\n</P>\n<P><B>int sched_get_priority_max(int</B> <I>policy</I><B>); <BR>int\nsched_get_priority_min(int</B> <I>policy</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>sched_get_priority_max</B> and <B>sched_get_priority_min</B>\nfunctions shall return the appropriate maximum or minimum,\nrespectively, for the scheduling policy specified by <I>policy</I>. \n</P>\n<P>The value of <I>policy</I> shall be one of the scheduling policy\nvalues defined in <I>&lt;sched.h&gt;</I>. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>If successful, the <B>sched_get_priority_max</B> and\n<B>sched_get_priority_min</B> functions shall return the appropriate\nmaximum or minimum values, respectively. If unsuccessful, they shall\nreturn a value of -1 and set <I>errno</I> to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>sched_get_priority_max</B> and <B>sched_get_priority_min</B>\nfunctions shall fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tThe value of the <I>policy</I> parameter does not represent a\n\tdefined scheduling policy. \n\t</DD></DL>\n<P>\n<I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"sched_getscheduler.html\"><B>sched_getscheduler</B>(3)</A>\n<B>,</B> <A HREF=\"sched_setscheduler.html\"><B>sched_setscheduler</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;sched.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/manual/sched_getscheduler.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;SCHED_GETSCHEDULER&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;13153500\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;13202000\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>sched_getscheduler - get scheduling policy (<B>REALTIME</B>) \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;sched.h&gt; </B>\n</P>\n<P><B>int sched_getscheduler(pid_t</B> <I>pid</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>sched_getscheduler</B> function shall return the scheduling\npolicy of the process specified by <I>pid</I>. If the value of <I>pid</I>\nis negative, the behavior of the <B>sched_getscheduler</B> function\nis unspecified. \n</P>\n<P>The values that can be returned by <B>sched_getscheduler</B> are\ndefined in the <I>&lt;sched.h&gt;</I> header. \n</P>\n<P><B>Pthreads-w32</B> only supports the <B>SCHED_OTHER</B> policy,\nwhich is the only value that can be returned. However, checks on <I>pid</I>\nand permissions are performed first so that the other useful side\neffects of this routine are retained.</P>\n<P>If a process specified by <I>pid</I> exists, and if the calling\nprocess has permission, the scheduling policy shall be returned for\nthe process whose process ID is equal to <I>pid</I>. \n</P>\n<P>If <I>pid</I> is zero, the scheduling policy shall be returned for\nthe calling process. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, the <B>sched_getscheduler</B> function\nshall return the scheduling policy of the specified process. If\nunsuccessful, the function shall return -1 and set <I>errno</I> to\nindicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>sched_getscheduler</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>EPERM</B> \n\t</DT><DD>\n\tThe requesting process does not have permission to determine the\n\tscheduling policy of the specified process. \n\t</DD><DT>\n\t<B>ESRCH</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tNo process can be found corresponding to that specified by <I>pid</I>.\n\t\t</DD></DL>\n<P>\n<I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"sched_setscheduler.html\"><B>sched_setscheduler</B>(3)</A>\n, the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;sched.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/sched_setscheduler.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;SCHED_SETSCHEDULER&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;13012200\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;13193700\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>sched_setscheduler - set scheduling policy and parameters\n(<B>REALTIME</B>) \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;sched.h&gt; </B>\n</P>\n<P><B>int sched_setscheduler(pid_t</B> <I>pid</I><B>, int</B> <I>policy</I><B>,\nconst struct sched_param *</B><I>param</I><B>); </B>\n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>sched_setscheduler</B> function shall set the scheduling\npolicy and scheduling parameters of the process specified by <I>pid</I>\nto <I>policy</I> and the parameters specified in the <B>sched_param</B>\nstructure pointed to by <I>param</I>, respectively. The value of the\n<I>sched_priority</I> member in the <B>sched_param</B> structure\nshall be any integer within the inclusive priority range for the\nscheduling policy specified by <I>policy</I>. If the value of <I>pid</I>\nis negative, the behavior of the <B>sched_setscheduler</B> function\nis unspecified. \n</P>\n<P>The possible values for the <I>policy</I> parameter are defined in\nthe <I>&lt;sched.h&gt;</I> header. \n</P>\n<P><B>Pthreads-w32</B> only supports the <B>SCHED_OTHER</B> policy.\nAny other value for <I>policy</I> will return failure with errno set\nto <B>ENOSYS</B>. However, checks on <I>pid</I> and permissions are\nperformed first so that the other useful side effects of this routine\nare retained.</P>\n<P>If a process specified by <I>pid</I> exists, and if the calling\nprocess has permission, the scheduling policy and scheduling\nparameters shall be set for the process whose process ID is equal to\n<I>pid</I>. \n</P>\n<P>If <I>pid</I> is zero, the scheduling policy and scheduling\nparameters shall be set for the calling process. \n</P>\n<P>Implementations may require that the requesting process have\npermission to set its own scheduling parameters or those of another\nprocess. Additionally, implementation-defined restrictions may apply\nas to the appropriate privileges required to set a process’ own\nscheduling policy, or another process’ scheduling policy, to a\nparticular value. \n</P>\n<P>The <B>sched_setscheduler</B> function shall be considered\nsuccessful if it succeeds in setting the scheduling policy and\nscheduling parameters of the process specified by <I>pid</I> to the\nvalues specified by <I>policy</I> and the structure pointed to by\n<I>param</I>, respectively. \n</P>\n<P>The effect of this function on individual threads is dependent on\nthe scheduling contention scope of the threads: \n</P>\n<DL>\n\t<DT>* \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tFor threads with system scheduling contention scope, these functions\n\tshall have no effect on their scheduling. \n\t</DD><DT>\n\t* \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tFor threads with process scheduling contention scope, the threads’\n\tscheduling policy and associated parameters shall not be affected.\n\tHowever, the scheduling of these threads with respect to threads in\n\tother processes may be dependent on the scheduling parameters of\n\ttheir process, which are governed using these functions. \n\t</DD></DL>\n<P>\nThis function is not atomic with respect to other threads in the\nprocess. Threads may continue to execute while this function call is\nin the process of changing the scheduling policy and associated\nscheduling parameters for the underlying kernel-scheduled entities\nused by the process contention scope threads. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>Upon successful completion, the function shall return the former\nscheduling policy of the specified process. If the <B>sched_setscheduler</B>\nfunction fails to complete successfully, the policy and scheduling\nparameters shall remain unchanged, and the function shall return a\nvalue of -1 and set <I>errno</I> to indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>The <B>sched_setscheduler</B> function shall fail if: \n</P>\n<DL>\n\t<DT><B>EINVAL</B> \n\t</DT><DD>\n\tThe value of the <I>policy</I> parameter is invalid, or one or more\n\tof the parameters contained in <I>param</I> is outside the valid\n\trange for the specified scheduling policy. \n\t</DD><DT>\n\t<B>EPERM</B> \n\t</DT><DD>\n\tThe requesting process does not have permission to set either or\n\tboth of the scheduling parameters or the scheduling policy of the\n\tspecified process. \n\t</DD><DT>\n\t<B>ESRCH</B> \n\t</DT><DD STYLE=\"margin-bottom: 0.5cm\">\n\tNo process can be found corresponding to that specified by <I>pid</I>.\n\t\t</DD></DL>\n<P>\n<I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P><A HREF=\"sched_getscheduler.html\"><B>sched_getscheduler</B>(3)</A>\n<B>,</B> the Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;sched.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/sched_yield.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>&quot;SCHED_YIELD&quot;(P) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;14034600\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050505;14050300\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>sched_yield - yield the processor \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;sched.h&gt; </B>\n</P>\n<P><B>int sched_yield(void);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>The <B>sched_yield</B> function shall force the running thread to\nrelinquish the processor until it again becomes the head of its\nthread list. It takes no arguments. \n</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Return Value</A></H2>\n<P>The <B>sched_yield</B> function shall return 0 if it completes\nsuccessfully; otherwise, it shall return a value of -1 and set <I>errno</I>\nto indicate the error. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Errors</A></H2>\n<P>No errors are defined. \n</P>\n<P><I>The following sections are informative.</I> \n</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Examples</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Application Usage</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc7\" NAME=\"sect7\">Rationale</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">Future Directions</A></H2>\n<P>None. \n</P>\n<H2><A HREF=\"#toc9\" NAME=\"sect9\">See Also</A></H2>\n<P>The Base Definitions volume of IEEE&nbsp;Std&nbsp;1003.1-2001,\n<I>&lt;sched.h&gt;</I> \n</P>\n<H2><A HREF=\"#toc10\" NAME=\"sect10\">Copyright</A></H2>\n<P>Portions of this text are reprinted and reproduced in electronic\nform from IEEE Std 1003.1, 2003 Edition, Standard for Information\nTechnology -- Portable Operating System Interface (POSIX), The Open\nGroup Base Specifications Issue 6, Copyright (C) 2001-2003 by the\nInstitute of Electrical and Electronics Engineers, Inc and The Open\nGroup. In the event of any discrepancy between this version and the\noriginal IEEE and The Open Group Standard, the original IEEE and The\nOpen Group Standard is the referee document. The original Standard\ncan be obtained online at <A HREF=\"http://www.opengroup.org/unix/online.html\">http://www.opengroup.org/unix/online.html</A>\n. \n</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Examples</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Application\n\tUsage</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Rationale</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect8\" NAME=\"toc8\">Future\n\tDirections</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect9\" NAME=\"toc9\">See\n\tAlso</A> \n\t</P>\n\t<LI><P><A HREF=\"#sect10\" NAME=\"toc10\">Copyright</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>\n"
  },
  {
    "path": "deps/w32-pthreads/manual/sem_init.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n<HTML>\n<HEAD>\n\t<META HTTP-EQUIV=\"CONTENT-TYPE\" CONTENT=\"text/html; charset=utf-8\">\n\t<TITLE>SEMAPHORES(3) manual page</TITLE>\n\t<META NAME=\"GENERATOR\" CONTENT=\"OpenOffice.org 1.1.3  (Linux)\">\n\t<META NAME=\"CREATED\" CONTENT=\"20050505;14061900\">\n\t<META NAME=\"CHANGED\" CONTENT=\"20050514;195200\">\n\t<!-- manual page source format generated by PolyglotMan v3.2, -->\n\t<!-- available at http://polyglotman.sourceforge.net/ -->\n</HEAD>\n<BODY LANG=\"en-GB\" BGCOLOR=\"#ffffff\" DIR=\"LTR\">\n<H4>POSIX Threads for Windows – REFERENCE - <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A></H4>\n<P><A HREF=\"index.html\">Reference Index</A></P>\n<P><A HREF=\"#toc\">Table of Contents</A></P>\n<H2><A HREF=\"#toc0\" NAME=\"sect0\">Name</A></H2>\n<P>sem_init, sem_wait, sem_trywait, sem_post, sem_getvalue,\nsem_destroy - operations on semaphores \n</P>\n<H2><A HREF=\"#toc1\" NAME=\"sect1\">Synopsis</A></H2>\n<P><B>#include &lt;semaphore.h&gt;</B> \n</P>\n<P><B>int sem_init(sem_t *</B><I>sem</I><B>, int </B><I>pshared</I><B>,\nunsigned int </B><I>value</I><B>);</B> \n</P>\n<P><B>int sem_wait(sem_t * </B><I>sem</I><B>);</B> \n</P>\n<P><B>int sem_timedwait(sem_t * </B><I>sem</I>, <B>const struct\ntimespec *</B><I>abstime</I><B>);</B> \n</P>\n<P><B>int sem_trywait(sem_t * </B><I>sem</I><B>);</B> \n</P>\n<P><B>int sem_post(sem_t * </B><I>sem</I><B>);</B> \n</P>\n<P><B>int sem_post_multiple(sem_t * </B><I>sem, </I><B>int</B>\n<I>number</I><B>);</B> \n</P>\n<P><B>int sem_getvalue(sem_t * </B><I>sem</I><B>, int * </B><I>sval</I><B>);</B>\n</P>\n<P><B>int sem_destroy(sem_t * </B><I>sem</I><B>);</B> \n</P>\n<H2><A HREF=\"#toc2\" NAME=\"sect2\">Description</A></H2>\n<P>Semaphores are counters for resources shared between threads. The\nbasic operations on semaphores are: increment the counter atomically,\nand wait until the counter is non-null and decrement it atomically. \n</P>\n<P><B>sem_init</B> initializes the semaphore object pointed to by\n<I>sem</I>. The count associated with the semaphore is set initially\nto <I>value</I>. The <I>pshared</I> argument indicates whether the\nsemaphore is local to the current process ( <I>pshared</I> is zero)\nor is to be shared between several processes ( <I>pshared</I> is not\nzero).</P>\n<P><B>Pthreads-w32</B> currently does not support process-shared\nsemaphores, thus <B>sem_init</B> always returns with error <B>EPERM</B>\nif <I>pshared</I> is not zero. \n</P>\n<P><B>sem_wait</B> atomically decrements <I>sem</I>'s count if it is\ngreater than 0 and returns immediately or it suspends the calling\nthread until it can resume following a call to <B>sem_post</B> or\n<B>sem_post_multiple</B>.</P>\n<P><B>sem_timedwait</B> atomically decrements <I>sem</I>'s count if\nit is greater than 0 and returns immediately, or it suspends the\ncalling thread. If <I>abstime</I> time arrives before the thread can\nresume following a call to <B>sem_post</B> or <B>sem_post_multiple</B>,\nthen <B>sem_timedwait</B> returns with a return code of -1 after\nhaving set <B>errno</B> to <B>ETIMEDOUT</B>. If the call can return\nwithout suspending then <I>abstime</I> is not checked.</P>\n<P><B>sem_trywait</B> atomically decrements <I>sem</I>'s count if it\nis greater than 0 and returns immediately, or it returns immediately\nwith a return code of -1 after having set <B>errno</B> to <B>EAGAIN</B>.\n<B>sem_trywait</B> never blocks.</P>\n<P><B>sem_post</B> either releases one thread if there are any\nwaiting on <I>sem</I>, or it atomically increments <I>sem</I>'s\ncount.</P>\n<P><B>sem_post_multiple</B> either releases multiple threads if there\nare any waiting on <I>sem</I> and/or it atomically increases <I>sem</I>'s\ncount. If there are currently <I>n</I> waiters, where <I>n</I> the\nlargest number less than or equal to <I>number</I>, then <I>n</I>\nwaiters are released and <I>sem</I>'s count is incremented by <I>number</I>\nminus <I>n</I>.</P>\n<P><B>sem_getvalue</B> stores in the location pointed to by <I>sval</I>\nthe current count of the semaphore <I>sem</I>. In the <B>Pthreads-w32</B>\nimplementation: if the value returned in <I>sval</I> is greater than\nor equal to 0 it was the <I>sem</I>'s count at some point during the\ncall to <B>sem_getvalue</B>. If the value returned in <I>sval</I> is\nless than 0 then it's absolute value represents the number of threads\nwaiting on <I>sem</I> at some point during the call to <B>sem_getvalue.\n</B>POSIX does not require an implementation of <B>sem_getvalue</B>\nto return a value in <I>sval</I> that is less than 0, but if it does\nthen it's absolute value must represent the number of waiters.</P>\n<P><B>sem_destroy</B> destroys a semaphore object, freeing the\nresources it might hold. No threads should be waiting on the\nsemaphore at the time <B>sem_destroy</B> is called.</P>\n<H2><A HREF=\"#toc3\" NAME=\"sect3\">Cancellation</A></H2>\n<P><B>sem_wait</B> and <B>sem_timedwait</B> are cancellation points. \n</P>\n<H2><A HREF=\"#toc4\" NAME=\"sect4\">Async-signal Safety</A></H2>\n<P>These routines are not async-cancel safe.</P>\n<H2><A HREF=\"#toc5\" NAME=\"sect5\">Return Value</A></H2>\n<P>All semaphore functions return 0 on success, or -1 on error in\nwhich case they write an error code in <B>errno</B>. \n</P>\n<H2><A HREF=\"#toc6\" NAME=\"sect6\">Errors</A></H2>\n<P>The <B>sem_init</B> function sets <B>errno</B> to the following\ncodes on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EINVAL</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<I>value</I> exceeds the maximal counter value <B>SEM_VALUE_MAX</B>\n\t\t\t\t</DD><DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\t<B>ENOSYS</B> \n\t\t</DT></DL>\n</DL>\n<BLOCKQUOTE STYLE=\"margin-left: 3cm\">\n<I>pshared</I> is not zero \n</BLOCKQUOTE>\n<P>The <B>sem_timedwait</B> function sets <B>errno</B> to the\nfollowing error code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ETIMEDOUT</B>\n\t\t\t\t</DT></DL>\n</DL>\n<BLOCKQUOTE STYLE=\"margin-left: 3cm\">\nif <I>abstime</I> arrives before the waiting thread can resume\nfollowing a call to <B>sem_post</B> or <B>sem_post_multiple</B>. \n</BLOCKQUOTE>\n<P>The <B>sem_trywait</B> function sets <B>errno</B> to the following\nerror code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EAGAIN</B> \n\t\t</DT></DL>\n</DL>\n<BLOCKQUOTE STYLE=\"margin-left: 3cm\">\nif the semaphore count is currently 0 \n</BLOCKQUOTE>\n<P>The <B>sem_post</B> and <B>sem_post_multiple</B> functions set\n<B>errno</B> to the following error code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>ERANGE</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tif after incrementing, the semaphore count would exceed\n\t\t<B>SEM_VALUE_MAX</B> (the semaphore count is left unchanged in this\n\t\tcase) \n\t\t</DD></DL>\n</DL>\n<P>\nThe <B>sem_destroy</B> function sets <B>errno</B> to the following\nerror code on error: \n</P>\n<DL>\n\t<DL>\n\t\t<DT STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\"><B>EBUSY</B> \n\t\t</DT><DD STYLE=\"margin-right: 1cm; margin-bottom: 0.5cm\">\n\t\tif some threads are currently blocked waiting on the semaphore. \n\t\t</DD></DL>\n</DL>\n<H2>\n<A HREF=\"#toc7\" NAME=\"sect7\">Author</A></H2>\n<P>Xavier Leroy &lt;Xavier.Leroy@inria.fr&gt; \n</P>\n<P>Modified by Ross Johnson for use with <A HREF=\"http://sources.redhat.com/pthreads-win32\">Pthreads-w32</A>.</P>\n<H2><A HREF=\"#toc8\" NAME=\"sect8\">See Also</A></H2>\n<P><A HREF=\"pthread_mutex_init.html\"><B>pthread_mutex_init</B>(3)</A>\n, <A HREF=\"pthread_cond_init.html\"><B>pthread_cond_init</B>(3)</A> ,\n<A HREF=\"pthread_cancel.html\"><B>pthread_cancel</B>(3)</A> . \n</P>\n<HR>\n<P><A NAME=\"toc\"></A><B>Table of Contents</B></P>\n<UL>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect0\" NAME=\"toc0\">Name</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect1\" NAME=\"toc1\">Synopsis</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect2\" NAME=\"toc2\">Description</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect3\" NAME=\"toc3\">Cancellation</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect4\" NAME=\"toc4\">Async-signal\n\tSafety</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect5\" NAME=\"toc5\">Return\n\tValue</A> \n\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect6\" NAME=\"toc6\">Errors</A>\n\t\t</P>\n\t<LI><P STYLE=\"margin-bottom: 0cm\"><A HREF=\"#sect7\" NAME=\"toc7\">Author</A>\n\t\t</P>\n\t<LI><P><A HREF=\"#sect8\" NAME=\"toc8\">See Also</A> \n\t</P>\n</UL>\n</BODY>\n</HTML>"
  },
  {
    "path": "deps/w32-pthreads/misc.c",
    "content": "/*\n * misc.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n#include \"pthread_kill.c\"\n#include \"pthread_once.c\"\n#include \"pthread_self.c\"\n#include \"pthread_equal.c\"\n#include \"pthread_setconcurrency.c\"\n#include \"pthread_getconcurrency.c\"\n#include \"ptw32_new.c\"\n#include \"ptw32_calloc.c\"\n#include \"ptw32_reuse.c\"\n#include \"w32_CancelableWait.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/mutex.c",
    "content": "/*\n * mutex.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#if ! defined(_UWIN) && ! defined(WINCE)\n#   include <process.h>\n#endif\n#if !defined(NEED_FTIME)\n#include <sys/timeb.h>\n#endif\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n#include \"ptw32_mutex_check_need_init.c\"\n#include \"pthread_mutex_init.c\"\n#include \"pthread_mutex_destroy.c\"\n#include \"pthread_mutexattr_init.c\"\n#include \"pthread_mutexattr_destroy.c\"\n#include \"pthread_mutexattr_getpshared.c\"\n#include \"pthread_mutexattr_setpshared.c\"\n#include \"pthread_mutexattr_settype.c\"\n#include \"pthread_mutexattr_gettype.c\"\n#include \"pthread_mutexattr_setrobust.c\"\n#include \"pthread_mutexattr_getrobust.c\"\n#include \"pthread_mutex_lock.c\"\n#include \"pthread_mutex_timedlock.c\"\n#include \"pthread_mutex_unlock.c\"\n#include \"pthread_mutex_trylock.c\"\n#include \"pthread_mutex_consistent.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/need_errno.h",
    "content": "/***\n* errno.h - system wide error numbers (set by system calls)\n*\n*       Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.\n*\n* Purpose:\n*       This file defines the system-wide error numbers (set by\n*       system calls).  Conforms to the XENIX standard.  Extended\n*       for compatibility with Uniforum standard.\n*       [System V]\n*\n*       [Public]\n*\n****/\n\n#if     _MSC_VER > 1000\n#pragma once\n#endif\n\n#if !defined(_INC_ERRNO)\n#define _INC_ERRNO\n\n#if !defined(_WIN32)\n#error ERROR: Only Win32 targets supported!\n#endif\n\n#include <winsock.h>\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\n\n\n/* Define _CRTIMP */\n\n#ifndef _CRTIMP\n#if defined(_DLL)\n#define _CRTIMP __declspec(dllimport)\n#else   /* ndef _DLL */\n#define _CRTIMP\n#endif  /* _DLL */\n#endif  /* _CRTIMP */\n\n\n/* Define __cdecl for non-Microsoft compilers */\n\n#if ( !defined(_MSC_VER) && !defined(__cdecl) )\n#define __cdecl\n#endif\n\n/* Define _CRTAPI1 (for compatibility with the NT SDK) */\n\n#if !defined(_CRTAPI1)\n#if\t_MSC_VER >= 800 && _M_IX86 >= 300\n#define _CRTAPI1 __cdecl\n#else\n#define _CRTAPI1\n#endif\n#endif\n\n#if !defined(PTW32_STATIC_LIB)\n#  if defined(PTW32_BUILD)\n#    define PTW32_DLLPORT __declspec (dllexport)\n#  else\n#    define PTW32_DLLPORT __declspec (dllimport)\n#  endif\n#else\n#  define PTW32_DLLPORT\n#endif\n\n/* declare reference to errno */\n\n#if (defined(_MT) || defined(_MD) || defined(_DLL)) && !defined(_MAC)\nPTW32_DLLPORT int * __cdecl _errno(void);\n#define errno   (*_errno())\n#else   /* ndef _MT && ndef _MD && ndef _DLL */\n_CRTIMP extern int errno;\n#endif  /* _MT || _MD || _DLL */\n\n/* Error Codes */\n\n#define EPERM           1\n#define ENOENT          2\n#define ESRCH           3\n#define EINTR           4\n#define EIO             5\n#define ENXIO           6\n#define E2BIG           7\n#define ENOEXEC         8\n#define EBADF           9\n#define ECHILD          10\n#define EAGAIN          11\n#define ENOMEM          12\n#define EACCES          13\n#define EFAULT          14\n#define EBUSY           16\n#define EEXIST          17\n#define EXDEV           18\n#define ENODEV          19\n#define ENOTDIR         20\n#define EISDIR          21\n#define EINVAL          22\n#define ENFILE          23\n#define EMFILE          24\n#define ENOTTY          25\n#define EFBIG           27\n#define ENOSPC          28\n#define ESPIPE          29\n#define EROFS           30\n#define EMLINK          31\n#define EPIPE           32\n#define EDOM            33\n#define ERANGE          34\n#define EDEADLK         36\n\n/* defined differently in winsock.h on WinCE */\n#if !defined(ENAMETOOLONG)\n#define ENAMETOOLONG    38\n#endif\n\n#define ENOLCK          39\n#define ENOSYS          40\n\n/* defined differently in winsock.h on WinCE */\n#if !defined(ENOTEMPTY)\n#define ENOTEMPTY       41\n#endif\n\n#define EILSEQ          42\n\n/* POSIX 2008 - robust mutexes */\n#define EOWNERDEAD\t43\n#define ENOTRECOVERABLE\t44\n\n/*\n * Support EDEADLOCK for compatibiity with older MS-C versions.\n */\n#define EDEADLOCK       EDEADLK\n\n#if defined(__cplusplus)\n}\n#endif\n\n#endif  /* _INC_ERRNO */\n"
  },
  {
    "path": "deps/w32-pthreads/nonportable.c",
    "content": "/*\n * nonportable.c\n *\n * Description:\n * This translation unit implements non-portable thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n#include \"pthread_mutexattr_setkind_np.c\"\n#include \"pthread_mutexattr_getkind_np.c\"\n#include \"pthread_getw32threadhandle_np.c\"\n#include \"pthread_getunique_np.c\"\n#include \"pthread_delay_np.c\"\n#include \"pthread_num_processors_np.c\"\n#include \"pthread_win32_attach_detach_np.c\"\n#include \"pthread_timechange_handler_np.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/private.c",
    "content": "/*\n * private.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n#include \"ptw32_MCS_lock.c\"\n#include \"ptw32_is_attr.c\"\n#include \"ptw32_processInitialize.c\"\n#include \"ptw32_processTerminate.c\"\n#include \"ptw32_threadStart.c\"\n#include \"ptw32_threadDestroy.c\"\n#include \"ptw32_tkAssocCreate.c\"\n#include \"ptw32_tkAssocDestroy.c\"\n#include \"ptw32_callUserDestroyRoutines.c\"\n#include \"ptw32_semwait.c\"\n#include \"ptw32_timespec.c\"\n#include \"ptw32_relmillisecs.c\"\n#include \"ptw32_throw.c\"\n#include \"ptw32_getprocessors.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/pthread.c",
    "content": "/*\n * pthread.c\n *\n * Description:\n * This translation unit agregates pthreads-win32 translation units.\n * It is used for inline optimisation of the library,\n * maximising for speed at the expense of size.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/* The following are ordered for inlining */\n\n#include \"private.c\"\n#include \"attr.c\"\n#include \"barrier.c\"\n#include \"cancel.c\"\n#include \"cleanup.c\"\n#include \"condvar.c\"\n#include \"create.c\"\n#include \"dll.c\"\n#include \"autostatic.c\"\n#include \"errno.c\"\n#include \"exit.c\"\n#include \"fork.c\"\n#include \"global.c\"\n#include \"misc.c\"\n#include \"mutex.c\"\n#include \"nonportable.c\"\n#include \"rwlock.c\"\n#include \"sched.c\"\n#include \"semaphore.c\"\n#include \"signal.c\"\n#include \"spin.c\"\n#include \"sync.c\"\n#include \"tsd.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/pthread.dsp",
    "content": "# Microsoft Developer Studio Project File - Name=\"pthread\" - Package Owner=<4>\n# Microsoft Developer Studio Generated Build File, Format Version 6.00\n# ** DO NOT EDIT **\n\n# TARGTYPE \"Win32 (x86) Dynamic-Link Library\" 0x0102\n\nCFG=pthread - Win32 Debug\n!MESSAGE This is not a valid makefile. To build this project using NMAKE,\n!MESSAGE use the Export Makefile command and run\n!MESSAGE \n!MESSAGE NMAKE /f \"pthread.mak\".\n!MESSAGE \n!MESSAGE You can specify a configuration when running NMAKE\n!MESSAGE by defining the macro CFG on the command line. For example:\n!MESSAGE \n!MESSAGE NMAKE /f \"pthread.mak\" CFG=\"pthread - Win32 Debug\"\n!MESSAGE \n!MESSAGE Possible choices for configuration are:\n!MESSAGE \n!MESSAGE \"pthread - Win32 Release\" (based on \"Win32 (x86) Dynamic-Link Library\")\n!MESSAGE \"pthread - Win32 Debug\" (based on \"Win32 (x86) Dynamic-Link Library\")\n!MESSAGE \n\n# Begin Project\n# PROP AllowPerConfigDependencies 0\n# PROP Scc_ProjName \"\"\n# PROP Scc_LocalPath \"\"\nCPP=cl.exe\nMTL=midl.exe\nRSC=rc.exe\n\n!IF  \"$(CFG)\" == \"pthread - Win32 Release\"\n\n# PROP BASE Use_MFC 0\n# PROP BASE Use_Debug_Libraries 0\n# PROP BASE Output_Dir \"Release\"\n# PROP BASE Intermediate_Dir \"Release\"\n# PROP BASE Target_Dir \"\"\n# PROP Use_MFC 0\n# PROP Use_Debug_Libraries 0\n# PROP Output_Dir \".\"\n# PROP Intermediate_Dir \".\"\n# PROP Ignore_Export_Lib 0\n# PROP Target_Dir \"\"\n# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /D \"_USRDLL\" /D \"PTW32_BUILD\" /YX /FD /c\n# ADD CPP /nologo /MD /W3 /GX /O2 /I \".\" /D \"__CLEANUP_C\" /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /D \"_USRDLL\" /D \"PTW32_BUILD\" /YX /FD /c\n# SUBTRACT CPP /u\n# ADD BASE MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\n# ADD MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\n# ADD BASE RSC /l 0x809 /d \"NDEBUG\"\n# ADD RSC /l 0x409 /i \".\" /d \"NDEBUG\" /d \"PTW32_RC_MSC\"\nBSC32=bscmake.exe\n# ADD BASE BSC32 /nologo\n# ADD BSC32 /nologo\nLINK32=link.exe\n# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386\n# ADD LINK32 kernel32.lib user32.lib ws2_32.lib /nologo /dll /pdb:none /machine:I386 /out:\".\\pthreadVC2.dll\"\n\n!ELSEIF  \"$(CFG)\" == \"pthread - Win32 Debug\"\n\n# PROP BASE Use_MFC 0\n# PROP BASE Use_Debug_Libraries 1\n# PROP BASE Output_Dir \"Debug\"\n# PROP BASE Intermediate_Dir \"Debug\"\n# PROP BASE Target_Dir \"\"\n# PROP Use_MFC 0\n# PROP Use_Debug_Libraries 1\n# PROP Output_Dir \".\"\n# PROP Intermediate_Dir \".\"\n# PROP Ignore_Export_Lib 0\n# PROP Target_Dir \"\"\n# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /D \"_USRDLL\" /D \"PTW32_BUILD\" /YX /FD /GZ /c\n# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I \".\" /D \"__CLEANUP_C\" /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /D \"_USRDLL\" /D \"PTW32_BUILD\" /YX /FD /GZ /c\n# ADD BASE MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\n# ADD MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\n# ADD BASE RSC /l 0x809 /d \"_DEBUG\"\n# ADD RSC /l 0x409 /i \".\" /d \"_DEBUG\" /d \"PTW32_RC_MSC\"\nBSC32=bscmake.exe\n# ADD BASE BSC32 /nologo\n# ADD BSC32 /nologo\nLINK32=link.exe\n# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept\n# ADD LINK32 kernel32.lib user32.lib ws2_32.lib /nologo /dll /map /debug /machine:I386 /out:\".\\pthreadVC2.dll\" /pdbtype:sept\n# SUBTRACT LINK32 /pdb:none\n\n!ENDIF \n\n# Begin Target\n\n# Name \"pthread - Win32 Release\"\n# Name \"pthread - Win32 Debug\"\n# Begin Group \"Source Files\"\n\n# PROP Default_Filter \"cpp;c;cxx;rc;def;r;odl;idl;hpj;bat\"\n# Begin Source File\n\nSOURCE=.\\pthread.c\n# End Source File\n# End Group\n# Begin Group \"Header Files\"\n\n# PROP Default_Filter \"h;hpp;hxx;hm;inl\"\n# Begin Source File\n\nSOURCE=.\\implement.h\n# End Source File\n# Begin Source File\n\nSOURCE=.\\pthread.h\n# End Source File\n# Begin Source File\n\nSOURCE=.\\sched.h\n# End Source File\n# Begin Source File\n\nSOURCE=.\\semaphore.h\n# End Source File\n# End Group\n# Begin Group \"Resource Files\"\n\n# PROP Default_Filter \"ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe\"\n# Begin Source File\n\nSOURCE=.\\version.rc\n\n!IF  \"$(CFG)\" == \"pthread - Win32 Release\"\n\n# ADD BASE RSC /l 0xc09\n# ADD RSC /l 0x409 /i \".\" /d \"PTW32_RC_MSC\"\n\n!ELSEIF  \"$(CFG)\" == \"pthread - Win32 Debug\"\n\n# ADD BASE RSC /l 0xc09\n# ADD RSC /l 0x409 /i \".\" /d \"PTW32_RC_MSC\"\n\n!ENDIF \n\n# End Source File\n# End Group\n# End Target\n# End Project\n"
  },
  {
    "path": "deps/w32-pthreads/pthread.dsw",
    "content": "Microsoft Developer Studio Workspace File, Format Version 6.00\n# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\n\n###############################################################################\n\nProject: \"pthread\"=.\\pthread.dsp - Package Owner=<4>\n\nPackage=<5>\n{{{\n}}}\n\nPackage=<4>\n{{{\n}}}\n\n###############################################################################\n\nGlobal:\n\nPackage=<5>\n{{{\n}}}\n\nPackage=<3>\n{{{\n}}}\n\n###############################################################################\n\n"
  },
  {
    "path": "deps/w32-pthreads/pthread.h",
    "content": "/* This is an implementation of the threads API of POSIX 1003.1-2001.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#if !defined( PTHREAD_H )\n#define PTHREAD_H\n\n/*\n * See the README file for an explanation of the pthreads-win32 version\n * numbering scheme and how the DLL is named etc.\n */\n#define PTW32_VERSION 2,9,1,0\n#define PTW32_VERSION_STRING \"2, 9, 1, 0\\0\"\n\n/* There are three implementations of cancel cleanup.\n * Note that pthread.h is included in both application\n * compilation units and also internally for the library.\n * The code here and within the library aims to work\n * for all reasonable combinations of environments.\n *\n * The three implementations are:\n *\n *   WIN32 SEH\n *   C\n *   C++\n *\n * Please note that exiting a push/pop block via\n * \"return\", \"exit\", \"break\", or \"continue\" will\n * lead to different behaviour amongst applications\n * depending upon whether the library was built\n * using SEH, C++, or C. For example, a library built\n * with SEH will call the cleanup routine, while both\n * C++ and C built versions will not.\n */\n\n/*\n * Define defaults for cleanup code.\n * Note: Unless the build explicitly defines one of the following, then\n * we default to standard C style cleanup. This style uses setjmp/longjmp\n * in the cancelation and thread exit implementations and therefore won't\n * do stack unwinding if linked to applications that have it (e.g.\n * C++ apps). This is currently consistent with most/all commercial Unix\n * POSIX threads implementations.\n */\n#if !defined( __CLEANUP_SEH ) && !defined( __CLEANUP_CXX ) && !defined( __CLEANUP_C )\n# define __CLEANUP_C\n#endif\n\n#if defined( __CLEANUP_SEH ) && ( !defined( _MSC_VER ) && !defined(PTW32_RC_MSC))\n#error ERROR [__FILE__, line __LINE__]: SEH is not supported for this compiler.\n#endif\n\n/*\n * Stop here if we are being included by the resource compiler.\n */\n#if !defined(RC_INVOKED)\n\n#undef PTW32_LEVEL\n\n#if defined(_POSIX_SOURCE)\n#define PTW32_LEVEL 0\n/* Early POSIX */\n#endif\n\n#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309\n#undef PTW32_LEVEL\n#define PTW32_LEVEL 1\n/* Include 1b, 1c and 1d */\n#endif\n\n#if defined(INCLUDE_NP)\n#undef PTW32_LEVEL\n#define PTW32_LEVEL 2\n/* Include Non-Portable extensions */\n#endif\n\n#define PTW32_LEVEL_MAX 3\n\n#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 )  || !defined(PTW32_LEVEL)\n#define PTW32_LEVEL PTW32_LEVEL_MAX\n/* Include everything */\n#endif\n\n#if defined(_UWIN)\n#   define HAVE_STRUCT_TIMESPEC 1\n#   define HAVE_SIGNAL_H        1\n#   undef HAVE_PTW32_CONFIG_H\n#   pragma comment(lib, \"pthread\")\n#endif\n\n/*\n * -------------------------------------------------------------\n *\n *\n * Module: pthread.h\n *\n * Purpose:\n *      Provides an implementation of PThreads based upon the\n *      standard:\n *\n *              POSIX 1003.1-2001\n *  and\n *    The Single Unix Specification version 3\n *\n *    (these two are equivalent)\n *\n *      in order to enhance code portability between Windows,\n *  various commercial Unix implementations, and Linux.\n *\n *      See the ANNOUNCE file for a full list of conforming\n *      routines and defined constants, and a list of missing\n *      routines and constants not defined in this implementation.\n *\n * Authors:\n *      There have been many contributors to this library.\n *      The initial implementation was contributed by\n *      John Bossom, and several others have provided major\n *      sections or revisions of parts of the implementation.\n *      Often significant effort has been contributed to\n *      find and fix important bugs and other problems to\n *      improve the reliability of the library, which sometimes\n *      is not reflected in the amount of code which changed as\n *      result.\n *      As much as possible, the contributors are acknowledged\n *      in the ChangeLog file in the source code distribution\n *      where their changes are noted in detail.\n *\n *      Contributors are listed in the CONTRIBUTORS file.\n *\n *      As usual, all bouquets go to the contributors, and all\n *      brickbats go to the project maintainer.\n *\n * Maintainer:\n *      The code base for this project is coordinated and\n *      eventually pre-tested, packaged, and made available by\n *\n *              Ross Johnson <rpj@callisto.canberra.edu.au>\n *\n * QA Testers:\n *      Ultimately, the library is tested in the real world by\n *      a host of competent and demanding scientists and\n *      engineers who report bugs and/or provide solutions\n *      which are then fixed or incorporated into subsequent\n *      versions of the library. Each time a bug is fixed, a\n *      test case is written to prove the fix and ensure\n *      that later changes to the code don't reintroduce the\n *      same error. The number of test cases is slowly growing\n *      and therefore so is the code reliability.\n *\n * Compliance:\n *      See the file ANNOUNCE for the list of implemented\n *      and not-implemented routines and defined options.\n *      Of course, these are all defined is this file as well.\n *\n * Web site:\n *      The source code and other information about this library\n *      are available from\n *\n *              http://sources.redhat.com/pthreads-win32/\n *\n * -------------------------------------------------------------\n */\n\n/* Try to avoid including windows.h */\n#if (defined(__MINGW64__) || defined(__MINGW32__)) && defined(__cplusplus)\n#define PTW32_INCLUDE_WINDOWS_H\n#endif\n\n#if defined(PTW32_INCLUDE_WINDOWS_H)\n#include <windows.h>\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER < 1300 || defined(__DMC__)\n/*\n * VC++6.0 or early compiler's header has no DWORD_PTR type.\n */\ntypedef unsigned long DWORD_PTR;\ntypedef unsigned long ULONG_PTR;\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER >= 1900\n#define HAVE_STRUCT_TIMESPEC\n#endif\n/*\n * -----------------\n * autoconf switches\n * -----------------\n */\n\n#if defined(HAVE_PTW32_CONFIG_H)\n#include \"config.h\"\n#endif /* HAVE_PTW32_CONFIG_H */\n\n#if !defined(NEED_FTIME)\n#include <time.h>\n#else /* NEED_FTIME */\n/* use native WIN32 time API */\n#endif /* NEED_FTIME */\n\n#if defined(HAVE_SIGNAL_H)\n#include <signal.h>\n#endif /* HAVE_SIGNAL_H */\n\n#include <limits.h>\n\n/*\n * Boolean values to make us independent of system includes.\n */\nenum {\n  PTW32_FALSE = 0,\n  PTW32_TRUE = (! PTW32_FALSE)\n};\n\n/*\n * This is a duplicate of what is in the autoconf config.h,\n * which is only used when building the pthread-win32 libraries.\n */\n\n#if !defined(PTW32_CONFIG_H)\n#  if defined(WINCE)\n#    define NEED_ERRNO\n#    define NEED_SEM\n#  endif\n#  if defined(__MINGW64__)\n#    define HAVE_STRUCT_TIMESPEC\n#    define HAVE_MODE_T\n#  elif defined(_UWIN) || defined(__MINGW32__)\n#    define HAVE_MODE_T\n#  endif\n#endif\n\n/*\n *\n */\n\n#if PTW32_LEVEL >= PTW32_LEVEL_MAX\n#if defined(NEED_ERRNO)\n#include \"need_errno.h\"\n#else\n#include <errno.h>\n#endif\n#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\n\n/*\n * Several systems don't define some error numbers.\n */\n#if !defined(ENOTSUP)\n#  define ENOTSUP 48   /* This is the value in Solaris. */\n#endif\n\n#if !defined(ETIMEDOUT)\n#  define ETIMEDOUT 10060 /* Same as WSAETIMEDOUT */\n#endif\n\n#if !defined(ENOSYS)\n#  define ENOSYS 140     /* Semi-arbitrary value */\n#endif\n\n#if !defined(EDEADLK)\n#  if defined(EDEADLOCK)\n#    define EDEADLK EDEADLOCK\n#  else\n#    define EDEADLK 36     /* This is the value in MSVC. */\n#  endif\n#endif\n\n/* POSIX 2008 - related to robust mutexes */\n#if !defined(EOWNERDEAD)\n#  define EOWNERDEAD 43\n#endif\n#if !defined(ENOTRECOVERABLE)\n#  define ENOTRECOVERABLE 44\n#endif\n\n#include \"sched.h\"\n\n/*\n * To avoid including windows.h we define only those things that we\n * actually need from it.\n */\n#if !defined(PTW32_INCLUDE_WINDOWS_H)\n#if !defined(HANDLE)\n# define PTW32__HANDLE_DEF\n# define HANDLE void *\n#endif\n#if !defined(DWORD)\n# define PTW32__DWORD_DEF\n# define DWORD unsigned long\n#endif\n#endif\n\n#if !defined(HAVE_STRUCT_TIMESPEC)\n#define HAVE_STRUCT_TIMESPEC\n#if !defined(_TIMESPEC_DEFINED)\n#define _TIMESPEC_DEFINED\nstruct timespec {\n        time_t tv_sec;\n        long tv_nsec;\n};\n#endif /* _TIMESPEC_DEFINED */\n#endif /* HAVE_STRUCT_TIMESPEC */\n\n#if !defined(SIG_BLOCK)\n#define SIG_BLOCK 0\n#endif /* SIG_BLOCK */\n\n#if !defined(SIG_UNBLOCK)\n#define SIG_UNBLOCK 1\n#endif /* SIG_UNBLOCK */\n\n#if !defined(SIG_SETMASK)\n#define SIG_SETMASK 2\n#endif /* SIG_SETMASK */\n\n#if defined(__cplusplus)\nextern \"C\"\n{\n#endif                          /* __cplusplus */\n\n/*\n * -------------------------------------------------------------\n *\n * POSIX 1003.1-2001 Options\n * =========================\n *\n * Options are normally set in <unistd.h>, which is not provided\n * with pthreads-win32.\n *\n * For conformance with the Single Unix Specification (version 3), all of the\n * options below are defined, and have a value of either -1 (not supported)\n * or 200112L (supported).\n *\n * These options can neither be left undefined nor have a value of 0, because\n * either indicates that sysconf(), which is not implemented, may be used at\n * runtime to check the status of the option.\n *\n * _POSIX_THREADS (== 200112L)\n *                      If == 200112L, you can use threads\n *\n * _POSIX_THREAD_ATTR_STACKSIZE (== 200112L)\n *                      If == 200112L, you can control the size of a thread's\n *                      stack\n *                              pthread_attr_getstacksize\n *                              pthread_attr_setstacksize\n *\n * _POSIX_THREAD_ATTR_STACKADDR (== -1)\n *                      If == 200112L, you can allocate and control a thread's\n *                      stack. If not supported, the following functions\n *                      will return ENOSYS, indicating they are not\n *                      supported:\n *                              pthread_attr_getstackaddr\n *                              pthread_attr_setstackaddr\n *\n * _POSIX_THREAD_PRIORITY_SCHEDULING (== -1)\n *                      If == 200112L, you can use realtime scheduling.\n *                      This option indicates that the behaviour of some\n *                      implemented functions conforms to the additional TPS\n *                      requirements in the standard. E.g. rwlocks favour\n *                      writers over readers when threads have equal priority.\n *\n * _POSIX_THREAD_PRIO_INHERIT (== -1)\n *                      If == 200112L, you can create priority inheritance\n *                      mutexes.\n *                              pthread_mutexattr_getprotocol +\n *                              pthread_mutexattr_setprotocol +\n *\n * _POSIX_THREAD_PRIO_PROTECT (== -1)\n *                      If == 200112L, you can create priority ceiling mutexes\n *                      Indicates the availability of:\n *                              pthread_mutex_getprioceiling\n *                              pthread_mutex_setprioceiling\n *                              pthread_mutexattr_getprioceiling\n *                              pthread_mutexattr_getprotocol     +\n *                              pthread_mutexattr_setprioceiling\n *                              pthread_mutexattr_setprotocol     +\n *\n * _POSIX_THREAD_PROCESS_SHARED (== -1)\n *                      If set, you can create mutexes and condition\n *                      variables that can be shared with another\n *                      process.If set, indicates the availability\n *                      of:\n *                              pthread_mutexattr_getpshared\n *                              pthread_mutexattr_setpshared\n *                              pthread_condattr_getpshared\n *                              pthread_condattr_setpshared\n *\n * _POSIX_THREAD_SAFE_FUNCTIONS (== 200112L)\n *                      If == 200112L you can use the special *_r library\n *                      functions that provide thread-safe behaviour\n *\n * _POSIX_READER_WRITER_LOCKS (== 200112L)\n *                      If == 200112L, you can use read/write locks\n *\n * _POSIX_SPIN_LOCKS (== 200112L)\n *                      If == 200112L, you can use spin locks\n *\n * _POSIX_BARRIERS (== 200112L)\n *                      If == 200112L, you can use barriers\n *\n *      + These functions provide both 'inherit' and/or\n *        'protect' protocol, based upon these macro\n *        settings.\n *\n * -------------------------------------------------------------\n */\n\n/*\n * POSIX Options\n */\n#undef _POSIX_THREADS\n#define _POSIX_THREADS 200809L\n\n#undef _POSIX_READER_WRITER_LOCKS\n#define _POSIX_READER_WRITER_LOCKS 200809L\n\n#undef _POSIX_SPIN_LOCKS\n#define _POSIX_SPIN_LOCKS 200809L\n\n#undef _POSIX_BARRIERS\n#define _POSIX_BARRIERS 200809L\n\n#undef _POSIX_THREAD_SAFE_FUNCTIONS\n#define _POSIX_THREAD_SAFE_FUNCTIONS 200809L\n\n#undef _POSIX_THREAD_ATTR_STACKSIZE\n#define _POSIX_THREAD_ATTR_STACKSIZE 200809L\n\n/*\n * The following options are not supported\n */\n#undef _POSIX_THREAD_ATTR_STACKADDR\n#define _POSIX_THREAD_ATTR_STACKADDR -1\n\n#undef _POSIX_THREAD_PRIO_INHERIT\n#define _POSIX_THREAD_PRIO_INHERIT -1\n\n#undef _POSIX_THREAD_PRIO_PROTECT\n#define _POSIX_THREAD_PRIO_PROTECT -1\n\n/* TPS is not fully supported.  */\n#undef _POSIX_THREAD_PRIORITY_SCHEDULING\n#define _POSIX_THREAD_PRIORITY_SCHEDULING -1\n\n#undef _POSIX_THREAD_PROCESS_SHARED\n#define _POSIX_THREAD_PROCESS_SHARED -1\n\n\n/*\n * POSIX 1003.1-2001 Limits\n * ===========================\n *\n * These limits are normally set in <limits.h>, which is not provided with\n * pthreads-win32.\n *\n * PTHREAD_DESTRUCTOR_ITERATIONS\n *                      Maximum number of attempts to destroy\n *                      a thread's thread-specific data on\n *                      termination (must be at least 4)\n *\n * PTHREAD_KEYS_MAX\n *                      Maximum number of thread-specific data keys\n *                      available per process (must be at least 128)\n *\n * PTHREAD_STACK_MIN\n *                      Minimum supported stack size for a thread\n *\n * PTHREAD_THREADS_MAX\n *                      Maximum number of threads supported per\n *                      process (must be at least 64).\n *\n * SEM_NSEMS_MAX\n *                      The maximum number of semaphores a process can have.\n *                      (must be at least 256)\n *\n * SEM_VALUE_MAX\n *                      The maximum value a semaphore can have.\n *                      (must be at least 32767)\n *\n */\n#undef _POSIX_THREAD_DESTRUCTOR_ITERATIONS\n#define _POSIX_THREAD_DESTRUCTOR_ITERATIONS     4\n\n#undef PTHREAD_DESTRUCTOR_ITERATIONS\n#define PTHREAD_DESTRUCTOR_ITERATIONS           _POSIX_THREAD_DESTRUCTOR_ITERATIONS\n\n#undef _POSIX_THREAD_KEYS_MAX\n#define _POSIX_THREAD_KEYS_MAX                  128\n\n#undef PTHREAD_KEYS_MAX\n#define PTHREAD_KEYS_MAX                        _POSIX_THREAD_KEYS_MAX\n\n#undef PTHREAD_STACK_MIN\n#define PTHREAD_STACK_MIN                       0\n\n#undef _POSIX_THREAD_THREADS_MAX\n#define _POSIX_THREAD_THREADS_MAX               64\n\n  /* Arbitrary value */\n#undef PTHREAD_THREADS_MAX\n#define PTHREAD_THREADS_MAX                     2019\n\n#undef _POSIX_SEM_NSEMS_MAX\n#define _POSIX_SEM_NSEMS_MAX                    256\n\n  /* Arbitrary value */\n#undef SEM_NSEMS_MAX\n#define SEM_NSEMS_MAX                           1024\n\n#undef _POSIX_SEM_VALUE_MAX\n#define _POSIX_SEM_VALUE_MAX                    32767\n\n#undef SEM_VALUE_MAX\n#define SEM_VALUE_MAX                           INT_MAX\n\n\n#if defined(__GNUC__) && !defined(__declspec)\n# error Please upgrade your GNU compiler to one that supports __declspec.\n#endif\n\n/*\n * When building the library, you should define PTW32_BUILD so that\n * the variables/functions are exported correctly. When using the library,\n * do NOT define PTW32_BUILD, and then the variables/functions will\n * be imported correctly.\n */\n#if !defined(PTW32_STATIC_LIB)\n#  if defined(PTW32_BUILD)\n#    define PTW32_DLLPORT __declspec (dllexport)\n#  else\n#    define PTW32_DLLPORT\n#  endif\n#else\n#  define PTW32_DLLPORT\n#endif\n\n/*\n * The Open Watcom C/C++ compiler uses a non-standard calling convention\n * that passes function args in registers unless __cdecl is explicitly specified\n * in exposed function prototypes.\n *\n * We force all calls to cdecl even though this could slow Watcom code down\n * slightly. If you know that the Watcom compiler will be used to build both\n * the DLL and application, then you can probably define this as a null string.\n * Remember that pthread.h (this file) is used for both the DLL and application builds.\n */\n#define PTW32_CDECL __cdecl\n\n#if defined(_UWIN) && PTW32_LEVEL >= PTW32_LEVEL_MAX\n#   include     <sys/types.h>\n#else\n/*\n * Generic handle type - intended to extend uniqueness beyond\n * that available with a simple pointer. It should scale for either\n * IA-32 or IA-64.\n */\ntypedef struct {\n    void * p;                   /* Pointer to actual object */\n    unsigned int x;             /* Extra information - reuse count etc */\n} ptw32_handle_t;\n\ntypedef ptw32_handle_t pthread_t;\ntypedef struct pthread_attr_t_ * pthread_attr_t;\ntypedef struct pthread_once_t_ pthread_once_t;\ntypedef struct pthread_key_t_ * pthread_key_t;\ntypedef struct pthread_mutex_t_ * pthread_mutex_t;\ntypedef struct pthread_mutexattr_t_ * pthread_mutexattr_t;\ntypedef struct pthread_cond_t_ * pthread_cond_t;\ntypedef struct pthread_condattr_t_ * pthread_condattr_t;\n#endif\ntypedef struct pthread_rwlock_t_ * pthread_rwlock_t;\ntypedef struct pthread_rwlockattr_t_ * pthread_rwlockattr_t;\ntypedef struct pthread_spinlock_t_ * pthread_spinlock_t;\ntypedef struct pthread_barrier_t_ * pthread_barrier_t;\ntypedef struct pthread_barrierattr_t_ * pthread_barrierattr_t;\n\n/*\n * ====================\n * ====================\n * POSIX Threads\n * ====================\n * ====================\n */\n\nenum {\n/*\n * pthread_attr_{get,set}detachstate\n */\n  PTHREAD_CREATE_JOINABLE       = 0,  /* Default */\n  PTHREAD_CREATE_DETACHED       = 1,\n\n/*\n * pthread_attr_{get,set}inheritsched\n */\n  PTHREAD_INHERIT_SCHED         = 0,\n  PTHREAD_EXPLICIT_SCHED        = 1,  /* Default */\n\n/*\n * pthread_{get,set}scope\n */\n  PTHREAD_SCOPE_PROCESS         = 0,\n  PTHREAD_SCOPE_SYSTEM          = 1,  /* Default */\n\n/*\n * pthread_setcancelstate paramters\n */\n  PTHREAD_CANCEL_ENABLE         = 0,  /* Default */\n  PTHREAD_CANCEL_DISABLE        = 1,\n\n/*\n * pthread_setcanceltype parameters\n */\n  PTHREAD_CANCEL_ASYNCHRONOUS   = 0,\n  PTHREAD_CANCEL_DEFERRED       = 1,  /* Default */\n\n/*\n * pthread_mutexattr_{get,set}pshared\n * pthread_condattr_{get,set}pshared\n */\n  PTHREAD_PROCESS_PRIVATE       = 0,\n  PTHREAD_PROCESS_SHARED        = 1,\n\n/*\n * pthread_mutexattr_{get,set}robust\n */\n  PTHREAD_MUTEX_STALLED         = 0,  /* Default */\n  PTHREAD_MUTEX_ROBUST          = 1,\n\n/*\n * pthread_barrier_wait\n */\n  PTHREAD_BARRIER_SERIAL_THREAD = -1\n};\n\n/*\n * ====================\n * ====================\n * Cancelation\n * ====================\n * ====================\n */\n#define PTHREAD_CANCELED       ((void *)(size_t) -1)\n\n\n/*\n * ====================\n * ====================\n * Once Key\n * ====================\n * ====================\n */\n#define PTHREAD_ONCE_INIT       { PTW32_FALSE, 0, 0, 0}\n\nstruct pthread_once_t_\n{\n  int          done;        /* indicates if user function has been executed */\n  void *       lock;\n  int          reserved1;\n  int          reserved2;\n};\n\n\n/*\n * ====================\n * ====================\n * Object initialisers\n * ====================\n * ====================\n */\n#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -1)\n#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -2)\n#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER ((pthread_mutex_t)(size_t) -3)\n\n/*\n * Compatibility with LinuxThreads\n */\n#define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP PTHREAD_RECURSIVE_MUTEX_INITIALIZER\n#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP PTHREAD_ERRORCHECK_MUTEX_INITIALIZER\n\n#define PTHREAD_COND_INITIALIZER ((pthread_cond_t)(size_t) -1)\n\n#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t)(size_t) -1)\n\n#define PTHREAD_SPINLOCK_INITIALIZER ((pthread_spinlock_t)(size_t) -1)\n\n\n/*\n * Mutex types.\n */\nenum\n{\n  /* Compatibility with LinuxThreads */\n  PTHREAD_MUTEX_FAST_NP,\n  PTHREAD_MUTEX_RECURSIVE_NP,\n  PTHREAD_MUTEX_ERRORCHECK_NP,\n  PTHREAD_MUTEX_TIMED_NP = PTHREAD_MUTEX_FAST_NP,\n  PTHREAD_MUTEX_ADAPTIVE_NP = PTHREAD_MUTEX_FAST_NP,\n  /* For compatibility with POSIX */\n  PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_FAST_NP,\n  PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,\n  PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,\n  PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL\n};\n\n\ntypedef struct ptw32_cleanup_t ptw32_cleanup_t;\n\n#if defined(_MSC_VER)\n/* Disable MSVC 'anachronism used' warning */\n#pragma warning( disable : 4229 )\n#endif\n\ntypedef void (* PTW32_CDECL ptw32_cleanup_callback_t)(void *);\n\n#if defined(_MSC_VER)\n#pragma warning( default : 4229 )\n#endif\n\nstruct ptw32_cleanup_t\n{\n  ptw32_cleanup_callback_t routine;\n  void *arg;\n  struct ptw32_cleanup_t *prev;\n};\n\n#if defined(__CLEANUP_SEH)\n        /*\n         * WIN32 SEH version of cancel cleanup.\n         */\n\n#define pthread_cleanup_push( _rout, _arg ) \\\n        { \\\n            ptw32_cleanup_t     _cleanup; \\\n            \\\n        _cleanup.routine        = (ptw32_cleanup_callback_t)(_rout); \\\n            _cleanup.arg        = (_arg); \\\n            __try \\\n              { \\\n\n#define pthread_cleanup_pop( _execute ) \\\n              } \\\n            __finally \\\n                { \\\n                    if( _execute || AbnormalTermination()) \\\n                      { \\\n                          (*(_cleanup.routine))( _cleanup.arg ); \\\n                      } \\\n                } \\\n        }\n\n#else /* __CLEANUP_SEH */\n\n#if defined(__CLEANUP_C)\n\n        /*\n         * C implementation of PThreads cancel cleanup\n         */\n\n#define pthread_cleanup_push( _rout, _arg ) \\\n        { \\\n            ptw32_cleanup_t     _cleanup; \\\n            \\\n            ptw32_push_cleanup( &_cleanup, (ptw32_cleanup_callback_t) (_rout), (_arg) ); \\\n\n#define pthread_cleanup_pop( _execute ) \\\n            (void) ptw32_pop_cleanup( _execute ); \\\n        }\n\n#else /* __CLEANUP_C */\n\n#if defined(__CLEANUP_CXX)\n\n        /*\n         * C++ version of cancel cleanup.\n         * - John E. Bossom.\n         */\n\n        class PThreadCleanup {\n          /*\n           * PThreadCleanup\n           *\n           * Purpose\n           *      This class is a C++ helper class that is\n           *      used to implement pthread_cleanup_push/\n           *      pthread_cleanup_pop.\n           *      The destructor of this class automatically\n           *      pops the pushed cleanup routine regardless\n           *      of how the code exits the scope\n           *      (i.e. such as by an exception)\n           */\n      ptw32_cleanup_callback_t cleanUpRout;\n          void    *       obj;\n          int             executeIt;\n\n        public:\n          PThreadCleanup() :\n            cleanUpRout( 0 ),\n            obj( 0 ),\n            executeIt( 0 )\n            /*\n             * No cleanup performed\n             */\n            {\n            }\n\n          PThreadCleanup(\n             ptw32_cleanup_callback_t routine,\n                         void    *       arg ) :\n            cleanUpRout( routine ),\n            obj( arg ),\n            executeIt( 1 )\n            /*\n             * Registers a cleanup routine for 'arg'\n             */\n            {\n            }\n\n          ~PThreadCleanup()\n            {\n              if ( executeIt && ((void *) cleanUpRout != (void *) 0) )\n                {\n                  (void) (*cleanUpRout)( obj );\n                }\n            }\n\n          void execute( int exec )\n            {\n              executeIt = exec;\n            }\n        };\n\n        /*\n         * C++ implementation of PThreads cancel cleanup;\n         * This implementation takes advantage of a helper\n         * class who's destructor automatically calls the\n         * cleanup routine if we exit our scope weirdly\n         */\n#define pthread_cleanup_push( _rout, _arg ) \\\n        { \\\n            PThreadCleanup  cleanup((ptw32_cleanup_callback_t)(_rout), \\\n                                    (void *) (_arg) );\n\n#define pthread_cleanup_pop( _execute ) \\\n            cleanup.execute( _execute ); \\\n        }\n\n#else\n\n#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined.\n\n#endif /* __CLEANUP_CXX */\n\n#endif /* __CLEANUP_C */\n\n#endif /* __CLEANUP_SEH */\n\n/*\n * ===============\n * ===============\n * Methods\n * ===============\n * ===============\n */\n\n/*\n * PThread Attribute Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_init (pthread_attr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_destroy (pthread_attr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_getdetachstate (const pthread_attr_t * attr,\n                                         int *detachstate);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_getstackaddr (const pthread_attr_t * attr,\n                                       void **stackaddr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_getstacksize (const pthread_attr_t * attr,\n                                       size_t * stacksize);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_setdetachstate (pthread_attr_t * attr,\n                                         int detachstate);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_setstackaddr (pthread_attr_t * attr,\n                                       void *stackaddr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_setstacksize (pthread_attr_t * attr,\n                                       size_t stacksize);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedparam (const pthread_attr_t *attr,\n                                        struct sched_param *param);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedparam (pthread_attr_t *attr,\n                                        const struct sched_param *param);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_setschedpolicy (pthread_attr_t *,\n                                         int);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_getschedpolicy (const pthread_attr_t *,\n                                         int *);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_setinheritsched(pthread_attr_t * attr,\n                                         int inheritsched);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_getinheritsched(const pthread_attr_t * attr,\n                                         int * inheritsched);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_setscope (pthread_attr_t *,\n                                   int);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_attr_getscope (const pthread_attr_t *,\n                                   int *);\n\n/*\n * PThread Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_create (pthread_t * tid,\n                            const pthread_attr_t * attr,\n                            void *(PTW32_CDECL *start) (void *),\n                            void *arg);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_detach (pthread_t tid);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_equal (pthread_t t1,\n                           pthread_t t2);\n\nPTW32_DLLPORT void PTW32_CDECL pthread_exit (void *value_ptr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_join (pthread_t thread,\n                          void **value_ptr);\n\nPTW32_DLLPORT pthread_t PTW32_CDECL pthread_self (void);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_cancel (pthread_t thread);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_setcancelstate (int state,\n                                    int *oldstate);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_setcanceltype (int type,\n                                   int *oldtype);\n\nPTW32_DLLPORT void PTW32_CDECL pthread_testcancel (void);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_once (pthread_once_t * once_control,\n                          void (PTW32_CDECL *init_routine) (void));\n\n#if PTW32_LEVEL >= PTW32_LEVEL_MAX\nPTW32_DLLPORT ptw32_cleanup_t * PTW32_CDECL ptw32_pop_cleanup (int execute);\n\nPTW32_DLLPORT void PTW32_CDECL ptw32_push_cleanup (ptw32_cleanup_t * cleanup,\n                                 ptw32_cleanup_callback_t routine,\n                                 void *arg);\n#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\n\n/*\n * Thread Specific Data Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_key_create (pthread_key_t * key,\n                                void (PTW32_CDECL *destructor) (void *));\n\nPTW32_DLLPORT int PTW32_CDECL pthread_key_delete (pthread_key_t key);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_setspecific (pthread_key_t key,\n                                 const void *value);\n\nPTW32_DLLPORT void * PTW32_CDECL pthread_getspecific (pthread_key_t key);\n\n\n/*\n * Mutex Attribute Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_init (pthread_mutexattr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_destroy (pthread_mutexattr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getpshared (const pthread_mutexattr_t\n                                          * attr,\n                                          int *pshared);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setpshared (pthread_mutexattr_t * attr,\n                                          int pshared);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind);\nPTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_gettype (const pthread_mutexattr_t * attr, int *kind);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setrobust(\n                                           pthread_mutexattr_t *attr,\n                                           int robust);\nPTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getrobust(\n                                           const pthread_mutexattr_t * attr,\n                                           int * robust);\n\n/*\n * Barrier Attribute Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_init (pthread_barrierattr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_destroy (pthread_barrierattr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_getpshared (const pthread_barrierattr_t\n                                            * attr,\n                                            int *pshared);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_barrierattr_setpshared (pthread_barrierattr_t * attr,\n                                            int pshared);\n\n/*\n * Mutex Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_mutex_init (pthread_mutex_t * mutex,\n                                const pthread_mutexattr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutex_destroy (pthread_mutex_t * mutex);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutex_lock (pthread_mutex_t * mutex);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutex_timedlock(pthread_mutex_t * mutex,\n                                    const struct timespec *abstime);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutex_trylock (pthread_mutex_t * mutex);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutex_unlock (pthread_mutex_t * mutex);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_mutex_consistent (pthread_mutex_t * mutex);\n\n/*\n * Spinlock Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_spin_init (pthread_spinlock_t * lock, int pshared);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_spin_destroy (pthread_spinlock_t * lock);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_spin_lock (pthread_spinlock_t * lock);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_spin_trylock (pthread_spinlock_t * lock);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_spin_unlock (pthread_spinlock_t * lock);\n\n/*\n * Barrier Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_barrier_init (pthread_barrier_t * barrier,\n                                  const pthread_barrierattr_t * attr,\n                                  unsigned int count);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_barrier_destroy (pthread_barrier_t * barrier);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_barrier_wait (pthread_barrier_t * barrier);\n\n/*\n * Condition Variable Attribute Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_condattr_init (pthread_condattr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_condattr_destroy (pthread_condattr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_condattr_getpshared (const pthread_condattr_t * attr,\n                                         int *pshared);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_condattr_setpshared (pthread_condattr_t * attr,\n                                         int pshared);\n\n/*\n * Condition Variable Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_cond_init (pthread_cond_t * cond,\n                               const pthread_condattr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_cond_destroy (pthread_cond_t * cond);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_cond_wait (pthread_cond_t * cond,\n                               pthread_mutex_t * mutex);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_cond_timedwait (pthread_cond_t * cond,\n                                    pthread_mutex_t * mutex,\n                                    const struct timespec *abstime);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_cond_signal (pthread_cond_t * cond);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_cond_broadcast (pthread_cond_t * cond);\n\n/*\n * Scheduling\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_setschedparam (pthread_t thread,\n                                   int policy,\n                                   const struct sched_param *param);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_getschedparam (pthread_t thread,\n                                   int *policy,\n                                   struct sched_param *param);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_setconcurrency (int);\n \nPTW32_DLLPORT int PTW32_CDECL pthread_getconcurrency (void);\n\n/*\n * Read-Write Lock Functions\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlock_init(pthread_rwlock_t *lock,\n                                const pthread_rwlockattr_t *attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlock_destroy(pthread_rwlock_t *lock);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlock_tryrdlock(pthread_rwlock_t *);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlock_trywrlock(pthread_rwlock_t *);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlock_rdlock(pthread_rwlock_t *lock);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedrdlock(pthread_rwlock_t *lock,\n                                       const struct timespec *abstime);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlock_wrlock(pthread_rwlock_t *lock);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlock_timedwrlock(pthread_rwlock_t *lock,\n                                       const struct timespec *abstime);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlock_unlock(pthread_rwlock_t *lock);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_init (pthread_rwlockattr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_destroy (pthread_rwlockattr_t * attr);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,\n                                           int *pshared);\n\nPTW32_DLLPORT int PTW32_CDECL pthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr,\n                                           int pshared);\n\n#if PTW32_LEVEL >= PTW32_LEVEL_MAX - 1\n\n/*\n * Signal Functions. Should be defined in <signal.h> but MSVC and MinGW32\n * already have signal.h that don't define these.\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_kill(pthread_t thread, int sig);\n\n/*\n * Non-portable functions\n */\n\n/*\n * Compatibility with Linux.\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_setkind_np(pthread_mutexattr_t * attr,\n                                         int kind);\nPTW32_DLLPORT int PTW32_CDECL pthread_mutexattr_getkind_np(pthread_mutexattr_t * attr,\n                                         int *kind);\n\n/*\n * Possibly supported by other POSIX threads implementations\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_delay_np (struct timespec * interval);\nPTW32_DLLPORT int PTW32_CDECL pthread_num_processors_np(void);\nPTW32_DLLPORT unsigned __int64 PTW32_CDECL pthread_getunique_np(pthread_t thread);\n\n/*\n * Useful if an application wants to statically link\n * the lib rather than load the DLL at run-time.\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_win32_process_attach_np(void);\nPTW32_DLLPORT int PTW32_CDECL pthread_win32_process_detach_np(void);\nPTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_attach_np(void);\nPTW32_DLLPORT int PTW32_CDECL pthread_win32_thread_detach_np(void);\n\n/*\n * Features that are auto-detected at load/run time.\n */\nPTW32_DLLPORT int PTW32_CDECL pthread_win32_test_features_np(int);\nenum ptw32_features {\n  PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE = 0x0001, /* System provides it. */\n  PTW32_ALERTABLE_ASYNC_CANCEL              = 0x0002  /* Can cancel blocked threads. */\n};\n\n/*\n * Register a system time change with the library.\n * Causes the library to perform various functions\n * in response to the change. Should be called whenever\n * the application's top level window receives a\n * WM_TIMECHANGE message. It can be passed directly to\n * pthread_create() as a new thread if desired.\n */\nPTW32_DLLPORT void * PTW32_CDECL pthread_timechange_handler_np(void *);\n\n#endif /*PTW32_LEVEL >= PTW32_LEVEL_MAX - 1 */\n\n#if PTW32_LEVEL >= PTW32_LEVEL_MAX\n\n/*\n * Returns the Win32 HANDLE for the POSIX thread.\n */\nPTW32_DLLPORT HANDLE PTW32_CDECL pthread_getw32threadhandle_np(pthread_t thread);\n/*\n * Returns the win32 thread ID for POSIX thread.\n */\nPTW32_DLLPORT DWORD PTW32_CDECL pthread_getw32threadid_np (pthread_t thread);\n\n\n/*\n * Protected Methods\n *\n * This function blocks until the given WIN32 handle\n * is signaled or pthread_cancel had been called.\n * This function allows the caller to hook into the\n * PThreads cancel mechanism. It is implemented using\n *\n *              WaitForMultipleObjects\n *\n * on 'waitHandle' and a manually reset WIN32 Event\n * used to implement pthread_cancel. The 'timeout'\n * argument to TimedWait is simply passed to\n * WaitForMultipleObjects.\n */\nPTW32_DLLPORT int PTW32_CDECL pthreadCancelableWait (HANDLE waitHandle);\nPTW32_DLLPORT int PTW32_CDECL pthreadCancelableTimedWait (HANDLE waitHandle,\n                                        DWORD timeout);\n\n#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\n\n/*\n * Thread-Safe C Runtime Library Mappings.\n */\n#if !defined(_UWIN)\n#  if defined(NEED_ERRNO)\n     PTW32_DLLPORT int * PTW32_CDECL _errno( void );\n#  else\n#    if !defined(errno)\n#      if (defined(_MT) || defined(_DLL))\n         __declspec(dllimport) extern int * __cdecl _errno(void);\n#        define errno   (*_errno())\n#      endif\n#    endif\n#  endif\n#endif\n\n/*\n * Some compiler environments don't define some things.\n */\n#if defined(__BORLANDC__)\n#  define _ftime ftime\n#  define _timeb timeb\n#endif\n\n#if defined(__cplusplus)\n\n/*\n * Internal exceptions\n */\nclass ptw32_exception {};\nclass ptw32_exception_cancel : public ptw32_exception {};\nclass ptw32_exception_exit   : public ptw32_exception {};\n\n#endif\n\n#if PTW32_LEVEL >= PTW32_LEVEL_MAX\n\n/* FIXME: This is only required if the library was built using SEH */\n/*\n * Get internal SEH tag\n */\nPTW32_DLLPORT DWORD PTW32_CDECL ptw32_get_exception_services_code(void);\n\n#endif /* PTW32_LEVEL >= PTW32_LEVEL_MAX */\n\n#if !defined(PTW32_BUILD)\n\n#if defined(__CLEANUP_SEH)\n\n/*\n * Redefine the SEH __except keyword to ensure that applications\n * propagate our internal exceptions up to the library's internal handlers.\n */\n#define __except( E ) \\\n        __except( ( GetExceptionCode() == ptw32_get_exception_services_code() ) \\\n                 ? EXCEPTION_CONTINUE_SEARCH : ( E ) )\n\n#endif /* __CLEANUP_SEH */\n\n#if defined(__CLEANUP_CXX)\n\n/*\n * Redefine the C++ catch keyword to ensure that applications\n * propagate our internal exceptions up to the library's internal handlers.\n */\n#if defined(_MSC_VER)\n        /*\n         * WARNING: Replace any 'catch( ... )' with 'PtW32CatchAll'\n         * if you want Pthread-Win32 cancelation and pthread_exit to work.\n         */\n\n#if !defined(PtW32NoCatchWarn)\n\n#pragma message(\"Specify \\\"/DPtW32NoCatchWarn\\\" compiler flag to skip this message.\")\n#pragma message(\"------------------------------------------------------------------\")\n#pragma message(\"When compiling applications with MSVC++ and C++ exception handling:\")\n#pragma message(\"  Replace any 'catch( ... )' in routines called from POSIX threads\")\n#pragma message(\"  with 'PtW32CatchAll' or 'CATCHALL' if you want POSIX thread\")\n#pragma message(\"  cancelation and pthread_exit to work. For example:\")\n#pragma message(\"\")\n#pragma message(\"    #if defined(PtW32CatchAll)\")\n#pragma message(\"      PtW32CatchAll\")\n#pragma message(\"    #else\")\n#pragma message(\"      catch(...)\")\n#pragma message(\"    #endif\")\n#pragma message(\"        {\")\n#pragma message(\"          /* Catchall block processing */\")\n#pragma message(\"        }\")\n#pragma message(\"------------------------------------------------------------------\")\n\n#endif\n\n#define PtW32CatchAll \\\n        catch( ptw32_exception & ) { throw; } \\\n        catch( ... )\n\n#else /* _MSC_VER */\n\n#define catch( E ) \\\n        catch( ptw32_exception & ) { throw; } \\\n        catch( E )\n\n#endif /* _MSC_VER */\n\n#endif /* __CLEANUP_CXX */\n\n#endif /* ! PTW32_BUILD */\n\n#if defined(__cplusplus)\n}                               /* End of extern \"C\" */\n#endif                          /* __cplusplus */\n\n#if defined(PTW32__HANDLE_DEF)\n# undef HANDLE\n#endif\n#if defined(PTW32__DWORD_DEF)\n# undef DWORD\n#endif\n\n#undef PTW32_LEVEL\n#undef PTW32_LEVEL_MAX\n\n#endif /* ! RC_INVOKED */\n\n#endif /* PTHREAD_H */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread.lastbuildstate",
    "content": "#v4.0:v100:false\nRelease-Static|x64|D:\\OBS2\\liboms-util\\w32-pthreads\\|\n"
  },
  {
    "path": "deps/w32-pthreads/pthread.sln",
    "content": "﻿\r\nMicrosoft Visual Studio Solution File, Format Version 11.00\r\n# Visual Studio 2010\r\nProject(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"pthread\", \"pthread.vcxproj\", \"{969D9298-8CEF-14A3-8EEF-E3C596896C8D}\"\r\nEndProject\r\nGlobal\r\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\r\n\t\tDebug|Win32 = Debug|Win32\r\n\t\tDebug|x64 = Debug|x64\r\n\t\tDebug-Static|Win32 = Debug-Static|Win32\r\n\t\tDebug-Static|x64 = Debug-Static|x64\r\n\t\tRelease|Win32 = Release|Win32\r\n\t\tRelease|x64 = Release|x64\r\n\t\tRelease-Static|Win32 = Release-Static|Win32\r\n\t\tRelease-Static|x64 = Release-Static|x64\r\n\tEndGlobalSection\r\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Debug|Win32.ActiveCfg = Debug|Win32\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Debug|Win32.Build.0 = Debug|Win32\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Debug|x64.ActiveCfg = Debug|x64\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Debug|x64.Build.0 = Debug|x64\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Debug-Static|Win32.ActiveCfg = Debug-Static|Win32\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Debug-Static|Win32.Build.0 = Debug-Static|Win32\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Debug-Static|x64.ActiveCfg = Debug-Static|x64\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Debug-Static|x64.Build.0 = Debug-Static|x64\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Release|Win32.ActiveCfg = Release|Win32\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Release|Win32.Build.0 = Release|Win32\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Release|x64.ActiveCfg = Release|x64\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Release|x64.Build.0 = Release|x64\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Release-Static|Win32.ActiveCfg = Release-Static|Win32\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Release-Static|Win32.Build.0 = Release-Static|Win32\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Release-Static|x64.ActiveCfg = Release-Static|x64\r\n\t\t{969D9298-8CEF-14A3-8EEF-E3C596896C8D}.Release-Static|x64.Build.0 = Release-Static|x64\r\n\tEndGlobalSection\r\n\tGlobalSection(SolutionProperties) = preSolution\r\n\t\tHideSolutionNode = FALSE\r\n\tEndGlobalSection\r\nEndGlobal\r\n"
  },
  {
    "path": "deps/w32-pthreads/pthread.vcxproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project DefaultTargets=\"Build\" ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup Label=\"ProjectConfigurations\">\r\n    <ProjectConfiguration Include=\"Debug-Static|Win32\">\r\n      <Configuration>Debug-Static</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug-Static|x64\">\r\n      <Configuration>Debug-Static</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|Win32\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Debug|x64\">\r\n      <Configuration>Debug</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release-Static|Win32\">\r\n      <Configuration>Release-Static</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release-Static|x64\">\r\n      <Configuration>Release-Static</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|Win32\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>Win32</Platform>\r\n    </ProjectConfiguration>\r\n    <ProjectConfiguration Include=\"Release|x64\">\r\n      <Configuration>Release</Configuration>\r\n      <Platform>x64</Platform>\r\n    </ProjectConfiguration>\r\n  </ItemGroup>\r\n  <PropertyGroup Label=\"Globals\">\r\n    <SccProjectName />\r\n    <SccLocalPath />\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.Default.props\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\r\n    <UseOfMfc>false</UseOfMfc>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-Static|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\r\n    <UseOfMfc>false</UseOfMfc>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\r\n    <UseOfMfc>false</UseOfMfc>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-Static|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseOfMfc>false</UseOfMfc>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\r\n    <UseOfMfc>false</UseOfMfc>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-Static|Win32'\" Label=\"Configuration\">\r\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\r\n    <UseOfMfc>false</UseOfMfc>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>DynamicLibrary</ConfigurationType>\r\n    <UseOfMfc>false</UseOfMfc>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-Static|x64'\" Label=\"Configuration\">\r\n    <ConfigurationType>StaticLibrary</ConfigurationType>\r\n    <UseOfMfc>false</UseOfMfc>\r\n    <CharacterSet>MultiByte</CharacterSet>\r\n  </PropertyGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.props\" />\r\n  <ImportGroup Label=\"ExtensionSettings\">\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n    <Import Project=\"$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-Static|Win32'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n    <Import Project=\"$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n    <Import Project=\"$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-Static|x64'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n    <Import Project=\"$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n    <Import Project=\"$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-Static|Win32'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n    <Import Project=\"$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n    <Import Project=\"$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props\" />\r\n  </ImportGroup>\r\n  <ImportGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-Static|x64'\" Label=\"PropertySheets\">\r\n    <Import Project=\"$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props\" Condition=\"exists('$(UserRootDir)\\Microsoft.Cpp.$(Platform).user.props')\" Label=\"LocalAppDataPlatform\" />\r\n    <Import Project=\"$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props\" />\r\n  </ImportGroup>\r\n  <PropertyGroup Label=\"UserMacros\" />\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <OutDir>.\\.\\</OutDir>\r\n    <IntDir>.\\.\\</IntDir>\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-Static|Win32'\">\r\n    <OutDir>.\\.\\</OutDir>\r\n    <IntDir>.\\.\\</IntDir>\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <OutDir>.\\.\\</OutDir>\r\n    <IntDir>.\\.\\</IntDir>\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-Static|x64'\">\r\n    <OutDir>.\\.\\</OutDir>\r\n    <IntDir>.\\.\\</IntDir>\r\n    <LinkIncremental>false</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <OutDir>.\\.\\</OutDir>\r\n    <IntDir>.\\.\\</IntDir>\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-Static|Win32'\">\r\n    <OutDir>.\\.\\</OutDir>\r\n    <IntDir>.\\.\\</IntDir>\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <OutDir>.\\.\\</OutDir>\r\n    <IntDir>.\\.\\</IntDir>\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-Static|x64'\">\r\n    <OutDir>.\\.\\</OutDir>\r\n    <IntDir>.\\.\\</IntDir>\r\n    <LinkIncremental>true</LinkIncremental>\r\n  </PropertyGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|Win32'\">\r\n    <ClCompile>\r\n      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r\n      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>\r\n      <StringPooling>true</StringPooling>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <UndefineAllPreprocessorDefinitions>false</UndefineAllPreprocessorDefinitions>\r\n      <PreprocessorDefinitions>__CLEANUP_C;WIN32;NDEBUG;_WINDOWS;_USRDLL;PTW32_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AssemblerListingLocation>.\\.\\</AssemblerListingLocation>\r\n      <PrecompiledHeaderOutputFile>.\\.\\pthread.pch</PrecompiledHeaderOutputFile>\r\n      <ObjectFileName>.\\.\\</ObjectFileName>\r\n      <ProgramDataBaseFileName>.\\.\\</ProgramDataBaseFileName>\r\n    </ClCompile>\r\n    <Midl>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <TypeLibraryName>.\\.\\pthread.tlb</TypeLibraryName>\r\n      <MkTypLibCompatible>true</MkTypLibCompatible>\r\n      <TargetEnvironment>Win32</TargetEnvironment>\r\n    </Midl>\r\n    <ResourceCompile>\r\n      <Culture>0x0409</Culture>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>NDEBUG;PTW32_RC_MSC;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Bscmake>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <OutputFile>.\\.\\pthread.bsc</OutputFile>\r\n    </Bscmake>\r\n    <Link>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <LinkDLL>true</LinkDLL>\r\n      <SubSystem>Console</SubSystem>\r\n      <OutputFile>.\\pthreadVC2.dll</OutputFile>\r\n      <ImportLibrary>.\\.\\pthreadVC2.lib</ImportLibrary>\r\n      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-Static|Win32'\">\r\n    <ClCompile>\r\n      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r\n      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>\r\n      <StringPooling>true</StringPooling>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <UndefineAllPreprocessorDefinitions>false</UndefineAllPreprocessorDefinitions>\r\n      <PreprocessorDefinitions>__CLEANUP_C;WIN32;NDEBUG;_WINDOWS;_USRDLL;PTW32_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AssemblerListingLocation>.\\.\\</AssemblerListingLocation>\r\n      <PrecompiledHeaderOutputFile>.\\.\\pthread.pch</PrecompiledHeaderOutputFile>\r\n      <ObjectFileName>.\\.\\</ObjectFileName>\r\n      <ProgramDataBaseFileName>.\\.\\</ProgramDataBaseFileName>\r\n    </ClCompile>\r\n    <Midl>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <TypeLibraryName>.\\.\\pthread.tlb</TypeLibraryName>\r\n      <MkTypLibCompatible>true</MkTypLibCompatible>\r\n      <TargetEnvironment>Win32</TargetEnvironment>\r\n    </Midl>\r\n    <ResourceCompile>\r\n      <Culture>0x0409</Culture>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>NDEBUG;PTW32_RC_MSC;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Bscmake>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <OutputFile>.\\.\\pthread.bsc</OutputFile>\r\n    </Bscmake>\r\n    <Link>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <LinkDLL>true</LinkDLL>\r\n      <SubSystem>Console</SubSystem>\r\n      <OutputFile>.\\pthreadVC2.dll</OutputFile>\r\n      <ImportLibrary>.\\.\\pthreadVC2.lib</ImportLibrary>\r\n      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|x64'\">\r\n    <ClCompile>\r\n      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r\n      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>\r\n      <StringPooling>true</StringPooling>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <UndefineAllPreprocessorDefinitions>false</UndefineAllPreprocessorDefinitions>\r\n      <PreprocessorDefinitions>__CLEANUP_C;WIN32;NDEBUG;_WINDOWS;_USRDLL;PTW32_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AssemblerListingLocation>.\\.\\</AssemblerListingLocation>\r\n      <PrecompiledHeaderOutputFile>.\\.\\pthread.pch</PrecompiledHeaderOutputFile>\r\n      <ObjectFileName>.\\.\\</ObjectFileName>\r\n      <ProgramDataBaseFileName>.\\.\\</ProgramDataBaseFileName>\r\n    </ClCompile>\r\n    <Midl>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <TypeLibraryName>.\\.\\pthread.tlb</TypeLibraryName>\r\n      <MkTypLibCompatible>true</MkTypLibCompatible>\r\n    </Midl>\r\n    <ResourceCompile>\r\n      <Culture>0x0409</Culture>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>NDEBUG;PTW32_RC_MSC;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Bscmake>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <OutputFile>.\\.\\pthread.bsc</OutputFile>\r\n    </Bscmake>\r\n    <Link>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <LinkDLL>true</LinkDLL>\r\n      <SubSystem>Console</SubSystem>\r\n      <OutputFile>.\\pthreadVC2.dll</OutputFile>\r\n      <ImportLibrary>.\\.\\pthreadVC2.lib</ImportLibrary>\r\n      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Release-Static|x64'\">\r\n    <ClCompile>\r\n      <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r\n      <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>\r\n      <StringPooling>true</StringPooling>\r\n      <FunctionLevelLinking>true</FunctionLevelLinking>\r\n      <Optimization>MaxSpeed</Optimization>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <UndefineAllPreprocessorDefinitions>false</UndefineAllPreprocessorDefinitions>\r\n      <PreprocessorDefinitions>__CLEANUP_C;WIN32;NDEBUG;_WINDOWS;_USRDLL;PTW32_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AssemblerListingLocation>.\\.\\</AssemblerListingLocation>\r\n      <PrecompiledHeaderOutputFile>.\\.\\pthread.pch</PrecompiledHeaderOutputFile>\r\n      <ObjectFileName>.\\.\\</ObjectFileName>\r\n      <ProgramDataBaseFileName>.\\.\\</ProgramDataBaseFileName>\r\n    </ClCompile>\r\n    <Midl>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <TypeLibraryName>.\\.\\pthread.tlb</TypeLibraryName>\r\n      <MkTypLibCompatible>true</MkTypLibCompatible>\r\n    </Midl>\r\n    <ResourceCompile>\r\n      <Culture>0x0409</Culture>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>NDEBUG;PTW32_RC_MSC;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Bscmake>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <OutputFile>.\\.\\pthread.bsc</OutputFile>\r\n    </Bscmake>\r\n    <Link>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <LinkDLL>true</LinkDLL>\r\n      <SubSystem>Console</SubSystem>\r\n      <OutputFile>.\\pthreadVC2.dll</OutputFile>\r\n      <ImportLibrary>.\\.\\pthreadVC2.lib</ImportLibrary>\r\n      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n    <Lib>\r\n      <OutputFile>.\\pthreadVC2.lib</OutputFile>\r\n    </Lib>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|Win32'\">\r\n    <ClCompile>\r\n      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n      <InlineFunctionExpansion>Default</InlineFunctionExpansion>\r\n      <FunctionLevelLinking>false</FunctionLevelLinking>\r\n      <Optimization>Disabled</Optimization>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <MinimalRebuild>true</MinimalRebuild>\r\n      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>__CLEANUP_C;WIN32;_DEBUG;_WINDOWS;_USRDLL;PTW32_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AssemblerListingLocation>.\\.\\</AssemblerListingLocation>\r\n      <PrecompiledHeaderOutputFile>.\\.\\pthread.pch</PrecompiledHeaderOutputFile>\r\n      <ObjectFileName>.\\.\\</ObjectFileName>\r\n      <ProgramDataBaseFileName>.\\.\\</ProgramDataBaseFileName>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n    </ClCompile>\r\n    <Midl>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <TypeLibraryName>.\\.\\pthread.tlb</TypeLibraryName>\r\n      <MkTypLibCompatible>true</MkTypLibCompatible>\r\n      <TargetEnvironment>Win32</TargetEnvironment>\r\n    </Midl>\r\n    <ResourceCompile>\r\n      <Culture>0x0409</Culture>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>_DEBUG;PTW32_RC_MSC;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Bscmake>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <OutputFile>.\\.\\pthread.bsc</OutputFile>\r\n    </Bscmake>\r\n    <Link>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <LinkDLL>true</LinkDLL>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <SubSystem>Console</SubSystem>\r\n      <OutputFile>.\\pthreadVC2.dll</OutputFile>\r\n      <ImportLibrary>.\\.\\pthreadVC2.lib</ImportLibrary>\r\n      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-Static|Win32'\">\r\n    <ClCompile>\r\n      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n      <InlineFunctionExpansion>Default</InlineFunctionExpansion>\r\n      <FunctionLevelLinking>false</FunctionLevelLinking>\r\n      <Optimization>Disabled</Optimization>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <MinimalRebuild>true</MinimalRebuild>\r\n      <DebugInformationFormat>EditAndContinue</DebugInformationFormat>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>__CLEANUP_C;WIN32;_DEBUG;_WINDOWS;_USRDLL;PTW32_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AssemblerListingLocation>.\\.\\</AssemblerListingLocation>\r\n      <PrecompiledHeaderOutputFile>.\\.\\pthread.pch</PrecompiledHeaderOutputFile>\r\n      <ObjectFileName>.\\.\\</ObjectFileName>\r\n      <ProgramDataBaseFileName>.\\.\\</ProgramDataBaseFileName>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n    </ClCompile>\r\n    <Midl>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <TypeLibraryName>.\\.\\pthread.tlb</TypeLibraryName>\r\n      <MkTypLibCompatible>true</MkTypLibCompatible>\r\n      <TargetEnvironment>Win32</TargetEnvironment>\r\n    </Midl>\r\n    <ResourceCompile>\r\n      <Culture>0x0409</Culture>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>_DEBUG;PTW32_RC_MSC;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Bscmake>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <OutputFile>.\\.\\pthread.bsc</OutputFile>\r\n    </Bscmake>\r\n    <Link>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <LinkDLL>true</LinkDLL>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <SubSystem>Console</SubSystem>\r\n      <OutputFile>.\\pthreadVC2.dll</OutputFile>\r\n      <ImportLibrary>.\\.\\pthreadVC2.lib</ImportLibrary>\r\n      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|x64'\">\r\n    <ClCompile>\r\n      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n      <InlineFunctionExpansion>Default</InlineFunctionExpansion>\r\n      <FunctionLevelLinking>false</FunctionLevelLinking>\r\n      <Optimization>Disabled</Optimization>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>__CLEANUP_C;WIN32;_DEBUG;_WINDOWS;_USRDLL;PTW32_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AssemblerListingLocation>.\\.\\</AssemblerListingLocation>\r\n      <PrecompiledHeaderOutputFile>.\\.\\pthread.pch</PrecompiledHeaderOutputFile>\r\n      <ObjectFileName>.\\.\\</ObjectFileName>\r\n      <ProgramDataBaseFileName>.\\.\\</ProgramDataBaseFileName>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n    </ClCompile>\r\n    <Midl>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <TypeLibraryName>.\\.\\pthread.tlb</TypeLibraryName>\r\n      <MkTypLibCompatible>true</MkTypLibCompatible>\r\n    </Midl>\r\n    <ResourceCompile>\r\n      <Culture>0x0409</Culture>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>_DEBUG;PTW32_RC_MSC;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Bscmake>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <OutputFile>.\\.\\pthread.bsc</OutputFile>\r\n    </Bscmake>\r\n    <Link>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <LinkDLL>true</LinkDLL>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <SubSystem>Console</SubSystem>\r\n      <OutputFile>.\\pthreadVC2.dll</OutputFile>\r\n      <ImportLibrary>.\\.\\pthreadVC2.lib</ImportLibrary>\r\n      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n  </ItemDefinitionGroup>\r\n  <ItemDefinitionGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug-Static|x64'\">\r\n    <ClCompile>\r\n      <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r\n      <InlineFunctionExpansion>Default</InlineFunctionExpansion>\r\n      <FunctionLevelLinking>false</FunctionLevelLinking>\r\n      <Optimization>Disabled</Optimization>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <WarningLevel>Level3</WarningLevel>\r\n      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>__CLEANUP_C;WIN32;_DEBUG;_WINDOWS;_USRDLL;PTW32_BUILD;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <AssemblerListingLocation>.\\.\\</AssemblerListingLocation>\r\n      <PrecompiledHeaderOutputFile>.\\.\\pthread.pch</PrecompiledHeaderOutputFile>\r\n      <ObjectFileName>.\\.\\</ObjectFileName>\r\n      <ProgramDataBaseFileName>.\\.\\</ProgramDataBaseFileName>\r\n      <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r\n    </ClCompile>\r\n    <Midl>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n      <TypeLibraryName>.\\.\\pthread.tlb</TypeLibraryName>\r\n      <MkTypLibCompatible>true</MkTypLibCompatible>\r\n    </Midl>\r\n    <ResourceCompile>\r\n      <Culture>0x0409</Culture>\r\n      <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r\n      <PreprocessorDefinitions>_DEBUG;PTW32_RC_MSC;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r\n    </ResourceCompile>\r\n    <Bscmake>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <OutputFile>.\\.\\pthread.bsc</OutputFile>\r\n    </Bscmake>\r\n    <Link>\r\n      <SuppressStartupBanner>true</SuppressStartupBanner>\r\n      <LinkDLL>true</LinkDLL>\r\n      <GenerateDebugInformation>true</GenerateDebugInformation>\r\n      <SubSystem>Console</SubSystem>\r\n      <OutputFile>.\\pthreadVC2.dll</OutputFile>\r\n      <ImportLibrary>.\\.\\pthreadVC2.lib</ImportLibrary>\r\n      <AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>\r\n    </Link>\r\n    <Lib>\r\n      <OutputFile>.\\pthreadVC2.lib</OutputFile>\r\n    </Lib>\r\n  </ItemDefinitionGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"pthread.c\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"implement.h\" />\r\n    <ClInclude Include=\"pthread.h\" />\r\n    <ClInclude Include=\"sched.h\" />\r\n    <ClInclude Include=\"semaphore.h\" />\r\n  </ItemGroup>\r\n  <Import Project=\"$(VCTargetsPath)\\Microsoft.Cpp.targets\" />\r\n  <ImportGroup Label=\"ExtensionTargets\">\r\n  </ImportGroup>\r\n</Project>"
  },
  {
    "path": "deps/w32-pthreads/pthread.vcxproj.filters",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <ItemGroup>\r\n    <Filter Include=\"Source Files\">\r\n      <UniqueIdentifier>{ed014216-ffe7-4e4f-b783-9812a5e778d7}</UniqueIdentifier>\r\n      <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Header Files\">\r\n      <UniqueIdentifier>{a9cc5beb-e838-4849-9cbd-ee1de4bda860}</UniqueIdentifier>\r\n      <Extensions>h;hpp;hxx;hm;inl</Extensions>\r\n    </Filter>\r\n    <Filter Include=\"Resource Files\">\r\n      <UniqueIdentifier>{636c1d95-8104-45b5-aa12-6e8e3c5ebcee}</UniqueIdentifier>\r\n      <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>\r\n    </Filter>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClCompile Include=\"pthread.c\">\r\n      <Filter>Source Files</Filter>\r\n    </ClCompile>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <ClInclude Include=\"implement.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"pthread.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"sched.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n    <ClInclude Include=\"semaphore.h\">\r\n      <Filter>Header Files</Filter>\r\n    </ClInclude>\r\n  </ItemGroup>\r\n</Project>"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_destroy.c",
    "content": "/*\n * pthread_attr_destroy.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_attr_destroy (pthread_attr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Destroys a thread attributes object.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_attr_t\n      *\n      *\n      * DESCRIPTION\n      *      Destroys a thread attributes object.\n      *\n      *      NOTES:\n      *              1)      Does not affect threads created with 'attr'.\n      *\n      * RESULTS\n      *              0               successfully destroyed attr,\n      *              EINVAL          'attr' is invalid.\n      *\n      * ------------------------------------------------------\n      */\n{\n  if (ptw32_is_attr (attr) != 0)\n    {\n      return EINVAL;\n    }\n\n  /*\n   * Set the attribute object to a specific invalid value.\n   */\n  (*attr)->valid = 0;\n  free (*attr);\n  *attr = NULL;\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_getdetachstate.c",
    "content": "/*\n * pthread_attr_getdetachstate.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_attr_getdetachstate (const pthread_attr_t * attr, int *detachstate)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function determines whether threads created with\n      *      'attr' will run detached.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_attr_t\n      *\n      *      detachstate\n      *              pointer to an integer into which is returned one\n      *              of:\n      *\n      *              PTHREAD_CREATE_JOINABLE\n      *                              Thread ID is valid, must be joined\n      *\n      *              PTHREAD_CREATE_DETACHED\n      *                              Thread ID is invalid, cannot be joined,\n      *                              canceled, or modified\n      *\n      *\n      * DESCRIPTION\n      *      This function determines whether threads created with\n      *      'attr' will run detached.\n      *\n      *      NOTES:\n      *              1)      You cannot join or cancel detached threads.\n      *\n      * RESULTS\n      *              0               successfully retrieved detach state,\n      *              EINVAL          'attr' is invalid\n      *\n      * ------------------------------------------------------\n      */\n{\n  if (ptw32_is_attr (attr) != 0 || detachstate == NULL)\n    {\n      return EINVAL;\n    }\n\n  *detachstate = (*attr)->detachstate;\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_getinheritsched.c",
    "content": "/*\n * pthread_attr_getinheritsched.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\npthread_attr_getinheritsched (const pthread_attr_t * attr, int *inheritsched)\n{\n  if (ptw32_is_attr (attr) != 0 || inheritsched == NULL)\n    {\n      return EINVAL;\n    }\n\n  *inheritsched = (*attr)->inheritsched;\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_getschedparam.c",
    "content": "/*\n * pthread_attr_getschedparam.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\npthread_attr_getschedparam (const pthread_attr_t * attr,\n\t\t\t    struct sched_param *param)\n{\n  if (ptw32_is_attr (attr) != 0 || param == NULL)\n    {\n      return EINVAL;\n    }\n\n  memcpy (param, &(*attr)->param, sizeof (*param));\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_getschedpolicy.c",
    "content": "/*\n * pthread_attr_getschedpolicy.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\npthread_attr_getschedpolicy (const pthread_attr_t * attr, int *policy)\n{\n  if (ptw32_is_attr (attr) != 0 || policy == NULL)\n    {\n      return EINVAL;\n    }\n\n  /*\n   * Validate the policy arg.\n   * Check that a policy constant wasn't passed rather than &policy.\n   */\n  if (policy <= (int *) SCHED_MAX)\n    {\n      return EINVAL;\n    }\n\n  *policy = SCHED_OTHER;\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_getscope.c",
    "content": "/*\n * pthread_attr_getscope.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/* ignore warning \"unreferenced formal parameter\" */\n#if defined(_MSC_VER)\n#pragma warning( disable : 4100 )\n#endif\n\nint\npthread_attr_getscope (const pthread_attr_t * attr, int *contentionscope)\n{\n#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)\n  *contentionscope = (*attr)->contentionscope;\n  return 0;\n#else\n  return ENOSYS;\n#endif\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_getstackaddr.c",
    "content": "/*\n * pthread_attr_getstackaddr.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/* ignore warning \"unreferenced formal parameter\" */\n#if defined(_MSC_VER)\n#pragma warning( disable : 4100 )\n#endif\n\nint\npthread_attr_getstackaddr (const pthread_attr_t * attr, void **stackaddr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function determines the address of the stack\n      *      on which threads created with 'attr' will run.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_attr_t\n      *\n      *      stackaddr\n      *              pointer into which is returned the stack address.\n      *\n      *\n      * DESCRIPTION\n      *      This function determines the address of the stack\n      *      on which threads created with 'attr' will run.\n      *\n      *      NOTES:\n      *              1)      Function supported only if this macro is\n      *                      defined:\n      *\n      *                              _POSIX_THREAD_ATTR_STACKADDR\n      *\n      *              2)      Create only one thread for each stack\n      *                      address..\n      *\n      * RESULTS\n      *              0               successfully retreived stack address,\n      *              EINVAL          'attr' is invalid\n      *              ENOSYS          function not supported\n      *\n      * ------------------------------------------------------\n      */\n{\n#if defined( _POSIX_THREAD_ATTR_STACKADDR )\n\n  if (ptw32_is_attr (attr) != 0)\n    {\n      return EINVAL;\n    }\n\n  *stackaddr = (*attr)->stackaddr;\n  return 0;\n\n#else\n\n  return ENOSYS;\n\n#endif /* _POSIX_THREAD_ATTR_STACKADDR */\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_getstacksize.c",
    "content": "/*\n * pthread_attr_getstacksize.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/* ignore warning \"unreferenced formal parameter\" */\n#if defined(_MSC_VER)\n#pragma warning( disable : 4100 )\n#endif\n\nint\npthread_attr_getstacksize (const pthread_attr_t * attr, size_t * stacksize)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function determines the size of the stack on\n      *      which threads created with 'attr' will run.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_attr_t\n      *\n      *      stacksize\n      *              pointer to size_t into which is returned the\n      *              stack size, in bytes.\n      *\n      *\n      * DESCRIPTION\n      *      This function determines the size of the stack on\n      *      which threads created with 'attr' will run.\n      *\n      *      NOTES:\n      *              1)      Function supported only if this macro is\n      *                      defined:\n      *\n      *                              _POSIX_THREAD_ATTR_STACKSIZE\n      *\n      *              2)      Use on newly created attributes object to\n      *                      find the default stack size.\n      *\n      * RESULTS\n      *              0               successfully retrieved stack size,\n      *              EINVAL          'attr' is invalid\n      *              ENOSYS          function not supported\n      *\n      * ------------------------------------------------------\n      */\n{\n#if defined(_POSIX_THREAD_ATTR_STACKSIZE)\n\n  if (ptw32_is_attr (attr) != 0)\n    {\n      return EINVAL;\n    }\n\n  /* Everything is okay. */\n  *stacksize = (*attr)->stacksize;\n  return 0;\n\n#else\n\n  return ENOSYS;\n\n#endif /* _POSIX_THREAD_ATTR_STACKSIZE */\n\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_init.c",
    "content": "/*\n * pthread_attr_init.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_attr_init (pthread_attr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Initializes a thread attributes object with default\n      *      attributes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_attr_t\n      *\n      *\n      * DESCRIPTION\n      *      Initializes a thread attributes object with default\n      *      attributes.\n      *\n      *      NOTES:\n      *              1)      Used to define thread attributes\n      *\n      * RESULTS\n      *              0               successfully initialized attr,\n      *              ENOMEM          insufficient memory for attr.\n      *\n      * ------------------------------------------------------\n      */\n{\n  pthread_attr_t attr_result;\n\n  if (attr == NULL)\n    {\n      /* This is disallowed. */\n      return EINVAL;\n    }\n\n  attr_result = (pthread_attr_t) malloc (sizeof (*attr_result));\n\n  if (attr_result == NULL)\n    {\n      return ENOMEM;\n    }\n\n#if defined(_POSIX_THREAD_ATTR_STACKSIZE)\n  /*\n   * Default to zero size. Unless changed explicitly this\n   * will allow Win32 to set the size to that of the\n   * main thread.\n   */\n  attr_result->stacksize = 0;\n#endif\n\n#if defined(_POSIX_THREAD_ATTR_STACKADDR)\n  /* FIXME: Set this to something sensible when we support it. */\n  attr_result->stackaddr = NULL;\n#endif\n\n  attr_result->detachstate = PTHREAD_CREATE_JOINABLE;\n\n#if defined(HAVE_SIGSET_T)\n  memset (&(attr_result->sigmask), 0, sizeof (sigset_t));\n#endif /* HAVE_SIGSET_T */\n\n  /*\n   * Win32 sets new threads to THREAD_PRIORITY_NORMAL and\n   * not to that of the parent thread. We choose to default to\n   * this arrangement.\n   */\n  attr_result->param.sched_priority = THREAD_PRIORITY_NORMAL;\n  attr_result->inheritsched = PTHREAD_EXPLICIT_SCHED;\n  attr_result->contentionscope = PTHREAD_SCOPE_SYSTEM;\n\n  attr_result->valid = PTW32_ATTR_VALID;\n\n  *attr = attr_result;\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_setdetachstate.c",
    "content": "/*\n * pthread_attr_setdetachstate.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_attr_setdetachstate (pthread_attr_t * attr, int detachstate)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function specifies whether threads created with\n      *      'attr' will run detached.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_attr_t\n      *\n      *      detachstate\n      *              an integer containing one of:\n      *\n      *              PTHREAD_CREATE_JOINABLE\n      *                              Thread ID is valid, must be joined\n      *\n      *              PTHREAD_CREATE_DETACHED\n      *                              Thread ID is invalid, cannot be joined,\n      *                              canceled, or modified\n      *\n      *\n      * DESCRIPTION\n      *      This function specifies whether threads created with\n      *      'attr' will run detached.\n      *\n      *      NOTES:\n      *              1)      You cannot join or cancel detached threads.\n      *\n      * RESULTS\n      *              0               successfully set detach state,\n      *              EINVAL          'attr' or 'detachstate' is invalid\n      *\n      * ------------------------------------------------------\n      */\n{\n  if (ptw32_is_attr (attr) != 0)\n    {\n      return EINVAL;\n    }\n\n  if (detachstate != PTHREAD_CREATE_JOINABLE &&\n      detachstate != PTHREAD_CREATE_DETACHED)\n    {\n      return EINVAL;\n    }\n\n  (*attr)->detachstate = detachstate;\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_setinheritsched.c",
    "content": "/*\n * pthread_attr_setinheritsched.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\npthread_attr_setinheritsched (pthread_attr_t * attr, int inheritsched)\n{\n  if (ptw32_is_attr (attr) != 0)\n    {\n      return EINVAL;\n    }\n\n  if (PTHREAD_INHERIT_SCHED != inheritsched\n      && PTHREAD_EXPLICIT_SCHED != inheritsched)\n    {\n      return EINVAL;\n    }\n\n  (*attr)->inheritsched = inheritsched;\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_setschedparam.c",
    "content": "/*\n * pthread_attr_setschedparam.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\npthread_attr_setschedparam (pthread_attr_t * attr,\n\t\t\t    const struct sched_param *param)\n{\n  int priority;\n\n  if (ptw32_is_attr (attr) != 0 || param == NULL)\n    {\n      return EINVAL;\n    }\n\n  priority = param->sched_priority;\n\n  /* Validate priority level. */\n  if (priority < sched_get_priority_min (SCHED_OTHER) ||\n      priority > sched_get_priority_max (SCHED_OTHER))\n    {\n      return EINVAL;\n    }\n\n  memcpy (&(*attr)->param, param, sizeof (*param));\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_setschedpolicy.c",
    "content": "/*\n * pthread_attr_setschedpolicy.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\npthread_attr_setschedpolicy (pthread_attr_t * attr, int policy)\n{\n  if (ptw32_is_attr (attr) != 0)\n    {\n      return EINVAL;\n    }\n\n  if (policy != SCHED_OTHER)\n    {\n      return ENOTSUP;\n    }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_setscope.c",
    "content": "/*\n * pthread_attr_setscope.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/* ignore warning \"unreferenced formal parameter\" */\n#if defined(_MSC_VER)\n#pragma warning( disable : 4100 )\n#endif\n\nint\npthread_attr_setscope (pthread_attr_t * attr, int contentionscope)\n{\n#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)\n  switch (contentionscope)\n    {\n    case PTHREAD_SCOPE_SYSTEM:\n      (*attr)->contentionscope = contentionscope;\n      return 0;\n    case PTHREAD_SCOPE_PROCESS:\n      return ENOTSUP;\n    default:\n      return EINVAL;\n    }\n#else\n  return ENOSYS;\n#endif\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_setstackaddr.c",
    "content": "/*\n * pthread_attr_setstackaddr.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_attr_setstackaddr (pthread_attr_t * attr, void *stackaddr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Threads created with 'attr' will run on the stack\n      *      starting at 'stackaddr'.\n      *      Stack must be at least PTHREAD_STACK_MIN bytes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_attr_t\n      *\n      *      stackaddr\n      *              the address of the stack to use\n      *\n      *\n      * DESCRIPTION\n      *      Threads created with 'attr' will run on the stack\n      *      starting at 'stackaddr'.\n      *      Stack must be at least PTHREAD_STACK_MIN bytes.\n      *\n      *      NOTES:\n      *              1)      Function supported only if this macro is\n      *                      defined:\n      *\n      *                              _POSIX_THREAD_ATTR_STACKADDR\n      *\n      *              2)      Create only one thread for each stack\n      *                      address..\n      *\n      *              3)      Ensure that stackaddr is aligned.\n      *\n      * RESULTS\n      *              0               successfully set stack address,\n      *              EINVAL          'attr' is invalid\n      *              ENOSYS          function not supported\n      *\n      * ------------------------------------------------------\n      */\n{\n#if defined( _POSIX_THREAD_ATTR_STACKADDR )\n\n  if (ptw32_is_attr (attr) != 0)\n    {\n      return EINVAL;\n    }\n\n  (*attr)->stackaddr = stackaddr;\n  return 0;\n\n#else\n\n  return ENOSYS;\n\n#endif /* _POSIX_THREAD_ATTR_STACKADDR */\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_attr_setstacksize.c",
    "content": "/*\n * pthread_attr_setstacksize.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_attr_setstacksize (pthread_attr_t * attr, size_t stacksize)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function specifies the size of the stack on\n      *      which threads created with 'attr' will run.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_attr_t\n      *\n      *      stacksize\n      *              stack size, in bytes.\n      *\n      *\n      * DESCRIPTION\n      *      This function specifies the size of the stack on\n      *      which threads created with 'attr' will run.\n      *\n      *      NOTES:\n      *              1)      Function supported only if this macro is\n      *                      defined:\n      *\n      *                              _POSIX_THREAD_ATTR_STACKSIZE\n      *\n      *              2)      Find the default first (using\n      *                      pthread_attr_getstacksize), then increase\n      *                      by multiplying.\n      *\n      *              3)      Only use if thread needs more than the\n      *                      default.\n      *\n      * RESULTS\n      *              0               successfully set stack size,\n      *              EINVAL          'attr' is invalid or stacksize too\n      *                              small or too big.\n      *              ENOSYS          function not supported\n      *\n      * ------------------------------------------------------\n      */\n{\n#if defined(_POSIX_THREAD_ATTR_STACKSIZE)\n\n#if PTHREAD_STACK_MIN > 0\n\n  /*  Verify that the stack size is within range. */\n  if (stacksize < PTHREAD_STACK_MIN)\n    {\n      return EINVAL;\n    }\n\n#endif\n\n  if (ptw32_is_attr (attr) != 0)\n    {\n      return EINVAL;\n    }\n\n  /* Everything is okay. */\n  (*attr)->stacksize = stacksize;\n  return 0;\n\n#else\n\n  return ENOSYS;\n\n#endif /* _POSIX_THREAD_ATTR_STACKSIZE */\n\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_barrier_destroy.c",
    "content": "/*\n * pthread_barrier_destroy.c\n *\n * Description:\n * This translation unit implements barrier primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_barrier_destroy (pthread_barrier_t * barrier)\n{\n  int result = 0;\n  pthread_barrier_t b;\n  ptw32_mcs_local_node_t node;\n\n  if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID)\n    {\n      return EINVAL;\n    }\n\n  if (0 != ptw32_mcs_lock_try_acquire(&(*barrier)->lock, &node))\n    {\n      return EBUSY;\n    }\n\n  b = *barrier;\n\n  if (b->nCurrentBarrierHeight < b->nInitialBarrierHeight)\n    {\n      result = EBUSY;\n    }\n  else\n\t{\n      if (0 == (result = sem_destroy (&(b->semBarrierBreeched))))\n        {\n          *barrier = (pthread_barrier_t) PTW32_OBJECT_INVALID;\n          /*\n           * Release the lock before freeing b.\n           *\n           * FIXME: There may be successors which, when we release the lock,\n           * will be linked into b->lock, which will be corrupted at some\n           * point with undefined results for the application. To fix this\n           * will require changing pthread_barrier_t from a pointer to\n           * pthread_barrier_t_ to an instance. This is a change to the ABI\n           * and will require a major version number increment.\n           */\n          ptw32_mcs_lock_release(&node);\n\t  (void) free (b);\n\t  return 0;\n\t}\n      else\n        {\n          /*\n           * This should not ever be reached.\n           * Restore the barrier to working condition before returning.\n           */\n          (void) sem_init (&(b->semBarrierBreeched), b->pshared, 0);\n        }\n\n      if (result != 0)\n        {\n          /*\n           * The barrier still exists and is valid\n           * in the event of any error above.\n           */\n          result = EBUSY;\n        }\n    }\n\n  ptw32_mcs_lock_release(&node);\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_barrier_init.c",
    "content": "/*\n * pthread_barrier_init.c\n *\n * Description:\n * This translation unit implements barrier primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_barrier_init (pthread_barrier_t * barrier,\n\t\t      const pthread_barrierattr_t * attr, unsigned int count)\n{\n  pthread_barrier_t b;\n\n  if (barrier == NULL || count == 0)\n    {\n      return EINVAL;\n    }\n\n  if (NULL != (b = (pthread_barrier_t) calloc (1, sizeof (*b))))\n    {\n      b->pshared = (attr != NULL && *attr != NULL\n\t\t    ? (*attr)->pshared : PTHREAD_PROCESS_PRIVATE);\n\n      b->nCurrentBarrierHeight = b->nInitialBarrierHeight = count;\n      b->lock = 0;\n\n      if (0 == sem_init (&(b->semBarrierBreeched), b->pshared, 0))\n\t    {\n\t      *barrier = b;\n\t      return 0;\n\t    }\n      (void) free (b);\n    }\n\n  return ENOMEM;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_barrier_wait.c",
    "content": "/*\n * pthread_barrier_wait.c\n *\n * Description:\n * This translation unit implements barrier primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_barrier_wait (pthread_barrier_t * barrier)\n{\n  int result;\n  pthread_barrier_t b;\n\n  ptw32_mcs_local_node_t node;\n\n  if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID)\n    {\n      return EINVAL;\n    }\n\n  ptw32_mcs_lock_acquire(&(*barrier)->lock, &node);\n\n  b = *barrier;\n  if (--b->nCurrentBarrierHeight == 0)\n    {\n      /*\n       * We are the last thread to arrive at the barrier before it releases us.\n       * Move our MCS local node to the global scope barrier handle so that the\n       * last thread out (not necessarily us) can release the lock.\n       */\n      ptw32_mcs_node_transfer(&b->proxynode, &node);\n\n      /*\n       * Any threads that have not quite entered sem_wait below when the\n       * multiple_post has completed will nevertheless continue through\n       * the semaphore (barrier).\n       */\n      result = (b->nInitialBarrierHeight > 1\n                ? sem_post_multiple (&(b->semBarrierBreeched),\n\t\t\t\t     b->nInitialBarrierHeight - 1) : 0);\n    }\n  else\n    {\n      ptw32_mcs_lock_release(&node);\n      /*\n       * Use the non-cancelable version of sem_wait().\n       *\n       * It is possible that all nInitialBarrierHeight-1 threads are\n       * at this point when the last thread enters the barrier, resets\n       * nCurrentBarrierHeight = nInitialBarrierHeight and leaves.\n       * If pthread_barrier_destroy is called at that moment then the\n       * barrier will be destroyed along with the semas.\n       */\n      result = ptw32_semwait (&(b->semBarrierBreeched));\n    }\n\n  if ((PTW32_INTERLOCKED_LONG)PTW32_INTERLOCKED_INCREMENT_LONG((PTW32_INTERLOCKED_LONGPTR)&b->nCurrentBarrierHeight)\n\t\t  == (PTW32_INTERLOCKED_LONG)b->nInitialBarrierHeight)\n    {\n      /*\n       * We are the last thread to cross this barrier\n       */\n      ptw32_mcs_lock_release(&b->proxynode);\n      if (0 == result)\n        {\n          result = PTHREAD_BARRIER_SERIAL_THREAD;\n        }\n    }\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_barrierattr_destroy.c",
    "content": "/*\n * pthread_barrier_attr_destroy.c\n *\n * Description:\n * This translation unit implements barrier primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_barrierattr_destroy (pthread_barrierattr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Destroys a barrier attributes object. The object can\n      *      no longer be used.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_barrierattr_t\n      *\n      *\n      * DESCRIPTION\n      *      Destroys a barrier attributes object. The object can\n      *      no longer be used.\n      *\n      *      NOTES:\n      *              1)      Does not affect barrieres created using 'attr'\n      *\n      * RESULTS\n      *              0               successfully released attr,\n      *              EINVAL          'attr' is invalid.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n\n  if (attr == NULL || *attr == NULL)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      pthread_barrierattr_t ba = *attr;\n\n      *attr = NULL;\n      free (ba);\n    }\n\n  return (result);\n}\t\t\t\t/* pthread_barrierattr_destroy */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_barrierattr_getpshared.c",
    "content": "/*\n * pthread_barrier_attr_getpshared.c\n *\n * Description:\n * This translation unit implements barrier primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_barrierattr_getpshared (const pthread_barrierattr_t * attr,\n\t\t\t\tint *pshared)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Determine whether barriers created with 'attr' can be\n      *      shared between processes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_barrierattr_t\n      *\n      *      pshared\n      *              will be set to one of:\n      *\n      *                      PTHREAD_PROCESS_SHARED\n      *                              May be shared if in shared memory\n      *\n      *                      PTHREAD_PROCESS_PRIVATE\n      *                              Cannot be shared.\n      *\n      *\n      * DESCRIPTION\n      *      Mutexes creatd with 'attr' can be shared between\n      *      processes if pthread_barrier_t variable is allocated\n      *      in memory shared by these processes.\n      *      NOTES:\n      *              1)      pshared barriers MUST be allocated in shared\n      *                      memory.\n      *              2)      The following macro is defined if shared barriers\n      *                      are supported:\n      *                              _POSIX_THREAD_PROCESS_SHARED\n      *\n      * RESULTS\n      *              0               successfully retrieved attribute,\n      *              EINVAL          'attr' is invalid,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n\n  if ((attr != NULL && *attr != NULL) && (pshared != NULL))\n    {\n      *pshared = (*attr)->pshared;\n      result = 0;\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return (result);\n}\t\t\t\t/* pthread_barrierattr_getpshared */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_barrierattr_init.c",
    "content": "/*\n * pthread_barrier_attr_init.c\n *\n * Description:\n * This translation unit implements barrier primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_barrierattr_init (pthread_barrierattr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Initializes a barrier attributes object with default\n      *      attributes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_barrierattr_t\n      *\n      *\n      * DESCRIPTION\n      *      Initializes a barrier attributes object with default\n      *      attributes.\n      *\n      *      NOTES:\n      *              1)      Used to define barrier types\n      *\n      * RESULTS\n      *              0               successfully initialized attr,\n      *              ENOMEM          insufficient memory for attr.\n      *\n      * ------------------------------------------------------\n      */\n{\n  pthread_barrierattr_t ba;\n  int result = 0;\n\n  ba = (pthread_barrierattr_t) calloc (1, sizeof (*ba));\n\n  if (ba == NULL)\n    {\n      result = ENOMEM;\n    }\n  else\n    {\n      ba->pshared = PTHREAD_PROCESS_PRIVATE;\n    }\n\n  *attr = ba;\n\n  return (result);\n}\t\t\t\t/* pthread_barrierattr_init */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_barrierattr_setpshared.c",
    "content": "/*\n * pthread_barrier_attr_setpshared.c\n *\n * Description:\n * This translation unit implements barrier primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_barrierattr_setpshared (pthread_barrierattr_t * attr, int pshared)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Barriers created with 'attr' can be shared between\n      *      processes if pthread_barrier_t variable is allocated\n      *      in memory shared by these processes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_barrierattr_t\n      *\n      *      pshared\n      *              must be one of:\n      *\n      *                      PTHREAD_PROCESS_SHARED\n      *                              May be shared if in shared memory\n      *\n      *                      PTHREAD_PROCESS_PRIVATE\n      *                              Cannot be shared.\n      *\n      * DESCRIPTION\n      *      Mutexes creatd with 'attr' can be shared between\n      *      processes if pthread_barrier_t variable is allocated\n      *      in memory shared by these processes.\n      *\n      *      NOTES:\n      *              1)      pshared barriers MUST be allocated in shared\n      *                      memory.\n      *\n      *              2)      The following macro is defined if shared barriers\n      *                      are supported:\n      *                              _POSIX_THREAD_PROCESS_SHARED\n      *\n      * RESULTS\n      *              0               successfully set attribute,\n      *              EINVAL          'attr' or pshared is invalid,\n      *              ENOSYS          PTHREAD_PROCESS_SHARED not supported,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n\n  if ((attr != NULL && *attr != NULL) &&\n      ((pshared == PTHREAD_PROCESS_SHARED) ||\n       (pshared == PTHREAD_PROCESS_PRIVATE)))\n    {\n      if (pshared == PTHREAD_PROCESS_SHARED)\n\t{\n\n#if !defined( _POSIX_THREAD_PROCESS_SHARED )\n\n\t  result = ENOSYS;\n\t  pshared = PTHREAD_PROCESS_PRIVATE;\n\n#else\n\n\t  result = 0;\n\n#endif /* _POSIX_THREAD_PROCESS_SHARED */\n\n\t}\n      else\n\t{\n\t  result = 0;\n\t}\n\n      (*attr)->pshared = pshared;\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return (result);\n\n}\t\t\t\t/* pthread_barrierattr_setpshared */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_cancel.c",
    "content": "/*\n * pthread_cancel.c\n *\n * Description:\n * POSIX thread functions related to thread cancellation.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"context.h\"\n\nstatic void\nptw32_cancel_self (void)\n{\n  ptw32_throw (PTW32_EPS_CANCEL);\n\n  /* Never reached */\n}\n\nstatic void CALLBACK\nptw32_cancel_callback (ULONG_PTR unused)\n{\n  ptw32_throw (PTW32_EPS_CANCEL);\n\n  /* Never reached */\n}\n\n/*\n * ptw32_RegisterCancelation() -\n * Must have args of same type as QueueUserAPCEx because this function\n * is a substitute for QueueUserAPCEx if it's not available.\n */\nDWORD\nptw32_RegisterCancelation (PAPCFUNC unused1, HANDLE threadH, DWORD unused2)\n{\n  CONTEXT context;\n\n  context.ContextFlags = CONTEXT_CONTROL;\n  GetThreadContext (threadH, &context);\n  PTW32_PROGCTR (context) = (DWORD_PTR) ptw32_cancel_self;\n  SetThreadContext (threadH, &context);\n  return 0;\n}\n\nint\npthread_cancel (pthread_t thread)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function requests cancellation of 'thread'.\n      *\n      * PARAMETERS\n      *      thread\n      *              reference to an instance of pthread_t\n      *\n      *\n      * DESCRIPTION\n      *      This function requests cancellation of 'thread'.\n      *      NOTE: cancellation is asynchronous; use pthread_join to\n      *                wait for termination of 'thread' if necessary.\n      *\n      * RESULTS\n      *              0               successfully requested cancellation,\n      *              ESRCH           no thread found corresponding to 'thread',\n      *              ENOMEM          implicit self thread create failed.\n      * ------------------------------------------------------\n      */\n{\n  int result;\n  int cancel_self;\n  pthread_t self;\n  ptw32_thread_t * tp;\n  ptw32_mcs_local_node_t stateLock;\n\n  result = pthread_kill (thread, 0);\n\n  if (0 != result)\n    {\n      return result;\n    }\n\n  if ((self = pthread_self ()).p == NULL)\n    {\n      return ENOMEM;\n    };\n\n  /*\n   * For self cancellation we need to ensure that a thread can't\n   * deadlock itself trying to cancel itself asynchronously\n   * (pthread_cancel is required to be an async-cancel\n   * safe function).\n   */\n  cancel_self = pthread_equal (thread, self);\n\n  tp = (ptw32_thread_t *) thread.p;\n\n  /*\n   * Lock for async-cancel safety.\n   */\n  ptw32_mcs_lock_acquire (&tp->stateLock, &stateLock);\n\n  if (tp->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS\n      && tp->cancelState == PTHREAD_CANCEL_ENABLE\n      && tp->state < PThreadStateCanceling)\n    {\n      if (cancel_self)\n\t{\n\t  tp->state = PThreadStateCanceling;\n\t  tp->cancelState = PTHREAD_CANCEL_DISABLE;\n\n\t  ptw32_mcs_lock_release (&stateLock);\n\t  ptw32_throw (PTW32_EPS_CANCEL);\n\n\t  /* Never reached */\n\t}\n      else\n\t{\n\t  HANDLE threadH = tp->threadH;\n\n\t  SuspendThread (threadH);\n\n\t  if (WaitForSingleObject (threadH, 0) == WAIT_TIMEOUT)\n\t    {\n\t      tp->state = PThreadStateCanceling;\n\t      tp->cancelState = PTHREAD_CANCEL_DISABLE;\n\t      /*\n\t       * If alertdrv and QueueUserAPCEx is available then the following\n\t       * will result in a call to QueueUserAPCEx with the args given, otherwise\n\t       * this will result in a call to ptw32_RegisterCancelation and only\n\t       * the threadH arg will be used.\n\t       */\n\t      ptw32_register_cancelation ((PAPCFUNC)ptw32_cancel_callback, threadH, 0);\n\t      ptw32_mcs_lock_release (&stateLock);\n\t      ResumeThread (threadH);\n\t    }\n\t}\n    }\n  else\n    {\n      /*\n       * Set for deferred cancellation.\n       */\n      if (tp->state < PThreadStateCancelPending)\n\t{\n\t  tp->state = PThreadStateCancelPending;\n\t  if (!SetEvent (tp->cancelEvent))\n\t    {\n\t      result = ESRCH;\n\t    }\n\t}\n      else if (tp->state >= PThreadStateCanceling)\n\t{\n\t  result = ESRCH;\n\t}\n\n      ptw32_mcs_lock_release (&stateLock);\n    }\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_cond_destroy.c",
    "content": "/*\n * pthread_cond_destroy.c\n *\n * Description:\n * This translation unit implements condition variables and their primitives.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_cond_destroy (pthread_cond_t * cond)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function destroys a condition variable\n      *\n      *\n      * PARAMETERS\n      *      cond\n      *              pointer to an instance of pthread_cond_t\n      *\n      *\n      * DESCRIPTION\n      *      This function destroys a condition variable.\n      *\n      *      NOTES:\n      *              1)      A condition variable can be destroyed\n      *                      immediately after all the threads that\n      *                      are blocked on it are awakened. e.g.\n      *\n      *                      struct list {\n      *                        pthread_mutex_t lm;\n      *                        ...\n      *                      }\n      *\n      *                      struct elt {\n      *                        key k;\n      *                        int busy;\n      *                        pthread_cond_t notbusy;\n      *                        ...\n      *                      }\n      *\n      *                      \n      *                      struct elt *\n      *                      list_find(struct list *lp, key k)\n      *                      {\n      *                        struct elt *ep;\n      *\n      *                        pthread_mutex_lock(&lp->lm);\n      *                        while ((ep = find_elt(l,k) != NULL) && ep->busy)\n      *                          pthread_cond_wait(&ep->notbusy, &lp->lm);\n      *                        if (ep != NULL)\n      *                          ep->busy = 1;\n      *                        pthread_mutex_unlock(&lp->lm);\n      *                        return(ep);\n      *                      }\n      *\n      *                      delete_elt(struct list *lp, struct elt *ep)\n      *                      {\n      *                        pthread_mutex_lock(&lp->lm);\n      *                        assert(ep->busy);\n      *                        ... remove ep from list ...\n      *                        ep->busy = 0;\n      *                    (A) pthread_cond_broadcast(&ep->notbusy);\n      *                        pthread_mutex_unlock(&lp->lm);\n      *                    (B) pthread_cond_destroy(&rp->notbusy);\n      *                        free(ep);\n      *                      }\n      *\n      *                      In this example, the condition variable\n      *                      and its list element may be freed (line B)\n      *                      immediately after all threads waiting for\n      *                      it are awakened (line A), since the mutex\n      *                      and the code ensure that no other thread\n      *                      can touch the element to be deleted.\n      *\n      * RESULTS\n      *              0               successfully released condition variable,\n      *              EINVAL          'cond' is invalid,\n      *              EBUSY           'cond' is in use,\n      *\n      * ------------------------------------------------------\n      */\n{\n  pthread_cond_t cv;\n  int result = 0, result1 = 0, result2 = 0;\n\n  /*\n   * Assuming any race condition here is harmless.\n   */\n  if (cond == NULL || *cond == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (*cond != PTHREAD_COND_INITIALIZER)\n    {\n      ptw32_mcs_local_node_t node;\n      ptw32_mcs_lock_acquire(&ptw32_cond_list_lock, &node);\n\n      cv = *cond;\n\n      /*\n       * Close the gate; this will synchronize this thread with\n       * all already signaled waiters to let them retract their\n       * waiter status - SEE NOTE 1 ABOVE!!!\n       */\n      if (ptw32_semwait (&(cv->semBlockLock)) != 0) /* Non-cancelable */\n\t{\n\t  result = errno;\n\t}\n      else\n        {\n          /*\n           * !TRY! lock mtxUnblockLock; try will detect busy condition\n           * and will not cause a deadlock with respect to concurrent\n           * signal/broadcast.\n           */\n          if ((result = pthread_mutex_trylock (&(cv->mtxUnblockLock))) != 0)\n\t    {\n\t      (void) sem_post (&(cv->semBlockLock));\n\t    }\n\t}\n\t\n      if (result != 0)\n        {\n          ptw32_mcs_lock_release(&node);\n          return result;\n        }\n\n      /*\n       * Check whether cv is still busy (still has waiters)\n       */\n      if (cv->nWaitersBlocked > cv->nWaitersGone)\n\t{\n\t  if (sem_post (&(cv->semBlockLock)) != 0)\n\t    {\n\t      result = errno;\n\t    }\n\t  result1 = pthread_mutex_unlock (&(cv->mtxUnblockLock));\n\t  result2 = EBUSY;\n\t}\n      else\n\t{\n\t  /*\n\t   * Now it is safe to destroy\n\t   */\n\t  *cond = NULL;\n\n\t  if (sem_destroy (&(cv->semBlockLock)) != 0)\n\t    {\n\t      result = errno;\n\t    }\n\t  if (sem_destroy (&(cv->semBlockQueue)) != 0)\n\t    {\n\t      result1 = errno;\n\t    }\n\t  if ((result2 = pthread_mutex_unlock (&(cv->mtxUnblockLock))) == 0)\n\t    {\n\t      result2 = pthread_mutex_destroy (&(cv->mtxUnblockLock));\n\t    }\n\n\t  /* Unlink the CV from the list */\n\n\t  if (ptw32_cond_list_head == cv)\n\t    {\n\t      ptw32_cond_list_head = cv->next;\n\t    }\n\t  else\n\t    {\n\t      cv->prev->next = cv->next;\n\t    }\n\n\t  if (ptw32_cond_list_tail == cv)\n\t    {\n\t      ptw32_cond_list_tail = cv->prev;\n\t    }\n\t  else\n\t    {\n\t      cv->next->prev = cv->prev;\n\t    }\n\n\t  (void) free (cv);\n\t}\n\n      ptw32_mcs_lock_release(&node);\n    }\n  else\n    {\n      ptw32_mcs_local_node_t node;\n      /*\n       * See notes in ptw32_cond_check_need_init() above also.\n       */\n      ptw32_mcs_lock_acquire(&ptw32_cond_test_init_lock, &node);\n\n      /*\n       * Check again.\n       */\n      if (*cond == PTHREAD_COND_INITIALIZER)\n\t{\n\t  /*\n\t   * This is all we need to do to destroy a statically\n\t   * initialised cond that has not yet been used (initialised).\n\t   * If we get to here, another thread waiting to initialise\n\t   * this cond will get an EINVAL. That's OK.\n\t   */\n\t  *cond = NULL;\n\t}\n      else\n\t{\n\t  /*\n\t   * The cv has been initialised while we were waiting\n\t   * so assume it's in use.\n\t   */\n\t  result = EBUSY;\n\t}\n\n      ptw32_mcs_lock_release(&node);\n    }\n\n  return ((result != 0) ? result : ((result1 != 0) ? result1 : result2));\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_cond_init.c",
    "content": "/*\n * pthread_cond_init.c\n *\n * Description:\n * This translation unit implements condition variables and their primitives.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function initializes a condition variable.\n      *\n      * PARAMETERS\n      *      cond\n      *              pointer to an instance of pthread_cond_t\n      *\n      *      attr\n      *              specifies optional creation attributes.\n      *\n      *\n      * DESCRIPTION\n      *      This function initializes a condition variable.\n      *\n      * RESULTS\n      *              0               successfully created condition variable,\n      *              EINVAL          'attr' is invalid,\n      *              EAGAIN          insufficient resources (other than\n      *                              memory,\n      *              ENOMEM          insufficient memory,\n      *              EBUSY           'cond' is already initialized,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n  pthread_cond_t cv = NULL;\n\n  if (cond == NULL)\n    {\n      return EINVAL;\n    }\n\n  if ((attr != NULL && *attr != NULL) &&\n      ((*attr)->pshared == PTHREAD_PROCESS_SHARED))\n    {\n      /*\n       * Creating condition variable that can be shared between\n       * processes.\n       */\n      result = ENOSYS;\n      goto DONE;\n    }\n\n  cv = (pthread_cond_t) calloc (1, sizeof (*cv));\n\n  if (cv == NULL)\n    {\n      result = ENOMEM;\n      goto DONE;\n    }\n\n  cv->nWaitersBlocked = 0;\n  cv->nWaitersToUnblock = 0;\n  cv->nWaitersGone = 0;\n\n  if (sem_init (&(cv->semBlockLock), 0, 1) != 0)\n    {\n      result = errno;\n      goto FAIL0;\n    }\n\n  if (sem_init (&(cv->semBlockQueue), 0, 0) != 0)\n    {\n      result = errno;\n      goto FAIL1;\n    }\n\n  if ((result = pthread_mutex_init (&(cv->mtxUnblockLock), 0)) != 0)\n    {\n      goto FAIL2;\n    }\n\n  result = 0;\n\n  goto DONE;\n\n  /*\n   * -------------\n   * Failed...\n   * -------------\n   */\nFAIL2:\n  (void) sem_destroy (&(cv->semBlockQueue));\n\nFAIL1:\n  (void) sem_destroy (&(cv->semBlockLock));\n\nFAIL0:\n  (void) free (cv);\n  cv = NULL;\n\nDONE:\n  if (0 == result)\n    {\n      ptw32_mcs_local_node_t node;\n\n      ptw32_mcs_lock_acquire(&ptw32_cond_list_lock, &node);\n\n      cv->next = NULL;\n      cv->prev = ptw32_cond_list_tail;\n\n      if (ptw32_cond_list_tail != NULL)\n\t{\n\t  ptw32_cond_list_tail->next = cv;\n\t}\n\n      ptw32_cond_list_tail = cv;\n\n      if (ptw32_cond_list_head == NULL)\n\t{\n\t  ptw32_cond_list_head = cv;\n\t}\n\n      ptw32_mcs_lock_release(&node);\n    }\n\n  *cond = cv;\n\n  return result;\n\n}\t\t\t\t/* pthread_cond_init */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_cond_signal.c",
    "content": "/*\n * pthread_cond_signal.c\n *\n * Description:\n * This translation unit implements condition variables and their primitives.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * -------------------------------------------------------------\n * Algorithm:\n * See the comments at the top of pthread_cond_wait.c.\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nstatic INLINE int\nptw32_cond_unblock (pthread_cond_t * cond, int unblockAll)\n     /*\n      * Notes.\n      *\n      * Does not use the external mutex for synchronisation,\n      * therefore semBlockLock is needed.\n      * mtxUnblockLock is for LEVEL-2 synch. LEVEL-2 is the\n      * state where the external mutex is not necessarily locked by\n      * any thread, ie. between cond_wait unlocking and re-acquiring\n      * the lock after having been signaled or a timeout or\n      * cancellation.\n      *\n      * Uses the following CV elements:\n      *   nWaitersBlocked\n      *   nWaitersToUnblock\n      *   nWaitersGone\n      *   mtxUnblockLock\n      *   semBlockLock\n      *   semBlockQueue\n      */\n{\n  int result;\n  pthread_cond_t cv;\n  int nSignalsToIssue;\n\n  if (cond == NULL || *cond == NULL)\n    {\n      return EINVAL;\n    }\n\n  cv = *cond;\n\n  /*\n   * No-op if the CV is static and hasn't been initialised yet.\n   * Assuming that any race condition is harmless.\n   */\n  if (cv == PTHREAD_COND_INITIALIZER)\n    {\n      return 0;\n    }\n\n  if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0)\n    {\n      return result;\n    }\n\n  if (0 != cv->nWaitersToUnblock)\n    {\n      if (0 == cv->nWaitersBlocked)\n\t{\n\t  return pthread_mutex_unlock (&(cv->mtxUnblockLock));\n\t}\n      if (unblockAll)\n\t{\n\t  cv->nWaitersToUnblock += (nSignalsToIssue = cv->nWaitersBlocked);\n\t  cv->nWaitersBlocked = 0;\n\t}\n      else\n\t{\n\t  nSignalsToIssue = 1;\n\t  cv->nWaitersToUnblock++;\n\t  cv->nWaitersBlocked--;\n\t}\n    }\n  else if (cv->nWaitersBlocked > cv->nWaitersGone)\n    {\n      /* Use the non-cancellable version of sem_wait() */\n      if (ptw32_semwait (&(cv->semBlockLock)) != 0)\n\t{\n\t  result = errno;\n\t  (void) pthread_mutex_unlock (&(cv->mtxUnblockLock));\n\t  return result;\n\t}\n      if (0 != cv->nWaitersGone)\n\t{\n\t  cv->nWaitersBlocked -= cv->nWaitersGone;\n\t  cv->nWaitersGone = 0;\n\t}\n      if (unblockAll)\n\t{\n\t  nSignalsToIssue = cv->nWaitersToUnblock = cv->nWaitersBlocked;\n\t  cv->nWaitersBlocked = 0;\n\t}\n      else\n\t{\n\t  nSignalsToIssue = cv->nWaitersToUnblock = 1;\n\t  cv->nWaitersBlocked--;\n\t}\n    }\n  else\n    {\n      return pthread_mutex_unlock (&(cv->mtxUnblockLock));\n    }\n\n  if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) == 0)\n    {\n      if (sem_post_multiple (&(cv->semBlockQueue), nSignalsToIssue) != 0)\n\t{\n\t  result = errno;\n\t}\n    }\n\n  return result;\n\n}\t\t\t\t/* ptw32_cond_unblock */\n\nint\npthread_cond_signal (pthread_cond_t * cond)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function signals a condition variable, waking\n      *      one waiting thread.\n      *      If SCHED_FIFO or SCHED_RR policy threads are waiting\n      *      the highest priority waiter is awakened; otherwise,\n      *      an unspecified waiter is awakened.\n      *\n      * PARAMETERS\n      *      cond\n      *              pointer to an instance of pthread_cond_t\n      *\n      *\n      * DESCRIPTION\n      *      This function signals a condition variable, waking\n      *      one waiting thread.\n      *      If SCHED_FIFO or SCHED_RR policy threads are waiting\n      *      the highest priority waiter is awakened; otherwise,\n      *      an unspecified waiter is awakened.\n      *\n      *      NOTES:\n      *\n      *      1)      Use when any waiter can respond and only one need\n      *              respond (all waiters being equal).\n      *\n      * RESULTS\n      *              0               successfully signaled condition,\n      *              EINVAL          'cond' is invalid,\n      *\n      * ------------------------------------------------------\n      */\n{\n  /*\n   * The '0'(FALSE) unblockAll arg means unblock ONE waiter.\n   */\n  return (ptw32_cond_unblock (cond, 0));\n\n}\t\t\t\t/* pthread_cond_signal */\n\nint\npthread_cond_broadcast (pthread_cond_t * cond)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function broadcasts the condition variable,\n      *      waking all current waiters.\n      *\n      * PARAMETERS\n      *      cond\n      *              pointer to an instance of pthread_cond_t\n      *\n      *\n      * DESCRIPTION\n      *      This function signals a condition variable, waking\n      *      all waiting threads.\n      *\n      *      NOTES:\n      *\n      *      1)      Use when more than one waiter may respond to\n      *              predicate change or if any waiting thread may\n      *              not be able to respond\n      *\n      * RESULTS\n      *              0               successfully signalled condition to all\n      *                              waiting threads,\n      *              EINVAL          'cond' is invalid\n      *              ENOSPC          a required resource has been exhausted,\n      *\n      * ------------------------------------------------------\n      */\n{\n  /*\n   * The TRUE unblockAll arg means unblock ALL waiters.\n   */\n  return (ptw32_cond_unblock (cond, PTW32_TRUE));\n\n}\t\t\t\t/* pthread_cond_broadcast */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_cond_wait.c",
    "content": "/*\n * pthread_cond_wait.c\n *\n * Description:\n * This translation unit implements condition variables and their primitives.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * -------------------------------------------------------------\n * Algorithm:\n * The algorithm used in this implementation is that developed by\n * Alexander Terekhov in colaboration with Louis Thomas. The bulk\n * of the discussion is recorded in the file README.CV, which contains\n * several generations of both colaborators original algorithms. The final\n * algorithm used here is the one referred to as\n *\n *     Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL\n * \n * presented below in pseudo-code as it appeared:\n *\n *\n * given:\n * semBlockLock - bin.semaphore\n * semBlockQueue - semaphore\n * mtxExternal - mutex or CS\n * mtxUnblockLock - mutex or CS\n * nWaitersGone - int\n * nWaitersBlocked - int\n * nWaitersToUnblock - int\n * \n * wait( timeout ) {\n * \n *   [auto: register int result          ]     // error checking omitted\n *   [auto: register int nSignalsWasLeft ]\n *   [auto: register int nWaitersWasGone ]\n * \n *   sem_wait( semBlockLock );\n *   nWaitersBlocked++;\n *   sem_post( semBlockLock );\n * \n *   unlock( mtxExternal );\n *   bTimedOut = sem_wait( semBlockQueue,timeout );\n * \n *   lock( mtxUnblockLock );\n *   if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {\n *     if ( bTimeout ) {                       // timeout (or canceled)\n *       if ( 0 != nWaitersBlocked ) {\n *         nWaitersBlocked--;\n *       }\n *       else {\n *         nWaitersGone++;                     // count spurious wakeups.\n *       }\n *     }\n *     if ( 0 == --nWaitersToUnblock ) {\n *       if ( 0 != nWaitersBlocked ) {\n *         sem_post( semBlockLock );           // open the gate.\n *         nSignalsWasLeft = 0;                // do not open the gate\n *                                             // below again.\n *       }\n *       else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {\n *         nWaitersGone = 0;\n *       }\n *     }\n *   }\n *   else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or\n *                                             // spurious semaphore :-)\n *     sem_wait( semBlockLock );\n *     nWaitersBlocked -= nWaitersGone;     // something is going on here\n *                                          //  - test of timeouts? :-)\n *     sem_post( semBlockLock );\n *     nWaitersGone = 0;\n *   }\n *   unlock( mtxUnblockLock );\n * \n *   if ( 1 == nSignalsWasLeft ) {\n *     if ( 0 != nWaitersWasGone ) {\n *       // sem_adjust( semBlockQueue,-nWaitersWasGone );\n *       while ( nWaitersWasGone-- ) {\n *         sem_wait( semBlockQueue );       // better now than spurious later\n *       }\n *     } sem_post( semBlockLock );          // open the gate\n *   }\n * \n *   lock( mtxExternal );\n * \n *   return ( bTimedOut ) ? ETIMEOUT : 0;\n * }\n * \n * signal(bAll) {\n * \n *   [auto: register int result         ]\n *   [auto: register int nSignalsToIssue]\n * \n *   lock( mtxUnblockLock );\n * \n *   if ( 0 != nWaitersToUnblock ) {        // the gate is closed!!!\n *     if ( 0 == nWaitersBlocked ) {        // NO-OP\n *       return unlock( mtxUnblockLock );\n *     }\n *     if (bAll) {\n *       nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;\n *       nWaitersBlocked = 0;\n *     }\n *     else {\n *       nSignalsToIssue = 1;\n *       nWaitersToUnblock++;\n *       nWaitersBlocked--;\n *     }\n *   }\n *   else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!\n *     sem_wait( semBlockLock );                  // close the gate\n *     if ( 0 != nWaitersGone ) {\n *       nWaitersBlocked -= nWaitersGone;\n *       nWaitersGone = 0;\n *     }\n *     if (bAll) {\n *       nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;\n *       nWaitersBlocked = 0;\n *     }\n *     else {\n *       nSignalsToIssue = nWaitersToUnblock = 1;\n *       nWaitersBlocked--;\n *     }\n *   }\n *   else { // NO-OP\n *     return unlock( mtxUnblockLock );\n *   }\n * \n *   unlock( mtxUnblockLock );\n *   sem_post( semBlockQueue,nSignalsToIssue );\n *   return result;\n * }\n * -------------------------------------------------------------\n *\n *     Algorithm 9 / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL\n * \n * presented below in pseudo-code; basically 8a...\n *                                      ...BUT W/O \"spurious wakes\" prevention:\n *\n *\n * given:\n * semBlockLock - bin.semaphore\n * semBlockQueue - semaphore\n * mtxExternal - mutex or CS\n * mtxUnblockLock - mutex or CS\n * nWaitersGone - int\n * nWaitersBlocked - int\n * nWaitersToUnblock - int\n * \n * wait( timeout ) {\n * \n *   [auto: register int result          ]     // error checking omitted\n *   [auto: register int nSignalsWasLeft ]\n * \n *   sem_wait( semBlockLock );\n *   ++nWaitersBlocked;\n *   sem_post( semBlockLock );\n * \n *   unlock( mtxExternal );\n *   bTimedOut = sem_wait( semBlockQueue,timeout );\n * \n *   lock( mtxUnblockLock );\n *   if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {\n *     --nWaitersToUnblock;\n *   }\n *   else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or\n *                                             // spurious semaphore :-)\n *     sem_wait( semBlockLock );\n *     nWaitersBlocked -= nWaitersGone;        // something is going on here\n *                                             //  - test of timeouts? :-)\n *     sem_post( semBlockLock );\n *     nWaitersGone = 0;\n *   }\n *   unlock( mtxUnblockLock );\n * \n *   if ( 1 == nSignalsWasLeft ) {\n *     sem_post( semBlockLock );               // open the gate\n *   }\n * \n *   lock( mtxExternal );\n * \n *   return ( bTimedOut ) ? ETIMEOUT : 0;\n * }\n * \n * signal(bAll) {\n * \n *   [auto: register int result         ]\n *   [auto: register int nSignalsToIssue]\n * \n *   lock( mtxUnblockLock );\n * \n *   if ( 0 != nWaitersToUnblock ) {        // the gate is closed!!!\n *     if ( 0 == nWaitersBlocked ) {        // NO-OP\n *       return unlock( mtxUnblockLock );\n *     }\n *     if (bAll) {\n *       nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;\n *       nWaitersBlocked = 0;\n *     }\n *     else {\n *       nSignalsToIssue = 1;\n *       ++nWaitersToUnblock;\n *       --nWaitersBlocked;\n *     }\n *   }\n *   else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!\n *     sem_wait( semBlockLock );                  // close the gate\n *     if ( 0 != nWaitersGone ) {\n *       nWaitersBlocked -= nWaitersGone;\n *       nWaitersGone = 0;\n *     }\n *     if (bAll) {\n *       nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;\n *       nWaitersBlocked = 0;\n *     }\n *     else {\n *       nSignalsToIssue = nWaitersToUnblock = 1;\n *       --nWaitersBlocked;\n *     }\n *   }\n *   else { // NO-OP\n *     return unlock( mtxUnblockLock );\n *   }\n *\n *   unlock( mtxUnblockLock );\n *   sem_post( semBlockQueue,nSignalsToIssue );\n *   return result;\n * }\n * -------------------------------------------------------------\n *\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/*\n * Arguments for cond_wait_cleanup, since we can only pass a\n * single void * to it.\n */\ntypedef struct\n{\n  pthread_mutex_t *mutexPtr;\n  pthread_cond_t cv;\n  int *resultPtr;\n} ptw32_cond_wait_cleanup_args_t;\n\nstatic void PTW32_CDECL\nptw32_cond_wait_cleanup (void *args)\n{\n  ptw32_cond_wait_cleanup_args_t *cleanup_args =\n    (ptw32_cond_wait_cleanup_args_t *) args;\n  pthread_cond_t cv = cleanup_args->cv;\n  int *resultPtr = cleanup_args->resultPtr;\n  int nSignalsWasLeft;\n  int result;\n\n  /*\n   * Whether we got here as a result of signal/broadcast or because of\n   * timeout on wait or thread cancellation we indicate that we are no\n   * longer waiting. The waiter is responsible for adjusting waiters\n   * (to)unblock(ed) counts (protected by unblock lock).\n   */\n  if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0)\n    {\n      *resultPtr = result;\n      return;\n    }\n\n  if (0 != (nSignalsWasLeft = cv->nWaitersToUnblock))\n    {\n      --(cv->nWaitersToUnblock);\n    }\n  else if (INT_MAX / 2 == ++(cv->nWaitersGone))\n    {\n      /* Use the non-cancellable version of sem_wait() */\n      if (ptw32_semwait (&(cv->semBlockLock)) != 0)\n\t{\n\t  *resultPtr = errno;\n\t  /*\n\t   * This is a fatal error for this CV,\n\t   * so we deliberately don't unlock\n\t   * cv->mtxUnblockLock before returning.\n\t   */\n\t  return;\n\t}\n      cv->nWaitersBlocked -= cv->nWaitersGone;\n      if (sem_post (&(cv->semBlockLock)) != 0)\n\t{\n\t  *resultPtr = errno;\n\t  /*\n\t   * This is a fatal error for this CV,\n\t   * so we deliberately don't unlock\n\t   * cv->mtxUnblockLock before returning.\n\t   */\n\t  return;\n\t}\n      cv->nWaitersGone = 0;\n    }\n\n  if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) != 0)\n    {\n      *resultPtr = result;\n      return;\n    }\n\n  if (1 == nSignalsWasLeft)\n    {\n      if (sem_post (&(cv->semBlockLock)) != 0)\n\t{\n\t  *resultPtr = errno;\n\t  return;\n\t}\n    }\n\n  /*\n   * XSH: Upon successful return, the mutex has been locked and is owned\n   * by the calling thread.\n   */\n  if ((result = pthread_mutex_lock (cleanup_args->mutexPtr)) != 0)\n    {\n      *resultPtr = result;\n    }\n}\t\t\t\t/* ptw32_cond_wait_cleanup */\n\nstatic INLINE int\nptw32_cond_timedwait (pthread_cond_t * cond,\n\t\t      pthread_mutex_t * mutex, const struct timespec *abstime)\n{\n  int result = 0;\n  pthread_cond_t cv;\n  ptw32_cond_wait_cleanup_args_t cleanup_args;\n\n  if (cond == NULL || *cond == NULL)\n    {\n      return EINVAL;\n    }\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static condition variable. We check\n   * again inside the guarded section of ptw32_cond_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*cond == PTHREAD_COND_INITIALIZER)\n    {\n      result = ptw32_cond_check_need_init (cond);\n    }\n\n  if (result != 0 && result != EBUSY)\n    {\n      return result;\n    }\n\n  cv = *cond;\n\n  /* Thread can be cancelled in sem_wait() but this is OK */\n  if (sem_wait (&(cv->semBlockLock)) != 0)\n    {\n      return errno;\n    }\n\n  ++(cv->nWaitersBlocked);\n\n  if (sem_post (&(cv->semBlockLock)) != 0)\n    {\n      return errno;\n    }\n\n  /*\n   * Setup this waiter cleanup handler\n   */\n  cleanup_args.mutexPtr = mutex;\n  cleanup_args.cv = cv;\n  cleanup_args.resultPtr = &result;\n\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth(0)\n#endif\n  pthread_cleanup_push (ptw32_cond_wait_cleanup, (void *) &cleanup_args);\n\n  /*\n   * Now we can release 'mutex' and...\n   */\n  if ((result = pthread_mutex_unlock (mutex)) == 0)\n    {\n\n      /*\n       * ...wait to be awakened by\n       *              pthread_cond_signal, or\n       *              pthread_cond_broadcast, or\n       *              timeout, or\n       *              thread cancellation\n       *\n       * Note:\n       *\n       *      sem_timedwait is a cancellation point,\n       *      hence providing the mechanism for making\n       *      pthread_cond_wait a cancellation point.\n       *      We use the cleanup mechanism to ensure we\n       *      re-lock the mutex and adjust (to)unblock(ed) waiters\n       *      counts if we are cancelled, timed out or signalled.\n       */\n      if (sem_timedwait (&(cv->semBlockQueue), abstime) != 0)\n\t{\n\t  result = errno;\n\t}\n    }\n\n  /*\n   * Always cleanup\n   */\n  pthread_cleanup_pop (1);\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth()\n#endif\n\n  /*\n   * \"result\" can be modified by the cleanup handler.\n   */\n  return result;\n\n}\t\t\t\t/* ptw32_cond_timedwait */\n\n\nint\npthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function waits on a condition variable until\n      *      awakened by a signal or broadcast.\n      *\n      *      Caller MUST be holding the mutex lock; the\n      *      lock is released and the caller is blocked waiting\n      *      on 'cond'. When 'cond' is signaled, the mutex\n      *      is re-acquired before returning to the caller.\n      *\n      * PARAMETERS\n      *      cond\n      *              pointer to an instance of pthread_cond_t\n      *\n      *      mutex\n      *              pointer to an instance of pthread_mutex_t\n      *\n      *\n      * DESCRIPTION\n      *      This function waits on a condition variable until\n      *      awakened by a signal or broadcast.\n      *\n      *      NOTES:\n      *\n      *      1)      The function must be called with 'mutex' LOCKED\n      *              by the calling thread, or undefined behaviour\n      *              will result.\n      *\n      *      2)      This routine atomically releases 'mutex' and causes\n      *              the calling thread to block on the condition variable.\n      *              The blocked thread may be awakened by \n      *                      pthread_cond_signal or \n      *                      pthread_cond_broadcast.\n      *\n      * Upon successful completion, the 'mutex' has been locked and \n      * is owned by the calling thread.\n      *\n      *\n      * RESULTS\n      *              0               caught condition; mutex released,\n      *              EINVAL          'cond' or 'mutex' is invalid,\n      *              EINVAL          different mutexes for concurrent waits,\n      *              EINVAL          mutex is not held by the calling thread,\n      *\n      * ------------------------------------------------------\n      */\n{\n  /*\n   * The NULL abstime arg means INFINITE waiting.\n   */\n  return (ptw32_cond_timedwait (cond, mutex, NULL));\n\n}\t\t\t\t/* pthread_cond_wait */\n\n\nint\npthread_cond_timedwait (pthread_cond_t * cond,\n\t\t\tpthread_mutex_t * mutex,\n\t\t\tconst struct timespec *abstime)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function waits on a condition variable either until\n      *      awakened by a signal or broadcast; or until the time\n      *      specified by abstime passes.\n      *\n      * PARAMETERS\n      *      cond\n      *              pointer to an instance of pthread_cond_t\n      *\n      *      mutex\n      *              pointer to an instance of pthread_mutex_t\n      *\n      *      abstime\n      *              pointer to an instance of (const struct timespec)\n      *\n      *\n      * DESCRIPTION\n      *      This function waits on a condition variable either until\n      *      awakened by a signal or broadcast; or until the time\n      *      specified by abstime passes.\n      *\n      *      NOTES:\n      *      1)      The function must be called with 'mutex' LOCKED\n      *              by the calling thread, or undefined behaviour\n      *              will result.\n      *\n      *      2)      This routine atomically releases 'mutex' and causes\n      *              the calling thread to block on the condition variable.\n      *              The blocked thread may be awakened by \n      *                      pthread_cond_signal or \n      *                      pthread_cond_broadcast.\n      *\n      *\n      * RESULTS\n      *              0               caught condition; mutex released,\n      *              EINVAL          'cond', 'mutex', or abstime is invalid,\n      *              EINVAL          different mutexes for concurrent waits,\n      *              EINVAL          mutex is not held by the calling thread,\n      *              ETIMEDOUT       abstime ellapsed before cond was signaled.\n      *\n      * ------------------------------------------------------\n      */\n{\n  if (abstime == NULL)\n    {\n      return EINVAL;\n    }\n\n  return (ptw32_cond_timedwait (cond, mutex, abstime));\n\n}\t\t\t\t/* pthread_cond_timedwait */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_condattr_destroy.c",
    "content": "/*\n * condvar_attr_destroy.c\n *\n * Description:\n * This translation unit implements condition variables and their primitives.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_condattr_destroy (pthread_condattr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Destroys a condition variable attributes object.\n      *      The object can no longer be used.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_condattr_t\n      *\n      *\n      * DESCRIPTION\n      *      Destroys a condition variable attributes object.\n      *      The object can no longer be used.\n      *\n      *      NOTES:\n      *      1)      Does not affect condition variables created\n      *              using 'attr'\n      *\n      * RESULTS\n      *              0               successfully released attr,\n      *              EINVAL          'attr' is invalid.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n\n  if (attr == NULL || *attr == NULL)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      (void) free (*attr);\n\n      *attr = NULL;\n      result = 0;\n    }\n\n  return result;\n\n}\t\t\t\t/* pthread_condattr_destroy */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_condattr_getpshared.c",
    "content": "/*\n * pthread_condattr_getpshared.c\n *\n * Description:\n * This translation unit implements condition variables and their primitives.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_condattr_getpshared (const pthread_condattr_t * attr, int *pshared)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Determine whether condition variables created with 'attr'\n      *      can be shared between processes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_condattr_t\n      *\n      *      pshared\n      *              will be set to one of:\n      *\n      *                      PTHREAD_PROCESS_SHARED\n      *                              May be shared if in shared memory\n      *\n      *                      PTHREAD_PROCESS_PRIVATE\n      *                              Cannot be shared.\n      *\n      *\n      * DESCRIPTION\n      *      Condition Variables created with 'attr' can be shared\n      *      between processes if pthread_cond_t variable is allocated\n      *      in memory shared by these processes.\n      *      NOTES:\n      *      1)      pshared condition variables MUST be allocated in\n      *              shared memory.\n      *\n      *      2)      The following macro is defined if shared mutexes\n      *              are supported:\n      *                      _POSIX_THREAD_PROCESS_SHARED\n      *\n      * RESULTS\n      *              0               successfully retrieved attribute,\n      *              EINVAL          'attr' or 'pshared' is invalid,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n\n  if ((attr != NULL && *attr != NULL) && (pshared != NULL))\n    {\n      *pshared = (*attr)->pshared;\n      result = 0;\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return result;\n\n}\t\t\t\t/* pthread_condattr_getpshared */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_condattr_init.c",
    "content": "/*\n * pthread_condattr_init.c\n *\n * Description:\n * This translation unit implements condition variables and their primitives.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_condattr_init (pthread_condattr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Initializes a condition variable attributes object\n      *      with default attributes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_condattr_t\n      *\n      *\n      * DESCRIPTION\n      *      Initializes a condition variable attributes object\n      *      with default attributes.\n      *\n      *      NOTES:\n      *              1)      Use to define condition variable types\n      *              2)      It is up to the application to ensure\n      *                      that it doesn't re-init an attribute\n      *                      without destroying it first. Otherwise\n      *                      a memory leak is created.\n      *\n      * RESULTS\n      *              0               successfully initialized attr,\n      *              ENOMEM          insufficient memory for attr.\n      *\n      * ------------------------------------------------------\n      */\n{\n  pthread_condattr_t attr_result;\n  int result = 0;\n\n  attr_result = (pthread_condattr_t) calloc (1, sizeof (*attr_result));\n\n  if (attr_result == NULL)\n    {\n      result = ENOMEM;\n    }\n\n  *attr = attr_result;\n\n  return result;\n\n}\t\t\t\t/* pthread_condattr_init */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_condattr_setpshared.c",
    "content": "/*\n * pthread_condattr_setpshared.c\n *\n * Description:\n * This translation unit implements condition variables and their primitives.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_condattr_setpshared (pthread_condattr_t * attr, int pshared)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Mutexes created with 'attr' can be shared between\n      *      processes if pthread_mutex_t variable is allocated\n      *      in memory shared by these processes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_mutexattr_t\n      *\n      *      pshared\n      *              must be one of:\n      *\n      *                      PTHREAD_PROCESS_SHARED\n      *                              May be shared if in shared memory\n      *\n      *                      PTHREAD_PROCESS_PRIVATE\n      *                              Cannot be shared.\n      *\n      * DESCRIPTION\n      *      Mutexes creatd with 'attr' can be shared between\n      *      processes if pthread_mutex_t variable is allocated\n      *      in memory shared by these processes.\n      *\n      *      NOTES:\n      *              1)      pshared mutexes MUST be allocated in shared\n      *                      memory.\n      *\n      *              2)      The following macro is defined if shared mutexes\n      *                      are supported:\n      *                              _POSIX_THREAD_PROCESS_SHARED\n      *\n      * RESULTS\n      *              0               successfully set attribute,\n      *              EINVAL          'attr' or pshared is invalid,\n      *              ENOSYS          PTHREAD_PROCESS_SHARED not supported,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n\n  if ((attr != NULL && *attr != NULL)\n      && ((pshared == PTHREAD_PROCESS_SHARED)\n\t  || (pshared == PTHREAD_PROCESS_PRIVATE)))\n    {\n      if (pshared == PTHREAD_PROCESS_SHARED)\n\t{\n\n#if !defined( _POSIX_THREAD_PROCESS_SHARED )\n\t  result = ENOSYS;\n\t  pshared = PTHREAD_PROCESS_PRIVATE;\n#else\n\t  result = 0;\n\n#endif /* _POSIX_THREAD_PROCESS_SHARED */\n\n\t}\n      else\n\t{\n\t  result = 0;\n\t}\n\n      (*attr)->pshared = pshared;\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return result;\n\n}\t\t\t\t/* pthread_condattr_setpshared */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_delay_np.c",
    "content": "/*\n * pthreads_delay_np.c\n *\n * Description:\n * This translation unit implements non-portable thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/*\n * pthread_delay_np\n *\n * DESCRIPTION\n *\n *       This routine causes a thread to delay execution for a specific period of time.\n *       This period ends at the current time plus the specified interval. The routine\n *       will not return before the end of the period is reached, but may return an\n *       arbitrary amount of time after the period has gone by. This can be due to\n *       system load, thread priorities, and system timer granularity. \n *\n *       Specifying an interval of zero (0) seconds and zero (0) nanoseconds is\n *       allowed and can be used to force the thread to give up the processor or to\n *       deliver a pending cancelation request. \n *\n *       The timespec structure contains the following two fields: \n *\n *            tv_sec is an integer number of seconds. \n *            tv_nsec is an integer number of nanoseconds. \n *\n *  Return Values\n *\n *  If an error condition occurs, this routine returns an integer value indicating\n *  the type of error. Possible return values are as follows: \n *\n *  0 \n *           Successful completion.\n *  [EINVAL] \n *           The value specified by interval is invalid. \n *\n * Example\n *\n * The following code segment would wait for 5 and 1/2 seconds\n *\n *  struct timespec tsWait;\n *  int      intRC;\n *\n *  tsWait.tv_sec  = 5;\n *  tsWait.tv_nsec = 500000000L;\n *  intRC = pthread_delay_np(&tsWait);\n */\nint\npthread_delay_np (struct timespec *interval)\n{\n  DWORD wait_time;\n  DWORD secs_in_millisecs;\n  DWORD millisecs;\n  DWORD status;\n  pthread_t self;\n  ptw32_thread_t * sp;\n\n  if (interval == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (interval->tv_sec == 0L && interval->tv_nsec == 0L)\n    {\n      pthread_testcancel ();\n      Sleep (0);\n      pthread_testcancel ();\n      return (0);\n    }\n\n  /* convert secs to millisecs */\n  secs_in_millisecs = (DWORD)interval->tv_sec * 1000L;\n\n  /* convert nanosecs to millisecs (rounding up) */\n  millisecs = (interval->tv_nsec + 999999L) / 1000000L;\n\n#if defined(__WATCOMC__)\n#pragma disable_message (124)\n#endif\n\n  /*\n   * Most compilers will issue a warning 'comparison always 0'\n   * because the variable type is unsigned, but we need to keep this\n   * for some reason I can't recall now.\n   */\n  if (0 > (wait_time = secs_in_millisecs + millisecs))\n    {\n      return EINVAL;\n    }\n\n#if defined(__WATCOMC__)\n#pragma enable_message (124)\n#endif\n\n  if (NULL == (self = pthread_self ()).p)\n    {\n      return ENOMEM;\n    }\n\n  sp = (ptw32_thread_t *) self.p;\n\n  if (sp->cancelState == PTHREAD_CANCEL_ENABLE)\n    {\n      /*\n       * Async cancelation won't catch us until wait_time is up.\n       * Deferred cancelation will cancel us immediately.\n       */\n      if (WAIT_OBJECT_0 ==\n\t  (status = WaitForSingleObject (sp->cancelEvent, wait_time)))\n\t{\n          ptw32_mcs_local_node_t stateLock;\n\t  /*\n\t   * Canceling!\n\t   */\n\t  ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock);\n\t  if (sp->state < PThreadStateCanceling)\n\t    {\n\t      sp->state = PThreadStateCanceling;\n\t      sp->cancelState = PTHREAD_CANCEL_DISABLE;\n\t      ptw32_mcs_lock_release (&stateLock);\n\n\t      ptw32_throw (PTW32_EPS_CANCEL);\n\t    }\n\n\t  ptw32_mcs_lock_release (&stateLock);\n\t  return ESRCH;\n\t}\n      else if (status != WAIT_TIMEOUT)\n\t{\n\t  return EINVAL;\n\t}\n    }\n  else\n    {\n      Sleep (wait_time);\n    }\n\n  return (0);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_detach.c",
    "content": "/*\n * pthread_detach.c\n *\n * Description:\n * This translation unit implements functions related to thread\n * synchronisation.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/*\n * Not needed yet, but defining it should indicate clashes with build target\n * environment that should be fixed.\n */\n#if !defined(WINCE)\n#  include <signal.h>\n#endif\n\n\nint\npthread_detach (pthread_t thread)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function detaches the given thread.\n      *\n      * PARAMETERS\n      *      thread\n      *              an instance of a pthread_t\n      *\n      *\n      * DESCRIPTION\n      *      This function detaches the given thread. You may use it to\n      *      detach the main thread or to detach a joinable thread.\n      *      NOTE:   detached threads cannot be joined;\n      *              storage is freed immediately on termination.\n      *\n      * RESULTS\n      *              0               successfully detached the thread,\n      *              EINVAL          thread is not a joinable thread,\n      *              ENOSPC          a required resource has been exhausted,\n      *              ESRCH           no thread could be found for 'thread',\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n  BOOL destroyIt = PTW32_FALSE;\n  ptw32_thread_t * tp = (ptw32_thread_t *) thread.p;\n  ptw32_mcs_local_node_t node;\n\n  ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node);\n\n  if (NULL == tp\n      || thread.x != tp->ptHandle.x)\n    {\n      result = ESRCH;\n    }\n  else if (PTHREAD_CREATE_DETACHED == tp->detachState)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      ptw32_mcs_local_node_t stateLock;\n      /*\n       * Joinable ptw32_thread_t structs are not scavenged until\n       * a join or detach is done. The thread may have exited already,\n       * but all of the state and locks etc are still there.\n       */\n      result = 0;\n\n      ptw32_mcs_lock_acquire (&tp->stateLock, &stateLock);\n      if (tp->state != PThreadStateLast)\n        {\n          tp->detachState = PTHREAD_CREATE_DETACHED;\n        }\n      else if (tp->detachState != PTHREAD_CREATE_DETACHED)\n        {\n          /*\n           * Thread is joinable and has exited or is exiting.\n           */\n          destroyIt = PTW32_TRUE;\n        }\n      ptw32_mcs_lock_release (&stateLock);\n    }\n\n  ptw32_mcs_lock_release(&node);\n\n  if (result == 0)\n    {\n      /* Thread is joinable */\n\n      if (destroyIt)\n\t{\n\t  /* The thread has exited or is exiting but has not been joined or\n\t   * detached. Need to wait in case it's still exiting.\n\t   */\n\t  (void) WaitForSingleObject(tp->threadH, INFINITE);\n\t  ptw32_threadDestroy (thread);\n\t}\n    }\n\n  return (result);\n\n}\t\t\t\t/* pthread_detach */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_equal.c",
    "content": "/*\n * pthread_equal.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_equal (pthread_t t1, pthread_t t2)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function returns nonzero if t1 and t2 are equal, else\n      *      returns zero\n      *\n      * PARAMETERS\n      *      t1,\n      *      t2\n      *              thread IDs\n      *\n      *\n      * DESCRIPTION\n      *      This function returns nonzero if t1 and t2 are equal, else\n      *      returns zero.\n      *\n      * RESULTS\n      *              non-zero        if t1 and t2 refer to the same thread,\n      *              0               t1 and t2 do not refer to the same thread\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n\n  /*\n   * We also accept NULL == NULL - treating NULL as a thread\n   * for this special case, because there is no error that we can return.\n   */\n  result = ( t1.p == t2.p && t1.x == t2.x );\n\n  return (result);\n\n}\t\t\t\t/* pthread_equal */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_exit.c",
    "content": "/*\n * pthread_exit.c\n *\n * Description:\n * This translation unit implements routines associated with exiting from\n * a thread.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#if !defined(_UWIN)\n/*#   include <process.h> */\n#endif\n\nvoid\npthread_exit (void *value_ptr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function terminates the calling thread, returning\n      *      the value 'value_ptr' to any joining thread.\n      *\n      * PARAMETERS\n      *      value_ptr\n      *              a generic data value (i.e. not the address of a value)\n      *\n      *\n      * DESCRIPTION\n      *      This function terminates the calling thread, returning\n      *      the value 'value_ptr' to any joining thread.\n      *      NOTE: thread should be joinable.\n      *\n      * RESULTS\n      *              N/A\n      *\n      * ------------------------------------------------------\n      */\n{\n  ptw32_thread_t * sp;\n\n  /*\n   * Don't use pthread_self() to avoid creating an implicit POSIX thread handle\n   * unnecessarily.\n   */\n  sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey);\n\n#if defined(_UWIN)\n  if (--pthread_count <= 0)\n    exit ((int) value_ptr);\n#endif\n\n  if (NULL == sp)\n    {\n      /*\n       * A POSIX thread handle was never created. I.e. this is a\n       * Win32 thread that has never called a pthreads-win32 routine that\n       * required a POSIX handle.\n       *\n       * Implicit POSIX handles are cleaned up in ptw32_throw() now.\n       */\n\n#if ! (defined (__MINGW64__) || defined(__MINGW32__)) || defined (__MSVCRT__)  || defined (__DMC__)\n      _endthreadex ((unsigned) (size_t) value_ptr);\n#else\n      _endthread ();\n#endif\n\n      /* Never reached */\n    }\n\n  sp->exitStatus = value_ptr;\n\n  ptw32_throw (PTW32_EPS_EXIT);\n\n  /* Never reached. */\n\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_getconcurrency.c",
    "content": "/*\n * pthread_getconcurrency.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_getconcurrency (void)\n{\n  return ptw32_concurrency;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_getschedparam.c",
    "content": "/*\n * sched_getschedparam.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\npthread_getschedparam (pthread_t thread, int *policy,\n\t\t       struct sched_param *param)\n{\n  int result;\n\n  /* Validate the thread id. */\n  result = pthread_kill (thread, 0);\n  if (0 != result)\n    {\n      return result;\n    }\n\n  /*\n   * Validate the policy and param args.\n   * Check that a policy constant wasn't passed rather than &policy.\n   */\n  if (policy <= (int *) SCHED_MAX || param == NULL)\n    {\n      return EINVAL;\n    }\n\n  /* Fill out the policy. */\n  *policy = SCHED_OTHER;\n\n  /*\n   * This function must return the priority value set by\n   * the most recent pthread_setschedparam() or pthread_create()\n   * for the target thread. It must not return the actual thread\n   * priority as altered by any system priority adjustments etc.\n   */\n  param->sched_priority = ((ptw32_thread_t *)thread.p)->sched_priority;\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_getspecific.c",
    "content": "/*\n * pthread_getspecific.c\n *\n * Description:\n * POSIX thread functions which implement thread-specific data (TSD).\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nvoid *\npthread_getspecific (pthread_key_t key)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function returns the current value of key in the\n      *      calling thread. If no value has been set for 'key' in \n      *      the thread, NULL is returned.\n      *\n      * PARAMETERS\n      *      key\n      *              an instance of pthread_key_t\n      *\n      *\n      * DESCRIPTION\n      *      This function returns the current value of key in the\n      *      calling thread. If no value has been set for 'key' in \n      *      the thread, NULL is returned.\n      *\n      * RESULTS\n      *              key value or NULL on failure\n      *\n      * ------------------------------------------------------\n      */\n{\n  void * ptr;\n\n  if (key == NULL)\n    {\n      ptr = NULL;\n    }\n  else\n    {\n      int lasterror = GetLastError ();\n#if defined(RETAIN_WSALASTERROR)\n      int lastWSAerror = WSAGetLastError ();\n#endif\n      ptr = TlsGetValue (key->key);\n\n      SetLastError (lasterror);\n#if defined(RETAIN_WSALASTERROR)\n      WSASetLastError (lastWSAerror);\n#endif\n    }\n\n  return ptr;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_getunique_np.c",
    "content": "/*\n * pthread_getunique_np.c\n *\n * Description:\n * This translation unit implements non-portable thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/*\n *\n */\nunsigned __int64\npthread_getunique_np (pthread_t thread)\n{\n  return ((ptw32_thread_t*)thread.p)->seqNumber;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_getw32threadhandle_np.c",
    "content": "/*\n * pthread_getw32threadhandle_np.c\n *\n * Description:\n * This translation unit implements non-portable thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/*\n * pthread_getw32threadhandle_np()\n *\n * Returns the win32 thread handle that the POSIX\n * thread \"thread\" is running as.\n *\n * Applications can use the win32 handle to set\n * win32 specific attributes of the thread.\n */\nHANDLE\npthread_getw32threadhandle_np (pthread_t thread)\n{\n  return ((ptw32_thread_t *)thread.p)->threadH;\n}\n\n/*\n * pthread_getw32threadid_np()\n *\n * Returns the win32 thread id that the POSIX\n * thread \"thread\" is running as.\n */\nDWORD\npthread_getw32threadid_np (pthread_t thread)\n{\n  return ((ptw32_thread_t *)thread.p)->thread;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_join.c",
    "content": "/*\n * pthread_join.c\n *\n * Description:\n * This translation unit implements functions related to thread\n * synchronisation.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/*\n * Not needed yet, but defining it should indicate clashes with build target\n * environment that should be fixed.\n */\n#if !defined(WINCE)\n#  include <signal.h>\n#endif\n\n\nint\npthread_join (pthread_t thread, void **value_ptr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function waits for 'thread' to terminate and\n      *      returns the thread's exit value if 'value_ptr' is not\n      *      NULL. This also detaches the thread on successful\n      *      completion.\n      *\n      * PARAMETERS\n      *      thread\n      *              an instance of pthread_t\n      *\n      *      value_ptr\n      *              pointer to an instance of pointer to void\n      *\n      *\n      * DESCRIPTION\n      *      This function waits for 'thread' to terminate and\n      *      returns the thread's exit value if 'value_ptr' is not\n      *      NULL. This also detaches the thread on successful\n      *      completion.\n      *      NOTE:   detached threads cannot be joined or canceled\n      *\n      * RESULTS\n      *              0               'thread' has completed\n      *              EINVAL          thread is not a joinable thread,\n      *              ESRCH           no thread could be found with ID 'thread',\n      *              ENOENT          thread couldn't find it's own valid handle,\n      *              EDEADLK         attempt to join thread with self\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n  pthread_t self;\n  ptw32_thread_t * tp = (ptw32_thread_t *) thread.p;\n  ptw32_mcs_local_node_t node;\n\n  ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node);\n\n  if (NULL == tp\n      || thread.x != tp->ptHandle.x)\n    {\n      result = ESRCH;\n    }\n  else if (PTHREAD_CREATE_DETACHED == tp->detachState)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      result = 0;\n    }\n\n  ptw32_mcs_lock_release(&node);\n\n  if (result == 0)\n    {\n      /* \n       * The target thread is joinable and can't be reused before we join it.\n       */\n      self = pthread_self();\n\n      if (NULL == self.p)\n\t{\n\t  result = ENOENT;\n\t}\n      else if (pthread_equal (self, thread))\n\t{\n\t  result = EDEADLK;\n\t}\n      else\n\t{\n\t  /*\n\t   * Pthread_join is a cancelation point.\n\t   * If we are canceled then our target thread must not be\n\t   * detached (destroyed). This is guarranteed because\n\t   * pthreadCancelableWait will not return if we\n\t   * are canceled.\n\t   */\n\t  result = pthreadCancelableWait (tp->threadH);\n\n\t  if (0 == result)\n\t    {\n\t      if (value_ptr != NULL)\n\t\t{\n\t\t  *value_ptr = tp->exitStatus;\n\t\t}\n\n\t      /*\n\t       * The result of making multiple simultaneous calls to\n\t       * pthread_join() or pthread_detach() specifying the same\n\t       * target is undefined.\n\t       */\n\t      result = pthread_detach (thread);\n\t    }\n\t  else\n\t    {\n\t      result = ESRCH;\n\t    }\n\t}\n    }\n\n  return (result);\n\n}\t\t\t\t/* pthread_join */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_key_create.c",
    "content": "/*\n * pthread_key_create.c\n *\n * Description:\n * POSIX thread functions which implement thread-specific data (TSD).\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n/* TLS_OUT_OF_INDEXES not defined on WinCE */\n#if !defined(TLS_OUT_OF_INDEXES)\n#define TLS_OUT_OF_INDEXES 0xffffffff\n#endif\n\nint\npthread_key_create (pthread_key_t * key, void (PTW32_CDECL *destructor) (void *))\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function creates a thread-specific data key visible\n      *      to all threads. All existing and new threads have a value\n      *      NULL for key until set using pthread_setspecific. When any\n      *      thread with a non-NULL value for key terminates, 'destructor'\n      *      is called with key's current value for that thread.\n      *\n      * PARAMETERS\n      *      key\n      *              pointer to an instance of pthread_key_t\n      *\n      *\n      * DESCRIPTION\n      *      This function creates a thread-specific data key visible\n      *      to all threads. All existing and new threads have a value\n      *      NULL for key until set using pthread_setspecific. When any\n      *      thread with a non-NULL value for key terminates, 'destructor'\n      *      is called with key's current value for that thread.\n      *\n      * RESULTS\n      *              0               successfully created semaphore,\n      *              EAGAIN          insufficient resources or PTHREAD_KEYS_MAX\n      *                              exceeded,\n      *              ENOMEM          insufficient memory to create the key,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  pthread_key_t newkey;\n\n  if ((newkey = (pthread_key_t) calloc (1, sizeof (*newkey))) == NULL)\n    {\n      result = ENOMEM;\n    }\n  else if ((newkey->key = TlsAlloc ()) == TLS_OUT_OF_INDEXES)\n    {\n      result = EAGAIN;\n\n      free (newkey);\n      newkey = NULL;\n    }\n  else if (destructor != NULL)\n    {\n      /*\n       * Have to manage associations between thread and key;\n       * Therefore, need a lock that allows competing threads\n       * to gain exclusive access to the key->threads list.\n       *\n       * The mutex will only be created when it is first locked.\n       */\n      newkey->keyLock = 0;\n      newkey->destructor = destructor;\n    }\n\n  *key = newkey;\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_key_delete.c",
    "content": "/*\n * pthread_key_delete.c\n *\n * Description:\n * POSIX thread functions which implement thread-specific data (TSD).\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_key_delete (pthread_key_t key)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function deletes a thread-specific data key. This\n      *      does not change the value of the thread specific data key\n      *      for any thread and does not run the key's destructor\n      *      in any thread so it should be used with caution.\n      *\n      * PARAMETERS\n      *      key\n      *              pointer to an instance of pthread_key_t\n      *\n      *\n      * DESCRIPTION\n      *      This function deletes a thread-specific data key. This\n      *      does not change the value of the thread specific data key\n      *      for any thread and does not run the key's destructor\n      *      in any thread so it should be used with caution.\n      *\n      * RESULTS\n      *              0               successfully deleted the key,\n      *              EINVAL          key is invalid,\n      *\n      * ------------------------------------------------------\n      */\n{\n  ptw32_mcs_local_node_t keyLock;\n  int result = 0;\n\n  if (key != NULL)\n    {\n      if (key->threads != NULL && key->destructor != NULL)\n\t{\n\t  ThreadKeyAssoc *assoc;\n\t  ptw32_mcs_lock_acquire (&(key->keyLock), &keyLock);\n\t  /*\n\t   * Run through all Thread<-->Key associations\n\t   * for this key.\n\t   *\n\t   * While we hold at least one of the locks guarding\n\t   * the assoc, we know that the assoc pointed to by\n\t   * key->threads is valid.\n\t   */\n\t  while ((assoc = (ThreadKeyAssoc *) key->threads) != NULL)\n\t    {\n              ptw32_mcs_local_node_t threadLock;\n\t      ptw32_thread_t * thread = assoc->thread;\n\n\t      if (assoc == NULL)\n\t\t{\n\t\t  /* Finished */\n\t\t  break;\n\t\t}\n\n\t      ptw32_mcs_lock_acquire (&(thread->threadLock), &threadLock);\n\t      /*\n\t       * Since we are starting at the head of the key's threads\n\t       * chain, this will also point key->threads at the next assoc.\n\t       * While we hold key->keyLock, no other thread can insert\n\t       * a new assoc via pthread_setspecific.\n\t       */\n\t      ptw32_tkAssocDestroy (assoc);\n\t      ptw32_mcs_lock_release (&threadLock);\n\t      ptw32_mcs_lock_release (&keyLock);\n\t    }\n\t}\n\n      TlsFree (key->key);\n      if (key->destructor != NULL)\n\t{\n\t  /* A thread could be holding the keyLock */\n\t  ptw32_mcs_lock_acquire (&(key->keyLock), &keyLock);\n\t  ptw32_mcs_lock_release (&keyLock);\n\t}\n\n#if defined( _DEBUG )\n      memset ((char *) key, 0, sizeof (*key));\n#endif\n      free (key);\n    }\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_kill.c",
    "content": "/*\n * pthread_kill.c\n *\n * Description:\n * This translation unit implements the pthread_kill routine.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/*\n * Not needed yet, but defining it should indicate clashes with build target\n * environment that should be fixed.\n */\n#if !defined(WINCE)\n#  include <signal.h>\n#endif\n\nint\npthread_kill (pthread_t thread, int sig)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function requests that a signal be delivered to the\n      *      specified thread. If sig is zero, error checking is\n      *      performed but no signal is actually sent such that this\n      *      function can be used to check for a valid thread ID.\n      *\n      * PARAMETERS\n      *      thread  reference to an instances of pthread_t\n      *      sig     signal. Currently only a value of 0 is supported.\n      *\n      *\n      * DESCRIPTION\n      *      This function requests that a signal be delivered to the\n      *      specified thread. If sig is zero, error checking is\n      *      performed but no signal is actually sent such that this\n      *      function can be used to check for a valid thread ID.\n      *\n      * RESULTS\n      *              ESRCH           the thread is not a valid thread ID,\n      *              EINVAL          the value of the signal is invalid\n      *                              or unsupported.\n      *              0               the signal was successfully sent.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  ptw32_thread_t * tp;\n  ptw32_mcs_local_node_t node;\n\n  ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node);\n\n  tp = (ptw32_thread_t *) thread.p;\n\n  if (NULL == tp\n      || thread.x != tp->ptHandle.x\n      || NULL == tp->threadH)\n    {\n      result = ESRCH;\n    }\n\n  ptw32_mcs_lock_release(&node);\n\n  if (0 == result && 0 != sig)\n    {\n      /*\n       * Currently does not support any signals.\n       */\n      result = EINVAL;\n    }\n\n  return result;\n\n}\t\t\t\t/* pthread_kill */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutex_consistent.c",
    "content": "/*\n * pthread_mutex_consistent.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n#if !defined(_UWIN)\n/*#   include <process.h> */\n#endif\n#include \"pthread.h\"\n#include \"implement.h\"\n\nINLINE\nint\nptw32_robust_mutex_inherit(pthread_mutex_t * mutex)\n{\n  int result;\n  pthread_mutex_t mx = *mutex;\n  ptw32_robust_node_t* robust = mx->robustNode;\n\n  switch ((LONG)PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG(\n            (PTW32_INTERLOCKED_LONGPTR)&robust->stateInconsistent,\n            (PTW32_INTERLOCKED_LONG)PTW32_ROBUST_INCONSISTENT,\n            (PTW32_INTERLOCKED_LONG)-1 /* The terminating thread sets this */))\n    {\n      case -1L:\n          result = EOWNERDEAD;\n          break;\n      case (LONG)PTW32_ROBUST_NOTRECOVERABLE:\n          result = ENOTRECOVERABLE;\n          break;\n      default:\n          result = 0;\n          break;\n    }\n\n  return result;\n}\n\n/*\n * The next two internal support functions depend on only being\n * called by the thread that owns the robust mutex. This enables\n * us to avoid additional locks.\n * Any mutex currently in the thread's robust mutex list is held\n * by the thread, again eliminating the need for locks.\n * The forward/backward links allow the thread to unlock mutexes\n * in any order, not necessarily the reverse locking order.\n * This is all possible because it is an error if a thread that\n * does not own the [robust] mutex attempts to unlock it.\n */\n\nINLINE\nvoid\nptw32_robust_mutex_add(pthread_mutex_t* mutex, pthread_t self)\n{\n  ptw32_robust_node_t** list;\n  pthread_mutex_t mx = *mutex;\n  ptw32_thread_t* tp = (ptw32_thread_t*)self.p;\n  ptw32_robust_node_t* robust = mx->robustNode;\n\n  list = &tp->robustMxList;\n  mx->ownerThread = self;\n  if (NULL == *list)\n    {\n      robust->prev = NULL;\n      robust->next = NULL;\n      *list = robust;\n    }\n  else\n    {\n      robust->prev = NULL;\n      robust->next = *list;\n      (*list)->prev = robust;\n      *list = robust;\n    }\n}\n\nINLINE\nvoid\nptw32_robust_mutex_remove(pthread_mutex_t* mutex, ptw32_thread_t* otp)\n{\n  ptw32_robust_node_t** list;\n  pthread_mutex_t mx = *mutex;\n  ptw32_robust_node_t* robust = mx->robustNode;\n\n  list = &(((ptw32_thread_t*)mx->ownerThread.p)->robustMxList);\n  mx->ownerThread.p = otp;\n  if (robust->next != NULL)\n    {\n      robust->next->prev = robust->prev;\n    }\n  if (robust->prev != NULL)\n    {\n      robust->prev->next = robust->next;\n    }\n  if (*list == robust)\n    {\n      *list = robust->next;\n    }\n}\n\n\nint\npthread_mutex_consistent (pthread_mutex_t* mutex)\n{\n  pthread_mutex_t mx = *mutex;\n  int result = 0;\n\n  /*\n   * Let the system deal with invalid pointers.\n   */\n  if (mx == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (mx->kind >= 0\n        || (PTW32_INTERLOCKED_LONG)PTW32_ROBUST_INCONSISTENT != PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG(\n                                                (PTW32_INTERLOCKED_LONGPTR)&mx->robustNode->stateInconsistent,\n                                                (PTW32_INTERLOCKED_LONG)PTW32_ROBUST_CONSISTENT,\n                                                (PTW32_INTERLOCKED_LONG)PTW32_ROBUST_INCONSISTENT))\n    {\n      result = EINVAL;\n    }\n\n  return (result);\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutex_destroy.c",
    "content": "/*\n * pthread_mutex_destroy.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutex_destroy (pthread_mutex_t * mutex)\n{\n  int result = 0;\n  pthread_mutex_t mx;\n\n  /*\n   * Let the system deal with invalid pointers.\n   */\n\n  /*\n   * Check to see if we have something to delete.\n   */\n  if (*mutex < PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)\n    {\n      mx = *mutex;\n\n      result = pthread_mutex_trylock (&mx);\n\n      /*\n       * If trylock succeeded and the mutex is not recursively locked it\n       * can be destroyed.\n       */\n      if (0 == result || ENOTRECOVERABLE == result)\n\t{\n\t  if (mx->kind != PTHREAD_MUTEX_RECURSIVE || 1 == mx->recursive_count)\n\t    {\n\t      /*\n\t       * FIXME!!!\n\t       * The mutex isn't held by another thread but we could still\n\t       * be too late invalidating the mutex below since another thread\n\t       * may already have entered mutex_lock and the check for a valid\n\t       * *mutex != NULL.\n\t       */\n\t      *mutex = NULL;\n\n\t      result = (0 == result)?pthread_mutex_unlock(&mx):0;\n\n\t      if (0 == result)\n\t\t{\n                  if (mx->robustNode != NULL)\n                    {\n                      free(mx->robustNode);\n                    }\n\t\t  if (!CloseHandle (mx->event))\n\t\t    {\n\t\t      *mutex = mx;\n\t\t      result = EINVAL;\n\t\t    }\n\t\t  else\n\t\t    {\n\t\t      free (mx);\n\t\t    }\n\t\t}\n\t      else\n\t\t{\n\t\t  /*\n\t\t   * Restore the mutex before we return the error.\n\t\t   */\n\t\t  *mutex = mx;\n\t\t}\n\t    }\n\t  else\t\t\t/* mx->recursive_count > 1 */\n\t    {\n\t      /*\n\t       * The mutex must be recursive and already locked by us (this thread).\n\t       */\n\t      mx->recursive_count--;\t/* Undo effect of pthread_mutex_trylock() above */\n\t      result = EBUSY;\n\t    }\n\t}\n    }\n  else\n    {\n      ptw32_mcs_local_node_t node;\n\n      /*\n       * See notes in ptw32_mutex_check_need_init() above also.\n       */\n\n      ptw32_mcs_lock_acquire(&ptw32_mutex_test_init_lock, &node);\n\n      /*\n       * Check again.\n       */\n      if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)\n\t{\n\t  /*\n\t   * This is all we need to do to destroy a statically\n\t   * initialised mutex that has not yet been used (initialised).\n\t   * If we get to here, another thread\n\t   * waiting to initialise this mutex will get an EINVAL.\n\t   */\n\t  *mutex = NULL;\n\t}\n      else\n\t{\n\t  /*\n\t   * The mutex has been initialised while we were waiting\n\t   * so assume it's in use.\n\t   */\n\t  result = EBUSY;\n\t}\n      ptw32_mcs_lock_release(&node);\n    }\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutex_init.c",
    "content": "/*\n * pthread_mutex_init.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutex_init (pthread_mutex_t * mutex, const pthread_mutexattr_t * attr)\n{\n  int result = 0;\n  pthread_mutex_t mx;\n\n  if (mutex == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (attr != NULL && *attr != NULL)\n    {\n      if ((*attr)->pshared == PTHREAD_PROCESS_SHARED)\n        {\n          /*\n           * Creating mutex that can be shared between\n           * processes.\n           */\n#if _POSIX_THREAD_PROCESS_SHARED >= 0\n\n          /*\n           * Not implemented yet.\n           */\n\n#error ERROR [__FILE__, line __LINE__]: Process shared mutexes are not supported yet.\n\n#else\n\n          return ENOSYS;\n\n#endif /* _POSIX_THREAD_PROCESS_SHARED */\n        }\n    }\n\n  mx = (pthread_mutex_t) calloc (1, sizeof (*mx));\n\n  if (mx == NULL)\n    {\n      result = ENOMEM;\n    }\n  else\n    {\n      mx->lock_idx = 0;\n      mx->recursive_count = 0;\n      mx->robustNode = NULL;\n      if (attr == NULL || *attr == NULL)\n        {\n          mx->kind = PTHREAD_MUTEX_DEFAULT;\n        }\n      else\n        {\n          mx->kind = (*attr)->kind;\n          if ((*attr)->robustness == PTHREAD_MUTEX_ROBUST)\n            {\n              /*\n               * Use the negative range to represent robust types.\n               * Replaces a memory fetch with a register negate and incr\n               * in pthread_mutex_lock etc.\n               *\n               * Map 0,1,..,n to -1,-2,..,(-n)-1\n               */\n              mx->kind = -mx->kind - 1;\n\n              mx->robustNode = (ptw32_robust_node_t*) malloc(sizeof(ptw32_robust_node_t));\n              mx->robustNode->stateInconsistent = PTW32_ROBUST_CONSISTENT;\n              mx->robustNode->mx = mx;\n              mx->robustNode->next = NULL;\n              mx->robustNode->prev = NULL;\n            }\n        }\n\n      mx->ownerThread.p = NULL;\n\n      mx->event = CreateEvent (NULL, PTW32_FALSE,    /* manual reset = No */\n                              PTW32_FALSE,           /* initial state = not signaled */\n                              NULL);                 /* event name */\n\n      if (0 == mx->event)\n        {\n          result = ENOSPC;\n          free (mx);\n          mx = NULL;\n        }\n    }\n\n  *mutex = mx;\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutex_lock.c",
    "content": "/*\n * pthread_mutex_lock.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#if !defined(_UWIN)\n/*#   include <process.h> */\n#endif\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_mutex_lock (pthread_mutex_t * mutex)\n{\n  int kind;\n  pthread_mutex_t mx;\n  int result = 0;\n\n  /*\n   * Let the system deal with invalid pointers.\n   */\n  if (*mutex == NULL)\n    {\n      return EINVAL;\n    }\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static mutex. We check\n   * again inside the guarded section of ptw32_mutex_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)\n    {\n      if ((result = ptw32_mutex_check_need_init (mutex)) != 0)\n\t{\n\t  return (result);\n\t}\n    }\n\n  mx = *mutex;\n  kind = mx->kind;\n\n  if (kind >= 0)\n    {\n      /* Non-robust */\n      if (PTHREAD_MUTEX_NORMAL == kind)\n        {\n          if ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n\t\t       (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t       (PTW32_INTERLOCKED_LONG) 1) != 0)\n\t    {\n\t      while ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n                              (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t\t      (PTW32_INTERLOCKED_LONG) -1) != 0)\n\t        {\n\t          if (WAIT_OBJECT_0 != WaitForSingleObject (mx->event, INFINITE))\n\t            {\n\t              result = EINVAL;\n\t\t      break;\n\t            }\n\t        }\n\t    }\n        }\n      else\n        {\n          pthread_t self = pthread_self();\n\n          if ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG(\n                       (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t       (PTW32_INTERLOCKED_LONG) 1,\n\t\t       (PTW32_INTERLOCKED_LONG) 0) == 0)\n\t    {\n\t      mx->recursive_count = 1;\n\t      mx->ownerThread = self;\n\t    }\n          else\n\t    {\n\t      if (pthread_equal (mx->ownerThread, self))\n\t        {\n\t          if (kind == PTHREAD_MUTEX_RECURSIVE)\n\t\t    {\n\t\t      mx->recursive_count++;\n\t\t    }\n\t          else\n\t\t    {\n\t\t      result = EDEADLK;\n\t\t    }\n\t        }\n\t      else\n\t        {\n\t          while ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n                                  (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t\t          (PTW32_INTERLOCKED_LONG) -1) != 0)\n\t\t    {\n\t              if (WAIT_OBJECT_0 != WaitForSingleObject (mx->event, INFINITE))\n\t\t        {\n\t                  result = EINVAL;\n\t\t          break;\n\t\t        }\n\t\t    }\n\n\t          if (0 == result)\n\t\t    {\n\t\t      mx->recursive_count = 1;\n\t\t      mx->ownerThread = self;\n\t\t    }\n\t        }\n\t    }\n        }\n    }\n  else\n    {\n      /*\n       * Robust types\n       * All types record the current owner thread.\n       * The mutex is added to a per thread list when ownership is acquired.\n       */\n      ptw32_robust_state_t* statePtr = &mx->robustNode->stateInconsistent;\n\n      if ((PTW32_INTERLOCKED_LONG)PTW32_ROBUST_NOTRECOVERABLE == PTW32_INTERLOCKED_EXCHANGE_ADD_LONG(\n                                                 (PTW32_INTERLOCKED_LONGPTR)statePtr,\n                                                 (PTW32_INTERLOCKED_LONG)0))\n        {\n          result = ENOTRECOVERABLE;\n        }\n      else\n        {\n          pthread_t self = pthread_self();\n\n          kind = -kind - 1; /* Convert to non-robust range */\n    \n          if (PTHREAD_MUTEX_NORMAL == kind)\n            {\n              if ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n                           (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n                           (PTW32_INTERLOCKED_LONG) 1) != 0)\n                {\n                  while (0 == (result = ptw32_robust_mutex_inherit(mutex))\n                           && (PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n                                       (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n                                       (PTW32_INTERLOCKED_LONG) -1) != 0)\n                    {\n                      if (WAIT_OBJECT_0 != WaitForSingleObject (mx->event, INFINITE))\n                        {\n                          result = EINVAL;\n                          break;\n                        }\n                      if ((PTW32_INTERLOCKED_LONG)PTW32_ROBUST_NOTRECOVERABLE ==\n                                  PTW32_INTERLOCKED_EXCHANGE_ADD_LONG(\n                                    (PTW32_INTERLOCKED_LONGPTR)statePtr,\n                                    (PTW32_INTERLOCKED_LONG)0))\n                        {\n                          /* Unblock the next thread */\n                          SetEvent(mx->event);\n                          result = ENOTRECOVERABLE;\n                          break;\n                        }\n                    }\n                }\n              if (0 == result || EOWNERDEAD == result)\n                {\n                  /*\n                   * Add mutex to the per-thread robust mutex currently-held list.\n                   * If the thread terminates, all mutexes in this list will be unlocked.\n                   */\n                  ptw32_robust_mutex_add(mutex, self);\n                }\n            }\n          else\n            {\n              if ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG(\n                           (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n                           (PTW32_INTERLOCKED_LONG) 1,\n                           (PTW32_INTERLOCKED_LONG) 0) == 0)\n                {\n                  mx->recursive_count = 1;\n                  /*\n                   * Add mutex to the per-thread robust mutex currently-held list.\n                   * If the thread terminates, all mutexes in this list will be unlocked.\n                   */\n                  ptw32_robust_mutex_add(mutex, self);\n                }\n              else\n                {\n                  if (pthread_equal (mx->ownerThread, self))\n                    {\n                      if (PTHREAD_MUTEX_RECURSIVE == kind)\n                        {\n                          mx->recursive_count++;\n                        }\n                      else\n                        {\n                          result = EDEADLK;\n                        }\n                    }\n                  else\n                    {\n                      while (0 == (result = ptw32_robust_mutex_inherit(mutex))\n                               && (PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n                                           (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n                                           (PTW32_INTERLOCKED_LONG) -1) != 0)\n                        {\n                          if (WAIT_OBJECT_0 != WaitForSingleObject (mx->event, INFINITE))\n                            {\n                              result = EINVAL;\n                              break;\n                            }\n                          if ((PTW32_INTERLOCKED_LONG)PTW32_ROBUST_NOTRECOVERABLE ==\n                                      PTW32_INTERLOCKED_EXCHANGE_ADD_LONG(\n                                        (PTW32_INTERLOCKED_LONGPTR)statePtr,\n                                        (PTW32_INTERLOCKED_LONG)0))\n                            {\n                              /* Unblock the next thread */\n                              SetEvent(mx->event);\n                              result = ENOTRECOVERABLE;\n                              break;\n                            }\n                        }\n\n                      if (0 == result || EOWNERDEAD == result)\n                        {\n                          mx->recursive_count = 1;\n                          /*\n                           * Add mutex to the per-thread robust mutex currently-held list.\n                           * If the thread terminates, all mutexes in this list will be unlocked.\n                           */\n                          ptw32_robust_mutex_add(mutex, self);\n                        }\n                    }\n\t        }\n            }\n        }\n    }\n\n  return (result);\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutex_timedlock.c",
    "content": "/*\n * pthread_mutex_timedlock.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nstatic INLINE int\nptw32_timed_eventwait (HANDLE event, const struct timespec *abstime)\n     /*\n      * ------------------------------------------------------\n      * DESCRIPTION\n      *      This function waits on an event until signaled or until\n      *      abstime passes.\n      *      If abstime has passed when this routine is called then\n      *      it returns a result to indicate this.\n      *\n      *      If 'abstime' is a NULL pointer then this function will\n      *      block until it can successfully decrease the value or\n      *      until interrupted by a signal.\n      *\n      *      This routine is not a cancelation point.\n      *\n      * RESULTS\n      *              0               successfully signaled,\n      *              ETIMEDOUT       abstime passed\n      *              EINVAL          'event' is not a valid event,\n      *\n      * ------------------------------------------------------\n      */\n{\n\n  DWORD milliseconds;\n  DWORD status;\n\n  if (event == NULL)\n    {\n      return EINVAL;\n    }\n  else\n    {\n      if (abstime == NULL)\n\t{\n\t  milliseconds = INFINITE;\n\t}\n      else\n\t{\n\t  /* \n\t   * Calculate timeout as milliseconds from current system time. \n\t   */\n\t  milliseconds = ptw32_relmillisecs (abstime);\n\t}\n\n      status = WaitForSingleObject (event, milliseconds);\n\n      if (status == WAIT_OBJECT_0)\n\t{\n\t  return 0;\n\t}\n      else if (status == WAIT_TIMEOUT)\n\t{\n\t  return ETIMEDOUT;\n\t}\n      else\n\t{\n\t  return EINVAL;\n\t}\n    }\n\n  return 0;\n\n}\t\t\t\t/* ptw32_timed_semwait */\n\n\nint\npthread_mutex_timedlock (pthread_mutex_t * mutex,\n\t\t\t const struct timespec *abstime)\n{\n  pthread_mutex_t mx;\n  int kind;\n  int result = 0;\n\n  /*\n   * Let the system deal with invalid pointers.\n   */\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static mutex. We check\n   * again inside the guarded section of ptw32_mutex_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)\n    {\n      if ((result = ptw32_mutex_check_need_init (mutex)) != 0)\n\t{\n\t  return (result);\n\t}\n    }\n\n  mx = *mutex;\n  kind = mx->kind;\n\n  if (kind >= 0)\n    {\n      if (mx->kind == PTHREAD_MUTEX_NORMAL)\n        {\n          if ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n\t\t       (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t       (PTW32_INTERLOCKED_LONG) 1) != 0)\n\t    {\n              while ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n                              (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t\t      (PTW32_INTERLOCKED_LONG) -1) != 0)\n                {\n\t          if (0 != (result = ptw32_timed_eventwait (mx->event, abstime)))\n\t\t    {\n\t\t      return result;\n\t\t    }\n\t        }\n\t    }\n        }\n      else\n        {\n          pthread_t self = pthread_self();\n\n          if ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG(\n                       (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t       (PTW32_INTERLOCKED_LONG) 1,\n\t\t       (PTW32_INTERLOCKED_LONG) 0) == 0)\n\t    {\n\t      mx->recursive_count = 1;\n\t      mx->ownerThread = self;\n\t    }\n          else\n\t    {\n\t      if (pthread_equal (mx->ownerThread, self))\n\t        {\n\t          if (mx->kind == PTHREAD_MUTEX_RECURSIVE)\n\t\t    {\n\t\t      mx->recursive_count++;\n\t\t    }\n\t          else\n\t\t    {\n\t\t      return EDEADLK;\n\t\t    }\n\t        }\n\t      else\n\t        {\n                  while ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n                                  (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t\t          (PTW32_INTERLOCKED_LONG) -1) != 0)\n                    {\n\t\t      if (0 != (result = ptw32_timed_eventwait (mx->event, abstime)))\n\t\t        {\n\t\t          return result;\n\t\t        }\n\t\t    }\n\n\t          mx->recursive_count = 1;\n\t          mx->ownerThread = self;\n\t        }\n\t    }\n        }\n    }\n  else\n    {\n      /*\n       * Robust types\n       * All types record the current owner thread.\n       * The mutex is added to a per thread list when ownership is acquired.\n       */\n      ptw32_robust_state_t* statePtr = &mx->robustNode->stateInconsistent;\n\n      if ((PTW32_INTERLOCKED_LONG)PTW32_ROBUST_NOTRECOVERABLE == PTW32_INTERLOCKED_EXCHANGE_ADD_LONG(\n                                                 (PTW32_INTERLOCKED_LONGPTR)statePtr,\n                                                 (PTW32_INTERLOCKED_LONG)0))\n        {\n          result = ENOTRECOVERABLE;\n        }\n      else\n        {\n          pthread_t self = pthread_self();\n\n          kind = -kind - 1; /* Convert to non-robust range */\n\n          if (PTHREAD_MUTEX_NORMAL == kind)\n            {\n              if ((PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n\t\t           (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t           (PTW32_INTERLOCKED_LONG) 1) != 0)\n\t        {\n                  while (0 == (result = ptw32_robust_mutex_inherit(mutex))\n                           && (PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n                                  (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t\t          (PTW32_INTERLOCKED_LONG) -1) != 0)\n                    {\n\t              if (0 != (result = ptw32_timed_eventwait (mx->event, abstime)))\n\t\t        {\n\t\t          return result;\n\t\t        }\n                      if ((PTW32_INTERLOCKED_LONG)PTW32_ROBUST_NOTRECOVERABLE ==\n                                  PTW32_INTERLOCKED_EXCHANGE_ADD_LONG(\n                                    (PTW32_INTERLOCKED_LONGPTR)statePtr,\n                                    (PTW32_INTERLOCKED_LONG)0))\n                        {\n                          /* Unblock the next thread */\n                          SetEvent(mx->event);\n                          result = ENOTRECOVERABLE;\n                          break;\n                        }\n\t            }\n\n                  if (0 == result || EOWNERDEAD == result)\n                    {\n                      /*\n                       * Add mutex to the per-thread robust mutex currently-held list.\n                       * If the thread terminates, all mutexes in this list will be unlocked.\n                       */\n                      ptw32_robust_mutex_add(mutex, self);\n                    }\n\t        }\n            }\n          else\n            {\n              pthread_t self = pthread_self();\n\n              if (0 == (PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG(\n                           (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t           (PTW32_INTERLOCKED_LONG) 1,\n\t\t           (PTW32_INTERLOCKED_LONG) 0))\n\t        {\n\t          mx->recursive_count = 1;\n                  /*\n                   * Add mutex to the per-thread robust mutex currently-held list.\n                   * If the thread terminates, all mutexes in this list will be unlocked.\n                   */\n                  ptw32_robust_mutex_add(mutex, self);\n\t        }\n              else\n\t        {\n\t          if (pthread_equal (mx->ownerThread, self))\n\t            {\n\t              if (PTHREAD_MUTEX_RECURSIVE == kind)\n\t\t        {\n\t\t          mx->recursive_count++;\n\t\t        }\n\t              else\n\t\t        {\n\t\t          return EDEADLK;\n\t\t        }\n\t            }\n\t          else\n\t            {\n                      while (0 == (result = ptw32_robust_mutex_inherit(mutex))\n                               && (PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_EXCHANGE_LONG(\n                                          (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t\t                  (PTW32_INTERLOCKED_LONG) -1) != 0)\n                        {\n\t\t          if (0 != (result = ptw32_timed_eventwait (mx->event, abstime)))\n\t\t            {\n\t\t              return result;\n\t\t            }\n\t\t        }\n\n                      if ((PTW32_INTERLOCKED_LONG)PTW32_ROBUST_NOTRECOVERABLE ==\n                                  PTW32_INTERLOCKED_EXCHANGE_ADD_LONG(\n                                    (PTW32_INTERLOCKED_LONGPTR)statePtr,\n                                    (PTW32_INTERLOCKED_LONG)0))\n                        {\n                          /* Unblock the next thread */\n                          SetEvent(mx->event);\n                          result = ENOTRECOVERABLE;\n                        }\n                      else if (0 == result || EOWNERDEAD == result)\n                        {\n                          mx->recursive_count = 1;\n                          /*\n                           * Add mutex to the per-thread robust mutex currently-held list.\n                           * If the thread terminates, all mutexes in this list will be unlocked.\n                           */\n                          ptw32_robust_mutex_add(mutex, self);\n                        }\n\t            }\n\t        }\n            }\n        }\n    }\n\n  return result;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutex_trylock.c",
    "content": "/*\n * pthread_mutex_trylock.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutex_trylock (pthread_mutex_t * mutex)\n{\n  pthread_mutex_t mx;\n  int kind;\n  int result = 0;\n\n  /*\n   * Let the system deal with invalid pointers.\n   */\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static mutex. We check\n   * again inside the guarded section of ptw32_mutex_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*mutex >= PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)\n    {\n      if ((result = ptw32_mutex_check_need_init (mutex)) != 0)\n\t{\n\t  return (result);\n\t}\n    }\n\n  mx = *mutex;\n  kind = mx->kind;\n\n  if (kind >= 0)\n    {\n      /* Non-robust */\n      if (0 == (PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG (\n\t\t         (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n\t\t         (PTW32_INTERLOCKED_LONG) 1,\n\t\t         (PTW32_INTERLOCKED_LONG) 0))\n        {\n          if (kind != PTHREAD_MUTEX_NORMAL)\n\t    {\n\t      mx->recursive_count = 1;\n\t      mx->ownerThread = pthread_self ();\n\t    }\n        }\n      else\n        {\n          if (kind == PTHREAD_MUTEX_RECURSIVE &&\n\t      pthread_equal (mx->ownerThread, pthread_self ()))\n\t    {\n\t      mx->recursive_count++;\n\t    }\n          else\n\t    {\n\t      result = EBUSY;\n\t    }\n        }\n    }\n  else\n    {\n      /*\n       * Robust types\n       * All types record the current owner thread.\n       * The mutex is added to a per thread list when ownership is acquired.\n       */\n      pthread_t self;\n      ptw32_robust_state_t* statePtr = &mx->robustNode->stateInconsistent;\n\n      if ((PTW32_INTERLOCKED_LONG)PTW32_ROBUST_NOTRECOVERABLE ==\n                  PTW32_INTERLOCKED_EXCHANGE_ADD_LONG(\n                    (PTW32_INTERLOCKED_LONGPTR)statePtr,\n                    (PTW32_INTERLOCKED_LONG)0))\n        {\n          return ENOTRECOVERABLE;\n        }\n\n      self = pthread_self();\n      kind = -kind - 1; /* Convert to non-robust range */\n\n      if (0 == (PTW32_INTERLOCKED_LONG) PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG (\n        \t         (PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n        \t         (PTW32_INTERLOCKED_LONG) 1,\n        \t         (PTW32_INTERLOCKED_LONG) 0))\n        {\n          if (kind != PTHREAD_MUTEX_NORMAL)\n            {\n              mx->recursive_count = 1;\n            }\n          ptw32_robust_mutex_add(mutex, self);\n        }\n      else\n        {\n          if (PTHREAD_MUTEX_RECURSIVE == kind &&\n              pthread_equal (mx->ownerThread, pthread_self ()))\n            {\n              mx->recursive_count++;\n            }\n          else\n            {\n              if (EOWNERDEAD == (result = ptw32_robust_mutex_inherit(mutex)))\n                {\n                  mx->recursive_count = 1;\n                  ptw32_robust_mutex_add(mutex, self);\n                }\n              else\n                {\n                  if (0 == result)\n                    { \n\t              result = EBUSY;\n                    }\n                }\n\t    }\n        }\n    }\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutex_unlock.c",
    "content": "/*\n * pthread_mutex_unlock.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutex_unlock (pthread_mutex_t * mutex)\n{\n  int result = 0;\n  int kind;\n  pthread_mutex_t mx;\n\n  /*\n   * Let the system deal with invalid pointers.\n   */\n\n  mx = *mutex;\n\n  /*\n   * If the thread calling us holds the mutex then there is no\n   * race condition. If another thread holds the\n   * lock then we shouldn't be in here.\n   */\n  if (mx < PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)\n    {\n      kind = mx->kind;\n\n      if (kind >= 0)\n        {\n          if (kind == PTHREAD_MUTEX_NORMAL)\n\t    {\n\t      LONG idx;\n\n\t      idx = (LONG) PTW32_INTERLOCKED_EXCHANGE_LONG ((PTW32_INTERLOCKED_LONGPTR)&mx->lock_idx,\n\t\t\t\t\t\t\t    (PTW32_INTERLOCKED_LONG)0);\n\t      if (idx != 0)\n\t        {\n\t          if (idx < 0)\n\t\t    {\n\t\t      /*\n\t\t       * Someone may be waiting on that mutex.\n\t\t       */\n\t\t      if (SetEvent (mx->event) == 0)\n\t\t        {\n\t\t          result = EINVAL;\n\t\t        }\n\t\t    }\n\t        }\n\t    }\n          else\n\t    {\n\t      if (pthread_equal (mx->ownerThread, pthread_self()))\n\t        {\n\t          if (kind != PTHREAD_MUTEX_RECURSIVE\n\t\t      || 0 == --mx->recursive_count)\n\t\t    {\n\t\t      mx->ownerThread.p = NULL;\n\n\t\t      if ((LONG) PTW32_INTERLOCKED_EXCHANGE_LONG ((PTW32_INTERLOCKED_LONGPTR)&mx->lock_idx,\n\t\t\t\t\t\t\t          (PTW32_INTERLOCKED_LONG)0) < 0L)\n\t\t        {\n\t\t          /* Someone may be waiting on that mutex */\n\t\t          if (SetEvent (mx->event) == 0)\n\t\t\t    {\n\t\t\t      result = EINVAL;\n\t\t\t    }\n\t\t        }\n\t\t    }\n\t        }\n\t      else\n\t        {\n\t          result = EPERM;\n\t        }\n\t    }\n        }\n      else\n        {\n          /* Robust types */\n          pthread_t self = pthread_self();\n          kind = -kind - 1; /* Convert to non-robust range */\n\n          /*\n           * The thread must own the lock regardless of type if the mutex\n           * is robust.\n           */\n          if (pthread_equal (mx->ownerThread, self))\n            {\n              PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG((PTW32_INTERLOCKED_LONGPTR) &mx->robustNode->stateInconsistent,\n                                                      (PTW32_INTERLOCKED_LONG)PTW32_ROBUST_NOTRECOVERABLE,\n                                                      (PTW32_INTERLOCKED_LONG)PTW32_ROBUST_INCONSISTENT);\n              if (PTHREAD_MUTEX_NORMAL == kind)\n                {\n                  ptw32_robust_mutex_remove(mutex, NULL);\n\n                  if ((LONG) PTW32_INTERLOCKED_EXCHANGE_LONG((PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n                                                             (PTW32_INTERLOCKED_LONG) 0) < 0)\n                    {\n                      /*\n                       * Someone may be waiting on that mutex.\n                       */\n                      if (SetEvent (mx->event) == 0)\n                        {\n                          result = EINVAL;\n                        }\n                    }\n                }\n              else\n                {\n                  if (kind != PTHREAD_MUTEX_RECURSIVE\n                      || 0 == --mx->recursive_count)\n                    {\n                      ptw32_robust_mutex_remove(mutex, NULL);\n\n                      if ((LONG) PTW32_INTERLOCKED_EXCHANGE_LONG((PTW32_INTERLOCKED_LONGPTR) &mx->lock_idx,\n                                                                 (PTW32_INTERLOCKED_LONG) 0) < 0)\n                        {\n                          /*\n                           * Someone may be waiting on that mutex.\n                           */\n                          if (SetEvent (mx->event) == 0)\n                            {\n                              result = EINVAL;\n                            }\n                        }\n                    }\n                }\n            }\n          else\n            {\n              result = EPERM;\n            }\n        }\n    }\n  else if (mx != PTHREAD_MUTEX_INITIALIZER)\n    {\n      result = EINVAL;\n    }\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutexattr_destroy.c",
    "content": "/*\n * pthread_mutexattr_destroy.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutexattr_destroy (pthread_mutexattr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Destroys a mutex attributes object. The object can\n      *      no longer be used.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_mutexattr_t\n      *\n      *\n      * DESCRIPTION\n      *      Destroys a mutex attributes object. The object can\n      *      no longer be used.\n      *\n      *      NOTES:\n      *              1)      Does not affect mutexes created using 'attr'\n      *\n      * RESULTS\n      *              0               successfully released attr,\n      *              EINVAL          'attr' is invalid.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n\n  if (attr == NULL || *attr == NULL)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      pthread_mutexattr_t ma = *attr;\n\n      *attr = NULL;\n      free (ma);\n    }\n\n  return (result);\n}\t\t\t\t/* pthread_mutexattr_destroy */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutexattr_getkind_np.c",
    "content": "/*\n * pthread_mutexattr_getkind_np.c\n *\n * Description:\n * This translation unit implements non-portable thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_mutexattr_getkind_np (pthread_mutexattr_t * attr, int *kind)\n{\n  return pthread_mutexattr_gettype (attr, kind);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutexattr_getpshared.c",
    "content": "/*\n * pthread_mutexattr_getpshared.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutexattr_getpshared (const pthread_mutexattr_t * attr, int *pshared)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Determine whether mutexes created with 'attr' can be\n      *      shared between processes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_mutexattr_t\n      *\n      *      pshared\n      *              will be set to one of:\n      *\n      *                      PTHREAD_PROCESS_SHARED\n      *                              May be shared if in shared memory\n      *\n      *                      PTHREAD_PROCESS_PRIVATE\n      *                              Cannot be shared.\n      *\n      *\n      * DESCRIPTION\n      *      Mutexes creatd with 'attr' can be shared between\n      *      processes if pthread_mutex_t variable is allocated\n      *      in memory shared by these processes.\n      *      NOTES:\n      *              1)      pshared mutexes MUST be allocated in shared\n      *                      memory.\n      *              2)      The following macro is defined if shared mutexes\n      *                      are supported:\n      *                              _POSIX_THREAD_PROCESS_SHARED\n      *\n      * RESULTS\n      *              0               successfully retrieved attribute,\n      *              EINVAL          'attr' is invalid,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n\n  if ((attr != NULL && *attr != NULL) && (pshared != NULL))\n    {\n      *pshared = (*attr)->pshared;\n      result = 0;\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return (result);\n\n}\t\t\t\t/* pthread_mutexattr_getpshared */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutexattr_getrobust.c",
    "content": "/*\n * pthread_mutexattr_getrobust.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutexattr_getrobust (const pthread_mutexattr_t * attr, int * robust)\n     /*\n      * ------------------------------------------------------\n      *\n      * DOCPUBLIC\n      * The pthread_mutexattr_setrobust() and\n      * pthread_mutexattr_getrobust() functions  respectively set and\n      * get the mutex robust  attribute. This attribute is set in  the\n      * robust parameter to these functions.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_mutexattr_t\n      *\n      *     robust \n      *              must be one of:\n      *\n      *                      PTHREAD_MUTEX_STALLED\n      *\n      *                      PTHREAD_MUTEX_ROBUST\n      *\n      * DESCRIPTION\n      * The pthread_mutexattr_setrobust() and\n      * pthread_mutexattr_getrobust() functions  respectively set and\n      * get the mutex robust  attribute. This attribute is set in  the\n      * robust  parameter to these functions. The default value of the\n      * robust  attribute is  PTHREAD_MUTEX_STALLED.\n      * \n      * The robustness of mutex is contained in the robustness attribute\n      * of the mutex attributes. Valid mutex robustness values are:\n      *\n      * PTHREAD_MUTEX_STALLED\n      * No special actions are taken if the owner of the mutex is\n      * terminated while holding the mutex lock. This can lead to\n      * deadlocks if no other thread can unlock the mutex.\n      * This is the default value.\n      * \n      * PTHREAD_MUTEX_ROBUST\n      * If the process containing the owning thread of a robust mutex\n      * terminates while holding the mutex lock, the next thread that\n      * acquires the mutex shall be notified about the termination by\n      * the return value [EOWNERDEAD] from the locking function. If the\n      * owning thread of a robust mutex terminates while holding the mutex\n      * lock, the next thread that acquires the mutex may be notified\n      * about the termination by the return value [EOWNERDEAD]. The\n      * notified thread can then attempt to mark the state protected by\n      * the mutex as consistent again by a call to\n      * pthread_mutex_consistent(). After a subsequent successful call to\n      * pthread_mutex_unlock(), the mutex lock shall be released and can\n      * be used normally by other threads. If the mutex is unlocked without\n      * a call to pthread_mutex_consistent(), it shall be in a permanently\n      * unusable state and all attempts to lock the mutex shall fail with\n      * the error [ENOTRECOVERABLE]. The only permissible operation on such\n      * a mutex is pthread_mutex_destroy().\n      *\n      * RESULTS\n      *              0               successfully set attribute,\n      *              EINVAL          'attr' or 'robust' is invalid,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = EINVAL;\n\n  if ((attr != NULL && *attr != NULL && robust != NULL))\n    {\n      *robust = (*attr)->robustness;\n      result = 0;\n    }\n\n  return (result);\n}\t\t\t\t/* pthread_mutexattr_getrobust */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutexattr_gettype.c",
    "content": "/*\n * pthread_mutexattr_gettype.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutexattr_gettype (const pthread_mutexattr_t * attr, int *kind)\n{\n  int result = 0;\n\n  if (attr != NULL && *attr != NULL && kind != NULL)\n    {\n      *kind = (*attr)->kind;\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutexattr_init.c",
    "content": "/*\n * pthread_mutexattr_init.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutexattr_init (pthread_mutexattr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Initializes a mutex attributes object with default\n      *      attributes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_mutexattr_t\n      *\n      *\n      * DESCRIPTION\n      *      Initializes a mutex attributes object with default\n      *      attributes.\n      *\n      *      NOTES:\n      *              1)      Used to define mutex types\n      *\n      * RESULTS\n      *              0               successfully initialized attr,\n      *              ENOMEM          insufficient memory for attr.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  pthread_mutexattr_t ma;\n\n  ma = (pthread_mutexattr_t) calloc (1, sizeof (*ma));\n\n  if (ma == NULL)\n    {\n      result = ENOMEM;\n    }\n  else\n    {\n      ma->pshared = PTHREAD_PROCESS_PRIVATE;\n      ma->kind = PTHREAD_MUTEX_DEFAULT;\n    }\n\n  *attr = ma;\n\n  return (result);\n}\t\t\t\t/* pthread_mutexattr_init */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutexattr_setkind_np.c",
    "content": "/*\n * pthread_mutexattr_setkind_np.c\n *\n * Description:\n * This translation unit implements non-portable thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_mutexattr_setkind_np (pthread_mutexattr_t * attr, int kind)\n{\n  return pthread_mutexattr_settype (attr, kind);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutexattr_setpshared.c",
    "content": "/*\n * pthread_mutexattr_setpshared.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutexattr_setpshared (pthread_mutexattr_t * attr, int pshared)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Mutexes created with 'attr' can be shared between\n      *      processes if pthread_mutex_t variable is allocated\n      *      in memory shared by these processes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_mutexattr_t\n      *\n      *      pshared\n      *              must be one of:\n      *\n      *                      PTHREAD_PROCESS_SHARED\n      *                              May be shared if in shared memory\n      *\n      *                      PTHREAD_PROCESS_PRIVATE\n      *                              Cannot be shared.\n      *\n      * DESCRIPTION\n      *      Mutexes creatd with 'attr' can be shared between\n      *      processes if pthread_mutex_t variable is allocated\n      *      in memory shared by these processes.\n      *\n      *      NOTES:\n      *              1)      pshared mutexes MUST be allocated in shared\n      *                      memory.\n      *\n      *              2)      The following macro is defined if shared mutexes\n      *                      are supported:\n      *                              _POSIX_THREAD_PROCESS_SHARED\n      *\n      * RESULTS\n      *              0               successfully set attribute,\n      *              EINVAL          'attr' or pshared is invalid,\n      *              ENOSYS          PTHREAD_PROCESS_SHARED not supported,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n\n  if ((attr != NULL && *attr != NULL) &&\n      ((pshared == PTHREAD_PROCESS_SHARED) ||\n       (pshared == PTHREAD_PROCESS_PRIVATE)))\n    {\n      if (pshared == PTHREAD_PROCESS_SHARED)\n\t{\n\n#if !defined( _POSIX_THREAD_PROCESS_SHARED )\n\n\t  result = ENOSYS;\n\t  pshared = PTHREAD_PROCESS_PRIVATE;\n\n#else\n\n\t  result = 0;\n\n#endif /* _POSIX_THREAD_PROCESS_SHARED */\n\n\t}\n      else\n\t{\n\t  result = 0;\n\t}\n\n      (*attr)->pshared = pshared;\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return (result);\n\n}\t\t\t\t/* pthread_mutexattr_setpshared */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutexattr_setrobust.c",
    "content": "/*\n * pthread_mutexattr_setrobust.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutexattr_setrobust (pthread_mutexattr_t * attr, int robust)\n     /*\n      * ------------------------------------------------------\n      *\n      * DOCPUBLIC\n      * The pthread_mutexattr_setrobust() and\n      * pthread_mutexattr_getrobust() functions  respectively set and\n      * get the mutex robust  attribute. This attribute is set in  the\n      * robust parameter to these functions.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_mutexattr_t\n      *\n      *     robust \n      *              must be one of:\n      *\n      *                      PTHREAD_MUTEX_STALLED\n      *\n      *                      PTHREAD_MUTEX_ROBUST\n      *\n      * DESCRIPTION\n      * The pthread_mutexattr_setrobust() and\n      * pthread_mutexattr_getrobust() functions  respectively set and\n      * get the mutex robust  attribute. This attribute is set in  the\n      * robust  parameter to these functions. The default value of the\n      * robust  attribute is  PTHREAD_MUTEX_STALLED.\n      * \n      * The robustness of mutex is contained in the robustness attribute\n      * of the mutex attributes. Valid mutex robustness values are:\n      *\n      * PTHREAD_MUTEX_STALLED\n      * No special actions are taken if the owner of the mutex is\n      * terminated while holding the mutex lock. This can lead to\n      * deadlocks if no other thread can unlock the mutex.\n      * This is the default value.\n      * \n      * PTHREAD_MUTEX_ROBUST\n      * If the process containing the owning thread of a robust mutex\n      * terminates while holding the mutex lock, the next thread that\n      * acquires the mutex shall be notified about the termination by\n      * the return value [EOWNERDEAD] from the locking function. If the\n      * owning thread of a robust mutex terminates while holding the mutex\n      * lock, the next thread that acquires the mutex may be notified\n      * about the termination by the return value [EOWNERDEAD]. The\n      * notified thread can then attempt to mark the state protected by\n      * the mutex as consistent again by a call to\n      * pthread_mutex_consistent(). After a subsequent successful call to\n      * pthread_mutex_unlock(), the mutex lock shall be released and can\n      * be used normally by other threads. If the mutex is unlocked without\n      * a call to pthread_mutex_consistent(), it shall be in a permanently\n      * unusable state and all attempts to lock the mutex shall fail with\n      * the error [ENOTRECOVERABLE]. The only permissible operation on such\n      * a mutex is pthread_mutex_destroy().\n      *\n      * RESULTS\n      *              0               successfully set attribute,\n      *              EINVAL          'attr' or 'robust' is invalid,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = EINVAL;\n\n  if ((attr != NULL && *attr != NULL))\n    {\n      switch (robust)\n        {\n          case PTHREAD_MUTEX_STALLED:\n          case PTHREAD_MUTEX_ROBUST:\n\t    (*attr)->robustness = robust;\n            result = 0;\n            break;\n        }\n    }\n\n  return (result);\n}\t\t\t\t/* pthread_mutexattr_setrobust */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_mutexattr_settype.c",
    "content": "/*\n * pthread_mutexattr_settype.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind)\n     /*\n      * ------------------------------------------------------\n      *\n      * DOCPUBLIC\n      * The pthread_mutexattr_settype() and\n      * pthread_mutexattr_gettype() functions  respectively set and\n      * get the mutex type  attribute. This attribute is set in  the\n      * type parameter to these functions.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_mutexattr_t\n      *\n      *      type\n      *              must be one of:\n      *\n      *                      PTHREAD_MUTEX_DEFAULT\n      *\n      *                      PTHREAD_MUTEX_NORMAL\n      *\n      *                      PTHREAD_MUTEX_ERRORCHECK\n      *\n      *                      PTHREAD_MUTEX_RECURSIVE\n      *\n      * DESCRIPTION\n      * The pthread_mutexattr_settype() and\n      * pthread_mutexattr_gettype() functions  respectively set and\n      * get the mutex type  attribute. This attribute is set in  the\n      * type  parameter to these functions. The default value of the\n      * type  attribute is  PTHREAD_MUTEX_DEFAULT.\n      * \n      * The type of mutex is contained in the type  attribute of the\n      * mutex attributes. Valid mutex types include:\n      *\n      * PTHREAD_MUTEX_NORMAL\n      *          This type of mutex does  not  detect  deadlock.  A\n      *          thread  attempting  to  relock  this mutex without\n      *          first unlocking it will  deadlock.  Attempting  to\n      *          unlock  a  mutex  locked  by  a  different  thread\n      *          results  in  undefined  behavior.  Attempting   to\n      *          unlock  an  unlocked  mutex  results  in undefined\n      *          behavior.\n      * \n      * PTHREAD_MUTEX_ERRORCHECK\n      *          This type of  mutex  provides  error  checking.  A\n      *          thread  attempting  to  relock  this mutex without\n      *          first unlocking it will return with  an  error.  A\n      *          thread  attempting to unlock a mutex which another\n      *          thread has locked will return  with  an  error.  A\n      *          thread attempting to unlock an unlocked mutex will\n      *          return with an error.\n      *\n      * PTHREAD_MUTEX_DEFAULT\n      *          Same as PTHREAD_MUTEX_NORMAL.\n      * \n      * PTHREAD_MUTEX_RECURSIVE\n      *          A thread attempting to relock this  mutex  without\n      *          first  unlocking  it  will  succeed in locking the\n      *          mutex. The relocking deadlock which can occur with\n      *          mutexes of type  PTHREAD_MUTEX_NORMAL cannot occur\n      *          with this type of mutex. Multiple  locks  of  this\n      *          mutex  require  the  same  number  of  unlocks  to\n      *          release  the  mutex  before  another  thread   can\n      *          acquire the mutex. A thread attempting to unlock a\n      *          mutex which another thread has locked will  return\n      *          with  an  error. A thread attempting to  unlock an\n      *          unlocked mutex will return  with  an  error.  This\n      *          type  of mutex is only supported for mutexes whose\n      *          process        shared         attribute         is\n      *          PTHREAD_PROCESS_PRIVATE.\n      *\n      * RESULTS\n      *              0               successfully set attribute,\n      *              EINVAL          'attr' or 'type' is invalid,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n\n  if ((attr != NULL && *attr != NULL))\n    {\n      switch (kind)\n\t{\n\tcase PTHREAD_MUTEX_FAST_NP:\n\tcase PTHREAD_MUTEX_RECURSIVE_NP:\n\tcase PTHREAD_MUTEX_ERRORCHECK_NP:\n\t  (*attr)->kind = kind;\n\t  break;\n\tdefault:\n\t  result = EINVAL;\n\t  break;\n\t}\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return (result);\n}\t\t\t\t/* pthread_mutexattr_settype */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_num_processors_np.c",
    "content": "/*\n * pthread_num_processors_np.c\n *\n * Description:\n * This translation unit implements non-portable thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/*\n * pthread_num_processors_np()\n *\n * Get the number of CPUs available to the process.\n */\nint\npthread_num_processors_np (void)\n{\n  int count;\n\n  if (ptw32_getprocessors (&count) != 0)\n    {\n      count = 1;\n    }\n\n  return (count);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_once.c",
    "content": "/*\n * pthread_once.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_once (pthread_once_t * once_control, void (PTW32_CDECL *init_routine) (void))\n{\n  if (once_control == NULL || init_routine == NULL)\n    {\n      return EINVAL;\n    }\n  \n  if ((PTW32_INTERLOCKED_LONG)PTW32_FALSE ==\n      (PTW32_INTERLOCKED_LONG)PTW32_INTERLOCKED_EXCHANGE_ADD_LONG((PTW32_INTERLOCKED_LONGPTR)&once_control->done,\n                                                                  (PTW32_INTERLOCKED_LONG)0)) /* MBR fence */\n    {\n      ptw32_mcs_local_node_t node;\n\n      ptw32_mcs_lock_acquire((ptw32_mcs_lock_t *)&once_control->lock, &node);\n\n      if (!once_control->done)\n\t{\n\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth(0)\n#endif\n\n\t  pthread_cleanup_push(ptw32_mcs_lock_release, &node);\n\t  (*init_routine)();\n\t  pthread_cleanup_pop(0);\n\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth()\n#endif\n\n\t  once_control->done = PTW32_TRUE;\n\t}\n\n\tptw32_mcs_lock_release(&node);\n    }\n\n  return 0;\n\n}\t\t\t\t/* pthread_once */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlock_destroy.c",
    "content": "/*\n * pthread_rwlock_destroy.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlock_destroy (pthread_rwlock_t * rwlock)\n{\n  pthread_rwlock_t rwl;\n  int result = 0, result1 = 0, result2 = 0;\n\n  if (rwlock == NULL || *rwlock == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (*rwlock != PTHREAD_RWLOCK_INITIALIZER)\n    {\n      rwl = *rwlock;\n\n      if (rwl->nMagic != PTW32_RWLOCK_MAGIC)\n\t{\n\t  return EINVAL;\n\t}\n\n      if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0)\n\t{\n\t  return result;\n\t}\n\n      if ((result =\n\t   pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)\n\t{\n\t  (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n\t  return result;\n\t}\n\n      /*\n       * Check whether any threads own/wait for the lock (wait for ex.access);\n       * report \"BUSY\" if so.\n       */\n      if (rwl->nExclusiveAccessCount > 0\n\t  || rwl->nSharedAccessCount > rwl->nCompletedSharedAccessCount)\n\t{\n\t  result = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted));\n\t  result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n\t  result2 = EBUSY;\n\t}\n      else\n\t{\n\t  rwl->nMagic = 0;\n\n\t  if ((result =\n\t       pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)\n\t    {\n\t      pthread_mutex_unlock (&rwl->mtxExclusiveAccess);\n\t      return result;\n\t    }\n\n\t  if ((result =\n\t       pthread_mutex_unlock (&(rwl->mtxExclusiveAccess))) != 0)\n\t    {\n\t      return result;\n\t    }\n\n\t  *rwlock = NULL;\t/* Invalidate rwlock before anything else */\n\t  result = pthread_cond_destroy (&(rwl->cndSharedAccessCompleted));\n\t  result1 = pthread_mutex_destroy (&(rwl->mtxSharedAccessCompleted));\n\t  result2 = pthread_mutex_destroy (&(rwl->mtxExclusiveAccess));\n\t  (void) free (rwl);\n\t}\n    }\n  else\n    {\n      ptw32_mcs_local_node_t node;\n      /*\n       * See notes in ptw32_rwlock_check_need_init() above also.\n       */\n      ptw32_mcs_lock_acquire(&ptw32_rwlock_test_init_lock, &node);\n\n      /*\n       * Check again.\n       */\n      if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)\n\t{\n\t  /*\n\t   * This is all we need to do to destroy a statically\n\t   * initialised rwlock that has not yet been used (initialised).\n\t   * If we get to here, another thread\n\t   * waiting to initialise this rwlock will get an EINVAL.\n\t   */\n\t  *rwlock = NULL;\n\t}\n      else\n\t{\n\t  /*\n\t   * The rwlock has been initialised while we were waiting\n\t   * so assume it's in use.\n\t   */\n\t  result = EBUSY;\n\t}\n\n      ptw32_mcs_lock_release(&node);\n    }\n\n  return ((result != 0) ? result : ((result1 != 0) ? result1 : result2));\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlock_init.c",
    "content": "/*\n * pthread_rwlock_init.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlock_init (pthread_rwlock_t * rwlock,\n\t\t     const pthread_rwlockattr_t * attr)\n{\n  int result;\n  pthread_rwlock_t rwl = 0;\n\n  if (rwlock == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (attr != NULL && *attr != NULL)\n    {\n      result = EINVAL;\t\t/* Not supported */\n      goto DONE;\n    }\n\n  rwl = (pthread_rwlock_t) calloc (1, sizeof (*rwl));\n\n  if (rwl == NULL)\n    {\n      result = ENOMEM;\n      goto DONE;\n    }\n\n  rwl->nSharedAccessCount = 0;\n  rwl->nExclusiveAccessCount = 0;\n  rwl->nCompletedSharedAccessCount = 0;\n\n  result = pthread_mutex_init (&rwl->mtxExclusiveAccess, NULL);\n  if (result != 0)\n    {\n      goto FAIL0;\n    }\n\n  result = pthread_mutex_init (&rwl->mtxSharedAccessCompleted, NULL);\n  if (result != 0)\n    {\n      goto FAIL1;\n    }\n\n  result = pthread_cond_init (&rwl->cndSharedAccessCompleted, NULL);\n  if (result != 0)\n    {\n      goto FAIL2;\n    }\n\n  rwl->nMagic = PTW32_RWLOCK_MAGIC;\n\n  result = 0;\n  goto DONE;\n\nFAIL2:\n  (void) pthread_mutex_destroy (&(rwl->mtxSharedAccessCompleted));\n\nFAIL1:\n  (void) pthread_mutex_destroy (&(rwl->mtxExclusiveAccess));\n\nFAIL0:\n  (void) free (rwl);\n  rwl = NULL;\n\nDONE:\n  *rwlock = rwl;\n\n  return result;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlock_rdlock.c",
    "content": "/*\n * pthread_rwlock_rdlock.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlock_rdlock (pthread_rwlock_t * rwlock)\n{\n  int result;\n  pthread_rwlock_t rwl;\n\n  if (rwlock == NULL || *rwlock == NULL)\n    {\n      return EINVAL;\n    }\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static rwlock. We check\n   * again inside the guarded section of ptw32_rwlock_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)\n    {\n      result = ptw32_rwlock_check_need_init (rwlock);\n\n      if (result != 0 && result != EBUSY)\n\t{\n\t  return result;\n\t}\n    }\n\n  rwl = *rwlock;\n\n  if (rwl->nMagic != PTW32_RWLOCK_MAGIC)\n    {\n      return EINVAL;\n    }\n\n  if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0)\n    {\n      return result;\n    }\n\n  if (++rwl->nSharedAccessCount == INT_MAX)\n    {\n      if ((result =\n\t   pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)\n\t{\n\t  (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n\t  return result;\n\t}\n\n      rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;\n      rwl->nCompletedSharedAccessCount = 0;\n\n      if ((result =\n\t   pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)\n\t{\n\t  (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n\t  return result;\n\t}\n    }\n\n  return (pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)));\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlock_timedrdlock.c",
    "content": "/*\n * pthread_rwlock_timedrdlock.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlock_timedrdlock (pthread_rwlock_t * rwlock,\n\t\t\t    const struct timespec *abstime)\n{\n  int result;\n  pthread_rwlock_t rwl;\n\n  if (rwlock == NULL || *rwlock == NULL)\n    {\n      return EINVAL;\n    }\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static rwlock. We check\n   * again inside the guarded section of ptw32_rwlock_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)\n    {\n      result = ptw32_rwlock_check_need_init (rwlock);\n\n      if (result != 0 && result != EBUSY)\n\t{\n\t  return result;\n\t}\n    }\n\n  rwl = *rwlock;\n\n  if (rwl->nMagic != PTW32_RWLOCK_MAGIC)\n    {\n      return EINVAL;\n    }\n\n  if ((result =\n       pthread_mutex_timedlock (&(rwl->mtxExclusiveAccess), abstime)) != 0)\n    {\n      return result;\n    }\n\n  if (++rwl->nSharedAccessCount == INT_MAX)\n    {\n      if ((result =\n\t   pthread_mutex_timedlock (&(rwl->mtxSharedAccessCompleted),\n\t\t\t\t    abstime)) != 0)\n\t{\n\t  if (result == ETIMEDOUT)\n\t    {\n\t      ++rwl->nCompletedSharedAccessCount;\n\t    }\n\t  (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n\t  return result;\n\t}\n\n      rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;\n      rwl->nCompletedSharedAccessCount = 0;\n\n      if ((result =\n\t   pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)\n\t{\n\t  (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n\t  return result;\n\t}\n    }\n\n  return (pthread_mutex_unlock (&(rwl->mtxExclusiveAccess)));\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlock_timedwrlock.c",
    "content": "/*\n * pthread_rwlock_timedwrlock.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlock_timedwrlock (pthread_rwlock_t * rwlock,\n\t\t\t    const struct timespec *abstime)\n{\n  int result;\n  pthread_rwlock_t rwl;\n\n  if (rwlock == NULL || *rwlock == NULL)\n    {\n      return EINVAL;\n    }\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static rwlock. We check\n   * again inside the guarded section of ptw32_rwlock_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)\n    {\n      result = ptw32_rwlock_check_need_init (rwlock);\n\n      if (result != 0 && result != EBUSY)\n\t{\n\t  return result;\n\t}\n    }\n\n  rwl = *rwlock;\n\n  if (rwl->nMagic != PTW32_RWLOCK_MAGIC)\n    {\n      return EINVAL;\n    }\n\n  if ((result =\n       pthread_mutex_timedlock (&(rwl->mtxExclusiveAccess), abstime)) != 0)\n    {\n      return result;\n    }\n\n  if ((result =\n       pthread_mutex_timedlock (&(rwl->mtxSharedAccessCompleted),\n\t\t\t\tabstime)) != 0)\n    {\n      (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n      return result;\n    }\n\n  if (rwl->nExclusiveAccessCount == 0)\n    {\n      if (rwl->nCompletedSharedAccessCount > 0)\n\t{\n\t  rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;\n\t  rwl->nCompletedSharedAccessCount = 0;\n\t}\n\n      if (rwl->nSharedAccessCount > 0)\n\t{\n\t  rwl->nCompletedSharedAccessCount = -rwl->nSharedAccessCount;\n\n\t  /*\n\t   * This routine may be a cancelation point\n\t   * according to POSIX 1003.1j section 18.1.2.\n\t   */\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth(0)\n#endif\n\t  pthread_cleanup_push (ptw32_rwlock_cancelwrwait, (void *) rwl);\n\n\t  do\n\t    {\n\t      result =\n\t\tpthread_cond_timedwait (&(rwl->cndSharedAccessCompleted),\n\t\t\t\t\t&(rwl->mtxSharedAccessCompleted),\n\t\t\t\t\tabstime);\n\t    }\n\t  while (result == 0 && rwl->nCompletedSharedAccessCount < 0);\n\n\t  pthread_cleanup_pop ((result != 0) ? 1 : 0);\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth()\n#endif\n\n\t  if (result == 0)\n\t    {\n\t      rwl->nSharedAccessCount = 0;\n\t    }\n\t}\n    }\n\n  if (result == 0)\n    {\n      rwl->nExclusiveAccessCount++;\n    }\n\n  return result;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlock_tryrdlock.c",
    "content": "/*\n * pthread_rwlock_tryrdlock.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlock_tryrdlock (pthread_rwlock_t * rwlock)\n{\n  int result;\n  pthread_rwlock_t rwl;\n\n  if (rwlock == NULL || *rwlock == NULL)\n    {\n      return EINVAL;\n    }\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static rwlock. We check\n   * again inside the guarded section of ptw32_rwlock_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)\n    {\n      result = ptw32_rwlock_check_need_init (rwlock);\n\n      if (result != 0 && result != EBUSY)\n\t{\n\t  return result;\n\t}\n    }\n\n  rwl = *rwlock;\n\n  if (rwl->nMagic != PTW32_RWLOCK_MAGIC)\n    {\n      return EINVAL;\n    }\n\n  if ((result = pthread_mutex_trylock (&(rwl->mtxExclusiveAccess))) != 0)\n    {\n      return result;\n    }\n\n  if (++rwl->nSharedAccessCount == INT_MAX)\n    {\n      if ((result =\n\t   pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)\n\t{\n\t  (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n\t  return result;\n\t}\n\n      rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;\n      rwl->nCompletedSharedAccessCount = 0;\n\n      if ((result =\n\t   pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)\n\t{\n\t  (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n\t  return result;\n\t}\n    }\n\n  return (pthread_mutex_unlock (&rwl->mtxExclusiveAccess));\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlock_trywrlock.c",
    "content": "/*\n * pthread_rwlock_trywrlock.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlock_trywrlock (pthread_rwlock_t * rwlock)\n{\n  int result, result1;\n  pthread_rwlock_t rwl;\n\n  if (rwlock == NULL || *rwlock == NULL)\n    {\n      return EINVAL;\n    }\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static rwlock. We check\n   * again inside the guarded section of ptw32_rwlock_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)\n    {\n      result = ptw32_rwlock_check_need_init (rwlock);\n\n      if (result != 0 && result != EBUSY)\n\t{\n\t  return result;\n\t}\n    }\n\n  rwl = *rwlock;\n\n  if (rwl->nMagic != PTW32_RWLOCK_MAGIC)\n    {\n      return EINVAL;\n    }\n\n  if ((result = pthread_mutex_trylock (&(rwl->mtxExclusiveAccess))) != 0)\n    {\n      return result;\n    }\n\n  if ((result =\n       pthread_mutex_trylock (&(rwl->mtxSharedAccessCompleted))) != 0)\n    {\n      result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n      return ((result1 != 0) ? result1 : result);\n    }\n\n  if (rwl->nExclusiveAccessCount == 0)\n    {\n      if (rwl->nCompletedSharedAccessCount > 0)\n\t{\n\t  rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;\n\t  rwl->nCompletedSharedAccessCount = 0;\n\t}\n\n      if (rwl->nSharedAccessCount > 0)\n\t{\n\t  if ((result =\n\t       pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted))) != 0)\n\t    {\n\t      (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n\t      return result;\n\t    }\n\n\t  if ((result =\n\t       pthread_mutex_unlock (&(rwl->mtxExclusiveAccess))) == 0)\n\t    {\n\t      result = EBUSY;\n\t    }\n\t}\n      else\n\t{\n\t  rwl->nExclusiveAccessCount = 1;\n\t}\n    }\n  else\n    {\n      result = EBUSY;\n    }\n\n  return result;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlock_unlock.c",
    "content": "/*\n * pthread_rwlock_unlock.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlock_unlock (pthread_rwlock_t * rwlock)\n{\n  int result, result1;\n  pthread_rwlock_t rwl;\n\n  if (rwlock == NULL || *rwlock == NULL)\n    {\n      return (EINVAL);\n    }\n\n  if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)\n    {\n      /*\n       * Assume any race condition here is harmless.\n       */\n      return 0;\n    }\n\n  rwl = *rwlock;\n\n  if (rwl->nMagic != PTW32_RWLOCK_MAGIC)\n    {\n      return EINVAL;\n    }\n\n  if (rwl->nExclusiveAccessCount == 0)\n    {\n      if ((result =\n\t   pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)\n\t{\n\t  return result;\n\t}\n\n      if (++rwl->nCompletedSharedAccessCount == 0)\n\t{\n\t  result = pthread_cond_signal (&(rwl->cndSharedAccessCompleted));\n\t}\n\n      result1 = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted));\n    }\n  else\n    {\n      rwl->nExclusiveAccessCount--;\n\n      result = pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted));\n      result1 = pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n\n    }\n\n  return ((result != 0) ? result : result1);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlock_wrlock.c",
    "content": "/*\n * pthread_rwlock_wrlock.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlock_wrlock (pthread_rwlock_t * rwlock)\n{\n  int result;\n  pthread_rwlock_t rwl;\n\n  if (rwlock == NULL || *rwlock == NULL)\n    {\n      return EINVAL;\n    }\n\n  /*\n   * We do a quick check to see if we need to do more work\n   * to initialise a static rwlock. We check\n   * again inside the guarded section of ptw32_rwlock_check_need_init()\n   * to avoid race conditions.\n   */\n  if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)\n    {\n      result = ptw32_rwlock_check_need_init (rwlock);\n\n      if (result != 0 && result != EBUSY)\n\t{\n\t  return result;\n\t}\n    }\n\n  rwl = *rwlock;\n\n  if (rwl->nMagic != PTW32_RWLOCK_MAGIC)\n    {\n      return EINVAL;\n    }\n\n  if ((result = pthread_mutex_lock (&(rwl->mtxExclusiveAccess))) != 0)\n    {\n      return result;\n    }\n\n  if ((result = pthread_mutex_lock (&(rwl->mtxSharedAccessCompleted))) != 0)\n    {\n      (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n      return result;\n    }\n\n  if (rwl->nExclusiveAccessCount == 0)\n    {\n      if (rwl->nCompletedSharedAccessCount > 0)\n\t{\n\t  rwl->nSharedAccessCount -= rwl->nCompletedSharedAccessCount;\n\t  rwl->nCompletedSharedAccessCount = 0;\n\t}\n\n      if (rwl->nSharedAccessCount > 0)\n\t{\n\t  rwl->nCompletedSharedAccessCount = -rwl->nSharedAccessCount;\n\n\t  /*\n\t   * This routine may be a cancelation point\n\t   * according to POSIX 1003.1j section 18.1.2.\n\t   */\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth(0)\n#endif\n\t  pthread_cleanup_push (ptw32_rwlock_cancelwrwait, (void *) rwl);\n\n\t  do\n\t    {\n\t      result = pthread_cond_wait (&(rwl->cndSharedAccessCompleted),\n\t\t\t\t\t  &(rwl->mtxSharedAccessCompleted));\n\t    }\n\t  while (result == 0 && rwl->nCompletedSharedAccessCount < 0);\n\n\t  pthread_cleanup_pop ((result != 0) ? 1 : 0);\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth()\n#endif\n\n\t  if (result == 0)\n\t    {\n\t      rwl->nSharedAccessCount = 0;\n\t    }\n\t}\n    }\n\n  if (result == 0)\n    {\n      rwl->nExclusiveAccessCount++;\n    }\n\n  return result;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlockattr_destroy.c",
    "content": "/*\n * pthread_rwlockattr_destroy.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlockattr_destroy (pthread_rwlockattr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Destroys a rwlock attributes object. The object can\n      *      no longer be used.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_rwlockattr_t\n      *\n      *\n      * DESCRIPTION\n      *      Destroys a rwlock attributes object. The object can\n      *      no longer be used.\n      *\n      *      NOTES:\n      *              1)      Does not affect rwlockss created using 'attr'\n      *\n      * RESULTS\n      *              0               successfully released attr,\n      *              EINVAL          'attr' is invalid.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n\n  if (attr == NULL || *attr == NULL)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      pthread_rwlockattr_t rwa = *attr;\n\n      *attr = NULL;\n      free (rwa);\n    }\n\n  return (result);\n}\t\t\t\t/* pthread_rwlockattr_destroy */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlockattr_getpshared.c",
    "content": "/*\n * pthread_rwlockattr_getpshared.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlockattr_getpshared (const pthread_rwlockattr_t * attr,\n\t\t\t       int *pshared)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Determine whether rwlocks created with 'attr' can be\n      *      shared between processes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_rwlockattr_t\n      *\n      *      pshared\n      *              will be set to one of:\n      *\n      *                      PTHREAD_PROCESS_SHARED\n      *                              May be shared if in shared memory\n      *\n      *                      PTHREAD_PROCESS_PRIVATE\n      *                              Cannot be shared.\n      *\n      *\n      * DESCRIPTION\n      *      Rwlocks creatd with 'attr' can be shared between\n      *      processes if pthread_rwlock_t variable is allocated\n      *      in memory shared by these processes.\n      *      NOTES:\n      *              1)      pshared rwlocks MUST be allocated in shared\n      *                      memory.\n      *              2)      The following macro is defined if shared rwlocks\n      *                      are supported:\n      *                              _POSIX_THREAD_PROCESS_SHARED\n      *\n      * RESULTS\n      *              0               successfully retrieved attribute,\n      *              EINVAL          'attr' is invalid,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n\n  if ((attr != NULL && *attr != NULL) && (pshared != NULL))\n    {\n      *pshared = (*attr)->pshared;\n      result = 0;\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return (result);\n\n}\t\t\t\t/* pthread_rwlockattr_getpshared */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlockattr_init.c",
    "content": "/*\n * pthread_rwlockattr_init.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlockattr_init (pthread_rwlockattr_t * attr)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Initializes a rwlock attributes object with default\n      *      attributes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_rwlockattr_t\n      *\n      *\n      * DESCRIPTION\n      *      Initializes a rwlock attributes object with default\n      *      attributes.\n      *\n      * RESULTS\n      *              0               successfully initialized attr,\n      *              ENOMEM          insufficient memory for attr.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  pthread_rwlockattr_t rwa;\n\n  rwa = (pthread_rwlockattr_t) calloc (1, sizeof (*rwa));\n\n  if (rwa == NULL)\n    {\n      result = ENOMEM;\n    }\n  else\n    {\n      rwa->pshared = PTHREAD_PROCESS_PRIVATE;\n    }\n\n  *attr = rwa;\n\n  return (result);\n}\t\t\t\t/* pthread_rwlockattr_init */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_rwlockattr_setpshared.c",
    "content": "/*\n * pthread_rwlockattr_setpshared.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\npthread_rwlockattr_setpshared (pthread_rwlockattr_t * attr, int pshared)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Rwlocks created with 'attr' can be shared between\n      *      processes if pthread_rwlock_t variable is allocated\n      *      in memory shared by these processes.\n      *\n      * PARAMETERS\n      *      attr\n      *              pointer to an instance of pthread_rwlockattr_t\n      *\n      *      pshared\n      *              must be one of:\n      *\n      *                      PTHREAD_PROCESS_SHARED\n      *                              May be shared if in shared memory\n      *\n      *                      PTHREAD_PROCESS_PRIVATE\n      *                              Cannot be shared.\n      *\n      * DESCRIPTION\n      *      Rwlocks creatd with 'attr' can be shared between\n      *      processes if pthread_rwlock_t variable is allocated\n      *      in memory shared by these processes.\n      *\n      *      NOTES:\n      *              1)      pshared rwlocks MUST be allocated in shared\n      *                      memory.\n      *\n      *              2)      The following macro is defined if shared rwlocks\n      *                      are supported:\n      *                              _POSIX_THREAD_PROCESS_SHARED\n      *\n      * RESULTS\n      *              0               successfully set attribute,\n      *              EINVAL          'attr' or pshared is invalid,\n      *              ENOSYS          PTHREAD_PROCESS_SHARED not supported,\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result;\n\n  if ((attr != NULL && *attr != NULL) &&\n      ((pshared == PTHREAD_PROCESS_SHARED) ||\n       (pshared == PTHREAD_PROCESS_PRIVATE)))\n    {\n      if (pshared == PTHREAD_PROCESS_SHARED)\n\t{\n\n#if !defined( _POSIX_THREAD_PROCESS_SHARED )\n\n\t  result = ENOSYS;\n\t  pshared = PTHREAD_PROCESS_PRIVATE;\n\n#else\n\n\t  result = 0;\n\n#endif /* _POSIX_THREAD_PROCESS_SHARED */\n\n\t}\n      else\n\t{\n\t  result = 0;\n\t}\n\n      (*attr)->pshared = pshared;\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return (result);\n\n}\t\t\t\t/* pthread_rwlockattr_setpshared */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_self.c",
    "content": "/*\n * pthread_self.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\npthread_t\npthread_self (void)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function returns a reference to the current running\n      *      thread.\n      *\n      * PARAMETERS\n      *      N/A\n      *\n      *\n      * DESCRIPTION\n      *      This function returns a reference to the current running\n      *      thread.\n      *\n      * RESULTS\n      *              pthread_t       reference to the current thread\n      *\n      * ------------------------------------------------------\n      */\n{\n  pthread_t self;\n  pthread_t nil = {NULL, 0};\n  ptw32_thread_t * sp;\n\n#if defined(_UWIN)\n  if (!ptw32_selfThreadKey)\n    return nil;\n#endif\n\n  sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey);\n\n  if (sp != NULL)\n    {\n      self = sp->ptHandle;\n    }\n  else\n    {\n      /*\n       * Need to create an implicit 'self' for the currently\n       * executing thread.\n       */\n      self = ptw32_new ();\n      sp = (ptw32_thread_t *) self.p;\n\n      if (sp != NULL)\n\t{\n\t  /*\n\t   * This is a non-POSIX thread which has chosen to call\n\t   * a POSIX threads function for some reason. We assume that\n\t   * it isn't joinable, but we do assume that it's\n\t   * (deferred) cancelable.\n\t   */\n\t  sp->implicit = 1;\n\t  sp->detachState = PTHREAD_CREATE_DETACHED;\n\t  sp->thread = GetCurrentThreadId ();\n\n#if defined(NEED_DUPLICATEHANDLE)\n\t  /*\n\t   * DuplicateHandle does not exist on WinCE.\n\t   *\n\t   * NOTE:\n\t   * GetCurrentThread only returns a pseudo-handle\n\t   * which is only valid in the current thread context.\n\t   * Therefore, you should not pass the handle to\n\t   * other threads for whatever purpose.\n\t   */\n\t  sp->threadH = GetCurrentThread ();\n#else\n\t  if (!DuplicateHandle (GetCurrentProcess (),\n\t\t\t\tGetCurrentThread (),\n\t\t\t\tGetCurrentProcess (),\n\t\t\t\t&sp->threadH,\n\t\t\t\t0, FALSE, DUPLICATE_SAME_ACCESS))\n\t    {\n\t      /*\n\t       * Should not do this, but we have no alternative if\n\t       * we can't get a Win32 thread handle.\n\t       * Thread structs are never freed.\n\t       */\n\t      ptw32_threadReusePush (self);\n\t      /*\n\t       * As this is a win32 thread calling us and we have failed,\n\t       * return a value that makes sense to win32.\n\t       */\n\t      return nil;\n\t    }\n#endif\n\n\t  /*\n\t   * No need to explicitly serialise access to sched_priority\n\t   * because the new handle is not yet public.\n\t   */\n\t  sp->sched_priority = GetThreadPriority (sp->threadH);\n\t  pthread_setspecific (ptw32_selfThreadKey, (void *) sp);\n\t}\n    }\n\n  return (self);\n\n}\t\t\t\t/* pthread_self */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_setcancelstate.c",
    "content": "/*\n * pthread_setcancelstate.c\n *\n * Description:\n * POSIX thread functions related to thread cancellation.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_setcancelstate (int state, int *oldstate)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function atomically sets the calling thread's\n      *      cancelability state to 'state' and returns the previous\n      *      cancelability state at the location referenced by\n      *      'oldstate'\n      *\n      * PARAMETERS\n      *      state,\n      *      oldstate\n      *              PTHREAD_CANCEL_ENABLE\n      *                      cancellation is enabled,\n      *\n      *              PTHREAD_CANCEL_DISABLE\n      *                      cancellation is disabled\n      *\n      *\n      * DESCRIPTION\n      *      This function atomically sets the calling thread's\n      *      cancelability state to 'state' and returns the previous\n      *      cancelability state at the location referenced by\n      *      'oldstate'.\n      *\n      *      NOTES:\n      *      1)      Use to disable cancellation around 'atomic' code that\n      *              includes cancellation points\n      *\n      * COMPATIBILITY ADDITIONS\n      *      If 'oldstate' is NULL then the previous state is not returned\n      *      but the function still succeeds. (Solaris)\n      *\n      * RESULTS\n      *              0               successfully set cancelability type,\n      *              EINVAL          'state' is invalid\n      *\n      * ------------------------------------------------------\n      */\n{\n  ptw32_mcs_local_node_t stateLock;\n  int result = 0;\n  pthread_t self = pthread_self ();\n  ptw32_thread_t * sp = (ptw32_thread_t *) self.p;\n\n  if (sp == NULL\n      || (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE))\n    {\n      return EINVAL;\n    }\n\n  /*\n   * Lock for async-cancel safety.\n   */\n  ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock);\n\n  if (oldstate != NULL)\n    {\n      *oldstate = sp->cancelState;\n    }\n\n  sp->cancelState = state;\n\n  /*\n   * Check if there is a pending asynchronous cancel\n   */\n  if (state == PTHREAD_CANCEL_ENABLE\n      && sp->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS\n      && WaitForSingleObject (sp->cancelEvent, 0) == WAIT_OBJECT_0)\n    {\n      sp->state = PThreadStateCanceling;\n      sp->cancelState = PTHREAD_CANCEL_DISABLE;\n      ResetEvent (sp->cancelEvent);\n      ptw32_mcs_lock_release (&stateLock);\n      ptw32_throw (PTW32_EPS_CANCEL);\n\n      /* Never reached */\n    }\n\n  ptw32_mcs_lock_release (&stateLock);\n\n  return (result);\n\n}\t\t\t\t/* pthread_setcancelstate */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_setcanceltype.c",
    "content": "/*\n * pthread_setcanceltype.c\n *\n * Description:\n * POSIX thread functions related to thread cancellation.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_setcanceltype (int type, int *oldtype)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function atomically sets the calling thread's\n      *      cancelability type to 'type' and returns the previous\n      *      cancelability type at the location referenced by\n      *      'oldtype'\n      *\n      * PARAMETERS\n      *      type,\n      *      oldtype\n      *              PTHREAD_CANCEL_DEFERRED\n      *                      only deferred cancelation is allowed,\n      *\n      *              PTHREAD_CANCEL_ASYNCHRONOUS\n      *                      Asynchronous cancellation is allowed\n      *\n      *\n      * DESCRIPTION\n      *      This function atomically sets the calling thread's\n      *      cancelability type to 'type' and returns the previous\n      *      cancelability type at the location referenced by\n      *      'oldtype'\n      *\n      *      NOTES:\n      *      1)      Use with caution; most code is not safe for use\n      *              with asynchronous cancelability.\n      *\n      * COMPATIBILITY ADDITIONS\n      *      If 'oldtype' is NULL then the previous type is not returned\n      *      but the function still succeeds. (Solaris)\n      *\n      * RESULTS\n      *              0               successfully set cancelability type,\n      *              EINVAL          'type' is invalid\n      *\n      * ------------------------------------------------------\n      */\n{\n  ptw32_mcs_local_node_t stateLock;\n  int result = 0;\n  pthread_t self = pthread_self ();\n  ptw32_thread_t * sp = (ptw32_thread_t *) self.p;\n\n  if (sp == NULL\n      || (type != PTHREAD_CANCEL_DEFERRED\n\t  && type != PTHREAD_CANCEL_ASYNCHRONOUS))\n    {\n      return EINVAL;\n    }\n\n  /*\n   * Lock for async-cancel safety.\n   */\n  ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock);\n\n  if (oldtype != NULL)\n    {\n      *oldtype = sp->cancelType;\n    }\n\n  sp->cancelType = type;\n\n  /*\n   * Check if there is a pending asynchronous cancel\n   */\n  if (sp->cancelState == PTHREAD_CANCEL_ENABLE\n      && type == PTHREAD_CANCEL_ASYNCHRONOUS\n      && WaitForSingleObject (sp->cancelEvent, 0) == WAIT_OBJECT_0)\n    {\n      sp->state = PThreadStateCanceling;\n      sp->cancelState = PTHREAD_CANCEL_DISABLE;\n      ResetEvent (sp->cancelEvent);\n      ptw32_mcs_lock_release (&stateLock);\n      ptw32_throw (PTW32_EPS_CANCEL);\n\n      /* Never reached */\n    }\n\n  ptw32_mcs_lock_release (&stateLock);\n\n  return (result);\n\n}\t\t\t\t/* pthread_setcanceltype */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_setconcurrency.c",
    "content": "/*\n * pthread_setconcurrency.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_setconcurrency (int level)\n{\n  if (level < 0)\n    {\n      return EINVAL;\n    }\n  else\n    {\n      ptw32_concurrency = level;\n      return 0;\n    }\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_setschedparam.c",
    "content": "/*\n * sched_setschedparam.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\npthread_setschedparam (pthread_t thread, int policy,\n\t\t       const struct sched_param *param)\n{\n  int result;\n\n  /* Validate the thread id. */\n  result = pthread_kill (thread, 0);\n  if (0 != result)\n    {\n      return result;\n    }\n\n  /* Validate the scheduling policy. */\n  if (policy < SCHED_MIN || policy > SCHED_MAX)\n    {\n      return EINVAL;\n    }\n\n  /* Ensure the policy is SCHED_OTHER. */\n  if (policy != SCHED_OTHER)\n    {\n      return ENOTSUP;\n    }\n\n  return (ptw32_setthreadpriority (thread, policy, param->sched_priority));\n}\n\n\nint\nptw32_setthreadpriority (pthread_t thread, int policy, int priority)\n{\n  int prio;\n  ptw32_mcs_local_node_t threadLock;\n  int result = 0;\n  ptw32_thread_t * tp = (ptw32_thread_t *) thread.p;\n\n  prio = priority;\n\n  /* Validate priority level. */\n  if (prio < sched_get_priority_min (policy) ||\n      prio > sched_get_priority_max (policy))\n    {\n      return EINVAL;\n    }\n\n#if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL)\n/* WinCE */\n#else\n/* Everything else */\n\n  if (THREAD_PRIORITY_IDLE < prio && THREAD_PRIORITY_LOWEST > prio)\n    {\n      prio = THREAD_PRIORITY_LOWEST;\n    }\n  else if (THREAD_PRIORITY_TIME_CRITICAL > prio\n\t   && THREAD_PRIORITY_HIGHEST < prio)\n    {\n      prio = THREAD_PRIORITY_HIGHEST;\n    }\n\n#endif\n\n  ptw32_mcs_lock_acquire (&tp->threadLock, &threadLock);\n\n  /* If this fails, the current priority is unchanged. */\n  if (0 == SetThreadPriority (tp->threadH, prio))\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      /*\n       * Must record the thread's sched_priority as given,\n       * not as finally adjusted.\n       */\n      tp->sched_priority = priority;\n    }\n\n  ptw32_mcs_lock_release (&threadLock);\n\n  return result;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_setspecific.c",
    "content": "/*\n * pthread_setspecific.c\n *\n * Description:\n * POSIX thread functions which implement thread-specific data (TSD).\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_setspecific (pthread_key_t key, const void *value)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function sets the value of the thread specific\n      *      key in the calling thread.\n      *\n      * PARAMETERS\n      *      key\n      *              an instance of pthread_key_t\n      *      value\n      *              the value to set key to\n      *\n      *\n      * DESCRIPTION\n      *      This function sets the value of the thread specific\n      *      key in the calling thread.\n      *\n      * RESULTS\n      *              0               successfully set value\n      *              EAGAIN          could not set value\n      *              ENOENT          SERIOUS!!\n      *\n      * ------------------------------------------------------\n      */\n{\n  pthread_t self;\n  int result = 0;\n\n  if (key != ptw32_selfThreadKey)\n    {\n      /*\n       * Using pthread_self will implicitly create\n       * an instance of pthread_t for the current\n       * thread if one wasn't explicitly created\n       */\n      self = pthread_self ();\n      if (self.p == NULL)\n\t{\n\t  return ENOENT;\n\t}\n    }\n  else\n    {\n      /*\n       * Resolve catch-22 of registering thread with selfThread\n       * key\n       */\n      ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey);\n\n      if (sp == NULL)\n        {\n\t  if (value == NULL)\n\t    {\n\t      return ENOENT;\n\t    }\n          self = *((pthread_t *) value);\n        }\n      else\n        {\n\t  self = sp->ptHandle;\n        }\n    }\n\n  result = 0;\n\n  if (key != NULL)\n    {\n      if (self.p != NULL && key->destructor != NULL && value != NULL)\n\t{\n          ptw32_mcs_local_node_t keyLock;\n          ptw32_mcs_local_node_t threadLock;\n\t  ptw32_thread_t * sp = (ptw32_thread_t *) self.p;\n\t  /*\n\t   * Only require associations if we have to\n\t   * call user destroy routine.\n\t   * Don't need to locate an existing association\n\t   * when setting data to NULL for WIN32 since the\n\t   * data is stored with the operating system; not\n\t   * on the association; setting assoc to NULL short\n\t   * circuits the search.\n\t   */\n\t  ThreadKeyAssoc *assoc;\n\n\t  ptw32_mcs_lock_acquire(&(key->keyLock), &keyLock);\n\t  ptw32_mcs_lock_acquire(&(sp->threadLock), &threadLock);\n\n\t  assoc = (ThreadKeyAssoc *) sp->keys;\n\t  /*\n\t   * Locate existing association\n\t   */\n\t  while (assoc != NULL)\n\t    {\n\t      if (assoc->key == key)\n\t\t{\n\t\t  /*\n\t\t   * Association already exists\n\t\t   */\n\t\t  break;\n\t\t}\n\t\tassoc = assoc->nextKey;\n\t    }\n\n\t  /*\n\t   * create an association if not found\n\t   */\n\t  if (assoc == NULL)\n\t    {\n\t      result = ptw32_tkAssocCreate (sp, key);\n\t    }\n\n\t  ptw32_mcs_lock_release(&threadLock);\n\t  ptw32_mcs_lock_release(&keyLock);\n\t}\n\n      if (result == 0)\n\t{\n\t  if (!TlsSetValue (key->key, (LPVOID) value))\n\t    {\n\t      result = EAGAIN;\n\t    }\n\t}\n    }\n\n  return (result);\n}\t\t\t\t/* pthread_setspecific */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_spin_destroy.c",
    "content": "/*\n * pthread_spin_destroy.c\n *\n * Description:\n * This translation unit implements spin lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_spin_destroy (pthread_spinlock_t * lock)\n{\n  register pthread_spinlock_t s;\n  int result = 0;\n\n  if (lock == NULL || *lock == NULL)\n    {\n      return EINVAL;\n    }\n\n  if ((s = *lock) != PTHREAD_SPINLOCK_INITIALIZER)\n    {\n      if (s->interlock == PTW32_SPIN_USE_MUTEX)\n\t{\n\t  result = pthread_mutex_destroy (&(s->u.mutex));\n\t}\n      else if ((PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED !=\n\t       PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG ((PTW32_INTERLOCKED_LONGPTR) &s->interlock,\n\t\t\t\t\t\t   (PTW32_INTERLOCKED_LONG) PTW32_SPIN_INVALID,\n\t\t\t\t\t\t   (PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED))\n\t{\n\t  result = EINVAL;\n\t}\n\n      if (0 == result)\n\t{\n\t  /*\n\t   * We are relying on the application to ensure that all other threads\n\t   * have finished with the spinlock before destroying it.\n\t   */\n\t  *lock = NULL;\n\t  (void) free (s);\n\t}\n    }\n  else\n    {\n      /*\n       * See notes in ptw32_spinlock_check_need_init() above also.\n       */\n      ptw32_mcs_local_node_t node;\n\n      ptw32_mcs_lock_acquire(&ptw32_spinlock_test_init_lock, &node);\n\n      /*\n       * Check again.\n       */\n      if (*lock == PTHREAD_SPINLOCK_INITIALIZER)\n\t{\n\t  /*\n\t   * This is all we need to do to destroy a statically\n\t   * initialised spinlock that has not yet been used (initialised).\n\t   * If we get to here, another thread\n\t   * waiting to initialise this mutex will get an EINVAL.\n\t   */\n\t  *lock = NULL;\n\t}\n      else\n\t{\n\t  /*\n\t   * The spinlock has been initialised while we were waiting\n\t   * so assume it's in use.\n\t   */\n\t  result = EBUSY;\n\t}\n\n       ptw32_mcs_lock_release(&node);\n    }\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_spin_init.c",
    "content": "/*\n * pthread_spin_init.c\n *\n * Description:\n * This translation unit implements spin lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_spin_init (pthread_spinlock_t * lock, int pshared)\n{\n  pthread_spinlock_t s;\n  int cpus = 0;\n  int result = 0;\n\n  if (lock == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (0 != ptw32_getprocessors (&cpus))\n    {\n      cpus = 1;\n    }\n\n  if (cpus > 1)\n    {\n      if (pshared == PTHREAD_PROCESS_SHARED)\n\t{\n\t  /*\n\t   * Creating spinlock that can be shared between\n\t   * processes.\n\t   */\n#if _POSIX_THREAD_PROCESS_SHARED >= 0\n\n\t  /*\n\t   * Not implemented yet.\n\t   */\n\n#error ERROR [__FILE__, line __LINE__]: Process shared spin locks are not supported yet.\n\n#else\n\n\t  return ENOSYS;\n\n#endif /* _POSIX_THREAD_PROCESS_SHARED */\n\n\t}\n    }\n\n  s = (pthread_spinlock_t) calloc (1, sizeof (*s));\n\n  if (s == NULL)\n    {\n      return ENOMEM;\n    }\n\n  if (cpus > 1)\n    {\n      s->u.cpus = cpus;\n      s->interlock = PTW32_SPIN_UNLOCKED;\n    }\n  else\n    {\n      pthread_mutexattr_t ma;\n      result = pthread_mutexattr_init (&ma);\n\n      if (0 == result)\n\t{\n\t  ma->pshared = pshared;\n\t  result = pthread_mutex_init (&(s->u.mutex), &ma);\n\t  if (0 == result)\n\t    {\n\t      s->interlock = PTW32_SPIN_USE_MUTEX;\n\t    }\n\t}\n      (void) pthread_mutexattr_destroy (&ma);\n    }\n\n  if (0 == result)\n    {\n      *lock = s;\n    }\n  else\n    {\n      (void) free (s);\n      *lock = NULL;\n    }\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_spin_lock.c",
    "content": "/*\n * pthread_spin_lock.c\n *\n * Description:\n * This translation unit implements spin lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_spin_lock (pthread_spinlock_t * lock)\n{\n  register pthread_spinlock_t s;\n\n  if (NULL == lock || NULL == *lock)\n    {\n      return (EINVAL);\n    }\n\n  if (*lock == PTHREAD_SPINLOCK_INITIALIZER)\n    {\n      int result;\n\n      if ((result = ptw32_spinlock_check_need_init (lock)) != 0)\n\t{\n\t  return (result);\n\t}\n    }\n\n  s = *lock;\n\n  while ((PTW32_INTERLOCKED_LONG) PTW32_SPIN_LOCKED ==\n\t PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG ((PTW32_INTERLOCKED_LONGPTR) &s->interlock,\n\t\t\t\t\t          (PTW32_INTERLOCKED_LONG) PTW32_SPIN_LOCKED,\n\t\t\t\t\t          (PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED))\n    {\n    }\n\n  if (s->interlock == PTW32_SPIN_LOCKED)\n    {\n      return 0;\n    }\n  else if (s->interlock == PTW32_SPIN_USE_MUTEX)\n    {\n      return pthread_mutex_lock (&(s->u.mutex));\n    }\n\n  return EINVAL;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_spin_trylock.c",
    "content": "/*\n * pthread_spin_trylock.c\n *\n * Description:\n * This translation unit implements spin lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_spin_trylock (pthread_spinlock_t * lock)\n{\n  register pthread_spinlock_t s;\n\n  if (NULL == lock || NULL == *lock)\n    {\n      return (EINVAL);\n    }\n\n  if (*lock == PTHREAD_SPINLOCK_INITIALIZER)\n    {\n      int result;\n\n      if ((result = ptw32_spinlock_check_need_init (lock)) != 0)\n\t{\n\t  return (result);\n\t}\n    }\n\n  s = *lock;\n\n  switch ((long)\n\t  PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG ((PTW32_INTERLOCKED_LONGPTR) &s->interlock,\n\t\t\t\t\t           (PTW32_INTERLOCKED_LONG) PTW32_SPIN_LOCKED,\n\t\t\t\t\t           (PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED))\n    {\n    case PTW32_SPIN_UNLOCKED:\n      return 0;\n    case PTW32_SPIN_LOCKED:\n      return EBUSY;\n    case PTW32_SPIN_USE_MUTEX:\n      return pthread_mutex_trylock (&(s->u.mutex));\n    }\n\n  return EINVAL;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_spin_unlock.c",
    "content": "/*\n * pthread_spin_unlock.c\n *\n * Description:\n * This translation unit implements spin lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\npthread_spin_unlock (pthread_spinlock_t * lock)\n{\n  register pthread_spinlock_t s;\n\n  if (NULL == lock || NULL == *lock)\n    {\n      return (EINVAL);\n    }\n\n  s = *lock;\n\n  if (s == PTHREAD_SPINLOCK_INITIALIZER)\n    {\n      return EPERM;\n    }\n\n  switch ((long)\n\t  PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG ((PTW32_INTERLOCKED_LONGPTR) &s->interlock,\n\t\t\t\t\t      (PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED,\n\t\t\t\t\t      (PTW32_INTERLOCKED_LONG) PTW32_SPIN_LOCKED))\n    {\n    case PTW32_SPIN_LOCKED:\n    case PTW32_SPIN_UNLOCKED:\n      return 0;\n    case PTW32_SPIN_USE_MUTEX:\n      return pthread_mutex_unlock (&(s->u.mutex));\n    }\n\n  return EINVAL;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_testcancel.c",
    "content": "/*\n * pthread_testcancel.c\n *\n * Description:\n * POSIX thread functions related to thread cancellation.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nvoid\npthread_testcancel (void)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function creates a deferred cancellation point\n      *      in the calling thread. The call has no effect if the\n      *      current cancelability state is\n      *              PTHREAD_CANCEL_DISABLE\n      *\n      * PARAMETERS\n      *      N/A\n      *\n      *\n      * DESCRIPTION\n      *      This function creates a deferred cancellation point\n      *      in the calling thread. The call has no effect if the\n      *      current cancelability state is\n      *              PTHREAD_CANCEL_DISABLE\n      *\n      *      NOTES:\n      *      1)      Cancellation is asynchronous. Use pthread_join\n      *              to wait for termination of thread if necessary\n      *\n      * RESULTS\n      *              N/A\n      *\n      * ------------------------------------------------------\n      */\n{\n  ptw32_mcs_local_node_t stateLock;\n  pthread_t self = pthread_self ();\n  ptw32_thread_t * sp = (ptw32_thread_t *) self.p;\n\n  if (sp == NULL)\n    {\n      return;\n    }\n\n  /*\n   * Pthread_cancel() will have set sp->state to PThreadStateCancelPending\n   * and set an event, so no need to enter kernel space if\n   * sp->state != PThreadStateCancelPending - that only slows us down.\n   */\n  if (sp->state != PThreadStateCancelPending)\n    {\n      return;\n    }\n\n  ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock);\n\n  if (sp->cancelState != PTHREAD_CANCEL_DISABLE)\n    {\n      ResetEvent(sp->cancelEvent);\n      sp->state = PThreadStateCanceling;\n      sp->cancelState = PTHREAD_CANCEL_DISABLE;\n      ptw32_mcs_lock_release (&stateLock);\n      ptw32_throw (PTW32_EPS_CANCEL);\n      /* Never returns here */\n    }\n\n  ptw32_mcs_lock_release (&stateLock);\n}\t\t\t\t/* pthread_testcancel */\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_timechange_handler_np.c",
    "content": "/*\n * pthread_timechange_handler_np.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n/*\n * Notes on handling system time adjustments (especially negative ones).\n * ---------------------------------------------------------------------\n *\n * This solution was suggested by Alexander Terekhov, but any errors\n * in the implementation are mine - [Ross Johnson]\n *\n * 1) The problem: threads doing a timedwait on a CV may expect to timeout\n *    at a specific absolute time according to a system timer. If the\n *    system clock is adjusted backwards then those threads sleep longer than\n *    expected. Also, pthreads-win32 converts absolute times to intervals in\n *    order to make use of the underlying Win32, and so waiting threads may\n *    awake before their proper abstimes.\n *\n * 2) We aren't able to distinquish between threads on timed or untimed waits,\n *    so we wake them all at the time of the adjustment so that they can\n *    re-evaluate their conditions and re-compute their timeouts.\n *\n * 3) We rely on correctly written applications for this to work. Specifically,\n *    they must be able to deal properly with spurious wakeups. That is,\n *    they must re-test their condition upon wakeup and wait again if\n *    the condition is not satisfied.\n */\n\nvoid *\npthread_timechange_handler_np (void *arg)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      Broadcasts all CVs to force re-evaluation and\n      *      new timeouts if required.\n      *\n      * PARAMETERS\n      *      NONE\n      *\n      *\n      * DESCRIPTION\n      *      Broadcasts all CVs to force re-evaluation and\n      *      new timeouts if required.\n      *\n      *      This routine may be passed directly to pthread_create()\n      *      as a new thread in order to run asynchronously.\n      *\n      *\n      * RESULTS\n      *              0               successfully broadcast all CVs\n      *              EAGAIN          Not all CVs were broadcast\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  pthread_cond_t cv;\n  ptw32_mcs_local_node_t node;\n\n  ptw32_mcs_lock_acquire(&ptw32_cond_list_lock, &node);\n\n  cv = ptw32_cond_list_head;\n\n  while (cv != NULL && 0 == result)\n    {\n      result = pthread_cond_broadcast (&cv);\n      cv = cv->next;\n    }\n\n  ptw32_mcs_lock_release(&node);\n\n  return (void *) (size_t) (result != 0 ? EAGAIN : 0);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/pthread_win32_attach_detach_np.c",
    "content": "/*\n * pthread_win32_attach_detach_np.c\n *\n * Description:\n * This translation unit implements non-portable thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n#include <tchar.h>\n\n/*\n * Handle to quserex.dll \n */\nstatic HINSTANCE ptw32_h_quserex;\n\nBOOL\npthread_win32_process_attach_np ()\n{\n  TCHAR QuserExDLLPathBuf[1024];\n  BOOL result = TRUE;\n\n  result = ptw32_processInitialize ();\n\n#if defined(_UWIN)\n  pthread_count++;\n#endif\n\n#if defined(__GNUC__)\n  ptw32_features = 0;\n#else\n  /*\n   * This is obsolete now.\n   */\n  ptw32_features = PTW32_SYSTEM_INTERLOCKED_COMPARE_EXCHANGE;\n#endif\n\n  /*\n   * Load QUSEREX.DLL and try to get address of QueueUserAPCEx.\n   * Because QUSEREX.DLL requires a driver to be installed we will\n   * assume the DLL is in the system directory.\n   *\n   * This should take care of any security issues.\n   */\n#if defined(__GNUC__) || _MSC_VER < 1400\n  if(GetSystemDirectory(QuserExDLLPathBuf, sizeof(QuserExDLLPathBuf)))\n  {\n    (void) strncat(QuserExDLLPathBuf,\n                   \"\\\\QUSEREX.DLL\",\n                   sizeof(QuserExDLLPathBuf) - strlen(QuserExDLLPathBuf) - 1);\n    ptw32_h_quserex = LoadLibrary(QuserExDLLPathBuf);\n  }\n#else\n  /* strncat is secure - this is just to avoid a warning */\n  if(GetSystemDirectory(QuserExDLLPathBuf, sizeof(QuserExDLLPathBuf) / sizeof(TCHAR)) &&\n     0 == _tcsncat_s(QuserExDLLPathBuf, sizeof(QuserExDLLPathBuf) / sizeof(TCHAR), TEXT(\"\\\\QUSEREX.DLL\"), 12))\n  {\n    ptw32_h_quserex = LoadLibrary(QuserExDLLPathBuf);\n  }\n#endif\n\n  if (ptw32_h_quserex != NULL)\n    {\n      ptw32_register_cancelation = (DWORD (*)(PAPCFUNC, HANDLE, DWORD))\n#if defined(NEED_UNICODE_CONSTS)\n\tGetProcAddress (ptw32_h_quserex,\n\t\t\t(const TCHAR *) TEXT (\"QueueUserAPCEx\"));\n#else\n\tGetProcAddress (ptw32_h_quserex, (LPCSTR) \"QueueUserAPCEx\");\n#endif\n    }\n\n  if (NULL == ptw32_register_cancelation)\n    {\n      ptw32_register_cancelation = ptw32_RegisterCancelation;\n\n      if (ptw32_h_quserex != NULL)\n\t{\n\t  (void) FreeLibrary (ptw32_h_quserex);\n\t}\n      ptw32_h_quserex = 0;\n    }\n  else\n    {\n      /* Initialise QueueUserAPCEx */\n      BOOL (*queue_user_apc_ex_init) (VOID);\n\n      queue_user_apc_ex_init = (BOOL (*)(VOID))\n#if defined(NEED_UNICODE_CONSTS)\n\tGetProcAddress (ptw32_h_quserex,\n\t\t\t(const TCHAR *) TEXT (\"QueueUserAPCEx_Init\"));\n#else\n\tGetProcAddress (ptw32_h_quserex, (LPCSTR) \"QueueUserAPCEx_Init\");\n#endif\n\n      if (queue_user_apc_ex_init == NULL || !queue_user_apc_ex_init ())\n\t{\n\t  ptw32_register_cancelation = ptw32_RegisterCancelation;\n\n\t  (void) FreeLibrary (ptw32_h_quserex);\n\t  ptw32_h_quserex = 0;\n\t}\n    }\n\n  if (ptw32_h_quserex)\n    {\n      ptw32_features |= PTW32_ALERTABLE_ASYNC_CANCEL;\n    }\n\n  return result;\n}\n\n\nBOOL\npthread_win32_process_detach_np ()\n{\n  if (ptw32_processInitialized)\n    {\n      ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey);\n\n      if (sp != NULL)\n\t{\n\t  /*\n\t   * Detached threads have their resources automatically\n\t   * cleaned up upon exit (others must be 'joined').\n\t   */\n\t  if (sp->detachState == PTHREAD_CREATE_DETACHED)\n\t    {\n\t      ptw32_threadDestroy (sp->ptHandle);\n\t      TlsSetValue (ptw32_selfThreadKey->key, NULL);\n\t    }\n\t}\n\n      /*\n       * The DLL is being unmapped from the process's address space\n       */\n      ptw32_processTerminate ();\n\n      if (ptw32_h_quserex)\n\t{\n\t  /* Close QueueUserAPCEx */\n\t  BOOL (*queue_user_apc_ex_fini) (VOID);\n\n\t  queue_user_apc_ex_fini = (BOOL (*)(VOID))\n#if defined(NEED_UNICODE_CONSTS)\n\t    GetProcAddress (ptw32_h_quserex,\n\t\t\t    (const TCHAR *) TEXT (\"QueueUserAPCEx_Fini\"));\n#else\n\t    GetProcAddress (ptw32_h_quserex, (LPCSTR) \"QueueUserAPCEx_Fini\");\n#endif\n\n\t  if (queue_user_apc_ex_fini != NULL)\n\t    {\n\t      (void) queue_user_apc_ex_fini ();\n\t    }\n\t  (void) FreeLibrary (ptw32_h_quserex);\n\t}\n    }\n\n  return TRUE;\n}\n\nBOOL\npthread_win32_thread_attach_np ()\n{\n  return TRUE;\n}\n\nBOOL\npthread_win32_thread_detach_np ()\n{\n  if (ptw32_processInitialized)\n    {\n      /*\n       * Don't use pthread_self() - to avoid creating an implicit POSIX thread handle\n       * unnecessarily.\n       */\n      ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey);\n\n      if (sp != NULL) // otherwise Win32 thread with no implicit POSIX handle.\n\t{\n          ptw32_mcs_local_node_t stateLock;\n\t  ptw32_callUserDestroyRoutines (sp->ptHandle);\n\n\t  ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock);\n\t  sp->state = PThreadStateLast;\n\t  /*\n\t   * If the thread is joinable at this point then it MUST be joined\n\t   * or detached explicitly by the application.\n\t   */\n\t  ptw32_mcs_lock_release (&stateLock);\n\n          /*\n           * Robust Mutexes\n           */\n          while (sp->robustMxList != NULL)\n            {\n              pthread_mutex_t mx = sp->robustMxList->mx;\n              ptw32_robust_mutex_remove(&mx, sp);\n              (void) PTW32_INTERLOCKED_EXCHANGE_LONG(\n                       (PTW32_INTERLOCKED_LONGPTR)&mx->robustNode->stateInconsistent,\n                       (PTW32_INTERLOCKED_LONG)-1);\n              /*\n               * If there are no waiters then the next thread to block will\n               * sleep, wakeup immediately and then go back to sleep.\n               * See pthread_mutex_lock.c.\n               */\n              SetEvent(mx->event);\n            }\n\n\n\t  if (sp->detachState == PTHREAD_CREATE_DETACHED)\n\t    {\n\t      ptw32_threadDestroy (sp->ptHandle);\n\n\t      TlsSetValue (ptw32_selfThreadKey->key, NULL);\n\t    }\n\t}\n    }\n\n  return TRUE;\n}\n\nBOOL\npthread_win32_test_features_np (int feature_mask)\n{\n  return ((ptw32_features & feature_mask) == feature_mask);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_MCS_lock.c",
    "content": "/*\n * ptw32_MCS_lock.c\n *\n * Description:\n * This translation unit implements queue-based locks.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n/*\n * About MCS locks:\n *\n * MCS locks are queue-based locks, where the queue nodes are local to the\n * thread. The 'lock' is nothing more than a global pointer that points to\n * the last node in the queue, or is NULL if the queue is empty.\n * \n * Originally designed for use as spin locks requiring no kernel resources\n * for synchronisation or blocking, the implementation below has adapted\n * the MCS spin lock for use as a general mutex that will suspend threads\n * when there is lock contention.\n *\n * Because the queue nodes are thread-local, most of the memory read/write\n * operations required to add or remove nodes from the queue do not trigger\n * cache-coherence updates.\n *\n * Like 'named' mutexes, MCS locks consume system resources transiently -\n * they are able to acquire and free resources automatically - but MCS\n * locks do not require any unique 'name' to identify the lock to all\n * threads using it.\n *\n * Usage of MCS locks:\n *\n * - you need a global ptw32_mcs_lock_t instance initialised to 0 or NULL.\n * - you need a local thread-scope ptw32_mcs_local_node_t instance, which\n *   may serve several different locks but you need at least one node for\n *   every lock held concurrently by a thread.\n *\n * E.g.:\n * \n * ptw32_mcs_lock_t lock1 = 0;\n * ptw32_mcs_lock_t lock2 = 0;\n *\n * void *mythread(void *arg)\n * {\n *   ptw32_mcs_local_node_t node;\n *\n *   ptw32_mcs_acquire (&lock1, &node);\n *   ptw32_mcs_lock_release (&node);\n *\n *   ptw32_mcs_lock_acquire (&lock2, &node);\n *   ptw32_mcs_lock_release (&node);\n *   {\n *      ptw32_mcs_local_node_t nodex;\n *\n *      ptw32_mcs_lock_acquire (&lock1, &node);\n *      ptw32_mcs_lock_acquire (&lock2, &nodex);\n *\n *      ptw32_mcs_lock_release (&nodex);\n *      ptw32_mcs_lock_release (&node);\n *   }\n *   return (void *)0;\n * }\n */\n\n#include \"pthread.h\"\n#include \"sched.h\"\n#include \"implement.h\"\n\n/*\n * ptw32_mcs_flag_set -- notify another thread about an event.\n * \n * Set event if an event handle has been stored in the flag, and\n * set flag to -1 otherwise. Note that -1 cannot be a valid handle value.\n */\nINLINE void \nptw32_mcs_flag_set (HANDLE * flag)\n{\n  HANDLE e = (HANDLE)(PTW32_INTERLOCKED_SIZE)PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(\n\t\t\t\t\t\t(PTW32_INTERLOCKED_SIZEPTR)flag,\n\t\t\t\t\t\t(PTW32_INTERLOCKED_SIZE)-1,\n\t\t\t\t\t\t(PTW32_INTERLOCKED_SIZE)0);\n  if ((HANDLE)0 != e)\n    {\n      /* another thread has already stored an event handle in the flag */\n      SetEvent(e);\n    }\n}\n\n/*\n * ptw32_mcs_flag_set -- wait for notification from another.\n * \n * Store an event handle in the flag and wait on it if the flag has not been\n * set, and proceed without creating an event otherwise.\n */\nINLINE void \nptw32_mcs_flag_wait (HANDLE * flag)\n{\n  if ((PTW32_INTERLOCKED_LONG)0 ==\n        PTW32_INTERLOCKED_EXCHANGE_ADD_SIZE((PTW32_INTERLOCKED_SIZEPTR)flag,\n                                            (PTW32_INTERLOCKED_SIZE)0)) /* MBR fence */\n    {\n      /* the flag is not set. create event. */\n\n      HANDLE e = CreateEvent(NULL, PTW32_FALSE, PTW32_FALSE, NULL);\n\n      if ((PTW32_INTERLOCKED_SIZE)0 == PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(\n\t\t\t                  (PTW32_INTERLOCKED_SIZEPTR)flag,\n\t\t\t                  (PTW32_INTERLOCKED_SIZE)e,\n\t\t\t                  (PTW32_INTERLOCKED_SIZE)0))\n\t{\n\t  /* stored handle in the flag. wait on it now. */\n\t  WaitForSingleObject(e, INFINITE);\n\t}\n\n      CloseHandle(e);\n    }\n}\n\n/*\n * ptw32_mcs_lock_acquire -- acquire an MCS lock.\n * \n * See: \n * J. M. Mellor-Crummey and M. L. Scott.\n * Algorithms for Scalable Synchronization on Shared-Memory Multiprocessors.\n * ACM Transactions on Computer Systems, 9(1):21-65, Feb. 1991.\n */\n#if defined(PTW32_BUILD_INLINED)\nINLINE \n#endif /* PTW32_BUILD_INLINED */\nvoid \nptw32_mcs_lock_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node)\n{\n  ptw32_mcs_local_node_t  *pred;\n  \n  node->lock = lock;\n  node->nextFlag = 0;\n  node->readyFlag = 0;\n  node->next = 0; /* initially, no successor */\n  \n  /* queue for the lock */\n  pred = (ptw32_mcs_local_node_t *)PTW32_INTERLOCKED_EXCHANGE_PTR((PTW32_INTERLOCKED_PVOID_PTR)lock,\n\t\t\t\t\t\t\t\t  (PTW32_INTERLOCKED_PVOID)node);\n\n  if (0 != pred)\n    {\n      /* the lock was not free. link behind predecessor. */\n      pred->next = node;\n      ptw32_mcs_flag_set(&pred->nextFlag);\n      ptw32_mcs_flag_wait(&node->readyFlag);\n    }\n}\n\n/*\n * ptw32_mcs_lock_release -- release an MCS lock.\n * \n * See: \n * J. M. Mellor-Crummey and M. L. Scott.\n * Algorithms for Scalable Synchronization on Shared-Memory Multiprocessors.\n * ACM Transactions on Computer Systems, 9(1):21-65, Feb. 1991.\n */\n#if defined(PTW32_BUILD_INLINED)\nINLINE \n#endif /* PTW32_BUILD_INLINED */\nvoid \nptw32_mcs_lock_release (ptw32_mcs_local_node_t * node)\n{\n  ptw32_mcs_lock_t *lock = node->lock;\n  ptw32_mcs_local_node_t *next =\n    (ptw32_mcs_local_node_t *)\n      PTW32_INTERLOCKED_EXCHANGE_ADD_SIZE((PTW32_INTERLOCKED_SIZEPTR)&node->next, (PTW32_INTERLOCKED_SIZE)0); /* MBR fence */\n\n  if (0 == next)\n    {\n      /* no known successor */\n\n      if (node == (ptw32_mcs_local_node_t *)\n\t  PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR((PTW32_INTERLOCKED_PVOID_PTR)lock,\n\t\t\t\t\t\t (PTW32_INTERLOCKED_PVOID)0,\n\t\t\t\t\t\t (PTW32_INTERLOCKED_PVOID)node))\n\t{\n\t  /* no successor, lock is free now */\n\t  return;\n\t}\n  \n      /* A successor has started enqueueing behind us so wait for them to link to us */\n      ptw32_mcs_flag_wait(&node->nextFlag);\n      next = (ptw32_mcs_local_node_t *)\n\tPTW32_INTERLOCKED_EXCHANGE_ADD_SIZE((PTW32_INTERLOCKED_SIZEPTR)&node->next, (PTW32_INTERLOCKED_SIZE)0); /* MBR fence */\n    }\n\n  /* pass the lock */\n  ptw32_mcs_flag_set(&next->readyFlag);\n}\n\n/*\n  * ptw32_mcs_lock_try_acquire\n */\n#if defined(PTW32_BUILD_INLINED)\nINLINE \n#endif /* PTW32_BUILD_INLINED */\nint \nptw32_mcs_lock_try_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node)\n{\n  node->lock = lock;\n  node->nextFlag = 0;\n  node->readyFlag = 0;\n  node->next = 0; /* initially, no successor */\n\n  return ((PTW32_INTERLOCKED_PVOID)PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR((PTW32_INTERLOCKED_PVOID_PTR)lock,\n                                                        (PTW32_INTERLOCKED_PVOID)node,\n                                                        (PTW32_INTERLOCKED_PVOID)0)\n                                 == (PTW32_INTERLOCKED_PVOID)0) ? 0 : EBUSY;\n}\n\n/*\n * ptw32_mcs_node_transfer -- move an MCS lock local node, usually from thread\n * space to, for example, global space so that another thread can release\n * the lock on behalf of the current lock owner.\n *\n * Example: used in pthread_barrier_wait where we want the last thread out of\n * the barrier to release the lock owned by the last thread to enter the barrier\n * (the one that releases all threads but not necessarily the last to leave).\n *\n * Should only be called by the thread that has the lock.\n */\n#if defined(PTW32_BUILD_INLINED)\nINLINE \n#endif /* PTW32_BUILD_INLINED */\nvoid \nptw32_mcs_node_transfer (ptw32_mcs_local_node_t * new_node, ptw32_mcs_local_node_t * old_node)\n{\n  new_node->lock = old_node->lock;\n  new_node->nextFlag = 0; /* Not needed - used only in initial Acquire */\n  new_node->readyFlag = 0; /* Not needed - we were waiting on this */\n  new_node->next = 0;\n\n  if ((ptw32_mcs_local_node_t *)PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR((PTW32_INTERLOCKED_PVOID_PTR)new_node->lock,\n                                                                       (PTW32_INTERLOCKED_PVOID)new_node,\n                                                                       (PTW32_INTERLOCKED_PVOID)old_node)\n       != old_node)\n    {\n      /*\n       * A successor has queued after us, so wait for them to link to us\n       */\n      while (old_node->next == 0)\n        {\n          sched_yield();\n        }\n      new_node->next = old_node->next;\n    }\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_OLL_lock.c",
    "content": "/*\n * ptw32_OLL_lock.c\n *\n * Description:\n * This translation unit implements extended reader/writer queue-based locks.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n/*\n * About the OLL lock (Scalable Reader-Writer Lock):\n *\n * OLL locks are queue-based locks similar to the MCS queue lock, where the queue\n * nodes are local to the thread but where reader threads can enter the critical\n * section immediately without going through a central guard lock if there are\n * already readers holding the lock.\n *\n * Covered by United States Patent Application 20100241774 (Oracle)\n */\n\n#include \"pthread.h\"\n#include \"sched.h\"\n#include \"implement.h\"\n\n/*\n * C-SNZI support\n */\ntypedef union  ptw32_oll_counter_t_\t\tptw32_oll_counter_t;\ntypedef struct ptw32_oll_snziRoot_t_\t\tptw32_oll_snziRoot_t;\ntypedef struct ptw32_oll_snziNode_t_\t\tptw32_oll_snziNode_t;\ntypedef union  ptw32_oll_snziNodeOrRoot_t_\tptw32_oll_snziNodeOrRoot_t;\ntypedef struct ptw32_oll_queryResult_t_\t\tptw32_oll_queryResult_t;\ntypedef struct ptw32_oll_ticket_t_\t\tptw32_oll_ticket_t;\ntypedef struct ptw32_oll_csnzi_t_\t\tptw32_oll_csnzi_t;\n\nenum\n{\n  ptw32_archWidth\t= sizeof(size_t)*8,\n  ptw32_oll_countWidth\t= ptw32_archWidth-2\n};\n\n#define PTW32_OLL_MAXREADERS (((size_t)2<<(ptw32_oll_countWidth-1))-1)\n\nunion ptw32_oll_counter_t_\n{\n  size_t\tword\t: ptw32_archWidth;\n  struct\n  {\n    /*\n     * This needs to be a single word\n     *\n     *    ------------------------------------\n     *    | STATE |  ROOT  | COUNT (readers) |\n     *    ------------------------------------\n     *     63 / 31  62 / 30   61 / 29 .. 0\n     */\n    size_t\tcount\t: ptw32_oll_countWidth;\n    size_t\troot\t: 1;\t\t\t/* ROOT or NODE */\n    size_t\tstate\t: 1;\t\t\t/* OPEN or CLOSED (root only) */\n  } internal;\n};\n\nstruct ptw32_oll_snziRoot_t_\n{\n  /*\n   * \"counter\" must be at same offset in both\n   * ptw32_oll_snziNode_t and ptw32_oll_snziRoot_t\n   */\n  ptw32_oll_counter_t\tcounter;\n};\n\nenum\n{\n  ptw32_oll_snziRoot_open\t= 0,\n  ptw32_oll_snziRoot_closed\t= 1\n};\n\nenum\n{\n  ptw32_oll_snzi_root\t= 0,\n  ptw32_oll_snzi_node\t= 1\n};\n\n/*\n * Some common SNZI root whole-word states that can be used to set or compare\n * root words with a single operation.\n */\nptw32_oll_snziRoot_t ptw32_oll_snziRoot_openAndZero = {.counter.internal.count = 0,\n                                                       .counter.internal.root = ptw32_oll_snzi_root,\n                                                       .counter.internal.state = ptw32_oll_snziRoot_open};\nptw32_oll_snziRoot_t ptw32_oll_snziRoot_closedAndZero = {.counter.internal.count = 0,\n                                                         .counter.internal.root = ptw32_oll_snzi_root,\n                                                         .counter.internal.state = ptw32_oll_snziRoot_closed};\n\nstruct ptw32_oll_queryResult_t_\n{\n  BOOL\tnonZero;\n  BOOL\topen;\n};\n\nunion ptw32_oll_snziNodeOrRoot_t_\n{\n  ptw32_oll_snziRoot_t* rootPtr;\n  ptw32_oll_snziNode_t* nodePtr;\n};\n\nstruct ptw32_oll_snziNode_t_\n{\n  /* \"counter\" must be at same offset in both\n   * ptw32_oll_snziNode_t and ptw32_oll_snziRoot_t\n   */\n  ptw32_oll_counter_t\t\tcounter;\n  ptw32_oll_snziNodeOrRoot_t\tparentPtr;\n};\n\nstruct ptw32_oll_ticket_t_\n{\n  ptw32_oll_snziNodeOrRoot_t\tsnziNodeOrRoot;\n};\n\nptw32_oll_ticket_t ptw32_oll_ticket_null = {NULL};\n\nstruct ptw32_oll_csnzi_t_\n{\n  ptw32_oll_snziRoot_t\tproxyRoot;\n  ptw32_oll_snziNode_t\tleafs[];\n};\n\n/*\n * FOLL lock support\n */\n\ntypedef struct ptw32_foll_node_t_ ptw32_foll_node_t;\ntypedef struct ptw32_foll_local_t_ ptw32_foll_local_t;\ntypedef struct ptw32_foll_rwlock_t_ ptw32_foll_rwlock_t;\n\nenum\n{\n  ptw32_srwl_reader,\n  ptw32_srwl_writer\n};\n\nenum\n{\n  ptw32_srwl_free,\n  ptw32_srwl_in_use\n};\n\nstruct ptw32_foll_node_t_\n{\n  ptw32_foll_node_t*\tqNextPtr;\n  ptw32_oll_csnzi_t*\tcsnziPtr;\n  ptw32_foll_node_t*\tnextPtr;\n  int\t\t\tkind;\n  int\t\t\tallocState;\n  BOOL\t\t\tspin;\n};\n\nstruct ptw32_foll_local_t_\n{\n  ptw32_foll_node_t*\trNodePtr; // Default read node. Immutable\n  ptw32_foll_node_t*\twNodePtr; // Write node. Immutable.\n  ptw32_foll_node_t*\tdepartFromPtr; // List node we last arrived at.\n  ptw32_oll_ticket_t\tticket; // C-SNZI ticket\n};\n\nstruct ptw32_foll_rwlock_t_\n{\n  ptw32_foll_node_t*\ttailPtr;\n  ptw32_foll_node_t*\trNodesPtr; // Head of reader node\n};\n\n/*\n * ShouldArriveAtTree() returns true if:\n *   the compare_exchange in Arrive() fails too often under read access; or\n *   ??\n * Note that this is measured across all access to \n * this lock, not just this attempt, so that highly\n * read-contended locks will use C-SNZI. Lightly\n * read-contended locks can reduce memory usage and some\n * processing by using the root directly.\n */\nBOOL\nptw32_oll_ShouldArriveAtTree()\n{\n  return PTW32_FALSE;\n}\n\nsize_t\nptw32_oll_GetLeafForThread()\n{\n  return 0;\n}\n\n/*\n * Only readers call ptw32_oll_Arrive()\n *\n * Checks whether the C-SNZI state is OPEN, and if so,\n * increments the surplus of the C-SNZI by either directly\n * arriving at the root node, or calling TreeArrive on one\n * of the leaf nodes. Returns a ticket pointing to the node\n * that was arrived at. If the state is CLOSED, makes no\n * change and returns a ticket that contains no pointer.\n */\nptw32_oll_ticket_t\nptw32_oll_Arrive(ptw32_oll_csnzi_t* csnzi)\n{\n  for (;;)\n  {\n    ptw32_oll_ticket_t ticket;\n    ptw32_oll_snziRoot_t oldProxy = csnzi->proxyRoot;\n    if (oldProxy.counter.internal.state != ptw32_oll_snziRoot_open)\n    {\n      ticket.snziNodeOrRoot.rootPtr = (ptw32_oll_snziRoot_t*)NULL;\n      return ticket;\n    }\n    if (!ptw32_oll_ShouldArriveAtTree())\n    {\n      ptw32_oll_snziRoot_t newProxy = oldProxy;\n      newProxy.counter.internal.count++;\n      if (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(\n                (PTW32_INTERLOCKED_SIZEPTR)&csnzi->proxyRoot.counter,\n                (PTW32_INTERLOCKED_SIZE)newProxy.counter.word,\n                (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word)\n          == (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word)\n      {\n        /* Exchange successful */\n        ticket.snziNodeOrRoot.rootPtr = &csnzi->proxyRoot;\n        return ticket;\n      }\n    }\n    else\n    {\n      ptw32_oll_snziNode_t* leafPtr = &csnzi->leafs[ptw32_oll_GetLeafForThread()];\n      ticket.snziNodeOrRoot.nodePtr = (ptw32_oll_TreeArrive(leafPtr) ? leafPtr : (ptw32_oll_snziNode_t*)NULL);\n      return ticket;\n    }\n  }\n}\n\n/*\n * Decrements the C-SNZI surplus. Returns false iff the\n * resulting state is CLOSED and the surplus is zero.\n * Ticket must have been returned by an arrival. Must have\n * received this ticket from Arrive more times than Depart\n * has been called with the ticket. (Thus, the surplus\n * must be greater than zero.)\n */\nBOOL\nptw32_oll_Depart(ptw32_oll_ticket_t ticket)\n{\n  return ptw32_oll_TreeDepart(ticket.snziNodeOrRoot);\n}\n\n/*\n * Increments the C-SNZI surplus and returns true if the\n * C-SNZI is open or has a surplus. Calls TreeArrive\n * recursively on the node's parent if needed.\n * Otherwise, returns false without making any changes.\n */\nBOOL\nptw32_oll_TreeArrive(ptw32_oll_snziNodeOrRoot_t snziNodeOrRoot)\n{\n  if (snziNodeOrRoot.nodePtr->counter.internal.root != ptw32_oll_snzi_root)\n  {\n    /* Non-root node */\n    ptw32_oll_counter_t newCounter, oldCounter;\n    BOOL arrivedAtParent = PTW32_FALSE;\n    do\n    {\n      oldCounter = snziNodeOrRoot.nodePtr->counter;\n      if (0 == oldCounter.internal.count && !arrivedAtParent)\n      {\n        if (ptw32_oll_TreeArrive(snziNodeOrRoot.nodePtr->parentPtr))\n          arrivedAtParent = PTW32_TRUE;\n        else\n          return PTW32_FALSE;\n      }\n      newCounter = oldCounter;\n      newCounter.internal.count++;\n    } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(\n                  (PTW32_INTERLOCKED_SIZEPTR)&snziNodeOrRoot.nodePtr->counter,\n                  (PTW32_INTERLOCKED_SIZE)newCounter.word,\n                  (PTW32_INTERLOCKED_SIZE)oldCounter.word)\n             != (PTW32_INTERLOCKED_SIZE)oldCounter.word);\n    if (newCounter.internal.count != 0 && arrivedAtParent)\n      ptw32_oll_TreeDepart(snziNodeOrRoot.nodePtr->parentPtr);\n    return PTW32_TRUE;\n  }\n  else\n  {\n    /* Root node */\n    ptw32_oll_snziRoot_t newRoot, oldRoot;\n    do\n    {\n      oldRoot = *(ptw32_oll_snziRoot_t*)snziNodeOrRoot.rootPtr;\n      if (oldRoot.counter.word == ptw32_oll_snziRoot_closedAndZero.counter.word)\n        return PTW32_FALSE;\n      newRoot = oldRoot;\n      newRoot.counter.internal.count++;\n    } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(\n                   (PTW32_INTERLOCKED_SIZEPTR)&snziNodeOrRoot.rootPtr->counter,\n                   (PTW32_INTERLOCKED_SIZE)newRoot.counter.word,\n                   (PTW32_INTERLOCKED_SIZE)oldRoot.counter.word)\n             != (PTW32_INTERLOCKED_SIZE)oldRoot.counter.word);\n    return PTW32_TRUE;\n  }\n}\n\n/*\n * Decrements the C-SNZI surplus, calling TreeDepart\n * recursively on the node's parent if needed. Returns\n * false iff the resulting state of the C-SNZI is CLOSED\n * and the surplus is zero. Otherwise, returns true.\n */\nBOOL\nptw32_oll_TreeDepart(ptw32_oll_snziNodeOrRoot_t snziNodeOrRoot)\n{\n  if (snziNodeOrRoot.nodePtr->counter.internal.root != ptw32_oll_snzi_root)\n  {\n    /* Non-root node */\n    ptw32_oll_counter_t newCounter, oldCounter;\n    do\n    {\n      newCounter = oldCounter = snziNodeOrRoot.nodePtr->counter;\n      newCounter.internal.count--;\n    } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(\n                   (PTW32_INTERLOCKED_SIZEPTR)&snziNodeOrRoot.nodePtr->counter,\n                   (PTW32_INTERLOCKED_SIZE)newCounter.word,\n                   (PTW32_INTERLOCKED_SIZE)oldCounter.word)\n             != (PTW32_INTERLOCKED_SIZE)oldCounter.word);\n    return (0 == newCounter.internal.count)\n             ? ptw32_oll_TreeDepart(snziNodeOrRoot.nodePtr->parentPtr)\n             : PTW32_TRUE;\n  }\n  else\n  {\n    /* Root node */\n    ptw32_oll_snziRoot_t newRoot, oldRoot;\n    do\n    {\n      newRoot = oldRoot = *(ptw32_oll_snziRoot_t*)snziNodeOrRoot.rootPtr;\n      newRoot.counter.internal.count--;\n    } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(\n                   (PTW32_INTERLOCKED_SIZEPTR)&snziNodeOrRoot.rootPtr->counter,\n                   (PTW32_INTERLOCKED_SIZE)newRoot.counter.word,\n                   (PTW32_INTERLOCKED_SIZE)oldRoot.counter.word)\n             != (PTW32_INTERLOCKED_SIZE)oldRoot.counter.word);\n    return (newRoot.counter.word != ptw32_oll_snziRoot_closedAndZero.counter.word);\n  }\n}\n\n/*\n * Opens a C-SNZI object. Requires C-SNZI state to be\n * CLOSED and the surplus to be zero.\n */\nvoid\nptw32_oll_Open(ptw32_oll_csnzi_t* csnziPtr)\n{\n  csnziPtr->proxyRoot = ptw32_oll_snziRoot_openAndZero;\n}\n\n/*\n * Opens a C-SNZI object while atomically performing count\n * arrivals. Requires C-SNZI state to be CLOSED and\n * the surplus to be zero.\n */\nvoid\nptw32_oll_OpenWithArrivals(ptw32_oll_csnzi_t* csnziPtr, size_t count, BOOL close)\n{\n  csnziPtr->proxyRoot.counter.internal.count = count;\n  csnziPtr->proxyRoot.counter.internal.state = (close ? ptw32_oll_snziRoot_closed : ptw32_oll_snziRoot_open);\n}\n\n/*\n * Closes a C-SNZI object. Returns true iff the C-SNZI\n * state changed from OPEN to CLOSED and the surplus is\n * zero.\n */\nBOOL\nptw32_oll_Close(ptw32_oll_csnzi_t* csnziPtr)\n{\n  ptw32_oll_snziRoot_t newProxy, oldProxy;\n  do\n  {\n    oldProxy = csnziPtr->proxyRoot;\n    if (oldProxy.counter.internal.state != ptw32_oll_snziRoot_open)\n    {\n      return PTW32_FALSE;\n    }\n    newProxy = oldProxy;\n    newProxy.counter.internal.state = ptw32_oll_snziRoot_closed;\n  } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(\n                 (PTW32_INTERLOCKED_SIZEPTR)&csnziPtr->proxyRoot.counter,\n                 (PTW32_INTERLOCKED_SIZE)newProxy.counter.word,\n                 (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word)\n           != (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word);\n  return (newProxy.counter.word == ptw32_oll_snziRoot_closedAndZero.counter.word);\n}\n\n/*\n * Closes a C-SNZI if its surplus is zero. Otherwise, does\n * nothing. Returns true iff C-SNZI state changed from\n * OPEN to CLOSED.\n */\nBOOL\nptw32_oll_CloseIfEmpty(ptw32_oll_csnzi_t* csnziPtr)\n{\n  ptw32_oll_snziRoot_t newProxy, oldProxy;\n  do\n  {\n    oldProxy = csnziPtr->proxyRoot;\n    if (oldProxy.counter.word != ptw32_oll_snziRoot_openAndZero.counter.word)\n    {\n      return PTW32_FALSE;\n    }\n    newProxy = ptw32_oll_snziRoot_closedAndZero;\n  } while (PTW32_INTERLOCKED_COMPARE_EXCHANGE_SIZE(\n                 (PTW32_INTERLOCKED_SIZEPTR)&csnziPtr->proxyRoot.counter,\n                 (PTW32_INTERLOCKED_SIZE)newProxy.counter.word,\n                 (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word)\n           != (PTW32_INTERLOCKED_SIZE)oldProxy.counter.word);\n  return PTW32_TRUE;\n}\n\n/*\n * Returns whether the C-SNZI has a nonzero surplus and\n * whether the C-SNZI is open.\n * \"nonZero\" doesn't appear to be used anywhere in the algorithms.\n */\nptw32_oll_queryResult_t\nptw32_oll_Query(ptw32_oll_csnzi_t* csnziPtr)\n{\n  ptw32_oll_queryResult_t query;\n  ptw32_oll_snziRoot_t proxy = csnziPtr->proxyRoot;\n\n  query.nonZero = (proxy.counter.internal.count > 0);\n  query.open = (proxy.counter.internal.state == ptw32_oll_snziRoot_open);\n  return query;\n}\n\n/*\n * Returns whether the Arrive operation that returned\n * the ticket succeeded.\n */\nBOOL\nptw32_oll_Arrived(ptw32_oll_ticket_t t)\n{\n  return (t.snziNodeOrRoot.nodePtr != NULL);\n}\n\n/*\n * Constructs and returns a ticket that can be used to\n * depart from the root node.\n */\nptw32_oll_ticket_t\nptw32_oll_DirectTicket(ptw32_oll_csnzi_t* csnziPtr)\n{\n  ptw32_oll_ticket_t ticket;\n  ticket.snziNodeOrRoot.rootPtr = &csnziPtr->proxyRoot;\n  return ticket;\n}\n\n/* Scalable RW Locks */\n\ntypedef struct ptw32_srwl_rwlock_t_ ptw32_srwl_rwlock_t;\ntypedef struct ptw32_srwl_node_t_ ptw32_srwl_node_t;\ntypedef struct ptw32_srwl_local_t_ ptw32_srwl_local_t;\n\nenum\n{\n  ptw32_srwl_reader\t= 0,\n  ptw32_srwl_writer\t= 1\n};\n\nenum\n{\n  ptw32_srwl_free\t= 0,\n  ptw32_srwl_in_use\t= 1\n};\n\nstruct ptw32_srwl_rwlock_t_\n{\n  ptw32_srwl_node_t* tailPtr;\n  ptw32_srwl_node_t* readerNodePtr;\n};\n\nstruct ptw32_srwl_node_t_\n{\n  ptw32_srwl_node_t*\tqNextPtr;\n  ptw32_oll_csnzi_t*\tcsnziPtr;\n  ptw32_srwl_node_t*\tnextReaderPtr;\n  int\t\t\tkind;\t\t/* ptw32_srwl_reader, ptw32_srwl_writer */\n  int\t\t\tallocState;\t/* ptw32_srwl_free, ptw32_srwl_in_use */\n  BOOL\t\t\tspin;\n};\n\n/*\n * When a ptw32_srwl_local_t is instantiated the \"kind\" of each of\n * rNode and wNode must be set as appropriate. This is the only\n * time \"kind\" is set.\n */\nstruct ptw32_srwl_local_t_\n{\n  ptw32_srwl_node_t*\trNodePtr;\n  ptw32_srwl_node_t*\twNodePtr;\n  ptw32_srwl_node_t*\tdepartFromPtr;\n  ptw32_oll_ticket_t\tticket;\n};\n\n/* Allocates a new reader node. */\nptw32_srwl_node_t*\nptw32_srwl_AllocReaderNode(ptw32_srwl_local_t* local)\n{\n  ptw32_srwl_node_t* currNodePtr = local->rNodePtr;\n  for (;;)\n  {\n    if (currNodePtr->allocState == ptw32_srwl_free)\n    {\n      if (PTW32_INTERLOCKED_COMPARE_EXCHANGE_LONG(\n            (PTW32_INTERLOCKED_LONGPTR)&currNodePtr->allocState,\n            (PTW32_INTERLOCKED_LONG)ptw32_srwl_in_use,\n            (PTW32_INTERLOCKED_LONG)ptw32_srwl_free)\n          == (PTW32_INTERLOCKED_LONG)ptw32_srwl_in_use)\n      {\n        return currNodePtr;\n      }\n    }\n    currNodePtr = currNodePtr->next;\n  }\n}\n\n/*\n * Frees a reader node. Requires that its allocState\n * is ptw32_srwl_in_use.\n */\nvoid\nptw32_srwl_FreeReaderNode(ptw32_srwl_node_t* nodePtr)\n{\n  nodePtr->allocState := ptw32_srwl_free;\n}\n\nvoid\nptw32_srwl_WriterLock(ptw32_srwl_rwlock_t* lockPtr, ptw32_srwl_local_t* localPtr)\n{\n  oldTailPtr = (ptw32_srwl_rwlock_t*)PTW32_INTERLOCKED_EXCHANGE_PTR(\n                                       (PTW32_INTERLOCKED_PVOID_PTR)&lockPtr->tailPtr,\n                                       (PTW32_INTERLOCKED_PVOID)localPtr->wNodePtr);\n  if (oldTailPtr != NULL)\n  {\n    localPtr->wNodePtr->spin := PTW32_TRUE;\n    oldTailPtr->qNextPtr = localPtr->wNodePtr;\n    if (oldTailPtr->kind == ptw32_srwl_writer)\n    {\n      while (localPtr->wNodePtr->spin);\n    }\n    else\n    {\n      /* Wait until node is properly recycled */\n      while (ptw32_oll_Query(oldTailPtr->csnzi).open);\n      /*\n       * Close C-SNZI of previous reader node.\n       * If there are no readers to signal us, spin on\n       * previous node and free it before entering\n       * critical section.\n       */\n      if (ptw32_oll_Close(oldTailPtr->csnzi))\n      {\n        while (oldTailPtr->spin);\n        ptw32_srwl_FreeReaderNode(oldTailPtr);\n      }\n      else\n      {\n        while (localPtr->wNodePtr->spin);\n      }\n    }\n  }\n}\n\nvoid\nptw32_srwl_WriterUnlock(ptw32_srwl_rwlock_t* lockPtr, ptw32_srwl_local_t* localPtr)\n{\n  if (localPtr->wNodePtr->qNextPtr == NULL)\n  {\n    if (PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR(\n              (PTW32_INTERLOCKED_PVOIDPTR)&lockPtr->tailPtr,\n              (PTW32_INTERLOCKED_PVOID)NULL,\n              (PTW32_INTERLOCKED_PVOID)localPtr->wNodePtr)\n        == (PTW32_INTERLOCKED_PVOID)NULL)\n    {\n      return;\n    }\n    else\n    {\n      while (localPtr->wNodePtr->qNextPtr == NULL);\n    }\n  }\n  /* Clean up */\n  localPtr->wNodePtr->qNextPtr->spin = PTW32_FALSE;\n  localPtr->wNodePtr->qNextPtr = NULL;\n}\n\nvoid\nptw32_srwl_ReaderLock(ptw32_srwl_rwlock_t* lockPtr, ptw32_srwl_local_t* localPtr)\n{\n  ptw32_srwl_node_t* rNodePtr = NULL;\n  for (;;)\n  {\n    ptw32_srwl_node_t* tailPtr = lockPtr->tailPtr;\n    /* If no nodes are in the queue */\n    if (tailPtr == NULL)\n    {\n      if (rNodePtr == NULL)\n      {\n        rNodePtr = ptw32_srwl_AllocReaderNode(localPtr);\n      }\n      rNodePtr->spin = PTW32_FALSE;\n      if (PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR(\n                (PTW32_INTERLOCKED_PVOIDPTR)&lockPtr->tailPtr,\n                (PTW32_INTERLOCKED_PVOID)rNodePtr,\n                (PTW32_INTERLOCKED_PVOID)NULL)\n          == (PTW32_INTERLOCKED_PVOID)rNodePtr)\n      {\n        ptw32_oll_Open(rNodePtr->csnzi);\n        localPtr->ticket = ptw32_oll_Arrive(rNodePtr->csnzi);\n        if (ptw32_oll_Arrived(localPtr->ticket))\n        {\n          localPtr->departFromPtr = rNodePtr;\n          return;\n        }\n        /* Avoid reusing inserted node */\n        rNodePtr = NULL;\n      }\n    }\n    /* Otherwise, there is a node in the queue */\n    else\n    {\n      /* Is last node a writer node? */\n      if (tailPtr->kind == ptw32_srwl_writer)\n      {\n        if (rNodePtr == NULL)\n        {\n          rNodePtr = ptw32_srwl_AllocReaderNode(localPtr);\n        }\n        rNodePtr->spin = PTW32_TRUE;\n        if (PTW32_INTERLOCKED_COMPARE_EXCHANGE_PTR(\n                  (PTW32_INTERLOCKED_PVOIDPTR)&lockPtr->tailPtr,\n                  (PTW32_INTERLOCKED_PVOID)rNodePtr,\n                  (PTW32_INTERLOCKED_PVOID)tailPtr)\n            == (PTW32_INTERLOCKED_PVOID)rNodePtr)\n        {\n          tailPtr->qNextPtr = rNodePtr;\n          localPtr->ticket = ptw32_oll_Arrive(rNodePtr->csnzi);\n          if (ptw32_oll_Arrived(localPtr->ticket))\n          {\n            localPtr->departFromPtr = rNodePtr;\n            while (rNodePtr->spin);\n            return;\n          }\n          /* Avoid reusing inserted node */\n          rNodePtr = NULL;\n        }\n      }\n      /*\n       * Otherwise, last node is a reader node.\n       * (tailPtr->kind == ptw32_srwl_reader)\n       */\n      else\n      {\n        localPtr->ticket = ptw32_oll_Arrive(tailPtr->csnzi);\n        if (ptw32_oll_Arrived(localPtr->ticket))\n        {\n          if (rNodePtr != NULL)\n          {\n            ptw32_srwl_FreeReaderNode(rNodePtr);\n          }\n          localPtr->departFromPtr = tailPtr;\n          while (tailPtr->spin);\n          return;\n        }\n      }\n    }\n  }\n}\n\nvoid\nptw32_srwl_ReaderUnlock(ptw32_srwl_rwlock_t* lockPtr, ptw32_srwl_local_t* localPtr)\n{\n  if (ptw32_oll_Depart(localPtr->departFromPtr->csnzi, localPtr->ticket))\n  {\n    return;\n  }\n  /* Clean up */\n  localPtr->departFromPtr->qNextPtr->spin = PTW32_FALSE;\n  localPtr->departFromPtr->qNextPtr = NULL;\n  ptw32_srwl_FreeReaderNode(localPtr->departFromPtr);\n}\n\n\n#include <stdio.h>\n\nint main()\n{\n  printf(\"%lx\\n\", PTW32_OLL_MAXREADERS);\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_callUserDestroyRoutines.c",
    "content": "/*\n * ptw32_callUserDestroyRoutines.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n#if defined(__CLEANUP_CXX)\n# if defined(_MSC_VER)\n#  include <eh.h>\n# elif defined(__WATCOMC__)\n#  include <eh.h>\n#  include <exceptio.h>\n# else\n#  if defined(__GNUC__) && __GNUC__ < 3\n#    include <new.h>\n#  else\n#    include <new>\n     using\n       std::terminate;\n#  endif\n# endif\n#endif\n\nvoid\nptw32_callUserDestroyRoutines (pthread_t thread)\n     /*\n      * -------------------------------------------------------------------\n      * DOCPRIVATE\n      *\n      * This the routine runs through all thread keys and calls\n      * the destroy routines on the user's data for the current thread.\n      * It simulates the behaviour of POSIX Threads.\n      *\n      * PARAMETERS\n      *              thread\n      *                      an instance of pthread_t\n      *\n      * RETURNS\n      *              N/A\n      * -------------------------------------------------------------------\n      */\n{\n  ThreadKeyAssoc * assoc;\n\n  if (thread.p != NULL)\n    {\n      ptw32_mcs_local_node_t threadLock;\n      ptw32_mcs_local_node_t keyLock;\n      int assocsRemaining;\n      int iterations = 0;\n      ptw32_thread_t * sp = (ptw32_thread_t *) thread.p;\n\n      /*\n       * Run through all Thread<-->Key associations\n       * for the current thread.\n       *\n       * Do this process at most PTHREAD_DESTRUCTOR_ITERATIONS times.\n       */\n      do\n\t{\n\t  assocsRemaining = 0;\n\t  iterations++;\n\n\t  ptw32_mcs_lock_acquire(&(sp->threadLock), &threadLock);\n\t  /*\n\t   * The pointer to the next assoc is stored in the thread struct so that\n\t   * the assoc destructor in pthread_key_delete can adjust it\n\t   * if it deletes this assoc. This can happen if we fail to acquire\n\t   * both locks below, and are forced to release all of our locks,\n\t   * leaving open the opportunity for pthread_key_delete to get in\n\t   * before us.\n\t   */\n\t  sp->nextAssoc = sp->keys;\n\t  ptw32_mcs_lock_release(&threadLock);\n\n\t  for (;;)\n\t    {\n\t      void * value;\n\t      pthread_key_t k;\n\t      void (*destructor) (void *);\n\n\t      /*\n\t       * First we need to serialise with pthread_key_delete by locking\n\t       * both assoc guards, but in the reverse order to our convention,\n\t       * so we must be careful to avoid deadlock.\n\t       */\n\t      ptw32_mcs_lock_acquire(&(sp->threadLock), &threadLock);\n\n\t      if ((assoc = (ThreadKeyAssoc *)sp->nextAssoc) == NULL)\n\t\t{\n\t\t  /* Finished */\n\t\t  ptw32_mcs_lock_release(&threadLock);\n\t\t  break;\n\t\t}\n\t      else\n\t\t{\n\t\t  /*\n\t\t   * assoc->key must be valid because assoc can't change or be\n\t\t   * removed from our chain while we hold at least one lock. If\n\t\t   * the assoc was on our key chain then the key has not been\n\t\t   * deleted yet.\n\t\t   *\n\t\t   * Now try to acquire the second lock without deadlocking.\n\t\t   * If we fail, we need to relinquish the first lock and the\n\t\t   * processor and then try to acquire them all again.\n\t\t   */\n\t\t  if (ptw32_mcs_lock_try_acquire(&(assoc->key->keyLock), &keyLock) == EBUSY)\n\t\t    {\n\t\t      ptw32_mcs_lock_release(&threadLock);\n\t\t      Sleep(0);\n\t\t      /*\n\t\t       * Go around again.\n\t\t       * If pthread_key_delete has removed this assoc in the meantime,\n\t\t       * sp->nextAssoc will point to a new assoc.\n\t\t       */\n\t\t      continue;\n\t\t    }\n\t\t}\n\n\t      /* We now hold both locks */\n\n\t      sp->nextAssoc = assoc->nextKey;\n\n\t      /*\n\t       * Key still active; pthread_key_delete\n\t       * will block on these same mutexes before\n\t       * it can release actual key; therefore,\n\t       * key is valid and we can call the destroy\n\t       * routine;\n\t       */\n\t      k = assoc->key;\n\t      destructor = k->destructor;\n\t      value = TlsGetValue(k->key);\n\t      TlsSetValue (k->key, NULL);\n\n\t      // Every assoc->key exists and has a destructor\n\t      if (value != NULL && iterations <= PTHREAD_DESTRUCTOR_ITERATIONS)\n\t\t{\n\t\t  /*\n\t\t   * Unlock both locks before the destructor runs.\n\t\t   * POSIX says pthread_key_delete can be run from destructors,\n\t\t   * and that probably includes with this key as target.\n\t\t   * pthread_setspecific can also be run from destructors and\n\t\t   * also needs to be able to access the assocs.\n\t\t   */\n\t\t  ptw32_mcs_lock_release(&threadLock);\n\t\t  ptw32_mcs_lock_release(&keyLock);\n\n\t\t  assocsRemaining++;\n\n#if defined(__cplusplus)\n\n\t\t  try\n\t\t    {\n\t\t      /*\n\t\t       * Run the caller's cleanup routine.\n\t\t       */\n\t\t      destructor (value);\n\t\t    }\n\t\t  catch (...)\n\t\t    {\n\t\t      /*\n\t\t       * A system unexpected exception has occurred\n\t\t       * running the user's destructor.\n\t\t       * We get control back within this block in case\n\t\t       * the application has set up it's own terminate\n\t\t       * handler. Since we are leaving the thread we\n\t\t       * should not get any internal pthreads\n\t\t       * exceptions.\n\t\t       */\n\t\t      terminate ();\n\t\t    }\n\n#else /* __cplusplus */\n\n\t\t  /*\n\t\t   * Run the caller's cleanup routine.\n\t\t   */\n\t\t  destructor (value);\n\n#endif /* __cplusplus */\n\n\t\t}\n\t      else\n\t\t{\n\t\t  /*\n\t\t   * Remove association from both the key and thread chains\n\t\t   * and reclaim it's memory resources.\n\t\t   */\n\t\t  ptw32_tkAssocDestroy (assoc);\n\t\t  ptw32_mcs_lock_release(&threadLock);\n\t\t  ptw32_mcs_lock_release(&keyLock);\n\t\t}\n\t    }\n\t}\n      while (assocsRemaining);\n    }\n}\t\t\t\t/* ptw32_callUserDestroyRoutines */\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_calloc.c",
    "content": "/*\n * ptw32_calloc.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n#if defined(NEED_CALLOC)\nvoid *\nptw32_calloc (size_t n, size_t s)\n{\n  unsigned int m = n * s;\n  void *p;\n\n  p = malloc (m);\n  if (p == NULL)\n    return NULL;\n\n  memset (p, 0, m);\n\n  return p;\n}\n#endif\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_cond_check_need_init.c",
    "content": "/*\n * ptw32_cond_check_need_init.c\n *\n * Description:\n * This translation unit implements condition variables and their primitives.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nINLINE int\nptw32_cond_check_need_init (pthread_cond_t * cond)\n{\n  int result = 0;\n  ptw32_mcs_local_node_t node;\n\n  /*\n   * The following guarded test is specifically for statically\n   * initialised condition variables (via PTHREAD_OBJECT_INITIALIZER).\n   */\n  ptw32_mcs_lock_acquire(&ptw32_cond_test_init_lock, &node);\n\n  /*\n   * We got here possibly under race\n   * conditions. Check again inside the critical section.\n   * If a static cv has been destroyed, the application can\n   * re-initialise it only by calling pthread_cond_init()\n   * explicitly.\n   */\n  if (*cond == PTHREAD_COND_INITIALIZER)\n    {\n      result = pthread_cond_init (cond, NULL);\n    }\n  else if (*cond == NULL)\n    {\n      /*\n       * The cv has been destroyed while we were waiting to\n       * initialise it, so the operation that caused the\n       * auto-initialisation should fail.\n       */\n      result = EINVAL;\n    }\n\n  ptw32_mcs_lock_release(&node);\n\n  return result;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_getprocessors.c",
    "content": "/*\n * ptw32_getprocessors.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n/*\n * ptw32_getprocessors()\n *\n * Get the number of CPUs available to the process.\n *\n * If the available number of CPUs is 1 then pthread_spin_lock()\n * will block rather than spin if the lock is already owned.\n *\n * pthread_spin_init() calls this routine when initialising\n * a spinlock. If the number of available processors changes\n * (after a call to SetProcessAffinityMask()) then only\n * newly initialised spinlocks will notice.\n */\nint\nptw32_getprocessors (int *count)\n{\n  DWORD_PTR vProcessCPUs;\n  DWORD_PTR vSystemCPUs;\n  int result = 0;\n\n#if defined(NEED_PROCESS_AFFINITY_MASK)\n\n  *count = 1;\n\n#else\n\n  if (GetProcessAffinityMask (GetCurrentProcess (),\n\t\t\t      &vProcessCPUs, &vSystemCPUs))\n    {\n      DWORD_PTR bit;\n      int CPUs = 0;\n\n      for (bit = 1; bit != 0; bit <<= 1)\n\t{\n\t  if (vProcessCPUs & bit)\n\t    {\n\t      CPUs++;\n\t    }\n\t}\n      *count = CPUs;\n    }\n  else\n    {\n      result = EAGAIN;\n    }\n\n#endif\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_is_attr.c",
    "content": "/*\n * ptw32_is_attr.c\n *\n * Description:\n * This translation unit implements operations on thread attribute objects.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nint\nptw32_is_attr (const pthread_attr_t * attr)\n{\n  /* Return 0 if the attr object is valid, non-zero otherwise. */\n\n  return (attr == NULL ||\n\t  *attr == NULL || (*attr)->valid != PTW32_ATTR_VALID);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_mutex_check_need_init.c",
    "content": "/*\n * ptw32_mutex_check_need_init.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nstatic struct pthread_mutexattr_t_ ptw32_recursive_mutexattr_s =\n  {PTHREAD_PROCESS_PRIVATE, PTHREAD_MUTEX_RECURSIVE};\nstatic struct pthread_mutexattr_t_ ptw32_errorcheck_mutexattr_s =\n  {PTHREAD_PROCESS_PRIVATE, PTHREAD_MUTEX_ERRORCHECK};\nstatic pthread_mutexattr_t ptw32_recursive_mutexattr = &ptw32_recursive_mutexattr_s;\nstatic pthread_mutexattr_t ptw32_errorcheck_mutexattr = &ptw32_errorcheck_mutexattr_s;\n\n\nINLINE int\nptw32_mutex_check_need_init (pthread_mutex_t * mutex)\n{\n  register int result = 0;\n  register pthread_mutex_t mtx;\n  ptw32_mcs_local_node_t node;\n\n  ptw32_mcs_lock_acquire(&ptw32_mutex_test_init_lock, &node);\n\n  /*\n   * We got here possibly under race\n   * conditions. Check again inside the critical section\n   * and only initialise if the mutex is valid (not been destroyed).\n   * If a static mutex has been destroyed, the application can\n   * re-initialise it only by calling pthread_mutex_init()\n   * explicitly.\n   */\n  mtx = *mutex;\n\n  if (mtx == PTHREAD_MUTEX_INITIALIZER)\n    {\n      result = pthread_mutex_init (mutex, NULL);\n    }\n  else if (mtx == PTHREAD_RECURSIVE_MUTEX_INITIALIZER)\n    {\n      result = pthread_mutex_init (mutex, &ptw32_recursive_mutexattr);\n    }\n  else if (mtx == PTHREAD_ERRORCHECK_MUTEX_INITIALIZER)\n    {\n      result = pthread_mutex_init (mutex, &ptw32_errorcheck_mutexattr);\n    }\n  else if (mtx == NULL)\n    {\n      /*\n       * The mutex has been destroyed while we were waiting to\n       * initialise it, so the operation that caused the\n       * auto-initialisation should fail.\n       */\n      result = EINVAL;\n    }\n\n  ptw32_mcs_lock_release(&node);\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_new.c",
    "content": "/*\n * ptw32_new.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\npthread_t\nptw32_new (void)\n{\n  pthread_t t;\n  pthread_t nil = {NULL, 0};\n  ptw32_thread_t * tp;\n\n  /*\n   * If there's a reusable pthread_t then use it.\n   */\n  t = ptw32_threadReusePop ();\n\n  if (NULL != t.p)\n    {\n      tp = (ptw32_thread_t *) t.p;\n    }\n  else\n    {\n      /* No reuse threads available */\n      tp = (ptw32_thread_t *) calloc (1, sizeof(ptw32_thread_t));\n\n      if (tp == NULL)\n\t{\n\t  return nil;\n\t}\n\n      /* ptHandle.p needs to point to it's parent ptw32_thread_t. */\n      t.p = tp->ptHandle.p = tp;\n      t.x = tp->ptHandle.x = 0;\n    }\n\n  /* Set default state. */\n  tp->seqNumber = ++ptw32_threadSeqNumber;\n  tp->sched_priority = THREAD_PRIORITY_NORMAL;\n  tp->detachState = PTHREAD_CREATE_JOINABLE;\n  tp->cancelState = PTHREAD_CANCEL_ENABLE;\n  tp->cancelType = PTHREAD_CANCEL_DEFERRED;\n  tp->stateLock = 0;\n  tp->threadLock = 0;\n  tp->robustMxListLock = 0;\n  tp->robustMxList = NULL;\n  tp->cancelEvent = CreateEvent (0, (int) PTW32_TRUE,\t/* manualReset  */\n\t\t\t\t (int) PTW32_FALSE,\t/* setSignaled  */\n\t\t\t\t NULL);\n\n  if (tp->cancelEvent == NULL)\n    {\n      ptw32_threadReusePush (tp->ptHandle);\n      return nil;\n    }\n\n  return t;\n\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_processInitialize.c",
    "content": "/*\n * ptw32_processInitialize.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\nptw32_processInitialize (void)\n     /*\n      * ------------------------------------------------------\n      * DOCPRIVATE\n      *      This function performs process wide initialization for\n      *      the pthread library.\n      *\n      * PARAMETERS\n      *      N/A\n      *\n      * DESCRIPTION\n      *      This function performs process wide initialization for\n      *      the pthread library.\n      *      If successful, this routine sets the global variable\n      *      ptw32_processInitialized to TRUE.\n      *\n      * RESULTS\n      *              TRUE    if successful,\n      *              FALSE   otherwise\n      *\n      * ------------------------------------------------------\n      */\n{\n  if (ptw32_processInitialized)\n    {\n      /* \n       * Ignore if already initialized. this is useful for \n       * programs that uses a non-dll pthread\n       * library. Such programs must call ptw32_processInitialize() explicitly,\n       * since this initialization routine is automatically called only when\n       * the dll is loaded.\n       */\n      return PTW32_TRUE;\n    }\n\n  ptw32_processInitialized = PTW32_TRUE;\n\n  /*\n   * Initialize Keys\n   */\n  if ((pthread_key_create (&ptw32_selfThreadKey, NULL) != 0) ||\n      (pthread_key_create (&ptw32_cleanupKey, NULL) != 0))\n    {\n\n      ptw32_processTerminate ();\n    }\n\n  return (ptw32_processInitialized);\n\n}\t\t\t\t/* processInitialize */\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_processTerminate.c",
    "content": "/*\n * ptw32_processTerminate.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nvoid\nptw32_processTerminate (void)\n     /*\n      * ------------------------------------------------------\n      * DOCPRIVATE\n      *      This function performs process wide termination for\n      *      the pthread library.\n      *\n      * PARAMETERS\n      *      N/A\n      *\n      * DESCRIPTION\n      *      This function performs process wide termination for\n      *      the pthread library.\n      *      This routine sets the global variable\n      *      ptw32_processInitialized to FALSE\n      *\n      * RESULTS\n      *              N/A\n      *\n      * ------------------------------------------------------\n      */\n{\n  if (ptw32_processInitialized)\n    {\n      ptw32_thread_t * tp, * tpNext;\n      ptw32_mcs_local_node_t node;\n\n      if (ptw32_selfThreadKey != NULL)\n\t{\n\t  /*\n\t   * Release ptw32_selfThreadKey\n\t   */\n\t  pthread_key_delete (ptw32_selfThreadKey);\n\n\t  ptw32_selfThreadKey = NULL;\n\t}\n\n      if (ptw32_cleanupKey != NULL)\n\t{\n\t  /*\n\t   * Release ptw32_cleanupKey\n\t   */\n\t  pthread_key_delete (ptw32_cleanupKey);\n\n\t  ptw32_cleanupKey = NULL;\n\t}\n\n      ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node);\n\n      tp = ptw32_threadReuseTop;\n      while (tp != PTW32_THREAD_REUSE_EMPTY)\n\t{\n\t  tpNext = tp->prevReuse;\n\t  free (tp);\n\t  tp = tpNext;\n\t}\n\n      ptw32_mcs_lock_release(&node);\n\n      ptw32_processInitialized = PTW32_FALSE;\n    }\n\n}\t\t\t\t/* processTerminate */\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_relmillisecs.c",
    "content": "/*\n * ptw32_relmillisecs.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#if !defined(NEED_FTIME)\n#include <sys/timeb.h>\n#endif\n\n\n#if defined(PTW32_BUILD_INLINED)\nINLINE \n#endif /* PTW32_BUILD_INLINED */\nDWORD\nptw32_relmillisecs (const struct timespec * abstime)\n{\n  const int64_t NANOSEC_PER_MILLISEC = 1000000;\n  const int64_t MILLISEC_PER_SEC = 1000;\n  DWORD milliseconds;\n  int64_t tmpAbsMilliseconds;\n  int64_t tmpCurrMilliseconds;\n#if defined(NEED_FTIME)\n  struct timespec currSysTime;\n  FILETIME ft;\n  SYSTEMTIME st;\n#else /* ! NEED_FTIME */\n#if ( defined(_MSC_VER) && _MSC_VER >= 1300 ) || \\\n    ( (defined(__MINGW64__) || defined(__MINGW32__)) && __MSVCRT_VERSION__ >= 0x0601 )\n  struct __timeb64 currSysTime;\n#else\n  struct _timeb currSysTime;\n#endif\n#endif /* NEED_FTIME */\n\n\n  /* \n   * Calculate timeout as milliseconds from current system time. \n   */\n\n  /*\n   * subtract current system time from abstime in a way that checks\n   * that abstime is never in the past, or is never equivalent to the\n   * defined INFINITE value (0xFFFFFFFF).\n   *\n   * Assume all integers are unsigned, i.e. cannot test if less than 0.\n   */\n  tmpAbsMilliseconds =  (int64_t)abstime->tv_sec * MILLISEC_PER_SEC;\n  tmpAbsMilliseconds += ((int64_t)abstime->tv_nsec + (NANOSEC_PER_MILLISEC/2)) / NANOSEC_PER_MILLISEC;\n\n  /* get current system time */\n\n#if defined(NEED_FTIME)\n\n  GetSystemTime(&st);\n  SystemTimeToFileTime(&st, &ft);\n  /*\n   * GetSystemTimeAsFileTime(&ft); would be faster,\n   * but it does not exist on WinCE\n   */\n\n  ptw32_filetime_to_timespec(&ft, &currSysTime);\n\n  tmpCurrMilliseconds = (int64_t)currSysTime.tv_sec * MILLISEC_PER_SEC;\n  tmpCurrMilliseconds += ((int64_t)currSysTime.tv_nsec + (NANOSEC_PER_MILLISEC/2))\n\t\t\t   / NANOSEC_PER_MILLISEC;\n\n#else /* ! NEED_FTIME */\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n  _ftime64_s(&currSysTime);\n#elif ( defined(_MSC_VER) && _MSC_VER >= 1300 ) || \\\n      ( (defined(__MINGW64__) || defined(__MINGW32__)) && __MSVCRT_VERSION__ >= 0x0601 )\n  _ftime64(&currSysTime);\n#else\n  _ftime(&currSysTime);\n#endif\n\n  tmpCurrMilliseconds = (int64_t) currSysTime.time * MILLISEC_PER_SEC;\n  tmpCurrMilliseconds += (int64_t) currSysTime.millitm;\n\n#endif /* NEED_FTIME */\n\n  if (tmpAbsMilliseconds > tmpCurrMilliseconds)\n    {\n      milliseconds = (DWORD) (tmpAbsMilliseconds - tmpCurrMilliseconds);\n      if (milliseconds == INFINITE)\n        {\n          /* Timeouts must be finite */\n          milliseconds--;\n        }\n    }\n  else\n    {\n      /* The abstime given is in the past */\n      milliseconds = 0;\n    }\n\n  return milliseconds;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_reuse.c",
    "content": "/*\n * ptw32_threadReuse.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n/*\n * How it works:\n * A pthread_t is a struct (2x32 bit scalar types on IA-32, 2x64 bit on IA-64)\n * which is normally passed/returned by value to/from pthreads routines.\n * Applications are therefore storing a copy of the struct as it is at that\n * time.\n *\n * The original pthread_t struct plus all copies of it contain the address of\n * the thread state struct ptw32_thread_t_ (p), plus a reuse counter (x). Each\n * ptw32_thread_t contains the original copy of it's pthread_t.\n * Once malloced, a ptw32_thread_t_ struct is not freed until the process exits.\n * \n * The thread reuse stack is a simple LILO stack managed through a singly\n * linked list element in the ptw32_thread_t.\n *\n * Each time a thread is destroyed, the ptw32_thread_t address is pushed onto the\n * reuse stack after it's ptHandle's reuse counter has been incremented.\n * \n * The following can now be said from this:\n * - two pthread_t's are identical if their ptw32_thread_t reference pointers\n * are equal and their reuse counters are equal. That is,\n *\n *   equal = (a.p == b.p && a.x == b.x)\n *\n * - a pthread_t copy refers to a destroyed thread if the reuse counter in\n * the copy is not equal to the reuse counter in the original.\n *\n *   threadDestroyed = (copy.x != ((ptw32_thread_t *)copy.p)->ptHandle.x)\n *\n */\n\n/*\n * Pop a clean pthread_t struct off the reuse stack.\n */\npthread_t\nptw32_threadReusePop (void)\n{\n  pthread_t t = {NULL, 0};\n  ptw32_mcs_local_node_t node;\n\n  ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node);\n\n  if (PTW32_THREAD_REUSE_EMPTY != ptw32_threadReuseTop)\n    {\n      ptw32_thread_t * tp;\n\n      tp = ptw32_threadReuseTop;\n\n      ptw32_threadReuseTop = tp->prevReuse;\n\n      if (PTW32_THREAD_REUSE_EMPTY == ptw32_threadReuseTop)\n        {\n          ptw32_threadReuseBottom = PTW32_THREAD_REUSE_EMPTY;\n        }\n\n      tp->prevReuse = NULL;\n\n      t = tp->ptHandle;\n    }\n\n  ptw32_mcs_lock_release(&node);\n\n  return t;\n\n}\n\n/*\n * Push a clean pthread_t struct onto the reuse stack.\n * Must be re-initialised when reused.\n * All object elements (mutexes, events etc) must have been either\n * detroyed before this, or never initialised.\n */\nvoid\nptw32_threadReusePush (pthread_t thread)\n{\n  ptw32_thread_t * tp = (ptw32_thread_t *) thread.p;\n  pthread_t t;\n  ptw32_mcs_local_node_t node;\n\n  ptw32_mcs_lock_acquire(&ptw32_thread_reuse_lock, &node);\n\n  t = tp->ptHandle;\n  memset(tp, 0, sizeof(ptw32_thread_t));\n\n  /* Must restore the original POSIX handle that we just wiped. */\n  tp->ptHandle = t;\n\n  /* Bump the reuse counter now */\n#if defined(PTW32_THREAD_ID_REUSE_INCREMENT)\n  tp->ptHandle.x += PTW32_THREAD_ID_REUSE_INCREMENT;\n#else\n  tp->ptHandle.x++;\n#endif\n\n  tp->state = PThreadStateReuse;\n\n  tp->prevReuse = PTW32_THREAD_REUSE_EMPTY;\n\n  if (PTW32_THREAD_REUSE_EMPTY != ptw32_threadReuseBottom)\n    {\n      ptw32_threadReuseBottom->prevReuse = tp;\n    }\n  else\n    {\n      ptw32_threadReuseTop = tp;\n    }\n\n  ptw32_threadReuseBottom = tp;\n\n  ptw32_mcs_lock_release(&node);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_rwlock_cancelwrwait.c",
    "content": "/*\n * ptw32_rwlock_cancelwrwait.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nvoid\nptw32_rwlock_cancelwrwait (void *arg)\n{\n  pthread_rwlock_t rwl = (pthread_rwlock_t) arg;\n\n  rwl->nSharedAccessCount = -rwl->nCompletedSharedAccessCount;\n  rwl->nCompletedSharedAccessCount = 0;\n\n  (void) pthread_mutex_unlock (&(rwl->mtxSharedAccessCompleted));\n  (void) pthread_mutex_unlock (&(rwl->mtxExclusiveAccess));\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_rwlock_check_need_init.c",
    "content": "/*\n * pthread_rwlock_check_need_init.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\nINLINE int\nptw32_rwlock_check_need_init (pthread_rwlock_t * rwlock)\n{\n  int result = 0;\n  ptw32_mcs_local_node_t node;\n\n  /*\n   * The following guarded test is specifically for statically\n   * initialised rwlocks (via PTHREAD_RWLOCK_INITIALIZER).\n   */\n  ptw32_mcs_lock_acquire(&ptw32_rwlock_test_init_lock, &node);\n\n  /*\n   * We got here possibly under race\n   * conditions. Check again inside the critical section\n   * and only initialise if the rwlock is valid (not been destroyed).\n   * If a static rwlock has been destroyed, the application can\n   * re-initialise it only by calling pthread_rwlock_init()\n   * explicitly.\n   */\n  if (*rwlock == PTHREAD_RWLOCK_INITIALIZER)\n    {\n      result = pthread_rwlock_init (rwlock, NULL);\n    }\n  else if (*rwlock == NULL)\n    {\n      /*\n       * The rwlock has been destroyed while we were waiting to\n       * initialise it, so the operation that caused the\n       * auto-initialisation should fail.\n       */\n      result = EINVAL;\n    }\n\n  ptw32_mcs_lock_release(&node);\n\n  return result;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_semwait.c",
    "content": "/*\n * ptw32_semwait.c\n *\n * Description:\n * This translation unit implements mutual exclusion (mutex) primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#if !defined(_UWIN)\n/*#   include <process.h> */\n#endif\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\nptw32_semwait (sem_t * sem)\n     /*\n      * ------------------------------------------------------\n      * DESCRIPTION\n      *      This function waits on a POSIX semaphore. If the\n      *      semaphore value is greater than zero, it decreases\n      *      its value by one. If the semaphore value is zero, then\n      *      the calling thread (or process) is blocked until it can\n      *      successfully decrease the value.\n      *\n      *      Unlike sem_wait(), this routine is non-cancelable.\n      *\n      * RESULTS\n      *              0               successfully decreased semaphore,\n      *              -1              failed, error in errno.\n      * ERRNO\n      *              EINVAL          'sem' is not a valid semaphore,\n      *              ENOSYS          semaphores are not supported,\n      *              EINTR           the function was interrupted by a signal,\n      *              EDEADLK         a deadlock condition was detected.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  sem_t s = *sem;\n\n  if (s == NULL)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      if ((result = pthread_mutex_lock (&s->lock)) == 0)\n        {\n          int v;\n\n\t  /* See sem_destroy.c\n\t   */\n\t  if (*sem == NULL)\n\t    {\n\t      (void) pthread_mutex_unlock (&s->lock);\n\t      errno = EINVAL;\n\t      return -1;\n\t    }\n\n          v = --s->value;\n          (void) pthread_mutex_unlock (&s->lock);\n\n          if (v < 0)\n            {\n              /* Must wait */\n              if (WaitForSingleObject (s->sem, INFINITE) == WAIT_OBJECT_0)\n\t\t{\n#if defined(NEED_SEM)\n\t\t  if (pthread_mutex_lock (&s->lock) == 0)\n\t\t    {\n        \t      if (*sem == NULL)\n        \t        {\n        \t          (void) pthread_mutex_unlock (&s->lock);\n        \t          errno = EINVAL;\n        \t          return -1;\n        \t        }\n\n\t\t      if (s->leftToUnblock > 0)\n\t\t\t{\n\t\t\t  --s->leftToUnblock;\n\t\t\t  SetEvent(s->sem);\n\t\t\t}\n\t\t      (void) pthread_mutex_unlock (&s->lock);\n\t\t    }\n#endif\n\t\t  return 0;\n\t\t}\n            }\n          else\n\t    {\n\t      return 0;\n\t    }\n        }\n    }\n\n  if (result != 0)\n    {\n      errno = result;\n      return -1;\n    }\n\n  return 0;\n\n}\t\t\t\t/* ptw32_semwait */\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_spinlock_check_need_init.c",
    "content": "/*\n * ptw32_spinlock_check_need_init.c\n *\n * Description:\n * This translation unit implements spin lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nINLINE int\nptw32_spinlock_check_need_init (pthread_spinlock_t * lock)\n{\n  int result = 0;\n  ptw32_mcs_local_node_t node;\n\n  /*\n   * The following guarded test is specifically for statically\n   * initialised spinlocks (via PTHREAD_SPINLOCK_INITIALIZER).\n   */\n  ptw32_mcs_lock_acquire(&ptw32_spinlock_test_init_lock, &node);\n\n  /*\n   * We got here possibly under race\n   * conditions. Check again inside the critical section\n   * and only initialise if the spinlock is valid (not been destroyed).\n   * If a static spinlock has been destroyed, the application can\n   * re-initialise it only by calling pthread_spin_init()\n   * explicitly.\n   */\n  if (*lock == PTHREAD_SPINLOCK_INITIALIZER)\n    {\n      result = pthread_spin_init (lock, PTHREAD_PROCESS_PRIVATE);\n    }\n  else if (*lock == NULL)\n    {\n      /*\n       * The spinlock has been destroyed while we were waiting to\n       * initialise it, so the operation that caused the\n       * auto-initialisation should fail.\n       */\n      result = EINVAL;\n    }\n\n  ptw32_mcs_lock_release(&node);\n\n  return (result);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_threadDestroy.c",
    "content": "/*\n * ptw32_threadDestroy.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nvoid\nptw32_threadDestroy (pthread_t thread)\n{\n  ptw32_thread_t * tp = (ptw32_thread_t *) thread.p;\n  ptw32_thread_t threadCopy;\n\n  if (tp != NULL)\n    {\n      /*\n       * Copy thread state so that the thread can be atomically NULLed.\n       */\n      memcpy (&threadCopy, tp, sizeof (threadCopy));\n\n      /*\n       * Thread ID structs are never freed. They're NULLed and reused.\n       * This also sets the thread to PThreadStateInitial (invalid).\n       */\n      ptw32_threadReusePush (thread);\n\n      /* Now work on the copy. */\n      if (threadCopy.cancelEvent != NULL)\n\t{\n\t  CloseHandle (threadCopy.cancelEvent);\n\t}\n\n#if ! (defined(__MINGW64__) || defined(__MINGW32__)) || defined (__MSVCRT__) || defined (__DMC__)\n      /*\n       * See documentation for endthread vs endthreadex.\n       */\n      if (threadCopy.threadH != 0)\n\t{\n\t  CloseHandle (threadCopy.threadH);\n\t}\n#endif\n\n    }\n}\t\t\t\t/* ptw32_threadDestroy */\n\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_threadStart.c",
    "content": "/*\n * ptw32_threadStart.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include <stdio.h>\n\n#if defined(__CLEANUP_C)\n# include <setjmp.h>\n#endif\n\n#if defined(__CLEANUP_SEH)\n\nstatic DWORD\nExceptionFilter (EXCEPTION_POINTERS * ep, DWORD * ei)\n{\n  switch (ep->ExceptionRecord->ExceptionCode)\n    {\n    case EXCEPTION_PTW32_SERVICES:\n      {\n\tDWORD param;\n\tDWORD numParams = ep->ExceptionRecord->NumberParameters;\n\n\tnumParams = (numParams > 3) ? 3 : numParams;\n\n\tfor (param = 0; param < numParams; param++)\n\t  {\n\t    ei[param] = ep->ExceptionRecord->ExceptionInformation[param];\n\t  }\n\n\treturn EXCEPTION_EXECUTE_HANDLER;\n\tbreak;\n      }\n    default:\n      {\n\t/*\n\t * A system unexpected exception has occurred running the user's\n\t * routine. We need to cleanup before letting the exception\n\t * out of thread scope.\n\t */\n\tpthread_t self = pthread_self ();\n\n\tptw32_callUserDestroyRoutines (self);\n\n\treturn EXCEPTION_CONTINUE_SEARCH;\n\tbreak;\n      }\n    }\n}\n\n#elif defined(__CLEANUP_CXX)\n\n#if defined(_MSC_VER)\n# include <eh.h>\n#elif defined(__WATCOMC__)\n# include <eh.h>\n# include <exceptio.h>\ntypedef terminate_handler\n  terminate_function;\n#else\n# if defined(__GNUC__) && __GNUC__ < 3\n#   include <new.h>\n# else\n#   include <new>\nusing\n  std::terminate_handler;\nusing\n  std::terminate;\nusing\n  std::set_terminate;\n# endif\ntypedef terminate_handler\n  terminate_function;\n#endif\n\nstatic terminate_function\n  ptw32_oldTerminate;\n\nvoid\nptw32_terminate ()\n{\n  set_terminate (ptw32_oldTerminate);\n  (void) pthread_win32_thread_detach_np ();\n  terminate ();\n}\n\n#endif\n\n#if ! (defined(__MINGW64__) || defined(__MINGW32__)) || (defined (__MSVCRT__) && ! defined (__DMC__))\nunsigned\n  __stdcall\n#else\nvoid\n#endif\nptw32_threadStart (void *vthreadParms)\n{\n  ThreadParms * threadParms = (ThreadParms *) vthreadParms;\n  pthread_t self;\n  ptw32_thread_t * sp;\n  void * (PTW32_CDECL *start) (void *);\n  void * arg;\n\n#if defined(__CLEANUP_SEH)\n  DWORD\n  ei[] = { 0, 0, 0 };\n#endif\n\n#if defined(__CLEANUP_C)\n  int setjmp_rc;\n#endif\n\n  ptw32_mcs_local_node_t stateLock;\n  void * status = (void *) 0;\n\n  self = threadParms->tid;\n  sp = (ptw32_thread_t *) self.p;\n  start = threadParms->start;\n  arg = threadParms->arg;\n\n  free (threadParms);\n\n#if (defined(__MINGW64__) || defined(__MINGW32__)) && ! defined (__MSVCRT__)\n  /*\n   * beginthread does not return the thread id and is running\n   * before it returns us the thread handle, and so we do it here.\n   */\n  sp->thread = GetCurrentThreadId ();\n  /*\n   * Here we're using stateLock as a general-purpose lock\n   * to make the new thread wait until the creating thread\n   * has the new handle.\n   */\n  ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock);\n  pthread_setspecific (ptw32_selfThreadKey, sp);\n#else\n  pthread_setspecific (ptw32_selfThreadKey, sp);\n  ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock);\n#endif\n\n  sp->state = PThreadStateRunning;\n  ptw32_mcs_lock_release (&stateLock);\n\n#if defined(__CLEANUP_SEH)\n\n  __try\n  {\n    /*\n     * Run the caller's routine;\n     */\n    status = sp->exitStatus = (*start) (arg);\n    sp->state = PThreadStateExiting;\n\n#if defined(_UWIN)\n    if (--pthread_count <= 0)\n      exit (0);\n#endif\n\n  }\n  __except (ExceptionFilter (GetExceptionInformation (), ei))\n  {\n    switch (ei[0])\n      {\n      case PTW32_EPS_CANCEL:\n\tstatus = sp->exitStatus = PTHREAD_CANCELED;\n#if defined(_UWIN)\n\tif (--pthread_count <= 0)\n\t  exit (0);\n#endif\n\tbreak;\n      case PTW32_EPS_EXIT:\n\tstatus = sp->exitStatus;\n\tbreak;\n      default:\n\tstatus = sp->exitStatus = PTHREAD_CANCELED;\n\tbreak;\n      }\n  }\n\n#else /* __CLEANUP_SEH */\n\n#if defined(__CLEANUP_C)\n\n  setjmp_rc = setjmp (sp->start_mark);\n\n  if (0 == setjmp_rc)\n    {\n\n      /*\n       * Run the caller's routine;\n       */\n      status = sp->exitStatus = (*start) (arg);\n      sp->state = PThreadStateExiting;\n    }\n  else\n    {\n      switch (setjmp_rc)\n\t{\n\tcase PTW32_EPS_CANCEL:\n\t  status = sp->exitStatus = PTHREAD_CANCELED;\n\t  break;\n\tcase PTW32_EPS_EXIT:\n\t  status = sp->exitStatus;\n\t  break;\n\tdefault:\n\t  status = sp->exitStatus = PTHREAD_CANCELED;\n\t  break;\n\t}\n    }\n\n#else /* __CLEANUP_C */\n\n#if defined(__CLEANUP_CXX)\n\n  ptw32_oldTerminate = set_terminate (&ptw32_terminate);\n\n  try\n  {\n    /*\n     * Run the caller's routine in a nested try block so that we\n     * can run the user's terminate function, which may call\n     * pthread_exit() or be canceled.\n     */\n    try\n    {\n      status = sp->exitStatus = (*start) (arg);\n      sp->state = PThreadStateExiting;\n    }\n    catch (ptw32_exception &)\n    {\n      /*\n       * Pass these through to the outer block.\n       */\n      throw;\n    }\n    catch (...)\n    {\n      /*\n       * We want to run the user's terminate function if supplied.\n       * That function may call pthread_exit() or be canceled, which will\n       * be handled by the outer try block.\n       *\n       * ptw32_terminate() will be called if there is no user\n       * supplied function.\n       */\n      terminate_function\n\tterm_func = set_terminate (0);\n      set_terminate (term_func);\n\n      if (term_func != 0)\n\t{\n\t  term_func ();\n\t}\n      throw;\n    }\n  }\n  catch (ptw32_exception_cancel &)\n  {\n    /*\n     * Thread was canceled.\n     */\n    status = sp->exitStatus = PTHREAD_CANCELED;\n  }\n  catch (ptw32_exception_exit &)\n  {\n    /*\n     * Thread was exited via pthread_exit().\n     */\n    status = sp->exitStatus;\n  }\n  catch (...)\n  {\n    /*\n     * A system unexpected exception has occurred running the user's\n     * terminate routine. We get control back within this block\n     * and exit with a substitute status. If the thread was not\n     * cancelled then this indicates the unhandled exception.\n     */\n    status = sp->exitStatus = PTHREAD_CANCELED;\n  }\n\n  (void) set_terminate (ptw32_oldTerminate);\n\n#else\n\n#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined.\n\n#endif /* __CLEANUP_CXX */\n#endif /* __CLEANUP_C */\n#endif /* __CLEANUP_SEH */\n\n#if defined(PTW32_STATIC_LIB)\n  /*\n   * We need to cleanup the pthread now if we have\n   * been statically linked, in which case the cleanup\n   * in dllMain won't get done. Joinable threads will\n   * only be partially cleaned up and must be fully cleaned\n   * up by pthread_join() or pthread_detach().\n   *\n   * Note: if this library has been statically linked,\n   * implicitly created pthreads (those created\n   * for Win32 threads which have called pthreads routines)\n   * must be cleaned up explicitly by the application\n   * (by calling pthread_win32_thread_detach_np()).\n   * For the dll, dllMain will do the cleanup automatically.\n   */\n  (void) pthread_win32_thread_detach_np ();\n#endif\n\n#if ! (defined(__MINGW64__) || defined(__MINGW32__)) || defined (__MSVCRT__) || defined (__DMC__)\n  _endthreadex ((unsigned)(size_t) status);\n#else\n  _endthread ();\n#endif\n\n  /*\n   * Never reached.\n   */\n\n#if ! (defined(__MINGW64__) || defined(__MINGW32__)) || defined (__MSVCRT__) || defined (__DMC__)\n  return (unsigned)(size_t) status;\n#endif\n\n}\t\t\t\t/* ptw32_threadStart */\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_throw.c",
    "content": "/*\n * ptw32_throw.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n#if defined(__CLEANUP_C)\n# include <setjmp.h>\n#endif\n\n/*\n * ptw32_throw\n *\n * All canceled and explicitly exited POSIX threads go through\n * here. This routine knows how to exit both POSIX initiated threads and\n * 'implicit' POSIX threads for each of the possible language modes (C,\n * C++, and SEH).\n */\n#if defined(_MSC_VER)\n/*\n * Ignore the warning:\n * \"C++ exception specification ignored except to indicate that\n * the function is not __declspec(nothrow).\"\n */\n#pragma warning(disable:4290)\n#endif\nvoid\nptw32_throw (DWORD exception)\n#if defined(__CLEANUP_CXX)\n  throw(ptw32_exception_cancel,ptw32_exception_exit)\n#endif\n{\n  /*\n   * Don't use pthread_self() to avoid creating an implicit POSIX thread handle\n   * unnecessarily.\n   */\n  ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey);\n\n#if defined(__CLEANUP_SEH)\n  DWORD exceptionInformation[3];\n#endif\n\n  sp->state = PThreadStateExiting;\n\n  if (exception != PTW32_EPS_CANCEL && exception != PTW32_EPS_EXIT)\n    {\n      /* Should never enter here */\n      exit (1);\n    }\n\n  if (NULL == sp || sp->implicit)\n    {\n      /*\n       * We're inside a non-POSIX initialised Win32 thread\n       * so there is no point to jump or throw back to. Just do an\n       * explicit thread exit here after cleaning up POSIX\n       * residue (i.e. cleanup handlers, POSIX thread handle etc).\n       */\n#if ! (defined(__MINGW64__) || defined(__MINGW32__)) || defined (__MSVCRT__) || defined (__DMC__)\n      unsigned exitCode = 0;\n\n      switch (exception)\n\t{\n\tcase PTW32_EPS_CANCEL:\n\t  exitCode = (unsigned)(size_t) PTHREAD_CANCELED;\n\t  break;\n\tcase PTW32_EPS_EXIT:\n\t  if (NULL != sp)\n\t    {\n\t      exitCode = (unsigned)(size_t) sp->exitStatus;\n\t    }\n\t  break;\n\t}\n#endif\n\n#if defined(PTW32_STATIC_LIB)\n\n      pthread_win32_thread_detach_np ();\n\n#endif\n\n#if ! (defined(__MINGW64__) || defined(__MINGW32__)) || defined (__MSVCRT__) || defined (__DMC__)\n      _endthreadex (exitCode);\n#else\n      _endthread ();\n#endif\n\n    }\n\n#if defined(__CLEANUP_SEH)\n\n\n  exceptionInformation[0] = (DWORD) (exception);\n  exceptionInformation[1] = (DWORD) (0);\n  exceptionInformation[2] = (DWORD) (0);\n\n  RaiseException (EXCEPTION_PTW32_SERVICES, 0, 3, exceptionInformation);\n\n#else /* __CLEANUP_SEH */\n\n#if defined(__CLEANUP_C)\n\n  ptw32_pop_cleanup_all (1);\n  longjmp (sp->start_mark, exception);\n\n#else /* __CLEANUP_C */\n\n#if defined(__CLEANUP_CXX)\n\n  switch (exception)\n    {\n    case PTW32_EPS_CANCEL:\n      throw ptw32_exception_cancel ();\n      break;\n    case PTW32_EPS_EXIT:\n      throw ptw32_exception_exit ();\n      break;\n    }\n\n#else\n\n#error ERROR [__FILE__, line __LINE__]: Cleanup type undefined.\n\n#endif /* __CLEANUP_CXX */\n\n#endif /* __CLEANUP_C */\n\n#endif /* __CLEANUP_SEH */\n\n  /* Never reached */\n}\n\n\nvoid\nptw32_pop_cleanup_all (int execute)\n{\n  while (NULL != ptw32_pop_cleanup (execute))\n    {\n    }\n}\n\n\nDWORD\nptw32_get_exception_services_code (void)\n{\n#if defined(__CLEANUP_SEH)\n\n  return EXCEPTION_PTW32_SERVICES;\n\n#else\n\n  return (DWORD)0;\n\n#endif\n}\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_timespec.c",
    "content": "/*\n * ptw32_timespec.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n#if defined(NEED_FTIME)\n\n/*\n * time between jan 1, 1601 and jan 1, 1970 in units of 100 nanoseconds\n */\n#define PTW32_TIMESPEC_TO_FILETIME_OFFSET \\\n\t  ( ((int64_t) 27111902 << 32) + (int64_t) 3577643008 )\n\nINLINE void\nptw32_timespec_to_filetime (const struct timespec *ts, FILETIME * ft)\n     /*\n      * -------------------------------------------------------------------\n      * converts struct timespec\n      * where the time is expressed in seconds and nanoseconds from Jan 1, 1970.\n      * into FILETIME (as set by GetSystemTimeAsFileTime), where the time is\n      * expressed in 100 nanoseconds from Jan 1, 1601,\n      * -------------------------------------------------------------------\n      */\n{\n  *(int64_t *) ft = ts->tv_sec * 10000000\n    + (ts->tv_nsec + 50) / 100 + PTW32_TIMESPEC_TO_FILETIME_OFFSET;\n}\n\nINLINE void\nptw32_filetime_to_timespec (const FILETIME * ft, struct timespec *ts)\n     /*\n      * -------------------------------------------------------------------\n      * converts FILETIME (as set by GetSystemTimeAsFileTime), where the time is\n      * expressed in 100 nanoseconds from Jan 1, 1601,\n      * into struct timespec\n      * where the time is expressed in seconds and nanoseconds from Jan 1, 1970.\n      * -------------------------------------------------------------------\n      */\n{\n  ts->tv_sec =\n    (int) ((*(int64_t *) ft - PTW32_TIMESPEC_TO_FILETIME_OFFSET) / 10000000);\n  ts->tv_nsec =\n    (int) ((*(int64_t *) ft - PTW32_TIMESPEC_TO_FILETIME_OFFSET -\n\t    ((int64_t) ts->tv_sec * (int64_t) 10000000)) * 100);\n}\n\n#endif /* NEED_FTIME */\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_tkAssocCreate.c",
    "content": "/*\n * ptw32_tkAssocCreate.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nint\nptw32_tkAssocCreate (ptw32_thread_t * sp, pthread_key_t key)\n     /*\n      * -------------------------------------------------------------------\n      * This routine creates an association that\n      * is unique for the given (thread,key) combination.The association \n      * is referenced by both the thread and the key.\n      * This association allows us to determine what keys the\n      * current thread references and what threads a given key\n      * references.\n      * See the detailed description\n      * at the beginning of this file for further details.\n      *\n      * Notes:\n      *      1)      New associations are pushed to the beginning of the\n      *              chain so that the internal ptw32_selfThreadKey association\n      *              is always last, thus allowing selfThreadExit to\n      *              be implicitly called last by pthread_exit.\n      *      2)      \n      *\n      * Parameters:\n      *              thread\n      *                      current running thread.\n      *              key\n      *                      key on which to create an association.\n      * Returns:\n      *       0              - if successful,\n      *       ENOMEM         - not enough memory to create assoc or other object\n      *       EINVAL         - an internal error occurred\n      *       ENOSYS         - an internal error occurred\n      * -------------------------------------------------------------------\n      */\n{\n  ThreadKeyAssoc *assoc;\n\n  /*\n   * Have to create an association and add it\n   * to both the key and the thread.\n   *\n   * Both key->keyLock and thread->threadLock are locked before\n   * entry to this routine.\n   */\n  assoc = (ThreadKeyAssoc *) calloc (1, sizeof (*assoc));\n\n  if (assoc == NULL)\n    {\n      return ENOMEM;\n    }\n\n  assoc->thread = sp;\n  assoc->key = key;\n\n  /*\n   * Register assoc with key\n   */\n  assoc->prevThread = NULL;\n  assoc->nextThread = (ThreadKeyAssoc *) key->threads;\n  if (assoc->nextThread != NULL)\n    {\n      assoc->nextThread->prevThread = assoc;\n    }\n  key->threads = (void *) assoc;\n\n  /*\n   * Register assoc with thread\n   */\n  assoc->prevKey = NULL;\n  assoc->nextKey = (ThreadKeyAssoc *) sp->keys;\n  if (assoc->nextKey != NULL)\n    {\n      assoc->nextKey->prevKey = assoc;\n    }\n  sp->keys = (void *) assoc;\n\n  return (0);\n\n}\t\t\t\t/* ptw32_tkAssocCreate */\n"
  },
  {
    "path": "deps/w32-pthreads/ptw32_tkAssocDestroy.c",
    "content": "/*\n * ptw32_tkAssocDestroy.c\n *\n * Description:\n * This translation unit implements routines which are private to\n * the implementation and may be used throughout it.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nvoid\nptw32_tkAssocDestroy (ThreadKeyAssoc * assoc)\n     /*\n      * -------------------------------------------------------------------\n      * This routine releases all resources for the given ThreadKeyAssoc\n      * once it is no longer being referenced\n      * ie) either the key or thread has stopped referencing it.\n      *\n      * Parameters:\n      *              assoc\n      *                      an instance of ThreadKeyAssoc.\n      * Returns:\n      *      N/A\n      * -------------------------------------------------------------------\n      */\n{\n\n  /*\n   * Both key->keyLock and thread->threadLock are locked before\n   * entry to this routine.\n   */\n  if (assoc != NULL)\n    {\n      ThreadKeyAssoc * prev, * next;\n\n      /* Remove assoc from thread's keys chain */\n      prev = assoc->prevKey;\n      next = assoc->nextKey;\n      if (prev != NULL)\n\t{\n\t  prev->nextKey = next;\n\t}\n      if (next != NULL)\n\t{\n\t  next->prevKey = prev;\n\t}\n\n      if (assoc->thread->keys == assoc)\n\t{\n\t  /* We're at the head of the thread's keys chain */\n\t  assoc->thread->keys = next;\n\t}\n      if (assoc->thread->nextAssoc == assoc)\n\t{\n\t  /*\n\t   * Thread is exiting and we're deleting the assoc to be processed next.\n\t   * Hand thread the assoc after this one.\n\t   */\n\t  assoc->thread->nextAssoc = next;\n\t}\n\n      /* Remove assoc from key's threads chain */\n      prev = assoc->prevThread;\n      next = assoc->nextThread;\n      if (prev != NULL)\n\t{\n\t  prev->nextThread = next;\n\t}\n      if (next != NULL)\n\t{\n\t  next->prevThread = prev;\n\t}\n\n      if (assoc->key->threads == assoc)\n\t{\n\t  /* We're at the head of the key's threads chain */\n\t  assoc->key->threads = next;\n\t}\n\n      free (assoc);\n    }\n\n}\t\t\t\t/* ptw32_tkAssocDestroy */\n"
  },
  {
    "path": "deps/w32-pthreads/rwlock.c",
    "content": "/*\n * rwlock.c\n *\n * Description:\n * This translation unit implements read/write lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"ptw32_rwlock_check_need_init.c\"\n#include \"ptw32_rwlock_cancelwrwait.c\"\n#include \"pthread_rwlock_init.c\"\n#include \"pthread_rwlock_destroy.c\"\n#include \"pthread_rwlockattr_init.c\"\n#include \"pthread_rwlockattr_destroy.c\"\n#include \"pthread_rwlockattr_getpshared.c\"\n#include \"pthread_rwlockattr_setpshared.c\"\n#include \"pthread_rwlock_rdlock.c\"\n#include \"pthread_rwlock_timedrdlock.c\"\n#include \"pthread_rwlock_wrlock.c\"\n#include \"pthread_rwlock_timedwrlock.c\"\n#include \"pthread_rwlock_unlock.c\"\n#include \"pthread_rwlock_tryrdlock.c\"\n#include \"pthread_rwlock_trywrlock.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/sched.c",
    "content": "/*\n * sched.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\n#include \"pthread_attr_setschedpolicy.c\"\n#include \"pthread_attr_getschedpolicy.c\"\n#include \"pthread_attr_setschedparam.c\"\n#include \"pthread_attr_getschedparam.c\"\n#include \"pthread_attr_setinheritsched.c\"\n#include \"pthread_attr_getinheritsched.c\"\n#include \"pthread_setschedparam.c\"\n#include \"pthread_getschedparam.c\"\n#include \"sched_get_priority_max.c\"\n#include \"sched_get_priority_min.c\"\n#include \"sched_setscheduler.c\"\n#include \"sched_getscheduler.c\"\n#include \"sched_yield.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/sched.h",
    "content": "/*\n * Module: sched.h\n *\n * Purpose:\n *      Provides an implementation of POSIX realtime extensions\n *      as defined in \n *\n *              POSIX 1003.1b-1993      (POSIX.1b)\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n#if !defined(_SCHED_H)\n#define _SCHED_H\n\n#undef PTW32_SCHED_LEVEL\n\n#if defined(_POSIX_SOURCE)\n#define PTW32_SCHED_LEVEL 0\n/* Early POSIX */\n#endif\n\n#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309\n#undef PTW32_SCHED_LEVEL\n#define PTW32_SCHED_LEVEL 1\n/* Include 1b, 1c and 1d */\n#endif\n\n#if defined(INCLUDE_NP)\n#undef PTW32_SCHED_LEVEL\n#define PTW32_SCHED_LEVEL 2\n/* Include Non-Portable extensions */\n#endif\n\n#define PTW32_SCHED_LEVEL_MAX 3\n\n#if ( defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112 )  || !defined(PTW32_SCHED_LEVEL)\n#define PTW32_SCHED_LEVEL PTW32_SCHED_LEVEL_MAX\n/* Include everything */\n#endif\n\n\n#if defined(__GNUC__) && !defined(__declspec)\n# error Please upgrade your GNU compiler to one that supports __declspec.\n#endif\n\n/*\n * When building the library, you should define PTW32_BUILD so that\n * the variables/functions are exported correctly. When using the library,\n * do NOT define PTW32_BUILD, and then the variables/functions will\n * be imported correctly.\n */\n#if !defined(PTW32_STATIC_LIB)\n#  if defined(PTW32_BUILD)\n#    define PTW32_DLLPORT __declspec (dllexport)\n#  else\n#    define PTW32_DLLPORT\n#  endif\n#else\n#  define PTW32_DLLPORT\n#endif\n\n/*\n * This is a duplicate of what is in the autoconf config.h,\n * which is only used when building the pthread-win32 libraries.\n */\n\n#if !defined(PTW32_CONFIG_H)\n#  if defined(WINCE)\n#    define NEED_ERRNO\n#    define NEED_SEM\n#  endif\n#  if defined(__MINGW64__)\n#    define HAVE_STRUCT_TIMESPEC\n#    define HAVE_MODE_T\n#  elif defined(_UWIN) || defined(__MINGW32__)\n#    define HAVE_MODE_T\n#  endif\n#endif\n\n/*\n *\n */\n\n#if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX\n#if defined(NEED_ERRNO)\n#include \"need_errno.h\"\n#else\n#include <errno.h>\n#endif\n#endif /* PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX */\n\n#if (defined(__MINGW64__) || defined(__MINGW32__)) || defined(_UWIN)\n# if PTW32_SCHED_LEVEL >= PTW32_SCHED_LEVEL_MAX\n/* For pid_t */\n#  include <sys/types.h>\n/* Required by Unix 98 */\n#  include <time.h>\n# else\n   typedef int pid_t;\n# endif\n#else\n typedef int pid_t;\n#endif\n\n/* Thread scheduling policies */\n\nenum {\n  SCHED_OTHER = 0,\n  SCHED_FIFO,\n  SCHED_RR,\n  SCHED_MIN   = SCHED_OTHER,\n  SCHED_MAX   = SCHED_RR\n};\n\nstruct sched_param {\n  int sched_priority;\n};\n\n#if defined(__cplusplus)\nextern \"C\"\n{\n#endif                          /* __cplusplus */\n\nPTW32_DLLPORT int __cdecl sched_yield (void);\n\nPTW32_DLLPORT int __cdecl sched_get_priority_min (int policy);\n\nPTW32_DLLPORT int __cdecl sched_get_priority_max (int policy);\n\nPTW32_DLLPORT int __cdecl sched_setscheduler (pid_t pid, int policy);\n\nPTW32_DLLPORT int __cdecl sched_getscheduler (pid_t pid);\n\n/*\n * Note that this macro returns ENOTSUP rather than\n * ENOSYS as might be expected. However, returning ENOSYS\n * should mean that sched_get_priority_{min,max} are\n * not implemented as well as sched_rr_get_interval.\n * This is not the case, since we just don't support\n * round-robin scheduling. Therefore I have chosen to\n * return the same value as sched_setscheduler when\n * SCHED_RR is passed to it.\n */\n#define sched_rr_get_interval(_pid, _interval) \\\n  ( errno = ENOTSUP, (int) -1 )\n\n\n#if defined(__cplusplus)\n}                               /* End of extern \"C\" */\n#endif                          /* __cplusplus */\n\n#undef PTW32_SCHED_LEVEL\n#undef PTW32_SCHED_LEVEL_MAX\n\n#endif                          /* !_SCHED_H */\n\n"
  },
  {
    "path": "deps/w32-pthreads/sched_get_priority_max.c",
    "content": "/*\n * sched_get_priority_max.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\n/*\n * On Windows98, THREAD_PRIORITY_LOWEST is (-2) and \n * THREAD_PRIORITY_HIGHEST is 2, and everything works just fine.\n * \n * On WinCE 3.0, it so happen that THREAD_PRIORITY_LOWEST is 5\n * and THREAD_PRIORITY_HIGHEST is 1 (yes, I know, it is funny:\n * highest priority use smaller numbers) and the following happens:\n * \n * sched_get_priority_min() returns 5\n * sched_get_priority_max() returns 1\n *\n * The following table shows the base priority levels for combinations\n * of priority class and priority value in Win32.\n *\n *   Process Priority Class               Thread Priority Level\n *   -----------------------------------------------------------------\n *   1 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_IDLE\n *   1 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_IDLE\n *   1 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_IDLE\n *   1 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_IDLE\n *   1 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_IDLE\n *   2 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_LOWEST\n *   3 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_BELOW_NORMAL\n *   4 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_NORMAL\n *   4 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_LOWEST\n *   5 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_ABOVE_NORMAL\n *   5 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_BELOW_NORMAL\n *   5 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_LOWEST\n *   6 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_HIGHEST\n *   6 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_NORMAL\n *   6 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_BELOW_NORMAL\n *   7 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_ABOVE_NORMAL\n *   7 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_NORMAL\n *   7 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_LOWEST\n *   8 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_HIGHEST\n *   8 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_ABOVE_NORMAL\n *   8 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_BELOW_NORMAL\n *   8 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_LOWEST\n *   9 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_HIGHEST\n *   9 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_NORMAL\n *   9 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_BELOW_NORMAL\n *  10 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_ABOVE_NORMAL\n *  10 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_NORMAL\n *  11 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_HIGHEST\n *  11 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_ABOVE_NORMAL\n *  11 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_LOWEST\n *  12 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_HIGHEST\n *  12 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_BELOW_NORMAL\n *  13 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_NORMAL\n *  14 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_ABOVE_NORMAL\n *  15 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_HIGHEST\n *  15 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_TIME_CRITICAL\n *  15 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_TIME_CRITICAL\n *  15 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_TIME_CRITICAL\n *  15 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_TIME_CRITICAL\n *  15 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_TIME_CRITICAL\n *  16 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_IDLE\n *  17 REALTIME_PRIORITY_CLASS            -7\n *  18 REALTIME_PRIORITY_CLASS            -6\n *  19 REALTIME_PRIORITY_CLASS            -5\n *  20 REALTIME_PRIORITY_CLASS            -4\n *  21 REALTIME_PRIORITY_CLASS            -3\n *  22 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_LOWEST\n *  23 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_BELOW_NORMAL\n *  24 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_NORMAL\n *  25 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_ABOVE_NORMAL\n *  26 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_HIGHEST\n *  27 REALTIME_PRIORITY_CLASS             3\n *  28 REALTIME_PRIORITY_CLASS             4\n *  29 REALTIME_PRIORITY_CLASS             5\n *  30 REALTIME_PRIORITY_CLASS             6\n *  31 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_TIME_CRITICAL\n *\n * Windows NT:  Values -7, -6, -5, -4, -3, 3, 4, 5, and 6 are not supported.\n */\n\n\nint\nsched_get_priority_max (int policy)\n{\n  if (policy < SCHED_MIN || policy > SCHED_MAX)\n    {\n      errno = EINVAL;\n      return -1;\n    }\n\n#if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL)\n  /* WinCE? */\n  return PTW32_MAX (THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL);\n#else\n  /* This is independent of scheduling policy in Win32. */\n  return PTW32_MAX (THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL);\n#endif\n}\n"
  },
  {
    "path": "deps/w32-pthreads/sched_get_priority_min.c",
    "content": "/*\n * sched_get_priority_min.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\n/*\n * On Windows98, THREAD_PRIORITY_LOWEST is (-2) and \n * THREAD_PRIORITY_HIGHEST is 2, and everything works just fine.\n * \n * On WinCE 3.0, it so happen that THREAD_PRIORITY_LOWEST is 5\n * and THREAD_PRIORITY_HIGHEST is 1 (yes, I know, it is funny:\n * highest priority use smaller numbers) and the following happens:\n * \n * sched_get_priority_min() returns 5\n * sched_get_priority_max() returns 1\n *\n * The following table shows the base priority levels for combinations\n * of priority class and priority value in Win32.\n *\n *   Process Priority Class               Thread Priority Level\n *   -----------------------------------------------------------------\n *   1 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_IDLE\n *   1 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_IDLE\n *   1 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_IDLE\n *   1 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_IDLE\n *   1 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_IDLE\n *   2 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_LOWEST\n *   3 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_BELOW_NORMAL\n *   4 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_NORMAL\n *   4 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_LOWEST\n *   5 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_ABOVE_NORMAL\n *   5 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_BELOW_NORMAL\n *   5 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_LOWEST\n *   6 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_HIGHEST\n *   6 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_NORMAL\n *   6 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_BELOW_NORMAL\n *   7 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_ABOVE_NORMAL\n *   7 Background NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_NORMAL\n *   7 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_LOWEST\n *   8 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_HIGHEST\n *   8 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_ABOVE_NORMAL\n *   8 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_BELOW_NORMAL\n *   8 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_LOWEST\n *   9 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_HIGHEST\n *   9 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_NORMAL\n *   9 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_BELOW_NORMAL\n *  10 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_ABOVE_NORMAL\n *  10 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_NORMAL\n *  11 Foreground NORMAL_PRIORITY_CLASS   THREAD_PRIORITY_HIGHEST\n *  11 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_ABOVE_NORMAL\n *  11 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_LOWEST\n *  12 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_HIGHEST\n *  12 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_BELOW_NORMAL\n *  13 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_NORMAL\n *  14 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_ABOVE_NORMAL\n *  15 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_HIGHEST\n *  15 HIGH_PRIORITY_CLASS                THREAD_PRIORITY_TIME_CRITICAL\n *  15 IDLE_PRIORITY_CLASS                THREAD_PRIORITY_TIME_CRITICAL\n *  15 BELOW_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_TIME_CRITICAL\n *  15 NORMAL_PRIORITY_CLASS              THREAD_PRIORITY_TIME_CRITICAL\n *  15 ABOVE_NORMAL_PRIORITY_CLASS        THREAD_PRIORITY_TIME_CRITICAL\n *  16 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_IDLE\n *  17 REALTIME_PRIORITY_CLASS            -7\n *  18 REALTIME_PRIORITY_CLASS            -6\n *  19 REALTIME_PRIORITY_CLASS            -5\n *  20 REALTIME_PRIORITY_CLASS            -4\n *  21 REALTIME_PRIORITY_CLASS            -3\n *  22 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_LOWEST\n *  23 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_BELOW_NORMAL\n *  24 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_NORMAL\n *  25 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_ABOVE_NORMAL\n *  26 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_HIGHEST\n *  27 REALTIME_PRIORITY_CLASS             3\n *  28 REALTIME_PRIORITY_CLASS             4\n *  29 REALTIME_PRIORITY_CLASS             5\n *  30 REALTIME_PRIORITY_CLASS             6\n *  31 REALTIME_PRIORITY_CLASS            THREAD_PRIORITY_TIME_CRITICAL\n *\n * Windows NT:  Values -7, -6, -5, -4, -3, 3, 4, 5, and 6 are not supported.\n *\n */\n\n\nint\nsched_get_priority_min (int policy)\n{\n  if (policy < SCHED_MIN || policy > SCHED_MAX)\n    {\n      errno = EINVAL;\n      return -1;\n    }\n\n#if (THREAD_PRIORITY_LOWEST > THREAD_PRIORITY_NORMAL)\n  /* WinCE? */\n  return PTW32_MIN (THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL);\n#else\n  /* This is independent of scheduling policy in Win32. */\n  return PTW32_MIN (THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL);\n#endif\n}\n"
  },
  {
    "path": "deps/w32-pthreads/sched_getscheduler.c",
    "content": "/*\n * sched_getscheduler.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\nsched_getscheduler (pid_t pid)\n{\n  /*\n   * Win32 only has one policy which we call SCHED_OTHER.\n   * However, we try to provide other valid side-effects\n   * such as EPERM and ESRCH errors.\n   */\n  if (0 != pid)\n    {\n      int selfPid = (int) GetCurrentProcessId ();\n\n      if (pid != selfPid)\n\t{\n\t  HANDLE h =\n\t    OpenProcess (PROCESS_QUERY_INFORMATION, PTW32_FALSE, (DWORD) pid);\n\n\t  if (NULL == h)\n\t    {\n\t      errno =\n\t\t(GetLastError () ==\n\t\t (0xFF & ERROR_ACCESS_DENIED)) ? EPERM : ESRCH;\n\t      return -1;\n\t    }\n\t  else\n\t    CloseHandle(h);\n\t}\n    }\n\n  return SCHED_OTHER;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/sched_setscheduler.c",
    "content": "/*\n * sched_setscheduler.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\nsched_setscheduler (pid_t pid, int policy)\n{\n  /*\n   * Win32 only has one policy which we call SCHED_OTHER.\n   * However, we try to provide other valid side-effects\n   * such as EPERM and ESRCH errors. Choosing to check\n   * for a valid policy last allows us to get the most value out\n   * of this function.\n   */\n  if (0 != pid)\n    {\n      int selfPid = (int) GetCurrentProcessId ();\n\n      if (pid != selfPid)\n\t{\n\t  HANDLE h =\n\t    OpenProcess (PROCESS_SET_INFORMATION, PTW32_FALSE, (DWORD) pid);\n\n\t  if (NULL == h)\n\t    {\n\t      errno =\n\t\t(GetLastError () ==\n\t\t (0xFF & ERROR_ACCESS_DENIED)) ? EPERM : ESRCH;\n\t      return -1;\n\t    }\n\t  else\n\t    CloseHandle(h);\n\t}\n    }\n\n  if (SCHED_OTHER != policy)\n    {\n      errno = ENOSYS;\n      return -1;\n    }\n\n  /*\n   * Don't set anything because there is nothing to set.\n   * Just return the current (the only possible) value.\n   */\n  return SCHED_OTHER;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/sched_yield.c",
    "content": "/*\n * sched_yield.c\n * \n * Description:\n * POSIX thread functions that deal with thread scheduling.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n#include \"sched.h\"\n\nint\nsched_yield (void)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function indicates that the calling thread is\n      *      willing to give up some time slices to other threads.\n      *\n      * PARAMETERS\n      *      N/A\n      *\n      *\n      * DESCRIPTION\n      *      This function indicates that the calling thread is\n      *      willing to give up some time slices to other threads.\n      *      NOTE: Since this is part of POSIX 1003.1b\n      *                (realtime extensions), it is defined as returning\n      *                -1 if an error occurs and sets errno to the actual\n      *                error.\n      *\n      * RESULTS\n      *              0               successfully created semaphore,\n      *              ENOSYS          sched_yield not supported,\n      *\n      * ------------------------------------------------------\n      */\n{\n  Sleep (0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/sem_close.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_close.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n/* ignore warning \"unreferenced formal parameter\" */\n#if defined(_MSC_VER)\n#pragma warning( disable : 4100 )\n#endif\n\nint\nsem_close (sem_t * sem)\n{\n  errno = ENOSYS;\n  return -1;\n}\t\t\t\t/* sem_close */\n"
  },
  {
    "path": "deps/w32-pthreads/sem_destroy.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_destroy.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n\nint\nsem_destroy (sem_t * sem)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function destroys an unnamed semaphore.\n      *\n      * PARAMETERS\n      *      sem\n      *              pointer to an instance of sem_t\n      *\n      * DESCRIPTION\n      *      This function destroys an unnamed semaphore.\n      *\n      * RESULTS\n      *              0               successfully destroyed semaphore,\n      *              -1              failed, error in errno\n      * ERRNO\n      *              EINVAL          'sem' is not a valid semaphore,\n      *              ENOSYS          semaphores are not supported,\n      *              EBUSY           threads (or processes) are currently\n      *                                      blocked on 'sem'\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  sem_t s = NULL;\n\n  if (sem == NULL || *sem == NULL)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      s = *sem;\n\n      if ((result = pthread_mutex_lock (&s->lock)) == 0)\n        {\n          if (s->value < 0)\n            {\n              (void) pthread_mutex_unlock (&s->lock);\n              result = EBUSY;\n            }\n          else\n            {\n              /* There are no threads currently blocked on this semaphore. */\n\n              if (!CloseHandle (s->sem))\n\t        {\n                  (void) pthread_mutex_unlock (&s->lock);\n\t          result = EINVAL;\n\t        }\n\t      else\n\t        {\n                  /*\n                   * Invalidate the semaphore handle when we have the lock.\n                   * Other sema operations should test this after acquiring the lock\n                   * to check that the sema is still valid, i.e. before performing any\n                   * operations. This may only be necessary before the sema op routine\n                   * returns so that the routine can return EINVAL - e.g. if setting\n                   * s->value to SEM_VALUE_MAX below does force a fall-through.\n                   */\n                  *sem = NULL;\n\n                  /* Prevent anyone else actually waiting on or posting this sema.\n                   */\n                  s->value = SEM_VALUE_MAX;\n\n                  (void) pthread_mutex_unlock (&s->lock);\n\n                  do\n                    {\n                      /* Give other threads a chance to run and exit any sema op\n                       * routines. Due to the SEM_VALUE_MAX value, if sem_post or\n                       * sem_wait were blocked by us they should fall through.\n                       */\n                      Sleep(0);\n                    }\n                  while (pthread_mutex_destroy (&s->lock) == EBUSY);\n                }\n            }\n        }\n    }\n\n  if (result != 0)\n    {\n      errno = result;\n      return -1;\n    }\n\n  free (s);\n\n  return 0;\n\n}\t\t\t\t/* sem_destroy */\n"
  },
  {
    "path": "deps/w32-pthreads/sem_getvalue.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_getvalue.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of PThreads.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1-2001\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n\nint\nsem_getvalue (sem_t * sem, int *sval)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function stores the current count value of the\n      *      semaphore.\n      * RESULTS\n      *\n      * Return value\n      *\n      *       0                  sval has been set.\n      *      -1                  failed, error in errno\n      *\n      *  in global errno\n      *\n      *      EINVAL              'sem' is not a valid semaphore,\n      *      ENOSYS              this function is not supported,\n      *\n      *\n      * PARAMETERS\n      *\n      *      sem                 pointer to an instance of sem_t\n      *\n      *      sval                pointer to int.\n      *\n      * DESCRIPTION\n      *      This function stores the current count value of the semaphore\n      *      pointed to by sem in the int pointed to by sval.\n      */\n{\n  if (sem == NULL || *sem == NULL || sval == NULL)\n    {\n      errno = EINVAL;\n      return -1;\n    }\n  else\n    {\n      long value;\n      register sem_t s = *sem;\n      int result = 0;\n\n      if ((result = pthread_mutex_lock(&s->lock)) == 0)\n        {\n\t  /* See sem_destroy.c\n\t   */\n\t  if (*sem == NULL)\n\t    {\n\t      (void) pthread_mutex_unlock (&s->lock);\n\t      errno = EINVAL;\n\t      return -1;\n\t    }\n\n          value = s->value;\n          (void) pthread_mutex_unlock(&s->lock);\n          *sval = value;\n        }\n\n      return result;\n    }\n\n}\t\t\t\t/* sem_getvalue */\n"
  },
  {
    "path": "deps/w32-pthreads/sem_init.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_init.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of PThreads.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1-2001\n *\n * -------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\nint\nsem_init (sem_t * sem, int pshared, unsigned int value)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function initializes a semaphore. The\n      *      initial value of the semaphore is 'value'\n      *\n      * PARAMETERS\n      *      sem\n      *              pointer to an instance of sem_t\n      *\n      *      pshared\n      *              if zero, this semaphore may only be shared between\n      *              threads in the same process.\n      *              if nonzero, the semaphore can be shared between\n      *              processes\n      *\n      *      value\n      *              initial value of the semaphore counter\n      *\n      * DESCRIPTION\n      *      This function initializes a semaphore. The\n      *      initial value of the semaphore is set to 'value'.\n      *\n      * RESULTS\n      *              0               successfully created semaphore,\n      *              -1              failed, error in errno\n      * ERRNO\n      *              EINVAL          'sem' is not a valid semaphore, or\n      *                              'value' >= SEM_VALUE_MAX\n      *              ENOMEM          out of memory,\n      *              ENOSPC          a required resource has been exhausted,\n      *              ENOSYS          semaphores are not supported,\n      *              EPERM           the process lacks appropriate privilege\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  sem_t s = NULL;\n\n  if (pshared != 0)\n    {\n      /*\n       * Creating a semaphore that can be shared between\n       * processes\n       */\n      result = EPERM;\n    }\n  else if (value > (unsigned int)SEM_VALUE_MAX)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      s = (sem_t) calloc (1, sizeof (*s));\n\n      if (NULL == s)\n\t{\n\t  result = ENOMEM;\n\t}\n      else\n\t{\n\n\t  s->value = value;\n\t  if (pthread_mutex_init(&s->lock, NULL) == 0)\n\t    {\n\n#if defined(NEED_SEM)\n\n\t  s->sem = CreateEvent (NULL,\n\t\t\t\tPTW32_FALSE,\t/* auto (not manual) reset */\n\t\t\t\tPTW32_FALSE,\t/* initial state is unset */\n\t\t\t\tNULL);\n\n\t  if (0 == s->sem)\n\t    {\n\t      free (s);\n\t      (void) pthread_mutex_destroy(&s->lock);\n\t      result = ENOSPC;\n\t    }\n\t  else\n\t    {\n\t      s->leftToUnblock = 0;\n\t    }\n\n#else /* NEED_SEM */\n\n\t      if ((s->sem = CreateSemaphore (NULL,\t/* Always NULL */\n\t\t\t\t\t     (long) 0,\t/* Force threads to wait */\n\t\t\t\t\t     (long) SEM_VALUE_MAX,\t/* Maximum value */\n\t\t\t\t\t     NULL)) == 0)\t/* Name */\n\t\t{\n\t\t  (void) pthread_mutex_destroy(&s->lock);\n\t\t  result = ENOSPC;\n\t\t}\n\n#endif /* NEED_SEM */\n\n\t    }\n\t  else\n\t    {\n\t      result = ENOSPC;\n\t    }\n\n\t  if (result != 0)\n\t    {\n\t      free(s);\n\t    }\n\t}\n    }\n\n  if (result != 0)\n    {\n      errno = result;\n      return -1;\n    }\n\n  *sem = s;\n\n  return 0;\n\n}\t\t\t\t/* sem_init */\n"
  },
  {
    "path": "deps/w32-pthreads/sem_open.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_open.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n/* ignore warning \"unreferenced formal parameter\" */\n#if defined(_MSC_VER)\n#pragma warning( disable : 4100 )\n#endif\n\nint\nsem_open (const char *name, int oflag, mode_t mode, unsigned int value)\n{\n  errno = ENOSYS;\n  return -1;\n}\t\t\t\t/* sem_open */\n"
  },
  {
    "path": "deps/w32-pthreads/sem_post.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_post.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n\nint\nsem_post (sem_t * sem)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function posts a wakeup to a semaphore.\n      *\n      * PARAMETERS\n      *      sem\n      *              pointer to an instance of sem_t\n      *\n      * DESCRIPTION\n      *      This function posts a wakeup to a semaphore. If there\n      *      are waiting threads (or processes), one is awakened;\n      *      otherwise, the semaphore value is incremented by one.\n      *\n      * RESULTS\n      *              0               successfully posted semaphore,\n      *              -1              failed, error in errno\n      * ERRNO\n      *              EINVAL          'sem' is not a valid semaphore,\n      *              ENOSYS          semaphores are not supported,\n      *              ERANGE          semaphore count is too big\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  sem_t s = *sem;\n\n  if (s == NULL)\n    {\n      result = EINVAL;\n    }\n  else if ((result = pthread_mutex_lock (&s->lock)) == 0)\n    {\n      /* See sem_destroy.c\n       */\n      if (*sem == NULL)\n        {\n          (void) pthread_mutex_unlock (&s->lock);\n          result = EINVAL;\n          return -1;\n        }\n\n      if (s->value < SEM_VALUE_MAX)\n\t{\n#if defined(NEED_SEM)\n\t  if (++s->value <= 0\n\t      && !SetEvent(s->sem))\n\t    {\n\t      s->value--;\n\t      result = EINVAL;\n\t    }\n#else\n\t  if (++s->value <= 0\n\t      && !ReleaseSemaphore (s->sem, 1, NULL))\n\t    {\n\t      s->value--;\n\t      result = EINVAL;\n\t    }\n#endif /* NEED_SEM */\n\t}\n      else\n\t{\n\t  result = ERANGE;\n\t}\n\n      (void) pthread_mutex_unlock (&s->lock);\n    }\n\n  if (result != 0)\n    {\n      errno = result;\n      return -1;\n    }\n\n  return 0;\n\n}\t\t\t\t/* sem_post */\n"
  },
  {
    "path": "deps/w32-pthreads/sem_post_multiple.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_post_multiple.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n\nint\nsem_post_multiple (sem_t * sem, int count)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function posts multiple wakeups to a semaphore.\n      *\n      * PARAMETERS\n      *      sem\n      *              pointer to an instance of sem_t\n      *\n      *      count\n      *              counter, must be greater than zero.\n      *\n      * DESCRIPTION\n      *      This function posts multiple wakeups to a semaphore. If there\n      *      are waiting threads (or processes), n <= count are awakened;\n      *      the semaphore value is incremented by count - n.\n      *\n      * RESULTS\n      *              0               successfully posted semaphore,\n      *              -1              failed, error in errno\n      * ERRNO\n      *              EINVAL          'sem' is not a valid semaphore\n      *                              or count is less than or equal to zero.\n      *              ERANGE          semaphore count is too big\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  long waiters;\n  sem_t s = *sem;\n\n  if (s == NULL || count <= 0)\n    {\n      result = EINVAL;\n    }\n  else if ((result = pthread_mutex_lock (&s->lock)) == 0)\n    {\n      /* See sem_destroy.c\n       */\n      if (*sem == NULL)\n        {\n          (void) pthread_mutex_unlock (&s->lock);\n          result = EINVAL;\n          return -1;\n        }\n\n      if (s->value <= (SEM_VALUE_MAX - count))\n\t{\n\t  waiters = -s->value;\n\t  s->value += count;\n\t  if (waiters > 0)\n\t    {\n#if defined(NEED_SEM)\n\t      if (SetEvent(s->sem))\n\t\t{\n\t\t  waiters--;\n\t\t  s->leftToUnblock += count - 1;\n\t\t  if (s->leftToUnblock > waiters)\n\t\t    {\n\t\t      s->leftToUnblock = waiters;\n\t\t    }\n\t\t}\n#else\n\t      if (ReleaseSemaphore (s->sem,  (waiters<=count)?waiters:count, 0))\n\t\t{\n\t\t  /* No action */\n\t\t}\n#endif\n\t      else\n\t\t{\n\t\t  s->value -= count;\n\t\t  result = EINVAL;\n\t\t}\n\t    }\n\t}\n      else\n\t{\n\t  result = ERANGE;\n\t}\n      (void) pthread_mutex_unlock (&s->lock);\n    }\n\n  if (result != 0)\n    {\n      errno = result;\n      return -1;\n    }\n\n  return 0;\n\n}\t\t\t\t/* sem_post_multiple */\n"
  },
  {
    "path": "deps/w32-pthreads/sem_timedwait.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_timedwait.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n\ntypedef struct {\n  sem_t sem;\n  int * resultPtr;\n} sem_timedwait_cleanup_args_t;\n\n\nstatic void PTW32_CDECL\nptw32_sem_timedwait_cleanup (void * args)\n{\n  sem_timedwait_cleanup_args_t * a = (sem_timedwait_cleanup_args_t *)args;\n  sem_t s = a->sem;\n\n  if (pthread_mutex_lock (&s->lock) == 0)\n    {\n      /*\n       * We either timed out or were cancelled.\n       * If someone has posted between then and now we try to take the semaphore.\n       * Otherwise the semaphore count may be wrong after we\n       * return. In the case of a cancellation, it is as if we\n       * were cancelled just before we return (after taking the semaphore)\n       * which is ok.\n       */\n      if (WaitForSingleObject(s->sem, 0) == WAIT_OBJECT_0)\n\t{\n\t  /* We got the semaphore on the second attempt */\n\t  *(a->resultPtr) = 0;\n\t}\n      else\n\t{\n\t  /* Indicate we're no longer waiting */\n\t  s->value++;\n#if defined(NEED_SEM)\n\t  if (s->value > 0)\n\t    {\n\t      s->leftToUnblock = 0;\n\t    }\n#else\n          /*\n           * Don't release the W32 sema, it doesn't need adjustment\n           * because it doesn't record the number of waiters.\n           */\n#endif\n\t}\n      (void) pthread_mutex_unlock (&s->lock);\n    }\n}\n\n\nint\nsem_timedwait (sem_t * sem, const struct timespec *abstime)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function waits on a semaphore possibly until\n      *      'abstime' time.\n      *\n      * PARAMETERS\n      *      sem\n      *              pointer to an instance of sem_t\n      *\n      *      abstime\n      *              pointer to an instance of struct timespec\n      *\n      * DESCRIPTION\n      *      This function waits on a semaphore. If the\n      *      semaphore value is greater than zero, it decreases\n      *      its value by one. If the semaphore value is zero, then\n      *      the calling thread (or process) is blocked until it can\n      *      successfully decrease the value or until interrupted by\n      *      a signal.\n      *\n      *      If 'abstime' is a NULL pointer then this function will\n      *      block until it can successfully decrease the value or\n      *      until interrupted by a signal.\n      *\n      * RESULTS\n      *              0               successfully decreased semaphore,\n      *              -1              failed, error in errno\n      * ERRNO\n      *              EINVAL          'sem' is not a valid semaphore,\n      *              ENOSYS          semaphores are not supported,\n      *              EINTR           the function was interrupted by a signal,\n      *              EDEADLK         a deadlock condition was detected.\n      *              ETIMEDOUT       abstime elapsed before success.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  sem_t s = *sem;\n\n  pthread_testcancel();\n\n  if (sem == NULL)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      DWORD milliseconds;\n\n      if (abstime == NULL)\n\t{\n\t  milliseconds = INFINITE;\n\t}\n      else\n\t{\n\t  /* \n\t   * Calculate timeout as milliseconds from current system time. \n\t   */\n\t  milliseconds = ptw32_relmillisecs (abstime);\n\t}\n\n      if ((result = pthread_mutex_lock (&s->lock)) == 0)\n\t{\n\t  int v;\n\n\t  /* See sem_destroy.c\n\t   */\n\t  if (*sem == NULL)\n\t    {\n\t      (void) pthread_mutex_unlock (&s->lock);\n\t      errno = EINVAL;\n\t      return -1;\n\t    }\n\n\t  v = --s->value;\n\t  (void) pthread_mutex_unlock (&s->lock);\n\n\t  if (v < 0)\n\t    {\n#if defined(NEED_SEM)\n\t      int timedout;\n#endif\n\t      sem_timedwait_cleanup_args_t cleanup_args;\n\n\t      cleanup_args.sem = s;\n\t      cleanup_args.resultPtr = &result;\n\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth(0)\n#endif\n\t      /* Must wait */\n              pthread_cleanup_push(ptw32_sem_timedwait_cleanup, (void *) &cleanup_args);\n#if defined(NEED_SEM)\n\t      timedout =\n#endif\n\t      result = pthreadCancelableTimedWait (s->sem, milliseconds);\n\t      pthread_cleanup_pop(result);\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth()\n#endif\n\n#if defined(NEED_SEM)\n\n\t      if (!timedout && pthread_mutex_lock (&s->lock) == 0)\n\t        {\n        \t  if (*sem == NULL)\n        \t    {\n        \t      (void) pthread_mutex_unlock (&s->lock);\n        \t      errno = EINVAL;\n        \t      return -1;\n        \t    }\n\n\t          if (s->leftToUnblock > 0)\n\t            {\n\t\t      --s->leftToUnblock;\n\t\t      SetEvent(s->sem);\n\t\t    }\n\t          (void) pthread_mutex_unlock (&s->lock);\n\t        }\n\n#endif /* NEED_SEM */\n\n\t    }\n\t}\n\n    }\n\n  if (result != 0)\n    {\n\n      errno = result;\n      return -1;\n\n    }\n\n  return 0;\n\n}\t\t\t\t/* sem_timedwait */\n"
  },
  {
    "path": "deps/w32-pthreads/sem_trywait.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_trywait.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n\nint\nsem_trywait (sem_t * sem)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function tries to wait on a semaphore.\n      *\n      * PARAMETERS\n      *      sem\n      *              pointer to an instance of sem_t\n      *\n      * DESCRIPTION\n      *      This function tries to wait on a semaphore. If the\n      *      semaphore value is greater than zero, it decreases\n      *      its value by one. If the semaphore value is zero, then\n      *      this function returns immediately with the error EAGAIN\n      *\n      * RESULTS\n      *              0               successfully decreased semaphore,\n      *              -1              failed, error in errno\n      * ERRNO\n      *              EAGAIN          the semaphore was already locked,\n      *              EINVAL          'sem' is not a valid semaphore,\n      *              ENOTSUP         sem_trywait is not supported,\n      *              EINTR           the function was interrupted by a signal,\n      *              EDEADLK         a deadlock condition was detected.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  sem_t s = *sem;\n\n  if (s == NULL)\n    {\n      result = EINVAL;\n    }\n  else if ((result = pthread_mutex_lock (&s->lock)) == 0)\n    {\n      /* See sem_destroy.c\n       */\n     if (*sem == NULL)\n        {\n          (void) pthread_mutex_unlock (&s->lock);\n          errno = EINVAL;\n          return -1;\n        }\n\n      if (s->value > 0)\n\t{\n\t  s->value--;\n\t}\n      else\n\t{\n\t  result = EAGAIN;\n\t}\n\n      (void) pthread_mutex_unlock (&s->lock);\n    }\n\n  if (result != 0)\n    {\n      errno = result;\n      return -1;\n    }\n\n  return 0;\n\n}\t\t\t\t/* sem_trywait */\n"
  },
  {
    "path": "deps/w32-pthreads/sem_unlink.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_unlink.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n/* ignore warning \"unreferenced formal parameter\" */\n#if defined(_MSC_VER)\n#pragma warning( disable : 4100 )\n#endif\n\nint\nsem_unlink (const char *name)\n{\n  errno = ENOSYS;\n  return -1;\n}\t\t\t\t/* sem_unlink */\n"
  },
  {
    "path": "deps/w32-pthreads/sem_wait.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: sem_wait.c\n *\n * Purpose:\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n\nstatic void PTW32_CDECL\nptw32_sem_wait_cleanup(void * sem)\n{\n  sem_t s = (sem_t) sem;\n\n  if (pthread_mutex_lock (&s->lock) == 0)\n    {\n      /*\n       * If sema is destroyed do nothing, otherwise:-\n       * If the sema is posted between us being cancelled and us locking\n       * the sema again above then we need to consume that post but cancel\n       * anyway. If we don't get the semaphore we indicate that we're no\n       * longer waiting.\n       */\n      if (*((sem_t *)sem) != NULL && !(WaitForSingleObject(s->sem, 0) == WAIT_OBJECT_0))\n\t{\n\t  ++s->value;\n#if defined(NEED_SEM)\n\t  if (s->value > 0)\n\t    {\n\t      s->leftToUnblock = 0;\n\t    }\n#else\n\t  /*\n\t   * Don't release the W32 sema, it doesn't need adjustment\n\t   * because it doesn't record the number of waiters.\n\t   */\n#endif /* NEED_SEM */\n\t}\n      (void) pthread_mutex_unlock (&s->lock);\n    }\n}\n\nint\nsem_wait (sem_t * sem)\n     /*\n      * ------------------------------------------------------\n      * DOCPUBLIC\n      *      This function  waits on a semaphore.\n      *\n      * PARAMETERS\n      *      sem\n      *              pointer to an instance of sem_t\n      *\n      * DESCRIPTION\n      *      This function waits on a semaphore. If the\n      *      semaphore value is greater than zero, it decreases\n      *      its value by one. If the semaphore value is zero, then\n      *      the calling thread (or process) is blocked until it can\n      *      successfully decrease the value or until interrupted by\n      *      a signal.\n      *\n      * RESULTS\n      *              0               successfully decreased semaphore,\n      *              -1              failed, error in errno\n      * ERRNO\n      *              EINVAL          'sem' is not a valid semaphore,\n      *              ENOSYS          semaphores are not supported,\n      *              EINTR           the function was interrupted by a signal,\n      *              EDEADLK         a deadlock condition was detected.\n      *\n      * ------------------------------------------------------\n      */\n{\n  int result = 0;\n  sem_t s = *sem;\n\n  pthread_testcancel();\n\n  if (s == NULL)\n    {\n      result = EINVAL;\n    }\n  else\n    {\n      if ((result = pthread_mutex_lock (&s->lock)) == 0)\n\t{\n\t  int v;\n\n\t  /* See sem_destroy.c\n\t   */\n\t  if (*sem == NULL)\n\t    {\n\t      (void) pthread_mutex_unlock (&s->lock);\n\t      errno = EINVAL;\n\t      return -1;\n\t    }\n\n          v = --s->value;\n\t  (void) pthread_mutex_unlock (&s->lock);\n\n\t  if (v < 0)\n\t    {\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth(0)\n#endif\n\t      /* Must wait */\n\t      pthread_cleanup_push(ptw32_sem_wait_cleanup, (void *) s);\n\t      result = pthreadCancelableWait (s->sem);\n\t      /* Cleanup if we're canceled or on any other error */\n\t      pthread_cleanup_pop(result);\n#if defined(_MSC_VER) && _MSC_VER < 1400\n#pragma inline_depth()\n#endif\n\t    }\n#if defined(NEED_SEM)\n\n\t  if (!result && pthread_mutex_lock (&s->lock) == 0)\n\t    {\n\t      if (*sem == NULL)\n\t        {\n\t          (void) pthread_mutex_unlock (&s->lock);\n\t          errno = EINVAL;\n\t          return -1;\n\t        }\n\n\t      if (s->leftToUnblock > 0)\n\t\t{\n\t\t  --s->leftToUnblock;\n\t\t  SetEvent(s->sem);\n\t\t}\n\t      (void) pthread_mutex_unlock (&s->lock);\n\t    }\n\n#endif /* NEED_SEM */\n\n\t}\n\n    }\n\n  if (result != 0)\n    {\n      errno = result;\n      return -1;\n    }\n\n  return 0;\n\n}\t\t\t\t/* sem_wait */\n"
  },
  {
    "path": "deps/w32-pthreads/semaphore.c",
    "content": "/*\n * -------------------------------------------------------------\n *\n * Module: semaphore.c\n *\n * Purpose:\n *\tConcatenated version of separate modules to allow\n *\tinlining optimisation, which it is assumed can only\n *\tbe effective within a single module.\n *\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * -------------------------------------------------------------\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#if !defined(NEED_FTIME)\n#  include <sys/timeb.h>\n#endif\n\n#include <limits.h>\n\n#include \"pthread.h\"\n#include \"semaphore.h\"\n#include \"implement.h\"\n\n\n#include \"sem_init.c\"\n#include \"sem_destroy.c\"\n#include \"sem_trywait.c\"\n#include \"sem_wait.c\"\n#include \"sem_timedwait.c\"\n#include \"sem_post.c\"\n#include \"sem_post_multiple.c\"\n#include \"sem_getvalue.c\"\n#include \"sem_open.c\"\n#include \"sem_close.c\"\n#include \"sem_unlink.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/semaphore.h",
    "content": "/*\n * Module: semaphore.h\n *\n * Purpose:\n *\tSemaphores aren't actually part of the PThreads standard.\n *\tThey are defined by the POSIX Standard:\n *\n *\t\tPOSIX 1003.1b-1993\t(POSIX.1b)\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n#if !defined( SEMAPHORE_H )\n#define SEMAPHORE_H\n\n#undef PTW32_SEMAPHORE_LEVEL\n\n#if defined(_POSIX_SOURCE)\n#define PTW32_SEMAPHORE_LEVEL 0\n/* Early POSIX */\n#endif\n\n#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309\n#undef PTW32_SEMAPHORE_LEVEL\n#define PTW32_SEMAPHORE_LEVEL 1\n/* Include 1b, 1c and 1d */\n#endif\n\n#if defined(INCLUDE_NP)\n#undef PTW32_SEMAPHORE_LEVEL\n#define PTW32_SEMAPHORE_LEVEL 2\n/* Include Non-Portable extensions */\n#endif\n\n#define PTW32_SEMAPHORE_LEVEL_MAX 3\n\n#if !defined(PTW32_SEMAPHORE_LEVEL)\n#define PTW32_SEMAPHORE_LEVEL PTW32_SEMAPHORE_LEVEL_MAX\n/* Include everything */\n#endif\n\n#if defined(__GNUC__) && ! defined (__declspec)\n# error Please upgrade your GNU compiler to one that supports __declspec.\n#endif\n\n/*\n * When building the library, you should define PTW32_BUILD so that\n * the variables/functions are exported correctly. When using the library,\n * do NOT define PTW32_BUILD, and then the variables/functions will\n * be imported correctly.\n */\n#if !defined(PTW32_STATIC_LIB)\n#  if defined(PTW32_BUILD)\n#    define PTW32_DLLPORT __declspec (dllexport)\n#  else\n#    define PTW32_DLLPORT\n#  endif\n#else\n#  define PTW32_DLLPORT\n#endif\n\n/*\n * This is a duplicate of what is in the autoconf config.h,\n * which is only used when building the pthread-win32 libraries.\n */\n\n#if !defined(PTW32_CONFIG_H)\n#  if defined(WINCE)\n#    define NEED_ERRNO\n#    define NEED_SEM\n#  endif\n#  if defined(__MINGW64__)\n#    define HAVE_STRUCT_TIMESPEC\n#    define HAVE_MODE_T\n#  elif defined(_UWIN) || defined(__MINGW32__)\n#    define HAVE_MODE_T\n#  endif\n#endif\n\n/*\n *\n */\n\n#if PTW32_SEMAPHORE_LEVEL >= PTW32_SEMAPHORE_LEVEL_MAX\n#if defined(NEED_ERRNO)\n#include \"need_errno.h\"\n#else\n#include <errno.h>\n#endif\n#endif /* PTW32_SEMAPHORE_LEVEL >= PTW32_SEMAPHORE_LEVEL_MAX */\n\n#define _POSIX_SEMAPHORES\n\n#if defined(__cplusplus)\nextern \"C\"\n{\n#endif\t\t\t\t/* __cplusplus */\n\n#if !defined(HAVE_MODE_T)\ntypedef unsigned int mode_t;\n#endif\n\n\ntypedef struct sem_t_ * sem_t;\n\nPTW32_DLLPORT int __cdecl sem_init (sem_t * sem,\n\t\t\t    int pshared,\n\t\t\t    unsigned int value);\n\nPTW32_DLLPORT int __cdecl sem_destroy (sem_t * sem);\n\nPTW32_DLLPORT int __cdecl sem_trywait (sem_t * sem);\n\nPTW32_DLLPORT int __cdecl sem_wait (sem_t * sem);\n\nPTW32_DLLPORT int __cdecl sem_timedwait (sem_t * sem,\n\t\t\t\t const struct timespec * abstime);\n\nPTW32_DLLPORT int __cdecl sem_post (sem_t * sem);\n\nPTW32_DLLPORT int __cdecl sem_post_multiple (sem_t * sem,\n\t\t\t\t     int count);\n\nPTW32_DLLPORT int __cdecl sem_open (const char * name,\n\t\t\t    int oflag,\n\t\t\t    mode_t mode,\n\t\t\t    unsigned int value);\n\nPTW32_DLLPORT int __cdecl sem_close (sem_t * sem);\n\nPTW32_DLLPORT int __cdecl sem_unlink (const char * name);\n\nPTW32_DLLPORT int __cdecl sem_getvalue (sem_t * sem,\n\t\t\t\tint * sval);\n\n#if defined(__cplusplus)\n}\t\t\t\t/* End of extern \"C\" */\n#endif\t\t\t\t/* __cplusplus */\n\n#undef PTW32_SEMAPHORE_LEVEL\n#undef PTW32_SEMAPHORE_LEVEL_MAX\n\n#endif\t\t\t\t/* !SEMAPHORE_H */\n"
  },
  {
    "path": "deps/w32-pthreads/signal.c",
    "content": "/*\n * signal.c\n *\n * Description:\n * Thread-aware signal functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n/*\n * Possible future strategy for implementing pthread_kill()\n * ========================================================\n *\n * Win32 does not implement signals.\n * Signals are simply software interrupts.\n * pthread_kill() asks the system to deliver a specified\n * signal (interrupt) to a specified thread in the same\n * process.\n * Signals are always asynchronous (no deferred signals).\n * Pthread-win32 has an async cancelation mechanism.\n * A similar system can be written to deliver signals\n * within the same process (on ix86 processors at least).\n *\n * Each thread maintains information about which\n * signals it will respond to. Handler routines\n * are set on a per-process basis - not per-thread.\n * When signalled, a thread will check it's sigmask\n * and, if the signal is not being ignored, call the\n * handler routine associated with the signal. The\n * thread must then (except for some signals) return to\n * the point where it was interrupted.\n *\n * Ideally the system itself would check the target thread's\n * mask before possibly needlessly bothering the thread\n * itself. This could be done by pthread_kill(), that is,\n * in the signaling thread since it has access to\n * all pthread_t structures. It could also retrieve\n * the handler routine address to minimise the target\n * threads response overhead. This may also simplify\n * serialisation of the access to the per-thread signal\n * structures.\n *\n * pthread_kill() eventually calls a routine similar to\n * ptw32_cancel_thread() which manipulates the target\n * threads processor context to cause the thread to\n * run the handler launcher routine. pthread_kill() must\n * save the target threads current context so that the\n * handler launcher routine can restore the context after\n * the signal handler has returned. Some handlers will not\n * return, eg. the default SIGKILL handler may simply\n * call pthread_exit().\n *\n * The current context is saved in the target threads\n * pthread_t structure.\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n#if defined(HAVE_SIGSET_T)\n\nstatic void\nptw32_signal_thread ()\n{\n}\n\nstatic void\nptw32_signal_callhandler ()\n{\n}\n\nint\npthread_sigmask (int how, sigset_t const *set, sigset_t * oset)\n{\n  pthread_t thread = pthread_self ();\n\n  if (thread.p == NULL)\n    {\n      return ENOENT;\n    }\n\n  /* Validate the `how' argument. */\n  if (set != NULL)\n    {\n      switch (how)\n\t{\n\tcase SIG_BLOCK:\n\t  break;\n\tcase SIG_UNBLOCK:\n\t  break;\n\tcase SIG_SETMASK:\n\t  break;\n\tdefault:\n\t  /* Invalid `how' argument. */\n\t  return EINVAL;\n\t}\n    }\n\n  /* Copy the old mask before modifying it. */\n  if (oset != NULL)\n    {\n      memcpy (oset, &(thread.p->sigmask), sizeof (sigset_t));\n    }\n\n  if (set != NULL)\n    {\n      unsigned int i;\n\n      /* FIXME: this code assumes that sigmask is an even multiple of\n         the size of a long integer. */\n\n      unsigned long *src = (unsigned long const *) set;\n      unsigned long *dest = (unsigned long *) &(thread.p->sigmask);\n\n      switch (how)\n\t{\n\tcase SIG_BLOCK:\n\t  for (i = 0; i < (sizeof (sigset_t) / sizeof (unsigned long)); i++)\n\t    {\n\t      /* OR the bit field longword-wise. */\n\t      *dest++ |= *src++;\n\t    }\n\t  break;\n\tcase SIG_UNBLOCK:\n\t  for (i = 0; i < (sizeof (sigset_t) / sizeof (unsigned long)); i++)\n\t    {\n\t      /* XOR the bitfield longword-wise. */\n\t      *dest++ ^= *src++;\n\t    }\n\tcase SIG_SETMASK:\n\t  /* Replace the whole sigmask. */\n\t  memcpy (&(thread.p->sigmask), set, sizeof (sigset_t));\n\t  break;\n\t}\n    }\n\n  return 0;\n}\n\nint\nsigwait (const sigset_t * set, int *sig)\n{\n  /* This routine is a cancellation point */\n  pthread_test_cancel();\n}\n\nint\nsigaction (int signum, const struct sigaction *act, struct sigaction *oldact)\n{\n}\n\n#endif /* HAVE_SIGSET_T */\n"
  },
  {
    "path": "deps/w32-pthreads/spin.c",
    "content": "/*\n * spin.c\n *\n * Description:\n * This translation unit implements spin lock primitives.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n#include \"ptw32_spinlock_check_need_init.c\"\n#include \"pthread_spin_init.c\"\n#include \"pthread_spin_destroy.c\"\n#include \"pthread_spin_lock.c\"\n#include \"pthread_spin_unlock.c\"\n#include \"pthread_spin_trylock.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/sync.c",
    "content": "/*\n * sync.c\n *\n * Description:\n * This translation unit implements functions related to thread\n * synchronisation.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n#include \"pthread_detach.c\"\n#include \"pthread_join.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/tests/Bmakefile",
    "content": "# Makefile for the pthreads test suite.\n# If all of the .pass files can be created, the test suite has passed.\n#\n# --------------------------------------------------------------------------\n#\n#      Pthreads-win32 - POSIX Threads Library for Win32\n#      Copyright(C) 1998 John E. Bossom\n#      Copyright(C) 1999,2005 Pthreads-win32 contributors\n# \n#      Contact Email: rpj@callisto.canberra.edu.au\n# \n#      The current list of contributors is contained\n#      in the file CONTRIBUTORS included with the source\n#      code distribution. The list can also be seen at the\n#      following World Wide Web location:\n#      http://sources.redhat.com/pthreads-win32/contributors.html\n# \n#      This library is free software; you can redistribute it and/or\n#      modify it under the terms of the GNU Lesser General Public\n#      License as published by the Free Software Foundation; either\n#      version 2 of the License, or (at your option) any later version.\n# \n#      This library is distributed in the hope that it will be useful,\n#      but WITHOUT ANY WARRANTY; without even the implied warranty of\n#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n#      Lesser General Public License for more details.\n# \n#      You should have received a copy of the GNU Lesser General Public\n#      License along with this library in the file COPYING.LIB;\n#      if not, write to the Free Software Foundation, Inc.,\n#      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n#\n\nDLL_VER\t= 2\n\nCP\t= copy\nRM\t= erase\nCAT\t= type\nMKDIR\t= mkdir\nTOUCH\t= echo Passed >\nECHO\t= @echo\n\n# The next path is relative to $BUILD_DIR\nQAPC\t= # ..\\QueueUserAPCEx\\User\\quserex.dll\n\nCPHDR\t= pthread.h semaphore.h sched.h\n\nOPTIM\t= -O2\n\nXXLIBS\t= cw32mti.lib ws2_32.lib\n\n# C++ Exceptions\nBCEFLAGS\t= -P -DPtW32NoCatchWarn -D__CLEANUP_CXX\nBCELIB\t= pthreadBCE$(DLL_VER).lib\nBCEDLL\t= pthreadBCE$(DLL_VER).dll\n# C cleanup code\nBCFLAGS\t= -D__CLEANUP_C\nBCLIB\t= pthreadBC$(DLL_VER).lib\nBCDLL\t= pthreadBC$(DLL_VER).dll\n# C++ Exceptions in application - using VC version of pthreads dll\nBCXFLAGS\t= -D__CLEANUP_C\n\n# Defaults\nCPLIB\t= $(BCLIB)\nCPDLL\t= $(BCDLL)\n\nCFLAGS= -q $(OPTIM) /D_WIN32_WINNT=0x400 -w -tWC -tWM -4 -w-aus -w-asc -w-par\nLFLAGS= \nINCLUDES=-I.\nBUILD_DIR=..\n\nCOPYFILES\t= $(CPHDR) $(CPLIB) $(CPDLL) $(QAPC)\n\nEHFLAGS\t=\n\n# If a test case returns a non-zero exit code to the shell, make will\n# stop.\n\nPASSES=   loadfree.pass \\\n\t  errno1.pass  \\\n\t  self1.pass  mutex5.pass  \\\n\t  mutex1.pass  mutex1n.pass  mutex1e.pass  mutex1r.pass  \\\n\t  semaphore1.pass  semaphore2.pass  semaphore3.pass  \\\n\t  mutex2.pass  mutex3.pass  \\\n\t  mutex2r.pass  mutex2e.pass  mutex3r.pass  mutex3e.pass  \\\n\t  condvar1.pass  condvar1_1.pass  condvar1_2.pass  condvar2.pass  condvar2_1.pass  \\\n\t  exit1.pass  create1.pass  create2.pass  reuse1.pass  reuse2.pass  equal1.pass  \\\n\t  sequence1.pass  kill1.pass  valid1.pass  valid2.pass  \\\n\t  exit2.pass  exit3.pass  exit4.pass  exit5.pass  \\\n\t  join0.pass  join1.pass  detach1.pass  join2.pass join3.pass  \\\n\t  mutex4.pass  mutex6.pass  mutex6n.pass  mutex6e.pass  mutex6r.pass  \\\n\t  mutex6s.pass  mutex6es.pass  mutex6rs.pass  \\\n\t  mutex7.pass  mutex7n.pass  mutex7e.pass  mutex7r.pass  \\\n\t  mutex8.pass  mutex8n.pass  mutex8e.pass  mutex8r.pass  \\\n\t  robust1.pass  robust2.pass  robust3.pass  robust4.pass  robust5.pass  \\\n\t  count1.pass  \\\n\t  once1.pass  once2.pass  once3.pass  once4.pass  \\\n\t  self2.pass  \\\n\t  cancel1.pass  cancel2.pass  \\\n\t  semaphore4.pass  semaphore4t.pass  semaphore5.pass  \\\n\t  barrier1.pass  barrier2.pass  barrier3.pass  barrier4.pass  barrier5.pass barrier6.pass \\\n\t  tsd1.pass  tsd2.pass  delay1.pass  delay2.pass  eyal1.pass  \\\n\t  condvar3.pass  condvar3_1.pass  condvar3_2.pass  condvar3_3.pass  \\\n\t  condvar4.pass  condvar5.pass  condvar6.pass  \\\n\t  condvar7.pass  condvar8.pass  condvar9.pass  \\\n\t  rwlock1.pass  rwlock2.pass  rwlock3.pass  rwlock4.pass  \\\n\t  rwlock5.pass  rwlock6.pass  rwlock7.pass  rwlock8.pass  \\\n\t  rwlock2_t.pass  rwlock3_t.pass  rwlock4_t.pass  rwlock5_t.pass  rwlock6_t.pass  rwlock6_t2.pass  \\\n\t  context1.pass  \\\n\t  cancel3.pass  cancel4.pass  cancel5.pass  cancel6a.pass  cancel6d.pass  \\\n\t  cancel7.pass  cancel8.pass  \\\n\t  cleanup0.pass  cleanup1.pass  cleanup2.pass  cleanup3.pass  \\\n\t  priority1.pass priority2.pass inherit1.pass  \\\n\t  spin1.pass  spin2.pass  spin3.pass  spin4.pass  \\\n\t  exception1.pass  exception2.pass  exception3.pass  \\\n\t  cancel9.pass  stress1.pass\n\nBENCHRESULTS = \\\n\t  benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench\n\nhelp:\n\t@ $(ECHO) Run one of the following command lines:\n\t@ $(ECHO) make clean BC    (to test using BC dll with VC (no EH) applications)\n\t@ $(ECHO) make clean BCX   (to test using BC dll with VC++ (EH) applications)\n\t@ $(ECHO) make clean BCE   (to test using the BCE dll with VC++ EH applications)\n\t@ $(ECHO) make clean BC-bench    (to benchtest using BC dll with C bench app)\n\t@ $(ECHO) make clean BCX-bench   (to benchtest using BC dll with C++ bench app)\n\t@ $(ECHO) make clean BCE-bench   (to benchtest using BCE dll with C++ bench app)\n\nall:\n\t@ make clean BC\n\t@ make clean BCX\n\t@ make clean BCE\n\t@ make clean BC-bench\n\n# This allows an individual test application to be made using the default lib.\n# e.g. make clean test cancel3.exe\ntest: $(CPLIB) $(CPDLL) $(CPHDR) $(QAPC)\n\ntests: $(CPLIB) $(CPDLL) $(CPHDR) $(QAPC) sizes.pass $(PASSES)\n\t@ $(ECHO) ALL TESTS PASSED! Congratulations!\n\nbenchtests: $(CPLIB) $(CPDLL) $(CPHDR) $(BENCHRESULTS)\n\t@ $(ECHO) ALL BENCH TESTS DONE.\n\nsizes.pass: sizes.exe\n\t@ $(ECHO) ... Running $(TEST) test: $*.exe\n\t@ .\\$*.exe > SIZES.$(TEST)\n\t@ $(CAT) SIZES.$(TEST)\n\t@ $(ECHO) ...... Passed\n\t@ $(TOUCH) $*.pass\n\nBCE:\n\t@ make -f Bmakefile TEST=\"$@\" CPLIB=\"$(BCELIB)\" CPDLL=\"$(BCEDLL)\" EHFLAGS=\"$(BCEFLAGS)\" tests\n\nBC:\n\t@ make -f Bmakefile TEST=\"$@\" CPLIB=\"$(BCLIB)\" CPDLL=\"$(BCDLL)\" EHFLAGS=\"$(BCFLAGS)\" tests\n\nBCX:\n\t@ make -f Bmakefile TEST=\"$@\" CPLIB=\"$(BCLIB)\" CPDLL=\"$(BCDLL)\" EHFLAGS=\"$(BCXFLAGS)\" tests\n\nBCE-bench:\n\t@ make -f Bmakefile TEST=\"$@\" CPLIB=\"$(BCELIB)\" CPDLL=\"$(BCEDLL)\" EHFLAGS=\"$(BCEFLAGS)\" XXLIBS=\"benchlib.o\" benchtests\n\nBC-bench:\n\t@ make -f Bmakefile TEST=\"$@\" CPLIB=\"$(BCLIB)\" CPDLL=\"$(BCDLL)\" EHFLAGS=\"$(BCFLAGS)\" XXLIBS=\"benchlib.o\" benchtests\n\nBCX-bench:\n\t@ make -f Bmakefile TEST=\"$@\" CPLIB=\"$(BCLIB)\" CPDLL=\"$(BCDLL)\" EHFLAGS=\"$(BCXFLAGS)\" XXLIBS=\"benchlib.o\" benchtests\n\n.exe.pass:\n\t@ $(ECHO) ... Running $(TEST) test: $<\n\t@ .\\$<\n\t@ $(ECHO) ...... Passed\n\t@ $(TOUCH) $@\n\n.exe.bench:\n\t@ $(ECHO) ... Running $(TEST) benchtest: $<\n\t@ .\\$<\n\t@ $(ECHO) ...... Done\n\t@ $(TOUCH) $@\n\n.c.exe:\n\t@ $(ECHO) $(CC) $(EHFLAGS) $(CFLAGS) $(INCLUDES) $< -e$@ $(LFLAGS) $(CPLIB) $(XXLIBS)\n\t@ $(CC) $(EHFLAGS) $(CFLAGS) $(INCLUDES) $< -e$@ $(LFLAGS) $(CPLIB) $(XXLIBS)\n\n.c.o:\n\t@ $(ECHO) $(CC) $(EHFLAGS) -c $(CFLAGS) $(INCLUDES) $< -o$@\n\t@ $(CC) $(EHFLAGS) $(CFLAGS) -c $(INCLUDES) $< -o$@\n\n\n.c.i:\n\t@ $(CC) /P $(EHFLAGS) $(CFLAGS) $(INCLUDES) $<\n\n$(COPYFILES):\n\t@ $(ECHO) Copying $(BUILD_DIR)\\$@\n\t@ $(CP) $(BUILD_DIR)\\$@ .\n\npthread.dll: $(CPDLL)\n\t@ $(CP) $(CPDLL) pthread.dll\n\t@ $(CP) $(CPLIB) pthread.lib\n\nclean:\n\t- $(RM) *.dll\n\t- $(RM) *.lib\n\t- $(RM) pthread.h\n\t- $(RM) semaphore.h\n\t- $(RM) sched.h\n\t- $(RM) *.e\n\t- $(RM) *.i\n\t- $(RM) *.obj\n\t- $(RM) *.tds\n\t- $(RM) *.pdb\n\t- $(RM) *.o\n\t- $(RM) *.asm\n\t- $(RM) *.exe\n\t- $(RM) *.pass\n\t- $(RM) *.bench\n\t- $(RM) *.log\n\nbenchtest1.bench:\nbenchtest2.bench:\nbenchtest3.bench:\nbenchtest4.bench:\nbenchtest5.bench:\nbarrier1.pass: semaphore4.pass\nbarrier2.pass: barrier1.pass\nbarrier3.pass: barrier2.pass\nbarrier4.pass: barrier3.pass\nbarrier5.pass: barrier4.pass\nbarrier6.pass: barrier5.pass\ncancel1.pass: create1.pass\ncancel2.pass: cancel1.pass\ncancel3.pass: context1.pass\ncancel4.pass: cancel3.pass\ncancel5.pass: cancel3.pass\ncancel6a.pass: cancel3.pass\ncancel6d.pass: cancel3.pass\ncancel7.pass: kill1.pass\ncancel8.pass: cancel7.pass\ncancel9.pass: cancel8.pass\ncleanup0.pass: cancel5.pass\ncleanup1.pass: cleanup0.pass\ncleanup2.pass: cleanup1.pass\ncleanup3.pass: cleanup2.pass\ncondvar1.pass:\ncondvar1_1.pass: condvar1.pass\ncondvar1_2.pass: join2.pass\ncondvar2.pass: condvar1.pass\ncondvar2_1.pass: condvar2.pass join2.pass\ncondvar3.pass: create1.pass condvar2.pass\ncondvar3_1.pass: condvar3.pass join2.pass\ncondvar3_2.pass: condvar3_1.pass\ncondvar3_3.pass: condvar3_2.pass\ncondvar4.pass: create1.pass\ncondvar5.pass: condvar4.pass\ncondvar6.pass: condvar5.pass\ncondvar7.pass: condvar6.pass cleanup1.pass\ncondvar8.pass: condvar7.pass\ncondvar9.pass: condvar8.pass\ncontext1.pass: cancel1.pass\ncount1.pass: join1.pass\ncreate1.pass: mutex2.pass\ncreate2.pass: create1.pass\ndelay1.pass:\ndelay2.pass: delay1.pass\ndetach1.pass: join0.pass\nequal1.pass: create1.pass\nerrno1.pass: mutex3.pass\nexception1.pass: cancel4.pass\nexception2.pass: exception1.pass\nexception3.pass: exception2.pass\nexit1.pass:\nexit2.pass: create1.pass\nexit3.pass: create1.pass\nexit4.pass:\nexit5.pass: kill1.pass\neyal1.pass: tsd1.pass\ninherit1.pass: join1.pass priority1.pass\njoin0.pass: create1.pass\njoin1.pass: create1.pass\njoin2.pass: create1.pass\njoin3.pass: join2.pass\nkill1.pass: \nloadfree.pass: pthread.dll\nmutex1.pass: self1.pass\nmutex1n.pass: mutex1.pass\nmutex1e.pass: mutex1.pass\nmutex1r.pass: mutex1.pass\nmutex2.pass: mutex1.pass\nmutex2r.pass: mutex2.pass\nmutex2e.pass: mutex2.pass\nmutex3.pass: create1.pass\nmutex3r.pass: mutex3.pass\nmutex3e.pass: mutex3.pass\nmutex4.pass: mutex3.pass\nmutex5.pass:\nmutex6.pass: mutex4.pass\nmutex6n.pass: mutex4.pass\nmutex6e.pass: mutex4.pass\nmutex6r.pass: mutex4.pass\nmutex6s.pass: mutex6.pass\nmutex6rs.pass: mutex6r.pass\nmutex6es.pass: mutex6e.pass\nmutex7.pass: mutex6.pass\nmutex7n.pass: mutex6n.pass\nmutex7e.pass: mutex6e.pass\nmutex7r.pass: mutex6r.pass\nmutex8.pass: mutex7.pass\nmutex8n.pass: mutex7n.pass\nmutex8e.pass: mutex7e.pass\nmutex8r.pass: mutex7r.pass\nonce1.pass: create1.pass\nonce2.pass: once1.pass\nonce3.pass: once2.pass\nonce4.pass: once3.pass\npriority1.pass: join1.pass\npriority2.pass: priority1.pass barrier3.pass\nreuse1.pass: create2.pass\nreuse2.pass: reuse1.pass\nrobust1.pass: mutex8r.pass\nrobust2.pass: mutex8r.pass\nrobust3.pass: robust2.pass\nrobust4.pass: robust3.pass\nrobust5.pass: robust4.pass\nrwlock1.pass: condvar6.pass\nrwlock2.pass: rwlock1.pass\nrwlock3.pass: rwlock2.pass join2.pass\nrwlock4.pass: rwlock3.pass\nrwlock5.pass: rwlock4.pass\nrwlock6.pass: rwlock5.pass\nrwlock7.pass: rwlock6.pass\nrwlock8.pass: rwlock7.pass\nrwlock2_t.pass: rwlock2.pass\nrwlock3_t.pass: rwlock2_t.pass\nrwlock4_t.pass: rwlock3_t.pass\nrwlock5_t.pass: rwlock4_t.pass\nrwlock6_t.pass: rwlock5_t.pass\nrwlock6_t2.pass: rwlock6_t.pass\nself1.pass:\nself2.pass: create1.pass\nsemaphore1.pass:\nsemaphore2.pass:\nsemaphore3.pass: semaphore2.pass\nsemaphore4.pass: semaphore3.pass cancel1.pass\nsemaphore4t.pass: semaphore4.pass\nsemaphore5.pass: semaphore4.pass\nsequence1.pass: reuse2.pass\nsizes.pass:\nspin1.pass:\nspin2.pass: spin1.pass\nspin3.pass: spin2.pass\nspin4.pass: spin3.pass\nstress1.pass:\ntsd1.pass: barrier5.pass join1.pass\ntsd2.pass: tsd1.pass\nvalid1.pass: join1.pass\nvalid2.pass: valid1.pass\n"
  },
  {
    "path": "deps/w32-pthreads/tests/ChangeLog",
    "content": "2011-07-03  Ross Johnson <ross dot johnson at homemail dot com dot au>\n\n\t* create3.c: Removed; testing a condition that is not in the library's\n\tscope and was more trouble than it was worth.\n\t* cancel2.c: Ensure this test only runs for Structured or C++ EH.\n\t* exit2.c: Shorten Sleep() time.\n\t* exit3.c: Likewise.\n\t* cancel1.c: Likewise.\n\t* cancel3.c: Likewise.\n\t* exception3.c: Likewise; make terminate routine consistent for all\n\tbuild environments.\n\n2011-07-02  Ross Johnson <ross dot johnson at homemail dot com dot au>\n\n\t* spin3.c: Unlock the unlocked spinlock now returns success.\n\t* rwlock3.c: Join the thread to ensure it's completed.\n\t* rwlock4.c: Likewise.\n\t* rwlock5.c: Likewise.\n\t* Makefile: Adjust prerequisites.\n\t* GNUmakefile: Likewise.\n\t* Bmakefile: Likewise.\n\t* Wmakefile: Likewise.\n\n2011-07-02 Daniel Richard G. <skunk at iskunk dot org>\n\n\t* *.[ch]: Cleanups around timeb struct, mainly centralising\n\tmacro definitions in test.h.\n\t* Makefile: Fix annoying nmake warning.\n\n2011-06-30  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* sequence1.c: Fix loop overrun.\n\n2011-05-11  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* GNUmakefile (GCE-debug): New target; expects pthreadGCE2d.dll.\n\t\n2011-05-05  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* openmp1.c: Add missing test; used to comfirm that this\n\tlibrary works with libgomp; if this test produces a segfault\n\tthen try upgrading your version of libgomp/gcc; gcc version\n\t4.5.2 passes this test.\n\n2011-03-26  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* sequence1.c: New test for new pthread_getsequence_np().\n\n2011-03-24  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* mutex*.c: Include tests for robust mutexes wherever\n\tappropriate.\n\t* benchtest*.c: Include comparisons for robust mutexes.\n\t* robust1.c: New test for robust mutex handling.\n\t* robust2.c: Likewise.\n\t* robust3.c: Likewise.\n\t* robust4.c: Likewise.\n\t* robust5.c: Likewise.\n\t* GNUmakefile: Include new tests.\n\t* Makefile: Likewise.\n\t* Bmakefile: Likewise (not tested).\n\t* Wmakefile: Likewise (not tested).\n\n2011-03-06  Ross Johnson <ross.johnson at homemail.com.au>\n\n\t* several (MINGW64): Cast and call fixups for 64 bit compatibility;\n\tclean build via x86_64-w64-mingw32 cross toolchain on Linux\n\ti686 targeting x86_64 win64.\n\n2011-03-04  Ross Johnson <Ross dot Johnson at homemail dot com dot au>\n\n\t* condvar3_2.c: abstime.tv_sec operation warning fixed.\n\t* several: Use correct casting on pthread_join result arg\n\tand associated declaration and usage; assumed that 64 bit\n\tgcc gave some warnings for it.\n\n2011-02-28  Ross Johnson <Ross dot Johnson at homemail dot com dot au>\n\n\t* test.h: Define FTIME to be _ftime64_s or _ftime64 or _ftime\n\tin that order of preference where supported.\n\t* several: Replace calls to _ftime with the FTIME macro.\n\n2010-06-19  Ross Johnson <Ross dot Johnson at homemail dot com dot au>\n\n\t* Makefile (STATICRESULTS): Add all tests into suite for static\n\tlibrary.\n\t* GNUmakefile (STATICTESTS): Likewise, except for openmp1.c which\n\thas a DLL dependency.\n\n2010-02-04  Ross Johnson <Ross dot Johnson at homemail dot com dot au>\n\n\t* openmp1.c: New; for libgomp compatibility (OpenMP).\n\t* barrier5.c: Rewrite after changes to barriers.\n\t* barrier6.c: New.\n\t* benchtest6.c: New; timing barriers.\n\t* GNUMakefile: Update for new tests.\n\t* Makefile: Ditto.\n\t* BMakefile: Ditto.\n\t* once3.c: Improve cancelation testing.\n\t* stress1.c: Fix comment.\n\n2007-01-04  Ross Johnson <Ross dot Johnson at homemail dot com dot au>\n\n        * context1.c: Include context.h from library sources and remove\n        x86 dependence in main().\n\n2005-06-12  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* stress1.c (millisecondsFromNow): Remove limit 0 <= millisecs < 1000;\n\tnow works for -INT_MAX <= millisecs <= INT_MAX; not needed for\n\tstress1.c but should be general anyway.\n\n2005-05-18  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* reuse2.c (main): Must use a read with memory barrier semantics\n\twhen polling 'done' to force the cache into coherence on MP systems.\n\n2005-05-15  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* detach1.c: New test.\n\t* join1.c: Reduce sleep times.\n\t* join0.c: Remove MSVCRT conditional compile - join should always\n\treturn the thread exit code.\n\t* join1.c: Likewise.\n\t* join2.c: Likewise.\n\t* join3.c: Likewise.\n\n2005-04-18  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* condvar3.c: Remove locks from around signalling calls - should not\n\tbe required for normal operation and only serve to mask deficiencies;\n\tensure that CV destruction is not premature after removing guards.\n\t* condvar3_1.c: Likewise.\n\t* condvar3_2.c: Likewise.\n\t* condvar3_3.c: Likewise.\n\t* condvar4.c: Likewise.\n\t* condvar5.c: Likewise.\n\t* condvar6.c: Likewise.\n\t* condvar7.c: Likewise.\n\t* condvar8.c: Likewise.\n\t* condvar9.c: Likewise.\n\n2005-04-11  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n        * once4.c: New test; tries to test priority adjustments\n        in pthread_once(); set priority class to realtime so that\n        any failures can be seen.\n\n2005-04-06  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* cleanup0.c: Fix unguarded global variable accesses.\n\t* cleanup1.c: Likewise.\n\t* cleanup2.c: Likewise.\n\t* cleanup3.c: Likewise.\n\t* once2.c: Likewise.\n\t* once3.c: Likewise.\n\n2005-04-01  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* GNUmakefile: Add target to test linking static link library.\n\t* Makefile: Likewise.\n\t* self1.c: Run process attach/detach routines when static linked.\n\n2005-03-16  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* mutex5.c: Prevent optimiser from removing asserts.\n\n2005-03-12  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* once3.c: New test.\n\n2005-03-08  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n        * once2.c: New test.\n\n2004-11-19  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* Bmakefile: New makefile for Borland.\n\t* Makefile (DLL_VER): Added.\n\t* GNUmakefile (DLL_VER): Added.\n\t* Wmakefile (DLL_VER): Added.\n\n2004-10-29  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* semaphore4.c: New test.\n\t* semaphore4t.c: New test.\n\t* Debug.dsp (et al): Created MSVC Workspace project to aid debugging.\n\t* All: Many tests have been modified to work with the new pthread\n\tID type; some other corrections were made after some library\n\tfunctions were semantically strengthened. For example,\n\tpthread_cond_destroy() no longer destroys a busy CV, which\n\trequired minor redesigns of some tests, including some where\n\tthe mutex associated with the CV was not locked during\n\tsignaling and broadcasting.\n\n2004-10-23  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* condvar3.c: Fixed mutex operations that were incorrectly\n\tplaced in relation to their condition variable operations.\n\tThe error became evident after sem_destroy() was rewritten\n\tand conditions for destroing the semaphore were tightened.\n\tAs a result, pthread_cond_destroy() was not able to\n\tdestroy the cv queueing sempahore.\n\t* condvar3_1.c: Likewise.\n\t* condvar3_2.c: Likewise.\n\t* condvar4.c: Likewise.\n\t* condvar5.c: Likewise.\n\t* condvar6.c: Likewise.\n\t* condvar7.c: Likewise.\n\t* condvar8.c: Likewise.\n\t* condvar9.c: Likewise.\n\n2004-10-19  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* semaphore3.c: New test.\n\n2004-10-14  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* rwlock7.c (main): Tidy up statistics reporting; randomise\n\tupdate accesses.\n\t* rwlock8.c: New test.\n\n2004-09-08  Alexandre Girao  <alexgirao@gmail.com>\n\n\t* cancel7.c (main): Win98 wants a valid (non-NULL) location\n\tfor the last arg of _beginthreadex().\n\t* cancel8.c (main): Likewise.\n\t* exit4.c (main): Likewise.\n\t* exit5.c (main): Likewise.\n\n2004-08-26  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* create3.c: New test.\n\n2004-06-21  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* mutex2r.c: New test.\n\t* mutex2e.c: New test.\n\t* mutex3r.c: New test.\n\t* mutex3e.c: New test.\n\t* mutex6s.c: New test.\n\t* mutex6rs.c: New test.\n\t* mutex6es.c: New test.\n\n2004-05-21  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* join3.c: New test.\n\n2004-05-16  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* condvar2.c (WIN32_WINNT): Define to avoid redefinition warning\n\tfrom inclusion of implement.h.\n\t* convar2_1.c: Likewise.\n\t* condvar3_1.c: Likewise.\n\t* condvar3_2.c: Likewise.\n\t* context1.c: Likewise.\n\t* sizes.c: Likewise.\n\t* Makefile: Don't define _WIN32_WINNT on compiler command line.\n\t* GNUmakefile: Likewise.\n\t* priority1.c (main): Add column to output for actual win32\n\tpriority.\n\n2004-05-16  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* cancel9.c: New test.\n\t* cancel3.c: Remove inappropriate conditional compilation;\n\tGNU C version of test suite no longer quietly skips this test.\n\t* cancel5.c: Likewise.\n\t* GNUmakefile: Can now build individual test app using default\n\tC version of library using 'make clean testname.c'.\n\t* Makefile: Likewise for VC using 'nmake clean test testname.c'.\n\n2003-10-14  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* Wmakefile: New makefile for Watcom testing.\n\n2003-09-18  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* benchtest.h: Move old mutex code into benchlib.c.\n\t* benchlib.c: New statically linked module to ensure that\n\tbench apps don't inline the code and therefore have an unfair\n\tadvantage over the pthreads lib routines. Made little or no\n\tdifference.\n\t* benchtest1.c: Minor change to avoid compiler warnings.\n\t* benchtest5.c: Likewise.\n\t* benchtest2.c: Fix misinformation in output report.\n\t* README.BENCH: Add comments on results.\n\n2003-09-14  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* priority1.c: Reworked to comply with modified priority\n\tmanagement and provide additional output.\n\t* priority2.c: Likewise.\n\t* inherit1.c: Likewise.\n\n2003-09-03  Ross Johnson  <rpj@callisto.canberra.edu.au>\n\n\t* exit4.c: New test.\n\t* exit5.c: New test.\n\t* cancel7.c: New test.\n\t* cancel8.c: New test.\n\n2003-08-13  Ross Johnson  <rpj@ise.canberra.edu.au>\n\n\t* reuse1.c: New test.\n\t* reuse1.c: New test.\n\t* valid1.c: New test.\n\t* valid2.c: New test.\n\t* kill1.c: New test.\n \t* create2.c: Now included in test regime.\n\n2003-07-19  Ross Johnson  <rpj@ise.canberra.edu.au>\n\n\t* eyal1.c (waste_time): Make threads do more work to ensure that\n\tall threads get to do some work.\n\t* semaphore1.c: Make it clear that certain errors are expected.\n\t* exception2.c (non_MSVC code sections): Change to include\n\tC++ standard include file, i.e. change <new.h> to <exception>.\n\t* exception3.c (non_MSVC code sections): Likewise; qualify std::\n\tnamespace entities where necessary.\n\t* GNUmakefile: modified to work in the MsysDTK (newer MinGW)\n\tenvironment; define CC as gcc or g++ as appropriate because\n\tusing gcc -x c++ doesn't link with required c++ libs by default,\n\tbut g++ does.\n\n2002-12-11  Ross Johnson  <ross@special.ise.canberra.edu.au>\n\n\t* mutex7e.c: Assert EBUSY return instead of EDEADLK.\n\n2002-06-03  Ross Johnson  <rpj@digit.ise.canberra.edu.au>\n\n\t* semaphore2.c: New test.\n\n2002-03-02  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* Makefile (CFLAGS): Changed /MT to /MD to link with\n\tthe correct library MSVCRT.LIB. Otherwise errno doesn't\n\twork.\n\n2002-02-28  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* exception3.c: Correct recent change.\n\n\t* semaphore1.c: New test.\n\n\t* Makefile: Add rule to generate pre-processor output.\n\n2002-02-28  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* exception3.c (terminateFunction): For MSVC++, call\n\texit() rather than pthread_exit(). Add comments to explain\n\twhy.\n\t   * Notes from the MSVC++ manual:\n\t   *       1) A term_func() should call exit(), otherwise\n\t   *          abort() will be called on return to the caller.\n\t   *          abort() raises SIGABRT. The default signal handler\n\t   *          for all signals terminates the calling program with\n\t   *          exit code 3.\n\t   *       2) A term_func() must not throw an exception. Therefore\n\t   *          term_func() should not call pthread_exit() if an\n\t   *          an exception-using version of pthreads-win32 library\n\t   *          is being used (i.e. either pthreadVCE or pthreadVSE).\n\n\n2002-02-23  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* rwlock2_t.c: New test.\n\t* rwlock3_t.c: New test.\n\t* rwlock4_t.c: New test.\n\t* rwlock5_t.c: New test.\n\t* rwlock6_t.c: New test.\n\t* rwlock6_t2.c: New test.\n\t* rwlock6.c (main): Swap thread and result variables\n\tto correspond to actual thread functions.\n\t* rwlock1.c: Change test description comment to correspond\n\tto the actual test.\n\n\t* condvar1_2.c: Loop over the test many times in the hope\n\tof detecting any intermittent deadlocks. This is to\n\ttest a fixed problem in pthread_cond_destroy.c.\n\n\t* spin4.c: Remove unused variable.\n\n2002-02-17  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* condvar1_1.c: New test.\n\t* condvar1_2.c: New test.\n\n2002-02-07  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* delay1.c: New test.\n\t* delay2.c: New test.\n\t* exit4.c: New test.\n\n2002-02-02  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* mutex8: New test.\n\t* mutex8n: New test.\n\t* mutex8e: New test.\n\t* mutex8r: New test.\n\t* cancel6a: New test.\n\t* cancel6d: New test.\n\t* cleanup0.c: Add pragmas for inline optimisation control.\n\t* cleanup1.c: Add pragmas for inline optimisation control.\n\t* cleanup2.c: Add pragmas for inline optimisation control.\n\t* cleanup3.c: Add pragmas for inline optimisation control.\n\t* condvar7.c: Add pragmas for inline optimisation control.\n\t* condvar8.c: Add pragmas for inline optimisation control.\n\t* condvar9.c: Add pragmas for inline optimisation control.\n\n2002-01-30  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* cleanup1.c (): Must be declared __cdecl when compiled\n\tas C++ AND testing the standard C library version.\n\n2002-01-16  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* spin4.c (main): Fix renamed function call.\n\n2002-01-14  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* exception3.c (main): Shorten wait time.\n\n2002-01-09  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* mutex7.c: New test.\n\t* mutex7n.c: New test.\n\t* mutex7e.c: New test.\n\t* mutex7r.c: New test.\n\t* mutex6.c: Modified to avoid leaving the locked mutex\n\taround on exit.\n\n2001-10-25  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>\n\n\t* condvar2.c: Remove reference to cv->nWaitersUnblocked.\n\t* condvar2_1.c: Likewise; lower NUMTHREADS from 60 to 30.\n\t* condvar3_1.c: Likewise.\n\t* condvar3_2.c: Likewise.\n\t* count1.c: lower NUMTHREADS from 60 to 30.\n\t* inherit1.c: Determine valid priority values and then\n\tassert values returned by POSIX routines are the same.\n\t* priority1.c: Likewise.\n\t* priority2.c: Likewise.\n\t\n2001-07-12  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>\n\n\t* barrier5.c: Assert that precisely one thread receives\n\tPTHREAD_BARRIER_SERIAL_THREAD at each barrier.\n\n2001-07-09  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>\n\n\t* barrier3.c: Fixed.\n\t* barrier4.c: Fixed.\n\t* barrier5.c: New; proves that all threads in the group\n\treaching the barrier wait and then resume together. Repeats the test\n\tusing groups of 1 to 16 threads. Each group of threads must negotiate\n\ta large number of barriers (10000).\n\t* spin4.c: Fixed.\n\t* test.h (error_string): Modified the success (0) value.\n\n2001-07-07  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>\n\n\t* spin3.c: Changed test and fixed.\n\t* spin4.c: Fixed.\n\t* barrier3.c: Fixed.\n\t* barrier4.c: Fixed.\n\n2001-07-05  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* spin1.c: New; testing spinlocks.\n\t* spin2.c: New; testing spinlocks.\n\t* spin3.c: New; testing spinlocks.\n\t* spin4.c: New; testing spinlocks.\n\t* barrier1.c: New; testing barriers.\n\t* barrier2.c: New; testing barriers.\n\t* barrier3.c: New; testing barriers.\n\t* barrier4.c: New; testing barriers.\n\t* GNUmakefile: Add new tests.\n\t* Makefile: Add new tests.\n\n2001-07-01  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* benchtest3.c: New; timing mutexes.\n\t* benchtest4.c: New; time mutexes.\n\t* condvar3_1.c: Fixed bug - Alexander Terekhov\n\t* condvar3_3.c: New test.\n\n2001-06-25  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* priority1.c: New test.\n\t* priority2.c: New test.\n\t* inherit1.c: New test.\n\t* benchtest1.c: New; timing mutexes.\n\t* benchtest2.c: New; timing mutexes.\n\t* mutex4.c: Modified to test all mutex types.\n\n2001-06-8  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* mutex5.c: Insert inert change to quell compiler warnings.\n\t* condvar3_2.c: Remove unused variable.\n\t\n2001-06-3  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* condvar2_1.c: New test.\n\t* condvar3_1.c: New test.\n\t* condvar3_2.c: New test.\n\n2001-05-30  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* mutex1n.c: New test.\n\t* mutex1e.c: New test.\n\t* mutex1r.c: New test.\n\t* mutex4.c: Now locks and unlocks a mutex.\n\t* mutex5.c: New test.\n\t* mutex6.c: New test.\n\t* mutex6n.c: New test.\n\t* mutex6e.c: New test.\n\t* mutex6r.c: New test.\n\t* Makefile: Added new tests; reorganised.\n\t* GNUmakefile: Likewise.\n\t* rwlock6.c: Fix to properly prove read-while-write locking\n\tand single writer locking.\n\n2001-05-29  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* Makefile: Reorganisation.\n\t* GNUmakefile: Likewise.\n\t- Thomas Pfaff <tpfaff@gmx.net>\n\n\t* exception1.c: Add stdio.h include to define fprintf and stderr\n\tin non-exception C version of main().\n\t* exception2.c: Likewise.\n\t* exception3.c: Likewise.\n\n\t* Makefile (rwlock7): Add new test.\n\t* GNUmakefile (rwlock7): Add new test.\n\t* rwlock7.c: New test.\n\t* rwlock6.c: Changed to test that writer has priority.\n\n\t* eyal1.c (main): Unlock each mutex_start lock before destroying\n\tit.\n\n2000-12-29  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* GNUmakefile: Add mutex4 test; ensure libpthreadw32.a is\n\tremoved for \"clean\" target.\n\t* Makefile: Add mutex4 test.\n\n\t* exception3.c: Remove SEH code; automatically pass the test\n\tunder SEH (which is an N/A environment).\n\n\t* mutex4.c: New test.\n\n\t* eyal1.c (do_work_unit): Add a dummy \"if\" to force the\n\toptimiser to retain code; reduce thread work loads.\n\n\t* condvar8.c (main): Add an additional \"assert\" for debugging;\n\tincrease pthread_cond_signal timeout.\n\n2000-12-28  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* eyal1.c: Increase thread work loads.\n\t* exception2.c: New test.\n\t* exception3.c: New test.\n\t* Makefile: Add new tests exception2.c and exception3.c.\n\t* GNUmakefile: Likewise.\n\n2000-12-11  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* cleanup3.c: Remove unused variable.\n\t* cleanup2.c: Likewise.\n\t* exception1.c: Throw an exception rather than use\n\ta deliberate zero divide so that catch(...) will\n\thandle it under Mingw32. Mingw32 now builds the\n\tlibrary correctly to pass all tests - see Thomas\n\tPfaff's detailed instructions re needed changes\n\tto Mingw32 in the Pthreads-Win32 FAQ.\n\n2000-09-08  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* cancel5.c: New; tests calling pthread_cancel()\n\tfrom the main thread without first creating a\n\tPOSIX thread struct for the non-POSIX main thread\n\t- this forces pthread_cancel() to create one via\n\tpthread_self().\n\t* Makefile (cancel5): Add new test.\n\t* GNUmakefile (cancel5): Likewise.\n\n2000-08-17  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* create2.c: New; Test that pthread_t contains\n\tthe W32 HANDLE before it calls the thread routine\n\tproper.\n\n2000-08-13  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* condvar3.c: Minor change to eliminate compiler\n\twarning.\n\n\t* condvar4.c: ditto.\n\n\t* condvar5.c: ditto.\n\n\t* condvar6.c: ditto.\n\n\t* condvar7.c: ditto.\n\n\t* condvar8.c: ditto.\n\n\t* condvar9.c: ditto.\n\n\t* exit1.c: Function needed return statement.\n\n\t* cleanup1.c: Remove unnecessary printf arg.\n\n\t* cleanup2.c: Fix cast.\n\n\t* rwlock6.c: Fix casts.\n\n\t* exception1.c (PtW32CatchAll): Had the wrong name;\n\tfix casts.\n\n\t* cancel3.c: Remove unused waitLock variable.\n\n\t* GNUmakefile: Change library/dll naming; add new tests;\n\tgeneral minor changes.\n\n\t* Makefile: Change library/dll naming; add targets for\n\ttesting each of the two VC++ EH scheme versions;\n\tdefault target now issues help message; compile warnings\n\tnow interpreted as errors to stop the make; add new\n\ttests; restructure to remove prerequisites needed\n\totherwise.\n\n\t* README: Updated.\n\n\n2000-08-10  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* eyal1.c (main): Change implicit cast to explicit\n\tcast when passing \"print_server\" function pointer;\n\tG++ no longer allows implicit func parameter casts.\n\n\t* cleanup1.c: Remove unused \"waitLock\".\n\t(main): Fix implicit parameter cast.\n\n\t* cancel2.c (main): Fix implicit parameter cast.\n\n\t* cancel4.c (main): Fix implicit parameter cast.\n\n\t* cancel3.c (main): Fix implicit parameter cast.\n\n\t* GNUmakefile: Renamed from Makefile; Add missing\n\tcancel1 and cancel2 test targets.\n\n\t* Makefile: Converted for use with MS nmake.\n\n2000-08-06  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* ccl.bat: Add /nologo to remove extraneous output.\n\n\t* exception1.c (exceptionedThread): Init 'dummy';\n\tput expression into if condition to prevent optimising away;\n\tremove unused variable.\n\n\t* cancel4.c (mythread): Cast return value to avoid warnings.\n\n\t* cancel2.c (mythread): Missing #endif.\n\n\t* condvar9.c (mythread): Cast return value to avoid warnings.\n\n\t* condvar8.c (mythread): Cast return value to avoid warnings.\n\n\t* condvar7.c (mythread): Cast return value to avoid warnings.\n\n\t* cleanup3.c (mythread): Cast return value to avoid warnings.\n\n\t* cleanup2.c (mythread): Cast return value to avoid warnings.\n\n\t* cleanup1.c (mythread): Cast return value to avoid warnings.\n\n\t* condvar5.c (mythread): Cast return value to avoid warnings.\n\n\t* condvar3.c (mythread): Cast return value to avoid warnings.\n\n\t* condvar6.c (mythread): Cast return value to avoid warnings.\n\n\t* condvar4.c (mythread): Cast return value to avoid warnings.\n\n2000-08-05  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* cancel2.c: Use PtW32CatchAll macro if defined.\n\n\t* exception1.c: Use PtW32CatchAll macro if defined.\n\n2000-08-02  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* tsd1.c: Fix typecasts of &result [g++ is now very fussy].\n\t\n\t* test.h (assert): Return 0's explicitly to allay\n\tg++ errors.\n\t\n\t* join2.c: Add explicit typecasts.\n\t\n\t* join1.c: Add explicit typecasts.\n\t\n\t* join0.c: Add explicit typecasts.\n\t\n\t* eyal1.c: Add explicit typecasts.\n\t\n\t* count1.c (main): Add type cast to remove g++ parse warning\n\t[gcc-2.95.2 seems to have tightened up on this].\n\n\t* Makefile (GLANG): Use c++ explicitly.\n\tRemove MSVC sections (was commented out).\n\tAdd target to generate cpp output.\n\n2000-07-25  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* runtest.bat: modified to work under W98.\n\t\n\t* runall.bat: Add new tests; modified to work under W98.\n\tIt was ok under NT.\n\n\t* Makefile: Add new tests.\n\n\t* exception1.c: New; Test passing exceptions back to the\n\tapplication and retaining library internal exceptions.\n\n\t* join0.c: New; Test a single join.\n\n2000-01-06  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* cleanup1.c: New; Test cleanup handler executes (when thread is\n\tcanceled).\n\n\t* cleanup2.c: New; Test cleanup handler executes (when thread is\n\tnot canceled).\n\n\t* cleanup3.c: New; Test cleanup handler does not execute\n\t(when thread is not canceled).\n\n2000-01-04  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* cancel4.c: New; Test cancelation does not occur in deferred\n\tcancelation threads with no cancelation points.\n\n\t* cancel3.c: New; Test asynchronous cancelation.\n\n\t* context1.c: New; Test context switching method for async\n\tcancelation.\n\n1999-11-23  Ross Johnson  <rpj@special.ise.canberra.edu.au>\n\n\t* test.h: Add header includes; include local header versions rather\n\tthan system versions; rearrange the assert macro defines.\n\n1999-11-07  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* loadfree.c: New. Test loading and freeing the library (DLL).\n\n1999-10-30  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* cancel1.c: New. Test pthread_setcancelstate and\n\tpthread_setcanceltype functions.\n\t* eyal1.c (waste_time): Change calculation to avoid FP exception\n\ton Aplhas\n\t- Rich Peters <rpeters@micro-magic.com>\n\nOct 14 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* condvar7.c: New. Test broadcast after waiting thread is canceled.\n\t* condvar8.c: New. Test multiple broadcasts.\n\t* condvar9.c: New. Test multiple broadcasts with thread\n\tcancelation.\n\t\nSep 16 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* rwlock6.c: New test.\n\nSep 15 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* rwlock1.c: New test.\n\t* rwlock2.c: New test.\n\t* rwlock3.c: New test.\n\t* rwlock4.c: New test.\n\t* rwlock5.c: New test.\n\nAug 22 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* runall.bat (join2): Add test.\n\nAug 19 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* join2.c: New test.\n\nWed Aug 12 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* Makefile (LIBS): Add -L.\n\nMon May 31 10:25:01 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* Makefile (GLANG): Add GCC language option.\n\nSat May 29 23:29:04 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* runall.bat (condvar5): Add new test.\n\n\t* runall.bat (condvar6): Add new test.\n\n\t* Makefile (condvar5) : Add new test.\n\t\n\t* Makefile (condvar6) : Add new test.\n\t\n\t* condvar5.c: New test for pthread_cond_broadcast().\n\n\t* condvar6.c: New test for pthread_cond_broadcast().\n\nSun Apr  4 12:04:28 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* tsd1.c (mythread): Change Sleep(0) to sched_yield().\n\t(sched.h): Include.\n\n\t* condvar3.c (mythread): Remove redundant Sleep().\n\n\t* runtest.bat: Re-organised to make more informative.\n\nFri Mar 19 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* *.bat: redirect unwanted output to nul:\n\n\t* runall.bat: new.\n\n\t* cancel1.c: new. Not part of suite yet.\n\t\nMon Mar 15 00:17:55 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* mutex1.c: only test mutex init and destroy; add assertions.\n\n\t* count1.c: raise number of spawned threads to 60 (appears to\n\tbe the limit under Win98).\n\nSun Mar 14 21:31:02 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* test.h (assert): add assertion trace option.\n\tUse:\n\t\"#define ASSERT_TRACE 1\" to turn it on,\n\t\"#define ASSERT_TRACE 0\" to turn it off (default).\n\n\t* condvar3.c (main): add more assertions.\n\n\t* condvar4.c (main): add more assertions.\n\n\t* condvar1.c (main): add more assertions.\n\nFri Mar 12 08:34:15 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* condvar4.c (cvthing): switch the order of the INITIALIZERs.\n\n\t* eyal1.c (main): Fix trylock loop; was not waiting for thread to lock\n\tthe \"started\" mutex.\n\nWed Mar 10 10:41:52 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* tryentercs.c: Apply typo patch from bje.\n\n\t* tryentercs2.c: Ditto.\n\nSun Mar  7 10:41:52 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* Makefile (condvar3, condvar4): Add tests.\n\n\t* condvar4.c (General): Reduce to simple test case; prerequisite\n\tis condvar3.c; add description.\n\n\t* condvar3.c (General): Reduce to simple test case; prerequisite\n\tis condvar2.c; add description.\n\n\t* condvar2.c (General): Reduce to simple test case; prerequisite\n\tis condvar1.c; add description.\n\n\t* condvar1.c (General): Reduce to simple test case; add\n\tdescription.\n\n\t* Template.c (Comments): Add generic test detail.\n\n1999-02-23  Ross Johnson  <rpj@ise.canberra.edu.au>\n\n        * Template.c: Revamp.\n\n        * condvar1.c: Add.\n\n        * condvar2.c: Add.\n\n        * Makefile: Add condvar1 condvar2 tests.\n\n        * exit1.c, exit2.c, exit3.c: Cosmetic changes.\n\n1999-02-23  Ross Johnson  <rpj@ise.canberra.edu.au>\n\n\t* Makefile: Some refinement.\n\n\t* *.c: More exhaustive checking through assertions; clean up;\n\tadd some more tests.\n\n\t* Makefile: Now actually runs the tests.\n\n\t* tests.h: Define our own assert macro. The Mingw32\n\tversion pops up a dialog but we want to run non-interactively.\n\n\t* equal1.c: use assert a little more directly so that it\n\tprints the actual call statement.\n\n\t* exit1.c: Modify to return 0 on success, 1 on failure.\n\n1999-02-22  Ross Johnson  <rpj@ise.canberra.edu.au>\n\n\t* self2.c: Bring up to date.\n\n\t* self3.c: Ditto.\n\n1999-02-21  Ben Elliston  <bje@cygnus.com>\n\n\t* README: Update.\n\n\t* Makefile: New file. Run all tests automatically. Primitive tests\n\tare run first; more complex tests are run last.\n\n\t* count1.c: New test. Validate the thread count.\n\n\t* exit2.c: Perform a simpler test.\n\t\n\t* exit3.c: New test. Replaces exit2.c, since exit2.c needs to\n\tperform simpler checking first.\n\n\t* create1.c: Update to use the new testsuite exiting convention.\n\t\n\t* equal1.c: Likewise.\n\n\t* mutex1.c: Likewise.\n\n\t* mutex2.c: Likewise.\n\n\t* once1.c: Likewise.\n\n\t* self2.c: Likewise.\n\n\t* self3.c: Likewise.\n\n\t* tsd1.c: Likewise.\n\n1999-02-20  Ross Johnson  <rpj@ise.canberra.edu.au>\n\n\t* mutex2.c: Test static mutex initialisation.\n\n\t* test.h: New. Declares a table mapping error numbers to\n\terror names.\n\n1999-01-17  Ross Johnson  <rpj@ise.canberra.edu.au>\n\n\t* runtest: New script to build and run a test in the tests directory.\n\nWed Dec 30 11:22:44 1998  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* tsd1.c: Re-written. See comments at start of file.\n\t* Template.c: New. Contains skeleton code and comment template\n\tintended to fully document the test.\n\nFri Oct 16 17:59:49 1998  Ross Johnson  <rpj@swan.canberra.edu.au>\n\n\t* tsd1.c (destroy_key): Add function. Change diagnostics.\n\nThu Oct 15 17:42:37 1998  Ross Johnson  <rpj@swan.canberra.edu.au>\n\n\t* tsd1.c (mythread): Fix some casts and add some message\n\toutput. Fix inverted conditional.\n\nMon Oct 12 02:12:29 1998  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>\n\n\t* tsd1.c: New. Test TSD using 1 key and 2 threads.\n\n1998-09-13  Ben Elliston  <bje@cygnus.com>\n\n\t* eyal1.c: New file; contributed by Eyal Lebedinsky\n\t<eyal@eyal.emu.id.au>.\n\n1998-09-12  Ben Elliston  <bje@cygnus.com>\n\n\t* exit2.c (func): Return a value.\n\t(main): Call the right thread entry function.\n\n1998-07-22  Ben Elliston  <bje@cygnus.com>\n\n\t* exit2.c (main): Fix size of pthread_t array.\n\n1998-07-10  Ben Elliston  <bje@cygnus.com>\n\n\t* exit2.c: New file; test pthread_exit() harder.\n\n\t* exit1.c: New file; test pthread_exit().\n"
  },
  {
    "path": "deps/w32-pthreads/tests/Debug.dsp",
    "content": "# Microsoft Developer Studio Project File - Name=\"Debug\" - Package Owner=<4>\n# Microsoft Developer Studio Generated Build File, Format Version 6.00\n# ** DO NOT EDIT **\n\n# TARGTYPE \"Win32 (x86) Console Application\" 0x0103\n\nCFG=Debug - Win32 Debug\n!MESSAGE This is not a valid makefile. To build this project using NMAKE,\n!MESSAGE use the Export Makefile command and run\n!MESSAGE \n!MESSAGE NMAKE /f \"Debug.mak\".\n!MESSAGE \n!MESSAGE You can specify a configuration when running NMAKE\n!MESSAGE by defining the macro CFG on the command line. For example:\n!MESSAGE \n!MESSAGE NMAKE /f \"Debug.mak\" CFG=\"Debug - Win32 Debug\"\n!MESSAGE \n!MESSAGE Possible choices for configuration are:\n!MESSAGE \n!MESSAGE \"Debug - Win32 Release\" (based on \"Win32 (x86) Console Application\")\n!MESSAGE \"Debug - Win32 Debug\" (based on \"Win32 (x86) Console Application\")\n!MESSAGE \n\n# Begin Project\n# PROP AllowPerConfigDependencies 0\n# PROP Scc_ProjName \"\"\n# PROP Scc_LocalPath \"\"\nCPP=cl.exe\nRSC=rc.exe\n\n!IF  \"$(CFG)\" == \"Debug - Win32 Release\"\n\n# PROP BASE Use_MFC 0\n# PROP BASE Use_Debug_Libraries 0\n# PROP BASE Output_Dir \"Release\"\n# PROP BASE Intermediate_Dir \"Release\"\n# PROP BASE Target_Dir \"\"\n# PROP Use_MFC 0\n# PROP Use_Debug_Libraries 0\n# PROP Output_Dir \"Release\"\n# PROP Intermediate_Dir \"Release\"\n# PROP Target_Dir \"\"\n# ADD BASE CPP /nologo /W3 /GX /O2 /D \"WIN32\" /D \"NDEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /YX /FD /c\n# ADD CPP /nologo /W3 /GX /O2 /D \"WIN32\" /D \"NDEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /YX /FD /c\n# ADD BASE RSC /l 0xc09 /d \"NDEBUG\"\n# ADD RSC /l 0xc09 /d \"NDEBUG\"\nBSC32=bscmake.exe\n# ADD BASE BSC32 /nologo\n# ADD BSC32 /nologo\nLINK32=link.exe\n# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\n# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\n\n!ELSEIF  \"$(CFG)\" == \"Debug - Win32 Debug\"\n\n# PROP BASE Use_MFC 0\n# PROP BASE Use_Debug_Libraries 1\n# PROP BASE Output_Dir \"Debug\"\n# PROP BASE Intermediate_Dir \"Debug\"\n# PROP BASE Target_Dir \"\"\n# PROP Use_MFC 0\n# PROP Use_Debug_Libraries 1\n# PROP Output_Dir \"Debug\"\n# PROP Intermediate_Dir \"Debug\"\n# PROP Ignore_Export_Lib 0\n# PROP Target_Dir \"\"\n# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /YX /FD /GZ /c\n# ADD CPP /nologo /MDd /W3 /WX /Gm /ZI /Od /I \"..\" /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"CLEANUP_C\" /FR /YX /FD /GZ /c\n# ADD BASE RSC /l 0xc09 /d \"_DEBUG\"\n# ADD RSC /l 0xc09 /d \"_DEBUG\"\nBSC32=bscmake.exe\n# ADD BASE BSC32 /nologo\n# ADD BSC32 /nologo\nLINK32=link.exe\n# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\n# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pthreadVC2d.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:\"..\"\n\n!ENDIF \n\n# Begin Target\n\n# Name \"Debug - Win32 Release\"\n# Name \"Debug - Win32 Debug\"\n# Begin Source File\n\nSOURCE=.\\Debug.txt\n# End Source File\n# Begin Source File\n\nSOURCE=.\\semaphore1.c\n# End Source File\n# End Target\n# End Project\n"
  },
  {
    "path": "deps/w32-pthreads/tests/Debug.dsw",
    "content": "Microsoft Developer Studio Workspace File, Format Version 6.00\n# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\n\n###############################################################################\n\nProject: \"Debug\"=.\\Debug.dsp - Package Owner=<4>\n\nPackage=<5>\n{{{\n}}}\n\nPackage=<4>\n{{{\n}}}\n\n###############################################################################\n\nGlobal:\n\nPackage=<5>\n{{{\n}}}\n\nPackage=<3>\n{{{\n}}}\n\n###############################################################################\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/Debug.plg",
    "content": "<html>\n<body>\n<pre>\n<h1>Build Log</h1>\n<h3>\n--------------------Configuration: Debug - Win32 Debug--------------------\n</h3>\n<h3>Command Lines</h3>\nCreating temporary file \"C:\\DOCUME~1\\ADMINI~1\\LOCALS~1\\Temp\\RSP9.tmp\" with contents\n[\n/nologo /MDd /W3 /WX /Gm /ZI /Od /I \"..\" /D \"WIN32\" /D \"_DEBUG\" /D \"_CONSOLE\" /D \"_MBCS\" /D \"CLEANUP_C\" /FR\"Debug/\" /Fp\"Debug/Debug.pch\" /YX /Fo\"Debug/\" /Fd\"Debug/\" /FD /GZ /c \n\"E:\\PTHREADS\\pthreads.2\\tests\\semaphore1.c\"\n]\nCreating command line \"cl.exe @C:\\DOCUME~1\\ADMINI~1\\LOCALS~1\\Temp\\RSP9.tmp\" \nCreating temporary file \"C:\\DOCUME~1\\ADMINI~1\\LOCALS~1\\Temp\\RSPA.tmp\" with contents\n[\nkernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pthreadVC2d.lib /nologo /subsystem:console /incremental:yes /pdb:\"Debug/Debug.pdb\" /debug /machine:I386 /out:\"Debug/Debug.exe\" /pdbtype:sept /libpath:\"..\" \n.\\Debug\\semaphore1.obj\n]\nCreating command line \"link.exe @C:\\DOCUME~1\\ADMINI~1\\LOCALS~1\\Temp\\RSPA.tmp\"\n<h3>Output Window</h3>\nCompiling...\nsemaphore1.c\nLinking...\n\n\n\n<h3>Results</h3>\nDebug.exe - 0 error(s), 0 warning(s)\n</pre>\n</body>\n</html>\n"
  },
  {
    "path": "deps/w32-pthreads/tests/Debug.txt",
    "content": "This project is used to debug individual test case programs.\n\nTo build and debug a test case:\n- add the .c file to this project;\n- remove any .c files from other test cases from this project.\n- build and debug as usual."
  },
  {
    "path": "deps/w32-pthreads/tests/GNUmakefile",
    "content": "# Makefile for the pthreads test suite.\n# If all of the .pass files can be created, the test suite has passed.\n#\n# --------------------------------------------------------------------------\n#\n#      Pthreads-win32 - POSIX Threads Library for Win32\n#      Copyright(C) 1998 John E. Bossom\n#      Copyright(C) 1999,2005 Pthreads-win32 contributors\n# \n#      Contact Email: rpj@callisto.canberra.edu.au\n# \n#      The current list of contributors is contained\n#      in the file CONTRIBUTORS included with the source\n#      code distribution. The list can also be seen at the\n#      following World Wide Web location:\n#      http://sources.redhat.com/pthreads-win32/contributors.html\n# \n#      This library is free software; you can redistribute it and/or\n#      modify it under the terms of the GNU Lesser General Public\n#      License as published by the Free Software Foundation; either\n#      version 2 of the License, or (at your option) any later version.\n# \n#      This library is distributed in the hope that it will be useful,\n#      but WITHOUT ANY WARRANTY; without even the implied warranty of\n#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n#      Lesser General Public License for more details.\n# \n#      You should have received a copy of the GNU Lesser General Public\n#      License along with this library in the file COPYING.LIB;\n#      if not, write to the Free Software Foundation, Inc.,\n#      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n#\n\nDLL_VER\t= 2\n\nCP\t= cp -f\nMV\t= mv -f\nRM\t= rm -f\nCAT\t= cat\nMKDIR\t= mkdir\nTOUCH\t= echo Passed >\nECHO\t= @echo\nMAKE\t= make -k\n\n# For cross compiling use e.g.\n# # make CROSS=i386-mingw32msvc- clean GC\nCROSS   =\n\n# For cross testing use e.g.\n# # make RUN=wine CROSS=i386-mingw32msvc- clean GC\nRUN     =\n\nAR      = $(CROSS)ar\nDLLTOOL = $(CROSS)dlltool\nCC      = $(CROSS)gcc\nCXX     = $(CROSS)g++\nRANLIB  = $(CROSS)ranlib\n\n#\n# Mingw32\n#\nXXCFLAGS\t= \nXXLIBS\t= -lws2_32 -lgomp\nOPT\t= -O3\nDOPT\t= -g -O0\n#CFLAGS\t= -O3 -UNDEBUG -Wall $(XXCFLAGS)\nCFLAGS\t= ${OPT} -UNDEBUG -Wall $(XXCFLAGS)\nBUILD_DIR\t= ..\nINCLUDES\t= -I.\n\n.INTERMEDIATE: %.exe %.pass\n.SECONDARY: %.exe %.pass\n.PRECIOUS: %.exe %.pass\n\nTEST\t= GC\n\n# Default lib version\nGCX\t= $(TEST)$(DLL_VER)\n\n# Files we need to run the tests\n# - paths are relative to pthreads build dir.\nHDR\t= pthread.h semaphore.h sched.h\nLIB\t= libpthread$(GCX).a\nDLL\t= pthread$(GCX).dll\n# The next path is relative to $BUILD_DIR\nQAPC\t=  # ../QueueUserAPCEx/User/quserex.dll\n\nCOPYFILES\t= $(HDR) $(LIB) $(DLL) $(QAPC)\n\n# If a test case returns a non-zero exit code to the shell, make will\n# stop.\n\nTESTS\t= \\\n\t  sizes loadfree \\\n\t  self1 mutex5 mutex1 mutex1e mutex1n mutex1r \\\n\t  semaphore1 semaphore2 semaphore3 \\\n\t  condvar1 condvar1_1 condvar1_2 condvar2 condvar2_1 exit1 \\\n\t  create1 create2 create3 reuse1 reuse2 equal1 \\\n\t  sequence1 kill1 valid1 valid2 \\\n\t  exit2 exit3 exit4 exit5 \\\n\t  join0 join1 detach1 join2 join3 \\\n\t  mutex2 mutex2r mutex2e mutex3 mutex3r mutex3e \\\n\t  mutex4 mutex6 mutex6n mutex6e mutex6r \\\n\t  mutex6s mutex6es mutex6rs \\\n\t  mutex7 mutex7n mutex7e mutex7r mutex8 mutex8n mutex8e mutex8r \\\n\t  robust1 robust2 robust3 robust4 robust5 \\\n\t  count1 \\\n\t  once1 once2 once3 once4 self2 \\\n\t  cancel1 cancel2 \\\n\t  semaphore4 semaphore4t semaphore5 \\\n\t  barrier1 barrier2 barrier3 barrier4 barrier5 barrier6 \\\n\t  tsd1 tsd2 openmp1 delay1 delay2 eyal1 \\\n\t  condvar3 condvar3_1 condvar3_2 condvar3_3 \\\n\t  condvar4 condvar5 condvar6 condvar7 condvar8 condvar9 \\\n\t  errno1 \\\n\t  rwlock1 rwlock2 rwlock3 rwlock4 rwlock5 rwlock6 rwlock7 rwlock8 \\\n\t  rwlock2_t rwlock3_t rwlock4_t rwlock5_t rwlock6_t rwlock6_t2 \\\n\t  context1 cancel3 cancel4 cancel5 cancel6a cancel6d \\\n\t  cancel7 cancel8 \\\n\t  cleanup0 cleanup1 cleanup2 cleanup3 \\\n\t  priority1 priority2 inherit1 \\\n\t  spin1 spin2 spin3 spin4 \\\n\t  exception1 exception2 exception3 \\\n\t  cancel9 stress1\n\nSTRESSTESTS = \\\n\tstress1\n\nBENCHTESTS = \\\n\tbenchtest1 benchtest2 benchtest3 benchtest4 benchtest5\n\nSTATICTESTS = \\\n\t  sizes \\\n\t  self1 mutex5 mutex1 mutex1e mutex1n mutex1r \\\n\t  semaphore1 semaphore2 semaphore3 \\\n\t  condvar1 condvar1_1 condvar1_2 condvar2 condvar2_1 exit1 \\\n\t  create1 create2 create3 reuse1 reuse2 equal1 \\\n\t  sequence1 kill1 valid1 valid2 \\\n\t  exit2 exit3 exit4 exit5 \\\n\t  join0 join1 detach1 join2 join3 \\\n\t  mutex2 mutex2r mutex2e mutex3 mutex3r mutex3e \\\n\t  mutex4 mutex6 mutex6n mutex6e mutex6r \\\n\t  mutex6s mutex6es mutex6rs \\\n\t  mutex7 mutex7n mutex7e mutex7r mutex8 mutex8n mutex8e mutex8r \\\n\t  robust1 robust2 robust3 robust4 robust5 \\\n\t  count1 \\\n\t  once1 once2 once3 once4 self2 \\\n\t  cancel1 cancel2 \\\n\t  semaphore4 semaphore4t semaphore5 \\\n\t  barrier1 barrier2 barrier3 barrier4 barrier5 barrier6 \\\n\t  tsd1 tsd2 delay1 delay2 eyal1 \\\n\t  condvar3 condvar3_1 condvar3_2 condvar3_3 \\\n\t  condvar4 condvar5 condvar6 condvar7 condvar8 condvar9 \\\n\t  errno1 \\\n\t  rwlock1 rwlock2 rwlock3 rwlock4 rwlock5 rwlock6 rwlock7 rwlock8 \\\n\t  rwlock2_t rwlock3_t rwlock4_t rwlock5_t rwlock6_t rwlock6_t2 \\\n\t  context1 cancel3 cancel4 cancel5 cancel6a cancel6d \\\n\t  cancel7 cancel8 \\\n\t  cleanup0 cleanup1 cleanup2 cleanup3 \\\n\t  priority1 priority2 inherit1 \\\n\t  spin1 spin2 spin3 spin4 \\\n\t  exception1 exception2 exception3 \\\n\t  cancel9 stress1\n\nALLTESTS = $(TESTS) $(BENCHTESTS)\n\nASM\t\t= $(ALLTESTS:%=%.s)\nPASSES\t\t= $(TESTS:%=%.pass)\nBENCHRESULTS\t= $(BENCHTESTS:%=%.bench)\nSTRESSRESULTS\t= $(STRESSTESTS:%=%.pass)\nSTATICRESULTS\t= $(STATICTESTS:%=%.pass)\n\nhelp:\n\t@ $(ECHO) \"Run one of the following command lines:\"\n\t@ $(ECHO) \"make clean GC    (to test using GC dll with C (no EH) applications)\"\n\t@ $(ECHO) \"make clean GCX   (to test using GC dll with C++ (EH) applications)\"\n\t@ $(ECHO) \"make clean GCE   (to test using GCE dll with C++ (EH) applications)\"\n\t@ $(ECHO) \"make clean GC-bench\t  (to benchtest using GNU C dll with C cleanup code)\"\n\t@ $(ECHO) \"make clean GCE-bench   (to benchtest using GNU C dll with C++ exception handling)\"\n\t@ $(ECHO) \"make clean GC-stress\t  (to stresstest using GNU C dll with C cleanup code)\"\n\t@ $(ECHO) \"make clean GCE-stress   (to stresstest using GNU C dll with C++ exception handling)\"\n\t@ $(ECHO) \"make clean GC-static   (to test using GC static lib with C (no EH) applications)\"\n\t@ $(ECHO) \"make clean GC-debug    (to test using GC dll with C (no EH) applications)\"\n\nall:\n\t@ $(MAKE) clean GC\n\t@ $(MAKE) clean GCX\n\t@ $(MAKE) clean GCE\n\nGC:\n\t$(MAKE) TEST=GC CC=$(CC) XXCFLAGS=\"-fopenmp -D__CLEANUP_C\" all-pass\n\nGC-asm:\n\t$(MAKE) TEST=GC CC=$(CC) XXCFLAGS=\"-fopenmp -D__CLEANUP_C\" all-asm\n\nGCE:\n\t$(MAKE) TEST=GCE CC=$(CXX) XXCFLAGS=\"-fopenmp -mthreads -D__CLEANUP_CXX\" all-pass\n\nGCX:\n\t$(MAKE) TEST=GC CC=$(CXX) XXCFLAGS=\"-fopenmp -mthreads -D__CLEANUP_C\" all-pass\n\nGC-bench:\n\t$(MAKE) TEST=GC CC=$(CC) XXCFLAGS=\"-D__CLEANUP_C\" XXLIBS=\"benchlib.o\" all-bench\n\nGCE-bench:\n\t$(MAKE) TEST=GCE  CC=$(CXX) XXCFLAGS=\"-mthreads -D__CLEANUP_CXX\" XXLIBS=\"benchlib.\" all-bench\n\nGC-debug:\n\t$(MAKE) TEST=GC CC=$(CC) XXCFLAGS=\"-fopenmp -D__CLEANUP_C\" OPT=\"${DOPT}\" DLL_VER=\"$(DLL_VER)d\" all-pass\n\nGCE-debug:\n\t$(MAKE) TEST=GCE CC=$(CXX) XXCFLAGS=\"-fopenmp -D__CLEANUP_CXX\" OPT=\"${DOPT}\" DLL_VER=\"$(DLL_VER)d\" all-pass\n\nGC-bench-debug:\n\t$(MAKE) TEST=GC CC=$(CC) XXCFLAGS=\"-D__CLEANUP_C\" XXLIBS=\"benchlib.o\" OPT=\"${OPT}\" DLL_VER=\"$(DLL_VER)d\" all-bench\n\nGC-static:\n\t$(MAKE) TEST=GC CC=$(CC) XXCFLAGS=\"-D__CLEANUP_C -DPTW32_STATIC_LIB\" XXLIBS=\"-lws2_32\" DLL=\"\" all-static\n\nGC-stress:\n\t$(ECHO) Stress tests can take a long time since they are trying to\n\t$(ECHO) expose weaknesses that may be intermittant or statistically rare.\n\t$(ECHO) A pass does not prove correctness, but may give greater confidence.\n\t$(MAKE) TEST=GC CC=$(CC) XXCFLAGS=\"-D__CLEANUP_C\" XXLIBS=\"\" all-stress\n\nGCE-stress:\n\t$(MAKE) TEST=GCE  CC=$(CXX) XXCFLAGS=\"-mthreads -D__CLEANUP_CXX\" XXLIBS=\"\" all-stress\n\nall-asm: $(ASM)\n\t@ $(ECHO) ALL TESTS PASSED! Congratulations!\n\nall-pass: $(PASSES)\n\t@ $(ECHO) ALL TESTS PASSED! Congratulations!\n\nall-bench: $(BENCHRESULTS)\n\t@ $(ECHO) BENCH TESTS COMPLETED.\n\nall-stress: $(STRESSRESULTS)\n\t@ $(ECHO) STRESS TESTS COMPLETED.\n\nall-static: $(STATICRESULTS)\n\t@ $(ECHO) ALL STATIC TESTS PASSED! Congratulations!\n\nbenchtest1.bench:\nbenchtest2.bench:\nbenchtest3.bench:\nbenchtest4.bench:\nbenchtest5.bench:\n\nbarrier1.pass: semaphore4.pass\nbarrier2.pass: barrier1.pass\nbarrier3.pass: barrier2.pass\nbarrier4.pass: barrier3.pass\nbarrier5.pass: barrier4.pass\nbarrier6.pass: barrier5.pass\ncancel1.pass: create1.pass\ncancel2.pass: cancel1.pass\ncancel3.pass: context1.pass\ncancel4.pass: cancel3.pass\ncancel5.pass: cancel3.pass\ncancel6a.pass: cancel3.pass\ncancel6d.pass: cancel3.pass\ncancel7.pass: kill1.pass\ncancel8.pass: cancel7.pass\ncancel9.pass: cancel8.pass\ncleanup0.pass: cancel5.pass\ncleanup1.pass: cleanup0.pass\ncleanup2.pass: cleanup1.pass\ncleanup3.pass: cleanup2.pass\ncondvar1.pass:\ncondvar1_1.pass: condvar1.pass\ncondvar1_2.pass: join2.pass\ncondvar2.pass: condvar1.pass\ncondvar2_1.pass: condvar2.pass join2.pass\ncondvar3.pass: create1.pass condvar2.pass\ncondvar3_1.pass: condvar3.pass join2.pass\ncondvar3_2.pass: condvar3_1.pass\ncondvar3_3.pass: condvar3_2.pass\ncondvar4.pass: create1.pass\ncondvar5.pass: condvar4.pass\ncondvar6.pass: condvar5.pass\ncondvar7.pass: condvar6.pass cleanup1.pass\ncondvar8.pass: condvar7.pass\ncondvar9.pass: condvar8.pass\ncontext1.pass: cancel1.pass\ncount1.pass: join1.pass\ncreate1.pass: mutex2.pass\ncreate2.pass: create1.pass\ncreate3.pass: create2.pass\ndelay1.pass:\ndelay2.pass: delay1.pass\ndetach1.pass: join0.pass\nequal1.pass: create1.pass\nerrno1.pass: mutex3.pass\nexception1.pass: cancel4.pass\nexception2.pass: exception1.pass\nexception3.pass: exception2.pass\nexit1.pass:\nexit2.pass: create1.pass\nexit3.pass: create1.pass\nexit4.pass:\nexit5.pass: exit4.pass kill1.pass\neyal1.pass: tsd1.pass\ninherit1.pass: join1.pass priority1.pass\njoin0.pass: create1.pass\njoin1.pass: create1.pass\njoin2.pass: create1.pass\njoin3.pass: join2.pass\nkill1.pass:\nloadfree.pass: pthread.dll\nmutex1.pass: self1.pass\nmutex1n.pass: mutex1.pass\nmutex1e.pass: mutex1.pass\nmutex1r.pass: mutex1.pass\nmutex2.pass: mutex1.pass\nmutex2r.pass: mutex2.pass\nmutex2e.pass: mutex2.pass\nmutex3.pass: create1.pass\nmutex3r.pass: mutex3.pass\nmutex3e.pass: mutex3.pass\nmutex4.pass: mutex3.pass\nmutex5.pass:\nmutex6.pass: mutex4.pass\nmutex6n.pass: mutex4.pass\nmutex6e.pass: mutex4.pass\nmutex6r.pass: mutex4.pass\nmutex6s.pass: mutex6.pass\nmutex6rs.pass: mutex6r.pass\nmutex6es.pass: mutex6e.pass\nmutex7.pass: mutex6.pass\nmutex7n.pass: mutex6n.pass\nmutex7e.pass: mutex6e.pass\nmutex7r.pass: mutex6r.pass\nmutex8.pass: mutex7.pass\nmutex8n.pass: mutex7n.pass\nmutex8e.pass: mutex7e.pass\nmutex8r.pass: mutex7r.pass\nonce1.pass: create1.pass\nonce2.pass: once1.pass\nonce3.pass: once2.pass\nonce4.pass: once3.pass\nopenmp1.pass: tsd2.pass\npriority1.pass: join1.pass\npriority2.pass: priority1.pass barrier3.pass\nreuse1.pass: create3.pass\nreuse2.pass: reuse1.pass\nrobust1.pass: mutex8r.pass\nrobust2.pass: mutex8r.pass\nrobust3.pass: robust2.pass\nrobust4.pass: robust3.pass\nrobust5.pass: robust4.pass\nrwlock1.pass: condvar6.pass\nrwlock2.pass: rwlock1.pass\nrwlock3.pass: rwlock2.pass join2.pass\nrwlock4.pass: rwlock3.pass\nrwlock5.pass: rwlock4.pass\nrwlock6.pass: rwlock5.pass\nrwlock7.pass: rwlock6.pass\nrwlock8.pass: rwlock7.pass\nrwlock2_t.pass: rwlock2.pass\nrwlock3_t.pass: rwlock2_t.pass\nrwlock4_t.pass: rwlock3_t.pass\nrwlock5_t.pass: rwlock4_t.pass\nrwlock6_t.pass: rwlock5_t.pass\nrwlock6_t2.pass: rwlock6_t.pass\nself1.pass:\nself2.pass: create1.pass\nsemaphore1.pass:\nsemaphore2.pass:\nsemaphore3.pass: semaphore2.pass\nsemaphore4.pass: semaphore3.pass cancel1.pass\nsemaphore4t.pass: semaphore4.pass\nsemaphore5.pass: semaphore4.pass\nsequence1.pass: reuse2.pass\nsizes.pass:\nspin1.pass:\nspin2.pass: spin1.pass\nspin3.pass: spin2.pass\nspin4.pass: spin3.pass\nstress1.pass:\ntsd1.pass: barrier5.pass join1.pass\ntsd2.pass: tsd1.pass\nvalid1.pass: join1.pass\nvalid2.pass: valid1.pass\n\nsizes.pass: sizes.exe\n\t@ $(ECHO) Running $*\n\t@ $(RUN) ./$< > SIZES.$(TEST)\n\t@ $(CAT) SIZES.$(TEST)\n\t@ $(ECHO) Passed\n\t@ $(TOUCH) $@\n\n%.pass: %.exe\n\t@ $(ECHO) Running $*\n\t@ $(RUN) ./$*\n\t@ $(ECHO) Passed\n\t@ $(TOUCH) $@\n\n%.bench: $(LIB) $(DLL) $(HDR) $(QAPC) $(XXLIBS) %.exe\n\t@ $(ECHO) Running $*\n\t@ $(RUN) ./$*\n\t@ $(ECHO) Done\n\t@ $(TOUCH) $@\n\n%.exe: %.c $(LIB) $(DLL) $(HDR) $(QAPC)\n\t@ $(ECHO) Compiling $@\n\t@ $(ECHO) $(CC) $(CFLAGS) -o $@ $< $(INCLUDES) -L. -lpthread$(GCX) -lsupc++ $(XXLIBS)\n\t@ $(CC) $(CFLAGS) -o $@ $< $(INCLUDES) -L. -lpthread$(GCX) -lsupc++ $(XXLIBS)\n\n%.pre: %.c $(HDR)\n\t@ $(CC) -E $(CFLAGS) -o $@ $< $(INCLUDES)\n\n%.s: %.c $(HDR)\n\t@ $(ECHO) Compiling $@\n\t@ $(CC) -S $(CFLAGS) -o $@ $< $(INCLUDES)\n\n$(COPYFILES):\n\t@ $(ECHO) Copying $(BUILD_DIR)/$@\n\t@ $(CP) $(BUILD_DIR)/$@ .\n\nbenchlib.o: benchlib.c\n\t@ $(ECHO) Compiling $@\n\t@ $(ECHO) $(CC) -c $(CFLAGS) $< $(INCLUDES)\n\t@ $(CC) -c $(CFLAGS) $< $(INCLUDES)\n\npthread.dll: $(DLL)\n\t@ $(CP) $(DLL) $@\n\nclean:\n\t- $(RM) *.dll\n\t- $(RM) *.lib\n\t- $(RM) pthread.h\n\t- $(RM) semaphore.h\n\t- $(RM) sched.h\n\t- $(RM) *.a\n\t- $(RM) *.e\n\t- $(RM) *.i\n\t- $(RM) *.o\n\t- $(RM) *.s\n\t- $(RM) *.so\n\t- $(RM) *.obj\n\t- $(RM) *.pdb\n\t- $(RM) *.exe\n\t- $(RM) *.pass\n\t- $(RM) *.bench\n\t- $(RM) *.static\n\t- $(RM) *.log\n"
  },
  {
    "path": "deps/w32-pthreads/tests/Makefile",
    "content": "# Makefile for the pthreads test suite.\n# If all of the .pass files can be created, the test suite has passed.\n#\n# --------------------------------------------------------------------------\n#\n#      Pthreads-win32 - POSIX Threads Library for Win32\n#      Copyright(C) 1998 John E. Bossom\n#      Copyright(C) 1999,2005 Pthreads-win32 contributors\n# \n#      Contact Email: rpj@callisto.canberra.edu.au\n# \n#      The current list of contributors is contained\n#      in the file CONTRIBUTORS included with the source\n#      code distribution. The list can also be seen at the\n#      following World Wide Web location:\n#      http://sources.redhat.com/pthreads-win32/contributors.html\n# \n#      This library is free software; you can redistribute it and/or\n#      modify it under the terms of the GNU Lesser General Public\n#      License as published by the Free Software Foundation; either\n#      version 2 of the License, or (at your option) any later version.\n# \n#      This library is distributed in the hope that it will be useful,\n#      but WITHOUT ANY WARRANTY; without even the implied warranty of\n#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n#      Lesser General Public License for more details.\n# \n#      You should have received a copy of the GNU Lesser General Public\n#      License along with this library in the file COPYING.LIB;\n#      if not, write to the Free Software Foundation, Inc.,\n#      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n#\n\nDLL_VER\t= 2\n\nCP\t= copy\nRM\t= erase\nCAT\t= type\nMKDIR\t= mkdir\nTOUCH\t= echo Passed >\nECHO\t= echo\n\n# The next path is relative to $BUILD_DIR\nQAPC\t= # ..\\QueueUserAPCEx\\User\\quserex.dll\n\nCPHDR\t= pthread.h semaphore.h sched.h\n\nOPTIM\t= /O2 /Ob0\n\nXXLIBS\t= ws2_32.lib\n\n# C++ Exceptions\nVCEFLAGS\t= /EHsc /TP /DPtW32NoCatchWarn /D__CLEANUP_CXX\nVCELIB\t= pthreadVCE$(DLL_VER).lib\nVCEDLL\t= pthreadVCE$(DLL_VER).dll\n# Structured Exceptions\nVSEFLAGS\t= /D__CLEANUP_SEH\nVSELIB\t= pthreadVSE$(DLL_VER).lib\nVSEDLL\t= pthreadVSE$(DLL_VER).dll\n# C cleanup code\nVCFLAGS\t= /D__CLEANUP_C\nVCLIB\t= pthreadVC$(DLL_VER).lib\nVCDLL\t= pthreadVC$(DLL_VER).dll\n# C++ Exceptions in application - using VC version of pthreads dll\nVCXFLAGS\t= /EHsc /TP /D__CLEANUP_C\n\n# Defaults\nCPLIB\t= $(VCLIB)\nCPDLL\t= $(VCDLL)\n\nCFLAGS= $(OPTIM) /W3 /MD /nologo /Z7\nLFLAGS= /INCREMENTAL:NO\nINCLUDES=-I.\nBUILD_DIR=..\n\nCOPYFILES\t= $(CPHDR) $(CPLIB) $(CPDLL) $(QAPC)\n\nTEST\t\t=\nEHFLAGS\t=\n\n# If a test case returns a non-zero exit code to the shell, make will\n# stop.\n\nPASSES = sizes.pass $(REGULAR_PASSES)\n\nREGULAR_PASSES = loadfree.pass \\\n\t  self1.pass  mutex5.pass  \\\n\t  mutex1.pass  mutex1n.pass  mutex1e.pass  mutex1r.pass  \\\n\t  semaphore1.pass  semaphore2.pass  semaphore3.pass  \\\n\t  mutex2.pass  mutex3.pass  \\\n\t  mutex2r.pass  mutex2e.pass  mutex3r.pass  mutex3e.pass  \\\n\t  condvar1.pass  condvar1_1.pass  condvar1_2.pass  condvar2.pass  condvar2_1.pass  \\\n\t  exit1.pass  create1.pass  create2.pass create3.pass reuse1.pass  reuse2.pass  equal1.pass  \\\n\t  sequence1.pass  kill1.pass  valid1.pass  valid2.pass  \\\n\t  exit2.pass  exit3.pass  exit4.pass  exit5.pass  \\\n\t  join0.pass  join1.pass  detach1.pass  join2.pass join3.pass  \\\n\t  mutex4.pass  mutex6.pass  mutex6n.pass  mutex6e.pass  mutex6r.pass  \\\n\t  mutex6s.pass  mutex6es.pass  mutex6rs.pass  \\\n\t  mutex7.pass  mutex7n.pass  mutex7e.pass  mutex7r.pass  \\\n\t  mutex8.pass  mutex8n.pass  mutex8e.pass  mutex8r.pass  \\\n\t  robust1.pass  robust2.pass  robust3.pass  robust4.pass  robust5.pass  \\\n\t  count1.pass  \\\n\t  once1.pass  once2.pass  once3.pass  once4.pass  \\\n\t  self2.pass  \\\n\t  cancel1.pass  cancel2.pass  \\\n\t  semaphore4.pass  semaphore4t.pass  semaphore5.pass  \\\n\t  barrier1.pass  barrier2.pass  barrier3.pass  barrier4.pass  barrier5.pass  barrier6.pass  \\\n\t  tsd1.pass  tsd2.pass  delay1.pass  delay2.pass  eyal1.pass  \\\n\t  condvar3.pass  condvar3_1.pass  condvar3_2.pass  condvar3_3.pass  \\\n\t  condvar4.pass  condvar5.pass  condvar6.pass  \\\n\t  condvar7.pass  condvar8.pass  condvar9.pass  \\\n\t  errno1.pass  \\\n\t  rwlock1.pass  rwlock2.pass  rwlock3.pass  rwlock4.pass  \\\n\t  rwlock5.pass  rwlock6.pass  rwlock7.pass  rwlock8.pass  \\\n\t  rwlock2_t.pass  rwlock3_t.pass  rwlock4_t.pass  rwlock5_t.pass  rwlock6_t.pass  rwlock6_t2.pass  \\\n\t  context1.pass  \\\n\t  cancel3.pass  cancel4.pass  cancel5.pass  cancel6a.pass  cancel6d.pass  \\\n\t  cancel7.pass  cancel8.pass  \\\n\t  cleanup0.pass  cleanup1.pass  cleanup2.pass  cleanup3.pass  \\\n\t  priority1.pass priority2.pass inherit1.pass  \\\n\t  spin1.pass  spin2.pass  spin3.pass  spin4.pass  \\\n\t  exception1.pass  exception2.pass  exception3.pass  \\\n\t  cancel9.pass  stress1.pass\n\nBENCHRESULTS = \\\n\t  benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench\n\nSTRESSRESULTS = \\\n\t  stress1.stress\n\nSTATICRESULTS = \\\n\t  sizes.pass  \\\n\t  self1.pass  mutex5.pass  \\\n\t  mutex1.pass  mutex1n.pass  mutex1e.pass  mutex1r.pass  \\\n\t  semaphore1.pass  semaphore2.pass  semaphore3.pass  \\\n\t  mutex2.pass  mutex3.pass  \\\n\t  mutex2r.pass  mutex2e.pass  mutex3r.pass  mutex3e.pass  \\\n\t  condvar1.pass  condvar1_1.pass  condvar1_2.pass  condvar2.pass  condvar2_1.pass  \\\n\t  exit1.pass  create1.pass  create2.pass  create3.pass  reuse1.pass  reuse2.pass  equal1.pass  \\\n\t  sequence1.pass  kill1.pass  valid1.pass  valid2.pass  \\\n\t  exit2.pass  exit3.pass  exit4.pass  exit5.pass  \\\n\t  join0.pass  join1.pass  detach1.pass  join2.pass join3.pass  \\\n\t  mutex4.pass  mutex6.pass  mutex6n.pass  mutex6e.pass  mutex6r.pass  \\\n\t  mutex6s.pass  mutex6es.pass  mutex6rs.pass  \\\n\t  mutex7.pass  mutex7n.pass  mutex7e.pass  mutex7r.pass  \\\n\t  mutex8.pass  mutex8n.pass  mutex8e.pass  mutex8r.pass  \\\n\t  robust1.pass  robust2.pass  robust3.pass  robust4.pass  robust5.pass  \\\n\t  count1.pass  \\\n\t  once1.pass  once2.pass  once3.pass  once4.pass  \\\n\t  self2.pass  \\\n\t  cancel1.pass  cancel2.pass  \\\n\t  semaphore4.pass  semaphore4t.pass  semaphore5.pass  \\\n\t  barrier1.pass  barrier2.pass  barrier3.pass  barrier4.pass  barrier5.pass  barrier6.pass  \\\n\t  tsd1.pass  tsd2.pass  delay1.pass  delay2.pass  eyal1.pass  \\\n\t  condvar3.pass  condvar3_1.pass  condvar3_2.pass  condvar3_3.pass  \\\n\t  condvar4.pass  condvar5.pass  condvar6.pass  \\\n\t  condvar7.pass  condvar8.pass  condvar9.pass  \\\n\t  errno1.pass  \\\n\t  rwlock1.pass  rwlock2.pass  rwlock3.pass  rwlock4.pass  \\\n\t  rwlock5.pass  rwlock6.pass  rwlock7.pass  rwlock8.pass  \\\n\t  rwlock2_t.pass  rwlock3_t.pass  rwlock4_t.pass  rwlock5_t.pass  rwlock6_t.pass  rwlock6_t2.pass  \\\n\t  context1.pass  \\\n\t  cancel3.pass  cancel4.pass  cancel5.pass  cancel6a.pass  cancel6d.pass  \\\n\t  cancel7.pass  cancel8.pass  \\\n\t  cleanup0.pass  cleanup1.pass  cleanup2.pass  cleanup3.pass  \\\n\t  priority1.pass priority2.pass inherit1.pass  \\\n\t  spin1.pass  spin2.pass  spin3.pass  spin4.pass  \\\n\t  exception1.pass  exception2.pass  exception3.pass  \\\n\t  cancel9.pass  stress1.pass\n\nhelp:\n\t@ $(ECHO) Run one of the following command lines:\n\t@ $(ECHO) nmake clean VC          (to test using VC dll with VC (no EH) apps)\n\t@ $(ECHO) nmake clean VC-bench    (to benchtest using VC dll with C bench apps)\n\t@ $(ECHO) nmake clean VC-stress   (to stresstest using VC dll with C stress apps)\n\t@ $(ECHO) nmake clean VC-static   (to test using VC static lib with VC (no EH) apps)\n\t@ $(ECHO) nmake clean VCX         (to test using VC dll with VC++ (EH) applications)\n\t@ $(ECHO) nmake clean VCX-bench   (to benchtest using VC dll with C++ bench apps)\n\t@ $(ECHO) nmake clean VCX-stress  (to stresstest using VC dll with C++ stress apps)\n\t@ $(ECHO) nmake clean VCE         (to test using the VCE dll with VC++ EH applications)\n\t@ $(ECHO) nmake clean VCE-bench   (to benchtest using VCE dll with C++ bench apps)\n\t@ $(ECHO) nmake clean VCE-stress  (to stresstest using VCE dll with C++ stress apps)\n\t@ $(ECHO) nmake clean VSE         (to test using VSE dll with VC (SEH) apps)\n\t@ $(ECHO) nmake clean VSE-bench   (to benchtest using VSE dll with SEH bench apps)\n\t@ $(ECHO) nmake clean VSE-stress  (to stresstest using VSE dll with SEH stress apps)\n\nall:\n\t@ $(MAKE) /E clean VC\n\t@ $(MAKE) /E clean VCX\n\t@ $(MAKE) /E clean VCE\n\t@ $(MAKE) /E clean VSE\n\t@ $(MAKE) /E clean VC-bench\n\t@ $(MAKE) /E clean VC-stress\n\n# This allows an individual test application to be made using the default lib.\n# e.g. nmake clean test cancel3.exe\ntest: $(CPLIB) $(CPDLL) $(CPHDR) $(QAPC)\n\ntests: $(CPLIB) $(CPDLL) $(CPHDR) $(QAPC) $(PASSES)\n\t@ $(ECHO) ALL TESTS PASSED! Congratulations!\n\nbenchtests: $(CPLIB) $(CPDLL) $(CPHDR) $(XXLIBS) $(BENCHRESULTS)\n\t@ $(ECHO) ALL BENCH TESTS DONE.\n\nstresstests: $(CPLIB) $(CPDLL) $(CPHDR) $(STRESSRESULTS)\n\t@ $(ECHO) ALL STRESS TESTS DONE.\n\nstatictests: $(CPLIB) $(CPDLL) $(CPHDR) $(STATICRESULTS)\n\t@ $(ECHO) ALL STATIC TESTS DONE.\n\nsizes.pass: sizes.exe\n\t@ $(ECHO) ... Running $(TEST)$(DLL_VER) test: $*.exe\n\t@ .\\$*.exe > SIZES.$(TEST)\n\t@ $(CAT) SIZES.$(TEST)\n\t@ $(ECHO) ...... Passed\n\t@ $(TOUCH) $*.pass\n\n$(REGULAR_PASSES): $*.exe\n\t@ $(ECHO) ... Running $(TEST) test: $*.exe\n\t@ .\\$*.exe\n\t@ $(ECHO) ...... Passed\n\t@ $(TOUCH) $*.pass\n\n$(BENCHRESULTS): $*.exe\n\t@ $(ECHO) ... Running $(TEST) benchtest: $*.exe\n\t@ .\\$*.exe\n\t@ $(ECHO) ...... Done\n\t@ $(TOUCH) $*.bench\n\n$(STRESSRESULTS): $*.exe\n\t@ $(ECHO) ... Running $(TEST) stresstest: $*.exe\n\t@ .\\$*.exe\n\t@ $(ECHO) ...... Done\n\t@ $(TOUCH) $*.pass\n\nVC:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VCLIB)\" CPDLL=\"$(VCDLL)\" EHFLAGS=\"$(VCFLAGS)\" tests\n\nVCE:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VCELIB)\" CPDLL=\"$(VCEDLL)\" EHFLAGS=\"$(VCEFLAGS)\" tests\n\nVSE:\t\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VSELIB)\" CPDLL=\"$(VSEDLL)\" EHFLAGS=\"$(VSEFLAGS)\" tests\n\nVCX:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VCLIB)\" CPDLL=\"$(VCDLL)\" EHFLAGS=\"$(VCXFLAGS)\" tests\n\nVC-bench:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VCLIB)\" CPDLL=\"$(VCDLL)\" EHFLAGS=\"$(VCFLAGS)\" XXLIBS=\"benchlib.o\" benchtests\n\nVCE-bench:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VCELIB)\" CPDLL=\"$(VCEDLL)\" EHFLAGS=\"$(VCEFLAGS)\" XXLIBS=\"benchlib.o\" benchtests\n\nVSE-bench:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VSELIB)\" CPDLL=\"$(VSEDLL)\" EHFLAGS=\"$(VSEFLAGS)\" XXLIBS=\"benchlib.o\" benchtests\n\nVCX-bench:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VCLIB)\" CPDLL=\"$(VCDLL)\" EHFLAGS=\"$(VCXFLAGS)\" XXLIBS=\"benchlib.o\" benchtests\n\nVC-stress:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VCLIB)\" CPDLL=\"$(VCDLL)\" EHFLAGS=\"$(VCFLAGS)\" stresstests\n\nVCE-stress:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VCELIB)\" CPDLL=\"$(VCEDLL)\" EHFLAGS=\"$(VCEFLAGS)\" stresstests\n\nVSE-stress:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VSELIB)\" CPDLL=\"$(VSEDLL)\" EHFLAGS=\"$(VSEFLAGS)\" stresstests\n\nVCX-stress:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VCLIB)\" CPDLL=\"$(VCDLL)\" EHFLAGS=\"$(VCXFLAGS)\" stresstests\n\nVC-static:\n\t@ $(MAKE) /E TEST=\"$@\" CPLIB=\"$(VCLIB)\" CPDLL=\"\" EHFLAGS=\"$(VCFLAGS) /DPTW32_STATIC_LIB\" statictests\n\n.c.exe:\n\t@ $(ECHO) $(CC) $(EHFLAGS) $(CFLAGS) $(INCLUDES) $< /Fe$@ /link $(LFLAGS) $(CPLIB) $(XXLIBS)\n\t@ $(CC) $(EHFLAGS) $(CFLAGS) $(INCLUDES) $< /Fe$@ /link $(LFLAGS) $(CPLIB) $(XXLIBS)\n\n.c.o:\n\t@ $(ECHO) $(CC) $(EHFLAGS) /c $(CFLAGS) $(INCLUDES) $< /Fo$@\n\t@ $(CC) $(EHFLAGS) $(CFLAGS) /c $(INCLUDES) $< /Fo$@\n\n.c.i:\n\t@ $(CC) /P $(EHFLAGS) $(CFLAGS) $(INCLUDES) $<\n\n$(COPYFILES):\n\t@ $(ECHO) Copying $(BUILD_DIR)\\$@\n\t@ $(CP) $(BUILD_DIR)\\$@ .\n\npthread.dll: $(CPDLL)\n\t@ $(CP) $(CPDLL) pthread.dll\n\t@ $(CP) $(CPLIB) pthread.lib\n\nclean:\n\t- $(RM) *.dll\n\t- $(RM) *.lib\n\t- $(RM) pthread.h\n\t- $(RM) semaphore.h\n\t- $(RM) sched.h\n\t- $(RM) *.e\n\t- $(RM) *.i\n\t- $(RM) *.obj\n\t- $(RM) *.pdb\n\t- $(RM) *.o\n\t- $(RM) *.asm\n\t- $(RM) *.exe\n\t- $(RM) *.pass\n\t- $(RM) *.bench\n\t- $(RM) *.log\n\nbenchtest1.bench:\nbenchtest2.bench:\nbenchtest3.bench:\nbenchtest4.bench:\nbenchtest5.bench:\n\nbarrier1.pass: semaphore4.pass\nbarrier2.pass: barrier1.pass\nbarrier3.pass: barrier2.pass\nbarrier4.pass: barrier3.pass\nbarrier5.pass: barrier4.pass\nbarrier6.pass: barrier5.pass\ncancel1.pass: create1.pass\ncancel2.pass: cancel1.pass\ncancel3.pass: context1.pass\ncancel4.pass: cancel3.pass\ncancel5.pass: cancel3.pass\ncancel6a.pass: cancel3.pass\ncancel6d.pass: cancel3.pass\ncancel7.pass: kill1.pass\ncancel8.pass: cancel7.pass\ncancel9.pass: cancel8.pass\ncleanup0.pass: cancel5.pass\ncleanup1.pass: cleanup0.pass\ncleanup2.pass: cleanup1.pass\ncleanup3.pass: cleanup2.pass\ncondvar1.pass:\ncondvar1_1.pass: condvar1.pass\ncondvar1_2.pass: join2.pass\ncondvar2.pass: condvar1.pass\ncondvar2_1.pass: condvar2.pass join2.pass\ncondvar3.pass: create1.pass condvar2.pass\ncondvar3_1.pass: condvar3.pass join2.pass\ncondvar3_2.pass: condvar3_1.pass\ncondvar3_3.pass: condvar3_2.pass\ncondvar4.pass: create1.pass\ncondvar5.pass: condvar4.pass\ncondvar6.pass: condvar5.pass\ncondvar7.pass: condvar6.pass cleanup1.pass\ncondvar8.pass: condvar7.pass\ncondvar9.pass: condvar8.pass\ncontext1.pass: cancel1.pass\ncount1.pass: join1.pass\ncreate1.pass: mutex2.pass\ncreate2.pass: create1.pass\ncreate3.pass: create2.pass\ndelay1.pass:\ndelay2.pass: delay1.pass\ndetach1.pass: join0.pass\nequal1.pass: create1.pass\nerrno1.pass: mutex3.pass\nexception1.pass: cancel4.pass\nexception2.pass: exception1.pass\nexception3.pass: exception2.pass\nexit1.pass:\nexit2.pass: create1.pass\nexit3.pass: create1.pass\nexit4.pass:\nexit5.pass: kill1.pass\neyal1.pass: tsd1.pass\ninherit1.pass: join1.pass priority1.pass\njoin0.pass: create1.pass\njoin1.pass: create1.pass\njoin2.pass: create1.pass\njoin3.pass: join2.pass\nkill1.pass: \nloadfree.pass: pthread.dll\nmutex1.pass: self1.pass\nmutex1n.pass: mutex1.pass\nmutex1e.pass: mutex1.pass\nmutex1r.pass: mutex1.pass\nmutex2.pass: mutex1.pass\nmutex2r.pass: mutex2.pass\nmutex2e.pass: mutex2.pass\nmutex3.pass: create1.pass\nmutex3r.pass: mutex3.pass\nmutex3e.pass: mutex3.pass\nmutex4.pass: mutex3.pass\nmutex5.pass:\nmutex6.pass: mutex4.pass\nmutex6n.pass: mutex4.pass\nmutex6e.pass: mutex4.pass\nmutex6r.pass: mutex4.pass\nmutex6s.pass: mutex6.pass\nmutex6rs.pass: mutex6r.pass\nmutex6es.pass: mutex6e.pass\nmutex7.pass: mutex6.pass\nmutex7n.pass: mutex6n.pass\nmutex7e.pass: mutex6e.pass\nmutex7r.pass: mutex6r.pass\nmutex8.pass: mutex7.pass\nmutex8n.pass: mutex7n.pass\nmutex8e.pass: mutex7e.pass\nmutex8r.pass: mutex7r.pass\nonce1.pass: create1.pass\nonce2.pass: once1.pass\nonce3.pass: once2.pass\nonce4.pass: once3.pass\npriority1.pass: join1.pass\npriority2.pass: priority1.pass barrier3.pass\nreuse1.pass: create3.pass\nreuse2.pass: reuse1.pass\nrobust1.pass: mutex8r.pass\nrobust2.pass: mutex8r.pass\nrobust3.pass: robust2.pass\nrobust4.pass: robust3.pass\nrobust5.pass: robust4.pass\nrwlock1.pass: condvar6.pass\nrwlock2.pass: rwlock1.pass\nrwlock3.pass: rwlock2.pass join2.pass\nrwlock4.pass: rwlock3.pass\nrwlock5.pass: rwlock4.pass\nrwlock6.pass: rwlock5.pass\nrwlock7.pass: rwlock6.pass\nrwlock8.pass: rwlock7.pass\nrwlock2_t.pass: rwlock2.pass\nrwlock3_t.pass: rwlock2_t.pass\nrwlock4_t.pass: rwlock3_t.pass\nrwlock5_t.pass: rwlock4_t.pass\nrwlock6_t.pass: rwlock5_t.pass\nrwlock6_t2.pass: rwlock6_t.pass\nself1.pass:\nself2.pass: create1.pass\nsemaphore1.pass:\nsemaphore2.pass:\nsemaphore3.pass: semaphore2.pass\nsemaphore4.pass: semaphore3.pass cancel1.pass\nsemaphore4t.pass: semaphore4.pass\nsemaphore5.pass: semaphore4.pass\nsequence1.pass: reuse2.pass\nsizes.pass:\nspin1.pass:\nspin2.pass: spin1.pass\nspin3.pass: spin2.pass\nspin4.pass: spin3.pass\nstress1.pass: condvar9.pass barrier5.pass\ntsd1.pass: barrier5.pass join1.pass\ntsd2.pass: tsd1.pass\nvalid1.pass: join1.pass\nvalid2.pass: valid1.pass\n"
  },
  {
    "path": "deps/w32-pthreads/tests/README",
    "content": "Running test cases in this directory\n------------------------------------\n\nThese make scripts expect to be able to copy the dll, library\nand header files from this directory's parent directory,\nwhich should be the pthreads-win32 source directory.\n\nMS VC nmake\n-------------\n\nRun the target corresponding to the DLL version being tested:\n \nnmake clean VC\n \nor:\n \nnmake clean VS\n\n\nGNU GCC make\n------------\n\nRun \"make clean\" and then \"make\". See the \"Known bugs\" section\nin ..\\README.\n\n\nWriting Test Cases\n------------------\n\nTests written in this test suite should behave in the following manner:\n\n\t* If a test fails, leave main() with a result of 1.\n\n\t* If a test succeeds, leave main() with a result of 0.\n\n\t* No diagnostic output should appear when the test is succeeding.\n\t  Diagnostic output may be emitted if something in the test\n\t  fails, to help determine the cause of the test failure.\n\nNotes:\n------\n\nMany test cases use knowledge of implementation internals which are supposed\nto be opaque to portable applications.\n"
  },
  {
    "path": "deps/w32-pthreads/tests/README.BENCHTESTS",
    "content": "\n------------\nBenchmarking\n------------\nThere is a set a benchmarking programs in the\n\"tests\" directory. These should be runnable using the\nfollowing command-lines corresponding to each of the possible\nlibrary builds:\n\nMSVC:\nnmake clean VC-bench\nnmake clean VCE-bench\nnmake clean VSE-bench\n\nMingw32:\nmake clean GC-bench\nmake clean GCE-bench\n\nUWIN:\nThe benchtests are run as part of the testsuite.\n\n\nMutex benchtests\n----------------\n\nbenchtest1 - Lock plus unlock on an unlocked mutex.\nbenchtest2 - Lock plus unlock on a locked mutex.\nbenchtest3 - Trylock on a locked mutex.\nbenchtest4 - Trylock plus unlock on an unlocked mutex.\n\n\nEach test times up to three alternate synchronisation\nimplementations as a reference, and then times each of\nthe four mutex types provided by the library. Each is\ndescribed below:\n\nSimple Critical Section\n- uses a simple Win32 critical section. There is no\nadditional overhead for this case as there is in the\nremaining cases.\n\nPOSIX mutex implemented using a Critical Section\n- The old implementation which uses runtime adaptation\ndepending on the Windows variant being run on. When\nthe pthreads DLL was run on WinNT or higher then\nPOSIX mutexes would use Win32 Critical Sections.\n\nPOSIX mutex implemented using a Win32 Mutex\n- The old implementation which uses runtime adaptation\ndepending on the Windows variant being run on. When\nthe pthreads DLL was run on Win9x then POSIX mutexes\nwould use Win32 Mutexes (because TryEnterCriticalSection\nis not implemented on Win9x).\n\nPTHREAD_MUTEX_DEFAULT\nPTHREAD_MUTEX_NORMAL\nPTHREAD_MUTEX_ERRORCHECK\nPTHREAD_MUTEX_RECURSIVE\n- The current implementation supports these mutex types.\nThe underlying basis of POSIX mutexes is now the same\nirrespective of the Windows variant, and should therefore\nhave consistent performance.\n\n\nSemaphore benchtests\n--------------------\n\nbenchtest5 - Timing for various uncontended cases.\n\n\nIn all benchtests, the operation is repeated a large\nnumber of times and an average is calculated. Loop\noverhead is measured and subtracted from all test times.\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/SIZES.GC",
    "content": "Sizes of pthreads-win32 structs\n-------------------------------\n                     pthread_t    8\n                ptw32_thread_t  160\n               pthread_attr_t_   28\n                        sem_t_   12\n              pthread_mutex_t_   28\n          pthread_mutexattr_t_   12\n           pthread_spinlock_t_    8\n            pthread_barrier_t_   36\n        pthread_barrierattr_t_    4\n                pthread_key_t_   16\n               pthread_cond_t_   32\n           pthread_condattr_t_    4\n             pthread_rwlock_t_   28\n         pthread_rwlockattr_t_    4\n               pthread_once_t_   16\n               ptw32_cleanup_t   12\n             ptw32_mcs_node_t_   16\n                   sched_param    4\n-------------------------------\n"
  },
  {
    "path": "deps/w32-pthreads/tests/SIZES.GCE",
    "content": "Sizes of pthreads-win32 structs\n-------------------------------\n                     pthread_t    8\n                ptw32_thread_t   96\n               pthread_attr_t_   28\n                        sem_t_   12\n              pthread_mutex_t_   28\n          pthread_mutexattr_t_   12\n           pthread_spinlock_t_    8\n            pthread_barrier_t_   36\n        pthread_barrierattr_t_    4\n                pthread_key_t_   16\n               pthread_cond_t_   32\n           pthread_condattr_t_    4\n             pthread_rwlock_t_   28\n         pthread_rwlockattr_t_    4\n               pthread_once_t_   16\n               ptw32_cleanup_t   12\n             ptw32_mcs_node_t_   16\n                   sched_param    4\n-------------------------------\n"
  },
  {
    "path": "deps/w32-pthreads/tests/SIZES.VC",
    "content": "Sizes of pthreads-win32 structs\n-------------------------------\n                     pthread_t   16\n                ptw32_thread_t  432\n               pthread_attr_t_   40\n                        sem_t_   24\n              pthread_mutex_t_   48\n          pthread_mutexattr_t_   12\n           pthread_spinlock_t_   16\n            pthread_barrier_t_   64\n        pthread_barrierattr_t_    4\n                pthread_key_t_   32\n               pthread_cond_t_   56\n           pthread_condattr_t_    4\n             pthread_rwlock_t_   40\n         pthread_rwlockattr_t_    4\n               pthread_once_t_   24\n               ptw32_cleanup_t   24\n             ptw32_mcs_node_t_   32\n                   sched_param    4\n-------------------------------\n"
  },
  {
    "path": "deps/w32-pthreads/tests/SIZES.VCE",
    "content": "Sizes of pthreads-win32 structs\n-------------------------------\n                     pthread_t    8\n                ptw32_thread_t   96\n               pthread_attr_t_   28\n                        sem_t_   12\n              pthread_mutex_t_   28\n          pthread_mutexattr_t_   12\n           pthread_spinlock_t_    8\n            pthread_barrier_t_   36\n        pthread_barrierattr_t_    4\n                pthread_key_t_   16\n               pthread_cond_t_   32\n           pthread_condattr_t_    4\n             pthread_rwlock_t_   28\n         pthread_rwlockattr_t_    4\n               pthread_once_t_   16\n               ptw32_cleanup_t   12\n             ptw32_mcs_node_t_   16\n                   sched_param    4\n-------------------------------\n"
  },
  {
    "path": "deps/w32-pthreads/tests/SIZES.VSE",
    "content": "Sizes of pthreads-win32 structs\n-------------------------------\n                     pthread_t    8\n                ptw32_thread_t   96\n               pthread_attr_t_   28\n                        sem_t_   12\n              pthread_mutex_t_   28\n          pthread_mutexattr_t_   12\n           pthread_spinlock_t_    8\n            pthread_barrier_t_   36\n        pthread_barrierattr_t_    4\n                pthread_key_t_   16\n               pthread_cond_t_   32\n           pthread_condattr_t_    4\n             pthread_rwlock_t_   28\n         pthread_rwlockattr_t_    4\n               pthread_once_t_   16\n               ptw32_cleanup_t   12\n             ptw32_mcs_node_t_   16\n                   sched_param    4\n-------------------------------\n"
  },
  {
    "path": "deps/w32-pthreads/tests/Wmakefile",
    "content": "# Watcom makefile for the pthreads test suite.\n# If all of the .pass files can be created, the test suite has passed.\n#\n# --------------------------------------------------------------------------\n#\n#      Pthreads-win32 - POSIX Threads Library for Win32\n#      Copyright(C) 1998 John E. Bossom\n#      Copyright(C) 1999,2005 Pthreads-win32 contributors\n# \n#      Contact Email: rpj@callisto.canberra.edu.au\n# \n#      The current list of contributors is contained\n#      in the file CONTRIBUTORS included with the source\n#      code distribution. The list can also be seen at the\n#     following World Wide Web location:\n#      http://sources.redhat.com/pthreads-win32/contributors.html\n# \n#      This library is free software; you can redistribute it and/or\n#      modify it under the terms of the GNU Lesser General Public\n#      License as published by the Free Software Foundation; either\n#      version 2 of the License, or (at your option) any later version.\n# \n#      This library is distributed in the hope that it will be useful,\n#      but WITHOUT ANY WARRANTY; without even the implied warranty of\n#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n#      Lesser General Public License for more details.\n# \n#      You should have received a copy of the GNU Lesser General Public\n#      License along with this library in the file COPYING.LIB;\n#      if not, write to the Free Software Foundation, Inc.,\n#      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n#\n\n\nDLL_VER\t= 2\n\n.EXTENSIONS:\n\n.EXTENSIONS: .pass .exe .obj .i .c\n\nCP\t= copy\nRM\t= erase\nCAT\t= type\nMKDIR\t= mkdir\nTOUCH\t= echo Passed >\nECHO\t= @echo\n\nCPHDR\t= pthread.h semaphore.h sched.h\n\nOPTIM\t= -od\n\nXXLIBS\t=\n\n# C++ Exceptions\nWCEFLAGS\t= -xs -dPtW32NoCatchWarn -d__CLEANUP_CXX\nWCELIB\t= pthreadWCE$(DLL_VER).lib\nWCEDLL\t= pthreadWCE$(DLL_VER).dll\n# C cleanup code\nWCFLAGS\t= -d__CLEANUP_C\nWCLIB\t= pthreadWC$(DLL_VER).lib\nWCDLL\t= pthreadWC$(DLL_VER).dll\n# C++ Exceptions in application - using WC version of pthreads dll\nWCXFLAGS\t= -xs -d__CLEANUP_C\n\nCFLAGS= -w4 -e25 -d_WIN32_WINNT=0x400 -d_REENTRANT -zq -bm $(OPTIM) -5r -bt=nt -mf -d2\n\nLFLAGS= \nINCLUDES= -i=.\nBUILD_DIR=..\n\n# The next path is relative to $BUILD_DIR\nQAPC\t= # ..\\QueueUserAPCEx\\User\\quserex.dll\n\nCOPYFILES\t= $(CPHDR) $(CPLIB) $(CPDLL) $(QAPC)\n\nTEST\t\t=\nEHFLAGS\t=\n\n# If a test case returns a non-zero exit code to the shell, make will\n# stop.\n\nPASSES\t= sizes.pass  loadfree.pass &\n\t  self1.pass  mutex5.pass  &\n\t  mutex1.pass  mutex1n.pass  mutex1e.pass  mutex1r.pass &\n\t  semaphore1.pass  semaphore2.pass semaphore3.pass &\n\t  mutex2.pass  mutex3.pass  &\n\t  mutex2r.pass  mutex2e.pass  mutex3r.pass  mutex3e.pass  &\n\t  condvar1.pass  condvar1_1.pass  condvar1_2.pass  condvar2.pass  condvar2_1.pass  &\n\t  exit1.pass  create1.pass  create2.pass  reuse1.pass  reuse2.pass  equal1.pass  &\n\t  sequence1.pass  kill1.pass  valid1.pass  valid2.pass  &\n\t  exit2.pass  exit3.pass  exit4  exit5  &\n\t  join0.pass  join1.pass  detach1.pass  join2.pass join3.pass  &\n\t  mutex4.pass  mutex6.pass  mutex6n.pass  mutex6e.pass  mutex6r.pass  &\n\t  mutex6s.pass  mutex6es.pass  mutex6rs.pass  &\n\t  mutex7.pass  mutex7n.pass  mutex7e.pass  mutex7r.pass  &\n\t  mutex8.pass  mutex8n.pass  mutex8e.pass  mutex8r.pass  &\n\t  robust1.pass  robust2.pass  robust3.pass  robust4.pass  robust5.pass  &\n\t  count1.pass  &\n\t  once1.pass  once2.pass  once3.pass  once4.pass  tsd1.pass  &\n\t  self2.pass  &\n\t  cancel1.pass  cancel2.pass  &\n\t  semaphore4.pass semaphore4t.pass semaphore5.pass &\n\t  delay1.pass  delay2.pass  eyal1.pass  &\n\t  condvar3.pass  condvar3_1.pass  condvar3_2.pass  condvar3_3.pass  &\n\t  condvar4.pass  condvar5.pass  condvar6.pass  &\n\t  condvar7.pass  condvar8.pass  condvar9.pass  &\n\t  errno1.pass  &\n\t  rwlock1.pass  rwlock2.pass  rwlock3.pass  rwlock4.pass  rwlock5.pass  &\n\t  rwlock6.pass  rwlock7.pass  rwlock8.pass  &\n\t  rwlock2_t.pass  rwlock3_t.pass  rwlock4_t.pass  rwlock5_t.pass  rwlock6_t.pass  rwlock6_t2.pass  &\n\t  context1.pass  &\n\t  cancel3.pass  cancel4.pass  cancel5.pass  cancel6a.pass  cancel6d.pass  &\n\t  cancel7  cancel8  &\n\t  cleanup0.pass  cleanup1.pass  cleanup2.pass  cleanup3.pass  &\n\t  priority1.pass priority2.pass inherit1.pass  &\n\t  spin1.pass  spin2.pass  spin3.pass  spin4.pass  &\n\t  barrier1.pass  barrier2.pass  barrier3.pass  barrier4.pass  barrier5.pass  &\n\t  exception1.pass  exception2.pass  exception3.pass  &\n\t  cancel9.pass  stress1.pass\n\nBENCHRESULTS = &\n\t  benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench\n\nhelp: .SYMBOLIC\n\t@ $(ECHO) Run one of the following command lines:\n\t@ $(ECHO) wmake /f Wmakefile clean WC    (to test using WC dll with wcc386 (no EH) applications)\n\t@ $(ECHO) wmake /f Wmakefile clean WCX   (to test using WC dll with wpp386 (EH) applications)\n\t@ $(ECHO) wmake /f Wmakefile clean WCE   (to test using the WCE dll with wpp386 EH applications)\n\t@ $(ECHO) wmake /f Wmakefile clean WC-bench    (to benchtest using WC dll with C bench app)\n\t@ $(ECHO) wmake /f Wmakefile clean WCX-bench   (to benchtest using WC dll with C++ bench app)\n\t@ $(ECHO) wmake /f Wmakefile clean WCE-bench   (to benchtest using WCE dll with C++ bench app)\n\nall: .SYMBOLIC\n\t@ wmake /f Wmakefile clean WC\n\t@ wmake /f Wmakefile clean WCX\n\t@ wmake /f Wmakefile clean WCE\n\t@ wmake /f Wmakefile clean WSE\n\t@ wmake /f Wmakefile clean WC-bench\n\ntests: $(CPLIB) $(CPDLL) $(CPHDR) $(PASSES) .SYMBOLIC\n\t@ $(ECHO) ALL TESTS PASSED! Congratulations!\n\nbenchtests: $(CPLIB) $(CPDLL) $(CPHDR) $(XXLIBS) $(BENCHRESULTS) .SYMBOLIC\n\t@ $(ECHO) ALL BENCH TESTS DONE.\n\n$(BENCHRESULTS): ($[*).exe\n\t@ $(ECHO) ... Running $(TEST) benchtest: ($[*).exe\n\t@ .\\($[*).exe\n\t@ $(ECHO) ...... Done\n\t@ $(TOUCH) ($[*).bench\n\nWCE: .SYMBOLIC\n\t@ wmake /f Wmakefile CC=wpp386 TEST=\"$@\" CPLIB=\"$(WCELIB)\" CPDLL=\"$(WCEDLL)\" EHFLAGS=\"$(WCEFLAGS)\" tests\n\nWC: .SYMBOLIC\n\t@ wmake /f Wmakefile CC=wcc386 TEST=\"$@\" CPLIB=\"$(WCLIB)\" CPDLL=\"$(WCDLL)\" EHFLAGS=\"$(WCFLAGS)\" tests\n\nWCX: .SYMBOLIC\n\t@ wmake /f Wmakefile CC=wpp386 TEST=\"$@\" CPLIB=\"$(WCLIB)\" CPDLL=\"$(WCDLL)\" EHFLAGS=\"$(WCXFLAGS)\" tests\n\nWCE-bench: .SYMBOLIC\n\t@ wmake /f Wmakefile CC=wpp386 TEST=\"$@\" CPLIB=\"$(WCELIB)\" CPDLL=\"$(WCEDLL)\" EHFLAGS=\"$(WCEFLAGS)\" XXLIBS=\"benchlib.o\" benchtests\n\nWC-bench: .SYMBOLIC\n\t@ wmake /f Wmakefile CC=wcc386 TEST=\"$@\" CPLIB=\"$(WCLIB)\" CPDLL=\"$(WCDLL)\" EHFLAGS=\"$(WCFLAGS)\" XXLIBS=\"benchlib.o\" benchtests\n\nWCX-bench: .SYMBOLIC\n\t@ wmake /f Wmakefile CC=wpp386 TEST=\"$@\" CPLIB=\"$(WCLIB)\" CPDLL=\"$(WCDLL)\" EHFLAGS=\"$(WCXFLAGS)\" XXLIBS=\"benchlib.o\" benchtests\n\nsizes.pass: sizes.exe\n\t@ $(ECHO) ... Running $(TEST) test: $^*\n\t@ $[@ > SIZES.$(TEST)\n\t@ $(CAT) SIZES.$(TEST)\n\t@ $(ECHO) ...... Passed\n\t@ $(TOUCH) $^@\n\n.exe.pass:\n\t@ $(ECHO) ... Running $(TEST) test: $^*\n\t@ $[@\n\t@ $(ECHO) ...... Passed\n\t@ $(TOUCH) $^@\n\n.obj.exe:\n\t@ $(ECHO) wlink NAME $^@ FILE $[@ LIBRARY $(CPLIB) OPTION quiet\n\t@ wlink NAME $^@ FILE $[@ LIBRARY $(CPLIB) OPTION quiet\n\n.c.obj:\n\t@ $(ECHO) $(CC) $^* $(EHFLAGS) $(CFLAGS) $(INCLUDES)\n\t@ $(CC) $^* $(EHFLAGS) $(CFLAGS) $(INCLUDES)\n\n.c.i:\n\t@ $(CC) /P $(EHFLAGS) $(CFLAGS) $(INCLUDES) $<\n\n$(COPYFILES): .SYMBOLIC\n\t@ $(ECHO) Copying $(BUILD_DIR)\\$@\n\t@ $(CP) $(BUILD_DIR)\\$@ .\n\npthread.dll:\n\t@ $(CP) $(CPDLL) $*.dll\n\t@ $(CP) $(CPLIB) $*.lib\n\nclean: .SYMBOLIC\n\t@ if exist *.dll $(RM) *.dll\n\t@ if exist *.lib $(RM) *.lib\n\t@ if exist *.err $(RM) *.err\n\t@ if exist pthread.h $(RM) pthread.h\n\t@ if exist semaphore.h $(RM) semaphore.h\n\t@ if exist sched.h $(RM) sched.h\n\t@ if exist *.e $(RM) *.e\n\t@ if exist *.i $(RM) *.i\n\t@ if exist *.obj $(RM) *.obj\n\t@ if exist *.pdb $(RM) *.pdb\n\t@ if exist *.o $(RM) *.o\n\t@ if exist *.asm $(RM) *.asm\n\t@ if exist *.exe $(RM) *.exe\n\t@ if exist *.pass $(RM) *.pass\n\t@ if exist *.bench $(RM) *.bench\n\t@ if exist *.log $(RM) *.log\n\t@ $(ECHO) Clean completed.\n\nbenchtest1.bench:\nbenchtest2.bench:\nbenchtest3.bench:\nbenchtest4.bench:\nbenchtest5.bench:\nbarrier1.pass:\nbarrier2.pass: barrier1.pass\nbarrier3.pass: barrier2.pass\nbarrier4.pass: barrier3.pass\nbarrier5.pass: barrier4.pass\ncancel1.pass: create1.pass\ncancel2.pass: cancel1.pass\ncancel3.pass: context1.pass\ncancel4.pass: cancel3.pass\ncancel5.pass: cancel3.pass\ncancel6a.pass: cancel3.pass\ncancel6d.pass: cancel3.pass\ncancel7.pass: kill1.pass\ncancel8.pass: cancel7.pass\ncleanup0.pass: cancel5.pass\ncleanup1.pass: cleanup0.pass\ncleanup2.pass: cleanup1.pass\ncleanup3.pass: cleanup2.pass\ncondvar1.pass:\ncondvar1_1.pass: condvar1.pass\ncondvar1_2.pass: join2.pass\ncondvar2.pass: condvar1.pass\ncondvar2_1.pass: condvar2.pass join2.pass\ncondvar3.pass: create1.pass condvar2.pass\ncondvar3_1.pass: condvar3.pass join2.pass\ncondvar3_2.pass: condvar3_1.pass\ncondvar3_3.pass: condvar3_2.pass\ncondvar4.pass: create1.pass\ncondvar5.pass: condvar4.pass\ncondvar6.pass: condvar5.pass\ncondvar7.pass: condvar6.pass cleanup1.pass\ncondvar8.pass: condvar7.pass\ncondvar9.pass: condvar8.pass\ncontext1.pass: cancel1.pass\ncount1.pass: join1.pass\ncreate1.pass: mutex2.pass\ncreate2.pass: create1.pass\ndelay1.pass:\ndelay2.pass: delay1.pass\ndetach1.pass: join0.pass\nequal1.pass: create1.pass\nerrno1.pass: mutex3.pass\nexception1.pass: cancel4.pass\nexception2.pass: exception1.pass\nexception3.pass: exception2.pass\nexit1.pass:\nexit2.pass: create1.pass\nexit3.pass: create1.pass\nexit4.pass:\nexit5.pass: kill1.pass\neyal1.pass: tsd1.pass\ninherit1.pass: join1.pass priority1.pass\njoin0.pass: create1.pass\njoin1.pass: create1.pass\njoin2.pass: create1.pass\njoin3.pass: join2.pass\nkill1.pass: \nloadfree.pass: pthread.dll\nmutex1.pass: self1.pass\nmutex1n.pass: mutex1.pass\nmutex1e.pass: mutex1.pass\nmutex1r.pass: mutex1.pass\nmutex2.pass: mutex1.pass\nmutex2r.pass: mutex2.pass\nmutex2e.pass: mutex2.pass\nmutex3.pass: create1.pass\nmutex3r.pass: mutex3.pass\nmutex3e.pass: mutex3.pass\nmutex4.pass: mutex3.pass\nmutex5.pass:\nmutex6.pass: mutex4.pass\nmutex6n.pass: mutex4.pass\nmutex6e.pass: mutex4.pass\nmutex6r.pass: mutex4.pass\nmutex6s.pass: mutex6.pass\nmutex6rs.pass: mutex6r.pass\nmutex6es.pass: mutex6e.pass\nmutex7.pass: mutex6.pass\nmutex7n.pass: mutex6n.pass\nmutex7e.pass: mutex6e.pass\nmutex7r.pass: mutex6r.pass\nmutex8.pass: mutex7.pass\nmutex8n.pass: mutex7n.pass\nmutex8e.pass: mutex7e.pass\nmutex8r.pass: mutex7r.pass\nonce1.pass: create1.pass\nonce2.pass: once1.pass\nonce3.pass: once2.pass\nonce4.pass: once3.pass\npriority1.pass: join1.pass\npriority2.pass: priority1.pass barrier3.pass\nreuse1.pass: create2.pass\nreuse2.pass: reuse1.pass\nrobust1.pass: mutex8r.pass\nrobust2.pass: mutex8r.pass\nrobust3.pass: robust2.pass\nrobust4.pass: robust3.pass\nrobust5.pass: robust4.pass\nrwlock1.pass: condvar6.pass\nrwlock2.pass: rwlock1.pass\nrwlock3.pass: rwlock2.pass join2.pass\nrwlock4.pass: rwlock3.pass\nrwlock5.pass: rwlock4.pass\nrwlock6.pass: rwlock5.pass\nrwlock7.pass: rwlock6.pass\nrwlock2_t.pass: rwlock2.pass\nrwlock3_t.pass: rwlock2_t.pass\nrwlock4_t.pass: rwlock3_t.pass\nrwlock5_t.pass: rwlock4_t.pass\nrwlock6_t.pass: rwlock5_t.pass\nrwlock6_t2.pass: rwlock6_t.pass\nself1.pass:\nself2.pass: create1.pass\nsemaphore1.pass:\nsemaphore2.pass:\nsemaphore3.pass: semaphore2.pass\nsemaphore4.pass: semaphore3.pass cancel1.pass\nsemaphore4t.pass: semaphore4.pass\nsemaphore5.pass: semaphore4.pass\nsequence1.pass: reuse2.pass\nsizes.pass:\nspin1.pass:\nspin2.pass: spin1.pass\nspin3.pass: spin2.pass\nspin4.pass: spin3.pass\nstress1.pass:\ntsd1.pass: join1.pass\nvalid1.pass: join1.pass\nvalid2.pass: valid1.pass\ncancel9.pass: cancel8.pass\n"
  },
  {
    "path": "deps/w32-pthreads/tests/barrier1.c",
    "content": "/* \n * barrier1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Create a barrier object and then destroy it.\n *\n */\n\n#include \"test.h\"\n\npthread_barrier_t barrier = NULL;\n\nint\nmain()\n{\n  assert(barrier == NULL);\n\n  assert(pthread_barrier_init(&barrier, NULL, 1) == 0);\n\n  assert(barrier != NULL);\n\n  assert(pthread_barrier_destroy(&barrier) == 0);\n\n  assert(barrier == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/barrier2.c",
    "content": "/* \n * barrier2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a single barrier object, wait on it, \n * and then destroy it.\n *\n */\n\n#include \"test.h\"\n \npthread_barrier_t barrier = NULL;\n\nint\nmain()\n{\n  assert(pthread_barrier_init(&barrier, NULL, 1) == 0);\n\n  assert(pthread_barrier_wait(&barrier) == PTHREAD_BARRIER_SERIAL_THREAD);\n\n  assert(pthread_barrier_destroy(&barrier) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/barrier3.c",
    "content": "/*\n * barrier3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a single barrier object with barrier attribute, wait on it, \n * and then destroy it.\n *\n */\n\n#include \"test.h\"\n \npthread_barrier_t barrier = NULL;\nstatic void* result = (void*)1;\n\nvoid * func(void * arg)\n{\n  return (void *) (size_t)pthread_barrier_wait(&barrier);\n}\n \nint\nmain()\n{\n  pthread_t t;\n  pthread_barrierattr_t ba;\n\n  assert(pthread_barrierattr_init(&ba) == 0);\n  assert(pthread_barrierattr_setpshared(&ba, PTHREAD_PROCESS_PRIVATE) == 0);\n  assert(pthread_barrier_init(&barrier, &ba, 1) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n\n  assert(pthread_join(t, &result) == 0);\n\n  assert((int)(size_t)result == PTHREAD_BARRIER_SERIAL_THREAD);\n\n  assert(pthread_barrier_destroy(&barrier) == 0);\n  assert(pthread_barrierattr_destroy(&ba) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/barrier4.c",
    "content": "/*\n * barrier4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a single barrier object, multiple wait on it, \n * and then destroy it.\n *\n */\n\n#include \"test.h\"\n\nenum {\n  NUMTHREADS = 16\n};\n \npthread_barrier_t barrier = NULL;\npthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER;\nstatic int serialThreadCount = 0;\nstatic int otherThreadCount = 0;\n\nvoid *\nfunc(void * arg)\n{\n  int result = pthread_barrier_wait(&barrier);\n\n  assert(pthread_mutex_lock(&mx) == 0);\n\n  if (result == PTHREAD_BARRIER_SERIAL_THREAD)\n    {\n      serialThreadCount++;\n    }\n  else if (0 == result)\n    {\n      otherThreadCount++;\n    }\n  else\n    {\n      printf(\"Barrier wait failed: error = %s\\n\", error_string[result]);\n      fflush(stdout);\n      return NULL;\n    }\n  assert(pthread_mutex_unlock(&mx) == 0);\n\n  return NULL;\n}\n\nint\nmain()\n{\n  int i, j;\n  pthread_t t[NUMTHREADS + 1];\n\n  for (j = 1; j <= NUMTHREADS; j++)\n    {\n      printf(\"Barrier height = %d\\n\", j);\n\n      serialThreadCount = 0;\n\n      assert(pthread_barrier_init(&barrier, NULL, j) == 0);\n\n      for (i = 1; i <= j; i++)\n        {\n          assert(pthread_create(&t[i], NULL, func, NULL) == 0);\n        }\n\n      for (i = 1; i <= j; i++)\n        {\n          assert(pthread_join(t[i], NULL) == 0);\n        }\n\n      assert(serialThreadCount == 1);\n\n      assert(pthread_barrier_destroy(&barrier) == 0);\n    }\n\n  assert(pthread_mutex_destroy(&mx) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/barrier5.c",
    "content": "/*\n * barrier5.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Set up a series of barriers at different heights and test various numbers\n * of threads accessing, especially cases where there are more threads than the\n * barrier height (count), i.e. test contention when the barrier is released.\n */\n\n#include \"test.h\"\n\nenum {\n  NUMTHREADS = 15,\n  HEIGHT = 10,\n  BARRIERMULTIPLE = 1000\n};\n \npthread_barrier_t barrier = NULL;\npthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER;\nLONG totalThreadCrossings;\n\nvoid *\nfunc(void * crossings)\n{\n  int result;\n  int serialThreads = 0;\n\n  while ((LONG)(size_t)crossings >= (LONG)InterlockedIncrement((LPLONG)&totalThreadCrossings))\n    {\n      result = pthread_barrier_wait(&barrier);\n\n      if (result == PTHREAD_BARRIER_SERIAL_THREAD)\n        {\n          serialThreads++;\n        }\n      else if (result != 0)\n        {\n          printf(\"Barrier failed: result = %s\\n\", error_string[result]);\n          fflush(stdout);\n          return NULL;\n        }\n    }\n\n  return (void*)(size_t)serialThreads;\n}\n\nint\nmain()\n{\n  int i, j;\n  void* result;\n  int serialThreadsTotal;\n  LONG Crossings;\n  pthread_t t[NUMTHREADS + 1];\n\n  for (j = 1; j <= NUMTHREADS; j++)\n    {\n      int height = j<HEIGHT?j:HEIGHT;\n\n      totalThreadCrossings = 0;\n      Crossings = height * BARRIERMULTIPLE;\n\n      printf(\"Threads=%d, Barrier height=%d\\n\", j, height);\n\n      assert(pthread_barrier_init(&barrier, NULL, height) == 0);\n\n      for (i = 1; i <= j; i++)\n        {\n          assert(pthread_create(&t[i], NULL, func, (void *)(size_t)Crossings) == 0);\n        }\n\n      serialThreadsTotal = 0;\n      for (i = 1; i <= j; i++)\n        {\n          assert(pthread_join(t[i], &result) == 0);\n          serialThreadsTotal += (int)(size_t)result;\n        }\n\n      assert(serialThreadsTotal == BARRIERMULTIPLE);\n\n      assert(pthread_barrier_destroy(&barrier) == 0);\n    }\n\n  assert(pthread_mutex_destroy(&mx) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/barrier6.c",
    "content": "/*\n * barrier6.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Destroy the barrier after initial count threads are released then let\n * additional threads attempt to wait on it.\n *\n */\n\n#include \"test.h\"\n\nenum {\n  NUMTHREADS = 31\n};\n \npthread_barrier_t barrier = NULL;\npthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER;\nstatic int serialThreadCount = 0;\nstatic int otherThreadCount = 0;\n\nvoid *\nfunc(void * arg)\n{\n  int result = pthread_barrier_wait(&barrier);\n\n  assert(pthread_mutex_lock(&mx) == 0);\n\n  if (result == PTHREAD_BARRIER_SERIAL_THREAD)\n    {\n      serialThreadCount++;\n    }\n  else if (0 == result)\n    {\n      otherThreadCount++;\n    }\n  assert(pthread_mutex_unlock(&mx) == 0);\n\n  return NULL;\n}\n\nint\nmain()\n{\n  int i, j, k;\n  pthread_t t[NUMTHREADS + 1];\n\n  for (j = 1; j <= NUMTHREADS; j++)\n    {\n      int howHigh = j/2 + 1;\n\n      printf(\"Barrier height = %d, Total threads %d\\n\", howHigh, j);\n\n      serialThreadCount = 0;\n      otherThreadCount = 0;\n\n      assert(pthread_barrier_init(&barrier, NULL, howHigh) == 0);\n\n      for (i = 1; i <= j; i++)\n        {\n          assert(pthread_create(&t[i], NULL, func, NULL) == 0);\n\n          if (i == howHigh)\n            {\n              for (k = 1; k <= howHigh; k++)\n                {\n                  assert(pthread_join(t[k], NULL) == 0);\n                }\n              assert(pthread_barrier_destroy(&barrier) == 0);\n            }\n        }\n\n      for (i = howHigh+1; i <= j; i++)\n        {\n          assert(pthread_join(t[i], NULL) == 0);\n        }\n\n      assert(serialThreadCount == 1);\n      assert(otherThreadCount == (howHigh - 1));\n\n      assert(pthread_barrier_destroy(&barrier) == EINVAL);\n    }\n\n  assert(pthread_mutex_destroy(&mx) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/benchlib.c",
    "content": "/*\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n */\n\n#include \"../config.h\"\n\n#include \"pthread.h\"\n#include \"sched.h\"\n#include \"semaphore.h\"\n#include <windows.h>\n#include <stdio.h>\n\n#ifdef __GNUC__\n#include <stdlib.h>\n#endif\n\n#include \"benchtest.h\"\n\nint old_mutex_use = OLD_WIN32CS;\n\nBOOL (WINAPI *ptw32_try_enter_critical_section)(LPCRITICAL_SECTION) = NULL;\nHINSTANCE ptw32_h_kernel32;\n\nvoid\ndummy_call(int * a)\n{\n}\n\nvoid\ninterlocked_inc_with_conditionals(int * a)\n{\n  if (a != NULL)\n    if (InterlockedIncrement((long *) a) == -1)\n      {\n        *a = 0;\n      }\n}\n\nvoid\ninterlocked_dec_with_conditionals(int * a)\n{\n  if (a != NULL)\n    if (InterlockedDecrement((long *) a) == -1)\n      {\n        *a = 0;\n      }\n}\n\nint\nold_mutex_init(old_mutex_t *mutex, const old_mutexattr_t *attr)\n{\n  int result = 0;\n  old_mutex_t mx;\n\n  if (mutex == NULL)\n    {\n      return EINVAL;\n    }\n\n  mx = (old_mutex_t) calloc(1, sizeof(*mx));\n\n  if (mx == NULL)\n    {\n      result = ENOMEM;\n      goto FAIL0;\n    }\n\n  mx->mutex = 0;\n\n  if (attr != NULL\n      && *attr != NULL\n      && (*attr)->pshared == PTHREAD_PROCESS_SHARED\n      )\n    {\n      result = ENOSYS;\n    }\n  else\n    {\n        CRITICAL_SECTION cs;\n\n        /*\n         * Load KERNEL32 and try to get address of TryEnterCriticalSection\n         */\n        ptw32_h_kernel32 = LoadLibrary(TEXT(\"KERNEL32.DLL\"));\n        ptw32_try_enter_critical_section = (BOOL (WINAPI *)(LPCRITICAL_SECTION))\n\n#if defined(NEED_UNICODE_CONSTS)\n        GetProcAddress(ptw32_h_kernel32,\n                       (const TCHAR *)TEXT(\"TryEnterCriticalSection\"));\n#else\n        GetProcAddress(ptw32_h_kernel32,\n                       (LPCSTR) \"TryEnterCriticalSection\");\n#endif\n\n        if (ptw32_try_enter_critical_section != NULL)\n          {\n            InitializeCriticalSection(&cs);\n            if ((*ptw32_try_enter_critical_section)(&cs))\n              {\n                LeaveCriticalSection(&cs);\n              }\n            else\n              {\n                /*\n                 * Not really supported (Win98?).\n                 */\n                ptw32_try_enter_critical_section = NULL;\n              }\n            DeleteCriticalSection(&cs);\n          }\n\n        if (ptw32_try_enter_critical_section == NULL)\n          {\n            (void) FreeLibrary(ptw32_h_kernel32);\n            ptw32_h_kernel32 = 0;\n          }\n\n      if (old_mutex_use == OLD_WIN32CS)\n\t{\n\t  InitializeCriticalSection(&mx->cs);\n\t}\n      else if (old_mutex_use == OLD_WIN32MUTEX)\n      {\n\t  mx->mutex = CreateMutex (NULL,\n\t\t\t\t   FALSE,\n\t\t\t\t   NULL);\n\n\t  if (mx->mutex == 0)\n\t    {\n\t      result = EAGAIN;\n\t    }\n\t}\n      else\n\t{\n        result = EINVAL;\n      }\n    }\n\n  if (result != 0 && mx != NULL)\n    {\n      free(mx);\n      mx = NULL;\n    }\n\nFAIL0:\n  *mutex = mx;\n\n  return(result);\n}\n\n\nint\nold_mutex_lock(old_mutex_t *mutex)\n{\n  int result = 0;\n  old_mutex_t mx;\n\n  if (mutex == NULL || *mutex == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (*mutex == (old_mutex_t) PTW32_OBJECT_AUTO_INIT)\n    {\n      /*\n       * Don't use initialisers when benchtesting.\n       */\n      result = EINVAL;\n    }\n\n  mx = *mutex;\n\n  if (result == 0)\n    {\n      if (mx->mutex == 0)\n\t{\n\t  EnterCriticalSection(&mx->cs);\n\t}\n      else\n\t{\n\t  result = (WaitForSingleObject(mx->mutex, INFINITE) \n\t\t    == WAIT_OBJECT_0)\n\t    ? 0\n\t    : EINVAL;\n\t}\n    }\n\n  return(result);\n}\n\nint\nold_mutex_unlock(old_mutex_t *mutex)\n{\n  int result = 0;\n  old_mutex_t mx;\n\n  if (mutex == NULL || *mutex == NULL)\n    {\n      return EINVAL;\n    }\n\n  mx = *mutex;\n\n  if (mx != (old_mutex_t) PTW32_OBJECT_AUTO_INIT)\n    {\n      if (mx->mutex == 0)\n\t{\n\t  LeaveCriticalSection(&mx->cs);\n\t}\n      else\n\t{\n\t  result = (ReleaseMutex (mx->mutex) ? 0 : EINVAL);\n\t}\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  return(result);\n}\n\n\nint\nold_mutex_trylock(old_mutex_t *mutex)\n{\n  int result = 0;\n  old_mutex_t mx;\n\n  if (mutex == NULL || *mutex == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (*mutex == (old_mutex_t) PTW32_OBJECT_AUTO_INIT)\n    {\n      /*\n       * Don't use initialisers when benchtesting.\n       */\n      result = EINVAL;\n    }\n\n  mx = *mutex;\n\n  if (result == 0)\n    {\n      if (mx->mutex == 0)\n\t{\n\t  if (ptw32_try_enter_critical_section == NULL)\n          {\n            result = 0;\n          }\n        else if ((*ptw32_try_enter_critical_section)(&mx->cs) != TRUE)\n\t    {\n\t      result = EBUSY;\n\t    }\n\t}\n      else\n\t{\n\t  DWORD status;\n\n\t  status = WaitForSingleObject (mx->mutex, 0);\n\n\t  if (status != WAIT_OBJECT_0)\n\t    {\n\t      result = ((status == WAIT_TIMEOUT)\n\t\t\t? EBUSY\n\t\t\t: EINVAL);\n\t    }\n\t}\n    }\n\n  return(result);\n}\n\n\nint\nold_mutex_destroy(old_mutex_t *mutex)\n{\n  int result = 0;\n  old_mutex_t mx;\n\n  if (mutex == NULL\n      || *mutex == NULL)\n    {\n      return EINVAL;\n    }\n\n  if (*mutex != (old_mutex_t) PTW32_OBJECT_AUTO_INIT)\n    {\n      mx = *mutex;\n\n      if ((result = old_mutex_trylock(&mx)) == 0)\n        {\n          *mutex = NULL;\n\n          (void) old_mutex_unlock(&mx);\n\n          if (mx->mutex == 0)\n            {\n              DeleteCriticalSection(&mx->cs);\n            }\n          else\n            {\n              result = (CloseHandle (mx->mutex) ? 0 : EINVAL);\n            }\n\n          if (result == 0)\n            {\n              mx->mutex = 0;\n              free(mx);\n            }\n          else\n            {\n              *mutex = mx;\n            }\n        }\n    }\n  else\n    {\n      result = EINVAL;\n    }\n\n  if (ptw32_try_enter_critical_section != NULL)\n    {\n      (void) FreeLibrary(ptw32_h_kernel32);\n      ptw32_h_kernel32 = 0;\n    }\n\n  return(result);\n}\n\n/****************************************************************************************/\n"
  },
  {
    "path": "deps/w32-pthreads/tests/benchtest.h",
    "content": "/*\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n */\n\n#include \"../config.h\"\n\nenum {\n  OLD_WIN32CS,\n  OLD_WIN32MUTEX\n};\n\nextern int old_mutex_use;\n\nstruct old_mutex_t_ {\n  HANDLE mutex;\n  CRITICAL_SECTION cs;\n};\n\ntypedef struct old_mutex_t_ * old_mutex_t;\n\nstruct old_mutexattr_t_ {\n  int pshared;\n};\n\ntypedef struct old_mutexattr_t_ * old_mutexattr_t;\n\nextern BOOL (WINAPI *ptw32_try_enter_critical_section)(LPCRITICAL_SECTION);\nextern HINSTANCE ptw32_h_kernel32;\n\n#define PTW32_OBJECT_AUTO_INIT ((void *) -1)\n\nvoid dummy_call(int * a);\nvoid interlocked_inc_with_conditionals(int *a);\nvoid interlocked_dec_with_conditionals(int *a);\nint old_mutex_init(old_mutex_t *mutex, const old_mutexattr_t *attr);\nint old_mutex_lock(old_mutex_t *mutex);\nint old_mutex_unlock(old_mutex_t *mutex);\nint old_mutex_trylock(old_mutex_t *mutex);\nint old_mutex_destroy(old_mutex_t *mutex);\n/****************************************************************************************/\n"
  },
  {
    "path": "deps/w32-pthreads/tests/benchtest1.c",
    "content": "/*\n * benchtest1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Measure time taken to complete an elementary operation.\n *\n * - Mutex\n *   Single thread iteration over lock/unlock for each mutex type.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n#ifdef __GNUC__\n#include <stdlib.h>\n#endif\n\n#include \"benchtest.h\"\n\n#define PTW32_MUTEX_TYPES\n#define ITERATIONS      10000000L\n\npthread_mutex_t mx;\npthread_mutexattr_t ma;\nPTW32_STRUCT_TIMEB currSysTimeStart;\nPTW32_STRUCT_TIMEB currSysTimeStop;\nlong durationMilliSecs;\nlong overHeadMilliSecs = 0;\nint two = 2;\nint one = 1;\nint zero = 0;\nint iter;\n\n#define GetDurationMilliSecs(_TStart, _TStop) ((long)((_TStop.time*1000+_TStop.millitm) \\\n                                               - (_TStart.time*1000+_TStart.millitm)))\n\n/*\n * Dummy use of j, otherwise the loop may be removed by the optimiser\n * when doing the overhead timing with an empty loop.\n */\n#define TESTSTART \\\n  { int i, j = 0, k = 0; PTW32_FTIME(&currSysTimeStart); for (i = 0; i < ITERATIONS; i++) { j++;\n\n#define TESTSTOP \\\n  }; PTW32_FTIME(&currSysTimeStop); if (j + k == i) j++; }\n\n\nvoid\nrunTest (char * testNameString, int mType)\n{\n#ifdef PTW32_MUTEX_TYPES\n  assert(pthread_mutexattr_settype(&ma, mType) == 0);\n#endif\n  assert(pthread_mutex_init(&mx, &ma) == 0);\n\n  TESTSTART\n  assert((pthread_mutex_lock(&mx),1) == one);\n  assert((pthread_mutex_unlock(&mx),2) == two);\n  TESTSTOP\n\n  assert(pthread_mutex_destroy(&mx) == 0);\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    testNameString,\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n}\n\n\nint\nmain (int argc, char *argv[])\n{\n  int i = 0;\n  CRITICAL_SECTION cs;\n  old_mutex_t ox;\n  pthread_mutexattr_init(&ma);\n\n  printf( \"=============================================================================\\n\");\n  printf( \"\\nLock plus unlock on an unlocked mutex.\\n%ld iterations\\n\\n\",\n          ITERATIONS);\n  printf( \"%-45s %15s %15s\\n\",\n\t    \"Test\",\n\t    \"Total(msec)\",\n\t    \"average(usec)\");\n  printf( \"-----------------------------------------------------------------------------\\n\");\n\n  /*\n   * Time the loop overhead so we can subtract it from the actual test times.\n   */\n  TESTSTART\n  assert(1 == one);\n  assert(2 == two);\n  TESTSTOP\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  overHeadMilliSecs = durationMilliSecs;\n\n\n  TESTSTART\n  assert((dummy_call(&i), 1) == one);\n  assert((dummy_call(&i), 2) == two);\n  TESTSTOP\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Dummy call x 2\",\n          durationMilliSecs,\n          (float) (durationMilliSecs * 1E3 / ITERATIONS));\n\n\n  TESTSTART\n  assert((interlocked_inc_with_conditionals(&i), 1) == one);\n  assert((interlocked_dec_with_conditionals(&i), 2) == two);\n  TESTSTOP\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Dummy call -> Interlocked with cond x 2\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n\n\n  TESTSTART\n  assert((InterlockedIncrement((LPLONG)&i), 1) == (LONG)one);\n  assert((InterlockedDecrement((LPLONG)&i), 2) == (LONG)two);\n  TESTSTOP\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"InterlockedOp x 2\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n\n\n  InitializeCriticalSection(&cs);\n\n  TESTSTART\n  assert((EnterCriticalSection(&cs), 1) == one);\n  assert((LeaveCriticalSection(&cs), 2) == two);\n  TESTSTOP\n\n  DeleteCriticalSection(&cs);\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Simple Critical Section\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n\n\n  old_mutex_use = OLD_WIN32CS;\n  assert(old_mutex_init(&ox, NULL) == 0);\n\n  TESTSTART\n  assert(old_mutex_lock(&ox) == zero);\n  assert(old_mutex_unlock(&ox) == zero);\n  TESTSTOP\n\n  assert(old_mutex_destroy(&ox) == 0);\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Old PT Mutex using a Critical Section (WNT)\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n\n\n  old_mutex_use = OLD_WIN32MUTEX;\n  assert(old_mutex_init(&ox, NULL) == 0);\n\n  TESTSTART\n  assert(old_mutex_lock(&ox) == zero);\n  assert(old_mutex_unlock(&ox) == zero);\n  TESTSTOP\n\n  assert(old_mutex_destroy(&ox) == 0);\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Old PT Mutex using a Win32 Mutex (W9x)\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n\n  printf( \".............................................................................\\n\");\n\n  /*\n   * Now we can start the actual tests\n   */\n#ifdef PTW32_MUTEX_TYPES\n  runTest(\"PTHREAD_MUTEX_DEFAULT\", PTHREAD_MUTEX_DEFAULT);\n\n  runTest(\"PTHREAD_MUTEX_NORMAL\", PTHREAD_MUTEX_NORMAL);\n\n  runTest(\"PTHREAD_MUTEX_ERRORCHECK\", PTHREAD_MUTEX_ERRORCHECK);\n\n  runTest(\"PTHREAD_MUTEX_RECURSIVE\", PTHREAD_MUTEX_RECURSIVE);\n#else\n  runTest(\"Non-blocking lock\", 0);\n#endif\n\n  printf( \".............................................................................\\n\");\n\n  pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);\n\n#ifdef PTW32_MUTEX_TYPES\n  runTest(\"PTHREAD_MUTEX_DEFAULT (Robust)\", PTHREAD_MUTEX_DEFAULT);\n\n  runTest(\"PTHREAD_MUTEX_NORMAL (Robust)\", PTHREAD_MUTEX_NORMAL);\n\n  runTest(\"PTHREAD_MUTEX_ERRORCHECK (Robust)\", PTHREAD_MUTEX_ERRORCHECK);\n\n  runTest(\"PTHREAD_MUTEX_RECURSIVE (Robust)\", PTHREAD_MUTEX_RECURSIVE);\n#else\n  runTest(\"Non-blocking lock\", 0);\n#endif\n\n  printf( \"=============================================================================\\n\");\n\n  /*\n   * End of tests.\n   */\n\n  pthread_mutexattr_destroy(&ma);\n\n  one = i; /* Dummy assignment to avoid 'variable unused' warning */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/benchtest2.c",
    "content": "/*\n * benchtest1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Measure time taken to complete an elementary operation.\n *\n * - Mutex\n *   Two threads iterate over lock/unlock for each mutex type.\n *   The two threads are forced into lock-step using two mutexes,\n *   forcing the threads to block on each lock operation. The\n *   time measured is therefore the worst case senario.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n#ifdef __GNUC__\n#include <stdlib.h>\n#endif\n\n#include \"benchtest.h\"\n\n#define PTW32_MUTEX_TYPES\n#define ITERATIONS      100000L\n\npthread_mutex_t gate1, gate2;\nold_mutex_t ox1, ox2;\nCRITICAL_SECTION cs1, cs2;\npthread_mutexattr_t ma;\nlong durationMilliSecs;\nlong overHeadMilliSecs = 0;\nPTW32_STRUCT_TIMEB currSysTimeStart;\nPTW32_STRUCT_TIMEB currSysTimeStop;\npthread_t worker;\nint running = 0;\n\n#define GetDurationMilliSecs(_TStart, _TStop) ((long)((_TStop.time*1000+_TStop.millitm) \\\n                                               - (_TStart.time*1000+_TStart.millitm)))\n\n/*\n * Dummy use of j, otherwise the loop may be removed by the optimiser\n * when doing the overhead timing with an empty loop.\n */\n#define TESTSTART \\\n  { int i, j = 0, k = 0; PTW32_FTIME(&currSysTimeStart); for (i = 0; i < ITERATIONS; i++) { j++;\n\n#define TESTSTOP \\\n  }; PTW32_FTIME(&currSysTimeStop); if (j + k == i) j++; }\n\n\nvoid *\noverheadThread(void * arg)\n{\n  do\n    {\n      sched_yield();\n    }\n  while (running);\n\n  return NULL;\n}\n\n\nvoid *\noldThread(void * arg)\n{\n  do\n    {\n      (void) old_mutex_lock(&ox1);\n      (void) old_mutex_lock(&ox2);\n      (void) old_mutex_unlock(&ox1);\n      sched_yield();\n      (void) old_mutex_unlock(&ox2);\n    }\n  while (running);\n\n  return NULL;\n}\n\nvoid *\nworkerThread(void * arg)\n{\n  do\n    {\n      (void) pthread_mutex_lock(&gate1);\n      (void) pthread_mutex_lock(&gate2);\n      (void) pthread_mutex_unlock(&gate1);\n      sched_yield();\n      (void) pthread_mutex_unlock(&gate2);\n    }\n  while (running);\n\n  return NULL;\n}\n\nvoid *\nCSThread(void * arg)\n{\n  do\n    {\n      EnterCriticalSection(&cs1);\n      EnterCriticalSection(&cs2);\n      LeaveCriticalSection(&cs1);\n      sched_yield();\n      LeaveCriticalSection(&cs2);\n    }\n  while (running);\n\n  return NULL;\n}\n\nvoid\nrunTest (char * testNameString, int mType)\n{\n#ifdef PTW32_MUTEX_TYPES\n  assert(pthread_mutexattr_settype(&ma, mType) == 0);\n#endif\n  assert(pthread_mutex_init(&gate1, &ma) == 0);\n  assert(pthread_mutex_init(&gate2, &ma) == 0);\n  assert(pthread_mutex_lock(&gate1) == 0);\n  assert(pthread_mutex_lock(&gate2) == 0);\n  running = 1;\n  assert(pthread_create(&worker, NULL, workerThread, NULL) == 0);\n  TESTSTART\n  (void) pthread_mutex_unlock(&gate1);\n  sched_yield();\n  (void) pthread_mutex_unlock(&gate2);\n  (void) pthread_mutex_lock(&gate1);\n  (void) pthread_mutex_lock(&gate2);\n  TESTSTOP\n  running = 0;\n  assert(pthread_mutex_unlock(&gate2) == 0);\n  assert(pthread_mutex_unlock(&gate1) == 0);\n  assert(pthread_join(worker, NULL) == 0);\n  assert(pthread_mutex_destroy(&gate2) == 0);\n  assert(pthread_mutex_destroy(&gate1) == 0);\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    testNameString,\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS / 4   /* Four locks/unlocks per iteration */);\n}\n\n\nint\nmain (int argc, char *argv[])\n{\n  assert(pthread_mutexattr_init(&ma) == 0);\n\n  printf( \"=============================================================================\\n\");\n  printf( \"\\nLock plus unlock on a locked mutex.\\n\");\n  printf(\"%ld iterations, four locks/unlocks per iteration.\\n\\n\", ITERATIONS);\n\n  printf( \"%-45s %15s %15s\\n\",\n\t    \"Test\",\n\t    \"Total(msec)\",\n\t    \"average(usec)\");\n  printf( \"-----------------------------------------------------------------------------\\n\");\n\n  /*\n   * Time the loop overhead so we can subtract it from the actual test times.\n   */\n\n  running = 1;\n  assert(pthread_create(&worker, NULL, overheadThread, NULL) == 0);\n  TESTSTART\n  sched_yield();\n  sched_yield();\n  TESTSTOP\n  running = 0;\n  assert(pthread_join(worker, NULL) == 0);\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  overHeadMilliSecs = durationMilliSecs;\n\n\n  InitializeCriticalSection(&cs1);\n  InitializeCriticalSection(&cs2);\n  EnterCriticalSection(&cs1);\n  EnterCriticalSection(&cs2);\n  running = 1;\n  assert(pthread_create(&worker, NULL, CSThread, NULL) == 0);\n  TESTSTART\n  LeaveCriticalSection(&cs1);\n  sched_yield();\n  LeaveCriticalSection(&cs2);\n  EnterCriticalSection(&cs1);\n  EnterCriticalSection(&cs2);\n  TESTSTOP\n  running = 0;\n  LeaveCriticalSection(&cs2);\n  LeaveCriticalSection(&cs1);\n  assert(pthread_join(worker, NULL) == 0);\n  DeleteCriticalSection(&cs2);\n  DeleteCriticalSection(&cs1);\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Simple Critical Section\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS / 4 );\n\n\n  old_mutex_use = OLD_WIN32CS;\n  assert(old_mutex_init(&ox1, NULL) == 0);\n  assert(old_mutex_init(&ox2, NULL) == 0);\n  assert(old_mutex_lock(&ox1) == 0);\n  assert(old_mutex_lock(&ox2) == 0);\n  running = 1;\n  assert(pthread_create(&worker, NULL, oldThread, NULL) == 0);\n  TESTSTART\n  (void) old_mutex_unlock(&ox1);\n  sched_yield();\n  (void) old_mutex_unlock(&ox2);\n  (void) old_mutex_lock(&ox1);\n  (void) old_mutex_lock(&ox2);\n  TESTSTOP\n  running = 0;\n  assert(old_mutex_unlock(&ox1) == 0);\n  assert(old_mutex_unlock(&ox2) == 0);\n  assert(pthread_join(worker, NULL) == 0);\n  assert(old_mutex_destroy(&ox2) == 0);\n  assert(old_mutex_destroy(&ox1) == 0);\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Old PT Mutex using a Critical Section (WNT)\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS / 4);\n\n\n  old_mutex_use = OLD_WIN32MUTEX;\n  assert(old_mutex_init(&ox1, NULL) == 0);\n  assert(old_mutex_init(&ox2, NULL) == 0);\n  assert(old_mutex_lock(&ox1) == 0);\n  assert(old_mutex_lock(&ox2) == 0);\n  running = 1;\n  assert(pthread_create(&worker, NULL, oldThread, NULL) == 0);\n  TESTSTART\n  (void) old_mutex_unlock(&ox1);\n  sched_yield();\n  (void) old_mutex_unlock(&ox2);\n  (void) old_mutex_lock(&ox1);\n  (void) old_mutex_lock(&ox2);\n  TESTSTOP\n  running = 0;\n  assert(old_mutex_unlock(&ox1) == 0);\n  assert(old_mutex_unlock(&ox2) == 0);\n  assert(pthread_join(worker, NULL) == 0);\n  assert(old_mutex_destroy(&ox2) == 0);\n  assert(old_mutex_destroy(&ox1) == 0);\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Old PT Mutex using a Win32 Mutex (W9x)\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS / 4);\n\n  printf( \".............................................................................\\n\");\n\n  /*\n   * Now we can start the actual tests\n   */\n#ifdef PTW32_MUTEX_TYPES\n  runTest(\"PTHREAD_MUTEX_DEFAULT\", PTHREAD_MUTEX_DEFAULT);\n\n  runTest(\"PTHREAD_MUTEX_NORMAL\", PTHREAD_MUTEX_NORMAL);\n\n  runTest(\"PTHREAD_MUTEX_ERRORCHECK\", PTHREAD_MUTEX_ERRORCHECK);\n\n  runTest(\"PTHREAD_MUTEX_RECURSIVE\", PTHREAD_MUTEX_RECURSIVE);\n#else\n  runTest(\"Non-blocking lock\", 0);\n#endif\n\n  printf( \".............................................................................\\n\");\n\n  pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);\n\n#ifdef PTW32_MUTEX_TYPES\n  runTest(\"PTHREAD_MUTEX_DEFAULT (Robust)\", PTHREAD_MUTEX_DEFAULT);\n\n  runTest(\"PTHREAD_MUTEX_NORMAL (Robust)\", PTHREAD_MUTEX_NORMAL);\n\n  runTest(\"PTHREAD_MUTEX_ERRORCHECK (Robust)\", PTHREAD_MUTEX_ERRORCHECK);\n\n  runTest(\"PTHREAD_MUTEX_RECURSIVE (Robust)\", PTHREAD_MUTEX_RECURSIVE);\n#else\n  runTest(\"Non-blocking lock\", 0);\n#endif\n\n  printf( \"=============================================================================\\n\");\n  /*\n   * End of tests.\n   */\n\n  pthread_mutexattr_destroy(&ma);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/benchtest3.c",
    "content": "/*\n * benchtest3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Measure time taken to complete an elementary operation.\n *\n * - Mutex\n *   Single thread iteration over a trylock on a locked mutex for each mutex type.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n#ifdef __GNUC__\n#include <stdlib.h>\n#endif\n\n#include \"benchtest.h\"\n\n#define PTW32_MUTEX_TYPES\n#define ITERATIONS      10000000L\n\npthread_mutex_t mx;\nold_mutex_t ox;\npthread_mutexattr_t ma;\nPTW32_STRUCT_TIMEB currSysTimeStart;\nPTW32_STRUCT_TIMEB currSysTimeStop;\nlong durationMilliSecs;\nlong overHeadMilliSecs = 0;\n\n#define GetDurationMilliSecs(_TStart, _TStop) ((long)((_TStop.time*1000+_TStop.millitm) \\\n                                               - (_TStart.time*1000+_TStart.millitm)))\n\n/*\n * Dummy use of j, otherwise the loop may be removed by the optimiser\n * when doing the overhead timing with an empty loop.\n */\n#define TESTSTART \\\n  { int i, j = 0, k = 0; PTW32_FTIME(&currSysTimeStart); for (i = 0; i < ITERATIONS; i++) { j++;\n\n#define TESTSTOP \\\n  }; PTW32_FTIME(&currSysTimeStop); if (j + k == i) j++; }\n\n\nvoid *\ntrylockThread (void * arg)\n{\n  TESTSTART\n  (void) pthread_mutex_trylock(&mx);\n  TESTSTOP\n\n  return NULL;\n}\n\n\nvoid *\noldTrylockThread (void * arg)\n{\n  TESTSTART\n  (void) old_mutex_trylock(&ox);\n  TESTSTOP\n\n  return NULL;\n}\n\n\nvoid\nrunTest (char * testNameString, int mType)\n{\n  pthread_t t;\n\n#ifdef PTW32_MUTEX_TYPES\n  (void) pthread_mutexattr_settype(&ma, mType);\n#endif\n  assert(pthread_mutex_init(&mx, &ma) == 0);\n  assert(pthread_mutex_lock(&mx) == 0);\n  assert(pthread_create(&t, NULL, trylockThread, 0) == 0);\n  assert(pthread_join(t, NULL) == 0);\n  assert(pthread_mutex_unlock(&mx) == 0);\n  assert(pthread_mutex_destroy(&mx) == 0);\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    testNameString,\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n}\n\n\nint\nmain (int argc, char *argv[])\n{\n  pthread_t t;\n\n  assert(pthread_mutexattr_init(&ma) == 0);\n\n  printf( \"=============================================================================\\n\");\n  printf( \"\\nTrylock on a locked mutex.\\n\");\n  printf( \"%ld iterations.\\n\\n\", ITERATIONS);\n  printf( \"%-45s %15s %15s\\n\",\n\t    \"Test\",\n\t    \"Total(msec)\",\n\t    \"average(usec)\");\n  printf( \"-----------------------------------------------------------------------------\\n\");\n\n  /*\n   * Time the loop overhead so we can subtract it from the actual test times.\n   */\n\n  TESTSTART\n  TESTSTOP\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  overHeadMilliSecs = durationMilliSecs;\n\n\n  old_mutex_use = OLD_WIN32CS;\n  assert(old_mutex_init(&ox, NULL) == 0);\n  assert(old_mutex_lock(&ox) == 0);\n  assert(pthread_create(&t, NULL, oldTrylockThread, 0) == 0);\n  assert(pthread_join(t, NULL) == 0);\n  assert(old_mutex_unlock(&ox) == 0);\n  assert(old_mutex_destroy(&ox) == 0);\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Old PT Mutex using a Critical Section (WNT)\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n\n  old_mutex_use = OLD_WIN32MUTEX;\n  assert(old_mutex_init(&ox, NULL) == 0);\n  assert(old_mutex_lock(&ox) == 0);\n  assert(pthread_create(&t, NULL, oldTrylockThread, 0) == 0);\n  assert(pthread_join(t, NULL) == 0);\n  assert(old_mutex_unlock(&ox) == 0);\n  assert(old_mutex_destroy(&ox) == 0);\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Old PT Mutex using a Win32 Mutex (W9x)\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n\n  printf( \".............................................................................\\n\");\n\n  /*\n   * Now we can start the actual tests\n   */\n#ifdef PTW32_MUTEX_TYPES\n  runTest(\"PTHREAD_MUTEX_DEFAULT\", PTHREAD_MUTEX_DEFAULT);\n\n  runTest(\"PTHREAD_MUTEX_NORMAL\", PTHREAD_MUTEX_NORMAL);\n\n  runTest(\"PTHREAD_MUTEX_ERRORCHECK\", PTHREAD_MUTEX_ERRORCHECK);\n\n  runTest(\"PTHREAD_MUTEX_RECURSIVE\", PTHREAD_MUTEX_RECURSIVE);\n#else\n  runTest(\"Non-blocking lock\", 0);\n#endif\n\n  printf( \".............................................................................\\n\");\n\n  pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);\n\n#ifdef PTW32_MUTEX_TYPES\n  runTest(\"PTHREAD_MUTEX_DEFAULT (Robust)\", PTHREAD_MUTEX_DEFAULT);\n\n  runTest(\"PTHREAD_MUTEX_NORMAL (Robust)\", PTHREAD_MUTEX_NORMAL);\n\n  runTest(\"PTHREAD_MUTEX_ERRORCHECK (Robust)\", PTHREAD_MUTEX_ERRORCHECK);\n\n  runTest(\"PTHREAD_MUTEX_RECURSIVE (Robust)\", PTHREAD_MUTEX_RECURSIVE);\n#else\n  runTest(\"Non-blocking lock\", 0);\n#endif\n\n  printf( \"=============================================================================\\n\");\n\n  /*\n   * End of tests.\n   */\n\n  pthread_mutexattr_destroy(&ma);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/benchtest4.c",
    "content": "/*\n * benchtest4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Measure time taken to complete an elementary operation.\n *\n * - Mutex\n *   Single thread iteration over trylock/unlock for each mutex type.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n#ifdef __GNUC__\n#include <stdlib.h>\n#endif\n\n#include \"benchtest.h\"\n\n#define PTW32_MUTEX_TYPES\n#define ITERATIONS      10000000L\n\npthread_mutex_t mx;\nold_mutex_t ox;\npthread_mutexattr_t ma;\nPTW32_STRUCT_TIMEB currSysTimeStart;\nPTW32_STRUCT_TIMEB currSysTimeStop;\nlong durationMilliSecs;\nlong overHeadMilliSecs = 0;\n\n#define GetDurationMilliSecs(_TStart, _TStop) ((long)((_TStop.time*1000+_TStop.millitm) \\\n                                               - (_TStart.time*1000+_TStart.millitm)))\n\n/*\n * Dummy use of j, otherwise the loop may be removed by the optimiser\n * when doing the overhead timing with an empty loop.\n */\n#define TESTSTART \\\n  { int i, j = 0, k = 0; PTW32_FTIME(&currSysTimeStart); for (i = 0; i < ITERATIONS; i++) { j++;\n\n#define TESTSTOP \\\n  }; PTW32_FTIME(&currSysTimeStop); if (j + k == i) j++; }\n\n\nvoid\noldRunTest (char * testNameString, int mType)\n{\n}\n\n\nvoid\nrunTest (char * testNameString, int mType)\n{\n#ifdef PTW32_MUTEX_TYPES\n  pthread_mutexattr_settype(&ma, mType);\n#endif\n  pthread_mutex_init(&mx, &ma);\n\n  TESTSTART\n  (void) pthread_mutex_trylock(&mx);\n  (void) pthread_mutex_unlock(&mx);\n  TESTSTOP\n\n  pthread_mutex_destroy(&mx);\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    testNameString,\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n}\n\n\nint\nmain (int argc, char *argv[])\n{\n  pthread_mutexattr_init(&ma);\n\n  printf( \"=============================================================================\\n\");\n  printf( \"Trylock plus unlock on an unlocked mutex.\\n\");\n  printf( \"%ld iterations.\\n\\n\", ITERATIONS);\n  printf( \"%-45s %15s %15s\\n\",\n\t    \"Test\",\n\t    \"Total(msec)\",\n\t    \"average(usec)\");\n  printf( \"-----------------------------------------------------------------------------\\n\");\n\n  /*\n   * Time the loop overhead so we can subtract it from the actual test times.\n   */\n\n  TESTSTART\n  TESTSTOP\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  overHeadMilliSecs = durationMilliSecs;\n\n  old_mutex_use = OLD_WIN32CS;\n  assert(old_mutex_init(&ox, NULL) == 0);\n  TESTSTART\n  (void) old_mutex_trylock(&ox);\n  (void) old_mutex_unlock(&ox);\n  TESTSTOP\n  assert(old_mutex_destroy(&ox) == 0);\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Old PT Mutex using a Critical Section (WNT)\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n\n  old_mutex_use = OLD_WIN32MUTEX;\n  assert(old_mutex_init(&ox, NULL) == 0);\n  TESTSTART\n  (void) old_mutex_trylock(&ox);\n  (void) old_mutex_unlock(&ox);\n  TESTSTOP\n  assert(old_mutex_destroy(&ox) == 0);\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    \"Old PT Mutex using a Win32 Mutex (W9x)\",\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n\n  printf( \".............................................................................\\n\");\n\n  /*\n   * Now we can start the actual tests\n   */\n#ifdef PTW32_MUTEX_TYPES\n  runTest(\"PTHREAD_MUTEX_DEFAULT\", PTHREAD_MUTEX_DEFAULT);\n\n  runTest(\"PTHREAD_MUTEX_NORMAL\", PTHREAD_MUTEX_NORMAL);\n\n  runTest(\"PTHREAD_MUTEX_ERRORCHECK\", PTHREAD_MUTEX_ERRORCHECK);\n\n  runTest(\"PTHREAD_MUTEX_RECURSIVE\", PTHREAD_MUTEX_RECURSIVE);\n#else\n  runTest(\"Non-blocking lock\", 0);\n#endif\n\n  printf( \".............................................................................\\n\");\n\n  pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST);\n\n#ifdef PTW32_MUTEX_TYPES\n  runTest(\"PTHREAD_MUTEX_DEFAULT (Robust)\", PTHREAD_MUTEX_DEFAULT);\n\n  runTest(\"PTHREAD_MUTEX_NORMAL (Robust)\", PTHREAD_MUTEX_NORMAL);\n\n  runTest(\"PTHREAD_MUTEX_ERRORCHECK (Robust)\", PTHREAD_MUTEX_ERRORCHECK);\n\n  runTest(\"PTHREAD_MUTEX_RECURSIVE (Robust)\", PTHREAD_MUTEX_RECURSIVE);\n#else\n  runTest(\"Non-blocking lock\", 0);\n#endif\n\n  printf( \"=============================================================================\\n\");\n\n  /*\n   * End of tests.\n   */\n\n  pthread_mutexattr_destroy(&ma);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/benchtest5.c",
    "content": "/*\n * benchtest5.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Measure time taken to complete an elementary operation.\n *\n * - Semaphore\n *   Single thread iteration over post/wait for a semaphore.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n#ifdef __GNUC__\n#include <stdlib.h>\n#endif\n\n#include \"benchtest.h\"\n\n#define ITERATIONS      1000000L\n\nsem_t sema;\nHANDLE w32sema;\n\nPTW32_STRUCT_TIMEB currSysTimeStart;\nPTW32_STRUCT_TIMEB currSysTimeStop;\nlong durationMilliSecs;\nlong overHeadMilliSecs = 0;\nint one = 1;\nint zero = 0;\n\n#define GetDurationMilliSecs(_TStart, _TStop) ((long)((_TStop.time*1000+_TStop.millitm) \\\n                                               - (_TStart.time*1000+_TStart.millitm)))\n\n/*\n * Dummy use of j, otherwise the loop may be removed by the optimiser\n * when doing the overhead timing with an empty loop.\n */\n#define TESTSTART \\\n  { int i, j = 0, k = 0; PTW32_FTIME(&currSysTimeStart); for (i = 0; i < ITERATIONS; i++) { j++;\n\n#define TESTSTOP \\\n  }; PTW32_FTIME(&currSysTimeStop); if (j + k == i) j++; }\n\n\nvoid\nreportTest (char * testNameString)\n{\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n\n  printf( \"%-45s %15ld %15.3f\\n\",\n\t    testNameString,\n          durationMilliSecs,\n          (float) durationMilliSecs * 1E3 / ITERATIONS);\n}\n\n\nint\nmain (int argc, char *argv[])\n{\n  printf( \"=============================================================================\\n\");\n  printf( \"\\nOperations on a semaphore.\\n%ld iterations\\n\\n\",\n          ITERATIONS);\n  printf( \"%-45s %15s %15s\\n\",\n\t    \"Test\",\n\t    \"Total(msec)\",\n\t    \"average(usec)\");\n  printf( \"-----------------------------------------------------------------------------\\n\");\n\n  /*\n   * Time the loop overhead so we can subtract it from the actual test times.\n   */\n\n  TESTSTART\n  assert(1 == one);\n  TESTSTOP\n\n  durationMilliSecs = GetDurationMilliSecs(currSysTimeStart, currSysTimeStop) - overHeadMilliSecs;\n  overHeadMilliSecs = durationMilliSecs;\n\n\n  /*\n   * Now we can start the actual tests\n   */\n  assert((w32sema = CreateSemaphore(NULL, (long) 0, (long) ITERATIONS, NULL)) != 0);\n  TESTSTART\n  assert((ReleaseSemaphore(w32sema, 1, NULL),1) == one);\n  TESTSTOP\n  assert(CloseHandle(w32sema) != 0);\n\n  reportTest(\"W32 Post with no waiters\");\n\n\n  assert((w32sema = CreateSemaphore(NULL, (long) ITERATIONS, (long) ITERATIONS, NULL)) != 0);\n  TESTSTART\n  assert((WaitForSingleObject(w32sema, INFINITE),1) == one);\n  TESTSTOP\n  assert(CloseHandle(w32sema) != 0);\n\n  reportTest(\"W32 Wait without blocking\");\n\n\n  assert(sem_init(&sema, 0, 0) == 0);\n  TESTSTART\n  assert((sem_post(&sema),1) == one);\n  TESTSTOP\n  assert(sem_destroy(&sema) == 0);\n\n  reportTest(\"POSIX Post with no waiters\");\n\n\n  assert(sem_init(&sema, 0, ITERATIONS) == 0);\n  TESTSTART\n  assert((sem_wait(&sema),1) == one);\n  TESTSTOP\n  assert(sem_destroy(&sema) == 0);\n\n  reportTest(\"POSIX Wait without blocking\");\n\n\n  printf( \"=============================================================================\\n\");\n\n  /*\n   * End of tests.\n   */\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cancel1.c",
    "content": "/*\n * File: cancel1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test setting cancel state and cancel type.\n * - \n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * - pthread_setcancelstate function\n * - pthread_setcanceltype function\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - pthread_create, pthread_self work.\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 2\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\nvoid *\nmythread(void * arg)\n{\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* ... */\n  {\n    int oldstate;\n    int oldtype;\n\n    assert(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) == 0);\n    assert(oldstate == PTHREAD_CANCEL_ENABLE); /* Check default */\n    assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);\n    assert(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL) == 0);\n    assert(pthread_setcancelstate(oldstate, &oldstate) == 0);\n    assert(oldstate == PTHREAD_CANCEL_DISABLE); /* Check setting */\n\n    assert(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype) == 0);\n    assert(oldtype == PTHREAD_CANCEL_DEFERRED); /* Check default */\n    assert(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) == 0);\n    assert(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);\n    assert(pthread_setcanceltype(oldtype, &oldtype) == 0);\n    assert(oldtype == PTHREAD_CANCEL_ASYNCHRONOUS); /* Check setting */\n  }\n\n  return 0;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      failed = !threadbag[i].started;\n\n      if (failed)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print ouput on failure.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      /* ... */\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cancel2.c",
    "content": "/*\n * File: cancel2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test SEH or C++ cancel exception handling within\n * application exception blocks.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n/*\n * Don't know how to identify if we are using SEH so it's only C++ for now\n */\n#if defined(__cplusplus)\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 4\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\nstatic pthread_mutex_t waitLock = PTHREAD_MUTEX_INITIALIZER;\n\nvoid *\nmythread(void * arg)\n{\n  int result = 0;\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* Set to known state and type */\n\n  assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);\n\n  switch (bag->threadnum % 2)\n    {\n    case 0:\n      assert(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);\n      result = 0;\n      break;\n    case 1:\n      assert(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) == 0);\n      result = 1;\n      break;\n    }\n\n#if !defined(__cplusplus)\n  __try\n#else\n  try\n#endif\n    {\n      /* Wait for go from main */\n      assert(pthread_mutex_lock(&waitLock) == 0);\n      assert(pthread_mutex_unlock(&waitLock) == 0);\n      sched_yield();\n\n      for (;;)\n\t{\n\t  pthread_testcancel();\n\t}\n    }\n#if !defined(__cplusplus)\n  __except(EXCEPTION_EXECUTE_HANDLER)\n#else\n#if defined(PtW32CatchAll)\n  PtW32CatchAll\n#else\n  catch(...)\n#endif\n#endif\n    {\n      /*\n       * Should not get into here.\n       */\n      result += 100;\n    }\n\n  /* \n   * Should not get to here either.\n   */\n  result += 1000;\n\n  return (void *) (size_t)result;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  assert((t[0] = pthread_self()).p != NULL);\n  assert(pthread_mutex_lock(&waitLock) == 0);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(500);\n\n  assert(pthread_mutex_unlock(&waitLock) == 0);\n\n  Sleep(500);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_cancel(t[i]) == 0);\n    }\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)0;\n\n      assert(pthread_join(t[i], &result) == 0);\n      fail = ((int)(size_t)result != (int) PTHREAD_CANCELED);\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: location %d: cancel type %s\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  (int)(size_t)result,\n\t\t  (((int)(size_t)result % 2) == 0) ? \"ASYNCHRONOUS\" : \"DEFERRED\");\n\t}\n      failed |= fail;\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n#else /* defined(__cplusplus) */\n\n#include <stdio.h>\n\nint\nmain()\n{\n  fprintf(stderr, \"Test N/A for this compiler environment.\\n\");\n  return 0;\n}\n\n#endif /* defined(__cplusplus) */\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cancel3.c",
    "content": "/*\n * File: cancel3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test asynchronous cancelation (alertable or non-alertable).\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * - Async cancel if thread is not blocked (i.e. voluntarily resumes if blocked).\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join.\n * - quserex.dll and alertdrv.sys are not available.\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum\n{\n  NUMTHREADS = 4\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_\n{\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\nvoid *\nmythread (void *arg)\n{\n  void* result = (void*)((int)(size_t)PTHREAD_CANCELED + 1);\n  bag_t *bag = (bag_t *) arg;\n\n  assert (bag == &threadbag[bag->threadnum]);\n  assert (bag->started == 0);\n  bag->started = 1;\n\n  /* Set to known state and type */\n\n  assert (pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL) == 0);\n\n  assert (pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);\n\n  /*\n   * We wait up to 10 seconds, waking every 0.1 seconds,\n   * for a cancelation to be applied to us.\n   */\n  for (bag->count = 0; bag->count < 100; bag->count++)\n    Sleep (100);\n\n  return result;\n}\n\nint\nmain ()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  assert ((t[0] = pthread_self ()).p != NULL);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert (pthread_create (&t[i], NULL, mythread, (void *) &threadbag[i])\n\t      == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep (NUMTHREADS * 100);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert (pthread_cancel (t[i]) == 0);\n    }\n\n  /*\n   * Give threads time to complete.\n   */\n  Sleep (NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf (stderr, \"Thread %d: started %d\\n\", i,\n\t\t   threadbag[i].started);\n\t}\n    }\n\n  assert (!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)0;\n\n      /*\n       * The thread does not contain any cancelation points, so\n       * a return value of PTHREAD_CANCELED confirms that async\n       * cancelation succeeded.\n       */\n      assert (pthread_join (t[i], &result) == 0);\n\n      fail = (result != PTHREAD_CANCELED);\n\n      if (fail)\n\t{\n\t  fprintf (stderr, \"Thread %d: started %d: count %d\\n\",\n\t\t   i, threadbag[i].started, threadbag[i].count);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert (!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cancel4.c",
    "content": "/*\n * File: cancel4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test cancelation does not occur in deferred\n *                cancelation threads with no cancelation points.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - pthread_create\n *   pthread_self\n *   pthread_cancel\n *   pthread_join\n *   pthread_setcancelstate\n *   pthread_setcanceltype\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 4\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\nvoid *\nmythread(void * arg)\n{\n  void* result = (void*)((int)(size_t)PTHREAD_CANCELED + 1);\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* Set to known state and type */\n\n  assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);\n\n  assert(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) == 0);\n\n  /*\n   * We wait up to 2 seconds, waking every 0.1 seconds,\n   * for a cancelation to be applied to us.\n   */\n  for (bag->count = 0; bag->count < 20; bag->count++)\n    Sleep(100);\n\n  return result;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(500);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_cancel(t[i]) == 0);\n    }\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)0;\n\n      /*\n       * The thread does not contain any cancelation points, so\n       * a return value of PTHREAD_CANCELED indicates that async\n       * cancelation occurred.\n       */\n      assert(pthread_join(t[i], &result) == 0);\n\n      fail = (result == PTHREAD_CANCELED);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: count %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  threadbag[i].count);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cancel5.c",
    "content": "/*\n * File: cancel5.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test calling pthread_cancel from the main thread\n *                without calling pthread_self() in main.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum\n{\n  NUMTHREADS = 4\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_\n{\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\nvoid *\nmythread (void *arg)\n{\n  void* result = (void*)((int)(size_t)PTHREAD_CANCELED + 1);\n  bag_t *bag = (bag_t *) arg;\n\n  assert (bag == &threadbag[bag->threadnum]);\n  assert (bag->started == 0);\n  bag->started = 1;\n\n  /* Set to known state and type */\n\n  assert (pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL) == 0);\n\n  assert (pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);\n\n  /*\n   * We wait up to 10 seconds, waking every 0.1 seconds,\n   * for a cancelation to be applied to us.\n   */\n  for (bag->count = 0; bag->count < 100; bag->count++)\n    Sleep (100);\n\n  return result;\n}\n\nint\nmain ()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert (pthread_create (&t[i], NULL, mythread, (void *) &threadbag[i])\n\t      == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep (500);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert (pthread_cancel (t[i]) == 0);\n    }\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep (NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf (stderr, \"Thread %d: started %d\\n\", i,\n\t\t   threadbag[i].started);\n\t}\n    }\n\n  assert (!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)((int)(size_t)PTHREAD_CANCELED + 1);\n\n      /*\n       * The thread does not contain any cancelation points, so\n       * a return value of PTHREAD_CANCELED confirms that async\n       * cancelation succeeded.\n       */\n      assert (pthread_join (t[i], &result) == 0);\n\n      fail = (result != PTHREAD_CANCELED);\n\n      if (fail)\n\t{\n\t  fprintf (stderr, \"Thread %d: started %d: count %d\\n\",\n\t\t   i, threadbag[i].started, threadbag[i].count);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert (!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cancel6a.c",
    "content": "/*\n * File: cancel6a.c\n *\n *\n * Pthreads-win32 - POSIX Threads Library for Win32\n * Copyright (C) 1998 Ben Elliston and Ross Johnson\n * Copyright (C) 1999,2000,2001 Ross Johnson\n *\n * Contact Email: rpj@ise.canberra.edu.au\n *\n * This library is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation; either\n * version 2.1 of the License, or (at your option) any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test double cancelation - asynchronous.\n * Second attempt should fail (ESRCH).\n *\n * Test Method (Validation or Falsification):\n * -\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 4\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\nvoid *\nmythread(void * arg)\n{\n  void* result = (void*)((int)(size_t)PTHREAD_CANCELED + 1);\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* Set to known state and type */\n\n  assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);\n\n  assert(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);\n\n  /*\n   * We wait up to 10 seconds, waking every 0.1 seconds,\n   * for a cancelation to be applied to us.\n   */\n  for (bag->count = 0; bag->count < 100; bag->count++)\n    Sleep(100);\n\n  return result;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(500);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_cancel(t[i]) == 0);\n      assert(pthread_cancel(t[i]) == ESRCH);\n    }\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)0;\n\n      /*\n       * The thread does not contain any cancelation points, so\n       * a return value of PTHREAD_CANCELED confirms that async\n       * cancelation succeeded.\n       */\n      assert(pthread_join(t[i], &result) == 0);\n\n      fail = (result != PTHREAD_CANCELED);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: count %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  threadbag[i].count);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cancel6d.c",
    "content": "/*\n * File: cancel6d.c\n *\n *\n * Pthreads-win32 - POSIX Threads Library for Win32\n * Copyright (C) 1998 Ben Elliston and Ross Johnson\n * Copyright (C) 1999,2000,2001 Ross Johnson\n *\n * Contact Email: rpj@ise.canberra.edu.au\n *\n * This library is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation; either\n * version 2.1 of the License, or (at your option) any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test double cancelation - deferred.\n * Second attempt should succeed (unless the canceled thread has started\n * cancelation already - not tested here).\n *\n * Test Method (Validation or Falsification):\n * -\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 4\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\nvoid *\nmythread(void * arg)\n{\n  void* result = (void*)((int)(size_t)PTHREAD_CANCELED + 1);\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* Set to known state and type */\n\n  assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);\n\n  assert(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) == 0);\n\n  /*\n   * We wait up to 10 seconds, waking every 0.1 seconds,\n   * for a cancelation to be applied to us.\n   */\n  for (bag->count = 0; bag->count < 100; bag->count++)\n    {\n      Sleep(100);\n      pthread_testcancel();\n    }\n\n  return result;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *)(size_t) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(500);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_cancel(t[i]) == 0);\n      if (pthread_cancel(t[i]) != 0)\n        {\n          printf(\"Second cancelation failed but this is expected sometimes.\\n\");\n        }\n    }\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)0;\n\n      assert(pthread_join(t[i], &result) == 0);\n\n      fail = (result != PTHREAD_CANCELED);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: count %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  threadbag[i].count);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cancel7.c",
    "content": "/*\n * File: cancel7.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test canceling a Win32 thread having created an\n * implicit POSIX handle for it.\n *\n * Test Method (Validation or Falsification):\n * - Validate return value and that POSIX handle is created and destroyed.\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#ifndef _UWIN\n#include <process.h>\n#endif\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 4\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n  pthread_t self;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\nunsigned __stdcall\n#else\nvoid\n#endif\nWin32thread(void * arg)\n{\n  int i;\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  assert((bag->self = pthread_self()).p != NULL);\n  assert(pthread_kill(bag->self, 0) == 0);\n\n  for (i = 0; i < 100; i++)\n    {\n      Sleep(100);\n      pthread_testcancel();\n    }\n\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\n  return 0;\n#endif\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  HANDLE h[NUMTHREADS + 1];\n  unsigned thrAddr; /* Dummy variable to pass a valid location to _beginthreadex (Win98). */\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\n      h[i] = (HANDLE) _beginthreadex(NULL, 0, Win32thread, (void *) &threadbag[i], 0, &thrAddr);\n#else\n      h[i] = (HANDLE) _beginthread(Win32thread, 0, (void *) &threadbag[i]);\n#endif\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(500);\n\n  /*\n   * Cancel all threads.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_kill(threadbag[i].self, 0) == 0);\n      assert(pthread_cancel(threadbag[i].self) == 0);\n    }\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      int result = 0;\n\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\n      assert(GetExitCodeThread(h[i], (LPDWORD) &result) == TRUE);\n#else\n      /*\n       * Can't get a result code.\n       */\n      result = (int)(size_t)PTHREAD_CANCELED;\n#endif\n\n      assert(threadbag[i].self.p != NULL);\n      assert(pthread_kill(threadbag[i].self, 0) == ESRCH);\n\n      fail = (result != (int)(size_t)PTHREAD_CANCELED);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: count %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  threadbag[i].count);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cancel8.c",
    "content": "/*\n * File: cancel8.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test cancelling a blocked Win32 thread having created an\n * implicit POSIX handle for it.\n *\n * Test Method (Validation or Falsification):\n * - Validate return value and that POSIX handle is created and destroyed.\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#ifndef _UWIN\n#include <process.h>\n#endif\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 4\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n  pthread_t self;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\npthread_cond_t CV = PTHREAD_COND_INITIALIZER;\npthread_mutex_t CVLock = PTHREAD_MUTEX_INITIALIZER;\n\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\nunsigned __stdcall\n#else\nvoid\n#endif\nWin32thread(void * arg)\n{\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  assert((bag->self = pthread_self()).p != NULL);\n  assert(pthread_kill(bag->self, 0) == 0);\n\n  assert(pthread_mutex_lock(&CVLock) == 0);\n  pthread_cleanup_push(pthread_mutex_unlock, &CVLock);\n  pthread_cond_wait(&CV, &CVLock);\n  pthread_cleanup_pop(1);\n\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\n  return 0;\n#endif\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  HANDLE h[NUMTHREADS + 1];\n  unsigned thrAddr; /* Dummy variable to pass a valid location to _beginthreadex (Win98). */\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\n      h[i] = (HANDLE) _beginthreadex(NULL, 0, Win32thread, (void *) &threadbag[i], 0, &thrAddr);\n#else\n      h[i] = (HANDLE) _beginthread(Win32thread, 0, (void *) &threadbag[i]);\n#endif\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(500);\n\n  /*\n   * Cancel all threads.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_kill(threadbag[i].self, 0) == 0);\n      assert(pthread_cancel(threadbag[i].self) == 0);\n    }\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      int result = 0;\n\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\n      assert(GetExitCodeThread(h[i], (LPDWORD) &result) == TRUE);\n#else\n      /*\n       * Can't get a result code.\n       */\n      result = (int)(size_t)PTHREAD_CANCELED;\n#endif\n\n      assert(threadbag[i].self.p != NULL);\n      assert(pthread_kill(threadbag[i].self, 0) == ESRCH);\n\n      fail = (result != (int)(size_t)PTHREAD_CANCELED);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: count %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  threadbag[i].count);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cancel9.c",
    "content": "/*\n * File: cancel9.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test true asynchronous cancelation with Alert driver.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * - Cancel threads, including those blocked on system recources\n *   such as network I/O.\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#include <windows.h>\n\n\nvoid *\ntest_udp (void *arg)\n{\n  struct sockaddr_in serverAddress;\n  struct sockaddr_in clientAddress;\n  SOCKET UDPSocket;\n  int addr_len;\n  int nbyte, bytes;\n  char buffer[4096];\n  WORD wsaVersion = MAKEWORD (2, 2);\n  WSADATA wsaData;\n\n  pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);\n  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);\n\n  if (WSAStartup (wsaVersion, &wsaData) != 0)\n    {\n      return NULL;\n    }\n\n  UDPSocket = socket (AF_INET, SOCK_DGRAM, 0);\n  if ((int)UDPSocket == -1)\n    {\n      printf (\"Server: socket ERROR \\n\");\n      exit (-1);\n    }\n\n  serverAddress.sin_family = AF_INET;\n  serverAddress.sin_addr.s_addr = INADDR_ANY;\n  serverAddress.sin_port = htons (9003);\n\n  if (bind\n      (UDPSocket, (struct sockaddr *) &serverAddress,\n       sizeof (struct sockaddr_in)))\n    {\n      printf (\"Server: ERROR can't bind UDPSocket\");\n      exit (-1);\n    }\n\n  addr_len = sizeof (struct sockaddr);\n\n  nbyte = 512;\n\n  bytes =\n    recvfrom (UDPSocket, (char *) buffer, nbyte, 0,\n\t      (struct sockaddr *) &clientAddress, &addr_len);\n\n  closesocket (UDPSocket);\n  WSACleanup ();\n\n  return NULL;\n}\n\n\nvoid *\ntest_sleep (void *arg)\n{\n  pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);\n  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);\n\n  Sleep (1000);\n  return NULL;\n\n}\n\nvoid *\ntest_wait (void *arg)\n{\n  HANDLE hEvent;\n  DWORD dwEvent;\n\n  pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);\n  pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);\n\n  hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);\n\n  dwEvent = WaitForSingleObject (hEvent, 1000);\t/* WAIT_IO_COMPLETION */\n\n  return NULL;\n}\n\n\nint\nmain ()\n{\n  pthread_t t;\n  void *result;\n\n  if (pthread_win32_test_features_np (PTW32_ALERTABLE_ASYNC_CANCEL))\n    {\n      printf (\"Cancel sleeping thread.\\n\");\n      assert (pthread_create (&t, NULL, test_sleep, NULL) == 0);\n      /* Sleep for a while; then cancel */\n      Sleep (100);\n      assert (pthread_cancel (t) == 0);\n      assert (pthread_join (t, &result) == 0);\n      assert (result == PTHREAD_CANCELED && \"test_sleep\" != NULL);\n\n      printf (\"Cancel waiting thread.\\n\");\n      assert (pthread_create (&t, NULL, test_wait, NULL) == 0);\n      /* Sleep for a while; then cancel. */\n      Sleep (100);\n      assert (pthread_cancel (t) == 0);\n      assert (pthread_join (t, &result) == 0);\n      assert (result == PTHREAD_CANCELED && \"test_wait\");\n\n      printf (\"Cancel blocked thread (blocked on network I/O).\\n\");\n      assert (pthread_create (&t, NULL, test_udp, NULL) == 0);\n      /* Sleep for a while; then cancel. */\n      Sleep (100);\n      assert (pthread_cancel (t) == 0);\n      assert (pthread_join (t, &result) == 0);\n      assert (result == PTHREAD_CANCELED && \"test_udp\" != NULL);\n    }\n  else\n    {\n      printf (\"Alertable async cancel not available.\\n\");\n    }\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cleanup0.c",
    "content": "/*\n * File: cleanup1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test cleanup handler executes (when thread is not canceled).\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#if defined(_MSC_VER) || defined(__cplusplus)\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 10\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\ntypedef struct {\n  int i;\n  CRITICAL_SECTION cs;\n} sharedInt_t;\n\nstatic sharedInt_t pop_count = {0, {0}};\n\nstatic void\nincrement_pop_count(void * arg)\n{\n  sharedInt_t * sI = (sharedInt_t *) arg;\n\n  EnterCriticalSection(&sI->cs);\n  sI->i++;\n  LeaveCriticalSection(&sI->cs);\n}\n\nvoid *\nmythread(void * arg)\n{\n  int result = 0;\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* Set to known state and type */\n\n  assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);\n\n  assert(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);\n\n#ifdef _MSC_VER\n#pragma inline_depth(0)\n#endif\n  pthread_cleanup_push(increment_pop_count, (void *) &pop_count);\n\n  Sleep(100);\n\n  pthread_cleanup_pop(1);\n#ifdef _MSC_VER\n#pragma inline_depth()\n#endif\n\n  return (void *) (size_t)result;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  InitializeCriticalSection(&pop_count.cs);\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(500);\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)0;\n\n      assert(pthread_join(t[i], &result) == 0);\n\n      fail = ((int)(size_t)result == (int) PTHREAD_CANCELED);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: result %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t(int)(size_t)result);\n\t  fflush(stderr);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  assert(pop_count.i == NUMTHREADS);\n\n  DeleteCriticalSection(&pop_count.cs);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n#else /* defined(_MSC_VER) || defined(__cplusplus) */\n\nint\nmain()\n{\n  return 0;\n}\n\n#endif /* defined(_MSC_VER) || defined(__cplusplus) */\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cleanup1.c",
    "content": "/*\n * File: cleanup1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test cleanup handler executes (when thread is canceled).\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#if defined(_MSC_VER) || defined(__cplusplus)\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 10\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\ntypedef struct {\n  int i;\n  CRITICAL_SECTION cs;\n} sharedInt_t;\n\nstatic sharedInt_t pop_count = {0, {0}};\n\nstatic void\n#ifdef __CLEANUP_C\n__cdecl\n#endif\nincrement_pop_count(void * arg)\n{\n  sharedInt_t * sI = (sharedInt_t *) arg;\n\n  EnterCriticalSection(&sI->cs);\n  sI->i++;\n  LeaveCriticalSection(&sI->cs);\n}\n\nvoid *\nmythread(void * arg)\n{\n  int result = 0;\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* Set to known state and type */\n\n  assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);\n\n  assert(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);\n\n#ifdef _MSC_VER\n#pragma inline_depth(0)\n#endif\n  pthread_cleanup_push(increment_pop_count, (void *) &pop_count);\n  /*\n   * We don't have true async cancelation - it relies on the thread\n   * at least re-entering the run state at some point.\n   * We wait up to 10 seconds, waking every 0.1 seconds,\n   * for a cancelation to be applied to us.\n   */\n  for (bag->count = 0; bag->count < 100; bag->count++)\n    Sleep(100);\n\n  pthread_cleanup_pop(0);\n#ifdef _MSC_VER\n#pragma inline_depth()\n#endif\n\n  return (void *) (size_t)result;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  InitializeCriticalSection(&pop_count.cs);\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(500);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_cancel(t[i]) == 0);\n    }\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)0;\n\n      assert(pthread_join(t[i], &result) == 0);\n\n      fail = ((int)(size_t)result != (int) PTHREAD_CANCELED);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: result %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  (int)(size_t)result);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  assert(pop_count.i == NUMTHREADS);\n\n  DeleteCriticalSection(&pop_count.cs);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n#else /* defined(_MSC_VER) || defined(__cplusplus) */\n\nint\nmain()\n{\n  return 0;\n}\n\n#endif /* defined(_MSC_VER) || defined(__cplusplus) */\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cleanup2.c",
    "content": "/*\n * File: cleanup2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test cleanup handler executes (when thread is not canceled).\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#if defined(_MSC_VER) || defined(__cplusplus)\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 10\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\ntypedef struct {\n  int i;\n  CRITICAL_SECTION cs;\n} sharedInt_t;\n\nstatic sharedInt_t pop_count = {0, {0}};\n\nstatic void\nincrement_pop_count(void * arg)\n{\n  sharedInt_t * sI = (sharedInt_t *) arg;\n\n  EnterCriticalSection(&sI->cs);\n  sI->i++;\n  LeaveCriticalSection(&sI->cs);\n}\n\nvoid *\nmythread(void * arg)\n{\n  int result = 0;\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n#ifdef _MSC_VER\n#pragma inline_depth(0)\n#endif\n  pthread_cleanup_push(increment_pop_count, (void *) &pop_count);\n\n  sched_yield();\n\n  pthread_cleanup_pop(1);\n#ifdef _MSC_VER\n#pragma inline_depth()\n#endif\n\n  return (void *) (size_t)result;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  InitializeCriticalSection(&pop_count.cs);\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(1000);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)0;\n\n      assert(pthread_join(t[i], &result) == 0);\n\n      fail = ((int)(size_t)result != 0);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: result: %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  (int)(size_t)result);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  assert(pop_count.i == NUMTHREADS);\n\n  DeleteCriticalSection(&pop_count.cs);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n#else /* defined(_MSC_VER) || defined(__cplusplus) */\n\nint\nmain()\n{\n  return 0;\n}\n\n#endif /* defined(_MSC_VER) || defined(__cplusplus) */\n"
  },
  {
    "path": "deps/w32-pthreads/tests/cleanup3.c",
    "content": "/*\n * File: cleanup3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test cleanup handler does not execute (when thread is\n * not canceled).\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#if defined(_MSC_VER) || defined(__cplusplus)\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 10\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\ntypedef struct {\n  int i;\n  CRITICAL_SECTION cs;\n} sharedInt_t;\n\nstatic sharedInt_t pop_count = {0, {0}};\n\nstatic void\nincrement_pop_count(void * arg)\n{\n  sharedInt_t * sI = (sharedInt_t *) arg;\n\n  EnterCriticalSection(&sI->cs);\n  sI->i++;\n  LeaveCriticalSection(&sI->cs);\n}\n\nvoid *\nmythread(void * arg)\n{\n  int result = 0;\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n#ifdef _MSC_VER\n#pragma inline_depth(0)\n#endif\n  pthread_cleanup_push(increment_pop_count, (void *) &pop_count);\n\n  sched_yield();\n\n  EnterCriticalSection(&pop_count.cs);\n  pop_count.i--;\n  LeaveCriticalSection(&pop_count.cs);\n\n  pthread_cleanup_pop(0);\n#ifdef _MSC_VER\n#pragma inline_depth()\n#endif\n\n  return (void *) (size_t)result;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  InitializeCriticalSection(&pop_count.cs);\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(1000);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)0;\n\n      assert(pthread_join(t[i], &result) == 0);\n\n      fail = ((int)(size_t)result != 0);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: result: %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  (int)(size_t)result);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  assert(pop_count.i == -(NUMTHREADS));\n\n  DeleteCriticalSection(&pop_count.cs);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n#else /* defined(_MSC_VER) || defined(__cplusplus) */\n\nint\nmain()\n{\n  return 0;\n}\n\n#endif /* defined(_MSC_VER) || defined(__cplusplus) */\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar1.c",
    "content": "/*\n * File: condvar1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test initialisation and destruction of a CV.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Creates and then imediately destroys a CV. Does not\n *   test the CV.\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - pthread_cond_init returns 0, and\n * - pthread_cond_destroy returns 0.\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - pthread_cond_init returns non-zero, or\n * - pthread_cond_destroy returns non-zero.\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\nstatic pthread_cond_t cv = NULL;\n\nint\nmain()\n{\n  assert(cv == NULL);\n\n  assert(pthread_cond_init(&cv, NULL) == 0);\n\n  assert(cv != NULL);\n\n  assert(pthread_cond_destroy(&cv) == 0);\n\n  assert(cv == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar1_1.c",
    "content": "/*\n * File: condvar1_1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test CV linked list management.\n *\n * Test Method (Validation or Falsification):\n * - Validation:\n *   Initiate and destroy several CVs in random order.\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Creates and then imediately destroys a CV. Does not\n *   test the CV.\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - All initialised CVs destroyed without segfault.\n * - Successfully broadcasts all remaining CVs after\n *   each CV is removed.\n *\n * Fail Criteria:\n */\n\n#include <stdlib.h>\n#include \"test.h\"\n\nenum {\n  NUM_CV = 100\n};\n\nstatic pthread_cond_t cv[NUM_CV];\n\nint\nmain()\n{\n  int i, j;\n\n  for (i = 0; i < NUM_CV; i++)\n    {\n      /* Traverse the list before every init of a CV. */\n      assert(pthread_timechange_handler_np(NULL) == (void *) 0);\n      assert(pthread_cond_init(&cv[i], NULL) == 0);\n    }\n\n  j = NUM_CV;\n  (void) srand((unsigned)time(NULL));\n\n  do\n    {\n      i = (NUM_CV - 1) * rand() / RAND_MAX;\n      if (cv[i] != NULL)\n        {\n          j--;\n          assert(pthread_cond_destroy(&cv[i]) == 0);\n          /* Traverse the list every time we remove a CV. */\n          assert(pthread_timechange_handler_np(NULL) == (void *) 0);\n        }\n    }\n  while (j > 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar1_2.c",
    "content": "/*\n * File: condvar1_2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test CV linked list management and serialisation.\n *\n * Test Method (Validation or Falsification):\n * - Validation:\n *   Initiate and destroy several CVs in random order.\n *   Asynchronously traverse the CV list and broadcast.\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Creates and then imediately destroys a CV. Does not\n *   test the CV.\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - All initialised CVs destroyed without segfault.\n * - Successfully broadcasts all remaining CVs after\n *   each CV is removed.\n *\n * Fail Criteria:\n */\n\n#include <stdlib.h>\n#include \"test.h\"\n\nenum {\n  NUM_CV = 5,\n  NUM_LOOPS = 5\n};\n\nstatic pthread_cond_t cv[NUM_CV];\n\nint\nmain()\n{\n  int i, j, k;\n  void* result = (void*)-1;\n  pthread_t t;\n\n  for (k = 0; k < NUM_LOOPS; k++)\n    {\n      for (i = 0; i < NUM_CV; i++)\n        {\n          assert(pthread_cond_init(&cv[i], NULL) == 0);\n        }\n\n      j = NUM_CV;\n      (void) srand((unsigned)time(NULL));\n\n      /* Traverse the list asynchronously. */\n      assert(pthread_create(&t, NULL, pthread_timechange_handler_np, NULL) == 0);\n\n      do\n        {\n          i = (NUM_CV - 1) * rand() / RAND_MAX;\n          if (cv[i] != NULL)\n            {\n              j--;\n              assert(pthread_cond_destroy(&cv[i]) == 0);\n            }\n        }\n      while (j > 0);\n\n      assert(pthread_join(t, &result) == 0);\n      assert ((int)(size_t)result == 0);\n    }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar2.c",
    "content": "/*\n * File: condvar2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test timed wait on a CV.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Because the CV is never signaled, we expect the wait to time out.\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - pthread_cond_timedwait returns ETIMEDOUT.\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - pthread_cond_timedwait does not return ETIMEDOUT.\n * - Process returns non-zero exit status.\n */\n\n#define _WIN32_WINNT 0x400\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\npthread_cond_t cv;\npthread_mutex_t mutex;\n\n#include \"../implement.h\"\n\nint\nmain()\n{\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  assert(pthread_cond_init(&cv, NULL) == 0);\n\n  assert(pthread_mutex_init(&mutex, NULL) == 0);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  /* get current system time */\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 1;\n\n  assert(pthread_cond_timedwait(&cv, &mutex, &abstime) == ETIMEDOUT);\n  \n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  {\n  int result = pthread_cond_destroy(&cv);\n  if (result != 0)\n    {\n      fprintf(stderr, \"Result = %s\\n\", error_string[result]);\n\tfprintf(stderr, \"\\tWaitersBlocked = %ld\\n\", cv->nWaitersBlocked);\n\tfprintf(stderr, \"\\tWaitersGone = %ld\\n\", cv->nWaitersGone);\n\tfprintf(stderr, \"\\tWaitersToUnblock = %ld\\n\", cv->nWaitersToUnblock);\n\tfflush(stderr);\n    }\n  assert(result == 0);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar2_1.c",
    "content": "/*\n * File: condvar2_1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test timeout of multiple waits on a CV with no signal/broadcast.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Because the CV is never signaled, we expect the waits to time out.\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - pthread_cond_timedwait returns ETIMEDOUT.\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - pthread_cond_timedwait does not return ETIMEDOUT.\n * - Process returns non-zero exit status.\n */\n\n#define _WIN32_WINNT 0x400\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\nstatic pthread_cond_t cv;\nstatic pthread_mutex_t mutex;\nstatic struct timespec abstime = { 0, 0 };\n\nenum {\n  NUMTHREADS = 30\n};\n\nvoid *\nmythread(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(pthread_cond_timedwait(&cv, &mutex, &abstime) == ETIMEDOUT);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return arg;\n}\n\n#include \"../implement.h\"\n\nint\nmain()\n{\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n  void* result = (void*)0;\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  assert(pthread_cond_init(&cv, NULL) == 0);\n\n  assert(pthread_mutex_init(&mutex, NULL) == 0);\n\n  /* get current system time */\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 5;\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_create(&t[i], NULL, mythread, (void *)(size_t)i) == 0);\n    }\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_join(t[i], &result) == 0);\n\tassert((int)(size_t)result == i);\n    }\n\n  {\n  int result = pthread_cond_destroy(&cv);\n  if (result != 0)\n    {\n      fprintf(stderr, \"Result = %s\\n\", error_string[result]);\n\tfprintf(stderr, \"\\tWaitersBlocked = %ld\\n\", cv->nWaitersBlocked);\n\tfprintf(stderr, \"\\tWaitersGone = %ld\\n\", cv->nWaitersGone);\n\tfprintf(stderr, \"\\tWaitersToUnblock = %ld\\n\", cv->nWaitersToUnblock);\n\tfflush(stderr);\n    }\n  assert(result == 0);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar3.c",
    "content": "/*\n * File: condvar3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test basic function of a CV\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - The primary thread takes the lock before creating any threads.\n *   The secondary thread blocks on the lock allowing the primary\n *   thread to enter the cv wait state which releases the lock.\n *   The secondary thread then takes the lock and signals the waiting\n *   primary thread.\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - pthread_cond_timedwait returns 0.\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - pthread_cond_timedwait returns ETIMEDOUT.\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\nstatic pthread_cond_t cv;\nstatic pthread_mutex_t mutex;\nstatic int shared = 0;\n\nenum {\n  NUMTHREADS = 2         /* Including the primary thread. */\n};\n\nvoid *\nmythread(void * arg)\n{\n  int result = 0;\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n  shared++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  if ((result = pthread_cond_signal(&cv)) != 0)\n    {\n      printf(\"Error = %s\\n\", error_string[result]);\n    }\n  assert(result == 0);\n\n\n  return (void *) 0;\n}\n\nint\nmain()\n{\n  pthread_t t[NUMTHREADS];\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  assert(pthread_cond_init(&cv, NULL) == 0);\n\n  assert(pthread_mutex_init(&mutex, NULL) == 0);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  /* get current system time */\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  assert(pthread_create(&t[1], NULL, mythread, (void *) 1) == 0);\n\n  abstime.tv_sec += 5;\n\n  while (! (shared > 0))\n    assert(pthread_cond_timedwait(&cv, &mutex, &abstime) == 0);\n\n  assert(shared > 0);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  assert(pthread_join(t[1], NULL) == 0);\n\n  assert(pthread_cond_destroy(&cv) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar3_1.c",
    "content": "/*\n * File: condvar3_1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test timeout of multiple waits on a CV with some signaled.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Because some CVs are never signaled, we expect their waits to time out.\n *   Some are signaled, the rest time out. Pthread_cond_destroy() will fail\n *   unless all are accounted for, either signaled or timedout.\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - pthread_cond_timedwait returns ETIMEDOUT.\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - pthread_cond_timedwait does not return ETIMEDOUT.\n * - Process returns non-zero exit status.\n */\n\n#define _WIN32_WINNT 0x400\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\nstatic pthread_cond_t cv;\nstatic pthread_cond_t cv1;\nstatic pthread_mutex_t mutex;\nstatic pthread_mutex_t mutex1;\nstatic struct timespec abstime = { 0, 0 };\nstatic int timedout = 0;\nstatic int signaled = 0;\nstatic int awoken = 0;\nstatic int waiting = 0;\n\nenum {\n  NUMTHREADS = 30\n};\n\nvoid *\nmythread(void * arg)\n{\n  int result;\n\n  assert(pthread_mutex_lock(&mutex1) == 0);\n  ++waiting;\n  assert(pthread_mutex_unlock(&mutex1) == 0);\n  assert(pthread_cond_signal(&cv1) == 0);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n  result = pthread_cond_timedwait(&cv, &mutex, &abstime);\n  if (result == ETIMEDOUT)\n    {\n      timedout++;\n    }\n  else\n    {\n      awoken++;\n    }\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return arg;\n}\n\n#include \"../implement.h\"\n\nint\nmain()\n{\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n  void* result = (void*)0;\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  assert(pthread_cond_init(&cv, NULL) == 0);\n  assert(pthread_cond_init(&cv1, NULL) == 0);\n\n  assert(pthread_mutex_init(&mutex, NULL) == 0);\n  assert(pthread_mutex_init(&mutex1, NULL) == 0);\n\n  /* get current system time */\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 5;\n\n  assert(pthread_mutex_lock(&mutex1) == 0);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_create(&t[i], NULL, mythread, (void *)(size_t)i) == 0);\n    }\n\n  do {\n    assert(pthread_cond_wait(&cv1,&mutex1) == 0);\n  } while ( NUMTHREADS > waiting );\n\n  assert(pthread_mutex_unlock(&mutex1) == 0);\n\n  for (i = NUMTHREADS/3; i <= 2*NUMTHREADS/3; i++)\n    {\n//      assert(pthread_mutex_lock(&mutex) == 0);\n      assert(pthread_cond_signal(&cv) == 0);\n//      assert(pthread_mutex_unlock(&mutex) == 0);\n\n      signaled++;\n    }\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_join(t[i], &result) == 0);\n        assert((int)(size_t)result == i);\n    }\n\n      fprintf(stderr, \"awk = %d\\n\", awoken);\n      fprintf(stderr, \"sig = %d\\n\", signaled);\n      fprintf(stderr, \"tot = %d\\n\", timedout);\n\n  assert(signaled == awoken);\n  assert(timedout == NUMTHREADS - signaled);\n\n  assert(pthread_cond_destroy(&cv1) == 0);\n\n  {\n  int result = pthread_cond_destroy(&cv);\n  if (result != 0)\n    {\n      fprintf(stderr, \"Result = %s\\n\", error_string[result]);\n        fprintf(stderr, \"\\tWaitersBlocked = %ld\\n\", cv->nWaitersBlocked);\n        fprintf(stderr, \"\\tWaitersGone = %ld\\n\", cv->nWaitersGone);\n        fprintf(stderr, \"\\tWaitersToUnblock = %ld\\n\", cv->nWaitersToUnblock);\n        fflush(stderr);\n    }\n  assert(result == 0);\n  }\n\n  assert(pthread_mutex_destroy(&mutex1) == 0);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar3_2.c",
    "content": "/*\n * File: condvar3_2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test timeout of multiple waits on a CV with remainder broadcast awoken.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Because some CVs are never signaled, we expect their waits to time out.\n *   Some time out, the rest are broadcast signaled. Pthread_cond_destroy() will fail\n *   unless all are accounted for, either signaled or timedout.\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - pthread_cond_timedwait returns ETIMEDOUT.\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - pthread_cond_timedwait does not return ETIMEDOUT.\n * - Process returns non-zero exit status.\n */\n\n#define _WIN32_WINNT 0x400\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\nstatic pthread_cond_t cv;\nstatic pthread_mutex_t mutex;\nstatic struct timespec abstime = { 0, 0 };\nstatic struct timespec abstime2 = { 0, 0 };\nstatic int timedout = 0;\nstatic int awoken = 0;\n\nenum {\n  NUMTHREADS = 30\n};\n\nvoid *\nmythread(void * arg)\n{\n  int result;\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  abstime2.tv_sec = abstime.tv_sec;\n\n  if ((int) (size_t)arg % 3 == 0)\n    {\n      abstime2.tv_sec += 2;\n    }\n\n  result = pthread_cond_timedwait(&cv, &mutex, &abstime2);\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  if (result == ETIMEDOUT)\n    {\n      InterlockedIncrement((LPLONG)&timedout);\n    }\n  else\n    {\n      InterlockedIncrement((LPLONG)&awoken);\n    }\n\n\n  return arg;\n}\n\n#include \"../implement.h\"\n\nint\nmain()\n{\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n  void* result = (void*)0;\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  assert(pthread_cond_init(&cv, NULL) == 0);\n\n  assert(pthread_mutex_init(&mutex, NULL) == 0);\n\n  /* get current system time */\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = abstime2.tv_sec = (long)currSysTime.time + 5;\n  abstime.tv_nsec = abstime2.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_create(&t[i], NULL, mythread, (void *)(size_t)i) == 0);\n    }\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_join(t[i], &result) == 0);\n\tassert((int)(size_t)result == i);\n      /*\n       * Approximately 2/3rds of the threads are expected to time out.\n       * Signal the remainder after some threads have woken up and exited\n       * and while some are still waking up after timeout.\n       * Also tests that redundant broadcasts don't return errors.\n       */\n\n//      assert(pthread_mutex_lock(&mutex) == 0);\n\n      if (InterlockedExchangeAdd((LPLONG)&awoken, 0L) > NUMTHREADS/3)\n        {\n          assert(pthread_cond_broadcast(&cv) == 0);\n        }\n\n//      assert(pthread_mutex_unlock(&mutex) == 0);\n\n    }\n\n  assert(awoken == NUMTHREADS - timedout);\n\n  {\n  int result = pthread_cond_destroy(&cv);\n  if (result != 0)\n    {\n      fprintf(stderr, \"Result = %s\\n\", error_string[result]);\n\tfprintf(stderr, \"\\tWaitersBlocked = %ld\\n\", cv->nWaitersBlocked);\n\tfprintf(stderr, \"\\tWaitersGone = %ld\\n\", cv->nWaitersGone);\n\tfprintf(stderr, \"\\tWaitersToUnblock = %ld\\n\", cv->nWaitersToUnblock);\n\tfflush(stderr);\n    }\n  assert(result == 0);\n  }\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar3_3.c",
    "content": "/*\n * File: condvar3_3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test timeouts and lost signals on a CV.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - pthread_cond_timedwait returns ETIMEDOUT.\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - pthread_cond_timedwait does not return ETIMEDOUT.\n * - Process returns non-zero exit status.\n */\n\n/* Timur Aydin (taydin@snet.net) */\n\n#include \"test.h\"\n\n#include <sys/timeb.h>\n\npthread_cond_t cnd;\npthread_mutex_t mtx;\n\nint main()\n{\n   int rc;\n\n   struct timespec abstime = { 0, 0 };\n   PTW32_STRUCT_TIMEB currSysTime;\n   const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n   assert(pthread_cond_init(&cnd, 0) == 0);\n   assert(pthread_mutex_init(&mtx, 0) == 0);\n\n   /* get current system time */\n   PTW32_FTIME(&currSysTime);\n\n   abstime.tv_sec = (long)currSysTime.time;\n   abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n   abstime.tv_sec += 1;\n\n   /* Here pthread_cond_timedwait should time out after one second. */\n\n   assert(pthread_mutex_lock(&mtx) == 0);\n\n   assert((rc = pthread_cond_timedwait(&cnd, &mtx, &abstime)) == ETIMEDOUT);\n\n   assert(pthread_mutex_unlock(&mtx) == 0);\n\n   /* Here, the condition variable is signaled, but there are no\n      threads waiting on it. The signal should be lost and\n      the next pthread_cond_timedwait should time out too. */\n\n//   assert(pthread_mutex_lock(&mtx) == 0);\n\n   assert((rc = pthread_cond_signal(&cnd)) == 0);\n\n//   assert(pthread_mutex_unlock(&mtx) == 0);\n\n   assert(pthread_mutex_lock(&mtx) == 0);\n\n   abstime.tv_sec = (long)currSysTime.time;\n   abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n   abstime.tv_sec += 1;\n\n   assert((rc = pthread_cond_timedwait(&cnd, &mtx, &abstime)) == ETIMEDOUT);\n\n   assert(pthread_mutex_unlock(&mtx) == 0);\n\n   return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar4.c",
    "content": "/*\n * File: condvar4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test PTHREAD_COND_INITIALIZER.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Test basic CV function but starting with a static initialised\n *   CV.\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - pthread_cond_timedwait returns 0.\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - pthread_cond_timedwait returns ETIMEDOUT.\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\ntypedef struct cvthing_t_ cvthing_t;\n\nstruct cvthing_t_ {\n  pthread_cond_t notbusy;\n  pthread_mutex_t lock;\n  int shared;\n};\n\nstatic cvthing_t cvthing = {\n  PTHREAD_COND_INITIALIZER,\n  PTHREAD_MUTEX_INITIALIZER,\n  0\n};\n\nenum {\n  NUMTHREADS = 2\n};\n\nvoid *\nmythread(void * arg)\n{\n  assert(pthread_mutex_lock(&cvthing.lock) == 0);\n  cvthing.shared++;\n  assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n  assert(pthread_cond_signal(&cvthing.notbusy) == 0);\n\n  return (void *) 0;\n}\n\nint\nmain()\n{\n  pthread_t t[NUMTHREADS];\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  cvthing.shared = 0;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);\n\n  assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);\n\n  assert(pthread_mutex_lock(&cvthing.lock) == 0);\n\n  assert(cvthing.lock != PTHREAD_MUTEX_INITIALIZER);\n\n  /* get current system time */\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 5;\n\n  assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == ETIMEDOUT);\n  \n  assert(cvthing.notbusy != PTHREAD_COND_INITIALIZER);\n\n  assert(pthread_create(&t[1], NULL, mythread, (void *) 1) == 0);\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 5;\n\n  while (! (cvthing.shared > 0))\n    assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);\n\n  assert(cvthing.shared > 0);\n\n  assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n  assert(pthread_join(t[1], NULL) == 0);\n\n  assert(pthread_mutex_destroy(&cvthing.lock) == 0);\n\n  assert(cvthing.lock == NULL);\n\n  assert(pthread_cond_destroy(&cvthing.notbusy) == 0);\n\n  assert(cvthing.notbusy == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar5.c",
    "content": "/*\n * File: condvar5.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test pthread_cond_broadcast.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Test broadcast with one waiting CV.\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - pthread_cond_timedwait returns 0.\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - pthread_cond_timedwait returns ETIMEDOUT.\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\ntypedef struct cvthing_t_ cvthing_t;\n\nstruct cvthing_t_ {\n  pthread_cond_t notbusy;\n  pthread_mutex_t lock;\n  int shared;\n};\n\nstatic cvthing_t cvthing = {\n  PTHREAD_COND_INITIALIZER,\n  PTHREAD_MUTEX_INITIALIZER,\n  0\n};\n\nenum {\n  NUMTHREADS = 2\n};\n\nvoid *\nmythread(void * arg)\n{\n  assert(pthread_mutex_lock(&cvthing.lock) == 0);\n  cvthing.shared++;\n  assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n  assert(pthread_cond_broadcast(&cvthing.notbusy) == 0);\n\n  return (void *) 0;\n}\n\nint\nmain()\n{\n  pthread_t t[NUMTHREADS];\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  cvthing.shared = 0;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);\n\n  assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);\n\n  assert(pthread_mutex_lock(&cvthing.lock) == 0);\n\n  assert(cvthing.lock != PTHREAD_MUTEX_INITIALIZER);\n\n  /* get current system time */\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 5;\n\n  assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == ETIMEDOUT);\n  \n  assert(cvthing.notbusy != PTHREAD_COND_INITIALIZER);\n\n  assert(pthread_create(&t[1], NULL, mythread, (void *) 1) == 0);\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 5;\n\n  while (! (cvthing.shared > 0))\n    assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);\n\n  assert(cvthing.shared > 0);\n\n  assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n  assert(pthread_join(t[1], NULL) == 0);\n\n  assert(pthread_mutex_destroy(&cvthing.lock) == 0);\n\n  assert(cvthing.lock == NULL);\n\n  assert(pthread_cond_destroy(&cvthing.notbusy) == 0);\n\n  assert(cvthing.notbusy == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar6.c",
    "content": "/*\n * File: condvar6.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test pthread_cond_broadcast.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Test broadcast with NUMTHREADS (=5) waiting CVs.\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 5\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\ntypedef struct cvthing_t_ cvthing_t;\n\nstruct cvthing_t_ {\n  pthread_cond_t notbusy;\n  pthread_mutex_t lock;\n  int shared;\n};\n\nstatic cvthing_t cvthing = {\n  PTHREAD_COND_INITIALIZER,\n  PTHREAD_MUTEX_INITIALIZER,\n  0\n};\n\nstatic pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER;\n\nstatic struct timespec abstime = { 0, 0 };\n\nstatic int awoken;\n\nvoid *\nmythread(void * arg)\n{\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* Wait for the start gun */\n  assert(pthread_mutex_lock(&start_flag) == 0);\n  assert(pthread_mutex_unlock(&start_flag) == 0);\n\n  assert(pthread_mutex_lock(&cvthing.lock) == 0);\n\n  while (! (cvthing.shared > 0))\n    assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);\n\n  assert(cvthing.shared > 0);\n\n  awoken++;\n\n  assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n  return (void *) 0;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  cvthing.shared = 0;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);\n\n  assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);\n\n  assert(pthread_mutex_lock(&start_flag) == 0);\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 5;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  awoken = 0;\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n\n  assert(pthread_mutex_unlock(&start_flag) == 0);\n\n  /*\n   * Give threads time to start.\n   */\n  Sleep(1000);\n\n  assert(pthread_mutex_lock(&cvthing.lock) == 0);\n  cvthing.shared++;\n  assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n  assert(pthread_cond_broadcast(&cvthing.notbusy) == 0);\n\n  /*\n   * Give threads time to complete.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      assert(pthread_join(t[i], NULL) == 0);\n    }\n\n  /* \n   * Cleanup the CV.\n   */\n  \n  assert(pthread_mutex_destroy(&cvthing.lock) == 0);\n\n  assert(cvthing.lock == NULL);\n\n  assert(pthread_cond_destroy(&cvthing.notbusy) == 0);\n\n  assert(cvthing.notbusy == NULL);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      failed = !threadbag[i].started;\n\n      if (failed)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here.\n   */\n\n  assert(awoken == NUMTHREADS);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar7.c",
    "content": "/*\n * File: condvar7.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test pthread_cond_broadcast with thread cancelation.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Test broadcast with NUMTHREADS (=5) waiting CVs, one is canceled while waiting.\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 5\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\ntypedef struct cvthing_t_ cvthing_t;\n\nstruct cvthing_t_ {\n  pthread_cond_t notbusy;\n  pthread_mutex_t lock;\n  int shared;\n};\n\nstatic cvthing_t cvthing = {\n  PTHREAD_COND_INITIALIZER,\n  PTHREAD_MUTEX_INITIALIZER,\n  0\n};\n\nstatic pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER;\n\nstatic struct timespec abstime = { 0, 0 };\n\nstatic int awoken;\n\nvoid *\nmythread(void * arg)\n{\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* Wait for the start gun */\n  assert(pthread_mutex_lock(&start_flag) == 0);\n  assert(pthread_mutex_unlock(&start_flag) == 0);\n\n  assert(pthread_mutex_lock(&cvthing.lock) == 0);\n\n#ifdef _MSC_VER\n#pragma inline_depth(0)\n#endif\n  pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock);\n\n  while (! (cvthing.shared > 0))\n    assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);\n\n  pthread_cleanup_pop(0);\n#ifdef _MSC_VER\n#pragma inline_depth()\n#endif\n\n  assert(cvthing.shared > 0);\n\n  awoken++;\n\n  assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n  return (void *) 0;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  cvthing.shared = 0;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);\n\n  assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);\n\n  assert(pthread_mutex_lock(&start_flag) == 0);\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (time_t)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 10;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  awoken = 0;\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n\n  assert(pthread_mutex_unlock(&start_flag) == 0);\n\n  /*\n   * Give threads time to start.\n   */\n  Sleep(1000);\n\n  /*\n   * Cancel one of the threads.\n   */\n  assert(pthread_cancel(t[1]) == 0);\n  assert(pthread_join(t[1], NULL) == 0);\n\n  assert(pthread_mutex_lock(&cvthing.lock) == 0);\n  cvthing.shared++;\n  assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n  /*\n   * Signal all remaining waiting threads.\n   */\n  assert(pthread_cond_broadcast(&cvthing.notbusy) == 0);\n\n  /*\n   * Wait for all threads to complete.\n   */\n  for (i = 2; i <= NUMTHREADS; i++)\n    assert(pthread_join(t[i], NULL) == 0);\n\n  /* \n   * Cleanup the CV.\n   */\n  \n  assert(pthread_mutex_destroy(&cvthing.lock) == 0);\n\n  assert(cvthing.lock == NULL);\n\n  assert(pthread_cond_destroy(&cvthing.notbusy) == 0);\n\n  assert(cvthing.notbusy == NULL);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      failed = !threadbag[i].started;\n\n      if (failed)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here.\n   */\n\n  assert(awoken == (NUMTHREADS - 1));\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar8.c",
    "content": "/*\n * File: condvar8.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test multiple pthread_cond_broadcasts.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Make NUMTHREADS threads wait on CV, broadcast signal them, and then repeat.\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 5\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\ntypedef struct cvthing_t_ cvthing_t;\n\nstruct cvthing_t_ {\n  pthread_cond_t notbusy;\n  pthread_mutex_t lock;\n  int shared;\n};\n\nstatic cvthing_t cvthing = {\n  PTHREAD_COND_INITIALIZER,\n  PTHREAD_MUTEX_INITIALIZER,\n  0\n};\n\nstatic pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER;\n\nstatic struct timespec abstime = { 0, 0 };\n\nstatic int awoken;\n\nstatic void *\nmythread(void * arg)\n{\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* Wait for the start gun */\n  assert(pthread_mutex_lock(&start_flag) == 0);\n  assert(pthread_mutex_unlock(&start_flag) == 0);\n\n  assert(pthread_mutex_lock(&cvthing.lock) == 0);\n\n#ifdef _MSC_VER\n#pragma inline_depth(0)\n#endif\n  pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock);\n\n  while (! (cvthing.shared > 0))\n    assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);\n\n  pthread_cleanup_pop(0);\n#ifdef _MSC_VER\n#pragma inline_depth()\n#endif\n\n  assert(cvthing.shared > 0);\n\n  awoken++;\n\n  assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n  return (void *) 0;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  int first, last;\n  pthread_t t[NUMTHREADS + 1];\n\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);\n\n  assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 10;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  awoken = 0;\n\n  for (first = 1, last = NUMTHREADS / 2;\n       first < NUMTHREADS;\n       first = last + 1, last = NUMTHREADS)\n    {\n      assert(pthread_mutex_lock(&start_flag) == 0);\n\n      for (i = first; i <= last; i++)\n\t{\n\t  threadbag[i].started = 0;\n\t  threadbag[i].threadnum = i;\n\t  assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n\t}\n\n      /*\n       * Code to control or munipulate child threads should probably go here.\n       */\n      cvthing.shared = 0;\n\n      assert(pthread_mutex_unlock(&start_flag) == 0);\n\n      /*\n       * Give threads time to start.\n       */\n      Sleep(100);\n\n      assert(pthread_mutex_lock(&cvthing.lock) == 0);\n      cvthing.shared++;\n      assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n      assert(pthread_cond_broadcast(&cvthing.notbusy) == 0);\n\n      /*\n       * Give threads time to complete.\n       */\n      for (i = first; i <= last; i++)\n\t{\n\t  assert(pthread_join(t[i], NULL) == 0);\n\t}\n\n      assert(awoken == (i - 1));\n    }\n\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      failed = !threadbag[i].started;\n\n      if (failed)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  /* \n   * Cleanup the CV.\n   */\n  \n  assert(pthread_mutex_destroy(&cvthing.lock) == 0);\n\n  assert(cvthing.lock == NULL);\n\n  assert(pthread_cond_destroy(&cvthing.notbusy) == 0);\n\n  assert(cvthing.notbusy == NULL);\n\n  assert(!failed);\n\n  /*\n   * Check any results here.\n   */\n\n  assert(awoken == NUMTHREADS);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/condvar9.c",
    "content": "/*\n * File: condvar9.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test multiple pthread_cond_broadcasts with thread cancelation.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - Make NUMTHREADS threads wait on CV, cancel one, broadcast signal them,\n *   and then repeat.\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 9\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  int finished;\n  /* Add more per-thread state variables here */\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\ntypedef struct cvthing_t_ cvthing_t;\n\nstruct cvthing_t_ {\n  pthread_cond_t notbusy;\n  pthread_mutex_t lock;\n  int shared;\n};\n\nstatic cvthing_t cvthing = {\n  PTHREAD_COND_INITIALIZER,\n  PTHREAD_MUTEX_INITIALIZER,\n  0\n};\n\nstatic pthread_mutex_t start_flag = PTHREAD_MUTEX_INITIALIZER;\n\nstatic struct timespec abstime = { 0, 0 };\n\nstatic int awoken;\n\nstatic void *\nmythread(void * arg)\n{\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /* Wait for the start gun */\n  assert(pthread_mutex_lock(&start_flag) == 0);\n  assert(pthread_mutex_unlock(&start_flag) == 0);\n\n  assert(pthread_mutex_lock(&cvthing.lock) == 0);\n\n  /*\n   * pthread_cond_timedwait is a cancelation point and we're\n   * going to cancel some threads deliberately.\n   */\n#ifdef _MSC_VER\n#pragma inline_depth(0)\n#endif\n  pthread_cleanup_push(pthread_mutex_unlock, (void *) &cvthing.lock);\n\n  while (! (cvthing.shared > 0))\n    assert(pthread_cond_timedwait(&cvthing.notbusy, &cvthing.lock, &abstime) == 0);\n\n  pthread_cleanup_pop(0);\n#ifdef _MSC_VER\n#pragma inline_depth()\n#endif\n\n  assert(cvthing.shared > 0);\n\n  awoken++;\n  bag->finished = 1;\n\n  assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n  return (void *) 0;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  int first, last;\n  int canceledThreads = 0;\n  pthread_t t[NUMTHREADS + 1];\n\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  assert(cvthing.notbusy == PTHREAD_COND_INITIALIZER);\n\n  assert(cvthing.lock == PTHREAD_MUTEX_INITIALIZER);\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 5;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  awoken = 0;\n\n  for (first = 1, last = NUMTHREADS / 2;\n       first < NUMTHREADS;\n       first = last + 1, last = NUMTHREADS)\n    {\n      int ct;\n\n      assert(pthread_mutex_lock(&start_flag) == 0);\n\n      for (i = first; i <= last; i++)\n\t{\n\t  threadbag[i].started = threadbag[i].finished = 0;\n\t  threadbag[i].threadnum = i;\n\t  assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n\t}\n\n      /*\n       * Code to control or munipulate child threads should probably go here.\n       */\n      cvthing.shared = 0;\n\n      assert(pthread_mutex_unlock(&start_flag) == 0);\n\n      /*\n       * Give threads time to start.\n       */\n      Sleep(1000);\n\n      ct = (first + last) / 2;\n      assert(pthread_cancel(t[ct]) == 0);\n      canceledThreads++;\n      assert(pthread_join(t[ct], NULL) == 0);\n\n      assert(pthread_mutex_lock(&cvthing.lock) == 0);\n      cvthing.shared++;\n      assert(pthread_mutex_unlock(&cvthing.lock) == 0);\n\n      assert(pthread_cond_broadcast(&cvthing.notbusy) == 0);\n\n      /*\n       * Standard check that all threads started - and wait for them to finish.\n       */\n      for (i = first; i <= last; i++)\n\t{ \n\t  failed = !threadbag[i].started;\n\n          if (failed)\n\t    {\n\t      fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t    }\n\t  else\n\t    {\n\t      assert(pthread_join(t[i], NULL) == 0 || threadbag[i].finished == 0);\n//\t      fprintf(stderr, \"Thread %d: finished %d\\n\", i, threadbag[i].finished);\n\t    }\n\t}\n    }\n\n  /* \n   * Cleanup the CV.\n   */\n\n  assert(pthread_mutex_destroy(&cvthing.lock) == 0);\n\n  assert(cvthing.lock == NULL);\n\n  assert_e(pthread_cond_destroy(&cvthing.notbusy), ==, 0);\n\n  assert(cvthing.notbusy == NULL);\n\n  assert(!failed);\n\n  /*\n   * Check any results here.\n   */\n\n  assert(awoken == NUMTHREADS - canceledThreads);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/context1.c",
    "content": "/*\n * File: context1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test context switching method.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - pthread_create\n *   pthread_exit\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#define _WIN32_WINNT 0x400\n\n#include \"test.h\"\n#include \"../implement.h\"\n#include \"../context.h\"\n\nstatic int washere = 0;\n\nstatic void * func(void * arg)\n{\n  washere = 1;\n\n  Sleep(1000);\n\n  return 0; \n}\n\nstatic void\nanotherEnding ()\n{\n  /*\n   * Switched context\n   */\n  washere++;\n\n  pthread_exit(0);\n}\n\nint\nmain()\n{\n  pthread_t t;\n  HANDLE hThread;\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n\n  hThread = ((ptw32_thread_t *)t.p)->threadH;\n\n  Sleep(500);\n\n  SuspendThread(hThread);\n\n  if (WaitForSingleObject(hThread, 0) == WAIT_TIMEOUT) \n    {\n      /*\n       * Ok, thread did not exit before we got to it.\n       */\n      CONTEXT context;\n\n      context.ContextFlags = CONTEXT_CONTROL;\n\n      GetThreadContext(hThread, &context);\n      PTW32_PROGCTR (context) = (DWORD_PTR) anotherEnding;\n      SetThreadContext(hThread, &context);\n      ResumeThread(hThread);\n    }\n  else\n    {\n      printf(\"Exited early\\n\");\n      fflush(stdout);\n    }\n\n  Sleep(1000);\n\n  assert(washere == 2);\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/count1.c",
    "content": "/*\n * count1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Description:\n * Test some basic assertions about the number of threads at runtime.\n */\n\n#include \"test.h\"\n\n#define NUMTHREADS (30)\n\nstatic pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;\nstatic pthread_t threads[NUMTHREADS];\nstatic unsigned numThreads = 0;\n\nvoid *\nmyfunc(void *arg)\n{\n  pthread_mutex_lock(&lock);\n  numThreads++;\n  pthread_mutex_unlock(&lock);\n\n  Sleep(1000);\n  return 0;\n}\nint\nmain()\n{\n  int i;\n  int maxThreads = sizeof(threads) / sizeof(pthread_t);\n\n  /*\n   * Spawn NUMTHREADS threads. Each thread should increment the\n   * numThreads variable, sleep for one second.\n   */\n  for (i = 0; i < maxThreads; i++)\n    {\n      assert(pthread_create(&threads[i], NULL, myfunc, 0) == 0);\n    }\n  \n  /*\n   * Wait for all the threads to exit.\n   */\n  for (i = 0; i < maxThreads; i++)\n    {\n      assert(pthread_join(threads[i], NULL) == 0);\n    }\n\n  /* \n   * Check the number of threads created.\n   */\n  assert((int) numThreads == maxThreads);\n  \n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/create1.c",
    "content": "/*\n * create1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Description:\n * Create a thread and check that it ran.\n *\n * Depends on API functions: None.\n */\n\n#include \"test.h\"\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  washere = 1;\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n\n  /* A dirty hack, but we cannot rely on pthread_join in this\n     primitive test. */\n  Sleep(2000);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/create2.c",
    "content": "/*\n * File: create2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test that threads have a Win32 handle when started.\n *\n * Test Method (Validation or Falsification):\n * - Statistical, not absolute (depends on sample size).\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\nenum {\n  NUMTHREADS = 10000\n};\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  washere = 1;\n  return (void *) 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n  pthread_attr_t attr;\n  void * result = NULL;\n  int i;\n\n  pthread_attr_init(&attr);\n  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);\n\n  for (i = 0; i < NUMTHREADS; i++)\n    {\n      washere = 0;\n      assert(pthread_create(&t, &attr, func, NULL) == 0);\n      assert(pthread_join(t, &result) == 0);\n      assert((int)(size_t)result == 0);\n      assert(washere == 1);\n    }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/create3.c",
    "content": "/*\n * File: create3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test passing arg to thread function.\n *\n * Test Method (Validation or Falsification):\n * - Statistical, not absolute (depends on sample size).\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\nenum {\n  NUMTHREADS = 10000\n};\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  washere = (int)(size_t)arg;\n  return (void *) 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n  pthread_attr_t attr;\n  void * result = NULL;\n  int i;\n\n  pthread_attr_init(&attr);\n  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);\n\n  for (i = 0; i < NUMTHREADS; i++)\n    {\n      washere = 0;\n      assert(pthread_create(&t, &attr, func, (void *)(size_t)1) == 0);\n      assert(pthread_join(t, &result) == 0);\n      assert((int)(size_t)result == 0);\n      assert(washere == 1);\n    }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/delay1.c",
    "content": "/*\n * delay1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on API functions:\n *    pthread_delay_np\n */\n\n#include \"test.h\"\n\nint\nmain(int argc, char * argv[])\n{\n  struct timespec interval = {1L, 500000000L};\n\n  assert(pthread_delay_np(&interval) == 0);\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/delay2.c",
    "content": "/*\n * delay1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on API functions:\n *    pthread_delay_np\n */\n\n#include \"test.h\"\n\npthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER;\n\nvoid *\nfunc(void * arg)\n{\n  struct timespec interval = {5, 500000000L};\n\n  assert(pthread_mutex_lock(&mx) == 0);\n\n#ifdef _MSC_VER\n#pragma inline_depth(0)\n#endif\n  pthread_cleanup_push(pthread_mutex_unlock, &mx);\n  assert(pthread_delay_np(&interval) == 0);\n  pthread_cleanup_pop(1);\n#ifdef _MSC_VER\n#pragma inline_depth()\n#endif\n\n  return (void *)(size_t)1;\n}\n\nint\nmain(int argc, char * argv[])\n{\n  pthread_t t;\n  void* result = (void*)0;\n\n  assert(pthread_mutex_lock(&mx) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n  assert(pthread_cancel(t) == 0);\n\n  assert(pthread_mutex_unlock(&mx) == 0);\n\n  assert(pthread_join(t, &result) == 0);\n  assert(result == (void*)PTHREAD_CANCELED);\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/detach1.c",
    "content": "/*\n * Test for pthread_detach().\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on API functions: pthread_create(), pthread_detach(), pthread_exit().\n */\n\n#include \"test.h\"\n\n\nenum {\n  NUMTHREADS = 100\n};\n\nvoid *\nfunc(void * arg)\n{\n    int i = (int)(size_t)arg;\n\n    Sleep(i * 10);\n\n    pthread_exit(arg);\n\n    /* Never reached. */\n    exit(1);\n}\n\nint\nmain(int argc, char * argv[])\n{\n\tpthread_t id[NUMTHREADS];\n\tint i;\n\n\t/* Create a few threads and then exit. */\n\tfor (i = 0; i < NUMTHREADS; i++)\n\t  {\n\t    assert(pthread_create(&id[i], NULL, func, (void *)(size_t)i) == 0);\n\t  }\n\n\t/* Some threads will finish before they are detached, some after. */\n\tSleep(NUMTHREADS/2 * 10 + 50);\n\n\tfor (i = 0; i < NUMTHREADS; i++)\n\t  {\n\t    assert(pthread_detach(id[i]) == 0);\n\t  }\n\n\tSleep(NUMTHREADS * 10 + 100);\n\n\t/*\n\t * Check that all threads are now invalid.\n\t * This relies on unique thread IDs - e.g. works with\n\t * pthreads-w32 or Solaris, but may not work for Linux, BSD etc.\n\t */\n\tfor (i = 0; i < NUMTHREADS; i++)\n\t  {\n\t    assert(pthread_kill(id[i], 0) == ESRCH);\n\t  }\n\n\t/* Success. */\n\treturn 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/equal1.c",
    "content": "/*\n * Test for pthread_equal.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on functions: pthread_create().\n */\n\n#include \"test.h\"\n\nvoid * func(void * arg)\n{\n  Sleep(2000);\n  return 0;\n}\n\nint \nmain()\n{\n  pthread_t t1, t2;\n\n  assert(pthread_create(&t1, NULL, func, (void *) 1) == 0);\n\n  assert(pthread_create(&t2, NULL, func, (void *) 2) == 0);\n\n  assert(pthread_equal(t1, t2) == 0);\n\n  assert(pthread_equal(t1,t1) != 0);\n\n  /* This is a hack. We don't want to rely on pthread_join\n     yet if we can help it. */\n   Sleep(4000);\n\n  /* Success. */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/errno1.c",
    "content": "/*\n * File: errno1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test thread-safety of errno\n * - \n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 3\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\npthread_mutex_t stop_here = PTHREAD_MUTEX_INITIALIZER;\n\nvoid *\nmythread(void * arg)\n{\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  errno = bag->threadnum;\n\n  Sleep(1000);\n\n  pthread_mutex_lock(&stop_here);\n\n  assert(errno == bag->threadnum);\n\n  pthread_mutex_unlock(&stop_here);\n\n  Sleep(1000);\n\n  return 0;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t t[NUMTHREADS + 1];\n\n  pthread_mutex_lock(&stop_here);\n  errno = 0;\n\n  assert((t[0] = pthread_self()).p != NULL);\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n      assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(2000);\n  pthread_mutex_unlock(&stop_here);\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 1000);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      failed = !threadbag[i].started;\n\n      if (failed)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print ouput on failure.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      /* ... */\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/exception1.c",
    "content": "/*\n * File: exception1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test passing of exceptions back to the application.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel, pthread_join\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#if defined(_MSC_VER) || defined(__cplusplus)\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 4\n};\n\nvoid *\nexceptionedThread(void * arg)\n{\n  int dummy = 0;\n  void* result = (void*)((int)(size_t)PTHREAD_CANCELED + 1);\n  /* Set to async cancelable */\n\n  assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);\n\n  assert(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);\n\n  Sleep(100);\n\n#if defined(_MSC_VER) && !defined(__cplusplus)\n  __try\n  {\n    int zero = (int) (size_t)arg; /* Passed in from arg to avoid compiler error */\n    int one = 1;\n    /*\n     * The deliberate exception condition (zero divide) is\n     * in an \"if\" to avoid being optimised out.\n     */\n    if (dummy == one/zero)\n      Sleep(0);\n  }\n  __except (EXCEPTION_EXECUTE_HANDLER)\n  {\n    /* Should get into here. */\n    result = (void*)((int)(size_t)PTHREAD_CANCELED + 2);\n  }\n#elif defined(__cplusplus)\n  try\n  {\n    /*\n     * I had a zero divide exception here but it\n     * wasn't being caught by the catch(...)\n     * below under Mingw32. That could be a problem.\n     */\n    throw dummy;\n  }\n#if defined(PtW32CatchAll)\n  PtW32CatchAll\n#else\n  catch (...)\n#endif\n  {\n    /* Should get into here. */\n    result = (void*)((int)(size_t)PTHREAD_CANCELED + 2);\n  }\n#endif\n\n  return (void *) (size_t)result;\n}\n\nvoid *\ncanceledThread(void * arg)\n{\n  void* result = (void*)((int)(size_t)PTHREAD_CANCELED + 1);\n  int count;\n\n  /* Set to async cancelable */\n\n  assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);\n\n  assert(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);\n\n#if defined(_MSC_VER) && !defined(__cplusplus)\n  __try\n  {\n    /*\n     * We wait up to 10 seconds, waking every 0.1 seconds,\n     * for a cancelation to be applied to us.\n     */\n    for (count = 0; count < 100; count++)\n      Sleep(100);\n  }\n  __except (EXCEPTION_EXECUTE_HANDLER)\n  {\n    /* Should NOT get into here. */\n    result = (void*)((int)(size_t)PTHREAD_CANCELED + 2);\n  }\n#elif defined(__cplusplus)\n  try\n  {\n    /*\n     * We wait up to 10 seconds, waking every 0.1 seconds,\n     * for a cancelation to be applied to us.\n     */\n    for (count = 0; count < 100; count++)\n      Sleep(100);\n  }\n#if defined(PtW32CatchAll)\n  PtW32CatchAll\n#else\n  catch (...)\n#endif\n  {\n    /* Should NOT get into here. */\n    result = (void*)((int)(size_t)PTHREAD_CANCELED + 2);\n  }\n#endif\n\n  return (void *) (size_t)result;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  pthread_t mt;\n  pthread_t et[NUMTHREADS];\n  pthread_t ct[NUMTHREADS];\n\n  assert((mt = pthread_self()).p != NULL);\n\n  for (i = 0; i < NUMTHREADS; i++)\n    {\n      assert(pthread_create(&et[i], NULL, exceptionedThread, (void *) 0) == 0);\n      assert(pthread_create(&ct[i], NULL, canceledThread, NULL) == 0);\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(1000);\n\n  for (i = 0; i < NUMTHREADS; i++)\n    {\n      assert(pthread_cancel(ct[i]) == 0);\n    }\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 1000);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 0; i < NUMTHREADS; i++)\n    {\n      int fail = 0;\n      void* result = (void*)0;\n\n\t/* Canceled thread */\n      assert(pthread_join(ct[i], &result) == 0);\n      assert(!(fail = (result != PTHREAD_CANCELED)));\n\n      failed = (failed || fail);\n\n      /* Exceptioned thread */\n      assert(pthread_join(et[i], &result) == 0);\n      assert(!(fail = (result != (void*)((int)(size_t)PTHREAD_CANCELED + 2))));\n\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n#else /* defined(_MSC_VER) || defined(__cplusplus) */\n\n#include <stdio.h>\n\nint\nmain()\n{\n  fprintf(stderr, \"Test N/A for this compiler environment.\\n\");\n  return 0;\n}\n\n#endif /* defined(_MSC_VER) || defined(__cplusplus) */\n"
  },
  {
    "path": "deps/w32-pthreads/tests/exception2.c",
    "content": "/*\n * File: exception2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test passing of exceptions out of thread scope.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n\n#if defined(_MSC_VER) || defined(__cplusplus)\n\n#if defined(_MSC_VER) && defined(__cplusplus)\n#include <eh.h>\n#elif defined(__cplusplus)\n#include <exception>\n#endif\n\n#ifdef __GNUC__\n#include <stdlib.h>\n#endif\n\n#include \"test.h\"\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 1\n};\n\n\nvoid *\nexceptionedThread(void * arg)\n{\n  int dummy = 0x1;\n\n#if defined(_MSC_VER) && !defined(__cplusplus)\n\n  RaiseException(dummy, 0, 0, NULL);\n\n#elif defined(__cplusplus)\n\n  throw dummy;\n\n#endif\n\n  return (void *) 100;\n}\n\nint\nmain(int argc, char* argv[])\n{\n  int i;\n  pthread_t mt;\n  pthread_t et[NUMTHREADS];\n\n  if (argc <= 1)\n    {\n      int result;\n\n      printf(\"You should see an \\\"abnormal termination\\\" message\\n\");\n      fflush(stdout);\n      result = system(\"exception2.exe die\");\n      exit(0);\n    }\n\n  assert((mt = pthread_self()).p != NULL);\n\n  for (i = 0; i < NUMTHREADS; i++)\n    {\n      assert(pthread_create(&et[i], NULL, exceptionedThread, NULL) == 0);\n    }\n\n  Sleep(1000);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n#else /* defined(_MSC_VER) || defined(__cplusplus) */\n\n#include <stdio.h>\n\nint\nmain()\n{\n  fprintf(stderr, \"Test N/A for this compiler environment.\\n\");\n  return 0;\n}\n\n#endif /* defined(_MSC_VER) || defined(__cplusplus) */\n"
  },
  {
    "path": "deps/w32-pthreads/tests/exception3.c",
    "content": "/*\n * File: exception3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test running of user supplied terminate() function.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n#if defined(__cplusplus)\n\n#if defined(_MSC_VER)\n# include <eh.h>\n#else\n# if defined(__GNUC__) && __GNUC__ < 3\n#   include <new.h>\n# else\n#   include <new>\n    using std::set_terminate;\n# endif\n#endif\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 1\n};\n\nint caught = 0;\npthread_mutex_t caughtLock;\n\nvoid\nterminateFunction ()\n{\n  assert(pthread_mutex_lock(&caughtLock) == 0);\n  caught++;\n#if 0\n  {\n     FILE * fp = fopen(\"pthread.log\", \"a\");\n     fprintf(fp, \"Caught = %d\\n\", caught);\n     fclose(fp);\n  }\n#endif\n  assert(pthread_mutex_unlock(&caughtLock) == 0);\n\n  /*\n   * Notes from the MSVC++ manual:\n   *       1) A term_func() should call exit(), otherwise\n   *          abort() will be called on return to the caller.\n   *          abort() raises SIGABRT. The default signal handler\n   *          for all signals terminates the calling program with\n   *          exit code 3.\n   *       2) A term_func() must not throw an exception. Dev: Therefore\n   *          term_func() should not call pthread_exit() if an\n   *          exception-using version of pthreads-win32 library\n   *          is being used (i.e. either pthreadVCE or pthreadVSE).\n   */\n  exit(0);\n}\n\nvoid *\nexceptionedThread(void * arg)\n{\n  int dummy = 0x1;\n\n  (void) set_terminate(&terminateFunction);\n\n  throw dummy;\n\n  return (void *) 0;\n}\n\nint\nmain()\n{\n  int i;\n  pthread_t mt;\n  pthread_t et[NUMTHREADS];\n  pthread_mutexattr_t ma;\n\n  assert((mt = pthread_self()).p != NULL);\n\n  printf(\"See the notes inside of exception3.c re term_funcs.\\n\");\n\n  assert(pthread_mutexattr_init(&ma) == 0);\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK) == 0);\n  assert(pthread_mutex_init(&caughtLock, &ma) == 0);\n  assert(pthread_mutexattr_destroy(&ma) == 0);\n\n  for (i = 0; i < NUMTHREADS; i++)\n    {\n      assert(pthread_create(&et[i], NULL, exceptionedThread, NULL) == 0);\n    }\n\n  Sleep(NUMTHREADS * 100);\n\n  assert(caught == NUMTHREADS);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n#else /* defined(__cplusplus) */\n\n#include <stdio.h>\n\nint\nmain()\n{\n  fprintf(stderr, \"Test N/A for this compiler environment.\\n\");\n  return 0;\n}\n\n#endif /* defined(__cplusplus) */\n"
  },
  {
    "path": "deps/w32-pthreads/tests/exit1.c",
    "content": "/*\n * Test for pthread_exit().\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on API functions: None.\n */\n\n#include \"test.h\"\n\nint\nmain(int argc, char * argv[])\n{\n\t/* A simple test first. */\n\tpthread_exit((void *) 0);\n\n\t/* Not reached */\n\tassert(0);\n\treturn 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/exit2.c",
    "content": "/*\n * Test for pthread_exit().\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on API functions:\n *\tpthread_create()\n *\tpthread_exit()\n */\n\n#include \"test.h\"\n\nvoid *\nfunc(void * arg)\n{\n\tpthread_exit(arg);\n\n\t/* Never reached. */\n\tassert(0);\n\n\treturn NULL;\n}\n\nint\nmain(int argc, char * argv[])\n{\n  pthread_t t;\n\n  assert(pthread_create(&t, NULL, func, (void *) NULL) == 0);\n\n  Sleep(100);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/exit3.c",
    "content": "/*\n * Test for pthread_exit().\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on API functions: pthread_create().\n */\n\n#include \"test.h\"\n\nvoid *\nfunc(void * arg)\n{\n\tpthread_exit(arg);\n\n\t/* Never reached. */\n\tassert(0);\n\n\treturn NULL;\n}\n\nint\nmain(int argc, char * argv[])\n{\n\tpthread_t id[4];\n\tint i;\n\n\t/* Create a few threads and then exit. */\n\tfor (i = 0; i < 4; i++)\n\t  {\n\t    assert(pthread_create(&id[i], NULL, func, (void *)(size_t)i) == 0);\n\t  }\n\n\tSleep(400);\n\n\t/* Success. */\n\treturn 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/exit4.c",
    "content": "/*\n * File: exit4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test calling pthread_exit from a Win32 thread\n *                without having created an implicit POSIX handle for it.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#ifndef _UWIN\n#include <process.h>\n#endif\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 4\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\nunsigned __stdcall\n#else\nvoid\n#endif\nWin32thread(void * arg)\n{\n  int result = 1;\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  /*\n   * Doesn't return and doesn't create an implicit POSIX handle.\n   */\n  pthread_exit((void *)(size_t)result);\n\n  return 0;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  HANDLE h[NUMTHREADS + 1];\n  unsigned thrAddr; /* Dummy variable to pass a valid location to _beginthreadex (Win98). */\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\n      h[i] = (HANDLE) _beginthreadex(NULL, 0, Win32thread, (void *) &threadbag[i], 0, &thrAddr);\n#else\n      h[i] = (HANDLE) _beginthread(Win32thread, 0, (void *) &threadbag[i]);\n#endif\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(500);\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      int result = 0;\n\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\n      assert(GetExitCodeThread(h[i], (LPDWORD) &result) == TRUE);\n#else\n      /*\n       * Can't get a result code.\n       */\n      result = 1;\n#endif\n\n      fail = (result != 1);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: count %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  threadbag[i].count);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/exit5.c",
    "content": "/*\n * File: exit5.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Test calling pthread_exit from a Win32 thread\n *                having created an implicit POSIX handle for it.\n *\n * Test Method (Validation or Falsification):\n * - Validate return value and that POSIX handle is created and destroyed.\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock\n *   pthread_testcancel, pthread_cancel\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n#ifndef _UWIN\n#include <process.h>\n#endif\n\n/*\n * Create NUMTHREADS threads in addition to the Main thread.\n */\nenum {\n  NUMTHREADS = 4\n};\n\ntypedef struct bag_t_ bag_t;\nstruct bag_t_ {\n  int threadnum;\n  int started;\n  /* Add more per-thread state variables here */\n  int count;\n  pthread_t self;\n};\n\nstatic bag_t threadbag[NUMTHREADS + 1];\n\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\nunsigned __stdcall\n#else\nvoid\n#endif\nWin32thread(void * arg)\n{\n  int result = 1;\n  bag_t * bag = (bag_t *) arg;\n\n  assert(bag == &threadbag[bag->threadnum]);\n  assert(bag->started == 0);\n  bag->started = 1;\n\n  assert((bag->self = pthread_self()).p != NULL);\n  assert(pthread_kill(bag->self, 0) == 0);\n\n  /*\n   * Doesn't return.\n   */\n  pthread_exit((void *)(size_t)result);\n\n  return 0;\n}\n\nint\nmain()\n{\n  int failed = 0;\n  int i;\n  HANDLE h[NUMTHREADS + 1];\n  unsigned thrAddr; /* Dummy variable to pass a valid location to _beginthreadex (Win98). */\n\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      threadbag[i].started = 0;\n      threadbag[i].threadnum = i;\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\n      h[i] = (HANDLE) _beginthreadex(NULL, 0, Win32thread, (void *) &threadbag[i], 0, &thrAddr);\n#else\n      h[i] = (HANDLE) _beginthread(Win32thread, 0, (void *) &threadbag[i]);\n#endif\n    }\n\n  /*\n   * Code to control or munipulate child threads should probably go here.\n   */\n  Sleep(500);\n\n  /*\n   * Give threads time to run.\n   */\n  Sleep(NUMTHREADS * 100);\n\n  /*\n   * Standard check that all threads started.\n   */\n  for (i = 1; i <= NUMTHREADS; i++)\n    { \n      if (!threadbag[i].started)\n\t{\n\t  failed |= !threadbag[i].started;\n\t  fprintf(stderr, \"Thread %d: started %d\\n\", i, threadbag[i].started);\n\t}\n    }\n\n  assert(!failed);\n\n  /*\n   * Check any results here. Set \"failed\" and only print output on failure.\n   */\n  failed = 0;\n  for (i = 1; i <= NUMTHREADS; i++)\n    {\n      int fail = 0;\n      int result = 0;\n\n#if ! defined (__MINGW32__) || defined (__MSVCRT__)\n      assert(GetExitCodeThread(h[i], (LPDWORD) &result) == TRUE);\n#else\n      /*\n       * Can't get a result code.\n       */\n      result = 1;\n#endif\n\n      assert(threadbag[i].self.p != NULL && pthread_kill(threadbag[i].self, 0) == ESRCH);\n\n      fail = (result != 1);\n\n      if (fail)\n\t{\n\t  fprintf(stderr, \"Thread %d: started %d: count %d\\n\",\n\t\t  i,\n\t\t  threadbag[i].started,\n\t\t  threadbag[i].count);\n\t}\n      failed = (failed || fail);\n    }\n\n  assert(!failed);\n\n  /*\n   * Success.\n   */\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/eyal1.c",
    "content": "/* Simple POSIX threads program.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Author: Eyal Lebedinsky eyal@eyal.emu.id.au\n * Written: Sep 1998.\n * Version Date: 12 Sep 1998\n *\n * Do we need to lock stdout or is it thread safe?\n *\n * Used:\n *\tpthread_t\n *\tpthread_attr_t\n *\tpthread_create()\n *\tpthread_join()\n *\tpthread_mutex_t\n *\tPTHREAD_MUTEX_INITIALIZER\n *\tpthread_mutex_init() [not used now]\n *\tpthread_mutex_destroy()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_trylock()\n *\tpthread_mutex_unlock()\n *\n * What this program does is establish a work queue (implemented using\n * four mutexes for each thread). It then schedules work (by storing\n * a number in 'todo') and releases the threads. When the work is done\n * the threads will block. The program then repeats the same thing once\n * more (just to test the logic) and when the work is done it destroyes\n * the threads.\n *\n * The 'work' we do is simply burning CPU cycles in a loop.\n * The 'todo' work queue is trivial - each threads pops one element\n * off it by incrementing it, the poped number is the 'work' to do.\n * When 'todo' reaches the limit (nwork) the queue is considered\n * empty.\n *\n * The number displayed at the end is the amount of work each thread\n * did, so we can see if the load was properly distributed.\n *\n * The program was written to test a threading setup (not seen here)\n * rather than to demonstrate correct usage of the pthread facilities.\n *\n * Note how each thread is given access to a thread control structure\n * (TC) which is used for communicating to/from the main program (e.g.\n * the threads knows its 'id' and also filles in the 'work' done).\n*/\n\n#include \"test.h\"\n\n#include <stdlib.h>\n#include <math.h>\n\nstruct thread_control {\n  int\t\tid;\n  pthread_t\tthread;\t\t/* thread id */\n  pthread_mutex_t\tmutex_start;\n  pthread_mutex_t\tmutex_started;\n  pthread_mutex_t\tmutex_end;\n  pthread_mutex_t\tmutex_ended;\n  long\t\twork;\t\t/* work done */\n  int\t\tstat;\t\t/* pthread_init status */\n};\n\ntypedef struct thread_control\tTC;\n\nstatic TC\t\t*tcs = NULL;\nstatic int\t\tnthreads = 10;\nstatic int\t\tnwork = 100;\nstatic int\t\tquiet = 0;\n\nstatic int\t\ttodo = -1;\n\nstatic pthread_mutex_t\tmutex_todo = PTHREAD_MUTEX_INITIALIZER;\nstatic pthread_mutex_t\tmutex_stdout = PTHREAD_MUTEX_INITIALIZER;\n\n\nstatic void\ndie (int ret)\n{\n  if (NULL != tcs)\n    {\n      free (tcs);\n      tcs = NULL;\n    }\n\n  if (ret)\n    exit (ret);\n}\n\n\nstatic double\nwaste_time (int n)\n{\n  int\t\ti;\n  double\tf, g, h, s;\n\n  s = 0.0;\n\n  /*\n   * Useless work.\n   */\n  for (i = n*100; i > 0; --i)\n    {\n      f = rand ();\n      g = rand ();\n      h = rand ();\n      s += 2.0 * f * g / (h != 0.0 ? (h * h) : 1.0);\n    }\n  return s;\n}\n\nstatic int\ndo_work_unit (int who, int n)\n{\n  int\t\ti;\n  static int\tnchars = 0;\n  double\tf = 0.0;\n\n  if (quiet)\n    i = 0;\n  else {\n    /*\n     * get lock on stdout\n     */\n    assert(pthread_mutex_lock (&mutex_stdout) == 0);\n\n    /*\n     * do our job\n     */\n    i = printf (\"%c\", \"0123456789abcdefghijklmnopqrstuvwxyz\"[who]);\n\n    if (!(++nchars % 50))\n      printf (\"\\n\");\n\n    fflush (stdout);\n\n    /*\n     * release lock on stdout\n     */\n    assert(pthread_mutex_unlock (&mutex_stdout) == 0);\n  }\n\n  n = rand () % 10000;\t/* ignore incoming 'n' */\n  f = waste_time (n);\n\n  /* This prevents the statement above from being optimised out */\n  if (f > 0.0)\n    return(n);\n\n  return (n);\n}\n\nstatic int\nprint_server (void *ptr)\n{\n  int\t\tmywork;\n  int\t\tn;\n  TC\t\t*tc = (TC *)ptr;\n\n  assert(pthread_mutex_lock (&tc->mutex_started) == 0);\n\n  for (;;)\n    {\n      assert(pthread_mutex_lock (&tc->mutex_start) == 0);\n      assert(pthread_mutex_unlock (&tc->mutex_start) == 0);\n      assert(pthread_mutex_lock (&tc->mutex_ended) == 0);\n      assert(pthread_mutex_unlock (&tc->mutex_started) == 0);\n\n      for (;;)\n\t{\n\n\t  /*\n\t   * get lock on todo list\n\t   */\n\t  assert(pthread_mutex_lock (&mutex_todo) == 0);\n\n\t  mywork = todo;\n\t  if (todo >= 0)\n\t    {\n\t      ++todo;\n\t      if (todo >= nwork)\n\t\ttodo = -1;\n\t    }\n\t  assert(pthread_mutex_unlock (&mutex_todo) == 0);\n\n\t  if (mywork < 0)\n\t    break;\n\n\t  assert((n = do_work_unit (tc->id, mywork)) >= 0);\n\t  tc->work += n;\n\t}\n\n      assert(pthread_mutex_lock (&tc->mutex_end) == 0);\n      assert(pthread_mutex_unlock (&tc->mutex_end) == 0);\n      assert(pthread_mutex_lock (&tc->mutex_started) == 0);\n      assert(pthread_mutex_unlock (&tc->mutex_ended) == 0);\n\n      if (-2 == mywork)\n\tbreak;\n    }\n\n  assert(pthread_mutex_unlock (&tc->mutex_started) == 0);\n\n  return (0);\n}\n\nstatic void\ndosync (void)\n{\n  int\t\ti;\n\n  for (i = 0; i < nthreads; ++i)\n    {\n      assert(pthread_mutex_lock (&tcs[i].mutex_end) == 0);\n      assert(pthread_mutex_unlock (&tcs[i].mutex_start) == 0);\n      assert(pthread_mutex_lock (&tcs[i].mutex_started) == 0);\n      assert(pthread_mutex_unlock (&tcs[i].mutex_started) == 0);\n    }\n\n  /*\n   * Now threads do their work\n   */\n  for (i = 0; i < nthreads; ++i)\n    {\n      assert(pthread_mutex_lock (&tcs[i].mutex_start) == 0);\n      assert(pthread_mutex_unlock (&tcs[i].mutex_end) == 0);\n      assert(pthread_mutex_lock (&tcs[i].mutex_ended) == 0);\n      assert(pthread_mutex_unlock (&tcs[i].mutex_ended) == 0);\n    }\n}\n\nstatic void\ndowork (void)\n{\n  todo = 0;\n  dosync();\n\n  todo = 0;\n  dosync();\n}\n\nint\nmain (int argc, char *argv[])\n{\n  int\t\ti;\n\n  assert(NULL != (tcs = (TC *) calloc (nthreads, sizeof (*tcs))));\n\n  /* \n   * Launch threads\n   */\n  for (i = 0; i < nthreads; ++i)\n    {\n      tcs[i].id = i;\n\n      assert(pthread_mutex_init (&tcs[i].mutex_start, NULL) == 0);\n      assert(pthread_mutex_init (&tcs[i].mutex_started, NULL) == 0);\n      assert(pthread_mutex_init (&tcs[i].mutex_end, NULL) == 0);\n      assert(pthread_mutex_init (&tcs[i].mutex_ended, NULL) == 0);\n\n      tcs[i].work = 0;  \n\n      assert(pthread_mutex_lock (&tcs[i].mutex_start) == 0);\n      assert((tcs[i].stat = \n\t      pthread_create (&tcs[i].thread,\n\t\t\t      NULL,\n                  (void *(*)(void *))print_server,\n                (void *) &tcs[i])\n\t      ) == 0);\n\n      /* \n       * Wait for thread initialisation\n       */\n      {\n\tint trylock = 0;\n\n\twhile (trylock == 0)\n\t  {\n\t    trylock = pthread_mutex_trylock(&tcs[i].mutex_started);\n\t    assert(trylock == 0 || trylock == EBUSY);\n\n\t    if (trylock == 0)\n\t      {\n\t\tassert(pthread_mutex_unlock (&tcs[i].mutex_started) == 0);\n\t      }\n\t  }\n      }\n    }\n\n  dowork ();\n\n  /*\n   * Terminate threads\n   */\n  todo = -2;\t/* please terminate */\n  dosync();\n\n  for (i = 0; i < nthreads; ++i)\n    {\n      if (0 == tcs[i].stat)\n\tassert(pthread_join (tcs[i].thread, NULL) == 0);\n    }\n\n  /* \n   * destroy locks\n   */\n  assert(pthread_mutex_destroy (&mutex_stdout) == 0);\n  assert(pthread_mutex_destroy (&mutex_todo) == 0);\n\n  /*\n   * Cleanup\n   */\n  printf (\"\\n\");\n\n  /*\n   * Show results\n   */\n  for (i = 0; i < nthreads; ++i)\n    {\n      printf (\"%2d \", i);\n      if (0 == tcs[i].stat)\n\tprintf (\"%10ld\\n\", tcs[i].work);\n      else\n\tprintf (\"failed %d\\n\", tcs[i].stat);\n\n      assert(pthread_mutex_unlock(&tcs[i].mutex_start) == 0);\n\n      assert(pthread_mutex_destroy (&tcs[i].mutex_start) == 0);\n      assert(pthread_mutex_destroy (&tcs[i].mutex_started) == 0);\n      assert(pthread_mutex_destroy (&tcs[i].mutex_end) == 0);\n      assert(pthread_mutex_destroy (&tcs[i].mutex_ended) == 0);\n    }\n\n  die (0);\n\n  return (0);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/inherit1.c",
    "content": "/*\n * File: inherit1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test thread priority inheritance.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\nenum {\n  PTW32TEST_THREAD_INIT_PRIO = 0,\n  PTW32TEST_MAXPRIORITIES = 512\n};\n\nint minPrio;\nint maxPrio;\nint validPriorities[PTW32TEST_MAXPRIORITIES];\n\n\nvoid * func(void * arg)\n{\n  int policy;\n  struct sched_param param;\n\n  assert(pthread_getschedparam(pthread_self(), &policy, &param) == 0);\n  return (void *) (size_t)param.sched_priority;\n}\n\n\nvoid *\ngetValidPriorities(void * arg)\n{\n  int prioSet;\n  pthread_t thread = pthread_self();\n  HANDLE threadH = pthread_getw32threadhandle_np(thread);\n  struct sched_param param;\n\n  for (prioSet = minPrio;\n       prioSet <= maxPrio;\n       prioSet++)\n    {\n        /*\n       * If prioSet is invalid then the threads priority is unchanged\n       * from the previous value. Make the previous value a known\n       * one so that we can check later.\n       */\n        param.sched_priority = prioSet;\n        assert(pthread_setschedparam(thread, SCHED_OTHER, &param) == 0);\n        validPriorities[prioSet+(PTW32TEST_MAXPRIORITIES/2)] = GetThreadPriority(threadH);\n    }\n\n  return (void *) 0;\n}\n\n\nint\nmain()\n{\n  pthread_t t;\n  pthread_t mainThread = pthread_self();\n  pthread_attr_t attr;\n  void * result = NULL;\n  struct sched_param param;\n  struct sched_param mainParam;\n  int prio;\n  int policy;\n  int inheritsched = -1;\n  pthread_t threadID = pthread_self();\n  HANDLE threadH = pthread_getw32threadhandle_np(threadID);\n\n  assert((maxPrio = sched_get_priority_max(SCHED_OTHER)) != -1);\n  assert((minPrio = sched_get_priority_min(SCHED_OTHER)) != -1);\n\n  assert(pthread_create(&t, NULL, getValidPriorities, NULL) == 0);\n  assert(pthread_join(t, &result) == 0);\n\n  assert(pthread_attr_init(&attr) == 0);\n  assert(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED) == 0);\n  assert(pthread_attr_getinheritsched(&attr, &inheritsched) == 0);\n  assert(inheritsched == PTHREAD_INHERIT_SCHED);\n\n  for (prio = minPrio; prio <= maxPrio; prio++)\n    {\n      mainParam.sched_priority = prio;\n\n      /* Set the thread's priority to a known initial value. */\n      SetThreadPriority(threadH, PTW32TEST_THREAD_INIT_PRIO);\n\n      /* Change the main thread priority */\n      assert(pthread_setschedparam(mainThread, SCHED_OTHER, &mainParam) == 0);\n      assert(pthread_getschedparam(mainThread, &policy, &mainParam) == 0);\n      assert(policy == SCHED_OTHER);\n      /* Priority returned below should be the level set by pthread_setschedparam(). */\n      assert(mainParam.sched_priority == prio);\n      assert(GetThreadPriority(threadH) ==\n               validPriorities[prio+(PTW32TEST_MAXPRIORITIES/2)]);\n\n      for (param.sched_priority = prio;\n           param.sched_priority <= maxPrio;\n           param.sched_priority++)\n        {\n          /* The new thread create should ignore this new priority */\n          assert(pthread_attr_setschedparam(&attr, &param) == 0);\n          assert(pthread_create(&t, &attr, func, NULL) == 0);\n          pthread_join(t, &result);\n          assert((int)(size_t) result == mainParam.sched_priority);\n        }\n    }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/join0.c",
    "content": "/*\n * Test for pthread_join().\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on API functions: pthread_create(), pthread_exit().\n */\n\n#include \"test.h\"\n\nvoid *\nfunc(void * arg)\n{\n  Sleep(2000);\n\n  pthread_exit(arg);\n\n  /* Never reached. */\n  exit(1);\n}\n\nint\nmain(int argc, char * argv[])\n{\n  pthread_t id;\n  void* result = (void*)0;\n\n  /* Create a single thread and wait for it to exit. */\n  assert(pthread_create(&id, NULL, func, (void *) 123) == 0);\n\n  assert(pthread_join(id, &result) == 0);\n\n  assert((int)(size_t)result == 123);\n\n  /* Success. */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/join1.c",
    "content": "/*\n * Test for pthread_join().\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on API functions: pthread_create(), pthread_join(), pthread_exit().\n */\n\n#include \"test.h\"\n\nvoid *\nfunc(void * arg)\n{\n    int i = (int)(size_t)arg;\n\n    Sleep(i * 100);\n\n    pthread_exit(arg);\n\n    /* Never reached. */\n    exit(1);\n}\n\nint\nmain(int argc, char * argv[])\n{\n\tpthread_t id[4];\n\tint i;\n\tvoid* result = (void*)-1;\n\n\t/* Create a few threads and then exit. */\n\tfor (i = 0; i < 4; i++)\n\t  {\n\t    assert(pthread_create(&id[i], NULL, func, (void *)(size_t)i) == 0);\n\t  }\n\n\t/* Some threads will finish before they are joined, some after. */\n\tSleep(2 * 100 + 50);\n\n\tfor (i = 0; i < 4; i++)\n\t  {\n\t    assert(pthread_join(id[i], &result) == 0);\n\t    assert((int)(size_t)result == i);\n\t  }\n\n\t/* Success. */\n\treturn 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/join2.c",
    "content": "/*\n * Test for pthread_join() returning return value from threads.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on API functions: pthread_create().\n */\n\n#include \"test.h\"\n\nvoid *\nfunc(void * arg)\n{\n\tSleep(1000);\n\treturn arg;\n}\n\nint\nmain(int argc, char * argv[])\n{\n\tpthread_t id[4];\n\tint i;\n\tvoid* result = (void*)-1;\n\n\t/* Create a few threads and then exit. */\n\tfor (i = 0; i < 4; i++)\n\t  {\n\t    assert(pthread_create(&id[i], NULL, func, (void *)(size_t)i) == 0);\n\t  }\n\n\tfor (i = 0; i < 4; i++)\n\t  {\n\t    assert(pthread_join(id[i], &result) == 0);\n\t    assert((int)(size_t)result == i);\n\t  }\n\n\t/* Success. */\n\treturn 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/join3.c",
    "content": "/*\n * Test for pthread_join() returning return value from threads.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Depends on API functions: pthread_create().\n */\n\n#include \"test.h\"\n\nvoid *\nfunc(void * arg)\n{\n\tsched_yield();\n\treturn arg;\n}\n\nint\nmain(int argc, char * argv[])\n{\n\tpthread_t id[4];\n\tint i;\n\tvoid* result = (void*)-1;\n\n\t/* Create a few threads and then exit. */\n\tfor (i = 0; i < 4; i++)\n\t  {\n\t    assert(pthread_create(&id[i], NULL, func, (void *)(size_t)i) == 0);\n\t  }\n\n\t/*\n\t * Let threads exit before we join them.\n\t * We should still retrieve the exit code for the threads.\n\t */\n\tSleep(1000);\n\n\tfor (i = 0; i < 4; i++)\n\t  {\n\t    assert(pthread_join(id[i], &result) == 0);\n\t    assert((int)(size_t)result == i);\n\t  }\n\n\t/* Success. */\n\treturn 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/kill1.c",
    "content": "/*\n * File: kill1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - pthread_kill() does not support non zero signals..\n *\n * Test Method (Validation or Falsification):\n * -\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n\nint\nmain()\n{\n  assert(pthread_kill(pthread_self(), 1) == EINVAL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/loadfree.c",
    "content": "/*\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n * From: Todd Owen <towen@lucidcalm.dropbear.id.au>\n * To: pthreads-win32@sourceware.cygnus.com\n * Subject: invalid page fault when using LoadLibrary/FreeLibrary\n * \n * hi,\n * for me, pthread.dll consistently causes an \"invalid page fault in\n * kernel32.dll\" when I load it \"explicitly\"...to be precise, loading (with\n * LoadLibrary) isn't a problem, it gives the error when I call FreeLibrary.\n * I guess that the dll's cleanup must be causing the error.\n * \n * Implicit linkage of the dll has never given me this problem.  Here's a\n * program (console application) that gives me the error.\n * \n * I compile with: mingw32 (gcc-2.95 release), with the MSVCRT add-on (not\n * that the compiler should make much difference in this case).\n * PTHREAD.DLL: is the precompiled 1999-11-02 one (I tried an older one as\n * well, with the same result).\n * \n * Fascinatingly, if you have your own dll (mycode.dll) which implicitly\n * loads pthread.dll, and then do LoadLibrary/FreeLibrary on _this_ dll, the\n * same thing happens.\n * \n */\n\n#include \"test.h\"\n\nint main() {\n  HINSTANCE hinst;\n\n  assert((hinst = LoadLibrary(\"pthread\")) != (HINSTANCE) 0);\n\n  Sleep(100);\n\n  FreeLibrary(hinst);\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex1.c",
    "content": "/* \n * mutex1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Create a simple mutex object, lock it, and then unlock it again.\n * This is the simplest test of the pthread mutex family that we can do.\n *\n * Depends on API functions:\n * \tpthread_mutex_init()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n *\tpthread_mutex_destroy()\n */\n\n#include \"test.h\"\n\npthread_mutex_t mutex = NULL;\n\nint\nmain()\n{\n  assert(mutex == NULL);\n\n  assert(pthread_mutex_init(&mutex, NULL) == 0);\n\n  assert(mutex != NULL);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  assert(mutex == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex1e.c",
    "content": "/* \n * mutex1e.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * As for mutex1.c but with type set to PTHREAD_MUTEX_ERRORCHECK.\n *\n * Create a simple mutex object, lock it, unlock it, then destroy it.\n * This is the simplest test of the pthread mutex family that we can do.\n *\n * Depends on API functions:\n *\tpthread_mutexattr_settype()\n * \tpthread_mutex_init()\n *\tpthread_mutex_destroy()\n */\n\n#include \"test.h\"\n\npthread_mutex_t mutex = NULL;\npthread_mutexattr_t mxAttr;\n\nint\nmain()\n{\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_ERRORCHECK) == 0);\n\n  assert(mutex == NULL);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(mutex != NULL);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  assert(mutex == NULL);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex1n.c",
    "content": "/* \n * mutex1n.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * As for mutex1.c but with type set to PTHREAD_MUTEX_NORMAL.\n *\n * Create a simple mutex object, lock it, unlock it, then destroy it.\n * This is the simplest test of the pthread mutex family that we can do.\n *\n * Depends on API functions:\n *\tpthread_mutexattr_settype()\n * \tpthread_mutex_init()\n *\tpthread_mutex_destroy()\n */\n\n#include \"test.h\"\n\npthread_mutex_t mutex = NULL;\npthread_mutexattr_t mxAttr;\n\nint\nmain()\n{\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_NORMAL) == 0);\n\n  assert(mutex == NULL);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(mutex != NULL);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  assert(mutex == NULL);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex1r.c",
    "content": "/* \n * mutex1r.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * As for mutex1.c but with type set to PTHREAD_MUTEX_RECURSIVE.\n *\n * Create a simple mutex object, lock it, unlock it, then destroy it.\n * This is the simplest test of the pthread mutex family that we can do.\n *\n * Depends on API functions:\n *\tpthread_mutexattr_settype()\n * \tpthread_mutex_init()\n *\tpthread_mutex_destroy()\n */\n\n#include \"test.h\"\n\npthread_mutex_t mutex = NULL;\npthread_mutexattr_t mxAttr;\n\nint\nmain()\n{\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_RECURSIVE) == 0);\n\n  assert(mutex == NULL);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(mutex != NULL);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  assert(mutex == NULL);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex2.c",
    "content": "/* \n * mutex2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static mutex object, lock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n \npthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;\n\nint\nmain()\n{\n  assert(mutex == PTHREAD_MUTEX_INITIALIZER);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(mutex != PTHREAD_MUTEX_INITIALIZER);\n\n  assert(mutex != NULL);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  assert(mutex == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex2e.c",
    "content": "/* \n * mutex2e.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static mutex object, lock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n \npthread_mutex_t mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER;\n\nint\nmain()\n{\n  assert(mutex == PTHREAD_ERRORCHECK_MUTEX_INITIALIZER);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(mutex != PTHREAD_ERRORCHECK_MUTEX_INITIALIZER);\n\n  assert(mutex != NULL);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  assert(mutex == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex2r.c",
    "content": "/* \n * mutex2r.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static mutex object, lock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n \npthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;\n\nint\nmain()\n{\n  assert(mutex == PTHREAD_RECURSIVE_MUTEX_INITIALIZER);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(mutex != PTHREAD_RECURSIVE_MUTEX_INITIALIZER);\n\n  assert(mutex != NULL);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  assert(mutex == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex3.c",
    "content": "/* \n * mutex3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static mutex object, lock it, trylock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_mutex_lock()\n *\tpthread_mutex_trylock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n \npthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  assert(pthread_mutex_trylock(&mutex1) == EBUSY);\n\n  washere = 1;\n\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_mutex_lock(&mutex1) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n  assert(pthread_join(t, NULL) == 0);\n\n  assert(pthread_mutex_unlock(&mutex1) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex3e.c",
    "content": "/* \n * mutex3e.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static mutex object, lock it, trylock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_mutex_lock()\n *\tpthread_mutex_trylock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n \npthread_mutex_t mutex1 = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER;\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  assert(pthread_mutex_trylock(&mutex1) == EBUSY);\n\n  washere = 1;\n\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_mutex_lock(&mutex1) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n  assert(pthread_join(t, NULL) == 0);\n\n  assert(pthread_mutex_unlock(&mutex1) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex3r.c",
    "content": "/* \n * mutex3r.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static mutex object, lock it, trylock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_mutex_lock()\n *\tpthread_mutex_trylock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n \npthread_mutex_t mutex1 = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  assert(pthread_mutex_trylock(&mutex1) == EBUSY);\n\n  washere = 1;\n\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_mutex_lock(&mutex1) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n  assert(pthread_join(t, NULL) == 0);\n\n  assert(pthread_mutex_unlock(&mutex1) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex4.c",
    "content": "/* \n * mutex4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Thread A locks mutex - thread B tries to unlock.\n *\n * Depends on API functions: \n *\tpthread_mutex_lock()\n *\tpthread_mutex_trylock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int wasHere = 0;\n\nstatic pthread_mutex_t mutex1;\n \nvoid * unlocker(void * arg)\n{\n  int expectedResult = (int)(size_t)arg;\n\n  wasHere++;\n  assert(pthread_mutex_unlock(&mutex1) == expectedResult);\n  wasHere++;\n  return NULL;\n}\n \nint\nmain()\n{\n  pthread_t t;\n  pthread_mutexattr_t ma;\n\n  assert(pthread_mutexattr_init(&ma) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(ma)\n\n  wasHere = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_DEFAULT) == 0);\n  assert(pthread_mutex_init(&mutex1, &ma) == 0);\n  assert(pthread_mutex_lock(&mutex1) == 0);\n  assert(pthread_create(&t, NULL, unlocker, (void *)(size_t)(IS_ROBUST?EPERM:0)) == 0);\n  assert(pthread_join(t, NULL) == 0);\n  assert(pthread_mutex_unlock(&mutex1) == 0);\n  assert(wasHere == 2);\n\n  wasHere = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_NORMAL) == 0);\n  assert(pthread_mutex_init(&mutex1, &ma) == 0);\n  assert(pthread_mutex_lock(&mutex1) == 0);\n  assert(pthread_create(&t, NULL, unlocker, (void *)(size_t)(IS_ROBUST?EPERM:0)) == 0);\n  assert(pthread_join(t, NULL) == 0);\n  assert(pthread_mutex_unlock(&mutex1) == 0);\n  assert(wasHere == 2);\n\n  wasHere = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK) == 0);\n  assert(pthread_mutex_init(&mutex1, &ma) == 0);\n  assert(pthread_mutex_lock(&mutex1) == 0);\n  assert(pthread_create(&t, NULL, unlocker, (void *)(size_t) EPERM) == 0);\n  assert(pthread_join(t, NULL) == 0);\n  assert(pthread_mutex_unlock(&mutex1) == 0);\n  assert(wasHere == 2);\n\n  wasHere = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE) == 0);\n  assert(pthread_mutex_init(&mutex1, &ma) == 0);\n  assert(pthread_mutex_lock(&mutex1) == 0);\n  assert(pthread_create(&t, NULL, unlocker, (void *)(size_t) EPERM) == 0);\n  assert(pthread_join(t, NULL) == 0);\n  assert(pthread_mutex_unlock(&mutex1) == 0);\n  assert(wasHere == 2);\n\n  END_MUTEX_STALLED_ROBUST(ma)\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex5.c",
    "content": "/* \n * mutex5.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Confirm the equality/inequality of the various mutex types,\n * and the default not-set value.\n */\n\n#include \"test.h\"\n\nstatic pthread_mutexattr_t mxAttr;\n\n/* Prevent optimiser from removing dead or obvious asserts. */\nint _optimiseFoil;\n#define FOIL(x) (_optimiseFoil = x)\n\nint\nmain()\n{\n  int mxType = -1;\n\n  assert(FOIL(PTHREAD_MUTEX_DEFAULT) == PTHREAD_MUTEX_NORMAL);\n  assert(FOIL(PTHREAD_MUTEX_DEFAULT) != PTHREAD_MUTEX_ERRORCHECK);\n  assert(FOIL(PTHREAD_MUTEX_DEFAULT) != PTHREAD_MUTEX_RECURSIVE);\n  assert(FOIL(PTHREAD_MUTEX_RECURSIVE) != PTHREAD_MUTEX_ERRORCHECK);\n\n  assert(FOIL(PTHREAD_MUTEX_NORMAL) == PTHREAD_MUTEX_FAST_NP);\n  assert(FOIL(PTHREAD_MUTEX_RECURSIVE) == PTHREAD_MUTEX_RECURSIVE_NP);\n  assert(FOIL(PTHREAD_MUTEX_ERRORCHECK) == PTHREAD_MUTEX_ERRORCHECK_NP);\n\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n  assert(pthread_mutexattr_gettype(&mxAttr, &mxType) == 0);\n  assert(mxType == PTHREAD_MUTEX_NORMAL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex6.c",
    "content": "/* \n * mutex6.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test the default (type not set) mutex type.\n * Should be the same as PTHREAD_MUTEX_NORMAL.\n * Thread locks mutex twice (recursive lock).\n * Locking thread should deadlock on second attempt.\n *\n * Depends on API functions: \n *\tpthread_mutex_lock()\n *\tpthread_mutex_trylock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount = 0;\n\nstatic pthread_mutex_t mutex;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n\n  /* Should wait here (deadlocked) */\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return 0;\n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_mutex_init(&mutex, NULL) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  Sleep(1000);\n\n  assert(lockCount == 1);\n\n  /*\n   * Should succeed even though we don't own the lock\n   * because FAST mutexes don't check ownership.\n   */\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  Sleep (1000);\n\n  assert(lockCount == 2);\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex6e.c",
    "content": "/* \n * mutex6e.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_ERRORCHECK mutex type.\n * Thread locks mutex twice (recursive lock).\n * This should fail with an EDEADLK error.\n * The second unlock attempt should fail with an EPERM error.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *      pthread_mutexattr_init()\n *      pthread_mutexattr_destroy()\n *      pthread_mutexattr_settype()\n *      pthread_mutexattr_gettype()\n *      pthread_mutex_init()\n *      pthread_mutex_destroy()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\nstatic pthread_mutexattr_t mxAttr;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex) == EDEADLK);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == EPERM);\n\n  return (void *) 555;\n}\n \nint\nmain()\n{\n  pthread_t t;\n  void* result = (void*)0;\n  int mxType = -1;\n\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_ERRORCHECK) == 0);\n  assert(pthread_mutexattr_gettype(&mxAttr, &mxType) == 0);\n  assert(mxType == PTHREAD_MUTEX_ERRORCHECK);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  assert(pthread_join(t, &result) == 0);\n  assert((int)(size_t)result == 555);\n\n  assert(lockCount == 2);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  assert(pthread_mutexattr_destroy(&mxAttr) == 0);\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex6es.c",
    "content": "/* \n * mutex6es.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_ERRORCHECK static mutex type.\n * Thread locks mutex twice (recursive lock).\n * This should fail with an EDEADLK error.\n * The second unlock attempt should fail with an EPERM error.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *      pthread_mutexattr_init()\n *      pthread_mutexattr_destroy()\n *      pthread_mutexattr_settype()\n *      pthread_mutexattr_gettype()\n *      pthread_mutex_init()\n *      pthread_mutex_destroy()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount = 0;\n\nstatic pthread_mutex_t mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex) == EDEADLK);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == EPERM);\n\n  return (void *) 555;\n}\n \nint\nmain()\n{\n  pthread_t t;\n  void* result = (void*)0;\n\n  assert(mutex == PTHREAD_ERRORCHECK_MUTEX_INITIALIZER);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  assert(pthread_join(t, &result) == 0);\n  assert((int)(size_t)result == 555);\n\n  assert(lockCount == 2);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex6n.c",
    "content": "/* \n * mutex6n.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_NORMAL mutex type.\n * Thread locks mutex twice (recursive lock).\n * The thread should deadlock.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_mutexattr_init()\n *      pthread_mutexattr_settype()\n *      pthread_mutexattr_gettype()\n *      pthread_mutex_init()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\nstatic pthread_mutexattr_t mxAttr;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n\n  /* Should wait here (deadlocked) */\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return (void *) 555;\n}\n \nint\nmain()\n{\n  pthread_t t;\n  int mxType = -1;\n\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_NORMAL) == 0);\n  assert(pthread_mutexattr_gettype(&mxAttr, &mxType) == 0);\n  assert(mxType == PTHREAD_MUTEX_NORMAL);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  Sleep(100);\n\n  assert(lockCount == 1);\n\n  assert(pthread_mutex_unlock(&mutex) == IS_ROBUST?EPERM:0);\n\n  Sleep (100);\n\n  assert(lockCount == IS_ROBUST?1:2);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex6r.c",
    "content": "/* \n * mutex6r.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_RECURSIVE mutex type.\n * Thread locks mutex twice (recursive lock).\n * Both locks and unlocks should succeed.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *      pthread_mutexattr_init()\n *      pthread_mutexattr_destroy()\n *      pthread_mutexattr_settype()\n *      pthread_mutexattr_gettype()\n *      pthread_mutex_init()\n *      pthread_mutex_destroy()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\nstatic pthread_mutexattr_t mxAttr;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return (void *) 555;\n}\n \nint\nmain()\n{\n  pthread_t t;\n  void* result = (void*)0;\n  int mxType = -1;\n\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_RECURSIVE) == 0);\n  assert(pthread_mutexattr_gettype(&mxAttr, &mxType) == 0);\n  assert(mxType == PTHREAD_MUTEX_RECURSIVE);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  assert(pthread_join(t, &result) == 0);\n  assert((int)(size_t)result == 555);\n\n  assert(lockCount == 2);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  assert(pthread_mutexattr_destroy(&mxAttr) == 0);\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex6rs.c",
    "content": "/* \n * mutex6rs.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_RECURSIVE static mutex type.\n * Thread locks mutex twice (recursive lock).\n * Both locks and unlocks should succeed.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *      pthread_mutexattr_init()\n *      pthread_mutexattr_destroy()\n *      pthread_mutexattr_settype()\n *      pthread_mutexattr_gettype()\n *      pthread_mutex_init()\n *      pthread_mutex_destroy()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount = 0;\n\nstatic pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return (void *) 555;\n}\n \nint\nmain()\n{\n  pthread_t t;\n  void* result = (void*)0;\n\n  assert(mutex == PTHREAD_RECURSIVE_MUTEX_INITIALIZER);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  assert(pthread_join(t, &result) == 0);\n  assert((int)(size_t)result == 555);\n\n  assert(lockCount == 2);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex6s.c",
    "content": "/* \n * mutex6s.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test the default (type not set) static mutex type.\n * Should be the same as PTHREAD_MUTEX_NORMAL.\n * Thread locks mutex twice (recursive lock).\n * Locking thread should deadlock on second attempt.\n *\n * Depends on API functions: \n *\tpthread_mutex_lock()\n *\tpthread_mutex_trylock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount = 0;\n\nstatic pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n\n  /* Should wait here (deadlocked) */\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return 0;\n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(mutex == PTHREAD_MUTEX_INITIALIZER);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  Sleep(1000);\n\n  assert(lockCount == 1);\n\n  /*\n   * Should succeed even though we don't own the lock\n   * because FAST mutexes don't check ownership.\n   */\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  Sleep (1000);\n\n  assert(lockCount == 2);\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex7.c",
    "content": "/* \n * mutex7.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test the default (type not set) mutex type.\n * Should be the same as PTHREAD_MUTEX_NORMAL.\n * Thread locks then trylocks mutex (attempted recursive lock).\n * The thread should lock first time and EBUSY second time.\n *\n * Depends on API functions: \n *\tpthread_mutex_lock()\n *\tpthread_mutex_trylock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount = 0;\n\nstatic pthread_mutex_t mutex;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_trylock(&mutex) == EBUSY);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return 0;\n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_mutex_init(&mutex, NULL) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  Sleep(1000);\n\n  assert(lockCount == 2);\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex7e.c",
    "content": "/* \n * mutex7e.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_ERRORCHECK mutex type.\n * Thread locks and then trylocks mutex (attempted recursive lock).\n * Trylock should fail with an EBUSY error.\n * The second unlock attempt should fail with an EPERM error.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *      pthread_mutexattr_init()\n *      pthread_mutexattr_destroy()\n *      pthread_mutexattr_settype()\n *      pthread_mutexattr_gettype()\n *      pthread_mutex_init()\n *      pthread_mutex_destroy()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\nstatic pthread_mutexattr_t mxAttr;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_trylock(&mutex) == EBUSY);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return (void *) 555;\n}\n \nint\nmain()\n{\n  pthread_t t;\n  void* result = (void*)0;\n  int mxType = -1;\n\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_ERRORCHECK) == 0);\n  assert(pthread_mutexattr_gettype(&mxAttr, &mxType) == 0);\n  assert(mxType == PTHREAD_MUTEX_ERRORCHECK);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  assert(pthread_join(t, &result) == 0);\n  assert((int)(size_t)result == 555);\n\n  assert(lockCount == 2);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  assert(pthread_mutexattr_destroy(&mxAttr) == 0);\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex7n.c",
    "content": "/* \n * mutex7n.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_NORMAL mutex type.\n * Thread locks then trylocks mutex (attempted recursive lock).\n * The thread should lock first time and EBUSY second time.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_mutexattr_init()\n *      pthread_mutexattr_settype()\n *      pthread_mutexattr_gettype()\n *      pthread_mutex_init()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\nstatic pthread_mutexattr_t mxAttr;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_trylock(&mutex) == EBUSY);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return (void *) 555;\n}\n \nint\nmain()\n{\n  pthread_t t;\n  int mxType = -1;\n\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_NORMAL) == 0);\n  assert(pthread_mutexattr_gettype(&mxAttr, &mxType) == 0);\n  assert(mxType == PTHREAD_MUTEX_NORMAL);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  Sleep(100);\n\n  assert(lockCount == 2);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  assert(pthread_mutexattr_destroy(&mxAttr) == 0);\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex7r.c",
    "content": "/* \n * mutex7r.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_RECURSIVE mutex type.\n * Thread locks mutex then trylocks mutex (recursive lock twice).\n * Both locks and unlocks should succeed.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *      pthread_mutexattr_init()\n *      pthread_mutexattr_destroy()\n *      pthread_mutexattr_settype()\n *      pthread_mutexattr_gettype()\n *      pthread_mutex_init()\n *      pthread_mutex_destroy()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\nstatic pthread_mutexattr_t mxAttr;\n\nvoid * locker(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_trylock(&mutex) == 0);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return (void *) 555;\n}\n \nint\nmain()\n{\n  pthread_t t;\n  void* result = (void*)0;\n  int mxType = -1;\n\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_RECURSIVE) == 0);\n  assert(pthread_mutexattr_gettype(&mxAttr, &mxType) == 0);\n  assert(mxType == PTHREAD_MUTEX_RECURSIVE);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  assert(pthread_join(t, &result) == 0);\n  assert((int)(size_t)result == 555);\n\n  assert(lockCount == 2);\n\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  assert(pthread_mutexattr_destroy(&mxAttr) == 0);\n\n  exit(0);\n\n  /* Never reached */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex8.c",
    "content": "/* \n * mutex8.c\n *\n *\n * Pthreads-win32 - POSIX Threads Library for Win32\n * Copyright (C) 1998 Ben Elliston and Ross Johnson\n * Copyright (C) 1999,2000,2001 Ross Johnson\n *\n * Contact Email: rpj@ise.canberra.edu.au\n *\n * This library is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation; either\n * version 2.1 of the License, or (at your option) any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test the default (type not set) mutex type exercising timedlock.\n * Thread locks mutex, another thread timedlocks the mutex.\n * Timed thread should timeout.\n *\n * Depends on API functions:\n *\tpthread_mutex_lock()\n *\tpthread_mutex_timedlock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\nstatic int lockCount = 0;\n\nstatic pthread_mutex_t mutex;\n\nvoid * locker(void * arg)\n{\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 1;\n\n  assert(pthread_mutex_timedlock(&mutex, &abstime) == ETIMEDOUT);\n\n  lockCount++;\n\n  return 0;\n}\n\nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_mutex_init(&mutex, NULL) == 0);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  Sleep(2000);\n\n  assert(lockCount == 1);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex8e.c",
    "content": "/* \n * mutex8e.c\n *\n *\n * Pthreads-win32 - POSIX Threads Library for Win32\n * Copyright (C) 1998 Ben Elliston and Ross Johnson\n * Copyright (C) 1999,2000,2001 Ross Johnson\n *\n * Contact Email: rpj@ise.canberra.edu.au\n *\n * This library is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation; either\n * version 2.1 of the License, or (at your option) any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_ERRORCHECK mutex type exercising timedlock.\n * Thread locks mutex, another thread timedlocks the mutex.\n * Timed thread should timeout.\n *\n * Depends on API functions:\n *\tpthread_create()\n *\tpthread_mutexattr_init()\n *\tpthread_mutexattr_destroy()\n *\tpthread_mutexattr_settype()\n *\tpthread_mutexattr_gettype()\n *\tpthread_mutex_init()\n *\tpthread_mutex_destroy()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_timedlock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\nstatic pthread_mutexattr_t mxAttr;\n\nvoid * locker(void * arg)\n{\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 1;\n\n  assert(pthread_mutex_timedlock(&mutex, &abstime) == ETIMEDOUT);\n\n  lockCount++;\n\n  return 0;\n}\n\nint\nmain()\n{\n  pthread_t t;\n  int mxType = -1;\n\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_ERRORCHECK) == 0);\n  assert(pthread_mutexattr_gettype(&mxAttr, &mxType) == 0);\n  assert(mxType == PTHREAD_MUTEX_ERRORCHECK);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  Sleep(2000);\n\n  assert(lockCount == 1);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex8n.c",
    "content": "/*\n * mutex8n.c\n *\n *\n * Pthreads-win32 - POSIX Threads Library for Win32\n * Copyright (C) 1998 Ben Elliston and Ross Johnson\n * Copyright (C) 1999,2000,2001 Ross Johnson\n *\n * Contact Email: rpj@ise.canberra.edu.au\n *\n * This library is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation; either\n * version 2.1 of the License, or (at your option) any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_NORMAL mutex type exercising timedlock.\n * Thread locks mutex, another thread timedlocks the mutex.\n * Timed thread should timeout.\n *\n * Depends on API functions:\n *\tpthread_create()\n *\tpthread_mutexattr_init()\n *\tpthread_mutexattr_destroy()\n *\tpthread_mutexattr_settype()\n *\tpthread_mutexattr_gettype()\n *\tpthread_mutex_init()\n *\tpthread_mutex_destroy()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_timedlock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\nstatic pthread_mutexattr_t mxAttr;\n\nvoid * locker(void * arg)\n{\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 1;\n\n  assert(pthread_mutex_timedlock(&mutex, &abstime) == ETIMEDOUT);\n\n  lockCount++;\n\n  return 0;\n}\n\nint\nmain()\n{\n  pthread_t t;\n  int mxType = -1;\n\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_NORMAL) == 0);\n  assert(pthread_mutexattr_gettype(&mxAttr, &mxType) == 0);\n  assert(mxType == PTHREAD_MUTEX_NORMAL);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  Sleep(2000);\n\n  assert(lockCount == 1);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/mutex8r.c",
    "content": "/* \n * mutex8r.c\n *\n *\n * Pthreads-win32 - POSIX Threads Library for Win32\n * Copyright (C) 1998 Ben Elliston and Ross Johnson\n * Copyright (C) 1999,2000,2001 Ross Johnson\n *\n * Contact Email: rpj@ise.canberra.edu.au\n *\n * This library is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation; either\n * version 2.1 of the License, or (at your option) any later version.\n *\n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n * Lesser General Public License for more details.\n *\n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; if not, write to the Free Software\n * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Tests PTHREAD_MUTEX_RECURSIVE mutex type exercising timedlock.\n * Thread locks mutex, another thread timedlocks the mutex.\n * Timed thread should timeout.\n *\n * Depends on API functions:\n *\tpthread_create()\n *\tpthread_mutexattr_init()\n *\tpthread_mutexattr_destroy()\n *\tpthread_mutexattr_settype()\n *\tpthread_mutexattr_gettype()\n *\tpthread_mutex_init()\n *\tpthread_mutex_destroy()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_timedlock()\n *\tpthread_mutex_unlock()\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\nstatic pthread_mutexattr_t mxAttr;\n\nvoid * locker(void * arg)\n{\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 1;\n\n  assert(pthread_mutex_timedlock(&mutex, &abstime) == ETIMEDOUT);\n\n  lockCount++;\n\n  return 0;\n}\n\nint\nmain()\n{\n  pthread_t t;\n  int mxType = -1;\n\n  assert(pthread_mutexattr_init(&mxAttr) == 0);\n\n  BEGIN_MUTEX_STALLED_ROBUST(mxAttr)\n\n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&mxAttr, PTHREAD_MUTEX_RECURSIVE) == 0);\n  assert(pthread_mutexattr_gettype(&mxAttr, &mxType) == 0);\n  assert(mxType == PTHREAD_MUTEX_RECURSIVE);\n\n  assert(pthread_mutex_init(&mutex, &mxAttr) == 0);\n\n  assert(pthread_mutex_lock(&mutex) == 0);\n\n  assert(pthread_create(&t, NULL, locker, NULL) == 0);\n\n  Sleep(2000);\n\n  assert(lockCount == 1);\n\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  END_MUTEX_STALLED_ROBUST(mxAttr)\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/once1.c",
    "content": "/*\n * once1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Create a static pthread_once and test that it calls myfunc once.\n *\n * Depends on API functions:\n *\tpthread_once()\n *\tpthread_create()\n */\n\n#include \"test.h\"\n\npthread_once_t once = PTHREAD_ONCE_INIT;\n\nstatic int washere = 0;\n\nvoid\nmyfunc(void)\n{\n  washere++;\n}\n\nvoid *\nmythread(void * arg)\n{\n   assert(pthread_once(&once, myfunc) == 0);\n\n   return 0;\n}\n\nint\nmain()\n{\n  pthread_t t1, t2;\n  \n  assert(pthread_create(&t1, NULL, mythread, NULL) == 0);\n\n  assert(pthread_create(&t2, NULL, mythread, NULL) == 0);\n\n  Sleep(2000);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/once2.c",
    "content": "/*\n * once2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Create several static pthread_once objects and channel several threads\n * through each.\n *\n * Depends on API functions:\n *\tpthread_once()\n *\tpthread_create()\n */\n\n#include \"test.h\"\n\n#define NUM_THREADS 100 /* Targeting each once control */\n#define NUM_ONCE    10\n\npthread_once_t o = PTHREAD_ONCE_INIT;\npthread_once_t once[NUM_ONCE];\n\ntypedef struct {\n  int i;\n  CRITICAL_SECTION cs;\n} sharedInt_t;\n\nstatic sharedInt_t numOnce = {0, {0}};\nstatic sharedInt_t numThreads = {0, {0}};\n\nvoid\nmyfunc(void)\n{\n  EnterCriticalSection(&numOnce.cs);\n  numOnce.i++;\n  LeaveCriticalSection(&numOnce.cs);\n  /* Simulate slow once routine so that following threads pile up behind it */\n  Sleep(100);\n}\n\nvoid *\nmythread(void * arg)\n{\n   assert(pthread_once(&once[(int)(size_t)arg], myfunc) == 0);\n   EnterCriticalSection(&numThreads.cs);\n   numThreads.i++;   \n   LeaveCriticalSection(&numThreads.cs);\n   return (void*)(size_t)0;\n}\n\nint\nmain()\n{\n  pthread_t t[NUM_THREADS][NUM_ONCE];\n  int i, j;\n  \n  InitializeCriticalSection(&numThreads.cs);\n  InitializeCriticalSection(&numOnce.cs);\n\n  for (j = 0; j < NUM_ONCE; j++)\n    {\n      once[j] = o;\n\n      for (i = 0; i < NUM_THREADS; i++)\n        {\n\t  /* GCC build: create was failing with EAGAIN after 790 threads */\n          while (0 != pthread_create(&t[i][j], NULL, mythread, (void *)(size_t)j))\n\t    sched_yield();\n        }\n    }\n\n  for (j = 0; j < NUM_ONCE; j++)\n    for (i = 0; i < NUM_THREADS; i++)\n      if (pthread_join(t[i][j], NULL) != 0)\n        printf(\"Join failed for [thread,once] = [%d,%d]\\n\", i, j);\n\n  assert(numOnce.i == NUM_ONCE);\n  assert(numThreads.i == NUM_THREADS * NUM_ONCE);\n\n  DeleteCriticalSection(&numOnce.cs);\n  DeleteCriticalSection(&numThreads.cs);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/once3.c",
    "content": "/*\n * once3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Create several pthread_once objects and channel several threads\n * through each. Make the init_routine cancelable and cancel them with\n * waiters waiting.\n *\n * Depends on API functions:\n *\tpthread_once()\n *\tpthread_create()\n *      pthread_testcancel()\n *      pthread_cancel()\n *      pthread_once()\n */\n\n/* #define ASSERT_TRACE */\n\n#include \"test.h\"\n\n#define NUM_THREADS 100 /* Targeting each once control */\n#define NUM_ONCE    10\n\nstatic pthread_once_t o = PTHREAD_ONCE_INIT;\nstatic pthread_once_t once[NUM_ONCE];\n\ntypedef struct {\n  int i;\n  CRITICAL_SECTION cs;\n} sharedInt_t;\n\nstatic sharedInt_t numOnce = {0, {0}};\nstatic sharedInt_t numThreads = {0, {0}};\n\nvoid\nmyfunc(void)\n{\n  EnterCriticalSection(&numOnce.cs);\n  numOnce.i++;\n  assert(numOnce.i > 0);\n  LeaveCriticalSection(&numOnce.cs);\n  /* Simulate slow once routine so that following threads pile up behind it */\n  Sleep(10);\n  /* test for cancelation late so we're sure to have waiters. */\n  pthread_testcancel();\n}\n\nvoid *\nmythread(void * arg)\n{\n  /*\n   * Cancel every thread. These threads are deferred cancelable only, so\n   * only the thread performing the once routine (my_func) will see it (there are\n   * no other cancelation points here). The result will be that every thread\n   * eventually cancels only when it becomes the new 'once' thread.\n   */\n  assert(pthread_cancel(pthread_self()) == 0);\n  assert(pthread_once(&once[(int)(size_t)arg], myfunc) == 0);\n  EnterCriticalSection(&numThreads.cs);\n  numThreads.i++;\n  LeaveCriticalSection(&numThreads.cs);\n  return (void*)(size_t)0;\n}\n\nint\nmain()\n{\n  pthread_t t[NUM_THREADS][NUM_ONCE];\n  int i, j;\n  \n  InitializeCriticalSection(&numThreads.cs);\n  InitializeCriticalSection(&numOnce.cs);\n\n  for (j = 0; j < NUM_ONCE; j++)\n    {\n      once[j] = o;\n\n      for (i = 0; i < NUM_THREADS; i++)\n        {\n          /* GCC build: create was failing with EAGAIN after 790 threads */\n          while (0 != pthread_create(&t[i][j], NULL, mythread, (void *)(size_t)j))\n            sched_yield();\n        }\n    }\n\n  for (j = 0; j < NUM_ONCE; j++)\n    for (i = 0; i < NUM_THREADS; i++)\n      if (pthread_join(t[i][j], NULL) != 0)\n        printf(\"Join failed for [thread,once] = [%d,%d]\\n\", i, j);\n\n  /*\n   * All threads will cancel, none will return normally from\n   * pthread_once and so numThreads should never be incremented. However,\n   * numOnce should be incremented by every thread (NUM_THREADS*NUM_ONCE).\n   */\n  assert(numOnce.i == NUM_ONCE * NUM_THREADS);\n  assert(numThreads.i == 0);\n\n  DeleteCriticalSection(&numOnce.cs);\n  DeleteCriticalSection(&numThreads.cs);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/once4.c",
    "content": "/*\n * once4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Create several pthread_once objects and channel several threads\n * through each. Make the init_routine cancelable and cancel them\n * waiters waiting. Vary the priorities.\n *\n * Depends on API functions:\n *\tpthread_once()\n *\tpthread_create()\n *      pthread_testcancel()\n *      pthread_cancel()\n *      pthread_once()\n */\n\n#include \"test.h\"\n\n#define NUM_THREADS 100 /* Targeting each once control */\n#define NUM_ONCE    10\n\npthread_once_t o = PTHREAD_ONCE_INIT;\npthread_once_t once[NUM_ONCE];\n\ntypedef struct {\n  int i;\n  CRITICAL_SECTION cs;\n} sharedInt_t;\n\nstatic sharedInt_t numOnce = {0, {0}};\nstatic sharedInt_t numThreads = {0, {0}};\n\ntypedef struct {\n  int threadnum;\n  int oncenum;\n  int myPrio;\n  HANDLE w32Thread;\n} bag_t;\n\nstatic bag_t threadbag[NUM_THREADS][NUM_ONCE];\n\nCRITICAL_SECTION print_lock;\n\nvoid\nmycleanupfunc(void * arg)\n{\n  bag_t * bag = (bag_t *) arg;\n  EnterCriticalSection(&print_lock);\n  /*      once thrd  prio error */\n  printf(\"%4d %4d %4d %4d\\n\",\n\t bag->oncenum,\n\t bag->threadnum,\n\t bag->myPrio,\n\t bag->myPrio - GetThreadPriority(bag->w32Thread));\n  LeaveCriticalSection(&print_lock);\n}\n\nvoid\nmyinitfunc(void)\n{\n  EnterCriticalSection(&numOnce.cs);\n  numOnce.i++;\n  LeaveCriticalSection(&numOnce.cs);\n  /* Simulate slow once routine so that following threads pile up behind it */\n  Sleep(10);\n  /* test for cancelation late so we're sure to have waiters. */\n  pthread_testcancel();\n}\n\nvoid *\nmythread(void * arg)\n{\n  bag_t * bag = (bag_t *) arg;\n  struct sched_param param;\n\n  /*\n   * Cancel every thread. These threads are deferred cancelable only, so\n   * only the thread performing the init_routine will see it (there are\n   * no other cancelation points here). The result will be that every thread\n   * eventually cancels only when it becomes the new initter.\n   */\n  pthread_t self = pthread_self();\n  bag->w32Thread = pthread_getw32threadhandle_np(self);\n  /*\n   * Set priority between -2 and 2 inclusive.\n   */\n  bag->myPrio = (bag->threadnum % 5) - 2;\n  param.sched_priority = bag->myPrio;\n  pthread_setschedparam(self, SCHED_OTHER, &param);\n\n  /* Trigger a cancellation at the next cancellation point in this thread */\n  pthread_cancel(self);\n#if 0\n  pthread_cleanup_push(mycleanupfunc, arg);\n  assert(pthread_once(&once[bag->oncenum], myinitfunc) == 0);\n  pthread_cleanup_pop(1);\n#else\n  assert(pthread_once(&once[bag->oncenum], myinitfunc) == 0);\n#endif\n  EnterCriticalSection(&numThreads.cs);\n  numThreads.i++;\n  LeaveCriticalSection(&numThreads.cs);\n  return 0;\n}\n\nint\nmain()\n{\n  pthread_t t[NUM_THREADS][NUM_ONCE];\n  int i, j;\n  \n  InitializeCriticalSection(&print_lock);\n  InitializeCriticalSection(&numThreads.cs);\n  InitializeCriticalSection(&numOnce.cs);\n\n#if 0\n  /*       once thrd  prio change */\n  printf(\"once thrd  prio  error\\n\");\n#endif\n\n  /*\n   * Set the priority class to realtime - otherwise normal\n   * Windows random priority boosting will obscure any problems.\n   */\n  SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);\n  /* Set main thread to lower prio than threads */\n  SetThreadPriority(GetCurrentThread(), -2);\n\n  for (j = 0; j < NUM_ONCE; j++)\n    {\n      once[j] = o;\n\n      for (i = 0; i < NUM_THREADS; i++)\n        {\n\t  bag_t * bag = &threadbag[i][j];\n\t  bag->threadnum = i;\n\t  bag->oncenum = j;\n          /* GCC build: create was failing with EAGAIN after 790 threads */\n          while (0 != pthread_create(&t[i][j], NULL, mythread, (void *)bag))\n            sched_yield();\n        }\n    }\n\n  for (j = 0; j < NUM_ONCE; j++)\n    for (i = 0; i < NUM_THREADS; i++)\n      if (pthread_join(t[i][j], NULL) != 0)\n        printf(\"Join failed for [thread,once] = [%d,%d]\\n\", i, j);\n\n  /*\n   * All threads will cancel, none will return normally from\n   * pthread_once and so numThreads should never be incremented. However,\n   * numOnce should be incremented by every thread (NUM_THREADS*NUM_ONCE).\n   */\n  assert(numOnce.i == NUM_ONCE * NUM_THREADS);\n  assert(numThreads.i == 0);\n\n  DeleteCriticalSection(&numOnce.cs);\n  DeleteCriticalSection(&numThreads.cs);\n  DeleteCriticalSection(&print_lock);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/openmp1.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#ifdef _OPENMP\n#  include <omp.h>\n#endif\n#include <pthread.h>\n\nenum {\n  Size = 10000\n};\n\nconst int  ShouldSum = (Size-1)*Size/2;\n\nshort Verbose = 1;\n\nshort ThreadOK[3] = {0,0,0}; // Main, Thread1, Thread2\n\n// Thread\nvoid *_thread(void* Id) {\n  int i;\n  int x[Size];\n\n#ifdef _OPENMP\n#  pragma omp parallel for\n#endif\n  for ( i = 0; i < Size; i++ ) {\n#ifdef _OPENMP\n    if (Verbose && i%1000==0) {\n      int tid = omp_get_thread_num();\n#  pragma omp critical\n      printf(\"thread %d : tid %d handles %d\\n\",(int)Id,tid,i);\n    }\n#endif\n\n    x[i] = i;\n  }\n\n  int Sum=0;\n  for ( i = 0; i < Size; i++ ) {\n    Sum += x[i];\n  }\n  if (Verbose) {\n#ifdef _OPENMP\n#  pragma omp critical\n#endif\n    printf(\"Id %d : %s : %d(should be %d)\\n\",(int)Id, __FUNCTION__, Sum,ShouldSum);\n  }\n  if (Sum == ShouldSum) ThreadOK[(int)Id] = 1;\n  return NULL;\n}\n\n// MainThread\nvoid MainThread() {\n  int i;\n\n#ifdef _OPENMP\n#  pragma omp parallel for\n#endif\n  for ( i = 0; i < 4; i++ ) {\n#ifdef _OPENMP\n      int tid = omp_get_thread_num();\n#  pragma omp critical\n      printf(\"Main : tid %d\\n\",tid);\n      _thread((void *)tid);\n#endif\n  }\n  return;\n}\n\n// Comment in/out for checking the effect of multiple threads.\n#define SPAWN_THREADS\n\n// main\nint main(int argc, char *argv[]) {\n\n  if (argc>1) Verbose = 1;\n\n#ifdef _OPENMP\n  omp_set_nested(-1);\n  printf(\"%s%s%s\\n\", \"Nested parallel blocks are \", omp_get_nested()?\" \":\"NOT \", \"supported.\");\n#endif\n\n  MainThread();\n\n#ifdef SPAWN_THREADS\n  {\n    pthread_t a_thr;\n    pthread_t b_thr;\n    int status;\n\n    printf(\"%s:%d - %s - a_thr:%p - b_thr:%p\\n\",\n           __FILE__,__LINE__,__FUNCTION__,a_thr.p,b_thr.p);\n\n    status = pthread_create(&a_thr, NULL, _thread, (void*) 1 );\n    if ( status != 0 ) {\n      printf(\"Failed to create thread 1\\n\");\n      return (-1);\n    }\n\n    status = pthread_create(&b_thr, NULL, _thread, (void*) 2 );\n    if ( status != 0 ) {\n      printf(\"Failed to create thread 2\\n\");\n      return (-1);\n    }\n\n    status = pthread_join(a_thr, NULL);\n    if ( status != 0 ) {\n      printf(\"Failed to join thread 1\\n\");\n      return (-1);\n    }\n    printf(\"Joined thread1\\n\");\n\n    status = pthread_join(b_thr, NULL);\n    if ( status != 0 ) {\n      printf(\"Failed to join thread 2\\n\");\n      return (-1);\n    }\n    printf(\"Joined thread2\\n\");\n  }\n#endif // SPAWN_THREADS\n\n  short OK = 0;\n  // Check that we have OpenMP before declaring things OK formally.\n#ifdef _OPENMP\n    OK = 1;\n    {\n      short i;\n      for (i=0;i<3;i++) OK &= ThreadOK[i];\n    }\n    if (OK) printf(\"OMP : All looks good\\n\");\n    else printf(\"OMP : Error\\n\");\n#else\n    printf(\"OpenMP seems not enabled ...\\n\");\n#endif\n\n  return OK?0:1;\n}\n\n//g++ -fopenmp omp_test.c -o omp_test -lpthread\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/priority1.c",
    "content": "/*\n * File: priority1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test thread priority explicit setting using thread attribute.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\nenum {\n  PTW32TEST_THREAD_INIT_PRIO = 0,\n  PTW32TEST_MAXPRIORITIES = 512\n};\n\nint minPrio;\nint maxPrio;\nint validPriorities[PTW32TEST_MAXPRIORITIES];\n\nvoid *\nfunc(void * arg)\n{\n  int policy;\n  struct sched_param param;\n  pthread_t threadID = pthread_self();\n\n  assert(pthread_getschedparam(threadID, &policy, &param) == 0);\n  assert(policy == SCHED_OTHER);\n  return (void *) (size_t)(param.sched_priority);\n}\n\nvoid *\ngetValidPriorities(void * arg)\n{\n  int prioSet;\n  pthread_t threadID = pthread_self();\n  HANDLE threadH = pthread_getw32threadhandle_np(threadID);\n\n  printf(\"Using GetThreadPriority\\n\");\n  printf(\"%10s %10s\\n\", \"Set value\", \"Get value\");\n\n  for (prioSet = minPrio;\n       prioSet <= maxPrio;\n       prioSet++)\n    {\n\t/*\n       * If prioSet is invalid then the threads priority is unchanged\n       * from the previous value. Make the previous value a known\n       * one so that we can check later.\n       */\n        if (prioSet < 0)\n\t  SetThreadPriority(threadH, THREAD_PRIORITY_LOWEST);\n        else\n\t  SetThreadPriority(threadH, THREAD_PRIORITY_HIGHEST);\n\tSetThreadPriority(threadH, prioSet);\n\tvalidPriorities[prioSet+(PTW32TEST_MAXPRIORITIES/2)] = GetThreadPriority(threadH);\n\tprintf(\"%10d %10d\\n\", prioSet, validPriorities[prioSet+(PTW32TEST_MAXPRIORITIES/2)]);\n    }\n\n  return (void *) 0;\n}\n\n\nint\nmain()\n{\n  pthread_t t;\n  pthread_attr_t attr;\n  void * result = NULL;\n  struct sched_param param;\n\n  assert((maxPrio = sched_get_priority_max(SCHED_OTHER)) != -1);\n  assert((minPrio = sched_get_priority_min(SCHED_OTHER)) != -1);\n\n  assert(pthread_create(&t, NULL, getValidPriorities, NULL) == 0);\n  assert(pthread_join(t, &result) == 0);\n\n  assert(pthread_attr_init(&attr) == 0);\n  assert(pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED) == 0);\n\n  /* Set the thread's priority to a known initial value. */\n  SetThreadPriority(pthread_getw32threadhandle_np(pthread_self()),\n                    PTW32TEST_THREAD_INIT_PRIO);\n\n  printf(\"Using pthread_getschedparam\\n\");\n  printf(\"%10s %10s %10s\\n\", \"Set value\", \"Get value\", \"Win priority\");\n\n  for (param.sched_priority = minPrio;\n       param.sched_priority <= maxPrio;\n       param.sched_priority++)\n    {\n      int prio;\n\n      assert(pthread_attr_setschedparam(&attr, &param) == 0);\n      assert(pthread_create(&t, &attr, func, (void *) &attr) == 0);\n\n      assert((prio = GetThreadPriority(pthread_getw32threadhandle_np(t)))\n             == validPriorities[param.sched_priority+(PTW32TEST_MAXPRIORITIES/2)]);\n\n      assert(pthread_join(t, &result) == 0);\n\n      assert(param.sched_priority == (int)(size_t) result);\n      printf(\"%10d %10d %10d\\n\", param.sched_priority, (int)(size_t) result, prio);\n    }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/priority2.c",
    "content": "/*\n * File: priority2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test thread priority setting after creation.\n *\n * Test Method (Validation or Falsification):\n * - \n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\nenum {\n  PTW32TEST_THREAD_INIT_PRIO = 0,\n  PTW32TEST_MAXPRIORITIES = 512\n};\n\nint minPrio;\nint maxPrio;\nint validPriorities[PTW32TEST_MAXPRIORITIES];\npthread_barrier_t startBarrier, endBarrier;\n\nvoid * func(void * arg)\n{\n  int policy;\n  int result;\n  struct sched_param param;\n\n  result = pthread_barrier_wait(&startBarrier);\n  assert(result == 0 || result == PTHREAD_BARRIER_SERIAL_THREAD);\n  assert(pthread_getschedparam(pthread_self(), &policy, &param) == 0);\n  assert(policy == SCHED_OTHER);\n  result = pthread_barrier_wait(&endBarrier);\n  assert(result == 0 || result == PTHREAD_BARRIER_SERIAL_THREAD);\n  return (void *) (size_t)param.sched_priority;\n}\n\n\nvoid *\ngetValidPriorities(void * arg)\n{\n  int prioSet;\n  pthread_t thread = pthread_self();\n  HANDLE threadH = pthread_getw32threadhandle_np(thread);\n  struct sched_param param;\n\n  for (prioSet = minPrio;\n       prioSet <= maxPrio;\n       prioSet++)\n    {\n\t/*\n       * If prioSet is invalid then the threads priority is unchanged\n       * from the previous value. Make the previous value a known\n       * one so that we can check later.\n       */\n        param.sched_priority = prioSet;\n\tassert(pthread_setschedparam(thread, SCHED_OTHER, &param) == 0);\n\tvalidPriorities[prioSet+(PTW32TEST_MAXPRIORITIES/2)] = GetThreadPriority(threadH);\n    }\n\n  return (void *) 0;\n}\n\n\nint\nmain()\n{\n  pthread_t t;\n  void * result = NULL;\n  int result2;\n  struct sched_param param;\n\n  assert((maxPrio = sched_get_priority_max(SCHED_OTHER)) != -1);\n  assert((minPrio = sched_get_priority_min(SCHED_OTHER)) != -1);\n\n  assert(pthread_create(&t, NULL, getValidPriorities, NULL) == 0);\n  assert(pthread_join(t, &result) == 0);\n\n  assert(pthread_barrier_init(&startBarrier, NULL, 2) == 0);\n  assert(pthread_barrier_init(&endBarrier, NULL, 2) == 0);\n\n  /* Set the thread's priority to a known initial value.\n   * If the new priority is invalid then the threads priority\n   * is unchanged from the previous value.\n   */\n  SetThreadPriority(pthread_getw32threadhandle_np(pthread_self()),\n                    PTW32TEST_THREAD_INIT_PRIO);\n\n  for (param.sched_priority = minPrio;\n       param.sched_priority <= maxPrio;\n       param.sched_priority++)\n    {\n      assert(pthread_create(&t, NULL, func, NULL) == 0);\n      assert(pthread_setschedparam(t, SCHED_OTHER, &param) == 0);\n      result2 = pthread_barrier_wait(&startBarrier);\n      assert(result2 == 0 || result2 == PTHREAD_BARRIER_SERIAL_THREAD);\n      result2 = pthread_barrier_wait(&endBarrier);\n      assert(result2 == 0 || result2 == PTHREAD_BARRIER_SERIAL_THREAD);\n      assert(GetThreadPriority(pthread_getw32threadhandle_np(t)) ==\n\t  validPriorities[param.sched_priority+(PTW32TEST_MAXPRIORITIES/2)]);\n      pthread_join(t, &result);\n      assert(param.sched_priority == (int)(size_t)result);\n    }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/reuse1.c",
    "content": "/*\n * File: reuse1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Confirm that thread reuse works for joined threads.\n *\n * Test Method (Validation or Falsification):\n * -\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\nenum {\n\tNUMTHREADS = 100\n};\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  washere = 1;\n  return arg; \n}\n \nint\nmain()\n{\n  pthread_t t,\n            last_t;\n  pthread_attr_t attr;\n  void * result = NULL;\n  int i;\n\n  assert(pthread_attr_init(&attr) == 0);;\n  assert(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == 0);\n\n  washere = 0;\n  assert(pthread_create(&t, &attr, func, NULL) == 0);\n  assert(pthread_join(t, &result) == 0);;\n  assert((int)(size_t)result == 0);\n  assert(washere == 1);\n  last_t = t;\n\n  for (i = 1; i < NUMTHREADS; i++)\n    {\n      washere = 0;\n      assert(pthread_create(&t, &attr, func, (void *)(size_t)i) == 0);\n      pthread_join(t, &result);\n      assert((int)(size_t) result == i);\n      assert(washere == 1);\n      /* thread IDs should be unique */\n      assert(!pthread_equal(t, last_t));\n      /* thread struct pointers should be the same */\n      assert(t.p == last_t.p);\n      /* thread handle reuse counter should be different by one */\n      assert(t.x == last_t.x+1);\n      last_t = t;\n    }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/reuse2.c",
    "content": "/*\n * File: reuse2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test that thread reuse works for detached threads.\n * - Analyse thread struct reuse.\n *\n * Test Method (Validation or Falsification):\n * -\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * - This test is implementation specific\n * because it uses knowledge of internals that should be\n * opaque to an application.\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n/*\n */\n\nenum {\n\tNUMTHREADS = 10000\n};\n\n\nstatic long done = 0;\n\nvoid * func(void * arg)\n{\n  sched_yield();\n\n  InterlockedIncrement(&done);\n\n  return (void *) 0; \n}\n \nint\nmain()\n{\n  pthread_t t[NUMTHREADS];\n  pthread_attr_t attr;\n  int i;\n  unsigned int notUnique = 0,\n\t       totalHandles = 0,\n\t       reuseMax = 0,\n\t       reuseMin = NUMTHREADS;\n\n  assert(pthread_attr_init(&attr) == 0);\n  assert(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0);\n\n  for (i = 0; i < NUMTHREADS; i++)\n    {\n      while(pthread_create(&t[i], &attr, func, NULL) != 0)\n        Sleep(1);\n    }\n\n  while (NUMTHREADS > InterlockedExchangeAdd((LPLONG)&done, 0L))\n    Sleep(100);\n\n  Sleep(100);\n\n  /*\n   * Analyse reuse by computing min and max number of times pthread_create()\n   * returned the same pthread_t value.\n   */\n  for (i = 0; i < NUMTHREADS; i++)\n    {\n      if (t[i].p != NULL)\n        {\n          unsigned int j, thisMax;\n\n          thisMax = t[i].x;\n\n          for (j = i+1; j < NUMTHREADS; j++)\n            if (t[i].p == t[j].p)\n              {\n\t\tif (t[i].x == t[j].x)\n\t\t  notUnique++;\n                if (thisMax < t[j].x)\n                  thisMax = t[j].x;\n                t[j].p = NULL;\n              }\n\n          if (reuseMin > thisMax)\n            reuseMin = thisMax;\n\n          if (reuseMax < thisMax)\n            reuseMax = thisMax;\n        }\n    }\n\n  for (i = 0; i < NUMTHREADS; i++)\n    if (t[i].p != NULL)\n      totalHandles++;\n\n  /*\n   * pthread_t reuse counts start at 0, so we need to add 1\n   * to the max and min values derived above.\n   */\n  printf(\"For %d total threads:\\n\", NUMTHREADS);\n  printf(\"Non-unique IDs = %d\\n\", notUnique);\n  printf(\"Reuse maximum  = %d\\n\", reuseMax + 1);\n  printf(\"Reuse minimum  = %d\\n\", reuseMin + 1);\n  printf(\"Total handles  = %d\\n\", totalHandles);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/robust1.c",
    "content": "/* \n * robust1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * For all robust mutex types.\n * Thread A locks mutex\n * Thread A terminates with no threads waiting on robust mutex\n * Thread B acquires (inherits) mutex and unlocks\n * Main attempts to lock mutex with unrecovered state.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *\tpthread_mutex_init()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n *\tpthread_mutex_destroy()\n *\tpthread_mutexattr_init()\n *\tpthread_mutexattr_setrobust()\n *\tpthread_mutexattr_settype()\n *\tpthread_mutexattr_destroy()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\n\nvoid * owner(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n\n  return 0;\n}\n \nvoid * inheritor(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == EOWNERDEAD);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return 0;\n}\n \nint\nmain()\n{\n  pthread_t to, ti;\n  pthread_mutexattr_t ma;\n\n  assert(pthread_mutexattr_init(&ma) == 0);\n  assert(pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST) == 0);\n\n  /* Default (NORMAL) type */ \n  lockCount = 0;\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == ENOTRECOVERABLE);\n  assert(pthread_mutex_unlock(&mutex) == EPERM);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  /* NORMAL type */ \n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_NORMAL) == 0);\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == ENOTRECOVERABLE);\n  assert(pthread_mutex_unlock(&mutex) == EPERM);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  /* ERRORCHECK type */ \n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK) == 0);\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == ENOTRECOVERABLE);\n  assert(pthread_mutex_unlock(&mutex) == EPERM);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  /* RECURSIVE type */ \n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE) == 0);\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == ENOTRECOVERABLE);\n  assert(pthread_mutex_unlock(&mutex) == EPERM);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  assert(pthread_mutexattr_destroy(&ma) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/robust2.c",
    "content": "/* \n * robust2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * For all robust mutex types.\n * Thread A locks mutex\n * Thread B blocks on mutex\n * Thread A terminates with threads waiting on robust mutex\n * Thread B awakes and inherits mutex and unlocks\n * Main attempts to lock mutex with unrecovered state.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *\tpthread_mutex_init()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n *\tpthread_mutex_destroy()\n *\tpthread_mutexattr_init()\n *\tpthread_mutexattr_setrobust()\n *\tpthread_mutexattr_settype()\n *\tpthread_mutexattr_destroy()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\n\nvoid * owner(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  Sleep(200);\n\n  return 0;\n}\n \nvoid * inheritor(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == EOWNERDEAD);\n  lockCount++;\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return 0;\n}\n \nint\nmain()\n{\n  pthread_t to, ti;\n  pthread_mutexattr_t ma;\n\n  assert(pthread_mutexattr_init(&ma) == 0);\n  assert(pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST) == 0);\n\n  /* Default (NORMAL) type */ \n  lockCount = 0;\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == ENOTRECOVERABLE);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  /* NORMAL type */ \n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_NORMAL) == 0);\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == ENOTRECOVERABLE);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  /* ERRORCHECK type */ \n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK) == 0);\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == ENOTRECOVERABLE);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  /* RECURSIVE type */ \n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE) == 0);\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == ENOTRECOVERABLE);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  assert(pthread_mutexattr_destroy(&ma) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/robust3.c",
    "content": "/* \n * robust3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * For all robust mutex types.\n * Thread A locks mutex\n * Thread B blocks on mutex\n * Thread A terminates with threads waiting on robust mutex\n * Thread B awakes and inherits mutex, sets consistent and unlocks\n * Main acquires mutex with recovered state.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *\tpthread_mutex_init()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n *\tpthread_mutex_consistent()\n *\tpthread_mutex_destroy()\n *\tpthread_mutexattr_init()\n *\tpthread_mutexattr_setrobust()\n *\tpthread_mutexattr_settype()\n *\tpthread_mutexattr_destroy()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex;\n\nvoid * owner(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == 0);\n  lockCount++;\n  Sleep(200);\n\n  return 0;\n}\n \nvoid * inheritor(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex) == EOWNERDEAD);\n  lockCount++;\n  assert(pthread_mutex_consistent(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == 0);\n\n  return 0;\n}\n \nint\nmain()\n{\n  pthread_t to, ti;\n  pthread_mutexattr_t ma;\n\n  assert(pthread_mutexattr_init(&ma) == 0);\n  assert(pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST) == 0);\n\n  /* Default (NORMAL) type */ \n  lockCount = 0;\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  /* NORMAL type */ \n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_NORMAL) == 0);\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  /* ERRORCHECK type */ \n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK) == 0);\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  /* RECURSIVE type */ \n  lockCount = 0;\n  assert(pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE) == 0);\n  assert(pthread_mutex_init(&mutex, &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 2);\n  assert(pthread_mutex_lock(&mutex) == 0);\n  assert(pthread_mutex_unlock(&mutex) == 0);\n  assert(pthread_mutex_destroy(&mutex) == 0);\n\n  assert(pthread_mutexattr_destroy(&ma) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/robust4.c",
    "content": "/* \n * robust4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Thread A locks multiple robust mutexes\n * Thread B blocks on same mutexes in different orderings\n * Thread A terminates with thread waiting on mutexes\n * Thread B awakes and inherits each mutex in turn, sets consistent and unlocks\n * Main acquires mutexes with recovered state.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *\tpthread_mutex_init()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n *\tpthread_mutex_destroy()\n *\tpthread_mutexattr_init()\n *\tpthread_mutexattr_setrobust()\n *\tpthread_mutexattr_settype()\n *\tpthread_mutexattr_destroy()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex[3];\n\nvoid * owner(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex[0]) == 0);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex[1]) == 0);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex[2]) == 0);\n  lockCount++;\n  Sleep(200);\n\n  return 0;\n}\n \nvoid * inheritor(void * arg)\n{\n  int* o = (int*)arg;\n\n  assert(pthread_mutex_lock(&mutex[o[0]]) == EOWNERDEAD);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex[o[1]]) == EOWNERDEAD);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex[o[2]]) == EOWNERDEAD);\n  lockCount++;\n  assert(pthread_mutex_consistent(&mutex[o[2]]) == 0);\n  assert(pthread_mutex_consistent(&mutex[o[1]]) == 0);\n  assert(pthread_mutex_consistent(&mutex[o[0]]) == 0);\n  assert(pthread_mutex_unlock(&mutex[o[2]]) == 0);\n  assert(pthread_mutex_unlock(&mutex[o[1]]) == 0);\n  assert(pthread_mutex_unlock(&mutex[o[0]]) == 0);\n\n  return 0;\n}\n \nint\nmain()\n{\n  pthread_t to, ti;\n  pthread_mutexattr_t ma;\n  int order[3];\n\n  assert(pthread_mutexattr_init(&ma) == 0);\n  assert(pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST) == 0);\n\n  order[0]=0;\n  order[1]=1;\n  order[2]=2;\n  lockCount = 0;\n  assert(pthread_mutex_init(&mutex[0], &ma) == 0);\n  assert(pthread_mutex_init(&mutex[1], &ma) == 0);\n  assert(pthread_mutex_init(&mutex[2], &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, (void *)order) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 6);\n  assert(pthread_mutex_lock(&mutex[0]) == 0);\n  assert(pthread_mutex_unlock(&mutex[0]) == 0);\n  assert(pthread_mutex_destroy(&mutex[0]) == 0);\n  assert(pthread_mutex_lock(&mutex[1]) == 0);\n  assert(pthread_mutex_unlock(&mutex[1]) == 0);\n  assert(pthread_mutex_destroy(&mutex[1]) == 0);\n  assert(pthread_mutex_lock(&mutex[2]) == 0);\n  assert(pthread_mutex_unlock(&mutex[2]) == 0);\n  assert(pthread_mutex_destroy(&mutex[2]) == 0);\n\n  order[0]=1;\n  order[1]=0;\n  order[2]=2;\n  lockCount = 0;\n  assert(pthread_mutex_init(&mutex[0], &ma) == 0);\n  assert(pthread_mutex_init(&mutex[1], &ma) == 0);\n  assert(pthread_mutex_init(&mutex[2], &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, (void *)order) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 6);\n  assert(pthread_mutex_lock(&mutex[0]) == 0);\n  assert(pthread_mutex_unlock(&mutex[0]) == 0);\n  assert(pthread_mutex_destroy(&mutex[0]) == 0);\n  assert(pthread_mutex_lock(&mutex[1]) == 0);\n  assert(pthread_mutex_unlock(&mutex[1]) == 0);\n  assert(pthread_mutex_destroy(&mutex[1]) == 0);\n  assert(pthread_mutex_lock(&mutex[2]) == 0);\n  assert(pthread_mutex_unlock(&mutex[2]) == 0);\n  assert(pthread_mutex_destroy(&mutex[2]) == 0);\n\n  order[0]=0;\n  order[1]=2;\n  order[2]=1;\n  lockCount = 0;\n  assert(pthread_mutex_init(&mutex[0], &ma) == 0);\n  assert(pthread_mutex_init(&mutex[1], &ma) == 0);\n  assert(pthread_mutex_init(&mutex[2], &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, (void *)order) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 6);\n  assert(pthread_mutex_lock(&mutex[0]) == 0);\n  assert(pthread_mutex_unlock(&mutex[0]) == 0);\n  assert(pthread_mutex_destroy(&mutex[0]) == 0);\n  assert(pthread_mutex_lock(&mutex[1]) == 0);\n  assert(pthread_mutex_unlock(&mutex[1]) == 0);\n  assert(pthread_mutex_destroy(&mutex[1]) == 0);\n  assert(pthread_mutex_lock(&mutex[2]) == 0);\n  assert(pthread_mutex_unlock(&mutex[2]) == 0);\n  assert(pthread_mutex_destroy(&mutex[2]) == 0);\n\n  order[0]=2;\n  order[1]=1;\n  order[2]=0;\n  lockCount = 0;\n  assert(pthread_mutex_init(&mutex[0], &ma) == 0);\n  assert(pthread_mutex_init(&mutex[1], &ma) == 0);\n  assert(pthread_mutex_init(&mutex[2], &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&ti, NULL, inheritor, (void *)order) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 6);\n  assert(pthread_mutex_lock(&mutex[0]) == 0);\n  assert(pthread_mutex_unlock(&mutex[0]) == 0);\n  assert(pthread_mutex_destroy(&mutex[0]) == 0);\n  assert(pthread_mutex_lock(&mutex[1]) == 0);\n  assert(pthread_mutex_unlock(&mutex[1]) == 0);\n  assert(pthread_mutex_destroy(&mutex[1]) == 0);\n  assert(pthread_mutex_lock(&mutex[2]) == 0);\n  assert(pthread_mutex_unlock(&mutex[2]) == 0);\n  assert(pthread_mutex_destroy(&mutex[2]) == 0);\n\n  assert(pthread_mutexattr_destroy(&ma) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/robust5.c",
    "content": "/* \n * robust5.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Thread A locks multiple robust mutexes\n * Thread B blocks on same mutexes\n * Thread A terminates with thread waiting on mutexes\n * Thread B awakes and inherits each mutex in turn\n * Thread B terminates leaving orphaned mutexes\n * Main inherits mutexes, sets consistent and unlocks.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *\tpthread_mutex_init()\n *\tpthread_mutex_lock()\n *\tpthread_mutex_unlock()\n *\tpthread_mutex_destroy()\n *\tpthread_mutexattr_init()\n *\tpthread_mutexattr_setrobust()\n *\tpthread_mutexattr_settype()\n *\tpthread_mutexattr_destroy()\n */\n\n#include \"test.h\"\n\nstatic int lockCount;\n\nstatic pthread_mutex_t mutex[3];\n\nvoid * owner(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex[0]) == 0);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex[1]) == 0);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex[2]) == 0);\n  lockCount++;\n\n  return 0;\n}\n \nvoid * inheritor(void * arg)\n{\n  assert(pthread_mutex_lock(&mutex[0]) == EOWNERDEAD);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex[1]) == EOWNERDEAD);\n  lockCount++;\n  assert(pthread_mutex_lock(&mutex[2]) == EOWNERDEAD);\n  lockCount++;\n\n  return 0;\n}\n \nint\nmain()\n{\n  pthread_t to, ti;\n  pthread_mutexattr_t ma;\n\n  assert(pthread_mutexattr_init(&ma) == 0);\n  assert(pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST) == 0);\n\n  lockCount = 0;\n  assert(pthread_mutex_init(&mutex[0], &ma) == 0);\n  assert(pthread_mutex_init(&mutex[1], &ma) == 0);\n  assert(pthread_mutex_init(&mutex[2], &ma) == 0);\n  assert(pthread_create(&to, NULL, owner, NULL) == 0);\n  assert(pthread_join(to, NULL) == 0);\n  assert(pthread_create(&ti, NULL, inheritor, NULL) == 0);\n  assert(pthread_join(ti, NULL) == 0);\n  assert(lockCount == 6);\n  assert(pthread_mutex_lock(&mutex[0]) == EOWNERDEAD);\n  assert(pthread_mutex_consistent(&mutex[0]) == 0);\n  assert(pthread_mutex_unlock(&mutex[0]) == 0);\n  assert(pthread_mutex_destroy(&mutex[0]) == 0);\n  assert(pthread_mutex_lock(&mutex[1]) == EOWNERDEAD);\n  assert(pthread_mutex_consistent(&mutex[1]) == 0);\n  assert(pthread_mutex_unlock(&mutex[1]) == 0);\n  assert(pthread_mutex_destroy(&mutex[1]) == 0);\n  assert(pthread_mutex_lock(&mutex[2]) == EOWNERDEAD);\n  assert(pthread_mutex_consistent(&mutex[2]) == 0);\n  assert(pthread_mutex_unlock(&mutex[2]) == 0);\n  assert(pthread_mutex_destroy(&mutex[2]) == 0);\n\n  assert(pthread_mutexattr_destroy(&ma) == 0);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock1.c",
    "content": "/* \n * rwlock1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Create a simple rwlock object and then destroy it.\n *\n * Depends on API functions:\n * \tpthread_rwlock_init()\n *\tpthread_rwlock_destroy()\n */\n\n#include \"test.h\"\n\npthread_rwlock_t rwlock = NULL;\n\nint\nmain()\n{\n  assert(rwlock == NULL);\n\n  assert(pthread_rwlock_init(&rwlock, NULL) == 0);\n\n  assert(rwlock != NULL);\n\n  assert(pthread_rwlock_destroy(&rwlock) == 0);\n\n  assert(rwlock == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock2.c",
    "content": "/* \n * rwlock2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static rwlock object, lock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_rwlock_rdlock()\n *\tpthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n \npthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;\n\nint\nmain()\n{\n  assert(rwlock == PTHREAD_RWLOCK_INITIALIZER);\n\n  assert(pthread_rwlock_rdlock(&rwlock) == 0);\n\n  assert(rwlock != PTHREAD_RWLOCK_INITIALIZER);\n\n  assert(rwlock != NULL);\n\n  assert(pthread_rwlock_unlock(&rwlock) == 0);\n\n  assert(pthread_rwlock_destroy(&rwlock) == 0);\n\n  assert(rwlock == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock2_t.c",
    "content": "/* \n * rwlock2_t.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static rwlock object, timed-lock it, \n * and then unlock it again.\n *\n * Depends on API functions:\n *\tpthread_rwlock_timedrdlock()\n *\tpthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\npthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;\n\nint\nmain()\n{\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 1;\n\n  assert(rwlock == PTHREAD_RWLOCK_INITIALIZER);\n\n  assert(pthread_rwlock_timedrdlock(&rwlock, &abstime) == 0);\n\n  assert(rwlock != PTHREAD_RWLOCK_INITIALIZER);\n\n  assert(rwlock != NULL);\n\n  assert(pthread_rwlock_unlock(&rwlock) == 0);\n\n  assert(pthread_rwlock_destroy(&rwlock) == 0);\n\n  assert(rwlock == NULL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock3.c",
    "content": "/* \n * rwlock3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static rwlock object, wrlock it, trywrlock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_create()\n *\tpthread_join()\n *\tpthread_rwlock_wrlock()\n *\tpthread_rwlock_trywrlock()\n *\tpthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n \npthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  assert(pthread_rwlock_trywrlock(&rwlock1) == EBUSY);\n\n  washere = 1;\n\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_rwlock_wrlock(&rwlock1) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n\n  assert(pthread_join(t, NULL) == 0);\n\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock3_t.c",
    "content": "/* \n * rwlock3_t.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static rwlock object, timed-wrlock it, trywrlock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_rwlock_timedwrlock()\n *\tpthread_rwlock_trywrlock()\n *\tpthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\npthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  assert(pthread_rwlock_trywrlock(&rwlock1) == EBUSY);\n\n  washere = 1;\n\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 1;\n\n  assert(pthread_rwlock_timedwrlock(&rwlock1, &abstime) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n\n  Sleep(2000);\n\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock4.c",
    "content": "/* \n * rwlock4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static rwlock object, rdlock it, trywrlock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n * \tpthread_create()\n * \tpthread_join()\n *\tpthread_rwlock_rdlock()\n *\tpthread_rwlock_trywrlock()\n *\tpthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n \npthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  assert(pthread_rwlock_trywrlock(&rwlock1) == EBUSY);\n\n  washere = 1;\n\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_rwlock_rdlock(&rwlock1) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n\n  assert(pthread_join(t, NULL) == 0);\n\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock4_t.c",
    "content": "/* \n * rwlock4_t.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static rwlock object, timed-rdlock it, trywrlock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_rwlock_timedrdlock()\n *\tpthread_rwlock_trywrlock()\n *\tpthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\npthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  assert(pthread_rwlock_trywrlock(&rwlock1) == EBUSY);\n\n  washere = 1;\n\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 1;\n\n  assert(pthread_rwlock_timedrdlock(&rwlock1, &abstime) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n\n  Sleep(2000);\n\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock5.c",
    "content": "/* \n * rwlock5.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static rwlock object, rdlock it, tryrdlock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *      pthread_create()\n *      pthread_join()\n *\tpthread_rwlock_rdlock()\n *\tpthread_rwlock_tryrdlock()\n *\tpthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n \npthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  assert(pthread_rwlock_tryrdlock(&rwlock1) == 0);\n\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  washere = 1;\n\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_rwlock_rdlock(&rwlock1) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n\n  assert(pthread_join(t, NULL) == 0);\n\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock5_t.c",
    "content": "/* \n * rwlock5_t.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static rwlock object, timed-rdlock it, tryrdlock it, \n * and then unlock it again.\n *\n * Depends on API functions: \n *\tpthread_rwlock_timedrdlock()\n *\tpthread_rwlock_tryrdlock()\n *\tpthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\npthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  assert(pthread_rwlock_tryrdlock(&rwlock1) == 0);\n\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  washere = 1;\n\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 1;\n\n  assert(pthread_rwlock_timedrdlock(&rwlock1, &abstime) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n\n  Sleep(2000);\n\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock6.c",
    "content": "/*\n * rwlock6.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Check writer and reader locking\n *\n * Depends on API functions:\n *      pthread_rwlock_rdlock()\n *      pthread_rwlock_wrlock()\n *      pthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n\nstatic pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;\n\nstatic int bankAccount = 0;\n\nvoid * wrfunc(void * arg)\n{\n  int ba;\n\n  assert(pthread_rwlock_wrlock(&rwlock1) == 0);\n  Sleep(200);\n  bankAccount += 10;\n  ba = bankAccount;\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  return ((void *)(size_t)ba);\n}\n\nvoid * rdfunc(void * arg)\n{\n  int ba;\n\n  assert(pthread_rwlock_rdlock(&rwlock1) == 0);\n  ba = bankAccount;\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  return ((void *)(size_t)ba);\n}\n\nint\nmain()\n{\n  pthread_t wrt1;\n  pthread_t wrt2;\n  pthread_t rdt;\n  void* wr1Result = (void*)0;\n  void* wr2Result = (void*)0;\n  void* rdResult = (void*)0;\n\n  bankAccount = 0;\n\n  assert(pthread_create(&wrt1, NULL, wrfunc, NULL) == 0);\n  Sleep(50);\n  assert(pthread_create(&rdt, NULL, rdfunc, NULL) == 0);\n  Sleep(50);\n  assert(pthread_create(&wrt2, NULL, wrfunc, NULL) == 0);\n\n  assert(pthread_join(wrt1, &wr1Result) == 0);\n  assert(pthread_join(rdt, &rdResult) == 0);\n  assert(pthread_join(wrt2, &wr2Result) == 0);\n\n  assert((int)(size_t)wr1Result == 10);\n  assert((int)(size_t)rdResult == 10);\n  assert((int)(size_t)wr2Result == 20);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock6_t.c",
    "content": "/*\n * rwlock6_t.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Check writer and reader locking with reader timeouts\n *\n * Depends on API functions: \n *      pthread_rwlock_timedrdlock()\n *      pthread_rwlock_wrlock()\n *      pthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\nstatic pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;\n\nstatic int bankAccount = 0;\n\nvoid * wrfunc(void * arg)\n{\n  assert(pthread_rwlock_wrlock(&rwlock1) == 0);\n  Sleep(2000);\n  bankAccount += 10;\n  assert(pthread_rwlock_unlock(&rwlock1) == 0);\n\n  return ((void *)(size_t)bankAccount);\n}\n\nvoid * rdfunc(void * arg)\n{\n  int ba = -1;\n  struct timespec abstime = { 0, 0 };\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n\n  if ((int) (size_t)arg == 1)\n    {\n      abstime.tv_sec += 1;\n      assert(pthread_rwlock_timedrdlock(&rwlock1, &abstime) == ETIMEDOUT);\n      ba = 0;\n    }\n  else if ((int) (size_t)arg == 2)\n    {\n      abstime.tv_sec += 3;\n      assert(pthread_rwlock_timedrdlock(&rwlock1, &abstime) == 0);\n      ba = bankAccount;\n      assert(pthread_rwlock_unlock(&rwlock1) == 0);\n    }\n\n  return ((void *)(size_t)ba);\n}\n\nint\nmain()\n{\n  pthread_t wrt1;\n  pthread_t wrt2;\n  pthread_t rdt1;\n  pthread_t rdt2;\n  void* wr1Result = (void*)0;\n  void* wr2Result = (void*)0;\n  void* rd1Result = (void*)0;\n  void* rd2Result = (void*)0;\n\n  bankAccount = 0;\n\n  assert(pthread_create(&wrt1, NULL, wrfunc, NULL) == 0);\n  Sleep(500);\n  assert(pthread_create(&rdt1, NULL, rdfunc, (void *)(size_t)1) == 0);\n  Sleep(500);\n  assert(pthread_create(&wrt2, NULL, wrfunc, NULL) == 0);\n  Sleep(500);\n  assert(pthread_create(&rdt2, NULL, rdfunc, (void *)(size_t)2) == 0);\n\n  assert(pthread_join(wrt1, &wr1Result) == 0);\n  assert(pthread_join(rdt1, &rd1Result) == 0);\n  assert(pthread_join(wrt2, &wr2Result) == 0);\n  assert(pthread_join(rdt2, &rd2Result) == 0);\n\n  assert((int)(size_t)wr1Result == 10);\n  assert((int)(size_t)rd1Result == 0);\n  assert((int)(size_t)wr2Result == 20);\n  assert((int)(size_t)rd2Result == 20);\n\n  return 0;\n}\n\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock6_t2.c",
    "content": "/*\n * rwlock6_t2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Check writer and reader timeouts.\n *\n * Depends on API functions: \n *      pthread_rwlock_timedrdlock()\n *      pthread_rwlock_timedwrlock()\n *      pthread_rwlock_unlock()\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\nstatic pthread_rwlock_t rwlock1 = PTHREAD_RWLOCK_INITIALIZER;\n\nstatic int bankAccount = 0;\nstruct timespec abstime = { 0, 0 };\n\nvoid * wrfunc(void * arg)\n{\n  int result;\n\n  result = pthread_rwlock_timedwrlock(&rwlock1, &abstime);\n  if ((int) (size_t)arg == 1)\n    {\n      assert(result == 0);\n      Sleep(2000);\n      bankAccount += 10;\n      assert(pthread_rwlock_unlock(&rwlock1) == 0);\n      return ((void *)(size_t)bankAccount);\n    }\n  else if ((int) (size_t)arg == 2)\n    {\n      assert(result == ETIMEDOUT);\n      return ((void *) 100);\n    }\n\n  return ((void *)(size_t)-1);\n}\n\nvoid * rdfunc(void * arg)\n{\n  int ba = 0;\n\n  assert(pthread_rwlock_timedrdlock(&rwlock1, &abstime) == ETIMEDOUT);\n\n  return ((void *)(size_t)ba);\n}\n\nint\nmain()\n{\n  pthread_t wrt1;\n  pthread_t wrt2;\n  pthread_t rdt;\n  void* wr1Result = (void*)0;\n  void* wr2Result = (void*)0;\n  void* rdResult = (void*)0;\n  PTW32_STRUCT_TIMEB currSysTime;\n  const DWORD NANOSEC_PER_MILLISEC = 1000000;\n\n  PTW32_FTIME(&currSysTime);\n\n  abstime.tv_sec = (long)currSysTime.time;\n  abstime.tv_nsec = NANOSEC_PER_MILLISEC * currSysTime.millitm;\n\n  abstime.tv_sec += 1;\n\n  bankAccount = 0;\n\n  assert(pthread_create(&wrt1, NULL, wrfunc, (void *)(size_t)1) == 0);\n  Sleep(100);\n  assert(pthread_create(&rdt, NULL, rdfunc, NULL) == 0);\n  Sleep(100);\n  assert(pthread_create(&wrt2, NULL, wrfunc, (void *)(size_t)2) == 0);\n\n  assert(pthread_join(wrt1, &wr1Result) == 0);\n  assert(pthread_join(rdt, &rdResult) == 0);\n  assert(pthread_join(wrt2, &wr2Result) == 0);\n\n  assert((int)(size_t)wr1Result == 10);\n  assert((int)(size_t)rdResult == 0);\n  assert((int)(size_t)wr2Result == 100);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock7.c",
    "content": "/*\n * rwlock7.c\n *\n * Hammer on a bunch of rwlocks to test robustness and fairness.\n * Printed stats should be roughly even for each thread.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n#ifdef __GNUC__\n#include <stdlib.h>\n#endif\n\n#define THREADS         5\n#define DATASIZE        7\n#define ITERATIONS      1000000\n\n/*\n * Keep statistics for each thread.\n */\ntypedef struct thread_tag {\n  int         thread_num;\n  pthread_t   thread_id;\n  int         updates;\n  int         reads;\n  int         changed;\n  int         seed;\n} thread_t;\n\n/*\n * Read-write lock and shared data\n */\ntypedef struct data_tag {\n  pthread_rwlock_t    lock;\n  int                 data;\n  int                 updates;\n} data_t;\n\nstatic thread_t threads[THREADS];\nstatic data_t data[DATASIZE];\n\n/*\n * Thread start routine that uses read-write locks\n */\nvoid *thread_routine (void *arg)\n{\n  thread_t *self = (thread_t*)arg;\n  int iteration;\n  int element = 0;\n  int seed = self->seed;\n  int interval = 1 + rand_r (&seed) % 71;\n\n  self->changed = 0;\n\n  for (iteration = 0; iteration < ITERATIONS; iteration++)\n    {\n      if (iteration % (ITERATIONS / 10) == 0)\n        {\n          putchar('.');\n          fflush(stdout);\n        }\n      /*\n       * Each \"self->interval\" iterations, perform an\n       * update operation (write lock instead of read\n       * lock).\n       */\n      if ((iteration % interval) == 0)\n        {\n          assert(pthread_rwlock_wrlock (&data[element].lock) == 0);\n          data[element].data = self->thread_num;\n          data[element].updates++;\n          self->updates++;\n\t  interval = 1 + rand_r (&seed) % 71;\n          assert(pthread_rwlock_unlock (&data[element].lock) == 0);\n        } else {\n          /*\n           * Look at the current data element to see whether\n           * the current thread last updated it. Count the\n           * times, to report later.\n           */\n          assert(pthread_rwlock_rdlock (&data[element].lock) == 0);\n\n          self->reads++;\n\n          if (data[element].data != self->thread_num)\n            {\n              self->changed++;\n\t      interval = 1 + self->changed % 71;\n            }\n\n          assert(pthread_rwlock_unlock (&data[element].lock) == 0);\n        }\n\n      element = (element + 1) % DATASIZE;\n\n    }\n\n  return NULL;\n}\n\nint\nmain (int argc, char *argv[])\n{\n  int count;\n  int data_count;\n  int thread_updates = 0;\n  int data_updates = 0;\n  int seed = 1;\n\n  PTW32_STRUCT_TIMEB currSysTime1;\n  PTW32_STRUCT_TIMEB currSysTime2;\n\n  /*\n   * Initialize the shared data.\n   */\n  for (data_count = 0; data_count < DATASIZE; data_count++)\n    {\n      data[data_count].data = 0;\n      data[data_count].updates = 0;\n\n      assert(pthread_rwlock_init (&data[data_count].lock, NULL) == 0);\n    }\n\n  PTW32_FTIME(&currSysTime1);\n\n  /*\n   * Create THREADS threads to access shared data.\n   */\n  for (count = 0; count < THREADS; count++)\n    {\n      threads[count].thread_num = count;\n      threads[count].updates = 0;\n      threads[count].reads = 0;\n      threads[count].seed = 1 + rand_r (&seed) % 71;\n\n      assert(pthread_create (&threads[count].thread_id,\n                             NULL, thread_routine, (void*)(size_t)&threads[count]) == 0);\n    }\n\n  /*\n   * Wait for all threads to complete, and collect\n   * statistics.\n   */\n  for (count = 0; count < THREADS; count++)\n    {\n      assert(pthread_join (threads[count].thread_id, NULL) == 0);\n    }\n\n  putchar('\\n');\n  fflush(stdout);\n\n  for (count = 0; count < THREADS; count++)\n    {\n      if (threads[count].changed > 0)\n        {\n          printf (\"Thread %d found changed elements %d times\\n\",\n                  count, threads[count].changed);\n        }\n    }\n\n  putchar('\\n');\n  fflush(stdout);\n\n  for (count = 0; count < THREADS; count++)\n    {\n      thread_updates += threads[count].updates;\n      printf (\"%02d: seed %d, updates %d, reads %d\\n\",\n              count, threads[count].seed,\n              threads[count].updates, threads[count].reads);\n    }\n\n  putchar('\\n');\n  fflush(stdout);\n\n  /*\n   * Collect statistics for the data.\n   */\n  for (data_count = 0; data_count < DATASIZE; data_count++)\n    {\n      data_updates += data[data_count].updates;\n      printf (\"data %02d: value %d, %d updates\\n\",\n              data_count, data[data_count].data, data[data_count].updates);\n      assert(pthread_rwlock_destroy (&data[data_count].lock) == 0);\n    }\n\n  printf (\"%d thread updates, %d data updates\\n\",\n          thread_updates, data_updates);\n\n  PTW32_FTIME(&currSysTime2);\n\n  printf( \"\\nstart: %ld/%d, stop: %ld/%d, duration:%ld\\n\",\n          (long)currSysTime1.time,currSysTime1.millitm,\n          (long)currSysTime2.time,currSysTime2.millitm,\n          ((long)((currSysTime2.time*1000+currSysTime2.millitm) -\n          (currSysTime1.time*1000+currSysTime1.millitm))));\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/rwlock8.c",
    "content": "/*\n * rwlock8.c\n *\n * Hammer on a bunch of rwlocks to test robustness and fairness.\n * Printed stats should be roughly even for each thread.\n *\n * Yield during each access to exercise lock contention code paths\n * more than rwlock7.c does (particularly on uni-processor systems).\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n\n#ifdef __GNUC__\n#include <stdlib.h>\n#endif\n\n#define THREADS         5\n#define DATASIZE        7\n#define ITERATIONS      100000\n\n/*\n * Keep statistics for each thread.\n */\ntypedef struct thread_tag {\n  int         thread_num;\n  pthread_t   thread_id;\n  int         updates;\n  int         reads;\n  int         changed;\n  int         seed;\n} thread_t;\n\n/*\n * Read-write lock and shared data\n */\ntypedef struct data_tag {\n  pthread_rwlock_t    lock;\n  int                 data;\n  int                 updates;\n} data_t;\n\nstatic thread_t threads[THREADS];\nstatic data_t data[DATASIZE];\n\n/*\n * Thread start routine that uses read-write locks\n */\nvoid *thread_routine (void *arg)\n{\n  thread_t *self = (thread_t*)arg;\n  int iteration;\n  int element = 0;\n  int seed = self->seed;\n  int interval = 1 + rand_r (&seed) % 71;\n\n  self->changed = 0;\n\n  for (iteration = 0; iteration < ITERATIONS; iteration++)\n    {\n      if (iteration % (ITERATIONS / 10) == 0)\n        {\n          putchar('.');\n          fflush(stdout);\n        }\n      /*\n       * Each \"self->interval\" iterations, perform an\n       * update operation (write lock instead of read\n       * lock).\n       */\n      if ((iteration % interval) == 0)\n        {\n          assert(pthread_rwlock_wrlock (&data[element].lock) == 0);\n          data[element].data = self->thread_num;\n          data[element].updates++;\n          self->updates++;\n\t  interval = 1 + rand_r (&seed) % 71;\n\t  sched_yield();\n          assert(pthread_rwlock_unlock (&data[element].lock) == 0);\n        } else {\n          /*\n           * Look at the current data element to see whether\n           * the current thread last updated it. Count the\n           * times, to report later.\n           */\n          assert(pthread_rwlock_rdlock (&data[element].lock) == 0);\n\n          self->reads++;\n\n          if (data[element].data != self->thread_num)\n            {\n              self->changed++;\n\t      interval = 1 + self->changed % 71;\n            }\n\n\t  sched_yield();\n\n          assert(pthread_rwlock_unlock (&data[element].lock) == 0);\n        }\n\n      element = (element + 1) % DATASIZE;\n\n    }\n\n  return NULL;\n}\n\nint\nmain (int argc, char *argv[])\n{\n  int count;\n  int data_count;\n  int thread_updates = 0;\n  int data_updates = 0;\n  int seed = 1;\n\n  PTW32_STRUCT_TIMEB currSysTime1;\n  PTW32_STRUCT_TIMEB currSysTime2;\n\n  /*\n   * Initialize the shared data.\n   */\n  for (data_count = 0; data_count < DATASIZE; data_count++)\n    {\n      data[data_count].data = 0;\n      data[data_count].updates = 0;\n\n      assert(pthread_rwlock_init (&data[data_count].lock, NULL) == 0);\n    }\n\n  PTW32_FTIME(&currSysTime1);\n\n  /*\n   * Create THREADS threads to access shared data.\n   */\n  for (count = 0; count < THREADS; count++)\n    {\n      threads[count].thread_num = count;\n      threads[count].updates = 0;\n      threads[count].reads = 0;\n      threads[count].seed = 1 + rand_r (&seed) % 71;\n\n      assert(pthread_create (&threads[count].thread_id,\n                             NULL, thread_routine, (void*)(size_t)&threads[count]) == 0);\n    }\n\n  /*\n   * Wait for all threads to complete, and collect\n   * statistics.\n   */\n  for (count = 0; count < THREADS; count++)\n    {\n      assert(pthread_join (threads[count].thread_id, NULL) == 0);\n    }\n\n  putchar('\\n');\n  fflush(stdout);\n\n  for (count = 0; count < THREADS; count++)\n    {\n      if (threads[count].changed > 0)\n        {\n          printf (\"Thread %d found changed elements %d times\\n\",\n                  count, threads[count].changed);\n        }\n    }\n\n  putchar('\\n');\n  fflush(stdout);\n\n  for (count = 0; count < THREADS; count++)\n    {\n      thread_updates += threads[count].updates;\n      printf (\"%02d: seed %d, updates %d, reads %d\\n\",\n              count, threads[count].seed,\n              threads[count].updates, threads[count].reads);\n    }\n\n  putchar('\\n');\n  fflush(stdout);\n\n  /*\n   * Collect statistics for the data.\n   */\n  for (data_count = 0; data_count < DATASIZE; data_count++)\n    {\n      data_updates += data[data_count].updates;\n      printf (\"data %02d: value %d, %d updates\\n\",\n              data_count, data[data_count].data, data[data_count].updates);\n      assert(pthread_rwlock_destroy (&data[data_count].lock) == 0);\n    }\n\n  printf (\"%d thread updates, %d data updates\\n\",\n          thread_updates, data_updates);\n\n  PTW32_FTIME(&currSysTime2);\n\n  printf( \"\\nstart: %ld/%d, stop: %ld/%d, duration:%ld\\n\",\n          (long)currSysTime1.time,currSysTime1.millitm,\n          (long)currSysTime2.time,currSysTime2.millitm,\n          ((long)((currSysTime2.time*1000+currSysTime2.millitm) -\n          (currSysTime1.time*1000+currSysTime1.millitm))));\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/self1.c",
    "content": "/*\n * self1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test for pthread_self().\n *\n * Depends on API functions:\n *\tpthread_self()\n *\n * Implicitly depends on:\n *\tpthread_getspecific()\n *\tpthread_setspecific()\n */\n\n#include \"test.h\"\n\nint\nmain(int argc, char * argv[])\n{\n\t/*\n\t * This should always succeed unless the system has no\n\t * resources (memory) left.\n\t */\n\tpthread_t self;\n\n#if defined(PTW32_STATIC_LIB) && !(defined(_MSC_VER) || defined(__MINGW32__))\n\tpthread_win32_process_attach_np();\n#endif\n\n\tself = pthread_self();\n\n\tassert(self.p != NULL);\n\n#if defined(PTW32_STATIC_LIB) && !(defined(_MSC_VER) || defined(__MINGW32__))\n\tpthread_win32_process_detach_np();\n#endif\n\treturn 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/self2.c",
    "content": "/*\n * self2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test for pthread_self().\n *\n * Depends on API functions:\n *\tpthread_create()\n *\tpthread_self()\n *\n * Implicitly depends on:\n *\tpthread_getspecific()\n *\tpthread_setspecific()\n */\n\n#include \"test.h\"\n#include <string.h>\n\nstatic pthread_t me;\n\nvoid *\nentry(void * arg)\n{\n  me = pthread_self();\n\n  return arg;\n}\n\nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_create(&t, NULL, entry, NULL) == 0);\n\n  Sleep(100);\n\n  assert(pthread_equal(t, me) != 0);\n\n  /* Success. */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/semaphore1.c",
    "content": "/*\n * File: semaphore1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Verify trywait() returns -1 and sets EAGAIN.\n * - \n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\nvoid *\nthr(void * arg)\n{\n  sem_t s;\n  int result;\n\n  assert(sem_init(&s, PTHREAD_PROCESS_PRIVATE, 0) == 0);\n\n  assert((result = sem_trywait(&s)) == -1);\n\n  if ( result == -1 )\n  {\n    int err = errno;\n    if (err != EAGAIN)\n    {\n      printf(\"thread: sem_trywait 1: expecting error %s: got %s\\n\",\n\t     error_string[EAGAIN], error_string[err]); fflush(stdout);\n    }\n    assert(err == EAGAIN);\n  }\n  else\n  {\n    printf(\"thread: ok 1\\n\");\n  }\n\n  assert((result = sem_post(&s)) == 0);\n\n  assert((result = sem_trywait(&s)) == 0);\n\n  assert(sem_post(&s) == 0);\n\n  return NULL;\n}\n\n\nint\nmain()\n{\n  pthread_t t;\n  sem_t s;\n  void* result1 = (void*)-1;\n  int result2;\n\n  assert(pthread_create(&t, NULL, thr, NULL) == 0);\n  assert(pthread_join(t, &result1) == 0);\n  assert((int)(size_t)result1 == 0);\n\n  assert(sem_init(&s, PTHREAD_PROCESS_PRIVATE, 0) == 0);\n\n  assert((result2 = sem_trywait(&s)) == -1);\n\n  if (result2 == -1)\n  {\n    int err = errno;\n    if (err != EAGAIN)\n    {\n      printf(\"main: sem_trywait 1: expecting error %s: got %s\\n\",\n\t     error_string[EAGAIN], error_string[err]); fflush(stdout);\n    }\n    assert(err == EAGAIN);\n  }\n  else\n  {\n    printf(\"main: ok 1\\n\");\n  }\n\n  assert((result2 = sem_post(&s)) == 0);\n\n  assert((result2 = sem_trywait(&s)) == 0);\n\n  assert(sem_post(&s) == 0);\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/semaphore2.c",
    "content": "/*\n * File: semaphore2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Verify sem_getvalue returns the correct value.\n * - \n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n#define MAX_COUNT 100\n\nint\nmain()\n{\n  sem_t s;\n\tint value = 0;\n\tint i;\n\n  assert(sem_init(&s, PTHREAD_PROCESS_PRIVATE, MAX_COUNT) == 0);\n\tassert(sem_getvalue(&s, &value) == 0);\n\tassert(value == MAX_COUNT);\n//\t  printf(\"Value = %ld\\n\", value);\n\n\tfor (i = MAX_COUNT - 1; i >= 0; i--)\n\t\t{\n\t\t\tassert(sem_wait(&s) == 0);\n\t\t\tassert(sem_getvalue(&s, &value) == 0);\n//\t\t\t  printf(\"Value = %ld\\n\", value);\n\t\t\tassert(value == i);\n\t\t}\n\n\tfor (i = 1; i <= MAX_COUNT; i++)\n\t\t{\n\t\t\tassert(sem_post(&s) == 0);\n\t\t\tassert(sem_getvalue(&s, &value) == 0);\n//\t\t\t  printf(\"Value = %ld\\n\", value);\n\t\t\tassert(value == i);\n\t\t}\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/semaphore3.c",
    "content": "/*\n * File: semaphore3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Verify sem_getvalue returns the correct number of waiters.\n * - \n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n#define MAX_COUNT 100\n\nsem_t s;\n\nvoid *\nthr (void * arg)\n{\n  assert(sem_wait(&s) == 0);\n  assert(pthread_detach(pthread_self()) == 0);\n  return NULL;\n}\n\nint\nmain()\n{\n\tint value = 0;\n\tint i;\n\tpthread_t t[MAX_COUNT+1];\n\n\tassert(sem_init(&s, PTHREAD_PROCESS_PRIVATE, 0) == 0);\n\tassert(sem_getvalue(&s, &value) == 0);\n//\tprintf(\"Value = %d\\n\", value);\tfflush(stdout);\n\tassert(value == 0);\n\n\tfor (i = 1; i <= MAX_COUNT; i++)\n\t\t{\n\t\t\tassert(pthread_create(&t[i], NULL, thr, NULL) == 0);\n\t\t\tdo {\n\t\t\t  sched_yield();\n\t\t\t  assert(sem_getvalue(&s, &value) == 0);\n\t\t\t} while (value != -i);\n//\t\t\tprintf(\"Value = %d\\n\", value); fflush(stdout);\n\t\t\tassert(-value == i);\n\t\t}\n\n\tfor (i = MAX_COUNT - 1; i >= 0; i--)\n\t\t{\n\t\t\tassert(sem_post(&s) == 0);\n\t\t\tassert(sem_getvalue(&s, &value) == 0);\n//\t\t\tprintf(\"Value = %d\\n\", value);\tfflush(stdout);\n\t\t\tassert(-value == i);\n\t\t}\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/semaphore4.c",
    "content": "/*\n * File: semaphore4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Verify sem_getvalue returns the correct number of waiters\n * after threads are cancelled.\n * - \n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n#define MAX_COUNT 100\n\nsem_t s;\n\nvoid *\nthr (void * arg)\n{\n  assert(sem_wait(&s) == 0);\n  return NULL;\n}\n\nint\nmain()\n{\n\tint value = 0;\n\tint i;\n\tpthread_t t[MAX_COUNT+1];\n\n\tassert(sem_init(&s, PTHREAD_PROCESS_PRIVATE, 0) == 0);\n\tassert(sem_getvalue(&s, &value) == 0);\n\tassert(value == 0);\n\n\tfor (i = 1; i <= MAX_COUNT; i++)\n\t\t{\n\t\t\tassert(pthread_create(&t[i], NULL, thr, NULL) == 0);\n\t\t\tdo {\n\t\t\t  sched_yield();\n\t\t\t  assert(sem_getvalue(&s, &value) == 0);\n\t\t\t} while (value != -i);\n\t\t\tassert(-value == i);\n\t\t}\n\n\tassert(sem_getvalue(&s, &value) == 0);\n\tassert(-value == MAX_COUNT);\n\tassert(pthread_cancel(t[50]) == 0);\n\t  {\n\t    void* result;\n\t    assert(pthread_join(t[50], &result) == 0);\n\t  }\n\tassert(sem_getvalue(&s, &value) == 0);\n\tassert(-value == (MAX_COUNT - 1));\n\n\tfor (i = MAX_COUNT - 2; i >= 0; i--)\n\t\t{\n\t\t\tassert(sem_post(&s) == 0);\n\t\t\tassert(sem_getvalue(&s, &value) == 0);\n\t\t\tassert(-value == i);\n\t\t}\n\n\tfor (i = 1; i <= MAX_COUNT; i++)\n\t  if (i != 50)\n\t    assert(pthread_join(t[i], NULL) == 0);\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/semaphore4t.c",
    "content": "/*\n * File: semaphore4t.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Verify sem_getvalue returns the correct number of waiters\n * after threads are cancelled.\n * - \n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - sem_timedwait cancellation.\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\n#define MAX_COUNT 100\n\nsem_t s;\n\nvoid *\nthr (void * arg)\n{\n  assert(sem_timedwait(&s, NULL) == 0);\n  return NULL;\n}\n\nint\nmain()\n{\n\tint value = 0;\n\tint i;\n\tpthread_t t[MAX_COUNT+1];\n\n\tassert(sem_init(&s, PTHREAD_PROCESS_PRIVATE, 0) == 0);\n\tassert(sem_getvalue(&s, &value) == 0);\n\tassert(value == 0);\n\n\tfor (i = 1; i <= MAX_COUNT; i++)\n\t\t{\n\t\t\tassert(pthread_create(&t[i], NULL, thr, NULL) == 0);\n\t\t\tdo {\n\t\t\t  sched_yield();\n\t\t\t  assert(sem_getvalue(&s, &value) == 0);\n\t\t\t} while (value != -i);\n\t\t\tassert(-value == i);\n\t\t}\n\n\tassert(sem_getvalue(&s, &value) == 0);\n\tassert(-value == MAX_COUNT);\n\tassert(pthread_cancel(t[50]) == 0);\n\tassert(pthread_join(t[50], NULL) == 0);\n\tassert(sem_getvalue(&s, &value) == 0);\n\tassert(-value == MAX_COUNT - 1);\n\n\tfor (i = MAX_COUNT - 2; i >= 0; i--)\n\t\t{\n\t\t\tassert(sem_post(&s) == 0);\n\t\t\tassert(sem_getvalue(&s, &value) == 0);\n\t\t\tassert(-value == i);\n\t\t}\n\n        for (i = 1; i <= MAX_COUNT; i++)\n          if (i != 50)\n            assert(pthread_join(t[i], NULL) == 0);\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/semaphore5.c",
    "content": "/*\n * File: semaphore5.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis: Verify sem_destroy EBUSY race avoidance\n * - \n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - \n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n// #define ASSERT_TRACE\n\n#include \"test.h\"\n\nvoid *\nthr(void * arg)\n{\n  assert(sem_post((sem_t *)arg) == 0);\n\n  return 0;\n}\n\n\nint\nmain()\n{\n  pthread_t t;\n  sem_t s;\n\n  assert(sem_init(&s, PTHREAD_PROCESS_PRIVATE, 0) == 0);\n  assert(pthread_create(&t, NULL, thr, (void *)&s) == 0);\n\n  assert(sem_wait(&s) == 0);\n  assert(sem_destroy(&s) == 0);\n\n  assert(pthread_join(t, NULL) == 0);\n\n  return 0;\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/sequence1.c",
    "content": "/*\n * File: sequence1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - that unique thread sequence numbers are generated.\n * - Analyse thread struct reuse.\n *\n * Test Method (Validation or Falsification):\n * -\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * - This test is implementation specific\n * because it uses knowledge of internals that should be\n * opaque to an application.\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - analysis output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - unique sequence numbers are generated for every new thread.\n *\n * Fail Criteria:\n * - \n */\n\n#include \"test.h\"\n\n/*\n */\n\nenum {\n\tNUMTHREADS = 10000\n};\n\n\nstatic long done = 0;\n/*\n * seqmap should have 1 in every element except [0]\n * Thread sequence numbers start at 1 and we will also\n * include this main thread so we need NUMTHREADS+2\n * elements. \n */\nstatic UINT64 seqmap[NUMTHREADS+2];\n\nvoid * func(void * arg)\n{\n  sched_yield();\n  seqmap[(int)pthread_getunique_np(pthread_self())] = 1;\n  InterlockedIncrement(&done);\n\n  return (void *) 0; \n}\n \nint\nmain()\n{\n  pthread_t t[NUMTHREADS];\n  pthread_attr_t attr;\n  int i;\n\n  assert(pthread_attr_init(&attr) == 0);\n  assert(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == 0);\n\n  for (i = 0; i < NUMTHREADS+2; i++)\n    {\n      seqmap[i] = 0;\n    }\n\n  for (i = 0; i < NUMTHREADS; i++)\n    {\n      if (NUMTHREADS/2 == i)\n        {\n          /* Include this main thread, which will be an implicit pthread_t */\n          seqmap[(int)pthread_getunique_np(pthread_self())] = 1;\n        }\n      assert(pthread_create(&t[i], &attr, func, NULL) == 0);\n    }\n\n  while (NUMTHREADS > InterlockedExchangeAdd((LPLONG)&done, 0L))\n    Sleep(100);\n\n  Sleep(100);\n\n  assert(seqmap[0] == 0);\n  for (i = 1; i < NUMTHREADS+2; i++)\n    {\n      assert(seqmap[i] == 1);\n    }\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/sizes.c",
    "content": "#define _WIN32_WINNT 0x400\n\n#include \"test.h\"\n#include \"../implement.h\"\n\nint\nmain()\n{\n  printf(\"Sizes of pthreads-win32 structs\\n\");\n  printf(\"-------------------------------\\n\");\n  printf(\"%30s %4d\\n\", \"pthread_t\", (int)sizeof(pthread_t));\n  printf(\"%30s %4d\\n\", \"ptw32_thread_t\", (int)sizeof(ptw32_thread_t));\n  printf(\"%30s %4d\\n\", \"pthread_attr_t_\", (int)sizeof(struct pthread_attr_t_));\n  printf(\"%30s %4d\\n\", \"sem_t_\", (int)sizeof(struct sem_t_));\n  printf(\"%30s %4d\\n\", \"pthread_mutex_t_\", (int)sizeof(struct pthread_mutex_t_));\n  printf(\"%30s %4d\\n\", \"pthread_mutexattr_t_\", (int)sizeof(struct pthread_mutexattr_t_));\n  printf(\"%30s %4d\\n\", \"pthread_spinlock_t_\", (int)sizeof(struct pthread_spinlock_t_));\n  printf(\"%30s %4d\\n\", \"pthread_barrier_t_\", (int)sizeof(struct pthread_barrier_t_));\n  printf(\"%30s %4d\\n\", \"pthread_barrierattr_t_\", (int)sizeof(struct pthread_barrierattr_t_));\n  printf(\"%30s %4d\\n\", \"pthread_key_t_\", (int)sizeof(struct pthread_key_t_));\n  printf(\"%30s %4d\\n\", \"pthread_cond_t_\", (int)sizeof(struct pthread_cond_t_));\n  printf(\"%30s %4d\\n\", \"pthread_condattr_t_\", (int)sizeof(struct pthread_condattr_t_));\n  printf(\"%30s %4d\\n\", \"pthread_rwlock_t_\", (int)sizeof(struct pthread_rwlock_t_));\n  printf(\"%30s %4d\\n\", \"pthread_rwlockattr_t_\", (int)sizeof(struct pthread_rwlockattr_t_));\n  printf(\"%30s %4d\\n\", \"pthread_once_t_\", (int)sizeof(struct pthread_once_t_));\n  printf(\"%30s %4d\\n\", \"ptw32_cleanup_t\", (int)sizeof(struct ptw32_cleanup_t));\n  printf(\"%30s %4d\\n\", \"ptw32_mcs_node_t_\", (int)sizeof(struct ptw32_mcs_node_t_));\n  printf(\"%30s %4d\\n\", \"sched_param\", (int)sizeof(struct sched_param));\n  printf(\"-------------------------------\\n\");\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/spin1.c",
    "content": "/* \n * spin1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Create a simple spinlock object, lock it, and then unlock it again.\n * This is the simplest test of the pthread mutex family that we can do.\n *\n */\n\n#include \"test.h\"\n\npthread_spinlock_t lock;\n\nint\nmain()\n{\n  assert(pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE) == 0);\n\n  assert(pthread_spin_lock(&lock) == 0);\n\n  assert(pthread_spin_unlock(&lock) == 0);\n\n  assert(pthread_spin_destroy(&lock) == 0);\n\n  assert(pthread_spin_lock(&lock) == EINVAL);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/spin2.c",
    "content": "/* \n * spin2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a spinlock object, lock it, trylock it, \n * and then unlock it again.\n *\n */\n\n#include \"test.h\"\n \npthread_spinlock_t lock = NULL;\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  assert(pthread_spin_trylock(&lock) == EBUSY);\n\n  washere = 1;\n\n  return 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  assert(pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE) == 0);\n\n  assert(pthread_spin_lock(&lock) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n  assert(pthread_join(t, NULL) == 0);\n\n  assert(pthread_spin_unlock(&lock) == 0);\n\n  assert(pthread_spin_destroy(&lock) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/spin3.c",
    "content": "/* \n * spin3.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Thread A locks spin - thread B tries to unlock.\n * This should succeed, but it's undefined behaviour.\n *\n */\n\n#include \"test.h\"\n\nstatic int wasHere = 0;\n\nstatic pthread_spinlock_t spin;\n \nvoid * unlocker(void * arg)\n{\n  int expectedResult = (int)(size_t)arg;\n\n  wasHere++;\n  assert(pthread_spin_unlock(&spin) == expectedResult);\n  wasHere++;\n  return NULL;\n}\n \nint\nmain()\n{\n  pthread_t t;\n\n  wasHere = 0;\n  assert(pthread_spin_init(&spin, PTHREAD_PROCESS_PRIVATE) == 0);\n  assert(pthread_spin_lock(&spin) == 0);\n  assert(pthread_create(&t, NULL, unlocker, (void*)0) == 0);\n  assert(pthread_join(t, NULL) == 0);\n  /*\n   * Our spinlocks don't record the owner thread so any thread can unlock the spinlock,\n   * but nor is it an error for any thread to unlock a spinlock that is not locked.\n   */\n  assert(pthread_spin_unlock(&spin) == 0);\n  assert(pthread_spin_destroy(&spin) == 0);\n  assert(wasHere == 2);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/spin4.c",
    "content": "/* \n * spin4.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Declare a static spinlock object, lock it, spin on it, \n * and then unlock it again.\n */\n\n#include \"test.h\"\n#include <sys/timeb.h>\n \npthread_spinlock_t lock = PTHREAD_SPINLOCK_INITIALIZER;\nPTW32_STRUCT_TIMEB currSysTimeStart;\nPTW32_STRUCT_TIMEB currSysTimeStop;\n\n#define GetDurationMilliSecs(_TStart, _TStop) ((_TStop.time*1000+_TStop.millitm) \\\n\t\t\t\t\t       - (_TStart.time*1000+_TStart.millitm))\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  PTW32_FTIME(&currSysTimeStart);\n  washere = 1;\n  assert(pthread_spin_lock(&lock) == 0);\n  assert(pthread_spin_unlock(&lock) == 0);\n  PTW32_FTIME(&currSysTimeStop);\n\n  return (void *)(size_t)GetDurationMilliSecs(currSysTimeStart, currSysTimeStop);\n}\n \nint\nmain()\n{\n  void* result = (void*)0;\n  pthread_t t;\n  int CPUs;\n  PTW32_STRUCT_TIMEB sysTime;\n\n  if ((CPUs = pthread_num_processors_np()) == 1)\n    {\n      printf(\"Test not run - it requires multiple CPUs.\\n\");\n\texit(0);\n    }\n\n  assert(pthread_spin_lock(&lock) == 0);\n\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n\n  while (washere == 0)\n    {\n      sched_yield();\n    }\n\n  do\n    {\n      sched_yield();\n      PTW32_FTIME(&sysTime);\n    }\n  while (GetDurationMilliSecs(currSysTimeStart, sysTime) <= 1000);\n\n  assert(pthread_spin_unlock(&lock) == 0);\n\n  assert(pthread_join(t, &result) == 0);\n  assert((int)(size_t)result > 1000);\n\n  assert(pthread_spin_destroy(&lock) == 0);\n\n  assert(washere == 1);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/stress1.c",
    "content": "/*\n * stress1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Stress test condition variables, mutexes, semaphores.\n *\n * Test Method (Validation or Falsification):\n * - Validation\n *\n * Requirements Tested:\n * - Correct accounting of semaphore and condition variable waiters.\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Description:\n * Attempting to expose race conditions in cond vars, semaphores etc.\n * - Master attempts to signal slave close to when timeout is due.\n * - Master and slave do battle continuously until main tells them to stop.\n * - Afterwards, the CV must be successfully destroyed (will return an\n * error if there are waiters (including any internal semaphore waiters,\n * which, if there are, cannot be real waiters).\n *\n * Environment:\n * - \n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * - \n *\n * Pass Criteria:\n * - CV is successfully destroyed.\n *\n * Fail Criteria:\n * - CV destroy fails.\n */\n\n#include \"test.h\"\n#include <string.h>\n#include <sys/timeb.h>\n\n\nconst unsigned int ITERATIONS = 1000;\n\nstatic pthread_t master, slave;\ntypedef struct {\n  int value;\n  pthread_cond_t cv;\n  pthread_mutex_t mx;\n} mysig_t;\n\nstatic int allExit;\nstatic mysig_t control = {0, PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER};\nstatic pthread_barrier_t startBarrier, readyBarrier, holdBarrier;\nstatic int timeoutCount = 0;\nstatic int signalsTakenCount = 0;\nstatic int signalsSent = 0;\nstatic int bias = 0;\nstatic int timeout = 10; // Must be > 0\n\nenum {\n  CTL_STOP     = -1\n};\n\n/*\n * Returns abstime 'milliseconds' from 'now'.\n *\n * Works for: -INT_MAX <= millisecs <= INT_MAX\n */\nstruct timespec *\nmillisecondsFromNow (struct timespec * time, int millisecs)\n{\n  PTW32_STRUCT_TIMEB currSysTime;\n  int64_t nanosecs, secs;\n  const int64_t NANOSEC_PER_MILLISEC = 1000000;\n  const int64_t NANOSEC_PER_SEC = 1000000000;\n\n  /* get current system time and add millisecs */\n  PTW32_FTIME(&currSysTime);\n\n  secs = (int64_t)(currSysTime.time) + (millisecs / 1000);\n  nanosecs = ((int64_t) (millisecs%1000 + currSysTime.millitm)) * NANOSEC_PER_MILLISEC;\n  if (nanosecs >= NANOSEC_PER_SEC)\n    {\n      secs++;\n      nanosecs -= NANOSEC_PER_SEC;\n    }\n  else if (nanosecs < 0)\n    {\n      secs--;\n      nanosecs += NANOSEC_PER_SEC;\n    }\n\n  time->tv_nsec = (long)nanosecs;\n  time->tv_sec = (long)secs;\n\n  return time;\n}\n\nvoid *\nmasterThread (void * arg)\n{\n  int dither = (int)(size_t)arg;\n\n  timeout = (int)(size_t)arg;\n\n  pthread_barrier_wait(&startBarrier);\n\n  do\n    {\n      int sleepTime;\n\n      assert(pthread_mutex_lock(&control.mx) == 0);\n      control.value = timeout;\n      assert(pthread_mutex_unlock(&control.mx) == 0);\n\n      /*\n       * We are attempting to send the signal close to when the slave\n       * is due to timeout. We feel around by adding some [non-random] dither.\n       *\n       * dither is in the range 2*timeout peak-to-peak\n       * sleep time is the average of timeout plus dither.\n       * e.g.\n       * if timeout = 10 then dither = 20 and\n       * sleep millisecs is: 5 <= ms <= 15\n       *\n       * The bias value attempts to apply some negative feedback to keep\n       * the ratio of timeouts to signals taken close to 1:1.\n       * bias changes more slowly than dither so as to average more.\n       *\n       * Finally, if abs(bias) exceeds timeout then timeout is incremented.\n       */\n      if (signalsSent % timeout == 0)\n\t{\n          if (timeoutCount > signalsTakenCount)\n\t    {\n\t      bias++;\n\t    }\n          else if (timeoutCount < signalsTakenCount)\n\t    {\n\t      bias--;\n\t    }\n\t  if (bias < -timeout || bias > timeout)\n\t    {\n\t      timeout++;\n\t    }\n\t}\n      dither = (dither + 1 ) % (timeout * 2);\n      sleepTime = (timeout - bias + dither) / 2;\n      Sleep(sleepTime);\n      assert(pthread_cond_signal(&control.cv) == 0);\n      signalsSent++;\n\n      pthread_barrier_wait(&holdBarrier);\n      pthread_barrier_wait(&readyBarrier);\n    }\n  while (!allExit);\n\n  return NULL;\n}\n\nvoid *\nslaveThread (void * arg)\n{\n  struct timespec time;\n\n  pthread_barrier_wait(&startBarrier);\n\n  do\n    {\n      assert(pthread_mutex_lock(&control.mx) == 0);\n      if (pthread_cond_timedwait(&control.cv,\n\t\t\t\t &control.mx,\n\t\t\t\t millisecondsFromNow(&time, control.value)) == ETIMEDOUT)\n\t{\n\t  timeoutCount++;\n\t}\n      else\n\t{\n\t  signalsTakenCount++;\n\t}\n      assert(pthread_mutex_unlock(&control.mx) == 0);\n\n      pthread_barrier_wait(&holdBarrier);\n      pthread_barrier_wait(&readyBarrier);\n    }\n  while (!allExit);\n\n  return NULL;\n}\n\nint\nmain ()\n{\n  unsigned int i;\n\n  assert(pthread_barrier_init(&startBarrier, NULL, 3) == 0);\n  assert(pthread_barrier_init(&readyBarrier, NULL, 3) == 0);\n  assert(pthread_barrier_init(&holdBarrier, NULL, 3) == 0);\n\n  assert(pthread_create(&master, NULL, masterThread, (void *)(size_t)timeout) == 0);\n  assert(pthread_create(&slave, NULL, slaveThread, NULL) == 0);\n\n  allExit = FALSE;\n\n  pthread_barrier_wait(&startBarrier);\n\n  for (i = 1; !allExit; i++)\n    {\n      pthread_barrier_wait(&holdBarrier);\n      if (i >= ITERATIONS)\n\t{\n\t  allExit = TRUE;\n\t}\n      pthread_barrier_wait(&readyBarrier);\n    }\n\n  assert(pthread_join(slave, NULL) == 0);\n  assert(pthread_join(master, NULL) == 0);\n\n  printf(\"Signals sent = %d\\nWait timeouts = %d\\nSignals taken = %d\\nBias = %d\\nTimeout = %d\\n\",\n\t signalsSent,\n\t timeoutCount,\n\t signalsTakenCount,\n\t (int) bias,\n\t timeout);\n\n  /* Cleanup */\n  assert(pthread_barrier_destroy(&holdBarrier) == 0);\n  assert(pthread_barrier_destroy(&readyBarrier) == 0);\n  assert(pthread_barrier_destroy(&startBarrier) == 0);\n  assert(pthread_cond_destroy(&control.cv) == 0);\n  assert(pthread_mutex_destroy(&control.mx) == 0);\n\n  /* Success. */\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/test.h",
    "content": "/* \n * test.h\n *\n * Useful definitions and declarations for tests.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n */\n\n#ifndef _PTHREAD_TEST_H_\n#define _PTHREAD_TEST_H_\n\n#include \"pthread.h\"\n#include \"sched.h\"\n#include \"semaphore.h\"\n\n#include <windows.h>\n#include <stdio.h>\n\n#define PTW32_THREAD_NULL_ID {NULL,0}\n\n/*\n * Some non-thread POSIX API substitutes\n */\n#define rand_r( _seed ) \\\n        ( _seed == _seed? rand() : rand() )\n\n#if defined(__MINGW32__)\n#include <stdint.h>\n#elif defined(__BORLANDC__)\n#define int64_t ULONGLONG\n#else\n#define int64_t _int64\n#endif\n\n#if defined(_MSC_VER) && _MSC_VER >= 1400\n#  define PTW32_FTIME(x) _ftime64_s(x)\n#  define PTW32_STRUCT_TIMEB struct __timeb64\n#elif ( defined(_MSC_VER) && _MSC_VER >= 1300 ) || \\\n      ( defined(__MINGW32__) && __MSVCRT_VERSION__ >= 0x0601 )\n#  define PTW32_FTIME(x) _ftime64(x)\n#  define PTW32_STRUCT_TIMEB struct __timeb64\n#else\n#  define PTW32_FTIME(x) _ftime(x)\n#  define PTW32_STRUCT_TIMEB struct _timeb\n#endif\n\n\nconst char * error_string[] = {\n  \"ZERO_or_EOK\",\n  \"EPERM\",\n  \"ENOFILE_or_ENOENT\",\n  \"ESRCH\",\n  \"EINTR\",\n  \"EIO\",\n  \"ENXIO\",\n  \"E2BIG\",\n  \"ENOEXEC\",\n  \"EBADF\",\n  \"ECHILD\",\n  \"EAGAIN\",\n  \"ENOMEM\",\n  \"EACCES\",\n  \"EFAULT\",\n  \"UNKNOWN_15\",\n  \"EBUSY\",\n  \"EEXIST\",\n  \"EXDEV\",\n  \"ENODEV\",\n  \"ENOTDIR\",\n  \"EISDIR\",\n  \"EINVAL\",\n  \"ENFILE\",\n  \"EMFILE\",\n  \"ENOTTY\",\n  \"UNKNOWN_26\",\n  \"EFBIG\",\n  \"ENOSPC\",\n  \"ESPIPE\",\n  \"EROFS\",\n  \"EMLINK\",\n  \"EPIPE\",\n  \"EDOM\",\n  \"ERANGE\",\n  \"UNKNOWN_35\",\n  \"EDEADLOCK_or_EDEADLK\",\n  \"UNKNOWN_37\",\n  \"ENAMETOOLONG\",\n  \"ENOLCK\",\n  \"ENOSYS\",\n  \"ENOTEMPTY\",\n  \"EILSEQ\",\n  \"EOWNERDEAD\",\n  \"ENOTRECOVERABLE\"\n};\n\n/*\n * The Mingw32 assert macro calls the CRTDLL _assert function\n * which pops up a dialog. We want to run in batch mode so\n * we define our own assert macro.\n */\n#ifdef assert\n# undef assert\n#endif\n\n#ifndef ASSERT_TRACE\n# define ASSERT_TRACE 0\n#else\n# undef ASSERT_TRACE\n# define ASSERT_TRACE 1\n#endif\n\n# define assert(e) \\\n   ((e) ? ((ASSERT_TRACE) ? fprintf(stderr, \\\n                                    \"Assertion succeeded: (%s), file %s, line %d\\n\", \\\n\t\t\t            #e, __FILE__, (int) __LINE__), \\\n\t                            fflush(stderr) : \\\n                             0) : \\\n          (fprintf(stderr, \"Assertion failed: (%s), file %s, line %d\\n\", \\\n                   #e, __FILE__, (int) __LINE__), exit(1), 0))\n\nint assertE;\n# define assert_e(e, o, r) \\\n   (((assertE = e) o (r)) ? ((ASSERT_TRACE) ? fprintf(stderr, \\\n                                    \"Assertion succeeded: (%s), file %s, line %d\\n\", \\\n\t\t\t            #e, __FILE__, (int) __LINE__), \\\n\t                            fflush(stderr) : \\\n                             0) : \\\n          (fprintf(stderr, \"Assertion failed: (%s %s %s), file %s, line %d, error %s\\n\", \\\n                   #e,#o,#r, __FILE__, (int) __LINE__, error_string[assertE]), exit(1), 0))\n\n#endif\n\n# define BEGIN_MUTEX_STALLED_ROBUST(mxAttr) \\\n  for(;;) \\\n    { \\\n      static int _i=0; \\\n      static int _robust; \\\n      pthread_mutexattr_getrobust(&(mxAttr), &_robust);\n\n# define END_MUTEX_STALLED_ROBUST(mxAttr) \\\n      printf(\"Pass %s\\n\", _robust==PTHREAD_MUTEX_ROBUST?\"Robust\":\"Non-robust\"); \\\n      if (++_i > 1) \\\n        break; \\\n      else \\\n        { \\\n          pthread_mutexattr_t *pma, *pmaEnd; \\\n          for(pma = &(mxAttr), pmaEnd = pma + sizeof(mxAttr)/sizeof(pthread_mutexattr_t); \\\n              pma < pmaEnd; \\\n              pthread_mutexattr_setrobust(pma++, PTHREAD_MUTEX_ROBUST)); \\\n        } \\\n    }\n\n# define IS_ROBUST (_robust==PTHREAD_MUTEX_ROBUST)\n"
  },
  {
    "path": "deps/w32-pthreads/tests/tryentercs.c",
    "content": "/*\n * tryentercs.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * See if we have the TryEnterCriticalSection function.\n * Does not use any part of pthreads.\n */\n\n#include <windows.h>\n#include <process.h>\n#include <stdio.h>\n\n/*\n * Function pointer to TryEnterCriticalSection if it exists\n * - otherwise NULL\n */\nBOOL (WINAPI *_try_enter_critical_section)(LPCRITICAL_SECTION) =\nNULL;\n\n/*\n * Handle to kernel32.dll\n */\nstatic HINSTANCE _h_kernel32;\n\n\nint\nmain()\n{\n  CRITICAL_SECTION cs;\n\n  SetLastError(0);\n\n  printf(\"Last Error [main enter] %ld\\n\", (long) GetLastError());\n\n  /*\n   * Load KERNEL32 and try to get address of TryEnterCriticalSection\n   */\n  _h_kernel32 = LoadLibrary(TEXT(\"KERNEL32.DLL\"));\n  _try_enter_critical_section =\n        (BOOL (PT_STDCALL *)(LPCRITICAL_SECTION))\n        GetProcAddress(_h_kernel32,\n                         (LPCSTR) \"TryEnterCriticalSection\");\n\n  if (_try_enter_critical_section != NULL)\n    {\n      InitializeCriticalSection(&cs);\n\n      SetLastError(0);\n\n      if ((*_try_enter_critical_section)(&cs) != 0)\n        {\n          LeaveCriticalSection(&cs);\n        }\n\telse\n\t  {\n\t    printf(\"Last Error [try enter] %ld\\n\", (long) GetLastError());\n\n\t    _try_enter_critical_section = NULL;\n\t  }\n      DeleteCriticalSection(&cs);\n    }\n\n  (void) FreeLibrary(_h_kernel32);\n\n  printf(\"This system %s TryEnterCriticalSection.\\n\",\n         (_try_enter_critical_section == NULL) ? \"DOES NOT SUPPORT\" : \"SUPPORTS\");\n  printf(\"POSIX Mutexes will be based on Win32 %s.\\n\",\n         (_try_enter_critical_section == NULL) ? \"Mutexes\" : \"Critical Sections\");\n\n  return(0);\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/tryentercs2.c",
    "content": "/*\n * tryentercs.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * See if we have the TryEnterCriticalSection function.\n * Does not use any part of pthreads.\n */\n\n#include <windows.h>\n#include <process.h>\n#include <stdio.h>\n\n/*\n * Function pointer to TryEnterCriticalSection if it exists\n * - otherwise NULL\n */\nBOOL (WINAPI *_try_enter_critical_section)(LPCRITICAL_SECTION) = NULL;\n\n/*\n * Handle to kernel32.dll\n */\nstatic HINSTANCE _h_kernel32;\n\n\nint\nmain()\n{\n  LPCRITICAL_SECTION lpcs = NULL;\n\n  SetLastError(0);\n\n  printf(\"Last Error [main enter] %ld\\n\", (long) GetLastError());\n\n  /*\n   * Load KERNEL32 and try to get address of TryEnterCriticalSection\n   */\n  _h_kernel32 = LoadLibrary(TEXT(\"KERNEL32.DLL\"));\n  _try_enter_critical_section =\n        (BOOL (PT_STDCALL *)(LPCRITICAL_SECTION))\n        GetProcAddress(_h_kernel32,\n                         (LPCSTR) \"TryEnterCriticalSection\");\n\n  if (_try_enter_critical_section != NULL)\n    {\n      SetLastError(0);\n\n      (*_try_enter_critical_section)(lpcs);\n\n      printf(\"Last Error [try enter] %ld\\n\", (long) GetLastError());\n    }\n\n  (void) FreeLibrary(_h_kernel32);\n\n  printf(\"This system %s TryEnterCriticalSection.\\n\",\n         (_try_enter_critical_section == NULL) ? \"DOES NOT SUPPORT\" : \"SUPPORTS\");\n  printf(\"POSIX Mutexes will be based on Win32 %s.\\n\",\n         (_try_enter_critical_section == NULL) ? \"Mutexes\" : \"Critical Sections\");\n\n  return(0);\n}\n\n"
  },
  {
    "path": "deps/w32-pthreads/tests/tsd1.c",
    "content": "/*\n * tsd1.c\n *\n * Test Thread Specific Data (TSD) key creation and destruction.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n *\n * --------------------------------------------------------------------------\n *\n * Description:\n * - \n *\n * Test Method (validation or falsification):\n * - validation\n *\n * Requirements Tested:\n * - keys are created for each existing thread including the main thread\n * - keys are created for newly created threads\n * - keys are thread specific\n * - destroy routine is called on each thread exit including the main thread\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - none\n *\n * Output:\n * - text to stdout\n *\n * Assumptions:\n * - already validated:     pthread_create()\n *                          pthread_once()\n * - main thread also has a POSIX thread identity\n *\n * Pass Criteria:\n * - stdout matches file reference/tsd1.out\n *\n * Fail Criteria:\n * - fails to match file reference/tsd1.out\n * - output identifies failed component\n */\n\n#include <sched.h>\n#include \"test.h\"\n\nenum {\n  NUM_THREADS = 100\n};\n\nstatic pthread_key_t key = NULL;\nstatic int accesscount[NUM_THREADS];\nstatic int thread_set[NUM_THREADS];\nstatic int thread_destroyed[NUM_THREADS];\nstatic pthread_barrier_t startBarrier;\n\nstatic void\ndestroy_key(void * arg)\n{\n  int * j = (int *) arg;\n\n  (*j)++;\n\n  assert(*j == 2);\n\n  thread_destroyed[j - accesscount] = 1;\n}\n\nstatic void\nsetkey(void * arg)\n{\n  int * j = (int *) arg;\n\n  thread_set[j - accesscount] = 1;\n\n  assert(*j == 0);\n\n  assert(pthread_getspecific(key) == NULL);\n\n  assert(pthread_setspecific(key, arg) == 0);\n  assert(pthread_setspecific(key, arg) == 0);\n  assert(pthread_setspecific(key, arg) == 0);\n\n  assert(pthread_getspecific(key) == arg);\n\n  (*j)++;\n\n  assert(*j == 1);\n}\n\nstatic void *\nmythread(void * arg)\n{\n  (void) pthread_barrier_wait(&startBarrier);\n\n  setkey(arg);\n\n  return 0;\n\n  /* Exiting the thread will call the key destructor. */\n}\n\nint\nmain()\n{\n  int i;\n  int fail = 0;\n  pthread_t thread[NUM_THREADS];\n\n  assert(pthread_barrier_init(&startBarrier, NULL, NUM_THREADS/2) == 0);\n\n  for (i = 1; i < NUM_THREADS/2; i++)\n    {\n      accesscount[i] = thread_set[i] = thread_destroyed[i] = 0;\n      assert(pthread_create(&thread[i], NULL, mythread, (void *)&accesscount[i]) == 0);\n    }\n\n  /*\n   * Here we test that existing threads will get a key created\n   * for them.\n   */\n  assert(pthread_key_create(&key, destroy_key) == 0);\n\n  (void) pthread_barrier_wait(&startBarrier);\n\n  /*\n   * Test main thread key.\n   */\n  accesscount[0] = 0;\n  setkey((void *) &accesscount[0]);\n\n  /*\n   * Here we test that new threads will get a key created\n   * for them.\n   */\n  for (i = NUM_THREADS/2; i < NUM_THREADS; i++)\n    {\n      accesscount[i] = thread_set[i] = thread_destroyed[i] = 0;\n      assert(pthread_create(&thread[i], NULL, mythread, (void *)&accesscount[i]) == 0);\n    }\n\n  /*\n   * Wait for all threads to complete.\n   */\n  for (i = 1; i < NUM_THREADS; i++)\n    {\n\tassert(pthread_join(thread[i], NULL) == 0);\n    }\n\n  assert(pthread_key_delete(key) == 0);\n\n  assert(pthread_barrier_destroy(&startBarrier) == 0);\n\n  for (i = 1; i < NUM_THREADS; i++)\n    {\n\t/*\n\t * The counter is incremented once when the key is set to\n\t * a value, and again when the key is destroyed. If the key\n\t * doesn't get set for some reason then it will still be\n\t * NULL and the destroy function will not be called, and\n\t * hence accesscount will not equal 2.\n\t */\n\tif (accesscount[i] != 2)\n\t  {\n\t    fail++;\n\t    fprintf(stderr, \"Thread %d key, set = %d, destroyed = %d\\n\",\n\t\t\ti, thread_set[i], thread_destroyed[i]);\n\t  }\n    }\n\n  fflush(stderr);\n\n  return (fail);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/tsd2.c",
    "content": "/*\n * tsd2.c\n *\n * Test Thread Specific Data (TSD) key creation and destruction.\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n *\n * --------------------------------------------------------------------------\n *\n * Description:\n * - \n *\n * Test Method (validation or falsification):\n * - validation\n *\n * Requirements Tested:\n * - keys are created for each existing thread including the main thread\n * - keys are created for newly created threads\n * - keys are thread specific\n * - destroy routine is called on each thread exit including the main thread\n *\n * Features Tested:\n * - \n *\n * Cases Tested:\n * - \n *\n * Environment:\n * - \n *\n * Input:\n * - none\n *\n * Output:\n * - text to stdout\n *\n * Assumptions:\n * - already validated:     pthread_create()\n *                          pthread_once()\n * - main thread also has a POSIX thread identity\n *\n * Pass Criteria:\n * - stdout matches file reference/tsd1.out\n *\n * Fail Criteria:\n * - fails to match file reference/tsd1.out\n * - output identifies failed component\n */\n\n#include <sched.h>\n#include \"test.h\"\n\nenum {\n  NUM_THREADS = 100\n};\n\nstatic pthread_key_t key = NULL;\nstatic int accesscount[NUM_THREADS];\nstatic int thread_set[NUM_THREADS];\nstatic int thread_destroyed[NUM_THREADS];\nstatic pthread_barrier_t startBarrier;\n\nstatic void\ndestroy_key(void * arg)\n{\n  int * j = (int *) arg;\n\n  (*j)++;\n\n  /* Set TSD key from the destructor to test destructor iteration */\n  if (*j == 2)\n    assert(pthread_setspecific(key, arg) == 0);\n  else\n    assert(*j == 3);\n\n  thread_destroyed[j - accesscount] = 1;\n}\n\nstatic void\nsetkey(void * arg)\n{\n  int * j = (int *) arg;\n\n  thread_set[j - accesscount] = 1;\n\n  assert(*j == 0);\n\n  assert(pthread_getspecific(key) == NULL);\n\n  assert(pthread_setspecific(key, arg) == 0);\n  assert(pthread_setspecific(key, arg) == 0);\n  assert(pthread_setspecific(key, arg) == 0);\n\n  assert(pthread_getspecific(key) == arg);\n\n  (*j)++;\n\n  assert(*j == 1);\n}\n\nstatic void *\nmythread(void * arg)\n{\n  (void) pthread_barrier_wait(&startBarrier);\n\n  setkey(arg);\n\n  return 0;\n\n  /* Exiting the thread will call the key destructor. */\n}\n\nint\nmain()\n{\n  int i;\n  int fail = 0;\n  pthread_t thread[NUM_THREADS];\n\n  assert(pthread_barrier_init(&startBarrier, NULL, NUM_THREADS/2) == 0);\n\n  for (i = 1; i < NUM_THREADS/2; i++)\n    {\n      accesscount[i] = thread_set[i] = thread_destroyed[i] = 0;\n      assert(pthread_create(&thread[i], NULL, mythread, (void *)&accesscount[i]) == 0);\n    }\n\n  /*\n   * Here we test that existing threads will get a key created\n   * for them.\n   */\n  assert(pthread_key_create(&key, destroy_key) == 0);\n\n  (void) pthread_barrier_wait(&startBarrier);\n\n  /*\n   * Test main thread key.\n   */\n  accesscount[0] = 0;\n  setkey((void *) &accesscount[0]);\n\n  /*\n   * Here we test that new threads will get a key created\n   * for them.\n   */\n  for (i = NUM_THREADS/2; i < NUM_THREADS; i++)\n    {\n      accesscount[i] = thread_set[i] = thread_destroyed[i] = 0;\n      assert(pthread_create(&thread[i], NULL, mythread, (void *)&accesscount[i]) == 0);\n    }\n\n  /*\n   * Wait for all threads to complete.\n   */\n  for (i = 1; i < NUM_THREADS; i++)\n    {\n\tassert(pthread_join(thread[i], NULL) == 0);\n    }\n\n  assert(pthread_key_delete(key) == 0);\n\n  assert(pthread_barrier_destroy(&startBarrier) == 0);\n\n  for (i = 1; i < NUM_THREADS; i++)\n    {\n\t/*\n\t * The counter is incremented once when the key is set to\n\t * a value, and again when the key is destroyed. If the key\n\t * doesn't get set for some reason then it will still be\n\t * NULL and the destroy function will not be called, and\n\t * hence accesscount will not equal 2.\n\t */\n\tif (accesscount[i] != 3)\n\t  {\n\t    fail++;\n\t    fprintf(stderr, \"Thread %d key, set = %d, destroyed = %d\\n\",\n\t\t\ti, thread_set[i], thread_destroyed[i]);\n\t  }\n    }\n\n  fflush(stderr);\n\n  return (fail);\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/valid1.c",
    "content": "/*\n * File: valid1.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Test that thread validation works.\n *\n * Test Method (Validation or Falsification):\n * -\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n\nenum {\n\tNUMTHREADS = 1\n};\n\nstatic int washere = 0;\n\nvoid * func(void * arg)\n{\n  washere = 1;\n  return (void *) 0; \n}\n \nint\nmain()\n{\n  pthread_t t;\n  void * result = NULL;\n\n  washere = 0;\n  assert(pthread_create(&t, NULL, func, NULL) == 0);\n  assert(pthread_join(t, &result) == 0);\n  assert((int)(size_t)result == 0);\n  assert(washere == 1);\n  sched_yield();\n  assert(pthread_kill(t, 0) == ESRCH);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tests/valid2.c",
    "content": "/*\n * File: valid2.c\n *\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n *\n * --------------------------------------------------------------------------\n *\n * Test Synopsis:\n * - Confirm that thread validation fails for garbage thread ID.\n *\n * Test Method (Validation or Falsification):\n * -\n *\n * Requirements Tested:\n * -\n *\n * Features Tested:\n * -\n *\n * Cases Tested:\n * -\n *\n * Description:\n * -\n *\n * Environment:\n * -\n *\n * Input:\n * - None.\n *\n * Output:\n * - File name, Line number, and failed expression on failure.\n * - No output on success.\n *\n * Assumptions:\n * -\n *\n * Pass Criteria:\n * - Process returns zero exit status.\n *\n * Fail Criteria:\n * - Process returns non-zero exit status.\n */\n\n#include \"test.h\"\n \nint\nmain()\n{\n  pthread_t NullThread = PTW32_THREAD_NULL_ID;\n\n  assert(pthread_kill(NullThread, 0) == ESRCH);\n\n  return 0;\n}\n"
  },
  {
    "path": "deps/w32-pthreads/tsd.c",
    "content": "/*\n * tsd.c\n *\n * Description:\n * POSIX thread functions which implement thread-specific data (TSD).\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\n#include \"pthread_key_create.c\"\n#include \"pthread_key_delete.c\"\n#include \"pthread_setspecific.c\"\n#include \"pthread_getspecific.c\"\n"
  },
  {
    "path": "deps/w32-pthreads/version.rc",
    "content": "/* This is an implementation of the threads API of POSIX 1003.1-2001.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include <winver.h>\n#include \"pthread.h\"\n\n/*\n * Note: the correct __CLEANUP_* macro must be defined corresponding to\n * the definition used for the object file builds. This is done in the\n * relevent makefiles for the command line builds, but users should ensure\n * that their resource compiler knows what it is too.\n * If using the default (no __CLEANUP_* defined), pthread.h will define it\n * as __CLEANUP_C.\n */\n\n#if defined(__MINGW64__)\n#  define PTW32_ARCH \"x64 (mingw64)\"\n#elif defined (__MINGW32__)\n#  define PTW32_ARCH \"x86 (mingw32)\"\n#endif\n#if defined(PTW32_ARCHx64)\n#  define PTW32_ARCH \"x64\"\n#elif defined(PTW32_ARCHx86)\n#  define PTW32_ARCH \"x86\"\n#endif\n\n#if defined(PTW32_RC_MSC)\n#  if defined(__CLEANUP_C)\n#    define PTW32_VERSIONINFO_NAME \"pthreadVC2.DLL\\0\"\n#    define PTW32_VERSIONINFO_DESCRIPTION \"MS C \" PTW32_ARCH \"\\0\"\n#  elif defined(__CLEANUP_CXX)\n#    define PTW32_VERSIONINFO_NAME \"pthreadVCE2.DLL\\0\"\n#    define PTW32_VERSIONINFO_DESCRIPTION \"MS C++ \" PTW32_ARCH \"\\0\"\n#  elif defined(__CLEANUP_SEH)\n#    define PTW32_VERSIONINFO_NAME \"pthreadVSE2.DLL\\0\"\n#    define PTW32_VERSIONINFO_DESCRIPTION \"MS C SEH \" PTW32_ARCH \"\\0\"\n#  else\n#    error Resource compiler doesn't know which cleanup style you're using - see version.rc\n#  endif\n#elif defined(__GNUC__)\n#  if defined(__CLEANUP_C)\n#    define PTW32_VERSIONINFO_NAME \"pthreadGC2.DLL\\0\"\n#    define PTW32_VERSIONINFO_DESCRIPTION \"GNU C \" PTW32_ARCH \"\\0\"\n#  elif defined(__CLEANUP_CXX)\n#    define PTW32_VERSIONINFO_NAME \"pthreadGCE2.DLL\\0\"\n#    define PTW32_VERSIONINFO_DESCRIPTION \"GNU C++ \" PTW32_ARCH \"\\0\"\n#  else\n#    error Resource compiler doesn't know which cleanup style you're using - see version.rc\n#  endif\n#elif defined(__BORLANDC__)\n#  if defined(__CLEANUP_C)\n#    define PTW32_VERSIONINFO_NAME \"pthreadBC2.DLL\\0\"\n#    define PTW32_VERSIONINFO_DESCRIPTION \"BORLAND C \" PTW32_ARCH \"\\0\"\n#  elif defined(__CLEANUP_CXX)\n#    define PTW32_VERSIONINFO_NAME \"pthreadBCE2.DLL\\0\"\n#    define PTW32_VERSIONINFO_DESCRIPTION \"BORLAND C++ \" PTW32_ARCH \"\\0\"\n#  else\n#    error Resource compiler doesn't know which cleanup style you're using - see version.rc\n#  endif\n#elif defined(__WATCOMC__)\n#  if defined(__CLEANUP_C)\n#    define PTW32_VERSIONINFO_NAME \"pthreadWC2.DLL\\0\"\n#    define PTW32_VERSIONINFO_DESCRIPTION \"WATCOM C \" PTW32_ARCH \"\\0\"\n#  elif defined(__CLEANUP_CXX)\n#    define PTW32_VERSIONINFO_NAME \"pthreadWCE2.DLL\\0\"\n#    define PTW32_VERSIONINFO_DESCRIPTION \"WATCOM C++ \" PTW32_ARCH \"\\0\"\n#  else\n#    error Resource compiler doesn't know which cleanup style you're using - see version.rc\n#  endif\n#else\n#  error Resource compiler doesn't know which compiler you're using - see version.rc\n#endif\n\n\nVS_VERSION_INFO VERSIONINFO\n  FILEVERSION    PTW32_VERSION\n  PRODUCTVERSION PTW32_VERSION\n  FILEFLAGSMASK  VS_FFI_FILEFLAGSMASK\n  FILEFLAGS      0\n  FILEOS         VOS__WINDOWS32\n  FILETYPE       VFT_DLL\nBEGIN\n    BLOCK \"StringFileInfo\"\n    BEGIN\n        BLOCK \"040904b0\"\n        BEGIN\n            VALUE \"ProductName\", \"POSIX Threads for Windows LPGL\\0\"\n            VALUE \"ProductVersion\", PTW32_VERSION_STRING\n            VALUE \"FileVersion\", PTW32_VERSION_STRING\n            VALUE \"FileDescription\", PTW32_VERSIONINFO_DESCRIPTION\n            VALUE \"InternalName\", PTW32_VERSIONINFO_NAME\n            VALUE \"OriginalFilename\", PTW32_VERSIONINFO_NAME\n            VALUE \"CompanyName\", \"Open Source Software community LGPL\\0\"\n            VALUE \"LegalCopyright\", \"Copyright (C) Project contributors 2012\\0\"\n            VALUE \"Comments\", \"http://sourceware.org/pthreads-win32/\\0\"\n        END\n    END\n    BLOCK \"VarFileInfo\"\n    BEGIN\n        VALUE \"Translation\", 0x409, 1200\n    END\nEND\n\n/*\nVERSIONINFO Resource\n\nThe VERSIONINFO resource-definition statement creates a version-information\nresource. The resource contains such information about the file as its\nversion number, its intended operating system, and its original filename.\nThe resource is intended to be used with the Version Information functions.\n\nversionID VERSIONINFO fixed-info  { block-statement...}\n\nversionID\n    Version-information resource identifier. This value must be 1.\n\nfixed-info\n    Version information, such as the file version and the intended operating\n    system. This parameter consists of the following statements.\n\n\n    Statement \t\tDescription\n    --------------------------------------------------------------------------\n    FILEVERSION\n    version \t\tBinary version number for the file. The version\n\t\t\tconsists of two 32-bit integers, defined by four\n\t\t\t16-bit integers. For example, \"FILEVERSION 3,10,0,61\"\n\t\t\tis translated into two doublewords: 0x0003000a and\n\t\t\t0x0000003d, in that order. Therefore, if version is\n\t\t\tdefined by the DWORD values dw1 and dw2, they need\n\t\t\tto appear in the FILEVERSION statement as follows:\n\t\t\tHIWORD(dw1), LOWORD(dw1), HIWORD(dw2), LOWORD(dw2).\n    PRODUCTVERSION\n    version \t\tBinary version number for the product with which the\n\t\t\tfile is distributed. The version parameter is two\n\t\t\t32-bit integers, defined by four 16-bit integers.\n\t\t\tFor more information about version, see the\n\t\t\tFILEVERSION description.\n    FILEFLAGSMASK\n    fileflagsmask \tBits in the FILEFLAGS statement are valid. If a bit\n\t\t\tis set, the corresponding bit in FILEFLAGS is valid.\n    FILEFLAGSfileflags \tAttributes of the file. The fileflags parameter must\n\t\t\tbe the combination of all the file flags that are\n\t\t\tvalid at compile time. For 16-bit Windows, this\n\t\t\tvalue is 0x3f.\n    FILEOSfileos \tOperating system for which this file was designed.\n\t\t\tThe fileos parameter can be one of the operating\n\t\t\tsystem values given in the Remarks section.\n    FILETYPEfiletype \tGeneral type of file. The filetype parameter can be\n\t\t\tone of the file type values listed in the Remarks\n\t\t\tsection.\n    FILESUBTYPE\n    subtype \t\tFunction of the file. The subtype parameter is zero\n\t\t\tunless the type parameter in the FILETYPE statement\n\t\t\tis VFT_DRV, VFT_FONT, or VFT_VXD. For a list of file\n\t\t\tsubtype values, see the Remarks section.\n\nblock-statement\n    Specifies one or more version-information blocks. A block can contain\n    string information or variable information. For more information, see\n    StringFileInfo Block or VarFileInfo Block.\n\nRemarks\n\nTo use the constants specified with the VERSIONINFO statement, you must\ninclude the Winver.h or Windows.h header file in the resource-definition file.\n\nThe following list describes the parameters used in the VERSIONINFO statement:\n\nfileflags\n    A combination of the following values.\n\n    Value \t\tDescription\n\n    VS_FF_DEBUG \tFile contains debugging information or is compiled\n\t\t\twith debugging features enabled.\n    VS_FF_PATCHED \tFile has been modified and is not identical to the\n\t\t\toriginal shipping file of the same version number.\n    VS_FF_PRERELEASE \tFile is a development version, not a commercially\n\t\t\treleased product.\n    VS_FF_PRIVATEBUILD \tFile was not built using standard release procedures.\n\t\t\tIf this value is given, the StringFileInfo block must\n\t\t\tcontain a PrivateBuild string.\n    VS_FF_SPECIALBUILD \tFile was built by the original company using standard\n\t\t\trelease procedures but is a variation of the standard\n\t\t\tfile of the same version number. If this value is\n\t\t\tgiven, the StringFileInfo block must contain a\n\t\t\tSpecialBuild string.\n\nfileos\n    One of the following values.\n\n    Value \t\tDescription\n\n    VOS_UNKNOWN \tThe operating system for which the file was designed\n\t\t\tis unknown.\n    VOS_DOS \t\tFile was designed for MS-DOS.\n    VOS_NT \t\tFile was designed for Windows Server 2003 family,\n\t\t\tWindows XP, Windows 2000, or Windows NT.\n    VOS__WINDOWS16 \tFile was designed for 16-bit Windows.\n    VOS__WINDOWS32 \tFile was designed for 32-bit Windows.\n    VOS_DOS_WINDOWS16 \tFile was designed for 16-bit Windows running with\n\t\t\tMS-DOS.\n    VOS_DOS_WINDOWS32 \tFile was designed for 32-bit Windows running with\n\t\t\tMS-DOS.\n    VOS_NT_WINDOWS32 \tFile was designed for Windows Server 2003 family,\n\t\t\tWindows XP, Windows 2000, or Windows NT.\n\n    The values 0x00002L, 0x00003L, 0x20000L and 0x30000L are reserved.\n\nfiletype\n    One of the following values.\n\n    Value \t\tDescription\n\n    VFT_UNKNOWN \tFile type is unknown.\n    VFT_APP \t\tFile contains an application.\n    VFT_DLL \t\tFile contains a dynamic-link library (DLL).\n    VFT_DRV \t\tFile contains a device driver. If filetype is\n\t\t\tVFT_DRV, subtype contains a more specific\n\t\t\tdescription of the driver.\n    VFT_FONT \t\tFile contains a font. If filetype is VFT_FONT,\n\t\t\tsubtype contains a more specific description of the\n\t\t\tfont.\n    VFT_VXD \t\tFile contains a virtual device.\n    VFT_STATIC_LIB \tFile contains a static-link library.\n\n    All other values are reserved for use by Microsoft.\n\nsubtype\n    Additional information about the file type.\n\n    If filetype specifies VFT_DRV, this parameter can be one of the\n    following values.\n\n    Value \t\t\tDescription\n\n    VFT2_UNKNOWN \t\tDriver type is unknown.\n    VFT2_DRV_COMM \t\tFile contains a communications driver.\n    VFT2_DRV_PRINTER \t\tFile contains a printer driver.\n    VFT2_DRV_KEYBOARD \t\tFile contains a keyboard driver.\n    VFT2_DRV_LANGUAGE \t\tFile contains a language driver.\n    VFT2_DRV_DISPLAY \t\tFile contains a display driver.\n    VFT2_DRV_MOUSE \t\tFile contains a mouse driver.\n    VFT2_DRV_NETWORK \t\tFile contains a network driver.\n    VFT2_DRV_SYSTEM \t\tFile contains a system driver.\n    VFT2_DRV_INSTALLABLE \tFile contains an installable driver.\n    VFT2_DRV_SOUND \t\tFile contains a sound driver.\n    VFT2_DRV_VERSIONED_PRINTER \tFile contains a versioned printer driver.\n\n    If filetype specifies VFT_FONT, this parameter can be one of the\n    following values.\n\n    Value \t\tDescription\n\n    VFT2_UNKNOWN \tFont type is unknown.\n    VFT2_FONT_RASTER \tFile contains a raster font.\n    VFT2_FONT_VECTOR \tFile contains a vector font.\n    VFT2_FONT_TRUETYPE \tFile contains a TrueType font.\n\n    If filetype specifies VFT_VXD, this parameter must be the virtual-device\n    identifier included in the virtual-device control block.\n\n    All subtype values not listed here are reserved for use by Microsoft.\n\nlangID\n    One of the following language codes.\n\n    Code \tLanguage \t\tCode \tLanguage\n\n    0x0401 \tArabic \t\t\t0x0415 \tPolish\n    0x0402 \tBulgarian \t\t0x0416 \tPortuguese (Brazil)\n    0x0403 \tCatalan \t\t0x0417 \tRhaeto-Romanic\n    0x0404 \tTraditional Chinese \t0x0418 \tRomanian\n    0x0405 \tCzech \t\t\t0x0419 \tRussian\n    0x0406 \tDanish \t\t\t0x041A \tCroato-Serbian (Latin)\n    0x0407 \tGerman \t\t\t0x041B \tSlovak\n    0x0408 \tGreek \t\t\t0x041C \tAlbanian\n    0x0409 \tU.S. English \t\t0x041D \tSwedish\n    0x040A \tCastilian Spanish \t0x041E \tThai\n    0x040B \tFinnish \t\t0x041F \tTurkish\n    0x040C \tFrench \t\t\t0x0420 \tUrdu\n    0x040D \tHebrew \t\t\t0x0421 \tBahasa\n    0x040E \tHungarian \t\t0x0804 \tSimplified Chinese\n    0x040F \tIcelandic \t\t0x0807 \tSwiss German\n    0x0410 \tItalian \t\t0x0809 \tU.K. English\n    0x0411 \tJapanese \t\t0x080A \tMexican Spanish\n    0x0412 \tKorean \t\t\t0x080C \tBelgian French\n    0x0413 \tDutch \t\t\t0x0C0C \tCanadian French\n    0x0414 \tNorwegian – Bokmal \t0x100C \tSwiss French\n    0x0810 \tSwiss Italian \t\t0x0816 \tPortuguese (Portugal)\n    0x0813 \tBelgian Dutch \t\t0x081A \tSerbo-Croatian (Cyrillic)\n    0x0814 \tNorwegian – Nynorsk \t  \t \n\ncharsetID\n    One of the following character-set identifiers.\n\n    Identifier \tCharacter Set\n\n    0 \t\t7-bit ASCII\n    932 \tJapan (Shift \u001b%G–\u001b%@ JIS X-0208)\n    949 \tKorea (Shift \u001b%G–\u001b%@ KSC 5601)\n    950 \tTaiwan (Big5)\n    1200 \tUnicode\n    1250 \tLatin-2 (Eastern European)\n    1251 \tCyrillic\n    1252 \tMultilingual\n    1253 \tGreek\n    1254 \tTurkish\n    1255 \tHebrew\n    1256 \tArabic\n\nstring-name\n    One of the following predefined names.\n\n    Name \t\tDescription\n\n    Comments \t\tAdditional information that should be displayed for\n\t\t\tdiagnostic purposes.\n    CompanyName \tCompany that produced the file\u001b%G—\u001b%@for example,\n\t\t\t\"Microsoft Corporation\" or \"Standard Microsystems\n\t\t\tCorporation, Inc.\" This string is required.\n    FileDescription \tFile description to be presented to users. This\n\t\t\tstring may be displayed in a list box when the user\n\t\t\tis choosing files to install\u001b%G—\u001b%@for example,\n\t\t\t\"Keyboard Driver for AT-Style Keyboards\". This\n\t\t\tstring is required.\n    FileVersion \tVersion number of the file\u001b%G—\u001b%@for example,\n\t\t\t\"3.10\" or \"5.00.RC2\". This string is required.\n    InternalName \tInternal name of the file, if one exists — for\n\t\t\texample, a module name if the file is a dynamic-link\n\t\t\tlibrary. If the file has no internal name, this\n\t\t\tstring should be the original filename, without\n\t\t\textension. This string is required.\n    LegalCopyright \tCopyright notices that apply to the file. This\n\t\t\tshould include the full text of all notices, legal\n\t\t\tsymbols, copyright dates, and so on — for example,\n\t\t\t\"Copyright (C) Microsoft Corporation 1990–1999\".\n\t\t\tThis string is optional.\n    LegalTrademarks \tTrademarks and registered trademarks that apply to\n\t\t\tthe file. This should include the full text of all\n\t\t\tnotices, legal symbols, trademark numbers, and so on.\n\t\t\tThis string is optional.\n    OriginalFilename \tOriginal name of the file, not including a path.\n\t\t\tThis information enables an application to determine\n\t\t\twhether a file has been renamed by a user. The\n\t\t\tformat of the name depends on the file system for\n\t\t\twhich the file was created. This string is required.\n    PrivateBuild \tInformation about a private version of the file — for\n\t\t\texample, \"Built by TESTER1 on \\TESTBED\". This string\n\t\t\tshould be present only if VS_FF_PRIVATEBUILD is\n\t\t\tspecified in the fileflags parameter of the root\n\t\t\tblock.\n    ProductName \tName of the product with which the file is\n\t\t\tdistributed. This string is required.\n    ProductVersion \tVersion of the product with which the file is\n\t\t\tdistributed — for example, \"3.10\" or \"5.00.RC2\".\n\t\t\tThis string is required.\n    SpecialBuild \tText that indicates how this version of the file\n\t\t\tdiffers from the standard version — for example,\n\t\t\t\"Private build for TESTER1 solving mouse problems\n\t\t\ton M250 and M250E computers\". This string should be\n\t\t\tpresent only if VS_FF_SPECIALBUILD is specified in\n\t\t\tthe fileflags parameter of the root block.\n */\n"
  },
  {
    "path": "deps/w32-pthreads/w32-pthreadsConfig.cmake.in",
    "content": "# - Config file for the w32-pthreads package\n# It defines the following variables\n#  THREADS_INCLUDE_DIRS\n#  THREADS_LIBRARIES\n\nset(W32_PTHREADS_INCLUDE_DIRS \"@CONF_INCLUDE_DIRS@\")\n\n# Cleanup possible relative paths\nget_filename_component(W32_PTHREADS_INCLUDE_DIRS \"${THREADS_INCLUDE_DIRS}\" ABSOLUTE)\n\nif(NOT TARGET w32-pthreads)\n\tinclude(\"${CMAKE_CURRENT_LIST_DIR}/w32-pthreadsTarget.cmake\")\nendif()\n\nset(W32_PTHREADS_LIBRARIES w32-pthreads)\n"
  },
  {
    "path": "deps/w32-pthreads/w32-pthreadsConfigVersion.cmake.in",
    "content": ""
  },
  {
    "path": "deps/w32-pthreads/w32_CancelableWait.c",
    "content": "/*\n * w32_CancelableWait.c\n *\n * Description:\n * This translation unit implements miscellaneous thread functions.\n *\n * --------------------------------------------------------------------------\n *\n *      Pthreads-win32 - POSIX Threads Library for Win32\n *      Copyright(C) 1998 John E. Bossom\n *      Copyright(C) 1999,2005 Pthreads-win32 contributors\n * \n *      Contact Email: rpj@callisto.canberra.edu.au\n * \n *      The current list of contributors is contained\n *      in the file CONTRIBUTORS included with the source\n *      code distribution. The list can also be seen at the\n *      following World Wide Web location:\n *      http://sources.redhat.com/pthreads-win32/contributors.html\n * \n *      This library is free software; you can redistribute it and/or\n *      modify it under the terms of the GNU Lesser General Public\n *      License as published by the Free Software Foundation; either\n *      version 2 of the License, or (at your option) any later version.\n * \n *      This library is distributed in the hope that it will be useful,\n *      but WITHOUT ANY WARRANTY; without even the implied warranty of\n *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n *      Lesser General Public License for more details.\n * \n *      You should have received a copy of the GNU Lesser General Public\n *      License along with this library in the file COPYING.LIB;\n *      if not, write to the Free Software Foundation, Inc.,\n *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\n */\n\n#include \"pthread.h\"\n#include \"implement.h\"\n\n\nstatic INLINE int\nptw32_cancelable_wait (HANDLE waitHandle, DWORD timeout)\n     /*\n      * -------------------------------------------------------------------\n      * This provides an extra hook into the pthread_cancel\n      * mechanism that will allow you to wait on a Windows handle and make it a\n      * cancellation point. This function blocks until the given WIN32 handle is\n      * signaled or pthread_cancel has been called. It is implemented using\n      * WaitForMultipleObjects on 'waitHandle' and a manually reset WIN32\n      * event used to implement pthread_cancel.\n      * \n      * Given this hook it would be possible to implement more of the cancellation\n      * points.\n      * -------------------------------------------------------------------\n      */\n{\n  int result;\n  pthread_t self;\n  ptw32_thread_t * sp;\n  HANDLE handles[2];\n  DWORD nHandles = 1;\n  DWORD status;\n\n  handles[0] = waitHandle;\n\n  self = pthread_self();\n  sp = (ptw32_thread_t *) self.p;\n\n  if (sp != NULL)\n    {\n      /*\n       * Get cancelEvent handle\n       */\n      if (sp->cancelState == PTHREAD_CANCEL_ENABLE)\n\t{\n\n\t  if ((handles[1] = sp->cancelEvent) != NULL)\n\t    {\n\t      nHandles++;\n\t    }\n\t}\n    }\n  else\n    {\n      handles[1] = NULL;\n    }\n\n  status = WaitForMultipleObjects (nHandles, handles, PTW32_FALSE, timeout);\n\n  switch (status - WAIT_OBJECT_0)\n    {\n    case 0:\n      /*\n       * Got the handle.\n       * In the event that both handles are signalled, the smallest index\n       * value (us) is returned. As it has been arranged, this ensures that\n       * we don't drop a signal that we should act on (i.e. semaphore,\n       * mutex, or condition variable etc).\n       */\n      result = 0;\n      break;\n\n    case 1:\n      /*\n       * Got cancel request.\n       * In the event that both handles are signaled, the cancel will\n       * be ignored (see case 0 comment).\n       */\n      ResetEvent (handles[1]);\n\n      if (sp != NULL)\n\t{\n          ptw32_mcs_local_node_t stateLock;\n\t  /*\n\t   * Should handle POSIX and implicit POSIX threads..\n\t   * Make sure we haven't been async-canceled in the meantime.\n\t   */\n\t  ptw32_mcs_lock_acquire (&sp->stateLock, &stateLock);\n\t  if (sp->state < PThreadStateCanceling)\n\t    {\n\t      sp->state = PThreadStateCanceling;\n\t      sp->cancelState = PTHREAD_CANCEL_DISABLE;\n\t      ptw32_mcs_lock_release (&stateLock);\n\t      ptw32_throw (PTW32_EPS_CANCEL);\n\n\t      /* Never reached */\n\t    }\n\t  ptw32_mcs_lock_release (&stateLock);\n\t}\n\n      /* Should never get to here. */\n      result = EINVAL;\n      break;\n\n    default:\n      if (status == WAIT_TIMEOUT)\n\t{\n\t  result = ETIMEDOUT;\n\t}\n      else\n\t{\n\t  result = EINVAL;\n\t}\n      break;\n    }\n\n  return (result);\n\n}\t\t\t\t/* CancelableWait */\n\nint\npthreadCancelableWait (HANDLE waitHandle)\n{\n  return (ptw32_cancelable_wait (waitHandle, INFINITE));\n}\n\nint\npthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout)\n{\n  return (ptw32_cancelable_wait (waitHandle, timeout));\n}\n"
  },
  {
    "path": "docs/sphinx/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nSPHINXPROJ    = OBSStudio\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)"
  },
  {
    "path": "docs/sphinx/_build/.gitignore",
    "content": ""
  },
  {
    "path": "docs/sphinx/_static/.gitignore",
    "content": ""
  },
  {
    "path": "docs/sphinx/_static/css/custom.css",
    "content": "/* Override \"references as links\" style to match standard links.\n   Example code: :c:func:`OBS_DECLARE_MODULE()` */\na.reference > code.xref > span.pre {\n    color: #2980b9;\n}\n\na.reference:hover > code.xref > span.pre {\n    color: #3091d1;\n}\n\na.reference:visited > code.xref > span.pre {\n    color: #9b59b6;\n}\n"
  },
  {
    "path": "docs/sphinx/_templates/.gitignore",
    "content": ""
  },
  {
    "path": "docs/sphinx/backend-design.rst",
    "content": "OBS Studio Backend Design\n=========================\nThe OBS Studio backend is powered by the library libobs.  Libobs\nprovides the main pipeline, the video/audio subsystems, and the general\nframework for all plugins.\n\n\nLibobs Plugin Objects\n---------------------\nLibobs is designed to be modular, where adding modules will add custom\nfunctionality.  There are four libobs objects that you can make plugins\nfor:\n\n- :ref:`plugins_sources` -- Sources are used to render video and/or\n  audio on stream.  Things such as capturing displays/games/audio,\n  playing a video, showing an image, or playing audio.  Sources can also\n  be used to implement audio and video filters.\n\n- :ref:`plugins_outputs` -- Outputs allow the ability to output the\n  currently rendering audio/video.  Streaming and recording are two\n  common examples of outputs, but not the only types of outputs.\n  Outputs can receive the raw data or receive encoded data.\n\n- :ref:`plugins_encoders` -- Encoders are OBS-specific implementations\n  of video/audio encoders, which are used with outputs that use\n  encoders.  x264, NVENC, Quicksync are examples of encoder\n  implementations.\n\n- :ref:`plugins_services` -- Services are custom implementations of\n  streaming services, which are used with outputs that stream.  For\n  example, you could have a custom implementation for streaming to\n  Twitch, and another for YouTube to allow the ability to log in and use\n  their APIs to do things such as get the RTMP servers or control the\n  channel.\n\n*(Author's note: the service API is incomplete as of this writing)*\n\n\nLibobs Threads\n--------------\nThere are three primary threads spawned by libobs on initialization:\n\n- The obs_graphics_thread_ function used exclusively for rendering in\n  `libobs/obs-video.c`_\n\n- The video_thread_ function used exclusively for video encoding/output\n  in `libobs/media-io/video-io.c`_\n\n- The audio_thread_ function used for all audio\n  processing/encoding/output in `libobs/media-io/audio-io.c`_\n\n*(Author's note: obs_graphics_thread was originally named\nobs_video_thread; it was renamed as of this writing to prevent confusion\nwith video_thread)*\n\n\n.. _output_channels:\n\nOutput Channels\n---------------\nRendering video or audio starts from output channels.  You assign a\nsource to an output channel via the :c:func:`obs_set_output_source()`\nfunction.  The *channel* parameter can be any number from\n0..(MAX_CHANNELS_-1).  You may initially think that this is how you\ndisplay multiple sources at once; however, sources are hierarchical.\nSources such as scenes or transitions can have multiple sub-sources, and\nthose sub-sources in turn can have sub-sources and so on (see\n:ref:`displaying_sources` for more information).  Typically, you would\nuse scenes to draw multiple sources as a group with specific transforms\nfor each source, as a scene is just another type of source.  The\n\"channel\" design allows for highly complex video presentation setups.\nThe OBS Studio front-end has yet to even fully utilize this back-end\ndesign for its rendering, and currently only uses one output channel to\nrender one scene at a time.  It does however utilize additional channels\nfor things such as global audio sources which are set in audio settings.\n\n*(Author's note: \"Output channels\" are not to be confused with output\nobjects or audio channels.  Output channels are used to set the sources\nyou want to output, and output objects are used for actually\nstreaming/recording/etc.)*\n\n\nGeneral Video Pipeline Overview\n-------------------------------\nThe video graphics pipeline is run from two threads: a dedicated\ngraphics thread that renders preview displays as well as the final mix\n(the obs_graphics_thread_ function in `libobs/obs-video.c`_), and a\ndedicated thread specific to video encoding/output (the video_thread_\nfunction in `libobs/media-io/video-io.c`_).\n\nSources assigned to output channels will be drawn from channels\n0..(MAX_CHANNELS_-1).  They are drawn on to the final texture which will\nbe used for output `[1]`_.  Once all sources are drawn, the final\ntexture is converted to whatever format that libobs is set to (typically\na YUV format).  After being converted to the back-end video format, it's\nthen sent along with its timestamp to the current video handler,\n`obs_core_video::video`_.\n\nIt then puts that raw frame in a queue of MAX_CACHE_SIZE_ in the `video\noutput handler`_.  A semaphore is posted, then the video-io thread will\nprocess frames as it's able.  If the video frame queue is full, it will\nduplicate the last frame in the queue in an attempt to reduce video\nencoding complexity (and thus CPU usage) `[2]`_.  This is why you may\nsee frame skipping when the encoder can't keep up.  Frames are sent to\nany raw outputs or video encoders that are currently active `[3]`_.\n\nIf it's sent to a video encoder object (`libobs/obs-encoder.c`_), it\nencodes the frame and sends the encoded packet off to the outputs that\nencoder is connected to (which can be multiple).  If the output takes\nboth encoded video/audio, it puts the packets in an interleave queue to\nensure encoded packets are sent in monotonic timestamp order `[4]`_.\n\nThe encoded packet or raw frame is then sent to the output.\n\n\nGeneral Audio Pipeline Overview\n-------------------------------\nThe audio pipeline is run from a dedicated audio thread in the audio\nhandler (the `audio_thread`_ function in `libobs/media-io/audio-io.c`_);\nassuming that AUDIO_OUTPUT_FRAMES_ is set to 1024, the audio thread\n\"ticks\" (processes audio data) once every 1024 audio samples (around\nevery 21 millisecond intervals at 48khz), and calls the audio_callback_\nfunction in `libobs/obs-audio.c`_ where most of the audio processing is\naccomplished.\n\nA source with audio will output its audio via the\nobs_source_output_audio_ function, and that audio data will be appended\nor inserted in to the circular buffer `obs_source::audio_input_buf`_.\nIf the sample rate or channel count does not match what the back-end is\nset to, the audio is automatically remixed/resampled via swresample\n`[5]`_.  Before insertion, audio data is also run through any audio\nfilters attached to the source `[6]`_.\n\nEach audio tick, the audio thread takes a reference snapshot of the\naudio source tree (stores references of all sources that output/process\naudio) `[7]`_.  On each audio leaf (audio source), it takes the closest\naudio (relative to the current audio thread timestamp) stored in the\ncircular buffer `obs_source::audio_input_buf`_, and puts it in\n`obs_source::audio_output_buf`_.\n\nThen, the audio samples stored in `obs_source::audio_output_buf`_ of the\nleaves get sent through their parents in the source tree snapshot for\nmixing or processing at each source node in the hierarchy `[8]`_.\nSources with multiple children such as scenes or transitions will\nmix/process their children's audio themselves via the\n`obs_source_info::audio_render`_ callback.  This allows, for example,\ntransitions to fade in the audio of one source and fade in the audio of\na new source when they're transitioning between two sources.  The mix or\nprocessed audio data is then stored in `obs_source::audio_output_buf`_\nof that node similarly, and the process is repeated until the audio\nreaches the root nodes of the tree.\n\nFinally, when the audio has reached the base of the snapshot tree, the\naudio of all the sources in each output channel are mixed together for a\nfinal mix `[9]`_.  That final mix is then sent to any raw outputs or\naudio encoders that are currently active `[10]`_.\n\nIf it's sent to an audio encoder object (`libobs/obs-encoder.c`_), it\nencodes the audio data and sends the encoded packet off to the outputs\nthat encoder is connected to (which can be multiple).  If the output\ntakes both encoded video/audio, it puts the packets in an interleave\nqueue to ensure encoded packets are sent in monotonic timestamp order\n`[4]`_.\n\nThe encoded packet or raw audio data is then sent to the output.\n\n.. _obs_graphics_thread: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-video.c#L588-L651\n.. _libobs/obs-audio.c: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-audio.c\n.. _libobs/obs-video.c: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-video.c\n.. _video_thread: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/video-io.c#L169-L195\n.. _libobs/media-io/video-io.c: https://github.com/obsproject/obs-studio/blob/master/libobs/media-io/video-io.c\n.. _video output handler: https://github.com/obsproject/obs-studio/blob/master/libobs/media-io/video-io.c\n.. _audio_thread: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/audio-io.c#L241-L282\n.. _libobs/media-io/audio-io.c: https://github.com/obsproject/obs-studio/blob/master/libobs/media-io/audio-io.c\n.. _MAX_CHANNELS: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-defs.h#L20-L21\n.. _[1]: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-video.c#L99-L129\n.. _obs_core_video::video: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-internal.h#L250\n.. _MAX_CACHE_SIZE: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/video-io.c#L34\n.. _[2]: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/video-io.c#L431-L434\n.. _[3]: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/video-io.c#L115-L167\n.. _libobs/obs-encoder.c: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-encoder.c\n.. _[4]: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-output.c#L1382-L1439\n.. _AUDIO_OUTPUT_FRAMES: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/audio-io.h#L30\n.. _audio_callback: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-audio.c#L367-L485\n.. _obs_source_output_audio: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-source.c#L2578-L2608\n.. _obs_source::audio_input_buf: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-source.c#L1280-L1283\n.. _[5]: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-source.c#L2561-L2563\n.. _[6]: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-source.c#L2591\n.. _[7]: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-audio.c#L393-L415\n.. _obs_source::audio_output_buf: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-internal.h#L580\n.. _[8]: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-audio.c#L417-L423\n.. _obs_source_info::audio_render: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-source.h#L410-L412\n.. _[9]: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/obs-audio.c#L436-L453\n.. _[10]: https://github.com/obsproject/obs-studio/blob/2c58185af3c85f4e594a4c067c9dfe5fa4b5b0a9/libobs/media-io/audio-io.c#L144-L165\n"
  },
  {
    "path": "docs/sphinx/conf.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# OBS Studio documentation build configuration file, created by\n# sphinx-quickstart on Wed Oct 25 00:03:21 2017.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\n# import os\n# import sys\n# sys.path.insert(0, os.path.abspath('.'))\n\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    \"sphinx.ext.autodoc\",\n    \"sphinx.ext.coverage\",\n    \"sphinx.ext.viewcode\",\n    \"sphinx.ext.napoleon\",\n    \"sphinx_rtd_theme\",\n    \"sphinx.ext.autosectionlabel\",\n    \"sphinx.ext.extlinks\",\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\nprimary_domain = 'c'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = 'OBS Studio'\ncopyright = '2017-2022, Lain Bailey'\nauthor = 'Lain Bailey'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = '27.2.0'\n# The full version, including alpha/beta/rc tags.\nrelease = '27.2.0'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This patterns also effect to html_static_path and html_extra_path\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = False\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'sphinx_rtd_theme'\nhtml_theme_options = {\n    \"logo_only\": True,\n    \"navigation_depth\": 5,\n    'prev_next_buttons_location': 'both',\n    'vcs_pageview_mode': 'edit',\n    'style_external_links': True,\n    'style_nav_header_background': '#202C73',\n    'collapse_navigation': False\n}\n\nhtml_logo = 'logo.svg'\n\nhtml_favicon = 'favicon.ico'\n\nhtml_context = {\n    \"display_github\": True, # Integrate GitHub\n    \"github_user\": \"OBSProject\", # Username\n    \"github_repo\": \"obs-studio\", # Repo name\n    \"github_version\": \"master\", # Version\n    \"conf_py_path\": \"/docs/sphinx/\", # Path in the checkout to the docs root\n}\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# html_theme_options = {}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\nhtml_css_files = [\n    'css/custom.css',\n]\n\n# Custom sidebar templates, must be a dictionary that maps document names\n# to template names.\n#\n# This is required for the alabaster theme\n# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars\nhtml_sidebars = {\n    '**': [\n        'relations.html',  # needs 'show_related': True theme option to display\n        'searchbox.html',\n    ]\n}\n\nhtml_link_suffix = None\n\n# -- Options for HTMLHelp output ------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'OBSStudiodoc'\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, 'OBSStudio.tex', 'OBS Studio Documentation',\n     'Lain Bailey', 'manual'),\n]\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, 'obsstudio', 'OBS Studio Documentation',\n     [author], 1)\n]\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, 'OBSStudio', 'OBS Studio Documentation',\n     author, 'OBSStudio', 'One line description of project.',\n     'Miscellaneous'),\n]\n\nextlinks = {\n    'source': ('https://github.com/OBSProject/obs-studio/blob/master/%s', '%s'),\n    'wiki': ('https://obsproject.com/wiki/%s', 'wiki/%s')\n}\nextlinks_detect_hardcoded_links = True\n\nautosectionlabel_prefix_document = True\n"
  },
  {
    "path": "docs/sphinx/frontends.rst",
    "content": "Frontends\n=========\n\nInitialization and Shutdown\n---------------------------\n\nTo initialize libobs, you must call :c:func:`obs_startup()`,\n:c:func:`obs_reset_video()`, and then :c:func:`obs_reset_audio()`.\nAfter that, modules typically should be loaded.\n\nYou can load individual modules manually by calling\n:c:func:`obs_open_module()`.  After loading, the\n:c:func:`obs_open_module()` function, you must then call\n:c:func:`obs_init_module()` to initialize the module.\n\nYou can load modules automatically via two functions:\n:c:func:`obs_add_module_path()` and :c:func:`obs_load_all_modules()`.\n\nAfter all plugin modules have been loaded, call\n:c:func:`obs_post_load_modules()`.\n\nCertain modules may optionally use a configuration storage directory,\nwhich is set as a parameter to :c:func:`obs_startup()`.\n\nWhen it's time to shut down the frontend, make sure to release all\nreferences to any objects, free any data, and then call\n:c:func:`obs_shutdown()`.  If for some reason any libobs objects have\nnot been released, they will be destroyed automatically and a warning\nwill be logged.\n\nTo detect if any general memory allocations have not been freed, call\nthe :c:func:`bnum_allocs()` to get the number of allocations remaining.\nIf the number remaining is above 0, there are memory leaks.\n\nSee :ref:`obs_init_shutdown_reference` for more information.\n\n\nReconfiguring Video\n-------------------\n\nAny time after initialization, video settings can be reconfigured by\ncalling :c:func:`obs_reset_video()` as long as no outputs are active.\nAudio was originally intended to have this capability as well, but\ncurrently is not able to be reset once initialized; libobs must be fully\nshutdown in order to reconfigure audio settings.\n\n\nDisplays\n--------\n\nDisplays as the name implies are used for display/preview panes.  To use\ndisplays, you must have a native window handle or identifier to draw on.\n\nFirst you must call :c:func:`obs_display_create()` to initialize the\ndisplay, then you must assign a draw callback with\n:c:func:`obs_display_add_draw_callback()`.  If you need to remove a draw\ncallback, call :c:func:`obs_display_remove_draw_callback()` similarly.\n\nWhen drawing, to draw the main preview window (if any), call\n:c:func:`obs_render_main_texture()`.  If you need to render a specific\nsource on a secondary display, you can increment its \"showing\" state\nwith :c:func:`obs_source_inc_showing()` while it's showing in the\nsecondary display, draw it with :c:func:`obs_source_video_render()` in\nthe draw callback, then when it's no longer showing in the secondary\ndisplay, call :c:func:`obs_source_dec_showing()`.\n\nIf the display needs to be resized, call :c:func:`obs_display_resize()`.\n\nIf the display needs a custom background color other than black, call\n:c:func:`obs_display_set_background_color()`.\n\nIf the display needs to be temporarily disabled, call\n:c:func:`obs_display_set_enabled()` to disable, and\n:c:func:`obs_display_enabled()` to get its enabled/disabled state.\n\nThen call :c:func:`obs_display_destroy()` to destroy the display when\nit's no longer needed.\n\n*(Important note: do not use more than one display widget within the\nhierarchy of the same base window; this will cause presentation stalls\non macOS.)*\n\nFor an example of how displays are used with Qt, see\n`UI/qt-display.hpp`_ and `UI/qt-display.cpp`_.\n\nSee :ref:`display_reference` for more information.\n\n\nSaving/Loading Objects and Object Management\n--------------------------------------------\n\nThe frontend is generally expected to manage its own objects, however\nfor sources, there are some helper functions to allow easier\nsaving/loading all sources: :c:func:`obs_save_sources()` and\n:c:func:`obs_load_sources()`.  With those functions, all sources that\naren't private will automatically be saved and loaded.  You can also\nsave/load individual sources manually by using\n:c:func:`obs_save_source()` and :c:func:`obs_load_source()`.\n\n*(Author's note: I should not have written those helper functions; the\ndownside is I had to add \"private\" sources that aren't saveable via the*\n:c:func:`obs_source_create_private()` *function.  Just one of the many\nminor design flaws that can occur during long-term development.)*\n\nFor outputs, encoders, and services, there are no helper functions, so\nusually you'd get their settings individually and save them as json.\n(See :c:func:`obs_output_get_settings()`).  You don't have to save each\nobject to different files individually; you'd save multiple objects\ntogether in a bigger :c:type:`obs_data_t` object, then save that via\n:c:func:`obs_data_save_json_safe()`, then load everything again via\n:c:func:`obs_data_create_from_json_file_safe()`.\n\n\nSignals\n-------\n\nThe core, as well as scenes and sources, have a set of standard signals\nthat are used to determine when something happens or changes.\n\nTypically the most important signals are the\n:ref:`output_signal_handler_reference`:  the **start**, **stop**,\n**starting**, **stopping**, **reconnect**, **reconnect_success**\nsignals in particular.\n\nMost other signals for scenes/sources are optional if you are the only\nthing controlling their state.  However, it's generally recommended to\nwatch most signals when possible for consistency.  See\n:ref:`source_signal_handler_reference` and :ref:`scene_signal_reference`\nfor more information.\n\nFor example, let's say you wanted to connect a callback to the **stop**\nsignal of an output.  The **stop** signal has two parameters:  *output*\nand *code*.  A callback for this signal would typically look something\nlike this:\n\n.. code:: cpp\n\n   static void output_stopped(void *my_data, calldata_t *cd)\n   {\n           obs_output_t *output = calldata_ptr(cd, \"output\");\n           int code = calldata_int(cd, \"code\");\n\n           [...]\n   }\n\n*(Note that callbacks are not thread-safe.)*\n\nThen to connect it to the **stop** signal, you use the\n:c:func:`signal_handler_connect()` with the callback.  In this case for\nexample:\n\n.. code:: cpp\n\n   signal_handler_t *handler = obs_output_get_signal_handler(output);\n   signal_handler_connect(handler, \"stop\", output_stopped);\n\n\n.. _displaying_sources:\n\nDisplaying Sources\n------------------\n\nSources are displayed on stream/recording via :ref:`output_channels`\nwith the :c:func:`obs_set_output_source()` function.  There are 64\nchannels that you can assign sources to, which will draw on top of each\nother in ascending index order.  Typically, a normal source shouldn't be\ndirectly assigned with this function; you would use a scene or a\ntransition containing scenes.\n\nTo draw one or more sources together with a specific transform applied\nto them, scenes are used.  To create a scene, you call\n:c:func:`obs_scene_create()`.  Child sources are referenced using scene\nitems, and then specific transforms are applied to those scene items.\nScene items are not sources but containers for sources; the same source\ncan be referenced by multiple scene items within the same scene, or can\nbe referenced in multiple scenes.  To create a scene item that\nreferences a source, you call :c:func:`obs_scene_add()`, which returns a\nnew reference to a scene item.\n\nTo change the transform of a scene item, you typically would call a\nfunction like :c:func:`obs_sceneitem_set_pos()` to change its position,\n:c:func:`obs_sceneitem_set_rot()` to change its rotation, or\n:c:func:`obs_sceneitem_set_scale()` to change its scaling.  Scene items\ncan also force scaling in to a custom size constraint referred to as a\n\"bounding box\"; a bounding box will force the source to be drawn at a\nspecific size and with specific scaling constraint within that size.  To\nuse a bounding box, you call the\n:c:func:`obs_sceneitem_set_bounds_type()`,\n:c:func:`obs_sceneitem_set_bounds()`, and\n:c:func:`obs_sceneitem_set_bounds_alignment()`.  Though the easiest way\nto handle everything related to transforms is to use the\n:c:func:`obs_sceneitem_set_info2()` and\n:c:func:`obs_sceneitem_get_info2()` functions.  See\n:ref:`scene_item_reference` for all the functions related to scene\nitems.\n\nUsually, a smooth transition between multiple scenes is required.  To do\nthis, transitions are used.  To create a transition, you use\n:c:func:`obs_source_create()` or :c:func:`obs_source_create_private()`\nlike any other source.  Then, to activate a transition, you call\n:c:func:`obs_transition_start()`.  When the transition is not active and\nis only displaying one source, it performs a pass-through to the current\ndisplaying source.  See :ref:`transitions` for more functions related to\nusing transitions.\n\nThe recommended way to set up your structure is to have a transition as\nthe source that is used as the main output source, then your scene as a\nchild of the transition, then your sources as children in the scene.\nWhen you need to switch to a new scene, simply call\n:c:func:`obs_transition_start()`.\n\n\nOutputs, Encoders, and Services\n-------------------------------\n\nOutputs, encoders, and services are all used together, and managed a bit\ndifferently than sources.  There currently is no global function to\nsave/load them, that must be accomplished manually for now via their\nsettings if needed.\n\nEncoders are used with outputs that expect encoded data (which is almost\nall typical outputs), such as standard file recording or streaming.\n\nServices are used with outputs to a stream; the `RTMP output`_ is the\nquintessential example of this.\n\nHere's an example of how an output would be used with encoders and\nservices:\n\n.. code:: cpp\n\n   obs_encoder_set_video(my_h264_encoder, obs_get_video());\n   obs_encoder_set_audio(my_aac_encoder, obs_get_audio());\n   obs_output_set_video_encoder(my_output, my_h264_encoder);\n   obs_output_set_audio_encoder(my_output, my_aac_encoder);\n   obs_output_set_service(my_output, my_service); /* if a stream */\n   obs_output_start(my_output);\n\nOnce the output has started successfully, it automatically starts\ncapturing the video and/or audio from the current video/audio output\n(i.e. any sources that are assigned to the :ref:`output_channels`).\n\nIf the output fails to start up, it will send the **stop** signal with\nan error code in the *code* parameter, possibly accompanied by a\ntranslated error message stored that can be obtained via the\n:c:func:`obs_output_get_last_error()` function.\n\n.. --------------------------------------------------------------------\n\n.. _RTMP Output: https://github.com/obsproject/obs-studio/blob/master/plugins/obs-outputs/rtmp-stream.c\n.. _UI/qt-display.hpp: https://github.com/obsproject/obs-studio/blob/master/UI/qt-display.hpp\n.. _UI/qt-display.cpp: https://github.com/obsproject/obs-studio/blob/master/UI/qt-display.cpp\n"
  },
  {
    "path": "docs/sphinx/graphics.rst",
    "content": "Rendering Graphics\n==================\n\nLibobs has a custom-made programmable graphics subsystem that wraps both\nDirect3D 11 and OpenGL.  The reason why it was designed with a custom\ngraphics subsystem was to accommodate custom capture features only\navailable on specific operating systems.\n\n*(Author's note: In retrospect, I probably should have used something\nlike ANGLE, but I would have to modify it to accommodate my specific\nuse-cases.)*\n\nMost rendering is dependent upon effects.  Effects are used by all video\nobjects in libobs; they're used to easily bundle related vertex/pixel\nshaders in to one file.\n\nAn effect file has a nearly identical syntax to Direct3D 11 HLSL effect\nfiles.  The only differences are as follows:\n\n- Sampler states are named \"sampler_state\"\n- Position semantic is called \"POSITION\" rather than \"SV_Position\"\n- Target semantic is called \"TARGET\" rather than \"SV_Target\"\n\n*(Author's note: I'm probably missing a few exceptions here, if I am\nplease let me know)*\n\n\nThe Graphics Context\n--------------------\n\nUsing graphics functions isn't possible unless the current thread has\nentered a graphics context, and the graphics context can only be used by\none thread at a time.  To enter the graphics context, use\n:c:func:`obs_enter_graphics()`, and to leave the graphics context, use\n:c:func:`obs_leave_graphics()`.\n\nCertain callback will automatically be within the graphics context:\n:c:member:`obs_source_info.video_render`, and the draw callback\nparameter of :c:func:`obs_display_add_draw_callback()`, and\n:c:func:`obs_add_main_render_callback()`.\n\n\nCreating Effects\n----------------\n\nEffect Parameters\n^^^^^^^^^^^^^^^^^\n\nTo create an effect, it's recommended to start with the uniforms\n(parameters) of the effect.\n\nThere are a number of different types of uniforms:\n\n+------------------+---------------+------------------+------------+------------+\n| Floating points: | **float**     | **float2**       | **float3** | **float4** |\n+------------------+---------------+------------------+------------+------------+\n| Matrices:        | **float3x3**  | **float4x4**     |            |            |\n+------------------+---------------+------------------+------------+------------+\n| Integers:        | **int**       | **int2**         | **int3**   | **int4**   |\n+------------------+---------------+------------------+------------+------------+\n| Booleans:        | **bool**      |                  |            |            |\n+------------------+---------------+------------------+------------+------------+\n| Textures:        | **texture2d** | **texture_cube** |            |            |\n+------------------+---------------+------------------+------------+------------+\n\nTo get the effect uniform parameters, you use\n:c:func:`gs_effect_get_param_by_name()` or\n:c:func:`gs_effect_get_param_by_idx()`.\n\nThen the uniforms are set through the following functions:\n\n- :c:func:`gs_effect_set_bool()`\n- :c:func:`gs_effect_set_float()`\n- :c:func:`gs_effect_set_int()`\n- :c:func:`gs_effect_set_matrix4()`\n- :c:func:`gs_effect_set_vec2()`\n- :c:func:`gs_effect_set_vec3()`\n- :c:func:`gs_effect_set_vec4()`\n- :c:func:`gs_effect_set_texture()`\n- :c:func:`gs_effect_set_texture_srgb()`\n\nThere are two \"universal\" effect parameters that may be expected of\neffects:  **ViewProj**, and **image**.  The **ViewProj** parameter\n(which is a float4x4) is used for the primary view/projection matrix\ncombination.  The **image** parameter (which is a texture2d) is a\ncommonly used parameter for the main texture; this parameter will be\nused with the functions :c:func:`obs_source_draw()`,\n:c:func:`gs_draw_sprite()`, :c:func:`gs_draw_quadf()`, and\n:c:func:`obs_source_process_filter_end()`.\n\nHere is an example of effect parameters:\n\n.. code:: cpp\n\n   uniform float4x4 ViewProj;\n   uniform texture2d image;\n\n   uniform float4 my_color_param;\n   uniform float my_float_param;\n\nEffect parameters can also have default values.  Default parameters of\nelements that have multiple elements should be treated as an array.\n\nHere are some examples of default parameters:\n\n.. code:: cpp\n\n   uniform float4x4 my_matrix = {1.0, 0.0, 0.0, 0.0,\n                                 0.0, 1.0, 0.0, 0.0,\n                                 0.0, 0.0, 1.0, 0.0,\n                                 0.0, 0.0, 0.0, 1.0};\n\n   uniform float4 my_float4 = {1.0, 0.5, 0.25, 0.0};\n   uniform float my_float = 4.0;\n   uniform int my_int = 5;\n\nEffect Sampler States\n^^^^^^^^^^^^^^^^^^^^^\n\nThen, if textures are used, sampler states should be defined.  Sampler\nstates have certain sub-parameters:\n\n- **Filter** - The type of filtering to use.  Can be one of the\n  following values:\n\n  - **Anisotropy**\n  - **Point**\n  - **Linear**\n  - **MIN_MAG_POINT_MIP_LINEAR**\n  - **MIN_POINT_MAG_LINEAR_MIP_POINT**\n  - **MIN_POINT_MAG_MIP_LINEAR**\n  - **MIN_LINEAR_MAG_MIP_POINT**\n  - **MIN_LINEAR_MAG_POINT_MIP_LINEAR**\n  - **MIN_MAG_LINEAR_MIP_POINT**\n\n- **AddressU**, **AddressV** -  Specifies how to handle the sampling\n  when the coordinate goes beyond 0.0..1.0.  Can be one of the following\n  values:\n\n  - **Wrap** or **Repeat**\n  - **Clamp** or **None**\n  - **Mirror**\n  - **Border** (uses *BorderColor* to fill the color)\n  - **MirrorOnce**\n\n- **BorderColor** - Specifies the border color if using the \"Border\"\n  address mode.  This value should be a hexadecimal value representing\n  the color, in the format of: AARRGGBB.  For example, 7FFF0000 would\n  have its alpha value at 127, its red value at 255, and blue and green\n  at 0.  If *Border* is not used as an addressing type, this value is\n  ignored.\n\nHere is an example of writing a sampler state in an effect file:\n\n.. code:: cpp\n\n   sampler_state defaultSampler {\n           Filter      = Linear;\n           AddressU    = Border;\n           AddressV    = Border;\n           BorderColor = 7FFF0000;\n   };\n\nThis sampler state would use linear filtering, would use border\naddressing for texture coordinate values beyond 0.0..1.0, and the border\ncolor would be the color specified above.\n\nWhen a sampler state is used, it's used identically to the HLSL form:\n\n.. code:: cpp\n\n   [...]\n\n   uniform texture2d image;\n\n   sampler_state defaultSampler {\n           Filter      = Linear;\n           AddressU    = Clamp;\n           AddressV    = Clamp;\n   };\n\n   [...]\n\n   float4 MyPixelShaderFunc(VertInOut vert_in) : TARGET\n   {\n           return image.Sample(def_sampler, vert_in.uv);\n   }\n\nEffect Vertex/Pixel Semantics\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThen structures should be defined for inputs and outputs vertex\nsemantics.\n\nVertex components can have the following semantics:\n\n- **COLOR**          - Color value (*float4*).\n- **POSITION**       - Position value (*float4*).\n- **NORMAL**         - Normal value (*float4*).\n- **TANGENT**        - Tangent value (*float4*).\n- **TEXCOORD[0..7]** - Texture coordinate value (*float2*, *float3*, or\n  *float4*).\n\nHere is an example of a vertex semantic structure:\n\n.. code:: cpp\n\n   struct VertexIn {\n           float4 my_position : POSITION;\n           float2 my_texcoord : TEXCOORD0;\n   };\n\nThese semantic structures are then passed in as a parameter to the\nprimary shader entry point, and used as a return value for the vertex\nshader.  Note that the vertex shader is allowed to return different\nsemantics than it takes in; but the return type of the vertex shader and\nthe parameter of the pixel shader must match.\n\nThe semantic structure used for the parameter to the vertex shader\nfunction will require that the vertex buffer have those values, so if\nyou have POSITION and TEXCOORD0, the vertex buffer will have to have at\nleast a position buffer and a texture coordinate buffer in it.\n\nFor pixel shaders, they need to return with a TARGET semantic (which is\na float4 RGBA value).  Here is an example of how it's usually used with\na pixel shader function:\n\n.. code:: cpp\n\n   float4 MyPixelShaderFunc(VertInOut vert_in) : TARGET\n   {\n           return image.Sample(def_sampler, vert_in.uv);\n   }\n\n\nEffect Techniques\n^^^^^^^^^^^^^^^^^\n\nTechniques are used to define the primary vertex/pixel shader entry\nfunctions per pass.  One technique can have multiple passes or custom\npass setup.\n\n*(Author's note: These days, multiple passes aren't really needed; GPUs\nare powerful enough to where you can perform all actions in the same\nshader.  Named passes can be useful for custom draw setups, but even\nthen you can just make it a separate technique.  For that reason, it's\nbest to just ignore the extra pass functionality.)*\n\nIf you're making an effect filter for video sources, typically you'd\nname the pass **Draw**, and then\n:c:func:`obs_source_process_filter_end()` will automatically call that\nspecific effect name.  However, you can also use\n:c:func:`obs_source_process_filter_tech_end()` to make the filter use a\nspecific technique by its name.\n\nThe first parameter of the vertex/pixel shader functions in passes\nshould always be the name of its vertex semantic structure parameter.\n\nFor techniques, it's better to show some examples of how techniques\nwould be used:\n\n.. code:: cpp\n\n   uniform float4x4 ViewProj;\n   uniform texture2d image;\n\n   struct VertInOut {\n           float4 pos : POSITION;\n           float2 uv : TEXCOORD0;\n   };\n\n   VertInOut MyVertexShaderFunc(VertInOut vert_in)\n   {\n           VertInOut vert_out;\n           vert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);\n           vert_out.uv  = vert_in.uv;\n           return vert_out;\n   }\n\n   float4 MyPixelShaderFunc(VertInOut vert_in) : TARGET\n   {\n           return image.Sample(def_sampler, vert_in.uv);\n   }\n\n   technique Draw\n   {\n           pass\n           {\n                   vertex_shader = MyVertexShaderFunc(vert_in);\n                   pixel_shader  = MyPixelShaderFunc(vert_in);\n           }\n   };\n\nUsing Effects\n-------------\n\nThe recommended way to use effects is like so:\n\n.. code:: cpp\n\n   for (gs_effect_loop(effect, \"technique\")) {\n           [draw calls go here]\n   }\n\nThis will automatically handle loading/unloading of the effect and its\nshaders for a given technique name.\n\n\nRendering Video Sources\n-----------------------\n\nA synchronous video source renders in its\n:c:member:`obs_source_info.video_render` callback.\n\nSources can render with custom drawing (via the OBS_SOURCE_CUSTOM_DRAW\noutput capability flag), or without.  When sources render without custom\nrendering, it's recommended to render a single texture with\n:c:func:`obs_source_draw()`.  Otherwise the source is expected to\nperform rendering on its own and manage its own effects.\n\nLibobs comes with a set of default/standard effects that can be accessed\nvia the :c:func:`obs_get_base_effect()` function.  You can use these\neffects to render, or you can create custom effects with\n:c:func:`gs_effect_create_from_file()` and render with a custom effect.\n\n\nRendering Video Effect Filters\n------------------------------\n\nFor most video effect filters, it comprises of adding a layer of\nprocessing shaders to an existing image in its\n:c:member:`obs_source_info.video_render` callback.  When this is the\ncase, it's expected that the filter has its own effect created, and to\ndraw the effect, one would simply use the\n:c:func:`obs_source_process_filter_begin()` function, set the parameters\non your custom effect, then call either\n:c:func:`obs_source_process_filter_end()` or\n:c:func:`obs_source_process_filter_tech_end()` to finish rendering the\nfilter.\n\nHere's an example of rendering a filter from the color key filter:\n\n.. code:: cpp\n\n   static void color_key_render(void *data, gs_effect_t *effect)\n   {\n           struct color_key_filter_data *filter = data;\n   \n           if (!obs_source_process_filter_begin(filter->context, GS_RGBA,\n                                   OBS_ALLOW_DIRECT_RENDERING))\n                   return;\n   \n           gs_effect_set_vec4(filter->color_param, &filter->color);\n           gs_effect_set_float(filter->contrast_param, filter->contrast);\n           gs_effect_set_float(filter->brightness_param, filter->brightness);\n           gs_effect_set_float(filter->gamma_param, filter->gamma);\n           gs_effect_set_vec4(filter->key_color_param, &filter->key_color);\n           gs_effect_set_float(filter->similarity_param, filter->similarity);\n           gs_effect_set_float(filter->smoothness_param, filter->smoothness);\n   \n           obs_source_process_filter_end(filter->context, filter->effect, 0, 0);\n   \n           UNUSED_PARAMETER(effect);\n   }\n\n"
  },
  {
    "path": "docs/sphinx/index.rst",
    "content": ".. OBS Studio documentation master file, created by\n   sphinx-quickstart on Wed Oct 25 00:03:21 2017.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\n.. _contents:\n\nWelcome to OBS Studio's documentation!\n======================================\n\n* **Setting up an OBS development environment?** :wiki:`Use the Wiki <Install-Instructions>`\n* **Developing your first OBS plugin?** `Use the obs-plugintemplate <https://github.com/obsproject/obs-plugintemplate#obs-plugin-template>`_\n\n.. toctree::\n   :caption: Core Concepts\n   :maxdepth: 1\n\n   backend-design\n   plugins\n   frontends\n   graphics\n   scripting\n\n.. toctree::\n   :caption: API Reference\n   :maxdepth: 2\n\n   OBS Core <reference-core>\n   Modules <reference-modules>\n   Core API Object <reference-core-objects>\n   Platform/Utility <reference-libobs-util>\n   Callbacks (libobs/callback) <reference-libobs-callback>\n   Graphics (libobs/graphics) <reference-libobs-graphics>\n   Media I/O (libobs/media-io) <reference-libobs-media-io>\n   reference-frontend-api\n\n.. toctree::\n   :caption: Additional Resources\n   :maxdepth: 1\n   :hidden:\n\n   Build Instructions <https://obsproject.com/wiki/Install-Instructions>\n   Plugin Template <https://github.com/obsproject/obs-plugintemplate#obs-plugin-template>"
  },
  {
    "path": "docs/sphinx/make.bat",
    "content": "@ECHO OFF\n\npushd %~dp0\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset SOURCEDIR=.\nset BUILDDIR=_build\nset SPHINXPROJ=OBSStudio\n\nif \"%1\" == \"\" goto help\n\n%SPHINXBUILD% >NUL 2>NUL\nif errorlevel 9009 (\n\techo.\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\n\techo.installed, then set the SPHINXBUILD environment variable to point\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\n\techo.may add the Sphinx directory to PATH.\n\techo.\n\techo.If you don't have Sphinx installed, grab it from\n\techo.http://sphinx-doc.org/\n\texit /b 1\n)\n\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\ngoto end\n\n:help\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\n\n:end\npopd\n"
  },
  {
    "path": "docs/sphinx/plugins.rst",
    "content": "Plugins\n=======\nAlmost all custom functionality is added through plugin modules, which\nare typically dynamic libraries or scripts.  The ability to capture\nand/or output audio/video, make a recording, output to an RTMP stream,\nencode in x264 are all examples of things that are accomplished via\nplugin modules.\n\nPlugins can implement sources, outputs, encoders, and services.\n\nWriting your first plugin? We provide `a basic template plugin <https://github.com/obsproject/obs-plugintemplate#obs-plugin-template>`_\nto get you started.\n\nPlugin Module Headers\n---------------------\nThese are some notable headers commonly used by plugins:\n\n- `libobs/obs-module.h`_ -- The primary header used for creating plugin\n  modules.  This file automatically includes the following files: \n\n  - `libobs/obs.h`_ -- The main libobs header.  This file automatically\n    includes the following files:\n\n    - `libobs/obs-source.h`_ -- Used for implementing sources in plugin\n      modules\n\n    - `libobs/obs-output.h`_ -- Used for implementing outputs in plugin\n      modules\n\n    - `libobs/obs-encoder.h`_ -- Used for implementing encoders in\n      plugin modules\n\n    - `libobs/obs-service.h`_ -- Used for implementing services in\n      plugin modules\n\n    - `libobs/obs-data.h`_ -- Used for managing settings for libobs\n      objects\n\n    - `libobs/obs-properties.h`_ -- Used for generating properties for\n      libobs objects\n\n    - `libobs/graphics/graphics.h`_ -- Used for graphics rendering\n\n\nCommon Directory Structure and CMakeLists.txt\n---------------------------------------------\nThe common way source files are organized is to have one file for plugin\ninitialization, and then specific files for each individual object\nyou're implementing.  For example, if you were to create a plugin called\n'my-plugin', you'd have something like my-plugin.c where plugin\ninitialization is done, my-source.c for the definition of a custom\nsource, my-output.c for the definition of a custom output, etc.  (This\nis not a rule of course)\n\nThis is an example of a common directory structure for a native plugin\nmodule::\n\n  my-plugin/data/locale/en-US.ini\n  my-plugin/CMakeLists.txt\n  my-plugin/my-plugin.c\n  my-plugin/my-source.c\n  my-plugin/my-output.c\n  my-plugin/my-encoder.c\n  my-plugin/my-service.c\n\nThis would be an example of a common CMakeLists.txt file associated with\nthese files::\n\n  # my-plugin/CMakeLists.txt\n\n  project(my-plugin)\n\n  set(my-plugin_SOURCES\n        my-plugin.c\n        my-source.c\n        my-output.c\n        my-encoder.c\n        my-service.c)\n\n  add_library(my-plugin MODULE\n        ${my-plugin_SOURCES})\n  target_link_libraries(my-plugin\n        libobs)\n\n  install_obs_plugin_with_data(my-plugin data)\n\n\nNative Plugin Initialization\n----------------------------\nTo create a native plugin module, you will need to include the\n`libobs/obs-module.h`_ header, use :c:func:`OBS_DECLARE_MODULE()` macro,\nthen create a definition of the function :c:func:`obs_module_load()`.\nIn your :c:func:`obs_module_load()` function, you then register any of\nyour custom sources, outputs, encoders, or services.  See the\n:doc:`reference-modules` for more information.\n\nThe following is an example of my-plugin.c, which would register one\nobject of each type:\n\n.. code:: cpp\n\n  /* my-plugin.c */\n  #include <obs-module.h>\n\n  /* Defines common functions (required) */\n  OBS_DECLARE_MODULE()\n\n  /* Implements common ini-based locale (optional) */\n  OBS_MODULE_USE_DEFAULT_LOCALE(\"my-plugin\", \"en-US\")\n\n  extern struct obs_source_info  my_source;  /* Defined in my-source.c  */\n  extern struct obs_output_info  my_output;  /* Defined in my-output.c  */\n  extern struct obs_encoder_info my_encoder; /* Defined in my-encoder.c */\n  extern struct obs_service_info my_service; /* Defined in my-service.c */\n\n  bool obs_module_load(void)\n  {\n          obs_register_source(&my_source);\n          obs_register_output(&my_output);\n          obs_register_encoder(&my_encoder);\n          obs_register_service(&my_service);\n          return true;\n  }\n\n\n.. _plugins_sources:\n\nSources\n-------\nSources are used to render video and/or audio on stream.  Things such as\ncapturing displays/games/audio, playing a video, showing an image, or\nplaying audio.  Sources can also be used to implement audio and video\nfilters as well as transitions.  The `libobs/obs-source.h`_ file is the\ndedicated header for implementing sources.  See the\n:doc:`reference-sources` for more information.\n\nFor example, to implement a source object, you need to define an\n:c:type:`obs_source_info` structure and fill it out with information and\ncallbacks related to your source:\n\n.. code:: cpp\n\n  /* my-source.c */\n\n  [...]\n\n  struct obs_source_info my_source {\n          .id           = \"my_source\",\n          .type         = OBS_SOURCE_TYPE_INPUT,\n          .output_flags = OBS_SOURCE_VIDEO,\n          .get_name     = my_source_name,\n          .create       = my_source_create,\n          .destroy      = my_source_destroy,\n          .update       = my_source_update,\n          .video_render = my_source_render,\n          .get_width    = my_source_width,\n          .get_height   = my_source_height\n  };\n\nThen, in my-plugin.c, you would call :c:func:`obs_register_source()` in\n:c:func:`obs_module_load()` to register the source with libobs.\n\n.. code:: cpp\n\n  /* my-plugin.c */\n\n  [...]\n  \n  extern struct obs_source_info  my_source;  /* Defined in my-source.c  */\n\n  bool obs_module_load(void)\n  {\n          obs_register_source(&my_source);\n\n          [...]\n\n          return true;\n  }\n\nSome simple examples of sources:\n\n- Synchronous video source: The `image source <https://github.com/obsproject/obs-studio/blob/master/plugins/image-source/image-source.c>`_\n- Asynchronous video source: The `random texture test source <https://github.com/obsproject/obs-studio/blob/master/test/test-input/test-random.c>`_\n- Audio source: The `sine wave test source <https://github.com/obsproject/obs-studio/blob/master/test/test-input/test-sinewave.c>`_\n- Video filter: The `test video filter <https://github.com/obsproject/obs-studio/blob/master/test/test-input/test-filter.c>`_\n- Audio filter: The `gain audio filter <https://github.com/obsproject/obs-studio/blob/master/plugins/obs-filters/gain-filter.c>`_\n\n\n.. _plugins_outputs:\n\nOutputs\n-------\nOutputs allow the ability to output the currently rendering audio/video.\nStreaming and recording are two common examples of outputs, but not the\nonly types of outputs.  Outputs can receive the raw data or receive\nencoded data.  The `libobs/obs-output.h`_ file is the dedicated header\nfor implementing outputs.  See the :doc:`reference-outputs` for more\ninformation.\n\nFor example, to implement an output object, you need to define an\n:c:type:`obs_output_info` structure and fill it out with information and\ncallbacks related to your output:\n\n.. code:: cpp\n\n  /* my-output.c */\n\n  [...]\n\n  struct obs_output_info my_output {\n          .id                   = \"my_output\",\n          .flags                = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED,\n          .get_name             = my_output_name,\n          .create               = my_output_create,\n          .destroy              = my_output_destroy,\n          .start                = my_output_start,\n          .stop                 = my_output_stop,\n          .encoded_packet       = my_output_data,\n          .get_total_bytes      = my_output_total_bytes,\n          .encoded_video_codecs = \"h264\",\n          .encoded_audio_codecs = \"aac\"\n  };\n\nThen, in my-plugin.c, you would call :c:func:`obs_register_output()` in\n:c:func:`obs_module_load()` to register the output with libobs.\n\n.. code:: cpp\n\n  /* my-plugin.c */\n\n  [...]\n  \n  extern struct obs_output_info  my_output;  /* Defined in my-output.c  */\n\n  bool obs_module_load(void)\n  {\n          obs_register_output(&my_output);\n\n          [...]\n\n          return true;\n  }\n\nSome examples of outputs:\n\n- Encoded video/audio outputs:\n\n  - The `FLV output <https://github.com/obsproject/obs-studio/blob/master/plugins/obs-outputs/flv-output.c>`_\n  - The `FFmpeg muxer output <https://github.com/obsproject/obs-studio/blob/master/plugins/obs-ffmpeg/obs-ffmpeg-mux.c>`_\n  - The `RTMP stream output <https://github.com/obsproject/obs-studio/blob/master/plugins/obs-outputs/rtmp-stream.c>`_\n\n- Raw video/audio outputs:\n\n  - The `FFmpeg output <https://github.com/obsproject/obs-studio/blob/master/plugins/obs-ffmpeg/obs-ffmpeg-output.c>`_\n\n\n.. _plugins_encoders:\n\nEncoders\n--------\nEncoders are OBS-specific implementations of video/audio encoders, which\nare used with outputs that use encoders.  x264, NVENC, Quicksync are\nexamples of encoder implementations.  The `libobs/obs-encoder.h`_ file\nis the dedicated header for implementing encoders.  See the\n:doc:`reference-encoders` for more information.\n\nFor example, to implement an encoder object, you need to define an\n:c:type:`obs_encoder_info` structure and fill it out with information\nand callbacks related to your encoder:\n\n.. code:: cpp\n\n  /* my-encoder.c */\n\n  [...]\n\n  struct obs_encoder_info my_encoder_encoder = {\n          .id             = \"my_encoder\",\n          .type           = OBS_ENCODER_VIDEO,\n          .codec          = \"h264\",\n          .get_name       = my_encoder_name,\n          .create         = my_encoder_create,\n          .destroy        = my_encoder_destroy,\n          .encode         = my_encoder_encode,\n          .update         = my_encoder_update,\n          .get_extra_data = my_encoder_extra_data,\n          .get_sei_data   = my_encoder_sei,\n          .get_video_info = my_encoder_video_info\n  };\n\nThen, in my-plugin.c, you would call :c:func:`obs_register_encoder()`\nin :c:func:`obs_module_load()` to register the encoder with libobs.\n\n.. code:: cpp\n\n  /* my-plugin.c */\n\n  [...]\n  \n  extern struct obs_encoder_info my_encoder; /* Defined in my-encoder.c */\n\n  bool obs_module_load(void)\n  {\n          obs_register_encoder(&my_encoder);\n\n          [...]\n\n          return true;\n  }\n\n**IMPORTANT NOTE:** Encoder settings currently have a few expected\ncommon setting values that should have a specific naming convention:\n\n  - **\"bitrate\"** - This value should be used for both video and audio\n    encoders: bitrate, in kilobits.\n  - **\"rate_control\"** - This is a setting used for video encoders.\n    It's generally expected to have at least a \"CBR\" rate control.\n    Other common rate controls are \"VBR\", \"CQP\".\n  - **\"keyint_sec\"** - For video encoders, sets the keyframe interval\n    value, in seconds, or closest possible approximation.  *(Author's\n    note: This should have have been \"keyint\", in frames.)*\n\nExamples of encoders:\n\n- Video encoders:\n\n  - The `x264 encoder <https://github.com/obsproject/obs-studio/tree/master/plugins/obs-x264>`_\n  - The `FFmpeg NVENC encoder <https://github.com/obsproject/obs-studio/blob/master/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c>`_\n  - The `Quicksync encoder <https://github.com/obsproject/obs-studio/tree/master/plugins/obs-qsv11>`_\n\n- Audio encoders:\n\n  - The `FFmpeg AAC/Opus encoder <https://github.com/obsproject/obs-studio/blob/master/plugins/obs-ffmpeg/obs-ffmpeg-audio-encoders.c>`_\n\n\n.. _plugins_services:\n\nServices\n--------\nServices are custom implementations of streaming services, which are\nused with outputs that stream.  For example, you could have a custom\nimplementation for streaming to Twitch, and another for YouTube to allow\nthe ability to log in and use their APIs to do things such as get the\nRTMP servers or control the channel.  The `libobs/obs-service.h`_ file\nis the dedicated header for implementing services.  See the\n:doc:`reference-services` for more information.\n\n*(Author's note: the service API is incomplete as of this writing)*\n\nFor example, to implement a service object, you need to define an\n:c:type:`obs_service_info` structure and fill it out with information\nand callbacks related to your service:\n\n.. code:: cpp\n\n  /* my-service.c */\n\n  [...]\n\n  struct obs_service_info my_service_service = {\n          .id       = \"my_service\",\n          .get_name = my_service_name,\n          .create   = my_service_create,\n          .destroy  = my_service_destroy,\n          .encode   = my_service_encode,\n          .update   = my_service_update,\n          .get_url  = my_service_url,\n          .get_key  = my_service_key\n  };\n\nThen, in my-plugin.c, you would call :c:func:`obs_register_service()` in\n:c:func:`obs_module_load()` to register the service with libobs.\n\n.. code:: cpp\n\n  /* my-plugin.c */\n\n  [...]\n  \n  extern struct obs_service_info my_service; /* Defined in my-service.c */\n\n  bool obs_module_load(void)\n  {\n          obs_register_service(&my_service);\n\n          [...]\n\n          return true;\n  }\n\nThe only two existing services objects are the \"common RTMP services\"\nand \"custom RTMP service\" objects in `plugins/rtmp-services\n<https://github.com/obsproject/obs-studio/tree/master/plugins/rtmp-services>`_\n\n\nSettings\n--------\nSettings (see `libobs/obs-data.h`_) are used to get or set settings data\ntypically associated with libobs objects, and can then be saved and\nloaded via Json text.  See the :doc:`reference-settings` for more\ninformation.\n\nThe *obs_data_t* is the equivalent of a Json object, where it's a string\ntable of sub-objects, and the *obs_data_array_t* is similarly used to\nstore an array of *obs_data_t* objects, similar to Json arrays (though\nnot quite identical).\n\nTo create an *obs_data_t* or *obs_data_array_t* object, you'd call the\n:c:func:`obs_data_create()` or :c:func:`obs_data_array_create()`\nfunctions.  *obs_data_t* and *obs_data_array_t* objects are reference\ncounted, so when you are finished with the object, call\n:c:func:`obs_data_release()` or :c:func:`obs_data_array_release()` to\nrelease those references.  Any time an *obs_data_t* or\n*obs_data_array_t* object is returned by a function, their references\nare incremented, so you must release those references each time.\n\nTo set values for an *obs_data_t* object, you'd use one of the following\nfunctions:\n\n.. code:: cpp\n\n  /* Set functions */\n  EXPORT void obs_data_set_string(obs_data_t *data, const char *name, const char *val);\n  EXPORT void obs_data_set_int(obs_data_t *data, const char *name, long long val);\n  EXPORT void obs_data_set_double(obs_data_t *data, const char *name, double val);\n  EXPORT void obs_data_set_bool(obs_data_t *data, const char *name, bool val);\n  EXPORT void obs_data_set_obj(obs_data_t *data, const char *name, obs_data_t *obj);\n  EXPORT void obs_data_set_array(obs_data_t *data, const char *name, obs_data_array_t *array);\n\nSimilarly, to get a value from an *obs_data_t* object, you'd use one of\nthe following functions:\n\n.. code:: cpp\n\n  /* Get functions */\n  EXPORT const char *obs_data_get_string(obs_data_t *data, const char *name);\n  EXPORT long long obs_data_get_int(obs_data_t *data, const char *name);\n  EXPORT double obs_data_get_double(obs_data_t *data, const char *name);\n  EXPORT bool obs_data_get_bool(obs_data_t *data, const char *name);\n  EXPORT obs_data_t *obs_data_get_obj(obs_data_t *data, const char *name);\n  EXPORT obs_data_array_t *obs_data_get_array(obs_data_t *data, const char *name);\n\nUnlike typical Json data objects, the *obs_data_t* object can also set\ndefault values.  This allows the ability to control what is returned if\nthere is no value assigned to a specific string in an *obs_data_t*\nobject when that data is loaded from a Json string or Json file.  Each\nlibobs object also has a *get_defaults* callback which allows setting\nthe default settings for the object on creation.\n\nThese functions control the default values are as follows:\n\n.. code:: cpp\n\n  /* Default value functions. */\n  EXPORT void obs_data_set_default_string(obs_data_t *data, const char *name, const char *val);\n  EXPORT void obs_data_set_default_int(obs_data_t *data, const char *name, long long val);\n  EXPORT void obs_data_set_default_double(obs_data_t *data, const char *name, double val);\n  EXPORT void obs_data_set_default_bool(obs_data_t *data, const char *name, bool val);\n  EXPORT void obs_data_set_default_obj(obs_data_t *data, const char *name, obs_data_t *obj);\n\n\nProperties\n----------\nProperties (see `libobs/obs-properties.h`_) are used to automatically\ngenerate user interface to modify settings for a libobs object (if\ndesired).  Each libobs object has a *get_properties* callback which is\nused to generate properties.  The properties API defines specific\nproperties that are linked to the object's settings, and the front-end\nuses those properties to generate widgets in order to allow the user to\nmodify the settings.  For example, if you had a boolean setting, you\nwould use :c:func:`obs_properties_add_bool()` to allow the user to be\nable to change that setting.  See the :doc:`reference-properties` for\nmore information.\n\nAn example of this:\n\n.. code:: cpp\n\n   static obs_properties_t *my_source_properties(void *data)\n   {\n           obs_properties_t *ppts = obs_properties_create();\n           obs_properties_add_bool(ppts, \"my_bool\",\n                           obs_module_text(\"MyBool\"));\n           UNUSED_PARAMETER(data);\n           return ppts;\n   }\n\n   [...]\n\n   struct obs_source_info my_source {\n           .get_properties = my_source_properties,\n           [...]\n   };\n\nThe *data* parameter is the object's data if the object is present.\nTypically this is unused and probably shouldn't be used if possible.  It\ncan be null if the properties are retrieved without an object associated\nwith it.\n\nProperties can also be modified depending on what settings are shown.\nFor example, you can mark certain properties as disabled or invisible\ndepending on what a particular setting is set to using the\n:c:func:`obs_property_set_modified_callback()` function.\n\nFor example, if you wanted boolean property A to hide text property B:\n\n.. code:: cpp\n\n   static bool setting_a_modified(obs_properties_t *ppts,\n                   obs_property_t *p, obs_data_t *settings)\n   {\n           bool enabled = obs_data_get_bool(settings, \"setting_a\");\n           p = obs_properties_get(ppts, \"setting_b\");\n           obs_property_set_enabled(p, enabled);\n\n           /* return true to update property widgets, false\n              otherwise */\n           return true;\n   }\n\n   [...]\n\n   static obs_properties_t *my_source_properties(void *data)\n   {\n           obs_properties_t *ppts = obs_properties_create();\n           obs_property_t *p;\n\n           p = obs_properties_add_bool(ppts, \"setting_a\",\n                           obs_module_text(\"SettingA\"));\n           obs_property_set_modified_callback(p, setting_a_modified);\n\n           obs_properties_add_text(ppts, \"setting_b\",\n                           obs_module_text(\"SettingB\"),\n                           OBS_TEXT_DEFAULT);\n           return ppts;\n   }\n\n\nLocalization\n------------\nTypically, most plugins bundled with OBS Studio will use a simple\nini-file localization method, where each file is a different language.\nWhen using this method, the :c:func:`OBS_MODULE_USE_DEFAULT_LOCALE()`\nmacro is used which will automatically load/destroy the locale data with\nno extra effort on part of the plugin.  Then the\n:c:func:`obs_module_text()` function (which is automatically declared as\nan extern by `libobs/obs-module.h`_) is used when text lookup is needed.\n\nThere are two exports the module used to load/destroy locale: the\n:c:func:`obs_module_set_locale()` export, and the\n:c:func:`obs_module_free_locale()` export.  The\n:c:func:`obs_module_set_locale()` export is called by libobs to set the\ncurrent language, and then the :c:func:`obs_module_free_locale()` export\nis called by libobs on destruction of the module.  If you wish to\nimplement a custom locale implementation for your plugin, you'd want to\ndefine these exports along with the :c:func:`obs_module_text()` extern\nyourself instead of relying on the\n:c:func:`OBS_MODULE_USE_DEFAULT_LOCALE()` macro.\n\n\n.. ---------------------------------------------------------------------------\n\n.. _libobs/obs-module.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-module.h\n.. _libobs/obs.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs.h\n.. _libobs/obs-source.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-source.h\n.. _libobs/obs-output.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-output.h\n.. _libobs/obs-encoder.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-encoder.h\n.. _libobs/obs-service.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-service.h\n.. _libobs/obs-data.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-data.h\n.. _libobs/obs-properties.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-properties.h\n.. _libobs/graphics/graphics.h: https://github.com/obsproject/obs-studio/blob/master/libobs/graphics/graphics.h\n"
  },
  {
    "path": "docs/sphinx/reference-canvases.rst",
    "content": "Canvas API Reference (obs_canvas_t)\n===================================\n\n.. danger::\n\n   Canvases are still in their early stages of implementation and the API will evolve as they are gradually integrated\n   across OBS Studio.\n\n   The Canvas API should be considered **unstable** and may be changed without warning. This documentation serves to aid\n   in their iterative development within OBS Studio.\n\n   **If you are developing a plugin that utilizes canvases, proceed with great caution.**\n\nCanvases are reference-counted objects that contain scenes and define how those are rendered.\nThey provide a video object which can be used with encoders or raw outputs.\n\nlibobs maintains a main canvas that exists at all times and is used for the default video outputs.\n\n.. type:: obs_canvas_t\n\n   A reference-counted canvas.\n\n.. type:: obs_weak_canvas_t\n\n   A weak reference to a canvas.\n\n.. code:: cpp\n\n   #include <obs.h>\n\n.. _canvas_signal_handler_reference:\n\nCanvas Signals\n--------------\n\nThe following signals are defined for canvases:\n\n**remove** (ptr canvas)\n\n   Called when the :c:func:`obs_canvas_remove()` function is called on the canvas.\n\n**destroy** (ptr canvas)\n\n   Called when a canvas is about to be destroyed.\n\n**video_reset** (ptr canvas)\n\n   Called when the canvas's video mix has been reset after a call to\n   :c:func:`obs_reset_video()` or :c:func:`obs_canvas_reset_video()`.\n\n**source_add** (ptr canvas, ptr source)\n\n   Called when a source has been added to the canvas.\n\n**source_remove** (ptr canvas, ptr source)\n\n   Called when a source has been removed from the canvas.\n\n**rename** (ptr canvas, string new_name, string prev_name)\n\n   Called when the canvas has been renamed.\n\n**channel_change** (ptr canvas, int channel, in out ptr source, ptr prev_source)\n\n   Called when a channel source has been changed.\n\nCanvas Flags\n------------\n\nCanvases can have different behaviors, these can be controlled via the **flags** parameter when creating a canvas.\n\nFlags may be `0` or a bitwise `OR` combination of the following values:\n\n- **MAIN** - Main canvas, cannot be renamed or reset, cannot be set by user\n- **ACTIVATE** - Canvas's sources will become active when they are visible\n- **MIX_AUDIO** - Audio from channels in this canvas will be mixed into the audio output\n- **SCENE_REF** - Canvas will hold references for scene sources\n- **EPHEMERAL** - Indicates this canvas is not supposed to be saved\n\nAdditionally, the following preset combinations of flags are defined:\n\n- **PROGRAM** which equals `ACTIVATE | MIX_AUDIO | SCENE_REF`\n- **PREVIEW** which equals `EPHEMERAL`\n- **DEVICE** which equals `ACTIVATE | EPHEMERAL`\n\nGeneral Canvas Functions\n------------------------\n\n.. function:: obs_canvas_t *obs_get_main_canvas()\n\n   Get a strong reference to the main OBS canvas.\n\n---------------------\n\n.. function:: obs_canvas_t *obs_canvas_create(const char *name, struct obs_video_info *ovi, uint32_t flags)\n\n   Creates a new canvas.\n\n   :param name: Name, will be deduplicated if necessary\n   :param ovi: Video configuration to use for this canvas's video output\n   :param flags: Canvas flags\n   :return: Canvas object\n\n---------------------\n\n.. function:: obs_canvas_t *obs_canvas_create_private(const char *name, struct obs_video_info *ovi, uint32_t flags)\n\n   Creates a new private canvas.\n\n   :param name: Name, will **not** be deduplicated\n   :param ovi: Video configuration to use for this canvas's video output\n   :param flags: Canvas flags\n   :return: Canvas object\n\n---------------------\n\n.. function:: void obs_canvas_remove(obs_canvas_t *canvas)\n\n   Signal that references to canvas should be released and mark the canvas as removed.\n\n---------------------\n\n.. function:: bool obs_canvas_removed(obs_canvas_t *canvas)\n\n   Returns if a canvas is marked as removed (i.e., should no longer be used).\n\n---------------------\n\n.. function:: void obs_canvas_set_name(obs_canvas_t *canvas, const char *name)\n\n   Set canvas name\n\n---------------------\n\n.. function:: const char *obs_canvas_get_name(const obs_canvas_t *canvas)\n\n   Get canvas name\n\n---------------------\n\n.. function:: const char *obs_canvas_get_uuid(const obs_canvas_t *canvas)\n\n   Get canvas UUID\n\n---------------------\n\n.. function:: uint32_t obs_canvas_get_flags(const obs_canvas_t *canvas)\n\n   Gets flags set on a canvas\n\n---------------------\n\nSaving/Loading Functions\n------------------------\n\n.. function:: obs_data_t *obs_save_canvas(obs_canvas_t *source)\n\n   Saves a canvas to settings data\n\n---------------------\n\n.. function:: obs_canvas_t *obs_load_canvas(obs_data_t *data)\n\n   Loads a canvas from settings data\n\n---------------------\n\nReference Counting Functions\n----------------------------\n\n.. function:: obs_canvas_t *obs_canvas_get_ref(obs_canvas_t *canvas)\n\n   Add strong reference to a canvas\n\n---------------------\n\n.. function:: void obs_canvas_release(obs_canvas_t *canvas)\n\n   Release strong reference\n\n---------------------\n\n.. function:: void obs_weak_canvas_addref(obs_weak_canvas_t *weak)\n\n   Add weak reference\n\n---------------------\n\n.. function:: void obs_weak_canvas_release(obs_weak_canvas_t *weak)\n\n   Release weak reference\n\n---------------------\n\n.. function:: obs_weak_canvas_t *obs_canvas_get_weak_canvas(obs_canvas_t *canvas)\n\n   Get weak reference from strong reference\n\n---------------------\n\n.. function:: obs_canvas_t *obs_weak_canvas_get_canvas(obs_weak_canvas_t *weak)\n\n   Get strong reference from weak reference\n\n---------------------\n\nCanvas Channel Functions\n------------------------\n\n.. function:: void obs_canvas_set_channel(obs_canvas_t *canvas, uint32_t channel, obs_source_t *source)\n\n   Sets the source to be used for a canvas channel.\n\n---------------------\n\n.. function:: obs_source_t *obs_canvas_get_channel(obs_canvas_t *canvas, uint32_t channel)\n\n   Gets the source currently in use for a canvas channel.\n\n---------------------\n\nCanvas Source List Functions\n----------------------------\n\n.. function:: obs_scene_t *obs_canvas_scene_create(obs_canvas_t *canvas, const char *name)\n\n   Create scene attached to a canvas.\n\n---------------------\n\n.. function:: void obs_canvas_scene_remove(obs_scene_t *scene)\n\n   Remove a scene from a canvas.\n\n---------------------\n\n.. function:: void obs_canvas_move_scene(obs_scene_t *scene, obs_canvas_t *dst)\n\n   Move scene to another canvas, detaching it from the previous one and deduplicating the name if needed.\n\n---------------------\n\n.. function:: void obs_canvas_enum_scenes(obs_canvas_t *canvas, bool (*enum_proc)(void *, obs_source_t *), void *param)\n\n   Enumerates scenes belonging to a canvas.\n\n   Callback function returns true to continue enumeration, or false to end enumeration.\n\n---------------------\n\n.. function:: obs_source_t *obs_canvas_get_source_by_name(const char *name)\n\n   Gets a canvas source by its name.\n  \n   Increments the source reference counter, use\n   :c:func:`obs_source_release()` to release it when complete.\n\n---------------------\n\n.. function:: obs_scene_t *obs_canvas_get_scene_by_name(const char *name)\n\n   Gets a canvas scene by its name.\n  \n   Increments the source reference counter, use\n   :c:func:`obs_scene_release()` to release it when complete.\n\n---------------------\n\nCanvas Video Functions\n----------------------\n\n.. function:: bool obs_canvas_reset_video(obs_canvas_t *canvas, struct obs_video_info *ovi)\n\n   Reset a canvas's video configuration.\n   \n   Note that the frame rate property is ignored and the global rendering frame rate is used instead.\n\n---------------------\n\n.. function:: bool obs_canvas_has_video(obs_canvas_t *canvas)\n\n   Returns true if the canvas video is configured.\n\n---------------------\n\n.. function:: video_t *obs_canvas_get_video(const obs_canvas_t *canvas)\n\n   Get canvas video output\n\n---------------------\n\n.. function:: bool obs_canvas_get_video_info(const obs_canvas_t *canvas, struct obs_video_info *ovi)\n\n   Get canvas video info (if any)\n\n---------------------\n\n.. function:: void obs_canvas_render(obs_canvas_t *canvas)\n\n   Render the canvas's view. Must be called on the graphics thread.\n\n---------------------\n"
  },
  {
    "path": "docs/sphinx/reference-core-objects.rst",
    "content": "Core API Object Reference\n=========================\n\n.. toctree::\n   :maxdepth: 3\n\n   Sources (obs_source_t) <reference-sources>\n   Scenes (obs_scene_t) <reference-scenes>\n   Outputs (obs_output_t) <reference-outputs>\n   Encoders (obs_encoder_t) <reference-encoders>\n   Services (obs_service_t) <reference-services>\n   Data Settings (obs_data_t) <reference-settings>\n   Properties (obs_properties_t) <reference-properties>\n"
  },
  {
    "path": "docs/sphinx/reference-core.rst",
    "content": "OBS Core API Reference\n======================\n\n.. code:: cpp\n\n   #include <obs.h>\n\n\n.. _obs_init_shutdown_reference:\n\nInitialization, Shutdown, and Information\n-----------------------------------------\n\n.. function:: bool obs_startup(const char *locale, const char *module_config_path, profiler_name_store_t *store)\n\n   Initializes the OBS core context.\n  \n   :param  locale:             The locale to use for modules\n                               (E.G. \"en-US\")\n   :param  module_config_path: Path to module config storage directory\n                               (or *NULL* if none)\n   :param  store:              The profiler name store for OBS to use or NULL\n   :return:                    *false* if already initialized or failed\n                               to initialize\n\n---------------------\n\n.. function:: void obs_shutdown(void)\n\n   Releases all data associated with OBS and terminates the OBS context.\n\n---------------------\n\n.. function:: bool obs_initialized(void)\n\n   :return: true if the main OBS context has been initialized\n\n---------------------\n\n.. function:: uint32_t obs_get_version(void)\n\n   :return: The current core version\n\n---------------------\n\n.. function:: const char *obs_get_version_string(void)\n\n   :return: The current core version string\n\n---------------------\n\n.. function:: void obs_set_locale(const char *locale)\n\n   Sets a new locale to use for modules.  This will call\n   :c:func:`obs_module_set_locale()` for each module with the new locale.\n  \n   :param  locale: The locale to use for modules\n\n---------------------\n\n.. function:: const char *obs_get_locale(void)\n\n   :return: The current locale\n\n---------------------\n\n.. function:: profiler_name_store_t *obs_get_profiler_name_store(void)\n\n   :return: The profiler name store (see util/profiler.h) used by OBS,\n            which is either a name store passed to :c:func:`obs_startup()`, an\n            internal name store, or NULL in case :c:func:`obs_initialized()`\n            returns false.\n\n---------------------\n\n.. function:: int obs_reset_video(struct obs_video_info *ovi)\n\n   Sets base video output base resolution/fps/format.\n  \n   Note: This data cannot be changed if an output is currently active.\n\n   Note: The graphics module cannot be changed without fully destroying\n   the OBS context.\n\n   :param   ovi: Pointer to an obs_video_info structure containing the\n                 specification of the graphics subsystem,\n   :return:      | OBS_VIDEO_SUCCESS          - Success\n                 | OBS_VIDEO_NOT_SUPPORTED    - The adapter lacks capabilities\n                 | OBS_VIDEO_INVALID_PARAM    - A parameter is invalid\n                 | OBS_VIDEO_CURRENTLY_ACTIVE - Video is currently active\n                 | OBS_VIDEO_MODULE_NOT_FOUND - The graphics module is not found\n                 | OBS_VIDEO_FAIL             - Generic failure\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   struct obs_video_info {\n           /**\n            * Graphics module to use (usually \"libobs-opengl\" or \"libobs-d3d11\")\n            */\n           const char          *graphics_module;\n   \n           uint32_t            fps_num;       /**< Output FPS numerator */\n           uint32_t            fps_den;       /**< Output FPS denominator */\n   \n           uint32_t            base_width;    /**< Base compositing width */\n           uint32_t            base_height;   /**< Base compositing height */\n   \n           uint32_t            output_width;  /**< Output width */\n           uint32_t            output_height; /**< Output height */\n           enum video_format   output_format; /**< Output format */\n   \n           /** Video adapter index to use (NOTE: avoid for optimus laptops) */\n           uint32_t            adapter;\n   \n           /** Use shaders to convert to different color formats */\n           bool                gpu_conversion;\n   \n           enum video_colorspace colorspace;  /**< YUV type (if YUV) */\n           enum video_range_type range;       /**< YUV range (if YUV) */\n   \n           enum obs_scale_type scale_type;    /**< How to scale if scaling */\n   };\n\n---------------------\n\n.. function:: bool obs_reset_audio(const struct obs_audio_info *oai)\n\n   Sets base audio output format/channels/samples/etc.\n\n   Note: Cannot reset base audio if an output is currently active.\n\n   :return: *true* if successful, *false* otherwise\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   struct obs_audio_info {\n           uint32_t            samples_per_sec;\n           enum speaker_layout speakers;\n   };\n\n---------------------\n\n.. function:: bool obs_reset_audio2(const struct obs_audio_info2 *oai)\n\n   Sets base audio output format/channels/samples/etc. Also allows the\n   ability to set the maximum audio latency of OBS, and set whether the\n   audio buffering is fixed or dynamically increasing.\n\n   When using fixed audio buffering, OBS will automatically buffer to\n   the maximum audio latency on startup.\n\n   Maximum audio latency will clamp to the closest multiple of the audio\n   output frames (which is typically 1024 audio frames).\n\n   Note: Cannot reset base audio if an output is currently active.\n\n   :return: *true* if successful, *false* otherwise\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   struct obs_audio_info2 {\n           uint32_t            samples_per_sec;\n           enum speaker_layout speakers;\n\n           uint32_t max_buffering_ms;\n           bool fixed_buffering;\n   };\n\n---------------------\n\n.. function:: bool obs_get_video_info(struct obs_video_info *ovi)\n\n   Gets the current video settings.\n\n   :return: *false* if no video\n\n---------------------\n\n.. function:: float obs_get_video_sdr_white_level(void)\n\n   Gets the current SDR white level.\n\n   :return: SDR white level, 300.f if no video\n\n---------------------\n\n.. function:: float obs_get_video_hdr_nominal_peak_level(void)\n\n   Gets the current HDR nominal peak level.\n\n   :return: HDR nominal peak level, 1000.f if no video\n\n---------------------\n\n.. function:: void obs_set_video_levels(float sdr_white_level, float hdr_nominal_peak_level)\n\n   Sets the current video levels.\n\n---------------------\n\n.. function:: bool obs_get_audio_info(struct obs_audio_info *oai)\n\n   Gets the current audio settings.\n   \n   :return: *false* if no audio\n\n---------------------\n\n\nLibobs Objects\n--------------\n\n.. function:: bool obs_enum_source_types(size_t idx, const char **id)\n\n   Enumerates all source types (inputs, filters, transitions, etc).\n\n---------------------\n\n.. function:: bool obs_enum_input_types(size_t idx, const char **id)\n\n   Enumerates all available inputs source types.\n  \n   Inputs are general source inputs (such as capture sources, device sources,\n   etc).\n\n---------------------\n\n.. function:: bool obs_enum_filter_types(size_t idx, const char **id)\n\n   Enumerates all available filter source types.\n  \n   Filters are sources that are used to modify the video/audio output of\n   other sources.\n\n---------------------\n\n.. function:: bool obs_enum_transition_types(size_t idx, const char **id)\n\n   Enumerates all available transition source types.\n  \n   Transitions are sources used to transition between two or more other\n   sources.\n\n---------------------\n\n.. function:: bool obs_enum_output_types(size_t idx, const char **id)\n\n   Enumerates all available output types.\n\n---------------------\n\n.. function:: bool obs_enum_encoder_types(size_t idx, const char **id)\n\n   Enumerates all available encoder types.\n\n---------------------\n\n.. function:: bool obs_enum_service_types(size_t idx, const char **id)\n\n   Enumerates all available service types.\n\n---------------------\n\n.. function:: void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param)\n\n   Enumerates all input sources.\n  \n   Callback function returns true to continue enumeration, or false to end\n   enumeration.\n  \n   Use :c:func:`obs_source_get_ref()` or\n   :c:func:`obs_source_get_weak_source()` if you want to retain a\n   reference after obs_enum_sources finishes.\n\n   For scripting, use :py:func:`obs_enum_sources`.\n\n---------------------\n\n.. function:: void obs_enum_scenes(bool (*enum_proc)(void*, obs_source_t*), void *param)\n\n   Enumerates all scenes. Use :c:func:`obs_scene_from_source()` if the scene is\n   needed as an :c:type:`obs_scene_t`. The order that they are enumerated should\n   not be relied on. If one intends to enumerate the scenes in the order\n   presented by the OBS Studio Frontend, use :c:func:`obs_frontend_get_scenes()`.\n  \n   Callback function returns true to continue enumeration, or false to end\n   enumeration.\n  \n   Use :c:func:`obs_source_get_ref()` or\n   :c:func:`obs_source_get_weak_source()` if you want to retain a\n   reference after obs_enum_scenes finishes.\n \n---------------------\n\n.. function:: void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t*), void *param)\n\n   Enumerates outputs.\n\n   Callback function returns true to continue enumeration, or false to end\n   enumeration.\n\n   Use :c:func:`obs_output_get_ref()` or\n   :c:func:`obs_output_get_weak_output()` if you want to retain a\n   reference after obs_enum_outputs finishes.\n\n---------------------\n\n.. function:: void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t*), void *param)\n\n   Enumerates encoders.\n\n   Callback function returns true to continue enumeration, or false to end\n   enumeration.\n\n   Use :c:func:`obs_encoder_get_ref()` or\n   :c:func:`obs_encoder_get_weak_encoder()` if you want to retain a\n   reference after obs_enum_encoders finishes.\n\n---------------------\n\n.. function:: void obs_enum_canvases(bool (*enum_proc)(void*, obs_canvas_t*), void *param)\n\n   Enumerates canvases.\n\n   Callback function returns true to continue enumeration, or false to end\n   enumeration.\n\n   Use :c:func:`obs_canvas_get_ref()` or\n   :c:func:`obs_canvas_get_weak_canvas()` if you want to retain a\n   reference after obs_enum_canvases finishes.\n\n---------------------\n\n.. function:: obs_source_t *obs_get_source_by_name(const char *name)\n\n   Gets a source by its name.\n  \n   Increments the source reference counter, use\n   :c:func:`obs_source_release()` to release it when complete.\n\n---------------------\n\n.. function:: obs_source_t *obs_get_source_by_uuid(const char *uuid)\n\n   Gets a source by its UUID.\n  \n   Increments the source reference counter, use\n   :c:func:`obs_source_release()` to release it when complete.\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: obs_source_t *obs_get_transition_by_name(const char *name)\n\n   Gets a transition by its name.\n  \n   Increments the source reference counter, use\n   :c:func:`obs_source_release()` to release it when complete.\n\n   .. deprecated:: 31.1\n      Use :c:func:`obs_frontend_get_transitions` from the Frontend API or :c:func:`obs_get_source_by_uuid` instead.\n\n---------------------\n\n.. function:: obs_source_t *obs_get_transition_by_uuid(const char *uuid)\n\n   Gets a transition by its UUID.\n\n   Increments the source reference counter, use\n   :c:func:`obs_source_release()` to release it when complete.\n\n   .. versionadded:: 29.1\n\n   .. deprecated:: 31.1\n      Use :c:func:`obs_get_source_by_uuid` instead.\n\n---------------------\n\n.. function:: obs_scene_t *obs_get_scene_by_name(const char *name)\n\n   Gets a scene by its name.\n  \n   Increments the scene reference counter, use\n   :c:func:`obs_scene_release()` to release it when complete.\n\n---------------------\n\n.. function:: obs_output_t *obs_get_output_by_name(const char *name)\n\n   Gets an output by its name.\n\n   Increments the output reference counter, use\n   :c:func:`obs_output_release()` to release it when complete.\n\n---------------------\n\n.. function:: obs_encoder_t *obs_get_encoder_by_name(const char *name)\n\n   Gets an encoder by its name.\n\n   Increments the encoder reference counter, use\n   :c:func:`obs_encoder_release()` to release it when complete.\n\n---------------------\n\n.. function:: obs_service_t *obs_get_service_by_name(const char *name)\n\n   Gets an service by its name.\n\n   Increments the service reference counter, use\n   :c:func:`obs_service_release()` to release it when complete.\n\n---------------------\n\n.. function:: obs_canvas_t *obs_get_canvas_by_name(const char *name)\n\n   Get a canvas by its name.\n   \n   Increments the canvas reference counter, use\n   :c:func:`obs_canvas_release()` to release it when complete.\n\n---------------------\n\n.. function:: obs_canvas_t *obs_get_canvas_by_uuid(const char *uuid)\n\n   Get a canvas by its UUID.\n\n   Increments the canvas reference counter, use\n   :c:func:`obs_canvas_release()` to release it when complete.\n\n---------------------\n\n.. function:: obs_data_t *obs_save_source(obs_source_t *source)\n\n   :return: A new reference to a source's saved data. Use\n            :c:func:`obs_data_release()` to release it when complete.\n\n---------------------\n\n.. function:: obs_source_t *obs_load_source(obs_data_t *data)\n\n   :return: A source created from saved data\n\n---------------------\n\n.. function:: void obs_load_sources(obs_data_array_t *array, obs_load_source_cb cb, void *private_data)\n\n   Helper function to load active sources from a data array.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   typedef void (*obs_load_source_cb)(void *private_data, obs_source_t *source);\n\n---------------------\n\n.. function:: obs_data_array_t *obs_save_sources(void)\n\n   :return: A data array with the saved data of all active sources\n\n---------------------\n\n.. function:: obs_data_array_t *obs_save_sources_filtered(obs_save_source_filter_cb cb, void *data)\n\n   :return: A data array with the saved data of all active sources,\n            filtered by the *cb* function\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   typedef bool (*obs_save_source_filter_cb)(void *data, obs_source_t *source);\n\n---------------------\n\n\nVideo, Audio, and Graphics\n--------------------------\n\n.. function:: void obs_enter_graphics(void)\n\n   Helper function for entering the OBS graphics context.\n\n---------------------\n\n.. function:: void obs_leave_graphics(void)\n\n   Helper function for leaving the OBS graphics context.\n\n---------------------\n\n.. function:: audio_t *obs_get_audio(void)\n\n   :return: The main audio output handler for this OBS context\n\n---------------------\n\n.. function:: video_t *obs_get_video(void)\n\n   :return: The main video output handler for this OBS context\n\n---------------------\n\n.. function:: void obs_set_output_source(uint32_t channel, obs_source_t *source)\n\n   Sets the primary output source for a channel.\n\n---------------------\n\n.. function:: obs_source_t *obs_get_output_source(uint32_t channel)\n\n   Gets the primary output source for a channel and increments the reference\n   counter for that source.  Use :c:func:`obs_source_release()` to release.\n\n---------------------\n\n.. function:: gs_effect_t *obs_get_base_effect(enum obs_base_effect effect)\n\n   Returns a commonly used base effect.\n\n   :param effect: | Can be one of the following values:\n                  | OBS_EFFECT_DEFAULT             - RGB/YUV\n                  | OBS_EFFECT_DEFAULT_RECT        - RGB/YUV (using texture_rect)\n                  | OBS_EFFECT_OPAQUE              - RGB/YUV (alpha set to 1.0)\n                  | OBS_EFFECT_SOLID               - RGB/YUV (solid color only)\n                  | OBS_EFFECT_BICUBIC             - Bicubic downscale\n                  | OBS_EFFECT_LANCZOS             - Lanczos downscale\n                  | OBS_EFFECT_BILINEAR_LOWRES     - Bilinear low resolution downscale\n                  | OBS_EFFECT_PREMULTIPLIED_ALPHA - Premultiplied alpha\n\n---------------------\n\n.. function:: void obs_render_main_texture(void)\n\n   Renders the main output texture.  Useful for rendering a preview pane\n   of the main output.\n\n---------------------\n\n.. function:: bool obs_audio_monitoring_available(void)\n\n   :return: Whether audio monitoring is supported and available on the current platform\n\n---------------------\n\n.. function:: void obs_reset_audio_monitoring(void)\n\n   Resets all audio monitoring devices.\n\n   .. versionadded:: 30.1\n\n---------------------\n\n.. function:: void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)\n\n   Enumerates audio devices which can be used for audio monitoring.\n\n   Callback function returns true to continue enumeration, or false to end\n   enumeration.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   typedef bool (*obs_enum_audio_device_cb)(void *data, const char *name, const char *id);\n\n---------------------\n\n.. function:: bool obs_set_audio_monitoring_device(const char *name, const char *id)\n\n   Sets the current audio device for audio monitoring.\n\n---------------------\n\n.. function:: void obs_get_audio_monitoring_device(const char **name, const char **id)\n\n   Gets the current audio device for audio monitoring.\n\n---------------------\n\n.. function:: void obs_add_main_render_callback(void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param)\n              void obs_remove_main_render_callback(void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param)\n\n   Adds/removes a main rendering callback.  Allows custom rendering to\n   the main stream/recording output.\n\n   For scripting (**Lua only**), use :py:func:`obs_add_main_render_callback`\n   and :py:func:`obs_remove_main_render_callback`.\n\n---------------------\n\n.. function:: void obs_add_main_rendered_callback(void (*rendered)(void *param), void *param)\n              void obs_remove_main_rendered_callback(void (*rendered)(void *param), void *param)\n\n   Adds/removes a main rendered callback.  Allows using the result of\n   the main stream/recording output.\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: void obs_add_raw_video_callback(const struct video_scale_info *conversion, void (*callback)(void *param, struct video_data *frame), void *param)\n              void obs_remove_raw_video_callback(void (*callback)(void *param, struct video_data *frame), void *param)\n\n   Adds/removes a raw video callback.  Allows the ability to obtain raw\n   video frames without necessarily using an output.\n\n   :param conversion: Specifies conversion requirements.  Can be NULL.\n   :param callback:   The callback that receives raw video frames.\n   :param param:      The private data associated with the callback.\n\n---------------------\n\n.. function:: void obs_add_raw_audio_callback(size_t mix_idx, const struct audio_convert_info *conversion, audio_output_callback_t callback, void *param)\n              void obs_remove_raw_raw_callback(size_t track, audio_output_callback_t callback, void *param)\n\n   Adds/removes a raw audio callback.  Allows the ability to obtain raw\n   audio data without necessarily using an output.\n\n   :param mix_idx:    Specifies audio track to get data from.\n   :param conversion: Specifies conversion requirements.  Can be NULL.\n   :param callback:   The callback that receives raw audio data.\n   :param param:      The private data associated with the callback.\n\nPrimary signal/procedure handlers\n---------------------------------\n\n.. function:: signal_handler_t *obs_get_signal_handler(void)\n\n   :return: The primary obs signal handler. Should not be manually freed,\n            as its lifecycle is managed by libobs.\n\n   See :ref:`core_signal_handler_reference` for more information on\n   core signals.\n\n---------------------\n\n.. function:: proc_handler_t *obs_get_proc_handler(void)\n\n   :return: The primary obs procedure handler. Should not be manually freed,\n            as its lifecycle is managed by libobs.\n\n\n.. _core_signal_handler_reference:\n\nCore OBS Signals\n----------------\n\n**source_create** (ptr source)\n\n   Called when a source has been created.\n\n**source_destroy** (ptr source)\n\n   Called when a source has been destroyed.\n\n**source_remove** (ptr source)\n\n   Called when a source has been removed (:c:func:`obs_source_remove()`\n   has been called on the source).\n\n**source_update** (ptr source)\n\n   Called when a source's settings have been updated.\n\n**source_save** (ptr source)\n\n   Called when a source is being saved.\n\n**source_load** (ptr source)\n\n   Called when a source is being loaded.\n\n**source_activate** (ptr source)\n\n   Called when a source has been activated in the main view (visible on\n   stream/recording).\n\n**source_deactivate** (ptr source)\n\n   Called when a source has been deactivated from the main view (no\n   longer visible on stream/recording).\n\n**source_show** (ptr source)\n\n   Called when a source is visible on any display and/or on the main\n   view.\n\n**source_hide** (ptr source)\n\n   Called when a source is no longer visible on any display and/or on\n   the main view.\n\n**source_rename** (ptr source, string new_name, string prev_name)\n\n   Called when a source has been renamed.\n\n**source_volume** (ptr source, in out float volume)\n\n   Called when a source's volume has changed.\n\n**source_audio_activate** (ptr source)\n\n   Called when a source's audio becomes active.\n\n**source_audio_deactivate** (ptr source)\n\n   Called when a source's audio becomes inactive.\n\n**source_filter_add** (ptr source, ptr filter)\n\n   Called when a filter is added to a source.\n\n**source_filter_remove** (ptr source, ptr filter)\n\n   Called when a filter is removed from a source.\n\n**source_transition_start** (ptr source)\n\n   Called when a transition has started its transition.\n\n**source_transition_video_stop** (ptr source)\n\n   Called when a transition has stopped its video transitioning.\n\n**source_transition_stop** (ptr source)\n\n   Called when a transition has stopped its transition.\n\n**channel_change** (int channel, in out ptr source, ptr prev_source)\n\n   Called when :c:func:`obs_set_output_source()` has been called.\n\n**hotkey_layout_change** ()\n\n   Called when the hotkey layout has changed.\n\n**hotkey_register** (ptr hotkey)\n\n   Called when a hotkey has been registered.\n\n**hotkey_unregister** (ptr hotkey)\n\n   Called when a hotkey has been unregistered.\n\n**hotkey_bindings_changed** (ptr hotkey)\n\n   Called when a hotkey's bindings has changed.\n\n**canvas_create** (ptr canvas)\n\n   Called when a new public canvas has been created.\n\n**canvas_remove** (ptr canvas)\n\n   Called when the :c:func:`obs_canvas_remove()` function is called on a public canvas.\n\n**canvas_destroy** (ptr canvas)\n\n   Called when a public canvas is about to be destroyed.\n\n**canvas_video_reset** (ptr canvas)\n\n   Called when a public canvas's video mix has been reset after a call to\n   :c:func:`obs_reset_video()` or :c:func:`obs_canvas_reset_video()`.\n\n**canvas_rename** (ptr canvas, string new_name, string prev_name)\n\n   Called when a public canvas has been renamed.\n\n**video_reset** ()\n\n   Called when a the main OBS video has been reset.\n\n---------------------\n\n\n.. _display_reference:\n\nDisplays\n--------\n\n.. function:: obs_display_t *obs_display_create(const struct gs_init_data *graphics_data)\n\n   Adds a new window display linked to the main render pipeline.  This creates\n   a new swap chain which updates every frame.\n  \n   *(Important note: do not use more than one display widget within the\n   hierarchy of the same base window; this will cause presentation\n   stalls on macOS.)*\n\n   :param  graphics_data: The swap chain initialization data\n   :return:               The new display context, or NULL if failed\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   enum gs_color_format {\n           [...]\n           GS_RGBA,\n           GS_BGRX,\n           GS_BGRA,\n           GS_RGBA16F,\n           GS_RGBA32F,\n           [...]\n   };\n   \n   enum gs_zstencil_format {\n           GS_ZS_NONE,\n           GS_Z16,\n           GS_Z24_S8,\n           GS_Z32F,\n           GS_Z32F_S8X24\n   };\n   \n   struct gs_window {\n   #if defined(_WIN32)\n           void                    *hwnd;\n   #elif defined(__APPLE__)\n           __unsafe_unretained id  view;\n   #elif defined(__linux__) || defined(__FreeBSD__)\n           uint32_t                id;\n           void                    *display;\n   #endif\n   };\n   \n   struct gs_init_data {\n           struct gs_window        window;\n           uint32_t                cx, cy;\n           uint32_t                num_backbuffers;\n           enum gs_color_format    format;\n           enum gs_zstencil_format zsformat;\n           uint32_t                adapter;\n   };\n\n---------------------\n\n.. function:: void obs_display_destroy(obs_display_t *display)\n\n   Destroys a display context.\n\n---------------------\n\n.. function:: void obs_display_resize(obs_display_t *display, uint32_t cx, uint32_t cy)\n\n   Changes the size of a display context.\n\n---------------------\n\n.. function:: void obs_display_add_draw_callback(obs_display_t *display, void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param)\n\n   Adds a draw callback for a display context, which will be called\n   whenever the display is rendered.\n  \n   :param  display: The display context\n   :param  draw:    The draw callback which is called each time a frame\n                    updates\n   :param  param:   The user data to be associated with this draw callback\n\n---------------------\n\n.. function:: void obs_display_remove_draw_callback(obs_display_t *display, void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param)\n\n   Removes a draw callback for a display context.\n\n---------------------\n\n.. function:: void obs_display_set_enabled(obs_display_t *display, bool enable)\n\n   Enables/disables a display context.\n\n---------------------\n\n.. function:: bool obs_display_enabled(obs_display_t *display)\n\n   :return: *true* if the display is enabled, *false* otherwise\n\n---------------------\n\n.. function:: void obs_display_set_background_color(obs_display_t *display, uint32_t color)\n\n   Sets the background (clear) color for the display context.\n\n.. _view_reference:\n\nViews\n----------------\n\n.. function:: obs_view_t *obs_view_create(void)\n\n   :return: A view context\n\n---------------------\n\n.. function:: void obs_view_destroy(obs_view_t *view)\n\n   Destroys a view context.\n\n---------------------\n\n.. function:: void obs_view_render(obs_view_t *view)\n\n   Renders the sources of this view context.\n\n---------------------\n\n.. function:: video_t *obs_view_add(obs_view_t *view)\n\n   Renders the sources of this view context.\n\n   :return: The main video output handler for the view context\n\n---------------------\n\n.. function:: video_t *obs_view_add2(obs_view_t *view, struct obs_video_info *ovi)\n\n   Adds a view to the main render loop, with custom video settings.\n\n   :return: The main video output handler for the view context\n\n---------------------\n\n.. function:: void obs_view_remove(obs_view_t *view)\n\n   Removes a view from the main render loop.\n\n---------------------\n\n.. function:: void obs_view_set_source(obs_view_t *view, uint32_t channel, obs_source_t *source)\n\n   Sets the source to be used for this view context.\n\n---------------------\n\n.. function:: obs_source_t *obs_view_get_source(obs_view_t *view, uint32_t channel)\n\n   :return: The source currently in use for this view context\n\n---------------------\n\n.. function:: void obs_view_enum_video_info(obs_view_t *view, bool (*enum_proc)(void *, struct obs_video_info *), void *param)\n\n   Enumerates all the video info of all mixes that use the specified mix.\n\n   .. versionadded:: 30.1\n"
  },
  {
    "path": "docs/sphinx/reference-encoders.rst",
    "content": "Encoder API Reference (obs_encoder_t)\n=====================================\n\nEncoders are OBS-specific implementations of video/audio encoders, which\nare used with outputs that use encoders.  x264, NVENC, Quicksync are\nexamples of encoder implementations.  The `libobs/obs-encoder.h`_ file\nis the dedicated header for implementing encoders\n\n.. type:: obs_encoder_t\n\n   A reference-counted encoder object.\n\n.. type:: obs_weak_encoder_t\n\n   A weak reference to an encoder object.\n\n.. code:: cpp\n\n   #include <obs.h>\n\n\nEncoder Definition Structure (obs_encoder_info)\n-----------------------------------------------\n\n.. struct:: obs_encoder_info\n\n   Encoder definition structure.\n\n.. member:: const char *obs_encoder_info.id\n\n   Unique string identifier for the encoder (required).\n\n.. member:: enum obs_encoder_type obs_encoder_info.type\n\n   Type of encoder.\n\n   - **OBS_ENCODER_VIDEO** - Video encoder\n   - **OBS_ENCODER_AUDIO** - Audio encoder\n\n.. member:: const char *obs_encoder_info.codec\n\n   The codec, in string form.  For example, \"h264\" for an H.264 encoder.\n\n.. member:: const char *(*obs_encoder_info.get_name)(void *type_data)\n\n   Get the translated name of the encoder type.\n\n   :param  type_data:  The type_data variable of this structure\n   :return:            The translated name of the encoder type\n\n.. member:: void *(*obs_encoder_info.create)(obs_data_t *settings, obs_encoder_t *encoder)\n\n   Creates the implementation data for the encoder.\n\n   :param  settings: Settings to initialize the encoder with\n   :param  encoder:  Encoder that this data is associated with\n   :return:          The implementation data associated with this encoder\n\n.. member:: void (*obs_encoder_info.destroy)(void *data)\n\n   Destroys the implementation data for the encoder.\n\n.. member:: bool (*encode)(void *data, struct encoder_frame *frame, struct encoder_packet *packet, bool *received_packet)\n\n   Called to encode video or audio and outputs packets as they become\n   available.\n\n   :param frame:           Raw audio/video data to encode\n   :param packet:          Encoder packet output, if any\n   :param received_packet: Set to *true* if a packet was received,\n                           *false* otherwise\n   :return:                true if successful, false on critical failure\n\n.. member:: size_t (*get_frame_size)(void *data)\n\n   :return: An audio encoder's frame size.  For example, for AAC this\n            number would be 1024\n\n.. member:: void (*obs_encoder_info.get_defaults)(obs_data_t *settings)\n            void (*obs_encoder_info.get_defaults2)(obs_data_t *settings, void *type_data)\n\n   Sets the default settings for this encoder.\n\n   :param  settings:  Default settings.  Call obs_data_set_default*\n                      functions on this object to set default setting\n                      values\n\n.. member:: obs_properties_t *(*obs_encoder_info.get_properties)(void *data)\n            obs_properties_t *(*obs_encoder_info.get_properties2)(void *data, void *type_data)\n\n   Gets the property information of this encoder.\n\n   :param  data:  The implementation data associated with this encoder.\n                  This value can be null (e.g., when\n                  :c:func:`obs_get_encoder_properties()` is called on the\n                  encoder type), make sure to handle this gracefully.\n\n   (Optional)\n\n   :return: The properties of the encoder\n\n.. member:: void (*obs_encoder_info.update)(void *data, obs_data_t *settings)\n\n   Updates the settings for this encoder.\n\n   (Optional)\n\n   :param settings: New settings for this encoder\n\n.. member:: bool (*obs_encoder_info.get_extra_data)(void *data, uint8_t **extra_data, size_t *size)\n\n   Returns extra data associated with this encoder (usually header).\n\n   (Optional)\n\n   :param  extra_data: Pointer to receive the extra data\n   :param  size:       Pointer to receive the size of the extra\n                       data\n   :return:            true if extra data available, false\n                       otherwise\n\n.. member:: bool (*obs_encoder_info.get_sei_data)(void *data, uint8_t **sei_data, size_t *size)\n\n   Gets the SEI data of a video encoder that has SEI data.\n\n   (Optional)\n\n   :param  sei_data: Pointer to receive the SEI data\n   :param  size:     Pointer to receive the SEI data size\n   :return:          true if SEI data available, false otherwise\n\n.. member:: void (*obs_encoder_info.get_audio_info)(void *data, struct audio_convert_info *info)\n\n   Returns desired audio format and sample information.  This callback\n   can be used to tell the back-end that the audio data needs to be\n   automatically converted to a different sample rate or audio format\n   before being sent to the encoder.\n\n   (Optional)\n\n   :param  info: Audio format information\n\n.. member:: void (*obs_encoder_info.get_video_info)(void *data, struct video_scale_info *info)\n\n   Returns desired video format information.  This callback can be used\n   to tell the back-end that the video data needs to be automatically\n   converted to a different video format or specific size before being\n   sent to the encoder.\n\n   :param  info: Video format information\n\n.. member:: void *obs_encoder_info.type_data\n            void (*obs_encoder_info.free_type_data)(void *type_data)\n\n   Private data associated with this entry.  Note that this is not the\n   same as the implementation data; this is used to differentiate\n   between two different types if the same callbacks are used for more\n   than one different type.\n\n.. member:: uint32_t obs_encoder_info.caps\n\n   Can be 0 or a bitwise OR combination of one or more of the following\n   values:\n\n   - **OBS_ENCODER_CAP_DEPRECATED** - Encoder is deprecated\n   - **OBS_ENCODER_CAP_ROI** - Encoder supports region of interest feature\n   - **OBS_ENCODER_CAP_SCALING** - Encoder implements its own scaling logic,\n                                   desiring to receive unscaled frames\n\n.. member:: size_t (*get_priming_samples)(void *data)\n\n   Returns the number of priming samples that must be skipped for correct playback for audio produced by this encoder.\n   Only required for lossy codecs such as AAC or Opus.\n\n   :return: Number of samples\n\nEncoder Packet Structure (encoder_packet)\n-----------------------------------------\n\n.. struct:: encoder_packet\n\n   Encoder packet structure.\n\n.. member:: uint8_t               *encoder_packet.data\n\n   Packet data.\n\n.. member:: size_t                encoder_packet.size\n\n   Packet size.\n\n.. member:: int64_t               encoder_packet.pts\n            int64_t               encoder_packet.dts\n\n   Packet presentation and decode timestamps.\n\n.. member:: int32_t               encoder_packet.timebase_num\n            int32_t               encoder_packet.timebase_den\n\n   Packet time base.\n\n.. member:: enum obs_encoder_type encoder_packet.type\n\n   Can be one of the following values:\n\n   - **OBS_ENCODER_VIDEO** - Video data\n   - **OBS_ENCODER_AUDIO** - Audio data\n\n.. member:: bool                  encoder_packet.keyframe\n\n   Packet is a keyframe.\n\n.. member:: int64_t               encoder_packet.dts_usec\n\n   The DTS in microseconds.\n\n   (This should not be set by the encoder implementation)\n\n.. member:: int64_t               encoder_packet.sys_dts_usec\n\n   The system time of this packet in microseconds.\n\n   (This should not be set by the encoder implementation)\n\n.. member:: int                   encoder_packet.priority\n\n   Packet priority.  This is no longer used.\n\n   (This should not be set by the encoder implementation)\n\n.. member:: int                   encoder_packet.drop_priority\n\n   Packet drop priority.\n\n   If this packet needs to be dropped, the next packet must be of this\n   priority or higher to continue transmission.\n\n   (This should not be set by the encoder implementation)\n\n.. member:: size_t                encoder_packet.track_idx\n\n   Audio track index.\n\n   (This should not be set by the encoder implementation)\n\n.. member:: obs_encoder_t         *encoder_packet.encoder\n\n   Encoder object associated with this packet.\n\n   (This should not be set by the encoder implementation)\n\n\nRaw Frame Data Structure (encoder_frame)\n----------------------------------------\n\n.. struct:: encoder_frame\n\n   Raw frame data structure.\n\n.. member:: uint8_t *encoder_frame.data[MAX_AV_PLANES]\n\n   Raw video/audio data.\n\n.. member:: uint32_t encoder_frame.linesize[MAX_AV_PLANES]\n\n   Line size of each plane.\n\n.. member:: uint32_t encoder_frame.frames\n\n   Number of audio frames (if audio).\n\n.. member:: int64_t encoder_frame.pts\n\n   Presentation timestamp.\n\n\nEncoder Region of Interest Structure (obs_encoder_roi)\n------------------------------------------------------\n\n.. struct:: obs_encoder_roi\n\n   Encoder region of interest structure.\n\n   .. versionadded:: 30.1\n\n.. member:: uint32_t top\n            uint32_t bottom\n            uint32_t left\n            uint32_t right\n\n   The rectangle edges of the region are specified as number of pixels from the input video's top and left edges (i.e. row/column 0).\n\n.. member:: float priority\n\n   Priority is specified as a float value between *-1.0f* and *1*.\n   These are converted to encoder-specific values by the encoder.\n   Values above 0 tell the encoder to increase quality for that region, values below tell it to worsen it.\n   Not all encoders support negative values and they may be ignored.\n\nGeneral Encoder Functions\n-------------------------\n\n.. function:: void obs_register_encoder(struct obs_encoder_info *info)\n\n   Registers an encoder type.  Typically used in\n   :c:func:`obs_module_load()` or in the program's initialization phase.\n\n---------------------\n\n.. function:: const char *obs_encoder_get_display_name(const char *id)\n\n   Calls the :c:member:`obs_encoder_info.get_name` callback to get the\n   translated display name of an encoder type.\n\n   :param    id:            The encoder type string identifier\n   :return:                 The translated display name of an encoder type\n\n---------------------\n\n.. function:: obs_encoder_t *obs_video_encoder_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data)\n\n   Creates a video encoder with the specified settings.\n\n   The \"encoder\" context is used for encoding video/audio data.  Use\n   obs_encoder_release to release it.\n\n   :param   id:             The encoder type string identifier\n   :param   name:           The desired name of the encoder.  If this is\n                            not unique, it will be made to be unique\n   :param   settings:       The settings for the encoder, or *NULL* if\n                            none\n   :param   hotkey_data:    Saved hotkey data for the encoder, or *NULL*\n                            if none\n   :return:                 A reference to the newly created encoder, or\n                            *NULL* if failed\n\n---------------------\n\n.. function:: obs_encoder_t *obs_audio_encoder_create(const char *id, const char *name, obs_data_t *settings, size_t mixer_idx, obs_data_t *hotkey_data)\n\n   Creates an audio encoder with the specified settings.\n\n   The \"encoder\" context is used for encoding video/audio data.  Use\n   :c:func:`obs_encoder_release()` to release it.\n\n   :param   id:             The encoder type string identifier\n   :param   name:           The desired name of the encoder.  If this is\n                            not unique, it will be made to be unique\n   :param   settings:       The settings for the encoder, or *NULL* if\n                            none\n   :param   mixer_idx:      The audio mixer index this audio encoder\n                            will capture audio from\n   :param   hotkey_data:    Saved hotkey data for the encoder, or *NULL*\n                            if none\n   :return:                 A reference to the newly created encoder, or\n                            *NULL* if failed\n\n---------------------\n\n.. function:: obs_encoder_t *obs_encoder_get_ref(obs_encoder_t *encoder)\n\n   Returns an incremented reference if still valid, otherwise returns\n   *NULL*. Release with :c:func:`obs_encoder_release()`.\n\n---------------------\n\n.. function:: void obs_encoder_release(obs_encoder_t *encoder)\n\n   Releases a reference to an encoder.  When the last reference is released,\n   the encoder is destroyed.\n\n---------------------\n\n.. function:: obs_weak_encoder_t *obs_encoder_get_weak_encoder(obs_encoder_t *encoder)\n              obs_encoder_t *obs_weak_encoder_get_encoder(obs_weak_encoder_t *weak)\n\n   These functions are used to get a weak reference from a strong encoder\n   reference, or a strong encoder reference from a weak reference.  If\n   the encoder is destroyed, *obs_weak_encoder_get_encoder* will return\n   *NULL*.\n\n---------------------\n\n.. function:: void obs_weak_encoder_addref(obs_weak_encoder_t *weak)\n              void obs_weak_encoder_release(obs_weak_encoder_t *weak)\n\n   Adds/releases a weak reference to an encoder.\n\n---------------------\n\n.. function:: void obs_encoder_set_name(obs_encoder_t *encoder, const char *name)\n\n   Sets the name of an encoder.  If the encoder is not private and the\n   name is not unique, it will automatically be given a unique name.\n\n---------------------\n\n.. function:: const char *obs_encoder_get_name(const obs_encoder_t *encoder)\n\n   :return: The name of the encoder\n\n---------------------\n\n.. function:: const char *obs_encoder_get_codec(const obs_encoder_t *encoder)\n              const char *obs_get_encoder_codec(const char *id)\n\n   :return: The codec identifier of the encoder\n\n---------------------\n\n.. function:: enum obs_encoder_type obs_encoder_get_type(const obs_encoder_t *encoder)\n              enum obs_encoder_type obs_get_encoder_type(const char *id)\n\n   :return: The encoder type: OBS_ENCODER_VIDEO or OBS_ENCODER_AUDIO\n\n---------------------\n\n.. function:: void obs_encoder_set_scaled_size(obs_encoder_t *encoder, uint32_t width, uint32_t height)\n\n   Sets the scaled resolution for a video encoder.  Set width and height to 0\n   to disable scaling.  If the encoder is active, this function will trigger\n   a warning, and do nothing.\n\n---------------------\n\n.. function:: bool obs_encoder_scaling_enabled(const obs_encoder_t *encoder)\n\n   :return: *true* if pre-encode (CPU) scaling enabled, *false*\n            otherwise.\n\n---------------------\n\n.. function:: uint32_t obs_encoder_get_width(const obs_encoder_t *encoder)\n              uint32_t obs_encoder_get_height(const obs_encoder_t *encoder)\n\n   :return: The width/height of a video encoder's encoded image\n\n---------------------\n\n.. function:: uint32_t obs_encoder_get_sample_rate(const obs_encoder_t *encoder)\n\n   :return: The sample rate of an audio encoder's audio data\n\n---------------------\n\n.. function:: size_t obs_encoder_get_frame_size(const obs_encoder_t *encoder)\n\n   :return: The frame size of the audio packet\n\n---------------------\n\n.. function:: size_t obs_encoder_get_mixer_index(const obs_encoder_t *encoder)\n\n   :return: The mixer index for the audio track which is encoded by the encoder\n\n---------------------\n\n.. function:: void obs_encoder_set_preferred_video_format(obs_encoder_t *encoder, enum video_format format)\n              enum video_format obs_encoder_get_preferred_video_format(const obs_encoder_t *encoder)\n\n\n   Sets the preferred video format for a video encoder.  If the encoder can use\n   the format specified, it will force a conversion to that format if the\n   obs output format does not match the preferred format.\n\n   If the format is set to VIDEO_FORMAT_NONE, will revert to the default\n   functionality of converting only when absolutely necessary.\n\n---------------------\n\n.. function:: obs_data_t *obs_encoder_defaults(const char *id)\n              obs_data_t *obs_encoder_get_defaults(const obs_encoder_t *encoder)\n\n   :return: An incremented reference to the encoder's default settings.\n            Release with :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: obs_properties_t *obs_encoder_properties(const obs_encoder_t *encoder)\n              obs_properties_t *obs_get_encoder_properties(const char *id)\n\n   Use these functions to get the properties of an encoder or encoder\n   type.  Properties are optionally used (if desired) to automatically\n   generate user interface widgets to allow users to update settings.\n\n   :return: The properties list for a specific existing encoder.  Free\n            with :c:func:`obs_properties_destroy()`\n\n---------------------\n\n.. function:: void obs_encoder_update(obs_encoder_t *encoder, obs_data_t *settings)\n\n   Updates the settings for this encoder context.\n\n---------------------\n\n.. function:: obs_data_t *obs_encoder_get_settings(const obs_encoder_t *encoder)\n\n   :return: An incremented reference to the encoder's settings. Release with\n            :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: signal_handler_t *obs_encoder_get_signal_handler(const obs_encoder_t *encoder)\n\n   :return: The signal handler of the encoder. Should not be manually freed,\n            as its lifecycle is managed by libobs.\n\n---------------------\n\n.. function:: proc_handler_t *obs_encoder_get_proc_handler(const obs_encoder_t *encoder)\n\n   :return: The procedure handler of the encoder. Should not be manually freed,\n            as its lifecycle is managed by libobs.\n\n---------------------\n\n.. function:: bool obs_encoder_get_extra_data(const obs_encoder_t *encoder, uint8_t **extra_data, size_t *size)\n\n   Gets extra data (headers) associated with this encoder.\n\n   :return: *true* if successful, *false* if no extra data associated\n            with this encoder\n\n---------------------\n\n.. function:: void obs_encoder_set_video(obs_encoder_t *encoder, video_t *video)\n              void obs_encoder_set_audio(obs_encoder_t *encoder, audio_t *audio)\n\n   Sets the video/audio handler to use with this video/audio encoder.\n   This is used to capture the raw video/audio data.\n\n---------------------\n\n.. function:: video_t *obs_encoder_video(const obs_encoder_t *encoder)\n              video_t *obs_encoder_parent_video(const obs_encoder_t *encoder)\n              audio_t *obs_encoder_audio(const obs_encoder_t *encoder)\n\n   :return: The video/audio handler associated with this encoder, or\n            *NULL* if none or not a matching encoder type.\n            *parent_video* returns the \"original\" video handler\n            associated with this encoder, regardless of whether an FPS\n            divisor is set.\n\n---------------------\n\n.. function:: bool obs_encoder_active(const obs_encoder_t *encoder)\n\n   :return: *true* if the encoder is active, *false* otherwise\n\n---------------------\n\n.. function:: bool obs_encoder_add_roi(obs_encoder_t *encoder, const struct obs_encoder_roi *roi)\n\n    Adds a new region of interest to the encoder if ROI feature is supported.\n\n   :return: *true* if adding succeeded, *false* otherwise.\n\n   .. versionadded:: 30.1\n\n---------------------\n\n.. function:: bool obs_encoder_has_roi(obs_encoder_t *encoder)\n\n   :return: *true* if encoder has ROI regions set, *false* otherwise.\n\n   .. versionadded:: 30.1\n\n---------------------\n\n.. function:: void obs_encoder_clear_roi(obs_encoder_t *encoder)\n\n    Clear region of interest list, if any.\n\n   .. versionadded:: 30.1\n\n---------------------\n\n.. function:: void obs_encoder_enum_roi(obs_encoder_t *encoder, void (*enum_proc)(void *, struct obs_encoder_roi *), void *param)\n\n    Enumerate currently configured ROIs by invoking callback for each entry, in reverse order of addition (i.e. most recent to oldest).\n\n    **Note:** If the encoder has scaling enabled the struct passed to the callback will be scaled accordingly.\n\n   .. versionadded:: 30.1\n\n---------------------\n\n.. function:: uint32_t obs_encoder_get_roi_increment(const obs_encoder_t *encoder)\n\n   Encoders shall refresh their ROI configuration if the increment value changes.\n\n   :return: Increment/revision of ROI list\n\n   .. versionadded:: 30.1\n\n---------------------\n\n.. function:: uint32_t obs_encoder_get_priming_samples(const obs_encoder_t *encoder)\n\n   Gets the number of samples that shall be skipped when playing back the encoded audio.\n   Commonly referred to as \"encoder delay\" or \"priming samples\" in AAC/Opus.\n\n   :return: Number of priming samples\n\n   .. versionadded:: 32.1\n\n---------------------\n\nFunctions used by encoders\n--------------------------\n\n.. function:: void obs_encoder_packet_ref(struct encoder_packet *dst, struct encoder_packet *src)\n              void obs_encoder_packet_release(struct encoder_packet *packet)\n\n   Adds or releases a reference to an encoder packet.\n\n.. ---------------------------------------------------------------------------\n\n.. _libobs/obs-encoder.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-encoder.h\n"
  },
  {
    "path": "docs/sphinx/reference-frontend-api.rst",
    "content": "OBS Studio Frontend API\n=======================\n\nThe OBS Studio frontend API is the API specific to OBS Studio itself.\n\n.. code:: cpp\n\n   #include <obs-frontend-api.h>\n\n\nStructures/Enumerations\n-----------------------\n\n.. enum:: obs_frontend_event\n\n   Specifies a front-end event.  Can be one of the following values:\n\n   - **OBS_FRONTEND_EVENT_STREAMING_STARTING**\n\n     Triggered when streaming is starting.\n\n   - **OBS_FRONTEND_EVENT_STREAMING_STARTED**\n\n     Triggered when streaming has successfully started.\n\n   - **OBS_FRONTEND_EVENT_STREAMING_STOPPING**\n\n     Triggered when streaming is stopping.\n\n   - **OBS_FRONTEND_EVENT_STREAMING_STOPPED**\n\n     Triggered when streaming has fully stopped.\n\n   - **OBS_FRONTEND_EVENT_RECORDING_STARTING**\n\n     Triggered when recording is starting.\n\n   - **OBS_FRONTEND_EVENT_RECORDING_STARTED**\n\n     Triggered when recording has successfully started.\n\n   - **OBS_FRONTEND_EVENT_RECORDING_STOPPING**\n\n     Triggered when recording is stopping.\n\n   - **OBS_FRONTEND_EVENT_RECORDING_STOPPED**\n\n     Triggered when recording has fully stopped.\n\n   - **OBS_FRONTEND_EVENT_RECORDING_PAUSED**\n\n     Triggered when the recording has been paused.\n\n   - **OBS_FRONTEND_EVENT_RECORDING_UNPAUSED**\n\n     Triggered when the recording has been unpaused.\n\n   - **OBS_FRONTEND_EVENT_SCENE_CHANGED**\n\n     Triggered when the current scene has changed.\n\n   - **OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED**\n\n     Triggered when a scenes has been added/removed/reordered by the\n     user.\n\n   - **OBS_FRONTEND_EVENT_TRANSITION_CHANGED**\n\n     Triggered when the current transition has changed by the user.\n\n   - **OBS_FRONTEND_EVENT_TRANSITION_STOPPED**\n\n     Triggered when a transition has completed.\n\n   - **OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED**\n\n     Triggered when the user adds/removes transitions.\n\n   - **OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED**\n\n     Triggered when the transition duration has been changed by the\n     user.\n\n   - **OBS_FRONTEND_EVENT_TBAR_VALUE_CHANGED**\n\n     Triggered when the transition bar is moved.\n\n   - **OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING**\n\n     Triggered when the current scene collection is about to change.\n\n   - **OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED**\n\n     Triggered when the current scene collection has changed.\n\n   - **OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED**\n\n     Triggered when a scene collection has been added or removed.\n\n   - **OBS_FRONTEND_EVENT_SCENE_COLLECTION_RENAMED**\n\n     Triggered when a scene collection has been renamed.\n\n   - **OBS_FRONTEND_EVENT_PROFILE_CHANGING**\n\n     Triggered when the current profile is about to change.\n\n   - **OBS_FRONTEND_EVENT_PROFILE_CHANGED**\n\n     Triggered when the current profile has changed.\n\n   - **OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED**\n\n     Triggered when a profile has been added or removed.\n\n   - **OBS_FRONTEND_EVENT_PROFILE_RENAMED**\n\n     Triggered when a profile has been renamed.\n\n   - **OBS_FRONTEND_EVENT_FINISHED_LOADING**\n\n     Triggered when the program has finished loading.\n\n   - **OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN**\n\n     Triggered when scripts need to know that OBS is exiting.  The\n     **OBS_FRONTEND_EVENT_EXIT** event is normally called after scripts\n     have been destroyed.\n\n   - **OBS_FRONTEND_EVENT_EXIT**\n\n     Triggered when the program is about to exit. This is the last chance\n     to call any frontend API functions for any saving / cleanup / etc.\n     After returning from this event callback, it is not permitted to make\n     any further frontend API calls.\n\n   - **OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING**\n\n     Triggered when the replay buffer is starting.\n\n   - **OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED**\n\n     Triggered when the replay buffer has successfully started.\n\n   - **OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING**\n\n     Triggered when the replay buffer is stopping.\n\n   - **OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED**\n\n     Triggered when the replay buffer has fully stopped.\n\n   - **OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED**\n\n     Triggered when the replay buffer has been saved.\n\n   - **OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED**\n\n     Triggered when the user has turned on studio mode.\n\n   - **OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED**\n\n     Triggered when the user has turned off studio mode.\n\n   - **OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED**\n\n     Triggered when the current preview scene has changed in studio\n     mode.\n\n   - **OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP**\n\n     Triggered when a scene collection has been completely unloaded, and\n     the program is either about to load a new scene collection, or the\n     program is about to exit.\n\n   - **OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED**\n\n     Triggered when the virtual camera is started.\n\n   - **OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED**\n\n     Triggered when the virtual camera is stopped.\n\n   - **OBS_FRONTEND_EVENT_THEME_CHANGED**\n\n     Triggered when the theme is changed.\n\n     .. versionadded:: 29.0.0\n\n   - **OBS_FRONTEND_EVENT_SCREENSHOT_TAKEN**\n\n     Triggered when a screenshot is taken.\n\n     .. versionadded:: 29.0.0\n\n.. struct:: obs_frontend_source_list\n\n   - DARRAY(obs_source_t*) **sources**\n\n   Example usage:\n\n   .. code:: cpp\n\n      struct obs_frontend_source_list scenes = {0};\n\n      obs_frontend_get_scenes(&scenes);\n\n      for (size_t i = 0; i < scenes.sources.num; i++) {\n            /* Do NOT call `obs_source_release` or `obs_scene_release`\n             * on these sources\n             */\n            obs_source_t *source = scenes.sources.array[i];\n\n            /* Convert to obs_scene_t if needed */\n            obs_scene_t *scene = obs_scene_from_source(source);\n\n            [...]\n      }\n\n      obs_frontend_source_list_free(&scenes);\n\n.. type:: void (*obs_frontend_cb)(void *private_data)\n\n   Frontend tool menu callback\n\n.. type:: void (*obs_frontend_event_cb)(enum obs_frontend_event event, void *private_data)\n\n   Frontend event callback\n\n.. type:: void (*obs_frontend_save_cb)(obs_data_t *save_data, bool saving, void *private_data)\n\n   Frontend save/load callback\n\n.. type:: bool (*obs_frontend_translate_ui_cb)(const char *text, const char **out)\n\n   Translation callback\n\n.. type::  void (*undo_redo_cb)(const char *data)\n\n   Undo redo callback\n\n\nFunctions\n---------\n\n.. function:: void obs_frontend_source_list_free(struct obs_frontend_source_list *source_list)\n\n   Releases sources within a source list and frees the list.\n\n   :param source_list: Source list to free\n\n---------------------------------------\n\n.. function:: void *obs_frontend_get_main_window(void)\n\n   :return: The QMainWindow pointer to the OBS Studio window\n\n---------------------------------------\n\n.. function:: void *obs_frontend_get_main_window_handle(void)\n\n   :return: The native window handle of the OBS Studio window\n\n---------------------------------------\n\n.. function:: char **obs_frontend_get_scene_names(void)\n\n   :return: The scene name list, ending with NULL.  The list is stored\n            within one contiguous segment of memory, so freeing the\n            returned pointer with :c:func:`bfree()` will free the entire\n            list. The order is same as the way the frontend displays it in\n            the Scenes dock.\n\n---------------------------------------\n\n.. function:: void obs_frontend_get_scenes(struct obs_frontend_source_list *sources)\n\n   Populates ``sources`` with reference-incremented scenes in the same order as\n   the frontend displays it in the Scenes dock. Release with\n   :c:func:`obs_frontend_source_list_free`, which will automatically release all\n   scenes with :c:func:`obs_source_release`. Do not release a scene manually to\n   prevent double releasing, which may cause scenes to be deleted.\n\n   Use :c:func:`obs_scene_from_source` to access a source from the list as an\n   :c:type:`obs_scene_t` object.\n\n   If you wish to keep a reference to a certain scene, use\n   :c:func:`obs_source_get_ref` or :c:func:`obs_scene_get_ref` on that scene and\n   release it with either :c:func:`obs_source_release` or\n   :c:func:`obs_scene_release`. Use only one release function, as both releases\n   the same object.\n\n   :param sources: Pointer to a :c:type:`obs_frontend_source_list`\n                   structure to receive the list of reference-incremented\n                   scenes.\n\n---------------------------------------\n\n.. function:: obs_source_t *obs_frontend_get_current_scene(void)\n\n   :return: A new reference to the currently active scene. Release with\n            :c:func:`obs_source_release()`.\n\n---------------------------------------\n\n.. function:: void obs_frontend_set_current_scene(obs_source_t *scene)\n\n   :param scene: The scene to set as the current scene\n\n---------------------------------------\n\n.. function:: void obs_frontend_get_transitions(struct obs_frontend_source_list *sources)\n\n   :param sources: Pointer to a :c:type:`obs_frontend_source_list`\n                   structure to receive the list of\n                   reference-incremented transitions.  Release with\n                   :c:func:`obs_frontend_source_list_free`\n\n---------------------------------------\n\n.. function:: obs_source_t *obs_frontend_get_current_transition(void)\n\n   :return: A new reference to the currently active transition.\n            Release with :c:func:`obs_source_release()`.\n\n---------------------------------------\n\n.. function:: void obs_frontend_set_current_transition(obs_source_t *transition)\n\n   :param transition: The transition to set as the current transition\n\n---------------------------------------\n\n.. function:: int obs_frontend_get_transition_duration(void)\n\n   :return: The transition duration (in milliseconds) currently set in the UI\n\n---------------------------------------\n\n.. function:: void obs_frontend_set_transition_duration(int duration)\n\n   :param duration: Desired transition duration, in milliseconds\n\n---------------------------------------\n\n.. function:: void obs_frontend_release_tbar(void)\n\n   Emulate a mouse button release on the transition bar and determine transition status.\n\n---------------------------------------\n\n.. function:: void obs_frontend_set_tbar_position(int position)\n\n   Set the value of the transition bar.\n\n   :param position: The position to set the T-bar to, with a range of 0-1023\n\n---------------------------------------\n\n.. function:: int obs_frontend_get_tbar_position(void)\n\n   Get the value of the transition bar.\n\n   :return: The value of the position of the T-bar to, with a range of 0-1023\n\n---------------------------------------\n\n.. function:: char **obs_frontend_get_scene_collections(void)\n\n   :return: The list of scene collection names, ending with NULL.  The list is\n            stored within one contiguous segment of memory, so freeing\n            the returned pointer with :c:func:`bfree()` will free the\n            entire list.\n\n---------------------------------------\n\n.. function:: char *obs_frontend_get_current_scene_collection(void)\n\n   :return: A new pointer to the current scene collection name.  Free\n            with :c:func:`bfree()`\n\n---------------------------------------\n\n.. function:: void obs_frontend_set_current_scene_collection(const char *collection)\n\n   :param collection: Name of the scene collection to activate\n\n---------------------------------------\n\n.. function:: bool obs_frontend_add_scene_collection(const char *name)\n\n   Add a new scene collection, then switch to it.\n\n   :param name: Name of the scene collection to add/create\n\n---------------------------------------\n\n.. function:: char **obs_frontend_get_profiles(void)\n\n   :return: The list of profile names, ending with NULL.  The list is\n            stored within one contiguous segment of memory, so freeing\n            the returned pointer with :c:func:`bfree()` will free the\n            entire list.\n\n---------------------------------------\n\n.. function:: char *obs_frontend_get_current_profile(void)\n\n   :return: A new pointer to the current profile name.  Free with\n            :c:func:`bfree()`\n\n---------------------------------------\n\n.. function:: char *obs_frontend_get_current_profile_path(void)\n\n   :return: A new pointer to the current profile's path on the\n            filesystem.  Free with :c:func:`bfree()`\n\n---------------------------------------\n\n.. function:: void obs_frontend_set_current_profile(const char *profile)\n\n   :param profile: Name of the profile to activate\n\n---------------------------------------\n\n.. function:: bool obs_frontend_create_profile(const char *name)\n\n   :param name: Name of the new profile to create (must be unique)\n\n---------------------------------------\n\n.. function:: bool obs_frontend_duplicate_profile(const char *name)\n\n   :param name: Name of the duplicate profile to create (must be unique)\n\n---------------------------------------\n\n.. function:: void obs_frontend_delete_profile(const char *profile)\n\n   :param profile: Name of the profile to delete\n\n---------------------------------------\n\n.. function:: void *obs_frontend_add_tools_menu_qaction(const char *name)\n\n   Adds a QAction to the tools menu then returns it.\n\n   :param name: Name for the new menu item\n   :return: A pointer to the added QAction\n\n---------------------------------------\n\n.. function:: void obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data)\n\n   Adds a tools menu item and links the ::clicked signal to the\n   callback.\n\n   :param name: The name for the new menu item\n   :param callback: Callback to use when the menu item is clicked\n   :param private_data: Private data associated with the callback\n\n---------------------------------------\n\n.. function:: bool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget)\n\n   Adds a dock with the widget to the UI with a toggle in the Docks\n   menu.\n\n   When the dock is closed, a custom QEvent of type `QEvent::User + QEvent::Close`\n   is sent to the widget to enable it to react to the event (e.g., unload elements\n   to save resources).\n   A generic QShowEvent is already sent by default when the widget is being\n   shown (e.g., dock opened).\n\n   Note: Use :c:func:`obs_frontend_remove_dock` to remove the dock\n         and the id from the UI.\n\n   :param id: Unique identifier of the dock\n   :param title: Window title of the dock\n   :param widget: QWidget to insert in the dock\n   :return: *true* if the dock was added, *false* if the id was already\n            used\n\n   .. versionadded:: 30.0\n\n---------------------------------------\n\n.. function:: void obs_frontend_remove_dock(const char *id)\n\n   Removes the dock with this id from the UI.\n\n   :param id: Unique identifier of the dock to remove.\n\n   .. versionadded:: 30.0\n\n---------------------------------------\n\n.. function:: bool obs_frontend_add_custom_qdock(const char *id, void *dock)\n\n   Adds a custom dock to the UI with no toggle.\n\n   Note: Use :c:func:`obs_frontend_remove_dock` to remove the dock\n         reference and id from the UI.\n\n   :param id: Unique identifier of the dock\n   :param dock: QDockWidget to add\n   :return: *true* if the dock was added, *false* if the id was already\n            used\n\n   .. versionadded:: 30.0\n\n---------------------------------------\n\n.. function:: void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data)\n\n   Adds a callback that will be called when a frontend event occurs.\n   See :c:type:`obs_frontend_event` on what sort of events can be\n   triggered.\n\n   :param callback:     Callback to use when a frontend event occurs\n   :param private_data: Private data associated with the callback\n\n---------------------------------------\n\n.. function:: void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data)\n\n   Removes an event callback.\n\n   :param callback:     Callback to remove\n   :param private_data: Private data associated with the callback\n\n---------------------------------------\n\n.. function:: void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data)\n\n   Adds a callback that will be called when the current scene collection\n   is being saved/loaded.\n\n   :param callback:     Callback to use when saving/loading a scene\n                        collection\n   :param private_data: Private data associated with the callback\n\n---------------------------------------\n\n.. function:: void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data)\n\n   Removes a save/load callback.\n\n   :param callback:     Callback to remove\n   :param private_data: Private data associated with the callback\n\n---------------------------------------\n\n.. function:: void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data)\n\n   Adds a callback that will be called right before a scene collection\n   is loaded.\n\n   :param callback:     Callback to use when pre-loading\n   :param private_data: Private data associated with the callback\n\n---------------------------------------\n\n.. function:: void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data)\n\n   Removes a pre-load callback.\n\n   :param callback:     Callback to remove\n   :param private_data: Private data associated with the callback\n\n---------------------------------------\n\n.. function:: void obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate)\n\n   Pushes a UI translation callback.  This allows a front-end plugin to\n   intercept when Qt is automatically generating translating data.\n   Typically this is just called with obs_module_get_string.\n\n   :param translate: The translation callback to use\n\n---------------------------------------\n\n.. function:: void obs_frontend_pop_ui_translation(void)\n\n   Pops the current UI translation callback.\n\n---------------------------------------\n\n.. function:: void obs_frontend_streaming_start(void)\n\n   Starts streaming.\n\n---------------------------------------\n\n.. function:: void obs_frontend_streaming_stop(void)\n\n   Stops streaming.\n\n---------------------------------------\n\n.. function:: bool obs_frontend_streaming_active(void)\n\n   :return: *true* if streaming active, *false* otherwise\n\n---------------------------------------\n\n.. function:: void obs_frontend_recording_start(void)\n\n   Starts recording.\n\n---------------------------------------\n\n.. function:: void obs_frontend_recording_stop(void)\n\n   Stops recording.\n\n---------------------------------------\n\n.. function:: bool obs_frontend_recording_active(void)\n\n   :return: *true* if recording active, *false* otherwise\n\n---------------------------------------\n\n.. function:: void obs_frontend_recording_pause(bool pause)\n\n   :pause: *true* to pause recording, *false* to unpause\n\n---------------------------------------\n\n.. function:: bool obs_frontend_recording_paused(void)\n\n   :return: *true* if recording paused, *false* otherwise\n\n---------------------------------------\n\n.. function:: bool obs_frontend_recording_split_file(void)\n\n   Asks OBS to split the current recording file.\n\n   :return: *true* if splitting was successfully requested (this\n            does not mean that splitting has finished or guarantee that it\n            split successfully), *false* if recording is inactive or paused\n            or if file splitting is disabled.\n\n---------------------------------------\n\n.. function:: bool obs_frontend_recording_add_chapter(const char *name)\n\n   Asks OBS to insert a chapter marker at the current output time into the recording.\n\n   :param name: The name for the chapter, may be *NULL* to use an automatically generated name (\"Unnamed <Chapter number>\" or localized equivalent).\n   :return: *true* if insertion was successful, *false* if recording is inactive, paused, or if chapter insertion is not supported by the current output.\n\n   .. versionadded:: 30.2\n\n---------------------------------------\n\n.. function:: void obs_frontend_replay_buffer_start(void)\n\n   Starts the replay buffer.\n\n---------------------------------------\n\n.. function:: void obs_frontend_replay_buffer_stop(void)\n\n   Stops the replay buffer.\n\n---------------------------------------\n\n.. function:: void obs_frontend_replay_buffer_save(void)\n\n   Saves a replay if the replay buffer is active.\n\n---------------------------------------\n\n.. function:: bool obs_frontend_replay_buffer_active(void)\n\n   :return: *true* if replay buffer active, *false* otherwise\n\n---------------------------------------\n\n.. function:: void obs_frontend_open_projector(const char *type, int monitor, const char *geometry, const char *name)\n\n   :param type:     \"Preview\", \"Source\", \"Scene\", \"StudioProgram\", or \"Multiview\" (case insensitive)\n   :param monitor:  Monitor to open the projector on.  If -1, this opens a window.\n   :param geometry: If *monitor* is -1, size and position of the projector window.  Encoded in Base64 using Qt's geometry encoding.\n   :param name:     If *type* is \"Source\" or \"Scene\", name of the source or scene to be displayed\n\n---------------------------------------\n\n.. function:: void obs_frontend_save(void)\n\n   Saves the current scene collection.\n\n---------------------------------------\n\n.. function:: obs_output_t *obs_frontend_get_streaming_output(void)\n\n   :return: A new reference to the current streaming output.\n            Release with :c:func:`obs_output_release()`.\n\n---------------------------------------\n\n.. function:: obs_output_t *obs_frontend_get_recording_output(void)\n\n   :return: A new reference to the current recording output.\n            Release with :c:func:`obs_output_release()`.\n\n---------------------------------------\n\n.. function:: obs_output_t *obs_frontend_get_replay_buffer_output(void)\n\n   :return: A new reference to the current replay buffer output.\n            Release with :c:func:`obs_output_release()`.\n\n---------------------------------------\n\n.. function:: config_t *obs_frontend_get_profile_config(void)\n\n   :return: The config_t* associated with the current profile\n\n---------------------------------------\n\n.. deprecated:: 31.0\n.. function:: config_t *obs_frontend_get_global_config(void)\n\n   :return: The config_t* associated with the global config (global.ini)\n\n---------------------------------------\n\n.. function:: config_t *obs_frontend_get_app_config(void)\n\n   :return: The config_t* associated with system-wide settings (global.ini)\n\n   .. versionadded:: 31.0\n\n---------------------------------------\n\n.. function:: config_t *obs_frontend_get_user_config(void)\n\n   :return: The config_t* associated with user settings (user.ini)\n\n   .. versionadded:: 31.0\n\n---------------------------------------\n\n.. function:: void obs_frontend_set_streaming_service(obs_service_t *service)\n\n   Sets the current streaming service to stream with.\n\n   :param service: The streaming service to set\n\n---------------------------------------\n\n.. function:: obs_service_t *obs_frontend_get_streaming_service(void)\n\n   :return: The current streaming service object. Does not increment the\n            reference.\n\n---------------------------------------\n\n.. function:: void obs_frontend_save_streaming_service(void)\n\n   Saves the current streaming service data.\n\n---------------------------------------\n\n.. function:: bool obs_frontend_preview_program_mode_active(void)\n\n   :return: *true* if studio mode is active, *false* otherwise\n\n---------------------------------------\n\n.. function:: void obs_frontend_set_preview_program_mode(bool enable)\n\n   Activates/deactivates studio mode.\n\n   :param enable: *true* to activate studio mode, *false* to deactivate\n                  studio mode\n\n---------------------------------------\n\n.. function:: void obs_frontend_preview_program_trigger_transition(void)\n\n   Triggers a preview-to-program transition if studio mode is active.\n\n---------------------------------------\n\n.. function:: obs_source_t *obs_frontend_get_current_preview_scene(void)\n\n   :return: A new reference to the current preview scene if studio mode\n            is active, or *NULL* if studio mode is not active. Release\n            with :c:func:`obs_source_release()`.\n\n---------------------------------------\n\n.. function:: void obs_frontend_set_current_preview_scene(obs_source_t *scene)\n\n   Sets the current preview scene in studio mode. Does nothing if studio\n   mode is disabled.\n\n   :param scene: The scene to set as the current preview\n\n---------------------------------------\n\n.. function:: void obs_frontend_set_preview_enabled(bool enable)\n\n   Sets the enable state of the preview display.  Only relevant with\n   studio mode disabled.\n\n   :param enable: *true* to enable preview, *false* to disable preview\n\n---------------------------------------\n\n.. function:: bool obs_frontend_preview_enabled(void)\n\n   :return: *true* if the preview display is enabled, *false* otherwise\n\n---------------------------------------\n\n.. function:: void *obs_frontend_take_screenshot(void)\n\n   Takes a screenshot of the main OBS output.\n\n---------------------------------------\n\n.. function:: void *obs_frontend_take_source_screenshot(obs_source_t *source)\n\n   Takes a screenshot of the specified source.\n\n   :param source: The source to take screenshot of\n\n---------------------------------------\n\n.. function:: obs_output_t *obs_frontend_get_virtualcam_output(void)\n\n   :return: A new reference to the current virtual camera output.\n            Release with :c:func:`obs_output_release()`.\n\n---------------------------------------\n\n.. function:: void obs_frontend_start_virtualcam(void)\n\n   Starts the virtual camera.\n\n---------------------------------------\n\n.. function:: void obs_frontend_stop_virtualcam(void)\n\n   Stops the virtual camera.\n\n---------------------------------------\n\n.. function:: bool obs_frontend_virtualcam_active(void)\n\n   :return: *true* if virtual camera is active, *false* otherwise\n\n---------------------------------------\n\n.. function:: void obs_frontend_reset_video(void)\n\n   Reloads the UI canvas and resets libobs video with latest data from\n   the current profile.\n\n---------------------------------------\n\n.. function:: void *obs_frontend_open_source_properties(obs_source_t *source)\n\n   Opens the properties window of the specified source.\n\n   :param source: The source to open the properties window of\n\n---------------------------------------\n\n.. function:: void *obs_frontend_open_source_filters(obs_source_t *source)\n\n   Opens the filters window of the specified source.\n\n   :param source: The source to open the filters window of\n\n---------------------------------------\n\n.. function:: void *obs_frontend_open_source_interaction(obs_source_t *source)\n\n   Opens the interact window of the specified source. Only call if\n   source has the *OBS_SOURCE_INTERACTION* output flag.\n\n   :param source: The source to open the interact window of\n\n---------------------------------------\n\n.. function:: void *obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item)\n\n   Opens the edit transform window of the specified sceneitem.\n\n   :param item: The sceneitem to open the edit transform window of\n\n   .. versionadded:: 29.1\n\n---------------------------------------\n\n.. function:: char *obs_frontend_get_current_record_output_path(void)\n\n   :return: A new pointer to the current record output path.  Free\n            with :c:func:`bfree()`\n\n---------------------------------------\n\n.. function:: const char *obs_frontend_get_locale_string(const char *string)\n\n   :return: Gets the frontend translation of a given string.\n\n---------------------------------------\n\n.. function:: bool obs_frontend_is_theme_dark(void)\n\n   :return: Checks if the current theme is dark or light.\n\n   .. versionadded:: 29.0.0\n\n---------------------------------------\n\n.. function:: char *obs_frontend_get_last_recording(void)\n\n   :return: The file path of the last recording. Free with :c:func:`bfree()`\n\n   .. versionadded:: 29.0.0\n\n---------------------------------------\n\n.. function:: char *obs_frontend_get_last_screenshot(void)\n\n   :return: The file path of the last screenshot taken. Free with\n            :c:func:`bfree()`\n\n   .. versionadded:: 29.0.0\n\n---------------------------------------\n\n.. function:: char *obs_frontend_get_last_replay(void)\n\n   :return: The file path of the last replay buffer saved. Free with\n            :c:func:`bfree()`\n\n   .. versionadded:: 29.0.0\n\n---------------------------------------\n\n.. function:: void obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo, const undo_redo_cb redo, const char *undo_data, const char *redo_data, bool repeatable)\n\n   :param name: The name of the undo redo action\n   :param undo: Callback to use for undo\n   :param redo: Callback to use for redo\n   :param undo_data: String with data for the undo callback\n   :param redo_data: String with data for the redo callback\n   :param repeatable: Allow multiple actions with the same name to be merged to 1 undo redo action.\n                      This uses the undo action from the first and the redo action from the last action.\n\n   .. versionadded:: 29.1\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-callback.rst",
    "content": "Callback API Reference (libobs/callback)\n========================================\n\n\nCalldata\n--------\n\nThe :c:type:`calldata_t` object is used to pass parameters from signal\nhandlers or to procedure handlers.\n\n.. type:: calldata_t\n\n---------------------\n\n.. function:: void calldata_init(calldata_t *data)\n\n   Initializes a calldata structure (zeroes it).\n\n   :param data: Calldata structure\n\n---------------------\n\n.. function:: void calldata_free(calldata_t *data)\n\n   Frees a calldata structure. Should only be used if :c:func:`calldata_init()`\n   was used. If the object is received as a callback parameter, this function\n   should not be used.\n\n   :param data: Calldata structure\n\n---------------------\n\n.. function:: void calldata_set_int(calldata_t *data, const char *name, long long val)\n\n   Sets an integer parameter.\n\n   :param data: Calldata structure\n   :param name: Parameter name\n   :param val:  Integer value\n\n---------------------\n\n.. function:: void calldata_set_float(calldata_t *data, const char *name, double val)\n\n   Sets a floating point parameter.\n\n   :param data: Calldata structure\n   :param name: Parameter name\n   :param val:  Floating point value\n\n---------------------\n\n.. function:: void calldata_set_bool(calldata_t *data, const char *name, bool val)\n\n   Sets a boolean parameter.\n\n   :param data: Calldata structure\n   :param name: Parameter name\n   :param val:  Boolean value\n\n---------------------\n\n.. function:: void calldata_set_ptr(calldata_t *data, const char *name, void *ptr)\n\n   Sets a pointer parameter.\n\n   :param data: Calldata structure\n   :param name: Parameter name\n   :param val:  Pointer value\n\n---------------------\n\n.. function:: void calldata_set_string(calldata_t *data, const char *name, const char *str)\n\n   Sets a string parameter.\n\n   :param data: Calldata structure\n   :param name: Parameter name\n   :param val:  String\n\n---------------------\n\n.. function:: long long calldata_int(const calldata_t *data, const char *name)\n\n   Gets an integer parameter.\n\n   :param data: Calldata structure\n   :param name: Parameter name\n   :return:     Integer value\n\n---------------------\n\n.. function:: double calldata_float(const calldata_t *data, const char *name)\n\n   Gets a floating point parameter.\n\n   :param data: Calldata structure\n   :param name: Parameter name\n   :return:     Floating point value\n\n---------------------\n\n.. function:: bool calldata_bool(const calldata_t *data, const char *name)\n\n   Gets a boolean parameter.\n\n   :param data: Calldata structure\n   :param name: Parameter name\n   :return:     Boolean value\n\n---------------------\n\n.. function:: void *calldata_ptr(const calldata_t *data, const char *name)\n\n   Gets a pointer parameter. For example, :ref:`core_signal_handler_reference`\n   that have ``ptr source`` as a parameter requires this function to get the\n   pointer, which can be casted to :c:type:`obs_source_t`. Does not have to be freed.\n\n   :param data: Calldata structure\n   :param name: Parameter name\n   :return:     Pointer value\n\n---------------------\n\n.. function:: const char *calldata_string(const calldata_t *data, const char *name)\n\n   Gets a string parameter.\n\n   :param data: Calldata structure\n   :param name: Parameter name\n   :return:     String value\n\n---------------------\n\n\nSignals\n-------\n\nSignals are used for all event-based callbacks.\n\n.. code:: cpp\n\n   #include <callback/signal.h>\n\n.. type:: signal_handler_t\n\n---------------------\n\n.. type:: void (*signal_callback_t)(void *data, calldata_t *cd)\n\n   Signal callback.\n\n   :param data: Private data passed to this callback\n   :param cd:   Calldata object\n\n---------------------\n\n.. function:: signal_handler_t *signal_handler_create(void)\n\n   Creates a new signal handler object.\n\n   :return: A new signal handler object\n\n---------------------\n\n.. function:: void signal_handler_destroy(signal_handler_t *handler)\n\n   Destroys a signal handler.\n\n   :param handler: Signal handler object\n\n---------------------\n\n.. function:: bool signal_handler_add(signal_handler_t *handler, const char *signal_decl)\n\n   Adds a signal to a signal handler.\n\n   :param handler:     Signal handler object\n   :param signal_decl: Signal declaration string\n\n---------------------\n\n.. function:: bool signal_handler_add_array(signal_handler_t *handler, const char **signal_decls)\n\n   Adds multiple signals to a signal handler.\n\n   :param handler:      Signal handler object\n   :param signal_decls: An array of signal declaration strings,\n                        terminated by *NULL*\n\n---------------------\n\n.. function:: void signal_handler_connect(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data)\n\n   Connects a callback to a signal on a signal handler. Does nothing\n   if the combination of ``signal``, ``callback``, and ``data``\n   is already connected to the handler.\n\n   :param handler:  Signal handler object\n   :param signal:   Name of signal to handle\n   :param callback: Signal callback\n   :param data:     Private data passed to the callback\n\n   For scripting, use :py:func:`signal_handler_connect`.\n\n---------------------\n\n.. function:: void signal_handler_connect_ref(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data)\n\n   Connects a callback to a signal on a signal handler, and increments\n   the handler's internal reference counter, preventing it from being\n   destroyed until the signal has been disconnected. Even if the combination of\n   ``signal``, ``callback``, and ``data`` is already connected to the handler,\n   the reference counter is still incremented.\n\n   :param handler:  Signal handler object\n   :param signal:   Name of signal to handle\n   :param callback: Signal callback\n   :param data:     Private data passed to the callback\n\n---------------------\n\n.. function:: void signal_handler_disconnect(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data)\n\n   Disconnects a callback from a signal on a signal handler. Does nothing\n   if the combination of ``signal``, ``callback``, and ``data``\n   is not yet connected to the handler.\n\n   :param handler:  Signal handler object\n   :param signal:   Name of signal that was handled\n   :param callback: Signal callback\n   :param data:     Private data passed to the callback\n\n   For scripting, use :py:func:`signal_handler_disconnect`.\n\n---------------------\n\n.. function:: void signal_handler_signal(signal_handler_t *handler, const char *signal, calldata_t *params)\n\n   Triggers a signal, calling all connected callbacks.\n\n   :param handler: Signal handler object\n   :param signal:  Name of signal to trigger\n   :param params:  Parameters to pass to the signal\n\n---------------------\n\n\nProcedure Handlers\n------------------\n\nProcedure handlers are used to call functions without having to have\ndirect access to declarations or callback pointers.\n\n.. code:: cpp\n\n   #include <callback/proc.h>\n\n.. type:: proc_handler_t\n\n---------------------\n\n.. type:: void (*proc_handler_proc_t)(void *data, calldata_t *cd)\n\n   Procedure handler callback.\n\n   :param data: Private data passed to this callback\n   :param cd:   Calldata object\n\n---------------------\n\n.. function:: proc_handler_t *proc_handler_create(void)\n\n   Creates a new procedure handler.\n\n   :return: A new procedure handler object\n\n---------------------\n\n.. function:: void proc_handler_destroy(proc_handler_t *handler)\n\n   Destroys a procedure handler object.\n\n   :param handler: Procedure handler object\n\n---------------------\n\n.. function:: void proc_handler_add(proc_handler_t *handler, const char *decl_string, proc_handler_proc_t proc, void *data)\n\n   Adds a procedure to a procedure handler.\n\n   :param handler:     Procedure handler object\n   :param decl_string: Procedure declaration string\n   :param proc:        Procedure callback\n   :param data:        Private data to pass to the callback\n\n---------------------\n\n.. function:: bool proc_handler_call(proc_handler_t *handler, const char *name, calldata_t *params)\n\n   Calls a procedure within the procedure handler.\n\n   :param handler: Procedure handler object\n   :param name:    Name of procedure to call\n   :param params:  Calldata structure to pass to the procedure\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics-axisang.rst",
    "content": "Axis Angle\n==========\n\nProvides a helper structure for conversion to quaternions.\n\n.. code:: cpp\n\n   #include <graphics/axisang.h>\n\n.. struct:: axisang\n.. member:: float axisang.x\n\n   X axis\n\n.. member:: float axisang.y\n\n   Y axis\n\n.. member:: float axisang.z\n\n   Z axis\n\n.. member:: float axisang.w\n\n   Angle\n\n.. member:: float axisang.ptr[4]\n\n---------------------\n\n.. function:: void axisang_zero(struct axisang *dst)\n\n   Zeroes the axis angle.\n\n   :param dst: Axis angle\n\n---------------------\n\n.. function:: void axisang_copy(struct axisang *dst, struct axisang *aa)\n\n   Copies an axis angle.\n\n   :param dst: Axis angle to copy to\n   :param aa:  Axis angle to copy from\n\n---------------------\n\n.. function:: void axisang_set(struct axisang *dst, float x, float y, float z, float w)\n\n   Sets an axis angle.\n\n   :param dst: Axis angle to set\n   :param x:   X axis\n   :param y:   Y axis\n   :param z:   Z axis\n   :param w:   Angle\n\n---------------------\n\n.. function:: void axisang_from_quat(struct axisang *dst, const struct quat *q)\n\n   Creates an axis angle from a quaternion.\n\n   :param dst: Axis angle destination\n   :param q:   Quaternion to convert\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics-effects.rst",
    "content": "Effects (Shaders)\n=================\n\nEffects are a single collection of related shaders.  They're used for\neasily writing vertex and pixel shaders together all in the same file in\nHLSL format.\n\n.. code:: cpp\n\n   #include <graphics/graphics.h>\n\n.. type:: struct gs_effect           gs_effect_t\n\n   Effect object.\n\n.. type:: struct gs_effect_technique gs_technique_t\n\n   Technique object.\n\n.. type:: struct gs_effect_param     gs_eparam_t\n\n   Effect parameter object.\n\n\n---------------------\n\n.. function:: gs_effect_t *gs_effect_create_from_file(const char *file, char **error_string)\n\n   Creates an effect from file.\n\n   :param file:         Path to the effect file\n   :param error_string: Receives a pointer to the error string, which\n                        must be freed with :c:func:`bfree()`.  If\n                        *NULL*, this parameter is ignored.\n   :return:             The effect object, or *NULL* on error\n\n---------------------\n\n.. function:: gs_effect_t *gs_effect_create(const char *effect_string, const char *filename, char **error_string)\n\n   Creates an effect from a string.\n\n   :param effect_String: Effect string\n   :param error_string:  Receives a pointer to the error string, which\n                         must be freed with :c:func:`bfree()`.  If\n                         *NULL*, this parameter is ignored.\n   :return:              The effect object, or *NULL* on error\n\n---------------------\n\n.. function:: void gs_effect_destroy(gs_effect_t *effect)\n\n   Destroys the effect\n\n   :param effect: Effect object\n\n---------------------\n\n.. function:: gs_technique_t *gs_effect_get_technique(const gs_effect_t *effect, const char *name)\n\n   Gets a technique of the effect.\n\n   :param effect: Effect object\n   :param name:   Name of the technique\n   :return:       Technique object, or *NULL* if not found\n\n---------------------\n\n.. function:: gs_technique_t *gs_effect_get_current_technique(const gs_effect_t *effect)\n\n   Gets the current active technique of the effect.\n\n   :param effect: Effect object\n   :return:       Technique object, or *NULL* if none currently active\n\n---------------------\n\n.. function:: size_t gs_technique_begin(gs_technique_t *technique)\n\n   Begins a technique.\n\n   :param technique: Technique object\n   :return:          Number of passes this technique uses\n\n---------------------\n\n.. function:: void gs_technique_end(gs_technique_t *technique)\n\n   Ends a technique.  Make sure all active passes have been ended before\n   calling.\n\n   :param technique: Technique object\n\n---------------------\n\n.. function:: bool gs_technique_begin_pass(gs_technique_t *technique, size_t pass)\n\n   Begins a pass.  Automatically loads the vertex/pixel shaders\n   associated with this pass.  Draw after calling this function.\n\n   :param technique: Technique object\n   :param pass:      Pass index\n   :return:          *true* if the pass is valid, *false* otherwise\n\n---------------------\n\n.. function:: bool gs_technique_begin_pass_by_name(gs_technique_t *technique, const char *name)\n\n   Begins a pass by its name if the pass has a name.  Automatically\n   loads the vertex/pixel shaders associated with this pass.  Draw after\n   calling this function.\n\n   :param technique: Technique object\n   :param name:      Name of the pass\n   :return:          *true* if the pass is valid, *false* otherwise\n\n---------------------\n\n.. function:: void gs_technique_end_pass(gs_technique_t *technique)\n\n   Ends a pass.\n\n   :param technique: Technique object\n\n---------------------\n\n.. function:: size_t gs_effect_get_num_params(const gs_effect_t *effect)\n\n   Gets the number of parameters associated with the effect.\n\n   :param effect: Effect object\n   :return:       Number of parameters the effect has\n\n---------------------\n\n.. function:: gs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect, size_t param)\n\n   Gets a parameter of an effect by its index.\n\n   :param effect: Effect object\n   :param param:  Parameter index\n   :return:       The effect parameter object, or *NULL* if index\n                  invalid\n\n---------------------\n\n.. function:: gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect, const char *name)\n\n   Gets parameter of an effect by its name.\n\n   :param effect: Effect object\n   :param name:   Name of the parameter\n   :return:       The effect parameter object, or *NULL* if not found\n\n---------------------\n\n.. function:: size_t gs_param_get_num_annotations(const gs_eparam_t *param)\n\n   Gets the number of annotations associated with the parameter.\n\n   :param param:  Param object\n   :return:       Number of annotations the param has\n\n---------------------\n\n.. function:: gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param, size_t annotation)\n\n   Gets an annotation of a param by its index.\n\n   :param param:  Param object\n   :param param:  Annotation index\n   :return:       The effect parameter object (annotation), or *NULL* if index\n                  invalid\n\n---------------------\n\n.. function:: gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param, const char *annotation)\n\n   Gets parameter of an effect by its name.\n\n   :param param:  Param object\n   :param name:   Name of the annotation\n   :return:       The effect parameter object (annotation), or *NULL* if not found\n\n---------------------\n\n.. function:: bool gs_effect_loop(gs_effect_t *effect, const char *name)\n\n   Helper function that automatically begins techniques/passes.\n\n   :param effect: Effect object\n   :param name:   Name of the technique to execute\n   :return:       *true* to draw, *false* when complete\n\n   Here is an example of how this function is typically used:\n\n.. code:: cpp\n\n   for (gs_effect_loop(effect, \"my_technique\")) {\n           /* perform drawing here */\n           [...]\n   }\n\n---------------------\n\n.. function:: gs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect)\n\n   Gets the view/projection matrix parameter (\"viewproj\") of the effect.\n\n   :param effect: Effect object\n   :return:       The view/projection matrix parameter of the effect\n\n---------------------\n\n.. function:: gs_eparam_t *gs_effect_get_world_matrix(const gs_effect_t *effect)\n\n   Gets the world matrix parameter (\"world\") of the effect.\n\n   :param effect: Effect object\n   :return:       The world matrix parameter of the effect\n\n---------------------\n\n.. function:: void gs_effect_get_param_info(const gs_eparam_t *param, struct gs_effect_param_info *info)\n\n   Gets information about an effect parameter.\n\n   :param param: Effect parameter\n   :param info:  Pointer to receive the data\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   enum gs_shader_param_type {\n           GS_SHADER_PARAM_UNKNOWN,\n           GS_SHADER_PARAM_BOOL,\n           GS_SHADER_PARAM_FLOAT,\n           GS_SHADER_PARAM_INT,\n           GS_SHADER_PARAM_STRING,\n           GS_SHADER_PARAM_VEC2,\n           GS_SHADER_PARAM_VEC3,\n           GS_SHADER_PARAM_VEC4,\n           GS_SHADER_PARAM_INT2,\n           GS_SHADER_PARAM_INT3,\n           GS_SHADER_PARAM_INT4,\n           GS_SHADER_PARAM_MATRIX4X4,\n           GS_SHADER_PARAM_TEXTURE,\n   };\n\n   struct gs_effect_param_info {\n           const char *name;\n           enum gs_shader_param_type type;\n   }\n\n---------------------\n\n.. function:: void gs_effect_set_bool(gs_eparam_t *param, bool val)\n\n   Sets a boolean parameter.\n\n   :param param: Effect parameter\n   :param val:   Boolean value\n\n---------------------\n\n.. function:: void gs_effect_set_float(gs_eparam_t *param, float val)\n\n   Sets a floating point parameter.\n\n   :param param: Effect parameter\n   :param val:   Floating point value\n\n---------------------\n\n.. function:: void gs_effect_set_int(gs_eparam_t *param, int val)\n\n   Sets a integer parameter.\n\n   :param param: Effect parameter\n   :param val:   Integer value\n\n---------------------\n\n.. function:: void gs_effect_set_matrix4(gs_eparam_t *param, const struct matrix4 *val)\n\n   Sets a matrix parameter.\n\n   :param param: Effect parameter\n   :param val:   Matrix\n\n---------------------\n\n.. function:: void gs_effect_set_vec2(gs_eparam_t *param, const struct vec2 *val)\n\n   Sets a 2-component vector parameter.\n\n   :param param: Effect parameter\n   :param val:   Vector\n\n---------------------\n\n.. function:: void gs_effect_set_vec3(gs_eparam_t *param, const struct vec3 *val)\n\n   Sets a 3-component vector parameter.\n\n   :param param: Effect parameter\n   :param val:   Vector\n\n---------------------\n\n.. function:: void gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val)\n\n   Sets a 4-component vector parameter.\n\n   :param param: Effect parameter\n   :param val:   Vector\n\n---------------------\n\n.. function:: void gs_effect_set_color(gs_eparam_t *param, uint32_t argb)\n\n   Convenience function for setting a color value via an integer value.\n\n   :param param: Effect parameter\n   :param argb:  Integer color value (i.e. hex value would be\n                 0xAARRGGBB)\n\n---------------------\n\n.. function:: void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val)\n\n   Sets a texture parameter.\n\n   :param param: Effect parameter\n   :param val:   Texture\n\n---------------------\n\n.. function:: void gs_effect_set_texture_srgb(gs_eparam_t *param, gs_texture_t *val)\n\n   Sets a texture parameter using SRGB view if available.\n\n   :param param: Effect parameter\n   :param val:   Texture\n\n---------------------\n\n.. function:: void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size)\n\n   Sets a parameter with data manually.\n\n   :param param: Effect parameter\n   :param val:   Pointer to data\n   :param size:  Size of data\n\n---------------------\n\n.. function:: void gs_effect_set_default(gs_eparam_t *param)\n\n   Sets the parameter to its default value\n\n   :param: Effect parameter\n\n---------------------\n\n.. function:: void gs_effect_set_next_sampler(gs_eparam_t *param, gs_samplerstate_t *sampler)\n\n   Manually changes the sampler for an effect parameter the next time\n   it's used.\n\n   :param param:   Effect parameter\n   :param sampler: Sampler state object\n\n---------------------\n\n.. function:: void *gs_effect_get_val(gs_eparam_t *param)\n\n   Returns a copy of the param's current value.\n\n   :param param:   Effect parameter\n   :return:        A pointer to the copied byte value of the param's current value. Freed with :c:func:`bfree()`.\n\n---------------------\n\n.. function:: void gs_effect_get_default_val(gs_eparam_t *param)\n\n   Returns a copy of the param's default value.\n\n   :param param:   Effect parameter\n   :return:        A pointer to the copied byte value of the param's default value. Freed with :c:func:`bfree()`.\n\n---------------------\n\n.. function:: size_t gs_effect_get_val_size(gs_eparam_t *param)\n\n   Returns the size in bytes of the param's current value.\n\n   :param param:   Effect parameter\n   :return:        The size in bytes of the param's current value.\n\n---------------------\n\n.. function:: size_t gs_effect_get_default_val_size(gs_eparam_t *param)\n\n   Returns the size in bytes of the param's default value.\n\n   :param param:   Effect parameter\n   :return:        The size in bytes of the param's default value.\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics-graphics.rst",
    "content": "Core Graphics API\n=================\n\n.. code:: cpp\n\n   #include <graphics/graphics.h>\n\n\nGraphics Enumerations\n---------------------\n\n.. enum:: gs_draw_mode\n\n   Draw mode.  Can be one of the following values:\n\n   - GS_POINTS    - Draws points\n   - GS_LINES     - Draws individual lines\n   - GS_LINESTRIP - Draws a line strip\n   - GS_TRIS      - Draws individual triangles\n   - GS_TRISTRIP  - Draws a triangle strip\n\n.. enum:: gs_color_format\n\n   Color format.  Can be one of the following values:\n\n   - GS_UNKNOWN     - Unknown format\n   - GS_A8          - 8 bit alpha channel only\n   - GS_R8          - 8 bit red channel only\n   - GS_RGBA        - RGBA, 8 bits per channel\n   - GS_BGRX        - BGRX, 8 bits per channel\n   - GS_BGRA        - BGRA, 8 bits per channel\n   - GS_R10G10B10A2 - RGBA, 10 bits per channel except alpha, which is 2\n     bits\n   - GS_RGBA16      - RGBA, 16 bits per channel\n   - GS_R16         - 16 bit red channel only\n   - GS_RGBA16F     - RGBA, 16 bit floating point per channel\n   - GS_RGBA32F     - RGBA, 32 bit floating point per channel\n   - GS_RG16F       - 16 bit floating point red and green channels only\n   - GS_RG32F       - 32 bit floating point red and green channels only\n   - GS_R16F        - 16 bit floating point red channel only\n   - GS_R32F        - 32 bit floating point red channel only\n   - GS_DXT1        - Compressed DXT1\n   - GS_DXT3        - Compressed DXT3\n   - GS_DXT5        - Compressed DXT5\n   - GS_RGBA_UNORM  - RGBA, 8 bits per channel, no SRGB aliasing\n   - GS_BGRX_UNORM  - BGRX, 8 bits per channel, no SRGB aliasing\n   - GS_BGRA_UNORM  - BGRA, 8 bits per channel, no SRGB aliasing\n   - GS_RG16        - RG, 16 bits per channel\n\n.. enum:: gs_color_space\n\n   Color space.  Can be one of the following values:\n\n   - GS_CS_SRGB         - sRGB\n   - GS_CS_SRGB_16F     - High-precision SDR\n   - GS_CS_709_EXTENDED - Canvas, Mac EDR (HDR)\n   - GS_CS_709_SCRGB    - 1.0 = 80 nits, Windows/Linux HDR\n\n.. enum:: gs_zstencil_format\n\n   Z-Stencil buffer format.  Can be one of the following values:\n\n   - GS_ZS_NONE    - No Z-stencil buffer\n   - GS_Z16        - 16 bit Z buffer\n   - GS_Z24_S8     - 24 bit Z buffer, 8 bit stencil\n   - GS_Z32F       - 32 bit floating point Z buffer\n   - GS_Z32F_S8X24 - 32 bit floating point Z buffer, 8 bit stencil\n\n.. enum:: gs_index_type\n\n   Index buffer type.  Can be one of the following values:\n\n   - GS_UNSIGNED_SHORT - 16 bit index\n   - GS_UNSIGNED_LONG  - 32 bit index\n\n.. enum:: gs_cull_mode\n\n   Cull mode.  Can be one of the following values:\n\n   - GS_BACK    - Cull back faces\n   - GS_FRONT   - Cull front faces\n   - GS_NEITHER - Cull neither\n\n.. enum:: gs_blend_type\n\n   Blend type.  Can be one of the following values:\n\n   - GS_BLEND_ZERO\n   - GS_BLEND_ONE\n   - GS_BLEND_SRCCOLOR\n   - GS_BLEND_INVSRCCOLOR\n   - GS_BLEND_SRCALPHA\n   - GS_BLEND_INVSRCALPHA\n   - GS_BLEND_DSTCOLOR\n   - GS_BLEND_INVDSTCOLOR\n   - GS_BLEND_DSTALPHA\n   - GS_BLEND_INVDSTALPHA\n   - GS_BLEND_SRCALPHASAT\n\n.. enum:: gs_depth_test\n\n   Depth test type.  Can be one of the following values:\n\n   - GS_NEVER\n   - GS_LESS\n   - GS_LEQUAL\n   - GS_EQUAL\n   - GS_GEQUAL\n   - GS_GREATER\n   - GS_NOTEQUAL\n   - GS_ALWAYS\n\n.. enum:: gs_stencil_side\n\n   Stencil side.  Can be one of the following values:\n\n   - GS_STENCIL_FRONT=1\n   - GS_STENCIL_BACK\n   - GS_STENCIL_BOTH\n\n.. enum:: gs_stencil_op_type\n\n   Stencil operation type.  Can be one of the following values:\n\n   - GS_KEEP\n   - GS_ZERO\n   - GS_REPLACE\n   - GS_INCR\n   - GS_DECR\n   - GS_INVERT\n\n.. enum:: gs_cube_sides\n\n   Cubemap side.  Can be one of the following values:\n\n   - GS_POSITIVE_X\n   - GS_NEGATIVE_X\n   - GS_POSITIVE_Y\n   - GS_NEGATIVE_Y\n   - GS_POSITIVE_Z\n   - GS_NEGATIVE_Z\n\n.. enum:: gs_sample_filter\n\n   Sample filter type.  Can be one of the following values:\n\n   - GS_FILTER_POINT\n   - GS_FILTER_LINEAR\n   - GS_FILTER_ANISOTROPIC\n   - GS_FILTER_MIN_MAG_POINT_MIP_LINEAR\n   - GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT\n   - GS_FILTER_MIN_POINT_MAG_MIP_LINEAR\n   - GS_FILTER_MIN_LINEAR_MAG_MIP_POINT\n   - GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR\n   - GS_FILTER_MIN_MAG_LINEAR_MIP_POINT\n\n.. enum:: gs_address_mode\n\n   Address mode.  Can be one of the following values:\n\n   - GS_ADDRESS_CLAMP\n   - GS_ADDRESS_WRAP\n   - GS_ADDRESS_MIRROR\n   - GS_ADDRESS_BORDER\n   - GS_ADDRESS_MIRRORONCE\n\n.. enum:: gs_texture_type\n\n   Texture type.  Can be one of the following values:\n\n   - GS_TEXTURE_2D\n   - GS_TEXTURE_3D\n   - GS_TEXTURE_CUBE\n\n\nGraphics Structures\n-------------------\n\n.. struct:: gs_monitor_info\n.. member:: int gs_monitor_info.rotation_degrees\n.. member:: long gs_monitor_info.x\n.. member:: long gs_monitor_info.y\n.. member:: long gs_monitor_info.cx\n.. member:: long gs_monitor_info.cy\n\n---------------------\n\n.. struct:: gs_tvertarray\n.. member:: size_t gs_tvertarray.width\n.. member:: void *gs_tvertarray.array\n\n---------------------\n\n.. struct:: gs_vb_data\n.. member:: size_t gs_vb_data.num\n.. member:: struct vec3 *gs_vb_data.points\n.. member:: struct vec3 *gs_vb_data.normals\n.. member:: struct vec3 *gs_vb_data.tangents\n.. member:: uint32_t *gs_vb_data.colors\n.. member:: size_t gs_vb_data.num_tex\n.. member:: struct gs_tvertarray *gs_vb_data.tvarray\n\n---------------------\n\n.. struct:: gs_sampler_info\n.. member:: enum gs_sample_filter gs_sampler_info.filter\n.. member:: enum gs_address_mode gs_sampler_info.address_u\n.. member:: enum gs_address_mode gs_sampler_info.address_v\n.. member:: enum gs_address_mode gs_sampler_info.address_w\n.. member:: int gs_sampler_info.max_anisotropy\n.. member:: uint32_t gs_sampler_info.border_color\n\n---------------------\n\n.. struct:: gs_display_mode\n.. member:: uint32_t gs_display_mode.width\n.. member:: uint32_t gs_display_mode.height\n.. member:: uint32_t gs_display_mode.bits\n.. member:: uint32_t gs_display_mode.freq\n\n---------------------\n\n.. struct:: gs_rect\n.. member:: int gs_rect.x\n.. member:: int gs_rect.y\n.. member:: int gs_rect.cx\n.. member:: int gs_rect.cy\n\n---------------------\n\n.. struct:: gs_window\n\n   A window structure.  This structure is used with a native widget.\n\n.. member:: void                    *gs_window.hwnd\n\n   (Windows only) an HWND widget.\n\n.. member:: id  gs_window.view\n\n   (macOS only) A view ID.\n\n.. member:: uint32_t gs_window.id\n            void* gs_window.display\n\n   (Linux only) Window ID and display\n\n---------------------\n\n.. struct:: gs_init_data\n\n   Swap chain initialization data.\n\n.. member:: struct gs_window        gs_init_data.window\n.. member:: uint32_t                gs_init_data.cx\n.. member:: uint32_t                gs_init_data.cy\n.. member:: uint32_t                gs_init_data.num_backbuffers\n.. member:: enum gs_color_format    gs_init_data.format\n.. member:: enum gs_zstencil_format gs_init_data.zsformat\n.. member:: uint32_t                gs_init_data.adapter\n\n---------------------\n\n\nInitialization Functions\n------------------------\n\n.. function:: void gs_enum_adapters(bool (*callback)(void *param, const char *name, uint32_t id), void *param)\n\n   Enumerates adapters (this really only applies on Windows).\n\n   :param callback: Enumeration callback\n   :param param:    Private data passed to the callback\n\n---------------------\n\n.. function:: int gs_create(graphics_t **graphics, const char *module, uint32_t adapter)\n\n   Creates a graphics context\n\n   :param graphics: Pointer to receive the graphics context\n   :param module:   Module name\n   :param adapter:  Adapter index\n   :return:         Can return one of the following values:\n\n                    - GS_SUCCESS\n                    - GS_ERROR_FAIL\n                    - GS_ERROR_MODULE_NOT_FOUND\n                    - GS_ERROR_NOT_SUPPORTED\n\n---------------------\n\n.. function:: void gs_destroy(graphics_t *graphics)\n\n   Destroys a graphics context\n\n   :param graphics: Graphics context\n\n---------------------\n\n.. function:: void gs_enter_context(graphics_t *graphics)\n\n   Enters and locks the graphics context\n\n   :param graphics: Graphics context\n\n---------------------\n\n.. function:: void gs_leave_context(void)\n\n   Leaves and unlocks the graphics context\n\n   :param graphics: Graphics context\n\n---------------------\n\n.. function:: graphics_t *gs_get_context(void)\n\n   :return: The currently locked graphics context for this thread\n\n---------------------\n\n\nMatrix Stack Functions\n----------------------\n\n.. function:: void gs_matrix_push(void)\n\n   Pushes the matrix stack and duplicates the current matrix.\n\n---------------------\n\n.. function:: void gs_matrix_pop(void)\n\n   Pops the current matrix from the matrix stack.\n\n---------------------\n\n.. function:: void gs_matrix_identity(void)\n\n   Sets the current matrix to an identity matrix.\n\n---------------------\n\n.. function:: void gs_matrix_transpose(void)\n\n   Transposes the current matrix.\n\n---------------------\n\n.. function:: void gs_matrix_set(const struct matrix4 *matrix)\n\n   Sets the current matrix.\n\n   :param matrix: The matrix to set\n\n---------------------\n\n.. function:: void gs_matrix_get(struct matrix4 *dst)\n\n   Gets the current matrix\n\n   :param dst: Destination matrix\n\n---------------------\n\n.. function:: void gs_matrix_mul(const struct matrix4 *matrix)\n\n   Multiplies the current matrix\n\n   :param matrix: Matrix to multiply the current stack matrix with\n\n---------------------\n\n.. function:: void gs_matrix_rotquat(const struct quat *rot)\n\n   Multiplies the current matrix with a quaternion\n\n   :param rot: Quaternion to multiple the current matrix stack with\n\n---------------------\n\n.. function:: void gs_matrix_rotaa(const struct axisang *rot)\n              void gs_matrix_rotaa4f(float x, float y, float z, float angle)\n\n   Multiplies the current matrix with an axis angle\n\n   :param rot: Axis angle to multiple the current matrix stack with\n\n---------------------\n\n.. function:: void gs_matrix_translate(const struct vec3 *pos)\n              void gs_matrix_translate3f(float x, float y, float z)\n\n   Translates the current matrix\n\n   :param pos: Vector to translate the current matrix stack with\n\n---------------------\n\n.. function:: void gs_matrix_scale(const struct vec3 *scale)\n              void gs_matrix_scale3f(float x, float y, float z)\n\n   Scales the current matrix\n\n   :param scale: Scale value to scale the current matrix stack with\n\n---------------------\n\n\nDraw Functions\n--------------\n\n.. function:: gs_effect_t *gs_get_effect(void)\n\n   :return: The currently active effect, or *NULL* if none active\n\n---------------------\n\n.. function:: void gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width, uint32_t height)\n\n   Draws a 2D sprite.  Sets the \"image\" parameter of the current effect\n   to the texture and renders a quad. Can be omitted if drawing without\n   a texture.\n\n   If width or height is 0, the width or height of the texture will be\n   used.  The flip value specifies whether the texture should be flipped\n   on the U or V axis with GS_FLIP_U and GS_FLIP_V.\n\n   :param tex:    Texture to draw (can be NULL if drawing without a\n                  texture)\n   :param flip:   Can be 0 or a bitwise-OR combination of one of the\n                  following values:\n\n                  - GS_FLIP_U - Flips the texture horizontally\n                  - GS_FLIP_V - Flips the texture vertically\n\n   :param width:  Width\n   :param height: Height\n\n---------------------\n\n.. function:: void gs_draw_quadf(gs_texture_t *tex, uint32_t flip, float width, float height)\n\n   Same as :c:func:`gs_draw_sprite()`, but with floating point width/height\n   values instead of integer width/height values.\n\n   :param tex:    Texture to draw (can be NULL if drawing without a\n                  texture)\n   :param flip:   Can be 0 or a bitwise-OR combination of one of the\n                  following values:\n\n                  - GS_FLIP_U - Flips the texture horizontally\n                  - GS_FLIP_V - Flips the texture vertically\n\n   :param width:  Width\n   :param height: Height\n\n---------------------\n\n.. function:: void gs_draw_sprite_subregion(gs_texture_t *tex, uint32_t flip, uint32_t x, uint32_t y, uint32_t cx, uint32_t cy)\n\n   Draws a subregion of a 2D sprite.  Sets the \"image\" parameter of the\n   current effect to the texture and renders a quad.\n\n   :param tex:    Texture to draw\n   :param flip:   Can be 0 or a bitwise-OR combination of one of the\n                  following values:\n\n                  - GS_FLIP_U - Flips the texture horizontally\n                  - GS_FLIP_V - Flips the texture vertically\n\n   :param x:      X value within subregion\n   :param y:      Y value within subregion\n   :param cx:     CX value of subregion\n   :param cy:     CY value of subregion\n\n---------------------\n\n.. function:: void gs_reset_viewport(void)\n\n    Sets the viewport to current swap chain size\n\n---------------------\n\n.. function:: void gs_set_2d_mode(void)\n\n    Sets the projection matrix to a default screen-sized orthographic\n    mode\n\n---------------------\n\n.. function:: void gs_set_3d_mode(double fovy, double znear, double zfar)\n\n    Sets the projection matrix to a default screen-sized perspective\n    mode\n\n    :param fovy:  Field of view (in degrees)\n    :param znear: Near plane\n    :param zfar:  Far plane\n\n---------------------\n\n.. function:: void gs_viewport_push(void)\n\n   Pushes/stores the current viewport\n\n---------------------\n\n.. function:: void gs_viewport_pop(void)\n\n   Pops/recalls the last pushed viewport\n\n---------------------\n\n.. function:: void gs_perspective(float fovy, float aspect, float znear, float zfar)\n\n   Sets the projection matrix to a perspective mode\n\n   :param fovy:   Field of view (in degrees)\n   :param aspect: Aspect ratio\n   :param znear:  Near plane\n   :param zfar:   Far plane\n\n---------------------\n\n.. function:: void gs_blend_state_push(void)\n\n   Pushes/stores the current blend state\n\n---------------------\n\n.. function:: void gs_blend_state_pop(void)\n\n   Pops/restores the last blend state\n\n---------------------\n\n.. function:: void gs_reset_blend_state(void)\n\n   Sets the blend state to the default value: source alpha and invert\n   source alpha.\n\n---------------------\n\n\nSwap Chains\n-----------\n\n.. function:: gs_swapchain_t *gs_swapchain_create(const struct gs_init_data *data)\n\n   Creates a swap chain (display view on a native widget)\n\n   :param data: Swap chain initialization data\n   :return:     New swap chain object, or *NULL* if failed\n\n---------------------\n\n.. function:: void     gs_swapchain_destroy(gs_swapchain_t *swapchain)\n\n   Destroys a swap chain\n\n---------------------\n\n.. function:: void gs_resize(uint32_t cx, uint32_t cy)\n\n   Resizes the currently active swap chain\n\n   :param cx: New width\n   :param cy: New height\n\n---------------------\n\n.. function:: void gs_update_color_space(void)\n\n   Updates the color space of the swap chain based on the HDR status of\n   the nearest monitor\n\n---------------------\n\n.. function:: void gs_get_size(uint32_t *cx, uint32_t *cy)\n\n   Gets the size of the currently active swap chain\n\n   :param cx: Pointer to receive width\n   :param cy: Pointer to receive height\n\n---------------------\n\n.. function:: uint32_t gs_get_width(void)\n\n   Gets the width of the currently active swap chain\n\n---------------------\n\n.. function:: uint32_t gs_get_height(void)\n\n   Gets the height of the currently active swap chain\n\n---------------------\n\n\nResource Loading\n----------------\n\n.. function:: void gs_load_vertexbuffer(gs_vertbuffer_t *vertbuffer)\n\n   Loads a vertex buffer\n\n   :param vertbuffer: Vertex buffer to load, or NULL to unload\n\n---------------------\n\n.. function:: void gs_load_indexbuffer(gs_indexbuffer_t *indexbuffer)\n\n   Loads a index buffer\n\n   :param indexbuffer: Index buffer to load, or NULL to unload\n\n---------------------\n\n.. function:: void gs_load_texture(gs_texture_t *tex, int unit)\n\n   Loads a texture (this is usually not called manually)\n\n   :param tex:  Texture to load, or NULL to unload\n   :param unit: Texture unit to load texture for\n\n---------------------\n\n.. function:: void gs_load_samplerstate(gs_samplerstate_t *samplerstate, int unit)\n\n   Loads a sampler state (this is usually not called manually)\n\n   :param samplerstate: Sampler state to load, or NULL to unload\n   :param unit:         Texture unit to load sampler state for\n\n---------------------\n\n.. function:: void gs_load_swapchain(gs_swapchain_t *swapchain)\n\n   Loads a swapchain\n\n   :param swapchain: Swap chain to load, or NULL to unload\n\n---------------------\n\n\nDraw Functions\n--------------\n\n.. function:: enum gs_color_space gs_get_color_space(void)\n\n   :return: The currently active color space\n\n---------------------\n\n.. function:: gs_texture_t  *gs_get_render_target(void)\n\n   :return: The currently active render target\n\n---------------------\n\n.. function:: gs_zstencil_t *gs_get_zstencil_target(void)\n\n   :return: The currently active Z-stencil target\n\n---------------------\n\n.. function:: void gs_set_render_target(gs_texture_t *tex, gs_zstencil_t *zstencil)\n\n   Sets the active render target with implicit GS_CS_SRGB color space\n\n   :param tex:      Texture to set as the active render target\n   :param zstencil: Z-stencil to use as the active render target\n\n---------------------\n\n.. function:: void gs_set_render_target_with_color_space(gs_texture_t *tex, gs_zstencil_t *zstencil, enum gs_color_space space)\n\n   Sets the active render target along with color space\n\n   :param tex:      Texture to set as the active render target\n   :param zstencil: Z-stencil to use as the active render target\n   :param space:    Color space of the render target\n\n---------------------\n\n.. function:: void gs_set_cube_render_target(gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil)\n\n   Sets a cubemap side as the active render target\n\n   :param cubetex:  Cubemap\n   :param side:     Cubemap side\n   :param zstencil: Z-stencil buffer, or *NULL* if none\n\n---------------------\n\n.. function:: void gs_copy_texture(gs_texture_t *dst, gs_texture_t *src)\n\n   Copies a texture\n\n   :param dst: Destination texture\n   :param src: Source texture\n\n---------------------\n\n.. function:: void gs_stage_texture(gs_stagesurf_t *dst, gs_texture_t *src)\n\n   Copies a texture to a staging surface and copies it to RAM.  Ideally\n   best to give this a frame to process to prevent stalling.\n\n   :param dst: Staging surface\n   :param src: Texture to stage\n\n---------------------\n\n.. function:: void gs_begin_scene(void)\n              void gs_end_scene(void)\n\n   Begins/ends a scene (this is automatically called by libobs, there's\n   no need to call this manually).\n\n---------------------\n\n.. function:: void gs_draw(enum gs_draw_mode draw_mode, uint32_t start_vert, uint32_t num_verts)\n\n   Draws a primitive or set of primitives.\n\n   :param draw_mode:  The primitive draw mode to use\n   :param start_vert: Starting vertex index\n   :param num_verts:  Number of vertices\n\n---------------------\n\n.. function:: void gs_clear(uint32_t clear_flags, const struct vec4 *color, float depth, uint8_t stencil)\n\n   Clears color/depth/stencil buffers.\n\n   :param clear_flags: Flags to clear with.  Can be one of the following\n                       values:\n\n                       - GS_CLEAR_COLOR   - Clears color buffer\n                       - GS_CLEAR_DEPTH   - Clears depth buffer\n                       - GS_CLEAR_STENCIL - Clears stencil buffer\n\n   :param color:       Color value to clear the color buffer with\n   :param depth:       Depth value to clear the depth buffer with\n   :param stencil:     Stencil value to clear the stencil buffer with\n\n---------------------\n\n.. function:: void gs_present(void)\n\n   Displays what was rendered on to the current render target\n\n---------------------\n\n.. function:: void gs_flush(void)\n\n   Flushes GPU calls\n\n---------------------\n\n.. function:: void gs_set_cull_mode(enum gs_cull_mode mode)\n\n   Sets the current cull mode.\n\n   :param mode: Cull mode\n\n---------------------\n\n.. function:: enum gs_cull_mode gs_get_cull_mode(void)\n\n   :return: The current cull mode\n\n---------------------\n\n.. function:: void gs_enable_blending(bool enable)\n\n   Enables/disables blending\n\n   :param enable: *true* to enable, *false* to disable\n\n---------------------\n\n.. function:: void gs_enable_depth_test(bool enable)\n\n   Enables/disables depth testing\n\n   :param enable: *true* to enable, *false* to disable\n\n---------------------\n\n.. function:: void gs_enable_stencil_test(bool enable)\n\n   Enables/disables stencil testing\n\n   :param enable: *true* to enable, *false* to disable\n\n---------------------\n\n.. function:: void gs_enable_stencil_write(bool enable)\n\n   Enables/disables stencil writing\n\n   :param enable: *true* to enable, *false* to disable\n\n---------------------\n\n.. function:: void gs_enable_color(bool red, bool green, bool blue, bool alpha)\n\n   Enables/disables specific color channels\n\n   :param red:   *true* to enable red channel, *false* to disable\n   :param green: *true* to enable green channel, *false* to disable\n   :param blue:  *true* to enable blue channel, *false* to disable\n   :param alpha: *true* to enable alpha channel, *false* to disable\n\n---------------------\n\n.. function:: void gs_blend_function(enum gs_blend_type src, enum gs_blend_type dest)\n\n   Sets the blend function's source and destination factors\n\n   :param src:  Blend type for the blending equation's source factors\n   :param dest: Blend type for the blending equation's destination factors\n\n---------------------\n\n.. function:: void gs_blend_function_separate(enum gs_blend_type src_c, enum gs_blend_type dest_c, enum gs_blend_type src_a, enum gs_blend_type dest_a)\n\n   Sets the blend function's source and destination factors for RGB and alpha separately\n\n   :param src_c:  Blend type for the blending equation's source RGB factor\n   :param dest_c: Blend type for the blending equation's destination RGB factor\n   :param src_a:  Blend type for the blending equation's source alpha factor\n   :param dest_a: Blend type for the blending equation's destination alpha factor\n\n---------------------\n\n.. function:: void gs_blend_op(enum gs_blend_op_type op)\n\n   Sets the blend function's operation type\n\n   :param op: Operation type for the blending equation\n\n---------------------\n\n.. function:: void gs_depth_function(enum gs_depth_test test)\n\n   Sets the depth function\n\n   :param test: Sets the depth test type\n\n---------------------\n\n.. function:: void gs_stencil_function(enum gs_stencil_side side, enum gs_depth_test test)\n\n   Sets the stencil function\n\n   :param side: Stencil side\n   :param test: Depth test\n\n---------------------\n\n.. function:: void gs_stencil_op(enum gs_stencil_side side, enum gs_stencil_op_type fail, enum gs_stencil_op_type zfail, enum gs_stencil_op_type zpass)\n\n   Sets the stencil operation\n\n   :param side:  Stencil side\n   :param fail:  Operation to perform on stencil test failure\n   :param zfail: Operation to perform on depth test failure\n   :param zpass: Operation to perform on depth test success\n\n---------------------\n\n.. function:: void gs_set_viewport(int x, int y, int width, int height)\n\n   Sets the current viewport\n\n   :param x:      X position relative to upper left\n   :param y:      Y position relative to upper left\n   :param width:  Width of the viewport\n   :param height: Height of the viewport\n\n---------------------\n\n.. function:: void gs_get_viewport(struct gs_rect *rect)\n\n   Gets the current viewport\n\n   :param rect: Pointer to receive viewport rectangle\n\n---------------------\n\n.. function:: void gs_set_scissor_rect(const struct gs_rect *rect)\n\n   Sets or clears the current scissor rectangle\n\n   :rect: Scissor rectangle, or *NULL* to clear\n\n---------------------\n\n.. function:: void gs_ortho(float left, float right, float top, float bottom, float znear, float zfar)\n\n   Sets the projection matrix to an orthographic matrix\n\n---------------------\n\n.. function:: void gs_frustum(float left, float right, float top, float bottom, float znear, float zfar)\n\n   Sets the projection matrix to a frustum matrix\n\n---------------------\n\n.. function:: void gs_projection_push(void)\n\n   Pushes/stores the current projection matrix\n\n---------------------\n\n.. function:: void gs_projection_pop(void)\n\n   Pops/restores the last projection matrix pushed\n\n---------------------\n\n\nTexture Functions\n-----------------\n\n.. function:: gs_texture_t *gs_texture_create(uint32_t width, uint32_t height, enum gs_color_format color_format, uint32_t levels, const uint8_t **data, uint32_t flags)\n\n   Creates a texture.\n\n   :param width:        Width\n   :param height:       Height\n   :param color_format: Color format\n   :param levels:       Number of total texture levels.  Set to 1 if no\n                        mip-mapping\n   :param data:         Pointer to array of texture data pointers\n   :param flags:        Can be 0 or a bitwise-OR combination of one or\n                        more of the following value:\n\n                        - GS_BUILD_MIPMAPS - Automatically builds\n                          mipmaps (Note: not fully tested)\n                        - GS_DYNAMIC - Dynamic\n                        - GS_RENDER_TARGET - Render target\n\n   :return:             A new texture object\n\n---------------------\n\n.. function:: gs_texture_t *gs_texture_create_from_file(const char *file)\n\n   Creates a texture from a file.  Note that this isn't recommended for\n   animated gifs -- instead use the :ref:`image_file_helper`.\n\n   :param file: Image file to open\n\n---------------------\n\n.. function:: void     gs_texture_destroy(gs_texture_t *tex)\n\n   Destroys a texture\n\n   :param tex: Texture object\n\n---------------------\n\n.. function:: uint32_t gs_texture_get_width(const gs_texture_t *tex)\n\n   Gets the texture's width\n\n   :param tex: Texture object\n   :return:    The texture's width\n\n---------------------\n\n.. function:: uint32_t gs_texture_get_height(const gs_texture_t *tex)\n\n   Gets the texture's height\n\n   :param tex: Texture object\n   :return:    The texture's height\n\n---------------------\n\n.. function:: enum gs_color_format gs_texture_get_color_format(const gs_texture_t *tex)\n\n   Gets the texture's color format\n\n   :param tex: Texture object\n   :return:    The texture's color format\n\n---------------------\n\n.. function:: bool     gs_texture_map(gs_texture_t *tex, uint8_t **ptr, uint32_t *linesize)\n\n   Maps a texture.\n\n   :param tex:      Texture object\n   :param ptr:      Pointer to receive the pointer to the texture data\n                    to write to\n   :param linesize: Pointer to receive the line size (pitch) of the\n                    texture\n\n---------------------\n\n.. function:: void     gs_texture_unmap(gs_texture_t *tex)\n\n   Unmaps a texture.\n\n   :param tex: Texture object\n\n---------------------\n\n.. function:: void gs_texture_set_image(gs_texture_t *tex, const uint8_t *data, uint32_t linesize, bool invert)\n\n   Sets the image of a dynamic texture\n\n   :param tex:      Texture object\n   :param data:     Data to set as the image\n   :param linesize: Line size (pitch) of the data\n   :param invert:   *true* to invert vertically, *false* otherwise\n\n---------------------\n\n.. function:: gs_texture_t *gs_texture_create_from_dmabuf(unsigned int width, unsigned int height, uint32_t drm_format, enum gs_color_format color_format, uint32_t n_planes, const int *fds, const uint32_t *strides, const uint32_t *offsets, const uint64_t *modifiers)\n\n   **only Linux, FreeBSD, DragonFly:** Creates a texture from DMA-BUF metadata.\n\n   Exchanging DMA-BUFs is a verbose process because of its multiplanar nature.\n   For example, YUV can have each plane as a color channel, or a monitor buffer\n   can have the cursor stored in a separate plane.\n\n   This function treats the OBS Studio format and the DRM format separately.\n   This allows creating textures from DMA-BUFs with unsupported formats (e.g.\n   YUV) and perform the color format conversion using shaders. However, be\n   careful to always try and match the formats correctly, otherwise textures\n   can fail to be created or rendered.\n\n   All modifiers passed in the modifiers array must be equal. Passing different\n   modifiers for each plane is unsupported.\n\n   :param width:        Width of the texture\n   :param height:       Height of the texture\n   :param drm_format:   DRM format of the DMA-BUF buffer\n   :param color_format: Color format compatible with OBS Studio\n   :param n_planes:     Number of planes of the DMA-BUF\n   :param fds:          Array of size *n_planes* with the file descriptor of each plane\n   :param strides:      Array of size *n_planes* with the stride of each plane\n   :param offsets:      Array of size *n_planes* with the offset of each plane\n   :param modifiers:    Array of size *n_planes* with the modifier of each plane\n   :return:             A texture object on success, or *NULL* on failure\n   :rtype:              gs_texture_t*\n\n---------------------\n\n.. enum:: gs_dmabuf_flags\n\n   DMA-BUF capabilities:\n\n   - GS_DMABUF_FLAG_NONE\n   - GS_DMABUF_FLAG_SUPPORTS_IMPLICIT_MODIFIERS  - Renderer supports implicit modifiers\n\n---------------------\n\n.. function:: bool *gs_query_dmabuf_capabilities(enum gs_dmabuf_flags *dmabuf_flags, uint32_t **drm_formats, size_t *n_formats)\n\n   **only Linux, FreeBSD, DragonFly:** Queries the capabilities for DMA-BUFs.\n\n   Graphics cards can optimize frame buffers by storing them in custom layouts,\n   depending on their hardware features. These layouts can make these frame\n   buffers unsuitable for linear processing. This function allows querying whether\n   the graphics card in use supports implicit modifiers, and the supported texture\n   formats.\n\n   The caller must free the `drm_formats` array with `bfree()` after use.\n\n   :param dmabuf_flags: Pointer to receive a capability bitmap\n   :param drm_formats:  Pointer to receive an array of DRM formats\n   :param n_formats:    Pointer to receive the number of formats\n   :rtype:              bool\n\n---------------------\n\n.. function:: bool *gs_query_dmabuf_modifiers_for_format(uint32_t drm_format, uint64_t **modifiers, size_t *n_modifiers)\n\n   **only Linux, FreeBSD, DragonFly:** Queries the supported DMA-BUF modifiers for a given format.\n\n   This function queries all supported explicit modifiers for a format,\n   stores them as an array and returns the number of supported modifiers.\n\n   The caller must free the `modifiers` array with `bfree()` after use.\n\n   :param drm_format:   DRM format of the DMA-BUF buffer\n   :param modifiers:    Pointer to receive an array of modifiers\n   :param n_modifiers:  Pointer to receive the number of modifiers\n   :rtype:              bool\n\n---------------------\n\n.. function:: bool gs_query_sync_capabilities(void)\n\n   **only Linux, FreeBSD, DragonFly:** Checks if the use of synchronization objects is supported\n\n   :rtype:  bool\n   :return: *true* if supported, *false* otherwise\n\n---------------------\n\n.. function:: gs_sync_t *gs_sync_create(void)\n\n   **only Linux, FreeBSD, DragonFly:** Creates a synchronization object\n\n   Inserts a fence command into the command stream of the bound context and\n   creates a synchronization object that is signalled when all the commands\n   preceding the inserted fence are executed.\n\n   :rtype: gs_sync_t*\n\n---------------------\n\n.. function:: gs_sync_t *gs_sync_create_from_syncobj_timeline_point(int syncobj_fd, uint64_t timeline_point)\n\n   **only Linux, FreeBSD, DragonFly:** Creates a synchronization object\n\n   Creates a synchronization object that is signalled when the specified\n   timeline point of the given DRM syncobj is signalled.\n\n   :param syncobj_fd:     A file descriptor that is referencing a DRM syncobj\n   :param timeline_point: Timeline point of the DRM syncobj\n   :rtype:                gs_sync_t*\n\n---------------------\n\n.. function:: void gs_sync_destroy(gs_sync_t *sync)\n\n   **only Linux, FreeBSD, DragonFly:** Destroys a synchronization object\n\n   If the given synchronization object is in use, it will be marked for\n   destruction and will be destroyed later when it is no longer referenced.\n\n   :param sync: Synchronization object\n\n---------------------\n\n.. function:: bool gs_sync_export_syncobj_timeline_point(gs_sync_t *sync, int syncobj_fd, uint64_t timeline_point)\n\n   **only Linux, FreeBSD, DragonFly:** Exports a synchronization object to a DRM syncobj timeline point\n\n   Creates a DRM syncobj timeline point that is signalled when the given\n   synchronization object is signalled.\n\n   :param sync:           Synchronization object\n   :param syncobj_fd:     A file descriptor that is referencing a DRM syncobj\n   :param timeline_point: Timeline point of the DRM syncobj\n   :rtype:                bool\n   :return:               *true* if the synchronization object is exported successfully, *false* otherwise\n\n---------------------\n\n.. function:: bool gs_sync_signal_syncobj_timeline_point(int syncobj_fd, uint64_t timeline_point)\n\n   **only Linux, FreeBSD, DragonFly:** Signals a DRM syncobj timeline point\n\n   :param syncobj_fd:     A file descriptor that is referencing a DRM syncobj\n   :param timeline_point: Timeline point of the DRM syncobj\n   :rtype:                bool\n   :return:               *true* if the timeline point is signalled successfully, *false* otherwise\n\n---------------------\n\n.. function:: bool gs_sync_wait(gs_sync_t *sync)\n\n   **only Linux, FreeBSD, DragonFly:** Wait for a synchronization object to be signalled\n\n   Blocks the execution of the commands that would be inserted into the command\n   stream of the bound context until the given synchronization object is\n   signalled.\n\n   :param sync: Synchronization object\n   :rtype:      bool\n   :return:     *true* if successful, *false* otherwise\n\n---------------------\n\n.. function:: gs_texture_t *gs_texture_create_from_iosurface(void *iosurf)\n\n   **macOS only:** Creates a texture from an IOSurface.\n\n   :param iosurf: IOSurface object\n\n---------------------\n\n.. function:: bool     gs_texture_rebind_iosurface(gs_texture_t *texture, void *iosurf)\n\n   **macOS only:** Rebinds a texture to another IOSurface\n\n   :param texture: Texture object\n   :param iosuf:   IOSurface object\n\n---------------------\n\n.. function:: gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height)\n\n   **Windows only:** Creates a GDI-interop texture\n\n   :param width:  Width\n   :param height: Height\n\n---------------------\n\n.. function:: void *gs_texture_get_dc(gs_texture_t *gdi_tex)\n\n   **Windows only:** Gets the HDC of a GDI-interop texture.  Call\n   :c:func:`gs_texture_release_dc()` to release the HDC.\n\n   :param gdi_tex: GDI-interop texture object\n   :return:        HDC object\n\n---------------------\n\n.. function:: void gs_texture_release_dc(gs_texture_t *gdi_tex)\n\n   **Windows only:** Releases the HDC of the GDI-interop texture.\n\n   :param gdi_tex: GDI-interop texture object\n\n---------------------\n\n.. function:: gs_texture_t *gs_texture_open_shared(uint32_t handle)\n\n   **Windows only:** Creates a texture from a shared texture handle.\n\n   :param handle: Shared texture handle\n   :return:       A texture object\n\n---------------------\n\n.. function:: bool gs_gdi_texture_available(void)\n\n   **Windows only:** Returns whether GDI-interop textures are available.\n\n   :return: *true* if available, *false* otherwise\n\n---------------------\n\n.. function:: bool gs_shared_texture_available(void)\n\n   **Windows only:** Returns whether shared textures are available.\n\n   :return: *true* if available, *false* otherwise\n\n---------------------\n\n\nCube Texture Functions\n----------------------\n\n.. function:: gs_texture_t *gs_cubetexture_create(uint32_t size, enum gs_color_format color_format, uint32_t levels, const uint8_t **data, uint32_t flags)\n\n   Creates a cubemap texture.\n\n   :param size:         Width/height/depth value\n   :param color_format: Color format\n   :param levels:       Number of texture levels\n   :param data:         Pointer to array of texture data pointers\n   :param flags:        Can be 0 or a bitwise-OR combination of one or\n                        more of the following value:\n\n                        - GS_BUILD_MIPMAPS - Automatically builds\n                          mipmaps (Note: not fully tested)\n                        - GS_DYNAMIC - Dynamic\n                        - GS_RENDER_TARGET - Render target\n\n   :return:             A new cube texture object\n\n---------------------\n\n.. function:: void     gs_cubetexture_destroy(gs_texture_t *cubetex)\n\n   Destroys a cube texture.\n\n   :param cubetex: Cube texture object\n\n---------------------\n\n.. function:: uint32_t gs_cubetexture_get_size(const gs_texture_t *cubetex)\n\n   Get the width/height/depth value of a cube texture.\n\n   :param cubetex: Cube texture object\n   :return:        The width/height/depth value of the cube texture\n\n---------------------\n\n.. function:: enum gs_color_format gs_cubetexture_get_color_format(const gs_texture_t *cubetex)\n\n   Gets the color format of a cube texture.\n\n   :param cubetex: Cube texture object\n   :return:        The color format of the cube texture\n\n---------------------\n\n.. function:: void gs_cubetexture_set_image(gs_texture_t *cubetex, uint32_t side, const void *data, uint32_t linesize, bool invert)\n\n   Sets an image of a cube texture side.\n\n   :param cubetex:  Cube texture object\n   :param side:     Side\n   :param data:     Texture data to set\n   :param linesize: Line size (pitch) of the texture data\n   :param invert:   *true* to invert texture data, *false* otherwise\n\n---------------------\n\n\nStaging Surface Functions\n-------------------------\n\nStaging surfaces are used to efficiently copy textures from VRAM to RAM.\n\n.. function:: gs_stagesurf_t *gs_stagesurface_create(uint32_t width, uint32_t height, enum gs_color_format color_format)\n\n   Creates a staging surface.\n\n   :param width:        Width\n   :param height:       Height\n   :param color_format: Color format\n   :return:             The staging surface object\n\n---------------------\n\n.. function:: void     gs_stagesurface_destroy(gs_stagesurf_t *stagesurf)\n\n   Destroys a staging surface.\n\n   :param stagesurf: Staging surface object\n\n---------------------\n\n.. function:: uint32_t gs_stagesurface_get_width(const gs_stagesurf_t *stagesurf)\n              uint32_t gs_stagesurface_get_height(const gs_stagesurf_t *stagesurf)\n\n   Gets the width/height of a staging surface object.\n\n   :param stagesurf: Staging surface object\n   :return:          Width/height of the staging surface\n\n---------------------\n\n.. function:: enum gs_color_format gs_stagesurface_get_color_format(const gs_stagesurf_t *stagesurf)\n\n   Gets the color format of a staging surface object.\n\n   :param stagesurf: Staging surface object\n   :return:          Color format of the staging surface\n\n---------------------\n\n.. function:: bool     gs_stagesurface_map(gs_stagesurf_t *stagesurf, uint8_t **data, uint32_t *linesize)\n\n   Maps the staging surface texture (for reading).  Call\n   :c:func:`gs_stagesurface_unmap()` to unmap when complete.\n\n   :param stagesurf: Staging surface object\n   :param data:      Pointer to receive texture data pointer\n   :param linesize:  Pointer to receive line size (pitch) of the texture\n                     data\n   :return:          *true* if map successful, *false* otherwise\n\n---------------------\n\n.. function:: void     gs_stagesurface_unmap(gs_stagesurf_t *stagesurf)\n\n   Unmaps a staging surface.\n\n   :param stagesurf: Staging surface object\n\n---------------------\n\n\nZ-Stencil Functions\n-------------------\n\n.. function:: gs_zstencil_t *gs_zstencil_create(uint32_t width, uint32_t height, enum gs_zstencil_format format)\n\n   Creates a Z-stencil surface object.\n\n   :param width:  Width\n   :param height: Height\n   :param format: Format\n   :return:       New Z-stencil surface object, or *NULL* if failed\n\n---------------------\n\n.. function:: void     gs_zstencil_destroy(gs_zstencil_t *zstencil)\n\n   Destroys a Z-stencil buffer.\n\n   :param zstencil: Z-stencil surface object\n\n---------------------\n\n\nSampler State Functions\n-----------------------\n\n.. function:: gs_samplerstate_t *gs_samplerstate_create(const struct gs_sampler_info *info)\n\n   Creates a sampler state object.\n\n   :param info: Sampler state information\n   :return:     New sampler state object\n\n---------------------\n\n.. function:: void     gs_samplerstate_destroy(gs_samplerstate_t *samplerstate)\n\n   Destroys a sampler state object.\n\n   :param samplerstate: Sampler state object\n\n---------------------\n\n\nVertex Buffer Functions\n-----------------------\n\n.. function:: gs_vertbuffer_t *gs_vertexbuffer_create(struct gs_vb_data *data, uint32_t flags)\n\n   Creates a vertex buffer.\n\n   :param data:  Vertex buffer data to create vertex buffer with.  The\n                 structure should be created with gs_vbdata_create(),\n                 and then buffers in this structure should be allocated\n                 with :c:func:`bmalloc()`, :c:func:`bzalloc()`, or\n                 :c:func:`brealloc()`.  The ownership of the gs_vb_data\n                 pointer is then passed to the function, and they should\n                 not be destroyed by the caller once passed\n\n   :param flags: Creation flags.  Can be 0 or a bitwise-OR combination\n                 of any of the following values:\n\n                 - GS_DYNAMIC - Can be dynamically updated in real time.\n                 - GS_DUP_BUFFER - Do not pass buffer ownership of the\n                   structure or the buffer pointers within the\n                   structure.\n\n   :return:      A new vertex buffer object, or *NULL* if failed\n\n---------------------\n\n.. function:: void     gs_vertexbuffer_destroy(gs_vertbuffer_t *vertbuffer)\n\n   Destroys a vertex buffer object.\n\n   :param vertbuffer: Vertex buffer object\n\n---------------------\n\n.. function:: void     gs_vertexbuffer_flush(gs_vertbuffer_t *vertbuffer)\n\n   Flushes a vertex buffer to its interval vertex data object.  To\n   modify its internal vertex data, call\n   :c:func:`gs_vertexbuffer_get_data()`.\n\n   Can only be used with dynamic vertex buffer objects.\n\n   :param vertbuffer: Vertex buffer object\n\n---------------------\n\n.. function:: void     gs_vertexbuffer_flush_direct(gs_vertbuffer_t *vertbuffer, const struct gs_vb_data *data)\n\n   Directly flushes a vertex buffer to the specified vertex buffer data.\n   .\n\n   Can only be used with dynamic vertex buffer objects.\n\n   :param vertbuffer: Vertex buffer object\n   :param data:       Vertex buffer data to flush.  Components that\n                      don't need to be flushed can be left *NULL*\n\n---------------------\n\n.. function:: struct gs_vb_data *gs_vertexbuffer_get_data(const gs_vertbuffer_t *vertbuffer)\n\n   Gets the vertex buffer data associated with a vertex buffer object.\n   This data can be changed and vertex buffer can be updated with\n   :c:func:`gs_vertexbuffer_flush()`.\n\n   Can only be used with dynamic vertex buffer objects.\n\n   :param vertbuffer: Vertex buffer object\n   :return:           Vertex buffer data structure\n\n---------------------\n\n\nIndex Buffer Functions\n----------------------\n\n.. function:: gs_indexbuffer_t *gs_indexbuffer_create(enum gs_index_type type, void *indices, size_t num, uint32_t flags)\n\n   Creates an index buffer.\n\n   :param type:    Index buffer type\n   :param indices: Index buffer data.  This buffer must be allocated\n                   with :c:func:`bmalloc()`, :c:func:`bzalloc()`, or\n                   :c:func:`bralloc()`, and ownership of this buffer is\n                   passed to the index buffer object.\n   :param num:     Number of indices in the buffer\n\n   :param flags:   Creation flags.  Can be 0 or a bitwise-OR combination\n                   of any of the following values:\n\n                   - GS_DYNAMIC - Can be dynamically updated in real time.\n                   - GS_DUP_BUFFER - Do not pass buffer ownership\n\n   :return:        A new index buffer object, or *NULL* if failed\n\n---------------------\n\n.. function:: void     gs_indexbuffer_destroy(gs_indexbuffer_t *indexbuffer)\n\n   Destroys an index buffer object.\n\n   :param indexbuffer: Index buffer object\n\n---------------------\n\n.. function:: void     gs_indexbuffer_flush(gs_indexbuffer_t *indexbuffer)\n\n   Flushes a index buffer to its interval index data object.  To modify\n   its internal index data, call :c:func:`gs_indexbuffer_get_data()`.\n\n   Can only be used with dynamic index buffer objects.\n\n   :param indexbuffer: Index buffer object\n\n---------------------\n\n.. function:: void     gs_indexbuffer_flush_direct(gs_indexbuffer_t *indexbuffer, const void *data)\n\n   Flushes a index buffer to the specified index buffer data.\n\n   Can only be used with dynamic index buffer objects.\n\n   :param indexbuffer: Index buffer object\n   :param data:        Index buffer data to flush\n\n---------------------\n\n.. function:: void     *gs_indexbuffer_get_data(const gs_indexbuffer_t *indexbuffer)\n\n   Gets the index buffer data associated with a index buffer object.\n   This data can be changed and index buffer can be updated with\n   :c:func:`gs_indexbuffer_flush()`.\n\n   Can only be used with dynamic index buffer objects.\n\n   :param vertbuffer: Index buffer object\n   :return:           Index buffer data pointer\n\n---------------------\n\n.. function:: size_t   gs_indexbuffer_get_num_indices(const gs_indexbuffer_t *indexbuffer)\n\n   Gets the number of indices associated with this index buffer.\n\n   :param indexbuffer: Index buffer object\n   :return:            Number of indices the vertex buffer object has\n\n---------------------\n\n.. function:: enum gs_index_type gs_indexbuffer_get_type(const gs_indexbuffer_t *indexbuffer)\n\n   Gets the type of index buffer.\n\n   :param indexbuffer: Index buffer object\n   :return:            Index buffer type\n\n---------------------\n\n\nDisplay Duplicator (Windows Only)\n---------------------------------\n\n.. function:: gs_duplicator_t *gs_duplicator_create(int monitor_idx)\n\n---------------------\n\n.. function:: void gs_duplicator_destroy(gs_duplicator_t *duplicator)\n\n---------------------\n\n.. function:: bool gs_duplicator_update_frame(gs_duplicator_t *duplicator)\n\n---------------------\n\n.. function:: gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator)\n\n---------------------\n\n.. function:: bool gs_get_duplicator_monitor_info(int monitor_idx, struct gs_monitor_info *monitor_info)\n\n---------------------\n\n\nMonitor Functions\n---------------------------------\n\n.. function:: bool gs_is_monitor_hdr(void *monitor)\n\n---------------------\n\n\nRender Helper Functions\n-----------------------\n\n.. function:: void gs_render_start(bool b_new)\n\n---------------------\n\n.. function:: void gs_render_stop(enum gs_draw_mode mode)\n\n---------------------\n\n.. function:: gs_vertbuffer_t *gs_render_save(void)\n\n---------------------\n\n.. function:: void gs_vertex2f(float x, float y)\n\n---------------------\n\n.. function:: void gs_vertex3f(float x, float y, float z)\n\n---------------------\n\n.. function:: void gs_normal3f(float x, float y, float z)\n\n---------------------\n\n.. function:: void gs_color(uint32_t color)\n\n---------------------\n\n.. function:: void gs_texcoord(float x, float y, int unit)\n\n---------------------\n\n.. function:: void gs_vertex2v(const struct vec2 *v)\n\n---------------------\n\n.. function:: void gs_vertex3v(const struct vec3 *v)\n\n---------------------\n\n.. function:: void gs_normal3v(const struct vec3 *v)\n\n---------------------\n\n.. function:: void gs_color4v(const struct vec4 *v)\n\n---------------------\n\n.. function:: void gs_texcoord2v(const struct vec2 *v, int unit)\n\n---------------------\n\n\nGraphics Types\n--------------\n\n.. type:: struct gs_duplicator       gs_duplicator_t\n.. type:: struct gs_texture          gs_texture_t\n.. type:: struct gs_stage_surface    gs_stagesurf_t\n.. type:: struct gs_zstencil_buffer  gs_zstencil_t\n.. type:: struct gs_vertex_buffer    gs_vertbuffer_t\n.. type:: struct gs_index_buffer     gs_indexbuffer_t\n.. type:: struct gs_sampler_state    gs_samplerstate_t\n.. type:: struct gs_swap_chain       gs_swapchain_t\n.. type:: struct gs_texture_render   gs_texrender_t\n.. type:: struct gs_shader           gs_shader_t\n.. type:: struct gs_shader_param     gs_sparam_t\n.. type:: struct gs_device           gs_device_t\n.. type:: void                       gs_sync_t\n.. type:: struct graphics_subsystem  graphics_t\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics-image-file.rst",
    "content": ".. _image_file_helper:\n\nImage File Helper\n=================\n\nHelper functions/type for easily loading/managing image files, including\nanimated gif files.\n\n.. code:: cpp\n\n   #include <graphics/image-file.h>\n\n.. struct:: gs_image_file\n\n   Image file structure\n\n.. type:: gs_texture_t *gs_image_file.texture\n\n   Texture\n\n.. type:: struct gs_image_file gs_image_file_t\n\n   Image file type\n\n---------------------\n\n.. function:: void gs_image_file_init(gs_image_file_t *image, const char *file)\n\n   Loads an initializes an image file helper.  Does not initialize the\n   texture; call :c:func:`gs_image_file_init_texture()` to initialize\n   the texture.\n\n   :param image: Image file helper to initialize\n   :param file:  Path to the image file to load\n\n---------------------\n\n.. function:: void gs_image_file_free(gs_image_file_t *image)\n\n   Frees an image file helper\n\n   :param image: Image file helper\n\n---------------------\n\n.. function:: void gs_image_file_init_texture(gs_image_file_t *image)\n\n   Initializes the texture of an image file helper.  This is separate\n   from :c:func:`gs_image_file_init()` because it allows deferring the\n   graphics initialization if needed.\n\n   :param image: Image file helper\n\n---------------------\n\n.. function:: bool gs_image_file_tick(gs_image_file_t *image, uint64_t elapsed_time_ns)\n\n   Performs a tick operation on the image file helper (used primarily\n   for animated file).  Does not update the texture until\n   :c:func:`gs_image_file_update_texture()` is called.\n\n   :param image:           Image file helper\n   :param elapsed_time_ns: Elapsed time in nanoseconds\n\n---------------------\n\n.. function:: void gs_image_file_update_texture(gs_image_file_t *image)\n\n   Updates the texture (used primarily for animated files)\n\n   :param image: Image file helper\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics-math.rst",
    "content": "Extra Math Functions/Macros\n===========================\n\n.. code:: cpp\n\n   #include <graphics/math-extra.h>\n\nHelper functions/macros for graphics math.\n\n.. macro:: RAD(val)\n\n   Macro that converts a floating point degrees value to radians.\n\n.. macro:: DEG(val)\n\n   Macro that converts a floating point radians value to degrees.\n\n**LARGE_EPSILON**   1e-2f\n\n   Large epsilon value.\n\n**EPSILON**         1e-4f\n\n   Epsilon value.\n\n**TINY_EPSILON**    1e-5f\n\n   Tiny Epsilon value.\n\n**M_INFINITE**      3.4e38f\n\n   Infinite value\n\n---------------------\n\n.. function:: float rand_float(int positive_only)\n\n   Generates a random floating point value (from -1.0f..1.0f, or\n   0.0f..1.0f if *positive_only* is set).\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics-matrix4.rst",
    "content": "Matrix\n======\n\n.. code:: cpp\n\n   #include <graphics/matrix4.h>\n\n.. struct:: matrix4\n\n   Matrix structure\n\n.. member:: struct vec4 matrix4.x\n\n   X component vector\n\n.. member:: struct vec4 matrix4.y\n\n   Y component vector\n\n.. member:: struct vec4 matrix4.z\n\n   Z component vector\n\n.. member:: struct vec4 matrix4.w\n\n   W component vector\n\n---------------------\n\n.. function:: void matrix4_copy(struct matrix4 *dst, const struct matrix4 *m)\n\n   Copies a matrix\n\n   :param dst: Destination matrix\n   :param m:   Matrix to copy\n\n---------------------\n\n.. function:: void matrix4_identity(struct matrix4 *dst)\n\n   Sets an identity matrix\n\n   :param dst: Destination matrix\n\n---------------------\n\n.. function:: void matrix4_from_quat(struct matrix4 *dst, const struct quat *q)\n\n   Converts a quaternion to a matrix\n\n   :param dst: Destination matrix\n   :param q:   Quaternion to convert\n\n---------------------\n\n.. function:: void matrix4_from_axisang(struct matrix4 *dst, const struct axisang *aa)\n\n   Converts an axis angle to a matrix\n\n   :param dst: Destination matrix\n   :param aa:  Axis angle to convert\n\n---------------------\n\n.. function:: void matrix4_mul(struct matrix4 *dst, const struct matrix4 *m1, const struct matrix4 *m2)\n\n   Multiples two matrices\n\n   :param dst: Destination matrix\n   :param m1:  Matrix 1\n   :param m2:  Matrix 2\n\n---------------------\n\n.. function:: float matrix4_determinant(const struct matrix4 *m)\n\n   Gets the determinant value of a matrix\n\n   :param m: Matrix\n   :return:  Determinant\n\n---------------------\n\n.. function:: void matrix4_translate3v(struct matrix4 *dst, const struct matrix4 *m, const struct vec3 *v)\n              void matrix4_translate3f(struct matrix4 *dst, const struct matrix4 *m, float x, float y, float z)\n\n   Translates the matrix by a 3-component vector\n\n   :param dst: Destination matrix\n   :param m:   Matrix to translate\n   :param v:   Translation vector\n\n---------------------\n\n.. function:: void matrix4_translate4v(struct matrix4 *dst, const struct matrix4 *m, const struct vec4 *v)\n\n   Translates the matrix by a 4-component vector\n\n   :param dst: Destination matrix\n   :param m:   Matrix to translate\n   :param v:   Translation vector\n\n---------------------\n\n.. function:: void matrix4_rotate(struct matrix4 *dst, const struct matrix4 *m, const struct quat *q)\n\n   Rotates a matrix by a quaternion\n\n   :param dst: Destination matrix\n   :param m:   Matrix to rotate\n   :param q:   Rotation quaternion\n\n---------------------\n\n.. function:: void matrix4_rotate_aa(struct matrix4 *dst, const struct matrix4 *m, const struct axisang *aa)\n              void matrix4_rotate_aa4f(struct matrix4 *dst, const struct matrix4 *m, float x, float y, float z, float rot)\n\n   Rotates a matrix by an axis angle\n\n   :param dst: Destination matrix\n   :param m:   Matrix to rotate\n   :param aa:  Rotation anxis angle\n\n---------------------\n\n.. function:: void matrix4_scale(struct matrix4 *dst, const struct matrix4 *m, const struct vec3 *v)\n              void matrix4_scale3f(struct matrix4 *dst, const struct matrix4 *m, float x, float y, float z)\n\n   Scales each matrix component by the components of a 3-component vector\n\n   :param dst: Destination matrix\n   :param m:   Matrix to scale\n   :param v:   Scale vector\n\n---------------------\n\n.. function:: bool matrix4_inv(struct matrix4 *dst, const struct matrix4 *m)\n\n   Inverts a matrix\n\n   :param dst: Destination matrix\n   :param m:   Matrix to invert\n\n---------------------\n\n.. function:: void matrix4_transpose(struct matrix4 *dst, const struct matrix4 *m)\n\n   Transposes a matrix\n\n   :param dst: Destination matrix\n   :param m:   Matrix to transpose\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics-quat.rst",
    "content": "Quaternion\n==========\n\n.. code:: cpp\n\n   #include <graphics/quat.h>\n\n.. struct:: quat\n\n   Two component quaternion structure.\n\n.. member:: float quat.x\n\n   X component\n\n.. member:: float quat.y\n\n   Y component\n\n.. member:: float quat.z\n\n   Z component\n\n.. member:: float quat.w\n\n   W component\n\n.. member:: float quat.ptr[4]\n\n   Unioned array of all components\n\n---------------------\n\n.. function:: void quat_identity(struct quat *dst)\n\n   Sets a quaternion to {0.0f, 0.0f, 0.0f, 1.0f}.\n\n   :param dst: Destination\n\n---------------------\n\n.. function:: void quat_set(struct quat *dst, float x, float y)\n\n   Sets the individual components of a quaternion.\n\n   :param dst: Destination\n   :param x:   X component\n   :param y:   Y component\n   :param y:   Z component\n   :param w:   W component\n\n---------------------\n\n.. function:: void quat_copy(struct quat *dst, const struct quat *v)\n\n   Copies a quaternion\n\n   :param dst: Destination\n   :param v:   Quaternion to copy\n\n---------------------\n\n.. function:: void quat_add(struct quat *dst, const struct quat *v1, const struct quat *v2)\n\n   Adds two quaternions\n\n   :param dst: Destination\n   :param v1:  Quaternion 1\n   :param v2:  Quaternion 2\n\n---------------------\n\n.. function:: void quat_sub(struct quat *dst, const struct quat *v1, const struct quat *v2)\n\n   Subtracts two quaternions\n\n   :param dst: Destination\n   :param v1:  Quaternion being subtracted from\n   :param v2:  Quaternion being subtracted\n\n---------------------\n\n.. function:: void quat_mul(struct quat *dst, const struct quat *v1, const struct quat *v2)\n\n   Multiplies two quaternions\n\n   :param dst: Destination\n   :param v1:  Quaternion 1\n   :param v2:  Quaternion 2\n\n---------------------\n\n.. function:: void quat_addf(struct quat *dst, const struct quat *v, float f)\n\n   Adds a floating point to all components\n\n   :param dst: Destination\n   :param dst: Quaternion\n   :param f:   Floating point\n\n---------------------\n\n.. function:: void quat_subf(struct quat *dst, const struct quat *v, float f)\n\n   Subtracts a floating point from all components\n\n   :param dst: Destination\n   :param v:   Quaternion being subtracted from\n   :param f:   Floating point being subtracted\n\n---------------------\n\n.. function:: void quat_mulf(struct quat *dst, const struct quat *v, float f)\n\n   Multiplies a floating point with all components\n\n   :param dst: Destination\n   :param dst: Quaternion\n   :param f:   Floating point\n\n---------------------\n\n.. function:: void quat_inv(struct quat *dst, const struct quat *v)\n\n   Inverts a quaternion\n\n   :param dst: Destination\n   :param v:   Quaternion to invert\n\n---------------------\n\n.. function:: float quat_dot(const struct quat *v1, const struct quat *v2)\n\n   Performs a dot product between two quaternions\n\n   :param v1: Quaternion 1\n   :param v2: Quaternion 2\n   :return:   Result of the dot product\n\n---------------------\n\n.. function:: float quat_len(const struct quat *v)\n\n   Gets the length of a quaternion\n\n   :param v: Quaternion\n   :return:  The quaternion's length\n\n---------------------\n\n.. function:: float quat_dist(const struct quat *v1, const struct quat *v2)\n\n   Gets the distance between two quaternions\n\n   :param v1: Quaternion 1\n   :param v2: Quaternion 2\n   :return:   Distance between the two quaternions\n\n---------------------\n\n.. function:: void quat_from_axisang(struct quat *dst, const struct axisang *aa)\n\n   Converts an axis angle to a quaternion\n\n   :param dst: Destination quaternion\n   :param aa:  Axis angle\n\n---------------------\n\n.. function:: void quat_from_matrix4(struct quat *dst, const struct matrix4 *m)\n\n   Converts the rotational properties of a matrix to a quaternion\n\n   :param dst: Destination quaternion\n   :param m:   Matrix to convert\n\n---------------------\n\n.. function:: void quat_get_dir(struct vec3 *dst, const struct quat *q)\n\n   Converts a quaternion to a directional vector\n\n   :param dst: Destination 3-component vector\n   :param q:   Quaternion\n\n---------------------\n\n.. function:: void quat_set_look_dir(struct quat *dst, const struct vec3 *dir)\n\n   Creates a quaternion from a specific \"look\" direction\n\n   :param dst: Destination quaternion\n   :param dir: 3-component vector representing the look direction\n\n---------------------\n\n.. function:: void quat_interpolate(struct quat *dst, const struct quat *q1, const struct quat *q2, float t)\n\n   Linearly interpolates two quaternions\n\n   :param dst: Destination quaternion\n   :param q1:  Quaternion 1\n   :param q2:  Quaternion 2\n   :param t:   Time value (0.0f..1.0f)\n\n---------------------\n\n.. function:: void quat_get_tangent(struct quat *dst, const struct quat *prev, const struct quat *q, const struct quat *next)\n\n   Gets a tangent value for the center of three rotational values\n\n   :param dst:  Destination quaternion\n   :param prev: Previous rotation\n   :param q:    Rotation to get tangent for\n   :param next: Next rotation\n\n---------------------\n\n.. function:: void quat_interpolate_cubic(struct quat *dst, const struct quat *q1, const struct quat *q2, const struct quat *m1, const struct quat *m2, float t)\n\n   Performs cubic interpolation between two quaternions\n\n   :param dst: Destination quaternion\n   :param q1:  Quaternion 1\n   :param q2:  Quaternion 2\n   :param m1:  Tangent 1\n   :param m2:  Tangent 2\n   :param t:   Time value (0.0f..1.0f)\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics-vec2.rst",
    "content": "2-Component Vector\n==================\n\n.. code:: cpp\n\n   #include <graphics/vec2.h>\n\n.. struct:: vec2\n\n   Two component vector structure.\n\n.. member:: float vec2.x\n\n   X component\n\n.. member:: float vec2.y\n\n   Y component\n\n.. member:: float vec2.ptr[2]\n\n   Unioned array of both components\n\n---------------------\n\n.. function:: void vec2_zero(struct vec2 *dst)\n\n   Zeroes a vector\n\n   :param dst: Destination\n\n---------------------\n\n.. function:: void vec2_set(struct vec2 *dst, float x, float y)\n\n   Sets the individual components of a 2-component vector.\n\n   :param dst: Destination\n   :param x:   X component\n   :param y:   Y component\n\n---------------------\n\n.. function:: void vec2_copy(struct vec2 *dst, const struct vec2 *v)\n\n   Copies a vector\n\n   :param dst: Destination\n   :param v:   Vector to copy\n\n---------------------\n\n.. function:: void vec2_add(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2)\n\n   Adds two vectors\n\n   :param dst: Destination\n   :param v1:  Vector 1\n   :param v2:  Vector 2\n\n---------------------\n\n.. function:: void vec2_sub(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2)\n\n   Subtracts two vectors\n\n   :param dst: Destination\n   :param v1:  Vector being subtracted from\n   :param v2:  Vector being subtracted\n\n---------------------\n\n.. function:: void vec2_mul(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2)\n\n   Multiplies two vectors\n\n   :param dst: Destination\n   :param v1:  Vector 1\n   :param v2:  Vector 2\n\n---------------------\n\n.. function:: void vec2_div(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2)\n\n   Divides two vectors\n\n   :param dst: Destination\n   :param v1:  Dividend\n   :param v2:  Divisor\n\n---------------------\n\n.. function:: void vec2_addf(struct vec2 *dst, const struct vec2 *v, float f)\n\n   Adds a floating point to all components\n\n   :param dst: Destination\n   :param dst: Vector\n   :param f:   Floating point\n\n---------------------\n\n.. function:: void vec2_subf(struct vec2 *dst, const struct vec2 *v, float f)\n\n   Subtracts a floating point from all components\n\n   :param dst: Destination\n   :param v:   Vector being subtracted from\n   :param f:   Floating point being subtracted\n\n---------------------\n\n.. function:: void vec2_mulf(struct vec2 *dst, const struct vec2 *v, float f)\n\n   Multiplies a floating point with all components\n\n   :param dst: Destination\n   :param dst: Vector\n   :param f:   Floating point\n\n---------------------\n\n.. function:: void vec2_divf(struct vec2 *dst, const struct vec2 *v, float f)\n\n   Divides a floating point from all components\n\n   :param dst: Destination\n   :param v:   Vector (dividend)\n   :param f:   Floating point (divisor)\n\n---------------------\n\n.. function:: void vec2_neg(struct vec2 *dst, const struct vec2 *v)\n\n   Negates a vector\n\n   :param dst: Destination\n   :param v:   Vector to negate\n\n---------------------\n\n.. function:: float vec2_dot(const struct vec2 *v1, const struct vec2 *v2)\n\n   Performs a dot product between two vectors\n\n   :param v1: Vector 1\n   :param v2: Vector 2\n   :return:   Result of the dot product\n\n---------------------\n\n.. function:: float vec2_len(const struct vec2 *v)\n\n   Gets the length of a vector\n\n   :param v: Vector\n   :return:  The vector's length\n\n---------------------\n\n.. function:: float vec2_dist(const struct vec2 *v1, const struct vec2 *v2)\n\n   Gets the distance between two vectors\n\n   :param v1: Vector 1\n   :param v2: Vector 2\n   :return:   Distance between the two vectors\n\n---------------------\n\n.. function:: void vec2_minf(struct vec2 *dst, const struct vec2 *v, float val)\n\n   Gets the minimum values between a vector's components and a floating point\n\n   :param dst: Destination\n   :param v:   Vector\n   :param val: Floating point\n\n---------------------\n\n.. function:: void vec2_min(struct vec2 *dst, const struct vec2 *v, const struct vec2 *min_v)\n\n   Gets the minimum values between two vectors\n\n   :param dst:   Destination\n   :param v:     Vector 1\n   :param min_v: Vector 2\n\n---------------------\n\n.. function:: void vec2_maxf(struct vec2 *dst, const struct vec2 *v, float val)\n\n   Gets the maximum values between a vector's components and a floating point\n\n   :param dst: Destination\n   :param v:   Vector\n   :param val: Floating point\n\n---------------------\n\n.. function:: void vec2_max(struct vec2 *dst, const struct vec2 *v, const struct vec2 *max_v)\n\n   Gets the maximum values between two vectors\n\n   :param dst:   Destination\n   :param v:     Vector 1\n   :param max_v: Vector 2\n\n---------------------\n\n.. function:: void vec2_abs(struct vec2 *dst, const struct vec2 *v)\n\n   Gets the absolute values of each component\n\n   :param dst: Destination\n   :param v:   Vector\n\n---------------------\n\n.. function:: void vec2_floor(struct vec2 *dst, const struct vec2 *v)\n\n   Gets the floor values of each component\n\n   :param dst: Destination\n   :param v:   Vector\n\n---------------------\n\n.. function:: void vec2_ceil(struct vec2 *dst, const struct vec2 *v)\n\n   Gets the ceiling values of each component\n\n   :param dst: Destination\n   :param v:   Vector\n\n---------------------\n\n.. function:: int vec2_close(const struct vec2 *v1, const struct vec2 *v2, float epsilon)\n\n   Compares two vectors\n\n   :param v1:      Vector 1\n   :param v2:      Vector 2\n   :param epsilon: Maximum precision for comparison\n\n---------------------\n\n.. function:: void vec2_norm(struct vec2 *dst, const struct vec2 *v)\n\n   Normalizes a vector\n\n   :param dst: Destination\n   :param v:   Vector to normalize\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics-vec3.rst",
    "content": "3-Component Vector\n==================\n\n.. code:: cpp\n\n   #include <graphics/vec3.h>\n\n.. struct:: vec3\n\n   Two component vector structure.\n\n.. member:: float vec3.x\n\n   X component\n\n.. member:: float vec3.y\n\n   Y component\n\n.. member:: float vec3.z\n\n   Z component\n\n.. member:: float vec3.ptr[3]\n\n   Unioned array of all components\n\n---------------------\n\n.. function:: void vec3_zero(struct vec3 *dst)\n\n   Zeroes a vector\n\n   :param dst: Destination\n\n---------------------\n\n.. function:: void vec3_set(struct vec3 *dst, float x, float y)\n\n   Sets the individual components of a 3-component vector.\n\n   :param dst: Destination\n   :param x:   X component\n   :param y:   Y component\n   :param y:   Z component\n\n---------------------\n\n.. function:: void vec3_copy(struct vec3 *dst, const struct vec3 *v)\n\n   Copies a vector\n\n   :param dst: Destination\n   :param v:   Vector to copy\n\n---------------------\n\n.. function:: void vec3_from_vec4(struct vec3 *dst, const struct vec4 *v)\n\n   Creates a 3-component vector from a 4-component vector\n\n   :param dst: 3-component vector destination\n   :param v:   4-component vector\n\n---------------------\n\n.. function:: void vec3_add(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n\n   Adds two vectors\n\n   :param dst: Destination\n   :param v1:  Vector 1\n   :param v2:  Vector 2\n\n---------------------\n\n.. function:: void vec3_sub(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n\n   Subtracts two vectors\n\n   :param dst: Destination\n   :param v1:  Vector being subtracted from\n   :param v2:  Vector being subtracted\n\n---------------------\n\n.. function:: void vec3_mul(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n\n   Multiplies two vectors\n\n   :param dst: Destination\n   :param v1:  Vector 1\n   :param v2:  Vector 2\n\n---------------------\n\n.. function:: void vec3_div(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n\n   Divides two vectors\n\n   :param dst: Destination\n   :param v1:  Dividend\n   :param v2:  Divisor\n\n---------------------\n\n.. function:: void vec3_addf(struct vec3 *dst, const struct vec3 *v, float f)\n\n   Adds a floating point to all components\n\n   :param dst: Destination\n   :param dst: Vector\n   :param f:   Floating point\n\n---------------------\n\n.. function:: void vec3_subf(struct vec3 *dst, const struct vec3 *v, float f)\n\n   Subtracts a floating point from all components\n\n   :param dst: Destination\n   :param v:   Vector being subtracted from\n   :param f:   Floating point being subtracted\n\n---------------------\n\n.. function:: void vec3_mulf(struct vec3 *dst, const struct vec3 *v, float f)\n\n   Multiplies a floating point with all components\n\n   :param dst: Destination\n   :param dst: Vector\n   :param f:   Floating point\n\n---------------------\n\n.. function:: void vec3_divf(struct vec3 *dst, const struct vec3 *v, float f)\n\n   Divides a floating point from all components\n\n   :param dst: Destination\n   :param v:   Vector (dividend)\n   :param f:   Floating point (divisor)\n\n---------------------\n\n.. function:: void vec3_neg(struct vec3 *dst, const struct vec3 *v)\n\n   Negates a vector\n\n   :param dst: Destination\n   :param v:   Vector to negate\n\n---------------------\n\n.. function:: float vec3_dot(const struct vec3 *v1, const struct vec3 *v2)\n\n   Performs a dot product between two vectors\n\n   :param v1: Vector 1\n   :param v2: Vector 2\n   :return:   Result of the dot product\n\n---------------------\n\n.. function:: void vec3_cross(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n\n   Performs a cross product between two vectors\n\n   :param dst: Destination\n   :param v1:  Vector 1\n   :param v2:  Vector 2\n\n---------------------\n\n.. function:: float vec3_len(const struct vec3 *v)\n\n   Gets the length of a vector\n\n   :param v: Vector\n   :return:  The vector's length\n\n---------------------\n\n.. function:: float vec3_dist(const struct vec3 *v1, const struct vec3 *v2)\n\n   Gets the distance between two vectors\n\n   :param v1: Vector 1\n   :param v2: Vector 2\n   :return:   Distance between the two vectors\n\n---------------------\n\n.. function:: void vec3_minf(struct vec3 *dst, const struct vec3 *v, float val)\n\n   Gets the minimum values between a vector's components and a floating point\n\n   :param dst: Destination\n   :param v:   Vector\n   :param val: Floating point\n\n---------------------\n\n.. function:: void vec3_min(struct vec3 *dst, const struct vec3 *v, const struct vec3 *min_v)\n\n   Gets the minimum values between two vectors\n\n   :param dst:   Destination\n   :param v:     Vector 1\n   :param min_v: Vector 2\n\n---------------------\n\n.. function:: void vec3_maxf(struct vec3 *dst, const struct vec3 *v, float val)\n\n   Gets the maximum values between a vector's components and a floating point\n\n   :param dst: Destination\n   :param v:   Vector\n   :param val: Floating point\n\n---------------------\n\n.. function:: void vec3_max(struct vec3 *dst, const struct vec3 *v, const struct vec3 *max_v)\n\n   Gets the maximum values between two vectors\n\n   :param dst:   Destination\n   :param v:     Vector 1\n   :param max_v: Vector 2\n\n---------------------\n\n.. function:: void vec3_abs(struct vec3 *dst, const struct vec3 *v)\n\n   Gets the absolute values of each component\n\n   :param dst: Destination\n   :param v:   Vector\n\n---------------------\n\n.. function:: void vec3_floor(struct vec3 *dst, const struct vec3 *v)\n\n   Gets the floor values of each component\n\n   :param dst: Destination\n   :param v:   Vector\n\n---------------------\n\n.. function:: void vec3_ceil(struct vec3 *dst, const struct vec3 *v)\n\n   Gets the ceiling values of each component\n\n   :param dst: Destination\n   :param v:   Vector\n\n---------------------\n\n.. function:: int vec3_close(const struct vec3 *v1, const struct vec3 *v2, float epsilon)\n\n   Compares two vectors\n\n   :param v1:      Vector 1\n   :param v2:      Vector 2\n   :param epsilon: Maximum precision for comparison\n\n---------------------\n\n.. function:: void vec3_norm(struct vec3 *dst, const struct vec3 *v)\n\n   Normalizes a vector\n\n   :param dst: Destination\n   :param v:   Vector to normalize\n\n---------------------\n\n.. function:: void vec3_transform(struct vec3 *dst, const struct vec3 *v, const struct matrix4 *m)\n\n   Transforms a vector\n\n   :param dst: Destination\n   :param v:   Vector\n   :param m:   Matrix\n\n---------------------\n\n.. function:: void vec3_rotate(struct vec3 *dst, const struct vec3 *v, const struct matrix3 *m)\n\n   Rotates a vector\n\n   :param dst: Destination\n   :param v:   Vector\n   :param m:   Matrix\n\n---------------------\n\n.. function:: void vec3_rand(struct vec3 *dst, int positive_only)\n\n   Generates a random vector\n\n   :param dst:           Destination\n   :param positive_only: *true* if positive only, *false* otherwise\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics-vec4.rst",
    "content": "4-Component Vector\n==================\n\n.. code:: cpp\n\n   #include <graphics/vec4.h>\n\n.. struct:: vec4\n\n   Two component vector structure.\n\n.. member:: float vec4.x\n\n   X component\n\n.. member:: float vec4.y\n\n   Y component\n\n.. member:: float vec4.z\n\n   Z component\n\n.. member:: float vec4.w\n\n   W component\n\n.. member:: float vec4.ptr[4]\n\n   Unioned array of all components\n\n---------------------\n\n.. function:: void vec4_zero(struct vec4 *dst)\n\n   Zeroes a vector\n\n   :param dst: Destination\n\n---------------------\n\n.. function:: void vec4_set(struct vec4 *dst, float x, float y, float z, float w)\n\n   Sets the individual components of a 4-component vector.\n\n   :param dst: Destination\n   :param x:   X component\n   :param y:   Y component\n   :param z:   Z component\n   :param w:   W component\n\n---------------------\n\n.. function:: void vec4_copy(struct vec4 *dst, const struct vec4 *v)\n\n   Copies a vector\n\n   :param dst: Destination\n   :param v:   Vector to copy\n\n---------------------\n\n.. function:: void vec4_from_vec3(struct vec4 *dst, const struct vec3 *v)\n\n   Creates a 4-component vector from a 3-component vector\n\n   :param dst: 4-component vector destination\n   :param v:   3-component vector\n\n---------------------\n\n.. function:: void vec4_add(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2)\n\n   Adds two vectors\n\n   :param dst: Destination\n   :param v1:  Vector 1\n   :param v2:  Vector 2\n\n---------------------\n\n.. function:: void vec4_sub(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2)\n\n   Subtracts two vectors\n\n   :param dst: Destination\n   :param v1:  Vector being subtracted from\n   :param v2:  Vector being subtracted\n\n---------------------\n\n.. function:: void vec4_mul(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2)\n\n   Multiplies two vectors\n\n   :param dst: Destination\n   :param v1:  Vector 1\n   :param v2:  Vector 2\n\n---------------------\n\n.. function:: void vec4_div(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2)\n\n   Divides two vectors\n\n   :param dst: Destination\n   :param v1:  Dividend\n   :param v2:  Divisor\n\n---------------------\n\n.. function:: void vec4_addf(struct vec4 *dst, const struct vec4 *v, float f)\n\n   Adds a floating point to all components\n\n   :param dst: Destination\n   :param dst: Vector\n   :param f:   Floating point\n\n---------------------\n\n.. function:: void vec4_subf(struct vec4 *dst, const struct vec4 *v, float f)\n\n   Subtracts a floating point from all components\n\n   :param dst: Destination\n   :param v:   Vector being subtracted from\n   :param f:   Floating point being subtracted\n\n---------------------\n\n.. function:: void vec4_mulf(struct vec4 *dst, const struct vec4 *v, float f)\n\n   Multiplies a floating point with all components\n\n   :param dst: Destination\n   :param dst: Vector\n   :param f:   Floating point\n\n---------------------\n\n.. function:: void vec4_divf(struct vec4 *dst, const struct vec4 *v, float f)\n\n   Divides a floating point from all components\n\n   :param dst: Destination\n   :param v:   Vector (dividend)\n   :param f:   Floating point (divisor)\n\n---------------------\n\n.. function:: void vec4_neg(struct vec4 *dst, const struct vec4 *v)\n\n   Negates a vector\n\n   :param dst: Destination\n   :param v:   Vector to negate\n\n---------------------\n\n.. function:: float vec4_dot(const struct vec4 *v1, const struct vec4 *v2)\n\n   Performs a dot product between two vectors\n\n   :param v1: Vector 1\n   :param v2: Vector 2\n   :return:   Result of the dot product\n\n---------------------\n\n.. function:: float vec4_len(const struct vec4 *v)\n\n   Gets the length of a vector\n\n   :param v: Vector\n   :return:  The vector's length\n\n---------------------\n\n.. function:: float vec4_dist(const struct vec4 *v1, const struct vec4 *v2)\n\n   Gets the distance between two vectors\n\n   :param v1: Vector 1\n   :param v2: Vector 2\n   :return:   Distance between the two vectors\n\n---------------------\n\n.. function:: void vec4_minf(struct vec4 *dst, const struct vec4 *v, float val)\n\n   Gets the minimum values between a vector's components and a floating point\n\n   :param dst: Destination\n   :param v:   Vector\n   :param val: Floating point\n\n---------------------\n\n.. function:: void vec4_min(struct vec4 *dst, const struct vec4 *v, const struct vec4 *min_v)\n\n   Gets the minimum values between two vectors\n\n   :param dst:   Destination\n   :param v:     Vector 1\n   :param min_v: Vector 2\n\n---------------------\n\n.. function:: void vec4_maxf(struct vec4 *dst, const struct vec4 *v, float val)\n\n   Gets the maximum values between a vector's components and a floating point\n\n   :param dst: Destination\n   :param v:   Vector\n   :param val: Floating point\n\n---------------------\n\n.. function:: void vec4_max(struct vec4 *dst, const struct vec4 *v, const struct vec4 *max_v)\n\n   Gets the maximum values between two vectors\n\n   :param dst:   Destination\n   :param v:     Vector 1\n   :param max_v: Vector 2\n\n---------------------\n\n.. function:: void vec4_abs(struct vec4 *dst, const struct vec4 *v)\n\n   Gets the absolute values of each component\n\n   :param dst: Destination\n   :param v:   Vector\n\n---------------------\n\n.. function:: void vec4_floor(struct vec4 *dst, const struct vec4 *v)\n\n   Gets the floor values of each component\n\n   :param dst: Destination\n   :param v:   Vector\n\n---------------------\n\n.. function:: void vec4_ceil(struct vec4 *dst, const struct vec4 *v)\n\n   Gets the ceiling values of each component\n\n   :param dst: Destination\n   :param v:   Vector\n\n---------------------\n\n.. function:: int vec4_close(const struct vec4 *v1, const struct vec4 *v2, float epsilon)\n\n   Compares two vectors\n\n   :param v1:      Vector 1\n   :param v2:      Vector 2\n   :param epsilon: Maximum precision for comparison\n\n---------------------\n\n.. function:: void vec4_norm(struct vec4 *dst, const struct vec4 *v)\n\n   Normalizes a vector\n\n   :param dst: Destination\n   :param v:   Vector to normalize\n\n---------------------\n\n.. function:: void vec4_transform(struct vec4 *dst, const struct vec4 *v, const struct matrix4 *m)\n\n   Transforms a vector\n\n   :param dst: Destination\n   :param v:   Vector\n   :param m:   Matrix\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-graphics.rst",
    "content": "Graphics API Reference (libobs/graphics)\n========================================\n\n.. toctree::\n   :maxdepth: 2\n\n   reference-libobs-graphics-effects\n   reference-libobs-graphics-vec2\n   reference-libobs-graphics-vec3\n   reference-libobs-graphics-vec4\n   reference-libobs-graphics-quat\n   reference-libobs-graphics-matrix4\n   reference-libobs-graphics-math\n   reference-libobs-graphics-image-file\n   reference-libobs-graphics-axisang\n   reference-libobs-graphics-graphics\n\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-media-io.rst",
    "content": "Media I/O API Reference (libobs/media-io)\n=========================================\n\n.. code:: cpp\n\n   #include <obs.h>\n\n\nVideo Handler\n-------------\n\n.. type:: video_t\n\n   Video output handler object\n\n---------------------\n\n.. enum:: video_format\n\n   Video format.  Can be one of the following values:\n\n   - VIDEO_FORMAT_I420\n   - VIDEO_FORMAT_NV12\n\n   - VIDEO_FORMAT_YVYU\n   - VIDEO_FORMAT_YUY2\n   - VIDEO_FORMAT_UYVY\n\n   - VIDEO_FORMAT_RGBA\n   - VIDEO_FORMAT_BGRA\n   - VIDEO_FORMAT_BGRX\n   - VIDEO_FORMAT_Y800\n\n   - VIDEO_FORMAT_I444\n\n   - VIDEO_FORMAT_BGR3\n\n   - VIDEO_FORMAT_I422\n\n   - VIDEO_FORMAT_I40A\n\n   - VIDEO_FORMAT_I42A\n\n   - VIDEO_FORMAT_YUVA\n\n   - VIDEO_FORMAT_AYUV\n\n   - VIDEO_FORMAT_I010\n   - VIDEO_FORMAT_P010\n\n   - VIDEO_FORMAT_I210\n\n   - VIDEO_FORMAT_I412\n\n   - VIDEO_FORMAT_YA2L\n\n   - VIDEO_FORMAT_P216\n\n   - VIDEO_FORMAT_P416\n\n   - VIDEO_FORMAT_V210\n\n   - VIDEO_FORMAT_R10L\n\n---------------------\n\n.. enum:: video_trc\n\n   Transfer characteristics.  Can be one of the following values:\n\n   - VIDEO_TRC_DEFAULT - sRGB TRC for SDR, PQ TRC for HDR\n   - VIDEO_TRC_SRGB    - sRGB TRC\n   - VIDEO_TRC_PQ      - PQ\n   - VIDEO_TRC_HLG     - HLG\n\n---------------------\n\n.. enum:: video_colorspace\n\n   YUV color space.  Can be one of the following values:\n\n   - VIDEO_CS_DEFAULT  - Equivalent to VIDEO_CS_709\n   - VIDEO_CS_601      - Rec. 601 color space\n   - VIDEO_CS_709      - Rec. 709 color space\n   - VIDEO_CS_SRGB     - sRGB color space\n   - VIDEO_CS_2100_PQ  - Rec. 2100 color space, PQ transfer\n   - VIDEO_CS_2100_HLG - Rec. 2100 color space, HLG transfer\n\n---------------------\n\n.. enum:: video_range_type\n\n   YUV color range.\n\n   - VIDEO_RANGE_DEFAULT - Equivalent to VIDEO_RANGE_PARTIAL\n   - VIDEO_RANGE_PARTIAL - Partial range\n   - VIDEO_RANGE_FULL    - Full range\n\n---------------------\n\n.. struct:: video_data\n\n   Video frame structure.\n\n.. member:: uint8_t           *video_data.data[MAX_AV_PLANES]\n.. member:: uint32_t          video_data.linesize[MAX_AV_PLANES]\n.. member:: uint64_t          video_data.timestamp\n\n---------------------\n\n.. struct:: video_output_info\n\n   Video output handler information\n\n.. member:: const char        *video_output_info.name\n.. member:: enum video_format video_output_info.format\n.. member:: uint32_t          video_output_info.fps_num\n.. member:: uint32_t          video_output_info.fps_den\n.. member:: uint32_t          video_output_info.width\n.. member:: uint32_t          video_output_info.height\n.. member:: size_t            video_output_info.cache_size\n.. member:: enum video_colorspace video_output_info.colorspace\n.. member:: enum video_range_type video_output_info.range\n\n---------------------\n\n.. function:: enum video_format video_format_from_fourcc(uint32_t fourcc)\n\n   Converts a fourcc value to a video format.\n\n   :param forcecc: Fourcc value\n   :return:        Video format\n\n---------------------\n\n.. function:: bool video_format_get_parameters(enum video_colorspace color_space, enum video_range_type range, float matrix[16], float min_range[3], float max_range[3])\n\n   Converts a color space/range to matrix/min/max values.\n\n   :param color_space: Color space to convert\n   :param range:       Color range to convert\n   :param matrix:      Pointer to the matrix\n   :param min_range:   Pointer to get the minimum range value\n   :param max_range:   Pointer to get the maximum range value\n\n---------------------\n\n.. function:: bool video_format_get_parameters_for_format(enum video_colorspace color_space, enum video_range_type range, enum video_format format, float matrix[16], float min_range[3], float max_range[3])\n\n   Converts a color space/range to matrix/min/max values for a given video format.\n\n   :param color_space: Color space to convert\n   :param range:       Color range to convert\n   :param format:      Video format\n   :param matrix:      Pointer to the matrix\n   :param min_range:   Pointer to get the minimum range value\n   :param max_range:   Pointer to get the maximum range value\n\n---------------------\n\n.. function:: bool video_output_connect(video_t *video, const struct video_scale_info *conversion, void (*callback)(void *param, struct video_data *frame), void *param)\n\n   Connects a raw video callback to the video output handler.\n\n   :param video:    Video output handler object\n   :param callback: Callback to receive video data\n   :param param:    Private data to pass to the callback\n\n---------------------\n\n.. function:: void video_output_disconnect(video_t *video, void (*callback)(void *param, struct video_data *frame), void *param)\n\n   Disconnects a raw video callback from the video output handler.\n\n   :param video:    Video output handler object\n   :param callback: Callback\n   :param param:    Private data\n\n---------------------\n\n.. function:: bool video_output_disconnect2(video_t *video, void (*callback)(void *param, struct video_data *frame), void *param)\n\n   Disconnects a raw video callback from the video output handler.\n\n   :param video:    Video output handler object\n   :param callback: Callback\n   :param param:    Private data\n   :return:         *true* if callback was removed, *false* otherwise (e.g., already removed)\n\n---------------------\n\n.. function:: const struct video_output_info *video_output_get_info(const video_t *video)\n\n   Gets the full video information of the video output handler.\n\n   :param video: Video output handler object\n   :return:      Video output info structure pointer\n\n---------------------\n\n.. function:: uint64_t video_output_get_frame_time(const video_t *video)\n\n   Gets the frame interval of the video output handler.\n\n   :param video: Video output handler object\n   :return:      Video frame interval in nanoseconds\n\n---------------------\n\n.. function:: enum video_format video_output_get_format(const video_t *video)\n\n   Gets the video format of the video output handler.\n\n   :param video: Video output handler object\n   :return:      Video format\n\n---------------------\n\n.. function:: uint32_t video_output_get_width(const video_t *video)\n.. function:: uint32_t video_output_get_height(const video_t *video)\n\n   Gets the width/height of the video output handler.\n\n   :param video: Video output handler object\n   :return:      Width/height\n\n---------------------\n\n.. function:: double video_output_get_frame_rate(const video_t *video)\n\n   Gets the frame rate (as a floating point) of the video output\n   handler.\n\n   :param video: Video output handler object\n   :return:      Frame rate\n\n---------------------\n\n.. function:: uint32_t video_output_get_skipped_frames(const video_t *video)\n\n   Gets the skipped frame count of the video output handler.\n\n   :param video: Video output handler object\n   :return:      Skipped frame count\n\n---------------------\n\n.. function:: uint32_t video_output_get_total_frames(const video_t *video)\n\n   Gets the total frames processed of the video output handler.\n\n   :param video: Video output handler object\n   :return:      Total frames processed\n\n---------------------\n\n\nAudio Handler\n-------------\n\n.. type:: audio_t\n\n---------------------\n\n.. enum:: audio_format\n\n   Audio format.  Can be one of the following values:\n\n   - AUDIO_FORMAT_UNKNOWN\n   - AUDIO_FORMAT_U8BIT\n   - AUDIO_FORMAT_16BIT\n   - AUDIO_FORMAT_32BIT\n   - AUDIO_FORMAT_FLOAT\n   - AUDIO_FORMAT_U8BIT_PLANAR\n   - AUDIO_FORMAT_16BIT_PLANAR\n   - AUDIO_FORMAT_32BIT_PLANAR\n   - AUDIO_FORMAT_FLOAT_PLANAR\n\n---------------------\n\n.. enum:: speaker_layout\n\n   Speaker layout.  Can be one of the following values:\n\n   - SPEAKERS_UNKNOWN\n   - SPEAKERS_MONO\n   - SPEAKERS_STEREO\n   - SPEAKERS_2POINT1\n   - SPEAKERS_4POINT0\n   - SPEAKERS_4POINT1\n   - SPEAKERS_5POINT1\n   - SPEAKERS_7POINT1\n\n---------------------\n\n.. struct:: audio_data\n\n   Audio data structure.\n\n.. member:: uint8_t             *audio_data.data[MAX_AV_PLANES]\n.. member:: uint32_t            audio_data.frames\n.. member:: uint64_t            audio_data.timestamp\n\n---------------------\n\n.. struct:: audio_output_data\n.. member:: float               *audio_output_data.data[MAX_AUDIO_CHANNELS]\n\n---------------------\n\n.. struct:: audio_output_info\n.. member:: const char             *audio_output_info.name\n.. member:: uint32_t               audio_output_info.samples_per_sec\n.. member:: enum audio_format      audio_output_info.format\n.. member:: enum speaker_layout    audio_output_info.speakers\n.. member:: audio_input_callback_t audio_output_info.input_callback\n.. member:: void                   *audio_output_info.input_param\n\n---------------------\n\n.. struct:: audio_convert_info\n.. member:: uint32_t            audio_convert_info.samples_per_sec\n.. member:: enum audio_format   audio_convert_info.format\n.. member:: enum speaker_layout audio_convert_info.speakers\n\n---------------------\n\n.. type:: bool (*audio_input_callback_t)(void *param, uint64_t start_ts, uint64_t end_ts, uint64_t *new_ts, uint32_t active_mixers, struct audio_output_data *mixes)\n\n   Audio input callback (typically used internally).\n\n---------------------\n\n.. function:: uint32_t get_audio_channels(enum speaker_layout speakers)\n\n   Converts a speaker layout to its audio channel count.\n\n   :param speakers: Speaker layout enumeration\n   :return:         Channel count\n\n---------------------\n\n.. function:: size_t get_audio_bytes_per_channel(enum audio_format format)\n\n   Gets the audio bytes per channel for a specific audio format.\n\n   :param format: Audio format\n   :return:       Bytes per channel\n\n---------------------\n\n.. function:: bool is_audio_planar(enum audio_format format)\n\n   Returns whether the audio format is a planar format.\n\n   :param format: Audio format\n   :return:       *true* if audio is planar, *false* otherwise\n\n---------------------\n\n.. function:: size_t get_audio_planes(enum audio_format format, enum speaker_layout speakers)\n\n   Gets the number of audio planes for a specific audio format and\n   speaker layout.\n\n   :param format:   Audio format\n   :param speakers: Speaker layout\n   :return:         Number of audio planes\n\n---------------------\n\n.. function:: size_t get_audio_size(enum audio_format format, enum speaker_layout speakers, uint32_t frames)\n\n   Gets the audio block size for a specific frame out with the given\n   format and speaker layout.\n\n   :param format:   Audio format\n   :param speakers: Speaker layout\n   :param frames:   Audio frame count\n   :return:         Audio block size\n\n---------------------\n\n.. function:: uint64_t audio_frames_to_ns(size_t sample_rate, uint64_t frames)\n\n   Helper function to convert a specific number of audio frames to\n   nanoseconds based upon its sample rate.\n\n   :param sample_rate: Sample rate\n   :param frames:      Frame count\n   :return:            Nanoseconds\n\n---------------------\n\n.. function:: uint64_t ns_to_audio_frames(size_t sample_rate, uint64_t ns)\n\n   Helper function to convert a specific number of nanoseconds to audio\n   frame count based upon its sample rate.\n\n   :param sample_rate: Sample rate\n   :param ns:          Nanoseconds\n   :return:            Frame count\n\n---------------------\n\n.. type:: void (*audio_output_callback_t)(void *param, size_t mix_idx, struct audio_data *data)\n\n   Audio output callback.  Typically used internally.\n\n---------------------\n\n.. function:: bool audio_output_connect(audio_t *audio, size_t mix_idx, const struct audio_convert_info *conversion, audio_output_callback_t callback, void *param)\n\n   Connects a raw audio callback to the audio output handler.\n   Optionally allows audio conversion if necessary.\n\n   :param audio:      Audio output handler object\n   :param mix_idx:    Mix index to get raw audio from\n   :param conversion: Audio conversion information, or *NULL* for no\n                      conversion\n   :param callback:   Raw audio callback\n   :param param:      Private data to pass to the callback\n\n---------------------\n\n.. function:: void audio_output_disconnect(audio_t *audio, size_t mix_idx, audio_output_callback_t callback, void *param)\n\n   Disconnects a raw audio callback from the audio output handler.\n\n   :param audio:      Audio output handler object\n   :param mix_idx:    Mix index to get raw audio from\n   :param callback:   Raw audio callback\n   :param param:      Private data to pass to the callback\n\n---------------------\n\n.. function:: size_t audio_output_get_block_size(const audio_t *audio)\n\n   Gets the audio block size of an audio output handler.\n\n   :param audio: Audio output handler object\n   :return:      Audio block size\n\n---------------------\n\n.. function:: size_t audio_output_get_planes(const audio_t *audio)\n\n   Gets the plane count of an audio output handler.\n\n   :param audio: Audio output handler object\n   :return:      Audio plane count\n\n---------------------\n\n.. function:: size_t audio_output_get_channels(const audio_t *audio)\n\n   Gets the channel count of an audio output handler.\n\n   :param audio: Audio output handler object\n   :return:      Audio channel count\n\n---------------------\n\n.. function:: uint32_t audio_output_get_sample_rate(const audio_t *audio)\n\n   Gets the sample rate of an audio output handler.\n\n   :param audio: Audio output handler object\n   :return:      Audio sample rate\n\n---------------------\n\n.. function:: const struct audio_output_info *audio_output_get_info(const audio_t *audio)\n\n   Gets all audio information for an audio output handler.\n\n   :param audio: Audio output handler object\n   :return:      Pointer to audio output information structure\n\n---------------------\n\n\nResampler\n---------\n\nFFmpeg wrapper to resample audio.\n\n.. type:: struct audio_resampler audio_resampler_t\n\n---------------------\n\n.. struct:: resample_info\n.. member:: uint32_t            resample_info.samples_per_sec\n.. member:: enum audio_format   resample_info.format\n.. member:: enum speaker_layout resample_info.speakers\n\n---------------------\n\n.. function:: audio_resampler_t *audio_resampler_create(const struct resample_info *dst, const struct resample_info *src)\n\n   Creates an audio resampler.\n\n   :param dst: Destination audio information\n   :param src: Source audio information\n   :return:    Audio resampler object\n\n---------------------\n\n.. function:: void audio_resampler_destroy(audio_resampler_t *resampler)\n\n   Destroys an audio resampler.\n\n   :param resampler: Audio resampler object\n\n---------------------\n\n.. function:: bool audio_resampler_resample(audio_resampler_t *resampler, uint8_t *output[], uint32_t *out_frames, uint64_t *ts_offset, const uint8_t *const input[], uint32_t in_frames)\n\n   Resamples audio frames.\n\n   :param resampler:   Audio resampler object\n   :param output:      Pointer to receive converted audio frames\n   :param out_frames:  Pointer to receive converted audio frame count\n   :param ts_offset:   Pointer to receive timestamp offset (in\n                       nanoseconds)\n   :param input: Input frames to convert\n   :param in_frames:   Input frame count\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-base.rst",
    "content": "Logging\n=======\n\nFunctions for logging and getting log data.\n\n.. code:: cpp\n\n   #include <util/base.h>\n\n\nLogging Levels\n--------------\n\n**LOG_ERROR** = 100\n\n   Use if there's a problem that can potentially affect the program,\n   but isn't enough to require termination of the program.\n\n   Use in creation functions and core subsystem functions.  Places that\n   should definitely not fail.\n\n**LOG_WARNING** = 200\n\n   Use if a problem occurs that doesn't affect the program and is\n   recoverable.\n\n   Use in places where failure isn't entirely unexpected, and can\n   be handled safely.\n\n**LOG_INFO** = 300\n\n   Informative message to be displayed in the log.\n\n**LOG_DEBUG** = 400\n\n   Debug message to be used mostly by and for developers.\n\n\nLogging Functions\n-----------------\n\n.. type:: void (*log_handler_t)(int lvl, const char *msg, va_list args, void *p)\n\n   Logging callback.\n\n---------------------\n\n.. function:: void base_set_log_handler(log_handler_t handler, void *param)\n              void base_get_log_handler(log_handler_t *handler, void **param)\n\n   Sets/gets the current log handler.\n\n---------------------\n\n.. function:: void base_set_crash_handler(void (*handler)(const char *, va_list, void *), void *param)\n\n   Sets the current crash handler.\n\n---------------------\n\n.. function:: void blogva(int log_level, const char *format, va_list args)\n\n   Logging function (using a va_list).\n\n---------------------\n\n.. function:: void blog(int log_level, const char *format, ...)\n\n   Logging function.\n\n---------------------\n\n.. function:: void bcrash(const char *format, ...)\n\n   Crash function.\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-bmem.rst",
    "content": "Memory Management\n=================\n\nVarious functions and helpers used for memory management.\n\n.. code:: cpp\n\n   #include <util/bmem.h>\n\n\nMemory Functions\n----------------\n\n.. function:: void *bmalloc(size_t size)\n\n   Allocates memory and increases the memory leak counter.\n\n---------------------\n\n.. function:: void *brealloc(void *ptr, size_t size)\n\n   Reallocates memory.  Use only with memory that's been allocated by\n   :c:func:`bmalloc()`.\n\n---------------------\n\n.. function:: void bfree(void *ptr)\n\n   Frees memory allocated with :c:func:`bmalloc()`.\n\n---------------------\n\n.. function:: long bnum_allocs(void)\n\n   Returns current number of active allocations.\n\n---------------------\n\n.. function:: void *bmemdup(const void *ptr, size_t size)\n\n   Duplicates memory.\n\n---------------------\n\n.. function:: void *bzalloc(size_t size)\n\n   Inline function that allocates zeroed memory.\n\n---------------------\n\n.. function:: char *bstrdup_n(const char *str, size_t n)\n              wchar_t *bwstrdup_n(const wchar_t *str, size_t n)\n\n   Duplicates a string of *n* bytes and automatically zero-terminates\n   it.\n\n---------------------\n\n.. function:: char *bstrdup(const char *str)\n              wchar_t *bwstrdup(const wchar_t *str)\n\n   Duplicates a string.\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-config-file.rst",
    "content": "Config Files\n============\n\nThe configuration file functions are a simple implementation of the INI\nfile format, with the addition of default values.\n\n.. code:: cpp\n\n   #include <util/config-file.h>\n\n.. type:: config_t\n\n\nConfig File Functions\n---------------------\n\n.. function:: config_t *config_create(const char *file)\n\n   Creates a new configuration object and associates it with the\n   specified file name.\n\n   :param file: Path to the new configuration file\n   :return:     A new configuration file object\n\n----------------------\n\n.. function:: int config_open(config_t **config, const char *file, enum config_open_type open_type)\n\n   Opens a configuration file.\n\n   :param config:    Pointer that receives a pointer to a new configuration\n                     file object (if successful)\n   :param file:      Path to the configuration file\n   :param open_type: Can be one of the following values:\n\n                     - CONFIG_OPEN_EXISTING - Fail if the file doesn't\n                       exist.\n                     - CONFIG_OPEN_ALWAYS - Try to open the file.  If\n                       the file doesn't exist, create it.\n\n   :return:          Can return the following values:\n\n                     - CONFIG_SUCCESS - Successful\n                     - CONFIG_FILENOTFOUND - File not found\n                     - CONFIG_ERROR - Generic error\n\n----------------------\n\n.. function:: int config_open_string(config_t **config, const char *str)\n\n   Opens configuration data via a string rather than a file.\n\n   :param config:    Pointer that receives a pointer to a new configuration\n                     file object (if successful)\n   :param str:       Configuration string\n\n   :return:          Can return the following values:\n\n                     - CONFIG_SUCCESS - Successful\n                     - CONFIG_FILENOTFOUND - File not found\n                     - CONFIG_ERROR - Generic error\n\n----------------------\n\n.. function:: int config_save(config_t *config)\n\n   Saves configuration data to a file (if associated with a file).\n\n   :param config:    Configuration object\n\n   :return:          Can return the following values:\n\n                     - CONFIG_SUCCESS - Successful\n                     - CONFIG_FILENOTFOUND - File not found\n                     - CONFIG_ERROR - Generic error\n\n----------------------\n\n.. function:: int config_save_safe(config_t *config, const char *temp_ext, const char *backup_ext)\n\n   Saves configuration data and minimizes overwrite corruption risk.\n   Saves the file with the file name\n\n   :param config:     Configuration object\n   :param temp_ext:   Temporary extension for the new file\n   :param backup_ext: Backup extension for the old file.  Can be *NULL*\n                      if no backup is desired.\n\n   :return:           Can return the following values:\n\n                      - CONFIG_SUCCESS - Successful\n                      - CONFIG_FILENOTFOUND - File not found\n                      - CONFIG_ERROR - Generic error\n\n----------------------\n\n.. function:: void config_close(config_t *config)\n\n   Closes the configuration object.\n\n   :param config:     Configuration object\n\n----------------------\n\n.. function:: size_t config_num_sections(config_t *config)\n\n   Returns the number of sections.\n\n   :param config:     Configuration object\n   :return:           Number of configuration sections\n\n----------------------\n\n.. function:: const char *config_get_section(config_t *config, size_t idx)\n\n   Returns a section name based upon its index.\n\n   :param config:     Configuration object\n   :param idx:        Index of the section\n   :return:           The section's name\n\nSet/Get Functions\n-----------------\n\n.. function:: void config_set_string(config_t *config, const char *section, const char *name, const char *value)\n\n   Sets a string value.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :param value:      The string value\n\n----------------------\n\n.. function:: void config_set_int(config_t *config, const char *section, const char *name, int64_t value)\n\n   Sets an integer value.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :param value:      The integer value\n\n----------------------\n\n.. function:: void config_set_uint(config_t *config, const char *section, const char *name, uint64_t value)\n\n   Sets an unsigned integer value.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :param value:      The unsigned integer value\n\n----------------------\n\n.. function:: void config_set_bool(config_t *config, const char *section, const char *name, bool value)\n\n   Sets a boolean value.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :param value:      The boolean value\n\n----------------------\n\n.. function:: void config_set_double(config_t *config, const char *section, const char *name, double value)\n\n   Sets a floating point value.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :param value:      The floating point value\n\n----------------------\n\n.. function:: const char *config_get_string(config_t *config, const char *section, const char *name)\n\n   Gets a string value.  If the value is not set, it will use the\n   default value.  If there is no default value, it will return *NULL*.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           The string value\n\n----------------------\n\n.. function:: int64_t config_get_int(config_t *config, const char *section, const char *name)\n\n   Gets an integer value.  If the value is not set, it will use the\n   default value.  If there is no default value, it will return 0.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           The integer value\n\n----------------------\n\n.. function:: uint64_t config_get_uint(config_t *config, const char *section, const char *name)\n\n   Gets an unsigned integer value.  If the value is not set, it will use\n   the default value.  If there is no default value, it will return 0.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           The unsigned integer value\n\n----------------------\n\n.. function:: bool config_get_bool(config_t *config, const char *section, const char *name)\n\n   Gets a boolean value.  If the value is not set, it will use the\n   default value.  If there is no default value, it will return false.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           The boolean value\n\n----------------------\n\n.. function:: double config_get_double(config_t *config, const char *section, const char *name)\n\n   Gets a floating point value.  If the value is not set, it will use\n   the default value.  If there is no default value, it will return 0.0.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           The floating point value\n\n----------------------\n\n.. function:: bool config_remove_value(config_t *config, const char *section, const char *name)\n\n   Removes a value.  Does not remove the default value if any.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n\n\nDefault Value Functions\n-----------------------\n\n.. function:: int config_open_defaults(config_t *config, const char *file)\n\n   Opens a file and uses it for default values.\n\n   :param config:     Configuration object\n   :param file:       The file to open for default values\n\n----------------------\n\n.. function:: void config_set_default_string(config_t *config, const char *section, const char *name, const char *value)\n\n   Sets a default string value.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :param value:      The string value\n\n----------------------\n\n.. function:: void config_set_default_int(config_t *config, const char *section, const char *name, int64_t value)\n\n   Sets a default integer value.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :param value:      The integer value\n\n----------------------\n\n.. function:: void config_set_default_uint(config_t *config, const char *section, const char *name, uint64_t value)\n\n   Sets a default unsigned integer value.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :param value:      The unsigned integer value\n\n----------------------\n\n.. function:: void config_set_default_bool(config_t *config, const char *section, const char *name, bool value)\n\n   Sets a default boolean value.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :param value:      The boolean value\n\n----------------------\n\n.. function:: void config_set_default_double(config_t *config, const char *section, const char *name, double value)\n\n   Sets a default floating point value.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :param value:      The floating point value\n\n----------------------\n\n.. function:: const char *config_get_default_string(config_t *config, const char *section, const char *name)\n\n   Gets a default string value.  If there is no default value, it will return *NULL*.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           The default string value\n\n----------------------\n\n.. function:: int64_t config_get_default_int(config_t *config, const char *section, const char *name)\n\n   Gets a default integer value.  If there is no default value, it will return 0.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           The integer value\n\n----------------------\n\n.. function:: uint64_t config_get_default_uint(config_t *config, const char *section, const char *name)\n\n   Gets a default unsigned integer value.  If there is no default value, it will return 0.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           The unsigned integer value\n\n----------------------\n\n.. function:: bool config_get_default_bool(config_t *config, const char *section, const char *name)\n\n   Gets a default boolean value.  If there is no default value, it will return false.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           The boolean value\n\n----------------------\n\n.. function:: double config_get_default_double(config_t *config, const char *section, const char *name)\n\n   Gets a default floating point value.  If there is no default value, it will return 0.0.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           The floating point value\n\n-----------------------\n\n.. function:: bool config_has_user_value(config_t *config, const char *section, const char *name)\n\n  Returns whether a value is user-set (true) or default/none (false).\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           Whether a user value exists\n\n-----------------------\n\n.. function:: bool config_has_default_value(config_t *config, const char *section, const char *name)\n\n  Returns whether a value has a default set.\n\n   :param config:     Configuration object\n   :param section:    The section of the value\n   :param name:       The value name\n   :return:           Whether a user value exists\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-darray.rst",
    "content": "Dynamic Arrays\n==============\n\nDynamically resizing arrays (a C equivalent to std::vector).\n\n.. code:: cpp\n\n   #include <util/darray.h>\n\n.. struct:: darray\n\n   The base dynamic array structure.\n\n.. macro:: DARRAY(type)\n\n   Macro for a dynamic array based upon an actual type.  Use this with\n   da_* macros.\n\n.. member:: void *darray.array\n\n   The array pointer.\n\n.. member:: size_t darray.num\n\n   The number of items within the array.\n\n.. member:: size_t darray.capacity\n\n   The capacity of the array.\n\n\nDynamic Array Macros\n--------------------\n\nThese macro functions are used with variables created with the\n**DARRAY** type macro.  When using these functions, do not use the\ndynamic array value with a reference (&) operator.  For example:\n\n.. code:: cpp\n\n   /* creates an array of integers: 0..9 */\n   DARRAY(int) array_of_integers;\n   da_init(array_of_integers);\n\n   for (size_t i = 0; i < 10; i++)\n           da_push_back(array_of_integers, &i);\n\n   [...]\n\n   /* free when complete */\n   da_free(array_of_integers);\n\nTo pass dynamic arrays to functions as parameters, create a typedef with the\n:c:macro:`DARRAY` macro and type so you can pass the dynamic array with a\nreference (&) operator. An alternative is to define a struct that will contain\nthe dynamic array, and that struct will be passed to the functions, instead of\nthe dynamic array directly. An example with the typedef method:\n\n.. code:: cpp\n\n   typedef DARRAY(int) int_array_t;\n\n   void generate_integers(int_array_t *integers, int start, int end)\n   {\n           for (int i = start; i < end; i++)\n                   da_push_back(*integers, &i);\n   }\n\n   [...]\n\n   int_array_t array_of_integers;\n   da_init(array_of_integers);\n\n   generate_integers(&array_of_integers, 0, 10);\n\n   /* free when complete */\n   da_free(array_of_integers);\n\n**IMPORTANT NOTE:** While it is also possible to accept the internal\n:c:struct:`darray` struct as a function parameter (via the ``da`` member\nvariable of dynamic arrays) and redefine a variable with :c:macro:`DARRAY`\ninside the function, doing so is not safe and not recommended. One potential\nissue with it is having a type declaration in the function that is different\nthan the type of the actual dynamic array that will be passed to the function,\nwhich will cause memory access issues that will not be caught by the compiler.\nAs mentioned above, the recommended way is to create a typedef or a container\nstruct, which will be safer in usage.\n\n.. function:: void da_init(da)\n\n   Initializes a dynamic array.\n\n   :param da: The dynamic array\n\n---------------------\n\n.. function:: void da_free(da)\n\n   Frees a dynamic array.\n\n   :param da: The dynamic array\n\n---------------------\n\n.. function:: size_t da_alloc_size(v)\n\n   Gets a size of allocated array in bytes.\n\n   :param da: The dynamic array\n   :return:   The allocated size of the dynamic array.\n\n---------------------\n\n.. function:: void *da_end(da)\n\n   Gets a pointer to the last value.\n\n   :param da: The dynamic array\n   :return:   The last value of a dynamic array, or *NULL* if empty.\n\n---------------------\n\n.. function:: void da_reserve(da, size_t capacity)\n\n   Reserves a specific amount of buffer space for the dynamic array.\n\n   :param da:       The dynamic array\n   :param capacity: New capacity of the dynamic array\n\n---------------------\n\n.. function:: void da_resize(da, size_t new_size)\n\n   Resizes the dynamic array with zeroed values.\n\n   :param da:   The dynamic array\n   :param size: New size of the dynamic array\n\n---------------------\n\n.. function:: void da_copy(da_dst, da_src)\n\n   Makes a copy of a dynamic array.\n\n   :param da_dst: The dynamic array to copy to\n   :param da_src: The dynamic array to copy from\n\n---------------------\n\n.. function:: void da_copy_array(da, const void *src_array, size_t size)\n\n   Makes a copy of an array pointer.\n\n   :param da:        The dynamic array\n   :param src_array: The array pointer to make a copy from\n   :param size:      New size of the dynamic array\n\n---------------------\n\n.. function:: void da_move(da_dst, da_src)\n\n   Moves one dynamic array variable to another without allocating new\n   data.  *da_dst* is freed before moving, *da_dst* is set to *da_src*,\n   then *da_src* is then zeroed.\n\n   :param da_dst: Destination variable\n   :param da_src: Source variable\n\n---------------------\n\n.. function:: size_t da_find(da, const void *item_data, size_t starting_idx)\n\n   Finds a value based upon its data.  If the value cannot be found, the\n   return value will be DARRAY_INVALID (-1).\n\n   :param da:           The dynamic array\n   :param item_data:    The item data to find\n   :param starting_idx: The index to start from or 0 to search the\n                        entire array\n\n---------------------\n\n.. function:: size_t da_push_back(da, const void *data)\n\n   Pushes data to the back of the array.\n\n   :param da:   The dynamic array\n   :param data: Pointer to the new data to push\n   :return:     Index of the new value\n\n---------------------\n\n.. function:: void *da_push_back_new(da)\n\n   Pushes a zeroed value to the back of the array, and returns a pointer\n   to it.\n\n   :param da: The dynamic array\n   :return:   Pointer to the new value\n\n---------------------\n\n.. function:: size_t da_push_back_array(da, const void *src_array, size_t item_count)\n\n   Pushes an array of values to the back of the array.\n\n   :param da:         The dynamic array\n   :param src_array:  Pointer of the array of values\n   :param item_count: Number of items to push back\n   :return:           Index of the first new value\n\n---------------------\n\n.. function:: void da_insert(da, size_t idx, const void *data)\n\n   Inserts a value at a given index.\n\n   :param da:   The dynamic array:\n   :param idx:  Index where the new item will be inserted\n   :param data: Pointer to the item data to insert\n\n---------------------\n\n.. function:: void *da_insert_new(da, size_t idx)\n\n   Inserts a new zeroed value at a specific index, and returns a pointer\n   to it.\n\n   :param da:  The dynamic array\n   :param idx: Index to insert at\n   :return:    Pointer to the new value\n\n---------------------\n\n.. function:: void da_insert_array(dst, size_t idx, src, size_t n)\n\n   Inserts one or more items at a given index.\n\n   :param dst: The dynamic array:\n   :param idx: Index where the new item will be inserted\n   :param src: Pointer to the first item to insert\n   :param n:   Number of items to insert\n\n---------------------\n\n.. function:: void da_insert_da(da_dst, size_t idx, da_src)\n\n   Inserts a dynamic array in to another dynamic array at a specific\n   index.\n\n   :param da_dst: Destination dynamic array being inserted in to\n   :param idx:    Index to insert the data at\n   :param da_src: The dynamic array to insert\n\n---------------------\n\n.. function:: void da_erase(da, size_t idx)\n\n   Erases an item at a specific index.\n\n   :param da:  The dynamic array\n   :param idx: The index of the value to remove\n\n---------------------\n\n.. function:: void da_erase_item(da, const void *item_data)\n\n   Erases an item that matches the value specified\n\n   :param da:        The dynamic array\n   :param item_data: Pointer to the data to remove\n\n---------------------\n\n.. function:: void da_erase_range(da, size_t start_idx, size_t end_idx)\n\n   Erases a range of values, including the element at ``start_idx``, but\n   not the one at ``end_idx``.\n\n   :param da:        The dynamic array\n   :param start_idx: The starting index\n   :param end_idx:   The ending index\n\n---------------------\n\n.. function:: void da_pop_back(da)\n\n   Removes one item from the end of a dynamic array.\n\n   :param da: The dynamic array\n\n---------------------\n\n.. function:: void da_join(da_dst, da_src)\n\n   Pushes *da_src* to the end of *da_dst* and frees *da_src*.\n\n   :param da_dst: The destination dynamic array\n   :param da_src: The source dynamic array\n\n---------------------\n\n.. function:: void da_split(da_dst1, da_dst2, da_src, size_t split_idx)\n\n   Creates two dynamic arrays by splitting another dynamic array at a\n   specific index.  If the destination arrays are not freed, they will\n   be freed before getting their new values.  The array being split will\n   not be freed.\n\n   :param da_dst1:   Dynamic array that will get the lower half\n   :param da_dst2:   Dynamic array that will get the upper half\n   :param da_src:    Dynamic array to split\n   :param split_idx: Index to split *da_src* at\n\n---------------------\n\n.. function:: void da_move_item(da, size_t src_idx, size_t dst_idx)\n\n   Moves an item from one index to another, moving data between if\n   necessary.\n\n   :param da:      The dynamic array\n   :param src_idx: The index of the item to move\n   :param dst_idx: The new index of where the item will be moved to\n\n---------------------\n\n.. function:: void da_swap(da, size_t idx1, size_t idx2)\n\n   Swaps two values at the given indices.\n\n   :param da: The dynamic array\n   :param idx1: Index of the first item to swap\n   :param idx2: Index of the second item to swap\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-deque.rst",
    "content": "Double-Ended Queue\n==================\n\nA double-ended queue (deque) that will automatically increase in size as necessary\nas data is pushed to the front or back.\n\n.. code:: cpp\n\n   #include <util/deque.h>\n\n.. versionadded:: 30.1\n\n\nDeque Structure (struct deque)\n--------------------------------------------\n\n.. struct:: deque\n.. member:: void   *deque.data\n.. member:: size_t deque.size\n.. member:: size_t deque.start_pos\n.. member:: size_t deque.end_pos\n.. member:: size_t deque.capacity\n\n\nDeque Inline Functions\n--------------------------------\n\n.. function:: void deque_init(struct deque *dq)\n\n   Initializes a deque (just zeroes out the entire structure).\n\n   :param dq: The deque\n\n---------------------\n\n.. function:: void deque_free(struct deque *dq)\n\n   Frees a deque.\n\n   :param dq: The deque\n\n---------------------\n\n.. function:: void deque_reserve(struct deque *dq, size_t capacity)\n\n   Reserves a specific amount of buffer space to ensure minimum\n   upsizing.\n\n   :param dq:       The deque\n   :param capacity: The new capacity, in bytes\n\n---------------------\n\n.. function:: void deque_upsize(struct deque *dq, size_t size)\n\n   Sets the current active (not just reserved) size.  Any new data is\n   zeroed.\n\n   :param dq:       The deque\n   :param size:     The new size, in bytes\n\n---------------------\n\n.. function:: void deque_place(struct deque *dq, size_t position, const void *data, size_t size)\n\n   Places data at a specific positional index (relative to the starting\n   point) within the deque.\n\n   :param dq:       The deque\n   :param position: Positional index relative to starting point\n   :param data:     Data to insert\n   :param size:     Size of data to insert\n\n---------------------\n\n.. function:: void deque_push_back(struct deque *dq, const void *data, size_t size)\n\n   Pushes data to the end of the deque.\n\n   :param dq:       The deque\n   :param data:     Data\n   :param size:     Size of data\n\n---------------------\n\n.. function:: void deque_push_front(struct deque *dq, const void *data, size_t size)\n\n   Pushes data to the front of the deque.\n\n   :param dq:       The deque\n   :param data:     Data\n   :param size:     Size of data\n\n---------------------\n\n.. function:: void deque_push_back_zero(struct deque *dq, size_t size)\n\n   Pushes zeroed data to the end of the deque.\n\n   :param dq:       The deque\n   :param size:     Size\n\n---------------------\n\n.. function:: void deque_push_front_zero(struct deque *dq, size_t size)\n\n   Pushes zeroed data to the front of the deque.\n\n   :param dq:       The deque\n   :param size:     Size\n\n---------------------\n\n.. function:: void deque_peek_front(struct deque *dq, void *data, size_t size)\n\n   Peeks data at the front of the deque.\n\n   :param dq:       The deque\n   :param data:     Buffer to store data in\n   :param size:     Size of data to retrieve\n\n---------------------\n\n.. function:: void deque_peek_back(struct deque *dq, void *data, size_t size)\n\n   Peeks data at the back of the deque.\n\n   :param dq:       The deque\n   :param data:     Buffer to store data in\n   :param size:     Size of data to retrieve\n\n---------------------\n\n.. function:: void deque_pop_front(struct deque *dq, void *data, size_t size)\n\n   Pops data from the front of the deque.\n\n   :param dq:       The deque\n   :param data:     Buffer to store data in, or *NULL*\n   :param size:     Size of data to retrieve\n\n---------------------\n\n.. function:: void deque_pop_back(struct deque *dq, void *data, size_t size)\n\n   Pops data from the back of the deque.\n\n   :param dq:       The deque\n   :param data:     Buffer to store data in, or *NULL*\n   :param size:     Size of data to retrieve\n\n---------------------\n\n.. function:: void *deque_data(struct deque *dq, size_t idx)\n\n   Gets a direct pointer to data at a specific positional index within\n   the deque, relative to the starting point.\n\n   :param dq:       The deque\n   :param idx:      Byte index relative to the starting point\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-dstr.rst",
    "content": "Dynamic Strings And String Helpers\n==================================\n\nProvides string helper structures/functions (roughly equivalent to\nstd::string).\n\n.. code:: cpp\n\n   #include <util/dstr.h>\n\n\nDynamic String Structure (struct dstr)\n--------------------------------------\n\n.. struct:: dstr\n.. member:: char *dstr.array\n.. member:: size_t dstr.len\n.. member:: size_t dstr.capacity\n\n\nGeneral String Helper Functions\n-------------------------------\n\n.. function:: int astrcmpi(const char *str1, const char *str2)\n\n   Case insensitive string comparison function.\n\n----------------------\n\n.. function:: int wstrcmpi(const wchar_t *str1, const wchar_t *str2)\n\n   Case insensitive wide string comparison function.\n\n----------------------\n\n.. function:: int astrcmp_n(const char *str1, const char *str2, size_t n)\n\n   String comparison function for a specific number of characters.\n\n----------------------\n\n.. function:: int wstrcmp_n(const wchar_t *str1, const wchar_t *str2, size_t n)\n\n   Wide string comparison function for a specific number of characters.\n\n----------------------\n\n.. function:: int astrcmpi_n(const char *str1, const char *str2, size_t n)\n\n   Case insensitive string comparison function for a specific number of\n   characters.\n\n----------------------\n\n.. function:: int wstrcmpi_n(const wchar_t *str1, const wchar_t *str2, size_t n)\n\n   Case insensitive wide string comparison function for a specific\n   number of characters.\n\n----------------------\n\n.. function:: char *astrstri(const char *str, const char *find)\n\n   Case insensitive version of strstr.\n\n----------------------\n\n.. function:: wchar_t *wstrstri(const wchar_t *str, const wchar_t *find)\n\n   Case insensitive version of wcsstr.\n\n----------------------\n\n.. function:: char *strdepad(char *str)\n\n   Removes padding characters (tab, space, CR, LF) from the front and\n   end of a string.\n\n----------------------\n\n.. function:: wchar_t *wcsdepad(wchar_t *str)\n\n   Removes padding characters (tab, space, CR, LF) from the front and\n   end of a wide string.\n\n----------------------\n\n.. function:: char **strlist_split(const char *str, char split_ch, bool include_empty)\n\n   Splits a string in to a list of multiple sub-strings, terminated by\n   ``NULL``. If ``split_ch`` does not exist in the string, the first\n   sub-string will be the same as ``str``. Free with :c:func:`strlist_free()`.\n\n   :param str: The string to be split\n   :param split_ch: The delimiter\n   :param include_empty: If *true*, empty strings caused by having the\n                         ``split_ch`` right next to another will be\n                         included in the list. If *false*, they won't\n                         be included.\n\n   Sample usage:\n\n   .. code:: cpp\n\n      char **words = strlist_split(\"OBS    Studio\", ' ', false);\n      int count = 0;\n\n      for (char **word = words; *word; ++word) {\n              count++;\n              blog(LOG_DEBUG, \"%s\", *word);\n      }\n\n      strlist_free(words);\n      // count == 2\n\n----------------------\n\n.. function:: void strlist_free(char **strlist)\n\n   Frees a string list created with :c:func:`strlist_split()`.\n\n---------------------\n\n\nDynamic String Functions\n------------------------\n\n.. function:: void dstr_init(struct dstr *dst)\n\n   Initializes a dynamic string variable (just zeroes the variable).\n\n   :param dst: Dynamic string to initialize\n\n----------------------\n\n.. function:: void dstr_init_move(struct dstr *dst, struct dstr *src)\n\n   Moves a *src* to *dst* without copying data and zeroes *src*.\n\n   :param dst: Destination\n   :param src: Source\n\n----------------------\n\n.. function:: void dstr_init_move_array(struct dstr *dst, char *str)\n\n   Sets a bmalloc-allocated string as the dynamic string without\n   copying/reallocating.\n\n   :param dst: Dynamic string to initialize\n   :param str: bmalloc-allocated string\n\n----------------------\n\n.. function:: void dstr_init_copy(struct dstr *dst, const char *src)\n\n   Initializes a dynamic string with a copy of a string\n\n   :param dst: Dynamic string to initialize\n   :param src: String to copy\n\n----------------------\n\n.. function:: void dstr_init_copy_dstr(struct dstr *dst, const struct dstr *src)\n\n   Initializes a dynamic string with a copy of another dynamic string\n\n   :param dst: Dynamic string to initialize\n   :param src: Dynamic string to copy\n\n----------------------\n\n.. function:: void dstr_free(struct dstr *dst)\n\n   Frees a dynamic string.\n\n   :param dst: Dynamic string\n\n----------------------\n\n.. function:: void dstr_copy(struct dstr *dst, const char *array)\n\n   Copies a string.\n\n   :param dst:   Dynamic string\n   :param array: String to copy\n\n----------------------\n\n.. function:: void dstr_copy_dstr(struct dstr *dst, const struct dstr *src)\n\n   Copies another dynamic string.\n\n   :param dst: Dynamic string\n   :param src: Dynamic string to copy\n\n----------------------\n\n.. function:: void dstr_ncopy(struct dstr *dst, const char *array, const size_t len)\n\n   Copies a specific number of characters from a string.\n\n   :param dst:   Dynamic string\n   :param array: String to copy\n   :param len:   Number of characters to copy\n\n----------------------\n\n.. function:: void dstr_ncopy_dstr(struct dstr *dst, const struct dstr *src, const size_t len)\n\n   Copies a specific number of characters from another dynamic string.\n\n   :param dst:   Dynamic string\n   :param src:   Dynamic string to copy\n   :param len:   Number of characters to copy\n\n----------------------\n\n.. function:: void dstr_resize(struct dstr *dst, const size_t num)\n\n   Sets the size of the dynamic string.  If the new size is bigger than\n   current size, zeroes the new characters.\n\n   :param dst: Dynamic string\n   :param num: New size\n\n----------------------\n\n.. function:: void dstr_reserve(struct dstr *dst, const size_t num)\n\n   Reserves a specific number of characters in the buffer (but does not\n   change the size).  Does not work if the value is smaller than the\n   current reserve size.\n\n   :param dst: Dynamic string\n   :param num: New reserve size\n\n----------------------\n\n.. function:: bool dstr_is_empty(const struct dstr *str)\n\n   Returns whether the dynamic string is empty.\n\n   :param str: Dynamic string\n   :return:    *true* if empty, *false* otherwise\n\n----------------------\n\n.. function:: void dstr_cat(struct dstr *dst, const char *array)\n\n   Concatenates a dynamic string.\n\n   :param dst:   Dynamic string\n   :param array: String to concatenate with\n\n----------------------\n\n.. function:: void dstr_cat_dstr(struct dstr *dst, const struct dstr *str)\n\n   Concatenates a dynamic string with another dynamic string.\n\n   :param dst: Dynamic string to concatenate to\n   :param str: Dynamic string to concatenate with\n\n----------------------\n\n.. function:: void dstr_cat_ch(struct dstr *dst, char ch)\n\n   Concatenates a dynamic string with a single character.\n\n   :param dst: Dynamic string to concatenate to\n   :param ch:  Character to concatenate\n\n----------------------\n\n.. function:: void dstr_ncat(struct dstr *dst, const char *array, const size_t len)\n\n   Concatenates a dynamic string with a specific number of characters\n   from a string.\n\n   :param dst:   Dynamic string to concatenate to\n   :param array: String to concatenate with\n   :param len:   Number of characters to concatenate with\n\n----------------------\n\n.. function:: void dstr_ncat_dstr(struct dstr *dst, const struct dstr *str, const size_t len)\n\n   Concatenates a dynamic string with a specific number of characters\n   from another dynamic string.\n\n   :param dst: Dynamic string to concatenate to\n   :param str: Dynamic string to concatenate with\n   :param len: Number of characters to concatenate with\n\n----------------------\n\n.. function:: void dstr_insert(struct dstr *dst, const size_t idx, const char *array)\n\n   Inserts a string in to a dynamic string at a specific index.\n\n   :param dst:   Dynamic string to insert in to\n   :param idx:   Character index to insert at\n   :param array: String to insert\n\n----------------------\n\n.. function:: void dstr_insert_dstr(struct dstr *dst, const size_t idx, const struct dstr *str)\n\n   Inserts another dynamic string in to a dynamic string at a specific\n   index.\n\n   :param dst:   Dynamic string to insert in to\n   :param idx:   Character index to insert at\n   :param str:   Dynamic string to insert\n\n----------------------\n\n.. function:: void dstr_insert_ch(struct dstr *dst, const size_t idx, const char ch)\n\n   Inserts a character in to a dynamic string at a specific index.\n\n   :param dst:   Dynamic string to insert in to\n   :param idx:   Character index to insert at\n   :param ch:    Character to insert\n\n----------------------\n\n.. function:: void dstr_remove(struct dstr *dst, const size_t idx, const size_t count)\n\n   Removes a specific number of characters starting from a specific\n   index.\n\n   :param dst:   Dynamic string\n   :param idx:   Index to start removing characters at\n   :param count: Number of characters to remove\n\n----------------------\n\n.. function:: void dstr_printf(struct dstr *dst, const char *format, ...)\n              void dstr_vprintf(struct dstr *dst, const char *format, va_list args)\n\n   Sets a dynamic string to a formatted string.\n\n   :param dst:    Dynamic string\n   :param format: Format string\n\n----------------------\n\n.. function:: void dstr_catf(struct dstr *dst, const char *format, ...)\n              void dstr_vcatf(struct dstr *dst, const char *format, va_list args)\n\n   Concatenates a dynamic string with a formatted string.\n\n   :param dst:    Dynamic string\n   :param format: Format string\n\n----------------------\n\n.. function:: const char *dstr_find_i(const struct dstr *str, const char *find)\n\n   Finds a string within a dynamic string, case insensitive.\n\n   :param str:  Dynamic string\n   :param find: String to find\n   :return:     Pointer to the first occurrence, or *NULL* if not found\n\n----------------------\n\n.. function:: const char *dstr_find(const struct dstr *str, const char *find)\n\n   Finds a string within a dynamic string.\n\n   :param str:  Dynamic string\n   :param find: String to find\n   :return:     Pointer to the first occurrence, or *NULL* if not found\n\n----------------------\n\n.. function:: void dstr_replace(struct dstr *str, const char *find, const char *replace)\n\n   Replaces all occurrences of *find* with *replace*.\n\n   :param str:     Dynamic string\n   :param find:    String to find\n   :param replace: Replacement string\n\n----------------------\n\n.. function:: int dstr_cmp(const struct dstr *str1, const char *str2)\n\n   Compares with a string.\n\n   :param str1: Dynamic string\n   :param str2: String to compare\n   :return:     0 if equal, nonzero otherwise\n\n----------------------\n\n.. function:: int dstr_cmpi(const struct dstr *str1, const char *str2)\n\n   Compares with a string, case-insensitive.\n\n   :param str1: Dynamic string\n   :param str2: String to compare\n   :return:     0 if equal, nonzero otherwise\n\n----------------------\n\n.. function:: int dstr_ncmp(const struct dstr *str1, const char *str2, const size_t n)\n\n   Compares a specific number of characters.\n\n   :param str1: Dynamic string\n   :param str2: String to compare\n   :param n:    Number of characters to compare\n   :return:     0 if equal, nonzero otherwise\n\n----------------------\n\n.. function:: int dstr_ncmpi(const struct dstr *str1, const char *str2, const size_t n)\n\n   Compares a specific number of characters, case-insensitive.\n\n   :param str1: Dynamic string\n   :param str2: String to compare\n   :param n:    Number of characters to compare\n   :return:     0 if equal, nonzero otherwise\n\n----------------------\n\n.. function:: void dstr_depad(struct dstr *dst)\n\n   Removes all padding characters (tabs, spaces, CR, LF) from the front\n   and ends of a dynamic string.\n\n   :param dst: Dynamic string\n\n----------------------\n\n.. function:: void dstr_left(struct dstr *dst, const struct dstr *str, const size_t pos)\n\n   Copies a certain number of characters from the left side of one\n   dynamic string in to another.\n\n   :param dst:   Destination\n   :param str:   Source\n   :param pos:   Number of characters\n\n----------------------\n\n.. function:: void dstr_mid(struct dstr *dst, const struct dstr *str, const size_t start, const size_t count)\n\n   Copies a certain number of characters from the middle of one dynamic\n   string in to another.\n\n   :param dst:   Destination\n   :param str:   Source\n   :param start: Starting index within *str*\n   :param count: Number of characters to copy\n\n----------------------\n\n.. function:: void dstr_right(struct dstr *dst, const struct dstr *str, const size_t pos)\n\n   Copies a certain number of characters from the right of one dynamic\n   string in to another.\n\n   :param dst:   Destination\n   :param str:   Source\n   :param pos:   Index of *str* to copy from\n\n----------------------\n\n.. function:: char dstr_end(const struct dstr *str)\n\n   :param str: Dynamic string\n   :return:    The last character of a dynamic string\n\n----------------------\n\n.. function:: void dstr_from_wcs(struct dstr *dst, const wchar_t *wstr)\n\n   Copies a wide string in to a dynamic string and converts it to UTF-8.\n\n   :param dst:  Dynamic string\n   :param wstr: Wide string\n\n----------------------\n\n.. function:: wchar_t *dstr_to_wcs(const struct dstr *str)\n\n   Converts a dynamic array to a wide string.  Free with\n   :c:func:`bfree()`.\n\n   :param str: Dynamic string\n   :return:    Wide string allocation.  Free with :c:func:`bfree()`\n\n----------------------\n\n.. function:: void dstr_to_upper(struct dstr *str)\n\n   Converts all characters within a dynamic array to uppercase.\n\n   :param str: Dynamic string\n\n----------------------\n\n.. function:: void dstr_to_lower(struct dstr *str)\n\n   Converts all characters within a dynamic array to lowercase.\n\n   :param str: Dynamic string\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-platform.rst",
    "content": "Platform Helpers\n================\n\nThese functions/structures/types are used to perform actions that\ntypically don't have shared functions across different operating systems\nand platforms.\n\n.. code:: cpp\n\n   #include <util/platform.h>\n\n\nFile Functions\n--------------\n\n.. function:: FILE *os_wfopen(const wchar_t *path, const char *mode)\n\n   Opens a file with a wide string path.\n\n----------------------\n\n.. function:: FILE *os_fopen(const char *path, const char *mode)\n\n   Opens a file with a UTF8 string path.\n\n----------------------\n\n.. function:: int64_t os_fgetsize(FILE *file)\n\n   Returns a file's size.\n\n----------------------\n\n.. function:: int os_stat(const char *file, struct stat *st)\n\n   Equivalent to the posix *stat* function.\n\n----------------------\n\n.. function:: int os_fseeki64(FILE *file, int64_t offset, int origin)\n\n   Equivalent to fseek.\n\n----------------------\n\n.. function:: int64_t os_ftelli64(FILE *file)\n\n   Gets the current file position.\n\n----------------------\n\n.. function:: size_t os_fread_utf8(FILE *file, char **pstr)\n\n   Reads a UTF8 encoded file and allocates a pointer to the UTF8 string.\n\n----------------------\n\n.. function:: char *os_quick_read_utf8_file(const char *path)\n\n   Reads a UTF8 encoded file and returns an allocated pointer to the\n   string.\n\n----------------------\n\n.. function:: bool os_quick_write_utf8_file(const char *path, const char *str, size_t len, bool marker)\n\n   Writes a UTF8 encoded file.\n\n----------------------\n\n.. function:: bool os_quick_write_utf8_file_safe(const char *path, const char *str, size_t len, bool marker, const char *temp_ext, const char *backup_ext)\n\n   Writes a UTF8 encoded file with overwrite corruption prevention.\n\n----------------------\n\n.. function:: int64_t os_get_file_size(const char *path)\n\n   Gets a file's size.\n\n----------------------\n\n.. function:: int64_t os_get_free_space(const char *path)\n\n   Gets free space of a specific file path.\n\n---------------------\n\n\nString Conversion Functions\n---------------------------\n\n.. function:: size_t os_utf8_to_wcs(const char *str, size_t len, wchar_t *dst, size_t dst_size)\n\n   Converts a UTF8 string to a wide string.\n\n----------------------\n\n.. function:: size_t os_wcs_to_utf8(const wchar_t *str, size_t len, char *dst, size_t dst_size)\n\n   Converts a wide string to a UTF8 string.\n\n----------------------\n\n.. function:: size_t os_utf8_to_wcs_ptr(const char *str, size_t len, wchar_t **pstr)\n\n   Gets an bmalloc-allocated wide string converted from a UTF8 string.\n\n----------------------\n\n.. function:: size_t os_wcs_to_utf8_ptr(const wchar_t *str, size_t len, char **pstr)\n\n   Gets an bmalloc-allocated UTF8 string converted from a wide string.\n\n---------------------\n\n\nNumber/String Conversion Functions\n----------------------------------\n\n.. function:: double os_strtod(const char *str)\n\n   Converts a string to a double.\n\n----------------------\n\n.. function:: int os_dtostr(double value, char *dst, size_t size)\n\n   Converts a double to a string.\n\n---------------------\n\n\nDynamic Link Library Functions\n------------------------------\n\nThese functions are roughly equivalent to dlopen/dlsym/dlclose.\n\n.. function:: void *os_dlopen(const char *path)\n\n   Opens a dynamic library.\n\n----------------------\n\n.. function:: void *os_dlsym(void *module, const char *func)\n\n   Returns a symbol from a dynamic library.\n\n----------------------\n\n.. function:: void os_dlclose(void *module)\n\n   Closes a dynamic library.\n\n---------------------\n\n.. function:: bool os_is_obs_plugin(const char *path)\n\n   Returns true if the path is a dynamic library that looks like an OBS plugin.\n\n   Currently only needed on Windows for performance reasons.\n\n---------------------\n\n\nCPU Usage Functions\n-------------------\n\n.. function:: os_cpu_usage_info_t *os_cpu_usage_info_start(void)\n\n   Creates a CPU usage information object.\n\n----------------------\n\n.. function:: double              os_cpu_usage_info_query(os_cpu_usage_info_t *info)\n\n   Queries the current CPU usage.\n\n----------------------\n\n.. function:: void                os_cpu_usage_info_destroy(os_cpu_usage_info_t *info)\n\n   Destroys a CPU usage information object.\n\n---------------------\n\n\nSleep/Time Functions\n--------------------\n\n.. function:: bool os_sleepto_ns(uint64_t time_target)\n\n   Sleeps to a specific time with high precision, in nanoseconds.\n\n---------------------\n\n.. function:: bool os_sleepto_ns_fast(uint64_t time_target)\n\n   Sleeps to a specific time without high precision, in nanoseconds.\n   The function won't return until reaching the specific time.\n\n---------------------\n\n.. function:: void os_sleep_ms(uint32_t duration)\n\n   Sleeps for a specific number of milliseconds.\n\n---------------------\n\n.. function:: uint64_t os_gettime_ns(void)\n\n   Gets the current high-precision system time, in nanoseconds.\n\n---------------------\n\nOther Path/File Functions\n-------------------------\n\n.. function:: int os_get_config_path(char *dst, size_t size, const char *name)\n              char *os_get_config_path_ptr(const char *name)\n\n   Gets the user-specific application configuration data path.\n\n---------------------\n\n.. function:: int os_get_program_data_path(char *dst, size_t size, const char *name)\n              char *os_get_program_data_path_ptr(const char *name)\n\n   Gets the application configuration data path.\n\n---------------------\n\n.. function:: bool os_file_exists(const char *path)\n\n   Returns true if a file/directory exists, false otherwise.\n\n---------------------\n\n.. function:: size_t os_get_abs_path(const char *path, char *abspath, size_t size)\n              char *os_get_abs_path_ptr(const char *path)\n\n   Converts a relative path to an absolute path.\n\n---------------------\n\n.. function:: const char *os_get_path_extension(const char *path)\n\n   Returns the extension portion of a path string, including the dot (.).\n\n---------------------\n\n.. type:: struct os_dir os_dir_t\n\n   A directory object.\n\n.. struct:: os_dirent\n\n   A directory entry record.\n\n.. member:: char os_dirent.d_name[256]\n\n   The directory entry name.\n\n.. member:: bool os_dirent.directory\n\n   *true* if the entry is a directory.\n\n---------------------\n\n.. function:: os_dir_t *os_opendir(const char *path)\n\n   Opens a directory object to enumerate files within the directory.\n\n---------------------\n\n.. function:: struct os_dirent *os_readdir(os_dir_t *dir)\n\n   Returns the linked list of directory entries.\n\n---------------------\n\n.. function:: void os_closedir(os_dir_t *dir)\n\n   Closes a directory object.\n\n---------------------\n\n.. struct:: os_globent\n\n   A glob entry.\n\n.. member:: char *os_globent.path\n\n   The full path to the glob entry.\n\n.. member:: bool os_globent.directory\n\n   *true* if the glob entry is a directory, *false* otherwise.\n\n.. struct:: os_glob_info\n\n   A glob object.\n\n.. member:: size_t             os_glob_info.gl_pathc\n\n   Number of glob entries.\n\n.. member:: struct os_globent *os_glob_info.gl_pathv\n\n   Array of glob entries.\n\n.. type:: struct os_glob_info os_glob_t\n\n---------------------\n\n.. function:: int os_glob(const char *pattern, int flags, os_glob_t **pglob)\n\n   Enumerates files based upon a glob string.\n\n---------------------\n\n.. function:: void os_globfree(os_glob_t *pglob)\n\n   Frees a glob object.\n\n---------------------\n\n.. function:: int os_unlink(const char *path)\n\n   Deletes a file.\n\n---------------------\n\n.. function:: int os_rmdir(const char *path)\n\n   Deletes a directory.\n\n---------------------\n\n.. function:: char *os_getcwd(char *path, size_t size)\n\n   Returns a new bmalloc-allocated path to the current working\n   directory.\n\n---------------------\n\n.. function:: int os_chdir(const char *path)\n\n   Changes the current working directory.\n\n---------------------\n\n.. function:: int os_mkdir(const char *path)\n\n   Creates a directory.\n\n---------------------\n\n.. function:: int os_mkdirs(const char *path)\n\n   Creates a full directory path if it doesn't exist.\n\n---------------------\n\n.. function:: int os_rename(const char *old_path, const char *new_path)\n\n   Renames a file.\n\n---------------------\n\n.. function:: int os_copyfile(const char *file_in, const char *file_out)\n\n   Copies a file.\n\n---------------------\n\n.. function:: int os_safe_replace(const char *target_path, const char *from_path, const char *backup_path)\n\n   Safely replaces a file.\n\n---------------------\n\n.. function:: char *os_generate_formatted_filename(const char *extension, bool space, const char *format)\n\n   Returns a new bmalloc-allocated filename generated from specific\n   formatting.\n\n---------------------\n\n\nSleep-Inhibition Functions\n--------------------------\n\nThese functions/types are used to inhibit the computer from going to\nsleep.\n\n.. struct:: os_inhibit_info\n.. type:: struct os_inhibit_info os_inhibit_t\n\n---------------------\n\n.. function:: os_inhibit_t *os_inhibit_sleep_create(const char *reason)\n\n   Creates a sleep inhibition object.\n\n---------------------\n\n.. function:: bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active)\n\n   Activates/deactivates a sleep inhibition object.\n\n---------------------\n\n.. function:: void os_inhibit_sleep_destroy(os_inhibit_t *info)\n\n   Destroys a sleep inhibition object.  If the sleep inhibition object\n   was active, it will be deactivated.\n\n---------------------\n\n\nOther Functions\n---------------\n\n.. function:: void os_breakpoint(void)\n\n   Triggers a debugger breakpoint (or crashes the program if no debugger\n   present).\n\n---------------------\n\n.. function:: int os_get_physical_cores(void)\n\n   Returns the number of physical cores available.\n\n---------------------\n\n.. function:: int os_get_logical_cores(void)\n\n   Returns the number of logical cores available.\n\n---------------------\n\n.. function:: uint64_t os_get_sys_free_size(void)\n\n   Returns the amount of memory available.\n\n---------------------\n\n.. function:: uint64_t os_get_sys_total_size(void)\n\n   Returns the amount of memory installed.\n\n   .. versionadded:: 29.0.0\n\n---------------------\n\n.. struct:: os_proc_memory_usage\n\n   Memory usage structure.\n\n.. member:: uint64_t os_proc_memory_usage.resident_size\n\n   Resident size.\n\n.. member:: uint64_t os_proc_memory_usage.virtual_size\n\n   Virtual size.\n\n.. type:: struct os_proc_memory_usage os_proc_memory_usage_t\n\n---------------------\n\n.. function:: bool os_get_proc_memory_usage(os_proc_memory_usage_t *usage)\n\n   Gets memory usage of the current process.\n\n---------------------\n\n.. function:: uint64_t os_get_proc_resident_size(void)\n\n   Returns the resident memory size of the current process.\n\n---------------------\n\n.. function:: uint64_t os_get_proc_virtual_size(void)\n\n   Returns the virtual memory size of the current process.\n\n---------------------\n\n.. function:: bool os_get_emulation_status(void)\n\n   Returns true if the current process is an x64 binary and is being emulated or translated\n   by the host operating system. On macOS, it returns true when an x64 binary is \n   being translated by Rosetta and running on Apple Silicon Macs. On Windows, it \n   returns true when an x64 binary is being emulated on Windows ARM64 PCs. On all other \n   platforms, it will always returns false.\n\n----------------------\n\n.. function:: char *os_generate_uuid(void)\n\n   Creates a version 4 UUID and returns a NULL-terminated 36-character string.\n   Must be freed with :c:func:`bfree()`.\n\n   .. versionadded:: 29.1\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-profiler.rst",
    "content": "Profiler\n========\n\nThe profiler is used to get information about program performance and\nefficiency.\n\n.. type:: struct profiler_snapshot profiler_snapshot_t\n.. type:: struct profiler_snapshot_entry profiler_snapshot_entry_t\n.. type:: struct profiler_name_store profiler_name_store_t\n.. type:: struct profiler_time_entry profiler_time_entry_t\n\n.. code:: cpp\n\n   #include <util/profiler.h>\n\n\nProfiler Structures\n-------------------\n\n.. struct:: profiler_time_entry\n.. member:: uint64_t profiler_time_entry.time_delta\n.. member:: uint64_t profiler_time_entry.count\n\n\nProfiler Control Functions\n--------------------------\n\n.. function:: void profiler_start(void)\n\n   Starts the profiler.\n\n----------------------\n\n.. function:: void profiler_stop(void)\n\n   Stops the profiler.\n\n----------------------\n\n.. function:: void profiler_print(profiler_snapshot_t *snap)\n\n   Creates a profiler snapshot and saves it within *snap*.\n\n   :param snap: A profiler snapshot object\n\n----------------------\n\n.. function:: void profiler_print_time_between_calls(profiler_snapshot_t *snap)\n\n   Creates a profiler snapshot of time between calls and saves it within\n   *snap*.\n\n   :param snap: A profiler snapshot object\n\n----------------------\n\n.. function:: void profiler_free(void)\n\n   Frees the profiler.\n\n----------------------\n\n\nProfiling Functions\n-------------------\n\n.. function:: void profile_register_root(const char *name, uint64_t expected_time_between_calls)\n\n   Registers a root profile node.\n\n   :param name:                        Name of the root profile node\n   :param expected_time_between_calls: The expected time between calls\n                                       of the profile root node, or 0 if\n                                       none.\n\n----------------------\n\n.. function:: void profile_start(const char *name)\n\n   Starts a profile node.  This profile node will be a child of the last\n   node that was started.\n\n   :param name: Name of the profile node\n\n----------------------\n\n.. function:: void profile_end(const char *name)\n\n   :param name: Name of the profile node\n\n----------------------\n\n.. function:: void profile_reenable_thread(void)\n\n   Because :c:func:`profiler_start()` can be called in a different\n   thread than the current thread, this is used to specify a point where\n   it's safe to re-enable profiling in the calling thread.  Call this\n   when you have looped root profile nodes and need to specify a safe\n   point where the root profile node isn't active and the profiler can\n   start up in the current thread again.\n\n----------------------\n\n\nProfiler Name Storage Functions\n-------------------------------\n\n.. function:: profiler_name_store_t *profiler_name_store_create(void)\n\n   Creates a profiler name storage object.\n\n   :return: Profiler name store object\n\n----------------------\n\n.. function:: void profiler_name_store_free(profiler_name_store_t *store)\n\n   Frees a profiler name storage object.\n\n   :param store: Profiler name storage object\n\n----------------------\n\n.. function:: const char *profile_store_name(profiler_name_store_t *store, const char *format, ...)\n\n   Creates a formatted string and stores it within a profiler name\n   storage object.\n\n   :param store:  Profiler name storage object\n   :param format: Formatted string\n   :return:       The string created from format specifications\n\n----------------------\n\n\nProfiler Data Access Functions\n------------------------------\n\n.. function:: profiler_snapshot_t *profile_snapshot_create(void)\n\n   Creates a profile snapshot.  Profiler snapshots are used to obtain\n   data about how the active profiles performed.\n\n   :return: A profiler snapshot object\n\n----------------------\n\n.. function:: void profile_snapshot_free(profiler_snapshot_t *snap)\n\n   Frees a profiler snapshot object.\n\n   :param snap: A profiler snapshot\n\n----------------------\n\n.. function:: bool profiler_snapshot_dump_csv(const profiler_snapshot_t *snap, const char *filename)\n\n   Creates a CSV file of the profiler snapshot.\n\n   :param snap:     A profiler snapshot\n   :param filename: The path to the CSV file to save\n   :return:         *true* if successfully written, *false* otherwise\n\n----------------------\n\n.. function:: bool profiler_snapshot_dump_csv_gz(const profiler_snapshot_t *snap, const char *filename)\n\n   Creates a gzipped CSV file of the profiler snapshot.\n\n   :param snap:     A profiler snapshot\n   :param filename: The path to the gzipped CSV file to save\n   :return:         *true* if successfully written, *false* otherwise\n\n----------------------\n\n.. function:: size_t profiler_snapshot_num_roots(profiler_snapshot_t *snap)\n\n   :param snap: A profiler snapshot\n   :return:     Number of root profiler nodes in the snapshot\n\n----------------------\n\n.. type:: bool (*profiler_entry_enum_func)(void *context, profiler_snapshot_entry_t *entry)\n\n   Profiler snapshot entry numeration callback\n\n   :param context: Private data passed to this callback\n   :param entry:   Profiler snapshot entry\n   :return:        *true* to continue enumeration, *false* otherwise\n\n----------------------\n\n.. function:: void profiler_snapshot_enumerate_roots(profiler_snapshot_t *snap, profiler_entry_enum_func func, void *context)\n\n   Enumerates root profile nodes.\n\n   :param snap:    A profiler snapshot\n   :param func:    Enumeration callback\n   :param context: Private data to pass to the callback\n\n----------------------\n\n.. type:: bool (*profiler_name_filter_func)(void *data, const char *name, bool *remove)\n\n   Callback used to determine what profile nodes are removed/filtered.\n\n   :param data:    Private data passed to this callback\n   :param name:    Profile node name to be filtered\n   :param remove:  Used to determined whether the node should be removed\n                   or not\n   :return:        *true* to continue enumeration, *false* otherwise\n\n----------------------\n\n.. function:: void profiler_snapshot_filter_roots(profiler_snapshot_t *snap, profiler_name_filter_func func, void *data)\n\n   Removes/filters profile roots based upon their names.\n\n   :param snap: A profiler snapshot\n   :param func: Enumeration callback to filter with\n   :param data: Private data to pass to the callback\n\n----------------------\n\n.. function:: size_t profiler_snapshot_num_children(profiler_snapshot_entry_t *entry)\n\n   :param entry: A profiler snapshot entry\n   :return:      Number of children for the entry\n\n----------------------\n\n.. function:: void profiler_snapshot_enumerate_children(profiler_snapshot_entry_t *entry, profiler_entry_enum_func func, void *context)\n\n   Enumerates child entries of a profiler snapshot entry.\n\n   :param entry:   A profiler snapshot entry\n   :param func:    Enumeration callback\n   :param context: Private data passed to the callback\n\n----------------------\n\n.. function:: const char *profiler_snapshot_entry_name(profiler_snapshot_entry_t *entry)\n\n   :param entry: A profiler snapshot entry\n   :return:      The name of the profiler snapshot entry\n\n----------------------\n\n.. function:: profiler_time_entries_t *profiler_snapshot_entry_times(profiler_snapshot_entry_t *entry)\n\n   Gets the time entries for a snapshot entry.\n\n   :param entry: A profiler snapshot entry\n   :return:      An array of profiler time entries\n\n----------------------\n\n.. function:: uint64_t profiler_snapshot_entry_min_time(profiler_snapshot_entry_t *entry)\n\n   Gets the minimum time for a profiler snapshot entry.\n\n   :param entry: A profiler snapshot entry\n   :return:      The minimum time value for the snapshot entry\n\n----------------------\n\n.. function:: uint64_t profiler_snapshot_entry_max_time(profiler_snapshot_entry_t *entry)\n\n   Gets the maximum time for a profiler snapshot entry.\n\n   :param entry: A profiler snapshot entry\n   :return:      The maximum time value for the snapshot entry\n\n----------------------\n\n.. function:: uint64_t profiler_snapshot_entry_overall_count(profiler_snapshot_entry_t *entry)\n\n   Gets the overall count for a profiler snapshot entry.\n\n   :param entry: A profiler snapshot entry\n   :return:      The overall count value for the snapshot entry\n\n----------------------\n\n.. function:: profiler_time_entries_t *profiler_snapshot_entry_times_between_calls(profiler_snapshot_entry_t *entry)\n\n   Gets an array of time between calls for a profiler snapshot entry.\n\n   :param entry: A profiler snapshot entry\n   :return:      An array of profiler time entries\n\n----------------------\n\n.. function:: uint64_t profiler_snapshot_entry_expected_time_between_calls(profiler_snapshot_entry_t *entry)\n\n   Gets the expected time between calls for a profiler snapshot entry.\n\n   :param entry: A profiler snapshot entry\n   :return:      The expected time between calls for the snapshot entry,\n                 or 0 if not set\n\n----------------------\n\n.. function:: uint64_t profiler_snapshot_entry_min_time_between_calls(profiler_snapshot_entry_t *entry)\n\n   Gets the minimum time seen between calls for a profiler snapshot entry.\n\n   :param entry: A profiler snapshot entry\n   :return:      The minimum time seen between calls for the snapshot entry\n\n----------------------\n\n.. function:: uint64_t profiler_snapshot_entry_max_time_between_calls(profiler_snapshot_entry_t *entry)\n\n   Gets the maximum time seen between calls for a profiler snapshot entry.\n\n   :param entry: A profiler snapshot entry\n   :return:      The maximum time seen between calls for the snapshot entry\n\n----------------------\n\n.. function:: uint64_t profiler_snapshot_entry_overall_between_calls_count(profiler_snapshot_entry_t *entry)\n\n   Gets the overall time between calls for a profiler snapshot entry.\n\n   :param entry: A profiler snapshot entry\n   :return:      The overall time between calls for the snapshot entry\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-serializers.rst",
    "content": "Serializer\n==========\n\nGeneral programmable serialization functions.  (A shared interface to\nvarious reading/writing to/from different inputs/outputs)\n\n.. code:: cpp\n\n   #include <serializer.h>\n\nSerializer Structure (struct serializer)\n----------------------------------------\n\n.. struct:: serializer\n.. member:: void     *serializer.data\n.. member:: size_t   (*serializer.read)(void *, void *, size_t)\n.. member:: size_t   (*serializer.write)(void *, const void *, size_t)\n.. member:: int64_t  (*serializer.seek)(void *, int64_t, enum serialize_seek_type)\n.. member:: int64_t  (*serializer.get_pos)(void *)\n\nSerializer Inline Functions\n---------------------------\n\n.. function:: size_t s_read(struct serializer *s, void *data, size_t size)\n\n---------------------\n\n.. function:: size_t s_write(struct serializer *s, const void *data, size_t size)\n\n---------------------\n\n.. function:: size_t serialize(struct serializer *s, void *data, size_t len)\n\n---------------------\n\n.. function:: int64_t serializer_seek(struct serializer *s, int64_t offset, enum serialize_seek_type seek_type)\n\n---------------------\n\n.. function:: int64_t serializer_get_pos(struct serializer *s)\n\n---------------------\n\n.. function:: void s_w8(struct serializer *s, uint8_t u8)\n\n---------------------\n\n.. function:: void s_wl16(struct serializer *s, uint16_t u16)\n\n---------------------\n\n.. function:: void s_wl32(struct serializer *s, uint32_t u32)\n\n---------------------\n\n.. function:: void s_wl64(struct serializer *s, uint64_t u64)\n\n---------------------\n\n.. function:: void s_wlf(struct serializer *s, float f)\n\n---------------------\n\n.. function:: void s_wld(struct serializer *s, double d)\n\n---------------------\n\n.. function:: void s_wb16(struct serializer *s, uint16_t u16)\n\n---------------------\n\n.. function:: void s_wb24(struct serializer *s, uint32_t u24)\n\n---------------------\n\n.. function:: void s_wb32(struct serializer *s, uint32_t u32)\n\n---------------------\n\n.. function:: void s_wb64(struct serializer *s, uint64_t u64)\n\n---------------------\n\n.. function:: void s_wbf(struct serializer *s, float f)\n\n---------------------\n\n.. function:: void s_wbd(struct serializer *s, double d)\n\n---------------------\n\n\nArray Output Serializer\n=======================\n\nProvides an output serializer used with dynamic arrays.\n\n.. versionchanged:: 30.2\n   Array output serializer now supports seeking.\n\n.. code:: cpp\n\n   #include <util/array-serializer.h>\n\nArray Output Serializer Structure (struct array_output_data)\n------------------------------------------------------------\n\n.. struct:: array_output_data\n\n.. code:: cpp\n\n   DARRAY(uint8_t) array_output_data.bytes\n\nArray Output Serializer Functions\n---------------------------------\n\n.. function:: void array_output_serializer_init(struct serializer *s, struct array_output_data *data)\n\n---------------------\n\n.. function:: void array_output_serializer_free(struct array_output_data *data)\n\n---------------------\n\n.. function:: void array_output_serializer_reset(struct array_output_data *data)\n\n   Resets serializer without freeing data.\n\n   .. versionadded:: 30.2\n\n---------------------\n\nFile Input/Output Serializers\n=============================\n\nProvides file reading/writing serializers.\n\n.. code:: cpp\n\n   #include <util/file-serializer.h>\n\n\nFile Input Serializer Functions\n-------------------------------\n\n.. function:: bool file_input_serializer_init(struct serializer *s, const char *path)\n\n   Initializes a file input serializer.\n\n   :return:     *true* if file opened successfully, *false* otherwise\n\n---------------------\n\n.. function:: void file_input_serializer_free(struct serializer *s)\n\n   Frees a file input serializer.\n\n---------------------\n\n\nFile Output Serializer Functions\n--------------------------------\n\n.. function:: bool file_output_serializer_init(struct serializer *s, const char *path)\n\n   Initializes a file output serializer.\n\n   :return:     *true* if file created successfully, *false* otherwise\n\n---------------------\n\n.. function:: bool file_output_serializer_init_safe(struct serializer *s, const char *path, const char *temp_ext)\n\n   Initializes and safely writes to a temporary file (determined by the\n   temporary extension) until freed.\n\n   :return:     *true* if file created successfully, *false* otherwise\n\n---------------------\n\n.. function:: void file_output_serializer_free(struct serializer *s)\n\n   Frees the file output serializer and saves the file.\n\n---------------------\n\nBuffered File Output Serializer\n===============================\n\nProvides a buffered file serializer that writes data asynchronously to prevent stalls due to slow disk I/O.\n\nWrites will only block when the buffer is full.\n\nThe buffer and chunk size are configurable with the defaults being 256 MiB and 1 MiB respectively.\n\n.. versionadded:: 30.2\n\n.. code:: cpp\n\n   #include <util/buffered-file-serializer.h>\n\n\nBuffered File Output Serializer Functions\n-----------------------------------------\n\n.. function:: bool buffered_file_serializer_init_defaults(struct serializer *s, const char *path)\n\n   Initializes a buffered file output serializer with default buffer and chunk sizes.\n\n   :return:     *true* if file created successfully, *false* otherwise\n\n---------------------\n\n.. function:: bool buffered_file_serializer_init(struct serializer *s, const char *path, size_t max_bufsize, size_t chunk_size)\n\n   Initialize buffered writer with specified buffer and chunk sizes. Setting either to `0` will use the default value.\n\n   :return:     *true* if file created successfully, *false* otherwise\n\n---------------------\n\n.. function:: void buffered_file_serializer_free(struct serializer *s)\n\n   Frees the file output serializer and saves the file. Will block until I/O thread completes outstanding writes.\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-source-profiler.rst",
    "content": "Source Profiler\n===============\n\nThe source profiler is used to get information about individual source's performance.\n\n.. struct:: profiler_result\n\n.. member:: uint64_t profiler_result.tick_avg\n            uint64_t profiler_result.render_avg\n   \n   Average execution time of this source's tick and render functions within the sampled timeframe (5 seconds).\n   \n   Note that a source will be ticked only once per frame, but may be rendered multiple times.\n\n.. member:: uint64_t profiler_result.tick_max\n            uint64_t profiler_result.render_max\n\n   Maximum execution time of this source's tick and render functions within the sampled timeframe (5 seconds).\n\n.. member:: uint64_t profiler_result.render_gpu_avg\n            uint64_t profiler_result.render_gpu_max\n   \n   Average and maximum execution time for GPU rendering to execute within the sampled timeframe (5 seconds).\n   \n   Note that GPU timing is not supported on macOS and is of limited accuracy due to variations in GPU load/clock speed.\n\n.. member:: uint64_t profiler_result.render_sum\n            uint64_t profiler_result.render_gpu_sum\n\n   Sum of all CPU/GPU render time in a frame, averaged over the sampled timeframe (5 seconds).\n   \n   For example, assuming a source with perfect consistency in its render time that gets rendered twice in a frame and a value for :c:member:`profiler_result.render_avg` of `1000000` (1 ms), will have a value for :c:member:`profiler_result.render_sum` of `2000000` (2 ms).\n\n.. member:: double profiler_result.async_fps\n\n   Framerate calculated from average time delta between async frames submitted via :c:func:`obs_source_output_video2()`.\n   \n   Only valid for async sources (e.g. Media Source).\n\n.. type:: struct profiler_result profiler_result_t\n\n.. code:: cpp\n\n   #include <util/source-profiler.h>\n\n\nSource Profiler Functions\n---------------------\n\n.. function:: void source_profiler_enable(bool enable)\n\n   Enables or disables the source profiler.\n   The profiler will then start or stop collecting data with the next rendered frame.\n   \n   Note that enabling the profiler may have a small performance penalty.\n\n   :param enable: Whether or not to enable the source profiler.\n\n---------------------\n\n.. function:: void source_profiler_gpu_enable(bool enable)\n\n   Enables or disables GPU profiling (not available on macOS).\n   GPU profiling will start or stop with the next frame OBS is rendering.\n   \n   Note that GPU profiling may have a larger performance impact.\n\n   :param enable: Whether or not to enable GPU profiling.\n\n---------------------\n\n.. function:: profiler_result_t *source_profiler_get_result(obs_source_t *source)\n\n   Returns profiling information for the provided `source`.\n   \n   Note that result must be freed with :c:func:`bfree()`.\n\n   :param source: Source to get profiling information for\n   :return:       Pointer to `profiler_result_t` with data, `NULL` otherwise.\n\n---------------------\n\n.. function:: bool source_profiler_fill_result(obs_source_t *source, profiler_result_t *result)\n\n   Fill a preexisting `profiler_result_t` object with data for `source`.\n   \n   This function exists to avoid having to allocate new memory each time a profiling result is queried.\n\n   :param source: Source to get profiling information for\n   :param result: Result object to fill\n   :return:       *true* if data for the source exists, *false* otherwise\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-text-lookup.rst",
    "content": "Text Lookup Interface\n=====================\n\nUsed for storing and looking up localized strings.  Uses an ini-file\nlike file format for localization lookup.\n\n.. struct:: text_lookup\n\n.. type:: struct text_lookup lookup_t\n\n.. code:: cpp\n\n   #include <util/text-lookup.h>\n\n\nText Lookup Functions\n---------------------\n\n.. function:: lookup_t *text_lookup_create(const char *path)\n\n   Creates a text lookup object from a text lookup file.\n\n   :param path: Path to the localization file\n   :return:     New lookup object, or *NULL* if an error occurred\n\n---------------------\n\n.. function:: bool text_lookup_add(lookup_t *lookup, const char *path)\n\n   Adds text lookup from a text lookup file and replaces any values.\n   For example, you would load a default fallback language such as\n   english with :c:func:`text_lookup_create()`, and then call this\n   function to load the actual desired language in case the desired\n   language isn't fully translated.\n\n   :param lookup: Lookup object\n   :param path:   Path to the localization file\n   :return:       *true* if successful, *false* otherwise\n\n---------------------\n\n.. function:: void text_lookup_destroy(lookup_t *lookup)\n\n   Destroys a text lookup object.\n\n   :param lookup: Lookup object\n\n---------------------\n\n.. function:: bool text_lookup_getstr(lookup_t *lookup, const char *lookup_val, const char **out)\n\n   Gets a localized text string.\n\n   :param lookup:     Lookup object\n   :param lookup_val: Value to look up\n   :param out:        Pointer that receives the translated string\n                      pointer\n   :return:           *true* if the value exists, *false* otherwise\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util-threading.rst",
    "content": "Threading\n=========\n\nLibobs provides a number of helper functions/types specifically for\nthreading.  The threading header will additionally provide access to\npthread functions even on Windows.\n\n.. code:: cpp\n\n   #include <util/threading.h>\n\n\nThreading Types\n---------------\n\n.. type:: os_event_t\n.. type:: os_sem_t\n\n\nGeneral Thread Functions\n------------------------\n\n.. function:: void os_set_thread_name(const char *name)\n\n   Sets the name of the current thread.\n\n----------------------\n\n\nEvent Functions\n---------------\n\n.. function:: int  os_event_init(os_event_t **event, enum os_event_type type)\n\n   Creates an event object.\n\n   :param event: Pointer that receives a pointer to a new event object\n   :param type:  Can be one of the following values:\n\n                 - OS_EVENT_TYPE_AUTO - Automatically resets when\n                   signaled\n                 - OS_EVENT_TYPE_MANUAL - Stays signaled until the\n                   :c:func:`os_event_reset()` function is called\n\n   :return:      0 if successful, negative otherwise\n\n----------------------\n\n.. function:: void os_event_destroy(os_event_t *event)\n\n   Destroys an event.\n\n   :param event: An event object\n\n----------------------\n\n.. function:: int  os_event_wait(os_event_t *event)\n\n   Waits for an event to signal.\n\n   :param event: An event object\n   :return:      0 if successful, negative otherwise\n\n----------------------\n\n.. function:: int  os_event_timedwait(os_event_t *event, unsigned long milliseconds)\n\n   Waits a specific duration for an event to signal.\n\n   :param event:        An event object\n   :param milliseconds: Milliseconds to wait\n   :return:             Can be one of the following values:\n   \n                        - 0 - successful\n                        - ETIMEDOUT - Timed out\n                        - EINVAL - An unexpected error occurred\n\n----------------------\n\n.. function:: int  os_event_try(os_event_t *event)\n\n   Checks for a signaled state without waiting.\n\n   :param event: An event object\n   :return:             Can be one of the following values:\n   \n                        - 0 - successful\n                        - EAGAIN - The event is not signaled\n                        - EINVAL - An unexpected error occurred\n\n----------------------\n\n.. function:: int  os_event_signal(os_event_t *event)\n\n   Signals the event.\n\n   :param event: An event object\n   :return:      0 if successful, negative otherwise\n\n----------------------\n\n.. function:: void os_event_reset(os_event_t *event)\n\n   Resets the signaled state of the event.\n\n   :param event: An event object\n\n----------------------\n\n\nSemaphore Functions\n-------------------\n\n.. function:: int  os_sem_init(os_sem_t **sem, int value)\n\n   Creates a semaphore object.\n\n   :param sem:   Pointer that receives a pointer to the semaphore object\n   :param value: Initial value of the semaphore\n   :return:      0 if successful, negative otherwise\n\n----------------------\n\n.. function:: void os_sem_destroy(os_sem_t *sem)\n\n   Destroys a semaphore object.\n\n   :param sem:   Semaphore object\n\n----------------------\n\n.. function:: int  os_sem_post(os_sem_t *sem)\n\n   Increments the semaphore.\n\n   :param sem:   Semaphore object\n   :return:      0 if successful, negative otherwise\n\n----------------------\n\n.. function:: int  os_sem_wait(os_sem_t *sem)\n\n   Decrements the semaphore or waits until the semaphore has been\n   incremented.\n\n   :param sem:   Semaphore object\n   :return:      0 if successful, negative otherwise\n\n---------------------\n\n\nAtomic Inline Functions\n-----------------------\n\n.. function:: long os_atomic_inc_long(volatile long *val)\n\n   Increments a long variable atomically.\n\n---------------------\n\n.. function:: long os_atomic_dec_long(volatile long *val)\n\n   Decrements a long variable atomically.\n\n---------------------\n\n.. function:: void os_atomic_store_long(volatile long *ptr, long val)\n\n   Stores the value of a long variable atomically.\n\n---------------------\n\n.. function:: long os_atomic_set_long(volatile long *ptr, long val)\n\n   Exchanges the value of a long variable atomically. Badly named.\n\n---------------------\n\n.. function:: long os_atomic_exchange_long(volatile long *ptr, long val)\n\n   Exchanges the value of a long variable atomically. Properly named.\n\n---------------------\n\n.. function:: long os_atomic_load_long(const volatile long *ptr)\n\n   Gets the value of a long variable atomically.\n\n---------------------\n\n.. function:: bool os_atomic_compare_swap_long(volatile long *val, long old_val, long new_val)\n\n   Swaps the value of a long variable atomically if its value matches.\n\n---------------------\n\n.. function:: void os_atomic_store_bool(volatile bool *ptr, bool val)\n\n   Stores the value of a boolean variable atomically.\n\n---------------------\n\n.. function:: bool os_atomic_set_bool(volatile bool *ptr, bool val)\n\n   Exchanges the value of a boolean variable atomically. Badly named.\n\n---------------------\n\n.. function:: bool os_atomic_exchange_bool(volatile bool *ptr, bool val)\n\n   Exchanges the value of a boolean variable atomically. Properly named.\n\n---------------------\n\n.. function:: bool os_atomic_load_bool(const volatile bool *ptr)\n\n   Gets the value of a boolean variable atomically.\n"
  },
  {
    "path": "docs/sphinx/reference-libobs-util.rst",
    "content": "Platform/Utility API Reference (libobs/util)\n============================================\n\n.. toctree::\n   :maxdepth: 2\n\n   reference-libobs-util-base\n   reference-libobs-util-bmem\n   reference-libobs-util-config-file\n   reference-libobs-util-darray\n   reference-libobs-util-deque\n   reference-libobs-util-dstr\n   reference-libobs-util-platform\n   reference-libobs-util-profiler\n   reference-libobs-util-serializers\n   reference-libobs-util-source-profiler\n   reference-libobs-util-text-lookup\n   reference-libobs-util-threading\n"
  },
  {
    "path": "docs/sphinx/reference-modules.rst",
    "content": "Module API Reference\n====================\n\nModules add custom functionality to libobs: typically\n:ref:`plugins_sources`, :ref:`plugins_outputs`, :ref:`plugins_encoders`,\nand :ref:`plugins_services`.\n\n.. type:: obs_module_t\n\n   A module object (not reference counted).\n\n.. code:: cpp\n\n   #include <obs-module.h>\n\n\nModule Macros\n-------------\n\nThese macros are used within custom plugin modules.\n\n.. macro:: OBS_DECLARE_MODULE()\n\n   Declares a libobs module.  Exports important core module functions\n   related to the module itself, OBS version, etc.\n\n---------------------\n\n.. macro:: OBS_MODULE_USE_DEFAULT_LOCALE(module_name, default_locale)\n\n   Helper macro that uses the standard ini file format for localization.\n   Automatically initializes and destroys localization data, and\n   automatically provides module externs such as\n   :c:func:`obs_module_text()` to be able to get a localized string with\n   little effort.\n\n---------------------\n\nModule Exports\n--------------\n\nThese are functions that plugin modules can optionally export in order\nto communicate with libobs and front-ends.\n\n.. function:: bool obs_module_load(void)\n\n   Required: Called when the module is loaded.  Implement this function\n   to load all the sources/encoders/outputs/services for your module, or\n   anything else that may need loading.\n\n   :return:          Return true to continue loading the module, otherwise\n                     false to indicate failure and unload the module\n\n---------------------\n\n.. function:: void obs_module_unload(void)\n\n   Optional: Called when the module is unloaded.\n\n---------------------\n\n.. function:: void obs_module_post_load(void)\n\n   Optional: Called when all modules have finished loading.\n\n---------------------\n\n.. function:: void obs_module_set_locale(const char *locale)\n\n   Called to set the locale language and load the locale data for the\n   module.\n\n---------------------\n\n.. function:: void obs_module_free_locale(void)\n\n   Called on module destruction to free locale data.\n\n---------------------\n\n.. function:: const char *obs_module_name(void)\n\n   (Optional)\n\n   :return: The full name of the module\n\n---------------------\n\n.. function:: const char *obs_module_description(void)\n\n   (Optional)\n\n   :return: A description of the module\n\n---------------------\n\n\nModule Externs\n--------------\n\nThese functions are externs that are usable throughout the module.\n\n.. function:: const char *obs_module_text(const char *lookup_string)\n\n   :return: A localized string\n\n---------------------\n\n.. function:: bool obs_module_get_string(const char *lookup_string, const char **translated_string)\n\n   Helper function for looking up locale.\n\n   :return: *true* if text found, otherwise *false*\n\n---------------------\n\n.. function:: obs_module_t *obs_current_module(void)\n\n   :return: The current module\n\n---------------------\n\n.. function:: char *obs_module_file(const char *file)\n\n   Returns the location to a module data file associated with the\n   current module.  Free with :c:func:`bfree()` when complete.\n\n   Equivalent to:\n\n.. code:: cpp\n\n      obs_find_module_file(obs_current_module(), file);\n\n---------------------\n\n.. function:: char *obs_module_config_path(const char *file)\n\n   Returns the location to a module config file associated with the\n   current module.  Free with :c:func:`bfree()` when complete.  Will\n   return NULL if configuration directory is not set.\n\n   Equivalent to:\n\n.. code:: cpp\n\n      obs_module_get_config_path(obs_current_module(), file);\n\n---------------------\n\n\nFrontend Module Functions\n--------------------------\n\nThese are functions used by frontends to load and get information about\nplugin modules.\n\n.. function:: int obs_open_module(obs_module_t **module, const char *path, const char *data_path)\n\n   Opens a plugin module directly from a specific path.\n\n   If the module already exists then the function will return successful, and\n   the module parameter will be given the pointer to the existing\n   module.\n\n   This does not initialize the module, it only loads the module image.  To\n   initialize the module, call :c:func:`obs_init_module()`.\n\n   :param  module:    The pointer to the created module\n   :param  path:      Specifies the path to the module library file.  If the\n                      extension is not specified, it will use the extension\n                      appropriate to the operating system\n   :param  data_path: Specifies the path to the directory where the module's\n                      data files are stored (or *NULL* if none)\n   :returns:          | MODULE_SUCCESS          - Successful\n                      | MODULE_ERROR            - A generic error occurred\n                      | MODULE_FILE_NOT_FOUND   - The module was not found\n                      | MODULE_MISSING_EXPORTS  - Required exports are missing\n                      | MODULE_INCOMPATIBLE_VER - Incompatible version\n                      | MODULE_HARDCODED_SKIP   - Skipped by hardcoded rules\n                                                  (e.g. obsolete obs-browser macOS plugin)\n\n---------------------\n\n.. function:: bool obs_init_module(obs_module_t *module)\n\n   Initializes the module, which calls its obs_module_load export.\n\n   :return: *true* if the module was loaded successfully\n\n---------------------\n\n.. function:: void obs_log_loaded_modules(void)\n\n   Logs loaded modules.\n\n---------------------\n\n.. function:: const char *obs_get_module_file_name(obs_module_t *module)\n\n   :return: The module file name\n\n---------------------\n\n.. function:: const char *obs_get_module_name(obs_module_t *module)\n\n   :return: The module full name (or *NULL* if none)\n\n---------------------\n\n.. function:: void obs_get_module_author(obs_module_t *module)\n\n   :return: The module author(s)\n\n---------------------\n\n.. function:: const char *obs_get_module_description(obs_module_t *module)\n\n   :return: The module description\n\n---------------------\n\n.. function:: const char *obs_get_module_binary_path(obs_module_t *module)\n\n   :return: The module binary path\n\n---------------------\n\n.. function:: const char *obs_get_module_data_path(obs_module_t *module)\n\n   :return: The module data path\n\n---------------------\n\n.. function:: void obs_add_module_path(const char *bin, const char *data)\n\n   Adds a module search path to be used with obs_find_modules.  If the search\n   path strings contain %module%, that text will be replaced with the module\n   name when used.\n\n   :param  bin:  Specifies the module's binary directory search path\n   :param  data: Specifies the module's data directory search path\n\n---------------------\n\n.. function:: void obs_load_all_modules(void)\n\n   Automatically loads all modules from module paths (convenience function).\n\n---------------------\n\n.. function:: void obs_load_all_modules2(struct obs_module_failure_info *mfi)\n\n   Automatically loads all modules from module paths (convenience function).\n   Additionally gives you information about modules that fail to load.\n\n   :param mfi: Provides module failure information. The *failed_modules*\n               member is a string list via a pointer to pointers of\n               strings of modules that failed to load. Can be freed\n               either with :c:func:`obs_module_failure_info_free()` or\n               by simply calling :c:func:`bfree()` on the\n               *failed_modules* member variable.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   struct obs_module_failure_info {\n           char **failed_modules;\n           size_t count;\n   };\n\n---------------------\n\n.. function:: void obs_add_safe_module(const char *name)\n\n   Adds a *name* to the list of modules allowed to load in Safe Mode.\n   If the list is empty, all modules are allowed.\n\n   :param  name: The name of the module (filename sans extension).\n\n   .. versionadded:: 30.0\n\n---------------------\n\n.. function:: void obs_module_failure_info_free(struct obs_module_failure_info *mfi)\n\n   Frees data allocated data used in the *mfi* parameter (calls\n   :c:func:`bfree()` on the *failed_modules* member variable).\n\n---------------------\n\n.. function:: void obs_post_load_modules(void)\n\n   Notifies modules that all modules have been loaded.\n\n---------------------\n\n.. function:: void obs_find_modules(obs_find_module_callback_t callback, void *param)\n\n   Finds all modules within the search paths added by\n   :c:func:`obs_add_module_path()`.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   struct obs_module_info {\n           const char *bin_path;\n           const char *data_path;\n   };\n\n   typedef void (*obs_find_module_callback_t)(void *param,\n                   const struct obs_module_info *info);\n\n---------------------\n\n.. function:: void obs_find_modules2(obs_find_module_callback_t callback, void *param)\n\n   Finds all modules within the search paths added by\n   :c:func:`obs_add_module_path()`.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   struct obs_module_info2 {\n           const char *bin_path;\n           const char *data_path;\n           const char *name;\n   };\n\n   typedef void (*obs_find_module_callback2_t)(void *param,\n                   const struct obs_module_info2 *info);\n\n---------------------\n\n.. function:: void obs_enum_modules(obs_enum_module_callback_t callback, void *param)\n\n   Enumerates all loaded modules.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   typedef void (*obs_enum_module_callback_t)(void *param, obs_module_t *module);\n\n---------------------\n\n.. function:: char *obs_find_module_file(obs_module_t *module, const char *file)\n\n   Returns the location of a plugin module data file.\n\n   Note:   Modules should use obs_module_file function defined in obs-module.h\n           as a more elegant means of getting their files without having to\n           specify the module parameter.\n\n   :param  module: The module associated with the file to locate\n   :param  file:   The file to locate\n   :return:        Path string, or NULL if not found.  Use bfree to free string\n\n---------------------\n\n.. function:: char *obs_module_get_config_path(obs_module_t *module, const char *file)\n\n   Returns the path of a plugin module config file (whether it exists or not).\n\n   Note:   Modules should use obs_module_config_path function defined in\n           obs-module.h as a more elegant means of getting their files without\n           having to specify the module parameter.\n\n   :param  module: The module associated with the path\n   :param  file:   The file to get a path to\n   :return:        Path string, or NULL if not found.  Use bfree to free string\n\n---------------------\n\n.. function:: void *obs_get_module_lib(obs_module_t *module)\n\n   Returns library file of specified module.\n\n   :param  module: The module where to find library file.\n   :return:        Pointer to module library.\n"
  },
  {
    "path": "docs/sphinx/reference-outputs.rst",
    "content": "Output API Reference (obs_output_t)\n===================================\n\nOutputs allow the ability to output the currently rendering audio/video.\nStreaming and recording are two common examples of outputs, but not the\nonly types of outputs.  Outputs can receive the raw data or receive\nencoded data.  The `libobs/obs-output.h`_ file is the dedicated header\nfor implementing outputs\n\n.. type:: obs_output_t\n\n   A reference-counted output object.\n\n.. type:: obs_weak_output_t\n\n   A weak reference to an output object.\n\n.. code:: cpp\n\n   #include <obs.h>\n\n\nOutput Definition Structure (obs_output_info)\n---------------------------------------------\n\n.. struct:: obs_output_info\n\n   Output definition structure.\n\n.. member:: const char *obs_output_info.id\n\n   Unique string identifier for the source (required).\n\n.. member:: uint32_t obs_output_info.flags\n\n   Output capability flags (required).\n\n   (Author's note: This should be renamed to \"capability_flags\")\n\n   A bitwise OR combination of one or more of the following values:\n\n   - **OBS_OUTPUT_VIDEO** - Can output video.\n\n   - **OBS_OUTPUT_AUDIO** - Can output audio.\n\n   - **OBS_OUTPUT_AV** - Combines OBS_OUTPUT_VIDEO and OBS_OUTPUT_AUDIO.\n\n   - **OBS_OUTPUT_ENCODED** - Output is encoded.\n\n     When this capability flag is used, the output must have encoders\n     assigned to it via the :c:func:`obs_output_set_video_encoder()`\n     and/or :c:func:`obs_output_set_audio_encoder()` functions in order\n     to be started.\n\n   - **OBS_OUTPUT_SERVICE** - Output requires a service object.\n\n     When this capability flag is used, the output must have a service\n     assigned to it via the :c:func:`obs_output_set_service()` function\n     in order to be started.\n\n     This is usually used with live streaming outputs that stream to\n     specific services.\n\n   - **OBS_OUTPUT_MULTI_TRACK** - Output supports multiple audio tracks.\n\n     When this capability flag is used, specifies that this output\n     supports multiple encoded audio tracks simultaneously.\n\n   - **OBS_OUTPUT_CAN_PAUSE** - Output supports pausing.\n\n     When this capability flag is used, the output supports pausing.\n     When an output is paused, raw or encoded audio/video data will be\n     halted when paused down to the exact point to the closest video\n     frame.  Audio data will be correctly truncated down to the exact\n     audio sample according to that video frame timing.\n\n.. member:: const char *(*obs_output_info.get_name)(void *type_data)\n\n   Get the translated name of the output type.\n\n   :param  type_data:  The type_data variable of this structure\n   :return:            The translated name of the output type\n\n.. member:: void *(*obs_output_info.create)(obs_data_t *settings, obs_output_t *output)\n\n   Creates the implementation data for the output.\n\n   :param  settings: Settings to initialize the output with\n   :param  output:   Output that this data is associated with\n   :return:          The implementation data associated with this output\n\n.. member:: void (*obs_output_info.destroy)(void *data)\n\n   Destroys the implementation data for the output.\n\n.. member:: bool (*obs_output_info.start)(void *data)\n\n   Starts the output.  If needed, this function can spawn a thread,\n   return *true* immediately, and then signal for failure later.\n\n   :return: *true* if successful or deferring to a signal to indicate\n            failure, *false* on failure to start\n\n.. member:: void (*obs_output_info.stop)(void *data, uint64_t ts)\n\n   Requests an output to stop at a specified time.  The *ts* parameter\n   indicates when the stop should occur.  Output will actually stop when\n   either the :c:func:`obs_output_end_data_capture()` or\n   :c:func:`obs_output_signal_stop()` functions are called.  If *ts* is\n   0, an immediate stop was requested.\n\n   :param ts: The timestamp to stop.  If 0, the output should attempt to\n              stop immediately rather than wait for any more data to\n              process\n\n.. member:: void (*obs_output_info.raw_video)(void *data, struct video_data *frame)\n\n   This is called when the output receives raw video data.  Only applies\n   to outputs that are not encoded.\n\n   :param frame: The raw video frame\n\n.. member:: void (*obs_output_info.raw_audio)(void *data, struct audio_data *frames)\n\n   This is called when the output receives raw audio data.  Only applies\n   to outputs that are not encoded.\n\n   **This callback must be used with single-track raw outputs.**\n\n   :param frames: The raw audio frames\n\n.. member:: void (*obs_output_info.raw_audio2)(void *data, size_t idx, struct audio_data *frames)\n\n   This is called when the output receives raw audio data.  Only applies\n   to outputs that are not encoded.\n\n   **This callback must be used with multi-track raw outputs.**\n\n   :param idx:    The audio track index\n   :param frames: The raw audio frames\n\n.. member:: void (*obs_output_info.encoded_packet)(void *data, struct encoder_packet *packet)\n\n   This is called when the output receives encoded video/audio data.\n   Only applies to outputs that are encoded.  Packets will always be\n   given in monotonic timestamp order.\n\n   :param packet: The video or audio packet.  If NULL, an encoder error\n                  occurred, and the output should call\n                  :c:func:`obs_output_signal_stop()` with the error code\n                  **OBS_OUTPUT_ENCODE_ERROR**.\n\n.. member:: void (*obs_output_info.update)(void *data, obs_data_t *settings)\n\n   Updates the settings for this output.\n\n   (Optional)\n\n   :param settings: New settings for this output\n\n.. member:: void (*obs_output_info.get_defaults)(obs_data_t *settings)\n            void (*obs_output_info.get_defaults2)(void *type_data, obs_data_t *settings)\n\n   Sets the default settings for this output.\n\n   (Optional)\n\n   :param  settings:  Default settings.  Call obs_data_set_default*\n                      functions on this object to set default setting\n                      values\n\n.. member:: obs_properties_t *(*obs_output_info.get_properties)(void *data)\n            obs_properties_t *(*obs_output_info.get_properties2)(void *data, void *type_data)\n\n   Gets the property information of this output.\n\n   (Optional)\n\n   :return: The properties of the output\n\n.. member:: void (*obs_output_info.unused1)(void *data)\n\n   This callback is no longer used.\n\n.. member:: uint64_t (*obs_output_info.get_total_bytes)(void *data)\n\n   Returns the number of total bytes processed by this output.\n\n   (Optional)\n\n   :return: Total bytes processed by this output since it started\n\n.. member:: int (*obs_output_info.get_dropped_frames)(void *data)\n\n   Returns the number of dropped frames.\n\n   (Optional)\n\n   :return: Number of dropped frames due to network congestion by this\n            output since it started\n\n.. member:: void *obs_output_info.type_data\n            void (*obs_output_info.free_type_data)(void *type_data)\n\n   Private data associated with this entry.  Note that this is not the\n   same as the implementation data; this is used to differentiate\n   between two different types if the same callbacks are used for more\n   than one different type.\n\n   (Optional)\n\n.. member:: float (*obs_output_info.get_congestion)(void *data)\n\n   This function is used to indicate how currently congested the output\n   is.  Useful for visualizing how much data is backed up on streaming\n   outputs.\n\n   (Optional)\n\n   :return: Current congestion value (0.0f..1.0f)\n\n.. member:: int (*obs_output_info.get_connect_time_ms)(void *data)\n\n   This function is used to determine how many milliseconds it took to\n   connect to its current server.\n\n   (Optional)\n\n   :return: Milliseconds it took to connect to its current server\n\n.. member:: const char *obs_output_info.encoded_video_codecs\n            const char *obs_output_info.encoded_audio_codecs\n\n   This variable specifies which codecs are supported by an encoded\n   output, separated by semicolon.\n\n   Required if **OBS_OUTPUT_SERVICE** flag is set, otherwise\n   recommended.\n\n.. member:: const char *obs_output_info.protocols\n\n   This variable specifies which protocols are supported by an output,\n   separated by semicolon.\n\n   Required only if **OBS_OUTPUT_SERVICE** flag is set.\n\n   .. versionadded:: 29.1\n\n.. _output_signal_handler_reference:\n\nOutput Signals\n--------------\n\n**start** (ptr output)\n\n   Called when the output starts.\n\n**stop** (ptr output, int code)\n\n   Called when the output stops.\n\n   :Parameters: - **code** - Can be one of the following values:\n\n                  | OBS_OUTPUT_SUCCESS        - Successfully stopped\n                  | OBS_OUTPUT_BAD_PATH       - The specified path was invalid\n                  | OBS_OUTPUT_CONNECT_FAILED - Failed to connect to a server\n                  | OBS_OUTPUT_INVALID_STREAM - Invalid stream path\n                  | OBS_OUTPUT_ERROR          - Generic error\n                  | OBS_OUTPUT_DISCONNECTED   - Unexpectedly disconnected\n                  | OBS_OUTPUT_UNSUPPORTED    - The settings, video/audio format, or codecs are unsupported by this output\n                  | OBS_OUTPUT_NO_SPACE       - Ran out of disk space\n                  | OBS_OUTPUT_ENCODE_ERROR   - Encoder error\n\n**pause** (ptr output)\n\n   Called when the output has been paused.\n\n**unpause** (ptr output)\n\n   Called when the output has been unpaused.\n\n**starting** (ptr output)\n\n   Called when the output is starting.\n\n**stopping** (ptr output)\n\n   Called when the output is stopping.\n\n**activate** (ptr output)\n\n   Called when the output activates (starts capturing data).\n\n**deactivate** (ptr output)\n\n   Called when the output deactivates (stops capturing data).\n\n**reconnect** (ptr output)\n\n   Called when the output is reconnecting.\n\n**reconnect_success** (ptr output)\n\n   Called when the output has successfully reconnected.\n\nGeneral Output Functions\n------------------------\n\n.. function:: void obs_register_output(struct obs_output_info *info)\n\n   Registers an output type.  Typically used in\n   :c:func:`obs_module_load()` or in the program's initialization phase.\n\n---------------------\n\n.. function:: const char *obs_output_get_display_name(const char *id)\n\n   Calls the :c:member:`obs_output_info.get_name` callback to get the\n   translated display name of an output type.\n\n   :param    id:            The output type string identifier\n   :return:                 The translated display name of an output type\n\n---------------------\n\n.. function:: obs_output_t *obs_output_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data)\n\n   Creates an output with the specified settings.\n\n   The \"output\" context is used for anything related to outputting the\n   final video/audio mix (E.g. streaming or recording).  Use\n   obs_output_release to release it.\n\n   :param   id:             The output type string identifier\n   :param   name:           The desired name of the output.  If this is\n                            not unique, it will be made to be unique\n   :param   settings:       The settings for the output, or *NULL* if\n                            none\n   :param   hotkey_data:    Saved hotkey data for the output, or *NULL*\n                            if none\n   :return:                 A reference to the newly created output, or\n                            *NULL* if failed\n\n---------------------\n\n.. function:: obs_output_t *obs_output_get_ref(obs_output_t *output)\n\n   Returns an incremented reference if still valid, otherwise returns\n   *NULL*. Release with :c:func:`obs_output_release()`.\n\n---------------------\n\n.. function:: void obs_output_release(obs_output_t *output)\n\n   Releases a reference to an output.  When the last reference is\n   released, the output is destroyed.\n\n---------------------\n\n.. function:: obs_weak_output_t *obs_output_get_weak_output(obs_output_t *output)\n              obs_output_t *obs_weak_output_get_output(obs_weak_output_t *weak)\n\n   These functions are used to get a weak reference from a strong output\n   reference, or a strong output reference from a weak reference.  If\n   the output is destroyed, *obs_weak_output_get_output* will return\n   *NULL*.\n\n---------------------\n\n.. function:: void obs_weak_output_addref(obs_weak_output_t *weak)\n              void obs_weak_output_release(obs_weak_output_t *weak)\n\n   Adds/releases a weak reference to an output.\n\n---------------------\n\n.. function:: bool obs_weak_output_references_output(obs_weak_output_t *weak, obs_output_t *output)\n\n   Compares a weak output reference with an output.\n\n   :return: Whether the weak output reference ties back to the specified output\n\n---------------------\n\n.. function:: const char *obs_output_get_name(const obs_output_t *output)\n\n   :return: The name of the output\n\n---------------------\n\n.. function:: const char *obs_output_get_id(const obs_output_t *output)\n\n   :return: The output's type identifier string\n\n---------------------\n\n.. function:: bool obs_output_start(obs_output_t *output)\n\n   Starts the output.\n\n   :return: *true* if output successfully started, *false* otherwise.  If\n            the output failed to start,\n            :c:func:`obs_output_get_last_error()` may contain a specific\n            error string related to the reason\n\n---------------------\n\n.. function:: void obs_output_stop(obs_output_t *output)\n\n   Requests the output to stop.  The output will wait until all data is\n   sent up until the time the call was made, then when the output has\n   successfully stopped, it will send the \"stop\" signal.  See\n   :ref:`output_signal_handler_reference` for more information on output\n   signals.\n\n---------------------\n\n.. function:: void obs_output_set_delay(obs_output_t *output, uint32_t delay_sec, uint32_t flags)\n\n   Sets the current output delay, in seconds (if the output supports delay)\n\n   If delay is currently active, it will set the delay value, but will not\n   affect the current delay, it will only affect the next time the output is\n   activated.\n\n   :param delay_sec: Amount to delay the output, in seconds\n   :param flags:      | Can be 0 or a combination of one of the following values:\n                      | OBS_OUTPUT_DELAY_PRESERVE - On reconnection, start where it left of on reconnection.  Note however that this option will consume extra memory to continually increase delay while waiting to reconnect\n\n---------------------\n\n.. function:: uint32_t obs_output_get_delay(const obs_output_t *output)\n\n   Gets the currently set delay value, in seconds.\n\n---------------------\n\n.. function:: uint32_t obs_output_get_active_delay(const obs_output_t *output)\n\n   If delay is active, gets the currently active delay value, in\n   seconds.  The active delay can increase if the\n   OBS_OUTPUT_DELAY_PRESERVE flag was set when setting a delay.\n\n---------------------\n\n.. function:: void obs_output_force_stop(obs_output_t *output)\n\n   Attempts to get the output to stop immediately without waiting for\n   data to send.\n\n---------------------\n\n.. function:: bool obs_output_active(const obs_output_t *output)\n\n   :return: *true* if the output is currently active, *false* otherwise\n\n---------------------\n\n.. function:: obs_data_t *obs_output_defaults(const char *id)\n\n   :return: An incremented reference to the output's default settings.\n            Release with :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: obs_properties_t *obs_output_properties(const obs_output_t *output)\n              obs_properties_t *obs_get_output_properties(const char *id)\n\n   Use these functions to get the properties of an output or output\n   type.  Properties are optionally used (if desired) to automatically\n   generate user interface widgets to allow users to update settings.\n\n   :return: The properties list for a specific existing output.  Free\n            with :c:func:`obs_properties_destroy()`\n\n---------------------\n\n.. function:: void obs_output_update(obs_output_t *output, obs_data_t *settings)\n\n   Updates the settings for this output context.\n\n---------------------\n\n.. function:: bool obs_output_can_pause(const obs_output_t *output)\n\n   :return: *true* if the output can be paused, *false* otherwise\n\n---------------------\n\n.. function:: bool obs_output_pause(obs_output_t *output, bool pause)\n\n   Pause an output (if supported by the output).\n\n   :return: *true* if the output was paused successfully, *false*\n            otherwise\n\n---------------------\n\n.. function:: bool obs_output_paused(const obs_output_t *output)\n\n   :return: *true* if the output is paused, *false* otherwise\n\n---------------------\n\n.. function:: obs_data_t *obs_output_get_settings(const obs_output_t *output)\n\n   :return: An incremented reference to the output's settings. Release with\n            :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: signal_handler_t *obs_output_get_signal_handler(const obs_output_t *output)\n\n   :return: The signal handler of the output. Should not be manually freed,\n            as its lifecycle is managed by libobs.\n\n---------------------\n\n.. function:: proc_handler_t *obs_output_get_proc_handler(const obs_output_t *output)\n\n   :return: The procedure handler of the output. Should not be manually freed,\n            as its lifecycle is managed by libobs.\n\n---------------------\n\n.. function:: void obs_output_set_media(obs_output_t *output, video_t *video, audio_t *audio)\n\n   Sets the current video/audio handlers for the output (typically\n   :c:func:`obs_get_video()` and :c:func:`obs_get_audio()`).  Only used\n   with raw outputs so they can catch the raw video/audio frames.\n\n---------------------\n\n.. function:: video_t *obs_output_video(const obs_output_t *output)\n              audio_t *obs_output_audio(const obs_output_t *output)\n\n   Gets the current video/audio handlers for the output.\n\n---------------------\n\n.. function:: void obs_output_set_mixer(obs_output_t *output, size_t mixer_idx)\n              size_t obs_output_get_mixer(const obs_output_t *output)\n\n   Sets/gets the current audio mixer for non-encoded outputs.  For\n   multi-track outputs, this would be the equivalent of setting the mask\n   only for the specified mixer index.\n\n---------------------\n\n.. function:: void obs_output_set_mixers(obs_output_t *output, size_t mixers)\n              size_t obs_output_get_mixers(const obs_output_t *output)\n\n   Sets/gets the current audio mixers (via mask) for non-encoded\n   multi-track outputs.  If used with single-track outputs, the\n   single-track output will use either the first set mixer track in the\n   bitmask, or the first track if none is set in the bitmask.\n\n---------------------\n\n.. function:: void obs_output_set_video_encoder(obs_output_t *output, obs_encoder_t *encoder)\n              void obs_output_set_audio_encoder(obs_output_t *output, obs_encoder_t *encoder, size_t idx)\n\n   Sets the video/audio encoders for an encoded output.\n\n   :param encoder: The video/audio encoder\n   :param idx:     The audio encoder index if the output supports\n                   multiple audio streams at once\n\n---------------------\n\n.. function:: obs_encoder_t *obs_output_get_video_encoder(const obs_output_t *output)\n              obs_encoder_t *obs_output_get_audio_encoder(const obs_output_t *output, size_t idx)\n\n   Gets the video/audio encoders for an encoded output.\n\n   :param idx:     The audio encoder index if the output supports\n                   multiple audio streams at once\n   :return:        The video/audio encoder.  The reference is not\n                   incremented\n\n---------------------\n\n.. function:: void obs_output_set_service(obs_output_t *output, obs_service_t *service)\n              obs_service_t *obs_output_get_service(const obs_output_t *output)\n\n   Sets/gets the service for outputs that require services (such as RTMP\n   outputs).  *obs_output_get_service* does not return an incremented\n   reference.\n\n---------------------\n\n.. function:: void obs_output_set_reconnect_settings(obs_output_t *output, int retry_count, int retry_sec);\n\n   Sets the auto-reconnect settings for outputs that support it.  The\n   retry time will double on each retry to prevent overloading services.\n\n   :param retry_count: Maximum retry count.  Set to 0 to disable\n                       reconnecting\n   :param retry_sec:   Starting retry wait duration, in seconds\n\n---------------------\n\n.. function:: uint64_t obs_output_get_total_bytes(const obs_output_t *output)\n\n   :return: Total bytes sent/processed\n\n---------------------\n\n.. function:: int obs_output_get_frames_dropped(const obs_output_t *output)\n\n   :return: Number of frames that were dropped due to network congestion\n\n---------------------\n\n.. function:: int obs_output_get_total_frames(const obs_output_t *output)\n\n   :return: Total frames sent/processed\n\n---------------------\n\n.. function:: void obs_output_set_preferred_size(obs_output_t *output, uint32_t width, uint32_t height)\n\n   Sets the preferred scaled resolution for this output.  Set width and height\n   to 0 to disable scaling.\n\n   If this output uses an encoder, it will call obs_encoder_set_scaled_size on\n   the encoder before the stream is started.  If the encoder is already active,\n   then this function will trigger a warning and do nothing.\n\n---------------------\n\n.. function:: uint32_t obs_output_get_width(const obs_output_t *output)\n              uint32_t obs_output_get_height(const obs_output_t *output)\n\n   :return: The width/height of the output\n\n---------------------\n\n.. function:: void obs_output_output_caption_text1(obs_output_t *output, const char *text)\n              void obs_output_output_caption_text2(obs_output_t *output, const char *text, double display_duration)\n\n   Outputs captions from the specified text input. *text1* is the same as\n   *text2*, except that the *display_duration* is hardcoded to 2.0 seconds.\n\n   *display_duration* represents the minimum quantity of time that a given\n   caption can be displayed for before moving onto the next caption in the\n   queue.\n\n---------------------\n\n.. function:: float obs_output_get_congestion(obs_output_t *output)\n\n   :return: The congestion value.  This value is used to visualize the\n            current congestion of a network output.  For example, if\n            there is no congestion, the value will be 0.0f, if it's\n            fully congested, the value will be 1.0f\n\n---------------------\n\n.. function:: int obs_output_get_connect_time_ms(obs_output_t *output)\n\n   :return: How long the output took to connect to a server, in\n            milliseconds\n\n---------------------\n\n.. function:: bool obs_output_reconnecting(const obs_output_t *output)\n\n   :return: *true* if the output is currently reconnecting to a server,\n            *false* otherwise\n\n---------------------\n\n.. function:: const char *obs_output_get_supported_video_codecs(const obs_output_t *output)\n              const char *obs_get_output_supported_video_codecs(const char *id)\n              const char *obs_output_get_supported_audio_codecs(const obs_output_t *output)\n              const char *obs_get_output_supported_audio_codecs(const char *id)\n\n   :return: Supported video/audio codecs of an encoded output, separated\n            by semicolon\n\n   .. versionadded:: 29.1\n      :c:func:`obs_get_output_supported_video_codecs` and\n      :c:func:`obs_get_output_supported_audio_codecs`\n\n---------------------\n\n.. function:: uint32_t obs_output_get_flags(const obs_output_t *output)\n              uint32_t obs_get_output_flags(const char *id)\n\n   :return: The output capability flags\n\n---------------------\n\n.. function:: const char *obs_output_get_protocols(const obs_output_t *output)\n\n   :return: Supported protocols, separated by semicolon. Always NULL if the\n            output is not **OBS_OUTPUT_SERVICE**.\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: bool obs_is_output_protocol_registered(const char *protocol)\n\n   Check if one of the registered output use the given protocol.\n\n   :return:                 A boolean showing if an output with the given\n                            protocol is registered\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: bool obs_enum_output_protocols(size_t idx, char **protocol)\n\n   Enumerates all registered protocol.\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: void obs_enum_output_types_with_protocol(const char *protocol, void *data, bool (*enum_cb)(void *data, const char *id))\n\n   Enumerates through a callback all available output types for the given protocol.\n\n   :param protocol: Protocol of the outputs to enumerate\n   :param data:     Data passed to the callback\n   :param enum_cb:  Callback used when a matching output is found, the id\n                    of the output is passed to the callback\n   :return:         When all outputs are enumerated or if the callback return *false*\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: void obs_output_add_packet_callback(obs_output_t *output, void (*packet_cb)(obs_output_t *output,\n              struct encoder_packet *pkt, struct encoder_packet_time *pkt_time, void *param), void *param)\n\n   Register a packet callback function for the output. The callback is invoked for each compressed\n   packet just before sending to the service. This packet callback mechanism is the preferred method\n   for all packet-level processing that is not required to be implemented in libobs. Any reallocation\n   of the packet buffer, if necessary, must be done with functions in `libobs\\util\\bmem.h`, otherwise\n   a memory leak may occur. Never use `memset()` to clear the packet buffer, as the buffer data is\n   needed for subsequent callback processing.\n\n   :param output:     The output to register the packet_cb() function against\n   :param packet_cb:  Function pointer to the callback function\n   :param param:      Data passed to the callback\n   :return:           When the callback is added\n\n   packet_cb() arguments:\n   :param output:     The output associated with the invoked callback function\n   :param pkt:        Compressed data packet (audio or video)\n   :param pkt_time:   encoder_packet_time structure associated with the data packet\n   :param param:      Data passed to the callback\n\n   .. versionadded:: 31.0\n\n---------------------\n\n.. function:: void obs_output_remove_packet_callback(obs_output_t *output, void (*packet_cb)(obs_output_t *output,\n              struct encoder_packet *pkt, struct encoder_packet_time *pkt_time, void *param), void *param)\n\n   Remove a packet callback function for the output, that had been previously registered with\n   `obs_output_add_packet_callback()`.\n\n   :param output:     The output to remove the packet_cb() function against\n   :param packet_cb:  Function pointer to the callback function\n   :param param:      Data passed to the callback\n   :return:           When the callback is removed\n\n   .. versionadded:: 31.0\n\n---------------------\n\n.. function:: void obs_output_set_reconnect_callback(obs_output_t *output, bool (*reconnect_cb)(void *data, obs_output_t *output, int code), void *param)\n\n   Sets a callback that can be used to decide whether or not to attempt reconnecting.\n   Can also be used to reconfigure the output within the callback before continuing the reconnection attempts,\n   e.g., to update the stream key after the previous one has expired.\n   \n   Passing in a `NULL` pointer removes the callback.\n\n   :param output:        The output to register the reconnect_cb() function against\n   :param reconnect_cb:  Function pointer to the callback function\n   :param param:         Data passed to the callback\n\n   reconnect_cb() arguments:\n   :param data:    Data passed to the callback\n   :param output:  The output associated with the invoked callback function\n   :param code:    Error code the output was stopped with\n   :return:        Whether or not to continue attempting to reconnect\n\n   .. versionadded:: 31.1\n\nFunctions used by outputs\n-------------------------\n\n.. function:: void obs_output_set_last_error(obs_output_t *output, const char *message)\n              const char *obs_output_get_last_error(obs_output_t *output)\n\n   Sets/gets the translated error message that is presented to a user in\n   case of disconnection, inability to connect, etc.\n\n---------------------\n\n.. function:: void obs_output_set_video_conversion(obs_output_t *output, const struct video_scale_info *conversion)\n              const struct video_scale_info *obs_output_get_video_conversion(obs_output_t *output)\n\n   Optionally sets/gets the video conversion information.  Only used by\n   raw outputs.\n\n   .. versionadded:: 29.1\n     :c:func:`obs_output_get_video_conversion`\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   enum video_format {\n           VIDEO_FORMAT_NONE,\n\n           /* planar 4:2:0 formats */\n           VIDEO_FORMAT_I420, /* three-plane */\n           VIDEO_FORMAT_NV12, /* two-plane, luma and packed chroma */\n\n           /* packed 4:2:2 formats */\n           VIDEO_FORMAT_YVYU,\n           VIDEO_FORMAT_YUY2, /* YUYV */\n           VIDEO_FORMAT_UYVY,\n\n           /* packed uncompressed formats */\n           VIDEO_FORMAT_RGBA,\n           VIDEO_FORMAT_BGRA,\n           VIDEO_FORMAT_BGRX,\n           VIDEO_FORMAT_Y800, /* grayscale */\n\n           /* planar 4:4:4 */\n           VIDEO_FORMAT_I444,\n\n           /* more packed uncompressed formats */\n           VIDEO_FORMAT_BGR3,\n\n           /* planar 4:2:2 */\n           VIDEO_FORMAT_I422,\n\n           /* planar 4:2:0 with alpha */\n           VIDEO_FORMAT_I40A,\n\n           /* planar 4:2:2 with alpha */\n           VIDEO_FORMAT_I42A,\n\n           /* planar 4:4:4 with alpha */\n           VIDEO_FORMAT_YUVA,\n\n           /* packed 4:4:4 with alpha */\n           VIDEO_FORMAT_AYUV,\n\n           /* planar 4:2:0 format, 10 bpp */\n           VIDEO_FORMAT_I010, /* three-plane */\n           VIDEO_FORMAT_P010, /* two-plane, luma and packed chroma */\n\n           /* planar 4:2:2 format, 10 bpp */\n           VIDEO_FORMAT_I210,\n\n           /* planar 4:4:4 format, 12 bpp */\n           VIDEO_FORMAT_I412,\n\n           /* planar 4:4:4:4 format, 12 bpp */\n           VIDEO_FORMAT_YA2L,\n\n           /* planar 4:2:2 format, 16 bpp */\n           VIDEO_FORMAT_P216, /* two-plane, luma and packed chroma */\n\n           /* planar 4:4:4 format, 16 bpp */\n           VIDEO_FORMAT_P416, /* two-plane, luma and packed chroma */\n\n           /* packed 4:2:2 format, 10 bpp */\n           VIDEO_FORMAT_V210,\n\n           /* packed uncompressed 10-bit format */\n           VIDEO_FORMAT_R10L,\n   };\n\n   enum video_colorspace {\n           VIDEO_CS_DEFAULT,\n           VIDEO_CS_601,\n           VIDEO_CS_709,\n           VIDEO_CS_SRGB,\n           VIDEO_CS_2100_PQ,\n           VIDEO_CS_2100_HLG,\n   };\n\n   enum video_range_type {\n           VIDEO_RANGE_DEFAULT,\n           VIDEO_RANGE_PARTIAL,\n           VIDEO_RANGE_FULL\n   };\n\n   struct video_scale_info {\n           enum video_format     format;\n           uint32_t              width;\n           uint32_t              height;\n           enum video_range_type range;\n           enum video_colorspace colorspace;\n   };\n\n---------------------\n\n.. function:: void obs_output_set_audio_conversion(obs_output_t *output, const struct audio_convert_info *conversion)\n\n   Optionally sets the audio conversion information.  Only used by raw\n   outputs.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   enum audio_format {\n           AUDIO_FORMAT_UNKNOWN,\n\n           AUDIO_FORMAT_U8BIT,\n           AUDIO_FORMAT_16BIT,\n           AUDIO_FORMAT_32BIT,\n           AUDIO_FORMAT_FLOAT,\n\n           AUDIO_FORMAT_U8BIT_PLANAR,\n           AUDIO_FORMAT_16BIT_PLANAR,\n           AUDIO_FORMAT_32BIT_PLANAR,\n           AUDIO_FORMAT_FLOAT_PLANAR,\n   };\n\n   enum speaker_layout {\n           SPEAKERS_UNKNOWN,\n           SPEAKERS_MONO,\n           SPEAKERS_STEREO,\n           SPEAKERS_2POINT1,\n           SPEAKERS_4POINT0,\n           SPEAKERS_4POINT1,\n           SPEAKERS_5POINT1,\n           SPEAKERS_5POINT1_SURROUND,\n           SPEAKERS_7POINT1,\n           SPEAKERS_7POINT1_SURROUND,\n           SPEAKERS_SURROUND,\n   };\n\n   struct audio_convert_info {\n           uint32_t            samples_per_sec;\n           enum audio_format   format;\n           enum speaker_layout speakers;\n   };\n\n---------------------\n\n.. function:: bool obs_output_can_begin_data_capture(const obs_output_t *output, int flags)\n\n   Determines whether video/audio capture (encoded or raw) is able to\n   start.  Call this before initializing any output state to ensure that\n   the output can start.\n\n   :param output: The output\n   :param flags: Reserved. Set this to 0.\n   :return:      *true* if data capture can begin\n\n---------------------\n\n.. function:: bool obs_output_initialize_encoders(obs_output_t *output, int flags)\n\n   Initializes any encoders/services associated with the output.  This\n   must be called for encoded outputs before calling\n   :c:func:`obs_output_begin_data_capture()`.\n\n   :param output: The output\n   :param flags: Reserved. Set this to 0.\n   :return:      *true* if successful, *false* otherwise\n\n---------------------\n\n.. function:: bool obs_output_begin_data_capture(obs_output_t *output, int flags)\n\n   Begins data capture from raw media or encoders.  This is typically\n   when the output actually activates (starts) internally.  Video/audio\n   data will start being sent to the callbacks of the output.\n\n   :param output: The output\n   :param flags: Reserved. Set this to 0.\n   :return:      *true* if successful, *false* otherwise.  Typically the\n                 return value does not need to be checked if\n                 :c:func:`obs_output_can_begin_data_capture2()` was\n                 called\n\n---------------------\n\n.. function:: void obs_output_end_data_capture(obs_output_t *output)\n\n   Ends data capture of an output.  This is typically when the output\n   actually intentionally deactivates (stops).  Video/audio data will\n   stop being sent to the callbacks of the output.  The output will\n   trigger the \"stop\" signal with the OBS_OUTPUT_SUCCESS code to\n   indicate that the output has stopped successfully.  See\n   :ref:`output_signal_handler_reference` for more information on output\n   signals.\n\n---------------------\n\n.. function:: void obs_output_signal_stop(obs_output_t *output, int code)\n\n   Ends data capture of an output with an output code, indicating that\n   the output stopped unexpectedly.  This is typically used if for\n   example the server was disconnected for some reason, or if there was\n   an error saving to file.  The output will trigger the \"stop\" signal\n   with the the desired code to indicate that the output has stopped\n   successfully.  See :ref:`output_signal_handler_reference` for more\n   information on output signals.\n\n   :c:func:`obs_output_set_last_error()` may be used in conjunction with\n   these error codes to optionally relay more detailed error information\n   to the user\n\n   :param code: | Can be one of the following values:\n                | OBS_OUTPUT_SUCCESS        - Successfully stopped\n                | OBS_OUTPUT_BAD_PATH       - The specified path was invalid\n                | OBS_OUTPUT_CONNECT_FAILED - Failed to connect to a server\n                | OBS_OUTPUT_INVALID_STREAM - Invalid stream path\n                | OBS_OUTPUT_ERROR          - Generic error\n                | OBS_OUTPUT_DISCONNECTED   - Unexpectedly disconnected\n                | OBS_OUTPUT_UNSUPPORTED    - The settings, video/audio format, or codecs are unsupported by this output\n                | OBS_OUTPUT_NO_SPACE       - Ran out of disk space\n\n---------------------\n\n.. function:: uint64_t obs_output_get_pause_offset(obs_output_t *output)\n\n   Returns the current pause offset of the output.  Used with raw\n   outputs to calculate system timestamps when using calculated\n   timestamps (see FFmpeg output for an example).\n\n.. ---------------------------------------------------------------------------\n\n.. _libobs/obs-output.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-output.h\n"
  },
  {
    "path": "docs/sphinx/reference-properties.rst",
    "content": "Properties API Reference (obs_properties_t)\n===========================================\n\nProperties are used to enumerate available settings for a libobs object.\nTypically this is used to automatically generate user interface widgets,\nthough can be used to enumerate available and/or valid values for\nspecific settings as well.\n\n.. type:: obs_properties_t\n\n   Properties object (not reference counted).\n\n.. type:: obs_property_t\n\n   A property object (not reference counted).\n\n.. code:: cpp\n\n   #include <obs.h>\n\n\nGeneral Functions\n-----------------\n\n.. function:: obs_properties_t *obs_properties_create(void)\n\n   :return: A new properties object.\n\n---------------------\n\n.. function:: obs_properties_t *obs_properties_create_param(void *param, void (*destroy)(void *param))\n\n   Creates a new properties object with specific private data *param*\n   associated with the object, and is automatically freed with the\n   object when the properties are destroyed via the *destroy* function.\n\n   :return: A new properties object.\n\n---------------------\n\n.. function:: void obs_properties_destroy(obs_properties_t *props)\n\n---------------------\n\n.. function:: void obs_properties_set_flags(obs_properties_t *props, uint32_t flags)\n              uint32_t obs_properties_get_flags(obs_properties_t *props)\n\n   :param flags: 0 or a bitwise OR combination of one of the following\n                 values:\n                 \n                 - OBS_PROPERTIES_DEFER_UPDATE - A hint that tells the\n                   front-end to defers updating the settings until the\n                   user has finished editing all properties rather than\n                   immediately updating any settings\n\n---------------------\n\n.. function:: void obs_properties_set_param(obs_properties_t *props, void *param, void (*destroy)(void *param))\n              void *obs_properties_get_param(obs_properties_t *props)\n\n   Sets custom data associated with this properties object.  If private\n   data is already associated with the object, that private data will be\n   destroyed before assigning new private data to it.\n\n---------------------\n\n.. function:: void obs_properties_apply_settings(obs_properties_t *props, obs_data_t *settings)\n\n   Applies settings to the properties by calling all the necessary\n   modification callbacks\n\n---------------------\n\n.. function:: obs_properties_t *obs_properties_get_parent(obs_properties_t *props)\n\n---------------------\n\n.. function:: void obs_properties_remove_by_name(obs_properties_t *props, const char *property)\n\n   Removes a property from a properties list. Only valid in ``get_properties``,\n   ``script_properties`` for scripts, ``modified_callback``, and ``modified_callback2``.\n   ``modified_callback`` and ``modified_callback2`` *must* return true so that\n   all UI properties are rebuilt. Returning false is undefined behavior.\n\n   :param props:    Properties to remove from.\n   :param property: Name of the property to remove.\n\n---------------------\n\n\nProperty Object Functions\n-------------------------\n\n.. function:: obs_property_t *obs_properties_add_bool(obs_properties_t *props, const char *name, const char *description)\n\n   Adds a boolean property.\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :return:               The property\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_int(obs_properties_t *props, const char *name, const char *description, int min, int max, int step)\n\n   Adds an integer property.\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :param    min:         Minimum value\n   :param    max:         Maximum value\n   :param    step:        Step value\n   :return:               The property\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_float(obs_properties_t *props, const char *name, const char *description, double min, double max, double step)\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :param    min:         Minimum value\n   :param    max:         Maximum value\n   :param    step:        Step value\n   :return:               The property\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_int_slider(obs_properties_t *props, const char *name, const char *description, int min, int max, int step)\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :param    min:         Minimum value\n   :param    max:         Maximum value\n   :param    step:        Step value\n   :return:               The property\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_float_slider(obs_properties_t *props, const char *name, const char *description, double min, double max, double step)\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :param    min:         Minimum value\n   :param    max:         Maximum value\n   :param    step:        Step value\n   :return:               The property\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_text(obs_properties_t *props, const char *name, const char *description, enum obs_text_type type)\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :param    type:        Can be one of the following values:\n\n                          - **OBS_TEXT_DEFAULT** - Single line of text\n                          - **OBS_TEXT_PASSWORD** - Single line of text (passworded)\n                          - **OBS_TEXT_MULTILINE** - Multi-line text\n                          - **OBS_TEXT_INFO** - Read-only informative text, behaves differently\n                            depending on whether description, string value and long description\n                            are set\n\n   :return:               The property\n\n   Important Related Functions:\n\n   - :c:func:`obs_property_text_set_info_type`\n   - :c:func:`obs_property_text_set_info_word_wrap`\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_path(obs_properties_t *props, const char *name, const char *description, enum obs_path_type type, const char *filter, const char *default_path)\n\n   Adds a 'path' property.  Can be a directory or a file.\n\n   If target is a file path, the filters should be this format, separated by\n   double semicolons, and extensions separated by space::\n\n     \"Example types 1 and 2 (*.ex1 *.ex2);;Example type 3 (*.ex3)\"\n\n   :param    name:         Setting identifier string\n   :param    description:  Localized name shown to user\n   :param    type:         Can be one of the following values:\n\n                           - **OBS_PATH_FILE** - File (for reading)\n                           - **OBS_PATH_FILE_SAVE** - File (for writing)\n                           - **OBS_PATH_DIRECTORY** - Directory\n\n   :param    filter:       If type is a file path, then describes the file filter\n                           that the user can browse.  Items are separated via\n                           double semicolons.  If multiple file types in a\n                           filter, separate with space.\n   :param    default_path: The default path to start in, or *NULL*\n   :return:                The property\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_list(obs_properties_t *props, const char *name, const char *description, enum obs_combo_type type, enum obs_combo_format format)\n\n   Adds an integer/string/floating point item list.  This would be\n   implemented as a combo box in user interface.\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :param    type:        Can be one of the following values:\n\n                          - **OBS_COMBO_TYPE_EDITABLE** - Can be edited.\n                            Only used with string lists.\n                          - **OBS_COMBO_TYPE_LIST** - Not editable. Displayed as combo box.\n                          - **OBS_COMBO_TYPE_RADIO** - Not editable. Displayed as radio buttons.\n\n                              .. versionadded:: 30.0\n\n   :param    format:      Can be one of the following values:\n\n                          - **OBS_COMBO_FORMAT_INT** - Integer list\n                          - **OBS_COMBO_FORMAT_FLOAT** - Floating point\n                            list\n                          - **OBS_COMBO_FORMAT_STRING** - String list\n                          - **OBS_COMBO_FORMAT_BOOL** - Boolean list\n\n                              .. versionadded:: 30.0\n\n   :return:               The property\n\n   Important Related Functions:\n\n   - :c:func:`obs_property_list_add_string`\n   - :c:func:`obs_property_list_add_int`\n   - :c:func:`obs_property_list_add_float`\n   - :c:func:`obs_property_list_insert_string`\n   - :c:func:`obs_property_list_insert_int`\n   - :c:func:`obs_property_list_insert_float`\n   - :c:func:`obs_property_list_item_remove`\n   - :c:func:`obs_property_list_clear`\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_color(obs_properties_t *props, const char *name, const char *description)\n\n   Adds a color property without alpha (stored internally with an alpha value of 255).\n   The color can be retrieved from an :c:type:`obs_data_t` object by using :c:func:`obs_data_get_int()`.\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :return:               The property\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_color_alpha(obs_properties_t *props, const char *name, const char *description)\n\n   Adds a color property with alpha. The color can be retrieved from an\n   :c:type:`obs_data_t` object by using :c:func:`obs_data_get_int()`.\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :return:               The property\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_button(obs_properties_t *props, const char *name, const char *text, obs_property_clicked_t callback)\n              obs_property_t *obs_properties_add_button2(obs_properties_t *props, const char *name, const char *text, obs_property_clicked_t callback, void *priv)\n\n   Adds a button property.  This property does not actually store any\n   settings; it's used to implement a button in user interface if the\n   properties are used to generate user interface.\n\n   If the properties need to be refreshed due to changes to the property layout,\n   the callback should return true, otherwise return false.\n\n   :param    name:        Setting identifier string\n   :param    text:        Localized name shown to user\n   :param    callback:    Callback to be executed when the button is pressed. Note that if the property\n                          is created with :c:func:`obs_properties_add_button` instead of\n                          :c:func:`obs_properties_add_button2`, the value of ``data`` is determined by\n                          the caller of :c:func:`obs_property_button_clicked`, and as such unspecified\n                          by libobs.\n   :param    priv:        Pointer passed back as the `data` argument of the callback\n   :return:               The property\n\n   Important Related Functions:\n\n      - :c:func:`obs_property_button_set_type`\n      - :c:func:`obs_property_button_set_url`\n\n   For scripting, use :py:func:`obs_properties_add_button`.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   typedef bool (*obs_property_clicked_t)(obs_properties_t *props,\n                   obs_property_t *property, void *data);\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_font(obs_properties_t *props, const char *name, const char *description)\n\n   Adds a font property. The font can be retrieved from an :c:type:`obs_data_t`\n   object by using :c:func:`obs_data_get_obj()`.\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :return:               The property\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_editable_list(obs_properties_t *props, const char *name, const char *description, enum obs_editable_list_type type, const char *filter, const char *default_path)\n\n   Adds a list in which the user can add/insert/remove items. The items can be\n   retrieved from an :c:type:`obs_data_t` object by using :c:func:`obs_data_get_array()`.\n\n   :param    name:         Setting identifier string\n   :param    description:  Localized name shown to user\n   :param    type:         Can be one of the following values:\n                          \n                           - **OBS_EDITABLE_LIST_TYPE_STRINGS** - An\n                             editable list of strings.\n                           - **OBS_EDITABLE_LIST_TYPE_FILES** - An\n                             editable list of files.\n                           - **OBS_EDITABLE_LIST_TYPE_FILES_AND_URLS** -\n                             An editable list of files and URLs.\n\n   :param    filter:       File filter to use if a file list\n   :param    default_path: Default path if a file list\n   :return:                The property\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_add_frame_rate(obs_properties_t *props, const char *name, const char *description)\n\n   Adds a frame rate property.\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :return:               The property\n\n   Important Related Functions:\n\n   - :c:func:`obs_property_frame_rate_option_add`\n   - :c:func:`obs_property_frame_rate_fps_range_add`\n   - :c:func:`obs_property_frame_rate_option_insert`\n   - :c:func:`obs_property_frame_rate_fps_range_insert`\n\n---------------------\n\n.. function:: 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)\n\n   Adds a property group.\n\n   :param    name:        Setting identifier string\n   :param    description: Localized name shown to user\n   :param    type:        Can be one of the following values:\n\n                          - **OBS_GROUP_NORMAL** - A normal group with just a name and content.\n                          - **OBS_GROUP_CHECKABLE** - A checkable group with a checkbox, name and content.\n\n   :param    group:       Group to add\n\n   :return:               The property\n\n   Important Related Functions:\n\n   - :c:func:`obs_property_group_type`\n   - :c:func:`obs_property_group_content`\n   - :c:func:`obs_properties_get_parent`\n\n---------------------\n\n\nProperty Enumeration Functions\n------------------------------\n\n.. function:: obs_property_t *obs_properties_first(obs_properties_t *props)\n\n   :return: The first property in the properties object.\n\n---------------------\n\n.. function:: obs_property_t *obs_properties_get(obs_properties_t *props, const char *property)\n\n   :param property: The name of the property to get\n   :return:         A specific property or *NULL* if not found\n\n---------------------\n\n.. function:: bool                   obs_property_next(obs_property_t **p)\n\n   :param p: Pointer to the pointer of the next property\n   :return: *true* if successful, *false* if no more properties\n\n---------------------\n\n.. function:: const char *           obs_property_name(obs_property_t *p)\n\n   :return: The setting identifier string of the property\n\n   *(Author's Note: \"name\" was a bad name to use here.  Should have been\n   \"setting\")*\n\n---------------------\n\n.. function:: const char *           obs_property_description(obs_property_t *p)\n\n   :return: The actual localized display name of the property\n\n   *(Author's note: This one should have been the \"name\")*\n\n---------------------\n\n.. function:: const char *           obs_property_long_description(obs_property_t *p)\n\n   :return: A detailed description of what the setting is used for.\n            Usually used with things like tooltips.\n\n---------------------\n\n.. function:: enum obs_property_type obs_property_get_type(obs_property_t *p)\n\n   :return: One of the following values:\n\n            - OBS_PROPERTY_INVALID\n            - OBS_PROPERTY_BOOL\n            - OBS_PROPERTY_INT\n            - OBS_PROPERTY_FLOAT\n            - OBS_PROPERTY_TEXT\n            - OBS_PROPERTY_PATH\n            - OBS_PROPERTY_LIST\n            - OBS_PROPERTY_COLOR\n            - OBS_PROPERTY_BUTTON\n            - OBS_PROPERTY_FONT\n            - OBS_PROPERTY_EDITABLE_LIST\n            - OBS_PROPERTY_FRAME_RATE\n            - OBS_PROPERTY_GROUP\n\n---------------------\n\n.. function:: bool                   obs_property_enabled(obs_property_t *p)\n\n---------------------\n\n.. function:: bool                   obs_property_visible(obs_property_t *p)\n\n---------------------\n\n.. function:: int                    obs_property_int_min(obs_property_t *p)\n\n---------------------\n\n.. function:: int                    obs_property_int_max(obs_property_t *p)\n\n---------------------\n\n.. function:: int                    obs_property_int_step(obs_property_t *p)\n\n---------------------\n\n.. function:: enum obs_number_type   obs_property_int_type(obs_property_t *p)\n\n---------------------\n\n.. function:: const char *           obs_property_int_suffix(obs_property_t *p)\n\n---------------------\n\n.. function:: double                 obs_property_float_min(obs_property_t *p)\n\n---------------------\n\n.. function:: double                 obs_property_float_max(obs_property_t *p)\n\n---------------------\n\n.. function:: double                 obs_property_float_step(obs_property_t *p)\n\n---------------------\n\n.. function:: enum obs_number_type   obs_property_float_type(obs_property_t *p)\n\n---------------------\n\n.. function:: const char *           obs_property_float_suffix(obs_property_t *p)\n\n---------------------\n\n.. function:: enum obs_text_type     obs_property_text_type(obs_property_t *p)\n\n---------------------\n\n.. function:: bool                   obs_property_text_monospace(obs_property_t *p)\n\n   Returns whether the input of the text property should be rendered\n   with a monospace font or not. Only has an effect if the text type\n   of the property is ``OBS_TEXT_MULTILINE``, even if this returns *true*.\n\n---------------------\n\n.. function:: enum obs_text_info_type     obs_property_text_info_type(obs_property_t *p)\n\n   :return: One of the following values:\n\n             - OBS_TEXT_INFO_NORMAL\n             - OBS_TEXT_INFO_WARNING\n             - OBS_TEXT_INFO_ERROR\n\n---------------------\n\n.. function:: bool     obs_property_text_info_word_wrap(obs_property_t *p)\n\n---------------------\n\n.. function:: enum obs_path_type     obs_property_path_type(obs_property_t *p)\n\n---------------------\n\n.. function:: const char *           obs_property_path_filter(obs_property_t *p)\n\n---------------------\n\n.. function:: const char *           obs_property_path_default_path(obs_property_t *p)\n\n---------------------\n\n.. function:: enum obs_combo_type    obs_property_list_type(obs_property_t *p)\n\n---------------------\n\n.. function:: enum obs_combo_format  obs_property_list_format(obs_property_t *p)\n\n---------------------\n\n.. function:: bool obs_property_list_item_disabled(obs_property_t *p, size_t idx)\n\n---------------------\n\n.. function:: size_t      obs_property_list_item_count(obs_property_t *p)\n\n---------------------\n\n.. function:: const char *obs_property_list_item_name(obs_property_t *p, size_t idx)\n\n---------------------\n\n.. function:: const char *obs_property_list_item_string(obs_property_t *p, size_t idx)\n\n---------------------\n\n.. function:: long long   obs_property_list_item_int(obs_property_t *p, size_t idx)\n\n---------------------\n\n.. function:: double      obs_property_list_item_float(obs_property_t *p, size_t idx)\n\n---------------------\n\n.. function:: enum obs_editable_list_type obs_property_editable_list_type(obs_property_t *p)\n\n---------------------\n\n.. function:: const char *obs_property_editable_list_filter(obs_property_t *p)\n\n---------------------\n\n.. function:: const char *obs_property_editable_list_default_path(obs_property_t *p)\n\n---------------------\n\n.. function:: size_t      obs_property_frame_rate_options_count(obs_property_t *p)\n\n---------------------\n\n.. function:: const char *obs_property_frame_rate_option_name(obs_property_t *p, size_t idx)\n\n---------------------\n\n.. function:: const char *obs_property_frame_rate_option_description( obs_property_t *p, size_t idx)\n\n---------------------\n\n.. function:: size_t      obs_property_frame_rate_fps_ranges_count(obs_property_t *p)\n\n---------------------\n\n.. function:: struct media_frames_per_second obs_property_frame_rate_fps_range_min( obs_property_t *p, size_t idx)\n\n---------------------\n\n.. function:: struct media_frames_per_second obs_property_frame_rate_fps_range_max( obs_property_t *p, size_t idx)\n\n---------------------\n\n.. function:: enum obs_button_type obs_property_button_type(obs_property_t *p)\n\n   :return: One of the following values:\n\n             - OBS_BUTTON_DEFAULT\n             - OBS_BUTTON_URL\n\n---------------------\n\n.. function:: const char *obs_property_button_url(obs_property_t *p)\n\n---------------------\n\n.. function:: enum obs_group_type obs_property_group_type(obs_property_t *p)\n\n  :return: One of the following values:\n\n            - OBS_COMBO_INVALID\n            - OBS_GROUP_NORMAL\n            - OBS_GROUP_CHECKABLE\n\n---------------------\n\n.. function:: obs_properties_t *obs_property_group_content(obs_property_t *p)\n\n---------------------\n\n\nProperty Modification Functions\n-------------------------------\n\n.. function:: void obs_property_set_modified_callback(obs_property_t *p, obs_property_modified_t modified)\n              void obs_property_set_modified_callback2(obs_property_t *p, obs_property_modified2_t modified2, void *priv)\n\n   Allows the ability to change the properties depending on what\n   settings are used by the user. The callback should return ``true``\n   if the property widgets need to be refreshed due to changes to the\n   property layout.\n\n   Relevant data types used with these functions:\n\n.. code:: cpp\n\n   typedef bool (*obs_property_modified_t)(obs_properties_t *props,\n                   obs_property_t *property, obs_data_t *settings);\n   typedef bool (*obs_property_modified2_t)(void *priv,\n                   obs_properties_t *props, obs_property_t *property,\n                   obs_data_t *settings);\n\n---------------------\n\n.. function:: bool obs_property_modified(obs_property_t *p, obs_data_t *settings)\n\n---------------------\n\n.. function:: bool obs_property_button_clicked(obs_property_t *p, void *obj)\n\n   Calls the ``callback`` of the button.\n\n   :param p:   The property\n   :param obj: Must be a pointer to an object of type ``obs_canvas_t``, ``obs_source_t``,\n               ``obs_output_t``, ``obs_encoder_t``, or ``obs_service_t``.\n               If the property was created with :c:func:`obs_properties_add_button`, that\n               object's context will be passed as the ``obj`` parameter.\n               If the property was created with :c:func:`obs_properties_add_button2`, it\n               will be ignored.\n\n---------------------\n\n.. function:: void obs_property_set_visible(obs_property_t *p, bool visible)\n\n---------------------\n\n.. function:: void obs_property_set_enabled(obs_property_t *p, bool enabled)\n\n---------------------\n\n.. function:: void obs_property_set_description(obs_property_t *p, const char *description)\n\n   Sets the displayed localized name of the property, shown to the user.\n\n---------------------\n\n.. function:: void obs_property_set_long_description(obs_property_t *p, const char *long_description)\n\n   Sets the localized long description of the property, usually shown to\n   a user via tooltip.\n\n---------------------\n\n.. function:: void obs_property_int_set_limits(obs_property_t *p, int min, int max, int step)\n\n---------------------\n\n.. function:: void obs_property_float_set_limits(obs_property_t *p, double min, double max, double step)\n\n---------------------\n\n.. function:: void obs_property_int_set_suffix(obs_property_t *p, const char *suffix)\n\n   Adds a suffix to the int property, such that 100 will show up\n   as \"100ms\" if the suffix is \"ms\". The user will only be able\n   to edit the number, not the suffix.\n\n---------------------\n\n.. function:: void obs_property_float_set_suffix(obs_property_t *p, const char *suffix)\n\n   Adds a suffix to the float property, such that 1.5 will show up\n   as \"1.5s\" if the suffix is \"s\". The user will only be able\n   to edit the number, not the suffix.\n\n---------------------\n\n.. function:: void obs_property_text_set_monospace(obs_property_t *p, bool monospace)\n\n   Sets whether the input of text property should be rendered with\n   a monospace font or not. Only has an effect if the text type of\n   the property is ``OBS_TEXT_MULTILINE``.\n\n---------------------\n\n.. function:: void     obs_property_text_set_info_type(obs_property_t *p, enum obs_text_info_type type)\n\n   :param   type: Can be one of the following values:\n\n                  - **OBS_TEXT_INFO_NORMAL** - To show a basic message\n                  - **OBS_TEXT_INFO_WARNING** - To show a warning\n                  - **OBS_TEXT_INFO_ERROR** - To show an error\n\n---------------------\n\n.. function:: void     obs_property_text_set_info_word_wrap(obs_property_t *p, bool word_wrap)\n\n---------------------\n\n.. function:: void obs_property_list_clear(obs_property_t *p)\n\n---------------------\n\n.. function:: size_t obs_property_list_add_string(obs_property_t *p, const char *name, const char *val)\n\n   Adds a string to a string list.\n\n   :param    name: Localized name shown to user\n   :param    val:  The actual string value stored and will be returned by :c:func:`obs_data_get_string`\n   :returns: The index of the list item.\n\n---------------------\n\n.. function:: size_t obs_property_list_add_int(obs_property_t *p, const char *name, long long val)\n\n   Adds an integer to a integer list.\n\n   :param    name: Localized name shown to user\n   :param    val:  The actual int value stored and will be returned by :c:func:`obs_data_get_int`\n   :returns: The index of the list item.\n\n---------------------\n\n.. function:: size_t obs_property_list_add_float(obs_property_t *p, const char *name, double val)\n\n   Adds a floating point to a floating point list.\n\n   :param    name: Localized name shown to user\n   :param    val:  The actual float value stored and will be returned by :c:func:`obs_data_get_double`\n   :returns: The index of the list item.\n\n---------------------\n\n.. function:: void obs_property_list_insert_string(obs_property_t *p, size_t idx, const char *name, const char *val)\n\n   Inserts a string in to a string list.\n\n   :param    idx:  The index of the list item\n   :param    name: Localized name shown to user\n   :param    val:  The actual string value stored and will be returned by :c:func:`obs_data_get_string`\n\n---------------------\n\n.. function:: void obs_property_list_insert_int(obs_property_t *p, size_t idx, const char *name, long long val)\n\n   Inserts an integer in to an integer list.\n\n   :param    idx:  The index of the list item\n   :param    name: Localized name shown to user\n   :param    val:  The actual int value stored and will be returned by :c:func:`obs_data_get_int`\n\n---------------------\n\n.. function:: void obs_property_list_insert_float(obs_property_t *p, size_t idx, const char *name, double val)\n\n   Inserts a floating point in to a floating point list.\n\n   :param    idx:  The index of the list item.\n   :param    name: Localized name shown to user\n   :param    val:  The actual float value stored and will be returned by :c:func:`obs_data_get_double`\n\n---------------------\n\n.. function:: void obs_property_list_item_disable(obs_property_t *p, size_t idx, bool disabled)\n\n---------------------\n\n.. function:: void obs_property_list_item_remove(obs_property_t *p, size_t idx)\n\n---------------------\n\n.. function:: size_t obs_property_frame_rate_option_add(obs_property_t *p, const char *name, const char *description)\n\n---------------------\n\n.. function:: size_t obs_property_frame_rate_fps_range_add(obs_property_t *p, struct media_frames_per_second min, struct media_frames_per_second max)\n\n---------------------\n\n.. function:: void obs_property_frame_rate_clear(obs_property_t *p)\n\n---------------------\n\n.. function:: void obs_property_frame_rate_options_clear(obs_property_t *p)\n\n---------------------\n\n.. function:: void obs_property_frame_rate_fps_ranges_clear(obs_property_t *p)\n\n---------------------\n\n.. function:: void obs_property_frame_rate_option_insert(obs_property_t *p, size_t idx, const char *name, const char *description)\n\n---------------------\n\n.. function:: void obs_property_frame_rate_fps_range_insert(obs_property_t *p, size_t idx, struct media_frames_per_second min, struct media_frames_per_second max)\n\n---------------------\n\n.. function:: void obs_property_button_set_type(obs_property_t *p, enum obs_button_type type)\n\n   Sets the type of the button. The button's ``callback`` will be called regardless of type.\n\n   :param   type: Can be one of the following values:\n\n                  - **OBS_BUTTON_DEFAULT** - Standard button\n                  - **OBS_BUTTON_URL** - Button that opens a URL\n   :return:       The property\n\n---------------------\n\n.. function:: void obs_property_button_set_url(obs_property_t *p, char *url)\n\n   :param p:   The property\n   :param url: The URL to be opened if the button is of type ``OBS_BUTTON_URL``\n"
  },
  {
    "path": "docs/sphinx/reference-scenes.rst",
    "content": "Scene API Reference (obs_scene_t)\n=================================\n\nA scene is a source which contains and renders other sources using\nspecific transforms and/or filtering\n\n.. type:: obs_scene_t\n\n   A reference-counted scene object.\n\n.. type:: obs_sceneitem_t\n\n   A reference-counted scene item object.\n\n.. code:: cpp\n\n   #include <obs.h>\n\n\nScene Item Transform Structure (obs_transform_info)\n---------------------------------------------------\n\n.. struct:: obs_transform_info\n\n   Scene item transform structure.\n\n.. member:: struct vec2          obs_transform_info.pos\n\n   Position.\n\n.. member:: float                obs_transform_info.rot\n\n   Rotation (degrees).\n\n.. member:: struct vec2          obs_transform_info.scale\n\n   Scale.\n\n.. member:: uint32_t             obs_transform_info.alignment\n\n   The alignment of the scene item relative to its position.\n\n   Can be 0 or a bitwise OR combination of one of the following values:\n\n   - **OBS_ALIGN_CENTER**\n   - **OBS_ALIGN_LEFT**\n   - **OBS_ALIGN_RIGHT**\n   - **OBS_ALIGN_TOP**\n   - **OBS_ALIGN_BOTTOM**\n\n.. member:: enum obs_bounds_type obs_transform_info.bounds_type\n\n   Can be one of the following values:\n\n   - **OBS_BOUNDS_NONE**            - No bounding box\n   - **OBS_BOUNDS_STRETCH**         - Stretch to the bounding box without preserving aspect ratio\n   - **OBS_BOUNDS_SCALE_INNER**     - Scales with aspect ratio to inner bounding box rectangle\n   - **OBS_BOUNDS_SCALE_OUTER**     - Scales with aspect ratio to outer bounding box rectangle\n   - **OBS_BOUNDS_SCALE_TO_WIDTH**  - Scales with aspect ratio to the bounding box width\n   - **OBS_BOUNDS_SCALE_TO_HEIGHT** - Scales with aspect ratio to the bounding box height\n   - **OBS_BOUNDS_MAX_ONLY**        - Scales with aspect ratio, but only to the size of the source maximum\n\n.. member:: uint32_t             obs_transform_info.bounds_alignment\n\n   The alignment of the source within the bounding box.\n\n   Can be 0 or a bitwise OR combination of one of the following values:\n\n   - **OBS_ALIGN_CENTER**\n   - **OBS_ALIGN_LEFT**\n   - **OBS_ALIGN_RIGHT**\n   - **OBS_ALIGN_TOP**\n   - **OBS_ALIGN_BOTTOM**\n\n.. member:: struct vec2          obs_transform_info.bounds\n\n   The bounding box (if a bounding box is enabled).\n\n\nScene Item Crop Structure (obs_sceneitem_crop)\n----------------------------------------------\n\n.. struct:: obs_sceneitem_crop\n\n   Scene item crop structure.\n\n.. member:: int obs_sceneitem_crop.left\n\n   Left crop value.\n\n.. member:: int obs_sceneitem_crop.top\n\n   Top crop value.\n\n.. member:: int obs_sceneitem_crop.right\n\n   Right crop value.\n\n.. member:: int obs_sceneitem_crop.bottom\n\n   Bottom crop value.\n\n\nScene Item Order Info Structure (\\*obs_sceneitem_order_info)\n------------------------------------------------------------\n\n.. struct:: obs_sceneitem_order_info\n\n   Scene item order info structure.\n\n.. member:: obs_sceneitem_t *obs_sceneitem_order_info.group\n\n   Specifies the group this scene item belongs to, or *NULL* if none.\n\n.. member:: obs_sceneitem_t *obs_sceneitem_order_info.item\n\n   Specifies the scene item.\n\n\n\n.. _scene_signal_reference:\n\nScene Signals\n-------------\n\n**item_add** (ptr scene, ptr item)\n\n   Called when a scene item has been added to the scene.\n\n**item_remove** (ptr scene, ptr item)\n\n   Called when a scene item has been removed from the scene.\n\n**reorder** (ptr scene)\n\n   Called when scene items have been reordered in the scene.\n\n**refresh** (ptr scene)\n\n   Called when the entire scene item list needs to be refreshed.\n   Usually this is only used when groups have changed.\n\n**item_visible** (ptr scene, ptr item, bool visible)\n\n   Called when a scene item's visibility state changes.\n\n**item_locked** (ptr scene, ptr item, bool locked)\n\n   Called when a scene item has been locked or unlocked.\n\n**item_select** (ptr scene, ptr item)\n\n**item_deselect** (ptr scene, ptr item)\n\n   Called when a scene item has been selected/deselected.\n\n   (Author's note: These should be replaced)\n\n**item_transform** (ptr scene, ptr item)\n\n   Called when a scene item's transform has changed.\n\n\nGeneral Scene Functions\n-----------------------\n\n.. function:: obs_scene_t *obs_scene_create(const char *name)\n\n   :param name: Name of the scene source.  If it's not unique, it will\n                be made unique\n   :return:     A reference to a scene\n\n---------------------\n\n.. function:: obs_scene_t *obs_scene_create_private(const char *name)\n\n   :param name: Name of the scene source.  Does not have to be unique,\n                or can be *NULL*\n   :return:     A reference to a private scene\n\n---------------------\n\n.. function:: obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name, enum obs_scene_duplicate_type type)\n\n   Duplicates a scene.  When a scene is duplicated, its sources can be\n   just referenced, or fully duplicated.\n\n   :param name: Name of the new scene source\n\n   :param type:  | Type of duplication:\n                 | OBS_SCENE_DUP_REFS         - Duplicates the scene, but scene items are only duplicated with references\n                 | OBS_SCENE_DUP_COPY         - Duplicates the scene, and scene items are also fully duplicated when possible\n                 | OBS_SCENE_DUP_PRIVATE_REFS - Duplicates with references, but the scene is a private source\n                 | OBS_SCENE_DUP_PRIVATE_COPY - Fully duplicates scene items when possible, but the scene and duplicates sources are private sources\n\n   :return:     A reference to a new scene\n\n---------------------\n\n.. function:: obs_scene_t *obs_scene_get_ref(obs_scene_t *scene)\n\n   Returns an incremented reference if still valid, otherwise returns\n   *NULL*. Release with :c:func:`obs_scene_release()`.\n\n---------------------\n\n.. function:: void obs_scene_release(obs_scene_t *scene)\n\n   Releases a reference to a scene.\n\n---------------------\n\n.. function:: obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)\n\n   :return: A new scene item for a source within a scene.  Does not\n            increment the reference\n\n---------------------\n\n.. function:: obs_source_t *obs_scene_get_source(const obs_scene_t *scene)\n\n   :return: The scene's source.  Does not increment the reference\n\n---------------------\n\n.. function:: obs_scene_t *obs_scene_from_source(const obs_source_t *source)\n\n   :return: The scene context, or *NULL* if not a scene.  Does not\n            increase the reference\n\n---------------------\n\n.. function:: obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name)\n\n   :param name: The name of the source to find\n   :return:     The scene item if found, otherwise *NULL* if not found\n\n---------------------\n\n.. function:: obs_sceneitem_t *obs_scene_find_source_recursive(obs_scene_t *scene, const char *name)\n\n   Same as obs_scene_find_source, but also searches groups within the\n   scene.\n\n   :param name: The name of the source to find\n   :return:     The scene item if found, otherwise *NULL* if not found\n\n---------------------\n\n.. function:: obs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene, int64_t id)\n\n   :param id: The unique numeric identifier of the scene item\n   :return:   The scene item if found, otherwise *NULL* if not found\n\n---------------------\n\n.. function:: void obs_scene_enum_items(obs_scene_t *scene, bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*), void *param)\n\n   Enumerates scene items within a scene in order of the bottommost scene item\n   to the topmost scene item.\n\n   Callback function returns true to continue enumeration, or false to end\n   enumeration.\n\n   Use :c:func:`obs_sceneitem_addref()` if you want to retain a\n   reference after obs_scene_enum_items finishes.\n\n   For scripting, use :py:func:`obs_scene_enum_items`.\n\n---------------------\n\n.. function:: bool obs_scene_reorder_items(obs_scene_t *scene, obs_sceneitem_t * const *item_order, size_t item_order_size)\n\n   Reorders items within a scene.\n\n---------------------\n\n.. function:: bool obs_scene_reorder_items2(obs_scene_t *scene, struct obs_sceneitem_order_info *item_order, size_t item_order_size)\n\n   Reorders items within a scene with groups and group sub-items.\n\n---------------------\n\n.. function:: void obs_scene_prune_sources(obs_scene_t *scene)\n\n   Releases all sources from a scene that have been marked as removed by obs_source_remove.\n\n---------------------\n\n\n.. _scene_item_reference:\n\nScene Item Functions\n--------------------\n\n.. function:: void obs_sceneitem_addref(obs_sceneitem_t *item)\n              void obs_sceneitem_release(obs_sceneitem_t *item)\n\n   Adds/releases a reference to a scene item.\n\n---------------------\n\n.. function:: void obs_sceneitem_remove(obs_sceneitem_t *item)\n\n   Removes the scene item from the scene.\n\n---------------------\n\n.. function:: obs_scene_t *obs_sceneitem_get_scene(const obs_sceneitem_t *item)\n\n   :return: The scene associated with the scene item.  Does not\n            increment the reference\n\n---------------------\n\n.. function:: obs_source_t *obs_sceneitem_get_source(const obs_sceneitem_t *item)\n\n   :return: The source associated with the scene item.  Does not\n            increment the reference\n\n---------------------\n\n.. function:: void obs_sceneitem_set_id(obs_sceneitem_t *item);\n\n   Sets the unique numeric identifier of the sceneitem. This is dangerous function and should not\n   normally be used. It can cause errors within obs.\n\n---------------------\n\n.. function:: int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item)\n\n   Gets the numeric identifier of the sceneitem.\n\n   :return: Gets the unique numeric identifier of the scene item.\n\n---------------------\n\n.. function:: obs_data_t *obs_scene_save_transform_states(obs_scene_t *scene, bool all_items)\n.. function:: void obs_scene_load_transform_states(const char *states)\n\n   Saves all the transformation states for the sceneitems in scene. When all_items is false, it\n   will only save selected items\n\n   :return: Data containing transformation states for all* sceneitems in scene\n\n---------------------\n\n.. function:: void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos)\n              void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos)\n\n   Sets/gets the position of a scene item.\n\n---------------------\n\n.. function:: void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot_deg)\n              float obs_sceneitem_get_rot(const obs_sceneitem_t *item)\n\n   Sets/gets the rotation of a scene item.\n\n---------------------\n\n.. function:: void obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale)\n              void obs_sceneitem_get_scale(const obs_sceneitem_t *item, struct vec2 *scale)\n\n   Sets/gets the scaling of the scene item.\n\n---------------------\n\n.. function:: void obs_sceneitem_set_alignment(obs_sceneitem_t *item, uint32_t alignment)\n              uint32_t obs_sceneitem_get_alignment(const obs_sceneitem_t *item)\n\n   Sets/gets the alignment of the scene item relative to its position.\n\n   :param alignment: | Can be any bitwise OR combination of:\n                     | OBS_ALIGN_CENTER\n                     | OBS_ALIGN_LEFT\n                     | OBS_ALIGN_RIGHT\n                     | OBS_ALIGN_TOP\n                     | OBS_ALIGN_BOTTOM\n\n---------------------\n\n.. function:: void obs_sceneitem_set_order(obs_sceneitem_t *item, enum obs_order_movement movement)\n\n   Changes the scene item's order relative to the other scene items\n   within the scene.\n\n   :param movement: | Can be one of the following:\n                    | OBS_ORDER_MOVE_UP\n                    | OBS_ORDER_MOVE_DOWN\n                    | OBS_ORDER_MOVE_TOP\n                    | OBS_ORDER_MOVE_BOTTOM\n\n---------------------\n\n.. function:: void obs_sceneitem_set_order_position(obs_sceneitem_t *item, int position)\n\n   Changes the sceneitem's order index.\n\n---------------------\n\n.. function:: int obs_sceneitem_get_order_position(obs_sceneitem_t *item)\n\n   :return: Gets position of sceneitem in its scene.\n\n---------------------\n\n.. function:: void obs_sceneitem_set_bounds_type(obs_sceneitem_t *item, enum obs_bounds_type type)\n              enum obs_bounds_type obs_sceneitem_get_bounds_type(const obs_sceneitem_t *item)\n\n   Sets/gets the bounding box type of a scene item.  Bounding boxes are\n   used to stretch/position the source relative to a specific bounding\n   box of a specific size.\n\n   :param type: | Can be one of the following values:\n                | OBS_BOUNDS_NONE            - No bounding box\n                | OBS_BOUNDS_STRETCH         - Stretch to the bounding box without preserving aspect ratio\n                | OBS_BOUNDS_SCALE_INNER     - Scales with aspect ratio to inner bounding box rectangle\n                | OBS_BOUNDS_SCALE_OUTER     - Scales with aspect ratio to outer bounding box rectangle\n                | OBS_BOUNDS_SCALE_TO_WIDTH  - Scales with aspect ratio to the bounding box width\n                | OBS_BOUNDS_SCALE_TO_HEIGHT - Scales with aspect ratio to the bounding box height\n                | OBS_BOUNDS_MAX_ONLY        - Scales with aspect ratio, but only to the size of the source maximum\n\n---------------------\n\n.. function:: void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item, uint32_t alignment)\n              uint32_t obs_sceneitem_get_bounds_alignment(const obs_sceneitem_t *item)\n\n   Sets/gets the alignment of the source within the bounding box.\n\n   :param alignment: | Can be any bitwise OR combination of:\n                     | OBS_ALIGN_CENTER\n                     | OBS_ALIGN_LEFT\n                     | OBS_ALIGN_RIGHT\n                     | OBS_ALIGN_TOP\n                     | OBS_ALIGN_BOTTOM\n\n---------------------\n\n.. function:: void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds)\n              void obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds)\n\n   Sets/gets the bounding box width/height of the scene item.\n\n---------------------\n\n.. function:: void obs_sceneitem_set_info2(obs_sceneitem_t *item, const struct obs_transform_info *info)\n              void obs_sceneitem_get_info2(const obs_sceneitem_t *item, struct obs_transform_info *info)\n\n   Sets/gets the transform information of the scene item.\n   This version of the function also sets the `crop_to_bounds` member of `obs_transform_info`.\n\n   .. versionadded:: 30.1\n\n---------------------\n\n.. function:: void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform)\n\n   Gets the transform matrix of the scene item used for drawing the\n   source.\n\n---------------------\n\n.. function:: void obs_sceneitem_get_box_transform(const obs_sceneitem_t *item, struct matrix4 *transform)\n\n   Gets the transform matrix of the scene item used for the bounding box\n   or edges of the scene item.\n\n---------------------\n\n.. function:: void obs_sceneitem_select(obs_sceneitem_t *item, bool select)\n              bool obs_sceneitem_selected(const obs_sceneitem_t *item)\n\n   Sets/gets the selection state of the scene item. Note that toggling\n   the selected state will not affect the selected state of other scene items,\n   as multiple scene items can be selected.\n\n---------------------\n\n.. function:: bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible)\n              bool obs_sceneitem_visible(const obs_sceneitem_t *item)\n\n   Sets/gets the visibility state of the scene item.\n\n---------------------\n\n.. function:: bool obs_sceneitem_set_locked(obs_sceneitem_t *item, bool locked)\n              bool obs_sceneitem_locked(const obs_sceneitem_t *item)\n\n   Sets/gets the locked/unlocked state of the scene item.\n\n---------------------\n\n.. function:: void obs_sceneitem_set_crop(obs_sceneitem_t *item, const struct obs_sceneitem_crop *crop)\n              void obs_sceneitem_get_crop(const obs_sceneitem_t *item, struct obs_sceneitem_crop *crop)\n\n   Sets/gets the cropping of the scene item.\n\n---------------------\n\n.. function:: void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item, enum obs_scale_type filter)\n              enum obs_scale_type obs_sceneitem_get_scale_filter( obs_sceneitem_t *item)\n\n   Sets/gets the scale filter used for the scene item.\n\n   :param filter: | Can be one of the following values:\n                  | OBS_SCALE_DISABLE\n                  | OBS_SCALE_POINT\n                  | OBS_SCALE_BICUBIC\n                  | OBS_SCALE_BILINEAR\n                  | OBS_SCALE_LANCZOS\n\n---------------------\n\n.. function:: void obs_sceneitem_set_blending_method(obs_sceneitem_t *item, enum obs_blending_method method)\n              enum obs_blending_method obs_sceneitem_get_blending_method(obs_sceneitem_t *item)\n\n   Sets/gets the blending method used for the scene item.\n\n   :param method: | Can be one of the following values:\n                  | OBS_BLEND_METHOD_DEFAULT\n                  | OBS_BLEND_METHOD_SRGB_OFF\n\n---------------------\n\n.. function:: void obs_sceneitem_set_blending_mode(obs_sceneitem_t *item, enum obs_blending_type type)\n              enum obs_blending_type obs_sceneitem_get_blending_mode(obs_sceneitem_t *item)\n\n   Sets/gets the blending mode used for the scene item.\n\n   :param type: | Can be one of the following values:\n                | OBS_BLEND_NORMAL\n                | OBS_BLEND_ADDITIVE\n                | OBS_BLEND_SUBTRACT\n                | OBS_BLEND_SCREEN\n                | OBS_BLEND_MULTIPLY\n                | OBS_BLEND_LIGHTEN\n                | OBS_BLEND_DARKEN\n\n---------------------\n\n.. function:: void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item)\n              void obs_sceneitem_defer_update_end(obs_sceneitem_t *item)\n\n   Allows the ability to call any one of the transform functions without\n   updating the internal matrices until obs_sceneitem_defer_update_end\n   has been called.\n\n---------------------\n\n.. function:: obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item)\n\n   :return: An incremented reference to the private settings of the\n            scene item.  Allows the front-end to set custom information\n            which is saved with the scene item. Release with\n            :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: void obs_sceneitem_set_transition(obs_sceneitem_t *item, bool show, obs_source_t *transition)\n\n   Sets a transition for showing or hiding a scene item.\n\n   :param item:       The target scene item\n   :param show:       If *true*, this will set the show transition.\n                      If *false*, this will set the hide transition.\n   :param transition: The transition to set. Pass *NULL* to remove the transition.\n\n---------------------\n\n.. function:: obs_source_t *obs_sceneitem_get_transition(obs_sceneitem_t *item, bool show)\n\n   :param item: The target scene item\n   :param show: If *true*, this will return the show transition.\n                If *false*, this will return the hide transition.\n   :return:     The transition for showing or hiding a scene item. *NULL* if no transition is set.\n\n---------------------\n\n---------------------\n\n.. function:: void obs_sceneitem_set_transition_duration(obs_sceneitem_t *item, bool show, uint32_t duration_ms)\n\n   Sets the transition duration for showing or hiding a scene item.\n\n   :param item:        The target scene item\n   :param show:        If *true*, this will set the duration of the show transition.\n                       If *false*, this will set the duration of the hide transition.\n   :param duration_ms: The transition duration in milliseconds\n\n---------------------\n\n.. function:: uint32_t obs_sceneitem_get_transition_duration(obs_sceneitem_t *item, bool show)\n\n   Gets the transition duration for showing or hiding a scene item.\n\n   :param item: The target scene item\n   :param show: If *true*, this will return the duration of the show transition.\n                If *false*, this will return the duration of the hide transition.\n   :return:     The transition duration in milliseconds\n\n---------------------\n\n.. function:: void obs_sceneitem_do_transition(obs_sceneitem_t *item, bool visible)\n\n   Start the transition for showing or hiding a scene item.\n\n---------------------\n\n\n.. _scene_item_group_reference:\n\nScene Item Group Functions\n--------------------------\n\n.. function:: obs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene, const char *name)\n\n   Adds a group with the specified name.  Does not signal the scene with\n   the *refresh* signal.\n\n   :param scene: Scene to add the group to\n   :param name:  Name of the group\n   :return:      The new group's scene item\n\n---------------------\n\n.. function:: obs_sceneitem_t *obs_scene_add_group2(obs_scene_t *scene, const char *name, bool signal)\n\n   Adds a group with the specified name.\n\n   :param scene:  Scene to add the group to\n   :param name:   Name of the group\n   :param signal: If *true*, signals the scene with the *refresh*\n                  signal\n   :return:       The new group's scene item\n\n---------------------\n\n.. function:: obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, const char *name, obs_sceneitem_t **items, size_t count)\n\n   Creates a group out of the specified scene items.  The group will be\n   inserted at the top scene item.  Does not signal the scene with the\n   *refresh* signal.\n\n   :param scene: Scene to add the group to\n   :param name:  Name of the group\n   :param items: Array of scene items to put in a group\n   :param count: Number of scene items in the array\n   :return:      The new group's scene item\n\n---------------------\n\n.. function:: obs_sceneitem_t *obs_scene_insert_group2(obs_scene_t *scene, const char *name, obs_sceneitem_t **items, size_t count, bool signal)\n\n   Creates a group out of the specified scene items.  The group will be\n   inserted at the top scene item.  Does not signal a refresh.\n\n   :param scene: Scene to add the group to\n   :param name:  Name of the group\n   :param items: Array of scene items to put in a group\n   :param count: Number of scene items in the array\n   :param signal: If *true*, signals the scene with the *refresh*\n                  signal\n   :return:      The new group's scene item\n\n---------------------\n\n.. function:: obs_sceneitem_t *obs_scene_get_group(obs_scene_t *scene, const char *name)\n\n   Finds a group within a scene by its name.\n\n   :param scene: Scene to find the group within\n   :param name:  The name of the group to find\n   :return:      The group scene item, or *NULL* if not found\n\n---------------------\n\n.. function:: obs_scene_t *obs_group_from_source(const obs_source_t *source)\n\n   :return: The group context, or *NULL* if not a group.  Does not\n            increase the reference\n\n---------------------\n\n.. function:: obs_scene_t *obs_group_or_scene_from_source(const obs_source_t *source)\n\n   :return: The context for the source, regardless of if it is a\n            group or a scene.  *NULL* if neither.  Does not increase\n            the reference\n\n---------------------\n\n.. function:: bool obs_sceneitem_is_group(obs_sceneitem_t *item)\n\n   :param item: Scene item\n   :return:     *true* if scene item is a group, *false* otherwise\n\n---------------------\n\n.. function:: obs_scene_t *obs_sceneitem_group_get_scene(const obs_sceneitem_t *group)\n\n   :param group: Group scene item\n   :return:      Scene of the group, or *NULL* if not a group\n\n---------------------\n\n.. function:: void obs_sceneitem_group_ungroup(obs_sceneitem_t *group)\n\n   Ungroups the specified group.  Scene items within the group will be\n   placed where the group was.  Does not signal the scene with the\n   *refresh* signal.\n\n---------------------\n\n.. function:: void obs_sceneitem_group_ungroup2(obs_sceneitem_t *group, bool signal)\n\n   Ungroups the specified group.  Scene items within the group will be\n   placed where the group was.\n\n   :param group: Group scene item\n   :param signal: If *true*, signals the scene with the *refresh*\n                  signal\n\n---------------------\n\n.. function:: void obs_sceneitem_group_add_item(obs_sceneitem_t *group, obs_sceneitem_t *item)\n\n   Adds a scene item to a group.\n\n---------------------\n\n.. function:: void obs_sceneitem_group_remove_item(obs_sceneitem_t *item)\n\n   Removes a scene item from a group.  The item will be placed before\n   the group in the main scene.\n\n---------------------\n\n.. function:: obs_sceneitem_t *obs_sceneitem_get_group(obs_scene_t *scene, obs_sceneitem_t *item)\n\n   Returns the parent group of a scene item.\n\n   :param scene: Scene to find the group within\n   :param item: Scene item to get the group of\n   :return:     The parent group of the scene item, or *NULL* if not in\n                a group\n\n---------------------\n\n.. function:: void obs_sceneitem_group_enum_items(obs_sceneitem_t *group, bool (*callback)(obs_scene_t*, obs_sceneitem_t*, void*), void *param)\n\n   Enumerates scene items within a group.\n\n   Callback function returns true to continue enumeration, or false to end\n   enumeration.\n\n   Use :c:func:`obs_sceneitem_addref()` if you want to retain a\n   reference after obs_sceneitem_group_enum_items finishes.\n\n\n---------------------\n\n.. function:: void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item)\n.. function:: void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item)\n\n   Allows the ability to call any one of the transform functions on\n   scene items within a group without updating the internal matrices of\n   the group until obs_sceneitem_defer_group_resize_end has been called.\n\n   This is necessary if the user is resizing items while they are within\n   a group, as the group's transform will automatically update its\n   transform every frame otherwise.\n"
  },
  {
    "path": "docs/sphinx/reference-services.rst",
    "content": "Service API Reference (obs_service_t)\n=====================================\n\nServices are custom implementations of streaming services, which are\nused with outputs that stream.  For example, you could have a custom\nimplementation for streaming to Twitch, and another for YouTube to allow\nthe ability to log in and use their APIs to do things such as get the\nRTMP servers or control the channel.  The `libobs/obs-service.h`_ file\nis the dedicated header for implementing services.\n\n*(Author's note: the service API is incomplete as of this writing)*\n\n.. type:: obs_service_t\n\n   A reference-counted service object.\n\n.. type:: obs_weak_service_t\n\n   A weak reference to a service object.\n\n.. code:: cpp\n\n   #include <obs.h>\n\n\nService Definition Structure\n----------------------------\n\n.. struct:: obs_service_info\n\n   Service definition structure.\n\n.. member:: const char *obs_service_info.id\n\n   Unique string identifier for the service (required).\n\n.. member:: const char *(*obs_service_info.get_name)(void *type_data)\n\n   Get the translated name of the service type.\n\n   :param  type_data:  The type_data variable of this structure\n   :return:            The translated name of the service type\n\n.. member:: void *(*obs_service_info.create)(obs_data_t *settings, obs_service_t *service)\n\n   Creates the implementation data for the service.\n\n   :param  settings: Settings to initialize the service with\n   :param  service:   Source that this data is associated with\n   :return:          The implementation data associated with this service\n\n.. member:: void (*obs_service_info.destroy)(void *data)\n\n   Destroys the implementation data for the service.\n\n.. member:: void (*obs_service_info.get_defaults)(obs_data_t *settings)\n            void (*obs_service_info.get_defaults2)(void *type_data, obs_data_t *settings)\n\n   Sets the default settings for this service.\n\n   :param  settings:  Default settings.  Call obs_data_set_default*\n                      functions on this object to set default setting\n                      values\n\n.. member:: obs_properties_t *(*obs_service_info.get_properties)(void *data)\n            obs_properties_t *(*obs_service_info.get_properties2)(void *data, void *type_data)\n\n   Gets the property information of this service.\n\n   (Optional)\n\n   :return: The properties of the service\n\n.. member:: void (*obs_service_info.update)(void *data, obs_data_t *settings)\n\n   Updates the settings for this service.\n\n   (Optional)\n\n   :param settings: New settings for this service\n\n.. member:: bool (*obs_service_info.initialize)(void *data, obs_output_t *output)\n\n   Called when getting ready to start up an output, before the encoders\n   and output are initialized.\n\n   (Optional)\n\n   :param  output: Output context to use this service with\n   :return:        *true* to allow the output to start up,\n                   *false* to prevent output from starting up\n\n.. member:: const char *(*obs_service_info.get_url)(void *data)\n\n   :return: The stream URL\n\n.. member:: const char *(*obs_service_info.get_key)(void *data)\n\n   :return: The stream key\n\n.. member:: const char *(*obs_service_info.get_username)(void *data)\n\n   (Optional)\n\n   :return: The username\n\n.. member:: const char *(*obs_service_info.get_password)(void *data)\n\n   (Optional)\n\n   :return: The password\n\n.. member:: void (*obs_service_info.apply_encoder_settings)(void *data, obs_data_t *video_encoder_settings, obs_data_t *audio_encoder_settings)\n\n   This function is called to apply custom encoder settings specific to\n   this service.  For example, if a service requires a specific keyframe\n   interval, or has a bitrate limit, the settings for the video and\n   audio encoders can be optionally modified if the front-end optionally\n   calls :c:func:`obs_service_apply_encoder_settings()`.\n\n   (Optional)\n\n   :param video_encoder_settings: The audio encoder settings to change\n   :param audio_encoder_settings: The video encoder settings to change\n\n.. member:: void *obs_service_info.type_data\n            void (*obs_service_info.free_type_data)(void *type_data)\n\n   Private data associated with this entry.  Note that this is not the\n   same as the implementation data; this is used to differentiate\n   between two different types if the same callbacks are used for more\n   than one different type.\n\n   (Optional)\n\n.. member:: const char *(*obs_service_info.get_output_type)(void *data)\n\n   (Optional)\n\n   :return: The output type that should be preferred with this service\n\n.. member:: const char **(*get_supported_video_codecs)(void *data)\n\n   (Optional)\n\n   :return: A string pointer array of the supported video codecs, should\n            be stored by the plugin so the caller does not need to free\n            the data manually (typically best to use strlist_split to\n            generate this)\n\n.. member:: const char **(*get_supported_audio_codecs)(void *data)\n\n   (Optional)\n\n   :return: A string pointer array of the supported audio codecs, should\n            be stored by the plugin so the caller does not need to free\n            the data manually (typically best to use strlist_split to\n            generate this)\n\n   .. versionadded:: 29.1\n\n.. member:: const char *(*obs_service_info.get_protocol)(void *data)\n\n   :return: The protocol used by the service\n\n   .. versionadded:: 29.1\n\n.. member:: const char *(*obs_service_info.get_connect_info)(void *data, uint32_t type)\n\n   Output a service connection info related to a given type value:\n\n   - **OBS_SERVICE_CONNECT_INFO_SERVER_URL** - Server URL (0)\n\n   - **OBS_SERVICE_CONNECT_INFO_STREAM_ID** - Stream ID (2)\n\n   - **OBS_SERVICE_CONNECT_INFO_STREAM_KEY** - Stream key (alias of **OBS_SERVICE_CONNECT_INFO_STREAM_ID**)\n\n   - **OBS_SERVICE_CONNECT_INFO_USERNAME** - Username (4)\n\n   - **OBS_SERVICE_CONNECT_INFO_PASSWORD** - Password (6)\n\n   - **OBS_SERVICE_CONNECT_INFO_ENCRYPT_PASSPHRASE** - Encryption passphrase (8)\n\n   - Odd values as types are reserved for third-party protocols\n\n   Depending on the protocol, the service will have to provide information\n   to the output to be able to connect.\n\n   Irrelevant or unused types can return `NULL`.\n\n   .. versionadded:: 29.1\n\n.. member:: bool (*obs_service_info.can_try_to_connect)(void *data)\n\n   (Optional)\n\n   :return: If the service has all the needed connection info to be\n            able to connect.\n\n   NOTE: If not set, :c:func:`obs_service_can_try_to_connect()`\n   returns *true* by default.\n\n   .. versionadded:: 29.1\n\nGeneral Service Functions\n-------------------------\n\n.. function:: void obs_register_service(struct obs_service_info *info)\n\n   Registers a service type.  Typically used in\n   :c:func:`obs_module_load()` or in the program's initialization phase.\n\n---------------------\n\n.. function:: const char *obs_service_get_display_name(const char *id)\n\n   Calls the :c:member:`obs_service_info.get_name` callback to get the\n   translated display name of a service type.\n\n   :param    id:            The service type string identifier\n   :return:                 The translated display name of a service type\n\n---------------------\n\n.. function:: obs_service_t *obs_service_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data)\n\n   Creates a service with the specified settings.\n\n   The \"service\" context is used for encoding video/audio data.  Use\n   obs_service_release to release it.\n\n   :param   id:             The service type string identifier\n   :param   name:           The desired name of the service.  If this is\n                            not unique, it will be made to be unique\n   :param   settings:       The settings for the service, or *NULL* if\n                            none\n   :param   hotkey_data:    Saved hotkey data for the service, or *NULL*\n                            if none\n   :return:                 A reference to the newly created service, or\n                            *NULL* if failed\n\n---------------------\n\n.. function:: obs_service_t *obs_service_get_ref(obs_service_t *service)\n\n   Returns an incremented reference if still valid, otherwise returns\n   *NULL*. Release with :c:func:`obs_service_release()`.\n\n---------------------\n\n.. function:: void obs_service_release(obs_service_t *service)\n\n   Releases a reference to a service.  When the last reference is\n   released, the service is destroyed.\n\n---------------------\n\n.. function:: obs_weak_service_t *obs_service_get_weak_service(obs_service_t *service)\n              obs_service_t *obs_weak_service_get_service(obs_weak_service_t *weak)\n\n   These functions are used to get a weak reference from a strong service\n   reference, or a strong service reference from a weak reference.  If\n   the service is destroyed, *obs_weak_service_get_service* will return\n   *NULL*.\n\n---------------------\n\n.. function:: void obs_weak_service_addref(obs_weak_service_t *weak)\n              void obs_weak_service_release(obs_weak_service_t *weak)\n\n   Adds/releases a weak reference to a service.\n\n---------------------\n\n.. function:: const char *obs_service_get_name(const obs_service_t *service)\n\n   :return: The name of the service\n\n---------------------\n\n.. function:: obs_data_t *obs_service_defaults(const char *id)\n\n   :return: An incremented reference to the service's default settings.\n            Release with :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: obs_properties_t *obs_service_properties(const obs_service_t *service)\n              obs_properties_t *obs_get_service_properties(const char *id)\n\n   Use these functions to get the properties of a service or service\n   type.  Properties are optionally used (if desired) to automatically\n   generate user interface widgets to allow users to update settings.\n\n   :return: The properties list for a specific existing service.  Free\n            with :c:func:`obs_properties_destroy()`\n\n---------------------\n\n.. function:: obs_data_t *obs_service_get_settings(const obs_service_t *service)\n\n   :return: An incremented reference to the service's settings. Release with\n            :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: void obs_service_update(obs_service_t *service, obs_data_t *settings)\n\n   Updates the settings for this service context.\n\n---------------------\n\n.. function:: void obs_service_apply_encoder_settings(obs_service_t *service, obs_data_t *video_encoder_settings, obs_data_t *audio_encoder_settings)\n\n   Applies service-specific video encoder settings.\n\n   :param  video_encoder_settings: Video encoder settings.  Can be *NULL*\n   :param  audio_encoder_settings: Audio encoder settings.  Can be *NULL*\n\n---------------------\n\n.. function:: const char **obs_service_get_supported_video_codecs(const obs_service_t *service)\n\n   :return: An array of string pointers containing the supported video\n            codecs for the service, terminated with a *NULL* pointer.\n            Does not need to be freed\n\n---------------------\n\n.. function:: const char **obs_service_get_supported_audio_codecs(const obs_service_t *service)\n\n   :return: An array of string pointers containing the supported audio\n            codecs for the service, terminated with a *NULL* pointer.\n            Does not need to be freed\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: const char *obs_service_get_protocol(const obs_service_t *service)\n\n   :return: Protocol currently used for this service\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: const char *obs_service_get_preferred_output_type(const obs_service_t *service)\n\n   :return: The output type that should be preferred with this service\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: const char *obs_service_get_connect_info(const obs_service_t *service, uint32_t type)\n\n   :param type: Check :c:member:`obs_service_info.get_connect_info` for\n                type values.\n   :return: Connection info related to the type value.\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: bool obs_service_can_try_to_connect(const obs_service_t *service)\n\n   :return: If the service has all the needed connection info to be\n            able to connect. Returns `true` if\n            :c:member:`obs_service_info.can_try_to_connect` is not set.\n\n   .. versionadded:: 29.1\n\n.. ---------------------------------------------------------------------------\n\n.. _libobs/obs-service.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-service.h\n"
  },
  {
    "path": "docs/sphinx/reference-settings.rst",
    "content": "Data Settings API Reference (obs_data_t)\n========================================\n\nData settings objects are reference-counted objects that store values in\na string-table or array.  They're similar to Json objects, but\nadditionally allow additional functionality such as default or\nauto-selection values.  Data is saved/loaded to/from Json text and Json\ntext files.\n\n.. type:: obs_data_t\n\n   A reference-counted data object.\n\n.. type:: obs_data_array_t\n\n   A reference-counted data array object.\n\n.. code:: cpp\n\n   #include <obs.h>\n\n\nGeneral Functions\n-----------------\n\n.. function:: obs_data_t *obs_data_create()\n\n   :return: A new reference to a data object. Release with\n            :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: obs_data_t *obs_data_create_from_json(const char *json_string)\n\n   Creates a data object from a Json string.\n\n   :param json_string: Json string\n   :return:            A new reference to a data object. Release with\n                       :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: obs_data_t *obs_data_create_from_json_file(const char *json_file)\n\n   Creates a data object from a Json file.\n\n   :param json_file: Json file path\n   :return:          A new reference to a data object. Release with\n                     :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: obs_data_t *obs_data_create_from_json_file_safe(const char *json_file, const char *backup_ext)\n\n   Creates a data object from a Json file, with a backup file in case\n   the original is corrupted or fails to load.\n\n   :param json_file:  Json file path\n   :param backup_ext: Backup file extension\n   :return:           A new reference to a data object. Release with\n                       :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: void obs_data_addref(obs_data_t *data)\n              void obs_data_release(obs_data_t *data)\n\n   Adds/releases a reference to a data object.\n\n---------------------\n\n.. function:: const char *obs_data_get_json(obs_data_t *data)\n\n   Generates a new json string. The string allocation is stored within\n   the data object itself, and does not need to be manually freed.\n\n   :return: Json string for this object\n\n---------------------\n\n.. function:: const char *obs_data_get_json_with_defaults(obs_data_t *data)\n\n   Same as :c:func:`obs_data_get_json()` but default values are also serialized.\n\n   :return: Json string for this object\n\n---------------------\n\n.. function:: const char *obs_data_get_json_pretty(obs_data_t *data)\n\n   Same as :c:func:`obs_data_get_json()` but the JSON data is pretty-printed.\n\n   :return: Json string for this object\n\n---------------------\n\n.. function:: const char *obs_data_get_json_pretty_with_defaults(obs_data_t *data)\n\n   Same as :c:func:`obs_data_get_json_pretty()` but default values are also serialized.\n\n   :return: Json string for this object\n\n---------------------\n\n.. function:: const char *obs_data_get_last_json(obs_data_t *data)\n\n   Returns the last json string generated for this data object. Does not\n   generate a new string. Use :c:func:`obs_data_get_json()` to generate\n   a json string first.\n\n   :return: Json string for this object\n\n---------------------\n\n.. function:: bool obs_data_save_json(obs_data_t *data, const char *file)\n\n   Saves the data to a file as Json text.\n\n   :param file: The file to save to\n   :return:     *true* if successful, *false* otherwise\n\n---------------------\n\n.. function:: bool obs_data_save_json_safe(obs_data_t *data, const char *file, const char *temp_ext, const char *backup_ext)\n\n   Saves the data to a file as Json text, and if overwriting an old\n   file, backs up that old file to help prevent potential file\n   corruption.\n\n   :param file:       The file to save to\n   :param backup_ext: The backup extension to use for the overwritten\n                      file if it exists\n   :return:           *true* if successful, *false* otherwise\n\n---------------------\n\n.. function:: void obs_data_apply(obs_data_t *target, obs_data_t *apply_data)\n\n   Merges the data of *apply_data* in to *target*.\n\n---------------------\n\n.. function:: void obs_data_erase(obs_data_t *data, const char *name)\n\n   Erases the user data for item *name* within the data object.\n\n---------------------\n\n.. function:: void obs_data_clear(obs_data_t *data)\n\n   Clears all user data in the data object.\n\n---------------------\n\n\nSet Functions\n-------------\n\n.. function:: void obs_data_set_string(obs_data_t *data, const char *name, const char *val)\n\n---------------------\n\n.. function:: void obs_data_set_int(obs_data_t *data, const char *name, long long val)\n\n---------------------\n\n.. function:: void obs_data_set_double(obs_data_t *data, const char *name, double val)\n\n---------------------\n\n.. function:: void obs_data_set_bool(obs_data_t *data, const char *name, bool val)\n\n---------------------\n\n.. function:: void obs_data_set_obj(obs_data_t *data, const char *name, obs_data_t *obj)\n\n---------------------\n\n.. function:: void obs_data_set_array(obs_data_t *data, const char *name, obs_data_array_t *array)\n\n---------------------\n\n\n.. _obs_data_get_funcs:\n\nGet Functions\n-------------\n\n.. function:: const char *obs_data_get_string(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: long long obs_data_get_int(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: double obs_data_get_double(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: bool obs_data_get_bool(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: obs_data_t *obs_data_get_obj(obs_data_t *data, const char *name)\n\n   :return: An incremented reference to a data object. Release with\n            :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: obs_data_array_t *obs_data_get_array(obs_data_t *data, const char *name)\n\n   :return: An incremented reference to a data array object. Release\n            with :c:func:`obs_data_array_release()`.\n\n---------------------\n\n\n.. _obs_data_default_funcs:\n\nDefault Value Functions\n-----------------------\n\nDefault values are used to determine what value will be given if a value\nis not set.\n\n.. function:: obs_data_t *obs_data_get_defaults(obs_data_t *data);\n\n   :return: obs_data_t * with all default values (recursively for all objects as well).\n\n-----------------------\n\n.. function:: void obs_data_set_default_string(obs_data_t *data, const char *name, const char *val)\n              const char *obs_data_get_default_string(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: void obs_data_set_default_int(obs_data_t *data, const char *name, long long val)\n              long long obs_data_get_default_int(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: void obs_data_set_default_double(obs_data_t *data, const char *name, double val)\n              double obs_data_get_default_double(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: void obs_data_set_default_bool(obs_data_t *data, const char *name, bool val)\n              bool obs_data_get_default_bool(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: void obs_data_set_default_obj(obs_data_t *data, const char *name, obs_data_t *obj)\n              obs_data_t *obs_data_get_default_obj(obs_data_t *data, const char *name)\n\n   :return: An incremented reference to a data object. Release with\n            :c:func:`obs_data_release()`.\n\n----------------------\n\n.. function:: void obs_data_set_default_array(obs_data_t *data, const char *name, obs_data_array_t *arr)\n              obs_data_array_t *obs_data_get_default_array(obs_data_t *data, const char *name)\n\n\nAutoselect Functions\n--------------------\n\nAutoselect values are optionally used to determine what values should be\nused to ensure functionality if the currently set values are\ninappropriate or invalid.\n\n.. function:: void obs_data_set_autoselect_string(obs_data_t *data, const char *name, const char *val)\n              const char *obs_data_get_autoselect_string(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: void obs_data_set_autoselect_int(obs_data_t *data, const char *name, long long val)\n              long long obs_data_get_autoselect_int(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: void obs_data_set_autoselect_double(obs_data_t *data, const char *name, double val)\n              double obs_data_get_autoselect_double(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: void obs_data_set_autoselect_bool(obs_data_t *data, const char *name, bool val)\n              bool obs_data_get_autoselect_bool(obs_data_t *data, const char *name)\n\n---------------------\n\n.. function:: void obs_data_set_autoselect_obj(obs_data_t *data, const char *name, obs_data_t *obj)\n              obs_data_t *obs_data_get_autoselect_obj(obs_data_t *data, const char *name)\n\n   :return: An incremented reference to a data object. Release with\n            :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: void obs_data_set_autoselect_array(obs_data_t *data, const char *name, obs_data_array_t *arr)\n              obs_data_array_t *obs_data_get_autoselect_array(obs_data_t *data, const char *name)\n\n   :return: An incremented reference to a data array object. Release\n             with :c:func:`obs_data_array_release()`.\n\n   .. versionadded:: 30.1\n\n---------------------\n\n\nArray Functions\n---------------\n\n.. function:: obs_data_array_t *obs_data_array_create()\n\n   :return: A new reference to a data array object. Release\n            with :c:func:`obs_data_array_release()`.\n\n---------------------\n\n.. function:: void obs_data_array_addref(obs_data_array_t *array)\n\n---------------------\n\n.. function:: void obs_data_array_release(obs_data_array_t *array)\n\n---------------------\n\n.. function:: size_t obs_data_array_count(obs_data_array_t *array)\n\n---------------------\n\n.. function:: obs_data_t *obs_data_array_item(obs_data_array_t *array, size_t idx)\n\n   :return: An incremented reference to the data object associated with\n            this array entry. Release with :c:func:`obs_data_release()`.\n\n---------------------\n\n.. function:: size_t obs_data_array_push_back(obs_data_array_t *array, obs_data_t *obj)\n\n---------------------\n\n.. function:: void obs_data_array_insert(obs_data_array_t *array, size_t idx, obs_data_t *obj)\n\n---------------------\n\n.. function:: void obs_data_array_erase(obs_data_array_t *array, size_t idx)\n"
  },
  {
    "path": "docs/sphinx/reference-sources.rst",
    "content": "Source API Reference (obs_source_t)\n===================================\n\nSources are used to render video and/or audio on stream.  Things such as\ncapturing displays/games/audio, playing a video, showing an image, or\nplaying audio.  Sources can also be used to implement audio and video\nfilters as well as transitions.  The `libobs/obs-source.h`_ file is the\ndedicated header for implementing sources.\n\n.. type:: obs_source_t\n\n   A reference-counted video/audio input source.\n\n.. type:: obs_weak_source_t\n\n   A weak reference to an video/audio input source.\n\n.. code:: cpp\n\n   #include <obs.h>\n\n\nSource Definition Structure (obs_source_info)\n---------------------------------------------\n\n.. struct:: obs_source_info\n\n   Source definition structure.\n\n.. member:: const char *obs_source_info.id\n\n   Unique string identifier for the source (required).\n\n.. member:: uint32_t version\n\n   Source version (optional).\n\n   This is used when a source's implementation is significantly\n   modified and the previous version is deprecated, but is kept to\n   prevent old sources from breaking.\n\n.. member:: enum obs_source_type obs_source_info.type\n\n   Type of source.\n\n   - **OBS_SOURCE_TYPE_INPUT**      - Video/Audio Input\n   - **OBS_SOURCE_TYPE_FILTER**     - Filter\n   - **OBS_SOURCE_TYPE_TRANSITION** - Transition\n\n.. member:: uint32_t obs_source_info.output_flags\n\n   Source output capability flags (required).\n\n   (Author's note: This should be renamed to \"capability_flags\")\n\n   A bitwise OR combination of one or more of the following values:\n\n   - **OBS_SOURCE_VIDEO** - Source has video\n\n     Unless SOURCE_ASYNC_VIDEO is specified, the source must include the\n     :c:member:`obs_source_info.video_render` callback in the source\n     definition structure.\n\n   - **OBS_SOURCE_AUDIO** - Source has audio\n\n     Use the :c:func:`obs_source_output_audio()` function to pass raw\n     audio data, which will be automatically converted and uploaded.  If\n     used with OBS_SOURCE_ASYNC_VIDEO, audio will automatically be\n     synced up to the video output based upon their mutual timestamps.\n\n   - **OBS_SOURCE_ASYNC** - Video is asynchronous (use\n     OBS_SOURCE_ASYNC_VIDEO instead to automatically combine this flag\n     with the OBS_SOURCE_VIDEO flag).\n\n   - **OBS_SOURCE_ASYNC_VIDEO** - Source passes raw video data via RAM\n\n     Use the :c:func:`obs_source_output_video()` function to pass raw\n     video data, which will be automatically drawn at a timing relative\n     to the provided timestamp.\n\n     If audio is also present on the source, the audio will\n     automatically be synced to the video based upon their mutual\n     timestamps.\n\n   - **OBS_SOURCE_CUSTOM_DRAW** - Source uses custom graphics calls,\n     rather than just rendering a single texture.\n\n     This capability flag must be used if the source does not use\n     :c:func:`obs_source_draw()` to render a single texture.\n\n     This capability flag is an important hint to turn off a specific\n     optimization that allows the first effect filter in the filter\n     chain to render the source directly with that effect filter.  The\n     optimization does not work if there are custom graphics calls, and\n     the source must be rendered to a texture first before being sent to\n     the first filter in the filter chain.\n\n     (Author's note: Ironically, not many sources render with that\n     optimization.  I should have made it so that the optimization isn't\n     used by default, and a flag should have been used to turn on the\n     optimization -- not turn it off).\n\n   - **OBS_SOURCE_INTERACTION** - Source can be interacted with by the\n     user.\n\n     When this is used, the source will receive interaction events if\n     these callbacks are provided:\n     :c:member:`obs_source_info.mouse_click`,\n     :c:member:`obs_source_info.mouse_move`,\n     :c:member:`obs_source_info.mouse_wheel`,\n     :c:member:`obs_source_info.focus`, and\n     :c:member:`obs_source_info.key_click`.\n\n   - **OBS_SOURCE_COMPOSITE** - Source composites child sources\n\n     When used, specifies that the source composites one or more child\n     sources.  Scenes and transitions are examples of sources that\n     contain and render child sources.\n\n     Sources that render sub-sources must implement the audio_render\n     callback in order to perform custom audio mixing of child sources.\n\n     This capability flag is always set for transitions.\n\n   - **OBS_SOURCE_DO_NOT_DUPLICATE** - Source should not be fully\n     duplicated.\n\n     When this is used, specifies that the source should not be fully\n     duplicated, and should prefer to duplicate via holding references\n     rather than full duplication.\n\n     When functions such as :c:func:`obs_source_duplicate()` or\n     :c:func:`obs_scene_duplicate()` are called, sources or child\n     sources with this flag will never be fully duplicated, and will\n     instead only be referenced.\n\n     An example of the type of sources that should not be fully\n     duplicated are video devices, browsers, and video/audio captures,\n     as they will either not function correctly or will cause\n     performance or resource issues when duplicated.\n\n   - **OBS_SOURCE_DEPRECATED** - Source is deprecated and should not be\n     used.\n\n   - **OBS_SOURCE_DO_NOT_SELF_MONITOR** - Audio of this source should\n     not allow monitoring if the current monitoring device is the same\n     device being captured by the source.\n\n     This flag is used as a hint to the back-end to prevent the source\n     from creating an audio feedback loop.  This is primarily only used\n     with desktop audio capture sources.\n\n   - **OBS_SOURCE_CAP_DISABLED** - This source type has been disabled\n     and should not be shown as a type of source the user can add.\n\n   - **OBS_SOURCE_CAP_OBSOLETE** - This source type is obsolete and\n     should not be shown as a type of source the user can add.\n     Identical to *OBS_SOURCE_CAP_DISABLED*.  Meant to be used when a\n     source has changed in some way (mostly defaults/properties), but\n     you want to avoid breaking older configurations.  Basically solves\n     the problem of \"I want to change the defaults of a source but I\n     don't want to break people's configurations\"\n\n   - **OBS_SOURCE_CONTROLLABLE_MEDIA** - This source has media that can\n     be controlled\n\n   - **OBS_SOURCE_MONITOR_BY_DEFAULT** - Source should enable\n     monitoring by default.  Monitoring should be set by the\n     frontend if this flag is set.\n\n   - **OBS_SOURCE_CEA_708** - Source type provides cea708 data\n\n   - **OBS_SOURCE_SRGB** - Source understands SRGB rendering\n\n   - **OBS_SOURCE_CAP_DONT_SHOW_PROPERTIES** - Source type prefers not\n     to have its properties shown on creation (prefers to rely on\n     defaults first)\n\n   - **OBS_SOURCE_REQUIRES_CANVAS** - Source type requires a canvas.\n\n.. member:: const char *(*obs_source_info.get_name)(void *type_data)\n\n   Get the translated name of the source type.\n\n   :param  type_data:  The type_data variable of this structure\n   :return:            The translated name of the source type\n\n.. member:: void *(*obs_source_info.create)(obs_data_t *settings, obs_source_t *source)\n\n   Creates the implementation data for the source.\n\n   :param  settings: Settings to initialize the source with\n   :param  source:   Source that this data is associated with\n   :return:          The implementation data associated with this source\n\n.. member:: void (*obs_source_info.destroy)(void *data)\n\n   Destroys the implementation data for the source.\n\n   Async sources must not call obs_source_output_video after returning\n   from destroy.\n\n.. member:: uint32_t (*obs_source_info.get_width)(void *data)\n            uint32_t (*obs_source_info.get_height)(void *data)\n\n   Returns the width/height of the source.  These callbacks are required\n   if this is a video source and is synchronous.\n\n   (Author's note: These should really be consolidated in to one\n   function, not two)\n\n   :return: The width/height of the video\n\n.. member:: void (*obs_source_info.get_defaults)(obs_data_t *settings)\n            void (*obs_source_info.get_defaults2)(void *type_data, obs_data_t *settings)\n\n   Sets the default settings for this source.\n\n   :param  settings:  Default settings.  Call obs_data_set_default*\n                      functions on this object to set default setting\n                      values\n\n.. member:: obs_properties_t *(*obs_source_info.get_properties)(void *data)\n            obs_properties_t *(*obs_source_info.get_properties2)(void *data, void *type_data)\n\n   Gets the property information of this source.\n   \n   :param  data:  The implementation data associated with this source.\n                  This value can be null (e.g., when\n                  :c:func:`obs_get_source_properties()` is called on the\n                  source type), make sure to handle this gracefully.\n\n   (Optional)\n\n   :return: The properties of the source\n\n.. member:: void (*obs_source_info.update)(void *data, obs_data_t *settings)\n\n   Updates the settings for this source.\n\n   (Optional)\n\n   :param settings: New settings for this source\n\n.. member:: void (*obs_source_info.activate)(void *data)\n\n   Called when the source has been activated in the main view (visible\n   on stream/recording).\n\n   (Optional)\n\n.. member:: void (*obs_source_info.deactivate)(void *data)\n\n   Called when the source has been deactivated from the main view (no\n   longer visible on stream/recording).\n\n   (Optional)\n\n.. member:: void (*obs_source_info.show)(void *data)\n\n   Called when the source is visible on any display and/or on the main\n   view.\n\n   (Optional)\n\n.. member:: void (*obs_source_info.hide)(void *data)\n\n   Called when the source is no longer visible on any display and/or on\n   the main view.\n\n   (Optional)\n\n.. member:: void (*obs_source_info.video_tick)(void *data, float seconds)\n\n   Called each video frame with the time elapsed.\n\n   (Optional)\n\n   :param  seconds: Seconds elapsed since the last frame\n\n.. member:: void (*obs_source_info.video_render)(void *data, gs_effect_t *effect)\n\n   Called when rendering the source with the graphics subsystem.\n\n   If this is an input/transition source, this is called to draw the\n   source texture with the graphics subsystem.\n\n   If this is a filter source, it wraps source draw calls (for example\n   applying a custom effect with custom parameters to a source).  In\n   this case, it's highly recommended to use the\n   :c:func:`obs_source_process_filter_begin()` and\n   :c:func:`obs_source_process_filter_end()` functions to automatically\n   handle effect-based filter processing.  However, you can implement\n   custom draw handling as desired as well.\n\n   If the source output capability flags do not include\n   OBS_SOURCE_CUSTOM_DRAW, the source must use\n   :c:func:`obs_source_draw()` to render the source's texture.\n\n   :param effect: This parameter is no longer used.  Instead, call\n                  :c:func:`obs_source_draw()`\n\n.. member:: struct obs_source_frame *(*obs_source_info.filter_video)(void *data, struct obs_source_frame *frame)\n\n   Called to filter raw async video data.  This function is only used\n   with asynchronous video filters.\n\n   :param  frame: Video frame to filter\n   :return:       New video frame data.  This can defer video data to\n                  be drawn later if time is needed for processing\n\n.. member:: struct obs_audio_data *(*obs_source_info.filter_audio)(void *data, struct obs_audio_data *audio)\n\n   Called to filter raw audio data.  This function is only used with\n   audio filters.\n\n   :param  audio: Audio data to filter\n   :return:       Modified or new audio data.  You can directly modify\n                  the data passed and return it, or you can defer audio\n                  data for later if time is needed for processing.  If\n                  you are returning new data, that data must exist until\n                  the next call to the\n                  :c:member:`obs_source_info.filter_audio` callback or\n                  until the filter is removed/destroyed\n\n.. member:: void (*obs_source_info.enum_active_sources)(void *data, obs_source_enum_proc_t enum_callback, void *param)\n\n   Called to enumerate all active sources being used within this\n   source.  If the source has children that render audio/video it must\n   implement this callback.  Only used with sources that have the\n   OBS_SOURCE_COMPOSITE output capability flag.\n\n   :param  enum_callback: Enumeration callback\n   :param  param:         User data to pass to callback\n\n.. member:: void (*obs_source_info.save)(void *data, obs_data_t *settings)\n\n   Called when saving custom data for a source.  This is a separate\n   function because sometimes a source needs to know when it is being\n   saved so it doesn't always have to update the current settings until\n   a certain point.\n\n   (Optional)\n\n   :param  settings: Settings object to save data to\n\n.. member:: void (*obs_source_info.load)(void *data, obs_data_t *settings)\n\n   Called when loading custom data from saved source data.  This is\n   called after all the loading sources have actually been created,\n   allowing the ability to reference other sources if desired.\n\n   (Optional)\n\n   :param  settings: Settings object to load data from\n\n.. member:: void (*obs_source_info.mouse_click)(void *data, const struct obs_mouse_event *event, int32_t type, bool mouse_up, uint32_t click_count)\n\n   Called when interacting with a source and a mouse-down or mouse-up\n   occurs.  Only used with sources that have the OBS_SOURCE_INTERACTION\n   output capability flag.\n\n   (Optional)\n\n   :param event:       Mouse event properties\n   :param type:        Mouse button pushed\n   :param mouse_up:    Mouse event type (true if mouse-up)\n   :param click_count: Mouse click count (1 for single click, etc.)\n\n.. member:: void (*obs_source_info.mouse_move)(void *data, const struct obs_mouse_event *event, bool mouse_leave)\n\n   Called when interacting with a source and a mouse-move occurs.  Only\n   used with sources that have the OBS_SOURCE_INTERACTION output\n   capability flag.\n\n   (Optional)\n\n   :param event:       Mouse event properties\n   :param mouse_leave: Mouse leave state (true if mouse left source)\n\n.. member:: void (*obs_source_info.mouse_wheel)(void *data, const struct obs_mouse_event *event, int x_delta, int y_delta)\n\n   Called when interacting with a source and a mouse-wheel occurs.  Only\n   used with sources that have the OBS_SOURCE_INTERACTION output\n   capability flag.\n\n   (Optional)\n\n   :param event:       Mouse event properties\n   :param x_delta:     Movement delta in the horizontal direction\n   :param y_delta:     Movement delta in the vertical direction\n\n\n.. member:: void (*obs_source_info.focus)(void *data, bool focus)\n\n   Called when interacting with a source and gain focus/lost focus event\n   occurs.  Only used with sources that have the OBS_SOURCE_INTERACTION\n   output capability flag.\n\n   (Optional)\n\n   :param focus:       Focus state (true if focus gained)\n\n.. member:: void (*obs_source_info.key_click)(void *data, const struct obs_key_event *event, bool key_up)\n\n   Called when interacting with a source and a key-up or key-down\n   occurs.  Only used with sources that have the OBS_SOURCE_INTERACTION\n   output capability flag.\n\n   (Optional)\n\n   :param event:       Key event properties\n   :param focus:       Key event type (true if mouse-up)\n\n.. member:: void (*obs_source_info.filter_add)(void *data, obs_source_t *source)\n\n   Called when the filter is added to a source.\n\n   (Optional)\n\n   :param  data:   Filter data\n   :param  source: Source that the filter is being added to\n\n.. member:: void (*obs_source_info.filter_remove)(void *data, obs_source_t *source)\n\n   Called when the filter is removed from a source.\n\n   (Optional)\n\n   :param  data:   Filter data\n   :param  source: Source that the filter being removed from\n\n.. member:: void *obs_source_info.type_data\n            void (*obs_source_info.free_type_data)(void *type_data)\n\n   Private data associated with this entry.  Note that this is not the\n   same as the implementation data; this is used to differentiate\n   between two different types if the same callbacks are used for more\n   than one different type.\n\n.. member:: bool (*obs_source_info.audio_render)(void *data, uint64_t *ts_out, struct obs_source_audio_mix *audio_output, uint32_t mixers, size_t channels, size_t sample_rate)\n\n   Called to render audio of composite sources.  Only used with sources\n   that have the OBS_SOURCE_COMPOSITE output capability flag.\n\n.. member:: void (*obs_source_info.enum_all_sources)(void *data, obs_source_enum_proc_t enum_callback, void *param)\n\n   Called to enumerate all active and inactive sources being used\n   within this source.  If this callback isn't implemented,\n   enum_active_sources will be called instead.  Only used with sources\n   that have the OBS_SOURCE_COMPOSITE output capability flag.\n\n   This is typically used if a source can have inactive child sources.\n\n   :param  enum_callback: Enumeration callback\n   :param  param:         User data to pass to callback\n\n.. member:: void (*obs_source_info.transition_start)(void *data)\n            void (*obs_source_info.transition_stop)(void *data)\n\n   Called on transition sources when the transition starts/stops.\n\n   (Optional)\n\n.. member:: enum obs_icon_type obs_source_info.icon_type\n\n   Icon used for the source.\n\n   - **OBS_ICON_TYPE_UNKNOWN**         - Unknown\n   - **OBS_ICON_TYPE_IMAGE**           - Image\n   - **OBS_ICON_TYPE_COLOR**           - Color\n   - **OBS_ICON_TYPE_SLIDESHOW**       - Slideshow\n   - **OBS_ICON_TYPE_AUDIO_INPUT**     - Audio Input\n   - **OBS_ICON_TYPE_AUDIO_OUTPUT**    - Audio Output\n   - **OBS_ICON_TYPE_DESKTOP_CAPTURE** - Desktop Capture\n   - **OBS_ICON_TYPE_WINDOW_CAPTURE**  - Window Capture\n   - **OBS_ICON_TYPE_GAME_CAPTURE**    - Game Capture\n   - **OBS_ICON_TYPE_CAMERA**          - Camera\n   - **OBS_ICON_TYPE_TEXT**            - Text\n   - **OBS_ICON_TYPE_MEDIA**           - Media\n   - **OBS_ICON_TYPE_BROWSER**         - Browser\n   - **OBS_ICON_TYPE_CUSTOM**          - Custom (not implemented yet)\n\n.. member:: void (*obs_source_info.media_play_pause)(void *data, bool pause)\n\n   Called to pause or play media.\n\n.. member:: void (*obs_source_info.media_restart)(void *data)\n\n   Called to restart the media.\n\n.. member:: void (*obs_source_info.media_stop)(void *data)\n\n   Called to stop the media.\n\n.. member:: void (*obs_source_info.media_next)(void *data)\n\n   Called to go to the next media.\n\n.. member:: void (*obs_source_info.media_previous)(void *data)\n\n   Called to go to the previous media.\n\n.. member:: int64_t (*obs_source_info.media_get_duration)(void *data)\n\n   Called to get the media duration.\n\n.. member:: int64_t (*obs_source_info.media_get_time)(void *data)\n\n   Called to get the current time of the media.\n\n.. member:: void (*obs_source_info.media_set_time)(void *data, int64_t milliseconds)\n\n   Called to set the media time.\n\n.. member:: enum obs_media_state (*obs_source_info.media_get_state)(void *data)\n\n   Called to get the state of the media.\n\n   - **OBS_MEDIA_STATE_NONE**      - None\n   - **OBS_MEDIA_STATE_PLAYING**   - Playing\n   - **OBS_MEDIA_STATE_OPENING**   - Opening\n   - **OBS_MEDIA_STATE_BUFFERING** - Buffering\n   - **OBS_MEDIA_STATE_PAUSED**    - Paused\n   - **OBS_MEDIA_STATE_STOPPED**   - Stopped\n   - **OBS_MEDIA_STATE_ENDED**     - Ended\n   - **OBS_MEDIA_STATE_ERROR**     - Error\n\n.. member:: obs_missing_files_t *(*missing_files)(void *data)\n\n   Called to get the missing files of the source.\n\n.. member:: enum gs_color_space (*obs_source_info.video_get_color_space)(void *data, size_t count, const enum gs_color_space *preferred_spaces)\n\n   Returns the color space of the source. Assume GS_CS_SRGB if not\n   implemented.\n\n   There's an optimization an SDR source can do when rendering to HDR.\n   Check if the active space is GS_CS_709_EXTENDED, and return\n   GS_CS_709_EXTENDED instead of GS_CS_SRGB to avoid an redundant\n   conversion. This optimization can only be done if the pixel shader\n   outputs linear 709, which is why it's not performed by default.\n\n   :return: The color space of the video\n\n\n.. _source_signal_handler_reference:\n\nCommon Source Signals\n---------------------\n\nThe following signals are defined for every source type:\n\n**destroy** (ptr *source*)\n\n   This signal is called when the source is about to be destroyed.  Do\n   not increment any references when using this signal.\n\n**remove** (ptr source)\n\n   Called when the :c:func:`obs_source_remove()` function is called on\n   the source.\n\n**update** (ptr source)\n\n   Called when the source's settings have been updated.\n\n   .. versionadded:: 29.0.0\n\n**save** (ptr source)\n\n   Called when the source is being saved.\n\n**load** (ptr source)\n\n   Called when the source is being loaded.\n\n**activate** (ptr source)\n\n   Called when the source has been activated in the main view (visible\n   on stream/recording).\n\n**deactivate** (ptr source)\n\n   Called when the source has been deactivated from the main view (no\n   longer visible on stream/recording).\n\n**show** (ptr source)\n\n   Called when the source is visible on any display and/or on the main\n   view.\n\n**hide** (ptr source)\n\n   Called when the source is no longer visible on any display and/or on\n   the main view.\n\n**mute** (ptr source, bool muted)\n\n   Called when the source is muted/unmuted.\n\n**push_to_mute_changed** (ptr source, bool enabled)\n\n   Called when push-to-mute has been enabled/disabled.\n\n**push_to_mute_delay** (ptr source, int delay)\n\n   Called when the push-to-mute delay value has changed.\n\n**push_to_talk_changed** (ptr source, bool enabled)\n\n   Called when push-to-talk has been enabled/disabled.\n\n**push_to_talk_delay** (ptr source, int delay)\n\n   Called when the push-to-talk delay value has changed.\n\n**enable** (ptr source, bool enabled)\n\n   Called when the source has been disabled/enabled.\n\n**rename** (ptr source, string new_name, string prev_name)\n\n   Called when the source has been renamed.\n\n**volume** (ptr source, in out float volume)\n\n   Called when the volume of the source has changed.\n\n**update_properties** (ptr source)\n\n   Called to signal to any properties view (or other users of the source's\n   obs_properties_t) that the presentable properties of the source have changed\n   and should be re-queried via obs_source_properties.\n   Does not mean that the source's *settings* (as configured by the user) have\n   changed. For that, use the `update` signal instead.\n\n**update_flags** (ptr source, int flags)\n\n   Called when the flags of the source have been changed.\n\n**audio_sync** (ptr source, int out int offset)\n\n   Called when the audio sync offset has changed.\n\n**audio_balance** (ptr source, in out float balance)\n\n   Called when the audio balance has changed.\n\n**audio_mixers** (ptr source, in out int mixers)\n\n   Called when the audio mixers have changed.\n\n**audio_activate** (ptr source)\n\n   Called when the source's audio becomes active.\n\n**audio_deactivate** (ptr source)\n\n   Called when the source's audio becomes inactive.\n\n**filter_add** (ptr source, ptr filter)\n\n   Called when a filter has been added to the source.\n\n   .. versionadded:: 30.0\n\n**filter_remove** (ptr source, ptr filter)\n\n   Called when a filter has been removed from the source.\n\n**reorder_filters** (ptr source)\n\n   Called when filters have been reordered.\n\n**transition_start** (ptr source)\n\n   Called when a transition is starting.\n\n**transition_video_stop** (ptr source)\n\n   Called when a transition's video transitioning has stopped.\n\n**transition_stop** (ptr source)\n\n   Called when a transition has stopped.\n\n**media_started** (ptr source)\n\n   Called when media has started.\n\n**media_ended** (ptr source)\n\n   Called when media has ended.\n\n**media_pause** (ptr source)\n\n   Called when media has been paused.\n\n**media_play** (ptr source)\n\n   Called when media starts playing.\n\n**media_restart** (ptr source)\n\n   Called when the playing of media has been restarted.\n\n**media_stopped** (ptr source)\n\n   Called when the playing of media has been stopped.\n\n**media_next** (ptr source)\n\n   Called when the media source switches to the next media.\n\n**media_previous** (ptr source)\n\n   Called when the media source switches to the previous media.\n\n\nSource-specific Signals\n-----------------------\n\n**slide_changed** (int index, string path)\n\n   Called when the source's currently displayed image changes.\n\n   :Defined by: - Image Slide Show\n\n-----------------------\n\n**hooked** (ptr source, string title, string class, string executable)\n\n   Called when the source successfully captures an existing window.\n\n   :Defined by: - Window Capture (Windows)\n                - Game Capture (Windows)\n                - Application Audio Output Capture (Windows)\n\n-----------------------\n\n**hooked** (ptr source, string name, string class)\n\n   Called when the source successfully captures an existing window.\n\n   :Defined by: - Window Capture (Xcomposite)\n\n-----------------------\n\n**unhooked** (ptr source)\n\n   Called when the source stops capturing.\n\n   :Defined by: - Window Capture (Windows)\n                - Game Capture (Windows)\n                - Application Audio Output Capture (Windows)\n                - Window Capture (Xcomposite)\n\n-----------------------\n\n\nSource-specific Procedures\n--------------------------\n\nThe following procedures are defined for specific sources only:\n\n**current_index** (out int current_index)\n\n   Returns the index of the currently displayed image in the slideshow.\n\n   :Defined by: - Image Slide Show\n\n-----------------------\n\n**total_files** (out int total_files)\n\n   Returns the total number of image files in the slideshow.\n\n   :Defined by: - Image Slide Show\n\n-----------------------\n\n**get_hooked** (out bool hooked, out string title, out string class, out string executable)\n\n   Returns whether the source is currently capturing a window and if yes, which.\n\n   :Defined by: - Window Capture (Windows)\n                - Game Capture (Windows)\n                - Application audio output capture (Windows)\n\n-----------------------\n\n**get_hooked** (out bool hooked, out string name, out string class)\n\n   Returns whether the source is currently capturing a window and if yes, which.\n\n   :Defined by: - Window Capture (Xcomposite)\n\n-----------------------\n\n**get_metadata** (in string tag_id, out string tag_data)\n\n   For a given metadata tag, returns the data associated with it.\n\n   :Defined by: - VLC Video Source\n\n-----------------------\n\n**restart** ()\n\n   Restarts the media.\n\n   :Defined by: - Media Source\n\n-----------------------\n\n**get_duration** (out int duration)\n\n   Returns the total duration of the media file, in nanoseconds.\n\n   :Defined by: - Media Source\n\n-----------------------\n\n**get_nb_frames** (out int num_frames)\n\n   Returns the total number of frames in the media file.\n\n   :Defined by: - Media Source\n\n-----------------------\n\n**activate** (in bool active)\n\n   Activates or deactivates the device.\n\n   :Defined by: - Video Capture Device Source (Windows)\n\n-----------------------\n\n\nGeneral Source Functions\n------------------------\n\n.. function:: void obs_register_source(struct obs_source_info *info)\n\n   Registers a source type.  Typically used in\n   :c:func:`obs_module_load()` or in the program's initialization phase.\n\n---------------------\n\n.. function:: const char *obs_source_get_display_name(const char *id)\n\n   Calls the :c:member:`obs_source_info.get_name` callback to get the\n   translated display name of a source type.\n\n   :param    id:            The source type string identifier\n   :return:                 The translated display name of a source type\n\n---------------------\n\n.. function:: obs_source_t *obs_source_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data)\n\n   Creates a source of the specified type with the specified settings.\n\n   The \"source\" context is used for anything related to presenting\n   or modifying video/audio.  Use :c:func:`obs_source_release` to release it.\n\n   :param   id:             The source type string identifier\n   :param   name:           The desired name of the source.  If this is\n                            not unique, it will be made to be unique\n   :param   settings:       The settings for the source, or *NULL* if\n                            none\n   :param   hotkey_data:    Saved hotkey data for the source, or *NULL*\n                            if none\n   :return:                 A reference to the newly created source, or\n                            *NULL* if failed\n\n---------------------\n\n.. function:: obs_source_t *obs_source_create_private(const char *id, const char *name, obs_data_t *settings)\n\n   Creates a 'private' source which is not enumerated by\n   :c:func:`obs_enum_sources()`, and is not saved by\n   :c:func:`obs_save_sources()`.\n\n   Author's Note: The existence of this function is a result of design\n   flaw: the front-end should control saving/loading of sources, and\n   functions like :c:func:`obs_enum_sources()` and\n   :c:func:`obs_save_sources()` should not exist in the back-end.\n\n   :param   id:             The source type string identifier\n   :param   name:           The desired name of the source.  For private\n                            sources, this does not have to be unique,\n                            and can additionally be *NULL* if desired\n   :param   settings:       The settings for the source, or *NULL* if\n                            none\n   :return:                 A reference to the newly created source, or\n                            *NULL* if failed\n\n---------------------\n\n.. function:: obs_source_t *obs_source_duplicate(obs_source_t *source, const char *desired_name, bool create_private)\n\n   Duplicates a source.  If the source has the\n   OBS_SOURCE_DO_NOT_DUPLICATE output flag set, this only returns a\n   new reference to the same source. Either way,\n   release with :c:func:`obs_source_release`.\n\n   :param source:         The source to duplicate\n   :param desired_name:   The desired name of the new source.  If this is\n                          not a private source and the name is not unique,\n                          it will be made to be unique\n   :param create_private: If *true*, the new source will be a private\n                          source if fully duplicated\n   :return:               A new source reference\n\n---------------------\n\n.. function:: obs_source_t *obs_source_get_ref(obs_source_t *source)\n\n   Returns an incremented reference if still valid, otherwise returns\n   *NULL*. Use :c:func:`obs_source_release` to release it.\n\n---------------------\n\n.. function:: void obs_source_release(obs_source_t *source)\n\n   Releases a reference to a source.  When the last reference is\n   released, the source is destroyed.\n\n---------------------\n\n.. function:: obs_weak_source_t *obs_source_get_weak_source(obs_source_t *source)\n              obs_source_t *obs_weak_source_get_source(obs_weak_source_t *weak)\n\n   These functions are used to get an incremented weak reference from a strong source\n   reference, or an incremented strong source reference from a weak reference. If\n   the source is destroyed, *obs_weak_source_get_source* will return\n   *NULL*. Release with :c:func:`obs_weak_source_release()` or\n   :c:func:`obs_source_release()`, respectively.\n\n---------------------\n\n.. function:: void obs_weak_source_addref(obs_weak_source_t *weak)\n              void obs_weak_source_release(obs_weak_source_t *weak)\n\n   Adds/releases a weak reference to a source.\n\n---------------------\n\n.. function:: void obs_source_remove(obs_source_t *source)\n\n   Notifies all reference holders of the source (via\n   :c:func:`obs_source_removed()`) that the source should be released.\n\n---------------------\n\n.. function:: bool obs_source_removed(const obs_source_t *source)\n\n   :return: *true* if the source should be released\n\n---------------------\n\n.. function:: bool obs_source_is_hidden(obs_source_t *source)\n              void obs_source_set_hidden(obs_source_t *source, bool hidden)\n\n   Gets/sets the hidden property that determines whether it should be hidden from the user.\n   Used when the source is still alive but should not be referenced.\n\n---------------------\n\n.. function:: uint32_t obs_source_get_output_flags(const obs_source_t *source)\n              uint32_t obs_get_source_output_flags(const char *id)\n\n   :return: Capability flags of a source\n\n   Author's Note: \"Output flags\" is poor wording in retrospect; this\n   should have been named \"Capability flags\", and the OBS_SOURCE_*\n   macros should really be OBS_SOURCE_CAP_* macros instead.\n\n   See :c:member:`obs_source_info.output_flags` for more information.\n\n---------------------\n\n.. function:: obs_data_t *obs_get_source_defaults(const char *id)\n\n   Calls :c:member:`obs_source_info.get_defaults` to get the defaults\n   settings of the source type.\n\n   :return: The default settings for a source type\n\n---------------------\n\n.. function:: obs_properties_t *obs_source_properties(const obs_source_t *source)\n              obs_properties_t *obs_get_source_properties(const char *id)\n\n   Use these functions to get the properties of a source or source type.\n   Properties are optionally used (if desired) to automatically generate\n   user interface widgets to allow users to update settings.\n\n   :return: The properties list for a specific existing source.  Free with\n            :c:func:`obs_properties_destroy()`\n\n---------------------\n\n.. function:: bool obs_source_configurable(const obs_source_t *source)\n              bool obs_is_source_configurable(const char *id)\n\n   :return: *true* if the the source has custom properties, *false*\n            otherwise\n\n---------------------\n\n.. function:: void obs_source_update(obs_source_t *source, obs_data_t *settings)\n\n   Updates the settings for a source and calls the\n   :c:member:`obs_source_info.update` callback of the source.  If the\n   source is a video source, the :c:member:`obs_source_info.update` will\n   be not be called immediately; instead, it will be deferred to the\n   video thread to prevent threading issues.\n\n---------------------\n\n.. function:: void obs_source_reset_settings(obs_source_t *source, obs_data_t *settings)\n\n   Same as :c:func:`obs_source_update`, but clears existing settings\n   first.\n\n---------------------\n\n.. function:: void obs_source_video_render(obs_source_t *source)\n\n   Renders a video source.  This will call the\n   :c:member:`obs_source_info.video_render` callback of the source.\n\n---------------------\n\n.. function:: uint32_t obs_source_get_width(obs_source_t *source)\n              uint32_t obs_source_get_height(obs_source_t *source)\n\n   Calls the :c:member:`obs_source_info.get_width` or\n   :c:member:`obs_source_info.get_height` of the source to get its width\n   and/or height.\n\n   Author's Note: These functions should be consolidated in to a single\n   function/callback rather than having a function for both width and\n   height.\n\n   :return: The width or height of the source\n\n---------------------\n\n.. function:: enum gs_color_space obs_source_get_color_space(obs_source_t *source, size_t count, const enum gs_color_space *preferred_spaces)\n\n   Calls the :c:member:`obs_source_info.video_get_color_space` of the\n   source to get its color space. Assumes GS_CS_SRGB if not implemented.\n\n   Disabled filters are skipped, and async video sources can figure out\n   the color space for themselves.\n\n   :return: The color space of the source\n\n---------------------\n\n.. function:: bool obs_source_get_texcoords_centered(obs_source_t *source)\n\n   Hints whether or not the source will blend texels.\n\n   :return: Whether or not the source will blend texels\n\n---------------------\n\n.. function:: obs_data_t *obs_source_get_settings(const obs_source_t *source)\n\n   :return: The settings string for a source.  The reference counter of the\n            returned settings data is incremented, so\n            :c:func:`obs_data_release()` must be called when the\n            settings are no longer used\n\n---------------------\n\n.. function:: const char *obs_source_get_name(const obs_source_t *source)\n\n   :return: The name of the source\n\n---------------------\n\n.. function:: const char *obs_source_get_uuid(const obs_source_t *source)\n\n   :return: The UUID of the source\n\n   .. versionadded:: 29.1\n\n---------------------\n\n.. function:: void obs_source_set_name(obs_source_t *source, const char *name)\n\n   Sets the name of a source.  If the source is not private and the name\n   is not unique, it will automatically be given a unique name.\n\n---------------------\n\n.. function:: enum obs_source_type obs_source_get_type(const obs_source_t *source)\n\n   :return: | OBS_SOURCE_TYPE_INPUT for inputs\n            | OBS_SOURCE_TYPE_FILTER for filters\n            | OBS_SOURCE_TYPE_TRANSITION for transitions\n            | OBS_SOURCE_TYPE_SCENE for scenes\n\n---------------------\n\n.. function:: bool obs_source_is_scene(const obs_source_t *source)\n\n   :return: *true* if the source is a scene\n\n---------------------\n\n.. function:: bool obs_source_is_group(const obs_source_t *source)\n\n   :return: *true* if the source is a group\n\n---------------------\n\n.. function:: const char *obs_source_get_id(const obs_source_t *source)\n\n   :return: The source's type identifier string. If the source is versioned,\n            \"_vN\" is appended at the end, where \"N\" is the source's version.\n\n ---------------------\n\n.. function:: const char *obs_source_get_unversioned_id(const obs_source_t *source)\n\n   :return: The source's unversioned type identifier string.\n\n---------------------\n\n.. function:: signal_handler_t *obs_source_get_signal_handler(const obs_source_t *source)\n\n   :return: The source's signal handler. Should not be manually freed,\n            as its lifecycle is managed by libobs.\n\n   See the :ref:`source_signal_handler_reference` for more information\n   on signals that are available for sources.\n\n---------------------\n\n.. function:: proc_handler_t *obs_source_get_proc_handler(const obs_source_t *source)\n\n   :return: The procedure handler for a source. Should not be manually freed,\n            as its lifecycle is managed by libobs.\n\n---------------------\n\n.. function:: void obs_source_set_volume(obs_source_t *source, float volume)\n              float obs_source_get_volume(const obs_source_t *source)\n\n   Sets/gets the user volume for a source that has audio output.\n\n---------------------\n\n.. function:: bool obs_source_muted(const obs_source_t *source)\n              void obs_source_set_muted(obs_source_t *source, bool muted)\n\n   Sets/gets whether the source's audio is muted.\n\n---------------------\n\n.. function:: enum speaker_layout obs_source_get_speaker_layout(obs_source_t *source)\n\n   Gets the current speaker layout.\n\n---------------------\n\n.. function:: void obs_source_set_balance_value(obs_source_t *source, float balance)\n              float obs_source_get_balance_value(const obs_source_t *source)\n\n   Sets/gets the audio balance value.\n\n---------------------\n\n.. function:: void obs_source_set_sync_offset(obs_source_t *source, int64_t offset)\n              int64_t obs_source_get_sync_offset(const obs_source_t *source)\n\n   Sets/gets the audio sync offset (in nanoseconds) for a source.\n\n---------------------\n\n.. function:: void obs_source_set_audio_mixers(obs_source_t *source, uint32_t mixers)\n              uint32_t obs_source_get_audio_mixers(const obs_source_t *source)\n\n   Sets/gets the audio mixer channels (i.e. audio tracks) that a source outputs to,\n   depending on what bits are set.  Audio mixers allow filtering\n   specific using multiple audio encoders to mix different sources\n   together depending on what mixer channel they're set to.\n\n   For example, to output to mixer 1 and 3, you would perform a bitwise\n   OR on bits 0 and 2:  (1<<0) | (1<<2), or 0x5.\n\n---------------------\n\n.. function:: void obs_source_set_monitoring_type(obs_source_t *source, enum obs_monitoring_type type)\n              enum obs_monitoring_type obs_source_get_monitoring_type(obs_source_t *source)\n\n   Sets/gets the desktop audio monitoring type.\n\n   :param order: | OBS_MONITORING_TYPE_NONE - Do not monitor\n                 | OBS_MONITORING_TYPE_MONITOR_ONLY - Send to monitor device, no outputs\n                 | OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT - Send to monitor device and outputs\n\n---------------------\n\n.. function:: void obs_source_set_audio_active(obs_source_t *source, bool active)\n              bool obs_source_audio_active(const obs_source_t *source)\n\n   Sets/gets the audio active status (controls whether the source is shown in the mixer).\n\n---------------------\n\n.. function:: void obs_source_enum_active_sources(obs_source_t *source, obs_source_enum_proc_t enum_callback, void *param)\n              void obs_source_enum_active_tree(obs_source_t *source, obs_source_enum_proc_t enum_callback, void *param)\n\n   Enumerates active child sources or source tree used by this source.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   typedef void (*obs_source_enum_proc_t)(obs_source_t *parent,\n                   obs_source_t *child, void *param);\n\n---------------------\n\n.. function:: bool obs_source_push_to_mute_enabled(const obs_source_t *source)\n              void obs_source_enable_push_to_mute(obs_source_t *source, bool enabled)\n\n   Sets/gets whether push-to-mute is enabled.\n\n---------------------\n\n.. function:: uint64_t obs_source_get_push_to_mute_delay(const obs_source_t *source)\n              void obs_source_set_push_to_mute_delay(obs_source_t *source, uint64_t delay)\n\n   Sets/gets the push-to-mute delay.\n\n---------------------\n\n.. function:: bool obs_source_push_to_talk_enabled(const obs_source_t *source)\n              void obs_source_enable_push_to_talk(obs_source_t *source, bool enabled)\n\n   Sets/gets whether push-to-talk is enabled.\n\n---------------------\n\n.. function:: uint64_t obs_source_get_push_to_talk_delay(const obs_source_t *source)\n              void obs_source_set_push_to_talk_delay(obs_source_t *source, uint64_t delay)\n\n   Sets/gets the push-to-talk delay.\n\n---------------------\n\n.. function:: bool obs_source_active(const obs_source_t *source)\n\n   :return: *true* if active, *false* if not.  A source is only\n            considered active if it's being shown on the final mix\n\n---------------------\n\n.. function:: bool obs_source_showing(const obs_source_t *source)\n\n   :return: *true* if showing, *false* if not.  A source is considered\n            showing if it's being displayed anywhere at all, whether on\n            a display context or on the final output\n\n---------------------\n\n.. function:: void obs_source_inc_showing(obs_source_t *source)\n              void obs_source_dec_showing(obs_source_t *source)\n\n   Increments/decrements a source's \"showing\" state.  Typically used\n   when drawing a source on a display manually.\n\n---------------------\n\n.. function:: void obs_source_set_flags(obs_source_t *source, uint32_t flags)\n              uint32_t obs_source_get_flags(const obs_source_t *source)\n\n   :param flags: OBS_SOURCE_FLAG_FORCE_MONO Forces audio to mono\n\n---------------------\n\n.. function:: void obs_source_enum_filters(obs_source_t *source, obs_source_enum_proc_t callback, void *param)\n\n   Enumerates active filters on a source.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   typedef void (*obs_source_enum_proc_t)(obs_source_t *parent,\n                   obs_source_t *child, void *param);\n\n---------------------\n\n.. function:: obs_source_t *obs_source_get_filter_by_name(obs_source_t *source, const char *name)\n\n   :return: The desired filter, or *NULL* if not found.  The reference\n            of the filter is incremented\n\n---------------------\n\n.. function:: void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src)\n\n   Copies filters from the source to the destination.  If filters by the\n   same name already exist in the destination source, the newer filters\n   will be given unique names.\n\n---------------------\n\n.. function:: void obs_source_copy_single_filter(obs_source_t *dst, obs_source_t *filter)\n\n   Copies the filter from the source to the destination. If a filter by the\n   same name already exists in the destination source, the newer filter\n   will be given a unique name.\n\n---------------------\n\n.. function:: size_t obs_source_filter_count(const obs_source_t *source)\n\n   Returns the number of filters the source has.\n\n---------------------\n\n.. function:: obs_data_array_t *obs_source_backup_filters(obs_source_t *source)\n              void obs_source_restore_filters(obs_source_t *source, obs_data_array_t *array)\n\n   Backs up and restores the current filter list and order.\n\n---------------------\n\n.. function:: bool obs_source_enabled(const obs_source_t *source)\n              void obs_source_set_enabled(obs_source_t *source, bool enabled)\n\n   Enables/disables a source, or returns the enabled state.\n\n---------------------\n\n.. function:: void obs_source_add_audio_capture_callback(obs_source_t *source, obs_source_audio_capture_t callback, void *param)\n              void obs_source_remove_audio_capture_callback(obs_source_t *source, obs_source_audio_capture_t callback, void *param)\n\n   Adds/removes an audio capture callback for a source.  This allows the\n   ability to get the raw audio data of a source as it comes in.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   typedef void (*obs_source_audio_capture_t)(void *param, obs_source_t *source,\n                   const struct audio_data *audio_data, bool muted);\n\n---------------------\n\n.. function:: void obs_source_set_deinterlace_mode(obs_source_t *source, enum obs_deinterlace_mode mode)\n              enum obs_deinterlace_mode obs_source_get_deinterlace_mode(const obs_source_t *source)\n\n   Sets/gets the deinterlace mode.\n\n   :param mode:   | OBS_DEINTERLACE_MODE_DISABLE    - Disables deinterlacing\n                  | OBS_DEINTERLACE_MODE_DISCARD    - Discard\n                  | OBS_DEINTERLACE_MODE_RETRO      - Retro\n                  | OBS_DEINTERLACE_MODE_BLEND      - Blend\n                  | OBS_DEINTERLACE_MODE_BLEND_2X   - Blend 2x\n                  | OBS_DEINTERLACE_MODE_LINEAR     - Linear\n                  | OBS_DEINTERLACE_MODE_LINEAR_2X  - Linear 2x\n                  | OBS_DEINTERLACE_MODE_YADIF      - Yadif\n                  | OBS_DEINTERLACE_MODE_YADIF_2X   - Yadif 2x\n\n\n---------------------\n\n.. function:: void obs_source_set_deinterlace_field_order(obs_source_t *source, enum obs_deinterlace_field_order order)\n              enum obs_deinterlace_field_order obs_source_get_deinterlace_field_order(const obs_source_t *source)\n\n   Sets/gets the deinterlace field order.\n\n   :param order: | OBS_DEINTERLACE_FIELD_ORDER_TOP - Start from top\n                 | OBS_DEINTERLACE_FIELD_ORDER_BOTTOM - Start from bottom\n\n---------------------\n\n.. function:: obs_data_t *obs_source_get_private_settings(obs_source_t *item)\n\n   Gets private front-end settings data.  This data is saved/loaded\n   automatically.  Returns an incremented reference. Use :c:func:`obs_data_release()`\n   to release it.\n\n---------------------\n\n.. function:: void obs_source_send_mouse_click(obs_source_t *source, const struct obs_mouse_event *event, int32_t type, bool mouse_up, uint32_t click_count)\n\n   Used for interacting with sources: sends a mouse down/up event to a\n   source.\n\n---------------------\n\n.. function:: void obs_source_send_mouse_move(obs_source_t *source, const struct obs_mouse_event *event, bool mouse_leave)\n\n   Used for interacting with sources: sends a mouse move event to a\n   source.\n\n---------------------\n\n.. function:: void obs_source_send_mouse_wheel(obs_source_t *source, const struct obs_mouse_event *event, int x_delta, int y_delta)\n\n   Used for interacting with sources:  sends a mouse wheel event to a\n   source.\n\n---------------------\n\n.. function:: void obs_source_send_focus(obs_source_t *source, bool focus)\n\n   Used for interacting with sources:  sends a got-focus or lost-focus\n   event to a source.\n\n---------------------\n\n.. function:: void obs_source_send_key_click(obs_source_t *source, const struct obs_key_event *event, bool key_up)\n\n   Used for interacting with sources:  sends a key up/down event to a\n   source.\n\n---------------------\n\n.. function:: enum obs_icon_type obs_source_get_icon_type(const char *id)\n\n   Calls the :c:member:`obs_source_info.icon_type` to get the icon type.\n\n---------------------\n\n.. function:: void obs_source_media_play_pause(obs_source_t *source, bool pause)\n\n   Calls the :c:member:`obs_source_info.media_play_pause` to pause or play media.\n\n---------------------\n\n.. function:: void obs_source_media_restart(obs_source_t *source)\n\n   Calls the :c:member:`obs_source_info.media_restart` to restart the media.\n\n---------------------\n\n.. function:: void obs_source_media_stop(obs_source_t *source)\n\n   Calls the :c:member:`obs_source_info.media_stop` to stop the media.\n\n---------------------\n\n.. function:: void obs_source_media_next(obs_source_t *source)\n\n   Calls the :c:member:`obs_source_info.media_next` to go to the next media.\n\n---------------------\n\n.. function:: void obs_source_media_previous(obs_source_t *source)\n\n   Calls the :c:member:`obs_source_info.media_previous` to go to the previous media.\n\n---------------------\n\n.. function:: int64_t obs_source_media_get_duration(obs_source_t *source)\n\n   Calls the :c:member:`obs_source_info.media_get_duration` to\n   get the media duration in milliseconds.\n\n---------------------\n\n.. function:: int64_t obs_source_media_get_time(obs_source_t *source)\n              void obs_source_media_set_time(obs_source_t *source, int64_t ms)\n\n   Calls the :c:member:`obs_source_info.media_get_time` or\n   :c:member:`obs_source_info.media_set_time` to get/set the\n   current time (in milliseconds) of the media. Will return 0\n   for non-media sources.\n\n---------------------\n\n.. function:: enum obs_media_state obs_source_media_get_state(obs_source_t *source)\n\n   Calls the :c:member:`obs_source_info.media_get_state` to get the state of the media.\n\n---------------------\n\n.. function:: void obs_source_media_started(obs_source_t *source)\n\n   Emits a **media_started** signal.\n\n---------------------\n\n.. function:: void obs_source_media_ended(obs_source_t *source)\n\n   Emits a **media_ended** signal.\n\n---------------------\n\n.. function:: obs_canvas_t *obs_source_get_canvas(const obs_source_t *source)\n\n   Get canvas this source belongs to (reference incremented)\n\n---------------------\n\nFunctions used by sources\n-------------------------\n\n.. function:: void obs_source_draw_set_color_matrix(const struct matrix4 *color_matrix, const struct vec3 *color_range_min, const struct vec3 *color_range_max)\n\n   Helper function to set the color matrix information when drawing the\n   source.\n\n   :param  color_matrix:    The color matrix.  Assigns to the 'color_matrix'\n                            effect variable.\n   :param  color_range_min: The minimum color range.  Assigns to the\n                            'color_range_min' effect variable.  If NULL,\n                            {0.0f, 0.0f, 0.0f} is used.\n   :param  color_range_max: The maximum color range.  Assigns to the\n                            'color_range_max' effect variable.  If NULL,\n                            {1.0f, 1.0f, 1.0f} is used.\n\n---------------------\n\n.. function:: void obs_source_draw(gs_texture_t *image, int x, int y, uint32_t cx, uint32_t cy, bool flip)\n\n   Helper function to draw sprites for a source (synchronous video).\n\n   :param  image:  The sprite texture to draw.  Assigns to the 'image' variable\n                   of the current effect.\n   :param  x:      X position of the sprite.\n   :param  y:      Y position of the sprite.\n   :param  cx:     Width of the sprite.  If 0, uses the texture width.\n   :param  cy:     Height of the sprite.  If 0, uses the texture height.\n   :param  flip:   Specifies whether to flip the image vertically.\n\n---------------------\n\n.. function:: void obs_source_output_video(obs_source_t *source, const struct obs_source_frame *frame)\n\n   Outputs asynchronous video data.  Set to NULL to deactivate the texture.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   enum video_format {\n           VIDEO_FORMAT_NONE,\n\n           /* planar 4:2:0 formats */\n           VIDEO_FORMAT_I420, /* three-plane */\n           VIDEO_FORMAT_NV12, /* two-plane, luma and packed chroma */\n\n           /* packed 4:2:2 formats */\n           VIDEO_FORMAT_YVYU,\n           VIDEO_FORMAT_YUY2, /* YUYV */\n           VIDEO_FORMAT_UYVY,\n\n           /* packed uncompressed formats */\n           VIDEO_FORMAT_RGBA,\n           VIDEO_FORMAT_BGRA,\n           VIDEO_FORMAT_BGRX,\n           VIDEO_FORMAT_Y800, /* grayscale */\n\n           /* planar 4:4:4 */\n           VIDEO_FORMAT_I444,\n\n           /* more packed uncompressed formats */\n           VIDEO_FORMAT_BGR3,\n\n           /* planar 4:2:2 */\n           VIDEO_FORMAT_I422,\n\n           /* planar 4:2:0 with alpha */\n           VIDEO_FORMAT_I40A,\n\n           /* planar 4:2:2 with alpha */\n           VIDEO_FORMAT_I42A,\n\n           /* planar 4:4:4 with alpha */\n           VIDEO_FORMAT_YUVA,\n\n           /* packed 4:4:4 with alpha */\n           VIDEO_FORMAT_AYUV,\n\n           /* planar 4:2:0 format, 10 bpp */\n           VIDEO_FORMAT_I010, /* three-plane */\n           VIDEO_FORMAT_P010, /* two-plane, luma and packed chroma */\n\n           /* planar 4:2:2 format, 10 bpp */\n           VIDEO_FORMAT_I210,\n\n           /* planar 4:4:4 format, 12 bpp */\n           VIDEO_FORMAT_I412,\n\n           /* planar 4:4:4:4 format, 12 bpp */\n           VIDEO_FORMAT_YA2L,\n\n           /* planar 4:2:2 format, 16 bpp */\n           VIDEO_FORMAT_P216, /* two-plane, luma and packed chroma */\n\n           /* planar 4:4:4 format, 16 bpp */\n           VIDEO_FORMAT_P416, /* two-plane, luma and packed chroma */\n\n           /* packed 4:2:2 format, 10 bpp */\n           VIDEO_FORMAT_V210,\n\n           /* packed uncompressed 10-bit format */\n           VIDEO_FORMAT_R10L,\n   };\n\n   struct obs_source_frame {\n           uint8_t             *data[MAX_AV_PLANES];\n           uint32_t            linesize[MAX_AV_PLANES];\n           uint32_t            width;\n           uint32_t            height;\n           uint64_t            timestamp;\n\n           enum video_format   format;\n           float               color_matrix[16];\n           bool                full_range;\n           uint16_t            max_luminance;\n           float               color_range_min[3];\n           float               color_range_max[3];\n           bool                flip;\n           uint8_t             flags;\n           uint8_t             trc; /* enum video_trc */\n   };\n\n---------------------\n\n.. function:: void obs_source_set_async_rotation(obs_source_t *source, long rotation)\n\n   Allows the ability to set rotation (0, 90, 180, -90, 270) for an\n   async video source.  The rotation will be automatically applied to\n   the source.\n\n---------------------\n\n.. function:: void obs_source_preload_video(obs_source_t *source, const struct obs_source_frame *frame)\n\n   Preloads a video frame to ensure a frame is ready for playback as\n   soon as video playback starts.\n\n---------------------\n\n.. function:: void obs_source_show_preloaded_video(obs_source_t *source)\n\n   Shows any preloaded video frame.\n\n---------------------\n\n.. function:: void obs_source_output_audio(obs_source_t *source, const struct obs_source_audio *audio)\n\n   Outputs audio data.\n\n---------------------\n\n.. function:: void obs_source_update_properties(obs_source_t *source)\n\n   Signals to any currently opened properties views (or other users of the\n   source's obs_properties_t) that the source's presentable properties have\n   changed and that the view should be updated.\n\n---------------------\n\n.. function:: bool obs_source_add_active_child(obs_source_t *parent, obs_source_t *child)\n\n   Adds an active child source.  Must be called by parent sources on child\n   sources when the child is added and active.  This ensures that the source is\n   properly activated if the parent is active.\n\n   :return: *true* if source can be added, *false* if it causes recursion\n\n---------------------\n\n.. function:: void obs_source_remove_active_child(obs_source_t *parent, obs_source_t *child)\n\n   Removes an active child source.  Must be called by parent sources on child\n   sources when the child is removed or inactive.  This ensures that the source\n   is properly deactivated if the parent is no longer active.\n\n---------------------\n\n\nFilters\n-------\n\n.. function:: obs_source_t *obs_filter_get_parent(const obs_source_t *filter)\n\n   If the source is a filter, returns the parent source of the filter.\n   The parent source is the source being filtered. Does not increment the\n   reference.\n\n   Only guaranteed to be valid inside of the video_render, filter_audio,\n   filter_video, filter_add, and filter_remove callbacks.\n\n---------------------\n\n.. function:: obs_source_t *obs_filter_get_target(const obs_source_t *filter)\n\n   If the source is a filter, returns the target source of the filter.\n   The target source is the next source in the filter chain. Does not increment\n   the reference.\n\n   Only guaranteed to be valid inside of the video_render, filter_audio,\n   filter_video, and filter_remove callbacks.\n\n---------------------\n\n.. function:: void obs_source_default_render(obs_source_t *source)\n\n   Can be used by filters to directly render a non-async parent source\n   without any filter processing.\n\n---------------------\n\n.. function:: void obs_source_filter_add(obs_source_t *source, obs_source_t *filter)\n              void obs_source_filter_remove(obs_source_t *source, obs_source_t *filter)\n\n   Adds/removes a filter to/from a source.\n\n---------------------\n\n.. function:: void obs_source_filter_set_order(obs_source_t *source, obs_source_t *filter, enum obs_order_movement movement)\n\n   Modifies the order of a specific filter.\n\n   :param movement: | Can be one of the following:\n                    | OBS_ORDER_MOVE_UP\n                    | OBS_ORDER_MOVE_DOWN\n                    | OBS_ORDER_MOVE_TOP\n                    | OBS_ORDER_MOVE_BOTTOM\n\n---------------------\n\n.. function:: void obs_source_filter_set_index(obs_source_t *source, obs_source_t *filter, size_t index)\n\n   Moves a filter to the specified index in the filters array.\n\n   :param index: | The index to move the filter to.\n\n   .. versionadded:: 30.0\n\n---------------------\n\n.. function:: int obs_source_filter_get_index(obs_source_t *source, obs_source_t *filter)\n\n   Gets the index of the specified filter.\n\n   :return: Index of the filter or -1 if it is invalid/not found.\n\n   .. versionadded:: 30.0\n\nFunctions used by filters\n-------------------------\n\n.. function:: bool obs_source_process_filter_begin(obs_source_t *filter, enum gs_color_format format, enum obs_allow_direct_render allow_direct)\n\n   Default RGB filter handler for generic effect filters.  Processes the\n   filter chain and renders them to texture if needed, then the filter is\n   drawn with.\n\n   After calling this, set your parameters for the effect, then call\n   obs_source_process_filter_end to draw the filter.\n\n   :return: *true* if filtering should continue, *false* if the filter\n            is bypassed for whatever reason\n\n---------------------\n\n.. function:: bool obs_source_process_filter_begin_with_color_space(obs_source_t *filter, enum gs_color_format format, enum gs_color_space space, enum obs_allow_direct_render allow_direct)\n\n   Similar to obs_source_process_filter_begin, but also set the active\n   color space.\n\n   :return: *true* if filtering should continue, *false* if the filter\n            is bypassed for whatever reason\n\n---------------------\n\n.. function:: void obs_source_process_filter_end(obs_source_t *filter, gs_effect_t *effect, uint32_t width, uint32_t height)\n\n   Draws the filter using the effect's \"Draw\" technique.\n\n   Before calling this function, first call obs_source_process_filter_begin and\n   then set the effect parameters, and then call this function to finalize the\n   filter.\n\n---------------------\n\n.. function:: void obs_source_process_filter_tech_end(obs_source_t *filter, gs_effect_t *effect, uint32_t width, uint32_t height, const char *tech_name)\n\n   Draws the filter with a specific technique in the effect.\n\n   Before calling this function, first call obs_source_process_filter_begin and\n   then set the effect parameters, and then call this function to finalize the\n   filter.\n\n---------------------\n\n.. function:: void obs_source_skip_video_filter(obs_source_t *filter)\n\n   Skips the filter if the filter is invalid and cannot be rendered.\n\n---------------------\n\n\n.. _transitions:\n\nTransitions\n-----------\n\n.. function:: obs_source_t *obs_transition_get_source(obs_source_t *transition, enum obs_transition_target target)\n\n   :param target: | OBS_TRANSITION_SOURCE_A - Source being transitioned from, or the current source if not transitioning\n                  | OBS_TRANSITION_SOURCE_B - Source being transitioned to\n   :return:       An incremented reference to the source or destination\n                  sources of the transition. Use :c:func:`obs_source_release`\n                  to release it.\n\n---------------------\n\n.. function:: void obs_transition_clear(obs_source_t *transition)\n\n   Clears the transition.\n\n---------------------\n\n.. function:: obs_source_t *obs_transition_get_active_source(obs_source_t *transition)\n\n   :return: An incremented reference to the currently active source of\n            the transition. Use :c:func:`obs_source_release` to release it.\n\n---------------------\n\n.. function:: bool obs_transition_start(obs_source_t *transition, enum obs_transition_mode mode, uint32_t duration_ms, obs_source_t *dest)\n\n   Starts the transition with the desired destination source.\n\n   :param mode:        Currently only OBS_TRANSITION_MODE_AUTO\n   :param duration_ms: Duration in milliseconds.  If the transition has\n                       a fixed duration set by\n                       :c:func:`obs_transition_enable_fixed`, this\n                       parameter will have no effect\n   :param dest:        The destination source to transition to\n\n---------------------\n\n.. function:: void obs_transition_is_active(obs_source_t *transition)\n\n   :return: *true* if the transition is currently transitioning, *false* otherwise.\n\n---------------------\n\n.. function:: void obs_transition_set_size(obs_source_t *transition, uint32_t cx, uint32_t cy)\n              void obs_transition_get_size(const obs_source_t *transition, uint32_t *cx, uint32_t *cy)\n\n   Sets/gets the dimensions of the transition.\n\n---------------------\n\n.. function:: void obs_transition_set_scale_type(obs_source_t *transition, enum obs_transition_scale_type type)\n              enum obs_transition_scale_type obs_transition_get_scale_type( const obs_source_t *transition)\n\n   Sets/gets the scale type for sources within the transition.\n\n   :param type: | OBS_TRANSITION_SCALE_MAX_ONLY - Scale to aspect ratio, but only to the maximum size of each source\n                | OBS_TRANSITION_SCALE_ASPECT   - Always scale the sources, but keep aspect ratio\n                | OBS_TRANSITION_SCALE_STRETCH  - Scale and stretch the sources to the size of the transition\n\n---------------------\n\n.. function:: void obs_transition_set_alignment(obs_source_t *transition, uint32_t alignment)\n              uint32_t obs_transition_get_alignment(const obs_source_t *transition)\n\n   Sets/gets the alignment used to draw the two sources within\n   transition the transition.\n\n   :param alignment: | Can be any bitwise OR combination of:\n                     | OBS_ALIGN_CENTER\n                     | OBS_ALIGN_LEFT\n                     | OBS_ALIGN_RIGHT\n                     | OBS_ALIGN_TOP\n                     | OBS_ALIGN_BOTTOM\n\n---------------------\n\n\nFunctions used by transitions\n-----------------------------\n\n.. function:: void obs_transition_enable_fixed(obs_source_t *transition, bool enable, uint32_t duration_ms)\n              bool obs_transition_fixed(obs_source_t *transition)\n\n   Sets/gets whether the transition uses a fixed duration.  Useful for\n   certain types of transitions such as stingers.  If this is set, the\n   *duration_ms* parameter of :c:func:`obs_transition_start()` has no\n   effect.\n\n---------------------\n\n.. function:: float obs_transition_get_time(obs_source_t *transition)\n\n   :return: The current transition time value (0.0f..1.0f)\n\n---------------------\n\n.. function:: void obs_transition_video_render(obs_source_t *transition, obs_transition_video_render_callback_t callback)\n              void obs_transition_video_render2(obs_source_t *transition, obs_transition_video_render_callback_t callback, gs_texture_t *placeholder_texture)\n\n   Helper function used for rendering transitions.  This function will\n   render two distinct textures for source A and source B of the\n   transition, allowing the ability to blend them together with a pixel\n   shader in a desired manner.\n\n   The *a* and *b* parameters of *callback* are automatically rendered\n   textures of source A and source B, *t* is the time value\n   (0.0f..1.0f), *cx* and *cy* are the current dimensions of the\n   transition, and *data* is the implementation's private data.\n\n   The *placeholder_texture* parameter allows a callback to receive\n   a replacement that isn't the default transparent texture, including\n   NULL if the caller desires.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   typedef void (*obs_transition_video_render_callback_t)(void *data,\n                   gs_texture_t *a, gs_texture_t *b, float t,\n                   uint32_t cx, uint32_t cy);\n\n---------------------\n\n.. function:: enum gs_color_space obs_transition_video_get_color_space(obs_source_t *transition)\n\n   Figure out the color space that encompasses both child sources.\n\n   The wider space wins.\n\n   :return: The color space of the transition\n\n---------------------\n\n.. function::  bool obs_transition_audio_render(obs_source_t *transition, uint64_t *ts_out, struct obs_source_audio_mix *audio, uint32_t mixers, size_t channels, size_t sample_rate, obs_transition_audio_mix_callback_t mix_a_callback, obs_transition_audio_mix_callback_t mix_b_callback)\n\n   Helper function used for transitioning audio.  Typically you'd call\n   this in the obs_source_info.audio_render callback with its\n   parameters, and use the mix_a_callback and mix_b_callback to\n   determine the the audio fading of source A and source B.\n\n   Relevant data types used with this function:\n\n.. code:: cpp\n\n   typedef float (*obs_transition_audio_mix_callback_t)(void *data, float t);\n\n---------------------\n\n.. function:: void obs_transition_swap_begin(obs_source_t *tr_dest, obs_source_t *tr_source)\n              void obs_transition_swap_end(obs_source_t *tr_dest, obs_source_t *tr_source)\n\n   Swaps two transitions.  Call obs_transition_swap_begin, swap the\n   source, then call obs_transition_swap_end when complete.  This allows\n   the ability to seamlessly swap two different transitions without it\n   affecting the output.\n\n   For example, if a transition is assigned to output channel 0, you'd\n   call obs_transition_swap_begin, then you'd call obs_set_output_source\n   with the new transition, then call\n   :c:func:`obs_transition_swap_begin()`.\n\n.. ---------------------------------------------------------------------------\n\n.. _libobs/obs-source.h: https://github.com/obsproject/obs-studio/blob/master/libobs/obs-source.h\n"
  },
  {
    "path": "docs/sphinx/requirements.txt",
    "content": "sphinx>=1.3\nsphinx_rtd_theme>=0.5.2\n"
  },
  {
    "path": "docs/sphinx/scripting.rst",
    "content": "Python/Lua Scripting\n====================\n\nScripting (21.0+) adds support for Python 3 and Luajit 2 (which is\nroughly equivalent to Lua 5.2), allowing the ability to quickly extend,\nadd features, or automate the program without needing to build a native\nplugin module.\n\nScripting can be accessed in OBS Studio via the Tools menu -> Scripts\noption, which will bring up the scripting dialog.  Scripts can be added,\nremoved, and reloaded in real time while the program is running.\n\n**NOTE:** To use Python on Windows or macOS, you must download and install a\nPython version that matches your OBS architecture.  Then, in the scripting\ndialog, you must set the path to the Python install in the \"Python Settings\" tab.\n\nAll API bindings are provided through the **obspython** module in\nPython, and the **obslua** module in Lua.\n\nCertain functions have been changed/replaced in order to provide script\ncallbacks, see :ref:`other_script_differences` for more information.\n\n**WARNING:** Because bindings to the entire API are provided, it is\npossible to leak memory or crash the program with an improperly-written\nscript.  Please exercise caution when making scripts and check the\nmemory leak counter in your log file to make sure scripts you write\naren't leaking memory.  **Please treat the API bindings as though you\nwere writing a C program:  read the documentation for functions you use,\nand release/destroy objects you reference or create via the API.**\n\n\nScript Function Exports\n-----------------------\n\nThere are a number of global functions that scripts can optionally\nprovide:\n\n.. py:function:: script_description()\n\n   Called to retrieve a description string to be displayed to the user\n   in the Scripts window.\n\n.. py:function:: script_load(settings)\n\n   Called on script startup with specific settings associated with the\n   script.  The *settings* parameter provided is not typically used for\n   settings that are set by the user; instead the parameter is used for\n   any extra internal settings data that may be used in the script.\n\n   :param settings: Settings associated with the script.\n\n.. py:function:: script_unload()\n\n   Called when the script is being unloaded.\n\n.. py:function:: script_save(settings)\n\n   Called when the script is being saved.  This is not necessary for\n   settings that are set by the user; instead this is used for any\n   extra internal settings data that may be used in the script.\n\n   :param settings: Settings associated with the script.\n\n.. py:function:: script_defaults(settings)\n\n   Called to set default settings (if any) associated with the script.\n   You would typically call :ref:`obs_data_default_funcs` for the\n   on the settings in order to set its default values.\n\n   :param settings: Settings associated with the script.\n\n.. py:function:: script_update(settings)\n\n   Called when the script's settings (if any) have been changed by the\n   user.\n\n   :param settings: Settings associated with the script.\n\n.. py:function:: script_properties()\n\n   Called to define user properties associated with the script.  These\n   properties are used to define how to show settings properties to a\n   user.\n\n   :return: obs_properties_t object created via\n            :c:func:`obs_properties_create()`.\n\n.. py:function:: script_tick(seconds)\n\n   Called every frame in case per-frame processing is needed.  If a\n   timer is needed, please use :ref:`scripting_timers` instead, as\n   timers are more efficient if all that's needed is basic timer\n   functionality.  Using this function in Python is not recommended due\n   to the global interpreter lock of Python.\n\n   :param seconds: Seconds passed since previous frame.\n\n\nGetting the Current Script's Path\n---------------------------------\n\nThere is a function you can use to get the current script's path.  This\nfunction is automatically implemented in to each script before the\nscript is loaded, and is part of the script's namespace, not\nobslua/obspython:\n\n.. py:function:: script_path()\n\n   :return: The path to the script.\n\n\n.. _scripting_timers:\n\nScript Timers\n-------------\n\nScript timers provide an efficient means of providing timer callbacks\nwithout necessarily having to lock scripts/interpreters every frame.\n(These functions are part of the obspython/obslua modules/namespaces).\n\n.. py:function:: timer_add(callback, milliseconds)\n\n    Adds an timer callback which triggers every *milliseconds*.\n\n    Note: Using instance methods as callbacks is not supported. Always\n    use module methods.\n\n.. py:function:: timer_remove(callback)\n\n    Removes a timer callback.  (Note: You can also use\n    :py:func:`remove_current_callback()` to terminate the timer from the\n    timer callback)\n\n\nScript Sources (Lua Only)\n-------------------------\n\nIt is possible to register sources in Lua.  To do so, create a table,\nand define its keys the same way you would define an\n:c:type:`obs_source_info` structure:\n\n.. code:: lua\n\n    local info = {}\n    info.id = \"my_source_id\"\n    info.type = obslua.OBS_SOURCE_TYPE_INPUT\n    info.output_flags = obslua.OBS_SOURCE_VIDEO\n\n    info.get_name = function()\n            return \"My Source\"\n    end\n\n    info.create = function(settings, source)\n            -- typically source data would be stored as a table\n            local my_source_data = {}\n\n            [...]\n\n            return my_source_data\n    end\n\n    info.video_render = function(my_source_data, effect)\n            [...]\n    end\n\n    info.get_width = function(my_source_data)\n            [...]\n\n            -- assuming the source data contains a 'width' key\n            return my_source_data.width\n    end\n\n    info.get_height = function(my_source_data)\n            [...]\n\n            -- assuming the source data contains a 'height' key\n            return my_source_data.height\n    end\n\n    -- register the source\n    obs_register_source(info)\n\n\n.. _other_script_differences:\n\nOther Differences From the C API\n--------------------------------\n\nCertain functions are implemented differently from the C API due to how\ncallbacks work.  (These functions are part of the obspython/obslua\nmodules/namespaces).\n\n.. py:function:: obs_enum_sources()\n\n   Enumerates all sources.\n\n   :return: An array of reference-incremented sources.  Release with\n            :py:func:`source_list_release()`.\n\n.. py:function:: obs_scene_enum_items(scene)\n\n   Enumerates scene items within a scene.\n\n   :param scene: obs_scene_t object to enumerate items from.\n   :return:      List of scene items.  Release with\n                 :py:func:`sceneitem_list_release()`.\n\n.. py:function:: obs_sceneitem_group_enum_items(group)\n\n   Enumerates scene items within a group.\n\n   :param group: obs_sceneitem_t object to enumerate items from.\n   :return:      List of scene items.  Release with\n                 :py:func:`sceneitem_list_release()`.\n\n.. py:function:: obs_add_main_render_callback(callback)\n\n   **Lua only:** Adds a primary output render callback.  This callback\n   has no parameters.\n\n   :param callback: Render callback.  Use\n                    :py:func:`obs_remove_main_render_callback()` or\n                    :py:func:`remove_current_callback()` to remove the\n                    callback.\n\n.. py:function:: obs_remove_main_render_callback(callback)\n\n   **Lua only:** Removes a primary output render callback.\n\n   :param callback: Render callback.\n\n.. py:function:: signal_handler_connect(handler, signal, callback)\n\n   Adds a callback to a specific signal on a signal handler.  This\n   callback has one parameter:  the calldata_t object.\n\n   :param handler:  A signal_handler_t object.\n   :param signal:   The signal on the signal handler (string)\n   :param callback: The callback to connect to the signal.  Use\n                    :py:func:`signal_handler_disconnect()` or\n                    :py:func:`remove_current_callback()` to remove the\n                    callback.\n\n.. py:function:: signal_handler_disconnect(handler, signal, callback)\n\n   Removes a callback from a specific signal of a signal handler.\n\n   :param handler:  A signal_handler_t object.\n   :param signal:   The signal on the signal handler (string)\n   :param callback: The callback to disconnect from the signal.\n\n.. py:function:: signal_handler_connect_global(handler, callback)\n\n   Adds a global callback to a signal handler.  This callback has two\n   parameters:  the first parameter is the signal string, and the second\n   parameter is the calldata_t object.\n\n   :param handler:  A signal_handler_t object.\n   :param callback: The callback to connect.  Use\n                    :py:func:`signal_handler_disconnect_global()` or\n                    :py:func:`remove_current_callback()` to remove the\n                    callback.\n\n.. py:function:: signal_handler_disconnect_global(handler, callback)\n\n   Removes a global callback from a signal handler.\n\n   :param handler:  A signal_handler_t object.\n   :param callback: The callback to disconnect.\n\n.. py:function:: obs_hotkey_register_frontend(name, description, callback)\n\n   Adds a frontend hotkey.  The callback takes one parameter: a boolean\n   'pressed' parameter.\n\n   :param name:        Unique name identifier string of the hotkey.\n   :param description: Hotkey description shown to the user.\n   :param callback:    Callback for the hotkey.  Use\n                       :py:func:`obs_hotkey_unregister()` or\n                       :py:func:`remove_current_callback()` to remove\n                       the callback.\n\n.. py:function:: obs_hotkey_unregister(callback)\n\n   Unregisters the hotkey associated with the specified callback.\n\n   :param callback: Callback of the hotkey to unregister.\n\n.. py:function:: obs_properties_add_button(properties, setting_name, text, callback)\n\n   Adds a button property to an obs_properties_t object.  The callback\n   takes two parameters:  the first parameter is the obs_properties_t\n   object, and the second parameter is the obs_property_t for the\n   button.\n\n   :param properties:   An obs_properties_t object.\n   :param setting_name: A setting identifier string.\n   :param text:         Button text.\n   :param callback:     Button callback.  This callback is automatically\n                        cleaned up.\n\n.. py:function:: remove_current_callback()\n\n   Removes the current callback being executed.  Does nothing if not\n   within a callback.\n\n.. py:function:: source_list_release(source_list)\n\n   Releases the references of a source list.\n\n   :param source_list: Array of sources to release.\n\n\n.. py:function:: sceneitem_list_release(item_list)\n\n   Releases the references of a scene item list.\n\n   :param item_list: Array of scene items to release.\n\n.. py:function:: calldata_source(calldata, name)\n\n   Casts a pointer parameter of a calldata_t object to an obs_source_t\n   object.\n\n   :param calldata: A calldata_t object.\n   :param name:     Name of the parameter.\n   :return:         A borrowed reference to an obs_source_t object.\n\n.. py:function:: calldata_sceneitem(calldata, name)\n\n   Casts a pointer parameter of a calldata_t object to an\n   obs_sceneitem_t object.\n\n   :param calldata: A calldata_t object.\n   :param name:     Name of the parameter.\n   :return:         A borrowed reference to an obs_sceneitem_t object.\n"
  },
  {
    "path": "frontend/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nadd_subdirectory(api)\n\noption(ENABLE_FRONTEND \"Enable building with UI frontend (requires Qt6)\" ON)\n\nif(NOT ENABLE_FRONTEND)\n  target_disable_feature(obs \"User Interface\")\n  return()\nelse()\n  target_enable_feature(obs \"User Interface\")\nendif()\n\nfind_package(FFmpeg REQUIRED COMPONENTS avcodec avutil avformat)\nfind_package(CURL REQUIRED)\n\nif(NOT TARGET OBS::json11)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/deps/json11\" json11)\nendif()\n\nif(NOT TARGET OBS::libobs)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/libobs\" libobs)\nendif()\n\nif(NOT TARGET OBS::bpm)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/shared/bpm\" bpm)\nendif()\n\nadd_executable(obs-studio)\nadd_executable(OBS::studio ALIAS obs-studio)\n\ntarget_link_libraries(\n  obs-studio\n  PRIVATE\n    CURL::libcurl\n    FFmpeg::avcodec\n    FFmpeg::avutil\n    FFmpeg::avformat\n    OBS::libobs\n    OBS::frontend-api\n    OBS::json11\n    OBS::bpm\n)\n\ninclude(cmake/ui-components.cmake)\ninclude(cmake/ui-dialogs.cmake)\ninclude(cmake/ui-docks.cmake)\ninclude(cmake/feature-importers.cmake)\ninclude(cmake/feature-plugin-manager.cmake)\ninclude(cmake/ui-models.cmake)\ninclude(cmake/ui-oauth.cmake)\ninclude(cmake/feature-browserpanels.cmake)\ninclude(cmake/ui-qt.cmake)\ninclude(cmake/ui-settings.cmake)\ninclude(cmake/ui-utility.cmake)\ninclude(cmake/ui-widgets.cmake)\ninclude(cmake/ui-wizards.cmake)\n\nif(NOT OAUTH_BASE_URL)\n  set(OAUTH_BASE_URL \"https://auth.obsproject.com/\" CACHE STRING \"Default OAuth base URL\")\n  mark_as_advanced(OAUTH_BASE_URL)\nendif()\ninclude(cmake/feature-twitch.cmake)\ninclude(cmake/feature-restream.cmake)\ninclude(cmake/feature-youtube.cmake)\ninclude(cmake/feature-whatsnew.cmake)\ninclude(cmake/feature-idian-playground.cmake)\n\nadd_subdirectory(plugins)\n\nconfigure_file(cmake/templates/ui-config.h.in ui-config.h)\n\ntarget_sources(\n  obs-studio\n  PRIVATE obs-main.cpp OBSStudioAPI.cpp OBSStudioAPI.hpp OBSApp.cpp OBSApp.hpp OBSApp_Themes.cpp ui-config.h\n)\n\nif(OS_WINDOWS)\n  include(cmake/os-windows.cmake)\nelseif(OS_MACOS)\n  include(cmake/os-macos.cmake)\nelseif(OS_LINUX)\n  include(cmake/os-linux.cmake)\nelseif(OS_FREEBSD OR OS_OPENBSD)\n  include(cmake/os-freebsd.cmake)\nendif()\n\nforeach(graphics_library IN ITEMS opengl metal d3d11)\n  string(TOUPPER ${graphics_library} graphics_library_U)\n  if(TARGET OBS::libobs-${graphics_library})\n    target_compile_definitions(\n      obs-studio\n      PRIVATE\n        DL_${graphics_library_U}=\"$<$<IF:$<PLATFORM_ID:Windows>,TARGET_FILE_NAME,TARGET_SONAME_FILE_NAME>:OBS::libobs-${graphics_library}>\"\n    )\n  else()\n    target_compile_definitions(obs-studio PRIVATE DL_${graphics_library_U}=\"\")\n  endif()\nendforeach()\n\nget_property(obs_module_list GLOBAL PROPERTY OBS_MODULES_ENABLED)\nlist(JOIN obs_module_list \"|\" SAFE_MODULES)\ntarget_compile_definitions(obs-studio PRIVATE \"SAFE_MODULES=\\\"${SAFE_MODULES}\\\"\")\n\nget_target_property(target_sources obs-studio SOURCES)\nset(target_cpp_sources ${target_sources})\nset(target_hpp_sources ${target_sources})\nset(target_qt_sources ${target_sources})\nlist(FILTER target_cpp_sources INCLUDE REGEX \".+\\\\.(cpp|mm|c|m)\")\nlist(SORT target_cpp_sources COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING)\nlist(FILTER target_hpp_sources INCLUDE REGEX \".+\\\\.(hpp|h)\")\nlist(SORT target_hpp_sources COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING)\nlist(FILTER target_qt_sources INCLUDE REGEX \".+\\\\.(ui|qrc)\")\nlist(SORT target_qt_sources COMPARE NATURAL CASE SENSITIVE ORDER ASCENDING)\nsource_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"Source Files\" FILES ${target_cpp_sources})\nsource_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"Header Files\" FILES ${target_hpp_sources})\nsource_group(TREE \"${CMAKE_CURRENT_SOURCE_DIR}\" PREFIX \"Qt Files\" FILES ${target_qt_sources})\n\nset_target_properties_obs(obs-studio PROPERTIES FOLDER frontend OUTPUT_NAME \"$<IF:$<PLATFORM_ID:Windows>,obs64,obs>\")\n"
  },
  {
    "path": "frontend/OBSApp.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSApp.hpp\"\n\n#include <components/Multiview.hpp>\n#include <dialogs/LogUploadDialog.hpp>\n#include <plugin-manager/PluginManager.hpp>\n#include <utility/CrashHandler.hpp>\n#include <utility/OBSEventFilter.hpp>\n#include <utility/OBSProxyStyle.hpp>\n#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)\n#include <utility/models/branches.hpp>\n#endif\n#include <widgets/OBSBasic.hpp>\n\n#if !defined(_WIN32) && !defined(__APPLE__)\n#include <obs-nix-platform.h>\n#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)\n#include <qpa/qplatformnativeinterface.h>\n#endif\n#endif\n#include <qt-wrappers.hpp>\n\n#include <QCheckBox>\n#include <QDesktopServices>\n#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)\n#include <QFile>\n#endif\n\n#include <QSessionManager>\n#ifndef _WIN32\n#include <QSocketNotifier>\n#endif\n\n#include <chrono>\n\n#ifdef _WIN32\n#include <sstream>\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#else\n#include <unistd.h>\n#include <sys/socket.h>\n#endif\n\n#include \"moc_OBSApp.cpp\"\n\nusing namespace std;\n\nstring currentLogFile;\nstring lastLogFile;\nstring lastCrashLogFile;\n\nextern bool portable_mode;\nextern bool safe_mode;\nextern bool multi;\nextern bool disable_3p_plugins;\nextern bool opt_disable_updater;\nextern bool opt_disable_missing_files_check;\nextern string opt_starting_collection;\nextern string opt_starting_profile;\n\n// GPU hint exports for AMD/NVIDIA laptops\n#ifdef _MSC_VER\nextern \"C\" __declspec(dllexport) DWORD NvOptimusEnablement = 1;\nextern \"C\" __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;\n#endif\n\nnamespace {\n\ntypedef struct UncleanLaunchAction {\n\tbool useSafeMode = false;\n\tbool sendCrashReport = false;\n} UncleanLaunchAction;\n\nUncleanLaunchAction handleUncleanShutdown(bool enableCrashUpload)\n{\n\tUncleanLaunchAction launchAction;\n\n\tblog(LOG_WARNING, \"Crash or unclean shutdown detected\");\n\n\tQMessageBox crashWarning;\n\n\tcrashWarning.setIcon(QMessageBox::Warning);\n#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)\n\tcrashWarning.setOption(QMessageBox::Option::DontUseNativeDialog);\n#endif\n\tcrashWarning.setWindowTitle(QTStr(\"CrashHandling.Dialog.Title\"));\n\tcrashWarning.setText(QTStr(\"CrashHandling.Labels.Text\"));\n\n\tif (enableCrashUpload) {\n\t\tcrashWarning.setInformativeText(QTStr(\"CrashHandling.Labels.PrivacyNotice\"));\n\n\t\tQCheckBox *sendCrashReportCheckbox = new QCheckBox(QTStr(\"CrashHandling.Checkbox.SendReport\"));\n\t\tcrashWarning.setCheckBox(sendCrashReportCheckbox);\n\t}\n\n\tQPushButton *launchSafeButton =\n\t\tcrashWarning.addButton(QTStr(\"CrashHandling.Buttons.LaunchSafe\"), QMessageBox::AcceptRole);\n\tQPushButton *launchNormalButton =\n\t\tcrashWarning.addButton(QTStr(\"CrashHandling.Buttons.LaunchNormal\"), QMessageBox::RejectRole);\n\n\tcrashWarning.setDefaultButton(launchNormalButton);\n\n\tcrashWarning.exec();\n\n\tbool useSafeMode = crashWarning.clickedButton() == launchSafeButton;\n\n\tif (useSafeMode) {\n\t\tlaunchAction.useSafeMode = true;\n\n\t\tblog(LOG_INFO, \"[Safe Mode] Safe mode launch selected, loading third-party plugins is disabled\");\n\t} else {\n\t\tblog(LOG_WARNING, \"[Safe Mode] Normal launch selected, loading third-party plugins is enabled\");\n\t}\n\n\tbool sendCrashReport = (enableCrashUpload) ? crashWarning.checkBox()->isChecked() : false;\n\n\tif (sendCrashReport) {\n\t\tlaunchAction.sendCrashReport = true;\n\n\t\tblog(LOG_INFO, \"User selected to send crash report\");\n\t}\n\n\treturn launchAction;\n}\n\nQAccessibleInterface *alignmentSelectorFactory(const QString &classname, QObject *object)\n{\n\tif (classname == QLatin1String(\"AlignmentSelector\")) {\n\t\tif (auto *w = qobject_cast<AlignmentSelector *>(object))\n\t\t\treturn new AccessibleAlignmentSelector(w);\n\t}\n\treturn nullptr;\n}\n} // namespace\n\nQObject *CreateShortcutFilter()\n{\n\treturn new OBSEventFilter([](QObject *obj, QEvent *event) {\n\t\tauto mouse_event = [](QMouseEvent &event) {\n\t\t\tif (!App()->HotkeysEnabledInFocus() && event.button() != Qt::LeftButton)\n\t\t\t\treturn true;\n\n\t\t\tobs_key_combination_t hotkey = {0, OBS_KEY_NONE};\n\t\t\tbool pressed = event.type() == QEvent::MouseButtonPress;\n\n\t\t\tswitch (event.button()) {\n\t\t\tcase Qt::NoButton:\n\t\t\tcase Qt::LeftButton:\n\t\t\tcase Qt::RightButton:\n\t\t\tcase Qt::AllButtons:\n\t\t\tcase Qt::MouseButtonMask:\n\t\t\t\treturn false;\n\n\t\t\tcase Qt::MiddleButton:\n\t\t\t\thotkey.key = OBS_KEY_MOUSE3;\n\t\t\t\tbreak;\n\n#define MAP_BUTTON(i, j)                       \\\n\tcase Qt::ExtraButton##i:               \\\n\t\thotkey.key = OBS_KEY_MOUSE##j; \\\n\t\tbreak;\n\t\t\t\tMAP_BUTTON(1, 4);\n\t\t\t\tMAP_BUTTON(2, 5);\n\t\t\t\tMAP_BUTTON(3, 6);\n\t\t\t\tMAP_BUTTON(4, 7);\n\t\t\t\tMAP_BUTTON(5, 8);\n\t\t\t\tMAP_BUTTON(6, 9);\n\t\t\t\tMAP_BUTTON(7, 10);\n\t\t\t\tMAP_BUTTON(8, 11);\n\t\t\t\tMAP_BUTTON(9, 12);\n\t\t\t\tMAP_BUTTON(10, 13);\n\t\t\t\tMAP_BUTTON(11, 14);\n\t\t\t\tMAP_BUTTON(12, 15);\n\t\t\t\tMAP_BUTTON(13, 16);\n\t\t\t\tMAP_BUTTON(14, 17);\n\t\t\t\tMAP_BUTTON(15, 18);\n\t\t\t\tMAP_BUTTON(16, 19);\n\t\t\t\tMAP_BUTTON(17, 20);\n\t\t\t\tMAP_BUTTON(18, 21);\n\t\t\t\tMAP_BUTTON(19, 22);\n\t\t\t\tMAP_BUTTON(20, 23);\n\t\t\t\tMAP_BUTTON(21, 24);\n\t\t\t\tMAP_BUTTON(22, 25);\n\t\t\t\tMAP_BUTTON(23, 26);\n\t\t\t\tMAP_BUTTON(24, 27);\n#undef MAP_BUTTON\n\t\t\t}\n\n\t\t\thotkey.modifiers = TranslateQtKeyboardEventModifiers(event.modifiers());\n\n\t\t\tobs_hotkey_inject_event(hotkey, pressed);\n\t\t\treturn true;\n\t\t};\n\n\t\tauto key_event = [&](QKeyEvent *event) {\n\t\t\tint key = event->key();\n\t\t\tbool enabledInFocus = App()->HotkeysEnabledInFocus();\n\n\t\t\tif (key != Qt::Key_Enter && key != Qt::Key_Escape && key != Qt::Key_Return && !enabledInFocus)\n\t\t\t\treturn true;\n\n\t\t\tQDialog *dialog = qobject_cast<QDialog *>(obj);\n\n\t\t\tobs_key_combination_t hotkey = {0, OBS_KEY_NONE};\n\t\t\tbool pressed = event->type() == QEvent::KeyPress;\n\n\t\t\tswitch (key) {\n\t\t\tcase Qt::Key_Shift:\n\t\t\tcase Qt::Key_Control:\n\t\t\tcase Qt::Key_Alt:\n\t\t\tcase Qt::Key_Meta:\n\t\t\t\tbreak;\n\n#ifdef __APPLE__\n\t\t\tcase Qt::Key_CapsLock:\n\t\t\t\t// kVK_CapsLock == 57\n\t\t\t\thotkey.key = obs_key_from_virtual_key(57);\n\t\t\t\tpressed = true;\n\t\t\t\tbreak;\n#endif\n\n\t\t\tcase Qt::Key_Enter:\n\t\t\tcase Qt::Key_Escape:\n\t\t\tcase Qt::Key_Return:\n\t\t\t\tif (dialog && pressed)\n\t\t\t\t\treturn false;\n\t\t\t\tif (!enabledInFocus)\n\t\t\t\t\treturn true;\n\t\t\t\t/* Falls through. */\n\t\t\tdefault:\n\t\t\t\thotkey.key = obs_key_from_virtual_key(event->nativeVirtualKey());\n\t\t\t}\n\n\t\t\tif (event->isAutoRepeat())\n\t\t\t\treturn true;\n\n\t\t\thotkey.modifiers = TranslateQtKeyboardEventModifiers(event->modifiers());\n\n\t\t\tobs_hotkey_inject_event(hotkey, pressed);\n\t\t\treturn true;\n\t\t};\n\n\t\tswitch (event->type()) {\n\t\tcase QEvent::MouseButtonPress:\n\t\tcase QEvent::MouseButtonRelease:\n\t\t\treturn mouse_event(*static_cast<QMouseEvent *>(event));\n\n\t\t/*case QEvent::MouseButtonDblClick:\n\t\tcase QEvent::Wheel:*/\n\t\tcase QEvent::KeyPress:\n\t\tcase QEvent::KeyRelease:\n\t\t\treturn key_event(static_cast<QKeyEvent *>(event));\n\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t});\n}\n\nstring CurrentDateTimeString()\n{\n\ttime_t now = time(0);\n\tstruct tm tstruct;\n\tchar buf[80];\n\ttstruct = *localtime(&now);\n\tstrftime(buf, sizeof(buf), \"%Y-%m-%d, %X\", &tstruct);\n\treturn buf;\n}\n\n#define DEFAULT_LANG \"en-US\"\n\n#ifndef _WIN32\nstd::array<int, 2> OBSApp::sigIntFileDescriptor{0, 0};\nstd::array<int, 2> OBSApp::sigTermFileDescriptor{0, 0};\nstd::array<int, 2> OBSApp::sigAbrtFileDescriptor{0, 0};\nstd::array<int, 2> OBSApp::sigQuitFileDescriptor{0, 0};\n#endif\n\nbool OBSApp::InitGlobalConfigDefaults()\n{\n\tconfig_set_default_uint(appConfig, \"General\", \"MaxLogs\", 10);\n\tconfig_set_default_int(appConfig, \"General\", \"InfoIncrement\", -1);\n\tconfig_set_default_string(appConfig, \"General\", \"ProcessPriority\", \"Normal\");\n\tconfig_set_default_bool(appConfig, \"General\", \"EnableAutoUpdates\", true);\n\n#if _WIN32\n\tconfig_set_default_string(appConfig, \"Video\", \"Renderer\", \"Direct3D 11\");\n#else\n#if defined(__APPLE__) && defined(__aarch64__)\n\t// TODO: Change this value to \"Metal\" once the renderer has reached production quality\n\tconfig_set_default_string(appConfig, \"Video\", \"Renderer\", \"OpenGL\");\n#else\n\tconfig_set_default_string(appConfig, \"Video\", \"Renderer\", \"OpenGL\");\n#endif\n#endif\n\n#ifdef _WIN32\n\tconfig_set_default_bool(appConfig, \"Audio\", \"DisableAudioDucking\", true);\n#endif\n\n#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)\n\tconfig_set_default_bool(appConfig, \"General\", \"BrowserHWAccel\", true);\n#endif\n\n#ifdef __APPLE__\n\tconfig_set_default_bool(appConfig, \"Video\", \"DisableOSXVSync\", true);\n\tconfig_set_default_bool(appConfig, \"Video\", \"ResetOSXVSyncOnExit\", true);\n#endif\n\n\treturn true;\n}\n\nbool OBSApp::InitGlobalLocationDefaults()\n{\n\tchar path[512];\n\n\tint len = GetAppConfigPath(path, sizeof(path), nullptr);\n\tif (len <= 0) {\n\t\tOBSErrorBox(NULL, \"Unable to get global configuration path.\");\n\t\treturn false;\n\t}\n\n\tconfig_set_default_string(appConfig, \"Locations\", \"Configuration\", path);\n\tconfig_set_default_string(appConfig, \"Locations\", \"SceneCollections\", path);\n\tconfig_set_default_string(appConfig, \"Locations\", \"Profiles\", path);\n\tconfig_set_default_string(appConfig, \"Locations\", \"PluginManagerSettings\", path);\n\n\treturn true;\n}\n\nvoid OBSApp::InitUserConfigDefaults()\n{\n\tconfig_set_default_bool(userConfig, \"General\", \"ConfirmOnExit\", true);\n\n\tconfig_set_default_string(userConfig, \"General\", \"HotkeyFocusType\", \"NeverDisableHotkeys\");\n\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"PreviewEnabled\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"PreviewProgramMode\", false);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"SceneDuplicationMode\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"SwapScenesMode\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"SnappingEnabled\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"ScreenSnapping\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"SourceSnapping\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"CenterSnapping\", false);\n\tconfig_set_default_double(userConfig, \"BasicWindow\", \"SnapDistance\", 10.0);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"SpacingHelpersEnabled\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"RecordWhenStreaming\", false);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"KeepRecordingWhenStreamStops\", false);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"SysTrayEnabled\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"SysTrayWhenStarted\", false);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"SaveProjectors\", false);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"ShowTransitions\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"ShowListboxToolbars\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"ShowStatusBar\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"ShowSourceIcons\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"ShowContextToolbars\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"StudioModeLabels\", true);\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"SideDocks\", true);\n\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"VerticalVolumeControl\", true);\n\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"MultiviewMouseSwitch\", true);\n\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"MultiviewDrawNames\", true);\n\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"MultiviewDrawAreas\", true);\n\n\tconfig_set_default_bool(userConfig, \"BasicWindow\", \"MediaControlsCountdownTimer\", true);\n\n\tconfig_set_default_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerShowInactive\", false);\n\tconfig_set_default_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerKeepInactiveLast\", false);\n\tconfig_set_default_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerShowHidden\", false);\n\tconfig_set_default_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerKeepHiddenLast\", false);\n\n\tconfig_set_default_int(userConfig, \"Appearance\", \"FontScale\", 10);\n\tconfig_set_default_int(userConfig, \"Appearance\", \"Density\", 1);\n}\n\nstatic bool do_mkdir(const char *path)\n{\n\tif (os_mkdirs(path) == MKDIR_ERROR) {\n\t\tOBSErrorBox(NULL, \"Failed to create directory %s\", path);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstatic bool MakeUserDirs()\n{\n\tchar path[512];\n\n\tif (GetAppConfigPath(path, sizeof(path), \"obs-studio/basic\") <= 0)\n\t\treturn false;\n\tif (!do_mkdir(path))\n\t\treturn false;\n\n\tif (GetAppConfigPath(path, sizeof(path), \"obs-studio/logs\") <= 0)\n\t\treturn false;\n\tif (!do_mkdir(path))\n\t\treturn false;\n\n\tif (GetAppConfigPath(path, sizeof(path), \"obs-studio/profiler_data\") <= 0)\n\t\treturn false;\n\tif (!do_mkdir(path))\n\t\treturn false;\n\n#ifdef _WIN32\n\tif (GetAppConfigPath(path, sizeof(path), \"obs-studio/crashes\") <= 0)\n\t\treturn false;\n\tif (!do_mkdir(path))\n\t\treturn false;\n#endif\n\n#ifdef WHATSNEW_ENABLED\n\tif (GetAppConfigPath(path, sizeof(path), \"obs-studio/updates\") <= 0)\n\t\treturn false;\n\tif (!do_mkdir(path))\n\t\treturn false;\n#endif\n\n\tif (GetAppConfigPath(path, sizeof(path), \"obs-studio/plugin_config\") <= 0)\n\t\treturn false;\n\tif (!do_mkdir(path))\n\t\treturn false;\n\n\treturn true;\n}\n\nconstexpr std::string_view OBSProfileSubDirectory = \"obs-studio/basic/profiles\";\nconstexpr std::string_view OBSScenesSubDirectory = \"obs-studio/basic/scenes\";\nconstexpr std::string_view OBSPluginManagerSubDirectory = \"obs-studio/plugin_manager\";\n\nstatic bool MakeUserProfileDirs()\n{\n\tconst std::filesystem::path userProfilePath =\n\t\tApp()->userProfilesLocation / std::filesystem::u8path(OBSProfileSubDirectory);\n\tconst std::filesystem::path userScenesPath =\n\t\tApp()->userScenesLocation / std::filesystem::u8path(OBSScenesSubDirectory);\n\tconst std::filesystem::path userPluginManagerPath =\n\t\tApp()->userPluginManagerSettingsLocation / std::filesystem::u8path(OBSPluginManagerSubDirectory);\n\n\tif (!std::filesystem::exists(userProfilePath)) {\n\t\ttry {\n\t\t\tstd::filesystem::create_directories(userProfilePath);\n\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\tblog(LOG_ERROR, \"Failed to create user profile directory '%s'\\n%s\",\n\t\t\t     userProfilePath.u8string().c_str(), error.what());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!std::filesystem::exists(userScenesPath)) {\n\t\ttry {\n\t\t\tstd::filesystem::create_directories(userScenesPath);\n\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\tblog(LOG_ERROR, \"Failed to create user scene collection directory '%s'\\n%s\",\n\t\t\t     userScenesPath.u8string().c_str(), error.what());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!std::filesystem::exists(userPluginManagerPath)) {\n\t\ttry {\n\t\t\tstd::filesystem::create_directories(userPluginManagerPath);\n\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\tblog(LOG_ERROR, \"Failed to create user plugin manager directory '%s'\\n%s\",\n\t\t\t     userPluginManagerPath.u8string().c_str(), error.what());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool OBSApp::UpdatePre22MultiviewLayout(const char *layout)\n{\n\tif (!layout)\n\t\treturn false;\n\n\tif (astrcmpi(layout, \"horizontaltop\") == 0) {\n\t\tconfig_set_int(userConfig, \"BasicWindow\", \"MultiviewLayout\",\n\t\t\t       static_cast<int>(MultiviewLayout::HORIZONTAL_TOP_8_SCENES));\n\t\treturn true;\n\t}\n\n\tif (astrcmpi(layout, \"horizontalbottom\") == 0) {\n\t\tconfig_set_int(userConfig, \"BasicWindow\", \"MultiviewLayout\",\n\t\t\t       static_cast<int>(MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES));\n\t\treturn true;\n\t}\n\n\tif (astrcmpi(layout, \"verticalleft\") == 0) {\n\t\tconfig_set_int(userConfig, \"BasicWindow\", \"MultiviewLayout\",\n\t\t\t       static_cast<int>(MultiviewLayout::VERTICAL_LEFT_8_SCENES));\n\t\treturn true;\n\t}\n\n\tif (astrcmpi(layout, \"verticalright\") == 0) {\n\t\tconfig_set_int(userConfig, \"BasicWindow\", \"MultiviewLayout\",\n\t\t\t       static_cast<int>(MultiviewLayout::VERTICAL_RIGHT_8_SCENES));\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool OBSApp::InitGlobalConfig()\n{\n\tchar path[512];\n\n\tint len = GetAppConfigPath(path, sizeof(path), \"obs-studio/global.ini\");\n\tif (len <= 0) {\n\t\treturn false;\n\t}\n\n\tint errorcode = appConfig.Open(path, CONFIG_OPEN_ALWAYS);\n\tif (errorcode != CONFIG_SUCCESS) {\n\t\tOBSErrorBox(NULL, \"Failed to open global.ini: %d\", errorcode);\n\t\treturn false;\n\t}\n\n\tuint32_t lastVersion = config_get_int(appConfig, \"General\", \"LastVersion\");\n\n\tif (lastVersion && lastVersion < MAKE_SEMANTIC_VERSION(31, 0, 0)) {\n\t\tbool migratedUserSettings = config_get_bool(appConfig, \"General\", \"Pre31Migrated\");\n\n\t\tif (!migratedUserSettings) {\n\t\t\tbool migrated = MigrateGlobalSettings();\n\n\t\t\tconfig_set_bool(appConfig, \"General\", \"Pre31Migrated\", migrated);\n\t\t\tconfig_save_safe(appConfig, \"tmp\", nullptr);\n\t\t}\n\t}\n\n\tInitGlobalConfigDefaults();\n\tInitGlobalLocationDefaults();\n\n\tstd::filesystem::path defaultUserConfigLocation =\n\t\tstd::filesystem::u8path(config_get_default_string(appConfig, \"Locations\", \"Configuration\"));\n\tstd::filesystem::path defaultUserScenesLocation =\n\t\tstd::filesystem::u8path(config_get_default_string(appConfig, \"Locations\", \"SceneCollections\"));\n\tstd::filesystem::path defaultUserProfilesLocation =\n\t\tstd::filesystem::u8path(config_get_default_string(appConfig, \"Locations\", \"Profiles\"));\n\tstd::filesystem::path defaultPluginManagerLocation =\n\t\tstd::filesystem::u8path(config_get_default_string(appConfig, \"Locations\", \"PluginManagerSettings\"));\n\n\tif (IsPortableMode()) {\n\t\tuserConfigLocation = std::move(defaultUserConfigLocation);\n\t\tuserScenesLocation = std::move(defaultUserScenesLocation);\n\t\tuserProfilesLocation = std::move(defaultUserProfilesLocation);\n\t\tuserPluginManagerSettingsLocation = std::move(defaultPluginManagerLocation);\n\t} else {\n\t\tstd::filesystem::path currentUserConfigLocation =\n\t\t\tstd::filesystem::u8path(config_get_string(appConfig, \"Locations\", \"Configuration\"));\n\t\tstd::filesystem::path currentUserScenesLocation =\n\t\t\tstd::filesystem::u8path(config_get_string(appConfig, \"Locations\", \"SceneCollections\"));\n\t\tstd::filesystem::path currentUserProfilesLocation =\n\t\t\tstd::filesystem::u8path(config_get_string(appConfig, \"Locations\", \"Profiles\"));\n\t\tstd::filesystem::path currentUserPluginManagerLocation =\n\t\t\tstd::filesystem::u8path(config_get_string(appConfig, \"Locations\", \"PluginManagerSettings\"));\n\n\t\tuserConfigLocation = (std::filesystem::exists(currentUserConfigLocation))\n\t\t\t\t\t     ? std::move(currentUserConfigLocation)\n\t\t\t\t\t     : std::move(defaultUserConfigLocation);\n\t\tuserScenesLocation = (std::filesystem::exists(currentUserScenesLocation))\n\t\t\t\t\t     ? std::move(currentUserScenesLocation)\n\t\t\t\t\t     : std::move(defaultUserScenesLocation);\n\t\tuserProfilesLocation = (std::filesystem::exists(currentUserProfilesLocation))\n\t\t\t\t\t       ? std::move(currentUserProfilesLocation)\n\t\t\t\t\t       : std::move(defaultUserProfilesLocation);\n\t\tuserPluginManagerSettingsLocation = (std::filesystem::exists(currentUserPluginManagerLocation))\n\t\t\t\t\t\t\t    ? std::move(currentUserPluginManagerLocation)\n\t\t\t\t\t\t\t    : std::move(defaultPluginManagerLocation);\n\t}\n\n\tbool userConfigResult = InitUserConfig(userConfigLocation, lastVersion);\n\n\treturn userConfigResult;\n}\n\nbool OBSApp::InitUserConfig(std::filesystem::path &userConfigLocation, uint32_t lastVersion)\n{\n\tconst std::string userConfigFile = userConfigLocation.u8string() + \"/obs-studio/user.ini\";\n\n\tint errorCode = userConfig.Open(userConfigFile.c_str(), CONFIG_OPEN_ALWAYS);\n\n\tif (errorCode != CONFIG_SUCCESS) {\n\t\tOBSErrorBox(nullptr, \"Failed to open user.ini: %d\", errorCode);\n\t\treturn false;\n\t}\n\n\tMigrateLegacySettings(lastVersion);\n\tInitUserConfigDefaults();\n\n\treturn true;\n}\n\nvoid OBSApp::MigrateLegacySettings(const uint32_t lastVersion)\n{\n\tbool hasChanges = false;\n\n\tconst uint32_t v19 = MAKE_SEMANTIC_VERSION(19, 0, 0);\n\tconst uint32_t v21 = MAKE_SEMANTIC_VERSION(21, 0, 0);\n\tconst uint32_t v23 = MAKE_SEMANTIC_VERSION(23, 0, 0);\n\tconst uint32_t v24 = MAKE_SEMANTIC_VERSION(24, 0, 0);\n\tconst uint32_t v24_1 = MAKE_SEMANTIC_VERSION(24, 1, 0);\n\n\tconst map<uint32_t, string> defaultsMap{\n\t\t{{v19, \"Pre19Defaults\"}, {v21, \"Pre21Defaults\"}, {v23, \"Pre23Defaults\"}, {v24_1, \"Pre24.1Defaults\"}}};\n\n\tfor (auto &[version, configKey] : defaultsMap) {\n\t\tif (!config_has_user_value(userConfig, \"General\", configKey.c_str())) {\n\t\t\tbool useOldDefaults = lastVersion && lastVersion < version;\n\t\t\tconfig_set_bool(userConfig, \"General\", configKey.c_str(), useOldDefaults);\n\n\t\t\thasChanges = true;\n\t\t}\n\t}\n\n\tif (config_has_user_value(userConfig, \"BasicWindow\", \"MultiviewLayout\")) {\n\t\tconst char *layout = config_get_string(userConfig, \"BasicWindow\", \"MultiviewLayout\");\n\n\t\tbool layoutUpdated = UpdatePre22MultiviewLayout(layout);\n\n\t\thasChanges = hasChanges | layoutUpdated;\n\t}\n\n\tif (lastVersion && lastVersion < v24) {\n\t\tbool disableHotkeysInFocus = config_get_bool(userConfig, \"General\", \"DisableHotkeysInFocus\");\n\n\t\tif (disableHotkeysInFocus) {\n\t\t\tconfig_set_string(userConfig, \"General\", \"HotkeyFocusType\", \"DisableHotkeysInFocus\");\n\t\t}\n\n\t\thasChanges = true;\n\t}\n\n\tif (hasChanges) {\n\t\tuserConfig.SaveSafe(\"tmp\");\n\t}\n}\n\nstatic constexpr string_view OBSGlobalIniPath = \"/obs-studio/global.ini\";\nstatic constexpr string_view OBSUserIniPath = \"/obs-studio/user.ini\";\n\nbool OBSApp::MigrateGlobalSettings()\n{\n\tchar path[512];\n\n\tint len = GetAppConfigPath(path, sizeof(path), nullptr);\n\tif (len <= 0) {\n\t\tOBSErrorBox(nullptr, \"Unable to get global configuration path.\");\n\t\treturn false;\n\t}\n\n\tstd::string legacyConfigFileString;\n\tlegacyConfigFileString.reserve(strlen(path) + OBSGlobalIniPath.size());\n\tlegacyConfigFileString.append(path).append(OBSGlobalIniPath);\n\n\tconst std::filesystem::path legacyGlobalConfigFile = std::filesystem::u8path(legacyConfigFileString);\n\n\tstd::string configFileString;\n\tconfigFileString.reserve(strlen(path) + OBSUserIniPath.size());\n\tconfigFileString.append(path).append(OBSUserIniPath);\n\n\tconst std::filesystem::path userConfigFile = std::filesystem::u8path(configFileString);\n\n\tif (std::filesystem::exists(userConfigFile)) {\n\t\tOBSErrorBox(nullptr,\n\t\t\t    \"Unable to migrate global configuration - user configuration file already exists.\");\n\t\treturn false;\n\t}\n\n\ttry {\n\t\tstd::filesystem::copy(legacyGlobalConfigFile, userConfigFile);\n\t} catch (const std::filesystem::filesystem_error &) {\n\t\tOBSErrorBox(nullptr, \"Unable to migrate global configuration - copy failed.\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool OBSApp::InitLocale()\n{\n\tProfileScope(\"OBSApp::InitLocale\");\n\n\tconst char *lang = config_get_string(userConfig, \"General\", \"Language\");\n\tbool userLocale = config_has_user_value(userConfig, \"General\", \"Language\");\n\tif (!userLocale || !lang || lang[0] == '\\0')\n\t\tlang = DEFAULT_LANG;\n\n\tlocale = lang;\n\n\t// set basic default application locale\n\tif (!locale.empty())\n\t\tQLocale::setDefault(QLocale(QString::fromStdString(locale).replace('-', '_')));\n\n\tstring englishPath;\n\tif (!GetDataFilePath(\"locale/\" DEFAULT_LANG \".ini\", englishPath)) {\n\t\tOBSErrorBox(NULL, \"Failed to find locale/\" DEFAULT_LANG \".ini\");\n\t\treturn false;\n\t}\n\n\ttextLookup = text_lookup_create(englishPath.c_str());\n\tif (!textLookup) {\n\t\tOBSErrorBox(NULL, \"Failed to create locale from file '%s'\", englishPath.c_str());\n\t\treturn false;\n\t}\n\n\tbool defaultLang = astrcmpi(lang, DEFAULT_LANG) == 0;\n\n\tif (userLocale && defaultLang)\n\t\treturn true;\n\n\tif (!userLocale && defaultLang) {\n\t\tfor (auto &locale_ : GetPreferredLocales()) {\n\t\t\tif (locale_ == lang)\n\t\t\t\treturn true;\n\n\t\t\tstringstream file;\n\t\t\tfile << \"locale/\" << locale_ << \".ini\";\n\n\t\t\tstring path;\n\t\t\tif (!GetDataFilePath(file.str().c_str(), path))\n\t\t\t\tcontinue;\n\n\t\t\tif (!text_lookup_add(textLookup, path.c_str()))\n\t\t\t\tcontinue;\n\n\t\t\tblog(LOG_INFO, \"Using preferred locale '%s'\", locale_.c_str());\n\t\t\tlocale = locale_;\n\n\t\t\t// set application default locale to the new chosen one\n\t\t\tif (!locale.empty())\n\t\t\t\tQLocale::setDefault(QLocale(QString::fromStdString(locale).replace('-', '_')));\n\n\t\t\treturn true;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tstringstream file;\n\tfile << \"locale/\" << lang << \".ini\";\n\n\tstring path;\n\tif (GetDataFilePath(file.str().c_str(), path)) {\n\t\tif (!text_lookup_add(textLookup, path.c_str()))\n\t\t\tblog(LOG_ERROR, \"Failed to add locale file '%s'\", path.c_str());\n\t} else {\n\t\tblog(LOG_ERROR, \"Could not find locale file '%s'\", file.str().c_str());\n\t}\n\n\treturn true;\n}\n\n#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)\nvoid ParseBranchesJson(const std::string &jsonString, vector<UpdateBranch> &out, std::string &error)\n{\n\tJsonBranches branches;\n\n\ttry {\n\t\tnlohmann::json json = nlohmann::json::parse(jsonString);\n\t\tbranches = json.get<JsonBranches>();\n\t} catch (nlohmann::json::exception &e) {\n\t\terror = e.what();\n\t\treturn;\n\t}\n\n\tfor (const JsonBranch &json_branch : branches) {\n#ifdef _WIN32\n\t\tif (!json_branch.windows)\n\t\t\tcontinue;\n#elif defined(__APPLE__)\n\t\tif (!json_branch.macos)\n\t\t\tcontinue;\n#endif\n\n\t\tUpdateBranch branch = {\n\t\t\tQString::fromStdString(json_branch.name),\n\t\t\tQString::fromStdString(json_branch.display_name),\n\t\t\tQString::fromStdString(json_branch.description),\n\t\t\tjson_branch.enabled,\n\t\t\tjson_branch.visible,\n\t\t};\n\t\tout.push_back(branch);\n\t}\n}\n\nbool LoadBranchesFile(vector<UpdateBranch> &out)\n{\n\tstring error;\n\tstring branchesText;\n\n\tBPtr<char> branchesFilePath = GetAppConfigPathPtr(\"obs-studio/updates/branches.json\");\n\n\tQFile branchesFile(branchesFilePath.Get());\n\tif (!branchesFile.open(QIODevice::ReadOnly)) {\n\t\terror = \"Opening file failed.\";\n\t\tgoto fail;\n\t}\n\n\tbranchesText = branchesFile.readAll().toStdString();\n\tif (branchesText.empty()) {\n\t\terror = \"File empty.\";\n\t\tgoto fail;\n\t}\n\n\tParseBranchesJson(branchesText, out, error);\n\tif (error.empty())\n\t\treturn !out.empty();\n\nfail:\n\tblog(LOG_WARNING, \"Loading branches from file failed: %s\", error.c_str());\n\treturn false;\n}\n#endif\n\nvoid OBSApp::SetBranchData(const string &data)\n{\n#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)\n\tstring error;\n\tvector<UpdateBranch> result;\n\n\tParseBranchesJson(data, result, error);\n\n\tif (!error.empty()) {\n\t\tblog(LOG_WARNING, \"Reading branches JSON response failed: %s\", error.c_str());\n\t\treturn;\n\t}\n\n\tif (!result.empty())\n\t\tupdateBranches = result;\n\n\tbranches_loaded = true;\n#else\n\tUNUSED_PARAMETER(data);\n#endif\n}\n\nstd::vector<UpdateBranch> OBSApp::GetBranches()\n{\n\tvector<UpdateBranch> out;\n\t/* Always ensure the default branch exists */\n\tout.push_back(UpdateBranch{\"stable\", \"\", \"\", true, true});\n\n#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)\n\tif (!branches_loaded) {\n\t\tvector<UpdateBranch> result;\n\t\tif (LoadBranchesFile(result))\n\t\t\tupdateBranches = result;\n\n\t\tbranches_loaded = true;\n\t}\n#endif\n\n\t/* Copy additional branches to result (if any) */\n\tif (!updateBranches.empty())\n\t\tout.insert(out.end(), updateBranches.begin(), updateBranches.end());\n\n\treturn out;\n}\n\nOBSApp::OBSApp(int &argc, char **argv, profiler_name_store_t *store)\n\t: QApplication(argc, argv),\n\t  profilerNameStore(store),\n\t  appLaunchUUID_(QUuid::createUuid())\n{\n\tinstallNativeEventFilter(new OBS::NativeEventFilter);\n\n\t/* fix float handling */\n#if defined(Q_OS_UNIX)\n\tif (!setlocale(LC_NUMERIC, \"C\"))\n\t\tblog(LOG_WARNING, \"Failed to set LC_NUMERIC to C locale\");\n#endif\n\n#ifndef _WIN32\n\t// Add POSIX signal handlers:\n\t// * SIGINT\n\t// * SIGTERM\n\t// * SIGABRT\n\t// * SIGQUIT\n\n\tusing SignalCallback = decltype(&OBSApp::processSigInt);\n\n\tauto connectSignal = [this](std::array<int, 2> &fileDescriptor, QPointer<QSocketNotifier> &notifier,\n\t\t\t\t    SignalCallback callback) -> void {\n\t\tsocketpair(AF_UNIX, SOCK_STREAM, 0, fileDescriptor.data());\n\t\tnotifier = new QSocketNotifier(fileDescriptor[1], QSocketNotifier::Read, this);\n\t\tconnect(notifier, &QSocketNotifier::activated, this, callback);\n\t};\n\n\tconnectSignal(sigIntFileDescriptor, sigIntNotifier, &OBSApp::processSigInt);\n\tconnectSignal(sigTermFileDescriptor, sigTermNotifier, &OBSApp::processSigTerm);\n\tconnectSignal(sigAbrtFileDescriptor, sigAbrtNotifier, &OBSApp::processSigAbrt);\n\tconnectSignal(sigQuitFileDescriptor, sigQuitNotifier, &OBSApp::processSigQuit);\n#endif\n\tconnect(qApp, &QGuiApplication::commitDataRequest, this, &OBSApp::commitData, Qt::DirectConnection);\n\n\tif (multi) {\n\t\tcrashHandler_ = std::make_unique<OBS::CrashHandler>();\n\t} else {\n\t\tcrashHandler_ = std::make_unique<OBS::CrashHandler>(appLaunchUUID_);\n\t}\n\n\tsleepInhibitor = os_inhibit_sleep_create(\"OBS Video/audio\");\n\n#ifndef __APPLE__\n\tsetWindowIcon(QIcon::fromTheme(\"obs\", QIcon(\":/res/images/obs.png\")));\n#endif\n\n\tsetDesktopFileName(\"com.obsproject.Studio\");\n\tpluginManager_ = std::make_unique<OBS::PluginManager>();\n}\n\nOBSApp::~OBSApp()\n{\n\tif (libobs_initialized) {\n\t\tapplicationShutdown();\n\t}\n};\n\nstatic void move_basic_to_profiles(void)\n{\n\tchar path[512];\n\n\tif (GetAppConfigPath(path, 512, \"obs-studio/basic\") <= 0) {\n\t\treturn;\n\t}\n\n\tconst std::filesystem::path basicPath = std::filesystem::u8path(path);\n\n\tif (!std::filesystem::exists(basicPath)) {\n\t\treturn;\n\t}\n\n\tconst std::filesystem::path profilesPath =\n\t\tApp()->userProfilesLocation / std::filesystem::u8path(\"obs-studio/basic/profiles\");\n\n\tif (std::filesystem::exists(profilesPath)) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tstd::filesystem::create_directories(profilesPath);\n\t} catch (const std::filesystem::filesystem_error &error) {\n\t\tblog(LOG_ERROR, \"Failed to create profiles directory for migration from basic profile\\n%s\",\n\t\t     error.what());\n\t\treturn;\n\t}\n\n\tconst std::filesystem::path newProfilePath = profilesPath / std::filesystem::u8path(Str(\"Untitled\"));\n\n\tfor (auto &entry : std::filesystem::directory_iterator(basicPath)) {\n\t\tif (entry.is_directory()) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.path().filename().u8string() == \"scenes.json\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!std::filesystem::exists(newProfilePath)) {\n\t\t\ttry {\n\t\t\t\tstd::filesystem::create_directory(newProfilePath);\n\t\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\t\tblog(LOG_ERROR, \"Failed to create profile directory for 'Untitled'\\n%s\", error.what());\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tconst filesystem::path destinationFile = newProfilePath / entry.path().filename();\n\n\t\tconst auto copyOptions = std::filesystem::copy_options::overwrite_existing;\n\n\t\ttry {\n\t\t\tstd::filesystem::copy(entry.path(), destinationFile, copyOptions);\n\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\tblog(LOG_ERROR, \"Failed to copy basic profile file '%s' to new profile 'Untitled'\\n%s\",\n\t\t\t     entry.path().filename().u8string().c_str(), error.what());\n\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nstatic void move_basic_to_scene_collections(void)\n{\n\tchar path[512];\n\n\tif (GetAppConfigPath(path, 512, \"obs-studio/basic\") <= 0) {\n\t\treturn;\n\t}\n\n\tconst std::filesystem::path basicPath = std::filesystem::u8path(path);\n\n\tif (!std::filesystem::exists(basicPath)) {\n\t\treturn;\n\t}\n\n\tconst std::filesystem::path sceneCollectionPath =\n\t\tApp()->userScenesLocation / std::filesystem::u8path(\"obs-studio/basic/scenes\");\n\n\tif (std::filesystem::exists(sceneCollectionPath)) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tstd::filesystem::create_directories(sceneCollectionPath);\n\t} catch (const std::filesystem::filesystem_error &error) {\n\t\tblog(LOG_ERROR,\n\t\t     \"Failed to create scene collection directory for migration from basic scene collection\\n%s\",\n\t\t     error.what());\n\t\treturn;\n\t}\n\n\tconst std::filesystem::path sourceFile = basicPath / std::filesystem::u8path(\"scenes.json\");\n\tconst std::filesystem::path destinationFile =\n\t\t(sceneCollectionPath / std::filesystem::u8path(Str(\"Untitled\"))).replace_extension(\".json\");\n\n\ttry {\n\t\tstd::filesystem::rename(sourceFile, destinationFile);\n\t} catch (const std::filesystem::filesystem_error &error) {\n\t\tblog(LOG_ERROR, \"Failed to rename basic scene collection file:\\n%s\", error.what());\n\t\treturn;\n\t}\n}\n\nvoid OBSApp::AppInit()\n{\n\tProfileScope(\"OBSApp::AppInit\");\n\n\tQAccessible::installFactory(alignmentSelectorFactory);\n\n\tif (!MakeUserDirs())\n\t\tthrow \"Failed to create required user directories\";\n\tif (!InitGlobalConfig())\n\t\tthrow \"Failed to initialize global config\";\n\tif (!InitLocale())\n\t\tthrow \"Failed to load locale\";\n\tif (!InitTheme())\n\t\tthrow \"Failed to load theme\";\n\n\tconfig_set_default_string(userConfig, \"Basic\", \"Profile\", Str(\"Untitled\"));\n\tconfig_set_default_string(userConfig, \"Basic\", \"ProfileDir\", Str(\"Untitled\"));\n\tconfig_set_default_string(userConfig, \"Basic\", \"SceneCollection\", Str(\"Untitled\"));\n\tconfig_set_default_string(userConfig, \"Basic\", \"SceneCollectionFile\", Str(\"Untitled\"));\n\tconfig_set_default_bool(userConfig, \"Basic\", \"ConfigOnNewProfile\", true);\n\n\tconst std::string_view profileName{config_get_string(userConfig, \"Basic\", \"Profile\")};\n\n\tif (profileName.empty()) {\n\t\tconfig_set_string(userConfig, \"Basic\", \"Profile\", Str(\"Untitled\"));\n\t\tconfig_set_string(userConfig, \"Basic\", \"ProfileDir\", Str(\"Untitled\"));\n\t}\n\n\tconst std::string_view sceneCollectionName{config_get_string(userConfig, \"Basic\", \"SceneCollection\")};\n\n\tif (sceneCollectionName.empty()) {\n\t\tconfig_set_string(userConfig, \"Basic\", \"SceneCollection\", Str(\"Untitled\"));\n\t\tconfig_set_string(userConfig, \"Basic\", \"SceneCollectionFile\", Str(\"Untitled\"));\n\t}\n\n#ifdef _WIN32\n\tbool disableAudioDucking = config_get_bool(appConfig, \"Audio\", \"DisableAudioDucking\");\n\tif (disableAudioDucking)\n\t\tDisableAudioDucking(true);\n#endif\n\n#ifdef __APPLE__\n\tif (config_get_bool(appConfig, \"Video\", \"DisableOSXVSync\"))\n\t\tEnableOSXVSync(false);\n#endif\n\n\tUpdateHotkeyFocusSetting(false);\n\n\tmove_basic_to_profiles();\n\tmove_basic_to_scene_collections();\n\n\tif (!MakeUserProfileDirs())\n\t\tthrow \"Failed to create profile directories\";\n}\n\nvoid OBSApp::checkForUncleanShutdown()\n{\n\tbool hasUncleanShutdown = crashHandler_->hasUncleanShutdown();\n\tbool hasNewCrashLog = crashHandler_->hasNewCrashLog();\n\n\tif (hasUncleanShutdown) {\n\t\tUncleanLaunchAction launchAction = handleUncleanShutdown(hasNewCrashLog);\n\n\t\tsafe_mode = launchAction.useSafeMode;\n\n\t\tif (launchAction.sendCrashReport) {\n\t\t\tcrashHandler_->uploadLastCrashLog();\n\t\t}\n\t}\n}\n\nconst char *OBSApp::GetRenderModule() const\n{\n#if defined(_WIN32)\n\tconst char *renderer = config_get_string(appConfig, \"Video\", \"Renderer\");\n\n\treturn (astrcmpi(renderer, \"Direct3D 11\") == 0) ? DL_D3D11 : DL_OPENGL;\n#elif defined(__APPLE__) && defined(__aarch64__)\n\tconst char *renderer = config_get_string(appConfig, \"Video\", \"Renderer\");\n\n\treturn (astrcmpi(renderer, \"Metal\") == 0) ? DL_METAL : DL_OPENGL;\n#else\n\treturn DL_OPENGL;\n#endif\n}\n\nstatic bool StartupOBS(const char *locale, profiler_name_store_t *store)\n{\n\tchar path[512];\n\n\tif (GetAppConfigPath(path, sizeof(path), \"obs-studio/plugin_config\") <= 0)\n\t\treturn false;\n\n\treturn obs_startup(locale, path, store);\n}\n\ninline void OBSApp::ResetHotkeyState(bool inFocus)\n{\n\tobs_hotkey_enable_background_press((inFocus && enableHotkeysInFocus) || (!inFocus && enableHotkeysOutOfFocus));\n}\n\nvoid OBSApp::UpdateHotkeyFocusSetting(bool resetState)\n{\n\tenableHotkeysInFocus = true;\n\tenableHotkeysOutOfFocus = true;\n\n\tconst char *hotkeyFocusType = config_get_string(userConfig, \"General\", \"HotkeyFocusType\");\n\n\tif (astrcmpi(hotkeyFocusType, \"DisableHotkeysInFocus\") == 0) {\n\t\tenableHotkeysInFocus = false;\n\t} else if (astrcmpi(hotkeyFocusType, \"DisableHotkeysOutOfFocus\") == 0) {\n\t\tenableHotkeysOutOfFocus = false;\n\t}\n\n\tif (resetState)\n\t\tResetHotkeyState(applicationState() == Qt::ApplicationActive);\n}\n\nvoid OBSApp::DisableHotkeys()\n{\n\tenableHotkeysInFocus = false;\n\tenableHotkeysOutOfFocus = false;\n\tResetHotkeyState(applicationState() == Qt::ApplicationActive);\n}\n\nvoid OBSApp::Exec(VoidFunc func)\n{\n\tfunc();\n}\n\nstatic void ui_task_handler(obs_task_t task, void *param, bool wait)\n{\n\tauto doTask = [=]() {\n\t\t/* to get clang-format to behave */\n\t\ttask(param);\n\t};\n\tQMetaObject::invokeMethod(App(), \"Exec\", wait ? WaitConnection() : Qt::AutoConnection, Q_ARG(VoidFunc, doTask));\n}\n\nbool OBSApp::OBSInit()\n{\n\tProfileScope(\"OBSApp::OBSInit\");\n\n\tqRegisterMetaType<VoidFunc>(\"VoidFunc\");\n\n#if !defined(_WIN32) && !defined(__APPLE__)\n\tif (QApplication::platformName() == \"xcb\") {\n#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)\n\t\tauto native = qGuiApp->nativeInterface<QNativeInterface::QX11Application>();\n\n\t\tobs_set_nix_platform_display(native->display());\n#endif\n\n\t\tobs_set_nix_platform(OBS_NIX_PLATFORM_X11_EGL);\n\n\t\tblog(LOG_INFO, \"Using EGL/X11\");\n\t}\n\n#ifdef ENABLE_WAYLAND\n\tif (QApplication::platformName().contains(\"wayland\")) {\n#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)\n\t\tauto native = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();\n\n\t\tobs_set_nix_platform_display(native->display());\n#endif\n\n\t\tobs_set_nix_platform(OBS_NIX_PLATFORM_WAYLAND);\n\t\tsetAttribute(Qt::AA_DontCreateNativeWidgetSiblings);\n\n\t\tblog(LOG_INFO, \"Platform: Wayland\");\n\t}\n#endif\n\n#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0)\n\tQPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();\n\tobs_set_nix_platform_display(native->nativeResourceForIntegration(\"display\"));\n#endif\n#endif\n\n#ifdef __APPLE__\n\tsetAttribute(Qt::AA_DontCreateNativeWidgetSiblings);\n#endif\n\n\tif (!StartupOBS(locale.c_str(), GetProfilerNameStore()))\n\t\treturn false;\n\n\tlibobs_initialized = true;\n\n\tobs_set_ui_task_handler(ui_task_handler);\n\n#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)\n\tbool browserHWAccel = config_get_bool(appConfig, \"General\", \"BrowserHWAccel\");\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_bool(settings, \"BrowserHWAccel\", browserHWAccel);\n\tobs_apply_private_data(settings);\n\n\tblog(LOG_INFO, \"Current Date/Time: %s\", CurrentDateTimeString().c_str());\n\n\tblog(LOG_INFO, \"Browser Hardware Acceleration: %s\", browserHWAccel ? \"true\" : \"false\");\n#endif\n#ifdef _WIN32\n\tbool hideFromCapture = config_get_bool(userConfig, \"BasicWindow\", \"HideOBSWindowsFromCapture\");\n\tblog(LOG_INFO, \"Hide OBS windows from screen capture: %s\", hideFromCapture ? \"true\" : \"false\");\n#endif\n\n\tblog(LOG_INFO, \"Qt Version: %s (runtime), %s (compiled)\", qVersion(), QT_VERSION_STR);\n\tblog(LOG_INFO, \"Portable mode: %s\", portable_mode ? \"true\" : \"false\");\n\n\tif (safe_mode) {\n\t\tblog(LOG_WARNING, \"Safe Mode enabled.\");\n\t} else if (disable_3p_plugins) {\n\t\tblog(LOG_WARNING, \"Third-party plugins disabled.\");\n\t}\n\n\tsetQuitOnLastWindowClosed(false);\n\n\tmainWindow = new OBSBasic();\n\n\tmainWindow->setAttribute(Qt::WA_DeleteOnClose, true);\n\n#ifndef __APPLE__\n\tconnect(QApplication::instance(), &QApplication::aboutToQuit, this, [this]() {\n\t\tif (mainWindow) {\n\t\t\tQPointer<OBSBasic> basicWindow = static_cast<OBSBasic *>(mainWindow.get());\n\n\t\t\tbasicWindow->closeWindow();\n\t\t}\n\n\t\tif (libobs_initialized) {\n\t\t\tapplicationShutdown();\n\t\t}\n\t});\n#endif\n\n\tmainWindow->OBSInit();\n\n\tconnect(OBSBasic::Get(), &OBSBasic::mainWindowClosed, crashHandler_.get(),\n\t\t&OBS::CrashHandler::applicationShutdownHandler);\n\n\tconnect(this, &QGuiApplication::applicationStateChanged, this,\n\t\t[this](Qt::ApplicationState state) { ResetHotkeyState(state == Qt::ApplicationActive); });\n\tResetHotkeyState(applicationState() == Qt::ApplicationActive);\n\n\tconnect(crashHandler_.get(), &OBS::CrashHandler::crashLogUploadFailed, this,\n\t\t[this](const QString &errorMessage) {\n\t\t\temit this->logUploadFailed(OBS::LogFileType::CrashLog, errorMessage);\n\t\t});\n\n\tconnect(crashHandler_.get(), &OBS::CrashHandler::crashLogUploadFinished, this,\n\t\t[this](const QString &fileUrl) { emit this->logUploadFinished(OBS::LogFileType::CrashLog, fileUrl); });\n\n\treturn true;\n}\n\nstring OBSApp::GetVersionString(bool platform) const\n{\n\tstringstream ver;\n\n\tver << obs_get_version_string();\n\n\tif (platform) {\n\t\tver << \" (\";\n#ifdef _WIN32\n\t\tif (sizeof(void *) == 8)\n\t\t\tver << \"64-bit, \";\n\t\telse\n\t\t\tver << \"32-bit, \";\n\n\t\tver << \"windows)\";\n#elif __APPLE__\n\t\tver << \"mac)\";\n#elif __OpenBSD__\n\t\tver << \"openbsd)\";\n#elif __FreeBSD__\n\t\tver << \"freebsd)\";\n#else /* assume linux for the time being */\n\t\tver << \"linux)\";\n#endif\n\t}\n\n\treturn ver.str();\n}\n\nbool OBSApp::IsPortableMode()\n{\n\treturn portable_mode;\n}\n\nbool OBSApp::IsUpdaterDisabled()\n{\n\treturn opt_disable_updater;\n}\n\nbool OBSApp::IsMissingFilesCheckDisabled()\n{\n\treturn opt_disable_missing_files_check;\n}\n\n#ifdef __APPLE__\n#define INPUT_AUDIO_SOURCE \"coreaudio_input_capture\"\n#define OUTPUT_AUDIO_SOURCE \"coreaudio_output_capture\"\n#elif _WIN32\n#define INPUT_AUDIO_SOURCE \"wasapi_input_capture\"\n#define OUTPUT_AUDIO_SOURCE \"wasapi_output_capture\"\n#else\n#define INPUT_AUDIO_SOURCE \"pulse_input_capture\"\n#define OUTPUT_AUDIO_SOURCE \"pulse_output_capture\"\n#endif\n\nconst char *OBSApp::InputAudioSource() const\n{\n\treturn INPUT_AUDIO_SOURCE;\n}\n\nconst char *OBSApp::OutputAudioSource() const\n{\n\treturn OUTPUT_AUDIO_SOURCE;\n}\n\nconst char *OBSApp::GetLastLog() const\n{\n\treturn lastLogFile.c_str();\n}\n\nconst char *OBSApp::GetCurrentLog() const\n{\n\treturn currentLogFile.c_str();\n}\n\nvoid OBSApp::openCrashLogDirectory() const\n{\n\tstd::filesystem::path crashLogDirectory = crashHandler_->getCrashLogDirectory();\n\n\tif (crashLogDirectory.empty()) {\n\t\treturn;\n\t}\n\n\tQString crashLogDirectoryString = QString::fromStdString(crashLogDirectory.u8string());\n\n\tQUrl crashLogDirectoryURL = QUrl::fromLocalFile(crashLogDirectoryString);\n\tQDesktopServices::openUrl(crashLogDirectoryURL);\n}\n\nvoid OBSApp::uploadLastAppLog() const\n{\n\tOBSBasic *basicWindow = static_cast<OBSBasic *>(GetMainWindow());\n\n\tbasicWindow->UploadLog(\"obs-studio/logs\", GetLastLog(), OBS::LogFileType::LastAppLog);\n}\n\nvoid OBSApp::uploadCurrentAppLog() const\n{\n\tOBSBasic *basicWindow = static_cast<OBSBasic *>(GetMainWindow());\n\n\tbasicWindow->UploadLog(\"obs-studio/logs\", GetCurrentLog(), OBS::LogFileType::CurrentAppLog);\n}\n\nvoid OBSApp::uploadLastCrashLog()\n{\n\tcrashHandler_->uploadLastCrashLog();\n}\n\nOBS::LogFileState OBSApp::getLogFileState(OBS::LogFileType type) const\n{\n\tswitch (type) {\n\tcase OBS::LogFileType::CrashLog: {\n\t\tbool hasNewCrashLog = crashHandler_->hasNewCrashLog();\n\n\t\treturn (hasNewCrashLog) ? OBS::LogFileState::New : OBS::LogFileState::Uploaded;\n\t}\n\tcase OBS::LogFileType::CurrentAppLog:\n\tcase OBS::LogFileType::LastAppLog:\n\t\treturn OBS::LogFileState::New;\n\tdefault:\n\t\treturn OBS::LogFileState::NoState;\n\t}\n}\n\nbool OBSApp::TranslateString(const char *lookupVal, const char **out) const\n{\n\tfor (obs_frontend_translate_ui_cb cb : translatorHooks) {\n\t\tif (cb(lookupVal, out))\n\t\t\treturn true;\n\t}\n\n\treturn text_lookup_getstr(App()->GetTextLookup(), lookupVal, out);\n}\n\nQStyle *OBSApp::GetInvisibleCursorStyle()\n{\n\tif (!invisibleCursorStyle) {\n\t\tinvisibleCursorStyle = std::make_unique<OBSInvisibleCursorProxyStyle>();\n\t}\n\treturn invisibleCursorStyle.get();\n}\n\n// Global handler to receive all QEvent::Show events so we can apply\n// display affinity on any newly created windows and dialogs without\n// caring where they are coming from (e.g. plugins).\nbool OBSApp::notify(QObject *receiver, QEvent *e)\n{\n\tQWidget *w;\n\tQWindow *window;\n\tint windowType;\n\n\tif (!receiver->isWidgetType())\n\t\tgoto skip;\n\n\tif (e->type() != QEvent::Show)\n\t\tgoto skip;\n\n\tw = qobject_cast<QWidget *>(receiver);\n\n\tif (!w->isWindow())\n\t\tgoto skip;\n\n\twindow = w->windowHandle();\n\tif (!window)\n\t\tgoto skip;\n\n\twindowType = window->flags() & Qt::WindowType::WindowType_Mask;\n\n\tif (windowType == Qt::WindowType::Dialog || windowType == Qt::WindowType::Window ||\n\t    windowType == Qt::WindowType::Tool) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tif (main)\n\t\t\tmain->SetDisplayAffinity(window);\n\t}\n\nskip:\n\treturn QApplication::notify(receiver, e);\n}\n\nstring GenerateTimeDateFilename(const char *extension, bool noSpace)\n{\n\ttime_t now = time(0);\n\tchar file[256] = {};\n\tstruct tm *cur_time;\n\n\tcur_time = localtime(&now);\n\tsnprintf(file, sizeof(file), \"%d-%02d-%02d%c%02d-%02d-%02d.%s\", cur_time->tm_year + 1900, cur_time->tm_mon + 1,\n\t\t cur_time->tm_mday, noSpace ? '_' : ' ', cur_time->tm_hour, cur_time->tm_min, cur_time->tm_sec,\n\t\t extension);\n\n\treturn string(file);\n}\n\nstring GenerateSpecifiedFilename(const char *extension, bool noSpace, const char *format)\n{\n\tBPtr<char> filename = os_generate_formatted_filename(extension, !noSpace, format);\n\treturn string(filename);\n}\n\nstatic void FindBestFilename(string &strPath, bool noSpace)\n{\n\tint num = 2;\n\n\tif (!os_file_exists(strPath.c_str()))\n\t\treturn;\n\n\tconst char *ext = strrchr(strPath.c_str(), '.');\n\tif (!ext)\n\t\treturn;\n\n\tint extStart = int(ext - strPath.c_str());\n\tfor (;;) {\n\t\tstring testPath = strPath;\n\t\tstring numStr;\n\n\t\tnumStr = noSpace ? \"_\" : \" (\";\n\t\tnumStr += to_string(num++);\n\t\tif (!noSpace)\n\t\t\tnumStr += \")\";\n\n\t\ttestPath.insert(extStart, numStr);\n\n\t\tif (!os_file_exists(testPath.c_str())) {\n\t\t\tstrPath = testPath;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic void ensure_directory_exists(string &path)\n{\n\treplace(path.begin(), path.end(), '\\\\', '/');\n\n\tsize_t last = path.rfind('/');\n\tif (last == string::npos)\n\t\treturn;\n\n\tstring directory = path.substr(0, last);\n\tos_mkdirs(directory.c_str());\n}\n\nstatic void remove_reserved_file_characters(string &s)\n{\n\treplace(s.begin(), s.end(), '\\\\', '/');\n\treplace(s.begin(), s.end(), '*', '_');\n\treplace(s.begin(), s.end(), '?', '_');\n\treplace(s.begin(), s.end(), '\"', '_');\n\treplace(s.begin(), s.end(), '|', '_');\n\treplace(s.begin(), s.end(), ':', '_');\n\treplace(s.begin(), s.end(), '>', '_');\n\treplace(s.begin(), s.end(), '<', '_');\n}\n\nstring GetFormatString(const char *format, const char *prefix, const char *suffix)\n{\n\tstring f;\n\n\tf = format;\n\n\tif (prefix && *prefix) {\n\t\tstring str_prefix = prefix;\n\n\t\tif (str_prefix.back() != ' ')\n\t\t\tstr_prefix += \" \";\n\n\t\tsize_t insert_pos = 0;\n\t\tsize_t tmp;\n\n\t\ttmp = f.find_last_of('/');\n\t\tif (tmp != string::npos && tmp > insert_pos)\n\t\t\tinsert_pos = tmp + 1;\n\n\t\ttmp = f.find_last_of('\\\\');\n\t\tif (tmp != string::npos && tmp > insert_pos)\n\t\t\tinsert_pos = tmp + 1;\n\n\t\tf.insert(insert_pos, str_prefix);\n\t}\n\n\tif (suffix && *suffix) {\n\t\tif (*suffix != ' ')\n\t\t\tf += \" \";\n\t\tf += suffix;\n\t}\n\n\tremove_reserved_file_characters(f);\n\n\treturn f;\n}\n\nstring GetFormatExt(const char *container)\n{\n\tstring ext = container;\n\tif (ext == \"fragmented_mp4\" || ext == \"hybrid_mp4\")\n\t\text = \"mp4\";\n\telse if (ext == \"fragmented_mov\" || ext == \"hybrid_mov\")\n\t\text = \"mov\";\n\telse if (ext == \"hls\")\n\t\text = \"m3u8\";\n\telse if (ext == \"mpegts\")\n\t\text = \"ts\";\n\n\treturn ext;\n}\n\nstring GetOutputFilename(const char *path, const char *container, bool noSpace, bool overwrite, const char *format)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tos_dir_t *dir = path && path[0] ? os_opendir(path) : nullptr;\n\n\tif (!dir) {\n\t\tif (main->isVisible())\n\t\t\tOBSMessageBox::warning(main, QTStr(\"Output.BadPath.Title\"), QTStr(\"Output.BadPath.Text\"));\n\t\telse\n\t\t\tmain->SysTrayNotify(QTStr(\"Output.BadPath.Text\"), QSystemTrayIcon::Warning);\n\t\treturn \"\";\n\t}\n\n\tos_closedir(dir);\n\n\tstring strPath;\n\tstrPath += path;\n\n\tchar lastChar = strPath.back();\n\tif (lastChar != '/' && lastChar != '\\\\')\n\t\tstrPath += \"/\";\n\n\tstring ext = GetFormatExt(container);\n\tstrPath += GenerateSpecifiedFilename(ext.c_str(), noSpace, format);\n\tensure_directory_exists(strPath);\n\tif (!overwrite)\n\t\tFindBestFilename(strPath, noSpace);\n\n\treturn strPath;\n}\n\nvector<pair<string, string>> GetLocaleNames()\n{\n\tstring path;\n\tif (!GetDataFilePath(\"locale.ini\", path))\n\t\tthrow \"Could not find locale.ini path\";\n\n\tConfigFile ini;\n\tif (ini.Open(path.c_str(), CONFIG_OPEN_EXISTING) != 0)\n\t\tthrow \"Could not open locale.ini\";\n\n\tsize_t sections = config_num_sections(ini);\n\n\tvector<pair<string, string>> names;\n\tnames.reserve(sections);\n\tfor (size_t i = 0; i < sections; i++) {\n\t\tconst char *tag = config_get_section(ini, i);\n\t\tconst char *name = config_get_string(ini, tag, \"Name\");\n\t\tnames.emplace_back(tag, name);\n\t}\n\n\treturn names;\n}\n\n#if defined(__APPLE__) || defined(__linux__)\n#define BASE_PATH \"..\"\n#else\n#define BASE_PATH \"../..\"\n#endif\n\n#define CONFIG_PATH BASE_PATH \"/config\"\n\n#if defined(ENABLE_PORTABLE_CONFIG) || defined(_WIN32)\n#define ALLOW_PORTABLE_MODE 1\n#else\n#define ALLOW_PORTABLE_MODE 0\n#endif\n\nint GetAppConfigPath(char *path, size_t size, const char *name)\n{\n#if ALLOW_PORTABLE_MODE\n\tif (portable_mode) {\n\t\tif (name && *name) {\n\t\t\treturn snprintf(path, size, CONFIG_PATH \"/%s\", name);\n\t\t} else {\n\t\t\treturn snprintf(path, size, CONFIG_PATH);\n\t\t}\n\t} else {\n\t\treturn os_get_config_path(path, size, name);\n\t}\n#else\n\treturn os_get_config_path(path, size, name);\n#endif\n}\n\nchar *GetAppConfigPathPtr(const char *name)\n{\n#if ALLOW_PORTABLE_MODE\n\tif (portable_mode) {\n\t\tchar path[512];\n\n\t\tif (snprintf(path, sizeof(path), CONFIG_PATH \"/%s\", name) > 0) {\n\t\t\treturn bstrdup(path);\n\t\t} else {\n\t\t\treturn NULL;\n\t\t}\n\t} else {\n\t\treturn os_get_config_path_ptr(name);\n\t}\n#else\n\treturn os_get_config_path_ptr(name);\n#endif\n}\n\nint GetProgramDataPath(char *path, size_t size, const char *name)\n{\n\treturn os_get_program_data_path(path, size, name);\n}\n\nchar *GetProgramDataPathPtr(const char *name)\n{\n\treturn os_get_program_data_path_ptr(name);\n}\n\nbool GetFileSafeName(const char *name, std::string &file)\n{\n\tsize_t base_len = strlen(name);\n\tsize_t len = os_utf8_to_wcs(name, base_len, nullptr, 0);\n\tstd::wstring wfile;\n\n\tif (!len)\n\t\treturn false;\n\n\twfile.resize(len);\n\tos_utf8_to_wcs(name, base_len, &wfile[0], len + 1);\n\n\tfor (size_t i = wfile.size(); i > 0; i--) {\n\t\tsize_t im1 = i - 1;\n\n\t\tif (iswspace(wfile[im1])) {\n\t\t\twfile[im1] = '_';\n\t\t} else if (wfile[im1] != '_' && !iswalnum(wfile[im1])) {\n\t\t\twfile.erase(im1, 1);\n\t\t}\n\t}\n\n\tif (wfile.size() == 0)\n\t\twfile = L\"characters_only\";\n\n\tlen = os_wcs_to_utf8(wfile.c_str(), wfile.size(), nullptr, 0);\n\tif (!len)\n\t\treturn false;\n\n\tfile.resize(len);\n\tos_wcs_to_utf8(wfile.c_str(), wfile.size(), &file[0], len + 1);\n\treturn true;\n}\n\nbool GetClosestUnusedFileName(std::string &path, const char *extension)\n{\n\tsize_t len = path.size();\n\tif (extension) {\n\t\tpath += \".\";\n\t\tpath += extension;\n\t}\n\n\tif (!os_file_exists(path.c_str()))\n\t\treturn true;\n\n\tint index = 1;\n\n\tdo {\n\t\tpath.resize(len);\n\t\tpath += std::to_string(++index);\n\t\tif (extension) {\n\t\t\tpath += \".\";\n\t\t\tpath += extension;\n\t\t}\n\t} while (os_file_exists(path.c_str()));\n\n\treturn true;\n}\n\nbool WindowPositionValid(QRect rect)\n{\n\tfor (QScreen *screen : QGuiApplication::screens()) {\n\t\tif (screen->availableGeometry().intersects(rect))\n\t\t\treturn true;\n\t}\n\treturn false;\n}\n\n#ifndef _WIN32\n// Static signal handlers\nvoid OBSApp::sigIntSignalHandler(int)\n{\n\tchar tmp = 1;\n\t::send(sigIntFileDescriptor[0], &tmp, sizeof(tmp), 0);\n}\n\nvoid OBSApp::sigTermSignalHandler(int)\n{\n\tchar tmp = 1;\n\t::send(sigTermFileDescriptor[0], &tmp, sizeof(tmp), 0);\n}\n\nvoid OBSApp::sigAbrtSignalHandler(int)\n{\n\tchar tmp = 1;\n\t::send(sigAbrtFileDescriptor[0], &tmp, sizeof(tmp), 0);\n}\n\nvoid OBSApp::sigQuitSignalHandler(int)\n{\n\tchar tmp = 1;\n\t::send(sigQuitFileDescriptor[0], &tmp, sizeof(tmp), 0);\n}\n\n// App instance signal processors\nvoid OBSApp::processSigInt()\n{\n\tif (!sigIntNotifier->isEnabled()) {\n\t\treturn;\n\t}\n\n\tsigIntNotifier->setEnabled(false);\n\n\tchar tmp;\n\t::recv(sigIntFileDescriptor[1], &tmp, sizeof(tmp), 0);\n\n\tsigIntNotifier->setEnabled(true);\n\n#ifndef __APPLE__\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\tmain->saveAll();\n\t\tmain->close();\n\t}\n#else\n\tquit();\n#endif\n}\n\nvoid OBSApp::processSigTerm()\n{\n\tif (!sigTermNotifier->isEnabled()) {\n\t\treturn;\n\t}\n\n\tsigTermNotifier->setEnabled(false);\n\n\tchar tmp;\n\t::recv(sigTermFileDescriptor[1], &tmp, sizeof(tmp), 0);\n\n\tsigTermNotifier->setEnabled(true);\n\n#ifndef __APPLE__\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\tmain->saveAll();\n\t}\n#endif\n\tquit();\n}\n\nvoid OBSApp::processSigAbrt()\n{\n\tif (!sigAbrtNotifier->isEnabled()) {\n\t\treturn;\n\t}\n\n\tsigAbrtNotifier->setEnabled(false);\n\n\tchar tmp;\n\t::recv(sigAbrtFileDescriptor[1], &tmp, sizeof(tmp), 0);\n\n\tsigAbrtNotifier->setEnabled(true);\n\n#ifndef __APPLE__\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\tmain->saveAll();\n\t}\n#endif\n\tquit();\n}\n\nvoid OBSApp::processSigQuit()\n{\n\tif (!sigQuitNotifier->isEnabled()) {\n\t\treturn;\n\t}\n\n\tsigQuitNotifier->setEnabled(false);\n\n\tchar tmp;\n\t::recv(sigQuitFileDescriptor[1], &tmp, sizeof(tmp), 0);\n\n\tsigQuitNotifier->setEnabled(true);\n\n#ifndef __APPLE__\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\tmain->saveAll();\n\t}\n#endif\n\tquit();\n}\n#else\n// App instance signal processor stub methods used on Windows for OBSApp API compliance\nvoid OBSApp::processSigInt()\n{\n\treturn;\n}\n\nvoid OBSApp::processSigTerm()\n{\n\treturn;\n}\n\nvoid OBSApp::processSigAbrt()\n{\n\treturn;\n}\n\nvoid OBSApp::processSigQuit()\n{\n\treturn;\n}\n#endif\n\nvoid OBSApp::commitData(QSessionManager &manager)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\tmain->saveAll();\n\n\t\tif (manager.allowsInteraction() && main->shouldPromptForClose()) {\n\t\t\tmanager.cancel();\n\t\t}\n\t}\n}\n\nvoid OBSApp::applicationShutdown() noexcept\n{\n#ifdef _WIN32\n\tbool disableAudioDucking = config_get_bool(appConfig, \"Audio\", \"DisableAudioDucking\");\n\tif (disableAudioDucking)\n\t\tDisableAudioDucking(false);\n#else\n\tauto disconnectSignal = [this](std::array<int, 2> &fileDescriptor,\n\t\t\t\t       QPointer<QSocketNotifier> &notifier) -> void {\n\t\tnotifier->setEnabled(false);\n\n\t\tstd::array<int, 2> tempFileDescriptor = std::exchange(fileDescriptor, {0, 0});\n\t\t::close(tempFileDescriptor[0]);\n\t\t::close(tempFileDescriptor[1]);\n\t};\n\n\tdisconnectSignal(sigIntFileDescriptor, sigIntNotifier);\n\tdisconnectSignal(sigTermFileDescriptor, sigTermNotifier);\n\tdisconnectSignal(sigAbrtFileDescriptor, sigAbrtNotifier);\n\tdisconnectSignal(sigQuitFileDescriptor, sigQuitNotifier);\n#endif\n\n#ifdef __APPLE__\n\tbool vsyncDisabled = config_get_bool(appConfig, \"Video\", \"DisableOSXVSync\");\n\tbool resetVSync = config_get_bool(appConfig, \"Video\", \"ResetOSXVSyncOnExit\");\n\tif (vsyncDisabled && resetVSync)\n\t\tEnableOSXVSync(true);\n#endif\n\n\tos_inhibit_sleep_set_active(sleepInhibitor, false);\n\tos_inhibit_sleep_destroy(sleepInhibitor);\n\n\tif (libobs_initialized) {\n\t\tobs_shutdown();\n\t\tlibobs_initialized = false;\n\t}\n}\n\nvoid OBSApp::addLogLine(int logLevel, const QString &message)\n{\n\temit logLineAdded(logLevel, message);\n}\n\nvoid OBSApp::loadAppModules(struct obs_module_failure_info &mfi)\n{\n\tpluginManager_->preLoad();\n\tblog(LOG_INFO, \"---------------------------------\");\n\tobs_load_all_modules2(&mfi);\n\tblog(LOG_INFO, \"---------------------------------\");\n\tobs_log_loaded_modules();\n\tblog(LOG_INFO, \"---------------------------------\");\n\tobs_post_load_modules();\n\tpluginManager_->postLoad();\n}\n\nvoid OBSApp::pluginManagerOpenDialog()\n{\n\tpluginManager_->open();\n}\n"
  },
  {
    "path": "frontend/OBSApp.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <utility/OBSTheme.hpp>\n#include <utility/NativeEventFilter.hpp>\n#include <widgets/OBSMainWindow.hpp>\n\n#include <obs-frontend-api.h>\n#include <util/platform.h>\n#include <util/profiler.hpp>\n#include <util/util.hpp>\n\n#include <QAbstractNativeEventFilter>\n#include <QApplication>\n#include <QPalette>\n#include <QPointer>\n#include <QUuid>\n\n#include <array>\n#include <deque>\n#include <functional>\n#include <string>\n#include <vector>\n\ntypedef std::function<void()> VoidFunc;\n\nQ_DECLARE_METATYPE(VoidFunc)\n\nclass QFileSystemWatcher;\nclass QSocketNotifier;\n\nnamespace OBS {\nclass CrashHandler;\n\nenum class LogFileType { NoType, CurrentAppLog, LastAppLog, CrashLog };\nenum class LogFileState { NoState, New, Uploaded };\nclass PluginManager;\n} // namespace OBS\n\nstruct UpdateBranch {\n\tQString name;\n\tQString display_name;\n\tQString description;\n\tbool is_enabled;\n\tbool is_visible;\n};\n\nclass OBSApp : public QApplication {\n\tQ_OBJECT\n\n\tfriend class OBS::NativeEventFilter;\n\nprivate:\n\tQUuid appLaunchUUID_;\n\tstd::unique_ptr<OBS::CrashHandler> crashHandler_;\n\n\tstd::string locale;\n\n\tConfigFile appConfig;\n\tConfigFile userConfig;\n\tTextLookup textLookup;\n\tQPointer<OBSMainWindow> mainWindow;\n\tprofiler_name_store_t *profilerNameStore = nullptr;\n\tstd::vector<UpdateBranch> updateBranches;\n\tbool branches_loaded = false;\n\n\tbool libobs_initialized = false;\n\n\tos_inhibit_t *sleepInhibitor = nullptr;\n\tint sleepInhibitRefs = 0;\n\n\tbool enableHotkeysInFocus = true;\n\tbool enableHotkeysOutOfFocus = true;\n\n\tstd::deque<obs_frontend_translate_ui_cb> translatorHooks;\n\n\tstd::unique_ptr<OBS::PluginManager> pluginManager_;\n\n\tbool UpdatePre22MultiviewLayout(const char *layout);\n\n\tbool InitGlobalConfig();\n\tbool InitGlobalConfigDefaults();\n\tbool InitGlobalLocationDefaults();\n\n\tbool MigrateGlobalSettings();\n\tvoid MigrateLegacySettings(uint32_t lastVersion);\n\n\tbool InitUserConfig(std::filesystem::path &userConfigLocation, uint32_t lastVersion);\n\tvoid InitUserConfigDefaults();\n\n\tbool InitLocale();\n\tbool InitTheme();\n\n\tinline void ResetHotkeyState(bool inFocus);\n\n\tQPalette defaultPalette;\n\tOBSTheme *currentTheme = nullptr;\n\tQHash<QString, OBSTheme> themes;\n\tQPointer<QFileSystemWatcher> themeWatcher;\n\tstd::unique_ptr<QStyle> invisibleCursorStyle;\n\n\tvoid FindThemes();\n\n\tbool notify(QObject *receiver, QEvent *e) override;\n\n#ifndef _WIN32\n\tstatic std::array<int, 2> sigIntFileDescriptor;\n\tstatic std::array<int, 2> sigTermFileDescriptor;\n\tstatic std::array<int, 2> sigAbrtFileDescriptor;\n\tstatic std::array<int, 2> sigQuitFileDescriptor;\n\n\tQPointer<QSocketNotifier> sigIntNotifier{};\n\tQPointer<QSocketNotifier> sigTermNotifier{};\n\tQPointer<QSocketNotifier> sigAbrtNotifier{};\n\tQPointer<QSocketNotifier> sigQuitNotifier{};\n#endif\n\nprivate slots:\n\tvoid commitData(QSessionManager &manager);\n\tvoid addLogLine(int logLevel, const QString &message);\n\tvoid themeFileChanged(const QString &);\n\tvoid applicationShutdown() noexcept;\n\npublic:\n\tOBSApp(int &argc, char **argv, profiler_name_store_t *store);\n\t~OBSApp();\n\n\tvoid AppInit();\n\tvoid checkForUncleanShutdown();\n\tbool OBSInit();\n\n\tvoid UpdateHotkeyFocusSetting(bool reset = true);\n\tvoid DisableHotkeys();\n\n\tinline bool HotkeysEnabledInFocus() const { return enableHotkeysInFocus; }\n\n\tinline QMainWindow *GetMainWindow() const { return mainWindow.data(); }\n\n\tinline config_t *GetAppConfig() const { return appConfig; }\n\tinline config_t *GetUserConfig() const { return userConfig; }\n\tstd::filesystem::path userConfigLocation;\n\tstd::filesystem::path userScenesLocation;\n\tstd::filesystem::path userProfilesLocation;\n\tstd::filesystem::path userPluginManagerSettingsLocation;\n\n\tinline const char *GetLocale() const { return locale.c_str(); }\n\n\tOBSTheme *GetTheme() const { return currentTheme; }\n\tQList<OBSTheme> GetThemes() const { return themes.values(); }\n\tOBSTheme *GetTheme(const QString &name);\n\tbool SetTheme(const QString &name);\n\tbool IsThemeDark() const { return currentTheme ? currentTheme->isDark : false; }\n\tQStyle *GetInvisibleCursorStyle();\n\n\tvoid SetBranchData(const std::string &data);\n\tstd::vector<UpdateBranch> GetBranches();\n\n\tinline lookup_t *GetTextLookup() const { return textLookup; }\n\n\tinline const char *GetString(const char *lookupVal) const { return textLookup.GetString(lookupVal); }\n\n\tbool TranslateString(const char *lookupVal, const char **out) const;\n\n\tprofiler_name_store_t *GetProfilerNameStore() const { return profilerNameStore; }\n\n\tconst char *GetLastLog() const;\n\tconst char *GetCurrentLog() const;\n\n\tvoid openCrashLogDirectory() const;\n\tvoid uploadLastAppLog() const;\n\tvoid uploadCurrentAppLog() const;\n\tvoid uploadLastCrashLog();\n\n\tOBS::LogFileState getLogFileState(OBS::LogFileType type) const;\n\n\tstd::string GetVersionString(bool platform = true) const;\n\tbool IsPortableMode();\n\tbool IsUpdaterDisabled();\n\tbool IsMissingFilesCheckDisabled();\n\n\tconst char *InputAudioSource() const;\n\tconst char *OutputAudioSource() const;\n\n\tconst char *GetRenderModule() const;\n\n\tinline void IncrementSleepInhibition()\n\t{\n\t\tif (!sleepInhibitor)\n\t\t\treturn;\n\t\tif (sleepInhibitRefs++ == 0)\n\t\t\tos_inhibit_sleep_set_active(sleepInhibitor, true);\n\t}\n\n\tinline void DecrementSleepInhibition()\n\t{\n\t\tif (!sleepInhibitor)\n\t\t\treturn;\n\t\tif (sleepInhibitRefs == 0)\n\t\t\treturn;\n\t\tif (--sleepInhibitRefs == 0)\n\t\t\tos_inhibit_sleep_set_active(sleepInhibitor, false);\n\t}\n\n\tinline void PushUITranslation(obs_frontend_translate_ui_cb cb) { translatorHooks.emplace_front(cb); }\n\n\tinline void PopUITranslation() { translatorHooks.pop_front(); }\n#ifndef _WIN32\n\tstatic void sigIntSignalHandler(int);\n\tstatic void sigTermSignalHandler(int);\n\tstatic void sigAbrtSignalHandler(int);\n\tstatic void sigQuitSignalHandler(int);\n#endif\n\n\tvoid loadAppModules(struct obs_module_failure_info &mfi);\n\n\t// Plugin Manager Accessors\n\tvoid pluginManagerOpenDialog();\n\npublic slots:\n\tvoid Exec(VoidFunc func);\n\tvoid processSigInt();\n\tvoid processSigTerm();\n\tvoid processSigAbrt();\n\tvoid processSigQuit();\n\nsignals:\n\tvoid logLineAdded(int logLevel, const QString &message);\n\tvoid StyleChanged();\n\n\tvoid logUploadFinished(OBS::LogFileType, const QString &fileUrl);\n\tvoid logUploadFailed(OBS::LogFileType, const QString &errorMessage);\n};\n\nint GetAppConfigPath(char *path, size_t size, const char *name);\nchar *GetAppConfigPathPtr(const char *name);\n\ninline OBSApp *App()\n{\n\treturn static_cast<OBSApp *>(qApp);\n}\n\nstd::vector<std::pair<std::string, std::string>> GetLocaleNames();\ninline const char *Str(const char *lookup)\n{\n\treturn App()->GetString(lookup);\n}\ninline QString QTStr(const char *lookupVal)\n{\n\treturn QString::fromUtf8(Str(lookupVal));\n}\n\nint GetProgramDataPath(char *path, size_t size, const char *name);\nchar *GetProgramDataPathPtr(const char *name);\n\nbool GetFileSafeName(const char *name, std::string &file);\nbool GetClosestUnusedFileName(std::string &path, const char *extension);\n\nbool WindowPositionValid(QRect rect);\n\n#ifdef _WIN32\nextern \"C\" void install_dll_blocklist_hook(void);\nextern \"C\" void log_blocked_dlls(void);\n#endif\n\nstd::string CurrentDateTimeString();\nstd::string GetFormatString(const char *format, const char *prefix, const char *suffix);\nstd::string GenerateTimeDateFilename(const char *extension, bool noSpace = false);\nstd::string GetFormatExt(const char *container);\nstd::string GetOutputFilename(const char *path, const char *container, bool noSpace, bool overwrite,\n\t\t\t      const char *format);\nQObject *CreateShortcutFilter();\n"
  },
  {
    "path": "frontend/OBSApp_Themes.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSApp.hpp\"\n\n#include <utility/OBSProxyStyle.hpp>\n#include <utility/OBSThemeVariable.hpp>\n#include <utility/platform.hpp>\n\n#include <qt-wrappers.hpp>\n#include <ui-config.h>\n#include <util/cf-parser.h>\n\n#include <QDir>\n#include <QDirIterator>\n#include <QFile>\n#include <QFileSystemWatcher>\n#include <QMetaEnum>\n#include <QRandomGenerator>\n#include <QTimer>\n\nusing namespace std;\n\nstruct CFParser {\n\tcf_parser cfp = {};\n\t~CFParser() { cf_parser_free(&cfp); }\n\toperator cf_parser *() { return &cfp; }\n\tcf_parser *operator->() { return &cfp; }\n};\n\nstatic optional<OBSTheme> ParseThemeMeta(const QString &path)\n{\n\tQFile themeFile(path);\n\tif (!themeFile.open(QIODeviceBase::ReadOnly))\n\t\treturn nullopt;\n\n\tOBSTheme meta;\n\tconst QByteArray data = themeFile.readAll();\n\tCFParser cfp;\n\tint ret;\n\n\tif (!cf_parser_parse(cfp, data.constData(), QT_TO_UTF8(path)))\n\t\treturn nullopt;\n\n\tif (cf_token_is(cfp, \"@\") || cf_go_to_token(cfp, \"@\", nullptr)) {\n\t\twhile (cf_next_token(cfp)) {\n\t\t\tif (cf_token_is(cfp, \"OBSThemeMeta\")) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (!cf_go_to_token(cfp, \"@\", nullptr))\n\t\t\t\treturn nullopt;\n\t\t}\n\n\t\tif (!cf_token_is(cfp, \"OBSThemeMeta\"))\n\t\t\treturn nullopt;\n\n\t\tif (!cf_next_token(cfp))\n\t\t\treturn nullopt;\n\n\t\tif (!cf_token_is(cfp, \"{\"))\n\t\t\treturn nullopt;\n\n\t\tfor (;;) {\n\t\t\tif (!cf_next_token(cfp))\n\t\t\t\treturn nullopt;\n\n\t\t\tret = cf_token_is_type(cfp, CFTOKEN_NAME, \"name\", nullptr);\n\t\t\tif (ret != PARSE_SUCCESS)\n\t\t\t\tbreak;\n\n\t\t\tstring name(cfp->cur_token->str.array, cfp->cur_token->str.len);\n\n\t\t\tret = cf_next_token_should_be(cfp, \":\", \";\", nullptr);\n\t\t\tif (ret != PARSE_SUCCESS)\n\t\t\t\tcontinue;\n\n\t\t\tif (!cf_next_token(cfp))\n\t\t\t\treturn nullopt;\n\n\t\t\tret = cf_token_is_type(cfp, CFTOKEN_STRING, \"value\", \";\");\n\n\t\t\tif (ret != PARSE_SUCCESS)\n\t\t\t\tcontinue;\n\n\t\t\tBPtr str = cf_literal_to_str(cfp->cur_token->str.array, cfp->cur_token->str.len);\n\n\t\t\tif (str) {\n\t\t\t\tif (name == \"dark\")\n\t\t\t\t\tmeta.isDark = strcmp(str, \"true\") == 0;\n\t\t\t\telse if (name == \"extends\")\n\t\t\t\t\tmeta.extends = str;\n\t\t\t\telse if (name == \"author\")\n\t\t\t\t\tmeta.author = str;\n\t\t\t\telse if (name == \"id\")\n\t\t\t\t\tmeta.id = str;\n\t\t\t\telse if (name == \"name\")\n\t\t\t\t\tmeta.name = str;\n\t\t\t}\n\n\t\t\tif (!cf_go_to_token(cfp, \";\", nullptr))\n\t\t\t\treturn nullopt;\n\t\t}\n\t}\n\n\tauto filepath = filesystem::u8path(path.toStdString());\n\tmeta.isBaseTheme = filepath.extension() == \".obt\";\n\tmeta.filename = filepath.stem();\n\n\tif (meta.id.isEmpty() || meta.name.isEmpty() || (!meta.isBaseTheme && meta.extends.isEmpty())) {\n\t\t/* Theme is invalid */\n\t\treturn nullopt;\n\t} else {\n\t\tmeta.location = absolute(filepath);\n\t\tmeta.isHighContrast = path.endsWith(\".oha\");\n\t\tmeta.isVisible = !path.contains(\"System\");\n\t}\n\n\treturn meta;\n}\n\nstatic bool ParseVarName(CFParser &cfp, QString &value)\n{\n\tint ret;\n\n\tret = cf_next_token_should_be(cfp, \"(\", \";\", nullptr);\n\tif (ret != PARSE_SUCCESS)\n\t\treturn false;\n\tret = cf_next_token_should_be(cfp, \"-\", \";\", nullptr);\n\tif (ret != PARSE_SUCCESS)\n\t\treturn false;\n\tret = cf_next_token_should_be(cfp, \"-\", \";\", nullptr);\n\tif (ret != PARSE_SUCCESS)\n\t\treturn false;\n\tif (!cf_next_token(cfp))\n\t\treturn false;\n\n\tvalue = QString::fromUtf8(cfp->cur_token->str.array, cfp->cur_token->str.len);\n\n\tret = cf_next_token_should_be(cfp, \")\", \";\", nullptr);\n\tif (ret != PARSE_SUCCESS)\n\t\treturn false;\n\n\treturn !value.isEmpty();\n}\n\nstatic QColor ParseColor(CFParser &cfp)\n{\n\tconst char *array;\n\tuint32_t color = 0;\n\tQColor res(QColor::Invalid);\n\n\tif (cf_token_is(cfp, \"#\")) {\n\t\tif (!cf_next_token(cfp))\n\t\t\treturn res;\n\n\t\tcolor = strtol(cfp->cur_token->str.array, nullptr, 16);\n\t} else if (cf_token_is(cfp, \"rgb\")) {\n\t\tint ret = cf_next_token_should_be(cfp, \"(\", \";\", nullptr);\n\t\tif (ret != PARSE_SUCCESS || !cf_next_token(cfp))\n\t\t\treturn res;\n\n\t\tarray = cfp->cur_token->str.array;\n\t\tcolor |= strtol(array, nullptr, 10) << 16;\n\n\t\tret = cf_next_token_should_be(cfp, \",\", \";\", nullptr);\n\t\tif (ret != PARSE_SUCCESS || !cf_next_token(cfp))\n\t\t\treturn res;\n\n\t\tarray = cfp->cur_token->str.array;\n\t\tcolor |= strtol(array, nullptr, 10) << 8;\n\n\t\tret = cf_next_token_should_be(cfp, \",\", \";\", nullptr);\n\t\tif (ret != PARSE_SUCCESS || !cf_next_token(cfp))\n\t\t\treturn res;\n\n\t\tarray = cfp->cur_token->str.array;\n\t\tcolor |= strtol(array, nullptr, 10);\n\n\t\tret = cf_next_token_should_be(cfp, \")\", \";\", nullptr);\n\t\tif (ret != PARSE_SUCCESS)\n\t\t\treturn res;\n\t} else if (cf_token_is(cfp, \"bikeshed\")) {\n\t\tcolor |= QRandomGenerator::global()->bounded(INT8_MAX) << 16;\n\t\tcolor |= QRandomGenerator::global()->bounded(INT8_MAX) << 8;\n\t\tcolor |= QRandomGenerator::global()->bounded(INT8_MAX);\n\t}\n\n\tres = color;\n\treturn res;\n}\n\nstatic bool ParseMath(CFParser &cfp, QStringList &values, vector<OBSThemeVariable> &vars)\n{\n\tint ret = cf_next_token_should_be(cfp, \"(\", \";\", nullptr);\n\tif (ret != PARSE_SUCCESS)\n\t\treturn false;\n\tif (!cf_next_token(cfp))\n\t\treturn false;\n\n\twhile (!cf_token_is(cfp, \")\")) {\n\t\tif (cf_token_is(cfp, \";\"))\n\t\t\tbreak;\n\n\t\tif (cf_token_is(cfp, \"calc\") || cf_token_is(cfp, \"max\") || cf_token_is(cfp, \"min\")) {\n\t\t\t/* Internal math operations do not have proper names.\n\t\t\t * They are anonymous variables */\n\t\t\tOBSThemeVariable var;\n\t\t\tQStringList subvalues;\n\n\t\t\tvar.name = QString(\"__unnamed_%1\").arg(QRandomGenerator::global()->generate64());\n\n\t\t\tOBSThemeVariable::VariableType varType;\n\t\t\tif (cf_token_is(cfp, \"calc\"))\n\t\t\t\tvarType = OBSThemeVariable::Calc;\n\t\t\telse if (cf_token_is(cfp, \"max\"))\n\t\t\t\tvarType = OBSThemeVariable::Max;\n\t\t\telse if (cf_token_is(cfp, \"min\"))\n\t\t\t\tvarType = OBSThemeVariable::Min;\n\n\t\t\tif (!ParseMath(cfp, subvalues, vars))\n\t\t\t\treturn false;\n\n\t\t\tvar.type = varType;\n\t\t\tvar.value = subvalues;\n\t\t\tvalues << var.name;\n\t\t\tvars.push_back(std::move(var));\n\t\t} else if (cf_token_is(cfp, \"var\")) {\n\t\t\tQString value;\n\t\t\tif (!ParseVarName(cfp, value))\n\t\t\t\treturn false;\n\n\t\t\tvalues << value;\n\t\t} else {\n\t\t\tvalues << QString::fromUtf8(cfp->cur_token->str.array, cfp->cur_token->str.len);\n\t\t}\n\n\t\tif (!cf_next_token(cfp))\n\t\t\treturn false;\n\t}\n\n\treturn !values.isEmpty();\n}\n\nstatic vector<OBSThemeVariable> ParseThemeVariables(const char *themeData)\n{\n\tCFParser cfp;\n\tint ret;\n\n\tstd::vector<OBSThemeVariable> vars;\n\n\tif (!cf_parser_parse(cfp, themeData, nullptr))\n\t\treturn vars;\n\n\tif (!cf_token_is(cfp, \"@\") && !cf_go_to_token(cfp, \"@\", nullptr))\n\t\treturn vars;\n\n\twhile (cf_next_token(cfp)) {\n\t\tif (cf_token_is(cfp, \"OBSThemeVars\"))\n\t\t\tbreak;\n\n\t\tif (!cf_go_to_token(cfp, \"@\", nullptr))\n\t\t\treturn vars;\n\t}\n\n\tif (!cf_next_token(cfp))\n\t\treturn {};\n\n\tif (!cf_token_is(cfp, \"{\"))\n\t\treturn {};\n\n\tfor (;;) {\n\t\tif (!cf_next_token(cfp))\n\t\t\treturn vars;\n\n\t\tif (!cf_token_is(cfp, \"-\"))\n\t\t\treturn vars;\n\n\t\tret = cf_next_token_should_be(cfp, \"-\", \";\", nullptr);\n\t\tif (ret != PARSE_SUCCESS)\n\t\t\tcontinue;\n\n\t\tif (!cf_next_token(cfp))\n\t\t\treturn vars;\n\n\t\tret = cf_token_is_type(cfp, CFTOKEN_NAME, \"key\", nullptr);\n\t\tif (ret != PARSE_SUCCESS)\n\t\t\tbreak;\n\n\t\tQString key = QString::fromUtf8(cfp->cur_token->str.array, cfp->cur_token->str.len);\n\t\tOBSThemeVariable var;\n\t\tvar.name = key;\n\n#ifdef _WIN32\n\t\tconst QString osPrefix = \"os_win_\";\n#elif __APPLE__\n\t\tconst QString osPrefix = \"os_mac_\";\n#else\n\t\tconst QString osPrefix = \"os_lin_\";\n#endif\n\n\t\tif (key.startsWith(osPrefix) && key.length() > osPrefix.length()) {\n\t\t\tvar.name = key.sliced(osPrefix.length());\n\t\t}\n\n\t\tret = cf_next_token_should_be(cfp, \":\", \";\", nullptr);\n\t\tif (ret != PARSE_SUCCESS)\n\t\t\tcontinue;\n\n\t\tif (!cf_next_token(cfp))\n\t\t\treturn vars;\n\n\t\t/* Special values passed to the theme by OBS are prefixed with 'obs', so we\n\t\t * prevent theme variables from using it as a prefix. */\n\t\tif (key.startsWith(\"obs\"))\n\t\t\tcontinue;\n\n\t\tif (cfp->cur_token->type == CFTOKEN_NUM) {\n\t\t\tconst char *ch = cfp->cur_token->str.array;\n\t\t\tconst char *end = ch + cfp->cur_token->str.len;\n\t\t\tdouble f = os_strtod(ch);\n\n\t\t\tvar.value = f;\n\t\t\tvar.type = OBSThemeVariable::Number;\n\n\t\t\t/* Look for a suffix and mark variable as size if it exists */\n\t\t\twhile (ch < end) {\n\t\t\t\tif (!isdigit(*ch) && !isspace(*ch) && *ch != '.') {\n\t\t\t\t\tvar.suffix = QString::fromUtf8(ch, end - ch);\n\t\t\t\t\tvar.type = OBSThemeVariable::Size;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tch++;\n\t\t\t}\n\t\t} else if (cf_token_is(cfp, \"rgb\") || cf_token_is(cfp, \"#\") || cf_token_is(cfp, \"bikeshed\")) {\n\t\t\tQColor color = ParseColor(cfp);\n\t\t\tif (!color.isValid())\n\t\t\t\tcontinue;\n\n\t\t\tvar.value = color;\n\t\t\tvar.type = OBSThemeVariable::Color;\n\t\t} else if (cf_token_is(cfp, \"var\")) {\n\t\t\tQString value;\n\n\t\t\tif (!ParseVarName(cfp, value))\n\t\t\t\tcontinue;\n\n\t\t\tvar.value = value;\n\t\t\tvar.type = OBSThemeVariable::Alias;\n\t\t} else if (cf_token_is(cfp, \"calc\") || cf_token_is(cfp, \"max\") || cf_token_is(cfp, \"min\")) {\n\t\t\tQStringList values;\n\n\t\t\tif (cf_token_is(cfp, \"calc\"))\n\t\t\t\tvar.type = OBSThemeVariable::Calc;\n\t\t\telse if (cf_token_is(cfp, \"max\"))\n\t\t\t\tvar.type = OBSThemeVariable::Max;\n\t\t\telse if (cf_token_is(cfp, \"min\"))\n\t\t\t\tvar.type = OBSThemeVariable::Min;\n\n\t\t\tif (!ParseMath(cfp, values, vars))\n\t\t\t\tcontinue;\n\n\t\t\tvar.value = values;\n\t\t} else {\n\t\t\tvar.type = OBSThemeVariable::String;\n\t\t\tBPtr strVal = cf_literal_to_str(cfp->cur_token->str.array, cfp->cur_token->str.len);\n\t\t\tvar.value = QString::fromUtf8(strVal.Get());\n\t\t}\n\n\t\tif (!cf_next_token(cfp))\n\t\t\treturn vars;\n\n\t\tif (cf_token_is(cfp, \"!\") &&\n\t\t    cf_next_token_should_be(cfp, \"editable\", nullptr, nullptr) == PARSE_SUCCESS) {\n\t\t\tif (var.type == OBSThemeVariable::Calc || var.type == OBSThemeVariable::Max ||\n\t\t\t    var.type == OBSThemeVariable::Min || var.type == OBSThemeVariable::Alias) {\n\t\t\t\tblog(LOG_WARNING, \"Math or alias variable type cannot be editable: %s\",\n\t\t\t\t     QT_TO_UTF8(var.name));\n\t\t\t} else {\n\t\t\t\tvar.editable = true;\n\t\t\t}\n\t\t}\n\n\t\tvars.push_back(std::move(var));\n\n\t\tif (!cf_token_is(cfp, \";\") && !cf_go_to_token(cfp, \";\", nullptr))\n\t\t\treturn vars;\n\t}\n\n\treturn vars;\n}\n\nvoid OBSApp::FindThemes()\n{\n\n\tQStringList filters;\n\tfilters << \"*.obt\" // OBS Base Theme\n\t\t<< \"*.ovt\" // OBS Variant Theme\n\t\t<< \"*.oha\" // OBS High-contrast Adjustment layer\n\t\t;\n\n\t{\n\t\tstring themeDir;\n\t\tGetDataFilePath(\"themes/\", themeDir);\n\t\tQDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files);\n\t\twhile (it.hasNext()) {\n\t\t\tauto theme = ParseThemeMeta(it.next());\n\t\t\tif (theme && !themes.contains(theme->id))\n\t\t\t\tthemes[theme->id] = std::move(*theme);\n\t\t}\n\t}\n\n\t{\n\t\tconst std::string themeDir = App()->userConfigLocation.u8string() + \"/obs-studio/themes\";\n\n\t\tQDirIterator it(QString::fromStdString(themeDir), filters, QDir::Files);\n\n\t\twhile (it.hasNext()) {\n\t\t\tauto theme = ParseThemeMeta(it.next());\n\t\t\tif (theme && !themes.contains(theme->id))\n\t\t\t\tthemes[theme->id] = std::move(*theme);\n\t\t}\n\t}\n\n\t/* Build dependency tree for all themes, removing ones that have items missing. */\n\tQSet<QString> invalid;\n\n\tfor (OBSTheme &theme : themes) {\n\t\tif (theme.extends.isEmpty()) {\n\t\t\tif (!theme.isBaseTheme) {\n\t\t\t\tblog(LOG_ERROR, R\"(Theme \"%s\" is not base, but does not specify parent!)\",\n\t\t\t\t     QT_TO_UTF8(theme.id));\n\t\t\t\tinvalid.insert(theme.id);\n\t\t\t}\n\n\t\t\tcontinue;\n\t\t}\n\n\t\tQString parentId = theme.extends;\n\t\twhile (!parentId.isEmpty()) {\n\t\t\tOBSTheme *parent = GetTheme(parentId);\n\t\t\tif (!parent) {\n\t\t\t\tblog(LOG_ERROR, R\"(Theme \"%s\" is missing ancestor \"%s\"!)\", QT_TO_UTF8(theme.id),\n\t\t\t\t     QT_TO_UTF8(parentId));\n\t\t\t\tinvalid.insert(theme.id);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (theme.isBaseTheme && !parent->isBaseTheme) {\n\t\t\t\tblog(LOG_ERROR, R\"(Ancestor \"%s\" of base theme \"%s\" is not a base theme!)\",\n\t\t\t\t     QT_TO_UTF8(parent->id), QT_TO_UTF8(theme.id));\n\t\t\t\tinvalid.insert(theme.id);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (parent->id == theme.id || theme.dependencies.contains(parent->id)) {\n\t\t\t\tblog(LOG_ERROR, R\"(Dependency chain of \"%s\" (\"%s\") contains recursion!)\",\n\t\t\t\t     QT_TO_UTF8(theme.id), QT_TO_UTF8(parent->id));\n\t\t\t\tinvalid.insert(theme.id);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t/* Mark this theme as a variant of first parent that is a base theme. */\n\t\t\tif (!theme.isBaseTheme && parent->isBaseTheme && theme.parent.isEmpty())\n\t\t\t\ttheme.parent = parent->id;\n\n\t\t\ttheme.dependencies.push_front(parent->id);\n\t\t\tparentId = parent->extends;\n\n\t\t\tif (parentId.isEmpty() && !parent->isBaseTheme) {\n\t\t\t\tblog(LOG_ERROR, R\"(Final ancestor of \"%s\" (\"%s\") is not a base theme!)\",\n\t\t\t\t     QT_TO_UTF8(theme.id), QT_TO_UTF8(parent->id));\n\t\t\t\tinvalid.insert(theme.id);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (const QString &name : invalid) {\n\t\tthemes.remove(name);\n\t}\n}\n\nstatic bool ResolveVariable(const QHash<QString, OBSThemeVariable> &vars, OBSThemeVariable &var)\n{\n\tif (var.type != OBSThemeVariable::Alias)\n\t\treturn true;\n\n\tQString key = var.value.toString();\n\twhile (vars[key].type == OBSThemeVariable::Alias) {\n\t\tkey = vars[key].value.toString();\n\n\t\tif (!vars.contains(key)) {\n\t\t\tblog(LOG_ERROR, R\"(Variable \"%s\" (aliased by \"%s\") does not exist!)\", QT_TO_UTF8(key),\n\t\t\t     QT_TO_UTF8(var.name));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tvar = vars[key];\n\n\treturn true;\n}\n\nstatic QString EvalMath(const QHash<QString, OBSThemeVariable> &vars, const OBSThemeVariable &var,\n\t\t\tconst OBSThemeVariable::VariableType type, const int recursion = 0);\n\nstatic OBSThemeVariable ParseMathVariable(const QHash<QString, OBSThemeVariable> &vars, const QString &value,\n\t\t\t\t\t  const int recursion = 0)\n{\n\tOBSThemeVariable var;\n\tconst QByteArray utf8 = value.toUtf8();\n\tconst char *data = utf8.constData();\n\n\tif (isdigit(*data)) {\n\t\tdouble f = os_strtod(data);\n\t\tvar.type = OBSThemeVariable::Number;\n\t\tvar.value = f;\n\n\t\tconst char *dataEnd = data + utf8.size();\n\t\twhile (data < dataEnd) {\n\t\t\tif (*data && !isdigit(*data) && *data != '.') {\n\t\t\t\tvar.suffix = QString::fromUtf8(data, dataEnd - data);\n\t\t\t\tvar.type = OBSThemeVariable::Size;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tdata++;\n\t\t}\n\t} else {\n\t\t/* Treat value as an alias/key and resolve it */\n\t\tvar.type = OBSThemeVariable::Alias;\n\t\tvar.value = value;\n\t\tResolveVariable(vars, var);\n\n\t\t/* Handle nested math calculations */\n\t\tif (var.type == OBSThemeVariable::Calc || var.type == OBSThemeVariable::Max ||\n\t\t    var.type == OBSThemeVariable::Min) {\n\t\t\tQString val = EvalMath(vars, var, var.type, recursion + 1);\n\t\t\tvar = ParseMathVariable(vars, val);\n\t\t}\n\n\t\t/* Only number or size would be valid here */\n\t\tif (var.type != OBSThemeVariable::Number && var.type != OBSThemeVariable::Size) {\n\t\t\tblog(LOG_ERROR, \"Math operand is not a size or number: %s %s %d\", QT_TO_UTF8(var.name),\n\t\t\t     QT_TO_UTF8(var.value.toString()), var.type);\n\t\t\tthrow invalid_argument(\"Operand not of numeric type\");\n\t\t}\n\t}\n\n\treturn var;\n}\n\nstatic QString EvalMath(const QHash<QString, OBSThemeVariable> &vars, const OBSThemeVariable &var,\n\t\t\tconst OBSThemeVariable::VariableType type, const int recursion)\n{\n\tif (recursion >= 10) {\n\t\t/* Abort after 10 levels of recursion */\n\t\tblog(LOG_ERROR, \"Maximum recursion levels hit!\");\n\t\treturn \"'Invalid expression'\";\n\t}\n\n\tif (type != OBSThemeVariable::Calc && type != OBSThemeVariable::Max && type != OBSThemeVariable::Min) {\n\t\tblog(LOG_ERROR, \"Invalid type for math operation!\");\n\t\treturn \"'Invalid expression'\";\n\t}\n\n\tQStringList args = var.value.toStringList();\n\tQString &opt = args[1];\n\tif (type == OBSThemeVariable::Calc && (opt != '*' && opt != '+' && opt != '-' && opt != '/')) {\n\t\tblog(LOG_ERROR, \"Unknown/invalid calc() operator: %s\", QT_TO_UTF8(opt));\n\t\treturn \"'Invalid expression'\";\n\t}\n\n\tif ((type == OBSThemeVariable::Max || type == OBSThemeVariable::Min) && opt != ',') {\n\t\tblog(LOG_ERROR, \"Invalid math separator: %s\", QT_TO_UTF8(opt));\n\t\treturn \"'Invalid expression'\";\n\t}\n\n\tif (args.length() != 3) {\n\t\tblog(LOG_ERROR, \"Math parse had invalid number of arguments: %lld (%s)\", args.length(),\n\t\t     QT_TO_UTF8(args.join(\", \")));\n\t\treturn \"'Invalid expression'\";\n\t}\n\n\tOBSThemeVariable val1, val2;\n\ttry {\n\t\tval1 = ParseMathVariable(vars, args[0], 0);\n\t\tval2 = ParseMathVariable(vars, args[2], 0);\n\t} catch (...) {\n\t\treturn \"'Invalid expression'\";\n\t}\n\n\t/* Ensure that suffixes match (if any) */\n\tif (!val1.suffix.isEmpty() && !val2.suffix.isEmpty() && val1.suffix != val2.suffix) {\n\t\tblog(LOG_ERROR, \"Math operation requires suffixes to match or only one to be present! %s != %s\",\n\t\t     QT_TO_UTF8(val1.suffix), QT_TO_UTF8(val2.suffix));\n\t\treturn \"'Invalid expression'\";\n\t}\n\n\tdouble d1 = val1.userValue.isValid() ? val1.userValue.toDouble() : val1.value.toDouble();\n\tdouble d2 = val2.userValue.isValid() ? val2.userValue.toDouble() : val2.value.toDouble();\n\n\tif (!isfinite(d1) || !isfinite(d2)) {\n\t\tblog(LOG_ERROR,\n\t\t     \"At least one invalid math value:\"\n\t\t     \" op1: %f, op2: %f\",\n\t\t     d1, d2);\n\t\treturn \"'Invalid expression'\";\n\t}\n\n\tdouble val = numeric_limits<double>::quiet_NaN();\n\n\tif (type == OBSThemeVariable::Calc) {\n\t\tif (opt == \"+\")\n\t\t\tval = d1 + d2;\n\t\telse if (opt == \"-\")\n\t\t\tval = d1 - d2;\n\t\telse if (opt == \"*\")\n\t\t\tval = d1 * d2;\n\t\telse if (opt == \"/\")\n\t\t\tval = d1 / d2;\n\n\t\tif (!isnormal(val)) {\n\t\t\tblog(LOG_ERROR, \"Invalid calc() resulted in non-normal number: %f %s %f = %f\", d1,\n\t\t\t     QT_TO_UTF8(opt), d2, val);\n\t\t\treturn \"'Invalid expression'\";\n\t\t}\n\t} else if (type == OBSThemeVariable::Max) {\n\t\tval = d1 > d2 ? d1 : d2;\n\t} else if (type == OBSThemeVariable::Min) {\n\t\tval = d1 < d2 ? d1 : d2;\n\t}\n\n\tbool isInteger = ceill(val) == val;\n\tQString result = QString::number(val, 'f', isInteger ? 0 : -1);\n\n\t/* Carry-over suffix */\n\tif (!val1.suffix.isEmpty())\n\t\tresult += val1.suffix;\n\telse if (!val2.suffix.isEmpty())\n\t\tresult += val2.suffix;\n\n\treturn result;\n}\n\nstatic qsizetype FindEndOfOBSMetadata(const QString &content)\n{\n\t/* Find end of last OBS-specific section and strip it, kinda jank but should work */\n\tqsizetype end = 0;\n\n\tfor (auto section : {\"OBSThemeMeta\", \"OBSThemeVars\", \"OBSTheme\"}) {\n\t\tqsizetype idx = content.indexOf(section, 0);\n\t\tif (idx > end) {\n\t\t\tend = content.indexOf('}', idx) + 1;\n\t\t}\n\t}\n\n\treturn end;\n}\n\nstatic QString PrepareQSS(const QHash<QString, OBSThemeVariable> &vars, const QStringList &contents)\n{\n\tQString stylesheet;\n\tQString needleTemplate(\"var(--%1)\");\n\n\tfor (const QString &content : contents) {\n\t\tqsizetype offset = FindEndOfOBSMetadata(content);\n\t\tif (offset >= 0) {\n\t\t\tstylesheet += \"\\n\";\n\t\t\tstylesheet += content.sliced(offset);\n\t\t}\n\t}\n\n\tfor (const OBSThemeVariable &var_ : vars) {\n\t\tOBSThemeVariable var(var_);\n\n\t\tif (!ResolveVariable(vars, var))\n\t\t\tcontinue;\n\n\t\tQString needle = needleTemplate.arg(var_.name);\n\t\tQString replace;\n\n\t\tQVariant value = var.userValue.isValid() ? var.userValue : var.value;\n\n\t\tif (var.type == OBSThemeVariable::Color) {\n\t\t\treplace = value.value<QColor>().name(QColor::HexRgb);\n\t\t} else if (var.type == OBSThemeVariable::Calc || var.type == OBSThemeVariable::Max ||\n\t\t\t   var.type == OBSThemeVariable::Min) {\n\t\t\treplace = EvalMath(vars, var, var.type);\n\t\t} else if (var.type == OBSThemeVariable::Size || var.type == OBSThemeVariable::Number) {\n\t\t\tdouble val = value.toDouble();\n\n\t\t\tbool isInteger = ceill(val) == val;\n\t\t\treplace = QString::number(val, 'f', isInteger ? 0 : -1);\n\n\t\t\tif (!var.suffix.isEmpty())\n\t\t\t\treplace += var.suffix;\n\t\t} else {\n\t\t\treplace = value.toString();\n\t\t}\n\n\t\t// Round any values with a px suffix. Qt does this anyway for some properties,\n\t\t// but then will flat out break with decimals for others.\n\t\tif (replace.right(2) == \"px\") {\n\t\t\tQString replaceValue = replace.sliced(0, replace.length() - 2);\n\t\t\tint val = (int)std::roundf(replaceValue.toDouble());\n\n\t\t\treplace = QString::number(val) + \"px\";\n\t\t}\n\n\t\tstylesheet = stylesheet.replace(needle, replace);\n\t}\n\n\treturn stylesheet;\n}\n\ntemplate<typename T> static void FillEnumMap(QHash<QString, T> &map)\n{\n\tQMetaEnum meta = QMetaEnum::fromType<T>();\n\n\tint numKeys = meta.keyCount();\n\tfor (int i = 0; i < numKeys; i++) {\n\t\tconst char *key = meta.key(i);\n\t\tQString keyName(key);\n\t\tmap[keyName.toLower()] = static_cast<T>(meta.keyToValue(key));\n\t}\n}\n\nstatic QPalette PreparePalette(const QHash<QString, OBSThemeVariable> &vars, const QPalette &defaultPalette)\n{\n\tstatic QHash<QString, QPalette::ColorRole> roleMap;\n\tstatic QHash<QString, QPalette::ColorGroup> groupMap;\n\n\tif (roleMap.empty())\n\t\tFillEnumMap<QPalette::ColorRole>(roleMap);\n\tif (groupMap.empty())\n\t\tFillEnumMap<QPalette::ColorGroup>(groupMap);\n\n\tQPalette pal(defaultPalette);\n\n\tfor (const OBSThemeVariable &var_ : vars) {\n\t\tif (!var_.name.startsWith(\"palette_\"))\n\t\t\tcontinue;\n\t\tif (var_.name.count(\"_\") < 1 || var_.name.count(\"_\") > 2)\n\t\t\tcontinue;\n\n\t\tOBSThemeVariable var(var_);\n\t\tif (!ResolveVariable(vars, var) || var.type != OBSThemeVariable::Color)\n\t\t\tcontinue;\n\n\t\t/* Determine role and optionally group based on name.\n\t\t * Format is: palette_<role>[_<group>] */\n\t\tQPalette::ColorRole role = QPalette::NoRole;\n\t\tQPalette::ColorGroup group = QPalette::All;\n\n\t\tQStringList parts = var_.name.split(\"_\");\n\t\tif (parts.length() >= 2) {\n\t\t\tQString key = parts[1].toLower();\n\t\t\tif (!roleMap.contains(key)) {\n\t\t\t\tblog(LOG_WARNING, \"Palette role \\\"%s\\\" is not valid!\", QT_TO_UTF8(parts[1]));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\trole = roleMap[key];\n\t\t}\n\n\t\tif (parts.length() == 3) {\n\t\t\tQString key = parts[2].toLower();\n\t\t\tif (!groupMap.contains(key)) {\n\t\t\t\tblog(LOG_WARNING, \"Palette group \\\"%s\\\" is not valid!\", QT_TO_UTF8(parts[2]));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tgroup = groupMap[key];\n\t\t}\n\n\t\tQVariant value = var.userValue.isValid() ? var.userValue : var.value;\n\n\t\tQColor color = value.value<QColor>().name(QColor::HexRgb);\n\t\tpal.setColor(group, role, color);\n\t}\n\n\treturn pal;\n}\n\nstatic double getPaddingForDensityId(int id)\n{\n\tdouble paddingValue = 4;\n\n\tif (id == -2) {\n\t\tpaddingValue = 0.25;\n\t} else if (id == -3) {\n\t\tpaddingValue = 2;\n\t} else if (id == -4) {\n\t\tpaddingValue = 4;\n\t} else if (id == -5) {\n\t\tpaddingValue = 6;\n\t}\n\n\treturn paddingValue;\n}\n\nOBSTheme *OBSApp::GetTheme(const QString &name)\n{\n\tif (!themes.contains(name))\n\t\treturn nullptr;\n\n\treturn &themes[name];\n}\n\nbool OBSApp::SetTheme(const QString &name)\n{\n\tOBSTheme *theme = GetTheme(name);\n\tif (!theme)\n\t\treturn false;\n\n\tif (themeWatcher) {\n\t\tthemeWatcher->blockSignals(true);\n\t\tthemeWatcher->removePaths(themeWatcher->files());\n\t}\n\n\tsetStyleSheet(\"\");\n\tcurrentTheme = theme;\n\n\tQStringList contents;\n\tQHash<QString, OBSThemeVariable> vars;\n\t/* Build list of themes to load (in order) */\n\tQStringList themeIds(theme->dependencies);\n\tthemeIds << theme->id;\n\n\t/* Inject Appearance settings into theme vars */\n\tOBSThemeVariable fontScale;\n\tfontScale.name = \"obsFontScale\";\n\tfontScale.type = OBSThemeVariable::Number;\n\tfontScale.value = QVariant::fromValue(config_get_int(App()->GetUserConfig(), \"Appearance\", \"FontScale\"));\n\n\tconst int density = config_get_int(App()->GetUserConfig(), \"Appearance\", \"Density\");\n\n\tOBSThemeVariable padding;\n\tpadding.name = \"obsPadding\";\n\tpadding.type = OBSThemeVariable::Number;\n\tpadding.value = QVariant::fromValue(getPaddingForDensityId(density));\n\n\tvars[fontScale.name] = std::move(fontScale);\n\tvars[padding.name] = std::move(padding);\n\n\t/* Find and add high contrast adjustment layer if available */\n\tif (HighContrastEnabled()) {\n\t\tfor (const OBSTheme &theme_ : themes) {\n\t\t\tif (!theme_.isHighContrast)\n\t\t\t\tcontinue;\n\t\t\tif (theme_.parent != theme->id)\n\t\t\t\tcontinue;\n\t\t\tthemeIds << theme_.id;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tQStringList filenames;\n\tfor (const QString &themeId : themeIds) {\n\t\tOBSTheme *cur = GetTheme(themeId);\n\n\t\tQFile file(cur->location);\n\t\tfilenames << file.fileName();\n\n\t\tif (!file.open(QIODeviceBase::ReadOnly))\n\t\t\treturn false;\n\t\tconst QByteArray content = file.readAll();\n\n\t\tfor (OBSThemeVariable &var : ParseThemeVariables(content.constData())) {\n\t\t\tvars[var.name] = std::move(var);\n\t\t}\n\n\t\tcontents.emplaceBack(content.constData());\n\t}\n\n\t/* Check if OBS appearance settings are used in the theme */\n\tcurrentTheme->usesFontScale = false;\n\tcurrentTheme->usesDensity = false;\n\tfor (const OBSThemeVariable &var : vars) {\n\t\tswitch (var.type) {\n\t\tcase OBSThemeVariable::Color:\n\t\tcase OBSThemeVariable::Size:\n\t\tcase OBSThemeVariable::Number:\n\t\tcase OBSThemeVariable::String:\n\t\t\tcontinue;\n\t\tcase OBSThemeVariable::Alias:\n\t\t\tif (var.value.toString() == \"obsFontScale\") {\n\t\t\t\tcurrentTheme->usesFontScale = true;\n\t\t\t}\n\n\t\t\tif (var.value.toString() == \"obsPadding\") {\n\t\t\t\tcurrentTheme->usesDensity = true;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase OBSThemeVariable::Calc:\n\t\tcase OBSThemeVariable::Max:\n\t\tcase OBSThemeVariable::Min:\n\t\t\tif (var.value.toStringList().contains(\"obsFontScale\")) {\n\t\t\t\tcurrentTheme->usesFontScale = true;\n\t\t\t}\n\t\t\tif (var.value.toStringList().contains(\"obsPadding\")) {\n\t\t\t\tcurrentTheme->usesDensity = true;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst QString stylesheet = PrepareQSS(vars, contents);\n\tconst QPalette palette = PreparePalette(vars, defaultPalette);\n\tsetPalette(palette);\n\tsetStyleSheet(stylesheet);\n\n#ifdef _DEBUG\n\t/* Write resulting QSS to file in config dir \"themes\" folder. */\n\tstring filename(\"obs-studio/themes/\");\n\tfilename += theme->id.toStdString();\n\tfilename += \".out\";\n\n\tfilesystem::path debugOut;\n\tchar configPath[512];\n\tif (GetAppConfigPath(configPath, sizeof(configPath), filename.c_str())) {\n\t\tdebugOut = absolute(filesystem::u8path(configPath));\n\t\tfilesystem::create_directories(debugOut.parent_path());\n\t}\n\n\tQFile debugFile(debugOut);\n\tif (debugFile.open(QIODeviceBase::WriteOnly)) {\n\t\tdebugFile.write(stylesheet.toUtf8());\n\t\tdebugFile.flush();\n\t}\n#endif\n\n#ifdef __APPLE__\n\tSetMacOSDarkMode(theme->isDark);\n#endif\n\n\temit StyleChanged();\n\n\tif (themeWatcher) {\n\t\tthemeWatcher->addPaths(filenames);\n\t\t/* Give it 250 ms before re-enabling the watcher to prevent too\n\t\t * many reloads when edited with an auto-saving IDE. */\n\t\tQTimer::singleShot(250, this, [&] { themeWatcher->blockSignals(false); });\n\t}\n\n\treturn true;\n}\n\nvoid OBSApp::themeFileChanged(const QString &path)\n{\n\tthemeWatcher->blockSignals(true);\n\tblog(LOG_INFO, \"Theme file \\\"%s\\\" changed, reloading...\", QT_TO_UTF8(path));\n\tSetTheme(currentTheme->id);\n}\n\nstatic map<string, string> themeMigrations = {\n\t{\"Yami\", DEFAULT_THEME},\n\t{\"Grey\", \"com.obsproject.Yami.Grey\"},\n\t{\"Rachni\", \"com.obsproject.Yami.Rachni\"},\n\t{\"Light\", \"com.obsproject.Yami.Light\"},\n\t{\"Dark\", \"com.obsproject.Yami.Classic\"},\n\t{\"Acri\", \"com.obsproject.Yami.Acri\"},\n\t{\"System\", \"com.obsproject.System\"},\n};\n\nbool OBSApp::InitTheme()\n{\n\tdefaultPalette = palette();\n#if !defined(_WIN32) && !defined(__APPLE__)\n\tsetStyle(new OBSProxyStyle(\"Fusion\"));\n#else\n\tsetStyle(new OBSProxyStyle());\n#endif\n\n\t/* Set search paths for custom 'theme:' URI prefix */\n\tstring searchDir;\n\tif (GetDataFilePath(\"themes\", searchDir)) {\n\t\tauto installSearchDir = filesystem::u8path(searchDir);\n\t\tQDir::addSearchPath(\"theme\", absolute(installSearchDir));\n\t}\n\n\tchar userDir[512];\n\tif (GetAppConfigPath(userDir, sizeof(userDir), \"obs-studio/themes\")) {\n\t\tauto configSearchDir = filesystem::u8path(userDir);\n\t\tQDir::addSearchPath(\"theme\", absolute(configSearchDir));\n\t}\n\n\t/* Load list of themes and read their metadata */\n\tFindThemes();\n\n\tif (config_get_bool(userConfig, \"Appearance\", \"AutoReload\")) {\n\t\t/* Set up Qt file watcher to automatically reload themes */\n\t\tthemeWatcher = new QFileSystemWatcher(this);\n\t\tconnect(themeWatcher.get(), &QFileSystemWatcher::fileChanged, this, &OBSApp::themeFileChanged);\n\t}\n\n\t/* Migrate old theme config key */\n\tif (config_has_user_value(userConfig, \"General\", \"CurrentTheme3\") &&\n\t    !config_has_user_value(userConfig, \"Appearance\", \"Theme\")) {\n\t\tconst char *old = config_get_string(userConfig, \"General\", \"CurrentTheme3\");\n\n\t\tif (themeMigrations.count(old)) {\n\t\t\tconfig_set_string(userConfig, \"Appearance\", \"Theme\", themeMigrations[old].c_str());\n\t\t}\n\t}\n\n\tQString themeName = config_get_string(userConfig, \"Appearance\", \"Theme\");\n\n\tif (themeName.isEmpty() || !GetTheme(themeName)) {\n\t\tif (!themeName.isEmpty()) {\n\t\t\tblog(LOG_WARNING,\n\t\t\t     \"Loading theme \\\"%s\\\" failed, falling back to \"\n\t\t\t     \"default theme (\\\"%s\\\").\",\n\t\t\t     QT_TO_UTF8(themeName), DEFAULT_THEME);\n\t\t}\n#ifdef _WIN32\n\t\tthemeName = HighContrastEnabled() ? \"com.obsproject.System\" : DEFAULT_THEME;\n#else\n\t\tthemeName = DEFAULT_THEME;\n#endif\n\t}\n\n\tif (!SetTheme(themeName)) {\n\t\tblog(LOG_ERROR,\n\t\t     \"Loading default theme \\\"%s\\\" failed, falling back to \"\n\t\t     \"system theme as last resort.\",\n\t\t     QT_TO_UTF8(themeName));\n\t\treturn SetTheme(\"com.obsproject.System\");\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "frontend/OBSStudioAPI.cpp",
    "content": "#include \"OBSStudioAPI.hpp\"\n\n#include <models/SceneCollection.hpp>\n#include <widgets/OBSBasic.hpp>\n#include <widgets/OBSProjector.hpp>\n\n#include <qt-wrappers.hpp>\n\nextern volatile bool streaming_active;\nextern volatile bool recording_active;\nextern volatile bool recording_paused;\nextern volatile bool replaybuf_active;\nextern volatile bool virtualcam_active;\n\ntemplate<typename T>\ninline size_t GetCallbackIdx(std::vector<OBSStudioCallback<T>> &callbacks, T callback, void *private_data)\n{\n\tfor (size_t i = 0; i < callbacks.size(); i++) {\n\t\tOBSStudioCallback<T> curCB = callbacks[i];\n\t\tif (curCB.callback == callback && curCB.private_data == private_data)\n\t\t\treturn i;\n\t}\n\n\treturn (size_t)-1;\n}\n\nvoid *OBSStudioAPI::obs_frontend_get_main_window()\n{\n\treturn (void *)main;\n}\n\nvoid *OBSStudioAPI::obs_frontend_get_main_window_handle()\n{\n\treturn (void *)main->winId();\n}\n\nvoid *OBSStudioAPI::obs_frontend_get_system_tray()\n{\n\treturn (void *)main->trayIcon.data();\n}\n\nvoid OBSStudioAPI::obs_frontend_get_scenes(struct obs_frontend_source_list *sources)\n{\n\tfor (int i = 0; i < main->ui->scenes->count(); i++) {\n\t\tQListWidgetItem *item = main->ui->scenes->item(i);\n\t\tOBSScene scene = GetOBSRef<OBSScene>(item);\n\t\tobs_source_t *source = obs_scene_get_source(scene);\n\n\t\tif (obs_source_get_ref(source) != nullptr)\n\t\t\tda_push_back(sources->sources, &source);\n\t}\n}\n\nobs_source_t *OBSStudioAPI::obs_frontend_get_current_scene()\n{\n\tif (main->IsPreviewProgramMode()) {\n\t\treturn obs_weak_source_get_source(main->programScene);\n\t} else {\n\t\tOBSSource source = main->GetCurrentSceneSource();\n\t\treturn obs_source_get_ref(source);\n\t}\n}\n\nvoid OBSStudioAPI::obs_frontend_set_current_scene(obs_source_t *scene)\n{\n\tif (main->IsPreviewProgramMode()) {\n\t\tQMetaObject::invokeMethod(main, \"TransitionToScene\", WaitConnection(),\n\t\t\t\t\t  Q_ARG(OBSSource, OBSSource(scene)));\n\t} else {\n\t\tQMetaObject::invokeMethod(main, \"SetCurrentScene\", WaitConnection(), Q_ARG(OBSSource, OBSSource(scene)),\n\t\t\t\t\t  Q_ARG(bool, false));\n\t}\n}\n\nvoid OBSStudioAPI::obs_frontend_get_transitions(struct obs_frontend_source_list *sources)\n{\n\tfor (const auto &[uuid, transition] : main->transitions) {\n\t\tobs_source_t *source = transition;\n\n\t\tif (obs_source_get_ref(source) != nullptr)\n\t\t\tda_push_back(sources->sources, &source);\n\t}\n}\n\nobs_source_t *OBSStudioAPI::obs_frontend_get_current_transition()\n{\n\tOBSSource tr = main->GetCurrentTransition();\n\treturn obs_source_get_ref(tr);\n}\n\nvoid OBSStudioAPI::obs_frontend_set_current_transition(obs_source_t *transition)\n{\n\tQMetaObject::invokeMethod(main, \"SetTransition\", Q_ARG(OBSSource, OBSSource(transition)));\n}\n\nint OBSStudioAPI::obs_frontend_get_transition_duration()\n{\n\treturn main->GetTransitionDuration();\n}\n\nvoid OBSStudioAPI::obs_frontend_set_transition_duration(int duration)\n{\n\tQMetaObject::invokeMethod(main, \"SetTransitionDuration\", Q_ARG(int, duration));\n}\n\nvoid OBSStudioAPI::obs_frontend_release_tbar()\n{\n\tQMetaObject::invokeMethod(main, \"TBarReleased\");\n}\n\nvoid OBSStudioAPI::obs_frontend_set_tbar_position(int position)\n{\n\tQMetaObject::invokeMethod(main, \"TBarChanged\", Q_ARG(int, position));\n}\n\nint OBSStudioAPI::obs_frontend_get_tbar_position()\n{\n\treturn main->tBar->value();\n}\n\nvoid OBSStudioAPI::obs_frontend_get_scene_collections(std::vector<std::string> &strings)\n{\n\tfor (auto &[collectionName, collection] : main->GetSceneCollectionCache()) {\n\t\tstrings.emplace_back(collectionName);\n\t}\n}\n\nchar *OBSStudioAPI::obs_frontend_get_current_scene_collection()\n{\n\ttry {\n\t\tconst OBS::SceneCollection &currentCollection = main->GetCurrentSceneCollection();\n\t\treturn bstrdup(currentCollection.getName().c_str());\n\t} catch (const std::exception &error) {\n\t\tblog(LOG_DEBUG, \"%s\", error.what());\n\t\tblog(LOG_ERROR, \"Failed to get current scene collection name\");\n\t\treturn nullptr;\n\t}\n}\n\nvoid OBSStudioAPI::obs_frontend_set_current_scene_collection(const char *collection)\n{\n\tQList<QAction *> menuActions = main->ui->sceneCollectionMenu->actions();\n\tQString qstrCollection = QT_UTF8(collection);\n\n\tfor (int i = 0; i < menuActions.count(); i++) {\n\t\tQAction *action = menuActions[i];\n\t\tQVariant v = action->property(\"file_name\");\n\n\t\tif (v.typeName() != nullptr) {\n\t\t\tif (action->text() == qstrCollection) {\n\t\t\t\taction->trigger();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool OBSStudioAPI::obs_frontend_add_scene_collection(const char *name)\n{\n\tbool success = false;\n\tQMetaObject::invokeMethod(main, \"CreateNewSceneCollection\", WaitConnection(), Q_RETURN_ARG(bool, success),\n\t\t\t\t  Q_ARG(QString, QT_UTF8(name)));\n\treturn success;\n}\n\nvoid OBSStudioAPI::obs_frontend_get_profiles(std::vector<std::string> &strings)\n{\n\tconst OBSProfileCache &profiles = main->GetProfileCache();\n\n\tfor (auto &[profileName, profile] : profiles) {\n\t\tstrings.emplace_back(profileName);\n\t}\n}\n\nchar *OBSStudioAPI::obs_frontend_get_current_profile()\n{\n\tconst OBSProfile &profile = main->GetCurrentProfile();\n\treturn bstrdup(profile.name.c_str());\n}\n\nchar *OBSStudioAPI::obs_frontend_get_current_profile_path()\n{\n\tconst OBSProfile &profile = main->GetCurrentProfile();\n\n\treturn bstrdup(profile.path.u8string().c_str());\n}\n\nvoid OBSStudioAPI::obs_frontend_set_current_profile(const char *profile)\n{\n\tQList<QAction *> menuActions = main->ui->profileMenu->actions();\n\tQString qstrProfile = QT_UTF8(profile);\n\n\tfor (int i = 0; i < menuActions.count(); i++) {\n\t\tQAction *action = menuActions[i];\n\t\tQVariant v = action->property(\"file_name\");\n\n\t\tif (v.typeName() != nullptr) {\n\t\t\tif (action->text() == qstrProfile) {\n\t\t\t\taction->trigger();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid OBSStudioAPI::obs_frontend_create_profile(const char *name)\n{\n\tQMetaObject::invokeMethod(main, \"CreateNewProfile\", Q_ARG(QString, name));\n}\n\nvoid OBSStudioAPI::obs_frontend_duplicate_profile(const char *name)\n{\n\tQMetaObject::invokeMethod(main, \"CreateDuplicateProfile\", Q_ARG(QString, name));\n}\n\nvoid OBSStudioAPI::obs_frontend_delete_profile(const char *profile)\n{\n\tQMetaObject::invokeMethod(main, \"DeleteProfile\", Q_ARG(QString, profile));\n}\n\nvoid OBSStudioAPI::obs_frontend_streaming_start()\n{\n\tQMetaObject::invokeMethod(main, \"StartStreaming\");\n}\n\nvoid OBSStudioAPI::obs_frontend_streaming_stop()\n{\n\tQMetaObject::invokeMethod(main, \"StopStreaming\");\n}\n\nbool OBSStudioAPI::obs_frontend_streaming_active()\n{\n\treturn os_atomic_load_bool(&streaming_active);\n}\n\nvoid OBSStudioAPI::obs_frontend_recording_start()\n{\n\tQMetaObject::invokeMethod(main, \"StartRecording\");\n}\n\nvoid OBSStudioAPI::obs_frontend_recording_stop()\n{\n\tQMetaObject::invokeMethod(main, \"StopRecording\");\n}\n\nbool OBSStudioAPI::obs_frontend_recording_active()\n{\n\treturn os_atomic_load_bool(&recording_active);\n}\n\nvoid OBSStudioAPI::obs_frontend_recording_pause(bool pause)\n{\n\tQMetaObject::invokeMethod(main, pause ? \"PauseRecording\" : \"UnpauseRecording\");\n}\n\nbool OBSStudioAPI::obs_frontend_recording_paused()\n{\n\treturn os_atomic_load_bool(&recording_paused);\n}\n\nbool OBSStudioAPI::obs_frontend_recording_split_file()\n{\n\tif (os_atomic_load_bool(&recording_active) && !os_atomic_load_bool(&recording_paused)) {\n\t\tproc_handler_t *ph = obs_output_get_proc_handler(main->outputHandler->fileOutput);\n\t\tuint8_t stack[128];\n\t\tcalldata cd;\n\t\tcalldata_init_fixed(&cd, stack, sizeof(stack));\n\t\tproc_handler_call(ph, \"split_file\", &cd);\n\t\tbool result = calldata_bool(&cd, \"split_file_enabled\");\n\t\treturn result;\n\t} else {\n\t\treturn false;\n\t}\n}\n\nbool OBSStudioAPI::obs_frontend_recording_add_chapter(const char *name)\n{\n\tif (!os_atomic_load_bool(&recording_active) || os_atomic_load_bool(&recording_paused))\n\t\treturn false;\n\n\tproc_handler_t *ph = obs_output_get_proc_handler(main->outputHandler->fileOutput);\n\n\tcalldata cd;\n\tcalldata_init(&cd);\n\tcalldata_set_string(&cd, \"chapter_name\", name);\n\tbool result = proc_handler_call(ph, \"add_chapter\", &cd);\n\tcalldata_free(&cd);\n\treturn result;\n}\n\nvoid OBSStudioAPI::obs_frontend_replay_buffer_start()\n{\n\tQMetaObject::invokeMethod(main, \"StartReplayBuffer\");\n}\n\nvoid OBSStudioAPI::obs_frontend_replay_buffer_save()\n{\n\tQMetaObject::invokeMethod(main, \"ReplayBufferSave\");\n}\n\nvoid OBSStudioAPI::obs_frontend_replay_buffer_stop()\n{\n\tQMetaObject::invokeMethod(main, \"StopReplayBuffer\");\n}\n\nbool OBSStudioAPI::obs_frontend_replay_buffer_active()\n{\n\treturn os_atomic_load_bool(&replaybuf_active);\n}\n\nvoid *OBSStudioAPI::obs_frontend_add_tools_menu_qaction(const char *name)\n{\n\tmain->ui->menuTools->setEnabled(true);\n\tQAction *action = main->ui->menuTools->addAction(QT_UTF8(name));\n\taction->setMenuRole(QAction::NoRole);\n\treturn static_cast<void *>(action);\n}\n\nvoid OBSStudioAPI::obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data)\n{\n\tmain->ui->menuTools->setEnabled(true);\n\n\tauto func = [private_data, callback]() {\n\t\tcallback(private_data);\n\t};\n\n\tQAction *action = main->ui->menuTools->addAction(QT_UTF8(name));\n\taction->setMenuRole(QAction::NoRole);\n\tQObject::connect(action, &QAction::triggered, action, func);\n}\n\nbool OBSStudioAPI::obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget)\n{\n\tif (main->IsDockObjectNameUsed(QT_UTF8(id))) {\n\t\tblog(LOG_WARNING,\n\t\t     \"Dock id '%s' already used!  \"\n\t\t     \"Duplicate library?\",\n\t\t     id);\n\t\treturn false;\n\t}\n\n\tOBSDock *dock = new OBSDock(main);\n\tdock->setWidget((QWidget *)widget);\n\tdock->setWindowTitle(QT_UTF8(title));\n\tdock->setObjectName(QT_UTF8(id));\n\n\tmain->AddDockWidget(dock, Qt::RightDockWidgetArea);\n\n\tdock->setVisible(false);\n\tdock->setFloating(true);\n\n\treturn true;\n}\n\nvoid OBSStudioAPI::obs_frontend_remove_dock(const char *id)\n{\n\tmain->RemoveDockWidget(QT_UTF8(id));\n}\n\nbool OBSStudioAPI::obs_frontend_add_custom_qdock(const char *id, void *dock)\n{\n\tif (main->IsDockObjectNameUsed(QT_UTF8(id))) {\n\t\tblog(LOG_WARNING,\n\t\t     \"Dock id '%s' already used!  \"\n\t\t     \"Duplicate library?\",\n\t\t     id);\n\t\treturn false;\n\t}\n\n\tQDockWidget *d = static_cast<QDockWidget *>(dock);\n\td->setObjectName(QT_UTF8(id));\n\n\tmain->AddCustomDockWidget(d);\n\n\treturn true;\n}\n\nvoid OBSStudioAPI::obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data)\n{\n\tsize_t idx = GetCallbackIdx(callbacks, callback, private_data);\n\tif (idx == (size_t)-1)\n\t\tcallbacks.emplace_back(callback, private_data);\n}\n\nvoid OBSStudioAPI::obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data)\n{\n\tsize_t idx = GetCallbackIdx(callbacks, callback, private_data);\n\tif (idx == (size_t)-1)\n\t\treturn;\n\n\tcallbacks.erase(callbacks.begin() + idx);\n}\n\nobs_output_t *OBSStudioAPI::obs_frontend_get_streaming_output()\n{\n\tauto multitrackVideo = main->outputHandler->multitrackVideo.get();\n\tauto mtvOutput = multitrackVideo ? obs_output_get_ref(multitrackVideo->StreamingOutput()) : nullptr;\n\tif (mtvOutput)\n\t\treturn mtvOutput;\n\n\tOBSOutput output = main->outputHandler->streamOutput.Get();\n\treturn obs_output_get_ref(output);\n}\n\nobs_output_t *OBSStudioAPI::obs_frontend_get_recording_output()\n{\n\tOBSOutput out = main->outputHandler->fileOutput.Get();\n\treturn obs_output_get_ref(out);\n}\n\nobs_output_t *OBSStudioAPI::obs_frontend_get_replay_buffer_output()\n{\n\tOBSOutput out = main->outputHandler->replayBuffer.Get();\n\treturn obs_output_get_ref(out);\n}\n\nconfig_t *OBSStudioAPI::obs_frontend_get_profile_config()\n{\n\treturn main->activeConfiguration;\n}\n\nconfig_t *OBSStudioAPI::obs_frontend_get_app_config()\n{\n\treturn App()->GetAppConfig();\n}\n\nconfig_t *OBSStudioAPI::obs_frontend_get_user_config()\n{\n\treturn App()->GetUserConfig();\n}\n\nvoid OBSStudioAPI::obs_frontend_open_projector(const char *type, int monitor, const char *geometry, const char *name)\n{\n\tSavedProjectorInfo proj = {\n\t\tProjectorType::Preview,\n\t\tmonitor,\n\t\tgeometry ? geometry : \"\",\n\t\tname ? name : \"\",\n\t};\n\tif (type) {\n\t\tif (astrcmpi(type, \"Source\") == 0)\n\t\t\tproj.type = ProjectorType::Source;\n\t\telse if (astrcmpi(type, \"Scene\") == 0)\n\t\t\tproj.type = ProjectorType::Scene;\n\t\telse if (astrcmpi(type, \"StudioProgram\") == 0)\n\t\t\tproj.type = ProjectorType::StudioProgram;\n\t\telse if (astrcmpi(type, \"Multiview\") == 0)\n\t\t\tproj.type = ProjectorType::Multiview;\n\t}\n\tQMetaObject::invokeMethod(main, \"OpenSavedProjector\", WaitConnection(), Q_ARG(SavedProjectorInfo *, &proj));\n}\n\nvoid OBSStudioAPI::obs_frontend_save()\n{\n\tmain->SaveProject();\n}\n\nvoid OBSStudioAPI::obs_frontend_defer_save_begin()\n{\n\tQMetaObject::invokeMethod(main, \"DeferSaveBegin\");\n}\n\nvoid OBSStudioAPI::obs_frontend_defer_save_end()\n{\n\tQMetaObject::invokeMethod(main, \"DeferSaveEnd\");\n}\n\nvoid OBSStudioAPI::obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data)\n{\n\tsize_t idx = GetCallbackIdx(saveCallbacks, callback, private_data);\n\tif (idx == (size_t)-1)\n\t\tsaveCallbacks.emplace_back(callback, private_data);\n}\n\nvoid OBSStudioAPI::obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data)\n{\n\tsize_t idx = GetCallbackIdx(saveCallbacks, callback, private_data);\n\tif (idx == (size_t)-1)\n\t\treturn;\n\n\tsaveCallbacks.erase(saveCallbacks.begin() + idx);\n}\n\nvoid OBSStudioAPI::obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data)\n{\n\tsize_t idx = GetCallbackIdx(preloadCallbacks, callback, private_data);\n\tif (idx == (size_t)-1)\n\t\tpreloadCallbacks.emplace_back(callback, private_data);\n}\n\nvoid OBSStudioAPI::obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data)\n{\n\tsize_t idx = GetCallbackIdx(preloadCallbacks, callback, private_data);\n\tif (idx == (size_t)-1)\n\t\treturn;\n\n\tpreloadCallbacks.erase(preloadCallbacks.begin() + idx);\n}\n\nvoid OBSStudioAPI::obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate)\n{\n\tApp()->PushUITranslation(translate);\n}\n\nvoid OBSStudioAPI::obs_frontend_pop_ui_translation()\n{\n\tApp()->PopUITranslation();\n}\n\nvoid OBSStudioAPI::obs_frontend_set_streaming_service(obs_service_t *service)\n{\n\tmain->SetService(service);\n}\n\nobs_service_t *OBSStudioAPI::obs_frontend_get_streaming_service()\n{\n\treturn main->GetService();\n}\n\nvoid OBSStudioAPI::obs_frontend_save_streaming_service()\n{\n\tmain->SaveService();\n}\n\nbool OBSStudioAPI::obs_frontend_preview_program_mode_active()\n{\n\treturn main->IsPreviewProgramMode();\n}\n\nvoid OBSStudioAPI::obs_frontend_set_preview_program_mode(bool enable)\n{\n\tmain->SetPreviewProgramMode(enable);\n}\n\nvoid OBSStudioAPI::obs_frontend_preview_program_trigger_transition()\n{\n\tQMetaObject::invokeMethod(main, \"TransitionClicked\");\n}\n\nbool OBSStudioAPI::obs_frontend_preview_enabled()\n{\n\treturn main->previewEnabled;\n}\n\nvoid OBSStudioAPI::obs_frontend_set_preview_enabled(bool enable)\n{\n\tif (main->previewEnabled != enable)\n\t\tmain->EnablePreviewDisplay(enable);\n}\n\nobs_source_t *OBSStudioAPI::obs_frontend_get_current_preview_scene()\n{\n\tif (main->IsPreviewProgramMode()) {\n\t\tOBSSource source = main->GetCurrentSceneSource();\n\t\treturn obs_source_get_ref(source);\n\t}\n\n\treturn nullptr;\n}\n\nvoid OBSStudioAPI::obs_frontend_set_current_preview_scene(obs_source_t *scene)\n{\n\tif (main->IsPreviewProgramMode()) {\n\t\tQMetaObject::invokeMethod(main, \"SetCurrentScene\", Q_ARG(OBSSource, OBSSource(scene)),\n\t\t\t\t\t  Q_ARG(bool, false));\n\t}\n}\n\nvoid OBSStudioAPI::obs_frontend_take_screenshot()\n{\n\tQMetaObject::invokeMethod(main, \"Screenshot\");\n}\n\nvoid OBSStudioAPI::obs_frontend_take_source_screenshot(obs_source_t *source)\n{\n\tQMetaObject::invokeMethod(main, \"Screenshot\", Q_ARG(OBSSource, OBSSource(source)));\n}\n\nobs_output_t *OBSStudioAPI::obs_frontend_get_virtualcam_output()\n{\n\tOBSOutput output = main->outputHandler->virtualCam.Get();\n\treturn obs_output_get_ref(output);\n}\n\nvoid OBSStudioAPI::obs_frontend_start_virtualcam()\n{\n\tQMetaObject::invokeMethod(main, \"StartVirtualCam\");\n}\n\nvoid OBSStudioAPI::obs_frontend_stop_virtualcam()\n{\n\tQMetaObject::invokeMethod(main, \"StopVirtualCam\");\n}\n\nbool OBSStudioAPI::obs_frontend_virtualcam_active()\n{\n\treturn os_atomic_load_bool(&virtualcam_active);\n}\n\nvoid OBSStudioAPI::obs_frontend_reset_video()\n{\n\tmain->ResetVideo();\n}\n\nvoid OBSStudioAPI::obs_frontend_open_source_properties(obs_source_t *source)\n{\n\tQMetaObject::invokeMethod(main, \"OpenProperties\", Q_ARG(OBSSource, OBSSource(source)));\n}\n\nvoid OBSStudioAPI::obs_frontend_open_source_filters(obs_source_t *source)\n{\n\tQMetaObject::invokeMethod(main, \"OpenFilters\", Q_ARG(OBSSource, OBSSource(source)));\n}\n\nvoid OBSStudioAPI::obs_frontend_open_source_interaction(obs_source_t *source)\n{\n\tQMetaObject::invokeMethod(main, \"OpenInteraction\", Q_ARG(OBSSource, OBSSource(source)));\n}\n\nvoid OBSStudioAPI::obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item)\n{\n\tQMetaObject::invokeMethod(main, \"OpenEditTransform\", Q_ARG(OBSSceneItem, OBSSceneItem(item)));\n}\n\nchar *OBSStudioAPI::obs_frontend_get_current_record_output_path()\n{\n\tconst char *recordOutputPath = main->GetCurrentOutputPath();\n\n\treturn bstrdup(recordOutputPath);\n}\n\nconst char *OBSStudioAPI::obs_frontend_get_locale_string(const char *string)\n{\n\treturn Str(string);\n}\n\nbool OBSStudioAPI::obs_frontend_is_theme_dark()\n{\n\treturn App()->IsThemeDark();\n}\n\nchar *OBSStudioAPI::obs_frontend_get_last_recording()\n{\n\treturn bstrdup(main->outputHandler->lastRecordingPath.c_str());\n}\n\nchar *OBSStudioAPI::obs_frontend_get_last_screenshot()\n{\n\treturn bstrdup(main->lastScreenshot.c_str());\n}\n\nchar *OBSStudioAPI::obs_frontend_get_last_replay()\n{\n\treturn bstrdup(main->lastReplay.c_str());\n}\n\nvoid OBSStudioAPI::obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo, const undo_redo_cb redo,\n\t\t\t\t\t\t     const char *undo_data, const char *redo_data, bool repeatable)\n{\n\tmain->undo_s.add_action(\n\t\tname, [undo](const std::string &data) { undo(data.c_str()); },\n\t\t[redo](const std::string &data) { redo(data.c_str()); }, undo_data, redo_data, repeatable);\n}\n\nvoid OBSStudioAPI::obs_frontend_get_canvases(obs_frontend_canvas_list *canvas_list)\n{\n\tfor (const auto &canvas : main->canvases) {\n\t\tobs_canvas_t *ref = obs_canvas_get_ref(canvas);\n\t\tif (ref)\n\t\t\tda_push_back(canvas_list->canvases, &ref);\n\t}\n}\n\nobs_canvas_t *OBSStudioAPI::obs_frontend_add_canvas(const char *name, obs_video_info *ovi, int flags)\n{\n\tauto &canvas = main->AddCanvas(std::string(name), ovi, flags);\n\treturn obs_canvas_get_ref(canvas);\n}\n\nbool OBSStudioAPI::obs_frontend_remove_canvas(obs_canvas_t *canvas)\n{\n\treturn main->RemoveCanvas(canvas);\n}\n\nvoid OBSStudioAPI::on_load(obs_data_t *settings)\n{\n\tfor (size_t i = saveCallbacks.size(); i > 0; i--) {\n\t\tauto cb = saveCallbacks[i - 1];\n\t\tcb.callback(settings, false, cb.private_data);\n\t}\n}\n\nvoid OBSStudioAPI::on_preload(obs_data_t *settings)\n{\n\tfor (size_t i = preloadCallbacks.size(); i > 0; i--) {\n\t\tauto cb = preloadCallbacks[i - 1];\n\t\tcb.callback(settings, false, cb.private_data);\n\t}\n}\n\nvoid OBSStudioAPI::on_save(obs_data_t *settings)\n{\n\tfor (size_t i = saveCallbacks.size(); i > 0; i--) {\n\t\tauto cb = saveCallbacks[i - 1];\n\t\tcb.callback(settings, true, cb.private_data);\n\t}\n}\n\nvoid OBSStudioAPI::on_event(enum obs_frontend_event event)\n{\n\tif (main->disableSaving && event != OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP &&\n\t    event != OBS_FRONTEND_EVENT_EXIT)\n\t\treturn;\n\n\tfor (size_t i = callbacks.size(); i > 0; i--) {\n\t\tauto cb = callbacks[i - 1];\n\t\tcb.callback(event, cb.private_data);\n\t}\n}\n\nobs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main)\n{\n\tobs_frontend_callbacks *api = new OBSStudioAPI(main);\n\tobs_frontend_set_callbacks_internal(api);\n\treturn api;\n}\n"
  },
  {
    "path": "frontend/OBSStudioAPI.hpp",
    "content": "/******************************************************************************\n Copyright (C) 2024 by Patrick Heyer <opensource@patrickheyer.com>\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>.\n ******************************************************************************/\n\n#pragma once\n\n#include <obs-frontend-internal.hpp>\n\nclass OBSBasic;\n\ntemplate<typename T> struct OBSStudioCallback {\n\tT callback;\n\tvoid *private_data;\n\n\tinline OBSStudioCallback(T cb, void *p) : callback(cb), private_data(p) {}\n};\n\nstruct OBSStudioAPI : obs_frontend_callbacks {\n\tOBSBasic *main;\n\tstd::vector<OBSStudioCallback<obs_frontend_event_cb>> callbacks;\n\tstd::vector<OBSStudioCallback<obs_frontend_save_cb>> saveCallbacks;\n\tstd::vector<OBSStudioCallback<obs_frontend_save_cb>> preloadCallbacks;\n\n\tinline OBSStudioAPI(OBSBasic *main_) : main(main_) {}\n\n\tvoid *obs_frontend_get_main_window(void) override;\n\n\tvoid *obs_frontend_get_main_window_handle(void) override;\n\n\tvoid *obs_frontend_get_system_tray(void) override;\n\n\tvoid obs_frontend_get_scenes(struct obs_frontend_source_list *sources) override;\n\n\tobs_source_t *obs_frontend_get_current_scene(void) override;\n\n\tvoid obs_frontend_set_current_scene(obs_source_t *scene) override;\n\n\tvoid obs_frontend_get_transitions(struct obs_frontend_source_list *sources) override;\n\n\tobs_source_t *obs_frontend_get_current_transition(void) override;\n\n\tvoid obs_frontend_set_current_transition(obs_source_t *transition) override;\n\n\tint obs_frontend_get_transition_duration(void) override;\n\n\tvoid obs_frontend_set_transition_duration(int duration) override;\n\n\tvoid obs_frontend_release_tbar(void) override;\n\n\tvoid obs_frontend_set_tbar_position(int position) override;\n\n\tint obs_frontend_get_tbar_position(void) override;\n\n\tvoid obs_frontend_get_scene_collections(std::vector<std::string> &strings) override;\n\n\tchar *obs_frontend_get_current_scene_collection(void) override;\n\n\tvoid obs_frontend_set_current_scene_collection(const char *collection) override;\n\n\tbool obs_frontend_add_scene_collection(const char *name) override;\n\n\tvoid obs_frontend_get_profiles(std::vector<std::string> &strings) override;\n\n\tchar *obs_frontend_get_current_profile(void) override;\n\n\tchar *obs_frontend_get_current_profile_path(void) override;\n\n\tvoid obs_frontend_set_current_profile(const char *profile) override;\n\n\tvoid obs_frontend_create_profile(const char *name) override;\n\n\tvoid obs_frontend_duplicate_profile(const char *name) override;\n\n\tvoid obs_frontend_delete_profile(const char *profile) override;\n\n\tvoid obs_frontend_streaming_start(void) override;\n\n\tvoid obs_frontend_streaming_stop(void) override;\n\n\tbool obs_frontend_streaming_active(void) override;\n\n\tvoid obs_frontend_recording_start(void) override;\n\n\tvoid obs_frontend_recording_stop(void) override;\n\n\tbool obs_frontend_recording_active(void) override;\n\n\tvoid obs_frontend_recording_pause(bool pause) override;\n\n\tbool obs_frontend_recording_paused(void) override;\n\n\tbool obs_frontend_recording_split_file(void) override;\n\n\tbool obs_frontend_recording_add_chapter(const char *name) override;\n\n\tvoid obs_frontend_replay_buffer_start(void) override;\n\n\tvoid obs_frontend_replay_buffer_save(void) override;\n\n\tvoid obs_frontend_replay_buffer_stop(void) override;\n\n\tbool obs_frontend_replay_buffer_active(void) override;\n\n\tvoid *obs_frontend_add_tools_menu_qaction(const char *name) override;\n\n\tvoid obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data) override;\n\n\tbool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget) override;\n\n\tvoid obs_frontend_remove_dock(const char *id) override;\n\n\tbool obs_frontend_add_custom_qdock(const char *id, void *dock) override;\n\n\tvoid obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) override;\n\n\tvoid obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data) override;\n\n\tobs_output_t *obs_frontend_get_streaming_output(void) override;\n\n\tobs_output_t *obs_frontend_get_recording_output(void) override;\n\n\tobs_output_t *obs_frontend_get_replay_buffer_output(void) override;\n\n\tconfig_t *obs_frontend_get_profile_config(void) override;\n\n\tconfig_t *obs_frontend_get_app_config(void) override;\n\n\tconfig_t *obs_frontend_get_user_config(void) override;\n\n\tvoid obs_frontend_open_projector(const char *type, int monitor, const char *geometry,\n\t\t\t\t\t const char *name) override;\n\n\tvoid obs_frontend_save(void) override;\n\n\tvoid obs_frontend_defer_save_begin(void) override;\n\n\tvoid obs_frontend_defer_save_end(void) override;\n\n\tvoid obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data) override;\n\n\tvoid obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data) override;\n\n\tvoid obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data) override;\n\n\tvoid obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data) override;\n\n\tvoid obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate) override;\n\n\tvoid obs_frontend_pop_ui_translation(void) override;\n\n\tvoid obs_frontend_set_streaming_service(obs_service_t *service) override;\n\n\tobs_service_t *obs_frontend_get_streaming_service(void) override;\n\n\tvoid obs_frontend_save_streaming_service(void) override;\n\n\tbool obs_frontend_preview_program_mode_active(void) override;\n\n\tvoid obs_frontend_set_preview_program_mode(bool enable) override;\n\n\tvoid obs_frontend_preview_program_trigger_transition(void) override;\n\n\tbool obs_frontend_preview_enabled(void) override;\n\n\tvoid obs_frontend_set_preview_enabled(bool enable) override;\n\n\tobs_source_t *obs_frontend_get_current_preview_scene(void) override;\n\n\tvoid obs_frontend_set_current_preview_scene(obs_source_t *scene) override;\n\n\tvoid obs_frontend_take_screenshot(void) override;\n\n\tvoid obs_frontend_take_source_screenshot(obs_source_t *source) override;\n\n\tobs_output_t *obs_frontend_get_virtualcam_output(void) override;\n\n\tvoid obs_frontend_start_virtualcam(void) override;\n\n\tvoid obs_frontend_stop_virtualcam(void) override;\n\n\tbool obs_frontend_virtualcam_active(void) override;\n\n\tvoid obs_frontend_reset_video(void) override;\n\n\tvoid obs_frontend_open_source_properties(obs_source_t *source) override;\n\n\tvoid obs_frontend_open_source_filters(obs_source_t *source) override;\n\n\tvoid obs_frontend_open_source_interaction(obs_source_t *source) override;\n\n\tvoid obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item) override;\n\n\tchar *obs_frontend_get_current_record_output_path(void) override;\n\n\tconst char *obs_frontend_get_locale_string(const char *string) override;\n\n\tbool obs_frontend_is_theme_dark(void) override;\n\n\tchar *obs_frontend_get_last_recording(void) override;\n\n\tchar *obs_frontend_get_last_screenshot(void) override;\n\n\tchar *obs_frontend_get_last_replay(void) override;\n\n\tvoid obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo, const undo_redo_cb redo,\n\t\t\t\t\t       const char *undo_data, const char *redo_data, bool repeatable) override;\n\n\tvoid obs_frontend_get_canvases(obs_frontend_canvas_list *canvas_list) override;\n\n\tobs_canvas_t *obs_frontend_add_canvas(const char *name, obs_video_info *ovi, int flags) override;\n\n\tbool obs_frontend_remove_canvas(obs_canvas_t *canvas) override;\n\n\tvoid on_load(obs_data_t *settings) override;\n\n\tvoid on_preload(obs_data_t *settings) override;\n\n\tvoid on_save(obs_data_t *settings) override;\n\n\tvoid on_event(enum obs_frontend_event event) override;\n};\n\nobs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main);\n"
  },
  {
    "path": "frontend/api/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nadd_library(obs-frontend-api SHARED)\nadd_library(OBS::frontend-api ALIAS obs-frontend-api)\n\ntarget_sources(obs-frontend-api PRIVATE obs-frontend-api.h obs-frontend-api.cpp obs-frontend-internal.hpp)\n\ntarget_link_libraries(obs-frontend-api PRIVATE OBS::libobs)\n\ntarget_include_directories(obs-frontend-api PUBLIC \"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\")\n\nif(OS_WINDOWS)\n  configure_file(cmake/windows/obs-module.rc.in obs-frontend-api.rc)\n  target_sources(obs-frontend-api PRIVATE obs-frontend-api.rc)\nelseif(OS_MACOS)\n  set_target_properties(obs-frontend-api PROPERTIES SOVERSION 1)\nendif()\n\nset_target_properties_obs(obs-frontend-api PROPERTIES FOLDER frontend PUBLIC_HEADER obs-frontend-api.h)\n\nif(OS_WINDOWS OR OS_MACOS)\n  set_property(TARGET obs-frontend-api PROPERTY PREFIX \"\")\nendif()\n\ntarget_export(obs-frontend-api)\n"
  },
  {
    "path": "frontend/api/cmake/linux/obs-frontend-api.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=${prefix}\nlibdir=${prefix}/lib\nincludedir=${prefix}/include\n\nName: obs-frontend-api\nDescription: OBS Studio frontend API library\nVersion: @OBS_VERSION_CANONICAL@\n\nRequires:\nLibs: -L${libdir} -lobs-frontend-api -lobs\nLibs.private: -lobs -lm -pthread\nCflags: -I${includedir} -std=gnu@CMAKE_C_STANDARD@ -fPIC -fvisibility=hidden -fopenmp-simd -Werror\n"
  },
  {
    "path": "frontend/api/cmake/obs-frontend-apiConfig.cmake.in",
    "content": "@PACKAGE_INIT@\n\ninclude(CMakeFindDependencyMacro)\n\nfind_dependency(libobs REQUIRED)\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake\")\ncheck_required_components(\"@PROJECT_NAME@\")\n"
  },
  {
    "path": "frontend/api/cmake/windows/obs-module.rc.in",
    "content": "1 VERSIONINFO\nFILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"040904B0\"\n    BEGIN\n      VALUE \"CompanyName\", \"${OBS_COMPANY_NAME}\"\n      VALUE \"FileDescription\", \"OBS Frontend API\"\n      VALUE \"FileVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"ProductName\", \"${OBS_PRODUCT_NAME}\"\n      VALUE \"ProductVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"Comments\", \"${OBS_COMMENTS}\"\n      VALUE \"LegalCopyright\", \"${OBS_LEGAL_COPYRIGHT}\"\n      VALUE \"InternalName\", \"obs-frontend-api\"\n      VALUE \"OriginalFilename\", \"obs-frontend-api\"\n    END\n  END\n\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0409, 0x04B0\n  END\nEND\n"
  },
  {
    "path": "frontend/api/obs-frontend-api.cpp",
    "content": "#include \"obs-frontend-internal.hpp\"\n#include <memory>\n\nusing namespace std;\n\nstatic unique_ptr<obs_frontend_callbacks> c;\n\nvoid obs_frontend_set_callbacks_internal(obs_frontend_callbacks *callbacks)\n{\n\tc.reset(callbacks);\n}\n\nstatic inline bool callbacks_valid_(const char *func_name)\n{\n\tif (!c) {\n\t\tblog(LOG_ERROR, \"Tried to call %s with no callbacks!\", func_name);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n#define callbacks_valid() callbacks_valid_(__FUNCTION__)\n\nstatic char **convert_string_list(vector<string> &strings)\n{\n\tsize_t size = 0;\n\tsize_t string_data_offset = (strings.size() + 1) * sizeof(char *);\n\tuint8_t *out;\n\tchar **ptr_list;\n\tchar *string_data;\n\n\tsize += string_data_offset;\n\n\tfor (auto &str : strings)\n\t\tsize += str.size() + 1;\n\n\tif (!size)\n\t\treturn 0;\n\n\tout = (uint8_t *)bmalloc(size);\n\tptr_list = (char **)out;\n\tstring_data = (char *)(out + string_data_offset);\n\n\tfor (auto &str : strings) {\n\t\t*ptr_list = string_data;\n\t\tmemcpy(string_data, str.c_str(), str.size() + 1);\n\n\t\tptr_list++;\n\t\tstring_data += str.size() + 1;\n\t}\n\n\t*ptr_list = nullptr;\n\treturn (char **)out;\n}\n\n/* ------------------------------------------------------------------------- */\n\nvoid *obs_frontend_get_main_window(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_main_window() : nullptr;\n}\n\nvoid *obs_frontend_get_main_window_handle(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_main_window_handle() : nullptr;\n}\n\nvoid *obs_frontend_get_system_tray(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_system_tray() : nullptr;\n}\n\nchar **obs_frontend_get_scene_names(void)\n{\n\tif (!callbacks_valid())\n\t\treturn NULL;\n\n\tstruct obs_frontend_source_list sources = {};\n\tvector<string> names;\n\tc->obs_frontend_get_scenes(&sources);\n\n\tfor (size_t i = 0; i < sources.sources.num; i++) {\n\t\tobs_source_t *source = sources.sources.array[i];\n\t\tconst char *name = obs_source_get_name(source);\n\t\tnames.emplace_back(name);\n\t}\n\n\tobs_frontend_source_list_free(&sources);\n\treturn convert_string_list(names);\n}\n\nvoid obs_frontend_get_scenes(struct obs_frontend_source_list *sources)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_get_scenes(sources);\n}\n\nobs_source_t *obs_frontend_get_current_scene(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_current_scene() : nullptr;\n}\n\nvoid obs_frontend_set_current_scene(obs_source_t *scene)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_set_current_scene(scene);\n}\n\nvoid obs_frontend_get_transitions(struct obs_frontend_source_list *sources)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_get_transitions(sources);\n}\n\nobs_source_t *obs_frontend_get_current_transition(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_current_transition() : nullptr;\n}\n\nvoid obs_frontend_set_current_transition(obs_source_t *transition)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_set_current_transition(transition);\n}\n\nint obs_frontend_get_transition_duration(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_transition_duration() : 0;\n}\n\nvoid obs_frontend_set_transition_duration(int duration)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_set_transition_duration(duration);\n}\n\nvoid obs_frontend_release_tbar(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_release_tbar();\n}\n\nint obs_frontend_get_tbar_position(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_tbar_position() : 0;\n}\n\nvoid obs_frontend_set_tbar_position(int position)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_set_tbar_position(position);\n}\n\nchar **obs_frontend_get_scene_collections(void)\n{\n\tif (!callbacks_valid())\n\t\treturn nullptr;\n\n\tvector<string> strings;\n\tc->obs_frontend_get_scene_collections(strings);\n\treturn convert_string_list(strings);\n}\n\nchar *obs_frontend_get_current_scene_collection(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_current_scene_collection() : nullptr;\n}\n\nvoid obs_frontend_set_current_scene_collection(const char *collection)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_set_current_scene_collection(collection);\n}\n\nbool obs_frontend_add_scene_collection(const char *name)\n{\n\treturn callbacks_valid() ? c->obs_frontend_add_scene_collection(name) : false;\n}\n\nchar **obs_frontend_get_profiles(void)\n{\n\tif (!callbacks_valid())\n\t\treturn nullptr;\n\n\tvector<string> strings;\n\tc->obs_frontend_get_profiles(strings);\n\treturn convert_string_list(strings);\n}\n\nchar *obs_frontend_get_current_profile(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_current_profile() : nullptr;\n}\n\nchar *obs_frontend_get_current_profile_path(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_current_profile_path() : nullptr;\n}\n\nvoid obs_frontend_set_current_profile(const char *profile)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_set_current_profile(profile);\n}\n\nvoid obs_frontend_create_profile(const char *name)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_create_profile(name);\n}\n\nvoid obs_frontend_duplicate_profile(const char *name)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_duplicate_profile(name);\n}\n\nvoid obs_frontend_delete_profile(const char *profile)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_delete_profile(profile);\n}\n\nvoid obs_frontend_streaming_start(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_streaming_start();\n}\n\nvoid obs_frontend_streaming_stop(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_streaming_stop();\n}\n\nbool obs_frontend_streaming_active(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_streaming_active() : false;\n}\n\nvoid obs_frontend_recording_start(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_recording_start();\n}\n\nvoid obs_frontend_recording_stop(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_recording_stop();\n}\n\nbool obs_frontend_recording_active(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_recording_active() : false;\n}\n\nvoid obs_frontend_recording_pause(bool pause)\n{\n\tif (!!callbacks_valid())\n\t\tc->obs_frontend_recording_pause(pause);\n}\n\nbool obs_frontend_recording_paused(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_recording_paused() : false;\n}\n\nbool obs_frontend_recording_split_file(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_recording_split_file() : false;\n}\n\nbool obs_frontend_recording_add_chapter(const char *name)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_recording_add_chapter(name) : false;\n}\n\nvoid obs_frontend_replay_buffer_start(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_replay_buffer_start();\n}\n\nvoid obs_frontend_replay_buffer_save(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_replay_buffer_save();\n}\n\nvoid obs_frontend_replay_buffer_stop(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_replay_buffer_stop();\n}\n\nbool obs_frontend_replay_buffer_active(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_replay_buffer_active() : false;\n}\n\nvoid *obs_frontend_add_tools_menu_qaction(const char *name)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_add_tools_menu_qaction(name) : nullptr;\n}\n\nvoid obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_add_tools_menu_item(name, callback, private_data);\n}\n\nbool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_add_dock_by_id(id, title, widget) : false;\n}\n\nvoid obs_frontend_remove_dock(const char *id)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_remove_dock(id);\n}\n\nbool obs_frontend_add_custom_qdock(const char *id, void *dock)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_add_custom_qdock(id, dock) : false;\n}\n\nvoid obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_add_event_callback(callback, private_data);\n}\n\nvoid obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_remove_event_callback(callback, private_data);\n}\n\nobs_output_t *obs_frontend_get_streaming_output(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_streaming_output() : nullptr;\n}\n\nobs_output_t *obs_frontend_get_recording_output(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_recording_output() : nullptr;\n}\n\nobs_output_t *obs_frontend_get_replay_buffer_output(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_replay_buffer_output() : nullptr;\n}\n\nconfig_t *obs_frontend_get_profile_config(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_profile_config() : nullptr;\n}\n\nconfig_t *obs_frontend_get_app_config(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_app_config() : nullptr;\n}\n\nconfig_t *obs_frontend_get_user_config(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_user_config() : nullptr;\n}\n\nconfig_t *obs_frontend_get_global_config(void)\n{\n\tblog(LOG_WARNING, \"DEPRECATION: obs_frontend_get_global_config is deprecated. \"\n\t\t\t  \"Use obs_frontend_get_app_config or obs_frontend_get_user_config explicitly instead.\");\n\treturn !!callbacks_valid() ? c->obs_frontend_get_app_config() : nullptr;\n}\n\nvoid obs_frontend_open_projector(const char *type, int monitor, const char *geometry, const char *name)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_open_projector(type, monitor, geometry, name);\n}\n\nvoid obs_frontend_save(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_save();\n}\n\nvoid obs_frontend_defer_save_begin(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_defer_save_begin();\n}\n\nvoid obs_frontend_defer_save_end(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_defer_save_end();\n}\n\nvoid obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_add_save_callback(callback, private_data);\n}\n\nvoid obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_remove_save_callback(callback, private_data);\n}\n\nvoid obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_add_preload_callback(callback, private_data);\n}\n\nvoid obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_remove_preload_callback(callback, private_data);\n}\n\nvoid obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_push_ui_translation(translate);\n}\n\nvoid obs_frontend_pop_ui_translation(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_pop_ui_translation();\n}\n\nobs_service_t *obs_frontend_get_streaming_service(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_streaming_service() : nullptr;\n}\n\nvoid obs_frontend_set_streaming_service(obs_service_t *service)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_set_streaming_service(service);\n}\n\nvoid obs_frontend_save_streaming_service(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_save_streaming_service();\n}\n\nbool obs_frontend_preview_program_mode_active(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_preview_program_mode_active() : false;\n}\n\nvoid obs_frontend_set_preview_program_mode(bool enable)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_set_preview_program_mode(enable);\n}\n\nvoid obs_frontend_preview_program_trigger_transition(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_preview_program_trigger_transition();\n}\n\nbool obs_frontend_preview_enabled(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_preview_enabled() : false;\n}\n\nvoid obs_frontend_set_preview_enabled(bool enable)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_set_preview_enabled(enable);\n}\n\nobs_source_t *obs_frontend_get_current_preview_scene(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_current_preview_scene() : nullptr;\n}\n\nvoid obs_frontend_set_current_preview_scene(obs_source_t *scene)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_set_current_preview_scene(scene);\n}\n\nvoid obs_frontend_take_screenshot(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_take_screenshot();\n}\n\nvoid obs_frontend_take_source_screenshot(obs_source_t *source)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_take_source_screenshot(source);\n}\n\nobs_output_t *obs_frontend_get_virtualcam_output(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_virtualcam_output() : nullptr;\n}\n\nvoid obs_frontend_start_virtualcam(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_start_virtualcam();\n}\n\nvoid obs_frontend_stop_virtualcam(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_stop_virtualcam();\n}\n\nbool obs_frontend_virtualcam_active(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_virtualcam_active() : false;\n}\n\nvoid obs_frontend_reset_video(void)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_reset_video();\n}\n\nvoid obs_frontend_open_source_properties(obs_source_t *source)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_open_source_properties(source);\n}\n\nvoid obs_frontend_open_source_filters(obs_source_t *source)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_open_source_filters(source);\n}\n\nvoid obs_frontend_open_source_interaction(obs_source_t *source)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_open_source_interaction(source);\n}\n\nvoid obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_open_sceneitem_edit_transform(item);\n}\n\nchar *obs_frontend_get_current_record_output_path(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_current_record_output_path() : nullptr;\n}\n\nconst char *obs_frontend_get_locale_string(const char *string)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_locale_string(string) : nullptr;\n}\n\nbool obs_frontend_is_theme_dark(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_is_theme_dark() : false;\n}\n\nchar *obs_frontend_get_last_recording(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_last_recording() : nullptr;\n}\n\nchar *obs_frontend_get_last_screenshot(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_last_screenshot() : nullptr;\n}\n\nchar *obs_frontend_get_last_replay(void)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_get_last_replay() : nullptr;\n}\n\nvoid obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo, const undo_redo_cb redo,\n\t\t\t\t       const char *undo_data, const char *redo_data, bool repeatable)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_add_undo_redo_action(name, undo, redo, undo_data, redo_data, repeatable);\n}\n\nvoid obs_frontend_get_canvases(obs_frontend_canvas_list *canvas_list)\n{\n\tif (callbacks_valid())\n\t\tc->obs_frontend_get_canvases(canvas_list);\n}\n\nobs_canvas_t *obs_frontend_add_canvas(const char *name, obs_video_info *ovi, int flags)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_add_canvas(name, ovi, flags) : nullptr;\n}\n\nbool obs_frontend_remove_canvas(obs_canvas_t *canvas)\n{\n\treturn !!callbacks_valid() ? c->obs_frontend_remove_canvas(canvas) : false;\n}\n"
  },
  {
    "path": "frontend/api/obs-frontend-api.h",
    "content": "#pragma once\n\n#include <obs.h>\n#include <util/darray.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct config_data;\ntypedef struct config_data config_t;\n\nstruct obs_data;\ntypedef struct obs_data obs_data_t;\n\nenum obs_frontend_event {\n\tOBS_FRONTEND_EVENT_STREAMING_STARTING,\n\tOBS_FRONTEND_EVENT_STREAMING_STARTED,\n\tOBS_FRONTEND_EVENT_STREAMING_STOPPING,\n\tOBS_FRONTEND_EVENT_STREAMING_STOPPED,\n\tOBS_FRONTEND_EVENT_RECORDING_STARTING,\n\tOBS_FRONTEND_EVENT_RECORDING_STARTED,\n\tOBS_FRONTEND_EVENT_RECORDING_STOPPING,\n\tOBS_FRONTEND_EVENT_RECORDING_STOPPED,\n\tOBS_FRONTEND_EVENT_SCENE_CHANGED,\n\tOBS_FRONTEND_EVENT_SCENE_LIST_CHANGED,\n\tOBS_FRONTEND_EVENT_TRANSITION_CHANGED,\n\tOBS_FRONTEND_EVENT_TRANSITION_STOPPED,\n\tOBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED,\n\tOBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED,\n\tOBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED,\n\tOBS_FRONTEND_EVENT_PROFILE_CHANGED,\n\tOBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED,\n\tOBS_FRONTEND_EVENT_EXIT,\n\n\tOBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING,\n\tOBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED,\n\tOBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING,\n\tOBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED,\n\n\tOBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED,\n\tOBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED,\n\tOBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED,\n\n\tOBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP,\n\tOBS_FRONTEND_EVENT_FINISHED_LOADING,\n\n\tOBS_FRONTEND_EVENT_RECORDING_PAUSED,\n\tOBS_FRONTEND_EVENT_RECORDING_UNPAUSED,\n\n\tOBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED,\n\tOBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED,\n\n\tOBS_FRONTEND_EVENT_VIRTUALCAM_STARTED,\n\tOBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED,\n\n\tOBS_FRONTEND_EVENT_TBAR_VALUE_CHANGED,\n\tOBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING,\n\tOBS_FRONTEND_EVENT_PROFILE_CHANGING,\n\tOBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN,\n\tOBS_FRONTEND_EVENT_PROFILE_RENAMED,\n\tOBS_FRONTEND_EVENT_SCENE_COLLECTION_RENAMED,\n\tOBS_FRONTEND_EVENT_THEME_CHANGED,\n\tOBS_FRONTEND_EVENT_SCREENSHOT_TAKEN,\n\n\tOBS_FRONTEND_EVENT_CANVAS_ADDED,\n\tOBS_FRONTEND_EVENT_CANVAS_REMOVED,\n};\n\n/* ------------------------------------------------------------------------- */\n\n#ifndef SWIG\n\nstruct obs_frontend_source_list {\n\tDARRAY(obs_source_t *) sources;\n};\n\nstatic inline void obs_frontend_source_list_free(struct obs_frontend_source_list *source_list)\n{\n\tsize_t num = source_list->sources.num;\n\tfor (size_t i = 0; i < num; i++)\n\t\tobs_source_release(source_list->sources.array[i]);\n\tda_free(source_list->sources);\n}\n\nstruct obs_frontend_canvas_list {\n\tDARRAY(obs_canvas_t *) canvases;\n};\n\nstatic inline void obs_frontend_canvas_list_free(struct obs_frontend_canvas_list *canvas_list)\n{\n\tsize_t num = canvas_list->canvases.num;\n\tfor (size_t i = 0; i < num; i++)\n\t\tobs_canvas_release(canvas_list->canvases.array[i]);\n\n\tda_free(canvas_list->canvases);\n}\n\n#endif //!SWIG\n\n/* ------------------------------------------------------------------------- */\n\n/* NOTE: Functions that return char** string lists are a single allocation of\n * memory with pointers to itself.  Free with a single call to bfree on the\n * base char** pointer. */\n\n/* NOTE: User interface should not use typical Qt locale translation methods,\n * as the OBS UI bypasses it to use a custom translation implementation.  Use\n * standard module translation methods, obs_module_text.  For text in a Qt\n * window, use obs_frontend_push_ui_translation when the text is about to be\n * translated, and obs_frontend_pop_ui_translation when translation is\n * complete. */\n\n#ifndef SWIG\n\nEXPORT void *obs_frontend_get_main_window(void);\nEXPORT void *obs_frontend_get_main_window_handle(void);\nEXPORT void *obs_frontend_get_system_tray(void);\n\nEXPORT char **obs_frontend_get_scene_names(void);\nEXPORT void obs_frontend_get_scenes(struct obs_frontend_source_list *sources);\nEXPORT obs_source_t *obs_frontend_get_current_scene(void);\nEXPORT void obs_frontend_set_current_scene(obs_source_t *scene);\n\nEXPORT void obs_frontend_get_transitions(struct obs_frontend_source_list *sources);\nEXPORT obs_source_t *obs_frontend_get_current_transition(void);\nEXPORT void obs_frontend_set_current_transition(obs_source_t *transition);\nEXPORT int obs_frontend_get_transition_duration(void);\nEXPORT void obs_frontend_set_transition_duration(int duration);\nEXPORT void obs_frontend_release_tbar(void);\nEXPORT void obs_frontend_set_tbar_position(int position);\nEXPORT int obs_frontend_get_tbar_position(void);\n\nEXPORT char **obs_frontend_get_scene_collections(void);\nEXPORT char *obs_frontend_get_current_scene_collection(void);\nEXPORT void obs_frontend_set_current_scene_collection(const char *collection);\nEXPORT bool obs_frontend_add_scene_collection(const char *name);\n\nEXPORT char **obs_frontend_get_profiles(void);\nEXPORT char *obs_frontend_get_current_profile(void);\nEXPORT char *obs_frontend_get_current_profile_path(void);\nEXPORT void obs_frontend_set_current_profile(const char *profile);\nEXPORT void obs_frontend_create_profile(const char *name);\nEXPORT void obs_frontend_duplicate_profile(const char *name);\nEXPORT void obs_frontend_delete_profile(const char *profile);\n\ntypedef void (*obs_frontend_cb)(void *private_data);\n\nEXPORT void *obs_frontend_add_tools_menu_qaction(const char *name);\nEXPORT void obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data);\n\n/* takes QWidget for widget */\nEXPORT bool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget);\n\nEXPORT void obs_frontend_remove_dock(const char *id);\n\n/* takes QDockWidget for dock */\nEXPORT bool obs_frontend_add_custom_qdock(const char *id, void *dock);\n\ntypedef void (*obs_frontend_event_cb)(enum obs_frontend_event event, void *private_data);\n\nEXPORT void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data);\nEXPORT void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data);\n\ntypedef void (*obs_frontend_save_cb)(obs_data_t *save_data, bool saving, void *private_data);\n\nEXPORT void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data);\nEXPORT void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data);\n\nEXPORT void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data);\nEXPORT void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data);\n\ntypedef bool (*obs_frontend_translate_ui_cb)(const char *text, const char **out);\n\nEXPORT void obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate);\nEXPORT void obs_frontend_pop_ui_translation(void);\n\n#endif //!SWIG\n\nEXPORT void obs_frontend_streaming_start(void);\nEXPORT void obs_frontend_streaming_stop(void);\nEXPORT bool obs_frontend_streaming_active(void);\n\nEXPORT void obs_frontend_recording_start(void);\nEXPORT void obs_frontend_recording_stop(void);\nEXPORT bool obs_frontend_recording_active(void);\nEXPORT void obs_frontend_recording_pause(bool pause);\nEXPORT bool obs_frontend_recording_paused(void);\nEXPORT bool obs_frontend_recording_split_file(void);\nEXPORT bool obs_frontend_recording_add_chapter(const char *name);\n\nEXPORT void obs_frontend_replay_buffer_start(void);\nEXPORT void obs_frontend_replay_buffer_save(void);\nEXPORT void obs_frontend_replay_buffer_stop(void);\nEXPORT bool obs_frontend_replay_buffer_active(void);\n\nEXPORT void obs_frontend_open_projector(const char *type, int monitor, const char *geometry, const char *name);\nEXPORT void obs_frontend_save(void);\nEXPORT void obs_frontend_defer_save_begin(void);\nEXPORT void obs_frontend_defer_save_end(void);\n\nEXPORT obs_output_t *obs_frontend_get_streaming_output(void);\nEXPORT obs_output_t *obs_frontend_get_recording_output(void);\nEXPORT obs_output_t *obs_frontend_get_replay_buffer_output(void);\n\nEXPORT config_t *obs_frontend_get_profile_config(void);\nOBS_DEPRECATED EXPORT config_t *obs_frontend_get_global_config(void);\nEXPORT config_t *obs_frontend_get_app_config(void);\nEXPORT config_t *obs_frontend_get_user_config(void);\n\nEXPORT void obs_frontend_set_streaming_service(obs_service_t *service);\nEXPORT obs_service_t *obs_frontend_get_streaming_service(void);\nEXPORT void obs_frontend_save_streaming_service(void);\n\nEXPORT bool obs_frontend_preview_program_mode_active(void);\nEXPORT void obs_frontend_set_preview_program_mode(bool enable);\nEXPORT void obs_frontend_preview_program_trigger_transition(void);\n\nEXPORT void obs_frontend_set_preview_enabled(bool enable);\nEXPORT bool obs_frontend_preview_enabled(void);\n\nEXPORT obs_source_t *obs_frontend_get_current_preview_scene(void);\nEXPORT void obs_frontend_set_current_preview_scene(obs_source_t *scene);\n\nEXPORT void obs_frontend_take_screenshot(void);\nEXPORT void obs_frontend_take_source_screenshot(obs_source_t *source);\n\nEXPORT obs_output_t *obs_frontend_get_virtualcam_output(void);\nEXPORT void obs_frontend_start_virtualcam(void);\nEXPORT void obs_frontend_stop_virtualcam(void);\nEXPORT bool obs_frontend_virtualcam_active(void);\n\nEXPORT void obs_frontend_reset_video(void);\n\nEXPORT void obs_frontend_open_source_properties(obs_source_t *source);\nEXPORT void obs_frontend_open_source_filters(obs_source_t *source);\nEXPORT void obs_frontend_open_source_interaction(obs_source_t *source);\nEXPORT void obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item);\n\nEXPORT char *obs_frontend_get_current_record_output_path(void);\nEXPORT const char *obs_frontend_get_locale_string(const char *string);\n\nEXPORT bool obs_frontend_is_theme_dark(void);\n\nEXPORT char *obs_frontend_get_last_recording(void);\nEXPORT char *obs_frontend_get_last_screenshot(void);\nEXPORT char *obs_frontend_get_last_replay(void);\n\ntypedef void (*undo_redo_cb)(const char *data);\nEXPORT void obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo, const undo_redo_cb redo,\n\t\t\t\t\t      const char *undo_data, const char *redo_data, bool repeatable);\n\nEXPORT void obs_frontend_get_canvases(struct obs_frontend_canvas_list *canvas_list);\nEXPORT obs_canvas_t *obs_frontend_add_canvas(const char *name, struct obs_video_info *ovi, int flags);\nEXPORT bool obs_frontend_remove_canvas(obs_canvas_t *canvas);\n\n/* ------------------------------------------------------------------------- */\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "frontend/api/obs-frontend-internal.hpp",
    "content": "#pragma once\n\n#include \"obs-frontend-api.h\"\n\n#include <vector>\n#include <string>\n\nstruct obs_frontend_callbacks {\n\tvirtual ~obs_frontend_callbacks() {}\n\tvirtual void *obs_frontend_get_main_window(void) = 0;\n\tvirtual void *obs_frontend_get_main_window_handle(void) = 0;\n\tvirtual void *obs_frontend_get_system_tray(void) = 0;\n\n\tvirtual void obs_frontend_get_scenes(struct obs_frontend_source_list *sources) = 0;\n\tvirtual obs_source_t *obs_frontend_get_current_scene(void) = 0;\n\tvirtual void obs_frontend_set_current_scene(obs_source_t *scene) = 0;\n\n\tvirtual void obs_frontend_get_transitions(struct obs_frontend_source_list *sources) = 0;\n\tvirtual obs_source_t *obs_frontend_get_current_transition(void) = 0;\n\tvirtual void obs_frontend_set_current_transition(obs_source_t *transition) = 0;\n\tvirtual int obs_frontend_get_transition_duration(void) = 0;\n\tvirtual void obs_frontend_set_transition_duration(int duration) = 0;\n\tvirtual void obs_frontend_release_tbar(void) = 0;\n\tvirtual int obs_frontend_get_tbar_position(void) = 0;\n\tvirtual void obs_frontend_set_tbar_position(int position) = 0;\n\n\tvirtual void obs_frontend_get_scene_collections(std::vector<std::string> &strings) = 0;\n\tvirtual char *obs_frontend_get_current_scene_collection(void) = 0;\n\tvirtual void obs_frontend_set_current_scene_collection(const char *collection) = 0;\n\tvirtual bool obs_frontend_add_scene_collection(const char *name) = 0;\n\n\tvirtual void obs_frontend_get_profiles(std::vector<std::string> &strings) = 0;\n\tvirtual char *obs_frontend_get_current_profile(void) = 0;\n\tvirtual char *obs_frontend_get_current_profile_path(void) = 0;\n\tvirtual void obs_frontend_set_current_profile(const char *profile) = 0;\n\tvirtual void obs_frontend_create_profile(const char *name) = 0;\n\tvirtual void obs_frontend_duplicate_profile(const char *name) = 0;\n\tvirtual void obs_frontend_delete_profile(const char *profile) = 0;\n\n\tvirtual void obs_frontend_streaming_start(void) = 0;\n\tvirtual void obs_frontend_streaming_stop(void) = 0;\n\tvirtual bool obs_frontend_streaming_active(void) = 0;\n\n\tvirtual void obs_frontend_recording_start(void) = 0;\n\tvirtual void obs_frontend_recording_stop(void) = 0;\n\tvirtual bool obs_frontend_recording_active(void) = 0;\n\tvirtual void obs_frontend_recording_pause(bool pause) = 0;\n\tvirtual bool obs_frontend_recording_paused(void) = 0;\n\tvirtual bool obs_frontend_recording_split_file(void) = 0;\n\tvirtual bool obs_frontend_recording_add_chapter(const char *name) = 0;\n\n\tvirtual void obs_frontend_replay_buffer_start(void) = 0;\n\tvirtual void obs_frontend_replay_buffer_save(void) = 0;\n\tvirtual void obs_frontend_replay_buffer_stop(void) = 0;\n\tvirtual bool obs_frontend_replay_buffer_active(void) = 0;\n\n\tvirtual void *obs_frontend_add_tools_menu_qaction(const char *name) = 0;\n\tvirtual void obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback,\n\t\t\t\t\t\t      void *private_data) = 0;\n\n\tvirtual bool obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget) = 0;\n\tvirtual void obs_frontend_remove_dock(const char *id) = 0;\n\tvirtual bool obs_frontend_add_custom_qdock(const char *id, void *dock) = 0;\n\n\tvirtual void obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data) = 0;\n\tvirtual void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data) = 0;\n\n\tvirtual obs_output_t *obs_frontend_get_streaming_output(void) = 0;\n\tvirtual obs_output_t *obs_frontend_get_recording_output(void) = 0;\n\tvirtual obs_output_t *obs_frontend_get_replay_buffer_output(void) = 0;\n\n\tvirtual config_t *obs_frontend_get_profile_config(void) = 0;\n\n\tvirtual config_t *obs_frontend_get_app_config(void) = 0;\n\tvirtual config_t *obs_frontend_get_user_config(void) = 0;\n\n\tvirtual void obs_frontend_open_projector(const char *type, int monitor, const char *geometry,\n\t\t\t\t\t\t const char *name) = 0;\n\tvirtual void obs_frontend_save(void) = 0;\n\tvirtual void obs_frontend_defer_save_begin(void) = 0;\n\tvirtual void obs_frontend_defer_save_end(void) = 0;\n\tvirtual void obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data) = 0;\n\tvirtual void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data) = 0;\n\n\tvirtual void obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data) = 0;\n\tvirtual void obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data) = 0;\n\n\tvirtual void obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate) = 0;\n\tvirtual void obs_frontend_pop_ui_translation(void) = 0;\n\n\tvirtual obs_service_t *obs_frontend_get_streaming_service(void) = 0;\n\tvirtual void obs_frontend_set_streaming_service(obs_service_t *service) = 0;\n\tvirtual void obs_frontend_save_streaming_service() = 0;\n\n\tvirtual bool obs_frontend_preview_program_mode_active(void) = 0;\n\tvirtual void obs_frontend_set_preview_program_mode(bool enable) = 0;\n\tvirtual void obs_frontend_preview_program_trigger_transition(void) = 0;\n\n\tvirtual bool obs_frontend_preview_enabled(void) = 0;\n\tvirtual void obs_frontend_set_preview_enabled(bool enable) = 0;\n\n\tvirtual obs_source_t *obs_frontend_get_current_preview_scene(void) = 0;\n\tvirtual void obs_frontend_set_current_preview_scene(obs_source_t *scene) = 0;\n\n\tvirtual void on_load(obs_data_t *settings) = 0;\n\tvirtual void on_preload(obs_data_t *settings) = 0;\n\tvirtual void on_save(obs_data_t *settings) = 0;\n\tvirtual void on_event(enum obs_frontend_event event) = 0;\n\n\tvirtual void obs_frontend_take_screenshot() = 0;\n\tvirtual void obs_frontend_take_source_screenshot(obs_source_t *source) = 0;\n\n\tvirtual obs_output_t *obs_frontend_get_virtualcam_output(void) = 0;\n\tvirtual void obs_frontend_start_virtualcam(void) = 0;\n\tvirtual void obs_frontend_stop_virtualcam(void) = 0;\n\tvirtual bool obs_frontend_virtualcam_active(void) = 0;\n\n\tvirtual void obs_frontend_reset_video(void) = 0;\n\n\tvirtual void obs_frontend_open_source_properties(obs_source_t *source) = 0;\n\tvirtual void obs_frontend_open_source_filters(obs_source_t *source) = 0;\n\tvirtual void obs_frontend_open_source_interaction(obs_source_t *source) = 0;\n\tvirtual void obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item) = 0;\n\n\tvirtual char *obs_frontend_get_current_record_output_path(void) = 0;\n\tvirtual const char *obs_frontend_get_locale_string(const char *string) = 0;\n\n\tvirtual bool obs_frontend_is_theme_dark(void) = 0;\n\n\tvirtual char *obs_frontend_get_last_recording(void) = 0;\n\tvirtual char *obs_frontend_get_last_screenshot(void) = 0;\n\tvirtual char *obs_frontend_get_last_replay(void) = 0;\n\n\tvirtual void obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo,\n\t\t\t\t\t\t       const undo_redo_cb redo, const char *undo_data,\n\t\t\t\t\t\t       const char *redo_data, bool repeatable) = 0;\n\n\tvirtual obs_canvas_t *obs_frontend_add_canvas(const char *name, obs_video_info *ovi, int flags) = 0;\n\tvirtual bool obs_frontend_remove_canvas(obs_canvas_t *canvas) = 0;\n\tvirtual void obs_frontend_get_canvases(obs_frontend_canvas_list *canvas_list) = 0;\n};\n\nEXPORT void obs_frontend_set_callbacks_internal(obs_frontend_callbacks *callbacks);\n"
  },
  {
    "path": "frontend/cmake/feature-browserpanels.cmake",
    "content": "if(TARGET OBS::browser-panels)\n  target_enable_feature(obs-studio \"Browser panels\" BROWSER_AVAILABLE)\n\n  target_link_libraries(obs-studio PRIVATE OBS::browser-panels)\n\n  target_sources(\n    obs-studio\n    PRIVATE\n      dialogs/OBSExtraBrowsers.cpp\n      dialogs/OBSExtraBrowsers.hpp\n      docks/BrowserDock.cpp\n      docks/BrowserDock.hpp\n      utility/ExtraBrowsersDelegate.cpp\n      utility/ExtraBrowsersDelegate.hpp\n      utility/ExtraBrowsersModel.cpp\n      utility/ExtraBrowsersModel.hpp\n  )\nendif()\n"
  },
  {
    "path": "frontend/cmake/feature-idian-playground.cmake",
    "content": "option(ENABLE_IDIAN_PLAYGROUND \"Enable building custom idian widget demo window\" OFF)\n\nif(ENABLE_IDIAN_PLAYGROUND)\n  target_sources(\n    obs-studio\n    PRIVATE dialogs/OBSIdianPlayground.hpp dialogs/OBSIdianPlayground.cpp forms/OBSIdianPlayground.ui\n  )\n  target_enable_feature(obs-studio \"Idian Playground\" ENABLE_IDIAN_PLAYGROUND)\nelse()\n  target_disable_feature(obs-studio \"Idian Playground\")\nendif()\n"
  },
  {
    "path": "frontend/cmake/feature-importers.cmake",
    "content": "target_sources(\n  obs-studio\n  PRIVATE\n    importer/ImporterEntryPathItemDelegate.cpp\n    importer/ImporterEntryPathItemDelegate.hpp\n    importer/ImporterModel.cpp\n    importer/ImporterModel.hpp\n    importer/OBSImporter.cpp\n    importer/OBSImporter.hpp\n    importers/classic.cpp\n    importers/importers.cpp\n    importers/importers.hpp\n    importers/sl.cpp\n    importers/studio.cpp\n    importers/xsplit.cpp\n)\n"
  },
  {
    "path": "frontend/cmake/feature-macos-update.cmake",
    "content": "include_guard(DIRECTORY)\n\nfind_package(nlohmann_json 3.11 REQUIRED)\n\nif(NOT TARGET OBS::blake2)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/deps/blake2\" \"${CMAKE_BINARY_DIR}/deps/blake2\")\nendif()\n\ntarget_sources(\n  obs-studio\n  PRIVATE\n    utility/WhatsNewBrowserInitThread.cpp\n    utility/WhatsNewBrowserInitThread.hpp\n    utility/WhatsNewInfoThread.cpp\n    utility/WhatsNewInfoThread.hpp\n    utility/crypto-helpers-mac.mm\n    utility/crypto-helpers.hpp\n    utility/models/branches.hpp\n    utility/models/whatsnew.hpp\n    utility/update-helpers.cpp\n    utility/update-helpers.hpp\n)\n\ntarget_link_libraries(\n  obs-studio\n  PRIVATE \"$<LINK_LIBRARY:FRAMEWORK,Security.framework>\" nlohmann_json::nlohmann_json OBS::blake2\n)\n"
  },
  {
    "path": "frontend/cmake/feature-plugin-manager.cmake",
    "content": "target_sources(\n  obs-studio\n  PRIVATE\n    plugin-manager/PluginManager.cpp\n    plugin-manager/PluginManager.hpp\n    plugin-manager/PluginManagerWindow.cpp\n    plugin-manager/PluginManagerWindow.hpp\n)\n"
  },
  {
    "path": "frontend/cmake/feature-restream.cmake",
    "content": "if(RESTREAM_CLIENTID AND RESTREAM_HASH MATCHES \"^(0|[a-fA-F0-9]+)$\" AND TARGET OBS::browser-panels)\n  target_sources(obs-studio PRIVATE oauth/RestreamAuth.cpp oauth/RestreamAuth.hpp)\n  target_enable_feature(obs-studio \"Restream API connection\" RESTREAM_ENABLED)\nelse()\n  target_disable_feature(obs-studio \"Restream API connection\")\n  set(RESTREAM_CLIENTID \"\")\n  set(RESTREAM_HASH \"0\")\nendif()\n"
  },
  {
    "path": "frontend/cmake/feature-sparkle.cmake",
    "content": "if(SPARKLE_APPCAST_URL AND SPARKLE_PUBLIC_KEY)\n  find_library(SPARKLE Sparkle)\n  mark_as_advanced(SPARKLE)\n  target_sources(\n    obs-studio\n    PRIVATE\n      utility/MacUpdateThread.cpp\n      utility/MacUpdateThread.hpp\n      utility/OBSSparkle.hpp\n      utility/OBSSparkle.mm\n      utility/OBSUpdateDelegate.h\n      utility/OBSUpdateDelegate.mm\n  )\n  set_source_files_properties(utility/OBSSparkle.mm PROPERTIES COMPILE_OPTIONS -fobjc-arc)\n\n  target_link_libraries(obs-studio PRIVATE \"$<LINK_LIBRARY:FRAMEWORK,${SPARKLE}>\")\n\n  if(OBS_BETA GREATER 0 OR OBS_RELEASE_CANDIDATE GREATER 0)\n    set(SPARKLE_UPDATE_INTERVAL 3600) # 1 hour\n  else()\n    set(SPARKLE_UPDATE_INTERVAL 86400) # 24 hours\n  endif()\n\n  target_enable_feature(obs-studio \"Sparkle updater\" ENABLE_SPARKLE_UPDATER)\n\n  include(cmake/feature-macos-update.cmake)\nelse()\n  set(SPARKLE_UPDATE_INTERVAL 0) # Set anything that's not an empty integer\n  target_disable_feature(obs-studio \"Sparkle updater\")\nendif()\n"
  },
  {
    "path": "frontend/cmake/feature-twitch.cmake",
    "content": "if(TWITCH_CLIENTID AND TWITCH_HASH MATCHES \"^(0|[a-fA-F0-9]+)$\" AND TARGET OBS::browser-panels)\n  target_sources(obs-studio PRIVATE oauth/TwitchAuth.cpp oauth/TwitchAuth.hpp)\n  target_enable_feature(obs-studio \"Twitch API connection\" TWITCH_ENABLED)\nelse()\n  target_disable_feature(obs-studio \"Twitch API connection\")\n  set(TWITCH_CLIENTID \"\")\n  set(TWITCH_HASH \"0\")\nendif()\n"
  },
  {
    "path": "frontend/cmake/feature-whatsnew.cmake",
    "content": "include_guard(DIRECTORY)\n\noption(ENABLE_WHATSNEW \"Enable WhatsNew dialog\" ON)\n\nif(ENABLE_WHATSNEW AND TARGET OBS::browser-panels)\n  if(OS_MACOS)\n    include(cmake/feature-macos-update.cmake)\n  elseif(OS_LINUX)\n    set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)\n    find_package(MbedTLS REQUIRED)\n    set(CMAKE_FIND_PACKAGE_PREFER_CONFIG FALSE)\n    find_package(nlohmann_json 3.11 REQUIRED)\n\n    if(NOT TARGET OBS::blake2)\n      add_subdirectory(\"${CMAKE_SOURCE_DIR}/deps/blake2\" \"${CMAKE_BINARY_DIR}/deps/blake2\")\n    endif()\n\n    target_link_libraries(obs-studio PRIVATE MbedTLS::mbedtls nlohmann_json::nlohmann_json OBS::blake2)\n\n    target_sources(\n      obs-studio\n      PRIVATE\n        utility/WhatsNewBrowserInitThread.cpp\n        utility/WhatsNewBrowserInitThread.hpp\n        utility/WhatsNewInfoThread.cpp\n        utility/WhatsNewInfoThread.hpp\n        utility/crypto-helpers-mbedtls.cpp\n        utility/crypto-helpers.hpp\n        utility/models/whatsnew.hpp\n        utility/update-helpers.cpp\n        utility/update-helpers.hpp\n    )\n  endif()\n\n  target_enable_feature(obs-studio \"What's New panel\" WHATSNEW_ENABLED)\nendif()\n"
  },
  {
    "path": "frontend/cmake/feature-youtube.cmake",
    "content": "if(\n  YOUTUBE_CLIENTID\n  AND YOUTUBE_SECRET\n  AND YOUTUBE_CLIENTID_HASH MATCHES \"^(0|[a-fA-F0-9]+)$\"\n  AND YOUTUBE_SECRET_HASH MATCHES \"^(0|[a-fA-F0-9]+)$\"\n  AND TARGET OBS::browser-panels\n)\n  target_sources(\n    obs-studio\n    PRIVATE\n      dialogs/OBSYoutubeActions.cpp\n      dialogs/OBSYoutubeActions.hpp\n      docks/YouTubeAppDock.cpp\n      docks/YouTubeAppDock.hpp\n      docks/YouTubeChatDock.cpp\n      docks/YouTubeChatDock.hpp\n      forms/OBSYoutubeActions.ui\n      oauth/YoutubeAuth.cpp\n      oauth/YoutubeAuth.hpp\n      utility/YoutubeApiWrappers.cpp\n      utility/YoutubeApiWrappers.hpp\n  )\n\n  target_enable_feature(obs-studio \"YouTube API connection\" YOUTUBE_ENABLED)\nelse()\n  target_disable_feature(obs-studio \"YouTube API connection\")\n  set(YOUTUBE_SECRET_HASH 0)\n  set(YOUTUBE_CLIENTID_HASH 0)\nendif()\n"
  },
  {
    "path": "frontend/cmake/linux/com.obsproject.Studio.desktop",
    "content": "[Desktop Entry]\nVersion=1.0\nName=OBS Studio\nGenericName=Streaming/Recording Software\nComment=Free and Open Source Streaming/Recording Software\nExec=obs\nIcon=com.obsproject.Studio\nTerminal=false\nType=Application\nCategories=AudioVideo;Recorder;\nStartupNotify=true\nStartupWMClass=obs\n\nGenericName[af_ZA]=Stroom-/Opnameprogrammatuur\nComment[af_ZA]=Gratis en oopbron stroom-/opnameprogrammatuur\nGenericName[an_ES]=Programa de retransmisión/gravación\nComment[an_ES]=Program de retransmisión/gravación libre y de codigo ubierto\nGenericName[ar_SA]=برنامَج للبث/التسجيل\nComment[ar_SA]=برنامَج بث/تسجيل مجاني ومفتوح المصدر\nGenericName[az_AZ]=Yayım/Yazma Proqramı\nComment[az_AZ]=Ödənişsiz və Açıq Mənbəli Yayım/Yazma Proqramı\nGenericName[be_BY]=Праграма для запісу відэа і стрымінга\nComment[be_BY]=Бясплатнае і адкрытае ПЗ для стрымінгу і запісу відэа\nGenericName[bg_BG]=Програма за видеозапис и излъчване\nComment[bg_BG]=Безплатна и с отворен код програма за видеозапис и излъчване\nGenericName[bn_BD]=স্ট্রিমিং/রেকর্ডিং সফটওয়্যার\nComment[bn_BD]=ফ্রি এবং মুক্ত সোর্স স্ট্রিমিং/রেকর্ডিং সফ্টওয়্যার\nGenericName[ca_ES]=Programa de retransmissió/enregistrament\nComment[ca_ES]=Programa de retransmissió/enregistrament de codi lliure i gratuït\nGenericName[cs_CZ]=Software pro vysílání a nahrávání\nComment[cs_CZ]=Svobodný software pro vysílání a nahrávání\nGenericName[da_DK]=Streaming-/optagelsessoftware\nComment[da_DK]=Helt gratis og open-source streaming-/optagelsessoftware\nGenericName[de_DE]=Streaming-/Aufnahme-Software\nComment[de_DE]=Kostenlose und freie Open-Source-Software für die Erstellung von Livestreams und Videoaufnahmen\nGenericName[el_GR]=Λογισμικό Ροής/Καταγραφής\nComment[el_GR]=Δωρεαν Λογισμικό Streaming/Kαταγραφή ανοιχτου κωδικα\nGenericName[en_GB]=Streaming/Recording Software\nComment[en_GB]=Free and Open Source Streaming/Recording Software\nGenericName[es_ES]=Software de retransmisión/grabación\nComment[es_ES]=Software de emisión en directo y grabación de código libre y abierto\nGenericName[et_EE]=Video voogesituse ja salvestuse tarkvara\nComment[et_EE]=Tasuta ja avatud lähtekoodiga video voogesituse ja salvestuse tarkvara\nGenericName[eu_ES]=Streaming/Grabazio Softwarea\nComment[eu_ES]=Dohainiko eta kode irekiko Streaming/Grabazio softwarea\nGenericName[fa_IR]=نرم‌افزار جریان و ضبط\nComment[fa_IR]=نرم‌افزار آزاد جریان و ضبط\nGenericName[fi_FI]=Striimaus-/tallennusohjelmisto\nComment[fi_FI]=Ilmainen ja avoimen lähdekoodin striimaus-/tallennusohjelmisto\nGenericName[fil_PH]=Software para sa Streaming/Recording\nComment[fil_PH]=Libre at Open Source na Streaming/Recording Software\nGenericName[fr_FR]=Logiciel de diffusion/enregistrement\nComment[fr_FR]=Logiciel Open Source gratuit de Diffusion & Enregistrement\nGenericName[gd_GB]=Bathar-bog sruthaidh/clàraidh\nComment[gd_GB]=Bathar-bog sruthaidh/clàraidh saor le bun-tùs fosgailte\nGenericName[gl_ES]=Software de gravación e vídeo en directo\nComment[gl_ES]=Software libre de fontes abertas para gravación e vídeo en directo\nGenericName[he_IL]=תוכנה לשידורים חיים והקלטות\nComment[he_IL]=תכנה חינמית בקוד פתוח לשידורים חיים והקלטות\nGenericName[hi_IN]=स्ट्रीमिंग/रिकॉर्डिंग सॉफ्टवेयर\nComment[hi_IN]=स्वतंत्र एवं खुले स्रोत वाला स्ट्रीमिंग/रिकॉर्डिंग सॉफ्टवेयर\nGenericName[hr_HR]=Softver za emitiranje/snimanje\nComment[hr_HR]=Besplatan softver otvorenog koda za emitiranje/snimanje\nGenericName[hu_HU]=Közvetítő/rögzítő szoftver\nComment[hu_HU]=Szabad és nyílt forráskódú közvetítő/rögzítő szoftver\nGenericName[hy_AM]=Հեռարձակման/տեսագրման ծրագրակազմ\nComment[hy_AM]=Ազատ և բաց կոդով հոսքային/ձայնագրող ծրագրակազմ\nGenericName[id_ID]=Perangkat Lunak Streaming/Perekaman\nComment[id_ID]=Perangkat Lunak Streaming/Perekaman Gratis dan Sumber Terbuka\nGenericName[it_IT]=Software per dirette e registrazione schermo\nComment[it_IT]=Software libero e a sorgente aperto per la trasmissione/registrazione\nGenericName[ja_JP]=配信/録画ソフトウェア\nComment[ja_JP]=無料のオープンソース配信/録画ソフトウェア\nGenericName[ka_GE]=ვიდეოს ეთერში გამშვები/ჩამწერი პროგრამა\nComment[ka_GE]=თავისუფალი და ღია წყაროს მქონე, ვიდეოს ეთერში გამშვები/ჩამწერი პროგრამა\nGenericName[kmr_TR]=Nermalava weşandin/tomarkirin-ê\nComment[kmr_TR]=Nermalava weşandin/tomarkirin-ê belaş û çavkaniya azad\nGenericName[ko_KR]=방송 및 녹화 프로그램\nComment[ko_KR]=스트리밍 및 녹화용 자유 오픈소스 소프트웨어\nGenericName[lo_LA]=ຊອບແວຖ່າຍທອດສົດ/ບັນທຶກ\nComment[lo_LA]=ຊອບແວຖ່າຍທອດສົດ/ບັນທຶກ ແບບຟຣີ ແລະ ໂອເພນຊອດ\nGenericName[lv_LV]=Straumēšanas/Filmešanas programmatūra\nComment[lv_LV]=Bezmaksas un ar atvērtu kodu Straumēšanas/Filmešanas programmatūra\nGenericName[mn_MN]=Шууд дамжуулах/Бичих Програм\nComment[mn_MN]=Үнэгүй ба Нээлттэй Шууд дамжуулах/Бичих Програм\nGenericName[ms_MY]=Perisian Penstriman/Rakaman\nComment[ms_MY]=Perisian Penstriman/Rakaman Bersumber Terbuka dan Percuma\nGenericName[nb_NO]=Opptak\nComment[nb_NO]=Fri og åpen opptaks-app\nGenericName[nl_NL]=Streaming/Opname Software\nComment[nl_NL]=Vrij en Open Source Streaming/Opname Software\nGenericName[pl_PL]=Oprogramowanie do transmisji strumieniowej/nagrywania\nComment[pl_PL]=Darmowe i otwarte oprogramowanie do transmisji strumieniowej/nagrywania\nGenericName[pt_BR]=Software de gravação e transmissão de vídeo\nComment[pt_BR]=Software livre e de código aberto para gravação e transmissão de vídeo\nGenericName[pt_PT]=Programa de transmissão/gravação\nComment[pt_PT]=Programa livre e de código aberto para transmissão/gravação de vídeo\nGenericName[ro_RO]=Program de streaming și înregistrare\nComment[ro_RO]=Program de streaming și înregistrare gratuit și open-source\nGenericName[ru_RU]=Приложение для потокового вещания и видеозаписи\nComment[ru_RU]=Свободное и открытое ПО для потокового вещания и видеозаписи\nGenericName[si_LK]=ප්‍රචාර/පටිගත මෘදුකාංගයකි\nComment[si_LK]=නොමිලේ හා විවෘත මූලාශ්‍ර ප්‍රචාර/පටිගත මෘදුකාංගයකි\nGenericName[sk_SK]=Streamovací/Nahrávací Softvér\nComment[sk_SK]=Bezplatný a otvorený streamovací/nahrávací softvér\nGenericName[sl_SI]=Pretočna/snemalna programska oprema\nComment[sl_SI]=Brezplačni in odprtokodna programska oprema za pretakanje/snemanje\nGenericName[sr_SP]=Софтвер за стримовање/снимање\nComment[sr_SP]=Бесплатан софтвер отвореног кода за стримовање/снимање\nGenericName[sv_SE]=Programvara för strömning/inspelning\nComment[sv_SE]=Fri programvara för strömning/inspelning med öppen källkod\nGenericName[ta_IN]=ஒளிபரப்பு/பதிவு செய்யும் மென்பொருள்\nComment[ta_IN]=இலவச மற்றும் திறந்த மூல ஒளிபரப்பு/பதிவு செய்யும் மென்பொருள்\nGenericName[te_IN]=స్ట్రీమింగ్/రికార్డింగ్ సాఫ్ట్‌వేర్\nComment[te_IN]=ఉచిత మరియు ఓపెన్ సోర్స్ స్ట్రీమింగ్/రికార్డింగ్ సాఫ్ట్‌వేర్\nGenericName[th_TH]=ซอฟต์แวร์สตรีมมิ่ง/บันทึก\nComment[th_TH]=ซอฟต์แวร์สตรีมมิ่ง/บันทึกฟรีและโอเพ่นซอร์ส\nGenericName[tl_PH]=Palatuntunan ng Pagsasahimpapawid/Pagtatala\nComment[tl_PH]=Walang Bayad at Bukas na Pinagmulang Palatuntunan ng Pagsasahimpapawid/Pagtatala\nGenericName[tr_TR]=Yayın/Kayıt Yazılımı\nComment[tr_TR]=Ücretsiz ve Açık Kaynaklı Yayın/Kayıt Yazılımı\nGenericName[ug_CN]=ئېقىم تارقىتىش/خاتىرىلەش يۇمشاق دېتالى\nComment[ug_CN]=ئەركىن ۋە ئوچۇق كودلۇق ئېقىم تارقىتىش/سىنغا ئېلىش يۇمشاق دېتالى\nGenericName[uk_UA]=Програма для трансляцій/запису\nComment[uk_UA]=Вільне та відкрите програмне забезпечення для трансляцій/запису\nGenericName[ur_PK]=سٹریمنگ/ریکارڈنگ سافٹ ویئر\nComment[ur_PK]=مفت اور اوپن سورس سٹریمنگ/ریکارڈنگ سافٹ ویئر\nGenericName[vi_VN]=Phần mềm ghi hình/phát luồng\nComment[vi_VN]=Phần mềm ghi hình/phát luồng mã nguồn mở miễn phí\nGenericName[zh_CN]=直播/录制软件\nComment[zh_CN]=自由且开源的直播/录制软件\nGenericName[zh_TW]=串流與錄影軟體\nComment[zh_TW]=免費，開源的串流與錄影軟體\n"
  },
  {
    "path": "frontend/cmake/linux/com.obsproject.Studio.metainfo.xml.in",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<component type=\"desktop\">\n  <id>com.obsproject.Studio</id>\n  <launchable type=\"desktop-id\">com.obsproject.Studio.desktop</launchable>\n  <metadata_license>CC0-1.0</metadata_license>\n  <project_license>GPL-2.0-or-later</project_license>\n  <name>OBS Studio</name>\n  <developer id=\"com.obsproject\">\n    <name>OBS Project</name>\n  </developer>\n  <summary>Live stream and record videos</summary>\n  <description>\n    <p>Free and open source software for video capturing, recording, and live streaming.</p>\n    <p>Features:</p>\n    <ul>\n      <li>High performance real time video/audio capturing and mixing. Create scenes made up of multiple sources including window captures, images, text, browser windows, webcams, capture cards and more.</li>\n      <li>Set up an unlimited number of scenes you can switch between seamlessly via custom transitions.</li>\n      <li>Intuitive audio mixer with per-source filters such as noise gate, noise suppression, and gain. Take full control with VST plugin support.</li>\n      <li>Powerful and easy to use configuration options. Add new Sources, duplicate existing ones, and adjust their properties effortlessly.</li>\n      <li>Streamlined Settings panel gives you access to a wide array of configuration options to tweak every aspect of your broadcast or recording.</li>\n      <li>Modular 'Dock' UI allows you to rearrange the layout exactly as you like. You can even pop out each individual Dock to its own window.</li>\n    </ul>\n    <p>Create Professional Productions:</p>\n    <ul>\n      <li>Choose from a number of different and customizable transitions for when you switch between your scenes or add your own stinger video files.</li>\n      <li>Set hotkeys for nearly every sort of action, such as switching between scenes, starting/stopping streams or recordings, muting audio sources, push to talk, and more.</li>\n      <li>Studio Mode lets you preview your scenes and sources before pushing them live. Adjust your scenes and sources or create new ones and ensure they're perfect before your viewers ever see them.</li>\n      <li>Get a high level view of your production using the Multiview. Monitor 8 different scenes and easily cue or transition to any of them with merely a single or double click.</li>\n    </ul>\n  </description>\n  <url type=\"homepage\">https://obsproject.com</url>\n  <url type=\"bugtracker\">https://github.com/obsproject/obs-studio/issues</url>\n  <url type=\"donation\">https://obsproject.com/contribute</url>\n  <url type=\"translate\">https://crowdin.com/project/obs-studio</url>\n  <url type=\"vcs-browser\">https://github.com/obsproject/obs-studio</url>\n\n  <recommends>\n    <internet>always</internet>\n    <control>pointing</control>\n    <control>keyboard</control>\n  </recommends>\n\n  <supports>\n    <internet>offline-only</internet>\n    <control>touch</control>\n  </supports>\n\n  <screenshots>\n    <screenshot type=\"default\">\n      <image>https://obsproject.com/assets/images/OBSDemoApp301Flathub.png</image>\n      <caption>The OBS Studio window showing a card game, the project website, a video, and a purple rectangle</caption>\n    </screenshot>\n  </screenshots>\n\n  <branding>\n    <color type=\"primary\">#284cb8</color>\n  </branding>\n\n  <content_rating type=\"oars-1.1\"/>\n\n  <releases>\n    <release version=\"@OBS_VERSION@\" date=\"@APPDATA_RELEASE_DATE@\">\n      <url type=\"details\">https://github.com/obsproject/obs-studio/releases/tag/@OBS_VERSION@</url>\n    </release>\n  </releases>\n  <custom>\n    <value key=\"flathub::manifest\">https://github.com/obsproject/obs-studio/blob/@GIT_HASH@/build-aux/</value>\n  </custom>\n</component>\n"
  },
  {
    "path": "frontend/cmake/macos/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon_16x16.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_16x16@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_32x32.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_32x32@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_128x128.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_128x128@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_256x256.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_256x256@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_512x512.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"512x512\"\n    },\n    {\n      \"filename\" : \"icon_512x512@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"512x512\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "frontend/cmake/macos/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "frontend/cmake/macos/Info.plist.in",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>NSCameraReactionEffectGesturesEnabledDefault</key>\n\t<false/>\n\t<key>NSHighResolutionCapable</key>\n\t<true/>\n\t<key>LSAppNapIsDisabled</key>\n\t<true/>\n\t<key>SUFeedURL</key>\n\t<string>${SPARKLE_APPCAST_URL}</string>\n\t<key>SUPublicEDKey</key>\n\t<string>${SPARKLE_PUBLIC_KEY}</string>\n\t<key>SUScheduledCheckInterval</key>\n\t<integer>${SPARKLE_UPDATE_INTERVAL}</integer>\n</dict>\n</plist>\n"
  },
  {
    "path": "frontend/cmake/macos/entitlements-extension.plist",
    "content": "<!--?xml version=\"1.0\" encoding=\"UTF-8\"?-->\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n    <dict>\n        <key>com.apple.security.cs.allow-unsigned-executable-memory</key>\n        <true/>\n        <key>com.apple.security.device.camera</key>\n        <true/>\n        <key>com.apple.security.device.audio-input</key>\n        <true/>\n        <key>com.apple.security.cs.disable-library-validation</key>\n        <true/>\n        <key>com.apple.developer.system-extension.install</key>\n        <true/>\n        <key>com.apple.security.application-groups</key>\n        <array>\n            <string>$(TeamIdentifierPrefix)com.obsproject.obs-studio</string>\n        </array>\n    </dict>\n</plist>\n"
  },
  {
    "path": "frontend/cmake/macos/entitlements.plist",
    "content": "<!--?xml version=\"1.0\" encoding=\"UTF-8\"?-->\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n    <dict>\n        <key>com.apple.security.cs.allow-unsigned-executable-memory</key>\n        <true/>\n        <key>com.apple.security.device.camera</key>\n        <true/>\n        <key>com.apple.security.device.audio-input</key>\n        <true/>\n        <key>com.apple.security.cs.disable-library-validation</key>\n        <true/>\n    </dict>\n</plist>\n"
  },
  {
    "path": "frontend/cmake/macos/exportOptions-extension.plist.in",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>method</key>\n\t<string>developer-id</string>\n\t<key>provisioningProfiles</key>\n\t<dict>\n\t\t<key>com.obsproject.obs-studio</key>\n\t\t<string>${OBS_PROVISIONING_PROFILE}</string>\n\t</dict>\n\t<key>signingStyle</key>\n\t<string>manual</string>\n\t<key>signingCertificate</key>\n\t<string>Developer ID Application</string>\n\t<key>teamID</key>\n\t<string>${OBS_CODESIGN_TEAM}</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "frontend/cmake/macos/exportOptions.plist.in",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>method</key>\n\t<string>developer-id</string>\n\t<key>signingStyle</key>\n\t<string>manual</string>\n\t<key>signingCertificate</key>\n\t<string>Developer ID Application</string>\n\t<key>teamID</key>\n\t<string>${OBS_CODESIGN_TEAM}</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "frontend/cmake/macos/qt.conf",
    "content": "[Paths]\nPlugins = PlugIns\n"
  },
  {
    "path": "frontend/cmake/os-freebsd.cmake",
    "content": "target_sources(\n  obs-studio\n  PRIVATE\n    utility/CrashHandler_FreeBSD.cpp\n    utility/NativeEventFilter.cpp\n    utility/platform-x11.cpp\n    utility/system-info-posix.cpp\n)\ntarget_compile_definitions(obs-studio PRIVATE OBS_INSTALL_PREFIX=\"${OBS_INSTALL_PREFIX}\")\ntarget_link_libraries(obs-studio PRIVATE Qt::DBus procstat)\n\nif(Qt6_VERSION AND Qt6_VERSION VERSION_LESS \"6.9.0\")\n  target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate)\nendif()\n\nfind_package(Libpci REQUIRED)\ntarget_link_libraries(obs-studio PRIVATE Libpci::pci)\n\nif(TARGET OBS::python)\n  find_package(Python REQUIRED COMPONENTS Interpreter Development)\n  target_link_libraries(obs-studio PRIVATE Python::Python)\n  target_link_options(obs-studio PRIVATE LINKER:-no-as-needed)\nendif()\n\nconfigure_file(cmake/linux/com.obsproject.Studio.metainfo.xml.in com.obsproject.Studio.metainfo.xml)\n\ninstall(\n  FILES \"${CMAKE_CURRENT_BINARY_DIR}/com.obsproject.Studio.metainfo.xml\"\n  DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/metainfo\"\n)\n\ninstall(FILES cmake/linux/com.obsproject.Studio.desktop DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/applications\")\n\ninstall(\n  FILES cmake/linux/icons/obs-logo-128.png\n  DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps\"\n  RENAME com.obsproject.Studio.png\n)\n\ninstall(\n  FILES cmake/linux/icons/obs-logo-256.png\n  DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps\"\n  RENAME com.obsproject.Studio.png\n)\n\ninstall(\n  FILES cmake/linux/icons/obs-logo-512.png\n  DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps\"\n  RENAME com.obsproject.Studio.png\n)\n\ninstall(\n  FILES cmake/linux/icons/obs-logo-scalable.svg\n  DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps\"\n  RENAME com.obsproject.Studio.svg\n)\n"
  },
  {
    "path": "frontend/cmake/os-linux.cmake",
    "content": "target_sources(\n  obs-studio\n  PRIVATE\n    utility/CrashHandler_Linux.cpp\n    utility/NativeEventFilter.cpp\n    utility/platform-x11.cpp\n    utility/system-info-posix.cpp\n)\ntarget_compile_definitions(\n  obs-studio\n  PRIVATE OBS_INSTALL_PREFIX=\"${OBS_INSTALL_PREFIX}\" $<$<BOOL:${ENABLE_PORTABLE_CONFIG}>:ENABLE_PORTABLE_CONFIG>\n)\ntarget_link_libraries(obs-studio PRIVATE Qt::DBus)\n\nif(Qt6_VERSION AND Qt6_VERSION VERSION_LESS \"6.9.0\")\n  target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate)\nendif()\n\nfind_package(Libpci REQUIRED)\ntarget_link_libraries(obs-studio PRIVATE Libpci::pci)\n\nif(TARGET OBS::python)\n  find_package(Python REQUIRED COMPONENTS Interpreter Development)\n  target_link_libraries(obs-studio PRIVATE Python::Python)\n  target_link_options(obs-studio PRIVATE LINKER:-no-as-needed)\nendif()\n\nif(NOT DEFINED APPDATA_RELEASE_DATE)\n  if(EXISTS \"${CMAKE_SOURCE_DIR}/.git\")\n    execute_process(\n      COMMAND git log --tags -1 --pretty=%cd --date=short\n      OUTPUT_VARIABLE APPDATA_RELEASE_DATE\n      WORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\n      OUTPUT_STRIP_TRAILING_WHITESPACE\n    )\n  elseif(EXISTS \"${CMAKE_SOURCE_DIR}/cmake/.CMakeBuildNumber\")\n    file(TIMESTAMP \"${CMAKE_SOURCE_DIR}/cmake/.CMakeBuildNumber\" APPDATA_RELEASE_DATE \"%Y-%m-%d\")\n  else()\n    file(TIMESTAMP \"${CMAKE_SOURCE_DIR}/CMakeLists.txt\" APPDATA_RELEASE_DATE \"%Y-%m-%d\")\n  endif()\nendif()\n\nif(NOT DEFINED GIT_HASH)\n  if(EXISTS \"${CMAKE_SOURCE_DIR}/.git\")\n    execute_process(\n      COMMAND git rev-parse HEAD\n      OUTPUT_VARIABLE GIT_HASH\n      WORKING_DIRECTORY \"${CMAKE_SOURCE_DIR}\"\n      OUTPUT_STRIP_TRAILING_WHITESPACE\n    )\n  else()\n    set(GIT_HASH \"master\")\n  endif()\nendif()\n\nconfigure_file(cmake/linux/com.obsproject.Studio.metainfo.xml.in com.obsproject.Studio.metainfo.xml)\n\ninstall(\n  FILES \"${CMAKE_CURRENT_BINARY_DIR}/com.obsproject.Studio.metainfo.xml\"\n  DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/metainfo\"\n)\n\ninstall(FILES cmake/linux/com.obsproject.Studio.desktop DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/applications\")\n\ninstall(\n  FILES cmake/linux/icons/obs-logo-128.png\n  DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/128x128/apps\"\n  RENAME com.obsproject.Studio.png\n)\n\ninstall(\n  FILES cmake/linux/icons/obs-logo-256.png\n  DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/256x256/apps\"\n  RENAME com.obsproject.Studio.png\n)\n\ninstall(\n  FILES cmake/linux/icons/obs-logo-512.png\n  DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/512x512/apps\"\n  RENAME com.obsproject.Studio.png\n)\n\ninstall(\n  FILES cmake/linux/icons/obs-logo-scalable.svg\n  DESTINATION \"${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps\"\n  RENAME com.obsproject.Studio.svg\n)\n"
  },
  {
    "path": "frontend/cmake/os-macos.cmake",
    "content": "include(cmake/feature-sparkle.cmake)\n\ntarget_sources(\n  obs-studio\n  PRIVATE\n    dialogs/OBSPermissions.cpp\n    dialogs/OBSPermissions.hpp\n    forms/OBSPermissions.ui\n    utility/CrashHandler_MacOS.mm\n    utility/NativeEventFilter.cpp\n    utility/platform-osx.mm\n    utility/system-info-macos.mm\n)\ntarget_compile_options(obs-studio PRIVATE -Wno-quoted-include-in-framework-header -Wno-comma)\n\nlist(APPEND _frontend_objcxx_compile_options -fobjc-arc -fmodules -fcxx-modules)\n\nset_property(\n  SOURCE utility/platform-osx.mm utility/CrashHandler_MacOS.mm\n  APPEND\n  PROPERTY COMPILE_OPTIONS ${_frontend_objcxx_compile_options}\n)\n\nif(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0.3)\n  target_compile_options(obs-studio PRIVATE -Wno-error=unqualified-std-cast-call)\nendif()\n\ntarget_link_libraries(\n  obs-studio\n  PRIVATE\n    \"$<LINK_LIBRARY:FRAMEWORK,AppKit.framework>\"\n    \"$<LINK_LIBRARY:FRAMEWORK,ApplicationServices.framework>\"\n    \"$<LINK_LIBRARY:FRAMEWORK,AVFoundation.framework>\"\n)\n"
  },
  {
    "path": "frontend/cmake/os-windows.cmake",
    "content": "if(NOT TARGET OBS::blake2)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/deps/blake2\" \"${CMAKE_BINARY_DIR}/deps/blake2\")\nendif()\n\nif(NOT TARGET OBS::w32-pthreads)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/deps/w32-pthreads\" \"${CMAKE_BINARY_DIR}/deps/w32-pthreads\")\nendif()\n\nset(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)\nfind_package(MbedTLS REQUIRED)\nset(CMAKE_FIND_PACKAGE_PREFER_CONFIG FALSE)\nfind_package(Detours REQUIRED)\nfind_package(nlohmann_json 3.11 REQUIRED)\n\nconfigure_file(cmake/windows/obs.rc.in obs.rc)\n\ntarget_sources(\n  obs-studio\n  PRIVATE\n    cmake/windows/obs.manifest\n    dialogs/OBSUpdate.cpp\n    dialogs/OBSUpdate.hpp\n    forms/OBSUpdate.ui\n    obs.rc\n    utility/AutoUpdateThread.cpp\n    utility/AutoUpdateThread.hpp\n    utility/CrashHandler_Windows.cpp\n    utility/NativeEventFilter_Windows.cpp\n    utility/WhatsNewBrowserInitThread.cpp\n    utility/WhatsNewBrowserInitThread.hpp\n    utility/WhatsNewInfoThread.cpp\n    utility/WhatsNewInfoThread.hpp\n    utility/crypto-helpers-mbedtls.cpp\n    utility/crypto-helpers.hpp\n    utility/models/branches.hpp\n    utility/models/whatsnew.hpp\n    utility/platform-windows.cpp\n    utility/system-info-windows.cpp\n    utility/update-helpers.cpp\n    utility/update-helpers.hpp\n    utility/win-dll-blocklist.c\n)\n\nadd_library(obs-updater-manifest INTERFACE)\nadd_library(OBS::updater-manifest ALIAS obs-updater-manifest)\n\ntarget_sources(obs-updater-manifest INTERFACE updater/manifest.hpp)\n\ntarget_link_libraries(\n  obs-studio\n  PRIVATE\n    crypt32\n    OBS::blake2\n    OBS::updater-manifest\n    OBS::w32-pthreads\n    MbedTLS::mbedtls\n    nlohmann_json::nlohmann_json\n    Detours::Detours\n)\n\ntarget_compile_definitions(obs-studio PRIVATE PSAPI_VERSION=2)\n\ntarget_link_options(obs-studio PRIVATE /IGNORE:4099 $<$<CONFIG:DEBUG>:/NODEFAULTLIB:MSVCRT>)\n\n# Set commit for untagged version comparisons in the Windows updater\nif(OBS_VERSION MATCHES \".+g[a-f0-9]+.*\")\n  string(REGEX REPLACE \".+g([a-f0-9]+).*$\" \"\\\\1\" OBS_COMMIT ${OBS_VERSION})\nelse()\n  set(OBS_COMMIT \"\")\nendif()\n\nset_source_files_properties(utility/AutoUpdateThread.cpp PROPERTIES COMPILE_DEFINITIONS OBS_COMMIT=\"${OBS_COMMIT}\")\n\nadd_subdirectory(updater)\n\nset_property(TARGET obs-studio APPEND PROPERTY AUTORCC_OPTIONS --format-version 1)\n\nset_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT obs-studio)\nset_target_properties(\n  obs-studio\n  PROPERTIES\n    WIN32_EXECUTABLE TRUE\n    VS_DEBUGGER_COMMAND \"${CMAKE_BINARY_DIR}/rundir/$<CONFIG>/bin/64bit/$<TARGET_FILE_NAME:obs-studio>\"\n    VS_DEBUGGER_WORKING_DIRECTORY \"${CMAKE_BINARY_DIR}/rundir/$<CONFIG>/bin/64bit\"\n)\n"
  },
  {
    "path": "frontend/cmake/templates/ui-config.h.in",
    "content": "#pragma once\n\n#define OAUTH_BASE_URL \"@OAUTH_BASE_URL@\"\n\n#define TWITCH_CLIENTID \"@TWITCH_CLIENTID@\"\n#define TWITCH_HASH     0x@TWITCH_HASH@\n\n#define RESTREAM_CLIENTID \"@RESTREAM_CLIENTID@\"\n#define RESTREAM_HASH     0x@RESTREAM_HASH@\n\n#define YOUTUBE_CLIENTID      \"@YOUTUBE_CLIENTID@\"\n#define YOUTUBE_SECRET        \"@YOUTUBE_SECRET@\"\n#define YOUTUBE_CLIENTID_HASH 0x@YOUTUBE_CLIENTID_HASH@\n#define YOUTUBE_SECRET_HASH   0x@YOUTUBE_SECRET_HASH@\n\n#define DEFAULT_THEME \"com.obsproject.Yami.Original\"\n"
  },
  {
    "path": "frontend/cmake/ui-components.cmake",
    "content": "if(NOT TARGET OBS::qt-slider-ignorewheel)\n  add_subdirectory(\n    \"${CMAKE_SOURCE_DIR}/shared/qt/slider-ignorewheel\"\n    \"${CMAKE_BINARY_DIR}/shared/qt/slider-ignorewheel\"\n  )\nendif()\n\ntarget_link_libraries(obs-studio PRIVATE OBS::qt-slider-ignorewheel)\n\ntarget_sources(\n  obs-studio\n  PRIVATE\n    components/AbsoluteSlider.cpp\n    components/AbsoluteSlider.hpp\n    components/AccessibleAlignmentCell.cpp\n    components/AccessibleAlignmentCell.hpp\n    components/AccessibleAlignmentSelector.cpp\n    components/AccessibleAlignmentSelector.hpp\n    components/AlignmentSelector.cpp\n    components/AlignmentSelector.hpp\n    components/ApplicationAudioCaptureToolbar.cpp\n    components/ApplicationAudioCaptureToolbar.hpp\n    components/AudioCaptureToolbar.cpp\n    components/AudioCaptureToolbar.hpp\n    components/BalanceSlider.hpp\n    components/BrowserToolbar.cpp\n    components/BrowserToolbar.hpp\n    components/ClickableLabel.hpp\n    components/ColorSourceToolbar.cpp\n    components/ColorSourceToolbar.hpp\n    components/ComboSelectToolbar.cpp\n    components/ComboSelectToolbar.hpp\n    components/DelButton.hpp\n    components/DeviceCaptureToolbar.cpp\n    components/DeviceCaptureToolbar.hpp\n    components/DisplayCaptureToolbar.cpp\n    components/DisplayCaptureToolbar.hpp\n    components/EditWidget.hpp\n    components/FocusList.cpp\n    components/FocusList.hpp\n    components/GameCaptureToolbar.cpp\n    components/GameCaptureToolbar.hpp\n    components/ImageSourceToolbar.cpp\n    components/ImageSourceToolbar.hpp\n    components/MediaControls.cpp\n    components/MediaControls.hpp\n    components/MenuButton.cpp\n    components/MenuButton.hpp\n    components/MenuCheckBox.cpp\n    components/MenuCheckBox.hpp\n    components/Multiview.cpp\n    components/Multiview.hpp\n    components/MuteCheckBox.hpp\n    components/OBSAdvAudioCtrl.cpp\n    components/OBSAdvAudioCtrl.hpp\n    components/OBSPreviewScalingComboBox.cpp\n    components/OBSPreviewScalingComboBox.hpp\n    components/OBSPreviewScalingLabel.cpp\n    components/OBSPreviewScalingLabel.hpp\n    components/OBSSourceLabel.cpp\n    components/OBSSourceLabel.hpp\n    components/SceneTree.cpp\n    components/SceneTree.hpp\n    components/SilentUpdateCheckBox.hpp\n    components/SilentUpdateSpinBox.hpp\n    components/SourceToolbar.cpp\n    components/SourceToolbar.hpp\n    components/SourceTree.cpp\n    components/SourceTree.hpp\n    components/SourceTreeDelegate.cpp\n    components/SourceTreeDelegate.hpp\n    components/SourceTreeItem.cpp\n    components/SourceTreeItem.hpp\n    components/SourceTreeModel.cpp\n    components/SourceTreeModel.hpp\n    components/TextSourceToolbar.cpp\n    components/TextSourceToolbar.hpp\n    components/UIValidation.cpp\n    components/UIValidation.hpp\n    components/UrlPushButton.cpp\n    components/UrlPushButton.hpp\n    components/VisibilityItemDelegate.cpp\n    components/VisibilityItemDelegate.hpp\n    components/VisibilityItemWidget.cpp\n    components/VisibilityItemWidget.hpp\n    components/VolumeAccessibleInterface.cpp\n    components/VolumeAccessibleInterface.hpp\n    components/VolumeControl.cpp\n    components/VolumeControl.hpp\n    components/VolumeMeter.cpp\n    components/VolumeMeter.hpp\n    components/VolumeName.cpp\n    components/VolumeName.hpp\n    components/VolumeSlider.cpp\n    components/VolumeSlider.hpp\n    components/WindowCaptureToolbar.cpp\n    components/WindowCaptureToolbar.hpp\n)\n"
  },
  {
    "path": "frontend/cmake/ui-dialogs.cmake",
    "content": "if(NOT TARGET OBS::idian)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/shared/qt/idian\" \"${CMAKE_BINARY_DIR}/shared/qt/idian\")\nendif()\n\ntarget_link_libraries(obs-studio PRIVATE OBS::idian)\n\nif(NOT TARGET OBS::properties-view)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/shared/properties-view\" \"${CMAKE_BINARY_DIR}/shared/properties-view\")\nendif()\n\ntarget_link_libraries(obs-studio PRIVATE OBS::properties-view)\n\ntarget_sources(\n  obs-studio\n  PRIVATE\n    dialogs/LogUploadDialog.cpp\n    dialogs/LogUploadDialog.hpp\n    dialogs/NameDialog.cpp\n    dialogs/NameDialog.hpp\n    dialogs/OAuthLogin.cpp\n    dialogs/OAuthLogin.hpp\n    dialogs/OBSAbout.cpp\n    dialogs/OBSAbout.hpp\n    dialogs/OBSBasicAdvAudio.cpp\n    dialogs/OBSBasicAdvAudio.hpp\n    dialogs/OBSBasicFilters.cpp\n    dialogs/OBSBasicFilters.hpp\n    dialogs/OBSBasicInteraction.cpp\n    dialogs/OBSBasicInteraction.hpp\n    dialogs/OBSBasicProperties.cpp\n    dialogs/OBSBasicProperties.hpp\n    dialogs/OBSBasicSourceSelect.cpp\n    dialogs/OBSBasicSourceSelect.hpp\n    dialogs/OBSBasicTransform.cpp\n    dialogs/OBSBasicTransform.hpp\n    dialogs/OBSBasicVCamConfig.cpp\n    dialogs/OBSBasicVCamConfig.hpp\n    dialogs/OBSLogViewer.cpp\n    dialogs/OBSLogViewer.hpp\n    dialogs/OBSMissingFiles.cpp\n    dialogs/OBSMissingFiles.hpp\n    dialogs/OBSRemux.cpp\n    dialogs/OBSRemux.hpp\n    dialogs/OBSWhatsNew.cpp\n    dialogs/OBSWhatsNew.hpp\n)\n"
  },
  {
    "path": "frontend/cmake/ui-docks.cmake",
    "content": "target_sources(obs-studio PRIVATE docks/OBSDock.cpp docks/OBSDock.hpp)\n"
  },
  {
    "path": "frontend/cmake/ui-models.cmake",
    "content": "target_sources(obs-studio PRIVATE models/Rect.cpp models/Rect.hpp models/SceneCollection.cpp models/SceneCollection.hpp)\n"
  },
  {
    "path": "frontend/cmake/ui-oauth.cmake",
    "content": "target_sources(\n  obs-studio\n  PRIVATE oauth/Auth.cpp oauth/Auth.hpp oauth/AuthListener.cpp oauth/AuthListener.hpp oauth/OAuth.cpp oauth/OAuth.hpp\n)\n"
  },
  {
    "path": "frontend/cmake/ui-qt.cmake",
    "content": "find_package(Qt6 REQUIRED Widgets Network Svg Xml)\n\nif(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD)\n  find_package(Qt6 REQUIRED Gui DBus)\nendif()\n\nif(NOT TARGET OBS::qt-wrappers)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/shared/qt/wrappers\" \"${CMAKE_BINARY_DIR}/shared/qt/wrappers\")\nendif()\n\ntarget_link_libraries(\n  obs-studio\n  PRIVATE Qt::Widgets Qt::Svg Qt::Xml Qt::Network OBS::qt-wrappers\n)\n\nset_target_properties(\n  obs-studio\n  PROPERTIES AUTOMOC TRUE AUTOUIC TRUE AUTORCC TRUE AUTOGEN_PARALLEL AUTO\n)\n\n# https://doc.qt.io/qt-6/qtglobal.html#QT_ENABLE_STRICT_MODE_UP_TO\ntarget_compile_definitions(obs-studio PRIVATE QT_ENABLE_STRICT_MODE_UP_TO=0xFF0000)\n\nset_property(TARGET obs-studio APPEND PROPERTY AUTOUIC_SEARCH_PATHS forms forms/source-toolbar)\n\ntarget_sources(\n  obs-studio\n  PRIVATE\n    forms/AutoConfigFinishPage.ui\n    forms/AutoConfigStartPage.ui\n    forms/AutoConfigStartPage.ui\n    forms/AutoConfigStreamPage.ui\n    forms/AutoConfigTestPage.ui\n    forms/AutoConfigVideoPage.ui\n    forms/ColorSelect.ui\n    forms/LogUploadDialog.ui\n    forms/OBSAbout.ui\n    forms/OBSAdvAudio.ui\n    forms/OBSBasic.ui\n    forms/OBSBasicControls.ui\n    forms/OBSBasicFilters.ui\n    forms/OBSBasicInteraction.ui\n    forms/OBSBasicProperties.ui\n    forms/OBSBasicSettings.ui\n    forms/OBSBasicSourceSelect.ui\n    forms/OBSBasicVCamConfig.ui\n    forms/OBSExtraBrowsers.ui\n    forms/OBSImporter.ui\n    forms/OBSMissingFiles.ui\n    forms/OBSRemux.ui\n    forms/StatusBarWidget.ui\n    forms/PluginManagerWindow.ui\n    forms/obs.qrc\n    forms/source-toolbar/browser-source-toolbar.ui\n    forms/source-toolbar/color-source-toolbar.ui\n    forms/source-toolbar/device-select-toolbar.ui\n    forms/source-toolbar/game-capture-toolbar.ui\n    forms/source-toolbar/image-source-toolbar.ui\n    forms/source-toolbar/media-controls.ui\n    forms/source-toolbar/text-source-toolbar.ui\n)\n"
  },
  {
    "path": "frontend/cmake/ui-settings.cmake",
    "content": "target_sources(\n  obs-studio\n  PRIVATE\n    settings/OBSBasicSettings_A11y.cpp\n    settings/OBSBasicSettings_Appearance.cpp\n    settings/OBSBasicSettings_Stream.cpp\n    settings/OBSBasicSettings.cpp\n    settings/OBSBasicSettings.hpp\n    settings/OBSHotkeyEdit.cpp\n    settings/OBSHotkeyEdit.hpp\n    settings/OBSHotkeyLabel.cpp\n    settings/OBSHotkeyLabel.hpp\n    settings/OBSHotkeyWidget.cpp\n    settings/OBSHotkeyWidget.hpp\n)\n"
  },
  {
    "path": "frontend/cmake/ui-utility.cmake",
    "content": "target_sources(\n  obs-studio\n  PRIVATE\n    utility/AdvancedOutput.cpp\n    utility/AdvancedOutput.hpp\n    utility/BaseLexer.hpp\n    utility/BasicOutputHandler.cpp\n    utility/BasicOutputHandler.hpp\n    utility/CrashHandler.cpp\n    utility/CrashHandler.hpp\n    utility/FFmpegCodec.cpp\n    utility/FFmpegCodec.hpp\n    utility/FFmpegFormat.cpp\n    utility/FFmpegFormat.hpp\n    utility/FFmpegShared.hpp\n    utility/GoLiveAPI_CensoredJson.cpp\n    utility/GoLiveAPI_CensoredJson.hpp\n    utility/GoLiveAPI_Network.cpp\n    utility/GoLiveAPI_Network.hpp\n    utility/GoLiveAPI_PostData.cpp\n    utility/GoLiveAPI_PostData.hpp\n    utility/MissingFilesModel.cpp\n    utility/MissingFilesModel.hpp\n    utility/MissingFilesPathItemDelegate.cpp\n    utility/MissingFilesPathItemDelegate.hpp\n    utility/MultitrackVideoError.cpp\n    utility/MultitrackVideoError.hpp\n    utility/MultitrackVideoOutput.cpp\n    utility/MultitrackVideoOutput.hpp\n    utility/NativeEventFilter.hpp\n    utility/OBSCanvas.cpp\n    utility/OBSCanvas.hpp\n    utility/OBSEventFilter.hpp\n    utility/OBSProxyStyle.cpp\n    utility/OBSProxyStyle.hpp\n    utility/OBSTheme.hpp\n    utility/OBSThemeVariable.hpp\n    utility/OBSTranslator.cpp\n    utility/OBSTranslator.hpp\n    utility/PreviewProgramSizeObserver.cpp\n    utility/PreviewProgramSizeObserver.hpp\n    utility/QuickTransition.cpp\n    utility/QuickTransition.hpp\n    utility/RemoteTextThread.cpp\n    utility/RemoteTextThread.hpp\n    utility/RemuxEntryPathItemDelegate.cpp\n    utility/RemuxEntryPathItemDelegate.hpp\n    utility/RemuxQueueModel.cpp\n    utility/RemuxQueueModel.hpp\n    utility/RemuxWorker.cpp\n    utility/RemuxWorker.hpp\n    utility/SceneRenameDelegate.cpp\n    utility/SceneRenameDelegate.hpp\n    utility/ScreenshotObj.cpp\n    utility/ScreenshotObj.hpp\n    utility/SettingsEventFilter.hpp\n    utility/SimpleOutput.cpp\n    utility/SimpleOutput.hpp\n    utility/StartMultiTrackVideoStreamingGuard.hpp\n    utility/SurfaceEventFilter.hpp\n    utility/VCamConfig.hpp\n    utility/audio-encoders.cpp\n    utility/audio-encoders.hpp\n    utility/display-helpers.hpp\n    utility/item-widget-helpers.cpp\n    utility/item-widget-helpers.hpp\n    utility/models/multitrack-video.hpp\n    utility/obf.c\n    utility/obf.h\n    utility/platform.hpp\n    utility/system-info.hpp\n    utility/undo_stack.cpp\n    utility/undo_stack.hpp\n)\n"
  },
  {
    "path": "frontend/cmake/ui-widgets.cmake",
    "content": "if(NOT TARGET OBS::qt-vertical-scroll-area)\n  add_subdirectory(\n    \"${CMAKE_SOURCE_DIR}/shared/qt/vertical-scroll-area\"\n    \"${CMAKE_BINARY_DIR}/shared/qt/vertical-scroll-area\"\n  )\nendif()\n\ntarget_link_libraries(obs-studio PRIVATE OBS::qt-vertical-scroll-area)\n\ntarget_sources(\n  obs-studio\n  PRIVATE\n    widgets/AudioMixer.cpp\n    widgets/AudioMixer.hpp\n    widgets/ColorSelect.cpp\n    widgets/ColorSelect.hpp\n    widgets/OBSBasic.cpp\n    widgets/OBSBasic.hpp\n    widgets/OBSBasic_Browser.cpp\n    widgets/OBSBasic_Canvases.cpp\n    widgets/OBSBasic_Clipboard.cpp\n    widgets/OBSBasic_ContextToolbar.cpp\n    widgets/OBSBasic_Docks.cpp\n    widgets/OBSBasic_Dropfiles.cpp\n    widgets/OBSBasic_Hotkeys.cpp\n    widgets/OBSBasic_Icons.cpp\n    widgets/OBSBasic_MainControls.cpp\n    widgets/OBSBasic_OutputHandler.cpp\n    widgets/OBSBasic_Preview.cpp\n    widgets/OBSBasic_Profiles.cpp\n    widgets/OBSBasic_Projectors.cpp\n    widgets/OBSBasic_Recording.cpp\n    widgets/OBSBasic_ReplayBuffer.cpp\n    widgets/OBSBasic_SceneCollections.cpp\n    widgets/OBSBasic_SceneItems.cpp\n    widgets/OBSBasic_Scenes.cpp\n    widgets/OBSBasic_Screenshots.cpp\n    widgets/OBSBasic_Service.cpp\n    widgets/OBSBasic_StatusBar.cpp\n    widgets/OBSBasic_Streaming.cpp\n    widgets/OBSBasic_StudioMode.cpp\n    widgets/OBSBasic_SysTray.cpp\n    widgets/OBSBasic_Transitions.cpp\n    widgets/OBSBasic_Updater.cpp\n    widgets/OBSBasic_VirtualCam.cpp\n    widgets/OBSBasic_YouTube.cpp\n    widgets/OBSBasicControls.cpp\n    widgets/OBSBasicControls.hpp\n    widgets/OBSBasicPreview.cpp\n    widgets/OBSBasicPreview.hpp\n    widgets/OBSBasicStats.cpp\n    widgets/OBSBasicStats.hpp\n    widgets/OBSBasicStatusBar.cpp\n    widgets/OBSBasicStatusBar.hpp\n    widgets/OBSMainWindow.hpp\n    widgets/OBSProjector.cpp\n    widgets/OBSProjector.hpp\n    widgets/OBSQTDisplay.cpp\n    widgets/OBSQTDisplay.hpp\n    widgets/StatusBarWidget.cpp\n    widgets/StatusBarWidget.hpp\n)\n"
  },
  {
    "path": "frontend/cmake/ui-wizards.cmake",
    "content": "target_sources(\n  obs-studio\n  PRIVATE\n    wizards/AutoConfig.cpp\n    wizards/AutoConfig.hpp\n    wizards/AutoConfigStartPage.cpp\n    wizards/AutoConfigStartPage.hpp\n    wizards/AutoConfigStreamPage.cpp\n    wizards/AutoConfigStreamPage.hpp\n    wizards/AutoConfigTestPage.cpp\n    wizards/AutoConfigTestPage.hpp\n    wizards/AutoConfigVideoPage.cpp\n    wizards/AutoConfigVideoPage.hpp\n    wizards/TestMode.hpp\n)\n"
  },
  {
    "path": "frontend/cmake/windows/obs.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n    <description>OBS Studio</description>\n    <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n        <security>\n            <requestedPrivileges>\n                <requestedExecutionLevel\n                    level=\"asInvoker\"\n                    uiAccess=\"false\"\n                />\n            </requestedPrivileges>\n        </security>\n    </trustInfo>\n    <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n        <application>\n            <!-- Windows 10 and Windows 11 -->\n            <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\n        </application> \n    </compatibility>\n</assembly>\n"
  },
  {
    "path": "frontend/cmake/windows/obs.rc.in",
    "content": "IDI_ICON1 ICON DISCARDABLE \"cmake/windows/obs-studio.ico\"\n\n1 VERSIONINFO\nFILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"040904B0\"\n    BEGIN\n      VALUE \"CompanyName\", \"OBS\"\n      VALUE \"FileDescription\", \"OBS Studio\"\n      VALUE \"FileVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"InternalName\", \"obs\"\n      VALUE \"OriginalFilename\", \"obs\"\n      VALUE \"ProductName\", \"OBS Studio\"\n      VALUE \"ProductVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"Comments\", \"Free and open source software for video recording and live streaming\"\n      VALUE \"LegalCopyright\", \"${OBS_LEGAL_COPYRIGHT}\"\n    END\n  END\n\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0409, 0x04B0\n  END\nEND\n"
  },
  {
    "path": "frontend/components/AbsoluteSlider.cpp",
    "content": "#include \"AbsoluteSlider.hpp\"\n\n#include <QPainter>\n\n#include \"moc_AbsoluteSlider.cpp\"\n\nAbsoluteSlider::AbsoluteSlider(QWidget *parent) : SliderIgnoreScroll(parent)\n{\n\tinstallEventFilter(this);\n\tsetMouseTracking(true);\n\n\ttickColor.setRgb(0x5b, 0x62, 0x73);\n}\n\nAbsoluteSlider::AbsoluteSlider(Qt::Orientation orientation, QWidget *parent) : SliderIgnoreScroll(orientation, parent)\n{\n\tinstallEventFilter(this);\n\tsetMouseTracking(true);\n\n\ttickColor.setRgb(0x5b, 0x62, 0x73);\n}\n\nvoid AbsoluteSlider::mousePressEvent(QMouseEvent *event)\n{\n\tdragging = (event->buttons() & Qt::LeftButton || event->buttons() & Qt::MiddleButton);\n\n\tif (dragging) {\n\t\tsetSliderDown(true);\n\t\tsetValue(posToRangeValue(event));\n\t\temit AbsoluteSlider::sliderMoved(posToRangeValue(event));\n\t}\n\n\tevent->accept();\n}\n\nvoid AbsoluteSlider::mouseReleaseEvent(QMouseEvent *event)\n{\n\tdragging = false;\n\tsetSliderDown(false);\n\tevent->accept();\n}\n\nvoid AbsoluteSlider::mouseMoveEvent(QMouseEvent *event)\n{\n\tint val = posToRangeValue(event);\n\n\tif (val > maximum())\n\t\tval = maximum();\n\telse if (val < minimum())\n\t\tval = minimum();\n\n\temit absoluteSliderHovered(val);\n\n\tif (dragging) {\n\t\tsetValue(posToRangeValue(event));\n\t\temit AbsoluteSlider::sliderMoved(posToRangeValue(event));\n\t}\n\n\tQSlider::mouseMoveEvent(event);\n\tevent->accept();\n}\n\nbool AbsoluteSlider::eventFilter(QObject *obj, QEvent *event)\n{\n\tif (event->type() == QEvent::KeyPress) {\n\t\tQKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);\n\n\t\tif (keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Down) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn QSlider::eventFilter(obj, event);\n}\n\nint AbsoluteSlider::posToRangeValue(QMouseEvent *event)\n{\n\tQStyleOptionSlider opt;\n\tinitStyleOption(&opt);\n\n\tint pos;\n\tint sliderMin;\n\tint sliderMax;\n\tint handleLength;\n\n\tconst QRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);\n\tconst QRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);\n\n\tif (orientation() == Qt::Horizontal) {\n\t\tpos = event->pos().x();\n\t\thandleLength = handle.width();\n\t\tsliderMin = groove.left() + (handleLength / 2);\n\t\tsliderMax = groove.right() - (handleLength / 2) + 1;\n\t} else {\n\t\tpos = event->pos().y();\n\t\thandleLength = handle.height();\n\t\tsliderMin = groove.top() + (handleLength / 2);\n\t\tsliderMax = groove.bottom() - (handleLength / 2) + 1;\n\t}\n\n\tint sliderValue = style()->sliderValueFromPosition(minimum(), maximum(), pos - sliderMin, sliderMax - sliderMin,\n\t\t\t\t\t\t\t   opt.upsideDown);\n\n\treturn sliderValue;\n}\n\nbool AbsoluteSlider::getDisplayTicks() const\n{\n\treturn displayTicks;\n}\n\nvoid AbsoluteSlider::setDisplayTicks(bool display)\n{\n\tdisplayTicks = display;\n}\n\nQColor AbsoluteSlider::getTickColor() const\n{\n\treturn tickColor;\n}\n\nvoid AbsoluteSlider::setTickColor(QColor c)\n{\n\ttickColor = std::move(c);\n}\n\nvoid AbsoluteSlider::paintEvent(QPaintEvent *event)\n{\n\tif (!getDisplayTicks()) {\n\t\tQSlider::paintEvent(event);\n\t\treturn;\n\t}\n\n\tQPainter painter(this);\n\n\tQStyleOptionSlider opt;\n\tinitStyleOption(&opt);\n\n\tQRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);\n\tQRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);\n\n\tconst bool isHorizontal = orientation() == Qt::Horizontal;\n\n\tconst int sliderLength = isHorizontal ? groove.width() - handle.width() : groove.height() - handle.height();\n\tconst int handleSize = isHorizontal ? handle.width() : handle.height();\n\tconst int grooveSize = isHorizontal ? groove.height() : groove.width();\n\tconst int grooveStart = isHorizontal ? groove.left() : groove.top();\n\tconst int tickLinePos = isHorizontal ? groove.center().y() : groove.center().x();\n\tconst int tickLength = std::max((int)(grooveSize * 1.5) + grooveSize, 8 + grooveSize);\n\tconst int tickLineStart = tickLinePos - (tickLength / 2) + 1;\n\n\tfor (double offset = minimum(); offset <= maximum(); offset += singleStep()) {\n\t\tdouble tickPercent = (offset - minimum()) / (maximum() - minimum());\n\t\tconst int tickLineOffset = grooveStart + std::floor(sliderLength * tickPercent) + (handleSize / 2);\n\n\t\tconst int xPos = isHorizontal ? tickLineOffset : tickLineStart;\n\t\tconst int yPos = isHorizontal ? tickLineStart : tickLineOffset;\n\n\t\tconst int tickWidth = isHorizontal ? 1 : tickLength;\n\t\tconst int tickHeight = isHorizontal ? tickLength : 1;\n\n\t\tpainter.fillRect(xPos, yPos, tickWidth, tickHeight, tickColor);\n\t}\n\n\tQSlider::paintEvent(event);\n}\n"
  },
  {
    "path": "frontend/components/AbsoluteSlider.hpp",
    "content": "#pragma once\n\n#include <slider-ignorewheel.hpp>\n\nclass AbsoluteSlider : public SliderIgnoreScroll {\n\tQ_OBJECT\n\tQ_PROPERTY(QColor tickColor READ getTickColor WRITE setTickColor DESIGNABLE true)\n\npublic:\n\tAbsoluteSlider(QWidget *parent = nullptr);\n\tAbsoluteSlider(Qt::Orientation orientation, QWidget *parent = nullptr);\n\n\tbool getDisplayTicks() const;\n\tvoid setDisplayTicks(bool display);\n\n\tQColor getTickColor() const;\n\tvoid setTickColor(QColor c);\n\nsignals:\n\tvoid absoluteSliderHovered(int value);\n\nprotected:\n\tvirtual void mouseMoveEvent(QMouseEvent *event) override;\n\tvirtual void mousePressEvent(QMouseEvent *event) override;\n\tvirtual void mouseReleaseEvent(QMouseEvent *event) override;\n\tvirtual bool eventFilter(QObject *obj, QEvent *event) override;\n\n\tint posToRangeValue(QMouseEvent *event);\n\n\tvirtual void paintEvent(QPaintEvent *event) override;\n\nprivate:\n\tbool dragging = false;\n\tbool displayTicks = false;\n\n\tQColor tickColor;\n};\n"
  },
  {
    "path": "frontend/components/AccessibleAlignmentCell.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"AccessibleAlignmentCell.hpp\"\n\n#include <OBSApp.hpp>\n\nusing namespace std::string_view_literals;\nconstexpr std::array indexToStrings = {\n\t\"Basic.TransformWindow.Alignment.TopLeft\"sv,    \"Basic.TransformWindow.Alignment.TopCenter\"sv,\n\t\"Basic.TransformWindow.Alignment.TopRight\"sv,   \"Basic.TransformWindow.Alignment.CenterLeft\"sv,\n\t\"Basic.TransformWindow.Alignment.Center\"sv,     \"Basic.TransformWindow.Alignment.CenterRight\"sv,\n\t\"Basic.TransformWindow.Alignment.BottomLeft\"sv, \"Basic.TransformWindow.Alignment.BottomCenter\"sv,\n\t\"Basic.TransformWindow.Alignment.BottomRight\"sv};\n\nAccessibleAlignmentCell::AccessibleAlignmentCell(QAccessibleInterface *parent, AlignmentSelector *widget, int index)\n\t: parent_(parent),\n\t  widget(widget),\n\t  index_(index)\n{\n}\n\nQRect AccessibleAlignmentCell::rect() const\n{\n\treturn widget->cellRect(index_);\n}\n\nQString AccessibleAlignmentCell::text(QAccessible::Text text) const\n{\n\tif (text == QAccessible::Name || text == QAccessible::Value) {\n\t\treturn QString(indexToStrings[index_].data());\n\t}\n\treturn QString();\n}\n\nQAccessible::State AccessibleAlignmentCell::state() const\n{\n\tQAccessible::State state;\n\tbool enabled = widget->isEnabled();\n\n\tbool isSelectedCell = widget->currentIndex() == index_;\n\tbool isFocusedCell = widget->focusedCell == index_;\n\n\tstate.disabled = !enabled;\n\tstate.focusable = enabled;\n\tstate.focused = widget->hasFocus() && isFocusedCell;\n\tstate.checkable = true;\n\tstate.checked = isSelectedCell;\n\tstate.selected = isSelectedCell;\n\n\treturn state;\n}\n\nQAccessible::Role AccessibleAlignmentCell::role() const\n{\n\treturn QAccessible::CheckBox;\n}\n"
  },
  {
    "path": "frontend/components/AccessibleAlignmentCell.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <components/AlignmentSelector.hpp>\n\n#include <QAccessibleInterface>\n#include <QRect>\n\nclass AlignmentSelector;\n\nclass AccessibleAlignmentCell : public QAccessibleInterface {\n\tQAccessibleInterface *parent_;\n\tAlignmentSelector *widget;\n\tint index_;\n\npublic:\n\tAccessibleAlignmentCell(QAccessibleInterface *parent, AlignmentSelector *widget, int index);\n\n\tint index() const { return index_; }\n\n\tQRect rect() const override;\n\tQString text(QAccessible::Text t) const override;\n\tQAccessible::State state() const override;\n\tQAccessible::Role role() const override;\n\n\tQObject *object() const override { return nullptr; }\n\tQAccessibleInterface *child(int) const override { return nullptr; }\n\tQAccessibleInterface *childAt(int, int) const override { return nullptr; }\n\tint childCount() const override { return 0; }\n\tint indexOfChild(const QAccessibleInterface *) const override { return -1; }\n\tQAccessibleInterface *parent() const override { return parent_; }\n\tQAccessibleInterface *focusChild() const override { return nullptr; }\n\tbool isValid() const override { return widget != nullptr; }\n\tvoid setText(QAccessible::Text, const QString &) override {}\n};\n"
  },
  {
    "path": "frontend/components/AccessibleAlignmentSelector.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"AccessibleAlignmentSelector.hpp\"\n\n#include <OBSApp.hpp>\n\nAccessibleAlignmentSelector::AccessibleAlignmentSelector(AlignmentSelector *widget_)\n\t: QAccessibleWidget(widget_, QAccessible::Grouping)\n{\n\tfor (int i = 0; i < cellCount; ++i) {\n\t\tAccessibleAlignmentCell *cell = new AccessibleAlignmentCell(this, widget_, i);\n\t\tQAccessible::registerAccessibleInterface(cell);\n\t\tcellInterfaces.insert(i, QAccessible::uniqueId(cell));\n\t}\n}\n\nAccessibleAlignmentSelector::~AccessibleAlignmentSelector()\n{\n\tfor (QAccessible::Id id : std::as_const(cellInterfaces)) {\n\t\tQAccessible::deleteAccessibleInterface(id);\n\t}\n}\n\nint AccessibleAlignmentSelector::childCount() const\n{\n\treturn cellCount;\n}\n\nQAccessibleInterface *AccessibleAlignmentSelector::child(int index) const\n{\n\tif (QAccessible::Id id = cellInterfaces.value(index)) {\n\t\treturn QAccessible::accessibleInterface(id);\n\t}\n\n\treturn nullptr;\n}\n\nint AccessibleAlignmentSelector::indexOfChild(const QAccessibleInterface *child) const\n{\n\tif (!child) {\n\t\treturn -1;\n\t}\n\n\tQAccessible::Id id = QAccessible::uniqueId(const_cast<QAccessibleInterface *>(child));\n\treturn cellInterfaces.key(id, -1);\n}\n\nbool AccessibleAlignmentSelector::isValid() const\n{\n\treturn widget() != nullptr;\n}\n\nQAccessibleInterface *AccessibleAlignmentSelector::focusChild() const\n{\n\tfor (int i = 0; i < childCount(); ++i) {\n\t\tif (child(i)->state().focused) {\n\t\t\treturn child(i);\n\t\t}\n\t}\n\treturn nullptr;\n}\n\nQRect AccessibleAlignmentSelector::rect() const\n{\n\treturn widget()->rect();\n}\n\nQString AccessibleAlignmentSelector::text(QAccessible::Text textType) const\n{\n\tif (textType == QAccessible::Name) {\n\t\tQString str = widget()->accessibleName();\n\t\tif (str.isEmpty()) {\n\t\t\tstr = QTStr(\"Accessible.Widget.Name.AlignmentSelector\");\n\t\t}\n\t\treturn str;\n\t}\n\n\tif (textType == QAccessible::Value) {\n\t\treturn value().toString();\n\t}\n\n\treturn QAccessibleWidget::text(textType);\n}\n\nQAccessible::Role AccessibleAlignmentSelector::role() const\n{\n\treturn QAccessible::Grouping;\n}\n\nQAccessible::State AccessibleAlignmentSelector::state() const\n{\n\tQAccessible::State state;\n\n\tstate.focusable = true;\n\tstate.focused = widget()->hasFocus();\n\tstate.disabled = !widget()->isEnabled();\n\tstate.readOnly = false;\n\n\treturn state;\n}\n\nQVariant AccessibleAlignmentSelector::value() const\n{\n\tfor (int i = 0; i < childCount(); ++i) {\n\t\tif (child(i)->state().checked) {\n\t\t\treturn child(i)->text(QAccessible::Name);\n\t\t}\n\t}\n\n\treturn QTStr(\"None\");\n}\n"
  },
  {
    "path": "frontend/components/AccessibleAlignmentSelector.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <components/AccessibleAlignmentCell.hpp>\n\n#include <QAccessible>\n#include <QAccessibleInterface>\n#include <QAccessibleWidget>\n\nclass AlignmentSelector;\n\nclass AccessibleAlignmentSelector : public QAccessibleWidget {\n\tmutable QHash<int, QAccessible::Id> cellInterfaces{};\n\tstatic constexpr int cellCount = 9;\n\npublic:\n\texplicit AccessibleAlignmentSelector(AlignmentSelector *widget);\n\t~AccessibleAlignmentSelector();\n\n\tQRect rect() const override;\n\tQAccessible::Role role() const override;\n\tQAccessible::State state() const override;\n\tQString text(QAccessible::Text t) const override;\n\tQAccessibleInterface *child(int index) const override;\n\tint childCount() const override;\n\tint indexOfChild(const QAccessibleInterface *child) const override;\n\tbool isValid() const override;\n\tQAccessibleInterface *focusChild() const override;\n\n\tQVariant value() const;\n};\n"
  },
  {
    "path": "frontend/components/AlignmentSelector.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"AlignmentSelector.hpp\"\n\n#include <util/base.h>\n\n#include <QAccessible>\n#include <QMouseEvent>\n#include <QPainter>\n#include <QStyleOptionButton>\n\nAlignmentSelector::AlignmentSelector(QWidget *parent) : QWidget(parent)\n{\n\tsetFocusPolicy(Qt::StrongFocus);\n\tsetMouseTracking(true);\n\tsetAttribute(Qt::WA_Hover);\n}\n\nQSize AlignmentSelector::sizeHint() const\n{\n\tint base = fontMetrics().height() * 2;\n\treturn QSize(base, base);\n}\n\nQSize AlignmentSelector::minimumSizeHint() const\n{\n\treturn QSize(16, 16);\n}\n\nQt::Alignment AlignmentSelector::value() const\n{\n\treturn cellAlignment(selectedCell);\n}\n\nint AlignmentSelector::currentIndex() const\n{\n\treturn selectedCell;\n}\n\nvoid AlignmentSelector::setAlignment(Qt::Alignment value)\n{\n\talignment = value;\n}\n\nvoid AlignmentSelector::setCurrentIndex(int index)\n{\n\tselectCell(index);\n}\n\nvoid AlignmentSelector::paintEvent(QPaintEvent *)\n{\n\tQPainter painter(this);\n\tQStyle *style = this->style();\n\n\tint cellW = gridRect().width() / 3;\n\tint cellH = gridRect().height() / 3;\n\n\tfor (int i = 0; i < 9; ++i) {\n\t\tQRect rect = cellRect(i);\n\t\trect = rect.adjusted(0, 0, -1, -1);\n\n\t\tQStyleOptionFrame frameOpt;\n\t\tframeOpt.rect = rect;\n\t\tframeOpt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;\n\t\tframeOpt.lineWidth = 1;\n\t\tframeOpt.midLineWidth = 0;\n\t\tif (i == hoveredCell) {\n\t\t\tframeOpt.state |= QStyle::State_MouseOver;\n\t\t}\n\t\tif (i == selectedCell) {\n\t\t\tframeOpt.state |= QStyle::State_On;\n\t\t}\n\t\tif (i == focusedCell && hasFocus()) {\n\t\t\tframeOpt.state |= QStyle::State_HasFocus;\n\t\t}\n\n\t\tQStyleOptionButton radioOpt;\n\t\tradioOpt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;\n\t\tradioOpt.rect = rect.adjusted(cellW / 6, cellH / 6, -cellW / 6, -cellH / 6);\n\t\tif (i == hoveredCell) {\n\t\t\tradioOpt.state |= QStyle::State_MouseOver;\n\t\t}\n\t\tif (i == selectedCell) {\n\t\t\tradioOpt.state |= QStyle::State_On;\n\t\t}\n\n\t\tif (i == focusedCell && hasFocus()) {\n\t\t\tradioOpt.state |= QStyle::State_HasFocus;\n\t\t}\n\t\tstyle->drawPrimitive(QStyle::PE_IndicatorRadioButton, &radioOpt, &painter, this);\n\n\t\tstyle->drawPrimitive(QStyle::PE_Frame, &frameOpt, &painter, this);\n\n\t\tif (i == focusedCell && hasFocus()) {\n\t\t\tQStyleOptionFocusRect focusOpt;\n\t\t\tfocusOpt.initFrom(this);\n\t\t\tfocusOpt.rect = rect.adjusted(1, 1, -1, -1);\n\t\t\tfocusOpt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;\n\t\t\tfocusOpt.state |= QStyle::State_HasFocus;\n\t\t\tstyle->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this);\n\t\t}\n\t}\n}\n\nQRect AlignmentSelector::cellRect(int index) const\n{\n\tint col = index % 3;\n\tint row = index / 3;\n\n\tQRect gridRect = this->gridRect();\n\tint cellW = gridRect.width() / 3;\n\tint cellH = gridRect.height() / 3;\n\n\treturn QRect(col * cellW + gridRect.left(), row * cellH + gridRect.top(), cellW, cellH);\n}\n\nQt::Alignment AlignmentSelector::cellAlignment(int index) const\n{\n\tQt::Alignment hAlign;\n\tQt::Alignment vAlign;\n\n\tswitch (index % 3) {\n\tcase 0:\n\t\thAlign = Qt::AlignLeft;\n\t\tbreak;\n\tcase 1:\n\t\thAlign = Qt::AlignHCenter;\n\t\tbreak;\n\tcase 2:\n\t\thAlign = Qt::AlignRight;\n\t\tbreak;\n\t}\n\n\tswitch (index / 3) {\n\tcase 0:\n\t\tvAlign = Qt::AlignTop;\n\t\tbreak;\n\tcase 1:\n\t\tvAlign = Qt::AlignVCenter;\n\t\tbreak;\n\tcase 2:\n\t\tvAlign = Qt::AlignBottom;\n\t\tbreak;\n\t}\n\n\treturn hAlign | vAlign;\n}\n\nvoid AlignmentSelector::leaveEvent(QEvent *)\n{\n\thoveredCell = -1;\n\tupdate();\n}\n\nvoid AlignmentSelector::mouseMoveEvent(QMouseEvent *event)\n{\n\tQRect grid = gridRect();\n\tint cellW = grid.width() / 3;\n\tint cellH = grid.height() / 3;\n\n\tQPoint pos = event->position().toPoint();\n\tif (!grid.contains(pos)) {\n\t\thoveredCell = -1;\n\t\treturn;\n\t}\n\n\tint col = (pos.x() - grid.left()) / cellW;\n\tint row = (pos.y() - grid.top()) / cellH;\n\tint cell = row * 3 + col;\n\n\tif (hoveredCell != cell) {\n\t\thoveredCell = cell;\n\t\tupdate();\n\t}\n}\n\nvoid AlignmentSelector::mousePressEvent(QMouseEvent *event)\n{\n\tQRect grid = gridRect();\n\tint cellW = grid.width() / 3;\n\tint cellH = grid.height() / 3;\n\n\tQPoint pos = event->position().toPoint();\n\tif (!grid.contains(pos)) {\n\t\treturn;\n\t}\n\n\tint col = (pos.x() - grid.left()) / cellW;\n\tint row = (pos.y() - grid.top()) / cellH;\n\tint cell = row * 3 + col;\n\n\tselectCell(cell);\n}\n\nvoid AlignmentSelector::keyPressEvent(QKeyEvent *event)\n{\n\tint moveX = 0;\n\tint moveY = 0;\n\n\tswitch (event->key()) {\n\tcase Qt::Key_Left:\n\t\tmoveX = -1;\n\t\tbreak;\n\tcase Qt::Key_Right:\n\t\tmoveX = 1;\n\t\tbreak;\n\tcase Qt::Key_Up:\n\t\tmoveY = -1;\n\t\tbreak;\n\tcase Qt::Key_Down:\n\t\tmoveY = 1;\n\t\tbreak;\n\tcase Qt::Key_Space:\n\tcase Qt::Key_Return:\n\tcase Qt::Key_Enter:\n\t\tselectCell(focusedCell);\n\t\treturn;\n\tdefault:\n\t\tQWidget::keyPressEvent(event);\n\t\treturn;\n\t}\n\n\tmoveFocusedCell(moveX, moveY);\n}\n\nQRect AlignmentSelector::gridRect() const\n{\n\tint side = std::min(width(), height());\n\tint x = 0;\n\tint y = 0;\n\n\tif (alignment & Qt::AlignHCenter) {\n\t\tx = (width() - side) / 2;\n\t} else if (alignment & Qt::AlignRight) {\n\t\tx = width() - side;\n\t}\n\n\tif (alignment & Qt::AlignVCenter) {\n\t\ty = (height() - side) / 2;\n\t} else if (alignment & Qt::AlignBottom) {\n\t\ty = height() - side;\n\t}\n\n\treturn QRect(x, y, side, side);\n}\n\nvoid AlignmentSelector::moveFocusedCell(int moveX, int moveY)\n{\n\tint row = focusedCell / 3;\n\tint col = focusedCell % 3;\n\n\trow = std::clamp(row + moveY, 0, 2);\n\tcol = std::clamp(col + moveX, 0, 2);\n\n\tint newCell = row * 3 + col;\n\tsetFocusedCell(newCell);\n}\n\nvoid AlignmentSelector::setFocusedCell(int cell)\n{\n\tif (cell != focusedCell) {\n\t\tfocusedCell = cell;\n\t\tupdate();\n\n\t\tif (AccessibleAlignmentSelector *interface =\n\t\t\t    dynamic_cast<AccessibleAlignmentSelector *>(QAccessible::queryAccessibleInterface(this))) {\n\t\t\tif (QAccessibleInterface *child = interface->child(cell)) {\n\t\t\t\tQAccessibleEvent event(child, QAccessible::Focus);\n\t\t\t\tQAccessible::updateAccessibility(&event);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid AlignmentSelector::selectCell(int cell)\n{\n\tsetFocusedCell(cell);\n\tif (cell != selectedCell) {\n\t\tselectedCell = cell;\n\n\t\temit valueChanged(cellAlignment(cell));\n\t\temit currentIndexChanged(cell);\n\t}\n\tupdate();\n\n\tif (AccessibleAlignmentSelector *interface =\n\t\t    dynamic_cast<AccessibleAlignmentSelector *>(QAccessible::queryAccessibleInterface(this))) {\n\t\tif (QAccessibleInterface *child = interface->child(cell)) {\n\t\t\tQAccessible::State state;\n\t\t\tstate.checked = true;\n\n\t\t\tQAccessibleStateChangeEvent event(child, state);\n\t\t\tQAccessible::updateAccessibility(&event);\n\t\t}\n\t}\n}\n\nvoid AlignmentSelector::focusInEvent(QFocusEvent *)\n{\n\tsetFocusedCell(selectedCell);\n\tupdate();\n}\n\nvoid AlignmentSelector::focusOutEvent(QFocusEvent *)\n{\n\thoveredCell = -1;\n\tsetFocusedCell(-1);\n\tupdate();\n}\n"
  },
  {
    "path": "frontend/components/AlignmentSelector.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <components/AccessibleAlignmentSelector.hpp>\n\n#include <QFocusEvent>\n#include <QKeyEvent>\n#include <QWidget>\n\nclass AlignmentSelector : public QWidget {\n\tQ_OBJECT\n\n\tfriend class AccessibleAlignmentSelector;\n\tfriend class AccessibleAlignmentCell;\n\npublic:\n\texplicit AlignmentSelector(QWidget *parent = nullptr);\n\n\tQSize sizeHint() const override;\n\tQSize minimumSizeHint() const override;\n\tQt::Alignment value() const;\n\tint currentIndex() const;\n\n\tvoid setAlignment(Qt::Alignment alignment);\n\tvoid setCurrentIndex(int index);\n\nsignals:\n\tvoid valueChanged(Qt::Alignment value);\n\tvoid currentIndexChanged(int value);\n\nprotected:\n\tQRect cellRect(int index) const;\n\n\tvoid paintEvent(QPaintEvent *event) override;\n\tvoid leaveEvent(QEvent *event) override;\n\tvoid mouseMoveEvent(QMouseEvent *event) override;\n\tvoid mousePressEvent(QMouseEvent *event) override;\n\tvoid keyPressEvent(QKeyEvent *event) override;\n\tvoid focusInEvent(QFocusEvent *event) override;\n\tvoid focusOutEvent(QFocusEvent *event) override;\n\nprivate:\n\tQt::Alignment alignment = Qt::AlignTop | Qt::AlignLeft;\n\n\tint hoveredCell = -1;\n\tint focusedCell = 4;\n\tint selectedCell = 4;\n\n\tQRect gridRect() const;\n\n\tvoid moveFocusedCell(int moveX, int moveY);\n\tvoid setFocusedCell(int cell);\n\tvoid selectCell(int cell);\n\tQt::Alignment cellAlignment(int index) const;\n};\n"
  },
  {
    "path": "frontend/components/ApplicationAudioCaptureToolbar.cpp",
    "content": "#include \"ApplicationAudioCaptureToolbar.hpp\"\n#include \"ui_device-select-toolbar.h\"\n#include \"moc_ApplicationAudioCaptureToolbar.cpp\"\n\nApplicationAudioCaptureToolbar::ApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source)\n\t: ComboSelectToolbar(parent, source)\n{\n}\n\nvoid ApplicationAudioCaptureToolbar::Init()\n{\n\tdelete ui->activateButton;\n\tui->activateButton = nullptr;\n\n\tobs_module_t *mod = obs_get_module(\"win-wasapi\");\n\tconst char *device_str = obs_module_get_locale_text(mod, \"Window\");\n\tui->deviceLabel->setText(device_str);\n\n\tprop_name = \"window\";\n\n\tComboSelectToolbar::Init();\n}\n"
  },
  {
    "path": "frontend/components/ApplicationAudioCaptureToolbar.hpp",
    "content": "#pragma once\n\n#include \"ComboSelectToolbar.hpp\"\n\nclass ApplicationAudioCaptureToolbar : public ComboSelectToolbar {\n\tQ_OBJECT\n\npublic:\n\tApplicationAudioCaptureToolbar(QWidget *parent, OBSSource source);\n\tvoid Init() override;\n};\n"
  },
  {
    "path": "frontend/components/AudioCaptureToolbar.cpp",
    "content": "#include \"AudioCaptureToolbar.hpp\"\n#include \"ui_device-select-toolbar.h\"\n#include \"moc_AudioCaptureToolbar.cpp\"\n\n#ifdef _WIN32\n#define get_os_module(win, mac, linux) obs_get_module(win)\n#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)\n#elif __APPLE__\n#define get_os_module(win, mac, linux) obs_get_module(mac)\n#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)\n#else\n#define get_os_module(win, mac, linux) obs_get_module(linux)\n#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)\n#endif\n\nAudioCaptureToolbar::AudioCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}\n\nvoid AudioCaptureToolbar::Init()\n{\n\tdelete ui->activateButton;\n\tui->activateButton = nullptr;\n\n\tobs_module_t *mod = get_os_module(\"win-wasapi\", \"mac-capture\", \"linux-pulseaudio\");\n\tif (!mod)\n\t\treturn;\n\n\tconst char *device_str = get_os_text(mod, \"Device\", \"CoreAudio.Device\", \"Device\");\n\tui->deviceLabel->setText(device_str);\n\n\tprop_name = \"device_id\";\n\n\tComboSelectToolbar::Init();\n}\n"
  },
  {
    "path": "frontend/components/AudioCaptureToolbar.hpp",
    "content": "#pragma once\n\n#include \"ComboSelectToolbar.hpp\"\n\nclass AudioCaptureToolbar : public ComboSelectToolbar {\n\tQ_OBJECT\n\npublic:\n\tAudioCaptureToolbar(QWidget *parent, OBSSource source);\n\tvoid Init() override;\n};\n"
  },
  {
    "path": "frontend/components/BalanceSlider.hpp",
    "content": "#pragma once\n\n#include <QMouseEvent>\n#include <QSlider>\n\nclass BalanceSlider : public QSlider {\n\tQ_OBJECT\n\npublic:\n\tinline BalanceSlider(QWidget *parent = 0) : QSlider(parent) {}\n\nsignals:\n\tvoid doubleClicked();\n\nprotected:\n\tvoid mouseDoubleClickEvent(QMouseEvent *event)\n\t{\n\t\temit doubleClicked();\n\t\tevent->accept();\n\t}\n};\n"
  },
  {
    "path": "frontend/components/BrowserToolbar.cpp",
    "content": "#include \"BrowserToolbar.hpp\"\n#include \"ui_browser-source-toolbar.h\"\n#include \"moc_BrowserToolbar.cpp\"\n\nBrowserToolbar::BrowserToolbar(QWidget *parent, OBSSource source)\n\t: SourceToolbar(parent, source),\n\t  ui(new Ui_BrowserSourceToolbar)\n{\n\tui->setupUi(this);\n}\n\nBrowserToolbar::~BrowserToolbar() {}\n\nvoid BrowserToolbar::on_refresh_clicked()\n{\n\tOBSSource source = GetSource();\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tobs_property_t *p = obs_properties_get(props.get(), \"refreshnocache\");\n\tobs_property_button_clicked(p, source.Get());\n}\n"
  },
  {
    "path": "frontend/components/BrowserToolbar.hpp",
    "content": "#pragma once\n\n#include \"SourceToolbar.hpp\"\n\nclass Ui_BrowserSourceToolbar;\n\nclass BrowserToolbar : public SourceToolbar {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui_BrowserSourceToolbar> ui;\n\npublic:\n\tBrowserToolbar(QWidget *parent, OBSSource source);\n\t~BrowserToolbar();\n\npublic slots:\n\tvoid on_refresh_clicked();\n};\n"
  },
  {
    "path": "frontend/components/ClickableLabel.hpp",
    "content": "#pragma once\n\n#include <QLabel>\n#include <QMouseEvent>\n\nclass ClickableLabel : public QLabel {\n\tQ_OBJECT\n\npublic:\n\tinline ClickableLabel(QWidget *parent = 0) : QLabel(parent) {}\n\nsignals:\n\tvoid clicked();\n\nprotected:\n\tvoid mousePressEvent(QMouseEvent *event)\n\t{\n\t\temit clicked();\n\t\tevent->accept();\n\t}\n};\n"
  },
  {
    "path": "frontend/components/ColorSourceToolbar.cpp",
    "content": "#include \"ColorSourceToolbar.hpp\"\n#include \"ui_color-source-toolbar.h\"\n\n#include <QColorDialog>\n\n#include \"moc_ColorSourceToolbar.cpp\"\n\nQColor color_from_int(long long val)\n{\n\treturn QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);\n}\n\nlong long color_to_int(QColor color)\n{\n\tauto shift = [&](unsigned val, int shift) {\n\t\treturn ((val & 0xff) << shift);\n\t};\n\n\treturn shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);\n}\n\nColorSourceToolbar::ColorSourceToolbar(QWidget *parent, OBSSource source)\n\t: SourceToolbar(parent, source),\n\t  ui(new Ui_ColorSourceToolbar)\n{\n\tui->setupUi(this);\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\tunsigned int val = (unsigned int)obs_data_get_int(settings, \"color\");\n\n\tcolor = color_from_int(val);\n\tUpdateColor();\n}\n\nColorSourceToolbar::~ColorSourceToolbar() {}\n\nvoid ColorSourceToolbar::UpdateColor()\n{\n\tQPalette palette = QPalette(color);\n\tui->color->setFrameStyle(QFrame::Sunken | QFrame::Panel);\n\tui->color->setText(color.name(QColor::HexRgb));\n\tui->color->setPalette(palette);\n\tui->color->setStyleSheet(QString(\"background-color :%1; color: %2;\")\n\t\t\t\t\t .arg(palette.color(QPalette::Window).name(QColor::HexRgb))\n\t\t\t\t\t .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));\n\tui->color->setAutoFillBackground(true);\n\tui->color->setAlignment(Qt::AlignCenter);\n}\n\nvoid ColorSourceToolbar::on_choose_clicked()\n{\n\tOBSSource source = GetSource();\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tobs_property_t *p = obs_properties_get(props.get(), \"color\");\n\tconst char *desc = obs_property_description(p);\n\n\tQColorDialog::ColorDialogOptions options;\n\n\toptions |= QColorDialog::ShowAlphaChannel;\n#ifdef __linux__\n\t// TODO: Revisit hang on Ubuntu with native dialog\n\toptions |= QColorDialog::DontUseNativeDialog;\n#endif\n\n\tQColor newColor = QColorDialog::getColor(color, this, desc, options);\n\tif (!newColor.isValid()) {\n\t\treturn;\n\t}\n\n\tcolor = newColor;\n\tUpdateColor();\n\n\tSaveOldProperties(source);\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_int(settings, \"color\", color_to_int(color));\n\tobs_source_update(source, settings);\n\n\tSetUndoProperties(source);\n}\n"
  },
  {
    "path": "frontend/components/ColorSourceToolbar.hpp",
    "content": "#pragma once\n\n#include \"SourceToolbar.hpp\"\n\nclass Ui_ColorSourceToolbar;\n\nclass ColorSourceToolbar : public SourceToolbar {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui_ColorSourceToolbar> ui;\n\tQColor color;\n\n\tvoid UpdateColor();\n\npublic:\n\tColorSourceToolbar(QWidget *parent, OBSSource source);\n\t~ColorSourceToolbar();\n\npublic slots:\n\tvoid on_choose_clicked();\n};\n"
  },
  {
    "path": "frontend/components/ComboSelectToolbar.cpp",
    "content": "#include \"ComboSelectToolbar.hpp\"\n#include \"ui_device-select-toolbar.h\"\n\n#include <OBSApp.hpp>\n#include <qt-wrappers.hpp>\n\n#include \"moc_ComboSelectToolbar.cpp\"\n\nComboSelectToolbar::ComboSelectToolbar(QWidget *parent, OBSSource source)\n\t: SourceToolbar(parent, source),\n\t  ui(new Ui_DeviceSelectToolbar)\n{\n\tui->setupUi(this);\n}\n\nComboSelectToolbar::~ComboSelectToolbar() {}\n\nint FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false)\n{\n\tsize_t count = obs_property_list_item_count(p);\n\tint cur_idx = -1;\n\n\tfor (size_t i = 0; i < count; i++) {\n\t\tconst char *name = obs_property_list_item_name(p, i);\n\t\tstd::string id;\n\n\t\tif (is_int) {\n\t\t\tid = std::to_string(obs_property_list_item_int(p, i));\n\t\t} else {\n\t\t\tconst char *val = obs_property_list_item_string(p, i);\n\t\t\tid = val ? val : \"\";\n\t\t}\n\n\t\tif (cur_id == id)\n\t\t\tcur_idx = (int)i;\n\n\t\tc->addItem(name, id.c_str());\n\t}\n\n\treturn cur_idx;\n}\n\nvoid UpdateSourceComboToolbarProperties(QComboBox *combo, OBSSource source, obs_properties_t *props,\n\t\t\t\t\tconst char *prop_name, bool is_int)\n{\n\tstd::string cur_id;\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\tif (is_int) {\n\t\tcur_id = std::to_string(obs_data_get_int(settings, prop_name));\n\t} else {\n\t\tcur_id = obs_data_get_string(settings, prop_name);\n\t}\n\n\tcombo->blockSignals(true);\n\n\tobs_property_t *p = obs_properties_get(props, prop_name);\n\tint cur_idx = FillPropertyCombo(combo, p, cur_id, is_int);\n\n\tif (cur_idx == -1 || obs_property_list_item_disabled(p, cur_idx)) {\n\t\tif (cur_idx == -1) {\n\t\t\tcombo->insertItem(0, QTStr(\"Basic.Settings.Audio.UnknownAudioDevice\"));\n\t\t\tcur_idx = 0;\n\t\t}\n\n\t\tSetComboItemEnabled(combo, cur_idx, false);\n\t}\n\n\tcombo->setCurrentIndex(cur_idx);\n\tcombo->blockSignals(false);\n}\n\nvoid ComboSelectToolbar::Init()\n{\n\tOBSSource source = GetSource();\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tUpdateSourceComboToolbarProperties(ui->device, source, props.get(), prop_name, is_int);\n}\n\nvoid UpdateSourceComboToolbarValue(QComboBox *combo, OBSSource source, int idx, const char *prop_name, bool is_int)\n{\n\tQString id = combo->itemData(idx).toString();\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tif (is_int) {\n\t\tobs_data_set_int(settings, prop_name, id.toInt());\n\t} else {\n\t\tobs_data_set_string(settings, prop_name, QT_TO_UTF8(id));\n\t}\n\tobs_source_update(source, settings);\n}\n\nvoid ComboSelectToolbar::on_device_currentIndexChanged(int idx)\n{\n\tOBSSource source = GetSource();\n\tif (idx == -1 || !source) {\n\t\treturn;\n\t}\n\n\tSaveOldProperties(source);\n\tUpdateSourceComboToolbarValue(ui->device, source, idx, prop_name, is_int);\n\tSetUndoProperties(source);\n}\n"
  },
  {
    "path": "frontend/components/ComboSelectToolbar.hpp",
    "content": "#pragma once\n\n#include \"SourceToolbar.hpp\"\n\nclass Ui_DeviceSelectToolbar;\n\nclass ComboSelectToolbar : public SourceToolbar {\n\tQ_OBJECT\n\nprotected:\n\tstd::unique_ptr<Ui_DeviceSelectToolbar> ui;\n\tconst char *prop_name;\n\tbool is_int = false;\n\npublic:\n\tComboSelectToolbar(QWidget *parent, OBSSource source);\n\t~ComboSelectToolbar();\n\tvirtual void Init();\n\npublic slots:\n\tvoid on_device_currentIndexChanged(int idx);\n};\n"
  },
  {
    "path": "frontend/components/DelButton.hpp",
    "content": "#pragma once\n\n#include <QPersistentModelIndex>\n#include <QPushButton>\n\nclass DelButton : public QPushButton {\n\tQ_OBJECT\n\npublic:\n\tinline DelButton(QModelIndex index_) : QPushButton(), index(index_) {}\n\n\tQPersistentModelIndex index;\n};\n"
  },
  {
    "path": "frontend/components/DeviceCaptureToolbar.cpp",
    "content": "#include \"DeviceCaptureToolbar.hpp\"\n#include \"ui_device-select-toolbar.h\"\n#include \"moc_DeviceCaptureToolbar.cpp\"\n\nDeviceCaptureToolbar::DeviceCaptureToolbar(QWidget *parent, OBSSource source)\n\t: QWidget(parent),\n\t  weakSource(OBSGetWeakRef(source)),\n\t  ui(new Ui_DeviceSelectToolbar)\n{\n\tui->setupUi(this);\n\n\tdelete ui->deviceLabel;\n\tdelete ui->device;\n\tui->deviceLabel = nullptr;\n\tui->device = nullptr;\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\tactive = obs_data_get_bool(settings, \"active\");\n\n\tobs_module_t *mod = obs_get_module(\"win-dshow\");\n\tif (!mod)\n\t\treturn;\n\n\tactivateText = obs_module_get_locale_text(mod, \"Activate\");\n\tdeactivateText = obs_module_get_locale_text(mod, \"Deactivate\");\n\n\tui->activateButton->setText(active ? deactivateText : activateText);\n}\n\nDeviceCaptureToolbar::~DeviceCaptureToolbar() {}\n\nvoid DeviceCaptureToolbar::on_activateButton_clicked()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\tbool now_active = obs_data_get_bool(settings, \"active\");\n\n\tbool desyncedSetting = now_active != active;\n\n\tactive = !active;\n\n\tconst char *text = active ? deactivateText : activateText;\n\tui->activateButton->setText(text);\n\n\tif (desyncedSetting) {\n\t\treturn;\n\t}\n\n\tcalldata_t cd = {};\n\tcalldata_set_bool(&cd, \"active\", active);\n\tproc_handler_t *ph = obs_source_get_proc_handler(source);\n\tproc_handler_call(ph, \"activate\", &cd);\n\tcalldata_free(&cd);\n}\n"
  },
  {
    "path": "frontend/components/DeviceCaptureToolbar.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QWidget>\n\nclass Ui_DeviceSelectToolbar;\n\nclass DeviceCaptureToolbar : public QWidget {\n\tQ_OBJECT\n\n\tOBSWeakSource weakSource;\n\n\tstd::unique_ptr<Ui_DeviceSelectToolbar> ui;\n\tconst char *activateText;\n\tconst char *deactivateText;\n\tbool active;\n\npublic:\n\tDeviceCaptureToolbar(QWidget *parent, OBSSource source);\n\t~DeviceCaptureToolbar();\n\npublic slots:\n\tvoid on_activateButton_clicked();\n};\n"
  },
  {
    "path": "frontend/components/DisplayCaptureToolbar.cpp",
    "content": "#include \"DisplayCaptureToolbar.hpp\"\n#include \"ui_device-select-toolbar.h\"\n#include \"moc_DisplayCaptureToolbar.cpp\"\n\n#ifdef _WIN32\n#define get_os_module(win, mac, linux) obs_get_module(win)\n#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)\n#elif __APPLE__\n#define get_os_module(win, mac, linux) obs_get_module(mac)\n#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)\n#else\n#define get_os_module(win, mac, linux) obs_get_module(linux)\n#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)\n#endif\n\nDisplayCaptureToolbar::DisplayCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}\n\nvoid DisplayCaptureToolbar::Init()\n{\n\tdelete ui->activateButton;\n\tui->activateButton = nullptr;\n\n\tobs_module_t *mod = get_os_module(\"win-capture\", \"mac-capture\", \"linux-capture\");\n\tif (!mod)\n\t\treturn;\n\n\tconst char *device_str = get_os_text(mod, \"Monitor\", \"DisplayCapture.Display\", \"Screen\");\n\tui->deviceLabel->setText(device_str);\n\n#ifdef _WIN32\n\tprop_name = \"monitor_id\";\n#elif __APPLE__\n\tprop_name = \"display_uuid\";\n#else\n\tis_int = true;\n\tprop_name = \"screen\";\n#endif\n\n\tComboSelectToolbar::Init();\n}\n"
  },
  {
    "path": "frontend/components/DisplayCaptureToolbar.hpp",
    "content": "#pragma once\n\n#include \"ComboSelectToolbar.hpp\"\n\nclass DisplayCaptureToolbar : public ComboSelectToolbar {\n\tQ_OBJECT\n\npublic:\n\tDisplayCaptureToolbar(QWidget *parent, OBSSource source);\n\tvoid Init() override;\n};\n"
  },
  {
    "path": "frontend/components/EditWidget.hpp",
    "content": "#pragma once\n\n#include <QLineEdit>\n#include <QModelIndex>\n\nclass QPersistentModelIndex;\n\nclass EditWidget : public QLineEdit {\n\tQ_OBJECT\n\npublic:\n\tinline EditWidget(QWidget *parent, QModelIndex index_) : QLineEdit(parent), index(index_) {}\n\n\tQPersistentModelIndex index;\n};\n"
  },
  {
    "path": "frontend/components/FocusList.cpp",
    "content": "#include \"FocusList.hpp\"\n\n#include <QDragMoveEvent>\n\n#include \"moc_FocusList.cpp\"\n\nFocusList::FocusList(QWidget *parent) : QListWidget(parent) {}\n\nvoid FocusList::focusInEvent(QFocusEvent *event)\n{\n\tQListWidget::focusInEvent(event);\n\n\temit GotFocus();\n}\n\nvoid FocusList::dragMoveEvent(QDragMoveEvent *event)\n{\n\tQPoint pos = event->position().toPoint();\n\tint itemRow = row(itemAt(pos));\n\n\tif ((itemRow == currentRow() + 1) || (currentRow() == count() - 1 && itemRow == -1))\n\t\tevent->ignore();\n\telse\n\t\tQListWidget::dragMoveEvent(event);\n}\n"
  },
  {
    "path": "frontend/components/FocusList.hpp",
    "content": "#pragma once\n\n#include <QListWidget>\n\nclass FocusList : public QListWidget {\n\tQ_OBJECT\n\npublic:\n\tFocusList(QWidget *parent);\n\nprotected:\n\tvoid focusInEvent(QFocusEvent *event) override;\n\tvirtual void dragMoveEvent(QDragMoveEvent *event) override;\n\nsignals:\n\tvoid GotFocus();\n};\n"
  },
  {
    "path": "frontend/components/GameCaptureToolbar.cpp",
    "content": "#include \"GameCaptureToolbar.hpp\"\n#include \"ui_game-capture-toolbar.h\"\n\n#include <qt-wrappers.hpp>\n\n#include \"moc_GameCaptureToolbar.cpp\"\n\nextern int FillPropertyCombo(QComboBox *c, obs_property_t *p, const std::string &cur_id, bool is_int = false);\n\nGameCaptureToolbar::GameCaptureToolbar(QWidget *parent, OBSSource source)\n\t: SourceToolbar(parent, source),\n\t  ui(new Ui_GameCaptureToolbar)\n{\n\tobs_property_t *p;\n\tint cur_idx;\n\n\tui->setupUi(this);\n\n\tobs_module_t *mod = obs_get_module(\"win-capture\");\n\tif (!mod)\n\t\treturn;\n\n\tui->modeLabel->setText(obs_module_get_locale_text(mod, \"Mode\"));\n\tui->windowLabel->setText(obs_module_get_locale_text(mod, \"WindowCapture.Window\"));\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\tstd::string cur_mode = obs_data_get_string(settings, \"capture_mode\");\n\tstd::string cur_window = obs_data_get_string(settings, \"window\");\n\n\tui->mode->blockSignals(true);\n\tp = obs_properties_get(props.get(), \"capture_mode\");\n\tcur_idx = FillPropertyCombo(ui->mode, p, cur_mode);\n\tui->mode->setCurrentIndex(cur_idx);\n\tui->mode->blockSignals(false);\n\n\tui->window->blockSignals(true);\n\tp = obs_properties_get(props.get(), \"window\");\n\tcur_idx = FillPropertyCombo(ui->window, p, cur_window);\n\tui->window->setCurrentIndex(cur_idx);\n\tui->window->blockSignals(false);\n\n\tif (cur_idx != -1 && obs_property_list_item_disabled(p, cur_idx)) {\n\t\tSetComboItemEnabled(ui->window, cur_idx, false);\n\t}\n\n\tUpdateWindowVisibility();\n}\n\nGameCaptureToolbar::~GameCaptureToolbar() {}\n\nvoid GameCaptureToolbar::UpdateWindowVisibility()\n{\n\tQString mode = ui->mode->currentData().toString();\n\tbool is_window = (mode == \"window\");\n\tui->windowLabel->setVisible(is_window);\n\tui->window->setVisible(is_window);\n}\n\nvoid GameCaptureToolbar::on_mode_currentIndexChanged(int idx)\n{\n\tOBSSource source = GetSource();\n\tif (idx == -1 || !source) {\n\t\treturn;\n\t}\n\n\tQString id = ui->mode->itemData(idx).toString();\n\n\tSaveOldProperties(source);\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"capture_mode\", QT_TO_UTF8(id));\n\tobs_source_update(source, settings);\n\tSetUndoProperties(source);\n\n\tUpdateWindowVisibility();\n}\n\nvoid GameCaptureToolbar::on_window_currentIndexChanged(int idx)\n{\n\tOBSSource source = GetSource();\n\tif (idx == -1 || !source) {\n\t\treturn;\n\t}\n\n\tQString id = ui->window->itemData(idx).toString();\n\n\tSaveOldProperties(source);\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"window\", QT_TO_UTF8(id));\n\tobs_source_update(source, settings);\n\tSetUndoProperties(source);\n}\n"
  },
  {
    "path": "frontend/components/GameCaptureToolbar.hpp",
    "content": "#pragma once\n\n#include \"SourceToolbar.hpp\"\n\nclass Ui_GameCaptureToolbar;\n\nclass GameCaptureToolbar : public SourceToolbar {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui_GameCaptureToolbar> ui;\n\n\tvoid UpdateWindowVisibility();\n\npublic:\n\tGameCaptureToolbar(QWidget *parent, OBSSource source);\n\t~GameCaptureToolbar();\n\npublic slots:\n\tvoid on_mode_currentIndexChanged(int idx);\n\tvoid on_window_currentIndexChanged(int idx);\n};\n"
  },
  {
    "path": "frontend/components/ImageSourceToolbar.cpp",
    "content": "#include \"ImageSourceToolbar.hpp\"\n#include \"ui_image-source-toolbar.h\"\n\n#include <qt-wrappers.hpp>\n\n#include \"moc_ImageSourceToolbar.cpp\"\n\nImageSourceToolbar::ImageSourceToolbar(QWidget *parent, OBSSource source)\n\t: SourceToolbar(parent, source),\n\t  ui(new Ui_ImageSourceToolbar)\n{\n\tui->setupUi(this);\n\n\tobs_module_t *mod = obs_get_module(\"image-source\");\n\tui->pathLabel->setText(obs_module_get_locale_text(mod, \"File\"));\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\tstd::string file = obs_data_get_string(settings, \"file\");\n\n\tui->path->setText(file.c_str());\n}\n\nImageSourceToolbar::~ImageSourceToolbar() {}\n\nvoid ImageSourceToolbar::on_browse_clicked()\n{\n\tOBSSource source = GetSource();\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tobs_property_t *p = obs_properties_get(props.get(), \"file\");\n\tconst char *desc = obs_property_description(p);\n\tconst char *filter = obs_property_path_filter(p);\n\tconst char *default_path = obs_property_path_default_path(p);\n\n\tQString startDir = ui->path->text();\n\tif (startDir.isEmpty())\n\t\tstartDir = default_path;\n\n\tQString path = OpenFile(this, desc, startDir, filter);\n\tif (path.isEmpty()) {\n\t\treturn;\n\t}\n\n\tui->path->setText(path);\n\n\tSaveOldProperties(source);\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"file\", QT_TO_UTF8(path));\n\tobs_source_update(source, settings);\n\tSetUndoProperties(source);\n}\n"
  },
  {
    "path": "frontend/components/ImageSourceToolbar.hpp",
    "content": "#pragma once\n\n#include \"SourceToolbar.hpp\"\n\nclass Ui_ImageSourceToolbar;\n\nclass ImageSourceToolbar : public SourceToolbar {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui_ImageSourceToolbar> ui;\n\npublic:\n\tImageSourceToolbar(QWidget *parent, OBSSource source);\n\t~ImageSourceToolbar();\n\npublic slots:\n\tvoid on_browse_clicked();\n};\n"
  },
  {
    "path": "frontend/components/MediaControls.cpp",
    "content": "#include \"MediaControls.hpp\"\n#include \"ui_media-controls.h\"\n\n#include <OBSApp.hpp>\n\n#include <QToolTip>\n\n#include \"moc_MediaControls.cpp\"\n\nvoid MediaControls::OBSMediaStopped(void *data, calldata_t *)\n{\n\tMediaControls *media = static_cast<MediaControls *>(data);\n\tQMetaObject::invokeMethod(media, \"SetRestartState\");\n}\n\nvoid MediaControls::OBSMediaPlay(void *data, calldata_t *)\n{\n\tMediaControls *media = static_cast<MediaControls *>(data);\n\tQMetaObject::invokeMethod(media, \"SetPlayingState\");\n}\n\nvoid MediaControls::OBSMediaPause(void *data, calldata_t *)\n{\n\tMediaControls *media = static_cast<MediaControls *>(data);\n\tQMetaObject::invokeMethod(media, \"SetPausedState\");\n}\n\nvoid MediaControls::OBSMediaStarted(void *data, calldata_t *)\n{\n\tMediaControls *media = static_cast<MediaControls *>(data);\n\tQMetaObject::invokeMethod(media, \"SetPlayingState\");\n}\n\nvoid MediaControls::OBSMediaNext(void *data, calldata_t *)\n{\n\tMediaControls *media = static_cast<MediaControls *>(data);\n\tQMetaObject::invokeMethod(media, \"UpdateSlideCounter\");\n}\n\nvoid MediaControls::OBSMediaPrevious(void *data, calldata_t *)\n{\n\tMediaControls *media = static_cast<MediaControls *>(data);\n\tQMetaObject::invokeMethod(media, \"UpdateSlideCounter\");\n}\n\nMediaControls::MediaControls(QWidget *parent) : QWidget(parent), ui(new Ui::MediaControls)\n{\n\tui->setupUi(this);\n\tsetFocusPolicy(Qt::StrongFocus);\n\n\tconnect(&mediaTimer, &QTimer::timeout, this, &MediaControls::SetSliderPosition);\n\tconnect(&seekTimer, &QTimer::timeout, this, &MediaControls::SeekTimerCallback);\n\tconnect(ui->slider, &AbsoluteSlider::sliderPressed, this, &MediaControls::AbsoluteSliderClicked);\n\tconnect(ui->slider, &AbsoluteSlider::absoluteSliderHovered, this, &MediaControls::AbsoluteSliderHovered);\n\tconnect(ui->slider, &AbsoluteSlider::sliderReleased, this, &MediaControls::AbsoluteSliderReleased);\n\tconnect(ui->slider, &AbsoluteSlider::sliderMoved, this, &MediaControls::AbsoluteSliderMoved);\n\n\tcountDownTimer = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MediaControlsCountdownTimer\");\n\n\tQAction *restartAction = new QAction(this);\n\trestartAction->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n\trestartAction->setShortcut({Qt::Key_R});\n\tconnect(restartAction, &QAction::triggered, this, &MediaControls::RestartMedia);\n\taddAction(restartAction);\n\n\tQAction *sliderFoward = new QAction(this);\n\tsliderFoward->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n\tconnect(sliderFoward, &QAction::triggered, this, &MediaControls::MoveSliderFoward);\n\tsliderFoward->setShortcut({Qt::Key_Right});\n\taddAction(sliderFoward);\n\n\tQAction *sliderBack = new QAction(this);\n\tsliderBack->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n\tconnect(sliderBack, &QAction::triggered, this, &MediaControls::MoveSliderBackwards);\n\tsliderBack->setShortcut({Qt::Key_Left});\n\taddAction(sliderBack);\n\n\tQAction *playPause = new QAction(this);\n\tplayPause->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n\tconnect(playPause, &QAction::triggered, this, &MediaControls::on_playPauseButton_clicked);\n\tplayPause->setShortcut({Qt::Key_Space});\n\taddAction(playPause);\n}\n\nMediaControls::~MediaControls() {}\n\nbool MediaControls::MediaPaused()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (!source) {\n\t\treturn false;\n\t}\n\n\tobs_media_state state = obs_source_media_get_state(source);\n\treturn state == OBS_MEDIA_STATE_PAUSED;\n}\n\nint64_t MediaControls::GetSliderTime(int val)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (!source) {\n\t\treturn 0;\n\t}\n\n\tfloat percent = (float)val / (float)ui->slider->maximum();\n\tfloat duration = (float)obs_source_media_get_duration(source);\n\tint64_t seekTo = (int64_t)(percent * duration);\n\n\treturn seekTo;\n}\n\nvoid MediaControls::AbsoluteSliderClicked()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tobs_media_state state = obs_source_media_get_state(source);\n\n\tif (state == OBS_MEDIA_STATE_PAUSED) {\n\t\tprevPaused = true;\n\t} else if (state == OBS_MEDIA_STATE_PLAYING) {\n\t\tprevPaused = false;\n\t\tPauseMedia();\n\t\tStopMediaTimer();\n\t}\n\n\tseek = ui->slider->value();\n\tseekTimer.start(100);\n}\n\nvoid MediaControls::AbsoluteSliderReleased()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tif (seekTimer.isActive()) {\n\t\tseekTimer.stop();\n\t\tif (lastSeek != seek) {\n\t\t\tobs_source_media_set_time(source, GetSliderTime(seek));\n\t\t}\n\n\t\tUpdateLabels(seek);\n\t\tseek = lastSeek = -1;\n\t}\n\n\tif (!prevPaused) {\n\t\tPlayMedia();\n\t\tStartMediaTimer();\n\t}\n}\n\nvoid MediaControls::AbsoluteSliderHovered(int val)\n{\n\tfloat seconds = ((float)GetSliderTime(val) / 1000.0f);\n\tQToolTip::showText(QCursor::pos(), FormatSeconds((int)seconds), this);\n}\n\nvoid MediaControls::AbsoluteSliderMoved(int val)\n{\n\tif (seekTimer.isActive()) {\n\t\tseek = val;\n\t\tUpdateLabels(seek);\n\t}\n}\n\nvoid MediaControls::SeekTimerCallback()\n{\n\tif (lastSeek != seek) {\n\t\tOBSSource source = OBSGetStrongRef(weakSource);\n\t\tif (source) {\n\t\t\tobs_source_media_set_time(source, GetSliderTime(seek));\n\t\t}\n\t\tlastSeek = seek;\n\t}\n}\n\nvoid MediaControls::StartMediaTimer()\n{\n\tif (isSlideshow)\n\t\treturn;\n\n\tif (!mediaTimer.isActive())\n\t\tmediaTimer.start(16);\n}\n\nvoid MediaControls::StopMediaTimer()\n{\n\tif (mediaTimer.isActive())\n\t\tmediaTimer.stop();\n}\n\nvoid MediaControls::SetPlayingState()\n{\n\tui->slider->setEnabled(true);\n\tui->playPauseButton->setProperty(\"class\", \"icon-media-pause\");\n\tui->playPauseButton->style()->unpolish(ui->playPauseButton);\n\tui->playPauseButton->style()->polish(ui->playPauseButton);\n\tui->playPauseButton->setToolTip(QTStr(\"ContextBar.MediaControls.PauseMedia\"));\n\n\tprevPaused = false;\n\n\tUpdateSlideCounter();\n\tStartMediaTimer();\n}\n\nvoid MediaControls::SetPausedState()\n{\n\tui->playPauseButton->setProperty(\"class\", \"icon-media-play\");\n\tui->playPauseButton->style()->unpolish(ui->playPauseButton);\n\tui->playPauseButton->style()->polish(ui->playPauseButton);\n\tui->playPauseButton->setToolTip(QTStr(\"ContextBar.MediaControls.PlayMedia\"));\n\n\tStopMediaTimer();\n}\n\nvoid MediaControls::SetRestartState()\n{\n\tui->playPauseButton->setProperty(\"class\", \"icon-media-restart\");\n\tui->playPauseButton->style()->unpolish(ui->playPauseButton);\n\tui->playPauseButton->style()->polish(ui->playPauseButton);\n\tui->playPauseButton->setToolTip(QTStr(\"ContextBar.MediaControls.RestartMedia\"));\n\n\tui->slider->setValue(0);\n\n\tif (!isSlideshow) {\n\t\tui->timerLabel->setText(\"--:--:--\");\n\t\tui->durationLabel->setText(\"--:--:--\");\n\t} else {\n\t\tui->timerLabel->setText(\"-\");\n\t\tui->durationLabel->setText(\"-\");\n\t}\n\n\tui->slider->setEnabled(false);\n\n\tStopMediaTimer();\n}\n\nvoid MediaControls::RefreshControls()\n{\n\tOBSSource source;\n\tsource = OBSGetStrongRef(weakSource);\n\n\tuint32_t flags = 0;\n\tconst char *id = nullptr;\n\n\tif (source) {\n\t\tflags = obs_source_get_output_flags(source);\n\t\tid = obs_source_get_unversioned_id(source);\n\t}\n\n\tif (!source || !(flags & OBS_SOURCE_CONTROLLABLE_MEDIA)) {\n\t\tSetRestartState();\n\t\tsetEnabled(false);\n\t\thide();\n\t\treturn;\n\t} else {\n\t\tsetEnabled(true);\n\t\tshow();\n\t}\n\n\tbool has_playlist = strcmp(id, \"ffmpeg_source\") != 0;\n\tui->previousButton->setVisible(has_playlist);\n\tui->nextButton->setVisible(has_playlist);\n\n\tisSlideshow = strcmp(id, \"slideshow\") == 0;\n\tui->slider->setVisible(!isSlideshow);\n\tui->emptySpaceAgain->setVisible(isSlideshow);\n\n\tobs_media_state state = obs_source_media_get_state(source);\n\n\tswitch (state) {\n\tcase OBS_MEDIA_STATE_STOPPED:\n\tcase OBS_MEDIA_STATE_ENDED:\n\tcase OBS_MEDIA_STATE_NONE:\n\t\tSetRestartState();\n\t\tbreak;\n\tcase OBS_MEDIA_STATE_PLAYING:\n\t\tSetPlayingState();\n\t\tbreak;\n\tcase OBS_MEDIA_STATE_PAUSED:\n\t\tSetPausedState();\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tif (isSlideshow)\n\t\tUpdateSlideCounter();\n\telse\n\t\tSetSliderPosition();\n}\n\nOBSSource MediaControls::GetSource()\n{\n\treturn OBSGetStrongRef(weakSource);\n}\n\nvoid MediaControls::SetSource(OBSSource source)\n{\n\tsigs.clear();\n\n\tif (source) {\n\t\tweakSource = OBSGetWeakRef(source);\n\t\tsignal_handler_t *sh = obs_source_get_signal_handler(source);\n\t\tsigs.emplace_back(sh, \"media_play\", OBSMediaPlay, this);\n\t\tsigs.emplace_back(sh, \"media_pause\", OBSMediaPause, this);\n\t\tsigs.emplace_back(sh, \"media_restart\", OBSMediaPlay, this);\n\t\tsigs.emplace_back(sh, \"media_stopped\", OBSMediaStopped, this);\n\t\tsigs.emplace_back(sh, \"media_started\", OBSMediaStarted, this);\n\t\tsigs.emplace_back(sh, \"media_ended\", OBSMediaStopped, this);\n\t\tsigs.emplace_back(sh, \"media_next\", OBSMediaNext, this);\n\t\tsigs.emplace_back(sh, \"media_previous\", OBSMediaPrevious, this);\n\t} else {\n\t\tweakSource = nullptr;\n\t}\n\n\tRefreshControls();\n}\n\nvoid MediaControls::SetSliderPosition()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tfloat time = (float)obs_source_media_get_time(source);\n\tfloat duration = (float)obs_source_media_get_duration(source);\n\n\tfloat sliderPosition;\n\n\tif (duration)\n\t\tsliderPosition = (time / duration) * (float)ui->slider->maximum();\n\telse\n\t\tsliderPosition = 0.0f;\n\n\tui->slider->setValue((int)sliderPosition);\n\tUpdateLabels((int)sliderPosition);\n}\n\nQString MediaControls::FormatSeconds(int totalSeconds)\n{\n\tint seconds = totalSeconds % 60;\n\tint totalMinutes = totalSeconds / 60;\n\tint minutes = totalMinutes % 60;\n\tint hours = totalMinutes / 60;\n\n\treturn QString::asprintf(\"%02d:%02d:%02d\", hours, minutes, seconds);\n}\n\nvoid MediaControls::on_playPauseButton_clicked()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tobs_media_state state = obs_source_media_get_state(source);\n\n\tswitch (state) {\n\tcase OBS_MEDIA_STATE_STOPPED:\n\tcase OBS_MEDIA_STATE_ENDED:\n\t\tRestartMedia();\n\t\tbreak;\n\tcase OBS_MEDIA_STATE_PLAYING:\n\t\tPauseMedia();\n\t\tbreak;\n\tcase OBS_MEDIA_STATE_PAUSED:\n\t\tPlayMedia();\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid MediaControls::RestartMedia()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (source) {\n\t\tobs_source_media_restart(source);\n\t}\n}\n\nvoid MediaControls::PlayMedia()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (source) {\n\t\tobs_source_media_play_pause(source, false);\n\t}\n}\n\nvoid MediaControls::PauseMedia()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (source) {\n\t\tobs_source_media_play_pause(source, true);\n\t}\n}\n\nvoid MediaControls::StopMedia()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (source) {\n\t\tobs_source_media_stop(source);\n\t}\n}\n\nvoid MediaControls::PlaylistNext()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (source) {\n\t\tobs_source_media_next(source);\n\t}\n}\n\nvoid MediaControls::PlaylistPrevious()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (source) {\n\t\tobs_source_media_previous(source);\n\t}\n}\n\nvoid MediaControls::on_stopButton_clicked()\n{\n\tStopMedia();\n}\n\nvoid MediaControls::on_nextButton_clicked()\n{\n\tPlaylistNext();\n}\n\nvoid MediaControls::on_previousButton_clicked()\n{\n\tPlaylistPrevious();\n}\n\nvoid MediaControls::on_durationLabel_clicked()\n{\n\tcountDownTimer = !countDownTimer;\n\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"MediaControlsCountdownTimer\", countDownTimer);\n\n\tif (MediaPaused())\n\t\tSetSliderPosition();\n}\n\nvoid MediaControls::MoveSliderFoward(int seconds)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\n\tif (!source)\n\t\treturn;\n\n\tint ms = obs_source_media_get_time(source);\n\tms += seconds * 1000;\n\n\tobs_source_media_set_time(source, ms);\n\tSetSliderPosition();\n}\n\nvoid MediaControls::MoveSliderBackwards(int seconds)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\n\tif (!source)\n\t\treturn;\n\n\tint ms = obs_source_media_get_time(source);\n\tms -= seconds * 1000;\n\n\tobs_source_media_set_time(source, ms);\n\tSetSliderPosition();\n}\n\nvoid MediaControls::UpdateSlideCounter()\n{\n\tif (!isSlideshow)\n\t\treturn;\n\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\n\tif (!source)\n\t\treturn;\n\n\tproc_handler_t *ph = obs_source_get_proc_handler(source);\n\tcalldata_t cd = {};\n\n\tproc_handler_call(ph, \"current_index\", &cd);\n\tint slide = calldata_int(&cd, \"current_index\");\n\n\tproc_handler_call(ph, \"total_files\", &cd);\n\tint total = calldata_int(&cd, \"total_files\");\n\tcalldata_free(&cd);\n\n\tif (total > 0) {\n\t\tui->timerLabel->setText(QString::number(slide + 1));\n\t\tui->durationLabel->setText(QString::number(total));\n\t} else {\n\t\tui->timerLabel->setText(\"-\");\n\t\tui->durationLabel->setText(\"-\");\n\t}\n}\n\nvoid MediaControls::UpdateLabels(int val)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tfloat duration = (float)obs_source_media_get_duration(source);\n\tfloat percent = (float)val / (float)ui->slider->maximum();\n\n\tfloat time = percent * duration;\n\n\tui->timerLabel->setText(FormatSeconds((int)(time / 1000.0f)));\n\n\tif (!countDownTimer)\n\t\tui->durationLabel->setText(FormatSeconds((int)(duration / 1000.0f)));\n\telse\n\t\tui->durationLabel->setText(QString(\"-\") + FormatSeconds((int)((duration - time) / 1000.0f)));\n}\n"
  },
  {
    "path": "frontend/components/MediaControls.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QTimer>\n#include <QWidget>\n\nclass Ui_MediaControls;\n\nclass MediaControls : public QWidget {\n\tQ_OBJECT\n\nprivate:\n\tstd::vector<OBSSignal> sigs;\n\tOBSWeakSource weakSource = nullptr;\n\tQTimer mediaTimer;\n\tQTimer seekTimer;\n\tint seek;\n\tint lastSeek;\n\tbool prevPaused = false;\n\tbool countDownTimer = false;\n\tbool isSlideshow = false;\n\n\tQString FormatSeconds(int totalSeconds);\n\tvoid StartMediaTimer();\n\tvoid StopMediaTimer();\n\tvoid RefreshControls();\n\tvoid SetScene(OBSScene scene);\n\tint64_t GetSliderTime(int val);\n\n\tstatic void OBSMediaStopped(void *data, calldata_t *calldata);\n\tstatic void OBSMediaPlay(void *data, calldata_t *calldata);\n\tstatic void OBSMediaPause(void *data, calldata_t *calldata);\n\tstatic void OBSMediaStarted(void *data, calldata_t *calldata);\n\tstatic void OBSMediaNext(void *data, calldata_t *calldata);\n\tstatic void OBSMediaPrevious(void *data, calldata_t *calldata);\n\n\tstd::unique_ptr<Ui_MediaControls> ui;\n\nprivate slots:\n\tvoid on_playPauseButton_clicked();\n\tvoid on_stopButton_clicked();\n\tvoid on_nextButton_clicked();\n\tvoid on_previousButton_clicked();\n\tvoid on_durationLabel_clicked();\n\n\tvoid AbsoluteSliderClicked();\n\tvoid AbsoluteSliderReleased();\n\tvoid AbsoluteSliderHovered(int val);\n\tvoid AbsoluteSliderMoved(int val);\n\tvoid SetSliderPosition();\n\tvoid SetPlayingState();\n\tvoid SetPausedState();\n\tvoid SetRestartState();\n\tvoid RestartMedia();\n\tvoid StopMedia();\n\tvoid PlaylistNext();\n\tvoid PlaylistPrevious();\n\n\tvoid SeekTimerCallback();\n\n\tvoid MoveSliderFoward(int seconds = 5);\n\tvoid MoveSliderBackwards(int seconds = 5);\n\n\tvoid UpdateSlideCounter();\n\tvoid UpdateLabels(int val);\n\npublic slots:\n\tvoid PlayMedia();\n\tvoid PauseMedia();\n\npublic:\n\tMediaControls(QWidget *parent = nullptr);\n\t~MediaControls();\n\n\tOBSSource GetSource();\n\tvoid SetSource(OBSSource newSource);\n\tbool MediaPaused();\n};\n"
  },
  {
    "path": "frontend/components/MenuButton.cpp",
    "content": "#include \"MenuButton.hpp\"\n\n#include <QKeyEvent>\n#include <QMouseEvent>\n\n#include \"moc_MenuButton.cpp\"\n\nvoid MenuButton::keyPressEvent(QKeyEvent *event)\n{\n\tif (menu()) {\n\t\tswitch (event->key()) {\n\t\tcase Qt::Key_Enter:\n\t\tcase Qt::Key_Return:\n\t\t\temit clicked();\n\t\t\treturn;\n\t\tcase Qt::Key_Down:\n\t\tcase Qt::Key_Space:\n\t\t\tshowMenu();\n\t\t\treturn;\n\t\t}\n\t}\n\n\tQPushButton::keyPressEvent(event);\n}\n\nvoid MenuButton::mousePressEvent(QMouseEvent *event)\n{\n\tif (menu()) {\n\t\tif (width() - event->pos().x() <= 30)\n\t\t\tshowMenu();\n\t\telse\n\t\t\tsetDown(true);\n\t} else {\n\t\tQPushButton::mousePressEvent(event);\n\t}\n}\n"
  },
  {
    "path": "frontend/components/MenuButton.hpp",
    "content": "#pragma once\n\n#include <QPushButton>\n\nclass MenuButton : public QPushButton {\n\tQ_OBJECT\n\nprotected:\n\tvoid mousePressEvent(QMouseEvent *event) override;\n\tvoid keyPressEvent(QKeyEvent *event) override;\n\npublic:\n\texplicit inline MenuButton(QWidget *parent = nullptr) : QPushButton(parent) {}\n\n\texplicit inline MenuButton(const QString &text, QWidget *parent = nullptr) : QPushButton(text, parent) {}\n};\n"
  },
  {
    "path": "frontend/components/MenuCheckBox.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"MenuCheckBox.hpp\"\n\n#include <QMenu>\n#include <QMouseEvent>\n#include <QStyleOptionButton>\n#include <QStylePainter>\n\nMenuCheckBox::MenuCheckBox(const QString &text, QWidget *parent) : QCheckBox(text, parent)\n{\n\tsetSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);\n\tsetContentsMargins(0, 0, 0, 0);\n\n\tif (auto menu = qobject_cast<QMenu *>(parent)) {\n\t\tconnect(menu, &QMenu::hovered, this, [this, menu](QAction *action) {\n\t\t\tif (action != menuAction) {\n\t\t\t\tsetHovered(false);\n\t\t\t\tupdate();\n\t\t\t}\n\t\t});\n\t\tconnect(menu, &QMenu::aboutToHide, this, [this]() { setHovered(false); });\n\t}\n}\n\nvoid MenuCheckBox::setAction(QAction *action)\n{\n\tmenuAction = action;\n}\n\nvoid MenuCheckBox::focusInEvent(QFocusEvent *)\n{\n\tsetHovered(true);\n}\n\nvoid MenuCheckBox::mousePressEvent(QMouseEvent *event)\n{\n\tif (event->button() == Qt::LeftButton) {\n\t\tmousePressInside = true;\n\t\tevent->accept();\n\t} else {\n\t\tQCheckBox::mousePressEvent(event);\n\t}\n}\n\nvoid MenuCheckBox::mouseMoveEvent(QMouseEvent *event)\n{\n\tif (!rect().contains(event->pos())) {\n\t\tmousePressInside = false;\n\t}\n\tevent->accept();\n}\n\nvoid MenuCheckBox::mouseReleaseEvent(QMouseEvent *event)\n{\n\tif (event->button() == Qt::LeftButton) {\n\t\tif (mousePressInside && rect().contains(event->pos())) {\n\t\t\ttoggle();\n\t\t}\n\t\tevent->accept();\n\t} else {\n\t\tQCheckBox::mouseReleaseEvent(event);\n\t}\n\n\tmousePressInside = false;\n}\n\nvoid MenuCheckBox::enterEvent(QEnterEvent *)\n{\n\tsetHovered(true);\n\tupdate();\n}\n\nvoid MenuCheckBox::leaveEvent(QEvent *)\n{\n\tsetHovered(false);\n\tupdate();\n}\n\nvoid MenuCheckBox::paintEvent(QPaintEvent *)\n{\n\tQStylePainter p(this);\n\tQStyleOptionButton opt;\n\tinitStyleOption(&opt);\n\n\tif (isHovered) {\n\t\topt.state |= QStyle::State_MouseOver;\n\t\topt.state |= QStyle::State_Selected;\n\t}\n\n\tp.drawControl(QStyle::CE_CheckBox, opt);\n}\n\nvoid MenuCheckBox::setHovered(bool hovered)\n{\n\tisHovered = hovered;\n}\n"
  },
  {
    "path": "frontend/components/MenuCheckBox.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QCheckBox>\n#include <QPointer>\n\nclass QStyleOptionButton;\nclass QStylePainter;\nclass QMouseEvent;\n\nclass MenuCheckBox : public QCheckBox {\n\tQ_OBJECT\n\npublic:\n\texplicit MenuCheckBox(const QString &text, QWidget *parent = nullptr);\n\n\tvoid setAction(QAction *action);\n\nprotected:\n\tvoid focusInEvent(QFocusEvent *event) override;\n\tvoid mousePressEvent(QMouseEvent *event) override;\n\tvoid mouseMoveEvent(QMouseEvent *event) override;\n\tvoid mouseReleaseEvent(QMouseEvent *event) override;\n\tvoid enterEvent(QEnterEvent *event) override;\n\tvoid leaveEvent(QEvent *event) override;\n\tvoid paintEvent(QPaintEvent *event) override;\n\nprivate:\n\tQPointer<QAction> menuAction = nullptr;\n\tbool mousePressInside = false;\n\n\tbool isHovered = false;\n\tvoid setHovered(bool hovered);\n};\n"
  },
  {
    "path": "frontend/components/Multiview.cpp",
    "content": "#include \"Multiview.hpp\"\n\n#include <utility/display-helpers.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <obs-frontend-api.h>\n\nMultiview::Multiview()\n{\n\tInitSafeAreas(&actionSafeMargin, &graphicsSafeMargin, &fourByThreeSafeMargin, &leftLine, &topLine, &rightLine);\n}\n\nMultiview::~Multiview()\n{\n\tfor (OBSWeakSource &weakSrc : multiviewScenes) {\n\t\tOBSSource src = OBSGetStrongRef(weakSrc);\n\t\tif (src)\n\t\t\tobs_source_dec_showing(src);\n\t}\n\n\tobs_enter_graphics();\n\tgs_vertexbuffer_destroy(actionSafeMargin);\n\tgs_vertexbuffer_destroy(graphicsSafeMargin);\n\tgs_vertexbuffer_destroy(fourByThreeSafeMargin);\n\tgs_vertexbuffer_destroy(leftLine);\n\tgs_vertexbuffer_destroy(topLine);\n\tgs_vertexbuffer_destroy(rightLine);\n\tobs_leave_graphics();\n}\n\nstatic OBSSource CreateLabel(const char *name, size_t h)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tOBSDataAutoRelease font = obs_data_create();\n\n\tstd::string text;\n\ttext += \" \";\n\ttext += name;\n\ttext += \" \";\n\n#if defined(_WIN32)\n\tobs_data_set_string(font, \"face\", \"Arial\");\n#elif defined(__APPLE__)\n\tobs_data_set_string(font, \"face\", \"Helvetica\");\n#else\n\tobs_data_set_string(font, \"face\", \"Monospace\");\n#endif\n\tobs_data_set_int(font, \"flags\", 1); // Bold text\n\tobs_data_set_int(font, \"size\", int(h / 9.81));\n\n\tobs_data_set_obj(settings, \"font\", font);\n\tobs_data_set_string(settings, \"text\", text.c_str());\n\tobs_data_set_bool(settings, \"outline\", false);\n\tobs_data_set_int(settings, \"opacity\", 50);\n\n#ifdef _WIN32\n\tconst char *text_source_id = \"text_gdiplus\";\n#else\n\tconst char *text_source_id = \"text_ft2_source\";\n#endif\n\n\tOBSSourceAutoRelease txtSource = obs_source_create_private(text_source_id, name, settings);\n\n\treturn txtSource.Get();\n}\n\nvoid Multiview::Update(MultiviewLayout multiviewLayout, bool drawLabel, bool drawSafeArea)\n{\n\tthis->multiviewLayout = multiviewLayout;\n\tthis->drawLabel = drawLabel;\n\tthis->drawSafeArea = drawSafeArea;\n\n\tstruct obs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tuint32_t w = ovi.base_width;\n\tuint32_t h = ovi.base_height;\n\tfw = float(w);\n\tfh = float(h);\n\tratio = fw / fh;\n\n\tstruct obs_frontend_source_list scenes = {};\n\tobs_frontend_get_scenes(&scenes);\n\n\tswitch (multiviewLayout) {\n\tcase MultiviewLayout::HORIZONTAL_TOP_18_SCENES:\n\t\tpvwprgCX = fw / 2;\n\t\tpvwprgCY = fh / 2;\n\n\t\tmaxSrcs = 18;\n\t\tbreak;\n\tcase MultiviewLayout::HORIZONTAL_TOP_24_SCENES:\n\t\tpvwprgCX = fw / 3;\n\t\tpvwprgCY = fh / 3;\n\n\t\tmaxSrcs = 24;\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_4_SCENES:\n\t\tpvwprgCX = fw / 2;\n\t\tpvwprgCY = fh / 2;\n\t\tmaxSrcs = 4;\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_9_SCENES:\n\t\tpvwprgCX = fw / 3;\n\t\tpvwprgCY = fh / 3;\n\t\tmaxSrcs = 9;\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_16_SCENES:\n\t\tpvwprgCX = fw / 4;\n\t\tpvwprgCY = fh / 4;\n\t\tmaxSrcs = 16;\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_25_SCENES:\n\t\tpvwprgCX = fw / 5;\n\t\tpvwprgCY = fh / 5;\n\t\tmaxSrcs = 25;\n\t\tbreak;\n\tdefault:\n\t\tpvwprgCX = fw / 2;\n\t\tpvwprgCY = fh / 2;\n\n\t\tmaxSrcs = 8;\n\t}\n\n\tppiCX = pvwprgCX - thicknessx2;\n\tppiCY = pvwprgCY - thicknessx2;\n\tppiScaleX = (pvwprgCX - thicknessx2) / fw;\n\tppiScaleY = (pvwprgCY - thicknessx2) / fh;\n\n\tswitch (multiviewLayout) {\n\tcase MultiviewLayout::HORIZONTAL_TOP_18_SCENES:\n\t\tscenesCX = pvwprgCX / 3;\n\t\tscenesCY = pvwprgCY / 3;\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_4_SCENES:\n\tcase MultiviewLayout::SCENES_ONLY_9_SCENES:\n\tcase MultiviewLayout::SCENES_ONLY_16_SCENES:\n\tcase MultiviewLayout::SCENES_ONLY_25_SCENES:\n\t\tscenesCX = pvwprgCX;\n\t\tscenesCY = pvwprgCY;\n\t\tbreak;\n\tdefault:\n\t\tscenesCX = pvwprgCX / 2;\n\t\tscenesCY = pvwprgCY / 2;\n\t}\n\n\tsiCX = scenesCX - thicknessx2;\n\tsiCY = scenesCY - thicknessx2;\n\tsiScaleX = (scenesCX - thicknessx2) / fw;\n\tsiScaleY = (scenesCY - thicknessx2) / fh;\n\n\tstd::vector<OBSWeakSource> updatedScenes;\n\tstd::vector<OBSSource> updatedLabels;\n\n\tupdatedLabels.emplace_back(CreateLabel(Str(\"StudioMode.Preview\"), h / 2));\n\tupdatedLabels.emplace_back(CreateLabel(Str(\"StudioMode.Program\"), h / 2));\n\n\tsize_t i = 0;\n\twhile (i < scenes.sources.num && updatedScenes.size() < maxSrcs) {\n\t\tobs_source_t *src = scenes.sources.array[i++];\n\t\tOBSDataAutoRelease data = obs_source_get_private_settings(src);\n\n\t\tobs_data_set_default_bool(data, \"show_in_multiview\", true);\n\t\tif (!obs_data_get_bool(data, \"show_in_multiview\"))\n\t\t\tcontinue;\n\n\t\tupdatedScenes.emplace_back(OBSGetWeakRef(src));\n\t\tobs_source_inc_showing(src);\n\n\t\tupdatedLabels.emplace_back(CreateLabel(obs_source_get_name(src), h / 3));\n\t}\n\n\tobs_frontend_source_list_free(&scenes);\n\n\tfor (OBSWeakSource &weakSrc : multiviewScenes) {\n\t\tOBSSource src = OBSGetStrongRef(weakSrc);\n\t\tif (src)\n\t\t\tobs_source_dec_showing(src);\n\t}\n\n\tmultiviewScenes = std::move(updatedScenes);\n\tmultiviewLabels = std::move(updatedLabels);\n}\n\nstatic inline uint32_t labelOffset(MultiviewLayout multiviewLayout, obs_source_t *label, uint32_t cx)\n{\n\tuint32_t w = obs_source_get_width(label);\n\n\tint n; // Twice of scale factor of preview and program scenes\n\tswitch (multiviewLayout) {\n\tcase MultiviewLayout::HORIZONTAL_TOP_24_SCENES:\n\t\tn = 6;\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_25_SCENES:\n\t\tn = 10;\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_16_SCENES:\n\t\tn = 8;\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_9_SCENES:\n\t\tn = 6;\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_4_SCENES:\n\t\tn = 4;\n\t\tbreak;\n\tdefault:\n\t\tn = 4;\n\t\tbreak;\n\t}\n\n\tw = uint32_t(w * ((1.0f) / n));\n\treturn (cx / 2) - w;\n}\n\nvoid Multiview::Render(uint32_t cx, uint32_t cy)\n{\n\tOBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();\n\n\tuint32_t targetCX, targetCY;\n\tint x, y;\n\tfloat scale;\n\n\ttargetCX = (uint32_t)fw;\n\ttargetCY = (uint32_t)fh;\n\n\tGetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale);\n\n\tOBSSource previewSrc = main->GetCurrentSceneSource();\n\tOBSSource programSrc = main->GetProgramSource();\n\tbool studioMode = main->IsPreviewProgramMode();\n\n\tauto drawBox = [&](float cx, float cy, uint32_t colorVal) {\n\t\tgs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);\n\t\tgs_eparam_t *color = gs_effect_get_param_by_name(solid, \"color\");\n\n\t\tgs_effect_set_color(color, colorVal);\n\t\twhile (gs_effect_loop(solid, \"Solid\"))\n\t\t\tgs_draw_sprite(nullptr, 0, (uint32_t)cx, (uint32_t)cy);\n\t};\n\n\tauto setRegion = [&](float bx, float by, float cx, float cy) {\n\t\tfloat vX = int(x + bx * scale);\n\t\tfloat vY = int(y + by * scale);\n\t\tfloat vCX = int(cx * scale);\n\t\tfloat vCY = int(cy * scale);\n\n\t\tfloat oL = bx;\n\t\tfloat oT = by;\n\t\tfloat oR = (bx + cx);\n\t\tfloat oB = (by + cy);\n\n\t\tstartRegion(vX, vY, vCX, vCY, oL, oR, oT, oB);\n\t};\n\n\tauto calcBaseSource = [&](size_t i) {\n\t\tswitch (multiviewLayout) {\n\t\tcase MultiviewLayout::HORIZONTAL_TOP_18_SCENES:\n\t\t\tsourceX = (i % 6) * scenesCX;\n\t\t\tsourceY = pvwprgCY + (i / 6) * scenesCY;\n\t\t\tbreak;\n\t\tcase MultiviewLayout::HORIZONTAL_TOP_24_SCENES:\n\t\t\tsourceX = (i % 6) * scenesCX;\n\t\t\tsourceY = pvwprgCY + (i / 6) * scenesCY;\n\t\t\tbreak;\n\t\tcase MultiviewLayout::VERTICAL_LEFT_8_SCENES:\n\t\t\tsourceX = pvwprgCX;\n\t\t\tsourceY = (i / 2) * scenesCY;\n\t\t\tif (i % 2 != 0)\n\t\t\t\tsourceX += scenesCX;\n\t\t\tbreak;\n\t\tcase MultiviewLayout::VERTICAL_RIGHT_8_SCENES:\n\t\t\tsourceX = 0;\n\t\t\tsourceY = (i / 2) * scenesCY;\n\t\t\tif (i % 2 != 0)\n\t\t\t\tsourceX = scenesCX;\n\t\t\tbreak;\n\t\tcase MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES:\n\t\t\tif (i < 4) {\n\t\t\t\tsourceX = (float(i) * scenesCX);\n\t\t\t\tsourceY = 0;\n\t\t\t} else {\n\t\t\t\tsourceX = (float(i - 4) * scenesCX);\n\t\t\t\tsourceY = scenesCY;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase MultiviewLayout::SCENES_ONLY_4_SCENES:\n\t\t\tsourceX = (i % 2) * scenesCX;\n\t\t\tsourceY = (i / 2) * scenesCY;\n\t\t\tbreak;\n\t\tcase MultiviewLayout::SCENES_ONLY_9_SCENES:\n\t\t\tsourceX = (i % 3) * scenesCX;\n\t\t\tsourceY = (i / 3) * scenesCY;\n\t\t\tbreak;\n\t\tcase MultiviewLayout::SCENES_ONLY_16_SCENES:\n\t\t\tsourceX = (i % 4) * scenesCX;\n\t\t\tsourceY = (i / 4) * scenesCY;\n\t\t\tbreak;\n\t\tcase MultiviewLayout::SCENES_ONLY_25_SCENES:\n\t\t\tsourceX = (i % 5) * scenesCX;\n\t\t\tsourceY = (i / 5) * scenesCY;\n\t\t\tbreak;\n\t\tdefault: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES:\n\t\t\tif (i < 4) {\n\t\t\t\tsourceX = (float(i) * scenesCX);\n\t\t\t\tsourceY = pvwprgCY;\n\t\t\t} else {\n\t\t\t\tsourceX = (float(i - 4) * scenesCX);\n\t\t\t\tsourceY = pvwprgCY + scenesCY;\n\t\t\t}\n\t\t}\n\t\tsiX = sourceX + thickness;\n\t\tsiY = sourceY + thickness;\n\t};\n\n\tauto calcPreviewProgram = [&](bool program) {\n\t\tswitch (multiviewLayout) {\n\t\tcase MultiviewLayout::HORIZONTAL_TOP_24_SCENES:\n\t\t\tsourceX = thickness + pvwprgCX / 2;\n\t\t\tsourceY = thickness;\n\t\t\tlabelX = offset + pvwprgCX / 2;\n\t\t\tlabelY = pvwprgCY;\n\t\t\tif (program) {\n\t\t\t\tsourceX += pvwprgCX;\n\t\t\t\tlabelX += pvwprgCX;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase MultiviewLayout::VERTICAL_LEFT_8_SCENES:\n\t\t\tsourceX = thickness;\n\t\t\tsourceY = pvwprgCY + thickness;\n\t\t\tlabelX = offset;\n\t\t\tlabelY = pvwprgCY * 2;\n\t\t\tif (program) {\n\t\t\t\tsourceY = thickness;\n\t\t\t\tlabelY = pvwprgCY;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase MultiviewLayout::VERTICAL_RIGHT_8_SCENES:\n\t\t\tsourceX = pvwprgCX + thickness;\n\t\t\tsourceY = pvwprgCY + thickness;\n\t\t\tlabelX = pvwprgCX + offset;\n\t\t\tlabelY = pvwprgCY * 2;\n\t\t\tif (program) {\n\t\t\t\tsourceY = thickness;\n\t\t\t\tlabelY = pvwprgCY;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES:\n\t\t\tsourceX = thickness;\n\t\t\tsourceY = pvwprgCY + thickness;\n\t\t\tlabelX = offset;\n\t\t\tlabelY = pvwprgCY * 2;\n\t\t\tif (program) {\n\t\t\t\tsourceX += pvwprgCX;\n\t\t\t\tlabelX += pvwprgCX;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase MultiviewLayout::SCENES_ONLY_4_SCENES:\n\t\tcase MultiviewLayout::SCENES_ONLY_9_SCENES:\n\t\tcase MultiviewLayout::SCENES_ONLY_16_SCENES:\n\t\t\tsourceX = thickness;\n\t\t\tsourceY = thickness;\n\t\t\tlabelX = offset;\n\t\t\tbreak;\n\t\tdefault: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES and 18_SCENES\n\t\t\tsourceX = thickness;\n\t\t\tsourceY = thickness;\n\t\t\tlabelX = offset;\n\t\t\tlabelY = pvwprgCY;\n\t\t\tif (program) {\n\t\t\t\tsourceX += pvwprgCX;\n\t\t\t\tlabelX += pvwprgCX;\n\t\t\t}\n\t\t}\n\t};\n\n\tauto paintAreaWithColor = [&](float tx, float ty, float cx, float cy, uint32_t color) {\n\t\tgs_matrix_push();\n\t\tgs_matrix_translate3f(tx, ty, 0.0f);\n\t\tdrawBox(cx, cy, color);\n\t\tgs_matrix_pop();\n\t};\n\n\t// Define the whole usable region for the multiview\n\tstartRegion(x, y, targetCX * scale, targetCY * scale, 0.0f, fw, 0.0f, fh);\n\n\t// Change the background color to highlight all sources\n\tdrawBox(fw, fh, outerColor);\n\n\t/* ----------------------------- */\n\t/* draw sources                  */\n\n\tfor (size_t i = 0; i < maxSrcs; i++) {\n\t\t// Handle all the offsets\n\t\tcalcBaseSource(i);\n\n\t\tif (i >= multiviewScenes.size()) {\n\t\t\t// Just paint the background and continue\n\t\t\tpaintAreaWithColor(sourceX, sourceY, scenesCX, scenesCY, outerColor);\n\t\t\tpaintAreaWithColor(siX, siY, siCX, siCY, backgroundColor);\n\t\t\tcontinue;\n\t\t}\n\n\t\tOBSSource src = OBSGetStrongRef(multiviewScenes[i]);\n\n\t\t// We have a source. Now chose the proper highlight color\n\t\tuint32_t colorVal = outerColor;\n\t\tif (src == programSrc)\n\t\t\tcolorVal = programColor;\n\t\telse if (src == previewSrc)\n\t\t\tcolorVal = studioMode ? previewColor : programColor;\n\n\t\t// Paint the background\n\t\tpaintAreaWithColor(sourceX, sourceY, scenesCX, scenesCY, colorVal);\n\t\tpaintAreaWithColor(siX, siY, siCX, siCY, backgroundColor);\n\n\t\t/* ----------- */\n\n\t\t// Render the source\n\t\tgs_matrix_push();\n\t\tgs_matrix_translate3f(siX, siY, 0.0f);\n\t\tgs_matrix_scale3f(siScaleX, siScaleY, 1.0f);\n\t\tsetRegion(siX, siY, siCX, siCY);\n\t\tobs_source_video_render(src);\n\t\tendRegion();\n\t\tgs_matrix_pop();\n\n\t\t/* ----------- */\n\n\t\t// Render the label\n\t\tif (!drawLabel)\n\t\t\tcontinue;\n\n\t\tobs_source *label = multiviewLabels[i + 2];\n\t\tif (!label)\n\t\t\tcontinue;\n\n\t\toffset = labelOffset(multiviewLayout, label, scenesCX);\n\n\t\tgs_matrix_push();\n\t\tgs_matrix_translate3f(sourceX + offset,\n\t\t\t\t      sourceY + scenesCY - (obs_source_get_height(label) * ppiScaleY) - (thickness * 3),\n\t\t\t\t      0.0f);\n\t\tgs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);\n\t\tdrawBox(obs_source_get_width(label), obs_source_get_height(label) + thicknessx2, labelColor);\n\t\tgs_matrix_translate3f(0, thickness, 0.0f);\n\t\tobs_source_video_render(label);\n\t\tgs_matrix_pop();\n\t}\n\n\tif (multiviewLayout == MultiviewLayout::SCENES_ONLY_4_SCENES ||\n\t    multiviewLayout == MultiviewLayout::SCENES_ONLY_9_SCENES ||\n\t    multiviewLayout == MultiviewLayout::SCENES_ONLY_16_SCENES ||\n\t    multiviewLayout == MultiviewLayout::SCENES_ONLY_25_SCENES) {\n\t\tendRegion();\n\t\treturn;\n\t}\n\n\t/* ----------------------------- */\n\t/* draw preview                  */\n\n\tobs_source_t *previewLabel = multiviewLabels[0];\n\toffset = labelOffset(multiviewLayout, previewLabel, pvwprgCX);\n\tcalcPreviewProgram(false);\n\n\t// Paint the background\n\tpaintAreaWithColor(sourceX, sourceY, ppiCX, ppiCY, backgroundColor);\n\n\t// Scale and Draw the preview\n\tgs_matrix_push();\n\tgs_matrix_translate3f(sourceX, sourceY, 0.0f);\n\tgs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);\n\tsetRegion(sourceX, sourceY, ppiCX, ppiCY);\n\tif (studioMode)\n\t\tobs_source_video_render(previewSrc);\n\telse\n\t\tobs_render_main_texture();\n\n\tif (drawSafeArea) {\n\t\tRenderSafeAreas(actionSafeMargin, targetCX, targetCY);\n\t\tRenderSafeAreas(graphicsSafeMargin, targetCX, targetCY);\n\t\tRenderSafeAreas(fourByThreeSafeMargin, targetCX, targetCY);\n\t\tRenderSafeAreas(leftLine, targetCX, targetCY);\n\t\tRenderSafeAreas(topLine, targetCX, targetCY);\n\t\tRenderSafeAreas(rightLine, targetCX, targetCY);\n\t}\n\n\tendRegion();\n\tgs_matrix_pop();\n\n\t/* ----------- */\n\n\t// Draw the Label\n\tif (drawLabel) {\n\t\tgs_matrix_push();\n\t\tgs_matrix_translate3f(\n\t\t\tlabelX, labelY - (obs_source_get_height(previewLabel) * ppiScaleY) - (thickness * 3), 0.0f);\n\t\tgs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);\n\t\tdrawBox(obs_source_get_width(previewLabel), obs_source_get_height(previewLabel) + thicknessx2,\n\t\t\tlabelColor);\n\t\tgs_matrix_translate3f(0, thickness, 0.0f);\n\t\tobs_source_video_render(previewLabel);\n\t\tgs_matrix_pop();\n\t}\n\n\t/* ----------------------------- */\n\t/* draw program                  */\n\n\tobs_source_t *programLabel = multiviewLabels[1];\n\toffset = labelOffset(multiviewLayout, programLabel, pvwprgCX);\n\tcalcPreviewProgram(true);\n\n\tpaintAreaWithColor(sourceX, sourceY, ppiCX, ppiCY, backgroundColor);\n\n\t// Scale and Draw the program\n\tgs_matrix_push();\n\tgs_matrix_translate3f(sourceX, sourceY, 0.0f);\n\tgs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);\n\tsetRegion(sourceX, sourceY, ppiCX, ppiCY);\n\tobs_render_main_texture();\n\tendRegion();\n\tgs_matrix_pop();\n\n\t/* ----------- */\n\n\t// Draw the Label\n\tif (drawLabel) {\n\t\tgs_matrix_push();\n\t\tgs_matrix_translate3f(\n\t\t\tlabelX, labelY - (obs_source_get_height(programLabel) * ppiScaleY) - (thickness * 3), 0.0f);\n\t\tgs_matrix_scale3f(ppiScaleX, ppiScaleY, 1.0f);\n\t\tdrawBox(obs_source_get_width(programLabel), obs_source_get_height(programLabel) + thicknessx2,\n\t\t\tlabelColor);\n\t\tgs_matrix_translate3f(0, thickness, 0.0f);\n\t\tobs_source_video_render(programLabel);\n\t\tgs_matrix_pop();\n\t}\n\n\t// Region for future usage with additional info.\n\tif (multiviewLayout == MultiviewLayout::HORIZONTAL_TOP_24_SCENES) {\n\t\t// Just paint the background for now\n\t\tpaintAreaWithColor(thickness, thickness, siCX, siCY * 2 + thicknessx2, backgroundColor);\n\t\tpaintAreaWithColor(thickness + 2.5 * (thicknessx2 + ppiCX), thickness, siCX, siCY * 2 + thicknessx2,\n\t\t\t\t   backgroundColor);\n\t}\n\n\tendRegion();\n}\n\nOBSSource Multiview::GetSourceByPosition(int x, int y)\n{\n\tint pos = -1;\n\tQWidget *rec = QApplication::activeWindow();\n\tif (!rec)\n\t\treturn nullptr;\n\tint cx = rec->width();\n\tint cy = rec->height();\n\tint minX = 0;\n\tint minY = 0;\n\tint maxX = cx;\n\tint maxY = cy;\n\n\tswitch (multiviewLayout) {\n\tcase MultiviewLayout::HORIZONTAL_TOP_18_SCENES:\n\t\tif (float(cx) / float(cy) > ratio) {\n\t\t\tint validX = cy * ratio;\n\t\t\tminX = (cx / 2) - (validX / 2);\n\t\t\tmaxX = (cx / 2) + (validX / 2);\n\t\t} else {\n\t\t\tint validY = cx / ratio;\n\t\t\tmaxY = (cy / 2) + (validY / 2);\n\t\t}\n\t\tminY = cy / 2;\n\n\t\tif (x < minX || x > maxX || y < minY || y > maxY)\n\t\t\tbreak;\n\n\t\tpos = (x - minX) / ((maxX - minX) / 6);\n\t\tpos += ((y - minY) / ((maxY - minY) / 3)) * 6;\n\n\t\tbreak;\n\tcase MultiviewLayout::HORIZONTAL_TOP_24_SCENES:\n\t\tif (float(cx) / float(cy) > ratio) {\n\t\t\tint validX = cy * ratio;\n\t\t\tminX = (cx / 2) - (validX / 2);\n\t\t\tmaxX = (cx / 2) + (validX / 2);\n\t\t\tminY = cy / 3;\n\t\t} else {\n\t\t\tint validY = cx / ratio;\n\t\t\tmaxY = (cy / 2) + (validY / 2);\n\t\t\tminY = (cy / 2) - (validY / 6);\n\t\t}\n\n\t\tif (x < minX || x > maxX || y < minY || y > maxY)\n\t\t\tbreak;\n\n\t\tpos = (x - minX) / ((maxX - minX) / 6);\n\t\tpos += ((y - minY) / ((maxY - minY) / 4)) * 6;\n\n\t\tbreak;\n\tcase MultiviewLayout::VERTICAL_LEFT_8_SCENES:\n\t\tif (float(cx) / float(cy) > ratio) {\n\t\t\tint validX = cy * ratio;\n\t\t\tmaxX = (cx / 2) + (validX / 2);\n\t\t} else {\n\t\t\tint validY = cx / ratio;\n\t\t\tminY = (cy / 2) - (validY / 2);\n\t\t\tmaxY = (cy / 2) + (validY / 2);\n\t\t}\n\n\t\tminX = cx / 2;\n\n\t\tif (x < minX || x > maxX || y < minY || y > maxY)\n\t\t\tbreak;\n\n\t\tpos = 2 * ((y - minY) / ((maxY - minY) / 4));\n\t\tif (x > minX + ((maxX - minX) / 2))\n\t\t\tpos++;\n\t\tbreak;\n\tcase MultiviewLayout::VERTICAL_RIGHT_8_SCENES:\n\t\tif (float(cx) / float(cy) > ratio) {\n\t\t\tint validX = cy * ratio;\n\t\t\tminX = (cx / 2) - (validX / 2);\n\t\t} else {\n\t\t\tint validY = cx / ratio;\n\t\t\tminY = (cy / 2) - (validY / 2);\n\t\t\tmaxY = (cy / 2) + (validY / 2);\n\t\t}\n\n\t\tmaxX = (cx / 2);\n\n\t\tif (x < minX || x > maxX || y < minY || y > maxY)\n\t\t\tbreak;\n\n\t\tpos = 2 * ((y - minY) / ((maxY - minY) / 4));\n\t\tif (x > minX + ((maxX - minX) / 2))\n\t\t\tpos++;\n\t\tbreak;\n\tcase MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES:\n\t\tif (float(cx) / float(cy) > ratio) {\n\t\t\tint validX = cy * ratio;\n\t\t\tminX = (cx / 2) - (validX / 2);\n\t\t\tmaxX = (cx / 2) + (validX / 2);\n\t\t} else {\n\t\t\tint validY = cx / ratio;\n\t\t\tminY = (cy / 2) - (validY / 2);\n\t\t}\n\n\t\tmaxY = (cy / 2);\n\n\t\tif (x < minX || x > maxX || y < minY || y > maxY)\n\t\t\tbreak;\n\n\t\tpos = (x - minX) / ((maxX - minX) / 4);\n\t\tif (y > minY + ((maxY - minY) / 2))\n\t\t\tpos += 4;\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_4_SCENES:\n\t\tif (float(cx) / float(cy) > ratio) {\n\t\t\tint validX = cy * ratio;\n\t\t\tminX = (cx / 2) - (validX / 2);\n\t\t\tmaxX = (cx / 2) + (validX / 2);\n\t\t} else {\n\t\t\tint validY = cx / ratio;\n\t\t\tmaxY = (cy / 2) + (validY / 2);\n\t\t\tminY = (cy / 2) - (validY / 2);\n\t\t}\n\n\t\tif (x < minX || x > maxX || y < minY || y > maxY)\n\t\t\tbreak;\n\n\t\tpos = (x - minX) / ((maxX - minX) / 2);\n\t\tpos += ((y - minY) / ((maxY - minY) / 2)) * 2;\n\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_9_SCENES:\n\t\tif (float(cx) / float(cy) > ratio) {\n\t\t\tint validX = cy * ratio;\n\t\t\tminX = (cx / 2) - (validX / 2);\n\t\t\tmaxX = (cx / 2) + (validX / 2);\n\t\t} else {\n\t\t\tint validY = cx / ratio;\n\t\t\tmaxY = (cy / 2) + (validY / 2);\n\t\t\tminY = (cy / 2) - (validY / 2);\n\t\t}\n\n\t\tif (x < minX || x > maxX || y < minY || y > maxY)\n\t\t\tbreak;\n\n\t\tpos = (x - minX) / ((maxX - minX) / 3);\n\t\tpos += ((y - minY) / ((maxY - minY) / 3)) * 3;\n\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_16_SCENES:\n\t\tif (float(cx) / float(cy) > ratio) {\n\t\t\tint validX = cy * ratio;\n\t\t\tminX = (cx / 2) - (validX / 2);\n\t\t\tmaxX = (cx / 2) + (validX / 2);\n\t\t} else {\n\t\t\tint validY = cx / ratio;\n\t\t\tmaxY = (cy / 2) + (validY / 2);\n\t\t\tminY = (cy / 2) - (validY / 2);\n\t\t}\n\n\t\tif (x < minX || x > maxX || y < minY || y > maxY)\n\t\t\tbreak;\n\n\t\tpos = (x - minX) / ((maxX - minX) / 4);\n\t\tpos += ((y - minY) / ((maxY - minY) / 4)) * 4;\n\n\t\tbreak;\n\tcase MultiviewLayout::SCENES_ONLY_25_SCENES:\n\t\tif (float(cx) / float(cy) > ratio) {\n\t\t\tint validX = cy * ratio;\n\t\t\tminX = (cx / 2) - (validX / 2);\n\t\t\tmaxX = (cx / 2) + (validX / 2);\n\t\t} else {\n\t\t\tint validY = cx / ratio;\n\t\t\tmaxY = (cy / 2) + (validY / 2);\n\t\t\tminY = (cy / 2) - (validY / 2);\n\t\t}\n\n\t\tif (x < minX || x > maxX || y < minY || y > maxY)\n\t\t\tbreak;\n\n\t\tpos = (x - minX) / ((maxX - minX) / 5);\n\t\tpos += ((y - minY) / ((maxY - minY) / 5)) * 5;\n\n\t\tbreak;\n\tdefault: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES\n\t\tif (float(cx) / float(cy) > ratio) {\n\t\t\tint validX = cy * ratio;\n\t\t\tminX = (cx / 2) - (validX / 2);\n\t\t\tmaxX = (cx / 2) + (validX / 2);\n\t\t} else {\n\t\t\tint validY = cx / ratio;\n\t\t\tmaxY = (cy / 2) + (validY / 2);\n\t\t}\n\n\t\tminY = (cy / 2);\n\n\t\tif (x < minX || x > maxX || y < minY || y > maxY)\n\t\t\tbreak;\n\n\t\tpos = (x - minX) / ((maxX - minX) / 4);\n\t\tif (y > minY + ((maxY - minY) / 2))\n\t\t\tpos += 4;\n\t}\n\n\tif (pos < 0 || pos >= (int)multiviewScenes.size())\n\t\treturn nullptr;\n\n\treturn OBSGetStrongRef(multiviewScenes[pos]);\n}\n"
  },
  {
    "path": "frontend/components/Multiview.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <vector>\n\nenum class MultiviewLayout : uint8_t {\n\tHORIZONTAL_TOP_8_SCENES = 0,\n\tHORIZONTAL_BOTTOM_8_SCENES = 1,\n\tVERTICAL_LEFT_8_SCENES = 2,\n\tVERTICAL_RIGHT_8_SCENES = 3,\n\tHORIZONTAL_TOP_24_SCENES = 4,\n\tHORIZONTAL_TOP_18_SCENES = 5,\n\tSCENES_ONLY_4_SCENES = 6,\n\tSCENES_ONLY_9_SCENES = 7,\n\tSCENES_ONLY_16_SCENES = 8,\n\tSCENES_ONLY_25_SCENES = 9,\n};\n\nclass Multiview {\npublic:\n\tMultiview();\n\t~Multiview();\n\tvoid Update(MultiviewLayout multiviewLayout, bool drawLabel, bool drawSafeArea);\n\tvoid Render(uint32_t cx, uint32_t cy);\n\tOBSSource GetSourceByPosition(int x, int y);\n\nprivate:\n\tbool drawLabel, drawSafeArea;\n\tMultiviewLayout multiviewLayout;\n\tsize_t maxSrcs;\n\tgs_vertbuffer_t *actionSafeMargin = nullptr;\n\tgs_vertbuffer_t *graphicsSafeMargin = nullptr;\n\tgs_vertbuffer_t *fourByThreeSafeMargin = nullptr;\n\tgs_vertbuffer_t *leftLine = nullptr;\n\tgs_vertbuffer_t *topLine = nullptr;\n\tgs_vertbuffer_t *rightLine = nullptr;\n\n\tstd::vector<OBSWeakSource> multiviewScenes;\n\tstd::vector<OBSSource> multiviewLabels;\n\n\t// Multiview position helpers\n\tfloat thickness = 6;\n\tfloat offset, thicknessx2 = thickness * 2, pvwprgCX, pvwprgCY, sourceX, sourceY, labelX, labelY, scenesCX,\n\t\t      scenesCY, ppiCX, ppiCY, siX, siY, siCX, siCY, ppiScaleX, ppiScaleY, siScaleX, siScaleY, fw, fh,\n\t\t      ratio;\n\n\t// argb colors\n\tstatic const uint32_t outerColor = 0xFF999999;\n\tstatic const uint32_t labelColor = 0x33000000;\n\tstatic const uint32_t backgroundColor = 0xFF000000;\n\tstatic const uint32_t previewColor = 0xFF00D000;\n\tstatic const uint32_t programColor = 0xFFD00000;\n};\n\nstatic inline void startRegion(int vX, int vY, int vCX, int vCY, float oL, float oR, float oT, float oB)\n{\n\tgs_projection_push();\n\tgs_viewport_push();\n\tgs_set_viewport(vX, vY, vCX, vCY);\n\tgs_ortho(oL, oR, oT, oB, -100.0f, 100.0f);\n}\n\nstatic inline void endRegion()\n{\n\tgs_viewport_pop();\n\tgs_projection_pop();\n}\n"
  },
  {
    "path": "frontend/components/MuteCheckBox.hpp",
    "content": "#pragma once\n\n#include <QCheckBox>\n\nclass MuteCheckBox : public QCheckBox {\n\tQ_OBJECT\n\npublic:\n\tMuteCheckBox(QWidget *parent = nullptr) : QCheckBox(parent)\n\t{\n\t\tsetTristate(true);\n\t\tsetProperty(\"class\", \"indicator-mute\");\n\t}\n\nprotected:\n\t/* While we need it to be tristate internally, we don't want a user being\n\t * able to manually get into the partial state. */\n\tvoid nextCheckState() override\n\t{\n\t\tif (checkState() != Qt::Checked)\n\t\t\tsetCheckState(Qt::Checked);\n\t\telse\n\t\t\tsetCheckState(Qt::Unchecked);\n\t}\n};\n"
  },
  {
    "path": "frontend/components/OBSAdvAudioCtrl.cpp",
    "content": "#include \"OBSAdvAudioCtrl.hpp\"\n\n#include <components/BalanceSlider.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QCheckBox>\n#include <QStackedWidget>\n\n#include \"moc_OBSAdvAudioCtrl.cpp\"\n\n#ifndef NSEC_PER_MSEC\n#define NSEC_PER_MSEC 1000000\n#endif\n\n#define MIN_DB -96.0\n#define MAX_DB 26.0\n\nstatic inline void setMixer(obs_source_t *source, const int mixerIdx, const bool checked);\n\nOBSAdvAudioCtrl::OBSAdvAudioCtrl(QGridLayout *, obs_source_t *source_) : source(source_)\n{\n\tQHBoxLayout *hlayout;\n\tsignal_handler_t *handler = obs_source_get_signal_handler(source);\n\tQString sourceName = QT_UTF8(obs_source_get_name(source));\n\tfloat vol = obs_source_get_volume(source);\n\tuint32_t flags = obs_source_get_flags(source);\n\tuint32_t mixers = obs_source_get_audio_mixers(source);\n\n\tmixerContainer = new QWidget();\n\tbalanceContainer = new QWidget();\n\tlabelL = new QLabel();\n\tlabelR = new QLabel();\n\ticonLabel = new QLabel();\n\tnameLabel = new QLabel();\n\tactive = new QLabel();\n\tstackedWidget = new QStackedWidget();\n\tvolume = new QDoubleSpinBox();\n\tpercent = new QSpinBox();\n\tforceMono = new QCheckBox();\n\tbalance = new BalanceSlider();\n\tif (obs_audio_monitoring_available())\n\t\tmonitoringType = new QComboBox();\n\tsyncOffset = new QSpinBox();\n\tmixer1 = new QCheckBox();\n\tmixer2 = new QCheckBox();\n\tmixer3 = new QCheckBox();\n\tmixer4 = new QCheckBox();\n\tmixer5 = new QCheckBox();\n\tmixer6 = new QCheckBox();\n\n\tsigs.emplace_back(handler, \"activate\", OBSSourceActivated, this);\n\tsigs.emplace_back(handler, \"deactivate\", OBSSourceDeactivated, this);\n\tsigs.emplace_back(handler, \"audio_activate\", OBSSourceActivated, this);\n\tsigs.emplace_back(handler, \"audio_deactivate\", OBSSourceDeactivated, this);\n\tsigs.emplace_back(handler, \"volume\", OBSSourceVolumeChanged, this);\n\tsigs.emplace_back(handler, \"audio_sync\", OBSSourceSyncChanged, this);\n\tsigs.emplace_back(handler, \"update_flags\", OBSSourceFlagsChanged, this);\n\tif (obs_audio_monitoring_available())\n\t\tsigs.emplace_back(handler, \"audio_monitoring\", OBSSourceMonitoringTypeChanged, this);\n\tsigs.emplace_back(handler, \"audio_mixers\", OBSSourceMixersChanged, this);\n\tsigs.emplace_back(handler, \"audio_balance\", OBSSourceBalanceChanged, this);\n\tsigs.emplace_back(handler, \"rename\", OBSSourceRenamed, this);\n\n\thlayout = new QHBoxLayout();\n\thlayout->setContentsMargins(0, 0, 0, 0);\n\tmixerContainer->setLayout(hlayout);\n\thlayout = new QHBoxLayout();\n\thlayout->setContentsMargins(0, 0, 0, 0);\n\tbalanceContainer->setLayout(hlayout);\n\tbalanceContainer->setFixedWidth(150);\n\n\tlabelL->setText(\"L\");\n\tlabelR->setText(\"R\");\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tQIcon sourceIcon = main->GetSourceIcon(obs_source_get_id(source));\n\tQPixmap pixmap = sourceIcon.pixmap(QSize(16, 16));\n\ticonLabel->setPixmap(pixmap);\n\ticonLabel->setFixedSize(16, 16);\n\ticonLabel->setStyleSheet(\"background: none\");\n\n\tSetSourceName(sourceName);\n\tnameLabel->setAlignment(Qt::AlignVCenter);\n\n\tbool isActive = obs_source_active(source) && obs_source_audio_active(source);\n\tactive->setText(isActive ? QTStr(\"Basic.Stats.Status.Active\") : QTStr(\"Basic.Stats.Status.Inactive\"));\n\tif (isActive)\n\t\tsetClasses(active, \"text-danger\");\n\tactive->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);\n\n\tvolume->setMinimum(MIN_DB - 0.1);\n\tvolume->setMaximum(MAX_DB);\n\tvolume->setSingleStep(0.1);\n\tvolume->setDecimals(1);\n\tvolume->setSuffix(\" dB\");\n\tvolume->setValue(obs_mul_to_db(vol));\n\tvolume->setAccessibleName(QTStr(\"Basic.AdvAudio.VolumeSource\").arg(sourceName));\n\n\tif (volume->value() < MIN_DB) {\n\t\tvolume->setSpecialValueText(\"-inf dB\");\n\t\tvolume->setAccessibleDescription(\"-inf dB\");\n\t}\n\n\tpercent->setMinimum(0);\n\tpercent->setMaximum(2000);\n\tpercent->setSuffix(\"%\");\n\tpercent->setValue((int)(obs_source_get_volume(source) * 100.0f));\n\tpercent->setAccessibleName(QTStr(\"Basic.AdvAudio.VolumeSource\").arg(sourceName));\n\n\tstackedWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);\n\tstackedWidget->setFixedWidth(100);\n\tstackedWidget->addWidget(volume);\n\tstackedWidget->addWidget(percent);\n\n\tVolumeType volType = (VolumeType)config_get_int(App()->GetUserConfig(), \"BasicWindow\", \"AdvAudioVolumeType\");\n\n\tSetVolumeWidget(volType);\n\n\tforceMono->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);\n\tforceMono->setChecked((flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0);\n\tforceMono->setAccessibleName(QTStr(\"Basic.AdvAudio.MonoSource\").arg(sourceName));\n\n\tbalance->setOrientation(Qt::Horizontal);\n\tbalance->setMinimum(0);\n\tbalance->setMaximum(100);\n\tbalance->setTickPosition(QSlider::TicksAbove);\n\tbalance->setTickInterval(50);\n\tbalance->setAccessibleName(QTStr(\"Basic.AdvAudio.BalanceSource\").arg(sourceName));\n\n\tconst char *speakers = config_get_string(main->Config(), \"Audio\", \"ChannelSetup\");\n\n\tif (strcmp(speakers, \"Mono\") == 0)\n\t\tbalance->setEnabled(false);\n\telse\n\t\tbalance->setEnabled(true);\n\n\tfloat bal = obs_source_get_balance_value(source) * 100.0f;\n\tbalance->setValue((int)bal);\n\n\tint64_t cur_sync = obs_source_get_sync_offset(source);\n\tsyncOffset->setMinimum(-950);\n\tsyncOffset->setMaximum(20000);\n\tsyncOffset->setSuffix(\" ms\");\n\tsyncOffset->setValue(int(cur_sync / NSEC_PER_MSEC));\n\tsyncOffset->setFixedWidth(100);\n\tsyncOffset->setAccessibleName(QTStr(\"Basic.AdvAudio.SyncOffsetSource\").arg(sourceName));\n\n\tint idx;\n\tif (obs_audio_monitoring_available()) {\n\t\tmonitoringType->addItem(QTStr(\"Basic.AdvAudio.Monitoring.None\"), (int)OBS_MONITORING_TYPE_NONE);\n\t\tmonitoringType->addItem(QTStr(\"Basic.AdvAudio.Monitoring.MonitorOnly\"),\n\t\t\t\t\t(int)OBS_MONITORING_TYPE_MONITOR_ONLY);\n\t\tmonitoringType->addItem(QTStr(\"Basic.AdvAudio.Monitoring.Both\"),\n\t\t\t\t\t(int)OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);\n\t\tint mt = (int)obs_source_get_monitoring_type(source);\n\t\tidx = monitoringType->findData(mt);\n\t\tmonitoringType->setCurrentIndex(idx);\n\t\tmonitoringType->setAccessibleName(QTStr(\"Basic.AdvAudio.MonitoringSource\").arg(sourceName));\n\t\tmonitoringType->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);\n\t}\n\n\tmixer1->setText(\"1\");\n\tmixer1->setChecked(mixers & (1 << 0));\n\tmixer1->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track1\"));\n\tmixer2->setText(\"2\");\n\tmixer2->setChecked(mixers & (1 << 1));\n\tmixer2->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track2\"));\n\tmixer3->setText(\"3\");\n\tmixer3->setChecked(mixers & (1 << 2));\n\tmixer3->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track3\"));\n\tmixer4->setText(\"4\");\n\tmixer4->setChecked(mixers & (1 << 3));\n\tmixer4->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track4\"));\n\tmixer5->setText(\"5\");\n\tmixer5->setChecked(mixers & (1 << 4));\n\tmixer5->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track5\"));\n\tmixer6->setText(\"6\");\n\tmixer6->setChecked(mixers & (1 << 5));\n\tmixer6->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track6\"));\n\n\tbalanceContainer->layout()->addWidget(labelL);\n\tbalanceContainer->layout()->addWidget(balance);\n\tbalanceContainer->layout()->addWidget(labelR);\n\n\tspeaker_layout sl = obs_source_get_speaker_layout(source);\n\n\tif (sl != SPEAKERS_STEREO)\n\t\tbalanceContainer->setEnabled(false);\n\n\tmixerContainer->layout()->addWidget(mixer1);\n\tmixerContainer->layout()->addWidget(mixer2);\n\tmixerContainer->layout()->addWidget(mixer3);\n\tmixerContainer->layout()->addWidget(mixer4);\n\tmixerContainer->layout()->addWidget(mixer5);\n\tmixerContainer->layout()->addWidget(mixer6);\n\tmixerContainer->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);\n\n\tconnect(volume, &QDoubleSpinBox::valueChanged, this, &OBSAdvAudioCtrl::volumeChanged);\n\tconnect(percent, &QSpinBox::valueChanged, this, &OBSAdvAudioCtrl::percentChanged);\n\tconnect(forceMono, &QCheckBox::clicked, this, &OBSAdvAudioCtrl::downmixMonoChanged);\n\tconnect(balance, &BalanceSlider::valueChanged, this, &OBSAdvAudioCtrl::balanceChanged);\n\tconnect(balance, &BalanceSlider::doubleClicked, this, &OBSAdvAudioCtrl::ResetBalance);\n\tconnect(syncOffset, &QSpinBox::valueChanged, this, &OBSAdvAudioCtrl::syncOffsetChanged);\n\tif (obs_audio_monitoring_available())\n\t\tconnect(monitoringType, &QComboBox::currentIndexChanged, this, &OBSAdvAudioCtrl::monitoringTypeChanged);\n\n\tauto connectMixer = [this](QCheckBox *mixer, int num) {\n\t\tconnect(mixer, &QCheckBox::clicked, this,\n\t\t\t[this, num](bool checked) { setMixer(source, num, checked); });\n\t};\n\tconnectMixer(mixer1, 0);\n\tconnectMixer(mixer2, 1);\n\tconnectMixer(mixer3, 2);\n\tconnectMixer(mixer4, 3);\n\tconnectMixer(mixer5, 4);\n\tconnectMixer(mixer6, 5);\n\n\tsetObjectName(sourceName);\n}\n\nOBSAdvAudioCtrl::~OBSAdvAudioCtrl()\n{\n\ticonLabel->deleteLater();\n\tnameLabel->deleteLater();\n\tactive->deleteLater();\n\tstackedWidget->deleteLater();\n\tforceMono->deleteLater();\n\tbalanceContainer->deleteLater();\n\tsyncOffset->deleteLater();\n\tif (obs_audio_monitoring_available())\n\t\tmonitoringType->deleteLater();\n\tmixerContainer->deleteLater();\n}\n\nvoid OBSAdvAudioCtrl::ShowAudioControl(QGridLayout *layout)\n{\n\tint lastRow = layout->rowCount();\n\tint idx = 0;\n\n\tlayout->addWidget(iconLabel, lastRow, idx++);\n\tlayout->addWidget(nameLabel, lastRow, idx++);\n\tlayout->addWidget(active, lastRow, idx++);\n\tlayout->addWidget(stackedWidget, lastRow, idx++);\n\tlayout->addWidget(forceMono, lastRow, idx++);\n\tlayout->addWidget(balanceContainer, lastRow, idx++);\n\tlayout->addWidget(syncOffset, lastRow, idx++);\n\tif (obs_audio_monitoring_available())\n\t\tlayout->addWidget(monitoringType, lastRow, idx++);\n\tlayout->addWidget(mixerContainer, lastRow, idx++);\n\tlayout->layout()->setAlignment(mixerContainer, Qt::AlignVCenter);\n\tlayout->setHorizontalSpacing(15);\n}\n\n/* ------------------------------------------------------------------------- */\n/* OBS source callbacks */\n\nvoid OBSAdvAudioCtrl::OBSSourceActivated(void *param, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<OBSAdvAudioCtrl *>(param), \"SourceActiveChanged\", Q_ARG(bool, true));\n}\n\nvoid OBSAdvAudioCtrl::OBSSourceDeactivated(void *param, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<OBSAdvAudioCtrl *>(param), \"SourceActiveChanged\", Q_ARG(bool, false));\n}\n\nvoid OBSAdvAudioCtrl::OBSSourceFlagsChanged(void *param, calldata_t *calldata)\n{\n\tuint32_t flags = (uint32_t)calldata_int(calldata, \"flags\");\n\tQMetaObject::invokeMethod(static_cast<OBSAdvAudioCtrl *>(param), \"SourceFlagsChanged\", Q_ARG(uint32_t, flags));\n}\n\nvoid OBSAdvAudioCtrl::OBSSourceVolumeChanged(void *param, calldata_t *calldata)\n{\n\tfloat volume = (float)calldata_float(calldata, \"volume\");\n\tQMetaObject::invokeMethod(static_cast<OBSAdvAudioCtrl *>(param), \"SourceVolumeChanged\", Q_ARG(float, volume));\n}\n\nvoid OBSAdvAudioCtrl::OBSSourceSyncChanged(void *param, calldata_t *calldata)\n{\n\tint64_t offset = calldata_int(calldata, \"offset\");\n\tQMetaObject::invokeMethod(static_cast<OBSAdvAudioCtrl *>(param), \"SourceSyncChanged\", Q_ARG(int64_t, offset));\n}\n\nvoid OBSAdvAudioCtrl::OBSSourceMonitoringTypeChanged(void *param, calldata_t *calldata)\n{\n\tint type = calldata_int(calldata, \"type\");\n\tQMetaObject::invokeMethod(static_cast<OBSAdvAudioCtrl *>(param), \"SourceMonitoringTypeChanged\",\n\t\t\t\t  Q_ARG(int, type));\n}\n\nvoid OBSAdvAudioCtrl::OBSSourceMixersChanged(void *param, calldata_t *calldata)\n{\n\tuint32_t mixers = (uint32_t)calldata_int(calldata, \"mixers\");\n\tQMetaObject::invokeMethod(static_cast<OBSAdvAudioCtrl *>(param), \"SourceMixersChanged\",\n\t\t\t\t  Q_ARG(uint32_t, mixers));\n}\n\nvoid OBSAdvAudioCtrl::OBSSourceBalanceChanged(void *param, calldata_t *calldata)\n{\n\tint balance = (float)calldata_float(calldata, \"balance\") * 100.0f;\n\tQMetaObject::invokeMethod(static_cast<OBSAdvAudioCtrl *>(param), \"SourceBalanceChanged\", Q_ARG(int, balance));\n}\n\nvoid OBSAdvAudioCtrl::OBSSourceRenamed(void *param, calldata_t *calldata)\n{\n\tQString newName = QT_UTF8(calldata_string(calldata, \"new_name\"));\n\n\tQMetaObject::invokeMethod(static_cast<OBSAdvAudioCtrl *>(param), \"SetSourceName\", Q_ARG(QString, newName));\n}\n\n/* ------------------------------------------------------------------------- */\n/* Qt event queue source callbacks */\n\nstatic inline void setCheckboxState(QCheckBox *checkbox, bool checked)\n{\n\tcheckbox->blockSignals(true);\n\tcheckbox->setChecked(checked);\n\tcheckbox->blockSignals(false);\n}\n\nvoid OBSAdvAudioCtrl::SourceActiveChanged(bool isActive)\n{\n\tif (isActive && obs_source_audio_active(source)) {\n\t\tactive->setText(QTStr(\"Basic.Stats.Status.Active\"));\n\t\tsetClasses(active, \"text-danger\");\n\t} else {\n\t\tactive->setText(QTStr(\"Basic.Stats.Status.Inactive\"));\n\t\tsetClasses(active, \"\");\n\t}\n}\n\nvoid OBSAdvAudioCtrl::SourceFlagsChanged(uint32_t flags)\n{\n\tbool forceMonoVal = (flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0;\n\tsetCheckboxState(forceMono, forceMonoVal);\n}\n\nvoid OBSAdvAudioCtrl::SourceVolumeChanged(float value)\n{\n\tvolume->blockSignals(true);\n\tpercent->blockSignals(true);\n\tvolume->setValue(obs_mul_to_db(value));\n\tpercent->setValue((int)std::round(value * 100.0f));\n\tpercent->blockSignals(false);\n\tvolume->blockSignals(false);\n}\n\nvoid OBSAdvAudioCtrl::SourceBalanceChanged(int value)\n{\n\tbalance->blockSignals(true);\n\tbalance->setValue(value);\n\tbalance->blockSignals(false);\n}\n\nvoid OBSAdvAudioCtrl::SourceSyncChanged(int64_t offset)\n{\n\tsyncOffset->blockSignals(true);\n\tsyncOffset->setValue(offset / NSEC_PER_MSEC);\n\tsyncOffset->blockSignals(false);\n}\n\nvoid OBSAdvAudioCtrl::SourceMonitoringTypeChanged(int type)\n{\n\tint idx = monitoringType->findData(type);\n\tmonitoringType->blockSignals(true);\n\tmonitoringType->setCurrentIndex(idx);\n\tmonitoringType->blockSignals(false);\n}\n\nvoid OBSAdvAudioCtrl::SourceMixersChanged(uint32_t mixers)\n{\n\tsetCheckboxState(mixer1, mixers & (1 << 0));\n\tsetCheckboxState(mixer2, mixers & (1 << 1));\n\tsetCheckboxState(mixer3, mixers & (1 << 2));\n\tsetCheckboxState(mixer4, mixers & (1 << 3));\n\tsetCheckboxState(mixer5, mixers & (1 << 4));\n\tsetCheckboxState(mixer6, mixers & (1 << 5));\n}\n\n/* ------------------------------------------------------------------------- */\n/* Qt control callbacks */\n\nvoid OBSAdvAudioCtrl::volumeChanged(double db)\n{\n\tfloat prev = obs_source_get_volume(source);\n\n\tif (db < MIN_DB) {\n\t\tvolume->setSpecialValueText(\"-inf dB\");\n\t\tdb = -INFINITY;\n\t}\n\n\tfloat val = obs_db_to_mul(db);\n\tobs_source_set_volume(source, val);\n\n\tauto undo_redo = [](const std::string &uuid, float val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_source_set_volume(source, val);\n\t};\n\n\tconst char *name = obs_source_get_name(source);\n\tconst char *uuid = obs_source_get_uuid(source);\n\tOBSBasic *main = OBSBasic::Get();\n\tmain->undo_s.add_action(QTStr(\"Undo.Volume.Change\").arg(name),\n\t\t\t\tstd::bind(undo_redo, std::placeholders::_1, prev),\n\t\t\t\tstd::bind(undo_redo, std::placeholders::_1, val), uuid, uuid, true);\n}\n\nvoid OBSAdvAudioCtrl::percentChanged(int percent)\n{\n\tfloat prev = obs_source_get_volume(source);\n\tfloat val = (float)percent / 100.0f;\n\n\tobs_source_set_volume(source, val);\n\n\tauto undo_redo = [](const std::string &uuid, float val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_source_set_volume(source, val);\n\t};\n\n\tconst char *name = obs_source_get_name(source);\n\tconst char *uuid = obs_source_get_uuid(source);\n\tOBSBasic::Get()->undo_s.add_action(QTStr(\"Undo.Volume.Change\").arg(name),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, prev),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid, true);\n}\n\nstatic inline void set_mono(obs_source_t *source, bool mono)\n{\n\tuint32_t flags = obs_source_get_flags(source);\n\tif (mono)\n\t\tflags |= OBS_SOURCE_FLAG_FORCE_MONO;\n\telse\n\t\tflags &= ~OBS_SOURCE_FLAG_FORCE_MONO;\n\tobs_source_set_flags(source, flags);\n}\n\nvoid OBSAdvAudioCtrl::downmixMonoChanged(bool val)\n{\n\tuint32_t flags = obs_source_get_flags(source);\n\tbool forceMonoActive = (flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0;\n\n\tif (forceMonoActive == val)\n\t\treturn;\n\n\tif (val)\n\t\tflags |= OBS_SOURCE_FLAG_FORCE_MONO;\n\telse\n\t\tflags &= ~OBS_SOURCE_FLAG_FORCE_MONO;\n\n\tobs_source_set_flags(source, flags);\n\n\tauto undo_redo = [](const std::string &uuid, bool val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tset_mono(source, val);\n\t};\n\n\tQString text = QTStr(val ? \"Undo.ForceMono.On\" : \"Undo.ForceMono.Off\");\n\n\tconst char *name = obs_source_get_name(source);\n\tconst char *uuid = obs_source_get_uuid(source);\n\tOBSBasic::Get()->undo_s.add_action(text.arg(name), std::bind(undo_redo, std::placeholders::_1, !val),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid);\n}\n\nvoid OBSAdvAudioCtrl::balanceChanged(int val)\n{\n\tfloat prev = obs_source_get_balance_value(source);\n\tfloat bal = (float)val / 100.0f;\n\n\tif (abs(50 - val) < 10) {\n\t\tbalance->blockSignals(true);\n\t\tbalance->setValue(50);\n\t\tbal = 0.5f;\n\t\tbalance->blockSignals(false);\n\t}\n\n\tobs_source_set_balance_value(source, bal);\n\n\tauto undo_redo = [](const std::string &uuid, float val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_source_set_balance_value(source, val);\n\t};\n\n\tconst char *name = obs_source_get_name(source);\n\tconst char *uuid = obs_source_get_uuid(source);\n\tOBSBasic::Get()->undo_s.add_action(QTStr(\"Undo.Balance.Change\").arg(name),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, prev),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, bal), uuid, uuid, true);\n}\n\nvoid OBSAdvAudioCtrl::ResetBalance()\n{\n\tbalance->setValue(50);\n}\n\nvoid OBSAdvAudioCtrl::syncOffsetChanged(int milliseconds)\n{\n\tint64_t prev = obs_source_get_sync_offset(source);\n\tint64_t val = int64_t(milliseconds) * NSEC_PER_MSEC;\n\n\tif (prev / NSEC_PER_MSEC == milliseconds)\n\t\treturn;\n\n\tobs_source_set_sync_offset(source, val);\n\n\tauto undo_redo = [](const std::string &uuid, int64_t val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_source_set_sync_offset(source, val);\n\t};\n\n\tconst char *name = obs_source_get_name(source);\n\tconst char *uuid = obs_source_get_uuid(source);\n\tOBSBasic::Get()->undo_s.add_action(QTStr(\"Undo.SyncOffset.Change\").arg(name),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, prev),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid, true);\n}\n\nvoid OBSAdvAudioCtrl::monitoringTypeChanged(int index)\n{\n\tobs_monitoring_type prev = obs_source_get_monitoring_type(source);\n\n\tobs_monitoring_type mt = (obs_monitoring_type)monitoringType->itemData(index).toInt();\n\tobs_source_set_monitoring_type(source, mt);\n\n\tconst char *type = nullptr;\n\n\tswitch (mt) {\n\tcase OBS_MONITORING_TYPE_NONE:\n\t\ttype = \"none\";\n\t\tbreak;\n\tcase OBS_MONITORING_TYPE_MONITOR_ONLY:\n\t\ttype = \"monitor only\";\n\t\tbreak;\n\tcase OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT:\n\t\ttype = \"monitor and output\";\n\t\tbreak;\n\t}\n\n\tconst char *name = obs_source_get_name(source);\n\tblog(LOG_INFO, \"User changed audio monitoring for source '%s' to: %s\", name ? name : \"(null)\", type);\n\n\tauto undo_redo = [](const std::string &uuid, obs_monitoring_type val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_source_set_monitoring_type(source, val);\n\t};\n\n\tconst char *uuid = obs_source_get_uuid(source);\n\tOBSBasic::Get()->undo_s.add_action(QTStr(\"Undo.MonitoringType.Change\").arg(name),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, prev),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, mt), uuid, uuid);\n}\n\nstatic inline void setMixer(obs_source_t *source, const int mixerIdx, const bool checked)\n{\n\tuint32_t mixers = obs_source_get_audio_mixers(source);\n\tuint32_t new_mixers = mixers;\n\n\tif (checked)\n\t\tnew_mixers |= (1 << mixerIdx);\n\telse\n\t\tnew_mixers &= ~(1 << mixerIdx);\n\n\tobs_source_set_audio_mixers(source, new_mixers);\n\n\tauto undo_redo = [](const std::string &uuid, uint32_t mixers) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_source_set_audio_mixers(source, mixers);\n\t};\n\n\tconst char *name = obs_source_get_name(source);\n\tconst char *uuid = obs_source_get_uuid(source);\n\tOBSBasic::Get()->undo_s.add_action(QTStr(\"Undo.Mixers.Change\").arg(name),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, mixers),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, new_mixers), uuid, uuid);\n}\n\nvoid OBSAdvAudioCtrl::SetVolumeWidget(VolumeType type)\n{\n\tswitch (type) {\n\tcase VolumeType::Percent:\n\t\tstackedWidget->setCurrentWidget(percent);\n\t\tbreak;\n\tcase VolumeType::dB:\n\t\tstackedWidget->setCurrentWidget(volume);\n\t\tbreak;\n\t}\n}\n\nvoid OBSAdvAudioCtrl::SetIconVisible(bool visible)\n{\n\tvisible ? iconLabel->show() : iconLabel->hide();\n}\n\nvoid OBSAdvAudioCtrl::SetSourceName(QString newName)\n{\n\tTruncateLabel(nameLabel, newName);\n}\n"
  },
  {
    "path": "frontend/components/OBSAdvAudioCtrl.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QObject>\n#include <QPointer>\n\nclass BalanceSlider;\nclass QCheckBox;\nclass QComboBox;\nclass QDoubleSpinBox;\nclass QGridLayout;\nclass QLabel;\nclass QSpinBox;\nclass QStackedWidget;\nclass QWidget;\n\nenum class VolumeType {\n\tdB,\n\tPercent,\n};\n\nclass OBSAdvAudioCtrl : public QObject {\n\tQ_OBJECT\n\nprivate:\n\tOBSSource source;\n\n\tQPointer<QWidget> mixerContainer;\n\tQPointer<QWidget> balanceContainer;\n\n\tQPointer<QLabel> iconLabel;\n\tQPointer<QLabel> nameLabel;\n\tQPointer<QLabel> active;\n\tQPointer<QStackedWidget> stackedWidget;\n\tQPointer<QSpinBox> percent;\n\tQPointer<QDoubleSpinBox> volume;\n\tQPointer<QCheckBox> forceMono;\n\tQPointer<BalanceSlider> balance;\n\tQPointer<QLabel> labelL;\n\tQPointer<QLabel> labelR;\n\tQPointer<QSpinBox> syncOffset;\n\tQPointer<QComboBox> monitoringType;\n\tQPointer<QCheckBox> mixer1;\n\tQPointer<QCheckBox> mixer2;\n\tQPointer<QCheckBox> mixer3;\n\tQPointer<QCheckBox> mixer4;\n\tQPointer<QCheckBox> mixer5;\n\tQPointer<QCheckBox> mixer6;\n\n\tstd::vector<OBSSignal> sigs;\n\n\tstatic void OBSSourceActivated(void *param, calldata_t *calldata);\n\tstatic void OBSSourceDeactivated(void *param, calldata_t *calldata);\n\tstatic void OBSSourceFlagsChanged(void *param, calldata_t *calldata);\n\tstatic void OBSSourceVolumeChanged(void *param, calldata_t *calldata);\n\tstatic void OBSSourceSyncChanged(void *param, calldata_t *calldata);\n\tstatic void OBSSourceMonitoringTypeChanged(void *param, calldata_t *calldata);\n\tstatic void OBSSourceMixersChanged(void *param, calldata_t *calldata);\n\tstatic void OBSSourceBalanceChanged(void *param, calldata_t *calldata);\n\tstatic void OBSSourceRenamed(void *param, calldata_t *calldata);\n\npublic:\n\tOBSAdvAudioCtrl(QGridLayout *layout, obs_source_t *source_);\n\tvirtual ~OBSAdvAudioCtrl();\n\n\tinline obs_source_t *GetSource() const { return source; }\n\tvoid ShowAudioControl(QGridLayout *layout);\n\n\tvoid SetVolumeWidget(VolumeType type);\n\tvoid SetIconVisible(bool visible);\n\npublic slots:\n\tvoid SourceActiveChanged(bool active);\n\tvoid SourceFlagsChanged(uint32_t flags);\n\tvoid SourceVolumeChanged(float volume);\n\tvoid SourceSyncChanged(int64_t offset);\n\tvoid SourceMonitoringTypeChanged(int type);\n\tvoid SourceMixersChanged(uint32_t mixers);\n\tvoid SourceBalanceChanged(int balance);\n\tvoid SetSourceName(QString newName);\n\n\tvoid volumeChanged(double db);\n\tvoid percentChanged(int percent);\n\tvoid downmixMonoChanged(bool checked);\n\tvoid balanceChanged(int val);\n\tvoid syncOffsetChanged(int milliseconds);\n\tvoid monitoringTypeChanged(int index);\n\tvoid ResetBalance();\n};\n"
  },
  {
    "path": "frontend/components/OBSPreviewScalingComboBox.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2024 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSPreviewScalingComboBox.hpp\"\n\n#include <OBSApp.hpp>\n\n#include \"moc_OBSPreviewScalingComboBox.cpp\"\n\nvoid OBSPreviewScalingComboBox::PreviewFixedScalingChanged(bool fixed)\n{\n\tif (fixedScaling == fixed)\n\t\treturn;\n\n\tfixedScaling = fixed;\n\tUpdateSelection();\n}\n\nvoid OBSPreviewScalingComboBox::CanvasResized(uint32_t width, uint32_t height)\n{\n\tSetCanvasSize(width, height);\n\tUpdateCanvasText();\n}\n\nvoid OBSPreviewScalingComboBox::OutputResized(uint32_t width, uint32_t height)\n{\n\tSetOutputSize(width, height);\n\n\tbool canvasMatchesOutput = output_width == canvas_width && output_height == canvas_height;\n\n\tSetScaleOutputEnabled(!canvasMatchesOutput);\n\tUpdateOutputText();\n}\n\nvoid OBSPreviewScalingComboBox::PreviewScaleChanged(float scale)\n{\n\tpreviewScale = scale;\n\n\tif (fixedScaling) {\n\t\tUpdateSelection();\n\t\tUpdateAllText();\n\t} else {\n\t\tUpdateScaledText();\n\t}\n}\n\nvoid OBSPreviewScalingComboBox::SetScaleOutputEnabled(bool show)\n{\n\tif (scaleOutputEnabled == show)\n\t\treturn;\n\n\tscaleOutputEnabled = show;\n\n\tif (scaleOutputEnabled) {\n\t\taddItem(QTStr(\"Basic.MainMenu.Edit.Scale.Output\"));\n\t} else {\n\t\tremoveItem(2);\n\t}\n}\n\nvoid OBSPreviewScalingComboBox::UpdateAllText()\n{\n\tUpdateCanvasText();\n\tUpdateOutputText();\n\tUpdateScaledText();\n}\n\nvoid OBSPreviewScalingComboBox::UpdateCanvasText()\n{\n\tQString text = QTStr(\"Basic.MainMenu.Edit.Scale.Canvas\");\n\ttext = text.arg(QString::number(canvas_width), QString::number(canvas_height));\n\tsetItemText(1, text);\n}\n\nvoid OBSPreviewScalingComboBox::UpdateOutputText()\n{\n\tif (scaleOutputEnabled) {\n\t\tQString text = QTStr(\"Basic.MainMenu.Edit.Scale.Output\");\n\t\ttext = text.arg(QString::number(output_width), QString::number(output_height));\n\t\tsetItemText(2, text);\n\t}\n}\n\nvoid OBSPreviewScalingComboBox::UpdateScaledText()\n{\n\tQString text = QTStr(\"Basic.MainMenu.Edit.Scale.Manual\");\n\ttext = text.arg(QString::number(floor(canvas_width * previewScale)),\n\t\t\tQString::number(floor(canvas_height * previewScale)));\n\tsetPlaceholderText(text);\n}\n\nvoid OBSPreviewScalingComboBox::UpdateSelection()\n{\n\tQSignalBlocker sb(this);\n\tfloat outputScale = float(output_width) / float(canvas_width);\n\n\tif (!fixedScaling) {\n\t\tsetCurrentIndex(0);\n\t} else {\n\t\tif (previewScale == 1.0f) {\n\t\t\tsetCurrentIndex(1);\n\t\t} else if (scaleOutputEnabled && (previewScale == outputScale)) {\n\t\t\tsetCurrentIndex(2);\n\t\t} else {\n\t\t\tsetCurrentIndex(-1);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "frontend/components/OBSPreviewScalingComboBox.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2024 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QComboBox>\n\nclass OBSPreviewScalingComboBox : public QComboBox {\n\tQ_OBJECT\n\npublic:\n\tOBSPreviewScalingComboBox(QWidget *parent = nullptr) : QComboBox(parent) {}\n\n\tinline void SetCanvasSize(uint32_t width, uint32_t height)\n\t{\n\t\tcanvas_width = width;\n\t\tcanvas_height = height;\n\t};\n\tinline void SetOutputSize(uint32_t width, uint32_t height)\n\t{\n\t\toutput_width = width;\n\t\toutput_height = height;\n\t};\n\tvoid UpdateAllText();\n\npublic slots:\n\tvoid PreviewScaleChanged(float scale);\n\tvoid PreviewFixedScalingChanged(bool fixed);\n\tvoid CanvasResized(uint32_t width, uint32_t height);\n\tvoid OutputResized(uint32_t width, uint32_t height);\n\nprivate:\n\tuint32_t canvas_width = 0;\n\tuint32_t canvas_height = 0;\n\n\tuint32_t output_width = 0;\n\tuint32_t output_height = 0;\n\n\tfloat previewScale = 0.0f;\n\n\tbool fixedScaling = false;\n\n\tbool scaleOutputEnabled = false;\n\tvoid SetScaleOutputEnabled(bool show);\n\n\tvoid UpdateCanvasText();\n\tvoid UpdateOutputText();\n\tvoid UpdateScaledText();\n\tvoid UpdateSelection();\n};\n"
  },
  {
    "path": "frontend/components/OBSPreviewScalingLabel.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2024 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSPreviewScalingLabel.hpp\"\n#include \"moc_OBSPreviewScalingLabel.cpp\"\n\nvoid OBSPreviewScalingLabel::PreviewScaleChanged(float scale)\n{\n\tpreviewScale = scale;\n\tUpdateScaleLabel();\n}\n\nvoid OBSPreviewScalingLabel::UpdateScaleLabel()\n{\n\tfloat previewScalePercent = floor(100.0f * previewScale);\n\tsetText(QString::number(previewScalePercent) + \"%\");\n}\n"
  },
  {
    "path": "frontend/components/OBSPreviewScalingLabel.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2024 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QLabel>\n\nclass OBSPreviewScalingLabel : public QLabel {\n\tQ_OBJECT\n\npublic:\n\tOBSPreviewScalingLabel(QWidget *parent = nullptr) : QLabel(parent) {}\n\npublic slots:\n\tvoid PreviewScaleChanged(float scale);\n\nprivate:\n\tfloat previewScale = 0.0f;\n\tvoid UpdateScaleLabel();\n};\n"
  },
  {
    "path": "frontend/components/OBSSourceLabel.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2015 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSSourceLabel.hpp\"\n#include \"moc_OBSSourceLabel.cpp\"\n\nOBSSourceLabel::OBSSourceLabel(const obs_source_t *source, QWidget *parent, Qt::WindowFlags f)\n\t: QLabel(obs_source_get_name(source), parent, f),\n\t  renamedSignal(obs_source_get_signal_handler(source), \"rename\", &OBSSourceLabel::obsSourceRenamed, this),\n\t  removedSignal(obs_source_get_signal_handler(source), \"remove\", &OBSSourceLabel::obsSourceRemoved, this),\n\t  destroyedSignal(obs_source_get_signal_handler(source), \"destroy\", &OBSSourceLabel::obsSourceDestroyed, this)\n{\n}\n\nvoid OBSSourceLabel::obsSourceRenamed(void *data, calldata_t *params)\n{\n\tauto &label = *static_cast<OBSSourceLabel *>(data);\n\n\tconst char *name = calldata_string(params, \"new_name\");\n\tlabel.setText(name);\n\n\temit label.renamed(name);\n}\n\nvoid OBSSourceLabel::obsSourceRemoved(void *data, calldata_t *)\n{\n\tauto &label = *static_cast<OBSSourceLabel *>(data);\n\temit label.removed();\n}\n\nvoid OBSSourceLabel::obsSourceDestroyed(void *data, calldata_t *)\n{\n\tauto &label = *static_cast<OBSSourceLabel *>(data);\n\temit label.destroyed();\n\n\tlabel.destroyedSignal.Disconnect();\n\tlabel.removedSignal.Disconnect();\n\tlabel.renamedSignal.Disconnect();\n}\n\nvoid OBSSourceLabel::mousePressEvent(QMouseEvent *event)\n{\n\temit clicked();\n\n\tQLabel::mousePressEvent(event);\n}\n"
  },
  {
    "path": "frontend/components/OBSSourceLabel.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2015 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <obs.hpp>\n\n#include <QLabel>\n\nclass OBSSourceLabel : public QLabel {\n\tQ_OBJECT;\n\npublic:\n\tOBSSignal renamedSignal;\n\tOBSSignal removedSignal;\n\tOBSSignal destroyedSignal;\n\n\tOBSSourceLabel(const obs_source_t *source, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());\n\nprotected:\n\tstatic void obsSourceRenamed(void *data, calldata_t *params);\n\tstatic void obsSourceRemoved(void *data, calldata_t *params);\n\tstatic void obsSourceDestroyed(void *data, calldata_t *params);\n\tvoid mousePressEvent(QMouseEvent *event);\n\nsignals:\n\tvoid renamed(const char *name);\n\tvoid removed();\n\tvoid destroyed();\n\tvoid clicked();\n};\n"
  },
  {
    "path": "frontend/components/SceneTree.cpp",
    "content": "#include \"SceneTree.hpp\"\n\n#include <QScrollBar>\n#include <QTimer>\n\n#include \"moc_SceneTree.cpp\"\n\nSceneTree::SceneTree(QWidget *parent_) : QListWidget(parent_)\n{\n\tinstallEventFilter(this);\n\tsetDragDropMode(InternalMove);\n\tsetMovement(QListView::Snap);\n}\n\nvoid SceneTree::SetGridMode(bool grid)\n{\n\tparent()->setProperty(\"class\", grid ? \"list-grid\" : \"\");\n\tgridMode = grid;\n\n\tif (gridMode) {\n\t\tsetResizeMode(QListView::Adjust);\n\t\tsetViewMode(QListView::IconMode);\n\t\tsetUniformItemSizes(true);\n\t\tsetStyleSheet(\"*{padding: 0; margin: 0;}\");\n\t} else {\n\t\tsetViewMode(QListView::ListMode);\n\t\tsetResizeMode(QListView::Fixed);\n\t\tsetStyleSheet(\"\");\n\t}\n\n\tQResizeEvent event(size(), size());\n\tresizeEvent(&event);\n}\n\nbool SceneTree::GetGridMode()\n{\n\treturn gridMode;\n}\n\nvoid SceneTree::SetGridItemWidth(int width)\n{\n\tmaxWidth = width;\n}\n\nvoid SceneTree::SetGridItemHeight(int height)\n{\n\titemHeight = height;\n}\n\nint SceneTree::GetGridItemWidth()\n{\n\treturn maxWidth;\n}\n\nint SceneTree::GetGridItemHeight()\n{\n\treturn itemHeight;\n}\n\nbool SceneTree::eventFilter(QObject *obj, QEvent *event)\n{\n\treturn QObject::eventFilter(obj, event);\n}\n\nvoid SceneTree::resizeEvent(QResizeEvent *event)\n{\n\tif (gridMode) {\n\t\tint scrollWid = verticalScrollBar()->sizeHint().width();\n\t\tconst QRect lastItem = visualItemRect(item(count() - 1));\n\t\tconst int h = lastItem.y() + lastItem.height();\n\n\t\tif (h < height()) {\n\t\t\tsetVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\n\t\t\tscrollWid = 0;\n\t\t} else {\n\t\t\tsetVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);\n\t\t}\n\n\t\tint wid = contentsRect().width() - scrollWid - 1;\n\t\tint items = (int)std::ceil((float)wid / maxWidth);\n\t\tint itemWidth = wid / items;\n\n\t\tsetGridSize(QSize(itemWidth, itemHeight));\n\n\t\tfor (int i = 0; i < count(); i++) {\n\t\t\titem(i)->setSizeHint(QSize(itemWidth, itemHeight));\n\t\t}\n\t} else {\n\t\tsetGridSize(QSize());\n\t\tfor (int i = 0; i < count(); i++) {\n\t\t\titem(i)->setData(Qt::SizeHintRole, QVariant());\n\t\t}\n\t}\n\n\tQListWidget::resizeEvent(event);\n}\n\nvoid SceneTree::startDrag(Qt::DropActions supportedActions)\n{\n\tQListWidget::startDrag(supportedActions);\n}\n\nvoid SceneTree::dropEvent(QDropEvent *event)\n{\n\tif (event->source() != this) {\n\t\tQListWidget::dropEvent(event);\n\t\treturn;\n\t}\n\n\tif (gridMode) {\n\t\tint scrollWid = verticalScrollBar()->sizeHint().width();\n\t\tconst QRect firstItem = visualItemRect(item(0));\n\t\tconst QRect lastItem = visualItemRect(item(count() - 1));\n\t\tconst int h = lastItem.y() + lastItem.height();\n\t\tconst int firstItemY = abs(firstItem.y());\n\n\t\tif (h < height()) {\n\t\t\tsetVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\n\t\t\tscrollWid = 0;\n\t\t} else {\n\t\t\tsetVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);\n\t\t}\n\n\t\tfloat wid = contentsRect().width() - scrollWid - 1;\n\n\t\tQPoint point = event->position().toPoint();\n\n\t\tint x = (float)point.x() / wid * std::ceil(wid / maxWidth);\n\t\tint y = (point.y() + firstItemY) / itemHeight;\n\n\t\tint r = x + y * std::ceil(wid / maxWidth);\n\n\t\tQListWidgetItem *item = takeItem(selectedIndexes()[0].row());\n\t\tinsertItem(r, item);\n\t\tsetCurrentItem(item);\n\t\tresize(size());\n\t}\n\n\tQListWidget::dropEvent(event);\n\n\t// We must call resizeEvent to correctly place all grid items.\n\t// We also do this in rowsInserted.\n\tQResizeEvent resEvent(size(), size());\n\tSceneTree::resizeEvent(&resEvent);\n\n\tQTimer::singleShot(100, [this]() { emit scenesReordered(); });\n}\n\nvoid SceneTree::RepositionGrid(QDragMoveEvent *event)\n{\n\tint scrollWid = verticalScrollBar()->sizeHint().width();\n\tconst QRect firstItem = visualItemRect(item(0));\n\tconst QRect lastItem = visualItemRect(item(count() - 1));\n\tconst int h = lastItem.y() + lastItem.height();\n\tconst int firstItemY = abs(firstItem.y());\n\n\tif (h < height()) {\n\t\tsetVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\n\t\tscrollWid = 0;\n\t} else {\n\t\tsetVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);\n\t}\n\n\tfloat wid = contentsRect().width() - scrollWid - 1;\n\n\tif (event) {\n\t\tQPoint point = event->position().toPoint();\n\n\t\tint x = (float)point.x() / wid * std::ceil(wid / maxWidth);\n\t\tint y = (point.y() + firstItemY) / itemHeight;\n\n\t\tint r = x + y * std::ceil(wid / maxWidth);\n\t\tint orig = selectedIndexes()[0].row();\n\n\t\tfor (int i = 0; i < count(); i++) {\n\t\t\tauto *wItem = item(i);\n\n\t\t\tif (wItem->isSelected())\n\t\t\t\tcontinue;\n\n\t\t\tQModelIndex index = indexFromItem(wItem);\n\n\t\t\tint off = (i >= r ? 1 : 0) - (i > orig && i > r ? 1 : 0) - (i > orig && i == r ? 2 : 0);\n\n\t\t\tint xPos = (i + off) % (int)std::ceil(wid / maxWidth);\n\t\t\tint yPos = (i + off) / (int)std::ceil(wid / maxWidth);\n\t\t\tQSize g = gridSize();\n\n\t\t\tQPoint position(xPos * g.width(), yPos * g.height());\n\t\t\tsetPositionForIndex(position, index);\n\t\t}\n\t} else {\n\t\tfor (int i = 0; i < count(); i++) {\n\t\t\tauto *wItem = item(i);\n\n\t\t\tif (wItem->isSelected())\n\t\t\t\tcontinue;\n\n\t\t\tQModelIndex index = indexFromItem(wItem);\n\n\t\t\tint xPos = i % (int)std::ceil(wid / maxWidth);\n\t\t\tint yPos = i / (int)std::ceil(wid / maxWidth);\n\t\t\tQSize g = gridSize();\n\n\t\t\tQPoint position(xPos * g.width(), yPos * g.height());\n\t\t\tsetPositionForIndex(position, index);\n\t\t}\n\t}\n}\n\nvoid SceneTree::dragMoveEvent(QDragMoveEvent *event)\n{\n\tif (gridMode) {\n\t\tRepositionGrid(event);\n\t}\n\n\tQListWidget::dragMoveEvent(event);\n}\n\nvoid SceneTree::dragLeaveEvent(QDragLeaveEvent *event)\n{\n\tif (gridMode) {\n\t\tRepositionGrid();\n\t}\n\n\tQListWidget::dragLeaveEvent(event);\n}\n\nvoid SceneTree::rowsInserted(const QModelIndex &parent, int start, int end)\n{\n\tQResizeEvent event(size(), size());\n\tSceneTree::resizeEvent(&event);\n\n\tQListWidget::rowsInserted(parent, start, end);\n}\n\n#if QT_VERSION < QT_VERSION_CHECK(6, 4, 3)\n// Workaround for QTBUG-105870. Remove once that is solved upstream.\nvoid SceneTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)\n{\n\tif (selected.count() == 0 && deselected.count() > 0 && !property(\"clearing\").toBool())\n\t\tsetCurrentRow(deselected.indexes().front().row());\n}\n#endif\n"
  },
  {
    "path": "frontend/components/SceneTree.hpp",
    "content": "#pragma once\n\n#include <QListWidget>\n#include <QObject>\n#include <QResizeEvent>\n#include <QWidget>\n\nclass SceneTree : public QListWidget {\n\tQ_OBJECT\n\tQ_PROPERTY(int gridItemWidth READ GetGridItemWidth WRITE SetGridItemWidth DESIGNABLE true)\n\tQ_PROPERTY(int gridItemHeight READ GetGridItemHeight WRITE SetGridItemHeight DESIGNABLE true)\n\n\tbool gridMode = false;\n\tint maxWidth = 150;\n\tint itemHeight = 24;\n\npublic:\n\tvoid SetGridMode(bool grid);\n\tbool GetGridMode();\n\n\tvoid SetGridItemWidth(int width);\n\tvoid SetGridItemHeight(int height);\n\tint GetGridItemWidth();\n\tint GetGridItemHeight();\n\n\texplicit SceneTree(QWidget *parent = nullptr);\n\nprivate:\n\tvoid RepositionGrid(QDragMoveEvent *event = nullptr);\n\nprotected:\n\tvirtual bool eventFilter(QObject *obj, QEvent *event) override;\n\tvirtual void resizeEvent(QResizeEvent *event) override;\n\tvirtual void startDrag(Qt::DropActions supportedActions) override;\n\tvirtual void dropEvent(QDropEvent *event) override;\n\tvirtual void dragMoveEvent(QDragMoveEvent *event) override;\n\tvirtual void dragLeaveEvent(QDragLeaveEvent *event) override;\n\tvirtual void rowsInserted(const QModelIndex &parent, int start, int end) override;\n#if QT_VERSION < QT_VERSION_CHECK(6, 4, 3)\n\tvirtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;\n#endif\n\nsignals:\n\tvoid scenesReordered();\n};\n"
  },
  {
    "path": "frontend/components/SilentUpdateCheckBox.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QCheckBox>\n\nclass SilentUpdateCheckBox : public QCheckBox {\n\tQ_OBJECT\n\npublic slots:\n\tvoid setCheckedSilently(bool checked)\n\t{\n\t\tbool blocked = blockSignals(true);\n\t\tsetChecked(checked);\n\t\tblockSignals(blocked);\n\t}\n};\n"
  },
  {
    "path": "frontend/components/SilentUpdateSpinBox.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QSpinBox>\n\nclass SilentUpdateSpinBox : public QSpinBox {\n\tQ_OBJECT\n\npublic slots:\n\tvoid setValueSilently(int val)\n\t{\n\t\tbool blocked = blockSignals(true);\n\t\tsetValue(val);\n\t\tblockSignals(blocked);\n\t}\n};\n"
  },
  {
    "path": "frontend/components/SourceToolbar.cpp",
    "content": "#include \"SourceToolbar.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include \"moc_SourceToolbar.cpp\"\n\nSourceToolbar::SourceToolbar(QWidget *parent, OBSSource source)\n\t: QWidget(parent),\n\t  weakSource(OBSGetWeakRef(source)),\n\t  props(obs_source_properties(source), obs_properties_destroy)\n{\n}\n\nvoid SourceToolbar::SaveOldProperties(obs_source_t *source)\n{\n\toldData = obs_data_create();\n\n\tOBSDataAutoRelease oldSettings = obs_source_get_settings(source);\n\tobs_data_apply(oldData, oldSettings);\n\tobs_data_set_string(oldData, \"undo_suuid\", obs_source_get_uuid(source));\n}\n\nvoid SourceToolbar::SetUndoProperties(obs_source_t *source, bool repeatable)\n{\n\tif (!oldData) {\n\t\tblog(LOG_ERROR, \"%s: somehow oldData was null.\", __FUNCTION__);\n\t\treturn;\n\t}\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tOBSSource currentSceneSource = main->GetCurrentSceneSource();\n\tif (!currentSceneSource)\n\t\treturn;\n\tstd::string scene_uuid = obs_source_get_uuid(currentSceneSource);\n\tauto undo_redo = [scene_uuid = std::move(scene_uuid), main](const std::string &data) {\n\t\tOBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(settings, \"undo_suuid\"));\n\t\tobs_source_reset_settings(source, settings);\n\n\t\tOBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());\n\t\tmain->SetCurrentScene(scene_source.Get(), true);\n\n\t\tmain->UpdateContextBar();\n\t};\n\n\tOBSDataAutoRelease new_settings = obs_data_create();\n\tOBSDataAutoRelease curr_settings = obs_source_get_settings(source);\n\tobs_data_apply(new_settings, curr_settings);\n\tobs_data_set_string(new_settings, \"undo_suuid\", obs_source_get_uuid(source));\n\n\tstd::string undo_data(obs_data_get_json(oldData));\n\tstd::string redo_data(obs_data_get_json(new_settings));\n\n\tif (undo_data.compare(redo_data) != 0)\n\t\tmain->undo_s.add_action(QTStr(\"Undo.Properties\").arg(obs_source_get_name(source)), undo_redo, undo_redo,\n\t\t\t\t\tundo_data, redo_data, repeatable);\n\n\toldData = nullptr;\n}\n"
  },
  {
    "path": "frontend/components/SourceToolbar.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QWidget>\n\nclass SourceToolbar : public QWidget {\n\tQ_OBJECT\n\n\tOBSWeakSource weakSource;\n\nprotected:\n\tusing properties_delete_t = decltype(&obs_properties_destroy);\n\tusing properties_t = std::unique_ptr<obs_properties_t, properties_delete_t>;\n\n\tproperties_t props;\n\tOBSDataAutoRelease oldData;\n\n\tvoid SaveOldProperties(obs_source_t *source);\n\tvoid SetUndoProperties(obs_source_t *source, bool repeatable = false);\n\npublic:\n\tSourceToolbar(QWidget *parent, OBSSource source);\n\n\tOBSSource GetSource() { return OBSGetStrongRef(weakSource); }\n\npublic slots:\n\tvirtual void Update() {}\n};\n"
  },
  {
    "path": "frontend/components/SourceTree.cpp",
    "content": "#include \"SourceTree.hpp\"\n#include \"SourceTreeDelegate.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include <QPainter>\n\n#include \"moc_SourceTree.cpp\"\n\nstatic inline OBSScene GetCurrentScene()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\treturn main->GetCurrentScene();\n}\n\n/* moves a scene item index (blame linux distros for using older Qt builds) */\nstatic inline void MoveItem(QVector<OBSSceneItem> &items, int oldIdx, int newIdx)\n{\n\tOBSSceneItem item = items[oldIdx];\n\titems.remove(oldIdx);\n\titems.insert(newIdx, item);\n}\n\nSourceTree::SourceTree(QWidget *parent_) : QListView(parent_)\n{\n\tSourceTreeModel *stm_ = new SourceTreeModel(this);\n\tsetModel(stm_);\n\tsetStyleSheet(QString(\"*[bgColor=\\\"1\\\"]{background-color:rgba(255,68,68,33%);}\"\n\t\t\t      \"*[bgColor=\\\"2\\\"]{background-color:rgba(255,255,68,33%);}\"\n\t\t\t      \"*[bgColor=\\\"3\\\"]{background-color:rgba(68,255,68,33%);}\"\n\t\t\t      \"*[bgColor=\\\"4\\\"]{background-color:rgba(68,255,255,33%);}\"\n\t\t\t      \"*[bgColor=\\\"5\\\"]{background-color:rgba(68,68,255,33%);}\"\n\t\t\t      \"*[bgColor=\\\"6\\\"]{background-color:rgba(255,68,255,33%);}\"\n\t\t\t      \"*[bgColor=\\\"7\\\"]{background-color:rgba(68,68,68,33%);}\"\n\t\t\t      \"*[bgColor=\\\"8\\\"]{background-color:rgba(255,255,255,33%);}\"));\n\n\tUpdateNoSourcesMessage();\n\tconnect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateNoSourcesMessage);\n\tconnect(App(), &OBSApp::StyleChanged, this, &SourceTree::UpdateIcons);\n\n\tsetItemDelegate(new SourceTreeDelegate(this));\n}\n\nvoid SourceTree::UpdateIcons()\n{\n\tSourceTreeModel *stm = GetStm();\n\tstm->SceneChanged();\n}\n\nvoid SourceTree::SetIconsVisible(bool visible)\n{\n\tSourceTreeModel *stm = GetStm();\n\n\ticonsVisible = visible;\n\tstm->SceneChanged();\n}\n\nvoid SourceTree::ResetWidgets()\n{\n\tOBSScene scene = GetCurrentScene();\n\n\tSourceTreeModel *stm = GetStm();\n\tstm->UpdateGroupState(false);\n\n\tfor (int i = 0; i < stm->items.count(); i++) {\n\t\tQModelIndex index = stm->createIndex(i, 0, nullptr);\n\t\tsetIndexWidget(index, new SourceTreeItem(this, stm->items[i]));\n\t}\n}\n\nvoid SourceTree::UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item)\n{\n\tsetIndexWidget(idx, new SourceTreeItem(this, item));\n}\n\nvoid SourceTree::UpdateWidgets(bool force)\n{\n\tSourceTreeModel *stm = GetStm();\n\n\tfor (int i = 0; i < stm->items.size(); i++) {\n\t\tobs_sceneitem_t *item = stm->items[i];\n\t\tSourceTreeItem *widget = GetItemWidget(i);\n\n\t\tif (!widget) {\n\t\t\tUpdateWidget(stm->createIndex(i, 0), item);\n\t\t} else {\n\t\t\twidget->Update(force);\n\t\t}\n\t}\n}\n\nvoid SourceTree::SelectItem(obs_sceneitem_t *sceneitem, bool select)\n{\n\tSourceTreeModel *stm = GetStm();\n\tint i = 0;\n\n\tfor (; i < stm->items.count(); i++) {\n\t\tif (stm->items[i] == sceneitem)\n\t\t\tbreak;\n\t}\n\n\tif (i == stm->items.count())\n\t\treturn;\n\n\tQModelIndex index = stm->createIndex(i, 0);\n\tif (index.isValid() && select != selectionModel()->isSelected(index))\n\t\tselectionModel()->select(index, select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);\n}\n\nvoid SourceTree::mouseDoubleClickEvent(QMouseEvent *event)\n{\n\tif (event->button() == Qt::LeftButton)\n\t\tQListView::mouseDoubleClickEvent(event);\n}\n\nvoid SourceTree::dropEvent(QDropEvent *event)\n{\n\tif (event->source() != this) {\n\t\tQListView::dropEvent(event);\n\t\treturn;\n\t}\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tOBSScene scene = GetCurrentScene();\n\tobs_source_t *scenesource = obs_scene_get_source(scene);\n\tSourceTreeModel *stm = GetStm();\n\tauto &items = stm->items;\n\tQModelIndexList indices = selectedIndexes();\n\n\tDropIndicatorPosition indicator = dropIndicatorPosition();\n\tint row = indexAt(event->position().toPoint()).row();\n\tbool emptyDrop = row == -1;\n\n\tif (emptyDrop) {\n\t\tif (!items.size()) {\n\t\t\tQListView::dropEvent(event);\n\t\t\treturn;\n\t\t}\n\n\t\trow = items.size() - 1;\n\t\tindicator = QAbstractItemView::BelowItem;\n\t}\n\n\t/* --------------------------------------- */\n\t/* store destination group if moving to a  */\n\t/* group                                   */\n\n\tobs_sceneitem_t *dropItem = items[row]; /* item being dropped on */\n\tbool itemIsGroup = obs_sceneitem_is_group(dropItem);\n\n\tobs_sceneitem_t *dropGroup = itemIsGroup ? dropItem : obs_sceneitem_get_group(scene, dropItem);\n\n\t/* not a group if moving above the group */\n\tif (indicator == QAbstractItemView::AboveItem && itemIsGroup)\n\t\tdropGroup = nullptr;\n\tif (emptyDrop)\n\t\tdropGroup = nullptr;\n\n\t/* --------------------------------------- */\n\t/* remember to remove list items if        */\n\t/* dropping on collapsed group             */\n\n\tbool dropOnCollapsed = false;\n\tif (dropGroup) {\n\t\tobs_data_t *data = obs_sceneitem_get_private_settings(dropGroup);\n\t\tdropOnCollapsed = obs_data_get_bool(data, \"collapsed\");\n\t\tobs_data_release(data);\n\t}\n\n\tif (indicator == QAbstractItemView::BelowItem || indicator == QAbstractItemView::OnItem ||\n\t    indicator == QAbstractItemView::OnViewport)\n\t\trow++;\n\n\tif (row < 0 || row > stm->items.count()) {\n\t\tQListView::dropEvent(event);\n\t\treturn;\n\t}\n\n\t/* --------------------------------------- */\n\t/* determine if any base group is selected */\n\n\tbool hasGroups = false;\n\tfor (int i = 0; i < indices.size(); i++) {\n\t\tobs_sceneitem_t *item = items[indices[i].row()];\n\t\tif (obs_sceneitem_is_group(item)) {\n\t\t\thasGroups = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/* --------------------------------------- */\n\t/* if dropping a group, detect if it's     */\n\t/* below another group                     */\n\n\tobs_sceneitem_t *itemBelow;\n\tif (row == stm->items.count())\n\t\titemBelow = nullptr;\n\telse\n\t\titemBelow = stm->items[row];\n\n\tif (hasGroups) {\n\t\tif (!itemBelow || obs_sceneitem_get_group(scene, itemBelow) != dropGroup) {\n\t\t\tdropGroup = nullptr;\n\t\t\tdropOnCollapsed = false;\n\t\t}\n\t}\n\n\t/* --------------------------------------- */\n\t/* if dropping groups on other groups,     */\n\t/* disregard as invalid drag/drop          */\n\n\tif (dropGroup && hasGroups) {\n\t\tQListView::dropEvent(event);\n\t\treturn;\n\t}\n\n\t/* --------------------------------------- */\n\t/* save undo data                          */\n\tstd::vector<obs_source_t *> sources;\n\tfor (int i = 0; i < indices.size(); i++) {\n\t\tobs_sceneitem_t *item = items[indices[i].row()];\n\t\tif (obs_sceneitem_get_scene(item) != scene)\n\t\t\tsources.push_back(obs_scene_get_source(obs_sceneitem_get_scene(item)));\n\t}\n\tif (dropGroup)\n\t\tsources.push_back(obs_sceneitem_get_source(dropGroup));\n\tOBSData undo_data = main->BackupScene(scene, &sources);\n\n\t/* --------------------------------------- */\n\t/* if selection includes base group items, */\n\t/* include all group sub-items and treat   */\n\t/* them all as one                         */\n\n\tif (hasGroups) {\n\t\t/* remove sub-items if selected */\n\t\tfor (int i = indices.size() - 1; i >= 0; i--) {\n\t\t\tobs_sceneitem_t *item = items[indices[i].row()];\n\t\t\tobs_scene_t *itemScene = obs_sceneitem_get_scene(item);\n\n\t\t\tif (itemScene != scene) {\n\t\t\t\tindices.removeAt(i);\n\t\t\t}\n\t\t}\n\n\t\t/* add all sub-items of selected groups */\n\t\tfor (int i = indices.size() - 1; i >= 0; i--) {\n\t\t\tobs_sceneitem_t *item = items[indices[i].row()];\n\n\t\t\tif (obs_sceneitem_is_group(item)) {\n\t\t\t\tfor (int j = items.size() - 1; j >= 0; j--) {\n\t\t\t\t\tobs_sceneitem_t *subitem = items[j];\n\t\t\t\t\tobs_sceneitem_t *subitemGroup = obs_sceneitem_get_group(scene, subitem);\n\n\t\t\t\t\tif (subitemGroup == item) {\n\t\t\t\t\t\tQModelIndex idx = stm->createIndex(j, 0);\n\t\t\t\t\t\tindices.insert(i + 1, idx);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/* --------------------------------------- */\n\t/* build persistent indices                */\n\n\tQList<QPersistentModelIndex> persistentIndices;\n\tpersistentIndices.reserve(indices.count());\n\tfor (QModelIndex &index : indices)\n\t\tpersistentIndices.append(index);\n\tstd::sort(persistentIndices.begin(), persistentIndices.end());\n\n\t/* --------------------------------------- */\n\t/* move all items to destination index     */\n\n\tint r = row;\n\tfor (auto &persistentIdx : persistentIndices) {\n\t\tint from = persistentIdx.row();\n\t\tint to = r;\n\t\tint itemTo = to;\n\n\t\tif (itemTo > from)\n\t\t\titemTo--;\n\n\t\tif (itemTo != from) {\n\t\t\tstm->beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);\n\t\t\tMoveItem(items, from, itemTo);\n\t\t\tstm->endMoveRows();\n\t\t}\n\n\t\tr = persistentIdx.row() + 1;\n\t}\n\n\tstd::sort(persistentIndices.begin(), persistentIndices.end());\n\tint firstIdx = persistentIndices.front().row();\n\tint lastIdx = persistentIndices.back().row();\n\n\t/* --------------------------------------- */\n\t/* reorder scene items in back-end         */\n\n\tQVector<struct obs_sceneitem_order_info> orderList;\n\tobs_sceneitem_t *lastGroup = nullptr;\n\tint insertCollapsedIdx = 0;\n\n\tauto insertCollapsed = [&](obs_sceneitem_t *item) {\n\t\tstruct obs_sceneitem_order_info info;\n\t\tinfo.group = lastGroup;\n\t\tinfo.item = item;\n\n\t\torderList.insert(insertCollapsedIdx++, info);\n\t};\n\n\tusing insertCollapsed_t = decltype(insertCollapsed);\n\n\tauto preInsertCollapsed = [](obs_scene_t *, obs_sceneitem_t *item, void *param) {\n\t\t(*static_cast<insertCollapsed_t *>(param))(item);\n\t\treturn true;\n\t};\n\n\tauto insertLastGroup = [&]() {\n\t\tOBSDataAutoRelease data = obs_sceneitem_get_private_settings(lastGroup);\n\t\tbool collapsed = obs_data_get_bool(data, \"collapsed\");\n\n\t\tif (collapsed) {\n\t\t\tinsertCollapsedIdx = 0;\n\t\t\tobs_sceneitem_group_enum_items(lastGroup, preInsertCollapsed, &insertCollapsed);\n\t\t}\n\n\t\tstruct obs_sceneitem_order_info info;\n\t\tinfo.group = nullptr;\n\t\tinfo.item = lastGroup;\n\t\torderList.insert(0, info);\n\t};\n\n\tauto updateScene = [&]() {\n\t\tstruct obs_sceneitem_order_info info;\n\n\t\tfor (int i = 0; i < items.size(); i++) {\n\t\t\tobs_sceneitem_t *item = items[i];\n\t\t\tobs_sceneitem_t *group;\n\n\t\t\tif (obs_sceneitem_is_group(item)) {\n\t\t\t\tif (lastGroup) {\n\t\t\t\t\tinsertLastGroup();\n\t\t\t\t}\n\t\t\t\tlastGroup = item;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!hasGroups && i >= firstIdx && i <= lastIdx)\n\t\t\t\tgroup = dropGroup;\n\t\t\telse\n\t\t\t\tgroup = obs_sceneitem_get_group(scene, item);\n\n\t\t\tif (lastGroup && lastGroup != group) {\n\t\t\t\tinsertLastGroup();\n\t\t\t}\n\n\t\t\tlastGroup = group;\n\n\t\t\tinfo.group = group;\n\t\t\tinfo.item = item;\n\t\t\torderList.insert(0, info);\n\t\t}\n\n\t\tif (lastGroup) {\n\t\t\tinsertLastGroup();\n\t\t}\n\n\t\tobs_scene_reorder_items2(scene, orderList.data(), orderList.size());\n\t};\n\n\tusing updateScene_t = decltype(updateScene);\n\n\tauto preUpdateScene = [](void *data, obs_scene_t *) {\n\t\t(*static_cast<updateScene_t *>(data))();\n\t};\n\n\tignoreReorder = true;\n\tobs_scene_atomic_update(scene, preUpdateScene, &updateScene);\n\tignoreReorder = false;\n\n\t/* --------------------------------------- */\n\t/* save redo data                          */\n\n\tOBSData redo_data = main->BackupScene(scene, &sources);\n\n\t/* --------------------------------------- */\n\t/* add undo/redo action                    */\n\n\tconst char *scene_name = obs_source_get_name(scenesource);\n\tQString action_name = QTStr(\"Undo.ReorderSources\").arg(scene_name);\n\tmain->CreateSceneUndoRedoAction(action_name, undo_data, redo_data);\n\n\t/* --------------------------------------- */\n\t/* remove items if dropped in to collapsed */\n\t/* group                                   */\n\n\tif (dropOnCollapsed) {\n\t\tstm->beginRemoveRows(QModelIndex(), firstIdx, lastIdx);\n\t\titems.remove(firstIdx, lastIdx - firstIdx + 1);\n\t\tstm->endRemoveRows();\n\t}\n\n\t/* --------------------------------------- */\n\t/* update widgets and accept event         */\n\n\tUpdateWidgets(true);\n\n\tevent->accept();\n\tevent->setDropAction(Qt::CopyAction);\n\n\tQListView::dropEvent(event);\n}\n\nvoid SourceTree::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)\n{\n\t{\n\t\tQSignalBlocker sourcesSignalBlocker(this);\n\t\tSourceTreeModel *stm = GetStm();\n\n\t\tQModelIndexList selectedIdxs = selected.indexes();\n\t\tQModelIndexList deselectedIdxs = deselected.indexes();\n\n\t\tfor (int i = 0; i < selectedIdxs.count(); i++) {\n\t\t\tint idx = selectedIdxs[i].row();\n\t\t\tobs_sceneitem_select(stm->items[idx], true);\n\t\t}\n\n\t\tfor (int i = 0; i < deselectedIdxs.count(); i++) {\n\t\t\tint idx = deselectedIdxs[i].row();\n\t\t\tobs_sceneitem_select(stm->items[idx], false);\n\t\t}\n\t}\n\tQListView::selectionChanged(selected, deselected);\n}\n\nvoid SourceTree::NewGroupEdit(int row)\n{\n\tif (!Edit(row)) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tmain->undo_s.pop_disabled();\n\n\t\tblog(LOG_WARNING, \"Uh, somehow the edit didn't process, this \"\n\t\t\t\t  \"code should never be reached.\\nAnd by \"\n\t\t\t\t  \"\\\"never be reached\\\", I mean that \"\n\t\t\t\t  \"theoretically, it should be\\nimpossible \"\n\t\t\t\t  \"for this code to be reached. But if this \"\n\t\t\t\t  \"code is reached,\\nfeel free to laugh at \"\n\t\t\t\t  \"Lain, because apparently it is, in fact, \"\n\t\t\t\t  \"actually\\npossible for this code to be \"\n\t\t\t\t  \"reached. But I mean, again, theoretically\\n\"\n\t\t\t\t  \"it should be impossible. So if you see \"\n\t\t\t\t  \"this in your log, just know that\\nit's \"\n\t\t\t\t  \"really dumb, and depressing. But at least \"\n\t\t\t\t  \"the undo/redo action is\\nstill covered, so \"\n\t\t\t\t  \"in theory things *should* be fine. But \"\n\t\t\t\t  \"it's entirely\\npossible that they might \"\n\t\t\t\t  \"not be exactly. But again, yea. This \"\n\t\t\t\t  \"really\\nshould not be possible.\");\n\n\t\tOBSData redoSceneData = main->BackupScene(GetCurrentScene());\n\n\t\tQString text = QTStr(\"Undo.GroupItems\").arg(\"Unknown\");\n\t\tmain->CreateSceneUndoRedoAction(text, undoSceneData, redoSceneData);\n\n\t\tundoSceneData = nullptr;\n\t}\n}\n\nbool SourceTree::Edit(int row)\n{\n\tSourceTreeModel *stm = GetStm();\n\tif (row < 0 || row >= stm->items.count())\n\t\treturn false;\n\n\tQModelIndex index = stm->createIndex(row, 0);\n\tQWidget *widget = indexWidget(index);\n\tSourceTreeItem *itemWidget = reinterpret_cast<SourceTreeItem *>(widget);\n\tif (itemWidget->IsEditing()) {\n#ifdef __APPLE__\n\t\titemWidget->ExitEditMode(true);\n#endif\n\t\treturn false;\n\t}\n\n\titemWidget->EnterEditMode();\n\tedit(index);\n\treturn true;\n}\n\nbool SourceTree::MultipleBaseSelected() const\n{\n\tSourceTreeModel *stm = GetStm();\n\tQModelIndexList selectedIndices = selectedIndexes();\n\n\tOBSScene scene = GetCurrentScene();\n\n\tif (selectedIndices.size() < 1) {\n\t\treturn false;\n\t}\n\n\tfor (auto &idx : selectedIndices) {\n\t\tobs_sceneitem_t *item = stm->items[idx.row()];\n\t\tif (obs_sceneitem_is_group(item)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tobs_scene *itemScene = obs_sceneitem_get_scene(item);\n\t\tif (itemScene != scene) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool SourceTree::GroupsSelected() const\n{\n\tSourceTreeModel *stm = GetStm();\n\tQModelIndexList selectedIndices = selectedIndexes();\n\n\tOBSScene scene = GetCurrentScene();\n\n\tif (selectedIndices.size() < 1) {\n\t\treturn false;\n\t}\n\n\tfor (auto &idx : selectedIndices) {\n\t\tobs_sceneitem_t *item = stm->items[idx.row()];\n\t\tif (!obs_sceneitem_is_group(item)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool SourceTree::GroupedItemsSelected() const\n{\n\tSourceTreeModel *stm = GetStm();\n\tQModelIndexList selectedIndices = selectedIndexes();\n\tOBSScene scene = GetCurrentScene();\n\n\tif (!selectedIndices.size()) {\n\t\treturn false;\n\t}\n\n\tfor (auto &idx : selectedIndices) {\n\t\tobs_sceneitem_t *item = stm->items[idx.row()];\n\t\tobs_scene *itemScene = obs_sceneitem_get_scene(item);\n\n\t\tif (itemScene != scene) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid SourceTree::Remove(OBSSceneItem item, OBSScene scene)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tGetStm()->Remove(item);\n\tmain->SaveProject();\n\n\tif (!main->SavingDisabled()) {\n\t\tobs_source_t *sceneSource = obs_scene_get_source(scene);\n\t\tobs_source_t *itemSource = obs_sceneitem_get_source(item);\n\t\tblog(LOG_INFO, \"User Removed source '%s' (%s) from scene '%s'\", obs_source_get_name(itemSource),\n\t\t     obs_source_get_id(itemSource), obs_source_get_name(sceneSource));\n\t}\n}\n\nvoid SourceTree::GroupSelectedItems()\n{\n\tQModelIndexList indices = selectedIndexes();\n\tstd::sort(indices.begin(), indices.end());\n\tGetStm()->GroupSelectedItems(indices);\n}\n\nvoid SourceTree::UngroupSelectedGroups()\n{\n\tQModelIndexList indices = selectedIndexes();\n\tGetStm()->UngroupSelectedGroups(indices);\n}\n\nvoid SourceTree::AddGroup()\n{\n\tGetStm()->AddGroup();\n}\n\nvoid SourceTree::UpdateNoSourcesMessage()\n{\n\tQString file = !App()->IsThemeDark() ? \":res/images/no_sources.svg\" : \"theme:Dark/no_sources.svg\";\n\ticonNoSources.load(file);\n\n\tQTextOption opt(Qt::AlignHCenter);\n\topt.setWrapMode(QTextOption::WordWrap);\n\ttextNoSources.setTextOption(opt);\n\ttextNoSources.setText(QTStr(\"NoSources.Label\").replace(\"\\n\", \"<br/>\"));\n\n\ttextPrepared = false;\n}\n\nvoid SourceTree::paintEvent(QPaintEvent *event)\n{\n\tSourceTreeModel *stm = GetStm();\n\tif (stm && !stm->items.count()) {\n\t\tQPainter p(viewport());\n\n\t\tif (!textPrepared) {\n\t\t\ttextNoSources.prepare(QTransform(), p.font());\n\t\t\ttextPrepared = true;\n\t\t}\n\n\t\tQRectF iconRect = iconNoSources.viewBoxF();\n\t\ticonRect.setSize(QSizeF(32.0, 32.0));\n\n\t\tQSizeF iconSize = iconRect.size();\n\t\tQSizeF textSize = textNoSources.size();\n\t\tQSizeF thisSize = size();\n\t\tconst qreal spacing = 16.0;\n\n\t\tqreal totalHeight = iconSize.height() + spacing + textSize.height();\n\n\t\tqreal x = thisSize.width() / 2.0 - iconSize.width() / 2.0;\n\t\tqreal y = thisSize.height() / 2.0 - totalHeight / 2.0;\n\t\ticonRect.moveTo(std::round(x), std::round(y));\n\t\ticonNoSources.render(&p, iconRect);\n\n\t\tx = thisSize.width() / 2.0 - textSize.width() / 2.0;\n\t\ty += spacing + iconSize.height();\n\t\tp.drawStaticText(x, y, textNoSources);\n\t} else {\n\t\tQListView::paintEvent(event);\n\t}\n}\n"
  },
  {
    "path": "frontend/components/SourceTree.hpp",
    "content": "#pragma once\n\n#include \"SourceTreeItem.hpp\"\n#include \"SourceTreeModel.hpp\"\n\n#include <QListView>\n#include <QStaticText>\n#include <QSvgRenderer>\n\nclass SourceTree : public QListView {\n\tQ_OBJECT\n\n\tbool ignoreReorder = false;\n\n\tfriend class SourceTreeModel;\n\tfriend class SourceTreeItem;\n\n\tbool textPrepared = false;\n\tQStaticText textNoSources;\n\tQSvgRenderer iconNoSources;\n\n\tOBSData undoSceneData;\n\n\tbool iconsVisible = true;\n\n\tvoid UpdateNoSourcesMessage();\n\n\tvoid ResetWidgets();\n\tvoid UpdateWidget(const QModelIndex &idx, obs_sceneitem_t *item);\n\tvoid UpdateWidgets(bool force = false);\n\n\tinline SourceTreeModel *GetStm() const { return reinterpret_cast<SourceTreeModel *>(model()); }\n\npublic:\n\tinline SourceTreeItem *GetItemWidget(int idx)\n\t{\n\t\tQWidget *widget = indexWidget(GetStm()->createIndex(idx, 0));\n\t\treturn reinterpret_cast<SourceTreeItem *>(widget);\n\t}\n\n\texplicit SourceTree(QWidget *parent = nullptr);\n\n\tinline bool IgnoreReorder() const { return ignoreReorder; }\n\tinline void Clear() { GetStm()->Clear(); }\n\n\tinline void Add(obs_sceneitem_t *item) { GetStm()->Add(item); }\n\tinline OBSSceneItem Get(int idx) { return GetStm()->Get(idx); }\n\tinline QString GetNewGroupName() { return GetStm()->GetNewGroupName(); }\n\n\tvoid SelectItem(obs_sceneitem_t *sceneitem, bool select);\n\n\tbool MultipleBaseSelected() const;\n\tbool GroupsSelected() const;\n\tbool GroupedItemsSelected() const;\n\n\tvoid UpdateIcons();\n\tvoid SetIconsVisible(bool visible);\n\npublic slots:\n\tinline void ReorderItems() { GetStm()->ReorderItems(); }\n\tinline void RefreshItems() { GetStm()->SceneChanged(); }\n\tvoid Remove(OBSSceneItem item, OBSScene scene);\n\tvoid GroupSelectedItems();\n\tvoid UngroupSelectedGroups();\n\tvoid AddGroup();\n\tbool Edit(int idx);\n\tvoid NewGroupEdit(int idx);\n\nprotected:\n\tvirtual void mouseDoubleClickEvent(QMouseEvent *event) override;\n\tvirtual void dropEvent(QDropEvent *event) override;\n\tvirtual void paintEvent(QPaintEvent *event) override;\n\n\tvirtual void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) override;\n};\n"
  },
  {
    "path": "frontend/components/SourceTreeDelegate.cpp",
    "content": "#include \"SourceTree.hpp\"\n#include \"SourceTreeDelegate.hpp\"\n#include \"moc_SourceTreeDelegate.cpp\"\n\nSourceTreeDelegate::SourceTreeDelegate(QObject *parent) : QStyledItemDelegate(parent) {}\n\nQSize SourceTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const\n{\n\tSourceTree *tree = qobject_cast<SourceTree *>(parent());\n\tQWidget *item = tree->indexWidget(index);\n\n\tif (!item)\n\t\treturn QStyledItemDelegate::sizeHint(option, index);\n\n\treturn (QSize(item->sizeHint()));\n}\n"
  },
  {
    "path": "frontend/components/SourceTreeDelegate.hpp",
    "content": "#pragma once\n\n#include <QModelIndex>\n#include <QObject>\n#include <QSize>\n#include <QStyledItemDelegate>\n#include <QStyleOptionViewItem>\n\nclass SourceTreeDelegate : public QStyledItemDelegate {\n\tQ_OBJECT\n\npublic:\n\tSourceTreeDelegate(QObject *parent);\n\tvirtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;\n};\n"
  },
  {
    "path": "frontend/components/SourceTreeItem.cpp",
    "content": "#include \"SourceTreeItem.hpp\"\n\n#include <components/OBSSourceLabel.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QCheckBox>\n#include <QLineEdit>\n#include <QPainter>\n\n#include \"plugin-manager/PluginManager.hpp\"\n\n#ifdef _WIN32\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#endif\n\n#include \"moc_SourceTreeItem.cpp\"\n\nstatic inline OBSScene GetCurrentScene()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\treturn main->GetCurrentScene();\n}\n\nSourceTreeItem::SourceTreeItem(SourceTree *tree_, OBSSceneItem sceneitem_) : tree(tree_), sceneitem(sceneitem_)\n{\n\tsetAttribute(Qt::WA_TranslucentBackground);\n\tsetMouseTracking(true);\n\n\tobs_source_t *source = obs_sceneitem_get_source(sceneitem);\n\tconst char *name = obs_source_get_name(source);\n\n\tOBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneitem);\n\tint preset = obs_data_get_int(privData, \"color-preset\");\n\n\tif (preset == 1) {\n\t\tconst char *color = obs_data_get_string(privData, \"color\");\n\t\tstd::string col = \"background: \";\n\t\tcol += color;\n\t\tsetStyleSheet(col.c_str());\n\t} else if (preset > 1) {\n\t\tsetStyleSheet(\"\");\n\t\tsetProperty(\"bgColor\", preset - 1);\n\t} else {\n\t\tsetStyleSheet(\"background: none\");\n\t}\n\n\tOBSBasic *main = OBSBasic::Get();\n\tconst char *id = obs_source_get_id(source);\n\n\tbool sourceVisible = obs_sceneitem_visible(sceneitem);\n\n\tif (tree->iconsVisible) {\n\t\tQIcon icon;\n\n\t\tif (strcmp(id, \"scene\") == 0)\n\t\t\ticon = main->GetSceneIcon();\n\t\telse if (strcmp(id, \"group\") == 0)\n\t\t\ticon = main->GetGroupIcon();\n\t\telse\n\t\t\ticon = main->GetSourceIcon(id);\n\n\t\tQPixmap pixmap = icon.pixmap(QSize(16, 16));\n\n\t\ticonLabel = new QLabel();\n\t\ticonLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);\n\t\ticonLabel->setPixmap(pixmap);\n\t\ticonLabel->setEnabled(sourceVisible);\n\t\ticonLabel->setStyleSheet(\"background: none\");\n\t\ticonLabel->setProperty(\"class\", \"source-icon\");\n\t}\n\n\tvis = new QCheckBox();\n\tvis->setProperty(\"class\", \"checkbox-icon indicator-visibility\");\n\tvis->setChecked(sourceVisible);\n\tvis->setAccessibleName(QTStr(\"Basic.Main.Sources.Visibility\"));\n\tvis->setAccessibleDescription(QTStr(\"Basic.Main.Sources.VisibilityDescription\").arg(name));\n\tvis->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);\n\n\tlock = new QCheckBox();\n\tlock->setProperty(\"class\", \"checkbox-icon indicator-lock\");\n\tlock->setChecked(obs_sceneitem_locked(sceneitem));\n\tlock->setAccessibleName(QTStr(\"Basic.Main.Sources.Lock\"));\n\tlock->setAccessibleDescription(QTStr(\"Basic.Main.Sources.LockDescription\").arg(name));\n\tlock->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);\n\n\tlabel = new OBSSourceLabel(source);\n\tlabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);\n\tlabel->setAttribute(Qt::WA_TranslucentBackground);\n\tlabel->setEnabled(sourceVisible);\n\n\tconst char *sourceId = obs_source_get_unversioned_id(source);\n\tswitch (obs_source_load_state(sourceId)) {\n\tcase OBS_MODULE_DISABLED:\n\tcase OBS_MODULE_MISSING:\n\t\tlabel->setStyleSheet(\"QLabel {color: #CC0000;}\");\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n#ifdef __APPLE__\n\tvis->setAttribute(Qt::WA_LayoutUsesWidgetRect);\n\tlock->setAttribute(Qt::WA_LayoutUsesWidgetRect);\n#endif\n\n\tboxLayout = new QHBoxLayout();\n\n\tboxLayout->setContentsMargins(0, 0, 0, 0);\n\tboxLayout->setSpacing(0);\n\tif (iconLabel) {\n\t\tboxLayout->addWidget(iconLabel);\n\t\tboxLayout->addSpacing(2);\n\t}\n\tboxLayout->addWidget(label);\n\tboxLayout->addWidget(vis);\n\tboxLayout->addWidget(lock);\n\n\tUpdate(false);\n\n\tsetLayout(boxLayout);\n\n\t/* --------------------------------------------------------- */\n\n\tauto setItemVisible = [this](bool val) {\n\t\tobs_scene_t *scene = obs_sceneitem_get_scene(sceneitem);\n\t\tobs_source_t *scenesource = obs_scene_get_source(scene);\n\t\tint64_t id = obs_sceneitem_get_id(sceneitem);\n\t\tconst char *name = obs_source_get_name(scenesource);\n\t\tconst char *uuid = obs_source_get_uuid(scenesource);\n\t\tobs_source_t *source = obs_sceneitem_get_source(sceneitem);\n\n\t\tauto undo_redo = [](const std::string &uuid, int64_t id, bool val) {\n\t\t\tOBSSourceAutoRelease s = obs_get_source_by_uuid(uuid.c_str());\n\t\t\tobs_scene_t *sc = obs_group_or_scene_from_source(s);\n\t\t\tobs_sceneitem_t *si = obs_scene_find_sceneitem_by_id(sc, id);\n\t\t\tif (si)\n\t\t\t\tobs_sceneitem_set_visible(si, val);\n\t\t};\n\n\t\tQString str = QTStr(val ? \"Undo.ShowSceneItem\" : \"Undo.HideSceneItem\");\n\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tmain->undo_s.add_action(str.arg(obs_source_get_name(source), name),\n\t\t\t\t\tstd::bind(undo_redo, std::placeholders::_1, id, !val),\n\t\t\t\t\tstd::bind(undo_redo, std::placeholders::_1, id, val), uuid, uuid);\n\n\t\tQSignalBlocker sourcesSignalBlocker(this);\n\t\tobs_sceneitem_set_visible(sceneitem, val);\n\t};\n\n\tauto setItemLocked = [this](bool checked) {\n\t\tQSignalBlocker sourcesSignalBlocker(this);\n\t\tobs_sceneitem_set_locked(sceneitem, checked);\n\t};\n\n\tconnect(vis, &QAbstractButton::clicked, this, setItemVisible);\n\tconnect(lock, &QAbstractButton::clicked, this, setItemLocked);\n}\n\nvoid SourceTreeItem::paintEvent(QPaintEvent *event)\n{\n\tQStyleOption opt;\n\topt.initFrom(this);\n\tQPainter p(this);\n\tstyle()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);\n\n\tQWidget::paintEvent(event);\n}\n\nvoid SourceTreeItem::DisconnectSignals()\n{\n\tsigs.clear();\n}\n\nvoid SourceTreeItem::Clear()\n{\n\tDisconnectSignals();\n\tsceneitem = nullptr;\n}\n\nvoid SourceTreeItem::ReconnectSignals()\n{\n\tif (!sceneitem)\n\t\treturn;\n\n\tDisconnectSignals();\n\n\t/* --------------------------------------------------------- */\n\n\tauto removeItem = [](void *data, calldata_t *cd) {\n\t\tSourceTreeItem *this_ = static_cast<SourceTreeItem *>(data);\n\t\tobs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, \"item\");\n\t\tobs_scene_t *curScene = (obs_scene_t *)calldata_ptr(cd, \"scene\");\n\n\t\tif (curItem == this_->sceneitem) {\n\t\t\tQMetaObject::invokeMethod(this_->tree, \"Remove\", Q_ARG(OBSSceneItem, curItem),\n\t\t\t\t\t\t  Q_ARG(OBSScene, curScene));\n\t\t\tcurItem = nullptr;\n\t\t}\n\t\tif (!curItem)\n\t\t\tQMetaObject::invokeMethod(this_, \"Clear\");\n\t};\n\n\tauto itemVisible = [](void *data, calldata_t *cd) {\n\t\tSourceTreeItem *this_ = static_cast<SourceTreeItem *>(data);\n\t\tobs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, \"item\");\n\t\tbool visible = calldata_bool(cd, \"visible\");\n\n\t\tif (curItem == this_->sceneitem)\n\t\t\tQMetaObject::invokeMethod(this_, \"VisibilityChanged\", Q_ARG(bool, visible));\n\t};\n\n\tauto itemLocked = [](void *data, calldata_t *cd) {\n\t\tSourceTreeItem *this_ = static_cast<SourceTreeItem *>(data);\n\t\tobs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, \"item\");\n\t\tbool locked = calldata_bool(cd, \"locked\");\n\n\t\tif (curItem == this_->sceneitem)\n\t\t\tQMetaObject::invokeMethod(this_, \"LockedChanged\", Q_ARG(bool, locked));\n\t};\n\n\tauto itemSelect = [](void *data, calldata_t *cd) {\n\t\tSourceTreeItem *this_ = static_cast<SourceTreeItem *>(data);\n\t\tobs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, \"item\");\n\n\t\tif (curItem == this_->sceneitem)\n\t\t\tQMetaObject::invokeMethod(this_, \"Select\");\n\t};\n\n\tauto itemDeselect = [](void *data, calldata_t *cd) {\n\t\tSourceTreeItem *this_ = static_cast<SourceTreeItem *>(data);\n\t\tobs_sceneitem_t *curItem = (obs_sceneitem_t *)calldata_ptr(cd, \"item\");\n\n\t\tif (curItem == this_->sceneitem)\n\t\t\tQMetaObject::invokeMethod(this_, \"Deselect\");\n\t};\n\n\tauto reorderGroup = [](void *data, calldata_t *) {\n\t\tSourceTreeItem *this_ = static_cast<SourceTreeItem *>(data);\n\t\tQMetaObject::invokeMethod(this_->tree, \"ReorderItems\");\n\t};\n\n\tobs_scene_t *scene = obs_sceneitem_get_scene(sceneitem);\n\tobs_source_t *sceneSource = obs_scene_get_source(scene);\n\tsignal_handler_t *signal = obs_source_get_signal_handler(sceneSource);\n\n\tsigs.emplace_back(signal, \"remove\", removeItem, this);\n\tsigs.emplace_back(signal, \"item_remove\", removeItem, this);\n\tsigs.emplace_back(signal, \"item_visible\", itemVisible, this);\n\tsigs.emplace_back(signal, \"item_locked\", itemLocked, this);\n\tsigs.emplace_back(signal, \"item_select\", itemSelect, this);\n\tsigs.emplace_back(signal, \"item_deselect\", itemDeselect, this);\n\n\tif (obs_sceneitem_is_group(sceneitem)) {\n\t\tobs_source_t *source = obs_sceneitem_get_source(sceneitem);\n\t\tsignal = obs_source_get_signal_handler(source);\n\n\t\tsigs.emplace_back(signal, \"reorder\", reorderGroup, this);\n\t}\n\n\t/* --------------------------------------------------------- */\n\n\tauto removeSource = [](void *data, calldata_t *) {\n\t\tSourceTreeItem *this_ = static_cast<SourceTreeItem *>(data);\n\t\tthis_->DisconnectSignals();\n\t\tthis_->sceneitem = nullptr;\n\t\tQMetaObject::invokeMethod(this_->tree, \"RefreshItems\");\n\t};\n\n\tobs_source_t *source = obs_sceneitem_get_source(sceneitem);\n\tsignal = obs_source_get_signal_handler(source);\n\tsigs.emplace_back(signal, \"remove\", removeSource, this);\n}\n\nvoid SourceTreeItem::mouseDoubleClickEvent(QMouseEvent *event)\n{\n\tQWidget::mouseDoubleClickEvent(event);\n\n\tif (expand) {\n\t\texpand->setChecked(!expand->isChecked());\n\t} else {\n\t\tobs_source_t *source = obs_sceneitem_get_source(sceneitem);\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tif (obs_source_configurable(source)) {\n#if defined(_WIN32)\n\t\t\t/* This timer works around a bug introduced around Qt 6.8.3 that causes\n\t\t\t * the application to hang when double clicking the sources list and the\n\t\t\t * Windows setting 'Snap mouse to default button in dialog boxes' is enabled.\n\t\t\t */\n\t\t\tBOOL snapEnabled = FALSE;\n\t\t\tSystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0, &snapEnabled, 0);\n\n\t\t\tif (snapEnabled) {\n\t\t\t\tQTimer::singleShot(200, this, [=]() { main->CreatePropertiesWindow(source); });\n\t\t\t} else {\n\t\t\t\tmain->CreatePropertiesWindow(source);\n\t\t\t}\n#else\n\t\t\tmain->CreatePropertiesWindow(source);\n#endif\n\t\t}\n\t}\n}\n\nvoid SourceTreeItem::enterEvent(QEnterEvent *event)\n{\n\tQWidget::enterEvent(event);\n\n\tOBSBasicPreview *preview = OBSBasicPreview::Get();\n\n\tstd::lock_guard<std::mutex> lock(preview->selectMutex);\n\tpreview->hoveredPreviewItems.clear();\n\tpreview->hoveredPreviewItems.push_back(sceneitem);\n}\n\nvoid SourceTreeItem::leaveEvent(QEvent *event)\n{\n\tQWidget::leaveEvent(event);\n\n\tOBSBasicPreview *preview = OBSBasicPreview::Get();\n\n\tstd::lock_guard<std::mutex> lock(preview->selectMutex);\n\tpreview->hoveredPreviewItems.clear();\n}\n\nbool SourceTreeItem::IsEditing()\n{\n\treturn editor != nullptr;\n}\n\nvoid SourceTreeItem::EnterEditMode()\n{\n\tsetFocusPolicy(Qt::StrongFocus);\n\tint index = boxLayout->indexOf(label);\n\tboxLayout->removeWidget(label);\n\teditor = new QLineEdit(label->text());\n\teditor->setStyleSheet(\"background: none\");\n\teditor->selectAll();\n\teditor->installEventFilter(this);\n\tboxLayout->insertWidget(index, editor);\n\tsetFocusProxy(editor);\n}\n\nvoid SourceTreeItem::ExitEditMode(bool save)\n{\n\tExitEditModeInternal(save);\n\n\tif (tree->undoSceneData) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tmain->undo_s.pop_disabled();\n\n\t\tOBSData redoSceneData = main->BackupScene(GetCurrentScene());\n\n\t\tQString text = QTStr(\"Undo.GroupItems\").arg(newName.c_str());\n\t\tmain->CreateSceneUndoRedoAction(text, tree->undoSceneData, redoSceneData);\n\n\t\ttree->undoSceneData = nullptr;\n\t}\n}\n\nvoid SourceTreeItem::ExitEditModeInternal(bool save)\n{\n\tif (!editor) {\n\t\treturn;\n\t}\n\n\tOBSBasic *main = OBSBasic::Get();\n\tOBSScene scene = main->GetCurrentScene();\n\n\tnewName = QT_TO_UTF8(editor->text());\n\n\tsetFocusProxy(nullptr);\n\tint index = boxLayout->indexOf(editor);\n\tboxLayout->removeWidget(editor);\n\tdelete editor;\n\teditor = nullptr;\n\tsetFocusPolicy(Qt::NoFocus);\n\tboxLayout->insertWidget(index, label);\n\tsetFocus();\n\n\t/* ----------------------------------------- */\n\t/* check for empty string                    */\n\n\tif (!save)\n\t\treturn;\n\n\tif (newName.empty()) {\n\t\tOBSMessageBox::information(main, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\treturn;\n\t}\n\n\t/* ----------------------------------------- */\n\t/* Check for same name                       */\n\n\tobs_source_t *source = obs_sceneitem_get_source(sceneitem);\n\tif (newName == obs_source_get_name(source))\n\t\treturn;\n\n\t/* ----------------------------------------- */\n\t/* check for existing source                 */\n\n\tOBSSourceAutoRelease existingSource = obs_get_source_by_name(newName.c_str());\n\tbool exists = !!existingSource;\n\n\tif (exists) {\n\t\tOBSMessageBox::information(main, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\t\treturn;\n\t}\n\n\t/* ----------------------------------------- */\n\t/* rename                                    */\n\n\tQSignalBlocker sourcesSignalBlocker(this);\n\tstd::string prevName(obs_source_get_name(source));\n\tstd::string scene_uuid = obs_source_get_uuid(main->GetCurrentSceneSource());\n\tauto undo = [scene_uuid, prevName, main](const std::string &data) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());\n\t\tobs_source_set_name(source, prevName.c_str());\n\n\t\tOBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());\n\t\tmain->SetCurrentScene(scene_source.Get(), true);\n\t};\n\n\tstd::string editedName = newName;\n\n\tauto redo = [scene_uuid, main, editedName](const std::string &data) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());\n\t\tobs_source_set_name(source, editedName.c_str());\n\n\t\tOBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());\n\t\tmain->SetCurrentScene(scene_source.Get(), true);\n\t};\n\n\tconst char *uuid = obs_source_get_uuid(source);\n\tmain->undo_s.add_action(QTStr(\"Undo.Rename\").arg(newName.c_str()), undo, redo, uuid, uuid);\n\n\tobs_source_set_name(source, newName.c_str());\n}\n\nbool SourceTreeItem::eventFilter(QObject *object, QEvent *event)\n{\n\tif (editor != object)\n\t\treturn false;\n\n\tif (LineEditCanceled(event)) {\n\t\tQMetaObject::invokeMethod(this, \"ExitEditMode\", Qt::QueuedConnection, Q_ARG(bool, false));\n\t\treturn true;\n\t}\n\tif (LineEditChanged(event)) {\n\t\tQMetaObject::invokeMethod(this, \"ExitEditMode\", Qt::QueuedConnection, Q_ARG(bool, true));\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid SourceTreeItem::VisibilityChanged(bool visible)\n{\n\tif (iconLabel) {\n\t\ticonLabel->setEnabled(visible);\n\t}\n\tlabel->setEnabled(visible);\n\tvis->setChecked(visible);\n}\n\nvoid SourceTreeItem::LockedChanged(bool locked)\n{\n\tlock->setChecked(locked);\n\tOBSBasic::Get()->UpdateEditMenu();\n}\n\nvoid SourceTreeItem::Update(bool force)\n{\n\tOBSScene scene = GetCurrentScene();\n\tobs_scene_t *itemScene = obs_sceneitem_get_scene(sceneitem);\n\n\tType newType;\n\n\t/* ------------------------------------------------- */\n\t/* if it's a group item, insert group checkbox       */\n\n\tif (obs_sceneitem_is_group(sceneitem)) {\n\t\tnewType = Type::Group;\n\n\t\t/* ------------------------------------------------- */\n\t\t/* if it's a group sub-item                          */\n\n\t} else if (itemScene != scene) {\n\t\tnewType = Type::SubItem;\n\n\t\t/* ------------------------------------------------- */\n\t\t/* if it's a regular item                            */\n\n\t} else {\n\t\tnewType = Type::Item;\n\t}\n\n\t/* ------------------------------------------------- */\n\n\tif (!force && newType == type) {\n\t\treturn;\n\t}\n\n\t/* ------------------------------------------------- */\n\n\tReconnectSignals();\n\n\tif (spacer) {\n\t\tboxLayout->removeItem(spacer);\n\t\tdelete spacer;\n\t\tspacer = nullptr;\n\t}\n\n\tif (type == Type::Group) {\n\t\tboxLayout->removeWidget(expand);\n\t\texpand->deleteLater();\n\t\texpand = nullptr;\n\t}\n\n\ttype = newType;\n\n\tif (type == Type::SubItem) {\n\t\tspacer = new QSpacerItem(16, 1);\n\t\tboxLayout->insertItem(0, spacer);\n\n\t} else if (type == Type::Group) {\n\t\texpand = new QCheckBox();\n\t\texpand->setProperty(\"class\", \"checkbox-icon indicator-expand\");\n\t\texpand->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);\n#ifdef __APPLE__\n\t\texpand->setAttribute(Qt::WA_LayoutUsesWidgetRect);\n#endif\n\t\tboxLayout->insertWidget(0, expand);\n\n\t\tOBSDataAutoRelease data = obs_sceneitem_get_private_settings(sceneitem);\n\t\texpand->blockSignals(true);\n\t\texpand->setChecked(obs_data_get_bool(data, \"collapsed\"));\n\t\texpand->blockSignals(false);\n\n\t\tconnect(expand, &QPushButton::toggled, this, &SourceTreeItem::ExpandClicked);\n\n\t} else {\n\t\tspacer = new QSpacerItem(3, 1);\n\t\tboxLayout->insertItem(0, spacer);\n\t}\n}\n\nvoid SourceTreeItem::ExpandClicked(bool checked)\n{\n\tOBSDataAutoRelease data = obs_sceneitem_get_private_settings(sceneitem);\n\n\tobs_data_set_bool(data, \"collapsed\", checked);\n\n\tif (!checked)\n\t\ttree->GetStm()->ExpandGroup(sceneitem);\n\telse\n\t\ttree->GetStm()->CollapseGroup(sceneitem);\n}\n\nvoid SourceTreeItem::Select()\n{\n\ttree->SelectItem(sceneitem, true);\n\tOBSBasic::Get()->UpdateContextBarDeferred();\n\tOBSBasic::Get()->UpdateEditMenu();\n}\n\nvoid SourceTreeItem::Deselect()\n{\n\ttree->SelectItem(sceneitem, false);\n\tOBSBasic::Get()->UpdateContextBarDeferred();\n\tOBSBasic::Get()->UpdateEditMenu();\n}\n"
  },
  {
    "path": "frontend/components/SourceTreeItem.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QFrame>\n\nclass QSpacerItem;\nclass QCheckBox;\nclass QLabel;\nclass QHBoxLayout;\nclass OBSSourceLabel;\nclass QLineEdit;\n\nclass SourceTree;\n\nclass SourceTreeItem : public QFrame {\n\tQ_OBJECT\n\n\tfriend class SourceTree;\n\tfriend class SourceTreeModel;\n\n\tvoid mouseDoubleClickEvent(QMouseEvent *event) override;\n\tvoid enterEvent(QEnterEvent *event) override;\n\tvoid leaveEvent(QEvent *event) override;\n\n\tvirtual bool eventFilter(QObject *object, QEvent *event) override;\n\n\tvoid Update(bool force);\n\n\tenum class Type {\n\t\tUnknown,\n\t\tItem,\n\t\tGroup,\n\t\tSubItem,\n\t};\n\n\tvoid DisconnectSignals();\n\tvoid ReconnectSignals();\n\n\tType type = Type::Unknown;\n\npublic:\n\texplicit SourceTreeItem(SourceTree *tree, OBSSceneItem sceneitem);\n\tbool IsEditing();\n\nprivate:\n\tQSpacerItem *spacer = nullptr;\n\tQCheckBox *expand = nullptr;\n\tQLabel *iconLabel = nullptr;\n\tQCheckBox *vis = nullptr;\n\tQCheckBox *lock = nullptr;\n\tQHBoxLayout *boxLayout = nullptr;\n\tOBSSourceLabel *label = nullptr;\n\n\tQLineEdit *editor = nullptr;\n\n\tstd::string newName;\n\n\tSourceTree *tree;\n\tOBSSceneItem sceneitem;\n\tstd::vector<OBSSignal> sigs;\n\n\tvirtual void paintEvent(QPaintEvent *event) override;\n\n\tvoid ExitEditModeInternal(bool save);\n\nprivate slots:\n\tvoid Clear();\n\n\tvoid EnterEditMode();\n\tvoid ExitEditMode(bool save);\n\n\tvoid VisibilityChanged(bool visible);\n\tvoid LockedChanged(bool locked);\n\n\tvoid ExpandClicked(bool checked);\n\n\tvoid Select();\n\tvoid Deselect();\n};\n"
  },
  {
    "path": "frontend/components/SourceTreeModel.cpp",
    "content": "#include \"SourceTreeModel.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include \"moc_SourceTreeModel.cpp\"\n\nstatic inline OBSScene GetCurrentScene()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\treturn main->GetCurrentScene();\n}\n\nvoid SourceTreeModel::OBSFrontendEvent(enum obs_frontend_event event, void *ptr)\n{\n\tSourceTreeModel *stm = static_cast<SourceTreeModel *>(ptr);\n\n\tswitch (event) {\n\tcase OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:\n\t\tstm->SceneChanged();\n\t\tbreak;\n\tcase OBS_FRONTEND_EVENT_EXIT:\n\t\tstm->Clear();\n\t\tobs_frontend_remove_event_callback(OBSFrontendEvent, stm);\n\t\tbreak;\n\tcase OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP:\n\t\tstm->Clear();\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid SourceTreeModel::Clear()\n{\n\tbeginResetModel();\n\titems.clear();\n\tendResetModel();\n\n\thasGroups = false;\n}\n\nstatic bool enumItem(obs_scene_t *, obs_sceneitem_t *item, void *ptr)\n{\n\tQVector<OBSSceneItem> &items = *static_cast<QVector<OBSSceneItem> *>(ptr);\n\n\tobs_source_t *src = obs_sceneitem_get_source(item);\n\tif (obs_source_removed(src)) {\n\t\treturn true;\n\t}\n\n\tif (obs_sceneitem_is_group(item)) {\n\t\tOBSDataAutoRelease data = obs_sceneitem_get_private_settings(item);\n\n\t\tbool collapse = obs_data_get_bool(data, \"collapsed\");\n\t\tif (!collapse) {\n\t\t\tobs_scene_t *scene = obs_sceneitem_group_get_scene(item);\n\n\t\t\tobs_scene_enum_items(scene, enumItem, &items);\n\t\t}\n\t}\n\n\titems.insert(0, item);\n\treturn true;\n}\n\nvoid SourceTreeModel::SceneChanged()\n{\n\tOBSScene scene = GetCurrentScene();\n\n\tbeginResetModel();\n\titems.clear();\n\tobs_scene_enum_items(scene, enumItem, &items);\n\tendResetModel();\n\n\tUpdateGroupState(false);\n\tst->ResetWidgets();\n\n\tfor (int i = 0; i < items.count(); i++) {\n\t\tbool select = obs_sceneitem_selected(items[i]);\n\t\tQModelIndex index = createIndex(i, 0);\n\n\t\tst->selectionModel()->select(index,\n\t\t\t\t\t     select ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);\n\t}\n}\n\n/* moves a scene item index (blame linux distros for using older Qt builds) */\nstatic inline void MoveItem(QVector<OBSSceneItem> &items, int oldIdx, int newIdx)\n{\n\tOBSSceneItem item = items[oldIdx];\n\titems.remove(oldIdx);\n\titems.insert(newIdx, item);\n}\n\n/* reorders list optimally with model reorder funcs */\nvoid SourceTreeModel::ReorderItems()\n{\n\tOBSScene scene = GetCurrentScene();\n\n\tQVector<OBSSceneItem> newitems;\n\tobs_scene_enum_items(scene, enumItem, &newitems);\n\n\t/* if item list has changed size, do full reset */\n\tif (newitems.count() != items.count()) {\n\t\tSceneChanged();\n\t\treturn;\n\t}\n\n\tfor (;;) {\n\t\tint idx1Old = 0;\n\t\tint idx1New = 0;\n\t\tint count;\n\t\tint i;\n\n\t\t/* find first starting changed item index */\n\t\tfor (i = 0; i < newitems.count(); i++) {\n\t\t\tobs_sceneitem_t *oldItem = items[i];\n\t\t\tobs_sceneitem_t *newItem = newitems[i];\n\t\t\tif (oldItem != newItem) {\n\t\t\t\tidx1Old = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/* if everything is the same, break */\n\t\tif (i == newitems.count()) {\n\t\t\tbreak;\n\t\t}\n\n\t\t/* find new starting index */\n\t\tfor (i = idx1Old + 1; i < newitems.count(); i++) {\n\t\t\tobs_sceneitem_t *oldItem = items[idx1Old];\n\t\t\tobs_sceneitem_t *newItem = newitems[i];\n\n\t\t\tif (oldItem == newItem) {\n\t\t\t\tidx1New = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/* if item could not be found, do full reset */\n\t\tif (i == newitems.count()) {\n\t\t\tSceneChanged();\n\t\t\treturn;\n\t\t}\n\n\t\t/* get move count */\n\t\tfor (count = 1; (idx1New + count) < newitems.count(); count++) {\n\t\t\tint oldIdx = idx1Old + count;\n\t\t\tint newIdx = idx1New + count;\n\n\t\t\tobs_sceneitem_t *oldItem = items[oldIdx];\n\t\t\tobs_sceneitem_t *newItem = newitems[newIdx];\n\n\t\t\tif (oldItem != newItem) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/* move items */\n\t\tbeginMoveRows(QModelIndex(), idx1Old, idx1Old + count - 1, QModelIndex(), idx1New + count);\n\t\tfor (i = 0; i < count; i++) {\n\t\t\tint to = idx1New + count;\n\t\t\tif (to > idx1Old)\n\t\t\t\tto--;\n\t\t\tMoveItem(items, idx1Old, to);\n\t\t}\n\t\tendMoveRows();\n\t}\n}\n\nvoid SourceTreeModel::Add(obs_sceneitem_t *item)\n{\n\tif (obs_sceneitem_is_group(item)) {\n\t\tSceneChanged();\n\t} else {\n\t\tbeginInsertRows(QModelIndex(), 0, 0);\n\t\titems.insert(0, item);\n\t\tendInsertRows();\n\n\t\tst->UpdateWidget(createIndex(0, 0, nullptr), item);\n\t}\n}\n\nvoid SourceTreeModel::Remove(obs_sceneitem_t *item)\n{\n\tint idx = -1;\n\tfor (int i = 0; i < items.count(); i++) {\n\t\tif (items[i] == item) {\n\t\t\tidx = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (idx == -1)\n\t\treturn;\n\n\tint startIdx = idx;\n\tint endIdx = idx;\n\n\tbool is_group = obs_sceneitem_is_group(item);\n\tif (is_group) {\n\t\tobs_scene_t *scene = obs_sceneitem_group_get_scene(item);\n\n\t\tfor (int i = endIdx + 1; i < items.count(); i++) {\n\t\t\tobs_sceneitem_t *subitem = items[i];\n\t\t\tobs_scene_t *subscene = obs_sceneitem_get_scene(subitem);\n\n\t\t\tif (subscene == scene)\n\t\t\t\tendIdx = i;\n\t\t\telse\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tbeginRemoveRows(QModelIndex(), startIdx, endIdx);\n\titems.remove(idx, endIdx - startIdx + 1);\n\tendRemoveRows();\n\n\tif (is_group)\n\t\tUpdateGroupState(true);\n\n\tOBSBasic::Get()->UpdateContextBarDeferred();\n}\n\nOBSSceneItem SourceTreeModel::Get(int idx)\n{\n\tif (idx == -1 || idx >= items.count())\n\t\treturn OBSSceneItem();\n\treturn items[idx];\n}\n\nSourceTreeModel::SourceTreeModel(SourceTree *st_) : QAbstractListModel(st_), st(st_)\n{\n\tobs_frontend_add_event_callback(OBSFrontendEvent, this);\n}\n\nint SourceTreeModel::rowCount(const QModelIndex &parent) const\n{\n\treturn parent.isValid() ? 0 : items.count();\n}\n\nQVariant SourceTreeModel::data(const QModelIndex &index, int role) const\n{\n\tif (role == Qt::AccessibleTextRole) {\n\t\tOBSSceneItem item = items[index.row()];\n\t\tobs_source_t *source = obs_sceneitem_get_source(item);\n\t\treturn QVariant(QT_UTF8(obs_source_get_name(source)));\n\t}\n\n\treturn QVariant();\n}\n\nQt::ItemFlags SourceTreeModel::flags(const QModelIndex &index) const\n{\n\tif (!index.isValid())\n\t\treturn QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled;\n\n\tobs_sceneitem_t *item = items[index.row()];\n\tbool is_group = obs_sceneitem_is_group(item);\n\n\treturn QAbstractListModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled |\n\t       (is_group ? Qt::ItemIsDropEnabled : Qt::NoItemFlags);\n}\n\nQt::DropActions SourceTreeModel::supportedDropActions() const\n{\n\treturn QAbstractItemModel::supportedDropActions() | Qt::MoveAction;\n}\n\nQString SourceTreeModel::GetNewGroupName()\n{\n\tOBSScene scene = GetCurrentScene();\n\tQString name = QTStr(\"Group\");\n\n\tint i = 2;\n\tfor (;;) {\n\t\tOBSSourceAutoRelease group = obs_get_source_by_name(QT_TO_UTF8(name));\n\t\tif (!group)\n\t\t\tbreak;\n\t\tname = QTStr(\"Basic.Main.Group\").arg(QString::number(i++));\n\t}\n\n\treturn name;\n}\n\nvoid SourceTreeModel::AddGroup()\n{\n\tQString name = GetNewGroupName();\n\tobs_sceneitem_t *group = obs_scene_add_group(GetCurrentScene(), QT_TO_UTF8(name));\n\tif (!group)\n\t\treturn;\n\n\tbeginInsertRows(QModelIndex(), 0, 0);\n\titems.insert(0, group);\n\tendInsertRows();\n\n\tst->UpdateWidget(createIndex(0, 0, nullptr), group);\n\tUpdateGroupState(true);\n\n\tQMetaObject::invokeMethod(st, \"Edit\", Qt::QueuedConnection, Q_ARG(int, 0));\n}\n\nvoid SourceTreeModel::GroupSelectedItems(QModelIndexList &indices)\n{\n\tif (indices.count() == 0)\n\t\treturn;\n\n\tOBSBasic *main = OBSBasic::Get();\n\tOBSScene scene = GetCurrentScene();\n\tQString name = GetNewGroupName();\n\n\tQVector<obs_sceneitem_t *> item_order;\n\n\tfor (int i = indices.count() - 1; i >= 0; i--) {\n\t\tobs_sceneitem_t *item = items[indices[i].row()];\n\t\titem_order << item;\n\t}\n\n\tst->undoSceneData = main->BackupScene(scene);\n\n\tobs_sceneitem_t *item = obs_scene_insert_group(scene, QT_TO_UTF8(name), item_order.data(), item_order.size());\n\tif (!item) {\n\t\tst->undoSceneData = nullptr;\n\t\treturn;\n\t}\n\n\tmain->undo_s.push_disabled();\n\n\tfor (obs_sceneitem_t *item : item_order)\n\t\tobs_sceneitem_select(item, false);\n\n\thasGroups = true;\n\tst->UpdateWidgets(true);\n\n\tobs_sceneitem_select(item, true);\n\n\t/* ----------------------------------------------------------------- */\n\t/* obs_scene_insert_group triggers a full refresh of scene items via */\n\t/* the item_add signal. No need to insert a row, just edit the one   */\n\t/* that's created automatically.                                     */\n\n\tint newIdx = indices[0].row();\n\tQMetaObject::invokeMethod(st, \"NewGroupEdit\", Qt::QueuedConnection, Q_ARG(int, newIdx));\n}\n\nvoid SourceTreeModel::UngroupSelectedGroups(QModelIndexList &indices)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tif (indices.count() == 0)\n\t\treturn;\n\n\tOBSScene scene = main->GetCurrentScene();\n\tOBSData undoData = main->BackupScene(scene);\n\n\tfor (int i = indices.count() - 1; i >= 0; i--) {\n\t\tobs_sceneitem_t *item = items[indices[i].row()];\n\t\tobs_sceneitem_group_ungroup(item);\n\t}\n\n\tSceneChanged();\n\n\tOBSData redoData = main->BackupScene(scene);\n\tmain->CreateSceneUndoRedoAction(QTStr(\"Basic.Main.Ungroup\"), undoData, redoData);\n}\n\nvoid SourceTreeModel::ExpandGroup(obs_sceneitem_t *item)\n{\n\tint itemIdx = items.indexOf(item);\n\tif (itemIdx == -1)\n\t\treturn;\n\n\titemIdx++;\n\n\tobs_scene_t *scene = obs_sceneitem_group_get_scene(item);\n\n\tQVector<OBSSceneItem> subItems;\n\tobs_scene_enum_items(scene, enumItem, &subItems);\n\n\tif (!subItems.size())\n\t\treturn;\n\n\tbeginInsertRows(QModelIndex(), itemIdx, itemIdx + subItems.size() - 1);\n\tfor (int i = 0; i < subItems.size(); i++)\n\t\titems.insert(i + itemIdx, subItems[i]);\n\tendInsertRows();\n\n\tst->UpdateWidgets();\n}\n\nvoid SourceTreeModel::CollapseGroup(obs_sceneitem_t *item)\n{\n\tint startIdx = -1;\n\tint endIdx = -1;\n\n\tobs_scene_t *scene = obs_sceneitem_group_get_scene(item);\n\n\tfor (int i = 0; i < items.size(); i++) {\n\t\tobs_scene_t *itemScene = obs_sceneitem_get_scene(items[i]);\n\n\t\tif (itemScene == scene) {\n\t\t\tif (startIdx == -1)\n\t\t\t\tstartIdx = i;\n\t\t\tendIdx = i;\n\t\t}\n\t}\n\n\tif (startIdx == -1)\n\t\treturn;\n\n\tbeginRemoveRows(QModelIndex(), startIdx, endIdx);\n\titems.remove(startIdx, endIdx - startIdx + 1);\n\tendRemoveRows();\n}\n\nvoid SourceTreeModel::UpdateGroupState(bool update)\n{\n\tbool nowHasGroups = false;\n\tfor (auto &item : items) {\n\t\tif (obs_sceneitem_is_group(item)) {\n\t\t\tnowHasGroups = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (nowHasGroups != hasGroups) {\n\t\thasGroups = nowHasGroups;\n\t\tif (update) {\n\t\t\tst->UpdateWidgets(true);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "frontend/components/SourceTreeModel.hpp",
    "content": "#pragma once\n\n#include <obs-frontend-api.h>\n#include <obs.hpp>\n\n#include <QAbstractListModel>\n\nclass SourceTree;\n\nclass SourceTreeModel : public QAbstractListModel {\n\tQ_OBJECT\n\n\tfriend class SourceTree;\n\tfriend class SourceTreeItem;\n\n\tSourceTree *st;\n\tQVector<OBSSceneItem> items;\n\tbool hasGroups = false;\n\n\tstatic void OBSFrontendEvent(enum obs_frontend_event event, void *ptr);\n\tvoid Clear();\n\tvoid SceneChanged();\n\tvoid ReorderItems();\n\n\tvoid Add(obs_sceneitem_t *item);\n\tvoid Remove(obs_sceneitem_t *item);\n\tOBSSceneItem Get(int idx);\n\tQString GetNewGroupName();\n\tvoid AddGroup();\n\n\tvoid GroupSelectedItems(QModelIndexList &indices);\n\tvoid UngroupSelectedGroups(QModelIndexList &indices);\n\n\tvoid ExpandGroup(obs_sceneitem_t *item);\n\tvoid CollapseGroup(obs_sceneitem_t *item);\n\n\tvoid UpdateGroupState(bool update);\n\npublic:\n\texplicit SourceTreeModel(SourceTree *st);\n\n\tvirtual int rowCount(const QModelIndex &parent) const override;\n\tvirtual QVariant data(const QModelIndex &index, int role) const override;\n\n\tvirtual Qt::ItemFlags flags(const QModelIndex &index) const override;\n\tvirtual Qt::DropActions supportedDropActions() const override;\n};\n"
  },
  {
    "path": "frontend/components/TextSourceToolbar.cpp",
    "content": "#include \"TextSourceToolbar.hpp\"\n#include \"ui_text-source-toolbar.h\"\n\n#include <OBSApp.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QColorDialog>\n#include <QFontDialog>\n\n#include \"moc_TextSourceToolbar.cpp\"\n\nextern void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false);\nextern QColor color_from_int(long long val);\nextern long long color_to_int(QColor color);\n\nTextSourceToolbar::TextSourceToolbar(QWidget *parent, OBSSource source)\n\t: SourceToolbar(parent, source),\n\t  ui(new Ui_TextSourceToolbar)\n{\n\tui->setupUi(this);\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\n\tconst char *id = obs_source_get_unversioned_id(source);\n\tbool ft2 = strcmp(id, \"text_ft2_source\") == 0;\n\tbool read_from_file = obs_data_get_bool(settings, ft2 ? \"from_file\" : \"read_from_file\");\n\n\tOBSDataAutoRelease font_obj = obs_data_get_obj(settings, \"font\");\n\tMakeQFont(font_obj, font);\n\n\t// Use \"color1\" if it's a freetype source and \"color\" elsewise\n\tunsigned int val = (unsigned int)obs_data_get_int(\n\t\tsettings, (strncmp(obs_source_get_id(source), \"text_ft2_source\", 15) == 0) ? \"color1\" : \"color\");\n\n\tcolor = color_from_int(val);\n\n\tconst char *text = obs_data_get_string(settings, \"text\");\n\n\tbool single_line = !read_from_file && (!text || (strchr(text, '\\n') == nullptr));\n\tui->emptySpace->setVisible(!single_line);\n\tui->text->setVisible(single_line);\n\tif (single_line)\n\t\tui->text->setText(text);\n}\n\nTextSourceToolbar::~TextSourceToolbar() {}\n\nvoid TextSourceToolbar::on_selectFont_clicked()\n{\n\tOBSSource source = GetSource();\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tQFontDialog::FontDialogOptions options;\n\tuint32_t flags;\n\tbool success;\n\n#ifndef _WIN32\n\toptions = QFontDialog::DontUseNativeDialog;\n#endif\n\n\tfont = QFontDialog::getFont(&success, font, this, QTStr(\"Basic.PropertiesWindow.SelectFont.WindowTitle\"),\n\t\t\t\t    options);\n\tif (!success) {\n\t\treturn;\n\t}\n\n\tOBSDataAutoRelease font_obj = obs_data_create();\n\n\tobs_data_set_string(font_obj, \"face\", QT_TO_UTF8(font.family()));\n\tobs_data_set_string(font_obj, \"style\", QT_TO_UTF8(font.styleName()));\n\tobs_data_set_int(font_obj, \"size\", font.pointSize());\n\tflags = font.bold() ? OBS_FONT_BOLD : 0;\n\tflags |= font.italic() ? OBS_FONT_ITALIC : 0;\n\tflags |= font.underline() ? OBS_FONT_UNDERLINE : 0;\n\tflags |= font.strikeOut() ? OBS_FONT_STRIKEOUT : 0;\n\tobs_data_set_int(font_obj, \"flags\", flags);\n\n\tSaveOldProperties(source);\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tobs_data_set_obj(settings, \"font\", font_obj);\n\n\tobs_source_update(source, settings);\n\n\tSetUndoProperties(source);\n}\n\nvoid TextSourceToolbar::on_selectColor_clicked()\n{\n\tOBSSource source = GetSource();\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tbool freetype = strncmp(obs_source_get_id(source), \"text_ft2_source\", 15) == 0;\n\n\tobs_property_t *p = obs_properties_get(props.get(), freetype ? \"color1\" : \"color\");\n\n\tconst char *desc = obs_property_description(p);\n\n\tQColorDialog::ColorDialogOptions options;\n\n\toptions |= QColorDialog::ShowAlphaChannel;\n#ifdef __linux__\n\t// TODO: Revisit hang on Ubuntu with native dialog\n\toptions |= QColorDialog::DontUseNativeDialog;\n#endif\n\n\tQColor newColor = QColorDialog::getColor(color, this, desc, options);\n\tif (!newColor.isValid()) {\n\t\treturn;\n\t}\n\n\tcolor = newColor;\n\n\tSaveOldProperties(source);\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tif (freetype) {\n\t\tobs_data_set_int(settings, \"color1\", color_to_int(color));\n\t\tobs_data_set_int(settings, \"color2\", color_to_int(color));\n\t} else {\n\t\tobs_data_set_int(settings, \"color\", color_to_int(color));\n\t}\n\tobs_source_update(source, settings);\n\n\tSetUndoProperties(source);\n}\n\nvoid TextSourceToolbar::on_text_textChanged()\n{\n\tOBSSource source = GetSource();\n\tif (!source) {\n\t\treturn;\n\t}\n\tstd::string newText = QT_TO_UTF8(ui->text->text());\n\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\tif (newText == obs_data_get_string(settings, \"text\")) {\n\t\treturn;\n\t}\n\tSaveOldProperties(source);\n\n\tobs_data_set_string(settings, \"text\", newText.c_str());\n\tobs_source_update(source, nullptr);\n\n\tSetUndoProperties(source, true);\n}\n"
  },
  {
    "path": "frontend/components/TextSourceToolbar.hpp",
    "content": "#pragma once\n\n#include \"SourceToolbar.hpp\"\n\nclass Ui_TextSourceToolbar;\n\nclass TextSourceToolbar : public SourceToolbar {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui_TextSourceToolbar> ui;\n\tQFont font;\n\tQColor color;\n\npublic:\n\tTextSourceToolbar(QWidget *parent, OBSSource source);\n\t~TextSourceToolbar();\n\npublic slots:\n\tvoid on_selectFont_clicked();\n\tvoid on_selectColor_clicked();\n\tvoid on_text_textChanged();\n};\n"
  },
  {
    "path": "frontend/components/UIValidation.cpp",
    "content": "#include \"UIValidation.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <QMessageBox>\n#include <QPushButton>\n\n#include \"moc_UIValidation.cpp\"\n\nstatic int CountVideoSources()\n{\n\tint count = 0;\n\tauto countSources = [](void *param, obs_source_t *source) {\n\t\tif (!source)\n\t\t\treturn true;\n\n\t\tuint32_t flags = obs_source_get_output_flags(source);\n\t\tif ((flags & OBS_SOURCE_VIDEO) != 0)\n\t\t\t(*static_cast<int *>(param))++;\n\n\t\treturn true;\n\t};\n\n\tobs_enum_sources(countSources, &count);\n\treturn count;\n}\n\nbool UIValidation::NoSourcesConfirmation(QWidget *parent)\n{\n\t// There are sources, don't need confirmation\n\tif (CountVideoSources() != 0)\n\t\treturn true;\n\n\t// Ignore no video if no parent is visible to alert on\n\tif (!parent->isVisible())\n\t\treturn true;\n\n\tQString msg = QTStr(\"NoSources.Text\");\n\tmsg += \"\\n\\n\";\n\tmsg += QTStr(\"NoSources.Text.AddSource\");\n\n\tQMessageBox messageBox(parent);\n\tmessageBox.setWindowTitle(QTStr(\"NoSources.Title\"));\n\tmessageBox.setText(msg);\n\n\tQAbstractButton *yesButton = messageBox.addButton(QTStr(\"Yes\"), QMessageBox::YesRole);\n\tmessageBox.addButton(QTStr(\"No\"), QMessageBox::NoRole);\n\tmessageBox.setIcon(QMessageBox::Question);\n\tmessageBox.exec();\n\n\tif (messageBox.clickedButton() != yesButton)\n\t\treturn false;\n\telse\n\t\treturn true;\n}\n\nStreamSettingsAction UIValidation::StreamSettingsConfirmation(QWidget *parent, OBSService service)\n{\n\tif (obs_service_can_try_to_connect(service))\n\t\treturn StreamSettingsAction::ContinueStream;\n\n\tchar const *serviceType = obs_service_get_type(service);\n\tbool isCustomService = (strcmp(serviceType, \"rtmp_custom\") == 0);\n\n\tchar const *streamUrl = obs_service_get_connect_info(service, OBS_SERVICE_CONNECT_INFO_SERVER_URL);\n\tchar const *streamKey = obs_service_get_connect_info(service, OBS_SERVICE_CONNECT_INFO_STREAM_KEY);\n\n\tbool streamUrlMissing = !(streamUrl != NULL && streamUrl[0] != '\\0');\n\tbool streamKeyMissing = !(streamKey != NULL && streamKey[0] != '\\0');\n\n\tQString msg;\n\tif (!isCustomService && streamUrlMissing && streamKeyMissing) {\n\t\tmsg = QTStr(\"Basic.Settings.Stream.MissingUrlAndApiKey\");\n\t} else if (!isCustomService && streamKeyMissing) {\n\t\tmsg = QTStr(\"Basic.Settings.Stream.MissingStreamKey\");\n\t} else {\n\t\tmsg = QTStr(\"Basic.Settings.Stream.MissingUrl\");\n\t}\n\n\tQMessageBox messageBox(parent);\n\tmessageBox.setWindowTitle(QTStr(\"Basic.Settings.Stream.MissingSettingAlert\"));\n\tmessageBox.setText(msg);\n\n\tQPushButton *cancel;\n\tQPushButton *settings;\n\n#ifdef __APPLE__\n#define ACCEPT_BUTTON QMessageBox::AcceptRole\n#define REJECT_BUTTON QMessageBox::ResetRole\n#else\n#define ACCEPT_BUTTON QMessageBox::NoRole\n#define REJECT_BUTTON QMessageBox::NoRole\n#endif\n\tsettings = messageBox.addButton(QTStr(\"Basic.Settings.Stream.StreamSettingsWarning\"), ACCEPT_BUTTON);\n\tcancel = messageBox.addButton(QTStr(\"Cancel\"), REJECT_BUTTON);\n\n\tmessageBox.setDefaultButton(settings);\n\tmessageBox.setEscapeButton(cancel);\n\n\tmessageBox.setIcon(QMessageBox::Warning);\n\tmessageBox.exec();\n\n\tif (messageBox.clickedButton() == settings)\n\t\treturn StreamSettingsAction::OpenSettings;\n\tif (messageBox.clickedButton() == cancel)\n\t\treturn StreamSettingsAction::Cancel;\n\n\treturn StreamSettingsAction::ContinueStream;\n}\n"
  },
  {
    "path": "frontend/components/UIValidation.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QObject>\n\nenum class StreamSettingsAction {\n\tOpenSettings,\n\tCancel,\n\tContinueStream,\n};\n\nclass UIValidation : public QObject {\n\tQ_OBJECT\n\npublic:\n\t/* Confirm video about to record or stream has sources.  Shows alert\n\t * box notifying there are no video sources Returns true if user clicks\n\t * \"Yes\" Returns false if user clicks \"No\" */\n\tstatic bool NoSourcesConfirmation(QWidget *parent);\n\n\t/* Check streaming requirements, shows warning with options to open\n\t * settings, cancel stream, or attempt connection anyways.  If setup\n\t * basics is missing in stream, explain missing fields and offer to\n\t * open settings, cancel, or continue.  Returns Continue if all\n\t * settings are valid. */\n\tstatic StreamSettingsAction StreamSettingsConfirmation(QWidget *parent, OBSService service);\n};\n"
  },
  {
    "path": "frontend/components/UrlPushButton.cpp",
    "content": "#include \"UrlPushButton.hpp\"\n\n#include <QDesktopServices>\n\n#include \"moc_UrlPushButton.cpp\"\n\nvoid UrlPushButton::setTargetUrl(QUrl url)\n{\n\tsetToolTip(url.toString());\n\tm_targetUrl = url;\n}\n\nQUrl UrlPushButton::targetUrl()\n{\n\treturn m_targetUrl;\n}\n\nvoid UrlPushButton::mousePressEvent(QMouseEvent *event)\n{\n\tQ_UNUSED(event)\n\tQUrl openUrl = m_targetUrl;\n\tif (openUrl.isEmpty())\n\t\treturn;\n\n\tQDesktopServices::openUrl(openUrl);\n}\n"
  },
  {
    "path": "frontend/components/UrlPushButton.hpp",
    "content": "#pragma once\n\n#include <QMouseEvent>\n#include <QPushButton>\n#include <QUrl>\n#include <QWidget>\n\nclass UrlPushButton : public QPushButton {\n\tQ_OBJECT\n\tQ_PROPERTY(QUrl targetUrl READ targetUrl WRITE setTargetUrl)\n\npublic:\n\tinline UrlPushButton(QWidget *parent = nullptr) : QPushButton(parent) {}\n\tvoid setTargetUrl(QUrl url);\n\tQUrl targetUrl();\n\nprotected:\n\tvoid mousePressEvent(QMouseEvent *event) override;\n\nprivate:\n\tQUrl m_targetUrl;\n};\n"
  },
  {
    "path": "frontend/components/VisibilityItemDelegate.cpp",
    "content": "#include \"VisibilityItemWidget.hpp\"\n#include \"VisibilityItemDelegate.hpp\"\n\n#include <QKeyEvent>\n\n#include \"moc_VisibilityItemDelegate.cpp\"\n\nVisibilityItemDelegate::VisibilityItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {}\n\nvoid VisibilityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,\n\t\t\t\t   const QModelIndex &index) const\n{\n\tQStyledItemDelegate::paint(painter, option, index);\n\n\tQObject *parentObj = parent();\n\tQListWidget *list = qobject_cast<QListWidget *>(parentObj);\n\tif (!list)\n\t\treturn;\n\n\tQListWidgetItem *item = list->item(index.row());\n\tVisibilityItemWidget *widget = qobject_cast<VisibilityItemWidget *>(list->itemWidget(item));\n\tif (!widget)\n\t\treturn;\n\n\tbool selected = option.state.testFlag(QStyle::State_Selected);\n\tbool active = option.state.testFlag(QStyle::State_Active);\n\n\tQPalette palette = list->palette();\n#if defined(_WIN32) || defined(__APPLE__)\n\tQPalette::ColorGroup group = active ? QPalette::Active : QPalette::Inactive;\n#else\n\tQPalette::ColorGroup group = QPalette::Active;\n#endif\n\n#ifdef _WIN32\n\tQPalette::ColorRole highlightRole = QPalette::WindowText;\n#else\n\tQPalette::ColorRole highlightRole = QPalette::HighlightedText;\n#endif\n\n\tQPalette::ColorRole role;\n\n\tif (selected && active)\n\t\trole = highlightRole;\n\telse\n\t\trole = QPalette::WindowText;\n\n\twidget->SetColor(palette.color(group, role), active, selected);\n}\n\nbool VisibilityItemDelegate::eventFilter(QObject *object, QEvent *event)\n{\n\tQWidget *editor = qobject_cast<QWidget *>(object);\n\tif (!editor)\n\t\treturn false;\n\n\tif (event->type() == QEvent::KeyPress) {\n\t\tQKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);\n\n\t\tif (keyEvent->key() == Qt::Key_Tab || keyEvent->key() == Qt::Key_Backtab) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn QStyledItemDelegate::eventFilter(object, event);\n}\n"
  },
  {
    "path": "frontend/components/VisibilityItemDelegate.hpp",
    "content": "#pragma once\n\n#include <QStyledItemDelegate>\n\nclass QObject;\n\nclass VisibilityItemDelegate : public QStyledItemDelegate {\n\tQ_OBJECT\n\npublic:\n\tVisibilityItemDelegate(QObject *parent = nullptr);\n\n\tvoid paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;\n\nprotected:\n\tbool eventFilter(QObject *object, QEvent *event) override;\n};\n"
  },
  {
    "path": "frontend/components/VisibilityItemWidget.cpp",
    "content": "#include \"VisibilityItemWidget.hpp\"\n\n#include <components/OBSSourceLabel.hpp>\n\n#include <QCheckBox>\n#include <QHBoxLayout>\n\n#include \"moc_VisibilityItemWidget.cpp\"\n\nVisibilityItemWidget::VisibilityItemWidget(obs_source_t *source_)\n\t: source(source_),\n\t  enabledSignal(obs_source_get_signal_handler(source), \"enable\", OBSSourceEnabled, this)\n{\n\tbool enabled = obs_source_enabled(source);\n\n\tvis = new QCheckBox();\n\tvis->setProperty(\"class\", \"checkbox-icon indicator-visibility\");\n\tvis->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);\n\tvis->setChecked(enabled);\n\n\tlabel = new OBSSourceLabel(source);\n\tlabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);\n\n\tQHBoxLayout *itemLayout = new QHBoxLayout();\n\titemLayout->addWidget(vis);\n\titemLayout->addWidget(label);\n\titemLayout->setContentsMargins(0, 0, 0, 0);\n\n\tsetLayout(itemLayout);\n\n\tconnect(vis, &QCheckBox::clicked, this, [this](bool visible) { obs_source_set_enabled(source, visible); });\n}\n\nvoid VisibilityItemWidget::OBSSourceEnabled(void *param, calldata_t *data)\n{\n\tVisibilityItemWidget *window = static_cast<VisibilityItemWidget *>(param);\n\tbool enabled = calldata_bool(data, \"enabled\");\n\n\tQMetaObject::invokeMethod(window, \"SourceEnabled\", Q_ARG(bool, enabled));\n}\n\nvoid VisibilityItemWidget::SourceEnabled(bool enabled)\n{\n\tif (vis->isChecked() != enabled)\n\t\tvis->setChecked(enabled);\n}\n\nvoid VisibilityItemWidget::SetColor(const QColor &color, bool active_, bool selected_)\n{\n\t/* Do not update unless the state has actually changed */\n\tif (active_ == active && selected_ == selected)\n\t\treturn;\n\n\tQPalette pal = vis->palette();\n\tpal.setColor(QPalette::WindowText, color);\n\tvis->setPalette(pal);\n\n\tlabel->setStyleSheet(QString(\"color: %1;\").arg(color.name()));\n\n\tactive = active_;\n\tselected = selected_;\n}\n\nvoid SetupVisibilityItem(QListWidget *list, QListWidgetItem *item, obs_source_t *source)\n{\n\tVisibilityItemWidget *baseWidget = new VisibilityItemWidget(source);\n\n\tlist->setItemWidget(item, baseWidget);\n}\n"
  },
  {
    "path": "frontend/components/VisibilityItemWidget.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QListWidget>\n#include <QWidget>\n\nclass OBSSourceLabel;\nclass QCheckBox;\n\nclass VisibilityItemWidget : public QWidget {\n\tQ_OBJECT\n\nprivate:\n\tOBSSource source;\n\tOBSSourceLabel *label = nullptr;\n\tQCheckBox *vis = nullptr;\n\n\tOBSSignal enabledSignal;\n\n\tbool active = false;\n\tbool selected = false;\n\n\tstatic void OBSSourceEnabled(void *param, calldata_t *data);\n\nprivate slots:\n\tvoid SourceEnabled(bool enabled);\n\npublic:\n\tVisibilityItemWidget(obs_source_t *source);\n\n\tvoid SetColor(const QColor &color, bool active, bool selected);\n};\n\nvoid SetupVisibilityItem(QListWidget *list, QListWidgetItem *item, obs_source_t *source);\n"
  },
  {
    "path": "frontend/components/VolumeAccessibleInterface.cpp",
    "content": "#include \"VolumeAccessibleInterface.hpp\"\n\n#include <components/VolumeSlider.hpp>\n\nVolumeAccessibleInterface::VolumeAccessibleInterface(QWidget *w) : QAccessibleWidget(w) {}\n\nVolumeSlider *VolumeAccessibleInterface::slider() const\n{\n\treturn qobject_cast<VolumeSlider *>(object());\n}\n\nQString VolumeAccessibleInterface::text(QAccessible::Text t) const\n{\n\tif (slider()->isVisible()) {\n\t\tswitch (t) {\n\t\tcase QAccessible::Text::Value:\n\t\t\treturn currentValue().toString();\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn QAccessibleWidget::text(t);\n}\n\nQVariant VolumeAccessibleInterface::currentValue() const\n{\n\tQString text;\n\tfloat db = obs_fader_get_db(slider()->fad);\n\n\tif (db < -96.0f)\n\t\ttext = \"-inf dB\";\n\telse\n\t\ttext = QString::number(db, 'f', 1).append(\" dB\");\n\n\treturn text;\n}\n\nvoid VolumeAccessibleInterface::setCurrentValue(const QVariant &value)\n{\n\tslider()->setValue(value.toInt());\n}\n\nQVariant VolumeAccessibleInterface::maximumValue() const\n{\n\treturn slider()->maximum();\n}\n\nQVariant VolumeAccessibleInterface::minimumValue() const\n{\n\treturn slider()->minimum();\n}\n\nQVariant VolumeAccessibleInterface::minimumStepSize() const\n{\n\treturn slider()->singleStep();\n}\n\nQAccessible::Role VolumeAccessibleInterface::role() const\n{\n\treturn QAccessible::Role::Slider;\n}\n"
  },
  {
    "path": "frontend/components/VolumeAccessibleInterface.hpp",
    "content": "#pragma once\n\n#include <QAccessibleWidget>\n\nclass VolumeSlider;\n\nclass VolumeAccessibleInterface : public QAccessibleWidget {\n\npublic:\n\tVolumeAccessibleInterface(QWidget *w);\n\n\tQVariant currentValue() const;\n\tvoid setCurrentValue(const QVariant &value);\n\n\tQVariant maximumValue() const;\n\tQVariant minimumValue() const;\n\n\tQVariant minimumStepSize() const;\n\nprivate:\n\tVolumeSlider *slider() const;\n\nprotected:\n\tvirtual QAccessible::Role role() const override;\n\tvirtual QString text(QAccessible::Text t) const override;\n};\n"
  },
  {
    "path": "frontend/components/VolumeControl.cpp",
    "content": "#include \"VolumeControl.hpp\"\n\n#include <components/MuteCheckBox.hpp>\n#include <components/VolumeMeter.hpp>\n#include <components/VolumeName.hpp>\n#include <components/VolumeSlider.hpp>\n#include <dialogs/NameDialog.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <QMessageBox>\n#include <QObjectCleanupHandler>\n\n#include \"moc_VolumeControl.cpp\"\n\nnamespace {\nbool isSourceUnassigned(obs_source_t *source)\n{\n\tuint32_t mixes = (obs_source_get_audio_mixers(source) & ((1 << MAX_AUDIO_MIXES) - 1));\n\tobs_monitoring_type mt = obs_source_get_monitoring_type(source);\n\n\treturn mixes == 0 && mt != OBS_MONITORING_TYPE_MONITOR_ONLY;\n}\n\nvoid showUnassignedWarning(const char *name)\n{\n\tauto msgBox = [=]() {\n\t\tQMessageBox msgbox(App()->GetMainWindow());\n\t\tmsgbox.setWindowTitle(QTStr(\"VolControl.UnassignedWarning.Title\"));\n\t\tmsgbox.setText(QTStr(\"VolControl.UnassignedWarning.Text\").arg(name));\n\t\tmsgbox.setIcon(QMessageBox::Icon::Information);\n\t\tmsgbox.addButton(QMessageBox::Ok);\n\n\t\tQCheckBox *cb = new QCheckBox(QTStr(\"DoNotShowAgain\"));\n\t\tmsgbox.setCheckBox(cb);\n\n\t\tmsgbox.exec();\n\n\t\tif (cb->isChecked()) {\n\t\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"WarnedAboutUnassignedSources\", true);\n\t\t\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n\t\t}\n\t};\n\n\tQMetaObject::invokeMethod(App(), \"Exec\", Qt::QueuedConnection, Q_ARG(VoidFunc, msgBox));\n}\n} // namespace\n\nVolumeControl::VolumeControl(obs_source_t *source, QWidget *parent, bool vertical)\n\t: weakSource_(OBSGetWeakRef(source)),\n\t  obs_fader(obs_fader_create(OBS_FADER_LOG)),\n\t  vertical(vertical),\n\t  contextMenu(nullptr),\n\t  QFrame(parent)\n{\n\tutils = std::make_unique<idian::Utils>(this);\n\n\tuuid = obs_source_get_uuid(source);\n\n\tmainLayout = new QBoxLayout(QBoxLayout::LeftToRight, this);\n\tmainLayout->setContentsMargins(0, 0, 0, 0);\n\tmainLayout->setSpacing(0);\n\tsetLayout(mainLayout);\n\n\tcategoryLabel = new QLabel(\"Active\");\n\tcategoryLabel->setAlignment(Qt::AlignCenter);\n\tutils->addClass(categoryLabel, \"mixer-category\");\n\tutils->addClass(categoryLabel, \"text-tiny\");\n\n\tnameButton = new VolumeName(source, this);\n\tnameButton->setMaximumWidth(140);\n\tutils->addClass(nameButton, \"text-small\");\n\tutils->addClass(nameButton, \"mixer-name\");\n\n\tmuteButton = new QPushButton(this);\n\tmuteButton->setCheckable(true);\n\tutils->addClass(muteButton, \"btn-mute\");\n\n\tmonitorButton = new QPushButton(this);\n\tmonitorButton->setCheckable(true);\n\tutils->addClass(monitorButton, \"btn-monitor\");\n\n\tvolumeLabel = new QLabel(this);\n\tvolumeLabel->setObjectName(\"volLabel\");\n\n\tslider = new VolumeSlider(obs_fader, Qt::Horizontal, this);\n\tslider->setMinimum(0);\n\tslider->setMaximum(int(FADER_PRECISION));\n\n\tsourceName = obs_source_get_name(source);\n\tsetObjectName(sourceName);\n\n\tutils->applyStateStylingEventFilter(muteButton);\n\tutils->applyStateStylingEventFilter(monitorButton);\n\n\tvolumeMeter = new VolumeMeter(this, source);\n\n\tbool muted = obs_source_muted(source);\n\tbool unassigned = isSourceUnassigned(source);\n\n\tvolumeMeter->setMuted(muted || unassigned);\n\n\tsetLayoutVertical(vertical);\n\tsetName(sourceName);\n\n\tobs_fader_add_callback(obs_fader, obsVolumeChanged, this);\n\n\tobsSignals.reserve(9);\n\tobsSignals.emplace_back(obs_source_get_signal_handler(source), \"mute\", obsVolumeMuted, this);\n\tobsSignals.emplace_back(obs_source_get_signal_handler(source), \"audio_mixers\", obsMixersOrMonitoringChanged,\n\t\t\t\tthis);\n\tobsSignals.emplace_back(obs_source_get_signal_handler(source), \"audio_monitoring\", obsMixersOrMonitoringChanged,\n\t\t\t\tthis);\n\tobsSignals.emplace_back(obs_source_get_signal_handler(source), \"activate\", VolumeControl::obsSourceActivated,\n\t\t\t\tthis);\n\tobsSignals.emplace_back(obs_source_get_signal_handler(source), \"deactivate\",\n\t\t\t\tVolumeControl::obsSourceDeactivated, this);\n\tobsSignals.emplace_back(obs_source_get_signal_handler(source), \"audio_activate\",\n\t\t\t\tVolumeControl::obsSourceActivated, this);\n\tobsSignals.emplace_back(obs_source_get_signal_handler(source), \"audio_deactivate\",\n\t\t\t\tVolumeControl::obsSourceDeactivated, this);\n\n\tobsSignals.emplace_back(obs_source_get_signal_handler(source), \"remove\", VolumeControl::obsSourceDestroy, this);\n\tobsSignals.emplace_back(obs_source_get_signal_handler(source), \"destroy\", VolumeControl::obsSourceDestroy,\n\t\t\t\tthis);\n\n\tsetContextMenuPolicy(Qt::CustomContextMenu);\n\tconnect(this, &QWidget::customContextMenuRequested, this, &VolumeControl::showVolumeControlMenu);\n\n\tconnect(nameButton, &VolumeName::renamed, this, &VolumeControl::setName);\n\tconnect(nameButton, &VolumeName::clicked, this, [&]() { showVolumeControlMenu(); });\n\n\tconnect(slider, &VolumeSlider::valueChanged, this, &VolumeControl::sliderChanged);\n\tconnect(muteButton, &QPushButton::clicked, this, &VolumeControl::handleMuteButton);\n\tconnect(monitorButton, &QPushButton::clicked, this, &VolumeControl::handleMonitorButton);\n\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\tconnect(main, &OBSBasic::profileSettingChanged, this,\n\t\t\t[this](const std::string &category, const std::string &name) {\n\t\t\t\tif (category == \"Audio\" && name == \"MeterDecayRate\") {\n\t\t\t\t\tupdateDecayRate();\n\t\t\t\t} else if (category == \"Audio\" && name == \"PeakMeterType\") {\n\t\t\t\t\tupdatePeakMeterType();\n\t\t\t\t}\n\t\t\t});\n\t}\n\n\tobs_fader_attach_source(obs_fader, source);\n\n\t// Call volume changed once to init the slider position and label\n\tchangeVolume();\n\n\tupdateMixerState();\n}\n\nVolumeControl::~VolumeControl()\n{\n\tobs_fader_remove_callback(obs_fader, obsVolumeChanged, this);\n\n\tobsSignals.clear();\n\n\tif (contextMenu) {\n\t\tcontextMenu->close();\n\t}\n}\n\nvoid VolumeControl::obsVolumeChanged(void *data, float)\n{\n\tVolumeControl *volControl = static_cast<VolumeControl *>(data);\n\n\tQMetaObject::invokeMethod(volControl, \"changeVolume\", Qt::QueuedConnection);\n}\n\nvoid VolumeControl::obsVolumeMuted(void *data, calldata_t *)\n{\n\tVolumeControl *volControl = static_cast<VolumeControl *>(data);\n\n\tQMetaObject::invokeMethod(volControl, \"updateMixerState\", Qt::QueuedConnection);\n}\n\nvoid VolumeControl::obsMixersOrMonitoringChanged(void *data, calldata_t *)\n{\n\tVolumeControl *volControl = static_cast<VolumeControl *>(data);\n\tQMetaObject::invokeMethod(volControl, \"updateMixerState\", Qt::QueuedConnection);\n}\n\nvoid VolumeControl::obsSourceActivated(void *data, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<VolumeControl *>(data), \"sourceActiveChanged\", Qt::QueuedConnection,\n\t\t\t\t  Q_ARG(bool, true));\n}\n\nvoid VolumeControl::obsSourceDeactivated(void *data, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<VolumeControl *>(data), \"sourceActiveChanged\", Qt::QueuedConnection,\n\t\t\t\t  Q_ARG(bool, false));\n}\n\nvoid VolumeControl::obsSourceDestroy(void *data, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<VolumeControl *>(data), \"handleSourceDestroyed\", Qt::QueuedConnection);\n}\n\nvoid VolumeControl::setLayoutVertical(bool vertical)\n{\n\tQBoxLayout *newLayout = new QBoxLayout(vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);\n\tnewLayout->setContentsMargins(0, 0, 0, 0);\n\tnewLayout->setSpacing(0);\n\n\tif (vertical) {\n\t\tsetMaximumWidth(110);\n\t\tsetSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);\n\n\t\tQHBoxLayout *categoryLayout = new QHBoxLayout;\n\t\tQHBoxLayout *nameLayout = new QHBoxLayout;\n\t\tQHBoxLayout *controlLayout = new QHBoxLayout;\n\t\tQHBoxLayout *volLayout = new QHBoxLayout;\n\t\tQFrame *meterFrame = new QFrame;\n\t\tQHBoxLayout *meterLayout = new QHBoxLayout;\n\n\t\tvolumeMeter->setVertical(true);\n\t\tvolumeMeter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);\n\n\t\tslider->setOrientation(Qt::Vertical);\n\t\tslider->setLayoutDirection(Qt::LeftToRight);\n\t\tslider->setDisplayTicks(true);\n\n\t\tnameButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);\n\t\tcategoryLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);\n\t\tvolumeLabel->setAlignment(Qt::AlignLeft);\n\n\t\tcategoryLayout->setAlignment(Qt::AlignCenter);\n\t\tnameLayout->setAlignment(Qt::AlignCenter);\n\t\tmeterLayout->setAlignment(Qt::AlignCenter);\n\t\tcontrolLayout->setAlignment(Qt::AlignCenter);\n\t\tvolLayout->setAlignment(Qt::AlignCenter);\n\n\t\tmeterFrame->setObjectName(\"volMeterFrame\");\n\n\t\tcategoryLayout->setContentsMargins(0, 0, 0, 0);\n\t\tcategoryLayout->setSpacing(0);\n\t\tcategoryLayout->addWidget(categoryLabel);\n\n\t\tnameLayout->setContentsMargins(0, 0, 0, 0);\n\t\tnameLayout->setSpacing(0);\n\t\tnameLayout->addWidget(nameButton);\n\n\t\tcontrolLayout->setContentsMargins(0, 0, 0, 0);\n\t\tcontrolLayout->setSpacing(0);\n\n\t\t// Add Headphone (audio monitoring) widget here\n\t\tcontrolLayout->addWidget(muteButton);\n\t\tcontrolLayout->addWidget(monitorButton);\n\n\t\tmeterLayout->setContentsMargins(0, 0, 0, 0);\n\t\tmeterLayout->setSpacing(0);\n\t\tmeterLayout->addWidget(slider);\n\t\tmeterLayout->addWidget(volumeMeter);\n\n\t\tmeterFrame->setLayout(meterLayout);\n\n\t\tvolLayout->setContentsMargins(0, 0, 0, 0);\n\t\tvolLayout->setSpacing(0);\n\t\tvolLayout->addWidget(volumeLabel);\n\t\tvolLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Maximum));\n\n\t\tnewLayout->addItem(categoryLayout);\n\t\tnewLayout->addItem(nameLayout);\n\t\tnewLayout->addItem(volLayout);\n\t\tnewLayout->addWidget(meterFrame);\n\t\tnewLayout->addItem(controlLayout);\n\n\t\tnewLayout->setStretch(0, 0);\n\t\tnewLayout->setStretch(1, 0);\n\t\tnewLayout->setStretch(2, 0);\n\t\tnewLayout->setStretch(3, 1);\n\t\tnewLayout->setStretch(4, 0);\n\n\t\tvolumeMeter->setFocusProxy(slider);\n\t} else {\n\t\tsetMaximumWidth(QWIDGETSIZE_MAX);\n\t\tsetSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);\n\n\t\tQVBoxLayout *textLayout = new QVBoxLayout;\n\t\tQHBoxLayout *controlLayout = new QHBoxLayout;\n\t\tQFrame *meterFrame = new QFrame;\n\t\tQVBoxLayout *meterLayout = new QVBoxLayout;\n\t\tQVBoxLayout *buttonLayout = new QVBoxLayout;\n\n\t\tvolumeMeter->setVertical(false);\n\t\tvolumeMeter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);\n\n\t\tslider->setOrientation(Qt::Horizontal);\n\t\tslider->setLayoutDirection(Qt::LeftToRight);\n\t\tslider->setDisplayTicks(true);\n\n\t\tnameButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);\n\t\tcategoryLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);\n\t\tvolumeLabel->setAlignment(Qt::AlignRight);\n\n\t\tQHBoxLayout *textTopLayout = new QHBoxLayout;\n\t\ttextTopLayout->setContentsMargins(0, 0, 0, 0);\n\t\ttextTopLayout->addWidget(categoryLabel);\n\t\ttextTopLayout->addWidget(volumeLabel);\n\n\t\ttextLayout->setContentsMargins(0, 0, 0, 0);\n\t\ttextLayout->addItem(textTopLayout);\n\t\ttextLayout->addWidget(nameButton);\n\n\t\tmeterFrame->setObjectName(\"volMeterFrame\");\n\t\tmeterFrame->setLayout(meterLayout);\n\n\t\tmeterLayout->setContentsMargins(0, 0, 0, 0);\n\t\tmeterLayout->setSpacing(0);\n\n\t\tmeterLayout->addWidget(slider);\n\t\tmeterLayout->addWidget(volumeMeter);\n\t\tmeterLayout->setStretch(0, 2);\n\t\tmeterLayout->setStretch(1, 2);\n\n\t\tbuttonLayout->setContentsMargins(0, 0, 0, 0);\n\t\tbuttonLayout->setSpacing(0);\n\n\t\tbuttonLayout->addWidget(muteButton);\n\t\tbuttonLayout->addWidget(monitorButton);\n\n\t\tcontrolLayout->addItem(buttonLayout);\n\t\tcontrolLayout->addWidget(meterFrame);\n\n\t\tnewLayout->addItem(textLayout);\n\t\tnewLayout->addItem(controlLayout);\n\t\tnewLayout->setStretch(0, 3);\n\t\tnewLayout->setStretch(1, 6);\n\n\t\tvolumeMeter->setFocusProxy(slider);\n\t}\n\n\tQWidget().setLayout(mainLayout);\n\n\tsetLayout(newLayout);\n\tmainLayout = newLayout;\n\tupdateTabOrder();\n\n\tadjustSize();\n}\n\nvoid VolumeControl::showVolumeControlMenu(QPoint pos)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource());\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tQMenu *popup = new QMenu(this);\n\n\t// Create menu QActions\n\tQAction *lockAction = new QAction(QTStr(\"LockVolume\"), popup);\n\tlockAction->setCheckable(true);\n\tlockAction->setChecked(mixerStatus().has(VolumeControl::MixerStatus::Locked));\n\n\tbool isGlobal = mixerStatus().has(VolumeControl::MixerStatus::Global);\n\n\tQAction *pinAction = new QAction(QTStr(\"Basic.AudioMixer.Pin\"), popup);\n\tbool isPinned = mixerStatus().has(VolumeControl::MixerStatus::Pinned);\n\tif (isPinned) {\n\t\tpinAction->setText(QTStr(\"Basic.AudioMixer.Unpin\"));\n\t}\n\n\tQAction *hideAction = new QAction(QTStr(\"Basic.AudioMixer.Hide\"), popup);\n\tbool isHidden = mixerStatus().has(VolumeControl::MixerStatus::Hidden);\n\tif (isHidden && !isGlobal) {\n\t\thideAction->setText(QTStr(\"Basic.AudioMixer.Unhide\"));\n\t}\n\n\tQAction *unhideAllAction = new QAction(QTStr(\"UnhideAll\"), popup);\n\tQAction *mixerRenameAction = new QAction(QTStr(\"Rename\"), popup);\n\n\tQAction *copyFiltersAction = new QAction(QTStr(\"Copy.Filters\"), popup);\n\tQAction *pasteFiltersAction = new QAction(QTStr(\"Paste.Filters\"), popup);\n\n\tQAction *filtersAction = new QAction(QTStr(\"Filters\"), popup);\n\tQAction *propertiesAction = new QAction(QTStr(\"Properties\"), popup);\n\n\t// Set properties on actions that require source reference\n\thideAction->setProperty(\"source\", QVariant::fromValue<OBSSource>(source));\n\tpinAction->setProperty(\"source\", QVariant::fromValue<OBSSource>(source));\n\n\tmixerRenameAction->setProperty(\"source\", QVariant::fromValue<OBSSource>(source));\n\n\tcopyFiltersAction->setProperty(\"source\", QVariant::fromValue<OBSSource>(source));\n\tpasteFiltersAction->setProperty(\"source\", QVariant::fromValue<OBSSource>(source));\n\n\tfiltersAction->setProperty(\"source\", QVariant::fromValue<OBSSource>(source));\n\tpropertiesAction->setProperty(\"source\", QVariant::fromValue<OBSSource>(source));\n\n\t// Connect actions to signals\n\tOBSBasic *main = OBSBasic::Get();\n\n\tconnect(unhideAllAction, &QAction::triggered, this, [this]() { emit unhideAll(); });\n\n\tconnect(hideAction, &QAction::triggered, this, [this, isHidden]() { setHiddenInMixer(!isHidden); });\n\tconnect(\n\t\tpinAction, &QAction::triggered, this, [this, isPinned]() { setPinnedInMixer(!isPinned); },\n\t\tQt::DirectConnection);\n\tconnect(lockAction, &QAction::toggled, this, &VolumeControl::setLocked);\n\n\tconnect(copyFiltersAction, &QAction::triggered, main, &OBSBasic::actionCopyFilters);\n\tconnect(pasteFiltersAction, &QAction::triggered, main, &OBSBasic::actionPasteFilters);\n\n\tconnect(mixerRenameAction, &QAction::triggered, this, &VolumeControl::renameSource);\n\n\tconnect(filtersAction, &QAction::triggered, main, &OBSBasic::actionOpenSourceFilters);\n\tconnect(propertiesAction, &QAction::triggered, main, &OBSBasic::actionOpenSourceProperties);\n\n\t// Enable/disable actions\n\tcopyFiltersAction->setEnabled(obs_source_filter_count(source) > 0);\n\tpasteFiltersAction->setEnabled(!obs_weak_source_expired(main->copyFiltersSource()));\n\n\tif (isGlobal) {\n\t\tpinAction->setDisabled(true);\n\t\thideAction->setDisabled(true);\n\t}\n\n\tif (isPinned) {\n\t\thideAction->setDisabled(true);\n\t}\n\n\t// Build menu\n\tpopup->addAction(unhideAllAction);\n\tpopup->addSeparator();\n\tpopup->addAction(pinAction);\n\tpopup->addAction(hideAction);\n\tpopup->addAction(lockAction);\n\n\tpopup->addSeparator();\n\tpopup->addAction(copyFiltersAction);\n\tpopup->addAction(pasteFiltersAction);\n\tpopup->addSeparator();\n\tpopup->addAction(mixerRenameAction);\n\tpopup->addSeparator();\n\tpopup->addAction(filtersAction);\n\tpopup->addAction(propertiesAction);\n\n\t// Calculate menu position\n\tQPoint popupPos = mapToGlobal(pos);\n\n\tif (pos.isNull()) {\n\t\tQPoint menuPos = nameButton->mapToGlobal(nameButton->rect().bottomLeft());\n\t\tQSize menuSize = popup->sizeHint();\n\n\t\tQRect available = QGuiApplication::screenAt(menuPos)->availableGeometry();\n\t\tint spaceBelow = available.bottom() - menuPos.y();\n\t\tint spaceAbove = menuPos.y() - available.top();\n\n\t\tif (menuSize.height() > spaceBelow && spaceAbove > spaceBelow) {\n\t\t\tmenuPos = nameButton->mapToGlobal(nameButton->rect().topLeft());\n\t\t\tmenuPos.ry() -= menuSize.height();\n\t\t}\n\n\t\tif (menuPos.x() + menuSize.width() > available.right()) {\n\t\t\tmenuPos.rx() = available.right() - menuSize.width();\n\t\t}\n\n\t\tpopupPos = menuPos;\n\t}\n\n\tpopup->popup(popupPos);\n\n\tconnect(popup, &QMenu::aboutToHide, popup, &QMenu::deleteLater);\n}\n\nvoid VolumeControl::renameSource()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\tOBSSource source = action->property(\"source\").value<OBSSource>();\n\n\tstd::string uuid = obs_source_get_uuid(source);\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\t// Defer the rename dialog to avoid blocking the UI thread while the context menu is closing, which can cause issues on some platforms\n\tQTimer::singleShot(0, main, [main, uuid]() {\n\t\tif (!main) {\n\t\t\treturn;\n\t\t}\n\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tif (!source) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst char *prevName = obs_source_get_name(source);\n\n\t\tfor (;;) {\n\t\t\tstd::string name;\n\t\t\tbool accepted = NameDialog::AskForName(main, QTStr(\"Basic.Main.MixerRename.Title\"),\n\t\t\t\t\t\t\t       QTStr(\"Basic.Main.MixerRename.Text\"), name,\n\t\t\t\t\t\t\t       QT_UTF8(prevName));\n\t\t\tif (!accepted) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (name.empty()) {\n\t\t\t\tOBSMessageBox::warning(main, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (name == prevName) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tOBSSourceAutoRelease sourceTest = obs_get_source_by_name(name.c_str());\n\n\t\t\tif (sourceTest) {\n\t\t\t\tOBSMessageBox::warning(main, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tstd::string prevName(obs_source_get_name(source));\n\t\t\tauto undo = [prevName](const std::string &data) {\n\t\t\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());\n\t\t\t\tobs_source_set_name(source, prevName.c_str());\n\t\t\t};\n\n\t\t\tstd::string editedName = name;\n\t\t\tauto redo = [editedName](const std::string &data) {\n\t\t\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());\n\t\t\t\tobs_source_set_name(source, editedName.c_str());\n\t\t\t};\n\n\t\t\tmain->undo_s.add_action(QTStr(\"Undo.Rename\").arg(name.c_str()), undo, redo, uuid, uuid);\n\n\t\t\tobs_source_set_name(source, name.c_str());\n\t\t\tbreak;\n\t\t}\n\t});\n}\n\nvoid VolumeControl::changeVolume()\n{\n\tQSignalBlocker blocker(slider);\n\tslider->setValue((int)(obs_fader_get_deflection(obs_fader) * FADER_PRECISION));\n\n\tupdateText();\n}\n\nvoid VolumeControl::setLocked(bool locked)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource());\n\tif (!source) {\n\t\treturn;\n\t}\n\tOBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);\n\tobs_data_set_bool(priv_settings, \"volume_locked\", locked);\n\n\tenableSlider(!locked);\n\tmixerStatus().set(VolumeControl::MixerStatus::Locked, locked);\n\n\tOBSBasic *main = OBSBasic::Get();\n\temit main->mixerStatusChanged(uuid);\n}\n\nvoid VolumeControl::updateCategoryLabel()\n{\n\tQString labelText = QTStr(\"Basic.AudioMixer.Category.Active\");\n\n\tif (mixerStatus().has(VolumeControl::MixerStatus::Unassigned)) {\n\t\tlabelText = QTStr(\"Basic.AudioMixer.Category.Unassigned\");\n\t} else if (mixerStatus().has(VolumeControl::MixerStatus::Global)) {\n\t\tlabelText = QTStr(\"Basic.AudioMixer.Category.Global\");\n\t} else if (mixerStatus().has(VolumeControl::MixerStatus::Pinned)) {\n\t\tlabelText = QTStr(\"Basic.AudioMixer.Category.Pinned\");\n\t} else if (mixerStatus().has(VolumeControl::MixerStatus::Hidden)) {\n\t\tlabelText = QTStr(\"Basic.AudioMixer.Category.Hidden\");\n\t} else if (!mixerStatus().has(VolumeControl::MixerStatus::Active)) {\n\t\tlabelText = QTStr(\"Basic.AudioMixer.Category.Inactive\");\n\n\t\tif (mixerStatus().has(VolumeControl::MixerStatus::Preview)) {\n\t\t\tlabelText = QTStr(\"Basic.AudioMixer.Category.Preview\");\n\t\t}\n\t}\n\n\tbool stylePinned = mixerStatus().has(VolumeControl::MixerStatus::Global) ||\n\t\t\t   mixerStatus().has(VolumeControl::MixerStatus::Pinned);\n\tbool styleInactive = mixerStatus().has(VolumeControl::MixerStatus::Active) != true;\n\tbool styleHidden = mixerStatus().has(VolumeControl::MixerStatus::Hidden);\n\tbool styleUnassigned = mixerStatus().has(VolumeControl::MixerStatus::Unassigned);\n\tbool stylePreviewed = mixerStatus().has(VolumeControl::MixerStatus::Preview);\n\n\tutils->toggleClass(\"volume-pinned\", stylePinned);\n\tutils->toggleClass(\"volume-inactive\", styleInactive);\n\tutils->toggleClass(\"volume-preview\", styleInactive && stylePreviewed);\n\tutils->toggleClass(\"volume-hidden\", styleHidden && !stylePinned);\n\tutils->toggleClass(\"volume-unassigned\", styleUnassigned);\n\n\tcategoryLabel->setText(labelText);\n\n\tutils->polishChildren();\n\n\tbool forceUpdate = true;\n\tvolumeMeter->updateBackgroundCache(forceUpdate);\n}\n\nvoid VolumeControl::updateDecayRate()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tdouble meterDecayRate = config_get_double(main->Config(), \"Audio\", \"MeterDecayRate\");\n\n\tsetMeterDecayRate(meterDecayRate);\n}\n\nvoid VolumeControl::updatePeakMeterType()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tuint32_t peakMeterTypeIdx = config_get_uint(main->Config(), \"Audio\", \"PeakMeterType\");\n\n\tenum obs_peak_meter_type peakMeterType;\n\tswitch (peakMeterTypeIdx) {\n\tcase 0:\n\t\tpeakMeterType = SAMPLE_PEAK_METER;\n\t\tbreak;\n\tcase 1:\n\t\tpeakMeterType = TRUE_PEAK_METER;\n\t\tbreak;\n\tdefault:\n\t\tpeakMeterType = SAMPLE_PEAK_METER;\n\t\tbreak;\n\t}\n\n\tsetPeakMeterType(peakMeterType);\n}\n\nvoid VolumeControl::setMuted(bool mute)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource());\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tbool prev = obs_source_muted(source);\n\tbool unassigned = isSourceUnassigned(source);\n\n\tobs_source_set_muted(source, mute);\n\n\tif (!mute && unassigned) {\n\t\t// Show notice about the source no being assigned to any tracks\n\t\tbool has_shown_warning =\n\t\t\tconfig_get_bool(App()->GetUserConfig(), \"General\", \"WarnedAboutUnassignedSources\");\n\t\tif (!has_shown_warning) {\n\t\t\tshowUnassignedWarning(obs_source_get_name(source));\n\t\t}\n\t}\n\n\tauto undo_redo = [](const std::string &uuid, bool val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_source_set_muted(source, val);\n\t};\n\n\tQString text = QTStr(mute ? \"Undo.Volume.Mute\" : \"Undo.Volume.Unmute\");\n\n\tconst char *name = obs_source_get_name(source);\n\tOBSBasic::Get()->undo_s.add_action(text.arg(name), std::bind(undo_redo, std::placeholders::_1, prev),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, mute), uuid, uuid);\n}\n\nvoid VolumeControl::setMonitoring(obs_monitoring_type type)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource());\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tobs_monitoring_type prevMonitoringType = obs_source_get_monitoring_type(source);\n\tobs_source_set_monitoring_type(source, type);\n\n\tauto undo_redo = [](const std::string &uuid, obs_monitoring_type val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_source_set_monitoring_type(source, val);\n\t};\n\n\tQString text = QTStr(\"Undo.MonitoringType.Change\");\n\n\tconst char *name = obs_source_get_name(source);\n\tOBSBasic::Get()->undo_s.add_action(text.arg(name),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, prevMonitoringType),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, type), uuid, uuid);\n}\n\nvoid VolumeControl::sourceActiveChanged(bool active)\n{\n\tsetUseDisabledColors(!active);\n\tmixerStatus().set(VolumeControl::MixerStatus::Active, active);\n\n\tOBSBasic *main = OBSBasic::Get();\n\temit main->mixerStatusChanged(uuid);\n}\n\nvoid VolumeControl::updateMixerState()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource());\n\tif (!source) {\n\t\tdeleteLater();\n\t\treturn;\n\t}\n\n\tbool muted = obs_source_muted(source);\n\tbool unassigned = isSourceUnassigned(source);\n\tobs_monitoring_type monitoringType = obs_source_get_monitoring_type(source);\n\n\tbool isActive = obs_source_active(source) && obs_source_audio_active(source);\n\n\tmixerStatus().set(VolumeControl::MixerStatus::Active, isActive);\n\tmixerStatus().set(VolumeControl::MixerStatus::Unassigned, unassigned);\n\n\tQSignalBlocker blockMute(muteButton);\n\tQSignalBlocker blockMonitor(monitorButton);\n\n\tbool showAsMuted = muted || monitoringType == OBS_MONITORING_TYPE_MONITOR_ONLY;\n\tbool showAsMonitored = !muted && monitoringType != OBS_MONITORING_TYPE_NONE;\n\tbool showAsUnassigned = !muted && unassigned;\n\n\tvolumeMeter->setMuted((showAsMuted || showAsUnassigned) && !showAsMonitored);\n\tsetUseDisabledColors(showAsMuted);\n\n\t// Qt doesn't support overriding the QPushButton icon using pseudo state selectors like :checked\n\t// in QSS so we set a checked class selector on the button to be used instead.\n\tutils->toggleClass(muteButton, \"checked\", showAsMuted);\n\tutils->toggleClass(monitorButton, \"checked\", showAsMonitored);\n\n\tutils->toggleClass(muteButton, \"mute-unassigned\", showAsUnassigned);\n\n\tmuteButton->setChecked(showAsMuted);\n\tmonitorButton->setChecked(showAsMonitored);\n\n\tif (showAsUnassigned) {\n\t\tQIcon unassignedIcon;\n\t\tunassignedIcon.addFile(QString::fromUtf8(\":/res/images/unassigned.svg\"), QSize(16, 16),\n\t\t\t\t       QIcon::Mode::Normal, QIcon::State::Off);\n\t\tmuteButton->setIcon(unassignedIcon);\n\t} else if (showAsMuted) {\n\t\tQIcon mutedIcon;\n\t\tmutedIcon.addFile(QString::fromUtf8(\":/res/images/mute.svg\"), QSize(16, 16), QIcon::Mode::Normal,\n\t\t\t\t  QIcon::State::Off);\n\t\tmuteButton->setIcon(mutedIcon);\n\t} else {\n\t\tQIcon unmutedIcon;\n\t\tunmutedIcon.addFile(QString::fromUtf8(\":/settings/images/settings/audio.svg\"), QSize(16, 16),\n\t\t\t\t    QIcon::Mode::Normal, QIcon::State::Off);\n\t\tmuteButton->setIcon(unmutedIcon);\n\t}\n\n\tif (showAsMonitored) {\n\t\tQIcon monitorOnIcon;\n\t\tmonitorOnIcon.addFile(QString::fromUtf8(\":/res/images/headphones.svg\"), QSize(16, 16),\n\t\t\t\t      QIcon::Mode::Normal, QIcon::State::Off);\n\t\tmonitorButton->setIcon(monitorOnIcon);\n\t} else {\n\t\tQIcon monitorOffIcon;\n\t\tmonitorOffIcon.addFile(QString::fromUtf8(\":/res/images/headphones-off.svg\"), QSize(16, 16),\n\t\t\t\t       QIcon::Mode::Normal, QIcon::State::Off);\n\t\tmonitorButton->setIcon(monitorOffIcon);\n\t}\n\n\tutils->repolish(muteButton);\n\tutils->repolish(monitorButton);\n\n\tupdateCategoryLabel();\n}\n\nvoid VolumeControl::handleMuteButton(bool mute)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource());\n\tif (!source) {\n\t\treturn;\n\t}\n\n\t// The Mute and Monitor buttons in the volume mixer work as a pseudo quad-state toggle.\n\t// Both buttons must be in their \"off\" state in order to actually process it as a mute.\n\t// Otherwise, clicking \"Mute\" with monitoring enabled will toggle the monitoring type.\n\tobs_monitoring_type monitoringType = obs_source_get_monitoring_type(source);\n\n\tif (mute && monitoringType == OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT) {\n\t\tsetMonitoring(OBS_MONITORING_TYPE_MONITOR_ONLY);\n\t} else if (!mute && monitoringType == OBS_MONITORING_TYPE_MONITOR_ONLY) {\n\t\tsetMonitoring(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);\n\t} else {\n\t\tsetMuted(mute);\n\t}\n}\n\nvoid VolumeControl::handleMonitorButton(bool enableMonitoring)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource());\n\tif (!source) {\n\t\treturn;\n\t}\n\n\t// The Mute and Monitor buttons in the volume mixer work as a pseudo quad-state toggle.\n\t// The source is only ever actually \"Muted\" if Monitoring is set to None.\n\tobs_monitoring_type monitoringType = obs_source_get_monitoring_type(source);\n\n\tbool muted = obs_source_muted(source);\n\n\tif (!enableMonitoring) {\n\t\tsetMonitoring(OBS_MONITORING_TYPE_NONE);\n\t\tif (monitoringType == OBS_MONITORING_TYPE_MONITOR_ONLY) {\n\t\t\tsetMuted(true);\n\t\t}\n\t} else if (enableMonitoring && muted) {\n\t\tsetMonitoring(OBS_MONITORING_TYPE_MONITOR_ONLY);\n\t\tsetMuted(false);\n\t} else if (enableMonitoring && !muted) {\n\t\tsetMonitoring(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);\n\t}\n}\n\nvoid VolumeControl::sliderChanged(int vol)\n{\n\tOBSSource source = OBSGetStrongRef(weakSource());\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tfloat prev = obs_source_get_volume(source);\n\n\tobs_fader_set_deflection(obs_fader, float(vol) / FADER_PRECISION);\n\tupdateText();\n\n\tauto undo_redo = [](const std::string &uuid, float val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_source_set_volume(source, val);\n\t};\n\n\tfloat val = obs_source_get_volume(source);\n\tconst char *name = obs_source_get_name(source);\n\tOBSBasic::Get()->undo_s.add_action(QTStr(\"Undo.Volume.Change\").arg(name),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, prev),\n\t\t\t\t\t   std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid, true);\n}\n\nvoid VolumeControl::updateText()\n{\n\tQString text;\n\tfloat db = obs_fader_get_db(obs_fader);\n\n\tif (db < -96.0f) {\n\t\ttext = \"-inf dB\";\n\t} else {\n\t\ttext = QString::number(db, 'f', 1).append(\" dB\");\n\t}\n\n\tvolumeLabel->setText(text);\n\n\tOBSSource source = OBSGetStrongRef(weakSource());\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tbool muted = obs_source_muted(source);\n\tconst char *accTextLookup = muted ? \"VolControl.SliderMuted\" : \"VolControl.SliderUnmuted\";\n\n\tQString sourceName = obs_source_get_name(source);\n\tQString accText = QTStr(accTextLookup).arg(sourceName);\n\n\tslider->setAccessibleName(accText);\n}\n\nvoid VolumeControl::setVertical(bool vertical_)\n{\n\tif (vertical == vertical_) {\n\t\treturn;\n\t}\n\n\tvertical = vertical_;\n\n\tsetLayoutVertical(vertical);\n}\n\nvoid VolumeControl::updateTabOrder()\n{\n\tQWidget *prevFocus = firstWidget()->previousInFocusChain();\n\tQWidget *lastFocus = lastWidget()->nextInFocusChain();\n\n\tif (vertical) {\n\t\tsetTabOrder(prevFocus, nameButton);\n\t\tsetTabOrder(nameButton, slider);\n\t\tsetTabOrder(slider, muteButton);\n\t\tsetTabOrder(muteButton, monitorButton);\n\t\tsetTabOrder(monitorButton, lastFocus);\n\t} else {\n\t\tsetTabOrder(prevFocus, nameButton);\n\t\tsetTabOrder(nameButton, muteButton);\n\t\tsetTabOrder(muteButton, monitorButton);\n\t\tsetTabOrder(monitorButton, slider);\n\t\tsetTabOrder(slider, lastFocus);\n\t}\n}\n\nvoid VolumeControl::updateName()\n{\n\tsetName(sourceName);\n}\n\nvoid VolumeControl::setName(QString name)\n{\n\tsourceName = name;\n\n\tmuteButton->setAccessibleName(QTStr(\"VolControl.Mute\").arg(name));\n}\n\nvoid VolumeControl::setMeterDecayRate(qreal q)\n{\n\tvolumeMeter->setPeakDecayRate(q);\n}\n\nvoid VolumeControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType)\n{\n\tvolumeMeter->setPeakMeterType(peakMeterType);\n}\n\nvoid VolumeControl::enableSlider(bool enable)\n{\n\tslider->setEnabled(enable);\n}\n\nvoid VolumeControl::setUseDisabledColors(bool greyscale)\n{\n\tvolumeMeter->setUseDisabledColors(greyscale);\n}\n\nvoid VolumeControl::setGlobalInMixer(bool global)\n{\n\tif (mixerStatus().has(VolumeControl::MixerStatus::Global) != global) {\n\t\tmixerStatus().set(VolumeControl::MixerStatus::Global, global);\n\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\temit main->mixerStatusChanged(uuid);\n\t}\n}\n\nvoid VolumeControl::setPinnedInMixer(bool pinned)\n{\n\tif (mixerStatus().has(VolumeControl::MixerStatus::Pinned) != pinned) {\n\t\tOBSSource source = OBSGetStrongRef(weakSource());\n\t\tif (!source) {\n\t\t\treturn;\n\t\t}\n\t\tOBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);\n\t\tobs_data_set_bool(priv_settings, \"mixer_pinned\", pinned);\n\n\t\tmixerStatus().set(VolumeControl::MixerStatus::Pinned, pinned);\n\n\t\tif (pinned) {\n\t\t\t// Unset hidden state when pinning controls\n\t\t\tsetHiddenInMixer(false);\n\t\t}\n\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\temit main->mixerStatusChanged(uuid);\n\t}\n}\n\nvoid VolumeControl::setHiddenInMixer(bool hidden)\n{\n\tif (mixerStatus().has(VolumeControl::MixerStatus::Hidden) != hidden) {\n\t\tOBSSource source = OBSGetStrongRef(weakSource());\n\t\tif (!source) {\n\t\t\treturn;\n\t\t}\n\t\tOBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);\n\t\tobs_data_set_bool(priv_settings, \"mixer_hidden\", hidden);\n\n\t\tmixerStatus().set(VolumeControl::MixerStatus::Hidden, hidden);\n\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\temit main->mixerStatusChanged(uuid);\n\t}\n}\n\nvoid VolumeControl::refreshColors()\n{\n\tvolumeMeter->refreshColors();\n}\n\nvoid VolumeControl::setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],\n\t\t\t      const float inputPeak[MAX_AUDIO_CHANNELS])\n{\n\tif (volumeMeter) {\n\t\tvolumeMeter->setLevels(magnitude, peak, inputPeak);\n\t}\n}\n"
  },
  {
    "path": "frontend/components/VolumeControl.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <components/VolumeName.hpp>\n#include <components/VolumeSlider.hpp>\n\n#include <Idian/Utils.hpp>\n#include <QFrame>\n#include <QPushButton>\n#include <QWidget>\n\nclass MuteCheckBox;\nclass QBoxLayout;\nclass QLabel;\nclass VolumeMeter;\n\nclass VolumeControl : public QFrame {\n\tQ_OBJECT\n\npublic:\n\tstruct MixerStatus {\n\t\tenum Value : uint32_t {\n\t\t\tNone = 0,\n\t\t\tActive = 1 << 0,\n\t\t\tLocked = 1 << 1,\n\t\t\tGlobal = 1 << 2,\n\t\t\tPinned = 1 << 3,\n\t\t\tHidden = 1 << 4,\n\t\t\tUnassigned = 1 << 5,\n\t\t\tPreview = 1 << 6,\n\t\t};\n\n\t\tMixerStatus() = default;\n\t\tMixerStatus(Value v) : bits(v) {}\n\n\t\tbool has(Value v) const { return (bits & v) != 0; }\n\t\tvoid set(Value v, bool enable)\n\t\t{\n\t\t\tif (enable) {\n\t\t\t\tbits |= v;\n\t\t\t} else {\n\t\t\t\tbits &= ~v;\n\t\t\t}\n\t\t}\n\n\t\tMixerStatus operator|(Value v) const { return MixerStatus(bits | v); }\n\t\tMixerStatus &operator|=(Value v)\n\t\t{\n\t\t\tbits |= v;\n\t\t\treturn *this;\n\t\t}\n\n\t\tMixerStatus operator&(Value v) const { return MixerStatus(bits & v); }\n\t\tMixerStatus &operator&=(Value v)\n\t\t{\n\t\t\tbits &= v;\n\t\t\treturn *this;\n\t\t}\n\n\t\tMixerStatus operator~() const { return MixerStatus(~bits); }\n\n\tprivate:\n\t\tuint32_t bits = None;\n\t\texplicit MixerStatus(uint32_t v) : bits(v) {}\n\t};\n\nprivate:\n\tstd::unique_ptr<idian::Utils> utils;\n\n\tOBSWeakSource weakSource_;\n\tconst char *uuid;\n\tstd::vector<OBSSignal> obsSignals;\n\n\tQBoxLayout *mainLayout;\n\tQLabel *categoryLabel;\n\tVolumeName *nameButton;\n\tQLabel *volumeLabel;\n\tVolumeMeter *volumeMeter;\n\tVolumeSlider *slider;\n\tQPushButton *muteButton;\n\tQPushButton *monitorButton;\n\n\tOBSFader obs_fader;\n\n\tQString sourceName;\n\tbool vertical;\n\n\tMixerStatus mixerStatus_;\n\n\tQMenu *contextMenu;\n\n\tstatic void obsVolumeChanged(void *param, float db);\n\tstatic void obsVolumeMuted(void *data, calldata_t *calldata);\n\tstatic void obsMixersOrMonitoringChanged(void *data, calldata_t *);\n\tstatic void obsSourceActivated(void *data, calldata_t *params);\n\tstatic void obsSourceDeactivated(void *data, calldata_t *params);\n\tstatic void obsSourceDestroy(void *data, calldata_t *params);\n\n\tvoid setLayoutVertical(bool vertical);\n\tvoid showVolumeControlMenu(QPoint pos = QPoint(0, 0));\n\tvoid updateCategoryLabel();\n\tvoid updateDecayRate();\n\tvoid updatePeakMeterType();\n\n\tvoid setMuted(bool mute);\n\tvoid setMonitoring(obs_monitoring_type type);\n\npublic slots:\n\tvoid sourceActiveChanged(bool active);\n\tvoid setUseDisabledColors(bool greyscale);\n\tvoid setLocked(bool locked);\n\tvoid updateMixerState();\n\nprivate slots:\n\tvoid renameSource();\n\tvoid changeVolume();\n\n\tvoid handleMuteButton(bool checked);\n\tvoid handleMonitorButton(bool checked);\n\tvoid sliderChanged(int vol);\n\tvoid updateText();\n\tvoid setName(QString name);\n\n\tvoid handleSourceDestroyed() { deleteLater(); }\n\nsignals:\n\tvoid unhideAll();\n\npublic:\n\texplicit VolumeControl(obs_source_t *source, QWidget *parent, bool vertical);\n\texplicit VolumeControl(obs_source_t *source, QWidget *parent) : VolumeControl(source, parent, false) {}\n\t~VolumeControl();\n\n\tinline OBSWeakSource weakSource() const { return weakSource_; }\n\tQString const &getCachedName() const { return sourceName; }\n\n\tvoid setMeterDecayRate(qreal q);\n\tvoid setPeakMeterType(enum obs_peak_meter_type peakMeterType);\n\n\tvoid enableSlider(bool enable);\n\tMixerStatus &mixerStatus() { return mixerStatus_; }\n\tvoid setGlobalInMixer(bool global);\n\tvoid setPinnedInMixer(bool pinned);\n\tvoid setHiddenInMixer(bool hidden);\n\tvoid setContextMenu(QMenu *cm) { contextMenu = cm; }\n\n\tbool isVertical() const { return vertical; }\n\tvoid setVertical(bool vertical);\n\n\tvoid updateTabOrder();\n\tQWidget *firstWidget() const { return qobject_cast<QWidget *>(nameButton); }\n\tQWidget *lastWidget() const\n\t{\n\t\treturn vertical ? qobject_cast<QWidget *>(monitorButton) : qobject_cast<QWidget *>(slider);\n\t}\n\n\tvoid updateName();\n\tvoid refreshColors();\n\tvoid setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],\n\t\t       const float inputPeak[MAX_AUDIO_CHANNELS]);\n};\n"
  },
  {
    "path": "frontend/components/VolumeMeter.cpp",
    "content": "#include \"VolumeMeter.hpp\"\n\n#include <OBSApp.hpp>\n#include <QEvent>\n#include <QMouseEvent>\n#include <QPainter>\n#include <QStyleOption>\n#include <QTimer>\n\n#include \"moc_VolumeMeter.cpp\"\n\nQPointer<QTimer> VolumeMeter::updateTimer = nullptr;\n\nnamespace {\nconstexpr int INDICATOR_THICKNESS = 3;\nconstexpr int CLIP_FLASH_DURATION_MS = 1000;\nconstexpr int TICK_SIZE = 2;\nconstexpr int TICK_DB_INTERVAL = 6;\n} // namespace\n\nstatic inline QColor color_from_int(long long val)\n{\n\tQColor color(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);\n\tcolor.setAlpha(255);\n\n\treturn color;\n}\n\nQColor VolumeMeter::getBackgroundNominalColor() const\n{\n\treturn p_backgroundNominalColor;\n}\n\nQColor VolumeMeter::getBackgroundNominalColorDisabled() const\n{\n\treturn backgroundNominalColorDisabled;\n}\n\nvoid VolumeMeter::setBackgroundNominalColor(QColor c)\n{\n\tp_backgroundNominalColor = std::move(c);\n\n\tif (config_get_bool(App()->GetUserConfig(), \"Accessibility\", \"OverrideColors\")) {\n\t\tbackgroundNominalColor =\n\t\t\tcolor_from_int(config_get_int(App()->GetUserConfig(), \"Accessibility\", \"MixerGreen\"));\n\t} else {\n\t\tbackgroundNominalColor = p_backgroundNominalColor;\n\t}\n}\n\nvoid VolumeMeter::setBackgroundNominalColorDisabled(QColor c)\n{\n\tbackgroundNominalColorDisabled = std::move(c);\n}\n\nQColor VolumeMeter::getBackgroundWarningColor() const\n{\n\treturn p_backgroundWarningColor;\n}\n\nQColor VolumeMeter::getBackgroundWarningColorDisabled() const\n{\n\treturn backgroundWarningColorDisabled;\n}\n\nvoid VolumeMeter::setBackgroundWarningColor(QColor c)\n{\n\tp_backgroundWarningColor = std::move(c);\n\n\tif (config_get_bool(App()->GetUserConfig(), \"Accessibility\", \"OverrideColors\")) {\n\t\tbackgroundWarningColor =\n\t\t\tcolor_from_int(config_get_int(App()->GetUserConfig(), \"Accessibility\", \"MixerYellow\"));\n\t} else {\n\t\tbackgroundWarningColor = p_backgroundWarningColor;\n\t}\n}\n\nvoid VolumeMeter::setBackgroundWarningColorDisabled(QColor c)\n{\n\tbackgroundWarningColorDisabled = std::move(c);\n}\n\nQColor VolumeMeter::getBackgroundErrorColor() const\n{\n\treturn p_backgroundErrorColor;\n}\n\nQColor VolumeMeter::getBackgroundErrorColorDisabled() const\n{\n\treturn backgroundErrorColorDisabled;\n}\n\nvoid VolumeMeter::setBackgroundErrorColor(QColor c)\n{\n\tp_backgroundErrorColor = std::move(c);\n\n\tif (config_get_bool(App()->GetUserConfig(), \"Accessibility\", \"OverrideColors\")) {\n\t\tbackgroundErrorColor =\n\t\t\tcolor_from_int(config_get_int(App()->GetUserConfig(), \"Accessibility\", \"MixerRed\"));\n\t} else {\n\t\tbackgroundErrorColor = p_backgroundErrorColor;\n\t}\n}\n\nvoid VolumeMeter::setBackgroundErrorColorDisabled(QColor c)\n{\n\tbackgroundErrorColorDisabled = std::move(c);\n}\n\nQColor VolumeMeter::getForegroundNominalColor() const\n{\n\treturn p_foregroundNominalColor;\n}\n\nQColor VolumeMeter::getForegroundNominalColorDisabled() const\n{\n\treturn foregroundNominalColorDisabled;\n}\n\nvoid VolumeMeter::setForegroundNominalColor(QColor c)\n{\n\tp_foregroundNominalColor = std::move(c);\n\n\tif (config_get_bool(App()->GetUserConfig(), \"Accessibility\", \"OverrideColors\")) {\n\t\tforegroundNominalColor =\n\t\t\tcolor_from_int(config_get_int(App()->GetUserConfig(), \"Accessibility\", \"MixerGreenActive\"));\n\t} else {\n\t\tforegroundNominalColor = p_foregroundNominalColor;\n\t}\n}\n\nvoid VolumeMeter::setForegroundNominalColorDisabled(QColor c)\n{\n\tforegroundNominalColorDisabled = std::move(c);\n}\n\nQColor VolumeMeter::getForegroundWarningColor() const\n{\n\treturn p_foregroundWarningColor;\n}\n\nQColor VolumeMeter::getForegroundWarningColorDisabled() const\n{\n\treturn foregroundWarningColorDisabled;\n}\n\nvoid VolumeMeter::setForegroundWarningColor(QColor c)\n{\n\tp_foregroundWarningColor = std::move(c);\n\n\tif (config_get_bool(App()->GetUserConfig(), \"Accessibility\", \"OverrideColors\")) {\n\t\tforegroundWarningColor =\n\t\t\tcolor_from_int(config_get_int(App()->GetUserConfig(), \"Accessibility\", \"MixerYellowActive\"));\n\t} else {\n\t\tforegroundWarningColor = p_foregroundWarningColor;\n\t}\n}\n\nvoid VolumeMeter::setForegroundWarningColorDisabled(QColor c)\n{\n\tforegroundWarningColorDisabled = std::move(c);\n}\n\nQColor VolumeMeter::getForegroundErrorColor() const\n{\n\treturn p_foregroundErrorColor;\n}\n\nQColor VolumeMeter::getForegroundErrorColorDisabled() const\n{\n\treturn foregroundErrorColorDisabled;\n}\n\nvoid VolumeMeter::setForegroundErrorColor(QColor c)\n{\n\tp_foregroundErrorColor = std::move(c);\n\n\tif (config_get_bool(App()->GetUserConfig(), \"Accessibility\", \"OverrideColors\")) {\n\t\tforegroundErrorColor =\n\t\t\tcolor_from_int(config_get_int(App()->GetUserConfig(), \"Accessibility\", \"MixerRedActive\"));\n\t} else {\n\t\tforegroundErrorColor = p_foregroundErrorColor;\n\t}\n}\n\nvoid VolumeMeter::setForegroundErrorColorDisabled(QColor c)\n{\n\tforegroundErrorColorDisabled = std::move(c);\n}\n\nQColor VolumeMeter::getMagnitudeColor() const\n{\n\treturn magnitudeColor;\n}\n\nvoid VolumeMeter::setMagnitudeColor(QColor c)\n{\n\tmagnitudeColor = std::move(c);\n}\n\nQColor VolumeMeter::getMajorTickColor() const\n{\n\treturn majorTickColor;\n}\n\nvoid VolumeMeter::setMajorTickColor(QColor c)\n{\n\tmajorTickColor = std::move(c);\n}\n\nQColor VolumeMeter::getMinorTickColor() const\n{\n\treturn minorTickColor;\n}\n\nvoid VolumeMeter::setMinorTickColor(QColor c)\n{\n\tminorTickColor = std::move(c);\n}\n\nqreal VolumeMeter::getWarningLevel() const\n{\n\treturn warningLevel;\n}\n\nvoid VolumeMeter::setWarningLevel(qreal v)\n{\n\twarningLevel = v;\n}\n\nqreal VolumeMeter::getErrorLevel() const\n{\n\treturn errorLevel;\n}\n\nvoid VolumeMeter::setErrorLevel(qreal v)\n{\n\terrorLevel = v;\n}\n\nvoid VolumeMeter::setPeakDecayRate(qreal decayRate)\n{\n\tpeakDecayRate = decayRate;\n}\n\nvoid VolumeMeter::setPeakMeterType(enum obs_peak_meter_type peakMeterType)\n{\n\tobs_volmeter_set_peak_meter_type(obsVolumeMeter, peakMeterType);\n\tswitch (peakMeterType) {\n\tcase TRUE_PEAK_METER:\n\t\t// For true-peak meters EBU has defined the Permitted Maximum,\n\t\t// taking into account the accuracy of the meter and further\n\t\t// processing required by lossy audio compression.\n\t\t//\n\t\t// The alignment level was not specified, but I've adjusted\n\t\t// it compared to a sample-peak meter. Incidentally Youtube\n\t\t// uses this new Alignment Level as the maximum integrated\n\t\t// loudness of a video.\n\t\t//\n\t\t//  * Permitted Maximum Level (PML) = -2.0 dBTP\n\t\t//  * Alignment Level (AL) = -13 dBTP\n\t\tsetErrorLevel(-2.0);\n\t\tsetWarningLevel(-13.0);\n\t\tbreak;\n\n\tcase SAMPLE_PEAK_METER:\n\tdefault:\n\t\t// For a sample Peak Meter EBU has the following level\n\t\t// definitions, taking into account inaccuracies of this meter:\n\t\t//\n\t\t//  * Permitted Maximum Level (PML) = -9.0 dBFS\n\t\t//  * Alignment Level (AL) = -20.0 dBFS\n\t\tsetErrorLevel(-9.0);\n\t\tsetWarningLevel(-20.0);\n\t\tbreak;\n\t}\n\n\tbool forceUpdate = true;\n\tupdateBackgroundCache(forceUpdate);\n}\n\nVolumeMeter::VolumeMeter(QWidget *parent, obs_source_t *source)\n\t: QWidget(parent),\n\t  weakSource(OBSGetWeakRef(source)),\n\t  obsVolumeMeter(obs_volmeter_create(OBS_FADER_LOG))\n{\n\tsetAttribute(Qt::WA_OpaquePaintEvent, true);\n\tsetAttribute(Qt::WA_TransparentForMouseEvents);\n\tsetFocusPolicy(Qt::NoFocus);\n\n\t// Default meter settings, they only show if\n\t// there is no stylesheet, do not remove.\n\tbackgroundNominalColor.setRgb(0x26, 0x7f, 0x26); // Dark green\n\tbackgroundWarningColor.setRgb(0x7f, 0x7f, 0x26); // Dark yellow\n\tbackgroundErrorColor.setRgb(0x7f, 0x26, 0x26);   // Dark red\n\tforegroundNominalColor.setRgb(0x4c, 0xff, 0x4c); // Bright green\n\tforegroundWarningColor.setRgb(0xff, 0xff, 0x4c); // Bright yellow\n\tforegroundErrorColor.setRgb(0xff, 0x4c, 0x4c);   // Bright red\n\n\tbackgroundNominalColorDisabled.setRgb(90, 90, 90);\n\tbackgroundWarningColorDisabled.setRgb(117, 117, 117);\n\tbackgroundErrorColorDisabled.setRgb(65, 65, 65);\n\tforegroundNominalColorDisabled.setRgb(163, 163, 163);\n\tforegroundWarningColorDisabled.setRgb(217, 217, 217);\n\tforegroundErrorColorDisabled.setRgb(113, 113, 113);\n\n\tclipColor.setRgb(0xff, 0xff, 0xff);      // Bright white\n\tmagnitudeColor.setRgb(0x00, 0x00, 0x00); // Black\n\tmajorTickColor.setRgb(0x00, 0x00, 0x00); // Black\n\tminorTickColor.setRgb(0x32, 0x32, 0x32); // Dark gray\n\tminimumLevel = -60.0;                    // -60 dB\n\twarningLevel = -20.0;                    // -20 dB\n\terrorLevel = -9.0;                       //  -9 dB\n\tclipLevel = 0.0;                         //  0 dB\n\tminimumInputLevel = -50.0;               // -50 dB\n\tpeakDecayRate = 11.76;                   //  20 dB / 1.7 sec\n\tmagnitudeIntegrationTime = 0.3;          //  99% in 300 ms\n\tpeakHoldDuration = 20.0;                 //  20 seconds\n\tinputPeakHoldDuration = 1.0;             //  1 second\n\tmeterThickness = 3;                      // Bar thickness in pixels\n\tmeterFontScaling = 0.8;                  // Font size for numbers is 80% of Widget's font size\n\tchannels = (int)audio_output_get_channels(obs_get_audio());\n\n\tobs_volmeter_add_callback(obsVolumeMeter, obsVolMeterChanged, this);\n\tobs_volmeter_attach_source(obsVolumeMeter, source);\n\n\tdestroyedSignal =\n\t\tOBSSignal(obs_source_get_signal_handler(source), \"destroy\", &VolumeMeter::obsSourceDestroyed, this);\n\n\tdoLayout();\n\n\tif (!updateTimer) {\n\t\tupdateTimer = new QTimer(qApp);\n\t\tupdateTimer->setTimerType(Qt::PreciseTimer);\n\t\tupdateTimer->start(16);\n\t}\n\n\tconnect(updateTimer, &QTimer::timeout, this, [this]() {\n\t\tif (needLayoutChange()) {\n\t\t\tdoLayout();\n\t\t\tupdate();\n\t\t} else {\n\t\t\tupdate(getBarRect());\n\t\t}\n\t});\n\n\tconnect(App(), &OBSApp::StyleChanged, this, &VolumeMeter::doLayout);\n}\n\nVolumeMeter::~VolumeMeter()\n{\n\tobs_volmeter_remove_callback(obsVolumeMeter, obsVolMeterChanged, this);\n\tobs_volmeter_detach_source(obsVolumeMeter);\n}\n\nvoid VolumeMeter::obsSourceDestroyed(void *data, calldata_t *)\n{\n\tVolumeMeter *self = static_cast<VolumeMeter *>(data);\n\tQMetaObject::invokeMethod(self, \"handleSourceDestroyed\", Qt::QueuedConnection);\n}\n\nvoid VolumeMeter::setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],\n\t\t\t    const float inputPeak[MAX_AUDIO_CHANNELS])\n{\n\tuint64_t ts = os_gettime_ns();\n\tQMutexLocker locker(&dataMutex);\n\n\tcurrentLastUpdateTime = ts;\n\tfor (int channelNr = 0; channelNr < MAX_AUDIO_CHANNELS; channelNr++) {\n\t\tcurrentMagnitude[channelNr] = magnitude[channelNr];\n\t\tcurrentPeak[channelNr] = peak[channelNr];\n\t\tcurrentInputPeak[channelNr] = inputPeak[channelNr];\n\t}\n\n\t// In case there are more updates then redraws we must make sure\n\t// that the ballistics of peak and hold are recalculated.\n\tlocker.unlock();\n\tcalculateBallistics(ts);\n}\n\nvoid VolumeMeter::obsVolMeterChanged(void *data, const float magnitude[MAX_AUDIO_CHANNELS],\n\t\t\t\t     const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS])\n{\n\tVolumeMeter *meter = static_cast<VolumeMeter *>(data);\n\n\tmeter->setLevels(magnitude, peak, inputPeak);\n}\n\ninline void VolumeMeter::resetLevels()\n{\n\tcurrentLastUpdateTime = 0;\n\tfor (int channelNr = 0; channelNr < MAX_AUDIO_CHANNELS; channelNr++) {\n\t\tcurrentMagnitude[channelNr] = -M_INFINITE;\n\t\tcurrentPeak[channelNr] = -M_INFINITE;\n\t\tcurrentInputPeak[channelNr] = -M_INFINITE;\n\n\t\tdisplayMagnitude[channelNr] = -M_INFINITE;\n\t\tdisplayPeak[channelNr] = -M_INFINITE;\n\t\tdisplayPeakHold[channelNr] = -M_INFINITE;\n\t\tdisplayPeakHoldLastUpdateTime[channelNr] = 0;\n\t\tdisplayInputPeakHold[channelNr] = -M_INFINITE;\n\t\tdisplayInputPeakHoldLastUpdateTime[channelNr] = 0;\n\t}\n}\n\nbool VolumeMeter::needLayoutChange()\n{\n\tint currentNrAudioChannels = obs_volmeter_get_nr_channels(obsVolumeMeter);\n\n\tif (!currentNrAudioChannels) {\n\t\tstruct obs_audio_info oai;\n\t\tobs_get_audio_info(&oai);\n\t\tcurrentNrAudioChannels = (oai.speakers == SPEAKERS_MONO) ? 1 : 2;\n\t}\n\n\tif (displayNrAudioChannels != currentNrAudioChannels) {\n\t\tdisplayNrAudioChannels = currentNrAudioChannels;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid VolumeMeter::setVertical(bool vertical_)\n{\n\tif (vertical == vertical_) {\n\t\treturn;\n\t}\n\n\tvertical = vertical_;\n\tdoLayout();\n}\n\nvoid VolumeMeter::setUseDisabledColors(bool enable)\n{\n\tif (useDisabledColors == enable) {\n\t\treturn;\n\t}\n\n\tuseDisabledColors = enable;\n\n\tbool forceUpdate = true;\n\tupdateBackgroundCache(forceUpdate);\n}\n\nvoid VolumeMeter::setMuted(bool mute)\n{\n\tif (muted == mute) {\n\t\treturn;\n\t}\n\n\tmuted = mute;\n}\n\nvoid VolumeMeter::refreshColors()\n{\n\tsetBackgroundNominalColor(getBackgroundNominalColor());\n\tsetBackgroundWarningColor(getBackgroundWarningColor());\n\tsetBackgroundErrorColor(getBackgroundErrorColor());\n\tsetForegroundNominalColor(getForegroundNominalColor());\n\tsetForegroundWarningColor(getForegroundWarningColor());\n\tsetForegroundErrorColor(getForegroundErrorColor());\n\n\tbool forceUpdate = true;\n\tupdateBackgroundCache(forceUpdate);\n}\n\nQRect VolumeMeter::getBarRect() const\n{\n\tQRect barRect = rect();\n\tif (vertical) {\n\t\tbarRect.setWidth(displayNrAudioChannels * (meterThickness + 1) - 1);\n\t} else {\n\t\tbarRect.setHeight(displayNrAudioChannels * (meterThickness + 1) - 1);\n\t}\n\n\treturn barRect;\n}\n\n// When this is called from the constructor, obs_volmeter_get_nr_channels has not\n// yet been called and Q_PROPERTY settings have not yet been read from the\n// stylesheet.\ninline void VolumeMeter::doLayout()\n{\n\tQMutexLocker locker(&dataMutex);\n\n\tif (displayNrAudioChannels) {\n\t\tint meterSize = std::floor(22 / displayNrAudioChannels);\n\t\tmeterThickness = std::clamp(meterSize, 3, 6);\n\t}\n\n\ttickFont = font();\n\tQFontInfo info(tickFont);\n\ttickFont.setPointSizeF(info.pointSizeF() * meterFontScaling);\n\n\tQFontMetrics metrics(tickFont);\n\t// This is a quick and naive assumption for widest potential tick label.\n\ttickTextTokenRect = metrics.boundingRect(\" -88 \");\n\n\tupdateBackgroundCache();\n\tresetLevels();\n\n\tupdateGeometry();\n}\n\ninline bool VolumeMeter::detectIdle(uint64_t ts)\n{\n\tdouble secondsSinceLastUpdate = (ts - currentLastUpdateTime) * 0.000000001;\n\tif (secondsSinceLastUpdate > 0.5) {\n\t\tresetLevels();\n\t\treturn true;\n\t} else {\n\t\treturn false;\n\t}\n}\n\ninline void VolumeMeter::calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw)\n{\n\tif (currentPeak[channelNr] >= displayPeak[channelNr] || isnan(displayPeak[channelNr])) {\n\t\t// Attack of peak is immediate.\n\t\tdisplayPeak[channelNr] = currentPeak[channelNr];\n\t} else {\n\t\t// Decay of peak is 40 dB / 1.7 seconds for Fast Profile\n\t\t// 20 dB / 1.7 seconds for Medium Profile (Type I PPM)\n\t\t// 24 dB / 2.8 seconds for Slow Profile (Type II PPM)\n\t\tfloat decay = float(peakDecayRate * timeSinceLastRedraw);\n\t\tdisplayPeak[channelNr] =\n\t\t\tstd::clamp(displayPeak[channelNr] - decay, std::min(currentPeak[channelNr], 0.f), 0.f);\n\t}\n\n\tif (currentPeak[channelNr] >= displayPeakHold[channelNr] || !isfinite(displayPeakHold[channelNr])) {\n\t\t// Attack of peak-hold is immediate, but keep track\n\t\t// when it was last updated.\n\t\tdisplayPeakHold[channelNr] = currentPeak[channelNr];\n\t\tdisplayPeakHoldLastUpdateTime[channelNr] = ts;\n\t} else {\n\t\t// The peak and hold falls back to peak\n\t\t// after 20 seconds.\n\t\tqreal timeSinceLastPeak = (uint64_t)(ts - displayPeakHoldLastUpdateTime[channelNr]) * 0.000000001;\n\t\tif (timeSinceLastPeak > peakHoldDuration) {\n\t\t\tdisplayPeakHold[channelNr] = currentPeak[channelNr];\n\t\t\tdisplayPeakHoldLastUpdateTime[channelNr] = ts;\n\t\t}\n\t}\n\n\tif (currentInputPeak[channelNr] >= displayInputPeakHold[channelNr] ||\n\t    !isfinite(displayInputPeakHold[channelNr])) {\n\t\t// Attack of peak-hold is immediate, but keep track\n\t\t// when it was last updated.\n\t\tdisplayInputPeakHold[channelNr] = currentInputPeak[channelNr];\n\t\tdisplayInputPeakHoldLastUpdateTime[channelNr] = ts;\n\t} else {\n\t\t// The peak and hold falls back to peak after 1 second.\n\t\tqreal timeSinceLastPeak = (uint64_t)(ts - displayInputPeakHoldLastUpdateTime[channelNr]) * 0.000000001;\n\t\tif (timeSinceLastPeak > inputPeakHoldDuration) {\n\t\t\tdisplayInputPeakHold[channelNr] = currentInputPeak[channelNr];\n\t\t\tdisplayInputPeakHoldLastUpdateTime[channelNr] = ts;\n\t\t}\n\t}\n\n\tif (!isfinite(displayMagnitude[channelNr])) {\n\t\t// The statements in the else-leg do not work with\n\t\t// NaN and infinite displayMagnitude.\n\t\tdisplayMagnitude[channelNr] = currentMagnitude[channelNr];\n\t} else {\n\t\t// A VU meter will integrate to the new value to 99% in 300 ms.\n\t\t// The calculation here is very simplified and is more accurate\n\t\t// with higher frame-rate.\n\t\tfloat attack = float((currentMagnitude[channelNr] - displayMagnitude[channelNr]) *\n\t\t\t\t     (timeSinceLastRedraw / magnitudeIntegrationTime) * 0.99);\n\t\tdisplayMagnitude[channelNr] =\n\t\t\tstd::clamp(displayMagnitude[channelNr] + attack, (float)minimumLevel, 0.f);\n\t}\n}\n\ninline void VolumeMeter::calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw)\n{\n\tQMutexLocker locker(&dataMutex);\n\n\tfor (int channelNr = 0; channelNr < MAX_AUDIO_CHANNELS; channelNr++) {\n\t\tcalculateBallisticsForChannel(channelNr, ts, timeSinceLastRedraw);\n\t}\n}\n\nQColor VolumeMeter::getPeakColor(float peakHold)\n{\n\tQColor color;\n\n\tif (peakHold < minimumInputLevel) {\n\t\tcolor = backgroundNominalColor;\n\t} else if (peakHold < warningLevel) {\n\t\tcolor = foregroundNominalColor;\n\t} else if (peakHold < errorLevel) {\n\t\tcolor = foregroundWarningColor;\n\t} else if (peakHold < clipLevel) {\n\t\tcolor = foregroundErrorColor;\n\t} else {\n\t\tcolor = clipColor;\n\t}\n\n\treturn color;\n}\n\nvoid VolumeMeter::paintHTicks(QPainter &painter, int x, int y, int width)\n{\n\tqreal scale = width / minimumLevel;\n\n\tpainter.setFont(tickFont);\n\tQFontMetrics metrics(tickFont);\n\tpainter.setPen(majorTickColor);\n\n\t// Draw major tick lines and numeric indicators.\n\tfor (int i = 0; i >= minimumLevel; i -= TICK_DB_INTERVAL) {\n\t\tint position = int(x + width - (i * scale) - 1);\n\t\tQString str = QString::number(i);\n\n\t\t// Center the number on the tick, but don't overflow\n\t\tQRect textBounds = metrics.boundingRect(str);\n\t\tint pos;\n\t\tif (i == 0) {\n\t\t\tpos = position - textBounds.width();\n\t\t} else {\n\t\t\tpos = position - (textBounds.width() / 2);\n\t\t\tif (pos < 0) {\n\t\t\t\tpos = 0;\n\t\t\t}\n\t\t}\n\t\tpainter.drawText(pos, y + 4 + metrics.capHeight(), str);\n\n\t\tpainter.drawLine(position, y, position, y + TICK_SIZE);\n\t}\n}\n\nvoid VolumeMeter::paintVTicks(QPainter &painter, int x, int y, int height)\n{\n\tqreal scale = height / minimumLevel;\n\n\tpainter.setFont(tickFont);\n\tQFontMetrics metrics(tickFont);\n\tpainter.setPen(majorTickColor);\n\n\t// Draw major tick lines and numeric indicators.\n\tfor (int i = 0; i >= minimumLevel; i -= TICK_DB_INTERVAL) {\n\t\tint position = y + int(i * scale);\n\t\tQString str = QString::number(i);\n\n\t\t// Center the number on the tick, but don't overflow\n\t\tif (i == 0) {\n\t\t\tpainter.drawText(x + 10, position + metrics.capHeight(), str);\n\t\t} else {\n\t\t\tpainter.drawText(x + 8, position + (metrics.capHeight() / 2), str);\n\t\t}\n\n\t\tpainter.drawLine(x, position, x + TICK_SIZE, position);\n\t}\n}\n\nvoid VolumeMeter::updateBackgroundCache(bool force)\n{\n\tif (!force && size().isEmpty()) {\n\t\treturn;\n\t}\n\n\tif (!force && backgroundCache.size() == size() && !backgroundCache.isNull()) {\n\t\treturn;\n\t}\n\n\tif (!force && displayNrAudioChannels <= 0) {\n\t\treturn;\n\t}\n\n\tQColor backgroundColor = palette().color(QPalette::Window);\n\n\tbackgroundCache = QPixmap(size() * devicePixelRatioF());\n\tbackgroundCache.setDevicePixelRatio(devicePixelRatioF());\n\tbackgroundCache.fill(backgroundColor);\n\n\tQPainter bg{&backgroundCache};\n\tQRect widgetRect = rect();\n\n\t// Draw ticks\n\tif (vertical) {\n\t\tpaintVTicks(bg, displayNrAudioChannels * (meterThickness + 1) - 1, 0,\n\t\t\t    widgetRect.height() - (INDICATOR_THICKNESS + 3));\n\t} else {\n\t\tpaintHTicks(bg, INDICATOR_THICKNESS + 3, displayNrAudioChannels * (meterThickness + 1) - 1,\n\t\t\t    widgetRect.width() - (INDICATOR_THICKNESS + 3));\n\t}\n\n\t// Draw meter backgrounds\n\tbool disabledColors = muted || useDisabledColors;\n\tQColor nominal = disabledColors ? backgroundNominalColorDisabled : backgroundNominalColor;\n\tQColor warning = disabledColors ? backgroundWarningColorDisabled : backgroundWarningColor;\n\tQColor error = disabledColors ? backgroundErrorColorDisabled : backgroundErrorColor;\n\n\tint meterStart = INDICATOR_THICKNESS + 2;\n\tint meterLength = vertical ? rect().height() - (INDICATOR_THICKNESS + 2)\n\t\t\t\t   : rect().width() - (INDICATOR_THICKNESS + 2);\n\n\tqreal scale = meterLength / minimumLevel;\n\n\tint warningPosition = meterLength - convertToInt(warningLevel * scale);\n\tint errorPosition = meterLength - convertToInt(errorLevel * scale);\n\n\tint nominalLength = warningPosition;\n\tint warningLength = nominalLength + (errorPosition - warningPosition);\n\n\tfor (int channelNr = 0; channelNr < displayNrAudioChannels; channelNr++) {\n\t\tint channelOffset = channelNr * (meterThickness + 1);\n\n\t\tif (vertical) {\n\t\t\tbg.fillRect(channelOffset, meterLength, meterThickness, -meterLength, error);\n\t\t\tbg.fillRect(channelOffset, meterLength, meterThickness, -warningLength, warning);\n\t\t\tbg.fillRect(channelOffset, meterLength, meterThickness, -nominalLength, nominal);\n\t\t} else {\n\t\t\tbg.fillRect(meterStart, channelOffset, meterLength, meterThickness, error);\n\t\t\tbg.fillRect(meterStart, channelOffset, warningLength, meterThickness, warning);\n\t\t\tbg.fillRect(meterStart, channelOffset, nominalLength, meterThickness, nominal);\n\t\t}\n\t}\n}\n\ninline int VolumeMeter::convertToInt(float number)\n{\n\tconstexpr int min = std::numeric_limits<int>::min();\n\tconstexpr int max = std::numeric_limits<int>::max();\n\n\t// NOTE: Conversion from 'const int' to 'float' changes max value from 2147483647 to 2147483648\n\tif (number >= (float)max) {\n\t\treturn max;\n\t} else if (number < min) {\n\t\treturn min;\n\t} else {\n\t\treturn int(number);\n\t}\n}\n\nvoid VolumeMeter::paintEvent(QPaintEvent *)\n{\n\tuint64_t ts = os_gettime_ns();\n\tqreal timeSinceLastRedraw = (ts - lastRedrawTime) * 0.000000001;\n\tcalculateBallistics(ts, timeSinceLastRedraw);\n\tbool idle = detectIdle(ts);\n\n\tQPainter painter(this);\n\n\tbool disabledColors = muted || useDisabledColors;\n\tQColor nominal = disabledColors ? foregroundNominalColorDisabled : foregroundNominalColor;\n\tQColor warning = disabledColors ? foregroundWarningColorDisabled : foregroundWarningColor;\n\tQColor error = disabledColors ? foregroundErrorColorDisabled : foregroundErrorColor;\n\n\tint meterStart = INDICATOR_THICKNESS + 2;\n\tint meterLength = vertical ? rect().height() - (INDICATOR_THICKNESS + 2)\n\t\t\t\t   : rect().width() - (INDICATOR_THICKNESS + 2);\n\n\tconst qreal scale = meterLength / minimumLevel;\n\n\t// Paint cached background pixmap\n\tpainter.drawPixmap(0, 0, backgroundCache);\n\n\t// Draw dynamic audio meter bars\n\tint warningPosition = meterLength - convertToInt(warningLevel * scale);\n\tint errorPosition = meterLength - convertToInt(errorLevel * scale);\n\tint clipPosition = meterLength - convertToInt(clipLevel * scale);\n\n\tint nominalLength = warningPosition;\n\tint warningLength = nominalLength + (errorPosition - warningPosition);\n\n\tfor (int channelNr = 0; channelNr < displayNrAudioChannels; channelNr++) {\n\t\tint channelNrFixed = (displayNrAudioChannels == 1 && channels > 2) ? 2 : channelNr;\n\n\t\tQMutexLocker locker(&dataMutex);\n\t\tfloat peak = displayPeak[channelNrFixed];\n\t\tfloat peakHold = displayPeakHold[channelNrFixed];\n\t\tfloat magnitude = displayMagnitude[channelNrFixed];\n\n\t\tint peakPosition = meterLength - convertToInt(peak * scale);\n\t\tint peakHoldPosition = meterLength - convertToInt(peakHold * scale);\n\t\tint magnitudePosition = meterLength - convertToInt(magnitude * scale);\n\t\tlocker.unlock();\n\n\t\tif (clipping) {\n\t\t\tpeakPosition = meterLength;\n\t\t}\n\n\t\tauto fill = [&](int pos, int length, const QColor &color) {\n\t\t\tif (vertical) {\n\t\t\t\tpainter.fillRect(pos, meterLength, meterThickness, -length, color);\n\t\t\t} else {\n\t\t\t\tpainter.fillRect(meterStart, pos, length, meterThickness, color);\n\t\t\t}\n\t\t};\n\n\t\tint channelOffset = channelNr * (meterThickness + 1);\n\n\t\t// Draw audio meter peak bars\n\t\tif (peakPosition >= clipPosition) {\n\t\t\tif (!clipping) {\n\t\t\t\tQTimer::singleShot(CLIP_FLASH_DURATION_MS, this, [&]() { clipping = false; });\n\t\t\t\tclipping = true;\n\t\t\t}\n\n\t\t\tfill(channelOffset, meterLength, error);\n\t\t} else {\n\t\t\tif (peakPosition > errorPosition) {\n\t\t\t\tfill(channelOffset, std::min(peakPosition, meterLength), error);\n\t\t\t}\n\t\t\tif (peakPosition > warningPosition) {\n\t\t\t\tfill(channelOffset, std::min(peakPosition, warningLength), warning);\n\t\t\t}\n\t\t\tif (peakPosition > meterStart) {\n\t\t\t\tfill(channelOffset, std::min(peakPosition, nominalLength), nominal);\n\t\t\t}\n\t\t}\n\n\t\t// Draw peak hold indicators\n\t\tQColor peakHoldColor = nominal;\n\t\tif (peakHoldPosition >= errorPosition) {\n\t\t\tpeakHoldColor = error;\n\t\t} else if (peakHoldPosition >= warningPosition) {\n\t\t\tpeakHoldColor = warning;\n\t\t}\n\n\t\tif (peakHoldPosition - 3 > 0) {\n\t\t\tif (vertical) {\n\t\t\t\tpainter.fillRect(channelOffset, meterLength - peakHoldPosition - 3, meterThickness, 3,\n\t\t\t\t\t\t peakHoldColor);\n\t\t\t} else {\n\t\t\t\tpainter.fillRect(meterStart + peakHoldPosition - 3, channelOffset, 3, meterThickness,\n\t\t\t\t\t\t peakHoldColor);\n\t\t\t}\n\t\t}\n\n\t\t// Draw magnitude indicator\n\t\tif (magnitudePosition - 3 >= 0) {\n\t\t\tif (vertical) {\n\t\t\t\tpainter.fillRect(channelOffset, meterLength - magnitudePosition - 3, meterThickness, 3,\n\t\t\t\t\t\t magnitudeColor);\n\t\t\t} else {\n\t\t\t\tpainter.fillRect(meterStart + magnitudePosition - 3, channelOffset, 3, meterThickness,\n\t\t\t\t\t\t magnitudeColor);\n\t\t\t}\n\t\t}\n\n\t\tif (idle) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Draw audio input indicator\n\t\tif (vertical) {\n\t\t\tpainter.fillRect(channelOffset, rect().height(), meterThickness, -INDICATOR_THICKNESS,\n\t\t\t\t\t getPeakColor(displayInputPeakHold[channelNrFixed]));\n\t\t} else {\n\t\t\tpainter.fillRect(0, channelOffset, INDICATOR_THICKNESS, meterThickness,\n\t\t\t\t\t getPeakColor(displayInputPeakHold[channelNrFixed]));\n\t\t}\n\t}\n\n\tlastRedrawTime = ts;\n}\n\nvoid VolumeMeter::resizeEvent(QResizeEvent *event)\n{\n\tupdateBackgroundCache();\n\treturn QWidget::resizeEvent(event);\n}\n\nvoid VolumeMeter::mousePressEvent(QMouseEvent *event)\n{\n\tsetFocus(Qt::MouseFocusReason);\n\tevent->accept();\n}\n\nvoid VolumeMeter::wheelEvent(QWheelEvent *event)\n{\n\tQApplication::sendEvent(focusProxy(), event);\n}\n\nQSize VolumeMeter::minimumSizeHint() const\n{\n\treturn sizeHint();\n}\n\nQSize VolumeMeter::sizeHint() const\n{\n\tQRect meterRect = getBarRect();\n\tint labelTotal = std::abs(minimumLevel / TICK_DB_INTERVAL) + 1;\n\n\tif (vertical) {\n\t\tint width = meterRect.width() + tickTextTokenRect.width() + TICK_SIZE + 10;\n\t\tint height = (labelTotal * tickTextTokenRect.height()) + INDICATOR_THICKNESS;\n\n\t\treturn QSize(width, height * 1.1);\n\t} else {\n\t\tint width = (labelTotal * tickTextTokenRect.width()) + INDICATOR_THICKNESS;\n\t\tint height = meterRect.height() + tickTextTokenRect.height();\n\n\t\treturn QSize(width * 1.1, height);\n\t}\n}\n"
  },
  {
    "path": "frontend/components/VolumeMeter.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QMutex>\n#include <QPixmap>\n#include <QWidget>\n\n#define FADER_PRECISION 4096.0\n\nclass VolumeMeter : public QWidget {\n\tQ_OBJECT\n\n\tQ_PROPERTY(QColor backgroundNominalColor READ getBackgroundNominalColor WRITE setBackgroundNominalColor\n\t\t\t   DESIGNABLE true)\n\tQ_PROPERTY(QColor backgroundWarningColor READ getBackgroundWarningColor WRITE setBackgroundWarningColor\n\t\t\t   DESIGNABLE true)\n\tQ_PROPERTY(\n\t\tQColor backgroundErrorColor READ getBackgroundErrorColor WRITE setBackgroundErrorColor DESIGNABLE true)\n\tQ_PROPERTY(QColor foregroundNominalColor READ getForegroundNominalColor WRITE setForegroundNominalColor\n\t\t\t   DESIGNABLE true)\n\tQ_PROPERTY(QColor foregroundWarningColor READ getForegroundWarningColor WRITE setForegroundWarningColor\n\t\t\t   DESIGNABLE true)\n\tQ_PROPERTY(\n\t\tQColor foregroundErrorColor READ getForegroundErrorColor WRITE setForegroundErrorColor DESIGNABLE true)\n\n\tQ_PROPERTY(QColor backgroundNominalColorDisabled READ getBackgroundNominalColorDisabled WRITE\n\t\t\t   setBackgroundNominalColorDisabled DESIGNABLE true)\n\tQ_PROPERTY(QColor backgroundWarningColorDisabled READ getBackgroundWarningColorDisabled WRITE\n\t\t\t   setBackgroundWarningColorDisabled DESIGNABLE true)\n\tQ_PROPERTY(QColor backgroundErrorColorDisabled READ getBackgroundErrorColorDisabled WRITE\n\t\t\t   setBackgroundErrorColorDisabled DESIGNABLE true)\n\tQ_PROPERTY(QColor foregroundNominalColorDisabled READ getForegroundNominalColorDisabled WRITE\n\t\t\t   setForegroundNominalColorDisabled DESIGNABLE true)\n\tQ_PROPERTY(QColor foregroundWarningColorDisabled READ getForegroundWarningColorDisabled WRITE\n\t\t\t   setForegroundWarningColorDisabled DESIGNABLE true)\n\tQ_PROPERTY(QColor foregroundErrorColorDisabled READ getForegroundErrorColorDisabled WRITE\n\t\t\t   setForegroundErrorColorDisabled DESIGNABLE true)\n\n\tQ_PROPERTY(QColor magnitudeColor READ getMagnitudeColor WRITE setMagnitudeColor DESIGNABLE true)\n\tQ_PROPERTY(QColor majorTickColor READ getMajorTickColor WRITE setMajorTickColor DESIGNABLE true)\n\tQ_PROPERTY(QColor minorTickColor READ getMinorTickColor WRITE setMinorTickColor DESIGNABLE true)\n\n\tfriend class VolumeControl;\n\nprivate:\n\tOBSWeakSource weakSource;\n\tOBSVolMeter obsVolumeMeter;\n\n\tstatic QPointer<QTimer> updateTimer;\n\n\tstatic void obsVolMeterChanged(void *data, const float magnitude[MAX_AUDIO_CHANNELS],\n\t\t\t\t       const float peak[MAX_AUDIO_CHANNELS], const float inputPeak[MAX_AUDIO_CHANNELS]);\n\n\tOBSSignal destroyedSignal;\n\tstatic void obsSourceDestroyed(void *data, calldata_t *);\n\n\tinline void resetLevels();\n\tinline void doLayout();\n\tinline bool detectIdle(uint64_t ts);\n\tinline void calculateBallistics(uint64_t ts, qreal timeSinceLastRedraw = 0.0);\n\tinline void calculateBallisticsForChannel(int channelNr, uint64_t ts, qreal timeSinceLastRedraw);\n\n\tinline int convertToInt(float number);\n\tQColor getPeakColor(float peakHold);\n\n\tvoid paintHTicks(QPainter &painter, int x, int y, int width);\n\tvoid paintVTicks(QPainter &painter, int x, int y, int height);\n\n\tQMutex dataMutex;\n\n\tuint64_t currentLastUpdateTime{0};\n\tfloat currentMagnitude[MAX_AUDIO_CHANNELS];\n\tfloat currentPeak[MAX_AUDIO_CHANNELS];\n\tfloat currentInputPeak[MAX_AUDIO_CHANNELS];\n\n\tint displayNrAudioChannels{0};\n\tfloat displayMagnitude[MAX_AUDIO_CHANNELS];\n\tfloat displayPeak[MAX_AUDIO_CHANNELS];\n\tfloat displayPeakHold[MAX_AUDIO_CHANNELS];\n\tuint64_t displayPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];\n\tfloat displayInputPeakHold[MAX_AUDIO_CHANNELS];\n\tuint64_t displayInputPeakHoldLastUpdateTime[MAX_AUDIO_CHANNELS];\n\n\tQPixmap backgroundCache;\n\tvoid updateBackgroundCache(bool force = false);\n\n\tQFont tickFont;\n\tQRect tickTextTokenRect;\n\n\tQColor backgroundNominalColor;\n\tQColor backgroundWarningColor;\n\tQColor backgroundErrorColor;\n\tQColor foregroundNominalColor;\n\tQColor foregroundWarningColor;\n\tQColor foregroundErrorColor;\n\n\tQColor backgroundNominalColorDisabled;\n\tQColor backgroundWarningColorDisabled;\n\tQColor backgroundErrorColorDisabled;\n\tQColor foregroundNominalColorDisabled;\n\tQColor foregroundWarningColorDisabled;\n\tQColor foregroundErrorColorDisabled;\n\n\tQColor clipColor;\n\tQColor magnitudeColor;\n\tQColor majorTickColor;\n\tQColor minorTickColor;\n\n\tint meterThickness;\n\tqreal meterFontScaling;\n\n\tqreal minimumLevel;\n\tqreal warningLevel;\n\tqreal errorLevel;\n\tqreal clipLevel;\n\tqreal minimumInputLevel;\n\tqreal peakDecayRate;\n\tqreal magnitudeIntegrationTime;\n\tqreal peakHoldDuration;\n\tqreal inputPeakHoldDuration;\n\n\tQColor p_backgroundNominalColor;\n\tQColor p_backgroundWarningColor;\n\tQColor p_backgroundErrorColor;\n\tQColor p_foregroundNominalColor;\n\tQColor p_foregroundWarningColor;\n\tQColor p_foregroundErrorColor;\n\n\tuint64_t lastRedrawTime{0};\n\tint channels{0};\n\tbool clipping{false};\n\tbool vertical{false};\n\tbool hidden{false};\n\tbool muted{false};\n\tbool useDisabledColors{false};\n\npublic:\n\texplicit VolumeMeter(QWidget *parent = nullptr, obs_source_t *source = nullptr);\n\t~VolumeMeter();\n\n\tvoid setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],\n\t\t       const float inputPeak[MAX_AUDIO_CHANNELS]);\n\tbool needLayoutChange();\n\n\tvoid setVertical(bool vertical = true);\n\tvoid setUseDisabledColors(bool enable);\n\tvoid setMuted(bool mute);\n\n\tvoid refreshColors();\n\tQRect getBarRect() const;\n\n\tQColor getBackgroundNominalColor() const;\n\tvoid setBackgroundNominalColor(QColor c);\n\tQColor getBackgroundWarningColor() const;\n\tvoid setBackgroundWarningColor(QColor c);\n\tQColor getBackgroundErrorColor() const;\n\tvoid setBackgroundErrorColor(QColor c);\n\tQColor getForegroundNominalColor() const;\n\tvoid setForegroundNominalColor(QColor c);\n\tQColor getForegroundWarningColor() const;\n\tvoid setForegroundWarningColor(QColor c);\n\tQColor getForegroundErrorColor() const;\n\tvoid setForegroundErrorColor(QColor c);\n\n\tQColor getBackgroundNominalColorDisabled() const;\n\tvoid setBackgroundNominalColorDisabled(QColor c);\n\tQColor getBackgroundWarningColorDisabled() const;\n\tvoid setBackgroundWarningColorDisabled(QColor c);\n\tQColor getBackgroundErrorColorDisabled() const;\n\tvoid setBackgroundErrorColorDisabled(QColor c);\n\tQColor getForegroundNominalColorDisabled() const;\n\tvoid setForegroundNominalColorDisabled(QColor c);\n\tQColor getForegroundWarningColorDisabled() const;\n\tvoid setForegroundWarningColorDisabled(QColor c);\n\tQColor getForegroundErrorColorDisabled() const;\n\tvoid setForegroundErrorColorDisabled(QColor c);\n\n\tQColor getMagnitudeColor() const;\n\tvoid setMagnitudeColor(QColor c);\n\tQColor getMajorTickColor() const;\n\tvoid setMajorTickColor(QColor c);\n\tQColor getMinorTickColor() const;\n\tvoid setMinorTickColor(QColor c);\n\n\tqreal getWarningLevel() const;\n\tvoid setWarningLevel(qreal v);\n\tqreal getErrorLevel() const;\n\tvoid setErrorLevel(qreal v);\n\n\tvoid setPeakDecayRate(qreal v);\n\tvoid setPeakMeterType(enum obs_peak_meter_type peakMeterType);\n\n\tvirtual void resizeEvent(QResizeEvent *event) override;\n\tvirtual void mousePressEvent(QMouseEvent *event) override;\n\tvirtual void wheelEvent(QWheelEvent *event) override;\n\n\tQSize minimumSizeHint() const override;\n\tQSize sizeHint() const override;\n\nprotected:\n\tvoid paintEvent(QPaintEvent *event) override;\n\nprivate slots:\n\tvoid handleSourceDestroyed() { deleteLater(); }\n};\n"
  },
  {
    "path": "frontend/components/VolumeName.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"VolumeName.hpp\"\n\n#include <QHBoxLayout>\n#include <QLabel>\n#include <QResizeEvent>\n#include <QStyle>\n#include <QStyleOptionButton>\n#include <QStylePainter>\n\n#include \"moc_VolumeName.cpp\"\n\nVolumeName::VolumeName(obs_source_t *source, QWidget *parent)\n\t: QAbstractButton(parent),\n\t  indicatorWidth(style()->pixelMetric(QStyle::PM_MenuButtonIndicator, nullptr, this))\n{\n\trenamedSignal = OBSSignal(obs_source_get_signal_handler(source), \"rename\", &VolumeName::obsSourceRenamed, this);\n\tremovedSignal = OBSSignal(obs_source_get_signal_handler(source), \"remove\", &VolumeName::obsSourceRemoved, this);\n\tdestroyedSignal =\n\t\tOBSSignal(obs_source_get_signal_handler(source), \"destroy\", &VolumeName::obsSourceDestroyed, this);\n\n\tQHBoxLayout *layout = new QHBoxLayout();\n\tsetLayout(layout);\n\n\tlabel = new QLabel(this);\n\tlayout->addWidget(label);\n\n\tlayout->setContentsMargins(0, 0, indicatorWidth, 0);\n\n\tQString name = obs_source_get_name(source);\n\tsetText(name);\n}\n\nVolumeName::~VolumeName() {}\n\nvoid VolumeName::setAlignment(Qt::Alignment alignment_)\n{\n\tif (textAlignment != alignment_) {\n\t\ttextAlignment = alignment_;\n\t\tupdate();\n\t}\n}\n\nQSize VolumeName::sizeHint() const\n{\n\tQStyleOptionButton opt;\n\topt.initFrom(this);\n\n\tconst QFontMetrics metrics(font());\n\tQSize textSize = metrics.size(Qt::TextSingleLine, text());\n\n\tint width = textSize.width();\n\tint height = textSize.height();\n\n\tif (!opt.icon.isNull()) {\n\t\theight = std::max(height, indicatorWidth);\n\t}\n\n\tconst int spacing = style()->pixelMetric(QStyle::PM_ButtonMargin, &opt, this) / 2;\n\twidth += indicatorWidth + spacing;\n\n\tQSize contentsSize = style()->sizeFromContents(QStyle::CT_PushButton, &opt, QSize(width, height), this);\n\treturn contentsSize;\n}\n\nvoid VolumeName::obsSourceRenamed(void *data, calldata_t *params)\n{\n\tVolumeName *widget = static_cast<VolumeName *>(data);\n\tconst char *name = calldata_string(params, \"new_name\");\n\n\tQMetaObject::invokeMethod(widget, \"onRenamed\", Qt::QueuedConnection, Q_ARG(QString, name));\n}\n\nvoid VolumeName::obsSourceRemoved(void *data, calldata_t *)\n{\n\tVolumeName *widget = static_cast<VolumeName *>(data);\n\n\tQMetaObject::invokeMethod(widget, \"onRemoved\", Qt::QueuedConnection);\n}\n\nvoid VolumeName::obsSourceDestroyed(void *data, calldata_t *)\n{\n\tVolumeName *widget = static_cast<VolumeName *>(data);\n\n\tQMetaObject::invokeMethod(widget, \"onDestroyed\", Qt::QueuedConnection);\n}\n\nvoid VolumeName::resizeEvent(QResizeEvent *event)\n{\n\tupdateLabelText(text());\n\tQAbstractButton::resizeEvent(event);\n}\n\nvoid VolumeName::paintEvent(QPaintEvent *)\n{\n\tQStylePainter painter(this);\n\tQStyleOptionButton opt;\n\topt.initFrom(this);\n\n\tpainter.drawControl(QStyle::CE_PushButtonBevel, opt);\n\n\tQRect contentRect = style()->subElementRect(QStyle::SE_PushButtonContents, &opt, this);\n\tint paddingRight = opt.rect.right() - contentRect.right();\n\n\tint indicatorWidth = style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &opt, this);\n\n\tQStyleOption arrowOpt = opt;\n\tQRect arrowRect(opt.rect.right() - indicatorWidth - paddingRight / 2,\n\t\t\topt.rect.center().y() - indicatorWidth / 2, indicatorWidth, indicatorWidth);\n\tarrowOpt.rect = arrowRect;\n\n\tpainter.drawPrimitive(QStyle::PE_IndicatorArrowDown, arrowOpt);\n}\n\nvoid VolumeName::onRenamed(QString name)\n{\n\tsetText(name);\n\n\tstd::string nameStr = name.toStdString();\n\temit renamed(nameStr.c_str());\n}\n\nvoid VolumeName::setText(const QString &text)\n{\n\tQAbstractButton::setText(text);\n\tupdateLabelText(text);\n}\n\nvoid VolumeName::updateLabelText(const QString &name)\n{\n\tQString plainText = name;\n\t// Handle source names that use rich text.\n\tif (name.contains(\"<\") && name.contains(\">\")) {\n\t\tQTextDocument doc;\n\t\tdoc.setHtml(name);\n\n\t\tplainText = doc.toPlainText();\n\t}\n\n\tQFontMetrics metrics(label->font());\n\tQString elidedText = metrics.elidedText(plainText, Qt::ElideMiddle, width() - indicatorWidth * 2);\n\n\tbool useElidedText = metrics.boundingRect(plainText).width() > width() - indicatorWidth;\n\n\tbool isRichText = name != plainText;\n\tlabel->setText(useElidedText && !isRichText ? elidedText : name);\n}\n\nvoid VolumeName::onRemoved()\n{\n\temit removed();\n}\n\nvoid VolumeName::onDestroyed()\n{\n\temit destroyed();\n}\n"
  },
  {
    "path": "frontend/components/VolumeName.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <obs.hpp>\n\n#include <QAbstractButton>\n#include <QLabel>\n#include <QPointer>\n\nclass VolumeName : public QAbstractButton {\n\tQ_OBJECT;\n\tQ_PROPERTY(Qt::Alignment textAlignment READ alignment WRITE setAlignment)\n\n\tQPointer<QLabel> label{};\n\tint indicatorWidth;\n\npublic:\n\tVolumeName(obs_source_t *source, QWidget *parent = nullptr);\n\t~VolumeName();\n\n\tvoid setAlignment(Qt::Alignment alignment);\n\tQt::Alignment alignment() const { return textAlignment; }\n\n\tQSize sizeHint() const override;\n\n\tvoid updateLabelText(const QString &name);\n\tvoid setText(const QString &text);\n\nprotected:\n\tOBSSignal renamedSignal;\n\tOBSSignal removedSignal;\n\tOBSSignal destroyedSignal;\n\n\tQt::Alignment textAlignment = Qt::AlignLeft;\n\n\tstatic void obsSourceRenamed(void *data, calldata_t *params);\n\tstatic void obsSourceRemoved(void *data, calldata_t *params);\n\tstatic void obsSourceDestroyed(void *data, calldata_t *params);\n\n\tvoid resizeEvent(QResizeEvent *event) override;\n\tvoid paintEvent(QPaintEvent *event) override;\n\nprivate slots:\n\tvoid onRenamed(QString name);\n\tvoid onRemoved();\n\tvoid onDestroyed();\n\nsignals:\n\tvoid renamed(const char *name);\n\tvoid removed();\n\tvoid destroyed();\n};\n"
  },
  {
    "path": "frontend/components/VolumeSlider.cpp",
    "content": "#include \"VolumeSlider.hpp\"\n\n#include <QPainter>\n\n#include \"moc_VolumeSlider.cpp\"\n\nVolumeSlider::VolumeSlider(obs_fader_t *fader, QWidget *parent) : AbsoluteSlider(parent)\n{\n\tfad = fader;\n}\n\nVolumeSlider::VolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent)\n\t: AbsoluteSlider(orientation, parent)\n{\n\tfad = fader;\n}\n\nbool VolumeSlider::getDisplayTicks() const\n{\n\treturn displayTicks;\n}\n\nvoid VolumeSlider::setDisplayTicks(bool display)\n{\n\tdisplayTicks = display;\n}\n\nvoid VolumeSlider::paintEvent(QPaintEvent *event)\n{\n\tif (!getDisplayTicks()) {\n\t\tQSlider::paintEvent(event);\n\t\treturn;\n\t}\n\n\tQPainter painter(this);\n\tQColor tickColor(91, 98, 115, 255);\n\n\tobs_fader_conversion_t fader_db_to_def = obs_fader_db_to_def(fad);\n\n\tQStyleOptionSlider opt;\n\tinitStyleOption(&opt);\n\n\tQRect groove = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderGroove, this);\n\tQRect handle = style()->subControlRect(QStyle::CC_Slider, &opt, QStyle::SC_SliderHandle, this);\n\n\tif (orientation() == Qt::Horizontal) {\n\t\tconst int sliderWidth = groove.width() - handle.width();\n\n\t\tfloat tickLength = groove.height() * 1.5;\n\t\ttickLength = std::max((int)tickLength + groove.height(), 8 + groove.height());\n\n\t\tfloat yPos = groove.center().y() - (tickLength / 2) + 1;\n\n\t\tfor (int db = -10; db >= -90; db -= 10) {\n\t\t\tfloat tickValue = fader_db_to_def(db);\n\n\t\t\tfloat xPos = groove.left() + (tickValue * sliderWidth) + (handle.width() / 2);\n\t\t\tpainter.fillRect(xPos, yPos, 1, tickLength, tickColor);\n\t\t}\n\t}\n\n\tif (orientation() == Qt::Vertical) {\n\t\tconst int sliderHeight = groove.height() - handle.height();\n\n\t\tfloat tickLength = groove.width() * 1.5;\n\t\ttickLength = std::max((int)tickLength + groove.width(), 8 + groove.width());\n\n\t\tfloat xPos = groove.center().x() - (tickLength / 2) + 1;\n\n\t\tfor (int db = -10; db >= -96; db -= 10) {\n\t\t\tfloat tickValue = fader_db_to_def(db);\n\n\t\t\tfloat yPos =\n\t\t\t\tgroove.height() + groove.top() - (tickValue * sliderHeight) - (handle.height() / 2);\n\t\t\tpainter.fillRect(xPos, yPos, tickLength, 1, tickColor);\n\t\t}\n\t}\n\n\tQSlider::paintEvent(event);\n}\n"
  },
  {
    "path": "frontend/components/VolumeSlider.hpp",
    "content": "#pragma once\n\n#include <components/AbsoluteSlider.hpp>\n\n#include <obs.hpp>\n\nclass VolumeSlider : public AbsoluteSlider {\n\tQ_OBJECT\n\npublic:\n\tobs_fader_t *fad;\n\n\tVolumeSlider(obs_fader_t *fader, QWidget *parent = nullptr);\n\tVolumeSlider(obs_fader_t *fader, Qt::Orientation orientation, QWidget *parent = nullptr);\n\n\tbool getDisplayTicks() const;\n\tvoid setDisplayTicks(bool display);\n\nprivate:\n\tbool displayTicks = false;\n\tQColor tickColor;\n\nprotected:\n\tvirtual void paintEvent(QPaintEvent *event) override;\n};\n"
  },
  {
    "path": "frontend/components/WindowCaptureToolbar.cpp",
    "content": "#include \"WindowCaptureToolbar.hpp\"\n#include \"ui_device-select-toolbar.h\"\n#include \"moc_WindowCaptureToolbar.cpp\"\n\n#ifdef _WIN32\n#define get_os_module(win, mac, linux) obs_get_module(win)\n#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, win)\n#elif __APPLE__\n#define get_os_module(win, mac, linux) obs_get_module(mac)\n#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, mac)\n#else\n#define get_os_module(win, mac, linux) obs_get_module(linux)\n#define get_os_text(mod, win, mac, linux) obs_module_get_locale_text(mod, linux)\n#endif\n\nWindowCaptureToolbar::WindowCaptureToolbar(QWidget *parent, OBSSource source) : ComboSelectToolbar(parent, source) {}\n\nvoid WindowCaptureToolbar::Init()\n{\n\tdelete ui->activateButton;\n\tui->activateButton = nullptr;\n\n\tobs_module_t *mod = get_os_module(\"win-capture\", \"mac-capture\", \"linux-capture\");\n\tif (!mod)\n\t\treturn;\n\n\tconst char *device_str = get_os_text(mod, \"WindowCapture.Window\", \"WindowUtils.Window\", \"Window\");\n\tui->deviceLabel->setText(device_str);\n\n#if !defined(_WIN32) && !defined(__APPLE__) //linux\n\tprop_name = \"capture_window\";\n#else\n\tprop_name = \"window\";\n#endif\n\n#ifdef __APPLE__\n\tis_int = true;\n#endif\n\n\tComboSelectToolbar::Init();\n}\n"
  },
  {
    "path": "frontend/components/WindowCaptureToolbar.hpp",
    "content": "#pragma once\n\n#include \"ComboSelectToolbar.hpp\"\n\nclass WindowCaptureToolbar : public ComboSelectToolbar {\n\tQ_OBJECT\n\npublic:\n\tWindowCaptureToolbar(QWidget *parent, OBSSource source);\n\tvoid Init() override;\n};\n"
  },
  {
    "path": "frontend/data/OBSPublicRSAKey.pem",
    "content": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAl3sverw9HQ+rYQNn9Ca7\n9LU62nG6NozE/FsIVNer+W/hueE8WQ1mrFP+2yA+iYRutYPeEgpxtodHhgkR4pEK\nVnrr18q4szlv89QI7t8lMoLTlF+t1IR0V4wJV34I3C+359KixnzL0Bl9aj/zDcrX\nWl5pHTioJwYOgMiBGLyPeitMFdjjTIpCM+mxTWXCrZ9dPUKvZtgzjd+IzlHidHtO\nORBN5mRs8LNO58k79r77DcgQYPNiiCtWgC+Y4K7uSZX3Hveom2tHbVXy0L/Cl7fM\nHKqfcQGuyrvud42OrWarAsn2p2Ei6Kzxb3G6ESCw15nHAgLal8zSq7+raE/xkLpC\nbYg5gmY6vbmWnq9dqWrUzaqOfrZPgvgG0WvkBShfaEOBaIUxA3QBgzAZhqeedF9h\nafMGMM9qVbfwuuzJ2uh+InaGaeH2c04oVcDFfeOaDuxRjCCbqr5sLSo1CWokynjN\nCB+b2rQF7DPPbD4s/nT9Nsck/NFzrBXRO+dqkeBwDUCv7bZgW7OxuOX07LTqfp5s\nOeGgububiwY3UdHYq+L9JqISG1tM4HeKjaHju1MDjvHZ2DbmLwUxuYa6JZDKWs7r\nIrdDwx3JwacF66h3YUW6tzUZhztcmQepP/u7BgGrkOPPpYA0NEJ80SeAx7hiN4va\neEQKnRn+EpBN6UBa5f7LoK8CAwEAAQ==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "frontend/data/license/gplv2.txt",
    "content": "GNU GENERAL PUBLIC LICENSE Version 2, June 1991\n\nCopyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.\n\nPreamble\n\nThe licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.\n\nWhen we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.\n\nTo protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.\n\nFor example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\n\nWe protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.\n\nAlso, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.\n\nFinally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.\n\nThe precise terms and conditions for copying, distribution and modification follow.\n\nGNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The \"Program\", below, refers to any such program or work, and a \"work based on the Program\" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term \"modification\".) Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.\n\n1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.\n\n2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:\n\na) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.\n\nb) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.\n\nc) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.\n\n3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:\n\na) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\n\nb) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\n\nc) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.\n\nIf distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.\n\n4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.\n\n5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.\n\n6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.\n\n7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.\n\nIt is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.\n\nThis section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.\n\n8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.\n\n9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\n\nEach version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and \"any later version\", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.\n\n10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.\n\nNO WARRANTY\n\n11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n"
  },
  {
    "path": "frontend/data/locale/af-ZA.ini",
    "content": "Language=\"Afrikaans\"\nOK=\"Goed\"\nApply=\"Pas toe\"\nCancel=\"Kanselleer\"\nClose=\"Maak toe\"\nSave=\"Stoor\"\nDiscard=\"Verwyder\"\nDisable=\"Deaktiveer\"\nYes=\"Ja\"\nNo=\"Nee\"\nAdd=\"Voeg by\"\nRemove=\"Verwyder\"\nRename=\"Hernoem\"\nInteract=\"Interaksie\"\nProperties=\"Eienskappe\"\nMoveUp=\"Beweeg Op\"\nMoveDown=\"Beweeg Af\"\nSettings=\"Instellings\"\nDisplay=\"Vertoon\"\nName=\"Naam\"\nExit=\"Verlaat\"\nMixer=\"OudioMenger\"\nBrowse=\"Blaai\"\nDroppedFrames=\"Geval rame %1 (%2%)\"\nProjector.Open.Program=\"Maak Programprojektor oop\"\nProjector.Open.Preview=\"Maak voorskouprojektor oop\"\nProjector.Open.Scene=\"Oop toneelprojektor\"\nProjector.Open.Source=\"Oopbronprojektor\"\nProjector.Open.Multiview=\"Maak Multiview oop\"\nProjector.Display=\"Vertoon: %1\"\nProjector.Window=\"Nuwe venster\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Toneel: %1\"\nProjector.Title.Source=\"Bron: %1\"\nProjector.ResizeWindowToContent=\"Pas venster by inhoud\"\nClear=\"Vee Uit\"\nRevert=\"Keur Terug\"\nShow=\"Wys\"\nHide=\"Versteek\"\nUnhideAll=\"Onsteek Alles\"\nUntitled=\"Ongetitelde\"\nNew=\"Nuwe\"\nDuplicate=\"Dupliseer\"\nEnable=\"Aktiveer\"\nDisableOSXVSync=\"Deaktiveer macOS V-Sync\"\nResetOSXVSyncOnExit=\"Stel macOS V-Sync terug by uitgang\"\nHighResourceUsage=\"Enkodering oorlaai! Oorweeg dit om video-instellings af te skakel of om 'n vinniger enkoderingvoorinstelling te gebruik.\"\nTransition=\"Transiesie\"\nQuickTransitions=\"Vinnege Transiesie\"\nFadeToBlack=\"Verdwyn na swart\"\nLeft=\"Links\"\nRight=\"Regs\"\nTop=\"Bo\"\nBottom=\"Onder\"\nReset=\"Terugstel\"\nHours=\"Ure\"\nMinutes=\"Minute\"\nSeconds=\"Sekondes\"\nDeprecated=\"Afgemaai\"\nReplayBuffer=\"Herhaal buffer\"\nImport=\"Voer in\"\nExport=\"Stuur uit\"\nCopy=\"Kopieer\"\nPaste=\"Plak\"\nPasteReference=\"Plak (verwysing)\"\nPasteDuplicate=\"Plak (dupliseer)\"\nRemuxRecordings=\"Hermuks opnames\"\nNext=\"Volgende\"\nBack=\"Terug\"\nDefaults=\"Verstekke\"\nRestoreDefaults=\"Verstekke\"\nHideMixer=\"Versteek in menger\"\nTransitionOverride=\"Oorgangsoorskakeling\"\nShowTransition=\"Toon oorgang\"\nHideTransition=\"Versteek oorgang\"\nNone=\"Geen\"\nStudioMode.Preview=\"Voorskou\"\nStudioMode.PreviewSceneName=\"Voorskou: %1\"\nShowInMultiview=\"Wys in Multiview\"\nVerticalLayout=\"Vertikale uitleg\"\nGroup=\"Groep\"\nDoNotShowAgain=\"Moet dit nie weer toon nie\"\nDefault=\"(Verstek)\"\nCalculating=\"Bereken tans…\"\nFullscreen=\"Volskerm\"\nWindowed=\"Venster\"\nRefreshBrowser=\"Verfris\"\nAspectRatio=\"Aspekverhouding <b>%1:%2</b>\"\nLockVolume=\"Vergrendel volume\"\nShowOnStartup=\"Wys tydens opstart\"\nOpenFile=\"Open lêer\"\nAddScene=\"Voeg toneel by\"\nAddSource=\"Voeg bron toe\"\nRemoveScene=\"Verwyder gekose toneel\"\nRemoveSource=\"erwyder gekose bron(ne)\"\nMoveSceneUp=\"Skuif toneel boontoe\"\nMoveSceneDown=\"Skuif toneel ondertoe\"\nMoveSourceUp=\"Skuif bron(ne) op\"\nMoveSourceDown=\"Skuif bron(ne) af\"\nSourceProperties=\"Open broneienskappe\"\nSourceFilters=\"Open bronfilters\"\nMixerToolbarMenu=\"Oudiomengerkieslys\"\nSceneFilters=\"Open toneelfilters\"\nList=\"Lys\"\nGrid=\"Rooster\"\nAutomatic=\"Outomatiese\"\nComingSoon=\"Binnekort\"\nPluginsFailedToLoad.Title=\"Inprop laai Fout\"\nPluginsFailedToLoad.Text=\"Die volgende OBS-inprop kon nie laai nie:\\n\\n%1\\nWerk dit by of verwyder hierdie inproppe.\"\nAlreadyRunning.Title=\"OBS loop reeds\"\nAlreadyRunning.Text=\"OBS loop reeds! Tensy die bedoeling was om dit te doen, moet u asb. enige bestaande instansies van OBS sluit voor u ’n nuwe instansie wil laat loop. Indien OBS in die stelsellaai verklein is, kyk asb. om te sien of dit nog daar loop.\"\nAlreadyRunning.LaunchAnyway=\"Laat in elk geval loop\"\nCrashHandling.Dialog.Title=\"OBS Studio-ongeluk opgespoor\"\nCrashHandling.Labels.Text=\"OBS Studio het nie behoorlik afgeskakel nie.\\n\\nLoop in Veilige Modus (derdeparty-inproppe, skripsie en WebSockets gedeaktiveer)?\"\nCrashHandling.Labels.PrivacyNotice=\"Jy kan ook kies om die mees onlangse ongelukverslag outomaties na die OBSProject op te laai.<br /><br />Lees asseblief die <a href='https://obsproject.com/privacy-policy'>Privaatheidsbeleid</a> voordat jy enige lêers oplaai en gee spesiale aandag aan die dele rakende lêeroplaaie.\"\nCrashHandling.Checkbox.SendReport=\"Ek het die privaatheidsbeleid gelees en stem in tot die oplaai.\"\nCrashHandling.Buttons.LaunchSafe=\"Hardloop in veilige modus\"\nCrashHandling.Buttons.LaunchNormal=\"Hardloop in normale modus\"\nCrashHandling.Errors.UploadJSONError=\"'n Fout het voorgekom tydens die oplaai van die mees onlangse ongeluklog. Probeer asseblief later weer.\"\nCrashHandling.Errors.Title=\"Omvallog-oplaaifout\"\nSafeMode.Restart=\"Wil u OBS in veilige modus herbegin (derdeparty-inproppe, skripte en WebSockets gedeaktiveer)?\"\nSafeMode.RestartNormal=\"Wil u OBS in normale modus herbegin?\"\nChromeOS.Title=\"Onondersteunde platform\"\nChromeOS.Text=\"OBS blyk binne ’n ChromeOS-houer te loop. Hierdie platform word nie ondersteun nie.\"\nWine.Title=\"Wine bespeur\"\nWine.Text=\"Om OBS in Wine te laat loop word nie ondersteun nie, en baie kenmerke soos vaslegging of toestelbronne sal nie werk nie of slegs in beperkte kapasiteit.<br><br>Dit word aanbeveel om eerder 'n oorspronklike weergawe van OBS te laat loop, byvoorbeeld <a href=' https://flathub.org/apps/details/com.obsproject.Studio'>ons Flatpak-weergawe</a> of jou bedryfstelsel se pakkette.\"\nDockCloseWarning.Title=\"Dokbare venster word gesluit\"\nDockCloseWarning.Text=\"U het pas ’n dokbare venster gesluit. Indien u dit weer wil vertoon kan u die Doks-kieslys in die kieslysbalk gebruik.\"\nExtraBrowsers=\"Pasgemaakte blaaierdoks\"\nExtraBrowsers.Info=\"Voeg doks toe deur ’n naam en URL daaraan te gee, klik dan op Pas toe of Sluit om die doks te open. U kan enige tyd doks toevoeg of verwyder.\"\nExtraBrowsers.DockName=\"Doknaam\"\nAuth.Authing.Title=\"Staaf tans...\"\nAuth.Authing.Text=\"Staaf tans met %1, wag asseblief...\"\nAuth.AuthFailure.Title=\"Verifikasie mislukking\"\nAuth.AuthFailure.Text=\"Kon nie staaf met %1:\\n\\n%2: %3 nie\"\nAuth.InvalidScope.Title=\"Stawing vereis\"\nAuth.InvalidScope.Text=\"Die stawingvereistes vir %1 het verander. Sommige kenmerke is dalk nie beskikbaar nie.\"\nAuth.LoadingChannel.Title=\"Laai tans kanaalinligting…\"\nAuth.LoadingChannel.Text=\"Laai tans kanaalinligting vir %1, wag asb…\"\nAuth.LoadingChannel.Error=\"Kon nie kanaalinligting kry nie.\"\nAuth.ChannelFailure.Title=\"Kon nie kanaal laai nie\"\nAuth.ChannelFailure.Text=\"Kon nie kanaalinligting vir %1\\n\\n%2: %3 laai nie\"\nAuth.Chat=\"Klets\"\nAuth.StreamInfo=\"Stroominligting\"\nTwitchAuth.Stats=\"Twitch-statiestieke\"\nTwitchAuth.Feed=\"Twitch-aktiwiteitsvoer\"\nTwitchAuth.TwoFactorFail.Title=\"Kon nie stroomsleutel navraag doen nie\"\nTwitchAuth.TwoFactorFail.Text=\"OBS kon nie aan u Twitch-rekening koppel nie; sorg dat tweestapwaarmerking ingestel is in die <a href='https://www.twitch.tv/settings/security'>Twitch-veiligheidssinstellings</a>, omdat dit vereis word om te kan stroom.\"\nRestreamAuth.Channels=\"Herstroom kanale\"\nCopy.Filters=\"Kopieer filters\"\nPaste.Filters=\"Plak filters\"\nBrowserPanelInit.Title=\"Inisialiseer tans blaaier …\"\nBrowserPanelInit.Text=\"Inisialiseer blaaier, wag asseblief...\"\nBandwidthTest.Region=\"Streek\"\nBandwidthTest.Region.US=\"Verenigde State\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Asië\"\nBandwidthTest.Region.Other=\"Ander\"\nBasic.AutoConfig=\"Outomatiese Konfigurasie-towenaar\"\nBasic.AutoConfig.ApplySettings=\"Pas instellings toe\"\nBasic.AutoConfig.StartPage=\"Gebruik inligting\"\nBasic.AutoConfig.StartPage.SubTitle=\"Spesifiseer waarvoor u die program wil gebruik\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimaliseer vir stroomdienste, opname is sekondêr\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimaliseer net vir opname, ek sal nie stroom nie\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Ek gaan slegs die virtuele kamera gebruik\"\nBasic.AutoConfig.VideoPage=\"Video-instellings\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Spesifiseer die video-instellings wat jy wil gebruik\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Gebruik huidige (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Beeldskerm %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Gebruik huidige (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 of 30, maar gee voorkeur aan 60 wanneer moontlik\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 of 30, maar gee voorkeur aan hoër beeldskerpte\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Nota: die basisbeeldskerpte (doek) hoef nie gelyk te wees aan die beeldskerpte waarmee u stroom of opneem nie. U stroom/opnamebeeldskerpte kan afgeskaal word vanaf die basisbeeldskerpte om die belasting op die rekenaar of die benodigde bistempo te verlaag.\"\nBasic.AutoConfig.StreamPage=\"Stroominligting\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Voer asseblief jou stroominligting in\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Koppel rekening (aanbeveel)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Ontkoppel rekening\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Ontkoppel rekening?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Hierdie verandering sal onmiddellik van toepassing wees. Is u seker u wil u rekening ontkoppel?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Kry stroomsleutel\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Meer inligting\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Gebruik stroomsleutel\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Gebruik Stroomsleutel (gevorderd)\"\nBasic.AutoConfig.StreamPage.Service=\"Diens\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Toon alles…\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Pasgemaak...\"\nBasic.AutoConfig.StreamPage.Server=\"Bediener\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Stroomsleutel\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: voer die enkripsiewagwoordfrase in.\\nRTMP: voer die sleutel in wat deur die diens verskaf word.\\nSRT: voer die stroom-ID in as die diens een gebruik.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Enkodeerdersleutel\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Draer Token\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Gekoppelde rekening\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Skat die bitsnelheid met bandwydtetoets (dit kan 'n paar minute neem)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Verkies apparatuurenkodering\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Apparatuurenkodering elimineer die meeste SVE-gebruik, maar kan meer bistempo vereis om dieselfde kwaliteitsvlak te handhaaf.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Stroomwaarskuwing\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Die bandwydtetoets gaan nou ewekansige videodata sonder klank na jou kanaal stroom. Indien moontlik, word dit aanbeveel om die stoor van video's van strome tydelik af te skakel en die stroom op privaat te stel totdat die toets voltooi is. Gaan voort?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Toets %1\"\nBasic.AutoConfig.TestPage=\"Finale resultate\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Die program voer nou ’n stel toetse uit om die ideale instellings te raam.\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Toetse is afgehandel\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Bandwydtetoets word uitgevoer, dit kan enkele minute duur…\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Geen afvoer vir die protokol van hierdie diens gevind\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Verbind met: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Kon nie aan enige bedieners koppel nie. Kontroleer asseblief jou internetverbinding en probeer weer.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Toets tans bandwydte vir: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Toets stroomkodeerder, dit mag dalk 'n minuut neem...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Toets opname-enkodeerder, dit mag dalk 'n minuut neem...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Kon nie die kodeerder begin nie\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Toets tans %1x%2 %3 RPS…\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Stroomkodeerder\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Opname-enkodeerder\"\nBasic.AutoConfig.TestPage.Result.Header=\"Die program het bepaal dat hierdie beraamde instellings ideaal vir jou is:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Om hierdie instellings te gebruik, klik Pas instellings toe. Om die towenaar te herkonfigureer en weer te probeer, klik Terug. Om instellings self handmatig te konfigureer, klik Kanselleer en maak Instellings oop.\"\nBasic.Stats=\"Statistieke\"\nBasic.Stats.CPUUsage=\"SVE-gebruik\"\nBasic.Stats.HDDSpaceAvailable=\"Beskikbare skyfspasie\"\nBasic.Stats.MemoryUsage=\"Geheuegebruik\"\nBasic.Stats.Status.Live=\"REGSTREEKS\"\nBasic.Stats.Status.Inactive=\"Onaktief\"\nBasic.Stats.Status.Active=\"Aktief\"\nResetUIWarning.Text=\"Herstel van die koppelvlak sal ekstra doks versteek. As u hierdie doks tog wil sien, dan moet u dit in die Doks-kieslys weer sigbaar maak.\\n\\nIs u seker u wil die koppelvlak herstel?\"\nUpdater.RemindMeLater=\"Herinner my later\"\nUpdater.Skip=\"Slaan weergawe oor\"\nUpdater.NoUpdatesAvailable.Title=\"Geen bywerkings beskikbaar\"\nUpdater.BranchNotFound.Text=\"U gekose bywerkkanaal is nie meer beskikbaar nie, OBS is teruggestel na die verstekbywerkkanaal.\"\nQuickTransitions.HotkeyName=\"Kitsoorgang: %1\"\nBasic.TransitionProperties=\"Oorgangeienskappe\"\nBasic.TransitionDuration=\"Duur\"\nBasic.TogglePreviewProgramMode=\"Studiomodus\"\nBasic.EnablePreviewProgramMode=\"Aktiveer Studiomodus\"\nBasic.DisablePreviewProgramMode=\"Deaktiveer Studiomodus\"\nUndo.Undo=\"Ontdoen\"\nUndo.Redo=\"Herdoen\"\nUndo.Add=\"Voeg ‘%1’ toe\"\nUndo.Delete=\"Skrap ‘%1’\"\nUndo.Rename=\"Hernoem ‘%1’\"\nUndo.SceneCollection.Switch=\"Wissel na ‘%1’\"\nUndo.Item.Undo=\"Ontdoen %1\"\nUndo.Item.Redo=\"Herdoen %1\"\nUndo.Sources.Multi=\"Skrap %1 bronne\"\nUndo.Transform.HFlip=\"Spieël horisontaal in ‘%1’\"\nUndo.Transform.HCenter=\"Plaas horisontaal in middel van skerm in ‘%1’\"\nUndo.Mixers.Change=\"Verander oudiomengers op ‘%1’\"\nTransitionNameDlg.Title=\"Oorgangnaam\"\nTitleBar.Profile=\"Profiel\"\nNameExists.Title=\"Naam bestaat reeds\"\nNameExists.Text=\"Die naam is reeds in gebruik\"\nNoNameEntered.Title=\"Voer asb. ’n geldige naam in\"\nNoNameEntered.Text=\"U kan nie leë name gebruik nie.\"\nConfirmStopRecord.Title=\"Stop opname?\"\nConfirmStopRecord.Text=\"Is u seker u wil die opname stop?\"\nConfirmReset.Text=\"Is u seker u wil die huidige eienskappe na die verstekwaarde terugstel?\"\nOutput.RecordError.Title=\"Opnamefout\"\nOutput.BadPath.Title=\"Ongeldige lêerpad\"\nOutput.BadPath.Text=\"Die gekonfigureerde lêerpad kon nie geopen word nie. Gaan u opnamepad onder Instellings → Afvoer → Opname na.\"\nRemux.SourceFile=\"OBS Opname\"\nRemux.TargetFile=\"Teikenlêer\"\nRemux.Remux=\"Hermuks\"\nRemux.Stop=\"Stop met hermuks\"\nRemux.FinishedTitle=\"Hermuks afgehandel\"\nRemux.Finished=\"Opname gehermuks\"\nRemux.FinishedError=\"Opname gehermuks, maar die lêer is dalk onvolledig\"\nRemux.SelectTarget=\"Kies teikenlêer…\"\nRemux.FileExistsTitle=\"Teikenlêer bestaan\"\nRemux.FileExists=\"Die volgende teikenlêers bestaan reeds. Wil u hulle vervang?\"\nRemux.ExitUnfinishedTitle=\"Besig met hermuks\"\nRemux.ExitUnfinished=\"Hermuks is nog nie klaar nie; as u nou stop kan die teikenlêer onbruikbaar word.\\nIs u seker u wil die hermuks stop?\"\nRemux.NoFilesAddedTitle=\"Geen hermukslêer toegevoeg\"\nRemux.NoFilesAdded=\"Geen lêer is toegevoeg om te hermuks nie. Los ’n vouer met een of meer videolêers hier.\"\nMissingFiles=\"Ontbrekende lêers\"\nMissingFiles.MissingFile=\"Ontbrekende lêer\"\nMissingFiles.NewFile=\"Nuwe lêer\"\nMissingFiles.HelpText=\"Sommige lêers ontbreek sedert u OBS laas gebruik het.\"\nMissingFiles.NumFound=\"%1 van %2 gevind\"\nMissingFiles.Search=\"Deursoek gids…\"\nMissingFiles.SelectFile=\"Kies lêers…\"\nMissingFiles.SelectDir=\"Kies die vouer waarin u wil soek\"\nMissingFiles.State=\"Toestand\"\nMissingFiles.Missing=\"Ontbrekend\"\nMissingFiles.Replaced=\"Vervang\"\nMissingFiles.Cleared=\"Gewis\"\nMissingFiles.Found=\"Gevind\"\nMacPermissions.Description=\"OBS Studio benodig u toestemming om bepaalde funksies te kan bied. Dit word aanbeveel om hierdie magtiging te aktiveer, maar dit is nie nodig om die toep te gebruik nie. U kan dit later altyd weer aktiveer.\"\nMacPermissions.Description.OpenDialog=\"U kan hierdie dialoogvenster heropen via die OBS Studio-kieslys.\"\nMacPermissions.AccessGranted=\"Toegang toegestaan\"\nMacPermissions.RequestAccess=\"Versoek toegang\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Microphone=\"Mikrofoon\"\nMacPermissions.Item.Accessibility=\"Toeganklikheid\"\nBasic.DisplayCapture=\"Beeldskermopname\"\nScaleFiltering.Bilinear=\"Bilineêr\"\nBlendingMethod.Default=\"Verstek\"\nBlendingMode=\"Mengmodus\"\nBlendingMode.Normal=\"Normaal\"\nBlendingMode.Additive=\"Voeg toe\"\nBlendingMode.Subtract=\"Trek af\"\nBlendingMode.Screen=\"Skerm\"\nBlendingMode.Multiply=\"Vermenigvuldig\"\nBlendingMode.Lighten=\"Maak ligter\"\nBlendingMode.Darken=\"Maak donkerder\"\nVolControl.UnassignedWarning.Text=\"“%1” is nie toegewys aan ’n oudiosnit nie en sal nie in die strome of opnames hoorbaar wees nie.\\n\\nOm ’n oudiobron toe te wys aan ’n kanaal, open die Gevorderde Oudio-eienskappe deur die regsklikkieslys of die ratknop in die mengerdoknutsbalk.\"\nBasic.Main.MixerRename.Title=\"Hernoem oudiobron\"\nBasic.Main.MixerRename.Text=\"Voer asb. die naam van die oudiobron in\"\nBasic.SourceSelect=\"Skep/kies bron\"\nBasic.SourceSelect.CreateNew=\"Skep nuwe\"\nBasic.SourceSelect.AddExisting=\"Voeg bestaande toe\"\nBasic.SourceSelect.AddVisible=\"Maak bron sigbaar\"\nBasic.Main.Sources.Visibility=\"Sigbaarheid\"\nBasic.Main.Sources.Lock=\"Vergrendel\"\nBasic.Main.Sources.LockDescription=\"Vergrendel die posisie en skaal van ‘%1’ op die doek\"\nBasic.PropertiesWindow=\"Eienskappe vir ‘%1’\"\nBasic.PropertiesWindow.SelectColor=\"Kies kleur\"\nBasic.PropertiesWindow.NoProperties=\"Geen eienskappe beskikbaar\"\nBasic.PropertiesWindow.AddFiles=\"Voeg lêers toe\"\nBasic.PropertiesWindow.AddDir=\"Voeg gids toe\"\nBasic.PropertiesWindow.AddURL=\"Voeg pad/URL toe\"\nBasic.PropertiesWindow.AddEditableListDir=\"Voeg gids toe tot ’%1’\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Voeg lêers toe tot ‘%1’\"\nBasic.PropertiesView.FPS.Simple=\"Eenvoudige RPS-waardes\"\nBasic.PropertiesView.FPS.Rational=\"Rasionele RPS-waardes\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Geldige RPS-bereike:\"\nBasic.PropertiesView.UrlButton.Text=\"Open hierdie skakel in u verstekwebblaaier?\"\nBasic.StatusBar.Delay=\"Vertraging (%1 sek)\"\nBasic.StatusBar.AutoRemuxedTo=\"Opname outomaties gehermuks na ‘%1’\"\nBasic.Filters.AsyncFilters=\"Oudio-/Videofilters\"\nBasic.Filters.AudioFilters=\"Oudiofilters\"\nBasic.Filters.EffectFilters=\"Effekfilters\"\nBasic.Filters.Title=\"Filters vir ‘%1’\"\nBasic.Filters.AddFilter.Title=\"Filternaam\"\nBasic.Filters.AddFilter.Text=\"Spesifiseer die naam vir die filter\"\nBasic.TransformWindow.Position=\"Posisie\"\nBasic.TransformWindow.PositionX=\"Posisie X\"\nBasic.TransformWindow.PositionY=\"Posisie Y\"\nBasic.TransformWindow.Rotation=\"Rotasie\"\nBasic.TransformWindow.Size=\"Grootte\"\nBasic.TransformWindow.Width=\"Breedte\"\nBasic.TransformWindow.Height=\"Hoogte\"\nBasic.TransformWindow.Alignment=\"Positionele inlynstelling\"\nBasic.TransformWindow.Crop=\"Snoei\"\nBasic.TransformWindow.CropLeft=\"Snoei links\"\nBasic.TransformWindow.CropRight=\"Snoei regs\"\nBasic.TransformWindow.CropTop=\"Snoei bo\"\nBasic.TransformWindow.CropBottom=\"Snoei onder\"\nBasic.Main.Sources=\"Bronne\"\nBasic.Main.Source=\"Bron\"\nBasic.Main.StartRecording=\"Begin opname\"\nBasic.Main.PauseRecording=\"Laat opname wag\"\nBasic.VCam.OutputType.Program=\"Program (Verstek)\"\nBasic.MainMenu.File.Remux=\"Her&muks opnames\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Spieël &horisontaal\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Spieël &vertikaal\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Horisontaal in die middel\"\nBasic.MainMenu.View.ListboxToolbars=\"Dok nutsbalke\"\nBasic.MainMenu.Docks=\"&Dokke\"\nBasic.MainMenu.Docks.ResetDocks=\"&Herstel dokke\"\nBasic.MainMenu.Docks.LockDocks=\"Vergrende&l dokke\"\nBasic.MainMenu.Docks.SideDocks=\"&Volledigehoogtedoks\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Pasgemaakte blaaierdoks…\"\nBasic.MainMenu.Help.WhatsNew=\"Wat is nuut\"\nBasic.MainMenu.Help.ReleaseNotes=\"Vrystellingsnotas\"\nBasic.MainMenu.Help.CheckForUpdates=\"Soek na bywerkings\"\nBasic.MainMenu.Help.Repair=\"Gaan lêerintegriteit na\"\nBasic.Settings.ConfirmTitle=\"Bevestig veranderinge\"\nBasic.Settings.Confirm=\"U het onbewaarde veranderinge. Bewaar verandeirnge?\"\nBasic.Settings.General=\"Algemeen\"\nBasic.Settings.General.Language=\"Taal\"\nBasic.Settings.General.Updater=\"Bywerkings\"\nBasic.Settings.General.UpdateChannel=\"Bywerkkanaal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Gedeaktiveer)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Verstek)\"\nBasic.Settings.General.Preview=\"Voorskou\"\nBasic.Settings.General.OverflowHidden=\"Versteek oorloop\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Oorloop altyd sigbaar\"\nBasic.Settings.General.OverflowSelectionHidden=\"Toon oorloop selfs wanneer bron onsigbaar is\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Teken veilige areas (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horisontaal, bo (8 tonele)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horisontaal, onder (8 tonele)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horisontaal, bo (18 tonele)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horisontaal, bo (24 tonele)\"\nBasic.Settings.General.ChannelName.stable=\"Stabiel\"\nBasic.Settings.General.ChannelDescription.stable=\"Nuutste stabiele vrystelling\"\nBasic.Settings.Stream.Custom.Username=\"Gebruikersnaam\"\nBasic.Settings.Stream.Custom.Password=\"Wagwoord\"\nBasic.Settings.Stream.TTVAddon.None=\"Geen\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Open instellings\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksimum beeldskerpte: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksimum RPS: %1\"\nBasic.Settings.Output.Format.MKV=\"Matroska-video (.mkv)\"\nBasic.Settings.Output.Format.fMP4=\"Gefragmenteerde MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Gefragmenteerde MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Gefragmenteerde MOV skryf die opname in dele en vereis nie dieselfde afsluiting as tradisionele MOV-lêers nie.\\nDit verseker dat die lêer speelbaar bly, selfs as die skryf na skyf onderbreek word, bv. as gevolg van ’n BSOD of kragonderbreking.\\n\\nDit is dalk nie versoenbaar met alle spelers en redigeerders nie. Gebruik Lêer → Hermuks opnames om die lêer indien nodig in ’n meer versoenbare formaat om te skakel.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Gefragmenteerde MP4 skryf die opname in dele en vereis nie dieselfde afsluiting as tradisionele MP4-lêers nie.\\nDit verseker dat die lêer speelbaar bly, selfs as die skryf na skyf onderbreek word, bv. as gevolg van ’n BSOD of kragonderbreking.\\n\\nDit is dalk nie versoenbaar met alle spelers en redigeerders nie. Gebruik Lêer → Hermuks opnames om die lêer indien nodig in ’n meer versoenbare formaat om te skakel.\"\nBasic.Settings.Output.Encoder.Video=\"Video-enkodeerder\"\nBasic.Settings.Output.Encoder.Audio=\"Oudio-enkodeerder\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Agtervoegsel\"\nBasic.Settings.Output.Simple.SavePath=\"Opnamepad\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Opnamekwaliteit\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Waarskuwing: opnames kan nie onderbreek word as de opnamekwaliteit op ‘Dieselfde as stroom’ ingestel is nie.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Waarskuwing: Die huidig gekose opnameformaat is onversoenbaar met die gekose stroomenkodeerder(s).\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Is u seker u wil verlieslose kwaliteit gebruik?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Verlieslosekwaliteitwaarskuwing!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Programmatuur (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Apparatuur (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Apparatuur (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Apparatuur (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Apparatuur (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Apparatuur (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Apparatuur (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Apparatuur (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Apparatuur (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Apparatuur (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Apparatuur (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Programmatuur (x264 lae sve-gebruik, verhoog lêergrootte)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Verstek)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Onversoenbare beeldskerpte/beeldduur\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Hierdie stroomdiens ondersteun nie die huidige afvoerresolusie en/of beeldduur nie; dit sal gewysig moet word na die naaste versoenbare waarde:\\n\\n%1\\n\\nWil u voortgaan?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Beeldskerpte: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"RPS: %1\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (verstek) (gemiddeld SVE-gebruik, standaard kwaliteit)\"\nBasic.Settings.Output.CustomMuxerSettings=\"Pasgemaakte mukserinstellings\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Alle lêers\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Lêerpad of URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Houerformaat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Oudio\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Verstekformaat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Houerformaatbeskrywing\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Verstekenkodeerder\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Deaktiveer enkodeerder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Video-enkodeerder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Video-enkodeerderinstellings (indien enige)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Oudio-enkodeerder\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Oudio-enkodeerderinstellings (indien enige)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Mukserinstellings (indien enige)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"sleutelraaminterval (rame)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg-instellings\"\nBasic.Settings.Output.EnableSplitFile=\"Outomatiese lêersplitsing\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Gesplits op tyd\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Gesplits op grootte\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Splits slegs handmatig\"\nBasic.Settings.Output.SplitFile.Time=\"Splits tyd\"\nBasic.Settings.Output.SplitFile.Size=\"Splits grootte\"\nFilenameFormatting.TT.CCYY=\"Jaar, vier syfers\"\nFilenameFormatting.TT.YY=\"Jaar, laaste twee syfers (00-99)\"\nFilenameFormatting.TT.MM=\"Maand as desimale getal (01-12)\"\nFilenameFormatting.TT.DD=\"Dag van die maand, nul-opgevul (01-31)\"\nFilenameFormatting.TT.hh=\"Uur in 24h-formaat (00-23)\"\nFilenameFormatting.TT.mm=\"Minuut (00-59)\"\nFilenameFormatting.TT.ss=\"Sekonde (00-59)\"\nFilenameFormatting.TT.Percent=\"’n %-teken\"\nFilenameFormatting.TT.a=\"Afgekorte weekdagnaam\"\nFilenameFormatting.TT.A=\"Volledige weekdagnaam\"\nFilenameFormatting.TT.b=\"Afgekorte maandnaam\"\nFilenameFormatting.TT.B=\"Volledige maandnaam\"\nFilenameFormatting.TT.d=\"Dag van die maand, nul-opgevul (01-31)\"\nFilenameFormatting.TT.H=\"Uur in 24h-formaat (00-23)\"\nFilenameFormatting.TT.I=\"Uur in 12h-formaat (01-12)\"\nFilenameFormatting.TT.m=\"Maand as desimale getal (01-12)\"\nFilenameFormatting.TT.M=\"Minuut (00-59)\"\nFilenameFormatting.TT.S=\"Sekonde (00-59)\"\nFilenameFormatting.TT.y=\"Jaar, laaste twee syfers (00-99)\"\nFilenameFormatting.TT.Y=\"Jaar\"\nFilenameFormatting.TT.FPS=\"Raampies per sekonde\"\nFilenameFormatting.TT.CRES=\"Basisbeeldskerpte (Doek)\"\nFilenameFormatting.TT.ORES=\"Afvoerbeeldskerpte (Geskaal)\"\nFilenameFormatting.TT.VF=\"Videoformaat\"\nBasic.Settings.Video.BaseResolution=\"Basisbeeldskerpte (Doek)\"\nBasic.Settings.Video.ScaledResolution=\"Afvoerbeeldskerpte (Geskaal)\"\nBasic.Settings.Video.FPS=\"RPS\"\nBasic.Settings.Video.FPSCommon=\"Algemene RPS-waardes\"\nBasic.Settings.Video.FPSInteger=\"Heelgetal-RPS-waarde\"\nBasic.Settings.Video.FPSFraction=\"Breuk-RPS-waarde\"\nBasic.Settings.Video.Numerator=\"Teller\"\nBasic.Settings.Video.Denominator=\"Noemer\"\nBasic.Settings.Video.InvalidResolution=\"Ongeldige beeldskerptewaarde. Moet [breedte] x [hoogte] wees (bv. 1920 x 1080)\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineêr (Vinnigste, maar wasig by skaling)\"\nBasic.Settings.Audio=\"Oudio\"\nBasic.Settings.Audio.Channels=\"Kanale\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Vinnig\"\nBasic.Settings.Audio.Disabled=\"Gedeaktiveer\"\nBasic.Settings.Accessibility=\"Toeganklikheid\"\nBasic.Settings.Accessibility.ColorOverrides=\"Gebruik verskillende kleure\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mengervolumeband (-60 tot -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mengervolumeband (-20 tot -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mengervolumeband (-9 tot -0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mengervolumeband (-60 tot -20dB) (aktief)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mengervolumeband (-20 tot -9dB) (aktief)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mengervolumeband (-9 tot -0dB) (aktief)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Kleurvoorinstelling\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Verstek\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Pasgemaak\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Vriendelik vir kleurblind\"\nBasic.Settings.Advanced=\"Gevorderd\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Hoog\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normaal\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Kleurformaat\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Kleurruimte\"\nBasic.Settings.Advanced.Video.ColorRange=\"Kleurbereik\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Beperk\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Volledig\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Verstek\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Duur\"\nBasic.Settings.Advanced.Network=\"Netwerk\"\nBasic.Settings.Advanced.Network.BindToIP=\"Bind aan IP\"\nBasic.Settings.Advanced.AutoRemux=\"Hermuks outomaties na %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(neem op as mkv)\"\nBasic.AdvAudio=\"Gevorderde oudio-eienskappe\"\nBasic.AdvAudio.ActiveOnly=\"Slegs aktiewe bronne\"\nBasic.AdvAudio.Name=\"Naam\"\nBasic.AdvAudio.VolumeSource=\"Volume vir ‘%1’\"\nBasic.AdvAudio.Balance=\"Balans\"\nBasic.AdvAudio.BalanceSource=\"Balans vir ‘%1’\"\nBasic.Settings.Hotkeys=\"Sneltoetse\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filter volgens sneltoets\"\nBasic.SystemTray.Show=\"Toon\"\nBasic.SystemTray.Hide=\"Versteek\"\nHotkeys.Left=\"Links\"\nHotkeys.Right=\"Regs\"\nHotkeys.Up=\"Boontoe\"\nHotkeys.Down=\"Ondertoe\"\nHotkeys.Menu=\"Kieslys\"\nHotkeys.Space=\"Spasie\"\nHotkeys.MouseButton=\"Muis %1\"\nSceneItemShow=\"Toon ‘%1’\"\nSceneItemHide=\"Versteek ‘%1’\"\nOutputWarnings.MP4Recording=\"Waarskuwing: opnames wat as MP4/MOV bewaar is, is onherstelbaar as die lêer nie korrek afgesluit kan word nie (bv. deur BSOD’s, kragonderbreking ens.). As u meerspoor-oudiosnitte wil opneem, oorweeg dan die gebruik van MKV en hermuks die opname na MP4/MOV nadat dit afgerond is (Lêer → Hermuks opnames)\"\nCodecCompat.CodecPlaceholder=\"Kies enkodeerder…\"\nCodecCompat.ContainerPlaceholder=\"Kies formaat…\"\nNoSources.Title=\"Geen bronne\"\nNoSources.Label=\"U het geen bronne nie.\\nKlik op die +-knop hieronder\\nof regsklik hier om een toe te voeg.\"\nChangeBG=\"Stel kleur in\"\nCustomColor=\"Pasgemaakte kleur\"\nAbout.Donate=\"Maak ’n bydrae\"\nAbout.GetInvolved=\"Raak betrokke\"\nAbout.Authors=\"Outeurs\"\nAbout.License=\"Lisensie\"\nAbout.Error=\"Fout! Lêer kon nie gelees word nie.\\n\\nGaan na: %1\"\nAbout.Contribute=\"Ondersteun die OBS-projek\"\nPreviewTransition=\"Oorvangvoorskou\"\nContextBar.NoSelectedSource=\"Geen bron gekies\"\nContextBar.MediaControls.BlindSeek=\"Mediasoekdingesie\"\nYouTube.Auth.NoChannels=\"Geen kanaal(e) beskikbaar op gekose rekening\"\nYouTube.Actions.Title=\"Titel*\"\nYouTube.Actions.MyBroadcast=\"My uitsending\"\nYouTube.Actions.Description=\"Beskrywing\"\nYouTube.Actions.Privacy=\"Privaatheid*\"\nYouTube.Actions.Privacy.Private=\"Privaat\"\nYouTube.Actions.Privacy.Public=\"Publiek\"\nYouTube.Actions.Privacy.Unlisted=\"Ongelys\"\nYouTube.Actions.Category=\"Kategorie\"\nYouTube.Actions.Thumbnail=\"Kiekie\"\nYouTube.Actions.Thumbnail.SelectFile=\"Kies lêer…\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Geen lêer gekies\"\nYouTube.Actions.MadeForKids=\"Is hierdie video vir kinders gemaak?*\"\nYouTube.Actions.MadeForKids.Yes=\"Ja, dit is vir kinders gemaak\"\nYouTube.Actions.MadeForKids.No=\"Nee, dit is nie vir kinders gemaak nie\"\nYouTube.Actions.AdditionalSettings=\"Bykomende instellings\"\nYouTube.Actions.Latency=\"Vertraging\"\nYouTube.Actions.Latency.Normal=\"Normaal\"\nYouTube.Actions.Latency.Low=\"Laag\"\nYouTube.Actions.ScheduleForLater=\"Skeduleer vir later\"\nYouTube.Actions.RememberSettings=\"Onthou hierdie instellings\"\nYouTube.Actions.Create_Ready=\"Skep uitsending\"\nYouTube.Actions.Create_GoLive=\"Skep uitsending en begin stroom\"\nYouTube.Actions.Choose_Ready=\"Kies uitsending\"\nYouTube.Actions.Choose_GoLive=\"Kies uitsending en begin stroom\"\nYouTube.Actions.Create_Schedule=\"Skeduleer uitsending\"\nYouTube.Actions.Create_Schedule_Ready=\"Skeduleer en kies uitsending\"\nYouTube.Actions.EventsLoading=\"Lys met gebeurtenisse word gelaai…\"\nYouTube.Actions.EventCreated.Title=\"Gebeurtenis geskep\"\nYouTube.Actions.EventCreated.Text=\"Gebeurtenis suksesvol geskep.\"\nYouTube.Actions.Stream=\"Stroom\"\nYouTube.Actions.Stream.ScheduledFor=\"Geskeduleer vir %1\"\nYouTube.Errors.errorStreamInactive=\"YouTube ontvang geen data vir u stroom nie. Gaan u opstelling na en probeer weer.\"\nYouTube.Errors.invalidTransition=\"Die poging tot oorgang was ongeldig. Dit kan wees omdat die stroom nog nie ’n vorige oorgang afgehandel het nie. Wag ’n paar sekondes en probeer weer.\"\nYouTube.DocksRemoval.Title=\"Wis legaat YouTube-blaaierdoks\"\nYouTube.DocksRemoval.Text=\"Hierdie blaaierdoks word as verouderd verwyder:\\n\\n%1\\nGebruik “Doks/YouTube Live Control Room” in plaas daarvan.\"\n"
  },
  {
    "path": "frontend/data/locale/an-ES.ini",
    "content": "Language=\"Aragonés\"\nOK=\"Acceptar\"\nApply=\"Aplicar\"\nCancel=\"Cancelar\"\nClose=\"Zarrar\"\nSave=\"Alzar\"\nDiscard=\"Descartar\"\nDisable=\"Deshabilitar\"\nYes=\"Ell\"\nAdd=\"Anyadir\"\nRemove=\"Eliminar\"\nRename=\"Renombrar\"\nInteract=\"Interactuar\"\nFilters=\"Filtros\"\nProperties=\"Propiedaz\"\nMoveUp=\"Mover enta alto\"\nMoveDown=\"Mover enta abaixo\"\nSettings=\"Achustes\"\nDisplay=\"Pantalla\"\nName=\"Nombre\"\nExit=\"Salir\"\nMixer=\"Mesclador d'audio\"\nBrowse=\"Examinar\"\nStereo=\"Estéreo\"\nDroppedFrames=\"Fotogramas Perdius %1 (%2%)\"\nClear=\"Borrar\"\nRevert=\"Revertir\"\nShow=\"Amostrar\"\nHide=\"Amagar\"\nUnhideAll=\"Amostrar-lo tot\"\nUntitled=\"Sin Titol\"\nNew=\"Nuevo\"\nDuplicate=\"Duplicar\"\nEnable=\"Habilitar\"\nDisableOSXVSync=\"Desahabilitar V-Sync en macOS\"\nResetOSXVSyncOnExit=\"Reiniciar V-Sync de macOS a la salida\"\nHighResourceUsage=\"Sobrecarga en codificar! Considera baixar los achustes de video u fe servir una configuración mas rapida d'o codificador.\"\nTransition=\"Transición\"\nQuickTransitions=\"Transicions rapidas\"\nFadeToBlack=\"Funir a negro\"\nLeft=\"Zurda\"\nRight=\"Dreita\"\nTop=\"Alto\"\nBottom=\"Abaixo\"\nReset=\"Reiniciar\"\nHours=\"Horas\"\nMinutes=\"Minutos\"\nSeconds=\"Segundos\"\nDeprecated=\"Obsoleto\"\nReplayBuffer=\"Bufer de reproducción\"\nImport=\"Importar\"\nExport=\"Exportar\"\nCopy=\"Copiar\"\nPaste=\"Apegar\"\nPasteReference=\"Apegar (Referencia)\"\nPasteDuplicate=\"Apegar (Duplicau)\"\nRemuxRecordings=\"Convertir gravacions\"\nNext=\"Siguient\"\nBack=\"Anterior\"\nDefaults=\"Per defecto\"\nRestoreDefaults=\"Por defecto\"\nHideMixer=\"Amagar en o mesclador\"\nTransitionOverride=\"Anulación d'a transición\"\nShowTransition=\"Transición Amostrar\"\nHideTransition=\"Transición Amagar\"\nNone=\"Garra\"\nStudioMode.Preview=\"Vista Previa\"\nStudioMode.Program=\"Programa\"\nStudioMode.PreviewSceneName=\"Vista previa: %1\"\nStudioMode.ProgramSceneName=\"Programa: %1\"\nShowInMultiview=\"Amostrar en vista multiple\"\nVerticalLayout=\"Interficie Vertical\"\nGroup=\"Grupo\"\nDoNotShowAgain=\"No amostrar de nuevo\"\nDefault=\"(Per defecto)\"\nCalculating=\"Calculando...\"\nFullscreen=\"Pantalla completa\"\nWindowed=\"En finestra\"\nRefreshBrowser=\"Actualizar\"\nAspectRatio=\"Relación d'aspecto <b>%1:%2</b>\"\nLockVolume=\"Blocar volumen\"\nLogViewer=\"Visor de rechistros\"\nShowOnStartup=\"Amostrar a l'inicio\"\nOpenFile=\"Ubrir fichero\"\nAddSource=\"Anyadir fuent\"\nAlreadyRunning.Title=\"OBS ya se ye executando\"\nAlreadyRunning.Text=\"OBS ya se ye executando! De no estar que quieras fer esto, per favor, zarra totas las finestras de OBS antes d'intentar iniciar una nueva. Si tiens configurau OBS pa que se minimice a la barra de fayenas, compreba si sigue executando-se astí.\"\nAlreadyRunning.LaunchAnyway=\"Lanzar de totas trazas\"\nChromeOS.Title=\"Plataforma no suportada\"\nDockCloseWarning.Title=\"Zarrando finestra acoplable\"\nExtraBrowsers=\"Panels de navegador personalizaus\"\nExtraBrowsers.Info=\"Anyade panels dando-les un nombre y una URL, dimpués fe clic en Aplicar u Zarrar pa ubrir los panels. Puez anyadir u eliminar los panels en cualsequier momento.\"\nExtraBrowsers.DockName=\"Nombre d'o Panel\"\nAuth.Authing.Title=\"Autentificando...\"\nAuth.Authing.Text=\"Autentificando con %1, per favor aguarda...\"\nAuth.AuthFailure.Title=\"Fallo d'autenticación\"\nAuth.AuthFailure.Text=\"Error en autentificar con %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Se requiere autentificación\"\nAuth.InvalidScope.Text=\"Los requisitos d'autenticación pa %1 han cambiau. Cualques caracteristicas pueden no estar disponibles.\"\nAuth.LoadingChannel.Title=\"Cargando información d'a canal...\"\nAuth.LoadingChannel.Text=\"Cargando información d'a canal pa %1, per favor aspere...\"\nAuth.LoadingChannel.Error=\"No s'ha puesto obtener la información d'a canal\"\nAuth.ChannelFailure.Title=\"Error en cargar canal\"\nAuth.ChannelFailure.Text=\"Error en cargar la información d'a canal pa %1\\n\\n%2: %3\"\nAuth.StreamInfo=\"Información de Stream\"\nTwitchAuth.Stats=\"Estatisticas de Twitch\"\nTwitchAuth.Feed=\"Fuent d'actividaz de Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"No se podió obtener la clau de transmisión\"\nTwitchAuth.TwoFactorFail.Text=\"OBS no podió connectar-se a la suya cuenta de Twitch. Per favor, s'asegure que l'autenticación en dos pasos ye configurada en a suya configuración de seguranza <a href='https://www.twitch.tv/settings/security'>Configuración de seguranza de Twitch</a> ya que esto ye necesario pa transmitir.\"\nRestreamAuth.Channels=\"Canales Restream\"\nCopy.Filters=\"Copiar filtros\"\nPaste.Filters=\"Apegar filtros\"\nBrowserPanelInit.Title=\"Iniciando navegador...\"\nBrowserPanelInit.Text=\"Iniciando navegador, per favor aguarde...\"\nBandwidthTest.Region=\"Rechión\"\nBandwidthTest.Region.US=\"Estaus Unius\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Other=\"Atros\"\nBasic.AutoConfig=\"Aduyant de configuración automatica\"\nBasic.AutoConfig.ApplySettings=\"Aplicar configuración\"\nBasic.AutoConfig.StartPage=\"Información sobre l'uso\"\nBasic.AutoConfig.StartPage.SubTitle=\"Especifique pa que quiere usar lo programa\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimizar pa transmisions, la gravación ye secundaria\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimizar nomás pa gravación, no voi a fer una transmisión\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Nomás faré servir la camara virtual\"\nBasic.AutoConfig.VideoPage=\"Achustes de video\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Especifique la configuración de video que le fería goyo usar\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Usar Actual (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Pantalla %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Fer servir l'actual (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 u 30, pero emplegar 60 cuan sía posible\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 u 30, pero emplegar alta resolución\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Nota: La resolución d'o lienzo (base) no ye necesariament la mesma que la resolución d'a transmisión u gravación. La resolución actual puede estar reducida d'o lienzo pa reducir l'uso d'os recursos u d'a taxa de bits.\"\nBasic.AutoConfig.StreamPage=\"Información de servicio de Stream\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Per favor, escribe información sobre lo tuyo servicio de stream\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Connectar cuenta (recomendau)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Desconnectar cuenta\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Desconnectar cuenta?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Este cambio s'aplicará immediatament. Yes seguro que quiers desconnectar la tuya cuenta?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Obtener clau de transmisión\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Mas información\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Usar clau d'emisión\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Usar una clau de retransmisión (abanzau)\"\nBasic.AutoConfig.StreamPage.Service=\"Servicio\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Amostrar-lo tot...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Personalizau...\"\nBasic.AutoConfig.StreamPage.Server=\"Servidor\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Clau de retransmisión\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Clau d'o codificador\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Cuenta connectada\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Estimar taxa de bits con una preba d'amplura de banda (puede tardar bel minuto)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferir codificación per hardware\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"La codificación per hardware elimina la mayoría de l'uso d'a CPU, pero puede requerir mas taxa de bits pa obtener lo mesmo nivel de calidat.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Alvertencia de transmisión\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"La preba d'amplura de banda ye en momentos de transmitir datos de video aleatorios sin audio a la tuya canal. Si puez, ye recomendable desactivar temporalment la gravación d'os videos d'as transmisions y fer la transmisión privada dimpués que la preba haiga finalizau. Deseya continar?\"\nBasic.AutoConfig.TestPage=\"Resultau final\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Lo programa agora ye executando un conchunto de prebas pa estimar los achustes optimos\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Preba completada\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Executando preba d'amplo de banda, esto puede tardar uns menutos...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Connectando a: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"No se podió connectar a garra servidor. Per favor revise la suya connexión a Internet y lo intente de nuevo.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Prebando amplo de banda pa: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Prebando codificador de transmisión, esto puede tardar un menuto...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Prebando codificador de gravación, esto puede tardar un menuto...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"No s'ha puesto iniciar lo codificador\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Prebando %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Codificador de transmisión\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Codificador de gravación\"\nBasic.AutoConfig.TestPage.Result.Header=\"Lo programa ha determinau que estes achustes estimaus son los ideyals pa tu:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Pa utilizar estes achustes, fe clic en \\\"Aplicar Configuración\\\". Pa tornar a configurar l'aduyant, fe clic en Dezaga. Pa configurar los achustes per tu mesmo, fe clic en Cancelar y ubre los achustes.\"\nBasic.AutoConfig.Info=\"L'aduyant de configuración automatica determinará la millor configuración basada en as especificacions d'o suyo ordinador y la velocidat d'Internet.\"\nBasic.AutoConfig.RunAnytime=\"Esto puede executar-se en cualsequier momento dende lo menú Ferramientas.\"\nBasic.Stats=\"Estatisticas\"\nBasic.Stats.CPUUsage=\"Uso de CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Espacio en disco disponible\"\nBasic.Stats.MemoryUsage=\"Uso de memoria\"\nBasic.Stats.AverageTimeToRender=\"Tiempo de meya pa procesar un fotograma\"\nBasic.Stats.SkippedFrames=\"Fotogramas omitius per retardo en a codificación\"\nBasic.Stats.MissedFrames=\"Fotogramas perdius per retardo de procesamiento\"\nBasic.Stats.Output.Stream=\"Transmisión\"\nBasic.Stats.Output.Recording=\"Gravación\"\nBasic.Stats.Status=\"Estau\"\nBasic.Stats.Status.Recording=\"Gravando\"\nBasic.Stats.Status.Live=\"EN VIVO\"\nBasic.Stats.Status.Reconnecting=\"Tornando a connectar\"\nBasic.Stats.Status.Inactive=\"Inactivo\"\nBasic.Stats.Status.Active=\"Activo\"\nBasic.Stats.DroppedFrames=\"Fotogramas Perdius (Ret)\"\nBasic.Stats.MegabytesSent=\"Salida de datos total\"\nBasic.Stats.Bitrate=\"Taxa de bits\"\nBasic.Stats.DiskFullIn=\"Disco pleno en (aprox.)\"\nBasic.Stats.ResetStats=\"Reiniciar estatisticas\"\nResetUIWarning.Title=\"Ye seguro que deseya restablir la interficie?\"\nUpdater.Title=\"Nueva actualización disponible\"\nUpdater.Text=\"I hai una nueva versión disponible:\"\nUpdater.UpdateNow=\"Actualizar agora\"\nUpdater.RemindMeLater=\"Recordar-me-lo mas tarde\"\nUpdater.Skip=\"Blincar Versión\"\nUpdater.NoUpdatesAvailable.Title=\"No i hai actualizacions disponibles\"\nUpdater.NoUpdatesAvailable.Text=\"No i hai actualizacions disponibles actualment\"\nUpdater.FailedToLaunch=\"No s'ha puesto lanzar l'actualizador\"\nQuickTransitions.DuplicateScene=\"Duplicar scena\"\nQuickTransitions.EditProperties=\"Duplicar fuents\"\nQuickTransitions.HotkeyName=\"Transición Rapida: %1\"\nBasic.AddTransition=\"Anyadir transición configurable\"\nBasic.RemoveTransition=\"Sacar transición configurable\"\nBasic.TransitionProperties=\"Propiedaz d'a transición\"\nBasic.SceneTransitions=\"Transicions de scena\"\nBasic.TransitionDuration=\"Duración\"\nBasic.TogglePreviewProgramMode=\"Modo Estudeyo\"\nUndo.Undo=\"Desfer\"\nUndo.Redo=\"Refer\"\nUndo.Add=\"Anyadir '%1'\"\nUndo.Delete=\"Borrar '%1'\"\nUndo.Rename=\"Renombrar '%1'\"\nUndo.SceneCollection.Switch=\"Pasara a '%1'\"\nUndo.Item.Undo=\"Desfer '%1'\"\nUndo.Item.Redo=\"Refer '%1'\"\nUndo.Sources.Multi=\"Borrar %1 fuents\"\nUndo.Filters=\"Filtrar los cambios en '%1'\"\nUndo.Filters.Paste.Single=\"Apegar lo filtro '%1' a '%2'\"\nUndo.Filters.Paste.Multiple=\"Copiar filtros de '%1' a '%2'\"\nUndo.Transform=\"Transformar las fuents a '%1'\"\nUndo.Transform.Paste=\"Apegar transformación en '%1'\"\nUndo.Transform.Rotate=\"Rotación en '%1'\"\nUndo.Transform.Reset=\"Reinicio d'a transformación en '%1'\"\nUndo.Transform.HFlip=\"Chira horizontalment en '%1'\"\nUndo.Transform.VFlip=\"Chira verticalment en '%1'\"\nUndo.Transform.FitToScreen=\"Achustar a la pantalla en '%1'\"\nUndo.Transform.StretchToScreen=\"Estirar a la pantalla en '%1'\"\nUndo.Transform.Center=\"Centrar en a pantalla en '%1'\"\nUndo.Transform.VCenter=\"Centrar verticalment en a pantalla en '%1'\"\nUndo.Transform.HCenter=\"Centrar horizontalment en a pantalla en '%1'\"\nUndo.Volume.Change=\"Cambio de volumen en '%1'\"\nUndo.Volume.Mute=\"Silenciar '%1'\"\nUndo.Volume.Unmute=\"No silenciar '%1'\"\nUndo.Balance.Change=\"Cambio de balanz d'audio en '%1'\"\nUndo.SyncOffset.Change=\"Cambio de compensación de sincronización d'audio en '%1'\"\nUndo.MonitoringType.Change=\"Cambiar monitorización d'audio en '%1'\"\nUndo.Mixers.Change=\"Cambiar los mescladors d'audio en '%1'\"\nUndo.ForceMono.On=\"Habilitar forzar mono en '%1'\"\nUndo.ForceMono.Off=\"Deshabilitar forzar mono en '%1'\"\nUndo.Properties=\"Cambio de propiedat en '%1'\"\nUndo.Scene.Duplicate=\"Duplicar scena '%1'\"\nUndo.ShowTransition=\"Transición Amostrar en '%1'\"\nUndo.HideTransition=\"Transición Amagar en '%1'\"\nUndo.ShowSceneItem=\"Amostrar '%1' en '%2'\"\nUndo.HideSceneItem=\"Amagar '%1' en '%2\"\nUndo.ReorderSources=\"Reubrir fuents en '%1'\"\nUndo.MoveUp=\"Mover «%1» entalto en «%2»\"\nUndo.MoveDown=\"Mover «%1» enta baixo en «%2»\"\nUndo.MoveToTop=\"Mover «%1» ta l'inicio en «%2»\"\nUndo.MoveToBottom=\"Mover «%1» ta la fin en «%2»\"\nUndo.PasteSource=\"Apegar fuent(s) en '%1'\"\nUndo.PasteSourceRef=\"Apegar referencia(s) de fuent(s) en '%1'\"\nUndo.GroupItems=\"Agrupar elementos en '%1'\"\nTransitionNameDlg.Text=\"Per favor, introduzca lo nombre d'a transición\"\nTransitionNameDlg.Title=\"Nombre d'a transición\"\nTitleBar.Profile=\"Perfil\"\nTitleBar.Scenes=\"Scenas\"\nNameExists.Title=\"Ixe nombre ya existe\"\nNameExists.Text=\"Este nombre ya ye en uso.\"\nNoNameEntered.Title=\"Per favor, introduzca un nombre valido\"\nNoNameEntered.Text=\"No puede deixar lo nombre vuedo.\"\nConfirmStart.Title=\"Encomenzar la transmisión?\"\nConfirmStart.Text=\"Yes seguro de que deseyas encomenzar la transmisión?\"\nConfirmStop.Title=\"Aturar la transmisión?\"\nConfirmStop.Text=\"Yes seguro que deseyas aturar la transmisión?\"\nConfirmStopRecord.Title=\"Aturar la gravación?\"\nConfirmStopRecord.Text=\"Yes seguro que deseyas aturar la gravación?\"\nConfirmBWTest.Title=\"Encomenzar la preba d'amplaria de banda?\"\nConfirmBWTest.Text=\"Tiens OBS configurau en modo de preba d'amplo de banda. Este modo permite prebas de ret sin que la tuya canal sía en vivo. Una vegada que haigas rematau de prebar, te caldrá desactivar-lo pa que los espectadors puedan veyer lo tuyo stream.\\n\\nQuiers continar?\"\nConfirmRemove.Title=\"Confirmar borrau\"\nConfirmRemove.TextMultiple=\"Seguro que quiers borrar %1 elementos?\"\nOutput.StartStreamFailed=\"No s'ha puesto iniciar la emisión\"\nOutput.StartRecordingFailed=\"No s'ha puesto iniciar la gravación\"\nOutput.StartReplayFailed=\"No s'ha puesto iniciar la memoria intermedia de reproducción\"\nOutput.StartVirtualCamFailed=\"No s'ha puesto iniciar una camara virtual\"\nOutput.StartFailedGeneric=\"No se podió encetar la salida. Per favor comprebe los logs pa pero detalles. \\n\\nNota: Si estas usando los codificadors de NVENC u AMD, asegura-te que los tuyos drivers de video son esviellaus.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"No se pueden alzar repeticions mientres ye pausau\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Alvertencia: No se pueden alzar repeticions mientres la gravación ye pausada.\"\nOutput.ConnectFail.Title=\"Error en connectar-se\"\nOutput.ConnectFail.BadPath=\"URL rota d'acceso u connexión no valida. Per favor, comprebe la suya configuración pa confirmar que ye correcta.\"\nOutput.ConnectFail.ConnectFailed=\"No se podió connectar a lo servidor\"\nOutput.ConnectFail.InvalidStream=\"No se podió acceder a la canal especificada u clau de transmisión, per favor comprebe la suya clau de transmisión. Si ye correcta, puede haber un problema en connectar a lo servidor.\"\nOutput.ConnectFail.Error=\"S'ha produciu una error inasperada en intentar connectar con o servidor. Mas información en o fichero de rechistro.\"\nOutput.ConnectFail.Disconnected=\"Desconnectau d'o servidor.\"\nOutput.StreamEncodeError.Title=\"Error de codificación\"\nOutput.StreamEncodeError.Msg=\"S'ha produciu una error de codificación mientres lo Streaming.\"\nOutput.RecordFail.Title=\"No s'ha puesto iniciar la gravación\"\nOutput.RecordFail.Unsupported=\"Lo formato de salida no ye suportau u no suporta mas d'una pista d'audio. Per favor, compreba los tuyos achustes y intenta-lo de nuevo.\"\nOutput.RecordNoSpace.Title=\"No i hai suficient espacio en disco\"\nOutput.RecordNoSpace.Msg=\"No i hai suficient espacio en disco pa continar gravando.\"\nOutput.RecordError.Title=\"Error en a gravación\"\nOutput.RecordError.Msg=\"S'ha produciu una error no especificada entre la gravación.\"\nOutput.RecordError.EncodeErrorMsg=\"S'ha produciu una error de codificación mientres se gravaba.\"\nOutput.BadPath.Title=\"Rota de fichero incorrecta\"\nRemux.SourceFile=\"Gravación OBS\"\nRemux.TargetFile=\"Fichero de destín\"\nRemux.Remux=\"Convertir\"\nRemux.Stop=\"Aturar la conversión\"\nRemux.ClearFinished=\"Borrar los elementos finalizaus\"\nRemux.ClearAll=\"Borrar toz los elementos\"\nRemux.OBSRecording=\"Gravación OBS\"\nRemux.FinishedTitle=\"Conversión finalizada\"\nRemux.Finished=\"Gravación convertida\"\nRemux.FinishedError=\"Gravación convertida, pero lo fichero podría estar incompleto\"\nRemux.SelectRecording=\"Tríe gravación de OBS...\"\nRemux.SelectTarget=\"Tríe fichero de destín...\"\nRemux.FileExistsTitle=\"Los fichers de destín ya existen\"\nRemux.FileExists=\"Los siguients fichers de destín ya existen. Quiers reemplazar-los?\"\nRemux.ExitUnfinishedTitle=\"Conversión en curso\"\nRemux.ExitUnfinished=\"La conversión no ha finalizau, aturar-la agora puede inutilizar lo fichero de destín.\\nYes seguro de querer aturar la conversión?\"\nRemux.HelpText=\"Suelta los fichers en esta finestra pa convertir-los, u tría una celda vueda \\\"OBS Recording\\\" pa buscar un fichero.\"\nMissingFiles=\"Fichers no trobaus\"\nMissingFiles.MissingFile=\"Fichero perdiu\"\nMissingFiles.NewFile=\"Nuevo fichero\"\nMissingFiles.HelpText=\"Faltan cualques fichers dende la zaguera vegada que usés OBS.\"\nMissingFiles.Clear=\"<vacío>\"\nMissingFiles.Search=\"Directorio de busqueda...\"\nMissingFiles.SelectFile=\"Triar fichero...\"\nMissingFiles.SelectDir=\"Tría carpeta an buscar\"\nMissingFiles.State=\"Estau\"\nMissingFiles.Missing=\"Perdiu\"\nMissingFiles.Replaced=\"Reemplazau\"\nMissingFiles.Cleared=\"Vuedo\"\nMissingFiles.Found=\"Trobau\"\nMissingFiles.AutoSearch=\"Trobadas coincidencias de fichers adicionals\"\nMissingFiles.AutoSearchText=\"OBS ha trobau coincidencias adicionals pa fichers perdius en ixe directorio. Quiers anyadir-los?\"\nMissingFiles.NoMissing.Title=\"Comprebación d'os fichers que faltan\"\nMissingFiles.NoMissing.Text=\"No pareix que falte garra fichero\"\nBasic.DesktopDevice1=\"Audio d'o escritorio\"\nBasic.DesktopDevice2=\"Audio d'o escritorio 2\"\nBasic.Scene=\"Scena\"\nBasic.DisplayCapture=\"Captura de pantalla\"\nBasic.Main.PreviewConextMenu.Enable=\"Habilitar previsualización\"\nBasic.Main.Preview.Disable=\"Deshabilitar previsualización\"\nScaleFiltering=\"Filtrau d'escala\"\nScaleFiltering.Point=\"Punto\"\nScaleFiltering.Bilinear=\"Bilineal\"\nScaleFiltering.Bicubic=\"Bicúbico\"\nScaleFiltering.Area=\"Aria\"\nDeinterlacing=\"Desentrelazado\"\nDeinterlacing.Discard=\"Descartar\"\nDeinterlacing.TopFieldFirst=\"Campo Superior Primer\"\nDeinterlacing.BottomFieldFirst=\"Campo Inferior Primer\"\nVolControl.SliderUnmuted=\"Eslizador de volumen pa '%1':\"\nVolControl.SliderMuted=\"Eslizador de volumen pa '%1': (actualment silenciau)\"\nVolControl.Mute=\"Silenciar '%1'\"\nVolControl.Properties=\"Propiedaz pa '%1'\"\nBasic.Main.AddSceneDlg.Title=\"Anyadir scena\"\nBasic.Main.AddSceneDlg.Text=\"Per favor, introduzca lo nombre d'a scena\"\nBasic.Main.DefaultSceneName.Text=\"Scena %1\"\nBasic.Main.AddSceneCollection.Title=\"Colección de &scenas\"\nBasic.Main.AddSceneCollection.Text=\"Per favor escriba lo nombre d'a colección de scenas\"\nBasic.Main.RenameSceneCollection.Title=\"Cambiar lo nombre de colección de scenas\"\nAddProfile.Title=\"Anyadir perfil\"\nAddProfile.Text=\"Escriba lo nombre d'o perfil\"\nAddProfile.WizardCheckbox=\"Amostrar l'aduyant de configuración automatica\"\nRenameProfile.Title=\"Renombrar perfil\"\nBasic.Main.MixerRename.Title=\"Cambiar lo nombre d'a fuent d'audio\"\nBasic.Main.MixerRename.Text=\"Per favor, introduzca lo nombre d'a fuent d'audio\"\nBasic.Main.PreviewDisabled=\"Vista previa desactivada\"\nBasic.SourceSelect=\"Creyar/triar fuent\"\nBasic.SourceSelect.CreateNew=\"Creyar nuevo\"\nBasic.SourceSelect.AddExisting=\"Anyadir existents\"\nBasic.SourceSelect.AddVisible=\"Fer visible la fuent\"\nBasic.Main.Sources.Visibility=\"Visibilidat\"\nBasic.Main.Sources.VisibilityDescription=\"Controla la visibilidat de '%1' en o lienzo\"\nBasic.Main.Sources.Lock=\"Blocar\"\nBasic.Main.Sources.LockDescription=\"Bloca la posición y la escala de '%1' en o lienzo\"\nBasic.PropertiesWindow=\"Propiedaz pa '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (triar: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Triar color\"\nBasic.PropertiesWindow.SelectFont=\"Tríe fuent\"\nBasic.PropertiesWindow.ConfirmTitle=\"Configuración cambiada\"\nBasic.PropertiesWindow.Confirm=\"I hai cambios sin alzar. Deseyas conservar-los?\"\nBasic.PropertiesWindow.NoProperties=\"No i hai propiedaz disponibles\"\nBasic.PropertiesWindow.AddFiles=\"Adhibir fichers\"\nBasic.PropertiesWindow.AddDir=\"Anyadir Carpeta\"\nBasic.PropertiesWindow.AddURL=\"Anyadir rota/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Anyadir Carpeta a '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Anyadir fichers a '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Adhibir dentrada a '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Editar dentrada de '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Valors FPS simples\"\nBasic.PropertiesView.FPS.Rational=\"Valors raciones de FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Rangos valius de FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"Quiers ubrir este vinclo en o tuyo navegador per defecto?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Ubrir URL\"\nBasic.InteractionWindow=\"Interactuando con '%1'\"\nBasic.StatusBar.Reconnecting=\"Desconnectau, reconexión en %2 segundo(s) (tentativa%1)\"\nBasic.StatusBar.AttemptingReconnect=\"Intentando tornar a connectar... (intento %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Reconexión exitosa\"\nBasic.StatusBar.Delay=\"Retardo (%1 sec)\"\nBasic.StatusBar.DelayStartingIn=\"Retardo (encomenzando en %1 s)\"\nBasic.StatusBar.DelayStoppingIn=\"Retardo (aturando en %1 sec)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Retardo (aturando en %1 s, encomentando en %2 s)\"\nBasic.StatusBar.RecordingSavedTo=\"S'ha alzau la gravación en '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"S'ha alzau la memoria intermedia de reproducción en '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"S'ha alzau lo pantallazo en '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"S'ha convertiu automaticament la gravación en '%1'\"\nBasic.Filters=\"Filtros\"\nBasic.Filters.AsyncFilters=\"Filtros d'audio/video\"\nBasic.Filters.AudioFilters=\"Filtros d'audio\"\nBasic.Filters.EffectFilters=\"Filtros d'efecto\"\nBasic.Filters.Title=\"Filtros pa '%1'\"\nBasic.Filters.AddFilter.Title=\"Nombre d'o filtro\"\nBasic.Filters.AddFilter.Text=\"Per favor especifique lo nombre d'o filtro\"\nBasic.TransformWindow=\"Achustes de scena\"\nBasic.TransformWindow.Position=\"Posición\"\nBasic.TransformWindow.PositionX=\"Posición X\"\nBasic.TransformWindow.PositionY=\"Posición Y\"\nBasic.TransformWindow.Rotation=\"Rotación\"\nBasic.TransformWindow.Size=\"Grandaria\"\nBasic.TransformWindow.Width=\"Amplaria\"\nBasic.TransformWindow.Height=\"Altaria\"\nBasic.TransformWindow.Alignment=\"Aliniamiento posicional\"\nBasic.TransformWindow.BoundsType=\"Tipo de cuadro delimitador\"\nBasic.TransformWindow.BoundsAlignment=\"Aliniamiento en o cuadro delimitador\"\nBasic.TransformWindow.Bounds=\"Grandaria de cuadro delimitador\"\nBasic.TransformWindow.BoundsWidth=\"Amplaria d'o cuadro delimitador\"\nBasic.TransformWindow.BoundsHeight=\"Altaria d'o cuadro delimitador\"\nBasic.TransformWindow.Crop=\"Retalle\"\nBasic.TransformWindow.CropLeft=\"Retallar per la zurda\"\nBasic.TransformWindow.CropRight=\"Retallar per la dreita\"\nBasic.TransformWindow.CropTop=\"Retallar per alto\"\nBasic.TransformWindow.CropBottom=\"Retallar per abaixo\"\nBasic.TransformWindow.Alignment.TopLeft=\"Parte superior zurda\"\nBasic.TransformWindow.Alignment.TopCenter=\"Parte superior central\"\nBasic.TransformWindow.Alignment.TopRight=\"Parte superior dreita\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Parte central zurda\"\nBasic.TransformWindow.Alignment.Center=\"Centrau\"\nBasic.TransformWindow.Alignment.CenterRight=\"Parte central dreita\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Parte inferior zurda\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Parte central inferior\"\nBasic.TransformWindow.Alignment.BottomRight=\"Parte inferior dreita\"\nBasic.TransformWindow.BoundsType.None=\"Sin limites\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Limitau a grandaria maxima\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Escalar dica limites internos\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Escalar dica limites externos\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"La escala a l'amplaria de limites\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"La escala a l'altura de limites\"\nBasic.TransformWindow.BoundsType.Stretch=\"Extender dica los limites\"\nBasic.TransformWindow.Title=\"Editar la transformación pa '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"No s'ha triau garra fuent\"\nBasic.Main.AddSourceHelp.Title=\"No se puede adhibir la fuent\"\nBasic.Main.AddSourceHelp.Text=\"Has de tener a lo menos 1 scena pa adhibir una fuent.\"\nBasic.Main.Scenes=\"Scenas\"\nBasic.Main.Sources=\"Fuentes\"\nBasic.Main.Source=\"Fuent\"\nBasic.Main.Connecting=\"Connectando...\"\nBasic.Main.StartRecording=\"Iniciar la gravación\"\nBasic.Main.StartReplayBuffer=\"Iniciar la reproducción d'a memoria intermedia\"\nBasic.Main.SaveReplay=\"Alzar repetición\"\nBasic.Main.StartStreaming=\"Iniciar la transmisión\"\nBasic.Main.StartBroadcast=\"Emitir en directo\"\nBasic.Main.StartVirtualCam=\"Iniciar la camara virtual\"\nBasic.Main.StopRecording=\"Aturar gravación\"\nBasic.Main.PauseRecording=\"Pausar la gravación\"\nBasic.Main.UnpauseRecording=\"Reprener la gravación\"\nBasic.Main.StoppingRecording=\"Aturando la gravación...\"\nBasic.Main.StopReplayBuffer=\"Aturar la reproducción d'a memoria intermedia\"\nBasic.Main.StoppingReplayBuffer=\"Aturando la reproducción d'a memoria intermedia...\"\nBasic.Main.StopStreaming=\"Aturar Transmisión\"\nBasic.Main.StopBroadcast=\"Finalizar la emisión\"\nBasic.Main.StoppingStreaming=\"Aturando la transmisión...\"\nBasic.Main.ForceStopStreaming=\"Aturar Transmisión (descartar retardo)\"\nBasic.Main.ShowContextBar=\"Amostrar barra de ferramientas de fuent\"\nBasic.Main.HideContextBar=\"Amagar barra de ferramientas de fuent\"\nBasic.Main.StopVirtualCam=\"Aturar Camera Virtual\"\nBasic.Main.Group=\"Grupo %1\"\nBasic.Main.GroupItems=\"Agrupar los elementos triaus\"\nBasic.Main.Ungroup=\"Desagrupar\"\nBasic.Main.GridMode=\"Modo Cuadricula\"\nBasic.Main.ListMode=\"Modo Lista\"\nBasic.MainMenu.File=\"&Fichero\"\nBasic.MainMenu.File.Export=\"&Exportar\"\nBasic.MainMenu.File.Import=\"&Importar\"\nBasic.MainMenu.File.ShowRecordings=\"Amostrar G&ravacions\"\nBasic.MainMenu.File.Remux=\"Con&vertir Gravacions\"\nBasic.MainMenu.File.Settings=\"&Configuración\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Amostrar carpeta de configuración\"\nBasic.MainMenu.File.ShowProfileFolder=\"Amostrar carpeta de perfils\"\nBasic.MainMenu.File.ShowMissingFiles=\"Comprebar si i faltan fichers\"\nBasic.MainMenu.File.Exit=\"&Salir\"\nBasic.MainMenu.Edit=\"&Editar\"\nBasic.MainMenu.Edit.Undo=\"&Desfer\"\nBasic.MainMenu.Edit.Redo=\"&Refer\"\nBasic.MainMenu.Edit.LockPreview=\"B&locar vista previa\"\nBasic.MainMenu.Edit.Scale=\"Vi&sta previa y escala\"\nBasic.MainMenu.Edit.Scale.Window=\"Achustar a la finestra\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Lienzo (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Salida (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"&Transformar\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Editar Transformación...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Copiar transformación\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Apegar trasformación\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Restablir transformación\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Chirar 90 graus en o sentiu d'as agullas d'o reloch\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Chirar 90 graus contra lo sentiu d'as agullas d'o reloch\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Chirar 180 graus\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Rotar &horizontalment\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Rotar &verticalment\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Achustar a la pantalla (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"E&stirar la pantalla\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centrar en a pantalla\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centrar Verticalment\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centrar Horizontalment\"\nBasic.MainMenu.Edit.Order=\"&Ordenar\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Mover entalto (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Mover enta abaixo (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Mover a l'inicio (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Mover a la fin (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Propiedaz d'&audio abanzadas\"\nBasic.MainMenu.View=\"&Vista\"\nBasic.MainMenu.View.Toolbars=\"Barra de Ferramien&tas\"\nBasic.MainMenu.View.ContextBar=\"Barra de ferramientas de fuent\"\nBasic.MainMenu.View.SourceIcons=\"&Iconos de fuents\"\nBasic.MainMenu.View.StatusBar=\"Barra d'E&stau\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Pantalla completa\"\nBasic.MainMenu.SceneCollection=\"Colección de &scenas\"\nBasic.MainMenu.Profile=\"&Perfil\"\nBasic.MainMenu.Profile.Import=\"Importar perfil\"\nBasic.MainMenu.Profile.Export=\"Exportar perfil\"\nBasic.MainMenu.SceneCollection.Import=\"Importar colección de scenas\"\nBasic.MainMenu.SceneCollection.Export=\"Exportar colección de scenas\"\nBasic.MainMenu.Profile.Exists=\"Lo perfil ya existe\"\nBasic.MainMenu.Tools=\"Ferramien&tas\"\nBasic.MainMenu.Help=\"&Aduya\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal d'aduya\"\nBasic.MainMenu.Help.Website=\"Visitar Puesto &Web\"\nBasic.MainMenu.Help.Discord=\"Unir-se a lo servidor de &Discord\"\nBasic.MainMenu.Help.Logs=\"Fichers de rechistro (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Amostrar ficher&s de rechistro\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"&Carga d'o fichero de rechistro actual\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Veyer rechistro actual\"\nBasic.MainMenu.Help.CheckForUpdates=\"Comprebar Actualizacions\"\nBasic.MainMenu.Help.CrashLogs=\"Informes d'e&rror\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Amo&strar informes d'error\"\nBasic.MainMenu.Help.About=\"&Alredor de\"\nBasic.Settings.ProgramRestart=\"Lo programa ha de reiniciar-se pa que esta configuración surta efecto.\"\nBasic.Settings.ConfirmTitle=\"Confirmar cambios\"\nBasic.Settings.Confirm=\"I hai cambios sin alzar. Alzar los cambios?\"\nBasic.Settings.General=\"Cheneral\"\nBasic.Settings.General.Language=\"Idioma\"\nBasic.Settings.General.EnableAutoUpdates=\"Comprebar si i hai actualizacions automaticas a l'inicio\"\nBasic.Settings.General.OpenStatsOnStartup=\"Ubrir lo dialogo d'estatisticas a l'inicio\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Amostrar lo dialogo de confirmación cuan s'inicie una transmisión\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Amostrar lo dialogo de confirmación cuan s'ature una transmisión\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Amostrar lo dialogo de confirmación cuan s'inicie una gravación\"\nBasic.Settings.General.Projectors=\"Prochectors\"\nBasic.Settings.General.HideProjectorCursor=\"Amagar lo cursor sobre los prochectors\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Prochectos siempre en o cobalto\"\nBasic.Settings.General.Snapping=\"Achuste d'aliniación d'a fuent\"\nBasic.Settings.General.ScreenSnapping=\"Achustar las fuents a lo canto d'a pantalla\"\nBasic.Settings.General.CenterSnapping=\"Achustar las fuents a lo centro horizontal y vertical\"\nBasic.Settings.General.SourceSnapping=\"Achustar las fuents a atras fuents\"\nBasic.Settings.General.SnapDistance=\"Achustar la sensibilidat\"\nBasic.Settings.General.RecordWhenStreaming=\"Gravar automaticament cuan se transmite\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Mantener la gravación cuan s'atura la transmisión\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Iniciar automaticament la reprodución d'a memoria intermedia mientres la transmisión\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Mantener activa la memoria intermedia de reproducción cuan s'ature la transmisión\"\nBasic.Settings.General.SysTray=\"Servilla d'o sistema\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimizar la servilla d'o sistema en iniciar\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Minimizar siempre en a servilla d'o sistema en cuenta d'en a barra de fayenas\"\nBasic.Settings.General.SaveProjectors=\"Alzar los prochectos en salir\"\nBasic.Settings.General.Preview=\"Vista previa\"\nBasic.Settings.General.OverflowHidden=\"Amagar lo sobreiximiento\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Sobreiximiento siempre visible\"\nBasic.Settings.General.OverflowSelectionHidden=\"Amostrar lo sobreiximiento mesmo cuan la fuent ye invisible\"\nBasic.Settings.General.Importers=\"Importadors\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Mirar ubicacions conoixidas de coleccions de scenas en importar\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Transición ta la scena en fer doble clic\"\nBasic.Settings.General.StudioPortraitLayout=\"Activar la disposición horizontal/vertical\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Amostrar las etiquetas de vista previa/programa\"\nBasic.Settings.General.Multiview=\"Vista multiple\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Fe clic pa cambiar entre scenas\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Amostrar lo nombre de la scena\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Debuixar las zonas seguras (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Disposición de vista multiple\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontal, Alto (8 scenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontal, Abaixo (8 Scenas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertical, Zurda (8 Scenas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertial, Dreita (8 Scenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontal, Alto (18 Scenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontal, Alto (24 Scenas)\"\nBasic.Settings.Stream=\"Emisión\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Usar l'autentificación\"\nBasic.Settings.Stream.Custom.Username=\"Nombre d'usuario\"\nBasic.Settings.Stream.Custom.Password=\"Clau\"\nBasic.Settings.Stream.BandwidthTestMode=\"Activar modo de preba d'amplo de banda\"\nBasic.Settings.Stream.TTVAddon=\"Complementos d'o chat de Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Garra\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV y FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Falta configuración de stream\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Ubrir Achustes\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Falta la URL y clau de retransmisión.\\n\\nAbre los achustes pa introducir la URL y la clau de retransmisión en a pestanya 'Emisión'.\"\nBasic.Settings.Stream.MissingUrl=\"Falta la URL d'o stream.\\n\\nAbre la configuración pa introducir la URL en a pestanya 'Emisión'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Falta la clau de retransmisión.\\n\\nAbre los achustes pa introducir la clau de retransmisión en a pestanya 'Emisión'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorar las recomendacions de configuración d'o servicio de streaming\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Anular los achustes recomendaus\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Alvertencia: Ignorar las limitacions d'o servicio puede provocar una degradación en a calidat d'o stream u impedir que s'emita.\\n\\nContinar?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maxima taxa de bits de video: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maxima taxa de bits d'audio: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Resolución maxima: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Taxa maxima de fotogramas: %1\"\nBasic.Settings.Output=\"Salida\"\nBasic.Settings.Output.Format=\"Formato de gravación\"\nBasic.Settings.Output.SelectDirectory=\"Tríe Directorio de gravación\"\nBasic.Settings.Output.DynamicBitrate=\"Cambia dinamicament la taxa de bits pa chestionar la conchestión\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Cambia dinamicament la taxa de bits pa chestionar la conchestión (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"En cuenta de blincar fotogramas pa reducir la conchestión, cambia dinamicament lo bitrate sobre la marcha.\\n\\nTen en cuenta que esto puede aumentar lo retardo d'a transmisión si i hai una conchestión repentina significativa.\\nCuando lo bitrate baixa, puede tardar uns menutos en restaurar-se.\\n\\nActualmente nomás ye compatible con RTMP.\"\nBasic.Settings.Output.Mode=\"Modo de salida\"\nBasic.Settings.Output.Mode.Simple=\"Sencillo\"\nBasic.Settings.Output.Mode.Adv=\"Abanzau\"\nBasic.Settings.Output.Mode.FFmpeg=\"Salida de FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Activar la reproducción d'a memoria intermedia\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Tiempo Maximo pa Repeticions\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Uso estimau de memoria: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"No se puede estimar l'uso de memoria. Estableixca lo limite maximo de memoria.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefixo d'o nombre de fichero d'a memoria intermedia\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufixo\"\nBasic.Settings.Output.Simple.SavePath=\"Rota de gravación\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Calidat de gravación\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Igual a la emisión\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Alta calidat, grandaria de fichero meyo\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Grandaria de fichero gran, calidat indistinguible\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Grandaria d'o fichero sin perda de calidat, tremendament gran\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Alvertencia: La taxa de bits de video d'o stream s'establirá en %1, que ye lo limite superior pa lo servicio de streaming actual.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Alvertencia: La taxa de bits d'audio d'o stream s'establirá en %1, que ye lo limite superior pa lo servicio de streaming actual.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Alvertencia: Las gravacions no se pueden pausar si la calidat de gravación s'estableix en \\\"Igual a lo stream\\\".\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"ALVERTENCIA: Gravar con un codificador de software d'una calidat diferent a la d'a transmisión requerirá un uso adicional d'a CPU si transmite y grava a lo mesmo tiempo.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"PARA CUENTA: La calidat sin perdidas chenera grandarias de fichero muito grans! La calidat sin perdas puede utilizar mas de 7 chigabytes d'espacio en disco per menuto en alta resolución y con alta taxa de fotogramas. La calidat sin perdas no se recomienda pa gravacions largas, de no estar que tienga una gran cantidat d'espacio en disco disponible. Tornar a reproducir lo búfer no ye disponible cuan s'emplega la calidat sin perdidas.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Confirma que deseya utilizar calidat sin perdidas?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Atención de calidat sin perdas!\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264 baixo uso de CPU, aumenta la grandaria de fichero)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Pista VOD de Twitch (utiliza la pista 2)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Resolución u taxa de fotogramas incompatible\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Este servicio de streaming no admite la suya resolución y/u taxa de fotogramas actual. Se cambiarán a las valors compatibles mas cercanos\\n\\n%1\\n\\nDeseya continar?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolución: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Taxa de fotogramas: %1\"\nBasic.Settings.Output.VideoBitrate=\"Bitrate de video\"\nBasic.Settings.Output.AudioBitrate=\"Bitrate d'audio\"\nBasic.Settings.Output.Reconnect=\"Reconnectar automaticament\"\nBasic.Settings.Output.RetryDelay=\"Retardo pa reintentar\"\nBasic.Settings.Output.MaxRetries=\"Reintentos maximos\"\nBasic.Settings.Output.EncoderPreset=\"Preajuste d'o codificador\"\nBasic.Settings.Output.CustomEncoderSettings=\"Configuración d'o codificador personalizada\"\nBasic.Settings.Output.CustomMuxerSettings=\"Configuración personalizada d'o conversor\"\nBasic.Settings.Output.NoSpaceFileName=\"Chenerar lo nombre d'o fichero sin espacio\"\nBasic.Settings.Output.Adv.Rescale=\"Cambiar la escala de salida\"\nBasic.Settings.Output.Adv.AudioTrack=\"Pista d'audio\"\nBasic.Settings.Output.Adv.Streaming=\"Emisión\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Pista 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Pista 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Pista 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Pista 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Pista 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Pista 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Pista VOD de Twitch\"\nBasic.Settings.Output.Adv.Recording=\"Gravación\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tipo de gravación\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tipo\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Estandard\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Salida personalizada (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Usar codificación de transmisión)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Nombre de fichero de formato\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Sobrescribir si lo fichero existe\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Tipo de salida de FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Salida a URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Salida a fichero\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Formatos comuns de gravación\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Toz los Fichers\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Rota de fichero u URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Formato d'o contenedor\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Formato predeterminau\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Descripción d'o formato contenedor\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Codec d'audio/video supuesto a partir d'a URL u rota de fichero\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Codificador per defecto\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Desactivar lo codificador\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Codificador de video\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Configuración de codificador de video (si existe)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Codificador d'audio\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Configuración de codificador d'audio (si existe)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Achustes d'o conversor (si en i hai)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Intervalo de fotogramas clau (en Fotogramas)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Amostrar toz los codecs (encara que sían potencialment incompatibles)\"\nScreenshot=\"Salida de captura de pantalla\"\nScreenshot.SourceHotkey=\"Captura de pantalla de fuent triada\"\nScreenshot.StudioProgram=\"Captura de pantalla (Programa)\"\nScreenshot.Preview=\"Captura de pantalla (Vista previa)\"\nScreenshot.Scene=\"Captura de pantalla (Scena)\"\nScreenshot.Source=\"Captura de pantalla (Fuent)\"\nBasic.Settings.Video.Adapter=\"Adaptador de video\"\nBasic.Settings.Video.BaseResolution=\"Resolución d'a base (Lienzo)\"\nBasic.Settings.Video.ScaledResolution=\"Resolución de salida (Escalada)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtro d'escala\"\nBasic.Settings.Video.FPSCommon=\"Valors comunas de FPS\"\nBasic.Settings.Video.FPSInteger=\"Valor entera de FPS\"\nBasic.Settings.Video.FPSFraction=\"Valor fraccional de FPS\"\nBasic.Settings.Video.Numerator=\"Numerador\"\nBasic.Settings.Video.Denominator=\"Denominador\"\nBasic.Settings.Video.Renderer=\"Renderizador\"\nBasic.Settings.Video.InvalidResolution=\"Valor d'a resolución no valido. Ha d'estar [amplo] x [altura] (per eixemplo, 1920 x 1080)\"\nBasic.Settings.Video.CurrentlyActive=\"La salida de video ye actualment activa. Per favor amorte cualsequier salida pa cambiar la configuración de video.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineal (mas rapido, pero borroso si s'escala la imachen)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicúbico (Escalau fino, 16 muestras)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Escalau fino, 36 muestras)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Aria (Suma ponderada, 4/6/9 muestras)\"\nBasic.Settings.Audio.SampleRate=\"Frecuencia de muestreyo\"\nBasic.Settings.Audio.Channels=\"Canales\"\nBasic.Settings.Audio.Meters=\"Mesuradors\"\nBasic.Settings.Audio.MeterDecayRate=\"Velocidat de decayimiento\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rapida\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Meya (PPM de tipo I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lenta (PPM de tipo II)\"\nBasic.Settings.Audio.PeakMeterType=\"Tipo de mesurador de pico\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Pico de muestra\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True Peak (mayor uso de CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ALVERTENCIA: l'audio de son envolvente ye habilitau.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Habilitar l'audio de son envolvente?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Seguro que quiere habilitar l'audio de son envolvente?\"\nBasic.Settings.Audio.Devices=\"Dispositivos d'audio globals\"\nBasic.Settings.Audio.DesktopDevice=\"Audio d'o escritorio\"\nBasic.Settings.Audio.DesktopDevice2=\"Audio d'o escritorio 2\"\nBasic.Settings.Audio.AuxDevice=\"Dispositivo d'audio Mic/auxiliar\"\nBasic.Settings.Audio.AuxDevice2=\"Dispositivo d'audio Mic/auxiliar 2\"\nBasic.Settings.Audio.AuxDevice3=\"Dispositivo d'audio Mic/auxiliar 3\"\nBasic.Settings.Audio.AuxDevice4=\"Dispositivo d'audio Mic/auxiliar 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Habilitar Pretar pa Silenciar\"\nBasic.Settings.Audio.PushToMuteDelay=\"Retardo de Presionar pa Silenciar\"\nBasic.Settings.Audio.EnablePushToTalk=\"Habilitar Pretar pa Charrar\"\nBasic.Settings.Audio.PushToTalkDelay=\"Retardo de Pretar pa Charrar\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Dispositivo no connectau u no ye disponible]\"\nBasic.Settings.Audio.Disabled=\"Deshabilitau\"\nBasic.Settings.Advanced=\"Abanzau\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prioridat d'o proceso\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Alta\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Mas que lo normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Menos que lo normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inactiva\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Formato de color\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 bit, 4:2:0, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 bit, 4:4:4, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 bit, 4:2:0, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 bit, 4:2:0, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Espacio de color\"\nBasic.Settings.Advanced.Video.ColorRange=\"Gama de colors\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limitau\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Completo\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Nivel de blanco SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nivel de pico nominal HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Dispositivo de monitorización d'audio\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Per defecto\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Desactivar reducción d'audio de Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Retardo d'a transmisión\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Duración\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Preservar lo punto de corte (aumento de retardo) en tornar a connectar\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Uso estimau de memoria: %1 MB\"\nBasic.Settings.Advanced.Network=\"Ret\"\nBasic.Settings.Advanced.Network.Disabled=\"Lo protocolo de transmisión actualment triau no permite de cambiar la configuración de ret.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Enlazar con IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Habilitar optimizacions de ret\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Habilitar lo control de ritmo TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Intenta fer que la salida RTMP sía mas amigable con atras aplicacions sensibles a la latencia en o ret regulando la taxa de transmisión.\\nPuede aumentar lo risgo de perdida de fotogramas en connexions inestables.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Comportamiento d'as teclas rapidas\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nunca desactivar las teclas rapidas\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Deshabilitar teclas d'acceso rapido cuan la finestra prencipal se trobe activa\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Deshabilitar teclas rapidas cuan la finestra prencipal no sía en primer plano\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(gravar como mkv)\"\nBasic.AdvAudio=\"Propiedaz d'Audio abanzadas\"\nBasic.AdvAudio.ActiveOnly=\"Nomás fuents activas\"\nBasic.AdvAudio.Name=\"Nombre\"\nBasic.AdvAudio.Volume=\"Volumen\"\nBasic.AdvAudio.VolumeSource=\"Volumen pa '%1'\"\nBasic.AdvAudio.MonoSource=\"Mescla monoaural pa '%1'\"\nBasic.AdvAudio.Balance=\"Balanz\"\nBasic.AdvAudio.BalanceSource=\"Balanz pa '%1'\"\nBasic.AdvAudio.SyncOffset=\"Intervalo de sincronización\"\nBasic.AdvAudio.SyncOffsetSource=\"Compensación de sincronismo pa '%1'\"\nBasic.AdvAudio.Monitoring=\"Monitorización d'audio\"\nBasic.AdvAudio.Monitoring.None=\"Monitorización desactivada\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Solo monitorización (silenciar la salida)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitorización y salida\"\nBasic.AdvAudio.MonitoringSource=\"Monitorización d'audio pa '%1'\"\nBasic.AdvAudio.AudioTracks=\"Pistas\"\nBasic.Settings.Hotkeys=\"Alcorces\"\nBasic.Settings.Hotkeys.Pair=\"Combinacions de teclas con '%1' actúan como commutadors\"\nBasic.Settings.Hotkeys.Filter=\"Filtro\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrar per alcorce\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Este alcorce s'ha asignau a una u mas accions. Fe clic pa veyer las errors\"\nBasic.Hotkeys.SelectScene=\"Cambiar a la scena\"\nBasic.SystemTray.Show=\"Amostrar\"\nBasic.SystemTray.Hide=\"Amagar\"\nBasic.SystemTray.Message.Reconnecting=\"Desconnectau. Tornando a connectar...\"\nHotkeys.Insert=\"Insertar\"\nHotkeys.Delete=\"Eliminar\"\nHotkeys.Home=\"Casa\"\nHotkeys.End=\"Final\"\nHotkeys.PageUp=\"AvPag\"\nHotkeys.PageDown=\"RePag\"\nHotkeys.NumLock=\"Bloq Num\"\nHotkeys.ScrollLock=\"Bloq. Scroll\"\nHotkeys.CapsLock=\"Bloq mayus\"\nHotkeys.Backspace=\"Retroceso\"\nHotkeys.Tab=\"Tabulador\"\nHotkeys.Print=\"Imprentar\"\nHotkeys.Pause=\"Pausa\"\nHotkeys.Left=\"Cucha\"\nHotkeys.Right=\"Dreita\"\nHotkeys.Up=\"Puyar\"\nHotkeys.Down=\"Baixar\"\nHotkeys.Windows=\"Tecla de Windows\"\nHotkeys.Menu=\"Menú\"\nHotkeys.Space=\"Espacio\"\nHotkeys.NumpadNum=\"%1 (teclau numerico)\"\nHotkeys.NumpadMultiply=\"Multiplicar (teclau numerico)\"\nHotkeys.NumpadDivide=\"Dividir (teclau numerico)\"\nHotkeys.NumpadAdd=\"Sumar (Teclau numerico)\"\nHotkeys.NumpadSubtract=\"Restar (teclau numerico)\"\nHotkeys.NumpadDecimal=\"Decimal (teclau numerico)\"\nHotkeys.AppleKeypadNum=\"%1 (teclau)\"\nHotkeys.AppleKeypadMultiply=\"* (Teclau)\"\nHotkeys.AppleKeypadDivide=\"/ (Teclau)\"\nHotkeys.AppleKeypadAdd=\"+ (Teclau)\"\nHotkeys.AppleKeypadSubtract=\"- (Teclau)\"\nHotkeys.AppleKeypadDecimal=\". (Teclau)\"\nHotkeys.AppleKeypadEqual=\"= (Teclau)\"\nHotkeys.MouseButton=\"Ratet %1\"\nMute=\"Silenciar\"\nUnmute=\"Sacar silencio\"\nPush-to-mute=\"Pretar pa silenciar\"\nPush-to-talk=\"Pretar pa charrar\"\nSceneItemShow=\"Amostrar '%1'\"\nSceneItemHide=\"Amagar '%1'\"\nOutputWarnings.NoTracksSelected=\"Ha de triar a lo menos una pista\"\nOutputWarnings.MP4Recording=\"Alvertencia: Las gravacions alzadas en MP4/MOV serán irrecuperables si lo fichero no puede finalizar-se (y.g. como resultau de BSODs, cortes electricos, etc.). Si quiers gravar multiples pistas d'audio considera usar MKV y convierte la gravación a MP4/MOV dimpués de finalizar (Fichero → Convertir Gravacions)\"\nOutputWarnings.CannotPause=\"Alvertencia: Las gravacions no se pueden pausar si lo codificador de gravación s'estableix en \\\"(Usar codificador de transmisión)\\\"\"\nCodecCompat.CodecPlaceholder=\"Seleccionar lo codificador...\"\nCodecCompat.ContainerPlaceholder=\"Seleccionar lo formato...\"\nCodecCompat.CodecMissingOnExit.Title=\"Garra codificador seleccionau\"\nFinalScene.Title=\"Eliminar scena\"\nFinalScene.Text=\"Cal que i haiga a lo menos una scena.\"\nNoSources.Title=\"Sin recursos\"\nNoSources.Text=\"Pareixe que no has anyadiu garra recurso de video encara, asinas que serás emitindo una pantalla en blanco. Yes seguro que quiers fer esto?\"\nNoSources.Text.AddSource=\"Puez anyadir recursos fendo clic en l'icono de \\\"+\\\" debaixo d'o menú Recursos en a finestra prencipal en cualsequier momento.\"\nNoSources.Label=\"No tiens garra fuent.\\nHaz clic en o botón +,\\no fe clic dreito aquí pa anyadir-ne una.\"\nChangeBG=\"Establir color\"\nCustomColor=\"Color personalizada\"\nBrowserSource.EnableHardwareAcceleration=\"Habilitar acceleración per hardware en o navegador\"\nAbout=\"Sobre\"\nAbout.Info=\"OBS Studio ye un software libre y gratuito de gravación de video y transmisión en directo.\"\nAbout.Donate=\"Fe una contribución\"\nAbout.GetInvolved=\"Dentra Agora\"\nAbout.Authors=\"Autors\"\nAbout.License=\"Licencia\"\nAbout.Contribute=\"Empara lo prochecto OBS\"\nAddUrl.Title=\"Anyadir fuent vía URL\"\nAddUrl.Text=\"Has arrocegau una URL en OBS. Esto anyadirá automaticament lo vinclo como fuent. Continar?\"\nPreviewTransition=\"Vista previa d'a Transición\"\nImporter=\"Importador de Colección de Scenas\"\nImporter.SelectCollection=\"Tría una colección de scenas\"\nImporter.Collection=\"Colección de scenas\"\nImporter.HelpText=\"Anyadir fichers a esta finestra pa importar coleccions de OBS u atros programas compatibles.\"\nImporter.Path=\"Rota d'a colección\"\nImporter.Program=\"Aplicación detectada\"\nImporter.AutomaticCollectionPrompt=\"Buscar automaticament coleccions de scenas\"\nImporter.AutomaticCollectionText=\"OBS puede trobar automaticament coleccions de scenas importables de programas de tercers suportaus. Quiers que OBS trobe automaticament coleccions de scenas?\\n\\nPuedes cambiar esto mas tarde en Achustes > Cheneral > Importadors.\"\nRestart=\"Reiniciar\"\nNeedsRestart=\"S'ha de reiniciar OBS Studio. Deseyas reiniciar-lo agora?\"\nContextBar.NoSelectedSource=\"Garra fuent triada\"\nContextBar.MediaControls.PlayMedia=\"Reproducir lo conteniu\"\nContextBar.MediaControls.PauseMedia=\"Pausar lo conteniu\"\nContextBar.MediaControls.StopMedia=\"Aturar lo conteniu\"\nContextBar.MediaControls.RestartMedia=\"Reiniciar lo conteniu\"\nContextBar.MediaControls.PlaylistNext=\"Siguient en a lista de reproducción\"\nContextBar.MediaControls.PlaylistPrevious=\"Anterior en a lista de reproducción\"\nContextBar.MediaControls.BlindSeek=\"Widget Barra de reproducción\"\nYouTube.Auth.Ok=\"S'ha autorizau correctament.\\nYa puez zarrar esta pachina.\"\nYouTube.Auth.NoCode=\"Lo proceso d'autorización no s'ha rematau.\"\nYouTube.Auth.NoChannels=\"No i hai garra canal disponible en a cuenta triada\"\nYouTube.Auth.WaitingAuth.Title=\"Autorización d'usuario de Youtube\"\nYouTube.Auth.WaitingAuth.Text=\"Remata lo proceso d'autorización en o tuyo navegador externo.<br>Si lo navegador no s'ubre, sigue este vinclo y finaliza l'autorización:<br>%1\"\nYouTube.AuthError.Text=\"Error en obtener la información d'a canal: %1.\"\nYouTube.Actions.Title=\"Titol*\"\nYouTube.Actions.MyBroadcast=\"La mía emisión\"\nYouTube.Actions.Description=\"Descripción\"\nYouTube.Actions.Privacy=\"Privacidat*\"\nYouTube.Actions.Privacy.Private=\"Privau\"\nYouTube.Actions.Privacy.Public=\"Publico\"\nYouTube.Actions.Privacy.Unlisted=\"Amagau\"\nYouTube.Actions.Category=\"Categoría\"\nYouTube.Actions.MadeForKids=\"Este video ye feito pa zagals?*\"\nYouTube.Actions.MadeForKids.Yes=\"Sí, ye feito pa zagals\"\nYouTube.Actions.MadeForKids.No=\"No, no ye feito pa zagals\"\nYouTube.Actions.Latency=\"Latencia\"\nYouTube.Actions.Latency.Low=\"Baixa\"\nYouTube.Actions.Latency.UltraLow=\"Ultra baixa\"\nYouTube.Actions.EnableAutoStart=\"Activar l'inicio automatico\"\nYouTube.Actions.EnableAutoStop=\"Activar l'aturada automatica\"\nYouTube.Actions.AutoStartStop.TT=\"Indica si este emisión programada s'ha d'iniciar automaticament\"\nYouTube.Actions.EnableDVR=\"Activar DVR\"\nYouTube.Actions.360Video=\"Video 360\"\nYouTube.Actions.ScheduleForLater=\"Programar pa dimpuesas\"\nYouTube.Actions.Error.Title=\"Error en crear la transmisión\"\nYouTube.Actions.Error.Text=\"Error d'acceso a YouTube '%1'.<br/>Se puet trobar una descripción mas detallada en <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Error d'acceso a YouTube. Verifica la connexión u lo servidor d'acceso a YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Error de creación de retransmisión '%1'.<br/>Se puet trobar una descripción mas detallada en <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"No s'ha creau garra emisión. Torna a vincular la cuenta.\"\nYouTube.Actions.Error.YouTubeApi=\"Error de l'API de YouTube. Consulta lo fichero de rechistro pa obtener-ne mas información.\"\nYouTube.Actions.Error.BroadcastNotFound=\"No s'ha trobau la emisión triada.\"\nYouTube.Actions.EventsLoading=\"Se ye cargando la lista d'eventos...\"\nYouTube.Actions.EventCreated.Title=\"S'ha creau lo esdevenimiento\"\nYouTube.Actions.EventCreated.Text=\"S'ha creau correctament l'esdevenimiento.\"\nYouTube.Actions.Stream=\"Retransmisión\"\nYouTube.Actions.Stream.ScheduledFor=\"Programau pa %1\"\nYouTube.Actions.Stream.Resume=\"Reanudar la transmisión interrompida\"\nYouTube.Actions.Stream.YTStudio=\"Creada automaticament per YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Se ye creando una nueva retransmisión, aguarda un momento...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Cal iniciar manualment\"\nYouTube.Actions.AutoStartStreamingWarning=\"L'inicio automatico ye desactivau pa este evento. Fe clic en \\\"Emitir en directo\\\" pa iniciar la transmisión.\"\nYouTube.Actions.AutoStopStreamingWarning=\"No podrás tornar a connectar.<br>La tuya emisión s'aturará y deixarás d'estar en directo.\"\nYouTube.Errors.liveStreamingNotEnabled=\"La transmisión en directo no ye activada en a canal de YouTube triada. <br/><br/>Consulta <a href='https://www.youtube.com/features'>youtube.com/features</a> pa tener-ne mas información.\"\nYouTube.Errors.livePermissionBlocked=\"La reproducción en directe no ye disponible en a canal de YouTube seleccionada.<br/>Tiene en cuenta que puede pasar dica 24 horas pa que la transmisión en directo sía disponible dimpués d'haber-la activada en a configuración d'a canal. <br/><br/>Consulta <a href='https://www.youtube.com/features'>youtube.com/features</a> pa tener-ne mas información.\"\n"
  },
  {
    "path": "frontend/data/locale/ar-SA.ini",
    "content": "Language=\"العربية\"\nOK=\"موافق\"\nApply=\"تطبيق\"\nCancel=\"إلغاء\"\nClose=\"إغلاق\"\nSave=\"حفظ\"\nDiscard=\"تجاهل\"\nDisable=\"تعطيل\"\nYes=\"نعم\"\nNo=\"لا\"\nAdd=\"أضف\"\nRemove=\"إحذف\"\nRename=\"إعادة تسمية\"\nInteract=\"تفاعل\"\nFilters=\"المرشّحات\"\nProperties=\"خصائص\"\nMoveUp=\"تحريك للاعلى\"\nMoveDown=\"تحريك للاسفل\"\nSettings=\"إعدادات\"\nDisplay=\"شاشة العرض\"\nName=\"الاسم\"\nExit=\"خروج\"\nMixer=\"خالط الصوتيات\"\nBrowse=\"استعراض\"\nMono=\"أُحادي\"\nStereo=\"ستيريو\"\nDroppedFrames=\"إسقاط المشاهد %1 (% %2)\"\nProjector.Open.Program=\"افتح جهاز عرض البرامج\"\nProjector.Open.Preview=\"افتح جهاز عرض المعاينة\"\nProjector.Open.Scene=\"افتح جهاز عرض المشهد\"\nProjector.Open.Source=\"افتح جهاز عرض المصدر\"\nProjector.Open.Multiview=\"افتح العرض المتعدد\"\nProjector.Display=\"عرض: %1\"\nProjector.Window=\"نافذة جديدة\"\nProjector.Title=\"جهاز العرض\"\nProjector.Title.Scene=\"المشهد: %1\"\nProjector.Title.Source=\"المصدر: %1\"\nProjector.Title.Multiview=\"عرض متعدد\"\nProjector.ResizeWindowToContent=\"احتواء النافذة على المحتوى\"\nClear=\"مسح\"\nRevert=\"استعادة\"\nShow=\"إظهار\"\nHide=\"إخفاء\"\nUnhideAll=\"إظهار الكل\"\nUntitled=\"بدون عنوان\"\nNew=\"جديد\"\nDuplicate=\"تكرار مزدوج\"\nEnable=\"تفعيل\"\nDisableOSXVSync=\"تعطيل خاصية macOS V-Sync\"\nResetOSXVSyncOnExit=\"اعادة تعيين خاصية macOS V-sync عند الخروج\"\nHighResourceUsage=\"زيادة الحِمل في عمليه ترميز الفيديو! خذ بعين الاعتبار تخفيض اعدادات الفيديو, او استخدم اعدادات ترميز اسرع.\"\nTransition=\"تبديل المشهد\"\nQuickTransitions=\"تبديل سريع\"\nFadeToBlack=\"تلاشى إلى الأسود\"\nLeft=\"يسار\"\nRight=\"يمين\"\nTop=\"أعلى\"\nBottom=\"أسفل\"\nReset=\"إعادة تعيين\"\nHours=\"ساعات\"\nMinutes=\"دقائق\"\nSeconds=\"ثواني\"\nDeprecated=\"مهمل\"\nReplayBuffer=\"إعادة تشغيل المخزن المؤقت\"\nImport=\"استيراد\"\nExport=\"تصدير\"\nCopy=\"نسخ\"\nPaste=\"لصق\"\nPasteReference=\"لصق (مرجع)\"\nPasteDuplicate=\"لصق (مكررة)\"\nRemuxRecordings=\"تسجيلات Remux\"\nNext=\"التالي\"\nBack=\"السابق\"\nDefaults=\"الافتراضي\"\nRestoreDefaults=\"الافتراضي\"\nHideMixer=\"إخفاء في الخالط\"\nTransitionOverride=\"فرض تأثير انتقالي\"\nShowTransition=\"إظهار الانتقال\"\nHideTransition=\"إخفاء الانتقال\"\nNone=\"لا شيء\"\nStudioMode.Preview=\"معاينة\"\nStudioMode.Program=\"البث\"\nStudioMode.PreviewSceneName=\"معاينة: %1\"\nStudioMode.ProgramSceneName=\"برنامج: %1\"\nShowInMultiview=\"إظهار في العرض المتعدد\"\nVerticalLayout=\"تنسيق عمودي\"\nGroup=\"مجموعة\"\nDoNotShowAgain=\"عدم الإظهار مرة أخرى\"\nDefault=\"(الافتراضي)\"\nCalculating=\"جاري حساب الوقت...\"\nFullscreen=\"شاشة كاملة\"\nWindowed=\"نافذة\"\nRefreshBrowser=\"تحديث\"\nAspectRatio=\"نسبة العرض إلى الارتفاع <b>%1:%2</b>\"\nLockVolume=\"قفل الصوت\"\nLogViewer=\"مشاهدة السجل\"\nShowOnStartup=\"إظهار عند بدء التشغيل\"\nOpenFile=\"فتح ملف\"\nAddScene=\"أضف المشهد\"\nAddSource=\"أضف مصدراً\"\nRemoveScene=\"أزِل المشهد المحدد\"\nRemoveSource=\"أزِل المورد/الموارد المحددة\"\nMoveSceneUp=\"حرك المشهد للأعلى\"\nMoveSceneDown=\"حرك المشهد للأسفل\"\nMoveSourceUp=\"حرك المصدر(المصادر) للأعلى\"\nMoveSourceDown=\"حرك المصدر(المصادر) للأسفل\"\nSourceProperties=\"الخصائص مفتوحة المصدر\"\nSourceFilters=\"فلاتر مفتوحة المصدر\"\nMixerToolbarMenu=\"قائمة مزج الصوت\"\nSceneFilters=\"افتح فلاتر المشهد\"\nList=\"قائمة\"\nGrid=\"شبكة\"\nAutomatic=\"تلقائي\"\nComingSoon=\"قريباً\"\nPluginsFailedToLoad.Title=\"خطأ في تحميل الإضافات\"\nPluginsFailedToLoad.Text=\"فشل تحميل إضافات OBS التالية:\\n\\n%1\\nالرجاء تحديث أو إزالة هذه الإضافات.\"\nAlreadyRunning.Title=\"OBS قيد التشغيل بالفعل\"\nAlreadyRunning.Text=\"OBS قيد التشغيل بالفعل! إلاّ إذا كنت تتعمد هذا, الرجاء إغلاق أي نسخة أخرى قيد التشغيل من OBS قبل محاولة تشغيل واحدة جديدة. إذا كان OBS لديك في وضعية التصغير إلى جانب الساعة, الرجاء التأكد إذا كان لازال يعمل هناك.\"\nAlreadyRunning.LaunchAnyway=\"إطلاق على أي حال\"\nCrashHandling.Dialog.Title=\"تم الكشف عن تعطل في OBS Studio\"\nCrashHandling.Labels.Text=\"لم يتم إغلاق OBS بطريقة صحيحة.\\n\\nقم بالتشغيل في الوضع الأمن؟\"\nCrashHandling.Labels.PrivacyNotice=\"يمكنك أيضًا اختيار تحميل أحدث تقرير عن الأعطال تلقائيًا إلى OBSProject.<br /><br />يرجى قراءة <a href=’https://obsproject.com/privacy-policy‘>سياسة الخصوصية</a> قبل رفع أي ملفات، مع الإهتمام خاصاً بالأجزاء المتعلقة بتحميل الملفات.\"\nCrashHandling.Checkbox.SendReport=\"لقد قرأت سياسة الخصوصية وأوافق على الرفع.\"\nCrashHandling.Buttons.LaunchSafe=\"تشغيل في الوضع الأمن\"\nCrashHandling.Buttons.LaunchNormal=\"تشغيل في الوضع العادي\"\nCrashHandling.Errors.UploadJSONError=\"لقد حدث حطأ اثناء محاولة رفع احدث سجل للأعطال. يرجي المحاولة لاحقاً.\"\nCrashHandling.Errors.Title=\"خطأ في رفع سجل الأعطال\"\nSafeMode.Restart=\"هل تريد إعادة تشغيل OBS في الوضع الآمن (سيتم تعطيل المكونات الإضافية والبرمجة النصية و WebSockets)؟\"\nSafeMode.RestartNormal=\"هل ترغب في إعادة تشغيل OBS في الوضع العادي؟\"\nChromeOS.Title=\"منصة غير مدعومة\"\nChromeOS.Text=\"يبدو أن OBS يعمل داخل حاوية ChromeOS. هذه المنصة غير مدعومة.\"\nWine.Title=\"تم اكتشاف النبيذ\"\nWine.Text=\"تشغيل OBS في النبيذ غير مدعوم، والعديد من الميزات مثل مصادر التقاط الجهاز لن تعمل أو لن تعمل إلا بقدرة محدودة.<br><br>من المستحسن تشغيل نسخة أصلية من OBS بدلاً من ذلك، على سبيل المثال <a href='https://flathub.org/apps/details/com.obsproject.Studio'>إصدار Flatpak</a> أو حزم نظام التشغيل الخاص بك.\"\nDockCloseWarning.Title=\"جارِ إغلاق نافذة قابل للإرساء\"\nDockCloseWarning.Text=\"لقد أغلقت للتو نافذة قابلة للإرسال. إذا كنت ترغب في إظهارها مرة أخرى، استخدم قائمة الأقفال على شريط القائمة.\"\nExtraBrowsers=\"أرصفة المتصفح المخصصة\"\nExtraBrowsers.Info=\"قم بإضافة الأرصفة بإعطائها اسم و رابط, ثم اضغط على تطبيق أو إغلاق لفتح الأرصفة. يمكنك إضافة أو حذف الأرصفة في أي وقت.\"\nExtraBrowsers.DockName=\"اسم الإرساء\"\nAuth.Authing.Title=\"جاري المصادقة...\"\nAuth.Authing.Text=\"جارٍ المصادقة مع %1, الرجاء الانتظار...\"\nAuth.AuthFailure.Title=\"فشل المصادقة\"\nAuth.AuthFailure.Text=\"فشل في المصادقة مع %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"مطلوب المصادقة\"\nAuth.InvalidScope.Text=\"متطلبات المصادقة ل %1 تغيرت. قد لا تعمل بعض الميزات.\"\nAuth.LoadingChannel.Title=\"جارٍ تحميل معلومات القناة...\"\nAuth.LoadingChannel.Text=\"جارٍ تحميل معلومات القناة من%1, الرجاء الانتظار...\"\nAuth.LoadingChannel.Error=\"تعذر الحصول على معلومات القناة.\"\nAuth.ChannelFailure.Title=\"فشل تحميل معلومات القناة\"\nAuth.ChannelFailure.Text=\"فشل في تحميل معلومات القناة %1\\n\\n%2: %3\"\nAuth.Chat=\"الدردشة\"\nAuth.StreamInfo=\"معلومات البث\"\nTwitchAuth.Stats=\"إحصائيات Twitch\"\nTwitchAuth.Feed=\"نشاط Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"لم نتمكن من الاستعلام عن مفتاح البث\"\nTwitchAuth.TwoFactorFail.Text=\"OBS غير قادر على الربط بحسابك التويتش. الرجاء التأكد من اعداد المصادقة الثنائية في <a href='https://www.twitch.tv/settings/security'>إعدادات أمان تويتش</a> لأنه أحد المتطلبات للتمكن من البث.\"\nRestreamAuth.Channels=\"قنوات Restream\"\nCopy.Filters=\"نسخ الفلتر\"\nPaste.Filters=\"لصق الفلتر\"\nBrowserPanelInit.Title=\"جارٍ بدء المستعرض...\"\nBrowserPanelInit.Text=\"جارٍ بدء المستعرض, الرجاء الانتظار...\"\nBandwidthTest.Region=\"منطقة\"\nBandwidthTest.Region.US=\"الولايات المتحدة\"\nBandwidthTest.Region.EU=\"أوروبا\"\nBandwidthTest.Region.Asia=\"آسيا\"\nBandwidthTest.Region.Other=\"\\\"غير ذلك\\\"\"\nBasic.AutoConfig=\"معالج التكوين التلقائي\"\nBasic.AutoConfig.ApplySettings=\"تطبيق الإعدادات\"\nBasic.AutoConfig.StartPage=\"معلومات الاستخدام\"\nBasic.AutoConfig.StartPage.SubTitle=\"حدد ما تريد استخدامه للبرنامج\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"تحسين لبث افيديو، والتسجيل ثانياً\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"تحسين فقط للتسجيل، وأنا لن أبث الفيديو\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"سأقوم فقط باستخدام الكاميرا الافتراضية\"\nBasic.AutoConfig.VideoPage=\"إعدادات الفيديو\"\nBasic.AutoConfig.VideoPage.SubTitle=\"حدد إعدادات الفيديو التي ترغب في استخدامها\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"الاستخدام الحالي (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"العرض %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"الاستخدام الحالي (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"إما 60 أو 30، ولكن يفضل 60 عندما يكون ذلك ممكنا\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"إما 60 أو 30، ولكن يفضل الدقة العالية\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"ملاحظة: قرار قاعدة (قاعدة) ليس بالضرورة يكون بنفس دقة البث أو التسجيل. قد يتم تصغير حجم تدفق البث/ دقة التسجيل أقل من دقة القاعدة لتقليل استخدام الموارد أو متطلبات معدل البت.\"\nBasic.AutoConfig.StreamPage=\"معلومات البث\"\nBasic.AutoConfig.StreamPage.SubTitle=\"من فضلك ادخل معلومات الخادم أو السيرفر\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"ربط الحساب (مستحسن)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"إلغاء ربط الحساب\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"إلغاء ربط الحساب؟\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"هذا التغيير سيتم تطبيقه مباشرة. هل أنت متأكد من أنك تريد إلغاء ربط حسابك؟\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"جلب مفتاح البث\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"مزيد من المعلومات\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"استخدم مفتاح البث\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"استخدام مفتاح البث (متقدم)\"\nBasic.AutoConfig.StreamPage.Service=\"خدمة\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"عرض الكل...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"مخصص...\"\nBasic.AutoConfig.StreamPage.Server=\"الخادم\"\nBasic.AutoConfig.StreamPage.StreamKey=\"مفتاح البث\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: أدخل كلمة مرور التشفير.\\nRTMP: أدخل المفتاح الذي توفره الخدمة.\\nSRT: أدخل التبسيط إذا كانت الخدمة تستخدم واحدة.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"مفتاح الترميز\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"الحساب المتصل\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"تقدير معدل التدفق باستخدام فحص سرعة الانترنت (ربما يحتاج لبعض الوقت)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"تفضيل استخدام الترميز بواسطة الهاردوير\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"استخدام الترميز بالهاردوير يلغي الحاجة الى معظم موارد المعالج, لكن قد يحتاج الى معدل بث أعلى للحفاظ على نفس مستوى الجودة.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"تحذير يتعلق بالبث\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"اختبار مستوى التدفق على وشك بدأ عرض مباشر لبيانات من فيديو عشوائي بدون صوت للقناة الخاصة بك. إذا أمكنك، يوصى بإيقاف حفظ فيديو للعرض المباشر مؤقتا وتعيين خصوصية العرض المباشر ل خاص إلى أن يكتمل الاختبار. هل تريد المتابعة؟\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"اختبار%1\"\nBasic.AutoConfig.TestPage=\"النتيجة النهائية\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"يقوم البرنامج الآن بتنفيذ مجموعة من الإختبارات لتقدير الإعدادات الأكثر مثالية\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"انتهى الاختبار\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"إجراء اختبار النطاق الترددي، قد يستغرق هذا بضع دقائق...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"لم يتم العثور على مخرجات لبروتوكول هذه الخدمة\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"جاري الاتصال بـ: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"فشل الاتصال بأي سيرفر، رجاءا تحقق من اتصالك بالانترنت وأعد المحاولة.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"اختبار التدفق لـ: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"جاري فحص برمجية ترميز البث، قد تستغرق هذه العملية دقيقة...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"جاري فحص برمجية ترميز تسجيل الفيديو، قد تستغرق هذه العملية دقيقة...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"فشل بدء برمجية الترميز\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"جاري فحص دقة %1x%2 ب%3 اطار/ث...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"نوعية ترميز البث\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"ترميز تسجيل الفيديو\"\nBasic.AutoConfig.TestPage.Result.Header=\"قام البرنامج بتقدير ان هذه هي الإعدادات المثالية لك:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"لاستخدام هذه الإعدادات اضغط على \\\"تطبيق الإعدادات\\\". لإعادة القيام بالإعدادات و المحاولة مرة أخرى، اضغط للخلف. للقيام بعمل الإعدادات يدوياً بنفسك، اضغط إلغاء و افتح الإعدادات.\"\nBasic.AutoConfig.Info=\"سيحدد معالج التهيئة التلقائية أفضل الإعدادات بناءً على مواصفات الكمبيوتر وسرعة الإنترنت.\"\nBasic.AutoConfig.RunAnytime=\"يمكن تشغيل هذا في أي وقت بالانتقال إلى قائمة الأدوات.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"دِقَّة البث (المحجم)\"\nBasic.Stats=\"إحصائيات\"\nBasic.Stats.CPUUsage=\"استخدام المعالج\"\nBasic.Stats.HDDSpaceAvailable=\"مساحة القرص المتوفرة\"\nBasic.Stats.MemoryUsage=\"استخدام الذاكرة\"\nBasic.Stats.AverageTimeToRender=\"معدل الوقت لإنشاء الإطار الواحد\"\nBasic.Stats.SkippedFrames=\"إطارات تم تخطيها بسبب تأخر الترميز\"\nBasic.Stats.MissedFrames=\"إطارات فقدت بسبب تأخر الترميز\"\nBasic.Stats.Output.Stream=\"بث\"\nBasic.Stats.Output.Recording=\"التسجيل\"\nBasic.Stats.Status=\"الحالة\"\nBasic.Stats.Status.Recording=\"جارٍ التسجيل\"\nBasic.Stats.Status.Live=\"مباشر\"\nBasic.Stats.Status.Reconnecting=\"جاري إعادة الاتصال\"\nBasic.Stats.Status.Inactive=\"غير نشط\"\nBasic.Stats.Status.Active=\"مفعّل\"\nBasic.Stats.DroppedFrames=\"الإطارات المفقودة (بسبب الشبكة)\"\nBasic.Stats.MegabytesSent=\"إجمالي البيانات الصادرة\"\nBasic.Stats.Bitrate=\"معدل النقل\"\nBasic.Stats.DiskFullIn=\"سيمتلىء القرص خلال (تقريباً)\"\nBasic.Stats.DiskFullIn.Text=\"%1 ساعات، %2 دقائق\"\nBasic.Stats.ResetStats=\"إعادة تعيين الإحصاءات\"\nResetUIWarning.Title=\"هل أنت متأكد أنك تريد إعادة تعيين واجهة المستخدم ؟\"\nResetUIWarning.Text=\"إعادة تعيين واجهة المستخدم سيخفي المربعات الاضافية. سيتوجب عليك إظهارها من قائمة \\\"عرض\\\" إذا اردت ذلك.\\n\\nهل أنت متأكد انك تريد إعادة تعيين واجهة المستخدم ؟\"\nUpdater.Title=\"تحديث جديد متوفر\"\nUpdater.Text=\"هناك إصدار جديد متوفر:\"\nUpdater.UpdateNow=\"حدث الآن\"\nUpdater.RemindMeLater=\"ذكرني لاحقاً\"\nUpdater.Skip=\"تخطي الإصدار\"\nUpdater.NoUpdatesAvailable.Title=\"لا توجد تحديثات متوفرة\"\nUpdater.NoUpdatesAvailable.Text=\"لا توجد تحديثات متوفرة حاليا\"\nUpdater.BranchNotFound.Title=\"تمت إزالة التحديث القناة\"\nUpdater.BranchNotFound.Text=\"لم تعد قناة التحديث المحددة متوفرة، تم إعادة تعيين OBS إلى الافتراضي.\"\nUpdater.RepairButUpdatesAvailable.Title=\"فحص السلامة غير متوفر\"\nUpdater.RepairButUpdatesAvailable.Text=\"يمكنك عمل فحص السلامة فقط على اخر أصدار متاح. استخدم مساعدة ← التحقق من وجود تحديث للتحقق من تثبيت OBS وتحديثه.\"\nUpdater.RepairConfirm.Title=\"تأكيد فحص السلامة\"\nUpdater.RepairConfirm.Text=\"بدأ فحص السلامة سوف يقوم بمسح تثبيت OBS الخاص بك للفساد وإعادة تحميل الملفات المعطلة أو المعدلة. قد يستغرق هذا لحظات.\\n\\n هل ترغب في المتابعة؟\"\nUpdater.FailedToLaunch=\"فشل في تشغيل التحديث\"\nQuickTransitions.SwapScenes=\"التبديل بين مشهدي المعاينة و الإخراج بعد عملية الانتقال\"\nQuickTransitions.SwapScenesTT=\"مقايضة المعاينة ومشاهد البرنامج بعد الانتقال (إذا كان المشهد الأصلي للبرنامج لا يزال موجوداً).\\nهذا لن يلغي أي تغييرات قد تكون أجريت على المشهد الأصلي للبرنامج.\"\nQuickTransitions.DuplicateScene=\"استنساخ المشهد\"\nQuickTransitions.DuplicateSceneTT=\"عند تحرير نفس المشهد، يسمح بتحرير تحول/رؤية المصادر دون تعديل ناتج البرنامج.\\nلتحرير خصائص المصادر دون تعديل إخراج البرنامج، قم بتفعيل 'تكرار المصادر'.\\nسيؤدي تغيير هذه القيمة إلى إعادة تعيين مشهد البرنامج الحالي (إذا كان لا يزال موجودا).\"\nQuickTransitions.EditProperties=\"استنساخ المصدر\"\nQuickTransitions.EditPropertiesTT=\"عند تحرير نفس المشهد، يسمح بتعديل خصائص المصادر دون تعديل ناتج البرنامج.\\nلا يمكن استخدام هذا إلا إذا تم تمكين 'تكرار المشهد'.\\nبعض المصادر (مثل مصادر التقاط أو وسائط الإعلام) لا تدعم ذلك ولا يمكن تحريرها بشكل منفصل.\\nسيؤدي تغيير هذه القيمة إلى إعادة تعيين مشهد البرنامج الحالي (إذا كان لا يزال موجوداً).\\n\\nتحذير: لأن المصادر سيتم تكرارها، وهذا قد يتطلب المزيد من النظام أو موارد الفيديو.\"\nQuickTransitions.HotkeyName=\"الانتقال السريع: %1\"\nBasic.AddTransition=\"إضافة المراحل الانتقالية للتكوين\"\nBasic.RemoveTransition=\"إزالة التأثير القابل للتكوين\"\nBasic.TransitionProperties=\"خصائص تأثير الإنتقال\"\nBasic.SceneTransitions=\"تأثير انتقال المشهد\"\nBasic.TransitionDuration=\"مدة الانتقال\"\nBasic.TogglePreviewProgramMode=\"طور الاستوديو\"\nBasic.EnablePreviewProgramMode=\"تشغيل نمط الأستوديو\"\nBasic.DisablePreviewProgramMode=\"إيقاف نمط الأستوديو\"\nUndo.Undo=\"تراجع\"\nUndo.Redo=\"إعادة\"\nUndo.Add=\"اضف %1'\"\nUndo.Delete=\"حذف '%1'\"\nUndo.Rename=\"إعادة تسمية '%1'\"\nUndo.SceneCollection.Switch=\"التبديل إلى '%1'\"\nUndo.Item.Undo=\"تراجع %1\"\nUndo.Item.Redo=\"إعادة %1\"\nUndo.Sources.Multi=\"حذف %1 من المصادر\"\nUndo.Filters=\"تغییرات الفلتر فی '%1'\"\nUndo.Filters.Paste.Single=\"لصق الفلتر '%1' إلى '%2'\"\nUndo.Filters.Paste.Multiple=\"نسخ الفلاتر من '%1' إلى '%2'\"\nUndo.Transform=\"تحويل المصدر (المصادر) في '%1'\"\nUndo.Transform.Paste=\"لصق التحول في '%1'\"\nUndo.Transform.Rotate=\"تدوير في '%1'\"\nUndo.Transform.Reset=\"إعادة تعيين التحويلات في '%1'\"\nUndo.Transform.HFlip=\"قلب أفقي في '%1'\"\nUndo.Transform.VFlip=\"قلب عمودي في '%1'\"\nUndo.Transform.FitToScreen=\"مناسبة للشاشة في '%1'\"\nUndo.Transform.StretchToScreen=\"مد لمناسبة الشاشة في '%1'\"\nUndo.Transform.Center=\"مَركِز في الشاشة في '%1'\"\nUndo.Transform.VCenter=\"مَركِز في الشاشة عموديًا في '%1'\"\nUndo.Transform.HCenter=\"مَركِز في الشاشة أفقيًا في '%1'\"\nUndo.Volume.Change=\"تغيير مستوي الصوت فی '%1'\"\nUndo.Volume.Mute=\"كتم صوت '%1'\"\nUndo.Volume.Unmute=\"إلغاء كتم صوت '%1'\"\nUndo.Balance.Change=\"تغيير موازنة الصوت لـ '%1'\"\nUndo.SyncOffset.Change=\"تغيير إزاحة مزامنة الصوت لـ '%1'\"\nUndo.MonitoringType.Change=\"تغيير مراقبة الصوت لـ '%1'\"\nUndo.Mixers.Change=\"تغيير خالط الصوت لـ '%1'\"\nUndo.ForceMono.On=\"تفعيل الصوت الأحادي إجبارياً لـ '%1'\"\nUndo.ForceMono.Off=\"تعطيل الصوت الأحادي إجبارياً لـ '%1'\"\nUndo.Properties=\"تغيير الخاصية في '%1'\"\nUndo.Scene.Duplicate=\"استنساخ المشهد '%1'\"\nUndo.ShowTransition=\"إظهار تأثير الانتقال على '%1'\"\nUndo.HideTransition=\"إخفاء تأثير الانتقال على '%1'\"\nUndo.ShowSceneItem=\"إظهار ''%1' في '%2'\"\nUndo.HideSceneItem=\"إخفاء ''%1' في '%2'\"\nUndo.ReorderSources=\"إعادة ترتيب المصادر في '%1'\"\nUndo.MoveUp=\"نقل '%1' للأعلى في '%2'\"\nUndo.MoveDown=\"نقل '%1' للأسفل في '%2'\"\nUndo.MoveToTop=\"نقل '%1' للمقدمة في '%2'\"\nUndo.MoveToBottom=\"نقل '%1' للمؤخرة في '%2'\"\nUndo.PasteSource=\"لصق المصدر/المصادر في '%1'\"\nUndo.PasteSourceRef=\"لصق مرجع/مراجع المصدر في '%1'\"\nUndo.GroupItems=\"تجميع العناصر في '%1'\"\nTransitionNameDlg.Text=\"يرجي إدخال اسم تأثير الإنتقال Text\"\nTransitionNameDlg.Title=\"اسم تأثير الإنتقال\"\nTitleBar.SafeMode=\"الوضع الآمن\"\nTitleBar.PortableMode=\"الوضع المحمول\"\nTitleBar.Profile=\"الملف الشخصي\"\nTitleBar.Scenes=\"المشاهد\"\nNameExists.Title=\"الاسم موجود بالفعل\"\nNameExists.Text=\"الاسم مستخدم حاليا.\"\nNoNameEntered.Title=\"الرجاء إدخال اسم صالح\"\nNoNameEntered.Text=\"لا يمكنك استخدام أسماء فارغة.\"\nConfirmStart.Title=\"بدء البث ؟\"\nConfirmStart.Text=\"هل انت متأكد انك تريد بدء البث ؟\"\nConfirmStop.Title=\"ايقاف البث؟\"\nConfirmStop.Text=\"هل أنت متأكد انك تريد إيقاف البث؟\"\nConfirmStopRecord.Title=\"إيقاف التسجيل؟\"\nConfirmStopRecord.Text=\"هل أنت متيقِّن انك تريد إيقاف التسجيل؟\"\nConfirmBWTest.Title=\"بدء فحص سرعة التدفق؟\"\nConfirmBWTest.Text=\"OBS لديك معد في وضع فحص تدفق السرعة. هذا الوضع يسمح لك بفحص الشبكة بدون ان تظهر في بث مباشر على قناتك. بمجرد انتهاء الفحص يجب عليك تعطيله لكي يستطيع مشاهدوك من رؤية البث.\\n\\nهل تريد الاستمرار ؟\"\nConfirmRemove.Title=\"تأكيد الإزالة\"\nConfirmRemove.Text=\"هل أنت متأكد من رغبتك في إزالة '%1'؟\"\nConfirmRemove.TextMultiple=\"هل أنت متاكد انك تريد حذف هذه ال%1 عناصر؟\"\nConfirmReset.Title=\"إعادة تعيين الخصائص\"\nConfirmReset.Text=\"هل أنت متيقِّن من إعادة تعيين الخصائص إلى الإعدادات الافتراضية؟\"\nOutput.StartStreamFailed=\"فشل في بدء البث\"\nOutput.StartRecordingFailed=\"فشل في بدء التسجيل\"\nOutput.StartReplayFailed=\"فشل بدء التخزين المؤقت للإعادة\"\nOutput.StartVirtualCamFailed=\"فشل في تشغيل الكاميرا الافتراضية\"\nOutput.StartFailedGeneric=\"فشل بدء الإخراج. الرجاء مراجعة السجل للتفاصيل.\\n\\nملاحظة: اذا كنت تستخدم مُرَمِز NVENC أو مُرَمِز AMD, تأكد من أن تعريف بطاقة الشاشة لديك محدَّث.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"لا يمكن حفظ الإعادات اثناء التوقف المؤقت\"\nOutput.ReplayBuffer.PauseWarning.Text=\"تحذير: لا يمكن حفظ الإعادات اثناء توقيف التسجيل مؤقتاً.\"\nOutput.ConnectFail.Title=\"فشل في الاتصال\"\nOutput.ConnectFail.BadPath=\"مسار أو رابط الاتصال غير صالح. الرجاء التحقق من الإعدادات للتحقق من كونه صالح.\"\nOutput.ConnectFail.ConnectFailed=\"فشل الاتصال بالسيرفر\"\nOutput.ConnectFail.InvalidStream=\"تعذر الوصول إلى القناة أو مفتاح البث المحدد، الرجاء أعد التحقق من صحة مفتاح البث الخاص بك، أما إذا كان صحيحاً، قد تكون المشكلة في الاتصال بالخادم.\"\nOutput.ConnectFail.HdrDisabled=\"مخرج HDR معطل حاليًا لهذا الإصدار.\"\nOutput.ConnectFail.Error=\"حدث خطأ غير متوقع عند محاولة الاتصال بالخادم. مزيد من المعلومات في ملف السجل.\"\nOutput.ConnectFail.Disconnected=\"تم قطع الاتصال من السيرفر.\"\nOutput.StreamEncodeError.Title=\"خطأ في الترميز\"\nOutput.StreamEncodeError.Msg=\"حدث خطأ في الترميز أثناء البث.\"\nOutput.StreamEncodeError.Msg.LastError=\"حدثَ خطأ في المُرَمِّز خلالَ عمليّة البَّث:<br><br>%1\"\nOutput.RecordFail.Title=\"فشل في بدء التسجيل\"\nOutput.RecordFail.Unsupported=\"تنسيق الإخراج أما غير معتمد أو لا يدعم أكثر من مسار للصوت. الرجاء التحقق من الإعدادات الخاصة بك وحاول مرة أخرى.\"\nOutput.RecordNoSpace.Title=\"مساحة القرص غير كافية\"\nOutput.RecordNoSpace.Msg=\"لا توجد مساحة كافية على القرص لمتابعة التسجيل.\"\nOutput.RecordError.Title=\"خطأ في التسجيل\"\nOutput.RecordError.Msg=\"حدث خطأ غير محدد أثناء التسجيل.\"\nOutput.RecordError.EncodeErrorMsg=\"حدث خطأ في الترميز أثناء التسجيل.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"حدثَ خطأ في المُرَمِّز خلالَ عمليّة التَّسجِيل:<br><br>%1\"\nOutput.BadPath.Title=\"مسار الملف غير صحيح\"\nOutput.BadPath.Text=\"تعذر فتح مسار التسجيل الذي تم تكوينه. الرجاء التحقق من مسار التسجيل الخاص بك تحت الإعدادات → الإخراج → التسجيل.\"\nOutput.NoBroadcast.Title=\"لم يتم تكوين البث\"\nOutput.NoBroadcast.Text=\"تحتاج إلى إعداد بث قبل أن تتمكن من بدء البث.\"\nOutput.BroadcastStartFailed=\"فشل في بدء البث\"\nOutput.BroadcastStopFailed=\"فشل في إيقاف البث\"\nLogUploadDialog.Title=\"تم رفع ملف سجل OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"يرجى قراءة <a href=’https://obsproject.com/privacy-policy‘>سياسة الخصوصية</a> والقسم المتعلق برفع الملفات قبل رفع أي ملفات.\"\nLogUploadDialog.Labels.Progress=\"جاري رفع السجل. إنتظر من فضلك...\"\nLogUploadDialog.Labels.Description.AppLog=\"تم تحميل تقرير الأعطال الخاص بك. يمكنك الآن مشاركة عنوان URL لأغراض المساعدة او تصحيح الأخطاء.\"\nLogUploadDialog.Labels.Description.CrashLog=\"تم تحميل تقرير الأعطال الخاص بك. يمكنك الآن مشاركة عنوان URL لأغراض تصحيح الأخطاء.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"رفع\"\nLogUploadDialog.Buttons.CopyURL=\"نسخ عنوان URL للسجل\"\nLogUploadDialog.Buttons.AnalyzeURL=\"حلل ملف السجل\"\nLogUploadDialog.Buttons.RetryButton=\"إعادة المحاولة\"\nLogUploadDialog.Errors.Template=\"\\حدث خطأ أثناء محاولة رفع الملف:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"لم يتم العثور على ملف للتحميل أو كان الملف فارغًا.\"\nRemux.SourceFile=\"تسجيل OBS\"\nRemux.TargetFile=\"الملف الهدف\"\nRemux.Remux=\"تحويل الصيغة\"\nRemux.Stop=\"إيقاف عملية تحويل الصيغ\"\nRemux.ClearFinished=\"مسح العناصر المنتهية\"\nRemux.ClearAll=\"مسح كل العناصر\"\nRemux.OBSRecording=\"تسجيل OBS\"\nRemux.FinishedTitle=\"انتهت عملية تحويل الصيغة\"\nRemux.Finished=\"تسجيل عملية تحويل الصيغة\"\nRemux.FinishedError=\"عملية التحويل قد تكون غير مكتملة\"\nRemux.SelectRecording=\"اختيار تسجيل OBS...\"\nRemux.SelectTarget=\"حدد الملف الوُّجهة...\"\nRemux.FileExistsTitle=\"الملف الهدف موجود\"\nRemux.FileExists=\"الملفات التالية موجودة فعليا. هل ترغب في استبدالها؟\"\nRemux.ExitUnfinishedTitle=\"تقدم عملية التحويل\"\nRemux.ExitUnfinished=\"إيقاف عميلة التحويل الآن قد تجعل الملف غير قابل للاستخدام.\\n هل أنت متأكد أنك تريد إيقاف عملية التحويل؟\"\nRemux.HelpText=\"أسفط الملفات في هذه النافذة ل remux، أو حدد خلية \\\"تسجيل OBS\\\" فارغة للتصفح من اجل ملف.\"\nRemux.NoFilesAddedTitle=\"لم يتم إضافة ملف remuxing\"\nRemux.NoFilesAdded=\"لم يتم إضافة ملف إلى remux. قم بإسقاط مجلد يحتوي على ملف فيديو واحد أو أكثر.\"\nMissingFiles=\"الملفات المفقودة\"\nMissingFiles.MissingFile=\"ملف مفقود\"\nMissingFiles.NewFile=\"ملف جديد\"\nMissingFiles.HelpText=\"بعض الملفات مفقودة منذ آخر مرة استخدمت فيها OBS.\"\nMissingFiles.NumFound=\"عُثر على %1 من %2\"\nMissingFiles.Search=\"البحث في الدليل...\"\nMissingFiles.SelectFile=\"اختر ملفّ...\"\nMissingFiles.SelectDir=\"حدد مجلد للبحث فيه\"\nMissingFiles.State=\"حالة\"\nMissingFiles.Missing=\"مفقود\"\nMissingFiles.Replaced=\"استبدل\"\nMissingFiles.Cleared=\"مسح\"\nMissingFiles.Found=\"وجدت\"\nMissingFiles.AutoSearch=\"تم العثور على ملفات إضافية مطابقة\"\nMissingFiles.AutoSearchText=\"وجد OBS تطابقات إضافية للملفات المفقودة في هذا المجلد. هل ترغب في إضافتهم؟\"\nMissingFiles.NoMissing.Title=\"التحقق من الملفات المفقودة\"\nMissingFiles.NoMissing.Text=\"لا يبدو أن أي ملفات مفقودة.\"\nMacPermissions.MenuAction=\"مراجعة أذونات التطبيق ...\"\nMacPermissions.Title=\"مراجعة أذونات التطبيق\"\nMacPermissions.Description=\"يتطلب OBS Studio إذنك لتتمكن من توفير ميزات معينة. يوصى بتمكين هذه الأذونات ، لكنها غير مطلوبة لاستخدام التطبيق. يمكنك دائمًا تمكينها لاحقًا.\"\nMacPermissions.Description.OpenDialog=\"يمكنك إعادة فتح مربع الحوار هذا عبر قائمة OBS Studio.\"\nMacPermissions.AccessGranted=\"تم السماح بالوصول\"\nMacPermissions.RequestAccess=\"طلب الوصول\"\nMacPermissions.OpenPreferences=\"فتح تفضيلات %1\"\nMacPermissions.Item.ScreenRecording=\"تسجيل الشاشة\"\nMacPermissions.Item.ScreenRecording.Details=\"يطلب OBS هذا الإذن ليكون قادراً على التقاط الشاشة.\"\nMacPermissions.Item.Camera=\"الكاميرا\"\nMacPermissions.Item.Camera.Details=\"هذا الإذن مطلوب لالتقاط المحتوى من كاميرا الويب أو بطاقة الالتقاط.\"\nMacPermissions.Item.Microphone=\"الميكروفون\"\nMacPermissions.Item.Microphone.Details=\"يتطلب OBS هذا الإذن إذا كنت تريد التقاط الميكروفون أو جهاز صوت خارجي.\"\nMacPermissions.Item.InputMonitoring=\"مراقبة الإدخال\"\nMacPermissions.Item.InputMonitoring.Details=\"هذا الإذن مطلوب لكي تعمل مفاتيح الاختصار بينما يكون OBS في الخلفية.\"\nMacPermissions.Item.Accessibility=\"إمكانية الوصول\"\nMacPermissions.Item.Accessibility.Details=\"في التثبيتات القديمة، قد يتم إدراج OBS ضمن \\\"إمكانية الوصول\\\" بدلاً من \\\"مراقبة الإدخال\\\".\"\nMacPermissions.Continue=\"المتابعة\"\nSourceLeak.Title=\"خطأ في تنظيف المصدر\"\nSourceLeak.Text=\"حدثت مشكلة أثناء تغيير مجموعات المشهد وتعذر تفريغ بعض المصادر. تحدث هذه المشكلة عادةً بسبب المكونات الإضافية التي لا تصدر الموارد بشكل صحيح. يرجى التأكد من أن أي مكونات إضافية تستخدمها محدثة.\\n\\nسيتم الآن إنهاء برنامَج OBS Studio لمنع أي تلف محتمل في البيانات.\"\nBasic.DesktopDevice1=\"صوت سطح المكتب\"\nBasic.DesktopDevice2=\"صوت سطح المكتب 2\"\nBasic.Scene=\"مشهد\"\nBasic.DisplayCapture=\"التقاط الشاشة\"\nBasic.Main.PreviewConextMenu.Enable=\"تمكين المعاينة\"\nBasic.Main.Preview.Disable=\"تعطيل المعاينه\"\nScaleFiltering=\"مقياس التصفية\"\nScaleFiltering.Point=\"نقطة\"\nScaleFiltering.Bilinear=\"ثنائي خطي\"\nScaleFiltering.Bicubic=\"ثنائي تكعيبي\"\nScaleFiltering.Lanczos=\"والغاوسي\"\nScaleFiltering.Area=\"المنطقة\"\nBlendingMethod=\"طريقة المزج\"\nBlendingMethod.Default=\"الافتراضي\"\nBlendingMethod.SrgbOff=\"إيقاف معيار الألوان 'أحمر أخضر أزرق'\"\nBlendingMode=\"وضع المزج\"\nBlendingMode.Normal=\"عادي\"\nBlendingMode.Additive=\"أضف\"\nBlendingMode.Subtract=\"طرح\"\nBlendingMode.Screen=\"الشاشة\"\nBlendingMode.Multiply=\"اضرب\"\nBlendingMode.Lighten=\"تفتيح\"\nBlendingMode.Darken=\"داكن\"\nDeinterlacing=\"تصحيح تشابك الصورة\"\nDeinterlacing.Discard=\"تجاهل\"\nDeinterlacing.TopFieldFirst=\"أعلى الحقل الأول\"\nDeinterlacing.BottomFieldFirst=\"أسفل الحقل الأول\"\nVolControl.SliderUnmuted=\"شريط تمرير مستوي الصوت لـ'%1':\"\nVolControl.SliderMuted=\"حجم شريط التمرير ل '%1': (مكتوم حاليًا)\"\nVolControl.Mute=\"كتم الصوت '%1'\"\nVolControl.Properties=\"خصائص ل '%1'\"\nVolControl.UnassignedWarning.Title=\"مصدر صوتي غير محدد\"\nVolControl.UnassignedWarning.Text=\"لم يتم تعيين \\\"%1\\\" لأي مسارات صوتية ولن يكون مسموعًا في التدفقات أو التسجيلات.\\n\\nلتعيين مصدر صوت إلى مسار، افتح \\\"خصائص الصوت المتقدمة\\\" من خلال قائمة النقر بزر الماوس الأيمن أو الزر المسنن في شريط أدوات حوض الخلاط.\"\nBasic.Main.AddSceneDlg.Title=\"أضف المشهد\"\nBasic.Main.AddSceneDlg.Text=\"الرجاء إدخال اسم المشهد\"\nBasic.Main.DefaultSceneName.Text=\"المشهد %1\"\nBasic.Main.AddSceneCollection.Title=\"إضافة مجموعة مشاهد\"\nBasic.Main.AddSceneCollection.Text=\"الرجاء إدخال اسم لمجموعة مشهد\"\nBasic.Main.RenameSceneCollection.Title=\"إعادة تسمية مجموعة مشاهد\"\nBasic.Main.RemigrateSceneCollection.Title=\"تحديث دِقَّة مجموعة المشاهد\"\nBasic.Main.RemigrateSceneCollection.Text=\"هل تريد تحديث دِقَّة مجموعة المشاهد \\\"%1\\\" لمطابقة دِقَّة اللوحة لملف التعريف الحالي البالغة %2x%3؟\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"لا يمكن تحديث دِقَّة مجموعة المشاهد بينما المخرجات مفعلة.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"فشل تحديث دِقَّة مجموعة المشاهد. الدِقَّة الأصلية غير معروفة.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"إعادة الضبط ليس ممكناً: فشل تغيير دِقَّة OBS.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"إعادة الضبط ليس ممكناً: الدِّقَّة الحالية هي فعلًا الدِّقَّة الأساسية لمجموعة المشهد.\"\nAddProfile.Title=\"إضافة ملف تعريف\"\nAddProfile.Text=\"الرجاء إدخال اسم الملف التعريفي\"\nAddProfile.WizardCheckbox=\"معالج التكوين التلقائي\"\nRenameProfile.Title=\"إعادة تسمية الملف التعريفي\"\nBasic.Main.MixerRename.Title=\"إعادة تسمية المصدر الصوتي\"\nBasic.Main.MixerRename.Text=\"الرجاء إدخال اسم للمصدر الصوتي\"\nBasic.Main.PreviewDisabled=\"المعاينة تم تعطيلها\"\nBasic.SourceSelect=\"إنشاء/تحديد المصدر\"\nBasic.SourceSelect.CreateNew=\"إنشاء جديد\"\nBasic.SourceSelect.AddExisting=\"إضافة عنصر موجود\"\nBasic.SourceSelect.AddVisible=\"جعل المصدر ظاهراً\"\nBasic.Main.Sources.Visibility=\"الرؤية\"\nBasic.Main.Sources.VisibilityDescription=\"يتحكم في وضوح '%1' في اللوحة\"\nBasic.Main.Sources.Lock=\"قفل\"\nBasic.Main.Sources.LockDescription=\"يقفل موضح وحجم '%1' في اللوحة\"\nBasic.PropertiesWindow=\"خصائص ل '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (اختيار تلقائي: %2)\"\nBasic.PropertiesWindow.SelectColor=\"تحديد اللون\"\nBasic.PropertiesWindow.SelectFont=\"إختيار خط\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"اختر نوع الخط\"\nBasic.PropertiesWindow.ConfirmTitle=\"تم تغيير الإعدادات\"\nBasic.PropertiesWindow.Confirm=\"يوجد تعديلات غير محفوظة. هل تريد الإبقاء عليها؟\"\nBasic.PropertiesWindow.NoProperties=\"لا يوجد خصائص متوفرة\"\nBasic.PropertiesWindow.AddFiles=\"إضافة ملفات\"\nBasic.PropertiesWindow.AddDir=\"إضافة مجلد\"\nBasic.PropertiesWindow.AddURL=\"إضافة مسار/رابط\"\nBasic.PropertiesWindow.AddEditableListDir=\"إضافة مجلد إلى '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"أضف ملفات إلى '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"أضف إدخال إلى '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"تعديل إدخال من '%1'\"\nBasic.PropertiesView.FPS.Simple=\"صيغ مبسطة لعدد الإطارات/ث\"\nBasic.PropertiesView.FPS.Rational=\"قيمة FPS كسرية\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"مقاييس FPS صحيحة:\"\nBasic.PropertiesView.UrlButton.Text=\"فتح هذا الرابط في متصفحك الافتراضي؟\"\nBasic.PropertiesView.UrlButton.Text.Url=\"الرابط: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"فتح الرابط\"\nBasic.InteractionWindow=\"التفاعل مع '%1'\"\nBasic.StatusBar.Reconnecting=\"قطع الاتصال، إعادة الاتصال في %2 ثانية(ثوان) (محاولة %1)\"\nBasic.StatusBar.AttemptingReconnect=\"محاولة إعادة الاتصال... (محاولة %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"إعادة الاتصال الناجح\"\nBasic.StatusBar.Delay=\"التأخير (%1 ثانية)\"\nBasic.StatusBar.DelayStartingIn=\"التأخير (بدءاً من %1 ثانية)\"\nBasic.StatusBar.DelayStoppingIn=\"تأخير (يتوف خلال %1 ثانية)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"تأخير (يتوقف في %1 ثانية، يبدأ في %2 ثانية)\"\nBasic.StatusBar.RecordingSavedTo=\"تم حفظ التسجيل في '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"تم حفظ إعادة التشغيل المؤقتة في '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"تم حفظ لقطة الشاشة في '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"تسجيل إعادة التحويل التلقائي إلى '%1'\"\nBasic.Filters=\"مرشحات\"\nBasic.Filters.AsyncFilters=\"مرشحات الصوت/الفيديو\"\nBasic.Filters.AudioFilters=\"مرشحات الصوت\"\nBasic.Filters.EffectFilters=\"مرشحات التأثير\"\nBasic.Filters.Title=\"مرشحات لـ '%1'\"\nBasic.Filters.AddFilter.Title=\"اسم المرشّح\"\nBasic.Filters.AddFilter.Text=\"الرجاء تحديد اسم المرشح\"\nBasic.TransformWindow=\"تحوّل عنصر المشهد\"\nBasic.TransformWindow.Position=\"موضع\"\nBasic.TransformWindow.PositionX=\"الموضع العاشر\"\nBasic.TransformWindow.PositionY=\"الموضع ص\"\nBasic.TransformWindow.Rotation=\"الإستدارة\"\nBasic.TransformWindow.Size=\"الحجم\"\nBasic.TransformWindow.Width=\"العرض\"\nBasic.TransformWindow.Height=\"الإرتفاع\"\nBasic.TransformWindow.Alignment=\"محاذاة الموضعية\"\nBasic.TransformWindow.BoundsType=\"نوع مربع الإحاطة\"\nBasic.TransformWindow.BoundsAlignment=\"المحاذاة في المربع المحيط\"\nBasic.TransformWindow.Bounds=\"حجم المربع المحيط\"\nBasic.TransformWindow.BoundsWidth=\"عرض المربع المحيط\"\nBasic.TransformWindow.BoundsHeight=\"طول المربع المحيط\"\nBasic.TransformWindow.CropToBounds=\"قص إلى المربع المحيط\"\nBasic.TransformWindow.Crop=\"قطع\"\nBasic.TransformWindow.CropLeft=\"قطع لليسار\"\nBasic.TransformWindow.CropRight=\"قطع لليمين\"\nBasic.TransformWindow.CropTop=\"قطع للأعلى\"\nBasic.TransformWindow.CropBottom=\"قطع للأسفل\"\nBasic.TransformWindow.Alignment.TopLeft=\"أعلى اليسار\"\nBasic.TransformWindow.Alignment.TopCenter=\"أعلى الوسط\"\nBasic.TransformWindow.Alignment.TopRight=\"أعلى اليمين\"\nBasic.TransformWindow.Alignment.CenterLeft=\"يسار الوسط\"\nBasic.TransformWindow.Alignment.Center=\"الوسط\"\nBasic.TransformWindow.Alignment.CenterRight=\"وسط اليمين\"\nBasic.TransformWindow.Alignment.BottomLeft=\"أسفل اليسار\"\nBasic.TransformWindow.Alignment.BottomCenter=\"أسفل الوسط\"\nBasic.TransformWindow.Alignment.BottomRight=\"أسفل اليمين\"\nBasic.TransformWindow.BoundsType.None=\"لا حدود له\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"الحد الأقصى للحجم فقط\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"تحجيم للحدود الداخلية\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"تحجيم للحدود الخارجية\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"مقياس لعرض الحدود\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"مقياس للارتفاع الحدود\"\nBasic.TransformWindow.BoundsType.Stretch=\"تمتد إلى الحدود\"\nBasic.TransformWindow.Title=\"تعديل التحويل ل '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"لم يتم تحديد مصدر\"\nBasic.Main.AddSourceHelp.Title=\"لا يمكن إضافة المصدر\"\nBasic.Main.AddSourceHelp.Text=\"تحتاج إلى أن يكون على الأقل مشهد واحد لإضافة مصدر.\"\nBasic.Main.Scenes=\"المشاهد\"\nBasic.Main.Sources=\"المصادر\"\nBasic.Main.Source=\"مصدر\"\nBasic.Main.Controls=\"التحكم\"\nBasic.Main.PreparingStream=\"التحضير...\"\nBasic.Main.Connecting=\"يتصل...\"\nBasic.Main.StartRecording=\"بدء التسجيل\"\nBasic.Main.StartReplayBuffer=\"بدء التخزين المؤقت لإعادة العرض\"\nBasic.Main.SaveReplay=\"حفظ الإعادة\"\nBasic.Main.StartStreaming=\"بدء البث\"\nBasic.Main.StartBroadcast=\"إبدأ البث\"\nBasic.Main.StartVirtualCam=\"تشغيل الكاميرا الافتراضية\"\nBasic.Main.StopRecording=\"إيقاف التسجيل\"\nBasic.Main.PauseRecording=\"إيقاف التسجيل مؤقتا\"\nBasic.Main.UnpauseRecording=\"إكمال التسجيل\"\nBasic.Main.SplitFile=\"تقسيم ملف التسجيل\"\nBasic.Main.AddChapterMarker=\"إضافة مؤشر للفصل يدعم فقط صيغ MP4/MOV الهجينة\"\nBasic.Main.StoppingRecording=\"إيقاف التسجيل...\"\nBasic.Main.StopReplayBuffer=\"إيقاف الخزن المؤقت لإعادة العرض\"\nBasic.Main.StoppingReplayBuffer=\"يتم ايقاف الخزن المؤقت...\"\nBasic.Main.SetupBroadcast=\"إدارة البث\"\nBasic.Main.StopStreaming=\"إيقاف البث\"\nBasic.Main.StopBroadcast=\"إنهاء البث\"\nBasic.Main.AutoStopEnabled=\"(إيقاف تلقائي)\"\nBasic.Main.StoppingStreaming=\"إيقاف البث...\"\nBasic.Main.ForceStopStreaming=\"إيقاف البث (تجاهل التأخير)\"\nBasic.Main.ShowContextBar=\"إظهار شريط أدوات المصدر\"\nBasic.Main.HideContextBar=\"إخفاء شريط أدوات المصدر\"\nBasic.Main.StopVirtualCam=\"إيقاف الكاميرا الافتراضية\"\nBasic.Main.Group=\"مجموعة %1\"\nBasic.Main.GroupItems=\"مجموعة العناصر المختارة\"\nBasic.Main.Ungroup=\"فك التجميع\"\nBasic.Main.GridMode=\"وضعية الشبكة\"\nBasic.Main.ListMode=\"وضع القائمة\"\nBasic.Main.VirtualCamConfig=\"تكوين الكاميرا الافتراضية\"\nBasic.VCam.VirtualCamera=\"كاميرا افتراضية\"\nBasic.VCam.OutputType=\"نوع الإخراج\"\nBasic.VCam.OutputSelection=\"تحديد الإخراج\"\nBasic.VCam.OutputType.Program=\"البرنامج (افتراضي)\"\nBasic.VCam.OutputSelection.NoSelection=\"لا يوجد اختيار لهذا النوع من الإخراج\"\nBasic.VCam.RestartWarning=\"ستتم إعادة تشغيل الكاميرا الافتراضية لتطبيق هذا التغيير\"\nBasic.MainMenu.File=\"ملف (&F)\"\nBasic.MainMenu.File.Export=\"تصدير (6) (&E)\"\nBasic.MainMenu.File.Import=\"استيراد (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"إظهار التسجيلات (&R)\"\nBasic.MainMenu.File.Remux=\"تسجيلات Remux\"\nBasic.MainMenu.File.Settings=\"الإعدادات (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"إظهار مجلد الإعدادات\"\nBasic.MainMenu.File.ShowProfileFolder=\"إظهار مجلد الملف الشخصي\"\nBasic.MainMenu.File.ShowMissingFiles=\"التحقق من الملفات المفقودة\"\nBasic.MainMenu.File.Exit=\"خروج (&X)\"\nBasic.MainMenu.Edit=\"تعديل (&E)\"\nBasic.MainMenu.Edit.Undo=\"تراجع (&U)\"\nBasic.MainMenu.Edit.Redo=\"إعادة (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"&قفل المعاينة\"\nBasic.MainMenu.Edit.Scale=\"مقاييس المعاينة\"\nBasic.MainMenu.Edit.Scale.Window=\"القياس لحد النافذة\"\nBasic.MainMenu.Edit.Scale.Canvas=\"كانفاس (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"المخرج (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"تم قياسه (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"تكبير\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"تصغير\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"إعادة ضبط التكبير\"\nBasic.MainMenu.Edit.Transform=\"تحويل (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"تحرير التحويل... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"نسخ التحويل\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"لصق التحويل\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"إعادة التحويل (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"تدوير 90 درجة CW\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"تدوير 90 درجة CCW\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"تدوير 180 درجة\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"انعكاس أفقي (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"انعكاس عمودي (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"تمديد الشاشة (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"تمتد إلى الشاشة (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"وسط الشاشة (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"توسيط عامودي\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"توسيط أفقي\"\nBasic.MainMenu.Edit.Order=\"الترتيب (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"التحرك للأعلى (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"التحرك للأسفل (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"التحرك للقمة (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"التحرك للقاع (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"خصائص الصوت المتقدمة (&A)\"\nBasic.MainMenu.View=\"عرض (&V)\"\nBasic.MainMenu.View.Toolbars=\"شريط الأدوات (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"أشرطة أدوات الإرساء\"\nBasic.MainMenu.View.ContextBar=\"شريط أدوات المصدر\"\nBasic.MainMenu.View.SourceIcons=\"المصدر &أيقونات\"\nBasic.MainMenu.View.StatusBar=\"شريط الحالة (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"ملء الشاشة\"\nBasic.MainMenu.View.ResetUI=\"&إعادة ضبط الواجهة\"\nBasic.MainMenu.View.AlwaysOnTop=\"&دائماً في القمة\"\nBasic.MainMenu.View.SceneListMode=\"وضع قائمة المشهد\"\nBasic.MainMenu.Docks=\"&المخزونات\"\nBasic.MainMenu.Docks.ResetDocks=\"&إعادة تعيين المخزونات\"\nBasic.MainMenu.Docks.LockDocks=\"&قفل المخزونات\"\nBasic.MainMenu.Docks.SideDocks=\"&أرصفة كاملة الارتفاع\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&أرصفة المتصفح المخصصة...\"\nBasic.MainMenu.Import=\"استيراد...\"\nBasic.MainMenu.Export=\"تصدير...\"\nBasic.MainMenu.SceneCollection=\"مجموعة المشاهد (&S)\"\nBasic.MainMenu.Profile=\"الملف الشخصي (&P)\"\nBasic.MainMenu.Profile.Import=\"استيراد ملف تعريف\"\nBasic.MainMenu.Profile.Export=\"تصدير الملف الشخصي\"\nBasic.MainMenu.SceneCollection.Import=\"إضافة مجموعة مَشاهد\"\nBasic.MainMenu.SceneCollection.Export=\"تصدير مجموعة ألمَشاهد\"\nBasic.MainMenu.SceneCollection.Remigrate=\"إعادة الضبط الدِّقَّة الأساسية\"\nBasic.MainMenu.SceneCollection.Migrate=\"تعيين الدِّقَّة الأساسية\"\nBasic.MainMenu.Profile.Exists=\"الملف الشخصي موجود مسبقاً\"\nBasic.MainMenu.Tools=\"أدوات (&T)\"\nBasic.MainMenu.Help=\"مساعدة (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"بوابة &المساعدة\"\nBasic.MainMenu.Help.Website=\"زيارة الموقع\"\nBasic.MainMenu.Help.Discord=\"انضم إلى سيرفر الديسكورد\"\nBasic.MainMenu.Help.WhatsNew=\"ما الجديد\"\nBasic.MainMenu.Help.Logs=\"ملفات السجل (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"&عرض السجلات\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"رفع ملف السجل الحالي (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"رفع &ملف السجل السابق\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&عرض السجل الحالي\"\nBasic.MainMenu.Help.ReleaseNotes=\"ملاحظات الإصدار\"\nBasic.MainMenu.Help.CheckForUpdates=\"التحقق من وجود تحديثات\"\nBasic.MainMenu.Help.Repair=\"التحقق من سلامة الملفات\"\nBasic.MainMenu.Help.RestartSafeMode=\"إعادة تشغيل في الوضع الأمن\"\nBasic.MainMenu.Help.RestartNormal=\"إعادة تشغيل في الوضع العادي\"\nBasic.MainMenu.Help.CrashLogs=\"تقارير &الأعطال\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"&عرض تقارير الأعطال\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"رفع تقرير العطل السابق\"\nBasic.MainMenu.Help.About=\"&حول\"\nBasic.Settings.ProgramRestart=\"يجب إعادة تشغيل البرنامج حتى تصبح الإعدادات نافذة المفعول.\"\nBasic.Settings.ConfirmTitle=\"تأكيد التغييرات\"\nBasic.Settings.Confirm=\"لديك تغييرات غير محفوظة. هل تريد حفظها?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 يتحكم في بعض إعدادات البث الخاصة بك\"\nBasic.Settings.General=\"عام\"\nBasic.Settings.General.Language=\"اللغة\"\nBasic.Settings.General.Updater=\"التحديثات\"\nBasic.Settings.General.UpdateChannel=\"قناة التحديث\"\nBasic.Settings.General.UpdateChannelDisabled=\"(معطّل)\"\nBasic.Settings.General.UpdateChannelDefault=\"(افتراضي)\"\nBasic.Settings.General.EnableAutoUpdates=\"التحقق تلقائيًا من وجود تحديثات عند بدء التشغيل\"\nBasic.Settings.General.OpenStatsOnStartup=\"فتح مربع الإحصائيات عند بدء التشغيل\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"إخفاء نوافذ OBS من التقاط الشاشة\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"سيؤدي هذا الإعداد إلى إخفاء جميع نوافذ استوديو OBS غير المعروضة من التقاط OBS ويؤثر على\\nتطبيقات أخرى، مثل عقد المؤتمرات، ومشاركة الشاشات، والدعم عن بعد، ولقطات الشاشة، وغير ذلك من برمجيات التقاط البيانات.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"سيؤدي هذا الإعداد إلى إخفاء جميع نوافذ استوديو OBS غير المعروضة من التقاط OBS ويؤثر على تطبيقات أخرى، مثل عقد المؤتمرات، ومشاركة الشاشات، والدعم عن بعد، ولقطات الشاشة، وغير ذلك من برمجيات التقاط البيانات.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"إظهار مربع التأكيد عند بدء البث\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"إظهار مربع التأكيد عند إيقاف البث\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"إظهار مربع التأكيد عند إيقاف التسجيل\"\nBasic.Settings.General.Projectors=\"جهاز العرض\"\nBasic.Settings.General.HideProjectorCursor=\"إخفاء المؤشر فوق أجهزة العرض\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"اجعل أجهزة العرض دائمًا في المقدمة\"\nBasic.Settings.General.Snapping=\"محاذاة المصدر المفاجئة\"\nBasic.Settings.General.ScreenSnapping=\"جذب المصادر لحافة الشاشة\"\nBasic.Settings.General.CenterSnapping=\"جذب المصادر للمركز العمودي والأفقي\"\nBasic.Settings.General.SourceSnapping=\"جذب المصادر للمصادر الأخرى\"\nBasic.Settings.General.SnapDistance=\"حساسية الجذب\"\nBasic.Settings.General.SpacingHelpers=\"إظهار أدلة محاذاة البكسل\"\nBasic.Settings.General.RecordWhenStreaming=\"التسجيل تلقائيا عند البث\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"الاستمرار بالتسجيل عند توقف البث\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"بدء التشغيل التلقائي للتخزين المؤقت عند البث\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"إبقِ إعادة تشغيل المخزن المؤقت نشط عند توقف البث\"\nBasic.Settings.General.SysTray=\"منطقة الإعلام (الإشعارات)\"\nBasic.Settings.General.SysTrayWhenStarted=\"التصغير إلى منطقة الإشعارات عند بدء التشغيل\"\nBasic.Settings.General.SystemTrayHideMinimize=\"التصغير دائماً إلى منطقة الإشعارات بدلاً من شريط المهام\"\nBasic.Settings.General.SaveProjectors=\"حفظ العرض عند الخروج\"\nBasic.Settings.General.CloseExistingProjectors=\"الحد من عرض ملء الشاشة لكل شاشة\"\nBasic.Settings.General.Preview=\"المعاينة\"\nBasic.Settings.General.OverflowHidden=\"إخفاء خلفية المعاينة\"\nBasic.Settings.General.OverflowAlwaysVisible=\"إظهار خلفية المعاينة دائماََ\"\nBasic.Settings.General.OverflowSelectionHidden=\"إظهار خلفية المعاينة حتى عندما يكون المصدر ظاهراََ\"\nBasic.Settings.General.Importers=\"الموارد\"\nBasic.Settings.General.AutomaticCollectionSearch=\"البحث عن المواقع المعروفة لمجموعات المشاهد عند الاستيراد\"\nBasic.Settings.General.SwitchOnDoubleClick=\"الانتقال إلى المشهد عند النقر المزدوج\"\nBasic.Settings.General.StudioPortraitLayout=\"تمكين التخطيط العامودي/الرأسي\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"إظهار تسميات المعاينة/البرنامج\"\nBasic.Settings.General.Multiview=\"عرض متعدد\"\nBasic.Settings.General.Multiview.MouseSwitch=\"انقر للتبديل بين المشاهد\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"إظهار أسماء المشهد\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"رسم مناطق آمنة (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"تخطيط المشاهد المتعددة\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"أفقي، أعلى (8 مشاهد)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"أفقي، أسفل (8 مشاهد)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"عمودي, يسار (8 مشاهد)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"عمودي, يمين (8 مشاهد)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"أفقي، أعلى (18 مشاهد)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"أفقي، أعلى (24 مشهد)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"المشاهد فقط (4 مشاهد)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"المشاهد فقط (9 مشاهد)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"المشاهد فقط (16 مشاهد)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"المشاهد فقط (25 مشاهد)\"\nBasic.Settings.General.ChannelName.stable=\"مستقر\"\nBasic.Settings.General.ChannelDescription.stable=\"أحدث إصدار مستقر\"\nBasic.Settings.General.ChannelName.beta=\"الإصدارات التجريبية / الإصدارات المرشحة\"\nBasic.Settings.General.ChannelDescription.beta=\"إصدارات ما قبل الإصدار غير مستقرة\"\nBasic.Settings.Appearance=\"المظهر\"\nBasic.Settings.Appearance.General=\"الإعدادات العامة\"\nBasic.Settings.Appearance.General.Theme=\"القالب\"\nBasic.Settings.Appearance.General.Variant=\"النمط\"\nBasic.Settings.Appearance.General.NoVariant=\"لا توجد أنماط متوفرة\"\nBasic.Settings.Appearance.FontScale=\"حجم الخط\"\nBasic.Settings.Appearance.Density=\"الكثافة\"\nBasic.Settings.Appearance.Classic=\"كلاسيكي\"\nBasic.Settings.Appearance.Compact=\"مضغوط\"\nBasic.Settings.Appearance.Normal=\"عادي\"\nBasic.Settings.Appearance.Comfortable=\"مريح\"\nBasic.Settings.Appearance.OptionsWarning=\"بعض تخصيصات المظهر غير متوفرة لهذا النمط\"\nBasic.Settings.Stream=\"بث\"\nBasic.Settings.Stream.Destination=\"وجهة\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"استخدام المصادقة\"\nBasic.Settings.Stream.Custom.Username=\"اسم المستخدم\"\nBasic.Settings.Stream.Custom.Password=\"كلمة السر\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: أدخل srp_username.\\nRTMP: أدخل اسم المستخدم.\\nSRT: غير مستخدم.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: أدخل كلمة المرور srp_passd.\\nRTMP: أدخل كلمة المرور.\\nSRT: أدخل كلمة مرور التشفير.\"\nBasic.Settings.Stream.BandwidthTestMode=\"تفعيل وضح اختبار عرض النطاق\"\nBasic.Settings.Stream.TTVAddon=\"Twitch إضافات المحادثة\"\nBasic.Settings.Stream.TTVAddon.None=\"بدون\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV و FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"إعداد البث مفقود\"\nBasic.Settings.Stream.StreamSettingsWarning=\"فتح الإعدادات\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"عنوان URL ومفتاح البث مفقودان.\\n\\nافتح الإعدادات لإدخال عنوان URL ومفتاح البث في علامة التبويب 'البث'.\"\nBasic.Settings.Stream.MissingUrl=\"عنوان URL البث مفقود.\\n\\nافتح الإعدادات وادخل عنوان الـURL في علامة التبويب 'البث'.\"\nBasic.Settings.Stream.MissingStreamKey=\"مفتاح البث مفقود.\\n\\nافتح الإعدادات وادخل مفتاح البث في علامة التبويب 'البث'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"تجاهل توصيات إعداد خدمات البث\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"تجاوز الإعدادات الموصى بها\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"تحذير: تجاهل قيود الخدمة قد يؤدي إلى تدهور جودة البث أو منعك من البث.\\n\\nالمتابعة؟\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"الحد الأقصى للفيديو: %1 كيلوبايت\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"أقصى معدل للصوت: %1 كيلوبايت\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"أقصى دقة : %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"الحد الأقصى من FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"حدد خادما مخصصا...\"\nBasic.Settings.Stream.ServiceCustomServer=\"خادم مخصص\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"تمكين %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"الحد الأقصى لعرض النطاق الترددي للبث\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"تلقائي\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"الحد الأقصى لمسارات الفيديو\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"تلقائي\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"قم بتمكين تفريغ البث إلى FLV يستخدم إعدادات ملف التسجيل البسيطة\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"تجاوز التكوين (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"تمكين تجاوز التكوين\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"فيديو متعدد المسارات\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"لوحات إضافية\"\nBasic.Settings.Stream.AdvancedOptions=\"خيارات متقدمة\"\nBasic.Settings.Output=\"المخرج\"\nBasic.Settings.Output.Format=\"صيغة التسجيل\"\nBasic.Settings.Output.Format.MKV=\"فيديو ماتروسكا (.mkv)\"\nBasic.Settings.Output.Format.MP4=\"MPEG-4 (mp4)\"\nBasic.Settings.Output.Format.hMP4=\"MP4 الهجين (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV الهجين (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 مجزأ (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV مجزأ (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"MOV المجزأ يكتب التسجيل في أجزاء ولا يتطلب نفس الصيغة النهائية لملفات MOV التقليدية.\\nيضمن هذا بقاء الملف قابلاً للتشغيل حتى في حالة مقاطعة الكتابة على القرص ، على سبيل المثال ، نتيجة الموت الزرقاء أو فقدان الطاقة.\\n\\nقد لا يكون هذا متوافقًا مع كافة المشغلات والمحررين. استخدم File → Remux Recordings لتحويل الملف إلى تنسيق أكثر توافقًا إذا لزم الأمر.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"MP4 المجزأ يكتب التسجيل في أجزاء ولا يتطلب نفس اللمسات الأخيرة مثل ملفات MP4 التقليدية.\\nيضمن هذا بقاء الملف قابلاً للتشغيل حتى في حالة مقاطعة الكتابة على القرص ، على سبيل المثال ، نتيجة لفقد شاشة الموت الزرقاء أو فقدان الطاقة.\\n\\nقد لا يكون هذا متوافقًا مع جميع المشغلات والمحررين. استخدم File → Remux Recordings لتحويل الملف إلى تنسيق أكثر توافقًا إذا لزم الأمر.\"\nBasic.Settings.Output.Encoder.Video=\"تشفير الفيديو\"\nBasic.Settings.Output.Encoder.Audio=\"تشفير الصوت\"\nBasic.Settings.Output.SelectDirectory=\"حدد دليل التسجيل\"\nBasic.Settings.Output.DynamicBitrate=\"غير معدل البيتس ديناميكيًا لإدارة الاكتظاظ\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"غير معدل البيتس ديناميكيًا لإدارة الاكتظاظ (بيتا)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"وبدلا من إسقاط الأطر للحد من الاكتظاظ، يغير ديناميكيا معدل البتة على الذباب.\\n\\nلاحظ أن هذا يمكن أن يزيد من التأخير للمشاهدين إذا كان هناك اكتظاظ مفاجئ كبير.\\nعندما ينخفض معدل البت، يمكن أن يستغرق الاستعادة بضع دقائق.\\n\\nمدعوم حاليا فقط لـ RTMP.\"\nBasic.Settings.Output.Mode=\"نوع المخرج\"\nBasic.Settings.Output.Mode.Simple=\"بسيط\"\nBasic.Settings.Output.Mode.Adv=\"متقدم\"\nBasic.Settings.Output.Mode.FFmpeg=\"مخرج FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"بدء التخزين المؤقت لإعادة العرض\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"الحد الأقصى لوقت إعادة العرض\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"الذاكرة العظمى\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"تقدير استخدام الذاكرة: %1 ميغابايت\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"تحذير: الاستخدام المقدر للذاكرة %1 ميجابايت أكبر من الحد الأقصى الموصى به %2 ميجابايت\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"لا يمكن تقدير استخدام الذاكرة. الرجاء تعيين حد أقصى للذاكرة.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"إعادة عرض بادئة اسم الملف المؤقت\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"اللاحق\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"لا يمكن استخدام إعادة التخزين المؤقت عند تعيين نوع التسجيل إلى إخراج مخصص من نوع (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"مسار مِلَفّ التسجيل\"\nBasic.Settings.Output.Simple.RecordingQuality=\"جودة التسجيل\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"مثل جودة البث\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"جودة عالية، حجم ملف متوسط\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"جودة لا تُميز، حجم ملف كبير\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"جودة غير مفقودة، حجم مِلَفّ كبير بشكل هائل\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"تحذير: سيتم تعيين معدل بث الفيديو إلى %1، وهو الحد الأعلى لخدمة البث الحالية.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"تحذير: سيتم تعيين مُعَدَل بث الصوت إلى %1، وهو الحد الأعلى لخدمة البث الحالية.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"تحذير: لا يمكن إيقاف التسجيلات مؤقتاً إذا تم تعيين جودة التسجيل إلى \\\"كما في البث\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"تحذير: تنسيق التسجيل المحدد حاليًا غير متوافق مع برنامج (المُرمِّز) الدفق المحدد.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"تحذير: التسجيل مع مشفر البرنامج بجودة مختلفة عن البث سيتطلب استخدام وحدة المعالجة المركزية الإضافية إذا قمت بالبث والتسجيل في نفس الوقت.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"تحذير: تؤدي الجودة غير المفقودة إلى إنشاء ملفات ذات أحجام كبيرة جدًا!  يمكن أن تستخدم الجودة غير المفقودة ما يصل إلى 7 غيغابايت من مساحة القرص في الدقيقة بدقة ومعدلات إطارات عالية.  لا يُنصح باستخدام خاصية Lossless للتسجيلات الطويلة إلا إذا كان لديك مساحة كبيرة جدًا على القرص متوفرة.  لا يتوفر المخزن المؤقت لإعادة التشغيل عند استخدام الجودة غير المفقودة.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"هل أنت متأكد أنك تريد استخدام الجودة الغير فقودة؟\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"تحذير الجودة الغير فقودة!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"برمجية (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"المعدات (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"الأجهزة (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"المعدات (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"المعدات (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"الأجهزة (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"المعدات (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"المعدات (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"المعدات (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"عتاد (أبل، H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"عتاد (أبل، H.)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"البرنامج (x264 منخفض استخدام المعالج مسبقاً، يزيد حجم الملف)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC(الافتراضي)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"تويتش مسار VOD (استخدم المسار 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"المسار الصوتي\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"دقة / معدل إطار غير متوافق\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"خدمة البث هذه لا تدعم الدِّقَّة الحالية أو عدد الإطار. سيتم التغيير إلى أقرب قيمة متوافقة:\\n\\n%1\\n\\nهل تريد المتابعة؟\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"الدقة: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"إلاطارات لكل ثانية: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"برنامَج التشفير غير متوافق\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"خدمة البث \\\"%1\\\" لا تدعم برنامَج التشفير \\\"%2\\\". سوف يتم تغيير برنامَج التشفير إلى \\\"%3\\\".\\n\\nهل ترغب في المتابعة؟\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"خدمة البث \\\"%1\\\" لا تدعم برنامَج التشفير \\\"%2\\\" و \\\"%3\\\". سوف يتم تغيير برنامَج التشفير إلى \\\"%4\\\" و \\\"%5\\\".\\n\\nهل ترغب في المتابعة؟\"\nBasic.Settings.Output.VideoBitrate=\"معدل البت للفيديو\"\nBasic.Settings.Output.AudioBitrate=\"معدل البت للصوت\"\nBasic.Settings.Output.Reconnect=\"إعادة الاتصال تلقائياً\"\nBasic.Settings.Output.RetryDelay=\"إعادة محاولة التأخير\"\nBasic.Settings.Output.MaxRetries=\"أقصى عدد للمحاولات\"\nBasic.Settings.Output.Advanced=\"تمكين إعدادات الترميز المخصصة (متقدم)\"\nBasic.Settings.Output.EncoderPreset=\"وضع الترميز\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (استخدام منخفض للمعالج، أدنى جودة)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (افتراضي) (متوسط استخدام وحدة المعالجة المركزية، الجودة القياسية)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (استخدام عالي المعالجة، جودة عالية)\"\nBasic.Settings.Output.CustomEncoderSettings=\"إعدادات الترميز المخصصة\"\nBasic.Settings.Output.CustomMuxerSettings=\"إعدادات المكسر المخصصة\"\nBasic.Settings.Output.NoSpaceFileName=\"إنشاء اسم الملف بدون مسافة\"\nBasic.Settings.Output.Adv.Rescale=\"إعادة ضبط قياسات المخرج\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"معطل\"\nBasic.Settings.Output.Adv.AudioTrack=\"مقطع صوتي\"\nBasic.Settings.Output.Adv.Streaming=\"البث\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"إعدادات البث\"\nBasic.Settings.Output.Adv.Audio.Track1=\"المسار 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"المسار 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"المسار 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"المسار 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"المسار 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"المسار 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"تراك فيديو-عند-الطلب لتويتش\"\nBasic.Settings.Output.Adv.Encoder=\"إعدادات الترميز\"\nBasic.Settings.Output.Adv.Recording=\"التسجيل\"\nBasic.Settings.Output.Adv.Recording.Settings=\"إعدادات التسجيل\"\nBasic.Settings.Output.Adv.Recording.RecType=\"نوع التسجيل\"\nBasic.Settings.Output.Adv.Recording.Type=\"النّوع\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"قياسي\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"إخراج مخصص (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(استخدم ترميز البث)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"تنسيق اسم الملف\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"مسح الملف ثم الحفظ إذا كان الملف موجود بالفعل\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"يوفَّر المُخرَج المخصص (FFmpeg) بلا وقاية.\\nضع في اعتبارك استخدام نوع التسجيل \\\"القياسي\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"نوع إخراج FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"التصدير إلى رابط URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"التصدير إلى ملف\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"صيغ التسجيل الشائعة\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"‮كل الملفات\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"مسار ملف أو URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"صيغة الحاوية\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"صوت\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"فيديو\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"الصيغة الافتراضية\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"تفاصيل صيغة الحاوية\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"تم تخمين ترميز الصوت/الفيديو من ملف أو من رابط URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"المُرَمّز الافتراضي\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"تعطيل المُرمّز\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"مُرمّز الفيديو\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"إعدادات مُرمّز الفيديو (إن وجد)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"مُرمّز الصوت\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"إعدادات مُرمّز الصوت (إن وجد)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"إعدادات Muxer (إن وجد)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"الفاصل الزمني للإطار المفتاح (الإطارات)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"إظهار جميع الترميزات (حتى لو كان غير متوافق)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"إعدادات FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"تقسيم الملفات تلقائياً\"\nBasic.Settings.Output.SplitFile.TypeTime=\"تقسيم حسب الوقت\"\nBasic.Settings.Output.SplitFile.TypeSize=\"تقسيم حسب الحجم\"\nBasic.Settings.Output.SplitFile.TypeManual=\"تقسيم فقط يدوياً\"\nBasic.Settings.Output.SplitFile.Time=\"تقسيم الوقت\"\nBasic.Settings.Output.SplitFile.Size=\"تقسيم الحجم\"\nScreenshot=\"أخذ لقطة شاشة للمَخرج\"\nScreenshot.SourceHotkey=\"أخذ لقطة شاشة للمصدر المحدد\"\nScreenshot.StudioProgram=\"أخذ لقطة شاشة (للبرنامج)\"\nScreenshot.Preview=\"أخذ لقطة شاشة (للمعاينة)\"\nScreenshot.Scene=\"أخذ لقطة شاشة (للمشهد)\"\nScreenshot.Source=\"أخذ لقطة شاشة (للمصدر)\"\nFilenameFormatting.TT.CCYY=\"السنة, اربع خانات\"\nFilenameFormatting.TT.YY=\"السنة, أخر خانتين(00-99)\"\nFilenameFormatting.TT.MM=\"الشهر كرقم (01-12)\"\nFilenameFormatting.TT.DD=\"يوم من الشهر، دون مبطن (01-31)\"\nFilenameFormatting.TT.hh=\"ضبط الوقت في صيغة 24 ساعة (00-23)\"\nFilenameFormatting.TT.mm=\"الدقيقة(00-59)\"\nFilenameFormatting.TT.ss=\"الثانية(00-59)\"\nFilenameFormatting.TT.Percent=\"الإشارة أ%\"\nFilenameFormatting.TT.a=\"اسم يوم من أيام الأسبوع المختصر\"\nFilenameFormatting.TT.A=\"الاسم الكامل ليوم الأسبوع\"\nFilenameFormatting.TT.b=\"اسم أحد الأشهر مختصر\"\nFilenameFormatting.TT.B=\"اسم الشهر كامل\"\nFilenameFormatting.TT.d=\"يوم من الشهر (01-31)\"\nFilenameFormatting.TT.H=\"الساعات في صيغة 24ساعة\"\nFilenameFormatting.TT.I=\"الساعات في صيغة 12ساعة\"\nFilenameFormatting.TT.m=\"الشهر بالأرقام\"\nFilenameFormatting.TT.M=\"الدقائق\"\nFilenameFormatting.TT.p=\"صباحا أو مساء\"\nFilenameFormatting.TT.s=\"الوقت بالثواني منذ عهد UNIX\"\nFilenameFormatting.TT.S=\"الثواني\"\nFilenameFormatting.TT.y=\"السنة\"\nFilenameFormatting.TT.Y=\"السنة\"\nFilenameFormatting.TT.z=\"إزاحة ISO 8601 عن التوقيت العالمي المنسق (UTC) في المنطقة الزمنية\"\nFilenameFormatting.TT.Z=\"اسم المنطقة الزمنية\"\nFilenameFormatting.TT.FPS=\"إطار في الثانية\"\nFilenameFormatting.TT.CRES=\"قرار القاعدة (قماش)\"\nFilenameFormatting.TT.ORES=\"دقة الإخراج (تحجيمها)\"\nFilenameFormatting.TT.VF=\"تنسيق الفيديو\"\nBasic.Settings.Video=\"الفيديو\"\nBasic.Settings.Video.Adapter=\"محول الفيديو\"\nBasic.Settings.Video.BaseResolution=\"دقة القاعدة (اللوحة)\"\nBasic.Settings.Video.ScaledResolution=\"دقة المخرجات (محجومة)\"\nBasic.Settings.Video.DownscaleFilter=\"فلتر Downscale\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[الأبعاد متطابقة, لا يلزم تقليص لمساحة الإطار]\"\nBasic.Settings.Video.FPSCommon=\"القيم المشتركة للـFPS\"\nBasic.Settings.Video.FPSInteger=\"قيمة FPS صحيحة\"\nBasic.Settings.Video.FPSFraction=\"قيمة FPS كسرى\"\nBasic.Settings.Video.Numerator=\"البسط\"\nBasic.Settings.Video.Denominator=\"قاسم\"\nBasic.Settings.Video.Renderer=\"العارض\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (تجريبي)\"\nBasic.Settings.Video.InvalidResolution=\"قيمة الأبعاد غير صالحة. يجب أن يكون [العرض]x[الطول] (مثال.1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"إخراج الفيديو نشط حاليا. الرجاء إيقاف كل الإخراجات لتغيير إعدادات الفيديو.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"ثنائي الخط (الأسرع، ولكن ضبابية إذا تم التحجيم)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"ثنائي المكعب (التحجيم الشفقي، 16 عينة)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"لانكزوس (التحجيم الشفقي، 36 عينة)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"المساحة (المجموع المرجح، 4/6/9 عينات)\"\nBasic.Settings.Audio=\"الصوت\"\nBasic.Settings.Audio.SampleRate=\"معدل العينات\"\nBasic.Settings.Audio.Channels=\"قنوات\"\nBasic.Settings.Audio.Meters=\"العدادات\"\nBasic.Settings.Audio.MeterDecayRate=\"معدل التضاؤل\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"سريع\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"متوسط (النوع الأول PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"بطيء (النوع الثاني PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"نوع متر الذروة\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"عينة الذروة\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"الذروة الحقيقية (استخدام المعالج أعلي)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"تحذير: الصوت المحيطي مفعل.\"\nBasic.Settings.Audio.MultichannelWarning=\"في حالة البث ، تحقق لمعرفة ما إذا كانت خدمة البث لديك تدعم كلاً من استيعاب الصوت المحيط وتشغيل الصوت المحيط. على سبيل المثال ، يدعم Facebook 360 Live الصوت المحيطي بشكل كامل ؛ يدعم YouTube Live إدخال الصوت 5.1 (والتشغيل على أجهزة التلفزيون).\\n\\nتتوافق عوامل تصفية الصوت OPS مع الصوت المحيط ، على الرغم من أن دعم المكون الإضافي VST غير مضمون.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"تمكين الصوت المحيط؟\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"هل أنت متأكد من أنك تريد تمكين الصوت المحيط؟\"\nBasic.Settings.Audio.Devices=\"أجهزة الصوت الشاملة\"\nBasic.Settings.Audio.DesktopDevice=\"صوت سطح المكتب\"\nBasic.Settings.Audio.DesktopDevice2=\"صوت سطح المكتب 2\"\nBasic.Settings.Audio.AuxDevice=\"صوت Mic/Auxiliary\"\nBasic.Settings.Audio.AuxDevice2=\"صوت Mic/Auxiliary 2\"\nBasic.Settings.Audio.AuxDevice3=\"صوت Mic/Auxiliary 3\"\nBasic.Settings.Audio.AuxDevice4=\"صوت Mic/Auxiliary 4\"\nBasic.Settings.Audio.EnablePushToMute=\"تمكين الدفع إلى كتم\"\nBasic.Settings.Audio.PushToMuteDelay=\"تأخير اضغط للكتم\"\nBasic.Settings.Audio.EnablePushToTalk=\"شغل الضغط للتحدث\"\nBasic.Settings.Audio.PushToTalkDelay=\"تأخير اضغط للتحدث\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[الجهاز غير متصل أو غير متوفر]\"\nBasic.Settings.Audio.Disabled=\"معطّل\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"التخزين المؤقت لوضع الاستجابة السريع للصوت (لمخرجات Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"تحذير: تم تمكين التخزين المؤقت لوضع الاستجابة السريع للصوت.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"وضع التخزين المؤقت للاستجابة السريعة للصوت قد يتسبب في مشاكل في الصوت أو التوقف عن التشغيل من بعض المصادر.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"تمكين وضع التخزين المؤقت للاستجابة السريعة للصوت؟\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"هل أنت متأكد من أنك تريد تمكين التخزين المؤقت لوضع الاستجابة السريع للصوت؟\"\nBasic.Settings.Accessibility=\"إمكانية الوصول\"\nBasic.Settings.Accessibility.ColorOverrides=\"استخدام ألوان مختلفة\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"حدود المصدر (اختيار)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"حدود المصدر (تحوم)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"نطاق خلط مستوى الصوت (-60 إلى -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"نطاق خلط مستوى الصوت (-20 to -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"نطاق خلط مستوى الصوت (-9 to 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"نطاق خلط مستوى الصوت (-60 to -20dB) (فعّال)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"نطاق خلط مستوى الصوت (-20 to -9dB) (فعّال)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"نطاق خلط مستوى الصوت (-9 to 0dB) (فعّال)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"التعيين المسبق لللون\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"افتراضي\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"مخصص\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"بديل كفيف اللون\"\nBasic.Settings.Advanced=\"متقدم\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"إظهار المخرجات النشطة تحذير عند الخروج\"\nBasic.Settings.Advanced.General.ProcessPriority=\"أولوية العملية\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"عالٍ\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"أعلى من الطبيعي\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"عادي\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"تحت المعدل الطبيعي\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"خامل\"\nBasic.Settings.Advanced.FormatWarning=\"تحذير: صيغ الألوان الأخرى غير NV12/P010 مخصصة في الأساس للتسجيل، ولا ينصح بها عند البث. قد يسبب البث زيادة في استخدام وحدة المعالجة المركزية بسبب تحويل تنسيق الألوان.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"تحذير: الصيغ عالية الدِّقَّة تستخدم غالباً مع مساحات ألوان HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"تحذير: التوصية 2100 ينبغي أن تستخدم صيغة أكثر دقة.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"تنسيق اللون\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 ب (8-بت، 4:2:0، مستويان)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 ب (8-بت، 4:2:0، 3 مستويات)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 ب (8-بت، 4:4:4، 3 مستويات)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 ب (10-بت، 4:2:0، مستويان)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 ب (10-بت، 4:2:0، 3 مستويات)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-بت، 4:2:2، مستويان)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-بت، 4:4:4، مستويان)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 بت)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"مساحة الألوان\"\nBasic.Settings.Advanced.Video.ColorRange=\"نظاق اللون\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"محدود\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"كامل\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"المستوى الأبيض لSDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"مستوى الذروة الاسمي لـ HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"جهاز المراقبة\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"الإفتراضي\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"تعطيل بطل صوت ويندوز\"\nBasic.Settings.Advanced.StreamDelay=\"تأخير البث\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"مدة الانتقال\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"الحفاظ على نقطة القطع (زيادة التأخير) عند إعادة الاتصال\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"استخدام الذاكرة المقدر: %1 ميغابايت\"\nBasic.Settings.Advanced.Network=\"الشبكة\"\nBasic.Settings.Advanced.Network.Disabled=\"بروتوكول البث المحدد حاليا لا يدعم تغيير إعدادات الشبكة.\"\nBasic.Settings.Advanced.Network.BindToIP=\"ربط بـ IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"عائلة IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"تمكين تحسينات الشبكة\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"تمكين سرعة TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"• محاولات جعل ناتج برنامج RTMP أكثر ملاءمة للتطبيقات الأخرى الحساسة للوقت على الشبكة عن طريق تنظيم معدل الإرسال.\\nقد يزيد من خطر إسقاط الأطر على الاتصالات غير المستقرة.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"سلوك التركيز على المفتاح الساخن\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"عدم تعطيل المفاتيح الساخنة\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"تعطيل المفاتيح الساخنة عندما تكون النافذة الرئيسية في التركيز\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"تعطيل المفاتيح الساخنة عندما لا تكون النافذة الرئيسية في التركيز\"\nBasic.Settings.Advanced.AutoRemux=\"إعادة تلقائياً إلى %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(تسجيل كـ mkv)\"\nBasic.AdvAudio=\"خصائص الصوت المتقدمة\"\nBasic.AdvAudio.ActiveOnly=\"المصادر الفعّالة فقط\"\nBasic.AdvAudio.Name=\"الاسم\"\nBasic.AdvAudio.Volume=\"مستوى الصوت\"\nBasic.AdvAudio.VolumeSource=\"حجم ل '%1'\"\nBasic.AdvAudio.Mono=\"أُحادي\"\nBasic.AdvAudio.MonoSource=\"واحد داونميكس ل '%1'\"\nBasic.AdvAudio.Balance=\"التوازن\"\nBasic.AdvAudio.BalanceSource=\"التعادل حسب '%1'\"\nBasic.AdvAudio.SyncOffset=\"إزاحة التزامن\"\nBasic.AdvAudio.SyncOffsetSource=\"إزاحة التزامن لـ'%1'\"\nBasic.AdvAudio.Monitoring=\"مراقبة الصوت\"\nBasic.AdvAudio.Monitoring.None=\"إيقاف المراقبة\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"مراقبة فقط (كتم الإخراج)\"\nBasic.AdvAudio.Monitoring.Both=\"راقب وانتج\"\nBasic.AdvAudio.MonitoringSource=\"مراقبة الصوت لـ'%1'\"\nBasic.AdvAudio.AudioTracks=\"المسارات\"\nBasic.Settings.Hotkeys=\"الاختصارات\"\nBasic.Settings.Hotkeys.Pair=\"مجموعات المفاتيح المشتركة مع '%1' عمل كتبديل\"\nBasic.Settings.Hotkeys.Filter=\"فلتر\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"تصفية حسب مفتاح الاختصار\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"يتم مشاركة مفتاح التشغيل السريع هذا عن طريق إجراء واحد أو أكثر ، انقر لإظهار التعارضات\"\nBasic.Settings.Hotkeys.PleaseWait=\"جاري تحميل اختصارات المفاتيح الرجاء الانتظار...\"\nBasic.Hotkeys.SelectScene=\"التبديل إلى المشهد\"\nBasic.SystemTray.Show=\"إظهار\"\nBasic.SystemTray.Hide=\"إخفاء\"\nBasic.SystemTray.Message.Reconnecting=\"تم قطع الاتصال. جاري إعادة الاتصال...\"\nHotkeys.Insert=\"إدراج\"\nHotkeys.Delete=\"حذف\"\nHotkeys.Home=\"الصفحة الرئيسية\"\nHotkeys.End=\"النهاية\"\nHotkeys.PageUp=\"صفحة لأعلى\"\nHotkeys.PageDown=\"صفحة لأسفل\"\nHotkeys.NumLock=\"قفل لوحة الأرقام\"\nHotkeys.ScrollLock=\"تشغيل المفتاح scroll lock\"\nHotkeys.CapsLock=\"مفتاح الحروف الكبيرة\"\nHotkeys.Backspace=\"مسح للخلف\"\nHotkeys.Tab=\"مفتاح Tab\"\nHotkeys.Print=\"طباعة\"\nHotkeys.Pause=\"مفتاح Pause\"\nHotkeys.Left=\"يسار\"\nHotkeys.Right=\"يمين\"\nHotkeys.Up=\"أعلى\"\nHotkeys.Down=\"أسفل\"\nHotkeys.Windows=\"ويندوز\"\nHotkeys.Super=\"سوبر\"\nHotkeys.Menu=\"القائمة\"\nHotkeys.Space=\"مسافة\"\nHotkeys.NumpadNum=\"لوحة الأرقام %1\"\nHotkeys.NumpadMultiply=\"ضرب (لوحة الأرقام)\"\nHotkeys.NumpadDivide=\"قسمة (لوحة الأرقام)\"\nHotkeys.NumpadAdd=\"زائد (لوحة الأرقام)\"\nHotkeys.NumpadSubtract=\"ناقص (لوحة الأرقام)\"\nHotkeys.NumpadDecimal=\"عشري (لوحة الأرقام)\"\nHotkeys.AppleKeypadNum=\"%1 (لوحة الأرقام)\"\nHotkeys.AppleKeypadMultiply=\"* (لوحة الأرقام)\"\nHotkeys.AppleKeypadDivide=\"/ (لوحة الأرقام)\"\nHotkeys.AppleKeypadAdd=\"+ (لوحة الأرقام)\"\nHotkeys.AppleKeypadSubtract=\"- (لوحة الأرقام)\"\nHotkeys.AppleKeypadDecimal=\". (لوحة الأرقام)\"\nHotkeys.AppleKeypadEqual=\"= (لوحة الأرقام)\"\nHotkeys.MouseButton=\"الفأرة %1\"\nMute=\"كتم الصوت\"\nUnmute=\"إلغاء كتم صوت\"\nPush-to-mute=\"الضغط للكتم\"\nPush-to-talk=\"اضغط لتتكلم\"\nSceneItemShow=\"أظهر '%1'\"\nSceneItemHide=\"اخفِ '%1'\"\nOutputWarnings.NoTracksSelected=\"يجب عليك أن تحدد علي الأقل مسار واحد\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"خطأ في إعدادات الإخراج\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"جميع المخرجات يجب أن تحتوي على صوت واحد محدد\"\nOutputWarnings.MP4Recording=\"تحذير: التسجيلات المحفوظة في MP4/MOV سوف تكون غير قابلة للاسترداد إذا لم يكن من الممكن الانتهاء من الملف (على سبيل المثال نتيجة ل BSOD، فقدان الطاقة، إلخ). إذا كنت ترغب في تسجيل مسارات صوتية متعددة تفكر في استخدام MKV وإعادة تعديل التسجيل إلى MP4/MOV بعد الانتهاء (الملف → Remux التسجيلات)\"\nOutputWarnings.CannotPause=\"تحذير: لا يمكن إيقاف التسجيلات مؤقتاً إذا تم تعيين مشفر التسجيل إلى \\\"(استخدام مشفر البث)\\\"\"\nOutputWarnings.CodecIncompatible=\"تمت إعادة تعيين اختيار برنامج ترميز الصوت أو الفيديو بسبب عدم التوافق. الرجاء تحديد برنامج تشفير متوافق من القائمة.\"\nCodecCompat.Incompatible=\"(غير متوافق مع%1)\"\nCodecCompat.CodecPlaceholder=\"حدد المُرمِّز...\"\nCodecCompat.ContainerPlaceholder=\"أختيار الصيغة...\"\nCodecCompat.CodecMissingOnExit.Title=\"لم يتم تحديد مُرمِّز\"\nCodecCompat.CodecMissingOnExit.Text=\"لم يتم تعيين برنامَج ترميز فيديو أو صوت واحد في الأقل. يُرجى التأكد من تحديد المُرمِّز لكل من التسجيل والبث.\"\nCodecCompat.ContainerMissingOnExit.Title=\"لا يوجد تنسيق محدد\"\nCodecCompat.ContainerMissingOnExit.Text=\"لم يتم تحديد تنسيق التسجيل. يُرجى تحديد تنسيق تسجيل متوافق مع مُرمِّز البث المحدد.\"\nFinalScene.Title=\"حذف المشهد\"\nFinalScene.Text=\"يجب أن يكون مشهد واحد على الأقل.\"\nNoSources.Title=\"لا يوجد مصدر\"\nNoSources.Text=\"يبدو أنك لم تضف أي مصادر فيديو حتى الآن، لذلك ستبث شاشة سوداء فقط. هل أنت متأكد من أنك تريد القيام بهذا؟\"\nNoSources.Text.AddSource=\"يمكنك إضافة مصادر بالنقر على الرمز + تحت مربع المصادر في النافذة الرئيسية في أي وقت.\"\nNoSources.Label=\"لا يوجد لديك اي مصادر.\\nاضغط على إشارة الـ+ في الأسفل,\\nأو انقر هنا بالزر الأيمن للفأرة لإضافة واحد.\"\nChangeBG=\"تعيين لون\"\nCustomColor=\"لون مخصص\"\nBrowserSource.EnableHardwareAcceleration=\"تمكين تسارع الجهاز المصدر للمتصفح\"\nAbout=\"حول البرنامج\"\nAbout.Info=\"استوديو OBS هو برنامج مجاني ومفتوح المصدر لتسجيل الفيديو وبرنامج بث مباشر.\"\nAbout.Donate=\"المساهمة\"\nAbout.GetInvolved=\"شارك معنا\"\nAbout.Authors=\"المؤلفون\"\nAbout.License=\"الترخيص\"\nAbout.Error=\"خطأ! تعذرت قراءة الملف.\\n\\nانتقل إلى: %1\"\nAbout.Contribute=\"دعم مشروع OBS\"\nAddUrl.Title=\"إضافة مصدر عبر عنوان URL\"\nAddUrl.Text=\"لقد قمت بسحب عنوان URL إلى OBS. سيؤدي هذا تلقائياً إلى إضافة الرابط كمصدر. هل تريد المتابعة؟\"\nAddUrl.Text.Url=\"الرابط: %1\"\nResizeOutputSizeOfSource=\"تغيير حجم الإخراج (حجم المصدر)\"\nResizeOutputSizeOfSource.Text=\"سيتم تغيير حجم دقة الأساس والإخراج إلى حجم المصدر الحالي.\"\nResizeOutputSizeOfSource.Continue=\"هل تريد الاستمرار؟\"\nPreviewTransition=\"عرض الانتقال\"\nImporter=\"مستورد مجموعة المشهد\"\nImporter.SelectCollection=\"تحديد مجموعة مشاهد\"\nImporter.Collection=\"مجموعة مشاهد\"\nImporter.HelpText=\"إضافة ملفات إلى هذه النافذة لاستيراد مجموعات من OBS أو برامج أخرى مدعومة.\"\nImporter.Path=\"مسار المجموعة\"\nImporter.Program=\"التطبيق المكتشف\"\nImporter.AutomaticCollectionPrompt=\"البحث تلقائياً عن مجموعات المشاهد\"\nImporter.AutomaticCollectionText=\"يمكن لـ OBS العثور تلقائياً على مجموعات مشاهد مستوردة من البرامج المدعومة من الأطراف الثالثة. هل ترغب في أن يجد OBS المجموعات لك تلقائياً؟\\n\\nيمكنك تغيير هذا لاحقًا في الإعدادات > العامة > المستوردين.\"\nImporter.SelectFile=\"تصفح...\"\nRestart=\"إعادة التشغيل\"\nNeedsRestart=\"يجب إعادة تشغيل OBS Studio. هل تريد إعادة التشغيل الآن؟\"\nLoadProfileNeedsRestart=\"يحتوي الملف الشخصي على إعدادات تتطلب إعادة تشغيل OBS:\\n%1\\n\\nهل تريد إعادة تشغيل OBS لتفعيل هذه الإعدادات؟\"\nContextBar.NoSelectedSource=\"لم يتم تحديد مصدر\"\nContextBar.MediaControls.PlayMedia=\"تشغيل الوسائط\"\nContextBar.MediaControls.PauseMedia=\"إيقاف الوسائط\"\nContextBar.MediaControls.StopMedia=\"‮أوقف الوسائط\"\nContextBar.MediaControls.RestartMedia=\"إعادة تشغيل الوسائط\"\nContextBar.MediaControls.PlaylistNext=\"التالي في قائمة التشغيل\"\nContextBar.MediaControls.PlaylistPrevious=\"السابق في قائمة التشغيل\"\nContextBar.MediaControls.BlindSeek=\"أداة البحث عن الوسائط\"\nYouTube.Auth.Ok=\"اكتمل التفويض بنجاح.\\nيمكنك الآن إغلاق هذه الصفحة.\"\nYouTube.Auth.NoCode=\"ولم تكتمل عملية التصريح.\"\nYouTube.Auth.NoChannels=\"لا توجد قناة (قنوات) متاحة في الحساب المحدد\"\nYouTube.Auth.WaitingAuth.Title=\"تفويض مستخدم اليوتيوب\"\nYouTube.Auth.WaitingAuth.Text=\"الرجاء إكمال الإذن في المتصفح الخارجي الخاص بك.<br>إذا لم يفتح المتصفح الخارجي، اتبع هذا الرابط وأكمل التفويض:<br>%1\"\nYouTube.AuthError.Text=\"فشل في الحصول على معلومات القناة: %1.\"\nYouTube.Actions.WindowTitle=\"إعداد بث اليوتيوب - القناة: %1\"\nYouTube.Actions.CreateNewEvent=\"إنشاء بث جديد\"\nYouTube.Actions.ChooseEvent=\"حدد البث الحالي\"\nYouTube.Actions.Title=\"العنوان*\"\nYouTube.Actions.MyBroadcast=\"البث الخاص بي\"\nYouTube.Actions.Description=\"الوصف\"\nYouTube.Actions.Privacy=\"الخصوصية*\"\nYouTube.Actions.Privacy.Private=\"خاص\"\nYouTube.Actions.Privacy.Public=\"عام\"\nYouTube.Actions.Privacy.Unlisted=\"غير مدرج\"\nYouTube.Actions.Category=\"الفئة\"\nYouTube.Actions.Thumbnail=\"صورة مصغرة\"\nYouTube.Actions.Thumbnail.SelectFile=\"اختر ملفّ...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"لم يتم تحديد ملف\"\nYouTube.Actions.Thumbnail.ClearFile=\"مسح\"\nYouTube.Actions.MadeForKids=\"هل هذا الفيديو موجه إلى الأطفال؟*\"\nYouTube.Actions.MadeForKids.Yes=\"نعم، إنه موجه للأطفال\"\nYouTube.Actions.MadeForKids.No=\"لا، إنه ليس موجه للأطفال\"\nYouTube.Actions.AdditionalSettings=\"إعدادات إضافية\"\nYouTube.Actions.Latency=\"تأخير\"\nYouTube.Actions.Latency.Normal=\"عادي\"\nYouTube.Actions.Latency.Low=\"منخفض\"\nYouTube.Actions.Latency.UltraLow=\"منخفض جدا\"\nYouTube.Actions.EnableAutoStart=\"تمكين التشغيل التلقائي\"\nYouTube.Actions.EnableAutoStop=\"تمكين الإيقاف التلقائي\"\nYouTube.Actions.AutoStartStop.TT=\"أشر إلى إذا كان يجب أن يبدأ هذا البث المجدول تلقائياً\"\nYouTube.Actions.EnableDVR=\"تمكين DVR\"\nYouTube.Actions.360Video=\"فيديو 360\"\nYouTube.Actions.ScheduleForLater=\"جدولة لاحقاً\"\nYouTube.Actions.RememberSettings=\"تذكر هذه الإعدادات\"\nYouTube.Actions.Create_Ready=\"إنشاء بث\"\nYouTube.Actions.Create_GoLive=\"إنشاء بث وبدء البث\"\nYouTube.Actions.Choose_Ready=\"حدد البث\"\nYouTube.Actions.Choose_GoLive=\"حدد البث وبدء البث\"\nYouTube.Actions.Create_Schedule=\"جدولة البث\"\nYouTube.Actions.Create_Schedule_Ready=\"جدولة واختيار البث\"\nYouTube.Actions.Dashboard=\"فتح استوديو اليوتيوب\"\nYouTube.Actions.Error.Title=\"خطأ في إنشاء البث المباشر\"\nYouTube.Actions.Error.Text=\"خطأ في الوصول إلى يوتمون '%1'.<br/>يمكن العثور على وصف خطأ مُفصّل في <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"خطأ في الوصول إلى اليوتيوب. الرجاء التحقق من اتصال الشبكة الخاصة بك أو القدرة على الوصول إلى خادم اليوتيوب.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"خطأ في إنشاء البث '%1'.<br/>يمكن العثور على وصف خطأ مفصل في <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"لم يتم إنشاء البث . الرجاء إعادة ربط الحساب الخاص بك.\"\nYouTube.Actions.Error.YouTubeApi=\"خطأ في واجهة برمجة التطبيقات على اليوتيوب. الرجاء الاطلاع على مِلَفّ السجل لمزيد من المعلومات.\"\nYouTube.Actions.Error.BroadcastNotFound=\"لم يتم العثور على البث المحدد.\"\nYouTube.Actions.Error.FileMissing=\"المِلَفّ المحدد غير موجود.\"\nYouTube.Actions.Error.FileOpeningFailed=\"فشل فتح المِلَفّ المحدد.\"\nYouTube.Actions.Error.FileTooLarge=\"المِلَفّ المحدد كبير جدا (الحد الأقصى: 2 ميغابايت).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"فشل نقل البث: %1<br/><br/>إذا استمر هذا الخطأ <a href='https://studio.youtube.com/video/%2/livestreaming'>فتح البث في استوديو يوتيوب</a> وحاول يدويا.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"البث ينتقل إلى مرحلة الاختبار، وهذا قد يستغرق بعض الوقت. الرجاء المحاولة مرة أخرى خلال 10-30 ثانية.\"\nYouTube.Actions.EventsLoading=\"تحميل قائمة الأحداث...\"\nYouTube.Actions.EventCreated.Title=\"تم إنشاء الحدث\"\nYouTube.Actions.EventCreated.Text=\"تم إنشاء الحدث بنجاح.\"\nYouTube.Actions.Stream=\"بث\"\nYouTube.Actions.Stream.ScheduledFor=\"مجدولة ل %1\"\nYouTube.Actions.Stream.Resume=\"استئناف البث المتقطع\"\nYouTube.Actions.Stream.YTStudio=\"تم إنشاؤها تلقائياً بواسطة استوديو يوتيوب\"\nYouTube.Actions.Notify.CreatingBroadcast=\"جاري إنشاء بث مباشر جديد ، يرجى الانتظار ...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"البدء اليدوي مطلوب\"\nYouTube.Actions.AutoStartStreamingWarning=\"بَدْء التشغيل التلقائي معطل لهذا الحدث، انقر فوق \\\"Go live\\\" لبدء البث الخاص بك.\"\nYouTube.Actions.AutoStopStreamingWarning=\"لن تتمكن من إعادة الاتصال.<br>سيتوقف البث الخاص بك ولن يكون البث حي.\"\nYouTube.Errors.liveStreamingNotEnabled=\"لم يتم تمكين البث المباشر على قناة يوتيوب المحددة.<br/><br/>راجع <a href='https://www.youtube.com/features'>youtube.com/features</a> لمزيد من المعلومات.\"\nYouTube.Errors.livePermissionBlocked=\"البث المباشر غير متوفر على قناة يوتيوب المختارة.<br/>يرجى ملاحظة أنه قد يستغرق ما يصل إلى 24 ساعة لكي يصبح البث المباشر متاحا بعد تمكينه في إعدادات القناة الخاصة بك.<br/><br/>راجع <a href='https://www.youtube.com/features'>youtube.com/features</a> للحصول على التفاصيل.\"\nYouTube.Errors.errorExecutingTransition=\"فشل الانتقال بسبب خطأ في الخلفية. الرجاء المحاولة مرة أخرى في بضع ثوان.\"\nYouTube.Errors.errorStreamInactive=\"يوتيوب لا يتلقى بيانات للبث الخاص بك. الرجاء التحقق من الإعدادات الخاصة بك وحاول مرة أخرى.\"\nYouTube.Errors.invalidTransition=\"محاولة الانتقال لم تنجح، قد يكون ذلك بسبب أن البث لم ينته من مرحلة انتقالية سابقة. الرجاء الانتظار بضع ثوان و المحاولة مرة أخرى.\"\nYouTube.DocksRemoval.Title=\"امسح قواعد متصفح YouTube القديمة\"\nYouTube.DocksRemoval.Text=\"ستتم إزالة قواعد إرساء المتصفح هذه باعتبارها مهملة: \\n\\n%1\\nاستخدم \\\"Docks / غرفة التحكم المباشر على YouTube\\\" بدلاً من ذلك.\"\nConfigDownload.WarningMessageTitle=\"تحذير\"\nFailedToStartStream.MissingConfigURL=\"لا يوجد عنوان URL للتكوين متاح للخدمة الحالية\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"لم يتم تحديد عنوان URL RTMP المخصص\"\nFailedToStartStream.InvalidCustomConfig=\"تكوين مخصص غير صالح\"\nFailedToStartStream.MissingCanvas=\"لا يوجد لوحة إضافية مهيئة\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"أخفق إنشاء خدمة فيديو متعددة المسارات\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"فشل إنشاء مُخرَج RTMP لفيديو متعدد المسارات\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC غير متوفر.\\n\\nفشل في العثور على نوع المُرمِّز '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"فشل إنشاء مُرمِّز الفيديو '%1' (النوع: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"فشل الحصول على معلومات فيديو OBS أثناء إنشاء المُرمِّز '%1' (النوع: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"فشل في إنشاء  مُرمِّز الصوت\"\nFailedToStartStream.NoRTMPURLInConfig=\"لا يحتوي التكوين على عنوان URL RTMP(S) لهدف البث\"\nFailedToStartStream.FallbackToDefault=\"فشل بدء البث باستخدام %1؛ هل تريد إعادة المحاولة باستخدام إعدادات التشفير الفردية؟\"\nFailedToStartStream.ConfigRequestFailed=\"تعذر جلب التكوين من %1<br><br>خطأ HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"تم استلام قيمة حالة غير معروفة '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\N<br><br>\\nهل تريد مواصلة البث بدون %1؟\"\nFailedToStartStream.WarningRetry=\"\\N<br><br>\\nهل تريد مواصلة البث؟\"\nFailedToStartStream.MissingEncoderConfigs=\"لم يتضمن تكوين الذهاب المباشر تكوينات المُرمِّز\"\nFailedToStartStream.InvalidEncoderConfig=\"تضمنت التهيئة الحية تهيئة مشفّر غير صالحة\"\nFailedToStartStream.StatusMissingHTML=\"أعاد طلب الذهاب المباشر خطأ غير محدد\"\nFailedToStartStream.NoConfigSupplied=\"التكوين مفقود\"\nMultitrackVideo.Info=\"يقوم %1 تلقائيا بتحسين إعداداتك لترميز وإرسال صفات فيديو متعددة. سيؤدي تحديد هذا الخيار إلى إرسال %2 من المعلومات حول إعداد جهاز الكمبيوتر والبرامج.\"\nMultitrackVideo.IncompatibleSettings.Title=\"إعدادات غير متوافقة\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 غير متوافق حاليا مع:\\n\\n%2\\nلمواصلة البث مع %1، تعطيل الإعدادات غير المتوافقة:\\n\\n%3\\nوبدء البث مرة أخرى.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"قم بتعطيل هذا البث وابدأ البث\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"قم بتحديث الإعدادات وابدأ البث\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"ليس %1 متوافق حاليا مع كون [الصوت ← عام ← القنوات] مضبوطا إلى ’%2‘، ’%3‘\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"من الضروري ضبط [الصوت ← عام ← القنوات] إلى ’%1‘\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"يحتاج %1 إعدادات مختلفة متعددة لـ [الصوت ← عام ← القنوات]\"\nBasic.OpenPluginManager=\"مدير المكونات الإضافية\"\nPluginManager=\"مدير المكونات الإضافية\"\nPluginManager.Restart=\"يجب إعادة تشغيل OBS Studio لتطبيق تغييرات المكون الإضافي\"\nPluginManager.MissingPlugin=\"لم يتم العثور على المكون الإضافي\"\nPluginManager.Section.Discover=\"تصفُح\"\nPluginManager.Section.Manage=\"مُثبت\"\nPluginManager.Section.Updates=\"التحديثات\"\nPluginManager.Section.Manage.Title=\"إدارة المكونات الإضافية المُفعلة\"\n"
  },
  {
    "path": "frontend/data/locale/az-AZ.ini",
    "content": "Language=\"Azərbaycanca\"\nOK=\"Oldu\"\nApply=\"Tətbiq et\"\nCancel=\"Ləğv et\"\nClose=\"Bağla\"\nSave=\"Saxla\"\nDiscard=\"İmtina et\"\nDisable=\"Deaktiv et\"\nYes=\"Bəli\"\nNo=\"Xeyr\"\nAdd=\"Əlavə et\"\nRemove=\"Sil\"\nRename=\"Adını Dəyiş...\"\nInteract=\"Qarşılıqlı Əlaqə\"\nFilters=\"Filtrlər\"\nProperties=\"Xüsusiyyətlər\"\nMoveUp=\"Yuxarı Keçir\"\nMoveDown=\"Aşağı Keçir\"\nSettings=\"Parametrlər\"\nDisplay=\"Ekran\"\nName=\"Ad\"\nExit=\"Çıxış\"\nMixer=\"Səs Mikseri\"\nBrowse=\"Gözdən keçir\"\nDroppedFrames=\"Buraxılmış Kadrlar %1 (%2%)\"\nProjector.Open.Program=\"Proqram Proyektorunu Aç\"\nProjector.Open.Preview=\"Ön Baxış Proyektorunu Aç\"\nProjector.Open.Scene=\"Səhnə Proyektorunu Aç\"\nProjector.Open.Source=\"Mənbə Proyektorunu Aç\"\nProjector.Open.Multiview=\"Çoxlu Görünüşü Aç\"\nProjector.Display=\"Ekran: %1\"\nProjector.Window=\"Yeni ayrıca pəncərə\"\nProjector.Title=\"Proyektor\"\nProjector.Title.Scene=\"Səhnə: %1\"\nProjector.Title.Source=\"Mənbə: %1\"\nProjector.Title.Multiview=\"Çoxlu Görünüş\"\nProjector.ResizeWindowToContent=\"Pəncərəni məzmuna sığdır\"\nClear=\"Təmizlə\"\nRevert=\"Geri Qaytar\"\nShow=\"Göstər\"\nHide=\"Gizlət\"\nUnhideAll=\"Hamısını Göstər\"\nUntitled=\"Adsız\"\nNew=\"Yeni...\"\nDuplicate=\"Çoxalt...\"\nEnable=\"Aktiv et\"\nDisableOSXVSync=\"macOS V-Sync-i Deaktiv et\"\nResetOSXVSyncOnExit=\"Çıxışda macOS V-Sync-i Sıfırla\"\nHighResourceUsage=\"Kodlaşdırma həddindən artıq yükləndi! Video parametrlərini azaltmağı və ya daha sürətli kodlaşdırma ilkin sazlamasından istifadə etməyi düşünün.\"\nTransition=\"Keçid\"\nQuickTransitions=\"Cəld Keçidlər\"\nFadeToBlack=\"Qaraya Tədrici Dəyiş\"\nLeft=\"Sol\"\nRight=\"Sağ\"\nTop=\"Üst\"\nBottom=\"Alt\"\nReset=\"Sıfırla\"\nHours=\"Saat\"\nMinutes=\"Dəqiqə\"\nSeconds=\"Saniyə\"\nDeprecated=\"Köhnəlmiş\"\nReplayBuffer=\"Təkrar Oxutma Buferi\"\nImport=\"İdxal et\"\nExport=\"İxrac et\"\nCopy=\"Kopyala\"\nPaste=\"Yapışdır\"\nPasteReference=\"Yapışdır (İstinad)\"\nPasteDuplicate=\"Yapışdır (Çoxalt)\"\nRemuxRecordings=\"Qeydə Alınmışları Yenidən Birləşdir\"\nNext=\"Növbəti\"\nBack=\"Geri\"\nDefaults=\"İlkin təyin olunanlar\"\nRestoreDefaults=\"İlkin təyin olunanlar\"\nHideMixer=\"Mikserdə Gizlət\"\nTransitionOverride=\"Keçid Əvəzetməsi\"\nShowTransition=\"Keçidi Göstər\"\nHideTransition=\"Keçidi Gizlət\"\nNone=\"Heç biri\"\nStudioMode.Preview=\"Ön Baxış\"\nStudioMode.Program=\"Proqram\"\nStudioMode.PreviewSceneName=\"Ön Baxış: %1\"\nStudioMode.ProgramSceneName=\"Proqram: %1\"\nShowInMultiview=\"Çoxlu Görünüşdə Göstər\"\nVerticalLayout=\"Şaquli Tərtibat\"\nGroup=\"Qrup\"\nDoNotShowAgain=\"Bir daha göstərmə\"\nDefault=\"(İlkin təyin olunan)\"\nCalculating=\"Hesablanır...\"\nFullscreen=\"Tam Ekran\"\nWindowed=\"Pəncərəli\"\nRefreshBrowser=\"Təzələ\"\nAspectRatio=\"Tərəf Nisbəti <b>%1:%2</b>\"\nLockVolume=\"Səs Ucalığını Kilidlə\"\nLogViewer=\"Jurnal Görüntüləyicisi\"\nShowOnStartup=\"Başlanğıcda göstər\"\nOpenFile=\"Faylı aç\"\nAddScene=\"Səhnə Əlavə et\"\nAddSource=\"Mənbə Əlavə et\"\nRemoveScene=\"Seçilmiş Səhnəni Sil\"\nRemoveSource=\"Seçilmiş Mənbə(lər)i Sil\"\nMoveSceneUp=\"Səhnəni Yuxarı Keçir\"\nMoveSceneDown=\"Səhnəni Aşağı Keçir\"\nMoveSourceUp=\"Mənbə(lər)i Yuxarı Keçir\"\nMoveSourceDown=\"Mənbə(lər)i Aşağı Keçir\"\nSourceProperties=\"Mənbə Xüsusiyyətlərini Aç\"\nSourceFilters=\"Mənbə Filtrlərini Aç\"\nMixerToolbarMenu=\"Səs Mikseri Menysu\"\nSceneFilters=\"Səhnə Filtrlərini Aç\"\nList=\"Siyahı\"\nGrid=\"Tor\"\nAutomatic=\"Avtomatik\"\nComingSoon=\"Tezliklə \"\nPluginsFailedToLoad.Title=\"Plagin Yükləmə Xətası\"\nPluginsFailedToLoad.Text=\"Aşağıdakı OBS plaginlərini yükləmək mümkün olmadı:\\n\\n%1\\nZəhmət olmasa, bu plaginləri güncəlləyin və ya silin.\"\nAlreadyRunning.Title=\"OBS artıq işləyir\"\nAlreadyRunning.Text=\"OBS artıq işləyir! Əgər bunu məqsədli şəkildə etməmisinizsə, zəhmət olmasa, yeni bir nüsxəni işə salmağa çalışmazdan əvvəl mövcud olan bütün OBS nüsxələrini bağlayın. Əgər OBS-i bildiriş sahəsinə kiçiltmək üçün ayarlamısınızsa, zəhmət olmasa, onun hələ də orada işlədiyini yoxlayın.\"\nAlreadyRunning.LaunchAnyway=\"Hər halda Başlat\"\nCrashHandling.Dialog.Title=\"OBS Studio Çökməsi Aşkarlandı\"\nCrashHandling.Labels.Text=\"OBS Studio düzgün bağlanmadı.\\n\\nTəhlükəsiz Rejimdə işə salınsın (üçüncü tərəf plaginləri, skriptlər və WebSocketlər deaktivdir)?\"\nCrashHandling.Labels.PrivacyNotice=\"Siz həmçinin ən son çökmə hesabatını avtomatik olaraq OBSProject-ə yükləməyi seçə bilərsiniz.<br /><br />Zəhmət olmasa, hər hansı bir faylı yükləmədən əvvəl <a href='https://obsproject.com/privacy-policy'>Məxfilik Siyasətini</a> oxuyun və fayl yükləmələri ilə bağlı hissələrə xüsusi diqqət yetirin.\"\nCrashHandling.Checkbox.SendReport=\"Məxfilik siyasətini oxudum və yüklənməsinə razılıq verirəm.\"\nCrashHandling.Buttons.LaunchSafe=\"Təhlükəsiz Rejimdə İşə Sal\"\nCrashHandling.Buttons.LaunchNormal=\"Normal Rejimdə İşə Sal\"\nCrashHandling.Errors.UploadJSONError=\"Ən son çökmə jurnalını yükləməyə çalışarkən xəta baş verdi. Zəhmət olmasa, daha sonra yenidən cəhd edin.\"\nCrashHandling.Errors.Title=\"Çökmə Jurnalı Yükləmə Xətası\"\nSafeMode.Restart=\"OBS-i Təhlükəsiz Rejimdə (üçüncü tərəf plaginləri, skriptlər və WebSocketlər deaktivdir) yenidən başlatmaq istəyirsiniz?\"\nSafeMode.RestartNormal=\"OBS-i Normal Rejimdə yenidən başlatmaq istəyirsiniz?\"\nChromeOS.Title=\"Dəstəklənməyən Platforma\"\nChromeOS.Text=\"Görünür, OBS ChromeOS konteyneri daxilində işləyir. Bu platforma dəstəklənmir.\"\nWine.Title=\"Wine aşkarlandı\"\nWine.Text=\"OBS-i Wine-da işlətmək dəstəklənmir və yaxalama və ya qurğu mənbələri kimi bir çox funksiya işləməyəcək və ya yalnız məhdud imkanlarla işləyəcək.<br><br>Bunun əvəzinə OBS-in yerli versiyasını, məsələn, <a href='https://flathub.org/apps/details/com.obsproject.Studio'>bizim Flatpak versiyamızı</a> və ya əməliyyat sisteminizin paketlərini işə salmağınız tövsiyə olunur.\"\nDockCloseWarning.Title=\"Sabitlənə Bilən Pəncərəni Bağla\"\nDockCloseWarning.Text=\"Siz indicə sabitlənilə bilən bir pəncərəni bağladınız. Əgər onu yenidən göstərmək istəsəniz, menyu zolağındakı Pəncərələr menyusundan istifadə edin.\"\nExtraBrowsers=\"Fərdi Brauzer Pəncərələri\"\nExtraBrowsers.Info=\"Onlara ad və URL verərək pəncərələr əlavə edin, sonra pəncərələri açmaq üçün Tətbiq Et və ya Bağla düyməsini klikləyin. Pəncərələri istədiyiniz zaman əlavə edə və ya silə bilərsiniz.\"\nExtraBrowsers.DockName=\"Pəncərə Adı\"\nAuth.Authing.Title=\"Autentifikasiya edilir...\"\nAuth.Authing.Text=\"%1 ilə autentifikasiya edilir, zəhmət olmasa gözləyin...\"\nAuth.AuthFailure.Title=\"Autentifikasiya Xətası\"\nAuth.AuthFailure.Text=\"%1 ilə autentifikasiya uğursuz oldu:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Autentifikasiya Tələb Olunur\"\nAuth.InvalidScope.Text=\"%1 üçün autentifikasiya tələbləri dəyişib. Bəzi funksiyalar mövcud olmaya bilər.\"\nAuth.LoadingChannel.Title=\"Kanal məlumatları yüklənir...\"\nAuth.LoadingChannel.Text=\"%1 üçün kanal məlumatları yüklənir, zəhmət olmasa gözləyin...\"\nAuth.LoadingChannel.Error=\"Kanal məlumatını əldə etmək mümkün olmadı.\"\nAuth.ChannelFailure.Title=\"Kanalı yükləmək mümkün olmadı\"\nAuth.ChannelFailure.Text=\"%1 üçün kanal məlumatını yükləmək mümkün olmadı\\n\\n%2: %3\"\nAuth.Chat=\"Söhbət\"\nAuth.StreamInfo=\"Yayım Məlumatı\"\nTwitchAuth.Stats=\"Twitch Statistikaları\"\nTwitchAuth.Feed=\"Twitch Fəaliyyət Axını\"\nTwitchAuth.TwoFactorFail.Title=\"Yayım açarını sorğulamaq mümkün olmadı\"\nTwitchAuth.TwoFactorFail.Text=\"OBS, Twitch hesabınıza qoşula bilmədi. Zəhmət olmasa, yayıma başlamaq üçün tələb olunan iki faktorlu autentifikasiyanın <a href='https://www.twitch.tv/settings/security'>Twitch təhlükəsizlik parametrlərində</a> qurulduğundan əmin olun.\"\nRestreamAuth.Channels=\"Restream Kanalları\"\nCopy.Filters=\"Filtrləri Kopyala\"\nPaste.Filters=\"Filtrləri Yapışdır\"\nBrowserPanelInit.Title=\"Brauzer Başladılır...\"\nBrowserPanelInit.Text=\"Brauzer başladılır, zəhmət olmasa gözləyin...\"\nBandwidthTest.Region=\"Bölgə\"\nBandwidthTest.Region.US=\"ABŞ\"\nBandwidthTest.Region.EU=\"Avropa\"\nBandwidthTest.Region.Asia=\"Asiya\"\nBandwidthTest.Region.Other=\"Digər\"\nBasic.AutoConfig=\"Avtomatik Konfiqurasiya Sehrbazı\"\nBasic.AutoConfig.ApplySettings=\"Parametrləri Tətbiq et\"\nBasic.AutoConfig.StartPage=\"İstifadə Məlumatı\"\nBasic.AutoConfig.StartPage.SubTitle=\"Proqramı nə üçün istifadə etmək istədiyinizi təyin edin\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Yayım üçün optimallaşdır, qeydə alma ikinci dərəcəlidir\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Yalnız qeydə alma üçün optimallaşdır, yayım etməyəcəm\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Yalnız virtual kameradan istifadə edəcəyəm\"\nBasic.AutoConfig.VideoPage=\"Video Parametrləri\"\nBasic.AutoConfig.VideoPage.SubTitle=\"İstifadə etmək istədiyiniz video parametrlərini təyin edin\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Cari İstifadədə Olanı İşlət (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Ekran %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Cari İstifadədə Olanı İşlət (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Ya 60, ya da 30, lakin mümkün olduqda 60-a üstünlük ver\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Ya 60, ya da 30, lakin yüksək rezolyusiyaya üstünlük ver\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Qeyd: Canvas (əsas) rezolyusiyası yayımladığınız və ya qeydə aldığınız rezolyusiya ilə eyni olmaq məcburiyyətində deyil. Resurs istifadəsini və ya bitreyt tələblərini azaltmaq üçün faktiki yayım/qeyd rezolyusiyanız canvas rezolyusiyasından aşağı miqyaslandırıla bilər.\"\nBasic.AutoConfig.StreamPage=\"Yayım Məlumatı\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Zəhmət olmasa, yayım məlumatınızı daxil edin\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Hesaba Qoşul (tövsiyə olunur)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Hesabdan Ayrıl\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Hesabdan Ayrılsın?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Bu dəyişiklik dərhal tətbiq olunacaq. Hesabınızdan ayrılmaq istədiyinizə əminsiniz?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Yayım Açarı Al\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Daha Çox Məlumat\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Yayım Açarı İstifadə et\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Yayım Açarından İstifadə et (qabaqcıl)\"\nBasic.AutoConfig.StreamPage.Service=\"Servis\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Hamısını Göstər...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Fərdi...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Yayım Açarı\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: şifrələmə parolasını daxil edin.\\nRTMP: xidmət tərəfindən təqdim edilən açarı daxil edin.\\nSRT: əgər xidmət istifadə edirsə, streamid daxil edin.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kodlayıcı Açar\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Daşıyıcı Token\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Qoşulmuş hesab\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Bağlantı sürəti testi ilə bitreyti təxmin et (bir neçə dəqiqə çəkə bilər)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Aparat kodlaşdırmasına üstünlük ver\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Aparat kodlaşdırması CPU istifadəsinin əksər hissəsini aradan qaldırır, lakin eyni keyfiyyət səviyyəsini əldə etmək üçün daha çox bitreyt tələb edə bilər.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Yayım xəbərdarlığı\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Bağlantı sürəti testi kanalınıza səssiz, təsadüfi video məlumatları yayımlamaq üzrədir. Əgər mümkünsə, test tamamlanana qədər yayımların videolarını saxlamağı müvəqqəti olaraq deaktiv etmək və yayımı məxfi olaraq təyin etmək tövsiyə olunur. Davam edilsin?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"%1-i Test et\"\nBasic.AutoConfig.TestPage=\"Yekun Nəticələr\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Proqram hazırda ideal parametrləri təxmin etmək üçün bir sıra testlər yerinə yetirir\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Test tamamlandı\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Bağlantı sürəti testi yerinə yetirilir, bu bir neçə dəqiqə çəkə bilər...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Bu xidmətin protokolu üçün heç bir çıxış tapılmadı\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Qoşulur: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Heç bir serverə qoşulmaq mümkün olmadı, zəhmət olmasa internet bağlantınızı yoxlayın və yenidən cəhd edin.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Bağlantı sürəti yoxlanılır: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Yayım kodlayıcısı test edilir, bu bir dəqiqə çəkə bilər...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Qeydə alma kodlayıcısı test edilir, bu bir dəqiqə çəkə bilər...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Kodlayıcını başlatmaq mümkün olmadı\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Test edilir: %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Yayım Kodlayıcısı\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Qeyd Kodlayıcısı\"\nBasic.AutoConfig.TestPage.Result.Header=\"Proqram bu təxmini parametrlərin sizin üçün ideal olduğunu müəyyən etdi:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Bu parametrləri istifadə etmək üçün Parametrləri Tətbiq et düyməsinə klikləyin. Sehrbazı yenidən konfiqurasiya etmək və təkrar cəhd etmək üçün Geri düyməsinə klikləyin. Parametrləri əl ilə konfiqurasiya etmək üçün Ləğv Et düyməsinə klikləyin və Parametrləri açın.\"\nBasic.AutoConfig.Info=\"Avtomatik konfiqurasiya sehrbazı kompüterinizin xüsusiyyətlərinə və internet sürətinizə əsasən ən yaxşı parametrləri müəyyən edəcək.\"\nBasic.AutoConfig.RunAnytime=\"Bunu istənilən vaxt Alətlər menyusuna daxil olaraq işə salmaq olar.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Yayım (Miqyaslandırılmış) Rezolyusiyası\"\nBasic.Stats=\"Statistikalar\"\nBasic.Stats.CPUUsage=\"CPU İstifadəsi\"\nBasic.Stats.HDDSpaceAvailable=\"Mövcud disk sahəsi\"\nBasic.Stats.MemoryUsage=\"Yaddaş İstifadəsi\"\nBasic.Stats.AverageTimeToRender=\"Kadrı render etmək üçün orta vaxt\"\nBasic.Stats.SkippedFrames=\"Kodlaşdırma gecikməsi səbəbindən ötürülmüş kadrlar\"\nBasic.Stats.MissedFrames=\"Render gecikməsi səbəbindən buraxılmış kadrlar\"\nBasic.Stats.Output.Stream=\"Yayım\"\nBasic.Stats.Output.Recording=\"Qeydə Alma\"\nBasic.Stats.Status=\"Vəziyyət\"\nBasic.Stats.Status.Recording=\"Qeydə Alma\"\nBasic.Stats.Status.Live=\"CANLI\"\nBasic.Stats.Status.Reconnecting=\"Yenidən qoşulur\"\nBasic.Stats.Status.Inactive=\"Qeyri-aktiv\"\nBasic.Stats.Status.Active=\"Aktiv\"\nBasic.Stats.DroppedFrames=\"Buraxılmış Kadrlar (Şəbəkə)\"\nBasic.Stats.MegabytesSent=\"Ümumi Verilənlər Çıxışı\"\nBasic.Stats.Bitrate=\"Bit sürəti\"\nBasic.Stats.DiskFullIn=\"Disk (təxminən) bu müddətə dolacaq\"\nBasic.Stats.DiskFullIn.Text=\"%1 Saat, %2 Dəqiqə\"\nBasic.Stats.ResetStats=\"Statistikanı Sıfırla\"\nResetUIWarning.Title=\"İstifadəçi interfeysini sıfırlamaq istədiyinizə əminsiniz?\"\nResetUIWarning.Text=\"İstifadəçi interfeysini sıfırlamaq əlavə pəncərələri gizlədəcək. Onların görünməsini istəyirsinizsə, bu pəncərələri Pəncərələr menyusundan yenidən görünən etməlisiniz.\\n\\nİstifadəçi interfeysini sıfırlamaq istədiyinizə əminsiniz?\"\nUpdater.Title=\"Yeni güncəlləmə mövcuddur\"\nUpdater.Text=\"Yeni güncəlləmə mövcuddur:\"\nUpdater.UpdateNow=\"İndi Güncəllə\"\nUpdater.RemindMeLater=\"Daha Sonra Xatırlat\"\nUpdater.Skip=\"Versiyanı Ötür\"\nUpdater.NoUpdatesAvailable.Title=\"Heç bir güncəlləmə mövcud deyil\"\nUpdater.NoUpdatesAvailable.Text=\"Hazırda heç bir güncəlləmə mövcud deyil\"\nUpdater.BranchNotFound.Title=\"Güncəlləmə Kanalı Silindi\"\nUpdater.BranchNotFound.Text=\"Seçdiyiniz güncəlləmə kanalı artıq mövcud deyil, OBS ilkin təyin olunan kanala sıfırlandı.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Bütövlük Yoxlanışı Mövcud Deyil\"\nUpdater.RepairButUpdatesAvailable.Text=\"Fayl bütövlüyünü yoxlamaq yalnız mövcud olan ən son versiya üçün mümkündür. OBS quraşdırmanızı yoxlamaq və güncəlləmək üçün Kömək → Güncəlləmələri Yoxla menyusundan istifadə edin.\"\nUpdater.RepairConfirm.Title=\"Bütövlük Yoxlanışını Təsdiqlə\"\nUpdater.RepairConfirm.Text=\"Bütövlük yoxlanışına başlamaq OBS quraşdırmanızı zədələnmə üçün skan edəcək və pozulmuş/dəyişdirilmiş faylları yenidən endirəcək. Bu, bir az vaxt apara bilər.\\n\\nDavam etmək istəyirsiniz?\"\nUpdater.FailedToLaunch=\"Güncəlləyicini başlatmaq mümkün olmadı\"\nQuickTransitions.SwapScenes=\"Keçiddən Sonra Ön Baxış/Proqram Səhnələrinin Yerini Dəyiş\"\nQuickTransitions.SwapScenesTT=\"Keçiddən sonra ön baxış və proqram səhnələrinin yerini dəyişir (əgər proqramın orijinal səhnəsi hələ də mövcuddursa).\\nBu, proqramın orijinal səhnəsində edilmiş hər hansı bir dəyişikliyi ləğv etməyəcək.\"\nQuickTransitions.DuplicateScene=\"Səhnəni Çoxalt\"\nQuickTransitions.DuplicateSceneTT=\"Eyni səhnəni redaktə edərkən, proqram çıxışını dəyişdirmədən mənbələrin çevrilməsini/görünməsini redaktə etməyə imkan verir.\\nProqram çıxışını dəyişdirmədən mənbələrin xüsusiyyətlərini redaktə etmək üçün 'Mənbələri Çoxalt' seçimini aktiv edin.\\nBu dəyəri dəyişdirmək cari proqram səhnəsini sıfırlayacaq (əgər hələ də mövcuddursa).\"\nQuickTransitions.EditProperties=\"Mənbələri Çoxalt\"\nQuickTransitions.EditPropertiesTT=\"Eyni səhnəni redaktə edərkən, proqram çıxışını dəyişdirmədən mənbələrin xüsusiyyətlərini redaktə etməyə imkan verir.\\nBu, yalnız 'Səhnəni Çoxalt' aktiv olduqda istifadə edilə bilər.\\nMüəyyən mənbələr (məsələn, yaxalama və ya media mənbələri) bunu dəstəkləmir və ayrıca redaktə edilə bilməz.\\nBu dəyəri dəyişdirmək cari proqram səhnəsini sıfırlayacaq (əgər hələ də mövcuddursa).\\n\\nXəbərdarlıq: Mənbələr çoxaldılacağı üçün bu, əlavə sistem və ya video resursları tələb edə bilər.\"\nQuickTransitions.HotkeyName=\"Cəld Keçid: %1\"\nBasic.AddTransition=\"Konfiqurasiya Edilə Bilən Keçid Əlavə et\"\nBasic.RemoveTransition=\"Konfiqurasiya Edilə Bilən Keçidi Sil\"\nBasic.TransitionProperties=\"Keçid Xüsusiyyətləri\"\nBasic.SceneTransitions=\"Səhnə Keçidləri\"\nBasic.TransitionDuration=\"Müddət\"\nBasic.TogglePreviewProgramMode=\"Studiya Rejimi\"\nBasic.EnablePreviewProgramMode=\"Studiya Rejimini Aktivləşdir\"\nBasic.DisablePreviewProgramMode=\"Studiya Rejimini Deaktivləşdir\"\nUndo.Undo=\"Geri Al\"\nUndo.Redo=\"Təkrarla\"\nUndo.Add=\"'%1' Əlavə et\"\nUndo.Delete=\"'%1'-i Sil\"\nUndo.Rename=\"'%1'-in Adını Dəyiş\"\nUndo.SceneCollection.Switch=\"'%1'-ə Keçid Et\"\nUndo.Item.Undo=\"%1 əməliyyatını geri al\"\nUndo.Item.Redo=\"%1 əməliyyatını təkrarla\"\nUndo.Sources.Multi=\"%1 Mənbəni Sil\"\nUndo.Filters=\"'%1'-də Filtr Dəyişiklikləri\"\nUndo.Filters.Paste.Single=\"'%1' Filtrini '%2'-ə Yapışdır\"\nUndo.Filters.Paste.Multiple=\"Filtrləri '%1'-dən '%2'-ə Kopyala\"\nUndo.Transform=\"'%1' Səhnəsində Mənbə(lər)i Çevir\"\nUndo.Transform.Paste=\"Çevrilməni '%1'-ə Yapışdır\"\nUndo.Transform.Rotate=\"'%1'-də Fırlanma\"\nUndo.Transform.Reset=\"'%1'-də Çevrilməni Sıfırla\"\nUndo.Transform.HFlip=\"'%1'-də Üfüqi Çevir\"\nUndo.Transform.VFlip=\"'%1'-də Şaquli Çevir\"\nUndo.Transform.FitToScreen=\"'%1'-də Ekrana Sığdır\"\nUndo.Transform.StretchToScreen=\"'%1'-də Ekrana Dart\"\nUndo.Transform.Center=\"'%1'-də Ekranda Mərkəzləşdir\"\nUndo.Transform.VCenter=\"'%1'-də Ekranda Şaquli Mərkəzləşdir\"\nUndo.Transform.HCenter=\"'%1'-də Ekranda Üfüqi Mərkəzləşdir\"\nUndo.Volume.Change=\"'%1'-də Səs Ucalığı Dəyişikliyi\"\nUndo.Volume.Mute=\"'%1'-i Səssizə Al\"\nUndo.Volume.Unmute=\"'%1'-in Səsini Aç\"\nUndo.Balance.Change=\"'%1'-də Səs Balansı Dəyişikliyi\"\nUndo.SyncOffset.Change=\"'%1'-də Səs Sinxronizasiyası Sürüşməsi Dəyişikliyi\"\nUndo.MonitoringType.Change=\"'%1'-də Səs İzləməsini Dəyiş\"\nUndo.Mixers.Change=\"'%1'-də Səs Mikserlərini Dəyiş\"\nUndo.ForceMono.On=\"'%1'-də Monoya Məcbur Etməni Aktivləşdir\"\nUndo.ForceMono.Off=\"'%1'-də Monoya Məcbur Etməni Deaktivləşdir\"\nUndo.Properties=\"'%1'-də Xüsusiyyət Dəyişikliyi\"\nUndo.Scene.Duplicate=\"'%1' Səhnəsini Çoxalt\"\nUndo.ShowTransition=\"'%1'-də Keçidi Göstər\"\nUndo.HideTransition=\"'%1'-də Keçidi Gizlət\"\nUndo.ShowSceneItem=\"'%2' Səhnəsində '%1'-i Göstər\"\nUndo.HideSceneItem=\"'%2' Səhnəsində '%1'-i Gizlət\"\nUndo.ReorderSources=\"'%1'-də mənbələri yenidən sırala\"\nUndo.MoveUp=\"'%2' səhnəsində '%1'-i yuxarı keçir\"\nUndo.MoveDown=\"'%2' səhnəsində '%1'-i aşağı keçir\"\nUndo.MoveToTop=\"'%2' səhnəsində '%1'-i ən yuxarıya keçir\"\nUndo.MoveToBottom=\"'%2' səhnəsində '%1'-i ən aşağıya keçir\"\nUndo.PasteSource=\"'%1' Səhnəsinə Mənbə(lər)i Yapışdır\"\nUndo.PasteSourceRef=\"'%1' Səhnəsinə Mənbə Referans(lar)ını Yapışdır\"\nUndo.GroupItems=\"Elementləri '%1' Qrupuna Daxil et\"\nTransitionNameDlg.Text=\"Zəhmət olmasa, keçidin adını daxil edin\"\nTransitionNameDlg.Title=\"Keçid Adı\"\nTitleBar.SafeMode=\"TƏHLÜKƏSİZ REJİM\"\nTitleBar.PortableMode=\"Portativ Rejim\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Səhnələr\"\nNameExists.Title=\"Ad artıq mövcuddur\"\nNameExists.Text=\"Ad artıq istifadədədir.\"\nNoNameEntered.Title=\"Zəhmət olmasa, keçərli bir ad daxil edin\"\nNoNameEntered.Text=\"Ad boş ola bilməz.\"\nConfirmStart.Title=\"Yayım Başladılsın?\"\nConfirmStart.Text=\"Yayımı başlatmaq istədiyinizə əminsiniz?\"\nConfirmStop.Title=\"Yayım Dayandırılsın?\"\nConfirmStop.Text=\"Yayımı dayandırmaq istədiyinizə əminsiniz?\"\nConfirmStopRecord.Title=\"Qeydə Alma Dayandırılsın?\"\nConfirmStopRecord.Text=\"Qeydə almanı dayandırmaq istədiyinizə əminsiniz?\"\nConfirmBWTest.Title=\"Bağlantı Sürəti Testinə Başlansın?\"\nConfirmBWTest.Text=\"Siz OBS-i bağlantı sürəti testi rejimində konfiqurasiya etmisiniz. Bu rejim, kanalınız canlı yayıma çıxmadan şəbəkə testi etməyə imkan verir. Testi bitirdikdən sonra, izləyicilərin yayımınızı görə bilməsi üçün bu rejimi deaktiv etməlisiniz.\\n\\nDavam etmək istəyirsiniz?\"\nConfirmExit.Title=\"Aktiv Çıxışlar\"\nConfirmExit.Text=\"OBS hələ də aktivdir. Bütün yayımlar/qeydlər dayandırılacaq.\"\nConfirmRemove.Title=\"Silməni Təsdiqlə\"\nConfirmRemove.Text=\"'%1'-i silmək istədiyinizə əminsiniz?\"\nConfirmRemove.TextMultiple=\"%1 elementi silmək istədiyinizə əminsiniz?\"\nConfirmReset.Title=\"Xüsusiyyətləri Sıfırla\"\nConfirmReset.Text=\"Cari xüsusiyyətləri ilkin təyin olunan dəyərlərinə sıfırlamaq istədiyinizə əminsiniz?\"\nOutput.StartStreamFailed=\"Yayımı başlatmaq mümkün olmadı\"\nOutput.StartRecordingFailed=\"Qeydə almanı başlatmaq mümkün olmadı\"\nOutput.StartReplayFailed=\"Təkrar buferini başlatmaq mümkün olmadı\"\nOutput.StartVirtualCamFailed=\"Virtual kameranı başlatmaq mümkün olmadı\"\nOutput.StartFailedGeneric=\"Çıxışı başlatmaq mümkün olmadı. Zəhmət olmasa, təfərrüatlar üçün jurnala baxın.\\n\\nQeyd: Əgər NVENC və ya AMD kodlayıcılarından istifadə edirsinizsə, video drayverlərinizin güncəl olduğundan əmin olun..\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Fasilə zamanı təkrarları saxlamaq mümkün deyil\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Xəbərdarlıq: Qeydə almağa fasilə verildikdə təkrarları saxlamaq mümkün deyil.\"\nOutput.ConnectFail.Title=\"Qoşulma uğursuz oldu\"\nOutput.ConnectFail.BadPath=\"Yanlış Yol və ya Qoşulma URL-i. Zəhmət olmasa, onların düzgün olduğunu yoxlamaq üçün parametrlərinizi yoxlayın.\"\nOutput.ConnectFail.ConnectFailed=\"Serverə qoşulma uğursuz oldu\"\nOutput.ConnectFail.InvalidStream=\"Təyin edilmiş kanala və ya yayım açarına daxil olmaq mümkün olmadı, zəhmət olmasa yayım açarınızı bir daha yoxlayın. Əgər düzgündürsə, serverə qoşulmada problem ola bilər.\"\nOutput.ConnectFail.HdrDisabled=\"Bu çıxış üçün HDR çıxışı hazırda deaktivdir.\"\nOutput.ConnectFail.Error=\"Serverə qoşulmağa cəhd edərkən gözlənilməz xəta baş verdi. Daha çox məlumat jurnal faylındadır.\"\nOutput.ConnectFail.Disconnected=\"Serverdən ayrıldı.\"\nOutput.StreamEncodeError.Title=\"Kodlaşdırma xətası\"\nOutput.StreamEncodeError.Msg=\"Yayım zamanı kodlayıcı xətası baş verdi.\"\nOutput.StreamEncodeError.Msg.LastError=\"Yayım zamanı kodlayıcı xətası baş verdi:<br><br>%1\"\nOutput.RecordFail.Title=\"Qeydə almanı başlatmaq mümkün olmadı\"\nOutput.RecordFail.Unsupported=\"Çıxış formatı ya dəstəklənmir, ya da birdən çox səs trekini dəstəkləmir. Zəhmət olmasa, parametrlərinizi yoxlayın və yenidən cəhd edin.\"\nOutput.RecordNoSpace.Title=\"Kifayət qədər disk sahəsi yoxdur\"\nOutput.RecordNoSpace.Msg=\"Qeydə almanı davam etdirmək üçün kifayət qədər disk sahəsi yoxdur.\"\nOutput.RecordError.Title=\"Qeydə alma xətası\"\nOutput.RecordError.Msg=\"Qeydə alma zamanı naməlum xəta baş verdi.\"\nOutput.RecordError.EncodeErrorMsg=\"Qeydə alma zamanı kodlayıcı xətası baş verdi.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Qeydə alma zamanı kodlayıcı xətası baş verdi:<br><br>%1\"\nOutput.BadPath.Title=\"Keçərsiz Fayl Yolu\"\nOutput.BadPath.Text=\"Konfiqurasiya edilmiş Qeyd Yolunu açmaq mümkün olmadı. Zəhmət olmasa, Parametrlər → Çıxış → Qeydə alma bölməsindəki Qeyd Yolunuzu yoxlayın.\"\nOutput.NoBroadcast.Title=\"Heç bir Yayım Konfiqurasiya Edilməyib\"\nOutput.NoBroadcast.Text=\"Yayımı başlatmazdan əvvəl bir yayım konfiqurasiya etməlisiniz.\"\nOutput.BroadcastStartFailed=\"Yayımı başlatmaq mümkün olmadı\"\nOutput.BroadcastStopFailed=\"Yayımı dayandırmaq mümkün olmadı\"\nLogUploadDialog.Title=\"OBS Studio Jurnal Faylı Yüklənməsi\"\nLogUploadDialog.Labels.PrivacyNotice=\"Zəhmət olmasa, hər hansı bir faylı yükləmədən əvvəl <a href='https://obsproject.com/privacy-policy'>Məxfilik Siyasətini</a> və onun fayl yükləmələri ilə bağlı bölməsini oxuyun.\"\nLogUploadDialog.Labels.Progress=\"Jurnal yüklənir. Zəhmət olmasa gözləyin...\"\nLogUploadDialog.Labels.Description.AppLog=\"Jurnal faylınız yükləndi. İndi siz URL-i sazlama və ya dəstək məqsədləri üçün paylaşa bilərsiniz.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Çökmə hesabatınız yükləndi. İndi siz URL-i sazlama məqsədləri üçün paylaşa bilərsiniz.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Yüklə\"\nLogUploadDialog.Buttons.CopyURL=\"Jurnal URL-ni Kopyala\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Jurnal Faylını Təhlil et\"\nLogUploadDialog.Buttons.RetryButton=\"Yenidən cəhd et\"\nLogUploadDialog.Errors.Template=\"Faylı yükləməyə çalışarkən xəta baş verdi:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Yükləmək üçün heçbir bir fayl tapılmadı və ya fayl boş idi.\"\nRemux.SourceFile=\"OBS Qeydi\"\nRemux.TargetFile=\"Hədəf Fayl\"\nRemux.Remux=\"Yenidən Birləşdir\"\nRemux.Stop=\"Yenidən Birləşdirməni Dayandır\"\nRemux.ClearFinished=\"Tamamlanmış Elementləri Təmizlə\"\nRemux.ClearAll=\"Bütün Elementləri Təmizlə\"\nRemux.OBSRecording=\"OBS Qeydi\"\nRemux.FinishedTitle=\"Yenidən birləşdirmə tamamlandı\"\nRemux.Finished=\"Qeyd yenidən birləşdirildi\"\nRemux.FinishedError=\"Qeyd yenidən birləşdirildi, lakin fayl natamam ola bilər\"\nRemux.SelectRecording=\"OBS Qeydini Seç...\"\nRemux.SelectTarget=\"Hədəf faylı seç...\"\nRemux.FileExistsTitle=\"Hədəf fayllar mövcuddur\"\nRemux.FileExists=\"Aşağıdakı hədəf fayllar artıq mövcuddur. Onları əvəz etmək istəyirsiniz?\"\nRemux.ExitUnfinishedTitle=\"Yenidən birləşdirmə gedir\"\nRemux.ExitUnfinished=\"Yenidən birləşdirmə tamamlanmayıb, indi dayandırmaq hədəf faylı yararsız hala gətirə bilər.\\nYenidən birləşdirməni dayandırmaq istədiyinizə əminsiniz?\"\nRemux.HelpText=\"Yenidən birləşdirmək üçün faylları bu pəncərəyə sürüşdürün, və ya fayl axtarmaq üçün boş \\\"OBS Qeydi\\\" xanasını seçin.\"\nRemux.NoFilesAddedTitle=\"Heç bir yenidən birləşdirmə faylı əlavə edilməyib\"\nRemux.NoFilesAdded=\"Yenidən birləşdirmək üçün heç bir fayl əlavə edilməyib. Tərkibində bir və ya daha çox video faylı olan qovluğu əlavə edin.\"\nMissingFiles=\"Çatışmayan Fayllar\"\nMissingFiles.MissingFile=\"Çatışmayan Fayl\"\nMissingFiles.NewFile=\"Yeni Fayl\"\nMissingFiles.HelpText=\"Son OBS istifadənizdən bəri bəzi fayllar çatışmır\"\nMissingFiles.NumFound=\"%2-dən %1 ədədi tapıldı\"\nMissingFiles.Search=\"Qovluqda Axtar...\"\nMissingFiles.SelectFile=\"Fayl seç...\"\nMissingFiles.SelectDir=\"Axtarmaq üçün Qovluq Seçin\"\nMissingFiles.State=\"Vəziyyət\"\nMissingFiles.Missing=\"Çatışmır\"\nMissingFiles.Replaced=\"Əvəz Edildi\"\nMissingFiles.Cleared=\"Təmizləndi\"\nMissingFiles.Found=\"Tapıldı\"\nMissingFiles.AutoSearch=\"Əlavə uyğun fayllar tapıldı\"\nMissingFiles.AutoSearchText=\"OBS bu qovluqda çatışmayan fayllar üçün əlavə uyğunluqlar tapdı. Onları əlavə etmək istəyirsiniz?\"\nMissingFiles.NoMissing.Title=\"Çatışmayan Faylların Yoxlanması\"\nMissingFiles.NoMissing.Text=\"Görünür, çatışmayan fayl yoxdur.\"\nMacPermissions.MenuAction=\"Tətbiq İcazələrini Nəzərdən Keçir...\"\nMacPermissions.Title=\"Tətbiq İcazələrini Nəzərdən Keçir\"\nMacPermissions.Description=\"OBS Studio müəyyən funksiyaları təmin etmək üçün sizin icazənizi tələb edir. Bu icazələri aktiv etmək tövsiyə olunur, lakin onlar tətbiqi istifadə etmək üçün məcburi deyil. Onları hər zaman sonradan aktiv edə bilərsiniz.\"\nMacPermissions.Description.OpenDialog=\"Bu dialoqu OBS Studio menyusu vasitəsilə yenidən aça bilərsiniz.\"\nMacPermissions.AccessGranted=\"Giriş İcazəsi Verildi\"\nMacPermissions.RequestAccess=\"Giriş Tələb Et\"\nMacPermissions.OpenPreferences=\"%1 Seçimlərini Aç\"\nMacPermissions.Item.ScreenRecording=\"Ekran Qeydi\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS ekranınızı yaxalaya bilmək üçün bu icazəni tələb edir.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Bu icazə veb-kamera və ya yaxalama kartından məzmun yaxalamaq üçün tələb olunur.\"\nMacPermissions.Item.Microphone=\"Mİkrofon\"\nMacPermissions.Item.Microphone.Details=\"Əgər mikrofonunuzu və ya xarici səs qurğusunu yaxalamaq istəyirsinizsə, OBS bu icazəni tələb edir.\"\nMacPermissions.Item.InputMonitoring=\"Giriş İzləməsi\"\nMacPermissions.Item.InputMonitoring.Details=\"OBS arxa fonda olarkən qısayol düymələrinin işləməsi üçün bu icazə tələb olunur.\"\nMacPermissions.Item.Accessibility=\"Əlçatanlıq\"\nMacPermissions.Item.Accessibility.Details=\"Köhnə quraşdırmalarda OBS \\\"Giriş İzləməsi\\\" əvəzinə \\\"Əlçatanlıq\\\" bölməsində göstərilə bilər.\"\nMacPermissions.Continue=\"Davam et\"\nSourceLeak.Title=\"Mənbə Təmizləmə Xətası\"\nSourceLeak.Text=\"Səhnə kolleksiyalarını dəyişdirərkən problem yarandı və bəzi mənbələri yaddaşdan çıxarmaq mümkün olmadı. Bu problem adətən resursları düzgün azad etməyən plaginlər tərəfindən yaranır. Zəhmət olmasa, istifadə etdiyiniz bütün plaginlərin güncəl olduğundan əmin olun.\\n\\nMümkün məlumat zədələnməsinin qarşısını almaq üçün OBS Studio indi bağlanacaq.\"\nBasic.DesktopDevice1=\"Masaüstü Səsi\"\nBasic.DesktopDevice2=\"Masaüstü Səsi 2\"\nBasic.Scene=\"Səhnə\"\nBasic.DisplayCapture=\"Ekran Yaxalama\"\nBasic.Main.PreviewConextMenu.Enable=\"Ön Baxışı Aktiv et\"\nBasic.Main.Preview.Disable=\"Ön Baxışı Deaktiv Et\"\nScaleFiltering=\"Miqyas Filtrləməsi\"\nScaleFiltering.Point=\"Nöqtə\"\nScaleFiltering.Bicubic=\"Bikubik\"\nScaleFiltering.Area=\"Sahə\"\nBlendingMethod=\"Qarışdırma Metodu\"\nBlendingMethod.Default=\"İlkin təyin olunan\"\nBlendingMethod.SrgbOff=\"SRGB Deaktiv\"\nBlendingMode=\"Qarışdırma Rejimi\"\nBlendingMode.Additive=\"Əlavə et\"\nBlendingMode.Subtract=\"Çıxma\"\nBlendingMode.Screen=\"Ekran\"\nBlendingMode.Multiply=\"Vurma\"\nBlendingMode.Lighten=\"Açıqlaşdır\"\nBlendingMode.Darken=\"Tündləşdir\"\nDeinterlacing=\"Sətirlərarasını Ləğv Etmə\"\nDeinterlacing.Discard=\"Ləğv et\"\nDeinterlacing.TopFieldFirst=\"Əvvəlcə Üst Sahə\"\nDeinterlacing.BottomFieldFirst=\"Əvvəlcə Alt Sahə\"\nVolControl.SliderUnmuted=\"'%1' üçün səs ucalığı sürgüsü:\"\nVolControl.SliderMuted=\"'%1' üçün səs ucalığı sürgüsü: (hazırda səssizdir)\"\nVolControl.Mute=\" '%1'-i Səssizləşdir\"\nVolControl.Properties=\"'%1' üçün Xüsusiyyətlər\"\nVolControl.UnassignedWarning.Title=\"Təyin Edilməmiş Səs Mənbəyi\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" heç bir səs trekinə təyin edilməyib və yayımlarda və ya qeydə almalarda eşidilən olmayacaq.\\n\\nSəs mənbəyini bir trekə təyin etmək üçün, mikser pəncərəsi alətlər panelindəki sağ klik menyusu və ya dişli çarx düyməsi vasitəsilə Qabaqcıl Səs Xüsusiyyətlərini açın.\"\nBasic.Main.AddSceneDlg.Title=\"Səhnə Əlavə et\"\nBasic.Main.AddSceneDlg.Text=\"Zəhmət olmasa, səhnənin adını daxil edin\"\nBasic.Main.DefaultSceneName.Text=\"Səhnə %1\"\nBasic.Main.AddSceneCollection.Title=\"Səhnə Kolleksiyası Əlavə et\"\nBasic.Main.AddSceneCollection.Text=\"Zəhmət olmasa, səhnə kolleksiyasının adını daxil edin\"\nBasic.Main.RenameSceneCollection.Title=\"Səhnə Kolleksiyasını Yenidən Adlandır\"\nBasic.Main.RemigrateSceneCollection.Title=\"Səhnə Kolleksiyasının Rezolyusiyasını Yenilə\"\nBasic.Main.RemigrateSceneCollection.Text=\"\\\"%1\\\" səhnə kolleksiyasının rezolyusiyasını cari profilin canvas rezolyusiyası olan %2x%3 ilə uyğunlaşdırmaq üçün yeniləmək istəyirsiniz?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Çıxışlar aktiv olduqda səhnə kolleksiyasının rezolyusiyasını yeniləmək mümkün deyil.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Səhnə kolleksiyasının rezolyusiyasını yeniləmək mümkün olmadı. Orijinal rezolyusiya məlum deyil.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Sıfırlama mümkün deyil: OBS rezolyusiyasını dəyişmək mümkün olmadı.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Sıfırlama mümkün deyil: Cari rezolyusiya artıq səhnə kolleksiyasının əsas rezolyusiyasıdır.\"\nAddProfile.Title=\"Profil Əlavə et\"\nAddProfile.Text=\"Zəhmət olmasa, profilin adını daxil edin\"\nAddProfile.WizardCheckbox=\"Avtomatik konfiqurasiya sehrbazını göstər\"\nRenameProfile.Title=\"Profili Yenidən Adlandır\"\nBasic.Main.MixerRename.Title=\"Səs Mənbəsini Yenidən Adlandır\"\nBasic.Main.MixerRename.Text=\"Zəhmət olmasa, səs mənbəsinin adını daxil edin\"\nBasic.Main.PreviewDisabled=\"Ön baxış hazırda deaktivdir\"\nBasic.SourceSelect=\"Mənbə Yarat/Seç \"\nBasic.SourceSelect.CreateNew=\"Yenisini yarat\"\nBasic.SourceSelect.AddExisting=\"Mövcud Olanı Əlavə et\"\nBasic.SourceSelect.AddVisible=\"Mənbəni görünən et\"\nBasic.Main.Sources.Visibility=\"Görünmə\"\nBasic.Main.Sources.VisibilityDescription=\"Canvas-da '%1'-in görünməsini idarə edir\"\nBasic.Main.Sources.Lock=\"Kilidlə\"\nBasic.Main.Sources.LockDescription=\"Canvas-da '%1'-in mövqeyini və miqyasını kilidləyir\"\nBasic.PropertiesWindow=\"'%1' üçün Xüsusiyyətlər\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (avtoseçim:%2)\"\nBasic.PropertiesWindow.SelectColor=\"Rəng seç\"\nBasic.PropertiesWindow.SelectFont=\"Şirft seç\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Bir Şrift Seç\"\nBasic.PropertiesWindow.ConfirmTitle=\"Parametrlər Dəyişdi\"\nBasic.PropertiesWindow.Confirm=\"Yadda saxlanılmayan dəyişikliklər var. Onları saxlamaq istəyirsiniz?\"\nBasic.PropertiesWindow.NoProperties=\"Heç bir xüsusiyyət mövcud deyil\"\nBasic.PropertiesWindow.AddFiles=\"Fayllar Əlavə et\"\nBasic.PropertiesWindow.AddDir=\"Qovluq Əlavə et\"\nBasic.PropertiesWindow.AddURL=\"Yol/URL Əlavə et\"\nBasic.PropertiesWindow.AddEditableListDir=\"'%1'-ə qovluq əlavə et\"\nBasic.PropertiesWindow.AddEditableListFiles=\"'%1'-ə fayllar əlavə et\"\nBasic.PropertiesWindow.AddEditableListEntry=\"'%1' siyahısına qeyd əlavə et\"\nBasic.PropertiesWindow.EditEditableListEntry=\"'%1' siyahısındakı qeydə düzəliş et\"\nBasic.PropertiesView.FPS.Simple=\"Sadə FPS Dəyərləri\"\nBasic.PropertiesView.FPS.Rational=\"Rasional FPS Dəyərləri\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Keçərli FPS Aralıqları:\"\nBasic.PropertiesView.UrlButton.Text=\"Bu link standart veb brauzerinizdə açılsın?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"URL-ni aç\"\nBasic.InteractionWindow=\"'%1' ilə Qarşılıqlı Əlaqə\"\nBasic.StatusBar.Reconnecting=\"Bağlantı kəsildi, %2 saniyə sonra yenidən qoşulur (cəhd %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Yenidən qoşulmağa cəhd edilir... (cəhd %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Yenidən qoşulma uğurlu oldu\"\nBasic.StatusBar.Delay=\"Gecikmə (%1 san)\"\nBasic.StatusBar.DelayStartingIn=\"Gecikmə (%1 saniyədə başlayır)\"\nBasic.StatusBar.DelayStoppingIn=\"Gecikmə (%1 saniyədə dayanır)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Gecikmə (%1 saniyədə dayanır,%2 saniyədə başlayır)\"\nBasic.StatusBar.RecordingSavedTo=\"Qeyd Alma '%1' ünvanında saxlandı\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Təkrar oxutma buferi '%1' ünvanında saxlandı\"\nBasic.StatusBar.ScreenshotSavedTo=\"Ekran görüntüsü '%1' ünvanında saxlandı\"\nBasic.StatusBar.AutoRemuxedTo=\"Qeyd Alma avtomatik olaraq '%1' ünvanında yenidən birləşdirildi\"\nBasic.Filters=\"Filtrlər\"\nBasic.Filters.AsyncFilters=\"Səs/Video Filtrləri\"\nBasic.Filters.AudioFilters=\"Səs Filtrləri\"\nBasic.Filters.EffectFilters=\"Effekt Filtrləri\"\nBasic.Filters.Title=\"'%1' üçün Filtrlər\"\nBasic.Filters.AddFilter.Title=\"Filtr adı\"\nBasic.Filters.AddFilter.Text=\"Zəhmət olmasa, filtrin adını daxil edin\"\nBasic.TransformWindow=\"Səhnə Elementinin Çevrilməsi\"\nBasic.TransformWindow.Position=\"Mövqe\"\nBasic.TransformWindow.PositionX=\"X Mövqeyi\"\nBasic.TransformWindow.PositionY=\"Y Mövqeyi\"\nBasic.TransformWindow.Rotation=\"Fırlanma\"\nBasic.TransformWindow.Size=\"Ölçü\"\nBasic.TransformWindow.Width=\"En\"\nBasic.TransformWindow.Height=\"Hündürlük\"\nBasic.TransformWindow.Alignment=\"Mövqeyə görə Düzlənmə\"\nBasic.TransformWindow.BoundsType=\"Məhdudlaşdırıcı Qutu Növü\"\nBasic.TransformWindow.BoundsAlignment=\"Məhdudlaşdırıcı Qutuda Düzlənmə\"\nBasic.TransformWindow.Bounds=\"Məhdudlaşdırıcı Qutu Ölçüsü\"\nBasic.TransformWindow.BoundsWidth=\"Məhdudlaşdırıcı Qutu Eni\"\nBasic.TransformWindow.BoundsHeight=\"Məhdudlaşdırıcı Qutu Hündürlüyü\"\nBasic.TransformWindow.CropToBounds=\"Məhdudlaşdırıcı Qutuya Görə Kəs\"\nBasic.TransformWindow.Crop=\"Kəs\"\nBasic.TransformWindow.CropLeft=\"Soldan Kəs\"\nBasic.TransformWindow.CropRight=\"Sağdan Kəs\"\nBasic.TransformWindow.CropTop=\"Üstdən Kəs\"\nBasic.TransformWindow.CropBottom=\"Altdan Kəs\"\nBasic.TransformWindow.Alignment.TopLeft=\"Yuxarı Sol\"\nBasic.TransformWindow.Alignment.TopCenter=\"Yuxarı Mərkəz\"\nBasic.TransformWindow.Alignment.TopRight=\"Yuxarı Sağ\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Mərkəz Sol\"\nBasic.TransformWindow.Alignment.Center=\"Mərkəz\"\nBasic.TransformWindow.Alignment.CenterRight=\"Mərkəz Sağ\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Aşağı Sol\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Aşağı Mərkəz\"\nBasic.TransformWindow.Alignment.BottomRight=\"Aşağı Sağ\"\nBasic.TransformWindow.BoundsType.None=\"Sərhəd yoxdur\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Yalnız maksimum ölçü\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Daxili sərhədlərə görə miqyaslandır\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Xarici sərhədlərə görə miqyaslandır\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Sərhədlərin eninə görə miqyaslandır\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Sərhədlərin hündürlüyünə görə miqyaslandır\"\nBasic.TransformWindow.BoundsType.Stretch=\"Sərhədlərə dart\"\nBasic.TransformWindow.Title=\"'%1' üçün Çevirməni Redaktə et\"\nBasic.TransformWindow.NoSelectedSource=\"Heç bir mənbə seçilməyib\"\nBasic.Main.AddSourceHelp.Title=\"Mənbə Əlavə Etmək Mümkün Deyil\"\nBasic.Main.AddSourceHelp.Text=\"Mənbə əlavə etmək üçün ən azı 1 səhnəniz olmalıdır.\"\nBasic.Main.Scenes=\"Səhnələr\"\nBasic.Main.Sources=\"Mənbələr\"\nBasic.Main.Source=\"Mənbə\"\nBasic.Main.Controls=\"Nəzarət Paneli\"\nBasic.Main.PreparingStream=\"Hazırlanır...\"\nBasic.Main.Connecting=\"Qoşulur...\"\nBasic.Main.StartRecording=\"Qeydə Almanı Başlat\"\nBasic.Main.StartReplayBuffer=\"Təkrar Oxutma Buferini Başlat\"\nBasic.Main.SaveReplay=\"Təkrar Oxutmanı Saxla\"\nBasic.Main.StartStreaming=\"Yayımı Başlat\"\nBasic.Main.StartBroadcast=\"Canlı Yayıma Keç\"\nBasic.Main.StartVirtualCam=\"Virtual Kameranı Başlat\"\nBasic.Main.StopRecording=\"Qeydə Almanı Dayandır\"\nBasic.Main.PauseRecording=\"Qeydə Almağa Fasilə ver\"\nBasic.Main.UnpauseRecording=\"Qeydə Almağa Davam et\"\nBasic.Main.SplitFile=\"Qeyd Faylını Böl\"\nBasic.Main.AddChapterMarker=\"Fəsil İşarəsi Əlavə et (yalnız Hibrid MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Qeydə Alma Dayandırılır...\"\nBasic.Main.StopReplayBuffer=\"Təkrar Oxutma Buferini Dayandır\"\nBasic.Main.StoppingReplayBuffer=\"Təkrar Oxutma Buferi Dayandırılır...\"\nBasic.Main.SetupBroadcast=\"Yayımı İdarə et\"\nBasic.Main.StopStreaming=\"Yayımı Dayandır\"\nBasic.Main.StopBroadcast=\"Yayımı Bitir\"\nBasic.Main.AutoStopEnabled=\"(Avto Dayandırma)\"\nBasic.Main.StoppingStreaming=\"Yayım Dayandırılır...\"\nBasic.Main.ForceStopStreaming=\"Yayımı Dayandır (gecikməni ləğv et)\"\nBasic.Main.ShowContextBar=\"Mənbə Alətlər Panelini Göstər\"\nBasic.Main.HideContextBar=\"Mənbə Alətlər Panelini Gizlət\"\nBasic.Main.StopVirtualCam=\"Virtual Kameranı Dayandır\"\nBasic.Main.Group=\"Qrup %1\"\nBasic.Main.GroupItems=\"Seçilmiş Elementləri Qruplaşdır\"\nBasic.Main.Ungroup=\"Qrupu Ləğv Et\"\nBasic.Main.GridMode=\"Tor Rejimi\"\nBasic.Main.ListMode=\"Siyahı Rejimi\"\nBasic.Main.VirtualCamConfig=\"Virtual Kameranı Konfiqurasiya et\"\nBasic.VCam.VirtualCamera=\"Virtual Kamera\"\nBasic.VCam.OutputType=\"Çıxış Növü\"\nBasic.VCam.OutputSelection=\"Çıxış Seçimi\"\nBasic.VCam.OutputType.Program=\"Proqram (İlkin Təyin olunan)\"\nBasic.VCam.OutputSelection.NoSelection=\"Bu çıxış növü üçün seçim yoxdur\"\nBasic.VCam.RestartWarning=\"Bu dəyişikliyi tətbiq etmək üçün virtual kamera yenidən başladılacaq\"\nBasic.MainMenu.File=\"&Fayl\"\nBasic.MainMenu.File.Export=\"&İxrac et\"\nBasic.MainMenu.File.Import=\"&İdxal et\"\nBasic.MainMenu.File.ShowRecordings=\"&Qeydə alınmışları Göstər\"\nBasic.MainMenu.File.Remux=\"&Qeydə Alınmışları Yenidən Birləşdir\"\nBasic.MainMenu.File.Settings=\"&Parametrlər\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Parametrlər Qovluğunu Göstər\"\nBasic.MainMenu.File.ShowProfileFolder=\"Profil Qovluğunu Göstər\"\nBasic.MainMenu.File.ShowMissingFiles=\"İtmiş Faylları Yoxla\"\nBasic.MainMenu.File.Exit=\"Çı&x\"\nBasic.MainMenu.Edit=\"&Redaktə et\"\nBasic.MainMenu.Edit.LockPreview=\"&Önizləməni Kilidlə\"\nBasic.MainMenu.Edit.Scale=\"Önizləmə &Miqyası\"\nBasic.MainMenu.Edit.Scale.Window=\"Pəncərəyə Uyğun Miqyaslandır\"\nBasic.MainMenu.Edit.Scale.Output=\"Çıxış (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Miqyaslandırılıb (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Yaxınlaşdır\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Uzaqlaşdır\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Miqyası Sıfırla\"\nBasic.MainMenu.Edit.Transform=\"&Çevir\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Çevirməni Redaktə et\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Çevrilməni Kopyala\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Çevrilməni Yapışdır\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Çevrilməni Sıfırla\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"90 dərəcə saat əqrəbi istiqamətində fırlat\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"90 dərəcə saat əqrəbinin əksinə fırlat\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180 dərəcə fırlat\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"&Üfüqi Çevir\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"&Şaquli Çevir\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"&Ekrana Sığdır\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&Ekrana Dart\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Ekranda Mərkəzləşdir\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Şaquli Mərkəzləşdir\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Üfiqi Mərkəzləşdir\"\nBasic.MainMenu.Edit.Order=\"&Sıralama\"\nBasic.MainMenu.Edit.Order.MoveUp=\"&Yuxarı Keçir\"\nBasic.MainMenu.Edit.Order.MoveDown=\"&Aşağı Keçir\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"&Ən Yuxarıya Keçir\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"&Ən Alta Keçir\"\nBasic.MainMenu.Edit.AdvAudio=\"&Qabaqcıl Səs Xüsusiyyətləri\"\nBasic.MainMenu.View=\"&Görünüş\"\nBasic.MainMenu.View.Toolbars=\"&Alətlər Paneli\"\nBasic.MainMenu.View.ListboxToolbars=\"Pəncərə Alətlər Panelləri\"\nBasic.MainMenu.View.ContextBar=\"Mənbə Alətlər Paneli\"\nBasic.MainMenu.View.SourceIcons=\"Mənbə &İkonları\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Tamekran İnterfeys\"\nBasic.MainMenu.View.ResetUI=\"UI-ı &Sıfırla\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Həmişə Üstə\"\nBasic.MainMenu.View.SceneListMode=\"Səhnə Siyahısı Rejimi\"\nBasic.MainMenu.Docks=\"&Pəncərələr\"\nBasic.MainMenu.Docks.ResetDocks=\"&Pəncərələri Sıfırla\"\nBasic.MainMenu.Docks.LockDocks=\"&Pəncərələri Kilidlə\"\nBasic.MainMenu.Docks.SideDocks=\"&Tam Hündürlüklü Pəncərələr\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Fərdi Brauzer Pəncərələri\"\nBasic.MainMenu.Import=\"İdxal et...\"\nBasic.MainMenu.Export=\"İxrac et...\"\nBasic.MainMenu.SceneCollection=\"&Səhnə Kolleksiyası\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Profili İdxal Et\"\nBasic.MainMenu.Profile.Export=\"Profili İxrac Et\"\nBasic.MainMenu.SceneCollection.Import=\"Səhnə Kolleksiyası İdxal et\"\nBasic.MainMenu.SceneCollection.Export=\"Səhnə Kolleksiyasını İxrac et\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Əsas Rezolyusiyanı Sıfırla\"\nBasic.MainMenu.SceneCollection.Migrate=\"Əsas Rezolyusiyanı Təyin et\"\nBasic.MainMenu.Profile.Exists=\"Profil artıq mövcuddur\"\nBasic.MainMenu.Tools=\"&Alətlər\"\nBasic.MainMenu.Help=\"&Kömək\"\nBasic.MainMenu.Help.HelpPortal=\"Kömək &Portalı\"\nBasic.MainMenu.Help.Website=\"&Vebsayta Keçid et\"\nBasic.MainMenu.Help.Discord=\"&Discord Serverinə Qoşul\"\nBasic.MainMenu.Help.WhatsNew=\"Yeniliklər\"\nBasic.MainMenu.Help.Logs=\"&Jurnal Faylları\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"&Jurnal Fayllarını Göstər\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"&Cari Jurnal Faylını Yüklə\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"&Əvvəlki Jurnal Faylını Yüklə\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Cari Jurnala Bax\"\nBasic.MainMenu.Help.ReleaseNotes=\"Buraxılış Qeydləri\"\nBasic.MainMenu.Help.CheckForUpdates=\"Güncəlləmələri Yoxla\"\nBasic.MainMenu.Help.Repair=\"Fayl Bütövlüyünü Yoxla\"\nBasic.MainMenu.Help.RestartSafeMode=\"Təhlükəsiz Rejimdə Yenidən Başlat\"\nBasic.MainMenu.Help.RestartNormal=\"Normal Rejimdə Yenidən Başlat\"\nBasic.MainMenu.Help.CrashLogs=\"&Çökmə Hesabatları\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"&Çökmə Hesabatlarını Göstər\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"&Əvvəlki Çökmə Hesabatını Yüklə\"\nBasic.MainMenu.Help.About=\"&Haqqında\"\nBasic.Settings.ProgramRestart=\"Bu parametrlərin qüvvəyə minməsi üçün proqram yenidən başladılmalıdır.\"\nBasic.Settings.ConfirmTitle=\"Dəyişiklikləri Təsdiqlə\"\nBasic.Settings.Confirm=\"Yaddaşa verilməmiş dəyişiklikləriniz var. Dəyişikliklər saxlanılsın?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 bəzi yayım parametrlərinizə nəzarət edir\"\nBasic.Settings.General=\"Ümumi\"\nBasic.Settings.General.Language=\"Dil\"\nBasic.Settings.General.Updater=\"Güncəlləmələr\"\nBasic.Settings.General.UpdateChannel=\"Güncəlləmə Kanalı\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Deaktiv)\"\nBasic.Settings.General.UpdateChannelDefault=\"(İlkin təyin olunan)\"\nBasic.Settings.General.EnableAutoUpdates=\"Başlanğıcda güncəlləmələri avtomatik yoxla\"\nBasic.Settings.General.OpenStatsOnStartup=\"Başlanğıcda statistika dialoqunu aç\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"OBS pəncərələrini ekran yaxalamasından gizlət\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Bu parametr bütün proyektor olmayan OBS Studio pəncərələrinin OBS tərəfindən yaxalanmasını gizlədəcək və konfrans, \\nekran paylaşımı, uzaqdan dəstək, ekran görüntüləri və digər yaxalama proqramları kimi digər tətbiqlərə təsir edəcək.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Bu seçimi aktiv etmək bütün proyektor olmayan OBS Studio pəncərələrinin OBS tərəfindən yaxalanmasını gizlədir və konfrans, ekran paylaşımı, uzaqdan dəstək, ekran görüntüləri və digər yaxalama proqramları kimi digər tətbiqlərə təsir edir.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Yayımları başlatdıqda təsdiq dialoqunu göstər\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Yayımları dayandırdıqda təsdiq dialoqunu göstərin\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Qeydə almanı dayandırdıqda təsdiq dialoqunu göstər\"\nBasic.Settings.General.Projectors=\"Proyektorlar\"\nBasic.Settings.General.HideProjectorCursor=\"Kursoru proyektorlar üzərində gizlət\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Proyektorları həmişə üstə tut\"\nBasic.Settings.General.Snapping=\"Mənbə Düzlənmə Yapışması\"\nBasic.Settings.General.ScreenSnapping=\"Mənbələri ekranın kənarına yapışdır\"\nBasic.Settings.General.CenterSnapping=\"Mənbələri üfüqi və şaquli mərkəzə yapışdır\"\nBasic.Settings.General.SourceSnapping=\"Mənbələri digər mənbələrə yapışdır\"\nBasic.Settings.General.SnapDistance=\"Yapışma Həssaslığı\"\nBasic.Settings.General.SpacingHelpers=\"Piksel düzlənmə bələdçilərini göstər\"\nBasic.Settings.General.RecordWhenStreaming=\"Yayım zamanı avtomatik qeydə al\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Yayım dayandıqda qeydə almanı davam etdir\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Yayım zamanı təkrar buferini avtomatik başlat\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Yayım dayandıqda təkrar buferini aktiv saxla\"\nBasic.Settings.General.SysTray=\"Sistem Bildiriş Sahəsi\"\nBasic.Settings.General.SysTrayWhenStarted=\"Başladıqda bildiriş sahəsinə kiçilt\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Həmişə tapşırıqlar zolağı əvəzinə bildiriş sahəsinə kiçilt\"\nBasic.Settings.General.SaveProjectors=\"Çıxışda proyektorları saxla\"\nBasic.Settings.General.CloseExistingProjectors=\"Hər ekranda yalnız bir tam ekranlı proyektora məhdudlaşdır\"\nBasic.Settings.General.Preview=\"Önbaxış\"\nBasic.Settings.General.OverflowHidden=\"Daşmanı Gizlət\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Daşma həmişə görünür\"\nBasic.Settings.General.OverflowSelectionHidden=\"Mənbə görünməz olduqda belə daşmanı göstər\"\nBasic.Settings.General.Importers=\"İdxalçılar\"\nBasic.Settings.General.AutomaticCollectionSearch=\"İdxal edərkən səhnə kolleksiyaları üçün məlum yerlərdə axtar\"\nBasic.Settings.General.SwitchOnDoubleClick=\"İki dəfə kliklədikdə səhnəyə keçid et\"\nBasic.Settings.General.StudioPortraitLayout=\"Portret/şaquli tərtibatı aktiv et\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Ön baxış/proqram etiketlərini göstər\"\nBasic.Settings.General.Multiview=\"Çoxlu Görünüş\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Səhnələr arasında keçid etmək üçün klikləyin\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Səhnə adlarını göstər\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Təhlükəsiz sahələri çək (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Çoxlu Görünüş Tərtibatı\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Üfüqi, Yuxarı (8 Səhnə)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Üfüqi, Aşağı (8 Səhnə)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Şaquli, Sol (8 Səhnə)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Şaquli, Sağ (8 Sәhnә)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Üfüqi, Yuxarı (18 Səhnə)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Üfüqi, Yuxarı (24 Səhnə)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Yalnız Səhnələr (4 Səhnə)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Yalnız Səhnələr (9 Səhnə)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Yalnız Səhnələr (16 Səhnə)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Yalnız Səhnələr (25 Səhnə)\"\nBasic.Settings.General.ChannelName.stable=\"Stabil\"\nBasic.Settings.General.ChannelDescription.stable=\"Ən son stabil buraxılış\"\nBasic.Settings.General.ChannelName.beta=\"Betalar / Buraxılış Namizədləri\"\nBasic.Settings.General.ChannelDescription.beta=\"Potensial olaraq qeyri-sabit ilkin buraxılış versiyaları\"\nBasic.Settings.Appearance=\"Görünüş\"\nBasic.Settings.Appearance.General=\"Ümumi\"\nBasic.Settings.Appearance.General.Theme=\"Mövzu\"\nBasic.Settings.Appearance.General.Variant=\"Stil\"\nBasic.Settings.Appearance.General.NoVariant=\"Heç Bir Stil Mövcud Deyil\"\nBasic.Settings.Appearance.FontScale=\"Şrift Ölçüsü\"\nBasic.Settings.Appearance.Density=\"Sıxlıq\"\nBasic.Settings.Appearance.Classic=\"Klassik\"\nBasic.Settings.Appearance.Compact=\"Kompakt\"\nBasic.Settings.Appearance.Comfortable=\"Rahat \"\nBasic.Settings.Appearance.OptionsWarning=\"Bəzi görünüş seçimləri bu stil üçün mövcud deyil.\"\nBasic.Settings.Stream=\"Yayım\"\nBasic.Settings.Stream.Destination=\"Təyinat Yeri\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Autentifikasiya istifadə et\"\nBasic.Settings.Stream.Custom.Username=\"İstifadəçi Adı\"\nBasic.Settings.Stream.Custom.Password=\"Şifrə\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: srp_username daxil edin.\\nRTMP: istifadəçi adını daxil edin.\\nSRT: istifadə edilmir.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: srp_password daxil edin.\\nRTMP: şifrəni daxil edin.\\nSRT: şifrələmə parolasını daxil edin.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Bağlantı Sürəti Test Rejimini Aktiv et\"\nBasic.Settings.Stream.TTVAddon=\"Twitch Söhbət Əlavələri\"\nBasic.Settings.Stream.TTVAddon.None=\"Heç biri\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV və FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Çatışmayan Yayım Quraşdırması\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Parametrləri Açın\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL və Yayım Açarınız çatışmır.\\n\\n'Yayım' tab-ında URL və Yayım Açarını daxil etmək üçün parametrləri açın.\"\nBasic.Settings.Stream.MissingUrl=\"Yayım URL-i çatışmır.\\n\\n'Yayım' tab-ında URL-i daxil etmək üçün parametrləri açın.\"\nBasic.Settings.Stream.MissingStreamKey=\"Yayım açarı çatışmır.\\n\\n'Yayım' tab-ında yayım açarını daxil etmək üçün parametrləri açın.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Yayım xidməti parametr tövsiyələrinə məhəl qoyma\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Tövsiyə Olunan Parametrləri Ləğv et\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Xəbərdarlıq: Xidmətin məhdudiyyətlərinə məhəl qoymamaq yayım keyfiyyətinin pisləşməsinə və ya yayımlamağınıza mane ola bilər.\\n\\nDavam edilsin?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maksimum Video Bit Sürəti: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maksimum Səs Bit Sürəti: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksimum Rezolyusiya: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksimum FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Fərdi Server Təyin et...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Fərdi Server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1-i Aktiv Et\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maksimum Yayım Bağlantı Sürəti\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Avto\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maksimum Video Trekləri\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Avto\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Yayımın FLV-yə yazılmasını aktiv et (sadə qeyd faylı parametrlərindən istifadə edir)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Konfiqurasiya Ləğvi (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Konfiqurasiya Ləğvini Aktiv et\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Çoxtrekli Video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Əlavə Canvas\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Sinxron Yayım\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Sinxron yayım bir neçə video keyfiyyətini kodlaşdırmağa və göndərməyə imkan verir. <a href='https://obsproject.com/kb/whip-streaming-guide'>Ətraflı</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Ümumi Qatlar\"\nBasic.Settings.Stream.AdvancedOptions=\"Qabaqcıl Seçimlər\"\nBasic.Settings.Output=\"Çıxış\"\nBasic.Settings.Output.Format=\"Qeyd Alma Formatı\"\nBasic.Settings.Output.Format.hMP4=\"Hibrid MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hibrid MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Fraqmentli MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fraqmentli MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fraqmentli MOV qeydi hissə-hissə yazır və ənənəvi MOV faylları kimi yekunlaşdırma tələb etmir.\\nBu, diskin yazılması kəsilsə belə, məsələn, BSOD və ya elektrik enerjisinin kəsilməsi nəticəsində faylın oxunaqlı qalmasını təmin edir.\\n\\nBu, bütün pleyerlər və redaktorlarla uyğun olmaya bilər. Lazım gələrsə, faylı daha uyğun bir formata çevirmək üçün Fayl → Qeydləri Yenidən Birləşdir menyusundan istifadə edin.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fraqmentli MP4 qeydi hissə-hissə yazır və ənənəvi MP4 faylları kimi yekunlaşdırma tələb etmir.\\nBu, diskin yazılması kəsilsə belə, məsələn, BSOD və ya elektrik enerjisinin kəsilməsi nəticəsində faylın oxunaqlı qalmasını təmin edir.\\n\\nBu, bütün pleyerlər və redaktorlarla uyğun olmaya bilər. Lazım gələrsə, faylı daha uyğun bir formata çevirmək üçün Fayl → Qeydləri Yenidən Birləşdir menyusundan istifadə edin.\"\nBasic.Settings.Output.Encoder.Video=\"Video Kodlayıcı\"\nBasic.Settings.Output.Encoder.Audio=\"Səs Kodlayıcı\"\nBasic.Settings.Output.SelectDirectory=\"Qeyd Qovluğunu Seç\"\nBasic.Settings.Output.DynamicBitrate=\"Tıxacları idarə etmək üçün bitreyti dinamik olaraq dəyişdirin\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Tıxacları idarə etmək üçün bitreyti dinamik olaraq dəyişdirin (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Tıxacları azaltmaq üçün kadrları buraxmaq əvəzinə, bitreyti anında dinamik olaraq dəyişdirir.\\n\\nNəzərə alın ki, əhəmiyyətli dərəcədə ani tıxac yaranarsa, bu, izləyicilər üçün gecikməni artıra bilər.\\nBitreyt aşağı düşdükdə, onun bərpası bir neçə dəqiqə çəkə bilər.\\n\\nHazırda yalnız RTMP üçün dəstəklənir.\"\nBasic.Settings.Output.Mode=\"Çıxış Rejimi\"\nBasic.Settings.Output.Mode.Simple=\"Sadə\"\nBasic.Settings.Output.Mode.Adv=\"Qabaqcıl\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg Çıxışı\"\nBasic.Settings.Output.UseReplayBuffer=\"Təkrar Oxutma Buferini Aktiv et\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maksimum Təkrar Vaxtı\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maksimum Yaddaş\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Təxmini yaddaş istifadəsi: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Xəbərdarlıq: Təxmini yaddaş istifadəsi olan %1 MiB, tövsiyə olunan maksimum %2 MiB-dən çoxdur\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Yaddaş istifadəsini təxmin etmək mümkün deyil. Zəhmət olmasa, maksimum yaddaş limitini təyin edin.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Təkrar Oxutma Buferi Fayl Adı Prefiksi\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Suffiks\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Qeydə Alma növü Xüsusi Çıxış (FFmpeg) olaraq təyin edildikdə Təkrar Oxutma Buferindən istifadə etmək mümkün deyil.\"\nBasic.Settings.Output.Simple.SavePath=\"Qeyd Yolu\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Qeyd Alma Keyfiyyəti\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Yayımla eyni\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Yüksək Keyfiyyət, Orta Fayl Ölçüsü\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Fərqləndirilməz Keyfiyyət, Böyük Fayl Ölçüsü\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"İtkisiz Keyfiyyət, Çox Böyük Fayl Ölçüsü\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Xəbərdarlıq: Yayım video bit sürəti %1 olaraq təyin ediləcək ki, bu da cari yayım xidməti üçün yuxarı limitdir.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Xəbərdarlıq: Yayım səs bit sürəti %1 olaraq təyin ediləcək ki, bu da cari yayım xidməti üçün yuxarı limitdir.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Xəbərdarlıq: Əgər qeyd keyfiyyəti \\\"Yayımla eyni\\\" olaraq təyin edilibsə, qeydlərə fasilə vermək mümkün deyil.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Xəbərdarlıq: Hazırda seçilmiş qeyd formatı seçilmiş yayım kodlayıcı(lar)ı ilə uyğun deyil.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Xəbərdarlıq: Proqram kodlayıcısı ilə yayımdan fərqli keyfiyyətdə qeydə almaq, eyni anda həm yayımlayıb həm də qeydə alırsınızsa, əlavə CPU istifadəsi tələb edəcək.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Xəbərdarlıq: İtkisiz keyfiyyət çox böyük fayl ölçüləri yaradır! İtkisiz keyfiyyət yüksək rezolyusiya və kadr tezliklərində dəqiqədə 7 giqabaytdan çox disk sahəsi istifadə edə bilər. Əgər çox böyük miqdarda disk sahəniz yoxdursa, itkisiz keyfiyyət uzun qeydlər üçün tövsiyə edilmir. İtkisiz keyfiyyət istifadə edildikdə təkrar oxutma buferi əlçatan deyil.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"İtkisiz keyfiyyət istifadə etmək istədiyinizə əminsiniz?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"İtkisiz keyfiyyət xəbərdarlığı!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Proqram təminatı (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Aparat təminatı (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Aparat təminatı (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Aparat təminatı (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Aparat təminatı (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Aparat təminatı (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Aparat təminatı (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Aparat təminatı (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Aparat təminatı (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Aparat təminatı (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Aparat təminatı (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Proqram təminatı (x264 aşağı CPU istifadəli ilkin sazlama, fayl ölçüsünü artırır)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (İlkin təyin olunan)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD Treki (Trek 2-ni istifadə edir)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Səs Treki\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Uyğunsuz Rezolyusiya/Kadr Tezliyi\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Bu yayım xidməti cari çıxış rezolyusiyanızı və/və ya kadr tezliyinizi dəstəkləmir. Onlar ən yaxın uyğun dəyərə dəyişdiriləcək:\\n\\n%1\\n\\nDavam etmək istəyirsiniz?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Rezolyusiya: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Uyğunsuz Kodlayıcı\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"\\\"%1\\\" yayım xidməti \\\"%2\\\" kodlayıcısını dəstəkləmir. Kodlayıcı \\\"%3\\\"-ə dəyişdiriləcək.\\n\\nDavam etmək istəyirsiniz?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"\\\"%1\\\" yayım xidməti \\\"%2\\\" və \\\"%3\\\" kodlayıcılarını dəstəkləmir. Bu kodlayıcılar \\\"%4\\\" və \\\"%5\\\"-ə dəyişdiriləcək.\\n\\nDavam etmək istəyirsiniz?\"\nBasic.Settings.Output.VideoBitrate=\"Video Bit Sürəti\"\nBasic.Settings.Output.AudioBitrate=\"Səs Bit Sürəti\"\nBasic.Settings.Output.Reconnect=\"Avtomatik Yenidən Qoşul\"\nBasic.Settings.Output.RetryDelay=\"Yenidən Cəhd Gecikməsi\"\nBasic.Settings.Output.MaxRetries=\"Maksimum Yenidən Cəhdlər\"\nBasic.Settings.Output.Advanced=\"Xüsusi Kodlayıcı Parametrlərini Aktiv Et (Qabaqcıl)\"\nBasic.Settings.Output.EncoderPreset=\"Kodlayıcı İlkin Sazlaması\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (aşağı CPU istifadəsi, ən aşağı keyfiyyət)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (ilkin təyin olunan) (orta CPU istifadəsi, standart keyfiyyət)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (yüksək CPU istifadəsi, yüksək keyfiyyət)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Fərdi Kodlayıcı Parametrləri\"\nBasic.Settings.Output.CustomMuxerSettings=\"Fərdi Birləşdirici Parametrləri\"\nBasic.Settings.Output.NoSpaceFileName=\"Boşluq Olmadan Fayl Adı Yarat\"\nBasic.Settings.Output.Adv.Rescale=\"Çıxışı Yenidən Miqyaslandır\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Deaktiv\"\nBasic.Settings.Output.Adv.AudioTrack=\"Səs Treki\"\nBasic.Settings.Output.Adv.Streaming=\"Yayım\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Yayım Parametrləri\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Trek 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Trek 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Trek 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Trek 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Trek 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Trek 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD Treki\"\nBasic.Settings.Output.Adv.Encoder=\"Kodlayıcı Parametrləri\"\nBasic.Settings.Output.Adv.Recording=\"Qeydə Alma\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Qeydə Alma Parametrləri\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Qeydə Alma Növü\"\nBasic.Settings.Output.Adv.Recording.Type=\"Növ\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standart\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Fərdi Çıxış (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Yayım kodlayıcısından istifadə et)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Fayl Adı Formatlaşdırması\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Fayl mövcuddursa, üzərinə yaz\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Fərdi Çıxış (FFmpeg) heç bir qorunma tədbiri olmadan təmin edilir.\\n\\\"Standart\\\" qeydə alma növündən istifadə etməyi düşünün.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg Çıxış Növü\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"URL-ə Çıxış\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Fayla Çıxış\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Geniş yayılmış qeydə alma formatları\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Bütün Fayllar\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Fayl yolu və ya URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Konteyner Formatı\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Səs\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"İlkin Təyin Olunan Format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Konteyner Formatının Təsviri\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Fayl yolu və ya URL-dən təxmin edilən Audio/Video Kodeki\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"İlkin Təyin Olunan Kodlayıcı \"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Kodlayıcını Deaktiv et\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Video Kodlayıcı\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Video Kodlayıcı Parametrləri (əgər varsa)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Səs Kodlayıcı\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Səs Kodlayıcı Parametrləri (əgər varsa)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Birləşdirici Parametrləri (əgər varsa)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Açar Kadr intervalı (kadrlar)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Bütün kodekləri göstər (potensial olaraq uyğunsuz olsalar belə)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg Parametrləri\"\nBasic.Settings.Output.EnableSplitFile=\"Avtomatik Fayl Bölünməsi\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Vaxta görə böl\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Ölçüyə görə böl\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Yalnız əl ilə böl\"\nBasic.Settings.Output.SplitFile.Time=\"Bölmə Vaxtı\"\nBasic.Settings.Output.SplitFile.Size=\"Bölmə Ölçüsü\"\nScreenshot=\"Ekran Görüntüsü Çıxışı\"\nScreenshot.SourceHotkey=\"Seçilmiş Mənbənin Ekran Görüntüsü\"\nScreenshot.StudioProgram=\"Proqramın Ekran Görüntüsünü Saxla\"\nScreenshot.Preview=\"Ön Baxışın Ekran Görüntüsünü Saxla\"\nScreenshot.Scene=\"Səhnənin Ekran Görüntüsünü Saxla\"\nScreenshot.Source=\"Mənbənin Ekran Görüntüsünü Saxla\"\nFilenameFormatting.TT.CCYY=\"İl, dörd rəqəm\"\nFilenameFormatting.TT.YY=\"İl, son iki rəqəm (00-99)\"\nFilenameFormatting.TT.MM=\"Ay onluq rəqəm kimi (01-12)\"\nFilenameFormatting.TT.DD=\"Ayın günü, sıfırla doldurulmuş (01-31)\"\nFilenameFormatting.TT.hh=\"Saat 24 saat formatında (00-23)\"\nFilenameFormatting.TT.mm=\"Dəqiqə (00-59)\"\nFilenameFormatting.TT.ss=\"Saniyə (00-59)\"\nFilenameFormatting.TT.Percent=\"% işarəsi\"\nFilenameFormatting.TT.a=\"Həftə gününün qısaldılmış adı\"\nFilenameFormatting.TT.A=\"Həftə gününün tam adı\"\nFilenameFormatting.TT.b=\"Ayın qısaldılmış adı\"\nFilenameFormatting.TT.B=\"Ayın tam adı\"\nFilenameFormatting.TT.d=\"Ayın günü, sıfırla doldurulmuş (01-31)\"\nFilenameFormatting.TT.H=\"Saat 24 saat formatında (00-23)\"\nFilenameFormatting.TT.I=\"Saat 12 saat formatında (01-12)\"\nFilenameFormatting.TT.m=\"Ay onluq rəqəm kimi (01-12)\"\nFilenameFormatting.TT.M=\"Dəqiqə (00-59)\"\nFilenameFormatting.TT.p=\"AM və ya PM təyinatı\"\nFilenameFormatting.TT.s=\"UNIX epoxundan bəri saniyə ilə vaxt\"\nFilenameFormatting.TT.S=\"Saniyə (00-59)\"\nFilenameFormatting.TT.y=\"İl, son iki rəqəm (00-99)\"\nFilenameFormatting.TT.Y=\"İl\"\nFilenameFormatting.TT.z=\"Saat qurşağında UTC-dən ISO 8601 ofseti\"\nFilenameFormatting.TT.Z=\"Saat qurşağının adı və ya qısaltması\"\nFilenameFormatting.TT.FPS=\"Saniyədəki kadr sayı\"\nFilenameFormatting.TT.CRES=\"Əsas (canvas) rezolyusiya\"\nFilenameFormatting.TT.ORES=\"Çıxış (miqyaslandırılmış) rezolyusiyası\"\nFilenameFormatting.TT.VF=\"Video formatı\"\nBasic.Settings.Video.Adapter=\"Video Adapteri\"\nBasic.Settings.Video.BaseResolution=\"Əsas (Canvas) Rezolyusiya\"\nBasic.Settings.Video.ScaledResolution=\"Çıxış (Miqyaslandırılmış) Rezolyusiyası\"\nBasic.Settings.Video.DownscaleFilter=\"Aşağı Miqyas Filtrləməsi\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Rezolyusiyalar uyğun gəlir, aşağı miqyaslandırma tələb olunmur]\"\nBasic.Settings.Video.FPSCommon=\"Geniş Yayılmış FPS Dəyərləri\"\nBasic.Settings.Video.FPSInteger=\"Tam Ədəd FPS Dəyəri\"\nBasic.Settings.Video.FPSFraction=\"Kəsrli FPS Dəyəri\"\nBasic.Settings.Video.Numerator=\"Surət\"\nBasic.Settings.Video.Denominator=\"Məxrəc\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Eksperimental)\"\nBasic.Settings.Video.InvalidResolution=\"Yanlış rezolyusiya dəyəri. [en]x[hündürlük] formatında olmalıdır (məsələn, 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Video çıxışı hazırda aktivdir. Video parametrlərini dəyişmək üçün zəhmət olmasa, bütün çıxışları söndürün.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (Ən sürətli, lakin miqyaslandırdıqda bulanıq)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bikubik (Kəskinləşdirilmiş miqyaslandırma, 16 nümunə)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Kəskinləşdirilmiş miqyaslandırma, 36 nümunə)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Sahə (Çəkili cəm, 4/6/9 nümunə)\"\nBasic.Settings.Audio=\"Səs\"\nBasic.Settings.Audio.SampleRate=\"Nümunə Tezliyi\"\nBasic.Settings.Audio.Channels=\"Kanallar\"\nBasic.Settings.Audio.Meters=\"Metr\"\nBasic.Settings.Audio.MeterDecayRate=\"Sönmə Dərəcəsi\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Sürətli\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Orta (Tip I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Yavaş (Tip II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Pikmetr Növü\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Nümunə Piki\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Həqiqi Pik (Daha yüksək CPU istifadəsi)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"XƏBƏRDARLIQ: Həcmli səs (surround) aktivdir.\"\nBasic.Settings.Audio.MultichannelWarning=\"Əgər yayımlayırsınızsa, yayım xidmətinizin həm həcmli səs qəbulunu, həm də həcmli səs oxutmasını dəstəklədiyini yoxlayın. Məsələn, Facebook 360 Live həcmli səsi tam dəstəkləyir; YouTube Live 5.1 səs qəbulunu (və televizorlarda oxutmanı) dəstəkləyir.\\n\\nOBS səs filtrləri həcmli səslə uyğundur, lakin VST plagin dəstəyinə zəmanət verilmir.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Həcmli səsi aktiv etmək istəyirsiniz?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Həcmli səsi aktiv etmək istədiyinizə əminsiniz?\"\nBasic.Settings.Audio.Devices=\"Qlobal Səs Qurğuları\"\nBasic.Settings.Audio.DesktopDevice=\"Masaüstü Səsi\"\nBasic.Settings.Audio.DesktopDevice2=\"Masaüstü Səsi 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/Köməkçi Səs\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/Köməkçi Səs 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/Köməkçi Səs 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/Köməkçi Səs 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Səssizə almaq üçün basmağı aktiv et\"\nBasic.Settings.Audio.PushToMuteDelay=\"Səssizə almaq üçün basma gecikməsi\"\nBasic.Settings.Audio.EnablePushToTalk=\"Danışmaq üçün basmağı aktiv et\"\nBasic.Settings.Audio.PushToTalkDelay=\"Danışmaq üçün basma gecikməsi\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Qurğu qoşulmayıb və ya mövcud deyil]\"\nBasic.Settings.Audio.Disabled=\"Deaktiv\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Aşağı Gecikməli Səs Buferləşdirmə Rejimi (Decklink/NDI çıxışları üçün)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"XƏBƏRDARLIQ: Aşağı gecikməli səs buferləşdirməsi aktivdir.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Aşağı gecikməli səs buferləşdirmə rejimi bəzi mənbələrdən gələn səsin pozulmasına və ya dayanmasına səbəb ola bilər.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Aşağı gecikməli səs buferləşdirmə rejimini aktiv etmək istəyirsiniz?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Aşağı gecikməli səs buferləşdirmə rejimini aktiv etmək istədiyinizə əminsiniz?\"\nBasic.Settings.Accessibility=\"Əlçatanlıq\"\nBasic.Settings.Accessibility.ColorOverrides=\"Fərqli Rənglərdən İstifadə et\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Mənbə Sərhədi (Seçim)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Mənbə Sərhədi (Kəsmə)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Mənbə Sərhədi (Üzərinə Gəldikdə)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mikser Səs Zolağı (-60-dan -20dB-ə qədər)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mikser Səs Zolağı (-20-dan -9dB-ə qədər)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mikser Səs Zolağı (-9-dan 0dB-ə qədər)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mikser Səs Zolağı (-60-dan -20dB-ə qədər) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mikser Səs Zolağı (-20-dan -9dB-ə qədər) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mikser Səs Zolağı (-9-dan 0dB-ə qədər) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Rəng İlkin Sazlaması\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"İlkin təyin olunan\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Fərdi\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Rəng Korluğu Alternativi\"\nBasic.Settings.Advanced=\"Qabaqcıl\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Çıxışda(exit) aktiv çıxışlar(outputs) haqqında xəbərdarlığı göstər\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Proses Prioriteti\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Yüksək\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Normadan Yuxarı\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Normadan Aşağı\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Boşda\"\nBasic.Settings.Advanced.FormatWarning=\"Xəbərdarlıq: NV12/P010-dan başqa rəng formatları əsasən qeydə almaq üçün nəzərdə tutulub və yayım zamanı tövsiyə edilmir. Yayım, rəng formatının çevrilməsi səbəbindən artan CPU istifadəsinə səbəb ola bilər.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Xəbərdarlıq: Yüksək dəqiqlikli formatlar daha çox HDR rəng fəzaları ilə istifadə olunur.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Xəbərdarlıq: Rec. 2100 daha çox dəqiqliyə malik bir format istifadə etməlidir.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Rəng Formatı\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 müstəvi)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 müstəvi)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 müstəvi)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 müstəvi)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 müstəvi)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 müstəvi)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 müstəvi)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Rəng Fəzası\"\nBasic.Settings.Advanced.Video.ColorRange=\"Rəng Aralığı\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Məhdud\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Tam\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR Ağ Səviyyəsi\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR Nominal Pik Səviyyəsi\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"İzləmə Qurğusu\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"İlkin təyin olunan\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Windows səs boğulmasını deaktiv et\"\nBasic.Settings.Advanced.StreamDelay=\"Yayım Gecikməsi\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Müddət\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Yenidən qoşulduqda kəsmə nöqtəsini qoru (gecikməni artır)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Təxmini Yaddaş İstifadəsi: %1 MB\"\nBasic.Settings.Advanced.Network=\"Şəbəkə\"\nBasic.Settings.Advanced.Network.Disabled=\"Hazırda seçilmiş yayım protokolu şəbəkə parametrlərinin dəyişdirilməsini dəstəkləmir.\"\nBasic.Settings.Advanced.Network.BindToIP=\"IP-yə Bağla\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP Ailəsi\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Şəbəkə optimallaşdırmalarını aktiv et\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"TCP templəməsini aktiv et\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Transmissiya sürətini tənzimləyərək RTMP çıxışını şəbəkədəki digər gecikməyə həssas tətbiqlər üçün daha əlverişli etməyə çalışır.\\nQeyri-sabit bağlantılarda buraxılmış kadr riskini artıra bilər.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Qısayol Düyməsi Fokus Davranışı\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Qısayol düymələrini heç vaxt deaktiv etmə\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Əsas pəncərə fokusda olduqda qısayol düymələrini deaktiv et\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Əsas pəncərə fokusda olmadıqda qısayol düymələrini deaktiv et\"\nBasic.Settings.Advanced.AutoRemux=\"Avtomatik olaraq %1-ə yenidən birləşdir\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(mkv kimi qeydə al)\"\nBasic.AdvAudio=\"Qabaqcıl Səs Xüsusiyyətləri\"\nBasic.AdvAudio.ActiveOnly=\"Yalnız Aktiv Mənbələr\"\nBasic.AdvAudio.Name=\"Ad\"\nBasic.AdvAudio.Volume=\"Səs səviyyəsi\"\nBasic.AdvAudio.VolumeSource=\"'%1' üçün səs ucalığı\"\nBasic.AdvAudio.MonoSource=\"'%1' üçün Mononu Stereoya Çevir\"\nBasic.AdvAudio.Balance=\"Balans\"\nBasic.AdvAudio.BalanceSource=\"'%1' üçün Balans\"\nBasic.AdvAudio.SyncOffset=\"Sinxronizasiya Sürüşməsi\"\nBasic.AdvAudio.SyncOffsetSource=\"'%1' üçün Sinxronizasiya Sürüşməsi\"\nBasic.AdvAudio.Monitoring=\"Səs İzləmə\"\nBasic.AdvAudio.Monitoring.None=\"İzləmə Deaktivdir\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Yalnız İzləmə (çıxışı səssizə al)\"\nBasic.AdvAudio.Monitoring.Both=\"İzləmə və Çıxış\"\nBasic.AdvAudio.MonitoringSource=\"'%1' üçün Səs İzləməsi\"\nBasic.AdvAudio.AudioTracks=\"Treklər\"\nBasic.Settings.Hotkeys=\"Qısayol Düymələri\"\nBasic.Settings.Hotkeys.Pair=\"'%1' ilə paylaşılan düymə kombinasiyaları dəyişdirici kimi fəaliyyət göstərir\"\nBasic.Settings.Hotkeys.Filter=\"Filtr\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Qısayol Düyməsinə Görə Filtrlə\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Bu qısayol düyməsi bir və ya daha çox başqa hərəkət tərəfindən paylaşılır, toqquşmaları göstərmək üçün klikləyin\"\nBasic.Settings.Hotkeys.PleaseWait=\"Qısayol düymələri yüklənir, zəhmət olmasa gözləyin.\"\nBasic.Hotkeys.SelectScene=\"Səhnəyə keç\"\nBasic.SystemTray.Show=\"Göstər\"\nBasic.SystemTray.Hide=\"Gizlət\"\nBasic.SystemTray.Message.Reconnecting=\"Bağlantı kəsildi. Yenidən bağlanılır...\"\nHotkeys.Insert=\"Daxil et\"\nHotkeys.Delete=\"Sil\"\nHotkeys.Home=\"Əsas Səhifə\"\nHotkeys.Left=\"Sol\"\nHotkeys.Right=\"Sağ\"\nHotkeys.Up=\"Yuxarı\"\nHotkeys.Down=\"Aşağı\"\nHotkeys.Menu=\"Menyu\"\nHotkeys.Space=\"Boşluq\"\nHotkeys.NumpadMultiply=\"Numpad Vurma\"\nHotkeys.NumpadDivide=\"Numpad Bölmə\"\nHotkeys.NumpadAdd=\"Numpad Əlavə et\"\nHotkeys.NumpadSubtract=\"Numpad Çıxma\"\nHotkeys.NumpadDecimal=\"Numpad Onluq\"\nHotkeys.MouseButton=\"Siçan %1\"\nMute=\"Səsi kəs\"\nUnmute=\"Səsi aç\"\nPush-to-mute=\"Səssizə almaq üçün bas\"\nPush-to-talk=\"Danışmaq üçün bas\"\nSceneItemShow=\"'%1'-i göstər\"\nSceneItemHide=\"'%1'-i gizlət\"\nOutputWarnings.NoTracksSelected=\"Ən azı bir trek seçməlisiniz\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Çıxış Parametrləri Xətası\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Bütün çıxışlarda ən azı bir səs treki seçilməlidir.\"\nOutputWarnings.MP4Recording=\"Xəbərdarlıq: MP4/MOV formatında saxlanılan qeydlər, fayl yekunlaşdırıla bilməzsə (məsələn, BSOD, elektrik enerjisinin kəsilməsi və s. nəticəsində) bərpaedilməz olacaq. Əgər birdən çox səs treki qeydə almaq istəyirsinizsə, MKV istifadə etməyi və qeyd tamamlandıqdan sonra onu MP4/MOV formatına yenidən birləşdirməyi düşünün (Fayl → Qeydləri Yenidən Birləşdir).\"\nOutputWarnings.CannotPause=\"Xəbərdarlıq: Əgər qeyd kodlayıcısı \\\"(Yayım kodlayıcısından istifadə et)\\\" olaraq təyin edilibsə, qeydlərə fasilə vermək mümkün deyil.\"\nOutputWarnings.CodecIncompatible=\"Audio və ya video kodlayıcı seçimi uyğunsuzluq səbəbindən sıfırlandı. Zəhmət olmasa, siyahıdan uyğun bir kodlayıcı seçin.\"\nCodecCompat.Incompatible=\"(%1 ilə uyğun deyil)\"\nCodecCompat.CodecPlaceholder=\"Kodlayıcı Seç...\"\nCodecCompat.ContainerPlaceholder=\"Format Seç...\"\nCodecCompat.CodecMissingOnExit.Title=\"Heç Bir Kodlayıcı Seçilməyib\"\nCodecCompat.CodecMissingOnExit.Text=\"Ən azı bir video və ya səs kodlayıcısı təyin edilməyib. Zəhmət olmasa, həm qeydə alma, həm də yayım üçün kodlayıcıları seçdiyinizdən əmin olun.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Heç Bir Format Seçilməyib\"\nCodecCompat.ContainerMissingOnExit.Text=\"Heç bir qeyd formatı seçilməyib. Zəhmət olmasa, seçilmiş yayım kodlayıcısı ilə uyğun olan bir qeyd formatı seçin.\"\nFinalScene.Title=\"Səhnəni Sil\"\nFinalScene.Text=\"Ən azı bir səhnə olmalıdır.\"\nNoSources.Title=\"Mənbə Yoxdur\"\nNoSources.Text=\"Görünür, hələ heç bir video mənbəyi əlavə etməmisiniz, buna görə də yalnız boş ekran yayımlayacaqsınız. Bunu etmək istədiyinizə əminsiniz?\"\nNoSources.Text.AddSource=\"İstədiyiniz zaman əsas pəncərədəki Mənbələr qutusunun altındakı + işarəsinə klikləməklə mənbələr əlavə edə bilərsiniz.\"\nNoSources.Label=\"Heç bir mənbəniz yoxdur.\\nMənbə əlavə etmək üçün aşağıdakı + düyməsinə klikləyin\\nvə ya burada sağ klikləyin.\"\nChangeBG=\"Rəng Təyin Et\"\nCustomColor=\"Fərdi Rəng\"\nBrowserSource.EnableHardwareAcceleration=\"Brauzer Mənbəyi Aparat Sürətləndirməsini Aktiv Et\"\nAbout=\"Haqqında\"\nAbout.Info=\"OBS Studio pulsuz və açıq mənbəli video yazı və canlı yayım proqram təminatıdır.\"\nAbout.Donate=\"Töhfə Ver\"\nAbout.GetInvolved=\"İştirak Et\"\nAbout.Authors=\"Müəlliflər\"\nAbout.License=\"Lisenziya\"\nAbout.Error=\"Xəta! Faylı oxumaq mümkün olmadı.\\n\\nKeçid edin: %1\"\nAbout.Contribute=\"OBS Layihəsini Dəstəklə\"\nAddUrl.Title=\"URL vasitəsilə Mənbə Əlavə Et\"\nAddUrl.Text=\"Siz OBS-ə bir URL sürüklədiniz. Bu, linki avtomatik olaraq mənbə kimi əlavə edəcək. Davam edilsin?\"\nResizeOutputSizeOfSource=\"Çıxışı yenidən ölçüləndir (mənbə ölçüsü)\"\nResizeOutputSizeOfSource.Text=\"Əsas və çıxış rezolyusiyaları cari mənbənin ölçüsünə görə yenidən ölçüləndiriləcək.\"\nResizeOutputSizeOfSource.Continue=\"Davam etmək istəyirsiniz?\"\nPreviewTransition=\"Keçidə Ön Baxış\"\nImporter=\"Səhnə Kolleksiyası İdxal et\"\nImporter.SelectCollection=\"Səhnə Kolleksiyası Seç\"\nImporter.Collection=\"Səhnə Kolleksiyası\"\nImporter.HelpText=\"OBS və ya digər dəstəklənən proqramlardan kolleksiyaları idxal etmək üçün bu pəncərəyə fayllar əlavə edin.\"\nImporter.Path=\"Kolleksiya Yolu\"\nImporter.Program=\"Aşkarlanan Tətbiq\"\nImporter.AutomaticCollectionPrompt=\"Səhnə Kolleksiyaları üçün Avtomatik Axtarış\"\nImporter.AutomaticCollectionText=\"OBS, dəstəklənən üçüncü tərəf proqramlarından idxal edilə bilən səhnə kolleksiyalarını avtomatik tapa bilər. OBS-in sizin üçün kolleksiyaları avtomatik tapmasını istəyirsiniz?\\n\\nBunu daha sonra Parametrlər > Ümumi > İdxalçılar bölməsində dəyişə bilərsiniz.\"\nImporter.SelectFile=\"Gözdən keçir...\"\nRestart=\"Yenidən başlat\"\nNeedsRestart=\"OBS Studio yenidən başladılmalıdır. İndi yenidən başlatmaq istəyirsiniz?\"\nLoadProfileNeedsRestart=\"Profil OBS-in yenidən başladılmasını tələb edən parametrlərə malikdir:\\n%1\\n\\nBu parametrlərin qüvvəyə minməsi üçün OBS-i yenidən başlatmaq istəyirsiniz?\"\nContextBar.NoSelectedSource=\"Heç bir mənbə seçilməyib\"\nContextBar.MediaControls.PlayMedia=\"Medianı Oxut\"\nContextBar.MediaControls.PauseMedia=\"Mediaya Fasilə ver\"\nContextBar.MediaControls.StopMedia=\"Medianı Dayandır\"\nContextBar.MediaControls.RestartMedia=\"Medianı Yenidən Başlat\"\nContextBar.MediaControls.PlaylistNext=\"Oxutma Siyahısında Növbəti\"\nContextBar.MediaControls.PlaylistPrevious=\"Oxutma Siyahısında Əvvəlki\"\nContextBar.MediaControls.BlindSeek=\"Media Axtarış Vidgeti\"\nYouTube.Auth.Ok=\"İcazəvermə (avtorizasiya) uğurla tamamlandı.\\nİndi bu səhifəni bağlaya bilərsiniz.\"\nYouTube.Auth.NoCode=\"İcazəvermə (Avtorizasiya) prosesi tamamlanmadı.\"\nYouTube.Auth.NoChannels=\"Seçilmiş hesabda heçbir kanal(lar) mövcud deyil\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube İstifadəçi Avtorizasiyası\"\nYouTube.Auth.WaitingAuth.Text=\"Zəhmət olmasa, avtorizasiyanı xarici brauzerinizdə tamamlayın.<br>Əgər xarici brauzer açılmasa, bu linkə daxil olun və avtorizasiyanı tamamlayın:<br>%1\"\nYouTube.AuthError.Text=\"Kanal məlumatını əldə etmək mümkün olmadı: %1.\"\nYouTube.Actions.WindowTitle=\"YouTube Yayım Quraşdırması - Kanal: %1\"\nYouTube.Actions.CreateNewEvent=\"Yeni Yayım Yarat\"\nYouTube.Actions.ChooseEvent=\"Mövcud Yayımı Seç\"\nYouTube.Actions.Title=\"Başlıq*\"\nYouTube.Actions.MyBroadcast=\"Mənim Yayımım\"\nYouTube.Actions.Description=\"Açıqlama\"\nYouTube.Actions.Privacy=\"Məxfilik*\"\nYouTube.Actions.Privacy.Private=\"Şəxsi\"\nYouTube.Actions.Privacy.Public=\"İctimai\"\nYouTube.Actions.Privacy.Unlisted=\"Siyahıda Olmayan\"\nYouTube.Actions.Category=\"Kateqoriya\"\nYouTube.Actions.Thumbnail=\"Miniatür\"\nYouTube.Actions.Thumbnail.SelectFile=\"Fayl seç...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Heç bir fayl seçilməyib\"\nYouTube.Actions.Thumbnail.ClearFile=\"Təmizlə\"\nYouTube.Actions.MadeForKids=\"Bu video uşaqlar üçün hazırlanıb?*\"\nYouTube.Actions.MadeForKids.Yes=\"Bəli, uşaqlar üçün hazırlanıb\"\nYouTube.Actions.MadeForKids.No=\"Xeyr, uşaqlar üçün hazırlanmayıb\"\nYouTube.Actions.AdditionalSettings=\"Əlavə parametrlər\"\nYouTube.Actions.Latency=\"Gecikmə\"\nYouTube.Actions.Latency.Low=\"Aşağı\"\nYouTube.Actions.Latency.UltraLow=\"Ultra aşağı\"\nYouTube.Actions.EnableAutoStart=\"Avtomatik Başlatmanı Aktiv et\"\nYouTube.Actions.EnableAutoStop=\"Avtomatik Dayandırmanı Aktiv et\"\nYouTube.Actions.AutoStartStop.TT=\"Bu planlaşdırılmış yayımın avtomatik başlamalı olub-olmadığını göstərir\"\nYouTube.Actions.EnableDVR=\"DVR-ı Aktiv et\"\nYouTube.Actions.ScheduleForLater=\"Daha sonraya planlaşdır\"\nYouTube.Actions.RememberSettings=\"Bu parametrləri yadda saxla\"\nYouTube.Actions.Create_Ready=\"Yayımı yarat\"\nYouTube.Actions.Create_GoLive=\"Yayımı yarat və yayıma başla\"\nYouTube.Actions.Choose_Ready=\"Yayımı seç\"\nYouTube.Actions.Choose_GoLive=\"Yayımı seç və yayıma başla\"\nYouTube.Actions.Create_Schedule=\"Yayımı planlaşdır\"\nYouTube.Actions.Create_Schedule_Ready=\"Yayımı planlaşdır və seç\"\nYouTube.Actions.Dashboard=\"YouTube Studio-nu aç\"\nYouTube.Actions.Error.Title=\"Canlı yayım yaratma xətası\"\nYouTube.Actions.Error.Text=\"YouTube giriş xətası '%1'.<br/>Ətraflı xəta təsvirini <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> ünvanında tapa bilərsiniz\"\nYouTube.Actions.Error.General=\"YouTube giriş xətası. Zəhmət olmasa, şəbəkə bağlantınızı və ya YouTube serverinə girişinizi yoxlayın.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Yayım yaratma xətası '%1'.<br/>Ətraflı xəta təsvirini <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> ünvanında tapa bilərsiniz\"\nYouTube.Actions.Error.NoStreamCreated=\"Heç bir yayım yaradılmadı. Zəhmət olmasa, hesabınızı yenidən bağlayın.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API Xətası. Daha ətraflı məlumat üçün zəhmət olmasa, jurnal faylına baxın.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Seçilmiş yayım tapılmadı.\"\nYouTube.Actions.Error.FileMissing=\"Seçilmiş fayl mövcud deyil.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Seçilmiş faylı açmaq mümkün olmadı.\"\nYouTube.Actions.Error.FileTooLarge=\"Seçilmiş fayl çox böyükdür (Limit: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Yayımı keçirmək mümkün olmadı: %1<br/><br/>Əgər bu xəta davam edərsə, <a href='https://studio.youtube.com/video/%2/livestreaming'>yayımı YouTube Studio-da açın</a> və əl ilə cəhd edin.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Yayım test mərhələsinə keçir, bu bir qədər vaxt apara bilər. Zəhmət olmasa, 10-30 saniyə sonra yenidən cəhd edin.\"\nYouTube.Actions.EventsLoading=\"Tədbirlərin siyahısı yüklənir...\"\nYouTube.Actions.EventCreated.Title=\"Tədbir Yaradıldı\"\nYouTube.Actions.EventCreated.Text=\"Tədbir uğurla yaradıldı.\"\nYouTube.Actions.Stream=\"Yayım\"\nYouTube.Actions.Stream.ScheduledFor=\"%1 tarixinə planlaşdırılıb\"\nYouTube.Actions.Stream.Resume=\"Kəsilmiş yayımı davam etdir\"\nYouTube.Actions.Stream.YTStudio=\"YouTube Studio tərəfindən avtomatik yaradılıb\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Yeni Canlı Yayım yaradılır, zəhmət olmasa gözləyin...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Əl ilə başlatma tələb olunur\"\nYouTube.Actions.AutoStartStreamingWarning=\"Bu tədbir üçün avtomatik başlatma deaktiv edilib, yayımınızı başlatmaq üçün \\\"Canlı Yayıma Çıx\\\" düyməsinə klikləyin.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Yenidən qoşula bilməyəcəksiniz.<br>Yayımınız dayanacaq və siz artıq canlı yayımda olmayacaqsınız.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Seçilmiş YouTube kanalında canlı yayım aktiv deyil.<br/><br/>Daha ətraflı məlumat üçün <a href='https://www.youtube.com/features'>youtube.com/features</a> səhifəsinə baxın.\"\nYouTube.Errors.livePermissionBlocked=\"Seçilmiş YouTube Kanalında canlı yayım mövcud deyil.<br/>Nəzərə alın ki, kanal parametrlərinizdə aktiv etdikdən sonra canlı yayımın mövcud olması 24 saata qədər çəkə bilər.<br/><br/>Təfərrüatlar üçün <a href='https://www.youtube.com/features'>youtube.com/features</a> səhifəsinə baxın.\"\nYouTube.Errors.errorExecutingTransition=\"Arxa tərəf xətası səbəbindən keçid uğursuz oldu. Zəhmət olmasa, bir neçə saniyə sonra yenidən cəhd edin.\"\nYouTube.Errors.errorStreamInactive=\"YouTube yayımınız üçün məlumat almır. Zəhmət olmasa, konfiqurasiyanızı yoxlayın və yenidən cəhd edin.\"\nYouTube.Errors.invalidTransition=\"Cəhd edilən keçid yanlış idi. Bu, yayımın əvvəlki keçidi tamamlamaması səbəbindən ola bilər. Zəhmət olmasa, bir neçə saniyə gözləyin və yenidən chd edin.\"\nYouTube.DocksRemoval.Title=\"Köhnə YouTube Brauzer Pəncərələrini Təmizlə\"\nYouTube.DocksRemoval.Text=\"Bu brauzer pəncərələri köhnəlmiş olduğu üçün silinəcək:\\n\\n%1\\nƏvəzinə \\\"Pəncərələr/YouTube Canlı İdarəetmə Otağı\\\"-dan istifadə edin.\"\nConfigDownload.WarningMessageTitle=\"Xəbərdarlıq\"\nFailedToStartStream.MissingConfigURL=\"Cari xidmət üçün heç bir konfiqurasiya URL-i mövcud deyil\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Fərdi RTMP URL-i təyin edilməyib\"\nFailedToStartStream.InvalidCustomConfig=\"Yanlış fərdi konfiqurasiya\"\nFailedToStartStream.MissingCanvas=\"Konfiqurasiya edilmiş əlavə canvas çatışmır\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Çoxtrekli video xidməti yaratmaq mümkün olmadı\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Çoxtrekli video RTMP çıxışı yaratmaq mümkün olmadı\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC mövcud deyil.\\n\\n'%1' kodlayıcı növünü tapmaq mümkün olmadı\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"'%1' video kodlayıcısını yaratmaq mümkün olmadı (növ: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"'%1' kodlayıcısını yaradarkən OBS video məlumatını əldə etmək mümkün olmadı (növ: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Səs kodlayıcısı yaratmaq mümkün olmadı\"\nFailedToStartStream.NoRTMPURLInConfig=\"Konfiqurasiya yayım hədəfi RTMP(S) URL-ini ehtiva etmir\"\nFailedToStartStream.FallbackToDefault=\"%1 istifadə edərək yayımı başlatmaq mümkün olmadı; tək kodlaşdırma parametrləri ilə yenidən cəhd etmək istəyirsiniz?\"\nFailedToStartStream.ConfigRequestFailed=\"Konfiqurasiyanı %1-dən əldə etmək mümkün olmadı<br><br>HTTP xətası: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Naməlum status dəyəri '%1' alındı\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\n%1 olmadan yayıma davam etmək istəyirsiniz?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nYayıma davam etmək istəyirsiniz?\"\nFailedToStartStream.MissingEncoderConfigs=\"Canlı yayıma çıxma konfiqurasiyasına kodlayıcı konfiqurasiyaları daxil edilməyib\"\nFailedToStartStream.InvalidEncoderConfig=\"Canlı yayıma çıxma konfiqurasiyasına yanlış kodlayıcı konfiqurasiyası daxil idi\"\nFailedToStartStream.StatusMissingHTML=\"Canlı yayıma çıxma sorğusu naməlum xəta qaytardı\"\nFailedToStartStream.NoConfigSupplied=\"Çatışmayan konfiqurasiya\"\nMultitrackVideo.Info=\"%1 avtomatik olaraq bir neçə video keyfiyyətini kodlaşdırmaq və göndərmək üçün parametrlərinizi optimallaşdırır. Bu seçimi etmək kompüteriniz və proqram təminatı quraşdırmanız haqqında %2 məlumatlarını göndərəcək.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Uyğunsuz Parametrlər\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 hazırda bunlarla uyğun deyil:\\n\\n%2\\n%1 ilə yayıma davam etmək üçün uyğunsuz parametrləri deaktiv edin:\\n\\n%3\\nvə yenidən Yayımı Başlatın.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Bu yayım üçün deaktiv et və Yayımı Başlat\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Parametrləri Güncəllə və Yayımı Başlat\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 hazırda '%2' olaraq təyin edilmiş [Səs → Ümumi → Kanallar] ilə uyğun deyil, %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Səs → Ümumi → Kanallar] '%1' olaraq təyin edilməlidir\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 [Səs → Ümumi → Kanallar] üçün bir neçə fərqli parametr tələb edir\"\nBasic.OpenPluginManager=\"Plagin İdarəedicisi\"\nPluginManager=\"Plagin İdarəedicisi\"\nPluginManager.Restart=\"Plagin dəyişikliklərinin tətbiq edilməsi üçün OBS Studio yenidən başladılmalıdır\"\nPluginManager.MissingPlugin=\"[PLAGİN TAPILMADI]\"\nPluginManager.Section.Discover=\"Gözdən keçir\"\nPluginManager.Section.Manage=\"Quraşdırıldı\"\nPluginManager.Section.Updates=\"Güncəlləmələr\"\nPluginManager.Section.Manage.Title=\"Aktiv Plaginləri İdarə et\"\n"
  },
  {
    "path": "frontend/data/locale/ba-RU.ini",
    "content": "Language=\"Башҡортса\"\nOK=\"Ярай\"\nApply=\"Файҙаланыу\"\nCancel=\"Бөтөрөү\"\nClose=\"Ябыу\"\nSave=\"Һаҡлау\"\nDiscard=\"Кире ҡағыу\"\nDisable=\"Һүндереү\"\nYes=\"Эйе\"\nNo=\"Юҡ\"\nAdd=\"Өҫтәү\"\nRemove=\"Юйыу\"\nRename=\"Атап бөтөрөү\"\nInteract=\"Йоҡташыу\"\nFilters=\"Фильтрҙар\"\nProperties=\"Үҙенсәлектәр\"\nMoveUp=\"Өҫкәрәк\"\nMoveDown=\"Аҫҡараҡ\"\nSettings=\"Көйләүҙәр\"\nDisplay=\"Экран\"\nName=\"Атама\"\nExit=\"Сығыу\"\nMixer=\"Аудио миксеры\"\nBrowse=\"Күҙәтеү\"\nMono=\"Моно\"\nStereo=\"Стерео\"\nDroppedFrames=\"%1 (%2%) тиклем кадрҙар үткәреү\"\nClear=\"Таҙартыу\"\nRevert=\"Ҡайтарыу\"\nShow=\"Күрһәтеү\"\nHide=\"Йәшереү\"\nUnhideAll=\"Бөтөн күрһәтеү\"\nUntitled=\"Атамһыҙ\"\nNew=\"Яһау\"\nDuplicate=\"Дубльләү\"\nEnable=\"Ҡабыҙыу\"\nDisableOSXVSync=\"macOS V-Sync һүндереү\"\nResetOSXVSyncOnExit=\"Сығыуҙа macOS V-Sync ташлатыу\"\nHighResourceUsage=\"Билдәгәрткес артып тулған! Видео көйләүҙәрҙе кесерәйеп әллә тиҙерәк билдәгәрткестең көйләүҙәрҙе файҙалап маташып ҡарағыҙ.\"\nTransition=\"Әүрелеү\"\nQuickTransitions=\"Тиҙ әүрелеүҙәр\"\nFadeToBlack=\"Ҡараға һүнеүе\"\nLeft=\"Һулда\"\nRight=\"Уңда\"\nTop=\"Өҫтә\"\nBottom=\"Аҫта\"\nReset=\"Ташлатыу\"\nHours=\"Сәғәт\"\nMinutes=\"Минут\"\nSeconds=\"Секунд\"\nDeprecated=\"Иҫкергән\"\nReplayBuffer=\"Ҡабатлау буферы\"\nImport=\"Импорт\"\nExport=\"Экспорт\"\nCopy=\"Күсермәһең алыу\"\nPaste=\"Ҡуйыу\"\nPasteReference=\"Ҡуйыу (Һылтанма)\"\nPasteDuplicate=\"Ҡуйыу (Дүпликәт)\"\nRemuxRecordings=\"Яҙмаларҙың ремукслау\"\nNext=\"Артабан\"\nBack=\"Артҡа\"\nDefaults=\"Ғәҙәти\"\nHideMixer=\"Миксерҙа күрһәтмәҫкә\"\nTransitionOverride=\"Әүрелеүҙең яңы билдәләүе\"\nNone=\"Юҡ\"\nStudioMode.Preview=\"Алдынан ҡарау\"\nStudioMode.Program=\"Програм\"\nShowInMultiview=\"Мульти-күренештә күрһәтеү\"\nVerticalLayout=\"Вертикаль торош\"\nGroup=\"Төркөм\"\nDoNotShowAgain=\"Бүтәнсә күрһәтмәү\"\nDefault=\"(Ғәҙәти)\"\nCalculating=\"Һанау...\"\nFullscreen=\"Тулы экран үтәмлеге\"\nWindowed=\"Тәҙрә үтәмлеге\"\nAspectRatio=\"<b>%1:%2</b> яҡтарҙың мөнәсәбәттәре\"\nLockVolume=\"Тауышты билдәләү\"\nOpenFile=\"Файл асырға\"\nAlreadyRunning.Title=\"OBS ҡабыҙылған инде\"\nAlreadyRunning.LaunchAnyway=\"Барабер ебәреү\"\nDockCloseWarning.Title=\"Билдәләп торған тәҙрәне ябыу\"\nExtraBrowsers=\"Браузерҙың панельдары\"\nExtraBrowsers.Info=\"Исем дә һылтанма индереп, док-панельдарҙы өҫтәгеҙ, шунан, док-панельдарҙы асырға тип, Ҡабул итеү йәки Абыу баҫығыҙ. Ҡасан булһа ла һеҙ уларҙы юйырға ла өҫтәргә мөмкин булаһығыҙ.\"\nExtraBrowsers.DockName=\"Панельдың исеме\"\nAuth.Authing.Title=\"Кереү...\"\nAuth.Authing.Text=\"%1 менән кереү бара, көтөп тороғоҙ, зинһар...\"\nAuth.AuthFailure.Title=\"Ауҫентификацияның хатаһы\"\nAuth.AuthFailure.Text=\"%1:\\n\\n%2: %3 менән ауҫентификацияның хатаһы\"\nAuth.InvalidScope.Title=\"Ауҫентификацияһы кәрәк\"\nAuth.InvalidScope.Text=\"Аутентификациялауҙың талаптары %1 өсөн үҙгәртелделәр инде. Ҡайһы берәүҙәр функциялар тыйылырға мөмкин.\"\nAuth.LoadingChannel.Title=\"Канал өсөн мәғлүмәт йөкләү...\"\nAuth.Chat=\"Чат\"\nAuth.StreamInfo=\"Стримдың мәғлүмәте\"\nTwitchAuth.Stats=\"Twitch статистиктары\"\nTwitchAuth.Feed=\"Twitch әүҙемлек таҫмаһы\"\nTwitchAuth.TwoFactorFail.Title=\"Стримдың асҡысын алып булманы\"\nRestreamAuth.Channels=\"Restream каналдары\"\nCopy.Filters=\"Фильтрҙарҙың күсермәһең алыу\"\nPaste.Filters=\"Фильтрҙарҙы ҡуйыу\"\nBrowserPanelInit.Title=\"Браузерҙың инициализацияһы...\"\nBrowserPanelInit.Text=\"Браузерҙың инициализацияһы, көтөп тороғоҙ, зинһар...\"\nBandwidthTest.Region=\"Өлкә\"\nBandwidthTest.Region.US=\"АҠШ\"\nBandwidthTest.Region.EU=\"Европа\"\nBandwidthTest.Region.Asia=\"Азия\"\nBandwidthTest.Region.Other=\"Ҡаршы\"\nBasic.AutoConfig=\"Автомат рәүештә көйләү оҫталыҡсығы\"\nBasic.AutoConfig.ApplySettings=\"Көйләүҙәрҙе файҙалау\"\nBasic.AutoConfig.StartPage=\"Ҡулланыу мәғлүмәте\"\nBasic.AutoConfig.VideoPage=\"Видео көйләүҙәре\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"(%1x%2) ағымдағыны файҙалау\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"%1 (%2x%3) дисплейы\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"(%1) ағымдағыны файҙалау\"\nBasic.AutoConfig.StreamPage=\"Стримдың мәғлүмәте\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Зинһар, трансляция тураһында мәғлүмәтте яҙығыҙ\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Стримдың асҡысын файҙалау\"\nBasic.AutoConfig.StreamPage.Service=\"Сервис\"\nBasic.AutoConfig.StreamPage.Server=\"Сервер\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Стримдың асҡысы\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Стрим билдәгәрткесе\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Яҙманың билдәгәрткесе\"\nBasic.Stats=\"Статистик\"\nBasic.Stats.CPUUsage=\"ҮП файҙалау\"\nBasic.Stats.HDDSpaceAvailable=\"Дисктың буш урыны\"\nBasic.Stats.MemoryUsage=\"Хәтер файҙаланыу\"\nBasic.Stats.Output.Stream=\"Стрим\"\nBasic.Stats.Output.Recording=\"Яҙыу\"\nBasic.Stats.Status=\"Хәләт\"\nBasic.Stats.Status.Recording=\"Яҙыу бара\"\nBasic.Stats.Status.Live=\"ТУРА ТАПШЫРЫУ\"\nBasic.Stats.Status.Reconnecting=\"Кире тотошоу\"\nBasic.Stats.Status.Inactive=\"Әүҙемһеҙ\"\nBasic.Stats.Status.Active=\"Әүҙемле\"\nBasic.Stats.DroppedFrames=\"Кадрҙар үткәреү (Селтәр)\"\nBasic.Stats.MegabytesSent=\"Ебәрелгән мәғлүмәт\"\nBasic.Stats.Bitrate=\"Битрейт\"\nBasic.Stats.ResetStats=\"Статистик ташлатыу\"\nUpdater.Title=\"Яңырыш сыҡты\"\nUpdater.Text=\"Яңы яңырыш бар:\"\nUpdater.UpdateNow=\"Хәҙер яңыртыу\"\nUpdater.RemindMeLater=\"Һуңғыраҡ иҫкә төрөрөү\"\nUpdater.Skip=\"Юрама ҡалдырыу\"\nUpdater.NoUpdatesAvailable.Title=\"Яңырышар юҡ\"\nUpdater.NoUpdatesAvailable.Text=\"Яңырышар табылмаған\"\nQuickTransitions.DuplicateScene=\"Сәхнәне дүпликәтләү\"\nQuickTransitions.EditProperties=\"Сығанаҡтарҙы дүпликәтләү\"\nQuickTransitions.HotkeyName=\"Тиҙ әүрелеү: %1\"\nBasic.AddTransition=\"Көйләүле әүрелеүҙе өҫтәргә\"\nBasic.RemoveTransition=\"Көйләүле әүрелеүҙе юйырға\"\nBasic.TransitionDuration=\"Оҙайлыҡ\"\nTitleBar.Profile=\"Профиль\"\nTitleBar.Scenes=\"Сәхнәләр\"\nOutput.StreamEncodeError.Title=\"Билдәгәрткес хатасы\"\nOutput.RecordError.Title=\"Яҙыу хатасы\"\nBasic.Scene=\"Сәхнә\"\nDeinterlacing.Discard=\"Кире ҡағыу\"\nBasic.Main.AddSceneDlg.Title=\"Сәхнәне өҫтәргә\"\nBasic.Main.DefaultSceneName.Text=\"Сәхнә %1\"\nBasic.Filters=\"Фильтрҙар\"\nBasic.Filters.AddFilter.Title=\"Фильтр исеме\"\nBasic.TransformWindow.Position=\"Позиция\"\nBasic.Main.Scenes=\"Сәхнәләр\"\nBasic.MainMenu.File=\"Файл (&F)\"\nBasic.MainMenu.File.Export=\"Экспорт (&E)\"\nBasic.MainMenu.File.Import=\"Импорт (&I)\"\nBasic.MainMenu.File.Remux=\"Яҙмаларҙың ремукслау (&M)\"\nBasic.MainMenu.File.Settings=\"Көйләүҙәр (&S)\"\nBasic.MainMenu.File.Exit=\"Сығыу (&X)\"\nBasic.Settings.General.Language=\"Тел\"\nBasic.Settings.General.Projectors=\"Проекторҙар\"\nBasic.Settings.General.Preview=\"Алдынан ҡарау\"\nBasic.Settings.Stream=\"Стрим\"\nBasic.Settings.Stream.TTVAddon.None=\"Юҡ\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Суффикс\"\nBasic.Settings.Output.VideoBitrate=\"Видео битрейты\"\nBasic.Settings.Output.AudioBitrate=\"Аудио битрейты\"\nBasic.Settings.Output.Adv.Streaming=\"Стрим\"\nBasic.Settings.Output.Adv.Recording=\"Яҙыу\"\nBasic.Settings.Output.Adv.Recording.Type=\"Төр\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Барлыҡ файлдар\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Аудио\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Видео\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Видео билдәгәрткесе\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Аудио билдәгәрткесе\"\nBasic.Settings.Audio=\"Аудио\"\nBasic.Settings.Audio.Channels=\"Каналлар\"\nBasic.Settings.Audio.Disabled=\"Һүндерелгән\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Оҙайлыҡ\"\nBasic.Settings.Advanced.Network=\"Селтәр\"\nBasic.AdvAudio.Name=\"Атама\"\nBasic.AdvAudio.Mono=\"Моно\"\nBasic.AdvAudio.AudioTracks=\"Тректар\"\nBasic.Settings.Hotkeys.Filter=\"Фильтр\"\nBasic.SystemTray.Show=\"Күрһәтеү\"\nBasic.SystemTray.Hide=\"Йәшереү\"\nHotkeys.Left=\"Һулда\"\nHotkeys.Right=\"Уңда\"\nAbout.Authors=\"Авторҙар\"\nRestart=\"Яңынан ебәрергә\"\n"
  },
  {
    "path": "frontend/data/locale/be-BY.ini",
    "content": "Language=\"Беларуская\"\nApply=\"Ужыць\"\nCancel=\"Скасаваць\"\nClose=\"Закрыць\"\nSave=\"Захаваць\"\nDiscard=\"Адхіліць\"\nDisable=\"Адключыць\"\nYes=\"Так\"\nNo=\"Не\"\nAdd=\"Дадаць\"\nRemove=\"Выдаліць\"\nRename=\"Перайменаваць\"\nInteract=\"Узаемадзейнічаць\"\nFilters=\"Фільтры\"\nProperties=\"Уласцівасці\"\nMoveUp=\"Перамясціць вышэй\"\nMoveDown=\"Перамясціць ніжэй\"\nSettings=\"Налады\"\nDisplay=\"Экран\"\nName=\"Назва\"\nExit=\"Выйсці\"\nMixer=\"Аўдыямікшар\"\nBrowse=\"Агляд\"\nMono=\"Мона\"\nStereo=\"Стэрэа\"\nDroppedFrames=\"Згубленых кадраў: %1 (%2%)\"\nProjector.Open.Program=\"Адкрыць праграмны праектар\"\nProjector.Open.Preview=\"Адкрыць праектар папярэдняга прагляду\"\nProjector.Open.Scene=\"Адкрыць праектар сцэны\"\nProjector.Open.Source=\"Адкрыць праектар крыніцы\"\nProjector.Open.Multiview=\"Адкрыць мультыпрагляд\"\nProjector.Display=\"Дысплэй: %1\"\nProjector.Window=\"Новае акно\"\nProjector.Title=\"Праектар\"\nProjector.Title.Scene=\"Сцэна: %1\"\nProjector.Title.Source=\"Крыніца: %1\"\nProjector.Title.Multiview=\"Мултыпрагляд\"\nProjector.ResizeWindowToContent=\"Падагнаць акно да змесціва\"\nClear=\"Ачысціць\"\nRevert=\"Вярнуць\"\nShow=\"Паказаць\"\nHide=\"Схаваць\"\nUnhideAll=\"Паказаць усё\"\nUntitled=\"Безыменны\"\nNew=\"Стварыць\"\nDuplicate=\"Дубляваць\"\nEnable=\"Уключыць\"\nDisableOSXVSync=\"Выключыць V-Sync на macOS\"\nResetOSXVSyncOnExit=\"Скінуць V-Sync на macOS пры выхадзе\"\nHighResourceUsage=\"Кадавальнік перагружаны! Панізьце налады відэа або скарыстайцеся хутчэйшым наборам налад кадавання.\"\nTransition=\"Пераход\"\nQuickTransitions=\"Хуткія пераходы\"\nFadeToBlack=\"Зацямненне\"\nLeft=\"Злева\"\nRight=\"Справа\"\nTop=\"Зверху\"\nBottom=\"Знізу\"\nReset=\"Скінуць\"\nHours=\"гадз\"\nMinutes=\"хв\"\nSeconds=\"с\"\nDeprecated=\"Састарэлае\"\nReplayBuffer=\"Буфер паўтору\"\nImport=\"Імпартаваць\"\nExport=\"Экспартаваць\"\nCopy=\"Скапіяваць\"\nPaste=\"Уставіць\"\nPasteReference=\"Уставіць (спасылку)\"\nPasteDuplicate=\"Уставіць (копію)\"\nRemuxRecordings=\"Перапакаваць запісы\"\nNext=\"Далей\"\nBack=\"Назад\"\nDefaults=\"Скінуць\"\nRestoreDefaults=\"Прадвызн.\"\nHideMixer=\"Схаваць у мікшары\"\nTransitionOverride=\"Перазапіс пераходу\"\nShowTransition=\"Пераход падчас паказу\"\nHideTransition=\"Пераход падчас схавання\"\nNone=\"Няма\"\nStudioMode.Preview=\"Перадпрагляд\"\nStudioMode.Program=\"Праграма\"\nStudioMode.PreviewSceneName=\"Прагляд: %1\"\nStudioMode.ProgramSceneName=\"Праграма: %1\"\nShowInMultiview=\"Паказаць у Мультыпраглядзе\"\nVerticalLayout=\"Вертыкальны расклад\"\nGroup=\"Група\"\nDoNotShowAgain=\"Не паказваць зноў\"\nDefault=\"(Прадвызначаны)\"\nCalculating=\"Ідзе вылічэнне...\"\nFullscreen=\"Поўнаэкранны рэжым\"\nWindowed=\"У акне\"\nRefreshBrowser=\"Абнавіць\"\nAspectRatio=\"Суадносіны бакоў <b>%1:%2</b>\"\nLockVolume=\"Зафіксаваць гучнасць\"\nLogViewer=\"Журнал адладкі\"\nShowOnStartup=\"Паказваць пры запуску\"\nOpenFile=\"Адкрыць файл\"\nAddScene=\"Дадаць сцэну\"\nAddSource=\"Дадаць крыніцу\"\nRemoveScene=\"Выдаліць выбраную сцэну\"\nRemoveSource=\"Выдаліць выбраную(-ыя) крыніцу(-ы)\"\nMoveSceneUp=\"Перацягнуць сцэну ўверх\"\nMoveSceneDown=\"Перацягнуць сцэну ўніз\"\nMoveSourceUp=\"Перацягнуць крыніцу(-цы) ўверх\"\nMoveSourceDown=\"Перацягнуць крыніцу(-цы) ўніз\"\nSourceProperties=\"Паказаць уласцівасці крыніцы\"\nSourceFilters=\"Паказаць фільтры крыніцы\"\nMixerToolbarMenu=\"Меню аўдыямікшара\"\nSceneFilters=\"Паказаць фільтры сцэны\"\nList=\"Спісам\"\nGrid=\"Сеткай\"\nAutomatic=\"Аўта\"\nComingSoon=\"Неўзабаве\"\nPluginsFailedToLoad.Title=\"Памылка загрузкі плагіна\"\nPluginsFailedToLoad.Text=\"Немагчыма запусціць наступныя плагіны OBS:\\n\\n%1\\nАбнавіце ці выдаліце гэтыя плагіны.\"\nAlreadyRunning.Title=\"OBS ужо працуе\"\nAlreadyRunning.Text=\"OBS ужо працуе! Калі вам не патрабуецца некалькі адначасова адкрытых вокнаў OBS, скончыце ўсе існуючыя працэсы праграмы перад запускам новага. Калі вы ўключылі згортванне OBS у вобласць апавяшчэнняў, праверце, ці ёсць там значок праграмы.\"\nAlreadyRunning.LaunchAnyway=\"Усё роўна запусціць\"\nCrashHandling.Dialog.Title=\"Выяўлены збой OBS Studio\"\nCrashHandling.Labels.Text=\"OBS Studio няправільна скончыла працу.\\n\\nЗапусціць у бяспечным рэжыме (адключаныя іншыя плагіны, скрыпты і WebSockets)?\"\nCrashHandling.Labels.PrivacyNotice=\"Вы таксама можаце аўтаматычна запампаваць апошнюю справаздачу аб збоі на серверы OBSProject.<br /><br />Перш як запампоўваць любыя файлы, азнаёмцеся з <a href='https://obsproject.com/privacy-policy'>Палітыкай прыватнасці</a> і звярніце асаблівую ўвагу на яе часткі пра запампоўванне файлаў.\"\nCrashHandling.Checkbox.SendReport=\"Я, азнаёміўшыся з палітыкай прыватнасці, пагаджаюся на запампоўванне.\"\nCrashHandling.Buttons.LaunchSafe=\"Запуск у бяспечным рэжыме\"\nCrashHandling.Buttons.LaunchNormal=\"Запуск у звычайным рэжыме\"\nCrashHandling.Errors.UploadJSONError=\"Падчас запампоўвання апошняй справаздачы аб збоі ўзнікла памылка. Паўтарыце спробу пазней.\"\nCrashHandling.Errors.Title=\"Памылка запампоўвання справаздачы\"\nSafeMode.Restart=\"Ці жадаеце Вы перазапусціць OBS у бяспечным рэжыме (адключаныя іншыя плагіны, скрыпты і WebSockets)?\"\nSafeMode.RestartNormal=\"Ці хочаце Вы перазапусціць OBS у нармальным рэжыме?\"\nChromeOS.Title=\"Платформа не падтрымліваецца\"\nChromeOS.Text=\"Здаецца, OBS працуе ў кантэйнеры ChromeOS. Гэта платформа не падтрымліваецца.\"\nWine.Title=\"Выяўлены Wine\"\nWine.Text=\"Запуск OBS праз Wine не падтрымліваецца, і многія функцыі, такія як крыніцы захопу або прылад, могуць не працаваць альбо працаваць з абмежаванай магчымасцю.<br><br>Рэкамендуецца запускаць версію OBS для вашай платформы, напрыклад <a href='https://flathub.org/apps/details/com.obsproject.Studio'>нашу версію з Flatpak</a> або са спісу пакетаў вашай сістэмы.\"\nDockCloseWarning.Title=\"Закрыццё док-акна\"\nDockCloseWarning.Text=\"Вы толькі што закрылі док-акно. Калі вы жадаеце зноў яго адкрыць, скарыстайцеся меню «Докі» ў радку меню.\"\nExtraBrowsers=\"Уласные докі браўзера\"\nExtraBrowsers.Info=\"Дадайце докі, даўшы ім імя і URL, потым націсніце «Ужыць» або «Закрыць», каб адкрыць докі. Вы можаце дадаваць ці выдаляць докі ў любы час.\"\nExtraBrowsers.DockName=\"Назва дока\"\nAuth.Authing.Title=\"Уваход...\"\nAuth.Authing.Text=\"Уваход у акаўнт %1, пачакайце...\"\nAuth.AuthFailure.Title=\"Памылка аўтэнтыфікацыі\"\nAuth.AuthFailure.Text=\"Не атрымалася ўвайсці ў %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Патрабуецца ўваход\"\nAuth.InvalidScope.Text=\"Патрабаванні для ўваходу ў %1 былі зменены. Некаторыя функцыі могуць быць недаступнымі.\"\nAuth.LoadingChannel.Title=\"Загрузка інфармацыі аб канале...\"\nAuth.LoadingChannel.Text=\"Загрузка інфармацыі аб канале %1, пачакайце...\"\nAuth.LoadingChannel.Error=\"Немагчыма атрымаць інфармацыю аб канале.\"\nAuth.ChannelFailure.Title=\"Не атрымалася загрузіць інфармацыю аб канале\"\nAuth.ChannelFailure.Text=\"Не атрымалася загрузіць інфармацыю аб канале %1\\n\\n%2: %3\"\nAuth.Chat=\"Чат\"\nAuth.StreamInfo=\"Звесткі пра плынь\"\nTwitchAuth.Stats=\"Статыстыка Twitch\"\nTwitchAuth.Feed=\"Стужка актыўнасці Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Не атрымалася запытаць ключ трансляцыі.\"\nTwitchAuth.TwoFactorFail.Text=\"OBS не можа злучыцца з вашым уліковым запісам Twitch. Пераканайцеся, што ў <a href='https://www.twitch.tv/settings/security'>наладах бяспекі Twitch</a> наладжана двухфактарная аўтэнтыфікацыя, бо яна патрабуецца для трансляцыі.\"\nRestreamAuth.Channels=\"Каналы Restream\"\nCopy.Filters=\"Капіраваць фільтры\"\nPaste.Filters=\"Уставіць фільтры\"\nBrowserPanelInit.Title=\"Запуск браўзера…\"\nBrowserPanelInit.Text=\"Ідзе запуск браўзера, пачакайце...\"\nBandwidthTest.Region=\"Рэгіён\"\nBandwidthTest.Region.US=\"Злучаныя Штаты\"\nBandwidthTest.Region.EU=\"Еўропа\"\nBandwidthTest.Region.Asia=\"Азія\"\nBandwidthTest.Region.Other=\"Іншы\"\nBasic.AutoConfig=\"Майстар аўтаканфігурацыі\"\nBasic.AutoConfig.ApplySettings=\"Ужыць налады\"\nBasic.AutoConfig.StartPage=\"Інфармацыя аб выкарыстанні\"\nBasic.AutoConfig.StartPage.SubTitle=\"Выберыце, для чаго вы хочаце выкарыстоўваць праграму\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Аптымізаваць для вяшчання, запіс на другім плане\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Аптымізаваць толькі для запісу, я не буду весці трансляцыі\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Я буду толькі выкарыстоўваць віртуальную камеру\"\nBasic.AutoConfig.VideoPage=\"Налады відэа\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Выберыце налады відэа, якія вы хочаце выкарыстоўваць\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Выкарыстоўваць бягучую (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Дысплэй %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Выкарыстоўваць бягучую (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 або 30, але па магчымасці аддаваць перавагу 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 або 30, але аддаваць перавагу больш высокай раздзяляльнасці\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Заўвага: Раздзяляльнасць палатна (асноўная) неабавязкова супадае з раздзяляльнасцю, з якой вы будзеце весці трансляцыю або запіс. Фактычная раздзяляльнасць стрыма/запісу можа быць зменшана адносна раздзяляльнасці палатна, каб паменшыць выкарыстанне рэсурсаў або патрабаванні да бітрэйту.\"\nBasic.AutoConfig.StreamPage=\"Інфармацыя пра плынь\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Увядзіце звесткі пра плынь\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Далучыць уліковы запіс (рэкамендуецца)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Адлучыць уліковы запіс\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Адлучыць акаўнт?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Гэта змена будзе ўжыта неадкладна. Ці вы ўпэўненыя, што хочаце адлучыць свой акаўнт?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Атрымаць ключ стрыма\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Дакладней\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Скарыстацца ключом трансляцыі\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Скарыстацца ключом трансляцыі (для прасунутых карыстальнікаў)\"\nBasic.AutoConfig.StreamPage.Service=\"Сэрвіс\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Паказаць усе\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Уласны...\"\nBasic.AutoConfig.StreamPage.Server=\"Сервер\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Ключ трансляцыі\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: увядзіце пароль шыфравання.\\nRTMP: увядзіце ключ, дадзены сэрвісам.\\nSRT: увядзіце ID стрыма, калі сэрвіс яго выкарыстоўвае.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Ключ кадавальніка\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Токен карыстальніка\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Звязаны ўліковы запіс\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Ацаніць бітрэйт з дапамогай тэста паласы прапускання (можа заняць некалькі хвілін)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Даваць перавагу апаратнаму кадаванню\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Апаратнае кадаванне менш за ўсё нагружае ЦП, але для дасягнення такога ж узроўню якасці можа патрабаваць большы бітрэйт.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Папярэджанне\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Тэст паласы прапускання будзе вяшчаць на ваш канал выпадковую відэаінфармацыю без гуку. Калі ёсць магчымасць, рэкамендуецца часова адключыць запіс мінулых трансляцый і пазначыць стрым як прыватны да канца тэсту. Працягнуць?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Тэсціраванне: %1\"\nBasic.AutoConfig.TestPage=\"Канчатковыя вынікі\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Цяпер праграма выконвае шэраг тэстаў, каб ацаніць найлепшыя налады\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Тэсціраванне завершана\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Ідзе тэст паласы прапускання, гэта можа заняць некалькі хвілін...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Пратакол вываду гэтага сэрвісу не быў знойдзены\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Злучэнне з: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Немагчыма падключыцца да ніводнага сервера. Праверце злучэнне з інтэрнэтам і паўтарыце спробу.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Праверка паласы прапускання для: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Праверка кадавальніка плыні, гэта можа заняць некаторы час...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Праверка кадавальніка запісу, гэта можа заняць некаторы час...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Немагчыма запусціць кадавальнік\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Праверка %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Кадавальнік плыні\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Кадавальнік запісу\"\nBasic.AutoConfig.TestPage.Result.Header=\"Праграмма вызначыла, што вам найбольш падыходзяць наступныя налады:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Каб карыстацца імі, націсніце «Ужыць налады». Каб змяніць канфігурацыю майстра і паўтарыць спробу, націсніце «Назад». Каб задаць налады ўручную, націсніце «Скасаваць» і адкрыйце налады.\"\nBasic.AutoConfig.Info=\"Майстар аўтаканфігурацыі вызначыць найлепшыя налады, зыходзячы з канфігурацыі вашага ПК і хуткасці інтэрнэту.\"\nBasic.AutoConfig.RunAnytime=\"Вы можаце запусціць яго ў любы час праз меню «Інструменты».\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Плынёвая раздзяляльнасць (маштабаваная)\"\nBasic.Stats=\"Статыстыка\"\nBasic.Stats.CPUUsage=\"Выкарыстанне працэсара\"\nBasic.Stats.HDDSpaceAvailable=\"Даступная дыскавая прастора\"\nBasic.Stats.MemoryUsage=\"Выкарыстанне памяці\"\nBasic.Stats.AverageTimeToRender=\"Сярэдні час рэндэрынгу кадра\"\nBasic.Stats.SkippedFrames=\"Прапушчаныя кадры з-за затрымкі кадавання\"\nBasic.Stats.MissedFrames=\"Прапушчаныя кадры з-за затрымкі рэндэрынгу\"\nBasic.Stats.Output.Stream=\"Стрым\"\nBasic.Stats.Output.Recording=\"Запіс\"\nBasic.Stats.Status=\"Стан\"\nBasic.Stats.Status.Recording=\"Запіс\"\nBasic.Stats.Status.Live=\"У ЭФІРЫ\"\nBasic.Stats.Status.Reconnecting=\"Паўторнае злучэнне\"\nBasic.Stats.Status.Inactive=\"Неактыўны\"\nBasic.Stats.Status.Active=\"Актыўна\"\nBasic.Stats.DroppedFrames=\"Прапушчаныя кадры (сетка)\"\nBasic.Stats.MegabytesSent=\"Сума адпраўленых даных\"\nBasic.Stats.Bitrate=\"Бітрэйт\"\nBasic.Stats.DiskFullIn=\"Да запаўнення дыска\"\nBasic.Stats.DiskFullIn.Text=\"%1 гадз, %2 хв\"\nBasic.Stats.ResetStats=\"Абнавіць статыстку\"\nResetUIWarning.Title=\"Ці вы ўпэўненыя, што хочаце скінуць налады інтэрфейса?\"\nResetUIWarning.Text=\"Пасля скіду налад дадатковыя докі будуць схаваны. Каб іх зноў было відаць, вам спатрэбіцца зрабіць іх бачнымі ў меню докаў.\\n\\nЦі вы ўпэўненыя, што хочаце скінуць налады інтэрфейса?\"\nUpdater.Title=\"Даступна абнаўленне\"\nUpdater.Text=\"Выйшла абнаўленне:\"\nUpdater.UpdateNow=\"Абнавіць зараз\"\nUpdater.RemindMeLater=\"Нагадаць пазней\"\nUpdater.Skip=\"Прапусціць версію\"\nUpdater.NoUpdatesAvailable.Title=\"Няма даступных абнаўленняў\"\nUpdater.NoUpdatesAvailable.Text=\"У дадзены момант няма даступных абнаўленняў\"\nUpdater.BranchNotFound.Title=\"Канал абнаўлення выдалены\"\nUpdater.BranchNotFound.Text=\"Выбраны канал абнаўлення больш не даступны. Налады OBS скінуты да прадвызначаных.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Праверка цэласнасці недаступная\"\nUpdater.RepairButUpdatesAvailable.Text=\"Правесці праверку цэласнасці файлаў магчыма толькі ў апошняй версіі праграмы. Каб абнавіць OBS, перайдзіце ў меню Даведка → Праверыць абнаўленні.\"\nUpdater.RepairConfirm.Title=\"Пацвердзіць праверку цэласнасці\"\nUpdater.RepairConfirm.Text=\"Пры праверцы цэласнасці правяраюцца ўсе файлы OBS і пашкоджаныя або змененыя файлы спампоўваюцца зноў.\\n\\nЦі жадаеце працягнуць?\"\nUpdater.FailedToLaunch=\"Не атрымалася запусціць інструмент абнаўлення\"\nQuickTransitions.SwapScenes=\"Пасля пераходу мяняць месцамі сцэны пердапрагляду і праграмы\"\nQuickTransitions.SwapScenesTT=\"Мяняе месцамі сцэны перадпрагляду і праграмы пасля пераходу (калі зыходная сцэна праграмы яшчэ існуе).\\nЗмяненні, якія былі ўнесены ў зыходную сцэну праграмы, адроблены не будуць.\"\nQuickTransitions.DuplicateScene=\"Дубліраваць сцэну\"\nQuickTransitions.DuplicateSceneTT=\"Пры рэдагаванні сцэны дазваляе мяняць памеры і бачнасць крыніц без уплыву на вывад праграмы.\\nКаб рэдагаваць уласцівасці крыніц без уплыву на вывад праграмы, уключыце параметр «Дубліраваць крыніцы».\\nЗмяненне гэтага параметра скіне налады бягучай сцэны праграмы (калі яна яшчэ існуе).\"\nQuickTransitions.EditProperties=\"Дубліраваць крыніцы\"\nQuickTransitions.EditPropertiesTT=\"Пры рэдагаванні сцэны дазваляе мяняць уласцівасці крыніц без уплыву на вывад праграмы.\\nМожна выкарыстаць толькі пры ўключаным параметры «Дубліраваць сцэну».\\nНекаторыя крыніцы (напрыклад, захоп ці медыякрыніцы) не падтрымліваюць гэтую функцыю і не могуць быць адрэдагаваны асобна.\\nЗмяненне гэтага параметра скіне налады бягучай сцэны праграмы (калі яна яшчэ існуе).\\n\\nУвага: дубліраванне крыніц патрабуе большых сістэмных або відэарэсурсаў.\"\nQuickTransitions.HotkeyName=\"Хуткі пераход: %1\"\nBasic.AddTransition=\"Дадаць дапасаваны пераход\"\nBasic.RemoveTransition=\"Выдаліць дапасаваны пераход\"\nBasic.TransitionProperties=\"Уласцівасці перахода\"\nBasic.SceneTransitions=\"Выгляд пераходу\"\nBasic.TransitionDuration=\"Працягласць\"\nBasic.TogglePreviewProgramMode=\"Рэжым студыі\"\nBasic.EnablePreviewProgramMode=\"Уключыць рэжым студыі\"\nBasic.DisablePreviewProgramMode=\"Выключыць рэжым студыі\"\nUndo.Undo=\"Адрабіць\"\nUndo.Redo=\"Узнавіць\"\nUndo.Add=\"дадаванне «%1»\"\nUndo.Delete=\"выдаленне «%1»\"\nUndo.Rename=\"перайменаванне «%1»\"\nUndo.SceneCollection.Switch=\"пераключэнне на «%1»\"\nUndo.Item.Undo=\"Адрабіць %1\"\nUndo.Item.Redo=\"Узнавіць %1\"\nUndo.Sources.Multi=\"выдаленне %1 крыніц\"\nUndo.Filters=\"змяненне фільтраў у «%1»\"\nUndo.Filters.Paste.Single=\"устаўку фільтра «%1» у «%2»\"\nUndo.Filters.Paste.Multiple=\"капіяванне фільтраў з «%1» у «%2»\"\nUndo.Transform=\"змяненне трансфармацыі крыніц(ы) у «%1»\"\nUndo.Transform.Paste=\"устаўку налад трансфармацыі ў «%1»\"\nUndo.Transform.Rotate=\"паварот у «%1»\"\nUndo.Transform.Reset=\"скід трансфармацыі ў «%1»\"\nUndo.Transform.HFlip=\"паварот па гарызанталі ў «%1»\"\nUndo.Transform.VFlip=\"паварот па вертыкалі ў «%1»\"\nUndo.Transform.FitToScreen=\"умяшчэнне ў экран у «%1»\"\nUndo.Transform.StretchToScreen=\"расцягванне на ўвесь экран у «%1»\"\nUndo.Transform.Center=\"цэнтраванне па экране ў «%1»\"\nUndo.Transform.VCenter=\"вертыкальнае цэнтраванне па экране ў «%1»\"\nUndo.Transform.HCenter=\"гарызантальнае цэнтраванне па экране ў «%1»\"\nUndo.Volume.Change=\"змяненне гучнасці ў «%1»\"\nUndo.Volume.Mute=\"заглушэнне «%1»\"\nUndo.Volume.Unmute=\"уключэнне гуку крыніцы «%1»\"\nUndo.Balance.Change=\"змяненне баланса аўдыя для «%1»\"\nUndo.SyncOffset.Change=\"змяненне затрымкі гуку для «%1»\"\nUndo.MonitoringType.Change=\"змяненне праслухоўвання аўдыя для «%1»\"\nUndo.Mixers.Change=\"наладжванне мікшараў аўдыя для «%1»\"\nUndo.ForceMono.On=\"уключэнне мона-гуку для «%1»\"\nUndo.ForceMono.Off=\"адключэнне мона-гуку для «%1»\"\nUndo.Properties=\"змяненне ўласцівасцяў «%1»\"\nUndo.Scene.Duplicate=\"дубляванне сцэны «%1»\"\nUndo.ShowTransition=\"пераход паказу для «%1»\"\nUndo.HideTransition=\"пераход схавання для «%1»\"\nUndo.ShowSceneItem=\"паказ «%1» на «%2»\"\nUndo.HideSceneItem=\"хаванне «%1» на «%2»\"\nUndo.ReorderSources=\"перапарадкаванне крыніц на «%1»\"\nUndo.MoveUp=\"падняцце «%1» на «%2»\"\nUndo.MoveDown=\"апусканне «%1» на «%2»\"\nUndo.MoveToTop=\"падняцце «%1» у самы верх на «%2»\"\nUndo.MoveToBottom=\"апусканне «%1» у самы ніз на «%2»\"\nUndo.PasteSource=\"устаўку крыніц(ы) на «%1»\"\nUndo.PasteSourceRef=\"устаўку спасылкі(-ак) на крыніцу(-ы) на «%1»\"\nUndo.GroupItems=\"групаванне элементаў у «%1»\"\nTransitionNameDlg.Text=\"Увядзіце назву перахода\"\nTransitionNameDlg.Title=\"Назва перахода\"\nTitleBar.SafeMode=\"БЯСПЕЧНЫ РЭЖЫМ\"\nTitleBar.PortableMode=\"Партатыўны рэжым\"\nTitleBar.Profile=\"Профіль\"\nTitleBar.Scenes=\"Сцэны\"\nNameExists.Title=\"Назва ўжо існуе\"\nNameExists.Text=\"Назва ўжо выкарыстоўваецца.\"\nNoNameEntered.Title=\"Увядзіце правільную назву\"\nNoNameEntered.Text=\"Вы не можаце выкарыстоўваць пустыя назвы.\"\nConfirmStart.Title=\"Пачаць трансляцыю?\"\nConfirmStart.Text=\"Ці вы ўпэўненыя, што хочаце пачаць трансляцыю?\"\nConfirmStop.Title=\"Спыніць трансляцыю?\"\nConfirmStop.Text=\"Ці вы ўпэўненыя, што хочаце спыніць трансляцыю?\"\nConfirmStopRecord.Title=\"Спыніць запіс?\"\nConfirmStopRecord.Text=\"Ці вы ўпэўненыя, што хочаце спыніць запіс?\"\nConfirmBWTest.Title=\"Пачаць тэст паласы прапускання?\"\nConfirmBWTest.Text=\"OBS наладжана ў рэжыме тэста паласы прапускання. Гэты рэжым дазваляе праверыць сетку без пачынання эфіру. Пасля завяршэння тэста вам спатрэбіцца яго адключыць, каб вашы гледачы маглі бачыць вашу трансляцыю.\\n\\nХочаце працягнуць?\"\nConfirmExit.Title=\"Ідзе вывад\"\nConfirmExit.Text=\"OBS у цяперашні момант працуе. Усе бягучыя працэсы трансляцыі/запісу будуць спынены.\"\nConfirmRemove.Title=\"Пацвердзіць выдаленне\"\nConfirmRemove.Text=\"Ці вы ўпэўненыя, што хочаце выдаліць «%1»?\"\nConfirmRemove.TextMultiple=\"Ці вы ўпэўненыя, што хочаце выдаліць %1 элементы(-аў)?\"\nConfirmReset.Title=\"Скінуць налады\"\nConfirmReset.Text=\"Ці вы ўпэўненыя, што хочаце вярнуць усе змены да налад па змаўчанні?\"\nOutput.StartStreamFailed=\"Не атрымалася пачаць трансляцыю\"\nOutput.StartRecordingFailed=\"Не атрымалася пачаць запіс\"\nOutput.StartReplayFailed=\"Не атрымалася запусціць буфер паўтору\"\nOutput.StartVirtualCamFailed=\"Не атрымалася запусціць віртуальную камеру\"\nOutput.StartFailedGeneric=\"Не атрымалася пачаць вывад. Падрабязнасці праверце ў журнале адладкі.\\n\\nЗаўвага: Калі вы выкарыстоўваеце кадавальнікі NVENC або AMD, пераканайцеся, што вашы драйверы відэа абноўлены.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Захоўванне паўтораў падчас паўзы не магчымае\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Увага: Паўторы нельга захоўваць, калі запіс прыпынены\"\nOutput.ConnectFail.Title=\"Не атрымалася злучыцца\"\nOutput.ConnectFail.BadPath=\"Няправільны шлях або URL злучэння. Праверце, ці правільна вы наладзілі праграму.\"\nOutput.ConnectFail.ConnectFailed=\"Не атрымалася злучыцца з серверам\"\nOutput.ConnectFail.InvalidStream=\"Памылка атрымання доступу да вызначанага канала або ключа трансляцыі. Пераканайцеся ў правільнасці ключа трансляцыі. Калі ён правільны, маглі ўзнікнуць праблемы са злучэннем з серверам.\"\nOutput.ConnectFail.HdrDisabled=\"Вывад HDR для гэтага вываду ў цяперашні момант адключаны.\"\nOutput.ConnectFail.Error=\"Пры злучэнні з серверам узнікла нечаканая памылка. Больш інфармацыі даступна ў файле журнала.\"\nOutput.ConnectFail.Disconnected=\"Кліент адлучаны ад сервера.\"\nOutput.StreamEncodeError.Title=\"Памылка кадавання\"\nOutput.StreamEncodeError.Msg=\"Падчас стрыма ўзнікла памылка кадавальніка.\"\nOutput.StreamEncodeError.Msg.LastError=\"Падчас стрыма ўзнікла памылка кадавальніка:<br><br>%1\"\nOutput.RecordFail.Title=\"Не атрымалася пачаць запіс\"\nOutput.RecordFail.Unsupported=\"Выбраны фармат вываду альбо не падтрымліваецца, альбо не падтрымлівае больш за адну аўдыядарожку. Праверце налады і паўтарыце спробу.\"\nOutput.RecordNoSpace.Title=\"Недастаткова месца на дыску\"\nOutput.RecordNoSpace.Msg=\"Для працягвання запісу на дыску недастаткова месца.\"\nOutput.RecordError.Title=\"Памылка запісу\"\nOutput.RecordError.Msg=\"Падчас запісвання ўзнікла невядомая памылка.\"\nOutput.RecordError.EncodeErrorMsg=\"Падчас запісвання ўзнікла памылка кадавальніка.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Падчас запісвання ўзнікла памылка кадавальніка:<br><br>%1\"\nOutput.BadPath.Title=\"Памылковы шлях\"\nOutput.BadPath.Text=\"Немагчыма адкрыць наладжаную папку запісу. Праверце шлях запісу: Налады → Вывад → Запіс.\"\nOutput.NoBroadcast.Title=\"Трансляцыя не наладжана\"\nOutput.NoBroadcast.Text=\"Перад тым, як пачаць стрым, наладзьце трансляцыю.\"\nOutput.BroadcastStartFailed=\"Не атрымалася пачаць трансляцыю\"\nOutput.BroadcastStopFailed=\"Не атрымалася спыніць трансляцыю\"\nLogUploadDialog.Title=\"Запампоўванне журнала OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Перш як запампоўваць файлы, азнаёмцеся з <a href='https://obsproject.com/privacy-policy'>Палітыкай прыватнасці</a> і асабліва з яе раздзелам, які мае дачыненне да запампоўвання файлаў.\"\nLogUploadDialog.Labels.Progress=\"Журнал запампоўваецца. Пачакайце…\"\nLogUploadDialog.Labels.Description.AppLog=\"Ваш файл журнала быў паспяхова запампаваны. Цяпер вы можаце абагуліць спасылку да яго для адладкі або атрымання падтрымкі.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Ваша справаздача аб збоях была паспяхова запампавана. Цяпер вы можаце абагуліць спасылку да яе для адладкі або атрымання падтрымкі.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Запампаваць\"\nLogUploadDialog.Buttons.CopyURL=\"Скапіяваць спасылку\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Прааналізаваць журнал\"\nLogUploadDialog.Buttons.RetryButton=\"Паўтарыць\"\nLogUploadDialog.Errors.Template=\"Падчас запампоўвання файла ўзнікла памылка:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Файл для запампоўвання не знойдзены або пусты.\"\nRemux.SourceFile=\"Файл запісу OBS\"\nRemux.TargetFile=\"Канчатковы файл\"\nRemux.Remux=\"Перапакаваць\"\nRemux.Stop=\"Спыніць перапакоўванне\"\nRemux.ClearFinished=\"Прыбраць завершаныя пункты\"\nRemux.ClearAll=\"Ачысціць табліцу\"\nRemux.OBSRecording=\"Файл запісу OBS\"\nRemux.FinishedTitle=\"Перапакаванне завершана\"\nRemux.Finished=\"Запіс перапакаваны\"\nRemux.FinishedError=\"Запіс перапакаваны, але файл можа быць незавершаным\"\nRemux.SelectRecording=\"Выберыце файл запісу OBS…\"\nRemux.SelectTarget=\"Выберыце канчатковы файл…\"\nRemux.FileExistsTitle=\"Канчатковыя файлы ўжо існуюць\"\nRemux.FileExists=\"Гэтыя канчатковыя файлы ўжо існуюць. Ці вы хочаце іх замяніць?\"\nRemux.ExitUnfinishedTitle=\"Перапакоўванне не завершана\"\nRemux.ExitUnfinished=\"Перапакоўванне не завершана. Яго спыненне можа фатальна пашкодзіць канчатковы файл.\\nЦі вы ўпэўненыя, што хочаце спыніць перапакоўванне?\"\nRemux.HelpText=\"Перацягніце файлы, якія вы хочаце перапакаваць, у гэтае акно або выберыце пусты пункт у першым слупку для агляду.\"\nRemux.NoFilesAddedTitle=\"Файл не дададзены\"\nRemux.NoFilesAdded=\"Файлы для перапакавання не былі дададзены. Перацягніце папку, якая змяшчае адно або некалькі відэа.\"\nMissingFiles=\"Страчаныя файлы\"\nMissingFiles.MissingFile=\"Страчаны файл\"\nMissingFiles.NewFile=\"Новы файл\"\nMissingFiles.HelpText=\"Некаторыя файлы з часу вашага апошняга выкарыстання OBS былі страчаны.\"\nMissingFiles.Clear=\"<ачышчаны>\"\nMissingFiles.NumFound=\"Знойдзена %1 з %2\"\nMissingFiles.Search=\"Пошук каталога…\"\nMissingFiles.SelectFile=\"Выбраць шрыфт…\"\nMissingFiles.SelectDir=\"Выберыце папку для пошуку\"\nMissingFiles.State=\"Стан\"\nMissingFiles.Missing=\"Адсутнічае\"\nMissingFiles.Replaced=\"Заменены\"\nMissingFiles.Cleared=\"Ачышчаны\"\nMissingFiles.Found=\"Знойдзены\"\nMissingFiles.AutoSearch=\"Знойдзены іншыя супадзенні\"\nMissingFiles.AutoSearchText=\"Праграма знайшла іншыя супадзенні файлаў, якія адсутнічаюць. Ці жадаеце іх дадаць?\"\nMissingFiles.NoMissing.Title=\"Праверка файлаў, якія адсутнічаюць\"\nMissingFiles.NoMissing.Text=\"Усе файлы прысутнічаюць.\"\nMacPermissions.MenuAction=\"Дазволы праграмы…\"\nMacPermissions.Title=\"Дазволы праграмы\"\nMacPermissions.Description=\"Для працы пэўных функцый OBS Studio патрабуецца ваш дазвол. Выконваць гэтае патрабаванне рэкамендуецца, але неабавязкова для выкарыстання праграмы. Вы заўжды можаце даць дазволы пазней.\"\nMacPermissions.Description.OpenDialog=\"Гэтае дыялогавае акно можна адкрыць зноў праз меню OBS Studio.\"\nMacPermissions.AccessGranted=\"Доступ дадзены\"\nMacPermissions.RequestAccess=\"Запыт доступу\"\nMacPermissions.OpenPreferences=\"Адкрыць налады %1\"\nMacPermissions.Item.ScreenRecording=\"Запіс экрана\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS патрабуецца гэты дазвол, каб запісваць выяву на экране.\"\nMacPermissions.Item.Camera=\"Камера\"\nMacPermissions.Item.Camera.Details=\"Гэты дазвол патрабуецца для захопу выявы з вэб-камеры або карты захопу.\"\nMacPermissions.Item.Microphone=\"Мікрафон\"\nMacPermissions.Item.Microphone.Details=\"OBS патрабуецца гэты дазвол, каб запісваць гук з мікрафона або вонкавай аўдыяпрылады.\"\nMacPermissions.Item.InputMonitoring=\"Маніторынг уводу\"\nMacPermissions.Item.InputMonitoring.Details=\"OBS патрабуецца гэты дазвол, каб рэагаваць на спалучэнні клавіш, калі праграма працуе ў фонавым рэжыме.\"\nMacPermissions.Item.Accessibility=\"Спецыяльныя магчымасці\"\nMacPermissions.Item.Accessibility.Details=\"У старэйшых версіях праграма можа знаходзіцца ў раздзеле «Спецыяльныя магчымасці», а не «Маніторынг уводу».\"\nMacPermissions.Continue=\"Працягнуць\"\nSourceLeak.Title=\"Памылка ачысткі крыніцы\"\nSourceLeak.Text=\"Пры змяненні некаторых калекцый сцэн узнікла праблема, і некаторыя крыніцы выгрузіць не атрымалася. Звычайна гэтая праблема выклікаецца няправільным вызваленнем рэсурсаў плагінамі. Упэўніцеся, што ў вас усталяваны апошнія версіі плагінаў.\\n\\nКаб не дапусціць далейшага магчымага пашкоджання даных, OBS Studio будзе закрыта.\"\nBasic.DesktopDevice1=\"Гук камп'ютара\"\nBasic.DesktopDevice2=\"Гук камп'ютара 2\"\nBasic.AuxDevice1=\"Мікрафон/дад.\"\nBasic.AuxDevice2=\"Мікрафон/дад. 2\"\nBasic.AuxDevice3=\"Мікрафон/дад. 3\"\nBasic.AuxDevice4=\"Мікрафон/дад. 4\"\nBasic.Scene=\"Сцэна\"\nBasic.DisplayCapture=\"Захоп экрана\"\nBasic.Main.PreviewConextMenu.Enable=\"Уключыць перадпрагляд\"\nBasic.Main.Preview.Disable=\"Выключыць перадпрагляд\"\nScaleFiltering=\"Фільтр маштабавання\"\nScaleFiltering.Point=\"Пунктавы\"\nScaleFiltering.Bilinear=\"Білінейны\"\nScaleFiltering.Bicubic=\"Бікубічны\"\nScaleFiltering.Lanczos=\"Ланцош\"\nScaleFiltering.Area=\"Змешванне пікселяў\"\nBlendingMethod=\"Метад змешвання\"\nBlendingMethod.Default=\"Прадвызначаны\"\nBlendingMethod.SrgbOff=\"Без SRGB\"\nBlendingMode=\"Рэжым змешвання\"\nBlendingMode.Normal=\"Звычайны\"\nBlendingMode.Additive=\"Дадаванне\"\nBlendingMode.Subtract=\"Адніманне\"\nBlendingMode.Screen=\"Экран\"\nBlendingMode.Multiply=\"Памнажэнне\"\nBlendingMode.Lighten=\"Асвятленне\"\nBlendingMode.Darken=\"Зацямненне\"\nDeinterlacing=\"Прадухіленне празрадковасці\"\nDeinterlacing.Discard=\"Адхіленне\"\nDeinterlacing.TopFieldFirst=\"Спачатку няпарныя радкі\"\nDeinterlacing.BottomFieldFirst=\"Спачатку парныя радкі\"\nVolControl.SliderUnmuted=\"Рэгулятар гучнасці для «%1»:\"\nVolControl.SliderMuted=\"Рэгулятар гучнасці для «%1»: (заглушаны)\"\nVolControl.Mute=\"Заглушыць: %1\"\nVolControl.Properties=\"Уласцівасці: %1\"\nVolControl.UnassignedWarning.Title=\"Крыніца гуку не прызначана\"\nVolControl.UnassignedWarning.Text=\"Крыніца «%1» не прызначана да ніводнай аўдыядарожкі, і на стрымах і запісах гук ад яе чуваць не будзе.\\n\\nКаб прызначыць крыніцу гуку да дарожкі, адкрыйце пашыраныя ўласцівасці гуку, націснуўшы правую кнопку мышы або значок у выглядзе шасцерань на доку мікшара.\"\nBasic.Main.AddSceneDlg.Title=\"Дадаць сцэну\"\nBasic.Main.AddSceneDlg.Text=\"Увядзіце назву сцэны\"\nBasic.Main.DefaultSceneName.Text=\"Сцэна %1\"\nBasic.Main.AddSceneCollection.Title=\"Дадаць калекцыю сцэн\"\nBasic.Main.AddSceneCollection.Text=\"Увядзіце назву калекцыі сцэн\"\nBasic.Main.RenameSceneCollection.Title=\"Перайменаваць калекцыю сцэн\"\nBasic.Main.RemigrateSceneCollection.Title=\"Абнаўленне раздзяляльнасці калекцыі сцэн\"\nBasic.Main.RemigrateSceneCollection.Text=\"Ці жадаеце вы абнавіць раздзяляльнасць сцэны «%1», каб яна супадала з асноўнай раздзяляльнасцю профілю (%2x%3)?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Немагчыма абнавіць раздзяляльнасць калекцыі сцэн, пакуль працуюць вывады.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Не атрымалася абнавіць раздзяляльнасць калекцыі сцэн. Арыгінальная раздзяляльнасць невядомая.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Скід немагчымы: не атрымалася змяніць раздзяляльнасць OBS.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Скід немагчымы: бягучая раздзяляльнасць супадае з асноўнай раздзяляльнасцю калекцыі сцэн.\"\nAddProfile.Title=\"Дадаць профіль\"\nAddProfile.Text=\"Увядзіце назву профілю\"\nAddProfile.WizardCheckbox=\"Паказаць майстар аўтаканфігурацыі\"\nRenameProfile.Title=\"Перайменаваць профіль\"\nBasic.Main.MixerRename.Title=\"Перайменаваць крыніцу гуку\"\nBasic.Main.MixerRename.Text=\"Увядзіце назву крыніцы гуку\"\nBasic.Main.PreviewDisabled=\"Перадпрагляд адключаны\"\nBasic.SourceSelect=\"Стварыць/выбраць крыніцу\"\nBasic.SourceSelect.CreateNew=\"Стварыць новую\"\nBasic.SourceSelect.AddExisting=\"Дадаць існуючую\"\nBasic.SourceSelect.AddVisible=\"Зрабіць крыніцу бачнай\"\nBasic.Main.Sources.Visibility=\"Бачнасць\"\nBasic.Main.Sources.VisibilityDescription=\"Мяняе бачнасць элемента «%1» на палатне\"\nBasic.Main.Sources.Lock=\"Зафіксаваць\"\nBasic.Main.Sources.LockDescription=\"Фіксуе пазіцыю і памер элемента «%1» на палатне\"\nBasic.PropertiesWindow=\"Уласцівасці: %1\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (аўтавыбар: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Выбраць колер\"\nBasic.PropertiesWindow.SelectFont=\"Выбраць шрыфт\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Выбраць шрыфт\"\nBasic.PropertiesWindow.ConfirmTitle=\"Змяніць налады?\"\nBasic.PropertiesWindow.Confirm=\"Ёсць незахаваныя змены. Вы хочаце захаваць іх?\"\nBasic.PropertiesWindow.NoProperties=\"Няма даступных уласцівасцяў\"\nBasic.PropertiesWindow.AddFiles=\"Дадаць файлы\"\nBasic.PropertiesWindow.AddDir=\"Дадаць каталог\"\nBasic.PropertiesWindow.AddURL=\"Дадаць шлях/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Дадаць папку да «%1»\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Дадаць файлы да «%1»\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Дадаць запіс да «%1»\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Рэдагаваць запіс з «%1»\"\nBasic.PropertiesView.FPS.Simple=\"Простыя значэнні FPS\"\nBasic.PropertiesView.FPS.Rational=\"Рацыянальныя значэнні FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Правільныя дыяпазоны FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"Адкрыць спасылку ў прадвызначаным вэб-браўзеры?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Адкрыць URL\"\nBasic.InteractionWindow=\"Узаемадзеянне з элементам «%1»\"\nBasic.StatusBar.Reconnecting=\"Кліент адлучаны, перазлучэнне праз %2 с (%1 спроба)\"\nBasic.StatusBar.AttemptingReconnect=\"Паўторнае злучэнне… (спроба %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Паспяховае паўторнае злучэнне\"\nBasic.StatusBar.Delay=\"Затрымка (%1 сек)\"\nBasic.StatusBar.DelayStartingIn=\"Затрымка (пачнецца праз %1 сек)\"\nBasic.StatusBar.DelayStoppingIn=\"Затрымка (скончыцца праз %1 сек)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Затрымка (скончыцца праз %1 сек, пачнецца праз %2 сек)\"\nBasic.StatusBar.RecordingSavedTo=\"Запіс захаваны ў «%1»\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Буфер паўтору захаваны ў «%1»\"\nBasic.StatusBar.ScreenshotSavedTo=\"Скрыншот захаваны ў «%1»\"\nBasic.StatusBar.AutoRemuxedTo=\"Запіс аўтаматычна перапакаваны ў «%1»\"\nBasic.Filters=\"Фільтры\"\nBasic.Filters.AsyncFilters=\"Фільтры аўдыя/відэа\"\nBasic.Filters.AudioFilters=\"Фільтры аўдыя\"\nBasic.Filters.EffectFilters=\"Фільтры эфектаў\"\nBasic.Filters.Title=\"Фільтры для «%1»\"\nBasic.Filters.AddFilter.Title=\"Назва фільтра\"\nBasic.Filters.AddFilter.Text=\"Вызначце назву фільтра\"\nBasic.TransformWindow=\"Змяненне памеру элемента сцэны\"\nBasic.TransformWindow.Position=\"Пазіцыя\"\nBasic.TransformWindow.PositionX=\"Пазіцыя па X\"\nBasic.TransformWindow.PositionY=\"Пазіцыя па Y\"\nBasic.TransformWindow.Rotation=\"Арыентацыя\"\nBasic.TransformWindow.Size=\"Памер\"\nBasic.TransformWindow.Width=\"Шырыня\"\nBasic.TransformWindow.Height=\"Вышыня\"\nBasic.TransformWindow.Alignment=\"Выраўноўванне пазіцыі\"\nBasic.TransformWindow.BoundsType=\"Тып абмежавальніка\"\nBasic.TransformWindow.BoundsAlignment=\"Выраўноўванне абмежавальніка\"\nBasic.TransformWindow.Bounds=\"Памер абмежавальніка\"\nBasic.TransformWindow.BoundsWidth=\"Шырыня абмежавальніка\"\nBasic.TransformWindow.BoundsHeight=\"Вышыня абмежавальніка\"\nBasic.TransformWindow.CropToBounds=\"Абрэзаць да абмежавальніка\"\nBasic.TransformWindow.Crop=\"Абрэзаць\"\nBasic.TransformWindow.CropLeft=\"Абрэзаць злева\"\nBasic.TransformWindow.CropRight=\"Абрэзаць справа\"\nBasic.TransformWindow.CropTop=\"Абрэзаць уверсе\"\nBasic.TransformWindow.CropBottom=\"Абрэзаць унізе\"\nBasic.TransformWindow.Alignment.TopLeft=\"Уверсе злева\"\nBasic.TransformWindow.Alignment.TopCenter=\"Уверсе па цэнтры\"\nBasic.TransformWindow.Alignment.TopRight=\"Уверсе справа\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Злева ў цэнтры\"\nBasic.TransformWindow.Alignment.Center=\"Па цэнтры\"\nBasic.TransformWindow.Alignment.CenterRight=\"Справа па цэнтры\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Унізе злева\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Унізе па цэнтры\"\nBasic.TransformWindow.Alignment.BottomRight=\"Унізе справа\"\nBasic.TransformWindow.BoundsType.None=\"Без меж\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Толькі максімальны памер\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Маштабаваць да ўнутранай межы\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Маштабаваць да вонкавай межы\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Маштабаваць да шырыні межы\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Маштабаваць да вышыні межы\"\nBasic.TransformWindow.BoundsType.Stretch=\"Расцягваць да меж\"\nBasic.TransformWindow.Title=\"Трансфармацыя: %1\"\nBasic.TransformWindow.NoSelectedSource=\"Крыніца не выбрана\"\nBasic.Main.AddSourceHelp.Title=\"Не атрымалася дадаць крыніцу\"\nBasic.Main.AddSourceHelp.Text=\"Каб дадаць крыніцу, неабходна стварыць прынамсі адну сцэну.\"\nBasic.Main.Scenes=\"Сцэны\"\nBasic.Main.Sources=\"Крыніцы\"\nBasic.Main.Source=\"Крыніца\"\nBasic.Main.Controls=\"Пульт\"\nBasic.Main.PreparingStream=\"Падрыхтоўка...\"\nBasic.Main.Connecting=\"Злучэнне…\"\nBasic.Main.StartRecording=\"Пачаць запіс\"\nBasic.Main.StartReplayBuffer=\"Запусціць буфер паўтору\"\nBasic.Main.SaveReplay=\"Захаваць паўтор\"\nBasic.Main.StartStreaming=\"Пачаць стрым\"\nBasic.Main.StartBroadcast=\"Пачаць эфір\"\nBasic.Main.StartVirtualCam=\"Запусціць віртуальную камеру\"\nBasic.Main.StopRecording=\"Спыніць запіс\"\nBasic.Main.PauseRecording=\"Прыпыніць запіс\"\nBasic.Main.UnpauseRecording=\"Працягнуць запіс\"\nBasic.Main.SplitFile=\"Раздзяліць файл запісу\"\nBasic.Main.AddChapterMarker=\"Дадаць метку главы (гібрыдныя фарматы MP4 і MOV)\"\nBasic.Main.StoppingRecording=\"Спыненне запісу…\"\nBasic.Main.StopReplayBuffer=\"Спыніць буфер паўтору\"\nBasic.Main.StoppingReplayBuffer=\"Спыненне буфера паўтору…\"\nBasic.Main.SetupBroadcast=\"Кіраванне трансляцыяй\"\nBasic.Main.StopStreaming=\"Спыніць стрым\"\nBasic.Main.StopBroadcast=\"Завяршыць трансляцыю\"\nBasic.Main.AutoStopEnabled=\"(Аўтаспыненне)\"\nBasic.Main.StoppingStreaming=\"Спыненне стрыма…\"\nBasic.Main.ForceStopStreaming=\"Выключыць трансляцыю (зараз жа)\"\nBasic.Main.ShowContextBar=\"Паказаць панэль крыніцы\"\nBasic.Main.HideContextBar=\"Схвавць панэль крыніцы\"\nBasic.Main.StopVirtualCam=\"Спыніць віртуальную камеру\"\nBasic.Main.Group=\"Група %1\"\nBasic.Main.GroupItems=\"Згрупаваць выбраныя элементы\"\nBasic.Main.Ungroup=\"Разгрупаваць\"\nBasic.Main.GridMode=\"Паказаць сеткай\"\nBasic.Main.ListMode=\"Паказаць спісам\"\nBasic.Main.VirtualCamConfig=\"Наладзіць віртуальную камеру\"\nBasic.VCam.VirtualCamera=\"Віртуальная камера\"\nBasic.VCam.OutputType=\"Тып вываду\"\nBasic.VCam.OutputSelection=\"Выбар вываду\"\nBasic.VCam.OutputType.Program=\"Праграмны (прадвызначаны)\"\nBasic.VCam.OutputSelection.NoSelection=\"Для гэтага тыпу вываду выбар недаступны\"\nBasic.VCam.RestartWarning=\"Для ўжывання гэтай змены віртуальная камера будзе перазапушчана\"\nBasic.MainMenu.File=\"Ф&айл\"\nBasic.MainMenu.File.Export=\"Экспарт\"\nBasic.MainMenu.File.Import=\"Імпарт\"\nBasic.MainMenu.File.ShowRecordings=\"Па&казаць запісы\"\nBasic.MainMenu.File.Remux=\"Перапакавац&ь запісы\"\nBasic.MainMenu.File.Settings=\"Налад&ы\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Паказаць папку наладаў\"\nBasic.MainMenu.File.ShowProfileFolder=\"Паказаць папку профілю\"\nBasic.MainMenu.File.ShowMissingFiles=\"Праверыць адсутнасць файлаў\"\nBasic.MainMenu.File.Exit=\"Выйсці\"\nBasic.MainMenu.Edit=\"Праўка\"\nBasic.MainMenu.Edit.LockPreview=\"Зафіксаваць пера&дпрагляд\"\nBasic.MainMenu.Edit.Scale=\"Маштаб перадпрагляду\"\nBasic.MainMenu.Edit.Scale.Window=\"Маштабаваць да памеру акна\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Палатно (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Вывад (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Маштабаваная (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Павялічыць\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Паменшыць\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Скінуць маштаб\"\nBasic.MainMenu.Edit.Transform=\"Трансфармаваць\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Змяніць трансфармацыю\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Скапіяваць трансфармацыю\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Уставіць трансфармацыю\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"С&кінуць трансфармацыю\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Павярнуць на 90° па ходу гадзіннікавай стрэлкі\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Павярнуць на 90° супраць гадзіннікавай стрэлкі\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Павярнуць на 180°\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Адбіць па га&рызанталі\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Адбіць па вертыкалі\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Умясціць у экр&ан\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Расцягнуць на ўвесь экран\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Цэнтраваць на экране\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Цэнтраваць па вертыкалі\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Цэнтраваць па гарызанталі\"\nBasic.MainMenu.Edit.Order=\"Упарадкаваць\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Вышэй\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Ніжэй\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"У самы в&ерх\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"У самы н&із\"\nBasic.MainMenu.Edit.AdvAudio=\"Пашыраныя ўласцівасці гуку\"\nBasic.MainMenu.View=\"Выгляд\"\nBasic.MainMenu.View.Toolbars=\"Панэлі\"\nBasic.MainMenu.View.ListboxToolbars=\"Док-панэлі інструментаў\"\nBasic.MainMenu.View.ContextBar=\"Панэль крыніцы\"\nBasic.MainMenu.View.SourceIcons=\"Значкі крыніц\"\nBasic.MainMenu.View.StatusBar=\"Радок стану\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Поўнаэкранны рэжым\"\nBasic.MainMenu.View.ResetUI=\"С&кінуць налады інтэрфейсу\"\nBasic.MainMenu.View.AlwaysOnTop=\"Зверху іншых вокнаў\"\nBasic.MainMenu.View.SceneListMode=\"Выгляд спісу сцэн\"\nBasic.MainMenu.Docks=\"Докі\"\nBasic.MainMenu.Docks.ResetDocks=\"С&кінуць докі\"\nBasic.MainMenu.Docks.LockDocks=\"Заблакіраваць &докі\"\nBasic.MainMenu.Docks.SideDocks=\"Докі н&а ўсю вышыню\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Ула&сныя докі браўзера…\"\nBasic.MainMenu.Import=\"Імпарт...\"\nBasic.MainMenu.Export=\"Экспарт...\"\nBasic.MainMenu.SceneCollection=\"Калекц&ыя сцэн\"\nBasic.MainMenu.Profile=\"Профіль\"\nBasic.MainMenu.Profile.Import=\"Імпартаваць профіль\"\nBasic.MainMenu.Profile.Export=\"Экспартаваць профіль\"\nBasic.MainMenu.SceneCollection.Import=\"Імпартаваць калекцыю сцэн\"\nBasic.MainMenu.SceneCollection.Export=\"Экспартаваць калекцыю сцэн\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Скінуць асноўную раздзяляльнасць\"\nBasic.MainMenu.SceneCollection.Migrate=\"Задаць асноўную раздзяляльнасць\"\nBasic.MainMenu.Profile.Exists=\"Гэты профіль ужо існуе\"\nBasic.MainMenu.Tools=\"Інструм&енты\"\nBasic.MainMenu.Help=\"Даведка\"\nBasic.MainMenu.Help.HelpPortal=\"Анлайн-даведка\"\nBasic.MainMenu.Help.Website=\"Наведа&ць вэб-сайт\"\nBasic.MainMenu.Help.Discord=\"Далучыцца да сервера ў &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Што новага\"\nBasic.MainMenu.Help.Logs=\"Файлы журнала\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Папка з файламі журнала\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Запампаваць апошні файл журнала\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"&Запампаваць папярэдні файл журнала\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Праглядзець апошні файл журнала\"\nBasic.MainMenu.Help.ReleaseNotes=\"Нататкі да рэліза\"\nBasic.MainMenu.Help.CheckForUpdates=\"Праверыць абнаўленні\"\nBasic.MainMenu.Help.Repair=\"Праверыць цэласнасць файлаў\"\nBasic.MainMenu.Help.RestartSafeMode=\"Перазапусціць у бяспечным рэжыме\"\nBasic.MainMenu.Help.RestartNormal=\"Перазапусціць у звычайным рэжыме\"\nBasic.MainMenu.Help.CrashLogs=\"Справаздачы аб збоях\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Папка са справаздачамі аб збоях\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"&Запампаваць папярэднюю справаздачу аб збоях\"\nBasic.MainMenu.Help.About=\"Пра праграму\"\nBasic.Settings.ProgramRestart=\"Каб налады ўвайшлі ў сілу, неабходна перазапусціць праграму.\"\nBasic.Settings.ConfirmTitle=\"Пацвярджэнне змяненняў\"\nBasic.Settings.Confirm=\"Некаторыя змены не захаваны. Захаваць іх?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 кантралюе некаторыя налады плыні\"\nBasic.Settings.General=\"Агульныя\"\nBasic.Settings.General.Language=\"Мова\"\nBasic.Settings.General.Updater=\"Абнаўленні\"\nBasic.Settings.General.UpdateChannel=\"Канал абнаўленняў\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Адкл.)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Прадвызначаны)\"\nBasic.Settings.General.EnableAutoUpdates=\"Правяраць абнаўленні пры запуску\"\nBasic.Settings.General.OpenStatsOnStartup=\"Адкрываць акно статыстыкі пры запуску\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Не запісваць вокны OBS\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Гэты параметр хавае ўсе вокны OBS Studio, якія не з'яўляюцца праектарамі, ад запісу і ўплывае на іншыя праграмы — \\nнапрыклад, для канферэнцый, дэманстрацыі экрана, аддаленай падтрымкі і на іншыя праграмы здымання экрана.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Гэты параметр хавае ўсе вокны OBS Studio, якія не з'яўляюцца праектарамі, ад запісу і ўплывае на іншыя праграмы — напрыклад, для канферэнцый, дэманстрацыі экрана, аддаленай падтрымкі і на іншыя праграмы здымання экрана.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Паказваць дыялогавае акно пацвярджэння перад пачаткам стрыма\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Паказваць дыялогавае акно пацвярджэння перад спыненнем стрыма\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Паказваць дыялогавае акно пацвярджэння перад спыненнем запісу\"\nBasic.Settings.General.Projectors=\"Праектары\"\nBasic.Settings.General.HideProjectorCursor=\"Хаваць курсор над праектарамі\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Паказваць праектары зверху іншых вокнаў\"\nBasic.Settings.General.Snapping=\"Прывязка і выраўноўванне крыніц\"\nBasic.Settings.General.ScreenSnapping=\"Прывязваць крыніцы да краю экрана\"\nBasic.Settings.General.CenterSnapping=\"Прывязваць крыніцы да цэнтра па гарызанталі і па вертыкалі\"\nBasic.Settings.General.SourceSnapping=\"Прывязваць крыніцы да іншых крыніц\"\nBasic.Settings.General.SnapDistance=\"Адчувальнасць прывязвання\"\nBasic.Settings.General.SpacingHelpers=\"Паказваць даўжыню да пунктаў прывязвання\"\nBasic.Settings.General.RecordWhenStreaming=\"Запісваць стрым\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Працягваць запіс пасля спынення стрыма\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Запускаць буфер паўтору падчас стрыма\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Не адключаць буфер паўтору пасля спынення стрыма\"\nBasic.Settings.General.SysTray=\"Вобласць апавяшчэнняў\"\nBasic.Settings.General.SysTrayWhenStarted=\"Пасля запуску згортваць у вобласць апавяшчэнняў\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Згортваць у вобласць апавяшчэнняў замест панэлі заданняў\"\nBasic.Settings.General.SaveProjectors=\"Захоўваць праектары пры выхадзе\"\nBasic.Settings.General.CloseExistingProjectors=\"Адзін поўнаэкранны праектар на адзін экран\"\nBasic.Settings.General.Preview=\"Перадпрагляд\"\nBasic.Settings.General.OverflowHidden=\"Хаваць выхад крыніц за межы палатна\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Выхад за межы заўжды бачны\"\nBasic.Settings.General.OverflowSelectionHidden=\"Паказваць выхад за межы, нават калі крыніца нябачная\"\nBasic.Settings.General.Importers=\"Імпарцёры\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Пры імпарце шукаць калекцыі сцэн па вядомым шляхам\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Пераход да сцэны пры двайным націсканні\"\nBasic.Settings.General.StudioPortraitLayout=\"Партрэтнае (вертыкальнае) размяшчэнне\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Паказваць тэкставыя абазначэнні перадпрагляду і праграмы\"\nBasic.Settings.General.Multiview=\"Мултыпрагляд\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Пераключэнне паміж сцэнамі націсканнем мышы\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Паказваць назвы сцэн\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Паказваць бяспечныя зоны (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Макет мультыпрагляду\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Гарызантальны, уверсе (8 сцэн)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Гарызантальны, унізе (8 сцэн)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Вертыкальны, злева (8 сцэн)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Вертыкальны, справа (8 сцэн)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Гарызантальны, уверсе (18 сцэн)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Гарызантальны, уверсе (24 сцэны)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Толькі сцэны (4 сцэны)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Толькі сцэны (9 сцэн)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Толькі сцэны (16 сцэн)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Толькі сцэны (25 сцэн)\"\nBasic.Settings.General.ChannelName.stable=\"Стабільны\"\nBasic.Settings.General.ChannelDescription.stable=\"Апошнія стабільныя рэлізы\"\nBasic.Settings.General.ChannelName.beta=\"Бэта-версіі / перадрэлізныя версіі\"\nBasic.Settings.General.ChannelDescription.beta=\"Патэнцыйна нестабільныя\"\nBasic.Settings.Appearance=\"Выгляд\"\nBasic.Settings.Appearance.General=\"Агульнае\"\nBasic.Settings.Appearance.General.Theme=\"Тэма\"\nBasic.Settings.Appearance.General.Variant=\"Стыль\"\nBasic.Settings.Appearance.General.NoVariant=\"Няма даступных стыляў\"\nBasic.Settings.Appearance.FontScale=\"Памер шрыфта\"\nBasic.Settings.Appearance.Density=\"Шчыльнасць\"\nBasic.Settings.Appearance.Classic=\"Класічная\"\nBasic.Settings.Appearance.Compact=\"Кампактная\"\nBasic.Settings.Appearance.Normal=\"Звычайная\"\nBasic.Settings.Appearance.Comfortable=\"Зручная\"\nBasic.Settings.Appearance.OptionsWarning=\"Некаторыя налады вонкавага выгляду недаступныя для гэтага стылю.\"\nBasic.Settings.Stream=\"Трансляцыя\"\nBasic.Settings.Stream.Destination=\"Атрымальнік сігналу\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Выкарыстоўваць уваход у акаўнт\"\nBasic.Settings.Stream.Custom.Username=\"Імя карыстальніка\"\nBasic.Settings.Stream.Custom.Password=\"Пароль\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: увядзіце srp_username.\\nRTMP: увядзіце імя карыстальніка.\\nSRT: не выкарыстоўваецца.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: увядзіце srp_password.\\nRTMP: увядзіце пароль.\\nSRT: увядзіце пароль шыфравання.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Рэжым тэсціравання паласы прапускання\"\nBasic.Settings.Stream.TTVAddon=\"Аддоны для чата Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Няма\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV і FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Стрым наладжаны не цалкам\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Адкрыць налады\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Адсутнічаюць URL і ключ трансляцыі.\\n\\nАдкрыйце налады і ўвядзіце іх у поле на ўкладцы «Трансляцыя».\"\nBasic.Settings.Stream.MissingUrl=\"Адсутнічае URL.\\n\\nАдкрыйце налады і ўвядзіце яго ў поле на ўкладцы «Трансляцыя».\"\nBasic.Settings.Stream.MissingStreamKey=\"Адсутнічае ключ трансляцыі.\\n\\nАдкрыйце налады і ўвядзіце яго ў поле на ўкладцы «Трансляцыя».\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ігнараваць рэкамендацыі стрымінгавага сэрвісу\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Адкінуць рэкамендаваныя налады\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Увага: ігнараванне абмежаванняў сэрвісу можа знізіць якасць стрыма або спыніць трансляцыю.\\n\\nПрацягнуць?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Максімальны бітрэйт відэа: %1 кбіт/с\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Максімальны бітрэйт аўдыя: %1 кбіт/с\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Максімальная раздзяляльнасць: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Максімальны FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Задаць уласны сервер…\"\nBasic.Settings.Stream.ServiceCustomServer=\"Уласны сервер\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Уключыць %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Ліміт паласы прапускання\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Аўта\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Максімум відэадарожак\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Аўта\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Запісваць стрым у FLV (з выкарыстаннем простых налад запісу)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Перазапіс файла канфігурацыі (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Уключыць перазапіс канфігурацыі\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Некалькі дарожак\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Дадатковае палатно\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Адначасная трансляцыя\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Адначасная трансляцыя дазваляе кадаваць і дасылаць відэа ў рознай якасці. <a href='https://obsproject.com/kb/whip-streaming-guide'>Даведацца больш</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Колькасць слояў\"\nBasic.Settings.Stream.AdvancedOptions=\"Дадатковыя налады\"\nBasic.Settings.Output=\"Вывад\"\nBasic.Settings.Output.Format=\"Фармат запісу\"\nBasic.Settings.Output.Format.MKV=\"Matroska (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"Гібрыдны MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Гібрыдны MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Фрагментаваны MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Фрагментаваны MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Відэа ў фрагментаваным фармаце MOV запісваецца кавалкамі; фіналізацыя, якую патрабуе традыцыйны фармат MOV, не патрабуецца.\\nГэта азначае, што файл можна будзе прайграць, нават калі запіс на дыск быў перарваны, напрыклад, у выніку сіняга экрана смерці або адключэння электрычнасці.\\n\\nНекаторыя прайгравальнікі і рэдактары відэа такі фармат не падтрымліваюць. Каб адрэндарыць запіс у больш прыдатным фармаце, перайдзіце ў Файл → Перапакаваць запісы.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Відэа ў фрагментаваным фармаце MP4 запісваецца кавалкамі; фіналізацыя, якую патрабуе традыцыйны фармат MP4, не патрабуецца.\\nГэта азначае, што файл можна будзе прайграць, нават калі запіс на дыск быў перарваны, напрыклад, у выніку сіняга экрана смерці або адключэння электрычнасці.\\n\\nНекаторыя прайгравальнікі і рэдактары відэа такі фармат не падтрымліваюць. Каб адрэндарыць запіс у больш прыдатным фармаце, перайдзіце ў Файл → Перапакаваць запісы.\"\nBasic.Settings.Output.Encoder.Video=\"Кадавальнік відэа\"\nBasic.Settings.Output.Encoder.Audio=\"Кадавальнік аўдыя\"\nBasic.Settings.Output.SelectDirectory=\"Выберыце каталог для запісаў\"\nBasic.Settings.Output.DynamicBitrate=\"Мяняць бітрэйт у залежнасці ад загружанасці сеткі\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Мяняць бітрэйт у залежнасці ад загружанасці сеткі (Бэта)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Пры высокай загружанасці сеткі замест пропуску кадраў аўтаматычна мяняе бітрэйт падчас эфіру.\\n\\nУвага: пры нечаканай занадта высокай загружанасці сеткі затрымка паказу для гледачоў можа павялічыцца.\\nПасля падзення бітрэйту яго аднаўленне можа заняць некалькі хвілін.\\n\\nУ дадзены момант падтрымліваецца толькі для RTMP.\"\nBasic.Settings.Output.Mode=\"Рэжым вываду\"\nBasic.Settings.Output.Mode.Simple=\"Просты\"\nBasic.Settings.Output.Mode.Adv=\"Пашыраны\"\nBasic.Settings.Output.Mode.FFmpeg=\"Вывад FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Уключыць буфер паўтору\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Максімальны час паўтору\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Максімальны аб'ём\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Прыблізны аб'ём: %1 МБ\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Увага: прыблізны аб'ём (%1 Мб) перавышае рэкамендаваны максімум (%2 Мб)\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Не атрымалася вылічыць прыблізны аб'ём. Задайце ліміт выкарыстання памяці.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Прэфікс у назве файла паўтору\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Суфікс\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Буфер паўтору не працуе пры зададзеным тыпе запісу «Адмысловы вывад (FFmpeg)».\"\nBasic.Settings.Output.Simple.SavePath=\"Шлях для запісаў\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Якасць запісу\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Як у трансляцыі\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Высокая; сярэдні памер файла\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Надзвычайная; вялікі памер файла\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Без страт; найвялікшы памер файла\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Увага: будзе зададзены бітрэйт плыннага відэа, гранічны для выбранага стрымінгавага сэрвісу (%1).\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Увага: будзе зададзены бітрэйт плыннага аўдыя, гранічны для выбранага стрымінгавага сэрвісу (%1).\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Увага: пры зададзенай якасці запісу «Як у трансляцыі» прыпыненне запісу немагчымае.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Увага: выбраны фармат запісу не сумяшчаецца з выбраным(і) кадавальнікам(і) плыні.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Увага: калі вы ведзяце і запіс, і стрым адначасова і ў разных якасцях, прытым запіс вядзецца праз праграмны кадавальнік, для запісу спатрэбяцца дадатковыя рэсурсы працэсара.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Увага: відэа без страт займаюць занадта шмат месца на дыску! Пры высокіх раздзяляльнасці і частаце кадраў такія відэа могуць займаць да 7 ГБ на адну хвіліну. Запіс у якасці без страт рэкамендуецца весці толькі ў тым выпадку, калі на вашым дыску свабодна вельмі шмат месца. Буфер паўтору падчас такога запісу недаступны.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Ці вы ўпэўненыя, што хочаце выкарыстоўваць якасць без страт?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Папярэджанне аб якасці без страт!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Праграмны (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Апаратны (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Апаратны (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Апаратны (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Апаратны (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Апаратны (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Апаратны (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Апаратны (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Апаратны (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Апаратны (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Апаратны (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Праграмны (x264; нізкае спажыванне рэсурсаў працэсара, большы памер файла)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (прадвызн.)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Дарожка Twitch VOD (выкарыстоўваецца дарожка 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Аўдыядарожка\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Несумяшчальная раздзяляльнасць або частата кадраў\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Гэты стрымінгавы сэрвіс не падтрымлівае наладжаную раздзяляльнасць і/або частату кадраў. Гэтыя значэнні будуць зменены на найбліжэйшыя з магчымых:\\n\\n%1\\n\\nЦі жадаеце працягнуць?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Раздзяляльнасць: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Частата кадраў: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Несумяшчальны кадавальнік\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Сэрвіс %1 не падтрымлівае кадавальнік %2. Будзе прызначаны кадавальнік %3.\\n\\nЦі жадаеце працягнуць?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Сэрвіс %1 не падтрымлівае кадавальнікі %2 і %3. Замест іх будуць адпаведна прызначаны кадавальнікі %4 і %5.\\n\\nЦі жадаеце працягнуць?\"\nBasic.Settings.Output.VideoBitrate=\"Бітрэйт відэа\"\nBasic.Settings.Output.AudioBitrate=\"Бітрэйт аўдыя\"\nBasic.Settings.Output.Reconnect=\"Аўтаматычнае перазлучэнне\"\nBasic.Settings.Output.RetryDelay=\"Частата паўтору спробы\"\nBasic.Settings.Output.MaxRetries=\"Макс. паўтораў\"\nBasic.Settings.Output.Advanced=\"Уласныя налады кадавальніка (дадатковыя)\"\nBasic.Settings.Output.EncoderPreset=\"Набор налад кадавальніка\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (нізкае спажыванне рэсурсаў ЦП, найніжэйшая якасць)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (прадвызн.) (сярэдняе спажыванне рэсурсаў ЦП, стандартная якасць)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (высокае спажыванне рэсурсаў ЦП, высокая якасць)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Уласныя налады кадавальніка\"\nBasic.Settings.Output.CustomMuxerSettings=\"Уласныя налады перапакоўшчыка\"\nBasic.Settings.Output.NoSpaceFileName=\"Не ўключаць прабелы ў назвы файлаў\"\nBasic.Settings.Output.Adv.Rescale=\"Перамаштабоўванне\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Адключана\"\nBasic.Settings.Output.Adv.AudioTrack=\"Аўдыядарожка\"\nBasic.Settings.Output.Adv.Streaming=\"Плынь\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Налады плыні\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Дарожка 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Дарожка 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Дарожка 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Дарожка 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Дарожка 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Дарожка 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Дарожка Twitch VOD\"\nBasic.Settings.Output.Adv.Encoder=\"Налады кадавальніка\"\nBasic.Settings.Output.Adv.Recording=\"Запіс\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Налады запісу\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Тып запісу\"\nBasic.Settings.Output.Adv.Recording.Type=\"Тып\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Стандартны\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Адмысловы вывад (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(кадавальнік плыні)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Фарматаванне назвы файла\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Перазапісваць, калі файл існуе\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Працаздольнасць Адмысловага вываду (FFmpeg) не гарантуецца.\\nРаім вам выкарыстоўваць тып запісу «Стандартны».\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Тып вываду FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Вывад па URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Вывад у файл\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Распаўсюджаныя фарматы запісу\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Усе файлы\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Шлях да файла або URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Фармат кантэйнера\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Аўдыя\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Відэа\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Прадвызначаны\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Апісанне фармату кантэйнера\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Аўдыя- ці відэакодэк, адгаданы па шляху да файла або па URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Прадвызначаны кадавальнік\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Адкючыць кадавальнік\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Кадавальнік відэа\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Налады кадавальніка відэа (калі існуюць)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Кадавальнік аўдыя\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Налады кадавальніка аўдыя (калі існуюць)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Налады перапакоўшчыка (калі існуюць)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Інтэрвал ключавых кадраў (кадры)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Паказаць усе кодэкі (улічваючы патэнцыйна несумяшчальныя)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Налады FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Аўтаматычнае раздзяленне файла\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Па даўжыні\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Па памеры\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Толькі ручое раздзяленне\"\nBasic.Settings.Output.SplitFile.Time=\"Даўжыня фрагмента\"\nBasic.Settings.Output.SplitFile.Size=\"Памер фрагмента\"\nScreenshot=\"Здымак вываду\"\nScreenshot.SourceHotkey=\"Здымак выбранай крыніцы\"\nScreenshot.StudioProgram=\"Скрыншот (Праграма)\"\nScreenshot.Preview=\"Скрыншот (Прагляд)\"\nScreenshot.Scene=\"Скрыншот (Сцэна)\"\nScreenshot.Source=\"Скрыншот (Крыніца)\"\nFilenameFormatting.TT.CCYY=\"Год, чатыры лічбы\"\nFilenameFormatting.TT.YY=\"Год, дзве апошнія лічбы (00–99)\"\nFilenameFormatting.TT.MM=\"Нумар месяца (01–12)\"\nFilenameFormatting.TT.DD=\"Дзень месяца, заўжды двухзначны (01–31)\"\nFilenameFormatting.TT.hh=\"Гадзіна ў 24-гадзінным фармаце (00–23)\"\nFilenameFormatting.TT.mm=\"Хвіліна (00–59)\"\nFilenameFormatting.TT.ss=\"Секунда (00–59)\"\nFilenameFormatting.TT.Percent=\"Сімвал %\"\nFilenameFormatting.TT.a=\"Скарочаная назва дня тыдня\"\nFilenameFormatting.TT.A=\"Поўная назва дня тыдня\"\nFilenameFormatting.TT.b=\"Скарочаная назва месяца\"\nFilenameFormatting.TT.B=\"Поўная назва месяца\"\nFilenameFormatting.TT.d=\"Дзень месяца, заўжды двухзначны (01–31)\"\nFilenameFormatting.TT.H=\"Гадзіна ў 24-гадзінным фармаце (00–23)\"\nFilenameFormatting.TT.I=\"Гадзіна ў 12-гадзінным фармаце (01–12)\"\nFilenameFormatting.TT.m=\"Нумар месяца (01–12)\"\nFilenameFormatting.TT.M=\"Хвіліна (00–59)\"\nFilenameFormatting.TT.p=\"Абазначэнне AM або PM\"\nFilenameFormatting.TT.s=\"Час па UNIX\"\nFilenameFormatting.TT.S=\"Секунда (00–59)\"\nFilenameFormatting.TT.y=\"Год, дзве апошнія лічбы (00–99)\"\nFilenameFormatting.TT.Y=\"Год\"\nFilenameFormatting.TT.z=\"Часавы пояс адносна UTC па стандарце ISO 8601\"\nFilenameFormatting.TT.Z=\"Назва або абрэвіяцыя часавага пояса\"\nFilenameFormatting.TT.FPS=\"Частата кадраў на секунду\"\nFilenameFormatting.TT.CRES=\"Раздзяляльнасць палатна (асноўная)\"\nFilenameFormatting.TT.ORES=\"Выніковая раздзяляльнасць (маштабаваная)\"\nFilenameFormatting.TT.VF=\"Фармат відэа\"\nBasic.Settings.Video=\"Відэа\"\nBasic.Settings.Video.Adapter=\"Відэаадаптар\"\nBasic.Settings.Video.BaseResolution=\"Раздзяляльнасць палатна (асноўная)\"\nBasic.Settings.Video.ScaledResolution=\"Выніковая раздзяляльнасць (маштабаваная)\"\nBasic.Settings.Video.DownscaleFilter=\"Фільтр маштабавання\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Раздзяляльнасці супадаюць, маштабаванне не патрабуецца]\"\nBasic.Settings.Video.FPSCommon=\"Папулярныя значэнні FPS\"\nBasic.Settings.Video.FPSInteger=\"Цэлае значэнне FPS\"\nBasic.Settings.Video.FPSFraction=\"Дробнае значэнне FPS\"\nBasic.Settings.Video.Numerator=\"Лічнік\"\nBasic.Settings.Video.Denominator=\"Назоўнік\"\nBasic.Settings.Video.Renderer=\"Рэндарар\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (эксперыментальны)\"\nBasic.Settings.Video.InvalidResolution=\"Памылковая раздзяляльнасць. Правільны фармат: [шырыня]x[вышыня] (напр., 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Працуе вывад відэа. Скончыце ўсе вывады, каб мяняць налады відэа.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Білінейны (найхутчэйшы, але выява размываецца)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Бікубічны (рэзкае маштабаванне, 16 сэмплаў)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Ланцош (рэзкае маштабаванне, 36 сэмплаў)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Змешванне (узважаная сума, 4/6/9 сэмплаў)\"\nBasic.Settings.Audio=\"Аўдыя\"\nBasic.Settings.Audio.SampleRate=\"Частата дыскрэтызацыі\"\nBasic.Settings.Audio.Channels=\"Каналы\"\nBasic.Settings.Audio.Meters=\"Вымяральнікі\"\nBasic.Settings.Audio.MeterDecayRate=\"Хуткасць спаду\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Высокая\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Сярэдняя (PPM тыпу I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Нізкая (PPM тыпу II)\"\nBasic.Settings.Audio.PeakMeterType=\"Тып вымяральніка пікаў\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Выбарачны\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Дакладны (большае спажыванне рэсурсаў ЦП)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"УВАГА: уключаны аб'ёмны гук.\"\nBasic.Settings.Audio.MultichannelWarning=\"Калі вы ведзяце стрым, упэўніцеся, што ваш сэрвіс падтрымлівае як прыём, так і прайграванне аб'ёмнага гуку. Напрыклад, Facebook 360 Live падтрымлівае аб'ёмны гук цалкам; YouTube Live падтрымлівае яго перадачу (і прайграванне на тэлевізары).\\n\\nАўдыяфільтры OBS сумяшчаюцца з аб'ёмным гукам, але яго падтрымка VST-плагінамі не гарантуецца.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Уключыць аб'ёмны гук?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Ці вы ўпэўненыя, што хочаце ўключыць аб'ёмны гук?\"\nBasic.Settings.Audio.Devices=\"Аўдыяпрылады\"\nBasic.Settings.Audio.DesktopDevice=\"Гук камп'ютара\"\nBasic.Settings.Audio.DesktopDevice2=\"Гук камп'ютара 2\"\nBasic.Settings.Audio.AuxDevice=\"Мікрафон/Дадатковае аўдыя\"\nBasic.Settings.Audio.AuxDevice2=\"Мікрафон/Дадатковае аўдыя 2\"\nBasic.Settings.Audio.AuxDevice3=\"Мікрафон/Дадатковае аўдыя 3\"\nBasic.Settings.Audio.AuxDevice4=\"Мікрафон/Дадатковае аўдыя 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Выключаць пры націсканні\"\nBasic.Settings.Audio.PushToMuteDelay=\"Затрымка адключэння\"\nBasic.Settings.Audio.EnablePushToTalk=\"Уключаць пры націсканні\"\nBasic.Settings.Audio.PushToTalkDelay=\"Затрымка ўключэння\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Прылада не падключана або недаступная]\"\nBasic.Settings.Audio.Disabled=\"Адкл.\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Буферызацыя аўдыя з нізкай затрымкай (для выхадаў Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"УВАГА: уключана буферызацыя аўдыя з нізкай затрымкай.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Пры ўключанай буферызацыі аўдыя з нізкай затрымкай гук ад некаторых крыніц можа сказіцца або адключыцца.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Уключыць буферызацыю аўдыя з нізкай затрымкай?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Ці вы ўпэўненыя, што хочаце ўключыць буферызацыю аўдыя з нізкай затрымкай?\"\nBasic.Settings.Accessibility=\"Спец. магчымасці\"\nBasic.Settings.Accessibility.ColorOverrides=\"Іншыя колеры\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Межы крыніцы (вылучэнне)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Межы крыніцы (абрэзка)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Межы крыніцы (навядзенне)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Гучнасць (ад -60 да -20 дБ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Гучнасць (ад -20 да -9 дБ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Гучнасць (ад -9 да 0 дБ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Гучнасць (ад -60 да -20 дБ) (актыўны)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Гучнасць (ад -20 да -9 дБ) (актыўны)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Гучнасць (ад -9 да 0 дБ) (актыўны)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Набор колераў\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Прадвызначаны\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Уласны\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Для дальтонікаў\"\nBasic.Settings.Advanced=\"Пашыраныя\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Пры выхадзе, калі запіс або трансляцыя не спынена, паказваць папярэджанне\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Прыярытэт працэсу\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Высокі\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Вышэй звычайнага\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Звычайны\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Ніжэй звычайнага\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Нізкі\"\nBasic.Settings.Advanced.FormatWarning=\"Увага: усе фарматы колеру, акрамя NV12/P010, у асноўным распрацаваны для запісу і не рэкамендуюцца для трансляцыі. Падчас эфіру з іх выкарыстаннем нагрузка на ЦП можа павысіцца з-за канвертацыі фармату колеру.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Увага: высокадакладныя фарматы ў асноўным выкарыстоўваюцца ў колеравых прасторах HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Увага: для Rec. 2100 патрабуецца фармат з большай дакладнасцю.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Фармат колеру\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 біт, 4:2:0, 2 планы)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 біт, 4:2:0, 3 планы)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 біт, 4:4:4, 3 планы)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 біт, 4:2:0, 2 планы)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 біт, 4:2:0, 3 планы)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 біт, 4:2:2, 2 планы)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 біт, 4:4:4, 2 планы)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 біт)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Колеравая прастора\"\nBasic.Settings.Advanced.Video.ColorRange=\"Дыяпазон колеру\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Абмежаваны\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Поўны\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Узровень белага SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Узровень намінальнага піка HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Прылада праслухоўвання\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Прадвызначаная\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Адключыць прыглушэнне гукаў Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Затрымка плыні\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Працягласць\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Пасля перазлучэння працягнуць з пункту спынення (і павялічыць затрымку)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Прыблізнае спажыванне памяці: %1 МБ\"\nBasic.Settings.Advanced.Network=\"Сетка\"\nBasic.Settings.Advanced.Network.Disabled=\"Выбраны пратакол трансляцыі не падтрымлівае змяненне налад сеткі.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Прывязка да IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Сям'я IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Уключыць аптымізацыю сеткі\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Уключыць TCP-пэйсінг\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Спрабуе зменшыць уплыў на іншыя, чуллівыя да затрымкі, праграмы рэгуляваннем перадачы вываду RTMP.\\nПры нестабільным злучэнні павялічвае рыск страты кадраў.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Калі працуюць спалучэнні клавіш\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Заўжды\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Калі галоўнае акно не вылучана\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Калі галоўнае акно вылучана\"\nBasic.Settings.Advanced.AutoRemux=\"Аўтаматычна перапакоўваць у фармат %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(запісваць у MKV)\"\nBasic.AdvAudio=\"Пашыраныя ўласцівасці гуку\"\nBasic.AdvAudio.ActiveOnly=\"Толькі актыўныя крыніцы\"\nBasic.AdvAudio.Name=\"Назва\"\nBasic.AdvAudio.Volume=\"Гучнасць\"\nBasic.AdvAudio.VolumeSource=\"Гучнасць: %1\"\nBasic.AdvAudio.Mono=\"Мона\"\nBasic.AdvAudio.MonoSource=\"Змяшэнне ў мона: %1\"\nBasic.AdvAudio.Balance=\"Баланс\"\nBasic.AdvAudio.BalanceSource=\"Баланс: %1\"\nBasic.AdvAudio.SyncOffset=\"Затрымка гуку\"\nBasic.AdvAudio.SyncOffsetSource=\"Затрымка гуку: %1\"\nBasic.AdvAudio.Monitoring=\"Праслухоўванне аўдыя\"\nBasic.AdvAudio.Monitoring.None=\"Выключана\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Толькі праслухоўванне (выкл. вывад)\"\nBasic.AdvAudio.Monitoring.Both=\"Праслухоўванне і вывад\"\nBasic.AdvAudio.MonitoringSource=\"Праслухоўванне: %1\"\nBasic.AdvAudio.AudioTracks=\"Дарожкі\"\nBasic.Settings.Hotkeys=\"Спалучэнні клавіш\"\nBasic.Settings.Hotkeys.Pair=\"Спалучэнні, што абагулены з камандай «%1», дзейнічаюць як пераключальнікі\"\nBasic.Settings.Hotkeys.Filter=\"Фільтраваць\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"па спалучэнні\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Гэтае спалучэнне ўжо наладжана для іншага(-ых) дзеяння(ў). Націсніце, каб паказаць канфліктаванні\"\nBasic.Settings.Hotkeys.PleaseWait=\"Загрузка спалучэнняў, пачакайце…\"\nBasic.Hotkeys.SelectScene=\"Пераключыцца на гэтую сцэну\"\nBasic.SystemTray.Show=\"Паказаць\"\nBasic.SystemTray.Hide=\"Схаваць\"\nBasic.SystemTray.Message.Reconnecting=\"Кліент адлучаны. Перазлучэнне…\"\nHotkeys.Left=\"Стрэлка ўлева\"\nHotkeys.Right=\"Стрэлка ўправа\"\nHotkeys.Up=\"Стрэлка ўверх\"\nHotkeys.Down=\"Стрэлка ўніз\"\nHotkeys.Windows=\"Win\"\nHotkeys.Menu=\"Меню\"\nHotkeys.Space=\"Прабел\"\nHotkeys.NumpadNum=\"Num %1\"\nHotkeys.NumpadMultiply=\"Num *\"\nHotkeys.NumpadDivide=\"Num /\"\nHotkeys.NumpadAdd=\"Num +\"\nHotkeys.NumpadSubtract=\"Num -\"\nHotkeys.NumpadDecimal=\"Num ,\"\nHotkeys.AppleKeypadNum=\"%1 (асн. частка)\"\nHotkeys.AppleKeypadMultiply=\"* (асн. частка)\"\nHotkeys.AppleKeypadDivide=\"/ (асн. частка)\"\nHotkeys.AppleKeypadAdd=\"+ (асн. частка)\"\nHotkeys.AppleKeypadSubtract=\"- (асн. частка)\"\nHotkeys.AppleKeypadDecimal=\". (асн. частка)\"\nHotkeys.AppleKeypadEqual=\"= (асн. частка)\"\nHotkeys.MouseButton=\"Мыш %1\"\nMute=\"Заглушыць\"\nUnmute=\"Уключыць гук\"\nPush-to-mute=\"Выключыць гук пры націсканні\"\nPush-to-talk=\"Уключыць гук пры націсканні\"\nSceneItemShow=\"Паказаць: %1\"\nSceneItemHide=\"Схаваць: %1\"\nOutputWarnings.NoTracksSelected=\"Выберыце прынамсі адну дарожку\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Памылка налад вываду\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Ва ўсіх вывадаў павінна быць выбрана прынамсі адна аўдыядарожка.\"\nOutputWarnings.MP4Recording=\"Увага: запісы ў фармаце MP4 або MOV немагчыма аднавіць, калі запіс быў скончаны прымусова (з-за сіняга экрана смерці, адключэння электрычнасці і г. д.). Калі вы хочаце запісаць некалькі аўдыядарожак, скарыстайцеся фарматам MKV, пасля завяршэння запісу перапакуйце яго ў MP4 або MOV (Файл → Перапакаваць запісы)\"\nOutputWarnings.CannotPause=\"Увага: пры зададзеным кадавальніку запісу «(кадавальнік плыні)» прыпыненне запісу немагчымае.\"\nOutputWarnings.CodecIncompatible=\"Прызначэнне кадавальніка аўдыя або відэа было скінута з-за несумяшчальнасці. Выберыце сумяшчальны кадавальнік са спіса.\"\nCodecCompat.Incompatible=\"(не сумяшчаецца з фарматам «%1»)\"\nCodecCompat.CodecPlaceholder=\"Выберыце кадавальнік…\"\nCodecCompat.ContainerPlaceholder=\"Выберыце фармат…\"\nCodecCompat.CodecMissingOnExit.Title=\"Кадавальнік не выбраны\"\nCodecCompat.CodecMissingOnExit.Text=\"Прынамсі адзін кадавальнік аўдыя або відэа не быў зададзены. Праверце прызначэнне кадавальнікаў як для запісу, так і для стрыма.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Фармат не выбраны\"\nCodecCompat.ContainerMissingOnExit.Text=\"Фармат запісу не выбраны. Выберыце фармат запісу, які сумяшчаецца з выбраным кадавальнікам плыні.\"\nFinalScene.Title=\"Выдаліць сцэну\"\nFinalScene.Text=\"Павінна існаваць прынамсі адна сцэна.\"\nNoSources.Title=\"Няма крыніц\"\nNoSources.Text=\"Здаецца, вы не дадалі ніводную крыніцу відэа, таму выводзіцца будзе чорны экран. Ці вы ўсё роўна жадаеце працягнуць?\"\nNoSources.Text.AddSource=\"Дадаць крыніцы можна ў любы момант, націснуўшы значок «+» унізе аддзела «Крыніцы» ў галоўным акне.\"\nNoSources.Label=\"Крыніцы адсутнічаюць.\\nНацісніце значок «+»\\nабо правую кнопку мышы,\\nкаб дадаць новую.\"\nChangeBG=\"Задаць колер\"\nCustomColor=\"Уласны\"\nBrowserSource.EnableHardwareAcceleration=\"Апаратнае паскарэнне крыніцы тыпу «Браўзер»\"\nAbout=\"Пра праграму\"\nAbout.Info=\"OBS Studio ёсць бясплатнай і адкрытай праграмай для запісу відэа і правядзення жывых эфіраў.\"\nAbout.Donate=\"Ахвяраваць грошы\"\nAbout.GetInvolved=\"Зрабіць уклад\"\nAbout.Authors=\"Аўтары\"\nAbout.License=\"Ліцэнзія\"\nAbout.Error=\"Памылка! Не атрымалася прачытаць файл.\\n\\nПерайдзіце сюды: %1\"\nAbout.Contribute=\"Падтрымайце наш праект\"\nAddUrl.Title=\"Дадаць крыніцу праз адрас URL\"\nAddUrl.Text=\"Вы перацягнулі URL у акно OBS. Стварыць крыніцу тыпу «Браўзер» з гэтым адрасам?\"\nResizeOutputSizeOfSource=\"Маштабаваць вывад (да памеру крыніцы)\"\nResizeOutputSizeOfSource.Text=\"Раздзяляльнасці палатна і вываду будуць маштабаваны да памеру гэтай крыніцы.\"\nResizeOutputSizeOfSource.Continue=\"Ці жадаеце працягнуць?\"\nPreviewTransition=\"Перадпрагляд пераходу\"\nImporter=\"Выберыце імпарцёр калекцыі сцэн\"\nImporter.SelectCollection=\"Выберыце калекцыю сцэн\"\nImporter.Collection=\"Калекцыя сцэн\"\nImporter.HelpText=\"Дадайце файлы, каб імпартаваць калекцыі з OBS або іншых сумяшчальных праграм.\"\nImporter.Path=\"Шлях да калекцыі\"\nImporter.Program=\"Выяўленая праграма\"\nImporter.AutomaticCollectionPrompt=\"Аўтаматычны пошук калекцый сцэн\"\nImporter.AutomaticCollectionText=\"OBS можа аўтаматычна знайсці калекцыі сцэн з іншых сумяшчальных праграм. Ці вы хочаце, каб OBS гэта зрабіла?\\n\\nГэта можна змяніць пазней тут: Налады → Агульныя → Імпарцёры.\"\nImporter.SelectFile=\"Агляд...\"\nRestart=\"Перазапуск\"\nNeedsRestart=\"Патрабуецца перазапуск OBS Studio. Перазапусціць праграму зараз?\"\nLoadProfileNeedsRestart=\"Профіль змяшчае налады, якія патрабуюць перазапуск OBS:\\n%1\\n\\nЦі жадаеце перазапусціць праграму, каб ужыць гэтыя налады?\"\nContextBar.NoSelectedSource=\"Крыніца не выбрана\"\nContextBar.MediaControls.PlayMedia=\"Прайграць\"\nContextBar.MediaControls.PauseMedia=\"Паўза\"\nContextBar.MediaControls.StopMedia=\"Сыпніць\"\nContextBar.MediaControls.RestartMedia=\"Нанова\"\nContextBar.MediaControls.PlaylistNext=\"Наступнае ў плэй-лісце\"\nContextBar.MediaControls.PlaylistPrevious=\"Папярэдняе ў плэй-лісце\"\nContextBar.MediaControls.BlindSeek=\"Віджэт перамоткі медыяфайлаў\"\nYouTube.Auth.Ok=\"Уваход паспяхова завершаны.\\nВы можаце закрыць гэтую старонку.\"\nYouTube.Auth.NoCode=\"Уваход не завершаны.\"\nYouTube.Auth.NoChannels=\"У выбраным акаўнце няма даступных каналаў.\"\nYouTube.Auth.WaitingAuth.Title=\"Уваход ва ўліковы запіс YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Працягніце ўваход у вонкавым браўзеры.<br>Калі вонкавы браўзер не адкрываецца, перайдзіце па гэтай спасылцы і завяршыце ўваход:<br>%1\"\nYouTube.AuthError.Text=\"Не атрымалася загрузіць звесткі аб канале: %1.\"\nYouTube.Actions.WindowTitle=\"Наладжванне трансляцыі YouTube — Канал: %1\"\nYouTube.Actions.CreateNewEvent=\"Стварыць трансляцыю\"\nYouTube.Actions.ChooseEvent=\"Выбраць існуючую трансляцыю\"\nYouTube.Actions.Title=\"Назва*\"\nYouTube.Actions.MyBroadcast=\"Мая трансляцыя\"\nYouTube.Actions.Description=\"Апісанне\"\nYouTube.Actions.Privacy=\"Бачнасць*\"\nYouTube.Actions.Privacy.Private=\"Прыватнае\"\nYouTube.Actions.Privacy.Public=\"Агульнадаступнае\"\nYouTube.Actions.Privacy.Unlisted=\"Доступ па спасылцы\"\nYouTube.Actions.Category=\"Катэгорыя\"\nYouTube.Actions.Thumbnail=\"Мініяцюра\"\nYouTube.Actions.Thumbnail.SelectFile=\"Выбраць файл…\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Файл не выбраны\"\nYouTube.Actions.Thumbnail.ClearFile=\"Ачысціць\"\nYouTube.Actions.MadeForKids=\"Гэта відэа для дзяцей?*\"\nYouTube.Actions.MadeForKids.Yes=\"Так, гэта змесціва для дзяцей\"\nYouTube.Actions.MadeForKids.No=\"Не, змесціва не для дзяцей\"\nYouTube.Actions.AdditionalSettings=\"Дадатковыя налады\"\nYouTube.Actions.Latency=\"Затрымка\"\nYouTube.Actions.Latency.Normal=\"Звычайная\"\nYouTube.Actions.Latency.Low=\"Нізкая\"\nYouTube.Actions.Latency.UltraLow=\"Звышнізкая\"\nYouTube.Actions.EnableAutoStart=\"Аўтазапуск\"\nYouTube.Actions.EnableAutoStop=\"Аўтаспыненне\"\nYouTube.Actions.AutoStartStop.TT=\"Паказвае, ці павінен гэты запланаваны эфір пачацца аўтаматычна\"\nYouTube.Actions.EnableDVR=\"DVR\"\nYouTube.Actions.360Video=\"Відэа 360°\"\nYouTube.Actions.ScheduleForLater=\"Запланаваць\"\nYouTube.Actions.RememberSettings=\"Запомніць налады\"\nYouTube.Actions.Create_Ready=\"Стварыць трансляцыю\"\nYouTube.Actions.Create_GoLive=\"Стварыць трансляцыю і пачаць эфір\"\nYouTube.Actions.Choose_Ready=\"Выбраць трансляцыю\"\nYouTube.Actions.Choose_GoLive=\"Выбраць трансляцыю і пачаць эфір\"\nYouTube.Actions.Create_Schedule=\"Запланаваць трансляцыю\"\nYouTube.Actions.Create_Schedule_Ready=\"Запланаваць і выбраць трансляцыю\"\nYouTube.Actions.Dashboard=\"Адкрыць Студыю YouTube\"\nYouTube.Actions.Error.Title=\"Памылка стварэння жывой трансляцыі\"\nYouTube.Actions.Error.Text=\"Памылка доступу да YouTube: %1.<br/>Яе падрабязнае апісанне можна знайсці на <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Памылка доступу да YouTube. Праверце злучэнне з інтэрнэтам або доступ да сервера YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Памылка стварэння трансляцыі: %1.<br/>Яе падрабязнае апісанне можна знайсці на <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Трансляцыя не створана. Прывяжыце свой акаўнт яшчэ раз.\"\nYouTube.Actions.Error.YouTubeApi=\"Памылка YouTube API. Каб даведацца больш, праглядзіце файл журнала.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Выбраная трансляцыя не знойдзена.\"\nYouTube.Actions.Error.FileMissing=\"Выбраны файл не існуе.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Памылка адкрыцця выбранага файла.\"\nYouTube.Actions.Error.FileTooLarge=\"Выбраны файл надта вялікі (ліміт: 2 Мб).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Памылка перадачы трансляцыі: %1<br/><br/>Калі памылка паўторыцца, <a href='https://studio.youtube.com/video/%2/livestreaming'>пачніце трансляцыю ўручную ў Студыі YouTube</a>.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Трансляцыя пераходзіць да праверачнага этапу, гэта зойме некаторы час. Паўтарыце спробу праз 10–30 секунд.\"\nYouTube.Actions.EventsLoading=\"Загрузка спіса падзей…\"\nYouTube.Actions.EventCreated.Title=\"Падзея створана\"\nYouTube.Actions.EventCreated.Text=\"Падзея паспяхова створана.\"\nYouTube.Actions.Stream=\"Трансляцыя\"\nYouTube.Actions.Stream.ScheduledFor=\"Запланавана: %1\"\nYouTube.Actions.Stream.Resume=\"Працягнуць перарваны стрым\"\nYouTube.Actions.Stream.YTStudio=\"Аўтаматычна створана Студыяй YouTube\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Стварэнне жывой трансляцыі, пачакайце…\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Патрабуецца ручны запуск\"\nYouTube.Actions.AutoStartStreamingWarning=\"Аўтазапуск адключаны для гэтай падзеі. Націсніце «Пачаць эфір», каб пачаць трансляцыю.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Вы не зможаце перазлучыцца.<br>Ваш стрым будзе спынены, і вы не будзеце ў эфіры.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Правядзенне жывых трансляцый не ўключана на выбраным канале YouTube.<br/><br/>Каб даведацца больш, перайдзіце сюды: <a href='https://www.youtube.com/features'>youtube.com/features</a>.\"\nYouTube.Errors.livePermissionBlocked=\"Правядзенне жывых трансляцый недаступнае на выбраным канале YouTube<br/>Звярніце ўвагу, што поўнае ўключэнне гэтай магчымасці можа заняць да 24 гадзін.<br/><br/>Каб даведацца больш, перайдзіце сюды: <a href='https://www.youtube.com/features'>youtube.com/features</a>.\"\nYouTube.Errors.errorExecutingTransition=\"Пераход не атрымаўся з-за ўнутранай памылкі. Паўтарыце спробу праз некалькі секунд.\"\nYouTube.Errors.errorStreamInactive=\"YouTube не атрымоўвае даныя для стрыма. Праверце канфігурацыю і паўтарыце спробу.\"\nYouTube.Errors.invalidTransition=\"Памылковы пераход. Верагодна, стрым не завяршыў папярэдні пераход. Паўтарыце спробу праз некалькі секунд.\"\nYouTube.DocksRemoval.Title=\"Ачысціць састарэлыя докі YouTube\"\nYouTube.DocksRemoval.Text=\"Гэтыя састарэлыя докі будуць выдалены:\\n\\n%1\\nЗамест іх карыстайцеся докам «YouTube Live Control Panel».\"\nConfigDownload.WarningMessageTitle=\"Увага\"\nFailedToStartStream.MissingConfigURL=\"Для бягучага сэрвісу няма даступных канфігурацыйных URL\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Уласны RTMP URL не зададзены\"\nFailedToStartStream.InvalidCustomConfig=\"Памылковая ўласная канфігурацыя\"\nFailedToStartStream.MissingCanvas=\"Адсутнічае наладжанае дадатковае палатно\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Не атрымалася стварыць сэрвіс відэа з некалькімі відэадарожкамі\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Не атрымалася стварыць RTMP-вывад відэа з некалькімі дарожкамі\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC недаступны.\\n\\nНе атрымалася знайсці кадавальнік тыпу «%1»\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Не атрымалася стварыць кадавальнік відэа «%1» (тып: %2)\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Падчас стварэння кадавальніка «%1» (тып: %2) узнікла памылка атрымання звестак аб відэа\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Не атрымалася стварыць кадавальнік аўдыя\"\nFailedToStartStream.NoRTMPURLInConfig=\"У канфігурацыі адсутнічае мэтавы RTMP(S) URL плыні\"\nFailedToStartStream.FallbackToDefault=\"Не атрымалася пачаць стрым праз %1. Ці жадаеце вы выкарыстаць прадвызначаныя налады кадавання?\"\nFailedToStartStream.ConfigRequestFailed=\"Канфігурацыя з %1 не атрымана<br><br>Памылка HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Атрымана невядомае значэнне стану '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nЦі жадаеце вы працягнуць стрым без %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nЦі жадаеце вы працягнуць стрым?\"\nFailedToStartStream.MissingEncoderConfigs=\"Канфігурацыя плыні не ўтрымлівае налады кадавальніка\"\nFailedToStartStream.InvalidEncoderConfig=\"Канфігурацыя плыні ўтрымлівае памылковыя налады кадавальніка\"\nFailedToStartStream.StatusMissingHTML=\"Падчас спробы пачатку плыні ўзнікла невядомая памылка\"\nFailedToStartStream.NoConfigSupplied=\"Канфігурацыя адсутнічае\"\nMultitrackVideo.Info=\"%1 аўтаматычна аптымізуе вашы налады пад кадаванне і адпраўленне відэа ў некалькіх якасцях. Пасля выбару гэтай опцыі сэрвісу %2 будуць адпраўлены звесткі аб вашым камп'ютары і ПЗ.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Несумяшчальныя налады\"\nMultitrackVideo.IncompatibleSettings.Text=\"У дадзены момант %1 не сумяшчаецца з наступнымі пунктамі:\\n\\n%2\\nКаб працягнуць стрым з %1, адключыце несумяшчальныя налады:\\n\\n%3\\nі зноў пачніце стрым.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Адключыць для гэтага стрыму\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Абнавіць налады і пачаць плынь\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 не сумяшчаецца з зададзенымі наладамі [Аўдыя → Агульныя → Каналы]: %2, %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"Патрабуецца змяніць наладу [Аўдыя → Агульныя → Каналы] на %1\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 патрабуе задаць наладу [Аўдыя → Агульныя → Каналы] інакш\"\nBasic.OpenPluginManager=\"Менеджар плагінаў\"\nPluginManager=\"Менеджар плагінаў\"\nPluginManager.Restart=\"Каб ужыць змяненне налад плагінаў, патрабуецца перазапусціць OBS Studio\"\nPluginManager.MissingPlugin=\"[ПЛАГІН НЕ ЗНОЙДЗЕНЫ]\"\nPluginManager.Section.Discover=\"Агляд\"\nPluginManager.Section.Manage=\"Усталяваны\"\nPluginManager.Section.Updates=\"Абнаўленні\"\nPluginManager.Section.Manage.Title=\"Кіраванне ўключанымі плагінамі\"\n"
  },
  {
    "path": "frontend/data/locale/bem-ZM.ini",
    "content": "Language=\"ChiBemba\"\nOK=\"Oke\"\nApply=\"Bomfya\"\nCancel=\"Kansula\"\nClose=\"Isala\"\nSave=\"Sunga\"\nDiscard=\"Fuuta\"\nDisable=\"Shimya\"\nYes=\"Ee(Suminnisha)\"\nNo=\"Awe(Kanya)\"\nAdd=\"Lundapo(Bikapo)\"\nRemove=\"Fumya\"\nRename=\"Cinja ishina\"\nInteract=\"ampana(intalakiti)\"\nFilters=\"Imicinjishe(filuta)\"\nProperties=\"amapulupati\"\nMoveUp=\"Kabiye Pamulu\"\nMoveDown=\"Kabiye Panshi\"\nSettings=\"ama setingi\"\nDisplay=\"Langisha\"\nName=\"Ishina\"\nExit=\"Fuma\"\nMixer=\"Mikisa wafiunda\"\nBrowse=\"Sala\"\nMono=\"laini Imo\"\nStereo=\"Yabili(sitiliyo)\"\nDroppedFrames=\"Ifikope fileputukila %1 (%2%)\"\nClear=\"futa\"\nRevert=\"Bwesha\"\nShow=\"Langisha\"\nHide=\"Fisa\"\nUnhideAll=\"Fisulula\"\nUntitled=\"tapali ishina\"\nNew=\"Tampa icipya\"\nDuplicate=\"Kopololako\"\nEnable=\"asha\"\nDisableOSXVSync=\"Shimya macOS V-Sync\"\nResetOSXVSyncOnExit=\"Pakwisala bwesesha macOS V-Syn ifyo yaciba\"\nHighResourceUsage=\"Flyafulisha ifilealulya! Shimya ama setingi ya filimu nangula cinja encoding precept\"\nTransition=\"Imyalukile\"\nQuickTransitions=\"imyalukila yamukwangufyanya\"\nLeft=\"Kukuso\"\nRight=\"Kukulyo\"\nTop=\"Pamulu\"\nBottom=\"Pesamba\"\nReset=\"Bwesesha ifyo fyaciba\"\nHours=\"Ama Ola\"\nMinutes=\"Ama minute\"\nSeconds=\"Ama Second\"\nDeprecated=\"Lesha\"\nReplayBuffer=\"Lisaha ifisungilwe\"\nImport=\"Ingisha(Import)\"\nExport=\"Cita export\"\nCopy=\"Kopolola\"\nPaste=\"Bika apa ifyokopolwele\"\nPasteReference=\"Bika apa icilipo kale\"\nPasteDuplicate=\"Bika apa ifyokopolweleko\"\nRemuxRecordings=\"Remux ama recording\"\nNext=\"Panatanshi\"\nBack=\"Panuma\"\nDefaults=\"Efyo fyaciba\"\nHideMixer=\"Fisa muli Mikisa\"\nStudioMode.Preview=\"Mona\"\n"
  },
  {
    "path": "frontend/data/locale/bg-BG.ini",
    "content": "Language=\"български\"\nApply=\"Приложи\"\nCancel=\"Откажи\"\nClose=\"Затвори\"\nSave=\"Запази\"\nDiscard=\"Отхвърли\"\nDisable=\"Изключено\"\nYes=\"Да\"\nNo=\"Не\"\nAdd=\"Добави\"\nRemove=\"Премахни\"\nRename=\"Преименувай\"\nInteract=\"Взаимодействие\"\nFilters=\"Филтри\"\nProperties=\"Свойства\"\nMoveUp=\"Премести нагоре\"\nMoveDown=\"Премести надолу\"\nSettings=\"Настройки\"\nDisplay=\"Дисплей\"\nName=\"Име\"\nExit=\"Изход\"\nMixer=\"Звуков смесител\"\nBrowse=\"Избери\"\nMono=\"Моно\"\nStereo=\"Стерео\"\nDroppedFrames=\"Изпуснати кадри %1 (%2%)\"\nClear=\"Изчисти\"\nRevert=\"Върни\"\nShow=\"Покажи\"\nHide=\"Скрий\"\nUnhideAll=\"Покажи всички\"\nUntitled=\"Неозаглавен\"\nNew=\"Нов\"\nDuplicate=\"Дублирай\"\nEnable=\"Включено\"\nDisableOSXVSync=\"Изключване на macOS V-синхронизация\"\nResetOSXVSyncOnExit=\"Рестартиране на macOS V-синхронизация при излизане\"\nHighResourceUsage=\"Претоварено кордиране! Намалете настройките за видео или ползвайте по-бърза настройка за кодиране.\"\nTransition=\"Преход\"\nQuickTransitions=\"Бързи преходи\"\nFadeToBlack=\"Постепенно затъмняване\"\nLeft=\"Ляво\"\nRight=\"Дясно\"\nTop=\"Горе\"\nBottom=\"Долу\"\nReset=\"Нулирай\"\nHours=\"Часа\"\nMinutes=\"Минути\"\nSeconds=\"Секунди\"\nDeprecated=\"Отхвърлени\"\nReplayBuffer=\"Буфер за повторение\"\nImport=\"Внасяне\"\nExport=\"Изнасяне\"\nCopy=\"Копиране\"\nPaste=\"Поставяне\"\nPasteReference=\"Постави (препратка)\"\nPasteDuplicate=\"Постави (дубликат)\"\nRemuxRecordings=\"Ремултиплексирани записи\"\nNext=\"Напред\"\nBack=\"Назад\"\nDefaults=\"По подразбиране\"\nRestoreDefaults=\"По подразбиране\"\nHideMixer=\"Скрий в Смесителя\"\nTransitionOverride=\"Налагане на преминавания\"\nShowTransition=\"Показване на прехода\"\nHideTransition=\"Скриване на прехода\"\nNone=\"Без\"\nStudioMode.Preview=\"Преглед\"\nStudioMode.Program=\"Програма\"\nStudioMode.PreviewSceneName=\"Нагледно: %1\"\nStudioMode.ProgramSceneName=\"Програма: %1\"\nShowInMultiview=\"Показване в множествен изглед\"\nVerticalLayout=\"Вертикално разположение\"\nGroup=\"Група\"\nDoNotShowAgain=\"Не показвай отново\"\nDefault=\"(По подразбиране)\"\nCalculating=\"Калкулира се...\"\nFullscreen=\"Цял екран\"\nWindowed=\"Прозорец\"\nRefreshBrowser=\"Опресняване\"\nAspectRatio=\"Съотношение <b>%1%2</b>\"\nLockVolume=\"Заключване на обема\"\nLogViewer=\"Преглед на дневника\"\nShowOnStartup=\"Покажи при стартиране\"\nOpenFile=\"Отваряне на файл\"\nAddSource=\"Добавяне на източник\"\nRemoveScene=\"Премахни избраната сцена\"\nRemoveSource=\"Премахване на избраните източници\"\nMoveSceneUp=\"Преместване сцената отгоре\"\nMoveSceneDown=\"Премести сцената отдолу\"\nMoveSourceUp=\"Преместване на източниците отгоре\"\nMoveSourceDown=\"Преместване на източниците отдолу\"\nSourceProperties=\"Отваряне на свойствата на източника\"\nSourceFilters=\"Отвори филтрите на източника\"\nMixerToolbarMenu=\"Меню на звуковия смесител\"\nSceneFilters=\"Отвори сценичните филтри\"\nList=\"Списък\"\nGrid=\"Решетка\"\nAutomatic=\"Автоматично\"\nPluginsFailedToLoad.Title=\"Грешка, зареждайки приставката\"\nPluginsFailedToLoad.Text=\"Неуспешно зареждане на съответните приставки на OBS:\\n\\n%1\\nОбновете или премахнете тези приставки.\"\nAlreadyRunning.Title=\"OBS вече се изпълнява\"\nAlreadyRunning.Text=\"OBS вече е включен! Освен ако не е по желание, моля изключете другите работещи инстанции на OBS преди да включите нова. Ако сте настроили OBS да се минимизира във системната табла, моля проверете дали все още е включен там.\"\nAlreadyRunning.LaunchAnyway=\"Стартирай въпреки това\"\nSafeMode.Restart=\"Искате ли да рестартирате OBS в безопасен режим (приставки на трети страни, скриптове, изключени WebSocket)?\"\nSafeMode.RestartNormal=\"Искате ли да рестартирате OBS в нормален режим?\"\nChromeOS.Title=\"Неподдържана платформа\"\nChromeOS.Text=\"OBS е стартиран в контейнер на ChromeOS. Тази платформа не се поддържа.\"\nWine.Title=\"Wine открит\"\nWine.Text=\"Използването на OBS в Wine не се поддържа, много от функциите като заснемане или източници на устройство ще работят с ограничен капацитет или няма да работят.<br><br>Препоръчително е да се използва нативна версия на OBS, например <a href='https://flathub.org/apps/details/com.obsproject.Studio'>нашата Flatpak версия</a> или пакетите на вашата операционна система.\"\nDockCloseWarning.Title=\"Затваряне на докинг прозорец\"\nDockCloseWarning.Text=\"Затворихте закачваем прозорец. Ако искате да го отворите отново, използвайте меню \\\"Закачваеми\\\" в лентата с менютата.\"\nExtraBrowsers=\"Ползвателски панели за браузър\"\nExtraBrowsers.Info=\"Добавете панели, като им приложите име и интернет адрес, а след това натиснете Приложи или Затвори, за да ги отворите. Може да добавяте или премахвате панели по всяко време.\"\nExtraBrowsers.DockName=\"Име на панела\"\nAuth.Authing.Title=\"Удостоверяване...\"\nAuth.Authing.Text=\"Удостоверяване с %1, моля изчакайте\"\nAuth.AuthFailure.Title=\"Неуспешно удостоверяване\"\nAuth.AuthFailure.Text=\"Неуспешно удостоверяване с %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Изисква се удостоверяване\"\nAuth.InvalidScope.Text=\"Изискванията за удостоверяване за %1 са променени. Някои функции може да не са достъпни.\"\nAuth.LoadingChannel.Title=\"Зареждане на информация за канала...\"\nAuth.LoadingChannel.Text=\"Зареждане на информация за канала %1, моля изчакайте...\"\nAuth.LoadingChannel.Error=\"Не можа да се зареди информацията на вашия канал\"\nAuth.ChannelFailure.Title=\"Неуспешно зареждане на канала\"\nAuth.ChannelFailure.Text=\"Неуспешно зареждане на информацията за канала %1\\n\\n%2: %3\"\nAuth.Chat=\"Чат\"\nAuth.StreamInfo=\"Сведения за потока\"\nTwitchAuth.Stats=\"Статистика за Twitch\"\nTwitchAuth.Feed=\"Канал за активност в Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Не може да се направи заявка за ключа на потока\"\nTwitchAuth.TwoFactorFail.Text=\"OBS не успя да се свърже с акаунта ви в Twitch. Моля, уверете се, че двуфакторното удостоверяване е настроено във вашите <a href='https://www.twitch.tv/settings/security'>Twitch настройки за сигурност</a>, тъй като това е необходимо за стрийминг.\"\nRestreamAuth.Channels=\"Канали Restream\"\nCopy.Filters=\"Копиране на филтрите\"\nPaste.Filters=\"Поставяне на филтрите\"\nBrowserPanelInit.Title=\"Установяване на първонач. състояние на браузъра...\"\nBrowserPanelInit.Text=\"Почакайте да се инициализира браузърът...\"\nBandwidthTest.Region=\"Регион\"\nBandwidthTest.Region.US=\"САЩ\"\nBandwidthTest.Region.EU=\"Европа\"\nBandwidthTest.Region.Asia=\"Азия\"\nBandwidthTest.Region.Other=\"Друго\"\nBasic.AutoConfig=\"Вълшебник за самонастройване\"\nBasic.AutoConfig.ApplySettings=\"Прилагане на настройките\"\nBasic.AutoConfig.StartPage=\"Сведения за употреба\"\nBasic.AutoConfig.StartPage.SubTitle=\"Посочете за какво искате да използвате програмата\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Оптимизиране за поточно предаване, записът е второстепенен\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Оптимизиране само за записи, няма да предавам поточно\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Ще използвам само виртуалната камера\"\nBasic.AutoConfig.VideoPage=\"Видеонастройки\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Въведете желаните видео настройки, които искате да използвате\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Използвай текущата (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Диспей %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Използвай Текущи (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Или 60 или 30 кадъра, но предпочитам 60 когато е възможно\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Или 60 или 30 кадъра, но предпочитам по-висока резолюция\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Бележка: резолюцията на платно (основата) не е задължително да бъде същата като резолюцията, с която ще излъчвате или записвате. Реалната разделителна способност на потока / записа може да бъде намалена от резолюцията на платното, за да се намалят използваните системни ресурси или битрейт.\"\nBasic.AutoConfig.StreamPage=\"Сведения за потока\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Въведете сведенията за потока си\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Свързване на акаунт (препоръчително)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Прекъсване на връзката с акаунта\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Прекъсвате ли връзката с акаунта?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Тази промяна се прилага незабавно. Сигурни ли сте, че искате да прекъснете връзката с акаунта си?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Получи ключ за излъчване\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Още инфо\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Използвай ключ за излъчване\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Използвай Stream Key (за напреднали)\"\nBasic.AutoConfig.StreamPage.Service=\"Услуга\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Покажи всички...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"По избор...\"\nBasic.AutoConfig.StreamPage.Server=\"Сървър\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Стрийм ключ\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: Въведете криптиращата фраза.\\nRTMP: Въведете ключа предоставен от услугата.\\nSRT: Въведете stream ID, ако услугата го използва.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Ключ за кодер\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Носител Token\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Свързан акаунт\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Прогнозиране на битрейта с тест за трафик (може да отнеме няколко минути)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Предпочитам хардуерно кодиране\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Хардуерното кодиране намалява по-голямата част от употребата на процесора, но може да изисква повече битрейт, за да получи същото ниво на качество.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Предупреждение за излъчването\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Тестът за трафик е на път да излъчи разнообразни видео данни без аудио сигнал към вашия канал. Ако сте в състояние, препоръчваме временно да изключите запазването на видеоклипове и да зададете излъчването като лично докато не приключи теста. Продължи?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Проба %1\"\nBasic.AutoConfig.TestPage=\"Крайни резултати\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Програмата изпълнява набор от тестове, за да прецени най-идеалните настройки\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Тестването завърши\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Провежда се тест за трафик, може да отнеме няколко минути...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Няма намерен отговор за протокола на тази услуга.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Свързване към: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Неуспешно свързването към сървъра, проверете връзката си с интернет и опитайте отново.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Тестване на трафик към: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Тестване на енкодера за излъчване, може да отнеме минутка...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Тестване на енкодера за записване, може да отнеме минутка...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Неуспешно стартиране на енкодер\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Тестване %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Поточен кодер\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Кодиране на записи\"\nBasic.AutoConfig.TestPage.Result.Header=\"Програмата определи тези приблизителни настройки за най-оптимални във вашия случай:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"За да ползвате тези настройки натиснете върху Приложи Настройките. За да промените настройките н тестовете и за да опитате отново, натиснете Назад. За ръчно настройване, натиснете Отхвърли и отворете Настройките.\"\nBasic.AutoConfig.Info=\"Авто-конфигурационният съветник ще избере най-добрите настройки според вашите компютърни спецификации и интернет скорост.\"\nBasic.AutoConfig.RunAnytime=\"Това може да бъде стартирано по всяко време от меню Инструменти\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Резолюция на поточното предаване (мащабирана)\"\nBasic.Stats=\"Статистика\"\nBasic.Stats.CPUUsage=\"Употреба на ЦПУ\"\nBasic.Stats.HDDSpaceAvailable=\"Свободно място на диска\"\nBasic.Stats.MemoryUsage=\"Използвана памет\"\nBasic.Stats.AverageTimeToRender=\"Средно време за рендиране на кадър\"\nBasic.Stats.SkippedFrames=\"Изпуснати кадри заради забавяне във кодирането\"\nBasic.Stats.MissedFrames=\"Брой изпуснати кадри поради забавяне във изобразяването\"\nBasic.Stats.Output.Stream=\"Поток\"\nBasic.Stats.Output.Recording=\"Записване\"\nBasic.Stats.Status=\"Състояние\"\nBasic.Stats.Status.Recording=\"Записване\"\nBasic.Stats.Status.Live=\"НА ЖИВО\"\nBasic.Stats.Status.Reconnecting=\"Повторно свързване\"\nBasic.Stats.Status.Inactive=\"Неактивно\"\nBasic.Stats.Status.Active=\"Активен\"\nBasic.Stats.DroppedFrames=\"Изпуснати Кадри (Мрежа)\"\nBasic.Stats.MegabytesSent=\"Общо количество изпратени данни\"\nBasic.Stats.Bitrate=\"Битрейт\"\nBasic.Stats.DiskFullIn=\"Дискът е пълен за (около)\"\nBasic.Stats.ResetStats=\"Изчисти статистика\"\nResetUIWarning.Title=\"Сигурни ли сте че искате да нулирате потребителския интерфейс?\"\nResetUIWarning.Text=\"Нулирането на потребителския интерфейс ще скрие допълните панели. За да останат панелите видими, трябва да премахнете настройката за тяхното скриване.\\n\\nНаистина ли искате да продължите?\"\nUpdater.Title=\"Има ново обновление\"\nUpdater.Text=\"Има налично ново обновление:\"\nUpdater.UpdateNow=\"Обнови сега\"\nUpdater.RemindMeLater=\"Напомни ми по-късно\"\nUpdater.Skip=\"Пропуск на версията\"\nUpdater.NoUpdatesAvailable.Title=\"Няма налични обновления\"\nUpdater.NoUpdatesAvailable.Text=\"Няма текущи обновявания на разположение\"\nUpdater.BranchNotFound.Title=\"Премахнат канал за обновления\"\nUpdater.BranchNotFound.Text=\"Вашия избран канал за обновления вече не е наличен и настройките на OBS бяха върнати по подразбиране.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Не е налична проверка на целостта\"\nUpdater.RepairButUpdatesAvailable.Text=\"Проверяването на целостта на файловете е възможно само при последната версия. Използвайте Помощ -> Проверете за ъпдейти, да потвърдите и обновите Вашата OBS инсталация.\"\nUpdater.RepairConfirm.Title=\"Потвърди проверка на целостта\"\nUpdater.RepairConfirm.Text=\"Стартирането на проверка на целостта ще огледа Вашата OBS инсталация за повреди и изтегли наново повредени/модифицирани файлове. Операцията ще отнеме малко време.\\n\\nЖелаете ли да продължите?\"\nUpdater.FailedToLaunch=\"Неуспешно стартиране на обновителя\"\nQuickTransitions.SwapScenes=\"Обръщане на местата на сцените за преглед и програми след прехода\"\nQuickTransitions.SwapScenesTT=\"Обръща местата на сцените за преглед и програма след прехода (ако оригиналната сцена на програмата все още съществува).\\nТова не отменя промените, които е възможно да са били направени в оригиналната сцена на програмата.\"\nQuickTransitions.DuplicateScene=\"Дубликат на сцената\"\nQuickTransitions.DuplicateSceneTT=\"Редактирането на същата сцена позволява променяне на трансформиране/видимост на източниците без модифициране на изходните данни на програмата.\\nЗа да редактирате свойствата на източниците без да модифицирате изходните данни на програмата, включете \\\"дублиране на източници\\\".\\nПроменянето на тази стойност ще нулира сегашната сцена на програмата(ако та съществува)\"\nQuickTransitions.EditProperties=\"Дубликат на източниците\"\nQuickTransitions.EditPropertiesTT=\"Редактирането на същата сцена позволява променяне на свойствата на източниците без модифициране на изходните данни на програмата.\\nТова може да бъде използвано, ако \\\"дублиране на източници\\\" е включено.\\nОпределени източници(като capture карта или медия източници) не поддържат това и не могат да бъдат редактирани поотделно.\\nПроменянето на сегашната стойност ще нулира настоящата програмна сцена(ако та съществува).\\n\\nВнимание: Понеже източниците ще бъдат дублирани, ще бъдат използвани допълнителни системни и видео ресурси.\"\nQuickTransitions.HotkeyName=\"Бърз преход: %1\"\nBasic.AddTransition=\"Добавяне на настройваем преход\"\nBasic.RemoveTransition=\"Премахване на настройваем преход\"\nBasic.TransitionProperties=\"Настройки на прехода\"\nBasic.SceneTransitions=\"Преход между сцените\"\nBasic.TransitionDuration=\"Времетраене\"\nBasic.TogglePreviewProgramMode=\"Режим Студио\"\nBasic.EnablePreviewProgramMode=\"Включване на режим студио\"\nBasic.DisablePreviewProgramMode=\"Изключване на режим студио\"\nUndo.Undo=\"Отмяна\"\nUndo.Redo=\"Възстановяване\"\nUndo.Add=\"Добавяне на '%1'\"\nUndo.Delete=\"Изтриване на %1\"\nUndo.Rename=\"Преименуване на %1\"\nUndo.SceneCollection.Switch=\"Превключване към '%1'\"\nUndo.Item.Undo=\"Отмяна на %1\"\nUndo.Item.Redo=\"Повтаряне на %1\"\nUndo.Sources.Multi=\"изтриване на %1 източника\"\nUndo.Filters=\"Настройки на филтъра на '%1'\"\nUndo.Filters.Paste.Single=\"Поставяне на филтър '%1' в '%2'\"\nUndo.Filters.Paste.Multiple=\"Копиране на филтри от '%1' в '%2'\"\nUndo.Transform=\"Преобразуване на източници в '%1'\"\nUndo.Transform.Paste=\"Поставяне на настройки за преобразуване в '%1'\"\nUndo.Transform.Rotate=\"Завъртане в '%1'\"\nUndo.Transform.Reset=\"Нулиране на настройки за преобразуване в '%1'\"\nUndo.Transform.HFlip=\"Огледално хоризонтално завъртане в \\\"%1\\\"\"\nUndo.Transform.VFlip=\"Огледално вертикално завъртане в '%1'\"\nUndo.Transform.FitToScreen=\"Побери в екран в '%1'\"\nUndo.Transform.StretchToScreen=\"Разтягане по размера на екрана в '%1'\"\nUndo.Transform.Center=\"Центрирай в екрана в '%1'\"\nUndo.Transform.VCenter=\"Вертикално центриране в екрана в '%1'\"\nUndo.Transform.HCenter=\"Хоризонтално центриране на екрана в '%1'\"\nUndo.Volume.Change=\"Промяна на силата на звука на '%1'\"\nUndo.Volume.Mute=\"Заглуши '%1'\"\nUndo.Volume.Unmute=\"Отглуши '%1'\"\nUndo.Balance.Change=\"Промяна на баланса на звука за '%1'\"\nUndo.SyncOffset.Change=\"Промяна на разместването на синхронизацията на звука за '%1'\"\nUndo.MonitoringType.Change=\"Смяна на аудиомониторинга за '%1'\"\nUndo.Mixers.Change=\"Промяна на звуковите смесители за '%1'\"\nUndo.ForceMono.On=\"Включване на принудително моно за '%1'\"\nUndo.ForceMono.Off=\"Изключване на принудително моно на '%1'\"\nUndo.Properties=\"Промяна на свойствата на източника за '%1'\"\nUndo.Scene.Duplicate=\"Дублиране на Сцената '%1'\"\nUndo.ShowTransition=\"Показване на прехода на '%1'\"\nUndo.HideTransition=\"Скриване на прехода на '%1'\"\nUndo.ShowSceneItem=\"показване на '%1' в '%2'\"\nUndo.HideSceneItem=\"скриване на '%1' в '%2'\"\nUndo.ReorderSources=\"Пренареждане на източниците в '%1'\"\nUndo.MoveUp=\"Преместване на '%1' нагоре в '%2'\"\nUndo.MoveDown=\"Преместване на '%1' надолу в '%2'\"\nUndo.MoveToTop=\"Преместване на '%1' на върха в '%2'\"\nUndo.MoveToBottom=\"Преместване на '%1' на дъното в '%2'\"\nUndo.PasteSource=\"Поставяне на източниците в '%1'\"\nUndo.PasteSourceRef=\"Поставяне на връзките за източниците в '%1'\"\nUndo.GroupItems=\"Обединяване на елементите в '%1'\"\nTransitionNameDlg.Text=\"Моля въведете имато на преминаването\"\nTransitionNameDlg.Title=\"Има на преминаването\"\nTitleBar.SafeMode=\"БЕЗОПАСЕН РЕЖИМ\"\nTitleBar.PortableMode=\"Портативен режим\"\nTitleBar.Profile=\"Профил\"\nTitleBar.Scenes=\"Сцени\"\nNameExists.Title=\"Името вече съществува\"\nNameExists.Text=\"Името вече е ползвано.\"\nNoNameEntered.Title=\"Въведете валидно име\"\nNoNameEntered.Text=\"Не може да използвате празни имена.\"\nConfirmStart.Title=\"Започни излъчването?\"\nConfirmStart.Text=\"Наистина ли искате да започнете с поточното предаване?\"\nConfirmStop.Title=\"Спирате ли излъчването?\"\nConfirmStop.Text=\"Сигурни ли сте че искате да спрете предаването?\"\nConfirmStopRecord.Title=\"Спирате ли записването?\"\nConfirmStopRecord.Text=\"Наистина ли искате да спрете записването?\"\nConfirmBWTest.Title=\"Да започне ли тест за честотната лента?\"\nConfirmBWTest.Text=\"OBS е конфигуриран в режим на тестване на честотната лента. Този режим позволява тестване на мрежата, без каналът ви да работи на живо. След като приключите с тестването, ще трябва да го деактивирате, за да могат зрителите да виждат вашия поток.\\n\\nИскате ли да продължите?\"\nConfirmRemove.Title=\"Потвърждаване на премахване\"\nConfirmRemove.Text=\"Сигурни ли сте че искате да премахнете'%1'?\"\nConfirmRemove.TextMultiple=\"Сигурни ли сте че искате да премахнете %1 избрани обекта?\"\nConfirmReset.Title=\"Нулиране на свойствата\"\nConfirmReset.Text=\"Сигурни ли сте че искате да върнете настоящите свойства по подразбиране?\"\nOutput.StartStreamFailed=\"Неуспешно стартиране на предаване\"\nOutput.StartRecordingFailed=\"Неуспешно стартиране на запис\"\nOutput.StartReplayFailed=\"Неуспешно включване на Буферa за повторение\"\nOutput.StartVirtualCamFailed=\"Неуспешно пускане на виртуалната камера\"\nOutput.StartFailedGeneric=\"Започването на изкарване на кадри неуспешно. Моля проверете списъка за подробности. \\n\\n Бележка: Ако ползвате кодиране NVENC или AMD, моля проверете дали версията на драйвъри за видео картата е актуална.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Не можете да запазвате повторения, докато сте на пауза\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Внимание: Повторенията не могат да се запазват, докато записът е спрян.\"\nOutput.ConnectFail.Title=\"Неуспешно свързване\"\nOutput.ConnectFail.BadPath=\"Невалиден път или URL. Проверете дали настройките ви са валидни.\"\nOutput.ConnectFail.ConnectFailed=\"Неуспешна връзка със сървъра\"\nOutput.ConnectFail.InvalidStream=\"Неуспешно сързване към посоченият канал или ключ за предаване, моля проверете повторно вашия ключ. Ако е верен, може би има проблем със всъзката към сървъра.\"\nOutput.ConnectFail.HdrDisabled=\"Изходът на HDR в момента е изключен за този изход.\"\nOutput.ConnectFail.Error=\"Неочаквана грешка при опит за връзка със сървъра. Повече информация в \\\"log\\\" файла.\"\nOutput.ConnectFail.Disconnected=\"Изключен от сървъра.\"\nOutput.StreamEncodeError.Title=\"Грешка при кодиране\"\nOutput.StreamEncodeError.Msg=\"По време на стрийминга е възникнала грешка с кодирането.\"\nOutput.StreamEncodeError.Msg.LastError=\"Възникна грешка в кодера, предавайки поточно:<br><br>%1\"\nOutput.RecordFail.Title=\"Неуспешно стартиране на запис\"\nOutput.RecordFail.Unsupported=\"Формата на записа или не е подържа или не подържа повече от една писта за звук. Моля проверете настройките си и опитайте отново.\"\nOutput.RecordNoSpace.Title=\"Недостатъчно място на диска\"\nOutput.RecordNoSpace.Msg=\"Няма достатъчно място на диска за да продължите да записвате.\"\nOutput.RecordError.Title=\"Грешка при записването\"\nOutput.RecordError.Msg=\"Неопределена грешка по време на записването.\"\nOutput.RecordError.EncodeErrorMsg=\"По време на записа е възникнала грешка с кодирането.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Възникна грешка в кодера, записвайки:<br><br>%1\"\nOutput.BadPath.Title=\"Лош път на файла\"\nOutput.BadPath.Text=\"Конфигурираният Път на Запис не можа да бъде отворен. Моля проверете вашия Път на Запис от Настройки -> Излъчване -> Запис\"\nOutput.NoBroadcast.Title=\"Излъчването не е настроено\"\nOutput.NoBroadcast.Text=\"Трябва да настроите излъчването, за да може да започнете предаването на живо.\"\nOutput.BroadcastStartFailed=\"Неуспешно начало на излъчването\"\nOutput.BroadcastStopFailed=\"Неуспешно спиране на излъчването\"\nRemux.SourceFile=\"OBS запис\"\nRemux.TargetFile=\"Целеви файл\"\nRemux.Remux=\"Ремултиплексиране\"\nRemux.Stop=\"Стоп на ремултиплексирането\"\nRemux.ClearFinished=\"Изчисти готовите елементи\"\nRemux.ClearAll=\"Изчисти всички елементи\"\nRemux.OBSRecording=\"OBS запис\"\nRemux.FinishedTitle=\"Ремултиплексирането завърши\"\nRemux.Finished=\"Записването е ремултиплексирано\"\nRemux.FinishedError=\"Записването е ремултиплексирано, но файлът може да е непълен\"\nRemux.SelectRecording=\"Избиране на OBS запис...\"\nRemux.SelectTarget=\"Изберете целеви файл...\"\nRemux.FileExistsTitle=\"Целевите файлове съществуват\"\nRemux.FileExists=\"Следните целеви файлове вече съществуват. Желаете ли да ги замените?\"\nRemux.ExitUnfinishedTitle=\"Ремултиплексира се\"\nRemux.ExitUnfinished=\"Конвертирането не е завършено все още, ако го спрете сега файлът може да бъде неизползваем.\\nНаистина ли искате да спрете ремултиплексирането?\"\nRemux.HelpText=\"Пуснете файловете чрез провлачване в този прозорец, за да ги ремултиплексирате или изберете празната клетка \\\"OBS запис\\\" за избор на файл.\"\nRemux.NoFilesAddedTitle=\"Не е добавен файл за конвертиране\"\nRemux.NoFilesAdded=\"Не е добавен файл за конвертиране. Плъзнете папка, съдържаща един или няколко видеофайла.\"\nMissingFiles=\"Липсващи файлове\"\nMissingFiles.MissingFile=\"Липсващ файл\"\nMissingFiles.NewFile=\"Нов файл\"\nMissingFiles.HelpText=\"Някои файлове липсват, откакто сте използвали OBS за последен път.\"\nMissingFiles.Clear=\"<изчистено>\"\nMissingFiles.NumFound=\"Намерен/и %1 от %2\"\nMissingFiles.Search=\"Търсене в Директория...\"\nMissingFiles.SelectFile=\"Избор на файл...\"\nMissingFiles.SelectDir=\"Избор на папка за търсене\"\nMissingFiles.State=\"Състояние\"\nMissingFiles.Missing=\"Липсва\"\nMissingFiles.Replaced=\"Заменено\"\nMissingFiles.Cleared=\"Изчистено\"\nMissingFiles.Found=\"Намерено\"\nMissingFiles.AutoSearch=\"Намерени са допълнителни съвпадения на файлове\"\nMissingFiles.AutoSearchText=\"OBS е открила допълнителни съвпадения за липсващи файлове в тази директория. Искате ли да ги добавите?\"\nMissingFiles.NoMissing.Title=\"Проверка за липсващи файлове\"\nMissingFiles.NoMissing.Text=\"Не бяха намерени липсващи файлове\"\nMacPermissions.MenuAction=\"Оглед на позволенията на приложението...\"\nMacPermissions.Title=\"Оглед на позволенията на приложението\"\nMacPermissions.Description=\"OBS Studio изисква вашето разрешение, за да може да осигури определени функции. Препоръчва се да разрешите тези разрешения, но те не са задължителни за използване на приложението, освен това, Винаги можете да ги активирате по-късно.\"\nMacPermissions.Description.OpenDialog=\"Можете повторно да отворите този диалог от менюто на OBS Studio.\"\nMacPermissions.AccessGranted=\"Предоставен достъп\"\nMacPermissions.RequestAccess=\"Поискване на достъп\"\nMacPermissions.OpenPreferences=\"Отвори %1 настройките.\"\nMacPermissions.Item.ScreenRecording=\"Заснемане на екрана\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS изисква това разрешение, за да може да заснеме екрана ви.\"\nMacPermissions.Item.Camera=\"Камера\"\nMacPermissions.Item.Camera.Details=\"Това разрешение е необходимо, за да може да бъде заснето съдържание от уеб камера или карта за заснемане на видео.\"\nMacPermissions.Item.Microphone=\"Микрофон\"\nMacPermissions.Item.Microphone.Details=\"OBS изисква това разрешение, ако искате да записвате звука от микрофона Ви или от външно аудио устройство.\"\nMacPermissions.Item.Accessibility=\"Достъпност\"\nMacPermissions.Continue=\"Продължаване\"\nSourceLeak.Title=\"Грешка, почиствайки източника\"\nSourceLeak.Text=\"Възникна проблем при промяната на колекциите на сцени и някои източници не можаха да бъдат спрени. Тази грешка обикновено е предизвикана от приставки, които не освобождават ресурси правилно. Моля уверете се, че всички приставки, които използвате, са актуални.\\n\\nOBS Studio сега ще се затвори, за да се избегне евентуална повреда на данните.\"\nBasic.DesktopDevice1=\"Аудио - работен плот\"\nBasic.DesktopDevice2=\"Аудио - работен плот 2\"\nBasic.AuxDevice1=\"Микрофон/Aux\"\nBasic.AuxDevice2=\"Микрофон/Aux 2\"\nBasic.AuxDevice3=\"Микрофон/Aux 3\"\nBasic.AuxDevice4=\"Микрофон/Aux 4\"\nBasic.Scene=\"Сцена\"\nBasic.DisplayCapture=\"Заснемане на екрана\"\nBasic.Main.PreviewConextMenu.Enable=\"Разреши преглед\"\nBasic.Main.Preview.Disable=\"Изключи прегледа\"\nScaleFiltering=\"Мащабно Филтриране\"\nScaleFiltering.Point=\"Точково\"\nScaleFiltering.Bilinear=\"Двулинейно\"\nScaleFiltering.Bicubic=\"Двукубово\"\nScaleFiltering.Lanczos=\"Ланцош\"\nScaleFiltering.Area=\"Зона\"\nBlendingMethod=\"Метод на смесване\"\nBlendingMethod.Default=\"По подразбиране\"\nBlendingMethod.SrgbOff=\"SRGB изкл.\"\nBlendingMode=\"Режим на смесване\"\nBlendingMode.Normal=\"Нормален\"\nBlendingMode.Additive=\"Добави\"\nBlendingMode.Subtract=\"Разделящ\"\nBlendingMode.Screen=\"Екран\"\nBlendingMode.Multiply=\"Умножение\"\nBlendingMode.Lighten=\"Осветление\"\nBlendingMode.Darken=\"Затъмнение\"\nDeinterlacing=\"Непреплитане\"\nDeinterlacing.Discard=\"Отхвърли\"\nDeinterlacing.TopFieldFirst=\"Първо горното поле\"\nDeinterlacing.BottomFieldFirst=\"Първо долното поле\"\nVolControl.SliderUnmuted=\"Регулатор на звука за '%1':\"\nVolControl.SliderMuted=\"Регулатор на звука за '%1': (понастоящем заглушен)\"\nVolControl.Mute=\"Заглуши '%1'\"\nVolControl.Properties=\"Свойства на '%1'\"\nVolControl.UnassignedWarning.Title=\"Неназначен звуков източник\"\nVolControl.UnassignedWarning.Text=\"На „%1“ не са назначени никакви аудио записи и няма да има звук в поточни предавания или записи.\\n\\nЗа да изберете аудио източник за запис, отворете разширените аудио свойства чрез менюто с десен бутон или с бутона за настройки в лентата с инструменти на аудио миксера.\"\nBasic.Main.AddSceneDlg.Title=\"Добави сцена\"\nBasic.Main.AddSceneDlg.Text=\"Моля, въведете името на сцената\"\nBasic.Main.DefaultSceneName.Text=\"Сцена %1\"\nBasic.Main.AddSceneCollection.Title=\"Добавяне на колекция\"\nBasic.Main.AddSceneCollection.Text=\"Моля, въведете името на колекцията от сцени\"\nBasic.Main.RenameSceneCollection.Title=\"Преименуване на Колекцията от сцени\"\nBasic.Main.RemigrateSceneCollection.Title=\"Обнови резолюцията на колекцията на сцени\"\nBasic.Main.RemigrateSceneCollection.Text=\"Искате ли да обновите резолюцията на колекцията на сцени на „%1“, за да съответства на резолюцията на рамката на текущия профил на %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Не може да се обнови резолюцията на колекцията на сцени, докато изходите са активни.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Неуспешно обновяване на резолюцията на колекцията на сцени. Началната резолюция е неизвестна.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Невъзможен рестарт – възникна грешка при промяната на резолюцията на OBS.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Невъзможен рестарт – текущата резолюция вече е основната резолюция на колекцията на сцени.\"\nAddProfile.Title=\"Добавяне на профил\"\nAddProfile.Text=\"Моля, въведете името на профила\"\nAddProfile.WizardCheckbox=\"Покажи помощника за автнастройка\"\nRenameProfile.Title=\"Преименуване на профил\"\nBasic.Main.MixerRename.Title=\"Преименуване на Звуков Източник\"\nBasic.Main.MixerRename.Text=\"Моля, въведете името на източника на звук\"\nBasic.Main.PreviewDisabled=\"Прегледа в момента е забранен\"\nBasic.SourceSelect=\"Създай/избери източник\"\nBasic.SourceSelect.CreateNew=\"Създай нов\"\nBasic.SourceSelect.AddExisting=\"Добави съществуващ\"\nBasic.SourceSelect.AddVisible=\"Направете видим източника\"\nBasic.Main.Sources.Visibility=\"Видимост\"\nBasic.Main.Sources.VisibilityDescription=\"Управлява видимостта на '%1' в рамката\"\nBasic.Main.Sources.Lock=\"Заключване\"\nBasic.Main.Sources.LockDescription=\"Заключва позицията и мащаба на '%1' в рамката\"\nBasic.PropertiesWindow=\"Свойства на '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (автоматично: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Избор на цвят\"\nBasic.PropertiesWindow.SelectFont=\"Избор на шрифт\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Подбор на шрифт\"\nBasic.PropertiesWindow.ConfirmTitle=\"Настройките са променени\"\nBasic.PropertiesWindow.Confirm=\"Има незаписани промени. Искате ли да ги запишете?\"\nBasic.PropertiesWindow.NoProperties=\"Без налични характеристики\"\nBasic.PropertiesWindow.AddFiles=\"Добавяне на файлове\"\nBasic.PropertiesWindow.AddDir=\"Добавяне на директория\"\nBasic.PropertiesWindow.AddURL=\"Добавяне на път/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Добави директория към '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Добавяне на файлове в \\\"%1\\\"\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Добавяне на запис към \\\"%1\\\"\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Редактиране на запис от \\\"%1\\\"\"\nBasic.PropertiesView.FPS.Simple=\"Прости Стойности на FPS\"\nBasic.PropertiesView.FPS.Rational=\"Дробни Стойности на FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Валиден обсег от Стойности на FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"Отваряне на връзката в браузъра по подразбиране?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Отваряне\"\nBasic.InteractionWindow=\"Взаимодействие с \\\"%1\\\"\"\nBasic.StatusBar.Reconnecting=\"Връзката прекъсна, свързване отново след %2 секунди(s) (опит %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Опит за повторно свързване... (опит %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Успешно свързване\"\nBasic.StatusBar.Delay=\"Забавяне (%1 сек.)\"\nBasic.StatusBar.DelayStartingIn=\"Забавяне (започване след %1 сек.)\"\nBasic.StatusBar.DelayStoppingIn=\"Забавяне (спиране след %1 сек.)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Забавяне (спиране след %1 сек., започване след %2 сек.)\"\nBasic.StatusBar.RecordingSavedTo=\"Записът запазен в: '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Буферът за повторение е запазен в '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Екранната снимка запазен в '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Записът е автоматично преправен на „%1“\"\nBasic.Filters=\"Филтри\"\nBasic.Filters.AsyncFilters=\"Аудио/видео филтри\"\nBasic.Filters.AudioFilters=\"Аудио филтри\"\nBasic.Filters.EffectFilters=\"Ефект филтри\"\nBasic.Filters.Title=\"Филтри за \\\"%1\\\"\"\nBasic.Filters.AddFilter.Title=\"Име на филтър\"\nBasic.Filters.AddFilter.Text=\"Моля задайте името на филтъра\"\nBasic.TransformWindow=\"Трансформиране на елемент от сцената\"\nBasic.TransformWindow.Position=\"Позиция\"\nBasic.TransformWindow.PositionX=\"Позиция X\"\nBasic.TransformWindow.PositionY=\"Позиция Y\"\nBasic.TransformWindow.Rotation=\"Завъртане\"\nBasic.TransformWindow.Size=\"Големина\"\nBasic.TransformWindow.Width=\"Ширина\"\nBasic.TransformWindow.Height=\"Височина\"\nBasic.TransformWindow.Alignment=\"Позиционно подравняване\"\nBasic.TransformWindow.BoundsType=\"Тип ограничител\"\nBasic.TransformWindow.BoundsAlignment=\"Подравняване в ограничителя\"\nBasic.TransformWindow.Bounds=\"Размер на ограничителя\"\nBasic.TransformWindow.BoundsWidth=\"Ширина на ограничителя\"\nBasic.TransformWindow.BoundsHeight=\"Височина на ограничителя\"\nBasic.TransformWindow.CropToBounds=\"Изрязване до ограничителната рамка\"\nBasic.TransformWindow.Crop=\"Изрязване\"\nBasic.TransformWindow.CropLeft=\"Изрязване вляво\"\nBasic.TransformWindow.CropRight=\"Изрязване вдясно\"\nBasic.TransformWindow.CropTop=\"Изрязване отгоре\"\nBasic.TransformWindow.CropBottom=\"Изрязване отдолу\"\nBasic.TransformWindow.Alignment.TopLeft=\"Горе вляво\"\nBasic.TransformWindow.Alignment.TopCenter=\"Горе в центъра\"\nBasic.TransformWindow.Alignment.TopRight=\"Горе вдясно\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Център вляво\"\nBasic.TransformWindow.Alignment.Center=\"Център\"\nBasic.TransformWindow.Alignment.CenterRight=\"Център вдясно\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Долу вляво\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Долу в центъра\"\nBasic.TransformWindow.Alignment.BottomRight=\"Долу вдясно\"\nBasic.TransformWindow.BoundsType.None=\"Без граници\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Само максимален размер\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Мащабирай до вътрешните граници\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Мащабирай до външните граници\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Мащабирай до широчината на границите\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Мащабирай до височината на границите\"\nBasic.TransformWindow.BoundsType.Stretch=\"Разтегни до границите\"\nBasic.TransformWindow.Title=\"Промяна на преобразуванието за '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Не е избран източник\"\nBasic.Main.AddSourceHelp.Title=\"Не може да се добави източник\"\nBasic.Main.AddSourceHelp.Text=\"Трябва да имате поне една сцена, за да добавите източник.\"\nBasic.Main.Scenes=\"Сцени\"\nBasic.Main.Sources=\"Източници\"\nBasic.Main.Source=\"Източник\"\nBasic.Main.Controls=\"Управление\"\nBasic.Main.PreparingStream=\"Подготовка...\"\nBasic.Main.Connecting=\"Свързване...\"\nBasic.Main.StartRecording=\"Започни запис\"\nBasic.Main.StartReplayBuffer=\"Включване на Буферa за Повторение\"\nBasic.Main.SaveReplay=\"Записване на повторението\"\nBasic.Main.StartStreaming=\"Започни стрийм\"\nBasic.Main.StartBroadcast=\"Към предаване на живо\"\nBasic.Main.StartVirtualCam=\"Начало на виртуалната камера\"\nBasic.Main.StopRecording=\"Стоп на записа\"\nBasic.Main.PauseRecording=\"Пауза на записа\"\nBasic.Main.UnpauseRecording=\"Отпускане на паузата на записа\"\nBasic.Main.SplitFile=\"Раздели записващия файл\"\nBasic.Main.StoppingRecording=\"Спиране на записа...\"\nBasic.Main.StopReplayBuffer=\"Изкючване на Буферa за Повторение\"\nBasic.Main.StoppingReplayBuffer=\"Спиране на Буферa за Повторение...\"\nBasic.Main.SetupBroadcast=\"Управление на излъчването\"\nBasic.Main.StopStreaming=\"Спри стрийм\"\nBasic.Main.StopBroadcast=\"Спиране на излъчването\"\nBasic.Main.AutoStopEnabled=\"(Автоспиране)\"\nBasic.Main.StoppingStreaming=\"Спиране на излъчването...\"\nBasic.Main.ForceStopStreaming=\"Спри излъчването (без забавяне)\"\nBasic.Main.ShowContextBar=\"Покажи лентата с инструменти на източника\"\nBasic.Main.HideContextBar=\"Скрий панела с инструменти на източника\"\nBasic.Main.StopVirtualCam=\"Спри виртуалната камера\"\nBasic.Main.Group=\"Група %1\"\nBasic.Main.GroupItems=\"Групиране на избраните елементи\"\nBasic.Main.Ungroup=\"Разгрупирай\"\nBasic.Main.GridMode=\"Режим решетка\"\nBasic.Main.ListMode=\"Режим списък\"\nBasic.Main.VirtualCamConfig=\"Конфигуриране на виртуалната камера\"\nBasic.VCam.VirtualCamera=\"Виртуална камера\"\nBasic.VCam.OutputType=\"Тип на изходните данни\"\nBasic.VCam.OutputSelection=\"Избор на изходно устройство\"\nBasic.VCam.OutputType.Program=\"Програма (По подразбиране)\"\nBasic.VCam.OutputSelection.NoSelection=\"Няма избор за този тип изход\"\nBasic.VCam.RestartWarning=\"Виртуалната камера ще се рестартира, за да се приложи тази промяна\"\nBasic.MainMenu.File=\"Файл (&F)\"\nBasic.MainMenu.File.Export=\"Експортиране (&E)\"\nBasic.MainMenu.File.Import=\"Импортиране (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Показване на записите (&R)\"\nBasic.MainMenu.File.Remux=\"Ремултиплексирани записи\"\nBasic.MainMenu.File.Settings=\"Настройки (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Показване на папката с настройки\"\nBasic.MainMenu.File.ShowProfileFolder=\"Показване на папката с профили\"\nBasic.MainMenu.File.ShowMissingFiles=\"Проверка за липсващи файлове\"\nBasic.MainMenu.File.Exit=\"Изход (&X)\"\nBasic.MainMenu.Edit=\"Редактиране (&E)\"\nBasic.MainMenu.Edit.Undo=\"Отмени (&U)\"\nBasic.MainMenu.Edit.Redo=\"Върни (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Заключване на прегледа (&L)\"\nBasic.MainMenu.Edit.Scale=\"Усъразмеряване на прегледа (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"Усъразмери по Прозорец\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Платно (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Запис (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Мащабирано (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"Преобразуване (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Редактиране на преобразуването... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Копиране на преобразуването\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Поставяне на преобразуването\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Анулирай трансформация (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Завърти 90 градуса по часовниковата\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Завърти 90 градуса обратно на часовниковата\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Завърти 180 градуса\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Хоризонтално обръщане (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Вертикално обръщане (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Побиране към екрана (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Разтягане до размера на екрана (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Центриране към екрана (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Вертикално центриране\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Хоризонтално центриране\"\nBasic.MainMenu.Edit.Order=\"Ред (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Преместване нагоре (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Преместване надолу (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Преместване най-отгоре (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Преместване най-отдолу (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Разширени звукови свойства (&A)\"\nBasic.MainMenu.View=\"Изглед (&V)\"\nBasic.MainMenu.View.Toolbars=\"Лента с инструменти (&T)\"\nBasic.MainMenu.View.ContextBar=\"Лента с инструменти на източника\"\nBasic.MainMenu.View.SourceIcons=\"Икони на източника (&I)\"\nBasic.MainMenu.View.StatusBar=\"Статус лента (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Интерфейс на цял екран\"\nBasic.MainMenu.View.ResetUI=\"&Нулиране на потреб. интерфейс\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Винаги най-отгоре\"\nBasic.MainMenu.View.SceneListMode=\"Режим списък със сцени\"\nBasic.MainMenu.Docks=\"Закачваеми панели (&D)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Ползвателски браузърни зак. панели(&C)\"\nBasic.MainMenu.SceneCollection=\"Колекция със сцени (&S)\"\nBasic.MainMenu.Profile=\"Профил (&P)\"\nBasic.MainMenu.Profile.Import=\"Добави Профил\"\nBasic.MainMenu.Profile.Export=\"Запис на Профил\"\nBasic.MainMenu.SceneCollection.Import=\"Добавяне на Колекция от сцени\"\nBasic.MainMenu.SceneCollection.Export=\"Износ на колекция със сцени\"\nBasic.MainMenu.Profile.Exists=\"Профилът вече съществува\"\nBasic.MainMenu.Tools=\"Инструменти (&T)\"\nBasic.MainMenu.Help=\"Помощ (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Портал за Помощ (&P)\"\nBasic.MainMenu.Help.Website=\"Посетете уебсайта (&W)\"\nBasic.MainMenu.Help.Discord=\"Страница в &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Какво ново\"\nBasic.MainMenu.Help.Logs=\"\\\"&Log\\\" файлове\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Покажи техническите записи (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Качи текущия \\\"Log\\\" файл (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Качване на предишния отчетен файл (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Преглеждане на Текущи Данни (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Бележки за изданието\"\nBasic.MainMenu.Help.CheckForUpdates=\"Проверка за обновяване\"\nBasic.MainMenu.Help.Repair=\"Проверка на целостта на файла\"\nBasic.MainMenu.Help.RestartSafeMode=\"Рестартиране в безопасен режим\"\nBasic.MainMenu.Help.RestartNormal=\"Рестартиране в нормален режим\"\nBasic.MainMenu.Help.CrashLogs=\"Доклади за сривове (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Показване на доклади за сривове (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Качване на предишния доклад за срив (&P)\"\nBasic.MainMenu.Help.About=\"За програмата (&A)\"\nBasic.Settings.ProgramRestart=\"Програмата трябва да бъде рестартирана, за да влязат в сила тези настройки.\"\nBasic.Settings.ConfirmTitle=\"Потвърди промените\"\nBasic.Settings.Confirm=\"Имате незаписани промени. Запиши промените?\"\nBasic.Settings.General=\"Общи\"\nBasic.Settings.General.Language=\"Език\"\nBasic.Settings.General.Updater=\"Обновявания\"\nBasic.Settings.General.UpdateChannel=\"Канал за обновяване\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Изключено)\"\nBasic.Settings.General.UpdateChannelDefault=\"(по подразбиране)\"\nBasic.Settings.General.EnableAutoUpdates=\"Автоматично проверяване за обновления при включване\"\nBasic.Settings.General.OpenStatsOnStartup=\"Отваряне на статистически прозорец при стартиране\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Скриване на прозорците на OBS от записа\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Тази настройка скрива всички прозорци на OBS Studio, които не са в проектора, от прихващане, като също така, влияе \\nвърху други програми, използвани за целите на конференции, отдалечен достъп, направа на снимки на екрана и т.н.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Включването на тази настройка ще скрие всички прозорци на OBS Studio, които не са в проектора, от прихващане, като също така, влияе върху други програми, използвани за целите на конференции, отдалечен достъп, направа на снимки на екрана и т.н.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Покажи диалогът за потвърждение при започване на предаване\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Покажи диалогът за потвърждение при приключване на предаване\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Показване на прозорец за потвърждение при спиране на запис\"\nBasic.Settings.General.Projectors=\"Прожектори\"\nBasic.Settings.General.HideProjectorCursor=\"Скрии показателят при движение над прожектори\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Поставяне на прожекторите над други прозорци\"\nBasic.Settings.General.Snapping=\"Залепяне и Подравняване на Източника\"\nBasic.Settings.General.ScreenSnapping=\"Залепи Източниците към ръба на екрана\"\nBasic.Settings.General.CenterSnapping=\"Залепи Източниците към хоризонталния и вертикалния център\"\nBasic.Settings.General.SourceSnapping=\"Залепи Източниците към други източници\"\nBasic.Settings.General.SnapDistance=\"Чувствителност на Залепянето\"\nBasic.Settings.General.RecordWhenStreaming=\"Автоматично записване при предаване\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Запазване на запис след приключване на предаването\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Автоматично включване на Буферa за повторение при излъчване\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Остави Буферa за повторение активен след приключване на излъчването\"\nBasic.Settings.General.SysTray=\"Системен контейнер\"\nBasic.Settings.General.SysTrayWhenStarted=\"Минимизирай във системния контейнер при включване\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Винаги минимизирай във системния контейнер вместо във лентата със програми\"\nBasic.Settings.General.SaveProjectors=\"Запамети прожекторите при изход\"\nBasic.Settings.General.CloseExistingProjectors=\"Ограничаване до един пълноекранен проектор на екран\"\nBasic.Settings.General.Preview=\"Преглед\"\nBasic.Settings.General.OverflowHidden=\"Скриване на препълването\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Препълването е винаги видимо\"\nBasic.Settings.General.OverflowSelectionHidden=\"Показване на препълването дори когато източникът е невидим\"\nBasic.Settings.General.Importers=\"Вграждачи\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Претърсване на известните места за колекции от сцени при импортиране\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Преминаване към сцена при двойно кликване\"\nBasic.Settings.General.StudioPortraitLayout=\"Включи портретен / вертикален изглед\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Показване на надписите за преглед и програма\"\nBasic.Settings.General.Multiview=\"Мулти-изглед\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Натиснете за превключване между сцените\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Покажи имената на сцените\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Показване на безопасни зони (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Разположение на Множествения Изглед\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Хоризонтал, Горе (8 сцени)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Хоризонтал, Долу (8 сцени)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Вертикал, Ляво (8 сцени)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Вертикал, Дясно (8 сцени)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Хоризонтално, Горе (18 сцени)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Хоризонтал, Горе (24 сцени)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Само сцени (4 сцени)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Само сцени (9 сцени)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Само сцени (16 сцени)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Само сцени (25 сцени)\"\nBasic.Settings.General.ChannelName.stable=\"Стабилна\"\nBasic.Settings.General.ChannelDescription.stable=\"Последна стабилна версия\"\nBasic.Settings.General.ChannelName.beta=\"Бета версии / Кандидати за издаване\"\nBasic.Settings.Appearance=\"Вид\"\nBasic.Settings.Appearance.General=\"Общи\"\nBasic.Settings.Appearance.General.Theme=\"Тема\"\nBasic.Settings.Appearance.General.Variant=\"Стил\"\nBasic.Settings.Appearance.General.NoVariant=\"Няма налични стилове\"\nBasic.Settings.Stream=\"Стрийм\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Използване на удостоверяване\"\nBasic.Settings.Stream.Custom.Username=\"Потребителско име\"\nBasic.Settings.Stream.Custom.Password=\"Парола\"\nBasic.Settings.Stream.BandwidthTestMode=\"Включване на тестов режим за прехвърляне на данни\"\nBasic.Settings.Stream.TTVAddon=\"Добавки за Twitch Chat\"\nBasic.Settings.Stream.TTVAddon.None=\"Без\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV и FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Ненастроен поток\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Отваряне на настройките\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Липсват URL-адрест и ключа за потока.\\n\\nОтворете прозореца с настройки, за да въведете URL-адреса и ключа за потока в раздел 'Поток'.\"\nBasic.Settings.Stream.MissingUrl=\"Липсва URL-адресът на потока.\\n\\nОтворете прозореца с настройки, за да въведете URL-адреса в раздел 'Поток'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Липсва ключа за потока..\\n\\nОтворете прозореца с настройки, за да въведете ключа за потока в раздел 'Поток'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Игнориране на препоръчаните настройки от услугата за излъчване\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Отхвърляне на препоръчителните настройки\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Предупреждение: Игнорирането на ограниченията, наложени от услугата, следователно може да повлияе качеството и стабилността на картината и дори да ви попречи да излъчвате.\\n\\nПродължаване?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Максимален битрейт на видеото: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Максимален битрейт на аудиото: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Максимална разд. способност: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Максимални кадри в сек.: %1\"\nBasic.Settings.Stream.ServiceCustomServer=\"Персонализиран сървър\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Включи %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Максимална честотна лента за стрийминг\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Автоматично\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Максимални видео сигнали\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Автоматично\"\nBasic.Settings.Stream.AdvancedOptions=\"Разширени настройки\"\nBasic.Settings.Output=\"Изход\"\nBasic.Settings.Output.Format=\"Формат на записа\"\nBasic.Settings.Output.Format.MKV=\"Видеоформат Матрьошка (.mkv)\"\nBasic.Settings.Output.Format.fMP4=\"Фрагментиран MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Фрагментиран MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Фрагментирания MOV записва запиза във парчета и не изисква същото финализиране като традиционните MOV файлове.\\nТова осигурява че файла остава годен за възпроизвеждане, дори ако записът на диска бъде прекъснат, за пример, в резултат на BSOD или загуба на захранване.\\n\\nТова може да не е съвместимо с всички плейъри и редактори. Използвайте Файл → Remux Recordings за да конвертирайте файла на по-съвместим формат, ако е необходимо.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Фрагментирания MP4 записва запиза във парчета и не изисква същото финализиране като традиционните MP4 файлове.\\nТова осигурява че файла остава годен за възпроизвеждане, дори ако записът на диска бъде прекъснат, за пример, в резултат на BSOD или загуба на захранване.\\n\\nТова може да не е съвместимо с всички плейъри и редактори. Използвайте Файл → Remux Recordings за да конвертирайте файла на по-съвместим формат, ако е необходимо.\"\nBasic.Settings.Output.Encoder.Video=\"Видео кодер\"\nBasic.Settings.Output.Encoder.Audio=\"Звуков енкодер\"\nBasic.Settings.Output.SelectDirectory=\"Настройте папка за записи\"\nBasic.Settings.Output.DynamicBitrate=\"Динамичен битрейт за предотвратяване на пренатоварването на мрежата\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Динамичен битрейт за предотвратяване на пренатоварването на мрежата (Бета)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Вместо пропускането на кадри, се използва динамично регулиране на битрейта в движение, за да се намали риска от пренатоварване.\\n\\nИмайте предвид,че това може да увеличи закъснението на сигнала при зрителите, ако се случи неочаквано пренатоварване.\\nНормализирането на битрейта, когато той спадне, може да отнеме до няколко минути.\\n\\nПонастоящем се поддържа само от RTMP.\"\nBasic.Settings.Output.Mode=\"Режим на излъчване\"\nBasic.Settings.Output.Mode.Simple=\"Опростен\"\nBasic.Settings.Output.Mode.Adv=\"Допълнителни настройки\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg изходен формат\"\nBasic.Settings.Output.UseReplayBuffer=\"Включване на Буферa за Повторение\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Максимален период за повторение\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Максимум памет\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Приблизително използвана памет: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Предупреждение: Очакваното използване на памет от %1 MiB е по-голямо от препоръчителния максимум от %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Не може да се определи използваната памет. Моля нстройте максималния лимит за паметта.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Представка на името на записи от Буферa за Повторение\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Надставка\"\nBasic.Settings.Output.Simple.SavePath=\"Път към записа\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Качество на записа\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Същото като излъчването\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Високо Качество, Среден Размер\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Много Високо Качество, Голям Размер на Записа\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Оригинално Качество, Изключително Голям размер на записа\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Предупреждение: битрейтът на видео потока на излъчването ще бъде зададен на %1, което е горната граница на услугата за излъчване.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Предупреждение: битрейтът на аудио потока на излъчването ще бъде зададен на %1, което е горната граница на услугата за излъчване.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Предупреждение: записите не могат да бъдат преустановявани, ако качеството на запис е зададено като \\\"Същото като потока\\\"\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Предупреждение: Текущият избран формат за запис е несъвместим с избрания кодер за поточно предаване.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Предупреждение: Записването със програмен енкодер при различно качество от излъчването ще изисква допълнителни ресурси от процесора ако записвате и излъчвате едновременно.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Внимание: Качество без загуби генерира огромни файлове! Качество без загуби може да използва до 7 гигабайта пространство за минута при високи резолюции и кадри. Качество без загуба не се препоръзва при дълги записи освен ако нямате голямо налично пространство. Буферното повтаряне не е налично при използване на качество без загуби.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Сигурни ли сте че искате да ползвате оригиналното качество на записа?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Предупреждение при ползване на Оригинално Качество!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Програмно (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Хардуер (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Хардуер (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Хардуер (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Хардуер (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Хардуер (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Хардуер (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Хардуер (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Хардуер (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Хардуер (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Хардуер (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Програмно (x264 ниска употреба на процесора, увеличава размера на записа)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (по подразбиране)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Трасе Twitch VOD (Използва Трасе 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Звукова писта\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Несъвместими разд. способност/кадри в сек\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Тази услуга за излъчване не поддържа зададената изходна разделителна способност и/или кадри в секунда. Те ще бъдат променени на най-близката съвместима стойност:\\n\\n%1\\n\\nИскате ли да продължите?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Разд. способност: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Кадри в сек.: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Несъвместим кодер\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Услугата за поточно предаване \\\"%1\\\" не поддържа кодера \\\"%2\\\". Кодерът ще се промени на \\\"%3\\\".\\n\\nИскате ли да продължите?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Услугата за поточно предаване \\\"%1\\\" не поддържа кодерите \\\"%2\\\" и \\\"%3\\\". Тези кодери ще се променят на \\\"%4\\\" и \\\"%5\\\".\\n\\nИскате ли да продължите?\"\nBasic.Settings.Output.VideoBitrate=\"Битрейт на видеото\"\nBasic.Settings.Output.AudioBitrate=\"Битрейт на звука\"\nBasic.Settings.Output.Reconnect=\"Автоматично повторно свързване\"\nBasic.Settings.Output.RetryDelay=\"Повтаряне на закъснение\"\nBasic.Settings.Output.MaxRetries=\"Максимален брой повторни опити\"\nBasic.Settings.Output.EncoderPreset=\"Пренастройка на енкодера\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (ниско натоварване на процесора, най-ниско качество)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (високо натоварване на процесора, високо качество)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Допълнителни Настройки на Енкодера\"\nBasic.Settings.Output.CustomMuxerSettings=\"Допълнителни Настройки при Смесване\"\nBasic.Settings.Output.NoSpaceFileName=\"Създавай името на записа без Празни Места\"\nBasic.Settings.Output.Adv.Rescale=\"Умащабяване на Изхода\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Изключено\"\nBasic.Settings.Output.Adv.AudioTrack=\"Звукова писта\"\nBasic.Settings.Output.Adv.Streaming=\"Поточно предаване\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Настройки за поточно предаване\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Писта 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Писта 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Писта 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Писта 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Писта 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Писта 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Трасе Twitch VOD\"\nBasic.Settings.Output.Adv.Encoder=\"Настройки на кодера\"\nBasic.Settings.Output.Adv.Recording=\"Запис\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Настройки за запис\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Тип на записа\"\nBasic.Settings.Output.Adv.Recording.Type=\"Тип\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Стандартен\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Изход по Избор (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Използвай енкодера за излъчването)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Формат на името\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Презапиши ако файлът съществува\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Персонализираният изход (FFmpeg) се предоставя без защитни механизми.\\nПомислете за използване на типа запис \\\"Стандартен\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Тип FFmpeg изходен формат\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Запиши към Интернет Адрес\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Запиши на файл\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Често срещани формати за запис\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Всички файлове\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Път към файла или адрес\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Формат на контейнера\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Звук\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Видео\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Формат по подразбиране\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Описание на формата на контейнера\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Аудио / видео кодека се разпознава от файла или URL адреса\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Кодер по подразбиране\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Изключване на кодера\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Кодер за видео\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Настройки на кодера за видео (ако има)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Кодер за звук\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Настройки на кодера за звук (ако има)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Настройки при сливане (ако има)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Интервал между ключови кадри (кадри)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Покажи всички кодеци (дори и ако са несъвместими)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Настройки на FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Автоматично разделяне на файлове\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Разделяне по време\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Разделяне по размер\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Разделяне само ръчно\"\nBasic.Settings.Output.SplitFile.Time=\"Време за разделяне\"\nBasic.Settings.Output.SplitFile.Size=\"Размер за разделяне\"\nScreenshot=\"Заснемане на екрана на изхода\"\nScreenshot.SourceHotkey=\"Заснемане на екрана на избрания източник\"\nScreenshot.StudioProgram=\"Заснемане на екранна снимка (Програма)\"\nScreenshot.Preview=\"Заснемане на екрана (онагледяване)\"\nScreenshot.Scene=\"Заснемане на екранна снимка (Сцена)\"\nScreenshot.Source=\"Заснемане на екранна снимка (Източник)\"\nFilenameFormatting.TT.CCYY=\"Година, четири цифри\"\nFilenameFormatting.TT.YY=\"Година, последните две цифри (00-99)\"\nFilenameFormatting.TT.MM=\"Месецът като десетично число (01-12)\"\nFilenameFormatting.TT.DD=\"Ден от месеца с добавена нула (01-31)\"\nFilenameFormatting.TT.hh=\"Часът във формат 24 часа (00-23)\"\nFilenameFormatting.TT.mm=\"Минута (00-59)\"\nFilenameFormatting.TT.ss=\"Секунда (00-59)\"\nFilenameFormatting.TT.Percent=\"Знак %\"\nFilenameFormatting.TT.a=\"Съкращение на името на дните на седмицата\"\nFilenameFormatting.TT.A=\"Цялото име на дните на седмицата\"\nFilenameFormatting.TT.b=\"Съкращение на името на месеца\"\nFilenameFormatting.TT.B=\"Пълното име на месеца\"\nFilenameFormatting.TT.d=\"Ден от месеца с добавена нула (01-31)\"\nFilenameFormatting.TT.H=\"Часът във формат 24 часа (00-23)\"\nFilenameFormatting.TT.I=\"Часът във формат 12 часа (00-23)\"\nFilenameFormatting.TT.m=\"Месецът като десетично число (01-12)\"\nFilenameFormatting.TT.M=\"Минута (00-59)\"\nFilenameFormatting.TT.p=\"Обозначаване на AM или PM\"\nFilenameFormatting.TT.S=\"Секунда (00-59)\"\nFilenameFormatting.TT.y=\"Година, последните две цифри (00-99)\"\nFilenameFormatting.TT.Y=\"Година\"\nFilenameFormatting.TT.z=\"ISO 8601 изместване от UTC в часовата зона\"\nFilenameFormatting.TT.Z=\"Име на часовата зона или съкращение\"\nFilenameFormatting.TT.FPS=\"Кадри за секунда\"\nFilenameFormatting.TT.VF=\"Видео формат\"\nBasic.Settings.Video=\"Видео\"\nBasic.Settings.Video.Adapter=\"Видео адаптер\"\nBasic.Settings.Video.BaseResolution=\"Основна Резолюция на Платното\"\nBasic.Settings.Video.ScaledResolution=\"Резолюция на записа (умален)\"\nBasic.Settings.Video.DownscaleFilter=\"Филтър за намаляване на мащаба\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Резолюциите съвпадат, не се налага понижаване на мащаба]\"\nBasic.Settings.Video.FPS=\"Кадри в секунда (FPS):\"\nBasic.Settings.Video.FPSCommon=\"Често използвани FPS стойности\"\nBasic.Settings.Video.FPSInteger=\"FPS стойност - цяло число\"\nBasic.Settings.Video.FPSFraction=\"FPS стойност - дробно число\"\nBasic.Settings.Video.Numerator=\"Числител\"\nBasic.Settings.Video.Denominator=\"Знаменател\"\nBasic.Settings.Video.Renderer=\"Рендърирител\"\nBasic.Settings.Video.InvalidResolution=\"Невалидна стойност на разделителна способност. Трябва да бъде [width]x[height] (например: 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"В момента излъчвате. Моля спрете излъчването за да промените видео настройките.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Двулинеен (Най-бърз, но замазан след усъразмеряване)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Двукубичен (Изострено при усъразмеряване, 16 семпли)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Ланцош (Изострено при усъразмеряване, 36 образци)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Площ (Отчетена сума, 4/6/9 образеца)\"\nBasic.Settings.Audio=\"Звук\"\nBasic.Settings.Audio.SampleRate=\"Честота на дискретизацията\"\nBasic.Settings.Audio.Channels=\"Канали\"\nBasic.Settings.Audio.Meters=\"Измерватели\"\nBasic.Settings.Audio.MeterDecayRate=\"Скорост на спадане\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Бързо\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Средно (ТИП I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Бавно (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Тип на връхната точка\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Образец на върха\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Истински връх (По-високо използване на CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ПРЕДУПРЕЖДЕНИЕ: Включен е Surround sound.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Включвате ли обкръжаващ звук?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Наистина ли искате да включите обкръжаващ звук?\"\nBasic.Settings.Audio.Devices=\"Общи звукови устройства\"\nBasic.Settings.Audio.DesktopDevice=\"Аудио - Работен плот\"\nBasic.Settings.Audio.DesktopDevice2=\"Аудио - Работен плот 2\"\nBasic.Settings.Audio.AuxDevice=\"Микрофон/Допълнително аудио\"\nBasic.Settings.Audio.AuxDevice2=\"Микрофон/Допълнително аудио 2\"\nBasic.Settings.Audio.AuxDevice3=\"Микрофон/Допълнително аудио 3\"\nBasic.Settings.Audio.AuxDevice4=\"Микрофон/Допълнително аудио 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Включи Натисни-за-да-заглуши\"\nBasic.Settings.Audio.PushToMuteDelay=\"Натисни-за-да-заглуши забавяне\"\nBasic.Settings.Audio.EnablePushToTalk=\"Включи Натисни-за-говорене\"\nBasic.Settings.Audio.PushToTalkDelay=\"Натисни-за-говорене забавяне\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Устройството не е свързано или не е на разположение]\"\nBasic.Settings.Audio.Disabled=\"Деактивирано\"\nBasic.Settings.Accessibility=\"Достъпност\"\nBasic.Settings.Accessibility.ColorOverrides=\"Употреба на различни цветове\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Цветови образци\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"По подразбиране\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Персонализиран\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Цвят за далтонисти\"\nBasic.Settings.Advanced=\"За напреднали\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Показвай активното output предупреждение при изход\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Приоритет на Програмата\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Висок\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Над Нормата\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Нормален\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Под Нормата\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Свободен\"\nBasic.Settings.Advanced.FormatWarning=\"Предупреждение: Другите цветови формати, с изключение на NV12/P010, са основно предвидени за записване и не се препоръчват при излъчване. При излъчването може да се повиши натоварването на процесора поради смяната на цветовия формат.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Предупреждение: Rec. 2100 трябва да използва формат с по-голяма прецизност.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Формат на цвета\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 бита, 4:2:0, 2 равнини)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 бита, 4:2:0, 3 равнини)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 бита, 4:4:4, 3 равнини)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 бита, 4:2:0, 2 равнини)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 бита, 4:2:0, 3 равнини)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 бита, 4:2:2, 2 равнини)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 бита, 4:4:4, 2 равнини)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 бита)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Цветово пространство\"\nBasic.Settings.Advanced.Video.ColorRange=\"Цветови обхват\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Ограничен\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Пълен\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR ниво на бялото\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Номинално пиково ниво на HDR \"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Устройство за наблюдение\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"По подразбиране\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Изключи намалянето на звука при разговори\"\nBasic.Settings.Advanced.StreamDelay=\"Забавяне на Предаването\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Времетраене\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Запази точката на прекъсване (увеличете забавянето) при повторно свързване\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Приблизително използвана памет: %1 MB\"\nBasic.Settings.Advanced.Network=\"Мрежа\"\nBasic.Settings.Advanced.Network.Disabled=\"Текущият избран протокол за поточно предаване не поддържа променящите се мрежови настройки.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Свържи към IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Семейство IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Включване на мрежова оптимизация\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Включване на TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Прави опит да намали негативното влияние на другите програми, зависими от мрежата, при използване на RTMP изхода, чрез регулиране скоростта на предаването на данни..\\nТова може да повиши риска от изпускане на кадри при нестабилна интернет връзка.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Поведение на фокуса на бързите клавиши\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Никога не изключвай бързите клавиши\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Изключване на бързите клавиши, когато главният прозорец е на фокус\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Изключване на бързите клавиши, когато главният прозорец не е на фокус\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(запиши като .mkv)\"\nBasic.AdvAudio=\"Допълнителни Звукови Характеристики\"\nBasic.AdvAudio.ActiveOnly=\"Само активни източници\"\nBasic.AdvAudio.Name=\"Име\"\nBasic.AdvAudio.Volume=\"Сила на звука\"\nBasic.AdvAudio.VolumeSource=\"Сила на звука за '%1'\"\nBasic.AdvAudio.Mono=\"Моно\"\nBasic.AdvAudio.MonoSource=\"Смесване на моно за '%1'\"\nBasic.AdvAudio.Balance=\"Баланс\"\nBasic.AdvAudio.BalanceSource=\"Баланс за '%1'\"\nBasic.AdvAudio.SyncOffset=\"Синхронизиращо разклонение\"\nBasic.AdvAudio.SyncOffsetSource=\"Синхронизиране на изместването за '%1'\"\nBasic.AdvAudio.Monitoring=\"Прослушване на звука\"\nBasic.AdvAudio.Monitoring.None=\"Наблюдението е изключено\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Само наблюдение (заглушаване на изхода)\"\nBasic.AdvAudio.Monitoring.Both=\"Монитор и изход\"\nBasic.AdvAudio.MonitoringSource=\"Завуково прослушване за '%1'\"\nBasic.AdvAudio.AudioTracks=\"Писти\"\nBasic.Settings.Hotkeys=\"Горещи клавиши\"\nBasic.Settings.Hotkeys.Pair=\"Клавишните комбинации, ползвани от `%1`, действат като превключватели\"\nBasic.Settings.Hotkeys.Filter=\"Филтър\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Подреждане по бързи клавиши\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Този бърз клавиш се използва за едно или повече действия, натиснете, за да видите конфликтните\"\nBasic.Settings.Hotkeys.PleaseWait=\"Зареждане на горещите клавиши, моля изчакайте...\"\nBasic.Hotkeys.SelectScene=\"Премини към сцена\"\nBasic.SystemTray.Show=\"Показване\"\nBasic.SystemTray.Hide=\"Скриване\"\nBasic.SystemTray.Message.Reconnecting=\"Връзка прекъсната, повторно свързване...\"\nHotkeys.Insert=\"Вмъкване\"\nHotkeys.Delete=\"Изтриване\"\nHotkeys.Home=\"Начало\"\nHotkeys.End=\"Край\"\nHotkeys.PageUp=\"Страница нагоре\"\nHotkeys.PageDown=\"Страница надолу\"\nHotkeys.Print=\"Print Screen\"\nHotkeys.Pause=\"Pause Break\"\nHotkeys.Left=\"Стрелка наляво\"\nHotkeys.Right=\"Стрелка надясно\"\nHotkeys.Up=\"Стрелка нагоре\"\nHotkeys.Down=\"Стрелка надолу\"\nHotkeys.Super=\"Супер\"\nHotkeys.Menu=\"Меню\"\nHotkeys.Space=\"Интервал\"\nHotkeys.NumpadNum=\"Нумпад %1\"\nHotkeys.NumpadMultiply=\"Нумпад Умножи\"\nHotkeys.NumpadDivide=\"Нумпад Раздели\"\nHotkeys.NumpadAdd=\"Цифрова клавиатура +\"\nHotkeys.NumpadSubtract=\"Цифрова клавиатура -\"\nHotkeys.NumpadDecimal=\"Цифрова клавиатура .\"\nHotkeys.AppleKeypadNum=\"%1 (Клавиатура)\"\nHotkeys.AppleKeypadMultiply=\"* (Клавиатура)\"\nHotkeys.AppleKeypadDivide=\"/ (Клавиатура)\"\nHotkeys.AppleKeypadAdd=\"+ (Клавиатура)\"\nHotkeys.AppleKeypadSubtract=\"- (Клавиатура)\"\nHotkeys.AppleKeypadDecimal=\". (Клавиатура)\"\nHotkeys.AppleKeypadEqual=\"= (Клавиатура)\"\nHotkeys.MouseButton=\"Мишка %1\"\nMute=\"Заглуши\"\nUnmute=\"Включи звука\"\nPush-to-mute=\"Натисни-за-да-заглуши\"\nPush-to-talk=\"Натисни-за-говорене\"\nSceneItemShow=\"Покажи '%1'\"\nSceneItemHide=\"Скрии '%1'\"\nOutputWarnings.NoTracksSelected=\"Трябва да изберете поне една писта за звук\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Грешка, настройвайки изхода\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Всички изходи трябва да имат избрана поне една звукова писта.\"\nOutputWarnings.MP4Recording=\"Предупреждение: Записи запаметени в MP4/MOV не могат да се възстановят, ако записът във файла не е приключил (в случай на спиране на програмата или при спиране на тока и т.н.). Ако искате да записвате на множество звукови писти ползвайте формат MKV и ремултиплексирайте записа в MP4/MOV след като приключи (от Файл->Ремултиплексирани записи)\"\nOutputWarnings.CannotPause=\"Предупреждение: Записите не могат да бъдат преустановявани, ако енкодера за запис е зададен на \\\"Използвай енкодера за излъчване\\\"\"\nOutputWarnings.CodecIncompatible=\"Избраният видео или звуков кодер е нулиран заради несъвместимост. Изберете съвместим кодер от списъка.\"\nCodecCompat.Incompatible=\"(Несъвместимо с %1)\"\nCodecCompat.CodecPlaceholder=\"Избор на кодер...\"\nCodecCompat.ContainerPlaceholder=\"Избор на формат...\"\nCodecCompat.CodecMissingOnExit.Title=\"Няма избран кодер\"\nCodecCompat.CodecMissingOnExit.Text=\"Поне един звуков или видео кодер не е зададен. Уверете се, че сте избрали кодер и за запис и за поточно предаване.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Няма избран формат\"\nCodecCompat.ContainerMissingOnExit.Text=\"Няма избран формат за запис. Изберете формат за запис, който е съвместим с избрания кодер за поточно предаване.\"\nFinalScene.Title=\"Изтриване на сцената\"\nFinalScene.Text=\"Трябва да има поне една сцена във наличност.\"\nNoSources.Title=\"Няма налични източници\"\nNoSources.Text=\"Изглежда все още не сте добавили никакви видео източници и ще се вижда само един празен екран. Сигурни ли сте, че желаете това?\"\nNoSources.Text.AddSource=\"Можете да добавите източници като щракнете върху + иконката под полето за източници в главния прозорец по всяко време.\"\nNoSources.Label=\"Не сте добавили никакви източници.\\nНатиснете бутона + отдолу\\n или натиснете с дясно щракване, за да добавите източник.\"\nChangeBG=\"Задаване на цвят\"\nCustomColor=\"Различен цвят\"\nBrowserSource.EnableHardwareAcceleration=\"Позволи ускорение, чрез браузър\"\nAbout=\"За програмата\"\nAbout.Info=\"OBS Studio е програма за видеозапис и видеопредаване на живо с отворен код, разпространяваща се безплатно.\"\nAbout.Donate=\"Направете принос\"\nAbout.GetInvolved=\"Включете се\"\nAbout.Authors=\"Автори\"\nAbout.License=\"Лиценз\"\nAbout.Error=\"Грешка! Файлът не може да се прочете.\\n\\nОтидете при: %1\"\nAbout.Contribute=\"Подкрепете проекта OBS\"\nAddUrl.Title=\"Добавяне на източник чрез URL\"\nAddUrl.Text=\"Добавихте чрез влачене URL адрес в OBS. Това автоматично ще добави връзката във вид на източник. Искате ли да продължите?\"\nAddUrl.Text.Url=\"URL адрес: %1\"\nResizeOutputSizeOfSource=\"Преоразмери изхода(размер на източника)\"\nResizeOutputSizeOfSource.Continue=\"Искате ли да продължите?\"\nPreviewTransition=\"Предварителен преглед на прехода\"\nImporter=\"Импортер на колекция със сцени\"\nImporter.SelectCollection=\"Избор на колекция със сцени\"\nImporter.Collection=\"Колекция със сцени\"\nImporter.HelpText=\"Добавете файлове в този прозорец за вграждане на колекции от OBS или други поддържани програми.\"\nImporter.Path=\"Местонахождение\"\nImporter.Program=\"Засечено приложение\"\nImporter.AutomaticCollectionPrompt=\"Автоматично търсене на колекции със сцени\"\nImporter.AutomaticCollectionText=\"OBS може автоматично да намери вградимите колекции със сцени от поддържаните странични програми. Искате ли OBS автоматично да намери колекциите за вас?\\n\\nМоже да промените тази настройка по-късно в Настройки > Основни > Вграждачи.\"\nRestart=\"Рестартиране\"\nNeedsRestart=\"OBS Studio трябва да бъде рестартирано. Искате ли да рестартирате сега?\"\nLoadProfileNeedsRestart=\"Профилът съдържа настройки, които изискват рестартиране на OBS:\\n%1\\n\\nИскате ли да рестартирате OBS, за да влязат направените промени в сила?\"\nContextBar.NoSelectedSource=\"Не е избран източник\"\nContextBar.MediaControls.PlayMedia=\"Възпроизвеждане на медия\"\nContextBar.MediaControls.PauseMedia=\"Пауза на носителя\"\nContextBar.MediaControls.RestartMedia=\"Рестартирайте Media\"\nContextBar.MediaControls.PlaylistNext=\"Следва в плейлиста\"\nContextBar.MediaControls.PlaylistPrevious=\"Предишно в плейлиста\"\nContextBar.MediaControls.BlindSeek=\"Приспособление за търсене на медия\"\nYouTube.Auth.Ok=\"Упълномощаването завърши успешно.\\nМоже да затворите тази страница.\"\nYouTube.Auth.NoCode=\"Упълномощаването не завърши успешно.\"\nYouTube.Auth.NoChannels=\"Не бяха намерени достъпни канали на избрания акаунт\"\nYouTube.Auth.WaitingAuth.Title=\"Упълномощаване на потребител в YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Моля, завършете процеса във вашия браузър.<br>Ако браузърът все пак не се отвори, използвайте следната връзка:<br>%1\"\nYouTube.AuthError.Text=\"Неуспешно зареждане на информацията за канала %1.\"\nYouTube.Actions.WindowTitle=\"Настройка за излъчване в YouTube - Канал: %1\"\nYouTube.Actions.CreateNewEvent=\"Създаване на ново излъчване\"\nYouTube.Actions.ChooseEvent=\"Избиране на съществуващо излъчване\"\nYouTube.Actions.Title=\"Заглавие*\"\nYouTube.Actions.MyBroadcast=\"Моето излъчване\"\nYouTube.Actions.Description=\"Описание\"\nYouTube.Actions.Privacy=\"Видимост*\"\nYouTube.Actions.Privacy.Private=\"Частно\"\nYouTube.Actions.Privacy.Public=\"Публично\"\nYouTube.Actions.Privacy.Unlisted=\"Само с връзка\"\nYouTube.Actions.Category=\"Категория\"\nYouTube.Actions.Thumbnail=\"Заглавно изображение\"\nYouTube.Actions.Thumbnail.SelectFile=\"Избиране на файл...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Не е избран файл\"\nYouTube.Actions.Thumbnail.ClearFile=\"Премахване\"\nYouTube.Actions.MadeForKids=\"Това видео включва ли съдържание за деца?*\"\nYouTube.Actions.MadeForKids.Yes=\"Да, включва съдържание за деца\"\nYouTube.Actions.MadeForKids.No=\"Не, не е направено за деца\"\nYouTube.Actions.AdditionalSettings=\"Допълнителни настройки\"\nYouTube.Actions.Latency=\"Задържане\"\nYouTube.Actions.Latency.Normal=\"Нормално\"\nYouTube.Actions.Latency.Low=\"Ниско\"\nYouTube.Actions.Latency.UltraLow=\"Най-ниско\"\nYouTube.Actions.EnableAutoStart=\"Включване на автоначало\"\nYouTube.Actions.EnableAutoStop=\"Включване на автокрай\"\nYouTube.Actions.AutoStartStop.TT=\"Показва дали отложеното излъчване трябва да започне автоматично\"\nYouTube.Actions.EnableDVR=\"Включване на DVR\"\nYouTube.Actions.360Video=\"360-во видео\"\nYouTube.Actions.ScheduleForLater=\"Отложи за по-късно\"\nYouTube.Actions.RememberSettings=\"Запомни тези настройки\"\nYouTube.Actions.Create_Ready=\"Създай излъчването\"\nYouTube.Actions.Create_GoLive=\"Създай излъчването и започни предаването\"\nYouTube.Actions.Choose_Ready=\"Избиране на излъчване\"\nYouTube.Actions.Choose_GoLive=\"Избиране на излъчване и започване на предаването\"\nYouTube.Actions.Create_Schedule=\"Отложено излъчване\"\nYouTube.Actions.Create_Schedule_Ready=\"Отлагане и избиране на излъчване\"\nYouTube.Actions.Dashboard=\"Отваряне на YouTube Studio\"\nYouTube.Actions.Error.Title=\"Грешка при създаването на живото излъчване\"\nYouTube.Actions.Error.Text=\"Неуспешно свързване с YouTube, грешка '%1'.<br/>Подробно описание на грешката може да откриете на <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Неуспешно свързване. Моля, проверете вашата интернет връзка или достъпа до сървъра на YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Неуспешно създаване на излъчване, грешка '%1'.<br/>Подробно описание на грешката може да откриете на <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Неуспешно създаване на излъчване. Моля, премахнете и свържете вашия акаунт наново.\"\nYouTube.Actions.Error.YouTubeApi=\"Грешка - YouTube API. Моля, прегледайте отчетния файл за повече информация.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Избраното излъчване не бе открито.\"\nYouTube.Actions.Error.FileMissing=\"Избраният файл не съществува.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Отварянето на избрания файл се провали.\"\nYouTube.Actions.Error.FileTooLarge=\"Избраният файл е твърде голям (Ограничение: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Прехвърлянето на излъчването се провали: %1<br/><br/>Ако тази грешка продължи <a href='https://studio.youtube.com/video/%2/livestreaming'>отворете излъчването в YouTube Studio</a> и опитайте да го направите ръчно.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Излъчването се прехвърля към тестовата сцена, това може да отнеме известно време. Моля, опитайте отново след 10-30 секунди.\"\nYouTube.Actions.EventsLoading=\"Зареждане на списък със събития...\"\nYouTube.Actions.EventCreated.Title=\"Създадено събитие\"\nYouTube.Actions.EventCreated.Text=\"Събитието беше създадено успешно\"\nYouTube.Actions.Stream=\"Поток\"\nYouTube.Actions.Stream.ScheduledFor=\"Планиран за %1\"\nYouTube.Actions.Stream.Resume=\"Продължаване на прекъснатото излъчване\"\nYouTube.Actions.Stream.YTStudio=\"Автоматично създадено от YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Създаване на ново излъчване, моля изчакайте...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Изисква се ръчно пускане\"\nYouTube.Actions.AutoStartStreamingWarning=\"Автопускането е изключено за това събитие, натиснете върху 'На живо', за да започнете излъчването.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Излъчването ще спре.<br>Няма да може да го продължите или презаредите след това.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Прякото излъчване не е включено при избрания YouTube канал.<br/><br/>Вижте <a href='https://www.youtube.com/features'>youtube.com/features</a> за повече информация.\"\nYouTube.Errors.livePermissionBlocked=\"Прякото излъчване е недостъпно при избрания YouTube канал.<br/>Моля, имайте в предвид, че може да отнеме до 24 часа, преди прякото излъчване да стане достъпно, след като сте го включили в настройките на вашия канал.<br/><br/>Вижте <a href='https://www.youtube.com/features'>youtube.com/features</a> за повече информация.\"\nYouTube.Errors.errorExecutingTransition=\"Прехвърлянето се провали поради грешка при бекенда. Моля, опитайте отново след няколко секунди.\"\nYouTube.Errors.errorStreamInactive=\"YouTube не получава никакви данни от вашето излъчване. Моля, проверете вашите настройки и опитайте отново.\"\nYouTube.Errors.invalidTransition=\"Направеният опит за прехвърляне е неправилен. Това може да се случва вследствие на незавършено от източника предишно прехвърляне. Моля, изчакайте няколко секунди и опитайте отново.\"\nConfigDownload.WarningMessageTitle=\"Предупреждение\"\n"
  },
  {
    "path": "frontend/data/locale/bn-BD.ini",
    "content": "Language=\"বাংলা\"\nOK=\"ঠিক আছে\"\nApply=\"প্রয়োগ করুন\"\nCancel=\"বাতিল\"\nClose=\"বন্ধ করুন\"\nSave=\"সংরক্ষণ করুন\"\nDiscard=\"অগ্রাহ্য\"\nDisable=\"নিষ্ক্রিয়\"\nYes=\"হ্যাঁ\"\nNo=\"না\"\nAdd=\"যোগ\"\nRemove=\"বাতিল\"\nRename=\"পুনঃনামকরণ\"\nInteract=\"যোগাযোগ\"\nFilters=\"ফিল্টার\"\nProperties=\"প্যারামিটারসমূহ\"\nMoveUp=\"উপরে উঠান\"\nMoveDown=\"নিচে নামান\"\nSettings=\"সেটিংস\"\nDisplay=\"ডিসপ্লে\"\nName=\"নাম\"\nExit=\"প্রস্থান করুন\"\nMixer=\"অডিও মিক্সার\"\nBrowse=\"ব্রাউজ\"\nMono=\"মোনো\"\nStereo=\"স্টিরিও\"\nDroppedFrames=\"ছেড়ে দেওয়া ফ্রেমসমূহ %1 (%2%)\"\nClear=\"পরিষ্কার\"\nRevert=\"প্রত্যাবর্তন\"\nShow=\"প্রদর্শন করা হবে\"\nHide=\"সরিয়ে রাখ\"\nUnhideAll=\"সব উন্মোচন করুন\"\nUntitled=\"শিরোনামহীন\"\nNew=\"নতুন\"\nDuplicate=\"প্রতিলিপি\"\nEnable=\"সচল\"\nDisableOSXVSync=\"macOS ভি-সিঙ্ক নিষ্ক্রিয় করুন\"\nResetOSXVSyncOnExit=\"প্রস্থান করার সময় macOS ভি-সিঙ্ক রিসেট করুন\"\nHighResourceUsage=\"অতিরিক্ত এনকোডিং! ভিডিও সেটিংস কমান বা দ্রুত এনকোডিং প্রিসেট ব্যবহারের বিবেচনা করুন।\"\nTransition=\"স্থানান্তর\"\nQuickTransitions=\"দ্রুত অবস্থান্তর\"\nFadeToBlack=\"বিবর্ণ থেকে কালো\"\nLeft=\"বামদিকে\"\nRight=\"ডানদিকে\"\nTop=\"শীর্ষ\"\nBottom=\"নিচে\"\nReset=\"পুনরায় ধার্য করুন\"\nHours=\"ঘন্টা\"\nMinutes=\"মিনিট\"\nSeconds=\"সেকেন্ড\"\nDeprecated=\"অবচিত\"\nReplayBuffer=\"রিপ্লে বাফার\"\nImport=\"ডাটা ইম্পোর্ট\"\nExport=\"ডাটা এক্সপোর্ট\"\nCopy=\"অনুলিপি\"\nPaste=\"প্রতিলেপন\"\nPasteReference=\"প্রতিলেপন (রেফারেন্স)\"\nPasteDuplicate=\"প্রতিলেপন (সদৃশ)\"\nRemuxRecordings=\"রিম্যাক্স রেকর্ডিংস\"\nNext=\"পরবর্তী\"\nBack=\"পিছনে যান\"\nDefaults=\"পূর্ব-নির্ধারিত\"\nRestoreDefaults=\"পূর্ব-নির্ধারিত\"\nHideMixer=\"মিক্সারে লুকান\"\nTransitionOverride=\"ট্রানজিশন ওভাররাইড\"\nShowTransition=\"ট্রানজিশন দেখান\"\nHideTransition=\"ট্রানজিশন লুকান\"\nNone=\"কোনটি না\"\nStudioMode.Preview=\"প্রাকবীক্ষন\"\nStudioMode.Program=\"প্রোগ্রাম\"\nStudioMode.PreviewSceneName=\"প্রাকদর্শন: %1\"\nStudioMode.ProgramSceneName=\"প্রোগ্রাম: %1\"\nShowInMultiview=\"মাল্টিভিউতে দেখান\"\nVerticalLayout=\"লম্ব লেআউট\"\nGroup=\"দল\"\nDoNotShowAgain=\"পুনরায় দেখাবেন না\"\nDefault=\"(পূর্ব-নির্ধারিত)\"\nCalculating=\"গণনা করা হচ্ছে...\"\nFullscreen=\"পুরো স্ক্রিন জুড়ে দেখুন\"\nWindowed=\"উইন্ডোড\"\nRefreshBrowser=\"পুন:সতেজ\"\nAspectRatio=\"<b>%1:%2</b> অনুপাত\"\nLockVolume=\"আওয়াজের মাত্রা লক করুন\"\nLogViewer=\"ইতিহাস দেখান\"\nShowOnStartup=\"শুরুতে দেখান\"\nOpenFile=\"ফাইল খুলুন\"\nAddSource=\"উৎস যোগ করো\"\nRemoveScene=\"নির্বাচিত পর্দা অপসারণ করো\"\nRemoveSource=\"নির্বাচিত উৎস অপসারণ করো\"\nMoveSceneUp=\"পর্দা উপরে উঠাও\"\nMoveSceneDown=\"পর্দা নিচে নামাও\"\nMoveSourceUp=\"উৎস উপরে উঠাও\"\nMoveSourceDown=\"উৎস নিচে নামাও\"\nSourceProperties=\"উৎস বৈশিষ্ট্য খুলো\"\nSourceFilters=\"উৎস ছাঁকনি খুলো\"\nMixerToolbarMenu=\"অডিও মিশ্রণ পছন্দসমূহ\"\nSceneFilters=\"পর্দা ছাঁকনি খুলো\"\nList=\"তালিকা\"\nGrid=\"ছক\"\nAutomatic=\"স্বয়ংক্রিয়\"\nPluginsFailedToLoad.Title=\"সংযোজক লোড ত্রুটি\"\nAlreadyRunning.Title=\"OBS ইতিমধ্যে চলছে\"\nAlreadyRunning.Text=\"ইতিমধ্যে OBS চলছে! আপনি এটি করতে না চাইলে দয়া করে নতুন ইন্সটেন্স চালানোর চেষ্টা করার আগে OBS এর বিদ্যমান কোনও ইন্সটেন্স বন্ধ করে দিন। যদি আপনার OBS সিস্টেম ট্রেতে থাকার জন্য সেট করে থাকেন তবে দয়া করে এটি এখনও চলছে কিনা তা যাচাই করে দেখুন।\"\nAlreadyRunning.LaunchAnyway=\"যাইহোক চালু করুন\"\nChromeOS.Title=\"অসমর্থিত প্ল্যাটফর্ম\"\nDockCloseWarning.Title=\"ডকযোগ্য উইন্ডো বন্ধ হচ্ছে\"\nExtraBrowsers=\"কৃত্রিম ব্রাউজার ডকস\"\nExtraBrowsers.Info=\"একটি নাম এবং ইউআরএল দিয়ে ডক্স যুক্ত করুন, তারপরে ডকগুলি খোলার জন্য প্রয়োগ বা বন্ধ ক্লিক করুন। আপনি যে কোনও সময় ডক্স যুক্ত করতে বা সরাতে পারেন।\"\nExtraBrowsers.DockName=\"ডকের নাম\"\nAuth.Authing.Title=\"প্রমাণ করা হচ্ছে...\"\nAuth.Authing.Text=\"%1 দিয়ে প্রমাণীকরণ করা হচ্ছে, দয়া করে অপেক্ষা করুন...\"\nAuth.AuthFailure.Title=\"প্রমাণীকরণ ব্যর্থতা\"\nAuth.AuthFailure.Text=\"%1:\\n\\n%2: %3 এর সাথে প্রমাণীকরণে ব্যর্থ\"\nAuth.InvalidScope.Title=\"প্রমাণীকরণ প্রয়োজন\"\nAuth.InvalidScope.Text=\"%1 এর জন্য প্রমাণীকরণের প্রয়োজনীয়তা পরিবর্তন হয়েছে। কিছু বৈশিষ্ট্য উপলব্ধ নাও হতে পারে।\"\nAuth.LoadingChannel.Title=\"চ্যানেলের তথ্য লোড হচ্ছে...\"\nAuth.LoadingChannel.Text=\"%1 চ্যানেলের তথ্য লোড হচ্ছে, অনুগ্রহপূর্বক অপেক্ষা করুন...\"\nAuth.LoadingChannel.Error=\"চ্যানেলের তথ্য পাওয়া যাচ্ছে না।\"\nAuth.ChannelFailure.Title=\"চ্যানেল লোড করতে ব্যর্থ\"\nAuth.ChannelFailure.Text=\"%1\\n\\n%2: %3 এর চ্যানেল তথ্য লোড করতে ব্যর্থ\"\nAuth.Chat=\"চ্যাট\"\nAuth.StreamInfo=\"ষ্টীম সম্পর্কিত তথ্য\"\nTwitchAuth.Stats=\"টুইচ পরিসংখ্যান\"\nTwitchAuth.Feed=\"টুইচ অ্যাক্টিভিটি ফিড\"\nTwitchAuth.TwoFactorFail.Title=\"স্ট্রিম কীটি জিজ্ঞাসা করা যায়নি\"\nTwitchAuth.TwoFactorFail.Text=\"OBS আপনার টুইচ অ্যাকাউন্টে সংযোগ করতে অক্ষম। আপনার <a href='https://www.twitch.tv/settings/security'>টুইচ সুরক্ষা সেটিংসের</a> মধ্যে দ্বি-গুণক প্রমাণীকরণ সেট আপ হয়েছে তা নিশ্চিত করুন কারণ স্ট্রিম করতে প্রয়োজন।\"\nRestreamAuth.Channels=\"Restream চ্যানেলসমূহ\"\nCopy.Filters=\"ফিল্টারগুলির নকল করুন\"\nPaste.Filters=\"ফিল্টারগুলি আটকান\"\nBrowserPanelInit.Title=\"ব্রাউজার শুরু হচ্ছে...\"\nBrowserPanelInit.Text=\"ব্রাউজার শুরু করা হচ্ছে, দয়া করে অপেক্ষা করুন...\"\nBandwidthTest.Region=\"অঞ্চল\"\nBandwidthTest.Region.US=\"মার্কিন যুক্তরাষ্ট্র\"\nBandwidthTest.Region.EU=\"ইউরোপ\"\nBandwidthTest.Region.Asia=\"এশিয়া\"\nBandwidthTest.Region.Other=\"অন্যান্য\"\nBasic.AutoConfig=\"স্বতঃ-কনফিগারেশন জাদুকর\"\nBasic.AutoConfig.ApplySettings=\"সেটিংস প্রয়োগ করুন\"\nBasic.AutoConfig.StartPage=\"ব্যাবহারিক তথ্য\"\nBasic.AutoConfig.StartPage.SubTitle=\"আপনি কিসের জন্য প্রোগ্রামটি ব্যবহার করতে চান তা উল্লেখ করুন\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"স্ট্রিমিংয়ের জন্য অনুকূলিত করুন, রেকর্ডিং আনুষঙ্গিক\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"কেবল রেকর্ডিংয়ের জন্য অনুকূলিত করুন, আমি স্ট্রিমিং করব না\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"আমি কেবল ভার্চুয়াল ক্যামেরা ব্যবহার করব\"\nBasic.AutoConfig.VideoPage=\"ভিডিও সেটিংস\"\nBasic.AutoConfig.VideoPage.SubTitle=\"আপনি যে ভিডিও সেটিংস ব্যবহার করতে চান তা উল্লেখ করুন\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"বর্তমানটি ব্যবহার করুন (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"ডিসপ্লে %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"বর্তমানটি ব্যবহার করুন (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"হয় ৬০ বা ৩০, তবে সম্ভব হলে ৬০\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"হয় ৬০ বা ৩০, তবে উচ্চ রেজোলিউশন বেশি পছন্দ করব\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"দ্রষ্টব্য: ক্যানভাস (ভিত্তি) রেজোলিউশন স্বভাবতই আপনি যেই রেজোলিউশনে ষ্টীম বা রেকর্ড করবেন ঠিক সেইরকম নয়। আপনার প্রকৃত ষ্টীম / রেকর্ডিং রেজোলিউশনটি ক্যানভাস রেজোলিউশন থেকে রিসোর্সের ব্যবহার বা বিটরেট প্রয়োজনীয়তা হ্রাস করতে সংক্ষিপ্ত আকারে সংরক্ষিত হতে পারে।\"\nBasic.AutoConfig.StreamPage=\"ষ্টীম সম্পর্কিত তথ্য\"\nBasic.AutoConfig.StreamPage.SubTitle=\"অনুগ্রহপূর্বক আপনার স্ট্রিম তথ্য লিখুন\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"অ্যাকাউন্টটি সংযুক্ত করুন (প্রস্তাবিত)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"অ্যাকাউন্টটি সংযোগ বিচ্ছিন্ন করুন\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"অ্যাকাউন্টটি সংযোগ বিচ্ছিন্ন করুন?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"এই পরিবর্তনটি তাৎক্ষনিকভাবে প্রযোজ্য হবে। আপনি কি নিশ্চিত যে আপনি নিজের অ্যাকাউন্টটির সংযোগ বিচ্ছিন্ন করতে চান?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"স্ট্রিম কী পান\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"আরো তথ্য\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"ষ্টীম কী ব্যাবহার করুন\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"স্ট্রিম কী ব্যবহার করুন (উন্নত)\"\nBasic.AutoConfig.StreamPage.Service=\"সেবা\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"সব দেখান...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"কাস্টম...\"\nBasic.AutoConfig.StreamPage.Server=\"সার্ভার\"\nBasic.AutoConfig.StreamPage.StreamKey=\"ষ্টীম কী\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"এনকোডার কী\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"সংযুক্ত অ্যাকাউন্টে\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"ব্যান্ডউইথ পরীক্ষার মাধ্যমে বিটরেটের নির্ধারণ করুন (কয়েক মিনিট সময় লাগতে পারে)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"হার্ডওয়্যার এনকোডিং বেশি পছন্দ করব\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"হার্ডওয়্যার এনকোডিং বেশিরভাগ সিপিইউ ব্যবহার বাদ দেয় তবে একই স্তরের মানের অর্জনের জন্য আরও বিটরেটের প্রয়োজন হতে পারে।\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"স্ট্রিম সতর্কতা\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"ব্যান্ডউইথ পরীক্ষাটি আপনার চ্যানেলে অডিও ছাড়াই এলোমেলোভাবে ভিডিও ডেটা স্ট্রিম করতে চলেছে। আপনি যদি সক্ষম হন, তবে পরীক্ষা শেষ হওয়ার আগ পর্যন্ত অস্থায়ীভাবে স্ট্রিমগুলির ভিডিও সংরক্ষণ এবং স্ট্রিমটি ব্যক্তিগততে সেট করার পরামর্শ রইল। চালিয়ে যাবেন?\"\nBasic.AutoConfig.TestPage=\"চূড়ান্ত ফলাফল\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"প্রোগ্রামটি এখন সবচেয়ে আদর্শ সেটিংস নির্ধারণ করার জন্য কিছু পরীক্ষা চালাচ্ছে\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"পরীক্ষা শেষ\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"ব্যান্ডউইথ পরীক্ষা চালানো হচ্ছে, এটি কয়েক মিনিট সময় নিতে পারে...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"%1 এর সাথে সংযুক্ত হচ্ছে...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"কোনও সার্ভারের সাথে সংযোগ করতে ব্যর্থ, আপনার ইন্টারনেট সংযোগ পরীক্ষা করে দেখুন এবং আবার চেষ্টা করুন।\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"এর জন্য ব্যান্ডউইথ পরীক্ষা চলছে: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"স্ট্রিম এনকোডারের পরীক্ষা চলছে, এটি এক মিনিট সময় নিতে পারে...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"রেকর্ডিং এনকোডারের পরীক্ষা চলছে, এটি এক মিনিট সময় নিতে পারে...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"এনকোডার শুরু করতে ব্যর্থ\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"%1x%2 %3 এফপিএস পরীক্ষা করা হচ্ছে...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"স্ট্রিমিং এনকোডার\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"রেকর্ডিং এনকোডার\"\nBasic.AutoConfig.TestPage.Result.Header=\"প্রোগ্রামটি নির্ধারণ করেছে যে এই অনুমানিত সেটিংসটি আপনার জন্য সবচেয়ে আদর্শ:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"এই সেটিংসটি ব্যবহার করতে, সেটিংস প্রয়োগ করুন ক্লিক করুন। উইজার্ডটি পুনরায় কনফিগার করতে এবং আবার চেষ্টা করতে, পিছনে যান এ ক্লিক করুন।\"\nBasic.AutoConfig.Info=\"অটো-কনফিগারেশন উইজার্ডটি আপনার কম্পিউটারের স্পেক এবং ইন্টারনেটের গতির উপর ভিত্তি করে সেরা সেটিংস নির্ধারণ করবে।\"\nBasic.AutoConfig.RunAnytime=\"এটি সরঞ্জাম মেনুতে গিয়ে যে কোনও সময় চালানো যেতে পারে।\"\nBasic.Stats=\"পরিসংখ্যান\"\nBasic.Stats.CPUUsage=\"সিপিইউ এর ব্যবহার\"\nBasic.Stats.HDDSpaceAvailable=\"ডিস্ক স্পেস উপলব্ধ\"\nBasic.Stats.MemoryUsage=\"মেমরি ব্যবহার\"\nBasic.Stats.AverageTimeToRender=\"ফ্রেম রেন্ডার করার গড় সময়\"\nBasic.Stats.SkippedFrames=\"এনকোডিং ল্যাগের কারণে এড়ানো ফ্রেমগুলি\"\nBasic.Stats.MissedFrames=\"রেন্ডারিং ল্যাগের কারণে হারানো ফ্রেমগুলি\"\nBasic.Stats.Output.Stream=\"স্ট্রিম\"\nBasic.Stats.Output.Recording=\"রেকর্ডিং\"\nBasic.Stats.Status=\"অবস্থা\"\nBasic.Stats.Status.Recording=\"রেকর্ড হচ্ছে\"\nBasic.Stats.Status.Live=\"সরাসরি\"\nBasic.Stats.Status.Reconnecting=\"পুনঃসংযোগ করা হচ্ছে\"\nBasic.Stats.Status.Inactive=\"নিষ্ক্রিয়\"\nBasic.Stats.Status.Active=\"সক্রিয়\"\nBasic.Stats.DroppedFrames=\"ছেড়ে দেওয়া ফ্রেমগুলি (নেটওয়ার্ক)\"\nBasic.Stats.MegabytesSent=\"মোট ডেটা আউটপুট\"\nBasic.Stats.Bitrate=\"বিটরেট\"\nBasic.Stats.DiskFullIn=\"ডিস্ক পূর্ণ (প্রায়)\"\nBasic.Stats.ResetStats=\"বিবরন রিসেট করুন\"\nResetUIWarning.Title=\"আপনি কি নিশ্চিত যে ইউআইটি রিসেট করতে চান?\"\nUpdater.Title=\"নতুন হালনাগাদ উপলব্ধ\"\nUpdater.Text=\"একটি নতুন হালনাগাদ উপলব্ধ আছে:\"\nUpdater.UpdateNow=\"এখনই হালনাগাদ করুন\"\nUpdater.RemindMeLater=\"আমাকে পরে স্মরন করিয়ে দিন\"\nUpdater.Skip=\"সংস্করণ এড়িয়ে যান\"\nUpdater.NoUpdatesAvailable.Title=\"কোন হালনাগাদ উপলব্ধ নেই\"\nUpdater.NoUpdatesAvailable.Text=\"বর্তমানে কোন হালনাগাদ উপলব্ধ নেই\"\nUpdater.FailedToLaunch=\"হালনাগাদকারী আরম্ভ করতে ব্যর্থ\"\nQuickTransitions.DuplicateScene=\"অনুরূপ দৃশ্য\"\nQuickTransitions.EditProperties=\"উৎসের অনুলিপি\"\nQuickTransitions.HotkeyName=\"দ্রুত পরিবর্তন: %1\"\nBasic.AddTransition=\"কনফিগার রূপান্তরণ যোগ করুন\"\nBasic.RemoveTransition=\"কনফিগার অবস্থান্তর অপসারণ করুন\"\nBasic.TransitionProperties=\"কোনও পরিবর্তন ঘটলে স্থানীয় বৈশিষ্ট্য\"\nBasic.SceneTransitions=\"ঘটনাস্থলে অবস্থান্তর\"\nBasic.TransitionDuration=\"সময়কাল\"\nBasic.TogglePreviewProgramMode=\"স্টুডিও মোড\"\nUndo.Undo=\"পূর্বাবস্থায় ফেরান\"\nUndo.Redo=\"আবার করুন\"\nUndo.Add=\"'%1' যোগ করুন\"\nUndo.Delete=\"'%1' মুছুন\"\nUndo.Rename=\"নাম বদলান '%1'\"\nUndo.SceneCollection.Switch=\"'%1' তে সুইচ করুন\"\nUndo.Item.Undo=\"আনডু %1\"\nUndo.Item.Redo=\"রিডু %1\"\nUndo.Sources.Multi=\"%1 টি সোর্স ডিলিট করুন\"\nUndo.Filters=\"'%1' এ পরিবর্তনগুলি ফিল্টার করুন\"\nUndo.Filters.Paste.Single=\"ফিল্টার '%1' থেকে '%2' এ আটকান\"\nUndo.Filters.Paste.Multiple=\"'%1' থেকে '%2' ফিল্টার অনুলিপিত করুন\"\nUndo.Transform=\"'%1' তে সোর্স(গুলি) রূপান্তরিত করুন\"\nUndo.Transform.Paste=\"রূপান্তরটি '%1' তে পেস্ট করুন\"\nUndo.Transform.Rotate=\"'%1' এ আবর্তন\"\nUndo.Transform.Reset=\"'%1' -এ রূপান্তর রিসেট করুন\"\nUndo.Transform.HFlip=\"'%1' এ অনুভূমিক ফ্লিপ\"\nUndo.Transform.VFlip=\"'%1' এ উল্লম্ব ফ্লিপ\"\nUndo.Transform.FitToScreen=\"'%1' তে স্ক্রিনে ফিট করুন\"\nUndo.Transform.StretchToScreen=\"'%1' তে স্ক্রিনে প্রসারিত করুন\"\nUndo.Transform.Center=\"'%1' তে স্ক্রিনে কেন্দ্রতে আনুন\"\nUndo.Transform.VCenter=\"'%1' এ স্ক্রীনে উল্লম্ব মধ্যভাগে আনুন\"\nUndo.Transform.HCenter=\"'%1' এ স্ক্রীনে অনুভূমিক মধ্যভাগে আনুন\"\nUndo.Volume.Change=\"'%1' এ ভলিউম পরিবর্তন\"\nUndo.Volume.Mute=\"'%1' মৌন করুন\"\nUndo.Volume.Unmute=\"'%1' অমৌন করুন\"\nUndo.Scene.Duplicate=\"'%1' এর দৃশ্য ডুপ্লিকেট করুন\"\nUndo.ShowTransition=\"'%1' এ ট্রানজিশন দেখান\"\nUndo.HideTransition=\"'%1' এ ট্রানজিশন লুকান\"\nUndo.ShowSceneItem=\"'%2' এ '%1' দেখান\"\nUndo.HideSceneItem=\"'%2' এ '%1' লুকান\"\nUndo.ReorderSources=\"'%1' এ সোর্সগুলি পুনরায় সাজান\"\nUndo.MoveUp=\"'%1' কে '%2' তে তুলে আনুন\"\nUndo.MoveDown=\"'%1' কে '%2' তে নামিয়ে আনুন\"\nUndo.MoveToTop=\"'%1' কে '%2' তে উপরে তুলুন\"\nUndo.MoveToBottom=\"'%1' কে '%2' তে নিচে নামান\"\nTransitionNameDlg.Text=\"কোনও পরিবর্তন ঘটলে স্থানীয় নাম লিখুন\"\nTransitionNameDlg.Title=\"কোনও পরিবর্তন ঘটলে স্থানীয় নাম\"\nTitleBar.Profile=\"প্রোফাইল\"\nTitleBar.Scenes=\"দৃশ্য\"\nNameExists.Title=\"নামটি ইতোমধ্যেই বিদ্যমান\"\nNameExists.Text=\"নামটি ইতোমধ্যে ব্যবহৃত হচ্ছে.\"\nNoNameEntered.Title=\"একটি বৈধ বয়স লিখুন।\"\nNoNameEntered.Text=\"তুমি খালি নাম ব্যবহার করতে পারবেন.\"\nConfirmStart.Title=\"ধারা শুরু হয়?\"\nConfirmStart.Text=\"আপনি কি নিশ্চিত যে আপনি ধারা সূচনা করতে চান?\"\nConfirmStop.Title=\"স্রোত বন্ধ করতে?\"\nConfirmStop.Text=\"আপনি কি নিশ্চিত যে আপনি ধারা সূচনা করতে চান?\"\nConfirmStopRecord.Title=\"রেকর্ডিং থামাবেন?\"\nConfirmStopRecord.Text=\"আপনি কি নিশ্চিত যে আপনি রেকর্ডিং বন্ধ করতে চান?\"\nConfirmBWTest.Title=\"ব্যান্ডউইথ পরীক্ষা শুরু করবেন?\"\nConfirmBWTest.Text=\"আপনি OBS ব্যান্ডউইথ পরীক্ষা মোডে সজ্জিত করেছেন। এই মোডটি আপনার চ্যানেলটি সরাসরি না করে নেটওয়ার্ক পরীক্ষার অনুমতি দেয়। একবার আপনার পরীক্ষাটি শেষ হলে, স্ট্রিমটি দর্শকদের দেখার জন্য এটিকে বন্ধ করতে হবে।\\n\\nআপনি কি অবিরত করতে চান?\"\nConfirmRemove.Title=\"টেমপ্লেট সংরক্ষণ করো\"\nConfirmRemove.TextMultiple=\"আপনি কি নিশ্চিত যে আপনি %1 আইটেম অপসারণ করতে চান?\"\nOutput.StartStreamFailed=\"স্ট্রীমিং চালু করতে ব্যর্থ হয়েছে\"\nOutput.StartRecordingFailed=\"রেকর্ড আরম্ভ করতে ব্যর্থ\"\nOutput.StartReplayFailed=\"রিপ্লে বাফার আরম্ভ করতে ব্যর্থ\"\nOutput.StartFailedGeneric=\"আউটপুট শুরু করতে ব্যর্থ হয়েছে। Details.\\n\\nNote জন্য লগ চেক করুন: NVENC অথবা এএমডি encoders ব্যবহার করলে আপনার ভিডিও ড্রাইভার আপ-টু-ডেট রয়েছে নিশ্চিত করুন.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"বিরতি দেওয়ার সময় রিপ্লেগুলি সংরক্ষণ সম্ভব নয়\"\nOutput.ReplayBuffer.PauseWarning.Text=\"সতর্কতা: রেকর্ডিং বিরতি দেওয়ার সময় রিপ্লেগুলি সংরক্ষণ করা যায় না।\"\nOutput.ConnectFail.Title=\"সংযোগ করতে ব্যর্থ হয়েছে\"\nOutput.ConnectFail.BadPath=\"ত্রুটিপূর্ণ URL পথ বা সংযোগ। দয়া করে সেগুলো কার্যকর নিশ্চিত করতে আপনার সেটিংস চেক করুন.\"\nOutput.ConnectFail.ConnectFailed=\"সার্ভারের সাথে সংযোগ স্থাপন করতে ব্যর্থ\"\nOutput.ConnectFail.InvalidStream=\"উল্লেখিত চ্যানেল বা ধারা কী, এ প্রবেশ করা যাচ্ছে না দয়া করে আপনার স্ট্রিম কী পরীক্ষা। যদি সঠিক হয়, সমস্যা হতে পারে সার্ভারের সাথে সংযোগ স্থাপন করা হচ্ছে.\"\nOutput.ConnectFail.Error=\"সার্ভারের সাথে সংযোগ করার চেষ্টা করার সময় একটি অপ্রত্যাশিত ত্রুটি সংঘটিত হয়েছে। লগ ফাইল আরও তথ্য.\"\nOutput.ConnectFail.Disconnected=\"সার্ভার থেকে সংযোগ বিচ্ছিন্ন হয়েছে.\"\nOutput.StreamEncodeError.Title=\"এনকোডিং ত্রুটি\"\nOutput.StreamEncodeError.Msg=\"স্ট্রিমিংয়ের সময় একটি এনকোডার ত্রুটি ঘটেছে।\"\nOutput.RecordFail.Title=\"রেকর্ড আরম্ভ করতে ব্যর্থ\"\nOutput.RecordFail.Unsupported=\"আউটপুট ফরম্যাট হয়। অসমর্থিত বা একাধিক অডিও ট্র্যাক সমর্থন করে না। অনুগ্রহ করে আপনার সেটিংস পরীক্ষা করুন এবং আবার চেষ্টা করুন.\"\nOutput.RecordNoSpace.Title=\"অপর্যাপ্ত ডিস্ক পরিসর\"\nOutput.RecordNoSpace.Msg=\"না চালিয়ে রেকর্ড করার জন্য পর্যাপ্ত ডিস্ক পরিসর নেই.\"\nOutput.RecordError.Title=\"রেকর্ড করার ত্রুটির\"\nOutput.RecordError.Msg=\"রেকর্ড করার সময় একটি অনির্দিষ্ট ত্রুটি সংঘটিত হয়েছে.\"\nOutput.RecordError.EncodeErrorMsg=\"রেকর্ডিংয়ের সময় একটি এনকোডার ত্রুটি ঘটেছে।\"\nOutput.BadPath.Title=\"খারাপ পার্ট অনুসন্ধানname\"\nOutput.NoBroadcast.Title=\"কোনো সম্প্রচার কনফিগার করা হয়নি\"\nRemux.SourceFile=\"OBS রেকর্ডিং\"\nRemux.TargetFile=\"গন্তব্য ফাইল\"\nRemux.Stop=\"রিমাক্সিং বন্ধ করুন\"\nRemux.ClearFinished=\"সমাপ্ত বস্তুগুলি সাফ করুন\"\nRemux.ClearAll=\"সমস্ত বস্তু সাফ করুন\"\nRemux.OBSRecording=\"OBS রেকর্ডিং\"\nRemux.FinishedTitle=\"Remuxing শেষ\"\nRemux.Finished=\"Remuxed রেকর্ড\"\nRemux.FinishedError=\"রেকর্ড remuxed, কিন্তু ফাইলটি অসম্পূর্ণ হতে পারে\"\nRemux.SelectRecording=\"OBS রেকর্ডিং নির্বাচন করুন...\"\nRemux.SelectTarget=\"লক্ষিত ফাইলটি নির্বাচন করুন...\"\nRemux.FileExistsTitle=\"লক্ষিত ফাইলটি বিদ্যমান\"\nRemux.FileExists=\"নিম্নলিখিত লক্ষিত ফাইলগুলি ইতিমধ্যে বিদ্যমান। আপনি কি তাদের প্রতিস্থাপন করতে চান?\"\nRemux.ExitUnfinishedTitle=\"Remuxing অগ্রসর হচ্ছে\"\nRemux.ExitUnfinished=\"Remuxing শেষ হয় না, এখন বন্ধ হতে পারে যেন গন্তব্য ফাইলটি ব্যবহারের অযোগ্য। আপনি কি নিশ্চিত যে আপনি remuxing বন্ধ করতে\\nAre?\"\nRemux.HelpText=\"রিমাক্স করতে এই উইন্ডোতে ফাইলগুলি ফেলে দিন, অথবা একটি ফাইল ব্রাউজ করতে একটি ফাঁকা \\\"OBS Recording\\\" সেল নির্বাচন করুন।\"\nMissingFiles=\"অনুপস্থিত ফাইলসমূহ\"\nMissingFiles.MissingFile=\"অনুপস্থিত ফাইল\"\nMissingFiles.NewFile=\"নতুন ফাইল\"\nMissingFiles.HelpText=\"আপনি সর্বশেষে OBS ব্যবহার করার পরে কিছু ফাইল অনুপস্থিত রয়েছে।\"\nMissingFiles.Clear=\"<পরিষ্কার>\"\nMissingFiles.Search=\"ডিরেক্টরি অনুসন্ধান করুন...\"\nMissingFiles.SelectFile=\"ফাইল নির্বাচন করুন...\"\nMissingFiles.SelectDir=\"অনুসন্ধান করতে ফোল্ডারটি নির্বাচন করুন\"\nMissingFiles.State=\"অবস্থা\"\nMissingFiles.Missing=\"অনুপস্থিত\"\nMissingFiles.Replaced=\"প্রতিস্থাপনকৃত\"\nMissingFiles.Cleared=\"পরিষ্কার হয়েছে\"\nMissingFiles.Found=\"প্রাপ্ত\"\nMissingFiles.AutoSearch=\"অতিরিক্ত ফাইল মিল খুঁজে পাওয়া গেছে\"\nMissingFiles.AutoSearchText=\"OBS সেই ডিরেক্টরিতে হারিয়ে যাওয়া ফাইলগুলির জন্য অতিরিক্ত মিল খুঁজে পেয়েছে। আপনি কি তাদের যুক্ত করতে চান?\"\nMissingFiles.NoMissing.Title=\"অনুপস্থিত ফাইল চেক\"\nMissingFiles.NoMissing.Text=\"কোনো ফাইলস অখোজ পাওয়া যায়নি।\"\nMacPermissions.AccessGranted=\"প্রবেশ অনুমোদন করা হয়েছে\"\nMacPermissions.Item.Camera=\"ক্যামেরা\"\nMacPermissions.Item.Microphone=\"মাইক্রোফোন\"\nMacPermissions.Item.Accessibility=\"প্রবেশযোগ্যতা\"\nMacPermissions.Continue=\"এগিয়ে যাও\"\nBasic.DesktopDevice1=\"ডেস্কটপ অডিও\"\nBasic.DesktopDevice2=\"ডেস্কটপ অডিও 2\"\nBasic.AuxDevice1=\"মাইক/সহায়ক\"\nBasic.AuxDevice2=\"মাইক/সহায়ক 2\"\nBasic.AuxDevice3=\"মাইক/সহায়ক 3\"\nBasic.AuxDevice4=\"মাইক/4 সহায়ক\"\nBasic.Scene=\"দৃশ্য\"\nBasic.DisplayCapture=\"অধিগ্রহণ প্রদর্শন করুন\"\nBasic.Main.PreviewConextMenu.Enable=\"প্রাকদর্শন সক্রিয় করুন\"\nBasic.Main.Preview.Disable=\"প্রাকদর্শন নিষ্ক্রিয়\"\nScaleFiltering=\"ফিল্টারিং এর আকার পরিবর্তন করো\"\nScaleFiltering.Point=\"বিন্দু\"\nScaleFiltering.Area=\"এলাকা\"\nBlendingMethod.Default=\"সহজাত\"\nBlendingMethod.SrgbOff=\"এসআরজিবি(sRGB) বন্ধ\"\nBlendingMode.Normal=\"সাধারণ\"\nBlendingMode.Additive=\"যোগ\"\nBlendingMode.Screen=\"পর্দা\"\nBlendingMode.Multiply=\"গুণ কর\"\nBlendingMode.Lighten=\"সাদাকরণ\"\nBlendingMode.Darken=\"অন্ধকারাচ্ছন্ন\"\nDeinterlacing.Discard=\"অগ্রাহ্য\"\nDeinterlacing.TopFieldFirst=\"প্রথম ক্ষেত্রে শীর্ষ\"\nDeinterlacing.BottomFieldFirst=\"মাঠে প্রথমে নীচের\"\nVolControl.SliderUnmuted=\"'%1' এর জন্য শব্দ স্লাইডার:\"\nVolControl.SliderMuted=\"'%1' এর জন্য শব্দ স্লাইডার: (বর্তমানে নিঃশব্দ)\"\nVolControl.Mute=\"কথা বলতে পারে '%1 '\"\nVolControl.Properties=\"'%1'-এর জন্য বৈশিষ্ট্যসমূহ\"\nBasic.Main.AddSceneDlg.Title=\"দৃশ্য যোগ করুন\"\nBasic.Main.AddSceneDlg.Text=\"ঘটনাস্থলে নাম লিখুন\"\nBasic.Main.DefaultSceneName.Text=\"ঘটনাস্থলে %1\"\nBasic.Main.AddSceneCollection.Title=\"দৃশ্য সংগ্রহ যুক্ত করুন\"\nBasic.Main.AddSceneCollection.Text=\"দৃশ্য সংগ্রহ নাম লিখুন\"\nBasic.Main.RenameSceneCollection.Title=\"দৃশ্য সংগ্রহ পুনঃনামকরণ করুন\"\nAddProfile.Title=\"প্রোফাইল যুক্ত করুন\"\nAddProfile.Text=\"প্রোফাইলের নাম লিখুন\"\nAddProfile.WizardCheckbox=\"স্বতঃ-কনফিগারেশন জাদুকর দেখান\"\nRenameProfile.Title=\"প্রোফাইল পুনঃনামকরণ করুন\"\nBasic.Main.MixerRename.Title=\"শব্দের উৎসের নামকরণ\"\nBasic.Main.MixerRename.Text=\"অনুগ্রহপূর্বক অডিও উৎসটির নাম লিখুন\"\nBasic.Main.PreviewDisabled=\"প্রাক্-বীক্ষণ বর্তমানে নিষ্ক্রিয় করা হয়েছে\"\nBasic.SourceSelect=\"সূত্র তৈরি/নির্বাচন করুন\"\nBasic.SourceSelect.CreateNew=\"নতুন তৈরি করুন\"\nBasic.SourceSelect.AddExisting=\"বর্তমান যুক্ত করুন\"\nBasic.SourceSelect.AddVisible=\"উৎস করে প্রদর্শিত\"\nBasic.Main.Sources.Visibility=\"দৃশ্যমানতা\"\nBasic.Main.Sources.VisibilityDescription=\"ক্যানভাসে '%1' এর দৃশ্যমানতা নিয়ন্ত্রণ করে\"\nBasic.Main.Sources.Lock=\"লক\"\nBasic.Main.Sources.LockDescription=\"ক্যানভাসে '%1' এর অবস্থান এবং স্কেল লক করে\"\nBasic.PropertiesWindow=\"'%1'-এর জন্য বৈশিষ্ট্যসমূহ\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (স্বয়ংনির্বাচন: %2)\"\nBasic.PropertiesWindow.SelectColor=\"রঙ নির্বাচন করুন\"\nBasic.PropertiesWindow.SelectFont=\"ফন্ট নির্বাচন করুন\"\nBasic.PropertiesWindow.ConfirmTitle=\"বৈশিষ্ট্য পরিবর্তিত হয়েছে\"\nBasic.PropertiesWindow.Confirm=\"এতে পরিবর্তন অসংরক্ষিত সংশোধনী রয়েছে। আপনি তাদের রেখে দিতে চান?\"\nBasic.PropertiesWindow.NoProperties=\"কোন বৈশিষ্ট্য সুপ্রাপ্য নয়\"\nBasic.PropertiesWindow.AddFiles=\"ফাইল যোগ করুন\"\nBasic.PropertiesWindow.AddDir=\"ডিরেক্টরিটি যোগ করুন\"\nBasic.PropertiesWindow.AddURL=\"পথ/ইউআরএল যুক্ত করুন\"\nBasic.PropertiesWindow.AddEditableListDir=\"\\\"%1\\\" ডিরেক্টরিটি যোগ করুন\"\nBasic.PropertiesWindow.AddEditableListFiles=\"'%1 জন্য' ফাইলসমূহ যুক্ত করুন\"\nBasic.PropertiesWindow.AddEditableListEntry=\"\\\"%1\\\" এন্ট্রি যোগ করো\"\nBasic.PropertiesWindow.EditEditableListEntry=\"'%1' থেকে এন্ট্রি সম্পাদনা করুন\"\nBasic.PropertiesView.FPS.Simple=\"FPS সাধারণ মানসমূহ\"\nBasic.PropertiesView.FPS.Rational=\"যৌক্তিক FPS মান\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"বৈধ FPS রেঞ্জ:\"\nBasic.PropertiesView.UrlButton.Text=\"এই লিংক কি আপনার ডিফল্ট ওয়েব ব্রাউজার দিয়ে খুলবেন?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"ইউআরএল: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"URL খুলুন\"\nBasic.InteractionWindow=\"%1 সাথে মিথস্ক্রিয়া\"\nBasic.StatusBar.Reconnecting=\"সংযোগ বিচ্ছিন্ন করে %2 (s) (%1 চেষ্টা) ইভেন্টটি পুনরায় সংযোগ করা হচ্ছে\"\nBasic.StatusBar.AttemptingReconnect=\"পুনঃসংযোগের জন্য চেষ্টা করছে... (%1 প্রয়াস)\"\nBasic.StatusBar.ReconnectSuccessful=\"প্রাইস সফল\"\nBasic.StatusBar.Delay=\"বিলম্ব (সেকেন্ড%1)\"\nBasic.StatusBar.DelayStartingIn=\"বিলম্ব (সেকেন্ড প্রতি %1 শুরু)\"\nBasic.StatusBar.DelayStoppingIn=\"বিলম্ব (সেকেন্ড প্রতি %1 থামানো হচ্ছে)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"বিলম্ব (সেকেন্ড %1 বন্ধ, %2 সেকেন্ড থেকে শুরু)\"\nBasic.StatusBar.RecordingSavedTo=\"রেকর্ডিং '%1' তে সেভ হয়েছে\"\nBasic.StatusBar.ReplayBufferSavedTo=\"'%1' তে সেভ হওয়া বাফার রিপ্লে করুন\"\nBasic.StatusBar.ScreenshotSavedTo=\"'%1' এ স্ক্রিনশট সংরক্ষিত\"\nBasic.StatusBar.AutoRemuxedTo=\"রেকর্ডিং '%1' তে অটো রিমাক্স হয়ে গেছে\"\nBasic.Filters=\"ফিল্টার\"\nBasic.Filters.AsyncFilters=\"অডিও/ভিডিও ফিল্টার\"\nBasic.Filters.AudioFilters=\"অডিও ফিল্টার\"\nBasic.Filters.EffectFilters=\"প্রভাব ফিল্টার\"\nBasic.Filters.Title=\"জন্য '%1' ফিল্টার\"\nBasic.Filters.AddFilter.Title=\"পরিশ্রুতক নাম\"\nBasic.Filters.AddFilter.Text=\"দয়া করে ফিল্টার নাম নির্দিষ্টভাবে উল্লেখ করুন\"\nBasic.TransformWindow=\"দৃশ্যে আইটেম রূপান্তর\"\nBasic.TransformWindow.Position=\"অবস্থান\"\nBasic.TransformWindow.PositionX=\"অবস্থান X\"\nBasic.TransformWindow.PositionY=\"অবস্থান Y\"\nBasic.TransformWindow.Rotation=\"ঘুর্ণন\"\nBasic.TransformWindow.Size=\"আকার\"\nBasic.TransformWindow.Width=\"প্রস্থ\"\nBasic.TransformWindow.Height=\"উচ্চতা\"\nBasic.TransformWindow.Alignment=\"অবস্থান প্রান্তিককরণ\"\nBasic.TransformWindow.BoundsType=\"লগ্ন বক্সে টাইপ করুন\"\nBasic.TransformWindow.BoundsAlignment=\"বাক্স লগ্ন প্রান্তিককরণ\"\nBasic.TransformWindow.Bounds=\"বাক্সের মাপ লগ্ন\"\nBasic.TransformWindow.BoundsWidth=\"বাউন্ডিং বক্স প্রস্থ\"\nBasic.TransformWindow.BoundsHeight=\"বাউন্ডিং বক্স উচ্চতা\"\nBasic.TransformWindow.Crop=\"ক্রপ\"\nBasic.TransformWindow.CropLeft=\"বামে ছাঁটুন\"\nBasic.TransformWindow.CropRight=\"ডানে ছাঁটুন\"\nBasic.TransformWindow.CropTop=\"শীর্ষে ছাঁটুন\"\nBasic.TransformWindow.CropBottom=\"নীচে ছাঁটুন\"\nBasic.TransformWindow.Alignment.TopLeft=\"উপরে বামে\"\nBasic.TransformWindow.Alignment.TopCenter=\"উপরে কেন্দ্রে\"\nBasic.TransformWindow.Alignment.TopRight=\"উপরে ডানে\"\nBasic.TransformWindow.Alignment.CenterLeft=\"কেন্দ্র বাম\"\nBasic.TransformWindow.Alignment.Center=\"কেন্দ্রে\"\nBasic.TransformWindow.Alignment.CenterRight=\"কেন্দ্র ঠিক\"\nBasic.TransformWindow.Alignment.BottomLeft=\"নিচে বামে\"\nBasic.TransformWindow.Alignment.BottomCenter=\"নিচে কেন্দ্রে\"\nBasic.TransformWindow.Alignment.BottomRight=\"নিচে ডানে\"\nBasic.TransformWindow.BoundsType.None=\"কোন সীমা\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"সর্বোচ্চ সাইজ মাত্র\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"ভেতরের সীমা থেকে আকার পরিবর্তন করো\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"বাইরের সীমা থেকে আকার পরিবর্তন করো\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"পাতার প্রস্থের মধ্যে সীমা আকার পরিবর্তন করো\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"সীমা উচ্চতা আকার পরিবর্তন করো\"\nBasic.TransformWindow.BoundsType.Stretch=\"প্রসারিত করার সীমা\"\nBasic.TransformWindow.NoSelectedSource=\"উৎস বাছাই হয়নি\"\nBasic.Main.AddSourceHelp.Title=\"উৎস যোগ করা যাচ্ছে না\"\nBasic.Main.AddSourceHelp.Text=\"আপনি সূত্র যোগ করার নূন্যতম ১ দৃশ্য থাকতে হবে.\"\nBasic.Main.Scenes=\"দৃশ্য\"\nBasic.Main.Sources=\"সোর্স\"\nBasic.Main.Source=\"উৎস\"\nBasic.Main.Controls=\"নিয়ন্ত্রণসমূহ\"\nBasic.Main.Connecting=\"সংযোগ করা হচ্ছে...\"\nBasic.Main.StartRecording=\"রেকর্ডিং শুরু করুন\"\nBasic.Main.StartReplayBuffer=\"রিপ্লে বাফার শুরু\"\nBasic.Main.SaveReplay=\"রিপ্লে সংরক্ষণ করুন\"\nBasic.Main.StartStreaming=\"স্ট্রিমিং-এর শুরু\"\nBasic.Main.StartBroadcast=\"সরাসরি যান।\"\nBasic.Main.StartVirtualCam=\"ভার্চুয়াল ক্যামেরা শুরু করুন\"\nBasic.Main.StopRecording=\"রেকর্ডিং বন্ধ করুন\"\nBasic.Main.PauseRecording=\"রেকর্ডিং বিরতি\"\nBasic.Main.UnpauseRecording=\"রেকর্ডিং আবিরত\"\nBasic.Main.StoppingRecording=\"রেকর্ড করা বন্ধ করা হচ্ছে...\"\nBasic.Main.StopReplayBuffer=\"রিপ্লে বাফার বন্ধ\"\nBasic.Main.StoppingReplayBuffer=\"রিপ্লে বাফার বন্ধ করা হচ্ছে...\"\nBasic.Main.SetupBroadcast=\"সম্প্রচার পরিচালনা\"\nBasic.Main.StopStreaming=\"স্ট্রিমিং বন্ধের\"\nBasic.Main.StopBroadcast=\"সরাসরি শেষ করুন\"\nBasic.Main.AutoStopEnabled=\"(স্বয়ংক্রিয় স্টপ)\"\nBasic.Main.StoppingStreaming=\"স্রোত বন্ধ হয়ে যাওয়ার...\"\nBasic.Main.ForceStopStreaming=\"(অবকাশ বাতিল) Streaming বন্ধ\"\nBasic.Main.ShowContextBar=\"উৎস সরঞ্জামদণ্ড প্রদর্শন করুন\"\nBasic.Main.HideContextBar=\"উৎস সরঞ্জামদণ্ডটি লুকান\"\nBasic.Main.StopVirtualCam=\"ভার্চুয়াল ক্যামেরা বন্ধ করুন\"\nBasic.Main.Group=\"গ্রুপ %1\"\nBasic.Main.GroupItems=\"নির্বাচিত বস্তুগুলো দলবদ্ধ করুন\"\nBasic.Main.Ungroup=\"দলবিহীন করুন\"\nBasic.Main.GridMode=\"গ্রিড মোড\"\nBasic.Main.ListMode=\"তালিকা মোড\"\nBasic.MainMenu.File=\"ফাইল (&F)\"\nBasic.MainMenu.File.Export=\"এক্সপোর্ট (&E)\"\nBasic.MainMenu.File.Import=\"আমদানি করুন (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"প্রদর্শন এবং রেকর্ডিং (&R)\"\nBasic.MainMenu.File.Remux=\"রিম্যাক্স রেকর্ডিংস (&M)\"\nBasic.MainMenu.File.Settings=\"সেটিংস (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"বৈশিষ্ট্য ফোল্ডার প্রদর্শন করা হবে\"\nBasic.MainMenu.File.ShowProfileFolder=\"প্রোফাইল ফোল্ডার প্রদর্শন করা হবে\"\nBasic.MainMenu.File.ShowMissingFiles=\"খোজ ফাইল খুঁজুন\"\nBasic.MainMenu.File.Exit=\"প্রস্থান (&X)\"\nBasic.MainMenu.Edit=\"সম্পাদনা করুন (&E)\"\nBasic.MainMenu.Edit.Undo=\"বাতিল করুন (&U)\"\nBasic.MainMenu.Edit.Redo=\"পুনরায় করুন (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"প্রাকদর্শন লক করুন (&L)\"\nBasic.MainMenu.Edit.Scale=\"প্রাক্-বীক্ষণ স্কেল (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"ভেতর থেকে জানালা\"\nBasic.MainMenu.Edit.Scale.Canvas=\"ক্যানভাস (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"আউটপুট (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"রূপান্তর (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"রূপান্তর সম্পাদনা করুন... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"রূপান্তর অনুলিপি করুন\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"রূপান্তর প্রতিলেপন করুন\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"রূপান্তর রিসেট করুন (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"CW 90 ডিগ্রী ঘোরাও\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"CW 90 ডিগ্রী ঘোরাও\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180 ডিগ্রী ঘোরাও\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"ফ্লিপ এবং অনুভূমিক (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"ফ্লিপ এবং উল্লম্ব (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"এবং পর্দায় সমন্বয় করুন (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"এবং পর্দা করতে বিস্তৃত করুন (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"এবং পর্দার কেন্দ্রে (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"উল্লম্বভাবে কেন্দ্র\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"অনুভূমিকভাবে কেন্দ্র\"\nBasic.MainMenu.Edit.Order=\"ও যেন (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"সরিয়ে নিন এবং আপ (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"নিচে নামান (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"সরিয়ে দিতে ও শীর্ষ (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"সরান এবং নিচে (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"এবং অডিও উন্নত বৈশিষ্ট্য (&A)\"\nBasic.MainMenu.View=\"এবং প্রদর্শন করুন (&V)\"\nBasic.MainMenu.View.Toolbars=\"টুলবার (&T)\"\nBasic.MainMenu.View.ContextBar=\"উৎস সরঞ্জামদণ্ড\"\nBasic.MainMenu.View.SourceIcons=\"উৎস আইকন (&I)\"\nBasic.MainMenu.View.StatusBar=\"স্ট্যাটাস বার (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"ফুলস্ক্রিন ইন্টারফেস\"\nBasic.MainMenu.Docks=\"&ডকসমূহ\"\nBasic.MainMenu.SceneCollection=\"দৃশ্য সংগ্রহ (&S)\"\nBasic.MainMenu.Profile=\"প্রোফাইল (&P)\"\nBasic.MainMenu.Profile.Import=\"প্রোফাইল আমদানি করুন\"\nBasic.MainMenu.Profile.Export=\"প্রোফাইল রপ্তানি করুন\"\nBasic.MainMenu.SceneCollection.Import=\"দৃশ্য সংগ্রহ আমদানি করুন\"\nBasic.MainMenu.SceneCollection.Export=\"দৃশ্য সংগ্রহ আমদানি করুন\"\nBasic.MainMenu.Profile.Exists=\"প্রোফাইল ইতোমধ্যেই বিদ্যমান\"\nBasic.MainMenu.Tools=\"সরঞ্জাম (&T)\"\nBasic.MainMenu.Help=\"সাহায্য (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"সহায়তা পোর্টাল (&P)\"\nBasic.MainMenu.Help.Website=\"দর্শন ও ওয়েবসাইট (&W)\"\nBasic.MainMenu.Help.Discord=\"ডিস্কর্ড সার্ভারে যোগ দিন (&D)\"\nBasic.MainMenu.Help.Logs=\"লগ ফাইল (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"লগ ফাইলগুলো প্রদর্শন করুন (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"বর্তমান লগ ফাইল আপলোড করুন (&C)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&View লগ দেখাও\"\nBasic.MainMenu.Help.CheckForUpdates=\"হালনাগাদের জন্য পরীক্ষা করুন\"\nBasic.MainMenu.Help.CrashLogs=\"ক্র্যাশ প্রতিবেদন (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"ক্র্যাশ প্রতিবেদন দেখান (&S)\"\nBasic.MainMenu.Help.About=\"সম্পর্কে (&A)\"\nBasic.Settings.ProgramRestart=\"এই প্রোগ্রামটি এই সেটিংস প্রভাবী করতে পুনর্সূচনা করা আবশ্যক.\"\nBasic.Settings.ConfirmTitle=\"পরিবর্তন করুন\"\nBasic.Settings.Confirm=\"পরিবর্তন অসংরক্ষিত সংশোধনী রয়েছে। পরিবর্তন সংরক্ষণ করব?\"\nBasic.Settings.General=\"সাধারণ\"\nBasic.Settings.General.Language=\"ভাষা\"\nBasic.Settings.General.Updater=\"হালনাগাদ\"\nBasic.Settings.General.EnableAutoUpdates=\"প্রারম্ভকালে আপডেটের জন্য স্বয়ংক্রিয়ভাবে চেক করুন\"\nBasic.Settings.General.OpenStatsOnStartup=\"শুরুতে পরিসংখ্যান ডায়ালগ খুলুন\"\nBasic.Settings.General.WarnBeforeStartingStream=\"ধারা শুরু করার সময় নিশ্চিতকরণ সংলাপ প্রদর্শন করুন\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"ধারা শুরু করার সময় নিশ্চিতকরণ সংলাপ প্রদর্শন করুন\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"রেকর্ডিং বন্ধ করার সময় নিশ্চিতকরণ ডায়ালগ দেখান\"\nBasic.Settings.General.Projectors=\"প্রক্ষেপকের\"\nBasic.Settings.General.HideProjectorCursor=\"প্রক্ষেপকের উপর কার্সর আড়াল করা হবে\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"প্রজেক্টরে সবসময় উপরে রাখুন\"\nBasic.Settings.General.Snapping=\"স্ন্যাপ উৎস প্রান্তিককরণ\"\nBasic.Settings.General.ScreenSnapping=\"পর্দার প্রান্ত থেকে সূত্র স্ন্যাপ করুন\"\nBasic.Settings.General.CenterSnapping=\"অনুভূমিক এবং উল্লম্ব কেন্দ্র সূত্র স্ন্যাপ করুন\"\nBasic.Settings.General.SourceSnapping=\"অন্যান্য সূত্র থেকে সূত্র স্ন্যাপ করুন\"\nBasic.Settings.General.SnapDistance=\"স্ন্যাপ সংবেদনশীলতা\"\nBasic.Settings.General.RecordWhenStreaming=\"স্ট্রীমিং-এর সময় স্বয়ংক্রিয়ভাবে রেকর্ড\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"স্রোত থেমে যাবে যখন রেকর্ড রাখা\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"স্ট্রীমিং-এর সময় স্বয়ংক্রিয়ভাবে রিপ্লে বাফার শুরু করুন\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"রিপ্লে বাফার বহা ধারা বন্ধ হয়ে যায় তখন\"\nBasic.Settings.General.SysTray=\"সিস্টেম ট্রে\"\nBasic.Settings.General.SysTrayWhenStarted=\"ছোট থেকে শুরু করে, তখন সিস্টেম ট্রে\"\nBasic.Settings.General.SystemTrayHideMinimize=\"টাস্ক বার এর পরিবর্তে সিস্টেম ট্রে থেকে সব সময় ছোট করুন\"\nBasic.Settings.General.SaveProjectors=\"প্রস্থানকালে প্রক্ষেপকের সংরক্ষণ করো\"\nBasic.Settings.General.Preview=\"প্রাকদর্শন\"\nBasic.Settings.General.OverflowHidden=\"ওভারফ্লো বন্ধ করুন\"\nBasic.Settings.General.OverflowAlwaysVisible=\"সর্বদা ওভারফ্লো দেখান\"\nBasic.Settings.General.OverflowSelectionHidden=\"উৎস অদৃশ্য হয়ে গেলেও ওভারফ্লো দেখান\"\nBasic.Settings.General.Importers=\"আমদানিকারকরা\"\nBasic.Settings.General.AutomaticCollectionSearch=\"আমদানি করার সময় দৃশ্যের সংগ্রহের জন্য পরিচিত অবস্থানগুলি অনুসন্ধান করুন\"\nBasic.Settings.General.SwitchOnDoubleClick=\"ডাবল ক্লিক করলে দৃশ্যে স্থানান্তর\"\nBasic.Settings.General.StudioPortraitLayout=\"প্রতিকৃতি/উল্লম্ব বিন্যাস সক্ষম করুন\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"পূর্বরূপ/প্রোগ্রামের লেবেলগুলি দেখান\"\nBasic.Settings.General.Multiview=\"মাল্টিভিউ\"\nBasic.Settings.General.Multiview.MouseSwitch=\"দৃশ্যের মধ্যে অদলবদল করতে ক্লিক করুন\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"দৃশ্যের নামগুলি দেখান\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"নিরাপদ অঞ্চলগুলি আঁকুন (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"মাল্টিভিউ লেআউট\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"অনুভূমিক, শীর্ষ (৮ টি দৃশ্য)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"অনুভূমিক, নীচে (৮ দৃশ্য)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"লম্ব, বাম (৮ টি দৃশ্য)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"লম্ব, ডান (৮ দৃশ্য)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"অনুভূমিক, শীর্ষ (২৪ টি দৃশ্য)\"\nBasic.Settings.General.ChannelName.stable=\"স্থিতিশীল\"\nBasic.Settings.Stream=\"ধারা\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"প্রমাণীকরণ ব্যবহার করুন\"\nBasic.Settings.Stream.Custom.Username=\"ইউজারনেম\"\nBasic.Settings.Stream.Custom.Password=\"পাসওয়ার্ড\"\nBasic.Settings.Stream.BandwidthTestMode=\"ব্যান্ডউইথ পরীক্ষা মোড সক্ষম করুন\"\nBasic.Settings.Stream.TTVAddon=\"টুইচ চ্যাট অ্যাড-অনস\"\nBasic.Settings.Stream.TTVAddon.None=\"কোনটিই নয়\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV এবং FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"স্ট্রিম সেটআপ অনুপস্থিত\"\nBasic.Settings.Stream.StreamSettingsWarning=\"সেটিংস খুলুন\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"ইউআরএল এবং স্ট্রিম কী অনুপস্থিত।\\n\\nসেটিংস খুলুন এবং 'স্ট্রিম' ট্যাবে স্ট্রিম কী ও ইউআরএল প্রবেশ করুন।\"\nBasic.Settings.Stream.MissingUrl=\"স্ট্রিম ইউআরএল অনুপস্থিত।\\n\\nসেটিংস খুলুন এবং 'স্ট্রিম' ট্যাবে ইউআরএলটি প্রবেশ করুন।\"\nBasic.Settings.Stream.MissingStreamKey=\"স্ট্রিম কী অনুপস্থিত।\\n\\nসেটিংস খুলুন এবং 'স্ট্রিম' ট্যাবে স্ট্রিম কী ও ইউআরএল প্রবেশ করান।\"\nBasic.Settings.Stream.IgnoreRecommended=\"স্ট্রিমিং পরিষেবা সেটিংয়ের প্রস্তাবগুলি উপেক্ষা করুন\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"প্রস্তাবিত সেটিংস ওভাররাইড করুন\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"সতর্কতা: পরিষেবাটির সীমাবদ্ধতা উপেক্ষা করার ফলে স্ট্রিমের মান হ্রাস পেতে পারে বা স্ট্রিমিং থেকে বিরত থাকতে পারে।\\n\\nচালিয়ে যাবেন?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"সর্বাধিক অডিও বিট্রেট: %1 কেবিপিএস\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"সর্বাধিক অডিও বিট্রেট: %1 কেবিপিএস\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"সর্বাধিক রেজোলিউশন: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"সর্বোচ্চ এফপিএস: %1\"\nBasic.Settings.Output=\"আউটপুট\"\nBasic.Settings.Output.Format=\"আমল লেখকবৃন্দ বিন্যাস\"\nBasic.Settings.Output.SelectDirectory=\"রেকর্ডিং ডিরেক্টরি নির্বাচন করুন\"\nBasic.Settings.Output.DynamicBitrate=\"যানজট পরিচালনা করতে গতিময়ভাবে বিটরেট পরিবর্তন করুন\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"যানজট (পরীক্ষামূলক) পরিচালনা করতে গতিময়ভাবে বিটরেট পরিবর্তন করুন\"\nBasic.Settings.Output.DynamicBitrate.TT=\"ভিড় কমাতে ফ্রেম বাদ দেওয়ার পরিবর্তে, গতিশীলভাবে বিটরেট পরিবর্তন করে।\\n\\nমনে রাখবেন যে উল্লেখযোগ্য হঠাৎ ভিড় থাকলে এটি দর্শকদের জন্য বিলম্ব বাড়িয়ে তুলতে পারে।\\nবিটরেট কমে গেলে, পুনরুদ্ধার করতে কয়েক মিনিট সময় নিতে পারে।\\n\\nবর্তমানে কেবল আরটিএমপি-র জন্য সমর্থিত।\"\nBasic.Settings.Output.Mode=\"আউটপুট মোড\"\nBasic.Settings.Output.Mode.Simple=\"সাধারণ\"\nBasic.Settings.Output.Mode.Adv=\"অ্যাডভান্সড\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg উত্পাদন\"\nBasic.Settings.Output.UseReplayBuffer=\"রিপ্লে বাফার সক্রিয় করা হবে\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"সর্বাধিক রিপ্লে সময়\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"ব্যবহার করা: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"স্মৃতি ব্যবহার অনুমান করতে পারে না। অনুগ্রহ করে স্মৃতি সর্বোচ্চ সীমা নির্ধারণ করুন.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"রিপ্লে বাফার ফাইলের নাম উপসর্গ\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"প্রত্যয়\"\nBasic.Settings.Output.Simple.SavePath=\"রেকর্ডিংয়ের পাথ\"\nBasic.Settings.Output.Simple.RecordingQuality=\"রেকর্ডিং গুণমান\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"স্ট্রিম এর সমান\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"উচ্চমানের, মাঝারি ফাইলের আকার\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"পৃথকীকরণযোগ্য গুণমান, বড় আকারের ফাইল\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"হ্রাসহীন গুণমান, বৃহৎ আকারের ফাইল\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"সতর্কতা: স্ট্রিমিং ভিডিওর বিটরেটটি %1 এ সেট করা হবে, যা বর্তমান স্ট্রিমিং পরিষেবার জন্য সর্বাধিক সীমা।\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"সতর্কতা: স্ট্রিমিং শব্দের বিটরেটটি %1 এ সেট করা হবে, যা বর্তমান স্ট্রিমিং পরিষেবার জন্য সর্বাধিক সীমা।\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"সতর্কতা: রেকর্ডিংয়ের মানটি \\\"স্ট্রিম এর সমান\\\" তে সেট করা থাকলে রেকর্ডিংগুলিকে বিরতি দেওয়া যায় না।\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"সতর্কতা: স্ট্রিমের চেয়ে আলাদা মানের একটি সফ্টওয়্যার এনকোডার দিয়ে রেকর্ডিংয়ের জন্য একই সময়ে স্ট্রিম করে রেকর্ড করলে অতিরিক্ত সিপিইউ ব্যবহারের প্রয়োজন হবে।\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"আপনি কি নিশ্চিতরূপে হ্রাসহীন গুণমানটি ব্যবহার করতে চান?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"হ্রাসহীন মানের সতর্কতা!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"সফটওয়্যার (x২৬৪)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"সফ্টওয়্যার (x২৬৪ কম সিপিইউ ব্যবহারের প্রিসেট, ফাইলের আকার বাড়ায়)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"টুইচ ভোড ট্র্যাক (ট্র্যাক ২ ব্যাবহার করে)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"বেমানান রেজোলিউশন/ফ্রেমহার\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"এই স্ট্রিমিং পরিষেবাটি আপনার বর্তমান আউটপুট রেজোলিউশন এবং/অথবা ফ্রেমহারকে সমর্থন করে না। এগুলি নিকটতম সামঞ্জস্যপূর্ণ মানটিতে পরিবর্তিত হবে:\\n\\n%1\\n\\nআপনি কি অবিরত করতে চান?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"রেজোলিউশন: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"এফপিএস: %1\"\nBasic.Settings.Output.VideoBitrate=\"ভিডিও বিটরেট\"\nBasic.Settings.Output.AudioBitrate=\"শব্দের বিটরেট\"\nBasic.Settings.Output.Reconnect=\"স্বয়ংক্রিয়ভাবে পুনঃসংযোগ\"\nBasic.Settings.Output.RetryDelay=\"পুনরায় চেষ্টার বিলম্ব\"\nBasic.Settings.Output.MaxRetries=\"সর্বাধিক পুনরায় চেষ্টা করুন\"\nBasic.Settings.Output.EncoderPreset=\"এনকোডার প্রিসেট\"\nBasic.Settings.Output.CustomEncoderSettings=\"পছন্দসই এনকোডার সেটিংস\"\nBasic.Settings.Output.CustomMuxerSettings=\"পছন্দসই মাক্সার সেটিংস\"\nBasic.Settings.Output.NoSpaceFileName=\"স্পেস ছাড়াই ফাইলের নাম উৎপন্ন করুন\"\nBasic.Settings.Output.Adv.Rescale=\"আউটপুট রিস্কেল করুন\"\nBasic.Settings.Output.Adv.AudioTrack=\"অডিও ট্র্যাক\"\nBasic.Settings.Output.Adv.Streaming=\"স্ট্রিমিং\"\nBasic.Settings.Output.Adv.Audio.Track1=\"ট্র্যাক ১\"\nBasic.Settings.Output.Adv.Audio.Track2=\"ট্র্যাক ২\"\nBasic.Settings.Output.Adv.Audio.Track3=\"ট্র্যাক ৩\"\nBasic.Settings.Output.Adv.Audio.Track4=\"ট্র্যাক ৪\"\nBasic.Settings.Output.Adv.Audio.Track5=\"ট্র্যাক ৫\"\nBasic.Settings.Output.Adv.Audio.Track6=\"ট্র্যাক ৬\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"টুইচ ভোড ট্র্যাক\"\nBasic.Settings.Output.Adv.Recording=\"রেকর্ড হচ্ছে\"\nBasic.Settings.Output.Adv.Recording.RecType=\"রেকর্ডিংয়ের ধরণ\"\nBasic.Settings.Output.Adv.Recording.Type=\"ধরন\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"সাধারণ\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"পছন্দসই আউটপুট (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(স্ট্রিম এনকোডার ব্যবহার করুন)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"ফাইলের নাম বিন্যাসকরণ\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"ফাইল বিদ্যমান থাকলে ওভাররাইট করুন\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg আউটপুটের ধরণ\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"ইউআরএল আউটপুট\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"আউটপুট ফাইল\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"সাধারণ রেকর্ড ফরম্যাট\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"সকল ফাইল\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"ফাইল পথ বা URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"কন্টেইনার ফরম্যাট\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"অডিও\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"ভিডিও\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"পূর্ব-নির্ধারিত বিন্যাস\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"কন্টেইনার ফরম্যাট বিবরণ\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"অডিও/ভিডিও ফাইলের পথটি বা URL থেকে অনুমিত কোডেক\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"পূর্ব-নির্ধারিত এনকোডার\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"এনকোডার নিষ্ক্রিয় করা হবে\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"ভিডিও এনকোডার\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"ভিডিও এনকোডার সেটিংস (যদি থাকে)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"অডিও এনকোডার\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"ভিডিও এনকোডার সেটিংস (যদি থাকে)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxer সেটিংস (যদি থাকে)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Keyframe বিরতি (ফ্রেম)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"সব কোডেক প্রদর্শন করা হবে (যদিও সম্ভাব্য অসঙ্গত)\"\nScreenshot=\"স্ক্রিনশট আউটপুট\"\nScreenshot.SourceHotkey=\"স্ক্রিনশট নির্বাচিত উৎস\"\nScreenshot.StudioProgram=\"স্ক্রিনশট (প্রোগ্রাম)\"\nScreenshot.Preview=\"স্ক্রিনশট (পূর্বরূপ)\"\nScreenshot.Scene=\"স্ক্রিনশট (দৃশ্য)\"\nScreenshot.Source=\"স্ক্রিনশট (উৎস)\"\nFilenameFormatting.TT.Y=\"বছর\"\nBasic.Settings.Video=\"ভিডিও\"\nBasic.Settings.Video.Adapter=\"ভিডিও অভিযোজক\"\nBasic.Settings.Video.BaseResolution=\"বেস (ক্যানভাস) নিয়মন\"\nBasic.Settings.Video.ScaledResolution=\"আউটপুট রেজুলেশন: (আকার পরিবর্তিত)\"\nBasic.Settings.Video.DownscaleFilter=\"Downscale ফিল্টার\"\nBasic.Settings.Video.FPSCommon=\"FPS সাধারণ মানসমূহ\"\nBasic.Settings.Video.FPSInteger=\"পূর্ণসংখ্যা FPS মান\"\nBasic.Settings.Video.FPSFraction=\"ভগ্নাংশের FPS মান\"\nBasic.Settings.Video.Numerator=\"লব।\"\nBasic.Settings.Video.Denominator=\"হর\"\nBasic.Settings.Video.Renderer=\"রূপায়ন।\"\nBasic.Settings.Video.InvalidResolution=\"অবৈধ প্রস্তাব মান। [width]x[height] (অর্থাৎ 1920 x 1080) থাকতে হবে\"\nBasic.Settings.Video.CurrentlyActive=\"ভিডিও আউটপুট বর্তমানে সক্রিয় আছে। ভিডিও বিন্যাসনসমূহ পরিবর্তন করতে কোন ভয়েস থেকে চালু করুন।.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (সবচেয়ে দ্রুত, কিন্তু ঘোলাটে হলে স্কেল)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (Sharpened স্কেল, 16 নমুনা)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Sharpened স্কেল, 36 নমুনা)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"অঞ্চল (ওজনফল, ৪/৬/৯ নমুনা)\"\nBasic.Settings.Audio=\"অডিও\"\nBasic.Settings.Audio.SampleRate=\"নমুনা হার\"\nBasic.Settings.Audio.Channels=\"চ্যানেলসমূহ\"\nBasic.Settings.Audio.Meters=\"মিটার\"\nBasic.Settings.Audio.MeterDecayRate=\"ক্ষয় হার\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"দ্রুত\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"মাঝারি (টাইপ আই পিপিএম)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"ধীর (টাইপ II পিপিএম)\"\nBasic.Settings.Audio.PeakMeterType=\"শীর্ষ মিটারের প্রকার\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"নমুনা শীর্ষ\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"সত্য পিক (উচ্চতর সিপিইউ ব্যবহার)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"সতর্কতা: চারপাশের শব্দ অডিও সক্ষম হয়েছে।\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"চারপাশের শব্দ অডিও সক্ষম করবেন?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"আপনি কি আশেপাশের সাউন্ড অডিও সক্ষম করতে চান?\"\nBasic.Settings.Audio.Devices=\"সার্বজনীন শব্দ যন্ত্র\"\nBasic.Settings.Audio.DesktopDevice=\"ডেস্কটপ শব্দ\"\nBasic.Settings.Audio.DesktopDevice2=\"ডেস্কটপ শব্দ ২\"\nBasic.Settings.Audio.AuxDevice=\"মাইক/সহায়ক শব্দ\"\nBasic.Settings.Audio.AuxDevice2=\"মাইক/সহায়ক শব্দ ২\"\nBasic.Settings.Audio.AuxDevice3=\"মাইক/সহায়ক শব্দ ৩\"\nBasic.Settings.Audio.AuxDevice4=\"মাইক/সহায়ক শব্দ ৪\"\nBasic.Settings.Audio.EnablePushToMute=\"মৌন করার ধাক্কা সক্রিয় করা হবে\"\nBasic.Settings.Audio.PushToMuteDelay=\"মৌন করার ধাক্কা বিলম্ব\"\nBasic.Settings.Audio.EnablePushToTalk=\"পুশ করার কথা সক্রিয় করা হবে\"\nBasic.Settings.Audio.PushToTalkDelay=\"পুশ করার কথা বিলম্ব\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[ডিভাইস সংযুক্ত নয় অথবা বিদ্যমান নয়।]\"\nBasic.Settings.Audio.Disabled=\"নিষ্ক্রিয়\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"সহজাত\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"বিশেষায়িত\"\nBasic.Settings.Advanced=\"অ্যাডভান্সড\"\nBasic.Settings.Advanced.General.ProcessPriority=\"প্রসেস\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"বেশি\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"স্বাভাবিকের উপরে\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"সাধারণের নীচে\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"নিষ্ক্রিয়\"\nBasic.Settings.Advanced.Video.ColorFormat=\"রঙ বিন্যাস\"\nBasic.Settings.Advanced.Video.ColorSpace=\"রঙের স্থান\"\nBasic.Settings.Advanced.Video.ColorRange=\"রঙ পরিসীমা\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"সীমাবদ্ধ\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"পূর্ণ\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"নিরীক্ষণ যন্ত্র\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"পূর্ব-নির্ধারিত\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Windows অডিও ডাকিং অক্ষম করুন\"\nBasic.Settings.Advanced.StreamDelay=\"স্ট্রিম বিলম্ব\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"সময়কাল\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"বিরোধিতায় লিপ্ত পয়েন্ট (বাড়তে দেরি) সংরক্ষণের সময় পুনরায় সংযোগ করা হচ্ছে\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"আনুমানিক মেমোরি ব্যবহারঃ %1 এমবি\"\nBasic.Settings.Advanced.Network=\"নেটওয়ার্ক\"\nBasic.Settings.Advanced.Network.BindToIP=\"আইপির সাথে বাঁধুন\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"নেটওয়ার্ক অপ্টিমাইজেশান সক্ষম করুন\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"টিসিপি প্যাসিং সক্ষম করুন\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"ট্রান্সমিশনের হার নিয়ন্ত্রণ করে নেটওয়ার্কে আরটিএমপি আউটপুটকে এবং অন্যান্য সংবেদনশীল অ্যাপ্লিকেশনগুলিতে বন্ধুত্বপূর্ণ করার চেষ্টা করে।\\nএটি অস্থিতিশীল সংযোগগুলিতে ঝরে যাওয়া ফ্রেমের ঝুঁকি বাড়িয়ে তুলতে পারে।\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"হটকি ফোকাস আচরণ\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"হটকিগুলি কখনই অক্ষম করবেন না\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"মূল উইন্ডোটি ফোকাসে থাকার সময় হটকিগুলি অক্ষম করুন\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"মূল উইন্ডোটি ফোকাসে না থাকলে হটকিগুলি অক্ষম করুন\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(এমকেভি হিসাবে রেকর্ড)\"\nBasic.AdvAudio=\"উন্নত অডিও বৈশিষ্ট্য\"\nBasic.AdvAudio.ActiveOnly=\"কেবলমাত্র সক্রিয় উৎস\"\nBasic.AdvAudio.Name=\"নাম\"\nBasic.AdvAudio.Volume=\"আওয়াজের মাত্রা\"\nBasic.AdvAudio.VolumeSource=\"'%1' এর জন্য আওয়াজের মাত্রা\"\nBasic.AdvAudio.Mono=\"মনো\"\nBasic.AdvAudio.MonoSource=\"'%1' এর জন্য মনো ডাউনমিক্স\"\nBasic.AdvAudio.Balance=\"ভারসাম্য\"\nBasic.AdvAudio.BalanceSource=\"'%1' এর জন্য ভারসাম্য\"\nBasic.AdvAudio.SyncOffset=\"সিঙ্ক অফসেট\"\nBasic.AdvAudio.SyncOffsetSource=\"'%1' এর জন্য সিঙ্ক অফসেট\"\nBasic.AdvAudio.Monitoring=\"অডিও নিরীক্ষণ\"\nBasic.AdvAudio.Monitoring.None=\"নিরীক্ষণ বন্ধ\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"শুধুমাত্র নিরীক্ষণ (নিঃশব্দ আউটপুট)\"\nBasic.AdvAudio.Monitoring.Both=\"নিরীক্ষণ এবং আউটপুট\"\nBasic.AdvAudio.MonitoringSource=\"'%1' এর জন্য অডিও নিরীক্ষণ\"\nBasic.AdvAudio.AudioTracks=\"ট্র্যাক\"\nBasic.Settings.Hotkeys=\"হট-কী\"\nBasic.Settings.Hotkeys.Pair=\"%1 সাথে শেয়ার কী সমন্বয় কাজ হিসেবে মোড পাল্টায়\"\nBasic.Settings.Hotkeys.Filter=\"ছাঁকনি\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Hotkey দ্বারা ফিল্টার\"\nBasic.Hotkeys.SelectScene=\"ঘটনাস্থলে স্যুইচ করুন\"\nBasic.SystemTray.Show=\"প্রদর্শন করা হবে\"\nBasic.SystemTray.Hide=\"সরিয়ে রাখ\"\nBasic.SystemTray.Message.Reconnecting=\"বিচ্ছিন্ন হয়ে গেছে। পুনরায় সংযোগ করা হচ্ছে...\"\nHotkeys.Insert=\"প্রবেশ করান\"\nHotkeys.Delete=\"মুছে ফেলা\"\nHotkeys.Home=\"হোম\"\nHotkeys.End=\"সমাপ্ত\"\nHotkeys.PageUp=\"উপরের পাতা\"\nHotkeys.PageDown=\"নিচের পাতা\"\nHotkeys.NumLock=\"Num লক\"\nHotkeys.ScrollLock=\"স্ক্রল লক\"\nHotkeys.CapsLock=\"ক্যাপস লক\"\nHotkeys.Backspace=\"ব্যাকস্পেস\"\nHotkeys.Tab=\"ট্যাব\"\nHotkeys.Print=\"প্রিন্ট\"\nHotkeys.Pause=\"বিরাম\"\nHotkeys.Left=\"বামদিকে\"\nHotkeys.Right=\"ডানদিকে\"\nHotkeys.Up=\"উপরে\"\nHotkeys.Down=\"নিচের\"\nHotkeys.Windows=\"উইন্ডোজ\"\nHotkeys.Super=\"সুপার\"\nHotkeys.Menu=\"মেনু\"\nHotkeys.Space=\"জায়গা\"\nHotkeys.NumpadMultiply=\"Numpad দ্বিগুণ\"\nHotkeys.NumpadDivide=\"Numpad ভাগ\"\nHotkeys.NumpadAdd=\"Numpad যুক্ত করুন\"\nHotkeys.NumpadSubtract=\"Numpad বিয়োগ\"\nHotkeys.NumpadDecimal=\"Numpad দশমিক\"\nHotkeys.AppleKeypadNum=\"%1 (কী-প্যাড)\"\nHotkeys.AppleKeypadMultiply=\"* (কী-প্যাড)\"\nHotkeys.AppleKeypadDivide=\"/ (কী-প্যাড)\"\nHotkeys.AppleKeypadAdd=\"/ (কী-প্যাড)\"\nHotkeys.AppleKeypadSubtract=\"/ (কী-প্যাড)\"\nHotkeys.AppleKeypadDecimal=\". (কী-প্যাড)\"\nHotkeys.AppleKeypadEqual=\"/ (কী-প্যাড)\"\nHotkeys.MouseButton=\"মাউস %1\"\nHotkeys.Escape=\"প্রস্থান\"\nMute=\"নিঃশব্দ করুন\"\nUnmute=\"সবাক করুন\"\nPush-to-mute=\"মৌন করার ধাক্কা\"\nPush-to-talk=\"পুশ করার কথা\"\nSceneItemShow=\"'%1' প্রদর্শন করা হবে\"\nSceneItemHide=\"'%1' আড়াল করা হবে\"\nOutputWarnings.NoTracksSelected=\"আপনি অবশ্যই কমপক্ষে একটি ট্র্যাক নির্বাচন\"\nOutputWarnings.MP4Recording=\"সতর্কতা: এমপি৪/এমওভিতে সংরক্ষিত রেকর্ডিংগুলি যদি চূড়ান্ত করা না যায় তবে সেগুলো ফিরে পাওয়া সম্ভব নয় (যেমন, বিএসওডগুলি, বিদ্যুতের ক্ষয়ক্ষতির ফলে)। আপনি যদি একাধিক অডিও ট্র্যাক রেকর্ড করতে চান তবে এমকেভি ব্যবহারের কথা বিবেচনা করুন এবং রেকর্ডিংটি এমপি৪/এমওভিতে সমাপ্ত হওয়ার পরে রিমাক্স করুন (ফাইল → রিমাক্স রেকর্ডিংস)\"\nOutputWarnings.CannotPause=\"সতর্কতা: রেকর্ডিং এনকোডারটি \\\"(স্ট্রিম এনকোডার ব্যবহার করুন)\\\" তে সেট করা থাকলে রেকর্ডিংগুলি বিরতি দেওয়া যায় না\"\nFinalScene.Title=\"দৃশ্য মুছুন\"\nFinalScene.Text=\"কমপক্ষে একটি দৃশ্য হওয়া দরকার।\"\nNoSources.Title=\"কোনও উৎস নেই\"\nNoSources.Text=\"মনে হচ্ছে আপনি এখনও কোনও ভিডিও উৎস যোগ করেন নি, তাই আপনি কেবল একটি ফাঁকা স্ক্রিন আউটপুট করবেন। আপনি কি সত্যি এটা করতে চান?\"\nNoSources.Text.AddSource=\"আপনি যে কোনও সময় প্রধান উইন্ডোতে উৎস বাক্সের নীচে + আইকনটি ক্লিক করে উৎসসমূহ যুক্ত করতে পারেন।\"\nNoSources.Label=\"আপনার কোনও উৎস নেই।\\nনীচে + বোতামটি ক্লিক করুন,\\nবা একটি যোগ করতে এখানে ডান ক্লিক করুন।\"\nChangeBG=\"রং নির্বাচন করুন\"\nCustomColor=\"পছন্দসই রং\"\nBrowserSource.EnableHardwareAcceleration=\"ব্রাউজার উৎস হার্ডওয়্যার ত্বরণ সক্ষম করুন\"\nAbout=\"সম্পর্কে\"\nAbout.Info=\"OBS Studio একটি বিনামূলের এবং ওপেন সোর্স ভিডিও রেকর্ডিং এবং লাইভ স্ট্রিমিং সফ্টওয়্যার।\"\nAbout.Donate=\"অবদান রাখুন\"\nAbout.GetInvolved=\"সম্পৃক্ত হোন\"\nAbout.Authors=\"তৈরিকারক\"\nAbout.License=\"লাইসেন্স\"\nAbout.Contribute=\"OBS প্রকল্পকে সমর্থন করুন\"\nAddUrl.Title=\"ইউআরএল এর মাধ্যমে উৎস যুক্ত করুন\"\nAddUrl.Text=\"আপনি OBS এ একটি ইউআরএল টেনে নিয়েছেন। এটি স্বয়ংক্রিয়ভাবে লিঙ্কটি উৎস হিসাবে যুক্ত করবে। চালিয়ে যাবেন?\"\nAddUrl.Text.Url=\"ইউআরএল: %1\"\nPreviewTransition=\"স্থানান্তরের পূর্বরূপ\"\nImporter=\"দৃশ্য সংগ্রহ আমদানিকারক\"\nImporter.SelectCollection=\"একটি দৃশ্য সংগ্রহ নির্বাচন করুন\"\nImporter.Collection=\"দৃশ্য সংগ্রহ\"\nImporter.HelpText=\"OBS বা অন্যান্য সমর্থিত প্রোগ্রামগুলি থেকে সংগ্রহগুলি আমদানি করতে এই উইন্ডোতে ফাইল যুক্ত করুন।\"\nImporter.Path=\"সংগ্রহের পাথ\"\nImporter.Program=\"অ্যাপ্লিকেশন সনাক্ত করা হয়েছে\"\nImporter.AutomaticCollectionPrompt=\"দৃশ্য সংগ্রহের জন্য স্বয়ংক্রিয়ভাবে অনুসন্ধান করুন\"\nImporter.AutomaticCollectionText=\"OBS সমর্থিত তৃতীয় পক্ষের প্রোগ্রামগুলি থেকে স্বয়ংক্রিয়ভাবে আমদানিযোগ্য দৃশ্যের সংগ্রহ খুঁজে পেতে পারে।আপনি কি চান OBS আপনার জন্য স্বয়ংক্রিয়ভাবে সংগ্রহগুলি সন্ধান করুক?\\n\\nআপনি এটি পরে সেটিংস> সাধারণ> আমদানিকারক এ পরিবর্তন করতে পারেন।\"\nRestart=\"পুনর্সূচনা\"\nNeedsRestart=\"OBS Studio পুনরায় চালু করা দরকার। আপনি কি এখনই আবার চালু করতে চান?\"\nContextBar.NoSelectedSource=\"কোনও উৎস বাছাই করা হয়নি\"\nContextBar.MediaControls.PlayMedia=\"মিডিয়া চালান\"\nContextBar.MediaControls.PauseMedia=\"মিডিয়া বিরতি দিন\"\nContextBar.MediaControls.StopMedia=\"মিডিয়া থামান\"\nContextBar.MediaControls.RestartMedia=\"মিডিয়া পুনরায় চালু করুন\"\nContextBar.MediaControls.PlaylistNext=\"প্লেলিস্টে পরবর্তী\"\nContextBar.MediaControls.PlaylistPrevious=\"প্লেলিস্টে পূর্ববর্তী\"\nContextBar.MediaControls.BlindSeek=\"মিডিয়া সিক উইজেট\"\nYouTube.Auth.Ok=\"অনুমোদন সপূর্ণ।\\nএই পেজ টি বন্ধ করতে পারেন।\"\nYouTube.Auth.NoCode=\"অনুমোদন এর প্রক্রিয়া শেষ হয়নি।\"\nYouTube.Auth.NoChannels=\"বাছাই অ্যাকাউন্টে এ কোনো চ্যানেল পাওয়া যায়নি\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube ব্যবহারকারী অনুমোদন\"\nYouTube.AuthError.Text=\"চ্যানেল এর তথ্য পাওয়া যায়নি: %1.\"\nYouTube.Actions.CreateNewEvent=\"নতুন সম্প্রচার তৈরি করুন\"\nYouTube.Actions.ChooseEvent=\"বিদ্যমান সম্প্রচার নির্বাচন করুন\"\nYouTube.Actions.Title=\"শিরোনাম*\"\nYouTube.Actions.MyBroadcast=\"আমার সম্প্রচার\"\nYouTube.Actions.Description=\"বর্ণনা\"\nYouTube.Actions.Privacy=\"গোপনীয়তা*\"\nYouTube.Actions.Privacy.Private=\"ব্যক্তিগত\"\nYouTube.Actions.Privacy.Public=\"সর্বজনীন\"\nYouTube.Actions.Privacy.Unlisted=\"তালিকামুক্ত\"\nYouTube.Actions.Category=\"তালিকা\"\nYouTube.Actions.Thumbnail=\"থাম্বনেইল\"\nYouTube.Actions.Thumbnail.SelectFile=\"ফাইল নির্বাচন করুন...\"\nYouTube.Actions.Thumbnail.ClearFile=\"পরিষ্কার\"\nYouTube.Actions.MadeForKids=\"এই ভিডিও কি শিশুদের জন্য তৈরি?*\"\nYouTube.Actions.MadeForKids.Yes=\"হ্যা, এটি শিশুদের জন্যে তৈরি\"\nYouTube.Actions.MadeForKids.No=\"না, এটি শিশু দের জন্যে তৈরি নয়\"\nYouTube.Actions.Latency=\"সুপ্তাবস্থা(ল্যাটেন্সি)\"\nYouTube.Actions.Latency.Normal=\"সাধারণ\"\nYouTube.Actions.Latency.Low=\"কম\"\nYouTube.Actions.Latency.UltraLow=\"অনেক বেশি কম\"\nYouTube.Actions.Stream=\"সম্প্রচার\"\nYouTube.Actions.Notify.CreatingBroadcast=\"সরাসরি সম্প্রচার তৈরি হচ্ছে, অপেক্ষা করুন।\"\n"
  },
  {
    "path": "frontend/data/locale/ca-ES.ini",
    "content": "Language=\"Català\"\nOK=\"D'acord\"\nApply=\"Aplica\"\nCancel=\"Cancel·la\"\nClose=\"Tanca\"\nSave=\"Desa\"\nDiscard=\"Descarta\"\nDisable=\"Inhabilita\"\nYes=\"Sí\"\nAdd=\"Afegeix\"\nRemove=\"Suprimeix\"\nRename=\"Reanomena\"\nInteract=\"Interactua\"\nFilters=\"Filtres\"\nProperties=\"Propietats\"\nMoveUp=\"Mou amunt\"\nMoveDown=\"Mou avall\"\nSettings=\"Configuració\"\nDisplay=\"Pantalla\"\nName=\"Nom\"\nExit=\"Surt\"\nMixer=\"Mesclador d'àudio\"\nBrowse=\"Navega\"\nStereo=\"Estèreo\"\nDroppedFrames=\"Quadres perduts %1 (%2%)\"\nProjector.Open.Program=\"Obre el projector de programa\"\nProjector.Open.Preview=\"Obre el projector de vista prèvia\"\nProjector.Open.Scene=\"Obre el projector d'escena\"\nProjector.Open.Source=\"Obre el projector de font\"\nProjector.Open.Multiview=\"Obre la vista múltiple\"\nProjector.Display=\"Pantalla: %1\"\nProjector.Window=\"Finestra nova\"\nProjector.Title.Scene=\"Escena: %1\"\nProjector.Title.Source=\"Font: %1\"\nProjector.Title.Multiview=\"Vista múltiple\"\nProjector.ResizeWindowToContent=\"Ajusta la finestra al contingut\"\nClear=\"Neteja\"\nRevert=\"Reverteix\"\nShow=\"Mostra\"\nHide=\"Amaga\"\nUnhideAll=\"Mostra-ho tot\"\nUntitled=\"Sense títol\"\nNew=\"Nova\"\nDuplicate=\"Duplica\"\nEnable=\"Habilita\"\nDisableOSXVSync=\"Inhabilita V-Sync en macOS\"\nResetOSXVSyncOnExit=\"Reinicia V-Sync de macOS a la sortida\"\nHighResourceUsage=\"Sobrecàrrega en codificar! Canvieu a una configuració de vídeo inferior o bé utilitzeu un codificador més ràpid.\"\nTransition=\"Transició\"\nQuickTransitions=\"Transicions ràpides\"\nFadeToBlack=\"Fos a negre\"\nLeft=\"Esquerra\"\nRight=\"Dreta\"\nTop=\"A dalt\"\nBottom=\"Avall\"\nReset=\"Reinicia\"\nHours=\"Hores\"\nMinutes=\"Minuts\"\nSeconds=\"Segons\"\nDeprecated=\"Obsolet\"\nReplayBuffer=\"Memòria intermèdia de reproducció\"\nImport=\"Importa\"\nExport=\"Exporta\"\nCopy=\"Copia\"\nPaste=\"Enganxa\"\nPasteReference=\"Enganxa (referència)\"\nPasteDuplicate=\"Enganxa (duplicat)\"\nRemuxRecordings=\"Conversions enregistrades\"\nNext=\"Endavant\"\nBack=\"Enrere\"\nDefaults=\"Per defecte\"\nRestoreDefaults=\"Per defecte\"\nHideMixer=\"Amaga al mesclador\"\nTransitionOverride=\"Anul·lació de la transició\"\nShowTransition=\"Mostra la transició\"\nHideTransition=\"Amaga la transició\"\nNone=\"Cap\"\nStudioMode.Preview=\"Vista prèvia\"\nStudioMode.Program=\"Programa\"\nStudioMode.PreviewSceneName=\"Previsualització: %1\"\nStudioMode.ProgramSceneName=\"Programa: %1\"\nShowInMultiview=\"Mostra en vista múltiple\"\nVerticalLayout=\"Disposició vertical\"\nGroup=\"Grup\"\nDoNotShowAgain=\"No tornis a mostrar\"\nDefault=\"(Per defecte)\"\nCalculating=\"S'està calculant...\"\nFullscreen=\"Pantalla completa\"\nWindowed=\"En finestra\"\nRefreshBrowser=\"Actualitza\"\nAspectRatio=\"Relació d'aspecte <b>%1:%2</b>\"\nLockVolume=\"Bloca el volum\"\nLogViewer=\"Visor d'informes\"\nShowOnStartup=\"Mostra a l'inici\"\nOpenFile=\"Obre el fitxer\"\nAddScene=\"Afegeix una escena\"\nAddSource=\"Afegeix una font\"\nRemoveScene=\"Elimina l'escena seleccionada\"\nRemoveSource=\"Elimina les fonts seleccionades\"\nMoveSceneUp=\"Mou l'escena cap amunt\"\nMoveSceneDown=\"Mou l'escena cap avall\"\nMoveSourceUp=\"Mou les fonts cap amunt\"\nMoveSourceDown=\"Mou les fonts cap avall\"\nSourceProperties=\"Propietats de codi obert\"\nSourceFilters=\"Filtres de codi obert\"\nMixerToolbarMenu=\"Menú del mesclador d'àudio\"\nSceneFilters=\"Obre els filtres d'escena\"\nList=\"Llista\"\nGrid=\"Graella\"\nAutomatic=\"Automàtic\"\nComingSoon=\"Pròximament\"\nPluginsFailedToLoad.Title=\"Error en iniciar complements\"\nPluginsFailedToLoad.Text=\"S'ha produït un error en iniciar aquests complements de l'OBS:\\n\\n%1\\nActualitzeu o suprimiu aquests complements.\"\nAlreadyRunning.Title=\"L'OBS ja s'està executant\"\nAlreadyRunning.Text=\"L'OBS ja s'està executant! A no ser que vulgueu fer això, tanqueu totes les finestres de l'OBS abans d'intentar iniciar una nova. Si teniu configurat OBS perquè es minimitzi a la barra de tasques, proveu a veure si segueix executant-se aquí.\"\nAlreadyRunning.LaunchAnyway=\"Executa de totes maneres\"\nCrashHandling.Dialog.Title=\"S'ha detectat un error a l'OBS Studio\"\nCrashHandling.Labels.Text=\"L'OBS Studio no s'ha tancat correctament.\\n\\nExecuta en mode segur (complements de terceres, scripts i WebSockets desactivats)?\"\nCrashHandling.Labels.PrivacyNotice=\"També podeu optar per carregar automàticament l'informe d'error més recent a l'OBSProject.<br /><br />Llegiu la <a href='https://obsproject.com/privacy-policy'>Política de privadesa</a> abans de carregar cap fitxer i pareu especial atenció a les parts relacionades amb la càrrega de fitxers.\"\nCrashHandling.Checkbox.SendReport=\"He llegit la política de privadesa i dono el meu consentiment per a la càrrega.\"\nCrashHandling.Buttons.LaunchSafe=\"Inicia en mode segur\"\nCrashHandling.Buttons.LaunchNormal=\"Inicia en mode normal\\n\"\nCrashHandling.Errors.UploadJSONError=\"S'ha produït un error en intentar carregar el registre d'errors més recent. Torneu-ho a provar més tard.\"\nCrashHandling.Errors.Title=\"Error en carregar el registre de fallades\\n\"\nSafeMode.Restart=\"Voleu reiniciar l'OBS en mode segur (es desactivaran els complements de tercers, scripts i WebSockets)?\"\nSafeMode.RestartNormal=\"Voleu reiniciar l'OBS en mode normal?\"\nChromeOS.Title=\"Plataforma no compatible\"\nChromeOS.Text=\"Sembla que l'OBS s'està executant dins d'un contenidor del ChromeOS. Aquesta plataforma no és compatible.\"\nWine.Title=\"Wine detectat\"\nWine.Text=\"L'execució d'OBS a Wine no és compatible i moltes funcions, com ara la captura o les fonts del dispositiu no funcionaran o només tindran una capacitat limitada.<br><br>Es recomana executar una versió nativa d'OBS, per exemple, <a href='https://flathub.org/apps/details/com.obsproject.Studio'>la nostra versió Flatpak</a> o els paquets del vostre sistema operatiu.\"\nDockCloseWarning.Title=\"Tancament de la finestra acoblada\"\nDockCloseWarning.Text=\"Heu tancat una finestra acoblada. Si voleu que es mostri novament, utilitzeu l'opció Acoblador de la barra de menús.\"\nExtraBrowsers=\"Panells de navegador personalitzats\"\nExtraBrowsers.Info=\"Afegiu contingut inserint un nom i l'URL i feu clic al botó Aplica o Tanca per obrir. Podeu afegir o suprimir contingut en qualsevol moment.\"\nExtraBrowsers.DockName=\"Nom del panell\"\nAuth.Authing.Title=\"S'està autenticant...\"\nAuth.Authing.Text=\"S'està autenticant amb %1, espereu...\"\nAuth.AuthFailure.Title=\"Error d'autenticació\"\nAuth.AuthFailure.Text=\"No s'ha pogut autenticar amb %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Es necessita autenticació\"\nAuth.InvalidScope.Text=\"Han canviat els requisits d'autenticació per %1. Potser algunes funcions no estaran disponibles.\"\nAuth.LoadingChannel.Title=\"S'està carregant la informació del canal...\"\nAuth.LoadingChannel.Text=\"S'està carregant la informació del canal a %1, espereu...\"\nAuth.LoadingChannel.Error=\"No s'ha pogut obtenir la informació del canal.\"\nAuth.ChannelFailure.Title=\"S'ha produït un error en carregar el canal\"\nAuth.ChannelFailure.Text=\"No s'ha pogut carregar la informació del canal per a %1\\n\\n%2: %3\"\nAuth.Chat=\"Xat\"\nAuth.StreamInfo=\"Informació de la transmissió\"\nTwitchAuth.Stats=\"Estat del Twitch\"\nTwitchAuth.Feed=\"Activitat del mur del Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"No s'ha pogut consultar la clau de la transmissió\"\nTwitchAuth.TwoFactorFail.Text=\"L'OBS no ha pogut connectar amb el vostre compte del Twitch. Assegureu-vos que l'autenticació en 2 passos està habilitada a les <a href='https://www.twitch.tv/settings/security'>opcions de seguretat del Twitch</a>, atès que és un requeriment per retransmetre.\"\nRestreamAuth.Channels=\"Canals al Restream\"\nCopy.Filters=\"Copia els filtres\"\nPaste.Filters=\"Enganxa els filtres\"\nBrowserPanelInit.Title=\"S'està inicialitzant el navegador...\"\nBrowserPanelInit.Text=\"S'està inicialitzant el navegador, espereu...\"\nBandwidthTest.Region=\"Regió\"\nBandwidthTest.Region.US=\"Estats Units\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Àsia\"\nBandwidthTest.Region.Other=\"Altre\"\nBasic.AutoConfig=\"Assistent de configuració automàtica\"\nBasic.AutoConfig.ApplySettings=\"Aplica la configuració\"\nBasic.AutoConfig.StartPage=\"Informació sobre l'ús\"\nBasic.AutoConfig.StartPage.SubTitle=\"Especifiqueu perquè vol fer servir el programa\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimitza per a transmissions, l'enregistrament és secundari\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimitza només per a l'enregistrament, no faré cap transmissió\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Utilitza només la càmera virtual\"\nBasic.AutoConfig.VideoPage=\"Configuració de vídeo\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Especifiqueu la configuració de vídeo que voleu utilitzar\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Utilitza l'actual (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Pantalla %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Utilitza l'actual (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 o 30, però utilitza 60 quan sigui possible\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 o 30, però utilitza l'alta resolució\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"La resolució del llenç (base) no és necessàriament la mateixa que la resolució de la transmissió o enregistrament. La resolució actual pot ser reduïda del llenç per reduir l'ús dels recursos o de la tassa de bits.\"\nBasic.AutoConfig.StreamPage=\"Informació de la transmissió\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Introduïu informació sobre la seva transmissió\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Connexió al compte (recomanat)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Desconnecta el compte\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Voleu desconnectar el compte?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Aquest canvi s'aplicarà immediatament. Segur que voleu desconnectar el compte?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Obté una clau\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Més informació\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Utilitza una clau de transmissió\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Utilitza una clau de retransmissió (avançat)\"\nBasic.AutoConfig.StreamPage.Service=\"Servei\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Mostra-ho tot...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Personalitzat...\"\nBasic.AutoConfig.StreamPage.Server=\"Servidor\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Clau de la transmissió\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: introduïu la contrasenya de xifratge.\\nRTMP: introduïu la clau proporcionada pel servei.\\nSRT: introduïu l'identificador de l'emissió si el servei n'utilitza un.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Clau del codificador\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Testimoni d'autorització\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Compte connectat\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Estima la taxa de bits amb una prova d'amplada de banda (pot trigar uns minuts)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Prioritza la codificació per maquinari\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"La codificació per maquinari elimina la majoria de l'ús de la CPU, però pot requerir una taxa de bits superior per obtenir el mateix nivell de qualitat.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Advertència de la transmissió\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"La prova d'amplada de banda està a punt de transmetre dades de vídeo aleatòries sense àudio al vostre canal. Es recomana quan sigui possible desactivar temporalment que es desin els vídeos de les transmissions i fer la transmissió privada després que la prova hagi finalitzat. Voleu continuar?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Prova %1\"\nBasic.AutoConfig.TestPage=\"Resultat final\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"El programa ara està executant un conjunt de proves per estimar la configuració òptima\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Prova finalitzada\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"S'està executant la prova d'amplada de banda, això pot trigar uns minuts...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"No s'ha trobat cap sortida per al protocol d'aquest servei\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"S'està connectant a: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"No s'ha pogut connectar a cap servidor. Reviseu la connexió a Internet i torneu-ho a provar.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"S'està provant l'amplada de banda per: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"S'està provant codificador de la transmissió, això pot trigar un minut...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"S'està provant codificador de l'enregistrament, això pot trigar un minut...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"No s'ha pogut iniciar el codificador\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"S'està provant %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Codificador de la transmissió\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Codificador de l'enregistrament\"\nBasic.AutoConfig.TestPage.Result.Header=\"El programa ha determinat que aquesta configuració és l'òptima per a aquest equip:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Per a utilitzar aquesta configuració, feu clic a Aplica la configuració. Per a tornar a configurar l'assistent, feu clic a Enrere. Per configurar els paràmetres personalment, feu clic a Cancel·la i obriu la configuració.\"\nBasic.AutoConfig.Info=\"L'assistent de configuració automàtica determinarà la millor configuració segons les especificacions del vostre ordinador i la velocitat d'Internet.\"\nBasic.AutoConfig.RunAnytime=\"Podeu iniciar-ho en qualsevol moment des del menú Eines.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Resolució de transmissió (escalada)\"\nBasic.Stats=\"Estadístiques\"\nBasic.Stats.CPUUsage=\"Ús de CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Espai al disc disponible\"\nBasic.Stats.MemoryUsage=\"Ús de la memòria\"\nBasic.Stats.AverageTimeToRender=\"Temps de mitjana per processar un fotograma\"\nBasic.Stats.SkippedFrames=\"Fotogrames omesos per retard de processament\"\nBasic.Stats.MissedFrames=\"Fotogrames perduts per retard de processament\"\nBasic.Stats.Output.Stream=\"Transmissió\"\nBasic.Stats.Output.Recording=\"Enregistrament\"\nBasic.Stats.Status=\"Estat\"\nBasic.Stats.Status.Recording=\"S'està enregistrant\"\nBasic.Stats.Status.Live=\"EN DIRECTE\"\nBasic.Stats.Status.Reconnecting=\"S'està reconnectant\"\nBasic.Stats.Status.Inactive=\"Inactiva\"\nBasic.Stats.Status.Active=\"Activa\"\nBasic.Stats.DroppedFrames=\"Fotogrames perduts (xarxa)\"\nBasic.Stats.MegabytesSent=\"Sortida de dades total\"\nBasic.Stats.Bitrate=\"Taxa de bits\"\nBasic.Stats.DiskFullIn=\"Disc ple en (aprox.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 Hores, %2 Minuts\"\nBasic.Stats.ResetStats=\"Reinicia les estadístiques\"\nResetUIWarning.Title=\"Esteu segur que voleu reiniciar la interfície d'usuari?\"\nResetUIWarning.Text=\"En reiniciar la interfície d'usuari s'amagaran els acobladors addicionals. Haureu d'habilitar-los des del menú Acoblador si voleu que siguin visibles novament.\\n\\nSegur que voleu reiniciar la interfície d'usuari?\"\nUpdater.Title=\"Actualització disponible\"\nUpdater.Text=\"Hi ha una actualització nova disponible:\"\nUpdater.UpdateNow=\"Actualitza ara\"\nUpdater.RemindMeLater=\"Recorda-m'ho més tard\"\nUpdater.Skip=\"Omet la versió\"\nUpdater.NoUpdatesAvailable.Title=\"No hi ha actualitzacions disponibles\"\nUpdater.NoUpdatesAvailable.Text=\"Actualment no hi ha cap actualització disponible\"\nUpdater.BranchNotFound.Title=\"S'ha eliminat el canal d'actualització\"\nUpdater.BranchNotFound.Text=\"El canal d'actualització seleccionat ja no està disponible, l'OBS s'ha restablert al valor per defecte.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Verificació d'integritat no disponible\"\nUpdater.RepairButUpdatesAvailable.Text=\"La verificació d'integritat del fitxer és disponible només a la darrera versió de l'OBS. Utilitzeu el menú «Ajuda» → «Comprova si hi ha cap actualització» per actualitzar a la darrera versió.\"\nUpdater.RepairConfirm.Title=\"Confirmació de verificació d'integritat\"\nUpdater.RepairConfirm.Text=\"En iniciar la verificació d'integritat, s'analitzarà la vostra instal·lació de l'OBS i es baixaran i restauraran els components que s'hagin trobat trencats/corruptes. Això trigarà una estona.\\n\\nVoleu continuar?\"\nUpdater.FailedToLaunch=\"Ha fallat en iniciar l'actualitzador\"\nQuickTransitions.SwapScenes=\"Canvia les escenes de previsualització/programa després de la transició\"\nQuickTransitions.SwapScenesTT=\"Canvia la vista prèvia i les escenes del programa després de la transició (si encara existeix l'escena original del programa).\\nAixò no desfarà cap canvi que s'hagi fet a l'escena original del programa.\"\nQuickTransitions.DuplicateScene=\"Duplica l'escena\"\nQuickTransitions.DuplicateSceneTT=\"En editar la mateixa escena, permet editar la transformació/visibilitat de les fonts sense modificar la sortida del programa.\\nPer editar les propietats de les fonts sense modificar la sortida del programa, activeu «Fonts duplicades».\\nSi canvieu aquest valor, es restablirà l'escena del programa actual (si encara existeix).\"\nQuickTransitions.EditProperties=\"Duplica les fonts\"\nQuickTransitions.EditPropertiesTT=\"En editar la mateixa escena, permet editar les propietats de les fonts sense modificar la sortida del programa.\\nAixò només es pot utilitzar si l'opció «Escena duplicada» està activada.\\nAlgunes fonts (com ara la captura o les fonts multimèdia) no ho admeten i no es poden editar per separat.\\nSi canvieu aquest valor, es restablirà l'escena del programa actual (si encara existeix).\\n\\nAdvertència: Com les fonts es duplicaran, és possible que es requereixin recursos addicionals del sistema o de vídeo.\"\nQuickTransitions.HotkeyName=\"Transició Ràpida: %1\"\nBasic.AddTransition=\"Afegir transició configurable\"\nBasic.RemoveTransition=\"Eliminar transició configurable\"\nBasic.TransitionProperties=\"Propietats de la transició\"\nBasic.SceneTransitions=\"Transicions d'escena\"\nBasic.TransitionDuration=\"Duració\"\nBasic.TogglePreviewProgramMode=\"Mode estudi\"\nBasic.EnablePreviewProgramMode=\"Habilita el Mode estudi\"\nBasic.DisablePreviewProgramMode=\"Deshabilita el Mode estudi\"\nUndo.Undo=\"Desfés\"\nUndo.Redo=\"Refés\"\nUndo.Add=\"Afegeix «%1»\"\nUndo.Delete=\"Suprimeix «%1»\"\nUndo.Rename=\"Reanomena «%1»\"\nUndo.SceneCollection.Switch=\"Canvia a «%1»\"\nUndo.Item.Undo=\"Desfés %1\"\nUndo.Item.Redo=\"Refés %1\"\nUndo.Sources.Multi=\"Suprimeix %1 fonts\"\nUndo.Filters=\"Filtra els canvis a «%1»\"\nUndo.Filters.Paste.Single=\"Enganxa el filtre «%1» a «%2»\"\nUndo.Filters.Paste.Multiple=\"Copia els filtres de «%1» a «%2»\"\nUndo.Transform=\"Transforma les fonts a «%1»\"\nUndo.Transform.Paste=\"Enganxa la transformació a «%1»\"\nUndo.Transform.Rotate=\"Rotació a «%1»\"\nUndo.Transform.Reset=\"Restableix la transformació a «%1»\"\nUndo.Transform.HFlip=\"Gira horitzontalment a «%1»\"\nUndo.Transform.VFlip=\"Gira verticalment a «%1»\"\nUndo.Transform.FitToScreen=\"Ajusta a la pantalla a «%1»\"\nUndo.Transform.StretchToScreen=\"Estira a la pantalla a «%1»\"\nUndo.Transform.Center=\"Centra a la pantalla a «%1»\"\nUndo.Transform.VCenter=\"Centre vertical a la pantalla a «%1»\"\nUndo.Transform.HCenter=\"Centre horitzontal a la pantalla a «%1»\"\nUndo.Volume.Change=\"Canvi de volum a «%1»\"\nUndo.Volume.Mute=\"Silencia «%1»\"\nUndo.Volume.Unmute=\"No silenciïs «%1»\"\nUndo.Balance.Change=\"Canvi de balanç d'àudio a «%1»\"\nUndo.SyncOffset.Change=\"Canvi de compensació de sincronització d'àudio a «%1»\"\nUndo.MonitoringType.Change=\"Canvia el monitoratge d'àudio a «%1»\"\nUndo.Mixers.Change=\"Canvia els mescladors d'àudio a «%1»\"\nUndo.ForceMono.On=\"Activa Força mono a «%1»\"\nUndo.ForceMono.Off=\"Desactiva Força mono a «%1»\"\nUndo.Properties=\"Canvi de propietat a «%1»\"\nUndo.Scene.Duplicate=\"Escena duplicada «%1»\"\nUndo.ShowTransition=\"Mostra la transició a «%1»\"\nUndo.HideTransition=\"Amaga la transició a «%1»\"\nUndo.ShowSceneItem=\"Mostra «%1» a «%2»\"\nUndo.HideSceneItem=\"Oculta «%1» a «%2»\"\nUndo.ReorderSources=\"Reordena les fonts a «%1»\"\nUndo.MoveUp=\"Mou «%1» amunt de «%2»\"\nUndo.MoveDown=\"Mou «%1» avall de «%2»\"\nUndo.MoveToTop=\"Mou «%1» a la part superior de «%2»\"\nUndo.MoveToBottom=\"Mou «%1» a la part inferior «%2»\"\nUndo.PasteSource=\"Enganxa les fonts a «%1»\"\nUndo.PasteSourceRef=\"Enganxa la referència de les fonts a «%1»\"\nUndo.GroupItems=\"Agrupa els elements a «%1»\"\nTransitionNameDlg.Text=\"Si us plau, introdueixi el nom de la transició\"\nTransitionNameDlg.Title=\"Nom de la transició\"\nTitleBar.SafeMode=\"MODE SEGUR\"\nTitleBar.PortableMode=\"Mode portable\"\nTitleBar.Profile=\"Perfil\"\nTitleBar.Scenes=\"Escenes\"\nNameExists.Title=\"Aquest nom ja existeix\"\nNameExists.Text=\"Aquest nom ja és en ús.\"\nNoNameEntered.Title=\"Introduïu un nom vàlid\"\nNoNameEntered.Text=\"No podeu usar noms buits.\"\nConfirmStart.Title=\"Inicia la transmissió?\"\nConfirmStart.Text=\"Està segur que desitja iniciar la transmissió?\"\nConfirmStop.Title=\"Atura la transmissió?\"\nConfirmStop.Text=\"Està segur que desitja aturar la transmissió?\"\nConfirmStopRecord.Title=\"Voleu aturar l'enregistrament?\"\nConfirmStopRecord.Text=\"Segur que voleu aturar l'enregistrament?\"\nConfirmBWTest.Title=\"Test d'amplada de banda\"\nConfirmBWTest.Text=\"Heu configurat l'OBS en mode Test d'amplada de banda. Aquest mode us permet analitzar la connexió sense cap retransmissió en directe. Una vegada finalitzada l'anàlisi, inhabiliteu aquest mode perquè el públic pugui veure la retransmissió.\\n\\nVoleu continuar?\"\nConfirmExit.Title=\"Sortides actives\"\nConfirmExit.Text=\"L'OBS encara està actiu. Totes les transmissions/gravacions es tancaran.\"\nConfirmRemove.Title=\"Confirmeu la supressió\"\nConfirmRemove.Text=\"Segur que voleu suprimir '%1'?\"\nConfirmRemove.TextMultiple=\"¿Segur que vols esborrar %1 elements?\"\nConfirmReset.Title=\"Restabliment de les propietats\"\nConfirmReset.Text=\"Esteu segur que voleu restablir les propietats actuals als valors per defecte?\"\nOutput.StartStreamFailed=\"No s'ha pogut iniciar la transmissió\"\nOutput.StartRecordingFailed=\"No s'ha pogut iniciar la gravació\"\nOutput.StartReplayFailed=\"No s'ha pogut iniciar la memòria intermèdia de reproducció\"\nOutput.StartVirtualCamFailed=\"Ha fallat en iniciar la càmera virtual\"\nOutput.StartFailedGeneric=\"Error en iniciar la sortida. Comproveu el registre per més detalls.\\n\\nAvís: Si utilitzeu els codificadors NVENC o AMD, assegureu-vos que els controladors de vídeo estan actualitzats.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"No es poden desar les repeticions mentre estigui en pausa\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Advertència: No es poden desar les repeticions mentre la gravació estigui en pausa.\"\nOutput.ConnectFail.Title=\"Error en connectar\"\nOutput.ConnectFail.BadPath=\"Ruta o adreça URL no vàlida. Si us plau, comproveu la configuració per confirmar que són vàlids.\"\nOutput.ConnectFail.ConnectFailed=\"No ha pogut connectar al servidor\"\nOutput.ConnectFail.InvalidStream=\"No pot accedir a la clau del canal especificat o de la transmissió, per favor comprovar dues vegades la seva clau de transmissió. Si és correcte, pot haver-hi un problema connectant-se al servidor.\"\nOutput.ConnectFail.HdrDisabled=\"La sortida HDR està actualment desactivada per a aquesta sortida.\"\nOutput.ConnectFail.Error=\"S'ha produït un error inesperat quan s'intentava connectar amb el servidor. Més informació al fitxer de registre.\"\nOutput.ConnectFail.Disconnected=\"Desconnectat del servidor.\"\nOutput.StreamEncodeError.Title=\"Error de codificació\"\nOutput.StreamEncodeError.Msg=\"S'ha produït un error del codificador durant la transmissió.\"\nOutput.StreamEncodeError.Msg.LastError=\"S'ha produït un error del codificador durant l'emissió:<br><br>%1\"\nOutput.RecordFail.Title=\"Error en iniciar la gravació\"\nOutput.RecordFail.Unsupported=\"El format de sortida és o no compatible o no admet més d'una pista d'àudio. Comproveu la configuració i torneu-ho a provar.\"\nOutput.RecordNoSpace.Title=\"Espai de disc insuficient\"\nOutput.RecordNoSpace.Msg=\"No hi ha prou espai de disc per continuar la gravació.\"\nOutput.RecordError.Title=\"Error en l'enregistrament\"\nOutput.RecordError.Msg=\"S'ha produït un error desconegut durant l'enregistrament.\"\nOutput.RecordError.EncodeErrorMsg=\"S'ha produït un error del codificador durant la gravació.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"S'ha produït un error del codificador durant la gravació:<br><br>%1\"\nOutput.BadPath.Title=\"Ruta del fitxer no vàlida\"\nOutput.BadPath.Text=\"No s'ha pogut obrir la ruta d'enregistrament configurada. Comproveu la ruta d'enregistrament a Configuració → Sortida → Enregistrament.\"\nOutput.NoBroadcast.Title=\"Cap emissió configurada\"\nOutput.NoBroadcast.Text=\"Heu de configurar primer una emissió abans de poder emetre.\"\nOutput.BroadcastStartFailed=\"S'ha produït un error en iniciar la retransmissió\"\nOutput.BroadcastStopFailed=\"S'ha produït un error en aturar la retransmissió\"\nLogUploadDialog.Title=\"Puja el fitxer de registre de l'OBS Studio\\n\"\nLogUploadDialog.Labels.PrivacyNotice=\"Llegiu la <a href='https://obsproject.com/privacy-policy'>Política de privadesa</a> i la seva secció sobre la càrrega de fitxers abans de carregar-ne cap.\"\nLogUploadDialog.Labels.Progress=\"Pujada del registre en curs. Espereu...\"\nLogUploadDialog.Labels.Description.AppLog=\"S'ha penjat el fitxer de registre. Ara podeu compartir l'URL amb finalitats de depuració o assistència.\"\nLogUploadDialog.Labels.Description.CrashLog=\"S'ha penjat l'informe d'error. Ara podeu compartir l'URL amb finalitats de depuració.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Penja\"\nLogUploadDialog.Buttons.CopyURL=\"Copia l'URL del registre\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analitza el fitxer de registre\"\nLogUploadDialog.Buttons.RetryButton=\"Reintenta\"\nLogUploadDialog.Errors.Template=\"S'ha produït un error en intentar pujar el fitxer:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"No s'ha trobat cap fitxer per pujar o el fitxer estava buit.\"\nRemux.SourceFile=\"Enregistrament OBS\"\nRemux.TargetFile=\"Fitxer de destinació\"\nRemux.Remux=\"Converteix\"\nRemux.Stop=\"Atura la conversió\"\nRemux.ClearFinished=\"Neteja els elements finalitzats\"\nRemux.ClearAll=\"Neteja-ho tot\"\nRemux.OBSRecording=\"Enregistrament OBS\"\nRemux.FinishedTitle=\"Conversió finalitzada\"\nRemux.Finished=\"Gravació convertida\"\nRemux.FinishedError=\"Gravació convertida, però l'arxiu pot ser incomplet\"\nRemux.SelectRecording=\"Seleccioneu l'enregistrament OBS...\"\nRemux.SelectTarget=\"Seleccioneu el fitxer de destinació...\"\nRemux.FileExistsTitle=\"Destinació existent\"\nRemux.FileExists=\"Aquests elements ja existeixen a la destinació. Voleu reemplaçar-los?\"\nRemux.ExitUnfinishedTitle=\"Conversió en curs\"\nRemux.ExitUnfinished=\"La conversió no ha finalitzat, parar ara pot fer l'arxiu de destinació inservible.\\n Esteu segur que voleu aturar la conversió?\"\nRemux.HelpText=\"Arrossegueu fitxers en aquesta finestra per convertir-los o seleccioneu una cel·la buida a «Enregistrament OBS» per cercar un fitxer.\"\nRemux.NoFilesAddedTitle=\"No s'ha afegit cap fitxer de conversió\"\nRemux.NoFilesAdded=\"No s'afegeix cap fitxer per convertir. Deixeu anar una carpeta que contingui un o més fitxers de vídeo.\"\nMissingFiles=\"Fitxers que falten\"\nMissingFiles.MissingFile=\"Fitxer que falta\"\nMissingFiles.NewFile=\"Fitxer nou\"\nMissingFiles.HelpText=\"Falten alguns fitxers des de la darrera vegada que vau utilitzar l'OBS.\"\nMissingFiles.Clear=\"<buit>\"\nMissingFiles.NumFound=\"S'ha trobat %1 de %2\"\nMissingFiles.Search=\"Cerca la carpeta...\"\nMissingFiles.SelectFile=\"Selecciona el fitxer...\"\nMissingFiles.SelectDir=\"Seleccioneu la carpeta on voleu cercar\"\nMissingFiles.State=\"Estat\"\nMissingFiles.Missing=\"Falta\"\nMissingFiles.Replaced=\"Reemplaçat\"\nMissingFiles.Cleared=\"Buit\"\nMissingFiles.Found=\"Trobat\"\nMissingFiles.AutoSearch=\"S'han trobat coincidències de fitxers addicionals\"\nMissingFiles.AutoSearchText=\"L'OBS ha trobat coincidències addicionals per als fitxers que falten en aquesta carpeta. Voleu afegir-los?\"\nMissingFiles.NoMissing.Title=\"Comprovació de fitxers que falten\"\nMissingFiles.NoMissing.Text=\"Sembla que no falten fitxers.\"\nMacPermissions.MenuAction=\"Revisa els permisos de l'aplicació...\"\nMacPermissions.Title=\"Revisió de permisos\"\nMacPermissions.Description=\"L'OBS Studio requereix el vostre consentiment per utilitzar determinades característiques. Tot i que no són estrictament necessàries per al funcionament de l'aplicació, sí que es recomana habilitar els permisos. També podeu habilitar-ho més tard.\"\nMacPermissions.Description.OpenDialog=\"Podeu accedir a aquesta opció en qualsevol moment des del menú de l'OBS Studio.\"\nMacPermissions.AccessGranted=\"S'ha habilitat el permís\"\nMacPermissions.RequestAccess=\"Sol·licitud d'accés\"\nMacPermissions.OpenPreferences=\"Obre les opcions de %1\"\nMacPermissions.Item.ScreenRecording=\"Gravació de pantalla\"\nMacPermissions.Item.ScreenRecording.Details=\"L'OBS necessita aquest permís per poder capturar la vostra pantalla.\"\nMacPermissions.Item.Camera=\"Càmera\"\nMacPermissions.Item.Camera.Details=\"Es requereix aquest permís per accedir al contingut d'una càmera web o targeta capturadora.\"\nMacPermissions.Item.Microphone=\"Micròfon\"\nMacPermissions.Item.Microphone.Details=\"L'OBS requereix aquest permís si voleu capturar el vostre micròfon o un dispositiu d'àudio extern.\"\nMacPermissions.Item.InputMonitoring=\"Monitoratge d'entrada\"\nMacPermissions.Item.InputMonitoring.Details=\"Aquest permís és necessari per a les tecles de drecera mentre l'OBS s'executa en rerefons.\"\nMacPermissions.Item.Accessibility=\"Accessibilitat\"\nMacPermissions.Item.Accessibility.Details=\"En instal·lacions antigues, l'OBS es pot trobar a «Accessibilitat» en comptes de «Monitoratge d'entrada».\"\nMacPermissions.Continue=\"Endavant\"\nSourceLeak.Title=\"Error en buidar els recursos\"\nSourceLeak.Text=\"S'ha produït un error en canviar la col·lecció d'escenes i els recursos d'algunes fonts no s'han aturat correctament. Aquest error és causat per complements que no finalitzen o buiden correctament els seus recursos. Assegureu-vos que tots els complements que utilitzeu estan actualitzats.\\n\\nL'OBS Studio es tancarà immediatament per evitar qualsevol pèrdua o corrupció de dades.\"\nBasic.DesktopDevice1=\"Àudio de l'escriptori\"\nBasic.DesktopDevice2=\"Àudio de l'escriptori 2\"\nBasic.Scene=\"Escena\"\nBasic.DisplayCapture=\"Captura de pantalla\"\nBasic.Main.PreviewConextMenu.Enable=\"Habilita la vista prèvia\"\nBasic.Main.Preview.Disable=\"Inhabilita la vista prèvia\"\nScaleFiltering=\"Escala de filtratge\"\nScaleFiltering.Point=\"Punt\"\nScaleFiltering.Bilinear=\"Bilineal\"\nScaleFiltering.Bicubic=\"Bicúbic\"\nScaleFiltering.Area=\"Àrea\"\nBlendingMethod=\"Mètode de barreja\"\nBlendingMethod.Default=\"Per defecte\"\nBlendingMethod.SrgbOff=\"SRGB desactivat\"\nBlendingMode=\"Mode de barreja\"\nBlendingMode.Additive=\"Afegeix\"\nBlendingMode.Subtract=\"Treu\"\nBlendingMode.Screen=\"Pantalla\"\nBlendingMode.Multiply=\"Multiplica\"\nBlendingMode.Lighten=\"Aclareix\"\nBlendingMode.Darken=\"Enfosqueix\"\nDeinterlacing=\"Desentrellaçar\"\nDeinterlacing.Discard=\"Descarta\"\nDeinterlacing.TopFieldFirst=\"Camp superior primer\"\nDeinterlacing.BottomFieldFirst=\"Camp inferior primer\"\nVolControl.SliderUnmuted=\"Control lliscant del volum per a '%1':\"\nVolControl.SliderMuted=\"Control lliscant del volum per a '%1': (silenciat)\"\nVolControl.Mute=\"Silencia '%1'\"\nVolControl.Properties=\"Propietats per '%1'\"\nVolControl.UnassignedWarning.Title=\"Font d'àudio no assignada\"\nVolControl.UnassignedWarning.Text=\"«%1» no està assignat a cap pista d'àudio i no s'escoltarà a les transmissions o enregistraments.\\n\\nPer assignar una font d'àudio a una pista, obriu les propietats d'àudio avançades mitjançant el menú del botó dret o el botó de l'engranatge de la barra d'eines de l'acoblador del mesclador.\"\nBasic.Main.AddSceneDlg.Title=\"Afegeix una escena\"\nBasic.Main.AddSceneDlg.Text=\"Introduïu el nom de l'escena\"\nBasic.Main.DefaultSceneName.Text=\"Escena %1\"\nBasic.Main.AddSceneCollection.Title=\"Afegeix una col·lecció d'escenes\"\nBasic.Main.AddSceneCollection.Text=\"Introduïu el nom de la col·lecció d'escenes\"\nBasic.Main.RenameSceneCollection.Title=\"Canvia el nom de col·lecció de l'escenes\"\nBasic.Main.RemigrateSceneCollection.Title=\"Actualitza la resolució de la col·lecció d'escenes\"\nBasic.Main.RemigrateSceneCollection.Text=\"Voleu actualitzar la resolució de la col·lecció d'escenes de \\\"%1\\\" perquè coincideixi amb la resolució del llenç del perfil actual de %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"No es pot actualitzar la resolució de la col·lecció d'escenes mentre la sortida és activa.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Ha fallat en actualitzar la resolució de la col·lecció d'escenes. La resolució original és desconeguda.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"No es pot reiniciar: Ha fallat el canvi de resolució de l'OBS.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"No es pot reiniciar: La resolució actual ja és l'establerta a la col·lecció d'escenes.\"\nAddProfile.Title=\"Afegeix un perfil\"\nAddProfile.Text=\"Introduïu el nom del perfil\"\nAddProfile.WizardCheckbox=\"Mostra l'assistent de configuració automàtica\"\nRenameProfile.Title=\"Canvia el nom del perfil\"\nBasic.Main.MixerRename.Title=\"Canvia el nom de la font d'àudio\"\nBasic.Main.MixerRename.Text=\"Introduïu el nom de la font d'àudio\"\nBasic.Main.PreviewDisabled=\"La previsualització està inhabilitada\"\nBasic.SourceSelect=\"Creació o selecció de l'origen\"\nBasic.SourceSelect.CreateNew=\"Crea\"\nBasic.SourceSelect.AddExisting=\"Afegeix-ne una d'existent\"\nBasic.SourceSelect.AddVisible=\"Fes visible l'origen\"\nBasic.Main.Sources.Visibility=\"Visibilitat\"\nBasic.Main.Sources.VisibilityDescription=\"Controla la visibilitat de '%1' al llenç\"\nBasic.Main.Sources.Lock=\"Bloca\"\nBasic.Main.Sources.LockDescription=\"Bloca la posició i l'escala de '%1' al llenç\"\nBasic.PropertiesWindow=\"Propietats de «%1»\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (autoselecció: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Seleccioneu un color\"\nBasic.PropertiesWindow.SelectFont=\"Seleccioneu un tipus de lletra\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Trieu una font\"\nBasic.PropertiesWindow.ConfirmTitle=\"Configuració canviada\"\nBasic.PropertiesWindow.Confirm=\"Hi ha canvis no desats. Voleu mantenir-los?\"\nBasic.PropertiesWindow.NoProperties=\"No hi ha propietats disponibles\"\nBasic.PropertiesWindow.AddFiles=\"Afegeix fitxers\"\nBasic.PropertiesWindow.AddDir=\"Afegir directori\"\nBasic.PropertiesWindow.AddURL=\"Afegeix el camí o l'URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Afegir directori a '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Afegeix fitxers a '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Afegeix una entrada a '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Edita l'entrada de '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Valors FPS simples\"\nBasic.PropertiesView.FPS.Rational=\"Valors FPS racionals\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Rangs de FPS vàlids:\"\nBasic.PropertiesView.UrlButton.Text=\"Voleu obrir aquest enllaç al navegador per defecte?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Obre l'URL\"\nBasic.InteractionWindow=\"Interacció amb '%1'\"\nBasic.StatusBar.Reconnecting=\"Desconnectat, tornant a connectar en %2 segon(s) (intent %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Intentant reconnectar... (intent %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Reconnexió exitosa\"\nBasic.StatusBar.Delay=\"Retard (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Retard (inici en %1 s)\"\nBasic.StatusBar.DelayStoppingIn=\"Retard (aturada en %1 s)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Retard (aturant en %1 seg, començant en %2 seg)\"\nBasic.StatusBar.RecordingSavedTo=\"S'ha desat la gravació a «%1»\"\nBasic.StatusBar.ReplayBufferSavedTo=\"S'ha desat la memòria intermèdia de reproducció a «%1»\"\nBasic.StatusBar.ScreenshotSavedTo=\"S'ha desat la captura de pantalla a «%1»\"\nBasic.StatusBar.AutoRemuxedTo=\"S'ha convertit la gravació automàtica a «%1»\"\nBasic.Filters=\"Filtres\"\nBasic.Filters.AsyncFilters=\"Filtres d'àudio i vídeo\"\nBasic.Filters.AudioFilters=\"Filtres d'àudio\"\nBasic.Filters.EffectFilters=\"Filtres d'efecte\"\nBasic.Filters.Title=\"Filtres per '%1'\"\nBasic.Filters.AddFilter.Title=\"Nom del filtre\"\nBasic.Filters.AddFilter.Text=\"Si us plau, especifiqui el nom del filtre\"\nBasic.TransformWindow=\"Transformació d'element d'escena\"\nBasic.TransformWindow.Position=\"Posició\"\nBasic.TransformWindow.PositionX=\"Posició X\"\nBasic.TransformWindow.PositionY=\"Posició Y\"\nBasic.TransformWindow.Rotation=\"Gir\"\nBasic.TransformWindow.Size=\"Mida\"\nBasic.TransformWindow.Width=\"Amplada\"\nBasic.TransformWindow.Height=\"Alçada\"\nBasic.TransformWindow.Alignment=\"Posició\"\nBasic.TransformWindow.BoundsType=\"Tipus de quadre de limitació\"\nBasic.TransformWindow.BoundsAlignment=\"Alineació en el quadre de limitació\"\nBasic.TransformWindow.Bounds=\"Mida del quadre de limitació\"\nBasic.TransformWindow.BoundsWidth=\"Amplada del quadre delimitador\"\nBasic.TransformWindow.BoundsHeight=\"Alçada del quadre delimitador\"\nBasic.TransformWindow.CropToBounds=\"Retalla al quadre delimitador\"\nBasic.TransformWindow.Crop=\"Retalla\"\nBasic.TransformWindow.CropLeft=\"Escapçament esquerre\"\nBasic.TransformWindow.CropRight=\"Escapçament dret\"\nBasic.TransformWindow.CropTop=\"Escapçament superior\"\nBasic.TransformWindow.CropBottom=\"Escapçament inferior\"\nBasic.TransformWindow.Alignment.TopLeft=\"Part superior esquerra\"\nBasic.TransformWindow.Alignment.TopCenter=\"Part superior centre\"\nBasic.TransformWindow.Alignment.TopRight=\"Part superior dreta\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Centre esquerra\"\nBasic.TransformWindow.Alignment.Center=\"Centre\"\nBasic.TransformWindow.Alignment.CenterRight=\"Centre dreta\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Part inferior esquerra\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Part inferior centre\"\nBasic.TransformWindow.Alignment.BottomRight=\"Part inferior dreta\"\nBasic.TransformWindow.BoundsType.None=\"Sense vores\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Només mida màxima\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Escala a l'interior dels límits\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Escala a l'exterior dels límits\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Escala a l'amplada dels límits\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Escala a l'alçada dels límits\"\nBasic.TransformWindow.BoundsType.Stretch=\"Estirar als límits\"\nBasic.TransformWindow.Title=\"Edita la transformació per a '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"No s'ha seleccionat cap font\"\nBasic.Main.AddSourceHelp.Title=\"No es pot afegir la font\"\nBasic.Main.AddSourceHelp.Text=\"Cal tenir com a mínim 1 escena per afegir una font.\"\nBasic.Main.Scenes=\"Escenes\"\nBasic.Main.Sources=\"Orígens\"\nBasic.Main.Source=\"Origen\"\nBasic.Main.PreparingStream=\"S'està preparant...\"\nBasic.Main.Connecting=\"S'està connectant...\"\nBasic.Main.StartRecording=\"Inicia l'enregistrament\"\nBasic.Main.StartReplayBuffer=\"Inicia la reproducció de la memòria intermèdia\"\nBasic.Main.SaveReplay=\"Desa la repetició\"\nBasic.Main.StartStreaming=\"Inicia la transmissió\"\nBasic.Main.StartBroadcast=\"Emet en directe\"\nBasic.Main.StartVirtualCam=\"Inicia la càmera virtual\"\nBasic.Main.StopRecording=\"Atura l'enregistrament\"\nBasic.Main.PauseRecording=\"Pausa la gravació\"\nBasic.Main.UnpauseRecording=\"Reprèn la gravació\"\nBasic.Main.SplitFile=\"Divideix el fitxer enregistrat\"\nBasic.Main.AddChapterMarker=\"Afegeix un marcador de capítol (només MP4/MOV híbrid)\"\nBasic.Main.StoppingRecording=\"Aturant l'enregistrament...\"\nBasic.Main.StopReplayBuffer=\"Atura la reproducció de la memòria intermèdia\"\nBasic.Main.StoppingReplayBuffer=\"S'està aturant la reproducció de la memòria intermèdia...\"\nBasic.Main.SetupBroadcast=\"Administrador de retransmissions\"\nBasic.Main.StopStreaming=\"Atura la transmissió\"\nBasic.Main.StopBroadcast=\"Finalitza l'emissió\"\nBasic.Main.AutoStopEnabled=\"(Aturada automàtica)\"\nBasic.Main.StoppingStreaming=\"Aturant la transmissió...\"\nBasic.Main.ForceStopStreaming=\"Atura l'enregistrament (descarta el retard)\"\nBasic.Main.ShowContextBar=\"Mostra la barra d'eines d'origen\"\nBasic.Main.HideContextBar=\"Amaga la barra d'eines d'origen\"\nBasic.Main.StopVirtualCam=\"Atura la càmera virtual\"\nBasic.Main.Group=\"Grup %1\"\nBasic.Main.GroupItems=\"Agrupa els elements seleccionats\"\nBasic.Main.Ungroup=\"Desagrupa\"\nBasic.Main.GridMode=\"Mode quadrícula\"\nBasic.Main.ListMode=\"Mode llista\"\nBasic.Main.VirtualCamConfig=\"Configuració de la càmera virtual\"\nBasic.VCam.VirtualCamera=\"Càmera virtual\"\nBasic.VCam.OutputType=\"Tipus de sortida\"\nBasic.VCam.OutputSelection=\"Destinació\"\nBasic.VCam.OutputType.Program=\"Aplicació (per defecte)\"\nBasic.VCam.OutputSelection.NoSelection=\"No hi ha cap selecció per a aquest tipus de sortida\"\nBasic.VCam.RestartWarning=\"La càmera virtual es reiniciarà per aplicar aquest canvi\"\nBasic.MainMenu.File=\"&Fitxer\"\nBasic.MainMenu.File.Export=\"&Exporta\"\nBasic.MainMenu.File.Import=\"&Importa\"\nBasic.MainMenu.File.ShowRecordings=\"Mostra els en&registraments\"\nBasic.MainMenu.File.Remux=\"Converteix el for&mat de gravacions\"\nBasic.MainMenu.File.Settings=\"Configuració (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Mostra la carpeta de configuració\"\nBasic.MainMenu.File.ShowProfileFolder=\"Mostra la carpeta del perfil\"\nBasic.MainMenu.File.ShowMissingFiles=\"Comprova si hi falten fitxers\"\nBasic.MainMenu.File.Exit=\"Surt (&X)\"\nBasic.MainMenu.Edit=\"&Edita\"\nBasic.MainMenu.Edit.Undo=\"Desfés (&U)\"\nBasic.MainMenu.Edit.Redo=\"&Refés\"\nBasic.MainMenu.Edit.LockPreview=\"B&loca la previsualització\"\nBasic.MainMenu.Edit.Scale=\"Vi&sta prèvia i escalat\"\nBasic.MainMenu.Edit.Scale.Window=\"Ajusta a la finestra\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Llenç (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Sortida (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Escalat (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Amplia\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Allunya\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Reinicia el zoom\"\nBasic.MainMenu.Edit.Transform=\"&Transforma\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Edita la transformació...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Copia la transformació\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Enganxa la transformació\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Restableix la transformació\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Gira 90 graus a la dreta\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Gira 90 graus a l'esquerra\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Gira 180 graus\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Capgira &horitzontalment\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Capgira &verticalment\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Ajusta a la pantalla (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"E&stira a la pantalla\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centra a la pantalla\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centra verticalment\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centra horitzontalment\"\nBasic.MainMenu.Edit.Order=\"&Ordre\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Mo&u amunt\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Mou avall (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Mou a la par&t superior\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Mou a la part inferior (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Propiet&ats avançades d'àudio\"\nBasic.MainMenu.View=\"&Visualitza\"\nBasic.MainMenu.View.Toolbars=\"Barres d'eines (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"Barra d'eines flotant\"\nBasic.MainMenu.View.ContextBar=\"Barra d'eines font\"\nBasic.MainMenu.View.SourceIcons=\"&Icones d'origen\"\nBasic.MainMenu.View.StatusBar=\"Barra d'e&stat\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Pantalla completa\"\nBasic.MainMenu.View.ResetUI=\"&Reinicia la interfície d'usuari\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Sempre al davant\"\nBasic.MainMenu.View.SceneListMode=\"Mode de llista d'escenes\"\nBasic.MainMenu.Docks=\"&Acobladors\"\nBasic.MainMenu.Docks.ResetDocks=\"&Reinicia els acobladors\"\nBasic.MainMenu.Docks.LockDocks=\"&Bloca els acobladors\"\nBasic.MainMenu.Docks.SideDocks=\"Barra d'eines a mida de la &finestra\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Acobladors de navegador personalitzats...\"\nBasic.MainMenu.Import=\"Importa...\"\nBasic.MainMenu.Export=\"Exporta...\"\nBasic.MainMenu.SceneCollection=\"Col·lecció d'e&scenes\"\nBasic.MainMenu.Profile=\"&Perfil\"\nBasic.MainMenu.Profile.Import=\"Importació del perfil\"\nBasic.MainMenu.Profile.Export=\"Exportació del perfil\"\nBasic.MainMenu.SceneCollection.Import=\"Importa col·lecció d'escenes\"\nBasic.MainMenu.SceneCollection.Export=\"Exporta la col·lecció d'escenes\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Reinicia la resolució base\"\nBasic.MainMenu.SceneCollection.Migrate=\"Estableix la resolució base\"\nBasic.MainMenu.Profile.Exists=\"El perfil ja existeix\"\nBasic.MainMenu.Tools=\"Eines (&T)\"\nBasic.MainMenu.Help=\"Ajuda (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal d'ajuda\"\nBasic.MainMenu.Help.Website=\"&Web de l'OBS\"\nBasic.MainMenu.Help.Discord=\"Uniu-vos a un servidor &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Novetats\"\nBasic.MainMenu.Help.Logs=\"Fitxers de registre (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Mo&stra els arxius de registre\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"&Carregar arxiu de registre actual\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Carrega el fitxer de registre anterior (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Visualitza el registre actual\"\nBasic.MainMenu.Help.ReleaseNotes=\"Notes de llançament\"\nBasic.MainMenu.Help.CheckForUpdates=\"Comprova si hi ha cap actualització\"\nBasic.MainMenu.Help.Repair=\"Verifica la integritat de fitxers\"\nBasic.MainMenu.Help.RestartSafeMode=\"Reinicia en mode segur\"\nBasic.MainMenu.Help.RestartNormal=\"Reinicia en mode normal\"\nBasic.MainMenu.Help.CrashLogs=\"Info&rme de fallada\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Mo&stra els informes de fallada\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Carrega l'informe d'error anterior (&P)\"\nBasic.MainMenu.Help.About=\"Qu&ant a\"\nBasic.Settings.ProgramRestart=\"El programa ha de ser re-iniciat per tal que aquesta configuració tingui efecte.\"\nBasic.Settings.ConfirmTitle=\"Confirma els canvis\"\nBasic.Settings.Confirm=\"Hi han canvis no desats. Voleu desar els canvis?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 controla algunes de les vostres opcions de reproducció\"\nBasic.Settings.General.Language=\"Llengua\"\nBasic.Settings.General.Updater=\"Actualitzacions\"\nBasic.Settings.General.UpdateChannel=\"Actualitza el canal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Desactivat)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Per defecte)\"\nBasic.Settings.General.EnableAutoUpdates=\"Comprova si hi ha actualitzacions automàticament a l'inici\"\nBasic.Settings.General.OpenStatsOnStartup=\"Obre el diàleg d'estadístiques a l'inici\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Amaga les finestres de l'OBS de la captura de pantalla\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Aquesta opció exclou a l'OBS Studio de ser enregistrat per si mateix i\\no afecta la visibilitat per a altres aplicacions, com ara videoconferència, compartició de pantalla, suport remot, captures de pantalla i altre programari d'enregistrament d'escriptori.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"En habilitar aquesta opció eviteu que l'OBS Studio sigui enregistrat per si mateix i afecta la visibilitat per a altres aplicacions, com ara videoconferència, compartició de pantalla, suport remot, captures de pantalla i altre programari d'enregistrament d'escriptori.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Mostra diàleg de confirmació quan s'iniciï una transmissió\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Mostra diàleg de confirmació quan s'aturi una transmissió\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Mostra un missatge de confirmació en aturar un enregistrament\"\nBasic.Settings.General.HideProjectorCursor=\"Amaga el cursor sobre projectors\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Projectors sempre en la part superior\"\nBasic.Settings.General.Snapping=\"Ajustament d'alineació de la font\"\nBasic.Settings.General.ScreenSnapping=\"Ajustar les fonts a la vora de la pantalla\"\nBasic.Settings.General.CenterSnapping=\"Ajustar les fonts al centre horitzontal i vertical\"\nBasic.Settings.General.SourceSnapping=\"Ajustar les fonts a altres fonts\"\nBasic.Settings.General.SnapDistance=\"Ajusta la sensibilitat\"\nBasic.Settings.General.SpacingHelpers=\"Mostra les guies d'alineació de píxels\"\nBasic.Settings.General.RecordWhenStreaming=\"Enregistra automàticament quan es transmet\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Mantenir l'enregistrament quan s'atura la transmissió\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Inicia la reproducció de la memòria intermèdia automàticament durant la transmissió\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Mantén activa la memòria intermèdia de reproducció quan la transmissió s'aturi\"\nBasic.Settings.General.SysTray=\"Safata del sistema\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimitzar a la safata del sistema en iniciar\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Minimitza sempre a la safata del sistema en lloc de la barra de tasques\"\nBasic.Settings.General.SaveProjectors=\"Desa els projectors en sortir\"\nBasic.Settings.General.CloseExistingProjectors=\"Limita a un projector de pantalla completa per pantalla\"\nBasic.Settings.General.Preview=\"Vista prèvia\"\nBasic.Settings.General.OverflowHidden=\"Amaga el desbordament\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Desbordament sempre visible\"\nBasic.Settings.General.OverflowSelectionHidden=\"Mostra el desbordament fins i tot quan la font és invisible\"\nBasic.Settings.General.Importers=\"Importadors\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Cerca ubicacions conegudes de col·leccions d'escenes quan importeu\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Transició a l'escena en fer doble clic\"\nBasic.Settings.General.StudioPortraitLayout=\"Habilita la disposició horitzontal/vertical\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Mostra les etiquetes de vista prèvia/programa\"\nBasic.Settings.General.Multiview=\"Vista múltiple\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Feu clic per canviar entre escenes\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Mostra el nom de l'escena\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Dibuixa les zones segures (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Disposició de vista múltiple\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horitzontal, part superior (8 escenes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horitzontal, part inferior (8 escenes)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertical, esquerra (8 escenes)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertical, dreta (8 escenes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horitzontal, part superior (18 escenes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horitzontal, part superior (24 escenes)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Només escenes (4 escenes)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Només escenes (9 escenes)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Només escenes (16 escenes)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Només escenes (25 escenes)\"\nBasic.Settings.General.ChannelName.stable=\"Estable\"\nBasic.Settings.General.ChannelDescription.stable=\"Últim llançament estable\"\nBasic.Settings.General.ChannelName.beta=\"Beta / versio candidata\"\nBasic.Settings.General.ChannelDescription.beta=\"Versions prèvies potencialment inestables\"\nBasic.Settings.Appearance=\"Aparença\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Estil\"\nBasic.Settings.Appearance.General.NoVariant=\"No hi ha estils disponibles\"\nBasic.Settings.Appearance.FontScale=\"Mida de la font\"\nBasic.Settings.Appearance.Density=\"Densitat\"\nBasic.Settings.Appearance.Classic=\"Clàssica\"\nBasic.Settings.Appearance.Compact=\"Compacta\"\nBasic.Settings.Appearance.Comfortable=\"Còmode\"\nBasic.Settings.Appearance.OptionsWarning=\"Algunes opcions d'aspecte no estan disponibles per a aquest estil.\"\nBasic.Settings.Stream=\"Directe\"\nBasic.Settings.Stream.Destination=\"Destinació\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Utilitza l'autenticació\"\nBasic.Settings.Stream.Custom.Username=\"Usuari\"\nBasic.Settings.Stream.Custom.Password=\"Contrasenya\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: introduïu l'srp_username.\\nRTMP: introduïu el nom d'usuari.\\nSRT: no utilitzat.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: introduïu la srp_password.\\nRTMP: introduïu la contrasenya.\\nSRT: introduïu la contrasenya de xifratge.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Habilita el mode Test d'amplada de banda\"\nBasic.Settings.Stream.TTVAddon=\"Complements de xat del Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Cap\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV i FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Mode graella\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Obre la configuració\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Falta l'URL i la clau de transmissió.\\n\\nObriu la configuració per introduir l'URL i la clau de transmissió a la pestanya «transmissió».\"\nBasic.Settings.Stream.MissingUrl=\"Falta l'URL de la transmissió.\\n\\nObriu la configuració per introduir l'URL a la pestanya «transmissió».\"\nBasic.Settings.Stream.MissingStreamKey=\"Falta la clau de transmissió.\\n\\nObriu la configuració per introduir la clau de transmissió a la pestanya «transmissió».\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignora la configuració recomanada del servei de retransmissió\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Ignora la configuració recomanada\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Atenció: Ignorar els límits del servei pot donar com a resultat una degradació o bandejament de la retransmissió.\\n\\nSegur que voleu continuar?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Taxa de bits màxima de vídeo: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Taxa de bits màxima d'àudio: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Resolució màxima: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"FPS màxims: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Especifica el servidor personalitzat...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Servidor personalitzat\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Activa %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Ample de banda màxim de transmissió\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Màxim de pistes de vídeo\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Activa l'abocament de flux a FLV (utilitza una configuració senzilla del fitxer de gravació)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Anul·la de configuració (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Activa l'anul·lació de la configuració\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Vídeo multipista\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Llenç addicional\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Difusió simultània\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"La difusió simultània permet codificar i enviar vídeo de diverses qualitats. <a href='https://obsproject.com/kb/whip-streaming-guide'>Més informació</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Capes totals\"\nBasic.Settings.Stream.AdvancedOptions=\"Opcions avançades\"\nBasic.Settings.Output=\"Sortida\"\nBasic.Settings.Output.Format=\"Format d'enregistrament\"\nBasic.Settings.Output.Format.MKV=\"Vídeo Matroska (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"MP4 híbrid (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV híbrid (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 fragmentat (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV fragmentat (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"El MOV fragmentat escriu l'enregistrament en trossos i no requereix la mateixa finalització que els fitxers MOV tradicionals.\\nAixò garanteix que el fitxer es pugui reproduir fins i tot si s'interromp l'escriptura al disc, per exemple, com a resultat d'un BSOD o una pèrdua d'energia.\\n\\nÉs possible que això no sigui compatible amb tots els reproductors i editors. Utilitzeu Fitxer → Enregistraments remux per convertir el fitxer a un format més compatible si cal.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"El MOV fragmentat escriu l'enregistrament en trossos i no requereix la mateixa finalització que els fitxers MOV tradicionals.\\nAixò garanteix que el fitxer es pugui reproduir fins i tot si s'interromp l'escriptura al disc, per exemple, com a resultat d'un BSOD o una pèrdua d'energia.\\n\\nÉs possible que això no sigui compatible amb tots els reproductors i editors. Utilitzeu Fitxer → Enregistraments remux per convertir el fitxer a un format més compatible si cal.\"\nBasic.Settings.Output.Encoder.Video=\"Codificador de vídeo\"\nBasic.Settings.Output.Encoder.Audio=\"Codificador d'àudio\"\nBasic.Settings.Output.SelectDirectory=\"Seleccioneu el directori de gravació\"\nBasic.Settings.Output.DynamicBitrate=\"Canvia dinàmicament la ràtio per manegar la saturació\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Canvia dinàmicament la ràtio per manegar la saturació (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"En comptes de perdre fotogrames per reduir la saturació, canvia dinàmicament la ràtio al vol.\\n\\nTingueu en compte que això pot incrementar el retard per als espectadors si hi ha cap saturació important.\\nEn baixar la ràtio, pot trigar uns minuts a restaurar el valor original.\\n\\nActualment només suportat per a RTMP.\"\nBasic.Settings.Output.Mode=\"Mode de sortida\"\nBasic.Settings.Output.Mode.Adv=\"Avançat\"\nBasic.Settings.Output.Mode.FFmpeg=\"Sortida FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Activa la reproducció de la memòria intermèdia\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Temps màxim de repetició\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Memòria màxima\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Ús aproximat de memòria: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Avís: l'ús de memòria estimat de %1 MiB és superior al màxim recomanat de %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"No es pot estimar l'ús de memòria. Establiu el límit màxim de memòria.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefix del nom del fitxer de la memòria intermèdia\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufix\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"La memòria intermèdia de reproducció no es pot utilitzar quan el tipus d'enregistrament està configurat com a sortida personalitzada (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Camí d'enregistrament\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Qualitat de l'enregistrament\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Mateixa que en directe\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Qualitat alta, mida d'arxiu mitjana\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Qualitat molt elevada, mida de fitxer gran\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Qualitat sense pèrdues, mida de l'arxiu molt gran\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Atenció: La taxa de bits de vídeo s’establirà a %1, que és el límit superior per al servei de transmissió actual.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Atenció: La taxa de bits d'àudio s’establirà a %1, que és el límit superior per al servei de transmissió actual.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Atenció: No es poden pausar els enregistraments si la qualitat està configurada a «Mateixa que en directe».\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Avís: el format d'enregistrament seleccionat actualment és incompatible amb els codificadors de flux seleccionats.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Nota: Enregistrar amb codificador basat en programari amb una qualitat diferent del directe requerirà ús de CPU addicional si el directe i la gravació es fan alhora.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Atenció: La qualitat sense pèrdues crea fitxers de mida exagerada! Podeu utilitzar p. ex. un total de 7 gigabytes d'espai al disc per a cada minut amb fotogrames i resolucions altes. Aquesta qualitat no és recomanable per a enregistraments llargs llevat que disposeu de gran quantitat d'espai disponible al disc.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Esteu segur que voleu utilitzar qualitat sense pèrdues?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Advertiment de qualitat sense pèrdues!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Programari (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Maquinari (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Maquinari (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Maquinari (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Maquinari (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Maquinari (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Maquinari (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Maquinari (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Maquinari (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Maquinari (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Maquinari (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Programari (preconfiguració de x264 amb baix ús de CPU, augmenta la mida del fitxer)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (per defecte)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Pista VOD del Twitch (utilitza la pista 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Pista d'àudio\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Resolució o velocitat de fotogrames incompatible\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Aquest servei de reproducció en temps real no admet la vostra resolució de sortida i/o velocitat de fotogrames actuals. Es canviaran al valor compatible més proper:\\n\\n%1\\n\\nVoleu continuar?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolució: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Còdec no compatible\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"El servei de retransmissió «%1» no suporta el còdec «%2». Es canviarà a «%3».\\n\\nVoleu continuar?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"El servei de retransmissió «%1» no admet els codificadors «%2» i «%3». Aquests codificadors es canviaran per «%4» i «%5».\\n\\nVoleu continuar?\"\nBasic.Settings.Output.VideoBitrate=\"Taxa de bits de vídeo\"\nBasic.Settings.Output.AudioBitrate=\"Taxa de bits d'àudio\"\nBasic.Settings.Output.Reconnect=\"Torna a connectar automàticament\"\nBasic.Settings.Output.RetryDelay=\"Retard fins al proper intent\"\nBasic.Settings.Output.MaxRetries=\"Nombre màxim de reintents\"\nBasic.Settings.Output.Advanced=\"Activa la configuració personalitzada del codificador (avançat)\"\nBasic.Settings.Output.EncoderPreset=\"Valors predefinits del codificador\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (ús baix de CPU, qualitat més baixa)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (per defecte) (ús mitjà de CPU, qualitat estàndard)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (ús alt de CPU, alta qualitat)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Configuració del codificador personalitzada\"\nBasic.Settings.Output.CustomMuxerSettings=\"Configuració del conversor personalitzada\"\nBasic.Settings.Output.NoSpaceFileName=\"Genera noms de fitxer sense espai\"\nBasic.Settings.Output.Adv.Rescale=\"Reescala la sortida\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Inhabilitat\"\nBasic.Settings.Output.Adv.AudioTrack=\"Pista d'àudio\"\nBasic.Settings.Output.Adv.Streaming=\"Directe\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Configuració de la retransmissió\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Pista 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Pista 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Pista 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Pista 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Pista 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Pista 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Pista VOD del Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"Configuració del codificador\"\nBasic.Settings.Output.Adv.Recording=\"Enregistrament\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Configuració de la gravació\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tipus de gravació\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tipus\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Estàndard\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Sortida personalitzada (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Fer servir codificador de directe)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Nom de l'arxiu de format\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Sobreescriure si l'arxiu existeix\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"La sortida (FFmpeg) personalitzada es proveeix sense cap garantia.\\nConsidereu utilitzar el tipus de gravació «Estàndard».\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Tipus de sortida FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Sortida a un URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Sortida a un fitxer\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Formats de gravació comuns\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Tots els fitxers\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Ruta del fitxer o URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format del contenidor\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Àudio\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Vídeo\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Format predeterminat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Descripció del Format de contenidor\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Còdec d'àudio/vídeo endevinat de la ruta del fitxer o URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Codificador predeterminat\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Desactiva el codificador\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Codificador de vídeo\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Configuració del codificador de vídeo (si escau)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Codificador d'àudio\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Configuració de codificador d'àudio (si escau)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Configuració del convertidor (si escau)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Interval de fotogrames clau (fotogrames)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Mostra tots els codificadors (encara que siguin potencialment incompatibles)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Configuració del FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Divisió automàtica del fitxer\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Divisió per temps\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Divisió per mida\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Només divisió manual\"\nBasic.Settings.Output.SplitFile.Time=\"Temps de divisió\"\nBasic.Settings.Output.SplitFile.Size=\"Mida de divisió\"\nScreenshot=\"Sortida de la captura de pantalla\"\nScreenshot.SourceHotkey=\"Captura de pantalla de la font seleccionada\"\nScreenshot.StudioProgram=\"Captura de pantalla (programa)\"\nScreenshot.Preview=\"Captura de pantalla (vista prèvia)\"\nScreenshot.Scene=\"Captura de pantalla (escena)\"\nScreenshot.Source=\"Captura de pantalla (origen)\"\nFilenameFormatting.TT.CCYY=\"Any, quatre dígits\"\nFilenameFormatting.TT.YY=\"Any, dos últims dígits (00-99)\"\nFilenameFormatting.TT.MM=\"Mes com a nombre decimal (01-12)\"\nFilenameFormatting.TT.DD=\"Dia del mes, precedit de zero (01-31)\"\nFilenameFormatting.TT.hh=\"Hora en format 24h (00-23)\"\nFilenameFormatting.TT.mm=\"Minut (00-59)\"\nFilenameFormatting.TT.ss=\"Segon (00-59)\"\nFilenameFormatting.TT.Percent=\"Un signe %\"\nFilenameFormatting.TT.a=\"Nom abreujat del dia de la setmana\"\nFilenameFormatting.TT.A=\"Nom complet del dia de la setmana\"\nFilenameFormatting.TT.b=\"Nom del mes abreujat\"\nFilenameFormatting.TT.B=\"Nom del mes complet\"\nFilenameFormatting.TT.d=\"Dia del mes, precedit de zero (01-31)\"\nFilenameFormatting.TT.H=\"Hora en format 24h (00-23)\"\nFilenameFormatting.TT.I=\"Hora en format 12h (01-12)\"\nFilenameFormatting.TT.m=\"Mes com a nombre decimal (01-12)\"\nFilenameFormatting.TT.M=\"Minut (00-59)\"\nFilenameFormatting.TT.p=\"Designació AM o PM\"\nFilenameFormatting.TT.s=\"Temps en segons des de l'època UNIX\"\nFilenameFormatting.TT.S=\"Segon (00-59)\"\nFilenameFormatting.TT.y=\"Any, dos últims dígits (00-99)\"\nFilenameFormatting.TT.Y=\"Any\"\nFilenameFormatting.TT.z=\"Desplaçament de la zona horària respecte a l'UTC en format ISO-8601\"\nFilenameFormatting.TT.Z=\"Nom o abreviatura de la zona horària\"\nFilenameFormatting.TT.FPS=\"Fotogrames per segon\"\nFilenameFormatting.TT.CRES=\"Resolució base (llenç)\"\nFilenameFormatting.TT.ORES=\"Resolució de sortida (escalat)\"\nFilenameFormatting.TT.VF=\"Format de vídeo\"\nBasic.Settings.Video=\"Vídeo\"\nBasic.Settings.Video.Adapter=\"Adaptador de vídeo\"\nBasic.Settings.Video.BaseResolution=\"Resolució base (Llenç)\"\nBasic.Settings.Video.ScaledResolution=\"Resolució de sortida (escalat)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtre de baixada d'escala\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Les resolucions coincideixen, no cal reduir l'escala]\"\nBasic.Settings.Video.FPSCommon=\"Valors comuns FPS\"\nBasic.Settings.Video.FPSInteger=\"Valor FPS sencer\"\nBasic.Settings.Video.FPSFraction=\"Valor FPS fraccionari\"\nBasic.Settings.Video.Numerator=\"Numerador\"\nBasic.Settings.Video.Denominator=\"Denominador\"\nBasic.Settings.Video.Renderer=\"Renderitzador\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (experimental)\"\nBasic.Settings.Video.InvalidResolution=\"El valor de resolució no és vàlid. Ha de ser [amplada]x[alçada] (per exemple, 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"La sortida de vídeo és actualment activa. Si us plau desactiveu qualsevol sortida per canviar la configuració de vídeo.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineal (més ràpida, però borrós si s'escala)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (escalat accentuat, 16 mostres)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (escalat accentuat, 36 mostres)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Àrea (suma ponderada, 4/6/9 mostres)\"\nBasic.Settings.Audio=\"Àudio\"\nBasic.Settings.Audio.SampleRate=\"Frequència de mostreig\"\nBasic.Settings.Audio.Channels=\"Canals\"\nBasic.Settings.Audio.Meters=\"Vúmetre\"\nBasic.Settings.Audio.MeterDecayRate=\"Velocitat d'esvaïment\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Ràpida\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Mitja (PPM de tipus I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lenta (PPM de tipus II)\"\nBasic.Settings.Audio.PeakMeterType=\"Tipus de mesurador de pics\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Pic de mostra\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True Peak (ús elevat de la CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ATENCIÓ: El so envoltant està habilitat.\"\nBasic.Settings.Audio.MultichannelWarning=\"Si esteu transmetent, comproveu que el vostre servei de reproducció admet tant la captura com la reproducció de so envoltant. Per exemple, Facebook 360 Live admet totalment el so envoltant. El YouTube Live admet la captura d'àudio 5.1 (i la reproducció als televisors).\\n\\nEls filtres d'àudio d'OBS són compatibles amb el so envoltant, tot i que no es garanteix el suport del complement VST.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Voleu habilitar el so envoltant?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Segur que voleu habilitar el so envoltant?\"\nBasic.Settings.Audio.Devices=\"Dispositius d'àudio globals\"\nBasic.Settings.Audio.DesktopDevice=\"Àudio de l'escriptori\"\nBasic.Settings.Audio.DesktopDevice2=\"Àudio de l'escriptori 2\"\nBasic.Settings.Audio.AuxDevice=\"Dispositiu d'àudio mic/auxiliar\"\nBasic.Settings.Audio.AuxDevice2=\"Dispositiu d'àudio mic/auxiliar 2\"\nBasic.Settings.Audio.AuxDevice3=\"Dispositiu d'àudio mic/auxiliar 3\"\nBasic.Settings.Audio.AuxDevice4=\"Dispositiu d'àudio mic/auxiliar 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Activa «prémer per silenciar»\"\nBasic.Settings.Audio.PushToMuteDelay=\"Retard de «prémer per-silenciar»\"\nBasic.Settings.Audio.EnablePushToTalk=\"Activa «prémer per parlar»\"\nBasic.Settings.Audio.PushToTalkDelay=\"Retard de «prémer per parlar»\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Dispositiu no connectat o no disponible]\"\nBasic.Settings.Audio.Disabled=\"Inhabilitat\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Mode de memòria intermèdia d'àudio de baixa latència (per a sortides Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"NOTA: la memòria intermèdia d'àudio de baixa latència està activada.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"El mode de memòria intermèdia d'àudio de baixa latència pot provocar que l'àudio falli o que deixi de reproduir-se des d'algunes fonts.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Voleu activar el mode de memòria intermèdia d'àudio de baixa latència?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Esteu segur que voleu activar el mode de memòria intermèdia d'àudio de baixa latència?\"\nBasic.Settings.Accessibility=\"Accessibilitat\"\nBasic.Settings.Accessibility.ColorOverrides=\"Utilitza colors diferents\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Vora de la font (selecció)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Vora de la font (retall)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Vora de la font (en passar per sobre)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Barra de volum del mesclador (-60 a -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Barra de volum del mesclador (-20 a -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Barra de volum del mesclador (-9 a 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Barra de volum del mesclador (-60 a -20dB) (actiu)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Barra de volum del mesclador (-20 a -9dB) (actiu)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Barra de volum del mesclador (-9 a 0dB) (actiu)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Configuració de color\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Per defecte\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Personalitzat\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Daltonisme (colors alternatius)\"\nBasic.Settings.Advanced=\"Avançat\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Mostra l'avís de sortides actives en sortir\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prioritat del procés\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Alta\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Per sobre del normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Per sota del normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inactiva\"\nBasic.Settings.Advanced.FormatWarning=\"Atenció: Altres formats de color diferents de NV12/P010 estan pensats primàriament per a gravació i no es recomana el seu ús durant la retransmissió. En aquest cas podeu experimentar un increment d'ús de CPU causat per la conversió del format del color.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Advertència: els formats d'alta precisió s'utilitzen més habitualment amb els espais de color HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Advertència: el Rec. 2100 hauria d'utilitzar un format amb més precisió.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Format del color\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 bits, 4:2:0, 2 plànols)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 bits, 4:2:0, 3 plànols)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 bits, 4:4:4, 3 plànols)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 bits, 4:2:0, 2 plànols)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 bits, 4:2:0, 3 plànols)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 bits, 4:2:2, 2 plànols)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 bits, 4:4:4, 2 plànols)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 bits)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Espai de color\"\nBasic.Settings.Advanced.Video.ColorRange=\"Gamma de colors\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limitat\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Màxima\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Nivell de blanc SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nivell màxim nominal HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Dispositiu de monitorització\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Per defecte\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Desactiva la reducció d'àudio de Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Retard del directe\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Durada\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Preservar el punt de tall (augmenta retard) quan s'estigui reconnectant\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Ús de memòria estimat: %1 MB\"\nBasic.Settings.Advanced.Network=\"Xarxa\"\nBasic.Settings.Advanced.Network.Disabled=\"El protocol de transmissió seleccionat actualment no admet el canvi de configuració de la xarxa.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Enllaçar amb\"\nBasic.Settings.Advanced.Network.IPFamily=\"Família IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Habilita optimitzacions de la xarxa\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Habilita el ritme TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Intenta fer més amigable la sortida RTMP cap a altres aplicacions sensibles a la latència de la xarxa mitjançant la regulació de la velocitat de transmissió.\\nPot augmentar el risc de perdre fotogrames en connexions inestables.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Comportament del focus amb les dreceres\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"No inhabilitis les dreceres\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Inhabilita les tecles de drecera quan la finestra principal estigui en primer pla\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Inhabilita les dreceres si la pantalla principal no és el focus\"\nBasic.Settings.Advanced.AutoRemux=\"Converteix automàticament a %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(enregistra com a mkv)\"\nBasic.AdvAudio=\"&Propietats avançades d'àudio\"\nBasic.AdvAudio.ActiveOnly=\"Només fonts actives\"\nBasic.AdvAudio.Name=\"Nom\"\nBasic.AdvAudio.Volume=\"Volum\"\nBasic.AdvAudio.VolumeSource=\"Volum per a '%1'\"\nBasic.AdvAudio.MonoSource=\"Mescla monoaural per a '%1'\"\nBasic.AdvAudio.Balance=\"Balanç\"\nBasic.AdvAudio.BalanceSource=\"Balanç per a '%1'\"\nBasic.AdvAudio.SyncOffset=\"Correcció de sincronització\"\nBasic.AdvAudio.SyncOffsetSource=\"Compensació de sincronisme per a '%1'\"\nBasic.AdvAudio.Monitoring=\"Monitorització d'àudio\"\nBasic.AdvAudio.Monitoring.None=\"Monitorització desactivada\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Només monitorizació (silencia la sortida)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitorització i sortida\"\nBasic.AdvAudio.MonitoringSource=\"Monitorització d'àudio per a '%1'\"\nBasic.AdvAudio.AudioTracks=\"Pistes\"\nBasic.Settings.Hotkeys=\"Dreceres de teclat\"\nBasic.Settings.Hotkeys.Pair=\"Combinacions de tecles compartides amb '%1' actuen com a palanques\"\nBasic.Settings.Hotkeys.Filter=\"Filtre\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Fitre per drecera\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Aquesta drecera s'ha assignat a una o més accions. Feu clic per veure els errors\"\nBasic.Settings.Hotkeys.PleaseWait=\"S'estan carregant les dreceres de teclat, espereu...\"\nBasic.Hotkeys.SelectScene=\"Canviar a escena\"\nBasic.SystemTray.Show=\"Mostra\"\nBasic.SystemTray.Hide=\"Amaga\"\nBasic.SystemTray.Message.Reconnecting=\"Desconnectat. Tornant a connectar...\"\nHotkeys.Insert=\"Insereix\"\nHotkeys.Delete=\"Suprimeix\"\nHotkeys.Home=\"Inici\"\nHotkeys.End=\"Final\"\nHotkeys.PageUp=\"Pàgina amunt\"\nHotkeys.PageDown=\"Pàgina avall\"\nHotkeys.NumLock=\"Bloq. núm.\"\nHotkeys.ScrollLock=\"Bloq. despl.\"\nHotkeys.CapsLock=\"Bloq. maj.\"\nHotkeys.Backspace=\"Retrocés\"\nHotkeys.Tab=\"Tabulador\"\nHotkeys.Print=\"Imprimeix\"\nHotkeys.Pause=\"Pausa\"\nHotkeys.Left=\"Esquerra\"\nHotkeys.Right=\"Dreta\"\nHotkeys.Up=\"Amunt\"\nHotkeys.Down=\"Avall\"\nHotkeys.Super=\"Súper\"\nHotkeys.Menu=\"Menú\"\nHotkeys.Space=\"Espai\"\nHotkeys.NumpadNum=\"%1 de teclat numèric\"\nHotkeys.NumpadMultiply=\"Multiplicació del teclat numèric\"\nHotkeys.NumpadDivide=\"Divisió del teclat numèric\"\nHotkeys.NumpadAdd=\"Suma del teclat numèric\"\nHotkeys.NumpadSubtract=\"Resta del teclat numèric\"\nHotkeys.NumpadDecimal=\"Decimal del teclat numèric\"\nHotkeys.AppleKeypadNum=\"%1 (teclat)\"\nHotkeys.AppleKeypadMultiply=\"* (Teclat)\"\nHotkeys.AppleKeypadDivide=\"/ (Teclat)\"\nHotkeys.AppleKeypadAdd=\"+ (Teclat)\"\nHotkeys.AppleKeypadSubtract=\"- (Teclat)\"\nHotkeys.AppleKeypadDecimal=\". (Teclat)\"\nHotkeys.AppleKeypadEqual=\"= (Teclat)\"\nHotkeys.MouseButton=\"Ratolí %1\"\nMute=\"Silenci\"\nUnmute=\"Treu el silenci\"\nPush-to-mute=\"Prémer per silenciar\"\nPush-to-talk=\"Prémer per parlar\"\nSceneItemShow=\"Mostra «%1»\"\nSceneItemHide=\"Amaga «%1»\"\nOutputWarnings.NoTracksSelected=\"Heu de seleccionar almenys una cançó\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Error de configuració de sortida\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Totes les sortides han de tenir almenys una pista d'àudio seleccionada.\"\nOutputWarnings.MP4Recording=\"Advertència: els enregistraments creats en MP4/MOV no es podran recuperar si el fitxer no es pot finalitzar (p. ex. degut a un mal funcionament del sistema o interrupcions, etc.) Si voleu enregistrar diverses pistes d'àudio, considereu utilitzar el format MKV i convertir l'enregistrament a MP4/MOV després d'haver finalitzat (Fitxer → Conversió de gravació)\"\nOutputWarnings.CannotPause=\"Advertència: les gravacions no es poden aturar si el codificador de gravació està configurat a «(Utilitza el codificador de flux)»\"\nOutputWarnings.CodecIncompatible=\"La selecció del codificador d'àudio o vídeo s'ha restablert a causa d'una incompatibilitat. Seleccioneu un codificador compatible de la llista.\"\nCodecCompat.Incompatible=\"(Incompatible amb %1)\"\nCodecCompat.CodecPlaceholder=\"Seleccioneu el codificador...\"\nCodecCompat.ContainerPlaceholder=\"Seleccioneu el format...\"\nCodecCompat.CodecMissingOnExit.Title=\"Cap codificador seleccionat\"\nCodecCompat.CodecMissingOnExit.Text=\"Almenys un codificador de vídeo o àudio no està configurat. Assegureu-vos de seleccionar codificadors tant per a l'enregistrament com per a la transmissió.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Cap format seleccionat\"\nCodecCompat.ContainerMissingOnExit.Text=\"No s'ha seleccionat cap format d'enregistrament. Seleccioneu un que sigui compatible amb el codificador de transmissió seleccionat.\"\nFinalScene.Title=\"Supressió de l'escena\"\nFinalScene.Text=\"Cal que hi hagi almenys una escena.\"\nNoSources.Title=\"Cap font\"\nNoSources.Text=\"Sembla que encara no heu afegit cap font de vídeo, de manera que només es mostrarà una pantalla en blanc. Esteu segur que voleu fer això?\"\nNoSources.Text.AddSource=\"Podeu afegir fonts fent clic a la icona «+» sota el quadre Fonts de la finestra principal en qualsevol moment.\"\nNoSources.Label=\"No teniu cap font.\\nFeu clic al botó + que hi ha a continuació\\no bé feu clic aquí per afegir-ne una.\"\nChangeBG=\"Estableix el color\"\nCustomColor=\"Color personalitzat\"\nBrowserSource.EnableHardwareAcceleration=\"Habilita l'acceleració per maquinari al navegador\"\nAbout=\"Quant a\"\nAbout.Info=\"L'OBS Studio és un programa de codi lliure i gratuït d'enregistrament de vídeo i retransmissió en directe.\"\nAbout.Donate=\"Feu una aportació\"\nAbout.GetInvolved=\"Col·laboreu-hi\"\nAbout.Authors=\"Autoria\"\nAbout.License=\"Llicència\"\nAbout.Error=\"El fitxer no es pot llegir i s'ha produït un error.\\n\\nVeieu: %1\"\nAbout.Contribute=\"Col·laboreu amb l'OBS Project\"\nAddUrl.Title=\"Afegeix una font mitjançant l'URL\"\nAddUrl.Text=\"Heu arrossegat un URL a l'OBS. Això afegirà automàticament l'enllaç com a font. Voleu continuar?\"\nResizeOutputSizeOfSource=\"Redimensiona la sortida (mida de la font)\"\nResizeOutputSizeOfSource.Text=\"La resolució base i de sortida es canviarà a la mida de la font actual.\"\nResizeOutputSizeOfSource.Continue=\"Voleu continuar?\"\nPreviewTransition=\"Previsualitza la transició\"\nImporter=\"Importador de col·leccions d'escenes\"\nImporter.SelectCollection=\"Seleccioneu una col·lecció d'escenes\"\nImporter.Collection=\"Col·lecció d'escenes\"\nImporter.HelpText=\"Afegiu fitxers a aquesta finestra per importar col·leccions d'OBS o d'altres programes compatibles.\"\nImporter.Path=\"Ruta de la col·lecció\"\nImporter.Program=\"Aplicació detectada\"\nImporter.AutomaticCollectionPrompt=\"Cerca automàticament col·leccions d'escenes\"\nImporter.AutomaticCollectionText=\"L'OBS pot trobar automàticament col·leccions d'escenes importables de programes de tercers compatibles. Voleu que l'OBS trobi automàticament col·leccions?\\n\\nPodeu canviar-ho més endavant a Configuració > General > Importadors.\"\nImporter.SelectFile=\"Navega...\"\nRestart=\"Reinicia\"\nNeedsRestart=\"Cal reiniciar l'OBS Studio. Voleu reiniciar-lo ara?\"\nLoadProfileNeedsRestart=\"El perfil conté paràmetres que requereixen reiniciar l'OBS:\\n%1\\n\\nVoleu reiniciar l'OBS perquè aquests paràmetres tinguin efecte?\"\nContextBar.NoSelectedSource=\"No s'ha seleccionat cap font\"\nContextBar.MediaControls.PlayMedia=\"Reprodueix el contingut\"\nContextBar.MediaControls.PauseMedia=\"Pausa el contingut\"\nContextBar.MediaControls.StopMedia=\"Atura el contingut\"\nContextBar.MediaControls.RestartMedia=\"Reinicia el contingut\"\nContextBar.MediaControls.PlaylistNext=\"Següent a la llista de reproducció\"\nContextBar.MediaControls.PlaylistPrevious=\"Anterior a la llista de reproducció\"\nContextBar.MediaControls.BlindSeek=\"Giny de recerca de mitjans\"\nYouTube.Auth.Ok=\"L'autorització s'ha completat correctament.\\nAra podeu tancar aquesta pàgina.\"\nYouTube.Auth.NoCode=\"El procés d'autorització no s'ha completat.\"\nYouTube.Auth.NoChannels=\"No hi ha cap canal disponible al compte seleccionat\"\nYouTube.Auth.WaitingAuth.Title=\"Autorització d'usuari a Youtube\"\nYouTube.Auth.WaitingAuth.Text=\"Finalitzeu l'autorització al vostre navegador.<br>Si no s'obre una finestra del navegador, feu clic en aquest enllaç per completar l'autorització:<br>%1\"\nYouTube.AuthError.Text=\"No s'ha pogut obtenir la informació del canal: %1.\"\nYouTube.Actions.WindowTitle=\"Configuració d'emissió al YouTube - Canal: %1\"\nYouTube.Actions.CreateNewEvent=\"Crea una emissió nova\"\nYouTube.Actions.ChooseEvent=\"Selecciona una emissió existent\"\nYouTube.Actions.Title=\"Títol*\"\nYouTube.Actions.MyBroadcast=\"La meva emissió\"\nYouTube.Actions.Description=\"Descripció\"\nYouTube.Actions.Privacy=\"Privadesa*\"\nYouTube.Actions.Privacy.Private=\"Privat\"\nYouTube.Actions.Privacy.Public=\"Públic\"\nYouTube.Actions.Privacy.Unlisted=\"No llistat\"\nYouTube.Actions.Category=\"Categoria\"\nYouTube.Actions.Thumbnail=\"Miniatura\"\nYouTube.Actions.Thumbnail.SelectFile=\"Trieu un fitxer...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Cap fitxer seleccionat\"\nYouTube.Actions.Thumbnail.ClearFile=\"Neteja\"\nYouTube.Actions.MadeForKids=\"Aquest vídeo és adequat per a menors?*\"\nYouTube.Actions.MadeForKids.Yes=\"Sí, és adequat per a un públic infantil\"\nYouTube.Actions.MadeForKids.No=\"No, està adreçat a un públic més adult\"\nYouTube.Actions.AdditionalSettings=\"Configuració addicional\"\nYouTube.Actions.Latency=\"Latència\"\nYouTube.Actions.Latency.Low=\"Baixa\"\nYouTube.Actions.Latency.UltraLow=\"Molt baixa\"\nYouTube.Actions.EnableAutoStart=\"Habilita l'inici automàtic\"\nYouTube.Actions.EnableAutoStop=\"Habilita l'aturada automàtica\"\nYouTube.Actions.AutoStartStop.TT=\"Indica si aquesta emissió programada s'ha d'iniciar automàticament\"\nYouTube.Actions.EnableDVR=\"Habilita DVR\"\nYouTube.Actions.360Video=\"Vídeo 360\"\nYouTube.Actions.ScheduleForLater=\"Programa per a més tard\"\nYouTube.Actions.RememberSettings=\"Recorda la configuració\"\nYouTube.Actions.Create_Ready=\"Crea una emissió nova\"\nYouTube.Actions.Create_GoLive=\"Crea una emissió i comença a emetre\"\nYouTube.Actions.Choose_Ready=\"Trieu una emissió\"\nYouTube.Actions.Choose_GoLive=\"Trieu una emissió i comenceu a emetre\"\nYouTube.Actions.Create_Schedule=\"Programador d'emissions\"\nYouTube.Actions.Create_Schedule_Ready=\"Seleccioneu i programeu una emissió\"\nYouTube.Actions.Dashboard=\"Obre el YouTube Studio\"\nYouTube.Actions.Error.Title=\"Error en crear l'emissió\"\nYouTube.Actions.Error.Text=\"Error d'accés a YouTube '%1'.<br/>Un error més descriptiu es pot trobar a <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Error d'accés al YouTube. Comproveu la vostra connexió de xarxa o l'accés al servidor del YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Error creant l'emissió '%1'.<br/>Un error més descriptiu es pot trobar a <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"No s'ha creat cap transmissió. Torneu a enllaçar el vostre compte.\"\nYouTube.Actions.Error.YouTubeApi=\"Error de l'API de YouTube. Consulteu el fitxer de registre per obtenir més informació.\"\nYouTube.Actions.Error.BroadcastNotFound=\"No s'ha trobat l'emissió seleccionada.\"\nYouTube.Actions.Error.FileMissing=\"El fitxer no existeix\"\nYouTube.Actions.Error.FileOpeningFailed=\"S'ha produït un error en obrir el fitxer seleccionat.\"\nYouTube.Actions.Error.FileTooLarge=\"El fitxer seleccionat és massa gran (Límit: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"S'ha produït un error amb la retransmissió: %1<br/><br/>Si l'error persisteix <a href='https://studio.youtube.com/video/%2/livestreaming'>obriu l'emissió al YouTube Studio</a> i proveu manualment.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"La transmissió està passant a l'etapa de prova, això pot trigar una mica. Torneu-ho a provar d'aquí a 10-30 segons.\"\nYouTube.Actions.EventsLoading=\"S'està carregant la llista d'esdeveniments...\"\nYouTube.Actions.EventCreated.Title=\"S'ha creat l'esdeveniment\"\nYouTube.Actions.EventCreated.Text=\"S'ha creat l’esdeveniment correctament.\"\nYouTube.Actions.Stream=\"Transmissió\"\nYouTube.Actions.Stream.ScheduledFor=\"Programat per a %1\"\nYouTube.Actions.Stream.Resume=\"Reprèn la transmissió interrompuda\"\nYouTube.Actions.Stream.YTStudio=\"Creada automàticament pel YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"S'està creant una retransmissió nova...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Cal iniciar manualment\"\nYouTube.Actions.AutoStartStreamingWarning=\"L'inici automàtic està inhabilitat per a aquest esdeveniment. Feu clic a \\\"Emet en directe\\\" per iniciar la transmissió.\"\nYouTube.Actions.AutoStopStreamingWarning=\"No podreu tornar a connectar.<br>La vostra emissió s'aturarà i deixareu d'estar en directe.\"\nYouTube.Errors.liveStreamingNotEnabled=\"La transmissió en directe no està habilitada al canal de YouTube seleccionat. <br/><br/>Consulteu <a href='https://www.youtube.com/features'>youtube.com/features</a> per obtenir més informació.\"\nYouTube.Errors.livePermissionBlocked=\"La reproducció en directe no està disponible al canal de YouTube seleccionat.<br/>Tingueu en compte que pot passar fins a 24 hores perquè la transmissió en directe estigui disponible després d’haver-la activat a la configuració del canal. <br/><br/>Consulteu <a href='https://www.youtube.com/features'>youtube.com/features</a> per obtenir més informació.\"\nYouTube.Errors.errorExecutingTransition=\"Ha fallat per un error intern. Torneu a provar en uns segons.\"\nYouTube.Errors.errorStreamInactive=\"El YouTube no està rebent dades de la vostra emissió. Verifiqueu la configuració i torneu a provar.\"\nYouTube.Errors.invalidTransition=\"L'intent de transició no és vàlid. Això pot ser degut a que la transmissió no ha acabat una transició anterior. Espereu uns segons i torneu-ho a provar.\"\nYouTube.DocksRemoval.Title=\"Neteja els acobladors de navegador heretats del YouTube\"\nYouTube.DocksRemoval.Text=\"Aquests acobladors se suprimiran per ser obsolets:\\n\\n%1\\nUtilitzeu \\\"Acobladors/Control de directes del YouTube\\\".\"\nConfigDownload.WarningMessageTitle=\"Avís\"\nFailedToStartStream.MissingConfigURL=\"No hi ha cap URL de configuració disponible per al servei actual\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"URL RTMP personalitzada no especificada\"\nFailedToStartStream.InvalidCustomConfig=\"Configuració personalitzada no vàlida\"\nFailedToStartStream.MissingCanvas=\"Falta un llenç addicional configurat\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"No s'ha pogut crear el servei de vídeo multipista\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"No s'ha pogut crear la sortida RTMP de vídeo multipista\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC no disponible.\\n\\nNo s'ha pogut trobar el tipus de codificador «%1»\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"No s'ha pogut crear el codificador de vídeo «%1» (tipus: «%2»)\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"No s'ha pogut obtenir la informació del vídeo d'OBS mentre es crea el codificador «%1» (tipus: «%2»)\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"No s'ha pogut crear el codificador d'àudio\"\nFailedToStartStream.NoRTMPURLInConfig=\"La configuració no conté l'URL RTMP(S) de destinació del flux\"\nFailedToStartStream.FallbackToDefault=\"No s'ha pogut iniciar la transmissió amb %1. Voleu tornar-ho a provar amb la configuració de codificació única?\"\nFailedToStartStream.ConfigRequestFailed=\"No s'ha pogut obtenir la configuració de %1<br><br>Error HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"S'ha rebut el valor d'estat desconegut «%1»\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nVoleu continuar en directe sense %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nVoleu continuar en directe?\"\nFailedToStartStream.MissingEncoderConfigs=\"La configuració del directe no incloïa les configuracions del codificador\"\nFailedToStartStream.InvalidEncoderConfig=\"La configuració del directe incloïa una configuració del codificador no vàlida.\"\nFailedToStartStream.StatusMissingHTML=\"La sol·licitud del directe ha retornat un error no especificat\"\nFailedToStartStream.NoConfigSupplied=\"Falta la configuració\"\nMultitrackVideo.Info=\"%1 optimitza automàticament la vostra configuració per codificar i enviar múltiples qualitats de vídeo. Si seleccioneu aquesta opció, s'enviarà %2 informació sobre el vostre ordinador i la configuració del programari.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Configuració incompatible\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 actualment no és compatible amb:\\n\\n%2\\nPer continuar en directe amb %1, desactiveu els paràmetres incompatibles:\\n\\n%3\\ni torneu a començar la transmissió.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Desactiva per aquesta transmissió i comença el directe\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Actualitza la configuració i comença la transmissió\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 no és compatible actualment amb [Àudio → General → Canals] establert a «%2», %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Àudio → General → Canals] s'ha d'establir a «%1»\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 requereix diversos paràmetres diferents per a [Àudio → General → Canals]\"\nBasic.OpenPluginManager=\"Gestor de complements\"\nPluginManager=\"Gestor de complements\"\nPluginManager.Restart=\"S'ha de reiniciar l'OBS Studio per aplicar els canvis del complement\"\nPluginManager.MissingPlugin=\"[COMPLEMENT NO TROBAT]\"\nPluginManager.Section.Discover=\"Navega\"\nPluginManager.Section.Manage=\"Instal·lat\"\nPluginManager.Section.Updates=\"Actualitzacions\"\nPluginManager.Section.Manage.Title=\"Gestiona els complements habilitats\"\n"
  },
  {
    "path": "frontend/data/locale/cs-CZ.ini",
    "content": "Language=\"Čeština\"\nApply=\"Použít\"\nCancel=\"Zrušit\"\nClose=\"Zavřít\"\nSave=\"Uložit\"\nDiscard=\"Zahodit\"\nDisable=\"Vypnuto\"\nYes=\"Ano\"\nNo=\"Ne\"\nAdd=\"Přidat\"\nRemove=\"Odebrat\"\nRename=\"Přejmenovat\"\nInteract=\"Interakce\"\nFilters=\"Filtry\"\nProperties=\"Vlastnosti\"\nMoveUp=\"Posunout nahoru\"\nMoveDown=\"Posunout dolů\"\nSettings=\"Nastavení\"\nDisplay=\"Obrazovka\"\nName=\"Název\"\nExit=\"Ukončit\"\nMixer=\"Směšovač zvuku\"\nBrowse=\"Procházet\"\nDroppedFrames=\"Ztracené snímky %1 (%2%)\"\nProjector.Open.Program=\"Otevřít projektor programu\"\nProjector.Open.Preview=\"Otevřít projektor náhledu\"\nProjector.Open.Scene=\"Otevřít projektor scény\"\nProjector.Open.Source=\"Otevřít projektor zdroje\"\nProjector.Open.Multiview=\"Otevřít Multiview\"\nProjector.Display=\"Obrazovka: %1\"\nProjector.Window=\"Nové okno\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Scéna: %1\"\nProjector.Title.Source=\"Zdroj: %1\"\nProjector.ResizeWindowToContent=\"Přizpůsobit okno obsahu\"\nClear=\"Vyčistit\"\nRevert=\"Zvrátit\"\nShow=\"Zobrazit\"\nHide=\"Skrýt\"\nUnhideAll=\"Odkrýt vše\"\nUntitled=\"Nepojmenované\"\nNew=\"Nové\"\nDuplicate=\"Duplikovat\"\nEnable=\"Povolit\"\nDisableOSXVSync=\"Zakázat macOS V-Sync\"\nResetOSXVSyncOnExit=\"Resetovat macOS V-Sync při ukončení\"\nHighResourceUsage=\"Vysoké zatížení enkodéru! Zvažte snížení požadavků v nastavení obrazu nebo použití rychlé předvolby enkodéru.\"\nTransition=\"Přechod\"\nQuickTransitions=\"Rychlé přechody\"\nFadeToBlack=\"Zeslábnutí do černé\"\nLeft=\"Vlevo\"\nRight=\"Vpravo\"\nTop=\"Nahoře\"\nBottom=\"Dole\"\nReset=\"Resetovat\"\nHours=\"Hodiny\"\nMinutes=\"Minuty\"\nSeconds=\"Sekundy\"\nDeprecated=\"Zastaralé\"\nReplayBuffer=\"Záznam do paměti\"\nImport=\"Importovat\"\nExport=\"Exportovat\"\nCopy=\"Kopírovat\"\nPaste=\"Vložit\"\nPasteReference=\"Vložit (jako referenci)\"\nPasteDuplicate=\"Vložit (jako kopii)\"\nRemuxRecordings=\"Převést nahrávky\"\nNext=\"Další\"\nBack=\"Zpět\"\nDefaults=\"Výchozí\"\nRestoreDefaults=\"Výchozí hodnoty\"\nHideMixer=\"Skrýt ve směšovači\"\nTransitionOverride=\"Přepsat přechod\"\nShowTransition=\"Zobrazit přechod\"\nHideTransition=\"Skrýt přechod\"\nNone=\"Žádný\"\nStudioMode.Preview=\"Náhled\"\nStudioMode.PreviewSceneName=\"Náhled: %1\"\nShowInMultiview=\"Zobrazit v Multiview\"\nVerticalLayout=\"Vertikální rozložení\"\nGroup=\"Skupina\"\nDoNotShowAgain=\"Znovu nezobrazovat\"\nDefault=\"(Výchozí)\"\nCalculating=\"Počítám...\"\nFullscreen=\"Celá obrazovka\"\nWindowed=\"V okně\"\nRefreshBrowser=\"Obnovit\"\nAspectRatio=\"Poměr stran <b>%1:%2</b>\"\nLockVolume=\"Zamknout hlasitost\"\nLogViewer=\"Prohlížeč záznamů\"\nShowOnStartup=\"Zobrazit při spuštění\"\nOpenFile=\"Otevřít soubor\"\nAddScene=\"Přidat scénu\"\nAddSource=\"Přidat zdroj\"\nRemoveScene=\"Odebrat vybranou scénu\"\nRemoveSource=\"Odebrat vybraný/é zdroj(e)\"\nMoveSceneUp=\"Posunout scénu nahoru\"\nMoveSceneDown=\"Posunout scénu dolů\"\nMoveSourceUp=\"Posunout zdroj(e) nahoru\"\nMoveSourceDown=\"Posunout zdroj(e) dolů\"\nSourceProperties=\"Otevřít vlastnosti zdroje\"\nSourceFilters=\"Otevřít filtry zdroje\"\nMixerToolbarMenu=\"Menu audio směšovače\"\nSceneFilters=\"Otevřít filtry scény\"\nList=\"Seznam\"\nGrid=\"Mřížka\"\nAutomatic=\"Automatické\"\nComingSoon=\"Již brzy\"\nPluginsFailedToLoad.Title=\"Chyba při načítání pluginu\"\nPluginsFailedToLoad.Text=\"Následující OBS pluginy se nepodařilo načíst:\\n\\n%1\\nAktualizujte nebo odstraňte tyto pluginy.\"\nAlreadyRunning.Title=\"OBS je již spuštěno\"\nAlreadyRunning.Text=\"OBS již běží! Pokud jste to opravdu nechtěli udělat, tak prosím ukončete ostatní běžící instance programu OBS před spuštěním nové. Pokud máte nastavenu minimalizaci do lišty, tak se prosím podívejte, zda neběží tam.\"\nAlreadyRunning.LaunchAnyway=\"Spustit tak či tak\"\nCrashHandling.Dialog.Title=\"Detekováno selhání OBS Studia\"\nCrashHandling.Labels.Text=\"OBS Studio nebylo správně ukončeno.\\n\\nSpustit v nouzovém režimu (pluginy a skripty třetích stran, WekSockety budou vypnuty)?\"\nCrashHandling.Labels.PrivacyNotice=\"Také můžete automaticky nahrát nejnovější soubor o selhání do OBSProject.<br /><br />Přeštěte si, prosím, <a href='https://obsproject.com/privacy-policy'>zásady ochrany osobních údajů</a> před nahráváním jakýkoliv souborů a věnujte zvláštní pozornost částem týkajícím se nahrávání souborů.\"\nCrashHandling.Checkbox.SendReport=\"Přečetl jsem zásady ochrany osobních údajů a souhlasím s nahráním.\"\nCrashHandling.Buttons.LaunchSafe=\"Spustit v nouzovém řežimu\"\nCrashHandling.Buttons.LaunchNormal=\"Spustit v normálním režimu\"\nCrashHandling.Errors.UploadJSONError=\"Při nahrávání nejnovějšího souboru o selhání došlo k chybě. Zkuste to, prosím, později.\"\nCrashHandling.Errors.Title=\"Chyba při nahrávání souboru o selhání\"\nSafeMode.Restart=\"Chcete restartovat OBS v nouzovém režimu (pluginy třetích stran, skripty a websockety budou zakázány)?\"\nSafeMode.RestartNormal=\"Chcete restartovat OBS v normálním režimu?\"\nChromeOS.Title=\"Nepodporovaná platforma\"\nChromeOS.Text=\"Zdá se, že OBS běží uvnitř kontejneru ChromeOS. Tato platforma není podporována.\"\nWine.Title=\"Wine bylo detekováno\"\nWine.Text=\"Spuštění OBS pomocí není podporováno a mnoho vlastností jako např. záznam, a některá zařízení, nemusejí fungovat vůbec nebo v omezené formě.<br><br>Je doporučeno používat nativní verzi OBS jako např. <a href='https://flathub.org/apps/details/com.obsproject.Studio'>naši Flatpak verzi</a> nebo z balíčku pro Váš operační systém.\"\nDockCloseWarning.Title=\"Zavření dokovatelného okna\"\nDockCloseWarning.Text=\"Právě jste zavřeli dokovatelné okno. Pokud byste ho chtěli zobrazit znovu, tak použijte položku Doky v menu.\"\nExtraBrowsers=\"Vlastní doky prohlížeče\"\nExtraBrowsers.Info=\"Přidejte doky zadáním názvu a URL a poté klikněte Použít nebo Zavřít pro otevření doků. Doky můžete přidat či odebrat kdykoliv.\"\nExtraBrowsers.DockName=\"Název doku\"\nAuth.Authing.Title=\"Přihlašování ...\"\nAuth.Authing.Text=\"Přihlašování ke službě %1 ...\"\nAuth.AuthFailure.Title=\"Přihlášení se nezdařilo\"\nAuth.AuthFailure.Text=\"Nepodařilo se nám přihlásit ke službě %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Vyžadováno přihlášení\"\nAuth.InvalidScope.Text=\"Požadavky pro přihlášení ke službě %1 se změnily. Některé funkce nemusí být dostupné.\"\nAuth.LoadingChannel.Title=\"Načítání informací o kanálu ...\"\nAuth.LoadingChannel.Text=\"Načítání informací o kanálu ze služby %1 ...\"\nAuth.LoadingChannel.Error=\"Nelze získat informace o kanálu.\"\nAuth.ChannelFailure.Title=\"Při načítání kanálu došlo k chybě\"\nAuth.ChannelFailure.Text=\"Informace o kanálu se pomocí služby %1 nepodařilo načíst\\n\\n%2: %3\"\nAuth.StreamInfo=\"Informace o vysílání\"\nTwitchAuth.Stats=\"Statistiky služby Twitch\"\nTwitchAuth.Feed=\"Informační kanál služby Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Nepovedlo se získat vysílací klíč\"\nTwitchAuth.TwoFactorFail.Text=\"OBS se nemohl připojit k vašemu Twitch účtu. Zkontrolujte, zda máte nastaveno dvoufázové ověření ve vašem <a href='https://www.twitch.tv/settings/security'>nastavení bezpečnosti</a>, jelikož je to pro vysílání nutné.\"\nRestreamAuth.Channels=\"Restream kanály\"\nCopy.Filters=\"Kopírovat filtry\"\nPaste.Filters=\"Vložit filtry\"\nBrowserPanelInit.Title=\"Načítání prohlížeče ...\"\nBrowserPanelInit.Text=\"Vyčkejte na načtení prohlížeče ...\"\nBandwidthTest.Region=\"Oblast\"\nBandwidthTest.Region.US=\"Spojené státy\"\nBandwidthTest.Region.EU=\"Evropa\"\nBandwidthTest.Region.Asia=\"Asie\"\nBandwidthTest.Region.Other=\"Jiná\"\nBasic.AutoConfig=\"Průvodce nastavením\"\nBasic.AutoConfig.ApplySettings=\"Použít\"\nBasic.AutoConfig.StartPage=\"Informace o použití\"\nBasic.AutoConfig.StartPage.SubTitle=\"Řekněte nám, k čemu chcete tento program používat\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimalizovat pro vysílání, nahrávání je druhořadé\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimalizovat pouze pro nahrávání, nebudu vysílat\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Budu používat pouze virtuální kameru\"\nBasic.AutoConfig.VideoPage=\"Nastavení obrazu\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Specifikujte nastavení obrazu, které chcete použít\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Použít aktuální (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Obrazovka %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Použít aktuální (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 nebo 30, pokud to jde, použij 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 nebo 30, ale preferuji vyšší rozlišení\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Poznámka: Základní rozlišení (plátno) není nutně stejné jako rozlišení, ve kterém budete vysílat či nahrávat. Vaše opravdové rozlišení může být několikrát zmenšeno oproti základnímu rozlišení, aby bylo sníženo využití prostředků nebo požadavků na bitrate.\"\nBasic.AutoConfig.StreamPage=\"Informace o vysílání\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Prosím zadejte své informace o vysílání\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Připojit účet (doporučeno)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Odpojit účet\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Odpojení účtu\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Tato změna proběhne okamžitě. Opravdu si přejete odpojit účet ?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Získat vysílací klíč\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Více informací\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Použít vysílací klíč\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Použít vysílací klíč (pokročilé)\"\nBasic.AutoConfig.StreamPage.Service=\"Služba\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Zobrazit vše...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Vlastní ...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Vysílací klíč\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: zadejte šifrovací frázi.\\nRTMP: zadejte klíč poskytnutý službou.\\nSRT: zadejte streamid, pokud jej služba používá.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kódovací klíč\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Připojený účet\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Odhadnout bitrate za pomocí testu rychlosti spojení (může to zabrat pár minut)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferovat hardwarové kódování\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Hardwarové kódování rapidně snižuje využití CPU, ale může vyžadovat použití vyššího bitratu k dosažení stejné kvality.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Varování o vysílání\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Test rychlosti spojení bude vysílat náhodná data obrazu bez zvuku na váš kanál. Pokud jste schopni, doporučujeme abyste dočasně vypnuli ukládání nahrávek vysílání a nastavili kanál na soukromý, dokud nebude tento test dokončen. Přejete si pokračovat?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Testovat %1\"\nBasic.AutoConfig.TestPage=\"Konečné výsledky\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Program právě provádí soubor testů pro výpočet ideálního nastavení\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testování dokončeno\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Probíhá testování rychlosti spojení, zabere to pár minut...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Nebyl nalezen žádný výstup pro protokol této služby\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Připojuji k: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Nepodařilo se připojit k žádnému serveru, zkontrolujte své připojení a zkuste to znovu.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testování rychlosti spojení pro: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testování kodéru pro vysílání, bude to minutka...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testování kodéru pro nahrávání, bude to minutka...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Nezdařilo se spuštění kodéru\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testování %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Kodér pro vysílání\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Kodér pro nahrávání\"\nBasic.AutoConfig.TestPage.Result.Header=\"Tento program určil, že tato přibližná nastavení jsou pro vás jako dělaná:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Pro použití těchto nastavení klikněte na Použít. Pro změnu v průvodci klikněte na Zpět. Pokud si přejete změnit nastavení manuálně, klikněte na Zrušit a poté otevřete Nastavení.\"\nBasic.AutoConfig.Info=\"Průvodce nastavením zjistí nejlepší nastavení podle výkonu počítače a rychlosti připojení k internetu.\"\nBasic.AutoConfig.RunAnytime=\"Tento proces může být spuštěn kdykoliv pomocí položky v menu Nástroje.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Rozlišení (škálované) pro vysílání\"\nBasic.Stats=\"Statistika\"\nBasic.Stats.CPUUsage=\"Využití CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Dostupné místo na disku\"\nBasic.Stats.MemoryUsage=\"Využití paměti\"\nBasic.Stats.AverageTimeToRender=\"Průměrný čas vykreslování snímku\"\nBasic.Stats.SkippedFrames=\"Přeskočené snímky kvůli chybě v kódování\"\nBasic.Stats.MissedFrames=\"Nevyužité snímky z důvodu chyby ve vykreslování\"\nBasic.Stats.Output.Stream=\"Vysílání\"\nBasic.Stats.Output.Recording=\"Nahrávání\"\nBasic.Stats.Status=\"Stav\"\nBasic.Stats.Status.Recording=\"Nahrává se\"\nBasic.Stats.Status.Live=\"ŽIVĚ\"\nBasic.Stats.Status.Reconnecting=\"Obnovování připojení\"\nBasic.Stats.Status.Inactive=\"Neaktivní\"\nBasic.Stats.Status.Active=\"Aktivní\"\nBasic.Stats.DroppedFrames=\"Ztracené snímky (síť)\"\nBasic.Stats.MegabytesSent=\"Celkový datový výstup\"\nBasic.Stats.DiskFullIn=\"Disk bude plný za cca.\"\nBasic.Stats.DiskFullIn.Text=\"%1 hodin(y), %2 minut(y)\"\nBasic.Stats.ResetStats=\"Resetovat statistiky\"\nResetUIWarning.Title=\"Opravdu si přejete resetovat rozhraní ?\"\nResetUIWarning.Text=\"Resetováním rozhraním budou skryty přídavné doky. Pro zobrazení těchto doků je budete muset povolit v menu doků.\\n\\nOpravdu si přejete resetovat rozhraní ?\"\nUpdater.Title=\"Aktualizace\"\nUpdater.Text=\"K dispozici je nová verze:\"\nUpdater.UpdateNow=\"Aktualizovat nyní\"\nUpdater.RemindMeLater=\"Upozornit později\"\nUpdater.Skip=\"Přeskočit verzi\"\nUpdater.NoUpdatesAvailable.Title=\"Žádná aktualizace\"\nUpdater.NoUpdatesAvailable.Text=\"Není dostupná žádná novější verze\"\nUpdater.BranchNotFound.Title=\"Kanál aktualizací byl odstraněn\"\nUpdater.BranchNotFound.Text=\"Váš vybraný kanál aktualizací již není dustupný, OBS bylo nastaveno na výchozí.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Kontrola integrity není dostupná\"\nUpdater.RepairButUpdatesAvailable.Text=\"Kontrola integrity souborů je k dispozici pouze pro nejnovější verzi. Použijte Pomoc → Zkontrolovat aktualizace pro ověření a aktualizaci OBS.\"\nUpdater.RepairConfirm.Title=\"Potvrďte kontrolu integrity\"\nUpdater.RepairConfirm.Text=\"Spuštění kontroli integrity projde soubory této instalace OBS a stáhne poškozené/upravené soubory. Může to chvíli trvat.\\n\\nPřejete si pokračovat?\"\nUpdater.FailedToLaunch=\"Nepodařilo se spustit aktualizátor\"\nQuickTransitions.SwapScenes=\"Prohodit scénu náhledu a programovou scénu po přechodu\"\nQuickTransitions.SwapScenesTT=\"Prohodí scény náhledu a programu po přechodu (pokud originální programová scéna stále existuje).\\nTato funkce nevrátí provedené změny, které byly provedeny v originální programové scéně.\"\nQuickTransitions.DuplicateScene=\"Duplikovat scénu\"\nQuickTransitions.DuplicateSceneTT=\"Při úpravě stejné scény, umožňuje úpravu pozice/viditelnosti zdrojů bez úpravy výstupu.\\nPro úpravu vlastností zdrojů bez úpravy výstupu, povolte 'Duplikovat zdroje'.\\nPo změně této hodnoty bude aktuální programová scéna resetována (pokud stále existuje).\"\nQuickTransitions.EditProperties=\"Duplikovat zdroje\"\nQuickTransitions.EditPropertiesTT=\"Při úpravě stejné scény, umožňuje úpravu vlastností zdrojů bez úpravy výstupu.\\nTato funkce může být použita pouze, pokud je zapnuto 'Duplikovat scénu'.\\nNěkteré zdroje (jako jsou zdroje záznamu či mediální zdroje) tuto funkci nepodporují a nemohou být upraveny samostatně.\\nPo změně této hodnoty bude aktuální scéna výstupu resetována (pokud stále existuje).\\n\\nVarování: Z důvodu duplikace zdrojů může tato funkce vyžadovat více systémových prostředků.\"\nQuickTransitions.HotkeyName=\"Rychlý přechod: %1\"\nBasic.AddTransition=\"Přidat nastavitelný přechod\"\nBasic.RemoveTransition=\"Odebrat nastavitelný přechod\"\nBasic.TransitionProperties=\"Vlastnosti přechodu\"\nBasic.SceneTransitions=\"Přechody scény\"\nBasic.TransitionDuration=\"Délka\"\nBasic.TogglePreviewProgramMode=\"Studiový mód\"\nBasic.EnablePreviewProgramMode=\"Povolit režim studia\"\nBasic.DisablePreviewProgramMode=\"Zakázat režim studia\"\nUndo.Undo=\"Vrátit\"\nUndo.Redo=\"Opakovat\"\nUndo.Add=\"přidání '%1'\"\nUndo.Delete=\"odstranění '%1'\"\nUndo.Rename=\"přejmenování '%1'\"\nUndo.SceneCollection.Switch=\"přepnutí na '%1'\"\nUndo.Item.Undo=\"Vrátit %1\"\nUndo.Item.Redo=\"Opakovat %1\"\nUndo.Sources.Multi=\"odstranění %1 zdroje/ů\"\nUndo.Filters=\"změny na filtru pro '%1'\"\nUndo.Filters.Paste.Single=\"vložení filtru '%1' do '%2'\"\nUndo.Filters.Paste.Multiple=\"zkopírování filtrů z '%1' do '%2'\"\nUndo.Transform=\"pozicování zdroje/ů scény '%1'\"\nUndo.Transform.Paste=\"vložení transformace do scény '%1'\"\nUndo.Transform.Rotate=\"rotaci ve scéně '%1'\"\nUndo.Transform.Reset=\"obnovení pozice ve scéně '%1'\"\nUndo.Transform.HFlip=\"vodorovné překlopení ve scéně '%1'\"\nUndo.Transform.VFlip=\"svislé překlopení ve scéně '%1'\"\nUndo.Transform.FitToScreen=\"přizpůsobení obrazovce ve scéně '%1'\"\nUndo.Transform.StretchToScreen=\"roztáhnutí na obrazovku ve scéně '%1'\"\nUndo.Transform.Center=\"vycentrování ve scéně '%1'\"\nUndo.Transform.VCenter=\"svislé vycentrování ve scéně '%1'\"\nUndo.Transform.HCenter=\"vodorovné vycentrování ve scéně '%1'\"\nUndo.Volume.Change=\"změnu hlasitosti ve scéně '%1'\"\nUndo.Volume.Mute=\"ztlumení pro '%1'\"\nUndo.Volume.Unmute=\"zrušení ztlumení pro '%1'\"\nUndo.Balance.Change=\"změnu vyvážení zvuku pro '%1'\"\nUndo.SyncOffset.Change=\"změnu zpoždění synchronizace pro '%1'\"\nUndo.MonitoringType.Change=\"změnu monitorování zvuku pro '%1'\"\nUndo.Mixers.Change=\"změnu směšovače zvuku pro '%1'\"\nUndo.ForceMono.On=\"zapnutí vynucení Mono pro '%1'\"\nUndo.ForceMono.Off=\"vypnutí vynucení Mono pro '%1'\"\nUndo.Properties=\"změnu vlastnosti pro '%1'\"\nUndo.Scene.Duplicate=\"duplikování scény '%1'\"\nUndo.ShowTransition=\"zobrazení přechodu na '%1'\"\nUndo.HideTransition=\"skrytí přechodu na '%1'\"\nUndo.ShowSceneItem=\"zobrazení '%1' ve scéně '%2'\"\nUndo.HideSceneItem=\"skrytí '%1' ve scéně '%2'\"\nUndo.ReorderSources=\"změnu pořadí zdrojů scény '%1'\"\nUndo.MoveUp=\"posunutí '%1' výše ve scéně '%2'\"\nUndo.MoveDown=\"posunutí '%1' níže ve scéně '%2'\"\nUndo.MoveToTop=\"posunutí '%1' na vršek ve scéně '%2'\"\nUndo.MoveToBottom=\"posunutí '%1' na spodek ve scéně '%2'\"\nUndo.PasteSource=\"vložení zdroje/ů do scény '%1'\"\nUndo.PasteSourceRef=\"vložení referencí zdroje do scény '%1'\"\nUndo.GroupItems=\"seskupení položek do '%1'\"\nTransitionNameDlg.Text=\"Zadejte název přechodu\"\nTransitionNameDlg.Title=\"Název přechodu\"\nTitleBar.SafeMode=\"NOUZOVÝ REŽIM\"\nTitleBar.PortableMode=\"Přenosný režim\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Scény\"\nNameExists.Title=\"Jméno již existuje\"\nNameExists.Text=\"Toto jméno již bylo použito.\"\nNoNameEntered.Title=\"Zadejte prosím platné jméno\"\nNoNameEntered.Text=\"Jméno nesmí být prázdné.\"\nConfirmStart.Title=\"Začít vysílat ?\"\nConfirmStart.Text=\"Opravdu si přejete začít vysílat ?\"\nConfirmStop.Title=\"Zastavit vysílání ?\"\nConfirmStop.Text=\"Opravdu si přejete zastavit vysílání ?\"\nConfirmStopRecord.Title=\"Zastavit nahrávání?\"\nConfirmStopRecord.Text=\"Opravdu si přejete zastavit nahrávání ?\"\nConfirmBWTest.Title=\"Spuštění testu rychlosti připojení\"\nConfirmBWTest.Text=\"Nastavili jste OBS do režimu testování připojení. Tento režim vám umožňuje otestovat vaše připojení bez toho, abyste vysílali. Poté co skončíte s testováním jej budete muset vypnout, aby vaše vysílání viděli vaši diváci.\\n\\nChcete začít s testováním ?\"\nConfirmExit.Title=\"Aktivní výstupy\"\nConfirmExit.Text=\"OBS je stále aktivní. Všechna vysílání/nahrávání budou ukončena.\"\nConfirmRemove.Title=\"Potvrzení odebrání\"\nConfirmRemove.Text=\"Opravdu si přejete odebrat '%1'?\"\nConfirmRemove.TextMultiple=\"Opravdu si přejete odebrat %1 položky ?\"\nConfirmReset.Title=\"Resetování vlastností\"\nConfirmReset.Text=\"Opravdu si přejete resetovat aktuální vlastnosti na jejich výchozí hodnoty?\"\nOutput.StartStreamFailed=\"Chyba při spouštění vysílání\"\nOutput.StartRecordingFailed=\"Chyba při spouštění nahrávání\"\nOutput.StartReplayFailed=\"Chyba při spouštění nahrávání do paměti\"\nOutput.StartVirtualCamFailed=\"Spuštění virtuální kamery se nezdařilo\"\nOutput.StartFailedGeneric=\"Nastala chyba při začátku nahrávání. Zkontrolujte, prosím, textový záznam pro další podrobnosti.\\n\\nPoznámka: Pokud používáte enkodér NVENC či AMD, zkontrolujte zda používáte aktuální verzi grafického ovladače.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Při pozastavení nelze ukládat záznamy\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Varování: Záznamy nelze ukládat, pokud je nahrávání pozastaveno.\"\nOutput.ConnectFail.Title=\"Spojení se nezdařilo\"\nOutput.ConnectFail.BadPath=\"Chybná cesta nebo adresa připojení. Zkontrolujte, prosím, správnost svých nastavení.\"\nOutput.ConnectFail.ConnectFailed=\"K serveru se nepodařilo připojit\"\nOutput.ConnectFail.InvalidStream=\"K nastavenému kanálu či klíči nelze přistoupit. Zkontrolujte, zda je vysílací klíč správný. Pokud ano, může být problém s připojením k serveru.\"\nOutput.ConnectFail.HdrDisabled=\"HDR je aktuálně vypnuto pro tento výstup.\"\nOutput.ConnectFail.Error=\"Při pokusu o připojení k serveru došlo k neočekávané chybě. Další informace v záznamovém souboru.\"\nOutput.ConnectFail.Disconnected=\"Odpojen od serveru.\"\nOutput.StreamEncodeError.Title=\"Chyba kodéru\"\nOutput.StreamEncodeError.Msg=\"Při vysílání došlo k chybě kodéru.\"\nOutput.StreamEncodeError.Msg.LastError=\"Při vysílání došlo k chybě kodéru:<br><br>%1\"\nOutput.RecordFail.Title=\"Chyba při spouštění nahrávání\"\nOutput.RecordFail.Unsupported=\"Výstupní formát není podporovaný nebo nepodporuje více než jednu zvukovou stopu. Zkontrolujte nastavení a zkuste to znovu.\"\nOutput.RecordNoSpace.Title=\"Nedostatek místa na disku\"\nOutput.RecordNoSpace.Msg=\"Pro pokračování nahrávání není dostatek místa na disku.\"\nOutput.RecordError.Title=\"Chyba při nahrávání\"\nOutput.RecordError.Msg=\"Při nahrávání došlo k nespecifikované chybě.\"\nOutput.RecordError.EncodeErrorMsg=\"Při nahrávání došlo k chybě kodéru.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Při nahrávání došlo k chybě kodéru:<br><br>%1\"\nOutput.BadPath.Title=\"Špatná cesta k souboru\"\nOutput.BadPath.Text=\"Nastavenou cestu pro nahrávání nelze otevřít. Zkontrolujte, prosím, cestu uvedenou pod Nastavení → Výstup → Nahrávání.\"\nOutput.NoBroadcast.Title=\"Není nastaveno žádné vysílání\"\nOutput.NoBroadcast.Text=\"Nejdříve musíte vysílání nastavit než můžete začít vysílat.\"\nOutput.BroadcastStartFailed=\"Nepodařilo se spustit vysílání\"\nOutput.BroadcastStopFailed=\"Nepodařilo se zastavit vysílání\"\nLogUploadDialog.Title=\"Nahrávání souboru záznamu OBS Studia\"\nLogUploadDialog.Labels.PrivacyNotice=\"Přečtěte si, prosím, <a href='https://obsproject.com/privacy-policy'>zásady ochrany osobních údajů</a> a to sekci o nahrávání souborů před nahráváním jakýkoliv souborů.\"\nLogUploadDialog.Labels.Progress=\"Probíhá nahrávání souboru záznamu. Čekejte prosím...\"\nLogUploadDialog.Labels.Description.AppLog=\"Váš soubor záznamu byl nahrán. Nyní můžete sdílet URL pro účely ladění či podpory.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Vaše hlášení o selhání bylo nahráno. Nyní můžete sdílet URL pro účely ladění či podpory.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Nahrát\"\nLogUploadDialog.Buttons.CopyURL=\"Zkopírovat URL\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analyzovat soubor záznamu\"\nLogUploadDialog.Buttons.RetryButton=\"Zkusit znovu\"\nLogUploadDialog.Errors.Template=\"Při pokusu o nahrání souboru došlo k chybě:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Nebyl nalezen žádný soubor k nahrání nebo byl prázdný.\"\nRemux.SourceFile=\"OBS nahrávka\"\nRemux.TargetFile=\"Cílový soubor\"\nRemux.Remux=\"Převést\"\nRemux.Stop=\"Zastavit převod\"\nRemux.ClearFinished=\"Odebrat dokončené položky\"\nRemux.ClearAll=\"Odebrat všechny položky\"\nRemux.OBSRecording=\"OBS nahrávka\"\nRemux.FinishedTitle=\"Převod dokončen\"\nRemux.Finished=\"Nahrávka převedena\"\nRemux.FinishedError=\"Nahrávka převedena, ale soubor nemusí být kompletní\"\nRemux.SelectRecording=\"Vyberte OBS nahrávku …\"\nRemux.SelectTarget=\"Vyberte cílový soubor …\"\nRemux.FileExistsTitle=\"Cílový soubor již existuje\"\nRemux.FileExists=\"Následující soubory již existují, chcete je nahradit?\"\nRemux.ExitUnfinishedTitle=\"Probíhá převod\"\nRemux.ExitUnfinished=\"Převod nebyl dokončen, zastavení může znemožnit další použití cílového souboru.\\nOpravdu chcete převod ukončit ?\"\nRemux.HelpText=\"Natáhněte soubory do tohoto okna k převodu nebo vyberte prázdné pole \\\"OBS nahrávka\\\" pro výběr souboru.\"\nRemux.NoFilesAddedTitle=\"Žádny soubor k převodu\"\nRemux.NoFilesAdded=\"Nebyl přidán žádný soubor k převodu. Vyberte složku obsahující jeden či více video souborů.\"\nMissingFiles=\"Chybějící soubory\"\nMissingFiles.MissingFile=\"Chybějící soubor\"\nMissingFiles.NewFile=\"Nový soubor\"\nMissingFiles.HelpText=\"Od posledního použití OBS chybí nějaké soubory.\"\nMissingFiles.Clear=\"<vymazáno>\"\nMissingFiles.NumFound=\"Nalezeno %1 z %2\"\nMissingFiles.Search=\"Vybrat složku pro hledání ...\"\nMissingFiles.SelectFile=\"Vybrat soubor ...\"\nMissingFiles.SelectDir=\"Vyberte složku, ve které chcete hledat\"\nMissingFiles.State=\"Stav\"\nMissingFiles.Missing=\"Chybí\"\nMissingFiles.Replaced=\"Nahrazen\"\nMissingFiles.Cleared=\"Vymazán\"\nMissingFiles.Found=\"Nalezen\"\nMissingFiles.AutoSearch=\"Byly nalezeny další odpovídající soubory\"\nMissingFiles.AutoSearchText=\"OBS našlo další odpovídající soubory ve vybrané složce. Přejete si je přidat ?\"\nMissingFiles.NoMissing.Title=\"Kontrola chybějících souborů\"\nMissingFiles.NoMissing.Text=\"Vypadá to, že nechybí žádné soubory.\"\nMacPermissions.MenuAction=\"Kontrola oprávnění aplikace...\"\nMacPermissions.Title=\"Kontrola oprávnění aplikace\"\nMacPermissions.Description=\"OBS Studio vyžaduje oprávnění ke zpřístupnění některých funkcí. Je doporučeno schválit tato oprávnění, ale nejsou potřebná k používání aplikace. Vždy je můžete schválit později.\"\nMacPermissions.Description.OpenDialog=\"Toto okno můžete znovu otevřít pomocí položky v menu OBS Studia.\"\nMacPermissions.AccessGranted=\"Přístup povolen\"\nMacPermissions.RequestAccess=\"Požádat o přístup\"\nMacPermissions.OpenPreferences=\"Otevřít nastavení %1\"\nMacPermissions.Item.ScreenRecording=\"Nahrávání obrazovky\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS vyžaduje toto oprávnění, aby bylo možné zachytávat vaši obrazovku.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Toto oprávnění je vyžadováno pro zachycení obsahu z webkamery nebo zachytávací karty.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS vyžaduje toto oprávnění, pokud si přejete zaznamenávat zvuk vašeho mikrofonu nebo externího zvukového zařízení.\"\nMacPermissions.Item.InputMonitoring=\"Sledování vstupu\"\nMacPermissions.Item.InputMonitoring.Details=\"Toto oprávnění je nutné, aby klávesové zkratky fungovaly, když je OBS na pozadí.\"\nMacPermissions.Item.Accessibility=\"Přístupnost\"\nMacPermissions.Item.Accessibility.Details=\"Ve starších instalacích může být OBS uvedeno v části „Zpřístupnění“ namísto „Sledování vstupu“.\"\nMacPermissions.Continue=\"Pokračovat\"\nSourceLeak.Title=\"Chyba při čistění zdroje\"\nSourceLeak.Text=\"Při změně kolekce scén se vyskytl problém a některé předchozí scény nemohly být odstraněny. Tento problém je obvykle způsoben pluginy, které správně neuvolňují prostředky. Zkontrolujte, zda jsou všechny pluginy aktuální.\\n\\nOBS Studio se nyní ukončí, aby se zabránilo případnému poškození dat.\"\nBasic.DesktopDevice1=\"Zvuk plochy\"\nBasic.DesktopDevice2=\"Zvuk plochy 2\"\nBasic.AuxDevice1=\"Mikrofon / AUX\"\nBasic.AuxDevice2=\"Mikrofon / AUX 2\"\nBasic.AuxDevice3=\"Mikrofon / AUX 3\"\nBasic.AuxDevice4=\"Mikrofon / AUX 4\"\nBasic.Scene=\"Scéna\"\nBasic.DisplayCapture=\"Záznam obrazovky\"\nBasic.Main.PreviewConextMenu.Enable=\"Povolit náhled\"\nBasic.Main.Preview.Disable=\"Vypnout náhled\"\nScaleFiltering=\"Filter škálování\"\nScaleFiltering.Point=\"Bod\"\nScaleFiltering.Bilinear=\"Bilineární\"\nScaleFiltering.Bicubic=\"Bikubický\"\nScaleFiltering.Area=\"Oblastní\"\nBlendingMethod=\"Metoda míšení\"\nBlendingMethod.Default=\"Výchozí\"\nBlendingMethod.SrgbOff=\"SRGB vypnuto\"\nBlendingMode=\"Režim míšení\"\nBlendingMode.Normal=\"Normální\"\nBlendingMode.Additive=\"Přičíst\"\nBlendingMode.Subtract=\"Odečíst\"\nBlendingMode.Screen=\"Clona\"\nBlendingMode.Multiply=\"Násobit\"\nBlendingMode.Lighten=\"Zesvětlit\"\nBlendingMode.Darken=\"Ztmavit\"\nDeinterlacing=\"Odstranění prokladu\"\nDeinterlacing.Discard=\"Zahození\"\nDeinterlacing.TopFieldFirst=\"Svrchní řádek dříve\"\nDeinterlacing.BottomFieldFirst=\"Spodní řádek dříve\"\nVolControl.SliderUnmuted=\"Posuvník hlasitosti pro '%1':\"\nVolControl.SliderMuted=\"Posuvník hlasitosti pro '%1': (aktuálně ztlumeno)\"\nVolControl.Mute=\"Ztlumit '%1'\"\nVolControl.Properties=\"Vlastnosti pro \\\"%1\\\"\"\nVolControl.UnassignedWarning.Title=\"Nepřiřazený zdroj zvuku\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" není přiřazeno ke zvukové stopě a nebude slyšet při streamování ani v nahrávkách.\\n\\nPro přiřazení zdroje zvuku ke stopě, otevřete Rozšířené vlastnosti zvuku pomocí nabídky pravého tlačítka myši nebo ozubeného tlačítka na panelu nástrojů v doku mixéru.\"\nBasic.Main.AddSceneDlg.Title=\"Přidat scénu\"\nBasic.Main.AddSceneDlg.Text=\"Prosím, zadejte jméno scény\"\nBasic.Main.DefaultSceneName.Text=\"Scéna %1\"\nBasic.Main.AddSceneCollection.Title=\"Přidání kolekce scén\"\nBasic.Main.AddSceneCollection.Text=\"Zadejte prosím jméno kolekce scén\"\nBasic.Main.RenameSceneCollection.Title=\"Přejmenování kolekce scén\"\nBasic.Main.RemigrateSceneCollection.Title=\"Aktualizovat rozlišení kolekce scén\"\nBasic.Main.RemigrateSceneCollection.Text=\"Chcete aktualizovat rozlišení kolekce scén „%1“ tak, aby odpovídalo rozlišení plátna aktuálního profilu %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Nelze aktualizovat rozlišení kolekce scén, zatímco jsou výstupy aktivní.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Nepodařilo se aktualizovat rozlišení kolekce scén. Původní rozlišení není známo.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Reset není možný: Změna rozlišení OBS selhala.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Reset není možný: Aktuální rozlišení je již základním rozlišením kolekce scén.\"\nAddProfile.Title=\"Přidání profilu\"\nAddProfile.Text=\"Zadejte prosím jméno profilu\"\nAddProfile.WizardCheckbox=\"Zobrazit Průvodce nastavením\"\nRenameProfile.Title=\"Přejmenování profilu\"\nBasic.Main.MixerRename.Title=\"Přejmenovat zdroj zvuku\"\nBasic.Main.MixerRename.Text=\"Zadejte prosím název zdroje zvuku\"\nBasic.Main.PreviewDisabled=\"Náhled je zakázán\"\nBasic.SourceSelect=\"Vytvořit/vybrat zdroj\"\nBasic.SourceSelect.CreateNew=\"Vytvořit nový\"\nBasic.SourceSelect.AddExisting=\"Přidat existující\"\nBasic.SourceSelect.AddVisible=\"Zviditelnit zdroj\"\nBasic.Main.Sources.Visibility=\"Viditelnost\"\nBasic.Main.Sources.VisibilityDescription=\"Ovládá viditelnost zdroje '%1' na plátně\"\nBasic.Main.Sources.Lock=\"Zamknout\"\nBasic.Main.Sources.LockDescription=\"Uzamkne pozici a velikost zdroje '%1' na plátně\"\nBasic.PropertiesWindow=\"Vlastnosti objektu '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (automatické: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Výběr barvy\"\nBasic.PropertiesWindow.SelectFont=\"Výběr písma\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Vyberte písmo\"\nBasic.PropertiesWindow.ConfirmTitle=\"Nastavení upraveno\"\nBasic.PropertiesWindow.Confirm=\"Chcete ponechat neuložená nastavení ?\"\nBasic.PropertiesWindow.NoProperties=\"Nejsou k dispozici žádné vlatnosti\"\nBasic.PropertiesWindow.AddFiles=\"Přidat soubory\"\nBasic.PropertiesWindow.AddDir=\"Přidat složku\"\nBasic.PropertiesWindow.AddURL=\"Přidat cestu/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Přidat složku do '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Přidat soubory do '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Přidat položku do '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Upravit položku z '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Jednoduché hodnoty FPS\"\nBasic.PropertiesView.FPS.Rational=\"Racionální hodnoty FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Platné rozsahy FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"Chcete otevřít tento odkaz ve vašem výchozím internetovém prohlížeči?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"Adresa: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Otevřít adresu\"\nBasic.InteractionWindow=\"Interakce s '%1'\"\nBasic.StatusBar.Reconnecting=\"Odpojen, další pokus o spojení za %2 sekund(y) (pokus č. %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Pokus o připojení... (pokus č. %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Spojení bylo obnoveno\"\nBasic.StatusBar.Delay=\"Zpoždění (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Zpoždění (začátek za %1 s)\"\nBasic.StatusBar.DelayStoppingIn=\"Zpoždění (zastavení za %1 s)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Zpoždění (zastavení za %1 s, začátek za %2 s)\"\nBasic.StatusBar.RecordingSavedTo=\"Nahrávka uložena do '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Záznam do paměti uložen do '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Snímek obrazovky uložen do '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Nahrávka byla automaticky převedena do '%1'\"\nBasic.Filters=\"Filtry\"\nBasic.Filters.AsyncFilters=\"Zvukové/obrazové filtry\"\nBasic.Filters.AudioFilters=\"Zvukové filtry\"\nBasic.Filters.EffectFilters=\"Filtry efektů\"\nBasic.Filters.Title=\"Filtry u '%1'\"\nBasic.Filters.AddFilter.Title=\"Název filtru\"\nBasic.Filters.AddFilter.Text=\"Zadejte název filtru\"\nBasic.TransformWindow=\"Pozicování položky scény\"\nBasic.TransformWindow.Position=\"Umístění\"\nBasic.TransformWindow.PositionX=\"Pozice X\"\nBasic.TransformWindow.PositionY=\"Pozice Y\"\nBasic.TransformWindow.Rotation=\"Otočení\"\nBasic.TransformWindow.Size=\"Velikost\"\nBasic.TransformWindow.Width=\"Šířka\"\nBasic.TransformWindow.Height=\"Výška\"\nBasic.TransformWindow.Alignment=\"Zarovnání pozice\"\nBasic.TransformWindow.BoundsType=\"Typ ohraničení\"\nBasic.TransformWindow.BoundsAlignment=\"Zarovnání v hranici\"\nBasic.TransformWindow.Bounds=\"Velikost hranice\"\nBasic.TransformWindow.BoundsWidth=\"Šířka hranice\"\nBasic.TransformWindow.BoundsHeight=\"Výška hranice\"\nBasic.TransformWindow.CropToBounds=\"Oříznout do ohraničujícího boxu\"\nBasic.TransformWindow.Crop=\"Oříznout\"\nBasic.TransformWindow.CropLeft=\"Oříznout vlevo\"\nBasic.TransformWindow.CropRight=\"Oříznout vpravo\"\nBasic.TransformWindow.CropTop=\"Oříznout nahoře\"\nBasic.TransformWindow.CropBottom=\"Oříznout dole\"\nBasic.TransformWindow.Alignment.TopLeft=\"Nahoře, vlevo\"\nBasic.TransformWindow.Alignment.TopCenter=\"Nahoře, uprostřed\"\nBasic.TransformWindow.Alignment.TopRight=\"Nahoře, vpravo\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Uprostřed, vlevo\"\nBasic.TransformWindow.Alignment.Center=\"Uprostřed\"\nBasic.TransformWindow.Alignment.CenterRight=\"Uprostřed, vpravo\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Dole, vlevo\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Dole, uprostřed\"\nBasic.TransformWindow.Alignment.BottomRight=\"Dole, vpravo\"\nBasic.TransformWindow.BoundsType.None=\"Bez ohraničení\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Pouze maximální velikost\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Škálovat do vnitřního ohraničení\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Škálovat do vnějšího ohraničení\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Škálovat do šířky ohraničení\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Škálovat do výšky ohraničení\"\nBasic.TransformWindow.BoundsType.Stretch=\"Roztáhnout do ohraničení\"\nBasic.TransformWindow.Title=\"Úprava transformaci pro '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Není vybrán žádný zdroj\"\nBasic.Main.AddSourceHelp.Title=\"Nelze přidat zdroj\"\nBasic.Main.AddSourceHelp.Text=\"Pro přidání zdroje je potřeba přidat alespoň jednu scénu.\"\nBasic.Main.Scenes=\"Scény\"\nBasic.Main.Sources=\"Zdroje\"\nBasic.Main.Source=\"Zdroj\"\nBasic.Main.Controls=\"Ovládací prvky\"\nBasic.Main.PreparingStream=\"Připravuje se ...\"\nBasic.Main.Connecting=\"Připojování...\"\nBasic.Main.StartRecording=\"Začít nahrávat\"\nBasic.Main.StartReplayBuffer=\"Spustit záznam do paměti\"\nBasic.Main.SaveReplay=\"Uložit záznam\"\nBasic.Main.StartStreaming=\"Začít vysílat\"\nBasic.Main.StartBroadcast=\"Začít vysílat\"\nBasic.Main.StartVirtualCam=\"Spustit virtuální kameru\"\nBasic.Main.StopRecording=\"Zastavit nahrávání\"\nBasic.Main.PauseRecording=\"Pozastavit nahrávání\"\nBasic.Main.UnpauseRecording=\"Pokračovat v nahrávání\"\nBasic.Main.SplitFile=\"Rozdělit soubor nahrávky\"\nBasic.Main.AddChapterMarker=\"Přidat značku kapitoly (pouze pro hybridní MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Zastavuji nahrávání...\"\nBasic.Main.StopReplayBuffer=\"Zastavit záznam do paměti\"\nBasic.Main.StoppingReplayBuffer=\"Zastavuji záznam do paměti...\"\nBasic.Main.SetupBroadcast=\"Spravovat vysílání\"\nBasic.Main.StopStreaming=\"Zastavit vysílání\"\nBasic.Main.StopBroadcast=\"Zastavit vysílání\"\nBasic.Main.AutoStopEnabled=\"(Automatické zastavení)\"\nBasic.Main.StoppingStreaming=\"Zastavuji vysílání...\"\nBasic.Main.ForceStopStreaming=\"Zastavit vysílání (bez zpoždění)\"\nBasic.Main.ShowContextBar=\"Zobrazit lištu nástrojů pro zdroje\"\nBasic.Main.HideContextBar=\"Skrýt lištu nástrojů pro zdroje\"\nBasic.Main.StopVirtualCam=\"Zastavit virtuální kameru\"\nBasic.Main.Group=\"Skupina %1\"\nBasic.Main.GroupItems=\"Seskupit vybrané\"\nBasic.Main.Ungroup=\"Rozdělit skupinu\"\nBasic.Main.GridMode=\"Režim mřížky\"\nBasic.Main.ListMode=\"Režim seznamu\"\nBasic.Main.VirtualCamConfig=\"Nastavení virtuální kamery\"\nBasic.VCam.VirtualCamera=\"Virtuální kamera\"\nBasic.VCam.OutputType=\"Typ výstupu\"\nBasic.VCam.OutputSelection=\"Výběr výstupu\"\nBasic.VCam.OutputType.Program=\"Program (Výchozí)\"\nBasic.VCam.OutputSelection.NoSelection=\"Pro tento typ nebyl vybrán žádný výstup\"\nBasic.VCam.RestartWarning=\"Pro aplikování změn bude tato virtuální kamera restartována\"\nBasic.MainMenu.File=\"Soubor (&F)\"\nBasic.MainMenu.File.Export=\"&Exportovat\"\nBasic.MainMenu.File.Import=\"&Importovat\"\nBasic.MainMenu.File.ShowRecordings=\"Zobrazit nah&rávky\"\nBasic.MainMenu.File.Remux=\"Převést nahrávky (&M)\"\nBasic.MainMenu.File.Settings=\"Na&stavení\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Zobrazit složku nastavení\"\nBasic.MainMenu.File.ShowProfileFolder=\"Zobrazit složku profilu\"\nBasic.MainMenu.File.ShowMissingFiles=\"Zkontrolovat, zda nechybí nějaké soubory\"\nBasic.MainMenu.File.Exit=\"Ukončit (&X)\"\nBasic.MainMenu.Edit=\"Upravit (&E)\"\nBasic.MainMenu.Edit.Undo=\"Zpět (&U)\"\nBasic.MainMenu.Edit.Redo=\"Znovu (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Uzamknout náh&led\"\nBasic.MainMenu.Edit.Scale=\"Škálování náhledu (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"Škálovat na okno\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Plátno (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Výstup (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Škálované (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Přiblížit\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Oddálit\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Resetovat přiblížení\"\nBasic.MainMenu.Edit.Transform=\"Pozicování (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Upravit pozici... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopírovat transformaci\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Vložit transformaci\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Obnovit pozici (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Otočit o 90 stupňů vpravo\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Otočit o 90 stupňů vlevo\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Otočit o 180 stupňů\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Překlopit vodorovně (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Překlopit s&visle\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Přizpůsobit obrazovce (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Roztáhnout na obrazovku (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Vy&centrovat\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Vycentrovat svisle\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Vycentrovat vodorovně\"\nBasic.MainMenu.Edit.Order=\"P&ořadí\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Posunout výše (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Posunout níže (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Posunout na vršek (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Posunout na spodek (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Rozšířené vl&astnosti zvuku\"\nBasic.MainMenu.View=\"Zobrazit (&V)\"\nBasic.MainMenu.View.Toolbars=\"Liš&ty nástrojů\"\nBasic.MainMenu.View.ListboxToolbars=\"Dokované lišty nástrojů\"\nBasic.MainMenu.View.ContextBar=\"Lišta nástrojů pro zdroje\"\nBasic.MainMenu.View.SourceIcons=\"&Ikony zdrojů\"\nBasic.MainMenu.View.StatusBar=\"&Stavový řádek\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Rozhraní přes celou obrazovku\"\nBasic.MainMenu.View.ResetUI=\"&Resetovat rozhraní\"\nBasic.MainMenu.View.AlwaysOnTop=\"Vždy n&avrchu\"\nBasic.MainMenu.View.SceneListMode=\"Režim seznamu scén\"\nBasic.MainMenu.Docks=\"&Doky\"\nBasic.MainMenu.Docks.ResetDocks=\"&Resetovat doky\"\nBasic.MainMenu.Docks.LockDocks=\"Zamknout doky (&L)\"\nBasic.MainMenu.Docks.SideDocks=\"Doky přes celou výšku (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Vlastní doky prohlížeče... (&C)\"\nBasic.MainMenu.Import=\"Importovat...\"\nBasic.MainMenu.Export=\"Exportovat...\"\nBasic.MainMenu.SceneCollection=\"Kolekce &scén\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Importovat profil\"\nBasic.MainMenu.Profile.Export=\"Exportovat profil\"\nBasic.MainMenu.SceneCollection.Import=\"Importovat kolekci scén\"\nBasic.MainMenu.SceneCollection.Export=\"Exportovat kolekci scén\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Resetovat základní rozlišení\"\nBasic.MainMenu.SceneCollection.Migrate=\"Nastavit základní rozlišení\"\nBasic.MainMenu.Profile.Exists=\"Tento profil již existuje\"\nBasic.MainMenu.Tools=\"Nás&troje\"\nBasic.MainMenu.Help=\"Pomoc (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portál pomoci\"\nBasic.MainMenu.Help.Website=\"Navštívit &web\"\nBasic.MainMenu.Help.Discord=\"Připojit se na &Discord server\"\nBasic.MainMenu.Help.WhatsNew=\"Co je nového\"\nBasic.MainMenu.Help.Logs=\"Soubory záznamu (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Zobrazit &soubory záznamu\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Nahrát aktuální soubor záznamu (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Nahrát &předchozí soubor záznamu\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Zobrazit aktuální záznam (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Novinky\"\nBasic.MainMenu.Help.CheckForUpdates=\"Zkontrolovat aktualizace\"\nBasic.MainMenu.Help.Repair=\"Zkontrolovat integritu souborů\"\nBasic.MainMenu.Help.RestartSafeMode=\"Restartovat v nouzovém režimu\"\nBasic.MainMenu.Help.RestartNormal=\"Restartovat v normálním režimu\"\nBasic.MainMenu.Help.CrashLogs=\"Hlášení o pádech (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Zobrazit hlášení o pádech (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Nahrát &předchozí hlášení o pádu\"\nBasic.MainMenu.Help.About=\"O &aplikaci\"\nBasic.Settings.ProgramRestart=\"Pro projevení nastavení je potřeba restartovat aplikaci.\"\nBasic.Settings.ConfirmTitle=\"Potvrzení změn\"\nBasic.Settings.Confirm=\"Některé změny nejsou uložené. Chcete je uložit nyní ?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 ovládá některá nastavení vysílání\"\nBasic.Settings.General=\"Obecné\"\nBasic.Settings.General.Language=\"Jazyk\"\nBasic.Settings.General.Updater=\"Aktualizace\"\nBasic.Settings.General.UpdateChannel=\"Kanál aktualizací\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Zakázán)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Výchozí)\"\nBasic.Settings.General.EnableAutoUpdates=\"Automaticky kontrolovat aktualizace při spuštění\"\nBasic.Settings.General.OpenStatsOnStartup=\"Otevřít okno statistik při spuštění\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Skrýt okna OBS ze záznamu obrazovky\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Toto nastavení skryje všechny ne-projektorová okna OBS Studia ze záznamu obrazovky pomocí OBS nebo jiných aplikací,\\njako např. aplikace pro konference, sdílení obrazovky, vzdálenou podporu, snímky obrazovky a další software pro záznam.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Povolením tohoto nastavení skryjete všechny ne-projektorová okna OBS Studia ze záznamu obrazovky pomocí OBS nebo jiných aplikací, jako např. aplikace pro konference, sdílení obrazovky, vzdálenou podporu, snímky obrazovky a další software pro záznam.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Vyžadovat potvrzení pro spuštění vysílání\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Vyžadovat potvrzení pro ukončení vysílání\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Vyžadovat potvrzení pro ukončení nahrávání\"\nBasic.Settings.General.Projectors=\"Projektory\"\nBasic.Settings.General.HideProjectorCursor=\"Skrýt kurzor přes projektor\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Zobrazovat projektory tak, aby vždy byly navrchu\"\nBasic.Settings.General.Snapping=\"Přichycování zdrojů\"\nBasic.Settings.General.ScreenSnapping=\"Přichytávat zdroje k okraji obrazovky\"\nBasic.Settings.General.CenterSnapping=\"Přichytávat zdroje k vertikálnímu a horizontálnímu středu\"\nBasic.Settings.General.SourceSnapping=\"Přichytávat zdroje k ostatním zdrojům\"\nBasic.Settings.General.SnapDistance=\"Citlivost přichycení\"\nBasic.Settings.General.SpacingHelpers=\"Zobrazit vodítka pro zarovnání pixelů\"\nBasic.Settings.General.RecordWhenStreaming=\"Automaticky nahrávat při vysílání\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Pokračovat v nahrávání i po zastavení vysílání\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Automaticky zapnout záznam do paměti se začátkem vysílání\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Ponechat záznam do paměti aktivní i po ukončení vysílání\"\nBasic.Settings.General.SysTray=\"Systémová lišta\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimalizovat do systémové lišty při spuštění\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Vždy minimalizovat do systémové lišty místo hlavního panelu\"\nBasic.Settings.General.SaveProjectors=\"Ukládat projektory při ukončení\"\nBasic.Settings.General.CloseExistingProjectors=\"Omezit na jeden celoobrazovkový projektor na obrazovku\"\nBasic.Settings.General.Preview=\"Náhled\"\nBasic.Settings.General.OverflowHidden=\"Skrýt přetečení\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Zobrazit přetečení\"\nBasic.Settings.General.OverflowSelectionHidden=\"Zobrazit přetečení, i když zdroj není viditelný\"\nBasic.Settings.General.Importers=\"Importéři\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Projít známé lokace pro kolekce scén při importování\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Přejít na scénu po dvojitém kliknutí\"\nBasic.Settings.General.StudioPortraitLayout=\"Povolit rozložení na výšku (portrét)\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Zobrazovat značky pod okny ve Studiovém módu\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Kliknutí pro přechod mezi scénami\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Zobrazovat názvy scén\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Vyznačovat viditelné oblasti (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Rozložení Multiview\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontálně, nahoře (8 scén)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontálně, dole (8 scén)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertikálně, vlevo (8 scén)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertikálně, vpravo (8 scén)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontálně, nahoře (18 scén)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontálně, nahoře (24 scén)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Pouze scény (4 scény)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Pouze scény (9 scén)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Pouze scény (16 scén)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Pouze scény (25 scén)\"\nBasic.Settings.General.ChannelName.stable=\"Stabilní\"\nBasic.Settings.General.ChannelDescription.stable=\"Nejnovější stabilní vydání\"\nBasic.Settings.General.ChannelName.beta=\"Beta / kandidáti vydání\"\nBasic.Settings.General.ChannelDescription.beta=\"Potenciálně nestabilní předběžné verze\"\nBasic.Settings.Appearance=\"Vzhled\"\nBasic.Settings.Appearance.General=\"Obecné\"\nBasic.Settings.Appearance.General.Theme=\"Motiv\"\nBasic.Settings.Appearance.General.Variant=\"Styl\"\nBasic.Settings.Appearance.General.NoVariant=\"Nejsou dostupné žádné styly\"\nBasic.Settings.Appearance.FontScale=\"Velikost písma\"\nBasic.Settings.Appearance.Density=\"Hustota\"\nBasic.Settings.Appearance.Classic=\"Klasický\"\nBasic.Settings.Appearance.Compact=\"Kompaktní\"\nBasic.Settings.Appearance.Normal=\"Normální\"\nBasic.Settings.Appearance.Comfortable=\"Komfortní\"\nBasic.Settings.Appearance.OptionsWarning=\"Některé možnosti vhledu nejsou dostupné u tohoto stylu.\"\nBasic.Settings.Stream=\"Vysílání\"\nBasic.Settings.Stream.Destination=\"Cíl\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Použít přihlášení\"\nBasic.Settings.Stream.Custom.Username=\"Uživatelské jméno\"\nBasic.Settings.Stream.Custom.Password=\"Heslo\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: zadejte srp_username.\\nRTMP: zadejte jméno.\\nSRT: nepoužito.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: zadejte srp_password.\\nRTMP: zadejte heslo.\\nSRT: zadejte šifrovací frázi.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Zapnout režim testování připojení\"\nBasic.Settings.Stream.TTVAddon=\"Rozšíření pro Twitch chat\"\nBasic.Settings.Stream.TTVAddon.None=\"Žádné\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV a FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Chybějící nastavení vysílání\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Otevřít nastavení\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Chybí vysílací klíč a adresa.\\n\\nPro nastavení klíče a adresy otevřete nastavení na záložce 'Vysílání'.\"\nBasic.Settings.Stream.MissingUrl=\"Chybí vysílací adresa.\\n\\nPro nastavení adresy otevřete nastavení na záložce 'Vysílání'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Chybí vysílací klíč.\\n\\nPro nastavení klíče otevřete nastavení na záložce 'Vysílání'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorovat doporučená nastavení vysílací služby\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Přepsat doporučená nastavení\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Varování: Ignorování limitací služby může způsobit sníženou kvalitu vysílání nebo úplně zabránit vysílání.\\n\\nChcete pokračovat ?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maximální bitrate videa: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maximální bitrate zvuku: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maximální rozlišení: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maximální FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Použít vlastní server ...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Vlastní server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Povolit %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Max. propustnost pro vysílání\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Autom.\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Max. počet stop obrazu\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Autom.\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Povolit ukládání vysílání do FLV souboru (použije jednoduché nastavení pro nahrávání)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Přepis nastavení (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Povolit přepis nastaveníí\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Video s více stopami\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Dodatečné plátno\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Simulcast umožňuje enkódovat a odesílat obraz ve vícero kvalitách. <a href='https://obsproject.com/kb/whip-streaming-guide'>Více informací</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Počet vrstev\"\nBasic.Settings.Stream.AdvancedOptions=\"Rozšířená nastavení\"\nBasic.Settings.Output=\"Výstup\"\nBasic.Settings.Output.Format=\"Formát nahrávání\"\nBasic.Settings.Output.Format.hMP4=\"Hybridní MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hybridní MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmentované MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmentované MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmentované MOV zapisuje záznam po částech a nevyžaduje stejnou finalizaci jako tradiční soubory MOV.\\nToto zaručí, že bude možné soubor přehrát i přes přerušení zápisu souboru na disk, třeba z důvodu BSOD nebo ztráty napájení.\\n\\nToto nemusí být kompatibilní se všemi přehrávači a editory. V případě potřeby použijte Soubor → Převést nahrávky pro převod souboru do kompatibilnějšího formátu.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmentované MP4 zapisuje záznam po částech a nevyžaduje stejnou finalizaci jako tradiční soubory MP4.\\nToto zaručí, že bude možné soubor přehrát i přes přerušení zápisu souboru na disk, třeba z důvodu BSOD nebo ztráty napájení.\\n\\nToto nemusí být kompatibilní se všemi přehrávači a editory. V případě potřeby použijte Soubor → Převést nahrávky pro převod souboru do kompatibilnějšího formátu.\"\nBasic.Settings.Output.Encoder.Video=\"Enkodér videa\"\nBasic.Settings.Output.Encoder.Audio=\"Enkodér zvuku\"\nBasic.Settings.Output.SelectDirectory=\"Vyberte složku pro nahrávání\"\nBasic.Settings.Output.DynamicBitrate=\"Dynamicky měnit bitrate v závislosti na zahlcení sítě\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dynamicky měnit bitrate v závislosti na zahlcení sítě (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Místo zahazování snímků, pro snížení zahlcení sítě, bude průběžně měněn bitrate.\\n\\nMějte na vědomí, že se může zvýšit odezva pro diváky, pokud dojde k náhlému zahlcení sítě.\\nPo snížení bitratu to může trvat pár minut, než se zase navýší.\\n\\nZatím je podporováno pouze RTMP.\"\nBasic.Settings.Output.Mode=\"Režim výstupu\"\nBasic.Settings.Output.Mode.Simple=\"Jednoduché\"\nBasic.Settings.Output.Mode.Adv=\"Rozšířené\"\nBasic.Settings.Output.Mode.FFmpeg=\"Výstup FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Povolit záznam do paměti\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maximální čas na opakování\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maximum paměti\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Přibližné využití paměti: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Varování: Odhadované využití paměti %1 MiB je větší než doporučené maximum %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Nedokáži odhadnout využití paměti. Nastavte, prosím, maximální využití paměti.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Přípona souboru záznamu do paměti\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Přípona\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Záznam do paměti není možné použít zárovneň s typem záznamu Vlastní výstup (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Cesta pro nahrávání\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Nahrávací kvalita\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Stejná jako vysílaná\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Vysoká kvalita, střední velikost souboru\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Nerozeznatelný pokles kvality, velké soubory\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Lossless, obrovské soubory\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Varování: Bitrate vysílaného videa bude nastaven na %1, což je horní mez pro aktuální vysílací službu.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Varování: Bitrate vysílaného zvuku bude nastaven na %1, což je horní mez pro aktuální vysílací službu.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Varování: Nahrávání není možné pozastavit, pokud je nahrávací kvalita nastavena na \\\"Stejná jako vysílaná\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Varování: Aktuálně vybraný formát nahrávání není kompatibilní s vybranými enkodéry vysílání.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Varování: Nahrávání se softwarovým enkodérem v jiné kvalitě než je vysílaná bude CPU využívat více, pokud budete vysílat a nahrávat současně.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Varování: Při použití této kvality budou výsledné nahrávky obrovské! Při použití vysokého rozlišení a snímkování mohou využít až 7 GB diskového prostoru za minutu nahrávky. Tato kvalita není doporučena pro dlouhé nahrávky, pokud nemáte opravdu velký volný prostor na disku. Při použití této kvality není dostupný záznam do paměti.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Opravdu chcete použít tuto kvalitu ?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Varování nastavené kvality!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Softwarový (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Hardwarový (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Hardwarový (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Hardwarový (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Hardwarový (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Hardwarový (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Hardwarový (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Hardwarový (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Hardwarový (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Hardwarový (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Hardwarový (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Softwarový (x264 předvolba nízkého zatížení CPU, větší soubory)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (výchozí)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Stopa Twitch VOD (používá stopu 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Zvuková stopa\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Nekompatibilní rozlišení/FPS\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Tato vysílací služba nepodporuje aktuálně vybrané rozlišení anebo FPS. Budou změněny na nejbližší kompatibilní hodnoty:\\n\\n%1\\n\\nOpravdu chcete pokračovat ?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Rozlišení: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Nepodporovaný enkodér\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Streamovací služba \\\"%1\\\" nepodporuje enkodér \\\"%2\\\". Enkodér bude změněn na \\\"%3\\\".\\n\\nChcete pokračovat?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Streamovací služba \\\"%1\\\" nepodporuje enkodéry \\\"%2\\\" a \\\"%3\\\". Tyto enkodéry budou změněny na \\\"%4\\\" a \\\"%5\\\".\\n\\nChcete pokračovat?\"\nBasic.Settings.Output.VideoBitrate=\"Bitrate videa\"\nBasic.Settings.Output.AudioBitrate=\"Bitrate zvuku\"\nBasic.Settings.Output.Reconnect=\"Automaticky obnovit spojení\"\nBasic.Settings.Output.RetryDelay=\"Doba opakování\"\nBasic.Settings.Output.MaxRetries=\"Maximální počet pokusů\"\nBasic.Settings.Output.Advanced=\"Povolit vlastní nastavení enkodéru (pokročilé)\"\nBasic.Settings.Output.EncoderPreset=\"Předvolba enkodéru\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (nízké využití CPU, nejnižší kvalita)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (výchozí) (střední využití CPU, standardní kvalita)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (vysoké využití CPU, vysoká kvalita)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Vlastní nastavení enkodéru\"\nBasic.Settings.Output.CustomMuxerSettings=\"Vlastní nastavení muxeru\"\nBasic.Settings.Output.NoSpaceFileName=\"Generovat název souboru bez mezery\"\nBasic.Settings.Output.Adv.Rescale=\"Přeškálovat výstup\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Zakázáno\"\nBasic.Settings.Output.Adv.AudioTrack=\"Zvuková stopa\"\nBasic.Settings.Output.Adv.Streaming=\"Vysílání\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Nastavení vysílání\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Stopa 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Stopa 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Stopa 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Stopa 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Stopa 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Stopa 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Stopa Twitch VOD\"\nBasic.Settings.Output.Adv.Encoder=\"Nastavení enkodéru\"\nBasic.Settings.Output.Adv.Recording=\"Nahrávání\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Nastavení nahrávání\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Typ záznamu\"\nBasic.Settings.Output.Adv.Recording.Type=\"Typ\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standardní\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Vlastní výstup (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Použít enkodér vysílání)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Formát názvu souborů\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Přepsat existující\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Vlastní výstup (FFmpeg) neobsahuje žádné ochrany.\\nJste si jisti, že chcete použít tento typ misto \\\"Standardního\\\"?\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Typ výstupu FFmpegu\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Výstup do URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Výstup do souboru\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Běžné formáty nahrávání\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Všechny soubory\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Cesta k souboru či URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Formát kontejneru\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Zvuk\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Obraz\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Výchozí formát\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Popis formátu kontejneru\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Kodek rozluštěný podle cesty souboru či URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Výchozí enkodér\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Zakázat enkodér\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Obrazový enkodér\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Nastavení enkodéru obrazu (pokud existuje)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Enkodér zvuku\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Nastavení enkodéru zvuku (pokud existuje)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Nastavení směšovače (pokud existuje)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Interval klíčových snímků (snímky)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Zobrazit všechny kodeky (i když je možné, že nejsou kompatibilní)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Nastavení FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Automatické dělení souborů\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Rozdělit podle času\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Rozdělit podle velikosti souboru\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Pouze ruční rozdělování\"\nBasic.Settings.Output.SplitFile.Time=\"Čas pro rozdělení\"\nBasic.Settings.Output.SplitFile.Size=\"Velikost pro rozdělení\"\nScreenshot=\"Výstup snímků obrazovky\"\nScreenshot.SourceHotkey=\"Vytvořit snímek obrazovky vybraného zdroje\"\nScreenshot.StudioProgram=\"Snímek obrazovky (program)\"\nScreenshot.Preview=\"Snímek obrazovky (náhled)\"\nScreenshot.Scene=\"Snímek obrazovky (scéna)\"\nScreenshot.Source=\"Snímek obrazovky (zdroj)\"\nFilenameFormatting.TT.CCYY=\"Rok, čtyři číslice\"\nFilenameFormatting.TT.YY=\"Rok, poslední dvě číslice (00-99)\"\nFilenameFormatting.TT.MM=\"Měsíc jako číslo (01-12)\"\nFilenameFormatting.TT.DD=\"Den v měsíci, s nulou na začátku (01-31)\"\nFilenameFormatting.TT.hh=\"Hodina ve 24h formátu (00-23)\"\nFilenameFormatting.TT.mm=\"Minuta (00-59)\"\nFilenameFormatting.TT.ss=\"Sekunda (00-59)\"\nFilenameFormatting.TT.Percent=\"Znak %\"\nFilenameFormatting.TT.a=\"Zkrácený název dne v týdnu\"\nFilenameFormatting.TT.A=\"Celý název dne v týdnu\"\nFilenameFormatting.TT.b=\"Zkrácený název měsíce\"\nFilenameFormatting.TT.B=\"Celý název měsíce\"\nFilenameFormatting.TT.d=\"Den v měsíci, s nulou na začátku (01-31)\"\nFilenameFormatting.TT.H=\"Hodina ve 24h formátu (00-23)\"\nFilenameFormatting.TT.I=\"Hodina ve 12h formátu (01-12)\"\nFilenameFormatting.TT.m=\"Měsíc jako číslo (01-12)\"\nFilenameFormatting.TT.M=\"Minuta (00-59)\"\nFilenameFormatting.TT.p=\"Označení AM nebo PM\"\nFilenameFormatting.TT.s=\"Čas v sekundách od epochy systému UNIX\"\nFilenameFormatting.TT.S=\"Sekunda (00-59)\"\nFilenameFormatting.TT.y=\"Rok, poslední dvě číslice (00-99)\"\nFilenameFormatting.TT.Y=\"Rok\"\nFilenameFormatting.TT.z=\"Odchylka časového pásma od UTC podle ISO 8601\"\nFilenameFormatting.TT.Z=\"Název nebo zkratka časového pásma\"\nFilenameFormatting.TT.FPS=\"Snímky za sekundu\"\nFilenameFormatting.TT.CRES=\"Základní rozlišení (plátno)\"\nFilenameFormatting.TT.ORES=\"Výstupní (škálované) rozlišení\"\nFilenameFormatting.TT.VF=\"Formát videa\"\nBasic.Settings.Video=\"Obraz\"\nBasic.Settings.Video.Adapter=\"Grafická karta\"\nBasic.Settings.Video.BaseResolution=\"Základní rozlišení (plátno)\"\nBasic.Settings.Video.ScaledResolution=\"Výstupní (škálované) rozlišení\"\nBasic.Settings.Video.DownscaleFilter=\"Škálovací filter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Rozlišení se shodují, škálování není potřeba]\"\nBasic.Settings.Video.FPSCommon=\"Běžné hodnoty FPS\"\nBasic.Settings.Video.FPSInteger=\"Číselná hodnota FPS\"\nBasic.Settings.Video.FPSFraction=\"Dílčí hodnota FPS\"\nBasic.Settings.Video.Numerator=\"Čitatel\"\nBasic.Settings.Video.Denominator=\"Jmenovatel\"\nBasic.Settings.Video.Renderer=\"Vykreslovač\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Experimentální)\"\nBasic.Settings.Video.InvalidResolution=\"Chybná hodnota rozlišení. Správný formát je [šířka]x[výška] (např. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Obrazový výstup je zapnutý. Pro změnu nastavení vypněte všechny výstupy.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineární (Nejrychlejší, ale rozmazané při škálování)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bikubické (Ostré při škálování, 16 vzorků)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Ostré při škálování, 36 vzorků)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Oblast (vážený součet, 4/6/9 vzorků)\"\nBasic.Settings.Audio=\"Zvuk\"\nBasic.Settings.Audio.SampleRate=\"Vzorkovací frekvence\"\nBasic.Settings.Audio.Channels=\"Kanály\"\nBasic.Settings.Audio.Meters=\"Měřiče\"\nBasic.Settings.Audio.MeterDecayRate=\"Rychlost rozpadu\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rychle\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Střední (typ I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Pomalu (typ II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Typ měřiče špičky\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Špička vzorky\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Pravá špička (Vyšší využití CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"VAROVÁNÍ: Prostorový zvuk je zapnut.\"\nBasic.Settings.Audio.MultichannelWarning=\"Předtím než začnete vysílat si zkontrolujte, zda vaše vysílací služba podporuje příjem a přehrávání prostorového zvuku. Facebook 360 Live je příkladem služby, která jej plně podporuje; YouTube Live podporuje příjem 5.1 kanálového zvuku (a přehrávání na televizích).\\n\\nOBS filtry zvuku jej plně podporují, ale podpora u pluginu VST není garantována.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Povolit prostorový zvuk?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Jste si jisti, že chcete povolit prostorový zvuk?\"\nBasic.Settings.Audio.Devices=\"Globální zvuková zařízení\"\nBasic.Settings.Audio.DesktopDevice=\"Zvuk plochy\"\nBasic.Settings.Audio.DesktopDevice2=\"Zvuk plochy 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/AUX\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/AUX 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/AUX 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/AUX 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Povolit Ztlumit podržením\"\nBasic.Settings.Audio.PushToMuteDelay=\"Zpoždění\"\nBasic.Settings.Audio.EnablePushToTalk=\"Povolit Naslouchat podržením\"\nBasic.Settings.Audio.PushToTalkDelay=\"Zpoždění\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Zařízení není připojeno nebo není k dispozici]\"\nBasic.Settings.Audio.Disabled=\"Vypnuto\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Nízko-odezvový mód pro vyrovnávací paměť zvuku (pro Decklink/NDI výstupy)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"VAROVÁNÍ: Nízko-odezvový mód pro vyrovnávací paměť zvuku je zapnut.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Nízko-odezvový mód pro vyrovnávací paměť zvuku může způsobit zasekávání zvuku nebo může přestat přehrávat z některých zdrojů.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Zapnout nízko-odezvový mód pro vyrovnávací paměť zvuku?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Opravdu si přejete zapnout nízko-odezvový mód pro vyrovnávací paměť zvuku?\"\nBasic.Settings.Accessibility=\"Přístupnost\"\nBasic.Settings.Accessibility.ColorOverrides=\"Použít jiné barvy\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Okraj zdroje (vybraný)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Okraj zdroje (oříznutí)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Okraj zdroje (při najetí)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Pruh hlasitosti směšovače (-60 až -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Pruh hlasitosti směšovače (-20 až -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Pruh hlasitosti směšovače (-9 až 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Pruh hlasitosti směšovače (-60 až -20dB) (aktivní)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Pruh hlasitosti směšovače (-20 až -9dB) (aktivní)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Pruh hlasitosti směšovače (-9 až 0dB) (aktivní)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Předvolba barvy\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Výchozí\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Vlastní\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternativní pro barvoslepé\"\nBasic.Settings.Advanced=\"Rozšířené\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Zobrazit varování o aktivních výstupech při ukončení\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Priorita procesu\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Vysoká\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Vyšší než normální\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normální\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Menší než normální\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Nečinný\"\nBasic.Settings.Advanced.FormatWarning=\"Varování: Jiné formáty barev, než NV12/P010 jsou primárně určeny pro nahrávání a nejsou doporučeny pro vysílání. Použití při vysílání může zvýšit využití CPU kvůli konverzi formátu barev.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Varování: Formáty s vysokou přesností jsou častěji používány s HDR spektry.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Varování: Rec .2100 by mělo používat formát s větší přesností.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Formát barev\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 roviny)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Barevný prostor\"\nBasic.Settings.Advanced.Video.ColorRange=\"Rozsah barev\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Omezený\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Plné\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR úroveň bílé\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nominální špičková úroveň HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Sledovací zařízení\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Výchozí\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Zakázat Windows výchozí utlumování zvuku\"\nBasic.Settings.Advanced.StreamDelay=\"Zpoždění vysílání\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Délka\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Zachovat zpoždění při obnovení spojení (zvýšení zpoždění)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Přibližné využití paměti: %1 MB\"\nBasic.Settings.Advanced.Network=\"Síť\"\nBasic.Settings.Advanced.Network.Disabled=\"Aktuálně vybraný protokol vysílání nepodporuje změnu nastavení sítě.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Svázat s adresou\"\nBasic.Settings.Advanced.Network.IPFamily=\"Rodina IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Povolit síťové optimalizace\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Povolit TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Bude se snažit přizpůsobit RTMP výstup tak, aby byl přívětivější k dalším aplikacím, které jsou citlivé na odezvu a to regulací rychlosti odesílání.\\nMůže zvýšit počet zahozených snímků na nestabilních připojeních.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Chování klávesových zkratek\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nikdy nezakazovat klávesové zkratky\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Zakázat klávesové zkratky, když je hlavní okno aktivní\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Zakázat klávesové zkratky, když je hlavní okno neaktivní\"\nBasic.Settings.Advanced.AutoRemux=\"Automaticky převést do %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(nahrávat jako mkv)\"\nBasic.AdvAudio=\"Rozšířené vlastnosti zvuku\"\nBasic.AdvAudio.ActiveOnly=\"Pouze aktivní zdroje\"\nBasic.AdvAudio.Name=\"Název\"\nBasic.AdvAudio.Volume=\"Hlasitost\"\nBasic.AdvAudio.VolumeSource=\"Hlasitost pro '%1'\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix pro '%1'\"\nBasic.AdvAudio.Balance=\"Vyvážení\"\nBasic.AdvAudio.BalanceSource=\"Vyvážení pro '%1'\"\nBasic.AdvAudio.SyncOffset=\"Zpoždění synchronizace\"\nBasic.AdvAudio.SyncOffsetSource=\"Synchronizační odsazení pro '%1'\"\nBasic.AdvAudio.Monitoring=\"Monitorování zvuku\"\nBasic.AdvAudio.Monitoring.None=\"Monitorování vypnuto\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Pouhé monitorování (ztlumit výstup)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitorovat a odesílat na výstup\"\nBasic.AdvAudio.MonitoringSource=\"Monitorování zvuku pro '%1'\"\nBasic.AdvAudio.AudioTracks=\"Stopy\"\nBasic.Settings.Hotkeys=\"Zkratky\"\nBasic.Settings.Hotkeys.Pair=\"Kombinace kláves je sdílená s '%1' funguje jako přepínač\"\nBasic.Settings.Hotkeys.Filter=\"Filtrovat\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrovat podle klávesové zkratky\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Tato klávesová zkratka je použita pro jednu či více dalších akcí, kliknutím zobrazíte konflikty\"\nBasic.Settings.Hotkeys.PleaseWait=\"Načítání zkratek, čekejte prosím...\"\nBasic.Hotkeys.SelectScene=\"Přepnout na scénu\"\nBasic.SystemTray.Show=\"Zobrazit\"\nBasic.SystemTray.Hide=\"Skrýt\"\nBasic.SystemTray.Message.Reconnecting=\"Odpojen. Obnovuji spojení...\"\nHotkeys.Left=\"Vlevo\"\nHotkeys.Right=\"Vpravo\"\nHotkeys.Up=\"Nahoru\"\nHotkeys.Down=\"Dolů\"\nHotkeys.Space=\"Mezerník\"\nHotkeys.NumpadNum=\"Číslice %1\"\nHotkeys.NumpadMultiply=\"Číselník - Násobení\"\nHotkeys.NumpadDivide=\"Číselník - Dělení\"\nHotkeys.NumpadAdd=\"Číselní - Sčítání\"\nHotkeys.NumpadSubtract=\"Číselník - Odčítání\"\nHotkeys.NumpadDecimal=\"Číselník - Čárka\"\nHotkeys.AppleKeypadNum=\"%1 (Číselník)\"\nHotkeys.AppleKeypadMultiply=\"* (Číselník)\"\nHotkeys.AppleKeypadDivide=\"/ (Číselník)\"\nHotkeys.AppleKeypadAdd=\"+ (Číselník)\"\nHotkeys.AppleKeypadSubtract=\"- (Číselník)\"\nHotkeys.AppleKeypadDecimal=\", (Číselník)\"\nHotkeys.AppleKeypadEqual=\"= (Číselník)\"\nHotkeys.MouseButton=\"Myš %1\"\nMute=\"Ztlumit\"\nUnmute=\"Odtlumit\"\nPush-to-mute=\"Ztlumit podržením\"\nPush-to-talk=\"Naslouchat podržením\"\nSceneItemShow=\"Zobrazit '%1'\"\nSceneItemHide=\"Skrýt '%1'\"\nOutputWarnings.NoTracksSelected=\"Musíte vybrat alespoň jednu stopu\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Chyba nastavení výstupu\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Pro každý výstup musí být vybrána alespoň jedna zvuková stopa.\"\nOutputWarnings.MP4Recording=\"Varování: Nahrávky uložené v MP4/MOV nebude možné obnovit, pokud soubor nemohl být dokončen (např. po BSOD, výpadku napájení atp.). Pokud chcete nahrávat více zvukových stop, promyslete použití MKV a poté převedení do MP4/MOV (Soubor → Převést nahrávky)\"\nOutputWarnings.CannotPause=\"Varování: Nahrávání není možné pozastavit, pokud je kodér pro nahrávání nastaven na \\\"(Použít enkodér vysílání)\\\"\"\nOutputWarnings.CodecIncompatible=\"Vybraný enkodér zvuku či videa byl resetován z důvodu nekompatibility. Vyberte kompatibilní enkodér ze seznamu.\"\nCodecCompat.Incompatible=\"(nekompatibilní s %1)\"\nCodecCompat.CodecPlaceholder=\"Vyberte enkodér ...\"\nCodecCompat.ContainerPlaceholder=\"Vyberte formát ...\"\nCodecCompat.CodecMissingOnExit.Title=\"Nebyl vybrán žádný enkodér\"\nCodecCompat.CodecMissingOnExit.Text=\"Musí být vybrán alespoň jeden enkodér zvuku a videa. Zkontrolujte, zda jsou vybrány enkodéry pro nahrávání i vysílání.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Nebyl vybrán žádný formát\"\nCodecCompat.ContainerMissingOnExit.Text=\"Nebyl vybrán žádný formát pro nahrávání. Zkontrolujte, zda formát pro nahrávání je kompatibilní s vybraným enkodérem pro vysílání.\"\nFinalScene.Title=\"Odstranění scény\"\nFinalScene.Text=\"Musí existovat alespoň jedna scéna, proto tuto není možno odstranit.\"\nNoSources.Title=\"Žádné zdroje\"\nNoSources.Text=\"Vypadá to, že jste zatím nepřidali žádné zdroje obrazu, takže budete vysílat černou obrazovku. Opravdu chcete pokračovat ?\"\nNoSources.Text.AddSource=\"Zdroje můžete kdykoliv přidat tlačítkem + pod seznamem Zdroje v hlavním okně.\"\nNoSources.Label=\"Nepřidali jste žádné zdroje.\\nKlikněte na tlačítko + níže, nebo klikněte zde pravým tlačítkem, pro přidání nového zdroje.\"\nChangeBG=\"Nastavit barvu\"\nCustomColor=\"Vlastní barva\"\nBrowserSource.EnableHardwareAcceleration=\"Zapnout hardwarovou akceleraci pro zdroj prohlížeče\"\nAbout=\"O aplikaci\"\nAbout.Info=\"OBS Studio je otevřený software pro nahrávání videa a živé vysílání.\"\nAbout.Donate=\"Přispět\"\nAbout.GetInvolved=\"Zapojte se\"\nAbout.Authors=\"Autoři\"\nAbout.License=\"Licence\"\nAbout.Error=\"Chyba! Soubor není možné přečíst.\\n\\nJděte na: %1\"\nAbout.Contribute=\"Podpořit projekt OBS\"\nAddUrl.Title=\"Přidat zdroj pomocí URL\"\nAddUrl.Text=\"Přetáhli jste URL do OBS. Tento odkaz bude automaticky přidán jako zdroj. Pokračovat?\"\nResizeOutputSizeOfSource=\"Změnit velikost výstupu (velikost zdroje)\"\nResizeOutputSizeOfSource.Text=\"Základní a výstupní rozlišení budou změněna na velikost aktuálního zdroje.\"\nResizeOutputSizeOfSource.Continue=\"Chcete pokračovat?\"\nPreviewTransition=\"Náhled přechodu\"\nImporter=\"Importér kolekcí scén\"\nImporter.SelectCollection=\"Vyberte kolekci scén\"\nImporter.Collection=\"Kolekce scén\"\nImporter.HelpText=\"Přidejte soubory do tohoto okna pro importování kolekcí z OBS nebo jiných podporovaných programů.\"\nImporter.Path=\"Cesta ke kolekci\"\nImporter.Program=\"Nalezená aplikace\"\nImporter.AutomaticCollectionPrompt=\"Automatické hledání kolekce scén\"\nImporter.AutomaticCollectionText=\"OBS může automaticky hledat importovatelné kolekce scén z podporovaných programů. Přejete si, aby OBS začalo automaticky hledat kolekce?\\n\\nToto nastavení můžete později změnit v Nastavení > Hlavní > Importéři.\"\nImporter.SelectFile=\"Procházet...\"\nRestart=\"Restartování\"\nNeedsRestart=\"OBS Studio je třeba restartovat. Chcete tak provést nyní?\"\nLoadProfileNeedsRestart=\"Profil obsahuje nastavení, které vyžaduje restartování OBS:\\n%1\\n\\nPřejete si restartovat OBS pro aplikování těchto změn?\"\nContextBar.NoSelectedSource=\"Není vybrán žádný zdroj\"\nContextBar.MediaControls.PlayMedia=\"Začít přehrávat\"\nContextBar.MediaControls.PauseMedia=\"Pozastavit přehrávání\"\nContextBar.MediaControls.StopMedia=\"Zastavit přehrávání\"\nContextBar.MediaControls.RestartMedia=\"Restartovat přehrávání\"\nContextBar.MediaControls.PlaylistNext=\"Další v seznamu skladeb\"\nContextBar.MediaControls.PlaylistPrevious=\"Předchozí v seznamu skladeb\"\nContextBar.MediaControls.BlindSeek=\"Posuvník přehrávání\"\nYouTube.Auth.Ok=\"Autorizace byla úspěšná.\\nTuto stránku můžete nyní zavřít.\"\nYouTube.Auth.NoCode=\"Autorizační proces nebyl dokončen.\"\nYouTube.Auth.NoChannels=\"Na vybraném účtu nejsou k dispozici žádné kanály\"\nYouTube.Auth.WaitingAuth.Title=\"Autorizace YouTube uživatele\"\nYouTube.Auth.WaitingAuth.Text=\"Dokončete autorizaci v externím prohlížeči.<br>Pokud se externí prohlížeč neotevřel, následujte tento odkaz a dokončete autorizaci:<br>%1\"\nYouTube.AuthError.Text=\"Nezdařilo se získat informace o kanálu: %1.\"\nYouTube.Actions.WindowTitle=\"Nastavení YouTube vysílání - kanál: %1\"\nYouTube.Actions.CreateNewEvent=\"Vytvořit nové vysílání\"\nYouTube.Actions.ChooseEvent=\"Vybrat existující vysílání\"\nYouTube.Actions.Title=\"Název*\"\nYouTube.Actions.MyBroadcast=\"Mé vysílání\"\nYouTube.Actions.Description=\"Popis\"\nYouTube.Actions.Privacy=\"Soukromí*\"\nYouTube.Actions.Privacy.Private=\"Soukromé\"\nYouTube.Actions.Privacy.Public=\"Veřejné\"\nYouTube.Actions.Privacy.Unlisted=\"Neveřejné\"\nYouTube.Actions.Category=\"Kategorie\"\nYouTube.Actions.Thumbnail=\"Náhled\"\nYouTube.Actions.Thumbnail.SelectFile=\"Vybrat soubor...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Není vybrán žádný soubor\"\nYouTube.Actions.Thumbnail.ClearFile=\"Vyčistit\"\nYouTube.Actions.MadeForKids=\"Je toto video vytvořeno pro děti?*\"\nYouTube.Actions.MadeForKids.Yes=\"Ano, je vytvořeno pro děti\"\nYouTube.Actions.MadeForKids.No=\"Ne, není vytvořeno pro děti\"\nYouTube.Actions.AdditionalSettings=\"Další nastavení\"\nYouTube.Actions.Latency=\"Odezva\"\nYouTube.Actions.Latency.Normal=\"Normální\"\nYouTube.Actions.Latency.Low=\"Nízká\"\nYouTube.Actions.Latency.UltraLow=\"Ultra nízká\"\nYouTube.Actions.EnableAutoStart=\"Povolit automatické spuštění\"\nYouTube.Actions.EnableAutoStop=\"Povolit automatické zastavení\"\nYouTube.Actions.AutoStartStop.TT=\"Indikuje, zda má být toto naplánované vysílání začít automaticky\"\nYouTube.Actions.EnableDVR=\"Povolit DVR\"\nYouTube.Actions.ScheduleForLater=\"Naplánovat na později\"\nYouTube.Actions.RememberSettings=\"Zapamatovat si tato nastavení\"\nYouTube.Actions.Create_Ready=\"Vytvořit vysílání\"\nYouTube.Actions.Create_GoLive=\"Vytvořit vysílání a začít vysílat\"\nYouTube.Actions.Choose_Ready=\"Vybrat vysílání\"\nYouTube.Actions.Choose_GoLive=\"Vybrat vysílání a začít vysílat\"\nYouTube.Actions.Create_Schedule=\"Naplánovat vysílání\"\nYouTube.Actions.Create_Schedule_Ready=\"Naplánovat a vybrat vysílání\"\nYouTube.Actions.Dashboard=\"Otevřít YouTube Studio\"\nYouTube.Actions.Error.Title=\"Chyba při vytváření živého vysílání\"\nYouTube.Actions.Error.Text=\"Chyba přístupu k YouTube '%1'.<br/>Detailní popis chyby můžete najít na <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Chyba přístupu k YouTube. Zkontrolujte prosím své připojení k internetu nebo přístup k YouTube serverům.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Chyba při vytváření vysílání '%1'.<br/>Detailní popis chyby můžete najít na <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Nebylo vytvořeno žádné vysílání. Připojte účet znovu.\"\nYouTube.Actions.Error.YouTubeApi=\"Chyba YouTube API. Pro více informací otevřete soubor záznamu.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Vybrané vysílání nebylo nalezeno.\"\nYouTube.Actions.Error.FileMissing=\"Vybraný soubor neexistuje.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Při otevírání vybraného souboru došlo k chybě.\"\nYouTube.Actions.Error.FileTooLarge=\"Vybraný soubor je moc velký (limit: 2 MiB)\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Přechod vysílání se nezdařil: %1<br/><br/>Pokud tato chyba přetrvává, <a href='https://studio.youtube.com/video/%2/livestreaming'>otevřete vysílání v YouTube Studio</a> a zkuste to ručně.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Vysílání přechází do testovací fáze, může to chvíli trvat. Zkuste to prosím znovu za 10 - 30 sekund.\"\nYouTube.Actions.EventsLoading=\"Načítání seznamu událostí...\"\nYouTube.Actions.EventCreated.Title=\"Událost vytvořena\"\nYouTube.Actions.EventCreated.Text=\"Událost byla úspěšně vytvořena.\"\nYouTube.Actions.Stream=\"Vysílání\"\nYouTube.Actions.Stream.ScheduledFor=\"Naplánováno na %1\"\nYouTube.Actions.Stream.Resume=\"Pokračovat v přerušeném vysílání\"\nYouTube.Actions.Stream.YTStudio=\"Automaticky vytvořeno YouTube Studiem\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Vytváření nového živého vysílání, čekejte prosím...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Vyžaduje manuální spuštění\"\nYouTube.Actions.AutoStartStreamingWarning=\"Automatické spuštění je pro tuto událost zakázáno. Pro zahájení vysílání klikněte na \\\"Začít vysílat\\\".\"\nYouTube.Actions.AutoStopStreamingWarning=\"Nebude moci obnovit připojení.<br>Vaše vysílání bude zastaveno a nebudete moci vysílat.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Živé vysílání není povoleno na vybraném YouTube kanálu.<br/><br/>Podívejte se na <a href='https://www.youtube.com/features'>youtube.com/features</a> pro více informací.\"\nYouTube.Errors.livePermissionBlocked=\"Živé vysílání není k dispozici na vybraném YouTube kanálu.<br/>Mějte na vědomí, že povolení vysílání na kanále může trvat až 24 hodit po zapnutí v nastavení kanálu.<br/><br/>Podívejte se na <a href='https://www.youtube.com/features'>youtube.com/features</a> pro více informací.\"\nYouTube.Errors.errorExecutingTransition=\"Přechod se nezdařil z důvodu chyby na pozadí. Zkuste to prosím znovu za pár sekund.\"\nYouTube.Errors.errorStreamInactive=\"YouTube nepřijímá data z vašeho vysílání. Zkontrolujte prosím vaši konfiguraci a zkuste to znovu.\"\nYouTube.Errors.invalidTransition=\"Pokus o přechod byl neplatný. Může to být způsobeno tím, že nebyl nedokončen předchozí přechod. Počkejte prosím pár sekund a zkuste to znovu.\"\nYouTube.DocksRemoval.Title=\"Vyčistit zastaralé YouTube doky prohlížeče\"\nYouTube.DocksRemoval.Text=\"Tyto doky prohlížeče budou odstraněny jako zasataralé:\\n\\n%1\\nPoužijte \\\"Doky/Ovládací panel YouTube Live\\\" místo toho.\"\nConfigDownload.WarningMessageTitle=\"Varování\"\nFailedToStartStream.MissingConfigURL=\"Není nastavena konfigurační URL adresa pro aktuální službu\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"URL adresa pro vlastní RTMP není nastavena\"\nFailedToStartStream.InvalidCustomConfig=\"Chybná vlastní konfigurace\"\nFailedToStartStream.MissingCanvas=\"Nastavené doplňkové plátno chybí\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Chyba při vytváření služby pro video s více stopami\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Chyba při vytváření RTMP výstupu pro video s více stopami\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC není dostupný.\\n\\nChyba při hledání kodéru typu '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Chyba při vytváření kodéru obrazu '%1' (typ: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Nezdařilo se získat OBS video info při vytváření kodéru '%1' (typ: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Chyba při vytváření kodéru zvuku\"\nFailedToStartStream.NoRTMPURLInConfig=\" Nastavení neobsahuje adresy cílových RTMP(S) serverů\"\nFailedToStartStream.FallbackToDefault=\"Spuštění vysílání pomocí %1 selhalo; chcete se pokusit o spuštění vysílní za použití nastavení jednoho kodéru?\"\nFailedToStartStream.ConfigRequestFailed=\"Nepodařilo se načtení nastavení z %1<br><br>HTTP chyba: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Přijata neznámá hodnota stavu '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nChcete pokračovat ve vysílání bez %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nChcete pokračovat ve vysílání?\"\nFailedToStartStream.MissingEncoderConfigs=\"Nastavení pro vysílání neobsahovalo nastavení enkodéru\"\nFailedToStartStream.InvalidEncoderConfig=\"Nastavení pro vysílání obsahovalo chybné nastavení enkodéru\"\nFailedToStartStream.StatusMissingHTML=\"Požadavek o spuštění vysílání skončil neznámou chybou\"\nFailedToStartStream.NoConfigSupplied=\"Chybějící nastavení\"\nMultitrackVideo.Info=\"%1 automaticky optimalizuje nastavení pro enkódování a odesílání videí v několika kvalitách. Tato možnost odešle službě %2 informace o vašem počítači a nainstalovaném softwaru.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Nekompatibilní nastavení\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 není aktuálně kompatibilní s:\\n\\n%2\\nPřo pokračování ve vysílání s %1, vypněte nekompatibilní nastavení:\\n\\n%3\\na začněte vysílat znovu.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Zakázat pro toto vysílání a začít vysílat\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Upravit nastavení a začít vysílat\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 není aktuálně kompatibilní s [Zvuk → Obecné → Kanály] nastavenými na '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Zvuk → Obecné → Kanály] musejí být nastaveny na '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 vyžaduje více rozdílných nastavení pro [Zvuk → Obecné → Kanály]\"\nBasic.OpenPluginManager=\"Správce pluginů\"\nPluginManager=\"Správce pluginů\"\nPluginManager.Restart=\"OBS Studio je nutno restartovat než budou aplikovány změny pluginů\"\nPluginManager.MissingPlugin=\"[PLUGIN NENALEZEN]\"\nPluginManager.Section.Discover=\"Procházet\"\nPluginManager.Section.Manage=\"Nainstalované\"\nPluginManager.Section.Updates=\"Aktualizace\"\nPluginManager.Section.Manage.Title=\"Spravovat povolené pluginy\"\n"
  },
  {
    "path": "frontend/data/locale/da-DK.ini",
    "content": "Language=\"Dansk\"\nApply=\"Anvend\"\nCancel=\"Annullér\"\nClose=\"Luk\"\nSave=\"Gem\"\nDiscard=\"Kassér\"\nDisable=\"Deaktivér\"\nYes=\"Ja\"\nNo=\"Nej\"\nAdd=\"Tilføj\"\nRemove=\"Fjern\"\nRename=\"Omdøb\"\nInteract=\"Interagere\"\nFilters=\"Filtre\"\nProperties=\"Egenskaber\"\nMoveUp=\"Flyt op\"\nMoveDown=\"Flyt ned\"\nSettings=\"Indstillinger\"\nDisplay=\"Skærm\"\nName=\"Navn\"\nExit=\"Afslut\"\nMixer=\"Lydmixer\"\nBrowse=\"Gennemse\"\nDroppedFrames=\"Tabte billeder (frames) %1 (%2%)\"\nProjector.Open.Program=\"Åben programprojektor\"\nProjector.Open.Preview=\"Åben forhåndsvisningsprojektor\"\nProjector.Open.Scene=\"Åben sceneprojektor\"\nProjector.Open.Source=\"Åben kildeprojektor\"\nProjector.Open.Multiview=\"Åben Multiview\"\nProjector.Display=\"Skærm: %1\"\nProjector.Window=\"Nyt vindue\"\nProjector.Title=\"Projektor\"\nProjector.Title.Source=\"Kilde: %1\"\nProjector.ResizeWindowToContent=\"Tilpas vindue til indhold\"\nClear=\"Ryd\"\nRevert=\"Tilbagefør\"\nShow=\"Vis\"\nHide=\"Skjul\"\nUnhideAll=\"Vis alle\"\nUntitled=\"Ikke-navngivet\"\nNew=\"Ny\"\nDuplicate=\"Dublere\"\nEnable=\"Aktivér\"\nDisableOSXVSync=\"Deaktivér macOS V-Sync\"\nResetOSXVSyncOnExit=\"Nulstil macOS V-Sync ved afslutning\"\nHighResourceUsage=\"Encodingproces overbelastet! Overvej at reducere videoressourceforbruget eller benytte et hurtigere encoding-forvalg.\"\nTransition=\"Overgang\"\nQuickTransitions=\"Hurtige overgange\"\nFadeToBlack=\"Udtoning til sort\"\nLeft=\"Venstre\"\nRight=\"Højre\"\nBottom=\"Bund\"\nReset=\"Nulstil\"\nHours=\"Timer\"\nMinutes=\"Minutter\"\nSeconds=\"Sekunder\"\nDeprecated=\"Forældet\"\nReplayBuffer=\"Genafspilningsbuffer\"\nImport=\"Importér\"\nExport=\"Eksportér\"\nCopy=\"Kopiér\"\nPaste=\"Indsæt\"\nPasteReference=\"Indsæt (reference)\"\nPasteDuplicate=\"Indsæt (dublet)\"\nRemuxRecordings=\"Remux optagelser\"\nNext=\"Næste\"\nBack=\"Tilbage\"\nDefaults=\"Standardindstillinger\"\nRestoreDefaults=\"Standardindstillinger\"\nHideMixer=\"Skjul i Mixer\"\nTransitionOverride=\"Tilsidesæt overgang\"\nShowTransition=\"Vis-overgang\"\nHideTransition=\"Skjul-overgang\"\nNone=\"Ingen\"\nStudioMode.Preview=\"Forhåndsvisning\"\nStudioMode.PreviewSceneName=\"Forhåndsvis: %1\"\nShowInMultiview=\"Vis i Multiview\"\nVerticalLayout=\"Lodret layout\"\nGroup=\"Gruppér\"\nDoNotShowAgain=\"Vis ikke igen\"\nDefault=\"(Standard)\"\nCalculating=\"Beregner...\"\nFullscreen=\"Fuldskærm\"\nWindowed=\"I vindue\"\nRefreshBrowser=\"Opfrisk\"\nAspectRatio=\"Størrelsesforhold <b>%1:%2</b>\"\nLockVolume=\"Lås lydstyrken\"\nLogViewer=\"Logvisning\"\nShowOnStartup=\"Vis ved opstart\"\nOpenFile=\"Åbn fil\"\nAddScene=\"Tilføj scene\"\nAddSource=\"Tilføj kilde\"\nRemoveScene=\"Fjern valgte scene\"\nRemoveSource=\"Fjern valgte kilde(r)\"\nMoveSceneUp=\"Flyt scene op\"\nMoveSceneDown=\"Flyt scene ned\"\nMoveSourceUp=\"Flyt kilde(r) op\"\nMoveSourceDown=\"Flyt kilde(r) ned\"\nSourceProperties=\"Åbn Kildeegenskaber\"\nSourceFilters=\"Åbn kildefiltre\"\nMixerToolbarMenu=\"LydMixer-menu\"\nSceneFilters=\"Åbn scenefiltre\"\nList=\"Liste\"\nGrid=\"Gitter\"\nAutomatic=\"Automatisk\"\nComingSoon=\"Kommer snart\"\nPluginsFailedToLoad.Title=\"Plugin-indlæsningsfejl\"\nPluginsFailedToLoad.Text=\"Flg. OBS-plugins kunne ikke indlæses:\\n\\n%1\\nOpdatér eller fjern disse plugins.\"\nAlreadyRunning.Title=\"OBS kører allerede\"\nAlreadyRunning.Text=\"OBS kører allerede! Medmindre dette er tilsigtet, så bør du lukke enhver eksisterende OBS-proces, inden du forsøger at køre en ny. Hvis du har OBS opsat til at minimeres sig til systembakken, så tjek om den stadig kører dér.\"\nAlreadyRunning.LaunchAnyway=\"Start alligevel\"\nCrashHandling.Dialog.Title=\"OBS Studio nedbrud opdaget\"\nCrashHandling.Labels.Text=\"OBS Studio lukkede ikke rigtigt ned.\\n\\nStart i Fejlsikret tilstand (tredjeparts udvidelser, scripts og WebSockets vil være slået fra)?\"\nCrashHandling.Checkbox.SendReport=\"Jeg har læst privatlivspolitikken og samtykker til at uploade.\"\nCrashHandling.Buttons.LaunchSafe=\"Start i Fejlsikret tilstand\"\nSafeMode.Restart=\"Vil du genstarte OBS i Fejlsikret tilstand  (tredjeparts udvidelser, scripts og WebSockets vil være slået fra)?\"\nSafeMode.RestartNormal=\"Vil du genstarte OBS i normal tilstand?\"\nChromeOS.Title=\"Uunderstøttet platform\"\nChromeOS.Text=\"OBS lader til at køre i en ChromeOS-container. Denne platform er uunderstøttet.\"\nWine.Title=\"Wine detekteret\"\nWine.Text=\"Kørsel af OBS i Wine er uunderstøttet, og mange funktioner, såsom optagelses- eller enhedskilder, fungerer ikke eller kun i begrænset omfang.<br><br>Det anbefales at køre en indbygget version af OBS i stedet, f.eks. <a href='https://flathub.org/apps/details/com.obsproject.Studio'>vores Flatpak-version</a> eller operativsystemets pakker.\"\nDockCloseWarning.Title=\"Lukker dokbart vindue\"\nDockCloseWarning.Text=\"Du har netop lukket et dokbart vindue. Skal det vises igen, så benyt Dokke-menuen på menubjælken.\"\nExtraBrowsers=\"Tilpassede browserdokke\"\nExtraBrowsers.Info=\"Tilføj en dokke ved at give dem et navn og URL, og klik derefter på Anvend/Luk for at åbne dem. Du kan tilføje/fjerne dokke efter behov.\"\nExtraBrowsers.DockName=\"Doknavn\"\nAuth.Authing.Title=\"Godkender...\"\nAuth.Authing.Text=\"Godkender med %1, afvent venligst...\"\nAuth.AuthFailure.Title=\"Godkendelsesfejl\"\nAuth.AuthFailure.Text=\"Mislykkedes at godkende med %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Godkendelse nødvendig\"\nAuth.InvalidScope.Text=\"Godkendelseskrav for %1 er ændret. Visse funktioner er muligvis ikke tilgængelige.\"\nAuth.LoadingChannel.Title=\"Indlæser kanalinformation...\"\nAuth.LoadingChannel.Text=\"Indlæser kanalinformation for %1, afvent venligst...\"\nAuth.LoadingChannel.Error=\"Kan ikke hente kanalinformation.\"\nAuth.ChannelFailure.Title=\"Mislykkedes at indlæse kanal\"\nAuth.ChannelFailure.Text=\"Mislykkedes at indlæse kanalinformation for %1\\n\\n%2: %3\"\nAuth.StreamInfo=\"Streaminformation\"\nTwitchAuth.Stats=\"Twitch-statistikker\"\nTwitchAuth.Feed=\"Twitch-aktivitetsfeed\"\nTwitchAuth.TwoFactorFail.Title=\"Stream-nøgle kunne ikke forespørges\"\nTwitchAuth.TwoFactorFail.Text=\"OBS kunne ikke forbinde til din Twitch-konto. Tjek, at tofaktorgodkendelse er opsat i dine <a href='https://www.twitch.tv/settings/security'>Twitch-sikkerhedsindstillinger</a>, da dette er nødvendigt for at streame.\"\nRestreamAuth.Channels=\"Restream Kanaler\"\nCopy.Filters=\"Kopiér filtre\"\nPaste.Filters=\"Indsæt filtre\"\nBrowserPanelInit.Title=\"Initialiserer browser...\"\nBrowserPanelInit.Text=\"Initialiserer browser, afvent venligst...\"\nBandwidthTest.Region.US=\"USA\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Asien\"\nBandwidthTest.Region.Other=\"Anden\"\nBasic.AutoConfig=\"Auto-opsætningsguide\"\nBasic.AutoConfig.ApplySettings=\"Effektuér indstillinger\"\nBasic.AutoConfig.StartPage=\"Brugsoplysninger\"\nBasic.AutoConfig.StartPage.SubTitle=\"Angiv, hvad du vil benytte programmet til\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimér til streaming, sekundært til optagelse\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimér kun til optagelse, jeg vil ikke foretage streaming\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Jeg kommer kun til at bruge det virtuelle kamera\"\nBasic.AutoConfig.VideoPage=\"Videoindstillinger\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Angiv de ønskede videoindstillinger\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Benyt aktuelle (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Skærm %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Benyt aktuelle (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Enten 60 eller 30, men foretræk 60 når muligt\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Enten 60 eller 30, men foretræk høj opløsning\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Bemærk: Lærredets (basis-) opløsning er ikke nødvendigvis identisk med den opløsning, du vil streame med eller optage i. Din aktuelle stream-/optagelsesopløsning kan blive nedskaleret fra lærredsopløsningen for at reducere ressourceforbrug eller bit-hastighedskrav.\"\nBasic.AutoConfig.StreamPage=\"Streamoplysninger\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Angiv dine streamoplysninger\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Forbind konto (anbefalet)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Afbryd konto\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Afbryde konto?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Denne ændring effektueres straks. Sikker på, at du vil afbryde din konto?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Hent Streamnøgle\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Yderligere info\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Benyt Streamnøgle\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Brug Streamnøgle (avanceret)\"\nBasic.AutoConfig.StreamPage.Service=\"Tjeneste\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Vis alle...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Tilpasset...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Streamnøgle\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: Indtast krypteringssætningen.\\nRTMP: Indtast nøglen fra din tjeneste.\\nSRT: Indtast streamid hvis din tjeneste bruger det.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Encoder-nøgle\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Tilsluttet konto\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Estimér bit-hastigheden vha. en båndbreddetest (kan tage et par minutter)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Foretræk hardwarekodning\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Hardwarekodning fjerner det meste CPU-forbrug, men kan kræve højere bit-hastighed for at opnå samme kvalitetsniveau.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Streamadvarsel\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Båndbreddetesten er ved at streame tilfældige videodata uden lyd til din kanal. Det anbefales, hvis muligheden foreligger, midlertidigt at fravælger at gemme streams samt at angive streamen som privat indtil testen er fuldført. Fortsæt?\"\nBasic.AutoConfig.TestPage=\"Endelige resultater\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Programmet udfører nu en række tests for at estimere de bedste indstillinger\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testning fuldført\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Udfører båndbreddetest, kan tage nogle få minutter...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Intet output for denne serviceprotokol var fundet\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Forbinder til: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Mislykkedes at forbinde til nogen servere. Tjek din Internetforbindelse og forsøg igen.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Tester båndbredde for: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Tester stream-encoder, dette kan tage et øjeblik...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Tester optagelses-encoder, dette kan tage et øjeblik...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Mislykkedes at starte encoder\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Tester %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Streaming-encoder\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Optagelses-encoder\"\nBasic.AutoConfig.TestPage.Result.Header=\"Programmet har fastslået, at disse estimerede indstillinger er de mest idéelle for dig:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Klik på Anvend indstillinger for at benytte denne opsætning. Klik på Tilbage for at genopsætte guiden og forsøge igen. For at opsætte manuelt, så klik på Annullér og åbn Indstillinger.\"\nBasic.AutoConfig.Info=\"Auto-opsætningsguiden vælger de bedste indstillinger ud fra dine computerspecifikationer og Internethastighed.\"\nBasic.AutoConfig.RunAnytime=\"Auto-opsætningen kan altid eksekveres via menuen Værktøjer.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Streamingsopløsning (skaleret)\"\nBasic.Stats=\"Statistikker\"\nBasic.Stats.CPUUsage=\"CPU-forbrug\"\nBasic.Stats.HDDSpaceAvailable=\"Tilgængelig diskplads\"\nBasic.Stats.MemoryUsage=\"Hukommelsesforbrug\"\nBasic.Stats.AverageTimeToRender=\"Gennemsnitstid for billedrendering\"\nBasic.Stats.SkippedFrames=\"Oversprang billeder grundet kodningsforsinkelse\"\nBasic.Stats.MissedFrames=\"Billeder tabt grundet renderingsforsinkelse\"\nBasic.Stats.Output.Recording=\"Optagelse\"\nBasic.Stats.Status.Recording=\"Optagelse\"\nBasic.Stats.Status.Reconnecting=\"Genopretter forbindelse\"\nBasic.Stats.Status.Inactive=\"Inaktiv\"\nBasic.Stats.Status.Active=\"Aktiv\"\nBasic.Stats.DroppedFrames=\"Tabte billeder (netværk)\"\nBasic.Stats.MegabytesSent=\"Samlet Dataoutput\"\nBasic.Stats.Bitrate=\"Bithastighed\"\nBasic.Stats.DiskFullIn=\"Disk fuld om (ca.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 timer, %2 minutter\"\nBasic.Stats.ResetStats=\"Nulstil statistik\"\nResetUIWarning.Title=\"Sikker på, at du vil nulstille UI'en?\"\nResetUIWarning.Text=\"Nulstilling af UI'en vil skjule ekstra dokke. Disse dokke vil skulle synliggøres i visningsmenuen, hvis de ønskes vist.\\n\\nSikker på, at UI'en skal nulstille?\"\nUpdater.Title=\"Ny opdatering tilgængelig\"\nUpdater.Text=\"Der er en ny opdatering tilgængelig:\"\nUpdater.UpdateNow=\"Opdatér nu\"\nUpdater.RemindMeLater=\"Påmind mig senere\"\nUpdater.Skip=\"Overspring version\"\nUpdater.NoUpdatesAvailable.Title=\"Ingen opdateringer tilgængelige\"\nUpdater.NoUpdatesAvailable.Text=\"Ingen opdateringer er pt. tilgængelige\"\nUpdater.BranchNotFound.Title=\"Opdateringskanal fjernet\"\nUpdater.BranchNotFound.Text=\"Din valgte opdateringskanal er ikke længere tilgængelig, OBS har derfor nulstillet den til standard.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Integritetstjek utilgængelig\"\nUpdater.RepairButUpdatesAvailable.Text=\"Tjek af filintegritet er kun mulig for den senest tilgængelige version. Brug Hjælp → Tjek for opdateringer for at verificere og opdatere en OBS-installation.\"\nUpdater.RepairConfirm.Title=\"Bekræft Integritetstjek\"\nUpdater.RepairConfirm.Text=\"Start af integritetstjek vil skanne OBS-installationen for beskadigelse og gendownloade evt. ødelagte/ændrede filer. Dette kan tage et øjeblik.\\n\\nFortsæt?\"\nUpdater.FailedToLaunch=\"Mislykkedes at starte opdatering\"\nQuickTransitions.SwapScenes=\"Ombyt forhåndsvisnings-/programscener efter overgang\"\nQuickTransitions.SwapScenesTT=\"Ombyt forhåndsvisnings-/programscener efter overgang (såfremt programmets oprindelige scene stadig eksisterer).\\nDette vil ikke omgøre evt. ændringer, som måtte været udført på programmets oprindelige scene.\"\nQuickTransitions.DuplicateScene=\"Dublér scene\"\nQuickTransitions.DuplicateSceneTT=\"Muliggør, ved redigering af den samme scene, redigering af transformering/synlighed af kilder uden at ændre programoutput.\\nFor at redigere egenskaber for kilder uden at ændre programoutput, så aktivér 'Dublér Kilder'.\\nÆndring af denne værd vil nulstille den nuværende outputscene (hvis den stadig findes).\"\nQuickTransitions.EditProperties=\"Dublér kilder\"\nQuickTransitions.EditPropertiesTT=\"Muliggør, ved redigering af den samme scene, redigering af egenskaber for kilder uden at ændre programoutput.\\nDette kan kun anvendes, hvis 'Dublér Scene' er aktiv.\\nVisse kilder (såsom optage- eller mediekilder) understøtter ikke dette og kan ikke redigeres separat.\\nÆndring af denne værdi vil nulstille den nuværende programscene (hvis den stadig findes).\\n\\nAdvarsel: Da kilder vil blive duubleret, kan dette kræve ekstra system- eller videoressourcer.\"\nQuickTransitions.HotkeyName=\"Hurtig overgang: %1\"\nBasic.AddTransition=\"Tilføj opsætbar overgang\"\nBasic.RemoveTransition=\"Fjern opsætbar overgang\"\nBasic.TransitionProperties=\"Overgangsegenskaber\"\nBasic.SceneTransitions=\"Sceneovergange\"\nBasic.TransitionDuration=\"Varighed\"\nBasic.TogglePreviewProgramMode=\"Studiotilstand\"\nBasic.EnablePreviewProgramMode=\"Aktiver Studietilstand\"\nBasic.DisablePreviewProgramMode=\"Deaktiver Studio Mode\"\nUndo.Undo=\"Fortryd\"\nUndo.Redo=\"Gentag\"\nUndo.Add=\"Tilføj %1'\"\nUndo.Delete=\"Slet '%1'\"\nUndo.Rename=\"Omdøb '%1'\"\nUndo.SceneCollection.Switch=\"Skift til '%1'\"\nUndo.Item.Undo=\"Fortryd %1\"\nUndo.Item.Redo=\"Gentag %1\"\nUndo.Sources.Multi=\"Slet %1 kilder\"\nUndo.Filters=\"Filterændringer på '%1'\"\nUndo.Filters.Paste.Single=\"Indsæt filter '%1' i '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopiér filtre fra '%1' til '%2'\"\nUndo.Transform=\"Transformér kilde(r) I '%1'\"\nUndo.Transform.Paste=\"Indsæt Transformation i '%1'\"\nUndo.Transform.Rotate=\"Rotation i '%1'\"\nUndo.Transform.Reset=\"Transformationsnulstilling i '%1'\"\nUndo.Transform.HFlip=\"Vandret vend i '%1'\"\nUndo.Transform.VFlip=\"Lodret vend i '%1'\"\nUndo.Transform.FitToScreen=\"Tilpas til skærm i '%1'\"\nUndo.Transform.StretchToScreen=\"Stræk til skærm i '%1'\"\nUndo.Transform.Center=\"Centrér på skærm i '%1'\"\nUndo.Transform.VCenter=\"Centrér lodret på skærm i '%1'\"\nUndo.Transform.HCenter=\"Centrér vandret på skærm i '%1'\"\nUndo.Volume.Change=\"Lydstyrkeændring på '%1'\"\nUndo.Volume.Mute=\"Tavsgør '%1'\"\nUndo.Volume.Unmute=\"Fjern tavsgøring af '%1'\"\nUndo.Balance.Change=\"Lydbalanceændring for '%1'\"\nUndo.SyncOffset.Change=\"Lydsynk forskydelsesændring for'%1'\"\nUndo.MonitoringType.Change=\"Ændr lydmonitorering for '%1'\"\nUndo.Mixers.Change=\"Ændr lydmixere for '%1'\"\nUndo.ForceMono.On=\"Aktivér Tving mono for '%1'\"\nUndo.ForceMono.Off=\"Deaktivér Tving mono for '%1'\"\nUndo.Properties=\"Egenskabsændring på '%1'\"\nUndo.Scene.Duplicate=\"Dublér scene'%1'\"\nUndo.ShowTransition=\"Vis overgang på '%1'\"\nUndo.HideTransition=\"Skjul overgang på '%1'\"\nUndo.ShowSceneItem=\"Vis '%1' i '%2'\"\nUndo.HideSceneItem=\"Skjul '%1' i '%2'\"\nUndo.ReorderSources=\"Optagerkilder i '%1'\"\nUndo.MoveUp=\"Flyt '%1' op i '%2'\"\nUndo.MoveDown=\"Flyt '%1' ned i '%2'\"\nUndo.MoveToTop=\"Flyt '%1' til toppen i '%2'\"\nUndo.MoveToBottom=\"Flyt '%1' til bunden i '%2'\"\nUndo.PasteSource=\"Indsæt kilde(r) i '%1'\"\nUndo.PasteSourceRef=\"Indsæt kildereference(r) i '%1'\"\nUndo.GroupItems=\"Gruppér elementer i '%1'\"\nTransitionNameDlg.Text=\"Angiv navnet på overgangen\"\nTransitionNameDlg.Title=\"Overgangsnavn\"\nTitleBar.SafeMode=\"FEJLSIKRET TILSTAND\"\nTitleBar.PortableMode=\"Transportabel mode\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Scener\"\nNameExists.Title=\"Navn findes allerede\"\nNameExists.Text=\"Navnet benyttes allerede.\"\nNoNameEntered.Title=\"Angiv et gyldigt navn\"\nNoNameEntered.Text=\"Navn er obligatorisk.\"\nConfirmStart.Title=\"Start stream?\"\nConfirmStart.Text=\"Sikker på, at du vil starte streamen?\"\nConfirmStop.Title=\"Stop stream?\"\nConfirmStop.Text=\"Sikker på, at du vil stoppe streamen?\"\nConfirmStopRecord.Title=\"Stop optagelse?\"\nConfirmStopRecord.Text=\"Sikker på, at du vil stoppe optagelsen?\"\nConfirmBWTest.Title=\"Start båndbreddetest?\"\nConfirmBWTest.Text=\"Du har OBS opsat i tilstanden båndbreddetest. Denne tilstand muliggør netværksaftestning, uden at din kanal er online. Når aftestningen er gennemført, så deaktivér tilstanden, så seerne vil kunne se din stream.\\n\\nVil du fortsætte?\"\nConfirmRemove.Title=\"Bekræft Fjern\"\nConfirmRemove.Text=\"Sikker på, at '%1' skal fjernes?\"\nConfirmRemove.TextMultiple=\"Sikker på, at du vil fjerne %1 emner?\"\nConfirmReset.Title=\"Nulstil egenskaber\"\nConfirmReset.Text=\"Er du sikke på, at du ønsker at nulstille de nuværende egenskaber til standard?\"\nOutput.StartStreamFailed=\"Mislykkedes at starte streaming\"\nOutput.StartRecordingFailed=\"Mislykkedes at starte optagelse\"\nOutput.StartReplayFailed=\"Mislykkedes at starte genafspilnings-buffer\"\nOutput.StartVirtualCamFailed=\"Kunne ikke starte virtuelt kamera\"\nOutput.StartFailedGeneric=\"Start af output mislykkedes. Tjek loggen for oplysninger.\\n\\nBemærk: Benytter du NVENC- eller AMD-encodere, så sørg for at dine videodrivere er opdaterede.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Kan ikke gemme genafspilninger under pause\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Advarsel: Genafspilninger kan ikke gemmes, mens optagelsen er pauset.\"\nOutput.ConnectFail.Title=\"Mislykkedes at forbinde\"\nOutput.ConnectFail.BadPath=\"Ugyldig sti eller forbindelses-URL. Tjek dine indstillinger for at bekræfte, at de er gyldige.\"\nOutput.ConnectFail.ConnectFailed=\"Mislykkedes at forbinde til server\"\nOutput.ConnectFail.InvalidStream=\"Kunne ikke tilgå den angivne kanel eller streamnøgle. Tjek din streamnøgle. Er den korrekt, kan der være et problemer med at forbinde til serveren.\"\nOutput.ConnectFail.HdrDisabled=\"HDR output er i øjeblikket deaktiveret for dette output.\"\nOutput.ConnectFail.Error=\"En uventet fejl opstod, under forsøgtet på at forbinde til serveren. Se yderligere info i logfilen.\"\nOutput.ConnectFail.Disconnected=\"Afbrudt fra serveren.\"\nOutput.StreamEncodeError.Title=\"Kodningsfejl\"\nOutput.StreamEncodeError.Msg=\"En kodningsfejl opstod under streamingen.\"\nOutput.StreamEncodeError.Msg.LastError=\"En encoderfejl opstod under streaming:<br><br>%1\"\nOutput.RecordFail.Title=\"Mislykkedes at starte optagelse\"\nOutput.RecordFail.Unsupported=\"Outputformatet er enten uunderstøttet eller understøtter ikke flere end ét lydspor. Tjek dine indstillinger og forsøg igen.\"\nOutput.RecordNoSpace.Title=\"Utilstrækkelig diskplads\"\nOutput.RecordNoSpace.Msg=\"Utilstrækkelig diskplads til at fortsætte optagelse.\"\nOutput.RecordError.Title=\"Optagelsesfejl\"\nOutput.RecordError.Msg=\"En uspecificeret fejl opstod under optagelse.\"\nOutput.RecordError.EncodeErrorMsg=\"En kodningsfejl opstod under optagelsen.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"En encoderfejl opstod under optagelse:<br><br>%1\"\nOutput.BadPath.Title=\"Ugyldig filsti\"\nOutput.BadPath.Text=\"Den opsatte Optagelsessti kunne ikke åbnes. Tjek optagelsesstien under Indstillinger → Output → Optagelse.\"\nOutput.NoBroadcast.Title=\"Ingen Udsendelse opsat\"\nOutput.NoBroadcast.Text=\"En udsendelse skal opættes, inden streaming kan påbegyndes.\"\nOutput.BroadcastStartFailed=\"Start af udsendelse mislykkedes\"\nOutput.BroadcastStopFailed=\"Stop af udsendelse mislykkedes\"\nLogUploadDialog.Title=\"OBS Studio Log Fil Upload\"\nLogUploadDialog.Labels.Progress=\"Log upload i gang. Vent venligst...\"\nLogUploadDialog.Buttons.CopyURL=\"Kopiér Log URL\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analyser Log fil\"\nLogUploadDialog.Buttons.RetryButton=\"Prøv igen\"\nRemux.SourceFile=\"OBS-optagelse\"\nRemux.TargetFile=\"Destinationsfil\"\nRemux.ClearFinished=\"Ryd færdige emner\"\nRemux.ClearAll=\"Ryd alle emner\"\nRemux.OBSRecording=\"OBS-optagelse\"\nRemux.FinishedTitle=\"Remuxing færdig\"\nRemux.Finished=\"Optagelse remuxed\"\nRemux.FinishedError=\"Optagelse remuxed, men filen kan være ufuldstændig\"\nRemux.SelectRecording=\"Vælg OBS-optagelse...\"\nRemux.SelectTarget=\"Vælg destinationsfil...\"\nRemux.FileExistsTitle=\"Destinationsfil findes\"\nRemux.FileExists=\"Flg. destinationsfiler findes allerede. Erstat disse?\"\nRemux.ExitUnfinishedTitle=\"Remuxing i gang\"\nRemux.ExitUnfinished=\"Remuxing er ikke færdig. Stopper du nu, kan det gøre destinationsfilen ubrugelig.\\nSikker på, at du vil stoppe remuxing?\"\nRemux.HelpText=\"Slip filer i vinduet for at remuxe eller vælg en tom \\\"OBS-optagelse\\\" celle for at søge efter en fil.\"\nRemux.NoFilesAddedTitle=\"Ingen remux fil tilføjet\"\nRemux.NoFilesAdded=\"Ingen fil er tilføjet til remux. Drop en mappe med en eller flere videofiler.\"\nMissingFiles=\"Filer mangler\"\nMissingFiles.MissingFile=\"Fil mangler\"\nMissingFiles.NewFile=\"Ny fil\"\nMissingFiles.HelpText=\"Nogle filer mangler siden du sidst brugte OBS.\"\nMissingFiles.Clear=\"<ryddet>\"\nMissingFiles.NumFound=\"Fundet %1 af %2\"\nMissingFiles.Search=\"Søg i mappe...\"\nMissingFiles.SelectFile=\"Vælg fil...\"\nMissingFiles.SelectDir=\"Vælg mappen, der skal søges i\"\nMissingFiles.State=\"Status\"\nMissingFiles.Missing=\"Mangler\"\nMissingFiles.Replaced=\"Erstattet\"\nMissingFiles.Cleared=\"Ryddet\"\nMissingFiles.Found=\"Fundet\"\nMissingFiles.AutoSearch=\"Yderligere filmatches fundet\"\nMissingFiles.AutoSearchText=\"OBS har fundet yderligere matches for manglende filer mappen. Tilføj disse?\"\nMissingFiles.NoMissing.Title=\"Filmangel tjek\"\nMissingFiles.NoMissing.Text=\"Ingen synes at mangle.\"\nMacPermissions.MenuAction=\"Gennemgå apptilladelser...\"\nMacPermissions.Title=\"Gennemgå apptilladelser\"\nMacPermissions.Description=\"OBS Studio kræver eksplicitte tilladelser for at kunne tilbyde visse funktioner. Det anbefales at tildele disse tilladelser, men kræves ikke for brug af appen. De kan altid tildeles senere.\"\nMacPermissions.Description.OpenDialog=\"Denne dialog kan genåbnes via OBS Studio-menuen.\"\nMacPermissions.AccessGranted=\"Adgang tildelt\"\nMacPermissions.RequestAccess=\"Anmod om adgang\"\nMacPermissions.OpenPreferences=\"Åbn %1-præferencer\"\nMacPermissions.Item.ScreenRecording=\"Skærmoptagelse\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS kræver denne tilladelse for at kunne optage skærmen.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Denne tilladelse kræves for at kunne optage indhold fra et webcam eller et optagekort.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS kræver denne tilladelse for at kunne optage din mikrofon eller en ekstern lydkilde.\"\nMacPermissions.Item.InputMonitoring=\"Input monitorering\"\nMacPermissions.Item.InputMonitoring.Details=\"Denne tilladelse er påkrævet for at genvejstaster virker, mens OBS kører i baggrunden\"\nMacPermissions.Item.Accessibility=\"Tilgængelighed\"\nMacPermissions.Item.Accessibility.Details=\"På ældre installation kan OBS fremgå i \\\"Tilgængelighed\\\" i stedet for \\\"Input monitorering\\\"\"\nMacPermissions.Continue=\"Fortsæt\"\nSourceLeak.Title=\"Kildeoprydningsfejl\"\nSourceLeak.Text=\"Der var et problem under skift af scenekollektion og nogle kilder kunne ikke blive fjernet. Dette problem forsages typisk af udvidelser, som ikke frigiver ressourcerne ordenligt. Sørg derfor for, at dine udvidelser er opdateret til seneste version.\\n\\nOBS Studio vil nu lukke ned for at undgå potentiel datakorruption.\"\nBasic.DesktopDevice1=\"Computerlyd\"\nBasic.DesktopDevice2=\"Computerlyd 2\"\nBasic.DisplayCapture=\"Skærmoptagelse\"\nBasic.Main.PreviewConextMenu.Enable=\"Aktivér forhåndsvisning\"\nBasic.Main.Preview.Disable=\"Deaktivér forhåndsvisning\"\nScaleFiltering=\"Skaleringsfiltrering\"\nScaleFiltering.Point=\"Punkt\"\nScaleFiltering.Bilinear=\"Bilineær\"\nScaleFiltering.Bicubic=\"Bikubisk\"\nScaleFiltering.Area=\"Område\"\nBlendingMethod=\"Blandingsmetode\"\nBlendingMethod.Default=\"Standard\"\nBlendingMethod.SrgbOff=\"SRGB Fra\"\nBlendingMode=\"Blending-tilstand\"\nBlendingMode.Additive=\"Tilføj\"\nBlendingMode.Subtract=\"Fratrækning\"\nBlendingMode.Screen=\"Skærm\"\nBlendingMode.Multiply=\"Multiplicering\"\nBlendingMode.Lighten=\"Lysne\"\nBlendingMode.Darken=\"Mørkne\"\nDeinterlacing.Discard=\"Kassér\"\nDeinterlacing.TopFieldFirst=\"Øverste felt først\"\nDeinterlacing.BottomFieldFirst=\"Nederste felt først\"\nVolControl.SliderUnmuted=\"Lydstyrkeskyder for '%1':\"\nVolControl.SliderMuted=\"Lydstyrkeskyder for '%1': (på lydløs i øjeblikket)\"\nVolControl.Mute=\"Gør '%1' lydløs\"\nVolControl.Properties=\"Egenskaber for '%1'\"\nVolControl.UnassignedWarning.Title=\"Lydkilde ikke tildelt\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" er ikke tildelt nogen lydspor og vil derfor ikke afspille lyd i streams eller optagelser.\\n\\nFor at tildele en lydkilde til et spor, åbn Avancerede lydegenskaber via højre-klik-menuen eller tandhjulet i Lydmixerdokkens værktøjslinje.\"\nBasic.Main.AddSceneDlg.Title=\"Tilføj scene\"\nBasic.Main.AddSceneDlg.Text=\"Angiv navnet på scenen\"\nBasic.Main.AddSceneCollection.Title=\"Tilføj scenesamling\"\nBasic.Main.AddSceneCollection.Text=\"Angiv navnet på scenesamlingen\"\nBasic.Main.RenameSceneCollection.Title=\"Omdøb scenesamling\"\nBasic.Main.RemigrateSceneCollection.Title=\"Opdater opløsning for scenesamling\"\nBasic.Main.RemigrateSceneCollection.Text=\"Vil du opdatere scenesamlingens opløsning af \\\"%1\\\"  til at matche den nuværende profils lærredsstørrelse af %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Kunne ikke opdatere scenesamlings opløsning mens outputs er aktive.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Kunne ikke opdatere scenesamlings opløsning. Den originale opløsning er ukendt.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Nulstil ikke muligt: Skift af OBS opløsning fejlede.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Nulstilling ikke muligt: Nuværende opløsning er allerede grundopløsning for scenesamling.\"\nAddProfile.Title=\"Tilføj profil\"\nAddProfile.Text=\"Angiv profilens navn\"\nAddProfile.WizardCheckbox=\"Vis Auto-opsætningsguide\"\nRenameProfile.Title=\"Omdøb profil\"\nBasic.Main.MixerRename.Title=\"Omdøb lydkilde\"\nBasic.Main.MixerRename.Text=\"Angiv navnet på lydkilden\"\nBasic.Main.PreviewDisabled=\"Forhåndsvisning er pt. inaktiv\"\nBasic.SourceSelect=\"Opret/Vælg kilde\"\nBasic.SourceSelect.CreateNew=\"Opret ny\"\nBasic.SourceSelect.AddExisting=\"Tilføj eksisterende\"\nBasic.SourceSelect.AddVisible=\"Gør kilde synlig\"\nBasic.Main.Sources.Visibility=\"Synlighed\"\nBasic.Main.Sources.VisibilityDescription=\"Styrer synligheden af '%1' på lærredet\"\nBasic.Main.Sources.Lock=\"Lås\"\nBasic.Main.Sources.LockDescription=\"Fastlåser position og skalering af '%1' på lærredet\"\nBasic.PropertiesWindow=\"Egenskaber for '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (auto-vælg: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Vælg farve\"\nBasic.PropertiesWindow.SelectFont=\"Vælg skrifttype\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Vælg en skrifttype\"\nBasic.PropertiesWindow.ConfirmTitle=\"Indstillinger ændret\"\nBasic.PropertiesWindow.Confirm=\"Der er ændringer, som ikke er gemt. Skal de gemmes?\"\nBasic.PropertiesWindow.NoProperties=\"Ingen egenskaber tilgængelige\"\nBasic.PropertiesWindow.AddFiles=\"Tilføj filer\"\nBasic.PropertiesWindow.AddDir=\"Tilføj mappe\"\nBasic.PropertiesWindow.AddURL=\"Tilføj sti/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Føj mappe til '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Føj filer til '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Føj emne til '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Redigér emne fra '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Simple FPS-værdier\"\nBasic.PropertiesView.FPS.Rational=\"Rationelle FPS-værdier\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Gyldige FPS-områder:\"\nBasic.PropertiesView.UrlButton.Text=\"Åbn dette link i din standard webbrowser?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Åbn URL\"\nBasic.InteractionWindow=\"Interagerer med '%1'\"\nBasic.StatusBar.Reconnecting=\"Afbrudt, genforbinder om %2 sekund(-er) (forsøg %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Forsøger at genforbinde... (forsøg %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Genforbundet nu\"\nBasic.StatusBar.Delay=\"Forsinkelse (%1 sek.)\"\nBasic.StatusBar.DelayStartingIn=\"Forsinkelse (starter om %1 sek.)\"\nBasic.StatusBar.DelayStoppingIn=\"Forsinkelse (stopper om %1 sek.)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Forsinkelse (stopper om %1 sek., starter om %2 sek.)\"\nBasic.StatusBar.RecordingSavedTo=\"Optagelse gemt til '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Genafspilningsbuffer gemt til '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Skærmfoto gemt til '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Optagelse auto-remuxed til '%1'\"\nBasic.Filters=\"Filtre\"\nBasic.Filters.AsyncFilters=\"Lyd-/videofiltre\"\nBasic.Filters.AudioFilters=\"Lydfiltre\"\nBasic.Filters.EffectFilters=\"Effektfiltre\"\nBasic.Filters.Title=\"Filtre til '%1'\"\nBasic.Filters.AddFilter.Title=\"Filternavn\"\nBasic.Filters.AddFilter.Text=\"Angiv navnet på filteret\"\nBasic.TransformWindow=\"Sceneemnetransformation\"\nBasic.TransformWindow.Size=\"Størrelse\"\nBasic.TransformWindow.Width=\"Bredde\"\nBasic.TransformWindow.Height=\"Højde\"\nBasic.TransformWindow.Alignment=\"Positionel justering\"\nBasic.TransformWindow.BoundsType=\"Afgrænsningskassetype\"\nBasic.TransformWindow.BoundsAlignment=\"Justering i afgrænsningskasse\"\nBasic.TransformWindow.Bounds=\"Afgrænsningskassestørrelse\"\nBasic.TransformWindow.BoundsWidth=\"Afgrænsningsfeltstørrelse\"\nBasic.TransformWindow.BoundsHeight=\"Afgrænsningsfelthøjde\"\nBasic.TransformWindow.CropToBounds=\"Beskær til afgræsningskasse\"\nBasic.TransformWindow.Crop=\"Beskær\"\nBasic.TransformWindow.CropLeft=\"Beskær venstre\"\nBasic.TransformWindow.CropRight=\"Beskær højre\"\nBasic.TransformWindow.CropTop=\"Beskær top\"\nBasic.TransformWindow.CropBottom=\"Beskær bund\"\nBasic.TransformWindow.Alignment.TopLeft=\"Øverst til venstre\"\nBasic.TransformWindow.Alignment.TopCenter=\"Øverst midtfor\"\nBasic.TransformWindow.Alignment.TopRight=\"Øverst til højre\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Venstre midtfor\"\nBasic.TransformWindow.Alignment.Center=\"Centreret\"\nBasic.TransformWindow.Alignment.CenterRight=\"Højre midtfor\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Nederst til venstre\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Nederst midfor\"\nBasic.TransformWindow.Alignment.BottomRight=\"Nederst til højre\"\nBasic.TransformWindow.BoundsType.None=\"Ingen afgrænsninger\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Kun maksimalstørrelse\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skalér til indre grænser\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skalér til ydre grænser\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skalér til afgrænsningsbredden\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skalér til afgrænsningshøjden\"\nBasic.TransformWindow.BoundsType.Stretch=\"Stræk til grænser\"\nBasic.TransformWindow.Title=\"Redigér Transformation for '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Ingen kilde valgt\"\nBasic.Main.AddSourceHelp.Title=\"Kan ikke tilføje kilde\"\nBasic.Main.AddSourceHelp.Text=\"Du behøver mindst 1 scene for at tilføje en kilde.\"\nBasic.Main.Scenes=\"Scener\"\nBasic.Main.Sources=\"Kilder\"\nBasic.Main.Source=\"Kilde\"\nBasic.Main.Controls=\"Styring\"\nBasic.Main.PreparingStream=\"Forbereder...\"\nBasic.Main.Connecting=\"Forbinder...\"\nBasic.Main.StartRecording=\"Start optagelse\"\nBasic.Main.StartReplayBuffer=\"Start Genafspilnings-buffer\"\nBasic.Main.SaveReplay=\"Gem Genafspilning\"\nBasic.Main.StartStreaming=\"Start streaming\"\nBasic.Main.StartBroadcast=\"Gå Live\"\nBasic.Main.StartVirtualCam=\"Start Virtuelt Kamera\"\nBasic.Main.StopRecording=\"Stop optagelse\"\nBasic.Main.PauseRecording=\"Sæt optagelse på pause\"\nBasic.Main.UnpauseRecording=\"Genoptag optagelse\"\nBasic.Main.SplitFile=\"Opdel optagelsesfil\"\nBasic.Main.AddChapterMarker=\"Tilføj kapitelmarkør (Kun Hybrid MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Stopper optagelse...\"\nBasic.Main.StopReplayBuffer=\"Stop Genafspilnings-buffer\"\nBasic.Main.StoppingReplayBuffer=\"Stopper Genafspilnings-buffer...\"\nBasic.Main.SetupBroadcast=\"Håndtere Udsendelse\"\nBasic.Main.StopStreaming=\"Stop streaming\"\nBasic.Main.StopBroadcast=\"Afslut udsendelse\"\nBasic.Main.AutoStopEnabled=\"(Auto-stop)\"\nBasic.Main.StoppingStreaming=\"Stopper stream...\"\nBasic.Main.ForceStopStreaming=\"Stop streaming (ignorér forsinkelse)\"\nBasic.Main.ShowContextBar=\"Vis Kildeværktøjsbjælke\"\nBasic.Main.HideContextBar=\"Skjul Kildeværktøjsbjælke\"\nBasic.Main.StopVirtualCam=\"Stop Virtuelt Kamera\"\nBasic.Main.Group=\"Gruppe %1\"\nBasic.Main.GroupItems=\"Gruppér valgte emner\"\nBasic.Main.Ungroup=\"Afgruppér\"\nBasic.Main.GridMode=\"Gittertilstand\"\nBasic.Main.ListMode=\"Listetilstand\"\nBasic.Main.VirtualCamConfig=\"Indstil Virtuelt Kamera\"\nBasic.VCam.VirtualCamera=\"Virtuelt Kamera\"\nBasic.VCam.OutputSelection=\"Valg Af Output\"\nBasic.VCam.OutputType.Program=\"Program (standard)\"\nBasic.VCam.OutputSelection.NoSelection=\"Intet valgt for denne outputtype\"\nBasic.VCam.RestartWarning=\"Det virtuelle kamera vil blive genstartet for at ændringen kan ikrafttræde\"\nBasic.MainMenu.File=\"&Fil\"\nBasic.MainMenu.File.Export=\"&Eksport\"\nBasic.MainMenu.File.ShowRecordings=\"Vis optagelse&r\"\nBasic.MainMenu.File.Remux=\"Re&mux optagelser\"\nBasic.MainMenu.File.Settings=\"Ind&stillinger\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Vis Indstillingsmappe\"\nBasic.MainMenu.File.ShowProfileFolder=\"Vis Profilmappe\"\nBasic.MainMenu.File.ShowMissingFiles=\"Tjek for manglende filer\"\nBasic.MainMenu.File.Exit=\"Afslut (&X)\"\nBasic.MainMenu.Edit=\"R&edigér\"\nBasic.MainMenu.Edit.Undo=\"Fortryd (&U)\"\nBasic.MainMenu.Edit.Redo=\"Gendan (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"&Lås forhåndsvisning\"\nBasic.MainMenu.Edit.Scale=\"Forhåndsvisnings&skalering\"\nBasic.MainMenu.Edit.Scale.Window=\"Skalér til vindue\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Lærred (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Skaleret (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Zoom ind\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Zoom ud\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Nulstil zoom\"\nBasic.MainMenu.Edit.Transform=\"&Transformering\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"R&edigere transformering...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopiér transformation\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Indsæt transformation\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Nulstil transforme&ring\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Rotér 90 grader højreom\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Rotér 90 grader venstreom\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Rotér 180 grader\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Flip &Horisontalt\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Flip &Vertikalt\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Tilpas til skærm (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&Stræk til skærm\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centér på skærm\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centrér vertikalt\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centrér horisontalt\"\nBasic.MainMenu.Edit.Order=\"Rækkefølge (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Flyt Op (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Flyt Ne&d\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Flyt til &Toppen\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Flyt til &Bunden\"\nBasic.MainMenu.Edit.AdvAudio=\"&Avancerede lydegenskaber\"\nBasic.MainMenu.View=\"&Vis\"\nBasic.MainMenu.View.Toolbars=\"Værk&tøjsbjælker\"\nBasic.MainMenu.View.ListboxToolbars=\"Dokke værktøjslinjer\"\nBasic.MainMenu.View.ContextBar=\"Kildeværktøjsbjælke\"\nBasic.MainMenu.View.SourceIcons=\"Kildeikoner (&I)\"\nBasic.MainMenu.View.StatusBar=\"&Statusbjælke\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Fuldskærmsgrænseflade\"\nBasic.MainMenu.View.ResetUI=\"Nulstil UI (&R)\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Altid øverst\"\nBasic.MainMenu.View.SceneListMode=\"Scenelistetilstand\"\nBasic.MainMenu.Docks=\"&Dokke\"\nBasic.MainMenu.Docks.ResetDocks=\"Nulstil Dokker (&R)\"\nBasic.MainMenu.Docks.LockDocks=\"&Lås Dokker\"\nBasic.MainMenu.Docks.SideDocks=\"&Fuldhøjdedokker\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Tilpassede browserdokke... (&C)\"\nBasic.MainMenu.Import=\"Importér...\"\nBasic.MainMenu.Export=\"Eksportér...\"\nBasic.MainMenu.SceneCollection=\"&Scenesamling\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Importér profil\"\nBasic.MainMenu.Profile.Export=\"Eksportér profil\"\nBasic.MainMenu.SceneCollection.Import=\"Importér scenesamling\"\nBasic.MainMenu.SceneCollection.Export=\"Eksportér scenesamling\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Nulstil grundopløsning\"\nBasic.MainMenu.SceneCollection.Migrate=\"Sæt grundopløsning\"\nBasic.MainMenu.Profile.Exists=\"Profilen findes allerede\"\nBasic.MainMenu.Tools=\"Værk&tøjer\"\nBasic.MainMenu.Help=\"&Hjælp\"\nBasic.MainMenu.Help.HelpPortal=\"Hjælpe&portal\"\nBasic.MainMenu.Help.Website=\"Besøg &Websted\"\nBasic.MainMenu.Help.Discord=\"Tilmeld til &Discord-server\"\nBasic.MainMenu.Help.WhatsNew=\"Hvad er nyt\"\nBasic.MainMenu.Help.Logs=\"&Logfiler\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Vi&s logfiler\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Upload aktuel logfil (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Upload foregående logfil (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Vis aktuel log\"\nBasic.MainMenu.Help.ReleaseNotes=\"Udgivelsesnoter\"\nBasic.MainMenu.Help.CheckForUpdates=\"Se efter opdateringer\"\nBasic.MainMenu.Help.Repair=\"Tjek filintegritet\"\nBasic.MainMenu.Help.RestartSafeMode=\"Genstart i Fejlsikret tilstand\"\nBasic.MainMenu.Help.RestartNormal=\"Genstart i normal tilstand\"\nBasic.MainMenu.Help.CrashLogs=\"Nedbruds&rapporter\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Vi&s nedbrudsrapporter\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Upload foregående nedbrudsrapport (&P)\"\nBasic.MainMenu.Help.About=\"Om (&A)\"\nBasic.Settings.ProgramRestart=\"Programmet skal genstartes, for at effektuere disse indstillinger.\"\nBasic.Settings.ConfirmTitle=\"Bekræft ændringer\"\nBasic.Settings.Confirm=\"Du har ugemte ændringer, gem dem nu?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 styrer nogle af dine streamindstillinger\"\nBasic.Settings.General=\"Generelt\"\nBasic.Settings.General.Language=\"Sprog\"\nBasic.Settings.General.Updater=\"Opdateringer\"\nBasic.Settings.General.UpdateChannel=\"Opdateringskanal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Deaktiveret)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Standard)\"\nBasic.Settings.General.EnableAutoUpdates=\"Søg automatisk efter opdateringer ved opstart\"\nBasic.Settings.General.OpenStatsOnStartup=\"Åbn statistikdialogen ved opstart\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Skjul OBS-vinduer i skærmoptagelse\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Denne indstilling skjuler alle ikke-projektor OBS Studio-vinduer fra at blive optaget af OBS og påvirke \\nanden optagesoftware, såsom konference-, skærmdelings-, fjernsupport- og skærmfotooptagere mv.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Aktivering af denne indstilling skjuler alle ikke-projektor OBS Studio-vinduer fra at blive optaget af OBS og påvirke anden optagesoftware, såsom konference-, skærmdelings-, fjernsupport-, skærmfotooptagere mv.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Vis bekræftelsesdialog ved opstart af stream\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Vis bekræftelsesdialog ved afslutning af stream\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Vis bekræftelsesdialog ved stop af optagelse\"\nBasic.Settings.General.Projectors=\"Projektorer\"\nBasic.Settings.General.HideProjectorCursor=\"Skjul markør over projektorer\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Projektorer altid øverst\"\nBasic.Settings.General.Snapping=\"Kildejusteringsfastgørelse\"\nBasic.Settings.General.ScreenSnapping=\"Fastgør kilder til skærmkant\"\nBasic.Settings.General.CenterSnapping=\"Fastgør kilder til horisontalt og vertikalt centrum\"\nBasic.Settings.General.SourceSnapping=\"Fastgør kilder til andre kilder\"\nBasic.Settings.General.SnapDistance=\"Fastgøringsfølsomhed\"\nBasic.Settings.General.SpacingHelpers=\"Vis pixeljusteringsguider\"\nBasic.Settings.General.RecordWhenStreaming=\"Optag automatisk ved streaming\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Fortsæt med at optage når stream stopper\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Start automatisk genafsplinings-buffer ved streaming\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Behold genafsplinings-buffer aktiv når stream stopper\"\nBasic.Settings.General.SysTray=\"Systembakke\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimér til systembakke når startet\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Minimér altid til systembakke i stedet for opgavebjælke\"\nBasic.Settings.General.SaveProjectors=\"Gem projektorer ved afslutning\"\nBasic.Settings.General.CloseExistingProjectors=\"Begræns til én fuldskærmsprojektor pr. skærm\"\nBasic.Settings.General.Preview=\"Forhåndsvisning\"\nBasic.Settings.General.OverflowHidden=\"Skjul overløb\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Overløb altid synlig\"\nBasic.Settings.General.OverflowSelectionHidden=\"Vis overløb, selv når kilden er usynlig\"\nBasic.Settings.General.Importers=\"Importører\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Søg på kendte placeringer efter scenesamlinger ved import\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Overgang til scenen ved dobbeltklik\"\nBasic.Settings.General.StudioPortraitLayout=\"Aktivér stående/liggende layout\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Vis etiket for forhåndsvisning/program\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Klik for at skifte mellem scener\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Vis scenenavne\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Tegn sikre områder (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Multiview-layout\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Vandret, øverst (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Vandret, nederst (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Lodret, venstre (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Lodret, højre (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Vandret, øverst (18 scener)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Vandret, øverst (24 scener)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Kun scener (4 scener)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Kun scener (9 scener)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Kun scener (16 scener)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Kun scener (25 scener)\"\nBasic.Settings.General.ChannelName.stable=\"Stabil\"\nBasic.Settings.General.ChannelDescription.stable=\"Seneste stabile udgivelse\"\nBasic.Settings.General.ChannelName.beta=\"Betaer / Udgivelses kandidater\"\nBasic.Settings.General.ChannelDescription.beta=\"Potentielle ustabile før-udgivelsesversioner\"\nBasic.Settings.Appearance=\"Udseende\"\nBasic.Settings.Appearance.General=\"Generelt\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Stil\"\nBasic.Settings.Appearance.General.NoVariant=\"Ingen stiler tilgængelig\"\nBasic.Settings.Appearance.FontScale=\"Skriftstørrelse\"\nBasic.Settings.Appearance.Density=\"Tæthed\"\nBasic.Settings.Appearance.Classic=\"Klassisk\"\nBasic.Settings.Appearance.Compact=\"Kompakt\"\nBasic.Settings.Appearance.Comfortable=\"Behageligt\"\nBasic.Settings.Appearance.OptionsWarning=\"Nogle indstillinger for udseende er ikke muligt for denne stil.\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Benyt godkendelse\"\nBasic.Settings.Stream.Custom.Username=\"Brugernavn\"\nBasic.Settings.Stream.Custom.Password=\"Adgangskode\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: Indtast srp_username.\\nRMTP: Indtast brugernavnet:\\nSRT: Ikke brugt.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: Indtast srp_password.\\nRMTP: Indtast adgangskoden:\\nSRT: Indtast krypteringssætningen.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Aktivér Båndbreddetesttilstand\"\nBasic.Settings.Stream.TTVAddon=\"Twitch Chat-tilføjelser\"\nBasic.Settings.Stream.TTVAddon.None=\"Ingen\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV og FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Manglende Streamopsætning\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Åbn Indstillinger\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL og Stream-nøgle mangler.\\n\\nÅbn Indstillinger for at angive URL og Stream-nøgle under 'Stream'-fanen.\"\nBasic.Settings.Stream.MissingUrl=\"Stream-URL mangler.\\n\\nÅbn Indstillinger for at angive URL'en under 'Stream'-fanen.\"\nBasic.Settings.Stream.MissingStreamKey=\"Stream-nøgle mangler.\\n\\nÅbn Indstillinger for at angive Stream-nøglen under 'Stream'-fanen.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorér anbefalede indstillinger for streamingtjeneste\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Tilsidesæt Anbefalede Indstillinger\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Advarsel: Ignoreres tjenestebegrænsningerne, kan det resultere i forringet streamkvalitet eller forhindre dig i at streame.\\n\\nFortsæt?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maks. videobithastighed: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maks. audiobithastighed: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maks. opløsning: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maks. billeder/sekund (FPS): %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Specificer tilpasset server...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Tilpasset server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Aktiver %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maksimum streamingsbåndbredde\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maksimum videospor\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Tillad streamdump til FLV (bruger simpel optagelses filindstillinger)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Tillad Config Override\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Multispor video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Ekstra lærred\"\nBasic.Settings.Stream.AdvancedOptions=\"Avancerede indstillinger\"\nBasic.Settings.Output.Format=\"Optagelsesformat\"\nBasic.Settings.Output.Format.fMP4=\"Fragmenteret MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmenteret MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmenteret MOV skriver optagelsen i bider og kræver derfor ikke den samme færdiggørelse som traditionelle MOV filer.\\nDette sikrer at filen stadigvæk kan afspilles, hvis skrivning til disken bliver afbrudt af fx BSOD eller strømtab.\\n\\nDette er muligvis kompatibelt med alle afspillere og redigeringsprogrammer. Brug Filer → Remux optagelser til at konvertere filen til en mere kompatibel format, hvis nødvendigt.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmenteret MP4 skriver optagelsen i bider og kræver derfor ikke den samme færdiggørelse som traditionelle MP4 filer.\\nDette sikrer at filen stadigvæk kan afspilles, hvis skrivning til disken bliver afbrudt af fx BSOD eller strømtab.\\n\\nDette er muligvis kompatibelt med alle afspillere og redigeringsprogrammer. Brug Filer → Remux optagelser til at konvertere filen til en mere kompatibel format, hvis nødvendigt.\"\nBasic.Settings.Output.Encoder.Video=\"Video-encoder\"\nBasic.Settings.Output.Encoder.Audio=\"Lyd encoder\"\nBasic.Settings.Output.SelectDirectory=\"Vælg optagelsesmappe\"\nBasic.Settings.Output.DynamicBitrate=\"Skift bithastighed dynamisk for at håndtere overbelastning\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Skift bithastighed dynamisk for at håndtere overbelastning (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"I stedet for at droppe billeder for at reducere overbelastning, ændrer dette dynamisk bithastigheden løbende.\\n\\nBemærk at dette kan øge forsinkelsen, som seerne oplever, hvis der opstår betydelig pludselig overbelastning.\\nNår bithastigheden falder, kan det tage op til et par få minutter for at gendanne den.\\n\\nKun RTMP understøttet pt.\"\nBasic.Settings.Output.Mode=\"Outputtilstand\"\nBasic.Settings.Output.Mode.Simple=\"Simpel\"\nBasic.Settings.Output.Mode.Adv=\"Avanceret\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg-output\"\nBasic.Settings.Output.UseReplayBuffer=\"Aktivér Genafspilnings-buffer\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maks. genafspilningstid\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maksimum hukommelse\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Estimeret hukommelsesforbrug: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Advarsel: Estimeret hukommelsesforbug af %1 MiB er større end det anbefalede maksimum af %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Ikke kan estimere hukommelsesforbrug. Angiv en maks. hukommelsesgrænse.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Genafspilnings-buffer, filnavnpræfiks\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Endelse\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Genafspilningsbuffer kan ikke blive brugt, mens optagelsestypen er sat til Tilpasset output (FFmpeg)\"\nBasic.Settings.Output.Simple.SavePath=\"Optagelsessti\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Optagelseskvalitet\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Samme som stream\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Høj kvalitet, medium filstørrelse\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Synlig identisk kvalitet, stor filstørrelse\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Tabsfri kvalitet, enorm stor filstørrelse\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Advarsel: Streamingsvideobithastigheden indstilles til %1, som er den øvre grænse for den aktuelle streamingtjeneste.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Advarsel: Streamingsaudiobithastigheden indstilles til %1, som er den øvre grænse for den aktuelle streamingtjeneste.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Advarsel: Optagelser kan ikke pauses, hvis optagekvaliteten er sat til \\\"Samme som strream\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Advarsel: Det nuværende optagelsesformat er ikke kompatibelt med den valgte streamencoder\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Advarsel: Optagelse med en software-encoder i en anden kvalitet end den streamede vil medføre ekstra CPU-forbrug, hvis du streamer og optager samtidigt.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Advarsel: Tabsfri kvalitet genererer gevaldigt store filstørrelser! Tabsfri kvalitet kan forbruge op til 7 GB diskplads pr. minut ved høje opløsninger og billedhastigheder. Tabsfri tilstand anbefales ikke til lange optagelser, medmindre du har masser af tilgængelig diskplads. Genafspilningsbuffer er ikke tilgængeligt, når du bruger tabsfri kvalitet.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Sikker på, at du vil benytte tabsfri kvalitet?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Tabsfri kvalitet-advarsel!\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264 lavt, forvalgt CPU-forbrug, øger filstørrelsen)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Standard)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD-spor (Benytter Spor 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Lydspor\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Inkompatibel opløsning/billedhastighed\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Denne streamingtjeneste understøtter ikke din aktuelle outputopløsning og/eller billedhastighed. De ændres til de(n) nærmeste kompatible værdi(er):\\n\\n%1\\n\\nFortsæt?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Opløsning: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"FPS (billeder/sekund): %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Inkompatibel encoder\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Streamingtjenesten \\\"%1\\\" understøtter ikke encoderen \\\"%2\\\". Encoderen ændres til \\\"%3\\\".\\n\\nFortsæt?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Streamingstjenesten \\\"%1\\\" understøtter ikke encoders \\\"%2\\\" og \\\"%3\\\". Disse encodere vil blive skiftet til \\\"%4\\\" og \\\"%5\\\".\\n\\nVil du fortsætte?\"\nBasic.Settings.Output.VideoBitrate=\"Videobit-hastighed\"\nBasic.Settings.Output.AudioBitrate=\"Audiobit-hastighed\"\nBasic.Settings.Output.Reconnect=\"Automatisk gentilslutning\"\nBasic.Settings.Output.RetryDelay=\"Prøv igen-forsinkelse\"\nBasic.Settings.Output.MaxRetries=\"Maks. antal forsøg\"\nBasic.Settings.Output.Advanced=\"Aktivér Tilpasset indkodningsindstillinger (avanceret)\"\nBasic.Settings.Output.EncoderPreset=\"Encoder-forvalg\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (lav CPU-forbrug, laveste kvalitet)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (standard) (medium CPU-forbrug, standardkvalitet)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (højt CPU-forbrug, højkvalitet)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Tilpasset Encoder-indstillinger\"\nBasic.Settings.Output.CustomMuxerSettings=\"Tilpasset Muxer-indstillinger\"\nBasic.Settings.Output.NoSpaceFileName=\"Generér filnavn uden mellemrum\"\nBasic.Settings.Output.Adv.Rescale=\"Omskalér output\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Deaktiveret\"\nBasic.Settings.Output.Adv.AudioTrack=\"Lydspor\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Streamingindstillinger\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Spor 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Spor 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Spor 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Spor 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Sport 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Spor 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD-spor\"\nBasic.Settings.Output.Adv.Encoder=\"Encoder-indstillinger\"\nBasic.Settings.Output.Adv.Recording=\"Optagelse\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Optagelsesindstillinger\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Optagelsestype\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Tilpasset output (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Benyt stream-encoder)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Filnavnsformatering\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Overskriv, hvis fil findes\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Tilpasset output (FFmpeg) er forsynet uden sikkerhedsforanstaltninger.\\nOvervej at bruge \\\"Standard\\\" optagelsestypen.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg outputtype\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Output til URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Output til fil\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Almindelige optagelsesformater\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Alle filer\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Filsti eller URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Containerformat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Lyd\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Standardformat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Containerformat beskrivelse\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Lyd-/Video-codec gættet fra filsti eller URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Standard-encoder\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Deaktivér encoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Video-encoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Video-encoder indstillinger (om nogle)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Lyd-encoder\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Lyd-encoder indstillinger (om nogle)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxer-indstillinger (om nogle)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Keyframe-Interval (billeder)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Vis alle codecs (selv potentielt inkompatible)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg-indstillinger\"\nBasic.Settings.Output.EnableSplitFile=\"Automatisk filopdeling\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Opdel efter tid\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Opdel efter størrelse\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Opdel kun manuelt\"\nBasic.Settings.Output.SplitFile.Time=\"Opdelingstid\"\nBasic.Settings.Output.SplitFile.Size=\"Opdelingsstørrelse\"\nScreenshot=\"Skærmfoto, output\"\nScreenshot.SourceHotkey=\"Skærmfoto, valgte kilde\"\nScreenshot.StudioProgram=\"Skærmfoto (program)\"\nScreenshot.Preview=\"Skærmfoto (forhåndsvisning)\"\nScreenshot.Scene=\"Skærmfoto (scene)\"\nScreenshot.Source=\"Skærmfoto (kilde)\"\nFilenameFormatting.TT.CCYY=\"År, fire cifre\"\nFilenameFormatting.TT.YY=\"År, sidste to cifre (00-99)\"\nFilenameFormatting.TT.MM=\"Måned som decimal (01-12)\"\nFilenameFormatting.TT.DD=\"Dag på måneden, med nuller (01-31)\"\nFilenameFormatting.TT.hh=\"Time i 24 timers format (00-23)\"\nFilenameFormatting.TT.mm=\"Minut (00-59)\"\nFilenameFormatting.TT.ss=\"Sekund (00-59)\"\nFilenameFormatting.TT.Percent=\"Et % tegn\"\nFilenameFormatting.TT.a=\"Forkortet navn på ugedag\"\nFilenameFormatting.TT.A=\"Fuldt navn på ugedag\"\nFilenameFormatting.TT.b=\"Forkortet navn på måned\"\nFilenameFormatting.TT.B=\"Fuldt navn på måned\"\nFilenameFormatting.TT.d=\"Dag på måneden, med nuller (01-31)\"\nFilenameFormatting.TT.H=\"Time i 24 timers format (00-23)\"\nFilenameFormatting.TT.I=\"Time i 12 timers format (01-12)\"\nFilenameFormatting.TT.m=\"Måned som decimal (01-12)\"\nFilenameFormatting.TT.M=\"Minut (00-59)\"\nFilenameFormatting.TT.p=\"AM eller PM betegnelse\"\nFilenameFormatting.TT.s=\"Tid i sekunder siden UNIX epoch\"\nFilenameFormatting.TT.S=\"Sekunder (00-59)\"\nFilenameFormatting.TT.y=\"År, sidste to cifre (00-99)\"\nFilenameFormatting.TT.Y=\"År\"\nFilenameFormatting.TT.z=\"ISO 8601 forskydning fra UTC i tidszone\"\nFilenameFormatting.TT.Z=\"Tidszone navn eller forkortelse\"\nFilenameFormatting.TT.FPS=\"Billeder i sekundet\"\nFilenameFormatting.TT.CRES=\"Grundopløsning (lærred)\"\nFilenameFormatting.TT.ORES=\"Outputopløsning (skaleret)\"\nBasic.Settings.Video.Adapter=\"Skærmkort\"\nBasic.Settings.Video.BaseResolution=\"Grundopløsning (lærred)\"\nBasic.Settings.Video.ScaledResolution=\"Outputopløsning (skaleret)\"\nBasic.Settings.Video.DownscaleFilter=\"Nedskaleringsfilter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Opløsningsmatch, ingen nedskalering krævet]\"\nBasic.Settings.Video.FPSCommon=\"Almindelige FPS-værdier\"\nBasic.Settings.Video.FPSInteger=\"Heltals FPS-værdi\"\nBasic.Settings.Video.FPSFraction=\"Fraktioneret FPS-værdi\"\nBasic.Settings.Video.Numerator=\"Tæller\"\nBasic.Settings.Video.Denominator=\"Nævner\"\nBasic.Settings.Video.Renderer=\"Gengiver\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Eksperimentel)\"\nBasic.Settings.Video.InvalidResolution=\"Ugyldig opløsning. Skal være [bredde]x[højde] (dvs. 1.920x1.080)\"\nBasic.Settings.Video.CurrentlyActive=\"Videooutput erpt. aktivt. Afbryd evt. outputs fra for at ændre videoindstillinger.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinær (hurtigst, men sløret ved skalering)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bikubisk (skarp skalering, 16 samples)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (skarp skalering, 36 samples)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Område (vægtet sum, 4/6/9 eksempler)\"\nBasic.Settings.Audio=\"Lyd\"\nBasic.Settings.Audio.SampleRate=\"Samplingshastighed\"\nBasic.Settings.Audio.Channels=\"Kanaler\"\nBasic.Settings.Audio.Meters=\"Udstyringsmålere\"\nBasic.Settings.Audio.MeterDecayRate=\"Aftagelseshastighed\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Hurtig\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Langsom (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Peak Meter-type\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Samplingsspidsværdi\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Sand spidsværdi (højere CPU-belastning)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ADVARSEL: Surround Sound-lyd er aktiveret.\"\nBasic.Settings.Audio.MultichannelWarning=\"Ved streaming, så tjek, om din streamingtjeneste understøtter både surround sound inddata og afspilning af surround sound. Facebook 360 Live er et eksempel på fuld surround sound-understøttelse; YouTube Live understøtter 5.1 lydinddata (og afspillning på TV'er).\\n\\nOBS lydfiltre er kompatible med surround sound, selvom VST pluginunderstøttelse ikke er garanteret.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Aktivér Surround Sound-lyd?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Sikker på, at du vil aktivere Surround Sound-lyd?\"\nBasic.Settings.Audio.Devices=\"Global Lydenhed\"\nBasic.Settings.Audio.DesktopDevice=\"Computerlyd\"\nBasic.Settings.Audio.DesktopDevice2=\"Computerlyd 2\"\nBasic.Settings.Audio.AuxDevice=\"Mic/Aux Lydenhed\"\nBasic.Settings.Audio.AuxDevice2=\"Mic/Aux Lydenhed 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mic/Aux Lydenhed 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mic/Aux Lydenhed 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Aktivér tryk-gør-tavs\"\nBasic.Settings.Audio.PushToMuteDelay=\"Tryk-gør-tavs forsinkelse\"\nBasic.Settings.Audio.EnablePushToTalk=\"Aktivér tryk-for-tale\"\nBasic.Settings.Audio.PushToTalkDelay=\"Tryk-for-tale forsinkelse\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Enhed ikke tilsluttet eller utilgængelig]\"\nBasic.Settings.Audio.Disabled=\"Deaktiveret\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Lav latens-lydbufferingtilstand (til Decklink-/NDI-udgange)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ADVARSEL: Lav latens-lydbuffering er aktiveret.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Lav latens-lydbufferingtilstand kan forårsage lydfejl eller stoppe afspilning fra nogle kilder.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Aktivér lav latens-lydbufferingtilstand?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Sikker på, at lav latens-lydbufferingtilstand skal aktiveres?\"\nBasic.Settings.Accessibility=\"Tilgængelighed\"\nBasic.Settings.Accessibility.ColorOverrides=\"Brug forskellige farver\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Kildes kant (valg)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Kildes kant (beskær)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Kildes kant (svæv)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mixer-lydstyrkebånd (-60 til -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mixer-lydstyrkebånd (-20 til -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mixer-lydstyrkebånd (-9 til 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mixer-lydstyrkebånd (-60 til -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mixer-lydstyrkebånd (-20 til -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mixer-lydstyrkebånd (-9 til 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Farveforvalg\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Standard\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Tilpasset\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Farveblinhed, alternativ\"\nBasic.Settings.Advanced=\"Avanceret\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Vis advarsel om aktive outputs ved afslutning\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Procesprioritet\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Høj\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Over normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Under normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inaktiv\"\nBasic.Settings.Advanced.FormatWarning=\"Advarsel: Farveformater ud over NV12/P010 er primært beregnet til optagelse og anbefales ikke brugt under streaming, da det kan medføre øget CPU-forbrug grundet farveformatkonvertering.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Advarsel: Høj præcisionsformatter er mere normalt brugt med HDR farveområder.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Advarsel: Rec. 2100 bør bruge et format med større præcision.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Farveformat\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 niveauer)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 niveauer)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 niveauer)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 niveauer)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 niveauer)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 niveauer)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 niveauer)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Farverum\"\nBasic.Settings.Advanced.Video.ColorRange=\"Farveområde\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Begrænset\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Fuld\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR-hvidniveau\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR nominelt spidsniveau\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Moniteringsenhed\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Standard\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Deaktivér Windows-lyddæmpning\"\nBasic.Settings.Advanced.StreamDelay=\"Streamforsinkelse\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Varighed\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Bevar afskæringspunkt (forøg forsinkelse) ved gentilslutning\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Estimeret hukommelsesforbrug: %1 MB\"\nBasic.Settings.Advanced.Network=\"Netværk\"\nBasic.Settings.Advanced.Network.Disabled=\"Aktuelt valgte streaming-protokol understøtter ikke ændring af netværksindstillinger.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Bind til IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP Familie\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Aktivér netværksoptimeringer\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Aktivér TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Forsøger at gøre RTMP-output mere håndterbart for andre latensfølsomme applikationer på netværket ved at regulere transmissionshastigheden.\\nDette kan øge risikoen for tabte billeder ved ustabile forbindelser.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Genvejstast-fokusadfærd\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Deaktivér aldrig genvejstaster\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Deaktivér genvejstaster, når hovedvinduet er i fokus\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Deaktivér genvejstaster, når vindue ikke er i fokus\"\nBasic.Settings.Advanced.AutoRemux=\"Automatisk remux til %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(optag som mkv)\"\nBasic.AdvAudio=\"Avancerede lydegenskaber\"\nBasic.AdvAudio.ActiveOnly=\"Kun aktive kilder\"\nBasic.AdvAudio.Name=\"Navn\"\nBasic.AdvAudio.Volume=\"Lydstyrke\"\nBasic.AdvAudio.VolumeSource=\"Lydstyrke for '%1'\"\nBasic.AdvAudio.MonoSource=\"Mono downmix for '%1'\"\nBasic.AdvAudio.BalanceSource=\"Saldo for '%1'\"\nBasic.AdvAudio.SyncOffset=\"Synk-forskydning\"\nBasic.AdvAudio.SyncOffsetSource=\"Synk-forskydning for '%1'\"\nBasic.AdvAudio.Monitoring=\"Lydovervågning\"\nBasic.AdvAudio.Monitoring.None=\"Overvågning Fra\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Kun overvågning (gør output tavs)\"\nBasic.AdvAudio.Monitoring.Both=\"Overvåg og output\"\nBasic.AdvAudio.MonitoringSource=\"Lydovervågning til '%1'\"\nBasic.AdvAudio.AudioTracks=\"Spor\"\nBasic.Settings.Hotkeys=\"Genvejstaster\"\nBasic.Settings.Hotkeys.Pair=\"Tastekombinationer delt med '%1' fungerer som omskiftere\"\nBasic.Settings.Hotkeys.Filter=\"Filtrér\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrér efter genvejstast\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Genvejstasten deles af en eller flere andre handlinger, klik for at vise konflikter\"\nBasic.Settings.Hotkeys.PleaseWait=\"Indlæser genveje, vent venligst...\"\nBasic.Hotkeys.SelectScene=\"Skift til scene\"\nBasic.SystemTray.Show=\"Vis\"\nBasic.SystemTray.Hide=\"Skjul\"\nBasic.SystemTray.Message.Reconnecting=\"Afbrudt. Genforbinder...\"\nHotkeys.PageUp=\"Page up\"\nHotkeys.Left=\"Venstre\"\nHotkeys.Right=\"Højre\"\nHotkeys.Up=\"Op\"\nHotkeys.Down=\"Ned\"\nHotkeys.Space=\"Mellemrum\"\nHotkeys.NumpadNum=\"Numerisk %1\"\nHotkeys.NumpadMultiply=\"Numerisk, multiplicér\"\nHotkeys.NumpadDivide=\"Numerisk, dividér\"\nHotkeys.NumpadAdd=\"Numerisk, addér\"\nHotkeys.NumpadSubtract=\"Numerisk, subtrahér\"\nHotkeys.NumpadDecimal=\"Numerisk, decimaltegn\"\nHotkeys.AppleKeypadNum=\"%1 (numerisk)\"\nHotkeys.AppleKeypadMultiply=\"* (numerisk)\"\nHotkeys.AppleKeypadDivide=\"/ (numerisk)\"\nHotkeys.AppleKeypadAdd=\"+ (numerisk)\"\nHotkeys.AppleKeypadSubtract=\"- (numerisk)\"\nHotkeys.AppleKeypadDecimal=\", (numerisk)\"\nHotkeys.AppleKeypadEqual=\"= (numerisk)\"\nHotkeys.MouseButton=\"Mus %1\"\nMute=\"Lyd fra\"\nUnmute=\"Lyd til\"\nPush-to-mute=\"Tryk-gør-tavs\"\nPush-to-talk=\"Tryk-for-tale\"\nSceneItemShow=\"Vis '%1'\"\nSceneItemHide=\"Skjul '%1'\"\nOutputWarnings.NoTracksSelected=\"Du skal vælge minimum ét spor\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Fejl i outputindstillinger\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Alle outputs skal have midst et lydspor valgt.\"\nOutputWarnings.MP4Recording=\"Advarsel: MP4-/MOV-optagelser vil ikke kunne genoprettes, såfremt filen ikke kan færdiggøres (grundet f.eks. BSOD'er, strømafbrydelse mv.). Vil du optage flere lydspor, så overvej at benytte MKV, og remuxe optagelsen til MP4, efter at den er færdiggjort (Fil → Remux optagelser)\"\nOutputWarnings.CannotPause=\"Advarsel: Optagelser kan ikke pauses, hvis optagelsesencoderen er sat til \\\"(Brug strreamenccoder)\\\"\"\nOutputWarnings.CodecIncompatible=\"Valg af lyd- eller videoencoder was nulstillet på grund af inkompabilitet. Vælg venligst en kompatibel encoder fra listen.\"\nCodecCompat.Incompatible=\"(Ikke kompatibel med %1)\"\nCodecCompat.CodecPlaceholder=\"Vælg encoder...\"\nCodecCompat.ContainerPlaceholder=\"Vælg format...\"\nCodecCompat.CodecMissingOnExit.Title=\"Ingen encoder valgt\"\nCodecCompat.CodecMissingOnExit.Text=\"Mindst en video- eller lydencoder er ikke blevet sat. Sørg for at sætte encodere til både optagelse og streaming.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Intet format er valgt\"\nCodecCompat.ContainerMissingOnExit.Text=\"Intet optagelsesformat er blevet valgt. Vælg venligst et optagelsesformat kompatibelt med den valgte streamingsencoder.\"\nFinalScene.Title=\"Slet scene\"\nFinalScene.Text=\"Mindst én scene kræves.\"\nNoSources.Title=\"Ingen kilder\"\nNoSources.Text=\"Det ser ud til, at du endnu ikke har tilføjet nogle videokilder, så dit output bliver en blank/tom skærm. Sikker på, at du vil gøre dette?\"\nNoSources.Text.AddSource=\"Du kan til enhver tid tilføje kilder ved at klikke på +-ikonet under feltet Kilder i hovedvinduet.\"\nNoSources.Label=\"Du har ingen kilder.\\nKlik på knappen + nedenfor,\\neller højreklik hér for at tilføje en.\"\nChangeBG=\"Sæt farve\"\nCustomColor=\"Tilpasset farve\"\nBrowserSource.EnableHardwareAcceleration=\"Aktivér Browser Source-hardwareacceleration\"\nAbout=\"Om\"\nAbout.Info=\"OBS Studio er en helt gratis open-source software til videooptagelse og live-streaming.\"\nAbout.Donate=\"Giv et bidrag\"\nAbout.GetInvolved=\"Deltag\"\nAbout.Authors=\"Forfattere\"\nAbout.License=\"Licens\"\nAbout.Error=\"Fejl! Fil kunne ikke blive læst. \\n\\nGå til: %1\"\nAbout.Contribute=\"Støt OBS-projektet\"\nAddUrl.Title=\"Tilføj kilde via URL\"\nAddUrl.Text=\"Du har trukket en URL ind i OBS, hvilket automatisk tilføjer linket som kilde. Fortsæt?\"\nResizeOutputSizeOfSource=\"Ændr størrelse på output (kildestørrelse)\"\nResizeOutputSizeOfSource.Text=\"Grund- og outputopløsningerne tilpasses den aktuelle kildes størrelse.\"\nResizeOutputSizeOfSource.Continue=\"Ønsker du at fortsætte?\"\nPreviewTransition=\"Overgangsforhåndsvisning\"\nImporter=\"Scenesamlingsimportør\"\nImporter.SelectCollection=\"Vælg en scenesamling\"\nImporter.Collection=\"Scenesamling\"\nImporter.HelpText=\"Føj filer til dette vindue for at importere samlinger fra OBS eller andre understøttede programmer.\"\nImporter.Path=\"Sti til Samling\"\nImporter.Program=\"Fundet program\"\nImporter.AutomaticCollectionPrompt=\"Søg automatisk efter scenesamlinger\"\nImporter.AutomaticCollectionText=\"OBS kan automatisk finde importerbare scenesamlinger fra understøttede tredjepartsprogrammer. Skal OBS automatisk finder samlinger til dig?\\n\\nDu kan ændre dette senere i Indstillinger > Generelt > Importører.\"\nImporter.SelectFile=\"Gennemse...\"\nRestart=\"Genstart\"\nNeedsRestart=\"Genstart af OBS Studio er nødvendig. Genstart nu?\"\nLoadProfileNeedsRestart=\"Profilen indeholder indstillinger, som kræver genstart af OBS:\\n%1\\n\\nGenstart OBS for at effektuere disse indstillinger?\"\nContextBar.NoSelectedSource=\"Ingen kilde valgt\"\nContextBar.MediaControls.PlayMedia=\"Afspil medie\"\nContextBar.MediaControls.PauseMedia=\"Pause medie\"\nContextBar.MediaControls.StopMedia=\"Stop medie\"\nContextBar.MediaControls.RestartMedia=\"Genstart medie\"\nContextBar.MediaControls.PlaylistNext=\"Næste på Afspilningsliste\"\nContextBar.MediaControls.PlaylistPrevious=\"Foregående på Afspilningsliste\"\nContextBar.MediaControls.BlindSeek=\"Mediesøgnings-widget\"\nYouTube.Auth.Ok=\"Godkendelse fuldført.\\nDenne side kan nu lukkes.\"\nYouTube.Auth.NoCode=\"Denne godkendelsesproces blev ikke fuldført.\"\nYouTube.Auth.NoChannels=\"Ingen tilgængelig kanal(er) på den valgte konto\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube Brugergodkendelse\"\nYouTube.Auth.WaitingAuth.Text=\"Fuldfør godkendelsen i en ekstern browser.<br>Åbner den eksterne browser ikke, så følg dette link og fuldfør godkendelsen:<br>%1\"\nYouTube.AuthError.Text=\"Mislykkedes at hente kanalinformation: %1.\"\nYouTube.Actions.WindowTitle=\"YouTube Udsendelsesopsætning - Kanal: %1\"\nYouTube.Actions.CreateNewEvent=\"Opret ny udsendelse\"\nYouTube.Actions.ChooseEvent=\"Vælg eksisterende udsendelse\"\nYouTube.Actions.Title=\"Titel*\"\nYouTube.Actions.MyBroadcast=\"Min Udsendelse\"\nYouTube.Actions.Description=\"Beskrivelse\"\nYouTube.Actions.Privacy=\"Fortrolighed*\"\nYouTube.Actions.Privacy.Private=\"Privat\"\nYouTube.Actions.Privacy.Public=\"Offentlig\"\nYouTube.Actions.Privacy.Unlisted=\"Ikke listet\"\nYouTube.Actions.Category=\"Kategori\"\nYouTube.Actions.Thumbnail=\"Miniature\"\nYouTube.Actions.Thumbnail.SelectFile=\"Vælg fil...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Ingen fil valgt\"\nYouTube.Actions.Thumbnail.ClearFile=\"Ryd\"\nYouTube.Actions.MadeForKids=\"Er denne video beregnet til børn?*\"\nYouTube.Actions.MadeForKids.Yes=\"Ja, den er beregnet til børn\"\nYouTube.Actions.MadeForKids.No=\"Nej, den er ikke beregnet til børn\"\nYouTube.Actions.AdditionalSettings=\"Yderligere indstillinger\"\nYouTube.Actions.Latency=\"Ventetid\"\nYouTube.Actions.Latency.Low=\"Lav\"\nYouTube.Actions.Latency.UltraLow=\"Ultra lav\"\nYouTube.Actions.EnableAutoStart=\"Aktivér auto-start\"\nYouTube.Actions.EnableAutoStop=\"Aktivér auto-stop\"\nYouTube.Actions.AutoStartStop.TT=\"Angiver, hvorvidt denne planlagte udsendelse skal starte automatisk\"\nYouTube.Actions.EnableDVR=\"Aktivér DVR\"\nYouTube.Actions.360Video=\"360-video\"\nYouTube.Actions.ScheduleForLater=\"Planlæg til senere\"\nYouTube.Actions.RememberSettings=\"Husk disse indstillinger\"\nYouTube.Actions.Create_Ready=\"Opret udsendelse\"\nYouTube.Actions.Create_GoLive=\"Opret udsendelse og påbegynd streaming\"\nYouTube.Actions.Choose_Ready=\"Vælg udsendelse\"\nYouTube.Actions.Choose_GoLive=\"Vælg udsendelse og påbegynd streaming\"\nYouTube.Actions.Create_Schedule=\"Planlæg udsendelse\"\nYouTube.Actions.Create_Schedule_Ready=\"Planlæg og vælg udsendelse\"\nYouTube.Actions.Dashboard=\"Åbn YouTube Studio\"\nYouTube.Actions.Error.Title=\"Fejl ved oprettelse af live-udsendelse\"\nYouTube.Actions.Error.Text=\"YouTube-adgangsfejl '%1'.<br/>En detaljeret fejlbeskrivelse kan findes på <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTube-adgangsfejl. Tjek netværksforbindelse eller YouTube-serveradgang.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Fejl ved oprettelse af udsendelse '%1'.<br/>En detaljeret fejlbeskrivelse kan findes på <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Ingen stream oprettet. Genassociér konto.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube-API fejl. Tjek logfilen for yderligere oplysninger.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Den valgte udsendelse kunne ikke findes.\"\nYouTube.Actions.Error.FileMissing=\"Valgte fil findes ikke.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Mislykkedes at åbne valgte fil.\"\nYouTube.Actions.Error.FileTooLarge=\"Valgte fil er for stor (maks.: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Overførslen af udsendelsen mislykkedes: %1<br/><br/>Fortsætter denne fejl <a href='https://studio.youtube.com/video/%2/livestreaming'>, så åbn udsendelsen i YouTube Studio</a> og forsøg manuelt.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Udsendelse overføres til testfasen, hvilket kan tage lidt tid. Prøv igen om 10-30 sekunder.\"\nYouTube.Actions.EventsLoading=\"Indlæser liste over begivenheder...\"\nYouTube.Actions.EventCreated.Title=\"Begivenhed oprettet\"\nYouTube.Actions.EventCreated.Text=\"Begivenhed er oprettet.\"\nYouTube.Actions.Stream.ScheduledFor=\"Planlagt til %1\"\nYouTube.Actions.Stream.Resume=\"Genoptag afbrudt stream\"\nYouTube.Actions.Stream.YTStudio=\"Automatisk oprettet af YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Opretter en ny Live-udsendelse, afvent venligst...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Manuel start nødvendig\"\nYouTube.Actions.AutoStartStreamingWarning=\"Auto-start er deaktiveret for denne begivenhed. Klik på \\\"Gå Live\\\" for at starte udsendelse.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Forbindelsen vil ikke kunne genoprettes.<br>Streamen stoppes, og live-udsendelse vil ophøre.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Live streaming er ikke aktiveret på den valgte YouTube-kanal.<br/><br/>Se <a href='https://www.youtube.com/features'>youtube.com/features</a> for mere information.\"\nYouTube.Errors.livePermissionBlocked=\"Live streaming er utilgængelig på den valgte YouTube-kanal.<br/>Bemærk, at det kan tage op til 24 timer inden live streaming er tilgængelig, efter at det er aktiveret i kanalindstillingerne.<br/><br/>Se <a href='https://www.youtube.com/features'>youtube.com/features</a> for detaljer.\"\nYouTube.Errors.errorExecutingTransition=\"Overførslen mislykkedes grundet en backend-fejl. Forsøg igen om et par sekunder.\"\nYouTube.Errors.errorStreamInactive=\"YouTube modtager ikke data til din stream. Tjek opsætningen og forsøg igen.\"\nYouTube.Errors.invalidTransition=\"Overførselsforsøget var ugyldigt. Årsagen an være, at streamen ikke havde afsluttet en tidligere overførsel. Vent et par sekunder og forsøg igen.\"\nYouTube.DocksRemoval.Title=\"Fjern ældre YouTube Browser Docks\"\nYouTube.DocksRemoval.Text=\"Disse browserdokker vil blive fjernet som forældede:\\n\\n%1\\nBrug \\\"Dokker/YouTube Live Control Panel\\\" i stedet.\"\nConfigDownload.WarningMessageTitle=\"Advarsel\"\nFailedToStartStream.MissingConfigURL=\"Intet config URL tilgængeligt for den nuværende tjeneste\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Tilpasset RTMP URL ikke specificeret\"\nFailedToStartStream.InvalidCustomConfig=\"Ugyldig tilpasset config\"\nFailedToStartStream.MissingCanvas=\"Et konfigureret ekstra lærred mangler\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Kunne ikke lave multispor video tjeneste\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Kunne ikke lave multispor video RTMP-output\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC ikke tilgængeligt.\\n\\nFejlede i at finde encodertype '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Fejlede i at skabe videoencoder '%1' (type: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Kunne ikke få OBS videoinfo ved oprettelsen af encoder '%1' (type: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Fejlede i at skabe lydencoder\"\nFailedToStartStream.NoRTMPURLInConfig=\"Config indeholder ikke streammålets RTMP(S) URL\"\nFailedToStartStream.FallbackToDefault=\"Start af streaming med %1 fejlede; vil du prøve igen ved bruge af simpel encodingindstillinger?\"\nFailedToStartStream.ConfigRequestFailed=\"Kunne ikke finde config fra %1<br><br>HTTP Fejl: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Fik ukendt statusværdi '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nVil du fortsætte med at streame uden %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nVil du fortsætte med at streame?\"\nFailedToStartStream.MissingEncoderConfigs=\"Go Live config indeholt ikke encoderkonfigurationer\"\nFailedToStartStream.InvalidEncoderConfig=\"Go Live config indeholdt en ugyldig encoderkonfiguration\"\nFailedToStartStream.StatusMissingHTML=\"Go Live anmodning fik en uspeciferet fejl\"\nFailedToStartStream.NoConfigSupplied=\"Manglede config\"\nMultitrackVideo.Info=\"%1 optimerer automatisk dine indstillinger til at encode og sende flere videokvaliteter. Ved at vælge denne indstilling sendes %2 informationer om din computer og programopsætning.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Ikke-kompatible indstillinger\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 er ikke i øjeblikket kompatibel med: \\n\\n%2\\nHvis du vil fortsat streame med %1, skal du fjerne ikke-kompatible indstillinger:\\n\\n%3\\nog Start streaming igen.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Deaktiver for denne stream og start streaming\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Opdater indstillinger og start streaming\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 er i øjeblikket ikke kompatibel med [Lyd → Generelt → Kanaler] sat til '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Lyd → Generelt → Kanaler] skal sættes til '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 kræver flere forskellige indstilling for [Lyd → Generalt → Kanaler]\"\nPluginManager.MissingPlugin=\"[PLUGIN IKKE FUNDET]\"\nPluginManager.Section.Discover=\"Gennemse\"\nPluginManager.Section.Manage=\"Installeret\"\nPluginManager.Section.Updates=\"Opdateringer\"\n"
  },
  {
    "path": "frontend/data/locale/de-DE.ini",
    "content": "Language=\"Deutsch\"\nApply=\"Übernehmen\"\nCancel=\"Abbrechen\"\nClose=\"Schließen\"\nSave=\"Speichern\"\nDiscard=\"Verwerfen\"\nDisable=\"Deaktivieren\"\nYes=\"Ja\"\nNo=\"Nein\"\nAdd=\"Hinzufügen\"\nRemove=\"Entfernen\"\nRename=\"Umbenennen...\"\nInteract=\"Interagieren\"\nFilters=\"Filter\"\nProperties=\"Eigenschaften\"\nMoveUp=\"Hochbewegen\"\nMoveDown=\"Runterbewegen\"\nSettings=\"Einstellungen\"\nDisplay=\"Monitor\"\nExit=\"Beenden\"\nMixer=\"Audiomixer\"\nBrowse=\"Durchsuchen\"\nDroppedFrames=\"Ausgelassene Frames %1 (%2 %)\"\nProjector.Open.Program=\"Programmprojektor öffnen\"\nProjector.Open.Preview=\"Vorschauprojektor öffnen\"\nProjector.Open.Scene=\"Szenenprojektor öffnen\"\nProjector.Open.Source=\"Quellenprojektor öffnen\"\nProjector.Open.Multiview=\"Multiview öffnen\"\nProjector.Display=\"Bildschirm: %1\"\nProjector.Window=\"Neues Fenster\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Szene: %1\"\nProjector.Title.Source=\"Quelle: %1\"\nProjector.ResizeWindowToContent=\"Fenster an Inhalt anpassen\"\nClear=\"Leeren\"\nRevert=\"Rückgängig\"\nShow=\"Zeigen\"\nHide=\"Verbergen\"\nUnhideAll=\"Alle einblenden\"\nUntitled=\"Unbenannt\"\nNew=\"Neu...\"\nDuplicate=\"Duplizieren\"\nEnable=\"Aktivieren\"\nDisableOSXVSync=\"macOS-VSync deaktivieren\"\nResetOSXVSyncOnExit=\"macOS-VSync beim Beenden zurücksetzen\"\nHighResourceUsage=\"Kodierung überlastet. Reduzieren Sie die Videoeinstellungen oder verwenden Sie eine schnellere Kodierervoreinstellung.\"\nTransition=\"Übergang\"\nQuickTransitions=\"Schnellübergänge\"\nFadeToBlack=\"Schwarzüberblende\"\nLeft=\"Links\"\nRight=\"Rechts\"\nTop=\"Oben\"\nBottom=\"Unten\"\nReset=\"Zurücksetzen\"\nHours=\"Stunde(n)\"\nMinutes=\"Minute(n)\"\nSeconds=\"Sekunde(n)\"\nDeprecated=\"Veraltet\"\nReplayBuffer=\"Replaypuffer\"\nImport=\"Importieren\"\nExport=\"Exportieren\"\nCopy=\"Kopieren\"\nPaste=\"Einfügen\"\nPasteReference=\"Einfügen (Verweis)\"\nPasteDuplicate=\"Einfügen (Duplikat)\"\nRemuxRecordings=\"Aufnahmen remuxen\"\nNext=\"Weiter\"\nBack=\"Zurück\"\nDefaults=\"Zurücksetzen\"\nRestoreDefaults=\"Zurücksetzen\"\nHideMixer=\"Im Mixer verbergen\"\nTransitionOverride=\"Übergangsüberschreibung\"\nShowTransition=\"Zeige-Übergang\"\nHideTransition=\"Verstecke-Übergang\"\nNone=\"Keine\"\nStudioMode.Preview=\"Vorschau\"\nStudioMode.Program=\"Programm\"\nStudioMode.PreviewSceneName=\"Vorschau: %1\"\nStudioMode.ProgramSceneName=\"Programm: %1\"\nShowInMultiview=\"In Multiview anzeigen\"\nVerticalLayout=\"Vertikales Layout\"\nGroup=\"Gruppe\"\nDoNotShowAgain=\"Nicht nochmal anzeigen\"\nDefault=\"(Standard)\"\nCalculating=\"Wird berechnet …\"\nFullscreen=\"Vollbildmodus\"\nWindowed=\"Fenstermodus\"\nRefreshBrowser=\"Aktualisieren\"\nAspectRatio=\"Seitenverhältnis <b>%1:%2</b>\"\nLockVolume=\"Lautstärke sperren\"\nLogViewer=\"Log ansehen\"\nShowOnStartup=\"Beim Start anzeigen\"\nOpenFile=\"Datei öffnen\"\nAddScene=\"Szene hinzufügen\"\nAddSource=\"Quelle hinzufügen\"\nRemoveScene=\"Ausgewählte Szene entfernen\"\nRemoveSource=\"Ausgewählte Quelle(n) entfernen\"\nMoveSceneUp=\"Szene hochbewegen\"\nMoveSceneDown=\"Szene runterbewegen\"\nMoveSourceUp=\"Quelle(n) hochbewegen\"\nMoveSourceDown=\"Quelle(n) runterbewegen\"\nSourceProperties=\"Quelleigenschaften öffnen\"\nSourceFilters=\"Quellfilter öffnen\"\nMixerToolbarMenu=\"Audiomixermenü\"\nSceneFilters=\"Szenenfilter öffnen\"\nList=\"Liste\"\nGrid=\"Raster\"\nAutomatic=\"Automatisch\"\nComingSoon=\"Bald verfügbar\"\nPluginsFailedToLoad.Title=\"Fehler beim Laden von Plugins\"\nPluginsFailedToLoad.Text=\"Folgende OBS-Plugins konnten nicht geladen werden:\\n\\n%1\\nAktualisieren oder entfernen Sie diese.\"\nAlreadyRunning.Title=\"OBS wird bereits ausgeführt\"\nAlreadyRunning.Text=\"OBS wird bereits ausgeführt und sollte gegebenenfalls vor dem Start einer weiteren Instanz (die sich möglicherweise im Infobereich befindet) beendet werden.\"\nAlreadyRunning.LaunchAnyway=\"Trotzdem starten\"\nCrashHandling.Dialog.Title=\"OBS Studio Absturz erkannt\"\nCrashHandling.Labels.Text=\"OBS Studio wurde nicht ordnungsgemäß heruntergefahren.\\n\\nAusführen im abgesicherten Modus (mit deaktivierten Drittanbieter-Plugins, Skripten und WebSockets)?\"\nCrashHandling.Labels.PrivacyNotice=\"Sie können auch festlegen, dass der letzte Absturzprotokoll automatisch in das OBSProject hochgeladen wird.<br /><br />Bitte lesen Sie die <a href='https://obsproject.com/privacy-policy'>Datenschutzbestimmungen</a>, bevor Sie Dateien hochladen, und achten Sie besonders auf die Abschnitte, die das Hochladen von Dateien betreffen.\"\nCrashHandling.Checkbox.SendReport=\"Ich habe die Datenschutzbestimmungen gelesen und stimme dem Upload zu.\"\nCrashHandling.Buttons.LaunchSafe=\"Im abgesicherten Modus starten\"\nCrashHandling.Buttons.LaunchNormal=\"Im normalen Modus starten\"\nCrashHandling.Errors.UploadJSONError=\"Beim Versuch, das letzte Absturzprotokoll hochzuladen, ist ein Fehler aufgetreten. Bitte versuchen Sie es später noch einmal.\"\nCrashHandling.Errors.Title=\"Fehler beim Hochladen des Absturzprotokolls\"\nSafeMode.Restart=\"Möchten Sie OBS im abgesicherten Modus (mit deaktivierten Drittanbieter-Plugins, Skripten und WebSockets) neu starten?\"\nSafeMode.RestartNormal=\"Möchten Sie OBS normal neu starten?\"\nChromeOS.Title=\"Nicht unterstützte Plattform\"\nChromeOS.Text=\"OBS scheint in einem ChromeOS-Container zu laufen. Diese Plattform wird nicht unterstützt.\"\nWine.Title=\"Wine erkannt\"\nWine.Text=\"OBS in Wine auszuführen wird nicht unterstützt und viele Funktionen wie Aufnahme- oder Gerätequellen funktionieren nur teils oder gar nicht.<br><br>Es wird empfohlen, stattdessen eine native Version von OBS zu verwenden, z. B. <a href='https://flathub.org/apps/details/com.obsproject.Studio'>unsere Flatpak-Version</a> oder die Pakete Ihres Betriebssystems.\"\nDockCloseWarning.Title=\"Dockbares Fenster schließen\"\nDockCloseWarning.Text=\"Sie haben gerade ein dockbares Fenster geschlossen. Wenn Sie es erneut anzeigen möchten, verwenden Sie das Docks-Menü in der Menüleiste.\"\nExtraBrowsers=\"Benutzerdefinierte Browser-Docks\"\nExtraBrowsers.Info=\"Fügen Sie Docks hinzu, indem Sie ihnen einen Namen und eine URL geben und dann auf „Übernehmen“ oder „Schließen“ klicken, um diese zu öffnen.\"\nExtraBrowsers.DockName=\"Name\"\nAuth.Authing.Title=\"Authentifizierung läuft …\"\nAuth.Authing.Text=\"Authentifizierung mit %1, bitte warten …\"\nAuth.AuthFailure.Title=\"Authentifizierungsfehler\"\nAuth.AuthFailure.Text=\"Authentifizierung mit %1 fehlgeschlagen:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Authentifizierung erforderlich\"\nAuth.InvalidScope.Text=\"Die Authentifizierungsanforderungen für %1 haben sich geändert. Einige Funktionen sind möglicherweise nicht verfügbar.\"\nAuth.LoadingChannel.Title=\"Kanalinformationen werden geladen …\"\nAuth.LoadingChannel.Text=\"Kanalinformationen für %1 werden geladen, bitte warten …\"\nAuth.LoadingChannel.Error=\"Kanalinformationen konnten nicht abgerufen werden.\"\nAuth.ChannelFailure.Title=\"Laden des Kanals fehlgeschlagen\"\nAuth.ChannelFailure.Text=\"Laden der Kanalinformationen für %1 fehlgeschlagen\\n\\n%2: %3\"\nAuth.StreamInfo=\"Streaminformation\"\nTwitchAuth.Stats=\"Twitch-Statistiken\"\nTwitchAuth.Feed=\"Twitch-Aktivitätsfeed\"\nTwitchAuth.TwoFactorFail.Title=\"Streamschlüssel konnte nicht abgefragt werden\"\nTwitchAuth.TwoFactorFail.Text=\"OBS konnte sich nicht mit Ihrem Twitch-Konto verbinden. Bitte stellen Sie sicher, dass die Zwei-Faktor-Authentifizierung in Ihren <a href='https://www.twitch.tv/settings/security'>Twitch-Sicherheitseinstellungen</a> eingerichtet ist, da diese für das Streamen benötigt wird.\"\nRestreamAuth.Channels=\"Restream-Kanäle\"\nCopy.Filters=\"Filter kopieren\"\nPaste.Filters=\"Filter einfügen\"\nBrowserPanelInit.Title=\"Browser wird initialisiert …\"\nBrowserPanelInit.Text=\"Browser wird initialisiert, bitte warten …\"\nBandwidthTest.Region.US=\"USA\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Asien\"\nBandwidthTest.Region.Other=\"Andere\"\nBasic.AutoConfig=\"Einrichtungsassistent\"\nBasic.AutoConfig.ApplySettings=\"Einstellungen übernehmen\"\nBasic.AutoConfig.StartPage=\"Informationen zur Verwendung\"\nBasic.AutoConfig.StartPage.SubTitle=\"Wofür möchten Sie das Programm verwenden?\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Für das Streamen optimieren, Aufnehmen ist zweitrangig\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Für das Aufnehmen optimieren, Streamen ist zweitrangig\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Ich werde nur die virtuelle Kamera verwenden\"\nBasic.AutoConfig.VideoPage=\"Videoeinstellungen\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Geben Sie die gewünschten Videoeinstellungen an.\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Aktuelle verwenden (%1 × %2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Monitor %1 (%2 × %3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Aktuelle verwenden (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Entweder 60 oder 30, aber wenn möglich, 60 bevorzugen\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Entweder 60 oder 30, aber hohe Auflösung bevorzugen\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Hinweis: Die Basis-(Leinwand-)Auflösung ist nicht unbedingt Ihre Stream- oder Aufnahmeauflösung. Ihre tatsächliche Auflösung kann ausgehend von der Leinwandauflösung herunterskaliert werden, um die Ressourcennutzung oder Bitratenanforderungen zu reduzieren.\"\nBasic.AutoConfig.StreamPage=\"Streaminformationen\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Bitte geben Sie Ihre Streaminformationen ein\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Konto verbinden (empfohlen)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Konto trennen\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Konto trennen?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Diese Änderung wird sofort wirksam. Möchten Sie Ihr Konto wirklich trennen?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Streamschlüssel erhalten\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Mehr Informationen\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Streamschlüssel verwenden\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Streamschlüssel verwenden (erweitert)\"\nBasic.AutoConfig.StreamPage.Service=\"Plattform\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Alle anzeigen\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Benutzerdefiniert\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Streamschlüssel\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: Geben Sie das Verschlüsselungspasswort ein.\\nRTMP: Geben Sie den vom Dienst bereitgestellten Schlüssel ein.\\nSRT: Geben Sie die streamid ein, wenn die Plattform eine verwendet.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kodiererschlüssel\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Verbundenes Konto\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Bitrate mit Bandbreitentest schätzen (für potenziell einige Minuten)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Hardwarekodierung bevorzugen\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Bei der Hardwarekodierung entfällt die meiste CPU-Nutzung, es kann aber eine höhere Bitrate erforderlich sein, um das gleiche Qualitätsniveau zu erreichen.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Streamwarnung\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Der Bandbreitentest streamt zufällige Videodaten ohne Ton zu Ihrem Kanal. Es ist empfohlen, das Speichern von Videos vorrübergehend zu deaktivieren und den Stream privat zu schalten, bis der Test abgeschlossen ist. Möchten Sie fortfahren?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"%1 testen\"\nBasic.AutoConfig.TestPage=\"Endergebnisse\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Das Programm führt nun eine Reihe von Tests durch, um optimale Einstellungen zu finden.\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Tests abgeschlossen\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Bandbreitentests werden durchgeführt, dies kann einige Minuten dauern …\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Keine Ausgabe für das Protokoll dieser Plattform wurde gefunden.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Verbindungsaufbau mit: %1 …\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Keine Verbindung zu Servern möglich, bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Bandbreite wird getestet für: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Streamkodierer wird getestet, dies kann einige Minuten dauern …\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Aufnahmekodierer wird getestet, dies kann einige Minuten dauern …\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Starten des Kodierers fehlgeschlagen\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"%1 × %2 mit %3 FPS wird getestet …\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Streamkodierer\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Aufnahmekodierer\"\nBasic.AutoConfig.TestPage.Result.Header=\"Das Programm hat festgestellt, dass diese geschätzten Einstellungen für Sie optimal sind:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Um den Assistenten neu zu konfigurieren und es erneut zu versuchen, klicken Sie auf „Zurück“. Um die Einstellungen selbst anzupassen, klicken Sie auf „Abbrechen“ und öffnen Sie die Einstellungen.\"\nBasic.AutoConfig.Info=\"Der Einrichtungsassistent ermittelt die besten Einstellungen basierend auf Ihren Computerspezifikationen und der Internetgeschwindigkeit.\"\nBasic.AutoConfig.RunAnytime=\"Er kann jederzeit über das Werkzeuge-Menü ausgeführt werden.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"(Skalierte) Auflösung wird gestreamt\"\nBasic.Stats=\"Statistiken\"\nBasic.Stats.CPUUsage=\"CPU-Auslastung\"\nBasic.Stats.HDDSpaceAvailable=\"Verfügbarer Speicherplatz\"\nBasic.Stats.MemoryUsage=\"RAM-Nutzung\"\nBasic.Stats.AverageTimeToRender=\"Durchschnittliche Framerenderzeit\"\nBasic.Stats.SkippedFrames=\"Übersprungene Frames durch Kodierungsverzögerung\"\nBasic.Stats.MissedFrames=\"Ausgelassene Frames durch Renderverzögerung\"\nBasic.Stats.Output.Recording=\"Aufnahme\"\nBasic.Stats.Status.Recording=\"Aufnehmen\"\nBasic.Stats.Status.Reconnecting=\"Erneut verbinden\"\nBasic.Stats.Status.Inactive=\"Inaktiv\"\nBasic.Stats.Status.Active=\"Aktiv\"\nBasic.Stats.DroppedFrames=\"Ausgelassene Frames (Netzwerk)\"\nBasic.Stats.MegabytesSent=\"Gesamte Datenausgabe\"\nBasic.Stats.DiskFullIn=\"Datenträger voll in ungefähr\"\nBasic.Stats.DiskFullIn.Text=\"%1 Stunde(n), %2 Minute(n)\"\nBasic.Stats.ResetStats=\"Statistiken zurücksetzen\"\nResetUIWarning.Title=\"Möchten Sie die Benutzeroberfläche wirklich zurücksetzen?\"\nResetUIWarning.Text=\"Durch das Zurücksetzen der Benutzeroberfläche werden zusätzliche Docks ausgeblendet. Sie können diese im Menü „Docks“ sichtbar machen.\\n\\nMöchten Sie die Benutzeroberfläche wirklich zurücksetzen?\"\nUpdater.Title=\"Neues Update verfügbar\"\nUpdater.Text=\"Ein neues Update ist verfügbar:\"\nUpdater.UpdateNow=\"Jetzt aktualisieren\"\nUpdater.RemindMeLater=\"Später erinnern\"\nUpdater.Skip=\"Version überspringen\"\nUpdater.NoUpdatesAvailable.Title=\"Keine Updates verfügbar\"\nUpdater.NoUpdatesAvailable.Text=\"Zurzeit sind keine Updates verfügbar\"\nUpdater.BranchNotFound.Title=\"Update-Kanal entfernt\"\nUpdater.BranchNotFound.Text=\"Ihr ausgewählter Update-Kanal ist nicht länger verfügbar. OBS wurde zurückgesetzt.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Integritätsprüfung nicht verfügbar\"\nUpdater.RepairButUpdatesAvailable.Text=\"Das Überprüfen der Dateiintegrität ist nur in der aktuellsten Version möglich. Verwenden Sie „Hilfe“ → „Nach Updates suchen“, um Ihre OBS-Installation zu überprüfen und zu aktualisieren.\"\nUpdater.RepairConfirm.Title=\"Integritätsprüfung bestätigen\"\nUpdater.RepairConfirm.Text=\"Beim Starten der Integritätsprüfung wird Ihre OBS-Installation auf Fehler geprüft und fehlerhafte oder modifizierte Dateien werden erneut heruntergeladen, was einen Moment in Anspruch nehmen kann.\\n\\nFortfahren?\"\nUpdater.FailedToLaunch=\"Starten des Updaters fehlgeschlagen\"\nQuickTransitions.SwapScenes=\"Vorschau- und Programmszenen nach Übergang tauschen\"\nQuickTransitions.SwapScenesTT=\"Tauscht die Vorschau- und Programmszenen nach dem Übergang (falls die ursprüngliche Programmszene noch vorhanden ist).\\nEventuelle Änderungen an der originalen Programmszene werden nicht rückgängig gemacht.\"\nQuickTransitions.DuplicateScene=\"Szene duplizieren\"\nQuickTransitions.DuplicateSceneTT=\"Ermöglicht beim Bearbeiten von Quellen derselben Szene, die Transformationen\\nund Sichtbarkeit von Quellen anzupassen, ohne die Programmausgabe zu verändern.\\nAktivieren Sie „Szene duplizieren“, um die Eigenschaften von\\nQuellen zu bearbeiten, ohne die Programmausgabe zu verändern.\\nDas Ändern dieses Wertes wird die derzeitige\\nProgrammszene zurücksetzen (falls sie noch existiert).\"\nQuickTransitions.EditProperties=\"Quellen duplizieren\"\nQuickTransitions.EditPropertiesTT=\"Ermöglicht beim Bearbeiten von Quellen derselben Szene, die Eigenschaften\\nvon Quellen anzupassen, ohne die Programmausgabe zu verändern.\\nDies kann nur verwendet werden, wenn „Szene duplizieren“ aktiviert ist.\\nBestimmte Quellen (wie Aufnahme- oder Medienquellen) unterstützen\\ndies nicht und können nicht einzeln bearbeitet werden.\\nDas Ändern dieses Wertes wird die derzeitige\\nProgrammszene zurücksetzen (falls sie noch existiert).\\n\\nAchtung: Zusätzliche System- oder Videoressourcen können\\nbeim Duplizieren von Quellen in Anspruch genommen werden.\"\nQuickTransitions.HotkeyName=\"Schnellübergang: %1\"\nBasic.AddTransition=\"Konfigurierbaren Übergang hinzufügen\"\nBasic.RemoveTransition=\"Konfigurierbaren Übergang entfernen\"\nBasic.TransitionProperties=\"Übergangseigenschaften\"\nBasic.SceneTransitions=\"Szenenübergänge\"\nBasic.TransitionDuration=\"Dauer\"\nBasic.TogglePreviewProgramMode=\"Studio-Modus\"\nBasic.EnablePreviewProgramMode=\"Studio-Modus aktivieren\"\nBasic.DisablePreviewProgramMode=\"Studio-Modus deaktivieren\"\nUndo.Undo=\"Rückgängig machen\"\nUndo.Redo=\"Wiederholen\"\nUndo.Add=\"„%1“ hinzufügen\"\nUndo.Delete=\"„%1“ entfernen\"\nUndo.Rename=\"„%1“ umbenennen\"\nUndo.SceneCollection.Switch=\"Zu „%1“ wechseln\"\nUndo.Item.Undo=\"%1 rückgängig machen\"\nUndo.Item.Redo=\"%1 wiederholen\"\nUndo.Sources.Multi=\"%1 Quellen entfernen\"\nUndo.Filters=\"Filteränderungen bei „%1“\"\nUndo.Filters.Paste.Single=\"Filter „%1“ nach „%2“ einfügen\"\nUndo.Filters.Paste.Multiple=\"Filter von „%1“ nach „%2“ kopieren\"\nUndo.Transform=\"Quelle(n) in „%1“ transformieren\"\nUndo.Transform.Paste=\"Transformation in „%1“ einfügen\"\nUndo.Transform.Rotate=\"Rotation in „%1“\"\nUndo.Transform.Reset=\"Transformation in „%1“ zurücksetzen\"\nUndo.Transform.HFlip=\"Horizontal in „%1“ spiegeln\"\nUndo.Transform.VFlip=\"Vertikal in „%1“ spiegeln\"\nUndo.Transform.FitToScreen=\"An Bildschirm in „%1“ anpassen\"\nUndo.Transform.StretchToScreen=\"Auf Bildschirm in „%1“ strecken\"\nUndo.Transform.Center=\"Auf Bildschirm in „%1“ zentrieren\"\nUndo.Transform.VCenter=\"Vertikal auf Bildschirm in „%1“ zentrieren\"\nUndo.Transform.HCenter=\"Horizontal auf Bildschirm in „%1“ zentrieren\"\nUndo.Volume.Change=\"Lautstärkeänderung bei „%1“\"\nUndo.Volume.Mute=\"„%1“ stummschalten\"\nUndo.Volume.Unmute=\"Stummschaltung von „%1“ aufheben\"\nUndo.Balance.Change=\"Audio-Balance bei „%1“ ändern\"\nUndo.SyncOffset.Change=\"Audiosynchronisationsausgleich bei „%1“ ändern\"\nUndo.MonitoringType.Change=\"Audiomonitoring bei „%1“ ändern\"\nUndo.Mixers.Change=\"Audiomixer bei „%1“ ändern\"\nUndo.ForceMono.On=\"Mono bei „%1“ erzwingen aktivieren\"\nUndo.ForceMono.Off=\"Mono bei „%1“ erzwingen deaktivieren\"\nUndo.Properties=\"Objektänderung bei „%1“\"\nUndo.Scene.Duplicate=\"Szene „%1“ duplizieren\"\nUndo.ShowTransition=\"Zeige-Übergang bei „%1“\"\nUndo.HideTransition=\"Verstecke-Übergang bei „%1“\"\nUndo.ShowSceneItem=\"„%1“ in „%2“ anzeigen\"\nUndo.HideSceneItem=\"„%1“ in „%2“ verbergen\"\nUndo.ReorderSources=\"Quellen in „%1“ neu anordnen\"\nUndo.MoveUp=\"„%1“ in „%2“ hochbewegen\"\nUndo.MoveDown=\"„%1“ in „%2“ runterbewegen\"\nUndo.MoveToTop=\"„%1“ in „%2“ ganz hochbewegen\"\nUndo.MoveToBottom=\"„%1“ in „%2“ ganz runterbewegen\"\nUndo.PasteSource=\"Quelle(n) in „%1“ einfügen\"\nUndo.PasteSourceRef=\"Quellenverweis(e) in „%1“ einfügen\"\nUndo.GroupItems=\"Elemente in „%1“ gruppieren\"\nTransitionNameDlg.Text=\"Bitte geben Sie den Namen des Übergangs ein\"\nTransitionNameDlg.Title=\"Übergangsname\"\nTitleBar.SafeMode=\"ABGESICHERTER MODUS\"\nTitleBar.PortableMode=\"Portabler Modus\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Szenensammlung\"\nNameExists.Title=\"Name existiert bereits\"\nNameExists.Text=\"Dieser Name wird bereits verwendet.\"\nNoNameEntered.Title=\"Bitte geben Sie einen gültigen Namen ein\"\nNoNameEntered.Text=\"Sie können keine leeren Namen verwenden.\"\nConfirmStart.Title=\"Stream starten?\"\nConfirmStart.Text=\"Möchten Sie den Stream wirklich starten?\"\nConfirmStop.Title=\"Stream beenden?\"\nConfirmStop.Text=\"Möchten Sie den Stream wirklich beenden?\"\nConfirmStopRecord.Title=\"Aufnahme beenden?\"\nConfirmStopRecord.Text=\"Möchten Sie die Aufnahme wirklich beenden?\"\nConfirmBWTest.Title=\"Bandbreitentest starten?\"\nConfirmBWTest.Text=\"Sie haben OBS im Bandbreitentestmodus konfiguriert. In diesem Modus können Sie Netzwerktests durchführen, ohne dass Ihr Kanal live geschaltet wird. Sobald Sie fertig mit dem Testen sind, müssen Sie ihn deaktivieren, damit die Zuschauer Ihren Stream sehen können.\\n\\nFortfahren?\"\nConfirmExit.Title=\"Aktive Ausgänge\"\nConfirmExit.Text=\"OBS ist gerade noch aktiv. Alle Streams / Aufnahmen werden beendet.\"\nConfirmRemove.Title=\"Entfernen bestätigen\"\nConfirmRemove.Text=\"Möchten Sie „%1“ wirklich entfernen?\"\nConfirmRemove.TextMultiple=\"Möchten Sie %1 Elemente wirklich entfernen?\"\nConfirmReset.Title=\"Eigenschaften zurücksetzen\"\nConfirmReset.Text=\"Möchten Sie die aktuellen Eigenschaften wirklich zurücksetzen?\"\nOutput.StartStreamFailed=\"Starten des Streams fehlgeschlagen\"\nOutput.StartRecordingFailed=\"Starten der Aufnahme fehlgeschlagen\"\nOutput.StartReplayFailed=\"Starten des Replaypuffers fehlgeschlagen\"\nOutput.StartVirtualCamFailed=\"Starten der virtuellen Kamera fehlgeschlagen\"\nOutput.StartFailedGeneric=\"Starten der Ausgabe fehlgeschlagen. Bitte überprüfen Sie die Logdatei für Details.\\n\\nHinweis: Wenn Sie die NVENC- oder AMD-Kodierer verwenden, stellen Sie sicher, dass Ihre Grafiktreiber aktuell sind.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Beim Pausieren können keine Replays gespeichert werden.\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Achtung: Replays können beim Pausieren der Aufnahme nicht gespeichert werden.\"\nOutput.ConnectFail.Title=\"Verbindung fehlgeschlagen\"\nOutput.ConnectFail.BadPath=\"Der Pfad oder die Verbindungs-URL ist ungültig. Bitte überprüfen Sie Ihre Einstellungen und stellen Sie sicher, dass diese korrekt sind.\"\nOutput.ConnectFail.ConnectFailed=\"Verbindung zum Server fehlgeschlagen\"\nOutput.ConnectFail.InvalidStream=\"Auf den angegebenen Kanal oder Streamschlüssel konnte nicht zugegriffen werden. Bitte überprüfen Sie den Streamschlüssel. Falls dieser korrekt ist, könnte das Verbinden mit dem Server das Problem sein.\"\nOutput.ConnectFail.HdrDisabled=\"HDR Ausgabe ist für diesen Ausgang momentan deaktiviert.\"\nOutput.ConnectFail.Error=\"Ein unerwarteter Fehler ist beim Verbindungsversuch zum Server aufgetreten. Mehr Informationen finden Sie in der Logdatei.\"\nOutput.ConnectFail.Disconnected=\"Verbindung zum Server getrennt.\"\nOutput.StreamEncodeError.Title=\"Kodierungsfehler\"\nOutput.StreamEncodeError.Msg=\"Ein Kodiererfehler ist beim Streamen aufgetreten.\"\nOutput.StreamEncodeError.Msg.LastError=\"Beim Streamen ist ein Kodiererfehler aufgetreten:<br><br>%1\"\nOutput.RecordFail.Title=\"Starten der Aufnahme fehlgeschlagen\"\nOutput.RecordFail.Unsupported=\"Das Ausgabeformat wird entweder nicht unterstützt oder unterstützt nicht mehrere Audiospuren. Bitte überprüfen Sie Ihre Einstellungen und versuchen Sie es erneut.\"\nOutput.RecordNoSpace.Title=\"Nicht genügend Speicherplatz\"\nOutput.RecordNoSpace.Msg=\"Es gibt nicht genügend Speicherplatz, um die Aufnahme fortzusetzen.\"\nOutput.RecordError.Title=\"Aufnahmefehler\"\nOutput.RecordError.Msg=\"Während der Aufnahme ist ein unbekannter Fehler aufgetreten.\"\nOutput.RecordError.EncodeErrorMsg=\"Ein Kodiererfehler ist beim Aufnehmen aufgetreten.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Beim Aufnehmen ist ein Kodiererfehler aufgetreten:<br><br>%1\"\nOutput.BadPath.Title=\"Ungültiger Dateipfad\"\nOutput.BadPath.Text=\"Der konfigurierte Aufnahmepfad konnte nicht geöffnet werden. Bitte überprüfen Sie Ihren Aufnahmepfad unter „Einstellungen“ → „Ausgabe“ → „Aufnahme“.\"\nOutput.NoBroadcast.Title=\"Keine Übertragung konfiguriert\"\nOutput.NoBroadcast.Text=\"Sie müssen eine Übertragung einrichten, bevor Sie mit dem Streamen beginnen können.\"\nOutput.BroadcastStartFailed=\"Starten der Übertragung fehlgeschlagen\"\nOutput.BroadcastStopFailed=\"Beenden der Übertragung fehlgeschlagen\"\nLogUploadDialog.Title=\"OBS Studio Log-Datei hochladen\"\nLogUploadDialog.Labels.PrivacyNotice=\"Bitte lesen Sie die <a href='https://obsproject.com/privacy-policy'>Datenschutzbestimmungen</a> und den Abschnitt über das Hochladen von Dateien, bevor Sie eine Datei hochladen.\"\nLogUploadDialog.Labels.Progress=\"Der Log-Upload läuft. Bitte warten...\"\nLogUploadDialog.Labels.Description.AppLog=\"Ihre Protokolldatei wurde hochgeladen. Sie können nun die URL für Debugging- oder Support-Zwecke freigeben.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Ihr Absturzbericht wurde hochgeladen. Sie können nun die URL zu Debugging-Zwecken freigeben.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Hochladen\"\nLogUploadDialog.Buttons.CopyURL=\"Protokoll-URL kopieren\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Protokolldatei auswerten\"\nLogUploadDialog.Buttons.RetryButton=\"Wiederholen\"\nLogUploadDialog.Errors.Template=\"Beim Versuch, die Datei hochzuladen, ist ein Fehler aufgetreten:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Keine Datei zum Hochladen gefunden oder Datei war leer.\"\nRemux.SourceFile=\"OBS-Aufnahme\"\nRemux.TargetFile=\"Zieldatei\"\nRemux.Remux=\"Remuxen\"\nRemux.Stop=\"Remuxen stoppen\"\nRemux.ClearFinished=\"Fertiggestellte Objekte entfernen\"\nRemux.ClearAll=\"Alle Objekte entfernen\"\nRemux.OBSRecording=\"OBS-Aufnahme\"\nRemux.FinishedTitle=\"Remuxen abgeschlossen\"\nRemux.Finished=\"Aufnahme remuxed\"\nRemux.FinishedError=\"Aufnahme remuxed, aber die Datei könnte unvollständig sein\"\nRemux.SelectRecording=\"Wählen Sie eine OBS-Aufnahme aus\"\nRemux.SelectTarget=\"Wählen Sie eine Zieldatei aus\"\nRemux.FileExistsTitle=\"Zieldateien existieren bereits\"\nRemux.FileExists=\"Die folgenden Zieldateien existieren bereits. Möchten Sie diese ersetzen?\"\nRemux.ExitUnfinishedTitle=\"Remuxen in Arbeit\"\nRemux.ExitUnfinished=\"Das Remuxen ist noch nicht abgeschlossen. Wenn Sie jetzt beenden, kann die Zieldatei unbrauchbar werden.\\nMöchten Sie das Remuxen wirklich abbrechen?\"\nRemux.HelpText=\"Legen Sie Dateien in diesem Fenster ab, um sie zu remuxen oder wählen Sie eine leere „OBS-Aufnahme“-Zelle aus, um nach einer Datei zu suchen.\"\nRemux.NoFilesAddedTitle=\"Keine Remuxing-Datei hinzugefügt\"\nRemux.NoFilesAdded=\"Es wurde keine Datei zum Remuxen hinzugefügt. Legen Sie einen Ordner mit Videodateien ab.\"\nMissingFiles=\"Fehlende Dateien\"\nMissingFiles.MissingFile=\"Fehlende Datei\"\nMissingFiles.NewFile=\"Neue Datei\"\nMissingFiles.HelpText=\"Einige Dateien fehlen, seitdem Sie OBS das letzte Mal verwendet haben.\"\nMissingFiles.Clear=\"<geleert>\"\nMissingFiles.NumFound=\"%1 von %2 gefunden\"\nMissingFiles.Search=\"Ordner durchsuchen …\"\nMissingFiles.SelectFile=\"Datei auswählen …\"\nMissingFiles.SelectDir=\"Ordner zum Suchen auswählen\"\nMissingFiles.State=\"Status\"\nMissingFiles.Missing=\"Fehlt\"\nMissingFiles.Replaced=\"Ersetzt\"\nMissingFiles.Cleared=\"Geleert\"\nMissingFiles.Found=\"Gefunden\"\nMissingFiles.AutoSearch=\"Weitere Dateitreffer gefunden\"\nMissingFiles.AutoSearchText=\"OBS hat weitere Treffer für fehlende Dateien in diesem Ordner gefunden. Möchten Sie diese hinzufügen?\"\nMissingFiles.NoMissing.Title=\"Auf fehlende Dateien prüfen\"\nMissingFiles.NoMissing.Text=\"Es scheinen keine Dateien zu fehlen.\"\nMacPermissions.MenuAction=\"App-Berechtigungen überprüfen …\"\nMacPermissions.Title=\"App-Berechtigungen überprüfen\"\nMacPermissions.Description=\"OBS Studio benötigt für bestimmte Funktionen zusätzliche Berechtigungen, die zwar empfohlen, aber nicht für die Benutzung des Programms erforderlich sind. Sie können diese jederzeit später aktivieren.\"\nMacPermissions.Description.OpenDialog=\"Dieses Fenster kann über die Menüleiste in OBS erneut geöffnet werden.\"\nMacPermissions.AccessGranted=\"Zugriff erlaubt\"\nMacPermissions.RequestAccess=\"Zugriff anfordern\"\nMacPermissions.OpenPreferences=\"Einstellungen öffnen\"\nMacPermissions.Item.ScreenRecording=\"Bildschirmaufnahmen\"\nMacPermissions.Item.ScreenRecording.Details=\"Diese Berechtigung wird für das Aufnehmen des Bildschirms benötigt.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Diese Berechtigung wird benötigt, um Inhalte einer Webcam oder Capture Card aufzunehmen.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"Diese Berechtigung wird von OBS benötigt, um die Aufnahme mit einem Mikrofon oder einem externen Audiogerät zu ermöglichen.\"\nMacPermissions.Item.InputMonitoring=\"Eingabeüberwachung\"\nMacPermissions.Item.InputMonitoring.Details=\"Diese Berechtigung ist erforderlich, damit Hotkeys funktionieren, während OBS im Hintergrund läuft.\"\nMacPermissions.Item.Accessibility=\"Bedienungshilfen\"\nMacPermissions.Item.Accessibility.Details=\"Bei älteren Installationen wird OBS möglicherweise unter „Bedienungshilfen“ statt unter „Eingabeüberwachung“ aufgeführt.\"\nMacPermissions.Continue=\"Fortfahren\"\nSourceLeak.Title=\"Quellenbereinigungsfehler\"\nSourceLeak.Text=\"Es gab ein Problem beim Wechseln der Szenensammlung und einige Quellen konnten nicht entladen werden. Dieses Problem wird in der Regel durch Plugins verursacht, die Ressourcen nicht richtig freigeben. Bitte stellen Sie sicher, dass Ihre Plugins auf dem neuesten Stand sind.\\n\\nOBS Studio wird nun beendet, um mögliche Datenbeschädigung zu vermeiden.\"\nBasic.DesktopDevice1=\"Desktop-Audio\"\nBasic.DesktopDevice2=\"Desktop-Audio 2\"\nBasic.AuxDevice1=\"Mikrofon-/AUX-Audio\"\nBasic.AuxDevice2=\"Mikrofon-/AUX-Audio 2\"\nBasic.AuxDevice3=\"Mikrofon-/AUX-Audio 3\"\nBasic.AuxDevice4=\"Mikrofon-/AUX-Audio 4\"\nBasic.Scene=\"Szene\"\nBasic.DisplayCapture=\"Monitoraufnahme\"\nBasic.Main.PreviewConextMenu.Enable=\"Vorschau aktivieren\"\nBasic.Main.Preview.Disable=\"Vorschau deaktivieren\"\nScaleFiltering=\"Skalierungsfilterung\"\nBlendingMethod=\"Überblendmethode\"\nBlendingMethod.Default=\"Standard\"\nBlendingMethod.SrgbOff=\"sRGB aus\"\nBlendingMode=\"Überblendmodi\"\nBlendingMode.Additive=\"Addieren\"\nBlendingMode.Subtract=\"Subtrahieren\"\nBlendingMode.Screen=\"Negativ multiplizieren\"\nBlendingMode.Multiply=\"Multiplizieren\"\nBlendingMode.Lighten=\"Aufhellen\"\nBlendingMode.Darken=\"Abdunkeln\"\nDeinterlacing.Discard=\"Verwerfen\"\nDeinterlacing.TopFieldFirst=\"Oberes Feld zuerst\"\nDeinterlacing.BottomFieldFirst=\"Unteres Feld zuerst\"\nVolControl.SliderUnmuted=\"Lautstärkeregler für „%1“:\"\nVolControl.SliderMuted=\"Lautstärkeregler für „%1“: (derzeit stummgeschaltet)\"\nVolControl.Mute=\"„%1“ stummschalten\"\nVolControl.Properties=\"Eigenschaften von „%1“\"\nVolControl.UnassignedWarning.Title=\"Audioquelle nicht zugewiesen\"\nVolControl.UnassignedWarning.Text=\"„%1“ ist keiner Audiospur zugewiesen und wird nicht in Streams oder Aufnahmen zu hören sein.\\n\\nUm eine Audioquelle eine Spur zuzuordnen, öffnen Sie die Erweiterten Audioeigenschaften, welche Sie durch einen Rechtsklick oder durch das Zahnrad im Mixer-Dock aufrufen können.\"\nBasic.Main.AddSceneDlg.Title=\"Szene hinzufügen\"\nBasic.Main.AddSceneDlg.Text=\"Bitte geben Sie den Namen der Szene ein\"\nBasic.Main.DefaultSceneName.Text=\"Szene %1\"\nBasic.Main.AddSceneCollection.Title=\"Szenensammlung erstellen\"\nBasic.Main.AddSceneCollection.Text=\"Bitte geben Sie den Namen der Szenensammlung ein\"\nBasic.Main.RenameSceneCollection.Title=\"Szenensammlung umbenennen\"\nBasic.Main.RemigrateSceneCollection.Title=\"Auflösung der Szenensammlung aktualisieren\"\nBasic.Main.RemigrateSceneCollection.Text=\"Möchten Sie die Auflösung der Szenensammlung „%1“ an die aktuelle Leinwandauflösung (%2 × %3) anpassen?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Auflösung der Szenensammlung kann nur aktualisiert werden, wenn keine Ausgaben aktiv sind.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Aktualisieren der Auflösung der Szenensammlung ist fehlgeschlagen. Die ursprüngliche Auflösung ist unbekannt.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Zurücksetzen nicht möglich: Ändern der OBS-Auflösung ist fehlgeschlagen.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Zurücksetzen nicht möglich: Aktuelle Auflösung ist bereits Basisauflösung der Szenensammlung.\"\nAddProfile.Title=\"Profil erstellen\"\nAddProfile.Text=\"Bitte geben Sie den Namen des Profils ein\"\nAddProfile.WizardCheckbox=\"Einrichtungsassistenten anzeigen\"\nRenameProfile.Title=\"Profil umbenennen\"\nBasic.Main.MixerRename.Title=\"Audioquelle umbenennen\"\nBasic.Main.MixerRename.Text=\"Bitte geben Sie den Namen der Audioquelle ein\"\nBasic.Main.PreviewDisabled=\"Vorschau ist deaktiviert\"\nBasic.SourceSelect=\"Quelle erstellen/auswählen\"\nBasic.SourceSelect.CreateNew=\"Neu erstellen\"\nBasic.SourceSelect.AddExisting=\"Existierende hinzufügen\"\nBasic.SourceSelect.AddVisible=\"Quelle sichtbar\"\nBasic.Main.Sources.Visibility=\"Sichtbarkeit\"\nBasic.Main.Sources.VisibilityDescription=\"Steuert die Sichtbarkeit von „%1“ in der Leinwand\"\nBasic.Main.Sources.Lock=\"Fixieren\"\nBasic.Main.Sources.LockDescription=\"Fixiert die Position und Größe von „%1“ in der Leinwand\"\nBasic.PropertiesWindow=\"Eigenschaften von „%1“\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (automatisch ausgewählt: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Farbe auswählen\"\nBasic.PropertiesWindow.SelectFont=\"Schriftart auswählen\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Schriftart wählen\"\nBasic.PropertiesWindow.ConfirmTitle=\"Einstellungen geändert\"\nBasic.PropertiesWindow.Confirm=\"Möchten Sie Ihre Änderungen speichern?\"\nBasic.PropertiesWindow.NoProperties=\"Keine Eigenschaften verfügbar\"\nBasic.PropertiesWindow.AddFiles=\"Dateien hinzufügen\"\nBasic.PropertiesWindow.AddDir=\"Ordner hinzufügen\"\nBasic.PropertiesWindow.AddURL=\"Pfad/URL hinzufügen\"\nBasic.PropertiesWindow.AddEditableListDir=\"Ordner zu „%1“ hinzufügen\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Dateien zu „%1“ hinzufügen\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Eintrag zu „%1“ hinzufügen\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Eintrag aus „%1“ bearbeiten\"\nBasic.PropertiesView.FPS.Simple=\"Typische FPS-Werte\"\nBasic.PropertiesView.FPS.Rational=\"Rationale FPS-Werte\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Gültige FPS-Bereiche:\"\nBasic.PropertiesView.UrlButton.Text=\"Diesen Link in Ihrem Standard-Browser öffnen?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"URL öffnen\"\nBasic.InteractionWindow=\"Interaktion mit „%1“\"\nBasic.StatusBar.Reconnecting=\"Verbindung getrennt. Verbindungsversuch %1 in %2 Sekunde(n)\"\nBasic.StatusBar.AttemptingReconnect=\"Versuch zur Wiederherstellung der Verbindung läuft … (Versuch %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Wiederverbindung erfolgreich\"\nBasic.StatusBar.Delay=\"Verzögerung (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Verzögerung (Beginn in %1 s)\"\nBasic.StatusBar.DelayStoppingIn=\"Verzögerung (Ende in %1 s)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Verzögerung (Ende in %1 s, Beginn in %2 s)\"\nBasic.StatusBar.RecordingSavedTo=\"Aufnahme nach „%1“ gespeichert\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Replaypuffer nach „%1“ gespeichert\"\nBasic.StatusBar.ScreenshotSavedTo=\"Screenshot nach „%1“ gespeichert\"\nBasic.StatusBar.AutoRemuxedTo=\"Aufnahme automatisch nach „%1“ remuxed\"\nBasic.Filters=\"Filter\"\nBasic.Filters.AsyncFilters=\"Audio-/Videofilter\"\nBasic.Filters.AudioFilters=\"Audiofilter\"\nBasic.Filters.EffectFilters=\"Effektfilter\"\nBasic.Filters.Title=\"Filter für „%1“\"\nBasic.Filters.AddFilter.Title=\"Filtername\"\nBasic.Filters.AddFilter.Text=\"Bitte geben Sie einen Namen für den Filter ein\"\nBasic.TransformWindow=\"Transformation von Szenenelement\"\nBasic.TransformWindow.PositionX=\"X-Position\"\nBasic.TransformWindow.PositionY=\"Y-Position\"\nBasic.TransformWindow.Size=\"Größe\"\nBasic.TransformWindow.Width=\"Breite\"\nBasic.TransformWindow.Height=\"Höhe\"\nBasic.TransformWindow.Alignment=\"Ausrichtung\"\nBasic.TransformWindow.BoundsType=\"Begrenzungsrahmenart\"\nBasic.TransformWindow.BoundsAlignment=\"Ausrichtung im Begrenzungsrahmen\"\nBasic.TransformWindow.Bounds=\"Begrenzungsrahmengröße\"\nBasic.TransformWindow.BoundsWidth=\"Begrenzungsrahmenbreite\"\nBasic.TransformWindow.BoundsHeight=\"Begrenzungsrahmenhöhe\"\nBasic.TransformWindow.CropToBounds=\"Auf Begrenzungsrahmen zuschneiden\"\nBasic.TransformWindow.Crop=\"Zuschneiden\"\nBasic.TransformWindow.CropLeft=\"Links abschneiden\"\nBasic.TransformWindow.CropRight=\"Rechts abschneiden\"\nBasic.TransformWindow.CropTop=\"Oben abschneiden\"\nBasic.TransformWindow.CropBottom=\"Unten abschneiden\"\nBasic.TransformWindow.Alignment.TopLeft=\"Oben links\"\nBasic.TransformWindow.Alignment.TopCenter=\"Oben in der Mitte\"\nBasic.TransformWindow.Alignment.TopRight=\"Oben rechts\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Mitte links\"\nBasic.TransformWindow.Alignment.Center=\"Mitte\"\nBasic.TransformWindow.Alignment.CenterRight=\"Mitte rechts\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Unten links\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Unten in der Mitte\"\nBasic.TransformWindow.Alignment.BottomRight=\"Unten rechts\"\nBasic.TransformWindow.BoundsType.None=\"Keine Begrenzungen\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Nur maximale Größe\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Auf innere Begrenzungen skalieren\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Auf äußere Begrenzungen skalieren\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Auf Breite der Begrenzungen skalieren\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Auf Höhe der Begrenzungen skalieren\"\nBasic.TransformWindow.BoundsType.Stretch=\"Bis zu Begrenzungen strecken\"\nBasic.TransformWindow.Title=\"Transformation für „%1“ bearbeiten\"\nBasic.TransformWindow.NoSelectedSource=\"Keine Quelle ausgewählt\"\nBasic.Main.AddSourceHelp.Title=\"Quelle konnte nicht hinzugefügt werden\"\nBasic.Main.AddSourceHelp.Text=\"Es muss mindestens eine Szene existieren, um eine Quelle hinzufügen zu können.\"\nBasic.Main.Scenes=\"Szenen\"\nBasic.Main.Sources=\"Quellen\"\nBasic.Main.Source=\"Quelle\"\nBasic.Main.Controls=\"Steuerung\"\nBasic.Main.PreparingStream=\"Wird vorbereitet …\"\nBasic.Main.Connecting=\"Verbindung wird hergestellt …\"\nBasic.Main.StartRecording=\"Aufnahme starten\"\nBasic.Main.StartReplayBuffer=\"Replaypuffer starten\"\nBasic.Main.SaveReplay=\"Replay speichern\"\nBasic.Main.StartStreaming=\"Stream starten\"\nBasic.Main.StartBroadcast=\"Live gehen\"\nBasic.Main.StartVirtualCam=\"Virtuelle Kamera starten\"\nBasic.Main.StopRecording=\"Aufnahme beenden\"\nBasic.Main.PauseRecording=\"Aufnahme pausieren\"\nBasic.Main.UnpauseRecording=\"Aufnahme fortsetzen\"\nBasic.Main.SplitFile=\"Aufnahmedatei teilen\"\nBasic.Main.AddChapterMarker=\"Kapitelmarkierung hinzufügen (nur Hybrid MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Aufnahme wird beendet …\"\nBasic.Main.StopReplayBuffer=\"Replaypuffer stoppen\"\nBasic.Main.StoppingReplayBuffer=\"Replaypuffer wird beendet …\"\nBasic.Main.SetupBroadcast=\"Übertragung verwalten\"\nBasic.Main.StopStreaming=\"Stream beenden\"\nBasic.Main.StopBroadcast=\"Übertragung beenden\"\nBasic.Main.AutoStopEnabled=\"(automatisch beenden)\"\nBasic.Main.StoppingStreaming=\"Stream wird beendet …\"\nBasic.Main.ForceStopStreaming=\"Stream beenden (Verzögerung ignorieren)\"\nBasic.Main.ShowContextBar=\"Quellenwerkzeugleiste anzeigen\"\nBasic.Main.HideContextBar=\"Quellenwerkzeugleiste verbergen\"\nBasic.Main.StopVirtualCam=\"Virtuelle Kamera beenden\"\nBasic.Main.Group=\"Gruppe %1\"\nBasic.Main.GroupItems=\"Ausgewählte Elemente gruppieren\"\nBasic.Main.Ungroup=\"Gruppierung aufheben\"\nBasic.Main.GridMode=\"Rastermodus\"\nBasic.Main.ListMode=\"Listenmodus\"\nBasic.Main.VirtualCamConfig=\"Virtuelle Kamera konfigurieren\"\nBasic.VCam.VirtualCamera=\"Virtuelle Kamera\"\nBasic.VCam.OutputType=\"Ausgabetyp\"\nBasic.VCam.OutputSelection=\"Ausgabeauswahl\"\nBasic.VCam.OutputType.Program=\"Programm (Standard)\"\nBasic.VCam.OutputSelection.NoSelection=\"Keine Auswahl für diesen Ausgabetyp\"\nBasic.VCam.RestartWarning=\"Die virtuelle Kamera wird neugestartet, um die Änderung anzuwenden.\"\nBasic.MainMenu.File=\"&Datei\"\nBasic.MainMenu.File.Export=\"&Exportieren\"\nBasic.MainMenu.File.Import=\"&Importieren\"\nBasic.MainMenu.File.ShowRecordings=\"&Aufnahmen anzeigen\"\nBasic.MainMenu.File.Remux=\"A&ufnahmen remuxen\"\nBasic.MainMenu.File.Settings=\"&Einstellungen\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Einstellungsordner öffnen\"\nBasic.MainMenu.File.ShowProfileFolder=\"Profilordner öffnen\"\nBasic.MainMenu.File.ShowMissingFiles=\"Auf fehlende Dateien prüfen\"\nBasic.MainMenu.File.Exit=\"&Beenden\"\nBasic.MainMenu.Edit=\"&Bearbeiten\"\nBasic.MainMenu.Edit.Undo=\"&Rückgängig machen\"\nBasic.MainMenu.Edit.Redo=\"&Wiederherstellen\"\nBasic.MainMenu.Edit.LockPreview=\"V&orschau fixieren\"\nBasic.MainMenu.Edit.Scale=\"&Vorschauskalierung\"\nBasic.MainMenu.Edit.Scale.Window=\"An Fenstergröße anpassen\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Leinwand (%1 × %2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Ausgabe (%1 × %2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Skaliert (%1 × %2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Hineinzoomen\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Herauszoomen\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Zoom zurücksetzen\"\nBasic.MainMenu.Edit.Transform=\"&Transformieren\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Transformation bearbeiten …\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Transformation kopieren\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Transformation einfügen\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"T&ransformation zurücksetzen\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"90° im Uhrzeigersinn drehen\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"90° gegen den Uhrzeigersinn drehen\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180° drehen\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"&Horizontal spiegeln\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"&Vertikal spiegeln\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"&An Bildschirm anpassen\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"A&uf Bildschirm strecken\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Au&f Bildschirm zentrieren\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Vertikal zentrieren\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Horizontal zentrieren\"\nBasic.MainMenu.Edit.Order=\"&Anordnen\"\nBasic.MainMenu.Edit.Order.MoveUp=\"&Hoch\"\nBasic.MainMenu.Edit.Order.MoveDown=\"&Runter\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"&Ganz hoch\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"G&anz runter\"\nBasic.MainMenu.Edit.AdvAudio=\"&Erweiterte Audioeigenschaften\"\nBasic.MainMenu.View=\"&Ansicht\"\nBasic.MainMenu.View.Toolbars=\"Werkzeugleis&ten\"\nBasic.MainMenu.View.ListboxToolbars=\"Dock-Werkzeugleisten\"\nBasic.MainMenu.View.ContextBar=\"Quellenwerkzeugleiste\"\nBasic.MainMenu.View.SourceIcons=\"&Quellsymbole\"\nBasic.MainMenu.View.StatusBar=\"&Statusleiste\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Vollbild\"\nBasic.MainMenu.View.ResetUI=\"&Benutzeroberfläche zurücksetzen\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Immer im Vordergrund\"\nBasic.MainMenu.View.SceneListMode=\"Szenenlistenmodus\"\nBasic.MainMenu.Docks.ResetDocks=\"D&ocks zurücksetzen\"\nBasic.MainMenu.Docks.LockDocks=\"&Docks sperren\"\nBasic.MainMenu.Docks.SideDocks=\"Docks mit voller Höhe (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Benutzerdefinierte Browser-Docks …\"\nBasic.MainMenu.Import=\"Importieren...\"\nBasic.MainMenu.Export=\"Exportieren...\"\nBasic.MainMenu.SceneCollection=\"&Szenensammlung\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Profil importieren\"\nBasic.MainMenu.Profile.Export=\"Profil exportieren\"\nBasic.MainMenu.SceneCollection.Import=\"Szenensammlung importieren\"\nBasic.MainMenu.SceneCollection.Export=\"Szenensammlung exportieren\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Basisauflösung zurücksetzen\"\nBasic.MainMenu.SceneCollection.Migrate=\"Basisauflösung anwenden\"\nBasic.MainMenu.Profile.Exists=\"Das Profil ist bereits vorhanden\"\nBasic.MainMenu.Tools=\"&Werkzeuge\"\nBasic.MainMenu.Help=\"&Hilfe\"\nBasic.MainMenu.Help.HelpPortal=\"&Hilfeportal\"\nBasic.MainMenu.Help.Website=\"&Webseite besuchen\"\nBasic.MainMenu.Help.Discord=\"&Discord-Server beitreten\"\nBasic.MainMenu.Help.WhatsNew=\"Neuigkeiten\"\nBasic.MainMenu.Help.Logs=\"&Logdateien\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"&Logdateien anzeigen\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"&Aktuelle Logdatei hochladen\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"&Vorherige Logdatei hochladen\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"A&ktuelles Log ansehen\"\nBasic.MainMenu.Help.ReleaseNotes=\"Versionshinweise\"\nBasic.MainMenu.Help.CheckForUpdates=\"Nach Updates suchen\"\nBasic.MainMenu.Help.Repair=\"Dateiintegrität überprüfen\"\nBasic.MainMenu.Help.RestartSafeMode=\"Im abgesicherten Modus neu starten\"\nBasic.MainMenu.Help.RestartNormal=\"Normal neu starten\"\nBasic.MainMenu.Help.CrashLogs=\"&Absturzberichte\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"&Absturzberichte anzeigen\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"&Vorherigen Absturzbericht hochladen\"\nBasic.MainMenu.Help.About=\"Ü&ber\"\nBasic.Settings.ProgramRestart=\"Das Programm muss neu gestartet werden, damit die Änderungen wirksam werden.\"\nBasic.Settings.ConfirmTitle=\"Änderungen bestätigen\"\nBasic.Settings.Confirm=\"Sie haben ungespeicherte Änderungen. Änderungen speichern?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 steuert einige Ihrer Streameinstellungen\"\nBasic.Settings.General=\"Allgemein\"\nBasic.Settings.General.Language=\"Sprache\"\nBasic.Settings.General.UpdateChannel=\"Update-Kanal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Deaktiviert)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Standard)\"\nBasic.Settings.General.EnableAutoUpdates=\"Beim Start nach Updates suchen\"\nBasic.Settings.General.OpenStatsOnStartup=\"Statistikenfenster beim Start öffnen\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"OBS-Fenster vor Bildschirmaufnahmen verbergen\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Diese Einstellung verhindert das Aufnehmen von OBS Studio-Fenstern von OBS selbst und anderen Programmen\\nfür z. B. Videokonferenzen, Bildschirmübertragungen, Fernunterstützung oder Screenshots.\\nOBS-Projektoren werden weiterhin aufgenommen.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Das Aktivieren dieser Option verhindert das Aufnehmen von OBS Studio-Fenstern von OBS selbst und anderen Programmen für z. B. Videokonferenzen, Bildschirmübertragungen, Fernunterstützung oder Screenshots. OBS-Projektoren werden weiterhin aufgenommen.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Bestätigungsdialog beim Starten des Streams anzeigen\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Bestätigungsdialog beim Beenden des Streams anzeigen\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Bestätigungsdialog beim Beenden der Aufnahme anzeigen\"\nBasic.Settings.General.Projectors=\"Projektoren\"\nBasic.Settings.General.HideProjectorCursor=\"Mauszeiger über Projektoren verbergen\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Projektoren immer im Vordergrund anzeigen\"\nBasic.Settings.General.Snapping=\"Quellenausrichtung\"\nBasic.Settings.General.ScreenSnapping=\"Quellen am Bildschirmrand ausrichten\"\nBasic.Settings.General.CenterSnapping=\"Quellen horizontal und vertikal zentrieren\"\nBasic.Settings.General.SourceSnapping=\"Quellen an anderen Quellen ausrichten\"\nBasic.Settings.General.SnapDistance=\"Ausrichtungsempfindlichkeit\"\nBasic.Settings.General.SpacingHelpers=\"Pixelangaben beim Ausrichten anzeigen\"\nBasic.Settings.General.RecordWhenStreaming=\"Automatisch aufnehmen beim Streamen\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Weiter aufnehmen, wenn der Stream beendet wird\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Replaypuffer mit Stream starten\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Replaypuffer laufen lassen, wenn der Stream beendet wird\"\nBasic.Settings.General.SysTray=\"Infobereich\"\nBasic.Settings.General.SysTrayWhenStarted=\"Beim Start zum Infobereich minimieren\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Immer zum Infobereich anstatt zur Taskleiste minimieren\"\nBasic.Settings.General.SaveProjectors=\"Projektoren beim Beenden speichern\"\nBasic.Settings.General.CloseExistingProjectors=\"Auf einen Vollbildprojektor pro Bildschirm begrenzen\"\nBasic.Settings.General.Preview=\"Vorschau\"\nBasic.Settings.General.OverflowHidden=\"Überlauf verbergen\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Überlauf immer anzeigen\"\nBasic.Settings.General.OverflowSelectionHidden=\"Überlauf trotz verborgener Quelle anzeigen\"\nBasic.Settings.General.Importers=\"Importeure\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Beim Importieren bekannte Orte nach Szenensammlungen durchsuchen\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Szenenübergang bei Doppelklick\"\nBasic.Settings.General.StudioPortraitLayout=\"Porträt-/Vertikales Layout aktivieren\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Vorschau- und Programmbeschriftung anzeigen\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Szenen durch Klicken wechseln\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Szenenamen anzeigen\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Sichere Bereiche anzeigen (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Multiview-Anordnung\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontal, oben (8 Szenen)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontal, unten (8 Szenen)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertikal, links (8 Szenen)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertikal, rechts (8 Szenen)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontal, oben (18 Szenen)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontal, oben (24 Szenen)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Nur Szenen (4 Szenen)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Nur Szenen (9 Szenen)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Nur Szenen (16 Szenen)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Nur Szenen (25 Szenen)\"\nBasic.Settings.General.ChannelDescription.stable=\"Neuster stabiler Release\"\nBasic.Settings.General.ChannelDescription.beta=\"Potenziell instabile Pre-Release-Versionen\"\nBasic.Settings.Appearance=\"Erscheinungsbild\"\nBasic.Settings.Appearance.General=\"Allgemein\"\nBasic.Settings.Appearance.General.Theme=\"Design\"\nBasic.Settings.Appearance.General.Variant=\"Stil\"\nBasic.Settings.Appearance.General.NoVariant=\"Keine Stile verfügbar\"\nBasic.Settings.Appearance.FontScale=\"Schriftgröße\"\nBasic.Settings.Appearance.Density=\"Dichte\"\nBasic.Settings.Appearance.Classic=\"Klassisch\"\nBasic.Settings.Appearance.Compact=\"Kompakt\"\nBasic.Settings.Appearance.Comfortable=\"Komfortabel\"\nBasic.Settings.Appearance.OptionsWarning=\"Einige Erscheinungaoptionen sind bei diesem Stil nicht verfügbar.\"\nBasic.Settings.Stream.Destination=\"Ziel\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Authentifizierung verwenden\"\nBasic.Settings.Stream.Custom.Username=\"Benutzername\"\nBasic.Settings.Stream.Custom.Password=\"Passwort\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: Geben Sie den srp_usernamen ein.\\nRTMP: Geben Sie den Benutzernamen ein.\\nSRT: Wird nicht verwendet.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: Geben Sie das srp_password ein.\\nRTMP: Geben Sie das Passwort ein.\\nSRT: Geben Sie das Verschlüsselungspasswort ein.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Bandbreitentestmodus aktivieren\"\nBasic.Settings.Stream.TTVAddon=\"Twitch-Chat-Add-Ons\"\nBasic.Settings.Stream.TTVAddon.None=\"Keins\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV und FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Unvollständige Streameinrichtung\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Einstellungen öffnen\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL und Streamschlüssel fehlen.\\n\\nÖffnen Sie die Einstellungen, um die URL und den Streamschlüssel im Stream-Tab einzugeben.\"\nBasic.Settings.Stream.MissingUrl=\"Stream-URL fehlt.\\n\\nÖffnen Sie die Einstellungen, um die URL im Stream-Tab einzugeben.\"\nBasic.Settings.Stream.MissingStreamKey=\"Streamschlüssel fehlt.\\n\\nÖffnen Sie die Einstellungen, um den Streamschlüssel im Stream-Tab einzugeben.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Empfehlungen für Einstellungen der Streamingplattform ignorieren\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Empfohlene Einstellungen überschreiben\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Achtung: Das Ignorieren von Limitierungen einer Plattform kann zu einer verschlechterten Streamqualität führen oder Sie komplett am Streamen hindern.\\n\\nFortfahren?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maximale Videobitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maximale Audiobitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maximale Auflösung: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maximale FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Benutzerdefinierter Server …\"\nBasic.Settings.Stream.ServiceCustomServer=\"Benutzerdefinierter Server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1 aktivieren\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maximale Streamingbandbreite\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automatisch\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maximale Videospuren\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automatisch\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Stream-Dump im FLV-Format aktivieren (Verwendet die simplen Aufnahmedateieinstellungen)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Konfiguration überschreiben (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Konfiguration überschreiben\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Mehrspur-Video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Zusätzliche Leinwand\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Mit Simulcast können Sie mehrere Videoqualitäten codieren und senden. <a href=‚https://obsproject.com/kb/whip-streaming-guide‘>Weitere Informationen</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Gesamtanzahl Ebenen\"\nBasic.Settings.Stream.AdvancedOptions=\"Erweiterte Optionen\"\nBasic.Settings.Output=\"Ausgabe\"\nBasic.Settings.Output.Format=\"Aufnahmeformat\"\nBasic.Settings.Output.Format.MKV=\"Matroska-Video (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"Hybrides MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hybrides MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmentiertes MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmentiertes MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Bei fragmentiertem MOV wird die Aufnahme in Abschnitten geschrieben und es wird nicht dieselbe Fertigstellung wie bei normalen MOV-Dateien benötigt.\\nSo bleiben Dateien auch bei Abbrüchen des Schreibvorgangs abspielbar, z. B. durch einen BSOD oder Stromausfall.\\n\\nDies ist möglicherweise nicht mit allen Medienspielern und Editoren kompatibel. Verwenden Sie „Datei“ → „Aufnahmen remuxen“, um Dateien gegebenenfalls in ein kompatibles Format zu konvertieren.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Bei fragmentiertem MP4 wird die Aufnahme in Abschnitten geschrieben und es wird nicht dieselbe Fertigstellung wie bei normalen MP4-Dateien benötigt.\\nSo bleiben Dateien auch bei Abbrüchen des Schreibvorgangs abspielbar, z. B. durch einen BSOD oder Stromausfall.\\n\\nDies ist möglicherweise nicht mit allen Medienspielern und Editoren kompatibel. Verwenden Sie „Datei“ → „Aufnahmen remuxen“, um Dateien gegebenenfalls in ein kompatibles Format zu konvertieren.\"\nBasic.Settings.Output.Encoder.Video=\"Videokodierer\"\nBasic.Settings.Output.Encoder.Audio=\"Audiokodierer\"\nBasic.Settings.Output.SelectDirectory=\"Wählen Sie den Aufnahmeordner aus\"\nBasic.Settings.Output.DynamicBitrate=\"Bitrate dynamisch verändern, um Überlastung zu kontrollieren\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Bitrate dynamisch verändern, um Überlastung zu kontrollieren (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Anstatt Frames wegfallen zu lassen, um Überlastung\\nzu verringern, wird die Bitrate dynamisch verändert.\\n\\nBeachten Sie, dass dies die Verzögerung zu den Zuschauern\\nbei erheblich unerwartet hoher Überlastung erhöhen kann.\\nWenn die Bitrate fällt, kann der Wiederherstellungsvorgang\\nein paar Minuten dauern.\\n\\nDerzeit nur für RTMP unterstützt.\"\nBasic.Settings.Output.Mode=\"Ausgabemodus\"\nBasic.Settings.Output.Mode.Simple=\"Einfach\"\nBasic.Settings.Output.Mode.Adv=\"Erweitert\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg-Ausgabe\"\nBasic.Settings.Output.UseReplayBuffer=\"Replaypuffer aktivieren\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maximale Replayzeit\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maximaler RAM-Verbrauch\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Geschätzter RAM-Verbrauch: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Achtung: Der geschätzte RAM-Verbrauch von %1 MiB ist höher als die empfohlenen %2 MiB.\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"RAM-Verbrauch kann nicht geschätzt werden. Setzen Sie den maximalen RAM-Verbrauch.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Replaypuffer-Dateinamen-Präfix\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Der Replaypuffer kann nicht verwendet werden, wenn der Aufnahmetyp auf „Benutzerdefinierte Ausgabe (FFmpeg)“ gesetzt ist.\"\nBasic.Settings.Output.Simple.SavePath=\"Aufnahmepfad\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Aufnahmequalität\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Gleich wie Stream\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Hohe Qualität, mittelgroße Dateien\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Ununterscheidbare Qualität, große Dateien\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Verlustfreie Qualität, enorm große Dateien\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Achtung: Die Streamvideobitrate wird auf %1 gesetzt, was der oberen Grenze der aktuellen Streamingplattform entspricht.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Achtung: Die Streamaudiobitrate wird auf %1 gesetzt, was der oberen Grenze der aktuellen Streamingplattform entspricht.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Achtung: Aufnahmen können nicht pausiert werden, wenn die Aufnahmequalität „Gleich wie Stream“ ist.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Achtung: Das Aufnahmeformat ist mit dem/den gewählten Streamkodierer(n) nicht kompatibel.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Achtung: Die Aufnahme mit einem Softwarekodierer in einer anderen Qualität als der des Streams verursacht zusätzliche CPU-Nutzung, wenn gleichzeitig gestreamt und aufgenommen wird.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Achtung: Bei verlustfreier Qualität werden enorm große Dateien erzeugt, was bei hohen Auflösungen und Frameraten mehr als 7 GB Speicherplatz pro Minute beanspruchen kann. Der Replaypuffer steht bei verlustfreier Qualität nicht zur Verfügung.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Möchten Sie verlustfreie Qualität wirklich verwenden?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Warnung zur verlustfreien Qualität\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264-Voreinstellung für geringe CPU-Auslastung, erhöht die Dateigröße)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Standard)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch-VOD-Spur (Verwendet Spur 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Audiospur\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Inkompatible Auflösung/Framerate\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Diese Streamingplattform unterstützt Ihre aktuelle Ausgabeauflösung und/oder Framerate nicht. Die Werte werden auf den nächstmöglichen kompatiblen Wert geändert:\\n\\n%1\\n\\nFortfahren?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Auflösung: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Inkompatibler Kodierer\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Ihr Kodierer wird zu „%3“ geändert, da „%1“ „%2“ nicht unterstützt.\\n\\nFortfahren?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Ihre Kodierer werden zu „%4“ und „%5“ geändert, da „%1“ „%2“ und „%3“ nicht unterstützt.\\n\\nFortfahren?\"\nBasic.Settings.Output.VideoBitrate=\"Videobitrate\"\nBasic.Settings.Output.AudioBitrate=\"Audiobitrate\"\nBasic.Settings.Output.Reconnect=\"Automatisches Wiederverbinden\"\nBasic.Settings.Output.RetryDelay=\"Wiederholungsverzögerung\"\nBasic.Settings.Output.MaxRetries=\"Maximale Wiederholungsversuche\"\nBasic.Settings.Output.Advanced=\"Benutzerdefinierte Kodierereinstellungen aktivieren (Erweitert)\"\nBasic.Settings.Output.EncoderPreset=\"Kodierervoreinstellung\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (niedrige CPU-Nutzung, niedrigste Qualität)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (Standard) (Mittlere CPU-Nutzung, Standardqualität)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (Hohe CPU-Nutzung und Qualität)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Benutzerdefinierte Kodierereinstellungen\"\nBasic.Settings.Output.CustomMuxerSettings=\"Benutzerdefinierte Muxereinstellungen\"\nBasic.Settings.Output.NoSpaceFileName=\"Dateinamen ohne Leerzeichen generieren\"\nBasic.Settings.Output.Adv.Rescale=\"Ausgabe umskalieren\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Deaktiviert\"\nBasic.Settings.Output.Adv.AudioTrack=\"Audiospur\"\nBasic.Settings.Output.Adv.Streaming=\"Stream\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Streameinstellungen\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Spur 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Spur 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Spur 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Spur 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Spur 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Spur 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch-VOD-Spur\"\nBasic.Settings.Output.Adv.Encoder=\"Kodierereinstellungen\"\nBasic.Settings.Output.Adv.Recording=\"Aufnahme\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Aufnahmeeinstellungen\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Aufnahmetyp\"\nBasic.Settings.Output.Adv.Recording.Type=\"Art\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Benutzerdefinierte Ausgabe (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Streamkodierer verwenden)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Dateinamenformatierung\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Überschreiben\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Die benutzerdefinierte Ausgabe (FFmpeg) wird ohne Absicherungen bereitgestellt.\\nEs wird empfohlen, den Aufnahmetyp \\\"Standard\\\" zu nutzen.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg-Ausgabetyp\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Datei\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Häufige Aufnahmeformate\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Alle Dateien\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Dateipfad oder URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Container-Format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Standardformat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Container-Formatbeschreibung\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Audio-/Videocodec wird aus Dateipfad oder URL gebildet\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Standardkodierer\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Kodierer deaktivieren\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Videokodierer\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Videokodierereinstellungen\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Audiokodierer\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Audiokodierereinstellungen\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxer-Einstellungen\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Keyframeintervall (Frames)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Alle Codecs anzeigen (auch wenn möglicherweise inkompatibel)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg-Einstellungen\"\nBasic.Settings.Output.EnableSplitFile=\"Datei automatisch teilen\"\nBasic.Settings.Output.SplitFile.TypeTime=\"nach Zeit\"\nBasic.Settings.Output.SplitFile.TypeSize=\"nach Größe\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Nur manuell teilen\"\nBasic.Settings.Output.SplitFile.Time=\"Trennzeit\"\nBasic.Settings.Output.SplitFile.Size=\"Trenngröße\"\nScreenshot=\"Screenshot von Ausgabe erstellen\"\nScreenshot.SourceHotkey=\"Screenshot von ausgewählter Quelle erstellen\"\nScreenshot.StudioProgram=\"Screenshot erstellen (Programm)\"\nScreenshot.Preview=\"Screenshot erstellen (Vorschau)\"\nScreenshot.Scene=\"Screenshot erstellen (Szene)\"\nScreenshot.Source=\"Screenshot erstellen (Quelle)\"\nFilenameFormatting.TT.CCYY=\"Jahr in vier Stellen\"\nFilenameFormatting.TT.YY=\"Letzte beiden Stellen des Jahres (00–99)\"\nFilenameFormatting.TT.MM=\"Monat als Zahl (01–12)\"\nFilenameFormatting.TT.DD=\"Tag des Monats mit Null aufgefüllt (01–31)\"\nFilenameFormatting.TT.hh=\"Stunde im 24-Stundenformat (00–23)\"\nFilenameFormatting.TT.mm=\"Minute (00–59)\"\nFilenameFormatting.TT.ss=\"Sekunde (00–59)\"\nFilenameFormatting.TT.Percent=\"Das %-Zeichen\"\nFilenameFormatting.TT.a=\"Abgekürzter Wochentagsname\"\nFilenameFormatting.TT.A=\"Voller Wochentagsname\"\nFilenameFormatting.TT.b=\"Abgekürzter Monatsname\"\nFilenameFormatting.TT.B=\"Voller Monatsname\"\nFilenameFormatting.TT.d=\"Tag des Monats mit Null aufgefüllt (01–31)\"\nFilenameFormatting.TT.H=\"Stunde im 24-Stundenformat (00–23)\"\nFilenameFormatting.TT.I=\"Stunde im 12-Stundenformat (01–12)\"\nFilenameFormatting.TT.m=\"Monat als Zahl (01–12)\"\nFilenameFormatting.TT.M=\"Minute (00–59)\"\nFilenameFormatting.TT.p=\"AM oder PM\"\nFilenameFormatting.TT.s=\"Sekunden seit UNIX-Epoche\"\nFilenameFormatting.TT.S=\"Sekunde (00–59)\"\nFilenameFormatting.TT.y=\"Letzte beiden Stellen des Jahres (00–99)\"\nFilenameFormatting.TT.Y=\"Jahr\"\nFilenameFormatting.TT.z=\"ISO-8601-Verschiebung der Zeitzone von UTC\"\nFilenameFormatting.TT.Z=\"Name oder Abkürzung der Zeitzone\"\nFilenameFormatting.TT.FPS=\"Frames pro Sekunde\"\nFilenameFormatting.TT.CRES=\"Basis-(Leinwand-)Auflösung\"\nFilenameFormatting.TT.ORES=\"(Skalierte) Ausgabeauflösung\"\nFilenameFormatting.TT.VF=\"Videoformat\"\nBasic.Settings.Video.Adapter=\"Grafikkarte\"\nBasic.Settings.Video.BaseResolution=\"Basis-(Leinwand-)Auflösung\"\nBasic.Settings.Video.ScaledResolution=\"(Skalierte) Ausgabeauflösung\"\nBasic.Settings.Video.DownscaleFilter=\"Skalierungsfilter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Auflösungen stimmen überein, keine Herunterskalierung nötig]\"\nBasic.Settings.Video.FPSCommon=\"Übliche FPS-Werte\"\nBasic.Settings.Video.FPSInteger=\"Ganzzahl-FPS-Wert\"\nBasic.Settings.Video.FPSFraction=\"Bruch-FPS-Wert\"\nBasic.Settings.Video.Numerator=\"Zähler\"\nBasic.Settings.Video.Denominator=\"Nenner\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (experimentell)\"\nBasic.Settings.Video.InvalidResolution=\"Ungültige Auflösung. Korrekte Formatierung: [Breite]x[Höhe] (z. B. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Videoausgabe ist derzeit aktiv. Bitte schalten Sie alle Ausgaben ab, um die Videoeinstellungen zu ändern.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (am schnellsten, bei Skalierung aber unscharf)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bikubisch (geschärfte Skalierung, 16 Stichproben)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (geschärfte Skalierung, 36 Stichproben)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Bereich (gewichtete Summe, 4/6/9 Stichproben)\"\nBasic.Settings.Audio.Channels=\"Kanäle\"\nBasic.Settings.Audio.Meters=\"Meter\"\nBasic.Settings.Audio.MeterDecayRate=\"Abklingrate\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Schnell\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Mittel (Type I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Langsam (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Spitzenmessertyp\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True Peak (höhere CPU-Nutzung)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"Achtung: Surround-Sound-Audio ist aktiviert.\"\nBasic.Settings.Audio.MultichannelWarning=\"Überprüfen Sie für das Streamen, ob Ihre Streamingplattform sowohl die Einspeisung als auch die Wiedergabe von Surround-Sound unterstützt. Beispielsweise unterstützt Facebook 360 Live Surround-Sound vollständig. YouTube Live unterstützt 5.1-Audioeinspeisung (und -Wiedergabe auf Fernsehgeräten).\\n\\nOBS-Audiofilter sind mit Surround-Sound kompatibel, die VST-Pluginunterstützung ist aber nicht garantiert.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Surround-Sound-Audio aktivieren?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Möchten Sie Surround-Sound-Audio wirklich aktivieren?\"\nBasic.Settings.Audio.Devices=\"Globale Audiogeräte\"\nBasic.Settings.Audio.DesktopDevice=\"Desktop-Audio\"\nBasic.Settings.Audio.DesktopDevice2=\"Desktop-Audio 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon-/AUX-Audio\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon-/AUX-Audio 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon-/AUX-Audio 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon-/AUX-Audio 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Push-To-Mute aktivieren\"\nBasic.Settings.Audio.PushToMuteDelay=\"Push-To-Mute-Verzögerung\"\nBasic.Settings.Audio.EnablePushToTalk=\"Push-To-Talk aktivieren\"\nBasic.Settings.Audio.PushToTalkDelay=\"Push-To-Talk-Verzögerung\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Gerät nicht angeschlossen oder verfügbar]\"\nBasic.Settings.Audio.Disabled=\"Deaktiviert\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Audiopufferung mit niedriger Latenz (für Decklink-/NDI-Ausgaben)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"Achtung: Audiopufferung mit niedriger Latenz ist aktiviert.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Audiopufferung mit niedriger Latenz kann dazu führen, dass Audio von einigen Quellen glitcht oder stoppt.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Audiopufferung mit niedriger Latenz aktivieren?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Möchten Sie Audiopufferung mit niedriger Latenz wirklich aktivieren?\"\nBasic.Settings.Accessibility=\"Barrierefreiheit\"\nBasic.Settings.Accessibility.ColorOverrides=\"Andere Farben verwenden\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Quellenrand beim Auswählen\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Quellenrand beim Zuschneiden\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Quellenrand beim Drüberfahren\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mixerlautstärkebalken (-60 bis -20 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mixerlautstärkebalken (-20 bis -9 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mixerlautstärkebalken (-9 bis 0 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mixerlautstärkebalken (-60 bis -20 dB) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mixerlautstärkebalken (-20 bis -9 dB) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mixerlautstärkebalken (-9 bis 0 dB) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Farbvoreinstellung\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Standard\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Benutzerdefiniert\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternative für Farbblindheit\"\nBasic.Settings.Advanced=\"Erweitert\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Warnen, wenn Ausgaben beim Beenden aktiv sind\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prozesspriorität\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Hoch\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Höher als normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Niedriger als normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Niedrig\"\nBasic.Settings.Advanced.FormatWarning=\"Achtung: Andere Farbformate als NV12 oder P010 sind in erster Linie für die Aufnahme bestimmt und für Streaming nicht zu empfehlen. Die erforderliche Farbformatkonvertierung kann eine höhere CPU-Nutzung hervorrufen.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Achtung: Formate mit hoher Präzision werden häufig mit HDR-Farbräumen verwendet.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Achtung: Rec. 2100 sollte ein Format mit größerer Genauigkeit verwenden.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Farbformat\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-Bit, 4:2:0, 2 Ebenen)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-Bit, 4:2:0, 3 Ebenen)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-Bit, 4:4:4, 3 Ebenen)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-Bit, 4:2:0, 2 Ebenen)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-Bit, 4:2:0, 3 Ebenen)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-Bit, 4:2:2, 2 Ebenen)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-Bit, 4:4:4, 2 Ebenen)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-Bit)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Farbraum\"\nBasic.Settings.Advanced.Video.ColorRange=\"Farbbereich\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Begrenzt\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Voll\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR-Weißwert\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nominaler HDR-Höchstwert\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Monitoringgerät\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Standard\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Windows-Audioducking deaktivieren\"\nBasic.Settings.Advanced.StreamDelay=\"Streamverzögerung\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Dauer\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Lückenloses Wiederverbinden (erhöht Verzögerung, um Videoverluste zu vermeiden)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Geschätzter RAM-Verbrauch: %1 MB\"\nBasic.Settings.Advanced.Network=\"Netzwerk\"\nBasic.Settings.Advanced.Network.Disabled=\"Das Ändern der Netzwerkeinstellungen wird durch das gewählte Streamingprotokoll nicht unterstützt.\"\nBasic.Settings.Advanced.Network.BindToIP=\"An IP binden\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP-Familie\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Netzwerkoptimierung aktivieren\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"TCP Pacing aktivieren\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Ein Versuch, die RTMP-Ausgabe für andere latenzempfindliche Anwendungen\\nim Netzwerk durch Regulierung der Übertragungsrate zu erleichtern, was aber\\ndas Risiko für ausgelassene Frames bei instabilen Verbindungen erhöhen kann.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Hotkeyfokusverhalten\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Hotkeys nie deaktivieren\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Hotkeys deaktivieren, wenn das Hauptfenster im Fokus ist\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Hotkeys deaktivieren, wenn das Hauptfenster nicht im Fokus ist\"\nBasic.Settings.Advanced.AutoRemux=\"Automatisch zu %1 remuxen\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(als mkv aufnehmen)\"\nBasic.AdvAudio=\"Erweiterte Audioeigenschaften\"\nBasic.AdvAudio.ActiveOnly=\"Nur aktive Quellen\"\nBasic.AdvAudio.Volume=\"Lautstärke\"\nBasic.AdvAudio.VolumeSource=\"Lautstärke für „%1“\"\nBasic.AdvAudio.MonoSource=\"Mono-Downmix für „%1“\"\nBasic.AdvAudio.BalanceSource=\"Balance für „%1“\"\nBasic.AdvAudio.SyncOffset=\"Synchronisationsausgleich\"\nBasic.AdvAudio.SyncOffsetSource=\"Synchronisationsausgleich für „%1“\"\nBasic.AdvAudio.Monitoring=\"Audiomonitoring\"\nBasic.AdvAudio.Monitoring.None=\"Monitoring aus\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Nur Monitoring an (keine Ausgabe)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitoring und Ausgabe an\"\nBasic.AdvAudio.MonitoringSource=\"Audiomonitoring für „%1“\"\nBasic.AdvAudio.AudioTracks=\"Spuren\"\nBasic.Settings.Hotkeys.Pair=\"Mit „%1“ geteilte Tastenkombinationen fungieren als Schalter\"\nBasic.Settings.Hotkeys.Filter=\"Filtern\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Nach Hotkey filtern\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Dieser Hotkey wird von mehreren Aktionen geteilt. Klicken Sie hier, um Konflikte anzuzeigen.\"\nBasic.Settings.Hotkeys.PleaseWait=\"Hotkeys werden geladen, bitte warten …\"\nBasic.Hotkeys.SelectScene=\"Zur Szene wechseln\"\nBasic.SystemTray.Show=\"Anzeigen\"\nBasic.SystemTray.Hide=\"Verstecken\"\nBasic.SystemTray.Message.Reconnecting=\"Verbindung verloren. Verbindung wird wiederhergestellt …\"\nHotkeys.Insert=\"Einfügen\"\nHotkeys.Delete=\"Entfernen\"\nHotkeys.Home=\"Pos1\"\nHotkeys.End=\"Ende\"\nHotkeys.PageUp=\"Bild-auf-Taste\"\nHotkeys.PageDown=\"Bild-ab-Taste\"\nHotkeys.NumLock=\"Num-Taste\"\nHotkeys.ScrollLock=\"Rollen-Taste\"\nHotkeys.CapsLock=\"Feststelltaste\"\nHotkeys.Backspace=\"Rücktaste\"\nHotkeys.Tab=\"Tabulator\"\nHotkeys.Print=\"Drucken\"\nHotkeys.Left=\"Pfeiltaste links\"\nHotkeys.Right=\"Pfeiltaste rechts\"\nHotkeys.Up=\"Pfeiltaste hoch\"\nHotkeys.Down=\"Pfeiltaste runter\"\nHotkeys.Menu=\"Menü\"\nHotkeys.Space=\"Leertaste\"\nHotkeys.NumpadNum=\"Ziffernblock %1\"\nHotkeys.NumpadMultiply=\"Ziffernblock: multiplizieren\"\nHotkeys.NumpadDivide=\"Ziffernblock: dividieren\"\nHotkeys.NumpadAdd=\"Ziffernblock: addieren\"\nHotkeys.NumpadSubtract=\"Ziffernblock: subtrahieren\"\nHotkeys.NumpadDecimal=\"Ziffernblock: Komma\"\nHotkeys.AppleKeypadNum=\"%1 (Ziffernblock)\"\nHotkeys.AppleKeypadMultiply=\"* (Ziffernblock)\"\nHotkeys.AppleKeypadDivide=\"/ (Ziffernblock)\"\nHotkeys.AppleKeypadAdd=\"+ (Ziffernblock)\"\nHotkeys.AppleKeypadSubtract=\"- (Ziffernblock)\"\nHotkeys.AppleKeypadDecimal=\", (Ziffernblock)\"\nHotkeys.AppleKeypadEqual=\"= (Ziffernblock)\"\nHotkeys.MouseButton=\"Maus %1\"\nMute=\"Stummschalten\"\nUnmute=\"Stummschaltung aufheben\"\nPush-to-mute=\"Push-To-Mute\"\nPush-to-talk=\"Push-To-Talk\"\nSceneItemShow=\"„%1“ anzeigen\"\nSceneItemHide=\"„%1“ verbergen\"\nOutputWarnings.NoTracksSelected=\"Sie müssen mindestens eine Spur auswählen.\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Ausgabeeinstellungsfehler\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Für jede Ausgabe muss mindestens eine Audiospur ausgewählt sein.\"\nOutputWarnings.MP4Recording=\"Achtung: Aufnahmen, die in MP4/MOV gespeichert werden, sind nicht wiederherstellbar, wenn die Datei nicht fertiggestellt werden konnte (z. B. als Folge eines BSOD oder Stromausfalls). Wenn Sie mehrere Audiospuren aufnehmen möchten, sollten Sie MKV verwenden und die Aufnahme nach ihrer Fertigstellung zu MP4/MOV remuxen. („Datei“ → „Aufnahmen remuxen“)\"\nOutputWarnings.CannotPause=\"Achtung: Aufnahmen können nicht pausiert werden, wenn der Aufnahmekodierer auf „(Streamkodierer verwenden)“ gesetzt ist.\"\nOutputWarnings.CodecIncompatible=\"Der ausgewählte Audio- oder Videokodierer wurde wegen Inkompatibilität zurückgesetzt. Bitte wählen Sie einen passenden Kodierer aus der Liste.\"\nCodecCompat.Incompatible=\"(Nicht mit %1 kompatibel)\"\nCodecCompat.CodecPlaceholder=\"Kodierer auswählen …\"\nCodecCompat.ContainerPlaceholder=\"Format auswählen …\"\nCodecCompat.CodecMissingOnExit.Title=\"Kein Kodierer ausgewählt\"\nCodecCompat.CodecMissingOnExit.Text=\"Mindestens ein Video- oder Audioenoder wurde nicht ausgewählt. Es sollte sichergestellt werden, ob für die Aufnahmen und für  \"\nCodecCompat.ContainerMissingOnExit.Title=\"Kein Format ausgewählt\"\nCodecCompat.ContainerMissingOnExit.Text=\"Kein Aufnahmeformat wurde gesetzt. Bitte wählen Sie ein Aufnahmeformat, welches mit dem gewählten Streamkodierer kompatibel ist.\"\nFinalScene.Title=\"Szene entfernen\"\nFinalScene.Text=\"Es muss mindestens eine Szene existieren.\"\nNoSources.Title=\"Keine Quellen\"\nNoSources.Text=\"Sie haben noch keine Videoquellen hinzugefügt, sodass nur ein leerer Bildschirm ausgegeben wird. Sind Sie sicher, dass Sie das wollen?\"\nNoSources.Text.AddSource=\"Sie können jederzeit Quellen hinzufügen, indem Sie auf das + unter dem Quellenfeld im Hauptfenster klicken.\"\nNoSources.Label=\"Sie haben keine Quellen hinzugefügt.\\nKlicken Sie auf das + oder\\nrechtsklicken Sie hier, um welche hinzuzufügen.\"\nChangeBG=\"Farbe auswählen\"\nCustomColor=\"Benutzerdefinierte Farbe\"\nBrowserSource.EnableHardwareAcceleration=\"Browserhardwarebeschleunigung aktivieren\"\nAbout=\"Über\"\nAbout.Info=\"OBS Studio ist eine kostenlose und freie Open-Source-Software für die Erstellung von Livestreams und Videoaufnahmen.\"\nAbout.Donate=\"Leisten Sie einen Beitrag\"\nAbout.GetInvolved=\"Wirken Sie mit\"\nAbout.Authors=\"Autoren\"\nAbout.License=\"Lizenzvereinbarung\"\nAbout.Error=\"Fehler beim Lesen der Datei.\\n\\nBesuchen Sie: %1\"\nAbout.Contribute=\"Unterstützen Sie das OBS-Projekt\"\nAddUrl.Title=\"Quelle per URL hinzufügen\"\nAddUrl.Text=\"Sie haben eine URL in OBS gezogen. Dies wird automatisch den Link als Quelle hinzufügen. Fortfahren?\"\nResizeOutputSizeOfSource=\"Ausgabeauflösung anpassen (Quellgröße)\"\nResizeOutputSizeOfSource.Text=\"Die Basis- und Ausgabeauflösung wird auf die Größe der aktuellen Quelle skaliert.\"\nResizeOutputSizeOfSource.Continue=\"Fortfahren?\"\nPreviewTransition=\"Übergangsvorschau\"\nImporter=\"Szenesammlung importieren\"\nImporter.SelectCollection=\"Wählen Sie eine Szenensammlung aus\"\nImporter.Collection=\"Szenensammlung\"\nImporter.HelpText=\"Fügen Sie Dateien zu diesem Fenster hinzu, um Sammlungen von OBS oder anderen bekannten Programmen zu importieren.\"\nImporter.Path=\"Sammlungspfad\"\nImporter.Program=\"Erkannte Anwendung\"\nImporter.AutomaticCollectionPrompt=\"Automatisch nach Szenensammlungen suchen\"\nImporter.AutomaticCollectionText=\"OBS kann automatisch importierbare Szenesammlungen von Drittanbieter-Programmen finden. Möchten Sie, dass OBS automatisch nach Sammlungen für Sie sucht?\\n\\nSie können diese Einstellung später unter „Einstellungen“ → „Allgemein“ → „Importierung“ ändern.\"\nImporter.SelectFile=\"Durchsuchen...\"\nRestart=\"Neustart\"\nNeedsRestart=\"OBS Studio muss neu gestartet werden. Möchten Sie jetzt neu starten?\"\nLoadProfileNeedsRestart=\"Das Profil enthält Einstellungen, die einen Neustart von OBS erfordern.\\n%1\\n\\nMöchten Sie OBS jetzt neu starten, damit diese Einstellungen wirksam werden?\"\nContextBar.NoSelectedSource=\"Keine Quelle ausgewählt\"\nContextBar.MediaControls.PlayMedia=\"Medien abspielen\"\nContextBar.MediaControls.PauseMedia=\"Medien pausieren\"\nContextBar.MediaControls.StopMedia=\"Medien stoppen\"\nContextBar.MediaControls.RestartMedia=\"Medien neu starten\"\nContextBar.MediaControls.PlaylistNext=\"Weiter in Playlist\"\nContextBar.MediaControls.PlaylistPrevious=\"Zurück in Playlist\"\nContextBar.MediaControls.BlindSeek=\"Mediensuchelement\"\nYouTube.Auth.Ok=\"Autorisierung erfolgreich abgeschlossen.\\nSie können diese Seite jetzt schließen.\"\nYouTube.Auth.NoCode=\"Der Autorisierungsprozess wurde nicht abgeschlossen.\"\nYouTube.Auth.NoChannels=\"Kein Kanal für das ausgewählte Konto verfügbar\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube-Benutzerautorisierung\"\nYouTube.Auth.WaitingAuth.Text=\"Bitte vervollständigen Sie die Autorisierung in Ihrem externen Browser.<br>Falls sich der Browser nicht öffnet, folgen Sie diesem Link und vervollständigen Sie dort die Autorisierung:<br>%1\"\nYouTube.AuthError.Text=\"Fehler beim Abrufen der Kanalinformationen: %1.\"\nYouTube.Actions.WindowTitle=\"Einrichtung einer YouTube-Übertragung – Kanal: %1\"\nYouTube.Actions.CreateNewEvent=\"Neue Übertragung erstellen\"\nYouTube.Actions.ChooseEvent=\"Existierende Übertragung auswählen\"\nYouTube.Actions.Title=\"Titel*\"\nYouTube.Actions.MyBroadcast=\"Meine Übertragung\"\nYouTube.Actions.Description=\"Beschreibung\"\nYouTube.Actions.Privacy=\"Sichtbarkeit*\"\nYouTube.Actions.Privacy.Private=\"Privat\"\nYouTube.Actions.Privacy.Public=\"Öffentlich\"\nYouTube.Actions.Privacy.Unlisted=\"Nicht gelistet\"\nYouTube.Actions.Category=\"Kategorie\"\nYouTube.Actions.Thumbnail.SelectFile=\"Datei auswählen …\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Keine Datei ausgewählt\"\nYouTube.Actions.Thumbnail.ClearFile=\"Entfernen\"\nYouTube.Actions.MadeForKids=\"Ist dieses Video speziell für Kinder?*\"\nYouTube.Actions.MadeForKids.Yes=\"Ja, es ist speziell für Kinder\"\nYouTube.Actions.MadeForKids.No=\"Nein, es ist nicht speziell für Kinder\"\nYouTube.Actions.AdditionalSettings=\"Weitere Einstellungen\"\nYouTube.Actions.Latency=\"Latenz\"\nYouTube.Actions.Latency.Low=\"Niedrig\"\nYouTube.Actions.Latency.UltraLow=\"Extrem niedrig\"\nYouTube.Actions.EnableAutoStart=\"Automatischen Start aktivieren\"\nYouTube.Actions.EnableAutoStop=\"Automatisches Beenden aktivieren\"\nYouTube.Actions.AutoStartStop.TT=\"Gibt an, ob diese geplante Übertragung automatisch starten soll\"\nYouTube.Actions.EnableDVR=\"DVR aktivieren\"\nYouTube.Actions.360Video=\"360°-Video\"\nYouTube.Actions.ScheduleForLater=\"Für später planen\"\nYouTube.Actions.RememberSettings=\"Einstellungen merken\"\nYouTube.Actions.Create_Ready=\"Übertragung erstellen\"\nYouTube.Actions.Create_GoLive=\"Übertragung erstellen und Stream starten\"\nYouTube.Actions.Choose_Ready=\"Übertragung auswählen\"\nYouTube.Actions.Choose_GoLive=\"Übertragung auswählen und Stream starten\"\nYouTube.Actions.Create_Schedule=\"Übertragung planen\"\nYouTube.Actions.Create_Schedule_Ready=\"Übertragung planen und auswählen\"\nYouTube.Actions.Dashboard=\"YouTube Studio öffnen\"\nYouTube.Actions.Error.Title=\"Erstellungsfehler der Live-Übertragung\"\nYouTube.Actions.Error.Text=\"YouTube-Zugriffsfehler „%1“.<br/>Eine detaillierte Fehlerbeschreibung finden Sie unter <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTube-Zugriffsfehler. Bitte überprüfen Sie Ihre Netzwerkverbindung oder Ihren YouTube-Serverzugriff.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Fehler „%1“ beim Erstellen der Übertragung.<br/>Eine detaillierte Fehlerbeschreibung finden Sie unter <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Kein Stream erstellt. Bitte verknüpfen Sie Ihr Konto erneut.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube-API-Fehler. Weitere Informationen finden Sie in der Logdatei.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Die ausgewählte Übertragung wurde nicht gefunden.\"\nYouTube.Actions.Error.FileMissing=\"Die ausgewählte Datei existiert nicht.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Fehler beim Öffnen der ausgewählten Datei.\"\nYouTube.Actions.Error.FileTooLarge=\"Die ausgewählte Datei ist zu groß (Limit: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Der Übergang zur Übertragung ist fehlgeschlagen: %1<br/><br/>Wenn dieses Problem weiterhin besteht, <a href='https://studio.youtube.com/video/%2/livestreaming'>öffnen Sie die Übertragung in YouTube Studio</a> und versuchen Sie es manuell erneut.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Die Übertragung geht in die Testphase über, was einige Zeit in Anspruch nehmen kann. Bitte versuchen Sie es in 10–30 Sekunden erneut.\"\nYouTube.Actions.EventsLoading=\"Liste der Events wird geladen …\"\nYouTube.Actions.EventCreated.Title=\"Event erstellt\"\nYouTube.Actions.EventCreated.Text=\"Event erfolgreich erstellt.\"\nYouTube.Actions.Stream.ScheduledFor=\"Geplant für %1\"\nYouTube.Actions.Stream.Resume=\"Unterbrochenen Stream fortsetzen\"\nYouTube.Actions.Stream.YTStudio=\"Automatisch von YouTube Studio erstellt\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Eine neue Live-Übertragung wird erstellt, bitte warten …\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Manueller Start erforderlich\"\nYouTube.Actions.AutoStartStreamingWarning=\"Der automatische Start ist für dieses Event deaktiviert. Klicken Sie auf „Live gehen“, um Ihre Übertragung zu starten.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Sie können die Verbindung nicht wiederherstellen.<br>Ihr Stream wird gestoppt und Sie werden nicht länger live sein.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Livestreams sind für den gewählten YouTube-Kanal nicht aktiviert.<br/><br/>Gehen Sie auf <a href='https://www.youtube.com/features'>youtube.com/features</a> für weitere Informationen.\"\nYouTube.Errors.livePermissionBlocked=\"Livestreams sind für den gewählten YouTube-Kanal nicht verfügbar.<br/>Nach Aktivierung in Ihren Kanaleinstellungen kann die tatsächliche Freischaltung bis zu 24 Stunden dauern.<br/><br/>Gehen Sie auf <a href='https://www.youtube.com/features'>youtube.com/features</a> für mehr Informationen.\"\nYouTube.Errors.errorExecutingTransition=\"Der Übergang ist aufgrund eines Backend-Fehlers fehlgeschlagen. Bitte versuchen Sie es in ein paar Sekunden erneut.\"\nYouTube.Errors.errorStreamInactive=\"YouTube erhält keine Streamdaten. Bitte überprüfen Sie Ihre Einstellungen und versuchen Sie es erneut.\"\nYouTube.Errors.invalidTransition=\"Der versuchte Übergang war ungültig. Möglicherweise hat der Stream einen vorherigen Übergang nicht beendet. Versuchen Sie es gleich noch mal.\"\nYouTube.DocksRemoval.Title=\"Veraltete YouTube-Browser-Docks bereinigen\"\nYouTube.DocksRemoval.Text=\"Folgende Browser-Docks werden entfernt, da diese veraltet sind:\\n\\n%1\\nVerwenden Sie stattdessen „Docks“ → „YouTube Live Control Room“.\"\nConfigDownload.WarningMessageTitle=\"Achtung\"\nFailedToStartStream.MissingConfigURL=\"Keine Konfigurations-URL für die aktuelle Plattform verfügbar\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Benutzerdefinierte RTMP-URL nicht angegeben\"\nFailedToStartStream.InvalidCustomConfig=\"Ungültige benutzerdefinierte Konfiguration\"\nFailedToStartStream.MissingCanvas=\"Eine konfigurierte zusätzliche Leinwand fehlt\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Mehrspur-Videodienst konnte nicht erstellt werden.\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Mehrspur-Video-RTMP-Ausgabe konnte nicht erstellt werden.\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC nicht verfügbar.\\n\\nDer Kodierertyp „%1“ konnte nicht gefunden werden.\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Der Videokodierer „%1“ (Typ: „%2“) konnte nicht erstellt werden.\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"OBS-Videoinformationen konnten beim Erstellen des Encoders „%1“ (Typ: „%2“) nicht abgerufen werden.\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Der Audiokodierer konnte nicht erstellt werden.\"\nFailedToStartStream.NoRTMPURLInConfig=\"Die Konfiguration beinhaltet keine RTMP(S)-Stream Ziel-URL\"\nFailedToStartStream.FallbackToDefault=\"Das Starten des Streams mit %1 ist fehlgeschlagen. Möchten Sie es noch einmal mit den Einstellungen für die einfache Kodierung versuchen?\"\nFailedToStartStream.ConfigRequestFailed=\"Die Konfiguration konnte nicht von %1 abgefragt werden.<br><br>HTTP-Fehler: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Unbekannten Statuswert „%1“ erhalten.\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nMöchten Sie ohne %1 weiter streamen?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nMöchten Sie weiter streamen?\"\nFailedToStartStream.MissingEncoderConfigs=\"Die Konfiguration um live gehen enthielt keine Kodiererkonfigurationen\"\nFailedToStartStream.InvalidEncoderConfig=\"Die Go-Live-Konfiguration enthielt eine ungültige Kodiererkonfiguration\"\nFailedToStartStream.StatusMissingHTML=\"Die Anfrage live zu gehen gab einen unbestimmten Fehler zurück\"\nFailedToStartStream.NoConfigSupplied=\"Fehlende Konfiguration\"\nMultitrackVideo.Info=\"%1 optimiert automatisch Ihre Einstellungen, um mehrere Videoqualitäten zu kodieren und zu senden. %2 werden dann Informationen über die Konfiguration Ihres Computers und Ihrer Software mitgeteilt.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Inkompatible Einstellungen\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 ist inkompatibel mit:\\n\\n%2\\nUm mit %1 weiter zu streamen, deaktivieren Sie inkompatible Einstellungen:\\n\\n%3\\nund starten Sie den Stream erneut.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Für diesen Stream deaktivieren und Stream starten\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Einstellungen aktualisieren und Stream starten\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 ist zurzeit nicht mit auf „%2“ gesetzte [Audio → Allgemein → Kanäle] kompatibel. %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Audio → Allgemein → Kanäle] muss auf „%1“ gesetzt werden.\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 benötigt mehrere verschiedene Einstellungen für [Audio → Allgemein → Kanäle].\"\nBasic.OpenPluginManager=\"Plugin-Manager\"\nPluginManager=\"Plugins\"\nPluginManager.Restart=\"OBS Studio muss neu gestartet werden, um die geänderten Plugins anzuwenden\"\nPluginManager.MissingPlugin=\"[PLUGIN NICHT GEFUNDEN]\"\nPluginManager.Section.Discover=\"Durchsuchen\"\nPluginManager.Section.Manage=\"Installiert\"\nPluginManager.Section.Manage.Title=\"Aktive Plugins verwalten\"\n"
  },
  {
    "path": "frontend/data/locale/el-GR.ini",
    "content": "Language=\"Ελληνικά\"\nOK=\"Εντάξει\"\nApply=\"Εφαρμογή\"\nCancel=\"Ακύρωση\"\nClose=\"Κλείσιμο\"\nSave=\"Αποθήκευση\"\nDiscard=\"Απόρριψη\"\nDisable=\"Απενεργοποίηση\"\nYes=\"Ναι\"\nNo=\"Όχι\"\nAdd=\"Προσθήκη\"\nRemove=\"Αφαίρεση\"\nRename=\"Μετονομασία...\"\nInteract=\"Αλληλεπίδραση\"\nFilters=\"Φίλτρα\"\nProperties=\"Ιδιότητες\"\nMoveUp=\"Μετακίνηση Πάνω\"\nMoveDown=\"Μετακίνηση Κάτω\"\nSettings=\"Ρυθμίσεις\"\nDisplay=\"Οθόνη\"\nName=\"Όνομα\"\nExit=\"Έξοδος\"\nMixer=\"Μείκτης Ήχου\"\nBrowse=\"Αναζήτηση\"\nMono=\"Μονοφωνικό\"\nStereo=\"Στερεοφωνικό\"\nDroppedFrames=\"Κομμένα καρέ %1 (%2%)\"\nProjector.Open.Program=\"Άνοιγμα Προβολέα Προγράμματος\"\nProjector.Open.Preview=\"Άνοιγμα Προβολέα Προεπισκόπησης\"\nProjector.Open.Scene=\"Άνοιγμα Προβολέα Σκηνής\"\nProjector.Open.Source=\"Άνοιγμα Προβολέα Πηγής\"\nProjector.Open.Multiview=\"Άνοιγμα προβολής πολλαπλών παραθύρων\"\nProjector.Display=\"Οθόνη: %1\"\nProjector.Window=\"Νέο παράθυρο\"\nProjector.Title=\"Προβολέας\"\nProjector.Title.Scene=\"Σκηνή: %1\"\nProjector.Title.Source=\"Πηγή: %1\"\nProjector.Title.Multiview=\"Προβολή πολλαπλών παραθύρων\"\nProjector.ResizeWindowToContent=\"Προσαρμογή παραθύρου στο περιεχόμενο\"\nClear=\"Εκκαθάριση\"\nRevert=\"Επαναφορά\"\nShow=\"Εμφάνιση\"\nHide=\"Απόκρυψη\"\nUnhideAll=\"Επανεμφάνιση όλων\"\nUntitled=\"Χωρίς όνομα\"\nNew=\"Νέο\"\nDuplicate=\"Διπλότυπο\"\nEnable=\"Ενεργοποίηση\"\nDisableOSXVSync=\"Απενεργοποίηση macOS V-Sync\"\nResetOSXVSyncOnExit=\"Επαναφορά macOS V-Sync κατά την έξοδο\"\nHighResourceUsage=\"Υπερφόρτωση κωδικοποίησης! Δοκιμάστε να χαμηλώσετε τις ρυθμίσεις βίντεο ή να χρησιμοποιήσετε μια ταχύτερη επιλογή κωδικοποίησης.\"\nTransition=\"Μετάβαση\"\nQuickTransitions=\"Γρήγορες Μεταβάσεις\"\nFadeToBlack=\"Ξεθωριάστε σε μαύρο\"\nLeft=\"Αριστερά\"\nRight=\"Δεξιά\"\nTop=\"Πάνω\"\nBottom=\"Κάτω\"\nReset=\"Επαναφορά\"\nHours=\"Ώρες\"\nMinutes=\"Λεπτά\"\nSeconds=\"Δευτερόλεπτα\"\nDeprecated=\"Καταργημένες\"\nReplayBuffer=\"Προσωρινή Μνήμη Επανάληψης\"\nImport=\"Εισαγωγή\"\nExport=\"Εξαγωγή\"\nCopy=\"Αντιγραφή\"\nPaste=\"Επικόλληση\"\nPasteReference=\"Επικόλληση (Με Αναφορά)\"\nPasteDuplicate=\"Επικόλληση (Αντίγραφο)\"\nRemuxRecordings=\"Μετατροπή Καταγραφών\"\nNext=\"Επόμενο\"\nBack=\"Προηγούμενο\"\nDefaults=\"Προεπιλογές\"\nRestoreDefaults=\"Προεπιλογές\"\nHideMixer=\"Απόκρυψη στον Μίκτη\"\nTransitionOverride=\"Παράκαμψη της μετάβασης\"\nShowTransition=\"Εμφάνιση Μετάβασης\"\nHideTransition=\"Απόκρυψη Μετάβασης\"\nNone=\"Καμία\"\nStudioMode.Preview=\"Προεπισκόπηση\"\nStudioMode.Program=\"Έξοδος\"\nStudioMode.PreviewSceneName=\"Προεπισκόπηση: %1\"\nStudioMode.ProgramSceneName=\"Πρόγραμμα: %1\"\nShowInMultiview=\"Εμφάνιση σε Multiview\"\nVerticalLayout=\"Κατακόρυφη Διάταξη\"\nGroup=\"Ομάδα\"\nDoNotShowAgain=\"Μην εμφανισθεί ξανά\"\nDefault=\"(Προεπιλογή)\"\nCalculating=\"Υπολογισμός...\"\nFullscreen=\"Πλήρης οθόνη\"\nWindowed=\"Παράθυρο\"\nRefreshBrowser=\"Ανανέωση\"\nAspectRatio=\"Λόγος Διαστάσεων <b>%1:%2</b>\"\nLockVolume=\"Κλείδωμα Έντασης\"\nLogViewer=\"Προβολή καταγραφών\"\nShowOnStartup=\"Εμφάνιση κατά την εκκίνηση\"\nOpenFile=\"Άνοιγμα αρχείου\"\nAddScene=\"Προσθήκη Σκηνής\"\nAddSource=\"Προσθήκη Πηγής\"\nRemoveScene=\"Αφαίρεση Επιλεγμένης Σκηνής\"\nRemoveSource=\"Αφαίρεση Επιλεγμένης(ων) Πηγής(ών)\"\nMoveSceneUp=\"Μετακίνηση Σκηνής Πάνω\"\nMoveSceneDown=\"Μετακίνηση Σκηνής Κάτω\"\nMoveSourceUp=\"Μετακίνηση Πηγής(ών) Πάνω\"\nMoveSourceDown=\"Μετακίνηση Πηγής(ών) Κάτω\"\nSourceProperties=\"Άνοιγμα Ιδιοτήτων Πηγής\"\nSourceFilters=\"Άνοιγμα Ιδιοτήτων Φίλτρου\"\nMixerToolbarMenu=\"Μίκτης Ήχου\"\nSceneFilters=\"Άνοιγμα Ιδιοτήτων Φίλτρου\"\nList=\"Λίστα\"\nGrid=\"Πλέγμα\"\nAutomatic=\"Αυτόματο\"\nComingSoon=\"Έρχεται σύντομα\"\nPluginsFailedToLoad.Title=\"Σφάλμα Φόρτωσης Πρόσθετου\"\nPluginsFailedToLoad.Text=\"Τα παρακάτω πρόσθετα του OBS απέτυχαν να φορτωθούν:\\n\\n%1\\nΠαρακαλώ αναβαθμίστε ή αφαιρέστε αυτά τα πρόσθετα.\"\nAlreadyRunning.Title=\"Το OBS εκτελείται ήδη\"\nAlreadyRunning.Text=\"Το OBS εκτελείται ήδη! Εκτός αν αυτός ήταν ο σκοπός σας, παρακαλούμε τερματίστε τις τρέχουσες διεργασίες OBS πριν προσπαθήσετε να εκκινήσετε μια καινούρια. Εάν έχετε ρυθμίσει το OBS να ελαχιστοποιείται στην γραμμή εργαλείων, παρακαλούμε να ελέγξετε αν τρέχει ήδη εκεί.\"\nAlreadyRunning.LaunchAnyway=\"Εκκίνηση ούτως ή άλλως\"\nCrashHandling.Checkbox.SendReport=\"Έχω διαβάσει την πολιτική απορρήτου και συναινώ στο ανέβασμα.\"\nCrashHandling.Buttons.LaunchSafe=\"Εκτέλεση σε Ασφαλή Λειτουργία\"\nCrashHandling.Buttons.LaunchNormal=\"Εκτέλεση σε Κανονική Λειτουργία\"\nSafeMode.Restart=\"Θέλετε να επανεκκινήσετε το OBS σε Ασφαλή Λειτουργία (απενεργοποιημένα πρόσθετα τρίτων, scripting και websockets)?\"\nSafeMode.RestartNormal=\"Θέλετε να επανεκκινήσετε το OBS σε Κανονική Λειτουργία;\"\nChromeOS.Title=\"Μη υποστηριγμένη πλατφόρμα\"\nChromeOS.Text=\"Το OBS φαίνεται να τρέχει μέσα σε έναν περιέκτη ChromeOS.Αυτή η πλατφόρμα δεν υποστηρίζεται.\"\nWine.Title=\"Ανιχνεύθηκε το Wine\"\nWine.Text=\"Η εκτέλεση του OBS με Wine δεν υποστηρίζεται, και πολλές δυνατότητες όπως πηγές καταγραφής ή συσκευών δε θα λειτουργούν ή θα λειτουργούν με περιορισμένες ικανότητες.<br><br>Προτείνεται η εκτέλεση μιας εγγενής έκδοσης του OBS, για παράδειγμα η <a href='https://flathub.org/apps/details/com.obsproject.Studio'>έκδοση Flatpak</a> ή τα πακέτα του λειτουργικού σας συστήματος.\"\nDockCloseWarning.Title=\"Κλείσιμο \\\"δεμένου\\\" παραθύρου\"\nDockCloseWarning.Text=\"Μόλις κλείσατε ένα στοιχιζόμενο παράθυρο. Αν θέλετε να το ξαναεμφανίσετε επιλέξτε το μενού Docks, στη γραμμή μενού.\"\nExtraBrowsers=\"Προσαρμοσμένες αποβάθρες προγράμματος περιήγησης\"\nExtraBrowsers.Info=\"Προσθέστε στοιχιζόμενα παράθυρα δίνοντας τους ένα όνομα και μια διεύθυνση URL, μετά πατήστε Εφαρμογή ή Κλείσιμο για να ανοίξουν. Μπορείτε να προσθέσετε ή να αφαιρέσετε στοιχιζόμενα παράθυρα οποιαδήποτε στιγμή.\"\nExtraBrowsers.DockName=\"Όνομα Dock\"\nAuth.Authing.Title=\"Επαλήθευση ταυτότητας...\"\nAuth.Authing.Text=\"Πιστοποίηση με %1, παρακαλώ περιμένετε...\"\nAuth.AuthFailure.Title=\"Αποτυχία ελέγχου ταυτότητας\"\nAuth.AuthFailure.Text=\"Απέτυχε η πιστοποίηση με %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Απαιτείται πιστοποίηση\"\nAuth.InvalidScope.Text=\"Οι απαιτήσεις πιστοποίησης για το %1 έχουν αλλάξει. Κάποιες δυνατότητες μπορεί να μην είναι διαθέσιμες.\"\nAuth.LoadingChannel.Title=\"Φόρτωση πληροφοριών καναλιού...\"\nAuth.LoadingChannel.Text=\"Φόρτωση πληροφοριών %1 καναλιού, παρακαλώ περιμένετε...\"\nAuth.LoadingChannel.Error=\"Δεν ήταν δυνατή η λήψη πληροφοριών βάσης δεδομένων.\"\nAuth.ChannelFailure.Title=\"Απέτυχε η φόρτωση του καναλιού\"\nAuth.ChannelFailure.Text=\"Αποτυχία φόρτωσης πληροφοριών %1 καναλιού\\n\\n%2:%3\"\nAuth.Chat=\"Συνομιλία\"\nAuth.StreamInfo=\"Πληροφορίες Ροής\"\nTwitchAuth.Stats=\"Στατιστηκά Twitch\"\nTwitchAuth.Feed=\"Twitch Ροή Δραστηριότητας\"\nTwitchAuth.TwoFactorFail.Title=\"Δεν ήταν δυνατή η ερώτηση του κλειδιού ροής\"\nTwitchAuth.TwoFactorFail.Text=\"Το OBS δεν μπόρεσε να συνδεθεί στο λογαριασμό σας Twitch. Παρακαλώ σιγουρευτείτε ότι ο έλεγχος ταυτότητας δύο παραγόντων έχει ρυθμιστεί στις <a href='https://www.twitch.tv/settings/security'>ρυθμίσεις ασφαλείας Twitch</a> καθώς αυτό απαιτείται για μετάδοση.\"\nRestreamAuth.Channels=\"Κανάλια Restream\"\nCopy.Filters=\"Αντιγραφή Φίλτρων\"\nPaste.Filters=\"Επικόλληση Φίλτρων\"\nBrowserPanelInit.Title=\"Αρχικοποίηση προγράμματος περιήγησης...\"\nBrowserPanelInit.Text=\"Αρχικοποίηση προγράμματος περιήγησης, παρακαλώ περιμένετε...\"\nBandwidthTest.Region=\"Περιοχή\"\nBandwidthTest.Region.US=\"Ηνωμένες Πολιτείες\"\nBandwidthTest.Region.EU=\"Ευρώπη\"\nBandwidthTest.Region.Asia=\"Ασία\"\nBandwidthTest.Region.Other=\"Άλλη\"\nBasic.AutoConfig=\"Οδηγός αυτόματης ρύθμισης παραμέτρων\"\nBasic.AutoConfig.ApplySettings=\"Εφαρμογή ρυθμίσεων\"\nBasic.AutoConfig.StartPage=\"Πληροφορίες χρήσης\"\nBasic.AutoConfig.StartPage.SubTitle=\"Προσδιορίστε το σκοπό χρήσης του προγράμματος\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Βελτιστοποίηση για ροή, η καταγραφή είναι δευτερεύουσα\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Βελτιστοποίηση μόνο για καταγραφή, όχι για ροή\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Θα χρησιμοποιήσω μόνο την εικονική κάμερα\"\nBasic.AutoConfig.VideoPage=\"Ρυθμίσεις βίντεο\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Καθορίστε τις ρυθμίσεις βίντεο που θα θέλατε να χρησιμοποιήσετε\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Χρήση Τρέχουσας (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Οθόνη %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Χρήση Τρεχόντων (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Είτε 60 είτε 30, αλλά προτίμηση 60 όταν είναι δυνατόν\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Είτε 60 είτε 30, αλλά προτίμηση υψηλής ανάλυσης\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Σημείωση: Η (βασική) ανάλυση του καμβά δεν είναι αναγκαστικά η ίδια με την ανάλυση που θα κάνετε ροή ή καταγραφή. Η πραγματική ανάλυση ροής/καταγραφής μπορεί να ελαττωθεί απο την ανάλυση του καμβά για να μειωθεί η χρήση πόρων ή οι ανάγκες ρυθμού μετάδοσης bits.\"\nBasic.AutoConfig.StreamPage=\"Πληροφορίες Ροής\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Παρακαλώ εισάγετε τις πληροφορίες της ροής σας\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Σύνδεση Λογαριασμού (προτείνεται)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Αποσύνδεση Λογαριασμού\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Αποσύνδεση Λογαριασμού;\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Αυτή η αλλαγή θα εφαρμοστεί άμεσα. Θέλετε σίγουρα να αποσυνδεθείτε από το λογαριασμό σας;\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Κλειδί Ροής\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Περισσότερες πληροφορίες\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Χρησιμοποιήστε το κλειδί ζωντανής ροής\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Χρήση Κλειδιού Ροής (προχωρημένο)\"\nBasic.AutoConfig.StreamPage.Service=\"Υπηρεσία\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Εμφάνιση Όλων...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Προσαρμοσμένο...\"\nBasic.AutoConfig.StreamPage.Server=\"Διακομιστής\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Κλειδί Ροής\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: Εισαγάγετε τον κωδικό πρόσβασης για την κρυπτογράφηση.\\nRTMP: Εισαγάγετε το κλειδί που παρέχεται από την υπηρεσία.\\nSRT: Εισαγάγετε το αναγνωριστικό ροής εάν η υπηρεσία χρησιμοποιεί ένα.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Κλειδί Κωδικοποιητή\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Μονάδα Φορέα\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Συνδεδεμένος λογαριασμός\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Εκτίμηση ρυθμού μετάδοσης δεδομένων με δοκιμή εύρους ζώνης (ενδέχεται να χρειαστούν μερικά λεπτά)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Προτίμηση κωδικοποίησης υποβοηθούμενης από το υλισμικό\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Η υποβοηθούμενη από το υλισμικό απαιτεί την περισσότερη χρήση της CPU, αλλά μπορεί να χρειαστεί υψηλότερο ρυθμό μετάδοσης bits για την επίτευξη του ίδιου επιπέδου ποιότητας.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Προειδοποίηση Ροής\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Η δοκιμή του εύρους ζώνης πρόκειται να μεταδόσει τυχαιοποιημένα δεδομένα βίντεο χωρίς ήχο, στο κανάλι σας. Εάν μπορείτε, συνιστάται να απενεργοποιήσετε προσωρινά την αποθήκευση των βίντεο ροής σας και να θέσετε τη ροή σας σε ιδιωτική, μέχρι την ολοκλήρωση της δοκιμής. Συνέχεια;\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Δοκιμή %1\"\nBasic.AutoConfig.TestPage=\"Τελικά αποτελέσματα\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Το πρόγραμμα εκτελεί τώρα ένα σύνολο από δοκιμές, για να εκτιμήσει τις πιο ιδανικές ρυθμίσεις\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Οι δοκιμές ολοκληρώθηκαν\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Πραγματοποίηση δοκιμής εύρους ζώνης, αυτό μπορεί να διαρκέσει μερικά λεπτά...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Δε βρέθηκε καμία έξοδος για το πρωτόκολλο αυτής της υπηρεσίας\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Σύνδεση με %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Αδυναμία σύνδεσης με όλους τους διακομιστές, παρακαλούμε ελέγξτε τη σύνδεση σας με το διαδίκτυο και προσπαθήστε ξανά.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Έλεγχος εύρους ζώνης προς: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Δοκιμή κωδικοποιητή ροής, αυτό μπορεί να διαρκέσει λίγο...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Δοκιμή κωδικοποιητή εγγραφής, αυτό μπορεί να διαρκέσει λίγο...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Αποτυχία έναρξης του κωδικοποιητή\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Δοκιμή σε %1x%2 με %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Κωδικοποιητής Ροής\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Κωδικοποιητής Καταγραφής\"\nBasic.AutoConfig.TestPage.Result.Header=\"Το πρόγραμμα έχει καθορίσει πως οι ακόλουθες ρυθμίσεις είναι οι πιο ιδανικές για σας:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Για χρήση αυτών των ρυθμίσεων, κάντε κλικ στο κουμπί Εφαρμογή Ρυθμίσεων. Για να ξαναρυθμίσετε τις παραμέτρους του οδηγού και να προσπαθήσετε ξανά, κάντε κλικ στο κουμπί Πίσω. Για να ρυθμίσετε χειροκίνητα οι ίδιοι τις ρυθμίσεις, κάντε κλικ στο κουμπί Άκυρο και ανοίξτε τις ρυθμίσεις.\"\nBasic.AutoConfig.Info=\"Ο οδηγός αυτόματης ρύθμισης παραμέτρων θα καθορίσει τις ιδανικές ρυθμίσεις για τα χαρακτηριστικά του υπολογιστή σας και την ταχύτητα της σύνδεσής σας.\"\nBasic.AutoConfig.RunAnytime=\"Αυτό μπορεί να τρέξει σε κάθε στιγμή πηγαίνοντας στο μενού εργαλίων.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Ανάλυση ροής (κλιμακωμένη)\"\nBasic.Stats=\"Στατιστικά\"\nBasic.Stats.CPUUsage=\"Χρήση της CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Διαθέσιμος χώρος στο δίσκο\"\nBasic.Stats.MemoryUsage=\"Χρήση Mνήμης\"\nBasic.Stats.AverageTimeToRender=\"Μέσος χρόνος απόδοσης καρέ\"\nBasic.Stats.SkippedFrames=\"Παραλειπόμενα καρέ λόγω καθυστέρησης κωδικοποίησης\"\nBasic.Stats.MissedFrames=\"Χαμένα καρέ λόγω καθυστέρησης απόδοσης\"\nBasic.Stats.Output.Stream=\"Ροή\"\nBasic.Stats.Output.Recording=\"Καταγραφή\"\nBasic.Stats.Status=\"Κατάσταση\"\nBasic.Stats.Status.Recording=\"Γίνεται καταγραφή\"\nBasic.Stats.Status.Live=\"ΖΩΝΤΑΝΑ\"\nBasic.Stats.Status.Reconnecting=\"Επανασύνδεση\"\nBasic.Stats.Status.Inactive=\"Ανενεργό\"\nBasic.Stats.Status.Active=\"Ενεργό\"\nBasic.Stats.DroppedFrames=\"Κομμένα Καρέ (Δίκτυο)\"\nBasic.Stats.MegabytesSent=\"Συνολικά Δεδομένα Εξόδου\"\nBasic.Stats.Bitrate=\"Ρυθμός μετάδοσης bit\"\nBasic.Stats.DiskFullIn=\"Ο δίσκος θα γεμίσει σε (περίπου)\"\nBasic.Stats.DiskFullIn.Text=\"%1 Ώρες, %2 Λεπτά\"\nBasic.Stats.ResetStats=\"Επαναφορά Στατιστικών\"\nResetUIWarning.Title=\"Είστε βέβαιοι ότι θέλετε να επαναφέρετε το UI;\"\nResetUIWarning.Text=\"Επαναφορά του UI θα αποκρύψει επιπλέον docks. Θα πρέπει να καταργήσετε την απόκρυψη αυτών των docks από το μενού Docks, αν θέλετε να είναι ορατές.\\n\\nΕίστε βέβαιοι ότι θέλετε να επαναφέρετε το UI?\"\nUpdater.Title=\"Νέα έκδοση διαθέσιμη\"\nUpdater.Text=\"Υπάρχει διαθέσιμη μια νέα έκδοση:\"\nUpdater.UpdateNow=\"Ενημέρωση Τώρα\"\nUpdater.RemindMeLater=\"Υπενθύμιση αργότερα\"\nUpdater.Skip=\"Παράλειψη έκδοσης\"\nUpdater.NoUpdatesAvailable.Title=\"Δεν υπάρχουν διαθέσιμες ενημερώσεις\"\nUpdater.NoUpdatesAvailable.Text=\"Δεν υπάρχουν διαθέσιμες ενημερώσεις αυτή τη στιγμή\"\nUpdater.BranchNotFound.Title=\"Το Ενημερωμένο Κανάλι Αφαιρέθηκε\"\nUpdater.BranchNotFound.Text=\"Το επιλεγμένο κανάλι ενημερώσεων δεν είναι πλέον διαθέσιμο. Το OBS έχει επαναφερθεί στο προεπιλεγμένο κανάλι.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Έλεγχος ακεραιότητας μη διαθέσιμος\"\nUpdater.RepairButUpdatesAvailable.Text=\"Ο έλεγχος της ακεραιότητας του αρχείου είναι δυνατός μόνο για την τελευταία διαθέσιμη έκδοση. Χρησιμοποιήστε τη Βοήθεια → Ελέγξτε για ενημερώσεις για να επαληθεύσετε και να ενημερώσετε την εγκατάσταση του OBS.\"\nUpdater.RepairConfirm.Title=\"Επιβεβαίωση Ελέγχου Ακεραιότητας\"\nUpdater.RepairConfirm.Text=\"Ξεκινώντας τον έλεγχο ακεραιότητας θα σαρώσετε την εγκατάσταση του OBS σας για φθορά και την επαναλήψη κατεστραμένων/τροποποιημένων αρχείων. Αυτό μπορεί να διαρκέσει λίγο.\\n\\nΘέλετε να συνεχίσετε?\"\nUpdater.FailedToLaunch=\"Αποτυχία εκκίνησης της υπηρεσίας ενημέρωσης\"\nQuickTransitions.SwapScenes=\"Εναλλαγή Σκηνών Προεπισκόπησης/Προγράμματος μετά τη Μετάβαση\"\nQuickTransitions.SwapScenesTT=\"Εναλλάσσει τις σκηνές προεπισκόπησης και προγράμματος μετά τη μετάβαση (αν η αρχική σκηνή του προγράμματος εξακολουθεί να υπάρχει).\\nΑυτό δεν θα αναιρέσει τυχόν αλλαγές που μπορεί να έχουν γίνει στην αρχική σκηνή του προγράμματος.\"\nQuickTransitions.DuplicateScene=\"Διπλότυπο Σκηνής\"\nQuickTransitions.DuplicateSceneTT=\"Κατά την επεξεργασία μιας σκηνής, επιτρέπει την επεξεργασία μετασχηματισμού/ορατότητας των πηγών χωρίς τροποποίηση της εξόδου.\\nΓια να επεξεργαστείτε τις ιδιότητες των πηγών χωρίς να αλλάξετε την έξοδο, ενεργοποιήστε τις \\\"Διπλότυπες Πηγές\\\".\\nΗ αλλαγή αυτής της τιμής θα επαναφέρει την τρέχουσα σκηνή (αν υπάρχει ακόμη).\"\nQuickTransitions.EditProperties=\"Διπλότυπες Πηγές\"\nQuickTransitions.EditPropertiesTT=\"Κατά την επεξεργασία μιας σκηνή, επιτρέπει την επεξεργασία ιδιοτήτων των πηγών χωρίς να τροποποιεί την έξοδο.\\nΧρησιμοποιείται μόνο εάν είναι ενεργοποιημένη η λειτουργία \\\"Διπλότυπες Πηγές\\\".\\nΟρισμένες πηγές (όπως πηγές καταγραφής ή πολυμέσων) δεν το υποστηρίζουν και δεν επεξεργάζονται ξεχωριστά.\\nΗ αλλαγή αυτής της τιμής θα επαναφέρει την τρέχουσα σκηνή εξόδου (αν υπάρχει ακόμη).\\n\\nΠροειδοποίηση: Επειδή οι πηγές θα διπλασιαστούν, μπορεί να χρειαστούν πρόσθετοι πόροι συστήματος ή βίντεο.\"\nQuickTransitions.HotkeyName=\"Γρήγορη Μετάβαση: %1\"\nBasic.AddTransition=\"Προσθήκη Προσαρμόσιμης Μετάβασης\"\nBasic.RemoveTransition=\"Αφαίρεση Προσαρμόσιμης Μετάβασης\"\nBasic.TransitionProperties=\"Ιδιότητες Μετάβασης\"\nBasic.SceneTransitions=\"Μεταβάσεις Σκηνών\"\nBasic.TransitionDuration=\"Διάρκεια\"\nBasic.TogglePreviewProgramMode=\"Λειτουργία στούντιο\"\nBasic.EnablePreviewProgramMode=\"Ενεργοποίηση λειτουργίας Στούντιο\"\nBasic.DisablePreviewProgramMode=\"Απενεργοποίηση λειτουργίας Στούντιο\"\nUndo.Undo=\"Αναίρεση\"\nUndo.Redo=\"Επανάληψη\"\nUndo.Add=\"Προσθήκη '%1'\"\nUndo.Delete=\"Διαγραφή του '%1'\"\nUndo.Rename=\"Μετονομασία '%1'\"\nUndo.SceneCollection.Switch=\"Εναλλαγή σε '%1'\"\nUndo.Item.Undo=\"Αναίρεση %1\"\nUndo.Item.Redo=\"Επανάληψη %1\"\nUndo.Sources.Multi=\"Διαγραφή %1 πηγών\"\nUndo.Filters=\"Φιλτράρισμα αλλαγών στο '%1'\"\nUndo.Filters.Paste.Single=\"Επικόλληση φίλτρου '%1' σε '%2'\"\nUndo.Filters.Paste.Multiple=\"Αντιγραφή Φίλτρων από '%1' σε '%2'\"\nUndo.Transform=\"Μετασχηματισμός πηγής(ών) στο(α) '%1'\"\nUndo.Transform.Paste=\"Επικόλληση μετασχηματισμού στο(α) '%1'\"\nUndo.Transform.Rotate=\"Περιστροφή στο(α) '%1'\"\nUndo.Transform.Reset=\"Μετασχηματισμός πηγής(ών) στο(α) '%1'\"\nUndo.Transform.HFlip=\"Οριζόντια αναστροφή στο '%1'\"\nUndo.Transform.VFlip=\"Κατακόρυφη αναστροφή στο '%1'\"\nUndo.Transform.FitToScreen=\"Προσαρμογή στην οθόνη στο '%1'\"\nUndo.Transform.StretchToScreen=\"Επέκταση στην οθόνη στο '%1'\"\nUndo.Transform.Center=\"Κεντράρισμα στην οθόνη στο '%1'\"\nUndo.Transform.VCenter=\"Κατακόρυφο κέντρο στην οθόνη στο '%1'\"\nUndo.Transform.HCenter=\"Οριζόντιο κέντρο στην οθόνη στο '%1'\"\nUndo.Volume.Change=\"Αλλαγή έντασης στο '%1'\"\nUndo.Volume.Mute=\"Σίγαση '%1'\"\nUndo.Volume.Unmute=\"Διακοπή Σίγασης '%1'\"\nUndo.Balance.Change=\"Αλλαγή Ισορροπήσεως Ήχου στο '%1'\"\nUndo.SyncOffset.Change=\"Αλλαγή Μετατόπισης Συγχρονισμού Ήχου στο '%1'\"\nUndo.MonitoringType.Change=\"Αλλαγή Παρακολουθήσεως Ήχου στο '%1'\"\nUndo.Mixers.Change=\"Αλλαγή Μείκτη Ήχου στο '%1'\"\nUndo.ForceMono.On=\"Ενεργοποίηση Εξαναγκασμού Μονοφωνικού Ήχου στο '%1'\"\nUndo.ForceMono.Off=\"Απενεργοποίηση Εξαναγκασμού Μονοφωνικού Ήχου στο '%1'\"\nUndo.Properties=\"Αλλαγή ιδιότητας στο '%1'\"\nUndo.Scene.Duplicate=\"Διπλότυπο Σκηνής'%1'\"\nUndo.ShowTransition=\"Εμφάνιση μετάβασης στο '%1'\"\nUndo.HideTransition=\"Απόκρυψη μετάβασης στο '%1'\"\nUndo.ShowSceneItem=\"Εμφάνιση του '%1' στο '%2'\"\nUndo.HideSceneItem=\"Απόκρυψη του '%1' στο '%2'\"\nUndo.ReorderSources=\"την πηγή καταγραφής στο %1\"\nUndo.MoveUp=\"Κίνηση του '%1' πάνω στο '%2'\"\nUndo.MoveDown=\"Κίνηση του '%1' κάτω στο '%2'\"\nUndo.MoveToTop=\"Κίνηση του '%1' πάνω στο '%2'\"\nUndo.MoveToBottom=\"Κίνηση του '%1' στο κάτω μέρος στο '%2'\"\nUndo.PasteSource=\"Επικόλληση πηγής/ών στο '%1'\"\nUndo.PasteSourceRef=\"Επικόλληση Αναφοράς/ών Πηγαίου Κώδικα στο '%1'\"\nUndo.GroupItems=\"Ομαδοποίηση αντικειμένων στο '%1'\"\nTransitionNameDlg.Text=\"Παρακαλώ εισάγετε το όνομα της μετάβασης\"\nTransitionNameDlg.Title=\"Όνομα Μετάβασης\"\nTitleBar.SafeMode=\"ΑΣΦΑΛΉΣ ΛΕΙΤΟΥΡΓΊΑ\"\nTitleBar.PortableMode=\"Φορητή Λειτουργία\"\nTitleBar.Profile=\"Προφίλ\"\nTitleBar.Scenes=\"Σκηνές\"\nNameExists.Title=\"Το όνομα υπάρχει ήδη\"\nNameExists.Text=\"Το όνομα χρησιμοποιείται ήδη.\"\nNoNameEntered.Title=\"Πληκτρολογήστε ένα έγκυρο όνομα\"\nNoNameEntered.Text=\"Δεν μπορείτε να χρησιμοποιήσετε κενά ονόματα.\"\nConfirmStart.Title=\"Εκκίνηση Ροής (Steam);\"\nConfirmStart.Text=\"Είστε σίγουροι οτι θέλετε να ξεκινήσετε τη ροή;\"\nConfirmStop.Title=\"Τερματισμός Ροής;\"\nConfirmStop.Text=\"Είστε σίγουροι οτι θέλετε να διακόψετε τη ροή;\"\nConfirmStopRecord.Title=\"Διακοπή εγγραφής?\"\nConfirmStopRecord.Text=\"Είστε σίγουροι ότι θέλετε να διακόψετε την εγγραφή;\"\nConfirmBWTest.Title=\"Έναρξη δοκιμής εύρους ζώνης;\"\nConfirmBWTest.Text=\"Έχετε ρυθμιστεί το OBS σε λειτουργία δοκιμής εύρους ζώνης. Αυτή η λειτουργία επιτρέπει τον έλεγχο δικτύου χωρίς να αρχίσετε ζωντανή μετάδοση. Μόλις τελειώσετε τη δοκιμή, θα πρέπει να την απενεργοποιήσετε για να μπορούν οι θεατές να δουν τη μετάδοση σας.\\n\\nΘέλετε να συνεχίσετε?\"\nConfirmRemove.Title=\"Επιβεβαίωση Κατάργησης\"\nConfirmRemove.Text=\"Είστε βέβαιοι ότι θέλετε να αφαιρέσετε το '%1';\"\nConfirmRemove.TextMultiple=\"Είστε βέβαιοι ότι θέλετε να καταργήσετε %1 επιλογές;\"\nConfirmReset.Title=\"Επαναφορά Ιδιοτήτων\"\nConfirmReset.Text=\"Θέλετε πράγματι να επαναφέρετε τις τρέχουσες ιδιότητες στις προεπιλογές τους;\"\nOutput.StartStreamFailed=\"Αποτυχία εκκίνησης της ροής\"\nOutput.StartRecordingFailed=\"Αποτυχία εκκίνησης της καταγραφής\"\nOutput.StartReplayFailed=\"Αποτυχία εκκίνησης της προσωρινής μνήμης των επαναλήψεων\"\nOutput.StartVirtualCamFailed=\"Απέτυχε η εκκίνηση της εικονικής κάμερας\"\nOutput.StartFailedGeneric=\"Αποτυχία εκκίνησης εξόδου. Παρακαλούμε ελέγξτε το αρχείο καταγραφής για λεπτομέρειες.\\n\\nΣημείωση: Εάν χρησιμοποιείτε τους κωδικοποιητές NVENC ή AMD, βεβαιωθείτε ότι τα προγράμματα οδήγησης της κάρτας γραφικών σας είναι ενημερωμένα.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Δεν είναι δυνατή η αποθήκευση των επαναλήψεων κατά την παύση\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Προειδοποίηση: Οι επαναλύψεις δεν μπορούν να σωθούν όταν η καταγραφή είναι σε παύση.\"\nOutput.ConnectFail.Title=\"Η σύνδεση απέτυχε\"\nOutput.ConnectFail.BadPath=\"Μη έγκυρη Διαδρομή ή URL Σύνδεσης. Παρακαλώ ελέγξτε τις ρυθμίσεις σας και επιβεβαιώστε ότι είναι έγκυρες.\"\nOutput.ConnectFail.ConnectFailed=\"Απέτυχε η σύνδεση στον διακομιστή\"\nOutput.ConnectFail.InvalidStream=\"Δεν μπορέσαμε να αποκτήσουμε πρόσβαση στο συγκεκριμένο κανάλι ή κλειδί ροής, παρακαλούμε ελέγξτε ξανά το κλειδί ροής σας. Εάν είναι σωστό, μπορεί να υπάρχει πρόβλημα στη σύνδεση με τον διακομιστή.\"\nOutput.ConnectFail.HdrDisabled=\"Η έξοδος HDR είναι προς το παρόν απενεργοποιημένη για αυτή την έξοδο.\"\nOutput.ConnectFail.Error=\"Παρουσιάστηκε μη αναμενόμενο σφάλμα κατά την προσπάθεια σύνδεσης με τον διακομιστή. Περισσότερες πληροφορίες στο αρχείο καταγραφής.\"\nOutput.ConnectFail.Disconnected=\"Αποσυνδεθήκατε από τον διακομιστή.\"\nOutput.StreamEncodeError.Title=\"Σφάλμα κωδικοποίησης\"\nOutput.StreamEncodeError.Msg=\"Ένα πρόβλημα κωδικοποίησης συνέβει κατά τη ζωντανή ροή.\"\nOutput.StreamEncodeError.Msg.LastError=\"Παρουσιάστηκε σφάλμα κωδικοποιητή κατά τη ροή:<br><br>%1\"\nOutput.RecordFail.Title=\"Αποτυχία εκκίνησης της καταγραφής\"\nOutput.RecordFail.Unsupported=\"Η μορφή εξόδου είτε δεν υποστηρίζεται ή δεν υποστηρίζει παραπάνω από ένα κομμάτι ήχου. Παρακαλώ ελέγξτε τις ρυθμίσεις σας και δοκιμάστε ξανά.\"\nOutput.RecordNoSpace.Title=\"Ανεπαρκής χώρος στον δίσκο\"\nOutput.RecordNoSpace.Msg=\"Δεν υπάρχει επαρκής χώρος στο δίσκο για να συνεχιστεί η καταγραφή.\"\nOutput.RecordError.Title=\"Σφάλμα καταγραφής\"\nOutput.RecordError.Msg=\"Παρουσιάστηκε ένα αδιευκρίνιστο σφάλμα κατά την καταγραφή.\"\nOutput.RecordError.EncodeErrorMsg=\"Ένα πρόβλημα κωδικοποίησης συνέβει κατά τη καταγραφή.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Παρουσιάστηκε σφάλμα κωδικοποιητή κατά την εγγραφή:<br><br>%1\"\nOutput.BadPath.Title=\"Λάθος Διαδρομή Αρχείου\"\nOutput.BadPath.Text=\"Η ορισμένη διαδρομή καταγραφής δεν μπόρεσε να βρεθεί. Ελέγξτε την πηγαίνοντας στις Ρυθμίσεις → Έξοδος → Καταγραφή.\"\nOutput.NoBroadcast.Title=\"Καμία ρυθμισμένη μετάδοση\"\nOutput.NoBroadcast.Text=\"Πρέπει να ορίσετε μια αναμετάδοση προτού ξεκινήσετε την ροή.\"\nOutput.BroadcastStartFailed=\"Αδυναμία εκκίνησης της αναμετάδοσης\"\nOutput.BroadcastStopFailed=\"Αδυναμία τερματισμού της αναμετάδοσης\"\nLogUploadDialog.Labels.PrivacyNotice=\"Παρακαλούμε διαβάστε την <a href='https://obsproject.com/privacy-policy'>Πολιτική Απορρήτου</a> και την ενότητα σχετικά με τη μεταφόρτωση αρχείων πριν αποστείλετε οποιαδήποτε αρχεία.\"\nLogUploadDialog.Labels.Progress=\"Ανέβασμα καταγραφής σε εξέλιξη. Παρακαλούμε περιμένετε...\"\nLogUploadDialog.Labels.Description.AppLog=\"Το αρχείο καταγραφής σας έχει ανέβει. Μπορείτε τώρα να μοιραστείτε το σύνδεσμο για σκοπούς αποσφαλμάτωσης και υποστήριξης.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Μεταφόρτωση\"\nLogUploadDialog.Buttons.CopyURL=\"Αντιγραφή URL καταγραφής\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Ανάλυση αρχείου καταγραφής\"\nLogUploadDialog.Buttons.RetryButton=\"Προσπάθεια ξανά\"\nLogUploadDialog.Errors.Template=\"Προέκυψε σφάλμα κατά το ανέβασμα του αρχείου:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Δε βρέθηκε αρχείο για ανέβασμα ή το αρχείο ήταν κενό.\"\nRemux.SourceFile=\"OBS Καταγραφή\"\nRemux.TargetFile=\"Αρχείο Προορισμού\"\nRemux.Remux=\"Μετατροπή\"\nRemux.Stop=\"Διακοπή ανακατασκευής\"\nRemux.ClearFinished=\"Εκκαθάριση Ολοκληρωμένων Στοιχείων\"\nRemux.ClearAll=\"Εκκαθάριση Όλων\"\nRemux.OBSRecording=\"Καταγραφή OBS\"\nRemux.FinishedTitle=\"Η μετατροπή ολοκληρώθηκε\"\nRemux.Finished=\"Η καταγραφή μετατράπηκε\"\nRemux.FinishedError=\"Η καταγραφή μετατράπηκε, άλλα ενδέχεται το αρχείο να είναι ελλιπές\"\nRemux.SelectRecording=\"Επιλέξτε καταγραφή OBS...\"\nRemux.SelectTarget=\"Επιλέξτε το αρχείο προορισμού...\"\nRemux.FileExistsTitle=\"Τα αρχεία προορισμού υπάρχουν\"\nRemux.FileExists=\"Τα παρακάτω αρχεία προορισμού υπάρχουν ήδη. Θέλετε να τα αντικαταστήσετε;\"\nRemux.ExitUnfinishedTitle=\"Μετατροπή σε εξέλιξη\"\nRemux.ExitUnfinished=\"Η Μετατροπή δεν τελείωσε, η διακοπή τώρα μπορεί να καταστήσει το αρχείο προορισμού άχρηστο.\\nΕίστε βέβαιοι ότι θέλετε να τερματίσετε την μετατροπή;\"\nRemux.HelpText=\"Αποθέστε τα αρχεία σε αυτό το παράθυρο για να τα μετατρέψετε, ή επιλέξτε ένα άδειο πεδίο \\\"Kαταγραφή OBS\\\" για αναζήτηση αρχείου.\"\nRemux.NoFilesAddedTitle=\"Δεν προστέθηκε αρχείο επανεμφανισμό.\"\nRemux.NoFilesAdded=\"Κανένα αρχείο δεν προστέθηκε για επανεμφανισμό. Αποθέστε ένα φάκελο που περιέχει ένα ή περισσότερα αρχεία βίντεο.\"\nMissingFiles=\"Αρχεία που λείπουν\"\nMissingFiles.MissingFile=\"Αρχείο που λείπει\"\nMissingFiles.NewFile=\"Νέο αρχείο\"\nMissingFiles.HelpText=\"Ορισμένα αρχεία λείπουν από την τελευταία χρήση του OBS.\"\nMissingFiles.Clear=\"<διαγράφηκε>\"\nMissingFiles.NumFound=\"Βρέθηκαν %1 από %2\"\nMissingFiles.Search=\"Αναζήτηση φακέλου...\"\nMissingFiles.SelectFile=\"Επιλογή αρχείου...\"\nMissingFiles.SelectDir=\"Επιλέξτε φάκελο για αναζήτηση\"\nMissingFiles.State=\"Κατάσταση\"\nMissingFiles.Missing=\"Λείπει\"\nMissingFiles.Replaced=\"Αντικαταστάθηκε\"\nMissingFiles.Cleared=\"Εκκαθαρίστηκε\"\nMissingFiles.Found=\"Βρέθηκαν\"\nMissingFiles.AutoSearch=\"Βρέθηκαν επιπλέον αντιστοιχίες αρχείου\"\nMissingFiles.AutoSearchText=\"Το OBS βρήκε επιπλέον αντιστοιχίες για τα αρχεία που λείπουν σε αυτόν τον κατάλογο. Θα θέλατε να τα προσθέσετε;\"\nMissingFiles.NoMissing.Title=\"Έλεγχος για αρχεία που λείπουν\"\nMissingFiles.NoMissing.Text=\"Δεν φαίνεται να λείπουν αρχεία.\"\nMacPermissions.MenuAction=\"Έλεγχος Δικαιωμάτων Εφαρμογής...\"\nMacPermissions.Title=\"Έλεγχος Δικαιωμάτων Εφαρμογής\"\nMacPermissions.Description=\"Το OBS Studio απαιτεί την άδειά σας για την παροχή συγκεκριμένων λειτουργιών. Συνιστάται να ενεργοποιήσετε αυτά τα δικαιώματα, αλλά δεν απαιτούνται για τη χρήση της εφαρμογής. Μπορείτε πάντα να τα ενεργοποιήσετε αργότερα.\"\nMacPermissions.Description.OpenDialog=\"Μπορείτε να ανοίξετε ξανά αυτόν τον διάλογο μέσω του μενού του OBS Studio.\"\nMacPermissions.AccessGranted=\"Επιτρέπεται η πρόσβαση\"\nMacPermissions.RequestAccess=\"Αίτημα Πρόσβασης\"\nMacPermissions.OpenPreferences=\"Άνοιγμα %1 Επιλογών\"\nMacPermissions.Item.ScreenRecording=\"Καταγραφή Οθόνης\"\nMacPermissions.Item.ScreenRecording.Details=\"Το OBS απαιτεί αυτήν την άδεια για τη σύλληψη της οθόνης σας.\"\nMacPermissions.Item.Camera=\"Κάμερα\"\nMacPermissions.Item.Camera.Details=\"Αυτή η άδεια είναι απαραίτητη για τη σύλληψη περιεχομένου από μια κάμερα ή μια κάρτα καταγραφής.\"\nMacPermissions.Item.Microphone=\"Μικρόφωνο\"\nMacPermissions.Item.Microphone.Details=\"Το OBS χρειάζεται αυτήν την άδεια αν θέλετε να καταγράψετε το μικρόφωνό σας ή κάποια εξωτερική συσκευή.\"\nMacPermissions.Item.InputMonitoring=\"Παρακολούθηση Εισόδου\"\nMacPermissions.Item.InputMonitoring.Details=\"Αυτή η άδεια απαιτείται ώστε οι συντομεύσεις πληκτρολογίου να λειτουργούν ενώ το OBS εκτελείται στο παρασκήνιο.\"\nMacPermissions.Item.Accessibility=\"Προσβασιμότητα\"\nMacPermissions.Item.Accessibility.Details=\"Σε παλαιότερες εγκαταστάσεις, το OBS ενδέχεται να εμφανίζεται στην «Προσβασιμότητα» αντί για την «Παρακολούθηση εισόδου».\"\nMacPermissions.Continue=\"Συνέχεια\"\nSourceLeak.Title=\"Σφάλμα Καθαρισμού Πηγής\"\nSourceLeak.Text=\"Παρουσιάστηκε πρόβλημα κατά την αλλαγή των συλλογών σκηνών και κάποιες πηγές δεν μπόρεσαν να αποφορτωθούν. Αυτό το πρόβλημα τυπικά προκαλείται από πρόσθετα τα οποία δεν απελευθερώνουν πόρους κατάλληλα. Παρακαλώ βεβαιωθείτε ότι τα πρόσθετα που χρησιμοποιείται είναι ενημερωμένα.\\n\\nΤο OBS Studio θα εξέλθει τώρα για να αποτρέψει πιθανή καταστροφή δεδομένων.\"\nBasic.DesktopDevice1=\"Ήχος Επιφάνειας Εργασίας\"\nBasic.DesktopDevice2=\"Ήχος Επιφάνειας Εργασίας 2\"\nBasic.AuxDevice1=\"Μικρόφωνο/Aux\"\nBasic.AuxDevice2=\"Μικρόφωνο/Aux 2\"\nBasic.AuxDevice3=\"Μικρόφωνο/Aux 3\"\nBasic.AuxDevice4=\"Μικρόφωνο/Aux 4\"\nBasic.Scene=\"Σκηνή\"\nBasic.DisplayCapture=\"Αποτύπωση Οθόνης\"\nBasic.Main.PreviewConextMenu.Enable=\"Ενεργοποίηση Προεπισκόπησης\"\nBasic.Main.Preview.Disable=\"Απενεργοποίηση προεπισκόπησης\"\nScaleFiltering=\"Κλίμακα Φίλτρου\"\nScaleFiltering.Area=\"Περιοχή\"\nBlendingMethod=\"Μέθοδος ανάμειξης\"\nBlendingMethod.Default=\"Προεπιλογή\"\nBlendingMethod.SrgbOff=\"SRGB ανενεργό\"\nBlendingMode=\"Λειτουργία ανάμειξης\"\nBlendingMode.Normal=\"Κανονική\"\nBlendingMode.Additive=\"Προσθήκη\"\nBlendingMode.Subtract=\"Αφαίρεση\"\nBlendingMode.Screen=\"Οθόνη\"\nBlendingMode.Multiply=\"Πολλαπλασιασμός\"\nBlendingMode.Lighten=\"Φωτισμός\"\nBlendingMode.Darken=\"Σκοτείνιασμα\"\nDeinterlacing=\"Απόπλεξη\"\nDeinterlacing.Discard=\"Απόρριψη\"\nDeinterlacing.TopFieldFirst=\"Επάνω Πεδίο Πρώτα\"\nDeinterlacing.BottomFieldFirst=\"Κάτω Πεδίο Πρώτα\"\nVolControl.SliderUnmuted=\"Ρυθμιστής έντασης για '%1':\"\nVolControl.SliderMuted=\"Ρυθμιστής έντασης για '%1': (αυτή τη στιγμή σε σίγαση)\"\nVolControl.Mute=\"Σίγαση '%1'\"\nVolControl.Properties=\"Ιδιότητες για '%1'\"\nVolControl.UnassignedWarning.Title=\"Μη Ορισμένη Πηγή Ήχου\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" δε έχει ανατεθεί σε κανένα κομμάτι ήχου και δε θα είναι ακουστό σε ροές ή εγγραφές.\\n\\nΓια να αναθέσετε μια πηγή ήχου σε ένα κομμάτι, ανοίξτε τις Προηγμένες Ιδιότητες Ήχου μέσω του μενού δεξιού κλικ ή του κουμπιού τροχαίας στην εργαλειοθήκη του μικτή.\"\nBasic.Main.AddSceneDlg.Title=\"Προσθήκη Σκηνής\"\nBasic.Main.AddSceneDlg.Text=\"Παρακαλώ εισάγετε το όνομα της σκηνής\"\nBasic.Main.DefaultSceneName.Text=\"Σκηνή %1\"\nBasic.Main.AddSceneCollection.Title=\"Προσθήκη Συλλογής Σκηνών\"\nBasic.Main.AddSceneCollection.Text=\"Παρακαλώ εισάγετε το όνομα της συλλογής σκηνών\"\nBasic.Main.RenameSceneCollection.Title=\"Μετονομασία Συλλογής Σκηνών\"\nBasic.Main.RemigrateSceneCollection.Title=\"Ενημέρωση ανάλυσης συλλογής σκηνών\"\nBasic.Main.RemigrateSceneCollection.Text=\"Θέλετε να ενημερώσετε την ανάλυση της συλλογής σκηνών «%1» ώστε να ταιριάζει με την ανάλυση καμβά του τρέχοντος προφίλ %2x%3;\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Αποτυχία ενημέρωσης της ανάλυσης συλλογής σκηνών. Η αρχική ανάλυση είναι άγνωστη.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Δεν είναι δυνατή η επαναφορά: Η αλλαγή ανάλυσης του OBS απέτυχε.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Δεν είναι δυνατή η επαναφορά: Η τρέχουσα ανάλυση είναι ήδη η βασική ανάλυση της συλλογής σκηνών.\"\nAddProfile.Title=\"Προσθήκη Προφίλ\"\nAddProfile.Text=\"Παρακαλώ εισάγετε το όνομα του προφίλ\"\nAddProfile.WizardCheckbox=\"Εμφάνιση οδηγού αυτόματης ρύθμισης\"\nRenameProfile.Title=\"Μετονομασία Προφίλ\"\nBasic.Main.MixerRename.Title=\"Μετονομασία της πηγής ήχου\"\nBasic.Main.MixerRename.Text=\"Παρακαλώ εισάγετε το όνομα της πηγής ήχου\"\nBasic.Main.PreviewDisabled=\"Η προεπισκόπηση είναι απενεργοποιημένη\"\nBasic.SourceSelect=\"Δημιουργία/Επιλογή Πηγής\"\nBasic.SourceSelect.CreateNew=\"Δημιουργία νέας\"\nBasic.SourceSelect.AddExisting=\"Προσθήκη Υπάρχουσας\"\nBasic.SourceSelect.AddVisible=\"Να γίνει η πηγή ορατή\"\nBasic.Main.Sources.Visibility=\"Ορατότητα\"\nBasic.Main.Sources.VisibilityDescription=\"Ελέγχει την ορατότητα του '%1' στον καμβά\"\nBasic.Main.Sources.Lock=\"Κλείδωμα\"\nBasic.Main.Sources.LockDescription=\"Κλειδώνει τη θέση και την κλίμακα του '%1' στον καμβά\"\nBasic.PropertiesWindow=\"Ιδιότητες για '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (αυτόματη επιλογή: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Επιλέξτε χρώμα\"\nBasic.PropertiesWindow.SelectFont=\"Επιλέξτε γραμματοσειρά\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Επιλέξτε μια γραμματοσειρά\"\nBasic.PropertiesWindow.ConfirmTitle=\"Άλλαξαν οι Ρυθμίσεις\"\nBasic.PropertiesWindow.Confirm=\"Υπάρχουν μη αποθηκευμένες αλλαγές. Θέλετε να τις κρατήσετε;\"\nBasic.PropertiesWindow.NoProperties=\"Δεν υπάρχουν διαθέσιμες ιδιότητες\"\nBasic.PropertiesWindow.AddFiles=\"Προσθήκη Αρχείων\"\nBasic.PropertiesWindow.AddDir=\"Προσθήκη Φακέλου\"\nBasic.PropertiesWindow.AddURL=\"Προσθήκη Διαδρομής/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Προσθήκη φακέλου στο '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Προσθήκη αρχείων στο '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Προσθήκη καταχώρησης στο '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Επεξεργασία καταχώρησης από '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Ακέραιες Τιμές FPS\"\nBasic.PropertiesView.FPS.Rational=\"Κλασματικές Τιμές FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Έγκυρο εύρος τιμών FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"Ανοίξτε αυτόν τον σύνδεσμο στον προεπιλεγμένο περιηγητή σας?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"Σύνδεσμος%1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Άνοιγμα URL\"\nBasic.InteractionWindow=\"Αλληλεπίδραση με '%1'\"\nBasic.StatusBar.Reconnecting=\"Έγινε αποσύνδεση, επανασύνδεση σε %2 δευτερόλεπτα (προσπάθεια %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Γίνεται προσπάθεια επανασύνδεσης... (προσπάθεια %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Επανασύνδεση επιτυχής\"\nBasic.StatusBar.Delay=\"Καθυστέρηση (%1 δευτερόλεπτα)\"\nBasic.StatusBar.DelayStartingIn=\"Καθυστέρηση (έναρξη σε %1 δευτερόλεπτα)\"\nBasic.StatusBar.DelayStoppingIn=\"Καθυστέρηση (διακοπή σε %1 δευτερόλεπτα)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Καθυστέρηση (διακοπή σε %1 δευτερόλεπτα, έναρξη σε %2 δευτερόλεπτα)\"\nBasic.StatusBar.RecordingSavedTo=\"Η εγγραφή αποθηκεύτηκε στο: '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Αναπαραγωγή αποθηκευμένου buffer στο '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Το στιγμιότυπο αποθηκεύτηκε στο '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Αυτόματη μετατροπή εγγραφής σε '%1'\"\nBasic.Filters=\"Φίλτρα\"\nBasic.Filters.AsyncFilters=\"Φίλτρα Ήχου/Βίντεο\"\nBasic.Filters.AudioFilters=\"Φίλτρα Ήχου\"\nBasic.Filters.EffectFilters=\"Φίλτρα Εφέ\"\nBasic.Filters.Title=\"Φίλτρα για '%1'\"\nBasic.Filters.AddFilter.Title=\"Όνομα φίλτρου\"\nBasic.Filters.AddFilter.Text=\"Παρακαλώ καθορίστε το όνομα του φίλτρου\"\nBasic.TransformWindow=\"Μετασχηματισμός Στοιχείου Σκηνής\"\nBasic.TransformWindow.Position=\"Τοποθεσία\"\nBasic.TransformWindow.PositionX=\"Τοποθεσία Χ\"\nBasic.TransformWindow.PositionY=\"Τοποθεσία Υ\"\nBasic.TransformWindow.Rotation=\"Περιστροφή\"\nBasic.TransformWindow.Size=\"Μέγεθος\"\nBasic.TransformWindow.Width=\"Πλάτος\"\nBasic.TransformWindow.Height=\"Ύψος\"\nBasic.TransformWindow.Alignment=\"Ευθυγράμμιση Τοποθεσίας\"\nBasic.TransformWindow.BoundsType=\"Τύπος Πλαισίου Οριοθέτησης\"\nBasic.TransformWindow.BoundsAlignment=\"Ευθυγράμμιση στο Πλαίσιο Οριοθέτησης\"\nBasic.TransformWindow.Bounds=\"Μέγεθος Πλαισίου Οριοθέτησης\"\nBasic.TransformWindow.BoundsWidth=\"Πλάτος Πλαισίου Οριοθέτησης\"\nBasic.TransformWindow.BoundsHeight=\"Ύψος Πλαισίου Οριοθέτησης\"\nBasic.TransformWindow.CropToBounds=\"Περικοπή στο πλαίσιο οριοθέτησης\"\nBasic.TransformWindow.Crop=\"Περικοπή\"\nBasic.TransformWindow.CropLeft=\"Περικοπή αριστερά\"\nBasic.TransformWindow.CropRight=\"Περικοπή δεξιά\"\nBasic.TransformWindow.CropTop=\"Περικοπή πάνω\"\nBasic.TransformWindow.CropBottom=\"Περικοπή κάτω\"\nBasic.TransformWindow.Alignment.TopLeft=\"Πάνω Αριστερά\"\nBasic.TransformWindow.Alignment.TopCenter=\"Πάνω Κέντρο\"\nBasic.TransformWindow.Alignment.TopRight=\"Πάνω Δεξιά\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Κέντρο Αριστερά\"\nBasic.TransformWindow.Alignment.Center=\"Κέντρο\"\nBasic.TransformWindow.Alignment.CenterRight=\"Κέντρο Δεξιά\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Κάτω Αριστερά\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Κάτω Κέντρο\"\nBasic.TransformWindow.Alignment.BottomRight=\"Κάτω Δεξιά\"\nBasic.TransformWindow.BoundsType.None=\"Χωρίς Όρια\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Μέγιστο μέγεθος μόνο\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Κλιμάκωση στα εσωτερικά όρια\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Κλιμάκωση στα εξωτερικά όρια\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Κλιμάκωση στο πλάτος των ορίων\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Κλιμάκωση στο ύψος των ορίων\"\nBasic.TransformWindow.BoundsType.Stretch=\"Κλιμάκωση στα όρια\"\nBasic.TransformWindow.Title=\"Επεξεργασία μετασχηματισμού για '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Δεν επιλέχθηκε πηγή\"\nBasic.Main.AddSourceHelp.Title=\"Αδύνατη Προσθήκη Πηγής\"\nBasic.Main.AddSourceHelp.Text=\"Πρέπει να έχετε τουλάχιστον 1 σκηνή για να προσθέσετε μια πηγή.\"\nBasic.Main.Scenes=\"Σκηνές\"\nBasic.Main.Sources=\"Πηγές\"\nBasic.Main.Source=\"Πηγή\"\nBasic.Main.Controls=\"Πλήκτρα ελέγχου\"\nBasic.Main.PreparingStream=\"Προετοιμάζεται...\"\nBasic.Main.Connecting=\"Σύνδεση...\"\nBasic.Main.StartRecording=\"Έναρξη Καταγραφής\"\nBasic.Main.StartReplayBuffer=\"Έναρξη επανάληψης προσωρινής μνήμης\"\nBasic.Main.SaveReplay=\"Αποθήκευση επανάληψης\"\nBasic.Main.StartStreaming=\"Έναρξη Ροής\"\nBasic.Main.StartBroadcast=\"Έναρξη μετάδοσης\"\nBasic.Main.StartVirtualCam=\"Έναρξη Εικονικής Κάμερας\"\nBasic.Main.StopRecording=\"Διακοπή Καταγραφής\"\nBasic.Main.PauseRecording=\"Παύση Εγγραφής\"\nBasic.Main.UnpauseRecording=\"Επανέναρξη Εγγραφής\"\nBasic.Main.SplitFile=\"Διαχωρισμός αρχείου εγγραφής\"\nBasic.Main.AddChapterMarker=\"Προσθήκη δείκτη κεφαλαίου (μόνο για υβριδικό MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Διακοπή Καταγραφής...\"\nBasic.Main.StopReplayBuffer=\"Διακοπή Επανάληψης Προσωρινής Μνήμης\"\nBasic.Main.StoppingReplayBuffer=\"Διακοπή Επανάληψης Προσωρινής Μνήμης...\"\nBasic.Main.SetupBroadcast=\"Διαχείριση μετάδοσης\"\nBasic.Main.StopStreaming=\"Διακοπή Ροής\"\nBasic.Main.StopBroadcast=\"Τερματισμός μετάδοσης\"\nBasic.Main.AutoStopEnabled=\"(Αυτόματη διακοπή)\"\nBasic.Main.StoppingStreaming=\"Διακοπή Ροής...\"\nBasic.Main.ForceStopStreaming=\"Διακοπή Ροής(απόρριψη καθυστέρησης)\"\nBasic.Main.ShowContextBar=\"Εμφάνιση γραμμής εργαλείων πηγής\"\nBasic.Main.HideContextBar=\"Απόκρυψη γραμμής εργαλείων πηγής\"\nBasic.Main.StopVirtualCam=\"Διακοπή Εικονικής Κάμερας\"\nBasic.Main.Group=\"Ομάδα %1\"\nBasic.Main.GroupItems=\"Ομάδα Επιλεγμένων Στοιχείων\"\nBasic.Main.Ungroup=\"Κατάργηση Ομάδας\"\nBasic.Main.GridMode=\"Πλαίσια Πλέγματος\"\nBasic.Main.ListMode=\"Λειτουργία Λίστας\"\nBasic.Main.VirtualCamConfig=\"Ρύθμιση Εικονικής Κάμερας\"\nBasic.VCam.VirtualCamera=\"Εικονική Κάμερα\"\nBasic.VCam.OutputType=\"Τύπος Εξόδου\"\nBasic.VCam.OutputSelection=\"Επιλογή Εξόδου\"\nBasic.VCam.OutputType.Program=\"Πρόγραμμα (Προεπιλογή)\"\nBasic.VCam.OutputSelection.NoSelection=\"Καμία επιλογή για αυτό τον τύπο εξόδου\"\nBasic.VCam.RestartWarning=\"Η εικονική κάμερα θα επανεκκινηθεί για να εφαρμοστεί αυτή η αλλαγή\"\nBasic.MainMenu.File=\"Αρχείο(&F)\"\nBasic.MainMenu.File.Export=\"Εξαγωγή(&E)\"\nBasic.MainMenu.File.Import=\"Εισαγωγή(Ι) (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Εμφάνιση Καταγραφών(&R)\"\nBasic.MainMenu.File.Remux=\"Μετατροπή Καταγραφών(&M)\"\nBasic.MainMenu.File.Settings=\"Ρυθμίσεις(&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Προβολή Φακέλου Ρυθμίσεων\"\nBasic.MainMenu.File.ShowProfileFolder=\"Προβολή Φακέλου Προφίλ\"\nBasic.MainMenu.File.ShowMissingFiles=\"Έλεγχος για αρχεία που λείπουν\"\nBasic.MainMenu.File.Exit=\"Έξοδος(&X)\"\nBasic.MainMenu.Edit=\"Επεξεργασία(&E)\"\nBasic.MainMenu.Edit.Undo=\"Αναίρεση(&U)\"\nBasic.MainMenu.Edit.Redo=\"Ακύρωση Αναίρεσης(&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Κλείδωμα Προεπισκόπησης(&L)\"\nBasic.MainMenu.Edit.Scale=\"Προεπισκόπηση Κλιμάκωσης(&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"Κλιμάκωση σε Παράθυρο\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Καμβάς (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Έξοδος (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Σε κλίμακα (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Μεγέθυνση\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Σμίκρυνση\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Επαναφορά Εστίασης\"\nBasic.MainMenu.Edit.Transform=\"Μετασχηματισμός(&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Επεξεργασία Μετασχηματισμού(&E)...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Αντιγραφή Μετασχηματισμού\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Επικόλληση Μετασχηματισμού\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Επαναφορά Μετασχηματισμού(&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Περιστροφή κατά 90 μοίρες CW\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Περιστροφή κατά 90 μοίρες CCW\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Περιστροφή 180 μοίρες\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Οριζόντια Αναστροφή(&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Κατακόρυφη Αναστροφή(&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Προσαρμογή στην οθόνη(&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Έκταση στην οθόνη(&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Κεντράρισμα στην οθόνη(&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Κέντρο Κάθετα\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Κέντρο Οριζόντια\"\nBasic.MainMenu.Edit.Order=\"Σειρά(&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Μετακίνηση Πάνω(&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Μετακίνηση Κάτω(&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Μετακίνηση στην Κορυφή(&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Μετακίνηση στη Βάση(&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Ιδιότητες Ήχου για Προχωρημένους(&A)\"\nBasic.MainMenu.View=\"Προβολή (&V)\"\nBasic.MainMenu.View.Toolbars=\"Γραμμές εργαλείων (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"Προσάρτηση γραμμών εργαλείων\"\nBasic.MainMenu.View.ContextBar=\"Γραμμή εργαλείων πηγής\"\nBasic.MainMenu.View.SourceIcons=\"Πηγές Εικονίδια\"\nBasic.MainMenu.View.StatusBar=\"Γραμμή κατάστασης(&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Διεπαφή Πλήρους Οθόνης\"\nBasic.MainMenu.View.ResetUI=\"&Επαναφορά διεπαφής χρήστη\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Πάντα στην Κορυφή\"\nBasic.MainMenu.View.SceneListMode=\"Λειτουργία Λίστας Σκηνών\"\nBasic.MainMenu.Docks=\"&Στοιχιζόμενα παράθυρα\"\nBasic.MainMenu.Docks.ResetDocks=\"&Επαναφορά στοιχιζόμενων παραθύρων\"\nBasic.MainMenu.Docks.LockDocks=\"&Κλείδωμα στοιχιζόμενων παραθύρων\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Προσαρμοσμένα Docks Προγράμματος Περιήγησης...\"\nBasic.MainMenu.Import=\"Εισαγωγή...\"\nBasic.MainMenu.Export=\"Εξαγωγή...\"\nBasic.MainMenu.SceneCollection=\"Συλλογή Σκηνών(&S)\"\nBasic.MainMenu.Profile=\"Προφίλ(&P)\"\nBasic.MainMenu.Profile.Import=\"Εισαγωγή Προφίλ\"\nBasic.MainMenu.Profile.Export=\"Εξαγωγή Προφίλ\"\nBasic.MainMenu.SceneCollection.Import=\"Εισαγωγή Συλλογής Σκηνών\"\nBasic.MainMenu.SceneCollection.Export=\"Εξαγωγή Συλλογής Σκηνών\"\nBasic.MainMenu.Profile.Exists=\"Το προφίλ υπάρχει ήδη\"\nBasic.MainMenu.Tools=\"Εργαλεία(&T)\"\nBasic.MainMenu.Help=\"Βοήθεια(&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Πύλη Βοήθειας(&P)\"\nBasic.MainMenu.Help.Website=\"Μετάβαση στην Ιστοσελίδα(&W)\"\nBasic.MainMenu.Help.Discord=\"Βρείτε μας στο &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Τι Νέο Υπάρχει\"\nBasic.MainMenu.Help.Logs=\"Αρχεία Καταγραφής(&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Εμφάνιση Αρχείων Καταγραφής(&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Μεταφορτώστε το Τρέχον Αρχείο Καταγραφής(&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Μεταφόρτωση &Προηγούμενου Αρχείου Καταγραφής\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Προβολή Τρέχοντος Αρχείου Καταγραφής(&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Σημειώσεις έκδοσης\"\nBasic.MainMenu.Help.CheckForUpdates=\"Ελέγχος Για Ενημερώσεις\"\nBasic.MainMenu.Help.Repair=\"Έλεγχος Ακεραιότητας Αρχείου\"\nBasic.MainMenu.Help.RestartSafeMode=\"Επανεκκίνηση σε Ασφαλή λειτουργία\"\nBasic.MainMenu.Help.RestartNormal=\"Επανεκκίνηση σε Κανονική Λειτουργία\"\nBasic.MainMenu.Help.CrashLogs=\"Αναφορές Σφαλμάτων(&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Εμφάνιση Αναφοράς Σφαλμάτων(&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Μεταφόρτωση &Προηγούμενης αναφοράς Κατάρρευσης\"\nBasic.MainMenu.Help.About=\"Σχετικά(&A)\"\nBasic.Settings.ProgramRestart=\"Το πρόγραμμα πρέπει να επανεκκινηθεί για να εφαρμοστούν αυτές οι ρυθμίσεις.\"\nBasic.Settings.ConfirmTitle=\"Επιβεβαίωση Αλλαγών\"\nBasic.Settings.Confirm=\"Έχετε μη αποθηκευμένες αλλαγές. Αποθήκευση αλλαγών;\"\nBasic.Settings.General=\"Γενικά\"\nBasic.Settings.General.Language=\"Γλώσσα\"\nBasic.Settings.General.Updater=\"Ενημερώσεις\"\nBasic.Settings.General.UpdateChannel=\"Ενημέρωση Καναλιού\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Απενεργοποιημένο)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Προεπιλεγμένο)\"\nBasic.Settings.General.EnableAutoUpdates=\"Αυτόματος έλεγχος ενημερώσεων κατά την εκκίνηση\"\nBasic.Settings.General.OpenStatsOnStartup=\"Άνοιγμα παραθύρου στατιστικών κατά την εκκίνηση\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Απόκρυψη παραθύρων OBS από την καταγραφή οθόνης\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Η ενεργοποίηση αυτής της επιλογής αποκρύπτει όλα τα παράθυρα του OBS Studio χωρίς προβολέα ώστε να μην καταγράφονται από το OBS και επηρεάζει άλλες εφαρμογές, όπως συνδιάσκεψη, κοινή χρήση οθόνης, απομακρυσμένη υποστήριξη, στιγμιότυπα οθόνης και άλλο λογισμικό λήψης.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Εμφάνιση παραθύρου επιβεβαίωσης κατά την εκκίνηση των ροών\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Εμφάνιση παραθύρου επιβεβαίωσης κατά τον τερματισμό των ροών\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Εμφάνιση παραθύρου επιβεβαίωσης κατά τον τερματισμό της καταγραφής\"\nBasic.Settings.General.Projectors=\"Προβολείς\"\nBasic.Settings.General.HideProjectorCursor=\"Απόκρυψη τού δρομέα πάνω από τούς προβολείς\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Προβολείς πάντα στην επιφάνεια\"\nBasic.Settings.General.Snapping=\"Προσκόλληση στην Ευθυγράμμιση Πηγής\"\nBasic.Settings.General.ScreenSnapping=\"Προσκόλληση πηγών στην άκρη της οθόνης\"\nBasic.Settings.General.CenterSnapping=\"Προσκόλληση πηγών οριζόντια και κατακόρυφα στο κέντρο\"\nBasic.Settings.General.SourceSnapping=\"Προσκόλληση πηγών σε άλλες πηγές\"\nBasic.Settings.General.SnapDistance=\"Ευαισθησία προσκόλλησης\"\nBasic.Settings.General.SpacingHelpers=\"Εμφάνιση οδηγών ευθυγράμμισης εικονοστοιχείων\"\nBasic.Settings.General.RecordWhenStreaming=\"Αυτόματη καταγραφή κατά την ροή\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Διατήρηση καταγραφής όταν τερματίζεται η ροή\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Αυτόματη εκκίνηση επανάληψης προσωρινής μνήμης κατά τη ροή\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Διατήρηση ενεργής επανάληψης προσωρινής μνήμης όταν τερματίζεται η ροή\"\nBasic.Settings.General.SysTray=\"Γραμμή Εργαλείων\"\nBasic.Settings.General.SysTrayWhenStarted=\"Ελαχιστοποίηση στη Γραμμή Εργαλείων κατά την εκκίνηση\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Συνεχής ελαχιστοποίηση στη γραμμή εργαλείων αντί για τη γραμμή εργασιών\"\nBasic.Settings.General.SaveProjectors=\"Αποθήκευση προβολής στην έξοδο\"\nBasic.Settings.General.CloseExistingProjectors=\"Περιορισμός ενός προβολέα πλήρους οθόνης ανά οθόνη\"\nBasic.Settings.General.Preview=\"Προεπισκόπηση\"\nBasic.Settings.General.OverflowHidden=\"Απόκρυψη υπερχείλισης\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Η υπερχείλιση είναι πάντα ορατή\"\nBasic.Settings.General.OverflowSelectionHidden=\"Εμφάνιση υπερχείλισης ακόμα και όταν η πηγή είναι είναι αόρατη\"\nBasic.Settings.General.Importers=\"Εισαγώμενα\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Ψάξτε γνωστές τοποθεσίες για τις συλλογές σκηνής όταν είναι απαραίτητο\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Μετάβαση στην σκηνή με διπλό κλικ\"\nBasic.Settings.General.StudioPortraitLayout=\"Ενεργοποίηση πορτρέτου/κατακόρυφης διάταξης\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Προβολή επισκόποιηση/προγράμματος επιγραφής\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Κάντε κλικ για εναλλαγή μεταξύ σκηνών\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Εμφάνιση ονομάτων σκηνών\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Εμφάνιση ασφαλών περιοχών (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Διάταξη MultiView\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Οριζόντια, Επάνω (8 Σκηνές)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Οριζόντια, Κάτω (8 Σκηνές)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Κατακόρυφα, Αριστερά (8 Σκηνές)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Κατακόρυφα, Δεξιά (8 Σκηνές)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Οριζόντια, Επάνω (18 Σκηνές)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Οριζόντια, Επάνω (24 Σκηνές)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Σκηνές μόνο (4 σκηνές)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Σκηνές μόνο (9 σκηνές)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Σκηνές μόνο (16 σκηνές)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Σκηνές μόνο (25 σκηνές)\"\nBasic.Settings.General.ChannelName.stable=\"Σταθερό\"\nBasic.Settings.General.ChannelDescription.stable=\"Τελευταία σταθερή έκδοση\"\nBasic.Settings.Appearance=\"Εμφάνιση\"\nBasic.Settings.Appearance.General=\"Γενικά\"\nBasic.Settings.Appearance.General.Theme=\"Θέμα\"\nBasic.Settings.Appearance.General.Variant=\"Στυλ\"\nBasic.Settings.Appearance.General.NoVariant=\"Δεν υπάρχουν διαθέσιμα στυλ\"\nBasic.Settings.Appearance.FontScale=\"Μέγεθος Γραμματοσειράς\"\nBasic.Settings.Appearance.Density=\"Πυκνότητα\"\nBasic.Settings.Appearance.Classic=\"Κλασική\"\nBasic.Settings.Appearance.Compact=\"Συμπυκνωμένη\"\nBasic.Settings.Appearance.Normal=\"Κανονική\"\nBasic.Settings.Appearance.Comfortable=\"Άνετη\"\nBasic.Settings.Stream=\"Ροή\"\nBasic.Settings.Stream.Destination=\"Προορισμός\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Χρήση πιστοποίησης\"\nBasic.Settings.Stream.Custom.Username=\"Όνομα χρήστη\"\nBasic.Settings.Stream.Custom.Password=\"Κωδικός\"\nBasic.Settings.Stream.BandwidthTestMode=\"Ενεργοποίηση λειτουργίας δοκιμής εύρους ζώνης\"\nBasic.Settings.Stream.TTVAddon=\"Συνομιλία Twitch πρόσθετα\"\nBasic.Settings.Stream.TTVAddon.None=\"Κανένα\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV και FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Ελείπει η ρύθμιση ροής\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Άνοιγμα Ρυθμίσεων\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Λείπει ο σύνδεσμος και το κλειδί ροής.\\n\\nΑνοίξτε τις ρυθμίσεις και βάλτε τον σύνδεσμο και το κλειδί ροής στη καρτέλα 'ροής'.\"\nBasic.Settings.Stream.MissingUrl=\"Λείπει ο σύνδεσμος ροής.\\n\\nΑνοίξτε τις ρυθμίσεις και βάλτε τον σύνδεσμο στη καρτέλα 'ροής'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Λείπει το κλειδί ροής.\\n\\nΑνοίξτε τις ρυθμίσεις και βάλτε το κλειδί ροής στη καρτέλα 'ροής'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Παράβλεψη συστάσεων ρύθμισης υπηρεσίας ροής\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Παράκαμψη Προτεινόμενων Ρυθμίσεων\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Προειδοποίηση: Αγνόηση των περιορισμών της υπηρεσίας μπορεί να οδηγήσει σε υποβαθμισμένη ποιότητα ροής ή να σας αποτρέψει από τη ροή.\\n\\nΣυνέχεια?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Μέγιστος ρυθμός μετάδοσης βίντεο: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Μέγιστος ρυθμός ήχου: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Μέγιστη ανάλυση: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Μέγιστο FPS: %1\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Ενεργοποίηση %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Αυτόματο\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Αυτόματο\"\nBasic.Settings.Stream.AdvancedOptions=\"Προχωρημένες Επιλογές\"\nBasic.Settings.Output=\"Έξοδος\"\nBasic.Settings.Output.Format=\"Μορφή Καταγραφής\"\nBasic.Settings.Output.Format.MKV=\"Βίντεο Matroska (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"Υβριδικό MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Υβριδικό MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Κατακερματισμένο MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Κατακερματισμένο MOV (.mov)\"\nBasic.Settings.Output.Encoder.Video=\"Κωδικοποιητής Βίντεο\"\nBasic.Settings.Output.Encoder.Audio=\"Κωδικοποιητής Ήχου\"\nBasic.Settings.Output.SelectDirectory=\"Επιλέξτε Φάκελο Καταγραφής\"\nBasic.Settings.Output.DynamicBitrate=\"Δυναμική αλλαγή ρυθμός μετάδοσης bit για τη διαχείρηση της συμφόρησης\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Δυναμική αλλαγή ρυθμός μετάδοσης bit για τη διαχείρηση της συμφόρησης(Δοκιμαστική Έκδοση)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Αντί να ρίχνει καρέ για να μειώσει τη συμφόρηση, αλλάζει δυναμικά bitrate στη μύγα.\\n\\nΣημειώστε ότι αυτό μπορεί να αυξήσει την καθυστέρηση στους θεατές αν υπάρχει σημαντική ξαφνική συμφόρηση.\\nΌταν τα bitrate πέφτουν, μπορεί να χρειαστούν έως και λίγα λεπτά για να επαναφέρετε.\\n\\nΠρος το παρόν υποστηρίζεται μόνο για RTMP.\"\nBasic.Settings.Output.Mode=\"Λειτουργία Εξόδου\"\nBasic.Settings.Output.Mode.Simple=\"Απλό\"\nBasic.Settings.Output.Mode.Adv=\"Για Προχωρημένους\"\nBasic.Settings.Output.Mode.FFmpeg=\"Έξοδος FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Ενεργοποίηση Επανάληψης Προσωρινής Μνήμης\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Μέγιστος Χρόνος Επανάληψης\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Μέγιστη Μνήμη\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Εκτιμώμενη χρήση μνήμης: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Προειδοποίηση: Η εκτιμώμενη χρήση μνήμης των %1 MiB είναι μεγαλύτερη από τη συνιστώμενη μέγιστη των %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Δεν είναι δυνατή η εκτίμηση της χρήσης μνήμης. Ορίστε μέγιστο όριο μνήμης.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Πρόθεμα ονόματος αρχείου Επανάληψης Προσωρινής Μνήμης\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Επίθεμα\"\nBasic.Settings.Output.Simple.SavePath=\"Διαδρομή Καταγραφής\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Ποιότητα Καταγραφής\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Ίδιο με την ροή\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Υψηλής Ποιότητας, Μεσαίου Μεγέθους Αρχείο\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Δυσδιάκριτης Ποιότητας, Μεγάλου Μεγέθους Αρχείο\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Μη Απωλεστικής Ποιότητας, Εξαιρετικά Μεγάλου Μεγέθους Αρχείο\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Προειδοποίηση: Το bitrate βίντεο ροής θα οριστεί σε %1, το οποίο είναι το ανώτερο όριο για την τρέχουσα υπηρεσία μετάδοσης.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Προειδοποίηση: Το bitrate ήχου ροής θα οριστεί σε %1, το οποίο είναι το ανώτερο όριο για την τρέχουσα υπηρεσία ροής.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Προειδοποίηση: Δε μπορείτε να σταματήσετε την καταγραφή εάν η ποιότητα εγγραφής έχει οριστεί σε \\\"Ίδιο με τη ροή\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Προειδοποίηση: Η τρέχουσα επιλεγμένη μορφή καταγραφής είναι ασύμβατη με τον επιλεγμένο κωδικοποιητή ροής.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Προειδοποίηση: Η καταγραφή με χρήση κωδικοποιητή λογισμικού σε διαφορετική ποιότητα από την ροή θα απαιτήσει πρόσθετη χρήση της CPU, αν πραγματοποιήσετε ταυτόχρονα μετάδοση ροής και καταγραφή.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Είστε σίγουρος ότι θέλετε να χρησιμοποιήσετε μη απωλεστική ποιότητα;\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Προειδοποίηση μη απωλεστικής ποιότητας!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Λογισμικό (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Υλικό (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Υλικό (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Υλικό (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Υλικό (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Υλικό (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Υλικό (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Υλικό (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Υλικό (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Υλικό (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Υλικό (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Λογισμικό (προεπιλογή x264 χαμηλής χρήσης CPU, αυξάνει το μέγεθος αρχείου)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Προεπιλογή)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Το Twitch Vod Track (Χρησιμοποιεί Track 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Κομμάτι Ήχου\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Μη Συμβατή Ανάλυση/Πλαίσιο\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Αυτή η υπηρεσία ροής δεν υποστηρίζει την τρέχουσα ανάλυση εξόδου και/ή framerate. Θα αλλάξουν στην πλησιέστερη συμβατή τιμή:\\n\\n%1\\n\\nΘέλετε να συνεχίσετε?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Ανάλυση: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Μη Συμβατός Κωδικοποιητής\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Η υπηρεσία ροής \\\"%1\\\" δεν υποστηρίζει τον κωδικοποιητή \\\"%2\\\". Ο κωδικοποιητής θα αλλάξει σε \\\"%3\\\".\\n\\nΘέλετε να συνεχίσετε;\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Η υπηρεσία ροής \\\"%1\\\" δεν υποστηρίζει τους κωδικοποιητές \\\"%2\\\" και \\\"%3\\\". Αυτοί οι κωδικοποιητές θα αλλάξουν σε \\\"%4\\\" και \\\"%5\\\".\\n\\nΘέλετε να συνεχίσετε;\"\nBasic.Settings.Output.VideoBitrate=\"Ρυθμός Μετάδοσης Bit του Βίντεο\"\nBasic.Settings.Output.AudioBitrate=\"Ρυθμός Μετάδοσης Bit του Ήχου\"\nBasic.Settings.Output.Reconnect=\"Αυτόματη Επανασύνδεση\"\nBasic.Settings.Output.RetryDelay=\"Επανάληψη Καθυστέρησης\"\nBasic.Settings.Output.MaxRetries=\"Μέγιστος Αριθμός Επαναλήψεων\"\nBasic.Settings.Output.Advanced=\"Ενεργοποίηση Προσαρμοσμένων Ρυθμίσεων Κωδικοποιητή (Για προχωρημένους)\"\nBasic.Settings.Output.EncoderPreset=\"Προεπιλογές Κωδικοποιητή\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (χαμηλή χρήση CPU, χαμηλότερη ποιότητα)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (προεπιλογή) (μέση χρήση CPU, τυπική ποιότητα)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (υψηλή χρήση CPU, υψηλή ποιότητα)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Προσαρμοσμένες Ρυθμίσεις Κωδικοποιητή\"\nBasic.Settings.Output.CustomMuxerSettings=\"Προσαρμοσμένες Ρυθμίσεις Πολυπλέκτη\"\nBasic.Settings.Output.NoSpaceFileName=\"Δημιουργία Ονόματος Αρχείου χωρίς Κενό\"\nBasic.Settings.Output.Adv.Rescale=\"Κλιμάκωση Εξόδου\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Απενεργοποιημένο\"\nBasic.Settings.Output.Adv.AudioTrack=\"Κομμάτι ήχου\"\nBasic.Settings.Output.Adv.Streaming=\"Ροή\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Ρυθμίσεις Ροής\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Κομμάτι 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Κομμάτι 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Κομμάτι 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Κομμάτι 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Κομμάτι 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Κομμάτι 6\"\nBasic.Settings.Output.Adv.Encoder=\"Ρυθμίσεις Κωδικοποιητή\"\nBasic.Settings.Output.Adv.Recording=\"Καταγραφή\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Ρυθμίσεις Καταγραφής\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Τύπος Εγγραφής\"\nBasic.Settings.Output.Adv.Recording.Type=\"Τύπος\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Κανονικός\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Προσαρμοσμένη Έξοδος (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Χρήση κωδικοποιητή ροής)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Μορφοποίηση Ονόματος Αρχείου\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Αντικατάσταση εάν το αρχείο υπάρχει\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Τύπος εξόδου FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Έξοδος σε διεύθυνση URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Έξοδος σε αρχείο\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Κοινές μορφές καταγραφής\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Όλα τα αρχεία\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Διαδρομή αρχείου ή URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Μορφή Περιέκτη\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Ήχος\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Βίντεο\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Προεπιλεγμένη Μορφή\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Περιγραφή Περιέκτη Μορφής\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Το Codec Ήχου/Βίντεο μαντεύθηκε από τη Διαδρομή Αρχείου ή το URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Προεπιλεγμένος Κωδικοποιητής\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Απενεργοποίηση Κωδικοποιητή\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Κωδικοποιητής Βίντεο\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Ρυθμίσεις Κωδικοποιητή Βίντεο (αν υπάρχουν)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Κωδικοποιητής Ήχου\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Ρυθμίσεις Κωδικοποιητή Ήχου (αν υπάρχουν)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Ρυθμίσεις Πολυπλέκτη (αν υπάρχει)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Διάστημα καρέ-κλειδιού (σε καρέ)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Προβολή όλων των codecs (ακόμα και αν δεν είναι συμβατοί)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Ρυθμίσεις FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Αυτόματος Διαχωρισμός Αρχείου\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Διαχωρισμός ανά Χρόνο\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Διαχωρισμός Βάση Μεγέθους\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Διαχωρισμός μόνο χειροκίνητα\"\nBasic.Settings.Output.SplitFile.Time=\"Χρονική Διάρκεια Τμημάτων\"\nBasic.Settings.Output.SplitFile.Size=\"Μέγεθος Τμημάτων\"\nScreenshot=\"Έξοδος Στιγμιότυπου Οθόνης\"\nScreenshot.SourceHotkey=\"Επιλεγμένη Προέλευση Στιγμιότυπου Οθόνης\"\nScreenshot.StudioProgram=\"Στιγμιότυπο οθόνης(Program)\"\nScreenshot.Preview=\"Στιγμιότυπο οθόνης(Preview)\"\nScreenshot.Scene=\"Στιγμιότυπο οθόνης(Scene)\"\nScreenshot.Source=\"Στιγμιότυπο οθόνης(Source)\"\nFilenameFormatting.TT.CCYY=\"Έτος, τέσσερα ψηφία\"\nFilenameFormatting.TT.YY=\"Έτος, τελευταία δύο ψηφία (00-99)\"\nFilenameFormatting.TT.MM=\"Μήνας ως δεκαδικό νούμερο (01-12)\"\nFilenameFormatting.TT.DD=\"Ημέρα του μήνα, με μηδέν (01-31)\"\nFilenameFormatting.TT.hh=\"Ώρα σε 24ωρη μορφή (00-23)\"\nFilenameFormatting.TT.mm=\"Λεπτό (00-59)\"\nFilenameFormatting.TT.ss=\"Δευτερόλεπτο (00-59)\"\nFilenameFormatting.TT.Percent=\"Ένα σύμβολο %\"\nFilenameFormatting.TT.a=\"Συντομευμένο όνομα ημέρας\"\nFilenameFormatting.TT.A=\"Πλήρες όνομα ημέρας\"\nFilenameFormatting.TT.b=\"Συντομευμένο όνομα μήνα\"\nFilenameFormatting.TT.B=\"Πλήρες όνομα μήνα\"\nFilenameFormatting.TT.d=\"Ημέρα του μήνα, με μηδέν (01-31)\"\nFilenameFormatting.TT.H=\"Ώρα σε 24ωρη μορφή (00-23)\"\nFilenameFormatting.TT.I=\"Ώρα σε 12ωρη μορφή (01-12)\"\nFilenameFormatting.TT.m=\"Μήνας ως δεκαδικό νούμερο (01-12)\"\nFilenameFormatting.TT.M=\"Λεπτό (00-59)\"\nFilenameFormatting.TT.p=\"Προσδιορισμός ΠΜ ή ΜΜ\"\nFilenameFormatting.TT.s=\"Χρόνος σε δευτερόλεπτα από το UNIX epoch\"\nFilenameFormatting.TT.S=\"Δευτερόλεπτο (00-59)\"\nFilenameFormatting.TT.y=\"Έτος, τελευταία δύο ψηφία (00-99)\"\nFilenameFormatting.TT.Y=\"Έτος\"\nFilenameFormatting.TT.Z=\"Ζώνη ώρας με όνομα ή συντομογραφία\"\nFilenameFormatting.TT.FPS=\"Καρέ ανά δευτερόλεπτο\"\nFilenameFormatting.TT.VF=\"Μορφή βίντεο\"\nBasic.Settings.Video=\"Βίντεο\"\nBasic.Settings.Video.Adapter=\"Προσαρμογέας Βίντεο\"\nBasic.Settings.Video.BaseResolution=\"Ανάλυση Βάσης (Καμβάς)\"\nBasic.Settings.Video.ScaledResolution=\"Ανάλυση Εξόδου (Κλίμακα)\"\nBasic.Settings.Video.DownscaleFilter=\"Φίλτρο Σμίκρυνσης\"\nBasic.Settings.Video.FPS=\"Καρέ ανά δευτερόλεπτο (FPS)\"\nBasic.Settings.Video.FPSCommon=\"Κοινές Τιμές FPS\"\nBasic.Settings.Video.FPSInteger=\"Ακέραια Τιμή FPS\"\nBasic.Settings.Video.FPSFraction=\"Κλασματική Τιμή FPS\"\nBasic.Settings.Video.Numerator=\"Αριθμητής\"\nBasic.Settings.Video.Denominator=\"Παρονομαστής\"\nBasic.Settings.Video.Renderer=\"Απόδοση\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Πειραματικό)\"\nBasic.Settings.Video.InvalidResolution=\"Μη έγκυρη τιμή ανάλυσης. Πρέπει να είναι [width]x[height] (π.χ. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Δεν υπάρχει ενεργή έξοδος βίντεο. Παρακαλώ απενεργοποιήστε κάθε έξοδο για να αλλάξετε τις ρυθμίσεις βίντεο.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Διγραμμικό (Πιο γρήγορο, αλλά θολό στην κλιμάκωση)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Δικυβικό (Οξυμμένη κλιμάκωση, 16 δείγματα)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Οξυμμένη κλιμάκωση, 36 δείγματα)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Περιοχή (Σταθμισμένο άθροισμα, δείγματα 4/6/9)\"\nBasic.Settings.Audio=\"Ήχος\"\nBasic.Settings.Audio.SampleRate=\"Ρυθμός Δειγματοληψίας\"\nBasic.Settings.Audio.Channels=\"Κανάλια\"\nBasic.Settings.Audio.Meters=\"Μέτρα\"\nBasic.Settings.Audio.MeterDecayRate=\"Ρυθμός Απόσβεσης\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Γρήγορος\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Μεσαίος (τύπος Ι PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Αργός (Τύπος ΙΙ PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Τύπος Μέτρησης Αιχμής\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Δείγμα Αιχμής\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Πραγματική Αιχμή (Υψηλότερη χρήση της CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"Προειδοποίηση: Ο ήχος Surround είναι ενεργοποιημένος.\"\nBasic.Settings.Audio.MultichannelWarning=\"Σε περίπτωση ροής, ελέγξτε εάν η υπηρεσία ροής σας υποστηρίζει τόσο την απορρόφηση ήχου surround όσο και την αναπαραγωγή ήχου surround. Για παράδειγμα, το Facebook 360 Live υποστηρίζει πλήρως ήχο surround. Το YouTube Live υποστηρίζει λήψη ήχου 5.1 (και αναπαραγωγή σε τηλεοράσεις).\\n\\nΤα φίλτρα ήχου OBS είναι συμβατά με ήχο surround, αν και η υποστήριξη της προσθήκης VST δεν είναι εγγυημένη.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Ενεργοποίηση ήχου surround;\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Είστε βέβαιοι ότι θέλετε να ενεργοποιήσετε τον ήχο surround;\"\nBasic.Settings.Audio.Devices=\"Παγκόσμιες συσκευές ήχου\"\nBasic.Settings.Audio.DesktopDevice=\"Ήχος Επιφάνειας Εργασίας\"\nBasic.Settings.Audio.DesktopDevice2=\"Ήχος Επιφάνειας Εργασίας 2\"\nBasic.Settings.Audio.AuxDevice=\"Μικρόφωνο/Βοηθητική Συσκευή Ήχου\"\nBasic.Settings.Audio.AuxDevice2=\"Μικρόφωνο/Βοηθητική Συσκευή Ήχου 2\"\nBasic.Settings.Audio.AuxDevice3=\"Μικρόφωνο/Βοηθητική Συσκευή Ήχου 3\"\nBasic.Settings.Audio.AuxDevice4=\"Μικρόφωνο/Βοηθητική Συσκευή Ήχου 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Ενεργοποίηση της Πίεσης-για-σίγαση\"\nBasic.Settings.Audio.PushToMuteDelay=\"Καθυστέρηση Πίεσης-για-σίγαση\"\nBasic.Settings.Audio.EnablePushToTalk=\"Ενεργοποίηση Πίεσης-για-ομιλία\"\nBasic.Settings.Audio.PushToTalkDelay=\"Push-to-talk καθυστέρηση\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Η συσκευή δεν είναι συνδεδεμένη ή δεν είναι διαθέσιμη]\"\nBasic.Settings.Audio.Disabled=\"Απενεργοποίηση\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Ενεργοποίηση λειτουργίας buffering ήχου χαμηλής καθυστέρησης ανταπόκρισης;\"\nBasic.Settings.Accessibility=\"Προσβασιμότητα\"\nBasic.Settings.Accessibility.ColorOverrides=\"Χρήση Διαφορετικών Χρωμάτων\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Μπάντα έντασης μίκτη (-60 to -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Μπάντα έντασης μίκτη (-20 to -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Μπάντα έντασης μίκτη (-9 to 0dB) (ενεργή)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Μπάντα έντασης μίκτη (-60 to -20dB) (ενεργή)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Μπάντα έντασης μίκτη (-20 to -9dB) (ενεργή)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Μπάντα έντασης μίκτη (-9 to 0dB) (ενεργή)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Προεπιλογές Χρώματος\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Προεπιλογή\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Προσαρμοσμένη\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Εναλλακτική Αχρωματοψίας\"\nBasic.Settings.Advanced=\"Για Προχωρημένους\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Εμφάνιση προειδοποίησης ενεργών εξόδων κατά την έξοδο\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Προτεραιότητα Διαδικασίας\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Υψηλή\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Πάνω από την Κανονική\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Κανονική\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Κάτω από την Κανονική\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Σε αδράνεια\"\nBasic.Settings.Advanced.FormatWarning=\"Προειδοποίηση: Οι μορφές χρωμάτων εκτός των NV12/P010 προορίζονται κυρίως για καταγραφή, και δεν συνιστώνται κατά τη ροή.Η ροή μπορεί να προκαλέσει αυξημένη χρήση CPU λόγω μετατροπής μορφής χρώματος.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Προειδοποίηση: οι μορφές υψηλής ακρίβειας χρησιμοποιούνται πιο συχνά με χρωματικούς χώρους HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Προειδοποίηση: το Rec. 2100 πρέπει να χρησιμοποιεί μορφή με περισσότερη ακρίβεια.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Μορφή Χρώματος\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 επίπεδα)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 επίπεδα)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 επίπεδα)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 επίπεδα)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 επίπεδα)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 επίπεδα)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 επίπεδα)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Πηγή χρώματος\"\nBasic.Settings.Advanced.Video.ColorRange=\"Εύρος Χρωμάτων Yuv\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Περιορισμένο\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Πλήρες\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR Επίπεδο λευκού\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR Ονομαστικό κορυφαίο επίπεδο\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Συσκευή Παρακολούθησης Ήχου\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Προεπιλεγμένη\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Απενεργοποίηση Σίγασης Ήχου Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Καθυστέρηση Ροής\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Διάρκεια\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Διατήρηση σημείου αποκοπής (αύξηση καθυστέρησης) κατά την επανασύνδεση\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Εκτιμώμενη Χρήση Μνήμης: %1 MB\"\nBasic.Settings.Advanced.Network=\"Δίκτυο\"\nBasic.Settings.Advanced.Network.Disabled=\"Το επιλεγμένο πρωτόκολλο ροής δεν υποστηρίζει αλλαγή ρυθμίσεων δικτύου.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Σύνδεση με IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Οικογένεια IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Ενεργοποίηση βελτιστοποίησεις δικτύου\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Ενεργοποίηση TCP περιθωρίου\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Προσπαθεί να κάνει την έξοδο RTMP πιο φιλική σε άλλες εφαρμογές που είναι ευαίσθητες σε λανθάνουσα κατάσταση στο δίκτυο ρυθμίζοντας το ρυθμό μετάδοσης.\\nΜπορεί να αυξήσει τον κίνδυνο των πτυσσόμενων καρέ σε ασταθείς συνδέσεις.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Συμπεριφορά εστίασης πλήκτρο συντόμευσης\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Ποτέ να μην απενεργοποιθούν τα πλήκτρα πρόσβασης\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Απενεργοποίηση πλήκτρων συντόμευσης όταν το κύριο παράθυρο βρίσκεται στο προσκήνιο\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Απενεργοποίηση πλήκτρων συντόμευσης όταν το κύριο παράθυρο δεν είναι στο επίκεντρο\"\nBasic.Settings.Advanced.AutoRemux=\"Αυτόματη μετατροπή σε %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(καταγραφή ως mkv)\"\nBasic.AdvAudio=\"Ιδιότητες Ήχου για Προχωρημένους\"\nBasic.AdvAudio.ActiveOnly=\"Μόνο ενεργές πηγές\"\nBasic.AdvAudio.Name=\"Όνομα\"\nBasic.AdvAudio.Volume=\"Ένταση\"\nBasic.AdvAudio.VolumeSource=\"Ένταση για το '%1'\"\nBasic.AdvAudio.Mono=\"Μονοφωνικό\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix για '%1'\"\nBasic.AdvAudio.Balance=\"Εξισορρόπηση\"\nBasic.AdvAudio.BalanceSource=\"Υπόλοιπο για '%1'\"\nBasic.AdvAudio.SyncOffset=\"Μετατόπιση Συγχρονισμού\"\nBasic.AdvAudio.SyncOffsetSource=\"Συγχρονισμός μετατόπισης για '%1'\"\nBasic.AdvAudio.Monitoring=\"Ηχητική Παρακολούθηση\"\nBasic.AdvAudio.Monitoring.None=\"Τερματισμός Παρακολούθησης\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Παρακολούθηση Μόνο (σίγαση εξόδου)\"\nBasic.AdvAudio.Monitoring.Both=\"Παρακολούθηση και Έξοδος\"\nBasic.AdvAudio.MonitoringSource=\"Παρακολούθηση ήχου για '%1'\"\nBasic.AdvAudio.AudioTracks=\"Κομμάτια\"\nBasic.Settings.Hotkeys=\"Πλήκτρα συντόμευσης\"\nBasic.Settings.Hotkeys.Pair=\"Συνδυασμοί πλήκτρων που μοιράζεται με το «%1» που ενεργούν ως εναλλαγή\"\nBasic.Settings.Hotkeys.Filter=\"Φίλτρο\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Φιλτράρισμα κατά Συντόμευση\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Αυτό το πλήκτρο συντόμευσης μοιράζεται από μία ή περισσότερες άλλες ενέργειες, κάντε κλικ για να εμφανίσετε αντιφάσεις\"\nBasic.Settings.Hotkeys.PleaseWait=\"Φόρτωση συντομεύσεων πληκτρολογίου, παρακαλώ περιμένετε...\"\nBasic.Hotkeys.SelectScene=\"Μετάβαση σε σκηνή\"\nBasic.SystemTray.Show=\"Εμφάνιση\"\nBasic.SystemTray.Hide=\"Απόκρυψη\"\nBasic.SystemTray.Message.Reconnecting=\"Έγινε αποσύνδεση. Γίνεται επανασύνδεση...\"\nHotkeys.Delete=\"Διαγραφή\"\nMute=\"Σίγαση\"\nUnmute=\"Κατάργηση σίγασης\"\nPush-to-mute=\"Πίεση-για-σίγαση\"\nPush-to-talk=\"Πίεση-για-ομολία\"\nSceneItemShow=\"Εμφάνιση '%1'\"\nSceneItemHide=\"Απόκρυψη '%1'\"\nOutputWarnings.NoTracksSelected=\"Πρέπει να επιλέξετε τουλάχιστον ένα κομμάτι\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Σφάλμα Ρυθμίσεων Εξόδου\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Όλες οι έξοδοι πρέπει να έχουν τουλάχιστον ένα κομμάτι ήχου επιλεγμένο.\"\nOutputWarnings.MP4Recording=\"Προειδοποίηση: Οι καταγραφές που έχουν αποθηκευτεί σε MP4 δε θα μπορούν να ανακτηθούν αν το αρχείο δεν είναι δυνατόν να ολοκληρωθεί (π.χ. λόγω BSODs, απώλεια ισχύος, κλπ.). Αν θέλετε να καταγράψετε πολλαπλά κομμάτια ήχου χρησιμοποιήστε το MKV και μετατροπή καταγραφής για mp4, αφού ολοκληρωθεί (αρχείο-> Μετατροπή Καταγραφών)\"\nOutputWarnings.CannotPause=\"Προειδοποίηση: Οι καταγραφές δεν μπορούν να γίνουν παύση άμα ο κωδικοποιητής εγγραφής έχει οριστεί \\\"(Χρησιμοποιήστε κωδικοποιητή ροής)\\\"\"\nOutputWarnings.CodecIncompatible=\"Έγινε επαναφορά της επιλογής κωδικοποιητή ήχου ή βίντεο λόγω ασυμβατότητας. Παρακαλώ επιλέξτε έναν συμβατό κωδικοποιητή από τη λίστα.\"\nCodecCompat.Incompatible=\"(Ασύμβατο με %1)\"\nCodecCompat.CodecPlaceholder=\"Επιλέξτε Κωδικοποιητή...\"\nCodecCompat.ContainerPlaceholder=\"Επιλέξτε Μορφή...\"\nCodecCompat.CodecMissingOnExit.Title=\"Δεν Επιλέχθηκε Κωδικοποιητής\"\nCodecCompat.CodecMissingOnExit.Text=\"Δεν έχει οριστεί τουλάχιστον ένας κωδικοποιητής βίντεο ή ήχου. Παρακαλώ βεβαιωθείτε να επιλέξετε κωδικοποιητές για καταγραφή και ροή.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Δεν Επιλέχθηκε Μορφή\"\nCodecCompat.ContainerMissingOnExit.Text=\"Δεν επιλέχθηκε καμία μορφή καταγραφής. Παρακαλώ επιλέξτε μια μορφή καταγραφής που να είναι συμβατή με τον επιλεγμένο κωδικοποιητή ροής.\"\nFinalScene.Title=\"Διαγραφή σκηνής\"\nFinalScene.Text=\"Πρέπει να υπάρχει τουλάχιστον μία σκηνή.\"\nNoSources.Title=\"Δεν Υπάρχουν Πηγές\"\nNoSources.Text=\"Δεν έχετε προσθέσει καμία πηγή βίντεο, επομένως θα εξαχθεί μία κενή οθόνη. Σίγουρα επιθυμείτε να συνεχίσετε;\"\nNoSources.Text.AddSource=\"Μπορείτε να προσθέσετε πηγές οποιαδήποτε στιγμή κάνοντας κλικ στο εικονίδιο + κάτω από το πλαίσιο Πηγές στο κύριο παράθυρο.\"\nNoSources.Label=\"Δεν έχετε καμία πηγή\\nΠατήστε το + ακόλουθο κουμπί,\\nή πατήστε το δεξί κλίκ για να προσθέσετε ένα.\"\nChangeBG=\"Ορίστε Χρώμα\"\nCustomColor=\"Προσαρμοσμένο Χρώμα\"\nBrowserSource.EnableHardwareAcceleration=\"Ενεργοποίηση Επιτάχυνσης Υλικού Πηγής του Προγράμματος Περιήγησης\"\nAbout=\"Σχετικά\"\nAbout.Info=\"Το OBS Studio είναι ένα ελεύθερο και ανοικτού κώδικα λογισμικό καταγραφής βίντεο και ζωντανής μετάδοσης.\"\nAbout.Donate=\"Κάντε μια Συνεισφορά\"\nAbout.GetInvolved=\"Συμμετάσχετε\"\nAbout.Authors=\"Δημιουργοί\"\nAbout.License=\"Άδεια\"\nAbout.Error=\"Σφάλμα! Το αρχείο δεν μπορούσε να αναγνωσθεί.\\n\\nΠηγαίνετε στο: %1\"\nAbout.Contribute=\"Υποστηρίξτε το εργο του OBS\"\nAddUrl.Title=\"Εισάγεται Πηγή μέσω Συνδέσμου\"\nAddUrl.Text=\"Έχετε τραβήξει ένα σύνδεσμο στο OBS. Αυτό αυτόματα θα προσπέσει τον σύνδεσμο ως πηγή. Θέλετε να συνεχίσετε;\"\nAddUrl.Text.Url=\"Σύνδεσμος%1\"\nResizeOutputSizeOfSource=\"Αλλαγή Διαστάσεων Εξόδου (Διαστάσεις Πηγής)\"\nResizeOutputSizeOfSource.Text=\"Η βασική ανάλυση και η ανάλυση εξόδου θα μεταβληθούν σύμφωνα με τις διαστάσεις της τρέχουσας πηγής.\"\nResizeOutputSizeOfSource.Continue=\"Θέλετε να συνεχίσετε?\"\nPreviewTransition=\"Προεπισκόπηση Μετάβασης\"\nImporter=\"Εισαγωγέας Συλλογής Σκηνής\"\nImporter.SelectCollection=\"Επιλέξτε μία Συλλογή Σκηνών\"\nImporter.Collection=\"Συλλογή Σκηνών\"\nImporter.HelpText=\"Προσπέστε αρχία σε αυτό το παράθυρο για να εισαχθεί η συλλογή από το OBS ή σε άλλα προγράμματα που υποστηρίζονται.\"\nImporter.Path=\"Τοποθεσία Συλλογής\"\nImporter.Program=\"Εντοπισμένη εφαρμογή\"\nImporter.AutomaticCollectionPrompt=\"Αυτόματη Αναζήτηση για Συλλογές Σκηνών\"\nImporter.AutomaticCollectionText=\"Το OBS μπορεί να βρει αυτόματα εισαγόμενες συλλογές σκηνών από υποστηριζόμενα τριτα προγράμματα . Θα θέλατε το OBS να βρίσκει αυτόματα συλλογές για σας?\\n\\nΜπορείτε να το αλλάξετε αργότερα στις Ρυθμίσεις> Γενικά> Εισαγωγείς.\"\nImporter.SelectFile=\"Αναζήτηση...\"\nRestart=\"Επανεκκίνηση\"\nNeedsRestart=\"Το OBS Studio χρειάζεται να γίνει επανεκκίνηση. Θέλετε να το κάνετε επανεκκίνηση τώρα;\"\nLoadProfileNeedsRestart=\"Το προφίλ περιέχει ρυθμίσεις που απαιτούν επανεκκίνηση του OBS:\\n%1\\n\\nΘέλετε να επανεκκινήσετε το OBS για να εφαρμοστούν αυτές οι ρυθμίσεις?\"\nContextBar.NoSelectedSource=\"Δεν επιλέχθηκε πηγή\"\nContextBar.MediaControls.PlayMedia=\"Αναπαραγωγή Πολυμέσων\"\nContextBar.MediaControls.PauseMedia=\"Παύση Πολυμέσων\"\nContextBar.MediaControls.StopMedia=\"Διακοπή Πολυμέσων\"\nContextBar.MediaControls.RestartMedia=\"Επανεκκίνηση Πολυμέσων\"\nContextBar.MediaControls.PlaylistNext=\"Επόμενο στη Λίστα\"\nContextBar.MediaControls.PlaylistPrevious=\"Προηγούμενο στη Λίστα\"\nContextBar.MediaControls.BlindSeek=\"Μενού Αναζήτησης Πολυμέσων\"\nYouTube.Auth.Ok=\"Η εξουσιοδότηση ολοκληρώθηκε επιτυχώς.\\n Τώρα μπορείτε να κλείσετε αυτή τη σελίδα.\"\nYouTube.Auth.NoCode=\"Η διαδικασία εξουσιοδότησης δεν ολοκληρώθηκε.\"\nYouTube.Auth.NoChannels=\"Δεν υπάρχουν διαθέσιμα κανάλια στον επιλεγμένο λογαριασμό\"\nYouTube.Auth.WaitingAuth.Title=\"Εξουσιοδότηση Χρήστη Γιουτούμπ\"\nYouTube.Auth.WaitingAuth.Text=\"Παρακαλούμε ολοκληρώστε την εξουσιοδότηση στον εξωτερικό σας περιηγητή.<br>Εάν ο εξωτερικός περιηγητής δεν ανοίξει, ακολουθήστε αυτόν τον σύνδεσμο και ολοκληρώστε την εξουσιοδότηση:<br>%1\"\nYouTube.AuthError.Text=\"Αποτυχία φόρτωσης πληροφοριών καναλιού: %1.\"\nYouTube.Actions.WindowTitle=\"Ρύθμιση Αναμετάδοσης YouTube - Κανάλι: %1\"\nYouTube.Actions.CreateNewEvent=\"Δημιουργία νέας αναμετάδοσης\"\nYouTube.Actions.ChooseEvent=\"Επιλογή υπάρχουσας αναμετάδοσης\"\nYouTube.Actions.Title=\"Τίτλος*\"\nYouTube.Actions.MyBroadcast=\"Η Μετάδοσή Μου\"\nYouTube.Actions.Description=\"Περιγραφή\"\nYouTube.Actions.Privacy=\"Απόρρητο*\"\nYouTube.Actions.Privacy.Private=\"Ιδιωτικό\"\nYouTube.Actions.Privacy.Public=\"Δημόσιο\"\nYouTube.Actions.Privacy.Unlisted=\"Μη Καταχωρημένο\"\nYouTube.Actions.Category=\"Κατηγορία\"\nYouTube.Actions.Thumbnail=\"Μικρογραφία\"\nYouTube.Actions.Thumbnail.SelectFile=\"Επιλογή αρχείου...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Δεν επιλέχθηκε αρχείο\"\nYouTube.Actions.Thumbnail.ClearFile=\"Εκκαθάριση\"\nYouTube.Actions.MadeForKids=\"Είναι το βίντεο αυτό για παιδιά;*\"\nYouTube.Actions.MadeForKids.Yes=\"Ναι, είναι φτιαγμένο για παιδιά\"\nYouTube.Actions.MadeForKids.No=\"Όχι, δεν είναι φτιαγμένο για παιδιά\"\nYouTube.Actions.AdditionalSettings=\"Πρόσθετες ρυθμίσεις\"\nYouTube.Actions.Latency=\"Καθυστέρηση\"\nYouTube.Actions.Latency.Normal=\"Κανονική\"\nYouTube.Actions.Latency.Low=\"Χαμηλή\"\nYouTube.Actions.Latency.UltraLow=\"Εξαιρετικά χαμηλή\"\nYouTube.Actions.EnableAutoStart=\"Ενεργοποίηση Αυτόματης Εκκίνησης\"\nYouTube.Actions.EnableAutoStop=\"Ενεργοποίηση Αυτόματης Διακοπής\"\nYouTube.Actions.AutoStartStop.TT=\"Υποδεικνύει αν αυτή η προγραμματισμένη αναμετάδοση θα ξεκινήσει αυτόματα\"\nYouTube.Actions.EnableDVR=\"Ενεργοποίηση Ψηφιακής Συσκευής Καταγραφής (DVR)\"\nYouTube.Actions.360Video=\"Βίντεο 360\"\nYouTube.Actions.ScheduleForLater=\"Προγραμματισμός για αργότερα\"\nYouTube.Actions.RememberSettings=\"Διατήρηση ρυθμίσεων\"\nYouTube.Actions.Create_Ready=\"Δημιουργία αναμετάδοσης\"\nYouTube.Actions.Create_GoLive=\"Δημιουργία αναμετάδοσης και έναρξη ροής\"\nYouTube.Actions.Choose_Ready=\"Επιλογή αναμετάδοσης\"\nYouTube.Actions.Choose_GoLive=\"Επιλογή αναμετάδοσης και έναρξη ροής\"\nYouTube.Actions.Create_Schedule=\"Προγραμματισμός αναμετάδοσης\"\nYouTube.Actions.Create_Schedule_Ready=\"Προγραμματισμός και επιλογή αναμετάδοσης\"\nYouTube.Actions.Dashboard=\"Άνοιγμα YouTube Studio\"\nYouTube.Actions.Error.Title=\"Σφάλμα δημιουργίας ζωντανής εκπομπής\"\nYouTube.Actions.Error.Text=\"Σφάλμα πρόσβασης Γιουτούμπ '%1'.<br/>Λεπτομερής περιγραφή σφάλματος μπορεί να βρεθεί στο <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Σφάλμα πρόσβασης στο YouTube. Παρακαλώ ελέγξτε τη σύνδεσή σας στο δίκτυο ή την πρόσβαση στον διακομιστή σας YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Σφάλμα δημιουργίας εκπομπής '%1'.<br/>Λεπτομερής περιγραφή σφάλματος μπορεί να βρεθεί στο <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Δεν δημιουργήθηκε η ροή. Παρακαλούμε επανασυνδέστε τον λογαριασμό.\"\nYouTube.Actions.Error.YouTubeApi=\"Σφάλμα YouTube API. Παρακαλώ δείτε το αρχείο καταγραφής για περισσότερες πληροφορίες.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Η επιλεγμένη αναμετάδοση δεν βρέθηκε.\"\nYouTube.Actions.Error.FileMissing=\"Το επιλεγμένο αρχείο δεν υπάρχει.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Σφάλμα στο άνοιγμα του επιλεγμένου αρχείου.\"\nYouTube.Actions.Error.FileTooLarge=\"Το επιλεγμένο αρχείο είναι πολύ μεγάλο (Όριο: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Η μετάβαση της αναμετάδοσης απέτυχε: %1<br/><br/>Αν αυτό το σφάλμα συνεχιστεί <a href='https://studio.youtube.com/video/%2/livestreaming'>ανοίξτε την αναμετάδοση στο YouTube Studio</a> και δοκιμάστε χειροκίνητα.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Η αναμετάδοση μεταβαίνει στο στάδιο δοκιμής, μπορεί να πάρει λίγο χρόνο. Δοκιμάστε ξανά σε 10-30 δευτερόλεπτα.\"\nYouTube.Actions.EventsLoading=\"Φόρτωση λίστας συμβάντων...\"\nYouTube.Actions.EventCreated.Title=\"Δημιουργήθηκε Συμβάν\"\nYouTube.Actions.EventCreated.Text=\"Το συμβάν δημιουργήθηκε επιτυχώς.\"\nYouTube.Actions.Stream=\"Ροή\"\nYouTube.Actions.Stream.ScheduledFor=\"Προγραμματισμένο για %1\"\nYouTube.Actions.Stream.Resume=\"Συνέχιση ροής που διακόπηκε\"\nYouTube.Actions.Stream.YTStudio=\"Δημιουργήθηκε αυτόματα από το YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Δημιουργία μιας νέας ζωντανής εκπομπής, παρακαλούμε περιμένετε...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Απαιτείται χειροκίνητη εκκίνηση\"\nYouTube.Actions.AutoStartStreamingWarning=\"Η αυτόματη εκκίνηση είναι απενεργοποιημένη για αυτό το συμβάν, κάντε κλικ στο \\\"Go Live\\\" για να ξεκινήσετε την αναμετάδοση.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Δεν θα είστε σε θέση να επανασυνδεθείτε.<br>Η ροή σας θα σταματήσει και δεν θα είναι πλέον ζωντανή.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Η ζωντανή μετάδοση δεν είναι ενεργοποιημένη στο επιλεγμένο κανάλι YouTube.<br/><br/>Δείτε <a href='https://www.youtube.com/features'>youtube.com/features</a> για περισσότερες πληροφορίες.\"\nYouTube.Errors.livePermissionBlocked=\"Η ζωντανή μετάδοση δεν είναι διαθέσιμη στο επιλεγμένο κανάλι YouTube.<br/>Μπορεί να χρειαστούν έως και 24 ώρες για να γίνει διαθέσιμη η ζωντανή ροή αφού την ενεργοποιήσετε στις ρυθμίσεις καναλιού σας.<br/><br/>Δείτε <a href='https://www.youtube.com/features'>youtube.com/features</a> για λεπτομέρειες.\"\nYouTube.Errors.errorExecutingTransition=\"Η μετάβαση απέτυχε λόγω σφάλματος συστήματος διαχείρισης. Παρακαλώ δοκιμάστε ξανά σε λίγα δευτερόλεπτα.\"\nYouTube.Errors.errorStreamInactive=\"Το YouTube δεν λαμβάνει δεδομένα για τη ροή σας. Παρακαλώ ελέγξτε τις ρυθμίσεις σας και προσπαθήστε ξανά.\"\nYouTube.Errors.invalidTransition=\"Η απόπειρα μετάβασης δεν ήταν έγκυρη. Αυτό μπορεί να οφείλεται σε προηγούμενη μη ολοκληρωμένη μετάβαση. Παρακαλώ περιμένετε μερικά δευτερόλεπτα και προσπαθήστε ξανά.\"\nConfigDownload.WarningMessageTitle=\"Προσοχή\"\nFailedToStartStream.MissingConfigURL=\"Δεν υπάρχει διαθέσιμο URL παραμετροποίησης για την τρέχουσα υπηρεσία\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Δεν έχει οριστεί προσαρμοσμένο URL του RTMP\"\nFailedToStartStream.NoRTMPURLInConfig=\"Η ρύθμιση δεν περιλαμβάνει URL στόχου ροής του RTMP(S)\"\nFailedToStartStream.WarningUnknownStatus=\"Ελήφθη άγνωστη κατάσταση τιμής '%1'\"\nFailedToStartStream.NoConfigSupplied=\"Λείπει ρύθμιση\"\nMultitrackVideo.IncompatibleSettings.Title=\"Μη συμβατές ρυθμίσεις\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 δεν είναι τώρα συμβατό με [Ήχος → Γενικά → Κανάλια] ορισμένα σε '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Ήχος → Γενικά → Κανάλια] πρέπει να οριστεί σε '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 απαιτεί πολλαπλές διαφορετικές ρυθμίσεις για [Ήχος → Γενικά → Κανάλια]\"\nBasic.OpenPluginManager=\"Διαχειριστής προσθέτων\"\nPluginManager=\"Διαχειριστής προσθέτων\"\nPluginManager.MissingPlugin=\"[ΠΡΟΣΘΕΤΟ ΔΕ ΒΡΕΘΗΚΕ]\"\nPluginManager.Section.Discover=\"Περιήγηση\"\nPluginManager.Section.Manage=\"Εγκατεστημένο\"\nPluginManager.Section.Updates=\"Ενημερώσεις\"\n"
  },
  {
    "path": "frontend/data/locale/en-GB.ini",
    "content": "Language=\"English (UK)\"\nRename=\"Rename\"\nProjector.Open.Multiview=\"Open Multi-view\"\nProjector.Display=\"Display%1\"\nProjector.Window=\"New Window\"\nProjector.Title.Scene=\"Scene%1\"\nProjector.Title.Source=\"Source%1\"\nProjector.Title.Multiview=\"Multi-view\"\nNew=\"New\"\nDuplicate=\"Duplicate\"\nFullscreen=\"Full-screen\"\nAddScene=\"Add scene\"\nMixerToolbarMenu=\"Audio mixer menu\"\nPluginsFailedToLoad.Title=\"Plugin load error\"\nAlreadyRunning.Text=\"OBS is already running! Unless you meant to do this, please shut down any existing instances of OBS before trying to run a new instance. If you have OBS set to minimise to the system tray, please check to see if it's still running there.\"\nCrashHandling.Labels.Text=\"OBS Studio did not shut down properly.\\n\\nRun in Safe Mode (third-party plugins, scripting, and WebSockets disabled)?\"\nCrashHandling.Labels.PrivacyNotice=\"You can also choose to automatically upload the most recent crash report to the OBS Project.<br /><br />Please read the <a href='https://obsproject.com/privacy-policy'>Privacy Policy</a> before uploading any files, paying particular attention to the sections regarding file uploads.\"\nWine.Text=\"Running OBS in Wine is unsupported, and many features, such as capture or device sources, will not work or only in limited capacity.<br><br>It is recommended to run a native version of OBS instead, for example <a href='https://flathub.org/apps/details/com.obsproject.Studio'>our Flatpak version</a> or your operating system's packages.\"\nBrowserPanelInit.Title=\"Initialising Browser...\"\nBrowserPanelInit.Text=\"Initialising browser, please wait...\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimise for streaming, recording is secondary\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimise just for recording, I will not be streaming\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: enter the encryption passphrase.\\nRTMP: enter the key provided by the service.\\nSRT:enter the streamid if the service uses one.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"The bandwidth test is about to stream randomised video data without audio to your channel. If you're able, it's recommended to temporarily turn off saving videos of streams and set the stream to private until after the test has completed. Continue?\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"The program is now executing a set of tests to estimate the most ideal settings\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Failed to connect to any servers, please check your Internet connection and try again.\"\nBasic.AutoConfig.TestPage.Result.Header=\"The program has determined that these estimated settings are the most ideal for you:\"\nBasic.AutoConfig.Info=\"The auto-configuration wizard will determine the best settings based on your computer specs and Internet speed.\"\nQuickTransitions.SwapScenes=\"Swap Preview/Output Scenes After Transitioning\"\nQuickTransitions.SwapScenesTT=\"Swaps the preview and output scenes after transitioning (if the output's original scene still exists).\\nThis will not undo any changes that may have been made to the output's original scene.\"\nQuickTransitions.DuplicateSceneTT=\"When editing the same scene, allows editing transform/visibility of sources without modifying the output.\\nTo edit properties of sources without modifying the output, enable 'Duplicate Sources'.\\nChanging this value will reset the current output scene (if it still exists).\"\nQuickTransitions.EditPropertiesTT=\"When editing the same scene, allows editing properties of sources without modifying the output.\\nThis can only be used if 'Duplicate Scene' is enabled.\\nCertain sources (such as capture or media sources) do not support this and cannot be edited separately.\\nChanging this value will reset the current output scene (if it still exists).\\n\\nWarning: Because sources will be duplicated, this may require extra system or video resources.\"\nUndo.Transform.Center=\"Centre to Screen in '%1'\"\nUndo.Transform.VCenter=\"Vertical Centre to Screen in '%1'\"\nUndo.Transform.HCenter=\"Horizontal Centre to Screen in '%1'\"\nOutput.BroadcastStartFailed=\"Failed to start brodcast\"\nRemux.HelpText=\"Drop files in this window to remux, or select an empty \\\"OBS Recording\\\" cell to browse for a file.\"\nRemux.NoFilesAdded=\"No file was added for remuxing. Drop a folder containing one or more video files.\"\nMissingFiles.Search=\"Search Folder...\"\nMissingFiles.SelectFile=\"Select File...\"\nMissingFiles.Missing=\"Not present\"\nMissingFiles.Replaced=\"Recreated\"\nMissingFiles.Cleared=\"Removed\"\nMissingFiles.Found=\"Detected\"\nMissingFiles.AutoSearch=\"Additional files detected\"\nMissingFiles.AutoSearchText=\"OBS detected more matches for disappeared files in that folder. Do you fancy adding them?\"\nMacPermissions.Description=\"OBS Studio requires explicit permissions to offer certain functions. Granting these permissions is recommended, but not required to use the app. They can always be granted later.\"\nMacPermissions.Description.OpenDialog=\"You can re-open this dialogue via the OBS Studio menu.\"\nMacPermissions.Item.Accessibility.Details=\"On older installations, OBS may be listed inside \\\"Accessibility\\\" and not \\\"Input Monitoring\\\"\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" is not assigned to any audio tracks and it will not be audible in streams or recordings.\\n\\nTo assign an audio source to a track, open the Advanced Audio Properties via the right-click menu or the cog button in the mixer dock toolbar.\"\nBasic.Main.RemigrateSceneCollection.Text=\"Do you want to update the scene collection resolution of \\\"%1\\\" to match the current profile's canvas resolution of %2x%3?\"\nBasic.PropertiesWindow.SelectColor=\"Select colour\"\nBasic.TransformWindow.Alignment.TopCenter=\"Top Centre\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Centre Left\"\nBasic.TransformWindow.Alignment.Center=\"Centre\"\nBasic.TransformWindow.Alignment.CenterRight=\"Centre Right\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Bottom Centre\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Edit Transform...\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Rotate 90 degrees Clockwise\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Rotate 90 degrees Anti-Clockwise\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centre to screen\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centre Vertically\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centre Horizontally\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Full-screen Interface\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Custom Browser Docks...\"\nBasic.Settings.General.OpenStatsOnStartup=\"Open stats dialogue on startup\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"This setting will hide all non-projector OBS Studio windows from being captured by OBS, and it will affect \\nother applications such as: conferencing, screen sharing, remote support, screenshots and other capture software.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Enabling this option will hide all non-projector OBS Studio windows from being captured by OBS, and it will affect other applications such as: conferencing, screen sharing, remote support, screenshots, and other capture software.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Show confirmation dialogue when starting streams\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Show confirmation dialogue when stopping streams\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Show confirmation dialogue when stopping recording\"\nBasic.Settings.General.CenterSnapping=\"Snap Sources to horizontal and vertical centre\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimise to system tray when started\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Always minimise to system tray instead of task bar\"\nBasic.Settings.General.CloseExistingProjectors=\"Limit to one full-screen projector per screen\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Warning: Recordings cannot be paused if the recording quality is set to \\\"Same as stream\\\".\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"The streaming service \\\"%1\\\" does not support the encoder \\\"%2\\\". The encoder will be changed to \\\"%3\\\".\\n\\nDo you want to continue?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"The streaming service \\\"%1\\\" does not support encoders \\\"%2\\\" and \\\"%3\\\". These encoders will be changed to \\\"%4\\\" and \\\"%5\\\".\\n\\nDo you want to continue?\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Custom Output (FFmpeg) is provided with no safeguards.\\nConsider using the \\\"Standard\\\" recording type.\"\nScreenshot.StudioProgram=\"Screenshot (Program)\"\nScreenshot.Preview=\"Screenshot (Preview)\"\nScreenshot.Scene=\"Screenshot (Scene)\"\nScreenshot.Source=\"Screenshot (Source)\"\nBasic.Settings.Accessibility.ColorOverrides=\"Use Different Colours\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Colour Preset\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Colour Blind Alternative\"\nBasic.Settings.Advanced.FormatWarning=\"Warning: Colour formats other than NV12/P010 are primarily intended for recording, and are not recommended when streaming. Streaming may incur increased CPU usage due to colour format conversion.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Warning: High-precision formats are more commonly used with HDR colour spaces.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Colour Format\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Colour Space\"\nBasic.Settings.Advanced.Video.ColorRange=\"Colour Range\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Enable network optimisations\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Hotkey Focus Behaviour\"\nOutputWarnings.MP4Recording=\"Warning: Recordings saved to MP4/MOV will be unrecoverable if the file cannot be finalised (e.g. as a result of BSODs, power losses, etc.). If you want to record multiple audio tracks consider using MKV and remux the recording to MP4/MOV after it is finished (File → Remux Recordings)\"\nOutputWarnings.CannotPause=\"Warning: Recordings cannot be paused if the recording encoder is set to \\\"(Use stream encoder)\\\"\"\nChangeBG=\"Set Colour\"\nCustomColor=\"Custom Colour\"\nAbout.License=\"Licence\"\nImporter=\"Scene Collection Importer\"\nYouTube.Auth.Ok=\"Authorisation completed successfully.\\nYou can now close this page.\"\nYouTube.Auth.NoCode=\"The authorisation process was not completed.\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube User Authorisation\"\nYouTube.Auth.WaitingAuth.Text=\"Please complete authorisation in your external browser.<br>If the external browser does not open, follow this link and complete the authorisation:<br>%1\"\nYouTube.Actions.Error.Text=\"YouTube access error '%1'.<br/>Detailed error description can be found at <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Broadcast creation error '%1'.<br/>Detailed error description can be found at <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.EventCreated.Text=\"Event successfully created.\"\nYouTube.Actions.AutoStartStreamingWarning=\"Auto-start is disabled for this event, click \\\"Go Live\\\" to start your broadcast.\"\nYouTube.DocksRemoval.Text=\"These browser docks will be removed as deprecated:\\n\\n%1\\nUse \\\"Docks/YouTube Live Control Room\\\" instead.\"\nFailedToStartStream.InvalidEncoderConfig=\"Go live config has an invalid encoder configuration\"\nBasic.OpenPluginManager=\"Plug-in Manager\"\nPluginManager=\"Plug-in Manager\"\n"
  },
  {
    "path": "frontend/data/locale/en-US.ini",
    "content": "# Note to translators: *DO NOT* translate this file directly. Instead, visit http://crowdin.com/project/obs-studio and submit your translations there.\n# Pull requests for translations outside of Crowdin will not be accepted.\n# Read this forum post for more instructions on submitting translations: https://obsproject.com/forum/threads/how-to-contribute-translations-for-obs.16327/\n\n# Language of this file\nLanguage=\"English\"\n\n# commonly shared locale\nOK=\"OK\"\nApply=\"Apply\"\nCancel=\"Cancel\"\nClose=\"Close\"\nSave=\"Save\"\nDiscard=\"Discard\"\nDisable=\"Disable\"\nYes=\"Yes\"\nNo=\"No\"\nAdd=\"Add\"\nRemove=\"Remove\"\nRename=\"Rename...\"\nInteract=\"Interact\"\nFilters=\"Filters\"\nProperties=\"Properties\"\nMoveUp=\"Move Up\"\nMoveDown=\"Move Down\"\nSettings=\"Settings\"\nDisplay=\"Display\"\nName=\"Name\"\nExit=\"Exit\"\nMixer=\"Audio Mixer\"\nBrowse=\"Browse\"\nMono=\"Mono\"\nStereo=\"Stereo\"\nDroppedFrames=\"Dropped Frames %1 (%2%)\"\nProjector.Open.Program=\"Open Program Projector\"\nProjector.Open.Preview=\"Open Preview Projector\"\nProjector.Open.Scene=\"Open Scene Projector\"\nProjector.Open.Source=\"Open Source Projector\"\nProjector.Open.Multiview=\"Open Multiview\"\nProjector.Display=\"Display: %1\"\nProjector.Window=\"New window\"\nProjector.Title=\"Projector\"\nProjector.Title.Scene=\"Scene: %1\"\nProjector.Title.Source=\"Source: %1\"\nProjector.Title.Multiview=\"Multiview\"\nProjector.ResizeWindowToContent=\"Fit window to content\"\nClear=\"Clear\"\nRevert=\"Revert\"\nShow=\"Show\"\nHide=\"Hide\"\nUnhideAll=\"Unhide All\"\nUntitled=\"Untitled\"\nNew=\"New...\"\nDuplicate=\"Duplicate...\"\nEnable=\"Enable\"\nDisableOSXVSync=\"Disable macOS V-Sync\"\nResetOSXVSyncOnExit=\"Reset macOS V-Sync on Exit\"\nHighResourceUsage=\"Encoding overloaded! Consider turning down video settings or using a faster encoding preset.\"\nTransition=\"Transition\"\nQuickTransitions=\"Quick Transitions\"\nFadeToBlack=\"Fade to Black\"\nLeft=\"Left\"\nRight=\"Right\"\nTop=\"Top\"\nBottom=\"Bottom\"\nWidth=\"Width\"\nHeight=\"Height\"\nReset=\"Reset\"\nHours=\"Hours\"\nMinutes=\"Minutes\"\nSeconds=\"Seconds\"\nDeprecated=\"Deprecated\"\nReplayBuffer=\"Replay Buffer\"\nImport=\"Import\"\nExport=\"Export\"\nCopy=\"Copy\"\nPaste=\"Paste\"\nPasteReference=\"Paste (Reference)\"\nPasteDuplicate=\"Paste (Duplicate)\"\nRemuxRecordings=\"Remux Recordings\"\nNext=\"Next\"\nBack=\"Back\"\nDefaults=\"Defaults\"\nRestoreDefaults=\"Defaults\"\nHideMixer=\"Hide in Mixer\"\nTransitionOverride=\"Transition Override\"\nShowTransition=\"Show Transition\"\nHideTransition=\"Hide Transition\"\nNone=\"None\"\nStudioMode.Preview=\"Preview\"\nStudioMode.Program=\"Program\"\nStudioMode.PreviewSceneName=\"Preview: %1\"\nStudioMode.ProgramSceneName=\"Program: %1\"\nShowInMultiview=\"Show in Multiview\"\nVerticalLayout=\"Vertical Layout\"\nGroup=\"Group\"\nDoNotShowAgain=\"Do not show again\"\nDefault=\"(Default)\"\nCalculating=\"Calculating...\"\nFullscreen=\"Fullscreen\"\nWindowed=\"Windowed\"\nRefreshBrowser=\"Refresh\"\nAspectRatio=\"Aspect Ratio <b>%1:%2</b>\"\nLockVolume=\"Lock Volume\"\nLogViewer=\"Log Viewer\"\nShowOnStartup=\"Show on startup\"\nOpenFile=\"Open file\"\nAddScene=\"Add Scene\"\nAddSource=\"Add Source\"\nRemoveScene=\"Remove Selected Scene\"\nRemoveSource=\"Remove Selected Source(s)\"\nMoveSceneUp=\"Move Scene Up\"\nMoveSceneDown=\"Move Scene Down\"\nMoveSourceUp=\"Move Source(s) Up\"\nMoveSourceDown=\"Move Source(s) Down\"\nSourceProperties=\"Open Source Properties\"\nSourceFilters=\"Open Source Filters\"\nMixerToolbarMenu=\"Audio Mixer Menu\"\nSceneFilters=\"Open Scene Filters\"\nList=\"List\"\nGrid=\"Grid\"\nAutomatic=\"Automatic\"\nComingSoon=\"Coming Soon\"\n\n# warning for plugin load failures\nPluginsFailedToLoad.Title=\"Plugin Load Error\"\nPluginsFailedToLoad.Text=\"The following OBS plugins failed to load:\\n\\n%1\\nPlease update or remove these plugins.\"\n\n# warning if program already open\nAlreadyRunning.Title=\"OBS is already running\"\nAlreadyRunning.Text=\"OBS is already running! Unless you meant to do this, please shut down any existing instances of OBS before trying to run a new instance. If you have OBS set to minimize to the system tray, please check to see if it's still running there.\"\nAlreadyRunning.LaunchAnyway=\"Launch Anyway\"\n\n# Prior Crash Detection\nCrashHandling.Dialog.Title=\"OBS Studio Crash Detected\"\nCrashHandling.Labels.Text=\"OBS Studio did not properly shut down.\\n\\nRun in Safe Mode (third-party plugins, scripting, and WebSockets disabled)?\"\nCrashHandling.Labels.PrivacyNotice=\"You can also choose to automatically upload the most recent crash report to the OBSProject.<br /><br />Please read the <a href='https://obsproject.com/privacy-policy'>Privacy Policy</a> before uploading any files and pay special attention to the parts regarding file uploads.\"\nCrashHandling.Checkbox.SendReport=\"I have read the privacy policy and consent to the upload.\"\nCrashHandling.Buttons.LaunchSafe=\"Run in Safe Mode\"\nCrashHandling.Buttons.LaunchNormal=\"Run in Normal Mode\"\nCrashHandling.Errors.UploadJSONError=\"An error occurred while trying to upload the most recent crash log. Please try again later.\"\nCrashHandling.Errors.Title=\"Crash Log Upload Error\"\n\n# Safe Mode Restart Option\nSafeMode.Restart=\"Do you want to restart OBS in Safe Mode (third-party plugins, scripting, and WebSockets disabled)?\"\nSafeMode.RestartNormal=\"Do you want to restart OBS in Normal Mode?\"\n\nChromeOS.Title=\"Unsupported Platform\"\nChromeOS.Text=\"OBS appears to be running inside a ChromeOS container. This platform is unsupported.\"\n\nWine.Title=\"Wine detected\"\nWine.Text=\"Running OBS in Wine is unsupported, and many features such as capture or device sources will not work or only in limited capacity.<br><br>It is recommended to run a native version of OBS instead, for example <a href='https://flathub.org/apps/details/com.obsproject.Studio'>our Flatpak version</a> or your operating system's packages.\"\n\n# warning when closing docks. it's frustrating that we actually need this.\nDockCloseWarning.Title=\"Closing Dockable Window\"\nDockCloseWarning.Text=\"You just closed a dockable window. If you'd like to show it again, use the Docks menu on the menu bar.\"\n\n# extra browser panels dialog\nExtraBrowsers=\"Custom Browser Docks\"\nExtraBrowsers.Info=\"Add docks by giving them a name and URL, then click Apply or Close to open the docks. You can add or remove docks at any time.\"\nExtraBrowsers.DockName=\"Dock Name\"\n\n# Auth\nAuth.Authing.Title=\"Authenticating...\"\nAuth.Authing.Text=\"Authenticating with %1, please wait...\"\nAuth.AuthFailure.Title=\"Authentication Failure\"\nAuth.AuthFailure.Text=\"Failed to authenticate with %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Authentication Required\"\nAuth.InvalidScope.Text=\"The authentication requirements for %1 have changed. Some features may not be available.\"\nAuth.LoadingChannel.Title=\"Loading channel information...\"\nAuth.LoadingChannel.Text=\"Loading channel information for %1, please wait...\"\nAuth.LoadingChannel.Error=\"Couldn't get channel information.\"\nAuth.ChannelFailure.Title=\"Failed to load channel\"\nAuth.ChannelFailure.Text=\"Failed to load channel information for %1\\n\\n%2: %3\"\nAuth.Chat=\"Chat\"\nAuth.StreamInfo=\"Stream Information\"\nTwitchAuth.Stats=\"Twitch Stats\"\nTwitchAuth.Feed=\"Twitch Activity Feed\"\nTwitchAuth.TwoFactorFail.Title=\"Could not query stream key\"\nTwitchAuth.TwoFactorFail.Text=\"OBS was unable to connect to your Twitch account. Please make sure two-factor authentication is set up in your <a href='https://www.twitch.tv/settings/security'>Twitch security settings</a> as this is required to stream.\"\nRestreamAuth.Channels=\"Restream Channels\"\n\n# copy filters\nCopy.Filters=\"Copy Filters\"\nPaste.Filters=\"Paste Filters\"\n\n# browser initialization dialog\nBrowserPanelInit.Title=\"Initializing Browser...\"\nBrowserPanelInit.Text=\"Initializing browser, please wait...\"\n\n# bandwidth test\nBandwidthTest.Region=\"Region\"\nBandwidthTest.Region.US=\"United States\"\nBandwidthTest.Region.EU=\"Europe\"\nBandwidthTest.Region.Asia=\"Asia\"\nBandwidthTest.Region.Other=\"Other\"\n\n# auto config wizard\nBasic.AutoConfig=\"Auto-Configuration Wizard\"\nBasic.AutoConfig.ApplySettings=\"Apply Settings\"\nBasic.AutoConfig.StartPage=\"Usage Information\"\nBasic.AutoConfig.StartPage.SubTitle=\"Specify what you want to use the program for\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimize for streaming, recording is secondary\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimize just for recording, I will not be streaming\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"I will only be using the virtual camera\"\nBasic.AutoConfig.VideoPage=\"Video Settings\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Specify the video settings you would like to use\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Use Current (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Display %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Use Current (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Either 60 or 30, but prefer 60 when possible\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Either 60 or 30, but prefer high resolution\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Note: The canvas (base) resolution is not necessarily the same as the resolution you will stream or record with. Your actual stream/recording resolution may be scaled down from the canvas resolution to reduce resource usage or bitrate requirements.\"\nBasic.AutoConfig.StreamPage=\"Stream Information\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Please enter your stream information\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Connect Account (recommended)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Disconnect Account\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Disconnect Account?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"This change will apply immediately. Are you sure you want to disconnect your account?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Get Stream Key\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"More Info\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Use Stream Key\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Use Stream Key (advanced)\"\nBasic.AutoConfig.StreamPage.Service=\"Service\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Show All...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Custom...\"\nBasic.AutoConfig.StreamPage.Server=\"Server\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Stream Key\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: enter the encryption passphrase.\\nRTMP: enter the key provided by the service.\\nSRT: enter the streamid if the service uses one.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Encoder Key\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Bearer Token\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Connected account\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Estimate bitrate with bandwidth test (may take a few minutes)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Prefer hardware encoding\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Hardware Encoding eliminates most CPU usage, but may require more bitrate to obtain the same level of quality.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Stream warning\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"The bandwidth test is about to stream randomized video data without audio to your channel. If you're able, it's recommended to temporarily turn off saving videos of streams and set the stream to private until after the test has completed. Continue?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Test %1\"\nBasic.AutoConfig.TestPage=\"Final Results\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"The program is now executing a set of tests to estimate the ideal settings\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testing complete\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Performing bandwidth test, this may take a few minutes...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"No output for the protocol of this service was found\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Connecting to: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Failed to connect to any servers, please check your internet connection and try again.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testing bandwidth for: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testing stream encoder, this may take a minute...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testing recording encoder, this may take a minute...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Failed to start up encoder\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testing %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Streaming Encoder\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Recording Encoder\"\nBasic.AutoConfig.TestPage.Result.Header=\"The program has determined that these estimated settings are ideal for you:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"To use these settings, click Apply Settings. To reconfigure the wizard and try again, click Back. To manually configure settings yourself, click Cancel and open Settings.\"\nBasic.AutoConfig.Info=\"The auto-configuration wizard will determine the best settings based on your computer specs and internet speed.\"\nBasic.AutoConfig.RunAnytime=\"This can be run at any time by going to the Tools menu.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Streaming (Scaled) Resolution\"\n\n# stats\nBasic.Stats=\"Stats\"\nBasic.Stats.CPUUsage=\"CPU Usage\"\nBasic.Stats.HDDSpaceAvailable=\"Disk space available\"\nBasic.Stats.MemoryUsage=\"Memory Usage\"\nBasic.Stats.AverageTimeToRender=\"Average time to render frame\"\nBasic.Stats.SkippedFrames=\"Skipped frames due to encoding lag\"\nBasic.Stats.MissedFrames=\"Frames missed due to rendering lag\"\nBasic.Stats.Output.Stream=\"Stream\"\nBasic.Stats.Output.Recording=\"Recording\"\nBasic.Stats.Status=\"Status\"\nBasic.Stats.Status.Recording=\"Recording\"\nBasic.Stats.Status.Live=\"LIVE\"\nBasic.Stats.Status.Reconnecting=\"Reconnecting\"\nBasic.Stats.Status.Inactive=\"Inactive\"\nBasic.Stats.Status.Active=\"Active\"\nBasic.Stats.DroppedFrames=\"Dropped Frames (Network)\"\nBasic.Stats.MegabytesSent=\"Total Data Output\"\nBasic.Stats.Bitrate=\"Bitrate\"\nBasic.Stats.DiskFullIn=\"Disk full in (approx.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 Hours, %2 Minutes\"\nBasic.Stats.ResetStats=\"Reset Stats\"\n\nResetUIWarning.Title=\"Are you sure you want to reset the UI?\"\nResetUIWarning.Text=\"Resetting the UI will hide additional docks. You will need to unhide these docks from the Docks menu if you want them to be visible.\\n\\nAre you sure you want to reset the UI?\"\n\n# updater\nUpdater.Title=\"New update available\"\nUpdater.Text=\"There is a new update available:\"\nUpdater.UpdateNow=\"Update Now\"\nUpdater.RemindMeLater=\"Remind me Later\"\nUpdater.Skip=\"Skip Version\"\nUpdater.NoUpdatesAvailable.Title=\"No updates available\"\nUpdater.NoUpdatesAvailable.Text=\"No updates are currently available\"\nUpdater.BranchNotFound.Title=\"Update Channel Removed\"\nUpdater.BranchNotFound.Text=\"Your selected update channel is no longer available, OBS has been reset to the default.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Integrity Check Unavailable\"\nUpdater.RepairButUpdatesAvailable.Text=\"Checking file integrity is only possible for the latest version available. Use Help → Check For Updates to verify and update your OBS installation.\"\nUpdater.RepairConfirm.Title=\"Confirm Integrity Check\"\nUpdater.RepairConfirm.Text=\"Starting the integrity check will scan your OBS installation for corruption and redownload broken/modified files. This may take a moment.\\n\\nDo you wish to proceed?\"\nUpdater.FailedToLaunch=\"Failed to launch updater\"\n\n# quick transitions\nQuickTransitions.SwapScenes=\"Swap Preview/Program Scenes After Transitioning\"\nQuickTransitions.SwapScenesTT=\"Swaps the preview and program scenes after transitioning (if the program's original scene still exists).\\nThis will not undo any changes that may have been made to the program's original scene.\"\nQuickTransitions.DuplicateScene=\"Duplicate Scene\"\nQuickTransitions.DuplicateSceneTT=\"When editing the same scene, allows editing transform/visibility of sources without modifying the program output.\\nTo edit properties of sources without modifying the program output, enable 'Duplicate Sources'.\\nChanging this value will reset the current program scene (if it still exists).\"\nQuickTransitions.EditProperties=\"Duplicate Sources\"\nQuickTransitions.EditPropertiesTT=\"When editing the same scene, allows editing properties of sources without modifying the program output.\\nThis can only be used if 'Duplicate Scene' is enabled.\\nCertain sources (such as capture or media sources) do not support this and cannot be edited separately.\\nChanging this value will reset the current program scene (if it still exists).\\n\\nWarning: Because sources will be duplicated, this may require extra system or video resources.\"\nQuickTransitions.HotkeyName=\"Quick Transition: %1\"\n\n# transitions\nBasic.AddTransition=\"Add Configurable Transition\"\nBasic.RemoveTransition=\"Remove Configurable Transition\"\nBasic.TransitionProperties=\"Transition Properties\"\nBasic.SceneTransitions=\"Scene Transitions\"\nBasic.TransitionDuration=\"Duration\"\nBasic.TogglePreviewProgramMode=\"Studio Mode\"\nBasic.EnablePreviewProgramMode=\"Enable Studio Mode\"\nBasic.DisablePreviewProgramMode=\"Disable Studio Mode\"\n\n# undo\nUndo.Undo=\"Undo\"\nUndo.Redo=\"Redo\"\nUndo.Add=\"Add '%1'\"\nUndo.Delete=\"Delete '%1'\"\nUndo.Rename=\"Rename '%1'\"\nUndo.SceneCollection.Switch=\"Switch to '%1'\"\nUndo.Item.Undo=\"Undo %1\"\nUndo.Item.Redo=\"Redo %1\"\nUndo.Sources.Multi=\"Delete %1 Sources\"\nUndo.Filters=\"Filter Changes on '%1'\"\nUndo.Filters.Paste.Single=\"Paste Filter '%1' to '%2'\"\nUndo.Filters.Paste.Multiple=\"Copy Filters from '%1' to '%2'\"\nUndo.Transform=\"Transform source(s) In '%1'\"\nUndo.Transform.Paste=\"Paste Transformation in '%1'\"\nUndo.Transform.Rotate=\"Rotation In '%1'\"\nUndo.Transform.Reset=\"Transform Reset In '%1'\"\nUndo.Transform.HFlip=\"Horizontal Flip In '%1'\"\nUndo.Transform.VFlip=\"Vertical Flip In '%1'\"\nUndo.Transform.FitToScreen=\"Fit to Screen In '%1'\"\nUndo.Transform.StretchToScreen=\"Stretch to Screen in '%1'\"\nUndo.Transform.Center=\"Center to Screen in '%1'\"\nUndo.Transform.VCenter=\"Vertical Center to Screen in '%1'\"\nUndo.Transform.HCenter=\"Horizontal Center to Screen in '%1'\"\nUndo.Volume.Change=\"Volume Change on '%1'\"\nUndo.Volume.Mute=\"Mute '%1'\"\nUndo.Volume.Unmute=\"Unmute '%1'\"\nUndo.Balance.Change=\"Audio Balance Change on '%1'\"\nUndo.SyncOffset.Change=\"Audio Sync Offset Change on '%1'\"\nUndo.MonitoringType.Change=\"Change Audio Monitoring on '%1'\"\nUndo.Mixers.Change=\"Change Audio Mixers on '%1'\"\nUndo.ForceMono.On=\"Enable Force Mono on '%1'\"\nUndo.ForceMono.Off=\"Disable Force Mono on '%1'\"\nUndo.Properties=\"Property Change on '%1'\"\nUndo.Scene.Duplicate=\"Duplicate Scene '%1'\"\nUndo.ShowTransition=\"Show Transition on '%1'\"\nUndo.HideTransition=\"Hide Transition on '%1'\"\nUndo.ShowSceneItem=\"Show '%1' in '%2'\"\nUndo.HideSceneItem=\"Hide '%1' in '%2'\"\nUndo.ReorderSources=\"Reorder sources in '%1'\"\nUndo.MoveUp=\"Move '%1' up in '%2'\"\nUndo.MoveDown=\"Move '%1' down in '%2'\"\nUndo.MoveToTop=\"Move '%1' to top in '%2'\"\nUndo.MoveToBottom=\"Move '%1' to bottom in '%2'\"\nUndo.PasteSource=\"Paste Source(s) in '%1'\"\nUndo.PasteSourceRef=\"Paste Source Reference(s) in '%1'\"\nUndo.GroupItems=\"Group Items into '%1'\"\nUndo.ScaleFiltering=\"Scale Filtering on '%1' in '%2'\"\nUndo.BlendingMode=\"Blending Mode on '%1' in '%2'\"\nUndo.BlendingMethod=\"Blending Method on '%1' in '%2'\"\nUndo.DeinterlacingMode=\"Deinterlacing Mode on '%1'\"\nUndo.DeinterlacingOrder=\"Deinterlacing Field Order on '%1'\"\n\n# transition name dialog\nTransitionNameDlg.Text=\"Please enter the name of the transition\"\nTransitionNameDlg.Title=\"Transition Name\"\n\n# title bar strings\nTitleBar.SafeMode=\"SAFE MODE\"\nTitleBar.PortableMode=\"Portable Mode\"\nTitleBar.Profile=\"Profile\"\nTitleBar.Scenes=\"Scenes\"\n\n# \"name already exists\" dialog box\nNameExists.Title=\"Name already exists\"\nNameExists.Text=\"The name is already in use.\"\n\nNoNameEntered.Title=\"Please enter a valid name\"\nNoNameEntered.Text=\"You cannot use empty names.\"\n\n# confirm start stream dialog box\nConfirmStart.Title=\"Start Stream?\"\nConfirmStart.Text=\"Are you sure you want to start the stream?\"\n\n# confirm stop stream dialog box\nConfirmStop.Title=\"Stop Stream?\"\nConfirmStop.Text=\"Are you sure you want to stop the stream?\"\n\n# confirm stop record dialog box\nConfirmStopRecord.Title=\"Stop Recording?\"\nConfirmStopRecord.Text=\"Are you sure you want to stop recording?\"\n\n# confirm bandwidth test dialog box\nConfirmBWTest.Title=\"Start Bandwidth Test?\"\nConfirmBWTest.Text=\"You have OBS configured in bandwidth test mode. This mode allows for network testing without your channel going live. Once you are done testing, you will need to disable it in order for viewers to be able to see your stream.\\n\\nDo you want to continue?\"\n\n# confirm exit dialog box\nConfirmExit.Title=\"Active Outputs\"\nConfirmExit.Text=\"OBS is still currently active. All streams/recordings will be shut down.\"\n\n# confirm delete dialog box\nConfirmRemove.Title=\"Confirm Remove\"\nConfirmRemove.Text=\"Are you sure you wish to remove '%1'?\"\nConfirmRemove.TextMultiple=\"Are you sure you wish to remove %1 items?\"\n\n# confirm reset to defaults dialog box\nConfirmReset.Title=\"Reset Properties\"\nConfirmReset.Text=\"Are you sure you wish to reset current properties to their defaults?\"\n\n# output start messages\nOutput.StartStreamFailed=\"Failed to start streaming\"\nOutput.StartRecordingFailed=\"Failed to start recording\"\nOutput.StartReplayFailed=\"Failed to start replay buffer\"\nOutput.StartVirtualCamFailed=\"Failed to start virtual camera\"\nOutput.StartFailedGeneric=\"Starting the output failed. Please check the log for details.\\n\\nNote: If you are using the NVENC or AMD encoders, make sure your video drivers are up to date.\"\n\n# replay buffer + pause warning message\nOutput.ReplayBuffer.PauseWarning.Title=\"Cannot save replays while paused\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Warning: Replays cannot be saved while recording is paused.\"\n\n# output connect messages\nOutput.ConnectFail.Title=\"Failed to connect\"\nOutput.ConnectFail.BadPath=\"Invalid Path or Connection URL. Please check your settings to confirm that they are valid.\"\nOutput.ConnectFail.ConnectFailed=\"Failed to connect to server\"\nOutput.ConnectFail.InvalidStream=\"Could not access the specified channel or stream key, please double-check your stream key. If it is correct, there may be a problem connecting to the server.\"\nOutput.ConnectFail.HdrDisabled=\"HDR output is currently disabled for this output.\"\nOutput.ConnectFail.Error=\"An unexpected error occurred when trying to connect to the server. More information in the log file.\"\nOutput.ConnectFail.Disconnected=\"Disconnected from server.\"\n\n# output streaming-related messages\nOutput.StreamEncodeError.Title=\"Encoding error\"\nOutput.StreamEncodeError.Msg=\"An encoder error occurred while streaming.\"\nOutput.StreamEncodeError.Msg.LastError=\"An encoder error occurred while streaming:<br><br>%1\"\n\n# output recording-related messages\nOutput.RecordFail.Title=\"Failed to start recording\"\nOutput.RecordFail.Unsupported=\"The output format is either unsupported or does not support more than one audio track. Please check your settings and try again.\"\nOutput.RecordNoSpace.Title=\"Insufficient disk space\"\nOutput.RecordNoSpace.Msg=\"There is not sufficient disk space to continue recording.\"\nOutput.RecordError.Title=\"Recording error\"\nOutput.RecordError.Msg=\"An unspecified error occurred while recording.\"\nOutput.RecordError.EncodeErrorMsg=\"An encoder error occurred while recording.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"An encoder error occurred while recording:<br><br>%1\"\n\n# output recording messages\nOutput.BadPath.Title=\"Bad File Path\"\n\n# \"Recording Path\" should match Basic.Settings.Output.Simple.SavePath\nOutput.BadPath.Text=\"The configured Recording Path could not be opened. Please check your Recording Path under Settings → Output → Recording.\"\n\n# broadcast setup messages\nOutput.NoBroadcast.Title=\"No Broadcast Configured\"\nOutput.NoBroadcast.Text=\"You need to set up a broadcast before you can start streaming.\"\nOutput.BroadcastStartFailed=\"Failed to start broadcast\"\nOutput.BroadcastStopFailed=\"Failed to stop broadcast\"\n\n# Log Upload Dialog for Application and Crash Logs\nLogUploadDialog.Title=\"OBS Studio Log File Upload\"\nLogUploadDialog.Labels.PrivacyNotice=\"Please read the <a href='https://obsproject.com/privacy-policy'>Privacy Policy</a> and its section regarding file uploads before uploading any files.\"\nLogUploadDialog.Labels.Progress=\"Log upload in progress. Please wait...\"\nLogUploadDialog.Labels.Description.AppLog=\"Your log file has been uploaded. You can now share the URL for debugging or support purposes.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Your crash report has been uploaded. You can now share the URL for debugging purposes.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Upload\"\nLogUploadDialog.Buttons.CopyURL=\"Copy Log URL\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analyze Log File\"\nLogUploadDialog.Buttons.RetryButton=\"Retry\"\nLogUploadDialog.Errors.Template=\"An error occurred while trying to upload the file:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"No file to upload found or file was empty.\"\n\n# remux dialog\nRemux.SourceFile=\"OBS Recording\"\nRemux.TargetFile=\"Target File\"\nRemux.Remux=\"Remux\"\nRemux.Stop=\"Stop Remuxing\"\nRemux.ClearFinished=\"Clear Finished Items\"\nRemux.ClearAll=\"Clear All Items\"\nRemux.OBSRecording=\"OBS Recording\"\nRemux.FinishedTitle=\"Remuxing finished\"\nRemux.Finished=\"Recording remuxed\"\nRemux.FinishedError=\"Recording remuxed, but the file may be incomplete\"\nRemux.SelectRecording=\"Select OBS Recording...\"\nRemux.SelectTarget=\"Select target file...\"\nRemux.FileExistsTitle=\"Target files exist\"\nRemux.FileExists=\"The following target files already exist. Do you want to replace them?\"\nRemux.ExitUnfinishedTitle=\"Remuxing in progress\"\nRemux.ExitUnfinished=\"Remuxing is not finished, stopping now may render the target file unusable.\\nAre you sure you want to stop remuxing?\"\nRemux.HelpText=\"Drop files in this window to remux, or select an empty \\\"OBS Recording\\\" cell to browse for a file.\"\nRemux.NoFilesAddedTitle=\"No remuxing file added\"\nRemux.NoFilesAdded=\"No file is added to remux. Drop a folder containing one or more video files.\"\n\n# missing file dialog\nMissingFiles=\"Missing Files\"\nMissingFiles.MissingFile=\"Missing File\"\nMissingFiles.NewFile=\"New File\"\nMissingFiles.HelpText=\"Some files are missing since you last used OBS.\"\nMissingFiles.Clear=\"<cleared>\"\nMissingFiles.NumFound=\"Found %1 of %2\"\nMissingFiles.Search=\"Search Directory...\"\nMissingFiles.SelectFile=\"Select file...\"\nMissingFiles.SelectDir=\"Select Folder to Search in\"\nMissingFiles.State=\"State\"\nMissingFiles.Missing=\"Missing\"\nMissingFiles.Replaced=\"Replaced\"\nMissingFiles.Cleared=\"Cleared\"\nMissingFiles.Found=\"Found\"\nMissingFiles.AutoSearch=\"Additional file matches found\"\nMissingFiles.AutoSearchText=\"OBS has found additional matches for missing files in that directory. Would you like to add them?\"\nMissingFiles.NoMissing.Title=\"Missing Files Check\"\nMissingFiles.NoMissing.Text=\"No files appear to be missing.\"\n\n# macOS permissions dialog\nMacPermissions.MenuAction=\"Review App Permissions...\"\nMacPermissions.Title=\"Review App Permissions\"\nMacPermissions.Description=\"OBS Studio requires your permission to be able to provide certain features. It is recommended to enable these permissions, but they are not required to use the app. You can always enable them later.\"\nMacPermissions.Description.OpenDialog=\"You can re-open this dialog via the OBS Studio menu.\"\nMacPermissions.AccessGranted=\"Access Granted\"\nMacPermissions.RequestAccess=\"Request Access\"\nMacPermissions.OpenPreferences=\"Open %1 Preferences\"\nMacPermissions.Item.ScreenRecording=\"Screen Recording\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS requires this permission to be able to capture your screen.\"\nMacPermissions.Item.Camera=\"Camera\"\nMacPermissions.Item.Camera.Details=\"This permission is needed in order to capture content from a webcam or capture card.\"\nMacPermissions.Item.Microphone=\"Microphone\"\nMacPermissions.Item.Microphone.Details=\"OBS requires this permission if you want to capture your microphone or an external audio device.\"\nMacPermissions.Item.InputMonitoring=\"Input Monitoring\"\nMacPermissions.Item.InputMonitoring.Details=\"This permission is required for hotkeys to work while OBS is in the background.\"\nMacPermissions.Item.Accessibility=\"Accessibility\"\nMacPermissions.Item.Accessibility.Details=\"On older installations, OBS may be listed in \\\"Accessibility\\\" instead of \\\"Input Monitoring\\\".\"\nMacPermissions.Continue=\"Continue\"\n\n# Source leak error message\nSourceLeak.Title=\"Source Cleanup Error\"\nSourceLeak.Text=\"There was a problem while changing scene collections and some sources could not be unloaded. This issue is typically caused by plugins that are not releasing resources properly. Please ensure that any plugins you are using are up to date.\\n\\nOBS Studio will now exit to prevent any potential data corruption.\"\n\n# audio device names\nBasic.DesktopDevice1=\"Desktop Audio\"\nBasic.DesktopDevice2=\"Desktop Audio 2\"\nBasic.AuxDevice1=\"Mic/Aux\"\nBasic.AuxDevice2=\"Mic/Aux 2\"\nBasic.AuxDevice3=\"Mic/Aux 3\"\nBasic.AuxDevice4=\"Mic/Aux 4\"\n\n# startup scene/capture\nBasic.Scene=\"Scene\"\nBasic.DisplayCapture=\"Display Capture\"\n\n# display context menu\nBasic.Main.PreviewConextMenu.Enable=\"Enable Preview\"\n\n# disable preview\nBasic.Main.Preview.Disable=\"Disable Preview\"\n\n# scale filtering\nScaleFiltering=\"Scale Filtering\"\nScaleFiltering.Point=\"Point\"\nScaleFiltering.Bilinear=\"Bilinear\"\nScaleFiltering.Bicubic=\"Bicubic\"\nScaleFiltering.Lanczos=\"Lanczos\"\nScaleFiltering.Area=\"Area\"\n\n# blending methods\nBlendingMethod=\"Blending Method\"\nBlendingMethod.Default=\"Default\"\nBlendingMethod.SrgbOff=\"SRGB Off\"\n\n# blending modes\nBlendingMode=\"Blending Mode\"\nBlendingMode.Normal=\"Normal\"\nBlendingMode.Additive=\"Add\"\nBlendingMode.Subtract=\"Subtract\"\nBlendingMode.Screen=\"Screen\"\nBlendingMode.Multiply=\"Multiply\"\nBlendingMode.Lighten=\"Lighten\"\nBlendingMode.Darken=\"Darken\"\n\n# deinterlacing\nDeinterlacing=\"Deinterlacing\"\nDeinterlacing.Discard=\"Discard\"\nDeinterlacing.Retro=\"Retro\"\nDeinterlacing.Blend=\"Blend\"\nDeinterlacing.Blend2x=\"Blend 2x\"\nDeinterlacing.Linear=\"Linear\"\nDeinterlacing.Linear2x=\"Linear 2x\"\nDeinterlacing.Yadif=\"Yadif\"\nDeinterlacing.Yadif2x=\"Yadif 2x\"\nDeinterlacing.TopFieldFirst=\"Top Field First\"\nDeinterlacing.BottomFieldFirst=\"Bottom Field First\"\n\n# volume control accessibility text\nVolControl.SliderUnmuted=\"Volume slider for '%1':\"\nVolControl.SliderMuted=\"Volume slider for '%1': (currently muted)\"\nVolControl.Mute=\"Mute '%1'\"\nVolControl.Properties=\"Properties for '%1'\"\nVolControl.UnassignedWarning.Title=\"Unassigned Audio Source\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" is not assigned to any audio tracks and it will not be audible in streams or recordings.\\n\\nTo assign an audio source to a track, open the Advanced Audio Properties via the Edit menu on the menu bar, or the cog button in the mixer dock toolbar.\"\n\n# add scene dialog\nBasic.Main.AddSceneDlg.Title=\"Add Scene\"\nBasic.Main.AddSceneDlg.Text=\"Please enter the name of the scene\"\n\n# add scene suggested name\nBasic.Main.DefaultSceneName.Text=\"Scene %1\"\n\n# add scene collection dialog\nBasic.Main.AddSceneCollection.Title=\"Add Scene Collection\"\nBasic.Main.AddSceneCollection.Text=\"Please enter the name of the scene collection\"\n\n# rename scene collection dialog\nBasic.Main.RenameSceneCollection.Title=\"Rename Scene Collection\"\n\n# remigrate scene collection dialog\nBasic.Main.RemigrateSceneCollection.Title=\"Update Scene Collection Resolution\"\nBasic.Main.RemigrateSceneCollection.Text=\"Do you want to update the scene collection resolution of \\\"%1\\\" to match the current profile's canvas resolution of %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Cannot update scene collection resolution while outputs are active.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Failed to update scene collection resolution. The original resolution is unknown.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Reset not possible: Changing OBS resolution failed.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Reset not possible: Current resolution already the base resolution of scene collection.\"\n\n# add profile dialog\nAddProfile.Title=\"Add Profile\"\nAddProfile.Text=\"Please enter the name of the profile\"\nAddProfile.WizardCheckbox=\"Show auto-configuration wizard\"\n\n# rename profile dialog\nRenameProfile.Title=\"Rename Profile\"\n\n# rename audio source in mixer\nBasic.Main.MixerRename.Title=\"Rename Audio Source\"\nBasic.Main.MixerRename.Text=\"Please enter the name of the audio source\"\n\n\n# preview window disabled\nBasic.Main.PreviewDisabled=\"Preview is currently disabled\"\n\n# add source dialog\nBasic.SourceSelect=\"Create/Select Source\"\nBasic.SourceSelect.CreateNew=\"Create new\"\nBasic.SourceSelect.AddExisting=\"Add Existing\"\nBasic.SourceSelect.AddVisible=\"Make source visible\"\n\n# source box\nBasic.Main.Sources.Visibility=\"Visibility\"\nBasic.Main.Sources.VisibilityDescription=\"Controls the visibility of '%1' in the canvas\"\nBasic.Main.Sources.Lock=\"Lock\"\nBasic.Main.Sources.LockDescription=\"Locks the position and scale of '%1' in the canvas\"\n\n# properties window\nBasic.PropertiesWindow=\"Properties for '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (autoselect: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Select color\"\nBasic.PropertiesWindow.SelectFont=\"Select font\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Pick a Font\"\nBasic.PropertiesWindow.ConfirmTitle=\"Settings Changed\"\nBasic.PropertiesWindow.Confirm=\"There are unsaved changes. Do you want to keep them?\"\nBasic.PropertiesWindow.NoProperties=\"No properties available\"\nBasic.PropertiesWindow.AddFiles=\"Add Files\"\nBasic.PropertiesWindow.AddDir=\"Add Directory\"\nBasic.PropertiesWindow.AddURL=\"Add Path/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Add directory to '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Add files to '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Add entry to '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Edit entry from '%1'\"\n\n# properties view\nBasic.PropertiesView.FPS.Simple=\"Simple FPS Values\"\nBasic.PropertiesView.FPS.Rational=\"Rational FPS Values\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Valid FPS Ranges:\"\nBasic.PropertiesView.UrlButton.Text=\"Open this link in your default web browser?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"URL: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Open URL\"\n\n# interaction window\nBasic.InteractionWindow=\"Interacting with '%1'\"\n\n# status bar\nBasic.StatusBar.Reconnecting=\"Disconnected, reconnecting in %2 second(s) (attempt %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Attempting to reconnect... (attempt %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Reconnection successful\"\nBasic.StatusBar.Delay=\"Delay (%1 sec)\"\nBasic.StatusBar.DelayStartingIn=\"Delay (starting in %1 sec)\"\nBasic.StatusBar.DelayStoppingIn=\"Delay (stopping in %1 sec)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Delay (stopping in %1 sec, starting in %2 sec)\"\nBasic.StatusBar.RecordingSavedTo=\"Recording saved to '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Replay buffer saved to '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Screenshot saved to '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Recording auto remuxed to '%1'\"\n\n# filters window\nBasic.Filters=\"Filters\"\nBasic.Filters.AsyncFilters=\"Audio/Video Filters\"\nBasic.Filters.AudioFilters=\"Audio Filters\"\nBasic.Filters.EffectFilters=\"Effect Filters\"\nBasic.Filters.Title=\"Filters for '%1'\"\nBasic.Filters.AddFilter.Title=\"Filter name\"\nBasic.Filters.AddFilter.Text=\"Please specify the name of the filter\"\n\n# transform window\nBasic.TransformWindow=\"Scene Item Transform\"\nBasic.TransformWindow.Position=\"Position\"\nBasic.TransformWindow.PositionX=\"X\"\nBasic.TransformWindow.PositionY=\"Y\"\nBasic.TransformWindow.Rotation=\"Rotation\"\nBasic.TransformWindow.Size=\"Size\"\nBasic.TransformWindow.Width=\"Width\"\nBasic.TransformWindow.Height=\"Height\"\nBasic.TransformWindow.Alignment=\"Alignment\"\nBasic.TransformWindow.BoundsType=\"Bounds Type\"\nBasic.TransformWindow.BoundsAlignment=\"Bounds Alignment\"\nBasic.TransformWindow.Bounds=\"Bounds\"\nBasic.TransformWindow.BoundsWidth=\"Bounding Box Width\"\nBasic.TransformWindow.BoundsHeight=\"Bounding Box Height\"\nBasic.TransformWindow.CropToBounds=\"Crop Bounds\"\nBasic.TransformWindow.Crop=\"Crop\"\nBasic.TransformWindow.CropLeft=\"Crop Left\"\nBasic.TransformWindow.CropRight=\"Crop Right\"\nBasic.TransformWindow.CropTop=\"Crop Top\"\nBasic.TransformWindow.CropBottom=\"Crop Bottom\"\nBasic.TransformWindow.Accessible.PositionX=\"X Position\"\nBasic.TransformWindow.Accessible.PositionY=\"Y Position\"\n\nBasic.TransformWindow.Alignment.TopLeft=\"Top Left\"\nBasic.TransformWindow.Alignment.TopCenter=\"Top Center\"\nBasic.TransformWindow.Alignment.TopRight=\"Top Right\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Center Left\"\nBasic.TransformWindow.Alignment.Center=\"Center\"\nBasic.TransformWindow.Alignment.CenterRight=\"Center Right\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Bottom Left\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Bottom Center\"\nBasic.TransformWindow.Alignment.BottomRight=\"Bottom Right\"\n\nBasic.TransformWindow.BoundsType.None=\"Automatic\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Maximum size only\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Fit\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Cover\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Fill Width\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Fill Height\"\nBasic.TransformWindow.BoundsType.Stretch=\"Stretch\"\n\nBasic.TransformWindow.Title=\"Edit Transform for '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"No source selected\"\n\n# no scene warning\nBasic.Main.AddSourceHelp.Title=\"Cannot Add Source\"\nBasic.Main.AddSourceHelp.Text=\"You need to have at least 1 scene to add a source.\"\n\n# basic mode main window\nBasic.Main.Scenes=\"Scenes\"\nBasic.Main.Sources=\"Sources\"\nBasic.Main.Source=\"Source\"\nBasic.Main.Controls=\"Controls\"\nBasic.Main.PreparingStream=\"Preparing...\"\nBasic.Main.Connecting=\"Connecting...\"\nBasic.Main.StartRecording=\"Start Recording\"\nBasic.Main.StartReplayBuffer=\"Start Replay Buffer\"\nBasic.Main.SaveReplay=\"Save Replay\"\nBasic.Main.StartStreaming=\"Start Streaming\"\nBasic.Main.StartBroadcast=\"Go Live\"\nBasic.Main.StartVirtualCam=\"Start Virtual Camera\"\nBasic.Main.StopRecording=\"Stop Recording\"\nBasic.Main.PauseRecording=\"Pause Recording\"\nBasic.Main.UnpauseRecording=\"Unpause Recording\"\nBasic.Main.SplitFile=\"Split Recording File\"\nBasic.Main.AddChapterMarker=\"Add Chapter Marker (Hybrid MP4/MOV only)\"\nBasic.Main.StoppingRecording=\"Stopping Recording...\"\nBasic.Main.StopReplayBuffer=\"Stop Replay Buffer\"\nBasic.Main.StoppingReplayBuffer=\"Stopping Replay Buffer...\"\nBasic.Main.SetupBroadcast=\"Manage Broadcast\"\nBasic.Main.StopStreaming=\"Stop Streaming\"\nBasic.Main.StopBroadcast=\"End Broadcast\"\nBasic.Main.AutoStopEnabled=\"(Auto Stop)\"\nBasic.Main.StoppingStreaming=\"Stopping Stream...\"\nBasic.Main.ForceStopStreaming=\"Stop Streaming (discard delay)\"\nBasic.Main.ShowContextBar=\"Show Source Toolbar\"\nBasic.Main.HideContextBar=\"Hide Source Toolbar\"\nBasic.Main.StopVirtualCam=\"Stop Virtual Camera\"\nBasic.Main.Group=\"Group %1\"\nBasic.Main.GroupItems=\"Group Selected Items\"\nBasic.Main.Ungroup=\"Ungroup\"\nBasic.Main.GridMode=\"Grid Mode\"\nBasic.Main.ListMode=\"List Mode\"\n\n# virtual camera configuration\nBasic.Main.VirtualCamConfig=\"Configure Virtual Camera\"\nBasic.VCam.VirtualCamera=\"Virtual Camera\"\nBasic.VCam.OutputType=\"Output Type\"\nBasic.VCam.OutputSelection=\"Output Selection\"\nBasic.VCam.OutputType.Program=\"Program (Default)\"\nBasic.VCam.OutputSelection.NoSelection=\"No selection for this output type\"\nBasic.VCam.RestartWarning=\"The virtual camera will be restarted to apply this change\"\n\n# basic mode file menu\nBasic.MainMenu.File=\"&File\"\nBasic.MainMenu.File.Export=\"&Export\"\nBasic.MainMenu.File.Import=\"&Import\"\nBasic.MainMenu.File.ShowRecordings=\"Show &Recordings\"\nBasic.MainMenu.File.Remux=\"Re&mux Recordings\"\nBasic.MainMenu.File.Settings=\"&Settings\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Show Settings Folder\"\nBasic.MainMenu.File.ShowProfileFolder=\"Show Profile Folder\"\nBasic.MainMenu.File.ShowMissingFiles=\"Check for Missing Files\"\nBasic.MainMenu.File.Exit=\"E&xit\"\n\n# basic mode edit menu\nBasic.MainMenu.Edit=\"&Edit\"\nBasic.MainMenu.Edit.Undo=\"&Undo\"\nBasic.MainMenu.Edit.Redo=\"&Redo\"\nBasic.MainMenu.Edit.LockPreview=\"&Lock Preview\"\nBasic.MainMenu.Edit.Scale=\"Preview &Scaling\"\nBasic.MainMenu.Edit.Scale.Window=\"Scale to Window\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Canvas (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Output (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Scaled (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Zoom In\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Zoom Out\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Reset Zoom\"\nBasic.MainMenu.Edit.Transform=\"&Transform\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Edit Transform\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Copy Transform\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Paste Transform\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Reset Transform\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Rotate 90 degrees CW\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Rotate 90 degrees CCW\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Rotate 180 degrees\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Flip &Horizontal\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Flip &Vertical\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"&Fit to screen\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&Stretch to screen\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Center to screen\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Center Vertically\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Center Horizontally\"\nBasic.MainMenu.Edit.Order=\"&Order\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Move &Up\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Move &Down\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Move to &Top\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Move to &Bottom\"\nBasic.MainMenu.Edit.AdvAudio=\"&Advanced Audio Properties\"\n\n# basic mode view menu\nBasic.MainMenu.View=\"&View\"\nBasic.MainMenu.View.Toolbars=\"&Toolbars\"\nBasic.MainMenu.View.ListboxToolbars=\"Dock Toolbars\"\nBasic.MainMenu.View.ContextBar=\"Source Toolbar\"\nBasic.MainMenu.View.SourceIcons=\"Source &Icons\"\nBasic.MainMenu.View.StatusBar=\"&Status Bar\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Fullscreen Interface\"\nBasic.MainMenu.View.ResetUI=\"&Reset UI\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Always On Top\"\nBasic.MainMenu.View.SceneListMode=\"Scene List Mode\"\n\n\n#basic mode docks menu\nBasic.MainMenu.Docks=\"&Docks\"\nBasic.MainMenu.Docks.ResetDocks=\"&Reset Docks\"\nBasic.MainMenu.Docks.LockDocks=\"&Lock Docks\"\nBasic.MainMenu.Docks.SideDocks=\"&Full-Height Docks\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Custom Browser Docks\"\n\n# basic mode profile/scene collection menus\nBasic.MainMenu.Import=\"Import...\"\nBasic.MainMenu.Export=\"Export...\"\nBasic.MainMenu.SceneCollection=\"&Scene Collection\"\nBasic.MainMenu.Profile=\"&Profile\"\nBasic.MainMenu.Profile.Import=\"Import Profile\"\nBasic.MainMenu.Profile.Export=\"Export Profile\"\nBasic.MainMenu.SceneCollection.Import=\"Import Scene Collection\"\nBasic.MainMenu.SceneCollection.Export=\"Export Scene Collection\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Reset Base Resolution\"\nBasic.MainMenu.SceneCollection.Migrate=\"Set Base Resolution\"\nBasic.MainMenu.Profile.Exists=\"The profile already exists\"\n\n# basic mode help menu\nBasic.MainMenu.Tools=\"&Tools\"\n\n# basic mode help menu\nBasic.MainMenu.Help=\"&Help\"\nBasic.MainMenu.Help.HelpPortal=\"Help &Portal\"\nBasic.MainMenu.Help.Website=\"Visit &Website\"\nBasic.MainMenu.Help.Discord=\"Join &Discord Server\"\nBasic.MainMenu.Help.WhatsNew=\"What's New\"\nBasic.MainMenu.Help.Logs=\"&Log Files\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"&Show Log Files\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Upload &Current Log File\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Upload &Previous Log File\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&View Current Log\"\nBasic.MainMenu.Help.ReleaseNotes=\"Release Notes\"\nBasic.MainMenu.Help.CheckForUpdates=\"Check For Updates\"\nBasic.MainMenu.Help.Repair=\"Check File Integrity\"\nBasic.MainMenu.Help.RestartSafeMode=\"Restart in Safe Mode\"\nBasic.MainMenu.Help.RestartNormal=\"Restart in Normal Mode\"\nBasic.MainMenu.Help.CrashLogs=\"Crash &Reports\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"&Show Crash Reports\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Upload &Previous Crash Report\"\nBasic.MainMenu.Help.About=\"&About\"\n\n# basic mode settings dialog\nBasic.Settings.ProgramRestart=\"The program must be restarted for these settings to take effect.\"\nBasic.Settings.ConfirmTitle=\"Confirm Changes\"\nBasic.Settings.Confirm=\"You have unsaved changes. Save changes?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 is controlling some of your stream settings\"\n\n# basic mode 'general' settings\nBasic.Settings.General=\"General\"\nBasic.Settings.General.Language=\"Language\"\nBasic.Settings.General.Updater=\"Updates\"\nBasic.Settings.General.UpdateChannel=\"Update Channel\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Disabled)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Default)\"\nBasic.Settings.General.EnableAutoUpdates=\"Automatically check for updates on startup\"\nBasic.Settings.General.OpenStatsOnStartup=\"Open stats dialog on startup\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Hide OBS windows from screen capture\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"This setting will hide all non-projector OBS Studio windows from being captured by OBS and affects \\nother applications, such as conferencing, screen sharing, remote support, screenshots, and other capture software.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Enabling this option hides all non-projector OBS Studio windows from being captured by OBS and affects other applications, such as conferencing, screen sharing, remote support, screenshots, and other capture software.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Show confirmation dialog when starting streams\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Show confirmation dialog when stopping streams\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Show confirmation dialog when stopping recording\"\nBasic.Settings.General.Projectors=\"Projectors\"\nBasic.Settings.General.HideProjectorCursor=\"Hide cursor over projectors\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Make projectors always on top\"\nBasic.Settings.General.Snapping=\"Source Alignment Snapping\"\nBasic.Settings.General.ScreenSnapping=\"Snap Sources to edge of screen\"\nBasic.Settings.General.CenterSnapping=\"Snap Sources to horizontal and vertical center\"\nBasic.Settings.General.SourceSnapping=\"Snap Sources to other sources\"\nBasic.Settings.General.SnapDistance=\"Snap Sensitivity\"\nBasic.Settings.General.SpacingHelpers=\"Show pixel alignment guides\"\nBasic.Settings.General.RecordWhenStreaming=\"Automatically record when streaming\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Keep recording when stream stops\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Automatically start replay buffer when streaming\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Keep replay buffer active when stream stops\"\nBasic.Settings.General.SysTray=\"System Tray\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimize to system tray when started\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Always minimize to system tray instead of task bar\"\nBasic.Settings.General.SaveProjectors=\"Save projectors on exit\"\nBasic.Settings.General.CloseExistingProjectors=\"Limit one fullscreen projector per screen\"\nBasic.Settings.General.Preview=\"Preview\"\nBasic.Settings.General.OverflowHidden=\"Hide overflow\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Overflow always visible\"\nBasic.Settings.General.OverflowSelectionHidden=\"Show overflow even when source is invisible\"\nBasic.Settings.General.Importers=\"Importers\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Search known locations for scene collections when importing\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Transition to scene when double-clicked\"\nBasic.Settings.General.StudioPortraitLayout=\"Enable portrait/vertical layout\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Show preview/program labels\"\nBasic.Settings.General.Multiview=\"Multiview\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Click to switch between scenes\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Show scene names\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Draw safe areas (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Multiview Layout\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontal, Top (8 Scenes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontal, Bottom (8 Scenes)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertical, Left (8 Scenes)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertical, Right (8 Scenes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontal, Top (18 Scenes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontal, Top (24 Scenes)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Scenes only (4 Scenes)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Scenes only (9 Scenes)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Scenes only (16 Scenes)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Scenes only (25 Scenes)\"\n\n# default channel name translations\nBasic.Settings.General.ChannelName.stable=\"Stable\"\nBasic.Settings.General.ChannelDescription.stable=\"Latest stable release\"\nBasic.Settings.General.ChannelName.beta=\"Betas / Release Candidates\"\nBasic.Settings.General.ChannelDescription.beta=\"Potentially unstable pre-release versions\"\n\n# basic mode 'appearance' settings\nBasic.Settings.Appearance=\"Appearance\"\nBasic.Settings.Appearance.General=\"General\"\nBasic.Settings.Appearance.General.Theme=\"Theme\"\nBasic.Settings.Appearance.General.Variant=\"Style\"\nBasic.Settings.Appearance.General.NoVariant=\"No Styles Available\"\nBasic.Settings.Appearance.FontScale=\"Font Size\"\nBasic.Settings.Appearance.Density=\"Density\"\nBasic.Settings.Appearance.Classic=\"Classic\"\nBasic.Settings.Appearance.Compact=\"Compact\"\nBasic.Settings.Appearance.Normal=\"Normal\"\nBasic.Settings.Appearance.Comfortable=\"Comfortable\"\nBasic.Settings.Appearance.OptionsWarning=\"Some appearance options are not available for this style.\"\n\n# basic mode 'stream' settings\nBasic.Settings.Stream=\"Stream\"\nBasic.Settings.Stream.Destination=\"Destination\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Use authentication\"\nBasic.Settings.Stream.Custom.Username=\"Username\"\nBasic.Settings.Stream.Custom.Password=\"Password\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: enter the srp_username.\\nRTMP: enter the username.\\nSRT: not used.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: enter the srp_password.\\nRTMP: enter the password.\\nSRT: enter the encryption passphrase.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Enable Bandwidth Test Mode\"\nBasic.Settings.Stream.TTVAddon=\"Twitch Chat Add-Ons\"\nBasic.Settings.Stream.TTVAddon.None=\"None\"\nBasic.Settings.Stream.TTVAddon.BTTV=\"BetterTTV\"\nBasic.Settings.Stream.TTVAddon.FFZ=\"FrankerFaceZ\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV and FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Missing Stream Setup\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Open Settings\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL and Stream Key are missing.\\n\\nOpen settings to enter the URL and Stream Key in the 'stream' tab.\"\nBasic.Settings.Stream.MissingUrl=\"Stream URL is missing.\\n\\nOpen settings to enter the URL in the 'Stream' tab.\"\nBasic.Settings.Stream.MissingStreamKey=\"Stream key is missing.\\n\\nOpen settings to enter the stream key in the 'Stream' tab.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignore streaming service setting recommendations\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Override Recommended Settings\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Warning: Ignoring the service's limitations may result in degraded stream quality or prevent you from streaming.\\n\\nContinue?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maximum Video Bitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maximum Audio Bitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maximum Resolution: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maximum FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Specify Custom Server...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Custom Server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Enable %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maximum Streaming Bandwidth\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Auto\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maximum Video Tracks\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Auto\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Enable stream dump to FLV (uses simple recording file settings)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Config Override (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Enable Config Override\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Multitrack Video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Additional Canvas\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Simulcast\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Simulcast allows you to encode and send multiple video qualities. <a href='https://obsproject.com/kb/whip-streaming-guide'>Learn More</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Total Layers\"\nBasic.Settings.Stream.AdvancedOptions=\"Advanced Options\"\n\n# basic mode 'output' settings\nBasic.Settings.Output=\"Output\"\nBasic.Settings.Output.Format=\"Recording Format\"\nBasic.Settings.Output.Format.MKV=\"Matroska Video (.mkv)\"\nBasic.Settings.Output.Format.FLV=\"Flash Video (.flv)\"\nBasic.Settings.Output.Format.MP4=\"MPEG-4 (.mp4)\"\nBasic.Settings.Output.Format.MOV=\"QuickTime (.mov)\"\nBasic.Settings.Output.Format.TS=\"MPEG-TS (.ts)\"\nBasic.Settings.Output.Format.HLS=\"HLS (.m3u8 + .ts)\"\nBasic.Settings.Output.Format.hMP4=\"Hybrid MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hybrid MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmented MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmented MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmented MOV writes the recording in chunks and does not require the same finalization as traditional MOV files.\\nThis ensures the file remains playable even if writing to disk is interrupted, for example, as a result of a BSOD or power loss.\\n\\nThis may not be compatible with all players and editors. Use File → Remux Recordings to convert the file into a more compatible format if necessary.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmented MP4 writes the recording in chunks and does not require the same finalization as traditional MP4 files.\\nThis ensures the file remains playable even if writing to disk is interrupted, for example, as a result of a BSOD or power loss.\\n\\nThis may not be compatible with all players and editors. Use File → Remux Recordings to convert the file into a more compatible format if necessary.\"\nBasic.Settings.Output.Encoder.Video=\"Video Encoder\"\nBasic.Settings.Output.Encoder.Audio=\"Audio Encoder\"\nBasic.Settings.Output.SelectDirectory=\"Select Recording Directory\"\nBasic.Settings.Output.DynamicBitrate=\"Dynamically change bitrate to manage congestion\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dynamically change bitrate to manage congestion (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Instead of dropping frames to reduce congestion, dynamically changes bitrate on the fly.\\n\\nNote that this can increase delay to viewers if there is significant sudden congestion.\\nWhen the bitrate drops, it can take up to a few minutes to restore.\\n\\nCurrently only supported for RTMP.\"\nBasic.Settings.Output.Mode=\"Output Mode\"\nBasic.Settings.Output.Mode.Simple=\"Simple\"\nBasic.Settings.Output.Mode.Adv=\"Advanced\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg Output\"\nBasic.Settings.Output.UseReplayBuffer=\"Enable Replay Buffer\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maximum Replay Time\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maximum Memory\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Estimated memory usage: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Warning: Estimated memory usage of %1 MiB is larger than recommended maximum of %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Cannot estimate memory usage. Please set maximum memory limit.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Replay Buffer Filename Prefix\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Suffix\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Replay Buffer cannot be used when recording type is set to Custom Output (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Recording Path\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Recording Quality\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Same as stream\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"High Quality, Medium File Size\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Indistinguishable Quality, Large File Size\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Lossless Quality, Tremendously Large File Size\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Warning: The streaming video bitrate will be set to %1, which is the upper limit for the current streaming service.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Warning: The streaming audio bitrate will be set to %1, which is the upper limit for the current streaming service.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Warning: Recordings cannot be paused if the recording quality is set to \\\"Same as stream\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Warning: The currently selected recording format is incompatible with the selected stream encoder(s).\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Warning: Recording with a software encoder at a different quality than the stream will require extra CPU usage if you stream and record at the same time.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Warning: Lossless quality generates tremendously large file sizes! Lossless quality can use upward of 7 gigabytes of disk space per minute at high resolutions and framerates. Lossless is not recommended for long recordings unless you have a very large amount of disk space available. Replay buffer is unavailable when using lossless quality.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Are you sure you want to use lossless quality?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Lossless quality warning!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Software (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Hardware (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Hardware (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Hardware (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Hardware (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Hardware (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Hardware (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Hardware (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Hardware (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Hardware (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Hardware (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264 low CPU usage preset, increases file size)\"\nBasic.Settings.Output.Simple.Codec.AAC=\"AAC\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Default)\"\nBasic.Settings.Output.Simple.Codec.Opus=\"Opus\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD Track (Uses Track 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Audio Track\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Incompatible Resolution/Framerate\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"This streaming service does not support your current output resolution and/or framerate. They will be changed to the closest compatible value:\\n\\n%1\\n\\nDo you want to continue?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolution: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"FPS: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Incompatible Encoder\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"The streaming service \\\"%1\\\" does not support the encoder \\\"%2\\\". The encoder will be changed to \\\"%3\\\".\\n\\nDo you want to continue?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"The streaming service \\\"%1\\\" does not support encoders \\\"%2\\\" and \\\"%3\\\". These encoders will be changed to \\\"%4\\\" and \\\"%5\\\".\\n\\nDo you want to continue?\"\nBasic.Settings.Output.VideoBitrate=\"Video Bitrate\"\nBasic.Settings.Output.AudioBitrate=\"Audio Bitrate\"\nBasic.Settings.Output.Reconnect=\"Automatically Reconnect\"\nBasic.Settings.Output.RetryDelay=\"Retry Delay\"\nBasic.Settings.Output.MaxRetries=\"Maximum Retries\"\nBasic.Settings.Output.Advanced=\"Enable Custom Encoder Settings (Advanced)\"\nBasic.Settings.Output.EncoderPreset=\"Encoder Preset\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (low CPU usage, lowest quality)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (default) (medium CPU usage, standard quality)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (high CPU usage, high quality)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Custom Encoder Settings\"\nBasic.Settings.Output.CustomMuxerSettings=\"Custom Muxer Settings\"\nBasic.Settings.Output.NoSpaceFileName=\"Generate File Name without Space\"\n\n# basic mode 'output' settings - advanced section\nBasic.Settings.Output.Adv.Rescale=\"Rescale Output\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Disabled\"\nBasic.Settings.Output.Adv.AudioTrack=\"Audio Track\"\nBasic.Settings.Output.Adv.Streaming=\"Streaming\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Streaming Settings\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Track 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Track 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Track 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Track 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Track 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Track 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD Track\"\nBasic.Settings.Output.Adv.Encoder=\"Encoder Settings\"\n\n# basic mode 'output' settings - advanced section - recording subsection\nBasic.Settings.Output.Adv.Recording=\"Recording\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Recording Settings\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Recording Type\"\nBasic.Settings.Output.Adv.Recording.Type=\"Type\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standard\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Custom Output (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Use stream encoder)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Filename Formatting\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Overwrite if file exists\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Custom Output (FFmpeg) is provided with no safeguards.\\nConsider using the \\\"Standard\\\" recording type.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg Output Type\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Output to URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Output to File\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Common recording formats\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"All Files\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"File path or URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Container Format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Audio\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Video\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Default Format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Container Format Description\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Audio/Video Codec guessed from File path or URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Default Encoder\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Disable Encoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Video Encoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Video Encoder Settings (if any)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Audio Encoder\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Audio Encoder Settings (if any)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxer Settings (if any)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Keyframe interval (frames)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Show all codecs (even if potentially incompatible)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg Settings\"\nBasic.Settings.Output.EnableSplitFile=\"Automatic File Splitting\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Split by Time\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Split by Size\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Only split manually\"\nBasic.Settings.Output.SplitFile.Time=\"Split Time\"\nBasic.Settings.Output.SplitFile.Size=\"Split Size\"\n\n# Screenshot\nScreenshot=\"Screenshot Output\"\nScreenshot.SourceHotkey=\"Screenshot Selected Source\"\nScreenshot.StudioProgram=\"Save Program Screenshot\"\nScreenshot.Preview=\"Save Preview Screenshot\"\nScreenshot.Scene=\"Save Scene Screenshot\"\nScreenshot.Source=\"Save Source Screenshot\"\n\n# basic mode 'output' settings - advanced section - recording subsection - completer\nFilenameFormatting.completer=\"%CCYY-%MM-%DD %hh-%mm-%ss\\n%YY-%MM-%DD %hh-%mm-%ss\\n%Y-%m-%d %H-%M-%S\\n%y-%m-%d %H-%M-%S\\n%a %Y-%m-%d %H-%M-%S\\n%A %Y-%m-%d %H-%M-%S\\n%Y-%b-%d %H-%M-%S\\n%Y-%B-%d %H-%M-%S\\n%Y-%m-%d %I-%M-%S-%p\\n%Y-%m-%d %H-%M-%S-%z\\n%Y-%m-%d %H-%M-%S-%Z\\n%FPS\\n%CRES\\n%ORES\\n%VF\"\n\n# basic mode 'output' settings - advanced section - recording subsection - TT\nFilenameFormatting.TT.CCYY=\"Year, four digits\"\nFilenameFormatting.TT.YY=\"Year, last two digits (00-99)\"\nFilenameFormatting.TT.MM=\"Month as a decimal number (01-12)\"\nFilenameFormatting.TT.DD=\"Day of the month, zero-padded (01-31)\"\nFilenameFormatting.TT.hh=\"Hour in 24h format (00-23)\"\nFilenameFormatting.TT.mm=\"Minute (00-59)\"\nFilenameFormatting.TT.ss=\"Second (00-59)\"\nFilenameFormatting.TT.Percent=\"A % sign\"\nFilenameFormatting.TT.a=\"Abbreviated weekday name\"\nFilenameFormatting.TT.A=\"Full weekday name\"\nFilenameFormatting.TT.b=\"Abbreviated month name\"\nFilenameFormatting.TT.B=\"Full month name\"\nFilenameFormatting.TT.d=\"Day of the month, zero-padded (01-31)\"\nFilenameFormatting.TT.H=\"Hour in 24h format (00-23)\"\nFilenameFormatting.TT.I=\"Hour in 12h format (01-12)\"\nFilenameFormatting.TT.m=\"Month as a decimal number (01-12)\"\nFilenameFormatting.TT.M=\"Minute (00-59)\"\nFilenameFormatting.TT.p=\"AM or PM designation\"\nFilenameFormatting.TT.s=\"Time in seconds since UNIX epoch\"\nFilenameFormatting.TT.S=\"Second (00-59)\"\nFilenameFormatting.TT.y=\"Year, last two digits (00-99)\"\nFilenameFormatting.TT.Y=\"Year\"\nFilenameFormatting.TT.z=\"ISO 8601 offset from UTC in timezone\"\nFilenameFormatting.TT.Z=\"Timezone name or abbreviation\"\nFilenameFormatting.TT.FPS=\"Frames per second\"\nFilenameFormatting.TT.CRES=\"Base (canvas) resolution\"\nFilenameFormatting.TT.ORES=\"Output (scaled) resolution\"\nFilenameFormatting.TT.VF=\"Video format\"\n\n# basic mode 'video' settings\nBasic.Settings.Video=\"Video\"\nBasic.Settings.Video.Adapter=\"Video Adapter\"\nBasic.Settings.Video.BaseResolution=\"Base (Canvas) Resolution\"\nBasic.Settings.Video.ScaledResolution=\"Output (Scaled) Resolution\"\nBasic.Settings.Video.DownscaleFilter=\"Downscale Filter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Resolutions match, no downscaling required]\"\nBasic.Settings.Video.FPS=\"FPS\"\nBasic.Settings.Video.FPSCommon=\"Common FPS Values\"\nBasic.Settings.Video.FPSInteger=\"Integer FPS Value\"\nBasic.Settings.Video.FPSFraction=\"Fractional FPS Value\"\nBasic.Settings.Video.Numerator=\"Numerator\"\nBasic.Settings.Video.Denominator=\"Denominator\"\nBasic.Settings.Video.Renderer=\"Renderer\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Experimental)\"\nBasic.Settings.Video.InvalidResolution=\"Invalid resolution value. Must be [width]x[height] (i.e. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Video output is currently active. Please turn off any outputs to change video settings.\"\n\n# scale filters\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (Fastest, but blurry if scaling)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (Sharpened scaling, 16 samples)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Sharpened scaling, 36 samples)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Area (Weighted sum, 4/6/9 samples)\"\n\n# basic mode 'audio' settings\nBasic.Settings.Audio=\"Audio\"\nBasic.Settings.Audio.SampleRate=\"Sample Rate\"\nBasic.Settings.Audio.Channels=\"Channels\"\nBasic.Settings.Audio.Meters=\"Meters\"\nBasic.Settings.Audio.MeterDecayRate=\"Decay Rate\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Fast\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Medium (Type I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Slow (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Peak Meter Type\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Sample Peak\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True Peak (Higher CPU usage)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"WARNING: Surround sound audio is enabled.\"\nBasic.Settings.Audio.MultichannelWarning=\"If streaming, check to see if your streaming service supports both surround sound ingest and surround sound playback. For instance, Facebook 360 Live fully supports surround sound; YouTube Live supports 5.1 audio ingest (and playback on TVs).\\n\\nOBS audio filters are compatible with surround sound, though VST plugin support isn't guaranteed.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Enable surround sound audio?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Are you sure you want to enable surround sound audio?\"\nBasic.Settings.Audio.Devices=\"Global Audio Devices\"\nBasic.Settings.Audio.DesktopDevice=\"Desktop Audio\"\nBasic.Settings.Audio.DesktopDevice2=\"Desktop Audio 2\"\nBasic.Settings.Audio.AuxDevice=\"Mic/Auxiliary Audio\"\nBasic.Settings.Audio.AuxDevice2=\"Mic/Auxiliary Audio 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mic/Auxiliary Audio 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mic/Auxiliary Audio 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Enable Push-to-mute\"\nBasic.Settings.Audio.PushToMuteDelay=\"Push-to-mute delay\"\nBasic.Settings.Audio.EnablePushToTalk=\"Enable Push-to-talk\"\nBasic.Settings.Audio.PushToTalkDelay=\"Push-to-talk delay\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Device not connected or not available]\"\nBasic.Settings.Audio.Disabled=\"Disabled\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Low Latency Audio Buffering Mode (For Decklink/NDI outputs)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"WARNING: Low latency audio buffering is enabled.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Low latency audio buffering mode may cause audio to glitch or stop playing from some sources.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Enable low latency audio buffering mode?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Are you sure you want to enable low latency audio buffering mode?\"\n\n# basic mode 'accessibility' settings\nBasic.Settings.Accessibility=\"Accessibility\"\nBasic.Settings.Accessibility.ColorOverrides=\"Use Different Colors\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Source Border (Selection)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Source Border (Crop)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Source Border (Hover)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mixer Volume Band (-60 to -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mixer Volume Band (-20 to -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mixer Volume Band (-9 to 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mixer Volume Band (-60 to -20dB) (active)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mixer Volume Band (-20 to -9dB) (active)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mixer Volume Band (-9 to 0dB) (active)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Color Preset\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Default\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Custom\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Color Blind Alternative\"\n\n# basic mode 'advanced' settings\nBasic.Settings.Advanced=\"Advanced\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Show active outputs warning on exit\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Process Priority\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"High\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Above Normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Below Normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Idle\"\nBasic.Settings.Advanced.FormatWarning=\"Warning: Color formats other than NV12/P010 are primarily intended for recording, and are not recommended when streaming. Streaming may incur increased CPU usage due to color format conversion.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Warning: High-precision formats are more commonly used with HDR color spaces.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Warning: Rec. 2100 should use a format with more precision.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Color Format\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 planes)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 planes)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 planes)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 planes)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 planes)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 planes)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 planes)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-bit)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Color Space\"\nBasic.Settings.Advanced.Video.ColorSpace.sRGB=\"sRGB\"\nBasic.Settings.Advanced.Video.ColorSpace.601=\"Rec. 601\"\nBasic.Settings.Advanced.Video.ColorSpace.709=\"Rec. 709\"\nBasic.Settings.Advanced.Video.ColorSpace.2100PQ=\"Rec. 2100 (PQ)\"\nBasic.Settings.Advanced.Video.ColorSpace.2100HLG=\"Rec. 2100 (HLG)\"\nBasic.Settings.Advanced.Video.ColorRange=\"Color Range\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limited\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Full\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR White Level\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR Nominal Peak Level\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Monitoring Device\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Default\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Disable Windows audio ducking\"\nBasic.Settings.Advanced.StreamDelay=\"Stream Delay\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Duration\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Preserve cutoff point (increase delay) when reconnecting\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Estimated Memory Usage: %1 MB\"\nBasic.Settings.Advanced.Network=\"Network\"\nBasic.Settings.Advanced.Network.Disabled=\"The currently selected streaming protocol does not support changing network settings.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Bind to IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP Family\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Enable network optimizations\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Enable TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Attempts to make RTMP output friendlier to other latency sensitive applications on the network by regulating the rate of transmission.\\nIt may increase the risk of dropped frames on unstable connections.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Hotkey Focus Behavior\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Never disable hotkeys\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Disable hotkeys when main window is in focus\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Disable hotkeys when main window is not in focus\"\nBasic.Settings.Advanced.AutoRemux=\"Automatically remux to %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(record as mkv)\"\n\n# audio mixer\nBasic.AudioMixer.Pin=\"Pin\"\nBasic.AudioMixer.Unpin=\"Unpin\"\nBasic.AudioMixer.Options=\"Options\"\nBasic.AudioMixer.Hide=\"Hide\"\nBasic.AudioMixer.Unhide=\"Unhide\"\nBasic.AudioMixer.HiddenTotal=\"%1 hidden\"\nBasic.AudioMixer.ShowHidden=\"Show hidden sources\"\nBasic.AudioMixer.KeepHiddenRight=\"Keep hidden sources to the right\"\nBasic.AudioMixer.KeepHiddenBottom=\"Keep hidden sources at the bottom\"\nBasic.AudioMixer.ShowInactive=\"Show inactive sources\"\nBasic.AudioMixer.KeepInactiveRight=\"Keep inactive sources to the right\"\nBasic.AudioMixer.KeepInactiveBottom=\"Keep inactive sources at the bottom\"\nBasic.AudioMixer.Category.Global=\"Global\"\nBasic.AudioMixer.Category.Pinned=\"Pinned\"\nBasic.AudioMixer.Category.Active=\"Active\"\nBasic.AudioMixer.Category.Inactive=\"Inactive\"\nBasic.AudioMixer.Category.Preview=\"Preview\"\nBasic.AudioMixer.Category.Hidden=\"Hidden\"\nBasic.AudioMixer.Category.Unassigned=\"Unassigned\"\nBasic.AudioMixer.Layout.Horizontal=\"Horizontal\"\nBasic.AudioMixer.Layout.Vertical=\"Vertical\"\n\n# advanced audio properties\nBasic.AdvAudio=\"Advanced Audio Properties\"\nBasic.AdvAudio.ActiveOnly=\"Active Sources Only\"\nBasic.AdvAudio.Name=\"Name\"\nBasic.AdvAudio.Volume=\"Volume\"\nBasic.AdvAudio.VolumeSource=\"Volume for '%1'\"\nBasic.AdvAudio.Mono=\"Mono\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix for '%1'\"\nBasic.AdvAudio.Balance=\"Balance\"\nBasic.AdvAudio.BalanceSource=\"Balance for '%1'\"\nBasic.AdvAudio.SyncOffset=\"Sync Offset\"\nBasic.AdvAudio.SyncOffsetSource=\"Sync Offset for '%1'\"\nBasic.AdvAudio.Monitoring=\"Audio Monitoring\"\nBasic.AdvAudio.Monitoring.None=\"Monitor Off\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Monitor Only (mute output)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitor and Output\"\nBasic.AdvAudio.MonitoringSource=\"Audio Monitoring for '%1'\"\nBasic.AdvAudio.AudioTracks=\"Tracks\"\n\n# basic mode 'hotkeys' settings\nBasic.Settings.Hotkeys=\"Hotkeys\"\nBasic.Settings.Hotkeys.Pair=\"Key combinations shared with '%1' act as toggles\"\nBasic.Settings.Hotkeys.Filter=\"Filter\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filter by Hotkey\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"This hotkey is shared by one or more other actions, click to show conflicts\"\nBasic.Settings.Hotkeys.PleaseWait=\"Loading hotkeys, please wait...\"\n\n# basic mode hotkeys\nBasic.Hotkeys.SelectScene=\"Switch to scene\"\n\n# system tray\nBasic.SystemTray.Show=\"Show\"\nBasic.SystemTray.Hide=\"Hide\"\n\n# system tray messages\nBasic.SystemTray.Message.Reconnecting=\"Disconnected. Reconnecting...\"\n\n# hotkeys that may lack translation on certain operating systems\nHotkeys.Insert=\"Insert\"\nHotkeys.Delete=\"Delete\"\nHotkeys.Home=\"Home\"\nHotkeys.End=\"End\"\nHotkeys.PageUp=\"Page Up\"\nHotkeys.PageDown=\"Page Down\"\nHotkeys.NumLock=\"Num Lock\"\nHotkeys.ScrollLock=\"Scroll Lock\"\nHotkeys.CapsLock=\"Caps Lock\"\nHotkeys.Backspace=\"Backspace\"\nHotkeys.Tab=\"Tab\"\nHotkeys.Print=\"Print\"\nHotkeys.Pause=\"Pause\"\nHotkeys.Left=\"Left\"\nHotkeys.Right=\"Right\"\nHotkeys.Up=\"Up\"\nHotkeys.Down=\"Down\"\nHotkeys.Windows=\"Windows\"\nHotkeys.Super=\"Super\"\nHotkeys.Menu=\"Menu\"\nHotkeys.Space=\"Space\"\nHotkeys.NumpadNum=\"Numpad %1\"\nHotkeys.NumpadMultiply=\"Numpad Multiply\"\nHotkeys.NumpadDivide=\"Numpad Divide\"\nHotkeys.NumpadAdd=\"Numpad Add\"\nHotkeys.NumpadSubtract=\"Numpad Subtract\"\nHotkeys.NumpadDecimal=\"Numpad Decimal\"\nHotkeys.AppleKeypadNum=\"%1 (Keypad)\"\nHotkeys.AppleKeypadMultiply=\"* (Keypad)\"\nHotkeys.AppleKeypadDivide=\"/ (Keypad)\"\nHotkeys.AppleKeypadAdd=\"+ (Keypad)\"\nHotkeys.AppleKeypadSubtract=\"- (Keypad)\"\nHotkeys.AppleKeypadDecimal=\". (Keypad)\"\nHotkeys.AppleKeypadEqual=\"= (Keypad)\"\nHotkeys.MouseButton=\"Mouse %1\"\nHotkeys.Escape=\"Esc\"\n\n# audio hotkeys\nMute=\"Mute\"\nUnmute=\"Unmute\"\nPush-to-mute=\"Push-to-mute\"\nPush-to-talk=\"Push-to-talk\"\n\n# scene item hotkeys\nSceneItemShow=\"Show '%1'\"\nSceneItemHide=\"Hide '%1'\"\n\n# Output warnings\nOutputWarnings.NoTracksSelected=\"You must select at least one track\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Output Settings Error\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"All outputs must have at least one audio track selected.\"\nOutputWarnings.MP4Recording=\"Warning: Recordings saved to MP4/MOV will be unrecoverable if the file cannot be finalized (e.g. as a result of BSODs, power losses, etc.). If you want to record multiple audio tracks consider using MKV and remux the recording to MP4/MOV after it is finished (File → Remux Recordings)\"\nOutputWarnings.CannotPause=\"Warning: Recordings cannot be paused if the recording encoder is set to \\\"(Use stream encoder)\\\"\"\nOutputWarnings.CodecIncompatible=\"The audio or video encoder selection was reset due to incompatibility. Please select a compatible encoder from the list.\"\n\n# codec compatibility\nCodecCompat.Incompatible=\"(Incompatible with %1)\"\nCodecCompat.CodecPlaceholder=\"Select Encoder...\"\nCodecCompat.ContainerPlaceholder=\"Select Format...\"\nCodecCompat.CodecMissingOnExit.Title=\"No Encoder Selected\"\nCodecCompat.CodecMissingOnExit.Text=\"At least one video or audio encoder is not set. Please make sure to select encoders for both recording and streaming.\"\nCodecCompat.ContainerMissingOnExit.Title=\"No Format Selected\"\nCodecCompat.ContainerMissingOnExit.Text=\"No recording format has been selected. Please select a recording format that is compatible with the selected stream encoder.\"\n\n# deleting final scene\nFinalScene.Title=\"Delete Scene\"\nFinalScene.Text=\"There needs to be at least one scene.\"\n\n# no sources\nNoSources.Title=\"No Sources\"\nNoSources.Text=\"It looks like you haven't added any video sources yet, so you will only be outputting a blank screen. Are you sure you want to do this?\"\nNoSources.Text.AddSource=\"You can add sources by clicking the + icon under the Sources box in the main window at any time.\"\nNoSources.Label=\"You don't have any sources.\\nClick the + button below,\\nor right click here to add one.\"\n\n# Scene item color selection\nChangeBG=\"Set Color\"\nCustomColor=\"Custom Color\"\n\n# Global settings for the browser source\nBrowserSource.EnableHardwareAcceleration=\"Enable Browser Source Hardware Acceleration\"\n\n# about dialog\nAbout=\"About\"\nAbout.Info=\"OBS Studio is a free and open source video recording and live streaming software.\"\nAbout.Donate=\"Make a Contribution\"\nAbout.GetInvolved=\"Get Involved\"\nAbout.Authors=\"Authors\"\nAbout.License=\"License\"\nAbout.Error=\"Error! File could not be read.\\n\\nGo to: %1\"\nAbout.Contribute=\"Support the OBS Project\"\n\n# Drag-drop URL\nAddUrl.Title=\"Add Source via URL\"\nAddUrl.Text=\"You have dragged a URL into OBS. This will automatically add the link as a source. Continue?\"\nAddUrl.Text.Url=\"URL: %1\"\n\n# Dynamic output size\nResizeOutputSizeOfSource=\"Resize output (source size)\"\nResizeOutputSizeOfSource.Text=\"The base and output resolutions will be resized to the size of the current source.\"\nResizeOutputSizeOfSource.Continue=\"Do you want to continue?\"\n\nPreviewTransition=\"Preview Transition\"\n\n# Import Dialog\nImporter=\"Import Scene Collection\"\nImporter.SelectCollection=\"Select a Scene Collection\"\nImporter.Collection=\"Scene Collection\"\nImporter.HelpText=\"Add files to this window to import collections from OBS or other supported programs.\"\nImporter.Path=\"Collection Path\"\nImporter.Program=\"Detected Application\"\nImporter.AutomaticCollectionPrompt=\"Automatically Search for Scene Collections\"\nImporter.AutomaticCollectionText=\"OBS can automatically find importable scene collections from supported third-party programs. Would you like OBS to automatically find collections for you?\\n\\nYou can change this later in Settings > General > Importers.\"\nImporter.SelectFile=\"Browse...\"\n\n# Importers\nOBSStudio=\"OBS Studio\"\nOBSClassic=\"OBS Classic\"\nStreamlabs=\"Streamlabs\"\nXSplitBroadcaster=\"XSplit Broadcaster\"\n\n# OBS restart\nRestart=\"Restart\"\nNeedsRestart=\"OBS Studio needs to be restarted. Do you want to restart now?\"\nLoadProfileNeedsRestart=\"Profile contains settings that require restarting OBS:\\n%1\\n\\nDo you want to restart OBS for these settings to take effect?\"\n\n# Context Bar\nContextBar.NoSelectedSource=\"No source selected\"\n\n# Context Bar Media Controls\nContextBar.MediaControls.PlayMedia=\"Play Media\"\nContextBar.MediaControls.PauseMedia=\"Pause Media\"\nContextBar.MediaControls.StopMedia=\"Stop Media\"\nContextBar.MediaControls.RestartMedia=\"Restart Media\"\nContextBar.MediaControls.PlaylistNext=\"Next in Playlist\"\nContextBar.MediaControls.PlaylistPrevious=\"Previous in Playlist\"\nContextBar.MediaControls.BlindSeek=\"Media Seek Widget\"\n\n# YouTube Actions and Auth\nYouTube.Auth.Ok=\"Authorization completed successfully.\\nYou can now close this page.\"\nYouTube.Auth.NoCode=\"The authorization process was not completed.\"\nYouTube.Auth.NoChannels=\"No channel(s) available on selected account\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube User Authorization\"\nYouTube.Auth.WaitingAuth.Text=\"Please complete the authorization in your external browser.<br>If the external browser does not open, follow this link and complete the authorization:<br>%1\"\nYouTube.AuthError.Text=\"Failed to get channel information: %1.\"\n\nYouTube.Actions.WindowTitle=\"YouTube Broadcast Setup - Channel: %1\"\nYouTube.Actions.CreateNewEvent=\"Create New Broadcast\"\nYouTube.Actions.ChooseEvent=\"Select Existing Broadcast\"\nYouTube.Actions.Title=\"Title*\"\nYouTube.Actions.MyBroadcast=\"My Broadcast\"\nYouTube.Actions.Description=\"Description\"\nYouTube.Actions.Privacy=\"Privacy*\"\nYouTube.Actions.Privacy.Private=\"Private\"\nYouTube.Actions.Privacy.Public=\"Public\"\nYouTube.Actions.Privacy.Unlisted=\"Unlisted\"\nYouTube.Actions.Category=\"Category\"\n\nYouTube.Actions.Thumbnail=\"Thumbnail\"\nYouTube.Actions.Thumbnail.SelectFile=\"Select file...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"No file selected\"\nYouTube.Actions.Thumbnail.ClearFile=\"Clear\"\n\nYouTube.Actions.MadeForKids=\"Is this video made for kids?*\"\nYouTube.Actions.MadeForKids.Yes=\"Yes, it's made for kids\"\nYouTube.Actions.MadeForKids.No=\"No, it's not made for kids\"\nYouTube.Actions.MadeForKids.Help=\"<a href='https://support.google.com/youtube/topic/9689353'>(?)</a>\"\nYouTube.Actions.AdditionalSettings=\"Additional settings\"\nYouTube.Actions.Latency=\"Latency\"\nYouTube.Actions.Latency.Normal=\"Normal\"\nYouTube.Actions.Latency.Low=\"Low\"\nYouTube.Actions.Latency.UltraLow=\"Ultra low\"\nYouTube.Actions.EnableAutoStart=\"Enable Auto-start\"\nYouTube.Actions.EnableAutoStop=\"Enable Auto-stop\"\nYouTube.Actions.AutoStartStop.TT=\"Indicates whether this scheduled broadcast should start automatically\"\nYouTube.Actions.EnableDVR=\"Enable DVR\"\nYouTube.Actions.360Video=\"360 video\"\nYouTube.Actions.360Video.Help=\"<a href='https://vr.youtube.com/create/360/'>(?)</a>\"\nYouTube.Actions.ScheduleForLater=\"Schedule for later\"\nYouTube.Actions.RememberSettings=\"Remember these settings\"\n\nYouTube.Actions.Create_Ready=\"Create broadcast\"\nYouTube.Actions.Create_GoLive=\"Create broadcast and start streaming\"\nYouTube.Actions.Choose_Ready=\"Select broadcast\"\nYouTube.Actions.Choose_GoLive=\"Select broadcast and start streaming\"\nYouTube.Actions.Create_Schedule=\"Schedule broadcast\"\nYouTube.Actions.Create_Schedule_Ready=\"Schedule and select broadcast\"\nYouTube.Actions.Dashboard=\"Open YouTube Studio\"\n\nYouTube.Actions.Error.Title=\"Live broadcast creation error\"\nYouTube.Actions.Error.Text=\"YouTube access error '%1'.<br/>A detailed error description can be found at <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTube access error. Please check your network connection or your YouTube server access.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Broadcast creation error '%1'.<br/>A detailed error description can be found at <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"No stream created. Please relink your account.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API Error. Please see the log file for more information.\"\nYouTube.Actions.Error.BroadcastNotFound=\"The selected broadcast was not found.\"\nYouTube.Actions.Error.FileMissing=\"Selected file does not exist.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Failed opening selected file.\"\nYouTube.Actions.Error.FileTooLarge=\"Selected file is too large (Limit: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Transitioning the broadcast failed: %1<br/><br/>If this error persists <a href='https://studio.youtube.com/video/%2/livestreaming'>open the broadcast in YouTube Studio</a> and try manually.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Broadcast is transitioning to the test stage, this can take some time. Please try again in 10-30 seconds.\"\n\nYouTube.Actions.EventsLoading=\"Loading list of events...\"\nYouTube.Actions.EventCreated.Title=\"Event Created\"\nYouTube.Actions.EventCreated.Text=\"Event created successfully.\"\n\nYouTube.Actions.Stream=\"Stream\"\nYouTube.Actions.Stream.ScheduledFor=\"Scheduled for %1\"\nYouTube.Actions.Stream.Resume=\"Resume interrupted stream\"\nYouTube.Actions.Stream.YTStudio=\"Automatically created by YouTube Studio\"\n\nYouTube.Actions.Notify.Title=\"YouTube\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Creating a new Live Broadcast, please wait...\"\n\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Manual start required\"\nYouTube.Actions.AutoStartStreamingWarning=\"Auto-start is disabled for this event, click \\\"Go Live\\\" to start your broadcast.\"\nYouTube.Actions.AutoStopStreamingWarning=\"You will not be able to reconnect.<br>Your stream will stop and you will no longer be live.\"\n\n# YouTube API errors in format \"YouTube.Errors.<error reason>\"\nYouTube.Errors.liveStreamingNotEnabled=\"Live streaming is not enabled on the selected YouTube channel.<br/><br/>See <a href='https://www.youtube.com/features'>youtube.com/features</a> for more information.\"\nYouTube.Errors.livePermissionBlocked=\"Live streaming is unavailable on the selected YouTube Channel.<br/>Please note that it may take up to 24 hours for live streaming to become available after enabling it in your channel settings.<br/><br/>See <a href='https://www.youtube.com/features'>youtube.com/features</a> for details.\"\nYouTube.Errors.errorExecutingTransition=\"Transition failed due to a backend error. Please try again in a few seconds.\"\nYouTube.Errors.errorStreamInactive=\"YouTube is not receiving data for your stream. Please check your configuration and try again.\"\nYouTube.Errors.invalidTransition=\"The attempted transition was invalid. This may be due to the stream not having finished a previous transition. Please wait a few seconds and try again.\"\n# Browser Dock\nYouTube.DocksRemoval.Title=\"Clear Legacy YouTube Browser Docks\"\nYouTube.DocksRemoval.Text=\"These browser docks will be removed as deprecated:\\n\\n%1\\nUse \\\"Docks/YouTube Live Control Room\\\" instead.\"\n\n# MultitrackVideo\nConfigDownload.WarningMessageTitle=\"Warning\"\nFailedToStartStream.MissingConfigURL=\"No config URL available for the current service\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Custom RTMP URL not specified\"\nFailedToStartStream.InvalidCustomConfig=\"Invalid custom config\"\nFailedToStartStream.MissingCanvas=\"A configured extra canvas is missing\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Failed to create multitrack video service\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Failed to create multitrack video RTMP output\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC not available.\\n\\nFailed to find encoder type '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Failed to create video encoder '%1' (type: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Failed to get OBS video info while creating encoder '%1' (type: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Failed to create audio encoder\"\nFailedToStartStream.NoRTMPURLInConfig=\"Config does not contain stream target RTMP(S) URL\"\nFailedToStartStream.FallbackToDefault=\"Starting the stream using %1 failed; do you want to retry using single encode settings?\"\nFailedToStartStream.ConfigRequestFailed=\"Could not fetch config from %1<br><br>HTTP error: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Received unknown status value '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nDo you want to continue streaming without %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nDo you want to continue streaming?\"\nFailedToStartStream.MissingEncoderConfigs=\"Go live config did not include encoder configurations\"\nFailedToStartStream.InvalidEncoderConfig=\"Go live config included an invalid encoder configuration\"\nFailedToStartStream.StatusMissingHTML=\"Go live request returned an unspecified error\"\nFailedToStartStream.NoConfigSupplied=\"Missing config\"\nMultitrackVideo.Info=\"%1 automatically optimizes your settings to encode and send multiple video qualities. Selecting this option will send %2 information about your computer and software setup.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Incompatible Settings\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 is not currently compatible with:\\n\\n%2\\nTo continue streaming with %1, disable incompatible settings:\\n\\n%3\\nand Start Streaming again.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Disable for this stream and Start Streaming\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Update Settings and Start Streaming\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 is not currently compatible with [Audio → General → Channels] set to '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Audio → General → Channels] needs to be set to '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 requires multiple different settings for [Audio → General → Channels]\"\n\n# Plugin Manager window\nBasic.OpenPluginManager=\"Plugin Manager\"\nPluginManager=\"Plugin Manager\"\nPluginManager.Restart=\"OBS Studio must be restarted to apply plugin changes\"\nPluginManager.MissingPlugin=\"Missing Plugins\"\nPluginManager.SafeMode=\"All plugins temporarily disabled by Safe Mode\"\nPluginManager.Section.Discover=\"Browse\"\nPluginManager.Section.Manage=\"Installed\"\nPluginManager.Section.Updates=\"Updates\"\nPluginManager.Section.Manage.Title=\"Manage Enabled Plugins\"\n\n# Custom Widget Localization\nAccessible.Widget.Name.AlignmentSelector=\"Alignment Selector\"\n"
  },
  {
    "path": "frontend/data/locale/eo-UY.ini",
    "content": "Language=\"Esperanto\"\nOK=\"Bone\"\nApply=\"Apliki\"\nCancel=\"Nuligi\"\nClose=\"Fermi\"\nSave=\"Konservi\"\nDiscard=\"Forĵeti\"\nDisable=\"Malebligi\"\nYes=\"Jes\"\nNo=\"Ne\"\nAdd=\"Aldoni\"\nRemove=\"Forigi\"\nRename=\"Renomi\"\nInteract=\"Interagi\"\nFilters=\"Filtriloj\"\nProperties=\"Ecoj\"\nMoveUp=\"Movi supren\"\nMoveDown=\"Movi malsupren\"\nSettings=\"Agordoj\"\nDisplay=\"Ekrano\"\nName=\"Nomo\"\nExit=\"Eliri\"\nMixer=\"Sonmiksilo\"\nBrowse=\"Foliumi\"\nMono=\"Monofonia\"\nStereo=\"Stereofonia\"\nDroppedFrames=\"Perditaj kadroj %1 (%2%)\"\nClear=\"Viŝi\"\nRevert=\"Malfari\"\nShow=\"Montri\"\nHide=\"Kaŝi\"\nUnhideAll=\"Malkaŝi ĉiujn\"\nUntitled=\"Sentitola\"\nNew=\"Nova\"\nDuplicate=\"Duobligi\"\nEnable=\"Ebligi\"\nDisableOSXVSync=\"Malebligi v-sinkon de macOS\"\nResetOSXVSyncOnExit=\"Restarigi v-sinkon de macOS je eliro\"\nHighResourceUsage=\"Kodado troŝarĝita! Konsideru malpliigi video-agordojn aŭ uzi pli rapidan kodado-antaŭagordon.\"\nTransition=\"Transpaso\"\nQuickTransitions=\"Rapidaj transpasoj\"\nFadeToBlack=\"Dissolvi al nigro\"\nLeft=\"Maldekstre\"\nRight=\"Dekstre\"\nTop=\"Supre\"\nBottom=\"Malsupre\"\nReset=\"Restarigi\"\nHours=\"Horoj\"\nMinutes=\"Minutoj\"\nSeconds=\"Sekundoj\"\nDeprecated=\"Evitindaj\"\nReplayBuffer=\"Reluda bufro\"\nImport=\"Importi\"\nExport=\"Eksporti\"\nCopy=\"Kopii\"\nPaste=\"Alglui\"\nPasteReference=\"Alglui (referenco)\"\nPasteDuplicate=\"Alglui (duobligi)\"\nRemuxRecordings=\"Konverti registraĵojn\"\nNext=\"Sekve\"\nBack=\"Antaŭe\"\nDefaults=\"Defaŭltoj\"\nRestoreDefaults=\"Defaŭltoj\"\nHideMixer=\"Malkaŝi en miksilo\"\nTransitionOverride=\"Devigi transpason\"\nShowTransition=\"Montri transpason\"\nHideTransition=\"Kaŝi transpason\"\nNone=\"Neniu\"\nStudioMode.Preview=\"Antaŭvido\"\nStudioMode.Program=\"Programaro\"\nStudioMode.PreviewSceneName=\"Antaŭvido: %1\"\nStudioMode.ProgramSceneName=\"Programo: %1\"\nShowInMultiview=\"Montri en multivido\"\nVerticalLayout=\"Vertikala aranĝo\"\nGroup=\"Grupo\"\nDoNotShowAgain=\"Ne montru denove\"\nDefault=\"(Defaŭlta)\"\nCalculating=\"Kalkulado...\"\nFullscreen=\"Plenekrano\"\nWindowed=\"En fenestro\"\nRefreshBrowser=\"Aktualigi\"\nAspectRatio=\"Ekranformato <b>%1:%2</b>\"\nLockVolume=\"Ŝlosi laŭtecon\"\nLogViewer=\"Vidilo de protokoloj\"\nShowOnStartup=\"Montri je lanĉo\"\nOpenFile=\"Malfermi dosieron\"\nAddSource=\"Aldonu fonton\"\nRemoveScene=\"Forigu elektitan scenon\"\nRemoveSource=\"Forigu elektitajn fontojn\"\nMoveSceneUp=\"Movi Scenon Supre\"\nMoveSceneDown=\"Movi Scenon Malsupre\"\nMoveSourceUp=\"Movi Fonto(j)n Supre\"\nMoveSourceDown=\"Movi Fonto(j)n Malsupre\"\nSourceProperties=\"Malfermi Fontajn Ecojn\"\nSourceFilters=\"Malfermi Fontajn Filtrilojn\"\nMixerToolbarMenu=\"Sonmiksila Menuo\"\nSceneFilters=\"Malfermi Scenajn Filtrilojn\"\nList=\"Listo\"\nGrid=\"Krado\"\nAutomatic=\"Aŭtomata\"\nPluginsFailedToLoad.Title=\"Kromprograma Ŝarga Eraro\"\nPluginsFailedToLoad.Text=\"La jenaj kromprogramoj de OBS malsukcesis ŝargi:\\n\\n%1\\nBonvolu ĝisdatigi aŭ forigi ĉi tiujn kromprogramojn.\"\nAlreadyRunning.Title=\"OBS jam ruliĝas\"\nAlreadyRunning.Text=\"OBS estas jam ruliĝanta! Krom se vi intencis fari tion, bonvolu fermi ajnajn ekzistantajn aperojn de OBS antaŭ ol provi ruli novan. Se vi agordis OBS por plejetiĝi al la taskopleto, bonvolu kontroli ĉu ĝi ankoraŭ ruliĝas tie.\"\nAlreadyRunning.LaunchAnyway=\"Lanĉi ĉiuokaze\"\nSafeMode.Restart=\"Ĉu vi volas restarti je OBS per Sendaĝera Reĝimo (kromprogramoj de eksteraj liverantoj, skriptado, kaj WebSockets malŝaltitaj)?\"\nSafeMode.RestartNormal=\"Ĉu vi volas restarti je OBS per Kutima Reĝimo?\"\nChromeOS.Title=\"Nesubtenata platformo\"\nWine.Title=\"Wine detektita\"\nWine.Text=\"Ruli OBS en Wine estas nesubtenata, kaj multaj funkcioj kiel kaptado aŭ aparatfontoj ne funkcios aŭ nur en limigita kapablo.<br><br> Oni rekomendas ruli denaskan version de OBS anstataŭe, ekzemple <a href='https://flathub.org/apps/details/com.obsproject.Studio'>nia Flatpak-versio</a> aŭ la pakaĵojn de via operaciumo.\"\nDockCloseWarning.Title=\"Fermado de dokebla fenestro\"\nDockCloseWarning.Text=\"Vi ĵus fermis dokeblan fenestron. Se vi volas ĝin remontrigi, uzu la Dokojn menuon sur la menustrio.\"\nExtraBrowsers=\"Laŭmendaj esploril-dokoj\"\nExtraBrowsers.Info=\"Aldonu dokojn per doni al ili nomon kaj retadreso, kaj tiam alklaku Apliki aŭ Fermi por malfermi la dokojn. Vi povas aldoni aŭ forigi dokojn iam ajn.\"\nExtraBrowsers.DockName=\"Dok-nomo\"\nAuth.Authing.Title=\"Aŭtentigado...\"\nAuth.Authing.Text=\"Aŭtentigado per %1, bonvole atendu...\"\nAuth.AuthFailure.Title=\"Malsukceso de aŭtentigo\"\nAuth.AuthFailure.Text=\"Malsukcesis aŭtentigi per %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Aŭtentigo deviga\"\nAuth.InvalidScope.Text=\"La aŭtentigaj postuloj por %1 ŝanĝiĝis. Iuj trajtoj eble ne disponeblas.\"\nAuth.LoadingChannel.Title=\"Ŝargado de kanal-informoj...\"\nAuth.LoadingChannel.Text=\"Ŝargado de kanal-informoj por %1, bonvole atendu...\"\nAuth.LoadingChannel.Error=\"Ne eblas akiri kanalinformon\"\nAuth.ChannelFailure.Title=\"Malsukcesis ŝargi kanalon\"\nAuth.ChannelFailure.Text=\"Malsukcesis ŝargi kanal-informojn por %1\\n\\n%2: %3\"\nAuth.Chat=\"Babilejo\"\nAuth.StreamInfo=\"Elsend-informoj\"\nTwitchAuth.Stats=\"Statistikoj de Twitch\"\nTwitchAuth.Feed=\"Aktivec-fluo de Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Ne povis informpeti elsend-ŝlosilon\"\nTwitchAuth.TwoFactorFail.Text=\"OBS ne povis konekti al via Twitch-konto. Bonvolu certigi ke dufaktora aŭtentigo estas agordita en viaj <a href='https://www.twitch.tv/settings/security'>agordoj de sekureco en Twitch</a>, pro ke tio estas necesa por elsendi.\"\nRestreamAuth.Channels=\"Kanaloj de Restream\"\nCopy.Filters=\"Kopii filtrilojn\"\nPaste.Filters=\"Alglui filtrilojn\"\nBrowserPanelInit.Title=\"Pravalorizado de esplorilo...\"\nBrowserPanelInit.Text=\"Pravalorizado de esplorilo, bonvole atendu...\"\nBandwidthTest.Region=\"Regiono\"\nBandwidthTest.Region.US=\"Usono\"\nBandwidthTest.Region.EU=\"Eŭropo\"\nBandwidthTest.Region.Asia=\"Azio\"\nBandwidthTest.Region.Other=\"Alia\"\nBasic.AutoConfig=\"Aŭtomata agordilo\"\nBasic.AutoConfig.ApplySettings=\"Apliki agordojn\"\nBasic.AutoConfig.StartPage=\"Informoj pri uzado\"\nBasic.AutoConfig.StartPage.SubTitle=\"Indiku por kio vi volas uzi la programaron\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimumigu por elsendado, registrado estas akcesora\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimumigi nur por registrado, mi ne elsendos\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Mi nur uzos la virtualan kameraon\"\nBasic.AutoConfig.VideoPage=\"Video-agordoj\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Indiku la video-agordojn, kiujn vi volas uzi\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Uzi aktualan (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Ekrano %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Uzi aktualan (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Aŭ 60 aŭ 30, sed preferu 60 kiam eblas\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Aŭ 60 aŭ 30, sed preferu altan distingivon\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Notu: La kanvasa (baza) distingivo ne necese samas kiel la distingivo per kiu vi elsendos aŭ registros. Via efektiva distingivo de elsendado/registrado eble reduktiĝos de la kanvasa distingivo por malpliigi rimed-uzadon aŭ bitrapidaj postuloj.\"\nBasic.AutoConfig.StreamPage=\"Elsend-informoj\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Bonvole enigi viajn elsend-informojn\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Konekti konton (rekomendite)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Malkonekti konton\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Ĉu malkonektu konton?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Tiu ĉi ŝanĝo aplikiĝos tuj. Ĉu vi certas, ke vi volas malkonekti vian konton?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Akiri elsend-ŝlosilon\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Pli da informoj\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Uzi elsend-ŝlosilon\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Uzi tujan elsendan kodon (altnivela)\"\nBasic.AutoConfig.StreamPage.Service=\"Servo\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Montri ĉiujn...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Laŭmenda...\"\nBasic.AutoConfig.StreamPage.Server=\"Servilo\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Elsend-ŝlosilo\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kodila kodo\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Konektita konto\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Taksi bitrapidon per kapacita testo (eble necesos kelkaj minutoj)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferi aparat-kodado\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Aparat-kodado forigas plej multajn CPU-uzadon, sed eble postulos pli da bitrapido por atingi la saman kvalit-nivelon.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Bit-fluo averto\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"La ret-trafik-kapacitotesto temas pri elsendi hazardigitan videodatumon al via kanalo sen aŭdaĵoj. Se vi kapablas, oni rekomendas kelk-tempe malŝalti konservi videojn el bitfluojn kaj agordi la elsendaĵon kiel privatan ĝis la testo finiĝos. Ĉu daŭrigi?\"\nBasic.AutoConfig.TestPage=\"Finaj rezultoj\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"La programo nun plenumas plurajn testojn por estimi la idealajn agordojn\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testado finita\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Plenumante ret-trafik-kapacitan teston. Tio eble daŭros kelkajn minutojn...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Konektante al: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Malsukcesis konekti ĉiujn servilojn, bonvolu kontroli vian interretan konekton kaj reprovu.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testante ret-trafik-kapacito je: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testanta bitfluon kodilon. Tio eble nelonge daŭros.\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testanta registradkodilon, tio eble nelonge daŭros...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Malsukcesis startigi kodilon\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testante %1x%2 %3 KS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Bitflukodilo\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Registradkodilo\"\nBasic.AutoConfig.TestPage.Result.Header=\"La programo difinis, ke ĉi tiujn agordojn estas la idealaj por vi:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Por uzi tiujn agordojn alklaku \\\"Apliky Agordojn\\\". Por reagordi la asistanton kaj reprovi, alklaku Reen. Por mane vi mem ŝanĝi agordojn, alklaku Rezigni kaj malfermu Agordoj-n.\"\nBasic.AutoConfig.Info=\"La aŭto-agorda sorĉisto determinos la plej bonajn agordojn laŭ via komputilaj specifoj kaj interreta rapideco.\"\nBasic.AutoConfig.RunAnytime=\"Ĉi tio povas esti rulita en ajna momento irante al la Iloj menuo.\"\nBasic.Stats=\"Statistikoj\"\nBasic.Stats.CPUUsage=\"Uzado de CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Diskospaco disponebla\"\nBasic.Stats.MemoryUsage=\"Uzado de Memoro\"\nBasic.Stats.AverageTimeToRender=\"Averaĝa tempo por redoni kadron.\"\nBasic.Stats.SkippedFrames=\"Preterpasitaj kadroj pro koda malfuro\"\nBasic.Stats.MissedFrames=\"Kadroj maltrafitaj pro redonanta malfuro\"\nBasic.Stats.Output.Stream=\"Rojo\"\nBasic.Stats.Output.Recording=\"Registrado\"\nBasic.Stats.Status=\"Stato\"\nBasic.Stats.Status.Recording=\"Registrado\"\nBasic.Stats.Status.Live=\"REKTA\"\nBasic.Stats.Status.Reconnecting=\"Rekonekti\"\nBasic.Stats.Status.Inactive=\"Neaktiva\"\nBasic.Stats.Status.Active=\"Aktiva\"\nBasic.Stats.DroppedFrames=\"Faligita Kadroj (Reta)\"\nBasic.Stats.MegabytesSent=\"Totala Datuma Eligo\"\nBasic.Stats.Bitrate=\"Bitrapideco\"\nBasic.Stats.DiskFullIn=\"Disko plena en (proksimuna)\"\nBasic.Stats.ResetStats=\"Restarigi Statistiojn\"\nResetUIWarning.Title=\"Ĉu vi certas, ke vi volas restartigi la UI?\"\nResetUIWarning.Text=\"Restartigi la UI kaŝos pliajn dokojn. Vi devos malkaŝi ĉi tiujn dokojn el la Dokoj menuo se vi volas, ke ili estu videblaj.\\n\\nĈu vi certas, ke vi volas restartigi la UI?\"\nUpdater.Title=\"Nova ĝisdatigo disponebla\"\nUpdater.Text=\"Estas nova ĝisdatigo disponebla:\"\nUpdater.UpdateNow=\"Ĝisdatigu Nun\"\nUpdater.RemindMeLater=\"Rememorigi al mi Poste\"\nUpdater.Skip=\"Preterpasi Version\"\nUpdater.NoUpdatesAvailable.Title=\"Neniuj ĝisdatigoj disponeblas\"\nUpdater.NoUpdatesAvailable.Text=\"Neniuj ĝisdatigoj estas nuntempe disponeblaj\"\nUpdater.FailedToLaunch=\"Malsukcesis lanĉi ĝisdatigilon\"\nQuickTransitions.DuplicateScene=\"Duobligi scenon\"\nQuickTransitions.EditProperties=\"Duobligi fontojn\"\nQuickTransitions.HotkeyName=\"Rapida transpaso: %1\"\nBasic.AddTransition=\"Aldoni agordeblan transpason\"\nBasic.RemoveTransition=\"Forigi agordeblan transpason\"\nBasic.TransitionProperties=\"Ecoj de transpaso\"\nBasic.SceneTransitions=\"Scen-transpasoj\"\nBasic.TransitionDuration=\"Daŭro\"\nBasic.TogglePreviewProgramMode=\"Studio-reĝimo\"\nUndo.Undo=\"Malfari\"\nUndo.Redo=\"Refari\"\nUndo.Add=\"Aldoni '%1'\"\nUndo.Delete=\"Forigi '%1'\"\nUndo.Rename=\"Alinomi '%1'\"\nUndo.SceneCollection.Switch=\"Ŝanĝi al '%1'\"\nUndo.Item.Undo=\"Malfari %1\"\nUndo.Item.Redo=\"Refari %1\"\nUndo.Sources.Multi=\"Forigi %1 Fontojn\"\nUndo.Filters=\"Filtrilo Ŝanĝoj en '%1'\"\nUndo.Filters.Paste.Single=\"Alglui Filtrilon '%1' al '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopii Filtrilojn de '%1' al '%2'\"\nUndo.Transform=\"Transformi fonton(j)n En '%1'\"\nUndo.Transform.Paste=\"Alglui Transformon en '%1'\"\nUndo.Transform.Rotate=\"Rotacio en '%1'\"\nUndo.Transform.Reset=\"Transformon Restarigi En '%1'\"\nUndo.Transform.HFlip=\"Horizontala Renversi En '%1'\"\nUndo.Transform.VFlip=\"Vertikala Renversi En '%1'\"\nUndo.Transform.FitToScreen=\"Alĝustigi al Ekrano En '%1'\"\nUndo.Transform.StretchToScreen=\"Streĉi al Ekrano en '%1'\"\nUndo.Transform.Center=\"Centri al Ekrano en '%1'\"\nUndo.Transform.VCenter=\"Vertikale Centri al Ekrano en '%1'\"\nUndo.Transform.HCenter=\"Horizontale Centri al Ekrano en '%1'\"\nUndo.Volume.Change=\"Volumo Ŝanĝi en '%1'\"\nUndo.Volume.Mute=\"Mutigi '%1'\"\nUndo.Volume.Unmute=\"Malmutigi '%1'\"\nUndo.Balance.Change=\"Sonbalanco Ŝanĝi en '%1'\"\nUndo.SyncOffset.Change=\"Aŭdio Sinkronigi Ofseton Ŝanĝi en '%1'\"\nUndo.MonitoringType.Change=\"Ŝanĝi Aŭdan Monitoradon en '%1'\"\nUndo.Mixers.Change=\"Ŝanĝi Aŭdiajn Miksilojn en '%1'\"\nUndo.ForceMono.On=\"Ebligi Forton Monofonon en '%1'\"\nUndo.ForceMono.Off=\"Malebligi Forton Monofonon en '%1'\"\nUndo.Scene.Duplicate=\"Duobligi Sceno '%1'\"\nUndo.ShowTransition=\"Spektakla Transiro sur '%1'\"\nUndo.ShowSceneItem=\"Montru '%1' en '%2'\"\nUndo.HideSceneItem=\"Kaŝi '%1' en '%2'\"\nTransitionNameDlg.Text=\"Bonvolu enigi la nomon de la transpaso\"\nTransitionNameDlg.Title=\"Nomo de transpaso\"\nTitleBar.Profile=\"Agordaro\"\nTitleBar.Scenes=\"Scenoj\"\nNameExists.Title=\"Nomo jam ekzistas\"\nNameExists.Text=\"La nomo jam estas uzata.\"\nNoNameEntered.Title=\"Bonvolu enigi validan nomon\"\nNoNameEntered.Text=\"La nomo estas deviga.\"\nConfirmStart.Title=\"Ĉu komenci elsendon?\"\nConfirmStart.Text=\"Ĉu vi certas, ke vi volas komenci la elsendon?\"\nConfirmStop.Title=\"Ĉu ĉesigi elsendon?\"\nConfirmStop.Text=\"Ĉu vi certas ke vi volas ĉesigi la elsendon?\"\nConfirmStopRecord.Title=\"Ĉu ĉesigi registradon?\"\nConfirmStopRecord.Text=\"Ĉu vi certas ke vi volas ĉesigi la registradon?\"\nConfirmBWTest.Title=\"Ĉu komenci kapacitan teston?\"\nConfirmBWTest.Text=\"Vi havas OBS agorditan en reĝimo de kapacita testo. Tiu ĉi reĝimo ebligas ret-testadon sen vere tujelsendi. Kiam vi finiĝas pri testado, vi devos malebligi ĝin por ke spektantoj povu vidi vian elsendon.\\n\\nĈu vi volas daŭrigi?\"\nConfirmRemove.Title=\"Konfirmado de forigo\"\nConfirmRemove.TextMultiple=\"Ĉu vi vere volas forigi %1 erojn?\"\nOutput.StartStreamFailed=\"Malsukcesis ekelsendi\"\nOutput.StartRecordingFailed=\"Malsukcesis ekregistri\"\nOutput.StartReplayFailed=\"Malsukcesis komenci reludan bufron\"\nOutput.StartFailedGeneric=\"Komencado de la eligo malsukcesis. Bonvolu kontroli la protokolon por detaloj.\\n\\nNotu: Se vi uzas la kodiloj NVENC aŭ AMD, certigu ke viaj videopeliloj estas ĝisdataj.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Ne povas konservi reludojn dum paŭzo\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Averto: Reludoj ne konserveblas dum registrado estas paŭzigita.\"\nOutput.ConnectFail.Title=\"Malsukcesis konekti\"\nOutput.ConnectFail.BadPath=\"Nevalida indiko aŭ konekt-retadreso. Bonvole kontrolu viajn agordojn por certigi, ke ili validas.\"\nRemux.OBSRecording=\"OBS Registradon\"\nMissingFiles=\"Mankaj Dosieroj\"\nMissingFiles.MissingFile=\"Manka Dosiero\"\nMissingFiles.NewFile=\"Nova Dosiero\"\nMissingFiles.State=\"Stato\"\nMissingFiles.Missing=\"Manka\"\nMissingFiles.Found=\"Trovita\"\nMissingFiles.NoMissing.Title=\"Mankaj Dosieroj Kontroli\"\nSourceLeak.Text=\"Problemo okazis dum ŝanĝado de scenaj kolektoj kaj kelkaj fontoj ne povis esti malŝargita. Ĉi tiu problemo estas tipe kaŭzita de kromprogramoj, kiuj ne liberigas resursojn ĝuste. Bonvolu certigi, ke ajnaj kromprogramoj, kiujn vi uzas, estas ĝisdata.\\n\\nOBS Studio nun fermos por preventi ajnan eblan difektigadon de datumoj.\"\nBasic.Scene=\"Sceno\"\nScaleFiltering=\"Skal-filtrado\"\nVolControl.Mute=\"Mutigi '%1'\"\nBasic.Main.DefaultSceneName.Text=\"Sceno %1\"\nBasic.Main.Sources.Lock=\"Ŝlosi\"\nBasic.PropertiesWindow.SelectColor=\"Elektu koloro\"\nBasic.PropertiesWindow.SelectFont=\"Elektu tiparo\"\nBasic.TransformWindow.PositionX=\"Pozicio X\"\nBasic.TransformWindow.PositionY=\"Pozicio Y\"\nBasic.TransformWindow.Width=\"Larĝeco\"\nBasic.TransformWindow.Height=\"Alteco\"\nBasic.TransformWindow.CropLeft=\"Tondi maldekstre\"\nBasic.TransformWindow.CropRight=\"Tondi dekstre\"\nBasic.TransformWindow.CropTop=\"Tondi Supre\"\nBasic.TransformWindow.CropBottom=\"Tondi Sube\"\nBasic.TransformWindow.Alignment.Center=\"Centra\"\nBasic.TransformWindow.Alignment.CenterRight=\"Centra Dekstra\"\nBasic.Main.Scenes=\"Scenoj\"\nBasic.Main.Sources=\"Fontoj\"\nBasic.Main.Source=\"Fonto\"\nBasic.MainMenu.File.Exit=\"Eliri(&X)\"\nBasic.MainMenu.Edit=\"&Editi\"\nBasic.MainMenu.Edit.Undo=\"Malfari(&U)\"\nBasic.MainMenu.Edit.Redo=\"&Refari\"\nBasic.MainMenu.Tools=\"&Iloj\"\nBasic.Settings.General.Language=\"Lingvo\"\nBasic.Settings.Appearance.General.Theme=\"Etoso\"\nBasic.Settings.Appearance.General.Variant=\"Stilo\"\nBasic.Settings.Stream.Custom.Username=\"Salutnomo\"\nBasic.Settings.Stream.Custom.Password=\"Pasvorto\"\nBasic.Settings.Stream.TTVAddon.None=\"Neniu\"\nBasic.Settings.Stream.IgnoreRecommended=\"Malatenti rekomendojn de elsend-servaj agordoj\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Transpasi rekomenditajn agordojn\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Averto: Malatenti la limojn de la servo eble rezultos en malaltigita elsend-kvalito aŭ preventos vin de elsendi.\\n\\nĈu daŭrigi?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maksimuma video-bitrapido: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maksimuma son-bitrapido: %1 kbps\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Averto: La elsenda video-bitrapido agordiĝos al %1, kio estas la supra limo por la aktuala elsend-servo.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Averto: La elsenda son-bitrapido agordiĝos al %1, kio estas la supra limo por la aktuala elsend-servo.\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Trako 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Trako 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Trako 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Trako 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Trako 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Trako 6\"\nBasic.Settings.Output.Adv.Recording=\"Registrado\"\nBasic.Settings.Output.Adv.Recording.Type=\"Speco\"\nFilenameFormatting.TT.mm=\"Minuto (00-59)\"\nFilenameFormatting.TT.ss=\"Sekundo (00-59)\"\nFilenameFormatting.TT.M=\"Minuto (00-59)\"\nFilenameFormatting.TT.S=\"Sekundo (00-59)\"\nFilenameFormatting.TT.Y=\"Jaro\"\nBasic.Settings.Audio=\"Sona\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rapida\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Malrapida (Speco II PPM)\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Daŭro\"\nBasic.Settings.Advanced.Network=\"Reto\"\nBasic.AdvAudio.Name=\"Nomo\"\nBasic.AdvAudio.AudioTracks=\"Trakoj\"\nBasic.SystemTray.Show=\"Montri\"\nBasic.SystemTray.Hide=\"Kaŝi\"\nAbout.Authors=\"Aŭtoroj\"\nAbout.License=\"Permesilo\"\nYouTube.Actions.Title=\"Titolo*\"\n"
  },
  {
    "path": "frontend/data/locale/es-ES.ini",
    "content": "Language=\"Español\"\nOK=\"Aceptar\"\nApply=\"Aplicar\"\nCancel=\"Cancelar\"\nClose=\"Cerrar\"\nSave=\"Guardar\"\nDiscard=\"Descartar\"\nDisable=\"Deshabilitar\"\nYes=\"Sí\"\nAdd=\"Agregar\"\nRemove=\"Eliminar\"\nRename=\"Renombrar\"\nInteract=\"Interactuar\"\nFilters=\"Filtros\"\nProperties=\"Propiedades\"\nMoveUp=\"Mover arriba\"\nMoveDown=\"Mover abajo\"\nSettings=\"Ajustes\"\nDisplay=\"Pantalla\"\nName=\"Nombre\"\nExit=\"Salir\"\nMixer=\"Mezclador de audio\"\nBrowse=\"Examinar\"\nStereo=\"Estéreo\"\nDroppedFrames=\"Fotogramas Perdidos %1 (%2%)\"\nProjector.Open.Program=\"Abrir proyector de programa\"\nProjector.Open.Preview=\"Abrir proyector de vista previa\"\nProjector.Open.Scene=\"Abrir proyector de escena\"\nProjector.Open.Source=\"Abrir proyector de fuente\"\nProjector.Open.Multiview=\"Abrir vista múltiple\"\nProjector.Display=\"Pantalla: %1\"\nProjector.Window=\"Nueva ventana\"\nProjector.Title=\"Proyector\"\nProjector.Title.Scene=\"Escena: %1\"\nProjector.Title.Source=\"Fuente: %1\"\nProjector.Title.Multiview=\"Vista múltiple\"\nProjector.ResizeWindowToContent=\"Ajustar ventana al contenido\"\nClear=\"Borrar\"\nRevert=\"Revertir\"\nShow=\"Mostrar\"\nHide=\"Ocultar\"\nUnhideAll=\"Mostrar todos\"\nUntitled=\"Sin Título\"\nNew=\"Nuevo\"\nDuplicate=\"Duplicar\"\nEnable=\"Habilitar\"\nDisableOSXVSync=\"Deshabilitar V-Sync en macOS\"\nResetOSXVSyncOnExit=\"Resetear V-Sync de macOS al salir\"\nHighResourceUsage=\"¡Sobrecarga al codificar! Considera bajar los ajustes de vídeo o usa una configuración del codificador más rápida.\"\nTransition=\"Transición\"\nQuickTransitions=\"Transiciones rápidas\"\nFadeToBlack=\"Desvanecer a negro\"\nLeft=\"Izquierda\"\nRight=\"Derecha\"\nTop=\"Arriba\"\nBottom=\"Abajo\"\nReset=\"Reiniciar\"\nHours=\"Horas\"\nMinutes=\"Minutos\"\nSeconds=\"Segundos\"\nDeprecated=\"Obsoleto\"\nReplayBuffer=\"Búfer de repetición\"\nImport=\"Importar\"\nExport=\"Exportar\"\nCopy=\"Copiar\"\nPaste=\"Pegar\"\nPasteReference=\"Pegar (Referencia)\"\nPasteDuplicate=\"Pegar (Duplicado)\"\nRemuxRecordings=\"Convertir grabaciones\"\nNext=\"Siguiente\"\nBack=\"Atrás\"\nDefaults=\"Predeterminados\"\nRestoreDefaults=\"Por defecto\"\nHideMixer=\"Ocultar en el mezclador\"\nTransitionOverride=\"Anulación de la transición\"\nShowTransition=\"Transición al mostrar\"\nHideTransition=\"Transición al ocultar\"\nNone=\"Ninguno\"\nStudioMode.Preview=\"Vista Previa\"\nStudioMode.Program=\"Programa\"\nStudioMode.PreviewSceneName=\"Vista previa: %1\"\nStudioMode.ProgramSceneName=\"Programa: %1\"\nShowInMultiview=\"Mostrar en vista múltiple\"\nVerticalLayout=\"Interfaz vertical\"\nGroup=\"Grupo\"\nDoNotShowAgain=\"No mostrar de nuevo\"\nDefault=\"(Por defecto)\"\nCalculating=\"Calculando...\"\nFullscreen=\"Pantalla completa\"\nWindowed=\"En ventana\"\nRefreshBrowser=\"Actualizar\"\nAspectRatio=\"Relación de aspecto <b>%1:%2</b>\"\nLockVolume=\"Bloquear volumen\"\nLogViewer=\"Visor de registros\"\nShowOnStartup=\"Mostrar al inicio\"\nOpenFile=\"Abrir archivo\"\nAddScene=\"Añadir escena\"\nAddSource=\"Añadir fuente\"\nRemoveScene=\"Eliminar escena seleccionada\"\nRemoveSource=\"Eliminar fuente(s) seleccionada(s)\"\nMoveSceneUp=\"Mover escena arriba\"\nMoveSceneDown=\"Mover escena abajo\"\nMoveSourceUp=\"Mover fuente(s) arriba\"\nMoveSourceDown=\"Mover fuente(s) abajo\"\nSourceProperties=\"Abrir propiedades de la fuente\"\nSourceFilters=\"Abrir filtros de la fuente\"\nMixerToolbarMenu=\"Menú del mezclador de audio\"\nSceneFilters=\"Abrir filtros de la escena\"\nList=\"Lista\"\nGrid=\"Cuadrícula\"\nAutomatic=\"Automático\"\nComingSoon=\"Próximamente\"\nPluginsFailedToLoad.Title=\"Error al cargar el plugin\"\nPluginsFailedToLoad.Text=\"No se han podido cargar los siguientes plugins de OBS:\\n\\n%1\\nPor favor, actualice o elimine estos plugins.\"\nAlreadyRunning.Title=\"OBS ya se está ejecutando\"\nAlreadyRunning.Text=\"¡OBS ya se está ejecutando! A no ser que quieras hacer esto, por favor, cierra todas las ventanas de OBS antes de intentar iniciar una nueva. Si tienes configurado OBS para que se minimice a la barra de tareas, comprueba si sigue ejecutándose ahí.\"\nAlreadyRunning.LaunchAnyway=\"Lanzar de todas maneras\"\nCrashHandling.Dialog.Title=\"Se ha detectado un error en OBS Studio\"\nCrashHandling.Labels.Text=\"OBS Studio no se ha cerrado correctamente.\\n\\n¿Quieres iniciar en modo seguro (plugins de terceros, scripts y WebSockets deshabilitados)?\"\nCrashHandling.Labels.PrivacyNotice=\"Tambien puedes optar por subir automáticamente el informe de error más reciente a OBSProject.<br /><br />Por favor, lee la <a href='https://obsproject.com/privacy-policy'>política de privacidad</a> antes de subir un archivo y ten especial atención con las partes relacionadas con la subida de archivos.\"\nCrashHandling.Checkbox.SendReport=\"He leído la política de privacidad y doy mi consentimiento a la carga.\"\nCrashHandling.Buttons.LaunchSafe=\"Ejecutar en modo seguro\"\nCrashHandling.Buttons.LaunchNormal=\"Ejecutar en modo normal\"\nCrashHandling.Errors.UploadJSONError=\"Se ha producido un error al intentar subir el registro de error más reciente. Por favor, inténtalo más tarde.\"\nCrashHandling.Errors.Title=\"Error al subir el registro de errores\"\nSafeMode.Restart=\"¿Quieres reiniciar OBS en modo seguro (plugins de terceros, scripts y websockets deshabilitados)?\"\nSafeMode.RestartNormal=\"¿Quieres reiniciar OBS en modo normal?\"\nChromeOS.Title=\"Plataforma no soportada\"\nChromeOS.Text=\"OBS parece estar ejecutándose dentro de un contenedor ChromeOS. Esta plataforma no está soportada.\"\nWine.Title=\"Se ha detectado Wine\"\nWine.Text=\"Ejecutar OBS en Wine no está soportado, y muchas características como fuentes de captura o de dispositivos no funcionarán, o lo harán con capacidad limitada.<br><br>Se recomienda ejecutar una versión nativa de OBS en su lugar, por ejemplo <a href='https://flathub.org/apps/details/com.obsproject.Studio'>nuestra versión de Flatpak</a> o los paquetes de su sistema operativo.\"\nDockCloseWarning.Title=\"Cerrando ventana acoplable\"\nDockCloseWarning.Text=\"Acabas de cerrar una ventana acoplable. Si quieres mostrarla de nuevo, usa el menú Paneles en la barra de menús.\"\nExtraBrowsers=\"Paneles de navegador personalizados\"\nExtraBrowsers.Info=\"Añade paneles dándoles un nombre y una URL, luego haz clic en Aplicar o Cerrar para abrir los paneles. Puedes añadir o eliminar los paneles en cualquier momento.\"\nExtraBrowsers.DockName=\"Nombre del panel\"\nAuth.Authing.Title=\"Autentificando...\"\nAuth.Authing.Text=\"Autentificando con %1, por favor espera...\"\nAuth.AuthFailure.Title=\"Fallo de autenticación\"\nAuth.AuthFailure.Text=\"Error al autentificar con %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Se requiere autentificación\"\nAuth.InvalidScope.Text=\"Los requisitos de autenticación para %1 han cambiado. Algunas características pueden no estar disponibles.\"\nAuth.LoadingChannel.Title=\"Cargando información del canal...\"\nAuth.LoadingChannel.Text=\"Cargando información del canal para %1, por favor espere...\"\nAuth.LoadingChannel.Error=\"No se pudo obtener información del canal.\"\nAuth.ChannelFailure.Title=\"Error al cargar el canal\"\nAuth.ChannelFailure.Text=\"Error al cargar la información del canal para %1\\n\\n%2: %3\"\nAuth.StreamInfo=\"Información de stream\"\nTwitchAuth.Stats=\"Estadísticas de Twitch\"\nTwitchAuth.Feed=\"Fuente de actividades de Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"No se pudo obtener la clave de transmisión\"\nTwitchAuth.TwoFactorFail.Text=\"OBS no pudo conectarse a su cuenta de Twitch. Por favor, asegúrese de que la autenticación en dos pasos está configurada en su configuración de seguridad <a href='https://www.twitch.tv/settings/security'>Configuración de seguridad de Twitch</a> ya que esto es necesario para transmitir.\"\nRestreamAuth.Channels=\"Canales Restream\"\nCopy.Filters=\"Copiar filtros\"\nPaste.Filters=\"Pegar filtros\"\nBrowserPanelInit.Title=\"Iniciando navegador...\"\nBrowserPanelInit.Text=\"Iniciando navegador, por favor espere...\"\nBandwidthTest.Region=\"Región\"\nBandwidthTest.Region.US=\"Estados Unidos\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Other=\"Otros\"\nBasic.AutoConfig=\"Asistente de configuración automática\"\nBasic.AutoConfig.ApplySettings=\"Aplicar configuración\"\nBasic.AutoConfig.StartPage=\"Información sobre el uso\"\nBasic.AutoConfig.StartPage.SubTitle=\"Especifique para que quiere usar el programa\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimizar para transmisiones, la grabación es secundaria\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimizar solo para grabación, no voy a hacer una transmisión\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Solo usaré la cámara virtual\"\nBasic.AutoConfig.VideoPage=\"Ajustes de vídeo\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Especifique la configuración de vídeo que le gustaría usar\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Usar Actual (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Pantalla %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Usar actual (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 o 30, pero usar 60 cuando sea posible\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 o 30, pero usar alta resolución\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Nota: La resolución del lienzo (base) no es necesariamente la misma que la resolución de la transmisión o grabación. La resolución actual puede ser reducida del lienzo para reducir el uso de los recursos o del bitrate.\"\nBasic.AutoConfig.StreamPage=\"Información de servicio de Stream\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Por favor, introduce información sobre tu servicio de stream\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Conectar cuenta (recomendado)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Desconectar cuenta\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"¿Desconectar cuenta?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Este cambio se aplicará inmediatamente. ¿Está seguro de que desea desconectar su cuenta?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Obtener clave de transmisión\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Más información\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Usar clave de emisión\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Usar clave de transmisión (avanzado)\"\nBasic.AutoConfig.StreamPage.Service=\"Servicio\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Mostrar todos...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Personalizado...\"\nBasic.AutoConfig.StreamPage.Server=\"Servidor\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Clave de retransmisión\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: introduzca la contraseña del cifrado.\\nRTMP: introduzca la clave proporcionada por el servicio.\\nSRT: introduzca el identificador de emisión si el servicio usa uno.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Clave del codificador\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Token del portador\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Cuenta conectada\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Estimar bitrate con una prueba de ancho de banda (puede tardar unos minutos)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferir codificación por hardware\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"La codificación por hardware elimina la mayoría del uso de la CPU, pero puede requerir más bitrate para obtener el mismo nivel de calidad.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Advertencia de transmisión\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"La prueba de ancho de banda esta a punto de transmitir datos de vídeo aleatorios sin audio a tu canal. Si puedes, es recomendable desactivar temporalmente el que se guarden los vídeos de las transmisiones y hacer la transmisión privada después de que la prueba haya finalizado. ¿Desea continuar?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Probar %1\"\nBasic.AutoConfig.TestPage=\"Resultado final\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"El programa ahora está ejecutando un conjunto de pruebas para estimar los ajustes óptimos\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Prueba completada\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Ejecutando prueba de ancho de banda, esto puede tardar unos minutos...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"No se encontró ninguna salida para el protocolo de este servicio\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Conectando a: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"No se pudo conectar a ningún servidor. Por favor revise su conexión a Internet e inténtelo de nuevo.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Probando ancho de banda para: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Probando codificador de transmisión, esto puede tardar un minuto...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Probando codificador de grabación, esto puede tardar un minuto...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"No se pudo iniciar el codificador\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Probando %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Codificador de transmisión\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Codificador de grabación\"\nBasic.AutoConfig.TestPage.Result.Header=\"El programa ha determinado que estos ajustes estimados son los ideales para ti:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Para utilizar estos ajustes, haga clic en \\\"Aplicar Configuración\\\". Para volver a configurar el asistente, haga clic en Atrás. Para configurar los ajustes por ti mismo, haga clic en Cancelar y abra los ajustes.\"\nBasic.AutoConfig.Info=\"El asistente de configuración automática determinará la mejor configuración basada en las especificaciones de su computadora y la velocidad de Internet.\"\nBasic.AutoConfig.RunAnytime=\"Esto puede ejecutarse en cualquier momento desde el menú Herramientas.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Resolución de la transmisión (escalada) \"\nBasic.Stats=\"Estadísticas\"\nBasic.Stats.CPUUsage=\"Uso de CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Espacio en disco disponible\"\nBasic.Stats.MemoryUsage=\"Uso de memoria\"\nBasic.Stats.AverageTimeToRender=\"Tiempo de media para procesar un fotograma\"\nBasic.Stats.SkippedFrames=\"Fotogramas omitidos por retraso en la codificación\"\nBasic.Stats.MissedFrames=\"Fotogramas perdidos por retraso de procesamiento\"\nBasic.Stats.Output.Stream=\"Transmisión\"\nBasic.Stats.Output.Recording=\"Grabación\"\nBasic.Stats.Status=\"Estado\"\nBasic.Stats.Status.Recording=\"Grabando\"\nBasic.Stats.Status.Live=\"EN VIVO\"\nBasic.Stats.Status.Reconnecting=\"Reconectando\"\nBasic.Stats.Status.Inactive=\"Inactivo\"\nBasic.Stats.Status.Active=\"Activo\"\nBasic.Stats.DroppedFrames=\"Fotogramas perdidos (Red)\"\nBasic.Stats.MegabytesSent=\"Salida de datos total\"\nBasic.Stats.DiskFullIn=\"Disco lleno en (aprox.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 horas, %2 minutos\"\nBasic.Stats.ResetStats=\"Resetear estadísticas\"\nResetUIWarning.Title=\"¿Está seguro de que desea restablecer la interfaz?\"\nResetUIWarning.Text=\"Restablecer la interfaz ocultará los paneles adicionales. Necesitarás mostrar estos paneles desde el menú Paneles si quieres que sean visibles.\\n\\n¿Estás seguro de que quieres restablecer la interfaz?\"\nUpdater.Title=\"Nueva actualización disponible\"\nUpdater.Text=\"Hay una nueva versión disponible:\"\nUpdater.UpdateNow=\"Actualizar ahora\"\nUpdater.RemindMeLater=\"Recordármelo más tarde\"\nUpdater.Skip=\"Saltar versión\"\nUpdater.NoUpdatesAvailable.Title=\"No hay actualizaciones disponibles\"\nUpdater.NoUpdatesAvailable.Text=\"No hay actualizaciones disponibles actualmente\"\nUpdater.BranchNotFound.Title=\"Canal de actualización eliminado\"\nUpdater.BranchNotFound.Text=\"El canal de actualización seleccionado ya no está disponible, OBS se ha restablecido al valor predeterminado.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Comprobación de integridad no disponible\"\nUpdater.RepairButUpdatesAvailable.Text=\"Comprobar la integridad de los archivos sólo es posible para la última versión disponible. Utilice Ayuda → Buscar actualizaciones para verificar y actualizar su instalación de OBS.\"\nUpdater.RepairConfirm.Title=\"Confirmar verificación de integridad\"\nUpdater.RepairConfirm.Text=\"Al iniciar la comprobación de integridad se escaneará la instalación de OBS para detectar archivos corruptos/modificados y descargarlos de nuevo. Esto puede llevar un tiempo.\\n\\n¿Desea continuar?\"\nUpdater.FailedToLaunch=\"No se pudo iniciar el actualizador\"\nQuickTransitions.SwapScenes=\"Intercambiar escenas de vista previa/programa después de la transición\"\nQuickTransitions.SwapScenesTT=\"Intercambia la vista previa y las escenas del programa después de la transición (si la escena original del programa continúa existiendo).\\nEsto no deshará ningún cambio que se haya realizado a la escena original del programa.\"\nQuickTransitions.DuplicateScene=\"Duplicar escena\"\nQuickTransitions.DuplicateSceneTT=\"Al editar la misma escena, permite editar la transformación/visibilidad de las fuentes sin modificar la salida del programa.\\nPara editar las propiedades de las fuentes sin modifcar la salida del programa, habilita 'Duplicar Fuentes'.\\nCambiando este valor se restablecerá la escena actual de salida (si aún existe).\"\nQuickTransitions.EditProperties=\"Duplicar fuentes\"\nQuickTransitions.EditPropertiesTT=\"Cuando se edita la misma escena, permite editar las propiedades de las fuentes sin modificar la salida del programa.\\nEsto solo puede ser usado si 'Duplicar escena' está habilitado.\\nCiertas fuentes (como fuentes de captura o multimedia) no soportan esto y no pueden ser editadas por separado.\\nCambiar este valor restablecerá la escena actual (si aún existe).\\n\\nAdvertencia: Debido a que las fuentes serán duplicadas, esto puede requerir recursos adicionales del sistema o de vídeo.\"\nQuickTransitions.HotkeyName=\"Transición Rápida: %1\"\nBasic.AddTransition=\"Añadir transición configurable\"\nBasic.RemoveTransition=\"Quitar transición configurable\"\nBasic.TransitionProperties=\"Propiedades de la transición\"\nBasic.SceneTransitions=\"Transiciones de escena\"\nBasic.TransitionDuration=\"Duración\"\nBasic.TogglePreviewProgramMode=\"Modo Estudio\"\nBasic.EnablePreviewProgramMode=\"Habilitar modo estudio\"\nBasic.DisablePreviewProgramMode=\"Deshabilitar modo estudio\"\nUndo.Undo=\"Deshacer\"\nUndo.Redo=\"Rehacer\"\nUndo.Add=\"Agregar '%1'\"\nUndo.Delete=\"Eliminar '%1'\"\nUndo.Rename=\"Renombrar '%1'\"\nUndo.SceneCollection.Switch=\"Cambiar a '%1'\"\nUndo.Item.Undo=\"Deshacer %1\"\nUndo.Item.Redo=\"Rehacer %1\"\nUndo.Sources.Multi=\"Eliminar %1 fuentes\"\nUndo.Filters=\"Cambios en el filtro '%1'\"\nUndo.Filters.Paste.Single=\"Pegar filtro '%1' a '%2'\"\nUndo.Filters.Paste.Multiple=\"Copiar filtros de '%1' a '%2'\"\nUndo.Transform=\"Transformar fuente(s) en '%1'\"\nUndo.Transform.Paste=\"Pegar transformación en '%1'\"\nUndo.Transform.Rotate=\"Rotación en '%1'\"\nUndo.Transform.Reset=\"Restablecer transformación en '%1'\"\nUndo.Transform.HFlip=\"Volteo horizontal en '%1'\"\nUndo.Transform.VFlip=\"Volteo vertical en '%1'\"\nUndo.Transform.FitToScreen=\"Ajustar a la pantalla en '%1'\"\nUndo.Transform.StretchToScreen=\"Estirar a la pantalla en '%1'\"\nUndo.Transform.Center=\"Centrar a la pantalla en '%1'\"\nUndo.Transform.VCenter=\"Centrar verticalmente a la pantalla en '%1'\"\nUndo.Transform.HCenter=\"Centrar horizontalmente a la pantalla en '%1'\"\nUndo.Volume.Change=\"Cambiar volumen en '%1'\"\nUndo.Volume.Mute=\"Silenciar '%1'\"\nUndo.Volume.Unmute=\"Dejar de silenciar '%1'\"\nUndo.Balance.Change=\"Cambio en balance de audio en '%1'\"\nUndo.SyncOffset.Change=\"Cambio en sincronización de audio en '%1'\"\nUndo.MonitoringType.Change=\"Cambio de monitorización de audio en '%1'\"\nUndo.Mixers.Change=\"Cambio en mezclador de audio en '%1'\"\nUndo.ForceMono.On=\"Habilitar forzar Mono en '%1'\"\nUndo.ForceMono.Off=\"Deshabilitar forzar Mono en '%1'\"\nUndo.Properties=\"Cambio en propiedades en '%1'\"\nUndo.Scene.Duplicate=\"Duplicar escena '%1'\"\nUndo.ShowTransition=\"Transición al mostrar en '%1'\"\nUndo.HideTransition=\"Transición al ocultar en '%1'\"\nUndo.ShowSceneItem=\"Mostrar '%1' en '%2'\"\nUndo.HideSceneItem=\"Ocultar '%1' en '%2'\"\nUndo.ReorderSources=\"Reordenar fuentes en '%1'\"\nUndo.MoveUp=\"Mover '%1' arriba en '%2'\"\nUndo.MoveDown=\"Mover '%1' abajo en '%2'\"\nUndo.MoveToTop=\"Mover '%1' al principio en '%2'\"\nUndo.MoveToBottom=\"Mover '%1' al final en '%2'\"\nUndo.PasteSource=\"Pegar fuente(s) en '%1'\"\nUndo.PasteSourceRef=\"Pegar referencia de fuente(s) en '%1'\"\nUndo.GroupItems=\"Agrupar elementos en '%1'\"\nTransitionNameDlg.Text=\"Por favor, introduzca el nombre de la transición\"\nTransitionNameDlg.Title=\"Nombre de la transición\"\nTitleBar.SafeMode=\"MODO SEGURO\"\nTitleBar.PortableMode=\"Modo portable\"\nTitleBar.Profile=\"Perfil\"\nTitleBar.Scenes=\"Escenas\"\nNameExists.Title=\"Ese nombre ya existe\"\nNameExists.Text=\"Este nombre ya está en uso.\"\nNoNameEntered.Title=\"Por favor, introduzca un nombre válido\"\nNoNameEntered.Text=\"No puede dejar el nombre vacío.\"\nConfirmStart.Title=\"¿Iniciar Transmisión?\"\nConfirmStart.Text=\"¿Está seguro que desea iniciar la transmisión?\"\nConfirmStop.Title=\"¿Parar Transmisión?\"\nConfirmStop.Text=\"¿Está seguro que desea parar la transmisión?\"\nConfirmStopRecord.Title=\"¿Detener la grabación?\"\nConfirmStopRecord.Text=\"¿Estás seguro de que deseas parar la grabación?\"\nConfirmBWTest.Title=\"¿Iniciar prueba de ancho de banda?\"\nConfirmBWTest.Text=\"Tienes OBS configurado en modo de prueba de ancho de banda. Este modo permite pruebas de red sin que tu canal esté en vivo. Una vez que hayas terminado de probar, necesitarás desactivarlo para que los espectadores puedan ver tu stream.\\n\\n¿Quieres continuar?\"\nConfirmExit.Title=\"Salidas activas\"\nConfirmExit.Text=\"OBS sigue activo. Se cerrarán todas las transmisiones y grabaciones.\"\nConfirmRemove.Title=\"Confirmar borrado\"\nConfirmRemove.Text=\"¿Seguro que quieres eliminar '%1'?\"\nConfirmRemove.TextMultiple=\"¿Seguro que quieres eliminar %1 elementos?\"\nConfirmReset.Title=\"Restablecer propiedades\"\nConfirmReset.Text=\"¿Estás seguro de que deseas restablecer las propiedades actuales a sus valores predeterminados?\"\nOutput.StartStreamFailed=\"No se pudo iniciar la emisión\"\nOutput.StartRecordingFailed=\"No se pudo iniciar grabación\"\nOutput.StartReplayFailed=\"No se pudo iniciar el búfer de repetición\"\nOutput.StartVirtualCamFailed=\"Error al iniciar la cámara virtual\"\nOutput.StartFailedGeneric=\"No se pudo iniciar la salida. Por favor comprueba el registro para más detalles.\\n\\nNota: Si estás usando los codificadores NVENC o AMD, asegúrate de que tus drivers de vídeo están actualizados.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"No se pueden guardar repeticiones mientras está pausado\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Advertencia: No se pueden guardar repeticiones mientras la grabación está pausada.\"\nOutput.ConnectFail.Title=\"Error al conectarse\"\nOutput.ConnectFail.BadPath=\"URL ruta de acceso o conexión no válida. Por favor, compruebe su configuración para confirmar que está correcta.\"\nOutput.ConnectFail.ConnectFailed=\"No se pudo conectar al servidor\"\nOutput.ConnectFail.InvalidStream=\"No se pudo acceder al canal especificado o clave de transmisión, por favor compruebe su clave de transmisión. Si es correcta, puede haber un problema al conectar al servidor.\"\nOutput.ConnectFail.HdrDisabled=\"La salida HDR está actualmente deshabilitada para esta salida.\"\nOutput.ConnectFail.Error=\"Se ha producido un error inesperado al intentar conectar con el servidor. Más información en el archivo de registro.\"\nOutput.ConnectFail.Disconnected=\"Desconectado del servidor.\"\nOutput.StreamEncodeError.Title=\"Error de codificación\"\nOutput.StreamEncodeError.Msg=\"Un error de codificación se produjo durante el Streaming.\"\nOutput.StreamEncodeError.Msg.LastError=\"Se produjo un error del codificador durante la transmisión:<br><br>%1\"\nOutput.RecordFail.Title=\"No se pudo iniciar grabación\"\nOutput.RecordFail.Unsupported=\"El formato de salida no está soportado o no soporta más de una pista de audio. Por favor, comprueba tus ajustes e inténtalo de nuevo.\"\nOutput.RecordNoSpace.Title=\"No hay suficiente espacio en disco\"\nOutput.RecordNoSpace.Msg=\"No hay suficiente espacio en disco para continuar grabando.\"\nOutput.RecordError.Title=\"Error en la grabación\"\nOutput.RecordError.Msg=\"Se ha producido un error no especificado durante la grabación.\"\nOutput.RecordError.EncodeErrorMsg=\"Un error de codificación se produjo mientras se grababa.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Se produjo un error del codificador durante la grabación:<br><br>%1\"\nOutput.BadPath.Title=\"Ruta de archivo incorrecta\"\nOutput.BadPath.Text=\"No se pudo abrir la ruta de grabación configurada. Por favor, comprueba la ruta de grabación en Ajustes → Salida → Grabación.\"\nOutput.NoBroadcast.Title=\"Ninguna emisión configurada\"\nOutput.NoBroadcast.Text=\"Necesitas configurar una emisión antes de poder comenzar a transmitir.\"\nOutput.BroadcastStartFailed=\"No se pudo iniciar la transmisión\"\nOutput.BroadcastStopFailed=\"No se pudo detener la transmisión\"\nLogUploadDialog.Title=\"Subir el registro de errores de OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Por favor, lee la <a href='https://obsproject.com/privacy-policy'>política de privacidad</a> y su sección acerca de la subida de archivos antes de subir alguno.\"\nLogUploadDialog.Labels.Progress=\"Subida del registro en curso. Por favor, espera...\"\nLogUploadDialog.Labels.Description.AppLog=\"Tu archivo de registro ha sido cargado. Ahora puedes compartir la URL para fines de depuración o soporte.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Tu informe de error ha sido cargado. Ahora puedes compartir la URL para fines de depuración.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Subir\"\nLogUploadDialog.Buttons.CopyURL=\"Copiar la URL del registro\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analizar el archivo de registro\"\nLogUploadDialog.Buttons.RetryButton=\"Reintentar\"\nLogUploadDialog.Errors.Template=\"Se ha producido un error en intentar subir el archivo:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"No se ha encontrado ningún archivo para subir o estaba vacío.\"\nRemux.SourceFile=\"Grabación OBS\"\nRemux.TargetFile=\"Archivo de destino\"\nRemux.Remux=\"Convertir\"\nRemux.Stop=\"Detener conversión\"\nRemux.ClearFinished=\"Borrar los elementos finalizados\"\nRemux.ClearAll=\"Borrar todos los elementos\"\nRemux.OBSRecording=\"Grabación de OBS\"\nRemux.FinishedTitle=\"Conversión finalizada\"\nRemux.Finished=\"Grabación convertida\"\nRemux.FinishedError=\"Grabación convertida, pero el archivo podría estar incompleto\"\nRemux.SelectRecording=\"Seleccione grabación de OBS...\"\nRemux.SelectTarget=\"Seleccione archivo de destino...\"\nRemux.FileExistsTitle=\"Los archivos de destino ya existen\"\nRemux.FileExists=\"Los siguientes archivos de destino ya existen. ¿Quieres reemplazarlos?\"\nRemux.ExitUnfinishedTitle=\"Conversión en curso\"\nRemux.ExitUnfinished=\"La conversión no ha finalizado, detenerla ahora puede inutilizar el archivo de destino.\\n¿Estás seguro de querer parar la conversión?\"\nRemux.HelpText=\"Suelta los archivos en esta ventana para convertirlos, o selecciona una celda vacía \\\"OBS Recording\\\" para buscar un archivo.\"\nRemux.NoFilesAddedTitle=\"No se ha añadido ningún archivo para convertir\"\nRemux.NoFilesAdded=\"No se ha añadido ningún archivo para convertir. Arrastra una carpeta que contenga uno o más archivos de video.\"\nMissingFiles=\"Archivos no encontrados\"\nMissingFiles.MissingFile=\"Archivo perdido\"\nMissingFiles.NewFile=\"Nuevo archivo\"\nMissingFiles.HelpText=\"Faltan algunos archivos desde la última vez que usaste OBS.\"\nMissingFiles.Clear=\"<vacío>\"\nMissingFiles.NumFound=\"Encontrado %1 de %2\"\nMissingFiles.Search=\"Directorio de búsqueda...\"\nMissingFiles.SelectFile=\"Seleccionar archivo...\"\nMissingFiles.SelectDir=\"Selecciona carpeta donde buscar\"\nMissingFiles.State=\"Estado\"\nMissingFiles.Missing=\"Perdido\"\nMissingFiles.Replaced=\"Reemplazado\"\nMissingFiles.Cleared=\"Vacío\"\nMissingFiles.Found=\"Encontrado\"\nMissingFiles.AutoSearch=\"Encontradas coincidencias de archivos adicionales\"\nMissingFiles.AutoSearchText=\"OBS ha encontrado coincidencias adicionales para archivos perdidos en ese directorio. ¿Quieres añadirlos?\"\nMissingFiles.NoMissing.Title=\"Comprobación de archivos faltantes\"\nMissingFiles.NoMissing.Text=\"No parece que falte ningún archivo.\"\nMacPermissions.MenuAction=\"Revisar los permisos de la aplicación...\"\nMacPermissions.Title=\"Revisión de permisos de la aplicación\"\nMacPermissions.Description=\"OBS Studio requiere su permiso para proporcionar ciertas características. Se recomienda activar estos permisos, pero no son necesarios para usar la aplicación. Siempre puedes activarlos más tarde.\"\nMacPermissions.Description.OpenDialog=\"Puede volver a abrir este diálogo a través del menú de OBS Studio.\"\nMacPermissions.AccessGranted=\"Acceso autorizado\"\nMacPermissions.RequestAccess=\"Solicitud de acceso\"\nMacPermissions.OpenPreferences=\"Abrir preferencias %1\"\nMacPermissions.Item.ScreenRecording=\"Grabación de pantalla\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS requiere este permiso para poder capturar la pantalla.\"\nMacPermissions.Item.Camera=\"Cámara\"\nMacPermissions.Item.Camera.Details=\"Este permiso es necesario para capturar contenido desde una cámara web o tarjeta de captura.\"\nMacPermissions.Item.Microphone=\"Micrófono\"\nMacPermissions.Item.Microphone.Details=\"OBS requiere este permiso si quieres capturar tu micrófono o un dispositivo de audio externo.\"\nMacPermissions.Item.InputMonitoring=\"Monitorización de entrada\"\nMacPermissions.Item.InputMonitoring.Details=\"Este permiso es necesario para que las teclas de acceso rápido funcionen mientras OBS está en segundo plano.\"\nMacPermissions.Item.Accessibility=\"Accesibilidad\"\nMacPermissions.Item.Accessibility.Details=\"En instalaciones antiguas, es posible que OBS aparezca en \\\"Accesibilidad\\\" en lugar de \\\"Monitorización de entrada\\\".\"\nMacPermissions.Continue=\"Continuar\"\nSourceLeak.Title=\"Error al liberar la fuente\"\nSourceLeak.Text=\"Hubo un problema al cambiar las colecciones de escenas y algunas fuentes no se han podido liberar. Este problema generalmente está causado por plugins que no han liberado los recursos correctamente. Asegúrate de que los plugins que estes usando estén actualizados.\\n\\nOBS Studio se cerrará ahora para evitar cualquier posible corrupción de datos.\"\nBasic.DesktopDevice1=\"Audio del escritorio\"\nBasic.DesktopDevice2=\"Audio del escritorio 2\"\nBasic.Scene=\"Escena\"\nBasic.DisplayCapture=\"Captura de pantalla\"\nBasic.Main.PreviewConextMenu.Enable=\"Habilitar previsualización\"\nBasic.Main.Preview.Disable=\"Deshabilitar previsualización\"\nScaleFiltering=\"Filtrado de escala\"\nScaleFiltering.Point=\"Punto\"\nScaleFiltering.Bilinear=\"Bilineal\"\nScaleFiltering.Bicubic=\"Bicúbico\"\nScaleFiltering.Area=\"Área\"\nBlendingMethod=\"Método de fusión\"\nBlendingMethod.Default=\"Predeterminado\"\nBlendingMethod.SrgbOff=\"SRGB desactivado\"\nBlendingMode=\"Modo de fusión\"\nBlendingMode.Additive=\"Añadir\"\nBlendingMode.Subtract=\"Restar\"\nBlendingMode.Screen=\"Pantalla\"\nBlendingMode.Multiply=\"Multiplicar\"\nBlendingMode.Lighten=\"Aclarar\"\nBlendingMode.Darken=\"Oscurecer\"\nDeinterlacing=\"Desentrelazado\"\nDeinterlacing.Discard=\"Descartar\"\nDeinterlacing.TopFieldFirst=\"Campo Superior Primero\"\nDeinterlacing.BottomFieldFirst=\"Campo Inferior Primero\"\nVolControl.SliderUnmuted=\"Deslizador de volumen para '%1':\"\nVolControl.SliderMuted=\"Deslizador de volumen para '%1': (actualmente silenciado)\"\nVolControl.Mute=\"Silenciar '%1'\"\nVolControl.Properties=\"Propiedades para '%1'\"\nVolControl.UnassignedWarning.Title=\"Fuente de audio no asignada\"\nVolControl.UnassignedWarning.Text=\"«%1» no está asignado a ninguna pista de audio y no se oirá en las transmisiones o grabaciones.\\n\\nPara asignar una fuente de audio a una pista, abre las propiedades de audio avanzadas mediante el menú del botón derecho o el botón del engranaje en la barra de herramientas del mezclador de audio.\"\nBasic.Main.AddSceneDlg.Title=\"Añadir escena\"\nBasic.Main.AddSceneDlg.Text=\"Por favor, introduzca el nombre de la escena\"\nBasic.Main.DefaultSceneName.Text=\"Escena %1\"\nBasic.Main.AddSceneCollection.Title=\"Agregar colección de escenas\"\nBasic.Main.AddSceneCollection.Text=\"Por favor escriba el nombre de la colección de escenas\"\nBasic.Main.RenameSceneCollection.Title=\"Renombrar la colección de escenas\"\nBasic.Main.RemigrateSceneCollection.Title=\"Actualizar la resolución de la colección de escenas\"\nBasic.Main.RemigrateSceneCollection.Text=\"¿Quieres actualizar la resolución de la colección de escenas de \\\"%1\\\" para que coincida con la resolución actual del lienzo del perfil  %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"No se puede actualizar la resolución de la colección de escenas mientras las salidas están activas.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"No se pudo actualizar la resolución de la colección de escenas. La resolución original es desconocida.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"No es posible restablecer: Falló el cambio de resolución de OBS.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"No es posible restablecer: La resolución actual ya es la resolución base de la colección de escenas.\"\nAddProfile.Title=\"Añadir perfil\"\nAddProfile.Text=\"Escriba el nombre del perfil\"\nAddProfile.WizardCheckbox=\"Mostrar el asistente de configuración automática\"\nRenameProfile.Title=\"Renombrar perfil\"\nBasic.Main.MixerRename.Title=\"Renombrar la fuente de audio\"\nBasic.Main.MixerRename.Text=\"Por favor, introduzca el nombre de la fuente de audio\"\nBasic.Main.PreviewDisabled=\"Vista previa desactivada\"\nBasic.SourceSelect=\"Crear/seleccionar fuente\"\nBasic.SourceSelect.CreateNew=\"Crear nuevo\"\nBasic.SourceSelect.AddExisting=\"Añadir existentes\"\nBasic.SourceSelect.AddVisible=\"Hacer visible la fuente\"\nBasic.Main.Sources.Visibility=\"Visibilidad\"\nBasic.Main.Sources.VisibilityDescription=\"Controla la visibilidad de '%1' en el lienzo\"\nBasic.Main.Sources.Lock=\"Bloquear\"\nBasic.Main.Sources.LockDescription=\"Bloquea la posición y la escala de '%1' en el lienzo\"\nBasic.PropertiesWindow=\"Propiedades para '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (seleccionar: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Seleccionar color\"\nBasic.PropertiesWindow.SelectFont=\"Elija fuente\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Elige una fuente\"\nBasic.PropertiesWindow.ConfirmTitle=\"Configuración cambiada\"\nBasic.PropertiesWindow.Confirm=\"Hay cambios sin guardar. ¿Deseas conservarlos?\"\nBasic.PropertiesWindow.NoProperties=\"No hay propiedades disponibles\"\nBasic.PropertiesWindow.AddFiles=\"Agregar archivos\"\nBasic.PropertiesWindow.AddDir=\"Añadir Carpeta\"\nBasic.PropertiesWindow.AddURL=\"Añadir ruta/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Añadir Carpeta a '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Añadir archivos a '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Agregar entrada a '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Editar entrada de '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Valores simples de FPS\"\nBasic.PropertiesView.FPS.Rational=\"Valores raciones de FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Rangos validos de FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"¿Abrir este enlace en su navegador predeterminado?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Abrir URL\"\nBasic.InteractionWindow=\"Interactuando con '%1'\"\nBasic.StatusBar.Reconnecting=\"Desconectado, reconexión en %2 segundo(s) (tentativa%1)\"\nBasic.StatusBar.AttemptingReconnect=\"Intentando volver a conectar... (intento %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Reconexión exitosa\"\nBasic.StatusBar.Delay=\"Retardo (%1 sec)\"\nBasic.StatusBar.DelayStartingIn=\"Retardo (iniciando en %1 sec)\"\nBasic.StatusBar.DelayStoppingIn=\"Retardo (parando en %1 sec)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Retardo (parando en %1 sec, iniciando en %2 sec)\"\nBasic.StatusBar.RecordingSavedTo=\"Grabación guardada en '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Búfer de repetición guardado en '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Captura de pantalla guardada en '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Grabación convertida automáticamente en '%1'\"\nBasic.Filters=\"Filtros\"\nBasic.Filters.AsyncFilters=\"Filtros de audio/vídeo\"\nBasic.Filters.AudioFilters=\"Filtros de audio\"\nBasic.Filters.EffectFilters=\"Filtros de efecto\"\nBasic.Filters.Title=\"Filtros para '%1'\"\nBasic.Filters.AddFilter.Title=\"Nombre del filtro\"\nBasic.Filters.AddFilter.Text=\"Por favor especifique el nombre del filtro\"\nBasic.TransformWindow=\"Ajustes de escena\"\nBasic.TransformWindow.Position=\"Posición\"\nBasic.TransformWindow.PositionX=\"Posición X\"\nBasic.TransformWindow.PositionY=\"Posición Y\"\nBasic.TransformWindow.Rotation=\"Rotación\"\nBasic.TransformWindow.Size=\"Tamaño\"\nBasic.TransformWindow.Width=\"Ancho\"\nBasic.TransformWindow.Height=\"Alto\"\nBasic.TransformWindow.Alignment=\"Alineamiento posicional\"\nBasic.TransformWindow.BoundsType=\"Tipo de cuadro delimitador\"\nBasic.TransformWindow.BoundsAlignment=\"Alineamiento en el cuadro delimitador\"\nBasic.TransformWindow.Bounds=\"Tamaño de cuadro delimitador\"\nBasic.TransformWindow.BoundsWidth=\"Ancho del cuadro delimitador\"\nBasic.TransformWindow.BoundsHeight=\"Alto del cuadro delimitador\"\nBasic.TransformWindow.CropToBounds=\"Recortar hasta el cuadro limitador\"\nBasic.TransformWindow.Crop=\"Recorte\"\nBasic.TransformWindow.CropLeft=\"Recortar a la izquierda\"\nBasic.TransformWindow.CropRight=\"Recortar a la derecha\"\nBasic.TransformWindow.CropTop=\"Recortar arriba\"\nBasic.TransformWindow.CropBottom=\"Recortar abajo\"\nBasic.TransformWindow.Alignment.TopLeft=\"Parte superior izquierda\"\nBasic.TransformWindow.Alignment.TopCenter=\"Parte superior central\"\nBasic.TransformWindow.Alignment.TopRight=\"Parte superior derecha\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Parte central izquierda\"\nBasic.TransformWindow.Alignment.Center=\"Centrado\"\nBasic.TransformWindow.Alignment.CenterRight=\"Parte central derecha\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Parte inferior izquierda\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Parte central inferior\"\nBasic.TransformWindow.Alignment.BottomRight=\"Parte inferior derecha\"\nBasic.TransformWindow.BoundsType.None=\"Sin límites\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Limitado a tamaño máximo\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Escalar hasta límites internos\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Escalar hasta límites externos\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Escalar al ancho de los límites\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Escalar a la altura de los límites\"\nBasic.TransformWindow.BoundsType.Stretch=\"Extender hasta los límites\"\nBasic.TransformWindow.Title=\"Editar transformación para '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Ninguna fuente seleccionada\"\nBasic.Main.AddSourceHelp.Title=\"No se puede agregar la fuente\"\nBasic.Main.AddSourceHelp.Text=\"Tienes que tener al menos 1 escena para agregar una fuente.\"\nBasic.Main.Scenes=\"Escenas\"\nBasic.Main.Sources=\"Fuentes\"\nBasic.Main.Source=\"Fuente\"\nBasic.Main.Controls=\"Controles\"\nBasic.Main.PreparingStream=\"Preparando...\"\nBasic.Main.Connecting=\"Conectando...\"\nBasic.Main.StartRecording=\"Iniciar Grabación\"\nBasic.Main.StartReplayBuffer=\"Iniciar el Búfer de Repetición\"\nBasic.Main.SaveReplay=\"Guardar repetición\"\nBasic.Main.StartStreaming=\"Iniciar Transmisión\"\nBasic.Main.StartBroadcast=\"Emitir en directo\"\nBasic.Main.StartVirtualCam=\"Iniciar Cámara Virtual\"\nBasic.Main.StopRecording=\"Detener Grabación\"\nBasic.Main.PauseRecording=\"Pausar Grabación\"\nBasic.Main.UnpauseRecording=\"Reanudar Grabación\"\nBasic.Main.SplitFile=\"Dividir archivo de grabación\"\nBasic.Main.AddChapterMarker=\"Agregar marcador de capítulo (sólo MP4 híbrido/MOV)\"\nBasic.Main.StoppingRecording=\"Deteniendo Grabación...\"\nBasic.Main.StopReplayBuffer=\"Detener el Búfer de Repetición\"\nBasic.Main.StoppingReplayBuffer=\"Deteniendo el Búfer de Repetición...\"\nBasic.Main.SetupBroadcast=\"Administrar emisión\"\nBasic.Main.StopStreaming=\"Detener Transmisión\"\nBasic.Main.StopBroadcast=\"Finalizar transmisión\"\nBasic.Main.AutoStopEnabled=\"Detener automáticamente\"\nBasic.Main.StoppingStreaming=\"Deteniendo la trasmisión...\"\nBasic.Main.ForceStopStreaming=\"Parar Transmisión (descartar retardo)\"\nBasic.Main.ShowContextBar=\"Mostrar barra de herramientas de fuente\"\nBasic.Main.HideContextBar=\"Ocultar barra de herramientas de fuente\"\nBasic.Main.StopVirtualCam=\"Detener Cámara Virtual\"\nBasic.Main.Group=\"Grupo %1\"\nBasic.Main.GroupItems=\"Agrupar los elementos seleccionados\"\nBasic.Main.Ungroup=\"Desagrupar\"\nBasic.Main.GridMode=\"Modo Cuadrícula\"\nBasic.Main.ListMode=\"Modo Lista\"\nBasic.Main.VirtualCamConfig=\"Configurar cámara virtual\"\nBasic.VCam.VirtualCamera=\"Cámara virtual\"\nBasic.VCam.OutputType=\"Tipo de salida\"\nBasic.VCam.OutputSelection=\"Selección de salida\"\nBasic.VCam.OutputType.Program=\"Programa (Por Defecto)\"\nBasic.VCam.OutputSelection.NoSelection=\"Ninguna selección para este tipo de salida\"\nBasic.VCam.RestartWarning=\"La cámara virtual se reiniciará para aplicar este cambio\"\nBasic.MainMenu.File=\"Archivo (&F)\"\nBasic.MainMenu.File.Export=\"&Exportar\"\nBasic.MainMenu.File.Import=\"&Importar\"\nBasic.MainMenu.File.ShowRecordings=\"Mostrar g&rabaciones\"\nBasic.MainMenu.File.Remux=\"Convertir grabaciones (&M)\"\nBasic.MainMenu.File.Settings=\"Ajuste&s\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Mostrar carpeta de configuración\"\nBasic.MainMenu.File.ShowProfileFolder=\"Mostrar carpeta de perfiles\"\nBasic.MainMenu.File.ShowMissingFiles=\"Comprobar si faltan archivos\"\nBasic.MainMenu.File.Exit=\"Salir (&X)\"\nBasic.MainMenu.Edit=\"&Editar\"\nBasic.MainMenu.Edit.Undo=\"Deshacer (&U)\"\nBasic.MainMenu.Edit.Redo=\"&Rehacer\"\nBasic.MainMenu.Edit.LockPreview=\"B&loquear vista previa\"\nBasic.MainMenu.Edit.Scale=\"Vi&sta previa y escala\"\nBasic.MainMenu.Edit.Scale.Window=\"Ajustar a la ventana\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Lienzo (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Salida (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Escalado (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Acercar\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Alejar\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Restablecer zoom\"\nBasic.MainMenu.Edit.Transform=\"&Transformar\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Editar transformación...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Copiar transformación\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Pegar trasformación\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Restablecer transformación\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Girar 90 grados en el sentido de las agujas del reloj\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Girar 90 grados contra el sentido de las agujas del reloj\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Girar 180 grados\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Rotar &horizontalmente\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Rotar &verticalmente\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Ajustar a la pantalla (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"E&stirar a la pantalla\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centrar en la pantalla\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centrar verticalmente\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centrar horizontalmente\"\nBasic.MainMenu.Edit.Order=\"&Ordenar\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Mover arriba (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Mover abajo (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Mover a la cima (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Mover al fondo (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Propiedades de &audio avanzadas\"\nBasic.MainMenu.View=\"&Vista\"\nBasic.MainMenu.View.Toolbars=\"Barra de Herramien&tas\"\nBasic.MainMenu.View.ListboxToolbars=\"Barra de herramientas de paneles\"\nBasic.MainMenu.View.ContextBar=\"Barra de herramientas de fuente\"\nBasic.MainMenu.View.SourceIcons=\"&Iconos de fuentes\"\nBasic.MainMenu.View.StatusBar=\"Barra de e&stado\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Pantalla completa\"\nBasic.MainMenu.View.ResetUI=\"&Restablecer la interfaz de usuario\"\nBasic.MainMenu.View.AlwaysOnTop=\"Siempre en primer pl&ano\"\nBasic.MainMenu.View.SceneListMode=\"Modo lista de escenas\"\nBasic.MainMenu.Docks=\"Paneles (&D)\"\nBasic.MainMenu.Docks.ResetDocks=\"&Restablecer paneles\"\nBasic.MainMenu.Docks.LockDocks=\"&Bloquear paneles\"\nBasic.MainMenu.Docks.SideDocks=\"Paneles a altura completa (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Paneles de navegador personalizados... (&C)\"\nBasic.MainMenu.Import=\"Importar...\"\nBasic.MainMenu.Export=\"Exportar...\"\nBasic.MainMenu.SceneCollection=\"Colección de E&scenas\"\nBasic.MainMenu.Profile=\"&Perfil\"\nBasic.MainMenu.Profile.Import=\"Importar perfil\"\nBasic.MainMenu.Profile.Export=\"Exportar perfil\"\nBasic.MainMenu.SceneCollection.Import=\"Importar colección de escenas\"\nBasic.MainMenu.SceneCollection.Export=\"Exportar colección de escenas\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Restablecer la resolución base\"\nBasic.MainMenu.SceneCollection.Migrate=\"Establecer la resolución base\"\nBasic.MainMenu.Profile.Exists=\"El perfil ya existe\"\nBasic.MainMenu.Tools=\"Herramien&tas\"\nBasic.MainMenu.Help=\"Ayuda (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Ayuda (&P)\"\nBasic.MainMenu.Help.Website=\"Visitar sitio &web\"\nBasic.MainMenu.Help.Discord=\"Unirse al servidor de &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Novedades\"\nBasic.MainMenu.Help.Logs=\"Archivos de registro (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Mostrar archivo&s de registro\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"&Cargar el archivo de registro actual\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Cargar el archivo de registro anterior (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Ver registro actual\"\nBasic.MainMenu.Help.ReleaseNotes=\"Notas de la versión\"\nBasic.MainMenu.Help.CheckForUpdates=\"Comprobar actualizaciones\"\nBasic.MainMenu.Help.Repair=\"Comprobar integridad de los archivos\"\nBasic.MainMenu.Help.RestartSafeMode=\"Reiniciar en modo seguro\"\nBasic.MainMenu.Help.RestartNormal=\"Reiniciar en modo normal\"\nBasic.MainMenu.Help.CrashLogs=\"Informes de e&rror\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Mo&strar informes de error\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Cargar el informe de error anterior (&P)\"\nBasic.MainMenu.Help.About=\"&Acerca de\"\nBasic.Settings.ProgramRestart=\"El programa debe reiniciarse para que esta configuración surta efecto.\"\nBasic.Settings.ConfirmTitle=\"Confirmar cambios\"\nBasic.Settings.Confirm=\"Hay cambios sin guardar. ¿Guardar los cambios?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 está controlando algunos de los ajustes de su transmisión\"\nBasic.Settings.General.Language=\"Idioma\"\nBasic.Settings.General.Updater=\"Actualizaciones\"\nBasic.Settings.General.UpdateChannel=\"Actualizar canal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Deshabilitado)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Predeterminado)\"\nBasic.Settings.General.EnableAutoUpdates=\"Comprobar actualizaciones automáticamente al inicio\"\nBasic.Settings.General.OpenStatsOnStartup=\"Abrir diálogo de estadísticas al inicio\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Ocultar las ventanas de OBS de la captura de pantalla\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Esta opción ocultará todas las ventanas de OBS Studio que no sean proyectores de ser capturadas por OBS y afectará a\\notras aplicaciones, como conferencias, compartir pantallas, soporte remoto, capturas de pantalla y otros programas de captura.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Habilitar esta opción oculta todas las ventanas de OBS Studio que no sean proyectores de ser capturadas por OBS y afecta a otras aplicaciones, como conferencias, compartir pantallas, soporte remoto, capturas de pantalla y otros programas de captura.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Mostrar diálogo de confirmación cuando se inicia una transmisión\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Mostrar diálogo de confirmación cuando se para una transmisión\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Mostrar diálogo de confirmación al detener la grabación\"\nBasic.Settings.General.Projectors=\"Proyectores\"\nBasic.Settings.General.HideProjectorCursor=\"Ocultar el cursor sobre proyectores\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Proyectores siempre en primer plano\"\nBasic.Settings.General.Snapping=\"Ajuste de alineación de la fuente\"\nBasic.Settings.General.ScreenSnapping=\"Ajustar las fuentes al borde de la pantalla\"\nBasic.Settings.General.CenterSnapping=\"Ajustar las fuentes al centro horizontal y vertical\"\nBasic.Settings.General.SourceSnapping=\"Ajustar las fuentes a otras fuentes\"\nBasic.Settings.General.SnapDistance=\"Ajustar la sensibilidad\"\nBasic.Settings.General.SpacingHelpers=\"Mostrar guías de alineación de píxeles\"\nBasic.Settings.General.RecordWhenStreaming=\"Grabar automáticamente cuando se transmite\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Mantener la grabación cuando se detiene la transmisión\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Iniciar el búfer de repetición automáticamente durante la transmisión\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Mantener activo el búfer de repetición cuando la transmisión se detenga\"\nBasic.Settings.General.SysTray=\"Bandeja del sistema\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimizar a la bandeja del sistema al iniciar\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Minimizar siempre en la bandeja del sistema en lugar de la barra de tareas\"\nBasic.Settings.General.SaveProjectors=\"Guardar los proyectores al salir\"\nBasic.Settings.General.CloseExistingProjectors=\"Limitar un proyector de pantalla completa por pantalla\"\nBasic.Settings.General.Preview=\"Previsualizar\"\nBasic.Settings.General.OverflowHidden=\"Ocultar desborde\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Desborde siempre visible\"\nBasic.Settings.General.OverflowSelectionHidden=\"Mostrar desborde incluso cuando la fuente sea invisible\"\nBasic.Settings.General.Importers=\"Importadores\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Buscar ubicaciones conocidas para colecciones de escenas al importar\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Transición a la escena cuando se hace doble clic\"\nBasic.Settings.General.StudioPortraitLayout=\"Habilitar la disposición horizontal/vertical\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Mostrar las etiquetas vista previa/programa\"\nBasic.Settings.General.Multiview=\"Vista Múltiple\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Clic para cambiar entre escenas\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Mostrar nombres de las escenas\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Dibujar áreas seguras (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Disposición de la vista múltiple\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontal, superior (8 Escenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontal, inferior (8 Escenas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertical, izquierda (8 Escenas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertical, derecha (8 Escenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontal, superior (18 Escenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontal, superior (24 Escenas)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Solo escenas (4 Escenas)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Solo escenas (9 Escenas)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Solo escenas (16 Escenas)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Solo escenas (25 Escenas)\"\nBasic.Settings.General.ChannelName.stable=\"Estable\"\nBasic.Settings.General.ChannelDescription.stable=\"Última versión estable\"\nBasic.Settings.General.ChannelName.beta=\"Betas / Candidatos a lanzamiento\"\nBasic.Settings.General.ChannelDescription.beta=\"Versiones pre-lanzamiento potencialmente inestables\"\nBasic.Settings.Appearance=\"Apariencia\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Estilo\"\nBasic.Settings.Appearance.General.NoVariant=\"No hay estilos disponibles\"\nBasic.Settings.Appearance.FontScale=\"Tamaño de la fuente\"\nBasic.Settings.Appearance.Density=\"Densidad\"\nBasic.Settings.Appearance.Classic=\"Clásico\"\nBasic.Settings.Appearance.Compact=\"Compacto\"\nBasic.Settings.Appearance.Comfortable=\"Confortable\"\nBasic.Settings.Appearance.OptionsWarning=\"Algunas opciones de apariencia no están disponibles para este estilo.\"\nBasic.Settings.Stream=\"Emisión\"\nBasic.Settings.Stream.Destination=\"Destino\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Usar la autentificación\"\nBasic.Settings.Stream.Custom.Username=\"Nombre de usuario\"\nBasic.Settings.Stream.Custom.Password=\"Contraseña\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: introduzca el srp_nombre de usuario.\\nRTMP: introduzca el nombre de usuario.\\nSRT: no utilizado.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: introduzca la srp_contraseña.\\nRTMP: introduzca la contraseña.\\nSRT: introduzca la contraseña del cifrado.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Activar modo de prueba de ancho de banda\"\nBasic.Settings.Stream.TTVAddon=\"Complementos del chat de Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Ninguno\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV y FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Falta configuración de stream\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Abrir Ajustes\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Falta la URL y clave de retransmisión.\\n\\nAbre los ajustes para introducir la URL y la clave de retransmisión en la pestaña 'Emisión'.\"\nBasic.Settings.Stream.MissingUrl=\"Falta la URL del stream.\\n\\nAbre los ajustes para introducir la URL en la pestaña 'Emisión'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Falta la clave de retransmisión.\\n\\nAbre los ajustes para introducir la clave de retransmisión en la pestaña 'Emisión'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorar las recomendaciones de configuración del servicio de streaming\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Anular los ajustes recomendados\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Advertencia: Ignorar las limitaciones del servicio puede provocar una degradación en la calidad del stream o impedir que se emita.\\n\\n¿Continuar?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Máxima tasa de bits de vídeo: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Máxima tasa de bits de audio: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Resolución máxima: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Tasa máxima de fotogramas: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Especifica un servidor personalizado...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Servidor personalizado\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Habilitar %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Ancho de banda máximo\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Máximas pistas de vídeo\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Habilitar el volcado de la transmisión a FLV (usa la configuración simple de grabación)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Anulación de la configuración (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Activar la anulación de la configuración\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Video multipista\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Lienzo adicional\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Transmisión simultánea\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"La transmisión simultánea permite codificar y enviar vídeo en distintas calidades. <a href='https://obsproject.com/kb/whip-streaming-guide'>Más información</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Capas totales\"\nBasic.Settings.Stream.AdvancedOptions=\"Opciones avanzadas\"\nBasic.Settings.Output=\"Salida\"\nBasic.Settings.Output.Format=\"Formato de grabación\"\nBasic.Settings.Output.Format.MKV=\"Video Matroska (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"MP4 híbrido (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV híbrido (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 fragmentado (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV fragmentado (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"El MOV fragmentado escribe la grabación en fragmentos y no requiere una finalización como los archivos MOV tradicionales.\\nEsto garantiza que el archivo se pueda reproducir incluso si se interrumpe la escritura en el disco, por ejemplo, como resultado de un pantallazo azul o una pérdida de energía.\\n\\nEsto puede no ser compatible con todos los reproductores y editores. Use Archivo → Convertir grabaciones para convertir el archivo a un formato más compatible si es necesario.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"El MP4 fragmentado escribe la grabación en fragmentos y no requiere una finalización como los archivos MP4 tradicionales.\\nEsto garantiza que el archivo se pueda reproducir incluso si se interrumpe la escritura en el disco, por ejemplo, como resultado de un pantallazo azul o una pérdida de energía.\\n\\nEsto puede no ser compatible con todos los reproductores y editores. Use Archivo → Convertir grabaciones para convertir el archivo a un formato más compatible si es necesario.\"\nBasic.Settings.Output.Encoder.Video=\"Codificador de vídeo\"\nBasic.Settings.Output.Encoder.Audio=\"Codificador de audio\"\nBasic.Settings.Output.SelectDirectory=\"Seleccione Directorio de grabación\"\nBasic.Settings.Output.DynamicBitrate=\"Cambia dinámicamente la tasa de bits para gestionar la congestión\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Cambia dinámicamente la tasa de bits para gestionar la congestión (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"En lugar de saltar fotogramas para reducir la congestión, cambia dinámicamente el bitrate sobre la marcha.\\n\\nTen en cuenta que esto puede aumentar el retardo de la transmisión si hay una congestión repentina significativa.\\nCuando el bitrate baja, puede tardar unos minutos en restaurarse.\\n\\nActualmente sólo es compatible con RTMP.\"\nBasic.Settings.Output.Mode=\"Modo de salida\"\nBasic.Settings.Output.Mode.Simple=\"Sencillo\"\nBasic.Settings.Output.Mode.Adv=\"Avanzado\"\nBasic.Settings.Output.Mode.FFmpeg=\"Salida de FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Activar el búfer de repetición\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Tiempo máximo de la repetición\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Memoria máxima\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Uso estimado de memoria: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Advertencia: El uso estimado de memoria de %1 MiB es mayor al máximo recomendado de %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"No se puede estimar el uso de memoria. Establezca el límite máximo de memoria.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefijo del nombre de archivo del búfer\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufijo\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"El búfer de repetición no se puede utilizar cuando el tipo de grabación está establecida como 'Salida personalizada (FFmpeg)'.\"\nBasic.Settings.Output.Simple.SavePath=\"Ruta de grabación\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Calidad de grabación\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Igual a la emisión\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Alta calidad, tamaño de archivo medio\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Tamaño de archivo grande, calidad indistinguible\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Tamaño del archivo sin pérdida de calidad, tremendamente grande\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Advertencia: La tasa de bits de vídeo del stream se establecerá en %1, que es el límite superior para el servicio de streaming actual.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Advertencia: La tasa de bits de audio del stream se establecerá en %1, que es el límite superior para el servicio de streaming actual.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Advertencia: Las grabaciones no se pueden pausar si la calidad de grabación se establece en \\\"Igual a la emisión\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Advertencia: El formato de grabación seleccionado actualmente no es compatible con el codificador o codificadores de transmisión seleccionados.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"ADVERTENCIA: Grabar con un codificador de software de una calidad diferente a la de la transmisión requerirá un uso adicional de la CPU si transmite y graba al mismo tiempo.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Advertencia: ¡La calidad sin perdidas genera archivos exageradamente grandes! Puede utilizar más de 7 gigabytes de espacio en disco por minuto en altas resoluciones y tasas de fotogramas. La calidad sin pérdidas no se recomienda para grabaciones largas a menos que tengas una gran cantidad de espacio disponible en tu disco. El búfer de repetición no está disponible cuando se utiliza la calidad sin pérdidas.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"¿Confirma que desea utilizar calidad sin perdidas?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"¡Atención de calidad sin pérdidas!\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264 bajo uso de CPU, aumenta el tamaño de archivo)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Predeterminado)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Pista VOD de Twitch (utiliza la pista 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Pista de audio\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Resolución o tasa de fotogramas incompatible\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Este servicio de streaming no admite su resolución y/o tasa de fotogramas actual. Se cambiarán a los valores compatibles más cercanos\\n\\n%1\\n\\n¿Desea continuar?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolución: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Tasa de fotogramas: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Codificador incompatible\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"El servicio de streaming \\\"%1\\\" no soporta el codificador \\\"%2\\\". El codificador se cambiará a \\\"%3\\\".\\n\\n¿Desea continuar?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"El servicio de transmisión \\\"%1\\\" no soporta los codificadores \\\"%2\\\" y \\\"%3\\\". Estos codificadores se cambiarán a \\\"%4\\\" y \\\"%5\\\".\\n\\n¿Quieres continuar?\"\nBasic.Settings.Output.VideoBitrate=\"Bitrate de vídeo\"\nBasic.Settings.Output.AudioBitrate=\"Bitrate de audio\"\nBasic.Settings.Output.Reconnect=\"Reconectar automáticamente\"\nBasic.Settings.Output.RetryDelay=\"Retraso para reintentar\"\nBasic.Settings.Output.MaxRetries=\"Reintentos máximos\"\nBasic.Settings.Output.Advanced=\"Activar configuración de codificador personalizada (avanzado)\"\nBasic.Settings.Output.EncoderPreset=\"Preajuste del codificador\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (uso de CPU bajo, menor calidad)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (predeterminado) (uso de CPU medio, calidad estándar)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (uso de CPU alto, calidad alta)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Configuración del codificador personalizada\"\nBasic.Settings.Output.CustomMuxerSettings=\"Configuración personalizada de Muxer\"\nBasic.Settings.Output.NoSpaceFileName=\"Generar el nombre del archivo sin espacio\"\nBasic.Settings.Output.Adv.Rescale=\"Cambiar la escala de salida\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Deshabilitado\"\nBasic.Settings.Output.Adv.AudioTrack=\"Pista de audio\"\nBasic.Settings.Output.Adv.Streaming=\"Emisión\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Ajustes de emisión\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Pista 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Pista 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Pista 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Pista 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Pista 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Pista 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Pista VOD de Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"Ajustes de codificación\"\nBasic.Settings.Output.Adv.Recording=\"Grabación\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Ajustes de grabación\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tipo de grabación\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tipo\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Estándar\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Salida personalizada (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Usar codificación de trasmisión)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Nombre de archivo de formato\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Sobrescribir si el archivo existe\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"La salida personalizada (FFmpeg) se proporciona sin garantías.\\nConsidere utilizar el tipo de grabación \\\"Estándar\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Tipo de salida de FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Salida a URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Salida a archivo\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Formatos comunes de grabación\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Todos los Archivos\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Ruta de archivo o URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Formato del contenedor\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Formato predeterminado\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Descripción del formato contenedor\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Codec de audio/vídeo supuesto a partir de la URL o ruta de archivo\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Codificador por defecto\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Desactivar el codificador\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Codificador de vídeo\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Configuración de codificador de vídeo (si existe)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Codificador de audio\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Configuración de codificador de audio (si existe)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Ajustes de Muxer (en caso de que haya)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Intervalo de fotogramas clave (en Fotogramas)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Mostrar todos los codecs (aunque sean potencialmente incompatibles)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Ajustes FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Dividir archivo automáticamente\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Dividir por tiempo\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Dividir por tamaño\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Solo dividir manualmente\"\nBasic.Settings.Output.SplitFile.Time=\"Tiempo de división\"\nBasic.Settings.Output.SplitFile.Size=\"Tamaño de división\"\nScreenshot=\"Salida de captura de pantalla\"\nScreenshot.SourceHotkey=\"Captura de pantalla de fuente seleccionada\"\nScreenshot.StudioProgram=\"Captura de pantalla (Programa)\"\nScreenshot.Preview=\"Captura de pantalla (Vista previa)\"\nScreenshot.Scene=\"Captura de pantalla (Escena)\"\nScreenshot.Source=\"Captura de pantalla (Fuente)\"\nFilenameFormatting.TT.CCYY=\"Año, cuatro dígitos\"\nFilenameFormatting.TT.YY=\"Año, dos últimos dígitos (00-99)\"\nFilenameFormatting.TT.MM=\"Mes como número decimal (01-12)\"\nFilenameFormatting.TT.DD=\"Día del mes, precedido de cero (01-31)\"\nFilenameFormatting.TT.hh=\"Hora en formato 24h (00-23)\"\nFilenameFormatting.TT.mm=\"Minuto (00-59)\"\nFilenameFormatting.TT.ss=\"Segundo (00-59)\"\nFilenameFormatting.TT.Percent=\"Un signo %\"\nFilenameFormatting.TT.a=\"Nombre abreviado del día de la semana\"\nFilenameFormatting.TT.A=\"Nombre del día de la semana completo\"\nFilenameFormatting.TT.b=\"Nombre del mes abreviado\"\nFilenameFormatting.TT.B=\"Nombre del mes completo\"\nFilenameFormatting.TT.d=\"Día del mes, precedido de cero (01-31)\"\nFilenameFormatting.TT.H=\"Hora en formato 24h (00-23)\"\nFilenameFormatting.TT.I=\"Hora en formato 12h (01-12)\"\nFilenameFormatting.TT.m=\"Mes como número decimal (01-12)\"\nFilenameFormatting.TT.M=\"Minuto (00-59)\"\nFilenameFormatting.TT.p=\"Designación AM o PM\"\nFilenameFormatting.TT.s=\"Tiempo en segundos desde época UNIX\"\nFilenameFormatting.TT.S=\"Segundo (00-59)\"\nFilenameFormatting.TT.y=\"Año, dos últimos dígitos (00-99)\"\nFilenameFormatting.TT.Y=\"Año\"\nFilenameFormatting.TT.z=\"Desfase ISO 8601 desde UTC en el huso horario\"\nFilenameFormatting.TT.Z=\"Nombre o abreviatura del huso horario\"\nFilenameFormatting.TT.FPS=\"Fotogramas por segundo\"\nFilenameFormatting.TT.CRES=\"Resolución base (lienzo)\"\nFilenameFormatting.TT.ORES=\"Resolución de salida (escalada)\"\nFilenameFormatting.TT.VF=\"Formato de vídeo\"\nBasic.Settings.Video=\"Vídeo\"\nBasic.Settings.Video.Adapter=\"Adaptador de vídeo\"\nBasic.Settings.Video.BaseResolution=\"Resolución de la base (Lienzo)\"\nBasic.Settings.Video.ScaledResolution=\"Resolución de salida (Escalada)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtro de escala\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Las resoluciones coinciden, no se requiere reducción de escala]\"\nBasic.Settings.Video.FPSCommon=\"Valores comunes de FPS\"\nBasic.Settings.Video.FPSInteger=\"Valor entero de FPS\"\nBasic.Settings.Video.FPSFraction=\"Valor fraccional de FPS\"\nBasic.Settings.Video.Numerator=\"Numerador\"\nBasic.Settings.Video.Denominator=\"Denominador\"\nBasic.Settings.Video.Renderer=\"Renderizador\"\nBasic.Settings.Video.InvalidResolution=\"Valor de la resolución no válido. Debe ser [ancho] x [altura] (por ejemplo, 1920 x 1080)\"\nBasic.Settings.Video.CurrentlyActive=\"La salida de vídeo está actualmente activa. Por favor apague cualquier salida para cambiar la configuración de vídeo.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineal (más rápido, pero borroso si se escala la imagen)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicúbico (Escalado fino, 16 muestras)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Escalado fino, 36 muestras)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Área (Suma ponderada, 4/6/9 muestras)\"\nBasic.Settings.Audio.SampleRate=\"Frecuencia de muestreo\"\nBasic.Settings.Audio.Channels=\"Canales\"\nBasic.Settings.Audio.Meters=\"Medidores\"\nBasic.Settings.Audio.MeterDecayRate=\"Velocidad de decaimiento\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rápida\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Media (PPM de tipo I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lenta (PPM de tipo II)\"\nBasic.Settings.Audio.PeakMeterType=\"Tipo de medidor de pico\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Pico de muestra\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True Peak (mayor uso de CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ADVERTENCIA: El audio de sonido envolvente está habilitado.\"\nBasic.Settings.Audio.MultichannelWarning=\"Si estás transmitiendo, comprueba si tu servicio de transmisión soporta tanto la ingesta como la reproducción de sonido envolvente. Por ejemplo, Facebook 360 Live soporta completamente el sonido envolvente; YouTube Live soporta la ingesta de audio 5.1 (y la reproducción en TV).\\n\\nLos filtros de audio de OBS son compatibles con el sonido envolvente, aunque no se garantiza la compatibilidad con plugins VST.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"¿Habilitar el audio de sonido envolvente?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"¿Seguro que quiere habilitar el audio de sonido envolvente?\"\nBasic.Settings.Audio.Devices=\"Dispositivos de audio globales\"\nBasic.Settings.Audio.DesktopDevice=\"Audio del escritorio\"\nBasic.Settings.Audio.DesktopDevice2=\"Audio del escritorio 2\"\nBasic.Settings.Audio.AuxDevice=\"Dispositivo de audio Mic/auxiliar\"\nBasic.Settings.Audio.AuxDevice2=\"Dispositivo de audio Mic/auxiliar 2\"\nBasic.Settings.Audio.AuxDevice3=\"Dispositivo de audio Mic/auxiliar 3\"\nBasic.Settings.Audio.AuxDevice4=\"Dispositivo de audio Mic/auxiliar 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Habilitar Pulsar para Silenciar\"\nBasic.Settings.Audio.PushToMuteDelay=\"Retardo de Presionar para Silenciar\"\nBasic.Settings.Audio.EnablePushToTalk=\"Habilitar Pulsar para Hablar\"\nBasic.Settings.Audio.PushToTalkDelay=\"Retardo de Pulsar para Hablar\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Dispositivo no conectado o no está disponible]\"\nBasic.Settings.Audio.Disabled=\"Deshabilitado\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Modo de búfer de audio de baja latencia (para salidas Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ATENCIÓN: El búfer de audio de baja latencia está activado.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"El modo de búfer de audio de baja latencia puede causar errores en el audio o dejar de reproducirlo desde algunas fuentes.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"¿Habilitar el modo de búfer de audio de baja latencia?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"¿Estás seguro de que deseas activar el modo de búfer de audio de baja latencia?\"\nBasic.Settings.Accessibility=\"Accesibilidad\"\nBasic.Settings.Accessibility.ColorOverrides=\"Usar otros colores\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Borde de la fuente (selección)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Borde de la fuente (recorte)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Borde de la fuente (al pasar por encima)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Banda del volumen del mezclador (-60 a -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Banda del volumen del mezclador (-20 a -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Banda del volumen del mezclador (-9 a 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Banda del volumen del mezclador (-60 a -20dB) (activo)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Banda del volumen del mezclador (-20 a -9dB) (activo)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Banda del volumen del mezclador (-9 a 0dB) (activo)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Preajuste de color\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Por defecto\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Personalizado\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Color alternativo para daltónicos\"\nBasic.Settings.Advanced=\"Avanzado\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Mostrar advertencia de salidas activas al salir\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prioridad del proceso\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Alta\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Mayor a Normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Por debajo de lo normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inactiva\"\nBasic.Settings.Advanced.FormatWarning=\"Advertencia: Los formatos de color que no sean NV12/P010 están destinados principalmente a la grabación, y no se recomiendan cuando se emite. Emitir puede incurrir en un mayor uso de la CPU debido a la conversión del formato de color.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Advertencia: Los formatos de alta precisión se utilizan más comúnmente con espacios de color HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Advertencia: Rec. 2100 debería usar un formato con más precisión.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Formato de color\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 bit, 4:2:0, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 bit, 4:4:4, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 bit, 4:2:0, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 bit, 4:2:0, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Espacio de color\"\nBasic.Settings.Advanced.Video.ColorRange=\"Gama de colores\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limitado\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Completo\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Nivel de blanco SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nivel de pico nominal HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Dispositivo de monitorización de audio\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Por defecto\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Desactivar reducción de audio de Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Retardo de la transmisión\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Duración\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Preservar el punto de corte (aumento de retardo) al volver a conectar\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Uso estimado de memoria: %1 MB\"\nBasic.Settings.Advanced.Network=\"Red\"\nBasic.Settings.Advanced.Network.Disabled=\"El protocolo de transmisión actualmente seleccionado no permite cambiar la configuración de red.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Enlazar con IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Familia IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Habilitar optimizaciones de red\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Habilitar el control de ritmo TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Intenta hacer que la salida RTMP sea más amigable con otras aplicaciones sensibles a la latencia en la red regulando la tasa de transmisión.\\nPuede aumentar el riesgo de pérdida de fotogramas en conexiones inestables.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Comportamiento de las teclas de acceso rápido\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nunca desactivar las teclas de acceso rápido\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Deshabilitar teclas de acceso rápido cuando la ventana principal se encuentre activa\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Deshabilitar teclas de acceso rápido cuando la ventana principal no esté en primer plano\"\nBasic.Settings.Advanced.AutoRemux=\"Convertir automáticamente a %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(grabar como mkv)\"\nBasic.AdvAudio=\"Propiedades de Audio avanzadas\"\nBasic.AdvAudio.ActiveOnly=\"Sólo fuentes activas\"\nBasic.AdvAudio.Name=\"Nombre\"\nBasic.AdvAudio.Volume=\"Volumen\"\nBasic.AdvAudio.VolumeSource=\"Volumen para '%1'\"\nBasic.AdvAudio.MonoSource=\"Mezcla monoaural para '%1'\"\nBasic.AdvAudio.BalanceSource=\"Balance para '%1'\"\nBasic.AdvAudio.SyncOffset=\"Intervalo de sincronización\"\nBasic.AdvAudio.SyncOffsetSource=\"Compensación de sincronismo para '%1'\"\nBasic.AdvAudio.Monitoring=\"Monitorización de audio\"\nBasic.AdvAudio.Monitoring.None=\"Monitorización desactivada\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Solo monitorización (salida silenciada)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitorización y salida\"\nBasic.AdvAudio.MonitoringSource=\"Monitorización de audio para '%1'\"\nBasic.AdvAudio.AudioTracks=\"Pistas\"\nBasic.Settings.Hotkeys=\"Atajos\"\nBasic.Settings.Hotkeys.Pair=\"Combinaciones de teclas con '%1' actúan como interruptores\"\nBasic.Settings.Hotkeys.Filter=\"Filtro\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrar por tecla de acceso rápido\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Esta tecla de acceso rápido es compartida por una o más acciones, haga clic para mostrar conflictos\"\nBasic.Settings.Hotkeys.PleaseWait=\"Cargando teclas de acceso rápido, por favor espere...\"\nBasic.Hotkeys.SelectScene=\"Cambiar a la escena\"\nBasic.SystemTray.Show=\"Mostrar\"\nBasic.SystemTray.Hide=\"Ocultar\"\nBasic.SystemTray.Message.Reconnecting=\"Desconectado. Volviendo a conectar...\"\nHotkeys.Insert=\"Insertar\"\nHotkeys.Delete=\"Eliminar\"\nHotkeys.Home=\"Casa\"\nHotkeys.End=\"Final\"\nHotkeys.PageUp=\"AvPag\"\nHotkeys.PageDown=\"RePag\"\nHotkeys.NumLock=\"Bloq Num\"\nHotkeys.ScrollLock=\"Bloq. Scroll\"\nHotkeys.CapsLock=\"Bloq mayus\"\nHotkeys.Backspace=\"Retroceso\"\nHotkeys.Tab=\"Tabulador\"\nHotkeys.Print=\"Imprimir\"\nHotkeys.Pause=\"Pausa\"\nHotkeys.Left=\"Izquierda\"\nHotkeys.Right=\"Derecha\"\nHotkeys.Up=\"Subir\"\nHotkeys.Down=\"Bajar\"\nHotkeys.Windows=\"Tecla de Windows\"\nHotkeys.Space=\"Espacio\"\nHotkeys.NumpadNum=\"%1 (teclado numérico)\"\nHotkeys.NumpadMultiply=\"Multiplicar (teclado numérico)\"\nHotkeys.NumpadDivide=\"Dividir (teclado numérico)\"\nHotkeys.NumpadAdd=\"Sumar (Teclado numérico)\"\nHotkeys.NumpadSubtract=\"Restar (teclado numérico)\"\nHotkeys.NumpadDecimal=\"Decimal (teclado numérico)\"\nHotkeys.AppleKeypadNum=\"%1 (teclado)\"\nHotkeys.AppleKeypadMultiply=\"* (Teclado)\"\nHotkeys.AppleKeypadDivide=\"/ (Teclado)\"\nHotkeys.AppleKeypadAdd=\"+ (Teclado)\"\nHotkeys.AppleKeypadSubtract=\"- (Teclado)\"\nHotkeys.AppleKeypadDecimal=\". (Teclado)\"\nHotkeys.AppleKeypadEqual=\"= (Teclado)\"\nHotkeys.MouseButton=\"Ratón %1\"\nMute=\"Silenciar\"\nUnmute=\"Quitar silencio\"\nPush-to-mute=\"Pulsar para Silenciar\"\nPush-to-talk=\"Pulsar para Hablar\"\nSceneItemShow=\"Mostrar '%1'\"\nSceneItemHide=\"Ocultar '%1'\"\nOutputWarnings.NoTracksSelected=\"Debe seleccionar al menos una pista\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Error de configuración de salida\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Todas las salidas han de tener al menos una pista de audio seleccionada.\"\nOutputWarnings.MP4Recording=\"Advertencia: Las grabaciones guardadas en MP4/MOV serán irrecuperables si el archivo no puede finalizarse (e.g. como resultado de BSODs, cortes eléctricos, etc.). Si quieres grabar múltiples pistas de audio considera usar MKV y convierte la grabación a MP4/MOV después de finalizar (Archivo → Convertir grabaciones)\"\nOutputWarnings.CannotPause=\"Advertencia: Las grabaciones no se pueden pausar si el codificador de grabación se establece en \\\"(Usar codificador de transmisión)\\\"\"\nOutputWarnings.CodecIncompatible=\"La selección de codificador de audio o video se restableció debido a una incompatibilidad. Seleccione un codificador compatible de la lista.\"\nCodecCompat.Incompatible=\"(Incompatible con %1)\"\nCodecCompat.CodecPlaceholder=\"Selecciona codificador...\"\nCodecCompat.ContainerPlaceholder=\"Selecciona formato...\"\nCodecCompat.CodecMissingOnExit.Title=\"Ningún codificador seleccionado\"\nCodecCompat.CodecMissingOnExit.Text=\"No has elegido un codificador de vídeo o audio. Por favor asegúrate de seleccionar codificadores tanto para la grabación como para la transmisión.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Ningún formato seleccionado\"\nCodecCompat.ContainerMissingOnExit.Text=\"No se ha seleccionado ningún formato de grabación. Por favor selecciona un formato de grabación que sea compatible con el codificador de transmisión seleccionado.\"\nFinalScene.Title=\"Eliminar escena\"\nFinalScene.Text=\"Debe haber al menos una escena.\"\nNoSources.Title=\"Sin recursos\"\nNoSources.Text=\"Parece que no has añadido ningún recurso de vídeo aún, así que estarás emitiendo una pantalla en blanco. ¿Estás seguro de que quieres hacer esto?\"\nNoSources.Text.AddSource=\"Puedes añadir recursos haciendo clic en el icono de \\\"+\\\" debajo del menú Recursos en la ventana principal en cualquier momento.\"\nNoSources.Label=\"No tienes ninguna fuente.\\nHaz clic en el botón +,\\no haz clic derecho aquí para añadir una.\"\nChangeBG=\"Establecer color\"\nCustomColor=\"Color personalizado\"\nBrowserSource.EnableHardwareAcceleration=\"Habilitar aceleración por hardware en el navegador\"\nAbout=\"Acerca de\"\nAbout.Info=\"OBS Studio es un software libre y gratuito de grabación de vídeo y transmisión en directo.\"\nAbout.Donate=\"Haz una contribución\"\nAbout.GetInvolved=\"Involúcrate\"\nAbout.Authors=\"Autores\"\nAbout.License=\"Licencia\"\nAbout.Error=\"¡Error! No se ha podido leer el archivo.\\n\\nVe a: %1\"\nAbout.Contribute=\"Apoya el proyecto OBS\"\nAddUrl.Title=\"Añadir fuente vía URL\"\nAddUrl.Text=\"Has arrastrado una URL en OBS. Esto añadirá automáticamente el enlace como fuente. ¿Continuar?\"\nResizeOutputSizeOfSource=\"Redimensionar salida (tamaño de la fuente)\"\nResizeOutputSizeOfSource.Text=\"La resolución base y de salida se redimensionarán al tamaño de la fuente actual.\"\nResizeOutputSizeOfSource.Continue=\"¿Quieres continuar?\"\nPreviewTransition=\"Vista previa de la Transición\"\nImporter=\"Importador de Colección de Escenas\"\nImporter.SelectCollection=\"Selecciona una colección de escenas\"\nImporter.Collection=\"Colección de escenas\"\nImporter.HelpText=\"Añadir archivos a esta ventana para importar colecciones de OBS u otros programas compatibles.\"\nImporter.Path=\"Ruta de la colección\"\nImporter.Program=\"Aplicación detectada\"\nImporter.AutomaticCollectionPrompt=\"Buscar automáticamente colecciones de escenas\"\nImporter.AutomaticCollectionText=\"OBS puede encontrar automáticamente colecciones de escenas importables de programas de terceros soportados. ¿Quieres que OBS encuentre automáticamente colecciones de escenas?\\n\\nPuedes cambiar esto más tarde en Ajustes > General > Importadores.\"\nImporter.SelectFile=\"Navegar...\"\nRestart=\"Reiniciar\"\nNeedsRestart=\"OBS Studio necesita ser reiniciado. ¿Desea reiniciar ahora?\"\nLoadProfileNeedsRestart=\"El perfil contiene ajustes que requieren reiniciar OBS:\\n%1\\n\\n¿Desea reiniciar OBS para que estos ajustes surtan efecto?\"\nContextBar.NoSelectedSource=\"Ninguna fuente seleccionada\"\nContextBar.MediaControls.PlayMedia=\"Reproducir Medio\"\nContextBar.MediaControls.PauseMedia=\"Pausar Medio\"\nContextBar.MediaControls.StopMedia=\"Detener Medio\"\nContextBar.MediaControls.RestartMedia=\"Reiniciar Medio\"\nContextBar.MediaControls.PlaylistNext=\"Siguiente en la lista de reproducción\"\nContextBar.MediaControls.PlaylistPrevious=\"Anterior en la lista de reproducción\"\nContextBar.MediaControls.BlindSeek=\"Widget Barra de reproducción\"\nYouTube.Auth.Ok=\"Se completó la autorización correctamente.\\nAhora puede cerrar esta página.\"\nYouTube.Auth.NoCode=\"No se completó el proceso de autorización.\"\nYouTube.Auth.NoChannels=\"No hay canal(es) disponible(s) en la cuenta seleccionada\"\nYouTube.Auth.WaitingAuth.Title=\"Autorización de usuario de YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Por favor, complete la autorización en su navegador externo.<br>Si el navegador externo no se abre, siga este enlace y complete la autorización:<br>%1\"\nYouTube.AuthError.Text=\"No se pudo obtener información del canal: %1.\"\nYouTube.Actions.WindowTitle=\"Configuración de emisión de YouTube - Canal: %1\"\nYouTube.Actions.CreateNewEvent=\"Crear nueva emisión\"\nYouTube.Actions.ChooseEvent=\"Seleccionar emisión existente\"\nYouTube.Actions.Title=\"Título*\"\nYouTube.Actions.MyBroadcast=\"Mi transmisión\"\nYouTube.Actions.Description=\"Descripción\"\nYouTube.Actions.Privacy=\"Privacidad*\"\nYouTube.Actions.Privacy.Private=\"Privado\"\nYouTube.Actions.Privacy.Public=\"Público\"\nYouTube.Actions.Privacy.Unlisted=\"Oculto\"\nYouTube.Actions.Category=\"Categoría\"\nYouTube.Actions.Thumbnail=\"Miniatura\"\nYouTube.Actions.Thumbnail.SelectFile=\"Seleccionar archivo...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Ningún archivo seleccionado\"\nYouTube.Actions.Thumbnail.ClearFile=\"Borrar\"\nYouTube.Actions.MadeForKids=\"¿El vídeo se ha creado para niños?*\"\nYouTube.Actions.MadeForKids.Yes=\"Sí, está hecho para niños\"\nYouTube.Actions.MadeForKids.No=\"No, no está hecho para niños\"\nYouTube.Actions.AdditionalSettings=\"Ajustes adicionales\"\nYouTube.Actions.Latency=\"Latencia\"\nYouTube.Actions.Latency.Low=\"Baja\"\nYouTube.Actions.Latency.UltraLow=\"Ultra baja\"\nYouTube.Actions.EnableAutoStart=\"Habilitar el inicio automático\"\nYouTube.Actions.EnableAutoStop=\"Habilitar detención automática\"\nYouTube.Actions.AutoStartStop.TT=\"Indica si esta emisión programada debe iniciarse automáticamente\"\nYouTube.Actions.EnableDVR=\"Habilitar DVR\"\nYouTube.Actions.360Video=\"Vídeo 360\"\nYouTube.Actions.ScheduleForLater=\"Programar para más tarde\"\nYouTube.Actions.RememberSettings=\"Recordar estos ajustes\"\nYouTube.Actions.Create_Ready=\"Crear emisión\"\nYouTube.Actions.Create_GoLive=\"Crear emisión y comenzar a transmitir\"\nYouTube.Actions.Choose_Ready=\"Seleccionar emisión\"\nYouTube.Actions.Choose_GoLive=\"Seleccionar emisión y comenzar a transmitir\"\nYouTube.Actions.Create_Schedule=\"Programar emisión\"\nYouTube.Actions.Create_Schedule_Ready=\"Programar y seleccionar emisión\"\nYouTube.Actions.Dashboard=\"Abrir YouTube Studio\"\nYouTube.Actions.Error.Title=\"Error al crear la transmisión\"\nYouTube.Actions.Error.Text=\"Error de acceso a YouTube '%1'.<br/>La descripción detallada del error se puede encontrar en <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Error de acceso a YouTube. Compruebe la conexión de red o el acceso al servidor de YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Error al crear la transmisión '%1'.<br/>La descripción detallada del error se puede encontrar en <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"No se creó ninguna transmisión. Vuelva a vincular la cuenta.\"\nYouTube.Actions.Error.YouTubeApi=\"Error en la API de YouTube. Para más información, consulte el archivo de registro.\"\nYouTube.Actions.Error.BroadcastNotFound=\"No se encontró la emisión seleccionada.\"\nYouTube.Actions.Error.FileMissing=\"El archivo seleccionado no existe.\"\nYouTube.Actions.Error.FileOpeningFailed=\"No se pudo abrir el archivo seleccionado.\"\nYouTube.Actions.Error.FileTooLarge=\"El archivo seleccionado es demasiado grande (Límite: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Ha fallado la transmisión: %1<br/><br/>Si este error persiste <a href='https://studio.youtube.com/video/%2/livestreaming'>abra la emisión en YouTube Studio</a> e intente manualmente.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"La transmisión está transicionando a la fase de prueba, esto puede tardar algún tiempo. Por favor, inténtalo de nuevo en 10-30 segundos.\"\nYouTube.Actions.EventsLoading=\"Cargando lista de eventos...\"\nYouTube.Actions.EventCreated.Title=\"Evento Creado\"\nYouTube.Actions.EventCreated.Text=\"Evento creado con éxito.\"\nYouTube.Actions.Stream=\"Transmitir\"\nYouTube.Actions.Stream.ScheduledFor=\"Programado para %1\"\nYouTube.Actions.Stream.Resume=\"Reanudar la transmisión interrumpida\"\nYouTube.Actions.Stream.YTStudio=\"Creado automáticamente por YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Creando una nueva transmisión en vivo, por favor espere...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Requiere inicio manual\"\nYouTube.Actions.AutoStartStreamingWarning=\"El inicio automático está deshabilitado para este evento, haga clic en \\\"Emitir en directo\\\" para iniciar su transmisión.\"\nYouTube.Actions.AutoStopStreamingWarning=\"No podrás reconectar.<br>Tu transmisión se detendrá y ya no estarás en vivo.\"\nYouTube.Errors.liveStreamingNotEnabled=\"El trasmisión en vivo no está habilitada en el canal de YouTube seleccionado.<br/><br/>Vea <a href='https://www.youtube.com/features'>youtube.com/features</a> para más información.\"\nYouTube.Errors.livePermissionBlocked=\"La transmisión en vivo no está disponible en el canal de YouTube seleccionado.<br/>Tenga en cuenta que puede tardar hasta 24 horas en estar disponible la transmisión en vivo después de habilitarla en la configuración de su canal.<br/><br/>Ver <a href='https://www.youtube.com/features'>youtube.com/features</a> para más detalles.\"\nYouTube.Errors.errorExecutingTransition=\"La transición ha fallado debido a un error del backend. Por favor, inténtalo de nuevo en unos segundos.\"\nYouTube.Errors.errorStreamInactive=\"YouTube no está recibiendo datos de tu transmisión. Por favor, comprueba tu configuración e inténtalo de nuevo.\"\nYouTube.Errors.invalidTransition=\"El intento de transición no fue válido. Esto puede deberse a que la transmisión no ha finalizado una transición anterior. Por favor, espere unos segundos y vuelva a intentarlo.\"\nYouTube.DocksRemoval.Title=\"Borrar los paneles de navegador heredados de YouTube\"\nYouTube.DocksRemoval.Text=\"Estos paneles de navegador se eliminarán como obsoletos:\\n\\n%1\\nUtiliza \\\"Paneles/YouTube Live Control Room\\\" en su lugar.\"\nConfigDownload.WarningMessageTitle=\"Advertencia\"\nFailedToStartStream.MissingConfigURL=\"No hay una URL de configuración disponible para el servicio actual\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"URL RTMP personalizada no especificada\"\nFailedToStartStream.InvalidCustomConfig=\"Configuración personalizada no valida\"\nFailedToStartStream.MissingCanvas=\"No se encuentra un lienzo adicional configurado\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Fallo al crear el servicio de vídeo multipista\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Error al crear la salida RTMP de vídeo multipista\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC no disponible.\\n\\n No se puedo encontrar el tipo de codificador '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"No se pudo crear el codificador de vídeo '%1' (tipo: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"No se pudo obtener la información de vídeo de OBS mientras se crea el codificador '%1' (tipo: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"No se puedo crear el codificador de audio\"\nFailedToStartStream.NoRTMPURLInConfig=\"La configuración no contiene la URL RTMP(S) de destino de la transmisión\"\nFailedToStartStream.FallbackToDefault=\"No se puedo iniciar la transmisión usando %1; ¿quieres reintentarlo usando la configuración de codificación simple?\"\nFailedToStartStream.ConfigRequestFailed=\"No se pudo obtener la configuración de %1<br><br>HTTP error: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Recibido valor de estado desconocido '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\n¿Quieres continuar con la transmisión sin %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\n¿Quieres continuar con la transmisión?\"\nFailedToStartStream.MissingEncoderConfigs=\"La configuración del directo no incluía las configuraciones del codificador\"\nFailedToStartStream.InvalidEncoderConfig=\"La configuración del directo incluía una configuración de codificador no válida\"\nFailedToStartStream.StatusMissingHTML=\"La solicitud del directo devolvió un error no especificado\"\nFailedToStartStream.NoConfigSupplied=\"Configuración no encontrada\"\nMultitrackVideo.Info=\" %1 optimiza automáticamente tu configuración para codificar y enviar múltiples calidades de vídeo. Al seleccionar esta opción se enviará a %2 información sobre la configuración de su ordenador y software.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Ajustes incompatibles\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 no es compatible actualmente con:\\n\\n%2\\nPara continuar la transmisión con %1, desactiva los ajustes incompatibles:\\n\\n%3\\ny empieza a transmitir de nuevo.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Deshabilitar para esta transmisión e iniciar transmisión\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Actualizar ajustes e iniciar transmisión\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 actualmente no es compatible con [Audio → General → Canales] establecidos a '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Audio → General → Canales] necesita establecerse a '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 requiere múltiples parámetros diferentes para [Audio → General → Canales]\"\nBasic.OpenPluginManager=\"Gestor de plugins\"\nPluginManager=\"Gestor de plugins\"\nPluginManager.Restart=\"OBS Studio debe reiniciarse para aplicar los cambios del plugin\"\nPluginManager.MissingPlugin=\"[PLUGIN NO ENCONTRADO]\"\nPluginManager.Section.Discover=\"Examinar\"\nPluginManager.Section.Manage=\"Instalado\"\nPluginManager.Section.Updates=\"Actualizaciones\"\nPluginManager.Section.Manage.Title=\"Administrar plugins habilitados\"\n"
  },
  {
    "path": "frontend/data/locale/et-EE.ini",
    "content": "Language=\"Eesti\"\nOK=\"Sobib\"\nApply=\"Rakenda\"\nCancel=\"Loobu\"\nClose=\"Sulge\"\nSave=\"Salvesta\"\nDiscard=\"Loobu muudatustest\"\nDisable=\"Lülita välja\"\nYes=\"Jah\"\nNo=\"Ei\"\nAdd=\"Lisa\"\nRemove=\"Eemalda\"\nRename=\"Nimeta ümber\"\nInteract=\"Kasuta\"\nFilters=\"Filtrid\"\nProperties=\"Omadused\"\nMoveUp=\"Liiguta üles\"\nMoveDown=\"Liiguta alla\"\nSettings=\"Sätted\"\nDisplay=\"Kuva\"\nName=\"Nimi\"\nExit=\"Välju\"\nMixer=\"Helimikser\"\nBrowse=\"Sirvi\"\nDroppedFrames=\"Vahele jäetud kaadreid %1 (%2%)\"\nProjector.Open.Program=\"Avatud programmi projektor\"\nProjector.Open.Preview=\"Ava eelvaateprojektor\"\nProjector.Open.Scene=\"Avatud stseeni projektor\"\nProjector.Open.Source=\"Avatud lähtekoodiga projektor\"\nProjector.Open.Multiview=\"Ava mitmikvaade\"\nProjector.Display=\"Ekraan: %1\"\nProjector.Window=\"Uus aken\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Stseen: %1\"\nProjector.Title.Source=\"Allikas: %1\"\nProjector.Title.Multiview=\"Mitmikvaade\"\nProjector.ResizeWindowToContent=\"Mahuta aken sisule\"\nClear=\"Eemalda\"\nRevert=\"Tühista\"\nShow=\"Näita\"\nHide=\"Peida\"\nUnhideAll=\"Peida kõik\"\nUntitled=\"Nimetu\"\nNew=\"Uus\"\nDuplicate=\"Tee koopia\"\nEnable=\"Sisse lülitatud\"\nDisableOSXVSync=\"Lülita macOS V-Sync välja\"\nResetOSXVSyncOnExit=\"Lähtesta macOS V-Sync väljumisel\"\nHighResourceUsage=\"Kodeering on ülekoormatud! Proovi kasutada madalamaid videosätteid või kiiremaid kodeerimissätteid.\"\nTransition=\"Üleminek\"\nQuickTransitions=\"Valitud üleminekud\"\nFadeToBlack=\"Tumenda\"\nLeft=\"Vasakult\"\nRight=\"Paremalt\"\nTop=\"Ülalt\"\nBottom=\"Alt\"\nReset=\"Lähtesta\"\nHours=\"Tundi\"\nMinutes=\"Minutit\"\nSeconds=\"Sekundit\"\nDeprecated=\"Aegunud\"\nReplayBuffer=\"Taasesituse puhver\"\nImport=\"Impordi\"\nExport=\"Ekspordi\"\nCopy=\"Kopeeri\"\nPaste=\"Kleebi\"\nPasteReference=\"Kleebi (Viide)\"\nPasteDuplicate=\"Kleebi (Koopia)\"\nRemuxRecordings=\"Konverteeri salvestusi\"\nNext=\"Edasi\"\nBack=\"Tagasi\"\nDefaults=\"Vaikeseaded\"\nRestoreDefaults=\"Vaikeseaded\"\nHideMixer=\"Peida Mixeris\"\nTransitionOverride=\"Sunni üleminekut\"\nShowTransition=\"Näita üleminekut\"\nHideTransition=\"Peida üleminek\"\nNone=\"Määramata\"\nStudioMode.Preview=\"Eelvaade\"\nStudioMode.Program=\"Programm\"\nStudioMode.PreviewSceneName=\"Eelvaade: %1\"\nStudioMode.ProgramSceneName=\"Programm: %1\"\nShowInMultiview=\"Näita mitmikvaates\"\nVerticalLayout=\"Vertikaalne paigutus\"\nGroup=\"Grupp\"\nDoNotShowAgain=\"Ära enam näita\"\nDefault=\"(Vaikeseade)\"\nCalculating=\"Arvutamine...\"\nFullscreen=\"Täisekraan\"\nWindowed=\"Akendatud\"\nRefreshBrowser=\"Värskenda\"\nAspectRatio=\"Kuvasuhe <b>%1:%2</b>\"\nLockVolume=\"Lukusta helitugevus\"\nLogViewer=\"Logivaataja\"\nShowOnStartup=\"Kuva käivitamisel\"\nOpenFile=\"Ava fail\"\nAddScene=\"Lisa stseen\"\nAddSource=\"Lisa allikas\"\nRemoveScene=\"Eemalda valitud stseen\"\nRemoveSource=\"Eemalda valitud allikas(ad)\"\nMoveSceneUp=\"Liiguta stseeen üles\"\nMoveSceneDown=\"Liiguta stseen alla\"\nMoveSourceUp=\"Liiguta allikas(ad) üles\"\nMoveSourceDown=\"Liiguta allikas(ad) alla\"\nSourceProperties=\"Ava allika atribuudid\"\nSourceFilters=\"Ava allika filtrid\"\nMixerToolbarMenu=\"Helimiksri menüü\"\nSceneFilters=\"Ava stseeni filtrid\"\nList=\"Loend\"\nGrid=\"Ruudustik\"\nAutomatic=\"Automaatne\"\nComingSoon=\"Varsti tulekul\"\nPluginsFailedToLoad.Title=\"Plugina laadimise viga\"\nPluginsFailedToLoad.Text=\"Järgmiste OBS-i pluginate laadimine ebaõnnestus:\\n\\n%1\\nPalun uuendage või eemaldage need pluginad.\"\nAlreadyRunning.Title=\"OBS juba töötab\"\nAlreadyRunning.Text=\"OBS on juba käivitatud! Palun välju olemasolevatest protsessidest enne kui alustad uut. Kui OBS on minimeeritud süsteemi salve, siis kontrolli kas see on seal.\"\nAlreadyRunning.LaunchAnyway=\"Käivita siiski\"\nCrashHandling.Dialog.Title=\"OBS Studio rike tuvastatud\"\nCrashHandling.Labels.Text=\"OBS Studio ei sulgunud korralikult.\\n\\nKas käivitada turvarežiimis (kolmandate osapoolte pluginad, skriptimine ja WebSockets keelatud)?\"\nCrashHandling.Labels.PrivacyNotice=\"Samuti saate valida uusima krahhiaruande automaatse üleslaadimise OBSProjecti.<br /><br />Enne failide üleslaadimist lugege palun <a href='https://obsproject.com/privacy-policy'>privaatsuspoliitikat</a> ja pöörake erilist tähelepanu failide üleslaadimist puudutavatele osadele.\"\nCrashHandling.Checkbox.SendReport=\"Olen lugenud privaatsuspoliitikat ja annan üleslaadimisega nõusoleku.\"\nCrashHandling.Buttons.LaunchSafe=\"Käivita turvarežiimis\"\nCrashHandling.Buttons.LaunchNormal=\"Käivita tavarežiimis\"\nCrashHandling.Errors.UploadJSONError=\"Uusima krahhilogi üleslaadimisel ilmnes viga. Palun proovige hiljem uuesti.\"\nCrashHandling.Errors.Title=\"Krahhi logi üleslaadimise viga\"\nSafeMode.Restart=\"Kas soovid OBS-i taaskäivitada turvalises režiimis (kolmandate osapoolte pluginad, skriptid ja WebSockets on keelatud)?\"\nSafeMode.RestartNormal=\"Kas soovid OBS-i taaskäivitada tavarežiimis?\"\nChromeOS.Title=\"Toetuseta platvorm\"\nChromeOS.Text=\"OBS näib jooksvat ChromeOS-i konteineri sees. See platvorm ei ole toetatud.\"\nWine.Title=\"Wine\"\nWine.Text=\"OBS-i käivitamine Wine-is ei ole toetatud ja paljud funktsioonid, nagu näiteks pildistamine või seadme allikad, ei tööta või toimivad ainult piiratud võimsusega.<br><br>Selle asemel on soovitatav kasutada OBS-i originaalversiooni, näiteks <a href='https://flathub.org/apps/details/com.obsproject.Studio'> meie Flatpak versiooni</a> või teie operatsioonisüsteemi pakette.\"\nDockCloseWarning.Title=\"Dokitava akna sulgemine\"\nDockCloseWarning.Text=\"Sa just sulgesid dokitava akna. Kui sa tahad seda näidata uuesti, kasuta Vaade → Dokid menüüd menüüribal.\"\nExtraBrowsers=\"Kohandatud veebilehe dokid\"\nExtraBrowsers.Info=\"Dokide lisamiseks anna neile nimi ja URL, siis klikki Rakenda või Sulge et avada dokid. Sa saad lisada või eemaldada dokke ükskõik millal.\"\nExtraBrowsers.DockName=\"Doki nimi\"\nAuth.Authing.Title=\"Autentimine...\"\nAuth.Authing.Text=\"Autentimine teenusega %1, palun oota...\"\nAuth.AuthFailure.Title=\"Autentimise viga\"\nAuth.AuthFailure.Text=\"Autentimine teenusega %1 ebaõnnestus:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Autentimine on vajalik\"\nAuth.InvalidScope.Text=\"Autentimisvajadused teenusel %1 on muutunud. Mõned funktsioonid võivad olla kättesaamatud.\"\nAuth.LoadingChannel.Title=\"Kanaliinfo laadimine...\"\nAuth.LoadingChannel.Text=\"Teenuse %1 kanaliinfo laadimine...\"\nAuth.LoadingChannel.Error=\"Kanaliteavet ei õnnestunud hankida.\"\nAuth.ChannelFailure.Title=\"Kanali laadimine ebaõnnestus\"\nAuth.ChannelFailure.Text=\"Kanaliinfo laadimine teenusel %1 ebaõnnestus\\n\\n%2: %3\"\nAuth.Chat=\"Vestlus\"\nAuth.StreamInfo=\"Voogedastuse info\"\nTwitchAuth.Stats=\"Twitch'i statistika\"\nTwitchAuth.Feed=\"Twitch'i aktiivsuse voog\"\nTwitchAuth.TwoFactorFail.Title=\"Voogedastuse võtme päring ebaõnnestus\"\nTwitchAuth.TwoFactorFail.Text=\"OBS ei suutnud ühendada sinu Twitch kontot. Palun tee kindlaks, et kahefaktoriline autentimine on üles seatud sinu <a href='https://www.twitch.tv/settings/security'>Twitch turvalisuse seadetes</a>, sest see on vajalik voogestamiseks.\"\nRestreamAuth.Channels=\"Restream kanalid\"\nCopy.Filters=\"Kopeeri filtrid\"\nPaste.Filters=\"Kleebi filtrid\"\nBrowserPanelInit.Title=\"Veebibrauseri käivitamine...\"\nBrowserPanelInit.Text=\"Veebibrauser käivitub, palun oota...\"\nBandwidthTest.Region=\"Piirkond\"\nBandwidthTest.Region.US=\"Ameerika Ühendriigid\"\nBandwidthTest.Region.EU=\"Euroopa\"\nBandwidthTest.Region.Asia=\"Aasia\"\nBandwidthTest.Region.Other=\"Muu\"\nBasic.AutoConfig=\"Automaat-konfiguratsiooni viisard\"\nBasic.AutoConfig.ApplySettings=\"Rakenda seaded\"\nBasic.AutoConfig.StartPage=\"Kasutus info\"\nBasic.AutoConfig.StartPage.SubTitle=\"Täpsustage, mille jaoks te programmi kasutate\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimiseeri voogestamiseks, salvestamine on teisane\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimiseeri lihtsalt salvestamiseks, ma ei plaani voogestada\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Kastuan ainult Virtuaalset kaamerat.\"\nBasic.AutoConfig.VideoPage=\"Video sätted\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Täpsusta soovitud video sätted\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Kasuta praegust (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Kuva %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Kasuta praegust (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 või 30, aga eelista 60 kui võimalik\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 või 30, aga eelista kõrgemat resolutsiooni\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"NB: Lõuendi (aluse) resolutsioon ei ole tingimata sama mis voogestus või salvestus resolutsioon. Tegelik voogestus/salvestus resolutsioon võib olla väiksem lõuendist et vähendada bitikiirust või koormust arvutil.\"\nBasic.AutoConfig.StreamPage=\"Voogedastuse info\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Palun sisesta teavet voogedastuse kohta\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Ühenda konto (soovitatud)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Katkesta ühendus kontoga\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Katkesta ühendus kontoga?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Muudatus rakendub koheselt. Olete kindel et soovide konto sulgeda.\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Taotle voogedastuse võti\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Lisateave\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Kasuta voogedastuse võtit\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Kasuta Voogedastuse Võtit (edasijõudnud)\"\nBasic.AutoConfig.StreamPage.Service=\"Teenus\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Kuva kõik...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Kohandatud...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Voogedastuse võti\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: sisestage krüpteerimise salasõna.\\nRTMP: sisestage teenuse poolt antud võti.\\nSRT: sisestage streamid, kui teenus kasutab seda.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kodeerija võti\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Kasutaja Token\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Ühendatud konto\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Teosta bandwidth test bitikiiruse hindamiseks (võib kesta mõned minutid)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Eelista riistvaralist kodeeringut\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"\\\"Hardware Encoding\\\" vähendab CPU kasutust kuid võib tarbida rohkem bitikiirust, et säilitada sama kvaliteet.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Voogedastuse hoiatus\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Ribalaiuse test hakkab suvalisi videoandmeid ilma helita teie kanalile voogedastama. Kui teil on võimalik, on soovitatav voogedastuse videote salvestamine ajutiselt välja lülitada ja seada voogedastamine privaatseks, kuni test on lõppenud. Jätkata?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Ttest %1\"\nBasic.AutoConfig.TestPage=\"Lõpptulemused\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Programm viib nüüd läbi rea teste, et hinnata ideaalseid seadeid\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testimine on valmis\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Ribalaiuse testimine, mis võib võtta mõned minutid...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Selle teenuse protokolli väljundit ei leitud\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Ühendamine: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Ühegi serveriga ühenduse loomine ebaõnnestus, kontrollige oma Interneti-ühendust ja proovige uuesti.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Katsetan ribalaiust ühendusele %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Voogedastuse kodeerija testimine, mis võib võtta mõned minutid...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Salvestaja kodeerija testimine, mis võib võtta mõned minutid...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Kodeerija käivtamine ebaõnnestus\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"%1x%2 %3 FPS testimine...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Voogedastuse kodeerija\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Salvestuse Kodeerija\"\nBasic.AutoConfig.TestPage.Result.Header=\"Programm on kindlaks teinud, et need soovituslikud seaded on teie jaoks ideaalsed:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Nende seadete kasutamiseks klõpsake nuppu Rakenda seaded. Nõustaja ümberkonfigureerimiseks ja uueks proovimiseks klõpsake nuppu Tagasi. Kui soovite seaded ise käsitsi konfigureerida, klõpsake nuppu Tühista ja avage Seaded.\"\nBasic.AutoConfig.Info=\"Automaatne konfigureerimisviisard määrab teie arvuti tehniliste andmete ja internetikiiruse põhjal kindlaks parimad seaded.\"\nBasic.AutoConfig.RunAnytime=\"Seda saab igal ajal käivitada, minnes menüüsse tööriistad.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Voogedastuse (skaleeritud) eraldusvõime\"\nBasic.Stats=\"Statistika\"\nBasic.Stats.CPUUsage=\"CPU kasutus\"\nBasic.Stats.HDDSpaceAvailable=\"Kettaruumi on saadaval\"\nBasic.Stats.MemoryUsage=\"Mälukasutus\"\nBasic.Stats.AverageTimeToRender=\"Keskmine aeg kaadri töötlemiseks\"\nBasic.Stats.SkippedFrames=\"Kodeerimisviivituse tõttu vahelejäänud kaadreid\"\nBasic.Stats.MissedFrames=\"Kodeerimisviivituse tõttu puudujäänud kaadreid\"\nBasic.Stats.Output.Stream=\"Voogedastus\"\nBasic.Stats.Output.Recording=\"Salvestamine\"\nBasic.Stats.Status=\"Staatus\"\nBasic.Stats.Status.Recording=\"Salvestamine\"\nBasic.Stats.Status.Live=\"OTSE\"\nBasic.Stats.Status.Reconnecting=\"Taasühendamine\"\nBasic.Stats.Status.Inactive=\"Pole aktiivne\"\nBasic.Stats.Status.Active=\"Aktiivne\"\nBasic.Stats.DroppedFrames=\"Vahele jäetud kaadreid (võrk)\"\nBasic.Stats.MegabytesSent=\"Andmete koguväljund\"\nBasic.Stats.Bitrate=\"Bitikiirus\"\nBasic.Stats.DiskFullIn=\"Ketas täis (umbes)\"\nBasic.Stats.DiskFullIn.Text=\"%1 tundi, %2 minutit\"\nBasic.Stats.ResetStats=\"Nulli näidud\"\nResetUIWarning.Title=\"Kas soovite lähestada UI valikud?\"\nResetUIWarning.Text=\"Kasutajaliidese lähtestamine peidab täiendavad dokid. Kui soovid, et need dokid oleksid nähtavad, tuleb need dokkide menüüst nähtavaks teha.\\n\\nKas oled kindel, et soovid kasutajaliidese lähtestamist?\"\nUpdater.Title=\"Uus värskendus saadaval\"\nUpdater.Text=\"Uus värskendus on saadaval:\"\nUpdater.UpdateNow=\"Värskenda kohe\"\nUpdater.RemindMeLater=\"Tuleta mulle hiljem meelde\"\nUpdater.Skip=\"Jäta vahele versioon\"\nUpdater.NoUpdatesAvailable.Title=\"Uusi värskendusi pole saadaval\"\nUpdater.NoUpdatesAvailable.Text=\"Värskendusi pole praegu saadaval\"\nUpdater.BranchNotFound.Title=\"Uuenduskanal eemaldatud\"\nUpdater.BranchNotFound.Text=\"Sinu valitud uuenduskanal ei ole enam saadaval, OBS on lähtestatud esialgsele seadistusele.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Terviklikkuse kontroll ei ole saadaval\"\nUpdater.RepairButUpdatesAvailable.Text=\"Faili terviklikkuse kontrollimine on võimalik ainult viimase olemasoleva versiooni puhul. OBS-i paigalduse kontrollimiseks ja uuendamiseks kasutage käsku Abi → Otsi värskendusi.\"\nUpdater.RepairConfirm.Title=\"Kinnita terviklikkuse kontroll\"\nUpdater.RepairConfirm.Text=\"Terviklikkuse kontrollimise käivitamine otsib teie OBS-i installeeringust kahjustusi ja laeb uuesti alla katkised/muudetud failid. See võib võtta veidi aega.\\n\\nKas soovid jätkata?\"\nUpdater.FailedToLaunch=\"Uuendaja käivitamine nurjus\"\nQuickTransitions.SwapScenes=\"Vaheta eelvaade ja programmi stseenid pärast üleminekut\"\nQuickTransitions.SwapScenesTT=\"Vahetab eelvaate ja programmi stseenid pärast üleminekut (kui programmi algne stseen on veel olemas).\\nSee ei pööra programmi algses stseenis tehtud muudatusi tagasi.\"\nQuickTransitions.DuplicateScene=\"Tee stseenist koopia\"\nQuickTransitions.DuplicateSceneTT=\"Sama stseeni redigeerimisel võimaldab redigeerida allikate transformatsiooni/nähtavust ilma programmi väljundit muutmata.\\nAllikate omaduste redigeerimiseks ilma programmi väljundit muutmata, lülitage sisse 'Duplicate Sources'.\\nSelle väärtuse muutmine lähtestab praeguse programmi stseeni (kui see on veel olemas).\"\nQuickTransitions.EditProperties=\"Dubleeritud allikad\"\nQuickTransitions.EditPropertiesTT=\"Sama stseeni redigeerimisel võimaldab redigeerida allikate omadusi ilma programmi väljundit muutmata.\\nSeda saab kasutada ainult siis, kui \\\"Dubleeri stseen\\\" on lubatud.\\nTeatud allikad (näiteks kaamera- või meediaallikad) seda ei toeta ja neid ei saa eraldi redigeerida. \\nSelle väärtuse muutmine lähtestab praeguse programmi stseeni (kui see on veel olemas).\\n\\nHoiatus: Kuna allikad dubleeritakse, võib see nõuda täiendavaid süsteemi- või videoressursse.\"\nQuickTransitions.HotkeyName=\"Valitud üleminek: %1\"\nBasic.AddTransition=\"Lisa seadistatav üleminek\"\nBasic.RemoveTransition=\"Eemalda seadistatav üleminek\"\nBasic.TransitionProperties=\"Ülemineku omadused\"\nBasic.SceneTransitions=\"Stseeni üleminekud\"\nBasic.TransitionDuration=\"Kestus\"\nBasic.TogglePreviewProgramMode=\"Stuudiorežiim\"\nBasic.EnablePreviewProgramMode=\"Lülita stuudiorežiim sisse\"\nBasic.DisablePreviewProgramMode=\"Lülita stuudiorežiim välja\"\nUndo.Undo=\"Võta tagasi\"\nUndo.Redo=\"Tee uuesti\"\nUndo.Add=\"Lisa „%1“\"\nUndo.Delete=\"Kustuta „%1“\"\nUndo.Rename=\"Muuda „%1“ nime\"\nUndo.SceneCollection.Switch=\"Lülita ümber '%1'\"\nUndo.Item.Undo=\"Võta tagasi - %1\"\nUndo.Item.Redo=\"Korda uuesti - %1\"\nUndo.Sources.Multi=\"Kustuta %1 andmeallikat\"\nUndo.Filters=\"Krahhi logi üleslaadimise viga\"\nUndo.Filters.Paste.Single=\"Kleebi filter '%1' filtrisse '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopeeri filtrid kaustast '%1' kausta '%2'\"\nUndo.Transform=\"Teisendusallikas(ed) failis '%1'\"\nUndo.Transform.Paste=\"Kleebi teisendus faili '%1'\"\nUndo.Transform.Rotate=\"Pööramine '%1'-s\"\nUndo.Transform.Reset=\"Teisendus Lähtesta failis '%1'\"\nUndo.Transform.HFlip=\"Horisontaalne pööramine '%1'-s\"\nUndo.Transform.VFlip=\"Vertikaalne pööramine '%1'-s\"\nUndo.Transform.FitToScreen=\"Mahuta ekraanile '%1'\"\nUndo.Transform.StretchToScreen=\"Venita ekraanile '%1'\"\nUndo.Transform.Center=\"Ekraani keskele paigutamine '%1'-s\"\nUndo.Transform.VCenter=\"Vertikaalselt ekraani keskpunktiks '%1'\"\nUndo.Transform.HCenter=\"Horisontaalselt ekraani keskpunkt '%1'-s\"\nUndo.Volume.Change=\"Helitugevuse muutus '%1'-l\"\nUndo.Volume.Mute=\"Summuta „%1“\"\nUndo.Volume.Unmute=\"Lõpeta „%1“ summutamine\"\nUndo.Balance.Change=\"Heli tasakaalu muutus '%1'-l\"\nUndo.SyncOffset.Change=\"Heli sünkroniseerimise nihke muutus '%1'-l\"\nUndo.MonitoringType.Change=\"Muuda heli jälgimist seadmel '%1'\"\nUndo.Mixers.Change=\"Muuda \\\"%1\\\"-is helimikserit\"\nUndo.ForceMono.On=\"Luba sunnitud monorežiim seadmel '%1'\"\nUndo.ForceMono.Off=\"Keela sundmono kasutamine seadmel '%1'\"\nUndo.Properties=\"Omaduse muutus '%1'-l\"\nUndo.Scene.Duplicate=\"Duplikaatstseen '%1'\"\nUndo.ShowTransition=\"Näita üleminekut lehel '%1'\"\nUndo.HideTransition=\"Peida üleminek lehel '%1'\"\nUndo.ShowSceneItem=\"Näita '%1' kohas '%2'\"\nUndo.HideSceneItem=\"Peida '%1' kohas '%2'\"\nUndo.ReorderSources=\"Allikate järjekorda muutmine failis '%1'\"\nUndo.MoveUp=\"Liiguta '%1' üles '%2'-s\"\nUndo.MoveDown=\"Liiguta '%1' allapoole '%2'-s\"\nUndo.MoveToTop=\"Liiguta '%1' '%2' üles\"\nUndo.MoveToBottom=\"Liiguta '%1' '%2' alla\"\nUndo.PasteSource=\"Kleebi allikas(ed) kausta '%1'\"\nUndo.PasteSourceRef=\"Kleebi allikaviide(d) kausta '%1'\"\nUndo.GroupItems=\"Rühmita üksused „%1“ alusel\"\nTransitionNameDlg.Text=\"Sisesta ülemineku nimi\"\nTransitionNameDlg.Title=\"Ülemineku nimi\"\nTitleBar.SafeMode=\"TURVAREŽIIM\"\nTitleBar.PortableMode=\"Kaasaskantav režiim\"\nTitleBar.Profile=\"Profiil\"\nTitleBar.Scenes=\"Stseenid\"\nNameExists.Title=\"Nimi on juba kasutusel\"\nNameExists.Text=\"See nimi on juba kasutuses.\"\nNoNameEntered.Title=\"Palun sisesta sobiv nimi\"\nNoNameEntered.Text=\"Tühja nime ei saa kasutada.\"\nConfirmStart.Title=\"Alustada voogedastusega?\"\nConfirmStart.Text=\"Kas soovid kindlasti voogedastust alustada?\"\nConfirmStop.Title=\"Lõpetada voogedastus?\"\nConfirmStop.Text=\"Kas soovid kindlasti voogedastust lõpetada?\"\nConfirmStopRecord.Title=\"Kas peatame salvestamise?\"\nConfirmStopRecord.Text=\"Kas sa oled kindel, et soovid salvestamist peatada?\"\nConfirmBWTest.Title=\"Kas käivitada ülekandekiiruse test?\"\nConfirmBWTest.Text=\"OBS on konfigureeritud ülekandekiiruse testi režiimi. See režiim võimaldab võrgutesti sooritamist ilma, et kanalile voogedastataks. Kui test on valmis, peate režiimi välja lülitama, et vaatajad taas näeksid teie voogedastust.\\n\\nKas soovite jätkata?\"\nConfirmExit.Title=\"Aktiivsed väljundid\"\nConfirmExit.Text=\"OBS on praegu veel aktiivne. Kõik voogedastused/salvestused suletakse.\"\nConfirmRemove.Title=\"Ümbernimetamise kinnitamine\"\nConfirmRemove.Text=\"Kas soovid kindkasti eemaldada '%1'?\"\nConfirmRemove.TextMultiple=\"Kas soovite kindlasti eemaldada %1 üksust?\"\nConfirmReset.Title=\"Lähtesta atribuudid\"\nConfirmReset.Text=\"Kas oled kindel, et soovid lähtestada praegused atribuudid vaikeseadeteks?\"\nOutput.StartStreamFailed=\"Voogedastuse alustamine nurjus\"\nOutput.StartRecordingFailed=\"Salvestamise alustamine nurjus\"\nOutput.StartReplayFailed=\"Taasesitus puhvri käivitamine ebaõnnestus\"\nOutput.StartVirtualCamFailed=\"Virtuaalse kaamera käivitamine ei õnnestunud\"\nOutput.StartFailedGeneric=\"Väljundi käivitamine ei õnnestunud. Palun vaata logist üksiasju.\\n\\nNB! Kui kasutad NVENC või AMD enkoodereid, palun veendu, et graafikadraiverid oleksid uuendatud.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Ei saa salvestada taasesitusi pausitud olekus\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Hoiatus: Taasesitusi ei saa salvestada pausitud olekus.\"\nOutput.ConnectFail.Title=\"Ühendamine ei õnnestunud\"\nOutput.ConnectFail.BadPath=\"Vigane rada või ühenduse URL. Palun veendu, et valitud sätted on õiged.\"\nOutput.ConnectFail.ConnectFailed=\"Serveriga ühendamine ebaõnnestus\"\nOutput.ConnectFail.InvalidStream=\"Määratud kanalile või voogedastuse võtmele ei saanud juurdepääsu, palun kontrolli oma voogedastuse võtit. Kui see on õige, võib probleem olla serveriga ühenduse loomisel.\"\nOutput.ConnectFail.HdrDisabled=\"HDR-väljund on praegu selle väljundi puhul välja lülitatud.\"\nOutput.ConnectFail.Error=\"Serveriga ühenduse loomisel ilmnes ootamatu viga. Lisateave logifailis.\"\nOutput.ConnectFail.Disconnected=\"Ühendus serveriga katkestatud.\"\nOutput.StreamEncodeError.Title=\"Kodeerimise viga\"\nOutput.StreamEncodeError.Msg=\"Voogedastamisel tekkis kodeerija viga.\"\nOutput.StreamEncodeError.Msg.LastError=\"Voogedastuse ajal ilmnes kodeerija viga:<br><br>%1\"\nOutput.RecordFail.Title=\"Salvestamise alustamine nurjus\"\nOutput.RecordFail.Unsupported=\"Väljundformaat ei ole toetatud või ei toeta rohkem kui ühte helirada. Palun kontrolli oma seadeid ja proovi uuesti.\"\nOutput.RecordNoSpace.Title=\"Kettaruumi pole piisavalt\"\nOutput.RecordNoSpace.Msg=\"Pole piisavalt kettaruumi, et jätkata salvestamist.\"\nOutput.RecordError.Title=\"Salvestamise tõrge\"\nOutput.RecordError.Msg=\"Salvestamise ajal ilmnes tundmatu tõrge.\"\nOutput.RecordError.EncodeErrorMsg=\"Salvestamise ajal ilmnes kodeerija viga.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Salvestamise ajal ilmnes kodeerija viga:<br><br>%1\"\nOutput.BadPath.Title=\"Vigane fali tee\"\nOutput.BadPath.Text=\"Konfigureeritud salvestuse teed ei saanud avada. Palun kontrolli oma salvestuse tee Seaded → Väljund → Salvestamine alt.\"\nOutput.NoBroadcast.Title=\"Ühtegi saadet pole konfigureeritud\"\nOutput.NoBroadcast.Text=\"Enne voogesituse alustamist pead seadistama saate.\"\nOutput.BroadcastStartFailed=\"Saate käivitamine ebaõnnestus\"\nOutput.BroadcastStopFailed=\"Saate peatamine ebaõnnestus\"\nLogUploadDialog.Title=\"OBS Studio logifaili üleslaadimine\"\nLogUploadDialog.Labels.PrivacyNotice=\"Enne failide üleslaadimist lugege palun läbi <a href='https://obsproject.com/privacy-policy'>privaatsuspoliitika</a> ja selle failide üleslaadimist käsitlev osa.\"\nLogUploadDialog.Labels.Progress=\"Logi üleslaadimine on pooleli. Palun oodake...\"\nLogUploadDialog.Labels.Description.AppLog=\"Teie logifail on üles laaditud. Nüüd saate URL-i veaotsingu või toe eesmärgil jagada.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Teie krahhiaruanne on üles laaditud. Nüüd saate URL-i veaotsingu eesmärgil jagada.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Laadi üles\"\nLogUploadDialog.Buttons.CopyURL=\"Kopeeri logi URL\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Logifaili analüüsimine\"\nLogUploadDialog.Buttons.RetryButton=\"Proovi uuesti\"\nLogUploadDialog.Errors.Template=\"Faili üleslaadimisel ilmnes viga:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Üleslaaditavat faili ei leitud või fail oli tühi.\"\nRemux.SourceFile=\"OBS-i salvestus\"\nRemux.TargetFile=\"Sihtfail\"\nRemux.Remux=\"Konverteeri\"\nRemux.Stop=\"Lõpeta konverteerimine\"\nRemux.ClearFinished=\"Eemalda valmis saanud üksused\"\nRemux.ClearAll=\"Eemalda kõik üksused\"\nRemux.OBSRecording=\"OBS-i salvestus\"\nRemux.FinishedTitle=\"Konverteerimine valmis\"\nRemux.Finished=\"Salvestus on ümber kodeeritud\"\nRemux.FinishedError=\"Salvestus on ümber kodeeritud, aga fail võib olla poolik\"\nRemux.SelectRecording=\"Vali OBS-i salvestus...\"\nRemux.SelectTarget=\"Vali sihtfail...\"\nRemux.FileExistsTitle=\"Sihtfailid on olemas\"\nRemux.FileExists=\"Järgmised sihtfailid on juba olemas. Kas soovid neid asendada?\"\nRemux.ExitUnfinishedTitle=\"Kodeerimine on pooleli\"\nRemux.ExitUnfinished=\"Kodeerimine ei ole veel lõpetatud. Kui kodeerimine peatada, siis võib sihtfail olla kasutuskõlbmatu.\\nKas soovid kindlasti kodeerimise peatada?\"\nRemux.HelpText=\"Tooge failid sellesse aknasse konverteerimiseks või valige tühi lahter \\\"OBS-i salvestus\\\", et otsida faili.\"\nRemux.NoFilesAddedTitle=\"Remux-faili pole lisatud\"\nRemux.NoFilesAdded=\"Remuxile ei lisatud ühtegi faili. Eemalda kaust, mis sisaldab ühte või mitut videofaili.\"\nMissingFiles=\"Puuduvad failid\"\nMissingFiles.MissingFile=\"Puuduv fail\"\nMissingFiles.NewFile=\"Uus fail\"\nMissingFiles.HelpText=\"Peale viimast OBS'i kasutamist on mõned failid kaotsi läinud.\"\nMissingFiles.NumFound=\"%1 leitud %2-st\"\nMissingFiles.Search=\"Otsi Kaust...\"\nMissingFiles.SelectFile=\"Vali fail...\"\nMissingFiles.SelectDir=\"Valige kaust, milles otsida\"\nMissingFiles.State=\"Olek\"\nMissingFiles.Missing=\"Puudub\"\nMissingFiles.Replaced=\"Asendatud\"\nMissingFiles.Cleared=\"Kustutatud\"\nMissingFiles.Found=\"Leitud\"\nMissingFiles.AutoSearch=\"Leiti täiendavaid failivasteid\"\nMissingFiles.AutoSearchText=\"OBS leidis selles kataloogis puuduvatele failidele täiendavaid vasteid. Kas soovite need lisada?\"\nMissingFiles.NoMissing.Title=\"Puuduvate failide kontroll\"\nMissingFiles.NoMissing.Text=\"Ühtegi faili ei tundu olevat puudu.\"\nMacPermissions.MenuAction=\"Kontrolli rakenduse õigusi...\"\nMacPermissions.Title=\"Kontrolli rakenduse õigusi\"\nMacPermissions.Description=\"OBS Studio vajab teatud funktsioonide võimaldamiseks sinu luba. Soovitatav on need õigused anda, kuid rakenduse kasutamiseks ei ole need nõutavad. Võid neid alati hiljem lubada.\"\nMacPermissions.Description.OpenDialog=\"Seda dialoogi saab uuesti avada OBS Studio menüüst.\"\nMacPermissions.AccessGranted=\"Ligipääs on lubatud\"\nMacPermissions.RequestAccess=\"Taotle ligipääsu\"\nMacPermissions.OpenPreferences=\"Ava %1 eelistused\"\nMacPermissions.Item.ScreenRecording=\"Ekraani salvestamine\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS vajab seda luba, et saaks sinu ekraani salvestada.\"\nMacPermissions.Item.Camera=\"Kaamera\"\nMacPermissions.Item.Camera.Details=\"See luba on vajalik veebikaamera või kaamerakaardi sisu salvestamiseks.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS nõuab seda luba, kui soovid salvestada oma mikrofoni või välist heliseadet.\"\nMacPermissions.Item.InputMonitoring=\"Sisendi jälgimine\"\nMacPermissions.Item.InputMonitoring.Details=\"See luba on vajalik kiirklahvide töötamiseks, kui OBS on taustal.\"\nMacPermissions.Item.Accessibility=\"Juurdepääsetavus\"\nMacPermissions.Item.Accessibility.Details=\"Vanemates installatsioonides võib OBS olla loetletud jaotises „Ligipääsetavus” jaotise „Sisendi jälgimine” asemel.\"\nMacPermissions.Continue=\"Jätka\"\nSourceLeak.Title=\"Allika puhastamise viga\"\nSourceLeak.Text=\"Stseenikogude muutmisel ilmnes probleem ja mõnda allikat ei saanud maha laadida. Selle probleemi põhjustavad tavaliselt pluginad, mis ei vabasta ressursse õigesti. Veenduge, et kõik teie kasutatavad pluginad oleksid ajakohased.\\n\\nOBS Studio sulgub nüüd, et vältida võimalikku andmete riknemist.\"\nBasic.DesktopDevice1=\"Töölaua heli\"\nBasic.DesktopDevice2=\"Töölaua heli 2\"\nBasic.AuxDevice1=\"Mikrofon/Aux\"\nBasic.AuxDevice2=\"Mikrofon/Aux 2\"\nBasic.AuxDevice3=\"Mikrofon/Aux 3\"\nBasic.AuxDevice4=\"Mikrofon/Aux 4\"\nBasic.Scene=\"Stseen\"\nBasic.DisplayCapture=\"Kuvari hõive\"\nBasic.Main.PreviewConextMenu.Enable=\"Lülita eelvaade sisse\"\nBasic.Main.Preview.Disable=\"Keela eelvaade\"\nScaleFiltering=\"Skaala filtreerimine\"\nScaleFiltering.Point=\"Punkt\"\nScaleFiltering.Bilinear=\"Bilineaarne\"\nScaleFiltering.Bicubic=\"Bikuubiline\"\nScaleFiltering.Area=\"Ala\"\nBlendingMethod=\"Segunemise meetod\"\nBlendingMethod.Default=\"Vaikeseade\"\nBlendingMethod.SrgbOff=\"SRGB väljalülitatud\"\nBlendingMode=\"Segunemise viis\"\nBlendingMode.Normal=\"Normaalne\"\nBlendingMode.Additive=\"Lisa\"\nBlendingMode.Subtract=\"Lahutamine\"\nBlendingMode.Screen=\"Ekraan\"\nBlendingMode.Multiply=\"Korrutamine\"\nBlendingMode.Lighten=\"Helendamine\"\nBlendingMode.Darken=\"Tumendamine\"\nDeinterlacing=\"Põimingu eemaldamine\"\nDeinterlacing.Discard=\"Loobu\"\nDeinterlacing.TopFieldFirst=\"Ülemine väli esimesena\"\nDeinterlacing.BottomFieldFirst=\"Alumine väli esimesena\"\nVolControl.SliderUnmuted=\"Helitugevuse liugur '%1' jaoks:\"\nVolControl.SliderMuted=\"Helitugevuse liugur seadmele '%1': (hetkel vaigistatud)\"\nVolControl.Mute=\"Summuta „%1“\"\nVolControl.Properties=\"'%1' atribuudid\"\nVolControl.UnassignedWarning.Title=\"Määramata heliallikas\"\nVolControl.UnassignedWarning.Text=\"„%1” pole ühelegi helirajale määratud ja seda ei kuule voogedastustes ega salvestistes.\\n\\nHeliallika rajale määramiseks avage paremklõpsu menüü või mikseri doki tööriistaribal oleva hammasratta nupu kaudu täpsemad heliomadused.\"\nBasic.Main.AddSceneDlg.Title=\"Stseeni lisamine\"\nBasic.Main.AddSceneDlg.Text=\"Sisesta stseeni nimi\"\nBasic.Main.DefaultSceneName.Text=\"Stseen %1\"\nBasic.Main.AddSceneCollection.Title=\"Lisada stseeni kogumik\"\nBasic.Main.AddSceneCollection.Text=\"Palun sisestage stseeni kogumiku nimi\"\nBasic.Main.RenameSceneCollection.Title=\"Nimeta ümber stseeni kogumik\"\nBasic.Main.RemigrateSceneCollection.Title=\"Stseenikogu resolutsiooni värskendamine\"\nBasic.Main.RemigrateSceneCollection.Text=\"Kas soovite uuendada stseenikogu \\\"%1\\\" resolutsiooni, et see vastaks praeguse profiili lõuendi resolutsioonile %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Stseenide kogu resolutsiooni ei saa värskendada, kui väljundid on aktiivsed.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Stseenikogu resolutsiooni uuendamine ebaõnnestus. Algne resolutsioon on teadmata.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Lähtestamine pole võimalik: OBS-i eraldusvõime muutmine ebaõnnestus.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Lähtestamine pole võimalik: praegune resolutsioon on juba stseenide kogu baasresolutsioon.\"\nAddProfile.Title=\"Profiili lisamine\"\nAddProfile.Text=\"Sisesta uue profiili nimi\"\nAddProfile.WizardCheckbox=\"Kuva automaatse konfigureerimise viisard\"\nRenameProfile.Title=\"Profiili ümbernimetamine\"\nBasic.Main.MixerRename.Title=\"Nimeta audioallikas ringi\"\nBasic.Main.MixerRename.Text=\"Palun sisesta audioallika nimi\"\nBasic.Main.PreviewDisabled=\"Eelvaade on hetkel välja lülitatud\"\nBasic.SourceSelect=\"Loo/vali allikas\"\nBasic.SourceSelect.CreateNew=\"Loo uus\"\nBasic.SourceSelect.AddExisting=\"Lisa olemasolev\"\nBasic.SourceSelect.AddVisible=\"Tee allikas nähtavaks\"\nBasic.Main.Sources.Visibility=\"Nähtavus\"\nBasic.Main.Sources.VisibilityDescription=\"Juhib '%1' nähtavust lõuendil\"\nBasic.Main.Sources.Lock=\"Lukusta\"\nBasic.Main.Sources.LockDescription=\"Lukustab '%1' asukoha ja skaala lõuendil\"\nBasic.PropertiesWindow=\"'%1' omadused\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (automaatne valik: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Vali värv\"\nBasic.PropertiesWindow.SelectFont=\"Vali font\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Valige font\"\nBasic.PropertiesWindow.ConfirmTitle=\"Seaded muutusid\"\nBasic.PropertiesWindow.Confirm=\"On salvestamata muutusi. Kas soovite neid säilitada?\"\nBasic.PropertiesWindow.NoProperties=\"Atribuudid pole saadaval\"\nBasic.PropertiesWindow.AddFiles=\"Lisa failid\"\nBasic.PropertiesWindow.AddDir=\"Lisa kataloog\"\nBasic.PropertiesWindow.AddURL=\"Lisa tee/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Lisa kataloog sihtkohta '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Lisa failid kausta '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Lisa kirje kausta '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Muuda kirjet allikast '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Lihtsad FPS väärtused\"\nBasic.PropertiesView.FPS.Rational=\"Ratsionaalsed FPS-i väärtused\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Kehtivad FPS-vahemikud:\"\nBasic.PropertiesView.UrlButton.Text=\"Ava see link oma vaikimisi veebibrauseris?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Ava URL\"\nBasic.InteractionWindow=\"Suhtlemine kasutajaga '%1'\"\nBasic.StatusBar.Reconnecting=\"Ühendus katkestatud, uuestiühendamine %2 sekundi pärast (katse %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Üritatakse uuesti ühendust luua... (katse %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Taasühendumine edukas\"\nBasic.StatusBar.Delay=\"Viivitus (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Viivitus (algab %1 s pärast)\"\nBasic.StatusBar.DelayStoppingIn=\"Viivitus (lõppeb %1 s pärast)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Viivitus (lõppeb %1 s pärast, algab %2 s pärast)\"\nBasic.StatusBar.RecordingSavedTo=\"Salvestus salvestati \\\"%1\\\"\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Salvestasin taasesituse puhvri „%1“ faili\"\nBasic.StatusBar.ScreenshotSavedTo=\"Ekraanipilt salvestatud asukohta '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Salvestus konverteeriti \\\"%1\\\"-ks automaatselt\"\nBasic.Filters=\"Filtrid\"\nBasic.Filters.AsyncFilters=\"Audio/Video filtrid\"\nBasic.Filters.AudioFilters=\"Helifiltrid\"\nBasic.Filters.EffectFilters=\"Efektifiltrid\"\nBasic.Filters.Title=\"Filtrid '%1' jaoks\"\nBasic.Filters.AddFilter.Title=\"Filtri nimi\"\nBasic.Filters.AddFilter.Text=\"Palun määra filtri nimi\"\nBasic.TransformWindow=\"Stseeni elemendi teisendus\"\nBasic.TransformWindow.Position=\"Asukoht\"\nBasic.TransformWindow.PositionX=\"Positsioon X\"\nBasic.TransformWindow.PositionY=\"Positsioon Y\"\nBasic.TransformWindow.Rotation=\"Pööramine\"\nBasic.TransformWindow.Size=\"Suurus\"\nBasic.TransformWindow.Width=\"Laius\"\nBasic.TransformWindow.Height=\"Kõrgus\"\nBasic.TransformWindow.Alignment=\"Joondamine\"\nBasic.TransformWindow.BoundsType=\"Piirdekasti tüüp\"\nBasic.TransformWindow.BoundsAlignment=\"Joondus piirdekastis\"\nBasic.TransformWindow.Bounds=\"Piirdekasti suurus\"\nBasic.TransformWindow.BoundsWidth=\"Piirdekasti laius\"\nBasic.TransformWindow.BoundsHeight=\"Piirdekasti kõrgus\"\nBasic.TransformWindow.CropToBounds=\"Kärbi piirdekastini\"\nBasic.TransformWindow.Crop=\"Kärbi\"\nBasic.TransformWindow.CropLeft=\"Kärbi vasakult\"\nBasic.TransformWindow.CropRight=\"Kärbi paremalt\"\nBasic.TransformWindow.CropTop=\"Kärbi ülevalt\"\nBasic.TransformWindow.CropBottom=\"Kärbi alt\"\nBasic.TransformWindow.Alignment.TopLeft=\"Ülale vasakule\"\nBasic.TransformWindow.Alignment.TopCenter=\"Ülale keskele\"\nBasic.TransformWindow.Alignment.TopRight=\"Ülale paremale\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Keskele vasakule\"\nBasic.TransformWindow.Alignment.Center=\"Keskele\"\nBasic.TransformWindow.Alignment.CenterRight=\"Keskele paremale\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Alla vasakule\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Alla keskele\"\nBasic.TransformWindow.Alignment.BottomRight=\"Alla paremale\"\nBasic.TransformWindow.BoundsType.None=\"Piirid määramata\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Maksimaalne suurus ainult\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skaleeri sisemiste piirideni\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skaleeri välispiirideni\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skaleeri piiride laiuse järgi\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skaala piiride kõrguse järgi\"\nBasic.TransformWindow.BoundsType.Stretch=\"Venitada piirideni\"\nBasic.TransformWindow.Title=\"Redigeeri teisendust '%1' jaoks\"\nBasic.TransformWindow.NoSelectedSource=\"Ühtegi allikat ei ole valitud\"\nBasic.Main.AddSourceHelp.Title=\"Allikat ei saa lisada\"\nBasic.Main.AddSourceHelp.Text=\"Allika lisamiseks peab olema vähemalt üks stseen.\"\nBasic.Main.Scenes=\"Stseenid\"\nBasic.Main.Sources=\"Allikad\"\nBasic.Main.Source=\"Allikas\"\nBasic.Main.Controls=\"Juhtnupud\"\nBasic.Main.PreparingStream=\"Ettevalmistus...\"\nBasic.Main.Connecting=\"Ühendamine...\"\nBasic.Main.StartRecording=\"Alusta salvestamist\"\nBasic.Main.StartReplayBuffer=\"Võta taasesituse puhver kasutusele\"\nBasic.Main.SaveReplay=\"Salvesta Taasesitus\"\nBasic.Main.StartStreaming=\"Alusta voogedastust\"\nBasic.Main.StartBroadcast=\"Käivita otseülekanne\"\nBasic.Main.StartVirtualCam=\"Käivita virtuaalne kaamera\"\nBasic.Main.StopRecording=\"Lõpeta salvestamine\"\nBasic.Main.PauseRecording=\"Peata salvestust\"\nBasic.Main.UnpauseRecording=\"Jätka salvestust\"\nBasic.Main.SplitFile=\"Tükelda salvestusfail\"\nBasic.Main.AddChapterMarker=\"Lisa peatüki märge (ainult hübriid MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Salvestamise peatamine...\"\nBasic.Main.StopReplayBuffer=\"Peata taasesituse puhver\"\nBasic.Main.StoppingReplayBuffer=\"Taasesitus puhvri peatamine...\"\nBasic.Main.SetupBroadcast=\"Halda leviedastust\"\nBasic.Main.StopStreaming=\"Lõpeta voogedastus\"\nBasic.Main.StopBroadcast=\"Lõpeta saade\"\nBasic.Main.AutoStopEnabled=\"(Automaatne seiskamine)\"\nBasic.Main.StoppingStreaming=\"Voogedastuse peatamine...\"\nBasic.Main.ForceStopStreaming=\"Lõpeta voogedastus (unusta viivitus)\"\nBasic.Main.ShowContextBar=\"Näita allika tööriistariba\"\nBasic.Main.HideContextBar=\"Peida allika tööriistariba\"\nBasic.Main.StopVirtualCam=\"Peata virtuaalne kaamera\"\nBasic.Main.Group=\"Grupp %1\"\nBasic.Main.GroupItems=\"Rühmita valitud üksused\"\nBasic.Main.Ungroup=\"Eemalda rühmitamine\"\nBasic.Main.GridMode=\"Ruudustikurežiim\"\nBasic.Main.ListMode=\"Loendirežiim\"\nBasic.Main.VirtualCamConfig=\"Seadista virtuaalne kaamera\"\nBasic.VCam.VirtualCamera=\"Virtuaalne kaamera\"\nBasic.VCam.OutputType=\"Väljundi tüüp\"\nBasic.VCam.OutputSelection=\"Väljundi valik\"\nBasic.VCam.OutputType.Program=\"Programm (vaikeseade)\"\nBasic.VCam.OutputSelection.NoSelection=\"Selle väljundtüübi jaoks pole valikut\"\nBasic.VCam.RestartWarning=\"Selle muudatuse jõustamiseks virtuaalne kaamera käivitatakse uuesti\"\nBasic.MainMenu.File=\"&Fail\"\nBasic.MainMenu.File.Export=\"&Ekspordi\"\nBasic.MainMenu.File.Import=\"&Impordi\"\nBasic.MainMenu.File.ShowRecordings=\"Ava salvestuste kaust (&R)\"\nBasic.MainMenu.File.Remux=\"&Konverteeri salvestusi\"\nBasic.MainMenu.File.Settings=\"&Sätted\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Ava sätete kaust\"\nBasic.MainMenu.File.ShowProfileFolder=\"Ava profiilide kaust\"\nBasic.MainMenu.File.ShowMissingFiles=\"Kontrolli, kas faile on puudu\"\nBasic.MainMenu.File.Exit=\"Välju (&X)\"\nBasic.MainMenu.Edit=\"Muuda (&E)\"\nBasic.MainMenu.Edit.Undo=\"Võta tagasi (&U)\"\nBasic.MainMenu.Edit.LockPreview=\"&Lukusta eelvaade\"\nBasic.MainMenu.Edit.Scale=\"Eelvaade ja skaleerimine\"\nBasic.MainMenu.Edit.Scale.Window=\"Skaleeri akna järgi\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Lõuend (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Väljund (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Skaleeritud (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Suumi sisse\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Suumi välja\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Lähtesta suum\"\nBasic.MainMenu.Edit.Transform=\"&Teisenda\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Muuda Teisendus\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopeeri teisendus\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Kleebi teisendus\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Lähtesta teisendus\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Pööra 90 kraadi paremale\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Pööra 90 kraadi vasakule\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Pööra 180 kraadi\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Pööra &horisontaalseks\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Pööra &vertikaalseks\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Sobita ekraanile (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Venita ekraanile (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Joonda ekraani &keskele\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Joonda vertikaalselt keskele\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Joonda horisontaalselt keskele\"\nBasic.MainMenu.Edit.Order=\"Järjek&ord\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Liig&uta Üles\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Liiguta Alla (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Liigu&ta Esimeseks\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Liiguta Viimaseks (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Täpsem&ad Heliatribuudid\"\nBasic.MainMenu.View=\"&Vaade\"\nBasic.MainMenu.View.Toolbars=\"&Tööriistaribad\"\nBasic.MainMenu.View.ListboxToolbars=\"Doki tööriistaribad\"\nBasic.MainMenu.View.ContextBar=\"Allika tööriistariba\"\nBasic.MainMenu.View.SourceIcons=\"Allikas ja ikoonid\"\nBasic.MainMenu.View.StatusBar=\"Olekuriba (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Täisekraaniliides\"\nBasic.MainMenu.View.ResetUI=\"&Lähtesta UI\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Alati pealmine\"\nBasic.MainMenu.View.SceneListMode=\"Stseenide loendi režiim\"\nBasic.MainMenu.Docks=\"&Dokid\"\nBasic.MainMenu.Docks.ResetDocks=\"&Lähtesta Docks\"\nBasic.MainMenu.Docks.LockDocks=\"&Lukusta Docks\"\nBasic.MainMenu.Docks.SideDocks=\"Täiskõrgused dokid\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Kohandatud brauseri dokid...\"\nBasic.MainMenu.Import=\"Impordi...\"\nBasic.MainMenu.Export=\"Ekspordi...\"\nBasic.MainMenu.SceneCollection=\"&Stseeni kogumik\"\nBasic.MainMenu.Profile=\"&Profiil\"\nBasic.MainMenu.Profile.Import=\"Impordi profiil\"\nBasic.MainMenu.Profile.Export=\"Ekspordi profiil\"\nBasic.MainMenu.SceneCollection.Import=\"Impordi stseeni kollektsioon\"\nBasic.MainMenu.SceneCollection.Export=\"Ekspordi stseeni kollektsioon\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Lähtesta baasresolutsioon\"\nBasic.MainMenu.SceneCollection.Migrate=\"Määra baasresolutsioon\"\nBasic.MainMenu.Profile.Exists=\"Profiil on juba olemas\"\nBasic.MainMenu.Tools=\"&Tööriistad\"\nBasic.MainMenu.Help=\"Abi (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Abi &portaal\"\nBasic.MainMenu.Help.Website=\"Külasta Kodulehte (&W)\"\nBasic.MainMenu.Help.Discord=\"Liituge &Discord-i serveriga\"\nBasic.MainMenu.Help.WhatsNew=\"Mis on uut\"\nBasic.MainMenu.Help.Logs=\"&Logifailid\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Näita logifaile (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Lae ülesse praegune logifail (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Laadi üles &eelmine logifail\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Vaata praegust logifaili\"\nBasic.MainMenu.Help.ReleaseNotes=\"Väljalaskemärkmed\"\nBasic.MainMenu.Help.CheckForUpdates=\"Otsi värskendusi\"\nBasic.MainMenu.Help.Repair=\"Kontrolli faili terviklikkust\"\nBasic.MainMenu.Help.RestartSafeMode=\"Taaskäivita turvarežiimis\"\nBasic.MainMenu.Help.RestartNormal=\"Taaskäivita tavarežiimis\"\nBasic.MainMenu.Help.CrashLogs=\"Krahhiaruanded\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"&Kuva krahhiaruanded\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Laadi üles &eelmine krahhiaruanne\"\nBasic.MainMenu.Help.About=\"&Teave\"\nBasic.Settings.ProgramRestart=\"Nende sätete jõustumiseks tuleb taaskäivitada programm.\"\nBasic.Settings.ConfirmTitle=\"Kinnita muudatused\"\nBasic.Settings.Confirm=\"Teil on salvestamata muutusi. Salvestame muudatused?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 kontrollib mõnda teie voogedastuse seadistust\"\nBasic.Settings.General=\"Üldine\"\nBasic.Settings.General.Language=\"Keel\"\nBasic.Settings.General.Updater=\"Uuendused\"\nBasic.Settings.General.UpdateChannel=\"Uuenduskanal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Välja lülitatud)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Vaikeseade)\"\nBasic.Settings.General.EnableAutoUpdates=\"Värskenduste automaatne kontrollimine käivitamisel\"\nBasic.Settings.General.OpenStatsOnStartup=\"Käivitamisel ava statistika aken\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Peida OBS aknad ekraanijäädvustuselt\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"See seade peidab kõik OBS Studio aknad, mis ei ole projektoriga seotud, OBS-i poolt jäädvustamise eest ja mõjutab \\nmuid rakendusi, nagu konverentsid, ekraani jagamine, kaugtugi, ekraanipildid ja muu jäädvustustarkvara.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Selle valiku lubamine peidab kõik OBS Studio aknad, mis ei ole projektoriga seotud, OBS-i poolt jäädvustamise eest ja mõjutab teisi rakendusi, nagu konverentsid, ekraani jagamine, kaugtugi, ekraanipildid ja muud jäädvustustarkvarad.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Kinnituse kuvamine voogedastuse käivitamisel\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Kinnituse kuvamine voogedastuse peatamisel\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Kinnituse kuvamine salvestuse lõpetamisel\"\nBasic.Settings.General.Projectors=\"Projektorid\"\nBasic.Settings.General.HideProjectorCursor=\"Peida kursor projektorite kohale\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Hoidke projektorid alati peal\"\nBasic.Settings.General.Snapping=\"Allika joondamise klõpsimine\"\nBasic.Settings.General.ScreenSnapping=\"Tõmba allikad ekraani serva\"\nBasic.Settings.General.CenterSnapping=\"Allikate horisontaalne ja vertikaalne keskpunkt\"\nBasic.Settings.General.SourceSnapping=\"Allikate sidumine teiste allikatega\"\nBasic.Settings.General.SnapDistance=\"Plõksutundlikkus\"\nBasic.Settings.General.SpacingHelpers=\"Näita pikslite joondamisjuhiseid\"\nBasic.Settings.General.RecordWhenStreaming=\"Automaatne salvestamine voogesituse ajal\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Jätka salvestamist juhul kui voogedastus peatub\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Voogedastuse puhul võta taasesituse puhver automaatselt kasutusele\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Voogedastuse lõppedes jäta taasesituse puhver kasutusele\"\nBasic.Settings.General.SysTray=\"Süsteemi salve\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimeeri süsteemi salve käivitumisel\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Alati Minimeeri süsteemi salve käivitumisel, tegumiriba asemel\"\nBasic.Settings.General.SaveProjectors=\"Salvesta projektorid väljumisel\"\nBasic.Settings.General.CloseExistingProjectors=\"Piirang: üks täisekraaniprojektor ekraani kohta\"\nBasic.Settings.General.Preview=\"Eelvaade\"\nBasic.Settings.General.OverflowHidden=\"Peida ületäitumine\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Ülevool on alati nähtav\"\nBasic.Settings.General.OverflowSelectionHidden=\"Näita ületäitumist isegi siis, kui allikas on nähtamatu\"\nBasic.Settings.General.Importers=\"Importijad\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Importimisel otsi stseenikogude teadaolevaid asukohti\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Üleminek stseenile topeltklõpsamisel\"\nBasic.Settings.General.StudioPortraitLayout=\"Luba portree-/vertikaalne paigutus\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Näita eelvaate/programmi silte\"\nBasic.Settings.General.Multiview=\"Mitmikvaade\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Klõpsa stseenide ümbervahetamiseks\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Näita stseenide nimesid\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Ohutute alade joonistamine (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Mitmikvaate paigutus\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horisontaalne, Üleval (8 stseeni)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horisontaalne, All (8 stseeni)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertikaalne, Vasakul (8 stseeni)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertikaalne, Paremal (8 stseeni)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horisontaalne, Üleval (18 stseeni)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horisontaalne, Üleval (24 stseeni)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Ainult stseenid (4 stseeni)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Ainult stseenid (9 stseeni)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Ainult stseenid (16 stseeni)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Ainult stseenid (25 stseeni)\"\nBasic.Settings.General.ChannelName.stable=\"Stabiilne\"\nBasic.Settings.General.ChannelDescription.stable=\"Viimane stabiilne väljalase\"\nBasic.Settings.General.ChannelName.beta=\"Beetad / väljalaske kandidaadid\"\nBasic.Settings.General.ChannelDescription.beta=\"Potentsiaalselt ebastabiilsed väljalaske-eelsed versioonid\"\nBasic.Settings.Appearance=\"Välimus\"\nBasic.Settings.Appearance.General=\"Üldine\"\nBasic.Settings.Appearance.General.Theme=\"Teema\"\nBasic.Settings.Appearance.General.Variant=\"Stiil\"\nBasic.Settings.Appearance.General.NoVariant=\"Stiile pole\"\nBasic.Settings.Appearance.FontScale=\"Kirja suurus\"\nBasic.Settings.Appearance.Density=\"Tihedus\"\nBasic.Settings.Appearance.Classic=\"Klassikaline\"\nBasic.Settings.Appearance.Compact=\"Kompaktne\"\nBasic.Settings.Appearance.Normal=\"Normaalne\"\nBasic.Settings.Appearance.Comfortable=\"Mugav\"\nBasic.Settings.Appearance.OptionsWarning=\"Mõned välimuse valikud ei ole selle stiili puhul saadaval.\"\nBasic.Settings.Stream=\"Voogedastus\"\nBasic.Settings.Stream.Destination=\"Sihtkoht\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Kasuta autentimist\"\nBasic.Settings.Stream.Custom.Username=\"Kasutajanimi\"\nBasic.Settings.Stream.Custom.Password=\"Salasõna\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: sisesta srp_username.\\nRTMP: sisesta kasutajanimi.\\nSRT: ei kasutata.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: sisestage srp_password.\\nRTMP: sisestage salasõna.\\nSRT: sisestage krüpteerimise salasõna.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Ribalaiuse testrežiimi sisselülitamine\"\nBasic.Settings.Stream.TTVAddon=\"Twitchi vestluse lisandmoodulid\"\nBasic.Settings.Stream.TTVAddon.None=\"Määramata\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV ja FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Puudub voogedastuse seadistus\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Ava Sätted\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL ja voogedastuse võti on puudu.\\n\\nAva seaded, et sisestada URL ja voogedastuse võti vahekaardil \\\"Voogedastus\\\".\"\nBasic.Settings.Stream.MissingUrl=\"Voogedastuse URL on puudu.\\n\\nAva seaded, et sisestada URL vahekaardil \\\"Voogedastus\\\".\"\nBasic.Settings.Stream.MissingStreamKey=\"Voogedastuse võti on puudu.\\n\\nAva seaded, et sisestada voogedastuse võti vahekaardil \\\"Voogedastus\\\".\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignoreeri otseülekande teenuse sätete soovitusi\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Soovitatud seadete asendamine\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Hoiatus: Teenuse piirangute eiramine võib põhjustada voogedastuse kvaliteedi halvenemist või takistada voogedastust.\\n\\nJätkata?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Video maksimaalne bitikiirus: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Audio maksimaalne bitikiirus: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksimaalne eraldusvõime: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksimaalne FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Määra kohandatud server...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Kohandatud server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Luba %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Voogedastuse maksimaalne ribalaius\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automaatne\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maksimaalne videoradade arv\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automaatne\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Luba voogesituse teisendamine FLV-vormingusse (kasutab lihtsaid salvestusfaili seadeid)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Konfiguratsiooni tühistamine (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Luba konfiguratsiooni tühistamine\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Mitmekanaliline video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Lisalõuend\"\nBasic.Settings.Stream.AdvancedOptions=\"Täpsemad valikud\"\nBasic.Settings.Output=\"Väljund\"\nBasic.Settings.Output.Format=\"Salvestusvorming\"\nBasic.Settings.Output.Format.MKV=\"Matroska videoformaat (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"Hübriid MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hübriid-MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Killustatud MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"KIllustatud MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Killustatud MOV kirjutab salvestuse tükkidena ja ei nõua samasugust lõplikku töötlemist nagu traditsioonilised MOV-failid.\\nSee tagab, et fail jääb mängitavaks isegi siis, kui kirjutamine kettale katkestatakse, näiteks BSOD-i või voolukatkestuse tõttu.\\n\\nSee ei pruugi ühilduda kõigi mängijate ja redaktoritega. Vajaduse korral kasutage faili teisendamiseks ühilduvamasse vormingusse Fail → Konverteeri salvestusi.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Killustatud MP4 kirjutab salvestuse tükkidena ja ei nõua samasugust lõplikku töötlemist nagu traditsioonilised MP4 -failid.\\nSee tagab, et fail jääb mängitavaks isegi siis, kui kirjutamine kettale katkestatakse, näiteks BSOD-i või voolukatkestuse tõttu.\\n\\nSee ei pruugi ühilduda kõigi mängijate ja redaktoritega. Vajaduse korral kasutage faili teisendamiseks ühilduvamasse vormingusse Fail → Konverteeri salvestusi.\"\nBasic.Settings.Output.Encoder.Video=\"Video kodeerija\"\nBasic.Settings.Output.Encoder.Audio=\"Heli kodeerija\"\nBasic.Settings.Output.SelectDirectory=\"Vali salvestuskaust\"\nBasic.Settings.Output.DynamicBitrate=\"Muutke dünaamiliselt bitikiirust ummikute haldamiseks\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Bitikiiruse dünaamiline muutmine ummikute haldamiseks (beetaversioon)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Selle asemel, et vähendada ülekoormust, muudetakse bitikiirust dünaamiliselt.\\n\\nPane tähele, et see võib suurendada viivitust vaatajate jaoks, kui tekib märkimisväärne äkiline ülekoormus.\\nBitikiiruse languse korral võib selle taastamine võtta kuni paar minutit.\\n\\n Praegu on toetatud ainult RTMP.\"\nBasic.Settings.Output.Mode=\"Väljundrežiim\"\nBasic.Settings.Output.Mode.Simple=\"Lihtne\"\nBasic.Settings.Output.Mode.Adv=\"Täpsemad seaded\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg väljund\"\nBasic.Settings.Output.UseReplayBuffer=\"Kasuta taasesituse puhvrit\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Taasesitusaja ülempiir\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maksimaalne mälu\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Hinnanguline mälukasutus: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Hoiatus: hinnanguline mälukasutus %1 MiB on suurem kui soovitatud maksimaalne mälukasutus %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Ei saa hinnata mälukasutust. Palun määrake maksimaalne piir.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Taasesituse puhvri failinime eesliide\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"järelliide\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Taasesituse puhvrit ei saa kasutada, kui salvestuse tüübiks on määratud kohandatud väljund (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Salvestuse koht\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Salvestuse kvaliteet\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Sama nagu voogedastus\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Hea kvaliteet, keskmine failimaht\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Praktiliselt eristamatu kvaliteet, suur failimaht\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Kadudeta kvaliteet, tohutult suur failimaht\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Hoiatus: Video voogedastuse bitikiiruseks määratakse %1, mis on praeguse voogedastusteenuse ülempiir.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Hoiatus: Audio voogedastuse bitikiiruseks määratakse %1, mis on praeguse voogedastusteenuse ülempiir.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Hoiatus: Salvestusi ei saa peatada, kui salvestuskvaliteediks on seatud \\\"Sama nagu voogedastus\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Hoiatus: Praegu valitud salvestusvorming ei ühildu valitud voogesituse kodeerija(te)ga.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Hoiatus: Salvestamine tarkvara-kodeerijaga teistsuguse kvaliteediga kui voogedastus nõuab täiendavat protsessori kasutamist, kui te voogedastate ja salvestate samal ajal.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Hoiatus. Kadudeta kvaliteet tekitab tohutult suuri failimahtusid! Kadudeta kvaliteet võib kasutada kuni 7 gigabaiti kettaruumi minutis kõrge eraldusvõime ja kaadrisageduse korral. Kadudeta kvaliteet ei ole soovitatav pikkade salvestuste jaoks, kui teil ei ole väga palju kettaruumi. Kadudeta kvaliteedi kasutamisel ei ole taasesituse puhver saadaval.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Kas soovite kindlasti kasutada kadudeta kvaliteeti?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Kadudeta kvaliteedi hoiatus!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Tarkvara (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Riistvara (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Riistvara (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Riistvara (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Riistvara (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Riistvara (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Riistvara (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Riistvara (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Riistvara (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Riistvara (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Riistvara (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Tarkvara (x264 madal CPU kasutus, suurendab faili suurust)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (vaikeseade)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitchi VOD-lugu (kasutab 2. lugu)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Helirada\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Ühildumatu eraldusvõime/kaadrisagedus\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"See voogedastusteenus ei toeta teie praegust väljundresolutsiooni ja/või kaadrisagedust. Need muudetakse lähima ühilduva väärtusega:\\n\\n%1\\n\\n\\nKas soovid jätkata?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Eraldusvõime: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Ühildamatu kodeerija\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Voogedastusteenus \\\"%1\\\" ei toeta kodeerijat \\\"%2\\\". Kodeerija vahetatakse \\\"%3\\\" vastu.\\n\\nKas soovid jätkata?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Voogedastusteenus \\\"%1\\\" ei toeta kodeerijaid \\\"%2\\\" ja \\\"%3\\\". Need kodeerijad muudetakse \\\"%4\\\" ja \\\"%5\\\"-ks.\\n\\nKas soovite jätkata?\"\nBasic.Settings.Output.VideoBitrate=\"Video bitikiirus\"\nBasic.Settings.Output.AudioBitrate=\"Audio bitikiirus\"\nBasic.Settings.Output.Reconnect=\"Taasühendu automaatselt\"\nBasic.Settings.Output.RetryDelay=\"Uuesti proovimise viivitus\"\nBasic.Settings.Output.MaxRetries=\"Maksimaalne uuesti proovimiste arv\"\nBasic.Settings.Output.Advanced=\"Luba kohandatud kodeeringu seaded (Täpsemad)\"\nBasic.Settings.Output.EncoderPreset=\"Kodeerija eelseadistus\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (madal protsessorikasutus, madalaim kvaliteet)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (vaikimisi) (keskmine protsessorikasutus, standardne kvaliteet)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (kõrge protsessorikasutus, kõrgeim kvaliteet)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Kodeerija kohandatud seaded\"\nBasic.Settings.Output.CustomMuxerSettings=\"Multiplekseri kohandatud seaded\"\nBasic.Settings.Output.NoSpaceFileName=\"Ära kasuta failinimes tühikuid\"\nBasic.Settings.Output.Adv.Rescale=\"Väljundi ümberskaleerimine\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Välja lülitatud\"\nBasic.Settings.Output.Adv.AudioTrack=\"Helirada\"\nBasic.Settings.Output.Adv.Streaming=\"Voogedastus\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Voogedastuse seaded\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Rada 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Rada 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Rada 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Rada 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Rada 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Rada 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitchi VOD-lugu\"\nBasic.Settings.Output.Adv.Encoder=\"Kodeerija seaded\"\nBasic.Settings.Output.Adv.Recording=\"Salvestus\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Salvestuse seaded\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Salvestuse tüüp\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tüüp\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standartne\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Kohandatud väljund (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Kasuta voogedastuse kodeerijat)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Failinime vorming\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Kirjuta üle, kui fail on olemas\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Kohandatud väljundit (FFmpeg) ei pakuta ilma turvameetmeteta.\\nKaaluge salvestustüübi „Standard” kasutamist.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg väljundi tüüp\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Kirjuta URL-ile\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Kirjuta faili\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Levinud salvestus formaadid\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Kõik failid\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Faili rada või URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Konteineri formaat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Heli\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Vaikevorming\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Konteineri formaadi kirjeldus\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Failiteest või URL-ist arvatud Audio/Video kodeering\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Vaikekodeerija\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Lülita välja kodeerija\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Video kodeerija\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Video kodeerija sätted (kui on)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Heli kodeerija\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Heli kodeerija sätted (kui on)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Multiplekseri seaded (kui on)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Võtmekaadri intervall (kaadrit)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Näita kõiki kodeerijaid (isegi kui need on potentsiaalselt ühildumatud)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg seaded\"\nBasic.Settings.Output.EnableSplitFile=\"Faili automaatne tükeldamine\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Tükelda aja järgi\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Tükelda suuruse järgi\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Tükelda ainult käsitsi\"\nBasic.Settings.Output.SplitFile.Time=\"Tüki aeg\"\nBasic.Settings.Output.SplitFile.Size=\"Tüki suurus\"\nScreenshot=\"Ekraanipildi väljund\"\nScreenshot.SourceHotkey=\"Valitud allika ekraanipilt\"\nScreenshot.StudioProgram=\"Ekraanipilt (Programm)\"\nScreenshot.Preview=\"Ekraanipilt (Eelvaade)\"\nScreenshot.Scene=\"Ekraanipilt (Stseen)\"\nScreenshot.Source=\"Ekraanipilt (Allikas)\"\nFilenameFormatting.TT.CCYY=\"Aasta, neli numbrit\"\nFilenameFormatting.TT.YY=\"Aasta, kaks numbrit (00-99)\"\nFilenameFormatting.TT.MM=\"Kuu numbrina (01-12)\"\nFilenameFormatting.TT.DD=\"Päev kuus, lisatud 0 kui vaja (01-31)\"\nFilenameFormatting.TT.hh=\"Tund 24-tunni vormingus (00-23)\"\nFilenameFormatting.TT.mm=\"Minut (00-59)\"\nFilenameFormatting.TT.ss=\"Sekund (00-59)\"\nFilenameFormatting.TT.Percent=\"%-märk\"\nFilenameFormatting.TT.a=\"Lühendatud nädalapäeva nimi\"\nFilenameFormatting.TT.A=\"Terve nädalapäeva nimi\"\nFilenameFormatting.TT.b=\"Lühendatud kuu nimi\"\nFilenameFormatting.TT.B=\"Terve kuu nimi\"\nFilenameFormatting.TT.d=\"Kuupäev, nulliga (01-31)\"\nFilenameFormatting.TT.H=\"Tund 24-tunni vormingus (00-23)\"\nFilenameFormatting.TT.I=\"Tund 12-tunni vormingus (00-12)\"\nFilenameFormatting.TT.m=\"Kuu numbrina (01-12)\"\nFilenameFormatting.TT.M=\"Minut (00-59)\"\nFilenameFormatting.TT.p=\"AM või PM märge\"\nFilenameFormatting.TT.s=\"Aeg sekundites UNIX'i aja algusest\"\nFilenameFormatting.TT.S=\"Sekund (00-59)\"\nFilenameFormatting.TT.y=\"Aasta, viimased kaks numbrit (00-99)\"\nFilenameFormatting.TT.Y=\"Aasta\"\nFilenameFormatting.TT.z=\"ISO 8601 nihe UTC-st ajavööndis\"\nFilenameFormatting.TT.Z=\"Ajavööndi nimi või lühend\"\nFilenameFormatting.TT.FPS=\"Kaadrit sekundis\"\nFilenameFormatting.TT.CRES=\"Alus (lõuendi) resolutsioon\"\nFilenameFormatting.TT.ORES=\"Väljundi (skaleeritud) eraldusvõime\"\nFilenameFormatting.TT.VF=\"Videovorming\"\nBasic.Settings.Video.Adapter=\"Kuvaadapter\"\nBasic.Settings.Video.BaseResolution=\"Alus (lõuendi) resolutsioon\"\nBasic.Settings.Video.ScaledResolution=\"Väljundresolutsioon (skaleeritud)\"\nBasic.Settings.Video.DownscaleFilter=\"Allaskaleerimise filter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Resolutsioonid sobivad, vähendamist pole vaja]\"\nBasic.Settings.Video.FPSCommon=\"Levinud FPS-i väärtused\"\nBasic.Settings.Video.FPSInteger=\"Täisarvuline FPS-väärtus\"\nBasic.Settings.Video.FPSFraction=\"Murdosaline FPS-i väärtus\"\nBasic.Settings.Video.Numerator=\"Lugeja\"\nBasic.Settings.Video.Denominator=\"Nimetaja\"\nBasic.Settings.Video.Renderer=\"Renderdaja\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Katseversioon)\"\nBasic.Settings.Video.InvalidResolution=\"Eraldusvõime ei sobi. See peab olema kujul [width]x[height] (nt 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Videoväljund on praegu aktiivne. Videoseadete muutmiseks lülitage kõik väljundid välja.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineaarne (kiireim, aga skaleerimisel udune)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bikuubiline (teritatud skaleering, 16 sämplit)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (teritatud skaleering, 36 sämplit)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Pindala (kaalutud summa, 4/6/9 valimit)\"\nBasic.Settings.Audio=\"Heli\"\nBasic.Settings.Audio.SampleRate=\"Diskreetimissagedus\"\nBasic.Settings.Audio.Channels=\"Kanalid\"\nBasic.Settings.Audio.Meters=\"Meetrit\"\nBasic.Settings.Audio.MeterDecayRate=\"Lagunemiskiirus\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Kiire\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Keskmine (tüüp I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Aeglane (tüüp II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Tippmõõturi tüüp\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Proovi tipp\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Tõeline tippkoormus (kõrgem protsessori kasutus)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"HOIATUS: ruumiline heli on sisse lülitatud.\"\nBasic.Settings.Audio.MultichannelWarning=\"Striimimise korral kontrollige, kas teie voogedastusteenus toetab nii ruumilise heli edastamist kui ka taasesitamist. Näiteks Facebook 360 Live toetab täielikult ruumilise heli edastamist; YouTube Live toetab 5.1 heli edastamist (ja taasesitamist telerites).\\n\\nOBS-i helifiltrid ühilduvad ruumilise heliga, kuigi VST-pluginate tuge ei garanteerita.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Luba ruumiline heli?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Kas oled kindel, et soovid lubada ruumilise heli kasutamist?\"\nBasic.Settings.Audio.Devices=\"Globaalsed audioseadmed\"\nBasic.Settings.Audio.DesktopDevice=\"Töölaua heli\"\nBasic.Settings.Audio.DesktopDevice2=\"Töölaua heli 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/Lisaheli\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/Lisaheli 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/Lisaheli 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/Lisaheli 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Vaigistamise vajutamise lubamine\"\nBasic.Settings.Audio.PushToMuteDelay=\"Vaigistamiseks vajutamise viivitus\"\nBasic.Settings.Audio.EnablePushToTalk=\"Luba raadiosaatja funktsioon\"\nBasic.Settings.Audio.PushToTalkDelay=\"Push-to-talk viivitus\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Seade ühendamata või pole saadaval]\"\nBasic.Settings.Audio.Disabled=\"Välja lülitatud\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Madala viivitusega audiopuhvri režiim (Decklink/NDI väljundite jaoks)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"HOIATUS: Madala viivitusega audio puhverdamine on sisse lülitatud.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Madala viivitusega audiopuhvri režiim võib põhjustada mõne allika puhul heli katkemist või esituse peatumist.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Luba madala viivitusega audio puhverdamisrežiim?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Kas oled kindel, et soovid lubada madala viivitusega audio puhverdamise režiimi?\"\nBasic.Settings.Accessibility=\"Juurdepääsetavus\"\nBasic.Settings.Accessibility.ColorOverrides=\"Kasuta erinevaid värve\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Allika piir (valik)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Allika piir (kärbi)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Allika piir (hõljuv)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mikseri helitugevuse sagedusala (-60 to -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mikseri helitugevuse sagedusala (-20 to -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mikseri helitugevuse sagedusala (-9 to 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mikseri helitugevuse sagedusala (-60 to -20dB) (aktiivne)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mikseri helitugevuse sagedusala (-20 to -9dB) (aktiivne)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mikseri helitugevuse sagedusala (-9 to 0dB) (aktiivne)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Värvi eelseadistus\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Vaikeseade\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Kohandatud\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Värvipimeda alternatiiv\"\nBasic.Settings.Advanced=\"Täpsemad\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Väljumisel näita aktiivsete väljundite hoiatust\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Protsessi prioriteet\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Kõrge\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Normaalsest kõrgem\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normaalne\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Alla normaalse\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Tegevusetu\"\nBasic.Settings.Advanced.FormatWarning=\"Hoiatus: Muud värviformaadid peale NV12/P010 on mõeldud eelkõige salvestamiseks ja neid ei soovitata voogedastamisel kasutada. Värvivormingute teisendamise tõttu võib protsessori koormus suureneda.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Hoiatus: suure täpsusega vorminguid kasutatakse tavaliselt HDR-värviruumide puhul.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Hoiatus: Rec. 2100 peaks kasutama täpsemat formaati.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Värvi formaat\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 tasandit)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 tasandit)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 tasandit)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 tasandit)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 tasandit)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 tasandit)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 tasandit)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-bitti)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Värviruum\"\nBasic.Settings.Advanced.Video.ColorRange=\"Värviruumi ulatus\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limiteeritud\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Täielik\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR-i valge värvi tase\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR-i nominaalne tipptase\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Jälgimisseade\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Vaikeseade\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Keela Windowsi heli vaigistamine\"\nBasic.Settings.Advanced.StreamDelay=\"Voogedastuse Viivitus\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Kestus\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Säilita katkestuspunkt (suurenda viivitust uuesti ühendamisel)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Hinnanguline mälu hõivatus: %1 MB\"\nBasic.Settings.Advanced.Network=\"Võrk\"\nBasic.Settings.Advanced.Network.Disabled=\"Hetkel valitud voogedastusprotokoll ei toeta võrguseadete muutmist.\"\nBasic.Settings.Advanced.Network.BindToIP=\"IP-aadressiga sidumine\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP-perekond\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Luba võrgu optimeerimine\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Luba TCP tempo\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Püüab muuta RTMP väljundit sõbralikumaks teistele võrgu viivitusetundlikele rakendustele, reguleerides edastamiskiirust.\\nSee võib suurendada ebastabiilsete ühenduste puhul kaadrite kadumise ohtu.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Kiirklahvide fookuse käitumine\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Ärge kunagi keela kiirklahvide kasutamist\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Keela kiirklahvid, kui peaaken on fookuses\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Keela kiirklahvid, kui peaaken pole fookuses\"\nBasic.Settings.Advanced.AutoRemux=\"Konverteeri %1-ks automaatselt\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(salvesta mkv'na)\"\nBasic.AdvAudio=\"Täpsemad heliatribuudid\"\nBasic.AdvAudio.ActiveOnly=\"Ainult aktiivsed allikad\"\nBasic.AdvAudio.Name=\"Nimi\"\nBasic.AdvAudio.Volume=\"Heli\"\nBasic.AdvAudio.VolumeSource=\"Helitugevus '%1' jaoks\"\nBasic.AdvAudio.MonoSource=\"Mono allamiksimine failile '%1'\"\nBasic.AdvAudio.Balance=\"Tasakaal\"\nBasic.AdvAudio.BalanceSource=\"Saldo summale '%1'\"\nBasic.AdvAudio.SyncOffset=\"Sünkroniseerimise nihe\"\nBasic.AdvAudio.SyncOffsetSource=\"Sünkroniseerimise nihe '%1' jaoks\"\nBasic.AdvAudio.Monitoring=\"Heli jälgimine\"\nBasic.AdvAudio.Monitoring.None=\"Välja lülitatud\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Ainult monitor (vaigistatud väljund)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitor ja väljund\"\nBasic.AdvAudio.MonitoringSource=\"Heli jälgimine '%1' jaoks\"\nBasic.AdvAudio.AudioTracks=\"Rajad\"\nBasic.Settings.Hotkeys=\"Kiirklahvid\"\nBasic.Settings.Hotkeys.Pair=\"'%1'-ga jagatud klahvikombinatsioonid toimivad lülititena\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtreeri kiirklahvi järgi\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"See kiirklahv on jagatud ühe või mitme teise toiminguga, klõpsake, et näidata konflikte\"\nBasic.Settings.Hotkeys.PleaseWait=\"Kiirklahvide laadimine, palun oota...\"\nBasic.Hotkeys.SelectScene=\"Lülitu stseenile\"\nBasic.SystemTray.Show=\"Näita\"\nBasic.SystemTray.Hide=\"Peida\"\nBasic.SystemTray.Message.Reconnecting=\"Ühendus katkestatud. Uuesti ühendamine...\"\nHotkeys.Insert=\"Sisesta\"\nHotkeys.Delete=\"Kustuta\"\nHotkeys.CapsLock=\"Suurtähelukk\"\nHotkeys.Backspace=\"Tagasisammuklahv\"\nHotkeys.Print=\"Prindi\"\nHotkeys.Left=\"Vasakule\"\nHotkeys.Right=\"Paremale\"\nHotkeys.Up=\"Üles\"\nHotkeys.Down=\"Alla\"\nHotkeys.Menu=\"Menüü\"\nHotkeys.Space=\"Tühik\"\nHotkeys.NumpadNum=\"Numbriklaviatuur %1\"\nHotkeys.NumpadMultiply=\"Numbriklahvistiku korrutamine\"\nHotkeys.NumpadDivide=\"Numbriklahvistiku jagamine\"\nHotkeys.NumpadAdd=\"Numbriklahvistiku lisamine\"\nHotkeys.NumpadSubtract=\"Numbriklahvistiku lahutamine\"\nHotkeys.NumpadDecimal=\"Numbriklaviatuuri kümnendsüsteemis\"\nHotkeys.AppleKeypadEqual=\"= (Klaviatuur)\"\nHotkeys.MouseButton=\"Hiir %1\"\nMute=\"Summuta\"\nUnmute=\"Lõpeta summutamine\"\nPush-to-mute=\"Vajuta ja vaigista\"\nPush-to-talk=\"Vajuta ja räägi\"\nSceneItemShow=\"Näita „%1“\"\nSceneItemHide=\"Peida '%1'\"\nOutputWarnings.NoTracksSelected=\"Pead valima vähemalt ühe raja\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Väljundseadete viga\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Kõigil väljunditel peab olema valitud vähemalt üks heliriba.\"\nOutputWarnings.MP4Recording=\"Hoiatus: MP4/MOV-i salvestusi ei saa taastada, kui faili ei saa lõplikult vormistada (nt BSOD-i, voolukatkestuse jms tõttu). Kui soovid salvestada mitu helirada, kaalu MKV kasutamist ja salvestuse konverteerimist MP4/MOV-i pärast selle lõpetamist (Fail → Konverteeri salvestusi).\"\nOutputWarnings.CannotPause=\"Hoiatus: Salvestusi ei saa peatada, kui salvestuse kodeerija on seadistatud \\\"(Kasuta voogedastuse kodeerijat)\\\"\"\nOutputWarnings.CodecIncompatible=\"Heli- või videokooderi valik lähtestati ühildumatuse tõttu. Palun valige loendist ühilduv kodeerija.\"\nCodecCompat.Incompatible=\"(Ei ühildu %1-ga)\"\nCodecCompat.CodecPlaceholder=\"Vali kodeerija...\"\nCodecCompat.ContainerPlaceholder=\"Vali vorming...\"\nCodecCompat.CodecMissingOnExit.Title=\"Kodeerija on valimata\"\nCodecCompat.CodecMissingOnExit.Text=\"Vähemalt üks heli või video kodeerija on määramata. Palun kontrolli ei kodeerijad on määratud nii salvestuse kui voogedastuse jaoks.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Vorming on valimata\"\nCodecCompat.ContainerMissingOnExit.Text=\"Salvestusvorming on valimata. Palun vali vorming, mis ühildub valitud meediavoo kodeerijaga.\"\nFinalScene.Title=\"Kustuta stseen\"\nFinalScene.Text=\"Vähemalt üks stseen on vajalik.\"\nNoSources.Title=\"Pole Allikaid\"\nNoSources.Text=\"Paistab, et sa ei ole veel ühtegi videoallikat lisanud, nii et sa väljastad ainult tühja ekraani. Kas oled kindel, et soovid seda teha?\"\nNoSources.Text.AddSource=\"The audio or video encoder selection was reset due to incompatibility. Please select a compatible encoder from the list.\"\nNoSources.Label=\"Sul ei ole ühtegi allikat.\\nKlõpsa alloleval + nupul,\\nvõi paremklõpsa siin, et lisada üks.\"\nChangeBG=\"Vali värv\"\nCustomColor=\"Kohandatud värv\"\nBrowserSource.EnableHardwareAcceleration=\"Brauseri allika riistvarakiirenduse lubamine\"\nAbout=\"Teave\"\nAbout.Info=\"OBS Studio on tasuta ja avatud lähtekoodiga video salvestamise ja otseülekande tarkvara.\"\nAbout.Donate=\"Tee panus\"\nAbout.GetInvolved=\"Võta osa\"\nAbout.Authors=\"Autorid\"\nAbout.License=\"Litsents\"\nAbout.Error=\"Viga! Faili lugemine ei õnnestu.\\n\\nJätka siin: %1\"\nAbout.Contribute=\"Toeta OBS Projekti\"\nAddUrl.Title=\"Lisa allikas URL'i näol\"\nAddUrl.Text=\"Sa oled lohistanud URL'i OBS'i liidesesse. Sellega lisama lingi automaatselt allikaks. Kas oled nõus ja jätkame?\"\nResizeOutputSizeOfSource=\"Väljundi suuruse muutmine (allika suurus)\"\nResizeOutputSizeOfSource.Text=\"Baasi ja väljundi eraldusvõimed muutuvad vastavalt praeguse allika suurusele.\"\nResizeOutputSizeOfSource.Continue=\"Kas soovid jätkata?\"\nPreviewTransition=\"Ülemineku eelvaade\"\nImporter=\"Stseenide kogu importimine\"\nImporter.SelectCollection=\"Valige stseenide kogu\"\nImporter.Collection=\"Stseenide kollektsioon\"\nImporter.HelpText=\"Lisa sellesse aknasse faile, et importida kollektsioone OBSist või teistest toetatud programmidest.\"\nImporter.Path=\"Kogumistee\"\nImporter.Program=\"Tuvastatud rakendused\"\nImporter.AutomaticCollectionPrompt=\"Stseenikogude automaatne otsimine\"\nImporter.AutomaticCollectionText=\"OBS suudab automaatselt leida imporditavad stseenikogumid toetatud kolmandate osapoolte programmidest. Kas soovid, et OBS leiaks sinu eest automaatselt kollektsioone?\\n\\nSeda saad hiljem muuta jaotises Seaded > Üldine > Importijad.\"\nImporter.SelectFile=\"Sirvi...\"\nRestart=\"Taaskäivita\"\nNeedsRestart=\"OBS Studio vajab taaskäivitamist. Kas soovite kohe taaskäivitada?\"\nLoadProfileNeedsRestart=\"Profiil sisaldab seadeid, mis nõuavad OBS-i taaskäivitamist:\\n%1\\n\\nKas soovid OBS-i taaskäivitada, et need seaded jõustuksid?\"\nContextBar.NoSelectedSource=\"Allikat pole valitud\"\nContextBar.MediaControls.PlayMedia=\"Käivita media\"\nContextBar.MediaControls.PauseMedia=\"Peata meedia\"\nContextBar.MediaControls.StopMedia=\"Lõpeta meedia\"\nContextBar.MediaControls.RestartMedia=\"Taaskäivita meedia\"\nContextBar.MediaControls.PlaylistNext=\"Järgmine esitusloendis\"\nContextBar.MediaControls.PlaylistPrevious=\"Eelmine esitusloendis\"\nContextBar.MediaControls.BlindSeek=\"Meediaotsingu vidin\"\nYouTube.Auth.Ok=\"Autoriseerimine edukalt lõpule viidud.\\nNüüd saate selle lehe sulgeda.\"\nYouTube.Auth.NoCode=\"Autoriseerimisprotsessi ei viidud lõpule.\"\nYouTube.Auth.NoChannels=\"Valitud kontol pole kanalit saadaval\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube'i kasutaja autoriseerimine\"\nYouTube.Auth.WaitingAuth.Text=\"Palun viige autoriseerimine lõpule oma välises brauseris.<br>Kui väline brauser ei avane, klõpsake sellel lingil ja viige autoriseerimine lõpule:<br>%1\"\nYouTube.AuthError.Text=\"Kanaliteabe hankimine ebaõnnestus: %1.\"\nYouTube.Actions.WindowTitle=\"Kanaliteabe hankimine ebaõnnestus: %1.\"\nYouTube.Actions.CreateNewEvent=\"Alusta uut otseülekannet\"\nYouTube.Actions.ChooseEvent=\"Vali olemasolev otseülekannet\"\nYouTube.Actions.Title=\"Pealkiri*\"\nYouTube.Actions.MyBroadcast=\"Minu otseülekanne\"\nYouTube.Actions.Description=\"Kirjeldus\"\nYouTube.Actions.Privacy=\"Privaatsus*\"\nYouTube.Actions.Privacy.Private=\"Privaatne\"\nYouTube.Actions.Privacy.Public=\"Avalik\"\nYouTube.Actions.Privacy.Unlisted=\"Registriväline\"\nYouTube.Actions.Category=\"Kategooria\"\nYouTube.Actions.Thumbnail=\"Pisipilt\"\nYouTube.Actions.Thumbnail.SelectFile=\"Valige fail...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Faili pole valitud\"\nYouTube.Actions.Thumbnail.ClearFile=\"Kustuta\"\nYouTube.Actions.MadeForKids=\"Kas see video on mõeldud lastele?*\"\nYouTube.Actions.MadeForKids.Yes=\"Jah, See on tehtud lastele\"\nYouTube.Actions.MadeForKids.No=\"Ei, See ei ole tehtud lastele\"\nYouTube.Actions.AdditionalSettings=\"Lisaseaded\"\nYouTube.Actions.Latency=\"Latsentsus\"\nYouTube.Actions.Latency.Normal=\"Normaalne\"\nYouTube.Actions.Latency.Low=\"Madal\"\nYouTube.Actions.Latency.UltraLow=\"Ülimadal\"\nYouTube.Actions.EnableAutoStart=\"Luba automaatne-sisselülitamine\"\nYouTube.Actions.EnableAutoStop=\"Luba automaatne-väljalülitamine\"\nYouTube.Actions.AutoStartStop.TT=\"Näitab, kas see ajastatud ülekanne peaks algama automaatselt\"\nYouTube.Actions.EnableDVR=\"Luba DVR\"\nYouTube.Actions.ScheduleForLater=\"Ajasta hilisemaks\"\nYouTube.Actions.RememberSettings=\"Jäta need seaded meelde\"\nYouTube.Actions.Create_Ready=\"Loo saade\"\nYouTube.Actions.Create_GoLive=\"Loo saade ja alusta voogedastust\"\nYouTube.Actions.Choose_Ready=\"Vali saade\"\nYouTube.Actions.Choose_GoLive=\"Vali saade ja alusta voogedastust\"\nYouTube.Actions.Create_Schedule=\"Ajasta saade\"\nYouTube.Actions.Create_Schedule_Ready=\"Ajasta ja vali saade\"\nYouTube.Actions.Dashboard=\"Ava YouTube Studio\"\nYouTube.Actions.Error.Title=\"Otseülekande loomise viga\"\nYouTube.Actions.Error.Text=\"YouTube'i juurdepääsuviga \\\"%1\\\".<br/>Täpne veakirjeldus on leitav aadressil <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTube'i juurdepääsu viga. Palun kontrollige oma võrguühendust või YouTube'i serveri juurdepääsu.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Saate loomise viga \\\"%1\\\".<br/>Täpne veakirjeldus on leitav aadressil <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Otseülekannet ei loodud. Palun linkige oma konto uuesti.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube'i API viga. Lisateabe saamiseks vaadake logifaili.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Valitud saadet ei leitud.\"\nYouTube.Actions.Error.FileMissing=\"Valitud fail ei eksisteeri.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Valitud faili avamine ei õnnestunud.\"\nYouTube.Actions.Error.FileTooLarge=\"Valitud fail on liiga suur (piirang: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Saate üleminek ebaõnnestus: %1<br/><br/>Kui see viga püsib, <a href='https://studio.youtube.com/video/%2/livestreaming'>avage saate YouTube Studios</a> ja proovige käsitsi.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Saade läheb üle testimisfaasi, see võib võtta veidi aega. Palun proovige uuesti 10–30 sekundi pärast.\"\nYouTube.Actions.EventsLoading=\"Sündmuste loendi laadimine...\"\nYouTube.Actions.EventCreated.Title=\"Sündmus loodud\"\nYouTube.Actions.EventCreated.Text=\"Sündmus edukalt loodud.\"\nYouTube.Actions.Stream=\"Voogedastus\"\nYouTube.Actions.Stream.ScheduledFor=\"Ajastatud %1 jaoks\"\nYouTube.Actions.Stream.Resume=\"Jätka katkenud otseülekannet\"\nYouTube.Actions.Stream.YTStudio=\"YouTube Studio loodud automaatselt\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Uue otseülekande loomine, palun oodake...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Manuaalne käivitamine vajalik\"\nYouTube.Actions.AutoStartStreamingWarning=\"Automaatne käivitamine on selle sündmuse puhul välja lülitatud, klõpsa \\\"Käivita otseülekanne\\\", et alustada ülekannet.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Sa ei saa uuesti ühendust luua.<br>Sinu voogedastus seiskub ja sa ei ole enam otseülekandes.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Valitud YouTube'i kanalil pole otseülekanne lubatud.<br/><br/>Lisateavet leiate aadressilt <a href='https://www.youtube.com/features'>youtube.com/features</a>.\"\nYouTube.Errors.livePermissionBlocked=\"Valitud YouTube'i kanalil pole otseülekanne saadaval.<br/>Pange tähele, et pärast otseülekande lubamist kanali seadetes võib selle kättesaadavaks muutumine võtta kuni 24 tundi.<br/><br/>Lisateavet leiate aadressilt <a href='https://www.youtube.com/features'>youtube.com/features</a>.\"\nYouTube.Errors.errorExecutingTransition=\"Üleminek ebaõnnestus backend-vea tõttu. Palun proovi mõne sekundi pärast uuesti.\"\nYouTube.Errors.errorStreamInactive=\"YouTube ei saa teie otseülekande andmeid kätte. Palun kontrollige oma konfiguratsiooni ja proovige uuesti.\"\nYouTube.Errors.invalidTransition=\"Üritatud üleminek oli kehtetu. See võib olla tingitud sellest, et voog pole eelmist üleminekut lõpetanud. Palun oota paar sekundit ja proovi uuesti.\"\nYouTube.DocksRemoval.Title=\"Tühjenda YouTube'i brauseri dokid\"\nYouTube.DocksRemoval.Text=\"Need brauseri dokid eemaldatakse aegunud tugi tõttu:\\n\\n%1\\nKasutage selle asemel valikut „Docks/YouTube Live Control Room”.\"\nConfigDownload.WarningMessageTitle=\"Hoiatus\"\nFailedToStartStream.MissingConfigURL=\"Praeguse teenuse jaoks pole konfiguratsiooni URL-i saadaval\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Kohandatud RTMP URL-i pole määratud\"\nFailedToStartStream.InvalidCustomConfig=\"Kehtetu kohandatud konfiguratsioon\"\nFailedToStartStream.MissingCanvas=\"Konfigureeritud lisalõuend puudub\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Mitmekanalilise videoteenuse loomine ebaõnnestus\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Mitmekanalilise video RTMP väljundi loomine ebaõnnestus\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC pole saadaval.\\in\\Kodeerija tüübi '%1' leidmine ebaõnnestus.\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Videokooderi '%1' loomine ebaõnnestus (tüüp: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"OBS-video info hankimine kodeerija '%1' loomisel ebaõnnestus (tüüp: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Helikooderi loomine ebaõnnestus\"\nFailedToStartStream.FallbackToDefault=\"Voo käivitamine %1 abil nurjus; kas soovite uuesti proovida ühe kodeerimise sätetega?\"\nFailedToStartStream.ConfigRequestFailed=\"Konfiguratsiooni %1-lt ei õnnestunud tuua<br><br>HTTP-viga: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Saadi tundmatu olekuväärtus '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nKas soovite voogesitust jätkata ilma %1-ta?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nKas soovite voogedastust jätkata?\"\nFailedToStartStream.MissingEncoderConfigs=\"Avaldamise konfiguratsioon ei sisaldanud kodeerija konfiguratsioone\"\nFailedToStartStream.InvalidEncoderConfig=\"Avaldamise konfiguratsioon sisaldas sobimatut kodeerija konfiguratsiooni\"\nFailedToStartStream.StatusMissingHTML=\"Avaldamise taotlus tagastas määratlemata vea\"\nFailedToStartStream.NoConfigSupplied=\"Puuduv konfiguratsioon\"\nMultitrackVideo.Info=\"%1 optimeerib automaatselt teie seadeid mitme videokvaliteedi kodeerimiseks ja saatmiseks. Selle valiku valimine saadab %2 teavet teie arvuti ja tarkvara seadistuse kohta.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Ühildamatud sätted\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 ei ühildu hetkel järgmisega:\\n\\n%2\\nVoogedastuse jätkamiseks %1-ga keelake ühildumatud sätted:\\n\\n%3\\nja alustage voogedastust uuesti.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Keela selle voo jaoks ja alusta voogesitust\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Värskenda seadeid ja alusta voogedastust\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 ei ole hetkel ühilduv [Heli → Üldine → Kanalid] seadistusega '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Heli → Üldine → Kanalid] peab olema seatud väärtusele '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 nõuab [Heli → Üldine → Kanalid] jaoks mitut erinevat seadet.\"\nBasic.OpenPluginManager=\"Pluginate haldur\"\nPluginManager=\"Pluginate haldur\"\nPluginManager.Restart=\"Pluginate muudatuste rakendamiseks tuleb OBS Studio taaskäivitada\"\nPluginManager.MissingPlugin=\"[Plugini ei leitud]\"\nPluginManager.Section.Discover=\"Sirvi\"\nPluginManager.Section.Manage=\"Paigaldatud\"\nPluginManager.Section.Updates=\"Värskendused\"\nPluginManager.Section.Manage.Title=\"Halda lubatud pluginaid\"\n"
  },
  {
    "path": "frontend/data/locale/eu-ES.ini",
    "content": "Language=\"Euskara\"\nOK=\"Ados\"\nApply=\"Aplikatu\"\nCancel=\"Utzi\"\nClose=\"Itxi\"\nSave=\"Gorde\"\nDiscard=\"Baztertu\"\nDisable=\"Desgaitu\"\nYes=\"Bai\"\nNo=\"Ez\"\nAdd=\"Gehitu\"\nRemove=\"Kendu\"\nRename=\"Aldatu izena\"\nInteract=\"Elkarreragin\"\nFilters=\"Iragazkiak\"\nProperties=\"Propietateak\"\nMoveUp=\"Mugitu gora\"\nMoveDown=\"Mugitu behera\"\nSettings=\"Ezarpenak\"\nDisplay=\"Pantaila\"\nName=\"Izena\"\nExit=\"Irten\"\nMixer=\"Audio nahastailea\"\nBrowse=\"Arakatu\"\nMono=\"Monoa\"\nStereo=\"Estereoa\"\nDroppedFrames=\"Galdutako fotogramak %1 (%2%)\"\nProjector.Open.Program=\"Ireki programen proiektorea\"\nProjector.Open.Preview=\"Irekin aurrebisten proiektorea\"\nProjector.Open.Scene=\"Ireki eszenen proiektorea\"\nProjector.Open.Source=\"Irekin iturburuen proiektorea\"\nProjector.Open.Multiview=\"Ireki ikuspegi anitz\"\nProjector.Display=\"Bistaratu: %1\"\nProjector.Window=\"Leiho berria\"\nProjector.Title=\"Proiektorea\"\nProjector.Title.Scene=\"Eszena: %1\"\nProjector.Title.Source=\"Iturburua: %1\"\nProjector.Title.Multiview=\"Ikuspegi anitz\"\nProjector.ResizeWindowToContent=\"Doitu leihoa edukira\"\nClear=\"Garbitu\"\nRevert=\"Leheneratu\"\nShow=\"Erakutsi\"\nHide=\"Ezkutatu\"\nUnhideAll=\"Erakutsi dena\"\nUntitled=\"Izengabea\"\nNew=\"Berria\"\nDuplicate=\"Bikoiztu\"\nEnable=\"Gaitu\"\nDisableOSXVSync=\"Desgaitu macOS V-Sync\"\nResetOSXVSyncOnExit=\"Berrezarri macOS V-Sync-a Irtetean\"\nHighResourceUsage=\"Kodeketa gainzamatuta! Saia zaitez bideo ezarpenak arintzen edo kodetze aurrezarpen azkarragoa erabiltzen.\"\nTransition=\"Trantsizioa\"\nQuickTransitions=\"Trantsizio azkarrak\"\nFadeToBlack=\"Iraungi beltzera\"\nLeft=\"Ezkerrean\"\nRight=\"Eskuinean\"\nTop=\"Goian\"\nBottom=\"Behean\"\nReset=\"Berrezarri\"\nHours=\"ordu\"\nMinutes=\"minutu\"\nSeconds=\"segundo\"\nDeprecated=\"Zaharkitua\"\nReplayBuffer=\"Erreprodukzio bufferra\"\nImport=\"Inportatu\"\nExport=\"Esportatu\"\nCopy=\"Kopiatu\"\nPaste=\"Itsatsi\"\nPasteReference=\"Itsatsi (Erreferentzia)\"\nPasteDuplicate=\"Itsatsi (Bikoiztu)\"\nRemuxRecordings=\"Birmultiplexatu grabazioak\"\nNext=\"Hurrengoa\"\nBack=\"Atzera\"\nDefaults=\"Lehenespenak\"\nRestoreDefaults=\"Lehenespenak\"\nHideMixer=\"Ezkutatu nahasgailuan\"\nTransitionOverride=\"Trantsizio mota\"\nShowTransition=\"Erakutsi trantsizioa\"\nHideTransition=\"Ezkutatu trantsizioa\"\nNone=\"Gabe\"\nStudioMode.Preview=\"Aurrebista\"\nStudioMode.Program=\"Programa\"\nStudioMode.PreviewSceneName=\"Aurrebista: %1\"\nStudioMode.ProgramSceneName=\"Programa: %1\"\nShowInMultiview=\"Erakutsi ikuspegi anitzean\"\nVerticalLayout=\"Diseinu bertikala\"\nGroup=\"Taldea\"\nDoNotShowAgain=\"Ez erakutsi berriro\"\nDefault=\"(Lehenetsia)\"\nCalculating=\"Kalkulatzen...\"\nFullscreen=\"Pantaila osoa\"\nWindowed=\"Leihoan\"\nRefreshBrowser=\"Eguneratu\"\nAspectRatio=\"Aspektu-erlazioa <b>%1:%2</b>\"\nLockVolume=\"Blokeatu bolumena\"\nLogViewer=\"Ikusi Erregistroa\"\nShowOnStartup=\"Ireki hasterakoan\"\nOpenFile=\"Ireki fitxategia\"\nAddScene=\"Gehitu eszena\"\nAddSource=\"Iturria gehitu\"\nRemoveScene=\"Kendu aukeratutako eszenak\"\nRemoveSource=\"Kendu hautatutako iturria(k)\"\nMoveSceneUp=\"Mugitu eszena gorantz\"\nMoveSceneDown=\"Mugitu eszena beherantz\"\nMoveSourceUp=\"Mugitu iturria(k) gorantz\"\nMoveSourceDown=\"Mugitu iturria(k) beherantz\"\nSourceProperties=\"Kode irekiko propietateak\"\nSourceFilters=\"Kode irekiko iragazkiak\"\nMixerToolbarMenu=\"Audio nahasgailuaren menua\"\nSceneFilters=\"Ireki eszena-iragazkiak\"\nList=\"Zerrenda\"\nGrid=\"Sareta\"\nAutomatic=\"Automatikoa\"\nComingSoon=\"Laster gurekin\"\nPluginsFailedToLoad.Title=\"Plugin-a kargatzeko errorea\"\nPluginsFailedToLoad.Text=\"OBS plugin hauek ezin izan dira kargatu:\\n\\n%1\\nMesedez, eguneratu edo kendu plugin hauek.\"\nAlreadyRunning.Title=\"OBS dagoeneko martxan dago\"\nAlreadyRunning.Text=\"OBS dagoeneko martxan dago! Bestelakorik nahi ez baduzu Itxi irekita dagoen saioa beste saio bat ireki baino lehen. Ezarri baduzu OBS agertzea minimizatua sistemaren erretiluan begiratu eta oraindik exekutatzen ari den bertan.\"\nAlreadyRunning.LaunchAnyway=\"Abiarazi hala ere\"\nCrashHandling.Dialog.Title=\"OBS Studioren hutsegitea detektatu da\"\nCrashHandling.Labels.Text=\"OBS Studiok ez da behar bezala itzali.\\n\\nNahi duzu segurtasun moduan exekutatzea (hirugarrenen pluginak eta websocket-ak desgaituta)?\"\nCrashHandling.Labels.PrivacyNotice=\"Aukeratu dezakezu ere automatikoki kargatzea azken hutsegite-txostena OBSProject-era.<br /><br />Irakurri, mesedez, <a href='https://obsproject.com/privacy-policy'> pribatutasun gidalerroa</a> edozein fitxategia kargatu aurretik eta jarri arreta berezia fitxategien igoerei buruzko zatietan.\"\nCrashHandling.Checkbox.SendReport=\"Irakurri dut pribatutasun gidalerroa eta baimena ematen dut igotzeko.\"\nCrashHandling.Buttons.LaunchSafe=\"Exekutatu segurtasun moduan\"\nCrashHandling.Buttons.LaunchNormal=\"Exekutatu modu arruntean\"\nCrashHandling.Errors.UploadJSONError=\"Errore bat gertatu da saiatzean azken hutsegite-txostena igotzen. Saiatu beranduago.\"\nCrashHandling.Errors.Title=\"Hutsegite erregistroa igotzeko errorea\"\nSafeMode.Restart=\"OBS modu seguruan berrabiarazi nahi duzu (hirugarrenen pluginak, script-ak eta WebSockets desgaituta)?\"\nSafeMode.RestartNormal=\"Nahi duzu OBS modu normalean berrabiarazi ?\"\nChromeOS.Title=\"Euskarririk gabeko plataforma\"\nChromeOS.Text=\"Badirudi OBS ChromeOS edukiontzi baten barruan dabilela. Plataforma hau ez da onartzen.\"\nWine.Title=\"Wine detektatu da\"\nWine.Text=\"OBS Wine-n exekutatzea ez da onartzen, eta, kaptura edo gailuen iturriak bezalako eginbide askok ez dute funtzionatuko edo gaitasun mugatuan bakarrik.<br><br>OBSren jatorrizko bertsioa exekutatzea gomendatzen da, adibidez <a href='https://flathub.org/apps/details/com.obsproject.Studio'>gure Flatpak bertsioa</a> edo zure sistema eragilearen paketeak.\"\nDockCloseWarning.Title=\"Leiho moldagarria ixten\"\nDockCloseWarning.Text=\"Panel bat itxi berri duzu. Berriro bistaratu nahi baduzu, erabili menu barrako Ikusi → Panelak menua.\"\nExtraBrowsers=\"Nabigatzailearen panel pertsonalizatuak\"\nExtraBrowsers.Info=\"Gehitu panelak haien izena eta URLa emanda, orduan egin klik Aplikatu-n edo Itxi panelak irekitzeko. Edozein unean gehitu edo kendu ditzakezu panelak.\"\nExtraBrowsers.DockName=\"Atrakearen izena\"\nAuth.Authing.Title=\"Egiaztatzen...\"\nAuth.Authing.Text=\"%1(r) ekin egiaztatzen, itxaron...\"\nAuth.AuthFailure.Title=\"Autentifikazioak huts egin du\"\nAuth.AuthFailure.Text=\"Huts egin du %1:\\n\\n%2: %3 bidezko autentifikazioa\"\nAuth.InvalidScope.Title=\"Autentifikazioa derrigorrezkoa\"\nAuth.InvalidScope.Text=\"%1 autentifikatzeko eskakizunak aldatu dira. Eginbide batzuk ezingo dira erabili.\"\nAuth.LoadingChannel.Title=\"Kanalaren informazioa kargatzen...\"\nAuth.LoadingChannel.Text=\"%1 (e) tik kanalaren informazioa kargatzen, itxaron...\"\nAuth.LoadingChannel.Error=\"Ezin da kanalaren informazioa eskuratu.\"\nAuth.ChannelFailure.Title=\"Huts egin du kanala kargatzen\"\nAuth.ChannelFailure.Text=\"Huts egin du %1\\n\\n%2: %3 kanalaren informazioa kargatzen\"\nAuth.Chat=\"Txata\"\nAuth.StreamInfo=\"Transmisioaren informazioa\"\nTwitchAuth.Stats=\"Twitch-estatistikak\"\nTwitchAuth.Feed=\"Twich aktibitateen jarioa\"\nTwitchAuth.TwoFactorFail.Title=\"Ezin izan da transmisioaren gakoa galdetu\"\nTwitchAuth.TwoFactorFail.Text=\"OBS-ek ez du lortu konektatzea zure Twitch kontuarekin. Egiazta ezazu bi urratsetako identifikazioa ezarrita dagoela zure <a href='https://www.twitch.tv/settings/security'>Twitch-eko segurtasun ezarpenetan</a> transmisiorako beharrezkoa da eta.\"\nRestreamAuth.Channels=\"Restream kanalak\"\nCopy.Filters=\"Kopiatu iragazkiak\"\nPaste.Filters=\"Itsatsi iragazkiak\"\nBrowserPanelInit.Title=\"Nabigatzailea irekitzen...\"\nBrowserPanelInit.Text=\"Nabigatzailea irekitzen, itxaron...\"\nBandwidthTest.Region=\"Eskualdea\"\nBandwidthTest.Region.US=\"Estatu Batuak\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Other=\"Beste bat\"\nBasic.AutoConfig=\"Ezarpen automatikoen morroia\"\nBasic.AutoConfig.ApplySettings=\"Aplikatu ezarpenak\"\nBasic.AutoConfig.StartPage=\"Erabilera-informazioa\"\nBasic.AutoConfig.StartPage.SubTitle=\"Zehaztu zertarako erabili nahi duzun programa\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimizatu transmisiorako, grabazioa bigarren mailakoa da\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimizatu grabazioa, ez du transmitituko\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Kamera birtuala bakarrik erabiliko dut\"\nBasic.AutoConfig.VideoPage=\"Bideo-ezarpenak\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Zehaztu erabili nahi dituzun bideo-ezarpenak\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Erabili unekoa (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"%1 (%2x%3) pantaila\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Erabili unekoa (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Izan daiteke 60 edo 30, baina ahal izanez gero 60 nahiago\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Izan daiteke 60 edo 30, baina nahiago dut bereizmen altua\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Oharra: grabaziorako erabiliko duzun bereizmena ez du zertan oihalaren (oinarriaren) bereizmen berdina. Uneko transmisioaren/grabazioaren bereizmena behera eskalatu daiteke txikiagotzeko baliabideen erabilera edo bit emariaren eskakizuna.\"\nBasic.AutoConfig.StreamPage=\"Transmisioaren informazioa\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Sartu transmisioaren informazioa\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Konektatu kontua (gomendatua)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Deskonektatu kontua\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Kontua deskonektatu?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Aldaketa hau berehalakoan aplika daiteke. Ziur zaude kontua deskonektatu nahi duzula?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Lortu transmisio giltza\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Informazio Gehiago\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Erabili transmisio giltza\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Erabili transmisioaren gakoa (aurreratua)\"\nBasic.AutoConfig.StreamPage.Service=\"Zerbitzua\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Erakutsi denak...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Pertsonalizatuta...\"\nBasic.AutoConfig.StreamPage.Server=\"Zerbitzaria\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Transmisio giltza\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: sartu enkriptazio pasa-esaldia.\\nRTMP: sartu zerbitzuak emandako gakoa.\\nSRT: sartu korronte-id-a zerbitzuak erabiltzen badu.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kodetzailearen gakoa\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Eramailearen Tokena\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Lotutako kontua\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Kalkulatu bit emaria banda zabaleraren testaren bidez (minutu batzuk beharko ditu)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Hobetsi hardware-kodeketa\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Hardware-kodeketa txikiagotzen du PUZaren erabilera, baina bit emari handiagoa eskatzen du kalitate maila bera lortzeko.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Transmisioaren alarma\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Banda zabaleraren testa zure kanalera audiorik gabeko ausazko bideo datuak transmitituko ditu. Posible baduzu, komeni da behin behinean transmisioen bideoen gordeketa ezgaitzea eta transmisio modua pribatua ezartzea testa bukatu arte. Jarraitu nahi duzu?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"%1. proba\"\nBasic.AutoConfig.TestPage=\"Azken emaitzak\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Programak une honetan test sorta bat exekutatzen ari da kalkulatzeko zein diren ezarpenik egokienak\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testa osatu da\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Banda zabalerako test bat egikaritzen, minutu batzuk beharko ditu...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Ez da zerbitzu honen protokolorako emaitzarik aurkitu\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Hona konektatzen: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Huts egin du hainbat zerbitzarirekin konektatzen, probatu zure Internet konexioa eta saiatu berriro.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Banda zabaleraren proba: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Transmisioaren kodeketa probatzen, minutu batzuk beharko ditu...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Grabazioaren kodeketa probatzen, minutu batzuk beharko ditu...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Huts egin du kodetzailea abiatzen\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Probatzen %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Transmisioaren kodetzailea\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Grabazioaren kodetzailea\"\nBasic.AutoConfig.TestPage.Result.Header=\"Programaren arabera hauek dira zuretzako ezarpenik egokienak:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Ezarpen hauek onartzeko, klika Aplikatu ezarpenak. Morroiaren bidez birkonfiguratzeko eta saiatzeko berriro klika Atzera. Ezarpenak eskuz zehazteko klika Utzi eta ireki Ezarpenak.\"\nBasic.AutoConfig.Info=\"Autokonfigurazio-laguntzaileak konfiguraziorik onena zehaztuko du, bere ordenagailuaren zehaztapenen eta Interneteko abiaduraren arabera.\"\nBasic.AutoConfig.RunAnytime=\"Hori edozein unetan exekutatu daiteke, Tresnak menura joatean.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Transmisio bereizmena (eskalatua)\"\nBasic.Stats=\"Estatistikak\"\nBasic.Stats.CPUUsage=\"PUZ erabilpena\"\nBasic.Stats.HDDSpaceAvailable=\"Toki erabilgarria diskoan\"\nBasic.Stats.MemoryUsage=\"Memoria erabilpena\"\nBasic.Stats.AverageTimeToRender=\"Fotograma errendatzeko batez besteko denbora\"\nBasic.Stats.SkippedFrames=\"Kodetze atzerapena dela eta saltatutako fotogramak\"\nBasic.Stats.MissedFrames=\"Kodetze atzerapena dela eta galdutako fotogramak\"\nBasic.Stats.Output.Stream=\"Transmisioa\"\nBasic.Stats.Output.Recording=\"Grabazioa\"\nBasic.Stats.Status=\"Egoera\"\nBasic.Stats.Status.Recording=\"Grabatzen\"\nBasic.Stats.Status.Live=\"Martxan\"\nBasic.Stats.Status.Reconnecting=\"Birkonektatzen\"\nBasic.Stats.Status.Inactive=\"Inaktiboa\"\nBasic.Stats.Status.Active=\"Aktiboa\"\nBasic.Stats.DroppedFrames=\"Jaregindako fotogramak (Sarean)\"\nBasic.Stats.MegabytesSent=\"Datu irteera denetara\"\nBasic.Stats.Bitrate=\"Bit-emaria\"\nBasic.Stats.DiskFullIn=\"Disko betea (gutxi gora behera)\"\nBasic.Stats.DiskFullIn.Text=\"%1 ordu, %2 minutu\"\nBasic.Stats.ResetStats=\"Berrezarri estatistikak\"\nResetUIWarning.Title=\"Ziur zaude interfaze grafikoa berrabiarazi nahi duzula?\"\nResetUIWarning.Text=\"Interfaze grafikoa berrezartzeak ezkuta ditzake panelak. Panelak bistaratu nahi baduzu menuko Ikusi atalean agerrarazi beharko dituzu.\\n\\nZiur zaude interfaze grafikoa berrezarri nahi duzula?\"\nUpdater.Title=\"Eguneraketa berria eskuragarri\"\nUpdater.Text=\"Eguneraketa berri bat eskuragarri dago:\"\nUpdater.UpdateNow=\"Eguneratu orain\"\nUpdater.RemindMeLater=\"Gogoratu geroago\"\nUpdater.Skip=\"Baztertu bertsioa\"\nUpdater.NoUpdatesAvailable.Title=\"Ez dago eguneraketarik eskuragarri\"\nUpdater.NoUpdatesAvailable.Text=\"Une honetan ez dago eguneraketarik eskuragarri\"\nUpdater.BranchNotFound.Title=\"Eguneratu kendutako kanala\"\nUpdater.BranchNotFound.Text=\"Aukeratu duzun eguneratzeko kanala jada ez dago erabilgarri, OBS lehenespenera berrezarri da.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Osotasun-egiaztapena ez dago erabilgarri\"\nUpdater.RepairButUpdatesAvailable.Text=\"Fitxategien osotasuna egiaztatzea eskuragarri dagoen azken bertsiorako bakarrik da posible. Erabili Laguntza → Egiaztatu eguneratzeak zure OBS instalazioa egiaztatzeko eta eguneratzeko.\"\nUpdater.RepairConfirm.Title=\"Berretsi osotasuna egiaztatzea\"\nUpdater.RepairConfirm.Text=\"Osotasun egiaztapena abiarazteak zure OBS instalazioa eskaneatu egingo du galeren bila eta hautsitako/aldatutako fitxategiak berriro deskargatuko dira. Baliteke une bat behar izatea.\\n\\nJarraitu nahi duzu?\"\nUpdater.FailedToLaunch=\"Huts egin du eguneratzailea abiarazten\"\nQuickTransitions.SwapScenes=\"Txandakatu Aurrebista/Programatu eszenak trantsizioa egin ondoren\"\nQuickTransitions.SwapScenesTT=\"Txandakatu aurrebistak eta programatutako eszenak trantsizioen ondoren (baldin eta programatutako jatorrizkoa eszena badago).\\n Honek ez du desegingo programatutako jatorrizko eszenari egindako aldaketak.\"\nQuickTransitions.DuplicateScene=\"Bikoiztu eszena\"\nQuickTransitions.DuplicateSceneTT=\"Eszena bera editatzerakoan, iturburuen eraldaketa/ikusgarritasuna editatzea ahalbidetzen du programa aldatu gabe.\\nIturburuen ezaugarriak editatzeko programa aldatu gabe, gaitu 'Bikoiztu iturburuak'.\\nBalio hau aldatzeak uneko programaren eszena (oraindik badago) berrezarriko du.\"\nQuickTransitions.EditProperties=\"Bikoiztu iturburuak\"\nQuickTransitions.EditPropertiesTT=\"Eszena bera editatzerakoan, iturburuen ezaugarriak programa aldatu gabe editatzea ahalbidetzen du.\\nHau 'Bikoiztu Iturburuak' gaituta badago bakarrik erabili daiteke.\\nZenbait iturburuk (kaptura edo multimedia iturburuak) ez dute hau onartzen eta ezin dira bereizita editatu.\\nBalio hau aldatzeak uneko programaren eszena (oraindik badago) berrezarriko du.\\n\\nKontuz: Iturburuak bikoiztuko direnez, honek sistema edo bideo baliabide gehigarriak behar izan ditzake.\"\nQuickTransitions.HotkeyName=\"Trantsizio azkarra: %1\"\nBasic.AddTransition=\"Gehitu trantsizio konfiguragarria\"\nBasic.RemoveTransition=\"Kendu trantsizio konfiguragarria\"\nBasic.TransitionProperties=\"Trantsizioaren ezaugarriak\"\nBasic.SceneTransitions=\"Eszena-trantsizioak\"\nBasic.TransitionDuration=\"Iraupena\"\nBasic.TogglePreviewProgramMode=\"Estudio modua\"\nBasic.EnablePreviewProgramMode=\"Gaitu Estudio modua\"\nBasic.DisablePreviewProgramMode=\"Desgaitu Estudio modua\"\nUndo.Undo=\"Desegin\"\nUndo.Redo=\"Berregin\"\nUndo.Add=\"Gehitu '%1'\"\nUndo.Delete=\"Ezabatu '%1'\"\nUndo.Rename=\"Berrizendatu \\\"%1\\\"\"\nUndo.SceneCollection.Switch=\"Aldatu \\\"%1\\\"-ra\"\nUndo.Item.Undo=\"Desegin %1\"\nUndo.Item.Redo=\"Berregin %1\"\nUndo.Sources.Multi=\"Ezabatu %1 iturri\"\nUndo.Filters=\"'%1'-ko iragazki-aldaketak\"\nUndo.Filters.Paste.Single=\"Itsatsi '%1' iragazkia '%2'-n\"\nUndo.Filters.Paste.Multiple=\"Kopiatu iragazkiak '%1'-tik '%2'-ra\"\nUndo.Transform=\"Aldatu '%1'-ko iturria(k)\"\nUndo.Transform.Paste=\"Itsatsi Eraldaketa '%1'-n\"\nUndo.Transform.Rotate=\"'%1'-ren biraketa\"\nUndo.Transform.Reset=\"Eraldaketaren berrezarpena '%1'-n\"\nUndo.Transform.HFlip=\"'%1'-ren iraulketa horizontala\"\nUndo.Transform.VFlip=\"'%1'-ren iraulketa bertikala\"\nUndo.Transform.FitToScreen=\"'%1'-ren Doitu pantailan\"\nUndo.Transform.StretchToScreen=\"'%1'-ren Luzatu pantailan\"\nUndo.Transform.Center=\"'%1'-ren Erdiratu pantailan\"\nUndo.Transform.VCenter=\"'%1'-ren Erdiratu bertikalki pantailan\"\nUndo.Transform.HCenter=\"'%1'-ren Erdiratu horizontalki pantailan\"\nUndo.Volume.Change=\"'%1'-ren bolumenaren aldaketa\"\nUndo.Volume.Mute=\"'%1' ixilarazi\"\nUndo.Volume.Unmute=\"'%1'-ren Aktibatu soinua\"\nUndo.Balance.Change=\"'%1'-ren Audio balantzearen aldaketa\"\nUndo.SyncOffset.Change=\"'%1'-ren sinkronizazioaren desplazamenduaren aldaketa\"\nUndo.MonitoringType.Change=\"'%1'-ren audio monitorizazioaren aldaketa\"\nUndo.Mixers.Change=\"'%1'-ren audio nahasketaren aldaketa\"\nUndo.ForceMono.On=\"'%1'-ren mono behartzearen aktibazioa\"\nUndo.ForceMono.Off=\"'%1'-ren mono behartzearen desgaitzea\"\nUndo.Properties=\"'%1'-ren propietateen aldaketa\"\nUndo.Scene.Duplicate=\"'%1'-ren eszenaren bikoizketa\"\nUndo.ShowTransition=\"'%1'-ren trantsizioaren erakustea\"\nUndo.HideTransition=\"'%1'-ren trantsizioaren ezkutaketa\"\nUndo.ShowSceneItem=\"'%1' '%2'-n erakustea\"\nUndo.HideSceneItem=\"'%1' '%2'-n ezkutatzea\"\nUndo.ReorderSources=\"'%1'-ren iturrien birordenatzea\"\nUndo.MoveUp=\"'%1' '%2'-n igotzea\"\nUndo.MoveDown=\"'%1' '%2'-n jaistea\"\nUndo.MoveToTop=\"'%1' '%2'-n goraino igotzea\"\nUndo.MoveToBottom=\"'%1' '%2'-n beheraino jaistea\"\nUndo.PasteSource=\"Itsatsi iturburua(k) '%1'-en\"\nUndo.PasteSourceRef=\"Itsatsi iturburuaren erreferentzia(k) '%1'-en\"\nUndo.GroupItems=\"Talde elementuak '%1'-en\"\nTransitionNameDlg.Text=\"Sartu trantsizioaren izena\"\nTransitionNameDlg.Title=\"Trantsizioaren Izena\"\nTitleBar.SafeMode=\"MODU SEGURUA\"\nTitleBar.PortableMode=\"Modu eramangarria\"\nTitleBar.Profile=\"Profila\"\nTitleBar.Scenes=\"Eszenak\"\nNameExists.Title=\"Izena badago jadanik\"\nNameExists.Text=\"Izena dagoeneko erabilia.\"\nNoNameEntered.Title=\"Sartu baliozko izena\"\nNoNameEntered.Text=\"Ezin duzu hutsik dagoen izena erabili.\"\nConfirmStart.Title=\"Hasi transmisioa?\"\nConfirmStart.Text=\"Ziur zaude transmisioa hasi nahi duzula?\"\nConfirmStop.Title=\"Gelditu transmisioa?\"\nConfirmStop.Text=\"Ziur zaude transmisioa gelditu nahi duzula?\"\nConfirmStopRecord.Title=\"Gelditu grabazioa?\"\nConfirmStopRecord.Text=\"Ziur zaude grabazioa gelditu nahi duzula?\"\nConfirmBWTest.Title=\"Banda zabaleraren azterketa hasi?\"\nConfirmBWTest.Text=\"OBS banda zabalera azterketa moduan duzu konfiguratua. Modu honek sareko azterketak egiteko aukera ematen du zure kanala aktibatu gabe. Behin azterketa egin ondoren, desaktibatu behar duzu ikusleek zure transmisioa ikus dezaten.\\n\\nJarraitu nahi duzu?\"\nConfirmRemove.Title=\"Baieztatu kentzea\"\nConfirmRemove.Text=\"Ziur zaude '%1' kendu nahi duzula?\"\nConfirmRemove.TextMultiple=\"Seguru zaude %1 elementu ezabatu nahi dituzula?\"\nConfirmReset.Title=\"Berrezarri propietateak\"\nConfirmReset.Text=\"Ziur uneko propietateak lehentsietako balioetara berrezarri nahi dituzula?\"\nOutput.StartStreamFailed=\"Huts egin du transmisioak\"\nOutput.StartRecordingFailed=\"Huts egin du grabazioak\"\nOutput.StartReplayFailed=\"Huts egin du erreprodukzio bufferrak\"\nOutput.StartVirtualCamFailed=\"Ezin izan da abiarazi kamera birtuala\"\nOutput.StartFailedGeneric=\"Huts egin du irteeraren abioak. Begiratu erregistroa zehaztasunak ikusteko.\\n\\nOharra: NVENC edo AMD kodetzaileak erabiltzen ari bazara segurtatu haien kontrolatzaileak eguneratuta daudela.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Ezin dira errepikapenak dauden pausatuta dagoenean\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Kontuz: errepikapenak ezin dira gorde grabaketa pausatuta dagoenean.\"\nOutput.ConnectFail.Title=\"Huts egin du konektatzean\"\nOutput.ConnectFail.BadPath=\"Helburu edo konexio-URL okerra. Egiaztatu zure ezarpenak baliozkoak direla baieztatzeko.\"\nOutput.ConnectFail.ConnectFailed=\"Huts egin du zerbitzariarekin konektatzean\"\nOutput.ConnectFail.InvalidStream=\"Ezin da atzitu kanala edo transmisioaren gakoa. Egiaztatu, mesedez, birritan transmisio gakoa. Zuzena bada zerbitzariarekin konektatzen arazoa egon daiteke.\"\nOutput.ConnectFail.HdrDisabled=\"HDR emaitza desgaituta dago irteera honetarako.\"\nOutput.ConnectFail.Error=\"Ustekabeko akats bat gertatu da zerbitzariarekin konektatzen saiatzerakoan. Argibide gehiago egunkari-fitxategian.\"\nOutput.ConnectFail.Disconnected=\"Zerbitzaritik deskonektatuta.\"\nOutput.StreamEncodeError.Title=\"Kodeketa errorea\"\nOutput.StreamEncodeError.Msg=\"Kodetzailearen errore bat gertatu da transmititzean.\"\nOutput.StreamEncodeError.Msg.LastError=\"Kodetzailearen errore bat gertatu da transmititzean:<br><br>%1\"\nOutput.RecordFail.Title=\"Huts egin du grabazioa hastean\"\nOutput.RecordFail.Unsupported=\"Irteera formatua ez da onartzen edo ez du audio pista bat baino gehiago onartzen. Egiaztatu zure ezarpenak eta saiatu berriro.\"\nOutput.RecordNoSpace.Title=\"Ez dago nahiko tokirik diskoan\"\nOutput.RecordNoSpace.Msg=\"Ez dago nahikoa tokirik diskoan grabatzen jarraitzeko.\"\nOutput.RecordError.Title=\"Grabazio akatsa\"\nOutput.RecordError.Msg=\"Zehaztugabeko akats bat gertatu da grabatzerakoan.\"\nOutput.RecordError.EncodeErrorMsg=\"Kodetzailearen errore bat gertatu da grabatzean.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Kodetzailearen errore bat gertatu da grabatzean:<br><br>%1\"\nOutput.BadPath.Title=\"Fitxategi-bide okerra\"\nOutput.BadPath.Text=\"Ezin izan da ireki konfiguratutako grabaketa-bidea. Mesedez, egiaztatu zure Grabaketa-bidea Ezarpenak → Irteera → Grabaketa atalean.\"\nOutput.NoBroadcast.Title=\"Ez dago emankizunik konfiguratuta\"\nOutput.NoBroadcast.Text=\"Emankizun bat konfiguratu behar duzu transmisio bat hasi ahal izateko.\"\nOutput.BroadcastStartFailed=\"Huts egin du emankizuna abiatzen\"\nOutput.BroadcastStopFailed=\"Huts egin du emankizuna gelditzen\"\nLogUploadDialog.Title=\"OBS Studio erregistro fitxategiaren igoera\"\nLogUploadDialog.Labels.PrivacyNotice=\"Mesedez, irakurri <a href='https://obsproject.com/privacy-policy'> pribatutasun gidalerroa</a> eta edozein fitxategi igo aurretik fitxategiak igotzeari buruzko atala.\"\nLogUploadDialog.Labels.Progress=\"Erregistroa igotzen. Itxaron...\"\nLogUploadDialog.Labels.Description.AppLog=\"Zure erregistro fitxategia igo da. Orain URLa partekatu dezakezu arazketa- edo euskarri-helburuetarako.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Hutsegite txostena igo da. Orain URLa parteka dezakezu arazketa proposatzeko.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Igo\"\nLogUploadDialog.Buttons.CopyURL=\"Kopiatu erregistroaren URLa\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Aztertu erregistro fitxategia\"\nLogUploadDialog.Buttons.RetryButton=\"Saiatu berriro\"\nLogUploadDialog.Errors.Template=\"Errore bat gertatu da fitxategia igotzean:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Ez dago igotzeko fitxategirik edo hutsik dago.\"\nRemux.SourceFile=\"OBS grabazioa\"\nRemux.TargetFile=\"Helburu-fitxategia\"\nRemux.Remux=\"Multiplexatu\"\nRemux.Stop=\"Gelditu bihurtzea\"\nRemux.ClearFinished=\"Garbitu bukatutako elementuak\"\nRemux.ClearAll=\"Garbitu elementu guztiak\"\nRemux.OBSRecording=\"OBS grabazioa\"\nRemux.FinishedTitle=\"Bihurketa amaituta\"\nRemux.Finished=\"Grabazioa bihurtuta\"\nRemux.FinishedError=\"Grabazioa bihurtua, baina fitxategia osatu gabe egon daiteke\"\nRemux.SelectRecording=\"OBS grabazio modua aukeratu...\"\nRemux.SelectTarget=\"Helburu-fitxategia aukeratu...\"\nRemux.FileExistsTitle=\"Helburu-fitxategiak badaude\"\nRemux.FileExists=\"Dagoeneko ondorengo helburu-fitxategiak existitzen dira. Ordezkatu nahi dituzu?\"\nRemux.ExitUnfinishedTitle=\"Multiplexazioa martxan\"\nRemux.ExitUnfinished=\"Bihurketa ez da amaitu, orain gelditzeak fitxategi-helburua erabiltezin bihur dezake.\\nZiur zaude bihurtzea gelditu nahi duzula?\"\nRemux.HelpText=\"Jaregin itzazu fitxategiak leiho honetan aldatzeko, edo hauta ezazu \\\"OBS Grabazioa\\\"-gelaxka huts bat fitxategia arakatzeko.\"\nRemux.NoFilesAddedTitle=\"Ez da remux fitxategirik gehitu\"\nRemux.NoFilesAdded=\"Ez da fitxategirik gehitu remux-era. Jaregin bideo fitxategi bat edo gehiago dituen karpeta bat.\"\nMissingFiles=\"Galdutako fitxategiak\"\nMissingFiles.MissingFile=\"Galdutako fitxategia\"\nMissingFiles.NewFile=\"Fitxategi berria\"\nMissingFiles.HelpText=\"Fitxategi batzuk falta dira OBS azken aldiz erabili zenuenetik.\"\nMissingFiles.Clear=\"<ezabatua>\"\nMissingFiles.NumFound=\"%2-tik %1 aurkitu dira\"\nMissingFiles.Search=\"Bilatu direktorioa...\"\nMissingFiles.SelectFile=\"Hautatu fitxategia...\"\nMissingFiles.SelectDir=\"Hautatu zein karpetan bilatu behar den\"\nMissingFiles.State=\"Egoera\"\nMissingFiles.Missing=\"Falta da\"\nMissingFiles.Replaced=\"Ordeztu da\"\nMissingFiles.Cleared=\"Garbitu da\"\nMissingFiles.Found=\"Aurkitu da\"\nMissingFiles.AutoSearch=\"Aurkitutako fitxategi partida osagarriak\"\nMissingFiles.AutoSearchText=\"OBS-ek direktorio honetan falta diren fitxategiekin bat egiten duten osagarriak topatu ditu. Nahi al duzu gehitzea?\"\nMissingFiles.NoMissing.Title=\"Falta diren fitxategiak egiaztatzea\"\nMissingFiles.NoMissing.Text=\"Ez dirudi fitxategirik falta denik.\"\nMacPermissions.MenuAction=\"Berrikusi aplikazioaren baimenak...\"\nMacPermissions.Title=\"Berrikusi aplikazioaren baimenak\"\nMacPermissions.Description=\"OBS Studio-k zure baimena behar du eginbide batzuk eman ahal izateko. Baimen hauek ematea gomendatzen da, baina ez dira beharrezkoak aplikazioa erabiltzeko. Geroago gaitu ditzakezu beti.\"\nMacPermissions.Description.OpenDialog=\"Elkarrizketa hau berriro ireki dezakezu OBS Studio menuaren bidez.\"\nMacPermissions.AccessGranted=\"Sarbidea onartuta\"\nMacPermissions.RequestAccess=\"Gunean sartzeko eskaera:\"\nMacPermissions.OpenPreferences=\"Zabaldu %1 preferentziak\"\nMacPermissions.Item.ScreenRecording=\"Pantaila grabatzea\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS-k baimen hau behar du zure pantaila grabatu ahal izateko.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Baimen hau beharrezkoa da webkamera edo bideo txarteleko edukia hartzeko.\"\nMacPermissions.Item.Microphone=\"Microfono-a\"\nMacPermissions.Item.Microphone.Details=\"OBS-k baimen hau behar du zure mikrofonoa edo kanpoko audio gailu bat hartzea nahi baduzu.\"\nMacPermissions.Item.InputMonitoring=\"Sarreren monitorizazioa\"\nMacPermissions.Item.InputMonitoring.Details=\"Baimen hau beharrezkoa da laster-teklek funtziona dezaten OBS atzeko planoan dagoen bitartean.\"\nMacPermissions.Item.Accessibility=\"Irisgarritasuna\"\nMacPermissions.Item.Accessibility.Details=\"Instalazio zaharretan, OBS \\\"Erabilerraztasuna\\\" atalean ager daiteke, \\\"Sarreren monitorizazioa\\\"-ren ordez.\"\nMacPermissions.Continue=\"Jarraitu\"\nSourceLeak.Title=\"Iturria garbitzeko errorea\"\nSourceLeak.Text=\"Arazo bat izan da eszena bildumak aldatzean eta iturri batzuk ezin izan dira deskargatu. Arazo hau normalean baliabideak behar bezala askatzen ez dituzten pluginek eragiten dute. Mesedez, ziurtatu erabiltzen ari zaren pluginak eguneratuta daudela.\\n\\nOBS Studio orain itxiko da, datuak hondatzea saihesteko.\"\nBasic.DesktopDevice1=\"Mahaigaineko audioa\"\nBasic.DesktopDevice2=\"Mahaigaineko audioa 2\"\nBasic.AuxDevice1=\"Mikro/Osag\"\nBasic.AuxDevice2=\"Mikro/Osag 2\"\nBasic.AuxDevice3=\"Mikro/Osag 3\"\nBasic.AuxDevice4=\"Mikro/Osag 4\"\nBasic.Scene=\"Eszena\"\nBasic.DisplayCapture=\"Pantaila-kaptura\"\nBasic.Main.PreviewConextMenu.Enable=\"Gaitu aurrebista\"\nBasic.Main.Preview.Disable=\"Desgaitu aurrebista\"\nScaleFiltering=\"Eskalaren iragazketa\"\nScaleFiltering.Point=\"Puntua\"\nScaleFiltering.Bilinear=\"Bilineala\"\nScaleFiltering.Bicubic=\"Bikubikoa\"\nBlendingMethod=\"Nahasi metodoa\"\nBlendingMethod.Default=\"Lehenetsi\"\nBlendingMethod.SrgbOff=\"Itzali SRGB\"\nBlendingMode=\"Nahasketa modua\"\nBlendingMode.Normal=\"Normala\"\nBlendingMode.Additive=\"Gehitu\"\nBlendingMode.Subtract=\"Kenketa\"\nBlendingMode.Screen=\"Pantaila\"\nBlendingMode.Multiply=\"Biderkatu\"\nBlendingMode.Lighten=\"Argitu\"\nBlendingMode.Darken=\"Ilundu\"\nDeinterlacing=\"Desgurutzelarkatzea\"\nDeinterlacing.Discard=\"Baztertu\"\nDeinterlacing.TopFieldFirst=\"Goiko eremua lehenik\"\nDeinterlacing.BottomFieldFirst=\"Beheko eremua lehenik\"\nVolControl.SliderUnmuted=\"Bolumen graduatzailea '%1'-rentzat:\"\nVolControl.SliderMuted=\"Bolumen graduatzailea '%1'-rentzat: (une honetan mutu)\"\nVolControl.Mute=\"Mututu '%1'\"\nVolControl.Properties=\"'%1'-ren ezaugarriak\"\nVolControl.UnassignedWarning.Title=\"Esleitu gabeko audio-iturria\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" ez dago inongo audio-pistari esleituta eta ez da entzungo transmisioetan edo grabaketetan.\\n\\nPista bati audio-iturri bat esleitzeko, ireki Audio-Propietate aurreratuak eskuineko botoiaren menuan edo engranaje-botoiaren bidez audio-nahasgailuaren tresna-barran.\"\nBasic.Main.AddSceneDlg.Title=\"Gehitu eszena\"\nBasic.Main.AddSceneDlg.Text=\"Sartu eszenaren izena\"\nBasic.Main.DefaultSceneName.Text=\"%1 eszena\"\nBasic.Main.AddSceneCollection.Title=\"Gehitu eszena-bilduma\"\nBasic.Main.AddSceneCollection.Text=\"Sartu eszena-bildumaren izena\"\nBasic.Main.RenameSceneCollection.Title=\"Berrizendatu eszena-bilduma\"\nBasic.Main.RemigrateSceneCollection.Title=\"Eguneratu eszena bildumaren bereizmena\"\nBasic.Main.RemigrateSceneCollection.Text=\"\\\"%1\\\" eszena bildumaren bereizmena eguneratu nahi duzu uneko profilaren %2x%3 oihal bereizmenarekin bat etor dadin?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Ezin da eszena bildumaren bereizmena eguneratu irteerak aktibatuta dauden bitartean.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Ezin izan da eguneratu eszena bildumaren bereizmena. Jatorrizko bereizmena ezezaguna da.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Ezin izan da berrezarri: OBS bereizmena aldatzeak huts egin du.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Ezin izan da berrezarri: uneko bereizmena dagoeneko eszena bildumaren oinarrizko bereizmena da.\"\nAddProfile.Title=\"Gehitu profila\"\nAddProfile.Text=\"Sartu profilaren izena\"\nAddProfile.WizardCheckbox=\"Ezarpen automatikoen laguntzailea\"\nRenameProfile.Title=\"Aldatu izena profilari\"\nBasic.Main.MixerRename.Title=\"Berrizendatu audio iturburua\"\nBasic.Main.MixerRename.Text=\"Sartu audio iturburuaren izena\"\nBasic.Main.PreviewDisabled=\"Aurrebista ezgaituta dago\"\nBasic.SourceSelect=\"Sortu/Hautatu Iturburua\"\nBasic.SourceSelect.CreateNew=\"Sortu berria\"\nBasic.SourceSelect.AddExisting=\"Gehitu lehendik dagoena\"\nBasic.SourceSelect.AddVisible=\"Egin iturburua ikusgarri\"\nBasic.Main.Sources.Visibility=\"Ikusgaitasuna\"\nBasic.Main.Sources.VisibilityDescription=\"Kontrolatzen du '%1' (e)k oihalean duen ikusgaitasuna\"\nBasic.Main.Sources.Lock=\"Blokeatu\"\nBasic.Main.Sources.LockDescription=\"Blokeatzen du '%1'(e)k oihalean duen kokapena eta eskala\"\nBasic.PropertiesWindow=\"'%1'-ren ezaugarriak\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (berez-hautatu: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Hautatu kolorea\"\nBasic.PropertiesWindow.SelectFont=\"Hautatu letra-mota\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Aukeratu letra-tipo bat\"\nBasic.PropertiesWindow.ConfirmTitle=\"Ezarpenak aldatuta\"\nBasic.PropertiesWindow.Confirm=\"Gorde gabeko aldaketak daude. Nahi duzu dagoena mantentzea?\"\nBasic.PropertiesWindow.NoProperties=\"Ezaugarriak ez daude eskuragarri\"\nBasic.PropertiesWindow.AddFiles=\"Gehitu fitxategiak\"\nBasic.PropertiesWindow.AddDir=\"Gehitu direktorioa\"\nBasic.PropertiesWindow.AddURL=\"Gehitu bidea/URL-a\"\nBasic.PropertiesWindow.AddEditableListDir=\"Gehitu direktorioa '%1'-ra\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Gehitu fitxategiak '%1'-ra\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Gehitu sarrera '%1'-ra\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Editatu sarrera '%1'-tik\"\nBasic.PropertiesView.FPS.Simple=\"FPS-ko balio arruntak\"\nBasic.PropertiesView.FPS.Rational=\"FPS-ko balio arrazionalak\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Baliozko FPS-ko tarteak:\"\nBasic.PropertiesView.UrlButton.Text=\"Ireki esteka hau zure nabigatzaile lehenetsian?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"URLa: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Ireki URL-a\"\nBasic.InteractionWindow=\"Elkarrekintza '%1'-rekin\"\nBasic.StatusBar.Reconnecting=\"Konexiorik gabe, %2 segundo barru birkonektatzen (saiakera %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Birkonektatzen saiatzen... (saiakera %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Birkonexioa ongi burutu da\"\nBasic.StatusBar.Delay=\"Atzerapena (%1 seg)\"\nBasic.StatusBar.DelayStartingIn=\"Atzerapena (hasiera %1 seg-tan)\"\nBasic.StatusBar.DelayStoppingIn=\"Atzerapena (gelditzea %1 seg-tan)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Atzerapena (gelditzea %1 seg-tan, hasiera %2 seg-tan)\"\nBasic.StatusBar.RecordingSavedTo=\"Grabazioa '%1'-n gorde da\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Erreproduzitu '%1'-n gordetako bufferra\"\nBasic.StatusBar.ScreenshotSavedTo=\"Pantaila-argazkia '%1'-n gorde da\"\nBasic.StatusBar.AutoRemuxedTo=\"Auto bihurketa '%1'-n grabatzen\"\nBasic.Filters=\"Iragazkiak\"\nBasic.Filters.AsyncFilters=\"Audio/Bideo iragazkiak\"\nBasic.Filters.AudioFilters=\"Audio iragazkiak\"\nBasic.Filters.EffectFilters=\"Efektu Iragazkiak\"\nBasic.Filters.Title=\"Iragazkiak '%1'-rako\"\nBasic.Filters.AddFilter.Title=\"Iragazkiaren izena\"\nBasic.Filters.AddFilter.Text=\"Adierazi iragazkiaren izena\"\nBasic.TransformWindow=\"Eszenaren elementuaren eraldaketa\"\nBasic.TransformWindow.Position=\"Kokapena\"\nBasic.TransformWindow.PositionX=\"X kokapena\"\nBasic.TransformWindow.PositionY=\"Y kokapena\"\nBasic.TransformWindow.Rotation=\"Biraketa\"\nBasic.TransformWindow.Size=\"Tamaina\"\nBasic.TransformWindow.Width=\"Zabalera\"\nBasic.TransformWindow.Height=\"Altuera\"\nBasic.TransformWindow.Alignment=\"Kokapen lerrokatzea\"\nBasic.TransformWindow.BoundsType=\"Muga-koadro mota\"\nBasic.TransformWindow.BoundsAlignment=\"Lerrokatu muga-koadroan\"\nBasic.TransformWindow.Bounds=\"Muga-koadroaren tamaina\"\nBasic.TransformWindow.BoundsWidth=\"Mugatzeko kutxaren zabalera\"\nBasic.TransformWindow.BoundsHeight=\"Mugatzeko kutxaren altuera\"\nBasic.TransformWindow.CropToBounds=\"Moztu muga-koadrora\"\nBasic.TransformWindow.Crop=\"Moztu\"\nBasic.TransformWindow.CropLeft=\"Moztu ezkerretik\"\nBasic.TransformWindow.CropRight=\"Moztu eskuinetik\"\nBasic.TransformWindow.CropTop=\"Moztu goitik\"\nBasic.TransformWindow.CropBottom=\"Moztu behetik\"\nBasic.TransformWindow.Alignment.TopLeft=\"Goian ezkerrean\"\nBasic.TransformWindow.Alignment.TopCenter=\"Goian erdian\"\nBasic.TransformWindow.Alignment.TopRight=\"Goian eskuinean\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Erdian ezkerrean\"\nBasic.TransformWindow.Alignment.Center=\"Erdian\"\nBasic.TransformWindow.Alignment.CenterRight=\"Erdian eskuinean\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Behean ezkerrean\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Behean erdian\"\nBasic.TransformWindow.Alignment.BottomRight=\"Behean eskuinean\"\nBasic.TransformWindow.BoundsType.None=\"Mugarik ez\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Gehienezko tamaina bakarrik\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Eskalatu barruko mugetara\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Eskalatu kanpoko mugetara\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Eskalatu muga zabalerara\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Eskalatu altuera-mugetara\"\nBasic.TransformWindow.BoundsType.Stretch=\"Luzatu mugetara\"\nBasic.TransformWindow.Title=\"Editatu '%1'ren eraldaketa\"\nBasic.TransformWindow.NoSelectedSource=\"Ez da iturririk hautatu\"\nBasic.Main.AddSourceHelp.Title=\"Ezin da iturbururik gehitu\"\nBasic.Main.AddSourceHelp.Text=\"Gutxienez eszena bat eduki behar duzu iturburu bat gehitzeko.\"\nBasic.Main.Scenes=\"Eszenak\"\nBasic.Main.Sources=\"Iturburuak\"\nBasic.Main.Source=\"Iturburua\"\nBasic.Main.Controls=\"Kontrolak\"\nBasic.Main.PreparingStream=\"Prestatzen...\"\nBasic.Main.Connecting=\"Konektatzen...\"\nBasic.Main.StartRecording=\"Hasi grabazioa\"\nBasic.Main.StartReplayBuffer=\"Abiatu erreprodukzio bufferra\"\nBasic.Main.SaveReplay=\"Gorde erreprodukzioa\"\nBasic.Main.StartStreaming=\"Hasi transmisioa\"\nBasic.Main.StartBroadcast=\"Zuzeneko transmisioa\"\nBasic.Main.StartVirtualCam=\"Hasi Kamera Birtuala\"\nBasic.Main.StopRecording=\"Gelditu grabazioa\"\nBasic.Main.PauseRecording=\"Pausatu grabazioa\"\nBasic.Main.UnpauseRecording=\"Amaitu grabazioaren pausa\"\nBasic.Main.SplitFile=\"Zatitu grabaketa fitxategia\"\nBasic.Main.AddChapterMarker=\"Gehitu kapitulu-markatzailea (bakarrik Hybrid MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Grabazioa gelditzen...\"\nBasic.Main.StopReplayBuffer=\"Gelditu erreprodukzio bufferra\"\nBasic.Main.StoppingReplayBuffer=\"Erreprodukzio bufferra gelditzen...\"\nBasic.Main.SetupBroadcast=\"Kudeatu emankizuna\"\nBasic.Main.StopStreaming=\"Gelditu transmisioa\"\nBasic.Main.StopBroadcast=\"Bukatu emankizuna\"\nBasic.Main.AutoStopEnabled=\"(Gelditze automatikoa)\"\nBasic.Main.StoppingStreaming=\"Transmisioa gelditzen...\"\nBasic.Main.ForceStopStreaming=\"Gelditu transmisioa (baztertu atzerapena)\"\nBasic.Main.ShowContextBar=\"Erakutsi iturburuaren tresna-barra\"\nBasic.Main.HideContextBar=\"Ezkutatu iturburuaren tresna-barra\"\nBasic.Main.StopVirtualCam=\"Amaitu Kamera Birtuala\"\nBasic.Main.Group=\"%1 taldea\"\nBasic.Main.GroupItems=\"Batu hautatutako elementuak\"\nBasic.Main.Ungroup=\"Banatu\"\nBasic.Main.GridMode=\"Sareta modua\"\nBasic.Main.ListMode=\"Zerrenda modua\"\nBasic.Main.VirtualCamConfig=\"Konfiguratu kamera birtuala\"\nBasic.VCam.VirtualCamera=\"Kamera birtuala\"\nBasic.VCam.OutputType=\"Irteera mota\"\nBasic.VCam.OutputSelection=\"Irteera hautapena\"\nBasic.VCam.OutputType.Program=\"Programa (lehenetsia)\"\nBasic.VCam.OutputSelection.NoSelection=\"Ez dago aukerarik hautatu irteera mota honetarako\"\nBasic.VCam.RestartWarning=\"Kamera birtuala berrabiaraziko da aldaketa hau aplikatzeko\"\nBasic.MainMenu.File=\"&Fitxategia\"\nBasic.MainMenu.File.Export=\"&Esportatu\"\nBasic.MainMenu.File.Import=\"&Inportatu\"\nBasic.MainMenu.File.ShowRecordings=\"E&rakutsi grabazioak\"\nBasic.MainMenu.File.Remux=\"&Multiplexatu grabazioak\"\nBasic.MainMenu.File.Settings=\"Ezarpenak (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Erakutsi ezarpenen karpeta\"\nBasic.MainMenu.File.ShowProfileFolder=\"Erakutsi profilaren karpeta\"\nBasic.MainMenu.File.ShowMissingFiles=\"Egiaztatu falta diren fitxategiak\"\nBasic.MainMenu.File.Exit=\"Irten (&X)\"\nBasic.MainMenu.Edit=\"&Editatu\"\nBasic.MainMenu.Edit.Undo=\"Desegin (&U)\"\nBasic.MainMenu.Edit.Redo=\"Be&rregin\"\nBasic.MainMenu.Edit.LockPreview=\"B&lokeatu aurrebista\"\nBasic.MainMenu.Edit.Scale=\"Aurrebi&staren eskala\"\nBasic.MainMenu.Edit.Scale.Window=\"Eskalatu leihora\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Oihala (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Irteera (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Eskalatua (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Handiagotu\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Txikiagotu\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Berrezarri zooma\"\nBasic.MainMenu.Edit.Transform=\"Eralda&tu\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Editatu eraldaketa...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopiatu eraldaketa\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Itsatsi eraldaketa\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Be&rrezarri eraldaketa\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Biratu 90 gradu erlojuaren norabidean\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Biratu 90 gradu erlojuaren kontrako norabidean\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Biratu 180 gradu\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Irauli &horizontala\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Irauli bertikala (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Doitu pantailara (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Luzatu pantailara (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Erdiratu pantailan (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Erdiratu bertikalki\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Erdiratu horizontalki\"\nBasic.MainMenu.Edit.Order=\"&Ordenatu\"\nBasic.MainMenu.Edit.Order.MoveUp=\"M&ugitu gora\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Mugitu behera (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Mugi&tu goraino\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Mugitu &beheraino\"\nBasic.MainMenu.Edit.AdvAudio=\"&Audio ezarpen aurreratuak\"\nBasic.MainMenu.View=\"Ikusi (&V)\"\nBasic.MainMenu.View.Toolbars=\"&Tresna-barrak\"\nBasic.MainMenu.View.ListboxToolbars=\"Atrakea tresna-barrak\"\nBasic.MainMenu.View.ContextBar=\"iturburuaren tresna-barra\"\nBasic.MainMenu.View.SourceIcons=\"Iturburuen &ikonoak\"\nBasic.MainMenu.View.StatusBar=\"Egoera-barra (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Pantaila osoa\"\nBasic.MainMenu.View.ResetUI=\"&Berezarri erabiltzaile-interfazea\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Beti gainean\"\nBasic.MainMenu.View.SceneListMode=\"Eszena zerrenda modua\"\nBasic.MainMenu.Docks=\"&Panel moldagarriak\"\nBasic.MainMenu.Docks.ResetDocks=\"&Berrezarri panelak\"\nBasic.MainMenu.Docks.LockDocks=\"&Blokeatu panelak\"\nBasic.MainMenu.Docks.SideDocks=\"&Altuera osoko panelak\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Nabigatzailearen panel pertsonalizatuak...\"\nBasic.MainMenu.Import=\"Inportatu...\"\nBasic.MainMenu.Export=\"Esportatu...\"\nBasic.MainMenu.SceneCollection=\"E&szena-bilduma\"\nBasic.MainMenu.Profile=\"&Profila\"\nBasic.MainMenu.Profile.Import=\"Inportatu profila\"\nBasic.MainMenu.Profile.Export=\"Esportatu profila\"\nBasic.MainMenu.SceneCollection.Import=\"Inportatu eszena bilduma\"\nBasic.MainMenu.SceneCollection.Export=\"Esportatu eszena bilduma\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Berrezarri oinarrizko bereizmena\"\nBasic.MainMenu.SceneCollection.Migrate=\"Ezarri oinarrizko bereizmena\"\nBasic.MainMenu.Profile.Exists=\"Profila lehendik ere badago\"\nBasic.MainMenu.Tools=\"&Tresnak\"\nBasic.MainMenu.Help=\"Laguntza (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Laguntza ataria (&P)\"\nBasic.MainMenu.Help.Website=\"Ikusi &webgunea\"\nBasic.MainMenu.Help.Discord=\"Bat egin &Discord zerbitzariarekin\"\nBasic.MainMenu.Help.WhatsNew=\"Zer dago berria\"\nBasic.MainMenu.Help.Logs=\"Egunkari-fitxategiak (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Erakut&si egunkari-fitxategiak\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Kargatu uneko egunkari-fitxategiak (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Igo &Aurreko Log fitxategia\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Ikusi uneko egunkari-fitxategia (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Argitalpen-oharrak\"\nBasic.MainMenu.Help.CheckForUpdates=\"Begiratu eguneraketak\"\nBasic.MainMenu.Help.Repair=\"Egiaztatu fitxategiaren osotasuna\"\nBasic.MainMenu.Help.RestartSafeMode=\"Berrabiarazi modu seguruan\"\nBasic.MainMenu.Help.RestartNormal=\"Berrabiarazi modu normalean\"\nBasic.MainMenu.Help.CrashLogs=\"Matxuren jakinarazpenak (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Erakut&si matxuren jakinarazpenak\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Igo &Aurreko matxura jakinarazpena\"\nBasic.MainMenu.Help.About=\"Honi buruz (&A)\"\nBasic.Settings.ProgramRestart=\"Programa berrabiarazi egin behar da ezarpen hauek eragina izateko.\"\nBasic.Settings.ConfirmTitle=\"Baieztatu aldaketak\"\nBasic.Settings.Confirm=\"Gordegabeko aldaketak dituzu. Gorde aldaketak?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 zure transmisio-ezarpenetako batzuk kontrolatzen ari da\"\nBasic.Settings.General=\"Orokorra\"\nBasic.Settings.General.Language=\"Hizkuntza\"\nBasic.Settings.General.Updater=\"Eguneraketak\"\nBasic.Settings.General.UpdateChannel=\"Eguneratu kanala\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Desgaituta)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Lehenetsia)\"\nBasic.Settings.General.EnableAutoUpdates=\"Abiaraztean begiratu automatikoki eguneraketarik ba ote dagoen\"\nBasic.Settings.General.OpenStatsOnStartup=\"Ireki estatistikak abiatzean\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Ezkutatu OBS leihoa pantaila kapturan\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Ezarpen honek ezkutatu egingo ditu OBS Studio-ko leiho guztiak OBS-ko kapturetan eta beste aplikazio\\nbatzuetan eragina izango du, hala nola, hitzaldiak, pantaila partekatzea, urruneko laguntza, pantaila-argazkiak eta kapturatzeko beste software batzuk.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Ezarpen honek ezkutatu egingo ditu OBS Studio-ko leiho guztiak OBS-ko kapturetan eta beste aplikazio batzuetan eragina izango du, hala nola, hitzaldiak, pantaila partekatzea, urruneko laguntza, pantaila-argazkiak eta kapturatzeko beste software batzuk.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Erakutsi berrespen elkarrizketa transmisioak hasterakoan\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Erakutsi berrespen elkarrizketa transmisioak gelditzerakoan\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Erakutsi berrespen elkarrizketa grabazioa gelditzerakoan\"\nBasic.Settings.General.Projectors=\"Proiektoreak\"\nBasic.Settings.General.HideProjectorCursor=\"Ezkutatu kurtsorea proiekzioetan\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Proiektoreak beti gainean\"\nBasic.Settings.General.Snapping=\"Iturburuaren lerrokatzearen doitzea\"\nBasic.Settings.General.ScreenSnapping=\"Doitu iturburuak pantailaren ertzera\"\nBasic.Settings.General.CenterSnapping=\"Doitu iturburuak bertikalki eta horizontalki erdira\"\nBasic.Settings.General.SourceSnapping=\"Doitu iturburuak beste iturburuetara\"\nBasic.Settings.General.SnapDistance=\"Doitu sentikortasuna\"\nBasic.Settings.General.SpacingHelpers=\"Erakutsi pixelen lerrokatze-gidak\"\nBasic.Settings.General.RecordWhenStreaming=\"Grabatu automatikoki transmisioa egitean\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Mantendu grabazioa transmisioa gelditzean\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Hasi erreprodukzio bufferra automatikoki transmititzean\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Mantendu erreprodukzio bufferra aktiboa transmisioa gelditzean\"\nBasic.Settings.General.SysTray=\"Sistemaren erretilua\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimizatu sistemaren erretilura hastean\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Minimizatu beti sistemaren erretilura ataza barrara egin ordez\"\nBasic.Settings.General.SaveProjectors=\"Gorde proiekzioak irtetean\"\nBasic.Settings.General.CloseExistingProjectors=\"Mugatu pantaila osoko proiekzio bat pantailako\"\nBasic.Settings.General.Preview=\"Aurrebista\"\nBasic.Settings.General.OverflowHidden=\"Ezkutatu gainezkatzea\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Gainezkatzea beti begi-bistan\"\nBasic.Settings.General.OverflowSelectionHidden=\"Erakutsi gainezkatzea baita iturburua ezkutuan dagoenean ere\"\nBasic.Settings.General.Importers=\"Inportatzaileak\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Bilatu eszena-bildumetarako toki ezagunak inportatzean\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Aldatu eszena klik bikoitza egitean\"\nBasic.Settings.General.StudioPortraitLayout=\"Gaitu diseinu horizontala/bertikala\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Erakutsi aurrebista/programa etiketak\"\nBasic.Settings.General.Multiview=\"Ikuspegi anitz\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Klikatu eszena batetik bestera pasatzeko\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Erakutsi eszenen izenak\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Markatu area seguruak (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Ikuspegi anitzeko diseinua\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontala, goian (8 eszena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontala, behean (8 eszena)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Bertikala, ezkerrean (8 eszena)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Bertikala, eskuinean (8 eszena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontala, goian (18 eszena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontala, goian (24 eszena)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Eszenak bakarrik (4 eszena)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Eszenak bakarrik (9 eszena)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Eszenak bakarrik (16 eszena)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Eszenak bakarrik (25 eszena)\"\nBasic.Settings.General.ChannelName.stable=\"Egonkorra\"\nBasic.Settings.General.ChannelDescription.stable=\"Azken bertsio egonkorra\"\nBasic.Settings.General.ChannelName.beta=\"Betak / Argitaratzeko hautagaiak\"\nBasic.Settings.General.ChannelDescription.beta=\"Ezegonkorrak izan daitezkeen aurreko bertsioak\"\nBasic.Settings.Appearance=\"Itxura\"\nBasic.Settings.Appearance.General=\"Orokorra\"\nBasic.Settings.Appearance.General.Theme=\"Gaia\"\nBasic.Settings.Appearance.General.Variant=\"Estiloa\"\nBasic.Settings.Appearance.General.NoVariant=\"Ez dago erabilgarri den estilorik.\"\nBasic.Settings.Appearance.FontScale=\"Letren tamaina\"\nBasic.Settings.Appearance.Density=\"Dentsitatea\"\nBasic.Settings.Appearance.Classic=\"Klasikoa\"\nBasic.Settings.Appearance.Compact=\"Trinkoa\"\nBasic.Settings.Appearance.Normal=\"Normala\"\nBasic.Settings.Appearance.Comfortable=\"Erosoa\"\nBasic.Settings.Appearance.OptionsWarning=\"Itxura-aukeretako batzuk ez daude eskuragarri estilo honetan.\"\nBasic.Settings.Stream=\"Transmisioa\"\nBasic.Settings.Stream.Destination=\"Helmuga\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Erabili autentifikazioa\"\nBasic.Settings.Stream.Custom.Username=\"Erabiltzaile izena\"\nBasic.Settings.Stream.Custom.Password=\"Pasahitza\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: sartu srp_username.\\nRTMP: sartu erabiltzaile izena.\\nSRT: ez da erabiltzen.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: sartu srp_password.\\nRTMP: sartu pasahitza.\\nSRT: sartu enkriptazio pasa-esaldia.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Gaitu banda zabalaren azterketa modua\"\nBasic.Settings.Stream.TTVAddon=\"Twitch txataren gehigarriak\"\nBasic.Settings.Stream.TTVAddon.None=\"Bat ere ez\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV eta FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Transmisioaren ezarpenak falta dira\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Ireki ezarpenak\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Transmisioaren URLa eta gakoa falta dira.\\n\\nIreki ezarpenak sartzeko URLa eta transmisioaren gakoa 'transmisioa' fitxan.\"\nBasic.Settings.Stream.MissingUrl=\"Trasnsmisioaren URLa falta da.\\n\\nIreki ezarpenak sartzeko URLa 'Transmisioa' fitxan.\"\nBasic.Settings.Stream.MissingStreamKey=\"Transmisioaren gakoa falta da.\\n\\nIreki ezarpenak sartzeko transmisioaren gakoa 'Transmisioa' fitxan.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Streaming zerbitzua konfiguratzeko gomendioak alde batera utzi\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Gomendatutako Doikuntzak Baliogabetu\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Abisua: Zerbitzuaren mugak kontuan ez izateak transmisioaren kalitatea degradatu dezake edo transmisioa bera eragotzi dezake.\\n\\n Jarraitu?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Bideoaren gehienezko bit-emaria: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Gehienezko audio bit-tasa: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Gehienezko bereizmena: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Gehienezko FPM: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Zehaztu zerbitzari pertsonalizatua...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Zerbitzari pertsonalizatua\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Gaitu %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Transmisioaren gehienezko banda-zabalera\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automatikoa\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Gehienezko bideo-pistak\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automatikoa\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Gaitu transmisio-iraulketa FLVra (grabaketa-fitxategien ezarpen sinpleak erabiltzen ditu)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Konfigurazioaren gainidaztea (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Gaitu konfigurazioaren gainidaztea\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Pista anitzeko bideoa\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Oihal gehigarria\"\nBasic.Settings.Stream.AdvancedOptions=\"Ezarpen aurreratuak\"\nBasic.Settings.Output=\"Irteera\"\nBasic.Settings.Output.Format=\"Grabazio-formatua\"\nBasic.Settings.Output.Format.MKV=\"Matroska Bideoa (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"MP4 hibridoa (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV hibridoa (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 Fragmentatuta (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV Fragmentatuta (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"MOV zatikatuak grabaketa zatika idazten du eta ez du MOV fitxategi tradizionalen amaiera bera behar.\\nHori esker, fitxategia erreproduzigarria izaten jarraitzen du diskoan idaztea eten bada ere, adibidez, BSOD edo energia galtzearen ondorioz.\\n\\n Baliteke hau ez izatea erreproduzigailu eta editore guztiekin bateragarria. Erabili Fitxategia → Remux Grabaketak fitxategia formatu bateragarriago batera bihurtzeko, behar izanez gero.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"MP4 zatikatuak grabaketa zatika idazten du eta ez du MP4 fitxategi tradizionalen amaiera bera behar.\\nHori esker, fitxategia erreproduzigarria izaten jarraitzen du diskoan idaztea eten bada ere, adibidez, BSOD edo energia galtzearen ondorioz.\\n\\n Baliteke hau ez izatea erreproduzigailu eta editore guztiekin bateragarria. Erabili Fitxategia → Remux Grabaketak fitxategia formatu bateragarriago batera bihurtzeko, behar izanez gero.\"\nBasic.Settings.Output.Encoder.Video=\"Bideo kodetzailea\"\nBasic.Settings.Output.Encoder.Audio=\"Audio kodetzailea\"\nBasic.Settings.Output.SelectDirectory=\"Hautatu grabazioaren karpeta\"\nBasic.Settings.Output.DynamicBitrate=\"Aldatu bit-emaria dinamikoki pilaketa kudeatzeko\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Aldatu bit-emaria dinamikoki pilaketa kudeatzeko (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Pilaketa murrizteko fotogramak askatu beharrean, abiadura dinamikoki aldatzen du.\\n\\n Kontuan izan horrek ikuslearentzat atzerapena areagotu dezakeela bat-bateko pilaketak nabarmenak badira.\\nBit-emaria jaisten denean, minutu batzuk iraun dezake berreskuratzeak.\\n\\nUne honetan bakarrik RTMPrako onartzen da.\"\nBasic.Settings.Output.Mode=\"Irteera-modua\"\nBasic.Settings.Output.Mode.Simple=\"Sinplea\"\nBasic.Settings.Output.Mode.Adv=\"Aurreratua\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg irteera\"\nBasic.Settings.Output.UseReplayBuffer=\"Gaitu erreprodukzio bufferra\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Erantzuteko gehienezko epea\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Gehienezko memoria\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Ustezko memoria erabilera: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Abisua: %1 MiB-ko memoria-erabilera estimatua %2 MiBko gomendatutako gehienezkoa baino handiagoa da\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Ezin da kalkulatu memoria erabilera. Ezarri gehienezko memoria.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Erreprodukzio bufferraren fitxategi aurrizkia\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Atzizkia\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Erreprodukzio Bufferra ezin da erabili grabazio mota Irteera pertsonalizatua (FFmpeg) ezarrita dagoenean.\"\nBasic.Settings.Output.Simple.SavePath=\"Grabazio-bidea\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Grabazio-kalitatea\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Transmisioaren berdina\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Kalitate altua. Fitxategi-tamaina ertaina\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Bereizigabeko kalitatea. Fitxategi-tamaina handia\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Galerarik gabeko kalitatea. Fitxategi-tamaina izugarri handia\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Abisua: Transmisioaren bit-emaria %1-ean ezarriko da, zeina uneko transmisio-zerbitzuaren goiko muga da.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Abisua: audioaren datu-korrontearen bit tasa %1 ean ezarriko da, hori da uneko datu-korrontearen zerbitzuaren goiko muga.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Kontuz: grabazioa ezin da pausatu grabazioaren kalitatearen konfigurazioa \\\"jarioaren berdina\\\" bada.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Abisua: une honetan hautatutako grabazio-formatua ez da bateragarria hautatutako korronte-kodetzailearekin.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Oharra: Transmisioak duen kalitatea ez bestelako kalitate batez grabatzeak PUZ-ren aparteko erabilera eskatzen du une berean transmititzen eta grabatzen baduzu.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Abisua: galerarik gabeko kalitateak fitxategi-tamaina izugarri handiak sortzen ditu! Galerarik gabeko kalitateak minutuko 7 gigabyte-ko espazioa erabil dezake bereizmen eta fotograma-tasa handietan. Lossless  edo galerarik gabeko kalitatea ez da gomendagarria grabaketa luzeetarako, diskoan leku oso handia eskuragarri ez baduzu. Erreprodukzio-buffer-a ez dago erabilgarri galerarik gabeko kalitatea erabiltzen denean.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Ziur zaude galerarik gabeko kalitatea erabili nahi duzula?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Galerarik gabeko kalitateaz oharra!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Softwarea (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Hardware\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Hardwarea (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Komposanteak (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Hardwarea (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Hardwarea (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Komposanteak (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Hardwarea (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Hardwarea (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Hardwarea (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Softwarea (x264 PUZ erabilpen apaleko aurre-ezarpena, fitxategiaren tamaina handitzen du)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Lehenetsia)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD pista (2. pista erabiltzen du)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Audio-pista\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Ebazpen Bateraezina\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Transmisio-zerbitzu honek ez du onartzen zuren uneko bereizmena edota fotograma-emaria. Hauen ordez bateragarria den antzekoena ezarriko da:\\n\\n%1\\n\\nJarraitu nahi duzu?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Bereizmena: : %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Kodetzaile bateraezina\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"\\\"%1\\\" transmisio zerbitzuak ez du \\\"%2\\\" kodetzailea onartzen. Kodetzailea \\\"%3\\\"-ra aldatuko da.\\n\\nJarraitu nahi duzu?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"\\\"%1\\\" streaming zerbitzua ez du \\\"%2\\\" eta \\\"%3\\\" kodetzaileak onartzen. Kodetzaile hauek aldatuko dira \\\"%4\\\" eta \\\"%5\\\" kodetzaileetara.\\n\\nJarraitu nahi duzu?\"\nBasic.Settings.Output.VideoBitrate=\"Bideo bit-emaria\"\nBasic.Settings.Output.AudioBitrate=\"Audio bit-emaria\"\nBasic.Settings.Output.Reconnect=\"Automatikoki birkonektatu\"\nBasic.Settings.Output.RetryDelay=\"Berriro saiatzeko atzerapena\"\nBasic.Settings.Output.MaxRetries=\"Gehienezko saiakerak\"\nBasic.Settings.Output.Advanced=\"Gaitu kodetzaile pertsonalizatuaren ezarpenak (aurreratua)\"\nBasic.Settings.Output.EncoderPreset=\"Aurrez ezarritako kodetzea\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (CPU erabilpen baxua, kalitate baxuena)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (lehenetsia) (tarteko CPU erabilera, kalitate estandarra)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (CPU erabilpen altua, kalitate altua)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Kodetzailearen ezarpen pertsonalizatuak\"\nBasic.Settings.Output.CustomMuxerSettings=\"Bihurtzailearen ezarpen pertsonalizatuak\"\nBasic.Settings.Output.NoSpaceFileName=\"Sortu tarterik gabeko fitxategi-izena\"\nBasic.Settings.Output.Adv.Rescale=\"Eskala-aldaketaren Irteera\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Ezgaituta\"\nBasic.Settings.Output.Adv.AudioTrack=\"Audio-pista\"\nBasic.Settings.Output.Adv.Streaming=\"Transmisioa\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Transmizio ezarpenak\"\nBasic.Settings.Output.Adv.Audio.Track1=\"1 pista\"\nBasic.Settings.Output.Adv.Audio.Track2=\"2 pista\"\nBasic.Settings.Output.Adv.Audio.Track3=\"3 pista\"\nBasic.Settings.Output.Adv.Audio.Track4=\"4 pista\"\nBasic.Settings.Output.Adv.Audio.Track5=\"5. pista\"\nBasic.Settings.Output.Adv.Audio.Track6=\"6. pista\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD pista\"\nBasic.Settings.Output.Adv.Encoder=\"Kodetzailearen ezarpenak\"\nBasic.Settings.Output.Adv.Recording=\"Grabazioa\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Grabazioaren ezarpenak\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Grabaketa mota\"\nBasic.Settings.Output.Adv.Recording.Type=\"Mota\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Estandarra\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Irteera pertsonalizatua (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Erabili transmisio kodetzailea)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Fitxategi-izenaren formatua\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Gainidatzi fitxategia badago\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Irteera pertsonalizatua (FFmpeg) bermerik gabe eskaintzen da.\\nKontuan izan \\\"Estandarra\\\" grabaketa mota erabiltzea.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg Irteera-mota\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Irteera URL-ra\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Irteera fitxategira\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Grabazio formatu arruntak\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Fitxategi guztiak\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Fitxategi-bidea edo URL-a\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Edukiontziaren formatua\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Audioa\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Bideoa\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Formatu lehenetsia\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Edukiontzi-formatuaren ezaugarriak\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Audio/Bideo Kodeka fitxategiaren bidetik edo URLtik igarria\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Lehenetsitako kodetzailea\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Ezgaitu kodetzailea\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Bideo kodetzailea\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Bideo kodetzailearen ezarpenak (egonez gero)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Audio kodetzailea\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Audio kodetzailearen ezarpenak (egonez gero)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Bihurtzailearen ezarpenak (egonez gero)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Gako-fotogramen tartea (fotogramak)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Erakutsi kodek guztiak (bateragarriak ez balira ere)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg ezarpenak\"\nBasic.Settings.Output.EnableSplitFile=\"Fitxategi banaketa automatikoa\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Banaketa denboran\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Banaketa tamainan\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Eskuz bakarrik zatitu\"\nBasic.Settings.Output.SplitFile.Time=\"Denbora banaketa\"\nBasic.Settings.Output.SplitFile.Size=\"Tamaina banaketa\"\nScreenshot=\"Pantaila-argazkia irteerari\"\nScreenshot.SourceHotkey=\"Pantaila-argazkia hautatutako iturburuari\"\nScreenshot.StudioProgram=\"Pantaila-argazkia (Programa)\"\nScreenshot.Preview=\"Pantaila-argazkia (aurrebista)\"\nScreenshot.Scene=\"Pantaila-argazkia (eszena)\"\nScreenshot.Source=\"Pantaila-argazkia (jatorria)\"\nFilenameFormatting.TT.CCYY=\"Urtea, lau zenbaki\"\nFilenameFormatting.TT.YY=\"Urtea, azken bi zenbakiak (00-99)\"\nFilenameFormatting.TT.MM=\"Hilabetea zenbaki dezimalaz (01-12)\"\nFilenameFormatting.TT.DD=\"Hilabetearen eguna, zero betegarriaz (01-31)\"\nFilenameFormatting.TT.hh=\"Ordua 24h formatuan (00-23)\"\nFilenameFormatting.TT.mm=\"Minutua (00-59)\"\nFilenameFormatting.TT.ss=\"Segundu (00-59)\"\nFilenameFormatting.TT.Percent=\"% ikurra\"\nFilenameFormatting.TT.a=\"Astegun izena laburtuta\"\nFilenameFormatting.TT.A=\"Astegun osoaren izena\"\nFilenameFormatting.TT.b=\"Hilabete izena laburtuta\"\nFilenameFormatting.TT.B=\"Hilabete osoaren izena\"\nFilenameFormatting.TT.d=\"Hilabetearen eguna, zero betegarriaz (01-31)\"\nFilenameFormatting.TT.H=\"Ordua 24h formatuan (00-23)\"\nFilenameFormatting.TT.I=\"Ordua 12h formatuan (01-12)\"\nFilenameFormatting.TT.m=\"Hilabetea zenbaki dezimalaz (01-12)\"\nFilenameFormatting.TT.M=\"Minutua (00-59)\"\nFilenameFormatting.TT.p=\"AM edo PM izendapena\"\nFilenameFormatting.TT.s=\"UNIXen garaitik denbora segundotan\"\nFilenameFormatting.TT.S=\"Segundua (00-59)\"\nFilenameFormatting.TT.y=\"Urtea, azken bi zenbakiak (00-99)\"\nFilenameFormatting.TT.Y=\"Urtea\"\nFilenameFormatting.TT.z=\"ISO 8601 desplazamendua UTC ordu-eremuan\"\nFilenameFormatting.TT.Z=\"Ordu-eremuaren izena edo laburdura\"\nFilenameFormatting.TT.FPS=\"Fotogramak segundu bakoitzetan\"\nFilenameFormatting.TT.CRES=\"Oinarriaren (oihalaren) bereizmena\"\nFilenameFormatting.TT.ORES=\"Irteera (eskalatuaren) bereizmena\"\nFilenameFormatting.TT.VF=\"Bideo-formatua\"\nBasic.Settings.Video=\"Bideoa\"\nBasic.Settings.Video.Adapter=\"Bideo moldagailua\"\nBasic.Settings.Video.BaseResolution=\"Oinarriaren (oihalaren) bereizmena\"\nBasic.Settings.Video.ScaledResolution=\"Irteera (eskalatuaren) bereizmena\"\nBasic.Settings.Video.DownscaleFilter=\"Behera-eskalatze iragazkia\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Bereizmenak berdinak dira, ez da behera-eskalatzerik behar]\"\nBasic.Settings.Video.FPSCommon=\"FPS balio arruntak\"\nBasic.Settings.Video.FPSInteger=\"FPS balio osoa\"\nBasic.Settings.Video.FPSFraction=\"FPS zatikizko balioa\"\nBasic.Settings.Video.Numerator=\"Zenbakitzailea\"\nBasic.Settings.Video.Denominator=\"Izendatzailea\"\nBasic.Settings.Video.Renderer=\"Errendatzailea\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (esperimentala)\"\nBasic.Settings.Video.InvalidResolution=\"Bereizmen baliogabea. Izan behar da [width]x[height] (adib. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Bideo irteera gaituta dago une honetan. Eten irteera guztiak bideo ezarpenak aldatzeko.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineala (Azkarrena, baina lausoa eskalatuz gero)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bikubikoa (enfokatutako eskalatzea, 16 lagin)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (eskalatze zorrotza, 36 lagin)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Area (Batura ponderatua, 4/6/9 lagin)\"\nBasic.Settings.Audio=\"Audioa\"\nBasic.Settings.Audio.SampleRate=\"Lagin-maiztasuna\"\nBasic.Settings.Audio.Channels=\"Kanalak\"\nBasic.Settings.Audio.Meters=\"Neurgailuak\"\nBasic.Settings.Audio.MeterDecayRate=\"Desintegrazio-tasa\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Azkarra\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Tartekoa (I motako PPMa)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Geldoa (II motako PPMa)\"\nBasic.Settings.Audio.PeakMeterType=\"Gailurren neurgailu mota\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Lagin-gailurra\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Benetako gailurra (CPUaren erabilera handiagoa)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"Kontu: soinu inguratzailea aktibatuta dago.\"\nBasic.Settings.Audio.MultichannelWarning=\"Erreproduzitzen baduzu, egiaztatu zure igorpen-zerbitzuak soinu inguratzailea hartzen eta soinu inguratzailea erreproduzitzea onartzen duen. Esate baterako, Facebook 360 Live-k guztiz onartzen du  soinu inguratzailea; YouTube Live-k 5.1 audioa hartzea onartzen du (eta telebistetan erreproduzitzea).\\n\\nOBS audio-iragazkiak soinu inguratzailearekin bateragarriak dira, nahiz eta VST pluginen laguntza ez dagoen bermatuta.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Nahi duzu soinu inguratzailea aktibatzea?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Seguru zaude soinu inguratzailea aktibatu nahi duzula?\"\nBasic.Settings.Audio.Devices=\"Audio-Gailu Globala\"\nBasic.Settings.Audio.DesktopDevice=\"Mahaigaineko audioa\"\nBasic.Settings.Audio.DesktopDevice2=\"Mahaigaineko audioa 2\"\nBasic.Settings.Audio.AuxDevice=\"Mik/Audio osagarria\"\nBasic.Settings.Audio.AuxDevice2=\"Mik/Audio osagarria 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mik/Audio osagarria 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mik/Audio osagarria 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Gaitu Sakatu-mututzeko\"\nBasic.Settings.Audio.PushToMuteDelay=\"Sakatu-mututzeko atzerapena\"\nBasic.Settings.Audio.EnablePushToTalk=\"Gaitu Sakatu-hitz-egiteko\"\nBasic.Settings.Audio.PushToTalkDelay=\"Sakatu-hitz-egiteko atzerapena\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Gailua konektatu gabe edo ez dago eskuragarri]\"\nBasic.Settings.Audio.Disabled=\"Desaktibatua\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Latentzia baxuko audio-buffer modua (Decklink/NDI irteeretarako)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"OHARRA: latentzia baxuko audio-buffer-a gaituta dago.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Latentzia baxuko audio-buffer moduak audioa akatsak edo iturri batzuetatik erreproduzitzeari uztea eragin dezake.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Latentzia baxuko audio-buffer modua gaitu?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Ziur latentzia baxuko audio-buffer modua gaitu nahi duzula?\"\nBasic.Settings.Accessibility=\"Irisgarritasuna\"\nBasic.Settings.Accessibility.ColorOverrides=\"Kolore desberdinak erabili\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Iturburu-ertza (hautapena)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Iturburu-ertza (moztu)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Iturburuaren ertza (gainetik pasa)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Nahasgailuaren bolumen-banda (-60 eta -20 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Nahasgailuaren bolumen-banda (-20 eta -9 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Nahasgailuaren bolumen-banda (-9 dB-tik 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Nahasgailuaren bolumen-banda (-60 eta -20 dB) (aktiboa)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Nahasgailuaren bolumen-banda (-20 eta -9 dB) (aktiboa)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Nahasgailuaren bolumen-banda (-9tik 0dBra) (aktiboa)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Kolore aurrez ezarritakoa\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Lehenetsia\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Norberea\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Daltonikoentzako kolore alternatiboa\"\nBasic.Settings.Advanced=\"Aurreratua\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Erakutsi irteera aktiboen abisua irtetean\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prozesuaren lehentasuna\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Altua\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Normala baino handiagoa\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normala\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Normalaren azpitik\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inaktiboa\"\nBasic.Settings.Advanced.FormatWarning=\"Oharra: NV12/P010 ez diren kolore formatuak grabaziorako dira gehienbat, eta ez daude gomendatuta transmisiorako. Transmisioak PUZ erabilpena handitu dezake kolore formatu bihurketa dela medio.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Abisua: zehaztasun handiko formatuak erabili ohi dira HDR kolore-espazioekin.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Oharra: Rec. 2100 zehaztasun gehiagoko formatu bat erabili beharko luke\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Kolore formatua\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 bit, 4:2:0, 2 plano)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 bit, 4:2:0, 3 plano)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 bit, 4:4:4, 3 plano)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 bit, 4:2:0, 2 plano)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 bit, 4:2:0, 3 plano)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 plano)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 plano)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Kolore-espazioa\"\nBasic.Settings.Advanced.Video.ColorRange=\"Kolore tartea\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Mugatua\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Osoa\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR zuri maila\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR gailur maila nominala\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Monitorizazio gailua\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Lehenetsia\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Ezgaitu Windows audio ducking\"\nBasic.Settings.Advanced.StreamDelay=\"Transmisio-atzerapena\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Iraupena\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Mantendu ebaketa puntua (handitu atzerapena) birkonektatzean\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Estimatutako memoria erabilpena: %1 MB\"\nBasic.Settings.Advanced.Network=\"Sarea\"\nBasic.Settings.Advanced.Network.Disabled=\"Une honetan hautatutako transmisio-protokoloak ez du onartzen sarearen ezarpenak aldatzea.\"\nBasic.Settings.Advanced.Network.BindToIP=\"IP bidez lotu\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP familia\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Gaitu sarearen optimizazioak\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Gaitu TCP erritmoa\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"RTMP irteera sareko latentziarekiko sentikorrak diren beste aplikazio batzuekin bateragarriagoa izan dadin saiatzen da transmisio-abiadura erregulatuz.\\nHonek konexio ezegonkorretan fotogramak galtzeko arriskua areagotu dezake.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Laster tekletan fokuratutako portaera\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Ez desaktibatu laster-teklak inoiz\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Ezgaitu laster-teklak leiho nagusia fokuan dagoenean\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Desaktibatu laster-teklak leiho nagusia fokuan ez dagoenean\"\nBasic.Settings.Advanced.AutoRemux=\"Bihurtu automatikoki %1-ra\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(grabatu mkv bezala)\"\nBasic.AdvAudio=\"Audio propietate aurreratuak\"\nBasic.AdvAudio.ActiveOnly=\"Iturburu aktiboak soilik\"\nBasic.AdvAudio.Name=\"Izena\"\nBasic.AdvAudio.Volume=\"Bolumena\"\nBasic.AdvAudio.VolumeSource=\"'%1'-ren bolumena\"\nBasic.AdvAudio.MonoSource=\"'%1'-ren Mono Downmix\"\nBasic.AdvAudio.Balance=\"Balantzea\"\nBasic.AdvAudio.BalanceSource=\"'%1'-ren balantzea\"\nBasic.AdvAudio.SyncOffset=\"Sinkronizazioaren desplazamendua\"\nBasic.AdvAudio.SyncOffsetSource=\"'%1'-ren sinkronizazioaren desplazamendua\"\nBasic.AdvAudio.Monitoring=\"Adioaren monitorizazioa\"\nBasic.AdvAudio.Monitoring.None=\"Ez monitorizatu\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Monitorea bakarrik (irteera mututua)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitorea eta irteera\"\nBasic.AdvAudio.MonitoringSource=\"Audio monitorizazioa '%1'entzat\"\nBasic.AdvAudio.AudioTracks=\"Pistak\"\nBasic.Settings.Hotkeys=\"Laster-teklak\"\nBasic.Settings.Hotkeys.Pair=\"'%1'-rekin egindako tekla konbinazioek txandakatze moduan jokatzen dute\"\nBasic.Settings.Hotkeys.Filter=\"Iragazkia\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Iragazi laster-teklen arabera\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Laster-tekla honi ekintza bat edo gehiago esleitu zaizkio, egin klik gatazkak ikusteko\"\nBasic.Settings.Hotkeys.PleaseWait=\"Laster-teklak kargatzen, itxaron mesedez...\"\nBasic.Hotkeys.SelectScene=\"Aldatu eszenara\"\nBasic.SystemTray.Show=\"Erakutsi\"\nBasic.SystemTray.Hide=\"Ezkutatu\"\nBasic.SystemTray.Message.Reconnecting=\"Deskonektatuta. Berriro konektatzen...\"\nHotkeys.Insert=\"Txertatu\"\nHotkeys.Delete=\"Ezabatu\"\nHotkeys.Home=\"Hasiera\"\nHotkeys.End=\"Amaiera\"\nHotkeys.PageUp=\"Orria gora\"\nHotkeys.PageDown=\"Orria behera\"\nHotkeys.NumLock=\"Blok Zenb\"\nHotkeys.ScrollLock=\"Blok korr\"\nHotkeys.CapsLock=\"Blok Maius\"\nHotkeys.Backspace=\"Atzera\"\nHotkeys.Print=\"Inprimatu\"\nHotkeys.Pause=\"Pausatu\"\nHotkeys.Left=\"Ezker\"\nHotkeys.Right=\"Eskuin\"\nHotkeys.Up=\"Gora\"\nHotkeys.Down=\"Behera\"\nHotkeys.Menu=\"Menua\"\nHotkeys.Space=\"Zuriunea\"\nHotkeys.NumpadNum=\"Zenbaki-teklatua %1\"\nHotkeys.NumpadMultiply=\"Zenb-teklatuko biderketa\"\nHotkeys.NumpadDivide=\"Zenbaki-teklatuaren zatiketa\"\nHotkeys.NumpadAdd=\"Zenb-teklatuaren gehiketa\"\nHotkeys.NumpadSubtract=\"Zenbaki-teklatuaren kenketa\"\nHotkeys.NumpadDecimal=\"Zenb-teklatuaren hamartarra\"\nHotkeys.AppleKeypadNum=\"%1 (tekla)\"\nHotkeys.AppleKeypadMultiply=\"* (tekla)\"\nHotkeys.AppleKeypadDivide=\"/ (tekla)\"\nHotkeys.AppleKeypadAdd=\"+ (tekla)\"\nHotkeys.AppleKeypadSubtract=\"- (tekla)\"\nHotkeys.AppleKeypadDecimal=\". (tekla)\"\nHotkeys.AppleKeypadEqual=\"= (tekla)\"\nHotkeys.MouseButton=\"Sagua %1\"\nMute=\"Mututu\"\nUnmute=\"Ez mututu\"\nPush-to-mute=\"Sakatu-mututzeko\"\nPush-to-talk=\"Sakatu-hitz-egiteko\"\nSceneItemShow=\"Erakutsi '%1'\"\nSceneItemHide=\"Ezkutatu '%1'\"\nOutputWarnings.NoTracksSelected=\"Gutxienez pista bat hautatu behar duzu\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Irteerako ezarpenen errorea\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Irteera guztiek audio-pista bat izan behar dute gutxienez hautatuta.\"\nOutputWarnings.MP4Recording=\"Kontuz: MP4 formatuz gordetako grabazioak izan daitezke berreskuraezinak fitxategia ezin bada bukatu (esate baterako BSODs-ren emaitza bat, energia etenak eta abar). Hainbat audio pista grabatu nahi baduzu erabil dezakezu MKV formatua eta mp4 bihurtu grabazioa bukatu ondoren (Fitxategia->Bihurtu grabazioak)\"\nOutputWarnings.CannotPause=\"Kontuz: Grabazioak ezin dira pausatu grabazioaren kodetzailea \\\"(Erabili transmisioaren kodetzailea)\\\" konfigurazioa badu\"\nOutputWarnings.CodecIncompatible=\"Audio- edo bideo-kodetzaileen aukeraketa berrezarri da bateraezintasunagatik. Hautatu zerrendako kodetzaile bateragarri bat.\"\nCodecCompat.Incompatible=\"(Bateraezina %1-rekin)\"\nCodecCompat.CodecPlaceholder=\"Hautatu kodetzailea...\"\nCodecCompat.ContainerPlaceholder=\"Hautatu formatua...\"\nCodecCompat.CodecMissingOnExit.Title=\"Ez da kodetzailerik hautatu\"\nCodecCompat.CodecMissingOnExit.Text=\"Gutxienez bideo- edo audio-kodetzaile bat ez dago ezarrita. Mesedez, ziurtatu kodetzaileak hautatzen dituzula grabaziorako eta transmisiorako.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Ez da formaturik hautatu\"\nCodecCompat.ContainerMissingOnExit.Text=\"Ez da grabazio formaturik hautatu. Hautatu aukeratutako transmisio-kodetzailearekin bateragarria den grabazio formatua.\"\nFinalScene.Title=\"Ezabatu eszena\"\nFinalScene.Text=\"Gutxienez eszena bat egon behar du.\"\nNoSources.Title=\"Iturbururik ez dago\"\nNoSources.Text=\"Badirudi ez duzula gehitu bideo iturbururik oraindik, beraz emaitza pantaila huts bat izango da. Ziur zaude hau egin nahi duzula?\"\nNoSources.Text.AddSource=\"Gehitzen ahal duzu iturburuak Iturburuak kutxako azpiko aldeko + ikonoa klikatuz edozein unetan.\"\nNoSources.Label=\"Ez duzu iturbururik aukeratu.\\nEgin klik beheko + botoian,\\nedo eskuin klik bat gehitzeko.\"\nChangeBG=\"Ezarri kolorea\"\nCustomColor=\"Kolore pertsonalizatua\"\nBrowserSource.EnableHardwareAcceleration=\"Gaitu nabigatzailearen iturburuko hardware azelerazioa\"\nAbout=\"Honi buruz\"\nAbout.Info=\"OBS Studio bideoa grabazioak eta zuzeneko transmisioak egiteko software libreko doako aplikazioa da.\"\nAbout.Donate=\"Egin diru ekarpen bat\"\nAbout.GetInvolved=\"Parte hartu\"\nAbout.Authors=\"Egileak\"\nAbout.License=\"Lizentzia\"\nAbout.Error=\"Errorea! Ezin izan da fitxategia irakurri.\\n\\nJoan: %1-ra\"\nAbout.Contribute=\"Sostengatu OBS proiektua\"\nAddUrl.Title=\"Gehitu iturburua URL bidez\"\nAddUrl.Text=\"Arrastatu duzu URL bat OBSra. Honek automatikoki gehituko du esteka hori iturburu moduan. Jarraitu?\"\nAddUrl.Text.Url=\"URLa: %1\"\nResizeOutputSizeOfSource=\"Aldatu irteeraren tamaina (iturriaren tamaina)\"\nResizeOutputSizeOfSource.Text=\"Oinarrizko eta irteerako bereizmenak uneko iturriaren tamainara aldatuko dira.\"\nResizeOutputSizeOfSource.Continue=\"Jarraitu nahi duzu?\"\nPreviewTransition=\"Aurreikusi trantsizioa\"\nImporter=\"Eszena-bildumen inportatzailea\"\nImporter.SelectCollection=\"Hautatu eszena-bilduma\"\nImporter.Collection=\"Eszena-bilduma\"\nImporter.HelpText=\"Gehitu fitxategiak leiho honi inportatzeko bildumak OBStik edo onartutako beste programetatik.\"\nImporter.Path=\"Bildumaren bide-izena\"\nImporter.Program=\"Atzemandako aplikazioa\"\nImporter.AutomaticCollectionPrompt=\"Bilatu automatikoki eszena-bildumak\"\nImporter.AutomaticCollectionText=\"OBSek bilatu dezake era automatikoan eszena inportagarriak onartutako beste programetatik. Nahi duzu OBSek automatikoki bilatzea bildumak zuretzat?\\n\\nAurrerago aldatu ahal izango duzu Ezarpenak > Orokorra > Inportatzaileak.\"\nImporter.SelectFile=\"Arakatu...\"\nRestart=\"Berrabiarazi\"\nNeedsRestart=\"OBS Studio berrabiarazi behar da. Nahi duzu orain berrabiaraztea?\"\nLoadProfileNeedsRestart=\"Profilak OBS berrabiarazi behar duten ezarpenak ditu:\\n%1\\n\\nOBS berrabiarazi nahi duzu ezarpen hauek eragina izan dezaten?\"\nContextBar.NoSelectedSource=\"Ez da iturririk hautatu\"\nContextBar.MediaControls.PlayMedia=\"Erreproduzitu Media\"\nContextBar.MediaControls.PauseMedia=\"Media Pausatu\"\nContextBar.MediaControls.StopMedia=\"Gelditu media\"\nContextBar.MediaControls.RestartMedia=\"Berrabiarazi Media\"\nContextBar.MediaControls.PlaylistNext=\"Hurrengoa Erreprodukzio-zerrendan\"\nContextBar.MediaControls.PlaylistPrevious=\"Aurrekoa Erreprodukzio-zerrendan\"\nYouTube.Auth.Ok=\"Baimena eskuratu da.\\nOrain orri hau itxi dezakezu.\"\nYouTube.Auth.NoCode=\"Baimen-prozesua ez da burutu.\"\nYouTube.Auth.NoChannels=\"Hautatutako kontuan ez dago kanal erabilgarririk\"\nYouTube.Auth.WaitingAuth.Title=\"YouTuberen erabiltzaile baimena\"\nYouTube.Auth.WaitingAuth.Text=\"Osatu baimena zure nabigatzailean.<br>Nabigatzailea irekitzen ez bada jarraitu esteka hau baimena eskuratzeko:<br>%1\"\nYouTube.AuthError.Text=\"Ezin da kanalaren informazioa eskuratu: %1\"\nYouTube.Actions.WindowTitle=\"YouTube emankizunaren ezarpenak - Kanala: %1\"\nYouTube.Actions.CreateNewEvent=\"Sortu emankizun berria\"\nYouTube.Actions.ChooseEvent=\"Hautatu dagoen emankizun bat\"\nYouTube.Actions.Title=\"Izenburua\"\nYouTube.Actions.MyBroadcast=\"Nire emisioa\"\nYouTube.Actions.Description=\"Deskribapena\"\nYouTube.Actions.Privacy=\"Pribatutasuna*\"\nYouTube.Actions.Privacy.Private=\"Pribatua\"\nYouTube.Actions.Privacy.Public=\"Publikoa\"\nYouTube.Actions.Privacy.Unlisted=\"Zerrendatu gabea\"\nYouTube.Actions.Category=\"Kategoria\"\nYouTube.Actions.Thumbnail=\"Miniaturak\"\nYouTube.Actions.Thumbnail.SelectFile=\"Hautatu fitxategia...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Ez da fitxategirik hautatu\"\nYouTube.Actions.Thumbnail.ClearFile=\"Garbitu\"\nYouTube.Actions.MadeForKids=\"Bideo hau haurrentzat bereziki sortuta dago?*\"\nYouTube.Actions.MadeForKids.Yes=\"Bai, haurrentzat da\"\nYouTube.Actions.MadeForKids.No=\"Ez, ez da haurrentzat\"\nYouTube.Actions.AdditionalSettings=\"Ezarpen gehiago\"\nYouTube.Actions.Latency=\"Atzerapena\"\nYouTube.Actions.Latency.Normal=\"Normala\"\nYouTube.Actions.Latency.Low=\"Baxua\"\nYouTube.Actions.Latency.UltraLow=\"Baxu baxua\"\nYouTube.Actions.EnableAutoStart=\"Gaitu abiatze automatikoa\"\nYouTube.Actions.EnableAutoStop=\"Gaitu gelditze automatikoa\"\nYouTube.Actions.AutoStartStop.TT=\"Programatutako emankizun hau automatikoki hasi behar den adierazten du\"\nYouTube.Actions.EnableDVR=\"Gaitu DVR\"\nYouTube.Actions.360Video=\"360 bideoa\"\nYouTube.Actions.ScheduleForLater=\"Gerorako programatu\"\nYouTube.Actions.RememberSettings=\"Gogoratu ezarpenak\"\nYouTube.Actions.Create_Ready=\"Sortu emankizuna\"\nYouTube.Actions.Create_GoLive=\"Sortu emankizuna eta hasi transmisioa\"\nYouTube.Actions.Choose_Ready=\"Hautatu emankizuna\"\nYouTube.Actions.Choose_GoLive=\"Sortu emankizuna eta hasi transmisioa\"\nYouTube.Actions.Create_Schedule=\"Programatu emankizuna\"\nYouTube.Actions.Create_Schedule_Ready=\"Programatu eta hautatu emankizuna\"\nYouTube.Actions.Dashboard=\"Ireki Youtube Studio\"\nYouTube.Actions.Error.Title=\"Errorea zuzeneko emisioa sortzean\"\nYouTube.Actions.Error.Text=\"'%1'errorea YouTuben sartzean.<br/>Errorearen deskribapen zehatza topatu dezakezu esteka honetan: <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Errorea YouTubera sartzean. Begiratu zure sareko konexioa edo YouTuberen zerbitzariko sarbidea.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"'%1' errorea emisioa sortzean.<br/>Errorearen gaineko deskribapen zehatza topatu dezakezu esteka honetan: <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Ez da transmisiorik sortu. Lotu berriro kontua.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTuberen API errorea. Begiratu erregistroan informazio gehiagorako\"\nYouTube.Actions.Error.BroadcastNotFound=\"Ez da aurkitu hautatutako emankizuna\"\nYouTube.Actions.Error.FileMissing=\"Fitxategi hau ez dago.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Ezin da aukeratutako fitxategia ireki\"\nYouTube.Actions.Error.FileTooLarge=\"Hautatutako fitxategia handiegia da (Maximoa: 2MiB)\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Emankizunaren igarotzeak huts egin du: %1<br/><br/>erroreak jarraitzen badu <a href='https://studio.youtube.com/video/%2/livestreaming'>ireki emankizuna YouTube Studion</a> eta saiatu eskuz.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Emankizuna proba fasera igarotzen ari da, denbora pixka bat behar izan dezake. Saiatu berriro 10-30 segundo barru.\"\nYouTube.Actions.EventsLoading=\"Ekitaldien zerrenda kargatzen...\"\nYouTube.Actions.EventCreated.Title=\"Ekitaldia sortu da.\"\nYouTube.Actions.EventCreated.Text=\"Ekitaldia behar bezala sortu da.\"\nYouTube.Actions.Stream=\"Transmisioa\"\nYouTube.Actions.Stream.ScheduledFor=\"%1 erako programatua\"\nYouTube.Actions.Stream.Resume=\"Berrekin pausatutako transmisioa\"\nYouTube.Actions.Stream.YTStudio=\"YouTube Studiok automatikoki sortua\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Zuzeneko igorpen berria sortzen, itxaron mesedez...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Eskuzko hasiera behar da\"\nYouTube.Actions.AutoStartStreamingWarning=\"Gertaera honetarako abiarazte automatikoa desgaituta dago, egin klik \\\"Hasi zuzenekoa\\\" zure emankizuna hasteko.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Ezingo duzu birkonektatu.<br>Zure transmisioa geldituko da eta ez zara zuzenean egongo.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Zuzeneko transmisioa ez dago gaituta hautatutako YouTube kanalean.<br/><br/>Ikusi <a href='https://www.youtube.com/features'>youtube.com/features</a> informazio gehiago lortzeko .\"\nYouTube.Errors.livePermissionBlocked=\"Zuzeneko transmisioa ez dago erabilgarri hautatutako YouTube kanalean.<br/>Kontuan izan 24 ordu arte behar izan daitezkeela zuzeneko erreprodukzioa erabilgarri egoteko kanalaren ezarpenetan gaitu ondoren.<br/><br/>Ikusi <a href='https://www.youtube.com/features'>youtube.com/features</a> xehetasunetarako.\"\nYouTube.Errors.errorExecutingTransition=\"Igarotzeak huts egin du backend-aren errore baten ondorioz. Mesedez, saiatu berriro segundo batzuk barru.\"\nYouTube.Errors.errorStreamInactive=\"YouTube ez da ari zure transmisioaren daturik jasotzen. Mesedez, egiaztatu zure konfigurazioa eta saiatu berriro.\"\nYouTube.Errors.invalidTransition=\"Igarotze saiakera baliogabea izan da. Baliteke transmisioak aurreko trantsizio bat amaitu ez duelako izatea. Mesedez, itxaron segundo batzuk eta saiatu berriro.\"\nYouTube.DocksRemoval.Title=\"Garbitu Legacy YouTube arakatzailearen panelak\"\nYouTube.DocksRemoval.Text=\"Arakatzailearen panel hauek zaharkituta daudelako kenduko dira:\\n\\n%1\\nErabili \\\"Panelak/YouTube Live Control Room\\\" haren ordez.\"\nConfigDownload.WarningMessageTitle=\"Erne\"\nFailedToStartStream.MissingConfigURL=\"Ez dago konfigurazio URLrik uneko zerbitzurako\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Ez da zehaztu RTMP URL pertsonalizatua\"\nFailedToStartStream.InvalidCustomConfig=\"Konfigurazio pertsonalizatua baliogabea\"\nFailedToStartStream.MissingCanvas=\"Konfiguratutako oihal gehigarria falta da\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Ezin izan da pista anitzeko bideo-zerbitzua sortu\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Ezin izan da pista anitzeko bideo RTMP irteera sortu\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC ez dago erabilgarri.\\n\\nEzin izan da '%1' kodetzaile mota aurkitu\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Ezin izan da sortu '%1' bideo-kodetzailea (mota: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Ezin izan da lortu OBS bideoaren informazioa '%1' kodetzailea sortu bitartean (mota: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Ezin izan da sortu audio-kodetzailea\"\nFailedToStartStream.NoRTMPURLInConfig=\"Konfigurazioak ez du transmisioaren helburuko RTMP(S) URLrik\"\nFailedToStartStream.FallbackToDefault=\"Transmisioa %1 erabiliz abiarazteak huts egin du; Saiatu nahi al duzu berriro kodetze bakarreko ezarpenak erabiltzen?\"\nFailedToStartStream.ConfigRequestFailed=\"Ezin izan da %1-tik konfigurazioa eskuratu<br><br>HTTP errorea: %2\"\nFailedToStartStream.WarningUnknownStatus=\"'%1' egoera-balio ezezaguna jaso da\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\n%1 gabe transmititzen jarraitu nahi duzu?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nNahi duzu segitu transmititzen?\"\nFailedToStartStream.MissingEncoderConfigs=\"Go zuzeneko konfigurazioak ez zuen kodetzaileen konfiguraziorik barne hartzen\"\nFailedToStartStream.InvalidEncoderConfig=\"Zuzeneko transmisiorako konfigurazioak baliogabeko kodeketa-ezarpena du\"\nFailedToStartStream.StatusMissingHTML=\"Go zuzeneko eskaerak zehaztu gabeko errore bat itzuli du\"\nFailedToStartStream.NoConfigSupplied=\"Konfigurazioa falta da\"\nMultitrackVideo.Info=\"%1 -ek automatikoki optimizatzen ditu zure ezarpenak hainbat bideo-kalitate kodetzeko eta bidaltzeko. Aukera hau hautatzen baduzu, zure ordenagailuaren eta softwarearen konfigurazioari buruzko %2 informazioa bidaliko da.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Ezarpen bateraezinak\"\nMultitrackVideo.IncompatibleSettings.Text=\"Une honetan %1 ez da bateragarria:\\n\\n%2\\n%1-rekin transmisioa segitzeko, desgaitu bateraezinak diren ezarpenak:\\n\\n%3\\neta Hasi berriro transmititzen.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Desgaitu transmisio honetarako eta hasi transmititzen\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Eguneratu ezarpenak eta hasi transmisioa\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"Une honetan %1 ez da bateragarria [Audioa → Orokorra → Kanalak] '%2', %3 gisa ezarrita\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Audioa → Orokorra → Kanalak] '%1' gisa ezarri behar da\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1-ek hainbat ezarpen desberdin behar ditu [Audioa → Orokorra → Kanalak]\"\nBasic.OpenPluginManager=\"Pluginen kudeatzailea\"\nPluginManager=\"Pluginen kudeatzailea\"\nPluginManager.Restart=\"OBS Studio berrabiarazi behar da plugin aldaketak aplikatzeko\"\nPluginManager.MissingPlugin=\"[PLUGINA EZ DA AURKITU]\"\nPluginManager.Section.Discover=\"Nabigatu\"\nPluginManager.Section.Manage=\"Instalatua\"\nPluginManager.Section.Updates=\"Eguneraketak\"\nPluginManager.Section.Manage.Title=\"Kudeatu gaitutako pluginak\"\n"
  },
  {
    "path": "frontend/data/locale/fa-IR.ini",
    "content": "Language=\"فارسی\"\nOK=\"تایید\"\nApply=\"اعمال\"\nCancel=\"لغو\"\nClose=\"بستن\"\nSave=\"ذخیره\"\nDiscard=\"صرف نظر\"\nDisable=\"غیرفعال\"\nYes=\"بله\"\nNo=\"خير\"\nAdd=\"افزودن\"\nRemove=\"برداشتن\"\nRename=\"تغییر نام\"\nInteract=\"تعامل\"\nFilters=\"پالایه‌ها\"\nProperties=\"ویژگی‌ها\"\nMoveUp=\"بالا بردن\"\nMoveDown=\"پایین بردن\"\nSettings=\"تنظیمات\"\nDisplay=\"صفحه نمایش\"\nName=\"نام\"\nExit=\"خروج\"\nMixer=\"تنظیمگر صدا\"\nBrowse=\"مرور\"\nMono=\"تک\"\nStereo=\"جفت\"\nDroppedFrames=\"قاب‌های افتاده %1 (%2٪)\"\nProjector.Open.Program=\"بازکردن پروژکتور برنامه\"\nProjector.Open.Preview=\"بازکردن پروژکتور پیش‌نمایش\"\nProjector.Open.Scene=\"بازکردن پروژکتور صحنه\"\nProjector.Open.Source=\"بازکردن پروژکتور منبع\"\nProjector.Open.Multiview=\"بازکردن نمای چندگانه\"\nProjector.Display=\"نمایش: %1\"\nProjector.Window=\"پنجره جدید\"\nProjector.Title=\"پروژکتور\"\nProjector.Title.Scene=\"صحنه: %1\"\nProjector.Title.Source=\"منبع: %1\"\nProjector.Title.Multiview=\"چندگانه\"\nProjector.ResizeWindowToContent=\"تناسب پنجره با محتوا\"\nClear=\"پاک‌سازی\"\nRevert=\"برگرداندن\"\nShow=\"نمایش\"\nHide=\"نهفتن\"\nUnhideAll=\"نانهفتن همه\"\nUntitled=\"بی‌‌عنوان\"\nNew=\"جدید\"\nDuplicate=\"ایجاد مشابه\"\nEnable=\"به کار انداختن\"\nDisableOSXVSync=\"از کار انداختن V-Sync مک‌اواس\"\nResetOSXVSyncOnExit=\"تنظیم مجدد macOS V-Sync هنگام خروج\"\nHighResourceUsage=\"کد گذاری بیش از حد! توجه کنید که تنظیمات ویدئویی را تغییر بدهید یا از یک پریست کد گذاری سریع تر استفاده کنید.\"\nTransition=\"انتقال\"\nQuickTransitions=\"جا به جایی سریع\"\nFadeToBlack=\"محو شدن به سمت سیاهی\"\nLeft=\"چپ\"\nRight=\"راست\"\nTop=\"بالا\"\nBottom=\"پایین\"\nReset=\"تنظیم مجدد\"\nHours=\"ساعت\"\nMinutes=\"دقیقه\"\nSeconds=\"ثانیه\"\nDeprecated=\"منسوخ شده\"\nReplayBuffer=\"بازپخشِ فوری\"\nImport=\"وارد کردن\"\nExport=\"خروجی گرفتن\"\nCopy=\"کپی\"\nPaste=\"جای گذاری\"\nPasteReference=\"چسبا (مرجع)\"\nPasteDuplicate=\"چسبا (تکرار)\"\nRemuxRecordings=\"ترکیب ضبط ها\"\nNext=\"بعدی\"\nBack=\"قبلی\"\nDefaults=\"پیش فرض ها\"\nRestoreDefaults=\"پیش‌فرض‌ها\"\nHideMixer=\"مخفی کردن در میکسر\"\nTransitionOverride=\"جلوه انتقال\"\nShowTransition=\"نمایش انتقال\"\nHideTransition=\"مخفی کردن انتقال\"\nNone=\"هیچ‌کدام\"\nStudioMode.Preview=\"پیش نمایش\"\nStudioMode.Program=\"برنامه\"\nStudioMode.PreviewSceneName=\"پیش‌نمایش: %1\"\nStudioMode.ProgramSceneName=\"برنامه: %1\"\nShowInMultiview=\"نمایش در چندپنجره\"\nVerticalLayout=\"چیدمان عمودی\"\nGroup=\"گروه\"\nDoNotShowAgain=\"دوباره نشان داده نشود\"\nDefault=\"(پیش فرض)\"\nCalculating=\"در حال محاسبه...\"\nFullscreen=\"تمام صفحه\"\nWindowed=\"پنجره\"\nRefreshBrowser=\"تازه سازی\"\nAspectRatio=\"نسبت ابعاد <b>%1:%2</b>\"\nLockVolume=\"قفل تنظیم صدا\"\nLogViewer=\"نمایشگر گزارش\"\nShowOnStartup=\"نمایش هنگام راه اندازی\"\nOpenFile=\"گشودن پرونده\"\nAddScene=\"افزودن صحنه\"\nAddSource=\"افزودن منبع\"\nRemoveScene=\"حذف صحنه انتخاب شده\"\nRemoveSource=\"حذف منبع(های) انتخاب شده\"\nMoveSceneUp=\"انتقال صحنه به بالا\"\nMoveSceneDown=\"انتقال صحنه به پایین\"\nMoveSourceUp=\"انتقال منبع(ها) به بالا\"\nMoveSourceDown=\"انتقال منبع(ها) به پایین\"\nSourceProperties=\"خصوصیات منبع باز\"\nSourceFilters=\"فیلتر های منبع باز\"\nMixerToolbarMenu=\"منوی ترکیب صدا\"\nSceneFilters=\"فیلترهای صحنه را باز کنید\"\nList=\"فهرست\"\nGrid=\"جدول\"\nAutomatic=\"خودکار\"\nComingSoon=\"بزودی\"\nPluginsFailedToLoad.Title=\"خطا بارگذاری افزونه\"\nPluginsFailedToLoad.Text=\"افزونه‌های OBS زیر بارگیری نشدند:\\n\\n%1\\nلطفاً این افزونه‌ها را به‌روزرسانی یا حذف کنید.\"\nAlreadyRunning.Title=\"برنامه OBS از قبل در حال اجراست\"\nAlreadyRunning.Text=\"برنامه OBS از قبل در حال اجراست! در صورتی که به صورت عمد این کار را کرده‌اید، لطفاً قبل از اجرای یک نمونه جدید سایر نمونه‌های باز OBS را ببندید. اگر OBS را در حالت کوچک کردن در نوار ابزار قرار داده‌اید، لطفاً بررسی کنید که آیا در آن جا هنوز در حال اجراست یا خیر.\"\nAlreadyRunning.LaunchAnyway=\"در هر حال اجرا کن\"\nCrashHandling.Dialog.Title=\"خرابی استودیو OBS شناسایی شد\"\nCrashHandling.Labels.Text=\"استودیو OBS به درستی خاموش نشد.\\n\\nدر حالت امن اجرا شود (افزونه‌های شخص ثالث، اسکریپت‌نویسی و سوکت‌های وب غیرفعال شوند)؟\"\nCrashHandling.Labels.PrivacyNotice=\"همچنین می‌توانید انتخاب کنید که جدیدترین گزارش خرابی به طور خودکار در پروژه OBS بارگذاری شود.<br /><br />لطفاً قبل از بارگذاری هرگونه فایل، <a href='https://obsproject.com/privacy-policy'>سیاست حفظ حریم خصوصی</a> را مطالعه کنید و به بخش‌های مربوط به بارگذاری فایل توجه ویژه داشته باشید.\"\nCrashHandling.Checkbox.SendReport=\"من سیاست حریم خصوصی را مطالعه کرده و برای آپلود رضایت دارم.\"\nCrashHandling.Buttons.LaunchSafe=\"اجرا در حالت ایمن\"\nCrashHandling.Buttons.LaunchNormal=\"اجرا در حالت عادی\"\nCrashHandling.Errors.UploadJSONError=\"در هنگام تلاش برای آپلود آخرین گزارش خرابی خطایی رخ داده است. لطفاً در زمان دیگری مجدداً تلاش کنید.\"\nCrashHandling.Errors.Title=\"خطا در آپلود گزارش خرابی\"\nSafeMode.Restart=\"آیا مایل هستید OBS را در حالت ایمن (غیر فعال بودن پلاگین های شخص ثالث، اسکریپت‌ها و وب‌سوکت‌ها) ری‌استارت کنید؟\"\nSafeMode.RestartNormal=\"آیا مایل هستید OBS را در حالت عادی ری‌استارت کنید؟\"\nChromeOS.Title=\"پلتفرم پشتیبانی نشده\"\nChromeOS.Text=\"به نظر می رسد OBS در داخل ظرف سیستم عامل Chrome در حال اجرا است. این سیستم عامل پشتیبانی نمی شود.\"\nWine.Title=\"Wine شناسایی شد\"\nWine.Text=\"اجرای OBS در واین پشتیبانی نمی‌شود و بسیاری از ویژگی‌ها مانند ضبط یا منابع افزاره کار نمی‌کنند یا فقط ظرفیت محدودی دارند.<br><br>توصیه می‌شود به جایش نگارشی بومی از OBS را اجرا کنید. برای مثال <a href='https://flathub.org/apps/details/com.obsproject.Studio'>نگارش فلت‌پکمان</a> یا بسته‌های سیستم‌عاملتان.\"\nDockCloseWarning.Title=\"بسته شدن پنجره قابل حمل\"\nDockCloseWarning.Text=\"شما فقط یک پنجره قابل اتصال را بستید. اگر می‌خواهید دوباره آن را نشان دهید، از منوی بارانداز در نوار منو استفاده کنید.\"\nExtraBrowsers=\"بخش مرورگر سفارشی\"\nExtraBrowsers.Info=\"با دادن نام و URL به آنها بخش مرورگر اضافه کنید ، سپس روی باز کردن یا بستن کلیک کنید تا بخش مرورگر باز شود. در هر زمان می توانید بخش مرورگر را اضافه یا حذف کنید.\"\nExtraBrowsers.DockName=\"نام بخش\"\nAuth.Authing.Title=\"در حال احراز هویت...\"\nAuth.Authing.Text=\"در حال احراز هویت با %1، لطفاً صبر کنید...\"\nAuth.AuthFailure.Title=\"احراز هویت ناموفق بود\"\nAuth.AuthFailure.Text=\"تأیید اعتبار انجام نشد %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"نیاز به تایید هویت\"\nAuth.InvalidScope.Text=\"الزامات تأیید اعتبار برای %1 تغییر کرده است. برخی از ویژگی ها ممکن است در دسترس نباشند.\"\nAuth.LoadingChannel.Title=\"بارگیری اطلاعات کانال...\"\nAuth.LoadingChannel.Text=\"در حال بارگیری اطلاعات کانال %1، لطفاً صبر کنید...\"\nAuth.LoadingChannel.Error=\"اطلاعات کانال دریافت نشد.\"\nAuth.ChannelFailure.Title=\"شکست در بار کردن کانال\"\nAuth.ChannelFailure.Text=\"شکست در بار کردن اطّلاعات کانال برای %1\\n\\n%2: %3\"\nAuth.Chat=\"گپ\"\nAuth.StreamInfo=\"اطلاعات جریان\"\nTwitchAuth.Stats=\"آمار توییچ\"\nTwitchAuth.Feed=\"خوراک فعّالیت‌های توییچ\"\nTwitchAuth.TwoFactorFail.Title=\"نتوانست کلید جریان را پرس‌وجو کند\"\nTwitchAuth.TwoFactorFail.Text=\"OBS قادر به اتصال به حساب Twitch شما نبود. لطفاً مطمئن شوید که احراز هویت دو مرحله‌ای در <a href='https://www.twitch.tv/settings/security'>تنظیمات امنیتی Twitch</a> تنظیم شده باشد، زیرا برای پخش جریانی لازم است.\"\nRestreamAuth.Channels=\"کانال‌های بازپخش\"\nCopy.Filters=\"رونوشت از پالایه‌ها\"\nPaste.Filters=\"چسباندن پالایه‌ها\"\nBrowserPanelInit.Title=\"مرورگر در حال شروع...\"\nBrowserPanelInit.Text=\"مرورگر در حال شروع، لطفاً صبر کنید...\"\nBandwidthTest.Region=\"منطقه\"\nBandwidthTest.Region.US=\"ایالات متحده\"\nBandwidthTest.Region.EU=\"اروپا\"\nBandwidthTest.Region.Asia=\"آسیا\"\nBandwidthTest.Region.Other=\"سایر\"\nBasic.AutoConfig=\"دستیار تنظیم خودکار\"\nBasic.AutoConfig.ApplySettings=\"اعمال تنظیمات\"\nBasic.AutoConfig.StartPage=\"اطّلاعات استفاده\"\nBasic.AutoConfig.StartPage.SubTitle=\"تعیین کنید که از برنامه برای چه کاری می‌خواهید استفاده کنید\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"بهینه‌سازی برای پخش، ضبط اولویت دوم است\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"فقط برای ضبط بهینه‌سازی می کنم، پخش زنده ندارم\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"من فقط از دوربین مجازی استفاده خواهم کرد\"\nBasic.AutoConfig.VideoPage=\"تنظیمات ویدئو\"\nBasic.AutoConfig.VideoPage.SubTitle=\"تنظیمات ویدئویی را که می خواهید استفاده کنید مشخص کنید\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"استفاده از فعلی (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"صفحه نمایش %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"استفاده از فعلی (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"هم ۶۰ و هم ۳۰ قابل قبول، اما درصورت امکان از ۶۰ استفاده کنید\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"یا ۶۰ یا ۳۰، اما وضوح بالا را ترجیح می دهم\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"نکته: اندازه بوم (پایه) لزوما همان اندازه‌ای نیست که با آن پخش یا ضبط می‌کنید . اندازه واقعی پخش/ضبط شما ممکن است برای کاهش استفاده از منابع و یا میزان درخواست بیت ریت کمتر باشد.\"\nBasic.AutoConfig.StreamPage=\"اطلاعات پخش زنده\"\nBasic.AutoConfig.StreamPage.SubTitle=\"لطفا اطلاعات پخش زنده خود را وارد کنید\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"اتصال حساب کاربری (توصیه می شود)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"قطع حساب\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"قطع حساب؟\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"این تغییر بلافاصله اعمال می شود. آیا مطمئن هستید که می خواهید از حساب کاربری خود خارج شوید?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"گرفتن کلید جریان\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"اطّلاعات بیش‌تر\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"استفاده از کلید جریان\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"استفاده از کلید جریان (پیشرفته)\"\nBasic.AutoConfig.StreamPage.Service=\"خدمت\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"نمایش همه...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"سفارشی...\"\nBasic.AutoConfig.StreamPage.Server=\"کارساز\"\nBasic.AutoConfig.StreamPage.StreamKey=\"کلید جریان\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: عبارت عبور رمزگذاری را وارد کنید.\\nRTMP: کلید ارائه شده توسط سرویس را وارد کنید.\\nSRT: در صورتی که سرویس از یکی استفاده می کند، streamid را وارد کنید.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"کلید رمزگذار\"\nBasic.AutoConfig.StreamPage.BearerToken=\"توکن رمز\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"حساب وصل شده\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"برآورد میزان بیت ریت بر ثانیه با تست پهنای باند (ممکن است چند دقیقه طول بکشد)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"کد گذاری سخت افزاری را ترجیح می دهم\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"رمزگذاری سخت افزاری مصرف زیاد CPU را از بین می برد، اما ممکن است برای بدست آوردن همان کیفیت کیفیت به بیت ریت بیشتری نیاز داشته باشد.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"هشدار جریان\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"تست پهنای باند در حال پخش داده های ویدیویی تصادفی بدون صدا به کانال شما است. در صورت امکان ، توصیه می شود فیلم های ذخیره شده به طور موقت را خاموش کنید و پخش زنده را به صورت خصوصی تنظیم کنید تا پس از اتمام آزمون. ادامه می دهید?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"تست %1\"\nBasic.AutoConfig.TestPage=\"نتایج نهایی\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"این برنامه اکنون مجموعه ای از تست ها را برای برآورد ایده آل ترین تنظیمات انجام می دهد\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"تست کامل شد\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"انجام آزمایش پهنای باند ، ممکن است چند دقیقه طول بکشد...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"هیچ خروجی برای پروتکل این سرویس یافت نشد\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"در حال اتصال به:%1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"اتصال به هیچ سروری انجام نشد ، لطفا ارتباط اینترنت خود را بررسی کنید و دوباره امتحان کنید.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"در حال تست پهنای باند برای: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"در حال امتحان کدگذاری پخش زنده، این عملیات ممکن است دقیقه‌ای طول بکشد...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"در حال تست انکودر ضبط، این عملیات ممکن است کمی طول بکشد...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"رمزگذار راه اندازی نشد\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"تست کردن %1x%2 %3 ف‌ب‌س...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"کدگذار پخش زنده\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"انکودر ضبط\"\nBasic.AutoConfig.TestPage.Result.Header=\"این برنامه تعیین کرده است که این تنظیمات تخمینی بهترین گزینه برای شما هستند:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"برای استفاده از این تنظیمات، روی اعمال تنظیمات کلیک کنید. برای پیکربندی مجدد جادوگر و دوباره امتحان کردن، روی بازگشت کلیک کنید. برای پیکربندی دستی تنظیمات خود، روی لغو کلیک کنید و تنظیمات را باز کنید.\"\nBasic.AutoConfig.Info=\"جادوگر پیکربندی خودکار بهترین تنظیمات را براساس مشخصات رایانه و سرعت اینترنت شما تعیین می کند.\"\nBasic.AutoConfig.RunAnytime=\"این کار در هر زمان با رفتن به منوی ابزار قابل اجرا است.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"رزولوشن استریم کردن (مقیاس شده)\"\nBasic.Stats=\"آمارها\"\nBasic.Stats.CPUUsage=\"میزان استفاده CPU\"\nBasic.Stats.HDDSpaceAvailable=\"فضای در دسترس دیسک\"\nBasic.Stats.MemoryUsage=\"میزان استفاده حافظه\"\nBasic.Stats.AverageTimeToRender=\"متوسط زمان ارائه فریم\"\nBasic.Stats.SkippedFrames=\"فریمها به دلیل تأخیر رمزگذاری رد شدند\"\nBasic.Stats.MissedFrames=\"فریم ها به دلیل تأخیر در رندر رد شدند\"\nBasic.Stats.Output.Stream=\"پخش زنده\"\nBasic.Stats.Output.Recording=\"درحال ضبط\"\nBasic.Stats.Status=\"وضعیت\"\nBasic.Stats.Status.Recording=\"درحال ضبط\"\nBasic.Stats.Status.Live=\"زنده\"\nBasic.Stats.Status.Reconnecting=\"اتصال مجدد\"\nBasic.Stats.Status.Inactive=\"غیر فعال\"\nBasic.Stats.Status.Active=\"فعال\"\nBasic.Stats.DroppedFrames=\"فریم های از دست رفته (شبکه)\"\nBasic.Stats.MegabytesSent=\"مجموع خروجی داده ها\"\nBasic.Stats.Bitrate=\"بیت ریت\"\nBasic.Stats.DiskFullIn=\"دیسک کامل (تقریباً)\"\nBasic.Stats.DiskFullIn.Text=\"%1 ساعت، %2 دقیقه\"\nBasic.Stats.ResetStats=\"تنظیم مجدد آمار\"\nResetUIWarning.Title=\"آیا مطمئنید که می‌خواهید رابط کاربری را مجدداً تنظیم کنید؟\"\nResetUIWarning.Text=\"بازنشانی رابط کاربری، پایه‌های اضافی را پنهان می‌کند. اگر می‌خواهید این پایه‌ها قابل مشاهده باشند، باید از منوی بارانداز پنهان شوند.\\n\\nآیا مطمئن هستید که می‌خواهید رابط کاربری را بازنشانی کنید؟\"\nUpdater.Title=\"بروزرسانی جدید موجود است\"\nUpdater.Text=\"نسخه جدیدی در دسترس است:\"\nUpdater.UpdateNow=\"اکنون بروزرسانی شود\"\nUpdater.RemindMeLater=\"بعدا یادآوری کن\"\nUpdater.Skip=\"رد شدن از نسخه\"\nUpdater.NoUpdatesAvailable.Title=\"بروزرسانی موجود نیست\"\nUpdater.NoUpdatesAvailable.Text=\"در حال حاظر بروزرسانی در دسترس نیست\"\nUpdater.BranchNotFound.Title=\"به روز رسانی کانال حذف شد\"\nUpdater.BranchNotFound.Text=\"کانال به‌روزرسانی انتخابی شما دیگر در دسترس نیست، OBS به حالت پیش‌فرض بازنشانی شده است.\"\nUpdater.RepairButUpdatesAvailable.Title=\"بررسی یکپارچگی در دسترس نیست\"\nUpdater.RepairButUpdatesAvailable.Text=\"بررسی یکپارچگی فایل فقط برای آخرین نسخه موجود امکان‌پذیر است. برای تأیید و به‌روزرسانی نصب OBS، از کمک → بررسی بروزرسانی استفاده کنید.\"\nUpdater.RepairConfirm.Title=\"بررسی یکپارچگی را تأیید کنید\"\nUpdater.RepairConfirm.Text=\"شروع بررسی یکپارچگی، نصب OBS شما را از نظر خرابی اسکن می کند و فایل های شکسته/تغییر شده را دوباره دانلود می کند. ممکن است کمی طول بکشد.\\n\\nآیا می‌خواهید ادامه دهید؟\"\nUpdater.FailedToLaunch=\"اجرای بروزرسانی کننده ناموفق بود\"\nQuickTransitions.SwapScenes=\"پیش‌نمایش/صحنه‌های برنامه را پس از انتقال عوض کنید\"\nQuickTransitions.SwapScenesTT=\"پیش‌نمایش و صحنه‌های برنامه را پس از انتقال عوض می‌کند (اگر صحنه اصلی برنامه هنوز وجود داشته باشد).\\nبا این کار هیچ تغییری که ممکن است در صحنه اصلی برنامه ایجاد شده باشد، لغو نمی‌شود.\"\nQuickTransitions.DuplicateScene=\"صحنه تکراری\"\nQuickTransitions.DuplicateSceneTT=\"هنگام ویرایش همان صحنه، امکان ویرایش تبدیل/مشاهده منابع را بدون تغییر خروجی برنامه فراهم می‌کند.\\nبرای ویرایش ویژگی‌های منابع بدون تغییر خروجی برنامه، «منابع تکراری» را فعال کنید.\\nتغییر این مقدار صحنه برنامه فعلی را بازنشانی می‌کند (اگر هنوز وجود دارد).\"\nQuickTransitions.EditProperties=\"منابع تکراری\"\nQuickTransitions.EditPropertiesTT=\"هنگام ویرایش یک صحنه، امکان ویرایش ویژگی‌های منابع را بدون تغییر خروجی برنامه فراهم می‌کند.\\nفقط در صورتی می‌توان از آن استفاده کرد که «صحنه تکراری» فعال باشد.\\nبرخی منابع (مانند منابع ضبط یا رسانه) این را پشتیبانی نمی‌کنند و نمی‌توان آنها را ویرایش کرد. به طور جداگانه.\\nتغییر این مقدار صحنه برنامه فعلی را بازنشانی می کند (اگر هنوز وجود داشته باشد).\\n\\nهشدار: از آنجایی که منابع تکراری خواهند بود، ممکن است به منابع سیستم یا ویدیوی اضافی نیاز داشته باشد.\"\nQuickTransitions.HotkeyName=\"ترنزیشن سریع: %1\"\nBasic.AddTransition=\"اضافه کردن انتقال قابل تنظیم\"\nBasic.RemoveTransition=\"حذف انتقال قابل تنظیم\"\nBasic.TransitionProperties=\"ویژگی‌های انتقال\"\nBasic.SceneTransitions=\"انتقال صحنه‌ها\"\nBasic.TransitionDuration=\"مدت زمان\"\nBasic.TogglePreviewProgramMode=\"حالت استودیو\"\nBasic.EnablePreviewProgramMode=\"فعال کردن حالت استودیو\"\nBasic.DisablePreviewProgramMode=\"غیر‌فعال کردن حالت استودیو\"\nUndo.Undo=\"بازگرد\"\nUndo.Redo=\"مجدد\"\nUndo.Add=\"اضافه \\\"%1\\\"\"\nUndo.Delete=\"حذف \\\"%1\\\"\"\nUndo.Rename=\"تغییر نام \\\"%1\\\"\"\nUndo.SceneCollection.Switch=\"تغییر به \\\"%1\\\"\"\nUndo.Item.Undo=\"برگرداندن %1\"\nUndo.Item.Redo=\"مجدد %1\"\nUndo.Sources.Multi=\"حذف %1 منبع\"\nUndo.Filters=\"تغییرات پالایه روی «%1»\"\nUndo.Filters.Paste.Single=\"چسباندن پالایه «%1» به «%2»\"\nUndo.Filters.Paste.Multiple=\"رونوشت پالایه‌ها از «%1» به «%2»\"\nUndo.Transform=\"تبدیل منبع (ها) در \\\"%1\\\"\"\nUndo.Transform.Paste=\"جایگذاری انتقال در \\\"%1\\\"\"\nUndo.Transform.Rotate=\"چرخش در \\\"%1\\\"\"\nUndo.Transform.Reset=\"تغییر انتقال در \\\"%1\\\"\"\nUndo.Transform.HFlip=\"تلنگر افقی در \\\"%1\\\"\"\nUndo.Transform.VFlip=\"تلنگر عمودی در \\\"%1\\\"\"\nUndo.Transform.FitToScreen=\"متناسب با صفحه نمایش در \\\"%1\\\"\"\nUndo.Transform.StretchToScreen=\"امتداد برای نمایش در \\\"%1\\\"\"\nUndo.Transform.Center=\"مرکز برای نمایش در \\\"%1\\\"\"\nUndo.Transform.VCenter=\"مرکز عمودی برای نمایش در \\\"%1\\\"\"\nUndo.Transform.HCenter=\"مرکز افقی برای نمایش در \\\"%1\\\"\"\nUndo.Volume.Change=\"تغییر میزان صدا در \\\"%1\\\"\"\nUndo.Volume.Mute=\"بی صدا \\\"%1\\\"\"\nUndo.Volume.Unmute=\"صدادار \\\"%1\\\"\"\nUndo.Balance.Change=\"تغییر تراز صوتی در \\\"%1\\\"\"\nUndo.SyncOffset.Change=\"تغییر افست همگام سازی صوتی در \\\"%1\\\"\"\nUndo.MonitoringType.Change=\"تغییر نظارت صوتی در \\\"%1\\\"\"\nUndo.Mixers.Change=\"تغییر میکسرهای صوتی در \\\"%1\\\"\"\nUndo.ForceMono.On=\"فعال کردن اجباری مونو در \\\"%1\\\"\"\nUndo.ForceMono.Off=\"غیرفعال کردن اجباری مونو در \\\"%1\\\"\"\nUndo.Properties=\"تغییر ویژگی در \\\"%1\\\"\"\nUndo.Scene.Duplicate=\"صحنه تکراری \\\"%1\\\"\"\nUndo.ShowTransition=\"نمایش انتقال در '%1'\"\nUndo.HideTransition=\"نمایش/مخفی کردن انتقال در '%1'\"\nUndo.ShowSceneItem=\"نمایش \\\"%1\\\" در \\\"%2\\\"\"\nUndo.HideSceneItem=\"مخفی کردن \\\"%1\\\" در \\\"%2\\\"\"\nUndo.ReorderSources=\"مرتب سازی مجدد منابع در \\\"%1\\\"\"\nUndo.MoveUp=\"انتقال \\\"%1\\\" به بالا در \\\"%2\\\"\"\nUndo.MoveDown=\"انتقال \\\"%1\\\" به پایین در \\\"%2\\\"\"\nUndo.MoveToTop=\"انتقال \\\"%1\\\" به بالا در \\\"%2\\\"\"\nUndo.MoveToBottom=\"انتقال \\\"%1\\\" به پایین در \\\"%2\\\"\"\nUndo.PasteSource=\"چسباندن منبع (ها) در \\\"%1\"\nUndo.PasteSourceRef=\"چسباندن مرجع (های) منبع در \\\"%1\\\"\"\nUndo.GroupItems=\"موارد را در \\\"%1\\\" گروه بندی کنید\"\nTransitionNameDlg.Text=\"لطفا نام انتقال را وارد کنید\"\nTransitionNameDlg.Title=\"نام انتقال\"\nTitleBar.SafeMode=\"حالت ایمن\"\nTitleBar.PortableMode=\"حالت قابل حمل\"\nTitleBar.Profile=\"نمایه\"\nTitleBar.Scenes=\"صحنه ها\"\nNameExists.Title=\"نام از قبل وجود دارد\"\nNameExists.Text=\"نام از قبل در حال استفاده است.\"\nNoNameEntered.Title=\"لطفاً یک نام معتبر وارد کنید\"\nNoNameEntered.Text=\"شما نمی توانید از نام های خالی استفاده کنید.\"\nConfirmStart.Title=\"شروع پخش؟\"\nConfirmStart.Text=\"آیا مطمئنید که می‌خواهید پخش را شروع کنید؟\"\nConfirmStop.Title=\"قطع پخش؟\"\nConfirmStop.Text=\"آیا مطمئنید که می‌خواهید پخش را قطع کنید؟\"\nConfirmStopRecord.Title=\"توقف ضبط؟\"\nConfirmStopRecord.Text=\"مطمئن هستید که می خواهید ضبط کردن را متوقف کنید؟\"\nConfirmBWTest.Title=\"شروع تست پهنای باند؟\"\nConfirmBWTest.Text=\"شما OBS را در حالت آزمایش پهنای باند پیکربندی کرده اید. این حالت امکان آزمایش شبکه بدون فعال شدن کانال شما را فراهم می کند. پس از پایان آزمایش ، برای اینکه بینندگان بتوانند جریان شما را ببینند ، باید آن را غیرفعال کنید.\\n\\nآیا می خواهید ادامه دهید?\"\nConfirmExit.Title=\"خروجی‌های فعال\"\nConfirmExit.Text=\"OBS هنوز فعال است. تمام پخش‌ها/ضبط‌ها متوقف خواهند شد.\"\nConfirmRemove.Title=\"تایید حذف\"\nConfirmRemove.Text=\"آیا مطمئنید که می خواهید «%1» را حذف کنید؟\"\nConfirmRemove.TextMultiple=\"آیا مطمئن هستید که می‌خواهید %1 آیتم را حذف کنید؟\"\nConfirmReset.Title=\"بازنشانی ویژگی ها\"\nConfirmReset.Text=\"آیا مطمئن هستید که می خواهید ویژگی های فعلی را به پیش فرض های خود بازنشانی کنید؟\"\nOutput.StartStreamFailed=\"شروع پخش ناموفق بود\"\nOutput.StartRecordingFailed=\"شروع ضبط ناموفق بود\"\nOutput.StartReplayFailed=\"بافر پخش دوباره شروع نشد\"\nOutput.StartVirtualCamFailed=\"شکست در راه‌اندازی دوربین مجازی\"\nOutput.StartFailedGeneric=\"شروع خروجی ناموفق بود. لطفاً جزئیات گزارش را چک کنید.\\n\\nتوجه: اگر از انکودرهای NVENC یا AMD استفاده می کنید، مطمئن شوید که درایورهای ویدئویی شما به روز هستند.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"ذخیره مجدد هنگام مکث امکان پذیر نیست\"\nOutput.ReplayBuffer.PauseWarning.Text=\"هشدار: هنگام مکث ضبط، امکان بارگیری مجدد وجود ندارد.\"\nOutput.ConnectFail.Title=\"اتصال ناموفق بود\"\nOutput.ConnectFail.BadPath=\"آدرس اینترنتی یا مسیر اتصال نامعتبر است. لطفاً تنظیمات خود را بررسی کنید تا معتبر بودن آنها را تأیید کنید.\"\nOutput.ConnectFail.ConnectFailed=\"اتصال به سرور ممکن نشد\"\nOutput.ConnectFail.InvalidStream=\"نمی توانید به کانال مشخص شده یا کلید پخش زنده دسترسی داشته باشید ، لطفاً کلید جریان خود را دوباره بررسی کنید. اگر صحیح باشد ، ممکن است مشکلی در اتصال به سرور وجود داشته باشد.\"\nOutput.ConnectFail.HdrDisabled=\"خروجی HDR در حال حاضر برای این خروجی غیرفعال است.\"\nOutput.ConnectFail.Error=\"هنگام تلاش برای اتصال به سرور ، یک خطای غیرمنتظره رخ داد. اطلاعات بیشتر در پرونده لاگ.\"\nOutput.ConnectFail.Disconnected=\"اتصال به سرور قطع شد.\"\nOutput.StreamEncodeError.Title=\"خطای رمزگذاری\"\nOutput.StreamEncodeError.Msg=\"هنگام پخش خطایی در انکودر رخ داد.\"\nOutput.StreamEncodeError.Msg.LastError=\"هنگام پخش جریانی یک خطای رمزگذار روی داد:<br><br>%1\"\nOutput.RecordFail.Title=\"شروع ضبط ناموفق بود\"\nOutput.RecordFail.Unsupported=\"فرمت خروجی پشتیبانی نمی شود یا بیش از یک لایه صوتی را پشتیبانی نمی کند. لطفاً تنظیمات خود را بررسی کرده و دوباره امتحان کنید.\"\nOutput.RecordNoSpace.Title=\"فضای ناکافی دیسک\"\nOutput.RecordNoSpace.Msg=\"فضای دیسک کافی برای ادامه ضبط وجود ندارد.\"\nOutput.RecordError.Title=\"خطای ضبط\"\nOutput.RecordError.Msg=\"هنگام ضبط خطای نامشخص رخ داده است.\"\nOutput.RecordError.EncodeErrorMsg=\"هنگام ضبط خطایی در انکودر رخ داده است.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"در هنگام ضبط یک خطای انکودر رخ داده است: <br><br>%1\"\nOutput.BadPath.Title=\"مسیر پروندهٔ بد\"\nOutput.BadPath.Text=\"مسیر ضبط پیکربندی شده باز نشد. لطفاً مسیر ضبط خود را در قسمت تنظیمات → خروجی → ضبط بررسی کنید.\"\nOutput.NoBroadcast.Title=\"پخش تنظیم نشده\"\nOutput.NoBroadcast.Text=\"قبل از شروع پخش زنده، باید آن را تنظیم کنید.\"\nOutput.BroadcastStartFailed=\"شروع پخش ناموفق بود\"\nOutput.BroadcastStopFailed=\"پخش متوقف نشد\"\nLogUploadDialog.Title=\"آپلود فایل لاگ OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"لطفاً <a href='https://obsproject.com/privacy-policy'>سیاست حریم خصوصی</a> و بخش مربوط به آپلود فایل در آن را قبل از آپلود هر فایلی، مطالعه کنید.\"\nLogUploadDialog.Labels.Progress=\"آپلود لاگ در حال انجام است. لطفاً صبر کنید...\"\nLogUploadDialog.Labels.Description.AppLog=\"فایل لاگ شما آپلود شد. شما هم اکنون می‌توانید آدرس آن را برای عیب‌یابی یا استفاده های پشتیبانی به اشتراک بگذارید.\"\nLogUploadDialog.Labels.Description.CrashLog=\"گزارش خرابی شما آپلود شد. شما هم اکنون می‌توانید آدرس آن را برای استفاده های عیب‌یابی به اشتراک بگذارید.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"آپلود\"\nLogUploadDialog.Buttons.CopyURL=\"کپی کردن آدرس لاگ\"\nLogUploadDialog.Buttons.AnalyzeURL=\"بررسی فایل لاگ\"\nLogUploadDialog.Buttons.RetryButton=\"تلاش مجدد\"\nLogUploadDialog.Errors.Template=\"در هنگام تلاش برای آپلود فایل خطایی رخ داده است:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"فایلی برای آپلود پیدا نشد یا فایل خالی بوده است.\"\nRemux.SourceFile=\"ضبط کننده او بی اس\"\nRemux.TargetFile=\"پروندهٔ مقصد\"\nRemux.Remux=\"بازسازی\"\nRemux.Stop=\"توقف بازسازی\"\nRemux.ClearFinished=\"پاک کردن موارد تمام شده\"\nRemux.ClearAll=\"پاک کردن آیتم ها\"\nRemux.OBSRecording=\"ضبط کننده او بی اس\"\nRemux.FinishedTitle=\"بازسازی به پایان رسید\"\nRemux.Finished=\"ضبط کردن بازسازی شد\"\nRemux.FinishedError=\"ضبط کردن بازسازی شد، اما ممکن است پرونده ناقص باشد\"\nRemux.SelectRecording=\"انتخاب ضبط OBS...\"\nRemux.SelectTarget=\"گزینش پروندهٔ مقصد…\"\nRemux.FileExistsTitle=\"پروندهٔ مقصد وجود دارد\"\nRemux.FileExists=\"فایل های زیر از قبل وجود دارند. آیا می‌خوهید آن‌ها را جایگزین کنید؟\"\nRemux.ExitUnfinishedTitle=\"بازسازی در حال انجام\"\nRemux.ExitUnfinished=\"بازسازی به پایان نرسیده است ، توقف اکنون ممکن است فایل هدف را غیر قابل استفاده کند.\\nآیا مطمئن هستید که می خواهید بازسازی را متوقف کنید?\"\nRemux.HelpText=\"پرونده ها را در این پنجره رها کنید تا دوباره اصلاح شود، یا یک سلول خالی \\\"ضبط OBS\\\" را انتخاب کنید تا یک فایل را مرور کنید.\"\nRemux.NoFilesAddedTitle=\"هیچ فایل ریموکس اضافه نشده است\"\nRemux.NoFilesAdded=\"هیچ فایلی به ریموکس اضافه نشده است. یک پوشه حاوی یک یا چند فایل ویدیویی در آن بگذارید.\"\nMissingFiles=\"پرونده ها وجود ندارد\"\nMissingFiles.MissingFile=\"پرونده موجود نیست\"\nMissingFiles.NewFile=\"پروندهٔ جدید\"\nMissingFiles.HelpText=\"برخی پرونده ها از آخرین باری که شما از OBS استفاده کرده اید گم شده اند.\"\nMissingFiles.NumFound=\"%1 از %2 یافت شد\"\nMissingFiles.Search=\"فهرست جستجو...\"\nMissingFiles.SelectFile=\"گزینش پرونده…\"\nMissingFiles.SelectDir=\"پوشه را برای جستجو در آن انتخاب کنید\"\nMissingFiles.State=\"حالت\"\nMissingFiles.Missing=\"گمشده\"\nMissingFiles.Replaced=\"جایگزین شده\"\nMissingFiles.Cleared=\"پاک شد\"\nMissingFiles.Found=\"یافت شد\"\nMissingFiles.AutoSearch=\"موارد اضافی پرونده پیدا شد\"\nMissingFiles.AutoSearchText=\"OBS موارد منطبق دیگری را برای پرونده های گمشده در آن فهرست پیدا کرده است. آیا می خواهید آنها را اضافه کنید؟\"\nMissingFiles.NoMissing.Title=\"بررسی فایل های گمشده\"\nMissingFiles.NoMissing.Text=\"به نظر می رسد هیچ فایلی گم نشده است.\"\nMacPermissions.MenuAction=\"بازبینی اجازه‌های دسترسی اپلیکیشن...\"\nMacPermissions.Title=\"بازبینی اجازه‌های دسترسی اپلیکیشن\"\nMacPermissions.Description=\"OBS Studio برای اینکه بتواند ویژگی های خاصی را ارائه دهد به اجازه شما نیاز دارد. توصیه می شود این مجوزها را فعال کنید، اما برای استفاده از برنامه نیازی به آنها نیست. همیشه می توانید بعداً آنها را فعال کنید.\"\nMacPermissions.Description.OpenDialog=\"می توانید این گفتگو را از طریق منوی OBS Studio دوباره باز کنید.\"\nMacPermissions.AccessGranted=\"دسترسی ممکن\"\nMacPermissions.RequestAccess=\"درخواست دسترسی\"\nMacPermissions.OpenPreferences=\"بازکردن %1 تنظیمات\"\nMacPermissions.Item.ScreenRecording=\"ضبط صفحه\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS برای اینکه بتواند از صفحه شما عکس بگیرد به این مجوز نیاز دارد.\"\nMacPermissions.Item.Camera=\"دوربین\"\nMacPermissions.Item.Camera.Details=\"این مجوز برای ضبط محتوا از وب‌کم یا کارت ضبط مورد نیاز است.\"\nMacPermissions.Item.Microphone=\"صدابَر\"\nMacPermissions.Item.Microphone.Details=\"در صورتی که می‌خواهید صدای میکروفون خود و یا یک دستگاه صوتی خارجی را ضبط کنید، OBS به این دسترسی نیاز دارد.\"\nMacPermissions.Item.InputMonitoring=\"نظارت بر ورودی\"\nMacPermissions.Item.InputMonitoring.Details=\"این مجوز برای کار کردن کلیدهای میانبر در حین اجرای OBS در پس‌زمینه لازم است.\"\nMacPermissions.Item.Accessibility=\"قابلیت دسترسی\"\nMacPermissions.Item.Accessibility.Details=\"در نسخه‌های قدیمی‌تر، OBS ممکن است به جای «نظارت بر ورودی» در «دسترسی» فهرست شده باشد.\"\nMacPermissions.Continue=\"ادامه\"\nSourceLeak.Title=\"خطای پاک‌سازی منبع\"\nSourceLeak.Text=\"در خلال تغییر مجموعه‌های صحنه مشکلی پیش آمد و برخی منابع به درستی حذف نشدند. این مشکل معمولا به واسطه افزونه‌هایی پیش می‌آید که به درستی منابع را آزاد نمی‌کنند. لطفا اطمینان حاصل کنید که همه افزونه‌هایی که استفاده می‌کنید به‌روز باشند.\\n\\nاستودیوی BBS به منظور جلوگیری از هرگونه احتمال خرابی داده‌ها، از برنامه خارج نمی‌شود.\"\nBasic.DesktopDevice1=\"صدای دسکتاپ\"\nBasic.DesktopDevice2=\"صدای دسکتاپ 2\"\nBasic.AuxDevice1=\"صدابَر / Aux\"\nBasic.AuxDevice2=\"صدابَر / Aux2\"\nBasic.AuxDevice3=\"صدابَر / Aux3\"\nBasic.AuxDevice4=\"صدابَر / Aux4\"\nBasic.Scene=\"صحنه‌ها\"\nBasic.DisplayCapture=\"ضبط صفحه نمایش\"\nBasic.Main.PreviewConextMenu.Enable=\"فعال سازی پیش‌نمایش\"\nBasic.Main.Preview.Disable=\"غیرفعال سازی پیش‌نمایش\"\nScaleFiltering=\"پالایهٔ مقیاس\"\nScaleFiltering.Point=\"نقطه\"\nScaleFiltering.Bilinear=\"دو خطی\"\nScaleFiltering.Bicubic=\"دو مکعبی\"\nScaleFiltering.Lanczos=\"لانچوس\"\nScaleFiltering.Area=\"ناحیه\"\nBlendingMethod=\"روش مخلوط کردن\"\nBlendingMethod.Default=\"پیش‌فرض\"\nBlendingMethod.SrgbOff=\"SRGB خاموش\"\nBlendingMode=\"حالت آمیختگی\"\nBlendingMode.Normal=\"عادی\"\nBlendingMode.Additive=\"افزودن\"\nBlendingMode.Subtract=\"کاهنده\"\nBlendingMode.Screen=\"صفحه نمایش\"\nBlendingMode.Multiply=\"ضرب\"\nBlendingMode.Lighten=\"روشن‌کننده\"\nBlendingMode.Darken=\"تاریک‌کننده\"\nDeinterlacing=\"ضد انعطاف پذیری\"\nDeinterlacing.Discard=\"دور انداختن\"\nDeinterlacing.TopFieldFirst=\"ابتدا فیلد برتر\"\nDeinterlacing.BottomFieldFirst=\"ابتدا فیلد پایین\"\nVolControl.SliderUnmuted=\"لغزنده میزان صدا برای \\\"%1\\\":\"\nVolControl.SliderMuted=\"لغزنده میزان صدا برای \\\"%1\\\": (فعلا بی صدا)\"\nVolControl.Mute=\"بی صدا \\\"%1\\\"\"\nVolControl.Properties=\"ویژگیهای \\\"%1\\\"\"\nVolControl.UnassignedWarning.Title=\"منبع صوتی اختصاص نیافته\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" به هیچ ترک صوتی اختصاص داده نشده است و در جریان‌ها یا ضبط‌ها شنیده نمی‌شود.\\n\\nبرای اختصاص یک منبع صوتی به یک ترک، به ویژگی‌های پیشرفته صوتی از طریق منوی راست کلیک یا دکمه چرخ‌دنده در نوار ابزار میکسر دسترسی پیدا کنید.\"\nBasic.Main.AddSceneDlg.Title=\"اضافه کردن صحنه\"\nBasic.Main.AddSceneDlg.Text=\"لطفا نامی را برای این صحنه انتخاب کنید\"\nBasic.Main.DefaultSceneName.Text=\"صحنه %1\"\nBasic.Main.AddSceneCollection.Title=\"اضافه کردن مجموعه صحنه\"\nBasic.Main.AddSceneCollection.Text=\"لطفاً نام مجموعه صحنه را وارد کنید\"\nBasic.Main.RenameSceneCollection.Title=\"تغییر نام مجموعه صحنه\"\nBasic.Main.RemigrateSceneCollection.Title=\"به روز رسانی وضوح مجموعه صحنه\"\nBasic.Main.RemigrateSceneCollection.Text=\"آیا می خواهید وضوح مجموعه صحنه \\\"%1\\\" را برای مطابقت با وضوح تصویر نمایه فعلی %2x%3 به روز کنید؟\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"هنگامی که خروجی ها فعال هستند، وضوح مجموعه صحنه را نمی توان به روز کرد.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"وضوح مجموعه صحنه به روز نشد. وضوح اصلی ناشناخته است.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"بازنشانی ممکن نیست: تغییر وضوح OBS انجام نشد.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"تنظیم مجدد امکان پذیر نیست: وضوح فعلی در حال حاضر وضوح پایه مجموعه صحنه است.\"\nAddProfile.Title=\"افزودن پروفایل\"\nAddProfile.Text=\"لطفاً نام نمایه را وارد کنید\"\nAddProfile.WizardCheckbox=\"نمایش جادوگر پیکربندی خودکار\"\nRenameProfile.Title=\"تغییر نام پروفایل\"\nBasic.Main.MixerRename.Title=\"تغییر نام منبع صوتی\"\nBasic.Main.MixerRename.Text=\"لطفاً نام منبع صوتی را وارد کنید\"\nBasic.Main.PreviewDisabled=\"پیش نمایش در حال حاضر غیرفعال است\"\nBasic.SourceSelect=\"ایجاد / انتخاب منبع\"\nBasic.SourceSelect.CreateNew=\"ایجاد جدید\"\nBasic.SourceSelect.AddExisting=\"اضافه کردن موجود\"\nBasic.SourceSelect.AddVisible=\"سورس را قابل رویت کن\"\nBasic.Main.Sources.Visibility=\"قابلیت نمایش\"\nBasic.Main.Sources.VisibilityDescription=\"دید '%1' در کانواس را کنترل میکند\"\nBasic.Main.Sources.Lock=\"قفل\"\nBasic.Main.Sources.LockDescription=\"موقعیت و مقیاس '%1' را در کانواس نشان می‌دهد\"\nBasic.PropertiesWindow=\"ویژگیها برای '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (انتخاب خودکار: %2)\"\nBasic.PropertiesWindow.SelectColor=\"انتخاب رنگ\"\nBasic.PropertiesWindow.SelectFont=\"انتخاب فونت\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"یک فونت را انتخاب کنید\"\nBasic.PropertiesWindow.ConfirmTitle=\"تنظیمات تغییر یافت\"\nBasic.PropertiesWindow.Confirm=\"تغییرات ذخیره نشده‌ای وجود دارند، آیا می‌خواهید آن‌ها را نگه دارید؟\"\nBasic.PropertiesWindow.NoProperties=\"ویژگی موجود نیست\"\nBasic.PropertiesWindow.AddFiles=\"افزودن پرونده‌ها\"\nBasic.PropertiesWindow.AddDir=\"افزودن پوشه\"\nBasic.PropertiesWindow.AddURL=\"اضافه کردن مسیر/آدرس اینترنتی\"\nBasic.PropertiesWindow.AddEditableListDir=\"اضافه کردن فهرست راهنما به '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"افزودن پرونده‌ها به «%1»\"\nBasic.PropertiesWindow.AddEditableListEntry=\"اضافه کردن ورودی به '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"تغییر دادن ورودی از '%1'\"\nBasic.PropertiesView.FPS.Simple=\"مقادیر ساده fps\"\nBasic.PropertiesView.FPS.Rational=\"مقادیر اعشاری fps\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"محدود های درست fps:\"\nBasic.PropertiesView.UrlButton.Text=\"این پیوند را در مرورگر وب پیش فرض خود باز می کنید?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"نشانی: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"گشودن نشانی\"\nBasic.InteractionWindow=\"تعامل با '%1'\"\nBasic.StatusBar.Reconnecting=\"اتصال قطع شده،اتصال دوباره در %2 ثانیه (تلاش شماره %1)\"\nBasic.StatusBar.AttemptingReconnect=\"تلاش برای اتصال دوباره... (تلاش شماره %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"اتصال دوباره موفق\"\nBasic.StatusBar.Delay=\"تاخیر (%1 ثانیه)\"\nBasic.StatusBar.DelayStartingIn=\"تاخیر (شروع در %1 ثانیه)\"\nBasic.StatusBar.DelayStoppingIn=\"تاخیر ( توقف در %1 ثانیه)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"تاخیر (توقف در %1 ثانیه، شروع در %2 ثانیه)\"\nBasic.StatusBar.RecordingSavedTo=\"ضبط در \\\"%1\\\" ذخیره شد\"\nBasic.StatusBar.ReplayBufferSavedTo=\"بافر بازپخش در \\\"%1\\\" ذخیره شد\"\nBasic.StatusBar.ScreenshotSavedTo=\"عکس صفحه در \\\"%1\\\" ذخیره شد\"\nBasic.StatusBar.AutoRemuxedTo=\"ضبط در \\\"%1\\\" بازسازی خودکار شد\"\nBasic.Filters=\"پالایه‌ها\"\nBasic.Filters.AsyncFilters=\"فلیتر های صوتی/ویدیویی\"\nBasic.Filters.AudioFilters=\"پالایه‌های صوتی\"\nBasic.Filters.EffectFilters=\"پالایه‌های جلوه ویژه\"\nBasic.Filters.Title=\"پالایه‌های «%1»\"\nBasic.Filters.AddFilter.Title=\"نام پالایه\"\nBasic.Filters.AddFilter.Text=\"لطفاً نام پالایه را مشخص کنید\"\nBasic.TransformWindow=\"تبدیل مورد صحنه\"\nBasic.TransformWindow.Position=\"موقعیت\"\nBasic.TransformWindow.PositionX=\"موقعیت X\"\nBasic.TransformWindow.PositionY=\"موقعیت Y\"\nBasic.TransformWindow.Rotation=\"چرخش\"\nBasic.TransformWindow.Size=\"اندازه\"\nBasic.TransformWindow.Width=\"عرض\"\nBasic.TransformWindow.Height=\"ارتفاع\"\nBasic.TransformWindow.Alignment=\"تراز مکانی\"\nBasic.TransformWindow.BoundsType=\"سبک جعبه محدود کننده\"\nBasic.TransformWindow.BoundsAlignment=\"هم ترازی در جعبه محدود کننده\"\nBasic.TransformWindow.Bounds=\"اندازه جعبه محدود کننده\"\nBasic.TransformWindow.BoundsWidth=\"عرض جعبه محدود\"\nBasic.TransformWindow.BoundsHeight=\"ارتفاع جعبه محدود\"\nBasic.TransformWindow.CropToBounds=\"برش به جعبه مرزی\"\nBasic.TransformWindow.Crop=\"بریدن\"\nBasic.TransformWindow.CropLeft=\"برش چپ\"\nBasic.TransformWindow.CropRight=\"برش راست\"\nBasic.TransformWindow.CropTop=\"برش بالا\"\nBasic.TransformWindow.CropBottom=\"برش پایین\"\nBasic.TransformWindow.Alignment.TopLeft=\"بالا سمت چپ\"\nBasic.TransformWindow.Alignment.TopCenter=\"بالا وسط\"\nBasic.TransformWindow.Alignment.TopRight=\"بالا سمت راست\"\nBasic.TransformWindow.Alignment.CenterLeft=\"مرکز سمت چپ\"\nBasic.TransformWindow.Alignment.Center=\"مرکز\"\nBasic.TransformWindow.Alignment.CenterRight=\"مرکز سمت راست\"\nBasic.TransformWindow.Alignment.BottomLeft=\"پایین سمت چپ\"\nBasic.TransformWindow.Alignment.BottomCenter=\"مرکز پایین\"\nBasic.TransformWindow.Alignment.BottomRight=\"پایین سمت راست\"\nBasic.TransformWindow.BoundsType.None=\"بدون محدودیت\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"فقط بیشترین اندازه\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"مقیاس تا مرزهای داخلی\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"مقیاس تا مرزهای خارجی\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"مقیاس تا عرض مرزها\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"مقیاس تا ارتفاع مرزها\"\nBasic.TransformWindow.BoundsType.Stretch=\"کشیدن تا مرزها\"\nBasic.TransformWindow.Title=\"ویرایش تبدیل برای '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"بدون انتخاب منبع\"\nBasic.Main.AddSourceHelp.Title=\"منبع اضافه نمیشود\"\nBasic.Main.AddSourceHelp.Text=\"برای افزودن منبع باید حداقل 1 صحنه داشته باشید.\"\nBasic.Main.Scenes=\"صحنه ها\"\nBasic.Main.Sources=\"منابع\"\nBasic.Main.Source=\"منبع\"\nBasic.Main.Controls=\"کنترل ها\"\nBasic.Main.PreparingStream=\"در حال آماده‌سازی...\"\nBasic.Main.Connecting=\"در حال اتصال...\"\nBasic.Main.StartRecording=\"شروع ضبط\"\nBasic.Main.StartReplayBuffer=\"شروع بافر پخش مجدد\"\nBasic.Main.SaveReplay=\"ذخیره بازپخش\"\nBasic.Main.StartStreaming=\"شروع پخش زنده\"\nBasic.Main.StartBroadcast=\"برو به لایو!\"\nBasic.Main.StartVirtualCam=\"شروع دوربین مجازی\"\nBasic.Main.StopRecording=\"پایان ضبط\"\nBasic.Main.PauseRecording=\"توقف ضبط\"\nBasic.Main.UnpauseRecording=\"ادامه ضبط\"\nBasic.Main.SplitFile=\"تقسیم پروندهٔ ضبط\"\nBasic.Main.AddChapterMarker=\"افزودن نشانگر فصل (فقط MP4/MOV ترکیبی)\"\nBasic.Main.StoppingRecording=\"درحال پایان ضبط...\"\nBasic.Main.StopReplayBuffer=\"توقف بافر بازپخش\"\nBasic.Main.StoppingReplayBuffer=\"توقف پخش بافر...\"\nBasic.Main.SetupBroadcast=\"تنظیم کردن پخش\"\nBasic.Main.StopStreaming=\"توقف پخش زنده\"\nBasic.Main.StopBroadcast=\"پایان پخش\"\nBasic.Main.AutoStopEnabled=\"(توقّف خودکار)\"\nBasic.Main.StoppingStreaming=\"درحال توقف پخش زنده...\"\nBasic.Main.ForceStopStreaming=\"توقف پخش زنده (تأخیر در صرف نظر کردن)\"\nBasic.Main.ShowContextBar=\"نمایش نوار ابزار منبع\"\nBasic.Main.HideContextBar=\"پنهان کردن نوار ابزار منبع\"\nBasic.Main.StopVirtualCam=\"توقف دوربین مجازی\"\nBasic.Main.Group=\"گروه %1\"\nBasic.Main.GroupItems=\"موارد انتخاب شده را گروه بندی کنید\"\nBasic.Main.Ungroup=\"لغو گروه بندی\"\nBasic.Main.GridMode=\"حالت شبکه\"\nBasic.Main.ListMode=\"حالت لیست\"\nBasic.Main.VirtualCamConfig=\"پیکربندی دوربین مجازی\"\nBasic.VCam.VirtualCamera=\"دوربین مجازی\"\nBasic.VCam.OutputType=\"نوع خروجی\"\nBasic.VCam.OutputSelection=\"انتخاب خروجی\"\nBasic.VCam.OutputType.Program=\"برنامه (پیش‌فرض)\"\nBasic.VCam.OutputSelection.NoSelection=\"هیچ انتخابی برای این نوع خروجی وجود ندارد\"\nBasic.VCam.RestartWarning=\"برای اعمال این تغییر، دوربین مجازی مجددا راه‌اندازی می شود\"\nBasic.MainMenu.File=\"&پرونده\"\nBasic.MainMenu.File.Export=\"&خروجی گرفتن\"\nBasic.MainMenu.File.Import=\"&وارد کردن\"\nBasic.MainMenu.File.ShowRecordings=\"نمایش &ضبط‌ها\"\nBasic.MainMenu.File.Remux=\"باز&سازی ضبط های مجدد\"\nBasic.MainMenu.File.Settings=\"&تنظیمات\"\nBasic.MainMenu.File.ShowSettingsFolder=\"نمایش پوشه تنظیمات\"\nBasic.MainMenu.File.ShowProfileFolder=\"نمایش پوشه نمایه\"\nBasic.MainMenu.File.ShowMissingFiles=\"بررسی برای پرونده‌های گمشده\"\nBasic.MainMenu.File.Exit=\"خروج\"\nBasic.MainMenu.Edit=\"ویرایش\"\nBasic.MainMenu.Edit.Undo=\"لغو عمل&\"\nBasic.MainMenu.Edit.Redo=\"& ازنو\"\nBasic.MainMenu.Edit.LockPreview=\"&قفل پیش نمایش\"\nBasic.MainMenu.Edit.Scale=\"پیش نمایش و مقیاس گذاری\"\nBasic.MainMenu.Edit.Scale.Window=\"مقیاس نسبت به پنجره\"\nBasic.MainMenu.Edit.Scale.Canvas=\"بوم (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"خروجی (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"مقیاس شده (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"بزرگ‌نمایی\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"کوچک‌نمایی\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"بازنشانی بزرگ‌نمایی\"\nBasic.MainMenu.Edit.Transform=\"&تبدیل\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&ویرایش تبدیل...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"کپی تبدیل\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"چسباندن تبدیل\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&تنظیم مجدد تبدیل\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"90 درجه CW را بچرخانید\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"90 درجه CCW بچرخانید\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180 درجه بچرخید\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"قرینه &اُفقی\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"قرینه &عمودی\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"&متناسب با صفحه\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&کشش برای نمایش\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&مرکز صفحه\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"مرکز بطور عمودی\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"مرکز بطور افقی\"\nBasic.MainMenu.Edit.Order=\"&سفارش\"\nBasic.MainMenu.Edit.Order.MoveUp=\"رفتن به &بالا\"\nBasic.MainMenu.Edit.Order.MoveDown=\"حرکت به &پایین\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"انتقال& به بالا\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"انتقال به & پایین\"\nBasic.MainMenu.Edit.AdvAudio=\"&خصوصیات پیشرفته صدا\"\nBasic.MainMenu.View=\"&نما\"\nBasic.MainMenu.View.Toolbars=\"&نوار ابزارها\"\nBasic.MainMenu.View.ListboxToolbars=\"نوار ابزار اسکله\"\nBasic.MainMenu.View.ContextBar=\"منبع نوار ابزار\"\nBasic.MainMenu.View.SourceIcons=\"منبع & نمادها\"\nBasic.MainMenu.View.StatusBar=\"& نوار وضعیت\"\nBasic.MainMenu.View.Fullscreen.Interface=\"رابط تمام صفحه\"\nBasic.MainMenu.View.ResetUI=\"&بازنشانی رابط\"\nBasic.MainMenu.View.AlwaysOnTop=\"&همیشه در بالا\"\nBasic.MainMenu.View.SceneListMode=\"حالت فهرست صحنه\"\nBasic.MainMenu.Docks=\"و بارانداز\"\nBasic.MainMenu.Docks.ResetDocks=\"&بازنشانی پایه‌ها\"\nBasic.MainMenu.Docks.LockDocks=\"&قفل کردن اسکله ها\"\nBasic.MainMenu.Docks.SideDocks=\"و اسکله های تمام ارتفاع\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&بارگیری مرورگر سفارشی...\"\nBasic.MainMenu.Import=\"وارد کردن...\"\nBasic.MainMenu.Export=\"صادر کردن...\"\nBasic.MainMenu.SceneCollection=\"& مجموعه صحنه\"\nBasic.MainMenu.Profile=\"&نمایه\"\nBasic.MainMenu.Profile.Import=\"وارد کردن نمایه\"\nBasic.MainMenu.Profile.Export=\"صادر کردن نمایه\"\nBasic.MainMenu.SceneCollection.Import=\"وارد کردن مجموعه صحنه\"\nBasic.MainMenu.SceneCollection.Export=\"صادرات مجموعه صحنه\"\nBasic.MainMenu.SceneCollection.Remigrate=\"تنظیم مجدد وضوح پایه\"\nBasic.MainMenu.SceneCollection.Migrate=\"تنظیم وضوح پایه\"\nBasic.MainMenu.Profile.Exists=\"نمایه از قبل وجود دارد\"\nBasic.MainMenu.Tools=\"&ابزارها\"\nBasic.MainMenu.Help=\"&راهنما\"\nBasic.MainMenu.Help.HelpPortal=\"راهنما& پورتال\"\nBasic.MainMenu.Help.Website=\"بازدید& وب سایت\"\nBasic.MainMenu.Help.Discord=\"به &Discord Server بپیوندید\"\nBasic.MainMenu.Help.WhatsNew=\"تازه چه خبر\"\nBasic.MainMenu.Help.Logs=\"& ثبت پرونده ها\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"& نمایش پرونده های گزارش\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"بارگذاری فایل گزارش فعلی\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"بارگذاری پروندهٔ گزارش &Previous\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"& مشاهده گزارش فعلی\"\nBasic.MainMenu.Help.ReleaseNotes=\"یادداشت های انتشار\"\nBasic.MainMenu.Help.CheckForUpdates=\"بررسی برای بروزرسانی\"\nBasic.MainMenu.Help.Repair=\"بررسی یکپارچکی پرونده\"\nBasic.MainMenu.Help.RestartSafeMode=\"ری‌استارت در حالت ایمن\"\nBasic.MainMenu.Help.RestartNormal=\"ری‌استارت در حالت عادی\"\nBasic.MainMenu.Help.CrashLogs=\"گزارش ها و خرابی\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"& نمایش گزارشهای خرابی\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"بارگذاری گزارش خرابی &Previous\"\nBasic.MainMenu.Help.About=\"&درباره\"\nBasic.Settings.ProgramRestart=\"برای تأثیرگذاری این تنظیمات، برنامه باید دوباره راه‌اندازی شود.\"\nBasic.Settings.ConfirmTitle=\"تایید تغییرات\"\nBasic.Settings.Confirm=\"شما تغییرات ذخیره نشده ای دارید. ذخیره تغییرات?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 برخی از تنظیمات استریم شما را کنترل می کند\"\nBasic.Settings.General=\"عمومی\"\nBasic.Settings.General.Language=\"زبان\"\nBasic.Settings.General.Updater=\"به‌روزرسانی‌ها\"\nBasic.Settings.General.UpdateChannel=\"کانال بروزرسانی\"\nBasic.Settings.General.UpdateChannelDisabled=\"(غیر فعال)\"\nBasic.Settings.General.UpdateChannelDefault=\"(پیش‌فرض)\"\nBasic.Settings.General.EnableAutoUpdates=\"به طور خودکار برای به روزرسانی هنگام راه اندازی بررسی کنید\"\nBasic.Settings.General.OpenStatsOnStartup=\"گشودن گفت‌وگوی آمار هنگام شروع\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"پنجره های OBS را از تصویربرداری از صفحه پنهان کنید\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"این تنظیم تمام پنجره‌های غیر پروژکتوری استودیوی OBS را از ضبط شدن به دست OBS پنهان کرده و بر\\nسایر برنامه‌ها مانند کنفرانس، هم‌رسانی صفحه، پشتیبانی از راه دور، نماگرفت‌ها و دیگر نرم‌افزارهای ضبط تأثیر می‌گذارد.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"به کار انداختن این گزینه تمام پنجره‌های غیر پروژکتوری استودیوی OBS را از ضبط شدن به دست OBS پنهان کرده و بر سایر برنامه‌ها مانند کنفرانس، هم‌رسانی صفحه، پشتیبانی از راه دور، نماگرفت‌ها و دیگر نرم‌افزارهای ضبط تأثیر می‌گذارد.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"هنگام شروع پخش زنده، صفحه تأیید نشان داده شود\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"هنگام توقف پخش زنده، پنجره تأیید نشان داده شود\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"هنگام توقف ضبط ، گفتگوی تأیید را نشان دهید\"\nBasic.Settings.General.Projectors=\"پروژکتورها\"\nBasic.Settings.General.HideProjectorCursor=\"مکان نما را بر روی پروژکتورها مخفی کنید\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"پروژکتورها را همیشه در بالا قرار دهید\"\nBasic.Settings.General.Snapping=\"چیدمان تراز منبع\"\nBasic.Settings.General.ScreenSnapping=\"ضربه ناگهانی منابع به لبه صفحه\"\nBasic.Settings.General.CenterSnapping=\"منابع جفت را به مرکز افقی و عمودی برسانید\"\nBasic.Settings.General.SourceSnapping=\"منابع را به منابع دیگر منتقل کنید\"\nBasic.Settings.General.SnapDistance=\"حساسیت ضربه ناگهانی\"\nBasic.Settings.General.SpacingHelpers=\"نمایش راهنماهای تراز پیکسل\"\nBasic.Settings.General.RecordWhenStreaming=\"ضبط خودکار در هنگام پخش زنده\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"هنگام قطع شدن لایو، ضبط را ادامه دهید\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"هنگام پخش زنده به طور خودکار بافر پخش را شروع کنید\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"با قطع پخش زنده، بافر پخش را فعال نگه دارید\"\nBasic.Settings.General.SysTray=\"سینی سیستم\"\nBasic.Settings.General.SysTrayWhenStarted=\"هنگام شروع ، سینی سیستم را به حداقل برسانید\"\nBasic.Settings.General.SystemTrayHideMinimize=\"همیشه به جای نوار وظیفه، در سینی سیستم به حداقل برسانید\"\nBasic.Settings.General.SaveProjectors=\"پروژکتورها را در هنگام خروج ذخیره کنید\"\nBasic.Settings.General.CloseExistingProjectors=\"یک پروژکتور تمام صفحه در هر صفحه محدود کنید\"\nBasic.Settings.General.Preview=\"پیش‌نمایش\"\nBasic.Settings.General.OverflowHidden=\"پنهان کردن سرریز\"\nBasic.Settings.General.OverflowAlwaysVisible=\"سرریز همیشه قابل مشاهده\"\nBasic.Settings.General.OverflowSelectionHidden=\"سرریز را حتی در صورت نامرئی بودن منبع نشان دهید\"\nBasic.Settings.General.Importers=\"واردکنندگان\"\nBasic.Settings.General.AutomaticCollectionSearch=\"هنگام وارد کردن ، مکانهای شناخته شده را برای مجموعه صحنه جستجو کنید\"\nBasic.Settings.General.SwitchOnDoubleClick=\"با دوبار کلیک به صحنه منتقل شوید\"\nBasic.Settings.General.StudioPortraitLayout=\"طرح عمودی/افقی را فعال کنید\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"نمایش برچسب های پیش نمایش/برنامه\"\nBasic.Settings.General.Multiview=\"چند دید\"\nBasic.Settings.General.Multiview.MouseSwitch=\"برای جابجایی بین صحنه ها کلیک کنید\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"نمایش نام صحنه ها\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"مناطق امن را ترسیم کنید (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"لایه بندی چند نما\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"افقی ، بالا (8 صحنه)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"افقی ، پایین (8 صحنه)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"عمودی ، چپ (8 صحنه)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"عمودی ، راست (8 صحنه)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"افقی ، بالا (18 صحنه)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"افقی ، بالا (24 صحنه)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"فقط صحنه ها (4 صحنه)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"فقط صحنه ها (9 صحنه)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"فقط صحنه ها (16 صحنه)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"فقط صحنه ها (25 صحنه)\"\nBasic.Settings.General.ChannelName.stable=\"پایدار\"\nBasic.Settings.General.ChannelDescription.stable=\"آخرین نسخه پایدار\"\nBasic.Settings.General.ChannelName.beta=\"بتا / انتشار داوطلبان\"\nBasic.Settings.General.ChannelDescription.beta=\"نسخه های پیش از انتشار بالقوه ناپایدار\"\nBasic.Settings.Appearance=\"ظاهر\"\nBasic.Settings.Appearance.General=\"کلی\"\nBasic.Settings.Appearance.General.Theme=\"پوسته\"\nBasic.Settings.Appearance.General.Variant=\"سبک\"\nBasic.Settings.Appearance.General.NoVariant=\"سبکی در دسترس نیست\"\nBasic.Settings.Appearance.FontScale=\"اندازه قلم\"\nBasic.Settings.Appearance.Density=\"تراکم\"\nBasic.Settings.Appearance.Classic=\"قدیمی\"\nBasic.Settings.Appearance.Compact=\"فشرده\"\nBasic.Settings.Appearance.Normal=\"عادی\"\nBasic.Settings.Appearance.Comfortable=\"راحت\"\nBasic.Settings.Appearance.OptionsWarning=\"برخی از گزینه‌های ظاهری برای این سبک در دسترس نیستند.\"\nBasic.Settings.Stream=\"پخش\"\nBasic.Settings.Stream.Destination=\"مقصد\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"استفاده از تایید اعتبار\"\nBasic.Settings.Stream.Custom.Username=\"نام کاربری\"\nBasic.Settings.Stream.Custom.Password=\"رمزعبور\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: srp_username را وارد کنید.\\nRTMP: نام کاربری را وارد کنید.\\nSRT: استفاده نشده است.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: srp_password را وارد کنید.\\nRTMP: رمز عبور را وارد کنید.\\nSRT: عبارت عبور رمزگذاری را وارد کنید.\"\nBasic.Settings.Stream.BandwidthTestMode=\"حالت تست پهنای باند را فعال کنید\"\nBasic.Settings.Stream.TTVAddon=\"افزونه های چت Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"هیچ‌کدام\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV و FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"تنظیم جریان از دست رفته است\"\nBasic.Settings.Stream.StreamSettingsWarning=\"گشودن تنظیمات\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"نشانی اینترنتی و کلید جریان وجود ندارد.\\n\\nبرای ورود نشانی و کلید جریان در برگهٔ «جریان»، تنظیمات را بگشایید.\"\nBasic.Settings.Stream.MissingUrl=\"نشانی اینترنتی وجود ندارد.\\n\\nبرای ورود نشانی در برگهٔ «جریان»، تنظیمات را بگشایید.\"\nBasic.Settings.Stream.MissingStreamKey=\"کلید جریان وجود ندارد.\\n\\nتنظیمات را باز کنید تا کلید جریان را در برگه \\\"جریان\\\" وارد کنید.\"\nBasic.Settings.Stream.IgnoreRecommended=\"توصیه‌های تنظیم خدمت پخش زنده را نادیده بگیرید\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"تنظیمات پیشنهادی را لغو کنید\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"هشدار: نادیده گرفتن محدودیت‌های خدمت ممکن است منجر به افت کیفیت پخش شود یا از پخش زنده شما جلوگیری کند.\\n\\nادامه می‌دهید؟\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"حداکثر بیت ریت ویدیو: %1\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"حداکثر بیت ریت صدا: %1\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"حداکثر وضوح: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"حداکثر فریم: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"مشخص کردن سرور سفارشی...\"\nBasic.Settings.Stream.ServiceCustomServer=\"سرور سفارشی\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"فعال کردن %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"حداکثر پنهای باند استریم کردن\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"خودکار\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"حداکثر قطعات ویدئویی\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"خودکار\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"فعال کردن اضافی استریم در FLV (از تنظیمات فایل ضبط ساده استفاده می کند)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"ابطال کانفینگ (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"فعال‌کردن ابطال کانفینگ\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"ویدئو چندلایه\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"بوم اضافی\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"پخش همزمان\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"پخش همزمان به شما امکان می‌دهد چندین کیفیت ویدیویی را رمزگذاری و ارسال کنید. <a href='https://obsproject.com/kb/whip-streaming-guide'>بیشتر بدانید</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"کل لایه‌ها\"\nBasic.Settings.Stream.AdvancedOptions=\"تنظیمات پیشرفته\"\nBasic.Settings.Output=\"خروجی\"\nBasic.Settings.Output.Format=\"فرمت ضبط\"\nBasic.Settings.Output.Format.MKV=\"فیلم ماتروسکا (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"MP4 ترکیبی (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV ترکیبی (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 قطعه شده (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV قطعه شده (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"MOV تکه‌تکه‌شده ضبط را به صورت تکه‌ای می‌نویسد و نیازی به نهایی‌سازی مشابه فایل‌های MOV سنتی ندارد.\\nاین تضمین می‌کند که فایل حتی اگر نوشتن روی دیسک قطع شود، مثلاً در نتیجه BSOD یا قطع برق، همچنان قابل پخش است.\\n\\ nاین ممکن است با همه پخش کننده ها و ویرایشگرها سازگار نباشد. از فایل ← ریماکس های ضبط شده برای تبدیل فایل به فرمت سازگارتر در صورت لزوم استفاده کنید.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"MP4 تکه‌تکه‌شده ضبط را به صورت تکه‌ای می‌نویسد و نیازی به نهایی‌سازی مشابه فایل‌های MP4 سنتی ندارد.\\nاین تضمین می‌کند که فایل قابل پخش باقی می‌ماند، حتی اگر نوشتن روی دیسک، برای مثال، در نتیجه BSOD یا قطع برق، قطع شود.\\n\\ nاین ممکن است با همه پخش کننده ها و ویرایشگرها سازگار نباشد. از فایل ← ریماکس های ضبط شده برای تبدیل فایل به فرمت سازگارتر در صورت لزوم استفاده کنید.\"\nBasic.Settings.Output.Encoder.Video=\"انکودر ویدئو\"\nBasic.Settings.Output.Encoder.Audio=\"انکودر صدا\"\nBasic.Settings.Output.SelectDirectory=\"انتخاب پوشه ضبط\"\nBasic.Settings.Output.DynamicBitrate=\"برای مدیریت ازدحام میزان بیت بصورت پویا تغییر دهید\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"تغییر دینامیک بیت ریت برای مدیریت ازدحام (بتا)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"به جای کاهش فریم‌ها برای کاهش تراکم، نرخ‌بیت‌ به صورت داینامیک و آنی تغییر می‌کند.\\n\\nتوجه داشته باشید که این ممکن است در صورت وجود تراکم ناگهانی و زیاد، تأخیر را برای بینندگان افزایش دهد.\\nهنگامی که نرخ‌بیت‌ کاهش می‌یابد، ممکن است بازگرداندن آن تا چند دقیقه طول بکشد.\\n\\nدر حال حاضر، این قابلیت فقط برای RTMP پشتیبانی می‌شود.\"\nBasic.Settings.Output.Mode=\"نوع خروجی\"\nBasic.Settings.Output.Mode.Simple=\"معمولی\"\nBasic.Settings.Output.Mode.Adv=\"پیشرفته\"\nBasic.Settings.Output.Mode.FFmpeg=\"خروجی FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"بافر پخش را فعال کنید\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"حداکثر زمان پخش\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"حداکثر حافظه\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"میزان مصرف حافظه تخمینی: %1 مگابایت\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"هشدار: استفاده تخمینی از حافظه %1 مگابایت بزرگتر از حداکثر توصیه شده %2 مگابایت است\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"استفاده از حافظه را نمی توان تخمین زد. لطفا حداکثر حد حافظه را تعیین کنید.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"پیشوند نام پرونده بافر را پر کنید\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"پسوند\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"هنگامی که نوع ضبط روی خروجی سفارشی (FFmpeg) تنظیم شده است، نمی توان از بافر پخش مجدد استفاده کرد.\"\nBasic.Settings.Output.Simple.SavePath=\"مسیر پوشه ضبط\"\nBasic.Settings.Output.Simple.RecordingQuality=\"کیفیت ضبط\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"همان جریان است\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"با کیفیت بالا، اندازه فایل متوسط\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"کیفیت غیر قابل تشخیص، اندازه بزرگ فایل\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"کیفیت بدون افت، حجم پرونده فوق العاده زیاد\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"هشدار: میزان ارسال بیت ریت ویدئو پخش روی %1 تنظیم می‌شود، که حد بالا برای خدمت پخش فعلی است.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"هشدار: میزان ارسال بیت ریت صوتی پخش روی %1 تنظیم می‌شود، که حد بالا برای خدمت پخش فعلی است.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"هشدار: اگر کیفیت ضبط روی \\\"همان جریان\\\" تنظیم شود، ضبط ها متوقف نمی شوند.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"هشدار: فرمت ضبط انتخابی فعلی با رمزگذار(های) جریان انتخابی سازگار نیست.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"هشدار: اگر همزمان با پخش، ضبط هم می‌کنید، ضبط با کدگذار نرم‌افزاری با کیفیتی متفاوت از پخش، به پردازش اضافی نیاز دارد.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"هشدار: کیفیت بدون افت حجم فایل‌های فوق‌العاده بزرگی تولید می‌کند! کیفیت بدون افت می‌تواند بیش از 7 گیگابایت فضای دیسک در دقیقه در وضوح و نرخ‌فریم بالا استفاده کند. کیفیت بدون افت برای ضبط های طولانی توصیه نمی‌شود مگر اینکه فضای دیسک بسیار زیادی در دسترس داشته باشید. بافر پخش مجدد در هنگام استفاده از کیفیت بدون افت در دسترس نخواهد بود.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"آیا مطمئن هستید که می‌خواهید از کیفیت بدون افت استفاده کنید؟\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"هشدار کیفیت بدون افت!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"نرم افزار (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"سخت‌افزار (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"سخت‌افزار (QSV، AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"سخت‌افزار (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"سخت‌افزار (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"سخت‌افزار (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"سخت‌افزار (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"سخت افزار (NVENC، AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"سخت افزار (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"سخت‌افزار (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"سخت‌افزار (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"نرم افزار (x264 پریست استفاده از پردازنده کم، اندازه فایل را افزایش می دهد)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (پیش‌فرض)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"تراک VOD توییچ (استفاده از تراک 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"تراک صدا\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"وضوح تصویر/نرخ فریم ناسازگار\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"این خدمت پخش از وضوح خروجی فعلی و/یا نرخ فریم شما پشتیبانی نمی‌کند. این مقادیر به نزدیکترین مقدار سازگار تغییر خواهند کرد:\\n\\n%1\\n\\nآیا می‌خواهید ادامه دهید؟\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"رزولوشن: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"ف د ث: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"رمزگذار ناسازگار\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"سرویس جریان \\\"%1\\\" از رمزگذار \\\"%2\\\" پشتیبانی نمی کند. رمزگذار به \\\"%3\\\" تغییر خواهد کرد.\\n\\nآیا می خواهید ادامه دهید؟\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"سرویس استریم «%1» از انکودر های «%2» و «%3» پشتیبانی نمی‌کند. این انکودر ها به «%4» و «%5» تغییر پیدا خواهند کرد.\\n\\nآیا مایل به ادامه هستید؟\"\nBasic.Settings.Output.VideoBitrate=\"بیت ریت ویدئو\"\nBasic.Settings.Output.AudioBitrate=\"بیت ریت صدا\"\nBasic.Settings.Output.Reconnect=\"اتصال دوباره خودکار\"\nBasic.Settings.Output.RetryDelay=\"تأخیر امتحان دوباره\"\nBasic.Settings.Output.MaxRetries=\"حداکثر تلاش مجدد\"\nBasic.Settings.Output.Advanced=\"فعال کردن تنظیمات رمزگذار سفارشی (پیشرفته)\"\nBasic.Settings.Output.EncoderPreset=\"پیش تنظیم انکودر\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (استفاده کم از پردازنده، پایین ترین کیفیت)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (پیش‌فرض) (مصرف پردازنده متوسط، کیفیت استاندارد)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (استفاده پردازنده بالا، کیفیت بالا)\"\nBasic.Settings.Output.CustomEncoderSettings=\"تنظیمات انکودر سفارشی\"\nBasic.Settings.Output.CustomMuxerSettings=\"تنظیمات سفارشی Muxer\"\nBasic.Settings.Output.NoSpaceFileName=\"نام پرونده را بدون فاصله ایجاد کنید\"\nBasic.Settings.Output.Adv.Rescale=\"خروجی مقیاس مجدد\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"غیر فعال\"\nBasic.Settings.Output.Adv.AudioTrack=\"تراک صدا\"\nBasic.Settings.Output.Adv.Streaming=\"پخش زنده\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"تنظیمات جریان\"\nBasic.Settings.Output.Adv.Audio.Track1=\"تراک 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"تراک 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"تراک 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"تراک 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"تراک 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"تراک 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"تراک VOD توییچ\"\nBasic.Settings.Output.Adv.Encoder=\"تنظیمات رمزگذار\"\nBasic.Settings.Output.Adv.Recording=\"درحال ضبط\"\nBasic.Settings.Output.Adv.Recording.Settings=\"تنظیمات ضبط\"\nBasic.Settings.Output.Adv.Recording.RecType=\"نوع ضبط\"\nBasic.Settings.Output.Adv.Recording.Type=\"نوع\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"استاندارد\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"خروجی سفارشی (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(از رمزگذار جریان استفاده کنید)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"قالب بندی نام پرونده\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"در صورت وجود پرونده ، آن را بازنویسی کنید\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"خروجی سفارشی (FFmpeg) بدون هیچ حفاظتی ارائه شده است.\\nاز نوع ضبط \\\"استاندارد\\\" استفاده کنید.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"نوع خروجی FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"خروجی به نشانی اینترنتی\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"خروجی در پرونده\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"قالب های ضبط معمول\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"تمامی پرونده‌ها\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"مسیر پرونده یا نشانی اینترنتی\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"قالب ظرف\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"صدا\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"ویدئو\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"قالب پیش فرض\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"توضیحات قالب ظرف\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"کدک صوتی / تصویری از مسیر فایل یا نشانی حدس زده است\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"انکودر پیش فرض\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"انکودر را غیرفعال کنید\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"انکودر ویدئو\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"تنظیمات انکودر ویدئو (در صورت وجود)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"انکودر صدا\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"تنظیمات انکودر صدا (در صورت وجود)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"تنظیمات Muxer (در صورت وجود)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"فاصله صفحه کلید (فریم)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"نمایش همه کدک ها (حتی اگر بالقوه ناسازگار باشد)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"تنظیمات FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"تقسیم خودکار فایل ها\"\nBasic.Settings.Output.SplitFile.TypeTime=\"تقسیم بر اساس زمان\"\nBasic.Settings.Output.SplitFile.TypeSize=\"تقسیم بر اندازه\"\nBasic.Settings.Output.SplitFile.TypeManual=\"فقط به صورت دستی تقسیم شود\"\nBasic.Settings.Output.SplitFile.Time=\"زمان بندی\"\nBasic.Settings.Output.SplitFile.Size=\"اندازه تقسیم\"\nScreenshot=\"خروجی عکس صفحه\"\nScreenshot.SourceHotkey=\"تصویر صفحه منبع انتخاب شده\"\nScreenshot.StudioProgram=\"عکس صفحه (برنامه)\"\nScreenshot.Preview=\"عکس صفحه (پیش نمایش)\"\nScreenshot.Scene=\"اسکرین شات (صحنه)\"\nScreenshot.Source=\"اسکرین شات (منبع)\"\nFilenameFormatting.TT.CCYY=\"سال، چهار رقمی\"\nFilenameFormatting.TT.YY=\"سال، دو رقم آخر (00-99)\"\nFilenameFormatting.TT.MM=\"ماه به عنوان عدد اعشاری (01-12)\"\nFilenameFormatting.TT.DD=\"روز ماه، بدون لایه (01-31)\"\nFilenameFormatting.TT.hh=\"ساعت در قالب 24 ساعت (00-23)\"\nFilenameFormatting.TT.mm=\"دقیقه (00-59)\"\nFilenameFormatting.TT.ss=\"ثانیه (00-59)\"\nFilenameFormatting.TT.Percent=\"یک نشانه %\"\nFilenameFormatting.TT.a=\"نام مختصر روز هفته\"\nFilenameFormatting.TT.A=\"نام کامل روز هفته\"\nFilenameFormatting.TT.b=\"نام مختصر ماه\"\nFilenameFormatting.TT.B=\"نام کامل ماه\"\nFilenameFormatting.TT.d=\"روز ماه، بدون لایه (01-31)\"\nFilenameFormatting.TT.H=\"ساعت در قالب 24 ساعت (00-23)\"\nFilenameFormatting.TT.I=\"ساعت در قالب 12 ساعت (01-12)\"\nFilenameFormatting.TT.m=\"ماه به عنوان عدد اعشاری (01-12)\"\nFilenameFormatting.TT.M=\"دقیقه (00-59)\"\nFilenameFormatting.TT.p=\"تعیین ق.ظ یا ب.ظ\"\nFilenameFormatting.TT.s=\"زمان در ثانیه از دوران یونیکس\"\nFilenameFormatting.TT.S=\"ثانیه (00-59)\"\nFilenameFormatting.TT.y=\"سال، دو رقم آخر (00-99)\"\nFilenameFormatting.TT.Y=\"سال\"\nFilenameFormatting.TT.z=\"ISO 8601 از UTC در منطقه زمانی خارج شد\"\nFilenameFormatting.TT.Z=\"نام یا مخفف منطقه زمانی\"\nFilenameFormatting.TT.FPS=\"فریم بر ثانیه\"\nFilenameFormatting.TT.CRES=\"وضوح پایه (بوم).\"\nFilenameFormatting.TT.ORES=\"وضوح خروجی (مقیاس شده)\"\nFilenameFormatting.TT.VF=\"فرمت ویدیویی\"\nBasic.Settings.Video=\"ویدئو\"\nBasic.Settings.Video.Adapter=\"آداپتور ویدئو\"\nBasic.Settings.Video.BaseResolution=\"وضوح پایه (پرده)\"\nBasic.Settings.Video.ScaledResolution=\"وضوح خروجی (مقیاس)\"\nBasic.Settings.Video.DownscaleFilter=\"پالایه کوچک مقیاس\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[رزولوشن ها مطابقت دارند، بدون نیاز به کاهش مقیاس]\"\nBasic.Settings.Video.FPS=\"فریم ریت\"\nBasic.Settings.Video.FPSCommon=\"مقادیر متداول فریم ریت\"\nBasic.Settings.Video.FPSInteger=\"مقدار فریم ریت صحیح\"\nBasic.Settings.Video.FPSFraction=\"مقدار فریم اعشاری\"\nBasic.Settings.Video.Numerator=\"صورت کسر\"\nBasic.Settings.Video.Denominator=\"مخرج کسر\"\nBasic.Settings.Video.Renderer=\"خروجی\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (آزمایشی)\"\nBasic.Settings.Video.InvalidResolution=\"مقدار رزولوشن نامعتبر است. باید [width]x[height] باشد (یعنی 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"خروجی ویدئو در حال حاضر فعال است. لطفاً برای تغییر تنظیمات ویدئو، همه ی خروجی ها را خاموش کنید.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"دوسویه (سریع تر، اما در صورت مقیاس زدگی تار است)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (مقیاس گذاری تیز ، 16 نمونه)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (مقیاس تیز ، 36 نمونه)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"مساحت (مجموع وزنی ، 4/6/9 نمونه)\"\nBasic.Settings.Audio=\"صدا\"\nBasic.Settings.Audio.SampleRate=\"نرخ نمونه\"\nBasic.Settings.Audio.Channels=\"کانال‌ها\"\nBasic.Settings.Audio.Meters=\"متر\"\nBasic.Settings.Audio.MeterDecayRate=\"نرخ تاخیر\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"سریع\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"متوسط (نوع I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"آهسته (نوع II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"نوع حد سنج\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"حد نمونه\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"حد واقعی (استفاده بالاتر از پردازنده)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"هشدار: صدای صوتی فراگیر فعال است.\"\nBasic.Settings.Audio.MultichannelWarning=\"در صورت پخش جریانی، بررسی کنید که آیا سرویس پخش شما هم از پخش صدای فراگیر و هم از ضبط صدای فراگیر پشتیبانی می‌کند. به عنوان مثال، Facebook 360 Live به طور کامل از صدای فراگیر پشتیبانی می کند. YouTube Live از دریافت صدا 5.1 (و پخش در تلویزیون‌ها) پشتیبانی می‌کند.\\n\\nفیلترهای صوتی OBS با صدای فراگیر سازگار هستند، اگرچه پشتیبانی از افزونه VST تضمین نشده است.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"صدای صوتی فراگیر فعال شود؟\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"آیا مطمئن هستید که می خواهید صدای فراگیر را فعال کنید?\"\nBasic.Settings.Audio.Devices=\"دستگاه های صوتی کلی\"\nBasic.Settings.Audio.DesktopDevice=\"صدای دسکتاپ\"\nBasic.Settings.Audio.DesktopDevice2=\"صدای دسکتاپ 2\"\nBasic.Settings.Audio.AuxDevice=\"صدابَر / صوتی کمکی\"\nBasic.Settings.Audio.AuxDevice2=\"صدابَر / صوتی کمکی ۲\"\nBasic.Settings.Audio.AuxDevice3=\"صدابَر / صوتی کمکی ۳\"\nBasic.Settings.Audio.AuxDevice4=\"صدابَر / صوتی کمکی ۴\"\nBasic.Settings.Audio.EnablePushToMute=\"فشار برای قطع کردن را فعال کنید\"\nBasic.Settings.Audio.PushToMuteDelay=\"تاخیر فشار برای قطع کردن\"\nBasic.Settings.Audio.EnablePushToTalk=\"فعال کردن فشار دادن برای مکالمه\"\nBasic.Settings.Audio.PushToTalkDelay=\"تأخیر فشار دادن برای مکالمه\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[دستگاه متصل نیست یا در دسترس نیست]\"\nBasic.Settings.Audio.Disabled=\"غیر فعال\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"حالت بافر صوتی با تاخیر کم (برای خروجی های Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"هشدار: بافر صوتی با تاخیر کم فعال است.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"حالت بافر صوتی با تأخیر کم ممکن است باعث اختلال در صدا یا توقف پخش صدا از برخی منابع شود.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"حالت بافر صوتی با تاخیر کم فعال شود؟\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"آیا مطمئن هستید که می خواهید حالت بافر صوتی با تاخیر کم را فعال کنید؟\"\nBasic.Settings.Accessibility=\"قابلیت دسترسی\"\nBasic.Settings.Accessibility.ColorOverrides=\"استفاده از رنگ های مختلف\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"مرز منبع (انتخاب)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"حاشیه منبع (برش)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"مرز منبع (شناور)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"باند ترکیب صدا (60- تا -20 دسی بل)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"باند ترکیب صدا (-20 تا -9 دسی بل)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"باند ترکیب صدا (-9 تا 0- دسی بل)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"باند ولوم میکسر (60- تا -20 دسی بل) (فعال)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"باند ترکیب صدا (-20 تا -9 دسی بل) (فعال)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"باند ترکیب صدا (-9 تا 0- دسی بل) (فعال)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"رنگ پیش تنظیم\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"پیش‌فرض\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"سفارشی\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"جایگزین کور رنگی\"\nBasic.Settings.Advanced=\"پیشرفته\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"نمایش هشدار خروجی‌های فعّال هنگام خروج\"\nBasic.Settings.Advanced.General.ProcessPriority=\"اولویت پردازش\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"بالا\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"بیشتر از عادی\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"معمولی\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"کمتر از عادی\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"بیکار\"\nBasic.Settings.Advanced.FormatWarning=\"هشدار: فرمت‌های رنگی غیر از NV12/P010 عمدتاً برای ضبط استفاده می‌شوند و توصیه نمی‌شود هنگام جریان استفاده شوند. در جریان زنده ممکن است باعث افزایش استفاده از پردازنده به دلیل تبدیل فرمت رنگی شود.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"هشدار: فرمت‌های با دقت بالا بیشتر با فضاهای رنگی HDR استفاده می‌شوند.\"\nBasic.Settings.Advanced.FormatWarning2100=\"هشدار: Rec. 2100 باید از فرمت با دقت بیشتری استفاده کند.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"فرمت رنگ\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 بیتی، 4:2:0، 2 سطح)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 بیتی، 4:2:0، 3 سطح)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 بیتی، 4:4:4، 3 سطح)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 بیتی، 4:2:0، 2 سطح)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 بیتی، 4:2:0، 3 صفحه)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 بیتی، 4:2:2، 2 سطح)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 بیتی، 4:4:4، 2 سطح)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 بیتی)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"فضای رنگی\"\nBasic.Settings.Advanced.Video.ColorRange=\"محدوده رنگ\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"محدود\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"کامل\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"سطح اوج اسمی HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"دستگاه نظارت\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"پیش فرض\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"اردک صوتی Windows را غیرفعال کنید\"\nBasic.Settings.Advanced.StreamDelay=\"تاخیر جریان\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"مدت زمان\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"هنگام اتصال مجدد ، نقطه قطع (افزایش تاخیر) را حفظ کنید\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"میزان استفاده تقریبی حافظه: %1 مگابایت\"\nBasic.Settings.Advanced.Network=\"شبکه\"\nBasic.Settings.Advanced.Network.Disabled=\"پروتکل پخش جریانی انتخاب شده فعلی از تغییر تنظیمات شبکه پشتیبانی نمی کند.\"\nBasic.Settings.Advanced.Network.BindToIP=\"اتصال به IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"آی پی خانواده\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"بهینه سازی شبکه را فعال کنید\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"قدم زدن TCP را فعال کنید\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"تلاش می شود تا با تنظیم میزان انتقال، خروجی RTMP با سایر برنامه های حساس به تأخیر در شبکه دوستانه تر شود.\\nممکن است خطر افت فریم در اتصالات ناپایدار را افزایش دهد.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"رفتار تمرکز کلید میانبر\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"هرگز کلیدهای میانبر را غیرفعال نکنید\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"کلیدهای میانبر را هنگام فوکوس در برنامه غیرفعال کنید\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"کلیدهای میانبر را هنگامی که پنجره اصلی درحالت فوکوس نیست غیرفعال کنید\"\nBasic.Settings.Advanced.AutoRemux=\"به طور خودکار به %1 بازگرداندن\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(ضبط به عنوان mkv)\"\nBasic.AdvAudio=\"خصوصیات پیشرفته صوتی\"\nBasic.AdvAudio.ActiveOnly=\"فقط منابع فعال\"\nBasic.AdvAudio.Name=\"نام\"\nBasic.AdvAudio.Volume=\"درجه صدا\"\nBasic.AdvAudio.VolumeSource=\"میزان صدا برای \\\"%1\\\"\"\nBasic.AdvAudio.Mono=\"مونو\"\nBasic.AdvAudio.MonoSource=\"مونو داونمیکس برای \\\"%1\\\"\"\nBasic.AdvAudio.Balance=\"بالانس\"\nBasic.AdvAudio.BalanceSource=\"تعادل برای \\\"%1\\\"\"\nBasic.AdvAudio.SyncOffset=\"جبران همگام سازی\"\nBasic.AdvAudio.SyncOffsetSource=\"جبران همگام سازی برای \\\"%1\\\"\"\nBasic.AdvAudio.Monitoring=\"نظارت صوتی\"\nBasic.AdvAudio.Monitoring.None=\"نمایش خاموش\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"فقط نمایش (خروجی بیصدا)\"\nBasic.AdvAudio.Monitoring.Both=\"نمایش و خروجی\"\nBasic.AdvAudio.MonitoringSource=\"نظارت صوتی برای \\\"%1\\\"\"\nBasic.AdvAudio.AudioTracks=\"قطعه‌ها\"\nBasic.Settings.Hotkeys=\"کلیدهای میانبر\"\nBasic.Settings.Hotkeys.Pair=\"ترکیبات کلیدی مشترک با \\\"%1\\\" به عنوان ضامن عمل می کنند\"\nBasic.Settings.Hotkeys.Filter=\"پالایه\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"فیلتر بر اساس کلید فوری\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"این کلید میانبر توسط یک یا چند عمل دیگر به اشتراک گذاشته شده است، برای نمایش تداخل ها کلیک کنید\"\nBasic.Settings.Hotkeys.PleaseWait=\"در حال بارگیری کلیدهای میانبر، لطفاً صبر کنید...\"\nBasic.Hotkeys.SelectScene=\"سوییچ به صحنه\"\nBasic.SystemTray.Show=\"نمایش\"\nBasic.SystemTray.Hide=\"پنهان\"\nBasic.SystemTray.Message.Reconnecting=\"قطع شده. اتصال مجدد...\"\nHotkeys.Insert=\"کلید Insert\"\nHotkeys.Delete=\"کلید Delete\"\nHotkeys.Home=\"کلید Home\"\nHotkeys.End=\"کلید End\"\nHotkeys.PageUp=\"کلید Page Up\"\nHotkeys.PageDown=\"کلید Page Down\"\nHotkeys.NumLock=\"کلید Num Lock\"\nHotkeys.ScrollLock=\"کلید Scroll Lock\"\nHotkeys.Backspace=\"کلید Backspace\"\nHotkeys.Tab=\"کلید Tab\"\nHotkeys.Print=\"کلید Print\"\nHotkeys.Pause=\"کلید Pause\"\nHotkeys.Left=\"کلید Left\"\nHotkeys.Right=\"کلید Right\"\nHotkeys.Up=\"کلید Up\"\nHotkeys.Down=\"کلید Down\"\nHotkeys.Windows=\"کلید Windows\"\nHotkeys.Super=\"کلید Super\"\nHotkeys.Menu=\"منو\"\nHotkeys.Space=\"کلید Space\"\nHotkeys.NumpadNum=\"کیبورد اعداد Numpad%1\"\nHotkeys.NumpadMultiply=\"ضرب کیبورد اعداد\"\nHotkeys.NumpadDivide=\"تقسیم کیبورد اعداد\"\nHotkeys.NumpadAdd=\"+ کیبورد اعداد\"\nHotkeys.NumpadSubtract=\"تفریق کیبورد اعداد\"\nHotkeys.NumpadDecimal=\"اعشاری کیبورد اعداد\"\nHotkeys.AppleKeypadNum=\"%1 (صفحه کلید)\"\nHotkeys.AppleKeypadMultiply=\"* (صفحه کلید)\"\nHotkeys.AppleKeypadDivide=\"/ (صفحه کلید)\"\nHotkeys.AppleKeypadAdd=\"+ (صفحه کلید)\"\nHotkeys.AppleKeypadSubtract=\"- (صفحه کلید)\"\nHotkeys.AppleKeypadDecimal=\". کیبورد اعداد\"\nHotkeys.AppleKeypadEqual=\"= (صفحه کلید)\"\nHotkeys.MouseButton=\"ماوس %1\"\nMute=\"بيصدا\"\nUnmute=\"صدادار\"\nPush-to-mute=\"فشار دادن برای بیصدا\"\nPush-to-talk=\"فشار دادن برای گفتگو\"\nSceneItemShow=\"نمایش \\\"%1\\\"\"\nSceneItemHide=\"مخفی کردن \\\"%1\\\"\"\nOutputWarnings.NoTracksSelected=\"شما حداقل باید یک لایه صوتی را انتخاب کنید\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"خطای تنظیمات خروجی\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"همه خروجی ها باید حداقل یک تراک صوتی انتخاب شده داشته باشند.\"\nOutputWarnings.MP4Recording=\"هشدار: اگر پرونده نتواند نهایی شود ، فایل ضبط شده در MP4 / MOV قابل بازیابی نیست (به عنوان مثال در نتیجه صفحه آبی مرگ ، افت برق و غیره). اگر می خواهید چندین لایه صوتی ضبط کنید ، از MKV استفاده کنید و پس از پایان ضبط به فرمت MP4 / MOV تغییر دهید (فایل-> بازسازی مجدد ضبط )\"\nOutputWarnings.CannotPause=\"هشدار: اگر کدگذار ضبط روی «(استفاده از کدگذار پخش)» تنظیم شود، ضبط‌ها نمی‌توانند متوقف شوند\"\nOutputWarnings.CodecIncompatible=\"انتخاب رمزگذار صوتی یا تصویری به دلیل ناسازگاری بازنشانی شد. لطفاً یک رمزگذار سازگار از لیست انتخاب کنید.\"\nCodecCompat.Incompatible=\"(ناسازگار با %1)\"\nCodecCompat.CodecPlaceholder=\"انتخاب رمزگذار...\"\nCodecCompat.ContainerPlaceholder=\"انتخاب فرمت...\"\nCodecCompat.CodecMissingOnExit.Title=\"رمزگذار انتخاب نشده است\"\nCodecCompat.CodecMissingOnExit.Text=\"حداقل یک رمزگذار ویدیو یا صدا تنظیم نشده است. لطفاً مطمئن شوید که رمزگذارها را هم برای ضبط و هم برای پخش جریانی انتخاب کنید.\"\nCodecCompat.ContainerMissingOnExit.Title=\"هیچ قالبی انتخاب نشده است\"\nCodecCompat.ContainerMissingOnExit.Text=\"هیچ قالب ضبطی انتخاب نشده است. لطفاً یک قالب ضبط را انتخاب کنید که با رمزگذار پخش جریانی انتخابی سازگار باشد.\"\nFinalScene.Title=\"حذف صحنه\"\nFinalScene.Text=\"حداقل باید یک صحنه وجود داشته باشد.\"\nNoSources.Title=\"بدون منابع\"\nNoSources.Text=\"به نظر می رسد شما هنوز هیچ منبع ویدیویی اضافه نکرده اید، بنابراین فقط از یک صفحه خالی خارج خواهید شد. آیا مطمئنید که می خواهید این کار را انجام دهید؟\"\nNoSources.Text.AddSource=\"هر وقت بخواهید با کلیک روی نماد + در زیر کادر منابع در پنجره اصلی می توانید منابع را اضافه کنید.\"\nNoSources.Label=\"هیچ منبعی ندارید.\\nروی دکمه+زیر کلیک کنید،\\nیا برای اضافه کردن منبع کلیک راست نکنید.\"\nChangeBG=\"تنظیم رنگ\"\nCustomColor=\"رنگ سفارشی\"\nBrowserSource.EnableHardwareAcceleration=\"سرعت بخشیدن به سخت افزار منبع مرورگر را فعال کنید\"\nAbout=\"درباره\"\nAbout.Info=\"استودیوی OBS نرم‌افزاری آزاد برای ضبط و پخش زندهٔ ویدیوست.\"\nAbout.Donate=\"مشارکت داشته باشید\"\nAbout.GetInvolved=\"مشارکت کنید\"\nAbout.Authors=\"سازنده ها\"\nAbout.License=\"مجوز\"\nAbout.Error=\"خطا! پرونده قابل خواند نیست.\\n\\nبرو به: %1\"\nAbout.Contribute=\"از پروژه OBS پشتیبانی کنید\"\nAddUrl.Title=\"منبع را از طریق نشانی اضافه کنید\"\nAddUrl.Text=\"شما یک نشانی را به OBS کشیده اید. با این کار پیوند بطور خودکار به عنوان منبع اضافه می شود. ادامه هید؟\"\nAddUrl.Text.Url=\"نشانی: %1\"\nResizeOutputSizeOfSource=\"تغییر اندازه خروجی (اندازه منبع)\"\nResizeOutputSizeOfSource.Text=\"رزولوشن های پایه و خروجی به اندازه منبع فعلی تغییر اندازه می دهند.\"\nResizeOutputSizeOfSource.Continue=\"آیا مایلید ادامه دهید؟\"\nPreviewTransition=\"پیش نمایش انتقال\"\nImporter=\"وارد کننده مجموعه صحنه\"\nImporter.SelectCollection=\"مجموعه صحنه را انتخاب کنید\"\nImporter.Collection=\"مجموعه صحنه\"\nImporter.HelpText=\"برای وارد کردن مجموعه ها از OBS یا سایر برنامه های پشتیبانی شده، پرونده ها را به این پنجره اضافه کنید.\"\nImporter.Path=\"مسیر مجموعه\"\nImporter.Program=\"برنامه شناسایی شده\"\nImporter.AutomaticCollectionPrompt=\"جستجوی خودکار مجموعه های صحنه\"\nImporter.AutomaticCollectionText=\"OBS می تواند به طور خودکار مجموعه صحنه های قابل واردات را از برنامه های شخص ثالث پشتیبانی شده پیدا کند. آیا دوست دارید OBS به طور خودکار مجموعه هایی برای شما پیدا کند؟\\n\\nبعداً می توانید این گزینه را در تنظیمات> عمومی> واردکنندگان تغییر دهید.\"\nImporter.SelectFile=\"مرور...\"\nRestart=\"اجرا مجدد\"\nNeedsRestart=\"استودیوی OBS باید دوباره آغاز شود. می‌خواهید اکنون دوباره آغازش کنید؟\"\nLoadProfileNeedsRestart=\"نمایه شامل تنظیماتی است که نیاز به آغاز دوبارهٔ OBS دارند:\\n%1\\n\\nمی‌خواهید برای اثر گرفتن این تنظیمات OBS را دوباره آغاز کنید؟\"\nContextBar.NoSelectedSource=\"بدون انتخاب منبع\"\nContextBar.MediaControls.PlayMedia=\"اجرا رسانه\"\nContextBar.MediaControls.PauseMedia=\"مکث رسانه\"\nContextBar.MediaControls.StopMedia=\"توقف رسانه\"\nContextBar.MediaControls.RestartMedia=\"راه اندازی دوباره رسانه\"\nContextBar.MediaControls.PlaylistNext=\"بعدی در لیست پخش\"\nContextBar.MediaControls.PlaylistPrevious=\"قبلی در لیست پخش\"\nContextBar.MediaControls.BlindSeek=\"ویجت جستجوی رسانه\"\nYouTube.Auth.Ok=\"مجوز با موفقیت انجام شد.\\nاکنون می توانید این صفحه را ببندید.\"\nYouTube.Auth.NoCode=\"فرایند مجوز کامل نشده است.\"\nYouTube.Auth.NoChannels=\"هیچ کانال (های) در حساب انتخابی موجود نیست\"\nYouTube.Auth.WaitingAuth.Title=\"مجوز کاربر YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"لطفاً مجوز را در مرورگر خارجی خود تکمیل کنید. <br> اگر مرورگر خارجی باز نشد ، این پیوند را دنبال کنید و مجوز را تکمیل کنید: <br>%1\"\nYouTube.AuthError.Text=\"دریافت اطلاعات کانال ناموفق بود: %1.\"\nYouTube.Actions.WindowTitle=\"راه اندازی پخش یوتیوب - کانال %1\"\nYouTube.Actions.CreateNewEvent=\"ایجاد پخش جدید\"\nYouTube.Actions.ChooseEvent=\"انتخاب پخش های موجود\"\nYouTube.Actions.Title=\"عنوان*\"\nYouTube.Actions.MyBroadcast=\"پخش جریان من\"\nYouTube.Actions.Description=\"توضیحات\"\nYouTube.Actions.Privacy=\"حریم خصوصی*\"\nYouTube.Actions.Privacy.Private=\"خصوصی\"\nYouTube.Actions.Privacy.Public=\"عمومی\"\nYouTube.Actions.Privacy.Unlisted=\"فهرست‌نشده\"\nYouTube.Actions.Category=\"دسته بندی\"\nYouTube.Actions.Thumbnail=\"تصویر روی صفحه (تامبنیل)\"\nYouTube.Actions.Thumbnail.SelectFile=\"گزینش پرونده…\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"هیچ پرونده‌ای گزیده نشده\"\nYouTube.Actions.Thumbnail.ClearFile=\"پاک کردن\"\nYouTube.Actions.MadeForKids=\"آیا این ویدیو برای کودکان ساخته شده است؟*\"\nYouTube.Actions.MadeForKids.Yes=\"بله ، برای بچه ها ساخته شده است\"\nYouTube.Actions.MadeForKids.No=\"نه ، برای بچه ها ساخته نشده است\"\nYouTube.Actions.AdditionalSettings=\"تنظیمات اضافی\"\nYouTube.Actions.Latency=\"تأخیر\"\nYouTube.Actions.Latency.Normal=\"معمولی\"\nYouTube.Actions.Latency.Low=\"کم\"\nYouTube.Actions.Latency.UltraLow=\"بسیار کم\"\nYouTube.Actions.EnableAutoStart=\"راه اندازی خودکار را فعال کنید\"\nYouTube.Actions.EnableAutoStop=\"توقف خودکار را فعال کنید\"\nYouTube.Actions.AutoStartStop.TT=\"نشان میدهد که آیا پخش برنامه ریزی شده باید به صورت خودکار شروع شود یا نه\"\nYouTube.Actions.EnableDVR=\"فعال کردن DVR\"\nYouTube.Actions.360Video=\"ویدئوی 360 درجه\"\nYouTube.Actions.ScheduleForLater=\"برنامه ریزی کردن برای بعد\"\nYouTube.Actions.RememberSettings=\"این تنظیمات را به یاد داشته باش\"\nYouTube.Actions.Create_Ready=\"ایجاد پخش\"\nYouTube.Actions.Create_GoLive=\"ایجاد پخش و شروع پخش زنده\"\nYouTube.Actions.Choose_Ready=\"انتخاب پخش\"\nYouTube.Actions.Choose_GoLive=\"انتخاب پخش و شروع پخش زنده\"\nYouTube.Actions.Create_Schedule=\"زمان بندی پخش\"\nYouTube.Actions.Create_Schedule_Ready=\"زمان بندی و انتخاب پخش\"\nYouTube.Actions.Dashboard=\"گشودن استودیوی یوتوب\"\nYouTube.Actions.Error.Title=\"خطای ایجاد پخش مستقیم\"\nYouTube.Actions.Error.Text=\"خطای دسترسی یوتیوب '%1'. <br/> شرح مشروح خطا را می توانید در<a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>پیدا کنید.\"\nYouTube.Actions.Error.General=\"خطای دسترسی به YouTube. لطفاً اتصال شبکه یا دسترسی به سرور YouTube خود را بررسی کنید.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"خطای ایجاد پخش زنده '%1'. <br/> شرح مشروح خطا را می توانید در <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> پیدا کنید.\"\nYouTube.Actions.Error.NoStreamCreated=\"جریانی ایجاد نشد. لطفاً حساب خود را مجدداً پیوند دهید.\"\nYouTube.Actions.Error.YouTubeApi=\"خطای APIیوتوب. برای اطّلاعات بیش‌تر لطفاً پروندهٔ گزارش را ببینید.\"\nYouTube.Actions.Error.BroadcastNotFound=\"پخش جریانی منتخب یافت نشد.\"\nYouTube.Actions.Error.FileMissing=\"پروندهٔ گزیده وجود ندارد.\"\nYouTube.Actions.Error.FileOpeningFailed=\"شکست در گشودن پروندهٔ گزیده.\"\nYouTube.Actions.Error.FileTooLarge=\"فالی انتخاب شده خیلی بزرگ است (محدودیت 2 مگابایت)\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"انتقال پخش انجام نشد: %1<br/><br/>اگر این خطا ادامه داشت <a href='https://studio.youtube.com/video/%2/livestreaming'>پخش را در استودیوی YouTube باز کنید</a> و به صورت دستی امتحان کنید.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"پخش در حال انتقال به مرحله آزمایشی است، این ممکن است کمی طول بکشد. لطفاً بعد از 10-30 ثانیه دوباره امتحان کنید.\"\nYouTube.Actions.EventsLoading=\"در حال بارگیری فهرست رویدادها...\"\nYouTube.Actions.EventCreated.Title=\"رویداد ایجاد شد\"\nYouTube.Actions.EventCreated.Text=\"رویداد با موفقیت ایجاد شد.\"\nYouTube.Actions.Stream=\"جریان\"\nYouTube.Actions.Stream.ScheduledFor=\"برای %1 برنامه ریزی شده\"\nYouTube.Actions.Stream.Resume=\"از سرگیری جریان قطع شده\"\nYouTube.Actions.Stream.YTStudio=\"به طور خودکار توسط YouTube Studio ایجاد شده است\"\nYouTube.Actions.Notify.CreatingBroadcast=\"در حال ایجاد پخش زنده جدید ، لطفاً منتظر بمانید ...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"شروع دستی مورد نیاز\"\nYouTube.Actions.AutoStartStreamingWarning=\"شروع خودکار برای این رویداد لغو شده است ، بر روی \\\"برو به لایو\\\" کلیک کنید تا پخش شروع شود\"\nYouTube.Actions.AutoStopStreamingWarning=\"شما نمی توانید دوباره وصل شوید. <br> جریان شما متوقف می شود و دیگر زنده نخواهید بود.\"\nYouTube.Errors.liveStreamingNotEnabled=\"پخش مستقیم در کانال انتخابی YouTube فعال نیست. <br/><br/>برای اطلاعات بیشتر به<a href='https://www.youtube.com/features'>youtube.com/features</a> مراجعه کنید.\"\nYouTube.Errors.livePermissionBlocked=\"پخش مستقیم در کانال انتخابی YouTube در دسترس نیست.<br/>لطفاً توجه داشته باشید که پس از فعال کردن آن در تنظیمات کانال شما ممکن است 24 ساعت طول بکشد.<br/><br/>مشاهده<a href='https://www.youtube.com/features'>youtube.com/features</a> برای جزئیات بیشتر.\"\nYouTube.Errors.errorExecutingTransition=\"انتقال به دلیل یک خطای انتهایی انجام نشد. لطفاً چند ثانیه دیگر دوباره امتحان کنید.\"\nYouTube.Errors.errorStreamInactive=\"YouTube داده‌ای را برای جریان شما دریافت نمی‌کند. لطفاً پیکربندی خود را بررسی کنید و دوباره امتحان کنید.\"\nYouTube.Errors.invalidTransition=\"تلاش برای انتقال نامعتبر بود. این ممکن است به این دلیل باشد که جریان یک انتقال قبلی را تمام نکرده است. لطفا چند ثانیه صبر کنید و دوباره امتحان کنید.\"\nYouTube.DocksRemoval.Title=\"پایگاه‌های مرورگر YouTube قدیمی را پاک کنید\"\nYouTube.DocksRemoval.Text=\"این پایه‌های مرورگر به‌عنوان منسوخ شده حذف خواهند شد:\\n\\n%1\\nبه‌جای آن از «Docks/YouTube Live Control Room» استفاده کنید.\"\nConfigDownload.WarningMessageTitle=\"اخطار\"\nFailedToStartStream.MissingConfigURL=\"هیچ نشانی اینترنتی پیکربندی برای سرویس فعلی موجود نیست\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"نشانی اینترنتی RTMP سفارشی مشخص نشده است\"\nFailedToStartStream.InvalidCustomConfig=\"پیکربندی سفارشی نامعتبر است\"\nFailedToStartStream.MissingCanvas=\"یک بوم اضافی پیکربندی شده وجود ندارد\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"سرویس ویدیوی چند مسیری ایجاد نشد\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"خروجی RTMP ویدیوی چند مسیری ایجاد نشد\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC در دسترس نیست.\\on\\در یافتن نوع رمزگذار «%1» ناموفق بود\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"رمزگذار ویدیوی «%1» ایجاد نشد (نوع: «%2»)\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"هنگام ایجاد رمزگذار «%1»، اطلاعات ویدیوی OBS دریافت نشد (نوع: «%2»)\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"رمزگذار صدا ایجاد نشد\"\nFailedToStartStream.NoRTMPURLInConfig=\"پیکربندی شامل نشانی اینترنتی RTMP(ها) هدف جریان نیست\"\nFailedToStartStream.FallbackToDefault=\"شروع استریم با استفاده از %1 ناموفق بود. آیا می خواهید با استفاده از تنظیمات تک کد مجددا امتحان کنید؟\"\nFailedToStartStream.ConfigRequestFailed=\"پیکربندی از %1<br><br>خطای HTTP: %2 واکشی نشد\"\nFailedToStartStream.WarningUnknownStatus=\"مقدار وضعیت ناشناخته \\\"%1\\\" دریافت کرد\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nآیا می‌خواهید بدون %1 به پخش جریانی ادامه دهید؟\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nآیا می‌خواهید پخش جریانی را ادامه دهید؟\"\nFailedToStartStream.MissingEncoderConfigs=\"پیکربندی رفتن به استریم شامل تنظیمات رمزگذار نبود\"\nFailedToStartStream.InvalidEncoderConfig=\"پیکربندی پخش زنده شامل پیکربندی رمزگذار نامعتبر بود\"\nFailedToStartStream.StatusMissingHTML=\"درخواست رفتن به استریم یک خطای نامشخص را نشان داد\"\nFailedToStartStream.NoConfigSupplied=\"پیکربندی موجود نیست\"\nMultitrackVideo.Info=\"%1 به طور خودکار تنظیمات شما را برای رمزگذاری و ارسال چندین کیفیت ویدیو بهینه می کند. با انتخاب این گزینه اطلاعات %2 درباره رایانه و تنظیم نرم‌افزار شما ارسال می شود.\"\nMultitrackVideo.IncompatibleSettings.Title=\"تنظیمات ناسازگار\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 در حال حاضر با:\\n\\n%2\\nبرای ادامه جریان با %1 سازگار نیست، تنظیمات ناسازگار را غیرفعال کنید:\\n\\n%3\\nو دوباره جریان را شروع کنید.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"غیرفعال کردن برای این استریم و شروع استریم\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"به‌روزرسانی تنظیمات و شروع استریم کردن\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 در حال حاضر با [صوت ← عمومی ← کانالها] تنظیم شده روی «%2»، %3 سازگار نیست\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[صوت ← عمومی ← کانالها] باید روی «%1» تنظیم شود\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 به تنظیمات مختلف چندگانه برای [صوت ← عمومی ← کانال ها] نیاز دارد\"\nBasic.OpenPluginManager=\"مدیریت افزونه\"\nPluginManager=\"مدیریت افزونه\"\nPluginManager.Restart=\"برای اعمال تغییرات پلاگین لازم است OBS Studio ری‌استارت شود\"\nPluginManager.MissingPlugin=\"[افزونه یافت نشد]\"\nPluginManager.Section.Discover=\"مرور\"\nPluginManager.Section.Manage=\"نصب شده\"\nPluginManager.Section.Updates=\"به‌روزرسانی‌ها\"\nPluginManager.Section.Manage.Title=\"مدیریت پلاگین‌های فعال شده\"\n"
  },
  {
    "path": "frontend/data/locale/fi-FI.ini",
    "content": "Language=\"suomi\"\nApply=\"Käytä\"\nCancel=\"Peruuta\"\nClose=\"Sulje\"\nSave=\"Tallenna\"\nDiscard=\"Hylkää\"\nDisable=\"Poista käytöstä\"\nYes=\"Kyllä\"\nNo=\"Ei\"\nAdd=\"Lisää\"\nRemove=\"Poista\"\nRename=\"Uudelleennimeä\"\nInteract=\"Vuorovaikuta\"\nFilters=\"Suodattimet\"\nProperties=\"Ominaisuudet\"\nMoveUp=\"Siirrä ylös\"\nMoveDown=\"Siirrä alas\"\nSettings=\"Asetukset\"\nDisplay=\"Näyttö\"\nName=\"Nimi\"\nExit=\"Poistu\"\nMixer=\"Äänimikseri\"\nBrowse=\"Selaa\"\nDroppedFrames=\"Pudotettuja ruutuja %1 (%2%)\"\nProjector.Open.Program=\"Avaa ohjelman projektori\"\nProjector.Open.Preview=\"Avaa esinäkymä projektori\"\nProjector.Open.Scene=\"Avaa näkymä projektori\"\nProjector.Open.Source=\"Avaa lähteen projektori\"\nProjector.Open.Multiview=\"Avaa Moninäkymä\"\nProjector.Display=\"Näyttö %1\"\nProjector.Window=\"Uusi ikkuna\"\nProjector.Title=\"Projektori\"\nProjector.Title.Scene=\"Näkymä: %1\"\nProjector.Title.Source=\"Lähde: %1\"\nProjector.Title.Multiview=\"Moninäkymä\"\nProjector.ResizeWindowToContent=\"Sovita ikkuna sisältöön\"\nClear=\"Tyhjennä\"\nRevert=\"Palauta\"\nShow=\"Näytä\"\nHide=\"Piilota\"\nUnhideAll=\"Näytä kaikki\"\nUntitled=\"Nimetön\"\nNew=\"Uusi\"\nDuplicate=\"Monista\"\nEnable=\"Ota käyttöön\"\nDisableOSXVSync=\"Poista macOS V-Sync käytöstä\"\nResetOSXVSyncOnExit=\"Palauta macOS V-Sync suljettaessa\"\nHighResourceUsage=\"Enkoodaus on ylikuormitettu! Harkitse laatu-asetusten laskemista tai käytä nopeampaa enkoodauksen esiasetusta.\"\nTransition=\"Siirtymä\"\nQuickTransitions=\"Pikasiirtymät\"\nFadeToBlack=\"Häivytys mustaan\"\nLeft=\"Vasen\"\nRight=\"Oikea\"\nTop=\"Ylhäältä\"\nBottom=\"Alhaalta\"\nReset=\"Nollaa\"\nHours=\"Tuntia\"\nMinutes=\"Minuuttia\"\nSeconds=\"Sekuntia\"\nDeprecated=\"Vanhentunut\"\nReplayBuffer=\"Toistopuskuri\"\nImport=\"Tuo\"\nExport=\"Vie\"\nCopy=\"Kopioi\"\nPaste=\"Liitä\"\nPasteReference=\"Liitä (viite)\"\nPasteDuplicate=\"Liitä (Kopio)\"\nRemuxRecordings=\"Muunna tallenteita\"\nNext=\"Seuraava\"\nBack=\"Edellinen\"\nDefaults=\"Oletukset\"\nRestoreDefaults=\"Oletukset\"\nHideMixer=\"Piilota mikseristä\"\nTransitionOverride=\"Siirtymän ohitus\"\nShowTransition=\"Näytä siirtymä\"\nHideTransition=\"Piilota siirtymä\"\nNone=\"Ei mitään\"\nStudioMode.Preview=\"Esikatselu\"\nStudioMode.Program=\"Ohjelma\"\nStudioMode.PreviewSceneName=\"Esikatselu: %1\"\nStudioMode.ProgramSceneName=\"Ohjelma: %1\"\nShowInMultiview=\"Näytä moninäkymässä\"\nVerticalLayout=\"Pystynäkymä\"\nGroup=\"Ryhmitä\"\nDoNotShowAgain=\"Älä näytä uudestaan\"\nDefault=\"(Oletus)\"\nCalculating=\"Lasketaan...\"\nFullscreen=\"Koko näyttö\"\nWindowed=\"Ikkunoitu\"\nRefreshBrowser=\"Päivitä\"\nAspectRatio=\"Kuvasuhde <b>%1:%2</b>\"\nLockVolume=\"Lukitse äänitaso\"\nLogViewer=\"Näytä loki\"\nShowOnStartup=\"Näytä käynnistyksessä\"\nOpenFile=\"Avaa tiedosto\"\nAddScene=\"Lisää näkymä\"\nAddSource=\"Lisää lähde\"\nRemoveScene=\"Poista valittu näkymä\"\nRemoveSource=\"Poista valitut näkymät\"\nMoveSceneUp=\"Siirrä näkymä ylös\"\nMoveSceneDown=\"Siirrä näkymä alas\"\nMoveSourceUp=\"Siirrä lähteet ylös\"\nMoveSourceDown=\"Siirrä lähteet alas\"\nSourceProperties=\"Avaa lähteen ominaisuudet\"\nSourceFilters=\"Avoimen lähdekoodin suodattimet\"\nMixerToolbarMenu=\"Äänen Mikserivalikko\"\nSceneFilters=\"Avaa näkymän suodattimet\"\nList=\"Lista\"\nGrid=\"Ruudukko\"\nAutomatic=\"Automaattinen\"\nComingSoon=\"Tulossa pian\"\nPluginsFailedToLoad.Title=\"Liitännäisen latausvirhe\"\nPluginsFailedToLoad.Text=\"Seuraavien OBS-liitännäisten lataaminen epäonnistui:\\n\\n%1\\nPäivitä tai poista kyseiset liitänäiset.\"\nAlreadyRunning.Title=\"OBS on jo käynnissä\"\nAlreadyRunning.Text=\"OBS on jo käynnissä! Ellet tarkoittanut tehdä näin, ole hyvä ja sulje aikaisemmat OBS-prosessit ennen uuden käynnistämistä. Jos olet asettanut OBS:n pienentymään ilmaisinalueelle, varmista ettei se ole siellä yhä päällä.\"\nAlreadyRunning.LaunchAnyway=\"Käynnistä joka tapauksessa\"\nCrashHandling.Dialog.Title=\"OBS Studion kaatuminen havaittu\"\nCrashHandling.Labels.Text=\"OBS Studio ei sulkeutunut oikeaoppisesti.\\n\\nKäynnistetäänkö vikasietotilassa (kolmannen osapuolen liitännäiset, skriptit ja WebSockets poistettuna käytöstä)?\"\nCrashHandling.Labels.PrivacyNotice=\"Voit myös valita viimeisimmän kaatumisraportin automaattisen lähettämisen OBS-projektille.<br /><br />Lue <a href='https://obsproject.com/privacy-policy'>tietosuojakäytäntö</a>, ennen kuin lähetät yhtäkään tiedostoa, ja kiinnitä erityistä huomiota tiedostojen lähetykseen liittyvään osioon.\"\nCrashHandling.Checkbox.SendReport=\"Olen lukenut tietosuojakäytännön ja hyväksyn lähetyksen.\"\nCrashHandling.Buttons.LaunchSafe=\"Käynnistä vikasietotilassa\"\nCrashHandling.Buttons.LaunchNormal=\"Käynnistä normaalissa tilassa\"\nCrashHandling.Errors.UploadJSONError=\"Virhe tapahtui ladatessa viimeisintä kaatumisilmoitusta. Yritäthän myöhemmin uudelleen.\"\nCrashHandling.Errors.Title=\"Kaatumislokin lähetysvirhe\"\nSafeMode.Restart=\"Haluatko käynnistää OBS:n uudelleen vikasietotilaan (kolmannen osapuolen liitännäiset, komentosarjat ja WebSocketit poistetaan käytöstä)?\"\nSafeMode.RestartNormal=\"Haluatko käynnistää OBS:n uudelleen normaalissa tilassa?\"\nChromeOS.Title=\"Alustaa ei tueta\"\nChromeOS.Text=\"OBS vaikuttaa olevan käynnissä ChromeOS-kontissa. Tämä alusta ei ole tuettu.\"\nWine.Title=\"Wine-ohjelmiston kautta ajaminen havaittu\"\nWine.Text=\"OBS:n käyttö Wine-ohjelman kautta ei ole tuettu, ja monet ominaisuudet, kuten kuvakaappaaminen tai laitelähteet eivät toimi tai toimivat ainoastaan rajoitetussa määrin.<br><br>On suositeltavaa ajaa OBS:n kotoperäistä (natiivia) versiota, esimerkiksi <a href='https://flathub.org/apps/details/com.obsproject.Studio'>oma flatpak-versiomme</a> tai käyttöjärjestelmäsi omista ohjelmavarastoista löytyvää pakettia.\"\nDockCloseWarning.Title=\"Telakoitu ikkuna suljettu\"\nDockCloseWarning.Text=\"Suljit juuri telakoidun ikkunan. Jos haluat sen takaisin näkyviin, mene Telakat-valikkoon.\"\nExtraBrowsers=\"Mukautetut selaintelakat\"\nExtraBrowsers.Info=\"Lisää telakoita antamalla niille nimi ja URL-osoite, sitten klikkaa Käytä tai Sulje avataksesi telakat. Voit lisätä ja poistaa telakoita milloin tahansa.\"\nExtraBrowsers.DockName=\"Telakan nimi\"\nAuth.Authing.Title=\"Tunnistaudutaan...\"\nAuth.Authing.Text=\"Todennetaan %1, ole hyvä ja odota...\"\nAuth.AuthFailure.Title=\"Tunnistautumisvirhe\"\nAuth.AuthFailure.Text=\"Varmennus epäonnistui %1:n kanssa:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Tunnistautuminen vaaditaan\"\nAuth.InvalidScope.Text=\"Tunnistautumisvaatimukset %1:iin ovat muuttuneet. Jotkin ominaisuuksista eivät välttämättä ole käytössä.\"\nAuth.LoadingChannel.Title=\"Ladataan kanavan tietoja...\"\nAuth.LoadingChannel.Text=\"Ladataan kanavan tietoja %1:stä, ole hyvä ja odota...\"\nAuth.LoadingChannel.Error=\"Kanavan tietoja ei saatu.\"\nAuth.ChannelFailure.Title=\"Kanavan lataus epäonnistui\"\nAuth.ChannelFailure.Text=\"Kanavan tietojen lataaminen epäonnistui %1:sta\\n\\n%2: %3\"\nAuth.StreamInfo=\"Lähetyksen tiedot\"\nTwitchAuth.Stats=\"Twitch-tilastot\"\nTwitchAuth.Feed=\"Twitch-tapahtumasyöte\"\nTwitchAuth.TwoFactorFail.Title=\"Striimiavainta ei saatu noudettua\"\nTwitchAuth.TwoFactorFail.Text=\"OBS ei pystynyt yhdistämään Twitch-tiliin. Varmista että kaksivaiheinen tunnistus on käytössä <a href='https://www.twitch.tv/settings/security'>Twitchin turvallisuusasetuksissa</a>, koska se vaaditaan lähetystä varten.\"\nRestreamAuth.Channels=\"Restream-kanavat\"\nCopy.Filters=\"Kopioi suodattimet\"\nPaste.Filters=\"Liitä suodattimet\"\nBrowserPanelInit.Title=\"Valmistellaan selainta...\"\nBrowserPanelInit.Text=\"Valmistellaan selainta, ole hyvä ja odota...\"\nBandwidthTest.Region=\"Alue\"\nBandwidthTest.Region.US=\"Yhdysvallat\"\nBandwidthTest.Region.EU=\"Eurooppa\"\nBandwidthTest.Region.Asia=\"Aasia\"\nBandwidthTest.Region.Other=\"Muu\"\nBasic.AutoConfig=\"Automaattinen määritystoiminto\"\nBasic.AutoConfig.ApplySettings=\"Ota käyttöön\"\nBasic.AutoConfig.StartPage=\"Käyttötiedot\"\nBasic.AutoConfig.StartPage.SubTitle=\"Määrittele kuinka haluat käyttää ohjelmaa\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimoi lähettämiseen, tallentaminen on toissijaista\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimoi tallentamiseen, lähettäminen on toissijaista\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Käytän vain virtuaalista kameraa\"\nBasic.AutoConfig.VideoPage=\"Kuva-asetukset\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Määrittele haluamasi kuva-asetukset\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Käytä nykyistä (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Näyttö %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Käytä nykyistä (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Joko 60 tai 30, mutta suositaan 60 kun mahdollista\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Joko 60 tai 30, mutta suositaan suurta resoluutiota\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Huomautus: Piirtoalueen resoluutio ei välttämättä ole sama kuin resoluutio jolla lähetetään tai tallennetaan. Lähetyksen/tallennuksen resoluutio saatetaan skaalata piirtoalueesta pienemmäksi resurssien käytön vähentämiseksi tai bittinopeusvaatimusten vuoksi.\"\nBasic.AutoConfig.StreamPage=\"Lähetyksen tiedot\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Ole hyvä ja syötä lähetyksen tiedot\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Yhdistä tili (suositeltu)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Katkaise yhteys tiliin\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Katkaise yhteys tiliin?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Tämä muutos tulee voimaan välittömästi. Oletko varma että haluat katkaista yhteyden tiliin?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Hae striimiavain\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Lisää tietoa\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Käytä striimiavainta\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Käytä striimiavainta (Laajennettu)\"\nBasic.AutoConfig.StreamPage.Service=\"Palvelu\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Näytä kaikki...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Mukautettu...\"\nBasic.AutoConfig.StreamPage.Server=\"Palvelin\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Striimiavain\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: kirjoita salaussalasana.\\nRTMP: kirjoita avain, jonka palvelu tarjoaa.\\nSRT: kirjoita streamin id, jos palvelu käyttää sitä.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Enkooderin avain\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Kantoaalto\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Yhdistetty tili\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Arvioi bittinopeus kaistan nopeustestin avulla (saattaa kestää muutaman minuutin)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Suosi laitteistokoodausta\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Laitteistoenkoodaus poistaa suurimman osan CPU-käytöstä, mutta saattaa vaatia suuremman bittinopeuden saavuttaakseen saman laadun.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Lähetysvaroitus\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Kaistan testauksessa lähetetään satunnaista videodataa kanavallesi ilman ääntä. On suositeltavaa ottaa pois käytöstä lähetysten tallennus ja asettaa lähetys yksityiseksi, kunnes testi on suoritettu. Jatketaanko?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Testaa %1\"\nBasic.AutoConfig.TestPage=\"Lopulliset tulokset\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Ohjelma käynnistää nyt muutamia testejä ihanteellisten asetusten arvioimiseksi\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testaus valmis\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Suoritetaan kaistan testausta, tämä saattaa kestää muutaman minuutin...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Tämän palvelun protokollalle ei löytynyt ulostuloa\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Yhdistetään: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Palvelimiin ei saada yhteyttä, ole hyvä ja tarkista internet-yhteys ja yritä uudelleen.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testataan kaistaa: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testataan lähetysenkooderia, tämä saattaa kestää hetken...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testataan tallennusenkooderia, tämä saattaa kestää hetken...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Enkooderin käynnistys epäonnistui\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testataan %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Lähetysenkooderi\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Tallennusenkooderi\"\nBasic.AutoConfig.TestPage.Result.Header=\"Ohjelma on määrittänyt nämä asetukset ihanteellisimmaksi juuri sinulle:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Ottaaksesi asetukset käyttöön, paina \\\"Ota käyttöön\\\". Määrittääksesi asetukset uudelleen, paina \\\"Edellinen\\\". Jos haluat määrittää asetukset itse, paina \\\"Peruuta\\\" ja avaa asetukset.\"\nBasic.AutoConfig.Info=\"Automaattinen määritystoiminto määrittää parhaat asetukset perustuen tietokoneesi laitteistoon ja internetin nopeuteen.\"\nBasic.AutoConfig.RunAnytime=\"Tämän voi suorittaa milloin vain Työkalut-valikosta.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Suoratoiston (skaalattu) resoluutio\"\nBasic.Stats=\"Tilastot\"\nBasic.Stats.CPUUsage=\"CPU-käyttö\"\nBasic.Stats.HDDSpaceAvailable=\"Tallennustilaa vapaana\"\nBasic.Stats.MemoryUsage=\"Muistinkäyttö\"\nBasic.Stats.AverageTimeToRender=\"Keskimääräinen aika kuvaruudun hahmontamiseen\"\nBasic.Stats.SkippedFrames=\"Ohitettuja ruutuja enkoodauksen hitauden takia\"\nBasic.Stats.MissedFrames=\"Ruutuja menetetty renderoinnin hitauden takia\"\nBasic.Stats.Output.Stream=\"Lähetys\"\nBasic.Stats.Output.Recording=\"Tallennus\"\nBasic.Stats.Status=\"Tila\"\nBasic.Stats.Status.Recording=\"Tallennus\"\nBasic.Stats.Status.Live=\"SUORA\"\nBasic.Stats.Status.Reconnecting=\"Yhdistetään uudelleen\"\nBasic.Stats.Status.Inactive=\"Ei käytössä\"\nBasic.Stats.Status.Active=\"Aktiivinen\"\nBasic.Stats.DroppedFrames=\"Pudotettuja ruutuja (verkko)\"\nBasic.Stats.MegabytesSent=\"Datan ulostulo yhteensä\"\nBasic.Stats.Bitrate=\"Bittinopeus\"\nBasic.Stats.DiskFullIn=\"Levy täynnä ajassa (arvio)\"\nBasic.Stats.DiskFullIn.Text=\"%1 tuntia, %2 minuuttia\"\nBasic.Stats.ResetStats=\"Nollaa tilastot\"\nResetUIWarning.Title=\"Haluatko varmasti nollata käyttöliittymän?\"\nResetUIWarning.Text=\"Käyttöliittymän nollaus piilottaa lisätyt telakat. Sinun täytyy laittaa telakat näkyviin \\\"Näkymät\\\" -valikosta jos tahdot ne näkyviin.\\n\\nHaluatko varmasti nollata käyttöliittymän?\"\nUpdater.Title=\"Uusi päivitys on saatavilla\"\nUpdater.Text=\"Uusi päivitys on saatavilla:\"\nUpdater.UpdateNow=\"Päivitä nyt\"\nUpdater.RemindMeLater=\"Muistuta myöhemmin\"\nUpdater.Skip=\"Ohita versio\"\nUpdater.NoUpdatesAvailable.Title=\"Päivityksiä ei ole saatavilla\"\nUpdater.NoUpdatesAvailable.Text=\"Päivityksiä ei ole tällä hetkellä saatavilla\"\nUpdater.BranchNotFound.Title=\"Päivityskanava poistettu\"\nUpdater.BranchNotFound.Text=\"Valitsemasi päivityskanava ei ole enää käytettävissä. OBS on palautettu oletusarvoon.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Eheystarkistus ei saatavilla\"\nUpdater.RepairButUpdatesAvailable.Text=\"Tiedoston eheyden tarkistaminen on mahdollista vain uusimmalla versiolla. Tarkista ja päivitä OBS-asennus valitsemalla Ohje → Tarkista päivitykset.\"\nUpdater.RepairConfirm.Title=\"Vahvistua eheystarkistus\"\nUpdater.RepairConfirm.Text=\"Eheyden tarkistus tarkistaa OBS-asennuksesi vioittumisen varalta ja lataa rikkinäiset/muokatut tiedostot uudelleen. Tämä voi kestää hetken.\\n\\nHaluatko jatkaa?\"\nUpdater.FailedToLaunch=\"Päivittäjän käynnistäminen epäonnistui\"\nQuickTransitions.SwapScenes=\"Vaihda esikatselu-/ohjelman näkymät siirtymisen jälkeen\"\nQuickTransitions.SwapScenesTT=\"Vaihtaa esikatselun ja ohjelman kohtauksia siirtymisen jälkeen (jos ohjelman alkuperäinen kohtaus on vielä olemassa).\\nTämä ei kumoa mitään muutoksia, jotka on mahdollisesti tehty ohjelman alkuperäiseen kohtaukseen.\"\nQuickTransitions.DuplicateScene=\"Kahdenna näkymä\"\nQuickTransitions.DuplicateSceneTT=\"Kun muokkaat samaa kohtausta, sallii lähteiden muokkaamisen muuntamisen/näkyvyyden muuttamatta ohjelman tuotosta.\\nMuokataksesi lähteiden ominaisuuksia muuttamatta ohjelman tuotosta, ota käyttöön 'Monista lähteet'.\\nTämän arvon muuttaminen palauttaa nykyisen ohjelman kohtauksen (jos se on vielä olemassa).\"\nQuickTransitions.EditProperties=\"Kahdenna lähteet\"\nQuickTransitions.EditPropertiesTT=\"Kun muokkaat samaa kohtausta, sallii lähteiden ominaisuuksien muokkaamisen muuttamatta ohjelman tuotosta.\\nTätä voidaan käyttää vain, jos 'Monista kohtaus' on käytössä.\\nTietyt lähteet (kuten talteenotto tai medialähteet) eivät tue tätä eikä niitä voida muokata erikseen.\\nTämän arvon muuttaminen palauttaa nykyisen ohjelman kohtauksen (jos se on vielä olemassa).\\n\\nVaroitus: koska lähteitä kahdennetaan, tämä saattaa vaatia ylimääräisiä järjestelmän tai videon resursseja.\"\nQuickTransitions.HotkeyName=\"Pikasiirtymä: %1\"\nBasic.AddTransition=\"Lisää muokattava siirtymä\"\nBasic.RemoveTransition=\"Poista muokattava siirtymä\"\nBasic.TransitionProperties=\"Siirtymän ominaisuudet\"\nBasic.SceneTransitions=\"Näkymäsiirtymät\"\nBasic.TransitionDuration=\"Kesto\"\nBasic.TogglePreviewProgramMode=\"Studio-tila\"\nBasic.EnablePreviewProgramMode=\"Käytä studiotilaa\"\nBasic.DisablePreviewProgramMode=\"Poista studiotila käytöstä\"\nUndo.Undo=\"Kumoa\"\nUndo.Redo=\"Tee uudelleen\"\nUndo.Add=\"Lisää %1'\"\nUndo.Delete=\"Poista %1\"\nUndo.Rename=\"Uudelleennimeä %1\"\nUndo.SceneCollection.Switch=\"Vaihda %1\"\nUndo.Item.Undo=\"Kumoa %1\"\nUndo.Item.Redo=\"Tee uudelleen %1\"\nUndo.Sources.Multi=\"Poista %1 lähdettä\"\nUndo.Filters=\"Suodatin muutokset '%1'\"\nUndo.Filters.Paste.Single=\"Liitä '%1'-Suodatin '%2'-Suodattimeen\"\nUndo.Filters.Paste.Multiple=\"Kopioi suodattimet '%1' - sta '%2'\"\nUndo.Transform=\"Muunna lähde (lähteet) In '%1'\"\nUndo.Transform.Paste=\"Liitä muuntaminen '%1'\"\nUndo.Transform.Rotate=\"Kierto '%1'\"\nUndo.Transform.Reset=\"Muunna nollaus '%1'\"\nUndo.Transform.HFlip=\"Vaakasuora kääntö%1'\"\nUndo.Transform.VFlip=\"Pystysuuntainen kääntö '%1'\"\nUndo.Transform.FitToScreen=\"Sovita näytölle '%1'\"\nUndo.Transform.StretchToScreen=\"Venytä ruudulle '%1 ' -tilassa\"\nUndo.Transform.Center=\"Keskitä ruudulle '%1'\"\nUndo.Transform.VCenter=\"Pystysuora keskitys näytölle '%1'\"\nUndo.Transform.HCenter=\"Vaakasuora keskitys näytölle '%1'\"\nUndo.Volume.Change=\"Äänenvoimakkuuden muutos '%1'\"\nUndo.Volume.Mute=\"Mykistä '%1'\"\nUndo.Volume.Unmute=\"Poista mykistys '%1'\"\nUndo.Balance.Change=\"Äänitasapainon muutos '%1'\"\nUndo.SyncOffset.Change=\"Äänen synkronoinnin tason muutos '%1'\"\nUndo.MonitoringType.Change=\"Muuta äänenvalvontaa '%1'\"\nUndo.Mixers.Change=\"Vaihda äänimikserit '%1'\"\nUndo.ForceMono.On=\"Ota käyttöön pakota mono '%1'\"\nUndo.ForceMono.Off=\"Poista käytöstä pakota mono '%1'\"\nUndo.Properties=\"Ominaisuuden muutos '%1'\"\nUndo.Scene.Duplicate=\"Kahdenna näkymä '%1'\"\nUndo.ShowTransition=\"Näytä siirtymä '%1'\"\nUndo.HideTransition=\"Piilota siirtymä '%1'\"\nUndo.ShowSceneItem=\"Näytä '%1' kohdassa '%2'\"\nUndo.HideSceneItem=\"Piilota '%1' kohdassa '%2'\"\nUndo.ReorderSources=\"Tallennuksen lähteet '%1'\"\nUndo.MoveUp=\"Siirrä '%1' ylöspäin '%2'\"\nUndo.MoveDown=\"Siirrä '%1' alaspäin '%2'\"\nUndo.MoveToTop=\"Siirrä '%1' ylhäälle '%2'\"\nUndo.MoveToBottom=\"Siirrä '%1' alas '%2'\"\nUndo.PasteSource=\"Liitä lähteitä '%1'\"\nUndo.PasteSourceRef=\"Liitä lähdeviitteet '%1'\"\nUndo.GroupItems=\"Ryhmittele kohteet '%1'\"\nTransitionNameDlg.Text=\"Syötä siirtymän nimi\"\nTransitionNameDlg.Title=\"Siirtymän nimi\"\nTitleBar.SafeMode=\"VIKASIETOTILA\"\nTitleBar.PortableMode=\"Siirrettävä tila\"\nTitleBar.Profile=\"Profiili\"\nTitleBar.Scenes=\"Näkymät\"\nNameExists.Title=\"Nimi on jo olemassa\"\nNameExists.Text=\"Nimi on jo käytössä.\"\nNoNameEntered.Title=\"Syötä sopiva nimi\"\nNoNameEntered.Text=\"Et voi käyttää tyhjiä nimiä.\"\nConfirmStart.Title=\"Aloita lähetys?\"\nConfirmStart.Text=\"Haluatko varmasti aloittaa lähetyksen?\"\nConfirmStop.Title=\"Pysäytä lähetys?\"\nConfirmStop.Text=\"Haluatko varmasti pysäyttää lähetyksen?\"\nConfirmStopRecord.Title=\"Pysäytä tallennus?\"\nConfirmStopRecord.Text=\"Haluatko varmasti lopettaa tallennuksen?\"\nConfirmBWTest.Title=\"Aloita kaistan nopeustesti?\"\nConfirmBWTest.Text=\"OBS on asetettu kaistan nopeustestitilaan. Tämä tila antaa sinun testata verkkoa ilman että kanava menee Live-tilaan. Kun olet valmis testauksen kanssa, sinun täytyy ottaa se pois käytöstä, jotta muut voivat nähdä lähetyksesi.\\n\\nHaluatko jatkaa?\"\nConfirmRemove.Title=\"Vahvista poisto\"\nConfirmRemove.Text=\"Haluatko varmasti poistaa '%1'?\"\nConfirmRemove.TextMultiple=\"Haluatko varmasti poistaa %1 kohdetta?\"\nConfirmReset.Title=\"Palauta ominaisuudet\"\nConfirmReset.Text=\"Haluatko varmasti palauttaa nykyiset ominaisuudet oletusasetuksiin?\"\nOutput.StartStreamFailed=\"Lähetyksen aloittaminen epäonnistui\"\nOutput.StartRecordingFailed=\"Tallennuksen aloittaminen epäonnistui\"\nOutput.StartReplayFailed=\"Toistopuskurin käynnistäminen epäonnistui\"\nOutput.StartVirtualCamFailed=\"Virtuaalikameran käynnistäminen epäonnistui\"\nOutput.StartFailedGeneric=\"Ulostulon käynnistäminen epäonnistui. Tarkista loki lisätietoja varten.\\n\\nHuomio: Jos käytät NVENC tai AMD -enkoodereita, varmista, että näytönohjaimen ajurit on päivitetty ajantasalle.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Uusintoja ei voi tallentaa pysäytettynä\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Varoitus: Uusintoja ei voi tallentaa kun tallennus on pysäytetty.\"\nOutput.ConnectFail.Title=\"Yhdistäminen epäonnistui\"\nOutput.ConnectFail.BadPath=\"Viallinen polku tai yhteysosoite. Tarkista, että asetuksesi ovat kunnossa.\"\nOutput.ConnectFail.ConnectFailed=\"Palvelimelle yhdistäminen epäonnistui\"\nOutput.ConnectFail.InvalidStream=\"Kanavaa tai lähetysavainta ei voida käyttää. Tarkista lähetysavain varmuuden vuoksi. Jos se kuitenkin on oikein, vika saattaa olla yhdistettävässä palvelimessa.\"\nOutput.ConnectFail.HdrDisabled=\"HDR-ulostulo on tällä hetkellä pois käytöstä tälle ulostulolle.\"\nOutput.ConnectFail.Error=\"Odottamaton virhe ilmeni palvelimeen yhdistäessä. Lisää tietoa saat lokitiedostosta.\"\nOutput.ConnectFail.Disconnected=\"Yhteys palvelimeen katkaistiin.\"\nOutput.StreamEncodeError.Title=\"Enkoodausvirhe\"\nOutput.StreamEncodeError.Msg=\"Lähetyksen aikana tapahtui enkoodausvirhe.\"\nOutput.StreamEncodeError.Msg.LastError=\"Striimauksen aikana tapahtui virhe:<br><br>%1\"\nOutput.RecordFail.Title=\"Tallennuksen aloittaminen epäonnistui\"\nOutput.RecordFail.Unsupported=\"Ulostulon muoto ei ole tuettu tai se ei tue useampaa kuin yhtä ääniraitaa. Tarkista asetuksesi ja yritä uudelleen.\"\nOutput.RecordNoSpace.Title=\"Liian vähän levytilaa\"\nOutput.RecordNoSpace.Msg=\"Levytilaa ei ole riittävästi tallennuksen jatkamiseen.\"\nOutput.RecordError.Title=\"Tallennusvirhe\"\nOutput.RecordError.Msg=\"Tallennuksen aikana tapahtui määrittelemätön virhe.\"\nOutput.RecordError.EncodeErrorMsg=\"Tallennuksen aikana tapahtui enkoodausvirhe.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Striimauksen aikana tapahtui virhe:<br><br>%1\"\nOutput.BadPath.Title=\"Viallinen tiedostopolku\"\nOutput.BadPath.Text=\"Määritettyä tallennuspolkua ei voitu avata. Tarkista polku kohdasta Asetukset → Ulostulo → Tallennus.\"\nOutput.NoBroadcast.Title=\"Lähetystä ei ole määritetty\"\nOutput.NoBroadcast.Text=\"Sinun täytyy määrittää lähetys, ennen kuin voit aloittaa striimauksen.\"\nOutput.BroadcastStartFailed=\"Lähetyksen käynnistäminen epäonnistui\"\nOutput.BroadcastStopFailed=\"Lähetyksen pysäyttäminen epäonnistui\"\nLogUploadDialog.Title=\"OBS Studion lokitiedoston lähetys\"\nLogUploadDialog.Labels.PrivacyNotice=\"Lue <a href='https://obsproject.com/privacy-policy'>tietosuojakäytäntö</a> ja sen osio liittyen tiedostojen lähettämiseen, ennen kuin lähetät yhtäkään tiedostoa.\"\nLogUploadDialog.Labels.Progress=\"Lokin lähetys on meneillään, odota hetki...\"\nLogUploadDialog.Labels.Description.AppLog=\"Lokitiedostosi on ladattu. Nyt voit jakaa URL-osoitteen vianjäljitystä tai tukea varten.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Kaatumisraporttisi on lähetetty. Nyt voit jakaa URL-osoitteen vianjäljitystä tai tukea varten.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Lähetä\"\nLogUploadDialog.Buttons.CopyURL=\"Kopioi lokin URL-osoite\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analysoi lokitiedosto\"\nLogUploadDialog.Buttons.RetryButton=\"Yritä uudelleen\"\nLogUploadDialog.Errors.Template=\"Tiedostoa lähettäessä tapahtui virhe:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Lähetettävää tiedostoa ei löytynyt, tai tiedosto oli tyhjä.\"\nRemux.SourceFile=\"OBS-tallenne\"\nRemux.TargetFile=\"Kohdetiedosto\"\nRemux.Remux=\"Muunna\"\nRemux.Stop=\"Pysäytä muuntaminen\"\nRemux.ClearFinished=\"Poista valmistuneet kohteet\"\nRemux.ClearAll=\"Poista kaikki kohteet\"\nRemux.OBSRecording=\"OBS-tallenne\"\nRemux.FinishedTitle=\"Muunto on valmistunut\"\nRemux.Finished=\"Tallenne muunnettu\"\nRemux.FinishedError=\"Tallenne on muunnettu, mutta tiedosto saattaa olla keskeneräinen\"\nRemux.SelectRecording=\"Valitse OBS-tallenne...\"\nRemux.SelectTarget=\"Valitse kohdetiedosto...\"\nRemux.FileExistsTitle=\"Kohdetiedostot ovat jo olemassa\"\nRemux.FileExists=\"Seuraavat tiedostot ovat jo olemassa. Haluatko ylikirjoittaa ne?\"\nRemux.ExitUnfinishedTitle=\"Muunto on käynnissä\"\nRemux.ExitUnfinished=\"Muunto ei ole valmis. Jos keskeytät nyt, kohdetiedostosta saattaa tulla käyttökelvoton.\\nHaluatko varmasti pysäyttää muunnon?\"\nRemux.HelpText=\"Pudota tiedostoja tähän ikkunaan muuntaaksesi ne tai valitse tyhjä \\\"OBS-tallenne\\\" -sarake selataksesi tiedoston.\"\nRemux.NoFilesAddedTitle=\"Remuxing-tiedostoa ei ole lisätty\"\nRemux.NoFilesAdded=\"Remux ei sisällä tiedostoa. Pudota kansio, joka sisältää yhden tai useamman videon.\"\nMissingFiles=\"Tiedostoja puuttuu\"\nMissingFiles.MissingFile=\"Puuttuva tiedosto\"\nMissingFiles.NewFile=\"Uusi tiedosto\"\nMissingFiles.HelpText=\"Joitakin tiedostoja puuttuu sitten viime OBS-käyttökertasi.\"\nMissingFiles.Clear=\"<poistettu>\"\nMissingFiles.NumFound=\"Löydetty %1/%2\"\nMissingFiles.Search=\"Hae hakemistoa...\"\nMissingFiles.SelectFile=\"Valitse tiedosto...\"\nMissingFiles.SelectDir=\"Valitse kansio josta etsitään\"\nMissingFiles.State=\"Tila\"\nMissingFiles.Missing=\"Puuttuu\"\nMissingFiles.Replaced=\"Korvattu\"\nMissingFiles.Cleared=\"Poistettu\"\nMissingFiles.Found=\"Löydetty\"\nMissingFiles.AutoSearch=\"Lisäisiä tiedostovastaavuuksia löydetty\"\nMissingFiles.AutoSearchText=\"OBS on löytänyt lisäisiä vastaavuuksia puuttuville tiedostoille kyseisessä hakemistossa. Haluatko lisätä ne?\"\nMissingFiles.NoMissing.Title=\"Puuttuvien tiedostojen tarkistus\"\nMissingFiles.NoMissing.Text=\"Yksikään tiedosto ei vaikuta puuttuvan.\"\nMacPermissions.MenuAction=\"Tarkista sovelluksen käyttöoikeudet...\"\nMacPermissions.Title=\"Tarkista sovelluksen käyttöoikeudet\"\nMacPermissions.Description=\"OBS Studio vaatii luvat voidakseen tarjota tiettyjä ominaisuuksia. Suositeltavaa ottaa nämä käyttöoikeudet käyttöön, mutta niitä ei vaadita sovelluksen käyttämiseen. Voit aina ottaa ne myöhemmin käyttöön.\"\nMacPermissions.Description.OpenDialog=\"Voit avata tämän ikkunan uudelleen OBS Studio valikon kautta.\"\nMacPermissions.AccessGranted=\"Käyttöoikeus myönnetty\"\nMacPermissions.RequestAccess=\"Pyydä käyttöoikeutta\"\nMacPermissions.OpenPreferences=\"Avaa %1 asetukset\"\nMacPermissions.Item.ScreenRecording=\"Näytön tallennus\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS tarvitsee tämän luvan voidakseen kaapata näyttösi.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Tätä lupaa tarvitaan web-kameran tai kaappauskortin sisällön tallentamiseen.\"\nMacPermissions.Item.Microphone=\"Mikrofoni\"\nMacPermissions.Item.Microphone.Details=\"OBS tarvitsee tämän luvan, jos haluat kaapata ääntä mikrofonista tai ulkoiselta laitteelta.\"\nMacPermissions.Item.InputMonitoring=\"Syötteen valvonta\"\nMacPermissions.Item.InputMonitoring.Details=\"Tämä käyttöoikeus tarvitaan, jotta pikanäppäimet toimivat, kun OBS on taustalla.\"\nMacPermissions.Item.Accessibility=\"Esteettömyys\"\nMacPermissions.Item.Accessibility.Details=\"Vanhemmissa asennuksissa OBS saattaa löytyä kohdasta \\\"Esteettömyys\\\" sen sijaan, että se olisi kohdassa \\\"Syötteen valvonta\\\".\"\nMacPermissions.Continue=\"Jatka\"\nSourceLeak.Title=\"Puhdistusvirhe\"\nSourceLeak.Text=\"Kohtausta vaihtaessa tapahtui ongelma, eikä kaikkia lähteitä voitu poistaa. Tämä ongelma johtuu yleensä laajennuksista, jotka eivät vapauta resursseja oikein. Varmista, että kaikki käyttämäsi laajennukset ovat ajan tasalla.\\n\\nOBS Studio sulkeutuu nyt mahdollisen tietojen vioittumisen estämiseksi.\"\nBasic.DesktopDevice1=\"Äänentoistolaite\"\nBasic.DesktopDevice2=\"Äänentoistolaite 2\"\nBasic.Scene=\"Näkymä\"\nBasic.DisplayCapture=\"Kaappaa näyttö\"\nBasic.Main.PreviewConextMenu.Enable=\"Näytä esikatselu\"\nBasic.Main.Preview.Disable=\"Poista esikatselu käytöstä\"\nScaleFiltering=\"Asteikko suodatus\"\nScaleFiltering.Point=\"Piste\"\nScaleFiltering.Area=\"Alue\"\nBlendingMethod=\"Sekoitusmenetelmä\"\nBlendingMethod.Default=\"Oletus\"\nBlendingMethod.SrgbOff=\"SRGB pois\"\nBlendingMode=\"Sekoitustila\"\nBlendingMode.Normal=\"Normaali\"\nBlendingMode.Additive=\"Lisää\"\nBlendingMode.Subtract=\"Vähentävä\"\nBlendingMode.Screen=\"Näyttö\"\nBlendingMode.Multiply=\"Moninkertaistava\"\nBlendingMode.Lighten=\"Vaalentava\"\nBlendingMode.Darken=\"Tummentava\"\nDeinterlacing=\"Lomituksen poisto (Deinterlace)\"\nDeinterlacing.Discard=\"Ohita\"\nDeinterlacing.TopFieldFirst=\"Ylin kenttä ensin\"\nDeinterlacing.BottomFieldFirst=\"Alin kenttä ensin\"\nVolControl.SliderUnmuted=\"Äänenvoimakkuus '%1':\"\nVolControl.SliderMuted=\"Äänenvoimakkuus '%1': (mykistetty)\"\nVolControl.Mute=\"Mykistä %1\"\nVolControl.Properties=\"Ominaisuudet %1:lle\"\nVolControl.UnassignedWarning.Title=\"Määrittämätön äänilähde\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" ei ole määritetty millekään ääniraidalle, eikä se voi kuulua suoratoistossa tai tallenteissa.\\n\\nJos haluat määrittää äänilähteen raidalle, avaa valikosta \\\"Äänen lisäasetukset\\\" hiiren oikealla painikkeella tai hammaspörästä mikseritelakan työkalussa.\"\nBasic.Main.AddSceneDlg.Title=\"Lisää näkymä\"\nBasic.Main.AddSceneDlg.Text=\"Aseta näkymän nimi\"\nBasic.Main.DefaultSceneName.Text=\"Näkymä %1\"\nBasic.Main.AddSceneCollection.Title=\"Lisää skene-kokoelma\"\nBasic.Main.AddSceneCollection.Text=\"Syötä skene-kokoelman nimi\"\nBasic.Main.RenameSceneCollection.Title=\"Uudelleennimeä skene-kokoelma\"\nBasic.Main.RemigrateSceneCollection.Title=\"Päivitä näkymäkokoelman resoluutio\"\nBasic.Main.RemigrateSceneCollection.Text=\"Haluatko päivittää näkymäkokoelman \\\"%1\\\" resoluution vastaamaan tämänhetkisen profiilin \\\"%2x%3\\\" kanvasresoluution?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Näkymäkokoelman resoluutiota ei voi päivittää, kun ulostulot ovat aktiivisina.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Näkymäkokoelman resoluutiota ei voitu päivittää. Alkuperäinen resoluutio on tuntematon.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Palautus ei mahdollinen: OBS:n resoluution vaihtaminen epäonnistui.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Palautus ei mahdollinen: Nykyinen resoluutio vastaa jo näkymäkokoelman pohjaresoluutio­ta.\"\nAddProfile.Title=\"Lisää profiili\"\nAddProfile.Text=\"Syötä profiilin nimi\"\nAddProfile.WizardCheckbox=\"Näytä automaattinen määritystoiminto\"\nRenameProfile.Title=\"Uudelleennimeä profiili\"\nBasic.Main.MixerRename.Title=\"Uudelleennimeä äänilähde\"\nBasic.Main.MixerRename.Text=\"Syötä äänilaitteen nimi\"\nBasic.Main.PreviewDisabled=\"Esikatselu on poistettu käytöstä\"\nBasic.SourceSelect=\"Luo/Valitse lähde\"\nBasic.SourceSelect.CreateNew=\"Luo uusi\"\nBasic.SourceSelect.AddExisting=\"Lisää olemassaoleva\"\nBasic.SourceSelect.AddVisible=\"Tee lähteestä näkyvä\"\nBasic.Main.Sources.Visibility=\"Näkyvyys\"\nBasic.Main.Sources.VisibilityDescription=\"Hallitsee '%1' näkyvyyttä piirtoalueella\"\nBasic.Main.Sources.Lock=\"Lukitus\"\nBasic.Main.Sources.LockDescription=\"Lukitsee '%1' sijainnin ja koon piirtoalueella\"\nBasic.PropertiesWindow=\"Ominaisuudet: %1\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (automaat.: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Valitse väri\"\nBasic.PropertiesWindow.SelectFont=\"Valitse kirjasin\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Valitse fontti\"\nBasic.PropertiesWindow.ConfirmTitle=\"Asetukset ovat muuttuneet\"\nBasic.PropertiesWindow.Confirm=\"Muutoksia ei ole vielä tallennettu. Haluatko pitää muutokset?\"\nBasic.PropertiesWindow.NoProperties=\"Ominaisuuksia ei ole saatavilla\"\nBasic.PropertiesWindow.AddFiles=\"Lisää tiedostoja\"\nBasic.PropertiesWindow.AddDir=\"Lisää kansio\"\nBasic.PropertiesWindow.AddURL=\"Lisää polku/URL-osoite\"\nBasic.PropertiesWindow.AddEditableListDir=\"Lisää kansio \\\"%1\\\":n\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Lisää tiedostoja '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Lisää merkintä '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Muokkaa merkintää '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Yksinkertaiset FPS-arvot\"\nBasic.PropertiesView.FPS.Rational=\"Rationaaliset FPS-arvot\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Kelvolliset FPS-alueet:\"\nBasic.PropertiesView.UrlButton.Text=\"Avataanko tämä linkki oletusselaimessasi?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"URL-osoite: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Avaa URL-osoite\"\nBasic.InteractionWindow=\"Vuorovaikutetaan '%1' kanssa\"\nBasic.StatusBar.Reconnecting=\"Yhteys katkaistu, yhdistetään uudelleen %2 sekunnissa (yrityskerta %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Yhdistetään uudelleen... (yrityskerta %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Uudelleenyhdistys onnistui\"\nBasic.StatusBar.Delay=\"Viive (%1s)\"\nBasic.StatusBar.DelayStartingIn=\"Viive (alku %1 sekunnissa)\"\nBasic.StatusBar.DelayStoppingIn=\"Viive (loppuu %1 sekunnissa)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Viive (loppuu %1 ja alkaa %2 sekunnissa)\"\nBasic.StatusBar.RecordingSavedTo=\"Tallenne tallennettu sijaintiin '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Toistopuskuri tallennettu '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Kuvakaappaus tallennettu sijaintiin '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Nauhoitetaan automaattisesti muotoon '%1'\"\nBasic.Filters=\"Suodattimet\"\nBasic.Filters.AsyncFilters=\"Ääni/kuva -suodattimet\"\nBasic.Filters.AudioFilters=\"Äänisuodattimet\"\nBasic.Filters.EffectFilters=\"Efektisuodattimet\"\nBasic.Filters.Title=\"Suodattimet '%1':lle\"\nBasic.Filters.AddFilter.Title=\"Suodattimen nimi\"\nBasic.Filters.AddFilter.Text=\"Määritä suodattimelle nimi\"\nBasic.TransformWindow=\"Näkymän lähteen muuntaminen\"\nBasic.TransformWindow.Position=\"Sijainti\"\nBasic.TransformWindow.PositionX=\"Sijainti X\"\nBasic.TransformWindow.PositionY=\"Sijainti Y\"\nBasic.TransformWindow.Rotation=\"Kierto\"\nBasic.TransformWindow.Size=\"Koko\"\nBasic.TransformWindow.Width=\"Leveys\"\nBasic.TransformWindow.Height=\"Korkeus\"\nBasic.TransformWindow.Alignment=\"Sijaintiin kohdistus\"\nBasic.TransformWindow.BoundsType=\"Rajauslaatikon tyyppi\"\nBasic.TransformWindow.BoundsAlignment=\"Rajauslaatikon kohdistus\"\nBasic.TransformWindow.Bounds=\"Rajauslaatikon koko\"\nBasic.TransformWindow.BoundsWidth=\"Rajauslaatikon Leveys\"\nBasic.TransformWindow.BoundsHeight=\"Rajauslaatikon Korkeus\"\nBasic.TransformWindow.CropToBounds=\"Rajaa ponnahdusruutuun\"\nBasic.TransformWindow.Crop=\"Rajaa\"\nBasic.TransformWindow.CropLeft=\"Rajaa Vasemmalle\"\nBasic.TransformWindow.CropRight=\"Rajaa Oikealle\"\nBasic.TransformWindow.CropTop=\"Rajaa Ylhäällä\"\nBasic.TransformWindow.CropBottom=\"Rajaa alhaalta\"\nBasic.TransformWindow.Alignment.TopLeft=\"Ylävasen\"\nBasic.TransformWindow.Alignment.TopCenter=\"Yläkeski\"\nBasic.TransformWindow.Alignment.TopRight=\"Yläoikea\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Keskivasen\"\nBasic.TransformWindow.Alignment.Center=\"Keskellä\"\nBasic.TransformWindow.Alignment.CenterRight=\"Keskioikea\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Alavasen\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Alakeski\"\nBasic.TransformWindow.Alignment.BottomRight=\"Alaoikea\"\nBasic.TransformWindow.BoundsType.None=\"Ei rajoja\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Vain täysi koko\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skaalaa sisäisiin rajoihin\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skaalaa ulkoisiin rajoihin\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skaalaa pituuden rajoihin\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skaalaa korkeuden rajoihin\"\nBasic.TransformWindow.BoundsType.Stretch=\"Venytä rajoihin\"\nBasic.TransformWindow.Title=\"Muokkaa muunnosta '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Lähdettä ei ole valittu\"\nBasic.Main.AddSourceHelp.Title=\"Lähdettä ei voi lisätä\"\nBasic.Main.AddSourceHelp.Text=\"Tarvitset vähintään yhden näkymän lisätäksesi lähteen.\"\nBasic.Main.Scenes=\"Näkymät\"\nBasic.Main.Sources=\"Lähteet\"\nBasic.Main.Source=\"Lähde\"\nBasic.Main.Controls=\"Ohjaimet\"\nBasic.Main.PreparingStream=\"Valmistellaan...\"\nBasic.Main.Connecting=\"Yhdistetään...\"\nBasic.Main.StartRecording=\"Aloita tallennus\"\nBasic.Main.StartReplayBuffer=\"Käynnistä toistopuskuri\"\nBasic.Main.SaveReplay=\"Tallenna uusinta\"\nBasic.Main.StartStreaming=\"Aloita lähetys\"\nBasic.Main.StartBroadcast=\"Aloita suora lähetys\"\nBasic.Main.StartVirtualCam=\"Käynnistä virtuaalinen kamera\"\nBasic.Main.StopRecording=\"Pysäytä tallennus\"\nBasic.Main.PauseRecording=\"Keskeytä tallennus\"\nBasic.Main.UnpauseRecording=\"Jatka tallennusta\"\nBasic.Main.SplitFile=\"Pilko tallennettu tiedosto\"\nBasic.Main.AddChapterMarker=\"Lisää kappalemerkki (vain hybridi MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Pysäytetään tallennusta...\"\nBasic.Main.StopReplayBuffer=\"Pysäytä toistopuskuri\"\nBasic.Main.StoppingReplayBuffer=\"Pysäytetään toistopuskuria...\"\nBasic.Main.SetupBroadcast=\"Hallitse lähetystä\"\nBasic.Main.StopStreaming=\"Pysäytä lähetys\"\nBasic.Main.StopBroadcast=\"Lopeta lähetys\"\nBasic.Main.AutoStopEnabled=\"(Automaattinen lopetus)\"\nBasic.Main.StoppingStreaming=\"Pysäytetään lähetystä...\"\nBasic.Main.ForceStopStreaming=\"Pysäytä lähetys (ohita viive)\"\nBasic.Main.ShowContextBar=\"Näytä Lähdetyökalurivi\"\nBasic.Main.HideContextBar=\"Piilota Lähdetyökalurivi\"\nBasic.Main.StopVirtualCam=\"Pysäytä virtuaalinen kamera\"\nBasic.Main.Group=\"Ryhmitä %1\"\nBasic.Main.GroupItems=\"Ryhmitä valitut lähteet\"\nBasic.Main.Ungroup=\"Poista ryhmästä\"\nBasic.Main.GridMode=\"Ruudukkotila\"\nBasic.Main.ListMode=\"Luettelotila\"\nBasic.Main.VirtualCamConfig=\"Määritä virtuaalikamera\"\nBasic.VCam.VirtualCamera=\"Virtuaalikamera\"\nBasic.VCam.OutputType=\"Ulostulon tyyppi\"\nBasic.VCam.OutputSelection=\"Ulostulon valinta\"\nBasic.VCam.OutputType.Program=\"Ohjelma (oletus)\"\nBasic.VCam.OutputSelection.NoSelection=\"Ei valintaa tälle ulostulotyypille\"\nBasic.VCam.RestartWarning=\"Virtuaalikamera käynnistetään uudelleen, jotta tämä muutos tulee voimaan\"\nBasic.MainMenu.File=\"&Tiedosto\"\nBasic.MainMenu.File.Export=\"Vi&e\"\nBasic.MainMenu.File.Import=\"T&uo\"\nBasic.MainMenu.File.ShowRecordings=\"N&äytä tallenteet\"\nBasic.MainMenu.File.Remux=\"&Muunna tallenteita\"\nBasic.MainMenu.File.Settings=\"A&setukset\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Avaa asetuskansio\"\nBasic.MainMenu.File.ShowProfileFolder=\"Avaa profiilikansio\"\nBasic.MainMenu.File.ShowMissingFiles=\"Tarkista puuttuvat tiedostot\"\nBasic.MainMenu.File.Exit=\"&Sulje\"\nBasic.MainMenu.Edit=\"&Muokkaa\"\nBasic.MainMenu.Edit.Undo=\"K&umoa\"\nBasic.MainMenu.Edit.Redo=\"Tee uudelleen (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"&Lukitse esikatselu\"\nBasic.MainMenu.Edit.Scale=\"Esikatselun &skaalaus\"\nBasic.MainMenu.Edit.Scale.Window=\"Skaalaa ikkunaan\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Kanvaasi (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Ulostulo (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Skaalattu (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Lähennä\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Loitonna\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Palauta lähennys\"\nBasic.MainMenu.Edit.Transform=\"Muunn&a\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Muokkaa muunnosta... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopioi muunnos\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Liitä muunnos\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Nollaa muunnos (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Kierrä 90 astetta myötäpäivään\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Kierrä 90 astetta vastapäivään\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Kierrä 180 astetta\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Käännä vaakatasossa (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Käännä pystytasossa (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Sovita ikkunaan (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Venytä ikkunaan (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Keskitä ikkunaan (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Keskitä pystysuunnassa\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Keskitä vaakasuunnassa\"\nBasic.MainMenu.Edit.Order=\"&Järjestä\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Siirrä ylös (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Siirrä alas (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Siirrä ylimmäksi (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Siirrä alimmaksi (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Äänen lisäomin&aisuudet\"\nBasic.MainMenu.View=\"&Näytä\"\nBasic.MainMenu.View.Toolbars=\"&Työkalurivit\"\nBasic.MainMenu.View.ListboxToolbars=\"Telakoi työkalupalkit\"\nBasic.MainMenu.View.ContextBar=\"Lähdetyökalurivi\"\nBasic.MainMenu.View.SourceIcons=\"Lähte&iden kuvakkeet\"\nBasic.MainMenu.View.StatusBar=\"Ti&lapalkki\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Koko näytön käyttöliittymä\"\nBasic.MainMenu.View.ResetUI=\"&Palauta käyttöliittymä\"\nBasic.MainMenu.View.AlwaysOnTop=\"Aina &päällimmäisenä\"\nBasic.MainMenu.View.SceneListMode=\"Kohtaus luettelo\"\nBasic.MainMenu.Docks=\"&Telakat\"\nBasic.MainMenu.Docks.ResetDocks=\"&Palauta telakat\"\nBasic.MainMenu.Docks.LockDocks=\"&Lukitse telakat\"\nBasic.MainMenu.Docks.SideDocks=\"&Täyden korkeuden telakat\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Mukautetut selaintelakat...\"\nBasic.MainMenu.Import=\"Tuo...\"\nBasic.MainMenu.Export=\"Vie...\"\nBasic.MainMenu.SceneCollection=\"&Näkymäkokoelma\"\nBasic.MainMenu.Profile=\"&Profiili\"\nBasic.MainMenu.Profile.Import=\"Tuo profiili\"\nBasic.MainMenu.Profile.Export=\"Vie profiili\"\nBasic.MainMenu.SceneCollection.Import=\"Tuo näkymäkokoelma\"\nBasic.MainMenu.SceneCollection.Export=\"Vie näkymäkokoelma\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Nollaa pohjaresoluutio\"\nBasic.MainMenu.SceneCollection.Migrate=\"Aseta pohjaresoluutio\"\nBasic.MainMenu.Profile.Exists=\"Profiili on jo olemassa\"\nBasic.MainMenu.Tools=\"&Työkalut\"\nBasic.MainMenu.Help=\"&Ohje\"\nBasic.MainMenu.Help.HelpPortal=\"O&hjekeskus\"\nBasic.MainMenu.Help.Website=\"Käy &verkkosivulla\"\nBasic.MainMenu.Help.Discord=\"Liity &Discord-palvelimelle\"\nBasic.MainMenu.Help.WhatsNew=\"Uutta\"\nBasic.MainMenu.Help.Logs=\"&Lokitiedostot\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Näytä lokitiedo&stot\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Lähetä &nykyinen lokitiedosto\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Lähetä &edellinen lokitiedosto\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Näytä nykyinen &loki\"\nBasic.MainMenu.Help.ReleaseNotes=\"Julkaisutiedot\"\nBasic.MainMenu.Help.CheckForUpdates=\"Tarkista päivitykset\"\nBasic.MainMenu.Help.Repair=\"Tarkista tiedoston eheys\"\nBasic.MainMenu.Help.RestartSafeMode=\"Käynnistä uudelleen vikasietotilassa\"\nBasic.MainMenu.Help.RestartNormal=\"Käynnistä uudelleen normaalissa tilassa\"\nBasic.MainMenu.Help.CrashLogs=\"Kaatumis&raportit\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Näytä kaatumi&sraportit\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Lähetä &edellinen kaatumisraportti\"\nBasic.MainMenu.Help.About=\"Tietoj&a\"\nBasic.Settings.ProgramRestart=\"Ohjelma on käynnistettävä uudelleen, jotta asetukset tulevat voimaan.\"\nBasic.Settings.ConfirmTitle=\"Vahvista muutokset\"\nBasic.Settings.Confirm=\"Sinulla on tallentamattomia muutoksia. Tallennetaanko?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 hallitsee joitain suoratoistoasetuksia\"\nBasic.Settings.General=\"Yleiset\"\nBasic.Settings.General.Language=\"Kieli\"\nBasic.Settings.General.Updater=\"Päivitykset\"\nBasic.Settings.General.UpdateChannel=\"Päivityskanava\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Pois käytöstä)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Oletus)\"\nBasic.Settings.General.EnableAutoUpdates=\"Tarkista päivitykset automaattisesti käynnistäessä\"\nBasic.Settings.General.OpenStatsOnStartup=\"Avaa tilastoikkuna käynnistyksessä\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Piilota OBS-ikkunat ruudunkaappauksesta\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Tämä asetus piilottaa kaikki muut kuin OBS Studio -ikkunat OBS:n kaappaamiselta ja vaikuttaa \\nmuihin sovelluksiin, kuten konferenssi, näytön jakaminen, etätukeen, ruudunkaappauksiin ja muihin kaappaus-ohjelmiin.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Tämä asetus piilottaa kaikki muut kuin OBS Studio -ikkunat OBS:n kaappaamiselta ja vaikuttaa muihin sovelluksiin, kuten konferenssi, näytön jakaminen, etätukeen, ruudunkaappauksiin ja muihin kaappaus-ohjelmiin.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Näytä varmistusikkuna kun lähetys aloitetaan\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Näytä varmistusikkuna kun lähetys pysäytetään\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Näytä varmistusikkuna kun tallennus pysäytetään\"\nBasic.Settings.General.Projectors=\"Peilaukset\"\nBasic.Settings.General.HideProjectorCursor=\"Piilota osoitin peilattaessa\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Pidä peilatut esikatselut aina päällimmäisenä\"\nBasic.Settings.General.Snapping=\"Lähteiden kiinnitys\"\nBasic.Settings.General.ScreenSnapping=\"Kiinnitä lähteitä ruudun reunaan\"\nBasic.Settings.General.CenterSnapping=\"Kiinnitä lähteitä vaaka- sekä pystysuunnan keskilinjaan\"\nBasic.Settings.General.SourceSnapping=\"Kiinnitä lähteitä muihin lähteisiin\"\nBasic.Settings.General.SnapDistance=\"Kiinnityksen herkkyys\"\nBasic.Settings.General.SpacingHelpers=\"Näytä pikselien kohdistuksen apuviivat\"\nBasic.Settings.General.RecordWhenStreaming=\"Tallenna automaattisesti kun lähetetään\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Jatka tallennusta lähetyksen loputtua\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Käynnistä toistopuskuri automaattisesti lähetettäessä\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Pidä toistopuskuri käytössä kun lähetys loppuu\"\nBasic.Settings.General.SysTray=\"Ilmaisinalue\"\nBasic.Settings.General.SysTrayWhenStarted=\"Pienennä ilmaisinalueelle käynnistyessä\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Pienennä aina tilapalkkiin tehtäväpalkin sijaan\"\nBasic.Settings.General.SaveProjectors=\"Tallenna peilaus poistuessa\"\nBasic.Settings.General.CloseExistingProjectors=\"Rajoita yksi koko näytön projektori näyttöä kohden\"\nBasic.Settings.General.Preview=\"Esikatselu\"\nBasic.Settings.General.OverflowHidden=\"Piilota pois jäävät osat\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Näytä pois jäävät osat aina\"\nBasic.Settings.General.OverflowSelectionHidden=\"Näytä pois jäävät osat myös silloin kun lähde on näkymätön\"\nBasic.Settings.General.Importers=\"Tuonti\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Etsi tunnetuista sijainneista näkymäkokoelmia tuodessa\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Siirtymä näkymään kaksoisnapsauttamalla\"\nBasic.Settings.General.StudioPortraitLayout=\"Ota pystyasettelu käyttöön\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Näytä esikatselun/ohjelman nimet\"\nBasic.Settings.General.Multiview=\"Moninäkymä\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Napsauta vaihtaaksesi näkymien välillä\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Näytä näkymien nimet\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Piirrä turva-alueet (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Moninäkymän asettelu\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Vaakasuunta, ylhäällä (8 näkymää)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Vaakasuunta, alhaalla (8 näkymää)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Pystysuunta, vasemmalla (8 näkymää)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Pystysuunta, oikealla (8 näkymää)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Vaakasuunta, ylhäällä (18 skeneä)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Vaakasuunta, ylhäällä (24 näkymää)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Vain kohtaukset (4 kohtausta)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Vain kohtaukset (9 kohtausta)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Vain kohtaukset (16 kohtausta)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Vain kohtaukset (25 kohtausta)\"\nBasic.Settings.General.ChannelName.stable=\"Vakaa\"\nBasic.Settings.General.ChannelDescription.stable=\"Viimeisin vakaa julkaisu\"\nBasic.Settings.General.ChannelName.beta=\"Beetat / julkaisuehdokkaat\"\nBasic.Settings.General.ChannelDescription.beta=\"Mahdollisesti epävakaat esijulkaisuversiot\"\nBasic.Settings.Appearance=\"Ulkoasu\"\nBasic.Settings.Appearance.General=\"Yleinen\"\nBasic.Settings.Appearance.General.Theme=\"Teema\"\nBasic.Settings.Appearance.General.Variant=\"Tyyli\"\nBasic.Settings.Appearance.General.NoVariant=\"Tyyliä ei ole saatavilla\"\nBasic.Settings.Appearance.FontScale=\"Fontin koko\"\nBasic.Settings.Appearance.Density=\"Tiheys\"\nBasic.Settings.Appearance.Classic=\"Perinteinen\"\nBasic.Settings.Appearance.Compact=\"Tiivis\"\nBasic.Settings.Appearance.Normal=\"Normaali\"\nBasic.Settings.Appearance.Comfortable=\"Mukava\"\nBasic.Settings.Appearance.OptionsWarning=\"Jotkin ulkoasun asetukset eivät ole käytettäväissä tässä tyylissä.\"\nBasic.Settings.Stream=\"Lähetys\"\nBasic.Settings.Stream.Destination=\"Kohde\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Käytä todennusta\"\nBasic.Settings.Stream.Custom.Username=\"Käyttäjätunnus\"\nBasic.Settings.Stream.Custom.Password=\"Salasana\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: kirjoita srp_username.\\nRTMP: kirjoita käyttäjänimi.\\nSRT: ei käytetä.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: kirjoita srp_password.\\nRTMP: kirjoita salasana.\\nSRT: kirjoita salaussalasana.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Ota kaistan nopeustestitila käyttöön\"\nBasic.Settings.Stream.TTVAddon=\"Twitch chatin lisäosat\"\nBasic.Settings.Stream.TTVAddon.None=\"Ei mitään\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV ja FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Puuttuva lähetysasetus\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Avaa asetukset\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL-osoite ja Striimiavain puuttuu.\\n\\nAvaa Asetukset syöttääksesi URL-osoitteen ja Striimiavaimen 'Lähetys' -osiossa.\"\nBasic.Settings.Stream.MissingUrl=\"URL-osoite puuttuu.\\n\\nAvaa Asetukset syöttääksesi URL-osoitteen 'Lähetys' -osiossa.\"\nBasic.Settings.Stream.MissingStreamKey=\"Lähetysavain puuttuu.\\n\\nAvaa Asetukset syöttääksesi URL-osoitteen 'Lähetys' -osiossa.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Älä huomioi palvelun asetusten suosituksia\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Ohita suositellut asetukset\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Varoitus: Rajoitusten ohittaminen saattaa huonontaa lähetyksen laatua tai estää sinua lähettämästä.\\n\\nJatka?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Kuvan maksimibittinopeus: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Äänen maksimibittinopeus: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksimiresoluutio: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksimi-FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Määritä omavalintainen palvelin...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Omavalintainen palvelin\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Käytä %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maksimi suoratoiston kaistanleveys\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Autom.\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maksimi videoraidat\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Autom.\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Stream dump käyttöön FLV-tiedostoon (käyttää yksinkertaisia tallennuksen asetuksia)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Määrityksen ohitus (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Käytä määrityksen ohitusta\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Moniraitainen video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Lisäpiirtoalue\"\nBasic.Settings.Stream.AdvancedOptions=\"Lisäasetukset\"\nBasic.Settings.Output=\"Ulostulo\"\nBasic.Settings.Output.Format=\"Tallennusmuoto\"\nBasic.Settings.Output.Format.fMP4=\"Hajanainen MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Hajanainen MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"HajanainenMOV kirjoittaa tallennuksen lohkoilla ja ei vaadi samaa viimeistelyä kuin perinteinen MOV-tiedosto.\\nTämä varmistaa, että tiedosto pysyy toistettavana, vaikka kirjoittaminen levylle keskeytyisi, mikä aiheutuu esimerkiksi sähkökatkon vuoksi.\\n\\nTämä ei välttämättä ole yhteensopiva kaikkien ohjelmien ja editorien kanssa. Käytä Tiedosto → Muunna tallenteita muuntaaksesi tiedoston yhteensopivaan muotoon tarvittaessa.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Hajanainen MP4 kirjoittaa tallennuksen lohkoilla ja ei vaadi samaa viimeistelyä kuin perinteinen MP4-tiedosto.\\nTämä varmistaa, että tiedosto pysyy toistettavana, vaikka kirjoittaminen levylle keskeytyisi, mikä aiheutuu esimerkiksi sähkökatkon vuoksi.\\n\\nTämä ei välttämättä ole yhteensopiva kaikkien ohjelmien ja editorien kanssa. Käytä Tiedosto → Muunna tallenteita muuntaaksesi tiedoston yhteensopivaan muotoon tarvittaessa.\"\nBasic.Settings.Output.Encoder.Video=\"Videokooderi\"\nBasic.Settings.Output.Encoder.Audio=\"Äänikooderi\"\nBasic.Settings.Output.SelectDirectory=\"Valitse tallennuskansio\"\nBasic.Settings.Output.DynamicBitrate=\"Muuta bittinopeutta verkon ruuhkautumisen mukaan\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Muuta bittinopeutta verkon ruuhkautumisen mukaan (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Kuvaruutujen pudottamisen sijaan vähentää bittinopeutta automaattisesti, kun verkko ruuhkautuu.\\n\\nHuomaathan, että tämä saattaa lisätä viivettä katsojille, mikäli verkko ruuhkautuu merkittävästi.\\nKun bittinopeus tippuu, sen nousemisessa saattaa kestää muutamia minuutteja.\\n\\nTällä hetkellä tuettu vain RTMP:ssä.\"\nBasic.Settings.Output.Mode=\"Ulostulon tila\"\nBasic.Settings.Output.Mode.Simple=\"Yksinkertainen\"\nBasic.Settings.Output.Mode.Adv=\"Kehittynyt\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg-ulostulo\"\nBasic.Settings.Output.UseReplayBuffer=\"Ota toistopuskuri käyttöön\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Toistopuskurin maksimikesto\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maksimi muisti\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Arvioitu muistinkäyttö: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Varoitus: Arvioitu muistin käyttö %1 MiB on suurempi kuin suositeltu maksimi %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Muistin käyttöä ei voida arvioida. Valitse muistiraja.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Toistopuskurin tiedostonimen etuliite\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Pääte\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Toistopuskuria ei voi käyttää, kun tallennustyypiksi on asetettu Custom Output (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Tallennuksen polku\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Tallennuksen laatu\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Sama kuin lähetyksessä\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Korkea laatu, keskikokoinen tiedostokoko\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Erottamaton laatu, suuri tiedostokoko\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Häviötön laatu, järjettömän suuri tiedostokoko\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Varoitus: Kuvan bittinopeus asetetaan olemaan %1, joka on valitun palvelun yläraja.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Varoitus: Äänen bittinopeus asetetaan %1, joka on valitun palvelun yläraja.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Varoitus: Tallennusta ei voi pysäyttää jos laatu on asetettu olemaan \\\"Sama kuin lähetyksessä\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Varoitus: Tällä hetkellä valittu tallennuksen formaatti ei ole yhteensopiva valittun suoratoiston pakkaajan kanssa.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Varoitus: Tallentaminen lähetyksestä eroavalla laadulla vaatii prosessorilta lisätyötä jos lähetät ja tallennat samanaikaisesti.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Varoitus: Häviötön laatu luo järjettömän kokoisia tiedostoja! Häviötön laatu voi käyttää jopa 7 gigatavua levytilaa minuutissa, mikäli käytät suurta resoluutiota ja korkeaa ruutunopeutta. Häviötöntä ei suositella pitkille tallennuksille, ellei sinulla ole erittäin paljon levytilaa. Toistopuskuri ei ole käytettävissä häviötöntä laatua käytettäessä.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Haluatko varmasti käyttää häviötöntä laatua?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Häviötön laatu!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Ohjelmistopohjainen (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Laitteisto (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Laitteisto (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Laitteisto (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Laitteisto (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Laitteisto (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Laitteisto (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Laitteisto (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Laitteisto (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Laitteisto (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Laitteisto (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Ohjelmistopohjainen (x264 matala CPU-käyttö, lisää tiedostokokoa)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Oletus)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD ääniraita (Käyttää raitaa 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Ääniraita\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Resoluutio/ruutunopeus ei ole yhteensopiva\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Tämä palvelu ei tue nykyistä ulostulon resoluutiota ja/tai ruutunopeutta. Nämä asetukset muutetaan lähimpään yhteensopivaan arvoon:\\n\\n%1\\n\\nHaluatko jatkaa?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resoluutio: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Yhteensopimaton enkooderi\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Suoratoistopalvelu \\\"%1\\\" ei tue kooderia \\\"%2\\\". Kooderiksi vaihdetaan \\\"%3\\\".\\n\\nHaluatko jatkaa?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Suoratoistopalvelu \\\"%1\\\" ei tue koodereita \\\"%2\\\" ja \\\"%3\\\". Nämä pakkauskooderit vaihdetaan \\\"%4\\\" ja \\\"%5\\\".\\n\\nHaluatko jatkaa?\"\nBasic.Settings.Output.VideoBitrate=\"Kuvan bittinopeus\"\nBasic.Settings.Output.AudioBitrate=\"Äänen bittinopeus\"\nBasic.Settings.Output.Reconnect=\"Automaattinen uudelleenyhdistys\"\nBasic.Settings.Output.RetryDelay=\"Uudelleenyritysviive\"\nBasic.Settings.Output.MaxRetries=\"Uudelleenyhdistyksien määrä\"\nBasic.Settings.Output.Advanced=\"Käytä mukautettuja enkooderiasetuksia (edistynyt)\"\nBasic.Settings.Output.EncoderPreset=\"Enkooderin esiasetus\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (alhainen suorittimen käyttö, matalin laatu)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (oletus) (keskitason suorittimen käyttö, tavallinen laatu)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (korkea suorittimen käyttö, korkea laatu)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Mukautetut enkooderin asetukset\"\nBasic.Settings.Output.CustomMuxerSettings=\"Mukautetut muuntajan asetukset\"\nBasic.Settings.Output.NoSpaceFileName=\"Luo tiedostonimi ilman välilyöntejä\"\nBasic.Settings.Output.Adv.Rescale=\"Uudelleenskaalaa ulostulo\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Pois käytöstä\"\nBasic.Settings.Output.Adv.AudioTrack=\"Ääniraita\"\nBasic.Settings.Output.Adv.Streaming=\"Lähetys\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Suoratoiston asetukset\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Raita 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Raita 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Raita 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Raita 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Raita 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Raita 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD ääniraita\"\nBasic.Settings.Output.Adv.Encoder=\"Enkooderin asetukset\"\nBasic.Settings.Output.Adv.Recording=\"Tallennus\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Tallennuksen asetukset\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tallennuksen tyyppi\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tyyppi\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Tavallinen\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Mukautettu lähtö (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Käytä lähetysenkooderia)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Tiedostonimen muoto\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Korvaa olemassa oleva\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Mukautettu ulostulo (FFmpeg) ei sisällä turvamekanismeja.\\nHarkitse \\\"Tavallinen\\\"-tallennustyypin käyttöä.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg ulostulon tyyppi\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Ulostulo osoitteeseen\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Ulostulo tiedostoon\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Yleiset tallennusmuodot\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Kaikki tiedostot\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Tiedostopolku tai URL-osoite\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Säiliömuoto\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Ääni\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Kuva\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Oletusmuoto\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Säiliömuodon kuvaus\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Ääni/Kuva koodekki päätellään tiedostopäätteen tai osoitteen perusteella\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Oletus\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Poista enkooderi käytöstä\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Videoenkooderi\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Videoenkooderin asetukset\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Äänienkooderi\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Äänienkooderin asetukset\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muunnon asetukset\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Avainruutujen väli (ruutuina)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Näytä kaikki koodekit (myös mahdollisesti yhteensopimattomat)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg-asetukset\"\nBasic.Settings.Output.EnableSplitFile=\"Automaattinen tiedoston monistaminen\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Monista ajan mukaan\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Monista koon mukaan\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Pilkotaan vain manuaalisesti\"\nBasic.Settings.Output.SplitFile.Time=\"Monista aika\"\nBasic.Settings.Output.SplitFile.Size=\"Monista koko\"\nScreenshot=\"Näyttökuva ulostulosta\"\nScreenshot.SourceHotkey=\"Näyttökuva valitusta lähteestä\"\nScreenshot.StudioProgram=\"Näyttökuva (Ohjelma)\"\nScreenshot.Preview=\"Näyttökuva (Esikatselu)\"\nScreenshot.Scene=\"Näyttökuva (näkymä)\"\nScreenshot.Source=\"Näyttökuva (Lähde)\"\nFilenameFormatting.TT.CCYY=\"Vuosi, neljä numeroa\"\nFilenameFormatting.TT.YY=\"Vuosi, kaksi viimeistä numeroa (00-99)\"\nFilenameFormatting.TT.MM=\"Kuukausi desimaalilukuna (01-12)\"\nFilenameFormatting.TT.DD=\"Kuukauden päivä, muodossa (01-31)\"\nFilenameFormatting.TT.hh=\"Tunti 24 tunnin muodossa (00-23)\"\nFilenameFormatting.TT.mm=\"Minuutti (00-59)\"\nFilenameFormatting.TT.ss=\"Sekunti (00-59)\"\nFilenameFormatting.TT.Percent=\"Merkki %\"\nFilenameFormatting.TT.a=\"Lyhennetty viikonpäivän nimi\"\nFilenameFormatting.TT.A=\"Viikonpäivän koko nimi\"\nFilenameFormatting.TT.b=\"Lyhennetty kuukauden nimi\"\nFilenameFormatting.TT.B=\"Kuukauden koko nimi\"\nFilenameFormatting.TT.d=\"Kuukauden päivä, muodossa (01-31)\"\nFilenameFormatting.TT.H=\"Tunti 24 tunnin muodossa (00-23)\"\nFilenameFormatting.TT.I=\"Tunti 12 tunnin muodossa (01-12)\"\nFilenameFormatting.TT.m=\"Kuukausi desimaalilukuna (01-12)\"\nFilenameFormatting.TT.M=\"Minuutti (00-59)\"\nFilenameFormatting.TT.p=\"Merkintä AM tai PM\"\nFilenameFormatting.TT.s=\"Aika sekunteina UNIX-kaudesta\"\nFilenameFormatting.TT.S=\"Sekunti (00-59)\"\nFilenameFormatting.TT.y=\"Vuosi, kaksi viimeistä numeroa (00-99)\"\nFilenameFormatting.TT.Y=\"Vuosi\"\nFilenameFormatting.TT.z=\"ISO 8601 -siirtymä UTC:stä aikavyöhykkeenä\"\nFilenameFormatting.TT.Z=\"Aikavyöhykkeen nimi tai lyhenne\"\nFilenameFormatting.TT.FPS=\"Kuvaa sekunnissa\"\nFilenameFormatting.TT.CRES=\"Piirtoalueen (kanvaasin) resoluutio\"\nFilenameFormatting.TT.ORES=\"Ulostulon (skaalattu) resoluutio\"\nFilenameFormatting.TT.VF=\"Videon muoto\"\nBasic.Settings.Video=\"Kuva\"\nBasic.Settings.Video.Adapter=\"Näytönohjain\"\nBasic.Settings.Video.BaseResolution=\"Piirtoalueen resoluutio\"\nBasic.Settings.Video.ScaledResolution=\"Ulostulon (skaalattu) resoluutio\"\nBasic.Settings.Video.DownscaleFilter=\"Skaalaussuodatin\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Resoluutiot vastaavat, skaalausta ei vaadita]\"\nBasic.Settings.Video.FPSCommon=\"Yleiset FPS-arvot\"\nBasic.Settings.Video.FPSInteger=\"Kokonaislukuinen FPS-arvo\"\nBasic.Settings.Video.FPSFraction=\"Murtoluvullinen FPS-arvo\"\nBasic.Settings.Video.Numerator=\"Osoittaja\"\nBasic.Settings.Video.Denominator=\"Nimittäjä\"\nBasic.Settings.Video.Renderer=\"Hahmontaja\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Kokeellinen)\"\nBasic.Settings.Video.InvalidResolution=\"Vialliset resoluution asetus. Muodon pitää olla [leveys]x[korkeus] (esim. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Kuvan ulostulo on tällä hetkellä käytössä. Kytke pois kaikki ulostulot vaihtaaksesi kuva-asetuksia.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (nopein, mutta epätarkka skaalattaessa)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (Terävöity skaalaus, 16 näytettä)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Terävöity skaalaus, 36 näytettä)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Alue (Painotettu summa, 4/6/9 näytettä)\"\nBasic.Settings.Audio=\"Ääni\"\nBasic.Settings.Audio.SampleRate=\"Näytteenottotaajuus\"\nBasic.Settings.Audio.Channels=\"Kanavat\"\nBasic.Settings.Audio.Meters=\"Mittarit\"\nBasic.Settings.Audio.MeterDecayRate=\"Madaltumisnopeus\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Nopea\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Keskinopea (Tyyppi I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Hidas (Tyyppi II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Huippumittarin tyyppi\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Huippuarvon näyte\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Todellinen huippuarvo (Korkeampi CPU:n käyttö)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"VAROITUS: Monikanavaääni on käytössä.\"\nBasic.Settings.Audio.MultichannelWarning=\"Varmista striimatessa että palvelu tukee sekä monikanavaisen äänen lähettämistä, että toistamista. Facebook 360 Live on esimerkiksi palvelu jossa tämä on täysin tuettu. Vaikka Facebook Live ja YouTube Live hyväksyvät monikanavaisen äänen lähettämisen, Facebook Live miksaa äänen stereoksi ja YouTube Live toistaa vain kaksi kanavaa.\\n\\nOBS suodattimet tukevat monikanavaista ääntä, mutta VST-liitännäiset eivät välttämättä sitä tue.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Käytä monikanava-ääntä?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Haluatko varmasti käyttää monikanavaista ääntä?\"\nBasic.Settings.Audio.Devices=\"Yleiset äänilaitteet\"\nBasic.Settings.Audio.DesktopDevice=\"Äänentoistolaite\"\nBasic.Settings.Audio.DesktopDevice2=\"Äänentoistolaite 2\"\nBasic.Settings.Audio.AuxDevice=\"Mic/Aux -äänilaite\"\nBasic.Settings.Audio.AuxDevice2=\"Mic/Aux -äänilaite 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mic/Aux -äänilaite 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mic/Aux -äänilaite 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Ota mykistyspainike käyttöön\"\nBasic.Settings.Audio.PushToMuteDelay=\"Mykistyspainikkeen viive\"\nBasic.Settings.Audio.EnablePushToTalk=\"Ota puhepainike käyttöön\"\nBasic.Settings.Audio.PushToTalkDelay=\"Puhepainikkeen viive\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Laitetta ei ole yhdistetty tai se ei ole saatavissa]\"\nBasic.Settings.Audio.Disabled=\"Pois käytöstä\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Vähäviiveinen äänenpuskurointitila (Decklink:ille/NDI-ulostuloille)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"VAROITUS: vähäviiveinen äänipuskurointi on kytketty päälle.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Vähäviiveinen äänipuskurointitila saattaa aiheuttaa ääneen virheellisyyksiä tai toisto voi katketa joidenkin lähteiden kohdalla.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Kytketäänkö vähäviiveinen äänipuskurointitila päälle?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Oletko varma että haluat kytkeä päälle vähäviiveisen äänipuskuroinnin tilan?\"\nBasic.Settings.Accessibility=\"Esteettömyys\"\nBasic.Settings.Accessibility.ColorOverrides=\"Käytä eri värejä\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Lähteen reuna (valinta)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Lähteen reuna (rajaus)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Lähteen reuna (osoittamalla)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mikserin voimakkuusalue (-60 - -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mikserin voimakkuusalue (-20 - -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mikserin voimakkuusalue (-9 - -0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mikserin voimakkuusalue (-60 - -20dB) (aktiivinen)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mikserin voimakkuusalue (-20 - -9dB) (aktiivinen)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mikserin voimakkuusalue (-9 - -0dB) (aktiivinen)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Värin esiasetus\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Oletus\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Mukautettu\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Vaihtoehto värisokeille\"\nBasic.Settings.Advanced=\"Lisäasetukset\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Näytä varoitus toiminnassaolevista lähdöistä poistuessa\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prosessin prioriteetti\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Suuri\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Suurempi kuin normaali\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normaali\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Alle normaalin\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Pieni\"\nBasic.Settings.Advanced.FormatWarning=\"Varoitus: Muut värimuodot kuin NV12/P010 on ensisijaisesti tarkoitettu tallentamiseen, eikä niitä suositella suoratoiston aikana. Suoratoisto voi aiheuttaa lisääntynyttä suorittimen käyttöä värimuodon muuntamisen vuoksi.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Varoitus: Korkean tarkkuuden formaatteja käytetään yleisemmin HDR-väritiloissa.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Varoitus: Rec. 2100:n tulisi käyttää muotoa, joka on tarkempi.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Värimuoto\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2-tasoa)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3-tasoa)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3-tasoa)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2-tasoa)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3-tasoa)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2-tasoa)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2-tasoa)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Väriavaruus\"\nBasic.Settings.Advanced.Video.ColorRange=\"Värialue\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Rajoitettu\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Täysi\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR:n valkoisen taso\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR:n nimellinen hupputaso\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Tarkkailulaite\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Oletusarvo\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Poista Windowsin äänien hiljennys käytöstä (Audio Ducking)\"\nBasic.Settings.Advanced.StreamDelay=\"Lähetyksen viive\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Kesto\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Säilytä katkaisupiste (lisää viivettä) uudelleenyhdistettäessä\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Arvioitu muistinkäyttö: %1 MB\"\nBasic.Settings.Advanced.Network=\"Verkko\"\nBasic.Settings.Advanced.Network.Disabled=\"Valittu suoratoistoprotokolla ei tue verkkoasetusten vaihtamista.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Liitä IP:seen\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP-perhe\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Käytä verkon optimointeja\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Käytä TCP:n tahdistusta\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Yrittää tehdä RTMP:n ulostulosta ystävällisemmän muita verkon viiveherkkiä sovelluksia varten säätelemällä tiedonsiirron nopeutta.\\nTämä saattaa kasvattaa ruutujen pudotusten riskiä epävakailla yhteyksillä.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Pikanäppäimen kohdistuksen käyttäytyminen\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Älä poista pikanäppäimiä koskaan käytöstä\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Poista pikanäppäimet käytöstä, kun pääikkuna on aktiivisena\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Poista pikanäppäimet käytöstä, kun pääikkuna ei ole aktiivisena\"\nBasic.Settings.Advanced.AutoRemux=\"Muunna automaattisesti %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(tallenna mkv-muotoon)\"\nBasic.AdvAudio=\"Äänen lisäominaisuudet\"\nBasic.AdvAudio.ActiveOnly=\"Vain aktiiviset lähteet\"\nBasic.AdvAudio.Name=\"Nimi\"\nBasic.AdvAudio.Volume=\"Äänenvoimakkuus\"\nBasic.AdvAudio.VolumeSource=\"Äänenvoimakkuus '%1'\"\nBasic.AdvAudio.MonoSource=\"Muuttaa '%1' yksikanavaiseksi\"\nBasic.AdvAudio.Balance=\"Balanssi\"\nBasic.AdvAudio.BalanceSource=\"Äänen tasapaino '%1'\"\nBasic.AdvAudio.SyncOffset=\"Synkronoinnin viivästys\"\nBasic.AdvAudio.SyncOffsetSource=\"Synkronoinnin viivästys '%1'\"\nBasic.AdvAudio.Monitoring=\"Äänen tarkkailu\"\nBasic.AdvAudio.Monitoring.None=\"Tarkkailu pois\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Vain tarkkailu (hiljennä ulostulo)\"\nBasic.AdvAudio.Monitoring.Both=\"Tarkkailu ja ulostulo\"\nBasic.AdvAudio.MonitoringSource=\"Äänen tarkkailu laitteelle '%1'\"\nBasic.AdvAudio.AudioTracks=\"Raidat\"\nBasic.Settings.Hotkeys=\"Pikanäppäimet\"\nBasic.Settings.Hotkeys.Pair=\"Yhteiset näppäinyhdistelmät '%1':n kanssa toimivat 'togglena'\"\nBasic.Settings.Hotkeys.Filter=\"Suodatin\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Suodata pikanäppäimen mukaan\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Tämä pikanäppäin on käytössä yhden tai useamman toiminnon kanssa, klikkaa näyttääksesi ristiriidat\"\nBasic.Settings.Hotkeys.PleaseWait=\"Ladataan pikanäppäimiä, odota...\"\nBasic.Hotkeys.SelectScene=\"Vaihda näkymään\"\nBasic.SystemTray.Show=\"Näytä\"\nBasic.SystemTray.Hide=\"Piilota\"\nBasic.SystemTray.Message.Reconnecting=\"Yhteys katkaistu. Yhdistetään uudelleen...\"\nHotkeys.Backspace=\"Askelpalautin\"\nHotkeys.Tab=\"Tabulaattori\"\nHotkeys.Print=\"Print Screen\"\nHotkeys.Left=\"Vasen\"\nHotkeys.Right=\"Oikea\"\nHotkeys.Up=\"Ylös\"\nHotkeys.Down=\"Alas\"\nHotkeys.Space=\"Välilyönti\"\nHotkeys.NumpadMultiply=\"Numpad *\"\nHotkeys.NumpadDivide=\"Numpad /\"\nHotkeys.NumpadAdd=\"Numpad +\"\nHotkeys.NumpadSubtract=\"Numpad -\"\nHotkeys.NumpadDecimal=\"Numpad ,\"\nHotkeys.MouseButton=\"Hiiren %1\"\nMute=\"Mykistä\"\nUnmute=\"Poista mykistys\"\nPush-to-mute=\"Mykistyspainike\"\nPush-to-talk=\"Puhepainike\"\nSceneItemShow=\"Näytä '%1'\"\nSceneItemHide=\"Piilota '%1'\"\nOutputWarnings.NoTracksSelected=\"Sinun täytyy valita ainakin yksi raita\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Virhe ulostuloasetuksissa\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Kaikilla ulostuloilla täytyy olla valittuna vähintään yksi ääniraita.\"\nOutputWarnings.MP4Recording=\"Varoitus: MP4/MOV-muotoon tallentaessa tiedostoista tulee lukukelvottomia, mikäli niitä ei voi viimeistellä. (esim. johtuen BSOD:sta, sähkökatkosta jne.) Jos haluat tallentaa useampaa ääniraitaa, kannattaa käyttää MKV-muotoa ja muuntaa jälkikäteen MP4/MOV-muotoon. (Tiedosto → Muunna tallenteita)\"\nOutputWarnings.CannotPause=\"Varoitus: Tallennusta ei voi pysäyttää jos tallennusenkooderi on asetettu olemaan \\\"(Käytä lähetysenkooderia)\\\"\"\nOutputWarnings.CodecIncompatible=\"Ääni- tai videokooderin valinta nollattiin yhteensopimattomuuden vuoksi. Valitse pakkauskooderi luettelosta.\"\nCodecCompat.Incompatible=\"(Ei ole yhteensopiva: %1)\"\nCodecCompat.CodecPlaceholder=\"Valitse pakkauskooderi...\"\nCodecCompat.ContainerPlaceholder=\"Valitse muoto...\"\nCodecCompat.CodecMissingOnExit.Title=\"Enkooderia ei valittu\"\nCodecCompat.CodecMissingOnExit.Text=\"Vähintään yhtä video- tai äänienkooderia ei ole asetettu. Valitse enkooderit sekä tallennukselle että suoratoistolle.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Muotoa ei valittu\"\nCodecCompat.ContainerMissingOnExit.Text=\"Tallennusmuotoa ei ole valittu. Valitse tallennusmuoto, joka on yhteensopiva valitun suoratoistoenkooderin kanssa.\"\nFinalScene.Title=\"Poista näkymä\"\nFinalScene.Text=\"Näkymiä pitää olla vähintään yksi.\"\nNoSources.Title=\"Ei lähteitä\"\nNoSources.Text=\"Näyttää siltä ettet ole vielä lisännyt yhtään kuvalähdettä, joten kuva on musta. Haluatko varmasti tehdä näin?\"\nNoSources.Text.AddSource=\"Voit lisätä lähteitä klikkaamalla \\\"+\\\"-kuvaketta \\\"Lähteet\\\"-alueen alapuolella.\"\nNoSources.Label=\"Sinulla ei ole lähteitä.\\nKlikkaa + painiketta alapuolella,\\ntai klikkaa tästä hiiren oikealla painikkeella lisätäksesi yhden.\"\nChangeBG=\"Aseta väri\"\nCustomColor=\"Mukautettu väri\"\nBrowserSource.EnableHardwareAcceleration=\"Käytä laitteistokiihdytystä \\\"Selain\\\"-lähteessä\"\nAbout=\"Tietoja\"\nAbout.Info=\"OBS Studio on vapaa, ilmainen ja avoimen lähdekoodin ohjelmisto videotallennuksiin ja live-lähetyksiin.\"\nAbout.Donate=\"Tee lahjoitus\"\nAbout.GetInvolved=\"Osallistu\"\nAbout.Authors=\"Tekijät\"\nAbout.License=\"Lisenssi\"\nAbout.Error=\"Virhe! Tiedostoa ei voitu lukea.\\n\\nMene: %1\"\nAbout.Contribute=\"Tue OBS-projektia\"\nAddUrl.Title=\"Lisää lähde URL-osoitteesta\"\nAddUrl.Text=\"Olet raahannut URL-osoitteen OBS-ohjelmistoon. Haluatko lisätä URL-osoitteen lähteeksi?\"\nResizeOutputSizeOfSource=\"Muuta ulostulon koko (lähteen kokoon)\"\nResizeOutputSizeOfSource.Text=\"Pohja- ja ulostuloresoluutio muutetaan nykyisen lähteen kokoon.\"\nResizeOutputSizeOfSource.Continue=\"Haluatko jatkaa?\"\nPreviewTransition=\"Esikatsele siirtymä\"\nImporter=\"Näkymäkokoelman tuonti\"\nImporter.SelectCollection=\"Valitse skene-kokoelma\"\nImporter.Collection=\"Näkymäkokoelma\"\nImporter.HelpText=\"Lisää tiedostoja tähän ikkunaan tuodaksesi kokoelmia OBS:stä tai muista tuetuista ohjelmista.\"\nImporter.Path=\"Kokoelman polku\"\nImporter.Program=\"Havaittu ohjelma\"\nImporter.AutomaticCollectionPrompt=\"Etsi skene-kokoelmia automaattisesti\"\nImporter.AutomaticCollectionText=\"OBS pystyy löytämään automaattisesti tuotavia näkymäkokoelmia tuetuista kolmannen osapuolen sovelluksista. Haluatko, että OBS etsii automaattisesti kokoelmia?\\n\\nVoit vaihtaa tätä asetusta myöhemmin Asetukset > Yleiset > Tuonti.\"\nImporter.SelectFile=\"Selaa...\"\nRestart=\"Käynnistä uudelleen\"\nNeedsRestart=\"OBS Studio pitää käynnistää uudelleen. Haluatko tehdä sen nyt?\"\nLoadProfileNeedsRestart=\"Profiili sisältää asetuksia, jotka vaativat OBS:n uudelleenkäynnistyksen:\\n%1\\n\\nHaluatko käynnistää OBS:n uudelleen, jotta muutokset tulevat voimaan?\"\nContextBar.NoSelectedSource=\"Lähdettä ei ole valittu\"\nContextBar.MediaControls.PlayMedia=\"Toista media\"\nContextBar.MediaControls.PauseMedia=\"Keskeytä media\"\nContextBar.MediaControls.StopMedia=\"Pysäytä media\"\nContextBar.MediaControls.RestartMedia=\"Uudelleenkäynnistä media\"\nContextBar.MediaControls.PlaylistNext=\"Seuraava soittolistalla\"\nContextBar.MediaControls.PlaylistPrevious=\"Edellinen soittolistalla\"\nContextBar.MediaControls.BlindSeek=\"Median hakutyökalu\"\nYouTube.Auth.Ok=\"Valtuutus suoritettu onnistuneesti.\\nVoit nyt sulkea tämän sivun.\"\nYouTube.Auth.NoCode=\"Valtuutusprosessia ei saatettu valmiiksi.\"\nYouTube.Auth.NoChannels=\"Kanavia ei ole saatavilla valitulla tilillä\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube-käyttäjävaltuutus\"\nYouTube.Auth.WaitingAuth.Text=\"Suorita valtuuden antaminen ulkoisessa selaimessa.<br>Jos ulkoinen selain ei avaudu, suorita valtuuttaminen seuraavan linkin kautta:<br>%1\"\nYouTube.AuthError.Text=\"Kanavan tietojen saaminen epäonnistui: %1.\"\nYouTube.Actions.WindowTitle=\"YouTube-lähetyksen asetukset - Kanava: %1\"\nYouTube.Actions.CreateNewEvent=\"Luo uusi lähetys\"\nYouTube.Actions.ChooseEvent=\"Valitse olemassa oleva lähetys\"\nYouTube.Actions.Title=\"Otsikko*\"\nYouTube.Actions.MyBroadcast=\"Oma lähetys\"\nYouTube.Actions.Description=\"Kuvaus\"\nYouTube.Actions.Privacy=\"Yksityisyys*\"\nYouTube.Actions.Privacy.Private=\"Yksityinen\"\nYouTube.Actions.Privacy.Public=\"Julkinen\"\nYouTube.Actions.Privacy.Unlisted=\"Listaamaton\"\nYouTube.Actions.Category=\"Luokka\"\nYouTube.Actions.Thumbnail=\"Esikatselukuva\"\nYouTube.Actions.Thumbnail.SelectFile=\"Valitse tiedosto...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Ei tiedostoa valittuna\"\nYouTube.Actions.Thumbnail.ClearFile=\"Tyhjennä\"\nYouTube.Actions.MadeForKids=\"Onko tämä video tehty lapsille?*\"\nYouTube.Actions.MadeForKids.Yes=\"Kyllä, se on tehty lapsille\"\nYouTube.Actions.MadeForKids.No=\"Ei, se ei ole tehty lapsille\"\nYouTube.Actions.AdditionalSettings=\"Lisäasetukset\"\nYouTube.Actions.Latency=\"Viive\"\nYouTube.Actions.Latency.Normal=\"Normaali\"\nYouTube.Actions.Latency.Low=\"Matala\"\nYouTube.Actions.Latency.UltraLow=\"Erittäin matala\"\nYouTube.Actions.EnableAutoStart=\"Käytä automaattista käynnistystä\"\nYouTube.Actions.EnableAutoStop=\"Käytä automaattista lopettamista\"\nYouTube.Actions.AutoStartStop.TT=\"Ilmaisee pitäisikö tämän ajastetun lähetyksen käynnistyä automaattisesti\"\nYouTube.Actions.EnableDVR=\"Käytä DVR:ää\"\nYouTube.Actions.ScheduleForLater=\"Ajasta myöhemmäksi\"\nYouTube.Actions.RememberSettings=\"Muista asetukset\"\nYouTube.Actions.Create_Ready=\"Luo lähetys\"\nYouTube.Actions.Create_GoLive=\"Luo lähetys ja aloita striimaaminen\"\nYouTube.Actions.Choose_Ready=\"Valitse lähetys\"\nYouTube.Actions.Choose_GoLive=\"Valitse lähetys ja aloita striimaaminen\"\nYouTube.Actions.Create_Schedule=\"Ajasta lähetys\"\nYouTube.Actions.Create_Schedule_Ready=\"Ajasta ja valitse lähetys\"\nYouTube.Actions.Dashboard=\"Avaa YouTube Studio\"\nYouTube.Actions.Error.Title=\"Live-lähetyksen luontivirhe\"\nYouTube.Actions.Error.Text=\"YouTube-pääsyvirhe '%1'.<br/>Yksityiskohtainen virhekuvaus on osoitteessa <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTube-pääsyvirhe. Tarkista verkkoyhteytesi tila tai pääsysi YouTube-palvelimelle.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Lähetyksen luontivirhe '%1'.<br/>Yksityiskohtainen virhekuvaus on osoitteessa <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Striimiä ei luotu. Yhdistä tilisi uudelleen.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTuben rajapintavirhe. Katso lokitiedosto saadaksesi lisätietoja.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Valittua lähetystä ei löytynyt.\"\nYouTube.Actions.Error.FileMissing=\"Valittua tiedostoa ei ole olemassa.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Valitun tiedoston avaaminen epäonnistui.\"\nYouTube.Actions.Error.FileTooLarge=\"Valittu tiedosto on liian suuri (Raja: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Lähetyksen siirtäminen epäonnistui: %1<br/><br/>Jos virhe jatkuu <a href='https://studio.youtube.com/video/%2/livestreaming'>avaa lähetys YouTube Studio</a> ja yritä manuaalisesti.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Lähetys on siirtymässä testivaiheeseen, tämä voi kestää jonkin aikaa. Yritä uudelleen 10-30 sekunnin kuluttua.\"\nYouTube.Actions.EventsLoading=\"Ladataan listaa tapahtumista...\"\nYouTube.Actions.EventCreated.Title=\"Tapahtuma luotu\"\nYouTube.Actions.EventCreated.Text=\"Tapahtuma luotu onnistuneesti.\"\nYouTube.Actions.Stream=\"Lähetys\"\nYouTube.Actions.Stream.ScheduledFor=\"Ajastettu alkamaan %1\"\nYouTube.Actions.Stream.Resume=\"Jatka keskeytynyttä lähetystä\"\nYouTube.Actions.Stream.YTStudio=\"Automaattisesti luotu YouTube Studion toimesta\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Luodaan uutta live-lähetystä, odota hetki...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Manuaalinen käynnistys vaaditaan\"\nYouTube.Actions.AutoStartStreamingWarning=\"Automaattinen käynnistys on poistettu käytöstä tämän tapahtuman osalta, napsauta \\\"Aloita suora lähetys\\\" aloittaaksesi lähetyksen.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Et voi yhdistää uudelleen.<br>Lähetyksesi pysähtyy, jolloin et ole enää suorassa lähetyksessä.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Live lähettäminen ei ole käytössä valitulla YouTube-kanavalla.<br/><br/>Katso <a href='https://www.youtube.com/features'>youtube.com/features</a> saadaksesi lisätietoja.\"\nYouTube.Errors.livePermissionBlocked=\"Suoratoistaminen ei ole käytettävissä valitulla YouTube-kanavalla.<br/>Huomioithan, että voi kestää jopa 24 tuntia, kun se on otettu käyttöön kanavaasetuksissa.<br/><br/>Katso lisätietoja <a href='https://www.youtube.com/features'>youtube.com/features</a>.\"\nYouTube.Errors.errorExecutingTransition=\"Siirtymä epäonnistui taustaosan virheen vuoksi. Yritä uudelleen muutaman sekunnin kuluttua.\"\nYouTube.Errors.errorStreamInactive=\"YouTube ei vastaanota tietoja suoratoistoosi. Tarkista asetuksesi ja yritä uudelleen.\"\nYouTube.Errors.invalidTransition=\"Siirtymäyritys oli virheellinen. Tämä voi johtua siitä, että lähetys ei ollut päättynyt edelliseen siirtymään. Odota muutama sekunti ja yritä uudelleen.\"\nYouTube.DocksRemoval.Title=\"Tyhjennä vanhat YouTube selaimen telakat\"\nYouTube.DocksRemoval.Text=\"Nämä selaimen telakoinnit poistetaan vanhentuneina:\\n\\n%1\\nKäytä sen sijaan \\\"Docks/YouTube Live Control Room\\\".\"\nConfigDownload.WarningMessageTitle=\"Varoitus\"\nFailedToStartStream.MissingConfigURL=\"Palvelulle ei ole saatavilla konfigurointi URL-osoitetta\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Mukautettua RTMP URL-osoitetta ei ole määritetty\"\nFailedToStartStream.InvalidCustomConfig=\"Virheellinen mukautettu määritys\"\nFailedToStartStream.MissingCanvas=\"Määritetty lisäpiirtoalue puuttuu\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Moniraitaisen videopalvelun luominen epäonnistui\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Moniraitaisen videon RTMP-ulostulon luominen epäonnistui\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC ei ole saatavilla.\\n\\n\\Kooderin tyyppiä \\\"%1\\\" ei löytynyt\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Videokooderin \\\"%1\\\" luominen epäonnistui (tyyppi: \\\"%2\\\")\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"OBS-videotietojen hakeminen epäonnistui luotaessa kooderia \\\"%1\\\" (tyyppi: \\\"%2\\\")\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Audiokooderin luominen epäonnistui\"\nFailedToStartStream.NoRTMPURLInConfig=\"Asetus ei sisällä suoratoiston kohdeosoitetta RTMP(S) URL\"\nFailedToStartStream.FallbackToDefault=\"Suoratoiston aloitus sovelluksella %1 epäonnistui; haluatko yrittää uudelleen käyttämällä koodaus asetusta?\"\nFailedToStartStream.ConfigRequestFailed=\"Asetusta ei voitu hakea %1<br><br>HTTP virhe: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Vastaanotettu tilan tuntematon arvo \\\"%1\\\"\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nHaluatko jatkaa suoratoistoa ilman %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nHaluatko jatkaa suoratoistoa?\"\nFailedToStartStream.MissingEncoderConfigs=\"Lähetyksen asetus ei sisältynyt kooderin määrityksiä\"\nFailedToStartStream.InvalidEncoderConfig=\"Live-lähetyksen asetuksissa on virheellinen kooderin määritys\"\nFailedToStartStream.StatusMissingHTML=\"Lähetyksen aloituspyyntö palautti määrittelemättömän virheen\"\nFailedToStartStream.NoConfigSupplied=\"Asetus puuttuu\"\nMultitrackVideo.Info=\"%1 optimoi asetuksesi automaattisesti koodaukselle ja lähettää useita videolaatuja kohteeseen . Tämän valitseminen lähettää tietoja %2 tietokoneestasi ja ohjelmiston asetuksista.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Ei yhteensopivia asetuksia\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 ei ole yhteensopiva seuraavien kanssa:\\n\\n%2\\nJos haluat jatkaa suoratoistoa sovelluksella %1, poista käytöstä yhteensopimattomat asetukset:\\n\\n%3\\nja aloita uudelleen.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Poista nyt käytöstä ja aloita suoratoisto\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Päivitä asetukset ja aloita suoratoisto\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 ei ole yhteensopiva [Ääni → Yleiset → Kanavat] asetettu \\\"%2\\\", %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Ääni → Yleiset → Kanavat] on asetettava arvoon \\\"%1\\\"\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 vaatii useita eri asetuksia [Ääni → Yleiset → Kanavat]\"\nBasic.OpenPluginManager=\"Liitännäisen hallinta\"\nPluginManager=\"Liitännäisen hallinta\"\nPluginManager.Restart=\"OBS Studio tulee käynnistää uudelleen, jotta liitännäismuutokset tulevat voimaan\"\nPluginManager.MissingPlugin=\"[LIITÄNNÄISTÄ EI LÖYTYNYT]\"\nPluginManager.Section.Discover=\"Selaa\"\nPluginManager.Section.Manage=\"Asennettu\"\nPluginManager.Section.Updates=\"Päivitykset\"\nPluginManager.Section.Manage.Title=\"Hallitse käytössä olevia liitännäisiä\"\n"
  },
  {
    "path": "frontend/data/locale/fil-PH.ini",
    "content": "Language=\"Filipino\"\nOK=\"Sige\"\nApply=\"I-apply\"\nCancel=\"Kanselahin\"\nClose=\"Isara\"\nSave=\"I-save\"\nDiscard=\"I-discard\"\nDisable=\"I-disable\"\nYes=\"Oo\"\nNo=\"Hindi\"\nAdd=\"Magdagdag\"\nRemove=\"Alisin\"\nRename=\"Baguhin ang pangalan\"\nInteract=\"Interaksyon\"\nFilters=\"Mga Filter\"\nProperties=\"Mga Katangian\"\nMoveUp=\"Igalaw Pataas\"\nMoveDown=\"Igalaw Pababa\"\nSettings=\"Pagtatakda\"\nDisplay=\"Displey\"\nName=\"Pangalan\"\nExit=\"Umalis\"\nMixer=\"Mixer ng Audio\"\nBrowse=\"Maghanap\"\nDroppedFrames=\"Nalaglag na mga Frame %1 (%2%)\"\nProjector.Open.Program=\"Buksan ang Program Projector\"\nProjector.Open.Preview=\"Buksan ang Preview Projector\"\nProjector.Open.Scene=\"Buksan ang Scene Projector\"\nProjector.Open.Source=\"Open Source na Projector\"\nProjector.Open.Multiview=\"Buksan ang Multiview\"\nProjector.Display=\"Displey: %1\"\nProjector.Window=\"Bagong window\"\nProjector.Title.Scene=\"Eksena: %1\"\nProjector.Title.Source=\"Pagtututok: %1\"\nProjector.ResizeWindowToContent=\"Pagkasyahin ang window sa nilalaman\"\nClear=\"Alisin\"\nRevert=\"Ibalik\"\nShow=\"Ipakita\"\nHide=\"Itago\"\nUnhideAll=\"Ipakita Lahat\"\nUntitled=\"Walang Pangalan\"\nNew=\"Bago\"\nDuplicate=\"I-duplicate\"\nEnable=\"Paganahin\"\nDisableOSXVSync=\"Huwag Paganahin ang macOS V-Sync\"\nResetOSXVSyncOnExit=\"I-reset ang V-Sync ng macOS pag-alis\"\nHighResourceUsage=\"Nasobrahan na sa kargada ang taga-encode! Mas mainam kung gumamit ka ng mas mababang setting sa video o gumamit ng mas mabilis na preset sa pag-e-encode.\"\nTransition=\"Transisyon\"\nQuickTransitions=\"Mabilisang Transisyon\"\nFadeToBlack=\"Mag-fade sa itim\"\nLeft=\"Kaliwa\"\nRight=\"Kanan\"\nTop=\"Taas\"\nBottom=\"Baba\"\nReset=\"Ulitin\"\nHours=\"Oras\"\nMinutes=\"Minuto\"\nSeconds=\"Segundo\"\nDeprecated=\"Hindi na ginagamit\"\nReplayBuffer=\"Buffer sa Pagre-replay\"\nImport=\"Mag-import\"\nExport=\"Mag-export\"\nCopy=\"Kopyahin\"\nPaste=\"Ilapat\"\nPasteReference=\"Ilapat (Sanggunian)\"\nPasteDuplicate=\"Ilapat (Duplicate)\"\nRemuxRecordings=\"Isalin ang mga Rekording\"\nNext=\"Susunod\"\nBack=\"Bumalik\"\nDefaults=\"Mga Default\"\nRestoreDefaults=\"Mga Default\"\nHideMixer=\"Itago sa Mixer\"\nTransitionOverride=\"Mas Susunding Transisyon\"\nShowTransition=\"Ipapakitang Transisyon\"\nHideTransition=\"Itatagong Transisyon\"\nNone=\"Wala\"\nStudioMode.Preview=\"Pribyu\"\nStudioMode.Program=\"Programa\"\nStudioMode.PreviewSceneName=\"Pasilip: %1\"\nStudioMode.ProgramSceneName=\"Programa: %1\"\nShowInMultiview=\"Ipakita sa Multiview\"\nVerticalLayout=\"Patayong Layout\"\nGroup=\"Grupo\"\nDoNotShowAgain=\"Huwag nang ipakita ulit\"\nCalculating=\"Kinukuwenta...\"\nFullscreen=\"Buong screen\"\nWindowed=\"Pa-window\"\nRefreshBrowser=\"I-refresh\"\nLockVolume=\"I-lock ang Lakas ng Tunog\"\nLogViewer=\"Pantingin sa Log\"\nShowOnStartup=\"Ipakita pagbukas\"\nOpenFile=\"Buksan ang file\"\nAddScene=\"Magdagdag ng Eksena\"\nAddSource=\"Magdagdag ng Pagtututok\"\nRemoveScene=\"Alisin ang Napiling Eksena\"\nRemoveSource=\"Alisin ang mga Napiling Pagtututok\"\nMoveSceneUp=\"Ilipat Ang Eksena Pababa\"\nMoveSceneDown=\"Ilipat Ang Eksena Pababa\"\nMoveSourceUp=\"Ilipat ang mga Pagtututok Pataas\"\nMoveSourceDown=\"Ilipat ang mga Pagtututok Pababa\"\nSourceProperties=\"Buksan ang mga Katangian ng Pagtututok\"\nSourceFilters=\"Buksan ang mga Filter ng Pagtututok\"\nMixerToolbarMenu=\"Tunog ng Mixer Menu\"\nSceneFilters=\"Buksan Ang Eskena Filters\"\nList=\"Listahan\"\nAutomatic=\"Awtomatiko\"\nComingSoon=\"Papaparating Na\"\nPluginsFailedToLoad.Title=\"Ang mga Plugin ay nag error sa pag-load.\"\nPluginsFailedToLoad.Text=\"Nabigong mag-load ang mga sumusunod na plugin ng OBS:\\n\\n%1\\nPaki-update o alisin ang mga plugin na ito.\"\nAlreadyRunning.Title=\"Tumatakbo na ang OBS\"\nAlreadyRunning.Text=\"Tumatakbo na ang OBS! Maliban na lamang kung gusto mong gawin ito, pakiusap patayin ang anomang umiiral na instansiya ng OBS bago subukang magpatakbo ng panibagong instansiya. Kung itinakda sa OBS na lumiit tungo sa system tray, pakitingnan kung ito ay tumatakbo pa rin doon.\"\nAlreadyRunning.LaunchAnyway=\"Patakbuhin pa rin\"\nCrashHandling.Dialog.Title=\"Nalamang Nag-crash ang OBS Studio\"\nCrashHandling.Labels.Text=\"Hindi maayos ang pagkasara ng OBS Studio.\\n\\nIpatakbo sa Safe Mode (hindi ipapagana ang mga third-party plugins, scripting, at WebSockets)?\"\nCrashHandling.Labels.PrivacyNotice=\"Maaari mo ring piliin na awtomatiko ang pag-upload ng pinakabagong crash report sa OBSProject.<br /><br />Mangyaring basahin ang <a href='https://obsproject.com/privacy-policy'>Mga Patakaran sa Pribasya</a> bago i-upload ang mga file at magbigay pansin sa mga parte ukol sa pag-upload ng mga file.\"\nCrashHandling.Checkbox.SendReport=\"Binasa ko ang patakaran sa pribasya at pinahihintulotan ko ang pag-upload.\"\nCrashHandling.Buttons.LaunchSafe=\"Ipatakbo sa Safe Mode.\"\nCrashHandling.Buttons.LaunchNormal=\"Ipatakbo sa Karaniwang Paraan.\"\nCrashHandling.Errors.UploadJSONError=\"Isang error ang nangyari habang ina-upload ang kamakailang ulat ng pagkandarapa. Mangyaring pakisubukan muli mamamaya.\"\nSafeMode.Restart=\"Nais mo bang i-restart ang OBS sa Ligtas na Mode (hindi pinagana ang mga third-party na plugin, scripting, at WebSockets)?\"\nSafeMode.RestartNormal=\"Nais mo bang i-restart ang OBS sa Normal na Mode?\"\nChromeOS.Title=\"Hindi suportadong Plataporma\"\nChromeOS.Text=\"Mukhang tumatakbo ang OBS sa loob ng isang lalagyan ng ChromeOS. Ang platform na ito ay hindi na suportado.\"\nWine.Title=\"May natuklasan na wine\"\nWine.Text=\"Hindi sinusuporta ang pagpapatakbo ng OBS sa Wine, at maraming feature ang hindi gagana o magkakakulang tulad ng dakip o mga pagtututok ng device. <br><br>Inimumungkahi ang pagtatakbo ng wastong bersiyon ng OBS, isang halimbawa na ang <a href='https://flathub.org/apps/details/com.obsproject.Studio'>bersiyong Flatpak namin</a> o package ng operating system mo.\"\nDockCloseWarning.Title=\"Isinasara ang Dockable Window\"\nDockCloseWarning.Text=\"Isinara mo ang dockable na window. Kung gusto mong ipakita itong muli, gamitin ang Docks menu sa menu bar.\"\nExtraBrowsers=\"Mga Pasadyang Browser Dock\"\nExtraBrowsers.Info=\"Magdagdag ng mga dock sa pamamagitan ng pagbibigay sa kanila ng pangalan at URL, saka pindutin ang Ilagay o Isara para buksan ang mga dock. Maaaring magdagdag o magbawas ng mga dock kahit kailan.\"\nExtraBrowsers.DockName=\"Pangalan ng Daugan\"\nAuth.Authing.Title=\"Pinapatunayan...\"\nAuth.Authing.Text=\"Pinapatunayan sa %1, mangyaring maghintay...\"\nAuth.AuthFailure.Title=\"Pagkabigo sa Pagpapatunay\"\nAuth.AuthFailure.Text=\"Bigo sa pagpapatunay sa %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Nangangailangan ng Pagpapatunay\"\nAuth.InvalidScope.Text=\"Ang mga kinakailangan sa pagpapatunay para sa %1 ay nagbago na. Ang ilang mga katangian ay maaaring wala na.\"\nAuth.LoadingChannel.Title=\"Nilo-load ang impormasyon ng channel...\"\nAuth.LoadingChannel.Text=\"Nilo-load ang impormasyon ng channel para sa %1, mangyaring maghintay...\"\nAuth.LoadingChannel.Error=\"Hindi makuha ang channel information.\"\nAuth.ChannelFailure.Title=\"Nabigo sa pagload ng channel\"\nAuth.ChannelFailure.Text=\"Nabigo sa pagload ng impormasyon sa channel para sa %1\\n\\n%2: %3\"\nAuth.StreamInfo=\"Impormasyon ng Stream\"\nTwitchAuth.Stats=\"Estadistika ng Twitch\"\nTwitchAuth.Feed=\"Twitch Aktibidad Feed\"\nTwitchAuth.TwoFactorFail.Title=\"Hindi ma-itanong ang stream key\"\nTwitchAuth.TwoFactorFail.Text=\"Hindi naka-connect ang OBS sa iyong account sa Twitch. Pakisiguradong nakaayos na ang two-factor authentication sa <a href='https://www.twitch.tv/settings/security'>security settings ng iyong Twitch</a> dahil kailangan ito para makapag-stream.\"\nRestreamAuth.Channels=\"I-restream ang mga Channel\"\nCopy.Filters=\"Kopyahin ang mga filter\"\nPaste.Filters=\"I-paste ang mga panala\"\nBrowserPanelInit.Title=\"Sinisimulan ang Browser...\"\nBrowserPanelInit.Text=\"Sinisimulan ang Browser, mangyaring maghintay...\"\nBandwidthTest.Region=\"Rehiyon\"\nBandwidthTest.Region.US=\"Estados Unidos\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Asya\"\nBandwidthTest.Region.Other=\"Iba pa\"\nBasic.AutoConfig=\"Kusang-tagaayos\"\nBasic.AutoConfig.ApplySettings=\"Gamitin ang Kaayusan\"\nBasic.AutoConfig.StartPage=\"Kaalaman sa Paggamit\"\nBasic.AutoConfig.StartPage.SubTitle=\"Tukuyin ang nais mong paggamitan ng programa\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"I-optimize para sa pagsi-stream, pangalawa lang ang pagrerecord\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"I-optimize para sa pagrerecord, hindi ako magsi-stream\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Virtual camera lang ang gagamitin ko\"\nBasic.AutoConfig.VideoPage=\"Kaayusan ng Video\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Tukuyin ang mga video setting na nais mong gamitin\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Gamitin ang Kasalukuyan(%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"%1 Displey (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Gamitin ang Kasalukuyan (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Maaaring 60 o 30, pero piliin ang 60 kung maaari\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 o 30, pero piliin ang mas mataas na resolusyon\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Paalala: Ang kanbas (basehan) na ito ay hindi kinakailangang magkaparehas sa resolusyon ng iyong pagsi-stream o pagrerekord. Maaaring pagkasyahin ang iyong tunay na resolusyon sa pagsi-stream o pagrerekord sa resolusyon ng kanbas upang mabawasan ang paggamit ng mapagkukunan at ng mga kinakailangang bitrate.\"\nBasic.AutoConfig.StreamPage=\"Impormasyon sa Stream\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Pakilagay ang iyong impormasyon pang stream\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"I-connect ang Account (ipinapayo)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"I-disconnect ang Account\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"I-disconnect ang Account?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Ang pagbabagong ito ay mangyayari agad. Sigurado ka bang nais mong i-disconnect ang iyong account?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Ang susi ng iyong stream\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Karagdagang Impormasyon\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Gumamit ang Stream key\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Gamitin ang Stream Key (Masalimuot)\"\nBasic.AutoConfig.StreamPage.Service=\"Serbisyo\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Ipakita lahat...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Pasadya...\"\nBasic.AutoConfig.StreamPage.Server=\"Serber\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: ilagay ang encryption passphrase \\nRTMP: ilagay ang key na ibinigay ng serbisyo.\\nSRT: ilagay ang streamid kung gumagamit ng isa ang serbisyo.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Susi ng Encoder\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Token Ng Tagapagdala\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Nakaconnect na account\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Tantiyahin ang bitrate sa pamamagitan ng pagsubok ng bandwidth (maaaring tumagal ng ilang minuto)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Pasyahin ang hardware encoding\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Ang Hardware Encoding ay nag-aalis ng halos lahat na nagamit na CPU, pero maaaring kailangan ng mas maraming bitrate para makuha ang parehong antas ng kalidad.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Babala sa pag stream\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Ang pagsubok ng bandwidth ay magsisimulang magstream ng randomized na video data na walang audio tungo sa iyong channel. Kung kaya mo, ipinapayo na pansamantalang patayin ang pagsa-save ng mga bidyo ng mga stream at itakda sa stream na private hanggang matapos ang pagsubok. Tuloy?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Pagsusulit %1\"\nBasic.AutoConfig.TestPage=\"Mga Huling Resulta\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Ang program ay nagsasagawa na ng isang set ng mga pagsusuri para matantiya ang mga pinakamainam na settings\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Tapos na ang pagsusuri\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Pagsasagawa ng bandwidth test, ito ay maaaring tumagal ng ilang minuto...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Walang output para sa protocol ng serbisyong ito ang natagpuan\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Kumukunekta sa: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Bigong kumonekta sa alin mang server, paki-tingnan ang iyong koneksyon sa internet at subukan ulit.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Sinusuri ang bandwidth ng: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Sinusuri ang stream encoder, maaaring itong tumagal ng isang minuto...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Sinusuri ang recording encoder, maaring tumagal ito ng isang minuto...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Bigong iandar ang encoder\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testingin %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.Header=\"Ang program na ito ay napagkaisahan na ang settings tinatayang lahat ay perpekto para sayo:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Para gamitin ang kaayusan na ito, pindutin ang \\\"Gamitin ang Kaayusan\\\". Para ma-reconfigure ang wizard at umulit ulit, pindutin ang Bumalik. Para mano-manong ayusin ang kaayusan, pindutin ang \\\"Bumalik\\\" at buksan ang Kaayusan.\"\nBasic.AutoConfig.Info=\"Ang auto-configuration wizard ay magpapasiya ng pinakamabuting settings batay sa iyong computer specs at bilis ng internet.\"\nBasic.AutoConfig.RunAnytime=\"Maaaring itong patakbuhin kahit kailan sa pamamagitan ng pagpunta sa menu ng Mga Kasangkapan.\"\nBasic.Stats=\"Mga Estadistika\"\nBasic.Stats.CPUUsage=\"Paggamit ng CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Natitirang bahagi ng disk\"\nBasic.Stats.MemoryUsage=\"Nagamit na Memory\"\nBasic.Stats.AverageTimeToRender=\"Ang average time para ma render ang frame\"\nBasic.Stats.SkippedFrames=\"Nilaktawan ang mga frame dahil sa encoding lag\"\nBasic.Stats.MissedFrames=\"Nalampasan ang frames dahil sa rendering lag\"\nBasic.Stats.Status=\"Kalagayan\"\nBasic.Stats.Status.Recording=\"Nagrerecord\"\nBasic.Stats.Status.Live=\"Naka-LIVE\"\nBasic.Stats.Status.Reconnecting=\"Muling kumukonekta\"\nBasic.Stats.Status.Inactive=\"Di-aktibo\"\nBasic.Stats.Status.Active=\"Aktibo\"\nBasic.Stats.DroppedFrames=\"Nalaglag na mga Frame (Network)\"\nBasic.Stats.MegabytesSent=\"Kabuoang Nilabas na Data\"\nBasic.Stats.DiskFullIn=\"Mapupuno ang disk (sa loob ng halos)\"\nBasic.Stats.DiskFullIn.Text=\"%s Oras, %2 Minuto\"\nBasic.Stats.ResetStats=\"I-reset ang Estadistika\"\nResetUIWarning.Title=\"Sigurado ka bang nais mong i-reset ang UI?\"\nResetUIWarning.Text=\"Ang pag-reset sa UI ay magtatago ng mga karagdagang dock. Kakailanganin mong i-unhide ang mga dock na ito mula sa menu ng Docks kung gusto mong makita ang mga ito.\\n\\nSigurado ka bang gusto mong i-reset ang UI?\"\nUpdater.Title=\"May bagong update na\"\nUpdater.Text=\"Mayroon nang bagong update:\"\nUpdater.UpdateNow=\"I-update Ngayon\"\nUpdater.RemindMeLater=\"Ipa-alala mo Mamaya\"\nUpdater.Skip=\"Laktawan ang Bersyon\"\nUpdater.NoUpdatesAvailable.Title=\"Walang magagamit na mga update\"\nUpdater.NoUpdatesAvailable.Text=\"Walang mga update ang kasalukuyang magagamit\"\nUpdater.BranchNotFound.Title=\"Ang Update Channel ay inalis\"\nUpdater.BranchNotFound.Text=\"Ang iyong napiling channel ng pag-update ay hindi na available, Ang OBS ay na-reset sa default.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Hindi Available ang Integrity Check\"\nUpdater.RepairButUpdatesAvailable.Text=\"Ang pagsuri sa integridad ng file ay posible lamang para sa pinakabagong bersyon na available. Gamitin ang Tulong → Suriin Para sa Mga Update upang i-verify at i-update ang iyong pag-install ng OBS.\"\nUpdater.RepairConfirm.Title=\"Kumpirmahin ang Integrity Check\"\nUpdater.RepairConfirm.Text=\"Ang pagsisimula ng pagsusuri sa integridad ay mai-scan ang iyong pag-install ng OBS para sa sirang at muling i-download ang mga sirang/binagong file. Maaaring tumagal ito ng ilang sandali.\\n\\nGusto mo bang magpatuloy?\"\nUpdater.FailedToLaunch=\"Nabigong ilunsad ang updater\"\nQuickTransitions.SwapScenes=\"Magpalit ng Pribyu/Programa ng Eksena Pagkatapos Mag-transition\"\nQuickTransitions.SwapScenesTT=\"Pinagpapalit ang mga pribyu at bunga na eksena matapos ang pagbabago (kung umiiral pa ang orihinal na eksena ng programa).\\nHindi nito maibabalik ang mga pagbabagong ginawa sa orihinal na eksena ng programa.\"\nQuickTransitions.DuplicateScene=\"I-duplicate ang Eksena\"\nQuickTransitions.DuplicateSceneTT=\"Kapag nag-e-edit ng parehong eksena, nagbibigay-daan sa pag-edit ng pagbabago/pagpapakita ng mga source nang hindi binabago ang output ng program.\\nUpang i-edit ang mga property ng mga source nang hindi binabago ang output ng program, paganahin ang 'Duplicate Sources'.\\nAng pagbabago sa value na ito ay magre-reset sa kasalukuyang eksena ng program (kung ito ay mayroon pa din).\"\nQuickTransitions.EditProperties=\"I-duplicate ang mga Pagtututok\"\nQuickTransitions.EditPropertiesTT=\"Kapag nag-e-edit ng parehong eksena, pinapayagan ang pag-edit ng mga katangian ng mga source nang hindi binabago ang output ng program.\\nMaaari lang itong gamitin kung ang 'Duplicate Scene' ay naka-enable.\\nAng ilang partikular na source (gaya ng pagkuha o media source) ay hindi sumusuporta dito at hindi maaaring i-edit hiwalay.\\nAng pagpapalit ng value na ito ay magre-reset sa kasalukuyang eksena ng programa (kung mayroon pa rin ito).\\n\\nBabala: Dahil madodoble ang mga source, maaaring mangailangan ito ng dagdag na mapagkukunan ng system o video.\"\nQuickTransitions.HotkeyName=\"Mabilis na Pagpapalit-eksena: %1\"\nBasic.AddTransition=\"Magdagdag ng Maisasaayos na Transisyon\"\nBasic.RemoveTransition=\"Tangalin ang Maisasaayos na Transisyon\"\nBasic.TransitionProperties=\"Mga Katangian ng Transisyon\"\nBasic.SceneTransitions=\"Mga Pagpapalit-eksena\"\nBasic.TransitionDuration=\"Tagal\"\nBasic.TogglePreviewProgramMode=\"Ang Studio Mode\"\nBasic.EnablePreviewProgramMode=\"Paganahin ang Studio Mode\"\nBasic.DisablePreviewProgramMode=\"Huwag Paganahin ang Studio Mode\"\nUndo.Undo=\"Ibalik\"\nUndo.Redo=\"Ulitin\"\nUndo.Add=\"Pagdagdag ng '%1'\"\nUndo.Delete=\"Pagbura ng '%1'\"\nUndo.Rename=\"Pagpapalit ng Pangalan ng '%1'\"\nUndo.SceneCollection.Switch=\"Paglipat sa '%1'\"\nUndo.Item.Undo=\"Bawiin ang %1\"\nUndo.Item.Redo=\"Gawain ulit ang %1\"\nUndo.Sources.Multi=\"Pagbura ng %1 Sources\"\nUndo.Filters=\"Pagbabago ng Filter sa '%1'\"\nUndo.Filters.Paste.Single=\"Pagpaste ng Filter na '%1' sa '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopyahin ang mga Filters mula sa '%1' patungong '%2'\"\nUndo.Transform=\"Pagtransform ng (mga) pagtutok Sa '%1'\"\nUndo.Transform.Paste=\"Pagpaste ng Transformation sa '%1'\"\nUndo.Transform.Rotate=\"Pagikot sa '%1'\"\nUndo.Transform.Reset=\"Pagreset ng Transform sa '%1'\"\nUndo.Transform.HFlip=\"Pahalang na Pagbabaliktad sa '%1'\"\nUndo.Transform.VFlip=\"Patayong Pagbabaliktad sa '%1'\"\nUndo.Transform.FitToScreen=\"Ikasya sa Tabing sa '%1'\"\nUndo.Transform.StretchToScreen=\"Ilapad sa Tabing sa '%1'\"\nUndo.Transform.Center=\"I-gitna sa Tabing sa '%1'\"\nUndo.Transform.VCenter=\"I-patayong Gitna sa Tabing sa '%1'\"\nUndo.Transform.HCenter=\"I-pahalang Gitna sa Tabing sa '%1'\"\nUndo.Volume.Change=\"Pagbago ng Lakas ng Tunog sa '%1'\"\nUndo.Volume.Mute=\"Pagtatahimik ng '%1'\"\nUndo.Volume.Unmute=\"Pag-unmute ng '%1'\"\nUndo.Balance.Change=\"Pagbabago ng Balanse ng Audio sa '%1'\"\nUndo.SyncOffset.Change=\"Pagbabago ng Offset ng Sync ng Audio sa '%1'\"\nUndo.MonitoringType.Change=\"Pagbago ng Pagsusubaybay ng Audio sa '%1'\"\nUndo.Mixers.Change=\"Pagpalit ng mga Audio Mixer sa '%1'\"\nUndo.ForceMono.On=\"Paganahin ang Pilit na Mono sa '%1'\"\nUndo.ForceMono.Off=\"Pagwawalang gana ng Force Mono sa '%1'\"\nUndo.Properties=\"Pagbabago ng Katangian sa '%1'\"\nUndo.Scene.Duplicate=\"Pagduduplicate ng Eksena '%1'\"\nUndo.ShowTransition=\"Ipakita ang sa '%1'\"\nUndo.HideTransition=\"Pagtago ng sa '%1'\"\nUndo.ShowSceneItem=\"Pagpapakita ng '%1' sa '%2'\"\nUndo.HideSceneItem=\"Pagtago ng '%1' sa '%2'\"\nUndo.ReorderSources=\"Muling Pagaayos ng mga Sources sa '%1'\"\nUndo.MoveUp=\"Paglipat ng '%1' pataas sa '%2'\"\nUndo.MoveDown=\"Paglipat ng '%1' pababa sa '%2'\"\nUndo.MoveToTop=\"Paglipat ng '%1' sa pinakataas sa '%2'\"\nUndo.MoveToBottom=\"Paglipat ng '%1' sa pinakamalalim sa '%2'\"\nUndo.PasteSource=\"Pagpasta ng/mga Sources sa '%1'\"\nUndo.PasteSourceRef=\"I-paste ang mga Sangguniang Pagtutukoy sa '%1'\"\nUndo.GroupItems=\"Pagtitipon ng mga items sa '%1'\"\nTransitionNameDlg.Text=\"Pakilagay ang pangalan ng transisyon\"\nTransitionNameDlg.Title=\"Pangalan ng Transisyon\"\nTitleBar.SafeMode=\"LIGTAS NA MODE\"\nTitleBar.Profile=\"Ang Profile\"\nTitleBar.Scenes=\"Mga Eksena\"\nNameExists.Title=\"Ang pangalan ay umiiral na\"\nNameExists.Text=\"Ang pangalan ay nagamit na.\"\nNoNameEntered.Title=\"Pakilagay ang balidong pangalan\"\nNoNameEntered.Text=\"Hindi pwede gumamit ng walang pangalan.\"\nConfirmStart.Title=\"Magsimula ng mag Stream?\"\nConfirmStart.Text=\"Sigurado ka ba na simulang ang pag stream?\"\nConfirmStop.Title=\"Itigil ba ang Steam?\"\nConfirmStop.Text=\"Sigurado ka itigil ang pag i-stream?\"\nConfirmStopRecord.Title=\"Itigil ang Pagrerecord?\"\nConfirmStopRecord.Text=\"Sigurado ka bang nais mong itigil ang pagrerecord?\"\nConfirmBWTest.Title=\"Simulan ang Pagsusuri ng Bandwidth?\"\nConfirmBWTest.Text=\"Na-configure mo ang OBS sa bandwidth test mode. Nagbibigay-daan ang mode na ito para sa subokin ang network nang hindi na-live ang iyong channel. Kapag tapos ka na sa pagtest, kakailanganin mong i-disable ito para makita ng mga manonood ang iyong stream.\\n\\nGusto mo bang magpatuloy?\"\nConfirmRemove.Title=\"I-kumpirma ang pagtangal\"\nConfirmRemove.Text=\"Sigurado ka bang nais mong tanggalin ang '$1'?\"\nConfirmRemove.TextMultiple=\"Sigurado ka bang nais mong alisin ang %1 na mga bagay?\"\nConfirmReset.Title=\"I-Reset Ang Mga Katangian\"\nConfirmReset.Text=\"Sigurado ka bang nais mong i-reset ang kasalukuyang mga properties sa kanilang mga default?\"\nOutput.StartStreamFailed=\"Nabigong simulan ang pag-stream\"\nOutput.StartRecordingFailed=\"Nabigong simulan ang pag-record\"\nOutput.StartReplayFailed=\"Nabigong simulan ang replay buffer\"\nOutput.StartVirtualCamFailed=\"Nabigo upang simulan ang virtual camera\"\nOutput.StartFailedGeneric=\"Bigong simulang ang output. Pakitingnan ang talaan ng mga detalye.\\n\\nNote: kung ikaw ay gumagamit ng NVENC or AMD encoders, siguraduhin na nag video drivers ay naka update.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Hindi makapagsa-save ng mga replay habang naka-pause\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Babala: Ang mga muling pagpapalabas ay hindi maaring ma-save kapag nakahinto ang recording.\"\nOutput.ConnectFail.Title=\"Bigong kumunekta\"\nOutput.ConnectFail.BadPath=\"Di-wastong Landas o Connection URL. Pakitingnan ang settings upang mapatunayang wasto ang mga ito.\"\nOutput.ConnectFail.ConnectFailed=\"Bigong kumunekta sa serber\"\nOutput.ConnectFail.InvalidStream=\"Di maka pasok sa tinutukoy na channel or stream key, pakitignan ng maayos ang stream key. Kung tama, Maaaring may problema sa pagkunekta sa serber.\"\nOutput.ConnectFail.HdrDisabled=\"Ang output ng HDR ay kasalukuyang hindi pinagana para sa output na ito.\"\nOutput.ConnectFail.Error=\"Isang di-inaasahang error ng subukang kumunekta sa serber. Karagdagang impormasyon ay nasa log file.\"\nOutput.ConnectFail.Disconnected=\"Nadiskonek mula sa serber.\"\nOutput.StreamEncodeError.Title=\"May error sa pag-encode\"\nOutput.StreamEncodeError.Msg=\"Mayroong error ang lumabas habang ikaw ay nag-si-stream.\"\nOutput.StreamEncodeError.Msg.LastError=\"Isang error sa encoder ang naganap habang nag streaming:<br><br>%1\"\nOutput.RecordFail.Title=\"Bigong simulang ang pag record\"\nOutput.RecordFail.Unsupported=\"Ang output format ay maaring di suportado or di sinusuportahan ang higit sa isang audio track. Pakitingnan ang iyong settings at simulan ulet.\"\nOutput.RecordNoSpace.Title=\"Hindi sapat ang iyong espasyo\"\nOutput.RecordNoSpace.Msg=\"Di sapat ang espasyo para ipatuloy ang pagrerekord.\"\nOutput.RecordError.Title=\"Error habanag nagrerekord\"\nOutput.RecordError.Msg=\"Isang hindi matiyak na error habang nagrerekord.\"\nOutput.RecordError.EncodeErrorMsg=\"Isang encoder error habang nagrerekord\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Isang encoder error habang nagrerekord:<br><br>%1\"\nOutput.BadPath.Title=\"Di-wastong Landas ng File\"\nOutput.BadPath.Text=\"Hindi mabuksan ang na-configure na Recording Path. Pakiusap pakisuri ang Recording Path sa loob ng Settings → Output → Pagre-record.\"\nOutput.NoBroadcast.Title=\"Walang Broadcast na Kinunfigure\"\nOutput.NoBroadcast.Text=\"Kailangan mong mag-set up ng broadcast bago ka magsimulang mag-stream.\"\nOutput.BroadcastStartFailed=\"Nabigong simulan ang palabas\"\nOutput.BroadcastStopFailed=\"Nabigong pigilan ang palabas\"\nLogUploadDialog.Title=\"Na-upload na ang Log File ng OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Pakibasa ng <a href='https://obsproject.com/privacy-policy'>Mga Patakaran sa Pribasya</a> at ang pangkat ukol sa pag-upload ng mga file bago mag-upload ng kahit na anong file.\"\nLogUploadDialog.Labels.Progress=\"Ina-upload ang iyong log file. Mangyaring pakihintay...\"\nLogUploadDialog.Labels.Description.AppLog=\"Na-upload na ang iyong log file. Maaari mo nang ibahagi ang URL para sa pagdedebug o pagpapatulong.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Na-upload na ang iyong ulat ng pagkandarapa. Maaari mo nang ibahagi ang URL para sa pagdedebug.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"I-upload\"\nLogUploadDialog.Buttons.CopyURL=\"Kopyahin ang Log URL\"\nLogUploadDialog.Buttons.RetryButton=\"Subukan muli\"\nRemux.Stop=\"Itigil ang Pagre-remux\"\nRemux.ClearFinished=\"Alisin ang mga Tapos na\"\nRemux.ClearAll=\"Alisin Lahat\"\nRemux.FinishedTitle=\"Tapos na ang Remuxing\"\nRemux.Finished=\"Nai-remux na ang pagrerekord\"\nRemux.FinishedError=\"Nai-remux na ang pagrerekord, ngunit maaring hindi kompleto ang file.\"\nRemux.SelectRecording=\"Pumili ng ni-record ng OBS...\"\nRemux.SelectTarget=\"Pumili ng patutunguhang file...\"\nRemux.FileExistsTitle=\"Ang napiling mga file ay umiiral na\"\nRemux.FileExists=\"Ang mga sumusunod na piniling file ay umiiral na. Nais mo bang palitan ang mga ito?\"\nRemux.ExitUnfinishedTitle=\"Ang remuxing ay naka progress\"\nRemux.ExitUnfinished=\"Di pa tapos ang Remuxing, pag itigil ang render ang napiling file ay di magagamit.\\nGusto mo bang huminto sa pag remuxing?\"\nRemux.HelpText=\"Mag-drop ng mga file sa window na ito para mag-remux, o pumili ng walang laman na cell na \\\"OBS Recording\\\" para mag-browse ng file.\"\nRemux.NoFilesAddedTitle=\"Walang idinagdag na remuxing file\"\nRemux.NoFilesAdded=\"Walang idinagdag na file sa remux. Mag-Drop ng isang folder na naglalaman ng isa o higit pang mga file ng video.\"\nMissingFiles=\"Nawawalang mga Files\"\nMissingFiles.MissingFile=\"Nawawalang File\"\nMissingFiles.NewFile=\"Bagong File\"\nMissingFiles.HelpText=\"Ilang files ay nawawala noong huli mong binuksan ang OBS.\"\nMissingFiles.Clear=\"<naalis>\"\nMissingFiles.NumFound=\"Natagpuan ang $1 ng $2\"\nMissingFiles.Search=\"I-search ang Directory...\"\nMissingFiles.SelectFile=\"Pumili ng file...\"\nMissingFiles.SelectDir=\"Pumili ng Folder na Paghahanapan\"\nMissingFiles.State=\"Kalagayan\"\nMissingFiles.Missing=\"Nawawala\"\nMissingFiles.Replaced=\"Pinalitan\"\nMissingFiles.Cleared=\"Naalis\"\nMissingFiles.Found=\"Nakita na\"\nMissingFiles.AutoSearch=\"May nahanap na karagdagang tugma na file\"\nMissingFiles.AutoSearchText=\"Nakahanap ng OBS ng mga karagdagang ugnay para sa mga nawawalang files sa direktoring iyon. Gusto mo bang idagdag sila?\"\nMissingFiles.NoMissing.Title=\"Pagsusuri ng Nawawalang mga Files\"\nMissingFiles.NoMissing.Text=\"Parang walang files na nawawala.\"\nMacPermissions.MenuAction=\"Suriin ang Mga Pahintulot sa App...\"\nMacPermissions.Title=\"Suriin ang Mga Pahintulot sa App\"\nMacPermissions.Description=\"Kinakailangan ng OBS Studio ang iyong pahintulot upang makapagbigay ng ilang partikular na feature. Inirerekomenda na i-enable ang mga pahintulot na ito, ngunit hindi kinakailangang gamitin ang mga ito sa app. Maaari mong paganahin ang mga ito sa ibang pagkakataon.\"\nMacPermissions.Description.OpenDialog=\"Maaari mong muling buksan ang dialog na ito sa pamamagitan ng menu ng OBS Studio.\"\nMacPermissions.AccessGranted=\"Pinayagan ang pag-access\"\nMacPermissions.RequestAccess=\"Humiling ng Access\"\nMacPermissions.OpenPreferences=\"Buksan ang %1 Mga Kagustuhan\"\nMacPermissions.Item.ScreenRecording=\"Pag-record ng Screen\"\nMacPermissions.Item.ScreenRecording.Details=\"Kinakailangan ng OBS ang pahintulot na ito upang makuha ang iyong screen.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Ang pahintulot na ito ay kailangan upang makakuha ang nilalaman mula sa webcam o capture card.\"\nMacPermissions.Item.Microphone=\"Mikropono\"\nMacPermissions.Item.Microphone.Details=\"Kailangan ang OBS ng itong permisyon kung gumugusto nakaptur ng iyong mikropono o isang eksternal na awdio kagamitan.\"\nMacPermissions.Item.Accessibility=\"Aksesibilidad\"\nMacPermissions.Continue=\"Ipagpatuloy\"\nSourceLeak.Title=\"Error Sa Paglilinis ng Pagtututok\"\nSourceLeak.Text=\"May problema habang binabago ang mga koleksyon ng eksena at ang ilang mga mapagkukunan ay hindi maaaring i-unload. Ang isyung ito ay karaniwang sanhi ng mga plugin na hindi naglalabas ng mga mapagkukunan nang maayos. Pakiusap tiyakin na ang anumang mga plugin na iyong ginagamit ay napapanahon.\\n\\nOBS Studio ay lalabas na ngayon para maiwasan ang anumang potensyal na katiwalian ng data.\"\nBasic.DesktopDevice1=\"Tinig ng Desktop\"\nBasic.DesktopDevice2=\"Ika-2 Tinig ng Desktop\"\nBasic.AuxDevice1=\"Ang Mic/Aux\"\nBasic.AuxDevice2=\"Ang Mic/Aux 2\"\nBasic.AuxDevice3=\"Ang Mic/Aux 3\"\nBasic.AuxDevice4=\"Ang Mic/Aux 4\"\nBasic.Scene=\"Eksena\"\nBasic.DisplayCapture=\"Dakip ng Displey\"\nBasic.Main.PreviewConextMenu.Enable=\"Paganahin ang Pribyu\"\nBasic.Main.Preview.Disable=\"Huwag Paganahin ang Pribyu\"\nScaleFiltering=\"I-filter iskala\"\nScaleFiltering.Point=\"Punto\"\nScaleFiltering.Area=\"Laki\"\nBlendingMethod=\"Blending ng Pamamaraan\"\nBlendingMethod.Default=\"I-Default\"\nBlendingMethod.SrgbOff=\"Naka-off ang SRGB\"\nBlendingMode=\"Paraan ng Pagbeblend\"\nBlendingMode.Additive=\"Magdagdag\"\nBlendingMode.Multiply=\"Multiplikahin\"\nBlendingMode.Lighten=\"Papusawin\"\nBlendingMode.Darken=\"Padilimin\"\nDeinterlacing.Discard=\"Baliwalain\"\nDeinterlacing.TopFieldFirst=\"Pang unang itaas na field\"\nDeinterlacing.BottomFieldFirst=\"Pang unang ibaba na field\"\nVolControl.SliderUnmuted=\"Pandausdos ng lakas ng tunog ng '%1':\"\nVolControl.SliderMuted=\"Pandausdos ng lakas ng tunog ng '%1': (kasalukuyang nakatahimik)\"\nVolControl.Properties=\"Mga Katangian para sa '%1'\"\nVolControl.UnassignedWarning.Title=\"Hindi Nakatalagang Pagtututok ng Audio\"\nVolControl.UnassignedWarning.Text=\"Ang \\\"%1 \\\" ay hindi nakatalaga sa anumang mga audio track at hindi ito maririnig sa mga stream o pag-record.\\n\\n upang magtalaga ng isang mapagkukunan ng audio sa isang track, buksan ang mga advanced na properties ng Audio sa pamamagitan ng menu ng pag-click sa kanan o ang pindutan ng cog sa toolbar ng mixer dock\"\nBasic.Main.AddSceneDlg.Title=\"Magdagdag ng mga Eksena\"\nBasic.Main.AddSceneDlg.Text=\"Pakilagay ang pangalan ng eksena\"\nBasic.Main.DefaultSceneName.Text=\"Eksena %1\"\nBasic.Main.AddSceneCollection.Title=\"Magdagdag ng Koleksyon ng Eksena\"\nBasic.Main.AddSceneCollection.Text=\"Mangyaring ipasok ang pangalan ng koleksyon ng eksena\"\nBasic.Main.RenameSceneCollection.Title=\"Pangalanang muli ang Koleksyon ng Eksena\"\nAddProfile.Title=\"Magdagdag ng Profile\"\nAddProfile.Text=\"Pakipasok ang pangalan ng profile\"\nAddProfile.WizardCheckbox=\"Ipakita ang alalay sa kusang pagsasaayos\"\nRenameProfile.Title=\"Palitan ang pangalan ng Profile\"\nBasic.Main.MixerRename.Title=\"Palitan ang pangalan ng Audio Source\"\nBasic.Main.MixerRename.Text=\"Maaring pangalanin ang pinagtututukan ng audio\"\nBasic.Main.PreviewDisabled=\"Kasalukuyang hindi pinapagana ang pribyu\"\nBasic.SourceSelect=\"Lumikha / Pumili ng mga Pagtutukan\"\nBasic.SourceSelect.CreateNew=\"Gumawa ng bago\"\nBasic.SourceSelect.AddExisting=\"Magdagdag ng Umiiral na\"\nBasic.SourceSelect.AddVisible=\"Gawing nakikita ang mapagkukunan\"\nBasic.Main.Sources.Visibility=\"Pagkakakita\"\nBasic.Main.Sources.VisibilityDescription=\"Pinapalit ang pagkakakita ng '%1' sa canvas\"\nBasic.Main.Sources.Lock=\"Nakakandado\"\nBasic.Main.Sources.LockDescription=\"Pake lock ang posisyon at sukat ng '%1' sa canvas\"\nBasic.PropertiesWindow=\"Mga Katangian para sa '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (awtomatikang-pagpili: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Pumili ng kulay\"\nBasic.PropertiesWindow.SelectFont=\"Piliin ang font\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Pumili ng isang Font\"\nBasic.PropertiesWindow.ConfirmTitle=\"Binago ang Mga Setting\"\nBasic.PropertiesWindow.Confirm=\"Mayroong mga hindi nai-save na pagbabago. Gusto mo bang panatilihin ang mga ito?\"\nBasic.PropertiesWindow.NoProperties=\"Walang makikitang mga katangian\"\nBasic.PropertiesWindow.AddFiles=\"Magdagdag ng mga File\"\nBasic.PropertiesWindow.AddDir=\"Magdagdag ng Direktoryo\"\nBasic.PropertiesWindow.AddURL=\"Magdagdag ng Path/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Magdagdag ng direktoryo sa '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Magdagdag ng mga file sa '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Magdagdag ng entry sa '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"I-edit ang entry mula sa '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Mga Simpleng FPS na Halaga\"\nBasic.PropertiesView.FPS.Rational=\"Mga Rational Value FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Mga wastong saklaw ng FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"Buksan ang link sa pangunahing web browser mo?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Buksan ang URL\"\nBasic.InteractionWindow=\"Nakikisalamuha... '%1'\"\nBasic.StatusBar.Reconnecting=\"Nadiskonek, muling kumukonekta sa loob ng %2 segundo(s) (pagtatangka %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Sinusubukang kumunekta... (tangka %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Matagumpay na reconnection\"\nBasic.StatusBar.Delay=\"Pag antala (%1 segundo)\"\nBasic.StatusBar.DelayStartingIn=\"Na antala(magsisimula %1 sec)\"\nBasic.StatusBar.DelayStoppingIn=\"Na antala(hihinto %1 sec)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Na antala (hihinto %1 sec, sisimula %2 sec)\"\nBasic.StatusBar.RecordingSavedTo=\"Na-save ang Recording sa '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Na-save ang Replay Buffer sa '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Naimpok ang screenshot sa '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Ang pagre-record ay awtomatikong na-remux sa '%1'\"\nBasic.Filters=\"Mga Filter\"\nBasic.Filters.AsyncFilters=\"Mga Filter ng Audio / Video\"\nBasic.Filters.AudioFilters=\"Mga Filter ng Audio\"\nBasic.Filters.EffectFilters=\"Mga Filter ng Epekto\"\nBasic.Filters.Title=\"Filter para sa mga '%1'\"\nBasic.Filters.AddFilter.Title=\"Salain ang pangalan\"\nBasic.Filters.AddFilter.Text=\"Mangyaring tukuyin ang pangalan ng filter\"\nBasic.TransformWindow=\"Pagbabagong-sukat ng Eksena\"\nBasic.TransformWindow.Position=\"Puwesto\"\nBasic.TransformWindow.PositionX=\"Posisyong X\"\nBasic.TransformWindow.PositionY=\"Posisyong Y\"\nBasic.TransformWindow.Rotation=\"Pag-ikot\"\nBasic.TransformWindow.Size=\"Sukat\"\nBasic.TransformWindow.Width=\"Lapad\"\nBasic.TransformWindow.Height=\"Taas\"\nBasic.TransformWindow.BoundsType=\"Uri ng Hangganang Kahon\"\nBasic.TransformWindow.BoundsAlignment=\"Pagkahanay ng Hangganang Kahon\"\nBasic.TransformWindow.Bounds=\"Sukat ng Hangganang Kahon\"\nBasic.TransformWindow.BoundsWidth=\"Lapad ng Makaratig na Kahon\"\nBasic.TransformWindow.BoundsHeight=\"Taas ng Makaratig na Kahon\"\nBasic.TransformWindow.Crop=\"Putol\"\nBasic.TransformWindow.CropLeft=\"Gupitin sa Kaliwa\"\nBasic.TransformWindow.CropRight=\"Gupitin sa Kanan\"\nBasic.TransformWindow.CropTop=\"Gupitin sa Itaas\"\nBasic.TransformWindow.CropBottom=\"Gupitin sa Ibaba\"\nBasic.TransformWindow.Alignment.TopLeft=\"Sa taas na kaliwa\"\nBasic.TransformWindow.Alignment.TopCenter=\"Sa gintang ibabaw\"\nBasic.TransformWindow.Alignment.TopRight=\"Sa taas ng kanan\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Naiwan ang Gitna\"\nBasic.TransformWindow.Alignment.Center=\"Gitna\"\nBasic.TransformWindow.Alignment.CenterRight=\"Tama sa Gitna\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Babang Kaliwa\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Babang Gitna\"\nBasic.TransformWindow.Alignment.BottomRight=\"Baba sa Kanan\"\nBasic.TransformWindow.BoundsType.None=\"Walang hangganan\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Pinakamalaking sukat lang\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Humugis sa panloob na hangganan\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Humugis sa panlabas na hangganan\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Humugis sa lapad ng hangganan\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Humugis sa taas ng hangganan\"\nBasic.TransformWindow.BoundsType.Stretch=\"Umunat hanggang sa hangganan\"\nBasic.TransformWindow.Title=\"I-edit ang Transform para sa '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Walang piniling source\"\nBasic.Main.AddSourceHelp.Title=\"Hindi Madagdag ang Pagtututukan\"\nBasic.Main.AddSourceHelp.Text=\"Kailangan mong magkaroon ng kahit isang eksena para magdagdag ng pagtutukan.\"\nBasic.Main.Scenes=\"Mga Eksena\"\nBasic.Main.Sources=\"Mga Tinututukan\"\nBasic.Main.Source=\"Pinagtututukan\"\nBasic.Main.Controls=\"Mga kontrol\"\nBasic.Main.PreparingStream=\"Naghahanda.\"\nBasic.Main.Connecting=\"Kumukonekta...\"\nBasic.Main.StartRecording=\"Simulan ang Pagrerecord\"\nBasic.Main.StartReplayBuffer=\"Simulan na ang pag Replay Buffer\"\nBasic.Main.SaveReplay=\"I-save ang Replay\"\nBasic.Main.StartStreaming=\"Simulan ang mag stream\"\nBasic.Main.StartBroadcast=\"Simulan ang Palabas\"\nBasic.Main.StartVirtualCam=\"Simulan ang Virtual Camera\"\nBasic.Main.StopRecording=\"Itigil ang Pagrerecord\"\nBasic.Main.PauseRecording=\"I-pause ang Pagrerecord\"\nBasic.Main.UnpauseRecording=\"Ituloy ang Pagrerecord\"\nBasic.Main.SplitFile=\"Hatiin ang Recording File\"\nBasic.Main.StoppingRecording=\"Tinitigil ang Pagrerecord...\"\nBasic.Main.StopReplayBuffer=\"Itigil ang pag Re-replay Buffer\"\nBasic.Main.StoppingReplayBuffer=\"Pagtigil sa Pagre-Replay Buffer...\"\nBasic.Main.SetupBroadcast=\"Isaayos ang Palabas\"\nBasic.Main.StopStreaming=\"Itiigil ang Pag-stream\"\nBasic.Main.StopBroadcast=\"Ihinto ang Palabas\"\nBasic.Main.AutoStopEnabled=\"(Kusang Pagtitigil)\"\nBasic.Main.StoppingStreaming=\"Pagtigil sa Pag-stream...\"\nBasic.Main.ForceStopStreaming=\"Itigil ang Pag-stream (Iwaksi ang Pagkaantala)\"\nBasic.Main.ShowContextBar=\"Ipakita ang Toolbar ng Source\"\nBasic.Main.HideContextBar=\"Itago ang Toolbar ng Source\"\nBasic.Main.StopVirtualCam=\"Itigil ang Virtual Camera\"\nBasic.Main.Group=\"Grupo %1\"\nBasic.Main.GroupItems=\"I-grupo ang napiling mga aytem\"\nBasic.Main.Ungroup=\"Alisin sa Grupo\"\nBasic.Main.GridMode=\"Grid na Paraan\"\nBasic.Main.ListMode=\"Listahang Paraan\"\nBasic.Main.VirtualCamConfig=\"I-configure ang Virtual Kamera\"\nBasic.VCam.VirtualCamera=\"Bertwal na Kamera\"\nBasic.VCam.OutputType=\"Uri ng Output\"\nBasic.VCam.OutputSelection=\"Pagpili ng Output\"\nBasic.VCam.OutputType.Program=\"Programa (Default)\"\nBasic.VCam.OutputSelection.NoSelection=\"Walang pagpipilian para sa uri ng output na ito\"\nBasic.VCam.RestartWarning=\"Ang virtual camera ay mai-restart upang mailapat ang pagbabagong nito\"\nBasic.MainMenu.File=\"Talaksan (&F)\"\nBasic.MainMenu.File.Export=\"I-&export\"\nBasic.MainMenu.File.Import=\"Angkat (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Ipakita at Pag-&record\"\nBasic.MainMenu.File.Settings=\"(&Mga) Kaayusan\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Ipakita ang Folder ng Mga Setting\"\nBasic.MainMenu.File.ShowProfileFolder=\"Ipakita ang Folder ng Profile\"\nBasic.MainMenu.File.ShowMissingFiles=\"I-check para sa nawawalang mga files\"\nBasic.MainMenu.Edit=\"I-&edit\"\nBasic.MainMenu.Edit.Undo=\"Pawalang-bisa (&U)\"\nBasic.MainMenu.Edit.Redo=\"Mag-&redo\"\nBasic.MainMenu.Edit.LockPreview=\"I-&lock ang Pribyu\"\nBasic.MainMenu.Edit.Scale=\"Pagsi-&scale ng Pribyu\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Kanbas (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"Anyo (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Baguhin ang Anyo (&E)...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopyahin ang Anyo\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"I-paste ang Anyo\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"I-&reset ang Anyo\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Paikutin ng 90 degrees pakanan\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Paikutin ng 90 degrees pakaliwa\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Paikutin ng 180 degrees\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Baligtaring Pa&halang\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Baligtaring Patayo (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Pagkasyahin sa screen (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Banatin sa &screen\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Igitna sa s&creen\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"I-patayong Gitna\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"I-pahalang Gitna\"\nBasic.MainMenu.Edit.Order=\"Ay&os\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Itaas (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Ibaba (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Ilipat sa &Tuktok\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Ilipat sa Ilalim (&B)\"\nBasic.MainMenu.View.Toolbars=\"Mga &Toolbar\"\nBasic.MainMenu.View.ListboxToolbars=\"Mga Toolbar Ng Dock\"\nBasic.MainMenu.View.ContextBar=\"Pinagmulan ng Toolbar\"\nBasic.MainMenu.View.SourceIcons=\"Mga &Icon ng mga Pinagtutukan\"\nBasic.MainMenu.View.StatusBar=\"&Status bar\"\nBasic.MainMenu.View.ResetUI=\"&I-reset ang UI\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Laging Nasa Tuktok\"\nBasic.MainMenu.View.SceneListMode=\"Mode Ng Listahan Ng Eksena\"\nBasic.MainMenu.Docks.ResetDocks=\"&I-reset ang Docks\"\nBasic.MainMenu.Docks.LockDocks=\"& I-Lock ang Docks\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Custom Browser Docks...\"\nBasic.MainMenu.SceneCollection=\"Koleksyon ng Ek&sena\"\nBasic.MainMenu.Profile.Import=\"Mag-import ng Profile\"\nBasic.MainMenu.Profile.Export=\"I-export ang Profile\"\nBasic.MainMenu.SceneCollection.Import=\"Umangkat ng Koleksyon ng Eksena\"\nBasic.MainMenu.SceneCollection.Export=\"Magluwas ng Koleksyon ng Eksena\"\nBasic.MainMenu.SceneCollection.Migrate=\"Ilagay ang Basehang Resolusyon\"\nBasic.MainMenu.Profile.Exists=\"Ang Profile ay umiiral na ngayon\"\nBasic.MainMenu.Tools=\"Mga Kasangkapan (&T)\"\nBasic.MainMenu.Help=\"Tulong (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Tulong (&P)\"\nBasic.MainMenu.Help.Website=\"Pagbisita &website\"\nBasic.MainMenu.Help.Discord=\"Sumali sa &Discord Server\"\nBasic.MainMenu.Help.WhatsNew=\"Anong Bago\"\nBasic.MainMenu.Help.Logs=\"Mag-&log ng mga File\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"ipakita ang Pag-log ng mga File (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Mag-upload Kasalukuyang Mag-log ng File (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"I-upload ang &Dating Log File\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Tignan ang Kasalukuyang pagla-log (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Mga note sa bagong labas na bersyon\"\nBasic.MainMenu.Help.CheckForUpdates=\"Maghanap ng mga Update\"\nBasic.MainMenu.Help.Repair=\"Suriin ang Integridad ng File\"\nBasic.MainMenu.Help.RestartSafeMode=\"I-restart sa Safe na Mode\"\nBasic.MainMenu.Help.RestartNormal=\"I-restart sa Normal na Mode\"\nBasic.MainMenu.Help.CrashLogs=\"Mga Ulat ng Pagkakabigo\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"&Ipakita ang Humintong Balita\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"I-upload ang &Dating Ulat sa Sira\"\nBasic.MainMenu.Help.About=\"&Tungkol\"\nBasic.Settings.ProgramRestart=\"Ang programa ay dapat umummpisa muli upang magbisa ang mga nabagong kaayusan.\"\nBasic.Settings.ConfirmTitle=\"Kumpirmahin ang mga Pagbabago\"\nBasic.Settings.Confirm=\"May mga hindi na-save na pagbabago. I-save ang mga pagbabago?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"Ang %1 %2 ay kumokontrol ng ilan sa iyong mga setting ng stream\"\nBasic.Settings.General=\"Pangkalahatan\"\nBasic.Settings.General.Language=\"Wika\"\nBasic.Settings.General.Updater=\"Mga Update\"\nBasic.Settings.General.UpdateChannel=\"I-update ang Channel\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Hindi pinagana)\"\nBasic.Settings.General.EnableAutoUpdates=\"Kusang maghanap ng mga update sa pasimula\"\nBasic.Settings.General.OpenStatsOnStartup=\"Magbukas ng palitang-usap ng awtomatikongng stats tungkol sa startup\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Itago ang mga OBS window mula sa screen capture\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Ang Setting na ito ay itatago para lahat ng hindi projector na window ng OBS Studio mula sa pagkuha ng OBS at makakaapekto sa \\nother application, gaya ng conferencing, pagbabahagi ng screen, remote na suporta, mga screenshot, at iba pang capture software.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Ang pagpapagana sa opsyong ito ay nagtatago sa lahat ng hindi projector na OBS Studio na mga window mula sa pagkuha ng OBS at nakakaapekto sa iba pang mga application, tulad ng conferencing, pagbabahagi ng screen, remote na suporta, mga screenshot, at iba pang capture software.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Ipakita ang konpirmasyon ng palitang-usap nang magsimula ang mga stream\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Ipakita ang konpirmasyon ng palitang-usap nang ihinto ang mga stream\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Ipakita ang Kumpirmasyon ng dialog kapag huminto sa pagre-record\"\nBasic.Settings.General.Projectors=\"Mga Projector\"\nBasic.Settings.General.HideProjectorCursor=\"Itago ang kursor sa harap ng mga prodyektor\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Gawing laging nakakataas ang mga prodyektor\"\nBasic.Settings.General.Snapping=\"Pinagmulan ng Paghahanay na isnaping\"\nBasic.Settings.General.ScreenSnapping=\"Paglagot sa mga Pinagmulan ukol sa gilid ng iskrin\"\nBasic.Settings.General.CenterSnapping=\"Paglagot sa mga Pinagmulan ukol sa pahalang at patayong sentro\"\nBasic.Settings.General.SourceSnapping=\"Paglagot sa mga Pinagmulan ukol sa iba pang mga pinagmulan\"\nBasic.Settings.General.SnapDistance=\"Pagkamadamdam na Paglagot\"\nBasic.Settings.General.SpacingHelpers=\"Ipakita ang mga gabay sa pag-align ng pixel\"\nBasic.Settings.General.RecordWhenStreaming=\"awtomatikong pagtala nang anod\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Panatilihin ang pagrerecord kapag tumigil na ang stream\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Awtomatikong pagsisimula ng replay buffer kapag streaming\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Panatilihing aktibo ang replay buffer kahit tumigil ang stream\"\nBasic.Settings.General.SysTray=\"Bandehadong Sistema\"\nBasic.Settings.General.SysTrayWhenStarted=\"Magbawas sa bandehadong sistema kapag nagsimula na\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Palaging magbawas sa bandehadong sistema sa halip na task bar\"\nBasic.Settings.General.SaveProjectors=\"I-save ang mga prodyektor sa labasan\"\nBasic.Settings.General.CloseExistingProjectors=\"Limitahan ang isang fullscreen projector sa bawat screen\"\nBasic.Settings.General.Preview=\"Pribyu\"\nBasic.Settings.General.OverflowHidden=\"Itago ang overflow\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Laging nakikita ang Overflow\"\nBasic.Settings.General.OverflowSelectionHidden=\"Ipakita ang overflow kahit na hindi nakikita ang pinagmulan\"\nBasic.Settings.General.Importers=\"Mga taga-import\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Maghanap sa mga kilalang lugar para sa mga koleksyon ng eksena kapag nag-aangkat\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Lumipat sa eksena kapag nakadalawang-pindot\"\nBasic.Settings.General.StudioPortraitLayout=\"Paganahin ang portrait/vertical layout\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Ipakita ang mga label ng preview/program\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Pindutin para lumipat sa mga eksena\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Ipakita ang mga pangalan ng eksena\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Iguhit ang mga safe area (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Pahalang, Taas (8 Eksena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Pahalang, Baba (8 Eksena)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Patayo, Kaliwa (8 Eksena)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Patayo, Kanan (8 Eksena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Pahalang, Taas (18 mga Eksena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Pahalang, Taas (24 Eksena)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Mga Eksena lang (4 na Eksena)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Mga eksena lang (9 na Eksena)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Mga eksena lang (16 na Eksena)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Mga eksena lang (25 na Eksena)\"\nBasic.Settings.General.ChannelName.stable=\"Esteybol\"\nBasic.Settings.General.ChannelDescription.stable=\"Pinakabagong stable ay inilabas\"\nBasic.Settings.General.ChannelName.beta=\"Betas/ Ilabas ang candidates\"\nBasic.Settings.General.ChannelDescription.beta=\"Potensyal na hindi matatag na mga bersyon ng pre-release\"\nBasic.Settings.Appearance=\"Itsura\"\nBasic.Settings.Appearance.General=\"Pangkalahatan\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Estilo\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Gumamit ng pagpapatunay\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: ilagay ang srp_username.\\nRTMP: ilagay ang username.\\nSRT: hindi ginamit.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: ilagay ang srp_password.\\nRTMP: ilagay ang password.\\nSRT: ilagay ang encryption passphrase.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Paganahin ang Bandwidth Test Mode\"\nBasic.Settings.Stream.TTVAddon.None=\"Wala\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV at FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Nawawalang Setup ng Stream\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Buksan ang Kaayusan\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Nawawala ang URL at Stream Key.\\n\\nBuksan ang mga setting para ilagay ang URL at Stream Key sa tab na 'stream'.\"\nBasic.Settings.Stream.MissingUrl=\"Nawawala ang URL ng stream.\\n\\nBuksan ang mga setting para ilagay ang URL sa tab na 'Stream'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Nawawala ang URL ng stream.\\n\\nBuksan ang mga setting para ilagay ang URL sa tab na 'Stream'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Huwag pansinin ang mga rekomendasyon sa setting ng serbisyo ng streaming\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Kanselahin ang Inererekomendang Kaayusan\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Babala: Ang pagbalewala sa mga limitasyon ng serbisyo ay maaaring magresulta sa mababang kalidad ng stream o pigilan ka sa streaming.\\n\\nMagpatuloy?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Pinakamataas na Bitrate ng Video: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Pinakamataas na Bitrate ng Video: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Pinakahangganan ng Resolusyon: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Pinakamataas na FPS: %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Awto\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Awto\"\nBasic.Settings.Output=\"Pagtutunguan\"\nBasic.Settings.Output.Format=\"Porma ng recording\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Sinusulat ng Fragmented MOV ang pag-record sa mga chunks at hindi nangangailangan ng parehong pagtatapos tulad ng tradisyonal na mga file ng MOV.tinitiyak nito na ang file ay mananatiling mai-play kahit na ang pagsulat sa disk ay nagambala, halimbawa, bilang isang resulta ng isang BSOD o pagkawala ng kuryente.\\n\\nmaaaring hindi ito tugma sa lahat ng mga manlalaro at editor. Gumamit ng File → Remux Recordings upang mai-convert ang file sa isang mas katugmang format kung kinakailangan.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Sinusulat ng Fragmented mp4 ang pag-record sa mga chunks at hindi nangangailangan ng parehong pagtatapos tulad ng tradisyonal na mga MP4 file.tinitiyak nito na ang file ay mananatiling mai-play kahit na ang pagsulat sa disk ay nagambala, halimbawa, bilang isang resulta ng isang BSOD o pagkawala ng kuryente.\\n\\nmaaaring hindi ito tugma sa lahat ng mga manlalaro at editor. Gumamit ng File → Remux Recordings upang mai-convert ang file sa isang mas katugmang format kung kinakailangan.\"\nBasic.Settings.Output.Encoder.Video=\"Bidyo Encoder\"\nBasic.Settings.Output.Encoder.Audio=\"Tonog ng Encoder\"\nBasic.Settings.Output.SelectDirectory=\"Piliin ang Lagayan ng Recording\"\nBasic.Settings.Output.DynamicBitrate=\"Dynamically Baguhin ang bitrate para pamahalaan ang congestion\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dynamically Baguhin ang bitrate para pamahalaan ang congestion (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Sa halip na mag-drop ng mga frame para mabawasan ang congestion, dynamically na nagbabago ang bitrate sa mabilisang paraan.\\n\\nTandaan na maaari nitong dagdagan ang pagkaantala sa mga manonood kung may makabuluhang biglaang pagsisikip.\\nKapag bumaba ang bitrate, maaaring tumagal ng hanggang ilang minuto upang maibalik. \\n\\nKasalukuyang sinusuportahan lamang para sa RTMP.\"\nBasic.Settings.Output.Mode=\"Paraan ng Pagluluwas\"\nBasic.Settings.Output.Mode.Adv=\"Pagsulong\"\nBasic.Settings.Output.Mode.FFmpeg=\"Bungang FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Simulan ang pag replay ng buffer\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Hangganang Oras ng Pagrereplay\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Ang na estimang nagamit na memorya: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Babala: Ang tinantyang paggamit ng memory na %1 MiB ay mas malaki kaysa sa inirerekomendang maximum na %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Hindi ma-estima ang nagamit na memorya. Pakilagay ng pinakamataas na limitasyon ng memorya.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Ang panlapi ng Replay Buffer Filename\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Hulapi\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Ang Replay Buffer ay hindi maaaring gamitin kapag ang uri ng pag-record ay nakatakda sa pasadyang Output (FFmpeg).\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Pagrekord ng Kalidad\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Parehong stream\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Mataas na Kalidad, Katamtamang Laki ng File\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Lossless Quality, Napakalaki ng Laki ng File\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Babala: Itatakda ang bitrate ng streaming video sa %1, na siyang pinakamataas na limitasyon para sa kasalukuyang serbisyo ng streaming.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Babala: Ang streaming audio bitrate ay itatakda sa %1, na siyang pinakamataas na limitasyon para sa kasalukuyang serbisyo ng streaming.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Babala: Ang mga pag-record ay hindi maaaring i-pause kung ang kalidad ng pag-record ay nakatakda sa \\\"Kapareho ng stream\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Babala: ang kasalukuyang napiling format ng pag-record ay hindi tugma sa(mga) napiling stream encoder.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Babala: Ang pagrekord sa isang encoder ng software sa ibang kalidad kaysa sa stream ay mangangailangan ng dagdag na paggamit ng CPU kung mag-stream at mag-record ka sa parehong oras.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Sigurado ka bang gusto mong gumamit ng kalidad na walang pagkawala?\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264 mababang preset ng paggamit ng CPU, nagpapataas ng laki ng file)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Di-Akmang Resolusyon o Framerate\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Ang serbisyong pangstreaming ito ay hindi sinusuporta ang kasalukuyang resolusyon at/o ang bilis ng frames mo. Papalitan sila sa mga pinakaakmang value:\\n\\n%1\\n\\n. Gusto mo bang magpatuloy?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolusyon: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Bilis ng Frames: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Hindi tugmang Encoder\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Hindi sinusuportahan ng streaming service na \\\"%1\\\" ang encoder na \\\"%2\\\". Ang encoder ay gagawing \\\"%3\\\".\\n\\nGusto mo bang magpatuloy?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Ang streaming service na\\\" %1 \\\"ay hindi sumusuporta sa mga encoder na\\\" %2 \\\"at\\\"%3\\\". Ang mga encoder na ito ay mababago sa \\\"%4\\\" at \\\"%5\\\".\\n\\n Gusto mo bang magpatuloy?\"\nBasic.Settings.Output.VideoBitrate=\"Bitrate ng Video\"\nBasic.Settings.Output.AudioBitrate=\"Bitrate ng Audio\"\nBasic.Settings.Output.Reconnect=\"Awtomatikong mag-reconnect\"\nBasic.Settings.Output.RetryDelay=\"Pagaantala sa pagreretry\"\nBasic.Settings.Output.MaxRetries=\"Pinakamataas na Retries\"\nBasic.Settings.Output.Advanced=\"Paganahin ang Mga Setting ng Custom Encoder (Advanced)\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (Mababa ang paggamit ng CPU, pinakamababang kalidad)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (default) (katamtamang paggamit ng CPU, karaniwang kalidad)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (Mataas na paggamit ng CPU, Mataas na kalidad)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Kaayusan ng Pasadyang Encoder\"\nBasic.Settings.Output.CustomMuxerSettings=\"Mga Setting ng Custom Muxer\"\nBasic.Settings.Output.NoSpaceFileName=\"Bumuo ng Pangalan ng File nang walang Space\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Na-disable\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Settings ng Streaming\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Subaybayan ang 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Subaybayan ang 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Subaybayan ang 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Subaybayan ang 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Subaybayan ang 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Subaybayan ang 6\"\nBasic.Settings.Output.Adv.Encoder=\"Settings ng Encoder\"\nBasic.Settings.Output.Adv.Recording=\"Pagre-record\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Settings ng Pagre-record\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Uri ng Pagrerecord\"\nBasic.Settings.Output.Adv.Recording.Type=\"Uri\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Gamitin ang encoder para sa stream)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Pag-format ng Filename\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"I-overwrite kung umiiral ang file\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Output sa URL\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Mga format ng karaniwang recording\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Lahat ng mga File\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Landas ng file or ang URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Lalagyang ng Format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Tunog\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Bidyo\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Ang lagayan ng deskripsyon ng format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Tunog/Bidyo Codec guessed mula sa landas ng File or URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Huwag paganahin ang Encoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Bidyo Encoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Ang settings ng Bidyo Encoder(kung mayroon)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Tonog ng Encoder\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Ang Settings ng Tonog Encoder(kung mayroon)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Ang mga Settings ng Muxer (kung mayroon)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Ang pagitan ng Keyframe (frames)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Ipakita lahat ng codecs (kahit itoy posibleng di-kompatibol)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Settings Ng FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Awtomatikong Paghahati ng File\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Hatiin ayon sa Oras\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Hatiin ayon sa Sukat\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Manu-manong hatiin lamang\"\nBasic.Settings.Output.SplitFile.Time=\"Hatiin ang Oras\"\nBasic.Settings.Output.SplitFile.Size=\"Hatiin ang Sukat\"\nScreenshot=\"Pagtutunguan ng Screenshot\"\nScreenshot.SourceHotkey=\"I-screenshot Ang Napiling Pinagtututukan\"\nScreenshot.StudioProgram=\"I-save ang Screenshot ng Programa\"\nScreenshot.Preview=\"I-save ang Preview Screenshot\"\nScreenshot.Scene=\"I-save ang Screenshot ng Eksena\"\nScreenshot.Source=\"I-save ang Screenshot ng Pinagtututukan\"\nFilenameFormatting.TT.CCYY=\"Taon, apat na digit\"\nFilenameFormatting.TT.YY=\"Taon, huling dalawang digit (00-99)\"\nFilenameFormatting.TT.MM=\"Buwan bilang isang decimal number (01-12)\"\nFilenameFormatting.TT.DD=\"Araw ng buwan, zero-padded (01-31)\"\nFilenameFormatting.TT.hh=\"Oras sa format na 24h (00-23)\"\nFilenameFormatting.TT.mm=\"Minuto (00-59)\"\nFilenameFormatting.TT.ss=\"Pangalawa (00-59)\"\nFilenameFormatting.TT.a=\"Pinaikli na pangalan sa araw ng linggo\"\nFilenameFormatting.TT.A=\"Buong pangalan sa araw ng linggo\"\nFilenameFormatting.TT.b=\"Pinaikling pangalan ng buwan\"\nFilenameFormatting.TT.B=\"Buong pangalan ng buwan\"\nFilenameFormatting.TT.d=\"Araw ng buwan, zero-padded (01-31)\"\nFilenameFormatting.TT.H=\"Oras sa 24o pormat (00-23)\"\nFilenameFormatting.TT.I=\"Oras sa 12o pormat (01-12)\"\nFilenameFormatting.TT.m=\"Buwan sa desimal ng numero (01-12)\"\nFilenameFormatting.TT.M=\"Minuto (00-59)\"\nFilenameFormatting.TT.p=\"AM o PM na indikasyon\"\nFilenameFormatting.TT.s=\"Oras sa segundo mula noong UNIX epoch\"\nFilenameFormatting.TT.S=\"Pangalawa (00-59)\"\nFilenameFormatting.TT.y=\"Taon, huling dalawang digit (00-99)\"\nFilenameFormatting.TT.Y=\"Taon\"\nBasic.Settings.Video=\"Bidyo\"\nBasic.Settings.Video.Adapter=\"Adapter ng Bidyo\"\nBasic.Settings.Video.BaseResolution=\"Basehang (Kanbas) Resolusyon\"\nBasic.Settings.Video.ScaledResolution=\"Output na (Scaled) Resolusyon\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Nagtutugma ang mga resolusyon, walang kinakailangang pagbabawas]\"\nBasic.Settings.Video.FPSCommon=\"Karaniwang Balyo ng FPS\"\nBasic.Settings.Video.FPSInteger=\"Balyo ng Integer FPS\"\nBasic.Settings.Video.FPSFraction=\"Balyo ng Fractional FPS\"\nBasic.Settings.Video.InvalidResolution=\"Ang value ng resolusyon ay imbalido. Dapat ito ay [width]x[height] (i.e. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Ang kasulukuyang Bidyo output ay aktibo. Paki turn off ang anumang output para mabago ang bidyo settings.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (Pinakamabilis, pero malabo pag mag i-scaling)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (Sharpened scaling, 16 mga halimbawa)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Sharpened scaling, 36 mga halimbawa)\"\nBasic.Settings.Audio=\"Tunog\"\nBasic.Settings.Audio.SampleRate=\"Halimbawa ng Antas\"\nBasic.Settings.Audio.Channels=\"Mga Channel\"\nBasic.Settings.Audio.Meters=\"Mga Metro\"\nBasic.Settings.Audio.MeterDecayRate=\"Bilis ng Pagkupas\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Pabilisin\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Katamtaman (Tipo I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Mabagal (Tipo II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Uri ng Tuktok ng Metro\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True Peak (Pinakamaraming gamit ng CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"Babala: Ang Surround sound audio ay naka andar.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Paganahin ang surround sound audio?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Sigurado ka ba gusto mong paganahin ang surround sound audio?\"\nBasic.Settings.Audio.Devices=\"Pangkalahataang Audio Devices\"\nBasic.Settings.Audio.DesktopDevice=\"Tinig ng Desktop\"\nBasic.Settings.Audio.DesktopDevice2=\"Ika-2 Tinig ng Desktop\"\nBasic.Settings.Audio.EnablePushToMute=\"Paganahin ang Push-to-mute\"\nBasic.Settings.Audio.EnablePushToTalk=\"Paganahin ang Push-to-talk\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Hindi konektado o hindi magagamit ang device]\"\nBasic.Settings.Audio.Disabled=\"Di-napagana\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Low Latency Audio Buffering Mode (Para sa Decklink/NDI outputs)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"BABALA: Ang low latency audio buffering ay naka-enable\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Ang low latency na audio buffering mode ay maaaring magdulot ng glitch ng audio o huminto sa pag-play mula sa ilang source.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"I-enable ang low latency audio buffering mode?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Sigurado ka bang gusto mong paganahin ang low latency audio buffering mode?\"\nBasic.Settings.Accessibility=\"Aksesibilidad\"\nBasic.Settings.Accessibility.ColorOverrides=\"Gumamit ng Iba't ibang Kulay\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Pinagmulan Ng Border (Selection)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Pinagmulan Ng Border (Crop)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Pinagmulan ng Border (Hover)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Preset ng Kulay\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Orihinal\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"I-Custom\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternatibong Color Blind\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Ipakita ang mga aktibong output ang babala sa pag-exit\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prayoridad sa Proseso\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Mataas\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Higit sa Normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Mas mababa sa normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Walang ginagawa\"\nBasic.Settings.Advanced.FormatWarning=\"Babala: Ang mga format ng kulay maliban sa NV12/P010 ay pangunahing inilaan para sa pag-record, at hindi inirerekomenda kapag nagsi-stream. Ang pag-stream ay maaaring magkaroon ng mas mataas na paggamit ng CPU dahil sa conversion ng format ng kulay.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Abala: Ang Rec. 2100 ay dapat maggamit ng format namay higit na katumpakan\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Format ng Kulay\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Espasyo ng Kulay\"\nBasic.Settings.Advanced.Video.ColorRange=\"Lawak ng Kulay\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Nanglilimit\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Buo\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Tagapayong Aparato\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"I-Default\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Huwag paganahin ang Windows audio ducking\"\nBasic.Settings.Advanced.StreamDelay=\"Ang Antala ng Stream\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Katagalan\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Panatiliin ang cutoff point (dumadagdag ng antala) kapang kumokonekta\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Ang na estimang nagamit na memorya: %1 MB\"\nBasic.Settings.Advanced.Network.Disabled=\"Ang kasalukuyang napiling streaming protocol ay hindi sumusuporta sa pagbabago ng mga setting ng network.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Ibigkis sa IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Paganahin ang pagbilis ng network\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Paganahin ang TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Mga pagtatangka na gawing mas friendly ang output ng RTMP sa iba pang latency na sensitibong application sa network sa pamamagitan ng pag-regulate sa rate ng transmission.\\nMaaari nitong dagdagan ang panganib ng mga bumabagsak na frame sa mga hindi matatag na koneksyon.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Ugali ng Pokus ng Hotkey\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Huwag i-pagana ang hotkeys\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Di-pinagana ang hotkeys kung ang main window ay nasa fokus\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Di-pinagana ang hotkeys kung ang main window ay hindi nasa fokus\"\nBasic.Settings.Advanced.AutoRemux=\"Awtomatikong Remux sa %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(i-rekord bilang mkv)\"\nBasic.AdvAudio=\"Ang aria-arian ng Advanced Audio\"\nBasic.AdvAudio.ActiveOnly=\"Lamang Actibong Sources\"\nBasic.AdvAudio.Name=\"Pangalan\"\nBasic.AdvAudio.Volume=\"Lakas ng Tunog\"\nBasic.AdvAudio.VolumeSource=\"Lakas ng tunog ng '%1'\"\nBasic.AdvAudio.Balance=\"Balanse\"\nBasic.AdvAudio.BalanceSource=\"Balanse para sa '%1'\"\nBasic.AdvAudio.SyncOffset=\"Urong ng Pagkakasabay\"\nBasic.AdvAudio.SyncOffsetSource=\"Urong ng Pagkakasabay para sa '%1'\"\nBasic.AdvAudio.Monitoring=\"Ang subaybay ng Audio\"\nBasic.AdvAudio.Monitoring.None=\"I-Off ang Monitor\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Monitor lamang (i-mute ang output)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitor at Awput\"\nBasic.AdvAudio.MonitoringSource=\"Pagsusubaybay sa Audio sa %1\"\nBasic.AdvAudio.AudioTracks=\"Mga Tracks\"\nBasic.Settings.Hotkeys=\"Ang mga Hotkeys\"\nBasic.Settings.Hotkeys.Pair=\"Ang nabahaging kombinasyon ng susi na may '%1' akto sa toggles\"\nBasic.Settings.Hotkeys.Filter=\"Salain\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Salain ayon sa Hotkey\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Ang hotkey na ito ay ibinabahagi ng isa o higit pang mga aksyon, i-click upang ipakita ang mga salungatan\"\nBasic.Hotkeys.SelectScene=\"Lumipat sa eksena\"\nBasic.SystemTray.Show=\"Ipakita\"\nBasic.SystemTray.Hide=\"Itago\"\nBasic.SystemTray.Message.Reconnecting=\"Nadiskonek. Kumokonekta...\"\nHotkeys.Insert=\"Ipasok\"\nHotkeys.Delete=\"Burahin\"\nHotkeys.Home=\"Pinagmulan\"\nHotkeys.End=\"Tapos\"\nHotkeys.PageUp=\"Itaas ng Pahina\"\nHotkeys.PageDown=\"Ibaba ng pahina\"\nHotkeys.Print=\"Printa\"\nHotkeys.Pause=\"Itigil\"\nHotkeys.Left=\"Kaliwa\"\nHotkeys.Right=\"Kanan\"\nHotkeys.Up=\"Itaas\"\nHotkeys.Down=\"Ibaba\"\nHotkeys.Menu=\"Pagpipilian\"\nHotkeys.Space=\"Espasyo\"\nHotkeys.NumpadMultiply=\"Paramihin ang Numpad\"\nHotkeys.NumpadDivide=\"Hatiin ang Numpad\"\nHotkeys.NumpadAdd=\"Magdagdag ng Numpad\"\nHotkeys.NumpadSubtract=\"Magbawas ng Numpad\"\nHotkeys.NumpadDecimal=\"Numero ng Numpad\"\nMute=\"I-Mute\"\nUnmute=\"I-Unmute\"\nPush-to-mute=\"Pindutin-para-i-mute\"\nPush-to-talk=\"Pindutan-para-magsalita\"\nSceneItemShow=\"Ipakita ang '%1'\"\nSceneItemHide=\"Itago ang '%1'\"\nOutputWarnings.NoTracksSelected=\"Dapat pumili ka ng kahit isang track\"\nOutputWarnings.MP4Recording=\"Babala: Ang mga rekord na naka-save sa MP4/MOV ay hindi na mababawi kung ang file ay hindi natapos (hal. dulot ng BSODs, pagkawala ng kuryente, atbp.). Kung nais mong magrekord ng maraming audio tracks, isaalang-alang ang paggamit ng MKV at i-remux ang recording sa MP4/MOV pagkatapos nitong matapos (File → Remux Recordings)\"\nOutputWarnings.CannotPause=\"Babala: Ang pagrerekord ay hindi mapapahintong sandali kung ang encoder sa pagrerekord ay nakatakda sa \\\"(Gamitin ang encoder para sa stream)\\\"\"\nCodecCompat.CodecPlaceholder=\"Pumili ang enkoder...\"\nCodecCompat.ContainerPlaceholder=\"Pumili ang pormat...\"\nCodecCompat.CodecMissingOnExit.Title=\"Walang enkoder na pili\"\nCodecCompat.ContainerMissingOnExit.Title=\"Walang pormat na pili\"\nFinalScene.Title=\"Tanggal-Eksena\"\nFinalScene.Text=\"Kailangang mayroong kahit isang eksena.\"\nNoSources.Title=\"Walang mga Pinagtututukan\"\nNoSources.Text=\"Mukhang wala ka pang nalalagay na pinagbabatayang mga video, kaya blankong tabing lang ang ipapalabas. Sigurado bang gusto mong gawin ito?\"\nNoSources.Text.AddSource=\"Maari kang maglagay ng pagbabatayan (sources) sa pamamagitan ng pag-click ng + icon sa ilalalim ng Sources box anumang oras sa pangunahing window.\"\nNoSources.Label=\"Walang kang mga sources. \\nPindutin mo ang + button sa baba,\\no kanang-pindutan mo dito upang magdagdag ng isa.\"\nChangeBG=\"I-takda ang Kulay\"\nCustomColor=\"Pasadyang Kulay\"\nBrowserSource.EnableHardwareAcceleration=\"Paganahin ang Browser Source Hardware Acceleration\"\nAbout=\"Tungkol dito\"\nAbout.Info=\"Ang OBS Studio ay isang libre at open source na software para sa parerercord ng bidyo at pag-iistream nang live.\"\nAbout.Donate=\"Tumulong\"\nAbout.GetInvolved=\"Makisali\"\nAbout.Authors=\"Mga Nag-gawa\"\nAbout.License=\"Lisensya\"\nAbout.Contribute=\"I-suporta ang OBS Project\"\nAddUrl.Title=\"Magdagdag ng Source gamit ang URL\"\nAddUrl.Text=\"Hinila mo ang isang URL papunta sa OBS. Kusang madaragdag ang link bilang isang source. Tuloy?\"\nResizeOutputSizeOfSource=\"Baguhin ang laki ng output (laki ng pinagmulan)\"\nResizeOutputSizeOfSource.Text=\"Ang basehan at output na resolusyon ay magiging magkasinlaki ng kasalukuyang source.\"\nResizeOutputSizeOfSource.Continue=\"Gusto mo bang magpatuloy?\"\nPreviewTransition=\"Silipin ang Transisyon\"\nImporter=\"Pang-angkat ng Koleksyon ng Eksena\"\nImporter.SelectCollection=\"Pumili ng Koleksyon ng Eksena\"\nImporter.Collection=\"Koleksyon ng Eksena\"\nImporter.HelpText=\"Magdagdag ng mga file sa window na ito upang mag-angkat ng mga koleksyon mula sa OBS o sa ibang mga suportadong program.\"\nImporter.Path=\"Kinaroroonan ng Koleksyon\"\nImporter.Program=\"Aplikasyong Natuklas\"\nImporter.AutomaticCollectionPrompt=\"Awtomatikong Maghanap ng mga Koleksyon ng Eksena\"\nImporter.AutomaticCollectionText=\"Kaya ng OBS na kusang maghanap ng maaangkas na koleksyon ng eksena mula sa mga suportadong third-party program. Nais mo bang kusang maghanap ng mga koleksyon ang OBS para sa iyo?\\n\\nMaaari mo itong palitan mamaya sa Settings > General > Importers.\"\nRestart=\"I-restart\"\nNeedsRestart=\"Kailangang i-restart ang OBS Studio. Gusto mo bang i-restart ngayon?\"\nLoadProfileNeedsRestart=\"Ang profile ay naglalaman ng mga setting na nangangailangan ng pag-restart ng OBS:\\n%1\\n\\nGusto mo bang i-restart ang OBS para magkabisa ang mga setting na ito?\"\nContextBar.NoSelectedSource=\"Walang piniling source\"\nContextBar.MediaControls.PlayMedia=\"Maglaro ng Media\"\nContextBar.MediaControls.PauseMedia=\"I-pause ang Media\"\nContextBar.MediaControls.StopMedia=\"I-tigil ang Media\"\nContextBar.MediaControls.RestartMedia=\"Ulitin ang Media\"\nContextBar.MediaControls.PlaylistNext=\"Kasunod sa Playlist\"\nContextBar.MediaControls.PlaylistPrevious=\"Nakaraan sa Playlist\"\nYouTube.Auth.Ok=\"Ang Awtorisasyon ay nakumpletong Tagumpay\\nMaaari mo na ngayong isara ang pahinang ito.\"\nYouTube.Auth.NoCode=\"Hindi natapos ang proseso ng awtorisasyon.\"\nYouTube.Auth.NoChannels=\"Walang channel (s) na available sa napiling account\"\nYouTube.Auth.WaitingAuth.Title=\"Pagpapahintulot ng Paggamit sa Youtube\"\nYouTube.Auth.WaitingAuth.Text=\"Mangyaring kumpletuhin ang awtorisasyon sa iyong browser.<br>Kung hindi bumukas ang browser, sundan ang link na ito at kumpletuhin ang awtorisasyon:<br>%1\"\nYouTube.AuthError.Text=\"Nabigong makuha ang impormasyon ng channel: %1.\"\nYouTube.Actions.WindowTitle=\"Setup ng Broadcast sa YouTube - Channel: %1\"\nYouTube.Actions.CreateNewEvent=\"Gumawa ng Bagong Palabas\"\nYouTube.Actions.ChooseEvent=\"Piliin ang Kasalukuyang Broadcast\"\nYouTube.Actions.Title=\"Pamagat*\"\nYouTube.Actions.MyBroadcast=\"Aking Palabas\"\nYouTube.Actions.Description=\"Paglalarawan\"\nYouTube.Actions.Privacy=\"Praybasi*\"\nYouTube.Actions.Privacy.Private=\"Naka-pribado\"\nYouTube.Actions.Privacy.Public=\"Naka-publiko\"\nYouTube.Actions.Privacy.Unlisted=\"Nakatago\"\nYouTube.Actions.Category=\"Kategorya\"\nYouTube.Actions.Thumbnail.SelectFile=\"Pumili ng file...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Walang napiling file\"\nYouTube.Actions.Thumbnail.ClearFile=\"Linisin\"\nYouTube.Actions.MadeForKids=\"Para ba sa mga bata ang bidyo na ito?*\"\nYouTube.Actions.MadeForKids.Yes=\"Oo, para sa mga bata\"\nYouTube.Actions.MadeForKids.No=\"Hindi para sa mga bata ito\"\nYouTube.Actions.AdditionalSettings=\"Mga karagdagang setting\"\nYouTube.Actions.Latency=\"Pagkakahuli\"\nYouTube.Actions.Latency.Low=\"Mababa\"\nYouTube.Actions.Latency.UltraLow=\"Napakababa\"\nYouTube.Actions.EnableAutoStart=\"Paganahin ang pagkusang pag-stastart\"\nYouTube.Actions.EnableAutoStop=\"Paganahin ang Pagkusang-hinto\"\nYouTube.Actions.AutoStartStop.TT=\"Isinasaad kung ang nakaiskedyul na broadcast na ito ay dapat na awtomatikong magsimula\"\nYouTube.Actions.EnableDVR=\"Paganahin ang DVR\"\nYouTube.Actions.360Video=\"Bidyong 360\"\nYouTube.Actions.ScheduleForLater=\"Ipalabas sa susunod\"\nYouTube.Actions.RememberSettings=\"Isaulo ang mga settings na ito\"\nYouTube.Actions.Create_Ready=\"Gumawa ng palabas\"\nYouTube.Actions.Create_GoLive=\"Gumawa ng palabas at umpisahang mag-stream\"\nYouTube.Actions.Choose_Ready=\"Pumili ng palabas\"\nYouTube.Actions.Choose_GoLive=\"Gumawa ng palabas at umpisahang mag-stream\"\nYouTube.Actions.Create_Schedule=\"Iskedyul ang palabas\"\nYouTube.Actions.Create_Schedule_Ready=\"Iskedyul at pumili ng palabas\"\nYouTube.Actions.Dashboard=\"Buksan ang Youtube Studio\"\nYouTube.Actions.Error.Title=\"Pagkakabigo sa paggawa ng Live na Palabas\"\nYouTube.Actions.Error.Text=\"Error sa pag-access sa YouTube '%1'.<br/>Makikita ang isang detalyadong paglalarawan ng error sa <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Error sa pag-access sa YouTube. Pakisuri ang iyong Internet koneksyon sa network o ang iyong access sa server sa YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Paggawa ng broadcast ay Error '%1'.<br/>Makikita ang detalyadong paglalarawan ng error sa <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Walang nagawang stream. Paki-link muli ang iyong account.\"\nYouTube.Actions.Error.YouTubeApi=\"May pagkabigo sa API ng Youtube. Basahin ang log file para sa karagdagang impormasyon.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Ang napiling broadcast ay hindi mahanap.\"\nYouTube.Actions.Error.FileMissing=\"Wala ang nakapiling file.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Nabigong buksan ang napiling file.\"\nYouTube.Actions.Error.FileTooLarge=\"Ang napiling file ay masyadong malaki (Hangganan: 2 MIB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Nabigo ang pag-tratransition ng palabas: %1<br/><br/>kung tuloy ang error <a href='https://studio.youtube.com/video/%2/livestreaming'>buksan ang palabas sa YouTube Studio</a> at subukan ito nang manu-mano.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Ang broadcast ay lumilipat sa yugto ng pagtest, maaari itong tumagal ng ilang oras. Pakisubukang muli sa loob ng 10-30 segundo.\"\nYouTube.Actions.EventsLoading=\"Naglulunan ng listahan ng mga events\"\nYouTube.Actions.EventCreated.Title=\"Nagawa ang Event\"\nYouTube.Actions.EventCreated.Text=\"Matagumpay na nagawa ang Event\"\nYouTube.Actions.Stream.ScheduledFor=\"Nakaiskedyul sa %1\"\nYouTube.Actions.Stream.Resume=\"Ipatuloy ang nahintong stream\"\nYouTube.Actions.Stream.YTStudio=\"Kusang ginawa ng Youtube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Gumagawa ng bagong Live na Palabas, sandali lang...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Kailangan ang manu-manong pagsisimula\"\nYouTube.Actions.AutoStartStreamingWarning=\"Hindi pinapagana ang kusang paguumpisa sa ebento na ito, pindutin lamang ang \\\"Maglive\\\" para simulan ang inyong broadcast.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Hindi ka maaring mag-reconnect.<br>Titigil ang stream mo at hindi kana live.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Hindi pinapagana ang Live streaming sa napiling Youtube channel.<br/><br/> Basahin ang <a href='https://www.youtube.com/features'>youtube.com/features</a> para sa karagdagang impormasyon.\"\nYouTube.Errors.livePermissionBlocked=\"Hindi available ang live streaming sa napiling Channel sa YouTube.<br/>Pakitandaan na maaaring tumagal nang hanggang 24 na oras bago maging available ang live streaming pagkatapos itong i-enable sa mga setting ng iyong channel.<br/><br/>Tingnan ang <a href='https://www.youtube.com/features'>youtube.com/features</a> para sa mga detalye.\"\nYouTube.Errors.errorExecutingTransition=\"Nabigo ang paglipat dahil sa isang backend na error. Pakisubukang muli sa loob ng ilang segundo.\"\nYouTube.Errors.errorStreamInactive=\"Hindi natatanggap ng Youtube ang data ng iyong stream.\"\nYouTube.Errors.invalidTransition=\"Di-wasto ang sinubukang paglipat. Maaaring ito ay dahil sa hindi natapos ng stream ang isang nakaraang transition. Mangyaring maghintay ng ilang segundo at subukang muli.\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Wala ang Custom na RTMP URL\"\n"
  },
  {
    "path": "frontend/data/locale/fr-FR.ini",
    "content": "Language=\"Français\"\nOK=\"Ok\"\nApply=\"Appliquer\"\nCancel=\"Annuler\"\nClose=\"Fermer\"\nSave=\"Enregistrer\"\nDiscard=\"Ignorer les modifications\"\nDisable=\"Désactiver\"\nYes=\"Oui\"\nNo=\"Non\"\nAdd=\"Ajouter\"\nRemove=\"Supprimer\"\nRename=\"Renommer\"\nInteract=\"Interagir\"\nFilters=\"Filtres\"\nProperties=\"Propriétés\"\nMoveUp=\"Déplacer vers le haut\"\nMoveDown=\"Déplacer vers le bas\"\nSettings=\"Paramètres\"\nDisplay=\"Moniteur\"\nName=\"Nom\"\nExit=\"Quitter OBS\"\nMixer=\"Mélangeur audio\"\nBrowse=\"Parcourir\"\nStereo=\"Stéréo\"\nDroppedFrames=\"Paquets perdus (réseau) : %1 (%2%)\"\nProjector.Open.Program=\"Ouvrir le programme du projecteur\"\nProjector.Open.Preview=\"Ouvrir la prévisualisation du projecteur\"\nProjector.Open.Scene=\"Ouvrir la scène du projecteur\"\nProjector.Open.Source=\"Ouvrir la source du projecteur\"\nProjector.Open.Multiview=\"Ouvrir la vue multiple\"\nProjector.Display=\"Moniteur : %1\"\nProjector.Window=\"Nouvelle fenêtre\"\nProjector.Title=\"Projecteur\"\nProjector.Title.Scene=\"Scène : %1\"\nProjector.Title.Source=\"Source : %1\"\nProjector.Title.Multiview=\"Vue multiple\"\nProjector.ResizeWindowToContent=\"Ajuster la fenêtre au contenu\"\nClear=\"Effacer\"\nRevert=\"Annuler\"\nShow=\"Afficher\"\nHide=\"Masquer\"\nUnhideAll=\"Tout afficher\"\nUntitled=\"Sans nom\"\nNew=\"Nouveau\"\nDuplicate=\"Dupliquer\"\nEnable=\"Activer\"\nDisableOSXVSync=\"Désactiver la synchronisation verticale de macOS (V-Sync)\"\nResetOSXVSyncOnExit=\"Réinitialiser la synchronisation verticale de macOS à la fermeture\"\nHighResourceUsage=\"L'encodeur est en surcharge ! Revoyez à la baisse vos paramètres vidéo ou utilisez un préréglage d'encodage plus rapide.\"\nQuickTransitions=\"Transitions rapides\"\nFadeToBlack=\"Fondu au noir\"\nLeft=\"À gauche\"\nRight=\"À droite\"\nTop=\"En haut\"\nBottom=\"En bas\"\nReset=\"Réinitialiser\"\nHours=\"Heures\"\nSeconds=\"Secondes\"\nDeprecated=\"Obsolète\"\nReplayBuffer=\"Tampon de relecture (Replay Buffer)\"\nImport=\"Importer\"\nExport=\"Exporter\"\nCopy=\"Copier\"\nPaste=\"Coller\"\nPasteReference=\"Coller (Référence)\"\nPasteDuplicate=\"Coller (Dupliquer)\"\nRemuxRecordings=\"Convertir des enregistrements\"\nNext=\"Suivant\"\nBack=\"Retour\"\nDefaults=\"Valeurs par défaut\"\nRestoreDefaults=\"Valeurs par défaut\"\nHideMixer=\"Cacher dans le mixeur\"\nTransitionOverride=\"Forcer une transition\"\nShowTransition=\"Transition à l'affichage\"\nHideTransition=\"Transition de masquage\"\nNone=\"Aucune\"\nStudioMode.Preview=\"Aperçu\"\nStudioMode.Program=\"Programme\"\nStudioMode.PreviewSceneName=\"Aperçu : %1\"\nStudioMode.ProgramSceneName=\"Programme : %1\"\nShowInMultiview=\"Afficher dans le multiview\"\nVerticalLayout=\"Disposition verticale\"\nGroup=\"Groupe\"\nDoNotShowAgain=\"Ne plus afficher\"\nDefault=\"(Défaut)\"\nCalculating=\"Calcul en cours...\"\nFullscreen=\"Plein écran\"\nWindowed=\"Fenêtré\"\nRefreshBrowser=\"Actualiser\"\nAspectRatio=\"Format d'image <b>%1:%2</b>\"\nLockVolume=\"Verrouiller le volume\"\nLogViewer=\"Voir les logs\"\nShowOnStartup=\"Afficher au démarrage\"\nOpenFile=\"Ouvrir le fichier\"\nAddScene=\"Ajouter une scène\"\nAddSource=\"Ajouter une source\"\nRemoveScene=\"Supprimer la scène sélectionnée\"\nRemoveSource=\"Supprimer les sources sélectionnées\"\nMoveSceneUp=\"Déplacer la scène vers le haut\"\nMoveSceneDown=\"Déplacer la scène vers le bas\"\nMoveSourceUp=\"Déplacer les sources vers le haut\"\nMoveSourceDown=\"Déplacer les sources vers le bas\"\nSourceProperties=\"Ouvrir les propriétés de la source\"\nSourceFilters=\"Ouvrir les filtres de la source\"\nMixerToolbarMenu=\"Menu du mixeur audio\"\nSceneFilters=\"Ouvrir les filtres de la scène\"\nList=\"Liste\"\nGrid=\"Grille\"\nAutomatic=\"Automatique\"\nComingSoon=\"Nouveauté à venir\"\nPluginsFailedToLoad.Title=\"Erreur de chargement du plugin\"\nPluginsFailedToLoad.Text=\"Les plugins OBS suivants n'ont pas pu être chargés :\\n\\n%1\\nVeuillez mettre à jour ou supprimer ces plugins.\"\nAlreadyRunning.Title=\"OBS est déjà en cours d'exécution\"\nAlreadyRunning.Text=\"OBS est déjà en cours d'exécution ! À moins que cela soit délibéré, veuillez fermer toute autre instance d'OBS (y compris celles minimisées dans la zone de notifications) avant d'en démarrer une nouvelle.\"\nAlreadyRunning.LaunchAnyway=\"Démarrer tout de même\"\nCrashHandling.Dialog.Title=\"Détection d'un crash d'OBS Studio\"\nCrashHandling.Labels.Text=\"OBS Studio ne s'est pas arrêté correctement. \\n\\nExécuter en mode sans échec (plugins tiers, scripts et WebSockets désactivés)?\"\nCrashHandling.Labels.PrivacyNotice=\"Vous pouvez également téléverser automatiquement le rapport de crash le plus récent vers OBSProject.<br /><br />Veuillez lire la <a href='https://obsproject.com/privacy-policy'>Politique de Confidentialité</a> et porter une attention particulière aux sections concernant le téléversement avant de téléverser des fichiers.\"\nCrashHandling.Checkbox.SendReport=\"J'ai lu la politique de confidentialité et j'accepte le téléversement.\"\nCrashHandling.Buttons.LaunchSafe=\"Démarrer en mode sans échec\"\nCrashHandling.Buttons.LaunchNormal=\"Démarrer en mode normal\"\nCrashHandling.Errors.UploadJSONError=\"Une erreur est survenue lors de la tentative de téléversement du rapport de crash le plus récent. Veuillez réessayer plus tard.\"\nCrashHandling.Errors.Title=\"Erreur de téléversement du rapport de crash\"\nSafeMode.Restart=\"Souhaitez-vous redémarrer OBS en mode sans échec (plugins tiers, scripts et WebSockets désactivés) ?\"\nSafeMode.RestartNormal=\"Souhaitez-vous redémarrer OBS normalement ?\"\nChromeOS.Title=\"Plateforme non prise en charge\"\nChromeOS.Text=\"OBS semble fonctionner dans un conteneur ChromeOS. Cette plateforme n'est pas prise en charge.\"\nWine.Title=\"Wine détecté\"\nWine.Text=\"L'exécution d'OBS dans Wine n'est pas prise en charge, et de nombreuses fonctionnalités comme la capture ou les sources de l'appareil ne fonctionneront pas ou seulement de manière limitée.<br><br>Il est recommandé d'exécuter une version native d'OBS à la place, par exemple <a href='https://flathub.org/apps/details/com.obsproject.Studio'>notre version Flatpak</a> ou les packages de votre système d'exploitation.\"\nDockCloseWarning.Title=\"Fermeture d'une fenêtre dockable\"\nDockCloseWarning.Text=\"Vous venez de fermer une fenêtre dockable. Si vous souhaitez l'afficher à nouveau, utilisez le menu Docks dans la barre de menus.\"\nExtraBrowsers=\"Docks Internet personnalisés\"\nExtraBrowsers.Info=\"Ajoutez des docks en leur donnant un nom et une URL, puis cliquez sur Appliquer ou Fermer pour les ouvrir. Vous pouvez en ajouter ou en supprimer à tout moment.\"\nExtraBrowsers.DockName=\"Nom du dock\"\nAuth.Authing.Title=\"Connexion en cours ...\"\nAuth.Authing.Text=\"Connexion sur %1 en cours, veuillez patienter ...\"\nAuth.AuthFailure.Title=\"Échec d'authentification\"\nAuth.AuthFailure.Text=\"Échec de l'authentification avec %1 :\\n\\n%2 : %3\"\nAuth.InvalidScope.Title=\"Authentification requise\"\nAuth.InvalidScope.Text=\"Les exigences d'authentification pour %1 ont changé. Certaines fonctionnalités peuvent être indisponibles.\"\nAuth.LoadingChannel.Title=\"Chargement des informations de chaîne...\"\nAuth.LoadingChannel.Text=\"Chargement des informations de la chaîne %1, veuillez patienter...\"\nAuth.LoadingChannel.Error=\"Impossible de récupérer les informations de la chaîne.\"\nAuth.ChannelFailure.Title=\"Échec du chargement de la chaîne\"\nAuth.ChannelFailure.Text=\"Échec du chargement des informations de la chaîne pour %1\\n\\n%2 : %3\"\nAuth.StreamInfo=\"Informations de la diffusion\"\nTwitchAuth.Stats=\"Statistiques Twitch\"\nTwitchAuth.Feed=\"Flux d'activité Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Impossible de trouver la clé de stream\"\nTwitchAuth.TwoFactorFail.Text=\"OBS n'a pas pu se connecter à votre compte Twitch. Veuillez vous assurer que l'authentification à deux facteurs est configurée dans vos <a href='https://www.twitch.tv/settings/security'>paramètres de sécurité Twitch</a> car cela est nécessaire pour lancer un stream.\"\nRestreamAuth.Channels=\"Chaînes Restream\"\nCopy.Filters=\"Copier les filtres\"\nPaste.Filters=\"Coller les filtres\"\nBrowserPanelInit.Title=\"Initialisation du Navigateur ...\"\nBrowserPanelInit.Text=\"Initialisation du Navigateur, veuillez patienter ...\"\nBandwidthTest.Region=\"Région\"\nBandwidthTest.Region.US=\"États-Unis\"\nBandwidthTest.Region.Asia=\"Asie\"\nBandwidthTest.Region.Other=\"Autre\"\nBasic.AutoConfig=\"Assistant de Configuration automatique\"\nBasic.AutoConfig.ApplySettings=\"Appliquer les paramètres\"\nBasic.AutoConfig.StartPage=\"Informations sur l’utilisation\"\nBasic.AutoConfig.StartPage.SubTitle=\"Sélectionnez ci-dessous l'usage-type correspondant à vos besoins\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimiser pour la diffusion en direct, l’enregistrement est secondaire\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimiser pour l’enregistrement, je ne diffuse pas\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Je n'utiliserai que la caméra virtuelle\"\nBasic.AutoConfig.VideoPage=\"Paramètres vidéo\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Précisez les réglages vidéos que vous souhaitez utiliser\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Garder tel quel (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Écran %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Garder tel quel (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ou 30, mais préférer 60 si possible\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ou 30, mais favoriser une résolution élevée\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Remarque : la résolution (de base) du canevas n'est pas nécessairement la même que la résolution avec laquelle vous allez diffuser (stream) ou enregistrer. La résolution actuelle de stream/enregistrement peut être réduite à partir de la résolution du canevas afin de diminuer l'utilisation des ressources et de la bande passante.\"\nBasic.AutoConfig.StreamPage=\"Informations de flux\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Entrez vos informations de stream\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Lier un compte (recommandé)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Déconnecter le compte\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Déconnecter le compte ?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Cette modification s'appliquera immédiatement. Êtes-vous sûr de vouloir déconnecter votre compte ?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Récupérer la clé de stream\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"En savoir plus\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Utiliser la clé de stream\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Utiliser une clé de stream (avancé)\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Afficher tout...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Personnalisé...\"\nBasic.AutoConfig.StreamPage.Server=\"Serveur\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Clé de stream\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST : entrez la clé de chiffrement.\\nRTMP : entrez la clé fournie par le service.\\nSRT : entrez le streamid si le service en utilise un.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Clé de l'encodeur\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Jeton du porteur\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Compte connecté\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Test de la bande passantes pour estimer le débit (peut prendre quelques minutes)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Préférer l’encodage matériel\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"L'encodage matériel minimise l'utilisation du processeur (CPU), mais peut nécessiter un débit vidéo plus élevé pour obtenir une niveau de qualité équivalent.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Avertissement de diffusion\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Le test de bande passante va diffuser un signal vidéo aléatoire sans son sur votre chaîne. Si vous en avez la possibilité, il est recommandé de désactiver temporairement l'enregistrement des diffusions et de configurer le stream en privé jusqu'à ce que le test soit terminé. Souhaitez-vous continuer ?\"\nBasic.AutoConfig.TestPage=\"Résultat final\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Le programme exécute maintenant une série de tests pour estimer vos paramètres idéaux\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Tests terminés\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Test de la bande passante en cours, cela peut prendre quelques minutes...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Aucune sortie pour le protocole de ce service n'a été trouvé\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Connexion à : %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Impossible de se connecter aux serveurs, veuillez vérifier votre connexion internet et réessayez.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Test de la bande passante pour : %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Test de l'encodeur de streaming, veuillez patienter un instant...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Test de l'encodeur d'enregistrement, veuillez patienter un instant...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"L'encodeur n'a pas pu être démarré\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Test %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Encodeur de streaming\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Encodeur d'enregistrement\"\nBasic.AutoConfig.TestPage.Result.Header=\"Le programme vous recommande ces paramètres :\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Pour utiliser ces paramètres, cliquez sur « Appliquer les paramètres ». Pour reconfigurer l’Assistant et essayer de nouveau, cliquez sur « Précédent ». Pour configurer les paramètres vous-même, cliquez sur « Annuler » et rendez-vous dans les Paramètres.\"\nBasic.AutoConfig.Info=\"L'assistant de configuration automatique déterminera les meilleurs paramètres en fonction des spécifications de votre ordinateur et du débit internet.\"\nBasic.AutoConfig.RunAnytime=\"Cet assistant peut être lancé à tout moment en allant dans le menu Outils.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Résolution (A l'échelle) du stream\"\nBasic.Stats=\"Statistiques\"\nBasic.Stats.CPUUsage=\"Utilisation du processeur\"\nBasic.Stats.HDDSpaceAvailable=\"Espace disque disponible\"\nBasic.Stats.MemoryUsage=\"Utilisation de la mémoire\"\nBasic.Stats.AverageTimeToRender=\"Temps de rendu (moyenne)\"\nBasic.Stats.SkippedFrames=\"Images ignorées (à cause d'un retard d'encodage)\"\nBasic.Stats.MissedFrames=\"Images manquées (à cause d'un retard de rendu)\"\nBasic.Stats.Output.Stream=\"Diffusion (Streaming)\"\nBasic.Stats.Output.Recording=\"Enregistrement\"\nBasic.Stats.Status=\"État\"\nBasic.Stats.Status.Recording=\"Enregistrement en cours\"\nBasic.Stats.Status.Live=\"EN DIRECT\"\nBasic.Stats.Status.Reconnecting=\"Reconnexion en cours\"\nBasic.Stats.Status.Inactive=\"Inactif\"\nBasic.Stats.Status.Active=\"Actif\"\nBasic.Stats.DroppedFrames=\"Trames perdues (réseau)\"\nBasic.Stats.MegabytesSent=\"Données transmises (total)\"\nBasic.Stats.Bitrate=\"Débit\"\nBasic.Stats.DiskFullIn=\"Disque plein dans (env.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 Heures, %2 Minutes\"\nBasic.Stats.ResetStats=\"Réinitialiser les statistiques\"\nResetUIWarning.Title=\"Êtes-vous sûr de vouloir réinitialiser l'interface graphique ?\"\nResetUIWarning.Text=\"Réinitialiser l'interface masquera vos docks supplémentaires. Vous devrez les réactiver depuis le menu Docks si vous voulez qu'ils soient visibles à nouveau.\\n\\nÊtes-vous sûr de vouloir réinitialiser l'interface ?\"\nUpdater.Title=\"Nouvelle mise à jour disponible\"\nUpdater.Text=\"Une nouvelle mise à jour est disponible :\"\nUpdater.UpdateNow=\"Mettre à jour maintenant\"\nUpdater.RemindMeLater=\"Me le rappeler ultérieurement\"\nUpdater.Skip=\"Ignorer la version\"\nUpdater.NoUpdatesAvailable.Title=\"Aucune mise à jour disponible\"\nUpdater.NoUpdatesAvailable.Text=\"Aucune mise à jour n’est actuellement disponible\"\nUpdater.BranchNotFound.Title=\"Canal de mise à jour supprimé\"\nUpdater.BranchNotFound.Text=\"Le canal de mise à jour sélectionné n'est plus disponible, OBS a été réinitialisé à la valeur par défaut.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Vérification de l'intégrité indisponible\"\nUpdater.RepairButUpdatesAvailable.Text=\"La vérification de l'intégrité des fichiers n'est possible que pour la dernière version disponible. Utilisez Aide → Vérifier les mises à jour pour vérifier et mettre à jour votre installation d'OBS.\"\nUpdater.RepairConfirm.Title=\"Confirmer la vérification d'intégrité\"\nUpdater.RepairConfirm.Text=\"La vérification d'intégrité va analyser votre installation d'OBS pour chercher des corruptions et retélécharger les fichiers cassés/modifiés. Cela peut prendre un moment.\\n\\nVoulez-vous continuer ?\"\nUpdater.FailedToLaunch=\"Impossible de démarrer la mise à jour\"\nQuickTransitions.SwapScenes=\"Échanger les scènes d'aperçu et de sortie après la transition\"\nQuickTransitions.SwapScenesTT=\"Échange les scènes d'aperçu et de sortie après la transition (si la scène d'origine de la sortie existe toujours).\\nCela n'annulera pas les modifications qui auront pu être faites sur la scène d'origine de la sortie.\"\nQuickTransitions.DuplicateScene=\"Dupliquer la scène\"\nQuickTransitions.DuplicateSceneTT=\"Lors de l'édition d'une même scène, permet la modification des transitions et de la visibilité des sources sans modifier la sortie.\\nPour modifier les propriétés des sources sans modifier la sortie, activez \\\"Dupliquer les sources\\\".\\nModifier ce paramètre réinitialisera la scène actuelle de sortie (si elle existe encore).\"\nQuickTransitions.EditProperties=\"Dupliquer les sources\"\nQuickTransitions.EditPropertiesTT=\"Lors de l'édition d'une même scène, permet la modification des propriétés des sources sans modifier la sortie.\\nCela ne peut être utilisé que si \\\"Dupliquer la scène\\\" est activé.\\nCertaines sources (telles que les captures ou sources de média) ne supportent pas cela et ne peuvent être modifiées séparément.\\nModifier ce paramètre réinitialisera la scène actuelle de sortie (si elle existe encore).\\n\\nAttention : puisque les sources seront dupliquées, cela peut nécessiter des ressources système ou vidéo supplémentaires.\"\nQuickTransitions.HotkeyName=\"Transition rapide : %1\"\nBasic.AddTransition=\"Ajouter une transition configurable\"\nBasic.RemoveTransition=\"Supprimer une transition configurable\"\nBasic.TransitionProperties=\"Propriétés de la transition\"\nBasic.SceneTransitions=\"Transition de scènes\"\nBasic.TransitionDuration=\"Durée\"\nBasic.TogglePreviewProgramMode=\"Mode studio\"\nBasic.EnablePreviewProgramMode=\"Activer le mode studio\"\nBasic.DisablePreviewProgramMode=\"Désactiver le mode studio\"\nUndo.Undo=\"Annuler\"\nUndo.Redo=\"Rétablir\"\nUndo.Add=\"Ajouter '%1'\"\nUndo.Delete=\"Supprimer '%1'\"\nUndo.Rename=\"Renommer '%1'\"\nUndo.SceneCollection.Switch=\"Basculer vers '%1'\"\nUndo.Item.Undo=\"Annuler %1\"\nUndo.Item.Redo=\"Rétablir %1\"\nUndo.Sources.Multi=\"Supprimer %1 sources\"\nUndo.Filters=\"les modifications du filtre pour '%1'\"\nUndo.Filters.Paste.Single=\"Copier le filtre de '%1' vers '%2'\"\nUndo.Filters.Paste.Multiple=\"Copier les filtres de '%1' vers '%2'\"\nUndo.Transform=\"Transformer la(les) source(s) dans '%1'\"\nUndo.Transform.Paste=\"Coller la transformation dans '%1'\"\nUndo.Transform.Rotate=\"Rotation dans '%1'\"\nUndo.Transform.Reset=\"Réinitialisation de la transformation dans '%1'\"\nUndo.Transform.HFlip=\"Retournement horizontal dans '%1'\"\nUndo.Transform.VFlip=\"Retournement vertical dans '%1'\"\nUndo.Transform.FitToScreen=\"Ajuster à l'écran dans '%1'\"\nUndo.Transform.StretchToScreen=\"Étirer à l'écran dans '%1'\"\nUndo.Transform.Center=\"Centrer sur l'écran dans '%1'\"\nUndo.Transform.VCenter=\"Centrer verticalement à l'écran dans '%1'\"\nUndo.Transform.HCenter=\"Centrer horizontalement à l'écran dans '%1'\"\nUndo.Volume.Change=\"Changement de volume sur '%1'\"\nUndo.Volume.Mute=\"Couper l'audio de '%1'\"\nUndo.Volume.Unmute=\"Activer l'audio de '%1'\"\nUndo.Balance.Change=\"Changer la balance audio sur '%1'\"\nUndo.SyncOffset.Change=\"Modifier le décalage de la synchronisation audio sur '%1'\"\nUndo.MonitoringType.Change=\"Modifier le Monitoring audio sur \\\"%1\\\"\"\nUndo.Mixers.Change=\"Modifier les mélangeurs audio sur '%1'\"\nUndo.ForceMono.On=\"Activer le son mono forcé sur '%1'\"\nUndo.ForceMono.Off=\"Désactiver le son mono forcé sur '%1'\"\nUndo.Properties=\"Changement de propriété sur '%1'\"\nUndo.Scene.Duplicate=\"Dupliquer la scène '%1'\"\nUndo.ShowTransition=\"Afficher la transition pour '%1'\"\nUndo.HideTransition=\"Cacher la transition pour '%1'\"\nUndo.ShowSceneItem=\"Afficher '%1' dans '%2'\"\nUndo.HideSceneItem=\"Masquer '%1' dans '%2'\"\nUndo.ReorderSources=\"Réorganiser les sources dans '%1'\"\nUndo.MoveUp=\"Monter '%1' d'un cran dans '%2'\"\nUndo.MoveDown=\"Descendre '%1' d'un cran dans '%2'\"\nUndo.MoveToTop=\"Déplacer '%1' tout en haut dans '%2'\"\nUndo.MoveToBottom=\"Déplacer '%1' tout en bas dans '%2'\"\nUndo.PasteSource=\"Coller les sources dans '%1'\"\nUndo.PasteSourceRef=\"Coller les références de sources dans '%1'\"\nUndo.GroupItems=\"Grouper les éléments dans '%1'\"\nTransitionNameDlg.Text=\"Veuillez entrer le nom de la transition\"\nTransitionNameDlg.Title=\"Nom de la transition\"\nTitleBar.SafeMode=\"MODE SANS ECHEC\"\nTitleBar.PortableMode=\"Mode portable\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Scènes\"\nNameExists.Title=\"Ce nom existe déjà\"\nNameExists.Text=\"Ce nom est déjà utilisé.\"\nNoNameEntered.Title=\"Veuillez saisir un nom valide\"\nNoNameEntered.Text=\"Vous ne pouvez pas utiliser de noms vides.\"\nConfirmStart.Title=\"Démarrer le stream ?\"\nConfirmStart.Text=\"Êtes-vous sûr de vouloir démarrer le stream ?\"\nConfirmStop.Title=\"Arrêter le stream ?\"\nConfirmStop.Text=\"Êtes-vous sûr de vouloir arrêter le stream ?\"\nConfirmStopRecord.Title=\"Arrêter l'enregistrement ?\"\nConfirmStopRecord.Text=\"Êtes-vous certain de vouloir arrêter l'enregistrement ?\"\nConfirmBWTest.Title=\"Démarrer le test de bande passante ?\"\nConfirmBWTest.Text=\"Vous avez configuré OBS en mode \\\"test de bande passante\\\". Ce mode permet de tester le réseau sans que votre chaîne ne soit en direct. Une fois que vous aurez effectué les tests, vous devrez le désactiver afin que les spectateurs puissent voir votre stream.\\n\\nVoulez-vous continuer ?\"\nConfirmExit.Title=\"Sorties Actives\"\nConfirmExit.Text=\"OBS est encore en cours d’utilisation. Tous les flux/enregistrements seront arrêtés.\"\nConfirmRemove.Title=\"Confirmer la suppression\"\nConfirmRemove.Text=\"Êtes-vous sûr de vouloir supprimer '%1' ?\"\nConfirmRemove.TextMultiple=\"Voulez-vous vraiment supprimer %1 éléments ?\"\nConfirmReset.Title=\"Réinitialiser les propriétés\"\nConfirmReset.Text=\"Êtes-vous sûr de vouloir réinitialiser les propriétés actuelles à leurs valeurs par défaut ?\"\nOutput.StartStreamFailed=\"Impossible de démarrer le streaming\"\nOutput.StartRecordingFailed=\"Impossible de démarrer l'enregistrement\"\nOutput.StartReplayFailed=\"Impossible de démarrer le tampon de relecture\"\nOutput.StartVirtualCamFailed=\"Échec du démarrage de la caméra virtuelle\"\nOutput.StartFailedGeneric=\"Le démarrage de la sortie a échoué. Veuillez consulter le journal pour plus de détails.\\n\\nRemarque : si vous utilisez les encodeurs NVENC ou AMD, assurez-vous que vos pilotes vidéo soient à jour.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Impossible d'enregistrer des replays quand l'enregistrement est en pause\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Attention : les replays ne peuvent pas être enregistrés lorsque l'enregistrement est en pause.\"\nOutput.ConnectFail.Title=\"Échec de la connexion\"\nOutput.ConnectFail.BadPath=\"Adresse de connexion ou chemin invalide. Veuillez vérifier vos paramètres afin de confirmer leur validité.\"\nOutput.ConnectFail.ConnectFailed=\"Échec de la connexion au serveur\"\nOutput.ConnectFail.InvalidStream=\"Impossible d'accéder à la chaîne ou à la clé de stream spécifiée, veuillez revérifier votre clé de stream. Si celle-ci est correcte, il y a peut-être un problème de connexion au serveur.\"\nOutput.ConnectFail.HdrDisabled=\"Le format HDR est désactivé pour le moment pour cette Sortie.\"\nOutput.ConnectFail.Error=\"Une erreur inattendue s'est produite en essayant de se connecter au serveur. Plus d'informations dans le fichier journal.\"\nOutput.ConnectFail.Disconnected=\"Déconnecté du serveur.\"\nOutput.StreamEncodeError.Title=\"Erreur d'encodage\"\nOutput.StreamEncodeError.Msg=\"L'encodeur a rencontré une erreur pendant la diffusion.\"\nOutput.StreamEncodeError.Msg.LastError=\"Une erreur d'encodeur s'est produite durant le streaming :<br><br>%1\"\nOutput.RecordFail.Title=\"Impossible de démarrer l'enregistrement\"\nOutput.RecordFail.Unsupported=\"Le format de sortie n'est pas pris en charge ou ne supporte qu'une piste audio. Veuillez vérifier vos paramètres et réessayez.\"\nOutput.RecordNoSpace.Title=\"Espace disque insuffisant\"\nOutput.RecordNoSpace.Msg=\"Il n'y a pas suffisamment d'espace disque pour poursuivre l'enregistrement.\"\nOutput.RecordError.Title=\"Erreur d'enregistrement\"\nOutput.RecordError.Msg=\"Une erreur non spécifiée s'est produite lors de l'enregistrement.\"\nOutput.RecordError.EncodeErrorMsg=\"L'encodeur a rencontré une erreur pendant l'enregistrement.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Une erreur d'encodeur s'est produite durant l'enregistrement :<br><br>%1\"\nOutput.BadPath.Title=\"Chemin d'accès au fichier incorrect\"\nOutput.BadPath.Text=\"Le chemin d'enregistrement configuré n'a pas pu être ouvert. Veuillez vérifier votre chemin d'enregistrement dans Réglages → Sortie → Enregistrement.\"\nOutput.NoBroadcast.Title=\"Aucune diffusion configurée\"\nOutput.NoBroadcast.Text=\"Vous devez configurer une diffusion avant de pouvoir commencer la diffusion.\"\nOutput.BroadcastStartFailed=\"Échec du démarrage de la diffusion.\"\nOutput.BroadcastStopFailed=\"Échec de l'arrêt de la diffusion.\"\nLogUploadDialog.Title=\"Téléversement du fichier journal d'OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Veuillez lire la <a href='https://obsproject.com/privacy-policy'>Politique de Confidentialité</a> et ses sections concernant le téléversement de fichier avant de téléverser.\"\nLogUploadDialog.Labels.Progress=\"Téléversement du journal en cours. Veuillez patienter...\"\nLogUploadDialog.Labels.Description.AppLog=\"Votre fichier journal a été téléversé. Vous pouvez à présent partager l'URL à des fins de débogage ou de support.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Votre rapport de crash a été téléversé. Vous pouvez à présent partager l'URL à des fins de débogage.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Téléverser\"\nLogUploadDialog.Buttons.CopyURL=\"Copier l'URL du journal\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analyser le fichier journal\"\nLogUploadDialog.Buttons.RetryButton=\"Réessayer\"\nLogUploadDialog.Errors.Template=\"Une erreur est survenue lors de la tentative de téléversement du fichier :\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Aucun fichier à téléverser n'a été trouvé ou le fichier était vide.\"\nRemux.SourceFile=\"Enregistrement OBS\"\nRemux.TargetFile=\"Fichier cible\"\nRemux.Remux=\"Convertir\"\nRemux.Stop=\"Arrêter la conversion\"\nRemux.ClearFinished=\"Effacer les éléments terminés\"\nRemux.ClearAll=\"Effacer tous les éléments\"\nRemux.OBSRecording=\"Enregistrement OBS\"\nRemux.FinishedTitle=\"Conversion terminée\"\nRemux.Finished=\"L'enregistrement a été converti\"\nRemux.FinishedError=\"L'enregistrement a été converti, mais le fichier peut être incomplet\"\nRemux.SelectRecording=\"Choisir l'enregistrement d'OBS ...\"\nRemux.SelectTarget=\"Choisir le fichier de Destination ...\"\nRemux.FileExistsTitle=\"Fichier cible déjà existant\"\nRemux.FileExists=\"Les fichiers suivants existent déjà, voulez-vous les remplacer ?\"\nRemux.ExitUnfinishedTitle=\"Conversion en cours\"\nRemux.ExitUnfinished=\"La conversion n'est pas terminée, arrêter maintenant peut rendre le fichier final inutilisable.\\nVoulez-vous vraiment arrêter la conversion ?\"\nRemux.HelpText=\"Déposez des fichiers dans cette fenêtre pour les convertir, ou sélectionnez une cellule « Enregistrement OBS » vide pour rechercher un fichier.\"\nRemux.NoFilesAddedTitle=\"Aucun fichier de remultiplexage ajouté\"\nRemux.NoFilesAdded=\"Aucun fichier n'est ajouté au remultiplexage. Déposez un dossier contenant un ou plusieurs fichiers vidéo.\"\nMissingFiles=\"Fichiers manquants\"\nMissingFiles.MissingFile=\"Fichier manquant\"\nMissingFiles.NewFile=\"Nouveau fichier\"\nMissingFiles.HelpText=\"Certains fichiers sont manquants depuis votre dernière utilisation d'OBS.\"\nMissingFiles.Clear=\"<vide>\"\nMissingFiles.NumFound=\"%1 sur %2 trouvé(s)\"\nMissingFiles.Search=\"Rechercher dans le dossier...\"\nMissingFiles.SelectFile=\"Sélectionner un fichier...\"\nMissingFiles.SelectDir=\"Sélectionner le dossier dans lequel rechercher\"\nMissingFiles.State=\"État\"\nMissingFiles.Missing=\"Manquant\"\nMissingFiles.Replaced=\"Remplacé\"\nMissingFiles.Cleared=\"Effacé\"\nMissingFiles.Found=\"Trouvé\"\nMissingFiles.AutoSearch=\"Des correspondances de fichier supplémentaires ont été trouvées\"\nMissingFiles.AutoSearchText=\"OBS a trouvé des correspondances supplémentaires pour les fichiers manquants dans ce dossier. Voulez-vous les ajouter ?\"\nMissingFiles.NoMissing.Title=\"Vérification des fichiers manquants\"\nMissingFiles.NoMissing.Text=\"Aucun fichier ne semble manquer.\"\nMacPermissions.MenuAction=\"Vérifiez les permissions de l'application...\"\nMacPermissions.Title=\"Vérifiez les permissions de l'application\"\nMacPermissions.Description=\"OBS Studio a besoin de votre permission pour pouvoir proposer certaines fonctionnalités. Il est recommandé d'accorder ces permissions, mais elles ne sont pas requises pour utiliser l'application. Vous pourrez toujours les activer ultérieurement.\"\nMacPermissions.Description.OpenDialog=\"Vous pouvez réouvrir ce dialogue via le menu OBS Studio\"\nMacPermissions.AccessGranted=\"Accès autorisé\"\nMacPermissions.RequestAccess=\"Demandez l'accès\"\nMacPermissions.OpenPreferences=\"Ouvrir les préférences de %1\"\nMacPermissions.Item.ScreenRecording=\"Captures d'écran\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS a besoin de cette permission pour pouvoir capturer votre écran.\"\nMacPermissions.Item.Camera=\"Caméra\"\nMacPermissions.Item.Camera.Details=\"Cette autorisation est nécessaire pour capturer le contenu d'une webcam ou d'une carte de capture.\"\nMacPermissions.Item.Microphone.Details=\"OBS a besoin de cette permission si vous voulez capturer votre microphone ou un périphérique audio externe.\"\nMacPermissions.Item.InputMonitoring=\"Surveillance de l'entrée\"\nMacPermissions.Item.InputMonitoring.Details=\"Cette autorisation est requise pour que les raccourcis clavier fonctionnent pendant que OBS est en arrière-plan.\"\nMacPermissions.Item.Accessibility=\"Accessibilité\"\nMacPermissions.Item.Accessibility.Details=\"Sur une ancienne installation, OBS est possiblement listé dans \\\"accessibilité\\\" au lieu de \\\"surveillance de l'entrée\\\".\"\nMacPermissions.Continue=\"Continuer\"\nSourceLeak.Title=\"Erreur de nettoyage des sources\"\nSourceLeak.Text=\"Un problème est survenu lors du changement de collection de scènes et certaines sources n'ont pas pu être déchargées. Ce problème est généralement causé par des modules dont les ressources ne sont pas correctement publiées. Veuillez vous assurer que tous les modules que vous utilisez sont à jour.\\n\\nOBS Studio va maintenant se fermer pour éviter tout risque de corruption des données.\"\nBasic.DesktopDevice1=\"Audio du bureau\"\nBasic.DesktopDevice2=\"Audio du bureau 2\"\nBasic.Scene=\"Scène\"\nBasic.DisplayCapture=\"Capture d'écran\"\nBasic.Main.PreviewConextMenu.Enable=\"Activer l'aperçu\"\nBasic.Main.Preview.Disable=\"Désactiver l'aperçu\"\nScaleFiltering=\"Filtre de mise à l'échelle\"\nScaleFiltering.Bilinear=\"Bilinéaire\"\nScaleFiltering.Bicubic=\"Bicubique\"\nScaleFiltering.Area=\"Zone\"\nBlendingMethod=\"Méthode de fusion\"\nBlendingMethod.Default=\"Par défaut\"\nBlendingMethod.SrgbOff=\"SRGB désactivé\"\nBlendingMode=\"Mode de fusion\"\nBlendingMode.Additive=\"Ajouter\"\nBlendingMode.Subtract=\"Soustraction\"\nBlendingMode.Screen=\"Superposition\"\nBlendingMode.Multiply=\"Produit\"\nBlendingMode.Lighten=\"Éclaircir\"\nBlendingMode.Darken=\"Obscurcir\"\nDeinterlacing=\"Désentrelacement\"\nDeinterlacing.Discard=\"Abandonner\"\nDeinterlacing.TopFieldFirst=\"Champ du haut prioritaire\"\nDeinterlacing.BottomFieldFirst=\"Champ du bas prioritaire\"\nVolControl.SliderUnmuted=\"Curseur de volume pour \\\"%1\\\" :\"\nVolControl.SliderMuted=\"Curseur de volume pour \\\"%1\\\": (actuellement muet)\"\nVolControl.Mute=\"Muet '%1'\"\nVolControl.Properties=\"Propriétés pour '%1'\"\nVolControl.UnassignedWarning.Title=\"Source audio non assignée\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" n'est assignée à aucune piste audio et sera inaudible dans les diffusions ou enregistrements.\\n\\nPour assigner une source audio à une piste, ouvrez les propriétés audio avancées via le menu du clic droit ou l'engrenage dans le dock mélangeur audio.\"\nBasic.Main.AddSceneDlg.Title=\"Ajouter une scène\"\nBasic.Main.AddSceneDlg.Text=\"Veuillez entrer le nom de la scène\"\nBasic.Main.DefaultSceneName.Text=\"Scène %1\"\nBasic.Main.AddSceneCollection.Title=\"Ajouter une collection de scènes\"\nBasic.Main.AddSceneCollection.Text=\"Veuillez entrer le nom de la collection de scènes\"\nBasic.Main.RenameSceneCollection.Title=\"Renommer la collection de scènes\"\nBasic.Main.RemigrateSceneCollection.Title=\"Mettre à jour la résolution de la collection de scènes\"\nBasic.Main.RemigrateSceneCollection.Text=\"Voulez-vous mettre à jour la résolution de la collection de scènes \\\"%1\\\" pour qu'elle corresponde à la résolution du canevas du profil actuelle de %2x%3 ?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Impossible de mettre à jour la résolution de la collection de scènes pendant qu'un flux est actif \"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Erreur lors de la mise à jour de la résolution de la collection de scènes : La résolution d'origine est inconnue.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Réinitialisation impossible : La modification de la résolution d'OBS a rencontré une erreur.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Réinitialisation impossible : La résolution actuelle est déjà la résolution d’origine de la collection de scènes.\"\nAddProfile.Title=\"Ajouter un profil\"\nAddProfile.Text=\"Veuillez entrer le nom du profil\"\nAddProfile.WizardCheckbox=\"Afficher l'assistant de configuration automatique\"\nRenameProfile.Title=\"Renommer le profil\"\nBasic.Main.MixerRename.Title=\"Renommer la source audio\"\nBasic.Main.MixerRename.Text=\"Veuillez indiquer le nom de la source audio\"\nBasic.Main.PreviewDisabled=\"L'aperçu est actuellement désactivé\"\nBasic.SourceSelect=\"Créer/Sélectionner la source\"\nBasic.SourceSelect.CreateNew=\"Créer une nouvelle source\"\nBasic.SourceSelect.AddExisting=\"Ajouter une source existante\"\nBasic.SourceSelect.AddVisible=\"Rendre la source visible\"\nBasic.Main.Sources.Visibility=\"Visibilité\"\nBasic.Main.Sources.VisibilityDescription=\"Contrôle la visibilité de \\\"%1\\\" sur le canevas\"\nBasic.Main.Sources.Lock=\"Verrouillage\"\nBasic.Main.Sources.LockDescription=\"Verrouille la position et la taille de \\\"%1\\\" sur le canevas\"\nBasic.PropertiesWindow=\"Propriétés pour '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (sélection automatique : %2)\"\nBasic.PropertiesWindow.SelectColor=\"Sélectionner une couleur\"\nBasic.PropertiesWindow.SelectFont=\"Sélectionner la police\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Choisir une police\"\nBasic.PropertiesWindow.ConfirmTitle=\"Paramètres modifiés\"\nBasic.PropertiesWindow.Confirm=\"Des modifications n'ont pas été enregistrées. Voulez-vous les conserver ?\"\nBasic.PropertiesWindow.NoProperties=\"Aucune propriété disponible\"\nBasic.PropertiesWindow.AddFiles=\"Ajouter des fichiers\"\nBasic.PropertiesWindow.AddDir=\"Ajouter un répertoire\"\nBasic.PropertiesWindow.AddURL=\"Ajouter un chemin/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Ajouter un répertoire à '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Ajouter des fichiers à '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Ajouter une entrée à '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Éditer l'entrée de '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Valeurs de FPS simples\"\nBasic.PropertiesView.FPS.Rational=\"Valeurs de FPS rationnelles\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Gammes de FPS valides:\"\nBasic.PropertiesView.UrlButton.Text=\"Ouvrir ce lien dans votre navigateur par défaut ?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Ouvrir l'URL\"\nBasic.InteractionWindow=\"Intéragir avec '%1'\"\nBasic.StatusBar.Reconnecting=\"Déconnecté, reconnexion dans %2 seconde(s) (tentative n° %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Tentative de reconnexion... (tentative n° %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Reconnexion réussie\"\nBasic.StatusBar.Delay=\"Retard (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Retard (commence dans %1 s)\"\nBasic.StatusBar.DelayStoppingIn=\"Retard (s'arrête dans %1 s)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Retard (s'arrête dans %1 s, commence dans %2 s)\"\nBasic.StatusBar.RecordingSavedTo=\"Enregistrement sauvegardé dans '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Tampon de relecture sauvegardé dans '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Capture d'écran sauvegardée dans '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Enregistrement automatiquement converti vers '%1'\"\nBasic.Filters=\"Filtres\"\nBasic.Filters.AsyncFilters=\"Filtres audio/vidéo\"\nBasic.Filters.AudioFilters=\"Filtres audio\"\nBasic.Filters.EffectFilters=\"Filtres d'effets\"\nBasic.Filters.Title=\"Filtres pour '%1'\"\nBasic.Filters.AddFilter.Title=\"Nom du filtre\"\nBasic.Filters.AddFilter.Text=\"Veuillez préciser le nom du filtre\"\nBasic.TransformWindow=\"Transformation de l’élément de scène\"\nBasic.TransformWindow.Size=\"Taille\"\nBasic.TransformWindow.Width=\"Largeur\"\nBasic.TransformWindow.Height=\"Hauteur\"\nBasic.TransformWindow.Alignment=\"Position de l'alignement\"\nBasic.TransformWindow.BoundsType=\"Type de Cadre\"\nBasic.TransformWindow.BoundsAlignment=\"Alignement à l'intérieur du Cadre\"\nBasic.TransformWindow.Bounds=\"Taille du Cadre\"\nBasic.TransformWindow.BoundsWidth=\"Largeur du cadre\"\nBasic.TransformWindow.BoundsHeight=\"Hauteur du cadre\"\nBasic.TransformWindow.CropToBounds=\"Recadrer dans le cadre de délimitation\"\nBasic.TransformWindow.Crop=\"Rogner\"\nBasic.TransformWindow.CropLeft=\"Rognage à gauche\"\nBasic.TransformWindow.CropRight=\"Rognage à droite\"\nBasic.TransformWindow.CropTop=\"Rognage en haut\"\nBasic.TransformWindow.CropBottom=\"Rognage en bas\"\nBasic.TransformWindow.Alignment.TopLeft=\"En haut à gauche\"\nBasic.TransformWindow.Alignment.TopCenter=\"Centrer en haut\"\nBasic.TransformWindow.Alignment.TopRight=\"En haut à droite\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Centrer à gauche\"\nBasic.TransformWindow.Alignment.Center=\"Centrer\"\nBasic.TransformWindow.Alignment.CenterRight=\"Centrer à droite\"\nBasic.TransformWindow.Alignment.BottomLeft=\"En bas à gauche\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Centrer en bas\"\nBasic.TransformWindow.Alignment.BottomRight=\"En bas à droite\"\nBasic.TransformWindow.BoundsType.None=\"Sans contours\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Taille maximale uniquement\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Mettre à l'échelle à partir des dimensions intérieures\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Mettre à l'échelle à partir des dimensions extérieures\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Mettre à l'échelle par la largeur\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Mettre à l'échelle par la hauteur\"\nBasic.TransformWindow.BoundsType.Stretch=\"Étirer aux dimensions du Cadre\"\nBasic.TransformWindow.Title=\"Modification de la transformation pour '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Aucune source sélectionnée\"\nBasic.Main.AddSourceHelp.Title=\"Impossible d'ajouter la source\"\nBasic.Main.AddSourceHelp.Text=\"Vous devez avoir au moins une scène pour ajouter une source.\"\nBasic.Main.Scenes=\"Scènes\"\nBasic.Main.Controls=\"Commandes\"\nBasic.Main.PreparingStream=\"Préparation...\"\nBasic.Main.Connecting=\"Connexion en cours...\"\nBasic.Main.StartRecording=\"Démarrer l'enregistrement\"\nBasic.Main.StartReplayBuffer=\"Démarrer le tampon de relecture\"\nBasic.Main.SaveReplay=\"Sauvegarder la relecture\"\nBasic.Main.StartStreaming=\"Commencer le streaming\"\nBasic.Main.StartBroadcast=\"Passer en direct\"\nBasic.Main.StartVirtualCam=\"Démarrer la caméra virtuelle\"\nBasic.Main.StopRecording=\"Arrêter l'enregistrement\"\nBasic.Main.PauseRecording=\"Mettre en pause l'enregistrement\"\nBasic.Main.UnpauseRecording=\"Reprendre l'enregistrement\"\nBasic.Main.SplitFile=\"Fractionner le fichier cible\"\nBasic.Main.AddChapterMarker=\"Ajouter un marqueur de chapitre (MP4/MOV hybride uniquement)\"\nBasic.Main.StoppingRecording=\"Arrêt de l'enregistrement...\"\nBasic.Main.StopReplayBuffer=\"Arrêter le tampon de relecture\"\nBasic.Main.StoppingReplayBuffer=\"Arrêt du tampon de relecture...\"\nBasic.Main.SetupBroadcast=\"Gérer la diffusion\"\nBasic.Main.StopStreaming=\"Arrêter le streaming\"\nBasic.Main.StopBroadcast=\"Arrêter le direct\"\nBasic.Main.AutoStopEnabled=\"(Arrêt automatique)\"\nBasic.Main.StoppingStreaming=\"Arrêt du stream...\"\nBasic.Main.ForceStopStreaming=\"Arrêter le streaming (immédiatement)\"\nBasic.Main.ShowContextBar=\"Afficher la barre d'outils Sources\"\nBasic.Main.HideContextBar=\"Cacher la barre d'outils Sources\"\nBasic.Main.StopVirtualCam=\"Arrêter la caméra virtuelle\"\nBasic.Main.Group=\"Groupe %1\"\nBasic.Main.GroupItems=\"Grouper les éléments sélectionnés\"\nBasic.Main.Ungroup=\"Dissocier\"\nBasic.Main.GridMode=\"Mode grille\"\nBasic.Main.ListMode=\"Mode liste\"\nBasic.Main.VirtualCamConfig=\"Configurer la caméra virtuelle\"\nBasic.VCam.VirtualCamera=\"Caméra virtuelle\"\nBasic.VCam.OutputType=\"Type de sortie\"\nBasic.VCam.OutputSelection=\"Sélection de sortie\"\nBasic.VCam.OutputType.Program=\"Programme (par défaut)\"\nBasic.VCam.OutputSelection.NoSelection=\"Aucune sélection pour ce type de sortie\"\nBasic.VCam.RestartWarning=\"La caméra virtuelle sera redémarrée pour appliquer cette modification\"\nBasic.MainMenu.File=\"&Fichier\"\nBasic.MainMenu.File.Export=\"&Exporter\"\nBasic.MainMenu.File.Import=\"&Importer\"\nBasic.MainMenu.File.ShowRecordings=\"Afficher les enregistrements (&R)\"\nBasic.MainMenu.File.Remux=\"Convertir des enregistre&ments\"\nBasic.MainMenu.File.Settings=\"Paramètre&s\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Afficher le dossier des paramètres\"\nBasic.MainMenu.File.ShowProfileFolder=\"Afficher le dossier du profil\"\nBasic.MainMenu.File.ShowMissingFiles=\"Vérifier les fichiers manquants\"\nBasic.MainMenu.File.Exit=\"Quitter (&X)\"\nBasic.MainMenu.Edit=\"&Editer\"\nBasic.MainMenu.Edit.Undo=\"Ann&uler\"\nBasic.MainMenu.Edit.Redo=\"&Rétablir\"\nBasic.MainMenu.Edit.LockPreview=\"Verroui&ller la prévisualisation\"\nBasic.MainMenu.Edit.Scale=\"Mi&se à l'échelle de l'aperçu\"\nBasic.MainMenu.Edit.Scale.Window=\"Adapter à la fenêtre\"\nBasic.MainMenu.Edit.Scale.Output=\"Sortie (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Mise à l’échelle (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Agrandir\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Réduire\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Réinitialiser le zoom\"\nBasic.MainMenu.Edit.Transform=\"&Transformer\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Édit&er la transformation...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Copier la transformation\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Coller la transformation\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Réinitialiser la transformation\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Rotation de 90° sens horaire\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Rotation de 90° sens antihoraire\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Rotation de 180°\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Inverser &horizontalement\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Inverser &verticalement\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Adapter à l'écran (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Étirer à l'écran (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centrer à l'écran\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centrer Verticalement\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centrer Horizontalement\"\nBasic.MainMenu.Edit.Order=\"&Ordonner\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Monter d'&un cran\"\nBasic.MainMenu.Edit.Order.MoveDown=\"&Descendre d'un cran\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Déplacer &tout en haut\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Déplacer tout en &bas\"\nBasic.MainMenu.Edit.AdvAudio=\"Propriétés &audio avancées\"\nBasic.MainMenu.View=\"Afficher (&V)\"\nBasic.MainMenu.View.Toolbars=\"Barres d'ou&tils\"\nBasic.MainMenu.View.ListboxToolbars=\"Barres d'outils des Docks\"\nBasic.MainMenu.View.ContextBar=\"Barre d'outils Sources\"\nBasic.MainMenu.View.SourceIcons=\"&Icônes de source\"\nBasic.MainMenu.View.StatusBar=\"Barre d'état (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Interface plein écran\"\nBasic.MainMenu.View.ResetUI=\"&Réinitialiser l'interface\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Toujours au premier plan\"\nBasic.MainMenu.View.SceneListMode=\"Mode d'affichage des scènes\"\nBasic.MainMenu.Docks.ResetDocks=\"&Réinitialiser les Docks\"\nBasic.MainMenu.Docks.LockDocks=\"Verrouiller les Docks\"\nBasic.MainMenu.Docks.SideDocks=\"&Docks en pleine hauteur\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Docks Internet personnalisés...\"\nBasic.MainMenu.Import=\"Importer...\"\nBasic.MainMenu.Export=\"Exporter...\"\nBasic.MainMenu.SceneCollection=\"Collection de &scènes\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Importer un profil\"\nBasic.MainMenu.Profile.Export=\"Exporter un profil\"\nBasic.MainMenu.SceneCollection.Import=\"Importer une collection de scènes\"\nBasic.MainMenu.SceneCollection.Export=\"Exporter une collection de scènes\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Restaurer la résolution de base\"\nBasic.MainMenu.SceneCollection.Migrate=\"Définir la résolution de base\"\nBasic.MainMenu.Profile.Exists=\"Ce profil existe déjà\"\nBasic.MainMenu.Tools=\"Ou&tils\"\nBasic.MainMenu.Help=\"Aide (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portail d'aide\"\nBasic.MainMenu.Help.Website=\"Consulter le site &Web\"\nBasic.MainMenu.Help.Discord=\"Rejoindre le serveur &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Nouveautés\"\nBasic.MainMenu.Help.Logs=\"Fichiers journaux (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Afficher le&s fichiers de log\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Mettre en ligne le fi&chier journal actuel\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Envoyer le fichier journal &précédent\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Voir le journal actuel\"\nBasic.MainMenu.Help.ReleaseNotes=\"Notes de version\"\nBasic.MainMenu.Help.CheckForUpdates=\"Rechercher des mises à jour\"\nBasic.MainMenu.Help.Repair=\"Vérifier l'intégrité du fichier\"\nBasic.MainMenu.Help.RestartSafeMode=\"Redémarrer en mode sans échec\"\nBasic.MainMenu.Help.RestartNormal=\"Redémarrer normalement\"\nBasic.MainMenu.Help.CrashLogs=\"&Rapports d'erreurs\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Montrer le&s rapports d'erreur\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Envoyer le rapport de plantage &Précédent\"\nBasic.MainMenu.Help.About=\"À propos (&A)\"\nBasic.Settings.ProgramRestart=\"Le programme doit être redémarré pour que les paramètres prennent effet.\"\nBasic.Settings.ConfirmTitle=\"Valider les modifications\"\nBasic.Settings.Confirm=\"Vous avez des modifications non enregistrées. Voulez-vous les enregistrer ?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 contrôle certains de vos paramètres de flux\"\nBasic.Settings.General=\"Général\"\nBasic.Settings.General.Language=\"Langue\"\nBasic.Settings.General.Updater=\"Mises à jour\"\nBasic.Settings.General.UpdateChannel=\"Canal de mise à jour\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Désactivé)\"\nBasic.Settings.General.UpdateChannelDefault=\"(par défaut)\"\nBasic.Settings.General.EnableAutoUpdates=\"Vérifier automatiquement les mises à jour au démarrage\"\nBasic.Settings.General.OpenStatsOnStartup=\"Ouvrir la boîte de dialogue des statistiques au démarrage\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Masquer les fenêtre d'OBS sur la capture d'écran\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Ce paramètre masquera toutes les fenêtres d'OBS Studio qui ne sont pas des projecteurs, empêchant ainsi la capture par OBS et affectera\\nd'autres applications comme les logiciels de visio-conférence, de partage d'écran, de dépannage à distance, de capture d'écran et autres types de capture.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"L'activation de ce paramètre option permet de masquer toutes les fenêtres d'OBS Studio qui ne sont pas sur un projecteur afin qu'elles ne soient pas capturées par OBS et affectent d'autres applications, telles que les visio-conférences, le partage d'écran, l'assistance à distance, les captures d'écran et d'autres logiciels de capture.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Afficher une boîte de dialogue de confirmation au démarrage d'un stream\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Afficher une boîte de dialogue de confirmation à l'arrêt d'un stream\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Demander confirmation avant d'arrêter un enregistrement\"\nBasic.Settings.General.Projectors=\"Projecteurs\"\nBasic.Settings.General.HideProjectorCursor=\"Cacher le curseur sur les projecteurs\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Projecteurs toujours au premier plan\"\nBasic.Settings.General.Snapping=\"Déclenchement d'alignement des sources\"\nBasic.Settings.General.ScreenSnapping=\"Déclencher avec les bords de l'écran\"\nBasic.Settings.General.CenterSnapping=\"Déclencher avec le centre de l'écran\"\nBasic.Settings.General.SourceSnapping=\"Déclencher avec d'autres sources\"\nBasic.Settings.General.SnapDistance=\"Sensibilité du déclenchement\"\nBasic.Settings.General.SpacingHelpers=\"Afficher les guides d'alignement des pixels\"\nBasic.Settings.General.RecordWhenStreaming=\"Enregistrer automatiquement lors d'un stream\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Continuer à enregistrer lorsque le stream s’arrête\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Démarrer automatiquement le tampon de relecture lors d'un stream\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Garder le tampon de relecture actif lors de l'arrêt du stream\"\nBasic.Settings.General.SysTray=\"Zone de notifications\"\nBasic.Settings.General.SysTrayWhenStarted=\"Réduire dans la zone de notification dès le démarrage\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Toujours réduire dans la zone de notification au lieu de la barre des tâches\"\nBasic.Settings.General.SaveProjectors=\"Enregistrer les projecteurs en quittant\"\nBasic.Settings.General.CloseExistingProjectors=\"Limiter à un projecteur plein écran par écran\"\nBasic.Settings.General.Preview=\"Aperçu\"\nBasic.Settings.General.OverflowHidden=\"Masquer le débordement\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Débordement toujours visible\"\nBasic.Settings.General.OverflowSelectionHidden=\"Afficher le débordement même si la source est invisible\"\nBasic.Settings.General.Importers=\"Importateurs\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Rechercher des collections de scènes dans les emplacements connus\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Effectuer la transition vers la scène en cas de double clic\"\nBasic.Settings.General.StudioPortraitLayout=\"Activer la mise en page portrait/verticale\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Afficher les étiquettes d'aperçu ou de programme\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Cliquez pour changer de scène\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Montrer les noms des scènes\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Afficher les zones sûres (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Disposition du multiview\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontal, Haut (8 Scènes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontal, Bas (8 Scènes)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertical, Gauche (8 Scènes)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertical, Droite (8 Scènes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontal, Haut (18 Scènes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontal, Haut (24 Scènes)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Scènes seulement (4 Scènes)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Scènes seulement (9 Scènes)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Scènes seulement (16 Scènes)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Scènes seulement (25 Scènes)\"\nBasic.Settings.General.ChannelDescription.stable=\"Dernière version stable\"\nBasic.Settings.General.ChannelName.beta=\"Bêtas / Versions de pré-sortie\"\nBasic.Settings.General.ChannelDescription.beta=\"Pré-versions potentiellement instables\"\nBasic.Settings.Appearance=\"Apparence\"\nBasic.Settings.Appearance.General=\"Général\"\nBasic.Settings.Appearance.General.Theme=\"Thème\"\nBasic.Settings.Appearance.General.NoVariant=\"Aucun style disponible\"\nBasic.Settings.Appearance.FontScale=\"Taille de la police\"\nBasic.Settings.Appearance.Density=\"Densité\"\nBasic.Settings.Appearance.Classic=\"Classique\"\nBasic.Settings.Appearance.Compact=\"Compacte\"\nBasic.Settings.Appearance.Normal=\"Normale\"\nBasic.Settings.Appearance.Comfortable=\"Confortable\"\nBasic.Settings.Appearance.OptionsWarning=\"Certaines options d'apparence ne sont pas disponibles pour ce style.\"\nBasic.Settings.Stream=\"Stream (flux)\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Utiliser l'authentification\"\nBasic.Settings.Stream.Custom.Username=\"Nom d'utilisateur\"\nBasic.Settings.Stream.Custom.Password=\"Mot de passe\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST : entrez le srp_username.\\nRTMP : entrez le nom d'utilisateur.\\nSRT : non utilisé.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST : entrez le srp_password.\\nRTMP : entrez le mot de passe.\\nSRT : entrez la clé de chiffrement.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Activer le mode Test de bande passante\"\nBasic.Settings.Stream.TTVAddon=\"Extensions du chat Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Aucun\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV et FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Configuration du Stream manquante\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Ouvrir les paramètres\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"L'URL et la clé de diffusion sont manquantes.\\n\\nOuvrez les paramètres et entrez l'URL et la clé de diffusion dans l'onglet Stream.\"\nBasic.Settings.Stream.MissingUrl=\"L'URL de diffusion est manquante.\\n\\nOuvrez les paramètres et entrez l'URL de diffusion dans l'onglet Stream.\"\nBasic.Settings.Stream.MissingStreamKey=\"La clé de diffusion est manquante.\\n\\nOuvrez les paramètres et entrez la clé de diffusion dans l'onglet Stream.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorer les paramètres recommandés du service de streaming\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Remplacer les paramètres recommandés\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Attention : ignorer les limitations du service de diffusion peut entraîner une dégradation de la qualité du flux voir vous empêcher de diffuser.\\n\\nContinuer ?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Débit vidéo maximal : %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Débit audio maximal : %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Résolution maximale : %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"FPS Maximum : %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Spécifier un serveur personnalisé...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Serveur personnalisé\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Activer %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Bande passante de streaming maximale\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Nombre de pistes vidéo max.\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Activer le vidage de flux au format FLV (utilise des paramètres de fichier d'enregistrement simples)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Configuration de remplacement (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Activer la configuration de remplacement\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Vidéo multipiste\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Canevas additionnel\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Simulcast vous permet d'encoder et d'envoyer plusieurs qualités vidéo. <a href=“https://obsproject.com/kb/whip-streaming-guide”>En savoir plus</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Nombre total de couches\"\nBasic.Settings.Stream.AdvancedOptions=\"Options avancées\"\nBasic.Settings.Output=\"Sortie\"\nBasic.Settings.Output.Format=\"Format d'enregistrement\"\nBasic.Settings.Output.Format.MKV=\"Vidéo Matroska (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"MP4 hybride (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV hybride (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 fragmenté (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV fragmenté (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Le MOV fragmenté enregistre l'enregistrement par morceaux et ne nécessite pas la même finalisation que les fichiers MOV traditionnels.\\nCela garantit que le fichier reste lisible même si l'écriture sur le disque est interrompue, par exemple, suite à un BSOD ou d'une coupure de courant.\\n\\nCe format peut ne pas être compatible avec tous les lecteurs et éditeurs. Utilisez Fichier → Convertir des enregistrements pour convertir le fichier dans un format plus compatible si nécessaire.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Le MP4 fragmenté enregistre l'enregistrement par morceaux et ne nécessite pas la même finalisation que les fichiers MP4 traditionnels.\\nCela garantit que le fichier reste lisible même si l'écriture sur le disque est interrompue, par exemple, suite à un BSOD ou d'une coupure de courant.\\n\\nCe format peut ne pas être compatible avec tous les lecteurs et éditeurs. Utilisez Fichier → Convertir des enregistrements pour convertir le fichier dans un format plus compatible si nécessaire.\"\nBasic.Settings.Output.Encoder.Video=\"Encodeur vidéo\"\nBasic.Settings.Output.Encoder.Audio=\"Encodeur audio\"\nBasic.Settings.Output.SelectDirectory=\"Sélectionnez le répertoire d'enregistrement\"\nBasic.Settings.Output.DynamicBitrate=\"Ajuster dynamiquement le débit de streaming en cas de congestion réseau\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Ajuster dynamiquement le débit de streaming en cas de congestion réseau (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Ajuste automatiquement le débit de streaming pour éviter les pertes d'images en cas de congestion ou de surcharge réseau.\\n\\nCela peut augmenter le délai entre vous et vos spectateurs si la congestion est significative.\\nQuand le débit est réduit automatiquement, plusieurs minutes peuvent s'écouler avant qu'il ne revienne à la normale.\\n\\nN'est supporté que pour RTMP pour l'instant.\"\nBasic.Settings.Output.Mode=\"Mode de Sortie\"\nBasic.Settings.Output.Mode.Adv=\"Avancé\"\nBasic.Settings.Output.Mode.FFmpeg=\"Sortie FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Activer le tampon de relecture\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Durée maximale d'un replay\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Mémoire maximale\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Estimation de la mémoire utilisée : %1 Mo\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Attention : l'utilisation estimée de la mémoire de %1 MiB est supérieure au maximum recommandé de %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Impossible d'estimer l'utilisation de la mémoire. Veuillez définir une limite de mémoire maximale.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Nom du fichier du tampon commençant par\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Finissant par\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Le tampon de relecture ne peut pas être utilisé lorsque le type d'enregistrement est défini sur Sortie personnalisée (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Chemin d'accès de l'enregistrement\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Qualité d'enregistrement\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Identique au stream\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Haute qualité, taille de fichier moyenne\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Qualité indistinguable, grande taille de fichier\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Qualité sans perte, énorme taille de fichier\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Attention : le débit vidéo de la diffusion sera réglé à %1, ce qui est la valeur maximale pour le service de diffusion sélectionné.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Attention : le débit audio de la diffusion sera réglé à %1, ce qui est la valeur maximale pour le service de diffusion sélectionné.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Attention : l'enregistrement ne peut pas être mis en pause si la qualité d'enregistrement est configurée sur \\\"Identique au stream\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Attention : le format d'enregistrement actuellement sélectionné est incompatible avec le(s) encodeur(s) de diffusion actuellement sélectionné(s).\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Attention : enregistrer via un encodeur logiciel avec une qualité autre que celle du stream sollicitera encore plus le CPU si vous streamez et enregistrez simultanément.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Avertissement : la qualité sans perte génère des fichiers extrêmement volumineux ! Ce paramètre peut générer des fichiers de plus de 7 Go d'espace disque par minute à des résolutions et des fréquences d'images élevées. Il est déconseillé de l'utiliser pour des enregistrements prolongés, à moins que vous ne disposiez d'un espace disque très important. Le tampon de relecture n'est pas disponible avec ce paramètre.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Êtes-vous certain de vouloir utiliser la qualité sans perte ?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Avertissement de qualité sans perte\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Logiciel (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Matériel (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Matériel (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Matériel (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Matériel (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Matériel (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Matériel (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Matériel (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Matériel (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Matériel (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Matériel (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Logiciel (pré-réglage x264 \\\"faible utilisation du CPU\\\", augmente la taille du fichier)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (par défaut)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Piste de VOD Twitch (utilise la piste 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Piste audio\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Résolution ou débit d'images incompatible\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Ce service de streaming ne prend pas en charge la résolution de sortie et/ou le débit d'images actuel. Ils seront modifiés à la valeur compatible la plus proche :\\n\\n%1\\n\\n Voulez vous continuer ?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Résolution : %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"FPS : %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Encodeur incompatible\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Le service de streaming \\\"%1\\\" ne prend pas en charge l'encodeur \\\"%2\\\". L'encodeur sera changé en \\\"%3\\\".\\n\\nVoulez-vous continuer ?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Le service de streaming \\\"%1\\\" ne prend pas en charge les encodeurs \\\"%2\\\" et \\\"%3\\\". Les encodeurs seront changés en \\\"%4\\\" et \\\"%5\\\".\\n\\nVoulez-vous continuer ?\"\nBasic.Settings.Output.VideoBitrate=\"Débit vidéo\"\nBasic.Settings.Output.AudioBitrate=\"Débit audio\"\nBasic.Settings.Output.Reconnect=\"Reconnexion automatique\"\nBasic.Settings.Output.RetryDelay=\"Délai avant nouvelle tentative\"\nBasic.Settings.Output.MaxRetries=\"Nombres de tentatives maximales\"\nBasic.Settings.Output.Advanced=\"Activer les paramètres personnalisés de l'encodeur (Avancé)\"\nBasic.Settings.Output.EncoderPreset=\"Pré-réglage d'encodeur\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (faible utilisation du processeur, qualité la plus basse)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (par défaut) (utilisation moyenne du processeur, qualité standard)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (utilisation élevée du processeur, haute qualité)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Paramètres personnalisés de l'encodeur\"\nBasic.Settings.Output.CustomMuxerSettings=\"Paramètres personnalisés du muxer\"\nBasic.Settings.Output.NoSpaceFileName=\"Générer le nom du fichier sans espace\"\nBasic.Settings.Output.Adv.Rescale=\"Mise à l'échelle pour la Sortie\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Désactivé\"\nBasic.Settings.Output.Adv.AudioTrack=\"Piste audio\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Paramètres de streaming\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Piste 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Piste 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Piste 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Piste 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Piste 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Piste 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Piste de VOD Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"Paramètres de l'encodeur\"\nBasic.Settings.Output.Adv.Recording=\"Enregistrement\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Paramètres d'enregistrement\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Type d'enregistrement\"\nBasic.Settings.Output.Adv.Recording.Type=\"Type \"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Sortie Personnalisée (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Utiliser le même encodeur que pour le stream)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Format du nom de fichier\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Écraser si le fichier existe\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Le flux de sortie (FFmpeg) est proposé sans garanties \\n Veuillez considérer l'utilisation du flux de sortie \\\"Standard\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Type de Sortie FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Sortie vers une URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Sortie vers un fichier\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Formats d'enregistrement courants\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Tous les fichiers\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Chemin d'accès ou URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format de conteneur\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Vidéo\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Format par défaut\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Description du format de conteneur\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Codec audio/vidéo deviné à partir du chemin d'accès ou de l'URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Encodeur par défaut\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Désactiver l'encodeur\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Encodeur vidéo\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Paramètres de l'encodeur vidéo (le cas échéant)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Encodeur audio\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Paramètres de l'encodeur audio (le cas échéant)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Paramètres du muxer (le cas échéant)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Intervalle d'images clés (en images)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Afficher tous les codecs (même si potentiellement incompatibles)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Paramètres FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Fractionnement automatique des fichiers\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Fractionner selon la durée\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Fractionner selon la taille\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Ne fractionner que manuellement\"\nBasic.Settings.Output.SplitFile.Time=\"Durée de la fraction\"\nBasic.Settings.Output.SplitFile.Size=\"Taille du fractionnement\"\nScreenshot=\"Capture d'écran de la sortie\"\nScreenshot.SourceHotkey=\"Capture d'écran de la source sélectionnée\"\nScreenshot.StudioProgram=\"Capture d'écran (programme)\"\nScreenshot.Preview=\"Capture d'écran (aperçu)\"\nScreenshot.Scene=\"Capture d'écran (scène)\"\nScreenshot.Source=\"Capture d'écran (source)\"\nFilenameFormatting.TT.CCYY=\"Année, 4 chiffres\"\nFilenameFormatting.TT.YY=\"Année, 2 derniers chiffres (00-99)\"\nFilenameFormatting.TT.MM=\"Mois en nombre (01-12)\"\nFilenameFormatting.TT.DD=\"Jour du mois, avec zéros (01-31)\"\nFilenameFormatting.TT.hh=\"Heure en format 24h (00-23)\"\nFilenameFormatting.TT.mm=\"Minutes (00-59)\"\nFilenameFormatting.TT.ss=\"Secondes (00-59)\"\nFilenameFormatting.TT.Percent=\"Un signe %\"\nFilenameFormatting.TT.a=\"Nom abrégé du jour\"\nFilenameFormatting.TT.A=\"Nom complet du jour\"\nFilenameFormatting.TT.b=\"Nom abrégé du mois\"\nFilenameFormatting.TT.B=\"Nom complet du mois\"\nFilenameFormatting.TT.d=\"Jour du mois, avec zéros (01-31)\"\nFilenameFormatting.TT.H=\"Heure en format 24h (00-23)\"\nFilenameFormatting.TT.I=\"Heure en format 12h (00-23)\"\nFilenameFormatting.TT.m=\"Mois en nombre (01-12)\"\nFilenameFormatting.TT.M=\"Minutes (00-59)\"\nFilenameFormatting.TT.p=\"AM ou PM\"\nFilenameFormatting.TT.s=\"Temps en secondes depuis l'epoch UNIX\"\nFilenameFormatting.TT.S=\"Secondes (00-59)\"\nFilenameFormatting.TT.y=\"Année, 2 derniers chiffres (00-99)\"\nFilenameFormatting.TT.Y=\"Année\"\nFilenameFormatting.TT.z=\"Décalage du fuseau horaire depuis l'UTC au format ISO-8601\"\nFilenameFormatting.TT.Z=\"Nom du fuseau horaire ou abréviation\"\nFilenameFormatting.TT.FPS=\"Images par seconde\"\nFilenameFormatting.TT.CRES=\"Résolution de base (canevas)\"\nFilenameFormatting.TT.ORES=\"Résolution de sortie (mise à l'échelle)\"\nFilenameFormatting.TT.VF=\"Format vidéo\"\nBasic.Settings.Video=\"Vidéo\"\nBasic.Settings.Video.Adapter=\"Périphérique vidéo\"\nBasic.Settings.Video.BaseResolution=\"Résolution de base (canevas)\"\nBasic.Settings.Video.ScaledResolution=\"Résolution de Sortie (mise à l'échelle)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtrage de la mise à l'échelle\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Les résolutions sont identiques, aucune réduction d'échelle n'est requise]\"\nBasic.Settings.Video.FPS=\"FPS (débit d'images)\"\nBasic.Settings.Video.FPSCommon=\"Valeurs courantes pour le débit d'images (FPS)\"\nBasic.Settings.Video.FPSInteger=\"Valeurs entières pour le débit d'images (FPS)\"\nBasic.Settings.Video.FPSFraction=\"Débit d'images (FPS) sous forme de fraction\"\nBasic.Settings.Video.Numerator=\"Numérateur\"\nBasic.Settings.Video.Denominator=\"Dénominateur\"\nBasic.Settings.Video.Renderer=\"Rendu\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Expérimental)\"\nBasic.Settings.Video.InvalidResolution=\"Valeurs de résolution non-valides. Elles doivent être au format [largeur]x[hauteur] (par exemple 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"La sortie vidéo est actuellement active. Veuillez éteindre toutes les sorties avant de modifier vos paramètres vidéo.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinéaire (le plus rapide, mais flou en cas de mise à l'échelle)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubique (mise à l'échelle avec netteté accentuée, 16 échantillons)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (mise à l'échelle avec netteté accentuée, 36 échantillons)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Zone (somme pondérée, 4/6/9 échantillons)\"\nBasic.Settings.Audio.SampleRate=\"Fréquence d'échantillonnage\"\nBasic.Settings.Audio.Channels=\"Canaux\"\nBasic.Settings.Audio.Meters=\"VU-mètre\"\nBasic.Settings.Audio.MeterDecayRate=\"Taux de décroissance\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rapide\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Moyen (Type I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lent (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Type de crête pour le VU-mètre\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Crête des échantillons audio\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Crête exacte (plus grande utilisation du CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ATTENTION : le son multicanal est activé.\"\nBasic.Settings.Audio.MultichannelWarning=\"Si vous diffusez en direct, vérifiez que votre service de diffusion prend en charge à la fois l'acquisition et la lecture du son surround. Par exemple, Facebook 360 Live prend entièrement en charge le son surround ; YouTube Live prend en charge l'acquisition audio 5.1 (et la lecture sur les téléviseurs).\\n\\nLes filtres audio OBS sont compatibles avec le son surround, bien que la prise en charge des plugins VST ne soit pas garantie.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Activer le son multicanal ?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Êtes vous sûr de vouloir activer le son multicanal ?\"\nBasic.Settings.Audio.Devices=\"Périphériques audio globaux\"\nBasic.Settings.Audio.DesktopDevice=\"Audio du bureau\"\nBasic.Settings.Audio.DesktopDevice2=\"Audio du bureau 2\"\nBasic.Settings.Audio.AuxDevice=\"Audio micro/auxiliaire\"\nBasic.Settings.Audio.AuxDevice2=\"Audio micro/auxiliaire 2\"\nBasic.Settings.Audio.AuxDevice3=\"Audio micro/auxiliaire 3\"\nBasic.Settings.Audio.AuxDevice4=\"Audio micro/auxiliaire 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Activer \\\"Appuyer-pour-désactiver\\\" (Push-to-mute)\"\nBasic.Settings.Audio.PushToMuteDelay=\"Délai du \\\"Appuyer-pour-désactiver\\\" (Push-to-mute)\"\nBasic.Settings.Audio.EnablePushToTalk=\"Activer le \\\"Appuyer-pour-parler\\\" (Push-to-talk)\"\nBasic.Settings.Audio.PushToTalkDelay=\"Délai du \\\"Appuyer-pour-parler\\\" (Push-to-talk)\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Périphérique non connecté ou non disponible]\"\nBasic.Settings.Audio.Disabled=\"Désactivé\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Mode de mise en mémoire tampon audio faible (Pour les sorties Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ATTENTION : La mise en mémoire tampon audio basse latence est activée.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Le mode de mise en mémoire tampon audio à faible latence peut causer des problèmes d'audio ou arrêter la lecture à partir de certaines sources.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Activer le mode de mise en mémoire tampon audio à faible latence ?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Êtes-vous sûr de vouloir activer le mode de mise en mémoire tampon à faible latence ?\"\nBasic.Settings.Accessibility=\"Accessibilité\"\nBasic.Settings.Accessibility.ColorOverrides=\"Utiliser des couleurs différentes\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Bordure de la source (sélection)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Bordure de la source (rognage)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Bordure de la source (survol)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Bande de volume du mixeur (-60 à -20 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Bande de volume du mixeur (-20 à -9 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Bande de volume du mixeur (-9 à 0 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Bande de volume du mixeur (-60 à -20 dB) (active)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Bande de volume du mixeur (-20 à -9 dB) (active)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Bande de volume du mixeur (-9 à 0 dB) (active)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Préréglage des couleurs\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Par défaut\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Personnalisé\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Daltoniens\"\nBasic.Settings.Advanced=\"Avancé\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Afficher un avertissement sur les sorties actives lors de la fermeture\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Priorité du processus\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Haute\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Supérieure à la normale\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normale\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Au-dessous de la normale\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Basse\"\nBasic.Settings.Advanced.FormatWarning=\"Attention : les formats de couleur autres que NV12/P010 sont principalement destinés à l'enregistrement, et ne sont pas recommandés pour le streaming. Le streaming peut pâtir d'une utilisation élevée du CPU due à la conversion de format de couleur.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Attention : les formats haute précision sont plus couramment utilisés avec les espaces colorimétriques HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Avertissement : Rec. 2100 devrait utiliser un format avec plus de précision.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Format de couleur\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 niveaux)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 niveaux)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 niveaux)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 niveaux)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 niveaux)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 niveaux)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 niveaux)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 bits)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Espace de couleurs YUV\"\nBasic.Settings.Advanced.Video.ColorRange=\"Gamme de couleurs YUV\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limité\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Complète\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Niveau de blanc SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Niveau de crête nominal HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Périphérique de Monitoring audio\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Par défaut\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Désactiver l'atténuation audio de Windows (ducking)\"\nBasic.Settings.Advanced.StreamDelay=\"Retard du stream\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Durée\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Préserver le point de coupure (augmente le retard) lors d'une reconnexion\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Utilisation estimée de la mémoire : %1 Mo\"\nBasic.Settings.Advanced.Network=\"Carte réseau (adresse IP source du flux)\"\nBasic.Settings.Advanced.Network.Disabled=\"Le protocole de streaming actuellement sélectionné ne prend pas en charge la modification des paramètres réseau.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Lier à :\"\nBasic.Settings.Advanced.Network.IPFamily=\"Famille d'IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Activer les optimisations réseau\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Activer la régulation TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Tente de rendre le flux RTMP plus coopératif avec les autres applications sensibles à la latence sur le réseau en régulant la vitesse de transmission.\\nCela peut augmenter le risque d'images perdues sur les connexions instables.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Comportement selon le focus\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Ne jamais désactiver les raccourcis clavier\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Désactiver les raccourcis clavier lorsque la fenêtre principale est au premier plan\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Désactiver les raccourcis clavier quand la fenêtre principale est en arrière-plan\"\nBasic.Settings.Advanced.AutoRemux=\"Convertir automatiquement en %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(enregistrer en MKV)\"\nBasic.AdvAudio=\"Propriétés Audio Avancées\"\nBasic.AdvAudio.ActiveOnly=\"Sources actives uniquement\"\nBasic.AdvAudio.Name=\"Nom\"\nBasic.AdvAudio.VolumeSource=\"Volume pour \\\"%1\\\"\"\nBasic.AdvAudio.MonoSource=\"Conversion en mono pour \\\"%1\\\"\"\nBasic.AdvAudio.BalanceSource=\"Balance audio pour \\\"%1\\\"\"\nBasic.AdvAudio.SyncOffset=\"Décalage de synchronisation\"\nBasic.AdvAudio.SyncOffsetSource=\"Décalage de synchronisation pour \\\"%1\\\"\"\nBasic.AdvAudio.Monitoring=\"Monitoring audio\"\nBasic.AdvAudio.Monitoring.None=\"Monitoring désactivé\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Monitoring uniquement (sortie coupée)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitoring et sortie\"\nBasic.AdvAudio.MonitoringSource=\"Monitoring audio pour \\\"%1\\\"\"\nBasic.AdvAudio.AudioTracks=\"Pistes\"\nBasic.Settings.Hotkeys=\"Raccourcis clavier\"\nBasic.Settings.Hotkeys.Pair=\"Les combinaisons de touches partagées avec '%1' agissent comme déclencheur\"\nBasic.Settings.Hotkeys.Filter=\"Filtre\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrer par Raccourci Clavier\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Ce raccourci clavier est partagé par une ou plusieurs autres actions, cliquez pour afficher les conflits\"\nBasic.Settings.Hotkeys.PleaseWait=\"Chargement des raccourcis claviers, veuillez patienter...\"\nBasic.Hotkeys.SelectScene=\"Passer à la scène\"\nBasic.SystemTray.Show=\"Restaurer\"\nBasic.SystemTray.Hide=\"Réduire\"\nBasic.SystemTray.Message.Reconnecting=\"Déconnecté. Reconnexion en cours...\"\nHotkeys.Insert=\"Insérer\"\nHotkeys.Delete=\"Supprimer\"\nHotkeys.Home=\"Début\"\nHotkeys.End=\"Fin\"\nHotkeys.PageUp=\"Page précédente\"\nHotkeys.PageDown=\"Page suivante\"\nHotkeys.NumLock=\"Verr Num\"\nHotkeys.ScrollLock=\"Arrêt défilement\"\nHotkeys.CapsLock=\"Verr Maj\"\nHotkeys.Backspace=\"Retour arrière\"\nHotkeys.Print=\"Impression écran\"\nHotkeys.Left=\"Gauche\"\nHotkeys.Right=\"Droite\"\nHotkeys.Up=\"Haut\"\nHotkeys.Down=\"Bas\"\nHotkeys.Space=\"Espace\"\nHotkeys.NumpadNum=\"Pavé numérique %1\"\nHotkeys.NumpadMultiply=\"Multiplication du pavé numérique\"\nHotkeys.NumpadDivide=\"Division du pavé numérique\"\nHotkeys.NumpadAdd=\"Addition du pavé numérique\"\nHotkeys.NumpadSubtract=\"Soustraction du pavé numérique\"\nHotkeys.NumpadDecimal=\"Virgule du pavé numérique\"\nHotkeys.AppleKeypadNum=\"%1 (Pavé numérique)\"\nHotkeys.AppleKeypadMultiply=\"* (Pavé numérique)\"\nHotkeys.AppleKeypadDivide=\"/ (Pavé numérique)\"\nHotkeys.AppleKeypadAdd=\"+ (Pavé numérique)\"\nHotkeys.AppleKeypadSubtract=\"- (Pavé numérique)\"\nHotkeys.AppleKeypadDecimal=\". (Pavé numérique)\"\nHotkeys.AppleKeypadEqual=\"= (Pavé numérique)\"\nHotkeys.MouseButton=\"Souris %1\"\nMute=\"Couper l'Audio\"\nUnmute=\"Restaurer l'Audio\"\nPush-to-talk=\"Appuyer-pour-parler\"\nSceneItemShow=\"Afficher '%1'\"\nSceneItemHide=\"Cacher '%1'\"\nOutputWarnings.NoTracksSelected=\"Vous devez sélectionner au moins une piste\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Erreur dans les paramètres de sortie\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Toutes les sorties doivent avoir au moins une piste audio sélectionnée.\"\nOutputWarnings.MP4Recording=\"Avertissement : les enregistrements sauvegardés en MP4/MOV seront irrécupérables si le fichier ne peut pas être finalisé (par ex. : en cas de BSOD, pannes de courant, etc.). Si vous voulez enregistrer plusieurs pistes audio, envisagez d'utiliser le format MKV et à convertir l'enregistrement en MP4/MOV une fois celui-ci terminé (Fichier → Convertir des enregistrements)\"\nOutputWarnings.CannotPause=\"Attention : l'enregistrement ne peut pas être mis en pause si l'encodeur d'enregistrement est configuré sur \\\"(Utiliser le même encodeur que pour le stream)\\\"\"\nOutputWarnings.CodecIncompatible=\"La sélection de l'encodeur audio ou vidéo a été réinitialisée pour cause d'incompatibilité. Veuillez sélectionner un encodeur compatible depuis la liste.\"\nCodecCompat.Incompatible=\"(incompatible avec %1)\"\nCodecCompat.CodecPlaceholder=\"Sélectionner un encodeur...\"\nCodecCompat.ContainerPlaceholder=\"Sélectionner un format...\"\nCodecCompat.CodecMissingOnExit.Title=\"Aucun encodeur sélectionné\"\nCodecCompat.CodecMissingOnExit.Text=\"Au moins un encodeur vidéo ou audio n'est pas défini. Veillez à sélectionner les encodeurs pour l'enregistrement et le streaming.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Aucun format sélectionné\"\nCodecCompat.ContainerMissingOnExit.Text=\"Aucun format d'enregistrement n'a été sélectionné. Veuillez choisir un format d'enregistrement compatible avec l'encodeur de streaming sélectionné.\"\nFinalScene.Title=\"Supprimer la scène\"\nFinalScene.Text=\"Il doit y avoir au moins une scène.\"\nNoSources.Title=\"Aucune source\"\nNoSources.Text=\"Il semble que vous n'ayez pas ajouté de source vidéo pour le moment, vous aurez donc un écran noir. Êtes-vous sûr de vouloir continuer ?\"\nNoSources.Text.AddSource=\"Vous pouvez à tout moment ajouter des sources en cliquant sur l'icone + en bas du cadre Sources de la fenêtre principale.\"\nNoSources.Label=\"Vous n'avez aucune source.\\nCliquez sur le bouton \\\"+\\\" ci-dessous,\\nou cliquez avec le bouton droit ici pour en ajouter une.\"\nChangeBG=\"Définir la couleur\"\nCustomColor=\"Couleur personnalisée\"\nBrowserSource.EnableHardwareAcceleration=\"Activer l’accélération matérielle de la source « navigateur »\"\nAbout=\"À propos\"\nAbout.Info=\"OBS Studio est un logiciel libre et open source d'enregistrement vidéo et de diffusion en direct.\"\nAbout.Donate=\"Faire un don\"\nAbout.GetInvolved=\"Contribuer\"\nAbout.Authors=\"Auteurs\"\nAbout.License=\"Licence\"\nAbout.Error=\"Erreur ! Impossible de lire le fichier.\\n\\nAccédez à : %1\"\nAbout.Contribute=\"Soutenir le projet OBS\"\nAddUrl.Title=\"Ajouter une source via une URL\"\nAddUrl.Text=\"Vous avez glissé une URL dans OBS. Cela va automatiquement ajouter le lien comme une source. Continuer ?\"\nAddUrl.Text.Url=\"URL : %1\"\nResizeOutputSizeOfSource=\"Redimensionner la sortie (à la taille de la source)\"\nResizeOutputSizeOfSource.Text=\"Les résolutions de base et de sortie seront redimensionnées à la taille de la source actuelle.\"\nResizeOutputSizeOfSource.Continue=\"Voulez-vous continuer ?\"\nPreviewTransition=\"Prévisualiser la transition\"\nImporter=\"Importateur de Collections de Scènes\"\nImporter.SelectCollection=\"Choisissez une Collection de Scènes\"\nImporter.Collection=\"Collection de Scènes\"\nImporter.HelpText=\"Ajoutez des fichiers à cette fenêtre pour importer des collections de scènes provenant d'OBS ou d'autres programmes pris en charge.\"\nImporter.Path=\"Chemin de la collection\"\nImporter.Program=\"Application détectée\"\nImporter.AutomaticCollectionPrompt=\"Recherche automatique de collections de scènes\"\nImporter.AutomaticCollectionText=\"OBS peut automatiquement trouver des collections de scènes importables à partir de programmes tiers pris en charge. Voulez-vous que OBS trouve automatiquement ces collections pour vous?\\n\\nVous pourrez modifier cela plus tard dans Paramètres > Général > Importation.\"\nImporter.SelectFile=\"Parcourir...\"\nRestart=\"Redémarrage\"\nNeedsRestart=\"OBS Studio doit être redémarré. Voulez-vous le redémarrer maintenant ?\"\nLoadProfileNeedsRestart=\"Le profil contient des paramètres qui requiert un redémarrage d'OBS:\\n%1\\n\\nVoulez-vous redémarrer OBS pour que ces paramètres prennent effet ?\"\nContextBar.NoSelectedSource=\"Aucune source sélectionnée\"\nContextBar.MediaControls.PlayMedia=\"Lire le média\"\nContextBar.MediaControls.PauseMedia=\"Mettre en pause les médias\"\nContextBar.MediaControls.StopMedia=\"Arrêter les médias\"\nContextBar.MediaControls.RestartMedia=\"Redémarrer le média\"\nContextBar.MediaControls.PlaylistNext=\"Suivant dans la playlist\"\nContextBar.MediaControls.PlaylistPrevious=\"Précédent dans la playlist\"\nContextBar.MediaControls.BlindSeek=\"Widget de Navigation dans le Média\"\nYouTube.Auth.Ok=\"Autorisation obtenue avec succès.\\nVous pouvez maintenant fermer cette page.\"\nYouTube.Auth.NoCode=\"Le processus d'autorisation n'est pas terminé.\"\nYouTube.Auth.NoChannels=\"Aucune chaîne disponible sur le compte sélectionné\"\nYouTube.Auth.WaitingAuth.Title=\"Autorisation utilisateur YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Veuillez compléter l'autorisation dans votre navigateur externe.<br>Si le navigateur externe ne s'ouvre pas, suivez ce lien et complétez l'autorisation:<br>%1\"\nYouTube.AuthError.Text=\"Impossible de récupérer les informations de la chaîne : %1.\"\nYouTube.Actions.WindowTitle=\"Configuration de la diffusion YouTube - Chaîne : %1\"\nYouTube.Actions.CreateNewEvent=\"Créer une nouvelle diffusion\"\nYouTube.Actions.ChooseEvent=\"Sélectionner une diffusion existante\"\nYouTube.Actions.Title=\"Titre*\"\nYouTube.Actions.MyBroadcast=\"Ma diffusion\"\nYouTube.Actions.Privacy=\"Confidentialité*\"\nYouTube.Actions.Privacy.Private=\"Privée\"\nYouTube.Actions.Privacy.Public=\"Publique\"\nYouTube.Actions.Privacy.Unlisted=\"Non répertoriée\"\nYouTube.Actions.Category=\"Catégorie\"\nYouTube.Actions.Thumbnail=\"Miniature\"\nYouTube.Actions.Thumbnail.SelectFile=\"Sélectionner un fichier...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Aucun fichier sélectionné\"\nYouTube.Actions.Thumbnail.ClearFile=\"Supprimer\"\nYouTube.Actions.MadeForKids=\"Cette vidéo convient-elle pour les enfants ?*\"\nYouTube.Actions.MadeForKids.Yes=\"Oui, elle convient aux enfants\"\nYouTube.Actions.MadeForKids.No=\"Non, elle ne convient pas pour les enfants\"\nYouTube.Actions.AdditionalSettings=\"Paramètres supplémentaires\"\nYouTube.Actions.Latency=\"Latence\"\nYouTube.Actions.Latency.Normal=\"Normale\"\nYouTube.Actions.Latency.Low=\"Faible\"\nYouTube.Actions.Latency.UltraLow=\"Ultra faible\"\nYouTube.Actions.EnableAutoStart=\"Activer le démarrage automatique\"\nYouTube.Actions.EnableAutoStop=\"Activer l'arrêt automatique\"\nYouTube.Actions.AutoStartStop.TT=\"Indique si cette diffusion planifiée doit démarrer automatiquement\"\nYouTube.Actions.EnableDVR=\"Activer le contrôle du direct\"\nYouTube.Actions.360Video=\"Vidéo 360°\"\nYouTube.Actions.ScheduleForLater=\"Planifier pour plus tard\"\nYouTube.Actions.RememberSettings=\"Mémoriser ces réglages\"\nYouTube.Actions.Create_Ready=\"Créer une diffusion\"\nYouTube.Actions.Create_GoLive=\"Créer une diffusion et commencer la diffusion\"\nYouTube.Actions.Choose_Ready=\"Sélectionner une diffusion\"\nYouTube.Actions.Choose_GoLive=\"Sélectionnez la diffusion et démarrez le streaming\"\nYouTube.Actions.Create_Schedule=\"Programmer la diffusion\"\nYouTube.Actions.Create_Schedule_Ready=\"Programmer et sélectionner la diffusion\"\nYouTube.Actions.Dashboard=\"Ouvrir le studio YouTube\"\nYouTube.Actions.Error.Title=\"Erreur de création de la diffusion en direct\"\nYouTube.Actions.Error.Text=\"Erreur d'accès YouTube '%1'.<br/>Une description détaillée de l'erreur peut être trouvée sur <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Erreur d'accès YouTube. Veuillez vérifier votre connexion réseau ou l'accès au serveurs de YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Erreur de création de la diffusion '%1'.<br/>Une description détaillée de l'erreur peut être trouvée sur <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Aucune diffusion créée. Veuillez reconnecter votre compte.\"\nYouTube.Actions.Error.YouTubeApi=\"Erreur API YouTube. Veuillez consulter le fichier journal pour plus d'informations.\"\nYouTube.Actions.Error.BroadcastNotFound=\"La diffusion sélectionnée n'a pas été trouvée.\"\nYouTube.Actions.Error.FileMissing=\"Le fichier sélectionné n'existe pas\"\nYouTube.Actions.Error.FileOpeningFailed=\"Impossible d'ouvrir le fichier sélectionné.\"\nYouTube.Actions.Error.FileTooLarge=\"Le fichier sélectionné est trop volumineux (limite : 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"La transition de la diffusion a échoué : %1<br/><br/>Si cette erreur persiste <a href='https://studio.youtube.com/video/%2/livestreaming'>ouvrez la diffusion dans YouTube Studio</a> et essayez manuellement.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"La diffusion est en cours de transition vers l'étape de test, cela peut prendre un certain temps. Veuillez réessayer dans 10-30 secondes.\"\nYouTube.Actions.EventsLoading=\"Chargement de la liste des évènements...\"\nYouTube.Actions.EventCreated.Title=\"Évènement créé.\"\nYouTube.Actions.EventCreated.Text=\"Évènement créé avec succès.\"\nYouTube.Actions.Stream=\"Diffuser\"\nYouTube.Actions.Stream.ScheduledFor=\"Planifié pour %1\"\nYouTube.Actions.Stream.Resume=\"Reprendre le flux interrompu\"\nYouTube.Actions.Stream.YTStudio=\"Créé automatiquement par YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Création d'une nouvelle diffusion en direct, veuillez patienter...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Démarrage manuel requis\"\nYouTube.Actions.AutoStartStreamingWarning=\"Le démarrage automatique est désactivé pour cet événement, cliquez sur \\\"Passer en direct\\\" pour démarrer votre diffusion.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Vous ne pourrez pas vous reconnecter.<br>Votre diffusion s'arrêtera et vous ne serez plus en direct.\"\nYouTube.Errors.liveStreamingNotEnabled=\"La diffusion en direct n'est pas activée sur la chaîne YouTube sélectionnée.<br/><br/>Visitez <a href='https://www.youtube.com/features'>youtube.com/features</a> pour plus d'informations.\"\nYouTube.Errors.livePermissionBlocked=\"La diffusion en direct n'est pas disponible sur la chaîne YouTube sélectionnée.<br/>Veuillez noter que cela peut prendre jusqu'à 24 heures pour que la diffusion en direct devienne disponible après l'activation dans vos paramètres de chaîne.<br/><br/>Visitez <a href='https://www.youtube.com/features'>youtube.com/features</a> pour plus de détails.\"\nYouTube.Errors.errorExecutingTransition=\"La transition a échoué en raison d'une erreur dans l'infrastructure. Veuillez réessayer dans quelques secondes.\"\nYouTube.Errors.errorStreamInactive=\"YouTube ne reçoit pas de données pour votre stream. Veuillez vérifier votre configuration et réessayer.\"\nYouTube.Errors.invalidTransition=\"La transition tentée était invalide. Cela peut être dû au fait que le stream n'a pas terminé une transition précédente. Veuillez patienter quelques secondes et réessayer.\"\nYouTube.DocksRemoval.Title=\"Effacer les anciens Docks Internet de YouTube\"\nYouTube.DocksRemoval.Text=\"Ces Docks Internet seront enlevés car obsolètes :\\n\\n%1\\nUtilisez les \\\"Docks/YouTube Live Control Room\\\" à la place.\"\nConfigDownload.WarningMessageTitle=\"Avertissement\"\nFailedToStartStream.MissingConfigURL=\"Aucune URL de configuration disponible pour ce service\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"URL RTMP personnalisée non spécifiée\"\nFailedToStartStream.InvalidCustomConfig=\"Configuration personnalisée invalide\"\nFailedToStartStream.MissingCanvas=\"Il manque un canevas configuré supplémentaire\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Échec de la création du service vidéo multipiste\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Erreur lors de la création de piste vidéo multiple avec un flux de sortie RTMP\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC non disponible.\\n\\nÉchec de la recherche du type d'encodeur '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Échec de la création de l'encodeur vidéo '%1' (type : '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Erreur lors de l'acquisition de la vidéo pendant la création de l'encodeur '%1' (type: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Échec de la création de l’encodeur audio\"\nFailedToStartStream.NoRTMPURLInConfig=\"La configuration ne contient pas l'URL RTMP(S) cible du flux\"\nFailedToStartStream.FallbackToDefault=\"Le démarrage du flux à l’aide de %1 a échoué. Voulez-vous réessayer en utilisant les paramètres d’encodage unique ?\"\nFailedToStartStream.ConfigRequestFailed=\"Impossible de récupérer la configuration à partir de %1<br><br>Erreur HTTP : %2\"\nFailedToStartStream.WarningUnknownStatus=\"Valeur d'état inconnue reçue '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nVoulez-vous continuer la diffusion sans %1 ?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nVoulez-vous continuer la diffusion ?\"\nFailedToStartStream.MissingEncoderConfigs=\"La configuration de passage en direct n’incluait pas les configurations d'encodeur\"\nFailedToStartStream.InvalidEncoderConfig=\"La configuration de passage en direct n’incluait pas les configurations d'encodeur\"\nFailedToStartStream.StatusMissingHTML=\"La demande de passage en direct a renvoyé une erreur non spécifiée\"\nFailedToStartStream.NoConfigSupplied=\"Configuration manquante\"\nMultitrackVideo.Info=\"%1 optimise automatiquement vos paramètres pour encoder et envoyer plusieurs qualités vidéo. La sélection de cette option enverra %2 des informations sur votre ordinateur et la configuration de vos logiciels.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Paramètres Incompatibles\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 n'est actuellement pas compatible avec :\\n\\n%2\\nPour continuer la diffusion avec %1, désactivez les paramètres incompatibles :\\n\\n%3\\net redémarrez la diffusion.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Désactiver pour ce flux et démarrer la diffusion\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Mettre à jour les paramètres et démarrer la diffusion\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 n'est pas compatible avec [Audio → Général → Chaînes] paramétré pour '%2', '%3'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Audio → Général → Canaux] doit être défini sur « %1 »\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 nécessite plusieurs paramètres différents pour [Audio → Général → Chaînes]\"\nBasic.OpenPluginManager=\"Gestionnaire de plugins\"\nPluginManager=\"Gestionnaire de plugins\"\nPluginManager.Restart=\"OBS Studio doit être redémarré pour applique les modifications de plugin\"\nPluginManager.MissingPlugin=\"[PLUGIN INTROUVABLE]\"\nPluginManager.Section.Discover=\"Parcourir\"\nPluginManager.Section.Manage=\"Installé\"\nPluginManager.Section.Updates=\"Mises à jour\"\nPluginManager.Section.Manage.Title=\"Gestion des plugins activés\"\n"
  },
  {
    "path": "frontend/data/locale/gd-GB.ini",
    "content": "Language=\"Gàidhlig\"\nOK=\"Ceart ma-thà\"\nApply=\"Cuir an sàs\"\nCancel=\"Sguir dheth\"\nClose=\"Dùin\"\nSave=\"Sàbhail\"\nDiscard=\"Tilg air falbh\"\nDisable=\"Cuir à comas\"\nYes=\"Tha\"\nNo=\"Chan eil\"\nAdd=\"Cuir ris\"\nRemove=\"Thoir air falbh\"\nRename=\"Thoir ainm ùr air\"\nInteract=\"Eadar-ghnìomhach\"\nFilters=\"Criathragan\"\nProperties=\"Roghainnean\"\nMoveUp=\"Gluais suas\"\nMoveDown=\"Gluais sìos\"\nSettings=\"Roghainnean\"\nDisplay=\"Uidheam-taisbeanaidh\"\nName=\"Ainm\"\nExit=\"Fàg an-seo\"\nMixer=\"Measgadair fuaime\"\nBrowse=\"Rùraich\"\nDroppedFrames=\"Frèaman a thuit: %1 (%2%)\"\nProjector.Open.Program=\"Proiseactair Prògram Fosgailte\"\nProjector.Open.Preview=\"Fosgail am Proiseactair Ro-shealladh\"\nProjector.Open.Scene=\"Proiseactair Seallaidh Fosgailte\"\nProjector.Open.Source=\"Proiseactair Stòr Fosgailte\"\nProjector.Open.Multiview=\"Fosgail Ioma-shealladh\"\nProjector.Window=\"Uinneag ùr\"\nProjector.Title=\"Pròiseactair\"\nProjector.Title.Scene=\"Sealladh:%1\"\nProjector.Title.Source=\"Stòr: %1\"\nProjector.Title.Multiview=\"Ioma-shealladh\"\nProjector.ResizeWindowToContent=\"Freagarraich an uinneag ris an t-susbaint\"\nClear=\"Falamhaich\"\nRevert=\"Till\"\nShow=\"Seall\"\nHide=\"Falaich\"\nUnhideAll=\"Seall na h-uile\"\nUntitled=\"Gun tiotal\"\nNew=\"Ùr\"\nDuplicate=\"Dùblaich\"\nEnable=\"Cuir an comas\"\nDisableOSXVSync=\"Cuir à comas sioncronachadh-V air macOS\"\nResetOSXVSyncOnExit=\"Ath-shuidhich sioncronachadh-V air macOS nuar a dh’fhàgar\"\nHighResourceUsage=\"Tha an còdachadh ro thrang! Feuch an tagh thu roghainnean video nas ìsle no an cleachd thu ro-sheata còdachaidh nas luaithe.\"\nTransition=\"Tar-mhùthadh\"\nQuickTransitions=\"Tar-mhùthaidhean luatha\"\nFadeToBlack=\"Crìon gu dubh\"\nLeft=\"Clì\"\nRight=\"Deas\"\nTop=\"Bàrr\"\nBottom=\"Bonn\"\nReset=\"Ath-shuidhich\"\nHours=\"Uair\"\nMinutes=\"Mionaid\"\nSeconds=\"Diog\"\nDeprecated=\"Cha mholar seo tuilleadh\"\nReplayBuffer=\"Bufair na h-ath-chluiche\"\nImport=\"Ion-phortaich\"\nExport=\"Às-phortaich\"\nCopy=\"Dèan lethbhreac\"\nPaste=\"Cuir ann\"\nPasteReference=\"Cuir ann (iomradh)\"\nPasteDuplicate=\"Cuir ann (dùblachadh)\"\nRemuxRecordings=\"Iompaich clàraidhean\"\nNext=\"Air adhart\"\nBack=\"Air ais\"\nDefaults=\"Bun-roghainnean\"\nRestoreDefaults=\"Bun-roghainnean\"\nHideMixer=\"Falaich sa mheasgadair\"\nTransitionOverride=\"Tar-àithneadh an tar-mhùthaidh\"\nShowTransition=\"Tar-mhùthadh a-steach\"\nHideTransition=\"Tar-mhùthadh a-mach\"\nNone=\"Chan eil gin\"\nStudioMode.Preview=\"Ro-shealladh\"\nStudioMode.Program=\"Prògram\"\nStudioMode.PreviewSceneName=\"Ro-sheall: %1\"\nStudioMode.ProgramSceneName=\"Prògram: %1\"\nShowInMultiview=\"Seall san ioma-shealladh\"\nVerticalLayout=\"Co-dhealbhachd inghearach\"\nGroup=\"Buidhnich\"\nDoNotShowAgain=\"Na seall seo a-rithist\"\nDefault=\"(Bun-roghainn)\"\nCalculating=\"’Ga àireamhachadh…\"\nFullscreen=\"Làn-sgrìn\"\nWindowed=\"Uinneagach\"\nRefreshBrowser=\"Ath-nuadhaich\"\nAspectRatio=\"Co-mheas an deilbh <b>%1:%2</b>\"\nLockVolume=\"Glais àirde na fuaime\"\nLogViewer=\"Sealladair logaichean\"\nShowOnStartup=\"Seall aig an toiseach\"\nOpenFile=\"Fosgail am faidhle\"\nAddScene=\"Cuir sealladh ris\"\nAddSource=\"Cuir na Tùs\"\nAlreadyRunning.Title=\"Tha OBS ’ga ruith mar-thà\"\nAlreadyRunning.Text=\"Tha OBS ’ga ruith mar-thà! Mur ann gun robh thu airson seo a dhèanamh, dùin sìos gach ionstans de dh’OBS mus fheuch thu ri ionstans eile dheth a ruith. Ma shuidhich thu OBS ach an dèid fhìor-lùghdachadh gu treidhe an t-siostaim thoir sùil a bheil e ’ga ruith an-siud fhathast.\"\nAlreadyRunning.LaunchAnyway=\"Cuir gu dol e co-dhiù\"\nChromeOS.Title=\"Ùrlar ris nach cuir sinn taic\"\nDockCloseWarning.Title=\"A’ dùnadh uinneag doca\"\nExtraBrowsers=\"Docaichean brabhsair gnàthaichte\"\nExtraBrowsers.Info=\"Cuir docaichean ris le ainm is URL agus briog air “Cuir an sàs” no “Dùin” an uairsin airson na docaichean fhosgladh. ’S urrainn dhut docaichean a chur ris no a thoirt air falbh uair sam bith.\"\nExtraBrowsers.DockName=\"Ainm an doca\"\nAuth.Authing.Title=\"’Gad dhearbhadh…\"\nAuth.Authing.Text=\"’Gad dhearbhadh le %1, fuirich greiseag…\"\nAuth.AuthFailure.Title=\"Dh’fhàillig an dearbhadh\"\nAuth.AuthFailure.Text=\"Dh’fhàillig an dearbhadh le %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Feum air dearbhadh\"\nAuth.InvalidScope.Text=\"Chaidh riatanasan dearbhaidh air %1 atharrachadh. Dh’fhaoidte nach bi gach gleus ri làimh dhut.\"\nAuth.LoadingChannel.Title=\"A’ luchdadh fiosrachadh an t-seanail…\"\nAuth.LoadingChannel.Text=\"A’ luchdadh fiosrachadh an t-seanail airson %1, fuirich greiseag…\"\nAuth.LoadingChannel.Error=\"Cha b’ urrainn dhuinn fiosrachadh an t-seanail fhaighinn.\"\nAuth.ChannelFailure.Title=\"Dh’fhàillig le luchdadh an t-seanail\"\nAuth.ChannelFailure.Text=\"Dh’fhàillig le luchdadh fiosrachadh an t-seanail airson %1\\n\\n%2: %3\"\nAuth.Chat=\"Cabadaich\"\nAuth.StreamInfo=\"Fiosrachadh an t-sruthaidh\"\nTwitchAuth.Stats=\"Stadastaireachd Twitch\"\nTwitchAuth.Feed=\"Inbhir gnìomhachd Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Cha d’ fhuair sinn grèim air iuchair an inbhir\"\nTwitchAuth.TwoFactorFail.Text=\"Cha b’ urrainn dha OBS ceangal ris a’ chunntas Twitch agad. Dèan cinnteach gun deach an dearbhadh dà-cheumnach a shuidheachadh sna <a href='https://www.twitch.tv/settings/security'>roghainnean tèarainteachd Twitch</a> agad on a bhios feum air sin mus fhaod tu sruthadh.\"\nRestreamAuth.Channels=\"Seanailean Restream\"\nCopy.Filters=\"Dèan lethbhreac dhe na criathragan\"\nPaste.Filters=\"Cuir ann criathragan\"\nBrowserPanelInit.Title=\"A’ tòiseachadh a’ bhrabhsair…\"\nBrowserPanelInit.Text=\"A’ tòiseachadh a’ bhrabhsair, fuirich greiseag…\"\nBandwidthTest.Region=\"Roinn-dùthcha\"\nBandwidthTest.Region.US=\"Na Stàitean Aonaichte\"\nBandwidthTest.Region.EU=\"An Roinn-Eòrpa\"\nBandwidthTest.Region.Asia=\"Àisia\"\nBandwidthTest.Region.Other=\"Roinn-dùthcha eile\"\nBasic.AutoConfig=\"Draoidh an fhèin-rèiteachaidh\"\nBasic.AutoConfig.ApplySettings=\"Cuir na roghainnean an sàs\"\nBasic.AutoConfig.StartPage=\"Fiosrachadh a’ chleachdaidh\"\nBasic.AutoConfig.StartPage.SubTitle=\"Sònraich na h-adhbharan air an cleachd thu am prògram\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Gleus airson sruthadh is chan eil clàradh cho cudromach sin\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Gleus airson clàradh a-mhàin, cha dèan mi sruthadh\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Cha bhios mi a’ cleachdadh ach an camara biortail\"\nBasic.AutoConfig.VideoPage=\"Roghainnean video\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Sònraich na roghainnean video a bu toigh leat cleachdadh\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"An roghainn làithreach (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Uidheam-taisbeanaidh %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"An roghainn làithreach (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"An dà chuid 60 no 30 ach b’ fheàrr leam 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"An dà chuid 60 no 30 ach b’ fheàrr leam dùmhlachd-bhreacaidh àrd\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"An aire: Cha bhi dùmhlachd-bhreacaidh bhunasach (a’ chanabhais) co-ionnan ris an dùmhlachd-bhreacaidh a thèid a chlàradh no a shruthadh an-còmhnaidh. Dh’fhaoidte gun dèid an dùmhlachd-bhreacaidh air sruthadh no clàradh ìsleachadh o dhùmhlachd-bhreacaidh a’ chanabhais airson freagairt ri feumalachdan cleachdaidh no reat bhiodan.\"\nBasic.AutoConfig.StreamPage=\"Fiosrachadh an t-sruthaidh\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Cuid a-steach fiosrachadh an t-sruthaidh agad\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Ceangail an cunntas agad (mholamaid seo)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Bris an ceangal ris a’ chunntas\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"A bheil thu airson an ceangal ris a’ chunntas a bhriseadh?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Thèid an t-atharradh seo a chur an sàs sa bhad. A bheil thu cinnteach gu bheil thu airson an ceangal ris a’ chunntas agad a bhriseadh?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Faigh iuchair sruthaidh\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Barrachd fiosrachaidh\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Cleachd iuchair sruthaidh\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Cleachd iuchair sruthaidh (adhartach)\"\nBasic.AutoConfig.StreamPage.Service=\"Seirbheis\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Seall na h-uile…\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Gnàthaichte…\"\nBasic.AutoConfig.StreamPage.Server=\"Frithealaiche\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Iuchair an t-sruthaidh\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Iuchair an inneil-chòdachaidh\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Cunntas ceangailte\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Dèan tuairmse air reat bhiodan le deuchainn air an leud-bhanna (dh’fhaoidte gun doir seo mionaid no dhà)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"B’ fheàrr leam còdachadh bathair-chruthaidh\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Le còdachadh bathair-chruaidh, cha dèid ach glè bheag dhen CPU a chleachdadh ach dh’fhaoidte gum bi feum air reat bhiodan nas àirde airson ann aon ìre a chàileachd fhaighinn.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Rabhadh sruthaidh\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Tha an deuchainn air an leud-bhanna gu bhith dàta video tuaireamach gun fhuaim a shruthadh dhan t-seanail agad. Ma ghabhas seo a dhèanamh, mholamaid gun cuir thu dheth sàbhaladh sruthaidh video agus gun dèan thu an sruth agad prìobhaideach gus am bi an deuchainn deiseil. A bheil thu airson leantainn air adhart?\"\nBasic.AutoConfig.TestPage=\"Na toraidhean deireannach\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Tha am prògram a’ ruith deuchainnean airson tuairmse a dhèanamh air na roghainnean as fheàrr\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Tha an deuchainn deiseil\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"A’ cur an leud-bhanna fo dheuchainn, dh’fhaoidte gun doir seo mionaid no dhà…\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"A’ ceangal ri: %1…\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Cha deach leinn ceangal a dhèanamh ri frithealaiche sam bith, thoir sùil air a’ cheangal agad dhan eadar-lìon is feuch ris a-rithist an uairsin.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"A’ cur an leud-bhanna fo dheuchainn airson: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"A’ cur inneal-còdachaidh an t-sruthaidh fo dheuchainn, dh’fhaoidte gun doir seo mionaid no dhà…\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"A’ cur inneal-còdachaidh a’ chlàraidh fo dheuchainn, dh’fhaoidte gun doir seo mionaid no dhà…\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Cha deach leinn an t-inneal-còdachaidh a chur gu dol\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"A’ feuchainn %1x%2 %3 FPS…\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Inneal-còdachaidh an t-sruthaidh\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Inneal-còdachaidh a’ chlàraidh\"\nBasic.AutoConfig.TestPage.Result.Header=\"Rinn am prògram tuairmse gur e seo na roghainnean as fheàrr dhut:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Airson na roghainnean seo a chleachdadh, briog air “Cuir na roghainnean an sàs”. Airson an draoidh a rèiteachadh às ùr is fheuchainn is a-rithist, briog air “Air ais”. Airson na roghainnean a rèiteachadh a làimh, briog air “Sguir dheth” is fosgail na “Roghainnean”.\"\nBasic.AutoConfig.Info=\"Cuiridh draoidh an rèiteachaidh fhèin-obrachail roimhe dè na roghainnean as fheàrr dhut, stèidhichte air comasan a’ choimpiutair agad agus luaths an eadar-lìn.\"\nBasic.AutoConfig.RunAnytime=\"’S urrainn dhut seo a ruith uair sam bith às a’ chlàir-thaice “Innealan”.\"\nBasic.Stats=\"Stadastaireachd\"\nBasic.Stats.CPUUsage=\"Cleachdadh a’ CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Àite ri fhaighinn air an diosg\"\nBasic.Stats.MemoryUsage=\"Cleachdadh a’ chuimhne\"\nBasic.Stats.AverageTimeToRender=\"Ùine cuibheasach air reandaradh frèama\"\nBasic.Stats.SkippedFrames=\"Na frèaman air an leigeil seachad ri linn dàil còdachaidh\"\nBasic.Stats.MissedFrames=\"Na frèaman a chaidh a dhìth ri linn dàil còdachaidh\"\nBasic.Stats.Output.Stream=\"Sruthadh\"\nBasic.Stats.Output.Recording=\"Clàradh\"\nBasic.Stats.Status=\"Staid\"\nBasic.Stats.Status.Recording=\"Clàradh\"\nBasic.Stats.Status.Live=\"BEÒ\"\nBasic.Stats.Status.Reconnecting=\"’Ga ath-cheangal\"\nBasic.Stats.Status.Inactive=\"Neo-ghnìomhach\"\nBasic.Stats.Status.Active=\"Gnìomhach\"\nBasic.Stats.DroppedFrames=\"Frèaman a thuit: (lìonra)\"\nBasic.Stats.MegabytesSent=\"Às-chur dàta iomlan\"\nBasic.Stats.Bitrate=\"Reat bhiodan\"\nBasic.Stats.DiskFullIn=\"Bidh an diosg làn an ceann mu\"\nBasic.Stats.ResetStats=\"Ath-shuidhich an stadastaireachd\"\nResetUIWarning.Title=\"A bheil thu cinnteach gu bheil thu airson an eadar-aghaidh ath-shuidheachadh?\"\nUpdater.Title=\"Tha ùrachadh ri fhaighinn\"\nUpdater.Text=\"Tha ùrachadh ri fhaighinn:\"\nUpdater.UpdateNow=\"Ùraich an-dràsta\"\nUpdater.RemindMeLater=\"Cuir nam chuimhne uaireigin eile\"\nUpdater.Skip=\"Leig seachad an tionndadh\"\nUpdater.NoUpdatesAvailable.Title=\"Chan eil ùrachadh ri fhaighinn\"\nUpdater.NoUpdatesAvailable.Text=\"Chan eil ùrachadh ri fhaighinn an-dràsta\"\nUpdater.FailedToLaunch=\"Cha deach leinn an t-inneal-ùrachaidh a chur gu dol\"\nQuickTransitions.DuplicateScene=\"Dùblaich an t-sealladh\"\nQuickTransitions.EditProperties=\"Dùblaich na tùsan\"\nQuickTransitions.HotkeyName=\"Tar-mhùthadh luath: %1\"\nBasic.AddTransition=\"Cuir ris tar-mhùthadh a ghabhas rèiteachadh\"\nBasic.RemoveTransition=\"Thoir air falbh tar-mhùthadh a ghabhas rèiteachadh\"\nBasic.TransitionProperties=\"Roghainnean an tar-mhùthaidh\"\nBasic.SceneTransitions=\"Tar-mhùthaidhean an t-seallaidh\"\nBasic.TransitionDuration=\"Faide\"\nBasic.TogglePreviewProgramMode=\"Modh stiùideo\"\nUndo.Undo=\"Neo-dhèan\"\nUndo.Redo=\"Ath-dhèan\"\nUndo.Add=\"Cur “%1” ris\"\nUndo.Delete=\"Sguabadh às “%1”\"\nUndo.Rename=\"Toirt ainm ùr air “%1”\"\nUndo.SceneCollection.Switch=\"Gearradh leum gu “%1”\"\nUndo.Item.Undo=\"Neo-dhèan %1\"\nUndo.Item.Redo=\"Ath-dhèan %1\"\nUndo.Sources.Multi=\"Sguabadh às tùsan (%1)\"\nUndo.Filters=\"Atharrachadh na criathraige air %1\"\nUndo.Filters.Paste.Single=\"Cur ann chriathraige o “%1” air “%2”\"\nUndo.Filters.Paste.Multiple=\"Lethbhreac dhe na criathragan o “%1” air “%2”\"\nUndo.Transform=\"Tar-mhùthadh thùsan air “'%1”\"\nUndo.Transform.Paste=\"Cur ann tar-mhùthaidh air “%1”\"\nUndo.Transform.Rotate=\"Cuairteachadh air “%1”\"\nUndo.Transform.Reset=\"Ath-shuidheachadh tar-mhùthaidh air “%1”\"\nUndo.Transform.HFlip=\"Flip còmhnard air “%1”\"\nUndo.Transform.VFlip=\"Flip inghearach air “%1”\"\nUndo.Transform.FitToScreen=\"Co-fhreagairt dhan sgrìn air “%1”\"\nUndo.Transform.StretchToScreen=\"Sìneadh dhan sgrìn air “%1”\"\nUndo.Transform.Center=\"Meadhanachadh “%1” air an sgrìn\"\nUndo.Transform.VCenter=\"Meadhanachadh “%1” air an sgrìn gu h-inghearach\"\nUndo.Transform.HCenter=\"Meadhanachadh “%1” air an sgrìn air a’ chòmhnard\"\nUndo.Volume.Change=\"Atharrachadh àirde na fuaime air “%1”\"\nUndo.Volume.Mute=\"Mùchadh “%1”\"\nUndo.Volume.Unmute=\"Dì-mhùchach “%1”\"\nUndo.Balance.Change=\"Atharrachadh balans na fuaime air “%1”\"\nUndo.SyncOffset.Change=\"Atharrachadh frith-àireamh an t-sioncronachaidh air “%1”\"\nUndo.MonitoringType.Change=\"Atharrachadh sgrùdadh fuaime air “%1”\"\nUndo.Mixers.Change=\"Atharrachadh nam measgadairean fuaime air “%1”\"\nUndo.ForceMono.On=\"Cur an comas sparradh mono air “%1”\"\nUndo.ForceMono.Off=\"Cur à comas sparradh mono air “%1”\"\nUndo.Properties=\"Atharrachadh roghainn air “%1”\"\nUndo.Scene.Duplicate=\"Dùblachadh seallaidh “%1”\"\nUndo.ShowTransition=\"Tar-mhùthadh a-steach air “%1”\"\nUndo.HideTransition=\"Tar-mhùthadh a-mach air “%1”\"\nUndo.ShowSceneItem=\"Sealladh “%1” am broinn “%2”\"\nUndo.HideSceneItem=\"Falach “%1” am broinn “%2”\"\nUndo.ReorderSources=\"Òrdugh nan tùsan air “%1”\"\nUndo.MoveUp=\"Gluasad “%1” suas am broinn “%2”\"\nUndo.MoveDown=\"Gluasad “%1” sìos am broinn “%2”\"\nUndo.MoveToTop=\"Gluasad “%1” dhan bhàrr am broinn “%2”\"\nUndo.MoveToBottom=\"Gluasad “%1” dhan bhonn am broinn “%2”\"\nUndo.PasteSource=\"Cur ann thùsan air “%1”\"\nUndo.PasteSourceRef=\"Cur ann iomraidhean air tùsan air “%1”\"\nUndo.GroupItems=\"Buidhneachadh nithean gu “%1”\"\nTransitionNameDlg.Text=\"Cuir a-steach ainm an tar-mhùthaidh\"\nTransitionNameDlg.Title=\"Ainm an tar-mhùthaidh\"\nTitleBar.Profile=\"Pròifil\"\nTitleBar.Scenes=\"Seallaidhean\"\nNameExists.Title=\"Tha an t-ainm ann mu thràth\"\nNameExists.Text=\"Tha an t-ainm seo ’ga chleachdadh mu thràth.\"\nNoNameEntered.Title=\"Cuir a-steach ainm dligheach\"\nNoNameEntered.Text=\"Chan urrainn dhut ainm falamh a chleachdadh.\"\nConfirmStart.Title=\"A bheil thu airson tòiseachadh air sruthadh?\"\nConfirmStart.Text=\"A bheil thu cinnteach gu bheil thu airson tòiseachadh air an t-sruthadh?\"\nConfirmStop.Title=\"A bheil thu airson stad a chur air an t-sruthadh?\"\nConfirmStop.Text=\"A bheil thu cinnteach gu bheil thu airson stad a chur air an t-sruthadh?\"\nConfirmStopRecord.Title=\"A bheil thu airson sgur dhen chlàradh?\"\nConfirmStopRecord.Text=\"A bheil thu cinnteach gu bheil thu airson sgur dhen chlàradh?\"\nConfirmBWTest.Title=\"A bheil thu airson deuchainn an leud banna a thòiseachadh?\"\nConfirmBWTest.Text=\"Rèitich thu OBS sa mhodh deuchainn leud banna. Leigidh am modh seo leat an lìonra a chur fo dheuchainn gun a bhith tighinn beò air an t-seanail agad. Nuair a bhios tu deiseil leis an deuchainn, feumaidh tu a chur à comas ach am faic an amharc an sruthadh agad.\\n\\nA bheil thu airson leantainn air adhart?\"\nConfirmRemove.Title=\"Dearbh an toirt air falbh\"\nConfirmRemove.TextMultiple=\"A bheil thu cinnteach gu bheil thu airson %1 nithean a thoirt air falbh?\"\nOutput.StartStreamFailed=\"Cha deach leinn tòiseachadh air an t-sruthadh\"\nOutput.StartRecordingFailed=\"Cha deach leinn tòiseachadh air a’ chlàradh\"\nOutput.StartReplayFailed=\"Cha deach leinn tòiseachadh air bufair na h-ath-chluiche\"\nOutput.StartFailedGeneric=\"Cha deach leinn tòiseachadh air an às-chur. Thoir sùil air an loga airson barrachd fiosrachaidh.\\n\\nAn aire: Ma tha thu a’ cleachdadh inneal-còdachaidh NVENC no AMD, dèan cinneach gu bheil na draibhearan video agad cho ùr ’s a ghabhas.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Cha ghabh ath-chluichean a shàbhaladh fhad ’s a bhios e ’na stad\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Rabhadh: Cha ghabh ath-chluichean a shàbhaladh fhad ’s a bhios an clàradh ’na stad.\"\nOutput.ConnectFail.Title=\"Cha deach leinn ceangal a dhèanamh\"\nOutput.ConnectFail.BadPath=\"Tha slighe no URL a’ cheangail mì-dhligheach. Thoir sùil air na roghainnean agad feuch a bheil iad mar bu chòir.\"\nOutput.ConnectFail.ConnectFailed=\"Dh’fhàillig an ceangal ris an fhrithealaiche\"\nOutput.ConnectFail.InvalidStream=\"Bha b’ urrainn dhuinn an t-seanail no an iuchair sruthaidh a shònraich thu inntrigeadh, dearbhaidh an iuchair sruthaidh agad. Ma tha i ceart, dh’fhaoidte gu bheil duilgheadas ann le ceangal ris an fhrithealaiche.\"\nOutput.ConnectFail.Error=\"Thachair mearachd ris nach robh dùil nuair a dh’fheuch sinn ri ceangal ris an fhrithealaiche. Tha barrachd fiosrachaidh ann am faidhle an loga.\"\nOutput.ConnectFail.Disconnected=\"Chaidh an ceangal dhan fhrithealaiche a bhriseadh.\"\nOutput.StreamEncodeError.Title=\"Mearachd còdachaidh\"\nOutput.StreamEncodeError.Msg=\"Thachair mearachd còdachaidh rè an t-sruthaidh.\"\nOutput.RecordFail.Title=\"Cha deach leinn tòiseachadh air a’ chlàradh\"\nOutput.RecordFail.Unsupported=\"Cha chuirear taic ri fòrmat an às-chuir no cha chuir e taic ri corr is aon traca fuaime. Thoir sùil air na roghainnean agad is feuch ris a-rithist.\"\nOutput.RecordNoSpace.Title=\"Chan eil àite gu leòr air an diosga\"\nOutput.RecordNoSpace.Msg=\"Chan eil àite gu leòr air an diosga airson leantainn air adhart leis a’ chlàradh.\"\nOutput.RecordError.Title=\"Mearachd clàraidh\"\nOutput.RecordError.Msg=\"Thachair mearachd nach deach a shònrachadh rè a’ chlàraidh.\"\nOutput.RecordError.EncodeErrorMsg=\"Thachair mearachd còdachaidh rè a’ chlàraidh.\"\nOutput.BadPath.Title=\"Droch-shlighe faidhle\"\nOutput.NoBroadcast.Title=\"Cha deach craoladh a rèiteachadh\"\nOutput.NoBroadcast.Text=\"Feumaidh tu craoladh a shuidheachadh mus urrainn dhut tòiseachadh air an t-sruthadh.\"\nOutput.BroadcastStartFailed=\"Cha deach leinn tòiseachadh air a’ chraoladh\"\nOutput.BroadcastStopFailed=\"Cha deach leinn stad a chur air a’ chraoladh\"\nRemux.SourceFile=\"Clàradh OBS\"\nRemux.TargetFile=\"Faidhle amais\"\nRemux.Remux=\"Iompaich\"\nRemux.Stop=\"Cuir stad air an iompachadh\"\nRemux.ClearFinished=\"Falamhaich na nithean coileanta\"\nRemux.ClearAll=\"Falamhaich a h-uile nì\"\nRemux.OBSRecording=\"Clàradh OBS\"\nRemux.FinishedTitle=\"Tha an t-iompachadh deiseil\"\nRemux.Finished=\"Chaidh an clàradh iompachadh\"\nRemux.FinishedError=\"Chaidh an clàradh iompachadh ach ’s ma dh’fhaoidte nach eil am faidhle coileanta\"\nRemux.SelectRecording=\"Tagh clàradh OBS…\"\nRemux.SelectTarget=\"Tagh faidhle amais…\"\nRemux.FileExistsTitle=\"Tha faidhlichean amais ann\"\nRemux.FileExists=\"Tha na faidhlichean amais seo ann mu thràth. A bheil thu airson an fheadhainn ùra a chur ’na àite?\"\nRemux.ExitUnfinishedTitle=\"’Ga iompachadh\"\nRemux.ExitUnfinished=\"Chan eil an t-iompachadh deiseil agus dh’fhaoidte nach gabh am faidhle amais a cleachdadh ma chuireas tu stad air an-dràsta.\\nA bheil thu cinnteach gu bheil thu airson stad a chur air an iompachadh?\"\nRemux.HelpText=\"Leig às faidhlichean san uinneag seo airson an iompachadh no tagh cealla “Clàradh OBS” falamh ach an dèan thu brabhsadh airson faidhle.\"\nMissingFiles=\"Faidhlichean a tha a dhìth\"\nMissingFiles.MissingFile=\"Faidhle a tha a dhìth\"\nMissingFiles.NewFile=\"Faidhle ùr\"\nMissingFiles.HelpText=\"Chaidh cuid a dh’fhaidhlichean a dhìth on turas mu dheireadh a dh’fhosgail thu OBS.\"\nMissingFiles.Clear=\"<falamh>\"\nMissingFiles.Search=\"Lorg ann am pasgan…\"\nMissingFiles.SelectFile=\"Tagh faidhle…\"\nMissingFiles.SelectDir=\"Tagh pasgan gus a lorg ann\"\nMissingFiles.State=\"Staid\"\nMissingFiles.Missing=\"A dhìth\"\nMissingFiles.Replaced=\"Ri cur ’na àite\"\nMissingFiles.Cleared=\"Falamh\"\nMissingFiles.Found=\"Chaidh a lorg\"\nMissingFiles.AutoSearch=\"Chaidh barrachd sheisean a lorg\"\nMissingFiles.AutoSearchText=\"Lorg OBS barrachd sheisean dha na faidhlichean a tha a dhìth sa phasgan seo. A bheil thu airson an cur ris?\"\nMissingFiles.NoMissing.Title=\"A’ sgrùdadh faidhlichean a dhìth\"\nMissingFiles.NoMissing.Text=\"Tha coltas nach eil faidhle a dhìth.\"\nMacPermissions.Item.Camera=\"Camara\"\nMacPermissions.Item.Microphone=\"Maicreafòn\"\nBasic.DesktopDevice1=\"Fuaim an deasg\"\nBasic.DesktopDevice2=\"Fuaim an deasg 2\"\nBasic.AuxDevice1=\"Micreofon/Taic\"\nBasic.AuxDevice2=\"Micreofon/Taic 2\"\nBasic.AuxDevice3=\"Micreofon/Taic 3\"\nBasic.AuxDevice4=\"Micreofon/Taic 4\"\nBasic.Scene=\"Sealladh\"\nBasic.DisplayCapture=\"Glacadh an uidheim-taisbeanaidh\"\nBasic.Main.PreviewConextMenu.Enable=\"Cuir an ro-shealladh an comas\"\nBasic.Main.Preview.Disable=\"Cuir an ro-shealladh à comas\"\nScaleFiltering=\"Criathradh sgèilidh\"\nScaleFiltering.Point=\"Puing\"\nScaleFiltering.Bilinear=\"Dà-loidhneach\"\nScaleFiltering.Bicubic=\"Dà-chiùbach\"\nScaleFiltering.Area=\"Raon\"\nBlendingMode.Additive=\"Cuir ris\"\nBlendingMode.Multiply=\"Iomadaich\"\nDeinterlacing=\"Dì-fhilleadh\"\nDeinterlacing.Discard=\"Tilg air falbh\"\nDeinterlacing.TopFieldFirst=\"An raon air a’ bhàrr an toiseach\"\nDeinterlacing.BottomFieldFirst=\"An raon aig a’ bhonn an toiseach\"\nVolControl.SliderUnmuted=\"Sleamhnachan àirde na fuaime airson “%1”:\"\nVolControl.SliderMuted=\"Sleamhnachan àirde na fuaime airson “%1”: (’ga mhùchadh)\"\nVolControl.Mute=\"Mùch “%1”\"\nVolControl.Properties=\"Roghainnean airson “%1”\"\nBasic.Main.AddSceneDlg.Title=\"Cuir sealladh ris\"\nBasic.Main.AddSceneDlg.Text=\"Cuir a-steach ainm an t-seallaidh\"\nBasic.Main.DefaultSceneName.Text=\"Sealladh %1\"\nBasic.Main.AddSceneCollection.Title=\"Cuir cruinneachadh sheallaidhean ris\"\nBasic.Main.AddSceneCollection.Text=\"Cuir a-steach ainm a’ chruinneachaidh sheallaidhean\"\nBasic.Main.RenameSceneCollection.Title=\"Thoir ainm ùr air a’ chruinneachadh sheallaidhean\"\nAddProfile.Title=\"Cuir pròifil ris\"\nAddProfile.Text=\"Cuir a-steach ainm na pròifil\"\nAddProfile.WizardCheckbox=\"Seall draoidh an fhèin-rèiteachaidh\"\nRenameProfile.Title=\"Thoir ainm ùr air a’ phròifil\"\nBasic.Main.MixerRename.Title=\"Thoir ainm ùr air an tùs fuaime\"\nBasic.Main.MixerRename.Text=\"Cuir a-steach ainm an tùis fuaime\"\nBasic.Main.PreviewDisabled=\"Tha an ro-shealladh às comas an-dràsta\"\nBasic.SourceSelect=\"Cruthaich/Tagh tùs\"\nBasic.SourceSelect.CreateNew=\"Cruthaich tùs ùr\"\nBasic.SourceSelect.AddExisting=\"Cuir ris tùs a tha ann\"\nBasic.SourceSelect.AddVisible=\"Seall an tùs\"\nBasic.Main.Sources.Visibility=\"Faicsinneachd\"\nBasic.Main.Sources.VisibilityDescription=\"Stiùirichidh seo an fhaicsinneachd aig “%1” air a’ chanabhas\"\nBasic.Main.Sources.Lock=\"Glais\"\nBasic.Main.Sources.LockDescription=\"Glaisidh seo an t-ionad agus an sgèile aig “%1” air a’ chanabhas\"\nBasic.PropertiesWindow=\"Roghainnean airson “%1”\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (taghadh fèin-obrachail: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Tagh dath\"\nBasic.PropertiesWindow.SelectFont=\"Tagh cruth-clò\"\nBasic.PropertiesWindow.ConfirmTitle=\"Chaidh na roghainnean atharrachadh\"\nBasic.PropertiesWindow.Confirm=\"Tha atharraichean gun sàbhaladh ann. A bheil thu airson an cumail?\"\nBasic.PropertiesWindow.NoProperties=\"Chan eil roghainn ann\"\nBasic.PropertiesWindow.AddFiles=\"Cuir faidhlichean ris\"\nBasic.PropertiesWindow.AddDir=\"Cuir pasgan ris\"\nBasic.PropertiesWindow.AddURL=\"Cuir slighe/URL ris\"\nBasic.PropertiesWindow.AddEditableListDir=\"Cuir pasgan ri “%1”\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Cuir faidhlichean ri “%1”\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Cuir innteart ri “%1”\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Deasaich innteart o “%1”\"\nBasic.PropertiesView.FPS.Simple=\"Luachan FPS simplidh\"\nBasic.PropertiesView.FPS.Rational=\"Luachan FPS reusanta\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Rainsean FPS dligheach:\"\nBasic.PropertiesView.UrlButton.Text=\"A bheil thu airson an ceangal seo fhosgladh sa bhrabhsair-lìn bhunaiteach agad?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Fosgail URL\"\nBasic.InteractionWindow=\"Conaltradh le “%1”\"\nBasic.StatusBar.Reconnecting=\"Gun cheangal, ’ga ath-cheangal an ceann %2 diog(an) (oidhirp %1)\"\nBasic.StatusBar.AttemptingReconnect=\"A’ feuchainn ri ath-cheangal… (oidhirp %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Chaidh ath-cheangal\"\nBasic.StatusBar.Delay=\"Dàil (%1d)\"\nBasic.StatusBar.DelayStartingIn=\"Dàil (a’ tòiseachadh an ceann %1d)\"\nBasic.StatusBar.DelayStoppingIn=\"Dàil (a’ stad an ceann %1d)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Dàil (a’ stad an ceann %1d, a’ tòiseachadh an ceann %2d)\"\nBasic.StatusBar.RecordingSavedTo=\"Chaidh an clàradh a shàbhaladh gu “%1”\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Chaidh bufair na h-ath-chluiche a shàbhaladh gu “%1”\"\nBasic.StatusBar.ScreenshotSavedTo=\"Chaidh an glacadh-sgrìn a shàbhaladh gu “%1”\"\nBasic.StatusBar.AutoRemuxedTo=\"Chaidh an clàradh iompachadh ’na “%1” gu fèin-obrachail\"\nBasic.Filters=\"Criathragan\"\nBasic.Filters.AsyncFilters=\"Criathragan fuaime/video\"\nBasic.Filters.AudioFilters=\"Criathragan fuaime\"\nBasic.Filters.EffectFilters=\"Criathragan èifeachd\"\nBasic.Filters.Title=\"Criathragan airson “%1”\"\nBasic.Filters.AddFilter.Title=\"Ainm na criathraige\"\nBasic.Filters.AddFilter.Text=\"Cuir a-steach ainm na criathraige\"\nBasic.TransformWindow=\"Tar-mhùthadh air nì an t-seallaidh\"\nBasic.TransformWindow.Position=\"Ionad\"\nBasic.TransformWindow.PositionX=\"Ionad X\"\nBasic.TransformWindow.PositionY=\"Ionad Y\"\nBasic.TransformWindow.Rotation=\"Cuairteachadh\"\nBasic.TransformWindow.Size=\"Meud\"\nBasic.TransformWindow.Width=\"Leud\"\nBasic.TransformWindow.Height=\"Àirde\"\nBasic.TransformWindow.Alignment=\"Co-thaobhadh ionaid\"\nBasic.TransformWindow.BoundsType=\"Seòrsa a’ bhogsa-iadhaidh\"\nBasic.TransformWindow.BoundsAlignment=\"Co-thaobhadh sa bhogsa-iadhaidh\"\nBasic.TransformWindow.Bounds=\"Meud a’ bhogsa-iadhaidh\"\nBasic.TransformWindow.BoundsWidth=\"Leud a’ bhogsa-iadhaidh\"\nBasic.TransformWindow.BoundsHeight=\"Àirde a’ bhogsa-iadhaidh\"\nBasic.TransformWindow.Crop=\"Beàrr\"\nBasic.TransformWindow.CropLeft=\"Beàrr aig an taobh chlì\"\nBasic.TransformWindow.CropRight=\"Beàrr aig an taobh deas\"\nBasic.TransformWindow.CropTop=\"Beàrr aig a’ bhàrr\"\nBasic.TransformWindow.CropBottom=\"Beàrr aig a’ bhonn\"\nBasic.TransformWindow.Alignment.TopLeft=\"Taobh clì aig a’ bhàrr\"\nBasic.TransformWindow.Alignment.TopCenter=\"Sa mheadhan aig a’ bhàrr\"\nBasic.TransformWindow.Alignment.TopRight=\"Taobh deas aig a’ bhàrr\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Sa mheadhan air an taobh chlì\"\nBasic.TransformWindow.Alignment.Center=\"Sa mheadhan\"\nBasic.TransformWindow.Alignment.CenterRight=\"Sa mheadhan air an taobh deas\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Taobh clì aig a’ bhonn\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Sa mheadhan aig a’ bhonn\"\nBasic.TransformWindow.Alignment.BottomRight=\"Taobh deas aig a’ bhonn\"\nBasic.TransformWindow.BoundsType.None=\"Gun iadhadh\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Am meud as motha a-mhàin\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Sgèilich ri iadhadh a-staigh\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Sgèilich ri iadhadh a-muigh\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Sgèilich ri leud an iadhaidh\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Sgèilich ri àirde an iadhaidh\"\nBasic.TransformWindow.BoundsType.Stretch=\"Sìn gun iadhadh\"\nBasic.TransformWindow.Title=\"Atharraich an tar-mhùthadh airson “%1”\"\nBasic.TransformWindow.NoSelectedSource=\"Cha deach tùs a thaghadh\"\nBasic.Main.AddSourceHelp.Title=\"Chan urrainn dhuinn an tùs a chur ris\"\nBasic.Main.AddSourceHelp.Text=\"Feumaidh do shealladh a bhith agad mus cuir thu tùs ris.\"\nBasic.Main.Scenes=\"Seallaidhean\"\nBasic.Main.Sources=\"Tùsan\"\nBasic.Main.Source=\"Tùs\"\nBasic.Main.Controls=\"Uidheaman-smachd\"\nBasic.Main.Connecting=\"’Ga cheangal…\"\nBasic.Main.StartRecording=\"Tòisich air clàradh\"\nBasic.Main.StartReplayBuffer=\"Tòisich air bufair ath-chluiche\"\nBasic.Main.SaveReplay=\"Sàbhail an ath-chluiche\"\nBasic.Main.StartStreaming=\"Tòisich air sruthadh\"\nBasic.Main.StartBroadcast=\"Cuir beò e\"\nBasic.Main.StartVirtualCam=\"Cuir an camara biortail gu dol\"\nBasic.Main.StopRecording=\"Cuir stad air a’ chlàradh\"\nBasic.Main.PauseRecording=\"Cuir an clàradh ’na stad\"\nBasic.Main.UnpauseRecording=\"Lean air a’ chlàradh\"\nBasic.Main.StoppingRecording=\"A’ cur stad air a’ chlàradh…\"\nBasic.Main.StopReplayBuffer=\"Cuir stad air bufair na h-ath-chluiche\"\nBasic.Main.StoppingReplayBuffer=\"A’ cur stad air bufair na h-ath-chluiche…\"\nBasic.Main.SetupBroadcast=\"Stiùirich an craoladh\"\nBasic.Main.StopStreaming=\"Cuir stad air an t-sruthadh\"\nBasic.Main.StopBroadcast=\"Cuir stad air a’ chraoladh\"\nBasic.Main.AutoStopEnabled=\"(Stad fèin-obrachail)\"\nBasic.Main.StoppingStreaming=\"A’ cur stad air an t-sruthadh…\"\nBasic.Main.ForceStopStreaming=\"Cuir stad air an t-sruthadh (leig seachad an dàil)\"\nBasic.Main.ShowContextBar=\"Seall bàr-inneal nan tùsan\"\nBasic.Main.HideContextBar=\"Falaich bàr-inneal nan tùsan\"\nBasic.Main.StopVirtualCam=\"Cuir stad air a’ chamara bhiortail\"\nBasic.Main.Group=\"Buidheann %1\"\nBasic.Main.GroupItems=\"Buidhnich na thagh thu\"\nBasic.Main.Ungroup=\"Sgaoil am buidheann\"\nBasic.Main.GridMode=\"Modh a’ ghrioda\"\nBasic.Main.ListMode=\"Modh na liosta\"\nBasic.MainMenu.File=\"&Faidhle\"\nBasic.MainMenu.File.Export=\"Às-phortaich (&E)\"\nBasic.MainMenu.File.Import=\"&Ion-phortaich\"\nBasic.MainMenu.File.ShowRecordings=\"Seall na clà&raidhean\"\nBasic.MainMenu.File.Remux=\"Io&mpaich na clàraidhean\"\nBasic.MainMenu.File.Settings=\"Roghainnean (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Seall pasgan nan roghainnean\"\nBasic.MainMenu.File.ShowProfileFolder=\"Seall pasgan na pròifil\"\nBasic.MainMenu.File.ShowMissingFiles=\"Thoir sùil airson faidhlichean a tha a dhìth\"\nBasic.MainMenu.File.Exit=\"Fàg an-seo (&X)\"\nBasic.MainMenu.Edit=\"D&easaich\"\nBasic.MainMenu.Edit.Undo=\"Neo-dhèan (&U)\"\nBasic.MainMenu.Edit.Redo=\"Ath-dhèan (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"G&lais an ro-shealladh\"\nBasic.MainMenu.Edit.Scale=\"&Sgèileadh an ro-sheallaidh\"\nBasic.MainMenu.Edit.Scale.Window=\"Sgèilich ris an uinneag\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Canabhas (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Às-chur (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"&Tar-mhùth\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"D&easaich an tar-mhùthadh…\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Dèan lethbhreac dhen tar-mhùthadh\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Cuir ann tar-mhùthadh\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Ath-shuidhich an ta&r-mhùthadh\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Cuairtich gu deiseil le 90 ceum\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Cuairtich gu tuathail le 90 ceum\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Cuairtich le 180 ceum\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"T&hoir flip air a’ chòmhnard\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Thoir flip gu h-inghearach (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Co-&fhreagair ri meud na sgrìn\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&Sìn gu meud na sgrìn\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Cuir air meadhan na sgrìn\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Meadhanaich gu h-inghearach\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Meadhanaich air a’ chòmhnard\"\nBasic.MainMenu.Edit.Order=\"Òrdugh (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Gluais s&uas\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Gluais sìos (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Gluais gun bhàrr (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Gluais gun &bhonn\"\nBasic.MainMenu.Edit.AdvAudio=\"Roghainnean &adhartach na fuaime\"\nBasic.MainMenu.View=\"Seall (&V)\"\nBasic.MainMenu.View.Toolbars=\"Bàraichean-inneal (&T)\"\nBasic.MainMenu.View.ContextBar=\"Bàr-inneal nan tùsan\"\nBasic.MainMenu.View.SourceIcons=\"Ìomha&igheagan nan tùsan\"\nBasic.MainMenu.View.StatusBar=\"Bàr-&staide\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Eadar-aghaidh làn-sgrìn\"\nBasic.MainMenu.SceneCollection=\"Cruinneachadh &sheallaidhean\"\nBasic.MainMenu.Profile=\"&Pròifil\"\nBasic.MainMenu.Profile.Import=\"Ion-phortaich pròifil\"\nBasic.MainMenu.Profile.Export=\"Às-phortaich a’ phròifil\"\nBasic.MainMenu.SceneCollection.Import=\"Ion-phortaich cruinneachadh sheallaidhean\"\nBasic.MainMenu.SceneCollection.Export=\"Às-phortaich cruinneachadh sheallaidhean\"\nBasic.MainMenu.Profile.Exists=\"Tha a’ phròifil ann mu thràth\"\nBasic.MainMenu.Tools=\"Innealan (&T)\"\nBasic.MainMenu.Help=\"Cob&hair\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal na cobharach\"\nBasic.MainMenu.Help.Website=\"Tadhail air an làrach-lìn (&W)\"\nBasic.MainMenu.Help.Discord=\"Gabh pàirt ann am frithealaiche &Discord\"\nBasic.MainMenu.Help.Logs=\"Faidhlichean an &loga\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"&Seall faidhlichean an loga\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Luchdai&ch suas faidhle an loga làithrich\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Seall an loga làithreach (&V)\"\nBasic.MainMenu.Help.CheckForUpdates=\"Thoir sùil airson ùrachaidhean\"\nBasic.MainMenu.Help.CrashLogs=\"Aithisgean tuislidh (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"&Seall na h-aithisgean tuislidh\"\nBasic.MainMenu.Help.About=\"Mu dhèidhinn (&A)\"\nBasic.Settings.ProgramRestart=\"Feumaidh tu am prògram ath-thòiseachadh gus na roghainnean seo a chur an sàs.\"\nBasic.Settings.ConfirmTitle=\"Dearbh na h-atharraichean\"\nBasic.Settings.Confirm=\"Tha atharraichean gun sàbhaladh agad. A bheil thu airson an sàbhaladh?\"\nBasic.Settings.General=\"Coitcheann\"\nBasic.Settings.General.Language=\"Cànan\"\nBasic.Settings.General.EnableAutoUpdates=\"Thoir sùil airson ùrachaidhean gu fèin-obrachail aig an toiseach\"\nBasic.Settings.General.OpenStatsOnStartup=\"Fosglaidh seo còmhradh na stadastaireachd aig an toiseach\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Seall còmhradh dearbhaidh mus dèid sruthadh a thòiseachadh\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Seall còmhradh dearbhaidh mus dèid stad a chur air sruthadh\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Seall còmhradh dearbhaidh mus dèid stad a chur air clàradh\"\nBasic.Settings.General.Projectors=\"Proiseactaran\"\nBasic.Settings.General.HideProjectorCursor=\"Falaich an cùrsair os cionn proiseactaran\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Cuir na proiseactaran air uachdar an-còmhnaidh\"\nBasic.Settings.General.Snapping=\"Greimeachadh co-thaobhadh nan tùsan\"\nBasic.Settings.General.ScreenSnapping=\"Greimich na tùsan ri oir na sgrìn\"\nBasic.Settings.General.CenterSnapping=\"Greimich na tùsan ris a’ mheadhan\"\nBasic.Settings.General.SourceSnapping=\"Greimich na tùsan ri tùsan eile\"\nBasic.Settings.General.SnapDistance=\"Mothalachd a’ ghreimeachaidh\"\nBasic.Settings.General.RecordWhenStreaming=\"Clàraich gu fèin-obrachail nuair a thèid rud a shruthadh\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Cùm an clàradh nuair a thèid stad a chur air an t-sruthadh\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Tòisich bufair na h-ath-chluiche gu fèin-obrachail nuair a bhios rud ’ga shruthadh\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Cùm bufair na h-ath-chluiche gnìomhach nuair a thèid stad a chur air sruthadh\"\nBasic.Settings.General.SysTray=\"Treidhe an t-siostaim\"\nBasic.Settings.General.SysTrayWhenStarted=\"Fìor-lùghdaich gu treidhe an t-siostaim aig an toiseach\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Fìor-lùghdaich gu treidhe an t-siostaim seach bàr nan saothair an-còmhnaidh\"\nBasic.Settings.General.SaveProjectors=\"Sàbhail na proiseactaran nuair a thèid fàgail an-seo\"\nBasic.Settings.General.Preview=\"Ro-shealladh\"\nBasic.Settings.General.OverflowHidden=\"Falaich an taomadh\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Seall an taomadh an-còmhnaidh\"\nBasic.Settings.General.OverflowSelectionHidden=\"Seall an taomadh fiù ma tha an tùs falaichte\"\nBasic.Settings.General.Importers=\"Innealan ion-phortaidh\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Lorg sna h-ionadan aithnichte airson cruinneachaidhean de sheallaidhean rè an ion-phortaidh\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Tar-mhùth gu sealladh le briogadh dùbailte\"\nBasic.Settings.General.StudioPortraitLayout=\"Cuir an comas co-dhealbhachd portraid/inghearach\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Seall na leubailean ro-sheallaidh/prògraim\"\nBasic.Settings.General.Multiview=\"Ioma-shealladh\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Briog airson leum a ghearradh eadar seallaidhean\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Seall ainmean nan seallaidhean\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Tarraing raointean sàbhailte (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Co-dhealbhachd an ioma-sheallaidh\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Còmhnard, bàrr (8 seallaidhean)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Còmhnard, bonn (8 seallaidhean)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Inghearach, clì (8 seallaidhean)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Inghearach, deas (8 seallaidhean)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Còmhnard, bàrr (18 seallaidhean)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Còmhnard, bàrr (24 sealladh)\"\nBasic.Settings.Stream=\"Sruthadh\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Cleachd dearbh-aithneachadh\"\nBasic.Settings.Stream.Custom.Username=\"Ainm-cleachdaiche\"\nBasic.Settings.Stream.Custom.Password=\"Facal-faire\"\nBasic.Settings.Stream.BandwidthTestMode=\"Cuir modh deuchainn leud banna an comas\"\nBasic.Settings.Stream.TTVAddon=\"Tuilleadain cabadaich Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Chan eil gin\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV ’s FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Tha suidheachadh srutha a dhìth\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Fosgail na roghainnean\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Tha URL ’s iuchair sruthaidh a dhìth.\\n\\nFosgail na roghainnean a chur a-steach URL ’s iuchair an t-sruthaidh air an taba “Sruthadh”.\"\nBasic.Settings.Stream.MissingUrl=\"Tha URL an t-sruthaidh a dhìth.\\n\\nFosgail na roghainnean a chur a-steach URL an t-sruthaidh air an taba “Sruthadh”.\"\nBasic.Settings.Stream.MissingStreamKey=\"Tha iuchair an t-sruthaidh a dhìth.\\n\\nFosgail na roghainnean a chur a-steach iuchair an t-sruthaidh air an taba “Sruthadh”.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Leig seachad na molaidhean air roghainnean na seirbheise sruthaidh\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Tar-àithn na roghainnean a mholamaid\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Rabhadh: Ma chuireas tu cuingeachadh na seirbheise dhan dàrna taobh, dh’fhaoidte nach fhaigh thu ach droch-chàileachd as an t-sruthadh no nach urrainn dhut a shruthadh idir.\\n\\nA bheil thu airson leantainn air adhart?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Reat bhiotaichean as motha a’ video: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Reat bhiotaichean as motha na fuaime: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"An dùmhlachd-bhreacaidh as motha: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Na FPS as motha: %1\"\nBasic.Settings.Output=\"Às-chur\"\nBasic.Settings.Output.Format=\"Fòrmat a’ chlàraidh\"\nBasic.Settings.Output.Encoder.Video=\"Còdaichean Bhidio\"\nBasic.Settings.Output.Encoder.Audio=\"Còdaichean Fhuaime\"\nBasic.Settings.Output.SelectDirectory=\"Tagh pasgan a’ chlàraidh\"\nBasic.Settings.Output.DynamicBitrate=\"Atharraich an reat bhiodan gu h-innsgineach a sheachnadh dùmhlachd\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Atharraich an reat bhiodan gu h-innsgineach a sheachnadh dùmhlachd (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Atharraichidh seo an reat bhiodan seach tuiteam fhrèaman nuair a bhios dùmhlachd ann.\\n\\nThoir an aire gum bi barrachd dàil air dhan amharc ma dh’fhaoidte ma thachras dùmhlachd gu h-obann.\\nNuair a thuiteas an reat bhiodan, dh’fhaoidte gun toir e mionaid no dhà mus dèid a shlànachadh.\\n\\nCha chuir sinn taic ach ri RTMP aig an àm seo.\"\nBasic.Settings.Output.Mode=\"Modh an às-chuir\"\nBasic.Settings.Output.Mode.Simple=\"Simplidh\"\nBasic.Settings.Output.Mode.Adv=\"Adhartach\"\nBasic.Settings.Output.Mode.FFmpeg=\"Às-chur FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Cuir an comas bufair ath-chluiche\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Ùine as motha nan ath-chluichean\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Tuairmse air cleachdadh na cuimhne: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Chan urrainn dhuinn tuairmse a dhèanamh air cleachdadh na cuimhne. Suidhich crìoch as motha na cuimhne.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Ro-leasachan air ainmean faidhle aig bufair na h-ath-chluiche\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Iar-leasachan\"\nBasic.Settings.Output.Simple.SavePath=\"Slighe a’ chlàraidh\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Càileachd a’ chlàraidh\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Co-ionnann ri tè an t-sruthaidh\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Càileachd àrd, faidhle meadhanach mòr\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Cha ghabh diofar aithneachadh, faidhle mòr\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Càileachd gun chall, faidhle uabhasach mòr\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Rabhadh: Thèid reat bhiotaichean video an t-sruthaidh a shuidheachadh air %1, seo a’ chrìoch as àirde aig an t-seirbheis sruthaidh làithreach.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Rabhadh: Thèid reat bhiotaichean fuaim an t-sruthaidh a shuidheachadh air %1, seo a’ chrìoch as àirde aig an t-seirbheis sruthaidh làithreach.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Rabhadh: Cha ghabh clàradh a chur ’na stad nuair a bhios càileachd “Co-ionnann ri tè an t-sruthaidh” air a’ chlàradh.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Rabhadh: Ma nì thu clàradh le inneal-còdachaidh bathair-bhog air nach eil an aon chàileachd ’s a th’ air an t-sruthadh, bi feum air barrachd cleachdadh a’ CPU nuair a bhios tu a’ sruthadh ’s a’ clàradh aig an aon àm.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"A bheil thu cinnteach gu bheil thu airson càileachd gun chall a chleachdadh?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Rabhadh a thaobh càileachd gun chall!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Bathar-bog (x264)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Bathar-bog (ro-shuidheachadh air x264 le cleachdadh a’ CPU ìosal, meudaichidh seo na faidhlichean)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Traca video air iarrtas Twitch (a’ cleachdadh traca 2)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Dùmhlachd-bhreacaidh/Reat fhrèaman neo-chòrdail\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Cha chuir seirbheis an t-sruthaidh taic ri dùmhlachd-bhreacaidh agus/no reat fhrèaman làithreach an às-chuir agad. Thèid an atharrachadh dhan luach cho-chòrdail as fhaisge:\\n\\n%1\\n\\nA bheil thu airson leantainn air adhart?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Dùmhlachd-bhreacaidh: %1\"\nBasic.Settings.Output.VideoBitrate=\"Reat bhiodan a’ video\"\nBasic.Settings.Output.AudioBitrate=\"Reat bhiodan na fuaime\"\nBasic.Settings.Output.Reconnect=\"Ath-cheangail gu fèin-obrachail\"\nBasic.Settings.Output.RetryDelay=\"Dàil na h-ath-fheuchainn\"\nBasic.Settings.Output.MaxRetries=\"Oidhirpean as motha\"\nBasic.Settings.Output.EncoderPreset=\"Ro-sheata an inneil-chòdachaidh\"\nBasic.Settings.Output.CustomEncoderSettings=\"Roghainnean gnàthaichte an inneil-chòdachaidh\"\nBasic.Settings.Output.CustomMuxerSettings=\"Roghainnean gnàthaichte an iompaicheir\"\nBasic.Settings.Output.NoSpaceFileName=\"Gin ainm faidhle gun spàs\"\nBasic.Settings.Output.Adv.Rescale=\"Ath-sgèilich an t-às-chur\"\nBasic.Settings.Output.Adv.AudioTrack=\"Traca fuaime\"\nBasic.Settings.Output.Adv.Streaming=\"Sruthadh\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Traca 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Traca 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Traca 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Traca 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Traca 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Traca 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Traca video air iarrtas Twitch\"\nBasic.Settings.Output.Adv.Recording=\"Clàradh\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Seòrsa dhen chlàradh\"\nBasic.Settings.Output.Adv.Recording.Type=\"Seòrsa\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Stannardach\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Às-chur gnàthaichte (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Cleachd inneal-còdachaidh sruthaidh)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Fòrmatadh nan ainmean faidhle\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Sgrìobh thairis air faidhle ma tha e ann\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Seòrsa às-chur FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Às-chuir gu URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Às-chuir gu faidhle\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Fòrmatan clàraidh cumanta\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Na h-uile faidhle\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Slighe faidhle no URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Fòrmat an t-soithich\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Fuaim\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Am fòrmat bunaiteach\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Tuairisgeul air fòrmat an t-soithich\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Chaidh codec fuaime/video a thuairmeas o shlighe an fhaidhle no URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"An t-inneal-còdachaidh bunaiteach\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Cuir an t-inneal-còdachaidh à comas\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Inneal-còdachaidh video\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Roghainnean an inneil-chòdachaidh video (ma tha gin ann)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Inneal-còdachaidh fuaime\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Roghainnean an inneil-chòdachaidh fuaime (ma tha gin ann)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Roghainnean an iompaicheir (ma tha gin ann)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Eadaramh nam frèaman-iuchrach (frèam)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Seall na h-uile codec (fiù an fheadhainn nach eil co-chòrdail ma dh’fhaoidte)\"\nScreenshot=\"Às-chur glacaidh-sgrìn\"\nScreenshot.SourceHotkey=\"Dèan glacadh-sgrìn dhen tùs a thagh thu\"\nScreenshot.StudioProgram=\"Glacadh-sgrìn (prògram)\"\nScreenshot.Preview=\"Glacadh-sgrìn (ro-shealladh)\"\nScreenshot.Scene=\"Glacadh-sgrìn (sealladh)\"\nScreenshot.Source=\"Glacadh-sgrìn (tùs)\"\nFilenameFormatting.TT.M=\"Mionaid (00-59)\"\nFilenameFormatting.TT.Y=\"Bliadhna\"\nBasic.Settings.Video.Adapter=\"Adaptar video\"\nBasic.Settings.Video.BaseResolution=\"Dùmhlachd-bhreacaidh bhunasach (a’ chanabhais)\"\nBasic.Settings.Video.ScaledResolution=\"Dùmhlachd-bhreacaidh (sgèilichte) an às-chuir\"\nBasic.Settings.Video.DownscaleFilter=\"Criathrag sgèileachaidh\"\nBasic.Settings.Video.FPSCommon=\"Luachan FPS cumanta\"\nBasic.Settings.Video.FPSInteger=\"Luach FPS slàn\"\nBasic.Settings.Video.FPSFraction=\"Luachan FPS bloigheach\"\nBasic.Settings.Video.Numerator=\"Àireamhaiche\"\nBasic.Settings.Video.Denominator=\"Seòrsaiche\"\nBasic.Settings.Video.Renderer=\"Inneal-reandaraidh\"\nBasic.Settings.Video.InvalidResolution=\"Chan eil luach na dùmhlachd-bhreacaidh dligheach. Feumaidh e a bhith ’na [leud]x[àirde] (can 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Tha às-chur video gnìomhach an-dràsta. cuir dheth gach às-chur airson roghainnean a’ video atharrachadh.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Dà-loidhneach (as luaithe ach sgleò air le sgèileachadh)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Dà-chiùbach (sgèileachadh geuraichte, 16 sampallan)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (sgèileachadh geuraichte, 36 sampall)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Raon (Iomlan le cuideam, 4/6/9 sampallan)\"\nBasic.Settings.Audio=\"Fuaim\"\nBasic.Settings.Audio.SampleRate=\"Reat shampallan\"\nBasic.Settings.Audio.Channels=\"Seanailean\"\nBasic.Settings.Audio.Meters=\"Meatair\"\nBasic.Settings.Audio.MeterDecayRate=\"Reat crìonaidh\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Luath\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Meadhanach (PPM seòrsa I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Slaodach (PPM seòrsa II)\"\nBasic.Settings.Audio.PeakMeterType=\"Seòrsa a’ mheidheadair-bharran\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Ball-sampaill de bhàrr\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Bàrr fìrinneach (cleachdadh nas àirde a’ CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"RABHADH: Tha fuaim cuairteachaidh an comas.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"A bheil thu airson fuaim cuairteachaidh a chur an comas?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"A bheil thu cinnteach gu bheil thu airson fuaim cuairteachaidh a chur an comas?\"\nBasic.Settings.Audio.Devices=\"Uidheaman fuaime uile-choitcheann\"\nBasic.Settings.Audio.DesktopDevice=\"Fuaim an deasg\"\nBasic.Settings.Audio.DesktopDevice2=\"Fuaim an deasg 2\"\nBasic.Settings.Audio.AuxDevice=\"Fuaim micreofoin/taice\"\nBasic.Settings.Audio.AuxDevice2=\"Fuaim micreofoin/taice 2\"\nBasic.Settings.Audio.AuxDevice3=\"Fuaim micreofoin/taice 3\"\nBasic.Settings.Audio.AuxDevice4=\"Fuaim micreofoin/taice 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Cuir an comas brùth-airson-mùchadh\"\nBasic.Settings.Audio.PushToMuteDelay=\"Dàil air brùth-airson-mùchadh\"\nBasic.Settings.Audio.EnablePushToTalk=\"Cuir an comas brùth-airson-bruidhinn\"\nBasic.Settings.Audio.PushToTalkDelay=\"Dàil air brùth-airson-bruidhinn\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Chan eil uidheam ceangailte no ri fhaighinn]\"\nBasic.Settings.Audio.Disabled=\"À comas\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Gnàthaichte\"\nBasic.Settings.Advanced=\"Adhartach\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Seall rabhadh air às-chuir ghnìomhach leis an fhàgail\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prìomhachas a’ phròiseis\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Àrd\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Nas àirde na àbhaisteach\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Àbhaisteach\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Nas ìsle na àbhaisteach\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"’Na tàmh\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Fòrmat nan dathan\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Spàs dhathan\"\nBasic.Settings.Advanced.Video.ColorRange=\"Rainse dhathan\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Làn\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Uidheam sgrùdaidh\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Bunaiteach\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Cuir à comas tumadh fuaime Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Dàil an t-sruthaidh\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Faid\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Glèidh puing a’ ghearraidh (meudaich an dàil) nuair a nithear ath-cheangal\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Tuairmse air cleachdadh na cuimhne: %1 MB\"\nBasic.Settings.Advanced.Network=\"Lìonra\"\nBasic.Settings.Advanced.Network.BindToIP=\"Nasg ri IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Cuir piseach air an lìonra an comas\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Cuir ceumadh TCP an comas\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Feuchaidh seo gum bi an t-às-chur RTMP nas fhasa làimhseachadh do dh’aplacaidean cugallach a thaobh foillidheachd lìonraidh le stiùireadh air reat an tar-chùir.\\nDh’fhaoidte gun tuit barrachd fhrèamaichean leis air ceanglaichean neo-sheasmhach.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Giùlan nan grad-iuchraichean a thaobh fòcas\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Na cuir grad-iuchraichean à comas idir\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Cuir à comas na grad-iuchraichean nuair a bhios am fòcas air a’ phrìomh-uinneag\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Cuir à comas na grad-iuchraichean mur eil am fòcas air a’ phrìomh-uinneag\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(clàraich mar mkv)\"\nBasic.AdvAudio=\"Roghainnean adhartach na fuaime\"\nBasic.AdvAudio.ActiveOnly=\"Na tùsan gnìomhach a-mhàin\"\nBasic.AdvAudio.Name=\"Ainm\"\nBasic.AdvAudio.Volume=\"Àirde na fuaime\"\nBasic.AdvAudio.VolumeSource=\"Àirde na fuaime air “%1”\"\nBasic.AdvAudio.MonoSource=\"Measgachadh sìos mono air “%1”\"\nBasic.AdvAudio.Balance=\"Balans\"\nBasic.AdvAudio.BalanceSource=\"Balans air “%1”\"\nBasic.AdvAudio.SyncOffset=\"Frith-àireamh an t-sioncronachaidh\"\nBasic.AdvAudio.SyncOffsetSource=\"Frith-àireamh an t-sioncronachaidh air “%1”\"\nBasic.AdvAudio.Monitoring=\"Sgrùdadh fuaime\"\nBasic.AdvAudio.Monitoring.None=\"Gun sgrùdadh\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Sgrùdadh a-mhàin (mùch an t-às-chur)\"\nBasic.AdvAudio.Monitoring.Both=\"Sgrùdadh is às-chur\"\nBasic.AdvAudio.MonitoringSource=\"Sgrùdadh fuaime air “%1”\"\nBasic.AdvAudio.AudioTracks=\"Tracaichean\"\nBasic.Settings.Hotkeys=\"Grad-iuchraichean\"\nBasic.Settings.Hotkeys.Pair=\"Nì na co-iuchraichean a tha ’gan co-roinneadh le “%1” toglachadh\"\nBasic.Settings.Hotkeys.Filter=\"Criathrag\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Criathraich a-rèir grad-iuchair\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Tha an grad-iuchair seo ’ga cho-roinneadh le gnìomh no dhà eile, briog airson na còmhstrithean a shealltainn\"\nBasic.Hotkeys.SelectScene=\"Geàrr leum dhan t-sealladh\"\nBasic.SystemTray.Show=\"Seall\"\nBasic.SystemTray.Hide=\"Falaich\"\nBasic.SystemTray.Message.Reconnecting=\"Gun cheangal. ’Ga ath-cheangal…\"\nHotkeys.Insert=\"Cuir a-steach\"\nHotkeys.Delete=\"Sguab às\"\nHotkeys.Left=\"Gu clì\"\nHotkeys.Right=\"Gu deas\"\nHotkeys.Up=\"Suas\"\nHotkeys.Down=\"Sìos\"\nHotkeys.NumpadNum=\"%1 air pada nan àireamh\"\nHotkeys.NumpadMultiply=\"Iomadachadh air pada nan àireamh\"\nHotkeys.NumpadDivide=\"Roinneadh air pada nan àireamh\"\nHotkeys.NumpadAdd=\"Cur ris air pada nan àireamh\"\nHotkeys.NumpadSubtract=\"Toir air falbh air pada nan àireamh\"\nHotkeys.NumpadDecimal=\"Puing air pada nan àireamh\"\nHotkeys.AppleKeypadNum=\"%1 (pada nan àireamh)\"\nHotkeys.AppleKeypadMultiply=\"* (pada nan àireamh)\"\nHotkeys.AppleKeypadDivide=\"/ (pada nan àireamh)\"\nHotkeys.AppleKeypadAdd=\"+ (pada nan àireamh)\"\nHotkeys.AppleKeypadSubtract=\"- (pada nan àireamh)\"\nHotkeys.AppleKeypadDecimal=\". (pada nan àireamh)\"\nHotkeys.AppleKeypadEqual=\"= (pada nan àireamh)\"\nHotkeys.MouseButton=\"%1 na luchaige\"\nMute=\"Mùch\"\nUnmute=\"Dì-mhùch\"\nPush-to-mute=\"Brùth-airson-mùchadh\"\nPush-to-talk=\"Brùth-airson-bruidhinn\"\nSceneItemShow=\"Seall “%1”\"\nSceneItemHide=\"Falaich “%1”\"\nOutputWarnings.NoTracksSelected=\"Feumaidh tu traca no dhà a thaghadh\"\nOutputWarnings.MP4Recording=\"Rabhadh: Cha ghabh clàraidhean a thèid a shàbhaladh gu MP4/MOV aiseag mura gabh am faidhle a thoirt gu crìch (can ri linn tuisleachaidh, call cumhachd is msaa.). Nam bu toigh leat iomadh traca fuaime a chlàradh, mholamaid gun cleachd thu MKV agus gun iompaich thu an clàradh gu MP4/MOV nuair a bhios e deiseil (Faidhle → Iompaich clàraidhean)\"\nOutputWarnings.CannotPause=\"Rabhadh: Cha ghabh clàradh a chur ’na stad ma chaidh inneal-còdachaidh a’ chlàraidh a shuidheachadh air “(Cleachd inneal-còdachaidh sruthaidh)”\"\nFinalScene.Title=\"Sguab às an sealladh\"\nFinalScene.Text=\"Feumaidh do shealladh a bhith ann.\"\nNoSources.Title=\"Gun tùs\"\nNoSources.Text=\"Tha coltas nach do chuir thu tùs video ris fhathast ’s mar sin chan fhaigh thu ach sgrìn bhàn. A bheil thu cinnteach gu bheil thu airson seo a dhèanamh?\"\nNoSources.Text.AddSource=\"’S urrainn dhut tùsan a chur ris uair sam bith ’s tu a’ briogadh air an ìomhaigheag “+” fon bhogsa “Tùsan” air a’ phrìomh-uinneag.\"\nNoSources.Label=\"Chan eil tùs sam bith agad.\\nBriog air a’ phutan + gu h-ìosal\\nno dèan briogadh deas an-seo airson fear a chur ris.\"\nChangeBG=\"Suidhich an dath\"\nCustomColor=\"Dath gnàthaichte\"\nBrowserSource.EnableHardwareAcceleration=\"Cuir an comas an luathachadh bathair-chruaidh aig tùs a’ bhrabhsair\"\nAbout=\"Mu dhèidhinn\"\nAbout.Info=\"’S e bathar-bog saor is open source airson clàradh video is sruthadh beò a th’ ann an OBS Studio.\"\nAbout.Donate=\"Thoir tìodhlac dhuinn\"\nAbout.GetInvolved=\"Gabh pàirt\"\nAbout.Authors=\"Ùghdaran\"\nAbout.License=\"Ceadachas\"\nAbout.Contribute=\"Cuidich am pròiseact OBS\"\nAddUrl.Title=\"Cuir tùs o URL ris\"\nAddUrl.Text=\"Shlaod thu URL gu OBS. Cuiridh seo an ceangal ’na thùs ris gu fèin-obrachail. A bheil thu airson leantainn air adhart?\"\nResizeOutputSizeOfSource.Continue=\"A bheil thu airson leantainn air adhart?\"\nPreviewTransition=\"Ro-sheall an tar-mhùthadh\"\nImporter=\"Ion-phortadh cruinneachadh sheallaidhean\"\nImporter.SelectCollection=\"Tagh cruinneachadh sheallaidhean\"\nImporter.Collection=\"Cruinneachadh sheallaidhean\"\nImporter.HelpText=\"Cuir faidhlichean ris an uinneag seo airson cruinneachaidhean ion-phortadh o OBS no prògraman eile ris an cuir sinn taic.\"\nImporter.Path=\"Slighe a’ chruinneachaidh\"\nImporter.Program=\"Mhothaich sinn do dh’aplacaid\"\nImporter.AutomaticCollectionPrompt=\"Lorg cruinneachaidhean sheallaidhean gu fèin-obrachail\"\nImporter.AutomaticCollectionText=\"’S urrainn do dh’OBS cruinneachaidhean sheallaidhean a ghabhas ion-phortadh o phrògraman threas-phàrtaidhean a lorg gu fèin-obrachail. A bheil thu airson ’s gun lorg OBS cruinneachaidhean gu fèin-obrachail dhut?\\n\\n’S urrainn dhut an roghainn seo atharrachadh uair sam bith ann an “Roghainnean” > “Coitcheann” > Ion-phortadh.\"\nRestart=\"Ath-thòisich\"\nNeedsRestart=\"Tha OBS Studio feumach air ath-thòiseachadh. A bheil thu airson ath-thòiseachadh an-dràsta?\"\nLoadProfileNeedsRestart=\"Tha roghainnean aig a’ phròifil a tha feumach air ath-thòiseachadh OBS:\\n%1\\n\\nA bheil thu airson OBS ath-thòiseachadh gus na roghainnean sin a chur an sàs?\"\nContextBar.NoSelectedSource=\"Cha deach tùs a thaghadh\"\nContextBar.MediaControls.PlayMedia=\"Cluich meadhan\"\nContextBar.MediaControls.PauseMedia=\"Cuir am meadhan ’na stad\"\nContextBar.MediaControls.StopMedia=\"Cuir stad air a’ mheadhan\"\nContextBar.MediaControls.RestartMedia=\"Ath-thòisich am meadhan\"\nContextBar.MediaControls.PlaylistNext=\"An ath-fhear air an liosta-chluich\"\nContextBar.MediaControls.PlaylistPrevious=\"An fhear roimhe air an liosta-chluich\"\nContextBar.MediaControls.BlindSeek=\"Widget sireadh meadhain\"\nYouTube.Auth.Ok=\"Chaidh leis an ùghdarrachadh.\\n’S urrainn dhut an duilleag seo a dhùnadh a-nis.\"\nYouTube.Auth.NoCode=\"Cha deach an t-ùghdarrachadh a choileanadh.\"\nYouTube.Auth.NoChannels=\"Chan eil seanail ri fhaighinn air a’ chunntas a thagh thu\"\nYouTube.Auth.WaitingAuth.Title=\"Ùghdarrachadh cleachdaiche YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Coilean an t-ùghdarrachadh taobh a-muigh sa bhrabhsair agad.<br>Mura fosgail am brabhsair air an taobh a-muigh, lean ris a’ cheangal seo agus coilean an t-ùghdarrachadh:<br>%1\"\nYouTube.AuthError.Text=\"Cha b’ urrainn dhuinn fiosrachadh na seanail fhaighinn: %1.\"\nYouTube.Actions.WindowTitle=\"Suidheachadh craoladh YouTube – Seanail: %1\"\nYouTube.Actions.CreateNewEvent=\"Cruthaich craoladh ùr\"\nYouTube.Actions.ChooseEvent=\"Tagh craoladh a tha ann\"\nYouTube.Actions.Title=\"Tiotal*\"\nYouTube.Actions.MyBroadcast=\"An craoladh agam\"\nYouTube.Actions.Description=\"Tuairisgeul\"\nYouTube.Actions.Privacy=\"Prìobhaideachd*\"\nYouTube.Actions.Privacy.Private=\"Prìobhaideach\"\nYouTube.Actions.Privacy.Public=\"Poblach\"\nYouTube.Actions.Privacy.Unlisted=\"Falaichte o liostaichean\"\nYouTube.Actions.Category=\"Roinn-seòrsa\"\nYouTube.Actions.Thumbnail=\"Dealbhag\"\nYouTube.Actions.Thumbnail.SelectFile=\"Tagh faidhle…\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Cha deach faidhle a thaghadh\"\nYouTube.Actions.Thumbnail.ClearFile=\"Falamhaich\"\nYouTube.Actions.MadeForKids=\"A bheil a’ video seo ag amas air a’ chlann?*\"\nYouTube.Actions.MadeForKids.Yes=\"Tha, tha e ag amas air a’ chlann\"\nYouTube.Actions.MadeForKids.No=\"Chan eil e ag amas air a’ chlann\"\nYouTube.Actions.Latency=\"Foillidheachd\"\nYouTube.Actions.Latency.Normal=\"Àbhaisteach\"\nYouTube.Actions.Latency.Low=\"Ìosal\"\nYouTube.Actions.Latency.UltraLow=\"Glè ìosal\"\nYouTube.Actions.EnableAutoStart=\"Cuir an tòiseachadh fèin-obrachail an comas\"\nYouTube.Actions.EnableAutoStop=\"Cuir an stad fèin-obrachail an comas\"\nYouTube.Actions.AutoStartStop.TT=\"Innsidh seo am bu chòir an craoladh seo air an sgeideal a thòiseachadh gu fèin-obrachail gus nach bu chòir\"\nYouTube.Actions.EnableDVR=\"Cuir DVR an comas\"\nYouTube.Actions.360Video=\"Video 360\"\nYouTube.Actions.ScheduleForLater=\"Cuir air an sgeideal airson uaireigin eile\"\nYouTube.Actions.RememberSettings=\"Cùm na roghainnean seo an cuimhne\"\nYouTube.Actions.Create_Ready=\"Cruthaich craoladh\"\nYouTube.Actions.Create_GoLive=\"Cruthaich craoladh is tòisich air sruthadh\"\nYouTube.Actions.Choose_Ready=\"Tagh craoladh\"\nYouTube.Actions.Choose_GoLive=\"Tagh craoladh is tòisich air sruthadh\"\nYouTube.Actions.Create_Schedule=\"Cuir craoladh air an sgeideal\"\nYouTube.Actions.Create_Schedule_Ready=\"Cuir craoladh air an sgeideal is tagh e\"\nYouTube.Actions.Dashboard=\"Fosgail YouTube Studio\"\nYouTube.Actions.Error.Title=\"Mearachd a’ cruthachadh a’ chraolaidh bheò\"\nYouTube.Actions.Error.Text=\"Mearachd inntrigeadh YouTube “%1”.<br/>Gheibh thu tuairisgeul mionaideach air a’ mhearachd air <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Mearachd inntrigeadh YouTube. Thoir sùil air a’ cheangal agad dhan lìonra no air an inntrigeadh agad air an fhrithealaiche YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Mearachd le cruthachadh a’ chraolaidh “%1”.<br/>Gheibh thu tuairisgeul mionaideach air a’ mhearachd air <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Cha deach sruth a chruthachadh. Ceangail an cunntas agad às ùr.\"\nYouTube.Actions.Error.YouTubeApi=\"Mearachd API YouTube. Faic faidhle an loga airson barrachd fiosrachaidh.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Cha deach an craoladh a thagh thu a lorg.\"\nYouTube.Actions.Error.FileMissing=\"Chaidh faidhle a thaghadh nach eil ann.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Cha deach leinn am faidhle a thagh thu fhosgladh.\"\nYouTube.Actions.Error.FileTooLarge=\"Tha am faidhle a thagh thu ro mhòr (crìoch: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Dh’fhàillig le tar-mhùthadh a’ chraolaidh: %1<br/><br/>a mhaireas a’ mhearachd seo, <a href='https://studio.youtube.com/video/%2/livestreaming'>fosgail an craoladh ann an YouTube Studio</a> is feuch ris a làimh.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Tha an craoladh ’ga thar-mhùthadh gu ceum na deuchainne, dh’fhaoidte gun doir seo greis. Feuch ris a-rithist an ceann 10-30 diog.\"\nYouTube.Actions.EventsLoading=\"A’ luchdadh liosta nan tachartasan…\"\nYouTube.Actions.EventCreated.Title=\"Chaidh an tachartas a chruthachadh\"\nYouTube.Actions.EventCreated.Text=\"Chaidh an tachartas a chruthachadh.\"\nYouTube.Actions.Stream=\"Sruthadh\"\nYouTube.Actions.Stream.ScheduledFor=\"Air an sgeideal aig %1\"\nYouTube.Actions.Stream.Resume=\"Lean air an t-sruthadh a chaidh a bhriseadh\"\nYouTube.Actions.Stream.YTStudio=\"Chaidh a chruthachadh gu fèin-obrachail le YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"A’ cruthachadh craoladh beò ùr, fuirich ort…\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Tha feum air ath-thòiseachadh a làimh\"\nYouTube.Actions.AutoStartStreamingWarning=\"Chaidh an tòiseachadh fèin-obrachail a chur à comas dhan tachartas seo, briog air “Cuir beò e” gus tòiseachadh air a’ chraoladh agad.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Chan urrainn dhut ath-cheangal a dhèanamh.<br>Thèid stad a chur air an t-sruthadh agad is cha bhi e beò tuilleadh.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Chan eil sruthadh beò an comas air an t-seanail YouTube a thagh thu.<br/><br/>Faic <a href='https://www.youtube.com/features'>youtube.com/features</a> airson barrachd fiosrachaidh.\"\nYouTube.Errors.livePermissionBlocked=\"Chan eil sruthadh beò ri fhaighinn air an t-seanail YouTube a thagh thu.<br/>Thoir an aire gun doir e gu ruige 24 uair a thìde gus am bi sruthadh beò ri fhaighinn às dhèidh dhut a chur an comas ann an roghainnean na seanail agad.<br/><br/>Faic <a href='https://www.youtube.com/features'>youtube.com/features</a> airson mion-fhiosrachadh.\"\nYouTube.Errors.errorExecutingTransition=\"Dh’fhàillig leis a’ chraoladh ri linn mearachd a’ backend. Feuch ris a-rithist an ceann diog no dhà.\"\nYouTube.Errors.errorStreamInactive=\"Chan eil YouTube a’ faighinn dàta dhan t-sruthadh agad. Thoir sùil air an rèiteachadh agad is feuch ris a-rithist.\"\nYouTube.Errors.invalidTransition=\"Cha robh oidhirp an tar-mhùthaidh dligheach. ’S dòcha gun robh an sruthadh trang le tar-mhùthadh eile fhathast. Fuirich diog no dhà is feuch ris a-rithist.\"\n"
  },
  {
    "path": "frontend/data/locale/gl-ES.ini",
    "content": "Language=\"Galego\"\nOK=\"Aceptar\"\nApply=\"Aplicar\"\nCancel=\"Cancelar\"\nClose=\"Pechar\"\nSave=\"Gardar\"\nDiscard=\"Desbotar\"\nDisable=\"Desactivar\"\nYes=\"Si\"\nNo=\"Non\"\nAdd=\"Engadir\"\nRemove=\"Eliminar\"\nRename=\"Renomear\"\nInteract=\"Interactuar\"\nFilters=\"Filtros\"\nProperties=\"Propiedades\"\nMoveUp=\"Mover cara a arriba\"\nMoveDown=\"Mover cara a abaixo\"\nSettings=\"Axustes\"\nDisplay=\"Pantalla\"\nName=\"Nome\"\nExit=\"Saír\"\nMixer=\"Mesturador de son\"\nBrowse=\"Examinar\"\nStereo=\"Estéreo\"\nDroppedFrames=\"Fotogramas perdidos %1 (%2%)\"\nProjector.Open.Program=\"Abrir proxector do programa\"\nProjector.Open.Preview=\"Abrir proxector de previsualización\"\nProjector.Open.Scene=\"Abrir proxector de escena\"\nProjector.Open.Source=\"Proxector de código aberto\"\nProjector.Open.Multiview=\"Abrir multivista\"\nProjector.Display=\"Pantalla: %1\"\nProjector.Window=\"Nova xanela\"\nProjector.Title=\"Proxector\"\nProjector.Title.Scene=\"Escena: %1\"\nProjector.Title.Source=\"Fonte: %1\"\nProjector.Title.Multiview=\"Multivista\"\nProjector.ResizeWindowToContent=\"Axustar a xanela ao contido\"\nClear=\"Limpar\"\nRevert=\"Reverter\"\nShow=\"Amosar\"\nHide=\"Agochar\"\nUnhideAll=\"Amosar todo\"\nUntitled=\"Sen título\"\nNew=\"Novo\"\nDuplicate=\"Duplicar\"\nEnable=\"Activar\"\nDisableOSXVSync=\"Desactivar V-Sync do macOS\"\nResetOSXVSyncOnExit=\"Restabelecer V-Sync do macOS ao saír\"\nHighResourceUsage=\"Codificación sobrecargada! Coida rexeitar a configuración de vídeo ou empregar un preaxuste de codificación máis rápido.\"\nTransition=\"Transición\"\nQuickTransitions=\"Transicións rápidas\"\nFadeToBlack=\"Esvaecer para negro\"\nLeft=\"Esquerda\"\nRight=\"Dereita\"\nTop=\"Arriba\"\nBottom=\"Abaixo\"\nReset=\"Restabelecer\"\nHours=\"Horas\"\nMinutes=\"Minutos\"\nSeconds=\"Segundos\"\nDeprecated=\"Obsoleto\"\nReplayBuffer=\"Memoria temporal de reprodución (buffer)\"\nImport=\"Importar\"\nExport=\"Exportar\"\nCopy=\"Copiar\"\nPaste=\"Pegar\"\nPasteReference=\"Pegar (referencia)\"\nPasteDuplicate=\"Pegar (duplicado)\"\nRemuxRecordings=\"Gravacións convertidas\"\nNext=\"Seguinte\"\nBack=\"Atrás\"\nDefaults=\"Predeterminados\"\nRestoreDefaults=\"Predeterminados\"\nHideMixer=\"Agochar no mesturador\"\nTransitionOverride=\"Substitución da transición\"\nShowTransition=\"Amosar transición\"\nHideTransition=\"Agochar transición\"\nNone=\"Ningunha\"\nStudioMode.Preview=\"Vista previa\"\nStudioMode.Program=\"Programa\"\nStudioMode.PreviewSceneName=\"Vista previa: %1\"\nStudioMode.ProgramSceneName=\"Programa: %1\"\nShowInMultiview=\"Amosar en vista múltiple\"\nVerticalLayout=\"Disposición vertical\"\nGroup=\"Grupo\"\nDoNotShowAgain=\"Non volver amosar\"\nDefault=\"(Predeterminado)\"\nCalculating=\"Calculando...\"\nFullscreen=\"Pantalla completa\"\nWindowed=\"Na xanela\"\nRefreshBrowser=\"Actualizar\"\nAspectRatio=\"Proporción do aspecto <b>%1:%2</b>\"\nLockVolume=\"Bloquear o volume\"\nLogViewer=\"Visor de rexistros\"\nShowOnStartup=\"Amosar ao iniciar\"\nOpenFile=\"Abrir ficheiro\"\nAddScene=\"Engadir Escena\"\nAddSource=\"Engadir fonte\"\nRemoveScene=\"Eliminar escena seleccionada\"\nRemoveSource=\"Eliminar a(s) fonte(s) seleccionada(s)\"\nMoveSceneUp=\"Mover escena cara arriba\"\nMoveSceneDown=\"Mover escena cara abaixo\"\nMoveSourceUp=\"Mover a(s) fonte(s) cara arriba\"\nMoveSourceDown=\"Mover a(s) fonte(s) cara abaixo\"\nSourceProperties=\"Abrir propiedades da fonte\"\nSourceFilters=\"Abrir filtros da fonte\"\nMixerToolbarMenu=\"Menú do mesturador de audio\"\nSceneFilters=\"Abrir filtros da escena\"\nList=\"Lista\"\nGrid=\"Grella\"\nAutomatic=\"Automático\"\nComingSoon=\"Proximamente\"\nPluginsFailedToLoad.Title=\"Erro ao cargar o complemento\"\nPluginsFailedToLoad.Text=\"Os seguintes complementos de OBS non se puideron cargar:\\n\\n%1\\nPor favor, actualiza ou elimina estes complementos.\"\nAlreadyRunning.Title=\"O OBS xa está en execución\"\nAlreadyRunning.Text=\"O OBS xa está en execución. A non ser que quixeses facer isto, apaga as instancias existentes do OBS antes de tentar executar unha nova instancia. Se tes o OBS configurado para minimizar na bandexa do sistema, comproba se aínda está en execución alí.\"\nAlreadyRunning.LaunchAnyway=\"Iniciar de todos os xeitos\"\nCrashHandling.Dialog.Title=\"Detectouse un fallo en OBS Studio\"\nCrashHandling.Labels.Text=\"OBS Studio non se pechou correctamente.\\n\\nExecutar en modo seguro (complementos de terceiros, scripts e WebSocket desactivados)?\"\nCrashHandling.Labels.PrivacyNotice=\"Tamén podes optar por cargar automaticamente o informe de erros máis recente no OBSProject.<br /><br />Le a <a href='https://obsproject.com/privacy-policy'>Política de privacidade</a> antes de cargar calquera ficheiro e presta especial atención ás partes relativas á carga de ficheiros.\\n\"\nCrashHandling.Checkbox.SendReport=\"Lin a política de privacidade e dou o meu consentimento para a subida.\"\nCrashHandling.Buttons.LaunchSafe=\"Executar en modo seguro\"\nCrashHandling.Buttons.LaunchNormal=\"Executar en modo normal\"\nCrashHandling.Errors.UploadJSONError=\"Produciuse un erro ao tentar cargar o rexistro de erros máis recente. Téntao de novo máis tarde.\"\nCrashHandling.Errors.Title=\"Erro de carga do rexistro de erros\"\nSafeMode.Restart=\"Queres reiniciar OBS en modo seguro (complementos de terceiros, scripts e WebSockets desactivados)?\"\nSafeMode.RestartNormal=\"Queres reiniciar OBS en modo normal?\"\nChromeOS.Title=\"Plataforma non compatíbel\"\nChromeOS.Text=\"OBS parece estar a executarse dentro dun contedor de ChromeOS. Esta plataforma non está soportada.\"\nWine.Title=\"Wine detectado\"\nWine.Text=\"Executar OBS en Wine non está soportado, e moitas características como a captura ou as fontes de dispositivos non funcionarán ou só funcionarán de xeito limitado.<br><br>Recoméndase executar unha versión nativa de OBS, por exemplo, <a href='https://flathub.org/apps/details/com.obsproject.Studio'>a nosa versión Flatpak</a> ou os paquetes do teu sistema operativo.\"\nDockCloseWarning.Title=\"Pechar a xanela ancorábel\"\nDockCloseWarning.Text=\"Acabas de pechar unha xanela acoplable. Se desexas volver a mostrala, usa o menú de paneis na barra de menú.\"\nExtraBrowsers=\"Docas personalizadas do navegador\"\nExtraBrowsers.Info=\"Engade docas dándolles un nome e URL, após preme en Aplicar ou en Pechar para abrir as docas. Podes engadir ou retirar docas en calquera intre.\"\nExtraBrowsers.DockName=\"Nome da doca\"\nAuth.Authing.Title=\"Autenticando...\"\nAuth.Authing.Text=\"Autenticando con %1, agarde...\"\nAuth.AuthFailure.Title=\"Produciuse un fallo de autenticación\"\nAuth.AuthFailure.Text=\"Produciuse un fallo de autenticación con %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"É necesaria a autenticación\"\nAuth.InvalidScope.Text=\"Mudaron os requisitos de autenticación para %1. É posíbel que algunhas funcións non estean dispoñíbeis.\"\nAuth.LoadingChannel.Title=\"Cargando a información da canle...\"\nAuth.LoadingChannel.Text=\"Cargando a información da canle para %1, agarde...\"\nAuth.LoadingChannel.Error=\"Non se atopou a información do canal.\"\nAuth.ChannelFailure.Title=\"Produciuse un fallo ao cargar a canle\"\nAuth.ChannelFailure.Text=\"Produciuse un fallo ao cargar a información da canle para %1\\n\\n%2: %3\"\nAuth.Chat=\"Conversa\"\nAuth.StreamInfo=\"Información da emisión\"\nTwitchAuth.Stats=\"Estatísticas do Twitch\"\nTwitchAuth.Feed=\"Fonte de actividades do Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Non foi posíbel consultar a chave da emisión\"\nTwitchAuth.TwoFactorFail.Text=\"OBS non foi quen de conectarse á súa conta Twitch. Asegúrese de que a autenticación de dous factores estea configurada na súa <a href='https://www.twitch.tv/settings/security'>Configuración de seguridade do Twitch</a> porque é necesaria para facer a emisión.\"\nRestreamAuth.Channels=\"Canles Restream\"\nCopy.Filters=\"Copiar filtros\"\nPaste.Filters=\"Pegar filtros\"\nBrowserPanelInit.Title=\"Iniciando o navegador...\"\nBrowserPanelInit.Text=\"Iniciando o navegador, agarda...\"\nBandwidthTest.Region=\"Rexión\"\nBandwidthTest.Region.US=\"Estados Unidos\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Other=\"Outra\"\nBasic.AutoConfig=\"Asistente de configuración automática\"\nBasic.AutoConfig.ApplySettings=\"Aplicar os axustes\"\nBasic.AutoConfig.StartPage=\"Información de uso\"\nBasic.AutoConfig.StartPage.SubTitle=\"Especifique para que quere empregar o programa\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimizar para distribución de emisións, a gravación é secundaria\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimizar só para gravar, non imos facer distribución de emisións\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Solo usarei a cámara virtual\"\nBasic.AutoConfig.VideoPage=\"Axustes de vídeo\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Especifique os axustes de vídeo que lle gustaría usar\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Usar a actual (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Pantalla %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Usar a actual (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ou 30, con prioridade a 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ou 30, con prioridade a alta resolución\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Nota: a resolución do lenzo (base) non é necesariamente a mesma que a resolución coa que se vai emitir ou gravar. A resolución real da emisión/gravación pode reducirse a partir da resolución do lenzo para atenuar o consumo de recursos ou os requirimentos da taxa de bits.\"\nBasic.AutoConfig.StreamPage=\"Información da emisión\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Insira a información da súa emisión\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Conectar a conta (recomendado)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Desconectar conta\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Desconectar a conta?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Este cambio aplicarase de inmediato. Está seguro de que quere desconectar a súa conta?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Obter chave da emisión\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Máis Información\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Empregar chave da emisión\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Use Stream Key (avanzado)\"\nBasic.AutoConfig.StreamPage.Service=\"Servizo\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Amosar todo...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Personalizado...\"\nBasic.AutoConfig.StreamPage.Server=\"Servidor\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Chave da emisión\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: introduce a frase de cifrado.\\nRTMP: introduce a clave proporcionada polo servizo.\\nSRT: introduce o streamid se o servizo usa un.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Chave do codificador\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Token de portador\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Contas conectadas\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Estimar a taxa de bits coa proba de largo de banda (pode levar uns minutos)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferir codificación por hardware\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"A codificación por hardware elimina a maior parte do uso da CPU, pero pode requirir unha maior taxa de bits para obter o mesmo nivel de calidade.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Aviso da emisión\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"A proba de largo de banda está a piques de emitir datos de vídeo ao chou sen son á súa canle. Se é posíbel, recoméndase desactivar temporalmente o gardado dos vídeos das emisións e estabelecer a emisión coma privada até que remate a proba. Continuar?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Proba %1\"\nBasic.AutoConfig.TestPage=\"Resultados finais\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"O programa está executando agora un conxunto de probas para estimar os axustes ideais\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Proba completa\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Realizando a proba de largo de banda, isto pode levar uns minutos...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Non se atopou saída para o protocolo deste servizo.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Conectando a: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Produciuse un fallo ao conectarse a calquera servidor, comprobe a conexión a Internet e ténteo de novo.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Probando o largo de banda para: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Probando o codificador da emisión, isto pode levar un minuto...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Probando o codificador da gravación, isto pode levar un minuto...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Produciuse un erro ao iniciar o codificador\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Probando %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Codificador da emisión\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Codificador da gravación\"\nBasic.AutoConfig.TestPage.Result.Header=\"O programa determinou que estes axustes estimados son os máis idóneos para vostede:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Para empregar estes axustes, preme en Aplicar os axustes. Para volver configurar o asistente e tentalo de novo, preme en Atrás. Para configurar de xeito manual os axustes, preme en Cancelar e abre Axustes.\"\nBasic.AutoConfig.Info=\"O asistente de configuración automática determinará os mellores axustes baseandose nas especificacións do seu computador e a velocidade da Internet.\"\nBasic.AutoConfig.RunAnytime=\"Isto pódese executar en calquera momento dende o menú Ferramentas.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Resolución da transmisión (escalada)\"\nBasic.Stats=\"Estatísticas\"\nBasic.Stats.CPUUsage=\"Uso da CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Espazo no disco dispoñíbel\"\nBasic.Stats.MemoryUsage=\"Uso da memoria\"\nBasic.Stats.AverageTimeToRender=\"Tempo de media para xerar os fotogramas\"\nBasic.Stats.SkippedFrames=\"Fotogramas omitidos pola demora na codificación\"\nBasic.Stats.MissedFrames=\"Fotogramas perdidos pola demora na xeración\"\nBasic.Stats.Output.Stream=\"Emisión\"\nBasic.Stats.Output.Recording=\"Gravando\"\nBasic.Stats.Status=\"Estado\"\nBasic.Stats.Status.Recording=\"Gravando\"\nBasic.Stats.Status.Live=\"AO VIVO\"\nBasic.Stats.Status.Reconnecting=\"Reconectando\"\nBasic.Stats.Status.Inactive=\"Inactivo\"\nBasic.Stats.Status.Active=\"Activo\"\nBasic.Stats.DroppedFrames=\"Fotogramas perdidos (rede)\"\nBasic.Stats.MegabytesSent=\"Saída total de datos\"\nBasic.Stats.Bitrate=\"Taxa de bits\"\nBasic.Stats.DiskFullIn=\"Disco cheo en (aprox.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 Horas, %2 Minutos\"\nBasic.Stats.ResetStats=\"Restabelecer estatísticas\"\nResetUIWarning.Title=\"Confirma que quere restabelecer a interface de usuario?\"\nResetUIWarning.Text=\"Restablecer a interface ocultará paneis adicionais. Necesitarás volver a amosar estes paneis desde o menú de Paneis se desexas que sexan visibles.\\n\\nEstás certo de que queres restablecer a interface?\"\nUpdater.Title=\"Nova actualización dispoñíbel\"\nUpdater.Text=\"Hai unha nova actualización dispoñíbel:\"\nUpdater.UpdateNow=\"Actualizar agora\"\nUpdater.RemindMeLater=\"Lembrarmo máis tarde\"\nUpdater.Skip=\"Omitir a versión\"\nUpdater.NoUpdatesAvailable.Title=\"Non hai actualizacións dispoñíbeis\"\nUpdater.NoUpdatesAvailable.Text=\"Non hai actualizacións dispoñíbeis actualmente\"\nUpdater.BranchNotFound.Title=\"Canle de actualización eliminado\"\nUpdater.BranchNotFound.Text=\"A canle de actualización seleccionada xa non está dispoñible, OBS foi restablecido á configuración predeterminada.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Comprobación de integridade non dispoñible\"\nUpdater.RepairButUpdatesAvailable.Text=\"Comprobar a integridade dos ficheiros só é posible para a última versión dispoñible. Usa Axuda → Comprobar actualizacións para verificar e actualizar a túa instalación de OBS.\"\nUpdater.RepairConfirm.Title=\"Confirmar comprobación de integridade\"\nUpdater.RepairConfirm.Text=\"Iniciar a comprobación de integridade escaneará a túa instalación de OBS en busca de corrupción e volverá descargar os ficheiros rotos ou modificados. Isto pode levar un momento.\\n\\nDesexas continuar?\"\nUpdater.FailedToLaunch=\"Produciuse un fallo ao iniciar o actualizador\"\nQuickTransitions.SwapScenes=\"Intercambiar escenas de vista previa/programa despois da transición\"\nQuickTransitions.SwapScenesTT=\"Intercambia as escenas de vista previa e programa despois da transición (se a escena orixinal do programa aínda existe).\\nIsto non desfará ningún cambio que se puidese facer na escena orixinal do programa.\"\nQuickTransitions.DuplicateScene=\"Escena duplicada\"\nQuickTransitions.DuplicateSceneTT=\"Ao editar a mesma escena, permítese editar a transformación/visibilidade das fontes sen modificar a saída do programa.\\nPara editar as propiedades das fontes sen modificar a saída do programa, activa 'Duplicar fontes'.\\nCambiar este valor restablecerá a escena actual do programa (se aínda existe).\"\nQuickTransitions.EditProperties=\"Fontes duplicadas\"\nQuickTransitions.EditPropertiesTT=\"Ao editar a mesma escena, permítese editar as propiedades das fontes sen modificar a saída do programa.\\nIsto só se pode usar se 'Duplicar escena' está activado.\\nAlgunhas fontes (como as de captura ou multimedia) non admiten esta opción e non se poden editar por separado.\\nCambiar este valor restablecerá a escena actual do programa (se aínda existe).\\n\\nAviso: Como as fontes serán duplicadas, isto pode requirir recursos adicionais do sistema ou de vídeo.\"\nQuickTransitions.HotkeyName=\"Transición rápida: %1\"\nBasic.AddTransition=\"Engadir transición configurábel\"\nBasic.RemoveTransition=\"Eliminar transición configurábel\"\nBasic.TransitionProperties=\"Propiedades da transición\"\nBasic.SceneTransitions=\"Transicións de escenas\"\nBasic.TransitionDuration=\"Duración\"\nBasic.TogglePreviewProgramMode=\"Modo de estudio\"\nBasic.EnablePreviewProgramMode=\"Activar o modo de estudio\"\nBasic.DisablePreviewProgramMode=\"Desactivar o modo de estudio\"\nUndo.Undo=\"Desfacer\"\nUndo.Redo=\"Refacer\"\nUndo.Add=\"Engadir %1'\"\nUndo.Delete=\"Eliminar %1\"\nUndo.Rename=\"Renomear '%1'\"\nUndo.SceneCollection.Switch=\"Cambiar a '%1'\"\nUndo.Item.Undo=\"Desfacer %1\"\nUndo.Item.Redo=\"Refacer %1\"\nUndo.Sources.Multi=\"Elimiñar %1 fontes\"\nUndo.Filters=\"Filtrar cambios en '%1'\"\nUndo.Filters.Paste.Single=\"Pega o filtro '%1' en '%2'\"\nUndo.Filters.Paste.Multiple=\"Copia o Filtro desde '%1' a '%2'\"\nUndo.Transform=\"Transformar fonte(s) en '%1'\"\nUndo.Transform.Paste=\"Pegar trasformando en '%1'\"\nUndo.Transform.Rotate=\"Rotación en '%1'\"\nUndo.Transform.Reset=\"Trasformación predefinida en '%1'\"\nUndo.Transform.HFlip=\"Xiro horizontal de '%1'\"\nUndo.Transform.VFlip=\"Xiro vertical de '%1'\"\nUndo.Transform.FitToScreen=\"Axustar á pantalla en '%1'\"\nUndo.Transform.StretchToScreen=\"Estirar á pantalla en '%1'\"\nUndo.Transform.Center=\"Centrar á pantalla en '%1'\"\nUndo.Transform.VCenter=\"Pantalla centrada en Vertical en '%1'\"\nUndo.Transform.HCenter=\"Pantalla Centrada Horizontal en '%1'\"\nUndo.Volume.Change=\"Trocar volume en '%1'\"\nUndo.Volume.Mute=\"Silenciar '%1'\"\nUndo.Volume.Unmute=\"Escoitar '%1'\"\nUndo.Balance.Change=\"Trocar o balance de audio en '%1'\"\nUndo.SyncOffset.Change=\"Cambiar a compensación de sincronización do audio en '%1'\"\nUndo.MonitoringType.Change=\"Cambiar a monitorización de audio en '%1'\"\nUndo.Mixers.Change=\"Cambiar os mesturadores de audio en '%1'\"\nUndo.ForceMono.On=\"Activar forzado de mono en '%1'\"\nUndo.ForceMono.Off=\"Desactivar mono forzado en '%1'\"\nUndo.Properties=\"Trocar propiedades de '%1'\"\nUndo.Scene.Duplicate=\"Duplicar escea '%1'\"\nUndo.ShowTransition=\"Amosar trasnsición en '%1'\"\nUndo.HideTransition=\"Agochar a transición en '%1'\"\nUndo.ShowSceneItem=\"Amosar '%1' en '%2'\"\nUndo.HideSceneItem=\"Agochar '%1' en '%2'\"\nUndo.ReorderSources=\"Reordear fontes en '%1'\"\nUndo.MoveUp=\"Mover '%1' cara arriba en '%2'\"\nUndo.MoveDown=\"Mover '%1' cara abaixo en '%2'\"\nUndo.MoveToTop=\"Mover '%1' o mais alto en '%2'\"\nUndo.MoveToBottom=\"Mover '%1' ó fondo en '%2'\"\nUndo.PasteSource=\"Pegar fonte(s) en «%1»\"\nUndo.PasteSourceRef=\"Pegar referencia(s) de fonte en '%1'\"\nUndo.GroupItems=\"Agrupar elementos en '%1'\"\nTransitionNameDlg.Text=\"Insira o nome da transición\"\nTransitionNameDlg.Title=\"Nome da transición\"\nTitleBar.SafeMode=\"MODO SEGURO\"\nTitleBar.PortableMode=\"Modo portátil\"\nTitleBar.Profile=\"Perfil\"\nTitleBar.Scenes=\"Escenas\"\nNameExists.Title=\"O nome xa existe\"\nNameExists.Text=\"O nome xa está en uso.\"\nNoNameEntered.Title=\"Insira un nome válido\"\nNoNameEntered.Text=\"Non é posíbel empregar nomes baleiros.\"\nConfirmStart.Title=\"Comezar a emisión?\"\nConfirmStart.Text=\"Confirma que quere iniciar a emisión?\"\nConfirmStop.Title=\"Deter a emisión?\"\nConfirmStop.Text=\"Confirma que quere deter a emisión?\"\nConfirmStopRecord.Title=\"Deter a gravación?\"\nConfirmStopRecord.Text=\"Confirma que quere deter a gravación?\"\nConfirmBWTest.Title=\"Iniciar a proba do largo de banda?\"\nConfirmBWTest.Text=\"Tes o OBS configurado no modo de proba de largo de banda. Este modo permite probar a rede sen que a túa canle entre en directo. Unha vez rematada a proba, terás que desactivalo para que os espectadores poidan ver a túa emisión.\\n\\nQueres continuar?\"\nConfirmExit.Title=\"Saídas activas\"\nConfirmExit.Text=\"O OBS segue activo. Finalizaranse todas as transmisións/gravacións.\"\nConfirmRemove.Title=\"Confirmar a retirada\"\nConfirmRemove.Text=\"Estás seguro de que desexas eliminar '%1'?\"\nConfirmRemove.TextMultiple=\"Confirmas que queres eliminar %1 elementos?\"\nConfirmReset.Title=\"Restablecer propiedades\"\nConfirmReset.Text=\"Estás seguro de que desexas restablecer as propiedades actuais aos seus valores predeterminados?\"\nOutput.StartStreamFailed=\"Produciuse un fallo ao iniciar a emisión\"\nOutput.StartRecordingFailed=\"Produciuse un fallo ao iniciar a gravación\"\nOutput.StartReplayFailed=\"Produciuse un fallo ao iniciar a reprodución da memoria temporal\"\nOutput.StartVirtualCamFailed=\"Non se puido iniciar a cámara virtual\"\nOutput.StartFailedGeneric=\"Produciuse unha falla na saída. Comproba o rexistro para ollar os detalles.\\n\\nNota: Se estás a empregar codificadores NVENC ou AMD, asegúrate de que os controladores de vídeo estean actualizados.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Non é posíbel gardar as reproducións mentres estea en pausa\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Aviso: non é posíbel gardar as reproducións mentres a gravación estea en pausa.\"\nOutput.ConnectFail.Title=\"Produciuse un erro ao se conectar\"\nOutput.ConnectFail.BadPath=\"Ruta ou URL de conexión non válidos. Comprobe os axustes para confirmar que son correctos.\"\nOutput.ConnectFail.ConnectFailed=\"Produciuse un fallo ao conectar co servidor\"\nOutput.ConnectFail.InvalidStream=\"Non foi posíbel acceder á canle ou clave da emisión especificada, faga a dobre comprobación da clave da emisión. Se é correcta, pode haber un problema na conexión ao servidor.\"\nOutput.ConnectFail.HdrDisabled=\"A saída HDR está actualmente desactivada para esta saída.\"\nOutput.ConnectFail.Error=\"Produciuse un erro inesperado ao tentar conectar co servidor. Máis información no ficheiro de rexistro.\"\nOutput.ConnectFail.Disconnected=\"Desconectado do servidor.\"\nOutput.StreamEncodeError.Title=\"Produciuse un erro de codificación\"\nOutput.StreamEncodeError.Msg=\"Produciuse un erro de codificación durante a emisión.\"\nOutput.StreamEncodeError.Msg.LastError=\"Produciuse un erro do codificador mentres se transmitía:<br><br>%1\"\nOutput.RecordFail.Title=\"Produciuse un erro ao iniciar a gravación\"\nOutput.RecordFail.Unsupported=\"O formato de saída non é compatíbel ou non admite máis dunha pista de son. Verifique a súa configuración e ténteo de novo.\"\nOutput.RecordNoSpace.Title=\"Non hai espazo abondo no disco\"\nOutput.RecordNoSpace.Msg=\"Non hai espazo abondo no disco para continuar coa gravación.\"\nOutput.RecordError.Title=\"Produciuse un erro de gravación\"\nOutput.RecordError.Msg=\"Produciuse un erro non especificado durante a gravación.\"\nOutput.RecordError.EncodeErrorMsg=\"Produciuse un erro de codificación durante a gravación.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Produciuse un erro de codificación mentres se gravaba:<br><br>%1\"\nOutput.BadPath.Title=\"Ruta do ficheiro incorrecta\"\nOutput.BadPath.Text=\"Non se puido abrir a ruta de gravación configurada. Comprobe a súa ruta de gravación en Configuración → Saída → Gravación.\"\nOutput.NoBroadcast.Title=\"Sen emisión configurada\"\nOutput.NoBroadcast.Text=\"Debes configurar unha retransmisión antes de poder comezar a transmitir.\"\nOutput.BroadcastStartFailed=\"Non se puido iniciar a retransmisión\"\nOutput.BroadcastStopFailed=\"Non se puido detener a retransmisión\"\nLogUploadDialog.Title=\"Subida do ficheiro de rexistro de OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Por favor lea a <a href='https://obsproject.com/privacy-policy'>Política de privacidade</a> e a súa sección relativa á subida de ficheiros antes de subir calquera ficheiro.\"\nLogUploadDialog.Labels.Progress=\"Subida do rexistro en progreso. Agarde, por favor...\"\nLogUploadDialog.Labels.Description.AppLog=\"O teu ficheiro de rexistro foi subido. Agora podes compartir a URL para fins de depuración ou soporte.\"\nLogUploadDialog.Labels.Description.CrashLog=\"O teu informe de erro foi subido. Agora podes compartir a URL para fins de depuración.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Subir\"\nLogUploadDialog.Buttons.CopyURL=\"Copiar URL do rexistro\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analizar o ficheiro de rexistro\"\nLogUploadDialog.Buttons.RetryButton=\"Volver tentar\"\nLogUploadDialog.Errors.Template=\"Produciuse un erro ao tentar cargar o ficheiro:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Non se atopou ningún ficheiro para cargar ou o ficheiro estaba baleiro.\"\nRemux.SourceFile=\"Gravación do OBS\"\nRemux.TargetFile=\"Ficheiro de destino\"\nRemux.Remux=\"Converter\"\nRemux.Stop=\"Deter a conversión\"\nRemux.ClearFinished=\"Limpar os elementos rematados\"\nRemux.ClearAll=\"Limpar todos os elementos\"\nRemux.OBSRecording=\"Gravación OBS\"\nRemux.FinishedTitle=\"Conversión rematada\"\nRemux.Finished=\"Gravando a conversión\"\nRemux.FinishedError=\"Gravación convertida, mais o ficheiro podería estar incompleto\"\nRemux.SelectRecording=\"Seleccionar a gravación OBS...\"\nRemux.SelectTarget=\"Seleccionar o ficheiro de destino...\"\nRemux.FileExistsTitle=\"Xa existen ficheiros de destino\"\nRemux.FileExists=\"Os ficheiros de destino seguintes xa existen. Quere substituílos?\"\nRemux.ExitUnfinishedTitle=\"Conversión en proceso\"\nRemux.ExitUnfinished=\"A conversión aínda non rematou, detela agora podería provocar que o ficheiro final fose inutilizábel.\\nConfirma que quere deter a conversión?\"\nRemux.HelpText=\"Solta os ficheiros para converter nesta xanela ou selecciona unha cela \\\"Gravación OBS\\\" baleira para procurar un ficheiro.\"\nRemux.NoFilesAddedTitle=\"Non se engadiu ningún ficheiro para converter\"\nRemux.NoFilesAdded=\"Non se engadiu ningún ficheiro para converter. Arrastra unha carpeta que conteña un ou máis ficheiros de vídeo.\"\nMissingFiles=\"Ficheiros que faltan\"\nMissingFiles.MissingFile=\"Ficheiro que falta\"\nMissingFiles.NewFile=\"Novo ficheiro\"\nMissingFiles.HelpText=\"Faltan algúns ficheiros dende que utilizou OBS por última vez.\"\nMissingFiles.Clear=\"<baleiro>\"\nMissingFiles.NumFound=\"Atopado %1 de %2\"\nMissingFiles.Search=\"Directorio de busca...\"\nMissingFiles.SelectFile=\"Seleccionar ficheiro...\"\nMissingFiles.SelectDir=\"Seleccionar o cartafol no que buscar\"\nMissingFiles.State=\"Estado\"\nMissingFiles.Missing=\"Falta\"\nMissingFiles.Replaced=\"Substituído\"\nMissingFiles.Cleared=\"Limpado\"\nMissingFiles.Found=\"Atopado\"\nMissingFiles.AutoSearch=\"Atopáronse coincidencias de ficheiros adicionais\"\nMissingFiles.AutoSearchText=\"OBS atopou coincidencias adicionais para os ficheiros que faltan nese directorio. Quere engadilos?\"\nMissingFiles.NoMissing.Title=\"Comprobación de ficheiros ausentes\"\nMissingFiles.NoMissing.Text=\"Non parece que falten ficheiros.\"\nMacPermissions.MenuAction=\"Review App Permissions…\"\nMacPermissions.Title=\"Revisar permisos da aplicación\"\nMacPermissions.Description=\"OBS Studio require o teu permiso para poder ofrecer certas funcións. Recoméndase activar estes permisos, pero non son necesarios para usar a aplicación. Sempre podes activalos máis adiante.\"\nMacPermissions.Description.OpenDialog=\"Podes volver abrir este diálogo a través do menú de OBS Studio.\"\nMacPermissions.AccessGranted=\"Acceso permitido\"\nMacPermissions.RequestAccess=\"Solicitar acceso\"\nMacPermissions.OpenPreferences=\"Abrir as preferencias de %1\"\nMacPermissions.Item.ScreenRecording=\"Gravación de pantalla\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS require este permiso para poder capturar a túa pantalla.\"\nMacPermissions.Item.Camera=\"Cámara\"\nMacPermissions.Item.Camera.Details=\"Este permiso é necesario para capturar contidos desde unha webcam ou unha tarxeta de captura.\"\nMacPermissions.Item.Microphone=\"Micrófono\"\nMacPermissions.Item.Microphone.Details=\"OBS require este permiso se desexas capturar o teu micrófono ou un dispositivo de son externo.\"\nMacPermissions.Item.InputMonitoring=\"Monitorización de entrada\"\nMacPermissions.Item.InputMonitoring.Details=\"Este permiso é necesario para que as teclas de atallo funcionen mentres OBS está en segundo plano.\"\nMacPermissions.Item.Accessibility=\"Accesibilidade\"\nMacPermissions.Item.Accessibility.Details=\"En instalacións máis antigas, OBS pode aparecer listado en \\\"Accesibilidade\\\" en lugar de \\\"Monitorización de entrada\\\".\"\nMacPermissions.Continue=\"Continuar\"\nSourceLeak.Title=\"Erro na limpeza da fonte\"\nSourceLeak.Text=\"Produciuse un problema ao cambiar as coleccións de escenas e algunhas fontes non puideron ser descargadas. Este problema adoita ser causado por complementos que non liberan os recursos correctamente. Por favor, asegúrate de que calquera complemento que esteas a utilizar estea actualizado.\\n\\nOBS Studio sairá agora para previr posibles corrupcións de datos.\"\nBasic.DesktopDevice1=\"Son do escritorio\"\nBasic.DesktopDevice2=\"Son do escritorio 2\"\nBasic.Scene=\"Escena\"\nBasic.DisplayCapture=\"Captura de pantalla\"\nBasic.Main.PreviewConextMenu.Enable=\"Activar a vista previa\"\nBasic.Main.Preview.Disable=\"Desactivar a vista previa\"\nScaleFiltering=\"Filtraxe de escala\"\nScaleFiltering.Point=\"Punto\"\nScaleFiltering.Bicubic=\"Bicúbico\"\nScaleFiltering.Area=\"Área\"\nBlendingMethod=\"Método de Mestura\"\nBlendingMethod.Default=\"Predeterminado\"\nBlendingMethod.SrgbOff=\"SRGB desactivado\"\nBlendingMode=\"Modo de Mestura\"\nBlendingMode.Additive=\"Engadir\"\nBlendingMode.Subtract=\"Restar\"\nBlendingMode.Screen=\"Pantalla\"\nBlendingMode.Multiply=\"Multiplicar\"\nBlendingMode.Lighten=\"Aclarar\"\nBlendingMode.Darken=\"Escurecer\"\nDeinterlacing=\"Desentrelazado\"\nDeinterlacing.Discard=\"Desbotar\"\nDeinterlacing.TopFieldFirst=\"Primeiro o campo superior\"\nDeinterlacing.BottomFieldFirst=\"Primeiro o campo inferior\"\nVolControl.SliderUnmuted=\"Potenciómetro de volume para «%1»:\"\nVolControl.SliderMuted=\"Potenciómetro de volume para «%1»: (actualmente silenciado)\"\nVolControl.Mute=\"Silenciar «%1»\"\nVolControl.Properties=\"Propiedades para '%1'\"\nVolControl.UnassignedWarning.Title=\"Fonte de son sen asignar\"\nVolControl.UnassignedWarning.Text=\"%1\\\" non está asignado a ningunha pista de son e non se escoitará nas retransmisións ou gravacións.\\n\\nPara asignar unha fonte de son a unha pista, abre as Propiedades Avanzadas de Son a través do menú co botón dereito ou co botón de engrenaxe na barra de ferramentas do panel do mesturador.\"\nBasic.Main.AddSceneDlg.Title=\"Engadir escena\"\nBasic.Main.AddSceneDlg.Text=\"Insira un nome para a escena\"\nBasic.Main.DefaultSceneName.Text=\"Escena %1\"\nBasic.Main.AddSceneCollection.Title=\"Engadir colección de escenas\"\nBasic.Main.AddSceneCollection.Text=\"Insira o nome da colección de escenas\"\nBasic.Main.RenameSceneCollection.Title=\"Renomear a colección de escenas\"\nBasic.Main.RemigrateSceneCollection.Title=\"Actualizar a resolución da colección de escenas\"\nBasic.Main.RemigrateSceneCollection.Text=\"Queres actualizar a resolución da colección de escenas de \\\"%1\\\" para coincidir coa resolución do lenzo do perfil actual de %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Non se pode actualizar a resolución da colección de escenas mentres as saídas están activas.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Fallou a actualización da resolución da colección de escenas. A resolución orixinal é descoñecida.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Reiniciar non é posible: Fallou o cambio de resolución en OBS.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Reiniciar non é posible: A resolución actual xa é a resolución base da colección de escenas.\"\nAddProfile.Title=\"Engadir perfil\"\nAddProfile.Text=\"Insira o nome do perfil\"\nAddProfile.WizardCheckbox=\"Amosar o asistente de configuración automática\"\nRenameProfile.Title=\"Renomear o perfil\"\nBasic.Main.MixerRename.Title=\"Renomear a fonte de son\"\nBasic.Main.MixerRename.Text=\"Insira un nome para a fonte de son\"\nBasic.Main.PreviewDisabled=\"A vista previa está desactivada actualmente\"\nBasic.SourceSelect=\"Crear/seleccionar fonte\"\nBasic.SourceSelect.CreateNew=\"Crear nova\"\nBasic.SourceSelect.AddExisting=\"Engadir existente\"\nBasic.SourceSelect.AddVisible=\"Facer visíbel a fonte\"\nBasic.Main.Sources.Visibility=\"Visibilidade\"\nBasic.Main.Sources.VisibilityDescription=\"Controla a visibilidade de «%1» no lenzo\"\nBasic.Main.Sources.Lock=\"Bloquear\"\nBasic.Main.Sources.LockDescription=\"Bloquea a posición e escala de «%1» no lenzo\"\nBasic.PropertiesWindow=\"Propiedades para «%1»\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (seleccionar automaticamente: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Seleccionar cor\"\nBasic.PropertiesWindow.SelectFont=\"Seleccionar o tipo de letra\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Elixir unha fonte\"\nBasic.PropertiesWindow.ConfirmTitle=\"Axustes mudados\"\nBasic.PropertiesWindow.Confirm=\"Hai cambios sen gardar. Quere conservalos?\"\nBasic.PropertiesWindow.NoProperties=\"Non hai ningunha propiedade dispoñíbel\"\nBasic.PropertiesWindow.AddFiles=\"Engadir ficheiros\"\nBasic.PropertiesWindow.AddDir=\"Engadir directorio\"\nBasic.PropertiesWindow.AddURL=\"Engadir ruta/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Engadir directorio a «%1»\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Engadir ficheiros a «%1»\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Engadir entrada a «%1»\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Editar entrada dende «%1»\"\nBasic.PropertiesView.FPS.Simple=\"Valores FPS sinxelos\"\nBasic.PropertiesView.FPS.Rational=\"Valores racionais FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Intervalos FPS válidos:\"\nBasic.PropertiesView.UrlButton.Text=\"Abrir esta ligazón no seu navegador predeterminado?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Abrir URL\"\nBasic.InteractionWindow=\"Interactuando con «%1»\"\nBasic.StatusBar.Reconnecting=\"Desconectado, reconectando en %2 segundo(s) (tentativa número %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Tentando reconectar... (tentativa número %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Reconexión satisfactoria\"\nBasic.StatusBar.Delay=\"Atraso (%1 seg)\"\nBasic.StatusBar.DelayStartingIn=\"Atraso (iniciando en %1 seg)\"\nBasic.StatusBar.DelayStoppingIn=\"Atraso (detendo en %1 seg)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Atraso (detendo en %1 seg, iniciando en %2 seg)\"\nBasic.StatusBar.RecordingSavedTo=\"Gravación gardada en '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Modo de almacenamento de repetición gardado en '%1\"\nBasic.StatusBar.ScreenshotSavedTo=\"\\\"Captura de pantalla gardada en '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Gravación convertida automáticamente a '%1'\"\nBasic.Filters=\"Filtros\"\nBasic.Filters.AsyncFilters=\"Filtros de son/vídeo\"\nBasic.Filters.AudioFilters=\"Filtros de son\"\nBasic.Filters.EffectFilters=\"Filtros de efectos\"\nBasic.Filters.Title=\"Filtros para «%1»\"\nBasic.Filters.AddFilter.Title=\"Nome do filtro\"\nBasic.Filters.AddFilter.Text=\"Especifique o nome do filtro\"\nBasic.TransformWindow=\"Axustes da escena\"\nBasic.TransformWindow.Position=\"Posición\"\nBasic.TransformWindow.PositionX=\"Posición X\"\nBasic.TransformWindow.PositionY=\"Posición Y\"\nBasic.TransformWindow.Rotation=\"Rotación\"\nBasic.TransformWindow.Size=\"Tamaño\"\nBasic.TransformWindow.Width=\"Largura\"\nBasic.TransformWindow.Height=\"Altura\"\nBasic.TransformWindow.Alignment=\"Aliñamento\"\nBasic.TransformWindow.BoundsType=\"Tipo de caixa contedora\"\nBasic.TransformWindow.BoundsAlignment=\"Aliñamento na caixa contedora\"\nBasic.TransformWindow.Bounds=\"Tamaño da caixa contedora\"\nBasic.TransformWindow.BoundsWidth=\"Largura do cadro delimitador\"\nBasic.TransformWindow.BoundsHeight=\"Altura do cadro delimitador\"\nBasic.TransformWindow.CropToBounds=\"Recortar ata o cadrado delimitador\"\nBasic.TransformWindow.Crop=\"Recortar\"\nBasic.TransformWindow.CropLeft=\"Cortar á esquerda\"\nBasic.TransformWindow.CropRight=\"Cortar á dereita\"\nBasic.TransformWindow.CropTop=\"Cortar enriba\"\nBasic.TransformWindow.CropBottom=\"Cortar embaixo\"\nBasic.TransformWindow.Alignment.TopLeft=\"Arriba á esquerda\"\nBasic.TransformWindow.Alignment.TopCenter=\"Arriba no centro\"\nBasic.TransformWindow.Alignment.TopRight=\"Arriba á dereita\"\nBasic.TransformWindow.Alignment.CenterLeft=\"No centro á esquerda\"\nBasic.TransformWindow.Alignment.Center=\"No centro\"\nBasic.TransformWindow.Alignment.CenterRight=\"No centro á dereita\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Abaixo á esquerda\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Abaixo no centro\"\nBasic.TransformWindow.Alignment.BottomRight=\"Abaixo á dereita\"\nBasic.TransformWindow.BoundsType.None=\"Sen límites\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Limitado ao tamaño máximo\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Escalar ata límites internos\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Escalar ata límites externos\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Escalar á largura dos límites\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Escalar á altura dos límites\"\nBasic.TransformWindow.BoundsType.Stretch=\"Estricar ata os límites\"\nBasic.TransformWindow.Title=\"Editar transformación para '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Ningunha fonte seleccionada\"\nBasic.Main.AddSourceHelp.Title=\"Non é posíbel engadir a fonte\"\nBasic.Main.AddSourceHelp.Text=\"Ten que ter cando menos 1 escena para engadir unha fonte.\"\nBasic.Main.Scenes=\"Escenas\"\nBasic.Main.Sources=\"Fontes\"\nBasic.Main.Source=\"Orixe\"\nBasic.Main.Controls=\"Controis\"\nBasic.Main.PreparingStream=\"Preparando...\"\nBasic.Main.Connecting=\"Conectando...\"\nBasic.Main.StartRecording=\"Iniciar a gravación\"\nBasic.Main.StartReplayBuffer=\"Iniciar a reprodución da memoria temporal\"\nBasic.Main.SaveReplay=\"Gardar a reprodución\"\nBasic.Main.StartStreaming=\"Comezar a emisión\"\nBasic.Main.StartBroadcast=\"En Vivo\"\nBasic.Main.StartVirtualCam=\"Iniciar a cámara virtual\"\nBasic.Main.StopRecording=\"Deter a gravación\"\nBasic.Main.PauseRecording=\"Pór en pausa a gravación\"\nBasic.Main.UnpauseRecording=\"Reiniciar a gravación\"\nBasic.Main.SplitFile=\"Dividir ficheiro de gravación\"\nBasic.Main.AddChapterMarker=\"Engadir marcador de capítulo (só MP4/MOV híbridos)\"\nBasic.Main.StoppingRecording=\"Detendo a gravación...\"\nBasic.Main.StopReplayBuffer=\"Deter a reprodución da memoria temporal\"\nBasic.Main.StoppingReplayBuffer=\"Detendo a reprodución da memoria temporal...\"\nBasic.Main.SetupBroadcast=\"Xestionar emisión en vivo\"\nBasic.Main.StopStreaming=\"Deter a emisión\"\nBasic.Main.StopBroadcast=\"Rematar Transmisión\"\nBasic.Main.AutoStopEnabled=\"(Parada automática)\"\nBasic.Main.StoppingStreaming=\"Detendo a emisión...\"\nBasic.Main.ForceStopStreaming=\"Deter emisión (desbotar atraso)\"\nBasic.Main.ShowContextBar=\"Amosar a barra de ferramentas da fonte\"\nBasic.Main.HideContextBar=\"Agochar a barra de ferramentas da fonte\"\nBasic.Main.StopVirtualCam=\"Deter cámara virtual\"\nBasic.Main.Group=\"Grupo %1\"\nBasic.Main.GroupItems=\"Agrupar os elementos seleccionados\"\nBasic.Main.Ungroup=\"Desagrupar\"\nBasic.Main.GridMode=\"Modo grella\"\nBasic.Main.ListMode=\"Modo lista\"\nBasic.Main.VirtualCamConfig=\"Configurar cámara virtual\"\nBasic.VCam.VirtualCamera=\"Cámara virtual\"\nBasic.VCam.OutputType=\"Tipo de saída\"\nBasic.VCam.OutputSelection=\"Selección de saída\"\nBasic.VCam.OutputType.Program=\"Programa (Predeterminado)\"\nBasic.VCam.OutputSelection.NoSelection=\"Sen selección para este tipo de saída\"\nBasic.VCam.RestartWarning=\"A cámara virtual será reiniciada para aplicar este cambio\"\nBasic.MainMenu.File=\"&Ficheiro\"\nBasic.MainMenu.File.Export=\"&Exportar\"\nBasic.MainMenu.File.Import=\"&Importar\"\nBasic.MainMenu.File.ShowRecordings=\"Amosar g&ravacións\"\nBasic.MainMenu.File.Remux=\"Gravacións de conversións (&M)\"\nBasic.MainMenu.File.Settings=\"Axu&stes\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Amosar o cartafol dos axustes\"\nBasic.MainMenu.File.ShowProfileFolder=\"Amosar o cartafol do perfil\"\nBasic.MainMenu.File.ShowMissingFiles=\"Comprobar se hai ficheiros que faltan\"\nBasic.MainMenu.File.Exit=\"Saír (&X)\"\nBasic.MainMenu.Edit=\"&Editar\"\nBasic.MainMenu.Edit.Undo=\"Desfacer (&U)\"\nBasic.MainMenu.Edit.Redo=\"&Refacer\"\nBasic.MainMenu.Edit.LockPreview=\"Vista previa do b&loqueo\"\nBasic.MainMenu.Edit.Scale=\"E&scalado da vista previa\"\nBasic.MainMenu.Edit.Scale.Window=\"Escalar á xanela\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Lenzo (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Saída (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Redimensionado (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Ampliar\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Reducir\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Restablecer Zoom\"\nBasic.MainMenu.Edit.Transform=\"&Transformar\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Editar a transformación...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Copiar a transformación\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Pegar a transformación\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Restabelecer a transformación\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Rotar 90 graos no sentido das agullas do reloxo\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Rotar 90 graos contra o sentido das agullas do relox\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Rotar 180 graos\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Inverter &horizontalmente\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Inverter &verticalmente\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Axustar á pantalla (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"E&stricar á pantalla\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centrar na pantalla\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centrar verticalmente\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centrar horizontalmente\"\nBasic.MainMenu.Edit.Order=\"&Ordenar\"\nBasic.MainMenu.Edit.Order.MoveUp=\"S&ubir\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Baixar (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Mover para a cima (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Levar para a&baixo\"\nBasic.MainMenu.Edit.AdvAudio=\"Propiedades &avanzadas de son\"\nBasic.MainMenu.View=\"&Ver\"\nBasic.MainMenu.View.Toolbars=\"Barras de ferramentas (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"Barra de ferramentas do panel\"\nBasic.MainMenu.View.ContextBar=\"Barra de ferramentas da fonte\"\nBasic.MainMenu.View.SourceIcons=\"&Iconas de fontes\"\nBasic.MainMenu.View.StatusBar=\"Barra de e&stado\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Interface de pantalla completa\"\nBasic.MainMenu.View.ResetUI=\"&Restablecer a interface de usuario\"\nBasic.MainMenu.View.AlwaysOnTop=\"Sempre en primeiro pl&ano\"\nBasic.MainMenu.View.SceneListMode=\"Modo lista de escenas\"\nBasic.MainMenu.Docks=\"&Paneis\"\nBasic.MainMenu.Docks.ResetDocks=\"&Restablecer paneis\"\nBasic.MainMenu.Docks.LockDocks=\"&Bloquear paneis\"\nBasic.MainMenu.Docks.SideDocks=\"&Paneis a altura completa\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Paneis do navegador personalizados... (&C)\"\nBasic.MainMenu.Import=\"Importar...\"\nBasic.MainMenu.Export=\"Exportar...\"\nBasic.MainMenu.SceneCollection=\"Colección de e&scenas\"\nBasic.MainMenu.Profile=\"&Perfil\"\nBasic.MainMenu.Profile.Import=\"Importar perfil\"\nBasic.MainMenu.Profile.Export=\"Exportar perfil\"\nBasic.MainMenu.SceneCollection.Import=\"Importar colección de escenas\"\nBasic.MainMenu.SceneCollection.Export=\"Exportar colección de escenas\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Restablecer a resolución base\"\nBasic.MainMenu.SceneCollection.Migrate=\"Restablecer a resolución base\"\nBasic.MainMenu.Profile.Exists=\"O perfil xa existe\"\nBasic.MainMenu.Tools=\"Ferramen&tas\"\nBasic.MainMenu.Help=\"Axuda (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal de axuda\"\nBasic.MainMenu.Help.Website=\"Visitar sitio &web\"\nBasic.MainMenu.Help.Discord=\"Unirse ao servidor do &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Que hai novo\"\nBasic.MainMenu.Help.Logs=\"Ficheiros de rexistro (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Amo&sar os ficheiros de rexistro\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Enviar o ficheiro de rexistro a&ctual\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Subir o arquivo de rexistro anterior (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Ver o rexistro actual\"\nBasic.MainMenu.Help.ReleaseNotes=\"Notas do lanzamento\"\nBasic.MainMenu.Help.CheckForUpdates=\"Comprobar se hai actualizacións\"\nBasic.MainMenu.Help.Repair=\"Comprobar a integridade dos ficheiros\"\nBasic.MainMenu.Help.RestartSafeMode=\"Reiniciar en modo seguro\"\nBasic.MainMenu.Help.RestartNormal=\"Reiniciar en modo normal\"\nBasic.MainMenu.Help.CrashLogs=\"Info&rmes de fallos\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Amo&sar os informes de fallos\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Subir o informe de fallo anterior (&P)\"\nBasic.MainMenu.Help.About=\"Sobre (&A)\"\nBasic.Settings.ProgramRestart=\"Debe reiniciarse o programa para que se aplique a nova configuración.\"\nBasic.Settings.ConfirmTitle=\"Confirmar os cambios\"\nBasic.Settings.Confirm=\"Hai cambios sen gardar. Quere gardalos?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 está a controlar algúns dos seus axustes de transmisión\"\nBasic.Settings.General=\"Xeral\"\nBasic.Settings.General.Language=\"Idioma\"\nBasic.Settings.General.Updater=\"Actualizacións\"\nBasic.Settings.General.UpdateChannel=\"Canle de actualización\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Desactivado)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Predeterminado)\"\nBasic.Settings.General.EnableAutoUpdates=\"Comprobar automaticamente no inicio se hai actualizacións\"\nBasic.Settings.General.OpenStatsOnStartup=\"Abrir o diálogo de estatísticas ao iniciar\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Ocultar as xanelas de OBS da captura de pantalla\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Esta configuración ocultará todas as xanelas de OBS Studio que non son de proxector para que non se capturen por OBS e afectará a\\noutras aplicacións, como videoconferencias, compartir pantalla, soporte remoto, capturas de pantalla e outros programas de captura.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Activar esta opción oculta todas as ventás de OBS Studio que non son de proxector para que non se capturen por OBS e afecta a outras aplicacións, como videoconferencias, compartir pantalla, soporte remoto, capturas de pantalla e outros programas de captura.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Amosar o diálogo de confirmación ao iniciar as emisións\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Amosar o diálogo de confirmación ao deter as emisións\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Amosar o diálogo de confirmación ao deter a gravación\"\nBasic.Settings.General.Projectors=\"Proxectores\"\nBasic.Settings.General.HideProjectorCursor=\"Agochar o cursor nos proxectores\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Pór os proxectores sempre na parte superior\"\nBasic.Settings.General.Snapping=\"Axuste do aliñamento da fonte\"\nBasic.Settings.General.ScreenSnapping=\"Axustar as fontes ao bordo da pantalla\"\nBasic.Settings.General.CenterSnapping=\"Axustar as fontes ao centro horizontal e vertical\"\nBasic.Settings.General.SourceSnapping=\"Axustar as fontes a outras fontes\"\nBasic.Settings.General.SnapDistance=\"Axustar a sensibilidade\"\nBasic.Settings.General.SpacingHelpers=\"Amosar guías de alineación de píxeles\"\nBasic.Settings.General.RecordWhenStreaming=\"Gravar automáticamente cando se está a emitir\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Seguir gravando cando se detén a emisión\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Iniciar automáticamente a memoria temporal de reprodución cando se está a emitir\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Manter a memoria temporal de reprodución activa cando se detén a emisión\"\nBasic.Settings.General.SysTray=\"Área de notificación\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimizar á área de notificación cando se inicia\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Minimizar sempre na área de notificación no canto de na barra de tarefas\"\nBasic.Settings.General.SaveProjectors=\"Gardar os proxectores ao saír\"\nBasic.Settings.General.CloseExistingProjectors=\"Limitar a un proxector en pantalla completa por pantalla\"\nBasic.Settings.General.Preview=\"Vista previa\"\nBasic.Settings.General.OverflowHidden=\"Agochar desbordamento\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Desbordamento sempre visíbel\"\nBasic.Settings.General.OverflowSelectionHidden=\"Amosar o desbordamento incluso cando a fonte sexa invisíbel\"\nBasic.Settings.General.Importers=\"Importadores\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Buscar localizacións coñecidas para coleccións de escenas ao importar\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Transición á escena cando faga dobre clic\"\nBasic.Settings.General.StudioPortraitLayout=\"Activar a disposición de retrato/vertical\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Amosar as etiquetas de vista previa/programa\"\nBasic.Settings.General.Multiview=\"Vista múltiple\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Prema para cambiar entre escenas\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Amosar o nome das esceas\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Debuxar áreas seguras (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Disposición de vista múltiple\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontal, arriba (8 escenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontal, abaixo (8 escenas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertical, esquerda (8 escenas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertical, dereita (8 escenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontal, arriba (18 escenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontal, arriba (24 escenas)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Só escenas (4 escenas)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Só escenas (9 escenas)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Só escenas (16 escenas)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Só escenas (25 escenas)\"\nBasic.Settings.General.ChannelName.stable=\"Estable\"\nBasic.Settings.General.ChannelDescription.stable=\"Última versión estable\"\nBasic.Settings.General.ChannelName.beta=\"Betas / Candidatos á versión\"\nBasic.Settings.General.ChannelDescription.beta=\"Versións previas potencialmente inestables\"\nBasic.Settings.Appearance=\"Aparencia\"\nBasic.Settings.Appearance.General=\"Xeral\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Estilo\"\nBasic.Settings.Appearance.General.NoVariant=\"Sen estilos dispoñibles\"\nBasic.Settings.Appearance.FontScale=\"Tamaño da fonte\"\nBasic.Settings.Appearance.Density=\"Densidade\"\nBasic.Settings.Appearance.Classic=\"Clásico\"\nBasic.Settings.Appearance.Compact=\"Compacto\"\nBasic.Settings.Appearance.Comfortable=\"Cómodo\"\nBasic.Settings.Appearance.OptionsWarning=\"Algunhas opcións de aparencia non están dispoñibles para este estilo.\"\nBasic.Settings.Stream=\"Emisión\"\nBasic.Settings.Stream.Destination=\"Destino\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Usar a autenticación\"\nBasic.Settings.Stream.Custom.Username=\"Nome de usuario\"\nBasic.Settings.Stream.Custom.Password=\"Contrasinal\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: introduce o srp_username.\\nRTMP: introduce o nome de usuario.\\nSRT: non usado.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: introduce o srp_password.\\nRTMP: introduce o contrasinal.\\nSRT: introduce a frase de acceso cifrada.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Activar o modo de proba de largo de banda\"\nBasic.Settings.Stream.TTVAddon=\"Engadidos de conversas do Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Ningún\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV e FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Configuración da emisión incompleta\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Abrir os axustes\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Non se atopa a URL e a chave da emisión.\\n\\nAbre os axustes para inserir a URL e a chave da emisión na lapela de \\\"Emisión\\\".\"\nBasic.Settings.Stream.MissingUrl=\"Non se atopa o URL.\\n\\nAbra os axustes para introducir o URL na lapela «Emisión».\"\nBasic.Settings.Stream.MissingStreamKey=\"Non se atopa a chave da emisión.\\n\\nAbre os axustes para inserir a chave da emisión na lapela de \\\"Emisión\\\".\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorar as recomendacións de axuste do servizo de transmisión\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Anular os axustes recomendados\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Advertencia: ignorar as limitacións do servizo pode provocar unha degradación da calidade do fluxo ou impedir a transmisión.\\n\\nQuere continuar?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Taxa máxima de bits de vídeo: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Taxa máxima de bits de son: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Resolución máxima: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Taxa máxima de cadros por segundo: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Especificar servidor personalizado...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Servidor personalizado\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Activar %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Ancho de banda máximo para transmisión\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automático\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Número máximo de pistas de vídeo\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automático\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Activar volcado de transmisión a FLV (usa axustes sinxelos de gravación de ficheiros)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Anulación de configuración (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Activar anulación de configuración\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Vídeo multipista\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Lenzo adicional\"\nBasic.Settings.Stream.AdvancedOptions=\"Opcións avanzadas\"\nBasic.Settings.Output=\"Saída\"\nBasic.Settings.Output.Format=\"Formato de gravación\"\nBasic.Settings.Output.Format.hMP4=\"Híbrido MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Híbrido MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 fragmentado (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV fragmentado (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"MOV fragmentado escribe a gravación en partes e non require a mesma finalización que os ficheiros MOV tradicionais.\\nIsto garante que o ficheiro siga sendo reproducible mesmo se a escrita no disco se interrompe, por exemplo, como resultado dun pantallazo azul ou perda de enerxía.\\n\\nIsto pode non ser compatible con todos os reprodutores e editores. Use Ficheiro → Remux gravacións para converter o ficheiro a un formato máis compatible se é necesario.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"O MP4 fragmentado escribe a gravación en partes e non require a mesma finalización que os ficheiros MP4 tradicionais.\\nIsto garante que o ficheiro siga sendo reproducible mesmo se a escrita no disco se interrompe, por exemplo, como resultado dun pantallazo azul ou perda de enerxía.\\n\\nIsto pode non ser compatible con todos os reprodutores e editores. Usa Ficheiro → Remux gravacións para converter o ficheiro a un formato máis compatible se é necesario.\"\nBasic.Settings.Output.Encoder.Video=\"Codificador de vídeo\"\nBasic.Settings.Output.Encoder.Audio=\"Codificador de son\"\nBasic.Settings.Output.SelectDirectory=\"Seleccionar o directorio de gravación\"\nBasic.Settings.Output.DynamicBitrate=\"Mudar de xeito dinámico a taxa de bits para xestionar a conxestión\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Mudar dinamicamente a taxa de bits para xestionar a conxestión (beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"No canto de deixar perder cadros para reducir a conxestión, cambia dinamicamente a taxa de bits.\\n\\nTeña en conta que isto pode aumentar o atraso para os espectadores se hai unha conxestión súbita importante.\\n\\n Cando a taxa de bits cae, pode tardar uns minutos en restaurarse.\\n\\nActualmente só é compatible con RTMP.\"\nBasic.Settings.Output.Mode=\"Modo de saída\"\nBasic.Settings.Output.Mode.Simple=\"Sinxelo\"\nBasic.Settings.Output.Mode.Adv=\"Avanzado\"\nBasic.Settings.Output.Mode.FFmpeg=\"Saída de FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Activar a reprodución da memoria temporal\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Tempo máximo de reprodución\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Memoria máxima\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Uso estimado da memoria: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Advertencia: O uso estimado de memoria de %1 MiB é maior que o máximo recomendado de %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Non é posíbel estimar o uso de memoria. Estabeleza o límite máximo de memoria.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefixo do nome de ficheiro de reprodución da memoria temporal\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufixo\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"O buffer de repetición non pode usarse cando o tipo de gravación está configurado en Saída personalizada (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Ruta da gravación\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Calidade da gravación\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"A mesma da emisión\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Alta calidade, tamaño medio do ficheiro\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Calidade indistinguíbel, tamaño de ficheiro grande\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Calidade sen perda, tamaño de ficheiro enorme\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Advertencia: a taxa de bits de vídeo en transmisión estabelecerase en %1, que é o límite superior para o servizo de transmisión actual.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Advertencia: a taxa de bits de son en transmisión estabelecerase en %1, que é o límite superior para o servizo de transmisión actual.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Aviso: Non é posíbel deter as gravacións se a calidade da gravación está estabelecida como «A mesma da emisión».\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Advertencia: O formato de gravación seleccionado actualmente é incompatible co(s) codificador(es) de fluxo seleccionado(s).\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Aviso: a gravación cun codificador de software cunha calidade diferente á da emisión requirirá un uso de CPU adicional se se emite e grava ao mesmo tempo.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Advertencia: A calidade sen perdas xera ficheiros de tamaño moi grande! A calidade sen perdas pode usar máis de 7 xigabytes de espazo en disco por minuto en altas resolucións e taxas de fotogramas. Non se recomenda o uso de calidade sen perdas para gravacións longas a non ser que teñas unha gran cantidade de espazo en disco dispoñible. O modo de almacenamento de repetición non está dispoñible cando se usa calidade sen perdas.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Confirma que quere empregar unha calidade sen perda?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Aviso de calidade sen perda!\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Hardware (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264 preaxustado o uso de CPU baixo, aumenta o tamaño do ficheiro)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Predeterminado)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Pista VOD de Twitch (utiliza a pista 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Pista de son\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Resolución ou taxa de cadros por segundo incompatíbel\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Este servizo de transmisión non admite a súa resolución de saída e/ou taxa de cadros por segundo actual. Cambiaranse aos valores compatíbeis máis próximos\\n\\n%1\\n\\nQuere continuar?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolución: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Cadros por segundo: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Codificador incompatible\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"O servizo de streaming \\\"%1\\\" non soporta o codificador \\\"%2\\\". O codificador cambiarase a \\\"%3\\\".\\n\\nQueres continuar?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"O servizo de streaming \\\"%1\\\" non soporta os codificadores \\\"%2\\\" e \\\"%3\\\". Estes codificadores cambiaranse a \\\"%4\\\" e \\\"%5\\\".\\n\\nQueres continuar?\"\nBasic.Settings.Output.VideoBitrate=\"Taxa de bits de vídeo\"\nBasic.Settings.Output.AudioBitrate=\"Taxa de bits de son\"\nBasic.Settings.Output.Reconnect=\"Volver conectar automaticamente\"\nBasic.Settings.Output.RetryDelay=\"Atraso dos novos intentos\"\nBasic.Settings.Output.MaxRetries=\"Número máximo de tentativas\"\nBasic.Settings.Output.Advanced=\"Activar configuracións personalizadas do codificador (Avanzado)\"\nBasic.Settings.Output.EncoderPreset=\"Preaxuste do codificador\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (baixo uso da CPU, menor calidade)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (por defecto) (uso medio da CPU, calidade estándar)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (alto uso da CPU, alta calidade)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Axustes personalizados do codificador\"\nBasic.Settings.Output.CustomMuxerSettings=\"Axustes personalizados do Muxer\"\nBasic.Settings.Output.NoSpaceFileName=\"Xerar o nome do ficheiro sen espazos\"\nBasic.Settings.Output.Adv.Rescale=\"Cambiar a escala da saída\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Desactivado\"\nBasic.Settings.Output.Adv.AudioTrack=\"Pista de son\"\nBasic.Settings.Output.Adv.Streaming=\"Emisión\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Configuración de transmisión\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Pista 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Pista 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Pista 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Pista 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Pista 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Pista 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Pista VOD do Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"Configuración do codificador\"\nBasic.Settings.Output.Adv.Recording=\"Gravando\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Configuración de gravación\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tipo de gravación\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tipo\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Estándar\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Saída personalizada (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Empregar o codificador da emisión)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Formato do nome de ficheiro\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Sobrescribir se existe o ficheiro\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"A saída personalizada (FFmpeg) non ten salvagardas.\\nConsidera usar o tipo de gravación \\\"Estándar\\\"\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Tipo de saída FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Saída a URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Saída a ficheiro\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Formatos comúns de gravación\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Todos os ficheiros\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Ruta ou URL do ficheiro\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Formato do contedor\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Son\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Vídeo\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Formato predeterminado\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Descrición do formato do contedor\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Códec de son/vídeo suposto a partires da ruta do ficheiro ou do URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Codificador predeterminado\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Desactivar o codificador\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Codificador de vídeo\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Axustes do codificador de vídeo (se existe)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Codificador de son\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Axustes do codificador de son (se existe)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Axustes do Muxer (se existe)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Intervalo de fotogramas clave (en fotogramas)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Amosar todos os códecs (aínda que poidan ser incompatíbeis)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Configuración de FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"División automática de ficheiro\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Dividir por Tempo\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Dividir por Tamaño\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Dividir só manualmente\"\nBasic.Settings.Output.SplitFile.Time=\"Tempo da división\"\nBasic.Settings.Output.SplitFile.Size=\"Tamaño da división\"\nScreenshot=\"Saída de captura de pantalla\"\nScreenshot.SourceHotkey=\"Captura de pantalla da fonte seleccionada\"\nScreenshot.StudioProgram=\"Captura de pantalla (programa)\"\nScreenshot.Preview=\"Captura de pantalla (vista previa)\"\nScreenshot.Scene=\"Captura de pantalla (escena)\"\nScreenshot.Source=\"Captura de pantalla (fonte)\"\nFilenameFormatting.TT.CCYY=\"Ano, catro díxitos\"\nFilenameFormatting.TT.YY=\"Ano, dous últimos díxitos (00-99)\"\nFilenameFormatting.TT.MM=\"Mes como número decimal (01-12)\"\nFilenameFormatting.TT.DD=\"Día do mes, con zero ao comezo (01-31)\"\nFilenameFormatting.TT.hh=\"Hora en formato 24h (00-23)\"\nFilenameFormatting.TT.mm=\"Minuto (00-59)\"\nFilenameFormatting.TT.ss=\"Segundo (00-59)\"\nFilenameFormatting.TT.Percent=\"Un signo de %\"\nFilenameFormatting.TT.a=\"Nome abreviado do día da semana\"\nFilenameFormatting.TT.A=\"Nome completo do día da semana\"\nFilenameFormatting.TT.b=\"Nome abreviado do mes\"\nFilenameFormatting.TT.B=\"Nome do mes completo\"\nFilenameFormatting.TT.d=\"Día do mes, con cero ao comezo (01-31)\"\nFilenameFormatting.TT.H=\"Hora en formato 24h (00-23)\"\nFilenameFormatting.TT.I=\"Hora en formato 12h (01-12)\"\nFilenameFormatting.TT.m=\"Mes como número decimal (01-12)\"\nFilenameFormatting.TT.M=\"Minuto (00-59)\"\nFilenameFormatting.TT.p=\"Designación AM ou PM\"\nFilenameFormatting.TT.s=\"Tempo en segundos desde a época UNIX\"\nFilenameFormatting.TT.S=\"Segundo (00-59)\"\nFilenameFormatting.TT.y=\"Ano, dous últimos díxitos (00-99)\"\nFilenameFormatting.TT.Y=\"Ano\"\nFilenameFormatting.TT.z=\"Desfase ISO 8601 con respecto ao UTC na zona horaria\"\nFilenameFormatting.TT.Z=\"Nome ou abreviatura da zona horaria\"\nFilenameFormatting.TT.FPS=\"Fotogramas por segundo\"\nFilenameFormatting.TT.CRES=\"Resolución base (lenzo)\"\nFilenameFormatting.TT.ORES=\"Resolución de saída (escalada)\"\nFilenameFormatting.TT.VF=\"Formato de vídeo\"\nBasic.Settings.Video=\"Vídeo\"\nBasic.Settings.Video.Adapter=\"Adaptador de vídeo\"\nBasic.Settings.Video.BaseResolution=\"Resolución da base (lenzo)\"\nBasic.Settings.Video.ScaledResolution=\"Resolución de saída (escalada)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtro de redución\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[As resolucións coinciden, non se require redimensionamento a menor escala]\"\nBasic.Settings.Video.FPSCommon=\"Valores comúns de FPS\"\nBasic.Settings.Video.FPSInteger=\"Valor enteiro de FPS\"\nBasic.Settings.Video.FPSFraction=\"Valor fraccionario de FPS\"\nBasic.Settings.Video.Numerator=\"Numerador\"\nBasic.Settings.Video.Denominator=\"Denominador\"\nBasic.Settings.Video.Renderer=\"Xerador\"\nBasic.Settings.Video.InvalidResolution=\"Valor da resolución non válido. Debe ser [width] x [height] (p. ex.: 1920 x 1080)\"\nBasic.Settings.Video.CurrentlyActive=\"A saída de vídeo está activa neste momento. Apaga calquera saída para cambiar a configuración de vídeo.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineal (máis rápido, mais borroso ao se escalar a imaxe)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicúbico (escalado fino, 16 mostras)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (escalado fino, 36 mostras)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Superficie (suma ponderada, 4/6/9 mostras)\"\nBasic.Settings.Audio=\"Son\"\nBasic.Settings.Audio.SampleRate=\"Frecuencia da mostraxe\"\nBasic.Settings.Audio.Channels=\"Canles\"\nBasic.Settings.Audio.Meters=\"Medidores\"\nBasic.Settings.Audio.MeterDecayRate=\"Taxa de decaemento\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rápido\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Medio (tipo I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lento (tipo II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Tipo de medidor de pico\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Pico de mostra\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Pico verdadeiro (maior uso de CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"AVISO: O son envolvente está activado.\"\nBasic.Settings.Audio.MultichannelWarning=\"Se estás transmitindo en directo, verifica se o teu servizo de transmisión admite tanto a recepción como a reprodución de son envolvente. Por exemplo, Facebook 360 Live soporta completamente o son envolvente; YouTube Live admite a recepción de audio 5.1 (e reprodución en televisores).\\n\\nOs filtros de audio de OBS son compatibles co son envolvente, aínda co soporte para complementos VST non está garantido.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Activar o son envolvente?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Confirma que quere activar o son envolvente?\"\nBasic.Settings.Audio.Devices=\"Dispositivos de son globais\"\nBasic.Settings.Audio.DesktopDevice=\"Son do escritorio\"\nBasic.Settings.Audio.DesktopDevice2=\"Son do escritorio 2\"\nBasic.Settings.Audio.AuxDevice=\"Son do micro/auxiliar\"\nBasic.Settings.Audio.AuxDevice2=\"Son micro/auxiliar 2\"\nBasic.Settings.Audio.AuxDevice3=\"Son micro/auxiliar 3\"\nBasic.Settings.Audio.AuxDevice4=\"Son micro/auxiliar 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Activar Premer para silenciar\"\nBasic.Settings.Audio.PushToMuteDelay=\"Atraso de Premer para falar\"\nBasic.Settings.Audio.EnablePushToTalk=\"Activar Premer para falar\"\nBasic.Settings.Audio.PushToTalkDelay=\"Atraso de Premer para falar\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Dispositivo sen conectar ou non dispoñíbel]\"\nBasic.Settings.Audio.Disabled=\"Desactivado\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Modo de almacenamento de audio con baixa latencia (para saídas Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ADVERTENCIA: O modo de almacenamento de audio con baixa latencia está activado.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"O modo de almacenamento de audio con baixa latencia pode causar que o audio teña fallos ou deixe de reproducirse nalgunhas fontes.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Activar o modo de almacenamento de audio con baixa latencia?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Estás seguro de que desexas activar o modo de almacenamento de audio con baixa latencia?\"\nBasic.Settings.Accessibility=\"Accesibilidade\"\nBasic.Settings.Accessibility.ColorOverrides=\"Usar cores diferentes\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Bordo da fonte (selección)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Bordo da fonte (Recortar)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Bordo da fonte (Ao Pasar o rato)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Rango de volume do mesturador (-60 a -20 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Rango de volume do mesturador (-20 to -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Rango de volume do mesturador (-9 to 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Rango de volume do mesturador (-60 to -20dB) (activo)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Rango de volume do mesturador (-20 to -9dB) (activo)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Rango de volume do mesturador (-9 a 0 dB) (activo)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Predefinición de cor\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Predeterminado\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Personalizado\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternativa para daltonismo\"\nBasic.Settings.Advanced=\"Avanzado\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Amosar advertencia de saídas activas ao saír\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prioridade do proceso\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Alta\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Por riba do normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Por baixo do normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inactivo\"\nBasic.Settings.Advanced.FormatWarning=\"Advertencia: Os formatos de cor diferentes de NV12/P010 están destinados principalmente para gravación e non se recomendan para transmisión. A transmisión pode provocar un aumento no uso da CPU debido á conversión de formatos de cor.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Advertencia: Os formatos de alta precisión utilízanse máis comunmente con espazos de cor HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Advertencia: Rec. 2100 debería usar un formato con máis precisión.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Formato da cor\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 bits, 4:2:0, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Espazo de cor\"\nBasic.Settings.Advanced.Video.ColorRange=\"Gama de cores\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limitado\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Total\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Nivel de branco SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nivel de pico nominal HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Dispositivo de monitorización\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Predeterminado\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Desactivar a atenuación de son do Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Atraso da emisión\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Duración\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Preservar o punto de corte (aumento do atraso) ao volver conectar\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Uso estimado da memoria: %1 MB\"\nBasic.Settings.Advanced.Network=\"Rede\"\nBasic.Settings.Advanced.Network.Disabled=\"O protocolo de transmisión seleccionado actualmente non admite cambiar os axustes de rede.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Ligar co IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Familia IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Activar optimizacións de rede\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Activar o ritmo controlado TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Intentase facer que a saída RTMP sexa máis amigábel a outras aplicacións sensíbeis á latencia na rede mediante a regulación da velocidade de transmisión.\\nPode aumentar o risco de perder cadros nas conexións inestábeis.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Comportamento das teclas rápidas en foco\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Non desactivar nunca as teclas rápidas\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Desactivar as teclas rápidas cando a xanela principal estea en foco\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Desactivar as teclas rápidas cando a xanela principal non estea en foco\"\nBasic.Settings.Advanced.AutoRemux=\"Converter automaticamente a %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(gravar como mkv)\"\nBasic.AdvAudio=\"Propiedades de son avanzadas\"\nBasic.AdvAudio.ActiveOnly=\"Só fontes activas\"\nBasic.AdvAudio.Name=\"Nome\"\nBasic.AdvAudio.VolumeSource=\"Volume para «%1»\"\nBasic.AdvAudio.MonoSource=\"Mestura monoaural para «%1»\"\nBasic.AdvAudio.BalanceSource=\"Balance para «%1»\"\nBasic.AdvAudio.SyncOffset=\"Desprazamento da sincronización\"\nBasic.AdvAudio.SyncOffsetSource=\"Desprazamento do sincronismo para «%1»\"\nBasic.AdvAudio.Monitoring=\"Monitorización do son\"\nBasic.AdvAudio.Monitoring.None=\"Monitor apagado\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Só o monitor (silenciar a saída)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitor e saída\"\nBasic.AdvAudio.MonitoringSource=\"Monitorización de son para «%1»\"\nBasic.AdvAudio.AudioTracks=\"Pistas\"\nBasic.Settings.Hotkeys=\"Teclas rápidas\"\nBasic.Settings.Hotkeys.Pair=\"As combinacións de teclas compartidas con «%1» actúan como conmutadores\"\nBasic.Settings.Hotkeys.Filter=\"Filtro\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrar por atallo\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Este atallo está compartido por unha ou máis accións, fai clic para mostrar conflitos\"\nBasic.Settings.Hotkeys.PleaseWait=\"Loading hotkeys, please wait…\"\nBasic.Hotkeys.SelectScene=\"Cambiar á escena\"\nBasic.SystemTray.Show=\"Amosar\"\nBasic.SystemTray.Hide=\"Agochar\"\nBasic.SystemTray.Message.Reconnecting=\"Sen conexión, reconectando...\"\nHotkeys.Insert=\"Inserir\"\nHotkeys.Delete=\"Eliminar\"\nHotkeys.Home=\"Inicio\"\nHotkeys.End=\"Fin\"\nHotkeys.PageUp=\"Re Páx\"\nHotkeys.PageDown=\"Av Páx\"\nHotkeys.NumLock=\"Block Num\"\nHotkeys.ScrollLock=\"Bloq Despr\"\nHotkeys.CapsLock=\"Bloq Maiús\"\nHotkeys.Backspace=\"Retroceso\"\nHotkeys.Print=\"Imprimir\"\nHotkeys.Pause=\"Pausa\"\nHotkeys.Left=\"Esquerda\"\nHotkeys.Right=\"Dereita\"\nHotkeys.Up=\"Arriba\"\nHotkeys.Down=\"Abaixo\"\nHotkeys.Super=\"Súper\"\nHotkeys.Menu=\"Menú\"\nHotkeys.Space=\"Espazo\"\nHotkeys.NumpadNum=\"Teclado numérico %1\"\nHotkeys.NumpadMultiply=\"Multiplicación do teclado númerico\"\nHotkeys.NumpadDivide=\"División do teclado numérico\"\nHotkeys.NumpadAdd=\"Suma do teclado numérico\"\nHotkeys.NumpadSubtract=\"Resta do teclado numérico\"\nHotkeys.NumpadDecimal=\"Decimal do teclado numérico\"\nHotkeys.AppleKeypadNum=\"%1 (teclado numérico)\"\nHotkeys.AppleKeypadMultiply=\"* (teclado numérico)\"\nHotkeys.AppleKeypadDivide=\"/ (teclado numérico)\"\nHotkeys.AppleKeypadAdd=\"+ (teclado numérico)\"\nHotkeys.AppleKeypadSubtract=\"- (teclado numérico)\"\nHotkeys.AppleKeypadDecimal=\". (teclado numérico)\"\nHotkeys.AppleKeypadEqual=\"= (teclado numérico)\"\nHotkeys.MouseButton=\"Rato %1\"\nMute=\"Silenciar\"\nUnmute=\"Activar son\"\nPush-to-mute=\"Premer para silenciar\"\nPush-to-talk=\"Premer para falar\"\nSceneItemShow=\"Amosar «%1»\"\nSceneItemHide=\"Agochar «%1»\"\nOutputWarnings.NoTracksSelected=\"Debe seleccionar, cando menos, unha pista\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Erro nas configuracións de saída\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Todas as saídas deben ter seleccionada polo menos unha pista de son.\"\nOutputWarnings.MP4Recording=\"Advertencia: as gravacións gardadas en MP4/MOV non serán recuperábeis se non se pode rematar o ficheiro (por exemplo, como resultado de BSOD, perdas de enerxía, etc.). Se quere gravar varias pistas de son, considere empregar MKV e converter a gravación a MP4/MOV unha vez rematada (Ficheiro → Gravacións de conversións)\"\nOutputWarnings.CannotPause=\"Aviso: Non é posíbel deter as gravacións se está estabelecido o codificador da gravación «(Utilizar o codificador da emisión)»\"\nOutputWarnings.CodecIncompatible=\"A selección do codificador de son ou vídeo foi restablecida debido a unha incompatibilidade. Por favor, selecciona un codificador compatible da lista.\"\nCodecCompat.Incompatible=\"(Incompatible con %1)\"\nCodecCompat.CodecPlaceholder=\"Select Encoder…\"\nCodecCompat.ContainerPlaceholder=\"Select Format…\"\nCodecCompat.CodecMissingOnExit.Title=\"Sen codificador seleccionado\"\nCodecCompat.CodecMissingOnExit.Text=\"Polo menos un codificador de vídeo ou audio non está configurado. Asegúrate de seleccionar codificadores tanto para a gravación como para a transmisión.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Non se seleccionou ningún formato\"\nCodecCompat.ContainerMissingOnExit.Text=\"Non se seleccionou ningún formato de gravación. Por favor, selecciona un formato de gravación compatible co codificador de transmisión seleccionado.\"\nFinalScene.Title=\"Eliminar escena\"\nFinalScene.Text=\"Debe haber polo menos unha escena.\"\nNoSources.Title=\"Non hai fontes\"\nNoSources.Text=\"Parece que aínda non engadiu ningunha fonte de vídeo, polo que a saída será unha pantalla en branco. É iso o que quere facer?\"\nNoSources.Text.AddSource=\"Pode engadir fontes premendo na icona + baixo a caixa Fontes na xanela principal en calquera momento.\"\nNoSources.Label=\"Non tes ningunha fonte.\\nPreme no botón + de embaixo,\\nou preme co botón dereito do rato para engadir unha.\"\nChangeBG=\"Establecer a cor\"\nCustomColor=\"Cor personalizada\"\nBrowserSource.EnableHardwareAcceleration=\"Activar a aceleración do hardware de fontes no navegador\"\nAbout=\"Sobre\"\nAbout.Info=\"O OBS Studio é un software libre para gravación e emisión ao vivo.\"\nAbout.Donate=\"Fai a túa achega\"\nAbout.GetInvolved=\"Colabora\"\nAbout.Authors=\"Autoría\"\nAbout.License=\"Licenza\"\nAbout.Error=\"Erro! Non se puido ler o ficheiro.\\n\\nVai a: %1\"\nAbout.Contribute=\"Apoia o Proxecto OBS\"\nAddUrl.Title=\"Engadir fonte mediante URL\"\nAddUrl.Text=\"Arrastrou un URL ao OBS. Isto engadirá automaticamente a ligazón como fonte. Continúar?\"\nResizeOutputSizeOfSource=\"Redimensionar saída (tamaño da fonte)\"\nResizeOutputSizeOfSource.Text=\"As resolucións base e de saída redimensionaranse ao tamaño da fonte actual.\"\nResizeOutputSizeOfSource.Continue=\"Quere continuar?\"\nPreviewTransition=\"Vista previa da transición\"\nImporter=\"Importador de colección de escenas\"\nImporter.SelectCollection=\"Seleccionar unha colección de escenas\"\nImporter.Collection=\"Colección de escenas\"\nImporter.HelpText=\"Engadir ficheiros a esta xanela para importar coleccións de OBS ou outros programas compatíbeis.\"\nImporter.Path=\"Ruta da colección\"\nImporter.Program=\"Aplicación detectada\"\nImporter.AutomaticCollectionPrompt=\"Buscar automaticamente coleccións de escenas\"\nImporter.AutomaticCollectionText=\"OBS pode atopar automaticamente coleccións de escenas importábeis de programas compatíbeis de terceiros. Quere que OBS atope automaticamente coleccións de escenas?\\n\\nPode cambiar isto máis adiante en Axustes > Xeral > Importadores.\"\nImporter.SelectFile=\"Navegar...\"\nRestart=\"Reiniciar\"\nNeedsRestart=\"É preciso reiniciar o OBS Studio. Quere reinicialo agora?\"\nLoadProfileNeedsRestart=\"O perfil contén configuracións que requiren reiniciar OBS: \\n%1\\n\\nQueres reiniciar OBS para que estas configuracións teñan efecto?\"\nContextBar.NoSelectedSource=\"Ningunha fonte seleccionada\"\nContextBar.MediaControls.PlayMedia=\"Reproducir o medio\"\nContextBar.MediaControls.PauseMedia=\"Pausar o medio\"\nContextBar.MediaControls.StopMedia=\"Deter o medio\"\nContextBar.MediaControls.RestartMedia=\"Reiniciar o medio\"\nContextBar.MediaControls.PlaylistNext=\"Seguinte na lista de reproducción\"\nContextBar.MediaControls.PlaylistPrevious=\"Anterior na lista de reproducción\"\nContextBar.MediaControls.BlindSeek=\"Trebello de busca de medios\"\nYouTube.Auth.Ok=\"A autorización completouse con éxito.\\nAgora podes pechar esta páxina.\"\nYouTube.Auth.NoCode=\"O proceso de autorización non se completou.\"\nYouTube.Auth.NoChannels=\"Non hai canles dispoñibles na conta seleccionada\"\nYouTube.Auth.WaitingAuth.Title=\"Autorización de usuario de YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Complete a autorización no seu navegador externo.<br>Se o navegador externo non se abre, siga esta ligazón e complete a autorización:<br> %1\"\nYouTube.AuthError.Text=\"Erro ao obter a información da canle: %1.\"\nYouTube.Actions.WindowTitle=\"Configuración de emisión da canle de YouTube: %1\"\nYouTube.Actions.CreateNewEvent=\"Facer unha nova emisión\"\nYouTube.Actions.ChooseEvent=\"Escoller unha emisión\"\nYouTube.Actions.Title=\"Titulo*\"\nYouTube.Actions.MyBroadcast=\"Miña emisión\"\nYouTube.Actions.Description=\"Descrición\"\nYouTube.Actions.Privacy=\"Privacidade\"\nYouTube.Actions.Privacy.Private=\"Privada\"\nYouTube.Actions.Privacy.Public=\"Público\"\nYouTube.Actions.Privacy.Unlisted=\"Non listado\"\nYouTube.Actions.Category=\"Categoría\"\nYouTube.Actions.Thumbnail=\"Miniatura\"\nYouTube.Actions.Thumbnail.SelectFile=\"Seleccionar ficheiro…\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Ficheiro non seleccionado\"\nYouTube.Actions.Thumbnail.ClearFile=\"Limpar\"\nYouTube.Actions.MadeForKids=\"Este vídeo está feito para nenos?*\"\nYouTube.Actions.MadeForKids.Yes=\"Si, está feito para nenos\"\nYouTube.Actions.MadeForKids.No=\"Non, non está feito para nenos\"\nYouTube.Actions.AdditionalSettings=\"Configuración adicional\"\nYouTube.Actions.Latency=\"Latencia\"\nYouTube.Actions.Latency.Low=\"Baixa\"\nYouTube.Actions.Latency.UltraLow=\"Moi baixo\"\nYouTube.Actions.EnableAutoStart=\"Activa o comezo automático\"\nYouTube.Actions.EnableAutoStop=\"Activa a parada automática\"\nYouTube.Actions.AutoStartStop.TT=\"Indica se esta emisión programada debe iniciarse automaticamente\"\nYouTube.Actions.EnableDVR=\"Activar DVR\"\nYouTube.Actions.ScheduleForLater=\"Axendar para máis tarde\"\nYouTube.Actions.RememberSettings=\"Lembra estas configuracións\"\nYouTube.Actions.Create_Ready=\"Crear emisión\"\nYouTube.Actions.Create_GoLive=\"Crea a emisión e comeza a transmitir\"\nYouTube.Actions.Choose_Ready=\"Escolla emisión\"\nYouTube.Actions.Choose_GoLive=\"Escolla emisión e comezar a transmitir\"\nYouTube.Actions.Create_Schedule=\"Programar a emisión\"\nYouTube.Actions.Create_Schedule_Ready=\"Programar e escoller emisión\"\nYouTube.Actions.Dashboard=\"Abrir YouTube Studio\"\nYouTube.Actions.Error.Title=\"Produciuse un erro na creación da emisión en vivo\"\nYouTube.Actions.Error.Text=\"Error de acceso a YouTube '%1'.<br/>Pódese atopar unha descrición detallada do erro en <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Erro de acceso a YouTube. Comproba a túa conexión de rede ou o acceso ao teu servidor de YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Erro na creación da emisión '%1'.<br/>Pódese atopar unha descrición detallada do erro en <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Non se creou ningunha emisión. Volve vincular a túa conta.\"\nYouTube.Actions.Error.YouTubeApi=\"Erro na API de YouTube. Consulte o ficheiro de rexistro para obter máis información.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Non se atopou a emisión escollida.\"\nYouTube.Actions.Error.FileMissing=\"O ficheiro seleccionado non existe\"\nYouTube.Actions.Error.FileOpeningFailed=\"Erro o abrir o ficheiro escollido.\"\nYouTube.Actions.Error.FileTooLarge=\"O ficheiro escollido e moi grande (Limite: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Produciuse un erro ao realizar a transición da emisión: %1 <br/><br/> Se este erro persiste <a href='https://studio.youtube.com/video/%2/livestreaming'>abre a emisión en YouTube Studio </a> e proba manualmente.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"A emisión está en transición á fase de proba, isto pode levar algún tempo. Téntao de novo en 10–30 segundos.\"\nYouTube.Actions.EventsLoading=\"Cargando lista de eventos…\"\nYouTube.Actions.EventCreated.Title=\"Evento creado\"\nYouTube.Actions.EventCreated.Text=\"Evento creado con éxito.\"\nYouTube.Actions.Stream=\"Fluxo\"\nYouTube.Actions.Stream.ScheduledFor=\"Programado para %1\"\nYouTube.Actions.Stream.Resume=\"Retomar o fluxo interrompido\"\nYouTube.Actions.Stream.YTStudio=\"Creado automaticamente por YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Facendo unha nova emisión en directo, agarda…\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Requírese inicio manual\"\nYouTube.Actions.AutoStartStreamingWarning=\"O inicio automático está desactivado para este evento, fai clic en «En directo» para iniciar a túa emisión.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Non poderás volver a conectarte.<br>A túa emisión deterase e xa non estarás en directo.\"\nYouTube.Errors.liveStreamingNotEnabled=\"A emisión en directo non está habilitada na canle de YouTube escollida. <br/><br/>Consulta <a href='https://www.youtube.com/features'>youtube.com/features</a> para obter máis información.\"\nYouTube.Errors.livePermissionBlocked=\"A transmisión en directo non está dispoñible no canal de YouTube seleccionado.<br/>Ten en conta que pode levar ata 24 horas para que a transmisión en directo estea dispoñible despois de habilitala na configuración do teu canal..<br/><br/>Consulta <a href='https://www.youtube.com/features'>youtube.com/features</a> para máis detalles.\"\nYouTube.Errors.errorExecutingTransition=\"A transición fallou debido a un erro de backend. Téntao de novo nuns segundos.\"\nYouTube.Errors.errorStreamInactive=\"YouTube non está a recibir datos da túa transmisión en directo. Por favor, verifica a túa configuración e téntao de novo.\"\nYouTube.Errors.invalidTransition=\"A transición tentada foi inválida. Isto pode ser debido a que o fluxo non rematou unha transición anterior. Por favor, agarda uns segundos e téntao de novo.\"\nYouTube.DocksRemoval.Title=\"Limpar os paneis do navegador de YouTube antigo\"\nYouTube.DocksRemoval.Text=\"Estes paneis de navegador serán eliminados por estar obsoletos:\\n\\n%1\\nUtiliza 'Paneis/Control de Directo de YouTube' en lugar diso.\"\nConfigDownload.WarningMessageTitle=\"Coidado\"\nFailedToStartStream.MissingConfigURL=\"Non hai URL de configuración dispoñible para o servizo actual\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"URL RTMP personalizada non especificada\"\nFailedToStartStream.InvalidCustomConfig=\"Configuración personalizada non válida\"\nFailedToStartStream.MissingCanvas=\"Falta un lenzo adicional configurado\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Fallou ao crear o servizo de vídeo multipista\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Fallou ao crear a saída de vídeo multipista RTMP\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC non está dispoñible.\\n\\nFallou ao atopar o tipo de codificador '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Fallou ao crear o codificador de vídeo '%1' (tipo: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Fallou ao obter a información de vídeo de OBS ao crear o codificador '%1' (tipo: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Fallou ao crear o codificador de audio\"\nFailedToStartStream.NoRTMPURLInConfig=\"A configuración non contén a URL de destino RTMP(S) da transsmisión\"\nFailedToStartStream.FallbackToDefault=\"A posta en marcha da transmisión utilizando %1 fallou; queres reintentar usando configuracións de codificación únicas?\"\nFailedToStartStream.ConfigRequestFailed=\"Non se puido obter a configuración desde %1<br><br>Erro HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Recibido valor de estado descoñecido '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nQueres continuar a transmisión sen %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nQueres continuar coa transmisión?\"\nFailedToStartStream.MissingEncoderConfigs=\"A configuración de ir en directo non incluíu as configuracións do codificador\"\nFailedToStartStream.InvalidEncoderConfig=\"A configuración de publicación incluía unha configuración de codificador non válida\"\nFailedToStartStream.StatusMissingHTML=\"A solicitude para ir en directo devolveu un erro non especificado\"\nFailedToStartStream.NoConfigSupplied=\"Configuración ausente\"\nMultitrackVideo.Info=\"%1 optimiza automaticamente os teus axustes para codificar e enviar múltiples calidades de vídeo. Seleccionar esta opción enviará a %2 información sobre o teu computador e configuración do software.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Axustes Incompatibles\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 non é actualmente compatible con:\\n\\n%2\\nPara continuar transmitindo con %1, desactiva os axustes incompatibles:\\n\\n%3\\ne comeza a transmisión de novo.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Desactivar para esta transmisión e Comezar a transmisión\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Actualizar configuracións e Comezar a transmisión\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 non é actualmente compatible con [Son → Xeral → Canles] configurado en '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Son → Xeral → Canles] ten que estar configurado en '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 require diferentes configuracións para [Son → Xeral → Canles]\"\nBasic.OpenPluginManager=\"Xestor de complementos\"\nPluginManager=\"Xestor de complementos\"\nPluginManager.Restart=\"É necesario reiniciar OBS Studio para aplicar os trocos no cmplemento\"\nPluginManager.MissingPlugin=\"[COMPLEMENTO NON ATOPADO]\"\nPluginManager.Section.Discover=\"Navegar\"\nPluginManager.Section.Manage=\"Instalado\"\nPluginManager.Section.Updates=\"Actualizacións\"\nPluginManager.Section.Manage.Title=\"Xestionar complementos Activados\"\n"
  },
  {
    "path": "frontend/data/locale/he-IL.ini",
    "content": "Language=\"עברית\"\nOK=\"אישור\"\nApply=\"החל\"\nCancel=\"ביטול\"\nClose=\"סגירה\"\nSave=\"שמירה\"\nDiscard=\"השלכה\"\nDisable=\"השבתה\"\nYes=\"כן\"\nNo=\"לא\"\nAdd=\"הוספת\"\nRemove=\"הסרה\"\nRename=\"שינוי שם\"\nInteract=\"אינטראקציה\"\nFilters=\"מסננים\"\nProperties=\"מאפיינים\"\nMoveUp=\"העלאה\"\nMoveDown=\"הורדה\"\nSettings=\"הגדרות\"\nDisplay=\"תצוגה\"\nName=\"שם\"\nExit=\"יציאה\"\nMixer=\"מערבל שמע\"\nBrowse=\"עיון\"\nMono=\"מונו\"\nStereo=\"סטריאו\"\nDroppedFrames=\"%1 תמוניות הושמטו (%2%)\"\nProjector.Open.Program=\"פתיחת מקרן תוכנית\"\nProjector.Open.Preview=\"פתיחת מקרן תצוגה מקדימה\"\nProjector.Open.Scene=\"פתיחת מקרן סצנות\"\nProjector.Open.Source=\"פתיחת מקרן מקור\"\nProjector.Open.Multiview=\"פתיחת ריבוי תצוגות\"\nProjector.Display=\"צג: %1\"\nProjector.Window=\"חלון חדש\"\nProjector.Title=\"מקרן\"\nProjector.Title.Scene=\"סצנה: %1\"\nProjector.Title.Source=\"מקור: %1\"\nProjector.Title.Multiview=\"ריבוי תצוגות\"\nProjector.ResizeWindowToContent=\"התאמת חלון לתוכן\"\nClear=\"ניקוי\"\nRevert=\"איפוס\"\nShow=\"הצגה\"\nHide=\"הסתרה\"\nUnhideAll=\"לא להציג הכול\"\nUntitled=\"ללא שם\"\nNew=\"חדש\"\nDuplicate=\"שכפול\"\nEnable=\"הפעלה\"\nDisableOSXVSync=\"השבתת סנכרון אנכי של macOS\"\nResetOSXVSyncOnExit=\"איפוס סנכרון אנכי של macOS ביציאה\"\nHighResourceUsage=\"קידוד עמוס מדי! נא לשקול להנמיך את הגדרות הווידאו או להשתמש בקידוד מהיר יותר.\"\nTransition=\"מעבר\"\nQuickTransitions=\"מעברים מהירים\"\nFadeToBlack=\"עמעום לצבע שחור\"\nLeft=\"שמאל\"\nRight=\"ימין\"\nTop=\"עליון\"\nBottom=\"תחתון\"\nReset=\"איפוס\"\nHours=\"שעות\"\nMinutes=\"דקות\"\nSeconds=\"שניות\"\nDeprecated=\"לא בפיתוח\"\nReplayBuffer=\"מטמון השמעה חוזרת\"\nImport=\"ייבוא\"\nExport=\"ייצוא\"\nCopy=\"העתקה\"\nPaste=\"הדבקה\"\nPasteReference=\"הדבקה (הפניה)\"\nPasteDuplicate=\"הדבקה (שכפול)\"\nRemuxRecordings=\"המרת הקלטות\"\nNext=\"הבא\"\nBack=\"אחורה\"\nDefaults=\"ברירת מחדל\"\nRestoreDefaults=\"ברירות מחדל\"\nHideMixer=\"הסתרה במערבל\"\nTransitionOverride=\"מעבר דרוס\"\nShowTransition=\"להציג מעברים\"\nHideTransition=\"הסתרת מעברים\"\nNone=\"ללא\"\nStudioMode.Preview=\"תצוגה מקדימה\"\nStudioMode.Program=\"תוכנית\"\nStudioMode.PreviewSceneName=\"תצוגה מקדימה: %1\"\nStudioMode.ProgramSceneName=\"תוכנית: %1\"\nShowInMultiview=\"להציג בריבוי תצוגות\"\nVerticalLayout=\"פריסה אנכית\"\nGroup=\"קבוצה\"\nDoNotShowAgain=\"לא להציג שוב\"\nDefault=\"(ברירת מחדל)\"\nCalculating=\"מתבצע חישוב...\"\nFullscreen=\"מסך מלא\"\nWindowed=\"חלון\"\nRefreshBrowser=\"רענון\"\nAspectRatio=\"יחס גובה־רוחב <b>%1:%2</b>\"\nLockVolume=\"נעילת שמע\"\nLogViewer=\"מציג היומן\"\nShowOnStartup=\"הצגה בעת הפעלה\"\nOpenFile=\"פתיחת קובץ\"\nAddScene=\"הוספת סצנה\"\nAddSource=\"הוספת מקור\"\nRemoveScene=\"הסרת הסצנה הנבחרת\"\nRemoveSource=\"הסרת המקורות הנבחרים\"\nMoveSceneUp=\"העלאת הסצנה\"\nMoveSceneDown=\"הורדת הסצנה\"\nMoveSourceUp=\"העלאת מקורות\"\nMoveSourceDown=\"הורדת מקורות\"\nSourceProperties=\"פתיחת מאפייני מקור\"\nSourceFilters=\"פתיחת מסנני מקור\"\nMixerToolbarMenu=\"תפריט מערבל שמע\"\nSceneFilters=\"פתיחת מסנני סצנה\"\nList=\"רשימה\"\nGrid=\"טבלה\"\nAutomatic=\"אוטומטי\"\nComingSoon=\"בקרוב\"\nPluginsFailedToLoad.Title=\"שגיאה בטעינת תוסף\"\nPluginsFailedToLoad.Text=\"טעינת תוספי OBS הבאים נכשלה:\\n\\n%1\\nנא לעדכן או להסיר תוספים אלה.\"\nAlreadyRunning.Title=\"OBS פועל כבר\"\nAlreadyRunning.Text=\"‏OBS כבר פעיל! אלא אם התכוונת לעשות זאת, נא לכבות את כל התהליכים הקיימים של OBS לפני ניסיון הפעלת תהליך חדש. אם OBS מוזער למגש המערכת, נא לבדוק אם הוא עדיין פעיל שם.\"\nAlreadyRunning.LaunchAnyway=\"הפעלה בכל מקרה\"\nCrashHandling.Dialog.Title=\"זוהתה קריסה של OBS Studio\"\nCrashHandling.Labels.Text=\"OBS Studio לא התכבה כראוי. \\n\\nלהפעיל מחדש במצב בטוח (תוספי צד-שלישי, סקריפטינג ו-WebSockets מושבתים)?\"\nCrashHandling.Labels.PrivacyNotice=\"ניתן גם לבחור להעלות באופן אוטומטי את דוח הקריסה האחרון ל-OBSProject <br /><br />אנא קרא את <a href='https://obsproject.com/privacy-policy'>מדיניות הפרטיות</a> לפני העלאת קבצים ושים לב במיוחד לחלקים הנוגעים להעלאת קבצים.\"\nCrashHandling.Checkbox.SendReport=\"קראתי את מדיניות הפרטיות ואני מסכים/ה להעלאה.\"\nCrashHandling.Buttons.LaunchSafe=\"הפעלה במצב בטוח\"\nCrashHandling.Buttons.LaunchNormal=\"להפעיל מחדש כרגיל\"\nCrashHandling.Errors.UploadJSONError=\"אירעה שגיאה בעת ניסיון להעלות את יומן הקריסה האחרון. נסה שוב מאוחר יותר.\"\nCrashHandling.Errors.Title=\"שגיאת העלאת יומן קריסה\"\nSafeMode.Restart=\"להפעיל את OBS מחדש במצב בטוח (תוספי צד־שלישי, סקריפטים ו־WebSockets מושבתים)?\"\nSafeMode.RestartNormal=\"להפעיל את OBS מחדש כרגיל?\"\nChromeOS.Title=\"פלטפורמה לא נתמכת\"\nChromeOS.Text=\"נראה ש־OBS פועל בתוך קונטיינר של ChromeOS. פלטפורמה זו אינה נתמכת.\"\nWine.Title=\"זוהה Wine\"\nWine.Text=\"אין תמיכה בהפעלת OBS על גבי Wine ויכולות רבות כגון הקלטה או מקורות התקנים לא יעבדו או שהקיבולת שלהם תהיה מוגבלת.<br><br>מומלץ להפעיל את הגרסה המותאמת של OBS במקום, למשל <a href='https://flathub.org/apps/details/com.obsproject.Studio'>גרסת ה־Flatpak שלנו</a> או חבילות מערכת ההפעלה שלך.\"\nDockCloseWarning.Title=\"סגירת חלון נצמד\"\nDockCloseWarning.Text=\"עכשיו סגרת חלון נצמד. כדי להציג אותו שוב, יש להשתמש בתפריט החלונות הנצמדים שבשורת התפריטים.\"\nExtraBrowsers=\"חלונות דפדפן נצמדים בהתאמה אישית\"\nExtraBrowsers.Info=\"אפשר להוסיף חלונות נצמדים בעזרת הוספת שם וכתובת, לאחר מכן יש ללחוץ על החלה או סגירה כדי לפתוח את החלונות הנצמדים. ניתן להוסיף או להסיר חלונות נצמדים בכל עת.\"\nExtraBrowsers.DockName=\"שם החלון הנצמד\"\nAuth.Authing.Title=\"מתבצע אימות...\"\nAuth.Authing.Text=\"מתבצע אימות עם %1, נא להמתין...\"\nAuth.AuthFailure.Title=\"האימות נכשל\"\nAuth.AuthFailure.Text=\"האימות עם %1 נכשל:\\n\\n%2: ‏%3\"\nAuth.InvalidScope.Title=\"דרושה הזדהות\"\nAuth.InvalidScope.Text=\"דרישות האימות עבור %1 השתנו. חלק מהתכונות לא יהיו זמינות.\"\nAuth.LoadingChannel.Title=\"טוען מידע על הערוץ...\"\nAuth.LoadingChannel.Text=\"טוען מידע על הערוץ עבור %1, אנא המתן...\"\nAuth.LoadingChannel.Error=\"לא ניתן לקבל מידע על הערוץ.\"\nAuth.ChannelFailure.Title=\"טעינת הערוץ נכשלה\"\nAuth.ChannelFailure.Text=\"נכשל בטעינת המידע עבור %1\\n\\n%2: %3\"\nAuth.Chat=\"צ׳אט\"\nAuth.StreamInfo=\"מידע על השידור החי\"\nTwitchAuth.Stats=\"סטטיסטיקה של Twitch\"\nTwitchAuth.Feed=\"ערוץ עדכוני Switch\"\nTwitchAuth.TwoFactorFail.Title=\"לא ניתן לשלוף מפתח שידורים חיים\"\nTwitchAuth.TwoFactorFail.Text=\"OBS לא הצליחה להתחבר לחשבון Twitch שלך. נא לוודא כי האימות הדו־שלבי מוגדר ב<a href='https://www.twitch.tv/settings/security'>הגדרות האבטחה שלך ב־Twitch</a> מכיוון וזוהי דרישה לשידור.\"\nRestreamAuth.Channels=\"ערוצי Restream\"\nCopy.Filters=\"העתקת מסננים\"\nPaste.Filters=\"הדבקת מסננים\"\nBrowserPanelInit.Title=\"הדפדפן מופעל...\"\nBrowserPanelInit.Text=\"הדפדפן מופעל, נא להמתין...\"\nBandwidthTest.Region=\"אזור\"\nBandwidthTest.Region.US=\"ארצות הברית\"\nBandwidthTest.Region.EU=\"אירופה\"\nBandwidthTest.Region.Asia=\"אסיה\"\nBandwidthTest.Region.Other=\"אחר\"\nBasic.AutoConfig=\"אשף תצורה אוטומטית\"\nBasic.AutoConfig.ApplySettings=\"החלת ההגדרות\"\nBasic.AutoConfig.StartPage=\"פרטי שימוש\"\nBasic.AutoConfig.StartPage.SubTitle=\"נא לציין איך ברצונך להשתמש בתוכנית\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"מיטוב לשידורים חיים, ההקלטה משנית\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"מיטוב להקלטה בלבד, לא יהיו שידורים חיים\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"אשתמש במצלמה הווירטואלית בלבד\"\nBasic.AutoConfig.VideoPage=\"הגדרות וידאו\"\nBasic.AutoConfig.VideoPage.SubTitle=\"נא לציין את הגדרות הווידאו בהן ברצונך להשתמש\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"שימוש בנוכחית (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"תצוגה %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"שימוש בנוכחי (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 או 30, אך 60 עדיפים כאשר זה אפשרי\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 או 30, אך רזולוציה גבוהה עדיפה\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"הערה: רזולוציית משטח העבודה (הבסיס) אינה זהה בהכרח לרזולוציית השידורים החיים או ההקלטה. רזולוציית השידורים החיים או ההקלטה בפועל עשויה להיות מופחתת במטרה להפחית את השימוש במשאבים או בתעבורת הרשת.\"\nBasic.AutoConfig.StreamPage=\"פרטי השידור החי\"\nBasic.AutoConfig.StreamPage.SubTitle=\"נא למלא את פרטי השידור החי שלך\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"התחברות לחשבון (מומלץ)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"ניתוק החשבון\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"לנתק את החשבון?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"שינוי זה יחול באופן מיידי. לנתק את חשבונך?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"קבלת מפתח שידורים חיים\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"מידע נוסף\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"שימוש במפתח שידורים חיים\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"שימוש במפתח שידורים חיים (מתקדם)\"\nBasic.AutoConfig.StreamPage.Service=\"שירות\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"להציג הכול...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"התאמה אישית...\"\nBasic.AutoConfig.StreamPage.Server=\"שרת\"\nBasic.AutoConfig.StreamPage.StreamKey=\"מפתח שידורים חיים\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: נא למלא את מילת צופן ההצפנה.\\nRTMP: נא למלא את המפתח שסופק על ידי השירות.\\nSRT: נא למלא את מזהה התזרים (streamid) אם השירות משתמש באחד.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"מפתח מקודד\"\nBasic.AutoConfig.StreamPage.BearerToken=\"אסימון נושא\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"חשבון מחובר\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"הערכת קצב נתונים עם בדיקת רוחב פס (עשוי להימשך דקות אחדות)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"העדף קידוד בחומרה\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"קידוד בחומרה מבטלת רוב השימוש במעבד, אבל עשוי לדרוש קצב נתונים גבוה יותר כדי להשיג את אותה רמת האיכות.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"אזהרה בנוגע לשידור החי\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"בדיקת רוחב הפס עומדת להתחיל שידור חי אקראי ללא שמע מהערוץ שלך. אם באפשרותך לעשות כן, מומלץ לכבות באופן זמני את שמירת הסרטונים של השידורים החיים ולהגדיר את השידורים החיים כפרטיים עד לתום הבדיקה. להמשיך?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"בדיקת %1\"\nBasic.AutoConfig.TestPage=\"תוצאות סופיות\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"התוכנית מריצה כעת מערך של בדיקות להערכת ההגדרות הטובות ביותר\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"הבדיקה הסתיימה\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"מתבצעת בדיקת רוחב פס, פעולה זו עשויה להימשך מספר דקות...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"לא נמצא פלט לפרוטוקול השירות הזה\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"מתבצעת התחברות אל: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"נכשלה ההתחברות לכל שרת שהוא, נא לבדוק את החיבור שלך לאינטרנט ולנסות שוב.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"בדיקות רוחב פס עבור: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"המקודד לשידורים החיים נבדק, זה עשוי לקחת כמה רגעים...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"בדיקות מקודד הקלטה, זה עלול לקחת דקה...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"נכשלה הפעלת המקודד\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"בדיקת %1x%2 %3 מסגרות לדקה...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"מקודד לשידורים חיים\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"מקודד הקלטה\"\nBasic.AutoConfig.TestPage.Result.Header=\"התוכנית קבעה כי ההגדרות האלה הן הטובות ביותר בשבילך:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"כדי להשתמש בהגדרות אלה, לחץ על החל הגדרות. כדי להגדיר מחדש את האשף ולנסות שוב, לחץ על אחורה. להגדיר ידנית את הגדרות, לחץ על ביטול ולאחר מכן פתח את הגדרות.\"\nBasic.AutoConfig.Info=\"אשף ההגדרה האוטומטית ימצא את ההגדרות הטובות ביותר בהתבסס על מפרט המחשב ומהירות האינטרנט שלך.\"\nBasic.AutoConfig.RunAnytime=\"אפשר להריץ אותו בכל זמן באמצעות תפריט הכלים.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"רזולוציית הזרמה (בגודל מתואם)\"\nBasic.Stats=\"סטטיסטיקה\"\nBasic.Stats.CPUUsage=\"שימוש במעבד\"\nBasic.Stats.HDDSpaceAvailable=\"שטח דיסק פנוי\"\nBasic.Stats.MemoryUsage=\"שימוש בזיכרון\"\nBasic.Stats.AverageTimeToRender=\"הזמן הממוצע לעיבוד מסגרת\"\nBasic.Stats.SkippedFrames=\"המערכת דילגה על מסגרות עקב השהית קידוד\"\nBasic.Stats.MissedFrames=\"מסגרות פוספסו עקב השהית קידוד\"\nBasic.Stats.Output.Stream=\"שידור חי\"\nBasic.Stats.Output.Recording=\"הקלטה\"\nBasic.Stats.Status=\"מצב\"\nBasic.Stats.Status.Recording=\"בהקלטה\"\nBasic.Stats.Status.Live=\"בשידור חי\"\nBasic.Stats.Status.Reconnecting=\"התחברות מחדש\"\nBasic.Stats.Status.Inactive=\"לא פעיל\"\nBasic.Stats.Status.Active=\"פעיל\"\nBasic.Stats.DroppedFrames=\"השמטת מסגרות (רשת)\"\nBasic.Stats.MegabytesSent=\"פלט נתונים כולל\"\nBasic.Stats.Bitrate=\"קצב נתונים\"\nBasic.Stats.DiskFullIn=\"דיסק מלא בעוד (לערך)\"\nBasic.Stats.DiskFullIn.Text=\"%1 שעות, %2 דקות\"\nBasic.Stats.ResetStats=\"לאפס סטטיסטיקה\"\nResetUIWarning.Title=\"לאפס את ממשק המשתמש?\"\nResetUIWarning.Text=\"איפוס ממשק המשתמש יסתיר חלונות נצמדים נוספים. יהיה אפשר לחשוף את החלונות הנצמדים מתפריט החלונות הנצמדים אם ברצונך להציגם.\\n\\nלאפס את ממשק המשתמש?\"\nUpdater.Title=\"עדכון חדש זמין\"\nUpdater.Text=\"יש עדכון חדש זמין:\"\nUpdater.UpdateNow=\"עדכון כעת\"\nUpdater.RemindMeLater=\"דחייה למועד מאוחר יותר\"\nUpdater.Skip=\"לא לעדכן\"\nUpdater.NoUpdatesAvailable.Title=\"אין עדכונים זמינים\"\nUpdater.NoUpdatesAvailable.Text=\"אין עדכונים זמינים כעת\"\nUpdater.BranchNotFound.Title=\"ערוץ העדכונים הוסר\"\nUpdater.BranchNotFound.Text=\"ערוץ העדכונים שבחרת אינו זמין עוד, OBS אופס לברירת המחדל.\"\nUpdater.RepairButUpdatesAvailable.Title=\"בדיקת תקינות לא זמינה\"\nUpdater.RepairButUpdatesAvailable.Text=\"בדיקת תקינות הקבצים זמינה רק עבור הגרסה העדכנית ביותר. יש להשתמש ב-עזרה ← בדיקת עדכונים כדי לוודא ולעדכן את התקנת OBS שלך.\"\nUpdater.RepairConfirm.Title=\"ווידוא בדיקת תקינות\"\nUpdater.RepairConfirm.Text=\"התחלת בדיקת התקינות תסרוק שיבושים בהתקנת ה-OBS שלך ותבצע הורדה מחדש של קבצים משובשים/שעברו שינוי. הפעולה עשויה לקחת זמן מה.\\n\\nהאם ברצונך להמשיך?\"\nUpdater.FailedToLaunch=\"הפעלת אשף העדכון נכשלה\"\nQuickTransitions.SwapScenes=\"החלפת סצנות תצוגה מקדימה/סצנות תוכנית לאחר המעבר\"\nQuickTransitions.SwapScenesTT=\"החלפת הסצנות של התצוגה המקדימה ושל התוכנית לאחר המעבר (במידה שהסצינה המקורית של התוכנית עדיין קיימת).\\n פעולה זו לא תבטל כל שינוי שייתכן שבוצע לסצינה המקורית של התוכנית.\"\nQuickTransitions.DuplicateScene=\"שכפול הסצנה\"\nQuickTransitions.DuplicateSceneTT=\"בעת עריכת אותה הסצנה, מאפשר עריכת שינוי צורה/הנראות של המקורות מבלי לשנות את פלט התוכנית.\\nכדי לערוך מאפייני מקורות מבלי לשנות את פלט התוכנית, יש לאפשר את \\\"שכפול מקורות\\\".\\nשינוי ערך זה יאפס את תוכנית הסצנה הנוכחית (אם עדיין קיימת).\"\nQuickTransitions.EditProperties=\"שכפול המקורות\"\nQuickTransitions.EditPropertiesTT=\"בזמן עריכת אותה הסצנה, מאפשר עריכת מאפיינים של מקורות ללא שינוי פלט התוכנית.\\nפעולה זו אפשרית רק אם אפשרות „שכפול סצנה” מופעלת.\\nמקורות מסוימים (כגון הקלטה או מקורות מדיה) לא תומכים באפשרות זו ולא ניתן לערוך אותם בנפרד.\\nשינוי ערך זה יאפס את תוכנית הסצנה (אם עדיין קיימת).\\n\\nאזהרה: בגלל שמקורות ישוכפלו, ייתכן שיידרשו משאבי מערכת או וידאו נוספים.\"\nQuickTransitions.HotkeyName=\"מעבר מהיר: %1\"\nBasic.AddTransition=\"הוסף מעבר בר הגדרה\"\nBasic.RemoveTransition=\"הסר מעבר בר הגדרה\"\nBasic.TransitionProperties=\"מאפייני מעבר\"\nBasic.SceneTransitions=\"מעברי סצינות\"\nBasic.TransitionDuration=\"משך זמן\"\nBasic.TogglePreviewProgramMode=\"מצב סטודיו\"\nBasic.EnablePreviewProgramMode=\"הפעלת מצב סטודיו\"\nBasic.DisablePreviewProgramMode=\"השבתת מצב סטודיו\"\nUndo.Undo=\"בטל\"\nUndo.Redo=\"בצע מחדש\"\nUndo.Add=\"הוספת \\\"%1\\\"\"\nUndo.Delete=\"מחיקת \\\"%1\\\"\"\nUndo.Rename=\"שינוי השם של \\\"%1\\\"\"\nUndo.SceneCollection.Switch=\"מעבר אל \\\"%1\\\"\"\nUndo.Item.Undo=\"ביטול %1\"\nUndo.Item.Redo=\"ביצוע %1 מחדש\"\nUndo.Sources.Multi=\"מחיקת המקורות של %1\"\nUndo.Filters=\"שינויים בפילטר %1\"\nUndo.Filters.Paste.Single=\"הדבק פילטר %1 ל%2\"\nUndo.Filters.Paste.Multiple=\"העתק פילטרים מ%1 ל%2\"\nUndo.Transform=\"שנה מקור(ות) ב'%1'\"\nUndo.Transform.Paste=\"הדבק שינוי ב-'%1'\"\nUndo.Transform.Rotate=\"סיבוב פריט בתוך \\\"%1\\\"\"\nUndo.Transform.Reset=\"איפוס שינוי ב-'%1'\"\nUndo.Transform.HFlip=\"היפוך אופקי לפריט בתוך \\\"%1\\\"\"\nUndo.Transform.VFlip=\"היפוך אנכי לפריט בתוך \\\"%1\\\"\"\nUndo.Transform.FitToScreen=\"התאמת \\\"%1\\\" למסך\"\nUndo.Transform.StretchToScreen=\"מתח לגודל המסך ב-'%1'\"\nUndo.Transform.Center=\"הזזה למרכז ב־‚%1’\"\nUndo.Transform.VCenter=\"מרכז למסך אנכית ב-'%1'\"\nUndo.Transform.HCenter=\"מרכז למסך אופקית ב-'%1'\"\nUndo.Volume.Change=\"שינוי עוצמת השמע של \\\"%1\\\"\"\nUndo.Volume.Mute=\"השתקת \\\"%1\\\"\"\nUndo.Volume.Unmute=\"ביטול השתקת \\\"%1\\\"\"\nUndo.Balance.Change=\"שינוי איזון שמע ב-'%1'\"\nUndo.SyncOffset.Change=\"שינוי היסט סנכרון שמע ב-'%1'\"\nUndo.MonitoringType.Change=\"שינוי ניטור שמע ב-'%1'\"\nUndo.Mixers.Change=\"שינוי מערבלי שמע ב-'%1'\"\nUndo.ForceMono.On=\"הפעלת אילוץ מונו ב-'%1'\"\nUndo.ForceMono.Off=\"כיבוי אילוץ מונו ב-'%1'\"\nUndo.Properties=\"שינוי מאפיין של \\\"%1\\\"\"\nUndo.Scene.Duplicate=\"שכפול סצנה %1\"\nUndo.ShowTransition=\"הצגת המעבר של \\\"%1\\\"\"\nUndo.HideTransition=\"הסתרת המעבר של \\\"%1\\\"\"\nUndo.ShowSceneItem=\"הצגת \\\"%1\\\" בתוך \\\"%2\\\"\"\nUndo.HideSceneItem=\"הסתרת \\\"%1\\\" מתוך \\\"%2\\\"\"\nUndo.ReorderSources=\"סידור המקורות מחדש בתוך \\\"%1\\\"\"\nUndo.MoveUp=\"העלאת ‚%1’ מעלה ב־‚%2’\"\nUndo.MoveDown=\"הורדת ‚%1’ מטה ב־‚%2’\"\nUndo.MoveToTop=\"העלאת ‚%1’ לראש ב־‚%2’\"\nUndo.MoveToBottom=\"הורדת ‚%1’ לתחתית ב־‚%2’\"\nUndo.PasteSource=\"הדבקת מקור(ות) ב-'%1'\"\nUndo.PasteSourceRef=\"הדבקת הפניה(ות) מקור ב-'%1'\"\nUndo.GroupItems=\"קבץ פריטים לכדי '%1'\"\nTransitionNameDlg.Text=\"נא לתת שם למעבר\"\nTransitionNameDlg.Title=\"שם מעבר\"\nTitleBar.SafeMode=\"מצב בטוח\"\nTitleBar.PortableMode=\"מצב נייד\"\nTitleBar.Profile=\"פרופיל\"\nTitleBar.Scenes=\"סצנות\"\nNameExists.Title=\"השם כבר קיים\"\nNameExists.Text=\"שם זה נמצא כבר בשימוש.\"\nNoNameEntered.Title=\"נא לציין שם חוקי\"\nNoNameEntered.Text=\"חובה לתת שם.\"\nConfirmStart.Title=\"להתחיל שידור חי?\"\nConfirmStart.Text=\"האם להתחיל את השידור החי?\"\nConfirmStop.Title=\"לעצור את השידור החי?\"\nConfirmStop.Text=\"האם לעצור את השידור החי?\"\nConfirmStopRecord.Title=\"לעצור את ההקלטה?\"\nConfirmStopRecord.Text=\"האם לעצור את ההקלטה?\"\nConfirmBWTest.Title=\"להתחיל את בדיקת רוחב הפס?\"\nConfirmBWTest.Text=\"הגדרת את OBS למצב בדיקת רוחב פס. מצב זה מאפשר בדיקת רשת בלי שהערוץ שלך ישדר. כאשר הבדיקה תסתיים, יש לבטל מצב זה על מנת שהצופים שלך יוכלו לצפות בשידור החי.\\n\\nלהמשיך?\"\nConfirmExit.Title=\"ערוצי פלט פעילים\"\nConfirmExit.Text=\"OBS עדיין פעיל כרגע. כל ההזרמות/הקלטות ייכבו.\"\nConfirmRemove.Title=\"אישור הסרה\"\nConfirmRemove.Text=\"להסיר את ‚%1’?\"\nConfirmRemove.TextMultiple=\"להסיר %1 פריטים?\"\nConfirmReset.Title=\"איפוס מאפיינים\"\nConfirmReset.Text=\"לאפס את המאפיינים הנוכחיים לברירות המחדל שלהם?\"\nOutput.StartStreamFailed=\"התחלת השידור החי נכשלה\"\nOutput.StartRecordingFailed=\"התחלת ההקלטה נכשלה\"\nOutput.StartReplayFailed=\"נכשלה הפעלת מטמון השמעה החוזרת\"\nOutput.StartVirtualCamFailed=\"הפעלת המצלמה הווירטואלית נכשלה\"\nOutput.StartFailedGeneric=\"הפלט נכשל. לפרטים נוספים נא לבדוק את קובץ היומן.\\n\\nלתשומת ליבך: אם נעשה שימוש במקודדי NVENC או AMD, נא לוודא שמנהלי ההתקן של כרטיס המסך שלך מעודכנים.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"לא ניתן לשמור השמעות חוזרות בזמן השהיה\"\nOutput.ReplayBuffer.PauseWarning.Text=\"אזהרה: לא ניתן לשמור השמעות חוזרות לשמירה כאשר ההקלטה מושהית.\"\nOutput.ConnectFail.Title=\"ההתחברות נכשלה\"\nOutput.ConnectFail.BadPath=\"כתובת לא חוקית של נתיב או חיבור. נא לבדוק את ההגדרות שלך כדי לוודא את תקינותן.\"\nOutput.ConnectFail.ConnectFailed=\"ההתחברות לשרת נכשלה\"\nOutput.ConnectFail.InvalidStream=\"לא ניתן להתחבר לערוץ שצוין או למפתח השידורים החיים, נא לבדוק שנית את מפתח השידורים החיים. אם הוא נכון, ככל הנראה יש בעיה בהתחברות לשרת.\"\nOutput.ConnectFail.HdrDisabled=\"פלט ה־HDR מושבת כרגע לפלט הזה.\"\nOutput.ConnectFail.Error=\"אירעה שגיאה בלתי צפויה בעת ניסיון להתחבר לשרת. מידע נוסף בקובץ יומן הרישום.\"\nOutput.ConnectFail.Disconnected=\"התנתקת מהשרת.\"\nOutput.StreamEncodeError.Title=\"שגיאת קידוד\"\nOutput.StreamEncodeError.Msg=\"אירעה שגיאת מקודד במהלך השידור החי.\"\nOutput.StreamEncodeError.Msg.LastError=\"אירעה שגיאה במצפין במהלך ההזרמה:<br><br>%1\"\nOutput.RecordFail.Title=\"התחלת ההקלטה נכשלה\"\nOutput.RecordFail.Unsupported=\"תבנית הפלט לא נתמכת או לא תומכת ביותר מרצועת שמע אחת. נא בדוק את ההגדרות ונסה שוב.\"\nOutput.RecordNoSpace.Title=\"אין מספיק שטח פנוי בכונן\"\nOutput.RecordNoSpace.Msg=\"אין מספיק שטח פנוי בכונן להמשך ההקלטה.\"\nOutput.RecordError.Title=\"שגיאת הקלטה\"\nOutput.RecordError.Msg=\"אירעה שגיאה לא מוגדרת בזמן ההקלטה.\"\nOutput.RecordError.EncodeErrorMsg=\"התרחשה שגיאת מקודד בזמן הקלטה.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"אירעה שגיאה במצפין במהלך ההקלטה:<br><br>%1\"\nOutput.BadPath.Title=\"נתיב קובץ לא תקין\"\nOutput.BadPath.Text=\"לא ניתן לפתוח את נתיב ההקלטה המוגדר. נא לבדוק את נתיב ההקלטה שלך תחת הגדרות ← פלט ← הקלטה.\"\nOutput.NoBroadcast.Title=\"אין הגדרת שידור\"\nOutput.NoBroadcast.Text=\"עליך להגדיר שידור לפני שתוכל להתחיל להזרים.\"\nOutput.BroadcastStartFailed=\"התחלת השידור נכשלה\"\nOutput.BroadcastStopFailed=\"עצירת השידור נכשלה\"\nLogUploadDialog.Title=\"העלאת קובץ יומן של OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"אנא קרא את <a href='https://obsproject.com/privacy-policy'>מדיניות הפרטיות</a> ואת הסעיף בנוגע להעלאת קובץ לפני העלאת קבצים כלשהם.\"\nLogUploadDialog.Labels.Progress=\"העלאת יומן מתבצעת. אנא המתן...\"\nLogUploadDialog.Labels.Description.AppLog=\"קובץ היומן שלך הועלה. כעת תוכל לשתף את כתובת האתר למטרות ניפוי באגים או תמיכה.\"\nLogUploadDialog.Labels.Description.CrashLog=\"דוח הקריסה שלך הועלה. כעת תוכל לשתף את כתובת האתר למטרות ניפוי באגים או תמיכה.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"העלה\"\nLogUploadDialog.Buttons.CopyURL=\"העתק כתובת יומן\"\nLogUploadDialog.Buttons.AnalyzeURL=\"אבחון קובץ יומן\"\nLogUploadDialog.Buttons.RetryButton=\"נסה שוב\"\nLogUploadDialog.Errors.Template=\"אירעה שגיאה בעת ניסיון להעלות את הקובץ:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"לא נמצא קובץ להעלאה או שהקובץ היה ריק.\"\nRemux.SourceFile=\"הקלטה ב־OBS\"\nRemux.TargetFile=\"קובץ היעד\"\nRemux.Remux=\"המרה\"\nRemux.Stop=\"עצירת המרה\"\nRemux.ClearFinished=\"פינוי פריטים שהסתיימו\"\nRemux.ClearAll=\"ניקוי כל הפריטים\"\nRemux.OBSRecording=\"הקלטה ב־OBS\"\nRemux.FinishedTitle=\"המרה הסתיימה\"\nRemux.Finished=\"הקלטה הומרה\"\nRemux.FinishedError=\"הקלטה הומרה, אבל הקובץ עשוי להיות לא שלם\"\nRemux.SelectRecording=\"בחירת הקלטת OBS...\"\nRemux.SelectTarget=\"בחירת קובץ יעד...\"\nRemux.FileExistsTitle=\"קובצי היעד כבר קיימים\"\nRemux.FileExists=\"קובצי היעד הבאים כבר קיימים. להחליף אותם?\"\nRemux.ExitUnfinishedTitle=\"המרה בתהליך\"\nRemux.ExitUnfinished=\"ההמרה לא הסתיימה, עצירה עכשיו עלולה להפוך את קובץ היעד לא שמיש.\\nלעצור את ההמרה?\"\nRemux.HelpText=\"אפשר לגרור קבצים להמרה לחלון זה, או לבחור את תא \\\"הקלטת OBS\\\" ריק כדי לבחור קובץ ידנית.\"\nRemux.NoFilesAddedTitle=\"לא נוסף קובץ ריבוב\"\nRemux.NoFilesAdded=\"לא נוסף קובץ לריבוב. יש להשליך לכאן תיקייה שמכילה קובץ וידאו אחד או יותר.\"\nMissingFiles=\"קבצים חסרים\"\nMissingFiles.MissingFile=\"קובץ חסר\"\nMissingFiles.NewFile=\"קובץ חדש\"\nMissingFiles.HelpText=\"חלק מהקבצים חסרים מאז השימוש האחרון שלך ב־OBS\"\nMissingFiles.Clear=\"<נוקה>\"\nMissingFiles.NumFound=\"נמצאו %1 מתוך %2\"\nMissingFiles.Search=\"חיפוש בתיקייה...\"\nMissingFiles.SelectFile=\"בחירת קובץ...\"\nMissingFiles.SelectDir=\"נא לבחור תיקיה לחפש בה\"\nMissingFiles.State=\"מצב\"\nMissingFiles.Missing=\"חסר\"\nMissingFiles.Replaced=\"הוחלף\"\nMissingFiles.Cleared=\"נוקה\"\nMissingFiles.Found=\"נמצא\"\nMissingFiles.AutoSearch=\"נמצאו התאמות נוספות לקבצים\"\nMissingFiles.AutoSearchText=\"OBS מצא התאמות נוספות לקבצים חסרים בתיקייה זו. להוסיף אותם?\"\nMissingFiles.NoMissing.Title=\"בדיקת קבצים חסרים\"\nMissingFiles.NoMissing.Text=\"לא קיימים קבצים חסרים.\"\nMacPermissions.MenuAction=\"סקירת הרשאות היישום…\"\nMacPermissions.Title=\"סקירת הרשאות היישום\"\nMacPermissions.Description=\"OBS Studio דורשת הרשאה בכדי לספק תכונות מסוימות. מומלץ להפעיל הרשאות אלו, אך הן לא נדרשות בכדי להשתמש באפליקציה. ניתן תמיד להפעיל אותן מאוחר יותר.\"\nMacPermissions.Description.OpenDialog=\"ניתן לפתוח מחדש את תיבת דו-שיח זו דרך תפריט OBS Studio.\"\nMacPermissions.AccessGranted=\"גישה הוענקה\"\nMacPermissions.RequestAccess=\"בקשת גישה\"\nMacPermissions.OpenPreferences=\"פתיחת הגדרות %1\"\nMacPermissions.Item.ScreenRecording=\"הקלטת מסך\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS דורשת הרשאה זו בכדי שתוכל להקליט את המסך שלך.\"\nMacPermissions.Item.Camera=\"מצלמה\"\nMacPermissions.Item.Camera.Details=\"הרשאה זו נדרשת לצורך הקלטת תוכן ממצלמת רשת או כרטיס הקלטה.\"\nMacPermissions.Item.Microphone=\"מיקרופון\"\nMacPermissions.Item.Microphone.Details=\"OBS דורשת הרשאה זו כדי להקליט מהמיקרופון שלך או מהתקן שמע חיצוני.\"\nMacPermissions.Item.InputMonitoring=\"ניטור קלט\"\nMacPermissions.Item.InputMonitoring.Details=\"ההרשאה הזאת נחוצה כדי שמקשי הקיצור יעבדו בזמן ש־OBS פועל ברקע.\"\nMacPermissions.Item.Accessibility=\"נגישות\"\nMacPermissions.Item.Accessibility.Details=\"בהתקנות ישנות יותר, OBS עלול להופיע תחת „נגישות” במקום „ניטור קלט”.\"\nMacPermissions.Continue=\"הבא\"\nSourceLeak.Title=\"שגיאת פינוי מקור\"\nSourceLeak.Text=\"אירעה תקלה בשינוי אוספי הסצנות ולא ניתן לפרוק חלק מהמקורות. התקלה הזאת בדרך כלל נגרמת על ידי תוספים שאינם משחררים משאבים כראוי. נא לוודא שהתוספים שמשמשים אותך הם עדכניים.\\n\\nOBS Studio ייסגר כעת כדי למנוע אפשרות של השחתת נתונים.\"\nBasic.DesktopDevice1=\"שמע בשולחן העבודה\"\nBasic.DesktopDevice2=\"שמע בשולחן העבודה 2\"\nBasic.AuxDevice1=\"מיקרופון/Aux\"\nBasic.AuxDevice2=\"מיקרופון/Aux מס׳ 2\"\nBasic.AuxDevice3=\"מיקרופון/Aux מס׳ 3\"\nBasic.AuxDevice4=\"מיקרופון/Aux מס׳ 4\"\nBasic.Scene=\"סצנה\"\nBasic.DisplayCapture=\"הקלטת תצוגה\"\nBasic.Main.PreviewConextMenu.Enable=\"הפעלת תצוגה מקדימה\"\nBasic.Main.Preview.Disable=\"להשבית תצוגה מקדימה\"\nScaleFiltering=\"סינון קנה מידה\"\nScaleFiltering.Point=\"נקודה\"\nScaleFiltering.Bilinear=\"ביליניארי\"\nScaleFiltering.Bicubic=\"ביקיוביק\"\nScaleFiltering.Lanczos=\"לנזוס\"\nScaleFiltering.Area=\"אזור\"\nBlendingMethod=\"שיטת ערבול\"\nBlendingMethod.Default=\"ברירת־מחדל\"\nBlendingMethod.SrgbOff=\"SRGB כבוי\"\nBlendingMode=\"מצב ערבול\"\nBlendingMode.Normal=\"רגיל\"\nBlendingMode.Additive=\"הוספה\"\nBlendingMode.Subtract=\"חיסור\"\nBlendingMode.Screen=\"מסך\"\nBlendingMode.Multiply=\"הכפלה\"\nBlendingMode.Lighten=\"הבהרה\"\nBlendingMode.Darken=\"האפלה\"\nDeinterlacing=\"ביטול שזירה\"\nDeinterlacing.Discard=\"מחיקה\"\nDeinterlacing.TopFieldFirst=\"שדה עליון ראשון\"\nDeinterlacing.BottomFieldFirst=\"שדה תחתון ראשון\"\nVolControl.SliderUnmuted=\"כיוון עוצמה עבור '%1':\"\nVolControl.SliderMuted=\"כיוון עוצמה עבור '%1': (כרגע מושתק)\"\nVolControl.Mute=\"השתקת ‚%1’\"\nVolControl.Properties=\"מאפייני ‚%1’\"\nVolControl.UnassignedWarning.Title=\"מקור שמע לא מוקצה\"\nVolControl.UnassignedWarning.Text=\"„%1” אינו מוקצה לאף רצועת שמע ואי אפשר יהיה להשמיע אותו בשידורים או בהקלטות.\\n\\nכדי להקצות מקור שמעלקבוצה, יש לפתוח את מאפייני השמע המתקדמים דרך התפריט בלחיצה ימנית או כפתור גלגל השיניים בסרגל הכלים שבמעגן.\"\nBasic.Main.AddSceneDlg.Title=\"הוספת סצנה\"\nBasic.Main.AddSceneDlg.Text=\"נא לתת שם לסצנה\"\nBasic.Main.DefaultSceneName.Text=\"סצנה %1\"\nBasic.Main.AddSceneCollection.Title=\"הוספת אוסף סצנות\"\nBasic.Main.AddSceneCollection.Text=\"נא לתת שם לאוסף הסצנות\"\nBasic.Main.RenameSceneCollection.Title=\"שינוי שם של אוסף הסצנות\"\nBasic.Main.RemigrateSceneCollection.Title=\"עדכון רזולוציית אוסף הסצנות\"\nBasic.Main.RemigrateSceneCollection.Text=\"לעדכן את רזולוציית אוסף הסצנות של „%1” כך שתתאים לרזולוציית משטח העבודה %2×%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"לא ניתן לעדכן את רזולוציית אוסף הסצנות תוך כדי שאמצעי הפלט פעילים.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"עדכון רזולוציית אוסף הסצנות נכשל. הרזולוציה המקורית לא ידועה.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"אי אפשר לאפס: החלפת הרזולוציה של OBS נכשלה.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"אי אפשר לאפס: הרזולוציה הנוכחית היא כבר רזולוציית הבסיס לאוסף הסצנות.\"\nAddProfile.Title=\"הוספת פרופיל\"\nAddProfile.Text=\"נא לתת שם לפרופיל\"\nAddProfile.WizardCheckbox=\"הצגת אשף ההגדרה האוטומטית\"\nRenameProfile.Title=\"שנה שם פרופיל\"\nBasic.Main.MixerRename.Title=\"שינוי שם מקור שמע\"\nBasic.Main.MixerRename.Text=\"נא לתת שם למקור השמע\"\nBasic.Main.PreviewDisabled=\"תצוגה מקדימה אינה זמינה כעת\"\nBasic.SourceSelect=\"יצירת/בחירת מקור\"\nBasic.SourceSelect.CreateNew=\"יצירת\"\nBasic.SourceSelect.AddExisting=\"הוסף קיימים\"\nBasic.SourceSelect.AddVisible=\"הפוך מקור לגלוי\"\nBasic.Main.Sources.Visibility=\"נראות\"\nBasic.Main.Sources.VisibilityDescription=\"שולט בנראות של '%1' במשטח העבודה\"\nBasic.Main.Sources.Lock=\"נעילה\"\nBasic.Main.Sources.LockDescription=\"נועל את המיקום ואת הגודל של '%1' במשטח העבודה\"\nBasic.PropertiesWindow=\"מאפיינים עבור \\\"%1\\\"\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (בחירה אוטומטית: %2)\"\nBasic.PropertiesWindow.SelectColor=\"בחירת צבע\"\nBasic.PropertiesWindow.SelectFont=\"בחירת גופן\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"נא לבחור גופן\"\nBasic.PropertiesWindow.ConfirmTitle=\"הגדרות שונו\"\nBasic.PropertiesWindow.Confirm=\"יש שינויים שלא נשמרו. לשמור אותם?\"\nBasic.PropertiesWindow.NoProperties=\"אין מאפיינים זמינים\"\nBasic.PropertiesWindow.AddFiles=\"הוסף קבצים\"\nBasic.PropertiesWindow.AddDir=\"הוסף ספריה\"\nBasic.PropertiesWindow.AddURL=\"הוספת נתיב/כתובת\"\nBasic.PropertiesWindow.AddEditableListDir=\"הוספת התיקייה אל \\\"%1\\\"\"\nBasic.PropertiesWindow.AddEditableListFiles=\"הוספת הקבצים אל \\\"%1\\\"\"\nBasic.PropertiesWindow.AddEditableListEntry=\"הוספת הערך אל \\\"%1\\\"\"\nBasic.PropertiesWindow.EditEditableListEntry=\"ערוך ערך של '%1'\"\nBasic.PropertiesView.FPS.Simple=\"ערכים פשוטים של פריימים לשניה\"\nBasic.PropertiesView.FPS.Rational=\"ערכים הגיוניים של פריימים לשניה\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"טווחים תקינים של פריימים לשניה:\"\nBasic.PropertiesView.UrlButton.Text=\"לפתוח קישור זה בדפדפן ברירת המחדל שלך?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"כתובת: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"פתיחת הכתובת\"\nBasic.InteractionWindow=\"אינטראקציה עם '%1'\"\nBasic.StatusBar.Reconnecting=\"החיבור מנותק, התחברות מחדש ב- %2 שניות (ניסיון %1)\"\nBasic.StatusBar.AttemptingReconnect=\"מתבצע ניסיון חוזר להתחברות מחדש... (ניסיון %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"ההתחברות מחדש הצליחה\"\nBasic.StatusBar.Delay=\"השהיה (%1 שניות)\"\nBasic.StatusBar.DelayStartingIn=\"השהיה (מתחיל בעוד %1 שניות)\"\nBasic.StatusBar.DelayStoppingIn=\"השהיה (מסיים בעוד %1 שניות)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"השהיה (מסיים בעוד %1 שניות, מתחיל בעוד %2 שניות)\"\nBasic.StatusBar.RecordingSavedTo=\"ההקלטה נשמרה ב%1\"\nBasic.StatusBar.ReplayBufferSavedTo=\"מטמון השמעה חוזרת נשמר ב־‚%1’\"\nBasic.StatusBar.ScreenshotSavedTo=\"צילום מסך נשמר ב%1\"\nBasic.StatusBar.AutoRemuxedTo=\"הקלטה הומרה אוטומטית ל-'%1'\"\nBasic.Filters=\"מסננים\"\nBasic.Filters.AsyncFilters=\"מסנני שמע/וידאו\"\nBasic.Filters.AudioFilters=\"מסנני שמע\"\nBasic.Filters.EffectFilters=\"מסנני אפקטים\"\nBasic.Filters.Title=\"מסננים עבור '%1'\"\nBasic.Filters.AddFilter.Title=\"שם מסנן\"\nBasic.Filters.AddFilter.Text=\"נא לציין את שם המסנן\"\nBasic.TransformWindow=\"שינוי פריט בסצנה\"\nBasic.TransformWindow.Position=\"מיקום\"\nBasic.TransformWindow.PositionX=\"מיקום X\"\nBasic.TransformWindow.PositionY=\"מיקום Y\"\nBasic.TransformWindow.Rotation=\"סיבוב\"\nBasic.TransformWindow.Size=\"גודל\"\nBasic.TransformWindow.Width=\"רוחב\"\nBasic.TransformWindow.Height=\"גובה\"\nBasic.TransformWindow.Alignment=\"יישור לפי מיקום\"\nBasic.TransformWindow.BoundsType=\"סוג התיבה התוחמת\"\nBasic.TransformWindow.BoundsAlignment=\"יישור בתיבה תוחמת\"\nBasic.TransformWindow.Bounds=\"גודל התיבה התוחמת\"\nBasic.TransformWindow.BoundsWidth=\"רוחב התיבה התוחמת\"\nBasic.TransformWindow.BoundsHeight=\"גובה התיבה התוחמת\"\nBasic.TransformWindow.CropToBounds=\"חיתוך לתיבה התוחמת\"\nBasic.TransformWindow.Crop=\"חיתוך\"\nBasic.TransformWindow.CropLeft=\"חיתוך משמאל\"\nBasic.TransformWindow.CropRight=\"חיתוך מימין\"\nBasic.TransformWindow.CropTop=\"חיתוך מלמעלה\"\nBasic.TransformWindow.CropBottom=\"חיתוך מלמטה\"\nBasic.TransformWindow.Alignment.TopLeft=\"שמאלה למעלה\"\nBasic.TransformWindow.Alignment.TopCenter=\"למעלה במרכז\"\nBasic.TransformWindow.Alignment.TopRight=\"ימינה למעלה\"\nBasic.TransformWindow.Alignment.CenterLeft=\"מרכז שמאל\"\nBasic.TransformWindow.Alignment.Center=\"מרכז\"\nBasic.TransformWindow.Alignment.CenterRight=\"ימינה במרכז\"\nBasic.TransformWindow.Alignment.BottomLeft=\"שמאלה למטה\"\nBasic.TransformWindow.Alignment.BottomCenter=\"למטה במרכז\"\nBasic.TransformWindow.Alignment.BottomRight=\"ימינה למטה\"\nBasic.TransformWindow.BoundsType.None=\"ללא גבולות\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"הגודל המרבי בלבד\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"שנה גודל לגבולות הפנימיים\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"שנה גודל לגבולות החיצוניים\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"שנה גודל לרוחב של הגבולות\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"שנה גודל לגובה של הגבולות\"\nBasic.TransformWindow.BoundsType.Stretch=\"מתח לגבולות\"\nBasic.TransformWindow.Title=\"עריכת שינוי עבור '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"לא נבחר מקור\"\nBasic.Main.AddSourceHelp.Title=\"אין אפשרות להוסיף מקור\"\nBasic.Main.AddSourceHelp.Text=\"צריכה להיות לפחות סצנה אחת כדי להוסיף מקור.\"\nBasic.Main.Scenes=\"סצנות\"\nBasic.Main.Sources=\"מקורות\"\nBasic.Main.Source=\"מקור\"\nBasic.Main.Controls=\"בקרה\"\nBasic.Main.PreparingStream=\"בהכנות…\"\nBasic.Main.Connecting=\"מתבצעת התחברות…\"\nBasic.Main.StartRecording=\"התחלת הקלטה\"\nBasic.Main.StartReplayBuffer=\"הפעלת מטמון השמעה חוזרת\"\nBasic.Main.SaveReplay=\"שמירת הילוך חוזר\"\nBasic.Main.StartStreaming=\"התחלת שידור חי\"\nBasic.Main.StartBroadcast=\"התחל שידור חי\"\nBasic.Main.StartVirtualCam=\"הפעלת מצלמה וירטואלית\"\nBasic.Main.StopRecording=\"עצירת הקלטה\"\nBasic.Main.PauseRecording=\"השהיית הקלטה\"\nBasic.Main.UnpauseRecording=\"ביטול השהיית ההקלטה\"\nBasic.Main.SplitFile=\"פיצול קובץ הקלטה\"\nBasic.Main.AddChapterMarker=\"הוספת סמן פרק (MP4/MOV משולב בלבד)\"\nBasic.Main.StoppingRecording=\"ההקלטה נעצרת…\"\nBasic.Main.StopReplayBuffer=\"עצירת מטמון ההשמעה החוזרת\"\nBasic.Main.StoppingReplayBuffer=\"מטמון ההשמעה החוזרת נעצר...\"\nBasic.Main.SetupBroadcast=\"ניהול שידור\"\nBasic.Main.StopStreaming=\"עצירת השידור החי\"\nBasic.Main.StopBroadcast=\"סיום שידור\"\nBasic.Main.AutoStopEnabled=\"(עצירה אוטומטית)\"\nBasic.Main.StoppingStreaming=\"השידור החי נעצר…\"\nBasic.Main.ForceStopStreaming=\"עצירת השידור החי (השהיה בביטול)\"\nBasic.Main.ShowContextBar=\"הצגת סרגל הכלים של המקורות\"\nBasic.Main.HideContextBar=\"הסתרת סרגל הכלים של המקורות\"\nBasic.Main.StopVirtualCam=\"עצור מצלמה וירטואלית\"\nBasic.Main.Group=\"קיבוץ %1\"\nBasic.Main.GroupItems=\"קיבוץ פריטים נבחרים\"\nBasic.Main.Ungroup=\"פירוק קבוצה\"\nBasic.Main.GridMode=\"מצב רשת\"\nBasic.Main.ListMode=\"מצב רשימה\"\nBasic.Main.VirtualCamConfig=\"הגדרת מצלמה וירטואלית\"\nBasic.VCam.VirtualCamera=\"מצלמה וירטואלית\"\nBasic.VCam.OutputType=\"סוג פלט\"\nBasic.VCam.OutputSelection=\"בחירת פלט\"\nBasic.VCam.OutputType.Program=\"תוכנית (ברירת מחדל)\"\nBasic.VCam.OutputSelection.NoSelection=\"אין בחירה לסוג פלט זה\"\nBasic.VCam.RestartWarning=\"המצלמה הווירטואלית תופעל מחדש כדי להחיל את השינוי הזה\"\nBasic.MainMenu.File=\"&קובץ\"\nBasic.MainMenu.File.Export=\"יי&צוא\"\nBasic.MainMenu.File.Import=\"יי&בוא\"\nBasic.MainMenu.File.ShowRecordings=\"הצגת הה&קלטות\"\nBasic.MainMenu.File.Remux=\"ה&מרת הקלטות\"\nBasic.MainMenu.File.Settings=\"ה&גדרות\"\nBasic.MainMenu.File.ShowSettingsFolder=\"הצגת תיקיית ההגדרות\"\nBasic.MainMenu.File.ShowProfileFolder=\"הצגת תיקיית פרופילים\"\nBasic.MainMenu.File.ShowMissingFiles=\"איתור קבצים חסרים\"\nBasic.MainMenu.File.Exit=\"י&ציאה\"\nBasic.MainMenu.Edit=\"&עריכה\"\nBasic.MainMenu.Edit.Undo=\"&ביטול\"\nBasic.MainMenu.Edit.Redo=\"ביצוע &חוזר\"\nBasic.MainMenu.Edit.LockPreview=\"&נעילת תצוגה מקדימה\"\nBasic.MainMenu.Edit.Scale=\"&קנה המידה של התצוגה המקדימה\"\nBasic.MainMenu.Edit.Scale.Window=\"התאמת קנה מידה לחלון\"\nBasic.MainMenu.Edit.Scale.Canvas=\"משטח עבודה (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"פלט (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"תואם קנה מידה (%1×%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"התקרבות\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"התרחקות\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"איפוס תקריב\"\nBasic.MainMenu.Edit.Transform=\"&שינוי\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&עריכת השינוי...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"העתקת מעבר\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"הדבקת מעבר\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"אי&פוס השינוי\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"סיבוב ב־90 מעלות בכיוון השעון\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"סיבוב ב־90 מעלות בניגוד לכיוון השעון\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"סיבוב ב־180 מעלות\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"להפוך &אופקית\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"הפוך &אנכית\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"&התאם למסך\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&מתח לגודל המסך\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&מרכז למסך\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"מירכוז אנכי\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"למרכז אופקית\"\nBasic.MainMenu.Edit.Order=\"&סדר\"\nBasic.MainMenu.Edit.Order.MoveUp=\"הזז &למעלה\"\nBasic.MainMenu.Edit.Order.MoveDown=\"הזז &למטה\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"הזזה לקצה הע&ליון\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"הזזה לקצה הת&חתון\"\nBasic.MainMenu.Edit.AdvAudio=\"מאפייני שמע &מתקדמים\"\nBasic.MainMenu.View=\"&תצוגה\"\nBasic.MainMenu.View.Toolbars=\"&סרגלי כלים\"\nBasic.MainMenu.View.ListboxToolbars=\"עיגון סרגלי כלים\"\nBasic.MainMenu.View.ContextBar=\"סרגל הכלים של המקורות\"\nBasic.MainMenu.View.SourceIcons=\"&סמלי מקורות\"\nBasic.MainMenu.View.StatusBar=\"&שורת מצב\"\nBasic.MainMenu.View.Fullscreen.Interface=\"ממשק מסך מלא\"\nBasic.MainMenu.View.ResetUI=\"אי&פוס הממשק\"\nBasic.MainMenu.View.AlwaysOnTop=\"תמיד &עליון\"\nBasic.MainMenu.View.SceneListMode=\"מצב רשימת סצנות\"\nBasic.MainMenu.Docks=\"חלונות נ&צמדים\"\nBasic.MainMenu.Docks.ResetDocks=\"אי&פוס החלון הנצמד\"\nBasic.MainMenu.Docks.LockDocks=\"נ&עילת החלונות הנצמדים\"\nBasic.MainMenu.Docks.SideDocks=\"מעגנים ב&גובה מלא\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&חלונות דפדפן נצמדים בהתאמה אישית…\"\nBasic.MainMenu.Import=\"ייבוא…\"\nBasic.MainMenu.Export=\"ייצוא…\"\nBasic.MainMenu.SceneCollection=\"אוסף &סצנות\"\nBasic.MainMenu.Profile=\"&פרופיל\"\nBasic.MainMenu.Profile.Import=\"ייבוא פרופיל\"\nBasic.MainMenu.Profile.Export=\"ייצוא פרופיל\"\nBasic.MainMenu.SceneCollection.Import=\"ייבוא אוסף סצנות\"\nBasic.MainMenu.SceneCollection.Export=\"ייצוא אוסף סצנות\"\nBasic.MainMenu.SceneCollection.Remigrate=\"איפוס רזולוציית בסיס\"\nBasic.MainMenu.SceneCollection.Migrate=\"הגדרת רזולוציית בסיס\"\nBasic.MainMenu.Profile.Exists=\"הפרופיל כבר קיים\"\nBasic.MainMenu.Tools=\"&כלים\"\nBasic.MainMenu.Help=\"עז&רה\"\nBasic.MainMenu.Help.HelpPortal=\"&פורטל עזרה\"\nBasic.MainMenu.Help.Website=\"ביקור ב&אתר\"\nBasic.MainMenu.Help.Discord=\"הצטרפות ל&שרת Discord‏\"\nBasic.MainMenu.Help.WhatsNew=\"מה חדש\"\nBasic.MainMenu.Help.Logs=\"קובצי &יומן\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"ה&צגת קובצי היומן\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"העלאת קובץ יומן &נוכחי\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"העלאת קובץ יומן &קודם\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"הצגת היומן הנוכחי\"\nBasic.MainMenu.Help.ReleaseNotes=\"הערות למהדורה\"\nBasic.MainMenu.Help.CheckForUpdates=\"בדיקת עדכונים\"\nBasic.MainMenu.Help.Repair=\"בדיקת תקינות קבצים\"\nBasic.MainMenu.Help.RestartSafeMode=\"להפעיל מחדש במצב בטוח\"\nBasic.MainMenu.Help.RestartNormal=\"להפעיל מחדש כרגיל\"\nBasic.MainMenu.Help.CrashLogs=\"דוחות &קריסה\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"ה&צגת דוחות קריסה\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"העלאת יומן קריסה &קודם\"\nBasic.MainMenu.Help.About=\"על &אודות\"\nBasic.Settings.ProgramRestart=\"יש להפעיל את התוכנית מחדש כדי שהגדרות האלה ייכנסו לתוקף.\"\nBasic.Settings.ConfirmTitle=\"אישור השינויים\"\nBasic.Settings.Confirm=\"קיימים שינויים שלא נשמרו. לשמור את השינויים?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"לשירות %1 %2 יש שליטה בחלק מהגדרות ההזרמה שלך\"\nBasic.Settings.General=\"כללי\"\nBasic.Settings.General.Language=\"שפה\"\nBasic.Settings.General.Updater=\"עדכונים\"\nBasic.Settings.General.UpdateChannel=\"ערוץ עדכונים\"\nBasic.Settings.General.UpdateChannelDisabled=\"(מושבת)\"\nBasic.Settings.General.UpdateChannelDefault=\"(ברירת מחדל)\"\nBasic.Settings.General.EnableAutoUpdates=\"לבדוק אם יש עדכונים אוטומטית עם ההפעלה\"\nBasic.Settings.General.OpenStatsOnStartup=\"לפתוח חלונית סטטיסטיקה בהפעלה\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"הסתרת חלונות OBS מצילומי מסך\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"הגדרה זאת מסתירה את כל החלונות של OBS Studio שאינם משדרים כדי שלא יצולמו על ידי OBS והיא משפיעה על יישומים אחרים\\n, כגון שיחות ועידה, שיתוף מסך, תמיכה מרחוק, צילומי מסך ותוכניות אחרות שמקבלות מסך כקלט.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"הפעלת אפשרות זאת מסתירה את כל החלונות של OBS Studio שאינם משדרים כדי שלא יצולמו על ידי OBS והיא משפיעה על יישומים אחרים, כגון שיחות ועידה, שיתוף מסך, תמיכה מרחוק, צילומי מסך ותוכניות אחרות שמקבלות מסך כקלט.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"הצגת תיבות דו־שיח לאישור בעת התחלת שידורים חיים\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"הצגת תיבות דו־שיח לאישור בעת עצירת שידורים חיים\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"הצגת תיבת דו־שיח לאישור בעת עצירת ההקלטה\"\nBasic.Settings.General.Projectors=\"מקרנים\"\nBasic.Settings.General.HideProjectorCursor=\"הסתר את הסמן מעל מקרנים\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"הפוך מקרנים תמיד בעליון\"\nBasic.Settings.General.Snapping=\"יישור הצמדת מקור\"\nBasic.Settings.General.ScreenSnapping=\"הצמדת מקורות לקצה המסך\"\nBasic.Settings.General.CenterSnapping=\"הצמדת מקורות למרכז הציר האופקי והאנכי\"\nBasic.Settings.General.SourceSnapping=\"הצמדת מקורות למקורות אחרים\"\nBasic.Settings.General.SnapDistance=\"רגישות צמד\"\nBasic.Settings.General.SpacingHelpers=\"הצגת כווני יישור פיקסלים\"\nBasic.Settings.General.RecordWhenStreaming=\"הקלטה אוטומטית בעת שידורים חיים\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"להמשיך להקליט כאשר השידור החי נעצר\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"הפעלה אוטומטית של מטמון השמעה חוזרת בעת שידורים חיים\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"השארת מטמון ההשמעה החוזרת פעיל כאשר השידור החי נעצר\"\nBasic.Settings.General.SysTray=\"מגש המערכת\"\nBasic.Settings.General.SysTrayWhenStarted=\"מזער למגש המערכת בתחילה\"\nBasic.Settings.General.SystemTrayHideMinimize=\"מזער תמיד למגש המערכת במקום שורת המשימות\"\nBasic.Settings.General.SaveProjectors=\"שמור את המקרנים ביציאה\"\nBasic.Settings.General.CloseExistingProjectors=\"הגבלה למקרן אחד במסך מלא בכל מסך\"\nBasic.Settings.General.Preview=\"תצוגה מקדימה\"\nBasic.Settings.General.OverflowHidden=\"החבא גלישה\"\nBasic.Settings.General.OverflowAlwaysVisible=\"הצג גלישה תמיד\"\nBasic.Settings.General.OverflowSelectionHidden=\"הצג גלישה אפילו אם המקור בלתי נראה\"\nBasic.Settings.General.Importers=\"מייבאים\"\nBasic.Settings.General.AutomaticCollectionSearch=\"חיפוש מקומות ידועים לאוספי סצנות כאשר מייבא\"\nBasic.Settings.General.SwitchOnDoubleClick=\"מעבר לסצנה על ידי הקלקה כפולה\"\nBasic.Settings.General.StudioPortraitLayout=\"אפשר פריסה אופקית/אנכית\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"הצג תגיות תצוגה מקדימה/תוכנית\"\nBasic.Settings.General.Multiview=\"ריבוי תצוגות\"\nBasic.Settings.General.Multiview.MouseSwitch=\"למעבר בין סצנות יש ללחוץ\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"הצג שמות סצנה\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"הצגת אזורים בטוחים (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"פריסת ריבוי תצוגות\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"אופקי, עליון (8 סצנות)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"אופקי, תחתון (8 סצנות)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"אנכי, שמאל (8 סצנות)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"אנכי, ימין (8 סצנות)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"אופקי, עליון (18 סצנות)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"אופקי, עליון (24 סצנות)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"סצנות בלבד (4 סצנות)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"סצנות בלבד (9 סצנות)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"סצנות בלבד (16 סצנות)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"סצנות בלבד (25 סצנות)\"\nBasic.Settings.General.ChannelName.stable=\"יציב\"\nBasic.Settings.General.ChannelDescription.stable=\"המהדורה היציבה העדכנית ביותר\"\nBasic.Settings.General.ChannelName.beta=\"מהדורות ניסוי (בטא) / מועמדות להפצה\"\nBasic.Settings.General.ChannelDescription.beta=\"גרסאות מפוקפקות טרום הפצה\"\nBasic.Settings.Appearance=\"מראה\"\nBasic.Settings.Appearance.General=\"כללי\"\nBasic.Settings.Appearance.General.Theme=\"ערכת נושא\"\nBasic.Settings.Appearance.General.Variant=\"סגנון\"\nBasic.Settings.Appearance.General.NoVariant=\"אין סגנונות זמינים\"\nBasic.Settings.Appearance.FontScale=\"גודל כתב\"\nBasic.Settings.Appearance.Density=\"צפיפות\"\nBasic.Settings.Appearance.Classic=\"קלסי\"\nBasic.Settings.Appearance.Compact=\"מרוכז\"\nBasic.Settings.Appearance.Normal=\"רגיל\"\nBasic.Settings.Appearance.Comfortable=\"נוח\"\nBasic.Settings.Appearance.OptionsWarning=\"חלק מאפשרויות ההצגה אינה זמינות לסגנון הזה.\"\nBasic.Settings.Stream=\"שידורים חיים\"\nBasic.Settings.Stream.Destination=\"יעד\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"שימוש באימות\"\nBasic.Settings.Stream.Custom.Username=\"שם משתמש\"\nBasic.Settings.Stream.Custom.Password=\"סיסמה\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: למלא את srp_username‏.\\nRTMP: למלא את שם המשתמש.\\nSRT: לא בשימוש.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: למלא את srp_password‏.\\nRTMP: למלא את הסיסמה.\\nSRT: למלא את מילת צופן ההצפנה.\"\nBasic.Settings.Stream.BandwidthTestMode=\"הפעלת מצב בדיקת רוחב פס\"\nBasic.Settings.Stream.TTVAddon=\"תוספי צ׳אט Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"ללא\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV וגם FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"הגדרת שידור חי חסר\"\nBasic.Settings.Stream.StreamSettingsWarning=\"פתיחת הגדרות\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"הכתובת ומפתח השידורים החיים חסרים\\n\\nנא לגשת לתפריט ההגדרות כדי למלא את הכתובת ואת מפתח השידורים החיים בלשונית \\\"שידורים חיים\\\".\"\nBasic.Settings.Stream.MissingUrl=\"מפתח השידורים החיים חסר\\n\\nנא לגשת לתפריט ההגדרות כדי למלא את מפתח השידורים החיים בלשונית \\\"שידורים חיים\\\".\"\nBasic.Settings.Stream.MissingStreamKey=\"מפתח השידורים החיים חסר\\n\\nנא לגשת לתפריט ההגדרות כדי למלא את מפתח השידורים החיים בלשונית \\\"שידורים חיים\\\".\"\nBasic.Settings.Stream.IgnoreRecommended=\"התעלמות מהמלצות להגדרת שירות השידור החי\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"עקיפת ההגדרות המומלצות\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"אזהרה: התעלמות מהגבלות השירות עלולה לגרום לירידה באיכות השידור או כלל למניעת השידור.\\n\\nלהמשיך?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"קצב נתונים מרבי לווידאו: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"קצב נתונים מרבי לשמע: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"רזולוציה מרבית: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"‏FPS מרבי: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"בחירת שרת משלך…\"\nBasic.Settings.Stream.ServiceCustomServer=\"שרת משלך\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"הפעלת %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"רוחב־פס מרבי להזרמה\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"אוטו׳\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"כמות רצועות וידאו מרבית\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"אוטו׳\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"הפעלת הטלת תזרימים ל־FLV (משתמש בהגדרות פשוטות לקובץ הקלטה)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"דריסת הגדרות (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"הפעלת דריסת הגדרות\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"סרטון רב־רצועות\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"משטח עבודה נוסף\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"שידור מקבילי\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"שידור מקבילי מאפשר לך לקודד ולשלוח מגוון איכויות וידאו. <a href='https://obsproject.com/kb/whip-streaming-guide'>מידע נוסף</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"סך כל השכבות\"\nBasic.Settings.Stream.AdvancedOptions=\"אפשרויות מתקדמות\"\nBasic.Settings.Output=\"פלט\"\nBasic.Settings.Output.Format=\"תצורת הקלטה\"\nBasic.Settings.Output.Format.MKV=\"סרטון Matroska‏ (‎.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"MP4 משולב (‎‎.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV משולב (‎.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 מחולק (‎.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV מחולק (‎.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"MOV מחולק כותב את ההקלטה במקטעים ואינו דורש את אותו הגימור כמו קובצי MOV מסורתיים.\\nאופן השמירה הזה מבטיח שלא תהיה בעיה לנגן את הקובץ אפילו אם הכתיבה לכונן נקטעת, למשל, כתוצאה ממסך כחול או נפילת חשמל.\\n\\nיש איזשהו חשש לחוסר תאימות מול כל הנגנים והעורכים. אפשר להשתמש בקובץ ← ריבוב הקלטות כדי להמיר את הקובץ לתצורה תואמת יותר במידת הצורך.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"MP4 מחולק כותב את ההקלטה במקטעים ואינו דורש את אותו הגימור כמו קובצי MP4 מסורתיים.\\nאופן השמירה הזה מבטיח שלא תהיה בעיה לנגן את הקובץ אפילו אם הכתיבה לכונן נקטעת, למשל, כתוצאה ממסך כחול או נפילת חשמל.\\n\\nיש איזשהו חשש לחוסר תאימות מול כל הנגנים והעורכים. אפשר להשתמש בקובץ ← ריבוב הקלטות כדי להמיר את הקובץ לתצורה תואמת יותר במידת הצורך.\"\nBasic.Settings.Output.Encoder.Video=\"מצפין וידאו\"\nBasic.Settings.Output.Encoder.Audio=\"מצפין שמע\"\nBasic.Settings.Output.SelectDirectory=\"בחירת תיקיית הקלטות\"\nBasic.Settings.Output.DynamicBitrate=\"התאמת קצב הסיביות דינמית בהתאם לעומס על הרשת\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"התאמת קצב הסיביות דינמית בהתאם לעומס על הרשת (בטא)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"במקום שפריימים יפלו כתוצאה מעומס על הרשת, קצב הביטים ישתנה באופן דינאמי.\\n\\nיש לשים לב כי הגדרה זו עלולה להעלות את ההשהייה לצופים בעת עומס פתאומי על הרשת.\\nכאשר קצב הביטים יורד, עשויות לעבור כמה דקות עד שישוחזר.\\n\\nכרגע נתמך רק עבור RTMP.\"\nBasic.Settings.Output.Mode=\"מצב פלט\"\nBasic.Settings.Output.Mode.Simple=\"פשוט\"\nBasic.Settings.Output.Mode.Adv=\"מתקדם\"\nBasic.Settings.Output.Mode.FFmpeg=\"פלט FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"הפעלת מטמון השמעה חוזרת\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"זמן מרבי להשמעה חוזרת\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"כמות זיכרון מרבית\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"שימוש משוער בזיכרון: %1 מ״ב\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"אזהרה: השימוש המשוער של %1 MiB מהזיכרון גדול מהכמות המומלצת המרבית בגודל %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"לא ניתן לאמוד את השימוש בזיכרון. נא להגדיר מגבלת זיכרון מרבי.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"קידומת שם לקובץ מטמון ההשמעה החוזרת\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"סיומת\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"לא ניתן להשתמש במטמון ההשמעה החוזרת כאשר סוג ההקלטה מוגדר כפלט מותאם אישית (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"נתיב הקלטה\"\nBasic.Settings.Output.Simple.RecordingQuality=\"איכות הקלטה\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"כמו בשידורים החיים\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"איכות גבוהה, גודל קובץ בינוני\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"איכות בלתי מובחנת, גודל קובץ גדול\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"ללא אובדן איכות, גודל קובץ עצום\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"אזהרה: קצב הנתונים של הווידאו בשידור החי יוגדר ל־%1, שהוא המגבלה הגבוהה ביותר לשירות השידורים החיים הנוכחי.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"אזהרה: קצב הנתונים של השמע בשידור החי יוגדר ל־%1, שהוא המגבלה הגבוהה ביותר לשירות השידורים החיים הנוכחי.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"אזהרה: לא ניתן להשהות הקלטות אם מקודד ההקלטה מוגדר ל־\\\"כמו בשידורים החיים\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"אזהרה: ההקלטה הנבחרת אינה תואמת למצפיני השידור הנבחרים.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"אזהרה: הקלטה עם מקודד תוכנה באיכות שונה מהשידור החי תדרוש שימוש במשאבי מעבד נוספים אם מתבצעים הקלטה ושידור חי במקביל.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"אזהרה: איכות ללא אובדן יוצרת גודלי קבצים גדולים מאוד! איכות ללא אובדן נתונים יכולה להשתמש ביותר מ־7 גיגה־בתים משטח הכונן לדקה ברזולוציות גבוהות וקצב תמוניות גבוה. שימוש באיכות ללא אובדן אינו מומלץ להקלטות ארוכות אלא אם יש מספיק מקום פנוי בכונן. מטמון ההשמעה החוזרת אינו זמין בעת שימוש באיכות ללא אובדן.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"להשתמש באיכות ללא אובדן איכות?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"אזהרה איכות ללא אובדן איכות!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"תוכנה (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"חומרה (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"חומרה (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"חומרה (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"חומרה (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"חומרה (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"חומרה (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"חומרה (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"חומרה (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"חומרה (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"חומרה (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"תוכנה (x 264 השימוש בהגדרת מעבד נמוך, גודל הקובץ גדל)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (ברירת מחדל)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"החלפת רצועת Twitch VOD (שימוש ברצועה 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"רצועת שמע\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"קצב פריימים\\רזולוציה לא נתמכת\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"שירות זה לשידורים חיים לא תומך ברזולצייה ובקצב הפריימים הנוכחיים שהגדרת. הם ישתנו לערך הכי קרוב ונתמך:\\n\\n%1\\n\\nלהמשיך?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"רזולוציה: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"פריימים לשנייה: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"מקודד לא נתמך\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"שירות השידור \\\"%1\\\" לא תומך במקודד \\\"%2\\\". המקודד ישתנה ל-\\\"%3\\\".\\n\\nהאם ברצונך להמשיך?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"שירות השידור „%1” לא תומך במצפינים „%2” ו־„%3”. המצפינים האלה יתחלפו ב־„%4” וב־„%5”.\\n\\nלהמשיך?\"\nBasic.Settings.Output.VideoBitrate=\"קצב הנתונים של הווידאו\"\nBasic.Settings.Output.AudioBitrate=\"קצב הנתונים של השמע\"\nBasic.Settings.Output.Reconnect=\"חיבור מחדש באופן אוטומטי\"\nBasic.Settings.Output.RetryDelay=\"השהיית ניסיון חוזר\"\nBasic.Settings.Output.MaxRetries=\"מרב נסינות\"\nBasic.Settings.Output.Advanced=\"הפעלת הגדרות הקידוד בהתאמה אישית (מתקדם)\"\nBasic.Settings.Output.EncoderPreset=\"ערכי מקודד קבועים מראש\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"‏%1 (שימוש נמוך במעבד, איכות נמוכה ביותר)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"‏%1 (ברירת מחדל) (שימוש בינוני במעבד, איכות סטנדרטית)\"\nBasic.Settings.Output.EncoderPreset.fast=\"‏%1 (שימוש גבוה במעבד, איכות גבוהה)\"\nBasic.Settings.Output.CustomEncoderSettings=\"הגדרות מקודד מותאמות אישית\"\nBasic.Settings.Output.CustomMuxerSettings=\"הגדרות ממיר מותאמות אישית\"\nBasic.Settings.Output.NoSpaceFileName=\"צור שם קובץ ללא רווחים\"\nBasic.Settings.Output.Adv.Rescale=\"שנה קנה מידה של הפלט\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"מושבת\"\nBasic.Settings.Output.Adv.AudioTrack=\"רצועת שמע\"\nBasic.Settings.Output.Adv.Streaming=\"שידורים חיים\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"הגדרות שידור\"\nBasic.Settings.Output.Adv.Audio.Track1=\"רצועה 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"רצועה 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"רצועה 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"רצועה 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"רצועה 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"רצועה 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"רצועת Twitch VOD\"\nBasic.Settings.Output.Adv.Encoder=\"הגדרות מקודד\"\nBasic.Settings.Output.Adv.Recording=\"הקלטה\"\nBasic.Settings.Output.Adv.Recording.Settings=\"הגדרות הקלטה\"\nBasic.Settings.Output.Adv.Recording.RecType=\"סוג ההקלטה\"\nBasic.Settings.Output.Adv.Recording.Type=\"סוג\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"ברירת מחדל\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"פלט מותאם אישית (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(שימוש במקודד לשידורים החיים)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"תבנית שם קובץ\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"דרוס אם קובץ קיים\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"פלט מותאם אישית (FFmpeg) מסופק ללא הגנות.\\nכדי לשקול להשתמש בסוג ההקלטה „רגיל”.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"סוג פלט FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"פלט לכתובת\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"פלט לקובץ\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"תבניות הקלטה נפוצות\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"כל הקבצים\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"נתיב קובץ או כתובת\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"פורמט קובץ\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"שמע\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"וידאו\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"פורמט ברירת-מחדל\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"תיאור סוג הקובץ\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"המפענח של השמע/וידאו אותר מתוך נתיב הקובץ או הכתובת\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"מקודד ברירת-מחדל\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"השבתת מצפין\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"מקודד וידאו\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"הגדרות מקודד וידאו (אם בכלל)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"מקודד שמע\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"הגדרות מקודד שמע (אם בכלל)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"הגדרות Muxer (אם בכלל)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"מרווח תמונות מפתח (פריימים)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"הצגת כל המפענחים (גם אם אינם תואמים)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"הגדרות FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"פיצול קבצים אוטומטי\"\nBasic.Settings.Output.SplitFile.TypeTime=\"פיצול לפי זמן\"\nBasic.Settings.Output.SplitFile.TypeSize=\"פיצול לפי גודל\"\nBasic.Settings.Output.SplitFile.TypeManual=\"פיצול ידני בלבד\"\nBasic.Settings.Output.SplitFile.Time=\"פיצול זמן\"\nBasic.Settings.Output.SplitFile.Size=\"פיצול גודל\"\nScreenshot=\"פלט צילום מסך\"\nScreenshot.SourceHotkey=\"צילום מסך של המקור הנבחר\"\nScreenshot.StudioProgram=\"צילום מסך (תכנה)\"\nScreenshot.Preview=\"צילום מסך (תצוגה מקדימה)\"\nScreenshot.Scene=\"צילום מסך (סצנה)\"\nScreenshot.Source=\"צילום מסך (מקור)\"\nFilenameFormatting.TT.CCYY=\"שנה, ארבע ספרות\"\nFilenameFormatting.TT.YY=\"שנה, שתי הספרות האחרונות (00-99)\"\nFilenameFormatting.TT.MM=\"חודש כמספר עשרוני (01-12)\"\nFilenameFormatting.TT.DD=\"יום בחודש, מרופד באפסים (01-31)\"\nFilenameFormatting.TT.hh=\"שעה בתצורת 24 שעות (00-23)\"\nFilenameFormatting.TT.mm=\"דקה (00-59)\"\nFilenameFormatting.TT.ss=\"שנייה (00-59)\"\nFilenameFormatting.TT.Percent=\"סימן %\"\nFilenameFormatting.TT.a=\"שם מקוצר של היום בשבוע\"\nFilenameFormatting.TT.A=\"השם המלא של היום בשבוע\"\nFilenameFormatting.TT.b=\"שם מקוצר של חודש\"\nFilenameFormatting.TT.B=\"שם חודש מלא\"\nFilenameFormatting.TT.d=\"יום בחודש, מרופד באפסים (01-31)\"\nFilenameFormatting.TT.H=\"שעה בתצורת 24 שעות (00-23)\"\nFilenameFormatting.TT.I=\"שעה בתצורת 12 שעות (01-12)\"\nFilenameFormatting.TT.m=\"חודש כמספר עשרוני (01-12)\"\nFilenameFormatting.TT.M=\"דקה (00-59)\"\nFilenameFormatting.TT.p=\"הקצאת AM או PM\"\nFilenameFormatting.TT.s=\"זמן בשניות מאז ראשית ה־epoch של יוניקס\"\nFilenameFormatting.TT.S=\"שנייה (00-59)\"\nFilenameFormatting.TT.y=\"שנה, שתי הספרות האחרונות (00-99)\"\nFilenameFormatting.TT.Y=\"שנה\"\nFilenameFormatting.TT.z=\"היסט ב־ISO 8601 מ־UTC באזור הזמן\"\nFilenameFormatting.TT.Z=\"שם אזור זמן או קיצור\"\nFilenameFormatting.TT.FPS=\"תמוניות לשנייה\"\nFilenameFormatting.TT.CRES=\"רזולוציית (משטח עבודה) בסיסית\"\nFilenameFormatting.TT.ORES=\"רזולוציית פלט (בקנה מידה)\"\nFilenameFormatting.TT.VF=\"תצורת סרטון\"\nBasic.Settings.Video=\"וידאו\"\nBasic.Settings.Video.Adapter=\"מתאם תצוגה\"\nBasic.Settings.Video.BaseResolution=\"רזולציית (משטח עבודה) בסיס\"\nBasic.Settings.Video.ScaledResolution=\"רזולוציית (קנה מידה) פלט\"\nBasic.Settings.Video.DownscaleFilter=\"פילטר צמצום קנה מידה\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[הרזולוציות תואמות, אין צורך בהקטנה]\"\nBasic.Settings.Video.FPS=\"פריימים לשנייה\"\nBasic.Settings.Video.FPSCommon=\"ערכים משותפים לפריימים לשניה\"\nBasic.Settings.Video.FPSInteger=\"ערך מספר שלם של פריימים לשניה\"\nBasic.Settings.Video.FPSFraction=\"ערך שברים של פריימים לשניה\"\nBasic.Settings.Video.Numerator=\"מונה\"\nBasic.Settings.Video.Denominator=\"מכנה\"\nBasic.Settings.Video.Renderer=\"מעבד\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (ניסיוני)\"\nBasic.Settings.Video.InvalidResolution=\"ערך רזולוציה לא חוקי. חייב להיות [width]x[height] (לדוגמה 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"פלט וידאו פעיל כעת. נא כבה את כל הפלטים בכדי לשנות הגדרות וידאו.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"דו־ליניארי (מהיר ביותר, מטושטש בשינוי קנה מידה)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"ממוצע משוקלל (חד בשינוי קנה המידה, 16 דגימות)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (חד בשינוי קנה המידה, 36 דגימות)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"שטח (סכום משוקלל, 4/6/9 דגימות)\"\nBasic.Settings.Audio=\"שמע\"\nBasic.Settings.Audio.SampleRate=\"קצב דגימה\"\nBasic.Settings.Audio.Channels=\"ערוצים\"\nBasic.Settings.Audio.Meters=\"מדי-שמע\"\nBasic.Settings.Audio.MeterDecayRate=\"קצב דעיכה\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"מהיר\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"בינוני (סוג I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"איטי (סוג II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"סוג שיא המד\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"שיא דגימה\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"שיא אמיתי (שימוש גבוה יותר במעבד)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"אזהרה: שמע צליל היקפי מאופשר.\"\nBasic.Settings.Audio.MultichannelWarning=\"עבור שידורים חיים, כדאי לבדוק אם שירות השידורים החיים תומך בקבלה והשמעה של צליל סראונד. למשל, Facebook 360 Live תומך בסראונד באופן מלא, YouTube Live תומך בקבלה של שמע 5.1 (ובנגינה בטלוויזיות).\\n\\nמסנני השמע של OBS תומכים בסראונד, למרות שהתמיכה בתוסף VST אינה מובטחת.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"להפעיל קול שמע מקיף?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"האם להפעיל קול שמע מקיף?\"\nBasic.Settings.Audio.Devices=\"התקני קול גלובליים\"\nBasic.Settings.Audio.DesktopDevice=\"שמע בשולחן העבודה\"\nBasic.Settings.Audio.DesktopDevice2=\"שמע בשולחן העבודה 2\"\nBasic.Settings.Audio.AuxDevice=\"מיקרופון/התקן שמע עזר\"\nBasic.Settings.Audio.AuxDevice2=\"מיקרופון/התקן שמע עזר 2\"\nBasic.Settings.Audio.AuxDevice3=\"מיקרופון/התקן שמע עזר 3\"\nBasic.Settings.Audio.AuxDevice4=\"מיקרופון/התקן שמע עזר 4\"\nBasic.Settings.Audio.EnablePushToMute=\"אפשר לחץ-להשתקה\"\nBasic.Settings.Audio.PushToMuteDelay=\"השהיית לחץ-להשתקה\"\nBasic.Settings.Audio.EnablePushToTalk=\"הפעלת לחיצה־לדיבור\"\nBasic.Settings.Audio.PushToTalkDelay=\"השהיית לחץ-כדי-לדבר\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[התקן לא מחובר או לא זמין]\"\nBasic.Settings.Audio.Disabled=\"מושבת\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"מצב מטמון שמע בהשהיה נמוכה (לערוצי פלט Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"אזהרה: מטמון שמע עם השהיה נמוכה פעיל.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"מצב מטמון שמע בהשהיה נמוכה עשוי לגרום לשמע להיתקע או להיעצר מחלק מהמקורות.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"להפעיל מצב מטמון שמע עם השהיה נמוכה?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"להפעיל מצב מטמון שמע עם השהיה נמוכה?\"\nBasic.Settings.Accessibility=\"נגישות\"\nBasic.Settings.Accessibility.ColorOverrides=\"להשתמש בצבעים שונים\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"מסגרת מקור (סימון)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"מסגרת מקור (חיתוך)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"מסגרת מקור (בעת מעבר עכבר)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"פס עוצמת השמע (-60 עד -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"פס עוצמת השמע (-20 עד -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"פס עוצמת השמע (-9 עד 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"פס עוצמת השמע (-60 עד -20dB) (פעיל)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"פס עוצמת השמע (-20 עד -9dB) (פעיל)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"פס עוצמת השמע (-9 עד 0dB) (פעיל)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"ערכת צבעים\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"ברירת מחדל\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"התאמה אישית\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"חלופה עבור עיוורון צבעים\"\nBasic.Settings.Advanced=\"מתקדם\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"הצגת אזהרת פלטים פעילים ביציאה\"\nBasic.Settings.Advanced.General.ProcessPriority=\"עדיפות תהליך\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"גבוה\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"מעל לרגיל\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"רגיל\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"מתחת לעדיפות רגילה\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"לא פעיל\"\nBasic.Settings.Advanced.FormatWarning=\"אזהרה: תבניות צבע שונות מ־NV12/P010 נועדו בעיקר להקלטה, ואינן מומלצות בעת שידורים חיים. שידורים חיים עלולים לגרום למשאבי עיבוד מוגברים כתוצאה מהמרת תבנית צבע.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"אזהרה: תצורות דיוק גבוה נפוצות יותר לשימוש במרחבי צבע HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"אזהרה: Rec. 2100 אמור להשתמש בתצורה עם יותר דיוק.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"תבנית צבע\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 סיביות, 4:2:0, 2 מישורים)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 סיביות, 4:2:0, 3 מישורים)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 סיביות, 4:4:4, 3 מישורים)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 סיביות, 4:2:0, 2 מישורים)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 סיביות, 4:2:0, 3 מישורים)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 סיביות, 4:2:2, 2 מישורים)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 סיביות, 4:4:4, 2 מישורים)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 סיביות)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"מרחב צבעים\"\nBasic.Settings.Advanced.Video.ColorRange=\"טווח צבעים\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"מוגבל\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"מלא\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"רמת SDR לבן\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"רמת שיא נומינלית של HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"מכשיר ניטור\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"ברירת-מחדל\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"בטל צימוד השמע של Windows\"\nBasic.Settings.Advanced.StreamDelay=\"השהיה לפני שידורים חיים\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"משך\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"שמר נקודת חיתוך (השהייה מוגדלת) בעת חיבור מחדש\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"שימוש זיכרון משוער: %1 MB\"\nBasic.Settings.Advanced.Network=\"רשת\"\nBasic.Settings.Advanced.Network.Disabled=\"הפרוטוקול הנוכחי שנבחר לשידורים החיים לא תומך בשינוי הגדרות הרשת.\"\nBasic.Settings.Advanced.Network.BindToIP=\"אגד ל- IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"משפחת IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"הפעלת מיטובי רשת\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"אפשר TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"מנסה להפוך את פלט RTMP לידידותי יותר עבור אפליקציות רגישות ל-latency ברשת על ידי ויסות קצב השידור.\\nזה עלול להעלות את הסיכון לנפילת פריימים בחיבורים לא יציבים.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"התנהגות מקש-חם\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"לעולם אל תבטל מקשים-חמים\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"בטל מקשים-חמים כאשר החלון הראשי בפוקוס\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"בטל מקשים-חמים כאשר החלון הראשי אינו בפוקוס\"\nBasic.Settings.Advanced.AutoRemux=\"לרבב אוטומטית ל־%1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(הקלטה בתור mkv)\"\nBasic.AdvAudio=\"מאפייני קול מתקדמים\"\nBasic.AdvAudio.ActiveOnly=\"מקורות פעילים בלבד\"\nBasic.AdvAudio.Name=\"שם\"\nBasic.AdvAudio.Volume=\"עוצמת שמע\"\nBasic.AdvAudio.VolumeSource=\"עוצמה עבור '%1'\"\nBasic.AdvAudio.Mono=\"מונו\"\nBasic.AdvAudio.MonoSource=\"דאון-מיקס מונו עבור '%1'\"\nBasic.AdvAudio.Balance=\"איזון\"\nBasic.AdvAudio.BalanceSource=\"איזון עבור '%1'\"\nBasic.AdvAudio.SyncOffset=\"היסט סינכרון\"\nBasic.AdvAudio.SyncOffsetSource=\"איזון סנכרון עבור '%1'\"\nBasic.AdvAudio.Monitoring=\"ניטור שמע\"\nBasic.AdvAudio.Monitoring.None=\"ללא ניטור\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"ניטור בלבד (השתקת הפלט)\"\nBasic.AdvAudio.Monitoring.Both=\"ניטור ופלט\"\nBasic.AdvAudio.MonitoringSource=\"ניטור שמע עבור \\\"%1\\\"\"\nBasic.AdvAudio.AudioTracks=\"רצועות\"\nBasic.Settings.Hotkeys=\"מקשי קיצור\"\nBasic.Settings.Hotkeys.Pair=\"צירופי מקשים משותים עם '%1' משמשים כמחליפים\"\nBasic.Settings.Hotkeys.Filter=\"סינון\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"סינון לפי מקש חם\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"מקש חם זה חולק פעולה אחת או יותר, הסתירות תופענה בלחיצה\"\nBasic.Settings.Hotkeys.PleaseWait=\"המקשים החמים נטענים, נא להמתין…\"\nBasic.Hotkeys.SelectScene=\"עבור לסצנה\"\nBasic.SystemTray.Show=\"הצג\"\nBasic.SystemTray.Hide=\"הסתר\"\nBasic.SystemTray.Message.Reconnecting=\"ההתקשרות נותקה. התחברות מחדש...\"\nHotkeys.Insert=\"הוסף\"\nHotkeys.Delete=\"הסר\"\nHotkeys.Home=\"בית\"\nHotkeys.End=\"סיום\"\nHotkeys.PageUp=\"דף למעלה\"\nHotkeys.PageDown=\"דף למטה\"\nHotkeys.CapsLock=\"Caps lock\"\nHotkeys.Backspace=\"מחק תו\"\nHotkeys.Tab=\"טאב\"\nHotkeys.Print=\"הדפס\"\nHotkeys.Pause=\"השהה\"\nHotkeys.Left=\"שמאל\"\nHotkeys.Right=\"ימין\"\nHotkeys.Up=\"למעלה\"\nHotkeys.Down=\"למטה\"\nHotkeys.Super=\"סופר\"\nHotkeys.Menu=\"תפריט\"\nHotkeys.Space=\"רווח\"\nHotkeys.NumpadNum=\"מקשי מספרים %1\"\nHotkeys.NumpadMultiply=\"מקשי מספרים הכפל\"\nHotkeys.NumpadDivide=\"מקשי מספרים חלק\"\nHotkeys.NumpadAdd=\"מקשי מספרים הוסף\"\nHotkeys.NumpadSubtract=\"מקשי מספרים חסר\"\nHotkeys.NumpadDecimal=\"מקשי מספרים עשרוני\"\nHotkeys.AppleKeypadNum=\"%1 (מקלדת נומרית)\"\nHotkeys.AppleKeypadMultiply=\"* (מקלדת נומרית)\"\nHotkeys.AppleKeypadDivide=\"/ (מקלדת נומרית)\"\nHotkeys.AppleKeypadAdd=\"+ (מקלדת נומרית)\"\nHotkeys.AppleKeypadSubtract=\"-(מקלדת נומרית)\"\nHotkeys.AppleKeypadDecimal=\". (מקלדת נומרית)\"\nHotkeys.AppleKeypadEqual=\"= (מקלדת נומרית)\"\nHotkeys.MouseButton=\"עכבר %1\"\nMute=\"השתק\"\nUnmute=\"בטל השתקה\"\nPush-to-mute=\"לחץ-להשתקה\"\nPush-to-talk=\"לחץ-כדי-לדבר\"\nSceneItemShow=\"הצגת \\\"%1\\\"\"\nSceneItemHide=\"הסתר '%1'\"\nOutputWarnings.NoTracksSelected=\"חובה לבחור רצועה אחת לפחות\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"שגיאת הגדרות פלט\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"בכל ערוצי הפלט אמורה להיות לפחות רצועת שמע אחת שנבחרה.\"\nOutputWarnings.MP4Recording=\"אזהרה: הקלטות שיישמרו בתור MP4/MOV יהיו בלתי־שמישות אם לא הקובץ לא ייצא בשלמותו (למשל, בגלל שגיאות מערכת, אובדן מתח חשמלי, וכו׳). אם ברצונך להקליט רצועות שמע מרובות, נא לשקול להשתמש בפורמט MKV ואז להמיר את ההקלטה לפורמט MP4/MOV לאחר סיום ההקלטה (קובץ←המרת הקלטות)\"\nOutputWarnings.CannotPause=\"אזהרה: לא ניתן להשהות הקלטות אם מקודד ההקלטה מוגדר ל־\\\"(שימוש במקודד לשידורים החיים)\\\"\"\nOutputWarnings.CodecIncompatible=\"בחירת מצפיני השמע או הווידאו אופסה עקב חוסר תאימות. נא לבחור במצפינים נתמכים מהרשימה.\"\nCodecCompat.Incompatible=\"(חוסר תאימות מול %1)\"\nCodecCompat.CodecPlaceholder=\"בחירת מצפין…\"\nCodecCompat.ContainerPlaceholder=\"בחירת תצורה…\"\nCodecCompat.CodecMissingOnExit.Title=\"לא נבחר מצפין\"\nCodecCompat.CodecMissingOnExit.Text=\"לפחות מצפין אחד של שמע או וידאו לא הוגדר. נא לוודא לבחור את המצפינים להקלטה ולשידור.\"\nCodecCompat.ContainerMissingOnExit.Title=\"לא נבחרה תצורה\"\nCodecCompat.ContainerMissingOnExit.Text=\"לא נבחרה תצורת הקלטה. נא לבחור תצורת הקלטה שתואמת למצפין התזרים הנבחר.\"\nFinalScene.Title=\"מחיקת הסצנה\"\nFinalScene.Text=\"נדרשת סצנה אחת לפחות.\"\nNoSources.Title=\"אין מקורות\"\nNoSources.Text=\"נראה שלא הוספת מקורות וידאו, לכן הפלט שלך יהיה מסך ריק. לעשות זאת?\"\nNoSources.Text.AddSource=\"אפשר להוסיף מקורות על ידי לחיצה על סמל ה + מתחת לתיבת המקורות בחלון הראשי בכל עת.\"\nNoSources.Label=\"אין לך מקורות.\\n\\לחץ על לחצן + למטה\\nאו לחצן ימני כאן בכדי להוסיף אחד.\"\nChangeBG=\"קבע צבע\"\nCustomColor=\"צבע מותאם אישית\"\nBrowserSource.EnableHardwareAcceleration=\"הפעלת האצת חומרה למקורות דפדפן\"\nAbout=\"על אודות\"\nAbout.Info=\"OBS Studio היא תכנה חופשית בקוד פתוח להקלטת וידאו ולשידורים חיים.\"\nAbout.Donate=\"מתן תרומה\"\nAbout.GetInvolved=\"לקיחת חלק\"\nAbout.Authors=\"יוצרים\"\nAbout.License=\"רישיון\"\nAbout.Error=\"שגיאה! לא ניתן לקרוא את הקובץ.\\n\\nנא לגשת אל: %1\"\nAbout.Contribute=\"תמיכה במיזם OBS\"\nAddUrl.Title=\"הוספת מקור דרך כתובת\"\nAddUrl.Text=\"גררת כתובת לתוך OBS. זה יוסיף אוטומטית את הקישור כמקור. להמשיך?\"\nAddUrl.Text.Url=\"כתובת: %1\"\nResizeOutputSizeOfSource=\"שינוי גודל פלט (גודל מקור)\"\nResizeOutputSizeOfSource.Text=\"גודל רזולוציות הפלט והבסיס ישתנה לגודל המקור הנוכחי.\"\nResizeOutputSizeOfSource.Continue=\"להמשיך?\"\nPreviewTransition=\"תצוגה מקדימה של המעבר\"\nImporter=\"יבואן אוספי סצינות\"\nImporter.SelectCollection=\"בחירת אוסף סצנות\"\nImporter.Collection=\"אוסף סצנות\"\nImporter.HelpText=\"אפשר להוסיף קבצים לחלון זה כדי לייבא אוספים מ־OBS או מתכניות נתמכות אחרות.\"\nImporter.Path=\"נתיב אוסף\"\nImporter.Program=\"יישום מזוהה\"\nImporter.AutomaticCollectionPrompt=\"חפש אוספי סצינות באופן אוטומטי\"\nImporter.AutomaticCollectionText=\"באפשרות OBS למצוא אוספי סצנות לייבוא מתוכניות צד שלישי נתמכות באופן אוטומטי. להגדיר את OBS לאתר בשבילך אוספים באופן אוטומטי?\\n\\nאפשר לשנות הגדרה זו מאוחר יותר דרך הגדרות > כללי > מייבאים.\"\nImporter.SelectFile=\"עיון…\"\nRestart=\"אתחל\"\nNeedsRestart=\"צריך להפעיל את OBS Studio מחדש. להפעיל מחדש כעת?\"\nLoadProfileNeedsRestart=\"הפרופיל מכיל הגדרות הדורשות הפעלת OBS מחדש:\\n%1\\n\\nלהפעיל את OBS מחדש להחלת הגדרות אלה?\"\nContextBar.NoSelectedSource=\"לא נבחר מקור\"\nContextBar.MediaControls.PlayMedia=\"ניגון מדיה\"\nContextBar.MediaControls.PauseMedia=\"השהה מדיה\"\nContextBar.MediaControls.StopMedia=\"עצור מדיה\"\nContextBar.MediaControls.RestartMedia=\"התחל מדיה מחדש\"\nContextBar.MediaControls.PlaylistNext=\"הבא בפלייליסט\"\nContextBar.MediaControls.PlaylistPrevious=\"הקודם בפלייליסט\"\nContextBar.MediaControls.BlindSeek=\"יישומון חיפוש מדיה\"\nYouTube.Auth.Ok=\"ההזדהות הושלמה בהצלחה.\\nכעת ניתן לסגור את העמוד הזה.\"\nYouTube.Auth.NoCode=\"תהליך ההזדהות לא הושלם.\"\nYouTube.Auth.NoChannels=\"אין ערוץ(ים) זמין(ים) לחשבון שנבחר\"\nYouTube.Auth.WaitingAuth.Title=\"הזדהות משתמש YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"נא להשלים את ההזדהות בדפדפן החיצוני שלך.<br>אם הדפדפן החיצוני לא נפתח, יש לעבור לקישור הזה כדי להשלים את ההזדהות:<br>%1\"\nYouTube.AuthError.Text=\"נכשל בקבלת מידע ערוץ:%1\"\nYouTube.Actions.WindowTitle=\"הגדרת שידור Youtube - ערוץ: %1\"\nYouTube.Actions.CreateNewEvent=\"ליצור שידור חדש\"\nYouTube.Actions.ChooseEvent=\"בחר שידור קיים\"\nYouTube.Actions.Title=\"כותרת*\"\nYouTube.Actions.MyBroadcast=\"השידור שלי\"\nYouTube.Actions.Description=\"תיאור\"\nYouTube.Actions.Privacy=\"פרטיות*\"\nYouTube.Actions.Privacy.Private=\"פרטי\"\nYouTube.Actions.Privacy.Public=\"גלוי לכול\"\nYouTube.Actions.Privacy.Unlisted=\"לא רשום\"\nYouTube.Actions.Category=\"קטגוריה\"\nYouTube.Actions.Thumbnail=\"תמונה ממוזערת\"\nYouTube.Actions.Thumbnail.SelectFile=\"בחירת קובץ...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"לא נבחר קובץ\"\nYouTube.Actions.Thumbnail.ClearFile=\"נקה\"\nYouTube.Actions.MadeForKids=\"האם הסרטון הזה מיועד לילדים?*\"\nYouTube.Actions.MadeForKids.Yes=\"כן, הוא מיועד לילדים\"\nYouTube.Actions.MadeForKids.No=\"לא, הוא לא מיועד לילדים\"\nYouTube.Actions.AdditionalSettings=\"הגדרות נוספות\"\nYouTube.Actions.Latency=\"זמן שיהוי (Latency)\"\nYouTube.Actions.Latency.Normal=\"רגיל\"\nYouTube.Actions.Latency.Low=\"נמוך\"\nYouTube.Actions.Latency.UltraLow=\"אולטרה נמוך\"\nYouTube.Actions.EnableAutoStart=\"הפעלת התחלה-אוטומטית\"\nYouTube.Actions.EnableAutoStop=\"הפעלת הפסקה-אוטומטית\"\nYouTube.Actions.AutoStartStop.TT=\"מציין האם שידור מתוזמן זה אמור להתחיל אוטומטית\"\nYouTube.Actions.EnableDVR=\"הפעלת DVR\"\nYouTube.Actions.360Video=\"וידאו 360\"\nYouTube.Actions.ScheduleForLater=\"תזמן למועד מאוחר יותר\"\nYouTube.Actions.RememberSettings=\"שמור הגדרות אלו\"\nYouTube.Actions.Create_Ready=\"ליצור שידור חדש\"\nYouTube.Actions.Create_GoLive=\"ליצור שידור ולהתחיל להזרים\"\nYouTube.Actions.Choose_Ready=\"בחר שידור\"\nYouTube.Actions.Choose_GoLive=\"בחר שידור והתחל להזרים\"\nYouTube.Actions.Create_Schedule=\"לוח זמנים לשידור\"\nYouTube.Actions.Create_Schedule_Ready=\"לתזמן ולבחור שידור\"\nYouTube.Actions.Dashboard=\"פתח אולפן יוטיוב\"\nYouTube.Actions.Error.Title=\"שגיאה ביצירת שידור חי\"\nYouTube.Actions.Error.Text=\"שגיאת גישה ל-YouTube '%1'.<br/>ניתן למצוא את פירוט השגיאה ב-<a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"שגיאת גישה ל-YouTube. נא לבדוק את חיבור הרשת שלך או את הגישה שלך לשרת YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"שגיאה ביצירת שידור חי '%1'.<br/>ניתן למצוא את פירוט השגיאה ב-<a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"לא נוצר שידור חי. נא לקשר את חשבונך מחדש.\"\nYouTube.Actions.Error.YouTubeApi=\"שגיאת YouTube API. נא לבדוק בקובץ היומן עבור מידע נוסף.\"\nYouTube.Actions.Error.BroadcastNotFound=\"השידור החי שנבחר לא נמצא.\"\nYouTube.Actions.Error.FileMissing=\"הקובץ לא קיים.\"\nYouTube.Actions.Error.FileOpeningFailed=\"פתיחת הקובץ הנבחר נכשלה\"\nYouTube.Actions.Error.FileTooLarge=\"הקובץ שנבחר גדול מדי (גבול: 2 MiB)\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"המרת השידור נכשלה: %1<br/><br/>אם שגיאה זו נשנית <a href='https://studio.youtube.com/video/%2/livestreaming'>יש לפתוח את השידור ב־YouTube Studio</a> ולנסות ידנית.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"השידור מועבר לשלב הבדיקה, פעולה זו עשויה לארוך זמן מה. נא לנסות שוב בעוד 10‏-30 שניות.\"\nYouTube.Actions.EventsLoading=\"טוען את רשימת האירועים...\"\nYouTube.Actions.EventCreated.Title=\"האירוע נוצר\"\nYouTube.Actions.EventCreated.Text=\"אירוע נוצר בהצלחה.\"\nYouTube.Actions.Stream=\"שידור חי\"\nYouTube.Actions.Stream.ScheduledFor=\"מתוזמן עבור %1\"\nYouTube.Actions.Stream.Resume=\"המשך שידור חי שהופסק\"\nYouTube.Actions.Stream.YTStudio=\"נוצר אוטומטית על ידי YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"יוצר אירוע שידור חי חדש, נא להמתין...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"נדרשת הפעלה ידנית\"\nYouTube.Actions.AutoStartStreamingWarning=\"הפעלה-אוטומטית מבוטלת עבור אירוע זה, יש ללחוץ על \\\"התחל שידור חי\\\" בכדי להתחיל את השידור.\"\nYouTube.Actions.AutoStopStreamingWarning=\"לא יהיה באפשרותך להתחבר מחדש.<br>השידור החי שלך יופסק לחלוטין.\"\nYouTube.Errors.liveStreamingNotEnabled=\"האפשרות לשידור חי לא מופעלת בערוץ ה-YouTube שנבחר.<br/><br/>עבור מידע נוסף <a href='https://www.youtube.com/features'>youtube.com/features</a>\"\nYouTube.Errors.livePermissionBlocked=\"האפשרות לשידור חי לא זמינה בערוץ ה-YouTube שנבחר.<br/>יש לשים לב שלאחר הפעלת שידורים חיים בהגדרות הערוץ, עשויות לעבור עד 24 שעות עד שניתן לשדר ממנו.<br/><br/>עבור מידע נוסף <a href='https://www.youtube.com/features'>youtube.com/features</a>\"\nYouTube.Errors.errorExecutingTransition=\"ההמרה נכשלה עקב שגיאת מנגנון. נא לנסות שוב בעוד כמה שניות.\"\nYouTube.Errors.errorStreamInactive=\"YouTube לא מקבל נתונים לתזרים שלך. נא לבדוק את ההגדרות שלך ולנסות שוב.\"\nYouTube.Errors.invalidTransition=\"ההמרה שניסית לבצע שגויה. יתכן שזה קרה כיוון שהתזרים לא סיים את ההמרה הקודמת. נא להמתין מספר שניות ולנסות שוב.\"\nYouTube.DocksRemoval.Title=\"מחיקת מעגנים מיושנים של דפדפן YouTube\"\nYouTube.DocksRemoval.Text=\"מעגני דפדפן אלה יוסרו כיוון שיצאו משימוש.\\n\\n%1\\nאפשר להשתמש ב„מעגנים/חדר בקרה ל־YouTube חי” במקום.\"\nConfigDownload.WarningMessageTitle=\"אזהרה\"\nFailedToStartStream.MissingConfigURL=\"אין כתובת הגדרות זמינה לשירות הנוכחי\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"לא צוינה כתובת RTMP מותאמת אישית\"\nFailedToStartStream.InvalidCustomConfig=\"הגדרות מותאמות שגויות\"\nFailedToStartStream.MissingCanvas=\"משטח עבודה מוגדר נוסף חסר\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"יצירת שירות וידאו רב־רצועות נכשל\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"יצירת פלט וידאו RTMP רב־רצועות נכשל\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC לא זמין.\\n\\איתור סוג מקודד ‚%1’ נכשל\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"יצירת מקודד שמע ‚%1’ (סוג: ‚%2’) נכשלה\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"משיכת פרטי וידאו OBS בעת יצירת מקודד ‚%1’ (מסוג: ‚%2’) נכשלה\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"יצירת מקודד השמע נכשלה\"\nFailedToStartStream.NoRTMPURLInConfig=\"ההגדרות לא מכילות כתובת יעד תזרים RTMP(S)\"\nFailedToStartStream.FallbackToDefault=\"התחלת ההזרמה באמצעות %1 נכשלה, לנסות שוב בעזרת הגדרות קידוד יחידניות?\"\nFailedToStartStream.ConfigRequestFailed=\"לא ניתן למשוך את ההגדרות מתוך %1<br><br>שגיאת HTTP‏: %2\"\nFailedToStartStream.WarningUnknownStatus=\"התקבל ערך מצב ‚%1’ לא מוכר\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nלהמשיך להזרים בלי %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nלהמשיך להזרים?\"\nFailedToStartStream.MissingEncoderConfigs=\"הגדרות העלייה לאוויר לא כללו הגדרות מקודדים\"\nFailedToStartStream.InvalidEncoderConfig=\"הגדרות עלייה לאוויר כוללות הגדרות מקודד שגויות\"\nFailedToStartStream.StatusMissingHTML=\"בקשת עלייה לאוויר החזירה שגיאה לא ידועה\"\nFailedToStartStream.NoConfigSupplied=\"הגדרות חסרות\"\nMultitrackVideo.Info=\"%1 ממטב את ההגדרות שלך אוטומטית כדי לקודד ולשלוח מגוון איכויות וידאו. בחירה באפשרות הזאת תשלח פרטים אל %2 על תצורת המחשב והתוכנה שלך.\"\nMultitrackVideo.IncompatibleSettings.Title=\"הגדרות לא תואמות\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 לא זמין כרגע עם:\\n\\n%2\\כדי להמשיך להזרים עם %1, יש להשבית הגדרות לא נתמכות:\\n\\n%3\\ולהתחיל להזרים שוב.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"להשבית לתזרים הזה ולהתחיל להזרים/לשדר\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"עדכון ההגדרות והתחלת הזרמה/שידור\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 כרגע לא בתאימות מול [שמע ← כללי ← ערוצים] יש להגדיר ל־‚%2’, %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"יש להגדיר את [שמע ← כללי ← ערוצים] לערך ‚%1’\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 דורש מגוון הגדרות שונות עבור [שמע ← כללי ← ערוצים]\"\nBasic.OpenPluginManager=\"מנהל תוספים\"\nPluginManager=\"מנהל תוספים\"\nPluginManager.Restart=\"צריך להפעיל מחדש את OBS Studio כדי להחיל שינויי תוספים\"\nPluginManager.MissingPlugin=\"[תוסף לא נמצא]\"\nPluginManager.Section.Discover=\"עיון\"\nPluginManager.Section.Manage=\"מותקן\"\nPluginManager.Section.Updates=\"עדכונים\"\nPluginManager.Section.Manage.Title=\"ניהול תוספים פעילים\"\n"
  },
  {
    "path": "frontend/data/locale/hi-IN.ini",
    "content": "Language=\"हिन्दी\"\nOK=\"ठीक है\"\nApply=\"लागू करें\"\nCancel=\"रद्द करें\"\nClose=\"बंद करें\"\nSave=\"सहेजें\"\nDiscard=\"छोड़ें\"\nDisable=\"निष्क्रिय\"\nYes=\"हाँ\"\nNo=\"नहीं\"\nAdd=\"जोड़ें\"\nRemove=\"निकालें\"\nRename=\"नाम बदलें\"\nInteract=\"बातचीत\"\nFilters=\"फिल्टर\"\nProperties=\"विशेषताएं\"\nMoveUp=\"ऊपर ले जाएँ\"\nMoveDown=\"नीचे ले जाएँ\"\nSettings=\"सेटिंग्स\"\nDisplay=\"डिस्प्ले\"\nName=\"नाम\"\nExit=\"निकास\"\nMixer=\"मिक्सर\"\nBrowse=\"ब्राउज करें\"\nMono=\"मोनो\"\nStereo=\"स्टीरियो\"\nDroppedFrames=\"हटाए गए फ़्रेम %1 (%2%)\"\nProjector.Open.Program=\"प्रोग्राम प्रोजेक्टर खोलें\"\nProjector.Open.Preview=\"पूर्वावलोकन प्रोजेक्टर खोलें\"\nProjector.Open.Scene=\"दृश्य प्रोजेक्टर खोलें\"\nProjector.Open.Source=\"स्रोत प्रोजेक्टर खोलें\"\nProjector.Open.Multiview=\"मल्टीव्यू में दिखाएँ\"\nProjector.Display=\"डिस्प्ले: %1\"\nProjector.Window=\"नई विंडो\"\nProjector.Title=\"प्रोजेक्टर\"\nProjector.Title.Scene=\"दृश्य: %1\"\nProjector.Title.Source=\"स्रोत: %1\"\nProjector.Title.Multiview=\"मल्टीव्यू\"\nProjector.ResizeWindowToContent=\"विंडो आकृति सामग्री के अनुसार बनाएઁ\"\nClear=\"साफ़ करें\"\nRevert=\"पहले जैसा करें\"\nShow=\"दिखाएँ\"\nHide=\"छुपायें\"\nUnhideAll=\"कुछ ना छिपाएं\"\nUntitled=\"अनामांकित\"\nNew=\"नया\"\nDuplicate=\"प्रतिलिपि बनाएं\"\nEnable=\"सक्षम करें\"\nDisableOSXVSync=\"macOS V-Sync अक्षम करें\"\nResetOSXVSyncOnExit=\"बंद करते समय macOS V-Sync रिसेट करें\"\nHighResourceUsage=\"संकेतीकरण / एन्कोडिंग अतिभारित हुआ है! विडियो सेटिंग्स न्यून करें या गतिमान संकेतीकरण का चुनाव करें।\"\nTransition=\"संक्रमण\"\nQuickTransitions=\"त्वरित संक्रमण\"\nFadeToBlack=\"काले रंग में लुप्त करें\"\nLeft=\"बाएं\"\nRight=\"दाएँ\"\nTop=\"शीर्ष\"\nBottom=\"नीचे\"\nReset=\"रीसेट\"\nHours=\"घंटे\"\nMinutes=\"मिनट\"\nSeconds=\"सेकंड\"\nDeprecated=\"पदावनत\"\nReplayBuffer=\"फिर से बफर चलाएं\"\nImport=\"आयात\"\nExport=\"निर्यात करें\"\nCopy=\"प्रतिलिपि\"\nPaste=\"चिपकाएँ\"\nPasteReference=\"चिपकाएं (सन्दर्भ)\"\nPasteDuplicate=\"चिपकाएं (प्रतिरूप)\"\nRemuxRecordings=\"रिमक्स रिकॉर्डिंग्स\"\nNext=\"अगले\"\nBack=\"वापस\"\nDefaults=\"पूर्व निर्धारित\"\nRestoreDefaults=\"आरंभिक\"\nHideMixer=\"मिक्सर में छिपायें\"\nTransitionOverride=\"संक्रमण सद्द करें\"\nShowTransition=\"संक्रांति दिखाएं\"\nHideTransition=\"संक्रांति छुपाएं\"\nNone=\"कुछ नहीं\"\nStudioMode.Preview=\"पूर्वावलोकन\"\nStudioMode.Program=\"कार्यक्रम\"\nStudioMode.PreviewSceneName=\"पूर्वावलोकन : %1\"\nStudioMode.ProgramSceneName=\"कार्यक्रम : %1\"\nShowInMultiview=\"मल्टीव्यू में दिखाएं\"\nVerticalLayout=\"लंबरूप लेआउट\"\nGroup=\"एकत्रित करना\"\nDoNotShowAgain=\"फिर से मत दिखाना\"\nDefault=\"(डिफ़ॉल्ट)\"\nCalculating=\"गणना जारी है\"\nFullscreen=\"पूर्ण पटल पर दिखाएं\"\nWindowed=\"चौकट में दिखाएं\"\nRefreshBrowser=\"ताज़ा करें\"\nAspectRatio=\"आकृति अनुपात <b>%1:%2</b>\"\nLockVolume=\"ध्वनि की तीव्रता को लॉक करें\"\nLogViewer=\"लॉग देखने वाला\"\nShowOnStartup=\"स्टार्टअप पर दिखाएँ\"\nOpenFile=\"फ़ाइल खोलें\"\nAddScene=\"दृश्य जोड़ें\"\nAddSource=\"स्रोत जोड़ें\"\nRemoveScene=\"चयनित दृश्य को हटाएँ\"\nRemoveSource=\"चयनित स्रोत(तों) को हटाएँ\"\nMoveSceneUp=\"दृश्य ऊपर ले जाएँ\"\nMoveSceneDown=\"दृश्य नीचे ले जाएँ\"\nMoveSourceUp=\"स्रोत(तों) को ऊपर ले जाएँ\"\nMoveSourceDown=\"स्रोत(तों) को नीचे ले जाएँ\"\nSourceProperties=\"स्रोत गुण खोलें\"\nSourceFilters=\"स्रोत फ़िल्टर खोलें\"\nMixerToolbarMenu=\"ऑडियो मिक्सर मेनू\"\nSceneFilters=\"दृश्य फ़िल्टर खोलें\"\nList=\"सूची\"\nGrid=\"ग्रिड\"\nAutomatic=\"स्वचालित\"\nComingSoon=\"जल्द आ रहा है\"\nPluginsFailedToLoad.Title=\"प्लगइन लोड त्रुटि\"\nPluginsFailedToLoad.Text=\"निम्‍न OBS प्‍लग इन लोड होने में विफल :\\n\\n%1\\nकृपया इन प्‍लग इन को अपडेट करें या निकालें.\"\nAlreadyRunning.Title=\"OBS चल रहा है\"\nAlreadyRunning.Text=\"OBS पहले से ही चल रहा है! अगर आप ऐसा नहीं करना चाहते हैं, कृपया एक नया उदाहरण चलाने की कोशिश करने से पहले OBS के किसी भी मौजूदा उदाहरण को बंद कर दें। यदि आपके पास सिस्टम ट्रे को कम करने के लिए OBS सेट है, तो कृपया जांचें कि क्या यह अभी भी वहां चल रहा है।\"\nAlreadyRunning.LaunchAnyway=\"फिर भी लॉन्च करें\"\nCrashHandling.Dialog.Title=\"OBS स्टूडियो क्रैश हुआ है\"\nCrashHandling.Labels.Text=\"आपके पिछले सत्र में OBS ठीक से बंद नहीं हुआ.\\n\\nक्या आप सुरक्षित मोड में चलाना चाहेंगे (तृतीय-पक्ष प्लगइन्स, स्क्रिप्टिंग और वेबसॉकेट्स अक्षम)?\"\nCrashHandling.Labels.PrivacyNotice=\"आप OBSProject पर नवीनतम क्रैश रिपोर्ट को स्वचालित रूप से अपलोड करना भी चुन सकते हैं.<br /><br />आप किसी भी फ़ाइल अपलोड करने से पहले कृपया <a href='https://obsproject.com/privacy-policy'> गोपनीयता नीति</a> पढ़ें  तथा फ़ाइल अपलोड करने से संबंधित भागों पर विशेष ध्यान दें.\"\nCrashHandling.Checkbox.SendReport=\"मैंने गोपनीयता नीति और अपलोड के लिए सहमति पढ़ी है.\"\nCrashHandling.Buttons.LaunchSafe=\"सुरक्षित मोड में चलाएं\"\nCrashHandling.Buttons.LaunchNormal=\"सामान्य मोड में चलाएं\"\nCrashHandling.Errors.UploadJSONError=\"सबसे हालिया क्रैश लॉग अपलोड करने का प्रयास करते समय एक त्रुटि हुई. कृपया बाद में फिर से प्रयास करें.\"\nCrashHandling.Errors.Title=\"क्रैश लॉग अपलोड त्रुटि\"\nSafeMode.Restart=\"क्या आप ओबीएस को सुरक्षित मोड में पुनः आरंभ करना चाहते हैं (तृतीय-पक्ष प्लगइन्स, स्क्रिप्टिंग और वेबसॉकेट्स अक्षम)?\"\nSafeMode.RestartNormal=\"क्या आप ओबीएस को सामान्य मोड में पुनः आरंभ करना चाहते हैं?\"\nChromeOS.Title=\"असमर्थित प्लेटफार्म\"\nChromeOS.Text=\"OBS ChromeOS कंटेनर के अंदर चलता हुआ दिखाई देता है. यह प्लेटफार्म असमर्थित है.\"\nWine.Title=\"Wine उपलब्थ है\"\nWine.Text=\"Wine में OBS चलाना समर्थित नहीं है, और कई सुविधाएं जैसे कैप्चर या डिवाइस स्रोत काम नहीं करेंगे या केवल सीमित क्षमता में काम करेंगे।<br><br>इसके बजाय OBS के मूल संस्करण को चलाना अनुशंषित है, उदाहरण के लिए <a href='https://flathub.org/apps/details/com.obsproject.Studio'>हमारा फ़्लैटपैक संस्करण</a> या आपके ऑपरेटिंग सिस्टम के पैकेज.\"\nDockCloseWarning.Title=\"डॉक करने योग्य विंडो बंद कर रहे हैं\"\nDockCloseWarning.Text=\"आपने अभी-अभी एक डॉक करने योग्य विंडो बंद की है। यदि आप इसे फिर से दिखाना चाहते हैं, तो मेनू बार पर डॉक्स मेनू का उपयोग करें।\"\nExtraBrowsers=\"कस्टम ब्राउज़र डॉक्स\"\nExtraBrowsers.Info=\"एक नाम और URL देकर डॉक्स जोड़ें, फिर डॉक्स खोलने के लिए लागू या बंद करें पर क्लिक करें. आप किसी भी समय डॉक जोड़ या हटा सकते हैं.\"\nExtraBrowsers.DockName=\"डॉक का नाम\"\nAuth.Authing.Title=\"सत्यापित कर रहे हैं...\"\nAuth.Authing.Text=\"%1 के साथ सत्यापन, कृपया प्रतीक्षा करें ...\"\nAuth.AuthFailure.Title=\"सत्यापन विफलता\"\nAuth.AuthFailure.Text=\"%1 के साथ सत्यापित करने में विफल :\\n\\n%2:%3\"\nAuth.InvalidScope.Title=\"सत्यापन अपेक्षित\"\nAuth.InvalidScope.Text=\"%1 के लिए सत्यापन आवश्यकताएँ बदल गई हैं. कुछ सुविधाएं अनुपलब्ध हो सकती हैं.\"\nAuth.LoadingChannel.Title=\"चैनल जानकारी लोड हो रही है...\"\nAuth.LoadingChannel.Text=\"%1 के लिए चैनल जानकारी लोड हो रही है, कृपया प्रतीक्षा करें...\"\nAuth.LoadingChannel.Error=\"चैनल की जानकारी नहीं मिल पा रही है.\"\nAuth.ChannelFailure.Title=\"चैनल को लोड करने में विफल रहा\"\nAuth.ChannelFailure.Text=\"%1\\n\\n%2: %3 के लिए चैनल जानकारी लोड करने में विफल\"\nAuth.Chat=\"बातचीत\"\nAuth.StreamInfo=\"स्ट्रीम जानकारी\"\nTwitchAuth.Stats=\"Twitch के आँकड़े\"\nTwitchAuth.Feed=\"Twitch गतिविधि फ़ीड\"\nTwitchAuth.TwoFactorFail.Title=\"स्ट्रीम कुंजी प्राप्त नहीं कर सका\"\nTwitchAuth.TwoFactorFail.Text=\"OBS आपके Twitch खाते से जुड़ने में असमर्थ रहा. कृपया सुनिश्चित करें कि द्वि-गुणक सत्यापन आपके <a href='https://www.twitch.tv/settings/security'> Twitch सुरक्षा सेटिंग्स </a> में सेट किया गया है क्योंकि स्ट्रीम करने के लिए यह आवश्यक है.\"\nRestreamAuth.Channels=\"चैनल पुन: प्रसारित करें\"\nCopy.Filters=\"फ़िल्टर प्रतिलिपि बनाएं\"\nPaste.Filters=\"फ़िल्टर चिपकाएं\"\nBrowserPanelInit.Title=\"ब्राउज़र का श्रीगणेश...\"\nBrowserPanelInit.Text=\"ब्राउज़र का प्रारंभ हो रहा है, कृपया प्रतीक्षा करें...\"\nBandwidthTest.Region=\"क्षेत्र\"\nBandwidthTest.Region.US=\"संयुक्त राज्य अमेरिका\"\nBandwidthTest.Region.EU=\"यूरोप\"\nBandwidthTest.Region.Asia=\"एशिया\"\nBandwidthTest.Region.Other=\"अन्य\"\nBasic.AutoConfig=\"ऑटो-कॉन्फ़िगरेशन विज़ार्ड\"\nBasic.AutoConfig.ApplySettings=\"सेटिंग लागू करें\"\nBasic.AutoConfig.StartPage=\"उपयोग की जानकारी\"\nBasic.AutoConfig.StartPage.SubTitle=\"किस प्रोग्राम के लिए उपयोग करना चाहते हैं निर्दिष्ट करें\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"स्ट्रीमिंग के लिए ऑप्टिमाइज़ करना, रिकॉर्डिंग अप्रधान है\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"केवल रिकॉर्डिंग के लिए ऑप्टिमाइज़ करें, मैं स्ट्रीमिंग नहीं करूंगा\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"मैं केवल आभासी कैमरे का उपयोग करूंगा\"\nBasic.AutoConfig.VideoPage=\"वीडियो सेटिंग्स\"\nBasic.AutoConfig.VideoPage.SubTitle=\"उन वीडियो सेटिंग्स को निर्दिष्ट करें जिनका आप उपयोग करना चाहते हैं\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"वर्तमान (%1x%2) का प्रयोग करें\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"%1 प्रदर्शित (%2x%3) करें\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"वर्तमान (%1) का प्रयोग करें\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"या तो 60 या 30, लेकिन जब संभव हो 60 पसंद करें\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"या तो 60 या 30, लेकिन उच्च रिजोल्यूशन पसंद करें\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"नोट : कैनवास (बेस) रिज़ॉल्यूशन जरूरी नहीं है कि आप जिस रिज़ॉल्यूशन को स्ट्रीम करेंगे या रिकॉर्ड करेंगे, वही होगा. संसाधन उपयोग या बिटरेट आवश्यकताओं को कम करने के लिए आपके वास्तविक स्ट्रीम/ रिकॉर्डिंग रिज़ॉल्यूशन को कैनवास रिज़ॉल्यूशन से घटाया जा सकता है.\"\nBasic.AutoConfig.StreamPage=\"स्ट्रीम जानकारी\"\nBasic.AutoConfig.StreamPage.SubTitle=\"कृपया अपनी stream सर्वर जानकारी दर्ज करें:\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"खाता जोड़ें (सिफारिश/संस्तुत)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"खाता डिस्कनेक्ट करें\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"खाता डिस्कनेक्ट करें ?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"यह बदलाव तुरंत लागू होगा. क्या आप वाकई अपना खाता डिस्कनेक्ट करना चाहते हैं?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"स्ट्रीम कुंजी प्राप्त करें\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"और जानें\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"स्ट्रीम कुंजी प्रयोग करें\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"स्ट्रीम कुंजी का उपयोग करें (उन्नत)\"\nBasic.AutoConfig.StreamPage.Service=\"सेवा\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"सब दिखाओ\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"अनुकूलित करें\"\nBasic.AutoConfig.StreamPage.Server=\"सर्वर\"\nBasic.AutoConfig.StreamPage.StreamKey=\"स्ट्रीमिंग चाबी\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: एन्क्रिप्शन पासफ़्रेज़ डालें.\\nRTMP: सेवा द्वारा प्रदान की गई कुंजी डालें.\\nSRT: यदि सेवा किसी स्ट्रीम ID का उपयोग करती है तो उसे यहाँ लिखें.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"एनकोडर कुंजी\"\nBasic.AutoConfig.StreamPage.BearerToken=\"बियरर टोकन\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"जुड़ा हुआ खाता\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"बैंडविड्थ परीक्षण कर बिटरेट का अनुमान लगाएं (कुछ मिनट लग सकते हैं)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"हार्डवेयर एन्कोडिंग को प्राथमिकता दें\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"हार्डवेयर एन्कोडिंग अधिकांश CPU उपयोग को समाप्त करता है, लेकिन गुणवत्ता के समान स्तर को प्राप्त करने के लिए अधिक बिटरेट की आवश्यकता हो सकती है.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"स्ट्रीम चेतावनी\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"बैंडविड्थ परीक्षण आपके चैनल के लिए ऑडियो के बिना यादृच्छिक वीडियो डेटा को स्ट्रीम करने वाला है. यदि आप सक्षम हैं, तो स्ट्रीमिंग वीडियो को सहेजना अस्थायी रूप से बंद करना ठीक होगा और परीक्षण पूरा तक स्ट्रीम को निजी सेट करना अच्छा रहेगा. आगे बढ़ें?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"टेस्ट %1\"\nBasic.AutoConfig.TestPage=\"अंतिम परिणाम\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"आदर्श सेटिंग्स का अनुमान लगाने के लिए कार्यक्रम अब परीक्षणों का एक सेट निष्पादित कर रहा है\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"परीक्षण पूर्ण\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"बैंडविड्थ परीक्षण हो रहा है, इसमें कुछ मिनट लग सकते हैं...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"इस सेवा के प्रोटोकॉल के लिए कोई आउटपुट नहीं मिला\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"इनसे जुड़ रहे हैं : %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"किसी भी सर्वर से जुड़ने में विफल, कृपया अपना इंटरनेट कनेक्शन जांचें और पुन: प्रयास करें.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"बैंडविड्थ परीक्षण : %1 के लिए\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"स्ट्रीम एनकोडर का परीक्षण, इसमें एक मिनट लग सकता है...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"रिकॉर्डिंग एन्कोडर का परीक्षण, इसमें एक मिनट लग सकता है ...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"एनकोडर शुरू करने में विफल\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"परीक्षण कर रहे हैं %1x%2%3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"स्ट्रीमिंग एनकोडर\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"रिकॉर्डिंग एनकोडर\"\nBasic.AutoConfig.TestPage.Result.Header=\"इस प्रोग्राम ने निर्धारित किया है कि ये अनुमानित सेटिंग्स आपके लिए आदर्श हैं :\"\nBasic.AutoConfig.TestPage.Result.Footer=\"इन सेटिंग्स का उपयोग करने के लिए, सेटिंग्स लागू करें पर क्लिक करें. विज़ार्ड को पुन: कॉन्फ़िगर करने और पुन: प्रयास करने के लिए, पीछे जाएं पर क्लिक करें. सेटिंग्स को स्वयं कॉन्फ़िगर करने के लिए, रद्द करें पर क्लिक करें और सेटिंग्स खोलें.\"\nBasic.AutoConfig.Info=\"ऑटो-कॉन्फ़िगरेशन विज़ार्ड आपके कंप्यूटर विशेष विवरण और इंटरनेट की गति के आधार पर सर्वोत्तम सेटिंग्स निर्धारित करेगा.\"\nBasic.AutoConfig.RunAnytime=\"इसे कभी भी टूल्स मेनू में जाकर चला सकते हैं.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"स्ट्रीमिंग (स्केल्ड) रिज़ॉल्यूशन\"\nBasic.Stats=\"आंकड़े\"\nBasic.Stats.CPUUsage=\"सि पि यु का उपयोग\"\nBasic.Stats.HDDSpaceAvailable=\"डिस्क में जगह उपलब्ध है\"\nBasic.Stats.MemoryUsage=\"मेमोरी उपयोग\"\nBasic.Stats.AverageTimeToRender=\"फ्रेम रेंडर करने के लिए औसत समय\"\nBasic.Stats.SkippedFrames=\"एन्कोडिंग विलंब के कारण फ्रेमों को छोड़ा गया\"\nBasic.Stats.MissedFrames=\"कई फ्रेम रेंडरिेंग विलंब के कारण छूट गए\"\nBasic.Stats.Output.Stream=\"स्ट्रीम\"\nBasic.Stats.Output.Recording=\"रिकॉर्डिंग\"\nBasic.Stats.Status=\"स्थिति\"\nBasic.Stats.Status.Recording=\"रिकॉर्डिंग\"\nBasic.Stats.Status.Live=\"लाइव\"\nBasic.Stats.Status.Reconnecting=\"पुन: जुड़ने का प्रयास\"\nBasic.Stats.Status.Inactive=\"निष्क्रिय\"\nBasic.Stats.Status.Active=\"सक्रिय\"\nBasic.Stats.DroppedFrames=\"छूटे फ्रेम्स (नेटवर्क)\"\nBasic.Stats.MegabytesSent=\"कुल डेटा आउटपुट\"\nBasic.Stats.Bitrate=\"बिटरेट\"\nBasic.Stats.DiskFullIn=\"डिस्क पूर्ण (लगभग)\"\nBasic.Stats.DiskFullIn.Text=\"%1 घंटे, %2 मिनट\"\nBasic.Stats.ResetStats=\"आँकड़े रीसेट करें\"\nResetUIWarning.Title=\"कृपया सुनिश्चित करें कि आप UI रीसेट करना चाहते हैं?\"\nResetUIWarning.Text=\"UI को रीसेट करने से अतिरिक्त डॉक छिप जाएंगे। अगर आप चाहते हैं कि ये डॉक दिखाई दें, तो आपको इन डॉक को डॉक्स मेनू से दिखाना होगा.\\n\\nक्या आप वाकई UI को रीसेट करना चाहते हैं?\"\nUpdater.Title=\"नया अपडेट उपलब्ध है\"\nUpdater.Text=\"एक नया संस्करण उपलब्ध है :\"\nUpdater.UpdateNow=\"अभी अपडेट करें\"\nUpdater.RemindMeLater=\"मुझे बाद में याद दिलाएं\"\nUpdater.Skip=\"इस संस्करण को छोड़ें\"\nUpdater.NoUpdatesAvailable.Title=\"कोई अपडेट उपलब्ध नहीं\"\nUpdater.NoUpdatesAvailable.Text=\"अभी कोई अपडेट उपलब्ध नहीं है\"\nUpdater.BranchNotFound.Title=\"अद्यतन चैनल हटा दिया गया\"\nUpdater.BranchNotFound.Text=\"आपका चयनित अद्यतन चैनल अब उपलब्ध नहीं है, OBS को डिफ़ॉल्ट पर रीसेट कर दिया गया है.\"\nUpdater.RepairButUpdatesAvailable.Title=\"अखंडता जाँच अनुपलब्ध\"\nUpdater.RepairButUpdatesAvailable.Text=\"फ़ाइल की अखंडता की जाँच केवल उपलब्ध नवीनतम संस्करण के लिए ही संभव है. अपने OBS इंस्टालेशन को सत्यापित और अपडेट करने के लिए मदद → अपडेट्स के लिए देखें का उपयोग करें.\"\nUpdater.RepairConfirm.Title=\"अखंडता जाँच की पुष्टि करें\"\nUpdater.RepairConfirm.Text=\"अखंडता जाँच शुरू हो रही है जिसमें त्रुतियुक्त फ़ाइलों के लिए आपके ओबीएस इंस्टॉलेशन को स्कैन किया जाएगा और टूटी/संशोधित फाइलों को फिर से डाउनलोड किया जाएगा. इसमें कुछ समय लग सकता है.\\n\\nक्या आप आगे बढ़ना चाहते हैं?\"\nUpdater.FailedToLaunch=\"अपडेटर शुरू करने में विफल हुआ\"\nQuickTransitions.SwapScenes=\"स्वैप पूर्वावलोकन/ संक्रांति के बाद प्रोग्राम दृश्य\"\nQuickTransitions.SwapScenesTT=\"संक्रांति के बाद पूर्वावलोकन और आउटपुट दृश्यों को अदल-बदल करता है (यदि प्रोग्राम का मूल दृश्य अभी भी मौजूद है).\\nयह प्रोग्राम के मूल दृश्य में किए गए किसी भी परिवर्तन को पूर्ववत नहीं करेगा.\"\nQuickTransitions.DuplicateScene=\"डुप्लिकेट दृश्य\"\nQuickTransitions.DuplicateSceneTT=\"एक ही दृश्य को संपादित करते समय, प्रोग्राम आउटपुट को संशोधित किए बिना स्रोतों के रूपांतर / दृश्यता को बदलने की अनुमति देता है.\\nप्रोग्राम आउटपुट को संशोधित किए बिना स्रोतों के गुणों को संपादित करने के लिए 'डुप्लिकेट स्रोतों' को सक्षम करें.\\nइस मूल्य को बदलने से वर्तमान प्रोग्राम दृश्य रीसेट हो जाएगा (यदि यह अभी भी मौजूद है).\"\nQuickTransitions.EditProperties=\"डुप्लिकेट स्रोत\"\nQuickTransitions.EditPropertiesTT=\"एक ही दृश्य को संपादित करते समय, प्रोग्राम आउटपुट को संशोधित किए बिना स्रोतों के गुणों के संपादन की अनुमति देता है.\\nइसका केवल तभी उपयोग किया जा सकता है जब 'डुप्लिकेट दृश्य' सक्षम हो.\\nकुछ स्रोत (जैसे कैप्चर या मीडिया स्रोत) इसका समर्थन नहीं करते हैं और इसे अलग से संपादित नहीं किया जा सकता है.\\nइस मान को बदलने से वर्तमान प्रोग्राम दृश्य (यदि यह अभी भी मौजूद है) रीसेट हो जाएगा.\\n\\nचेतावनी : क्योंकि स्रोतों का डुप्लीकेट बनेगा, इसके लिए अतिरिक्त सिस्टम या वीडियो संसाधनों की आवश्यकता हो सकती है.\"\nQuickTransitions.HotkeyName=\"त्वरित संक्रांति: %1\"\nBasic.AddTransition=\"विन्यासयोग्य संक्रांति जोड़ें\"\nBasic.RemoveTransition=\"विन्यासयोग्य संक्रांति हटाएं\"\nBasic.TransitionProperties=\"संक्रांति गुण\"\nBasic.SceneTransitions=\"दृश्य संक्रांति\"\nBasic.TransitionDuration=\"अंतराल\"\nBasic.TogglePreviewProgramMode=\"स्टूडियो मोड\"\nBasic.EnablePreviewProgramMode=\"स्टूडियो मोड सक्षम करें\"\nBasic.DisablePreviewProgramMode=\"स्टूडियो मोड अक्षम करें\"\nUndo.Undo=\"पूर्ववत\"\nUndo.Redo=\"दुहराएं\"\nUndo.Add=\"जोड़ें '%1'\"\nUndo.Delete=\"मिटाएं '%1'\"\nUndo.Rename=\"'%1' को नया नाम दें\"\nUndo.SceneCollection.Switch=\"'%1' पर जाएं\"\nUndo.Item.Undo=\"%1 पूर्ववत\"\nUndo.Item.Redo=\"%1 दुहराएं\"\nUndo.Sources.Multi=\"%1 स्रोत मिटाएं\"\nUndo.Filters=\"'%1' पर फिल्टर बदलता है\"\nUndo.Filters.Paste.Single=\"फ़िल्टर '%1' से '%2' में चिपकाएं\"\nUndo.Filters.Paste.Multiple=\"'%1' से '%2' में फ़िल्टर्स कॉपी करें\"\nUndo.Transform=\"'%1' में स्रोत(तों) को रूपांतरित करें\"\nUndo.Transform.Paste=\"'%1' में रूपांतरण डालें\"\nUndo.Transform.Rotate=\"'%1' में घुमाव\"\nUndo.Transform.Reset=\"'%1' में रूपांतर रीसेट करें\"\nUndo.Transform.HFlip=\"'%1' में क्षैतिज पलट\"\nUndo.Transform.VFlip=\"'%1' में लंबवत पलट\"\nUndo.Transform.FitToScreen=\"'%1' को स्क्रीन में अँटने दें\"\nUndo.Transform.StretchToScreen=\"'%1' को स्क्रीन में फैला दें\"\nUndo.Transform.Center=\"'%1' को स्क्रीन के केंद्र में करें\"\nUndo.Transform.VCenter=\"'%1' में स्क्रीन के लंबवत केंद्रित करें\"\nUndo.Transform.HCenter=\"'%1' में स्क्रीन के समतल केंद्र में करें\"\nUndo.Volume.Change=\"'%1' में वॉल्यूम परिवर्तन\"\nUndo.Volume.Mute=\"'%1' मौन करें\"\nUndo.Volume.Unmute=\"'%1' मुखर करें\"\nUndo.Balance.Change=\"'%1' पर ऑडियो संतुलन बदलें\"\nUndo.SyncOffset.Change=\"'%1' पर ऑडियो सिंक ऑफ़सेट बदलें\"\nUndo.MonitoringType.Change=\"'%1' पर ऑडियो मॉनिटरिंग बदलें\"\nUndo.Mixers.Change=\"'%1' पर ऑडियो मिश्रक बदलें\"\nUndo.ForceMono.On=\"'%1' पर मोनो बाध्य करना सक्रिय करें\"\nUndo.ForceMono.Off=\"'%1' पर मोनो बाध्य करना निष्क्रिय करें\"\nUndo.Properties=\"'%1' में गुण परिवर्तन\"\nUndo.Scene.Duplicate=\"'%1' दृश्य की प्रतिलिपि बनाएं\"\nUndo.ShowTransition=\"'%1' में संक्रांति दिखाएं\"\nUndo.HideTransition=\"'%1' में छुपाना संक्रांति\"\nUndo.ShowSceneItem=\"'%1' को '%2' में दिखाएं\"\nUndo.HideSceneItem=\"'%1' को '%2' में छुपाएं\"\nUndo.ReorderSources=\"'%1' में स्रोतों को पुन: व्यवस्थित करें\"\nUndo.MoveUp=\"'%1' को '%2' में ऊपर ले जाएं\"\nUndo.MoveDown=\"'%1' को '%2' में नीचे ले जाएं\"\nUndo.MoveToTop=\"'%1' को '%2' में शिखर पर ले जाएं\"\nUndo.MoveToBottom=\"'%1' को '%2' में तल पर ले जाएं\"\nUndo.PasteSource=\"'%1' में स्रोत(तों) को चिपकाएं\"\nUndo.PasteSourceRef=\"'%1' में स्रोत संदर्भ(र्भों) को चिपकाएं\"\nUndo.GroupItems=\"'%1' में चीजों को संगठित करें\"\nTransitionNameDlg.Text=\"कृपया संक्रांति का नाम लिखें\"\nTransitionNameDlg.Title=\"संक्रांति का नाम\"\nTitleBar.SafeMode=\"सुरक्षित मोड\"\nTitleBar.PortableMode=\"पोर्टेबल मोड\"\nTitleBar.Profile=\"प्रोफ़ाइल\"\nTitleBar.Scenes=\"दृश्य\"\nNameExists.Title=\"नाम पहले से विद्यमान है\"\nNameExists.Text=\"यह नाम पहले ही प्रयोग में है\"\nNoNameEntered.Title=\"कृपया कोई मान्य नाम लिखें\"\nNoNameEntered.Text=\"आप नाम का स्थान खाली नहीं छोड़ सकते.\"\nConfirmStart.Title=\"स्ट्रीम आरंभ करें?\"\nConfirmStart.Text=\"क्या आप स्ट्रीम आरंभ करना चाहते हैं?\"\nConfirmStop.Title=\"स्ट्रीम रोक दें?\"\nConfirmStop.Text=\"क्या आप स्ट्रीम को रोकना चाहते हैं?\"\nConfirmStopRecord.Title=\"रिकॉर्डिंग रोक दें?\"\nConfirmStopRecord.Text=\"क्या आप रिकॉर्डिंग को रोकना चाहते हैं?\"\nConfirmBWTest.Title=\"बैंडविड्थ टेस्ट आरंभ करें?\"\nConfirmBWTest.Text=\"आपका OBS बैंडविड्थ परीक्षण मोड में कॉन्फ़िगर है. यह मोड आपके चैनल को लाइव किए बिना नेटवर्क टेस्टिंग की अनुमति देता है. एक बार जब आप परीक्षण कर लेते हैं, तो आपको दर्शकों को अपनी स्ट्रीम देखने में सक्षम करने के लिए इसे निष्क्रिय करना होगा.\\n\\nक्या आप आगे जाना चाहते हैं?\"\nConfirmExit.Title=\"सक्रिय आउटपुट\"\nConfirmExit.Text=\"OBS अभी भी सक्रिय है. सभी सक्रिय स्ट्रीम/रिकॉर्डिंग बंद हो जाएंगे.\"\nConfirmRemove.Title=\"हटाने की संपुष्टि\"\nConfirmRemove.Text=\"क्या आप सचमुच '%1' को हटाना चाहते हैं?\"\nConfirmRemove.TextMultiple=\"क्या आप सचमुच %1 को हटाना चाहते हैं?\"\nConfirmReset.Title=\"गुण रीसेट करें\"\nConfirmReset.Text=\"क्या आप सचमुच मौजूदा गुणों को उनके डिफ़ॉल्ट पर रीसेट करना चाहते हैं?\"\nOutput.StartStreamFailed=\"स्ट्रीम आरंभ करने में विफल\"\nOutput.StartRecordingFailed=\"रिकॉर्डिंग आरंभ करने में विफल\"\nOutput.StartReplayFailed=\"रिप्ले बफ़र आरंभ करने में विफल\"\nOutput.StartVirtualCamFailed=\"वर्चुअल कैमरा प्रारंभ करने में विफल\"\nOutput.StartFailedGeneric=\"आउटपुट प्रारंभ करना विफल रहा. कृपया विवरण के लिए लॉग देखें.\\n\\nनोट: यदि आप NVENC या AMD एन्कोडर का उपयोग कर रहे हैं, तो सुनिश्चित करें कि आपके वीडियो ड्राइवर अद्यतित हैं.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"रुके हुए रिप्ले सहेजे नहीं जा सकते\"\nOutput.ReplayBuffer.PauseWarning.Text=\"चेतावनी: रिकॉर्डिंग ठहरे होने पर रिप्ले सहेजे नहीं जा सकते.\"\nOutput.ConnectFail.Title=\"जुड़ने में विफल\"\nOutput.ConnectFail.BadPath=\"अमान्य पथ या कनेक्शन URL. कृपया पुष्टि करने के लिए अपनी सेटिंग्स जांचें कि वे मान्य हैं.\"\nOutput.ConnectFail.ConnectFailed=\"सर्वर से कनेक्ट करने में विफल\"\nOutput.ConnectFail.InvalidStream=\"निर्दिष्ट चैनल या स्ट्रीम कुंजी तक नहीं पहुंच सका, कृपया अपनी स्ट्रीम कुंजी की दोबारा जांच करें. यदि यह सही है, तो सर्वर से कनेक्ट होने में समस्या हो सकती है.\"\nOutput.ConnectFail.HdrDisabled=\"HDR आउटपुट वर्तमान में इस आउटपुट के लिए अक्षम है.\"\nOutput.ConnectFail.Error=\"सर्वर से कनेक्ट करने का प्रयास करते समय कोई अनपेक्षित त्रुटि उत्पन्न हुई. अधिक जानकारी लॉग फ़ाईल में देखें.\"\nOutput.ConnectFail.Disconnected=\"सर्वर से डिस्कनेक्टेड है\"\nOutput.StreamEncodeError.Title=\"एन्कोडिंग त्रुटि\"\nOutput.StreamEncodeError.Msg=\"रिकॉर्डिंग के समय एक एनकोडर त्रुटि हुई.\"\nOutput.StreamEncodeError.Msg.LastError=\"स्ट्रीमिंग के समय एक एनकोडर त्रुटि हुई :<br><br>%1\"\nOutput.RecordFail.Title=\"रिकॉर्डिंग आरंभ करने में विफल\"\nOutput.RecordFail.Unsupported=\"आउटपुट स्वरूप या तो असमर्थित है या एक से अधिक ऑडियो ट्रैक का समर्थन नहीं करता है. कृपया अपनी सेटिंग देखें और पुनः प्रयास करें.\"\nOutput.RecordNoSpace.Title=\"डिस्क में स्थान अपर्याप्त है\"\nOutput.RecordNoSpace.Msg=\"रिकॉर्डिंग जारी रखने के लिए पर्याप्त डिस्क स्थान नहीं है.\"\nOutput.RecordError.Title=\"रिकॉर्डिंग त्रुटि\"\nOutput.RecordError.Msg=\"रिकॉर्डिंग करते समय एक अनिर्दिष्ट त्रुटि हुई.\"\nOutput.RecordError.EncodeErrorMsg=\"रिकॉर्डिंग करते समय एक एनकोडर त्रुटि हुई.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"रिकॉर्डिंग के समय एक एनकोडर त्रुटि हुई :<br><br>%1\"\nOutput.BadPath.Title=\"अमान्य फ़ाइल पथ\"\nOutput.BadPath.Text=\"निर्धारित रिकॉर्डिंग पथ खोला नहीं जा सका. कृपया सेटिंग → आउटपुट → रिकॉर्डिंग के अंतर्गत अपना रिकॉर्डिंग पथ जाँचें.\"\nOutput.NoBroadcast.Title=\"कोई प्रसारण कॉन्फ़िगरर्ड नहीं है\"\nOutput.NoBroadcast.Text=\"स्ट्रीमिंग शुरू करने से पहले आपको एक प्रसारण सेट करना होगा.\"\nOutput.BroadcastStartFailed=\"प्रसारण आरंभ करने में विफल\"\nOutput.BroadcastStopFailed=\"प्रसारण का अंत करने में विफल\"\nLogUploadDialog.Title=\"OBS Studio लॉग फ़ाइल अपलोड\"\nLogUploadDialog.Labels.PrivacyNotice=\"कृपया कोई भी फ़ाइल अपलोड करने से पहले <a href='https://obsproject.com/privacy-policy'>गोपनीयता नीति</a> और फ़ाइल अपलोड से संबंधित उसका अनुभाग पढ़ें।\"\nLogUploadDialog.Labels.Progress=\"लॉग अपलोड प्रगति पर है। कृपया प्रतीक्षा करें...\"\nLogUploadDialog.Labels.Description.AppLog=\"आपकी लॉग फ़ाइल अपलोड कर दी गई है। अब आप डीबगिंग या सहायता के लिए URL साझा कर सकते हैं।\"\nLogUploadDialog.Labels.Description.CrashLog=\"आपकी क्रैश रिपोर्ट अपलोड कर दी गई है। अब आप डीबगिंग के लिए URL शेयर कर सकते हैं।\"\nLogUploadDialog.Buttons.ConfirmUpload=\"अपलोड\"\nLogUploadDialog.Buttons.CopyURL=\"URL की प्रतिलिपि बनाएं\"\nLogUploadDialog.Buttons.AnalyzeURL=\"लॉग फ़ाइल का विश्लेषण करें\"\nLogUploadDialog.Buttons.RetryButton=\"दुबारा कोशिश करें\"\nLogUploadDialog.Errors.Template=\"फ़ाइल अपलोड करते समय एक त्रुटि हुई:\\n\\n%1\"\nRemux.SourceFile=\"OBS रिकॉर्डिंग\"\nRemux.TargetFile=\"लक्ष्य फाइल\"\nRemux.Remux=\"रीमक्स\"\nRemux.Stop=\"रीमक्सिंग रोकें\"\nRemux.ClearFinished=\"संपूर्ण वस्तुओं की सफाई करें\"\nRemux.ClearAll=\"सभी वस्तुओं की सफाई करें\"\nRemux.OBSRecording=\"OBS रिकॉर्डिंग\"\nRemux.FinishedTitle=\"रीमक्सिंग समाप्त\"\nRemux.Finished=\"रिकॉर्डिंग रीमक्स हो गया\"\nRemux.FinishedError=\"रिकॉर्डिंग रीमक्स की गई, लेकिन फ़ाइल अधूरी हो सकती है\"\nRemux.SelectRecording=\"OBS रिकॉर्डिंग चुनें...\"\nRemux.SelectTarget=\"लक्ष्य फ़ाइल चुनें...\"\nRemux.FileExistsTitle=\"लक्ष्य फ़ाइलें मौजूद हैं\"\nRemux.FileExists=\"निम्न लक्ष्य फ़ाइलें पहले से मौजूद हैं. क्या आप उन्हें बदलना चाहते हैं?\"\nRemux.ExitUnfinishedTitle=\"रीमक्सिंग हो रहा है\"\nRemux.ExitUnfinished=\"रीमक्सिंग पूरा नहीं हुआ है, अभी रोकना लक्ष्य फ़ाइल को अनुपयोगी बना सकता है.\\nक्या आप सचमुच रीमक्सिंग को रोकना चाहते हैं?\"\nRemux.HelpText=\"फ़ाइलों को रीमक्स करने के लिए इस विंडो में गिराएं, या किसी फ़ाइल को ब्राउज़ करने के लिए एक खाली \\\"OBS रिकॉर्डिंग\\\" सेल का चयन करें.\"\nRemux.NoFilesAddedTitle=\"कोई रीमक्सिंग फ़ाइल नहीं जोड़ी गई\"\nRemux.NoFilesAdded=\"रीमक्स में कोई फ़ाइल नहीं जोड़ी गई है. एक या अनेक वीडियो फ़ाइल वाले किसी फ़ोल्डर को हटा दें.\"\nMissingFiles=\"अनुपलब्ध फाइलें\"\nMissingFiles.MissingFile=\"अनुपलब्ध फाइल\"\nMissingFiles.NewFile=\"नई फ़ाइल\"\nMissingFiles.HelpText=\"आपके द्वारा पिछली बार OBS का उपयोग किए जाने के बाद से कुछ फ़ाइलें अनुपलब्ध हैं.\"\nMissingFiles.Clear=\"<अनुपलब्ध>\"\nMissingFiles.NumFound=\"कुल %2 में %1 मिला\"\nMissingFiles.Search=\"डायरेक्टरी में ढूंढें...\"\nMissingFiles.SelectFile=\"फ़ाइल चुनें...\"\nMissingFiles.SelectDir=\"ढूंढने के लिए फ़ोल्डर चुनें\"\nMissingFiles.State=\"अवस्था\"\nMissingFiles.Missing=\"अनुपलब्ध\"\nMissingFiles.Replaced=\"बदला गया\"\nMissingFiles.Cleared=\"हटाया गया\"\nMissingFiles.Found=\"मिला\"\nMissingFiles.AutoSearch=\"अतिरिक्त मेल खाती फा़ईलें मिलीं\"\nMissingFiles.AutoSearchText=\"OBS ने उस डायरेक्टरी में अनुपलब्ध फ़ाइलों के लिए अतिरिक्त मिलान पाया है. क्या आप उन्हें जोड़ना चाहेंगे?\"\nMissingFiles.NoMissing.Title=\"अनुपलब्ध फाइलों की जाँच\"\nMissingFiles.NoMissing.Text=\"एक भी फ़ाइल अनुपलब्ध नहीं है.\"\nMacPermissions.MenuAction=\"ऐप अनुमतियों की समीक्षा करें...\"\nMacPermissions.Title=\"ऐप अनुमतियों की समीक्षा करें\"\nMacPermissions.Description=\"OBS Studio को कुछ सुविधाएँ प्रदान करने में सक्षम होने के लिए आपकी अनुमति की आवश्यकता होती है. इन अनुमतियों को सक्षम करने की अनुशंसा की जाती है, लेकिन यह ऐप के प्रयोग के लिए आवश्यक नहीं हैं. आप उन्हें बाद में कभी भी सक्षम कर सकते हैं.\"\nMacPermissions.Description.OpenDialog=\"आप OBS स्टूडियो मेनू के माध्यम से इस संवाद को फिर से खोल सकते हैं.\"\nMacPermissions.AccessGranted=\"अनुमति प्रदान की गई\"\nMacPermissions.RequestAccess=\"अनुमति के लिए अनुरोध करें\"\nMacPermissions.OpenPreferences=\"%1 वरीयताएँ खोलें\"\nMacPermissions.Item.ScreenRecording=\"स्क्रीन रिकॉर्डिंग\"\nMacPermissions.Item.ScreenRecording.Details=\"आपकी स्क्रीन को कैप्चर करने में सक्षम होने के लिए OBS को इस अनुमति की आवश्यकता है.\"\nMacPermissions.Item.Camera=\"कैमरा\"\nMacPermissions.Item.Camera.Details=\"वेबकैम या कैप्चर कार्ड से सामग्री कैप्चर करने के लिए इस अनुमति की आवश्यकता है.\"\nMacPermissions.Item.Microphone=\"माइक्रोफ़ोन\"\nMacPermissions.Item.Microphone.Details=\"यदि आप अपना माइक्रोफ़ोन या कोई बाहरी ऑडियो यंत्र जोड़ना चाहते हैं तो OBS को इस अनुमति की आवश्यकता होगी.\"\nMacPermissions.Item.Accessibility=\"पहुँच-योग्यता\"\nMacPermissions.Continue=\"आगे बढ़ें\"\nSourceLeak.Title=\"स्रोत सफाई त्रुटि\"\nSourceLeak.Text=\"दृश्य संग्रह बदलते समय एक समस्या हुई और कुछ स्रोत अनलोड नहीं किए जा सके. यह समस्या आमतौर पर प्लगइन्स के कारण होती है जो संसाधनों की बिदाई ठीक से नहीं कर रहे हैं. कृपया सुनिश्चित करें कि आपके द्वारा उपयोग किए जा रहे सभी प्लगइन्स अद्यतित हैं.\\n\\nकिसी भी संभावित डेटा भ्रष्टाचार को रोकने के लिए Obs स्टूडियो अब बाहर निकल जाएगा.\"\nBasic.DesktopDevice1=\"डेस्कटॉप ऑडियो\"\nBasic.DesktopDevice2=\"डेस्कटॉप ऑडियो 2\"\nBasic.Scene=\"दृश्य\"\nBasic.DisplayCapture=\"डिसप्ले कैप्चर करें\"\nBasic.Main.PreviewConextMenu.Enable=\"पूर्वावलोकन सक्षम करें\"\nBasic.Main.Preview.Disable=\"पूर्वावलोकन अक्षम करें\"\nScaleFiltering=\"स्केल फ़िल्टरिंग\"\nScaleFiltering.Point=\"बिंदु\"\nScaleFiltering.Bilinear=\"द्विरेखीय\"\nScaleFiltering.Bicubic=\"बाइक्यूबिक\"\nScaleFiltering.Area=\"क्षेत्र\"\nBlendingMethod=\"ब्लेंडिंग विधि\"\nBlendingMethod.Default=\"आरंभिक\"\nBlendingMethod.SrgbOff=\"SRGB ऑफ़\"\nBlendingMode=\"ब्लेंडिंग मोड\"\nBlendingMode.Normal=\"सामान्य\"\nBlendingMode.Additive=\"जोड़ना\"\nBlendingMode.Subtract=\"घटाव\"\nBlendingMode.Screen=\"स्क्रीन\"\nBlendingMode.Multiply=\"गुणा\"\nBlendingMode.Lighten=\"हल्का\"\nBlendingMode.Darken=\"गहरा\"\nDeinterlacing=\"डीइंटरलेस\"\nDeinterlacing.Discard=\"छोड़ें\"\nDeinterlacing.TopFieldFirst=\"ऊपरी फील्ड पहले\"\nDeinterlacing.BottomFieldFirst=\"निचला फील्ड पहले\"\nVolControl.SliderUnmuted=\"वॉल्यूम स्लाइडर '%1' के लिए :\"\nVolControl.SliderMuted=\"'%1' के लिए वॉल्यूम स्लाइडर : (वर्तमान में मौन)\"\nVolControl.Mute=\"'%1' मौन करें\"\nVolControl.Properties=\"'%1' के गुण\"\nVolControl.UnassignedWarning.Title=\"अनिर्दिष्ट ऑडियो स्रोत\"\nVolControl.UnassignedWarning.Text=\"\"\nBasic.Main.AddSceneDlg.Title=\"दृश्य जोड़ें\"\nBasic.Main.AddSceneDlg.Text=\"कृपया दृश्य का नाम लिखें\"\nBasic.Main.DefaultSceneName.Text=\"दृश्य %1\"\nBasic.Main.AddSceneCollection.Title=\"दृश्य संग्रह जोड़ें\"\nBasic.Main.AddSceneCollection.Text=\"कृपया दृश्य संग्रह का नाम लिखें\"\nBasic.Main.RenameSceneCollection.Title=\"दृश्य संग्रह का नया नाम रखें\"\nBasic.Main.RemigrateSceneCollection.Title=\"दृश्य संग्रह रिज़ॉल्यूशन अपडेट करें\"\nBasic.Main.RemigrateSceneCollection.Text=\"क्या आप \\\"%1\\\" के दृश्य संग्रह रिज़ॉल्यूशन को वर्तमान प्रोफ़ाइल के कैनवास रिज़ॉल्यूशन %2x%3 से मिलान करने के लिए अपडेट करना चाहते हैं?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"आउटपुट सक्रिय होने पर दृश्य संग्रह रिज़ॉल्यूशन को अद्यतन नहीं किया जा सकता.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"दृश्य संग्रह रिज़ॉल्यूशन अपडेट करने में विफल. मूल रिज़ॉल्यूशन अज्ञात है.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"रीसेट संभव नहीं: OBS रिज़ॉल्यूशन बदलना विफल.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"रीसेट संभव नहीं: वर्तमान रिज़ॉल्यूशन पहले से ही दृश्य संग्रह का आधार रिज़ॉल्यूशन है.\"\nAddProfile.Title=\"प्रोफ़ाइल जोड़ें\"\nAddProfile.Text=\"कृपया प्रोफ़ाइल का नाम लिखें\"\nAddProfile.WizardCheckbox=\"ऑटो-कॉन्फ़िगरेशन विज़ार्ड दिखाएं\"\nRenameProfile.Title=\"प्रोफ़ाइल का नाम बदलें\"\nBasic.Main.MixerRename.Title=\"ऑडियो स्रोत का नाम बदलें\"\nBasic.Main.MixerRename.Text=\"कृपया ऑडियो स्रोत का नाम लिखें\"\nBasic.Main.PreviewDisabled=\"पूर्वालोकन अभी अक्षम है.\"\nBasic.SourceSelect=\"स्रोत बनाएं/चुनें\"\nBasic.SourceSelect.CreateNew=\"नया बनाएं\"\nBasic.SourceSelect.AddExisting=\"उपलब्ध जोड़ें\"\nBasic.SourceSelect.AddVisible=\"स्रोत दृश्यमान बनाएं\"\nBasic.Main.Sources.Visibility=\"दृश्यता\"\nBasic.Main.Sources.VisibilityDescription=\"कैनवास में '%1' की दृश्यता को नियंत्रित करता है\"\nBasic.Main.Sources.Lock=\"ताला\"\nBasic.Main.Sources.LockDescription=\"कैनवास में '%1' की स्थिति और पैमाने को ताला-बंद करता है\"\nBasic.PropertiesWindow=\"'%1' के गुण\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (स्वतः चयन : %2)\"\nBasic.PropertiesWindow.SelectColor=\"रंग चुनें\"\nBasic.PropertiesWindow.SelectFont=\"फ़ॉन्ट चुनें\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"एक फ़ॉन्ट चुनें\"\nBasic.PropertiesWindow.ConfirmTitle=\"सेटिंग्स बदल गईं\"\nBasic.PropertiesWindow.Confirm=\"आपके पास असंचित परिवर्तन हैं. क्या आप इन्हें सहेजना चाहते हैं?\"\nBasic.PropertiesWindow.NoProperties=\"कोई गुण उपलब्ध नहीं है\"\nBasic.PropertiesWindow.AddFiles=\"फाइलें जोड़ें\"\nBasic.PropertiesWindow.AddDir=\"डायरेक्टरी जोड़ें\"\nBasic.PropertiesWindow.AddURL=\"पथ/URL जोड़ें\"\nBasic.PropertiesWindow.AddEditableListDir=\"'%1' में डायरेक्टरी जोड़ें\"\nBasic.PropertiesWindow.AddEditableListFiles=\"'%1' में फ़ाइलें जोड़ें\"\nBasic.PropertiesWindow.AddEditableListEntry=\"'%1' में प्रविष्टि जोड़ें\"\nBasic.PropertiesWindow.EditEditableListEntry=\"'%1' से प्रविष्टि संपादित करें\"\nBasic.PropertiesView.FPS.Simple=\"सरल FPS मान\"\nBasic.PropertiesView.FPS.Rational=\"युक्तिसंगत FPS मान\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"वैध FPS रेंज :\"\nBasic.PropertiesView.UrlButton.Text=\"इस लिंक को अपने डिफ़ॉल्ट वेब ब्राउज़र में खोलें?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"URL खोलें\"\nBasic.InteractionWindow=\"'%1' से सहभागिता\"\nBasic.StatusBar.Reconnecting=\"डिस्कनेक्ट हो गया,%2 सेकंड में पुन: कनेक्ट हो रहा है (प्रयास %1)\"\nBasic.StatusBar.AttemptingReconnect=\"पुन: कनेक्ट करने का प्रयास कर रहे हैं... (प्रयास %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"कनेक्शन पुन: स्थापित हो गया\"\nBasic.StatusBar.Delay=\"विलंब (%1 सेकंड)\"\nBasic.StatusBar.DelayStartingIn=\"विलंब ( %1 सेकेंड में प्रारंभ होगा)\"\nBasic.StatusBar.DelayStoppingIn=\"विलंब ( %1 सेकेंड में रोकेंगे)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"विलंब (%1 सेकेंड मे रूकेगा, %2 सेकेंड मे प्रारंभ होगा)\"\nBasic.StatusBar.RecordingSavedTo=\"रिकॉर्डिंग को '%1 में सहेजा गया.\"\nBasic.StatusBar.ReplayBufferSavedTo=\"रीप्ले बफ़र '%1' में सहेजा गया\"\nBasic.StatusBar.ScreenshotSavedTo=\"स्क्रीनशॉट '%1' में सहेजा गया\"\nBasic.StatusBar.AutoRemuxedTo=\"रिकॉर्डिंग को '%1' में रीमक्स किया गया\"\nBasic.Filters=\"फिल्टर\"\nBasic.Filters.AsyncFilters=\"ऑडियो/वीडियो फिल्टर\"\nBasic.Filters.AudioFilters=\"ऑडियो फिल्टर\"\nBasic.Filters.EffectFilters=\"प्रभाव फिल्टर\"\nBasic.Filters.Title=\"'%1' के लिए फिल्टर\"\nBasic.Filters.AddFilter.Title=\"फ़िल्टर का नाम\"\nBasic.Filters.AddFilter.Text=\"कृपया फिल्टर के लिए एक नाम बताएं\"\nBasic.TransformWindow=\"दृश्य वस्तु रूपांतर करें\"\nBasic.TransformWindow.Position=\"स्थिति\"\nBasic.TransformWindow.PositionX=\"स्थिति X\"\nBasic.TransformWindow.PositionY=\"स्थिति Y\"\nBasic.TransformWindow.Rotation=\"घुमाव\"\nBasic.TransformWindow.Size=\"आकार\"\nBasic.TransformWindow.Width=\"चौड़ाई\"\nBasic.TransformWindow.Height=\"ऊँचाई\"\nBasic.TransformWindow.Alignment=\"स्थितीय संरेखण\"\nBasic.TransformWindow.BoundsType=\"बाउंडिंग बॉक्स प्रकार\"\nBasic.TransformWindow.BoundsAlignment=\"बाउंडिंग बॉक्स में संरेखण\"\nBasic.TransformWindow.Bounds=\"बाउंडिंग बॉक्स आकार\"\nBasic.TransformWindow.BoundsWidth=\"बाउंडिंग बॉक्स चौड़ाई\"\nBasic.TransformWindow.BoundsHeight=\"बाउंडिंग बॉक्स ऊँचाई\"\nBasic.TransformWindow.CropToBounds=\"बाउंडिंग बॉक्स में काटें\"\nBasic.TransformWindow.Crop=\"छँटाई करें\"\nBasic.TransformWindow.CropLeft=\"बाएं से छाँटें\"\nBasic.TransformWindow.CropRight=\"दाएं से छाँटें\"\nBasic.TransformWindow.CropTop=\"ऊपर से छाँटें\"\nBasic.TransformWindow.CropBottom=\"नीचे से छाँटें\"\nBasic.TransformWindow.Alignment.TopLeft=\"ऊपर बाएं\"\nBasic.TransformWindow.Alignment.TopCenter=\"ऊपरी मध्य\"\nBasic.TransformWindow.Alignment.TopRight=\"ऊपर दायें\"\nBasic.TransformWindow.Alignment.CenterLeft=\"बायां केंद्र\"\nBasic.TransformWindow.Alignment.Center=\"केंद्र\"\nBasic.TransformWindow.Alignment.CenterRight=\"दायां केंद्र\"\nBasic.TransformWindow.Alignment.BottomLeft=\"नीचे बायें\"\nBasic.TransformWindow.Alignment.BottomCenter=\"नीचे केंद्र में\"\nBasic.TransformWindow.Alignment.BottomRight=\"नीचे दाएं\"\nBasic.TransformWindow.BoundsType.None=\"असीमित\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"केवल अधिकतम आकार\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"आंतरिक सीमा तक बढ़ाएं\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"बाह्य सीमा तक बढ़ाएं\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"सीमा की चौड़ाई तक बढ़ाएं\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"सीमा की ऊँचाई तक बढ़ाएं\"\nBasic.TransformWindow.BoundsType.Stretch=\"सीमाओं तक विस्तृत करें\"\nBasic.TransformWindow.Title=\"'%1' के लिए रूपांतर संपादित करें\"\nBasic.TransformWindow.NoSelectedSource=\"कोई स्रोत नहीं चुना गया\"\nBasic.Main.AddSourceHelp.Title=\"स्रोत नहीं जोड़ सकते\"\nBasic.Main.AddSourceHelp.Text=\"स्रोत जोड़ने के लिए आपके पास कम से कम 1 दृश्य होना चाहिए.\"\nBasic.Main.Scenes=\"दृश्य\"\nBasic.Main.Sources=\"स्रोत\"\nBasic.Main.Source=\"स्रोत\"\nBasic.Main.Controls=\"नियंत्रण\"\nBasic.Main.PreparingStream=\"तैयार कर रहे हैं...\"\nBasic.Main.Connecting=\"कनेक्ट हो रहा है...\"\nBasic.Main.StartRecording=\"रिकॉर्डिंग शुरू करें\"\nBasic.Main.StartReplayBuffer=\"रीप्ले बफ़र आरंभ करें\"\nBasic.Main.SaveReplay=\"रीप्ले सहेजें\"\nBasic.Main.StartStreaming=\"स्ट्रीमिंग आरंभ करें\"\nBasic.Main.StartBroadcast=\"लाइव हो जाइए\"\nBasic.Main.StartVirtualCam=\"आभासी कैमरा चलाएं\"\nBasic.Main.StopRecording=\"रिकॉर्डिंग बंद करें\"\nBasic.Main.PauseRecording=\"रिकॉर्डिंग रोकें\"\nBasic.Main.UnpauseRecording=\"रिकॉर्डिंग चलने दें\"\nBasic.Main.SplitFile=\"रिकॉर्डिंग फ़ाइल विभाजित करें\"\nBasic.Main.AddChapterMarker=\"चैप्टर मार्कर जोड़ें (केवल हाइब्रिड MP4/MOV)\"\nBasic.Main.StoppingRecording=\"रिकॉर्डिंग बंद कर रहे हैं\"\nBasic.Main.StopReplayBuffer=\"रीप्ले बफ़र रोक दें\"\nBasic.Main.StoppingReplayBuffer=\"रीप्ले बफ़र रोक रहे हैं\"\nBasic.Main.SetupBroadcast=\"प्रसारण प्रबंध करें\"\nBasic.Main.StopStreaming=\"प्रसारण रोकें\"\nBasic.Main.StopBroadcast=\"प्रसारण समाप्त करें\"\nBasic.Main.AutoStopEnabled=\"(ऑटो स्टॉप)\"\nBasic.Main.StoppingStreaming=\"प्रसारण रोक रहे हैं\"\nBasic.Main.ForceStopStreaming=\"स्ट्रीमिंग बंद करें (विलंब त्यागें)\"\nBasic.Main.ShowContextBar=\"स्रोत टूलबार दिखाएं\"\nBasic.Main.HideContextBar=\"स्रोत टूलबार छिपाएं\"\nBasic.Main.StopVirtualCam=\"आभासी कैमरा रोक दें\"\nBasic.Main.Group=\"समूह %1\"\nBasic.Main.GroupItems=\"ग्रुप में चुनी वस्तुएं\"\nBasic.Main.Ungroup=\"ग्रुप से अलग करें\"\nBasic.Main.GridMode=\"ग्रिड मोड\"\nBasic.Main.ListMode=\"सूची मोड\"\nBasic.Main.VirtualCamConfig=\"आभासी कैमरा कॉन्फ़िगर करें\"\nBasic.VCam.VirtualCamera=\"आभासी कैमरा\"\nBasic.VCam.OutputType=\"आउटपुट प्रकार\"\nBasic.VCam.OutputSelection=\"आउटपुट चयन\"\nBasic.VCam.OutputType.Program=\"प्रोग्राम (आरंभिक)\"\nBasic.VCam.OutputSelection.NoSelection=\"इस आउटपुट प्रकार के लिए कोई चयन उपलब्ध नहीं है\"\nBasic.VCam.RestartWarning=\"इस बदलाव को लागू करने के लिए वर्चुअल कैमरा फिर से शुरू किया जाएगा\"\nBasic.MainMenu.File=\"फ़ाइल (&F)\"\nBasic.MainMenu.File.Export=\"निर्यात (&E)\"\nBasic.MainMenu.File.Import=\"आयात (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"रिकॉर्डिंग दिखाएं (&R)\"\nBasic.MainMenu.File.Remux=\"रिमक्स रिकॉर्डिंग्स (&m)\"\nBasic.MainMenu.File.Settings=\"सेटिंग्स (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"सेटिंग्स फोल्डर दिखाएं\"\nBasic.MainMenu.File.ShowProfileFolder=\"प्रोफ़ाइल फोल्डर दिखाएं\"\nBasic.MainMenu.File.ShowMissingFiles=\"अनुपलब्ध फाइलों की जाँच करें\"\nBasic.MainMenu.File.Exit=\"प्रस्थान (&x)\"\nBasic.MainMenu.Edit=\"संपादित करें\"\nBasic.MainMenu.Edit.Undo=\"पूर्ववत करें (&U)\"\nBasic.MainMenu.Edit.Redo=\"पुनः दुहराएं (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"पूर्वावलोकन लॉक करें\"\nBasic.MainMenu.Edit.Scale=\"स्केलिंग पूर्वावलोकन (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"विंडो में विस्तृत करें\"\nBasic.MainMenu.Edit.Scale.Canvas=\"कैनवास (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"आउटपुट (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"स्केल किया गया (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"ज़ूम इन\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"ज़ूम आउट\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"ज़ूम रीसेट करें\"\nBasic.MainMenu.Edit.Transform=\"रूपांतर करें (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"रूपांतर संपादित करें... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"रूपांतर की प्रतिलिपि बनाएं\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"रूपांतर चिपकाएं\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"रूपांतर रीसेट करें\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"90 डिग्री CW घुमाएं\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"90 डिग्री CCW घुमाएं\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180 डिग्री घुमाएं\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"क्षैतिज में पलटें (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"ऊर्ध्वाधर पलटें (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"स्क्रीन में फ़िट करें\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"पूरे स्क्रीन में फैला दें\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"स्क्रीन के मध्य में रखें\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"लंबवत केन्द्रित करें\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"क्षैतिज केन्द्रित करें\"\nBasic.MainMenu.Edit.Order=\"क्रमानुसार (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"ऊपर ले जाएँ\"\nBasic.MainMenu.Edit.Order.MoveDown=\"नीचे ले जाएँ\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"शीर्ष पर ले जाएँ (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"तल पर ले जाएं (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"उन्नत ऑडियो गुण (&A)\"\nBasic.MainMenu.View=\"देखें (&V)\"\nBasic.MainMenu.View.Toolbars=\"टूलबार (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"डॉक टूलबार\"\nBasic.MainMenu.View.ContextBar=\"स्रोत टूलबार\"\nBasic.MainMenu.View.SourceIcons=\"स्रोत आइकन्स (&I)\"\nBasic.MainMenu.View.StatusBar=\"स्टेटस बार (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"फ़ुलस्क्रीन इंटरफ़ेस\"\nBasic.MainMenu.View.ResetUI=\"UI &रीसेट करें\"\nBasic.MainMenu.View.AlwaysOnTop=\"हरदम ऊपर (&A)\"\nBasic.MainMenu.View.SceneListMode=\"दृश्य सूची मोड\"\nBasic.MainMenu.Docks=\"&डॉक्स\"\nBasic.MainMenu.Docks.ResetDocks=\"डॉक्स &रीसेट करें\"\nBasic.MainMenu.Docks.LockDocks=\"डॉक्स &लॉक करें\"\nBasic.MainMenu.Docks.SideDocks=\"पूर्ण-ऊंचाई वाले डॉक्स(&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&कस्टम ब्राउज़र डॉक्स...\"\nBasic.MainMenu.Import=\"आयात...\"\nBasic.MainMenu.Export=\"निर्यात...\"\nBasic.MainMenu.SceneCollection=\"दृश्य संग्रह (&S)\"\nBasic.MainMenu.Profile=\"प्रोफ़ाइल (&P)\"\nBasic.MainMenu.Profile.Import=\"प्रोफ़ाइल आयात करें\"\nBasic.MainMenu.Profile.Export=\"प्रोफ़ाइल निर्यात करें\"\nBasic.MainMenu.SceneCollection.Import=\"दृश्य संग्रह आयात करें\"\nBasic.MainMenu.SceneCollection.Export=\"दृश्य संग्रह निर्यात करें\"\nBasic.MainMenu.SceneCollection.Remigrate=\"आधार रिज़ॉल्यूशन रीसेट करें\"\nBasic.MainMenu.SceneCollection.Migrate=\"आधार रिज़ॉल्यूशन सेट करें\"\nBasic.MainMenu.Profile.Exists=\"प्रोफ़ाइल पहले ही विद्यमान है\"\nBasic.MainMenu.Tools=\"टूल्स (&T)\"\nBasic.MainMenu.Help=\"मदद (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"मदद पोर्टल (&P)\"\nBasic.MainMenu.Help.Website=\"वेबसाइट पर जाएँ (&W)\"\nBasic.MainMenu.Help.Discord=\"&Discord सर्वर से जुड़ें\"\nBasic.MainMenu.Help.WhatsNew=\"इसमें क्या नया है\"\nBasic.MainMenu.Help.Logs=\"लॉग फ़ाइलें (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"लॉग फ़ाइलें दिखाएं (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"वर्तमान लॉग फ़ाइल अपलोड करें (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"पिछली लॉग फ़ाइल अपलोड करें (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"वर्तमान लॉग देखें (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"रिलीज नोट्स\"\nBasic.MainMenu.Help.CheckForUpdates=\"अपडेट के लिए देखें\"\nBasic.MainMenu.Help.Repair=\"फ़ाइल की अखंडता की जाँच करें\"\nBasic.MainMenu.Help.RestartSafeMode=\"सुरक्षित मोड में पुनरारंभ करें\"\nBasic.MainMenu.Help.RestartNormal=\"सामान्य मोड में पुनरारंभ करें\"\nBasic.MainMenu.Help.CrashLogs=\"क्रैश रिपोर्ट (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"क्रैश रिपोर्ट दिखाएं (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"पिछला क्रैश रिपोर्ट अपलोड करें (&P)\"\nBasic.MainMenu.Help.About=\"परिचय (&A)\"\nBasic.Settings.ProgramRestart=\"परिवर्तनों को लागू करने के लिए प्रोग्राम को रीस्टार्ट करना जरूरी है.\"\nBasic.Settings.ConfirmTitle=\"परिवर्तनों की पुष्टि करें\"\nBasic.Settings.Confirm=\"आपने परिवर्तनों को अभी नहीं सहेजा है. क्या आप इन्हें सहेजना चाहते हैं?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 आपकी कुछ स्ट्रीम सेटिंग्स को नियंत्रित कर रहा है\"\nBasic.Settings.General=\"सामान्य\"\nBasic.Settings.General.Language=\"भाषा\"\nBasic.Settings.General.Updater=\"अपडेट\"\nBasic.Settings.General.UpdateChannel=\"अद्यतन चैनल\"\nBasic.Settings.General.UpdateChannelDisabled=\"(अक्षम)\"\nBasic.Settings.General.UpdateChannelDefault=\"(डिफ़ॉल्ट)\"\nBasic.Settings.General.EnableAutoUpdates=\"प्रारंभ में स्वतः अपडेट के लिए जांच करें\"\nBasic.Settings.General.OpenStatsOnStartup=\"प्रारंभ में आँकड़े दिखाने के लिए पूछें\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"OBS विंडो को स्क्रीन कैप्चर से छुपाएं\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"यह सेटिंग सभी गैर-प्रोजेक्टर OBS स्टूडियो के विंडो को OBS द्वारा कैप्चर किए जाने से छिपा देगी और\\nअन्य एप्लिकेशन, जैसे कॉन्फ़्रेंसिंग, स्क्रीन शेयरिंग, रिमोट सपोर्ट, स्क्रीनशॉट और अन्य कैप्चर सॉफ़्टवेयर को प्रभावित करती है.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"यह सेटिंग सक्रिय करने पर सभी गैर-प्रोजेक्टर OBS स्टूडियो के विंडो को OBS द्वारा कैप्चर किए जाने से छिपा देगी और अन्य एप्लिकेशन, जैसे कॉन्फ़्रेंसिंग, स्क्रीन शेयरिंग, रिमोट सपोर्ट, स्क्रीनशॉट और अन्य कैप्चर सॉफ़्टवेयर को प्रभावित करती है.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"नए स्ट्रीम्स आरंभ करने के लिए पुष्टि संवाद दिखाएँ\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"नए स्ट्रीम्स को रोकने के लिए पुष्टि संवाद दिखाएँ\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"रिकॉर्डिंग रोकने के लिए पुष्टि संवाद दिखाएँ\"\nBasic.Settings.General.Projectors=\"प्रदर्शक\"\nBasic.Settings.General.HideProjectorCursor=\"प्रोजेक्टर पर कर्सर छुपाएं\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"प्रोजेक्टर को हमेशा ऊपर रखें\"\nBasic.Settings.General.Snapping=\"स्रोत संरेखण स्नैपिंग\"\nBasic.Settings.General.ScreenSnapping=\"स्रोतों को स्क्रीन के किनारे पर स्नैप करें\"\nBasic.Settings.General.CenterSnapping=\"स्रोतों को क्षैतिज और लंबवत केंद्र में स्नैप करें\"\nBasic.Settings.General.SourceSnapping=\"स्रोतों को अन्य स्रोतों के साथ स्नैप करें\"\nBasic.Settings.General.SnapDistance=\"स्नैप संवेदनशीलता\"\nBasic.Settings.General.SpacingHelpers=\"पिक्सेल संरेखण मार्गदर्शिकाएँ दिखाएं\"\nBasic.Settings.General.RecordWhenStreaming=\"स्ट्रीमिंग करते समय स्वचालित रूप से रिकॉर्ड करें\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"स्ट्रीम रुकने पर रिकॉर्डिंग करते रहें\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"स्ट्रीमिंग करते समय स्वचालित रूप से रीप्ले बफर प्रारंभ करें\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"स्ट्रीम रुकने पर रीप्ले बफर को सक्रिय रखें\"\nBasic.Settings.General.SysTray=\"सिस्टम ट्रे\"\nBasic.Settings.General.SysTrayWhenStarted=\"प्रारंभ होने पर सिस्टम ट्रे में न्यूनतम करें\"\nBasic.Settings.General.SystemTrayHideMinimize=\"टास्क बार के बजाय हमेशा सिस्टम ट्रे में छोटा करें\"\nBasic.Settings.General.SaveProjectors=\"बाहर निकलने पर प्रोजेक्टरों को संचित करें\"\nBasic.Settings.General.CloseExistingProjectors=\"प्रति स्क्रीन एक पूर्णस्क्रीन प्रोजेक्टर सीमित करें\"\nBasic.Settings.General.Preview=\"पूर्वावलोकन\"\nBasic.Settings.General.OverflowHidden=\"ओवरफ्लो छुपाएं\"\nBasic.Settings.General.OverflowAlwaysVisible=\"ओवरफ्लो हमेशा दिखाई देगा\"\nBasic.Settings.General.OverflowSelectionHidden=\"स्रोत के अदृश्य होने पर भी ओवरफ्लो दिखाएं\"\nBasic.Settings.General.Importers=\"आयातकर्ता\"\nBasic.Settings.General.AutomaticCollectionSearch=\"आयात करते समय दृश्य संग्रह के लिए ज्ञात स्थानों में खोजें\"\nBasic.Settings.General.SwitchOnDoubleClick=\"दृश्य संक्रांति के लिए डबल-क्लिक करें\"\nBasic.Settings.General.StudioPortraitLayout=\"पोर्ट्रेट/वर्टिकल लेआउट सक्षम करें\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"पूर्वावलोकन/कार्यक्रम लेबल दिखाएं\"\nBasic.Settings.General.Multiview=\"मल्टीव्यू\"\nBasic.Settings.General.Multiview.MouseSwitch=\"दृश्य की अदला-बदला के लिए क्लिक करें.\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"दृश्यों के नाम दिखाएं\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"सुरक्षित क्षेत्र बनाएं (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"मल्टीव्यू लेआउट\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"क्षैतिज, शीर्ष (8 दृश्य)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"क्षैतिज, नीचे (8 दृश्य)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"लंबवत, बाएं (8 दृश्य)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"लंबवत, दाएं (8 दृश्य)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"क्षैतिज, शीर्ष (18 दृश्य)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"क्षैतिज, शीर्ष (24 दृश्य)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"केवल दृश्य (4 दृश्य)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"केवल दृश्य (9 दृश्य)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"केवल दृश्य (16 दृश्य)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"केवल दृश्य (25 दृश्य)\"\nBasic.Settings.General.ChannelName.stable=\"स्थिर\"\nBasic.Settings.General.ChannelDescription.stable=\"नवीनतम स्थिर रिलीज\"\nBasic.Settings.General.ChannelName.beta=\"बीटा / रिलीज़ उम्मीदवार\"\nBasic.Settings.General.ChannelDescription.beta=\"संभावित रूप से अस्थिर पूर्व-रिलीज़ संस्करण\"\nBasic.Settings.Appearance=\"रूप-रंग\"\nBasic.Settings.Appearance.General=\"सामान्य\"\nBasic.Settings.Appearance.General.Theme=\"प्रदर्शनशैली\"\nBasic.Settings.Appearance.General.Variant=\"शैली\"\nBasic.Settings.Appearance.General.NoVariant=\"कोई शैलियाँ उपलब्ध नहीं हैं\"\nBasic.Settings.Appearance.FontScale=\"फ़ॉन्ट आकार\"\nBasic.Settings.Appearance.Density=\"घनत्व\"\nBasic.Settings.Appearance.Classic=\"क्लासिक\"\nBasic.Settings.Appearance.Compact=\"सघन\"\nBasic.Settings.Appearance.Normal=\"सामान्य\"\nBasic.Settings.Appearance.Comfortable=\"सहज\"\nBasic.Settings.Appearance.OptionsWarning=\"इस शैली के लिए कुछ उपस्थिति विकल्प उपलब्ध नहीं हैं.\"\nBasic.Settings.Stream=\"स्ट्रीम\"\nBasic.Settings.Stream.Destination=\"गंतव्य\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"प्रमाणीकरण का प्रयोग करें\"\nBasic.Settings.Stream.Custom.Username=\"उपयोगकर्ता का नाम\"\nBasic.Settings.Stream.Custom.Password=\"पासवर्ड\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: srp_username लिखें.\\nRTMP: उपयोगकर्ता नाम लिखें.\\nSRT: उपयोग में नहीं है.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: srp_password लिखें.\\nRTMP: पासवर्ड लिखें.\\nSRT: एन्क्रिप्शन पासफ़्रेज़ लिखें.\"\nBasic.Settings.Stream.BandwidthTestMode=\"बैंडविड्थ टेस्ट मोड सक्षम करें\"\nBasic.Settings.Stream.TTVAddon=\"Twitch चैट ऐड-ऑन\"\nBasic.Settings.Stream.TTVAddon.None=\"कुछ नहीं\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV तथा FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"अनुपलब्ध स्ट्रीम सेटअप\"\nBasic.Settings.Stream.StreamSettingsWarning=\"सेटिंग्स खोलें\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL और स्ट्रीम कुंजी अनुपलब्ध हैं.\\n\\n'स्ट्रीम' टैब में URL और स्ट्रीम कुंजी दर्ज करने के लिए सेटिंग्स खोलें.\"\nBasic.Settings.Stream.MissingUrl=\"स्ट्रीम URL अनुपलब्ध है.\\n\\n'स्ट्रीम' टैब में URL दर्ज करने के लिए सेटिंग्स खोलें.\"\nBasic.Settings.Stream.MissingStreamKey=\"स्ट्रीम कुँजी अनुपलब्ध है.\\n\\n'स्ट्रीम' टैब में स्ट्रीम कुँजी दर्ज करने के लिए सेटिंग्स खोलें.\"\nBasic.Settings.Stream.IgnoreRecommended=\"स्ट्रीमिंग सेवा सेटिंग अनुशंसाओं पर ध्यान न दें\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"अनुशंसित सेटिंग्स को ओवरराइड करें\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"चेतावनी: सेवा की सीमाओं को अनदेखा करने से स्ट्रीम की गुणवत्ता में कमी आ सकती है या आपको स्ट्रीमिंग से रोका जा सकता है.\\n\\nजारी रखें?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"अधिकतम वीडियो बिट दर : %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"अधिकतम ऑडियो बिट दर : %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"अधिकतम रिज़ोल्यूशन : %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"अधिकतम FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"कस्टम सर्वर निर्दिष्ट करें...\"\nBasic.Settings.Stream.ServiceCustomServer=\"कस्टम सर्वर\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1 सक्षम करें\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"अधिकतम स्ट्रीमिंग बैंडविड्थ\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"स्वचालित\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"अधिकतम वीडियो ट्रैक\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"स्वचालित\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"स्ट्रीम डंप को FLV में सक्षम करें (सरल रिकॉर्डिंग फ़ाइल सेटिंग्स का उपयोग करता है)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"कॉन्फ़िग ओवरराइड (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"कॉन्फ़िग ओवरराइड सक्षम करें\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"मल्टीट्रैक वीडियो\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"अतिरिक्त कैनवास\"\nBasic.Settings.Stream.AdvancedOptions=\"उन्नत विकल्प\"\nBasic.Settings.Output=\"आउटपुट\"\nBasic.Settings.Output.Format=\"रिकॉर्डिंग प्रारूप\"\nBasic.Settings.Output.Format.MKV=\"मत्रोस्का वीडियो (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"हाइब्रिड MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"हाइब्रिड MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"खंडित MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"खंडित MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"खंडित MOV रिकॉर्डिंग को विखंडों में लिखता है और पारंपरिक MOV फ़ाइलों के समान इन्हेंअंतिम रूप देने की आवश्यकता नहीं होती है.\\nयह सुनिश्चित करता है कि डिस्क पर लिखने में बाधा होने पर भी फ़ाइल चलने योग्य बनी रहे, उदाहरण के लिए, BSOD या पावर लॉस के परिणामस्वरूप.\\n\\nयह सभी प्लेअर्स और संपादकों के साथ संगत नहीं भी हो सकता है. यदि आवश्यक हो तो फ़ाइल को अधिक संगत प्रारूप में बदलने के लिए फ़ाइल → रीमक्स रिकॉर्डिंग का उपयोग करें.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"खंडित MP4 रिकॉर्डिंग को विखंडों में लिखता है और पारंपरिक MP4 फ़ाइलों के समान अंतिम रूप देने की आवश्यकता नहीं होती है.\\n यह सुनिश्चित करता है कि डिस्क पर लेखन बाधित होने पर भी फ़ाइल देय बनी रहती है, उदाहरण के लिए, BSOD या पावर लॉस के परिणामस्वरूप.\\n\\n यह सभी प्लेअर और संपादकों के साथ संगत नहीं हो सकता है. यदि आवश्यक हो तो फ़ाइल को अधिक संगत प्रारूप में बदलने के लिए फ़ाइल → रीमक्स रिकॉर्डिंग का उपयोग करें.\"\nBasic.Settings.Output.Encoder.Video=\"वीडियो एनकोडर\"\nBasic.Settings.Output.Encoder.Audio=\"ऑडियो एनकोडर\"\nBasic.Settings.Output.SelectDirectory=\"रिकॉर्डिंग डायरेक्टरी चुनें\"\nBasic.Settings.Output.DynamicBitrate=\"भीड़भाड़ को प्रबंधित करने के लिए गतिशील रूप से बिटरेट बदलें\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"भीड़भाड़ को प्रबंधित करने के लिए गतिशील रूप से बिटरेट बदलें (बीटा)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"भीड़-भाड़ को कम करने के लिए फ़्रेम छोड़ने के बजाय, गतिशील रूप से बिटरेट को बदलता है.\\n\\nध्यान दें कि अगर अचानक से अत्यधिक भीड़ हो जाती है तो इससे दर्शकों के लिए विलंब बढ़ सकता है.\\nजब बिटरेट गिर जाता है, तो इसे पुनर्स्थापित करने में कुछ मिनट लग सकते हैं.\\n\\nवर्तमान में केवल RTMP के लिए समर्थित है.\"\nBasic.Settings.Output.Mode=\"आउटपुट मोड\"\nBasic.Settings.Output.Mode.Simple=\"सरल\"\nBasic.Settings.Output.Mode.Adv=\"उन्नत\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg आउटपुट\"\nBasic.Settings.Output.UseReplayBuffer=\"रीप्ले बफ़र सक्षम करें\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"अधिकतम रीप्ले समय\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"महत्तम मेमोरी\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"अनुमानित मेमोरी उपयोग : %1 एमबी\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"चेतावनी : %1 MiB का अनुमानित मेमोरी उपयोग अनुशंसित अधिकतम %2 MiB से अधिक है\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"मेमोरी उपयोग का अनुमान नहीं लगा सकता. कृपया अधिकतम मेमोरी सीमा निर्धारित करें.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"रीप्ले बफ़र फ़ाइल नाम उपसर्ग\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"प्रत्यय\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"रिप्ले बफ़र का उपयोग तब नहीं किया जा सकता जब रिकॉर्डिंग प्रकार कस्टम आउटपुट (FFmpeg) पर सेट हो.\"\nBasic.Settings.Output.Simple.SavePath=\"रिकॉर्डिंग पथ\"\nBasic.Settings.Output.Simple.RecordingQuality=\"रिकॉर्डिंग गुणवत्ता\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"स्ट्रीम के समान\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"उच्च गुणवत्ता, मध्यम फ़ाइल आकार\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"अप्रभेद्य गुणवत्ता, बड़ी फ़ाइल आकार\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"दोषरहित गुणवत्ता, अत्यधिक बड़ी फ़ाइल आकार\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"चेतावनी : स्ट्रीमिंग वीडियो बिटरेट को %1 पर सेट किया जाएगा, जो वर्तमान स्ट्रीमिंग सेवा की ऊपरी सीमा है.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"चेतावनी : स्ट्रीमिंग ऑडियो बिटरेट को %1 पर सेट किया जाएगा, जो वर्तमान स्ट्रीमिंग सेवा की ऊपरी सीमा है.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"चेतावनी : यदि रिकॉर्डिंग गुणवत्ता \\\"स्ट्रीम के समान\\\" पर सेट है, तो रिकॉर्डिंग को रोका नहीं जा सकता.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"चेतावनी : यदि आप एक ही समय में स्ट्रीम और रिकॉर्ड करते हैं, तो स्ट्रीम से भिन्न गुणवत्ता वाले सॉफ़्टवेयर एन्कोडर के साथ रिकॉर्डिंग के लिए अतिरिक्त CPU उपयोग की आवश्यकता होगी.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"चेतावनी: दोषरहित गुणवत्ता अत्यधिक बड़े फ़ाइल आकार की फ़ाईल बनाती है! दोषरहित गुणवत्ता उच्च रिज़ॉल्यूशन और फ्रे़मरेट पर प्रति मिनट 7 गीगाबाइट डिस्क स्थान का उपयोग कर सकती है. लंबी रिकॉर्डिंग के लिए दोषरहित की अनुशंसा नहीं की जाती है जब तक कि आपके पास बहुत बड़ी मात्रा में डिस्क स्थान उपलब्ध न हो. दोषरहित गुणवत्ता का उपयोग करते समय रीप्ले बफ़र अनुपलब्ध रहता है.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"क्या आप वाकई दोषरहित गुणवत्ता का उपयोग करना चाहते हैं?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"दोषरहित गुणवत्ता चेतावनी!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"सॉफ्टवेयर (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"हार्डवेयर (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"हार्डवेयर (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"हार्डवेयर (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"हार्डवेयर (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"हार्डवेयर (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"हार्डवेयर (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"हार्डवेयर (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"हार्डवेयर (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"हार्डवेयर (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"हार्डवेयर (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"सॉफ़्टवेयर (x264 कम CPU उपयोग प्रीसेट, फ़ाइल का आकार बढ़ाता है)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (डिफ़ॉल्ट)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD 'ट्रैक (ट्रैक 2 का प्रयोग)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"ऑडियो ट्रैक\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"असंगत रिज़ॉल्यूशन/फ़्रेमरेट\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"यह स्ट्रीमिंग सेवा आपके वर्तमान आउटपुट रिज़ॉल्यूशन और/या फ़्रेमरेट का समर्थन नहीं करती है. उन्हें निकटतम संगत मान में बदल दिया जाएगा:\\n\\n%1\\n\\nक्या आप जारी रखना चाहते हैं?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"रिजॉल्यूशन : %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"असंगत एनकोडर\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"स्ट्रीमिंग सेवा \\\"%1\\\" एन्कोडर \\\"%2\\\" का समर्थन नहीं करती है. इस एन्कोडर को \\\"%3\\\" में बदल दिया जाएगा.\\n\\nक्या आप जारी रखना चाहते हैं?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"\"\nBasic.Settings.Output.VideoBitrate=\"वीडियो बिटरेट\"\nBasic.Settings.Output.AudioBitrate=\"ऑडियो बिटरेट\"\nBasic.Settings.Output.Reconnect=\"स्वचालित रूप से पुन: कनेक्ट करें\"\nBasic.Settings.Output.RetryDelay=\"पुनर्प्रयास विलंब\"\nBasic.Settings.Output.MaxRetries=\"अधिकतम पुनर्प्रयास\"\nBasic.Settings.Output.Advanced=\"कस्टम एन्कोडर सेटिंग सक्षम करें (उन्नत)\"\nBasic.Settings.Output.EncoderPreset=\"एनकोडर प्रीसेट\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (CPU का कम प्रयोग, निम्नतम गुणवत्ता)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (डिफ़ॉल्ट) (CPU का मध्यम प्रयोग, मानक गुणवत्ता)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 ( CPU उच्च का प्रयोग, उच्च गुणवत्ता)\"\nBasic.Settings.Output.CustomEncoderSettings=\"निर्मित एनकोडर सेटिंग्स\"\nBasic.Settings.Output.CustomMuxerSettings=\"निर्मित मक्सर सेटिंग्स\"\nBasic.Settings.Output.NoSpaceFileName=\"रिक्त-स्थान के बिना फ़ाइल नाम उत्पन्न करें\"\nBasic.Settings.Output.Adv.Rescale=\"आउटपुट रीस्केल करें\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"अक्षम\"\nBasic.Settings.Output.Adv.AudioTrack=\"ऑडियो ट्रैक\"\nBasic.Settings.Output.Adv.Streaming=\"स्ट्रीमिंग\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"स्ट्रीमिंग सेटिंग्स\"\nBasic.Settings.Output.Adv.Audio.Track1=\"ट्रैक 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"ट्रैक 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"ट्रैक 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"ट्रैक 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"ट्रैक 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"ट्रैक 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD ट्रैक\"\nBasic.Settings.Output.Adv.Encoder=\"एनकोडर सेटिंग्स\"\nBasic.Settings.Output.Adv.Recording=\"रिकॉर्डिंग\"\nBasic.Settings.Output.Adv.Recording.Settings=\"रिकॉर्डिंग सेटिंग्स\"\nBasic.Settings.Output.Adv.Recording.RecType=\"रिकॉर्डिंग प्रकार\"\nBasic.Settings.Output.Adv.Recording.Type=\"प्रकार\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"मानक\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"निर्मित आउटपुट (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(स्ट्रीम एनकोडर प्रयोग करें)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"फ़ाइल नाम स्वरूपण\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"फ़ाइल उपस्थित होने पर अधिलेखित करें\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"कस्टम आउटपुट (FFmpeg) बिना किसी सुरक्षा उपाय के प्रदान किया जाता है.\\n\\\"मानक\\\" रिकॉर्डिंग प्रकार का उपयोग करने पर विचार करें.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg आउटपुट प्रकार\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"आउटपुट URL में डालें\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"आउटपुट फ़ाइल में डालें\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"सामान्य रिकॉर्डिंग प्रारूप\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"सभी फ़ाइलें\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"फ़ाइल पथ या URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"कंटेनर प्रारूप\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"ऑडियो\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"वीडियो\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"आरंभिक प्रारूप\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"कंटेनर प्रारूप विवरण\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"ऑडियो/वीडियो कोडेक का अनुमान फ़ाइल पथ या URL से लगाया गया\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"डिफ़ॉल्ट एनकोडर\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"एनकोडर अक्षम करें\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"वीडियो एनकोडर\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"वीडियो एनकोडर सेटिंग्स (यदि कोई हो)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"ऑडियो एनकोडर\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"ऑडियो एनकोडर सेटिंग्स (यदि कोई हो)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"मक्सर सेटिंग्स (यदि कोई हो)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"कीफ़्रेम अंतराल (फ़्रेम्स)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"सभी कोडेक्स दिखाएं (भले ही संभावित रूप से असंगत हों)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg सेटिंग्स\"\nBasic.Settings.Output.EnableSplitFile=\"स्वचालित फ़ाइल विभाजन\"\nBasic.Settings.Output.SplitFile.TypeTime=\"समय के अनुसार विभाजित करें\"\nBasic.Settings.Output.SplitFile.TypeSize=\"आकार से विभाजित करें\"\nBasic.Settings.Output.SplitFile.TypeManual=\"केवल मैन्युअल रूप से विभाजित करें\"\nBasic.Settings.Output.SplitFile.Time=\"विभाजन समय\"\nBasic.Settings.Output.SplitFile.Size=\"विभाजित आकार\"\nScreenshot=\"स्क्रीनशॉट आउटपुट\"\nScreenshot.SourceHotkey=\"चयनित स्रोत का स्क्रीनशॉट निकालें\"\nScreenshot.StudioProgram=\"स्क्रीनशॉट (प्रोग्राम)\"\nScreenshot.Preview=\"स्क्रीनशॉट (पूर्वदर्शन)\"\nScreenshot.Scene=\"स्क्रीनशॉट (दृश्य)\"\nScreenshot.Source=\"स्क्रीनशॉट (स्रोत)\"\nFilenameFormatting.TT.CCYY=\"वर्ष, चार अंको में\"\nFilenameFormatting.TT.YY=\"वर्ष, अंतिम दो अंक (00-99)\"\nFilenameFormatting.TT.MM=\"दशमलव संख्या के रूप में माह (01-12)\"\nFilenameFormatting.TT.DD=\"महीने का दिन, शून्य-पूरित (01-31)\"\nFilenameFormatting.TT.hh=\"\"\nFilenameFormatting.TT.mm=\"मिनट (00-59)\"\nFilenameFormatting.TT.ss=\"सेकेंड (00-59)\"\nFilenameFormatting.TT.Percent=\"एक % चिह्न\"\nFilenameFormatting.TT.a=\"कार्यदिवस का संक्षिप्त नाम\"\nFilenameFormatting.TT.A=\"कार्यदिवस का पूरा नाम\"\nFilenameFormatting.TT.b=\"संक्षिप्त महीने का नाम\"\nFilenameFormatting.TT.B=\"महीने का पूरा नाम\"\nFilenameFormatting.TT.d=\"महीने का दिन, शून्य-पूरित (01-31)\"\nFilenameFormatting.TT.H=\"24 घंटों के प्रारूप में घंटा (00-23)\"\nFilenameFormatting.TT.I=\"12 घंटों के प्रारूप में घंटा (01-12)\"\nFilenameFormatting.TT.m=\"दशमलव संख्या के रूप में महीना (01-12)\"\nFilenameFormatting.TT.M=\"मिनट (00-59)\"\nFilenameFormatting.TT.p=\"पूर्वाह्न या अपराह्न प्रहर नाम\"\nFilenameFormatting.TT.s=\"\"\nFilenameFormatting.TT.S=\"सेकेंड (00-59)\"\nFilenameFormatting.TT.y=\"वर्ष, अंतिम दो अंक (00-99)\"\nFilenameFormatting.TT.Y=\"वर्ष\"\nFilenameFormatting.TT.z=\"\"\nFilenameFormatting.TT.Z=\"समय क्षेत्र का नाम या संक्षिप्त नाम\"\nFilenameFormatting.TT.FPS=\"प्रति सेकंड फ्रेम्स\"\nFilenameFormatting.TT.CRES=\"मूल (कैनवास) रिजॉल्यूशन\"\nFilenameFormatting.TT.ORES=\"आउटपुट (स्केल्ड) रिजॉल्यूशन\"\nFilenameFormatting.TT.VF=\"वीडियो प्रारूप\"\nBasic.Settings.Video=\"वीडियो\"\nBasic.Settings.Video.Adapter=\"वीडियो अडैप्टर\"\nBasic.Settings.Video.BaseResolution=\"मूल (कैनवास) रिजॉल्यूशन\"\nBasic.Settings.Video.ScaledResolution=\"आउटपुट (स्केल्ड) रिजॉल्यूशन\"\nBasic.Settings.Video.DownscaleFilter=\"डाउनस्केल फ़िल्टर\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[रिजॉल्यूशन मेल खा रहा है, इसलिए डाउनस्केलिंग की आवश्यकता नहीं]\"\nBasic.Settings.Video.FPSCommon=\"सामान्य FPS मूल्य\"\nBasic.Settings.Video.FPSInteger=\"पूर्णांक FPS मूल्य\"\nBasic.Settings.Video.FPSFraction=\"भिन्नात्मक FPS मूल्य\"\nBasic.Settings.Video.Numerator=\"अंश\"\nBasic.Settings.Video.Denominator=\"भाजक\"\nBasic.Settings.Video.Renderer=\"रेंडरर\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (प्रयोगात्मक)\"\nBasic.Settings.Video.InvalidResolution=\"अमान्य रिज़ॉल्यूशन मान. [चौड़ाई] x [ऊंचाई] (यानी 1920x1080) होनी चाहिए\"\nBasic.Settings.Video.CurrentlyActive=\"वीडियो आउटपुट वर्तमान में सक्रिय है. वीडियो सेटिंग बदलने के लिए कृपया किसी भी आउटपुट को बंद करें.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"बाइलिनियर (सबसे तेज़, लेकिन स्केलिंग होने पर धुंधली)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"बाइक्यूबिक (सटीक स्केलिंग, 16 नमूने)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (सटीक स्केलिंग, 36 नमूने)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"क्षेत्रफल (भारित योग, 4/6/9 नमूने)\"\nBasic.Settings.Audio=\"ऑडियो\"\nBasic.Settings.Audio.SampleRate=\"नमूना दर\"\nBasic.Settings.Audio.Channels=\"चैनल\"\nBasic.Settings.Audio.Meters=\"मीटर\"\nBasic.Settings.Audio.MeterDecayRate=\"क्षय दर\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"तेज़\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"मध्यम (टाइप I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"धीमा (टाइप II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"पीक मीटर प्रकार\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"सैम्पल पीक\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"ट्रू पीक (उच्च CPU उपयोग)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"चेतावनी: सराउंड साउंड ऑडियो सक्षम किया हुआ है.\"\nBasic.Settings.Audio.MultichannelWarning=\"यदि स्ट्रीमिंग कर रहे हैं, तो यह देखने के लिए जांचें कि क्या आपकी स्ट्रीमिंग सेवा सराउंड साउंड इंजेस्ट और सराउंड साउंड प्लेबैक दोनों का समर्थन करती है. उदाहरण के लिए, फेसबुक 360 लाइव पूरी तरह से सराउंड साउंड का समर्थन करता है; YouTube लाइव 5.1 ऑडियो इंजेस्ट (और टीवी पर प्लेबैक) का समर्थन करता है।\\n\\nOBS ऑडियो फिल्टर सराउंड साउंड के साथ संगत हैं, हालांकि VST प्लगइन समर्थन की गारंटी नहीं है.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"सराउंड साउंड ऑडियो सक्षम करें?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"क्या आप वाकई सराउंड साउंड ऑडियो सक्षम करना चाहते हैं?\"\nBasic.Settings.Audio.Devices=\"ग्लोबल ऑडियो उपकरण\"\nBasic.Settings.Audio.DesktopDevice=\"डेस्कटॉप ऑडियो\"\nBasic.Settings.Audio.DesktopDevice2=\"डेस्कटॉप ऑडियो 2\"\nBasic.Settings.Audio.AuxDevice=\"माइक/सहायक ऑडियो\"\nBasic.Settings.Audio.AuxDevice2=\"माइक/सहायक ऑडियो 2\"\nBasic.Settings.Audio.AuxDevice3=\"माइक/सहायक ऑडियो 3\"\nBasic.Settings.Audio.AuxDevice4=\"माइक/सहायक ऑडियो 4\"\nBasic.Settings.Audio.EnablePushToMute=\"मौन-हेतू-दबाएं को सक्षम करें\"\nBasic.Settings.Audio.PushToMuteDelay=\"मौन-हेतू-दबाएं विलंब\"\nBasic.Settings.Audio.EnablePushToTalk=\"बातचीत-हेतु-दबाएं को सक्षम करें\"\nBasic.Settings.Audio.PushToTalkDelay=\"बातचीत-हेतु-दबाएं विलंब\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[डिवाइस कनेक्ट नहीं है या उपलब्ध नहीं है]\"\nBasic.Settings.Audio.Disabled=\"अक्षम\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"कम विलंबता ऑडियो बफरिंग मोड (डेकलिंक/NDI आउटपुट के लिए)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"चेतावनी : कम विलंबता ऑडियो बफ़रिंग सक्षम है.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"कम विलंबता ऑडियो बफ़रिंग मोड के कारण ऑडियो गड़बड़ हो सकता है या कुछ स्रोतों से चलना बंद हो सकता है.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"कम विलंबता ऑडियो बफ़रिंग मोड सक्षम करें?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"क्या आप वाकई कम विलंबता वाले ऑडियो बफ़रिंग मोड को सक्षम करना चाहते हैं?\"\nBasic.Settings.Accessibility=\"पहुँच-योग्यता\"\nBasic.Settings.Accessibility.ColorOverrides=\"विभिन्न रंगों का प्रयोग करें\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"स्रोत सीमारेखा (चयन)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"स्रोत सीमारेखा (छाँटे)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"स्रोत सीमा (होवर)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"मिक्सर वॉल्यूम बैंड (-60 से -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"मिक्सर वॉल्यूम बैंड (-20 से -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"मिक्सर वॉल्यूम बैंड (-9 से 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"मिक्सर वॉल्यूम बैंड (-60 से -20dB) (सक्रिय)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"मिक्सर वॉल्यूम बैंड (-20 से -9dB) (सक्रिय)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"मिक्सर वॉल्यूम बैंड (-9 से 0dB) (सक्रिय)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"रंग प्रीसेट\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"आरंभिक\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"निर्मित\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"रंगान्ध विकल्प\"\nBasic.Settings.Advanced=\"उन्नत\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"बाहर निकलने पर सक्रिय आउटपुट चेतावनी दिखाएं\"\nBasic.Settings.Advanced.General.ProcessPriority=\"प्रक्रिया प्राथमिकता\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"उच्च\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"सामान्य से ऊपर\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"सामान्य\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"सामान्य से नीचे\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"निष्क्रिय\"\nBasic.Settings.Advanced.FormatWarning=\"चेतावनी : NV12/P010 के अलावा अन्य रंग प्रारूप प्राथमिक रूप से रिकॉर्डिंग के लिए अभिप्रेत हैं, और स्ट्रीमिंग करते समय अनुशंसित नहीं हैं. रंग प्रारूप रूपांतरण के कारण स्ट्रीमिंग में CPU के उपयोग में वृद्धि हो सकती है.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"चेतावनी : HDR रंग व्योम में सामान्यतः उच्च-सुक्ष्मता प्रारूपों का अधिक उपयोग किया जाता है.\"\nBasic.Settings.Advanced.FormatWarning2100=\"चेतावनी : Rec. 2100 को अधिक सटीकता वाले प्रारूप का उपयोग करना चाहिए।\"\nBasic.Settings.Advanced.Video.ColorFormat=\"रंग प्रारुप\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-बिट्, 4:2:0, 2 प्लेन्स)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-बिट्, 4:2:0, 3 प्लेन्स)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-बिट्, 4:4:4, 3 प्लेन्स)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-बिट्, 4:2:0, 2 प्लेन्स)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-बिट्, 4:2:0, 3 प्लेन्स)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (10-बिट्, 4:2:2, 2 प्लेन्स)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-बिट्, 4:4:4, 2 प्लेन्स)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-बिट्)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"रंग स्थान\"\nBasic.Settings.Advanced.Video.ColorRange=\"रंग विस्तार\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"सीमित\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"पूर्ण\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR सफेद स्तर\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR अंकित शिखर स्तर\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"निगरानी उपकरण\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"आरंभिक\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"विंडोज ऑडियो डकिंग अक्षम करें\"\nBasic.Settings.Advanced.StreamDelay=\"स्ट्रीम विलंब\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"अंतराल\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"पुन: कनेक्ट करते समय कटऑफ बिंदु (विलंब बढ़ाएँ) को सुरक्षित रखें\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"अनुमानित मेमोरी उपयोग : %1 MB\"\nBasic.Settings.Advanced.Network=\"नेटवर्क\"\nBasic.Settings.Advanced.Network.Disabled=\"वर्तमान में चयनित स्ट्रीमिंग प्रोटोकॉल, नेटवर्क सेटिंग्स को बदलने का समर्थन नहीं करता है.\"\nBasic.Settings.Advanced.Network.BindToIP=\"IP से जोड़ें\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP परिवार\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"नेटवर्क अनुकूलन सक्षम करें\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"TCP pacing सक्षम करें\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"ट्रांसमिशन की दर को विनियमित करके नेटवर्क पर अन्य विलंबता संवेदनशील ऐप्लिकेशन्स के लिए RTMP आउटपुट को अनुकूल बनाने का प्रयास करता है.\\nयह अस्थिर कनेक्शन पर फ्रेम्स के छूट जाने का जोखिम बढ़ा सकता है.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"सक्रिय कुंजी केंद्रिकरण व्यवहार\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"सक्रिय कुंजी कभी अक्षम न करें\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"जब मुख्य विंडो केंद्रित हो तो सक्रिय कुंजी अक्षम करें\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"जब मुख्य विंडो जब केंद्रित न हो तो सक्रिय कुंजी अक्षम करें\"\nBasic.Settings.Advanced.AutoRemux=\"स्वचालित रूप से %1 मेें रीमक्स करें\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(mkv के रूप में रिकॉर्ड करें)\"\nBasic.AdvAudio=\"उन्नत ऑडियो गुण\"\nBasic.AdvAudio.ActiveOnly=\"केवल सक्रिय स्रोत\"\nBasic.AdvAudio.Name=\"नाम\"\nBasic.AdvAudio.Volume=\"वॉल्यूम\"\nBasic.AdvAudio.VolumeSource=\"'%1' के लिए वॉल्यूम\"\nBasic.AdvAudio.Mono=\"मोनो\"\nBasic.AdvAudio.MonoSource=\"'%1' के लिए मोनो डाउनमिक्स\"\nBasic.AdvAudio.Balance=\"बैलेंस\"\nBasic.AdvAudio.BalanceSource=\"'%1' के लिए बैलेंस\"\nBasic.AdvAudio.SyncOffset=\"सिंक ऑफसेट\"\nBasic.AdvAudio.SyncOffsetSource=\"'%1' के लिए सिंक ऑफसेट'\"\nBasic.AdvAudio.Monitoring=\"ऑडियो निगरानी\"\nBasic.AdvAudio.Monitoring.None=\"मॉनिटर बंद\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"केवल मॉनिटर (आउटपुट चुप करें)\"\nBasic.AdvAudio.Monitoring.Both=\"मॉनिटर और आउटपुट\"\nBasic.AdvAudio.MonitoringSource=\"'%1' के लिए ऑडियो मॉनिटरिंग\"\nBasic.AdvAudio.AudioTracks=\"ट्रैक\"\nBasic.Settings.Hotkeys=\"सक्रिय कुंजी\"\nBasic.Settings.Hotkeys.Pair=\"'%1' के साथ साझा किए गए कुंजी संयोजन टॉगल के रूप में कार्य करते हैं\"\nBasic.Settings.Hotkeys.Filter=\"फ़िल्टर\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"सक्रिय कुंजी से फ़िल्टर करें\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"यह सक्रिय कुंजी एक या अधिक अन्य क्रियाओं द्वारा साझा की जाती है, विरोध दिखाने के लिए क्लिक करें\"\nBasic.Settings.Hotkeys.PleaseWait=\"हॉटकी लोड हो रहे हैं, कृपया प्रतीक्षा करें...\"\nBasic.Hotkeys.SelectScene=\"दृश्य पर स्विच करें\"\nBasic.SystemTray.Show=\"दिखाएं\"\nBasic.SystemTray.Hide=\"छिपाएं\"\nBasic.SystemTray.Message.Reconnecting=\"डिस्कनेक्ट किया गया. फिर से कनेक्ट हो रहा है...\"\nHotkeys.Insert=\"डालें\"\nHotkeys.Delete=\"हटाएं\"\nHotkeys.Home=\"घर\"\nHotkeys.End=\"समाप्त\"\nHotkeys.PageUp=\"एक पृष्ठ ऊपर\"\nHotkeys.PageDown=\"एक पृष्ठ नीचे\"\nHotkeys.NumLock=\"Num लॉक\"\nHotkeys.ScrollLock=\"स्क्रॉल लॉक\"\nHotkeys.CapsLock=\"कैप्स लॉक\"\nHotkeys.Backspace=\"बैकस्पेस\"\nHotkeys.Print=\"प्रिंट करें\"\nHotkeys.Pause=\"विराम\"\nHotkeys.Left=\"बाएं\"\nHotkeys.Right=\"दाएँ\"\nHotkeys.Up=\"ऊपर\"\nHotkeys.Down=\"नीचे\"\nHotkeys.Super=\"सुपर\"\nHotkeys.Menu=\"मेन्यू\"\nHotkeys.Space=\"स्पेस\"\nHotkeys.NumpadMultiply=\"Numpad गुणा\"\nHotkeys.NumpadDivide=\"Numpad भाग\"\nHotkeys.NumpadAdd=\"Numpad जोड़\"\nHotkeys.NumpadSubtract=\"Numpad घटाव\"\nHotkeys.NumpadDecimal=\"Numpad दशमलव\"\nHotkeys.AppleKeypadNum=\"%1 (कीपैड)\"\nHotkeys.AppleKeypadMultiply=\"* (कीपैड)\"\nHotkeys.AppleKeypadDivide=\"/ (कीपैड)\"\nHotkeys.AppleKeypadAdd=\"+ (कीपैड)\"\nHotkeys.AppleKeypadSubtract=\"- (कीपैड)\"\nHotkeys.AppleKeypadDecimal=\". (कीपैड)\"\nHotkeys.AppleKeypadEqual=\"= (कीपैड)\"\nHotkeys.MouseButton=\"माउस %1\"\nMute=\"मौन\"\nUnmute=\"मुखर\"\nPush-to-mute=\"मौन-हेतू-दबाएं\"\nPush-to-talk=\"बातचीत-हेतु-दबाएं\"\nSceneItemShow=\"'%1' दिखाएं\"\nSceneItemHide=\"'%1' छुपाएं\"\nOutputWarnings.NoTracksSelected=\"आपको कम से कम एक ट्रैक चुनना ही होगा\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"आउटपुट सेटिंग्स त्रुटि\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"सभी आउटपुट में कम से कम एक ऑडियो ट्रैक चयनित होना चाहिए.\"\nOutputWarnings.MP4Recording=\"चेतावनी: यदि फ़ाइल को अंतिम रूप नहीं दिया जा सका (जैसे कि BSODs, बत्ती गुल, आदि के परिणामस्वरूप) तो MP4/MOV में सहेजी गई रिकॉर्डिंग अप्राप्य होगी. यदि आप एक से अधिक ऑडियो ट्रैक रिकॉर्ड करना चाहते हैं, तो MKV का उपयोग करने पर विचार करें और रिकॉर्डिंग समाप्त होने के बाद MP4/MOV में रीमक्स रिकॉर्डिंग करें (फाइल → रीमक्स रिकॉर्डिंग)\"\nOutputWarnings.CannotPause=\"चेतावनी: यदि रिकॉर्डिंग एनकोडर \\\"(स्ट्रीम एनकोडर का उपयोग करें)\\\" के लिए सेट है तो रिकॉर्डिंग को रोक कर नहीं रखा जा सकता\"\nOutputWarnings.CodecIncompatible=\"असंगतता के कारण ऑडियो या वीडियो एन्कोडर चयन रीसेट किया गया था. कृपया सूची से संगत एनकोडर चुनें.\"\nCodecCompat.Incompatible=\"(%1 से असंगत)\"\nCodecCompat.CodecPlaceholder=\"एनकोडर चुनें...\"\nCodecCompat.ContainerPlaceholder=\"प्रारूप चुनें...\"\nCodecCompat.CodecMissingOnExit.Title=\"कोई एन्कोडर नहीं चुना गया\"\nCodecCompat.CodecMissingOnExit.Text=\"कम से कम एक वीडियो या ऑडियो एन्कोडर सेट नहीं है. कृपया रिकॉर्डिंग और स्ट्रीमिंग दोनों के लिए एन्कोडर चुनना सुनिश्चित करें.\"\nCodecCompat.ContainerMissingOnExit.Title=\"कोई प्रारूप नहीं चुना गया\"\nCodecCompat.ContainerMissingOnExit.Text=\"कोई रिकॉर्डिंग प्रारूप नहीं चुना गया है. कृपया एक रिकॉर्डिंग प्रारूप चुनें जो चयनित स्ट्रीम एन्कोडर के साथ संगत हो.\"\nFinalScene.Title=\"दृश्य हटाएं\"\nFinalScene.Text=\"कम से कम एक दृश्य होना चाहिए.\"\nNoSources.Title=\"कोई स्रोत नहीं\"\nNoSources.Text=\"ऐसा लगता है कि आपने अभी तक कोई वीडियो स्रोत नहीं जोड़ा है, इसलिए आप केवल एक रिक्त स्क्रीन आउटपुट करेंगे. क्या आप सही में ऐसा करना चाहते हैं?\"\nNoSources.Text.AddSource=\"आप किसी भी समय मुख्य विंडो में स्रोत बॉक्स के नीचे + आइकन पर क्लिक करके स्रोत जोड़ सकते हैं.\"\nNoSources.Label=\"आपके पास कोई स्रोत नहीं है.\\nनीचे + बटन पर क्लिक करें,\\nऔर एक जोड़ने के लिए यहां दायाँ क्लिक करें.\"\nChangeBG=\"रंग निर्दिष्ट करें\"\nCustomColor=\"निर्मित रंग\"\nBrowserSource.EnableHardwareAcceleration=\"ब्राउज़र स्रोत हार्डवेयर त्वरण सक्षम करें\"\nAbout=\"परिचय\"\nAbout.Info=\"OBS Studio एक स्वतंत्र और खुले स्रोत वाला वीडियो रिकॉर्डिंग और लाइव स्ट्रीमिंग सॉफ्टवेयर है.\"\nAbout.Donate=\"योगदान करें\"\nAbout.GetInvolved=\"शामिल हो जाइए\"\nAbout.Authors=\"लेखकगण\"\nAbout.License=\"लाइसेंस\"\nAbout.Error=\"त्रुटि! फ़ाइल को पढ़ा नहीं जा सका.\\n\\nजाएँ: %1\"\nAbout.Contribute=\"OBS प्रोजेक्ट का समर्थन करें\"\nAddUrl.Title=\"URL के माध्यम से स्रोत जोड़ें\"\nAddUrl.Text=\"आपने एक URL को OBS में खींच लिया है. यह स्वचालित रूप से लिंक को एक स्रोत के रूप में जोड़ देगा. आगे चलें?\"\nResizeOutputSizeOfSource=\"आउटपुट का आकार बदलें (स्रोत का आकार)\"\nResizeOutputSizeOfSource.Text=\"मूल और आउटपुट रिज़ॉल्यूशन को वर्तमान स्रोत के आकार में बदल दिया जाएगा.\"\nResizeOutputSizeOfSource.Continue=\"क्या आप आगे बढ़ना चाहते हैं?\"\nPreviewTransition=\"संक्रांति पूर्व दर्शन\"\nImporter=\"दृश्य संग्रह आयातक\"\nImporter.SelectCollection=\"एक दृश्य संग्रह चुनें\"\nImporter.Collection=\"दृश्य संग्रह\"\nImporter.HelpText=\"OBS या अन्य समर्थित प्रोग्राम से संग्रह आयात करने के लिए इस विंडो में फ़ाइलों को जोड़ें.\"\nImporter.Path=\"संग्रह पथ\"\nImporter.Program=\"ऐप्लिकेशन का पता चला\"\nImporter.AutomaticCollectionPrompt=\"दृश्य संग्रह के लिए स्वचालित रूप से खोज करें\"\nImporter.AutomaticCollectionText=\"ओबीएस स्वचालित रूप से समर्थित तृतीय-पक्ष प्रोग्रामों से आयात योग्य दृश्य संग्रह ढूंढ सकता है. क्या आप चाहते हैं कि OBS आपके लिए स्वचालित रूप से संग्रह ढूंढे?\\n\\nआप इसे बाद में सेटिंग > सामान्य > आयातक में जाकर बदल सकते हैं.\"\nImporter.SelectFile=\"ब्राउज़ करें...\"\nRestart=\"पुनः आरंभ करें\"\nNeedsRestart=\"OBS स्टूडियो को पुनरारंभ करने की आवश्यकता है. क्या आप अभी पुनः आरंभ करना चाहते हैं?\"\nLoadProfileNeedsRestart=\"प्रोफ़ाइल में ऐसी सेटिंग्स हैं जिनके लिए OBS को पुन: आरंभ करने की आवश्यकता है :\\n%1\\n\\nक्या आप इन सेटिंग्स के प्रभावी होने के लिए OBS को पुन: आरंभ करना चाहते हैं?\"\nContextBar.NoSelectedSource=\"कोई स्रोत नहीं चुना गया\"\nContextBar.MediaControls.PlayMedia=\"मीडिया चलाएं\"\nContextBar.MediaControls.PauseMedia=\"मीडिया को रोकें\"\nContextBar.MediaControls.StopMedia=\"मीडिया रोक दें\"\nContextBar.MediaControls.RestartMedia=\"मीडिया पुन: चलाएं\"\nContextBar.MediaControls.PlaylistNext=\"प्लेलिस्ट में अगला\"\nContextBar.MediaControls.PlaylistPrevious=\"प्लेलिस्ट में पिछला\"\nContextBar.MediaControls.BlindSeek=\"मीडिया Seek Widget\"\nYouTube.Auth.Ok=\"प्राधिकरण सफलतापूर्वक पूर्ण.\\nअब आप इस पृष्ठ को बंद कर सकते हैं.\"\nYouTube.Auth.NoCode=\"प्राधिकरण प्रक्रिया संपूर्ण नहीं हुई.\"\nYouTube.Auth.NoChannels=\"चयनित खाते पर कोई चैनल उपलब्ध नहीं है\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube उपयोगकर्ता प्राधिकरण\"\nYouTube.Auth.WaitingAuth.Text=\"कृपया अपने बाहरी ब्राउज़र में प्राधिकरण पूर्ण करें.<br>यदि बाहरी ब्राउज़र नहीं खुलता है, तो इस लिंक का अनुसरण करें और प्राधिकरण पूर्ण करें:<br>%1\"\nYouTube.AuthError.Text=\"%1 : के लिए चैनल जानकारी लोड करने में विफल.\"\nYouTube.Actions.WindowTitle=\"यूट्यूब प्रसारण सेटअप - चैनल : %1\"\nYouTube.Actions.CreateNewEvent=\"नया प्रसारण बनाएं\"\nYouTube.Actions.ChooseEvent=\"उपलब्ध प्रसारण का चयन करें\"\nYouTube.Actions.Title=\"शीर्षक*\"\nYouTube.Actions.MyBroadcast=\"मेरा प्रसारण\"\nYouTube.Actions.Description=\"विवरण\"\nYouTube.Actions.Privacy=\"गोपनीयता*\"\nYouTube.Actions.Privacy.Private=\"व्यक्तिगत\"\nYouTube.Actions.Privacy.Public=\"सार्वजनिक\"\nYouTube.Actions.Privacy.Unlisted=\"असूचीबद्ध\"\nYouTube.Actions.Category=\"श्रेणी\"\nYouTube.Actions.Thumbnail=\"थम्बनेल\"\nYouTube.Actions.Thumbnail.SelectFile=\"फ़ाइल चुनें...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"कोई फाइल नहीं चुनी गई\"\nYouTube.Actions.Thumbnail.ClearFile=\"साफ़ करें\"\nYouTube.Actions.MadeForKids=\"क्या यह वीडियो बच्चों के लिए बनाया गया है?*\"\nYouTube.Actions.MadeForKids.Yes=\"हाँ, यह बच्चों के लिए बना है\"\nYouTube.Actions.MadeForKids.No=\"नहीं, यह बच्चों के लायक नहीं है\"\nYouTube.Actions.AdditionalSettings=\"अतिरिक्त सेटिंग\"\nYouTube.Actions.Latency=\"विलंबता\"\nYouTube.Actions.Latency.Normal=\"सामान्य\"\nYouTube.Actions.Latency.Low=\"निम्न\"\nYouTube.Actions.Latency.UltraLow=\"निम्नतम\"\nYouTube.Actions.EnableAutoStart=\"ऑटो-स्टार्ट सक्षम करें\"\nYouTube.Actions.EnableAutoStop=\"ऑटो-स्टॉप सक्षम करें\"\nYouTube.Actions.AutoStartStop.TT=\"इंगित करता है कि क्या यह निर्धारित प्रसारण स्वचालित रूप से प्रारंभ होना चाहिए\"\nYouTube.Actions.EnableDVR=\"DVR सक्षम करें\"\nYouTube.Actions.360Video=\"360 वीडियो\"\nYouTube.Actions.ScheduleForLater=\"बाद में रखें\"\nYouTube.Actions.RememberSettings=\"ये चीज़ें याद रखें\"\nYouTube.Actions.Create_Ready=\"प्रसारण बनाएं\"\nYouTube.Actions.Create_GoLive=\"प्रसारण बनाएं और स्ट्रीमिंग शुरू करें\"\nYouTube.Actions.Choose_Ready=\"प्रसारण चुनें\"\nYouTube.Actions.Choose_GoLive=\"प्रसारण चुनें और स्ट्रीमिंग शुरू करें\"\nYouTube.Actions.Create_Schedule=\"प्रसारण निर्धारित करें\"\nYouTube.Actions.Create_Schedule_Ready=\"समय निर्धारित करें और प्रसारण चुनें\"\nYouTube.Actions.Dashboard=\"YouTube स्टूडियो खोलें\"\nYouTube.Actions.Error.Title=\"लाइव प्रसारण निर्माण त्रुटि\"\nYouTube.Actions.Error.Text=\"YouTube पहुँच में त्रुटि '%1'.<br/>विस्तृत त्रुटि विवरण <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> पर मिल सकता है\"\nYouTube.Actions.Error.General=\"YouTube पहुंच त्रुटि. कृपया नेटवर्क कनेक्शन या YouTube सर्वर पहुंच की जांच करें.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"प्रसारण निर्माण त्रुटि '%1'.<br/>विस्तृत त्रुटि विवरण <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> पर मिल सकता है\"\nYouTube.Actions.Error.NoStreamCreated=\"कोई स्ट्रीम नहीं बनी. कृपया खाते को फिर से लिंक करें.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API त्रुटि. अधिक जानकारी के लिए कृपया लॉग फ़ाइल देखें.\"\nYouTube.Actions.Error.BroadcastNotFound=\"चयनित प्रसारण नहीं मिला.\"\nYouTube.Actions.Error.FileMissing=\"चुनी गई फ़ाइल उपलब्ध नहीं है.\"\nYouTube.Actions.Error.FileOpeningFailed=\"चयनित फ़ाइल को खोलने में विफल.\"\nYouTube.Actions.Error.FileTooLarge=\"चयनित फ़ाइल बहुत बड़ी है (सीमा: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"प्रसारण संक्रांति विफल रहा: %1<br/><br/>अगर यह गड़बड़ी बनी रहती है तो <a href='https://studio.youtube.com/video/%2/livestreaming'>YouTube स्टूडियो में प्रसारण खोलें</a> और मैन्युअल रूप से प्रयास करें.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"प्रसारण परीक्षण चरण में संक्रमण कर रहा है, इसमें कुछ समय लग सकता है. कृपया 10-30 सेकंड में पुन: प्रयास करें.\"\nYouTube.Actions.EventsLoading=\"कार्यक्रमों की सूची लोड हो रही है...\"\nYouTube.Actions.EventCreated.Title=\"ईवेंट बनाया गया.\"\nYouTube.Actions.EventCreated.Text=\"ईवेंट सफलतापूर्वक बनाया गया.\"\nYouTube.Actions.Stream=\"स्ट्रीम\"\nYouTube.Actions.Stream.ScheduledFor=\"%1 के लिए निर्धारित\"\nYouTube.Actions.Stream.Resume=\"बाधित स्ट्रीम फिर से शुरू करें\"\nYouTube.Actions.Stream.YTStudio=\"YouTube स्टूडियो द्वारा स्वचालित रूप से बनाया गया\"\nYouTube.Actions.Notify.CreatingBroadcast=\"एक नया लाइव प्रसारण बनाया जा रहा है, कृपया प्रतीक्षा करें...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"मैन्युअल प्रारंभ आवश्यक\"\nYouTube.Actions.AutoStartStreamingWarning=\"इस ईवेंट के लिए ऑटो-स्टार्ट अक्षम है, अपना प्रसारण शुरू करने के लिए \\\"लाइव जाएं\\\" पर क्लिक करें.\"\nYouTube.Actions.AutoStopStreamingWarning=\"आप फिर से कनेक्ट नहीं कर पाएंगे.<br>आपकी स्ट्रीम रुक जाएगी और आप लाइव नहीं रहेंगे.\"\nYouTube.Errors.liveStreamingNotEnabled=\"चयनित YouTube चैनल पर लाइव स्ट्रीमिंग सक्षम नहीं है.<br/><br/>अधिक जानकारी के लिए <a href='https://www.youtube.com/features'>youtube.com/features</a> देखें.\"\nYouTube.Errors.livePermissionBlocked=\"लाइव स्ट्रीमिंग चयनित YouTube चैनल पर उपलब्ध नहीं है.<br/>कृपया ध्यान दें कि लाइव स्ट्रीमिंग को आपके चैनल सेटिंग में सक्षम करने के बाद इसे उपलब्ध होने में 24 घंटे तक का समय लग सकता है.<br/><br/>देखें <a href='https://www.youtube.com/features'>youtube.com/features</a> विवरण के लिए.\"\nYouTube.Errors.errorExecutingTransition=\"बैकएंड त्रुटि के कारण संक्रमण विफल रहा. कृपया कुछ सेकंड में पुन: प्रयास करें.\"\nYouTube.Errors.errorStreamInactive=\"YouTube को आपकी स्ट्रीम के लिए डेटा नहीं मिल रहा है. कृपया अपने कॉन्फ़िगरेशन की जाँच करें और पुनः प्रयास करें.\"\nYouTube.Errors.invalidTransition=\"संक्रमण का प्रयास अमान्य था. यह स्ट्रीम के पिछले संक्रमण के समाप्त न होने के कारण हो सकता है. कृपया कुछ सेकंड प्रतीक्षा करें और पुनः प्रयास करें.\"\nYouTube.DocksRemoval.Title=\"लीगेसी यूट्यूब ब्राउज़र डॉक्स साफ़ करें\"\nYouTube.DocksRemoval.Text=\"इन ब्राउज़र डॉक को अप्रचलित मानकर हटा दिया जाएगा:\\n\\n%1\\nइसके बजाय \\\"डॉक्स/यूट्यूब लाइव कंट्रोल रूम\\\" का उपयोग करें.\"\nConfigDownload.WarningMessageTitle=\"चेतावनी\"\nFailedToStartStream.MissingConfigURL=\"वर्तमान सेवा के लिए कोई कॉन्फिग URL उपलब्ध नहीं है\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"कस्टम  RTMP URL निर्दिष्ट नहीं है\"\nFailedToStartStream.InvalidCustomConfig=\"अमान्य कस्टम कॉन्फ़िग\"\nFailedToStartStream.MissingCanvas=\"एक कॉन्फ़िगर किया गया अतिरिक्त कैनवास अनुपस्थित है\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"मल्टीट्रैक वीडियो सेवा निर्माण में विफल\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"मल्टीट्रैक वीडियो  RTMP आउटपुट बनाने में विफल\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC उपलब्ध नहीं है.\\n\\nएनकोडर प्रकार '%1' ढूंढने में विफल\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"वीडियो एनकोडर '%1' बनाने में विफल (प्रकार: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"एनकोडर '%1' बनाते समय OBS वीडियो जानकारी प्राप्त करने में विफल (प्रकार: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"ऑडियो एन्कोडर बनाने में विफल\"\nFailedToStartStream.NoRTMPURLInConfig=\"कॉन्फ़िगरेशन में स्ट्रीम लक्ष्य RTMP(S) URL शामिल नहीं है\"\nFailedToStartStream.FallbackToDefault=\"%1 का उपयोग करके स्ट्रीम प्रारंभ करना विफल रहा; क्या आप एकल एन्कोड सेटिंग्स का उपयोग करके पुनः प्रयास करना चाहते हैं?\"\nFailedToStartStream.ConfigRequestFailed=\"%1 से कॉन्फ़िगरेशन नहीं लाया जा सका<br><br>HTTP त्रुटि: %2\"\nFailedToStartStream.WarningUnknownStatus=\"अज्ञात स्थिति मान '%1' प्राप्त हुआ\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nक्या आप %1 के बिना स्ट्रीमिंग जारी रखना चाहते हैं?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nक्या आप  स्ट्रीमिंग जारी रखना चाहते हैं?\"\nFailedToStartStream.MissingEncoderConfigs=\"गो लाइव कॉन्फिग में एनकोडर कॉन्फिगरेशन शामिल नहीं था\"\nFailedToStartStream.StatusMissingHTML=\"गो लाइव अनुरोध ने एक अनिर्दिष्ट त्रुटि लौटा दी\"\nFailedToStartStream.NoConfigSupplied=\"अनुपलब्ध कॉन्फ़िग\"\nMultitrackVideo.Info=\"%1 एकाधिक वीडियो गुणवत्ता को एन्कोड करने और भेजने के लिए आपकी सेटिंग्स को स्वचालित रूप से अनुकूलित करता है. इस विकल्प को चुनने से आपके कंप्यूटर और सॉफ़्टवेयर सेटअप के बारे में %2 जानकारी भेजी जाएगी.\"\nMultitrackVideo.IncompatibleSettings.Title=\"असंगत सेटिंग्स\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 वर्तमान में इसके साथ संगत नहीं है:\\n\\n%2\\n%1 के साथ स्ट्रीमिंग जारी रखने के लिए, असंगत सेटिंग्स अक्षम करें:\\n\\n%3\\nऔर दुबारा स्ट्रीमिंग शुरू करें.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"इस स्ट्रीम के लिए अक्षम करें और स्ट्रीमिंग प्रारंभ करें\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"सेटिंग्स अपडेट करें और स्ट्रीमिंग शुरू करें\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 वर्तमान में [ऑडियो → सामान्य → चैनल] के साथ संगत नहीं है '%2', %3 पर सेट किया गया\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[ऑडियो → सामान्य → चैनल] को '%1' पर सेट करने की आवश्यकता है\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 को [ऑडियो → सामान्य → चैनल] के लिए कई अलग-अलग सेटिंग्स की आवश्यकता है\"\nBasic.OpenPluginManager=\"प्लगइन प्रबंधक\"\nPluginManager=\"प्लगइन प्रबंधक\"\nPluginManager.Restart=\"प्लगइन परिवर्तन लागू करने के लिए OBS स्टूडियो को पुनरारंभ किया जाना चाहिए\"\nPluginManager.MissingPlugin=\"[प्लगइन नहीं मिला]\"\nPluginManager.Section.Discover=\"ब्राउज़\"\nPluginManager.Section.Manage=\"इंस्टॉल हुआ\"\nPluginManager.Section.Updates=\"अपडेट\"\nPluginManager.Section.Manage.Title=\"सक्षम प्लगइन्स प्रबंधित करें\"\n"
  },
  {
    "path": "frontend/data/locale/hr-HR.ini",
    "content": "Language=\"hrvatski\"\nOK=\"U redu\"\nApply=\"Primijeni\"\nCancel=\"Odustani\"\nClose=\"Zatvori\"\nSave=\"Spremi\"\nDiscard=\"Odbaci\"\nDisable=\"Onemogući\"\nYes=\"Da\"\nNo=\"Ne\"\nAdd=\"Dodaj\"\nRemove=\"Ukloni\"\nRename=\"Preimenuj\"\nInteract=\"Interakcija\"\nFilters=\"Filteri\"\nProperties=\"Svojstva\"\nMoveUp=\"Premjesti gore\"\nMoveDown=\"Premjesti dolje\"\nSettings=\"Postavke\"\nDisplay=\"Zaslon\"\nName=\"Naziv\"\nExit=\"Izlaz\"\nMixer=\"Audio Mikser\"\nBrowse=\"Pretraži\"\nDroppedFrames=\"Ispušteni kadrovi %1 (%2%)\"\nProjector.Open.Program=\"Otvori programski projektor\"\nProjector.Open.Preview=\"Otvori pregled projektora\"\nProjector.Open.Scene=\"Projektor otvorene scene\"\nProjector.Open.Source=\"Projektor otvorenog koda\"\nProjector.Open.Multiview=\"Otvori višestruki prikaz\"\nProjector.Display=\"Prikaz: %1\"\nProjector.Window=\"Novi prozor\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Scena: %1\"\nProjector.Title.Source=\"Izvor: %1\"\nProjector.Title.Multiview=\"Višestruki prikaz\"\nProjector.ResizeWindowToContent=\"Prilagodi prozor sadržaju\"\nClear=\"Očisti\"\nRevert=\"Povrati\"\nShow=\"Prikaži\"\nHide=\"Sakrij\"\nUnhideAll=\"Otkrij sve\"\nUntitled=\"Nenaslovljeno\"\nNew=\"Novi\"\nDuplicate=\"Udvostruči\"\nEnable=\"Omogući\"\nDisableOSXVSync=\"Onemogući macOS V-Sync\"\nResetOSXVSyncOnExit=\"Poništi macOS V-Sync na izlazu\"\nHighResourceUsage=\"Kodiranje je preopterećeno! Razmislite o smanjenju postavki videozapisa ili korištenju bržeg kodiranja.\"\nTransition=\"Prijelaz\"\nQuickTransitions=\"Brzi prijelazi\"\nFadeToBlack=\"Prijelaz u crnu\"\nLeft=\"Lijevo\"\nRight=\"Desno\"\nTop=\"Vrh\"\nBottom=\"Dno\"\nReset=\"Poništi\"\nHours=\"Sati\"\nMinutes=\"Minuta\"\nSeconds=\"Sekundi\"\nDeprecated=\"Zastarjelo\"\nReplayBuffer=\"Međuspremnik ponovne reprodukcije\"\nImport=\"Uvezi\"\nExport=\"Izvezi\"\nCopy=\"Kopiraj\"\nPaste=\"Zalijepi\"\nPasteReference=\"Zalijepi (Referencu)\"\nPasteDuplicate=\"Zalijepi (Duplikat)\"\nRemuxRecordings=\"Prepakiranje snimaka\"\nNext=\"Dalje\"\nBack=\"Natrag\"\nDefaults=\"Zadano\"\nRestoreDefaults=\"Zadano\"\nHideMixer=\"Sakrij u Mikser\"\nTransitionOverride=\"Nadjačaj prijelaz\"\nShowTransition=\"Prikaži prijelaz\"\nHideTransition=\"Sakrij prijelaz\"\nNone=\"Nijedno\"\nStudioMode.Preview=\"Pregled\"\nStudioMode.PreviewSceneName=\"Pregled: %1\"\nStudioMode.ProgramSceneName=\"Program %1\"\nShowInMultiview=\"Prikaži u Višestrukom prikazu\"\nVerticalLayout=\"Okomiti raspored\"\nGroup=\"Grupa\"\nDoNotShowAgain=\"Ne prikazuj ponovno\"\nDefault=\"(Zadano)\"\nCalculating=\"Izračunavanje...\"\nFullscreen=\"Cijeli zaslon\"\nWindowed=\"U prozoru\"\nRefreshBrowser=\"Osvježi\"\nAspectRatio=\"Omjer slike <b>%1:%2</b>\"\nLockVolume=\"Zaključaj glasnoću\"\nLogViewer=\"Prikaz zapisnika\"\nShowOnStartup=\"Prikaži pri pokretanju\"\nOpenFile=\"Otvori datoteku\"\nAddScene=\"Dodaj scenu\"\nAddSource=\"Dodaj izvor\"\nRemoveScene=\"Ukloni odabranu scenu\"\nRemoveSource=\"Ukloni odabrane izvore\"\nMoveSceneUp=\"Premjesti scenu gore\"\nMoveSceneDown=\"Premjesti  scenu dolje\"\nMoveSourceUp=\"Premjesti izvore gore\"\nMoveSourceDown=\"Premjesti izvore dolje\"\nSourceProperties=\"Otvori Svojstva izvora\"\nSourceFilters=\"Filtri otvorenog izvora\"\nMixerToolbarMenu=\"Audio mikser jelovnik\"\nSceneFilters=\"Otvorite Filtere Scene\"\nList=\"Popis\"\nGrid=\"Rešetka\"\nAutomatic=\"Automatski\"\nComingSoon=\"Uskoro dolazi\"\nPluginsFailedToLoad.Title=\"Greška u učitavanju plugin\"\nPluginsFailedToLoad.Text=\"Naredni OBS plugins nisu uspjelo utvarati:\\n\\n%1\\nMolim vas ažuriranje ili sklanjajte ovi plugins.\"\nAlreadyRunning.Title=\"OBS je već pokrenut\"\nAlreadyRunning.Text=\"OBS je već pokrenut! Osim ako to niste namjeravali učiniti, isključite sve postojeće OBS instance prije nego što pokušate pokrenuti novu instancu. Ako imate OBS postavljen na minimiziraj u traku sustava, provjerite radi li još uvijek tamo.\"\nAlreadyRunning.LaunchAnyway=\"Svejedno pokreni\"\nCrashHandling.Dialog.Title=\"Rušenje programa OBS Studio\"\nCrashHandling.Buttons.LaunchSafe=\"Pokreni u sigurnom načinu\"\nCrashHandling.Buttons.LaunchNormal=\"Pokreni u normalnom načinu\"\nCrashHandling.Errors.UploadJSONError=\"Prilikom prijenosa posljednjeg zapisnika o rušenju dogodila se pogreška. Ponovno pokušajte kasnije.\"\nSafeMode.Restart=\"Želite li ponovno pokrenuti OBS u sigurnom načinu (strani dodatci, skripte i WebSocketi onemogućeni)?\"\nSafeMode.RestartNormal=\"Želite li ponovno pokrenuti OBS u normalnom načinu?\"\nChromeOS.Title=\"Nepodržana platforma\"\nChromeOS.Text=\"OBS čini se trčanje unutar ChromeOS spremnika. Ova platforma je bez podrške.\"\nWine.Title=\"Wine otkriveno\"\nWine.Text=\"Pokretanje OBS-a u Wineu nije podržano, a mnoge značajke poput snimanja ili izvora uređaja neće raditi ili će raditi samo s ograničenim kapacitetom.<br><br> Preporučuje se pokretanje izvorne verzije OBS-a, na primjer <a href='https://flathub.org/apps/details/com.obsproject.Studio'>naše Flatpak verzije</a> ili paketa vašeg operativnog sustava.\"\nDockCloseWarning.Title=\"Zatvaranje usidrenog prozora\"\nDockCloseWarning.Text=\"Opravo ste zatvorili usidreni prozor. Ukoliko ga želite ponovno prikazati, otvorite izbornik Sidrišta u traci izbornika.\"\nExtraBrowsers=\"Prilagođena sidrišta za preglednik\"\nExtraBrowsers.Info=\"Dodajte dokove dajući im ime i URL, onda kliknite Apply ili Close da možete otvarati dokove. Možete dodati ili ukloniti dokove u bilo koje vrijeme.\"\nExtraBrowsers.DockName=\"Naziv doka\"\nAuth.Authing.Title=\"Autentikacija...\"\nAuth.Authing.Text=\"Autentikacija na %1, molimo pričekajte...\"\nAuth.AuthFailure.Title=\"Greška kod autentikacije\"\nAuth.AuthFailure.Text=\"Greška autentikacije s %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Potrebna autentikacija\"\nAuth.InvalidScope.Text=\"Uvjeti autentičnost za %1 su se promijenili. Neke značajke možda neće biti dostupne.\"\nAuth.LoadingChannel.Title=\"Učitavam informacije o kanalu...\"\nAuth.LoadingChannel.Text=\"Učitavanje informacija kanala za %1, molim vaš čekajte...\"\nAuth.LoadingChannel.Error=\"Nije mogao dobiti informacije o kanalu.\"\nAuth.ChannelFailure.Title=\"Učitavanje kanala nije uspjelo\"\nAuth.ChannelFailure.Text=\"Nije uspio učitavanje informacija o kanalu za %1\\n\\n%2: %3\"\nAuth.Chat=\"Ćaskanje\"\nAuth.StreamInfo=\"Informacije o stream\"\nTwitchAuth.Stats=\"Twitch statistike\"\nTwitchAuth.Feed=\"Aktivnost od Twitch feed\"\nTwitchAuth.TwoFactorFail.Title=\"Nije moguće upisati ključ stream\"\nTwitchAuth.TwoFactorFail.Text=\"OBS se nije uspio povezati s vašim Twitch računom. Provjerite je li u <a href='https://www.twitch.tv/settings/security'>sigurnosnim postavkama vašeg Twitch računa</a> uključena provjera identiteta u dva koraka.\"\nRestreamAuth.Channels=\"Restream-kanali\"\nCopy.Filters=\"Kopiraj filtere\"\nPaste.Filters=\"Zalijepi filtere\"\nBrowserPanelInit.Title=\"Inicijalizacija Browser...\"\nBrowserPanelInit.Text=\"Inicijalizacija Browser, molim pričekajte...\"\nBandwidthTest.Region=\"Regija\"\nBandwidthTest.Region.US=\"Sjedinjene Američke Države\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Azija\"\nBandwidthTest.Region.Other=\"Ostalo\"\nBasic.AutoConfig=\"Čarobnjak za automatske konfiguraciju\"\nBasic.AutoConfig.ApplySettings=\"Primjeni postavke\"\nBasic.AutoConfig.StartPage=\"Informacije o korištenju\"\nBasic.AutoConfig.StartPage.SubTitle=\"Navedite što želite da koristite program za\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimiziraj za streaming, snimanje je drugo\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimiziraj samo za snimanje, neću prenositi uživo\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Ja samo ću koristit virtualnu kameru\"\nBasic.AutoConfig.VideoPage=\"Video postavke\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Navedite željene postavke videozapisa koje želite koristiti\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Koristi trenutačno (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Prikaz %1 (0}3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Koristi trenutačnu (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ili 30, ali radije 60 kada je to moguće\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ili 30, ali radije visoke rezolucije\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Pažnja: Rezolucija platna (baze) nije nužno ista kao rezolucija kojom ćete prosljeđivati ili snimati. Vaš stvarni tok / snimanje rezolucija može biti smanjena od platna rezolucije smanjiti korištenje resursa ili bitrate zahtjeve.\"\nBasic.AutoConfig.StreamPage=\"Informacije o stream\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Molim vas, unesite svoje Informacije o stream\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Poveži Account (preporučeno)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Isključite Account\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Isključite Account?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Ova promjena će se primijeniti odmah. Jeste li sigurni da želite isključiti tvoj Account?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Uzmi ključ za Stream\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Više informacija\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Upotrijebi svoj Stream ključ\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Upotrijebi svoj Stream ključ (Napredno)\"\nBasic.AutoConfig.StreamPage.Service=\"Usluga\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Prikaži sve...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Prilagođeno...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Stream ključ\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: unesite lozinku za šifriranje.\\nRTMP: unesite ključ koji nudi usluga.\\nSRT: unesite streamid ako ga usluga koristi.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Encoder ključ\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Token nositelj\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Poveži Account\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Procijenite brzinu prijenosa sa testom propusnosti (može potrajati nekoliko minuta)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Više volim hardversko kodiranje\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Hardversko kodiranje eliminira većinu upotrebe CPU, ali može zahtijevati veću brzinu prijenosa za postizanje iste razine kvalitete.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Stream upozorenje\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Test mrežne propusnosti će na vašem kanalu streamati nasumične videopodatke bez zvuka. Ako ste u mogućnosti, preporučeno je privremeno isključiti spremanje videozapisa od streamova i postaviti stream kao privatan do završetka testa. Nastaviti?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Testiraj %1\"\nBasic.AutoConfig.TestPage=\"Završni rezultat\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Program trenutno provodi niz testova kako bi procjenio idealne postavke\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testiranje završeno\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Provođenje testa propusnosti, ovo bi moglo potrajati nekoliko minuta...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Za protokol te usluge nije pronađen nijedan izlaz\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Povezivanje na: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Ne može se spojiti na nijedan server. Provjerite jeste li povezani na internet i pokušajte kasnije.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testiranje mrežne propusnosti za: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testiranje kodiranja streama. Ovo bi moglo potrajati...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testiranje kodiranja za snimanje. Ovo bi moglo potrajati...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Ne uspjelo pokretanje kodiranja\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testiranje %1 × %2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Koder Streama\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Greška pri kodiranju\"\nBasic.AutoConfig.TestPage.Result.Header=\"Program je utvrdio da su ove predložene postavke idealne za tebe:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Za korištenje ovih postavki, kliknite Primijeni postavke. Za ponovnu konfiguraciju čarobnjaka i ponovnog pokušaja, kliknite Natrag. Za ručnu konfiguraciju postavki, kliknite Poništi i otvorite Postavke.\"\nBasic.AutoConfig.Info=\"Čarobnjak za automatsku konfiguraciju odredit će najbolje postavke na temelju specifikacija vašeg računala i brzine interneta.\"\nBasic.AutoConfig.RunAnytime=\"Ovo može biti pokrenuto u bilo kojem trenutku odlaskom na izborniku Alati.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Rezolucija (skalirana) za emitiranje\"\nBasic.Stats=\"Statistika\"\nBasic.Stats.CPUUsage=\"Koristenje CPU-a\"\nBasic.Stats.HDDSpaceAvailable=\"Dostupan prostor na disku\"\nBasic.Stats.MemoryUsage=\"Korištenje memorije\"\nBasic.Stats.AverageTimeToRender=\"Prosječno vrijeme renderiranja slike\"\nBasic.Stats.SkippedFrames=\"Preskočeni okviri zbog zaostajanja kodiranja\"\nBasic.Stats.MissedFrames=\"Propuštene slike zbog zaostajanja u renderiranju\"\nBasic.Stats.Output.Stream=\"Prijenos\"\nBasic.Stats.Output.Recording=\"Snimak\"\nBasic.Stats.Status.Recording=\"Snimanje\"\nBasic.Stats.Status.Live=\"UŽIVO\"\nBasic.Stats.Status.Reconnecting=\"Ponovo povezivanje\"\nBasic.Stats.Status.Inactive=\"Neaktivno\"\nBasic.Stats.Status.Active=\"Aktivan\"\nBasic.Stats.DroppedFrames=\"Ispuštene Slike (Mreža)\"\nBasic.Stats.MegabytesSent=\"Ukupni Izlaz Podataka\"\nBasic.Stats.Bitrate=\"Stopa bitova\"\nBasic.Stats.DiskFullIn=\"Disk pun za (pribl.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 sati, %2 minute\"\nBasic.Stats.ResetStats=\"Resetiraj Statistiku\"\nResetUIWarning.Title=\"Jeste li sigurni da želite resetirati korisničko sučelje?\"\nResetUIWarning.Text=\"Ponovno postavljanje sučelja sakrit će dodatna sidrišta. Trebat ćete omogućiti prikaz tih sidrišta iz izbornika Sidrišta ako ih želite vidjeti.\\n\\nJeste li sigurni da želite ponovno postaviti sučelje?\"\nUpdater.Title=\"Novo ažuriranje dosutpno\"\nUpdater.Text=\"Novo je ažuriranje dostupno:\"\nUpdater.UpdateNow=\"Ažuriraj Sada\"\nUpdater.RemindMeLater=\"Podsjeti me kasnije\"\nUpdater.Skip=\"Preskoči Verziju\"\nUpdater.NoUpdatesAvailable.Title=\"Nema dostupnih ažuriranja\"\nUpdater.NoUpdatesAvailable.Text=\"Trenutno nema dostupnih ažuriranja\"\nUpdater.BranchNotFound.Title=\"Kanal za ažuriranje uklonjen\"\nUpdater.BranchNotFound.Text=\"Odabrani kanal ažuriranja više nije dostupan, OBS je vraćen na zadane postavke.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Provjera integriteta nije dostupna\"\nUpdater.RepairButUpdatesAvailable.Text=\"Provjera integriteta datoteke moguća je samo za najnoviju dostupnu verziju. Koristite Pomoć → Provjeri ažuriranja za provjeru i ažuriranje vaše OBS instalacije.\"\nUpdater.RepairConfirm.Title=\"Potvrdite provjeru integriteta\"\nUpdater.RepairConfirm.Text=\"Pokretanjem provjere integriteta skenirat će se vaša OBS instalacija u potrazi za oštećenjima i ponovno preuzeti oštećene/izmijenjene datoteke. To može potrajati.\\n\\nŽelite li nastaviti?\"\nUpdater.FailedToLaunch=\"Nije uspjelo pokretanje ažuriranja\"\nQuickTransitions.SwapScenes=\"Zamijeni scene pregledanja i programa nakon plrijelaza\"\nQuickTransitions.SwapScenesTT=\"Zamjenjuje scene pregleda i programa nakon tranzicije (ako izvorna scena programa još uvijek postoji).\\nOvo neće poništiti promjene koje su možda napravljene na izvornoj sceni programa.\"\nQuickTransitions.DuplicateScene=\"Udvostruči scenu\"\nQuickTransitions.DuplicateSceneTT=\"Prilikom uređivanja iste scene, omogućuje uređivanje transformacije/vidljivosti izvora bez mijenjanja izlaza programa.\\nZa uređivanje svojstava izvora bez mijenjanja izlaza programa, omogućite 'Duplicirani izvori'.\\nPromjenom ove vrijednosti resetirana je trenutna scena programa (ako još uvijek postoji).\"\nQuickTransitions.EditProperties=\"Udvostruči izvore\"\nQuickTransitions.EditPropertiesTT=\"Prilikom uređivanja iste scene, omogućuje uređivanje svojstava izvora bez mijenjanja izlaza programa.\\nOvo se može koristiti samo ako je omogućeno 'Dupliciraj scenu'.\\nOdređeni izvori (kao što su izvori snimanja ili medija) ne podržavaju ovo i ne mogu se zasebno uređivati.\\nPromjenom ove vrijednosti resetirana je trenutna scena programa (ako još uvijek postoji).\\n\\nUpozorenje: Budući da će se izvori duplicirati, to može zahtijevati dodatne sistemske ili video resurse.\"\nQuickTransitions.HotkeyName=\"Brzi prijelaz: %1\"\nBasic.AddTransition=\"Dodaj podesivi prijelaz\"\nBasic.RemoveTransition=\"Ukloni podesivi prijelaz\"\nBasic.TransitionProperties=\"Svojstva prijelaza\"\nBasic.SceneTransitions=\"Prijelazi scena\"\nBasic.TransitionDuration=\"Trajanje\"\nBasic.TogglePreviewProgramMode=\"Studijski način rada\"\nBasic.EnablePreviewProgramMode=\"Omogući studijski način\"\nBasic.DisablePreviewProgramMode=\"Onemogući studijski način\"\nUndo.Undo=\"Poništi\"\nUndo.Redo=\"Ponovi\"\nUndo.Add=\"Dodaj %1'\"\nUndo.Delete=\"Izbriši „%1”\"\nUndo.Rename=\"Preimenuj „%1”\"\nUndo.SceneCollection.Switch=\"Prebaci se na „%1”\"\nUndo.Item.Undo=\"Poništi %1\"\nUndo.Item.Redo=\"Ponovi %1\"\nUndo.Sources.Multi=\"Izbriši %1 izvora\"\nUndo.Filters=\"Promjene filtra za „%1”\"\nUndo.Filters.Paste.Single=\"Umetni filtar „%1” u „%2”\"\nUndo.Filters.Paste.Multiple=\"Kopiraj filtre od „%1” u „%2”\"\nUndo.Transform=\"Transformiraj izvor(e) u „%1”\"\nUndo.Transform.Paste=\"Umetni transformaciju u „%1”\"\nUndo.Transform.Rotate=\"Okretanje u „%1”\"\nUndo.Transform.Reset=\"Obnovi transformaciju u „%1”\"\nUndo.Transform.HFlip=\"Vodoravno preokretanje u „%1”\"\nUndo.Transform.VFlip=\"Okomito preokretanje u „%1”\"\nUndo.Transform.FitToScreen=\"Prilagodi veličini ekrana u „%1”\"\nUndo.Transform.StretchToScreen=\"Rastegni na veličinu ekrana u „%1”\"\nUndo.Transform.Center=\"Centriraj na ekran u „%1”\"\nUndo.Transform.VCenter=\"Centriraj okomito na ekran u „%1”\"\nUndo.Transform.HCenter=\"Centriraj vodoravno na ekran u „%1”\"\nUndo.Volume.Change=\"Promjena glasnoće za „%1”\"\nUndo.Volume.Mute=\"Isključi zvuk za „%1”\"\nUndo.Volume.Unmute=\"Uključi zvuk za „%1”\"\nUndo.Balance.Change=\"Promjena balansa zvuka na '%1'\"\nUndo.SyncOffset.Change=\"Promjena pomaka sinkronizacije zvuka na '%1'\"\nUndo.MonitoringType.Change=\"Promijeni nadzor zvuka na '%1'\"\nUndo.Mixers.Change=\"Promijeni audio miksere na '%1'\"\nUndo.ForceMono.On=\"Aktiviraj mono zvuk za „%1”\"\nUndo.ForceMono.Off=\"Deaktiviraj mono zvuk za „%1”\"\nUndo.Properties=\"Promjena svojstva za „%1”\"\nUndo.Scene.Duplicate=\"Dupliciraj scenu „%1”\"\nUndo.ShowTransition=\"Prikaži prijelaz za „%1”\"\nUndo.HideTransition=\"Sakrij prijelaz za „%1”\"\nUndo.ShowSceneItem=\"Prikaži „%1” u „%2”\"\nUndo.HideSceneItem=\"Sakrij „%1” u „%2”\"\nUndo.ReorderSources=\"Snimi izvore u „%1”\"\nUndo.MoveUp=\"Premjesti „%1” prema gore u „%2”\"\nUndo.MoveDown=\"Premjesti „%1” prema dolje u „%2”\"\nUndo.MoveToTop=\"Premjesti „%1” na vrh u „%2”\"\nUndo.MoveToBottom=\"Premjesti „%1” na kraj u „%2”\"\nUndo.PasteSource=\"Zalijepi izvor(e) u '%1'\"\nUndo.PasteSourceRef=\"Zalijepi izvornu referencu(e) u '%1'\"\nUndo.GroupItems=\"Grupiraj stavke u '%1'\"\nTransitionNameDlg.Text=\"Unesite ime prijelaza\"\nTransitionNameDlg.Title=\"Ime prijelaza\"\nTitleBar.SafeMode=\"SIGURNI NAČIN\"\nTitleBar.PortableMode=\"Prijenosni način\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Scene\"\nNameExists.Title=\"Ime već postoji\"\nNameExists.Text=\"Ime se već koristi.\"\nNoNameEntered.Title=\"Unesite ispravno ime\"\nNoNameEntered.Text=\"Ne možete izostaviti ime.\"\nConfirmStart.Title=\"Započni stream?\"\nConfirmStart.Text=\"Jeste li sigurni da želite započeti stream?\"\nConfirmStop.Title=\"Zaustavi stream?\"\nConfirmStop.Text=\"Jeste li sigurni da želite zaustaviti stream?\"\nConfirmStopRecord.Title=\"Prekinuti snimanje?\"\nConfirmStopRecord.Text=\"Stvarno želiš prekinuti snimanje?\"\nConfirmBWTest.Title=\"Započeti test propusnosti?\"\nConfirmBWTest.Text=\"OBS je konfiguriran u načinu rada za testiranje propusnosti. Ovaj način rada omogućuje testiranje mreže bez pokretanja uživo vašeg kanala. Nakon što završite s testiranjem, morat ćete ga onemogućiti kako bi gledatelji mogli vidjeti vaš stream.\\n\\nŽelite li nastaviti?\"\nConfirmRemove.Title=\"Potvrdi uklanjanje\"\nConfirmRemove.Text=\"Stvarno želiš ukloniti „%1”?\"\nConfirmRemove.TextMultiple=\"Jeste li sigurni da želite ukloniti %1 stavke?\"\nConfirmReset.Title=\"Ponovno postavi postavke\"\nConfirmReset.Text=\"Jeste li sigurni da želite vratiti postavke na zadane vrijednosti?\"\nOutput.StartStreamFailed=\"Pokretanje streaminga nije uspjelo\"\nOutput.StartRecordingFailed=\"Nije uspjelo započeti snimanje\"\nOutput.StartReplayFailed=\"Nije uspjelo pokretanje međuspremnika za reprodukciju\"\nOutput.StartVirtualCamFailed=\"Neuspješno pokretanje virtualne kamere\"\nOutput.StartFailedGeneric=\"Pokretanje izlaza nije uspjelo. Molimo provjerite zapisnik za detalje.\\n\\nNapomena: Ako koristite NVENC ili AMD enkodere, provjerite jesu li vaši video upravljački programi ažurirani.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Nije moguće spremiti snimke tijekom pauze\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Upozorenje: Snimke se ne mogu spremiti dok je snimanje pauzirano.\"\nOutput.ConnectFail.Title=\"Neuspješno povezivanje\"\nOutput.ConnectFail.BadPath=\"Neispravna putanja ili URL veze. Provjerite jesu li Vam postavke ispravne.\"\nOutput.ConnectFail.ConnectFailed=\"Neuspješno povezivanje s poslužiteljem\"\nOutput.ConnectFail.InvalidStream=\"Ne mogu pristupiti navedenom kanalu ili stream-ključu; provjerite potonji. Ako je ispravan, možda postoji problem pri povezivanju s poslužiteljem.\"\nOutput.ConnectFail.HdrDisabled=\"Ovaj izlaz trenutačno ne podržava HDR izlaz.\"\nOutput.ConnectFail.Error=\"Neočekivana greška u povezivanju s poslužiteljem. Više informacija nalazi se u zapisniku.\"\nOutput.ConnectFail.Disconnected=\"Prekinuta veza s poslužiteljem.\"\nOutput.StreamEncodeError.Title=\"Greška u kodiranju\"\nOutput.StreamEncodeError.Msg=\"Došlo je do greške kodera tijekom emitiranja.\"\nOutput.StreamEncodeError.Msg.LastError=\"Došlo je do greške kodera tijekom emitiranja:<br><br>%1\"\nOutput.RecordFail.Title=\"Neuspješno započinjanje snimanja\"\nOutput.RecordFail.Unsupported=\"Izlazni format ili nije podržan ili ne podržava više od jedne zvučne trake. Provjerite postavke i pokušajte ponovno.\"\nOutput.RecordNoSpace.Title=\"Nedovoljno prostora na disku\"\nOutput.RecordNoSpace.Msg=\"Nema dovoljno prostora na disku da se nastavi snimanje.\"\nOutput.RecordError.Title=\"Greška pri snimanju\"\nOutput.RecordError.Msg=\"Neodređena greška se dogodila pri snimanju.\"\nOutput.RecordError.EncodeErrorMsg=\"Došlo je do greške kodera tijekom snimanja.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Došlo je do greške kodera tijekom snimanja:<br><br>%1\"\nOutput.BadPath.Title=\"Neispravna putanja datoteke\"\nOutput.BadPath.Text=\"Konfigurirana putanja snimanja nije se mogla otvoriti. Provjerite putanju snimanja pod Postavke → Izlaz → Snimanje.\"\nOutput.NoBroadcast.Title=\"Nije konfigurirano emitiranje\"\nOutput.NoBroadcast.Text=\"Prije početka streaminga morate postaviti emitiranje.\"\nOutput.BroadcastStartFailed=\"Nije uspjelo započeti emitiranje\"\nOutput.BroadcastStopFailed=\"Zaustavljanje emitiranja nije uspjelo\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Prijenos\"\nLogUploadDialog.Buttons.CopyURL=\"Kopiraj URL zapisnika\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analizira Log datoteke\"\nLogUploadDialog.Buttons.RetryButton=\"Pokušaj ponovno\"\nLogUploadDialog.Errors.Template=\"Nastala je pogreška prilikom slanja datoteke:\\n\\n%1\"\nRemux.SourceFile=\"OBS-snimak\"\nRemux.TargetFile=\"Datoteka\"\nRemux.Stop=\"Zaustavi remultiplikator\"\nRemux.ClearFinished=\"Obriši gotove artikle\"\nRemux.ClearAll=\"Očisti sve stavke\"\nRemux.OBSRecording=\"OBS-snimak\"\nRemux.FinishedTitle=\"Remux završen\"\nRemux.Finished=\"Završen remux snimka\"\nRemux.FinishedError=\"Remux završen, ali datoteka možda nije dovršena\"\nRemux.SelectRecording=\"Odaberi OBS snimanje …\"\nRemux.SelectTarget=\"Odaberi ciljanu datoteku …\"\nRemux.FileExistsTitle=\"Ciljana datoteka postoji\"\nRemux.FileExists=\"Sljedeće ciljne datoteke već postoje. Želite li ih zamijeniti?\"\nRemux.ExitUnfinishedTitle=\"Remux u tijeku\"\nRemux.ExitUnfinished=\"Remux nije završen, ako sada zaustavite proces datoteka može biti neupotrebljiva.\\nJeste li sigurni da želite zaustaviti remux?\"\nRemux.HelpText=\"Ispustite datoteke u ovaj prozor za remuksiranje ili odaberite praznu ćeliju \\\"OBS snimanje\\\" za pretraživanje datoteke.\"\nRemux.NoFilesAddedTitle=\"Nije dodana datoteka za remuksiranje\"\nRemux.NoFilesAdded=\"Nije dodana nijedna datoteka za remuxiranje. Izbacite mapu koja sadrži jednu ili više video datoteka.\"\nMissingFiles=\"Nedostaju datoteke\"\nMissingFiles.MissingFile=\"Nedostaje datoteka\"\nMissingFiles.NewFile=\"Nova datoteka\"\nMissingFiles.HelpText=\"Neke datoteke nedostaju otkad ste zadnji put koristili OBS.\"\nMissingFiles.NumFound=\"Pronađeno %1 of %2\"\nMissingFiles.Search=\"Pretraži direktorij...\"\nMissingFiles.SelectFile=\"Odaberite datoteku...\"\nMissingFiles.SelectDir=\"Odaberite mapu za pretraživanje\"\nMissingFiles.State=\"Stanje\"\nMissingFiles.Missing=\"Nedostaje\"\nMissingFiles.Replaced=\"Zamijenjeno\"\nMissingFiles.Cleared=\"Izbrisano\"\nMissingFiles.Found=\"Pronađeno\"\nMissingFiles.AutoSearch=\"Dodatna podudaranja datoteka pronađeno\"\nMissingFiles.AutoSearchText=\"OBS je pronašao dodatne podudarnosti za nedostajuće datoteke u tom direktoriju. Želite li ih dodati?\"\nMissingFiles.NoMissing.Title=\"Provjera nedostajućih datoteka\"\nMissingFiles.NoMissing.Text=\"Čini se da nema datoteka koje nedostaju.\"\nMacPermissions.MenuAction=\"Pregledaj dopuštenja aplikacije...\"\nMacPermissions.Title=\"Pregledaj dopuštenja aplikacije\"\nMacPermissions.Description=\"OBS Studio zahtijeva vaše dopuštenje za pružanje određenih značajki. Preporučuje se omogućiti ta dopuštenja, ali ona nisu potrebna za korištenje aplikacije. Uvijek ih možete omogućiti kasnije.\"\nMacPermissions.Description.OpenDialog=\"Ovaj dijalog možete ponovno otvoriti putem izbornika OBS Studija.\"\nMacPermissions.AccessGranted=\"Pristup odobren\"\nMacPermissions.RequestAccess=\"Zatraži pristup\"\nMacPermissions.OpenPreferences=\"Otvori %1 postavke\"\nMacPermissions.Item.ScreenRecording=\"Snimanje ekrana\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS-u je potrebno ovo dopuštenje za snimanje zaslona.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Ovo dopuštenje je potrebno za snimanje sadržaja s web kamere ili kartice za snimanje.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS-u je potrebna ova dozvola ako želite snimati mikrofon ili vanjski audiouređaj.\"\nMacPermissions.Item.InputMonitoring=\"Nadzor ulaza\"\nMacPermissions.Item.InputMonitoring.Details=\"Ovo dopuštenje je potrebno da bi prečaci radili dok je OBS u pozadini.\"\nMacPermissions.Item.Accessibility=\"Pristupačnost\"\nMacPermissions.Item.Accessibility.Details=\"Na starijim instalacijama, OBS može biti naveden u odjeljku \\\"Pristupačnost\\\" umjesto \\\"Nadzor unosa\\\".\"\nMacPermissions.Continue=\"Nastavi\"\nSourceLeak.Title=\"Greška pri čišćenju izvora\"\nSourceLeak.Text=\"Došlo je do problema pri promjeni zbirke scena te neke izvore nije moguće ukloniti. Taj problem obično uzrokuju dodatci koji nepravilno otpuštaju resurse. Molimo provjerite jesu li dodatci koje upotrebljavate ažurni.\\n\\nOBS Studio zatvorit će se kako bi se spriječila moguća oštećenja podataka.\"\nBasic.DesktopDevice1=\"Zvuk s računala\"\nBasic.DesktopDevice2=\"Zvuk s računala 2\"\nBasic.AuxDevice1=\"Mikrofon/ulaz\"\nBasic.AuxDevice2=\"Mikrofon/ulaz 2\"\nBasic.AuxDevice3=\"Mikrofon/ulaz 3\"\nBasic.AuxDevice4=\"Mikrofon/ulaz 4\"\nBasic.Scene=\"Scena\"\nBasic.DisplayCapture=\"Snimanje zaslona\"\nBasic.Main.PreviewConextMenu.Enable=\"Omogući pregled\"\nBasic.Main.Preview.Disable=\"Onemogući pregled\"\nScaleFiltering=\"Filter uvećanja\"\nScaleFiltering.Point=\"Točka\"\nScaleFiltering.Bilinear=\"Bilinearno\"\nScaleFiltering.Bicubic=\"Bikubično\"\nScaleFiltering.Lanczos=\"Lančos\"\nScaleFiltering.Area=\"Područje\"\nBlendingMethod=\"Metoda miješanja\"\nBlendingMethod.Default=\"Zadano\"\nBlendingMethod.SrgbOff=\"SRGB isključen\"\nBlendingMode=\"Način miješanja\"\nBlendingMode.Normal=\"Normalno\"\nBlendingMode.Additive=\"Dodaj\"\nBlendingMode.Subtract=\"Oduzmi\"\nBlendingMode.Screen=\"Ekran\"\nBlendingMode.Multiply=\"Pomnoži\"\nBlendingMode.Lighten=\"Posvjelti\"\nBlendingMode.Darken=\"Potamni\"\nDeinterlacing=\"Preplitanje\"\nDeinterlacing.Discard=\"Odbaci\"\nDeinterlacing.TopFieldFirst=\"Prvo gornje polje\"\nDeinterlacing.BottomFieldFirst=\"Prvo donje polje\"\nVolControl.SliderUnmuted=\"Upravljač glasnoće za „%1”:\"\nVolControl.SliderMuted=\"Upravljač glasnoće za „%1”: (trenutačno isključen zvuk)\"\nVolControl.Mute=\"Isključi zvuk za „%1”\"\nVolControl.Properties=\"Svojstva za „%1”\"\nVolControl.UnassignedWarning.Title=\"Nedodijeljeni zvučni izvor\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" nije dodijeljen nijednoj zvučnoj traci i neće se moći čuti u prijenosima ili snimkama.\\n\\nKako biste traci dodijelili zvuči izvor, otvorite Napredne postavke zvuka preko izbornika desnog klika ili ikone zupčanika u alatnoj traci.\"\nBasic.Main.AddSceneDlg.Title=\"Dodaj scenu\"\nBasic.Main.AddSceneDlg.Text=\"Unesite ime scene\"\nBasic.Main.DefaultSceneName.Text=\"Scena %1\"\nBasic.Main.AddSceneCollection.Title=\"Dodaj zbirku scena\"\nBasic.Main.AddSceneCollection.Text=\"Unesite ime zbirke scena\"\nBasic.Main.RenameSceneCollection.Title=\"Preimenuj zbirku scena\"\nBasic.Main.RemigrateSceneCollection.Title=\"Ažuriranje rezolucije kolekcije scena\"\nBasic.Main.RemigrateSceneCollection.Text=\"Želite li ažurirati razlučivost kolekcije scena \\\"%1\\\" kako bi odgovarala trenutnoj razlučivosti platna profila od %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Nije moguće ažurirati razlučivost kolekcije scena dok su izlazi aktivni.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Nije uspjelo ažuriranje razlučivosti kolekcije scena. Izvorna razlučivost je nepoznata.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Resetiranje nije moguće: Promjena OBS rezolucije nije uspjela.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Resetiranje nije moguće: Trenutna rezolucija već je osnovna rezolucija kolekcije scena.\"\nAddProfile.Title=\"Dodaj profil\"\nAddProfile.Text=\"Unesite ime profila\"\nAddProfile.WizardCheckbox=\"Prikaži čarobnjaka za automatsku konfiguraciju\"\nRenameProfile.Title=\"Preimenuj profil\"\nBasic.Main.MixerRename.Title=\"Preimenuj audio izvor\"\nBasic.Main.MixerRename.Text=\"Molimo unesite naziv audio izvora\"\nBasic.Main.PreviewDisabled=\"Pregled je trenutačno onemogućen\"\nBasic.SourceSelect=\"Napravi/odaberi izvor\"\nBasic.SourceSelect.CreateNew=\"Napravi novi\"\nBasic.SourceSelect.AddExisting=\"Dodaj postojeći\"\nBasic.SourceSelect.AddVisible=\"Prikaži izvor\"\nBasic.Main.Sources.Visibility=\"Vidljivost\"\nBasic.Main.Sources.VisibilityDescription=\"Kontrolira vidljivost '%1' na platnu\"\nBasic.Main.Sources.Lock=\"Zaključaj\"\nBasic.Main.Sources.LockDescription=\"Zaključava položaj i skalu '%1' na platnu\"\nBasic.PropertiesWindow=\"Svojstva za '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (automatski odabir: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Odaberi boju\"\nBasic.PropertiesWindow.SelectFont=\"Odaberi font\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Odaberi font\"\nBasic.PropertiesWindow.ConfirmTitle=\"Postavke promijenjene\"\nBasic.PropertiesWindow.Confirm=\"Imate nespremljenih promjena. Želite li ih spremiti?\"\nBasic.PropertiesWindow.NoProperties=\"Nema dostupnih svojstava\"\nBasic.PropertiesWindow.AddFiles=\"Dodaj datoteke\"\nBasic.PropertiesWindow.AddDir=\"Dodaj direktorij\"\nBasic.PropertiesWindow.AddURL=\"Dodaj putanju/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Dodaj direktorij u '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Dodaj datoteke u '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Dodaj zapise u '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Izmijeni zapise za '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Jednostavne FPS-vrijednosti\"\nBasic.PropertiesView.FPS.Rational=\"Racionalne FPS-vrijednosti\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Ispravni opsezi FPS-a:\"\nBasic.PropertiesView.UrlButton.Text=\"Otvoriti ovu poveznicu u zadanom web pregledniku?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Otvori URL\"\nBasic.InteractionWindow=\"Interakcija s '%1'\"\nBasic.StatusBar.Reconnecting=\"Veza je prekinuta, ponovno povezivanje za %2 sekund(i) (%1 pokušaj)\"\nBasic.StatusBar.AttemptingReconnect=\"Pokušavam ponovno povezivanje... (%1 pokušaj)\"\nBasic.StatusBar.ReconnectSuccessful=\"Ponovno povezivanje uspješno\"\nBasic.StatusBar.Delay=\"Odlaganje (%1 sek)\"\nBasic.StatusBar.DelayStartingIn=\"Odlaganje (počinje za %1 sek)\"\nBasic.StatusBar.DelayStoppingIn=\"Odlaganje (zaustavljam za %1 sek)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Odlaganje (zaustavljam za %1 sek, počinje za %2 sek)\"\nBasic.StatusBar.RecordingSavedTo=\"Snimka spremljena u '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Međuspremnik za reprodukciju spremljen u '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Snimka zaslona spremljena u '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Snimanje se automatski remiksira na '%1'\"\nBasic.Filters=\"Filteri\"\nBasic.Filters.AsyncFilters=\"Zvučni/Video filteri\"\nBasic.Filters.AudioFilters=\"Zvučni filteri\"\nBasic.Filters.EffectFilters=\"Filteri efekata\"\nBasic.Filters.Title=\"Filteri za '%1'\"\nBasic.Filters.AddFilter.Title=\"Ime filtera\"\nBasic.Filters.AddFilter.Text=\"Naznačite ime filtera\"\nBasic.TransformWindow=\"Transformacija stavke na sceni\"\nBasic.TransformWindow.Position=\"Pozicija\"\nBasic.TransformWindow.PositionX=\"Pozicija X\"\nBasic.TransformWindow.PositionY=\"Pozicija Y\"\nBasic.TransformWindow.Rotation=\"Okretanje\"\nBasic.TransformWindow.Size=\"Veličina\"\nBasic.TransformWindow.Width=\"Širina\"\nBasic.TransformWindow.Height=\"Visina\"\nBasic.TransformWindow.Alignment=\"Pozicijsko poravnanje\"\nBasic.TransformWindow.BoundsType=\"Vrsta okvira\"\nBasic.TransformWindow.BoundsAlignment=\"Poravnanje u okviru\"\nBasic.TransformWindow.Bounds=\"Veličina okvira\"\nBasic.TransformWindow.BoundsWidth=\"Širina okvira za ograničavanje\"\nBasic.TransformWindow.BoundsHeight=\"Visina graničnog okvira\"\nBasic.TransformWindow.CropToBounds=\"Orezivanje u okvir za ograničavanje\"\nBasic.TransformWindow.Crop=\"Isijecanje\"\nBasic.TransformWindow.CropLeft=\"Odreži lijevo\"\nBasic.TransformWindow.CropRight=\"Odreži desno\"\nBasic.TransformWindow.CropTop=\"Odreži gore\"\nBasic.TransformWindow.CropBottom=\"Odreži dolje\"\nBasic.TransformWindow.Alignment.TopLeft=\"Gore lijevo\"\nBasic.TransformWindow.Alignment.TopCenter=\"Gore u sredini\"\nBasic.TransformWindow.Alignment.TopRight=\"Gore desno\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Lijevo od sredine\"\nBasic.TransformWindow.Alignment.Center=\"Sredina\"\nBasic.TransformWindow.Alignment.CenterRight=\"Desno od sredine\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Dolje lijevo\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Dolje od sredine\"\nBasic.TransformWindow.Alignment.BottomRight=\"Dolje desno\"\nBasic.TransformWindow.BoundsType.None=\"Bez okvira\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Samo najveća veličina\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skaliraj prema unutarnjim okvirima\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skaliraj prema vanjskim okvirima\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skaliraj do širine okvira\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skaliraj do visine okvira\"\nBasic.TransformWindow.BoundsType.Stretch=\"Razvuci do okvira\"\nBasic.TransformWindow.Title=\"Uredi transformaciju za „%1”\"\nBasic.TransformWindow.NoSelectedSource=\"Nije odabran nijedan izvor\"\nBasic.Main.AddSourceHelp.Title=\"Ne mogu dodati izvor\"\nBasic.Main.AddSourceHelp.Text=\"Potrebno je najmanje jedna scena da dodate izvor.\"\nBasic.Main.Scenes=\"Scene\"\nBasic.Main.Sources=\"Izvori\"\nBasic.Main.Source=\"Izvor\"\nBasic.Main.Controls=\"Kontrole\"\nBasic.Main.PreparingStream=\"Priprema...\"\nBasic.Main.Connecting=\"Povezivanje...\"\nBasic.Main.StartRecording=\"Počni snimanje\"\nBasic.Main.StartReplayBuffer=\"Pokreni međuspremnik za ponavljanje\"\nBasic.Main.SaveReplay=\"Spremi snimku\"\nBasic.Main.StartStreaming=\"Počni streamanje\"\nBasic.Main.StartBroadcast=\"Idi uživo\"\nBasic.Main.StartVirtualCam=\"Pokreni virtualnu kameru\"\nBasic.Main.StopRecording=\"Zaustavi snimanje\"\nBasic.Main.PauseRecording=\"Pauziraj snimanje\"\nBasic.Main.UnpauseRecording=\"Prekini snimanje\"\nBasic.Main.SplitFile=\"Podijeli datoteku snimke\"\nBasic.Main.StoppingRecording=\"Zaustavljanje snimanja...\"\nBasic.Main.StopReplayBuffer=\"Zaustavi međuspremnik za ponavljanje\"\nBasic.Main.StoppingReplayBuffer=\"Zaustavljanje međuspremnika za reprodukciju...\"\nBasic.Main.SetupBroadcast=\"Upravljaj emitiranjem\"\nBasic.Main.StopStreaming=\"Zaustavi streamanje\"\nBasic.Main.StopBroadcast=\"Prekini emitiranje\"\nBasic.Main.AutoStopEnabled=\"(Automatsko zaustavljanje)\"\nBasic.Main.StoppingStreaming=\"Zaustavljanje streamanja...\"\nBasic.Main.ForceStopStreaming=\"Zaustavi streamanje (poništi odlaganje)\"\nBasic.Main.ShowContextBar=\"Prikaži alatnu traku izvora\"\nBasic.Main.HideContextBar=\"Sakrij alatnu traku izvora\"\nBasic.Main.StopVirtualCam=\"Zaustavi virtualnu kameru\"\nBasic.Main.Group=\"Grupa %1\"\nBasic.Main.GroupItems=\"Grupiraj odabrane elemente\"\nBasic.Main.Ungroup=\"Razdvoji grupu\"\nBasic.Main.GridMode=\"Mrežni način rada\"\nBasic.Main.ListMode=\"Način popisa\"\nBasic.Main.VirtualCamConfig=\"Konfiguriraj virtualnu kameru\"\nBasic.VCam.VirtualCamera=\"Virtualna kamera\"\nBasic.VCam.OutputType=\"Vrsta izlaza\"\nBasic.VCam.OutputSelection=\"Odabir izlaza\"\nBasic.VCam.OutputType.Program=\"Program (zadano)\"\nBasic.VCam.OutputSelection.NoSelection=\"Nema odabira za ovu vrstu izlaza\"\nBasic.VCam.RestartWarning=\"Virtualna kamera će se ponovno pokrenuti kako bi se primijenila ova promjena\"\nBasic.MainMenu.File=\"Datoteka (&F)\"\nBasic.MainMenu.File.Export=\"Izv&ezi\"\nBasic.MainMenu.File.Import=\"Uvoz (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"P&rikaži snimke\"\nBasic.MainMenu.File.Remux=\"Re&mux snimke\"\nBasic.MainMenu.File.Settings=\"Podešavanja (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Prikaži mapu s postavkama\"\nBasic.MainMenu.File.ShowProfileFolder=\"Prikaži mapu s profilima\"\nBasic.MainMenu.File.ShowMissingFiles=\"Provjerite nedostajuće datoteke\"\nBasic.MainMenu.File.Exit=\"Izlaz (&X)\"\nBasic.MainMenu.Edit=\"Ur&edi\"\nBasic.MainMenu.Edit.Undo=\"Poništi (&U)\"\nBasic.MainMenu.Edit.Redo=\"Ponovi (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Zak&ljučaj prikaz\"\nBasic.MainMenu.Edit.Scale=\"Pregled &skaliranja\"\nBasic.MainMenu.Edit.Scale.Window=\"Skaliraj na veličinu prozora\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Platno (%1 × %2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Izlaz (%1 × %2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Skalirano (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Povećaj\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Smanji\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Poništi zumiranje\"\nBasic.MainMenu.Edit.Transform=\"&Transformiraj\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Uredi transformaciju\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopiraj transformaciju\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Umetni transformaciju\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Poništi t&ransformaciju\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Okreni za 90 stupnjeva udesno\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Okreni za 90 stupnjeva ulijevo\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Okreni za 180 stupnjeva\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Preokreni &vodoravno\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Preokreni &okomito\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Raširi po zaslonu (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Razvuci po zaslonu (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centriraj na zaslon\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centriraj okomito\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centriraj vodoravno\"\nBasic.MainMenu.Edit.Order=\"Red&oslijed\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Pomakni gore (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Pomakni &dolje\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Pomakni na vrh (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Pomakni na dno (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"&Napredne postavke zvuka\"\nBasic.MainMenu.View=\"Prikaz (&V)\"\nBasic.MainMenu.View.Toolbars=\"Alatne &trake\"\nBasic.MainMenu.View.ListboxToolbars=\"Usidri alatne trake\"\nBasic.MainMenu.View.ContextBar=\"Alatna traka izvora\"\nBasic.MainMenu.View.SourceIcons=\"Izvor i ikone\"\nBasic.MainMenu.View.StatusBar=\"Traka &stanja\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Sučelje preko cijelog zaslona\"\nBasic.MainMenu.View.ResetUI=\"&Resetiraj korisničko sučelje\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Uvijek gore\"\nBasic.MainMenu.View.SceneListMode=\"Način popisa scena\"\nBasic.MainMenu.Docks=\"&Sidrišta\"\nBasic.MainMenu.Docks.ResetDocks=\"&Ponovno postavi sidrišta\"\nBasic.MainMenu.Docks.LockDocks=\"&Zaključaj sidrišta\"\nBasic.MainMenu.Docks.SideDocks=\"&Sidrišta pune veličine\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Prilagođeni dokovi preglednika\"\nBasic.MainMenu.Import=\"Uvoz...\"\nBasic.MainMenu.Export=\"Izvoz...\"\nBasic.MainMenu.SceneCollection=\"Zbirka &scena\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Uvoz profila\"\nBasic.MainMenu.Profile.Export=\"Izvoz profila\"\nBasic.MainMenu.SceneCollection.Import=\"Dodaj zbirku scena\"\nBasic.MainMenu.SceneCollection.Export=\"Izvezi zbirku scena\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Poništi osnovnu rezoluciju\"\nBasic.MainMenu.SceneCollection.Migrate=\"Postavi osnovnu rezoluciju\"\nBasic.MainMenu.Profile.Exists=\"Profil već postoji\"\nBasic.MainMenu.Tools=\"Ala&ti\"\nBasic.MainMenu.Help=\"Pomoć (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Centar za &pomoć\"\nBasic.MainMenu.Help.Website=\"Posjeti stranicu (&W)\"\nBasic.MainMenu.Help.Discord=\"Pridruži se &Discord serveru\"\nBasic.MainMenu.Help.WhatsNew=\"Novosti\"\nBasic.MainMenu.Help.Logs=\"Zapisnici (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Prikaži zapisnike (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Pošalji trenutačni zapisnik (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Prenesi prethodnu datoteku zapisnika\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Prikaži trenutačni zapisnik (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Napomene uz izdane verzije\"\nBasic.MainMenu.Help.CheckForUpdates=\"Proveri verziju programa\"\nBasic.MainMenu.Help.Repair=\"Provjerite integritet datoteke\"\nBasic.MainMenu.Help.RestartSafeMode=\"Ponovno pokreni u sigurnom načinu\"\nBasic.MainMenu.Help.RestartNormal=\"Ponovno pokreni u normalnom načinu\"\nBasic.MainMenu.Help.CrashLogs=\"Izvješča o prekidu &rada\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Prikaži izvješča o prekidu rada (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Prenesi prethodno izvješće o rušenju programa\"\nBasic.MainMenu.Help.About=\"Više o &aplikaciji\"\nBasic.Settings.ProgramRestart=\"Program se mora ponovno pokrenuti da bi ova postavka stupila na snagu.\"\nBasic.Settings.ConfirmTitle=\"Potvrdite promjene\"\nBasic.Settings.Confirm=\"Niste spremili sve promjene. Spremiti?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 kontrolira neke od vaših postavki streama\"\nBasic.Settings.General=\"Općenito\"\nBasic.Settings.General.Language=\"Jezik\"\nBasic.Settings.General.Updater=\"Ažuriranja\"\nBasic.Settings.General.UpdateChannel=\"Ažuriraj kanal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Deaktivirano)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Zadano)\"\nBasic.Settings.General.EnableAutoUpdates=\"Automatski provjeri za nadogradnje pri pokretanju programa\"\nBasic.Settings.General.OpenStatsOnStartup=\"Otvori dijalog statistike pri pokretanju\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Sakrij OBS prozore iz snimke zaslona\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Ova postavka će sakriti sve prozore OBS Studija koji nisu namijenjeni za projektore, a utječe na snimanje u OBS-u i druge aplikacije, kao što su konferencije, dijeljenje zaslona, udaljena podrška, snimke zaslona i ostali softver za snimanje.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Omogućavanje ove opcije skriva sve prozore OBS Studija koji nisu projektori od snimanja od strane OBS-a i utječe na druge aplikacije, kao što su konferencije, dijeljenje zaslona, udaljena podrška, snimke zaslona i ostali softver za snimanje.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Prikaži prozor za potvrdu kad se započinju streamovi\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Prikaži prozor za potvrdu kad se zaustavljaju streamovi\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Prikaži dijalog za potvrdu prilikom zaustavljanja snimanja\"\nBasic.Settings.General.Projectors=\"Projektori\"\nBasic.Settings.General.HideProjectorCursor=\"Sakrij pokazivač na projektorima\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Uvijek postavi projektor na vrh prozora\"\nBasic.Settings.General.Snapping=\"Poravnavanje privlačenjem izvora\"\nBasic.Settings.General.ScreenSnapping=\"Privuci izvore rubu zaslona\"\nBasic.Settings.General.CenterSnapping=\"Privuci izvore vodoravnoj i okomitoj sredini\"\nBasic.Settings.General.SourceSnapping=\"Privlačenje izvora ka drugim izvorima\"\nBasic.Settings.General.SnapDistance=\"Osjetljivost privlačenja\"\nBasic.Settings.General.SpacingHelpers=\"Prikaži vodiče za poravnanje piksela\"\nBasic.Settings.General.RecordWhenStreaming=\"Automatski snimaj dok streamam\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Nastavi snimati nakon završetka streama\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Automatsko pokretanje međuspremnika za reprodukciju prilikom streaminga\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Održava međuspremnik za reprodukciju aktivnim kada se stream zaustavi\"\nBasic.Settings.General.SysTray=\"Sistemska traka\"\nBasic.Settings.General.SysTrayWhenStarted=\"Pri pokretanju minimiziraj na ikonicu u sistemskom panelu\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Uvijek minimiziraj u sistemsku traku umjesto u programsku traku\"\nBasic.Settings.General.SaveProjectors=\"Spremi projektore pri izlasku\"\nBasic.Settings.General.CloseExistingProjectors=\"Ograničenje na jedan projektor za cijeli zaslon po platnu\"\nBasic.Settings.General.Preview=\"Pregled\"\nBasic.Settings.General.OverflowHidden=\"Sakrij višak\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Prelijevanje je uvijek vidljivo\"\nBasic.Settings.General.OverflowSelectionHidden=\"Prikaži prelijevanje čak i kada je izvor nevidljiv\"\nBasic.Settings.General.Importers=\"Uvoznici\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Pretraživanje poznatih lokacija za kolekcije scena prilikom uvoza\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Prijelaz na scenu dvostrukim pritiskom\"\nBasic.Settings.General.StudioPortraitLayout=\"Omogući uspravni raspored\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Prikaži oznake pregleda/programa\"\nBasic.Settings.General.Multiview=\"Višestruki prikaz\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Pritisni za prebacivanje između scena\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Prikaži imena scena\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Nacrtajte sigurna područja (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Višeprikazni raspored\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Vodoravno, gore (8 scena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Vodoravno, dolje (8 scena)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Okomito, lijevo (8 scena)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Okomito, desno (8 scena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Vodoravno, gore (18 scena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Vodoravno, gore (24 scena)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Samo scene (4 scene)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Samo scene (9 scena)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Samo scene (16 scena)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Samo scene (25 scena)\"\nBasic.Settings.General.ChannelName.stable=\"Stabilno\"\nBasic.Settings.General.ChannelDescription.stable=\"Najnovije stabilno izdanje\"\nBasic.Settings.General.ChannelName.beta=\"Beta verzije / Kandidati za izdanje\"\nBasic.Settings.General.ChannelDescription.beta=\"Potencijalno nestabilne razvojne verzije\"\nBasic.Settings.Appearance=\"Izgled\"\nBasic.Settings.Appearance.General=\"Općenito\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Stil\"\nBasic.Settings.Appearance.General.NoVariant=\"Nema dostupnih stilova\"\nBasic.Settings.Appearance.FontScale=\"Veličina fonta\"\nBasic.Settings.Appearance.Density=\"Gustoća\"\nBasic.Settings.Appearance.Classic=\"Klasično\"\nBasic.Settings.Appearance.Compact=\"Kompaktno\"\nBasic.Settings.Appearance.Normal=\"Normalno\"\nBasic.Settings.Appearance.Comfortable=\"Ugodno\"\nBasic.Settings.Appearance.OptionsWarning=\"Neke opcije izgleda nisu dostupne za ovaj stil.\"\nBasic.Settings.Stream=\"Strim\"\nBasic.Settings.Stream.Destination=\"Odredište\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Koristi autentifikaciju\"\nBasic.Settings.Stream.Custom.Username=\"Korisničko ime\"\nBasic.Settings.Stream.Custom.Password=\"Lozinka\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: unesite korisničko_ime_srp.\\nRTMP: unesite korisničko ime.\\nSRT: ne koristi se.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: unesite srp_lozinku.\\nRTMP: unesite lozinku.\\nSRT: unesite šifru za šifriranje.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Omogući način testiranja propusnosti\"\nBasic.Settings.Stream.TTVAddon=\"Dodaci za Twitch chat\"\nBasic.Settings.Stream.TTVAddon.None=\"Ništa\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV i FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Nedostaje postavka streama\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Otvori Postavke\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Nedostaju URL i ključ streama.\\n\\nOtvorite postavke za unos URL-a i ključa streama u kartici 'stream'.\"\nBasic.Settings.Stream.MissingUrl=\"Nedostaje URL streama.\\n\\nOtvorite postavke za unos URL-a na kartici 'Stream'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Nedostaje ključ streama.\\n\\nOtvorite postavke da biste unijeli ključ streama na kartici 'Stream'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Zanemarite preporuke za postavke usluge streaminga\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Nadjačaj preporučene postavke\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Upozorenje: Ignoriranje ograničenja usluge može rezultirati smanjenom kvalitetom streama ili spriječiti streaming.\\n\\nŽelite li nastaviti?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maksimalna brzina prijenosa videa: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maksimalna brzina prijenosa zvuka: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksimalna rezolucija: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksimalni FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Navedite prilagođeni poslužitelj...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Prilagođeni poslužitelj\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Omogući %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maksimalna propusnost strujanja\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automatski\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maksimalan broj video zapisa\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automatski\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Omogući preusmjeravanje streama u FLV (koristi jednostavne postavke datoteke snimanja)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Nadjačavanje konfiguracije (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Omogući poništavanje konfiguracije\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Višekanalni video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Dodatno platno\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Simultani prijenos\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Simultani prijenos vam omogućuje odiranje i slanje videa različitih kvaliteta. <a href='https://obsproject.com/kb/whip-streaming-guide'>Više informacija</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Ukupno slojeva\"\nBasic.Settings.Stream.AdvancedOptions=\"Napredne mogućnosti\"\nBasic.Settings.Output=\"Izlaz\"\nBasic.Settings.Output.Format=\"Format snimanja\"\nBasic.Settings.Output.Format.MKV=\"Matroska video (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"Hibridni MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hibridni MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmentirani MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmentirani MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmentirani MOV sprema snimku u dijelovima i ne zahtijeva dovršavanje poput tradicionalnih MOV datoteka.\\nTo osigurava ispravno funkcioniranje datoteke čak i ako se snimanje prekine, primjerice zbog gubitka struje.\\n\\nOva značajka mogla bi biti nekompatibilna s nekim reproduktorima ili uređivačima. Za pretvorbu datoteke u kompatibilniji format idite na Datoteka → Prepakiranje snimaka.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmentirani MP4 sprema snimku u dijelovima i ne zahtijeva dovršavanje poput tradicionalnih MP4 datoteka.\\nTo osigurava ispravno funkcioniranje datoteke čak i ako se snimanje prekine, primjerice zbog gubitka struje.\\n\\nOva značajka mogla bi biti nekompatibilna s nekim reproduktorima ili uređivačima. Za pretvorbu datoteke u kompatibilniji format idite na Datoteka → Prepakiranje snimaka.\"\nBasic.Settings.Output.Encoder.Video=\"Enkoder za video\"\nBasic.Settings.Output.Encoder.Audio=\"Enkoder za audio\"\nBasic.Settings.Output.SelectDirectory=\"Odaberi direktorijum za snimanje\"\nBasic.Settings.Output.DynamicBitrate=\"Dinamički mijenjajte brzinu prijenosa podataka kako biste upravljali zagušenjem\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dinamički mijenjajte brzinu prijenosa podataka radi upravljanja zagušenjem (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Umjesto izostavljanja kadrova radi smanjenja zagušenja, dinamički mijenja brzinu prijenosa u hodu.\\n\\nImajte na umu da to može povećati kašnjenje za gledatelje ako dođe do značajnog iznenadnog zagušenja.\\nKada brzina prijenosa padne, vraćanje u normalu može potrajati i do nekoliko minuta.\\n\\nTrenutno podržano samo za RTMP.\"\nBasic.Settings.Output.Mode=\"Režim izlaza\"\nBasic.Settings.Output.Mode.Simple=\"Jednostavno\"\nBasic.Settings.Output.Mode.Adv=\"Napredno\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg izlaz\"\nBasic.Settings.Output.UseReplayBuffer=\"Omogući međuspremnik za reprodukciju\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maksimalno vrijeme ponavljanja\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Najviše memorije\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Procijenjena upotreba memorije: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Upozorenje: Procijenjeno korištenje memorije od %1 MiB je veće od preporučenog maksimuma od %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Nije moguće procijeniti korištenje memorije. Postavite maksimalno ograničenje memorije.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefiks naziva datoteke međuspremnika za reprodukciju\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufiks\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Međuspremnik ponovne reprodukcije ne funkcionira kada je tip snimanja prilagođeni izlaz (FFmpeg)\"\nBasic.Settings.Output.Simple.SavePath=\"Putanja za snimanje\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Kvaliteta snimanja\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Isto kao strim\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Visoki kvalitet, osrednja veličina datoteke\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Kvalitet sa neprimetnim razlikama, velika datoteka\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Kvalitet bez gubitka, izričito velika datoteka\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Upozorenje: Brzina prijenosa videa za streaming bit će postavljena na %1, što je gornja granica za trenutnu uslugu streaminga.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Upozorenje: Brzina prijenosa zvuka za strujanje bit će postavljena na %1, što je gornja granica za trenutnu uslugu strujanja.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Upozorenje: Snimke se ne mogu pauzirati ako je kvaliteta snimanja postavljena na \\\"Isto kao i stream\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Upozorenje: Odabrani format za snimanje nije kompatibilan s enkoderima za prijenos.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Upozorenje: Snimanje sa softverskim enkoderom drugačijeg kvaliteta u odnosu na strim će zahtevati dodatnu procesorsku snagu ako strimujete i snimate u isto vreme.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Upozorenje: Kvaliteta bez gubitka stvara iznimno velike datoteke! Kvaliteta bez gubitka može koristiti do 7 gigabajta prostora na disku po minutu pri visokim rezolucijama i framerateu. Kvaliteta bez gubitka ne preporuča se za dulje snimanje osim ako imate veliku količinu slobodnog prostora na disku. Međuspremnik ponovne reprodukcije nije dostupan pri uporabi kvalitete bez gubitka.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Da li ste sigurni da želite koristiti kvalitet bez gubitka?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Upozorenje za kvalitet bez gubitka!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Softverski (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Hardver (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Hardver (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Hardver (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Hardver (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Hardver (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Hardver (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Hardver (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Hardver (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Hardver (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Hardver (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Softverski (x264 niska upotreba procesora, povećava veličinu datoteke)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (zadano)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD zapis (koristi zapis 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Zvučna traka\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Nekompatibilna rezolucija/broj sličica u sekundi\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Ova usluga streaminga ne podržava vašu trenutnu izlaznu razlučivost i/ili broj sličica u sekundi. Bit će promijenjeni na najbližu kompatibilnu vrijednost:\\n\\n%1\\n\\nŽelite li nastaviti?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Rezolucija: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Nekompatibilni enkoder\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Usluga streaminga \\\"%1\\\" ne podržava koder \\\"%2\\\". Koder će biti promijenjen u \\\"%3\\\".\\n\\nŽelite li nastaviti?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Servis za streaming \\\"%1\\\" ne podržava enkodere \\\"%2\\\" i \\\"%3\\\". Ti će se enkoderi zamijeniti enkoderima \\\"%4\\\" i \\\"%5\\\".\\n\\nŽelite li nastaviti?\"\nBasic.Settings.Output.VideoBitrate=\"Protok videa\"\nBasic.Settings.Output.AudioBitrate=\"Protok zvuka\"\nBasic.Settings.Output.Reconnect=\"Automatski poveži ponovo\"\nBasic.Settings.Output.RetryDelay=\"Odgoda ponovnog pokušaja\"\nBasic.Settings.Output.MaxRetries=\"Maksimalan broj ponovnih pokušaja\"\nBasic.Settings.Output.Advanced=\"Omogući postavke prilagođenog kodera (napredno)\"\nBasic.Settings.Output.EncoderPreset=\"Unaprijed postavljeni enkoder\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (niska upotreba CPU-a, najniža kvaliteta)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (zadano) (srednja upotreba CPU-a, standardna kvaliteta)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (visoka upotreba CPU-a, visoka kvaliteta)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Prilagođena enkoder podešavanja\"\nBasic.Settings.Output.CustomMuxerSettings=\"Posebna podešavanja muksera\"\nBasic.Settings.Output.NoSpaceFileName=\"Stvori ime datoteke bez razmaka\"\nBasic.Settings.Output.Adv.Rescale=\"Skaliraj izlaz\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Onemogućeno\"\nBasic.Settings.Output.Adv.AudioTrack=\"Zvučni izvor\"\nBasic.Settings.Output.Adv.Streaming=\"Strimovanje\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Postavke streaminga\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Zapis 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Zapis 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Zapis 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Zapis 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Zapis 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Zapis 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD zapis\"\nBasic.Settings.Output.Adv.Encoder=\"Postavke enkodera\"\nBasic.Settings.Output.Adv.Recording=\"Snimanje\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Postavke snimanja\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Vrsta snimanja\"\nBasic.Settings.Output.Adv.Recording.Type=\"Vrsta\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standardno\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Prilagođeni izlaz (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Koristi strim enkoder)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Oblikovanje imena datoteke\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Prepiši ako postoji datoteka\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Prilagođeni izlaz (FFmpeg) pruža se bez ikakvih zaštitnih mjera. Razmislite o korištenju vrste snimanja \\\"Standardno\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg vrsta ispisa\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Izlaz na URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Izlaz u datoteku\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Uobičajeni formati snimaka\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Sve datoteke\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Putanja datoteke ili URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format kontejnera\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Zvuk\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Videozapis\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Podrazumevani format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Opis formata kontejnera\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Zvučni/Video kodek određen putanjom datoteke ili URL-a\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Podrazumevani enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Onemogući enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Video enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Podešavanja video enkodera (ako postoje)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Zvučni enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Podešavanja zvučnog enkodera (ako postoje)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Podešavanja muxer-a (ako postoje)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Interval ključnih kadrova (kadrili)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Prikaži sve kodeke (čak i ako su potencijalno nekompatibilni)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Postavke FFmpega\"\nBasic.Settings.Output.EnableSplitFile=\"Automatsko dijeljenje datoteka\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Podijeli po vremenu\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Podijeli po veličini\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Podijelite samo ručno\"\nBasic.Settings.Output.SplitFile.Time=\"Podijeljeno vrijeme\"\nBasic.Settings.Output.SplitFile.Size=\"Veličina podjele\"\nScreenshot=\"Izlaz zaslona\"\nScreenshot.SourceHotkey=\"Snimka zaslona odabranog izvora\"\nScreenshot.StudioProgram=\"Spremi snimku zaslona programa\"\nScreenshot.Preview=\"Spremi pregled zaslona\"\nScreenshot.Scene=\"Spremi snimku zaslona scene\"\nScreenshot.Source=\"Spremi izvorni snimak zaslona\"\nFilenameFormatting.TT.CCYY=\"Godina, četiri znamenke\"\nFilenameFormatting.TT.YY=\"Godina, zadnje dvije znamenke (00-99)\"\nFilenameFormatting.TT.MM=\"Mjesec u brojevnom zapisu (01-12)\"\nFilenameFormatting.TT.DD=\"Dan u mjesecu, dvoznamenkast (01-31)\"\nFilenameFormatting.TT.hh=\"Sat u formatu 24h (00-23)\"\nFilenameFormatting.TT.mm=\"Minuta (00-59)\"\nFilenameFormatting.TT.ss=\"Sekunda (00-59)\"\nFilenameFormatting.TT.Percent=\"Znak %\"\nFilenameFormatting.TT.a=\"Skraćeni naziv dana u tjednu\"\nFilenameFormatting.TT.A=\"Puni naziv dana u tjednu\"\nFilenameFormatting.TT.b=\"Skraćeni naziv mjeseca\"\nFilenameFormatting.TT.B=\"Puni naziv mjeseca\"\nFilenameFormatting.TT.d=\"Dan u mjesecu, dvoznamenkast (01-31)\"\nFilenameFormatting.TT.H=\"Sat u formatu 24h (00-23)\"\nFilenameFormatting.TT.I=\"Sat u formatu 12h (01-12)\"\nFilenameFormatting.TT.m=\"Mjesec u brojevnom zapisu (01-12)\"\nFilenameFormatting.TT.M=\"Minuta (00-59)\"\nFilenameFormatting.TT.p=\"Oznaka AM (prijepodne) ili PM (poslijepodne)\"\nFilenameFormatting.TT.s=\"Vrijeme u sekundama od epohe UNIX\"\nFilenameFormatting.TT.S=\"Sekunda (00-59)\"\nFilenameFormatting.TT.y=\"Godina, zadnje dvije znamenke (00-99)\"\nFilenameFormatting.TT.Y=\"Godina\"\nFilenameFormatting.TT.z=\"ISO 8601 otklon od UTC u vremenskoj zoni\"\nFilenameFormatting.TT.Z=\"Naziv ili pokrata za vremensku zonu\"\nFilenameFormatting.TT.FPS=\"Sličice po sekundi\"\nFilenameFormatting.TT.CRES=\"Osnovna (površinska) rezolucija\"\nFilenameFormatting.TT.ORES=\"Izlazna (skalirana) rezolucija\"\nFilenameFormatting.TT.VF=\"Format videa\"\nBasic.Settings.Video.Adapter=\"Video adapter\"\nBasic.Settings.Video.BaseResolution=\"Osnovna (površinska) rezolucija\"\nBasic.Settings.Video.ScaledResolution=\"Izlazna (skalirana) rezolucija\"\nBasic.Settings.Video.DownscaleFilter=\"Filter za skaliranje\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Rezolucije se podudaraju, nije potrebno smanjivanje razmjera]\"\nBasic.Settings.Video.FPSCommon=\"Uobičakene FPS vrednosti\"\nBasic.Settings.Video.FPSInteger=\"Brojevna FPS vrednost\"\nBasic.Settings.Video.FPSFraction=\"Brojevna FPS vrednost sa decimalama\"\nBasic.Settings.Video.Numerator=\"Brojač\"\nBasic.Settings.Video.Denominator=\"Nazivnik\"\nBasic.Settings.Video.InvalidResolution=\"Neispravna vrednost rezolucije. Mora biti [width]x[height] (npr. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Video izlaz je trenutno aktivan. Molim isključite sve izlaze da promenite video podešavanja.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (najbrže, ali mutno pri skaliranju)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (oštrije skaliranje, 16 uzoraka)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (oštrije skaliranje, 36 uzorka)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Površina (ponderirana suma, 4/6/9 uzoraka)\"\nBasic.Settings.Audio=\"Zvuk\"\nBasic.Settings.Audio.SampleRate=\"Protok\"\nBasic.Settings.Audio.Channels=\"Kanali\"\nBasic.Settings.Audio.Meters=\"Mjerači\"\nBasic.Settings.Audio.MeterDecayRate=\"Stopa raspadanja\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Brzo\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Srednji (PPM tipa I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Sporo (PPM tipa II)\"\nBasic.Settings.Audio.PeakMeterType=\"Vrsta vršnog mjerača\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Uzorak vrha\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Pravi vrh (veća upotreba CPU-a)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"UPOZORENJE: Omogućen je surround zvuk.\"\nBasic.Settings.Audio.MultichannelWarning=\"Ako streamate, provjerite podržava li vaša usluga streaminga i unošenje surround zvuka i reprodukciju surround zvuka. Na primjer, Facebook 360 Live u potpunosti podržava surround zvuk; YouTube Live podržava 5.1 audio unošenje (i reprodukciju na televizorima).\\n\\nOBS audio filtri kompatibilni su sa surround zvukom, iako podrška za VST dodatke nije zajamčena.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Omogućiti surround zvuk?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Jeste li sigurni da želite omogućiti surround zvuk?\"\nBasic.Settings.Audio.Devices=\"Globalni audio uređaji\"\nBasic.Settings.Audio.DesktopDevice=\"Zvuk s računala\"\nBasic.Settings.Audio.DesktopDevice2=\"Zvuk s računala 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/Pomoćni audio\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/Pomoćni audio 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/Pomoćni audio 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/Pomoćni audio 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Omogući stisni-za-mutiranje\"\nBasic.Settings.Audio.PushToMuteDelay=\"Stisni-za-mutiranje pauza\"\nBasic.Settings.Audio.EnablePushToTalk=\"Omogući Stisni-za-govor\"\nBasic.Settings.Audio.PushToTalkDelay=\"Stisni-za-govor pauza\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Uređaj nije povezan ili nije dostupan]\"\nBasic.Settings.Audio.Disabled=\"Onemogućeno\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Način međumemorije zvuka s niskom latencijom (za Decklink/NDI izlaze)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"UPOZORENJE: Omogućeno je međuspremništvo zvuka s niskom latencijom.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Način međuspremnika zvuka s niskom latencijom može uzrokovati probleme ili prestanak reprodukcije zvuka iz nekih izvora.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Omogućiti način međuspremnika zvuka s niskom latencijom?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Jeste li sigurni da želite omogućiti način međuspremnika zvuka s niskom latencijom?\"\nBasic.Settings.Accessibility=\"Pristupačnost\"\nBasic.Settings.Accessibility.ColorOverrides=\"Koristite različite boje\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Izvorni okvir (odabir)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Izvorni obrub (obrezivanje)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Izvorni okvir (zadržavanje pokazivača miša)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Raspon glasnoće miksera (-60 do -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Raspon glasnoće miksera (-20 do -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Opseg glasnoće miksera (-9 do 0 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Raspon glasnoće miksera (-60 do -20dB) (aktivno)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Raspon glasnoće miksera (-20 do -9dB) (aktivno)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Pojas glasnoće miksera (-9 do 0 dB) (aktivno)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Unaprijed postavljene boje\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Zadano\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Prilagođeno\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternativa za daltoniste\"\nBasic.Settings.Advanced=\"Napredno\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Prikaži upozorenje o aktivnim izlazima pri izlasku\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prioritet procesa\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Visoki\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Iznad normalnog\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normalni\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Ispod normale\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Besposleni\"\nBasic.Settings.Advanced.FormatWarning=\"Upozorenje: Formati boja koji nisu NV12/P010 prvenstveno su namijenjeni snimanju i ne preporučuju se prilikom streaminga. Streaming može uzrokovati povećano korištenje CPU-a zbog pretvorbe formata boja.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Upozorenje: Formati visoke preciznosti češće se koriste s HDR prostorima boja.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Upozorenje: Rec. 2100 trebao bi koristiti format s većom preciznošću.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Format boja\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bitni, 4:2:0, 2 ravnine)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bitni, 4:2:0, 3 ravnine)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bitni, 4:4:4, 3 ravnine)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bitni, 4:2:0, 2 ravnine)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bitni, 4:2:0, 3 ravnine)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bitni, 4:2:2, 2 ravnine)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bitni, 4:4:4, 2 ravnine)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-bitni)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Prostor boja\"\nBasic.Settings.Advanced.Video.ColorRange=\"Raspon boja\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Ograničeno\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Potpuno\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Razina bijele boje SDR-a\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nominalna vršna razina HDR-a\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Uređaj za praćenje\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Zadano\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Onemogući utišavanje zvuka u sustavu Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Odlaganje strima\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Trajanje\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Očuvaj tačku prekidanja (povećava odlaganje) kada se ponovo povezuje\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Procenjena upotreba memorije: %1 MB\"\nBasic.Settings.Advanced.Network=\"Mreža\"\nBasic.Settings.Advanced.Network.Disabled=\"Trenutno odabrani protokol strujanja ne podržava promjenu mrežnih postavki.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Veži se za IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP obitelj\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Omogući optimizacije mreže\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Omogući TCP tempo\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Pokušava učiniti RTMP izlaz prilagođenijim drugim aplikacijama osjetljivim na latenciju na mreži reguliranjem brzine prijenosa.\\nMože povećati rizik od gubitka okvira na nestabilnim vezama.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Ponašanje fokusa prečaca\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nikad ne onemogućujte prečace\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Onemogući prečace kada je glavni prozor u fokusu\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Onemogući prečace kada glavni prozor nije u fokusu\"\nBasic.Settings.Advanced.AutoRemux=\"Automatski remuksiraj u %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(snimi kao mkv)\"\nBasic.AdvAudio=\"Napredne postavke zvuka\"\nBasic.AdvAudio.ActiveOnly=\"Samo aktivni izvori\"\nBasic.AdvAudio.Name=\"Ime\"\nBasic.AdvAudio.Volume=\"Glasnoća\"\nBasic.AdvAudio.VolumeSource=\"Glasnoća za „%1”\"\nBasic.AdvAudio.MonoSource=\"Downmix za mono za \\\"%1\\\"\"\nBasic.AdvAudio.Balance=\"Ravnoteža\"\nBasic.AdvAudio.BalanceSource=\"Saldo za '%1'\"\nBasic.AdvAudio.SyncOffset=\"Pomak sinkronizacije\"\nBasic.AdvAudio.SyncOffsetSource=\"Pomak sinkronizacije za '%1'\"\nBasic.AdvAudio.Monitoring=\"Praćenje zvuka\"\nBasic.AdvAudio.Monitoring.None=\"Monitor isključen\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Samo monitor (isključivanje zvuka)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitor i izlaz\"\nBasic.AdvAudio.MonitoringSource=\"Praćenje audio snimke za „%1”\"\nBasic.AdvAudio.AudioTracks=\"Izvori\"\nBasic.Settings.Hotkeys=\"Prečice\"\nBasic.Settings.Hotkeys.Pair=\"Kombinacije tastera deljene sa '%1' se ponašaju kao prekidači\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtriraj po prečacu\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Ovaj prečac dijeli jedna ili više drugih radnji, kliknite za prikaz sukoba\"\nBasic.Settings.Hotkeys.PleaseWait=\"Učitavanje prečaca, molimo pričekajte...\"\nBasic.Hotkeys.SelectScene=\"Prebaci na scenu\"\nBasic.SystemTray.Show=\"Prikaži\"\nBasic.SystemTray.Hide=\"Sakrij\"\nBasic.SystemTray.Message.Reconnecting=\"Veza prekinuta. Ponovno uspostavljanje...\"\nHotkeys.Insert=\"Umetni\"\nHotkeys.Delete=\"Izbriši\"\nHotkeys.Home=\"Početna\"\nHotkeys.End=\"Kraj\"\nHotkeys.PageUp=\"Stranica gore\"\nHotkeys.PageDown=\"Stranica dolje\"\nHotkeys.Tab=\"Kartica\"\nHotkeys.Print=\"Ispis\"\nHotkeys.Pause=\"Pauza\"\nHotkeys.Left=\"Strelica levo\"\nHotkeys.Right=\"Desno\"\nHotkeys.Up=\"Gore\"\nHotkeys.Down=\"Dolje\"\nHotkeys.Menu=\"Izbornik\"\nHotkeys.Space=\"Razmak\"\nHotkeys.NumpadNum=\"Numerički %1\"\nHotkeys.NumpadMultiply=\"Numerička zvezdica\"\nHotkeys.NumpadDivide=\"Numerička kosa crta\"\nHotkeys.NumpadAdd=\"Numerički plus\"\nHotkeys.NumpadSubtract=\"Numerički minus\"\nHotkeys.NumpadDecimal=\"Numerička tačka\"\nHotkeys.AppleKeypadNum=\"%1 (Tipkovnica)\"\nHotkeys.AppleKeypadMultiply=\"* (Tipkovnica)\"\nHotkeys.AppleKeypadDivide=\"/ (Tipkovnica)\"\nHotkeys.AppleKeypadAdd=\"+ (Tipkovnica)\"\nHotkeys.AppleKeypadSubtract=\"- (Tipkovnica)\"\nHotkeys.AppleKeypadDecimal=\". (Tipkovnica)\"\nHotkeys.AppleKeypadEqual=\"= (Tipkovnica)\"\nHotkeys.MouseButton=\"Miš %1\"\nMute=\"Mutiraj\"\nUnmute=\"Odmutiraj\"\nPush-to-mute=\"Stisni-za-mutiranje\"\nPush-to-talk=\"Stisni-za-govor\"\nSceneItemShow=\"Prikaži '%1'\"\nSceneItemHide=\"Sakrij '%1'\"\nOutputWarnings.NoTracksSelected=\"Morate odabrati makar jednu traku\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Greška u postavkama izlaza\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Za sve izlaze mora biti odabrana barem jedna audiotraka.\"\nOutputWarnings.MP4Recording=\"Upozorenje: Snimke spremljene u MP4/MOV formatu neće se moći oporaviti ako se datoteka ne može finalizirati (npr. zbog BSOD-a, nestanka struje itd.). Ako želite snimiti više audio zapisa, razmislite o korištenju MKV-a i ponovnom muksiranju snimke u MP4/MOV nakon što je završena (Datoteka → Remuksiranje snimaka)\"\nOutputWarnings.CannotPause=\"Upozorenje: Snimke se ne mogu pauzirati ako je koder snimanja postavljen na \\\"(Koristi koder streama)\\\"\"\nOutputWarnings.CodecIncompatible=\"Odabir enkodera videa ili zvuka ponovno postavljen zbog nekompatibilnosti. Molimo odaberite kompatibilni enkoder.\"\nCodecCompat.Incompatible=\"(Nekompatibilno s %1)\"\nCodecCompat.CodecPlaceholder=\"Odaberi enkoder...\"\nCodecCompat.ContainerPlaceholder=\"Odaberi format...\"\nCodecCompat.CodecMissingOnExit.Title=\"Nijedan enkoder nije odabran\"\nCodecCompat.CodecMissingOnExit.Text=\"Jedan ili više enkodera videa ili zvuka nije postavljeno. Molimo odaberite enkodere i za snimanje i za prijenos.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Nijedan format nije odabrana\"\nCodecCompat.ContainerMissingOnExit.Text=\"Nijedan format za snimanje nije odabran. Molimo odaberite format kompatibilan s odabranim enkoderom za prijenos.\"\nFinalScene.Title=\"Izbriši scenu\"\nFinalScene.Text=\"Mora postojati barem jedna scena.\"\nNoSources.Title=\"Nema izvora\"\nNoSources.Text=\"Izgleda da još niste dodali nijedan video izvor, pa će se prikazivati samo prazan zaslon. Jeste li sigurni da to želite učiniti?\"\nNoSources.Text.AddSource=\"Izvore možete dodati klikom na ikonu + ispod okvira Izvori u glavnom prozoru u bilo kojem trenutku.\"\nNoSources.Label=\"Nemate nijedan izvor.\\nKliknite gumb + ispod ili kliknite ovdje desnom tipkom miša da biste ga dodali.\"\nChangeBG=\"Postavi boju\"\nCustomColor=\"Prilagođena boja\"\nBrowserSource.EnableHardwareAcceleration=\"Omogući ubrzanje hardvera izvora preglednika\"\nAbout=\"O programu\"\nAbout.Info=\"OBS Studio je besplatan softver otvorenog koda za snimanje videa i prijenos uživo.\"\nAbout.Donate=\"Dajte doprinos\"\nAbout.GetInvolved=\"Uključite se\"\nAbout.Authors=\"Autori\"\nAbout.License=\"Dozvola\"\nAbout.Error=\"Greška! Datoteka se ne može pročitati.\\n\\nIdite na: %1\"\nAbout.Contribute=\"Podržite projekt OBS\"\nAddUrl.Title=\"Dodajte izvor putem URL adrese\"\nAddUrl.Text=\"Povukli ste URL u OBS. Ovim će se poveznica automatski dodati kao izvor. Želite li nastaviti?\"\nResizeOutputSizeOfSource=\"Promjena veličine izlaza (veličina izvora)\"\nResizeOutputSizeOfSource.Text=\"Osnovna i izlazna rezolucija bit će promijenjene u veličinu trenutnog izvora.\"\nResizeOutputSizeOfSource.Continue=\"Želite li nastaviti?\"\nPreviewTransition=\"Pregledaj prijelaz\"\nImporter=\"Uvoz kolekcije scena\"\nImporter.SelectCollection=\"Odaberite kolekciju scena\"\nImporter.Collection=\"Kolekcija scena\"\nImporter.HelpText=\"Dodaj datoteke u ovaj prozor za uvoz zbirki iz OBS-a ili drugih podržanih programa.\"\nImporter.Path=\"Put prikupljanja\"\nImporter.Program=\"Izbrisana aplikacija\"\nImporter.AutomaticCollectionPrompt=\"Automatsko pretraživanje kolekcija scena\"\nImporter.AutomaticCollectionText=\"OBS može automatski pronaći kolekcije scena koje se mogu uvesti iz podržanih programa trećih strana. Želite li da OBS automatski pronađe kolekcije za vas?\\n\\nTo možete kasnije promijeniti u Postavke > Općenito > Uvoznici.\"\nImporter.SelectFile=\"Pregledaj...\"\nRestart=\"Pokreni ponovno\"\nNeedsRestart=\"OBS Studio treba ponovno pokrenuti. Želite li ga sada ponovno pokrenuti?\"\nLoadProfileNeedsRestart=\"Profil sadrži postavke koje zahtijevaju ponovno pokretanje OBS-a:\\n%1\\n\\nŽelite li ponovno pokrenuti OBS da bi ove postavke stupile na snagu?\"\nContextBar.NoSelectedSource=\"Nije odabran nijedan izvor\"\nContextBar.MediaControls.PlayMedia=\"Reproduciraj medije\"\nContextBar.MediaControls.PauseMedia=\"Pauziraj medije\"\nContextBar.MediaControls.StopMedia=\"Zaustavi medije\"\nContextBar.MediaControls.RestartMedia=\"Ponovno pokretanje medija\"\nContextBar.MediaControls.PlaylistNext=\"Sljedeće na popisu za reprodukciju\"\nContextBar.MediaControls.PlaylistPrevious=\"Prethodno na popisu za reprodukciju\"\nContextBar.MediaControls.BlindSeek=\"Widget za pretraživanje medija\"\nYouTube.Auth.Ok=\"Autorizacija je uspješno dovršena.\\nSada možete zatvoriti ovu stranicu.\"\nYouTube.Auth.NoCode=\"Postupak autorizacije nije dovršen.\"\nYouTube.Auth.NoChannels=\"Nema dostupnih kanala na odabranom računu\"\nYouTube.Auth.WaitingAuth.Title=\"Autorizacija korisnika YouTubea\"\nYouTube.Auth.WaitingAuth.Text=\"Molimo Vas da dovršite autorizaciju u svom vanjskom pregledniku.<br> Ako se vanjski preglednik ne otvori, slijedite ovu poveznicu i dovršite autorizaciju:<br> %1\"\nYouTube.AuthError.Text=\"Nije uspjelo dohvaćanje informacija o kanalu: %1.\"\nYouTube.Actions.WindowTitle=\"Postavljanje emitiranja na YouTubeu - Kanal: %1\"\nYouTube.Actions.CreateNewEvent=\"Izradi novo emitiranje\"\nYouTube.Actions.ChooseEvent=\"Odaberite postojeću emisiju\"\nYouTube.Actions.Title=\"Naslov*\"\nYouTube.Actions.MyBroadcast=\"Moja emisija\"\nYouTube.Actions.Description=\"Opis\"\nYouTube.Actions.Privacy=\"Privatnost*\"\nYouTube.Actions.Privacy.Private=\"Osobno\"\nYouTube.Actions.Privacy.Public=\"Javno\"\nYouTube.Actions.Privacy.Unlisted=\"Nije javno dostupno\"\nYouTube.Actions.Category=\"Kategorija\"\nYouTube.Actions.Thumbnail=\"Sličica\"\nYouTube.Actions.Thumbnail.SelectFile=\"Odaberi datoteku...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Datoteka nije izabrana\"\nYouTube.Actions.Thumbnail.ClearFile=\"Očisti\"\nYouTube.Actions.MadeForKids=\"Je li ovaj videozapis namijenjen djeci?*\"\nYouTube.Actions.MadeForKids.Yes=\"Da, namijenjeno je djeci\"\nYouTube.Actions.MadeForKids.No=\"Ne, nije namijenjeno djeci\"\nYouTube.Actions.AdditionalSettings=\"Dodatne postavke\"\nYouTube.Actions.Latency=\"Latencija\"\nYouTube.Actions.Latency.Normal=\"Normalna\"\nYouTube.Actions.Latency.Low=\"Niska\"\nYouTube.Actions.Latency.UltraLow=\"Ultra nisko\"\nYouTube.Actions.EnableAutoStart=\"Omogući automatsko pokretanje\"\nYouTube.Actions.EnableAutoStop=\"Omogući automatsko zaustavljanje\"\nYouTube.Actions.AutoStartStop.TT=\"Označava treba li ovo zakazano emitiranje započeti automatski\"\nYouTube.Actions.EnableDVR=\"Omogući DVR\"\nYouTube.Actions.360Video=\"360 videozapis\"\nYouTube.Actions.ScheduleForLater=\"Zakaži za kasnije\"\nYouTube.Actions.RememberSettings=\"Zapamti ove postavke\"\nYouTube.Actions.Create_Ready=\"Izradi emitiranje\"\nYouTube.Actions.Create_GoLive=\"Izradi emitiranje i započni streaming\"\nYouTube.Actions.Choose_Ready=\"Odaberite emitiranje\"\nYouTube.Actions.Choose_GoLive=\"Odaberite emitiranje i započnite streaming\"\nYouTube.Actions.Create_Schedule=\"Raspored emitiranja\"\nYouTube.Actions.Create_Schedule_Ready=\"Zakažite i odaberite emitiranje\"\nYouTube.Actions.Dashboard=\"Otvori YouTube Studio\"\nYouTube.Actions.Error.Title=\"Pogreška pri stvaranju prijenosa uživo\"\nYouTube.Actions.Error.Text=\"Pogreška pristupa YouTubeu '%1'.<br/> Detaljan opis pogreške možete pronaći na <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Pogreška pri pristupu YouTubeu. Provjerite mrežnu vezu ili pristup YouTube poslužitelju.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Pogreška pri stvaranju emitiranja '%1'.<br/> Detaljan opis pogreške možete pronaći na <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Nije stvoren stream. Ponovno povežite svoj račun.\"\nYouTube.Actions.Error.YouTubeApi=\"Pogreška YouTube API-ja. Za više informacija pogledajte datoteku zapisnika.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Odabrana emisija nije pronađena.\"\nYouTube.Actions.Error.FileMissing=\"Odabrana datoteka ne postoji.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Nije uspjelo otvaranje odabrane datoteke.\"\nYouTube.Actions.Error.FileTooLarge=\"Odabrana datoteka je prevelika (ograničenje: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Prijenos emitiranja nije uspio: %1<br/><br/> Ako se ova greška nastavi, <a href='https://studio.youtube.com/video/%2/livestreaming'>otvorite emitiranje u YouTube Studiju</a> i pokušajte ručno.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Emitiranje prelazi u testnu fazu, što može potrajati. Pokušajte ponovno za 10-30 sekundi.\"\nYouTube.Actions.EventsLoading=\"Učitavanje popisa događaja...\"\nYouTube.Actions.EventCreated.Title=\"Događaj je kreiran\"\nYouTube.Actions.EventCreated.Text=\"Događaj je uspješno kreiran.\"\nYouTube.Actions.Stream=\"Internetski prijenos\"\nYouTube.Actions.Stream.ScheduledFor=\"Planirano za %1\"\nYouTube.Actions.Stream.Resume=\"Nastavi prekinuti prijenos\"\nYouTube.Actions.Stream.YTStudio=\"Automatski izradio YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Izrada novog prijenosa uživo, pričekajte...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Potrebno je ručno pokretanje\"\nYouTube.Actions.AutoStartStreamingWarning=\"Automatsko pokretanje za ovaj događaj je onemogućeno. Klikni na \\\"Idi uživo\\\" za početak emitiranja.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Nećete se moći ponovno povezati.<br>Vaš će prijenos prestati i više nećete biti uživo.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Prijenos uživo nije omogućen na odabranom YouTube kanalu.<br/><br/>Pogledaj <a href='https://www.youtube.com/features'>youtube.com/features</a> za više informacija.\"\nYouTube.Errors.livePermissionBlocked=\"Prijenos uživo nije dostupan na odabranom YouTube kanalu.<br/>Može proći i do 24 sata da prijenos uživo postane dostupan nakon što ga omogućite u postavkama kanala.<br/><br/>Pogledaj <a href='https://www.youtube.com/features'>youtube.com/features</a> za detalje.\"\nYouTube.Errors.errorExecutingTransition=\"Prijelaz nije uspio zbog pozadinske pogreške. Pokušajte ponovno za nekoliko sekundi.\"\nYouTube.Errors.errorStreamInactive=\"YouTube ne prima podatke za vaš stream. Provjerite konfiguraciju i pokušajte ponovno.\"\nYouTube.Errors.invalidTransition=\"Pokušaj prijelaza nije bio valjan. To je možda zato što stream nije završio prethodni prijelaz. Pričekajte nekoliko sekundi i pokušajte ponovno.\"\nYouTube.DocksRemoval.Title=\"Očisti stara sidrišta preglednika za YouTube\"\nYouTube.DocksRemoval.Text=\"Ova sidrišta za preglednik uklonit će se na zahtjev:\\n\\n%1\\nUpotrijebite \\\"Docks/YouTube Live Control Room\\\" umjesto njih.\"\nConfigDownload.WarningMessageTitle=\"Upozorenje\"\nFailedToStartStream.MissingConfigURL=\"Nema dostupnog URL-a za konfiguraciju za trenutnu uslugu\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Prilagođeni RTMP URL nije naveden\"\nFailedToStartStream.InvalidCustomConfig=\"Nevažeća prilagođena konfiguracija\"\nFailedToStartStream.MissingCanvas=\"Nedostaje konfigurirano dodatno platno\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Nije uspjelo stvaranje višekanalne video usluge\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Nije uspjelo stvaranje RTMP izlaza za višekanalni video\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC nije dostupan.\\n\\nNije pronađen tip enkodera '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Nije uspjelo stvaranje video kodera '%1' (vrsta: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Nije uspjelo dohvaćanje informacija o OBS videu prilikom stvaranja kodera '%1' (vrsta: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Nije uspjelo stvaranje audio kodera\"\nFailedToStartStream.NoRTMPURLInConfig=\"Konfiguracija ne sadrži RTMP(S) URL cilja streama\"\nFailedToStartStream.FallbackToDefault=\"Pokretanje streama pomoću %1 failed; želite li ponovno pokušati s postavkama jednog kodiranja?\"\nFailedToStartStream.ConfigRequestFailed=\"Nije moguće dohvatiti konfiguraciju iz %1<br><br> HTTP pogreška: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Primljena nepoznata vrijednost statusa '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br> \\nŽelite li nastaviti streaming bez %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br> Želite li nastaviti streaming?\"\nFailedToStartStream.MissingEncoderConfigs=\"Konfiguracija za objavljivanje nije uključivala konfiguracije kodera\"\nFailedToStartStream.InvalidEncoderConfig=\"Konfiguracija za objavljivanje sadržaja uključivala je nevažeću konfiguraciju kodera\"\nFailedToStartStream.StatusMissingHTML=\"Zahtjev za objavu vratio je neodređenu grešku\"\nFailedToStartStream.NoConfigSupplied=\"Nedostaje konfiguracija\"\nMultitrackVideo.Info=\"%1 automatski optimizira vaše postavke za kodiranje i slanje više kvaliteta videa. Odabirom ove opcije poslat će se %2 informacije o vašem računalu i postavkama softvera.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Nekompatibilne postavke\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 trenutno nije kompatibilan s:\\n\\n%2\\nDa biste nastavili strujati s %1, onemogućite nekompatibilne postavke:\\n\\n%3\\ni ponovno pokrenite strujanje.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Onemogući za ovaj stream i pokreni streaming\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Ažuriraj postavke i započni streaming\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 trenutno nije kompatibilan s postavkom [Audio → Općenito → Kanali] postavljenom na '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Zvuk → Općenito → Kanali] treba biti postavljeno na '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 zahtijeva više različitih postavki za [Zvuk → Općenito → Kanali]\"\nBasic.OpenPluginManager=\"Upravljanje dodacima\"\nPluginManager=\"Upravljanje dodacima\"\nPluginManager.Restart=\"OBS Studio treba ponovno pokrenuti kako bi se promjene primijenile na dodatak\"\nPluginManager.MissingPlugin=\"[DODATAK NIJE PRONAĐEN]\"\nPluginManager.Section.Discover=\"Pregledaj\"\nPluginManager.Section.Manage=\"Instalirano\"\nPluginManager.Section.Updates=\"Ažuriranja\"\nPluginManager.Section.Manage.Title=\"Upravljanje omogućenim dodacima\"\n"
  },
  {
    "path": "frontend/data/locale/hu-HU.ini",
    "content": "Language=\"Magyar\"\nApply=\"Alkalmaz\"\nCancel=\"Mégse\"\nClose=\"Bezárás\"\nSave=\"Mentés\"\nDiscard=\"Elvetés\"\nDisable=\"Letiltás\"\nYes=\"Igen\"\nNo=\"Nem\"\nAdd=\"Hozzáadás\"\nRemove=\"Eltávolítás\"\nRename=\"Átnevezés\"\nInteract=\"Interakció\"\nFilters=\"Szűrők\"\nProperties=\"Tulajdonságok\"\nMoveUp=\"Mozgatás fel\"\nMoveDown=\"Mozgatás le\"\nSettings=\"Beállítások\"\nDisplay=\"Kijelző\"\nName=\"Név\"\nExit=\"Kilépés\"\nMixer=\"Hangkeverő\"\nBrowse=\"Tallózás\"\nStereo=\"Sztereó\"\nDroppedFrames=\"Eldobott képkockák: %1 (%2%)\"\nProjector.Open.Program=\"Programvetítő megnyitása\"\nProjector.Open.Preview=\"Előnézet-vetítő megnyitása\"\nProjector.Open.Scene=\"Jelenetvetítő megnyitása\"\nProjector.Open.Source=\"Forrásvetítő megnyitása\"\nProjector.Open.Multiview=\"Többszörös nézet megnyitása\"\nProjector.Display=\"Kijelző: %1\"\nProjector.Window=\"Új ablak\"\nProjector.Title=\"Vetítő\"\nProjector.Title.Scene=\"Jelenet: %1\"\nProjector.Title.Source=\"Forrás: %1\"\nProjector.Title.Multiview=\"Többszörös nézet\"\nProjector.ResizeWindowToContent=\"Ablak igazítása a tartalomhoz\"\nClear=\"Törlés\"\nRevert=\"Visszavonás\"\nShow=\"Megjelenítés\"\nHide=\"Elrejtés\"\nUnhideAll=\"Összes megjelenítése\"\nUntitled=\"Névtelen\"\nNew=\"Új\"\nDuplicate=\"Másolat készítése\"\nEnable=\"Engedélyezés\"\nDisableOSXVSync=\"macOS V-Sync kikapcsolása\"\nResetOSXVSyncOnExit=\"macOS V-Sync visszaállítása kilépéskor\"\nHighResourceUsage=\"Túlterhelt kódolás! Fontolja meg a videóbeállítások csökkentését vagy a gyorsabb kódolókészletre való váltást.\"\nTransition=\"Átmenet\"\nQuickTransitions=\"Gyors átmenetek\"\nFadeToBlack=\"Elfeketítés\"\nLeft=\"Bal\"\nRight=\"Jobb\"\nTop=\"Felső\"\nBottom=\"Alsó\"\nReset=\"Visszaállítás\"\nHours=\"óra\"\nMinutes=\"perc\"\nSeconds=\"másodperc\"\nDeprecated=\"Elavult\"\nReplayBuffer=\"Visszajátszási puffer\"\nImport=\"Importálás\"\nExport=\"Exportálás\"\nCopy=\"Másolás\"\nPaste=\"Beillesztés\"\nPasteReference=\"Beillesztés (hivatkozás)\"\nPasteDuplicate=\"Beillesztés (másolat)\"\nRemuxRecordings=\"Felvételek remuxolása\"\nNext=\"Következő\"\nBack=\"Vissza\"\nDefaults=\"Alapbeállítások\"\nRestoreDefaults=\"Alapbeállítások\"\nHideMixer=\"Elrejtés a keverőben\"\nTransitionOverride=\"Átmenet felülírása\"\nShowTransition=\"Átmenet megjelenítéskor\"\nHideTransition=\"Átmenet elrejtéskor\"\nNone=\"Nincs\"\nStudioMode.Preview=\"Előnézet\"\nStudioMode.PreviewSceneName=\"Előnézet: %1\"\nShowInMultiview=\"Megjelenítés többszörös nézetben\"\nVerticalLayout=\"Függőleges elrendezés\"\nGroup=\"Csoport\"\nDoNotShowAgain=\"Ne jelenjen meg többet\"\nDefault=\"(Alapértelmezett)\"\nCalculating=\"Számítás...\"\nFullscreen=\"Teljes képernyő\"\nWindowed=\"Ablakos\"\nRefreshBrowser=\"Frissítés\"\nAspectRatio=\"Képarány <b>%1:%2</b>\"\nLockVolume=\"Hangerő zárolása\"\nLogViewer=\"Naplómegjelenítő\"\nShowOnStartup=\"Mutatás indításkor\"\nOpenFile=\"Fájl megnyitása\"\nAddScene=\"Jelenet hozzáadása\"\nAddSource=\"Forrás hozzáadása\"\nRemoveScene=\"Kiválasztott forrás eltávolítása\"\nRemoveSource=\"Kiválasztott források eltávolítása\"\nMoveSceneUp=\"Jelenet fentebb helyezése\"\nMoveSceneDown=\"Jelenet lentebb helyezése\"\nMoveSourceUp=\"Források fentebb helyezése\"\nMoveSourceDown=\"Források lentebb helyezése\"\nSourceProperties=\"Forrástulajdonságok megjelenítése\"\nSourceFilters=\"Forrásszűrők megnyitása\"\nMixerToolbarMenu=\"Hangkeverő menü\"\nSceneFilters=\"Jelenetszűrők megnyitása\"\nList=\"Lista\"\nGrid=\"Rács\"\nAutomatic=\"Automatikus\"\nComingSoon=\"Hamarosan\"\nPluginsFailedToLoad.Title=\"Hiba a bővítmény betöltése során\"\nPluginsFailedToLoad.Text=\"A következő OBS bővítmények betöltése sikertelen:\\n\\n%1\\nFrissítse vagy távolítsa el ezeket a bővítményeket.\"\nAlreadyRunning.Title=\"Az OBS már fut\"\nAlreadyRunning.Text=\"Az OBS már fut. Hacsak nem ez volt a célja, akkor állítsa le az OBS összes futó példányát egy új indítása előtt. Ha úgy állította be az OBS-t, hogy rendszertálcára minimalizálódjon, akkor ellenőrizze, hogy ott fut-e még.\"\nAlreadyRunning.LaunchAnyway=\"Indítás mindenképpen\"\nCrashHandling.Dialog.Title=\"Az OBS Studio összeomlása észlelve\"\nCrashHandling.Labels.Text=\"Az OBS Studio nem megfelelően állt le.\\n\\nÚjraindítja csökkentett módban (letiltja a harmadik féltől származó bővítményeket, szkripteket és WebSocketeket)?\"\nCrashHandling.Labels.PrivacyNotice=\"Automatikusan is feltöltheti a legfrissebb összeomlás-jelentést az OBS Projekt felé.<br /><br />Mielőtt bármilyen fájlt feltöltene, olvassa el az <a href='https://obsproject.com/privacy-policy'>Adatvédelmi irányelveket</a>, és különösen figyeljen oda a fájlfeltöltésekre vonatkozó részeket.\"\nCrashHandling.Checkbox.SendReport=\"Elolvastam az adatvédelmi irányelveket, és beleegyezek a feltöltésbe.\"\nCrashHandling.Buttons.LaunchSafe=\"Futtatás csökkentett módban\"\nCrashHandling.Buttons.LaunchNormal=\"Futtatás normál módban\"\nCrashHandling.Errors.UploadJSONError=\"Hiba történt a legutóbbi összeomlás-jelentés feltöltési kísérlete során. Próbálja újra később.\"\nCrashHandling.Errors.Title=\"Összeomlásjelentés-feltöltési hiba\"\nSafeMode.Restart=\"Újraindítja csökkentett módban az OBS-t (letiltja a harmadik féltől származó bővítményeket, szkripteket és WebSocketeket)?\"\nSafeMode.RestartNormal=\"Újraindítja normál módban az OBS-t?\"\nChromeOS.Title=\"Nem támogatott platform\"\nChromeOS.Text=\"Úgy néz ki, hogy az OBS ChromeOS konténerben fut. Ez a platform nem támogatott.\"\nWine.Title=\"Wine észlelve\"\nWine.Text=\"Az OBS Wine-ban futtatása nem támogatott, és számos funkció, mint a rögzítés és az eszközforrások nem fognak működni, vagy csak korlátozottan.<br><br>Az OBS natív verziójának futtatása javasolt, például a <a href='https://flathub.org/apps/details/com.obsproject.Studio'>Flatpak verzió</a> vagy az operációs rendszerének csomagjainak használatával.\"\nDockCloseWarning.Title=\"Dokkolható ablak bezárása\"\nDockCloseWarning.Text=\"Most zárt be egy dokkolható ablakot. Ha szeretné újra megjeleníteni, akkor használja a Dokkok menüt a menüsávon.\"\nExtraBrowsers=\"Egyéni böngésződokkok\"\nExtraBrowsers.Info=\"Adjon hozzá dokkokat név és URL megadásával, majd kattintson az Alkalmaz vagy Bezárás gombra a dokkok megnyitásához. Bármikor tud dokkokat hozzáadni vagy eltávolítani.\"\nExtraBrowsers.DockName=\"Dokk neve\"\nAuth.Authing.Title=\"Hitelesítés…\"\nAuth.Authing.Text=\"Hitelesítés a következővel: %1, kis türelmet…\"\nAuth.AuthFailure.Title=\"Hitelesítési hiba\"\nAuth.AuthFailure.Text=\"Sikertelen hitelesítés a következővel: %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Hitelesítés szükséges\"\nAuth.InvalidScope.Text=\"A(z) %1 hitelesítési követelményei megváltoztak. Egyes funkciók lehet, hogy nem lesznek elérhetők.\"\nAuth.LoadingChannel.Title=\"Csatornainformációk betöltése…\"\nAuth.LoadingChannel.Text=\"A(z) %1 csatornainformációinak betöltése, kis türelmet…\"\nAuth.LoadingChannel.Error=\"Nem sikerült lekérni a csatornainformációkat.\"\nAuth.ChannelFailure.Title=\"A csatorna betöltése sikertelen\"\nAuth.ChannelFailure.Text=\"A csatornainformációk betöltése sikertelen: %1\\n\\n%2: %3\"\nAuth.Chat=\"Csevegő\"\nAuth.StreamInfo=\"Stream információ\"\nTwitchAuth.Stats=\"Twitch statisztika\"\nTwitchAuth.Feed=\"Twitch tevékenységnapló\"\nTwitchAuth.TwoFactorFail.Title=\"A közvetítítési kulcsot nem lehetett lekérni\"\nTwitchAuth.TwoFactorFail.Text=\"Az OBS nem tudott kapcsolódni a Twitch-fiókjához. Győződjön meg arról, hogy kétfaktoros hitelesítés be van állítva a <a href='https://www.twitch.tv/settings/security'>Twitch biztonsági beállításai</a> között, mivel ez szükséges a közvetítéshez.\"\nRestreamAuth.Channels=\"Restream csatornák\"\nCopy.Filters=\"Szűrők másolása\"\nPaste.Filters=\"Szűrők beillesztése\"\nBrowserPanelInit.Title=\"Böngésző előkészítése…\"\nBrowserPanelInit.Text=\"Böngésző előkészítése, kis türelmet…\"\nBandwidthTest.Region=\"Régió\"\nBandwidthTest.Region.US=\"Egyesült Államok\"\nBandwidthTest.Region.EU=\"Európa\"\nBandwidthTest.Region.Asia=\"Ázsia\"\nBandwidthTest.Region.Other=\"Egyéb\"\nBasic.AutoConfig=\"Automatikus konfiguráció varázsló\"\nBasic.AutoConfig.ApplySettings=\"Beállítások alkalmazása\"\nBasic.AutoConfig.StartPage=\"Használati információk\"\nBasic.AutoConfig.StartPage.SubTitle=\"Adja meg, hogy mire szeretné használni a programot\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Közvetítésre optimalizálás, a felvétel másodlagos\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Rögzítésre optimalizálás, a közvetítés másodlagos\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Csak a virtuális kamerát fogom használni\"\nBasic.AutoConfig.VideoPage=\"Videóbeállítások\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Adja meg a használandó videóbeállításokat\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Jelenlegi használata (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"%1. kijelző (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Jelenlegi használata (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 vagy 30, de inkább 60, ha lehetséges\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 vagy 30, de inkább nagyobb felbontás\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Megjegyzés: A vászon (alap) felbontása nem feltétlenül ugyanaz, mint a stream vagy felvétel felbontása. A tényleges stream/felvétel felbontása esetlegesen lefele lesz skálázva az erőforrások leterheltsége vagy a bitsebesség követelmények elérése érdekében.\"\nBasic.AutoConfig.StreamPage=\"Közvetítés információi\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Adja meg a közvetítés információit\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Fiók hozzákapcsolása (ajánlott)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Fiók szétkapcsolása\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Fiók szétkapcsolása?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Ez a változtatás azonnal érvénybe lép. Biztos, hogy megszakítja a kapcsolatot a fiókjával?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Közvetítési kulcs lekérése\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"További információk\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Közvetítési kulcs használata\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Közvetítési kulcs használata (speciális)\"\nBasic.AutoConfig.StreamPage.Service=\"Szolgáltatás\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Összes megjelenítése...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Egyéni…\"\nBasic.AutoConfig.StreamPage.Server=\"Kiszolgáló\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Közvetítési kulcs\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: adja meg a titkosítási jelmondatot.\\nRTMP: adja meg a szolgáltatás által biztosított kulcsot.\\nSRT: adja meg a „streamid” azonosítót, ha a szolgáltatás használja.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kódolókulcs\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Bemutatandó token\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Csatlakoztatott fiók\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Bitsebesség megbecsülése sávszélesség teszttel (néhány percig is eltarthat)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Hardveres kódolás előnyben részesítése\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"A hardveres kódolás megszünteti a CPU erőforrás-használat tetemes részét, viszont sokkal több bitsebességre lehet szükség az azonos képminőség eléréséhez.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Közvetítési figyelmeztetés\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"A sávszélesség teszt véletlenszerűsített videoadatokat fog közvetíteni hang nélkül a csatornájára. Ha tudja, ideiglenesen kapcsolja ki a streamek rögzítését és állítsa a streamet privátra amíg a teszt befejeződik. Folytatja?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"%1 tesztelése\"\nBasic.AutoConfig.TestPage=\"Végeredmény\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"A program most különböző tesztekkel megbecsüli a legideálisabb beállításokat\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"A teszt befejeződött\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Sávszélesség teszt végrehajtása, ez egy pár percig eltarthat...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Nem található kimenet a szolgáltatás protokolljához\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Csatlakozás: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Nem sikerült kapcsolódni egy szerverhez sem, ellenőrizze az internetkapcsolatát és próbálja újra.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Sávszélesség vizsgálat: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Stream kódoló tesztelése, ez néhány percig is eltarthat...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Felvétel kódoló tesztelése, ez néhány percig is eltarthat...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Kódoló indítása sikertelen\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Tesztelés: %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Közvetítések kódolója\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Felvétel kódoló\"\nBasic.AutoConfig.TestPage.Result.Header=\"A program megállapította, hogy ezek a becsült beállítások a legideálisabbak az ön számára:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"A beállítások használatához kattintson a Beállítások alkalmazása gombra. A varázsló újrakonfigurálásához és az újrapróbálkozáshoz, kattintson a Vissza gombra. A manuális beállításokhoz kattintson a Mégse gombra és nyissa meg a Beállításokat.\"\nBasic.AutoConfig.Info=\"Az automatikus konfigurációs varázsló a számítógép műszaki adatait és az internet sebessége alapján meghatározza a legjobb beállításokat.\"\nBasic.AutoConfig.RunAnytime=\"Ezt bármikor futtathatja az Eszközök menüben.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Közvetítéshez használt (átméretezett) felbontás\"\nBasic.Stats=\"Statisztika\"\nBasic.Stats.CPUUsage=\"Processzorhasználat\"\nBasic.Stats.HDDSpaceAvailable=\"Szabad lemezterület\"\nBasic.Stats.MemoryUsage=\"Memóriahasználat\"\nBasic.Stats.AverageTimeToRender=\"Átlagos képkocka feldolgozási idő\"\nBasic.Stats.SkippedFrames=\"Kihagyott képkockák kódolási lag miatt\"\nBasic.Stats.MissedFrames=\"Renderlag miatt nem fogadott képkockák\"\nBasic.Stats.Output.Recording=\"Felvétel\"\nBasic.Stats.Status=\"Állapot\"\nBasic.Stats.Status.Recording=\"Rögzítés\"\nBasic.Stats.Status.Live=\"ÉLŐ\"\nBasic.Stats.Status.Reconnecting=\"Újracsatlakozás\"\nBasic.Stats.Status.Inactive=\"Inaktív\"\nBasic.Stats.Status.Active=\"Aktív\"\nBasic.Stats.DroppedFrames=\"Képkockák ejtve (Hálózat)\"\nBasic.Stats.MegabytesSent=\"Összes kimeneti adat\"\nBasic.Stats.Bitrate=\"Bitsebesség\"\nBasic.Stats.DiskFullIn=\"Lemez megtelik (kb.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 óra, %2 perc\"\nBasic.Stats.ResetStats=\"Statisztika visszaállítása\"\nResetUIWarning.Title=\"Biztos, hogy alaphelyzetbe állítja a felületet?\"\nResetUIWarning.Text=\"A felület visszaállítása elrejti a további dokkokat. Meg kell szüntetnie az elrejtést a Dokkok menüben, ha azt szeretné, hogy láthatóak legyenek.\\n\\nBiztos, hogy visszaállítja a felületet?\"\nUpdater.Title=\"Új frissítés elérhető\"\nUpdater.Text=\"Új frissítés elérhető:\"\nUpdater.UpdateNow=\"Frissítés most\"\nUpdater.RemindMeLater=\"Emlékeztessen később\"\nUpdater.Skip=\"Verzió átugrása\"\nUpdater.NoUpdatesAvailable.Title=\"Nincs elérhető frissítés\"\nUpdater.NoUpdatesAvailable.Text=\"Jelenleg nincs elérhető frissítés\"\nUpdater.BranchNotFound.Title=\"Frissítési csatorna eltávolítva\"\nUpdater.BranchNotFound.Text=\"A kiválasztott frissítési csatorna már nem érhető el, az OBS visszaállt az alapértelmezettre.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Az épségellenőrzés nem érhető el\"\nUpdater.RepairButUpdatesAvailable.Text=\"A fájl épségének ellenőrzése csak a legfrissebb elérhető verzió esetén érhető el. Használja a Súgó → Frissítések keresése lehetőséget az ellenőrzéshez és az OBS telepítés frissítéséhez.\"\nUpdater.RepairConfirm.Title=\"Épségellenőrzés megerősítése\"\nUpdater.RepairConfirm.Text=\"Az épségellenőrzés elindítása átvizsgálja az OBS telepítést sérült fájlokat keresve, és újra letölti a sérült/módosított fájlokat. Ez eltarthat egy ideig.\\n\\nFolytatja?\"\nUpdater.FailedToLaunch=\"A frissítő indítása sikertelen\"\nQuickTransitions.SwapScenes=\"Előnézet/programjelenetek cseréje átmenet után\"\nQuickTransitions.SwapScenesTT=\"Az előnézet és a programjelenet cseréje átmenet után (ha a program eredeti jelenete még létezik).\\nEz nincs kihatással a program eredeti jelenetére.\"\nQuickTransitions.DuplicateScene=\"Jelenet kettőzése\"\nQuickTransitions.DuplicateSceneTT=\"Ugyanazon jelenet szerkesztésekor lehetősége van a program kimenetének átformálására ás a láthatóságának megváltoztatására.\\nA források tulajdonságainak a program kimenének módosítása nélkül történő szerkesztéséhéz engedélyezze a „Források kettőzés” lehetőséget.\\nAz érték megváltoztatása visszaállítja a jelenlegi programjelenetet (ha az még létezik).\"\nQuickTransitions.EditProperties=\"Forrás kettőzése\"\nQuickTransitions.EditPropertiesTT=\"Egyazon jelenet szerkesztésekor lehetősége van a források tulajdonságainak a program kimenének módosítása nélkül történő szerkesztésére.\\nEz csak akkor használható, ha a „Jelenet kettőzése” engedélyezve van.\\nEgyes források (például a felvevő és a médiaforrások) nem támogatják ezt, és nem lehet őket külön szerkeszteni.\\nAz érték megváltoztatása visszaállítja a jelenlegi programjelenetet (ha az még létezik).\\n\\nFigyelem: A források kettőzése további erőforrásigénnyel járhat.\"\nQuickTransitions.HotkeyName=\"Gyors átmenet: %1\"\nBasic.AddTransition=\"Konfigurálható átmenet hozzáadása\"\nBasic.RemoveTransition=\"Konfigurálható átmenet eltávolítása\"\nBasic.TransitionProperties=\"Átmenet tulajdonságai\"\nBasic.SceneTransitions=\"Jelenetátmenetek\"\nBasic.TransitionDuration=\"Időtartam\"\nBasic.TogglePreviewProgramMode=\"Stúdió mód\"\nBasic.EnablePreviewProgramMode=\"Stúdió mód engedélyezése\"\nBasic.DisablePreviewProgramMode=\"Stúdió mód letiltása\"\nUndo.Undo=\"Visszavonás\"\nUndo.Redo=\"Mégis\"\nUndo.Add=\"„%1” hozzáadása\"\nUndo.Delete=\"„%1” törlése\"\nUndo.Rename=\"„%1” átnevezése\"\nUndo.SceneCollection.Switch=\"Váltás erre: „%1”\"\nUndo.Item.Undo=\"%1 Visszavonása\"\nUndo.Item.Redo=\"%1 Újrateremtése\"\nUndo.Sources.Multi=\"%1 forrásinak törlése\"\nUndo.Filters=\"„%1” szűrő megváltoztatása\"\nUndo.Filters.Paste.Single=\"„%1” szűrő beillesztése ide: „%2”\"\nUndo.Filters.Paste.Multiple=\"Szűrők másolása innen: „%1”, ide: „%2”\"\nUndo.Transform=\"Források átalakítása itt: „%1”\"\nUndo.Transform.Paste=\"Átalakítás beillesztése itt: „%1”\"\nUndo.Transform.Rotate=\"Forgatás itt: „%1”\"\nUndo.Transform.Reset=\"Átalakítás visszaállítása itt: „%1”\"\nUndo.Transform.HFlip=\"Vízszintes átforgatás itt: „%1”\"\nUndo.Transform.VFlip=\"Függőleges átforgatás itt: „%1”\"\nUndo.Transform.FitToScreen=\"Képernyőhöz igazítás itt: „%1”\"\nUndo.Transform.StretchToScreen=\"Képernyőhöz nyújtás itt: „%1”\"\nUndo.Transform.Center=\"Középre igazítás itt: „%1”\"\nUndo.Transform.VCenter=\"Függőlegesen középre igazítás itt: „%1”\"\nUndo.Transform.HCenter=\"Vízszintesen középre igazítás itt: „%1”\"\nUndo.Volume.Change=\"Hangerő módosítása itt: „%1”\"\nUndo.Volume.Mute=\"„%1” némítása\"\nUndo.Volume.Unmute=\"„%1” némításának feloldása\"\nUndo.Balance.Change=\"Hangegyensúly módosítása itt: „%1”\"\nUndo.SyncOffset.Change=\"Hangszinkron eltolásának módosítása ezen: „%1”\"\nUndo.MonitoringType.Change=\"Hangfigyelés módosítása ezen: „%1”\"\nUndo.Mixers.Change=\"Hangkeverők módosítása ezen: „%1”\"\nUndo.ForceMono.On=\"Mono hang kényszerítése be itt: „%1”\"\nUndo.ForceMono.Off=\"Mono hang kényszerítése ki itt: „%1”\"\nUndo.Properties=\"Tulajdonságmódosítás itt: „%1”\"\nUndo.Scene.Duplicate=\"„%1” jelenet kettőzése\"\nUndo.ShowTransition=\"Átmenet megjelenítése itt: „%1”\"\nUndo.HideTransition=\"Átmenet elrejtése itt: „%1”\"\nUndo.ShowSceneItem=\"„%1” megjelenítése itt: „%2”\"\nUndo.HideSceneItem=\"„%1” elrejtése itt: „%2”\"\nUndo.ReorderSources=\"Források átrendezése itt: „%1”\"\nUndo.MoveUp=\"„%1” fentebb helyezése itt: „%2”\"\nUndo.MoveDown=\"„%1” lentebb helyezése itt: „%2”\"\nUndo.MoveToTop=\"„%1” legfelülre helyezése itt: „%2”\"\nUndo.MoveToBottom=\"„%1” legalulra helyezése itt: „%2”\"\nUndo.PasteSource=\"Források beillesztése ide: „%1”\"\nUndo.PasteSourceRef=\"Forráshivatkozások beillesztése ide: „%1”\"\nUndo.GroupItems=\"Elemek csoportosítása ebbe: „%1”\"\nTransitionNameDlg.Text=\"Adja meg az átmenet nevét\"\nTransitionNameDlg.Title=\"Átmenet neve\"\nTitleBar.SafeMode=\"CSÖKKENTETT MÓD\"\nTitleBar.PortableMode=\"Hordozható mód\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Jelenetek\"\nNameExists.Title=\"A megadott név már létezik\"\nNameExists.Text=\"A név már használatban van.\"\nNoNameEntered.Title=\"Kérem adjon meg egy érvényes nevet\"\nNoNameEntered.Text=\"Üres nevek nem használhatóak.\"\nConfirmStart.Title=\"Közvetítés indítása?\"\nConfirmStart.Text=\"Biztos, hogy elindítja a közvetítést?\"\nConfirmStop.Title=\"Közvetítés leállítása?\"\nConfirmStop.Text=\"Biztos, hogy leállítja a közvetítést?\"\nConfirmStopRecord.Title=\"Felvétel leállítása?\"\nConfirmStopRecord.Text=\"Biztos, hogy leállítja a felvételt?\"\nConfirmBWTest.Title=\"Sávszélesség teszt indítása?\"\nConfirmBWTest.Text=\"Az OBS sávszélességteszt módra van beállítva. Ez a mód hálózattesztelésre való, anélkül működik, hogy élő adást indítana. Amint a vizsgálat lezajlott, ki kell kapcsolnia, hogy az adás a nézők számára is megjelenjen.\\n\\nFolytatja?\"\nConfirmExit.Title=\"Aktív kimenetek\"\nConfirmExit.Text=\"Az OBS jelenleg is aktív. Az összes közvetítés/felvétel le fog állni.\"\nConfirmRemove.Title=\"Eltávolítás megerősítése\"\nConfirmRemove.Text=\"Biztos, hogy eltávolítja a következőt: „%1”?\"\nConfirmRemove.TextMultiple=\"Biztos, hogy eltávolítja ezt a(z) %1 elemet?\"\nConfirmReset.Title=\"Tulajdonságok visszaállítása\"\nConfirmReset.Text=\"Biztos, hogy visszaállítja a jelenlegi tulajdonságokat az alapértelmezéseikre?\"\nOutput.StartStreamFailed=\"Közvetítés indítása sikertelen\"\nOutput.StartRecordingFailed=\"Felvétel indítása sikertelen\"\nOutput.StartReplayFailed=\"Visszajátszás puffer indítása sikertelen\"\nOutput.StartVirtualCamFailed=\"A virtuális kamera indítása sikertelen\"\nOutput.StartFailedGeneric=\"A kimenet indítása sikertelen. Ellenőrizze az eseménynaplóban a részleteket.\\n\\nMegjegyzés: NVENC vagy AMD kódoló használata esetén, győződjön meg arról, hogy az illesztőprogramok naprakészek.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Szünet esetén nem lehet visszajátszást menteni\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Figyelmeztetés: Visszajátszást nem lehet menteni, amíg a felvétel szünetel.\"\nOutput.ConnectFail.Title=\"Csatlakozás sikertelen\"\nOutput.ConnectFail.BadPath=\"Érvénytelen elérési út vagy kapcsolati URL cím. Kérem, ellenőrizze a beállításokat és győződjön meg az érvényességükről.\"\nOutput.ConnectFail.ConnectFailed=\"Nem sikerült kapcsolódni a szerverhez\"\nOutput.ConnectFail.InvalidStream=\"Nem lehet hozzáférni a megadott csatornához vagy stream kulcshoz, kérem ellenőrizze a kulcsot. Ha helyesnek találja, akkor probléma merült fel a szerverhez csatlakozás során.\"\nOutput.ConnectFail.HdrDisabled=\"A HDR kimenet jelenleg le van tiltva ennél a kimenetnél.\"\nOutput.ConnectFail.Error=\"A kiszolgálóhoz való kapcsolódás során váratlan hiba történt. További információért tekintse meg a naplófájlt.\"\nOutput.ConnectFail.Disconnected=\"Kiszolgálóról lecsatlakoztatva.\"\nOutput.StreamEncodeError.Title=\"Kódolási hiba\"\nOutput.StreamEncodeError.Msg=\"Kódolóhiba történt közvetítés közben.\"\nOutput.StreamEncodeError.Msg.LastError=\"Kódolóhiba történt közvetítés közben: <br><br>%1\"\nOutput.RecordFail.Title=\"Felvétel indítása sikertelen\"\nOutput.RecordFail.Unsupported=\"A kimeneti formátum vagy nem támogatott, vagy nem támogatja az egynél több hangsávot. Kérem, ellenőrizze a beállításait és próbálkozzon újra.\"\nOutput.RecordNoSpace.Title=\"Nincs elég szabad lemezterület\"\nOutput.RecordNoSpace.Msg=\"Nincs elegendő lemezterület a felvétel folytatásához.\"\nOutput.RecordError.Title=\"Felvételi hiba\"\nOutput.RecordError.Msg=\"Ismeretlen hiba lépett fel a felvétel során.\"\nOutput.RecordError.EncodeErrorMsg=\"Kódolási hiba történt felvétel készítése közben.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Kódolóhiba történt a felvétel közben: <br><br>%1\"\nOutput.BadPath.Title=\"A fájl elérési útja hibás\"\nOutput.BadPath.Text=\"A beállított felvételi elérési út nem nyitható meg. Ellenőrizze a felvételi útvonalat a Beállítások → Kimenet → Felvétel alatt.\"\nOutput.NoBroadcast.Title=\"Nincs adás beállítva\"\nOutput.NoBroadcast.Text=\"Be kell állítania egy adást, hogy elindíthassa a közvetítést.\"\nOutput.BroadcastStartFailed=\"Az adás elindítása sikertelen\"\nOutput.BroadcastStopFailed=\"Az adás leállítása sikertelen\"\nLogUploadDialog.Title=\"OBS Studio-naplófájlfeltöltés\"\nLogUploadDialog.Labels.PrivacyNotice=\"Mielőtt bármilyen fájt feltöltene, olvassa el az <a href='https://obsproject.com/privacy-policy'>Adatvédelmi irányelveket</a> és a fájlfeltöltésre vonatkozó szakaszokat.\"\nLogUploadDialog.Labels.Progress=\"A napló feltöltése folyamatban van. Kis türelmet…\"\nLogUploadDialog.Labels.Description.AppLog=\"A naplófájl feltöltésre került. Most már megoszthatja ezt a webcímet hibakeresési és támogatási célokból.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Az összeomlási jelentés feltöltésre került. Most már megoszthatja ezt a webcímet hibakeresési célokból.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Feltöltés\"\nLogUploadDialog.Buttons.CopyURL=\"Napló webcímének másolása\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Naplófájl elemzése\"\nLogUploadDialog.Buttons.RetryButton=\"Újra\"\nLogUploadDialog.Errors.Template=\"Hiba történt a fájl feltöltése során:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Nem található feltöltendő fájl, vagy a fájl üres volt.\"\nRemux.SourceFile=\"OBS felvétel\"\nRemux.TargetFile=\"Célfájl\"\nRemux.Stop=\"Remux megállítása\"\nRemux.ClearFinished=\"Elkészült elemek törlése\"\nRemux.ClearAll=\"Összes elem törlése\"\nRemux.OBSRecording=\"OBS felvétel\"\nRemux.FinishedTitle=\"Remux kész\"\nRemux.Finished=\"Felvétel remuxolva\"\nRemux.FinishedError=\"Felvétel remuxolva, de a fájl hiányos lehet\"\nRemux.SelectRecording=\"OBS Felvétel kijelölése...\"\nRemux.SelectTarget=\"Célfájl kiválasztása...\"\nRemux.FileExistsTitle=\"A célfájl létezik\"\nRemux.FileExists=\"Az alábbi célfájlok már léteznek. Lecseréli őket?\"\nRemux.ExitUnfinishedTitle=\"Remuxolás folyamatban\"\nRemux.ExitUnfinished=\"A remuxolás nincs kész, a leállítása használhatatlanná teheti a célfájlt.\\nBiztos, hogy leállítja a remuxolást?\"\nRemux.HelpText=\"Dobja a fájlokat ebbe az ablakba a remuxoláshoz, vagy válasszon egy üres \\\"OBS felvétel\\\" cellát fájlok tallózásához.\"\nRemux.NoFilesAddedTitle=\"Nincs remuxolási fájl hozzáadva\"\nRemux.NoFilesAdded=\"Nincs fájl hozzáadva a remuxoláshoz. Ejtsen ide egy mappát, amely egy vagy több videófájlt tartalmaz.\"\nMissingFiles=\"Hiányzó fájlok\"\nMissingFiles.MissingFile=\"Hiányzó fájl\"\nMissingFiles.NewFile=\"Új fájl\"\nMissingFiles.HelpText=\"Néhány fájl hiányzik az OBS legutolsó használata óta.\"\nMissingFiles.NumFound=\"%1 / %2 találat\"\nMissingFiles.Search=\"Könyvtár keresése…\"\nMissingFiles.SelectFile=\"Fájl kiválasztása...\"\nMissingFiles.SelectDir=\"Mappa kiválasztása a kereséshez\"\nMissingFiles.State=\"Állapot\"\nMissingFiles.Missing=\"Hiányzik\"\nMissingFiles.Replaced=\"Cserélve\"\nMissingFiles.Cleared=\"Törölve\"\nMissingFiles.Found=\"Találat\"\nMissingFiles.AutoSearch=\"További fájlegyezések találhatóak\"\nMissingFiles.AutoSearchText=\"Az OBS további egyezéseket talált hiányzó fájlokhoz abban a könyvtárban. Hozzáadja őket?\"\nMissingFiles.NoMissing.Title=\"Hiányzó fájlok ellenőrzése\"\nMissingFiles.NoMissing.Text=\"Úgy tűnik, hogy egy fájl sem hiányzik.\"\nMacPermissions.MenuAction=\"Alkalmazás engedélyeinek áttekintése...\"\nMacPermissions.Title=\"Alkalmazás engedélyeinek áttekintése\"\nMacPermissions.Description=\"Az OBS Studio az engedélyét kéri, hogy bizonyos funkciókat szolgáltathasson. Ajánlott, hogy megadja ezeket az engedélyeket, de nem szükségesek az alkalmazás használatához. Ezeket később is bármikor megadhatja.\"\nMacPermissions.Description.OpenDialog=\"Újra megnyithatja ezt a párbeszédablakot az OBS Studio menüből.\"\nMacPermissions.AccessGranted=\"Hozzáférés megadva\"\nMacPermissions.RequestAccess=\"Hozzáférés kérése\"\nMacPermissions.OpenPreferences=\"A(z) %1 beállításainak megnyitása\"\nMacPermissions.Item.ScreenRecording=\"Képernyő felvétele\"\nMacPermissions.Item.ScreenRecording.Details=\"Az OBS-nek szüksége van erre az engedélyre, hogy felvegye a képernyőjét.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Ez az engedély azért szükséges, hogy a fel tudja venni a webkamerája vagy a rögzítőkártyája tartalmát.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"Az OBS-nek szüksége van erre az engedélyre, hogy rögzítsen a mikrofonjáról vagy egy külső eszközéről.\"\nMacPermissions.Item.InputMonitoring=\"Bemenetfigyelés\"\nMacPermissions.Item.InputMonitoring.Details=\"Ez az engedély szükséges, hogy az OBS gyorsbillentyűi a háttérben is működjenek.\"\nMacPermissions.Item.Accessibility=\"Akadálymentesítés\"\nMacPermissions.Item.Accessibility.Details=\"Régebbi telepítéseknél az OBS az „Akadálymentesítés” alatt lehet a „Bemenetfigyelés” helyett.\"\nMacPermissions.Continue=\"Folytatás\"\nSourceLeak.Title=\"Forrástisztítási hiba\"\nSourceLeak.Text=\"Probléma volt a jelenetgyűjtemények váltásakor, és egyes források eltávolítása nem sikerült. A problémát rendszerint olyan bővítmények okozzák, amelyek nem engedik el megfelelően az erőforrásokat. Győződjön meg róla, hogy minden bővítmény naprakész.\\n\\nAz OBS Studio most kilép, hogy megakadályozza az esetleges adatsérüléseket.\"\nBasic.DesktopDevice1=\"Asztali hang\"\nBasic.DesktopDevice2=\"2. asztali hang\"\nBasic.AuxDevice1=\"Mikrofon/Aux\"\nBasic.AuxDevice2=\"Mikrofon/Aux 2\"\nBasic.AuxDevice3=\"Mikrofon/Aux 3\"\nBasic.AuxDevice4=\"Mikrofon/Aux 4\"\nBasic.Scene=\"Jelenet\"\nBasic.DisplayCapture=\"Képernyőfelvétel\"\nBasic.Main.PreviewConextMenu.Enable=\"Előnézet bekapcsolása\"\nBasic.Main.Preview.Disable=\"Előnézet kikapcsolása\"\nScaleFiltering=\"Skála-szűrés\"\nScaleFiltering.Point=\"Pont\"\nScaleFiltering.Bilinear=\"Bilineáris\"\nScaleFiltering.Bicubic=\"Kettős köbös\"\nScaleFiltering.Area=\"Terület\"\nBlendingMethod=\"Keverési módszer\"\nBlendingMethod.Default=\"Alapértelmezett\"\nBlendingMethod.SrgbOff=\"SRGB Ki\"\nBlendingMode=\"Keverési mód\"\nBlendingMode.Normal=\"Normál\"\nBlendingMode.Additive=\"Hozzáadás\"\nBlendingMode.Subtract=\"Különbség\"\nBlendingMode.Screen=\"Szendvicsnegatív\"\nBlendingMode.Multiply=\"Szendvicspozitív\"\nBlendingMode.Lighten=\"Világosítás\"\nBlendingMode.Darken=\"Sötétítés\"\nDeinterlacing=\"Váltottsorosság\"\nDeinterlacing.Discard=\"Elvetés\"\nDeinterlacing.TopFieldFirst=\"Felső mező először\"\nDeinterlacing.BottomFieldFirst=\"Alsó mező először\"\nVolControl.SliderUnmuted=\"'%1' hangerő csúszkája:\"\nVolControl.SliderMuted=\"'%1' hangerő csúszkája: (jelenleg némítva)\"\nVolControl.Mute=\"Némítás '%1'\"\nVolControl.Properties=\"Tulajdonságok a '%1'-hez\"\nVolControl.UnassignedWarning.Title=\"Nem hozzárendelt hangforrás\"\nVolControl.UnassignedWarning.Text=\"A(z) „%1” nincs egyetlen hangsávhoz sem rendelve, és nem lesz hallható a közvetítésekben vagy felvételekben.\\n\\nHogy egy hangsávhoz rendeljen egy hangforrást, nyissa meg a Speciális hangbeállításokat a jobb gombos menüből, vagy a keverő dokkeszköztár fogaskerék ikonjának használatával.\"\nBasic.Main.AddSceneDlg.Title=\"Jelenet hozzáadása\"\nBasic.Main.AddSceneDlg.Text=\"Adja meg a jelenet nevét\"\nBasic.Main.DefaultSceneName.Text=\"Jelenet %1\"\nBasic.Main.AddSceneCollection.Title=\"Jelenet gyűjtemény hozzáadása\"\nBasic.Main.AddSceneCollection.Text=\"Kérjük, adja meg a jelenet gyűjtemény nevét\"\nBasic.Main.RenameSceneCollection.Title=\"Jelenet gyűjtemény átnevezése\"\nBasic.Main.RemigrateSceneCollection.Title=\"Jelenetgyűjtemény felbontásának frissítése\"\nBasic.Main.RemigrateSceneCollection.Text=\"Frissíti a jelenetgyűjtemény felbontását („%1”), hogy az megegyezzen a jelenlegi profil vásznának felbontásával: %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"A jelenetgyűjtemény felbontása addig nem frissíthető, amíg a kimenetek aktívak.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"A jelenetgyűjtemény felbontásának frissítése sikertelen. Az eredeti felbontás ismeretlen.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"A visszaállítás nem lehetséges: az OBS felbontásának módosítása sikertelen.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"A visszaállítás nem lehetséges: a jelenlegi felbontás megegyezik a jelenetgyűjtemény alap felbontásával.\"\nAddProfile.Title=\"Profil hozzáadása\"\nAddProfile.Text=\"Adja meg a profil nevét\"\nAddProfile.WizardCheckbox=\"Automatikus beállításvarázsló megjelenítése\"\nRenameProfile.Title=\"Profil átnevezése\"\nBasic.Main.MixerRename.Title=\"Hangforrás átnevezése\"\nBasic.Main.MixerRename.Text=\"Adja meg a hangforrás nevét\"\nBasic.Main.PreviewDisabled=\"Előnézet pillanatnyilag lekapcsolva\"\nBasic.SourceSelect=\"Forrás Létrehozása/Kijelölése\"\nBasic.SourceSelect.CreateNew=\"Új létrehozása\"\nBasic.SourceSelect.AddExisting=\"Létező tartalom hozzáadása\"\nBasic.SourceSelect.AddVisible=\"Forrás láthatóvá tétele\"\nBasic.Main.Sources.Visibility=\"Láthatóság\"\nBasic.Main.Sources.VisibilityDescription=\"Beállítja a(z) „%1” láthatóságát a vásznon\"\nBasic.Main.Sources.Lock=\"Zárolás\"\nBasic.Main.Sources.LockDescription=\"Zárolja '%1' pozícióját és méretét a vásznon\"\nBasic.PropertiesWindow=\"„%1” tulajdonságai\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (autokiválasztás: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Szín kiválasztása\"\nBasic.PropertiesWindow.SelectFont=\"Betűtípus kiválasztása\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Válasszon egy betűkészletet\"\nBasic.PropertiesWindow.ConfirmTitle=\"A beállítások megváltoztak\"\nBasic.PropertiesWindow.Confirm=\"A változtatások nincsenek elmentve. Megtartja őket?\"\nBasic.PropertiesWindow.NoProperties=\"Nincsenek elérhető tulajdonságok\"\nBasic.PropertiesWindow.AddFiles=\"Fájlok hozzáadása\"\nBasic.PropertiesWindow.AddDir=\"Könyvtár hozzáadása\"\nBasic.PropertiesWindow.AddURL=\"Elérési út/webcím hozzáadása\"\nBasic.PropertiesWindow.AddEditableListDir=\"Könyvtár hozzáadása ehhez: „%1”\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Fájlok hozzáadása ehhez: „%1”\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Bejegyzés hozzáadása ehhez: „%1”\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Bejegyzés szerkesztése: '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Egyszerű FPS értékek\"\nBasic.PropertiesView.FPS.Rational=\"Racionális FPS értékek\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Érvényes FPS tartományok:\"\nBasic.PropertiesView.UrlButton.Text=\"Az alapértelmezett böngészőben nyitja meg ezt a hivatkozást?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Webcím megnyitása\"\nBasic.InteractionWindow=\"Interakció ezzel: „%1”\"\nBasic.StatusBar.Reconnecting=\"Szétkapcsolt, újrakapcsolódás %2 másodperc múlva (%1. kísérlet)\"\nBasic.StatusBar.AttemptingReconnect=\"Újrakapcsolódási kísérlet... (%1. próbálkozás)\"\nBasic.StatusBar.ReconnectSuccessful=\"Sikeres újracsatlakozás\"\nBasic.StatusBar.Delay=\"Késleltetés (%1 mp)\"\nBasic.StatusBar.DelayStartingIn=\"Késleltetés (indul: %1 mp)\"\nBasic.StatusBar.DelayStoppingIn=\"Késleltetés (megáll: %1 mp)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Késleltetés (megáll: %1 mp; kezdés: %2 mp)\"\nBasic.StatusBar.RecordingSavedTo=\"Felvétel mentve ide: „%1”\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Visszajátszási puffer mentve ide: „%1”\"\nBasic.StatusBar.ScreenshotSavedTo=\"Képernyőkép elmentve ide: „%1”\"\nBasic.StatusBar.AutoRemuxedTo=\"Felvétel automatikusa remuxolva erre: „%1”\"\nBasic.Filters=\"Szűrők\"\nBasic.Filters.AsyncFilters=\"Audio/Videó szűrők\"\nBasic.Filters.AudioFilters=\"Hangszűrők\"\nBasic.Filters.EffectFilters=\"Effekt szűrők\"\nBasic.Filters.Title=\"Szűrők a \\\"%1\\\"\"\nBasic.Filters.AddFilter.Title=\"Szűrő neve\"\nBasic.Filters.AddFilter.Text=\"Kérem adja meg a szűrő nevét\"\nBasic.TransformWindow=\"Jelenet elemének alakítása\"\nBasic.TransformWindow.Position=\"Pozíció\"\nBasic.TransformWindow.PositionX=\"X pozíció\"\nBasic.TransformWindow.PositionY=\"Y pozíció\"\nBasic.TransformWindow.Rotation=\"Forgatás\"\nBasic.TransformWindow.Size=\"Méret\"\nBasic.TransformWindow.Width=\"Szélesség\"\nBasic.TransformWindow.Height=\"Magasság\"\nBasic.TransformWindow.Alignment=\"Tartalom pozícionálása\"\nBasic.TransformWindow.BoundsType=\"Méretezőkeret típusa\"\nBasic.TransformWindow.BoundsAlignment=\"Igazítás a méretezőkeretben\"\nBasic.TransformWindow.Bounds=\"Méretezőkeret mérete\"\nBasic.TransformWindow.BoundsWidth=\"Méretezőkeret szélessége\"\nBasic.TransformWindow.BoundsHeight=\"Méretezőkeret magassága\"\nBasic.TransformWindow.CropToBounds=\"Levágás a határoló téglalapra\"\nBasic.TransformWindow.Crop=\"Vágás\"\nBasic.TransformWindow.CropLeft=\"Levágás balra\"\nBasic.TransformWindow.CropRight=\"Levágás jobbra\"\nBasic.TransformWindow.CropTop=\"Levágás felül\"\nBasic.TransformWindow.CropBottom=\"Levágás alul\"\nBasic.TransformWindow.Alignment.TopLeft=\"Bal felül\"\nBasic.TransformWindow.Alignment.TopCenter=\"Középen felül\"\nBasic.TransformWindow.Alignment.TopRight=\"Jobb felül\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Középen balra\"\nBasic.TransformWindow.Alignment.Center=\"Középen\"\nBasic.TransformWindow.Alignment.CenterRight=\"Középen jobbra\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Bal alul\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Alul középen\"\nBasic.TransformWindow.Alignment.BottomRight=\"Jobb alul\"\nBasic.TransformWindow.BoundsType.None=\"Keret nélkül\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Csak a maximális méret\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Belső kerethez igazítás\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Külső kerethez igazítás\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Keret szélességéhez igazítás\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Keret magasságához igazítás\"\nBasic.TransformWindow.BoundsType.Stretch=\"Kerethez nyújtás\"\nBasic.TransformWindow.Title=\"A(z) „%1” átalakításának szerkesztése\"\nBasic.TransformWindow.NoSelectedSource=\"Nincs forrás kiválasztva\"\nBasic.Main.AddSourceHelp.Title=\"Forrás nem hozzáadható\"\nBasic.Main.AddSourceHelp.Text=\"Legalább 1 jelenetre van szükség forrás hozzáadásához.\"\nBasic.Main.Scenes=\"Jelenetek\"\nBasic.Main.Sources=\"Források\"\nBasic.Main.Source=\"Forrás\"\nBasic.Main.Controls=\"Vezérlés\"\nBasic.Main.PreparingStream=\"Előkészítés…\"\nBasic.Main.Connecting=\"Kapcsolódás...\"\nBasic.Main.StartRecording=\"Felvétel indítása\"\nBasic.Main.StartReplayBuffer=\"Visszajátszási puffer indítása\"\nBasic.Main.SaveReplay=\"Visszajátszás mentése\"\nBasic.Main.StartStreaming=\"Közvetítés indítása\"\nBasic.Main.StartBroadcast=\"Élő adás indítása\"\nBasic.Main.StartVirtualCam=\"Virtuális kamera indítása\"\nBasic.Main.StopRecording=\"Felvétel leállítása\"\nBasic.Main.PauseRecording=\"Felvétel szüneteltetése\"\nBasic.Main.UnpauseRecording=\"Felvétel folytatása\"\nBasic.Main.SplitFile=\"Felvétel fájljának felosztása\"\nBasic.Main.AddChapterMarker=\"Fejezetjel hozzáadása (csak hibrid MP4/MOV esetén)\"\nBasic.Main.StoppingRecording=\"Felvétel leállítása...\"\nBasic.Main.StopReplayBuffer=\"Visszajátszási puffer megállítása\"\nBasic.Main.StoppingReplayBuffer=\"Visszajátszási puffer megállítása…\"\nBasic.Main.SetupBroadcast=\"Adás kezelése\"\nBasic.Main.StopStreaming=\"Közvetítés leállítása\"\nBasic.Main.StopBroadcast=\"Közvetítés leállítása\"\nBasic.Main.AutoStopEnabled=\"(Automatikus leállítás)\"\nBasic.Main.StoppingStreaming=\"Közvetítés leállítása…\"\nBasic.Main.ForceStopStreaming=\"Közvetítés leállítása (késleltetés elvetése)\"\nBasic.Main.ShowContextBar=\"A Forrás Eszköztár Megjelenítése\"\nBasic.Main.HideContextBar=\"Forrás Eszköztár Elrejtése\"\nBasic.Main.StopVirtualCam=\"Virtuális kamera leállítása\"\nBasic.Main.Group=\"Csoport %1\"\nBasic.Main.GroupItems=\"Kijelölt elemek csoportosítása\"\nBasic.Main.Ungroup=\"Csoport megszüntetése\"\nBasic.Main.GridMode=\"Hálómód\"\nBasic.Main.ListMode=\"Lista mód\"\nBasic.Main.VirtualCamConfig=\"Virtuális kamera beállítása\"\nBasic.VCam.VirtualCamera=\"Virtuális kamera\"\nBasic.VCam.OutputType=\"Kimenet típusa\"\nBasic.VCam.OutputSelection=\"Kimenetválasztás\"\nBasic.VCam.OutputType.Program=\"Program (alapértelmezett)\"\nBasic.VCam.OutputSelection.NoSelection=\"Nincsenek lehetőségek ehhez a kimenettípushoz\"\nBasic.VCam.RestartWarning=\"A változtatás érvénybe lépéséhez újra kell indítani a virtuális kamerát\"\nBasic.MainMenu.File=\"&Fájl\"\nBasic.MainMenu.File.Export=\"&Exportálás\"\nBasic.MainMenu.File.Import=\"&Importálás\"\nBasic.MainMenu.File.ShowRecordings=\"&Felvételek megjelenítése\"\nBasic.MainMenu.File.Remux=\"Felvételek re&muxolása\"\nBasic.MainMenu.File.Settings=\"Beállítá&sok\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Beállítási mappa megjelenítése\"\nBasic.MainMenu.File.ShowProfileFolder=\"Profilmappa megjelenítése\"\nBasic.MainMenu.File.ShowMissingFiles=\"Hiányzó fájlok keresése\"\nBasic.MainMenu.File.Exit=\"&Kilépés\"\nBasic.MainMenu.Edit=\"Sz&erkesztés\"\nBasic.MainMenu.Edit.Undo=\"&Visszavonás\"\nBasic.MainMenu.Edit.Redo=\"&Mégis\"\nBasic.MainMenu.Edit.LockPreview=\"Előnézet &zárolása\"\nBasic.MainMenu.Edit.Scale=\"Előnézet &méretezése\"\nBasic.MainMenu.Edit.Scale.Window=\"Ablakhoz igazítás\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Vászon (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Kimenet (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Méretezett (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Nagyítás\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Kicsinyítés\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Nagyítás visszaállítása\"\nBasic.MainMenu.Edit.Transform=\"Á&talakítás\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Átalakítás sz&erkesztése…\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Átalakítás másolása\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Átalakítás beillesztése\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Átalakítás &visszaállítása\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Forgatás 90 fokkal balra\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Forgatás 90 fokkal jobbra\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Forgatás 180 fokkal\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"&Vízszintes tükrözés\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"&Függőleges tükrözés\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Képernyőhöz &igazítás\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&Nyújtás a kitöltéshez\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Képernyő &középpontjához\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Függőlegesen középre\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Vízszintesen középre\"\nBasic.MainMenu.Edit.Order=\"&Sorrend\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Mozgatás &fel\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Mozgatás &le\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Mozgatás a &tetejére\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Mozgatás az &aljára\"\nBasic.MainMenu.Edit.AdvAudio=\"Speciális h&angtulajdonságok\"\nBasic.MainMenu.View=\"&Nézet\"\nBasic.MainMenu.View.Toolbars=\"Eszköz&tárak\"\nBasic.MainMenu.View.ListboxToolbars=\"Dokk eszköztárak\"\nBasic.MainMenu.View.ContextBar=\"Forrás eszköztár\"\nBasic.MainMenu.View.SourceIcons=\"Forrás&ikonok\"\nBasic.MainMenu.View.StatusBar=\"Állapot&sor\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Teljes képernyős felület\"\nBasic.MainMenu.View.ResetUI=\"&Felület visszaállítása\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Mindig legfelül\"\nBasic.MainMenu.View.SceneListMode=\"Jelenetlista módja\"\nBasic.MainMenu.Docks=\"&Dokkok\"\nBasic.MainMenu.Docks.ResetDocks=\"&Dokkok visszaállítása\"\nBasic.MainMenu.Docks.LockDocks=\"&Dokkok zárolása\"\nBasic.MainMenu.Docks.SideDocks=\"&Teljes magasságú dokkok\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Egyéni böngésződokkok…\"\nBasic.MainMenu.Import=\"Importálás...\"\nBasic.MainMenu.Export=\"Exportálás...\"\nBasic.MainMenu.SceneCollection=\"&Jelenetgyűjtemény\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Profil importálása\"\nBasic.MainMenu.Profile.Export=\"Profil exportálása\"\nBasic.MainMenu.SceneCollection.Import=\"Jelenetgyűjtemény importálása\"\nBasic.MainMenu.SceneCollection.Export=\"Jelenetgyűjtemény exportálása\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Alap felbontás visszaállítása\"\nBasic.MainMenu.SceneCollection.Migrate=\"Alap felbontás beállítása\"\nBasic.MainMenu.Profile.Exists=\"A profil már létezik\"\nBasic.MainMenu.Tools=\"&Eszközök\"\nBasic.MainMenu.Help=\"&Súgó\"\nBasic.MainMenu.Help.HelpPortal=\"Súgó&portál\"\nBasic.MainMenu.Help.Website=\"&Weboldal megtekintése\"\nBasic.MainMenu.Help.Discord=\"Csatlakozás a &Discord-kiszolgálóhoz\"\nBasic.MainMenu.Help.WhatsNew=\"Újdonságok\"\nBasic.MainMenu.Help.Logs=\"Nap&lófájlok\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Naplófájlok megjeleníté&se\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"&Jelenlegi naplófájl feltöltése\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"&Előző naplófájl feltöltése\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Jelenlegi napló meg&tekintése\"\nBasic.MainMenu.Help.ReleaseNotes=\"Kiadási megjegyzések\"\nBasic.MainMenu.Help.CheckForUpdates=\"Frissítések ellenőrzése\"\nBasic.MainMenu.Help.Repair=\"Fájl épségének ellenőrzése\"\nBasic.MainMenu.Help.RestartSafeMode=\"Újraindítás csökkentett módban\"\nBasic.MainMenu.Help.RestartNormal=\"Újraindítás normál módban\"\nBasic.MainMenu.Help.CrashLogs=\"Összeomlás&jelentés\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Összeomlásjelentések &megjelenítése\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"&Következő naplófájl feltöltése\"\nBasic.MainMenu.Help.About=\"&Névjegy\"\nBasic.Settings.ProgramRestart=\"A beállítások érvénybe lépéséhez a program újraindítása szükséges.\"\nBasic.Settings.ConfirmTitle=\"Változtatások megerősítése\"\nBasic.Settings.Confirm=\"Nem mentette a módosításokat. Menti a változtatásokat?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"Egyes közvetítési beállításokat a(z) %1 %2 vezérel\"\nBasic.Settings.General=\"Általános\"\nBasic.Settings.General.Language=\"Nyelv\"\nBasic.Settings.General.Updater=\"Frissítések\"\nBasic.Settings.General.UpdateChannel=\"Frissítési csatorna\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Kikapcsolva)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Alapértelmezett)\"\nBasic.Settings.General.EnableAutoUpdates=\"Indításkor a frissítések automatikus ellenőrzése\"\nBasic.Settings.General.OpenStatsOnStartup=\"Statisztikai párbeszédpanel megnyitása indításkor\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"OBS ablakok elrejtése a képernyőfelvételről\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Ez a beállítás elrejti az összes nem vetítős OBS Studio ablakot az OBS általi rögzítés elől, és hatással lesz \\nmás alkalmazásokra is, például konferenciára, képernyőmegosztásra, távoli támogatásra, képernyőképekre és egyéb rögzítő szoftverekre.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"A beállítás engedélyezése elrejti az összes nem vetítős OBS Studio ablakot az OBS általi rögzítés elől, és hatással lesz más alkalmazásokra is, például konferenciára, képernyőmegosztásra, távoli támogatásra, képernyőképekre és egyéb rögzítő szoftverekre.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Megerősítő párbeszédpanel megjelenítése a közvetítés indításakor\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Megerősítő párbeszédpanel megjelenítése a közvetítés leállításakor\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Megerősítő párbeszédpanel megjelenítése a felvétel leállításakor\"\nBasic.Settings.General.Projectors=\"Vetítők\"\nBasic.Settings.General.HideProjectorCursor=\"Kurzor elrejtése a vetítők felett\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"A vetítők mindig legyenek legfelül\"\nBasic.Settings.General.Snapping=\"Forrás pozicionálásának igazítása\"\nBasic.Settings.General.ScreenSnapping=\"Források igazítása a képernyő széléhez\"\nBasic.Settings.General.CenterSnapping=\"Források vízszintes és függőleges középponthoz igazítása\"\nBasic.Settings.General.SourceSnapping=\"Források igazítása más forrásokhoz\"\nBasic.Settings.General.SnapDistance=\"Igazítás érzékenysége\"\nBasic.Settings.General.SpacingHelpers=\"Képpont-igazítási segédvonalak megjelenítése\"\nBasic.Settings.General.RecordWhenStreaming=\"Automatikus felvétel közvetítés esetén\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Felvétel folytatása a követítés leállása esetén\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Visszajátszási puffer automatikus indítása közvetítéskor\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Visszajátszási puffer aktívan tartása a közvetítés leállásakor\"\nBasic.Settings.General.SysTray=\"Értesítési terület\"\nBasic.Settings.General.SysTrayWhenStarted=\"Indításkor minimalizálás az értesítési területre\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Mindig az értesítési területre minimalizálás a tálca helyett\"\nBasic.Settings.General.SaveProjectors=\"Vetítők mentése kilépéskor\"\nBasic.Settings.General.CloseExistingProjectors=\"Képernyőnként egy teljes képernyős vetítőre korlátozás\"\nBasic.Settings.General.Preview=\"Előnézet\"\nBasic.Settings.General.OverflowHidden=\"Túlcsordulás elrejtése\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Túlcsordulás mindig látható\"\nBasic.Settings.General.OverflowSelectionHidden=\"Túlcsordulás megjelenítése, akkor is, ha a forrás láthatatlan\"\nBasic.Settings.General.Importers=\"Importálók\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Jelenetgyűjtemények keresése importáláskor az ismert elérési utakon\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Átmenet a jelenetre dupla kattintással\"\nBasic.Settings.General.StudioPortraitLayout=\"Portré/függőleges elrendezés engedélyezése\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Előnézet/programcímkék mutatása\"\nBasic.Settings.General.Multiview=\"Többszörös nézet\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Kattintás a jelenetek közötti váltáshoz\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Jelenetnevek megjelenítése\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Biztonságos területek kirajzolása (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Többszörös nézetes elrendezés\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Vízszintes, felső (8 jelenet)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Vízszintes, alsó (8 jelenet)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Függőleges, bal (8 jelenet)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Függőleges, jobb (8 jelenet)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Vízszintes, felső (18 jelenet)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Vízszintes, felső (24 jelenet)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Csak jelenetek (4 jelenet)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Csak jelenetek (9 jelenet)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Csak jelenetek (16 jelenet)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Csak jelenetek (25 jelenet)\"\nBasic.Settings.General.ChannelName.stable=\"Stabil\"\nBasic.Settings.General.ChannelDescription.stable=\"Legújabb stabil kiadás\"\nBasic.Settings.General.ChannelName.beta=\"Béták / kiadásra jelölt változatok\"\nBasic.Settings.General.ChannelDescription.beta=\"Potenciálisan instabil előzetes verziók\"\nBasic.Settings.Appearance=\"Megjelenés\"\nBasic.Settings.Appearance.General=\"Általános\"\nBasic.Settings.Appearance.General.Theme=\"Téma\"\nBasic.Settings.Appearance.General.Variant=\"Stílus\"\nBasic.Settings.Appearance.General.NoVariant=\"Nincs elérhető stílus\"\nBasic.Settings.Appearance.FontScale=\"Betűméret\"\nBasic.Settings.Appearance.Density=\"Sűrűség\"\nBasic.Settings.Appearance.Classic=\"Klasszikus\"\nBasic.Settings.Appearance.Compact=\"Kompakt\"\nBasic.Settings.Appearance.Normal=\"Normál\"\nBasic.Settings.Appearance.Comfortable=\"Kényelmes\"\nBasic.Settings.Appearance.OptionsWarning=\"Egyes megjelenési beállítások nem érhetőek el ehhez a stílushoz.\"\nBasic.Settings.Stream=\"Közvetítés\"\nBasic.Settings.Stream.Destination=\"Cél\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Hitelesítés használata\"\nBasic.Settings.Stream.Custom.Username=\"Felhasználónév\"\nBasic.Settings.Stream.Custom.Password=\"Jelszó\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: adja meg az „srp_username” felhasználónevet.\\nRTMP: adja meg a felhasználónevet.\\nSRT: nem használt.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: adja meg az „srp_password” jelszót.\\nRTMP: adja meg a jelszót.\\nSRT: adja meg a titkosítási jelmondatot.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Sávszélességteszt üzemmód engedélyezése\"\nBasic.Settings.Stream.TTVAddon=\"Twitch csevegési kiegészítők\"\nBasic.Settings.Stream.TTVAddon.None=\"Nincs\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV és FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Hiányzó közvetítési beállítások\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Beállítások megnyitása\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"A webcím és a közvetítési kulcs hiányzik.\\n\\nNyissa meg a beállításokat, hogy megadja a webcímet és a közvetítési kulcsot a „Közvetítés” lapon.\"\nBasic.Settings.Stream.MissingUrl=\"A közvetítés webcíme hiányzik.\\n\\nNyissa meg a beállításokat, hogy megadja az webcímet a „Közvetítés” lapon.\"\nBasic.Settings.Stream.MissingStreamKey=\"A közvetítési kulcs hiányzik.\\n\\nNyissa meg a beállításokat, hogy megadja a közvetítés kulcsot a „Közvetítés” lapon.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Közvetítési szolgáltatás ajánlott beállításainak figyelmen kívül hagyása\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Ajánlott beállítások felülbírálása\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Figyelem: A kiszolgáló korlátozásainak figyelmen kívül hagyása gyengébb minőséget eredményezhet, vagy megakadályozhatja a közvetítéstt.\\n\\nFolytatja?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Videó legnagyobb bitsebessége: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Hang legnagyobb bitsebessége: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Legnagyobb felbontás: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Legnagyobb FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Egyéni kiszolgáló megadása…\"\nBasic.Settings.Stream.ServiceCustomServer=\"Egyéni kiszolgáló\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1 bekapcsolása\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Legnagyobb közvetítési sávszélesség\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automatikus\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Legtöbb videósáv\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automatikus\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Közvetítés FLV-be írásának bekapcsolása (egyszerű rögzítési fájl beállításainak használata)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Beállítások felülbírálása (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Beállítások felülbírálásának bekapcsolása\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Többsávos videó\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"További vászon\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"A Simulcast lehetővé teszi több videóminőség egyidejű kódolását és küldését. <a href='https://obsproject.com/kb/whip-streaming-guide'>További tudnivalók</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Rétegek száma\"\nBasic.Settings.Stream.AdvancedOptions=\"Speciális beállítások\"\nBasic.Settings.Output=\"Kimenet\"\nBasic.Settings.Output.Format=\"Felvétel formátuma\"\nBasic.Settings.Output.Format.hMP4=\"Hibrid MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hibrid MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Töredékes MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Töredékes MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"A töredékes MOV darabokban írja ki a felvételt, és nem igényli a hagyományos MOV-fájlok esetén szükséges véglegesítést.\\nEz biztosítja azt, hogy a fájl akkor is lejátszható marad, ha a lemezre írás megszakad, például egy kékhalál vagy egy áramszünet miatt.\\n\\nEz nem biztos, hogy minden lejátszóval és szerkesztővel kompatibilis. Ha szükséges, akkor használja Fájl → Felvételek remuxolása lehetőséget, hogy kompatibilisebb formátumba alakítsa a fájlt.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"A töredékes MP4 darabokban írja ki a felvételt, és nem igényli a hagyományos MP4-fájlok esetén szükséges véglegesítést.\\nEz biztosítja azt, hogy a fájl akkor is lejátszható marad, ha a lemezre írás megszakad, például egy kékhalál vagy egy áramszünet miatt.\\n\\nEz nem biztos, hogy minden lejátszóval és szerkesztővel kompatibilis. Ha szükséges, akkor használja Fájl → Felvételek remuxolása lehetőséget, hogy kompatibilisebb formátumba alakítsa a fájlt.\"\nBasic.Settings.Output.Encoder.Video=\"Videó kódoló\"\nBasic.Settings.Output.Encoder.Audio=\"Hang kódoló\"\nBasic.Settings.Output.SelectDirectory=\"Felvételi könyvtár kiválasztása\"\nBasic.Settings.Output.DynamicBitrate=\"Dinamikus bitráta változtatás a hálózati terheléssel való megbirkózás érdekében\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dinamikus bitráta változtatás a hálózati terheléssel való megbirkózás érdekében (Béta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Hálózati terhelés esetén a képkockák eldobása helyett, dinamikusan változik a bitráta menet közben.\\n\\nTartsa észben, hogy ez növelheti a késleltetést ön és a nézők között, hogyha hirtelen megnövekszik a terhelés.\\nAmikor a bitráta lezuhan, akár percekig is eltarthat, amíg helyreáll.\\n\\nAktuálisan csak RTMP használata esetén támogatott.\"\nBasic.Settings.Output.Mode=\"Kimeneti mód\"\nBasic.Settings.Output.Mode.Simple=\"Egyszerű\"\nBasic.Settings.Output.Mode.Adv=\"Speciális\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg kimenet\"\nBasic.Settings.Output.UseReplayBuffer=\"Visszajátszási puffer engedélyezése\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Legrövidebb visszajátszási idő\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maximum memória\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Becsült memóriaigény: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Figyelem: A becsült memóriahasználat %1 MiB-tal nagyobb, mint a megengedett %2 MiB-os maximum\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Nem lehet megbecsülni a memóriaigényt. Állítson be egy felső memóriakorlátot.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Visszajátszási puffer fájlnévelőtagja\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Utótag\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"A visszajátszási puffer nem használható, ha a felvétel típusa „Egyéni kimenet (FFmpeg)”.\"\nBasic.Settings.Output.Simple.SavePath=\"Felvételek elérési útja\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Felvétel minősége\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"A közvetítésével megegyező\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Jó minőség, közepes fájlméret\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Megkülönböztethetetlen minőség, nagy fájlméret\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Veszteségmentes minőség, hatalmas fájlméret\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Figyelem: A közvetítés videójának bitsebessége a következőre lesz állítva: %1, mivel ennyi a kiválasztott közvetítési szolgáltatás felső korlátja.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Figyelem: A közvetítés hangjának bitsebessége a következőre lesz állítva: %1, mivel ennyi a kiválasztott közvetítési szolgáltatás felső korlátja.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Figyelmeztetés: A felvételeket nem lehet szüneteltetni, ha a felvétel minősége „A közvetítésével megegyező” értékre van állítva.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Figyelmeztetés: A jelenleg kiválasztott felvételi formátum nem kompatibilis a kiválasztott közvetítéshez használt kódolókkal.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Figyelem: A közvetítéstől eltérő minőséggel történő rögzítés további CPU erőforrásokat igényel, ha egyidejűleg használja a kettőt.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Figyelem: A veszteségmentes minőség hatalmas fájlméretet okoz. Ezzel a minőséggel, nagy felbontással és képkockasebességgel, percenként akár 7 gigabájt adatot is előállíthat. A veszteségmentes mód nem ajánlott hosszú felvételekhez, kivéve ha hatalmas lemezterület áll rendelkezésre. A visszajátszási puffer nem érhető el, ha veszteségmentes minőséget használ.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Biztos, hogy veszteségmentes minőséget használ?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Veszteségmentes minőség figyelem!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Szoftver (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Hardveres (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Hardveres (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Hardveres (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Hardveres (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Hardveres (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Hardveres (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Hardveres (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Hardveres (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Hardveres (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Hardveres (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Szoftver (x264 alacsony CPU használatú előbeállítás, növeli a fájlméretet)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (alapértelmezett)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD sáv (a 2. sávot használja)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Hangsáv\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Nem kompatibilis felbontás/képkockasebesség\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Ez a közvetítési szolgáltatás nem támogatja a jelenlegi kimeneti felbontást vagy képkockasebességet. A legközelebbi kompatibilis értékre lesz módosítva:\\n\\n%1\\n\\nFolytatja?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Felbontás: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Nem kompatibilis kódoló\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"A(z) „%1” közvetítési szolgáltatás nem támogatja a(z) „%2” kódolót. A kódoló a következőre lesz módosítva: „%3”.\\n\\nFolytatja?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"A(z) „%1” közvetítési szolgáltatás nem támogatja a(z) „%2” és a(z) „%3” kódolót. Ezek a kódolók a következőkre lesznek módosítva: „%4” és „%5”.\\n\\nFolytatja?\"\nBasic.Settings.Output.VideoBitrate=\"Videó bitsebessége\"\nBasic.Settings.Output.AudioBitrate=\"Hang bitsebessége\"\nBasic.Settings.Output.Reconnect=\"Automatikus újracsatlakozás\"\nBasic.Settings.Output.RetryDelay=\"Újrapróbálási késleltetés\"\nBasic.Settings.Output.MaxRetries=\"Újrapróbálkozások maximális száma\"\nBasic.Settings.Output.Advanced=\"Egyéni kódolási beállítások engedélyezése (speciális)\"\nBasic.Settings.Output.EncoderPreset=\"Kódoló előbeállítás\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (alacsony processzorhasználat, legalacsonyabb minőség)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (alapértelmezett) (közepes processzorhasználat, szokásos minőség)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (magas processzorhasználat, magas minőség)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Egyéni kódolási beállítások\"\nBasic.Settings.Output.CustomMuxerSettings=\"Egyéni muxer beállítások\"\nBasic.Settings.Output.NoSpaceFileName=\"Szóköz nélküli fájlnév előállítása\"\nBasic.Settings.Output.Adv.Rescale=\"Kimenet átméretezése\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Letiltva\"\nBasic.Settings.Output.Adv.AudioTrack=\"Hangsáv\"\nBasic.Settings.Output.Adv.Streaming=\"Közvetítés\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Közvetítési beállítások\"\nBasic.Settings.Output.Adv.Audio.Track1=\"1. sáv\"\nBasic.Settings.Output.Adv.Audio.Track2=\"2. sáv\"\nBasic.Settings.Output.Adv.Audio.Track3=\"3. sáv\"\nBasic.Settings.Output.Adv.Audio.Track4=\"4. sáv\"\nBasic.Settings.Output.Adv.Audio.Track5=\"5. sáv\"\nBasic.Settings.Output.Adv.Audio.Track6=\"6. sáv\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD sáv\"\nBasic.Settings.Output.Adv.Encoder=\"Kódoló beállításai\"\nBasic.Settings.Output.Adv.Recording=\"Felvétel\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Felvételi beállítások\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Felvétel típusa\"\nBasic.Settings.Output.Adv.Recording.Type=\"Típus\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Szabványos\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Egyéni kimenet (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Közvetítési kódoló használata)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Fájlnév formázás\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Létező fájl felülírása\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Az egyéni kimenet (FFmpeg) módhoz nem biztosítunk támogatást.\\nFontolja meg a „Szokásos” rögzítési mód használatát.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg kimenet típusa\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Kimenet URL-re\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Kimenet fájlba\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Általános felvételi formátumok\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Minden fájl\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Fájl elérési útja vagy URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Konténerformátum\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Hang\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Videó\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Alapértelmezett formátum\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Konténerformátum leírása\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Hang/videó kodek kitalálása a fájl elérési útjából vagy az URL-ből\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Alapértelmezett kódoló\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Kódoló letiltása\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Videó kódoló\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Videó kódoló beállítások (ha van)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Hang kódoló\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Hang kódoló beállítások (ha van)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxer beállítások (ha van)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Kulcsképkockák időköze (képkockák)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Minden kodek megjelenítése (akkor is, ha inkompatibilis lehet)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg beállítások\"\nBasic.Settings.Output.EnableSplitFile=\"Automatikus fájlfelosztás\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Felosztás idő alapján\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Felosztás méret alapján\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Csak kézi felosztás\"\nBasic.Settings.Output.SplitFile.Time=\"Felosztási idő\"\nBasic.Settings.Output.SplitFile.Size=\"Felosztási méret\"\nScreenshot=\"Képernyőkép a kimenetről\"\nScreenshot.SourceHotkey=\"Képernyőkép készítése a kiválasztott forrásról\"\nScreenshot.StudioProgram=\"Képernyőkép (program)\"\nScreenshot.Preview=\"Képernyőkép (előnézet)\"\nScreenshot.Scene=\"Képernyőkép (jelenet)\"\nScreenshot.Source=\"Képernyőkép (forrás)\"\nFilenameFormatting.TT.CCYY=\"Az év négy számjegye\"\nFilenameFormatting.TT.YY=\"Az év utolsó két számjegye (00-99)\"\nFilenameFormatting.TT.MM=\"A hónap decimális számként (01-12)\"\nFilenameFormatting.TT.DD=\"A hónap napja nullával kiegészítve (01-31)\"\nFilenameFormatting.TT.hh=\"Az óra 24 órás formátumban (00-23)\"\nFilenameFormatting.TT.mm=\"Perc (00-59)\"\nFilenameFormatting.TT.ss=\"Másodperc (00-59)\"\nFilenameFormatting.TT.Percent=\"A % jel\"\nFilenameFormatting.TT.a=\"A hét napjának rövidített neve\"\nFilenameFormatting.TT.A=\"A hét napjának teljes neve\"\nFilenameFormatting.TT.b=\"A hónap rövidített neve\"\nFilenameFormatting.TT.B=\"A hónap teljes neve\"\nFilenameFormatting.TT.d=\"A hónap napja nullával kiegészítve (01-31)\"\nFilenameFormatting.TT.H=\"Az óra 24 órás formátumban (00-23)\"\nFilenameFormatting.TT.I=\"Az óra 12 órás formátumban (01-12)\"\nFilenameFormatting.TT.m=\"A hónap decimális számként (01-12)\"\nFilenameFormatting.TT.M=\"Perc (00-59)\"\nFilenameFormatting.TT.p=\"A de. vagy du. jelölés\"\nFilenameFormatting.TT.s=\"A UNIX idő másodpercben\"\nFilenameFormatting.TT.S=\"Másodperc (00-59)\"\nFilenameFormatting.TT.y=\"Az év utolsó két számjegye (00-99)\"\nFilenameFormatting.TT.Y=\"Év\"\nFilenameFormatting.TT.z=\"ISO 8601 eltolás az UTC-hez képest az időzónában\"\nFilenameFormatting.TT.Z=\"Időzóna neve vagy rövidítése\"\nFilenameFormatting.TT.FPS=\"Képkocka / másodperc\"\nFilenameFormatting.TT.CRES=\"Alap (vászon) felbontás\"\nFilenameFormatting.TT.ORES=\"Kimeneti (skálázott) felbontás\"\nFilenameFormatting.TT.VF=\"Videóformátum\"\nBasic.Settings.Video=\"Videó\"\nBasic.Settings.Video.Adapter=\"Videó adapter\"\nBasic.Settings.Video.BaseResolution=\"Alap (vászon) felbontás\"\nBasic.Settings.Video.ScaledResolution=\"Kimeneti (skálázott) felbontás\"\nBasic.Settings.Video.DownscaleFilter=\"Leskálázási szűrő\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[A felbontások egyeznek, nincs szükség leskálázásra]\"\nBasic.Settings.Video.FPSCommon=\"Gyakori FPS értékek\"\nBasic.Settings.Video.FPSInteger=\"Egész FPS érték\"\nBasic.Settings.Video.FPSFraction=\"Tört FPS érték\"\nBasic.Settings.Video.Numerator=\"Számláló\"\nBasic.Settings.Video.Denominator=\"Nevező\"\nBasic.Settings.Video.Renderer=\"Megjelenítő\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (kísérleti)\"\nBasic.Settings.Video.InvalidResolution=\"Érvénytelen felbontás érték. [szélesség]x[magasság] formátumúnak kell lennie (például 1920 x 1080)\"\nBasic.Settings.Video.CurrentlyActive=\"A videokimenet jelenleg aktív. Kapcsoljon ki minden kimenetet a videobeállítások módosításához.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineáris (leggyorsabb, de méretezésnél elmosódott lehet)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Kettős köbös (élesített méretezés, 16 minta)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (élesített méretezés, 36 minta)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Terület (átlagolt, 4/6/9 minta)\"\nBasic.Settings.Audio=\"Hang\"\nBasic.Settings.Audio.SampleRate=\"Mintavételezés\"\nBasic.Settings.Audio.Channels=\"Csatornák\"\nBasic.Settings.Audio.Meters=\"Mérték\"\nBasic.Settings.Audio.MeterDecayRate=\"Halkulási ráta\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Gyors\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Közepes (I-es típusú PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lassú (II-es típusú PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Csúcsmérték típusa\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Csúcsminta\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Valós csúcs (magasabb CPU használat)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"Figyelmeztetés: a térhangzású hang engedélyezett.\"\nBasic.Settings.Audio.MultichannelWarning=\"Ha közvetít, ellenőrizze, hogy a szolgáltatója támogatja-e mind a térhangzású hang kezelését és lejátszását. Például a Facebook 360 Live teljes támogatást biztosít; a YouTube Live támogatja az 5.1-es hangfelvételeket (és a TV-n történő lejátszásukat).\\n\\nAz OBS hangszűrők kompatibilisek a térhangzású hanggal, viszont a VST bővítmények támogatása nem garantált.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Engedélyezi a térhangzást?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Biztos, hogy engedélyezi a térhangzást?\"\nBasic.Settings.Audio.Devices=\"Globális hangeszközök\"\nBasic.Settings.Audio.DesktopDevice=\"Asztali hang\"\nBasic.Settings.Audio.DesktopDevice2=\"2. asztali hang\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/aux hang\"\nBasic.Settings.Audio.AuxDevice2=\"2. mikrofon/aux hang\"\nBasic.Settings.Audio.AuxDevice3=\"3. mikrofon/aux hang\"\nBasic.Settings.Audio.AuxDevice4=\"4. mikrofon/aux hang\"\nBasic.Settings.Audio.EnablePushToMute=\"Gombnyomásra történő némítás engedélyezése\"\nBasic.Settings.Audio.PushToMuteDelay=\"Gombnyomásra történő némítás késleltetése\"\nBasic.Settings.Audio.EnablePushToTalk=\"Gombnyomásra történő beszéd engedélyezése\"\nBasic.Settings.Audio.PushToTalkDelay=\"Gombnyomásra történő beszéd késleltetése\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Az eszköz nincs csatlakoztatva vagy nem érhető el]\"\nBasic.Settings.Audio.Disabled=\"Letiltva\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Alacsony késleltetésű hangpufferelési mód (Decklink/NDI kimenetekhez)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"FIGYELMEZTETÉS: Az alacsony késleltetésű hangpufferelés be van kapcsolva.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Az alacsony késleltetésű hangpufferelés hanghibákat vagy akár az egyes források lejátszásának megállítását okozhatja.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Bekapcsolja az alacsony késleltetésű hangpufferelési módot?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Biztos, hogy bekapcsolja az alacsony késleltetésű hangpufferelési módot?\"\nBasic.Settings.Accessibility=\"Akadálymentesítés\"\nBasic.Settings.Accessibility.ColorOverrides=\"Eltérő színek használata\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Forrás kerete (kiválasztás)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Forrás kerete (levágás)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Forrás kerete (lebegő)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Keverő hangerősávja (-60-tól -20 dB-ig)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Keverő hangerősávja (-20-tól -9 dB-ig)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Keverő hangerősávja (-9-tól 0 dB-ig)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Keverő hangerősávja (-60-tól -20 dB-ig) (aktív)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Keverő hangerősávja (-20-tól -9 dB-ig) (aktív)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Keverő hangerősávja (-9-tól 0 dB-ig) (aktív)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Szín-előbeállítás\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Alapértelmezett\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Egyéni\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Színtévesztő változat\"\nBasic.Settings.Advanced=\"Speciális\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Aktív kimenetek figyelmeztetésének megjelenítése kilépéskor\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Folyamat prioritása\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Magas\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Normál feletti\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normál\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Normál alatti\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Tétlen\"\nBasic.Settings.Advanced.FormatWarning=\"Figyelem: Az NV12/P010-től eltérő színformátumok elsősorban felvételhez valók, és használatuk közvetítéshez nem ajánlott. A közvetítés megnövekedett processzorhasználatot okozhat a színformátum átalakítása miatt.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Figyelem: a magas precizitású formátumokat jellemzően HDR színterekkel együtt használják.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Figyelem: Rec. 2100 esetén használjon nagyobb pontosságú formátumot.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Színformátum\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 bites, 4:2:0, 2 sík)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 bites, 4:2:0, 3 sík)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 bites, 4:4:4, 3 sík)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 bites, 4:2:0, 2 sík)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 bites, 4:2:0, 3 sík)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 bites, 4:2:2, 2 sík)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 bites, 4:4:4, 2 sík)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 bites)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Színtér\"\nBasic.Settings.Advanced.Video.ColorRange=\"Színtartomány\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Korlátozott\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Teljes\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR fehérségszint\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR névleges csúcsszint\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Hangfigyelő eszköz\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Alapértelmezett\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Windows hangerőcsökkentés letiltása\"\nBasic.Settings.Advanced.StreamDelay=\"Közvetítés késleltetése\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Időtartam\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Töréspont megőrzése (késleltetés növelése) újrakapcsolódáskor\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Becsült memóriahasználat: %1 MB\"\nBasic.Settings.Advanced.Network=\"Hálózat\"\nBasic.Settings.Advanced.Network.Disabled=\"A jelenleg kiválasztott közvetítési protokoll nem támogatja a hálózati beállítások módosítását.\"\nBasic.Settings.Advanced.Network.BindToIP=\"IP-hez rendelés\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP-család\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Hálózati optimalizálás engedélyezése\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"TCP ütemezés engedélyezése\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Megpróbálja barátságosabbá tenni az RTMP kimenetet más késleltetésre érzékeny alkalmazások számára a hálózaton azáltal, hogy szabályozza az átvitel sebességét.\\nInstabil kapcsolatoknál ez növelheti az eldobott képkockát kockázatát.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Gyorsbillentyűk működése a fókusztól függően\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Sose tiltsa le a gyorsbillentyűket\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Gyorsbillentyűk letiltása, ha a fő ablak fókuszban van\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Gyorsbillentyűk letiltása, ha a fő ablak nincs fókuszban\"\nBasic.Settings.Advanced.AutoRemux=\"Automatikus remuxolás erre: %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(felvétel mkv-ként)\"\nBasic.AdvAudio=\"Speciális hangtulajdonságok\"\nBasic.AdvAudio.ActiveOnly=\"Csak aktív források\"\nBasic.AdvAudio.Name=\"Név\"\nBasic.AdvAudio.Volume=\"Hangerő\"\nBasic.AdvAudio.VolumeSource=\"„%1” hangereje\"\nBasic.AdvAudio.MonoSource=\"Mono lekeverés ehhez: „%1”\"\nBasic.AdvAudio.Balance=\"Egyensúly\"\nBasic.AdvAudio.BalanceSource=\"A(z) „%1” egyensúlya\"\nBasic.AdvAudio.SyncOffset=\"Szinkron eltolás\"\nBasic.AdvAudio.SyncOffsetSource=\"A(z) „%1” szinkron eltolása\"\nBasic.AdvAudio.Monitoring=\"Hangfigyelés\"\nBasic.AdvAudio.Monitoring.None=\"Figyelés kikapcsolása\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Csak figyelés (kimenet némítása)\"\nBasic.AdvAudio.Monitoring.Both=\"Figyelés és kimenet\"\nBasic.AdvAudio.MonitoringSource=\"A(z) „%1” hangfigyelése\"\nBasic.AdvAudio.AudioTracks=\"Sávok\"\nBasic.Settings.Hotkeys=\"Gyorsbillentyűk\"\nBasic.Settings.Hotkeys.Pair=\"A billentyűkombináció megegyezik ezzel: „%1”, ezért kapcsolóként működik\"\nBasic.Settings.Hotkeys.Filter=\"Szűrő\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Szűrés gyorsbillentyű alapján\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Ezen a gyorsbillentyűn több művelet is osztozik, kattintson az ütközések megjelenítéséhez\"\nBasic.Settings.Hotkeys.PleaseWait=\"Gyorsbillentyűk betöltése, kis türelmet...\"\nBasic.Hotkeys.SelectScene=\"Jelenethez kapcsolás\"\nBasic.SystemTray.Show=\"Megjelenítés\"\nBasic.SystemTray.Hide=\"Elrejtés\"\nBasic.SystemTray.Message.Reconnecting=\"Szétkapcsolva. Újrakapcsolódás...\"\nHotkeys.Print=\"Nyomtatás\"\nHotkeys.Pause=\"Szünet\"\nHotkeys.Left=\"Balra\"\nHotkeys.Right=\"Jobbra\"\nHotkeys.Up=\"Fel\"\nHotkeys.Down=\"Le\"\nHotkeys.Super=\"Szuper\"\nHotkeys.Menu=\"Menü\"\nHotkeys.Space=\"Szóköz\"\nHotkeys.NumpadMultiply=\"Numerikus szorzás\"\nHotkeys.NumpadDivide=\"Numerikus osztás\"\nHotkeys.NumpadAdd=\"Numerikus összeadás\"\nHotkeys.NumpadSubtract=\"Numerikus kivonás\"\nHotkeys.NumpadDecimal=\"Numerikus tizedesvessző\"\nHotkeys.AppleKeypadNum=\"%1 (billentyűzet)\"\nHotkeys.AppleKeypadMultiply=\"* (billentyűzet)\"\nHotkeys.AppleKeypadDivide=\"/ (billentyűzet)\"\nHotkeys.AppleKeypadAdd=\"+ (billentyűzet)\"\nHotkeys.AppleKeypadSubtract=\"- (billentyűzet)\"\nHotkeys.AppleKeypadDecimal=\". (billentyűzet)\"\nHotkeys.AppleKeypadEqual=\"= (billentyűzet)\"\nHotkeys.MouseButton=\"%1. egérgomb\"\nMute=\"Némítás\"\nUnmute=\"Némítás megszüntetése\"\nPush-to-mute=\"Gombnyomással történő némítás\"\nPush-to-talk=\"Gombnyomással történő beszéd\"\nSceneItemShow=\"„%1” megjelenítése\"\nSceneItemHide=\"„%1” elrejtése\"\nOutputWarnings.NoTracksSelected=\"Legalább egy sávot ki kell választania\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Kimeneti beállítási hiba\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Minden kimenetnél ki kell választani legalább egy hangsávot.\"\nOutputWarnings.MP4Recording=\"Figyelem: Az MP4/MOV-ban mentett felvételek javíthatatlanok, ha a fájl nem kerül lezárásra (például BSOD vagy áramkimaradás esetén). Ha mindenképpen több hangsávval kíván felvételt készíteni, akkor használjon MKV-t és remuxolja a felvételt MP4/MOV-ba, miután elkészült. (Fájl -> Felvételek remuxolása)\"\nOutputWarnings.CannotPause=\"Figyelmeztetés: A felvételeket nem lehet szüneteltetni, ha a felvétel kódolója a „(Közvetítés kódolójának használata)” lehetőségre van állítva.\"\nOutputWarnings.CodecIncompatible=\"A hang- és videókódoló kiválasztása inkompatibilitás miatt alaphelyzetbe állt. Válasszon egy kompatibilis kódolót a listából.\"\nCodecCompat.Incompatible=\"(Nem kompatibilis a következővel: %1)\"\nCodecCompat.CodecPlaceholder=\"Kódoló kiválasztása...\"\nCodecCompat.ContainerPlaceholder=\"Formátum kiválasztása...\"\nCodecCompat.CodecMissingOnExit.Title=\"Nincs kiválasztva kódoló\"\nCodecCompat.CodecMissingOnExit.Text=\"Legalább egy videó- vagy hangkódoló nincs beállítva. Győződjön meg róla, hogy választott kódolót mind a felvételhez, mind a közvetítéshez.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Nincs kiválasztva formátum\"\nCodecCompat.ContainerMissingOnExit.Text=\"Nincs felvételi formátum kiválaszva. Válasszon egy olyan felvételi formátumot, amely kompatibilis a kiválasztott közvetítési kódolóval.\"\nFinalScene.Title=\"Jelenet törlése\"\nFinalScene.Text=\"Legalább egy jelenetnek lennie kell.\"\nNoSources.Title=\"Nincsenek források\"\nNoSources.Text=\"Úgy fest, hogy nem adott hozzá semmilyen videóforrást, így fekete képet fog adni. Biztos, hogy ezt szeretné?\"\nNoSources.Text.AddSource=\"Hozzáadhat forrásokat bármikor a + ikonra kattintva a Források doboz alatt a fő ablakban.\"\nNoSources.Label=\"Nincsenek forrásai.\\nKattintson alul a + gombra,\\nvagy kattintson ide jobb egérgombbal,\\nhogy hozzáadjon egyet.\"\nChangeBG=\"Szín megadása\"\nCustomColor=\"Egyéni szín\"\nBrowserSource.EnableHardwareAcceleration=\"Böngészőforrás hardveres támogatásának engedélyezése\"\nAbout=\"Névjegy\"\nAbout.Info=\"Az OBS Studio egy ingyenes és nyílt forráskódú videofelvevő és élő közvetítési szoftver.\"\nAbout.Donate=\"Hozzájárulás\"\nAbout.GetInvolved=\"Részvétel\"\nAbout.Authors=\"Készítők\"\nAbout.License=\"Licenc\"\nAbout.Error=\"Hiba! A fájl nem olvasható.\\n\\nUgrás ide: %1\"\nAbout.Contribute=\"Támogassa az OBS Projektet\"\nAddUrl.Title=\"Forrás hozzáadása URL-en keresztül\"\nAddUrl.Text=\"Behúzott egy URL-t az OBS-be. Ez automatikusan forrásként adja hozzá a hivatkozást. Folytatja?\"\nResizeOutputSizeOfSource=\"Kimenet átméretezése (forrás mérete)\"\nResizeOutputSizeOfSource.Text=\"Az alap és a kimeneti felbontás átméretezése a jelenlegi forrás méretére.\"\nResizeOutputSizeOfSource.Continue=\"Folytatja?\"\nPreviewTransition=\"Átmenet előnézete\"\nImporter=\"Jelenetgyűjtemény importáló\"\nImporter.SelectCollection=\"Jelenetgyűjtemény kiválasztása\"\nImporter.Collection=\"Jelenetgyűjtemény\"\nImporter.HelpText=\"Adjon hozzá fájlokat az ablakhoz, hogy gyűjteményeket importáljon az OBS-ből vagy hasonló támogatott programokból.\"\nImporter.Path=\"Gyűjtemény elérési útja\"\nImporter.Program=\"Észlelt alkalmazás\"\nImporter.AutomaticCollectionPrompt=\"Jelenetgyűjtemények automatikus keresése\"\nImporter.AutomaticCollectionText=\"Az OBS képes arra, hogy automatikusan importáható jelenetgyűjteményeket keressen a támogatott harmadik felektől származó programokból. Szeretné, hogy az OBS automatikusan megkeresse a gyűjteményeket?\\n\\nEzt később a Beállítások > Általános > Importálók menüpont alatt változtathatja meg.\"\nImporter.SelectFile=\"Tallózás...\"\nRestart=\"Újraindítás\"\nNeedsRestart=\"Az OBS Studio újraindítása szükséges. Szeretné újraindítani most?\"\nLoadProfileNeedsRestart=\"A profil olyan beállításokat tartalmaz, melyhez az OBS újraindítása szükséges:\\n%1\\n\\nBiztos, hogy újraindítja az OBS-t a változások érvénybe lépéséhez?\"\nContextBar.NoSelectedSource=\"Nincs forrás kiválasztva\"\nContextBar.MediaControls.PlayMedia=\"Média lejátszása\"\nContextBar.MediaControls.PauseMedia=\"Média szüneteltetése\"\nContextBar.MediaControls.StopMedia=\"Média leállítása\"\nContextBar.MediaControls.RestartMedia=\"Média újraindítása\"\nContextBar.MediaControls.PlaylistNext=\"Következő a listán\"\nContextBar.MediaControls.PlaylistPrevious=\"Előző a listán\"\nContextBar.MediaControls.BlindSeek=\"Média tekerési ablakelem\"\nYouTube.Auth.Ok=\"Az engedélyezés sikeresen befejeződött.\\nMost már bezárhatja ezt az oldalt.\"\nYouTube.Auth.NoCode=\"Az engedélyezési folyamat nem fejeződött be.\"\nYouTube.Auth.NoChannels=\"Nem érhető el csatorna a kiválasztott fiókban\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube felhasználóhitelesítés\"\nYouTube.Auth.WaitingAuth.Text=\"Fejezze be az engedélyezést a külső böngészőben.<br>Ha a külső böngésző nem nyílik meg, akkor kövesse ezt a hivatkozást és fejezze be az engedélyezést:<br>%1\"\nYouTube.AuthError.Text=\"A csatornainformációk lekérése sikertelen: %1.\"\nYouTube.Actions.WindowTitle=\"YouTube adás beállítása – Csatorna: %1\"\nYouTube.Actions.CreateNewEvent=\"Új adás létrehozása\"\nYouTube.Actions.ChooseEvent=\"Meglévő adás kiválasztása\"\nYouTube.Actions.Title=\"Cím*\"\nYouTube.Actions.MyBroadcast=\"Saját közvetítés\"\nYouTube.Actions.Description=\"Leírás\"\nYouTube.Actions.Privacy=\"Láthatóság*\"\nYouTube.Actions.Privacy.Private=\"Privát\"\nYouTube.Actions.Privacy.Public=\"Nyilvános\"\nYouTube.Actions.Privacy.Unlisted=\"Nem listázott\"\nYouTube.Actions.Category=\"Kategória\"\nYouTube.Actions.Thumbnail=\"Bélyegkép\"\nYouTube.Actions.Thumbnail.SelectFile=\"Fájl kiválasztása…\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Nincs fájl kiválasztva\"\nYouTube.Actions.Thumbnail.ClearFile=\"Törlés\"\nYouTube.Actions.MadeForKids=\"Gyermekeknek készült ez a videó?*\"\nYouTube.Actions.MadeForKids.Yes=\"Igen, gyermekeknek készült\"\nYouTube.Actions.MadeForKids.No=\"Nem, nem gyermekeknek készült\"\nYouTube.Actions.AdditionalSettings=\"További beállítások\"\nYouTube.Actions.Latency=\"Késleltetés\"\nYouTube.Actions.Latency.Normal=\"Normál\"\nYouTube.Actions.Latency.Low=\"Alacsony\"\nYouTube.Actions.Latency.UltraLow=\"Ultra alacsony\"\nYouTube.Actions.EnableAutoStart=\"Automatikus indítás engedélyezése\"\nYouTube.Actions.EnableAutoStop=\"Automatikus leállítás engedélyezése\"\nYouTube.Actions.AutoStartStop.TT=\"Azt jelzi, hogy az ütemezett közvetítés automatikusan elindul-e\"\nYouTube.Actions.EnableDVR=\"DVR bekapcsolása\"\nYouTube.Actions.360Video=\"360°-os videó\"\nYouTube.Actions.ScheduleForLater=\"Beütemezés későbbre\"\nYouTube.Actions.RememberSettings=\"Ezen beállítások megjegyzése\"\nYouTube.Actions.Create_Ready=\"Adás létrehozása\"\nYouTube.Actions.Create_GoLive=\"Adás létrehozása és közvetítés indítása\"\nYouTube.Actions.Choose_Ready=\"Adás kiválasztása\"\nYouTube.Actions.Choose_GoLive=\"Adás választása és közvetítés indítása\"\nYouTube.Actions.Create_Schedule=\"Adás ütemezése\"\nYouTube.Actions.Create_Schedule_Ready=\"Ütemezés és adás kiválasztása\"\nYouTube.Actions.Dashboard=\"YouTube Studio megnyitása\"\nYouTube.Actions.Error.Title=\"Hiba az elő adás létrehozásakor\"\nYouTube.Actions.Error.Text=\"YouTube hozzáférési hiba: „%1”.<br/>A részletes hibaleírás megtalálható a <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> oldalon.\"\nYouTube.Actions.Error.General=\"YouTube hozzáférési hiba. Ellenőrizze a hálózati kapcsolatát, vagy a hozzáférését a YouTube kiszolgálóhoz.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Közvetítés létrehozási hiba: „%1”.<br/>A részletes hibaleírás megtalálható a <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> oldalon.\"\nYouTube.Actions.Error.NoStreamCreated=\"Nem lett közvetítés létrehozva. Kapcsolja össze újra a fiókját.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API hiba. További információkért nézze meg a naplófájlt.\"\nYouTube.Actions.Error.BroadcastNotFound=\"A kiválasztott adás nem található.\"\nYouTube.Actions.Error.FileMissing=\"A kiválasztott fájl nem létezik.\"\nYouTube.Actions.Error.FileOpeningFailed=\"A kiválasztott fájl megnyitása sikertelen.\"\nYouTube.Actions.Error.FileTooLarge=\"A kiválasztott fájl túl nagy (korlát: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Az adás átmenete sikertelen: %1<br/><br/>Ha a hiba továbbra is fennáll, <a href='https://studio.youtube.com/video/%2/livestreaming'>nyissa meg az adást a YouTube Studióban</a>, és próbálja meg kézileg.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Az adás átvált a tesztfázisra, ez eltarthat egy ideig. Próbálja ki 10-30 másodperc múlva.\"\nYouTube.Actions.EventsLoading=\"Események betöltése…\"\nYouTube.Actions.EventCreated.Title=\"Esemény létrehozva\"\nYouTube.Actions.EventCreated.Text=\"Esemény sikeresen létrehozva.\"\nYouTube.Actions.Stream=\"Közvetítés\"\nYouTube.Actions.Stream.ScheduledFor=\"Ekkorra ütemezve: %1\"\nYouTube.Actions.Stream.Resume=\"Megszakított közvetítés folytatása\"\nYouTube.Actions.Stream.YTStudio=\"A YouTube Studio által automatikusan létrehozva\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Új élő közvetítés létrehozása, kis türelmet…\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Kézi indítás szükséges\"\nYouTube.Actions.AutoStartStreamingWarning=\"Az automatikus indítás ki van kapcsolva ennél az eseménynél, a közvetítés indításához kattintson az „Élő adás indítása” gombra.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Nem fog tudni újrakapcsolódni.<br>A közvetítés véget ér, és többé nem lesz élő adásban.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Az elő közvetítés nem engedélyezett a kiválasztott YouTube csatornán.<br/><br/>További információkért lásd a <a href='https://www.youtube.com/features'>youtube.com/features</a> oldalt.\"\nYouTube.Errors.livePermissionBlocked=\"Az elő közvetítés nem engedélyezett a kiválasztott YouTube csatornán.<br/>Vegye figyelembe, hogy akár 24 óráig is eltarthat, amíg a közvetítés elérhetővé válik, miután bekapcsolta a csatorna beállításaiban.<br/><br/>További információkért lásd a <a href='https://www.youtube.com/features'>youtube.com/features</a> oldalt.\"\nYouTube.Errors.errorExecutingTransition=\"Az átmenet a háttérszolgáltatás hibája miatt sikertelen. Próbálja újra pár másodperc múlva.\"\nYouTube.Errors.errorStreamInactive=\"A YouTube nem fogad adatot a közvetítésétől. Ellenőrizze a konfigurációt, és próbálja újra.\"\nYouTube.Errors.invalidTransition=\"A próbált átmenet érvénytelen volt. Ez amiatt lehet, hogy a közvetítés nem fejezte be az előző átmenetet. Várjon néhány másodpercet, és próbálja újra.\"\nYouTube.DocksRemoval.Title=\"Örökölt YouTube böngésződokkok törlése\"\nYouTube.DocksRemoval.Text=\"Ezek a böngésződokkok el lesznek távolítva, mert elavultak:\\n\\n%1\\nHelyette használja a „Dokkok/YouTube Élő irányítóközpont” lehetőséget.\"\nConfigDownload.WarningMessageTitle=\"Figyelmeztetés\"\nFailedToStartStream.MissingConfigURL=\"Nem érhető el beállítási webcím a jelenlegi szolgáltatáshoz\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Nincs megadva egyéni RTMP webcím\"\nFailedToStartStream.InvalidCustomConfig=\"Érvénytelen egyéni beállítások\"\nFailedToStartStream.MissingCanvas=\"A beállított további vászon hiányzik\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"A többsávos videószolgáltatás létrehozása sikertelen\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"A többsávos RTMP videókimenet létrehozása sikertelen\"\nFailedToStartStream.EncoderNotAvailable=\"Az NVENC nem érhető el.\\n\\nA(z) „%1” típusú kódoló nem található.\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"A(z) „%1” (típus: „%2”) kódoló létrehozása sikertelen\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Az OBS videóinformációk lekérése sikertelen a(z) „%1” kódoló (típus: „%2”) létrehozása során\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"A hangkódoló létrehozása sikertelen\"\nFailedToStartStream.NoRTMPURLInConfig=\"A beállítás nem tartalmazza az RTMP(S) célwebcímet\"\nFailedToStartStream.FallbackToDefault=\"A közvetítés indítása a(z) %1 használatával sikertelen; megpróbálja újra egy kódolási beállítás használatával?\"\nFailedToStartStream.ConfigRequestFailed=\"A beállítások nem kérhetőek le innen: %1<br><br>HTTP hiba: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Ismeretlen állapotérték kapva: „%1”\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nFolytatja a közvetítést enélkül: %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nBiztos, hogy folytatja a közvetítést?\"\nFailedToStartStream.MissingEncoderConfigs=\"Az élő adás beállítása nem tartalmazta a kódolókonfigurációkat\"\nFailedToStartStream.InvalidEncoderConfig=\"Az élő adás beállítása érvénytelen kódolókonfigurációt tartalmazott\"\nFailedToStartStream.StatusMissingHTML=\"Az élő adási kérés nem meghatározott hibával tért vissza\"\nFailedToStartStream.NoConfigSupplied=\"Hiányzó konfiguráció\"\nMultitrackVideo.Info=\"Az %1 automatikusan optimalizálja a beállításait, hogy többféle minőségű videót kódoljon és küldjön. A beallítás választása információkat küld a(z) %2 számára a számítógépéről és szoftverbeállításairól.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Nem kompatibilis beállítások\"\nMultitrackVideo.IncompatibleSettings.Text=\"Az %1 jelenleg nem kompatibilis ezzel:\\n\\n%2\\nHogy folytassa a közvetítést az %1 használatával, kapcsolja ki a nem kompatibilis beállításokat:\\n\\n%3\\nés kezdje el újra az adást.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Kikapcsolás ennél az adásnál, és a közvetítés indítása\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Beállítások frissítése és a közvetítés indítása\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"A(z) %1 jelenleg nem kompatibilis a [Hang → Általános → Csatornák] ezen értékével: „%2”, %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"A [Hang → Általános → Csatornák] értékének ennek kell lennie: „%1”\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"A(z) %1 több különböző beállítást igényel ennél: [Hang → Általános → Csatornák]\"\nBasic.OpenPluginManager=\"Bővítménykezelő\"\nPluginManager=\"Bővítménykezelő\"\nPluginManager.Restart=\"A bővítményváltozások alkalmazásához újra kell indítani az OBS Studiót\"\nPluginManager.MissingPlugin=\"[A BŐVÍTMÉNY NEM TALÁLHATÓ]\"\nPluginManager.Section.Discover=\"Tallózás\"\nPluginManager.Section.Manage=\"Telepítve\"\nPluginManager.Section.Updates=\"Frissítések\"\nPluginManager.Section.Manage.Title=\"Engedélyezett bővítmények kezelése\"\n"
  },
  {
    "path": "frontend/data/locale/hy-AM.ini",
    "content": "Language=\"Հայերեն\"\nOK=\"Լավ\"\nApply=\"Կիրառել\"\nCancel=\"Չեղարկել\"\nClose=\"Փակել\"\nSave=\"Պահպանել\"\nDiscard=\"Հրաժարվել\"\nDisable=\"Անջատել\"\nYes=\"Այո\"\nNo=\"Ոչ\"\nAdd=\"Ավելացնել\"\nRemove=\"Ջնջել\"\nRename=\"Վերանվանել\"\nInteract=\"Փոխազդել\"\nFilters=\"Զտիչներ\"\nProperties=\"Հատկություններ\"\nMoveUp=\"Բարձրացնել վերև\"\nMoveDown=\"Իջեցնել\"\nSettings=\"Կարգավորումներ\"\nDisplay=\"Ցուցադրել\"\nName=\"Անուն\"\nExit=\"Ելք\"\nMixer=\"Աուդիո խառնիչ\"\nBrowse=\"Թերթել\"\nMono=\"Մոնո\"\nStereo=\"Ստերեո\"\nDroppedFrames=\"Ընկած շրջանակներ %1 (%2%)\"\nClear=\"Մաքրել\"\nRevert=\"Հետադարձել\"\nShow=\"Ցուցադրել\"\nHide=\"Թաքցնել\"\nUnhideAll=\"Թաքցնել բոլորը\"\nUntitled=\"Անվերնագիր\"\nNew=\"Նոր\"\nDuplicate=\"Կրկնօրինակել\"\nEnable=\"Միացնել\"\nDisableOSXVSync=\"Անջատել macOS V-Sync\"\nResetOSXVSyncOnExit=\"Վերականգնել macOS V-Sync-ը ելքից հետո\"\nHighResourceUsage=\"Կոդավորումը գերբեռնված է: Մտածեք մերժել տեսանյութի կարգավորումները կամ օգտագործել ավելի արագ կոդավորման նախադրյալ:\"\nTransition=\"Անցում\"\nQuickTransitions=\"Արագ անցումներ\"\nFadeToBlack=\"Սևանալ\"\nLeft=\"Ձախ\"\nRight=\"Աջ\"\nTop=\"Վերև\"\nBottom=\"Ներքև\"\nReset=\"Զրոյացնել\"\nHours=\"Ժամ\"\nMinutes=\"Րոպե\"\nSeconds=\"Վարկյան\"\nDeprecated=\"Հնացած\"\nReplayBuffer=\"Կրկնել բուֆերը\"\nImport=\"Ներմուծել\"\nExport=\"Արտահանել\"\nCopy=\"Պատճենել\"\nPaste=\"Տեղադրել\"\nPasteReference=\"Տեղադրել (հղում)\"\nPasteDuplicate=\"Տեղադրել (Կրկնօրինակ)\"\nRemuxRecordings=\"Ռեմուքսի տեսագրություններ\"\nNext=\"Հաջորդը\"\nBack=\"Ետ\"\nDefaults=\"Կանխադրվածներ\"\nRestoreDefaults=\"Կանխադրվածներ\"\nHideMixer=\"Թաքցնել Միքսերում\"\nTransitionOverride=\"Անցումային անտեսում\"\nShowTransition=\"Ցույց տալ անցումը\"\nHideTransition=\"Թաքցնել անցումը\"\nNone=\"Ոչ մի\"\nStudioMode.Preview=\"Նախադիտում\"\nStudioMode.Program=\"Ծրագիր\"\nStudioMode.PreviewSceneName=\"Նախադիտում: %1\"\nStudioMode.ProgramSceneName=\"Ծրագիր: %1\"\nShowInMultiview=\"Ցույց տալ բազմակի դիտում\"\nVerticalLayout=\"Ուղղահայաց դասավորություն\"\nGroup=\"Խումբ\"\nDoNotShowAgain=\"Այլևս չցուցադրել\"\nDefault=\"(Կանխադրված)\"\nCalculating=\"Հաշվարկվում է...\"\nFullscreen=\"Ամբողջ էկրանով\"\nWindowed=\"Պատուհանով\"\nRefreshBrowser=\"Թարմացնել\"\nAspectRatio=\"Ասպեկտների հարաբերակցություն <b>%1:%2</b>\"\nLockVolume=\"Կողպեքի ձայնը\"\nLogViewer=\"Մատյանների դիտիչ\"\nShowOnStartup=\"Ցույց տալ գործարկման ժամանակ\"\nOpenFile=\"Բացել ֆայլը\"\nAddSource=\"Ավելացնել Աղբյուր\"\nRemoveScene=\"Ջնջել ընտրված տեսարանը\"\nRemoveSource=\"Ջնջել ընտրված աղբյուր(ներ)ը\"\nMoveSceneUp=\"Տեղափոխել տեսարանը վերև\"\nMoveSceneDown=\"Տեղափոխել տեսարանը ներքև\"\nMoveSourceUp=\"Տեղաշարժել Աղբյուր(ները) Վերև\"\nMoveSourceDown=\"Տեղաշարժել Աղբյուր(ները) Ներքև\"\nSourceProperties=\"Բացել աղբյուրի հատկությունները\"\nSourceFilters=\"Բացել աղբյուրի զտիչները\"\nMixerToolbarMenu=\"Ձայնային խառնիչի ընտրացանկ\"\nSceneFilters=\"Բացել աղբյուրի զտիչները\"\nList=\"Ցանկ\"\nGrid=\"Ցանց\"\nAutomatic=\"Ինքնաշխատ\"\nPluginsFailedToLoad.Title=\"Միացնիչի բեռնման սխալ\"\nPluginsFailedToLoad.Text=\"Հետևյալ OBS հավելումները չհաջողվեց բեռնել։\\n\\n%1\\nԽնդրում ենք թարմացնել կամ հեռացնել այս միացնիչները:\"\nAlreadyRunning.Title=\"OBS-ն արդեն աշխատում է\"\nAlreadyRunning.Text=\"OBS-ն արդեն աշխատում է: Եթե ​​դուք չեք ցանկանում դա անել, խնդրում ենք փակել OBS-ի բոլոր գոյություն ունեցող օրինակները՝ նախքան նոր օրինակ գործարկելը: Եթե ​​դուք ունեք OBS համակարգային սկուտեղի նվազագույնի հասցնելու համար, խնդրում ենք ստուգել՝ արդյոք այն դեռ աշխատում է այնտեղ:\"\nAlreadyRunning.LaunchAnyway=\"Գործարկել ամեն դեպքում\"\nChromeOS.Title=\"Չաջակցվող հարթակ\"\nChromeOS.Text=\"Կարծես թե OBS-ն գործակում է Chrome OS կոնտեյների ներսում: Այս հարթակը չի աջակցվում.\"\nWine.Title=\"Հայտնաբերվել է գինի\"\nWine.Text=\"Wine-ում OBS-ի գործարկումը չի աջակցվում, և շատ գործառույթներ, ինչպիսիք են սարքերի հավաքագրումը կամ աղբյուրը գտնելը, չեն աշխատի կամ կաշխատի միայն սահմանափակ ձևով:<br><br>Խորհուրդ է տրվում գործարկել OBS-ի ձեր սեփական տարբերակը, օրինակ՝ <a href='https://flathub.org/apps/details/com.obsproject.Studio'>մեր Flatpak տարբերակը</a> կամ օգտագործեք ձեր օպերացիոն համակարգի փաթեթները:\"\nDockCloseWarning.Title=\"Փակել միացվող պատուհանը\"\nDockCloseWarning.Text=\"Դուք հենց նոր փակեցիք միացվող պատուհանը: Եթե ​​ցանկանում եք նորից ցուցադրել այն, օգտագործեք Դոկ վահանակի ընտրացանկը ցանկի տողում:\"\nExtraBrowsers=\"Հատուկ զննարկիչի դոկերը\"\nExtraBrowsers.Info=\"Ավելացրել դոկեր՝ նշելով անուն կամ հղում, այնուհետև սեղմեք Ընդունել կամ Փակել՝ նավամատույցները բացելու համար: Դուք կարող եք ավելացնել կամ հեռացնել նավահանգիստները, երբ ցանկանում եք:\"\nExtraBrowsers.DockName=\"Դոկի անվանումը\"\nAuth.Authing.Title=\"Մուտք գործել...\"\nAuth.Authing.Text=\"Մուտք գործեք %1, խնդրում ենք սպասել...\"\nAuth.AuthFailure.Title=\"Վավերացման սխալ\"\nAuth.AuthFailure.Text=\"Վավերացման սխալ %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Պահանջվում է վավերացում\"\nAuth.InvalidScope.Text=\"%1-ի նույնականացման պահանջները փոխվել են: Որոշ գործառույթներ կարող են անհասանելի լինել:\"\nAuth.LoadingChannel.Title=\"Ալիքի տեղեկությունները բեռնվում են...\"\nAuth.LoadingChannel.Text=\"Բեռնվում է %1-ի ալիքի տվյալները, խնդրում ենք սպասել...\"\nAuth.LoadingChannel.Error=\"Չհաջողվեց ստանալ ալիքի մասին տեղեկությունները:\"\nAuth.ChannelFailure.Title=\"Չհաջողվեց բեռնել ալիքը\"\nAuth.ChannelFailure.Text=\"Չհաջողվեց բեռնել %1-ի ալիքի տեղեկատվությունը\\n\\n%2: %3\"\nAuth.Chat=\"Զրուցարան\"\nAuth.StreamInfo=\"Հեռարձակման տեղեկատվություն\"\nTwitchAuth.Stats=\"Twitch վիճակագրություն\"\nTwitchAuth.Feed=\"Twitch գործունեության հոսք\"\nTwitchAuth.TwoFactorFail.Title=\"Չհաջողվեց պահանջել հեռարձակման բանալին\"\nTwitchAuth.TwoFactorFail.Text=\"OBS-ին չհաջողվեց միանալ ձեր Twitch հաշվին: Համոզվեք, որ ձեր <a href='https://www.twitch.tv/settings/security'>Twitch անվտանգության կարգավորումներում</a> տեղադրված է երկգործոն նույնականացումը, քանի որ դա անհրաժեշտ է հեռարձակման համար:\"\nRestreamAuth.Channels=\"Restream ալիքները\"\nCopy.Filters=\"Պատճենել զտիչներ\"\nPaste.Filters=\"Տեղադրել զտիչներ\"\nBrowserPanelInit.Title=\"Բրաուզերի սկզբնավորում...\"\nBrowserPanelInit.Text=\"Նախաձեռնվում է դիտարկիչը, խնդրում ենք սպասել...\"\nBandwidthTest.Region=\"Տարածաշրջան\"\nBandwidthTest.Region.US=\"Միացյալ Նահանգներ\"\nBandwidthTest.Region.EU=\"Եվրոպա\"\nBandwidthTest.Region.Asia=\"Ասիա\"\nBandwidthTest.Region.Other=\"Այլ\"\nBasic.AutoConfig=\"Ավտոմատ տեղադրման մոգ\"\nBasic.AutoConfig.ApplySettings=\"Կիրառել կարգավորումները\"\nBasic.AutoConfig.StartPage=\"Օգտագործման մասին տեղեկություններ\"\nBasic.AutoConfig.StartPage.SubTitle=\"Նշեք, թե ինչի համար եք ցանկանում օգտագործել ծրագիրը\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Օպտիմիզացնել հեռարձակման համար, երկրորդական տեսագրման համար\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Օպտիմալացնել միայն տեսագրման համար, ես չեմ հեռարձակվի\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Ես կօգտագործեմ միայն վիրտուալ տեսախցիկը\"\nBasic.AutoConfig.VideoPage=\"Վիդեո կարգավորումներ\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Նշեք տեսանյութի կարգավորումները, որոնք ցանկանում եք օգտագործել\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Օգտագործել ընթացիկ (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Էկրան %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Օգտագործել ընթացիկ (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 կամ 30, բայց հնարավորության դեպքում նախընտրեք 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 կամ 30, բայց գերադասում են բարձր լուծում\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Նշում. Պարտադիր չէ, որ կտավի լուծաչափը (բազային գիծը) նույնը լինի հեռարձակման կամ ձայնագրման լուծաչափին: Ձեր հեռարձակման/ձայնագրության իրական լուծումը կարող է կրճատվել՝ նվազեցնելու ռեսուրսի օգտագործումը կամ բիթային արագությունը:\"\nBasic.AutoConfig.StreamPage=\"Հեռարձակման տեղեկատվություն\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Խնդրում ենք մուտքագրել ձեր հեռարձակման տվյալները\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Միացնել հաշիվը (խորհուրդ է տրվում)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Անջատել հաշիվը:\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Անջատե՞լ հաշիվը:\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Այս փոփոխությունն ուժի մեջ կմտնի անմիջապես։ Իսկապե՞ս ուզում եք դուրս գալ:\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Ստացեք հոսքի բանալին\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Ավել տեղեկատվություն\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Օգտագործել հոսքի բանալին\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Օգտագործել հոսքի բանալին (առաջադեմ օգտվողների համար)\"\nBasic.AutoConfig.StreamPage.Service=\"Ծառայություն\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Ցուցադրել բոլորը...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Հաճախորդ...\"\nBasic.AutoConfig.StreamPage.Server=\"Սերվեր\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Հոսքի բանալի\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST․ մուտքագրեք ծածկագրված գաղտնաբառը։\\nRTMP․ մուտքագրեք ծառայության կողմից տրամադրվող բանալին։\\nSRT․ մուտքագրեք եթերի id-ն, երբ ծառայությունը կօգտագործվի։\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Կոդավորման բանալի\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Պատվերով Տոկեն\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Միացված հաշիվ\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Հաշվարկել բիթային արագությունը թողունակության փորձարկման ընթացքում (կարող է տևել մի քանի րոպե)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Նախընտրել ապարատային կոդավորումը\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Սարքավորումների կոդավորումը նվազեցնում է պրոցեսորի օգտագործումը, բայց որակի նույն մակարդակին հասնելու համար կարող է պահանջվել ավելի շատ բիթային արագություն:\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Հեռարձակման նախազգուշացում\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Թողունակության թեստ՝ ձեր ալիքում պատահական վիդեո տվյալների հեռարձակման համար՝ առանց ձայնի: Խորհուրդ ենք տալիս ժամանակավորապես անջատել հեռարձակման ձայնագրումը ֆայլում և հեռարձակումը դարձնել մասնավոր՝ մինչև փորձարկման ավարտը: Շարունակե՞լ\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Թեստ %1\"\nBasic.AutoConfig.TestPage=\"Վերջնական արդյունքներ\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Այժմ ծրագիրը կատարում է մի շարք թեստեր՝ լավագույն պարամետրերը գնահատելու համար\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Փորձարկումն ավարտված է\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Թողունակության թեստ, սա կարող է տևել մի քանի րոպե...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Այս ծառայության մատչման համար ելք չի գտնվել\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Միացվում է՝ %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Չհաջողվեց միանալ սերվերներին: Խնդրում ենք ստուգել ձեր ինտերնետային կապը և նորից փորձել:\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Գործողությունների փորձարկում՝ %1-ի համար\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Հեռարձակման կոդավորչի փորձարկումը կարող է տևել մեկ րոպե...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Տեսագրման կոդավորչի փորձարկումը կարող է տևել մեկ րոպե...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Չհաջողվեց գործարկել կոդավորիչը\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Փորձարկում է %1x%2 %3 ԿՄՎ...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Հոսքային կոդավորիչ\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Տեսագրման կոդավորիչ\"\nBasic.AutoConfig.TestPage.Result.Header=\"Ծրագիրը որոշել է, որ այս նմուշի կարգավորումները առավել հարմար են ձեզ համար.\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Այս կարգավորումներն օգտագործելու համար սեղմեք «Կիրառել կարգավորումները»: Վիզարդի միջոցով վերակազմավորելու համար սեղմեք Հետ: Ցանկալի կարգավորումները ինքնուրույն կարգավորելու համար սեղմեք «Չեղարկել» և բացեք «Կարգավորումներ»:\"\nBasic.AutoConfig.Info=\"Ավտոմատ տեղադրման հրաշագործը կորոշի լավագույն կարգավորումները՝ հիմնված ձեր համակարգչի և ինտերնետի արագության վրա:\"\nBasic.AutoConfig.RunAnytime=\"Այն կարող է գործարկվել ցանկացած ժամանակ՝ օգտագործելով «Գործիքներ» ցանկը:\"\nBasic.Stats=\"Վիճակագրություն\"\nBasic.Stats.CPUUsage=\"ԿՄՀ-ի օգտագործումը\"\nBasic.Stats.HDDSpaceAvailable=\"Հասանելի սկավառակի տարածություն\"\nBasic.Stats.MemoryUsage=\"Հիշողության օգտագործումը\"\nBasic.Stats.AverageTimeToRender=\"Կադրի ցուցադրման միջին ժամանակը\"\nBasic.Stats.SkippedFrames=\"Թողնել կադրեր (կոդավորում)\"\nBasic.Stats.MissedFrames=\"Թողնել կադրերը (արտադրում)\"\nBasic.Stats.Output.Stream=\"Հեռարձակում\"\nBasic.Stats.Output.Recording=\"Տեսագրությունը\"\nBasic.Stats.Status=\"Կարգավիճակ\"\nBasic.Stats.Status.Recording=\"Տեսագրությունը\"\nBasic.Stats.Status.Live=\"ՈՒՂԻՂ\"\nBasic.Stats.Status.Reconnecting=\"Վերամիացում\"\nBasic.Stats.Status.Inactive=\"Ոչ ակտիվ\"\nBasic.Stats.Status.Active=\"Ակտիվ է\"\nBasic.Stats.DroppedFrames=\"Թողնել կադրեր (ցանց)\"\nBasic.Stats.MegabytesSent=\"Ընդհանուր տվյալների թողարկում\"\nBasic.Stats.Bitrate=\"Բիթրեյթ\"\nBasic.Stats.DiskFullIn=\"Սկավառակը լցված է (օրինակ)\"\nBasic.Stats.ResetStats=\"Վերականգնել վիճակագրությունը\"\nResetUIWarning.Title=\"Իսկապե՞ս ուզում եք վերականգնել օգտատիրոջ միջերեսի կարգավորումները:\"\nResetUIWarning.Text=\"Օգտամիջերեսի վերակայումը կթաքցնի լրացուցիչ նավահանգիստները: Դուք պետք է ցուցադրեք այս նավահանգիստները դոկ բարերի ցանկում, եթե ցանկանում եք, որ դրանք տեսանելի լինեն:\\n\\nԻսկապե՞ս ուզում եք վերակայել միջերեսը:\"\nUpdater.Title=\"Հասանելի է նոր թարմացում\"\nUpdater.Text=\"Հասանելի է նոր թարմացում։\"\nUpdater.UpdateNow=\"Թարմացնել հիմա\"\nUpdater.RemindMeLater=\"Հիշեցնել ինձ ավելի ուշ\"\nUpdater.Skip=\"Բաց թողնել տարբերակը\"\nUpdater.NoUpdatesAvailable.Title=\"Հասանելի թարմացումներ չկան\"\nUpdater.NoUpdatesAvailable.Text=\"Թարմացումներ չեն գտնվել\"\nUpdater.BranchNotFound.Title=\"Թարմացման Ալիքը Հեռացված է\"\nUpdater.BranchNotFound.Text=\"Ձեր ընտրած թարմացման ալիքը այլևս հասանելի չէ, OBS-ը կվերակայվի կանխադրվածի։\"\nUpdater.RepairButUpdatesAvailable.Title=\"Ամբողջականության ստուգումը հասանելի չէ\"\nUpdater.RepairButUpdatesAvailable.Text=\"Ֆայլերի ամբողջականության ստուգումը հնարավոր է միայն վերջին տարբերակի համար: Օգտագործեք Օգնություն → Ստուգեք թարմացումները՝ ձեր OBS-ի տեղադրումը ստուգելու և թարմացնելու համար:\"\nUpdater.RepairConfirm.Title=\"Վավերացրեք ամբողջականության ստուգումը\"\nUpdater.RepairConfirm.Text=\"Անձեռնմխելիության ստուգումն իրականացնելը կսկանավորի ձեր OBS-ի տեղադրումը և կներբեռնի վնասված/փոփոխված ֆայլերը: Սա կարող է որոշ ժամանակ տևել:\\n\\nՑանկանու՞մ եք շարունակել:\"\nUpdater.FailedToLaunch=\"Չհաջողվեց ստուգել թարմացումների առկայությունը\"\nQuickTransitions.SwapScenes=\"Փոխանակեք նախադիտումը/ծրագրի տեսարանները անցումից հետո\"\nQuickTransitions.SwapScenesTT=\"Փոխանակում է նախադիտումը և ծրագրի տեսարանները անցումից հետո (եթե ծրագրի սկզբնական տեսարանը դեռ գոյություն ունի):\\nՍա չի չեղարկի որևէ փոփոխություն, որը կարող է կատարվել ծրագրի սկզբնական տեսարանում:\"\nQuickTransitions.DuplicateScene=\"Կրկնվող տեսարան\"\nQuickTransitions.DuplicateSceneTT=\"Նույն տեսարանը խմբագրելիս թույլ է տալիս խմբագրել աղբյուրների տրանսֆորմացիան/տեսանելիությունը՝ առանց ծրագրի ելքը փոփոխելու։\\nԱղբյուրների հատկությունները խմբագրելու համար՝ առանց ծրագրի ելքը փոփոխելու, միացրեք «Կրկնվող աղբյուրները»։\\nԱյս արժեքը փոխելու դեպքում կզրոյացվի ընթացիկ ծրագրի տեսարանը (եթե այն դեռ կա):\"\nQuickTransitions.EditProperties=\"Կրկնվող աղբյուրներ\"\nQuickTransitions.EditPropertiesTT=\"Միևնույն տեսարանը խմբագրելիս թույլ է տալիս խմբագրել աղբյուրների հատկությունները՝ առանց ծրագրի ելքը փոփոխելու:\\nՍա կարող է օգտագործվել միայն այն դեպքում, եթե «Կրկնվող տեսարանը» միացված է:\\nՈրոշ աղբյուրներ (օրինակ՝ նկարահանված կամ մեդիա աղբյուրներ) չեն աջակցում սա և չեն կարող խմբագրվել: Առանձին-առանձին:\\nԱյս արժեքը փոխելու դեպքում ծրագրի ընթացիկ տեսարանը կվերակայվի (եթե այն դեռ գոյություն ունի):\\n\\nԶգուշացում. Քանի որ աղբյուրները կրկնօրինակվելու են, դա կարող է պահանջել լրացուցիչ համակարգի կամ տեսագրման ռեսուրսներ:\"\nQuickTransitions.HotkeyName=\"Արագ անցում: %1\"\nBasic.AddTransition=\"Ավելացնել մաքսային անցում\"\nBasic.RemoveTransition=\"Հեռացնել մաքսային անցումը\"\nBasic.TransitionProperties=\"Անցումային ընտրանքներ\"\nBasic.SceneTransitions=\"Տեսարանների անցումներ\"\nBasic.TransitionDuration=\"Տեւողությունը\"\nBasic.TogglePreviewProgramMode=\"Ստուդիայի ռեժիմ\"\nUndo.Undo=\"Չեղարկել\"\nUndo.Redo=\"Կրկնել\"\nUndo.Add=\"Ավելացնել '%1'\"\nUndo.Delete=\"Ջնջել '%1'\"\nUndo.Rename=\"Վերանվանել '%1'\"\nUndo.SceneCollection.Switch=\"Անցնել '%1'\"\nUndo.Item.Undo=\"Չեղարկել %1\"\nUndo.Item.Redo=\"Կրկնել %1\"\nUndo.Sources.Multi=\"Ջնջել աղբյուրները %1\"\nUndo.Filters=\"Զտիչի կարգավորում '%1'-ում\"\nUndo.Filters.Paste.Single=\"Զտիչ '%1' մտցնելով '%2'\"\nUndo.Filters.Paste.Multiple=\"Պատճենել զտիչները '%1'-ից մինչև '%2'\"\nUndo.Transform=\"Փոխարկել աղբյուր(ներ)ը '%1'-ի\"\nUndo.Transform.Paste=\"Փոխակերպման կարգավորումների զետեղում «%1»-ում\"\nUndo.Transform.Rotate=\"Ռոտացիա «%1»-ում\"\nUndo.Transform.Reset=\"Փոխակերպման վերակայում «%1»-ում\"\nUndo.Transform.HFlip=\"«%1»-ում հորիզոնական արտացոլում\"\nUndo.Transform.VFlip=\"Ուղղահայաց շրջում «%1»-ում\"\nUndo.Transform.FitToScreen=\"Հարմարեցված էկրանին «%1»-ում\"\nUndo.Transform.StretchToScreen=\"Ձգվել, որպեսզի տեղավորվի էկրանը «%1»-ում\"\nUndo.Transform.Center=\"Էկրանի կենտրոնացում «%1»-ում\"\nUndo.Transform.VCenter=\"Ուղղահայաց կենտրոնացում էկրանին «%1»-ում\"\nUndo.Transform.HCenter=\"Հորիզոնական էկրանի կենտրոնացում «%1»-ում\"\nUndo.Volume.Change=\"ձայնի փոփոխություն «%1»-ի համար\"\nUndo.Volume.Mute=\"Ձայնը անջատել «%1»\"\nUndo.Volume.Unmute=\"Ձայնը միացնել «%1»\"\nUndo.Balance.Change=\"Փոխել աուդիո հաշվեկշիռը «%1»-ի\"\nUndo.SyncOffset.Change=\"Փոխել աուդիո համաժամացման օֆսեթը «%1»\"\nUndo.MonitoringType.Change=\"Փոխել աուդիո մոնիտորինգը «%1»-ի համար\"\nUndo.Mixers.Change=\"Աուդիո խառնիչի կարգավորումը փոխելով «%1»-ի\"\nUndo.ForceMono.On=\"միացնել հարկադիր մոնո «%1»\"\nUndo.ForceMono.Off=\"Անջատել հարկադիր մոնո «%1»\"\nUndo.Properties=\"Աղբյուրի սեփականության կարգավորումներ «%1»\"\nUndo.Scene.Duplicate=\"Կրկնօրինակ տեսարան «%1»\"\nUndo.ShowTransition=\"Ցույց տալիս անցումը «%1»-ին\"\nUndo.HideTransition=\"Թաքցնել ցատկը դեպի «%1»\"\nUndo.ShowSceneItem=\"Ցույց է տալիս «%1»-ը «%2»-ում\"\nUndo.HideSceneItem=\"Թաքցնում է «%1»-ը «%2»-ում\"\nUndo.ReorderSources=\"Առողջարանային աղբյուրներ «%1»-ում\"\nUndo.MoveUp=\"Տեղափոխել «%1»-ը «%2»-ի վերև\"\nUndo.MoveDown=\"Տեղափոխել «%1»-ը «%2»-ի ներքև\"\nUndo.MoveToTop=\"Տեղափոխել «%1»-ը «%2»-ի վերև\"\nUndo.MoveToBottom=\"տեղափոխել «%1»-ը «%2»-ի ներքև\"\nUndo.PasteSource=\"Տեղադրել աղբյուր(ներ)ը «%1»-ում\"\nUndo.PasteSourceRef=\"Տեղադրել հղում(ներ) դեպի աղբյուրները «%1»-ում\"\nUndo.GroupItems=\"Խմբավորված տարրեր «%1»-ում\"\nTransitionNameDlg.Text=\"Խնդրում ենք մուտքագրել անուն անցման համար\"\nTransitionNameDlg.Title=\"Անցումային անուն\"\nTitleBar.SafeMode=\"ԱՆՎՏԱՆԳ ՌԵԺԻՄ\"\nTitleBar.PortableMode=\"Տեղափոխման ռեժիմ\"\nTitleBar.Profile=\"Պրոֆիլ\"\nTitleBar.Scenes=\"Տեսարաններ\"\nNameExists.Title=\"Անունն արդեն գոյություն ունի\"\nNameExists.Text=\"Այս անունը արդեն օգտագործվում է։\"\nNoNameEntered.Title=\"Խնդրում ենք մուտքագրել վավեր անուն\"\nNoNameEntered.Text=\"Դուք չեք կարող օգտագործել դատարկ անուններ:\"\nConfirmStart.Title=\"Սկսե՞լ հեռարձակումը:\"\nConfirmStart.Text=\"Իսկապե՞ս ուզում եք սկսել հեռարձակումը:\"\nConfirmStop.Title=\"Դադարեցնե՞լ հեռարձակումը\"\nConfirmStop.Text=\"Իսկապե՞ս ուզում եք դադարեցնել հեռարձակումը:\"\nConfirmStopRecord.Title=\"Դադարեցնե՞լ տեսագրումը\"\nConfirmStopRecord.Text=\"Իսկապե՞ս ուզում եք դադարեցնել տեսագրումը:\"\nConfirmBWTest.Title=\"Սկսե՞լ թողունակության թեստը:\"\nConfirmBWTest.Text=\"Դուք կարգավորել եք OBS-ը թողունակության փորձարկման ռեժիմում: Այս ռեժիմը թույլ է տալիս փորձարկել ցանցը՝ առանց ձեր ալիքի ուղիղ եթերում: Փորձարկումն ավարտելուց հետո դուք պետք է անջատեք այս գործառույթը, որպեսզի դիտողները տեսնեն ձեր հեռարձակումը:\\n\\nՑանկանու՞մ եք շարունակել:\"\nConfirmRemove.Title=\"Հաստատել հեռացումը\"\nConfirmRemove.Text=\"Իսկապե՞ս ցանկանում եք հեռացնել '%1'-ը:\"\nConfirmRemove.TextMultiple=\"Իսկապե՞ս ուզում եք հեռացնել %1 տարր:\"\nConfirmReset.Title=\"Վերակայել հատկությունները\"\nConfirmReset.Text=\"Վստա՞հ եք, որ ցանկանում եք վերակայել ընթացիկ հատկությունները իրենց կանխադրվածներին:\"\nOutput.StartStreamFailed=\"Չհաջողվեց սկսել հեռարձակումը\"\nOutput.StartRecordingFailed=\"Չհաջողվեց սկսել տեսագրությունը\"\nOutput.StartReplayFailed=\"Չհաջողվեց վերագործարկել բուֆերը\"\nOutput.StartVirtualCamFailed=\"Չհաջողվեց գործարկել վիրտուալ տեսախցիկը\"\nOutput.StartFailedGeneric=\"Ելքի ձախողում. Մանրամասների համար տես գրանցամատյանը:\\n\\nՆշում. Եթե օգտագործում եք NVENC կամ AMD կոդավորիչներ, համոզվեք, որ տեղադրված եք վերջին վիդեո դրայվերը:\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Դուք չեք կարող կրկնել կրկնությունները դադարի ժամանակ\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Զգուշացում. կրկնությունները չեն կարող պահպանվել, երբ տեսագրությունը դադարեցված է:\"\nOutput.ConnectFail.Title=\"Չհաջողվեց միանալ\"\nOutput.ConnectFail.BadPath=\"Անվավեր կապի ուղի կամ URL: Խնդրում ենք ստուգել ձեր կարգավորումները՝ հաստատելու համար, որ դրանք վավեր են:\"\nOutput.ConnectFail.ConnectFailed=\"Չհաջողվեց միանալ սերվերին\"\nOutput.ConnectFail.InvalidStream=\"Անհնար է մուտք գործել նշված ալիք կամ հեռարձակման բանալի, խնդրում ենք կրկին ստուգել բանալին: Եթե ​​դա ճիշտ է, ապա խնդիրը կարող է կապված լինել սերվերին միանալու հետ:\"\nOutput.ConnectFail.HdrDisabled=\"HDR ելքը ներկայումս անջատված է այդ ելքի համար:\"\nOutput.ConnectFail.Error=\"Սերվերին միանալու փորձի ժամանակ անսպասելի սխալ առաջացավ: Մանրամասները գրանցամատյանում:\"\nOutput.ConnectFail.Disconnected=\"Անջատված է սերվերից:\"\nOutput.StreamEncodeError.Title=\"Կոդավորման սխալ\"\nOutput.StreamEncodeError.Msg=\"Հեռարձակման ընթացքում կոդավորման սխալ է տեղի ունեցել:\"\nOutput.RecordFail.Title=\"Չհաջողվեց սկսել տեսագրությունը\"\nOutput.RecordFail.Unsupported=\"Ելքային ձևաչափը կա՛մ չի աջակցվում, կա՛մ չի աջակցում մեկից ավելի աուդիո կատարում: Խնդրում ենք ստուգել ձեր կարգավորումները և նորից փորձել:\"\nOutput.RecordNoSpace.Title=\"Սկավառակի բավարար տարածք չկա\"\nOutput.RecordNoSpace.Msg=\"Տեսագրումը շարունակելու համար սկավառակի վրա բավարար տեղ չկա:\"\nOutput.RecordError.Title=\"Տեսագրման սխալ\"\nOutput.RecordError.Msg=\"Տեսագրման ընթացքում տեղի է ունեցել չճանաչված սխալ:\"\nOutput.RecordError.EncodeErrorMsg=\"Տեսագրման ընթացքում տեղի է ունեցել կոդավորման սխալ:\"\nOutput.BadPath.Title=\"Ֆայլի սխալ ուղի\"\nOutput.BadPath.Text=\"Կազմաձև մուտքի ուղին չի կարող բացվել: Խնդրում ենք ստուգել ձայնագրությունների ուղին Կարգավորումներ → Ելք → Տեսագրում:\"\nOutput.NoBroadcast.Title=\"Հեռարձակումը կարգավորված չէ\"\nOutput.NoBroadcast.Text=\"Ուղիղ եթեր սկսելու համար դուք պետք է ստեղծեք հեռարձակում:\"\nOutput.BroadcastStartFailed=\"Չհաջողվեց սկսել հեռարձակումը\"\nOutput.BroadcastStopFailed=\"Չհաջողվեց դադարեցնել հեռարձակումը\"\nRemux.SourceFile=\"OBS Տեսագրություն\"\nRemux.TargetFile=\"Նպատակային ֆայլ\"\nRemux.Remux=\"Ռեմուքս\"\nRemux.Stop=\"Դադարեցրել վերամիպլեքսավորումը\"\nRemux.ClearFinished=\"Ավարտված հեռացնել ցանկից\"\nRemux.ClearAll=\"Մաքրել ցուցակը\"\nRemux.OBSRecording=\"OBS Տեսագրություն\"\nRemux.FinishedTitle=\"Վերամիպլեքսավորումն ավարտված է\"\nRemux.Finished=\"Վերամիպլեքսի տեսագրումը\"\nRemux.FinishedError=\"Վերամիպլեքսի տեսագրումը, բայց ֆայլը կարող է լիքը չլինել\"\nRemux.SelectRecording=\"Ընտրել OBS գրառում...\"\nRemux.SelectTarget=\"Ընտրեք ֆայլ...\"\nRemux.FileExistsTitle=\"Գոյություն ունեն նպատակակետային ֆայլեր\"\nRemux.FileExists=\"Հետևյալ թիրախային ֆայլերն արդեն գոյություն ունեն: Ցանկանու՞մ եք դրանք փոխարինել:\"\nRemux.ExitUnfinishedTitle=\"Ռեմուլտիպլեքսիան ընթացքի մեջ է\"\nRemux.ExitUnfinished=\"Ռեմուլտիպլեքսիան չի ավարտվել, գործընթացի հիմա դադարեցումը կարող է անօգտագործելի դարձնել ֆայլը:\\nԻսկապե՞ս ուզում եք դադարեցնել վերամիքսումը:\"\nRemux.HelpText=\"Քաշեք և գցեք ֆայլերը այս պատուհանում՝ ռեմուքդի համար, կամ ընտրեք դատարկ «OBS Տեսագրում» վանդակը՝ ֆայլ ընտրելու համար:\"\nRemux.NoFilesAddedTitle=\"Վերամիավորման ֆայլը չի ավելացվել\"\nRemux.NoFilesAdded=\"Ոչ մի ֆայլ չի ավելացվել վերափոխման համար։ Քաշեք մեկ կամ մի քանի տեսանյութ պարունակող պանակը։\"\nMissingFiles=\"Ֆայլերը բացակայում է\"\nMissingFiles.MissingFile=\"Ֆայլը բացակայում է\"\nMissingFiles.NewFile=\"Նոր ֆայլ\"\nMissingFiles.HelpText=\"Որոշ ֆայլեր բացակայում են OBS-ի վերջին գործարկումից հետո:\"\nMissingFiles.Clear=\"<մաքրված>\"\nMissingFiles.NumFound=\"Գտնվել է %1-ը %2-ից\"\nMissingFiles.Search=\"Գրացուցակի որոնում...\"\nMissingFiles.SelectFile=\"Ընտրել ֆայլ\"\nMissingFiles.SelectDir=\"Ընտրել թղթապանակ որոնման համար\"\nMissingFiles.State=\"Վիճակ\"\nMissingFiles.Missing=\"Բացակայում է\"\nMissingFiles.Replaced=\"Փոխարինված\"\nMissingFiles.Cleared=\"Մաքրված\"\nMissingFiles.Found=\"Գտնվել է\"\nMissingFiles.AutoSearch=\"Գտնվել է ֆայլի հավելյալ համընկնում\"\nMissingFiles.AutoSearchText=\"OBS-ն այս պանակում բացակայող ֆայլերի հավելյալ համընկնում է գտել: Ցանկանու՞մ եք դրանք ավելացնել:\"\nMissingFiles.NoMissing.Title=\"Բացակայող ֆայլերի ստուգում\"\nMissingFiles.NoMissing.Text=\"Բացակայող ֆայլերը չեն գտնվել։\"\nMacPermissions.MenuAction=\"Դիտեք հավելվածի թույլտվությունները...\"\nMacPermissions.Title=\"Դիտեք հավելվածի թույլտվությունները\"\nMacPermissions.Description=\"OBS Studio-ն պահանջում է ձեր թույլտվությունը որոշակի հնարավորություններ տրամադրելու համար: Խորհուրդ է տրվում միացնել այս թույլտվությունները, սակայն դրանք չեն պահանջվում հավելվածն օգտագործելու համար: Դուք միշտ կարող եք դրանք միացնել ավելի ուշ:\"\nMacPermissions.Description.OpenDialog=\"Այս երկխոսությունը կարող է վերաբացվել OBS Studio մենյուի միջոցով:\"\nMacPermissions.AccessGranted=\"Մուտքը տրված է\"\nMacPermissions.RequestAccess=\"Մուտք գործելու հարցում\"\nMacPermissions.OpenPreferences=\"Բացեք կարգավորումները %1\"\nMacPermissions.Item.ScreenRecording=\"Էկրանի տեսագրում\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS-ը պահանջում է այս թույլտվությունը՝ ձեր էկրանը տեսագրելու համար:\"\nMacPermissions.Item.Camera=\"Տեսախցիկ\"\nMacPermissions.Item.Camera.Details=\"Այս թույլտվությունը պահանջվում է վեբ-տեսախցիկից կամ ձայնագրման քարտից բովանդակություն նկարելու համար:\"\nMacPermissions.Item.Microphone=\"Խոսափող\"\nMacPermissions.Item.Accessibility=\"Հասանելիություն\"\nMacPermissions.Continue=\"Շարունակել\"\nSourceLeak.Title=\"Աղբյուրի մաքրման սղալը\"\nSourceLeak.Text=\"Տեսարանների հավաքածուները փոխելիս խնդիր առաջացավ, և որոշ աղբյուրներ չկարողացան բեռնաթափվել: Այս խնդիրը սովորաբար առաջանում է պլագինների կողմից, որոնք պատշաճ կերպով չեն ազատում ռեսուրսները: Խնդրում ենք համոզվել, որ ձեր օգտագործած բոլոր պլագինները թարմացված էն:\\n\\nOBS Studio-ն ավարտում է աշխատանքը՝ տվյալների հնարավոր վնասը կանխելու համար:\"\nBasic.DesktopDevice1=\"Նվագարկման սարք\"\nBasic.DesktopDevice2=\"Նվագարկման սարք 2\"\nBasic.AuxDevice1=\"Միկ/Aux\"\nBasic.AuxDevice2=\"Միկ/Aux 2\"\nBasic.AuxDevice3=\"Միկ/Aux 3\"\nBasic.AuxDevice4=\"Միկ/Aux 4\"\nBasic.Scene=\"Տեսարան\"\nBasic.DisplayCapture=\"Էկրանի նկարահանում\"\nBasic.Main.PreviewConextMenu.Enable=\"Միացնել նախադիտումը\"\nBasic.Main.Preview.Disable=\"Անջատել նախադիտումը\"\nScaleFiltering=\"Մաշտաբային զտիչ\"\nScaleFiltering.Point=\"Կետավոր\"\nScaleFiltering.Bilinear=\"Երկգծային\"\nScaleFiltering.Bicubic=\"Երկխորանարդ\"\nScaleFiltering.Lanczos=\"Լանցոս\"\nScaleFiltering.Area=\"Տարածաշրջան\"\nBlendingMethod=\"Խառնման մեթոդ\"\nBlendingMethod.Default=\"Կանխադրված\"\nBlendingMethod.SrgbOff=\"Անջ. SRGB\"\nBlendingMode=\"Ծածկույթի մեթոդ\"\nBlendingMode.Normal=\"Նորմալ\"\nBlendingMode.Additive=\"Ավելացնել\"\nBlendingMode.Subtract=\"Պակասեցնել\"\nBlendingMode.Screen=\"Էկրան\"\nBlendingMode.Multiply=\"Բազմապատկում\"\nBlendingMode.Lighten=\"Կայծակում\"\nBlendingMode.Darken=\"Մթնշաղ\"\nDeinterlacing=\"Միահյուս վերացում\"\nDeinterlacing.Discard=\"Մերժել\"\nDeinterlacing.TopFieldFirst=\"Սկզբում վերին դաշտը\"\nDeinterlacing.BottomFieldFirst=\"Ներքևի դաշտ առաջին\"\nVolControl.SliderUnmuted=\"Ձայնի վերահսկում '%1':\"\nVolControl.SliderMuted=\"Ձայնի վերահսկում '%1': (այժմ խլացված է)\"\nVolControl.Mute=\"Ձայնը անջատել «%1»\"\nVolControl.Properties=\"Մանրամասններ '%1'\"\nVolControl.UnassignedWarning.Title=\"Չնշված ձայնի աղբյուր\"\nBasic.Main.AddSceneDlg.Title=\"Ավելացնել տեսարան\"\nBasic.Main.AddSceneDlg.Text=\"Խնդրում ենք մուտքագրել տեսարանի անունը\"\nBasic.Main.DefaultSceneName.Text=\"Տեսարան %1\"\nBasic.Main.AddSceneCollection.Title=\"Ավելացնել տեսարանների հավաքածու\"\nBasic.Main.AddSceneCollection.Text=\"Խնդրում ենք մուտքագրել տեսարանի հավաքածուի անունը\"\nBasic.Main.RenameSceneCollection.Title=\"Վերանվանել տեսարանի հավաքածուն\"\nAddProfile.Title=\"Ավելացնել պրոֆիլ\"\nAddProfile.Text=\"Խնդրում ենք մուտքագրել պրոֆիլի անուն\"\nAddProfile.WizardCheckbox=\"Ցույց տալ ավտոմատ կազմաձևման մոգերը\"\nRenameProfile.Title=\"Վերանվանել պրոֆիլը\"\nBasic.Main.MixerRename.Title=\"Վերանվանել աուդիո աղբյուրը\"\nBasic.Main.MixerRename.Text=\"Խնդրում ենք մուտքագրել աուդիո աղբյուրի անունը\"\nBasic.Main.PreviewDisabled=\"Նախադիտումը ներկայումս անջատված է\"\nBasic.SourceSelect=\"Ստեղծել/Ընտրել աղբյուրը\"\nBasic.SourceSelect.CreateNew=\"Ստեղծել նորը\"\nBasic.SourceSelect.AddExisting=\"Ավելացնել առկա\"\nBasic.SourceSelect.AddVisible=\"Աղբյուրը տեսանելի դարձրեք\"\nBasic.Main.Sources.Visibility=\"Տեսանելիությունը\"\nBasic.Main.Sources.VisibilityDescription=\"«%1»-ի տեսանելիության վերահսկում կտավի վրա\"\nBasic.Main.Sources.Lock=\"Արգելափակել\"\nBasic.Main.Sources.LockDescription=\"Կողպում է «%1»-ի դիրքն ու մասշտաբը կտավի վրա\"\nBasic.PropertiesWindow=\"Մանրամասններ '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (ինքնաընտրություն: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Ընտրել գույնը\"\nBasic.PropertiesWindow.SelectFont=\"Ընտրել տառատեսակ\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Ընտրեք տառատեսակ\"\nBasic.PropertiesWindow.ConfirmTitle=\"Կարգավորումները փոխվել են\"\nBasic.PropertiesWindow.Confirm=\"Կան չպահված փոփոխություններ: Կցանկանա՞ք դրանք պահել։\"\nBasic.PropertiesWindow.NoProperties=\"Ընտրանքներ չկան\"\nBasic.PropertiesWindow.AddFiles=\"Բոլոր ֆայլերը\"\nBasic.PropertiesWindow.AddDir=\"Թղթապանակ ավելացնել\"\nBasic.PropertiesWindow.AddURL=\"Ավելացնել ուղի/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Թղթապանակ ավելացնել «%1»-ին\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Ֆայլեր ավելացնել «%1»-ին\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Ավելացնել գրառում «%1»-ին\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Փոխել գրառումը «%1»-ից\"\nBasic.PropertiesView.FPS.Simple=\"Պարզ ԿՄՎ արժեքներ\"\nBasic.PropertiesView.FPS.Rational=\"ԿՄՎ-ի ռացիոնալ արժեքներ\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"ԿՄՎ վավեր տիրույթներ.\"\nBasic.PropertiesView.UrlButton.Text=\"Բացե՞լ այս հղումը կանխադրված դիտարկիչում:\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Բացել URL-ը\"\nBasic.InteractionWindow=\"Փոխազդեցություն «%1»-ի հետ\"\nBasic.StatusBar.Reconnecting=\"Կապը կորել է, նորից միանում է %2 վայրկյանից (փորձ %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Միանալու փորձ... (փորձ %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Միացումը հաջողվեց\"\nBasic.StatusBar.Delay=\"Հետաձգում (%1 վ)\"\nBasic.StatusBar.DelayStartingIn=\"Հետաձգում (սկսած %1 վրկ-ից)\"\nBasic.StatusBar.DelayStoppingIn=\"Հետաձգում (ավարտվում է %1 վրկ-ով)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Հետաձգում (ավարտվում է %1 վ-ով, սկսվում է %2 վ-ով)\"\nBasic.StatusBar.RecordingSavedTo=\"Տեսագրումը պահված է «%1»-ում\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Կրկին փորձեք «%1»-ում գրված բուֆեր\"\nBasic.StatusBar.ScreenshotSavedTo=\"Էկրանակադրը պահված է որպես «%1»\"\nBasic.StatusBar.AutoRemuxedTo=\"Տեսագրությունը վերամուլտիպլեքսացված է «%1»-ի\"\nBasic.Filters=\"Զտիչներ\"\nBasic.Filters.AsyncFilters=\"Աուդիո/վիդեո զտիչներ\"\nBasic.Filters.AudioFilters=\"Աուդիո զտիչներ\"\nBasic.Filters.EffectFilters=\"Էֆէկտի զտիչներ\"\nBasic.Filters.Title=\"Զտիչների համար '%1'\"\nBasic.Filters.AddFilter.Title=\"Զտիչի անվանումը\"\nBasic.Filters.AddFilter.Text=\"Խնդրում ենք մուտքագրել զտիչի անունը\"\nBasic.TransformWindow=\"Տեսարանի տարրի փոխակերպում\"\nBasic.TransformWindow.Position=\"Դիրք\"\nBasic.TransformWindow.PositionX=\"Դիրք X\"\nBasic.TransformWindow.PositionY=\"Դիրք Y\"\nBasic.TransformWindow.Rotation=\"Ռոտացիա\"\nBasic.TransformWindow.Size=\"Չափը\"\nBasic.TransformWindow.Width=\"Լայնություն\"\nBasic.TransformWindow.Height=\"Բարձրություն\"\nBasic.TransformWindow.Alignment=\"Դիրքի հավասարեցում\"\nBasic.TransformWindow.BoundsType=\"Սահմանափակիչի տեսակը\"\nBasic.TransformWindow.BoundsAlignment=\"Հավասարեցում սահմանաչափի ներսում\"\nBasic.TransformWindow.Bounds=\"Սահմանափակիչ չափը\"\nBasic.TransformWindow.BoundsWidth=\"Սահմանափակիչ լայնությունը\"\nBasic.TransformWindow.BoundsHeight=\"Սահմանափակիչ բարձրությունը\"\nBasic.TransformWindow.Crop=\"Կտրել\"\nBasic.TransformWindow.CropLeft=\"Կտրել ձախ\"\nBasic.TransformWindow.CropRight=\"Կտրել աջ\"\nBasic.TransformWindow.CropTop=\"Կտրել վերև\"\nBasic.TransformWindow.CropBottom=\"Կտրել ներքև\"\nBasic.TransformWindow.Alignment.TopLeft=\"Վերևի ձախ կողմում\"\nBasic.TransformWindow.Alignment.TopCenter=\"Վերին կենտրոն\"\nBasic.TransformWindow.Alignment.TopRight=\"Վերևի աջ\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Ձախ կենտրոն\"\nBasic.TransformWindow.Alignment.Center=\"Կենտրոն\"\nBasic.TransformWindow.Alignment.CenterRight=\"Աջ կենտրոն\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Ներքևի ձախ\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Ներքևի կենտրոն\"\nBasic.TransformWindow.Alignment.BottomRight=\"Ներքևի աջ\"\nBasic.TransformWindow.BoundsType.None=\"Առանց սահմանների\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Առավելագույն չափը\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Սանդղակ դեպի ներքին սահման\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Սանդղակ դեպի արտաքին սահման\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Սանդղակ սահմանի լայնությամբ\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Սանդղակ սահմանի բարձրության վրա\"\nBasic.TransformWindow.BoundsType.Stretch=\"Ձգվել դեպի սահման\"\nBasic.TransformWindow.Title=\"Փոխել փոխակերպումը «%1»-ի համար\"\nBasic.TransformWindow.NoSelectedSource=\"Աղբյուրը ընտրված չէ\"\nBasic.Main.AddSourceHelp.Title=\"Հնարավոր չէ ավելացնել աղբյուրը\"\nBasic.Main.AddSourceHelp.Text=\"Աղբյուր ավելացնելու համար դուք պետք է ստեղծեք առնվազն մեկ տեսարան:\"\nBasic.Main.Scenes=\"Տեսարաններ\"\nBasic.Main.Sources=\"Աղբյուրներ\"\nBasic.Main.Source=\"Աղբյուր\"\nBasic.Main.Controls=\"Կառավարում\"\nBasic.Main.PreparingStream=\"Նախապատրաստում․․․\"\nBasic.Main.Connecting=\"Միացում...\"\nBasic.Main.StartRecording=\"Սկսել տեսագրել\"\nBasic.Main.StartReplayBuffer=\"Սկսել կրկնությունը\"\nBasic.Main.SaveReplay=\"Պահպանել կրկնությունը\"\nBasic.Main.StartStreaming=\"Սկսել հեռարձակումը:\"\nBasic.Main.StartBroadcast=\"Սկսել Ուղիղ եթեր\"\nBasic.Main.StartVirtualCam=\"Գործարկել վիրտուալ տեսախցիկը\"\nBasic.Main.StopRecording=\"Ավարտել տեսագրել\"\nBasic.Main.PauseRecording=\"Դադարեցրել տեսագրել\"\nBasic.Main.UnpauseRecording=\"Վերսկսել տեսագրությունը\"\nBasic.Main.SplitFile=\"Պառակտել տեսագրման ֆայլը\"\nBasic.Main.StoppingRecording=\"Ավարտել տեսագրումը...\"\nBasic.Main.StopReplayBuffer=\"Դադարեցրել կրկնություն\"\nBasic.Main.StoppingReplayBuffer=\"Ավարտել կրկնություն...\"\nBasic.Main.SetupBroadcast=\"Կառավարել հեռարձակումը\"\nBasic.Main.StopStreaming=\"Դադարեցնել հեռարձակումը\"\nBasic.Main.StopBroadcast=\"Ավարտել հեռարձակումը\"\nBasic.Main.AutoStopEnabled=\"(Ավտոմատ կանգառ)\"\nBasic.Main.StoppingStreaming=\"Դադարեցնել հեռարձակումը...\"\nBasic.Main.ForceStopStreaming=\"Դադարեցնել հեռարձակումը (վերակայման ուշացում)\"\nBasic.Main.ShowContextBar=\"Ցույց տալ աղբյուրի գործիքագոտին\"\nBasic.Main.HideContextBar=\"Թաքցնել աղբյուրի գործիքագոտին\"\nBasic.Main.StopVirtualCam=\"Դադարեցրել վիրտուալ տեսախցիկը\"\nBasic.Main.Group=\"Խումբ %1\"\nBasic.Main.GroupItems=\"Խմբավորել ընտրված տարրերը\"\nBasic.Main.Ungroup=\"Ապախմբավորել\"\nBasic.Main.GridMode=\"Ցանցային ռեժիմ\"\nBasic.Main.ListMode=\"Ցուցակի ռեժիմ\"\nBasic.Main.VirtualCamConfig=\"Կարգավորել վիրտուալ տեսախցիկը\"\nBasic.VCam.VirtualCamera=\"Վիրտուալ տեսախցիկ\"\nBasic.VCam.OutputType=\"Ելքի տիպը\"\nBasic.VCam.OutputSelection=\"Ելքի ընտրություն\"\nBasic.VCam.OutputType.Program=\"Ծրագիրը (Լռելյայն)\"\nBasic.VCam.OutputSelection.NoSelection=\"Այս ելքի տեսակի համար ընտրություն չկա\"\nBasic.VCam.RestartWarning=\"Այս փոփոխությունը կիրառելու համար վիրտուալ տեսախցիկը կվերագործարկվի\"\nBasic.MainMenu.File=\"Ֆայլ (&F)\"\nBasic.MainMenu.File.Export=\"Արտահանել (&E)\"\nBasic.MainMenu.File.Import=\"Ներմուծել (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Ցույց տալ գրառումները (&R)\"\nBasic.MainMenu.File.Remux=\"Ռեմուլտիպլեքսիաի տեսագրություններ (&M)\"\nBasic.MainMenu.File.Settings=\"Կարգավորումներ (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Բացել կարգավորումների թղթապանակը\"\nBasic.MainMenu.File.ShowProfileFolder=\"Բացել պրոֆիլի թղթապանակը\"\nBasic.MainMenu.File.ShowMissingFiles=\"Ստուգել բացակայող ֆայլերը\"\nBasic.MainMenu.File.Exit=\"Ելք (&X)\"\nBasic.MainMenu.Edit=\"Խմբագրել (&E)\"\nBasic.MainMenu.Edit.Undo=\"Չեղարկել (&U)\"\nBasic.MainMenu.Edit.Redo=\"Կրկնել (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Արգելափակել նախադիտումը (&L)\"\nBasic.MainMenu.Edit.Scale=\"Նախադիտման մասշտաբավորում (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"Պատուհանի սանդղակ\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Կտավ (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Արդյունք (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Մասշտաբ (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"Փոխակերպել (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Փոխել էկրանը... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Պատճենել վերափոխումը\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Տեղադրել վերափոխում\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Վերականգնել վերափոխումը (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Պտտել 90 աստիճան ժամացույցի սլաքի ուղղությամբ\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Պտտել 90 աստիճանով ժամացույցի սլաքի ուղղությամբ\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Պտտել 180 աստիճանով\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Շրջել հորիզոնական\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Շրջել ուղղահայաց (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Հարմարվել էկրանին (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Ձգվել ամբողջ էկրանին (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Տեղադրեք էկրանի կենտրոնում (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Հավասարեցնել ուղղահայաց\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Հավասարեցրել հորիզոնական\"\nBasic.MainMenu.Edit.Order=\"Պատվեր (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Շարժվել վերև (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Շարժվել ներքև (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Բարձրանալ (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Իջեցնել (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Ընդլայնված աուդիո հատկություններ (&A)\"\nBasic.MainMenu.View=\"Դիտել (&V)\"\nBasic.MainMenu.View.Toolbars=\"Գործիքադարակ (&T)\"\nBasic.MainMenu.View.ContextBar=\"Աղբյուրի գործիքագոտին\"\nBasic.MainMenu.View.SourceIcons=\"Աղբյուրի պատկերակները (&I)\"\nBasic.MainMenu.View.StatusBar=\"Կարգավիճակի բար (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Ամբողջ էկրանով\"\nBasic.MainMenu.View.ResetUI=\"&Վերականգնել ինտերֆեյսը\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Միշտ առջևում\"\nBasic.MainMenu.Docks=\"&Դոկի վահանակներ\"\nBasic.MainMenu.Docks.ResetDocks=\"&Վերակայել նավահանգիստները (Docks)\"\nBasic.MainMenu.Docks.LockDocks=\"&կողպեք Docks\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Անհատականացվող զննարկիչի դոկերը...\"\nBasic.MainMenu.SceneCollection=\"Տեսարանի հավաքածու (&S)\"\nBasic.MainMenu.Profile=\"Պրոֆիլ (&P)\"\nBasic.MainMenu.Profile.Import=\"Ներմուծել պրոֆիլը\"\nBasic.MainMenu.Profile.Export=\"Արտահանել պրոֆիլը\"\nBasic.MainMenu.SceneCollection.Import=\"Ներմուծել տեսարանների հավաքածու\"\nBasic.MainMenu.SceneCollection.Export=\"Արտահանման տեսարանների հավաքածու\"\nBasic.MainMenu.Profile.Exists=\"Պրոֆիլն արդեն գոյություն ունի\"\nBasic.MainMenu.Tools=\"Գործիքներ (&T)\"\nBasic.MainMenu.Help=\"Օգնություն (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Օգնության պորտալ (&P)\"\nBasic.MainMenu.Help.Website=\"Այցելել կայք (&W)\"\nBasic.MainMenu.Help.Discord=\"Մտնել &Discord սերվեր\"\nBasic.MainMenu.Help.WhatsNew=\"Ինչն է նոր\"\nBasic.MainMenu.Help.Logs=\"Մատյան ֆայլեր (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Ցուցադրել մատյան ֆայլերը (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Ներբեռնել ընթացիկ գրանցամատյանի ֆայլը (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Ուղարկել նախորդ գրանցամատյանի ֆայլը (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Բացել ընթացիկ մատյանը (&V)\"\nBasic.MainMenu.Help.CheckForUpdates=\"Ստուգել թարմացումները\"\nBasic.MainMenu.Help.Repair=\"Ֆայլի ամբողջականության ստուգում\"\nBasic.MainMenu.Help.RestartSafeMode=\"Վերագործարկեք անվտանգ ռեժիմում\"\nBasic.MainMenu.Help.RestartNormal=\"Վերագործարկեք նորմալ ռեժիմում\"\nBasic.MainMenu.Help.CrashLogs=\"Սխալների մասին հաղորդել (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Ցույց տալ սխալների մասին հաշվետվությունները (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Ուղարկել սխալի նախորդ զեկույցը (&P)\"\nBasic.MainMenu.Help.About=\"Ծրագրի մասին (&A)\"\nBasic.Settings.ProgramRestart=\"Այս կարգավորումները փոխելու համար հարկավոր է վերագործարկել ծրագիրը:\"\nBasic.Settings.ConfirmTitle=\"Փոփոխությունների հաստատում\"\nBasic.Settings.Confirm=\"Դուք չպահված փոփոխություններ ունեք: Պահե՞լ դրանք:\"\nBasic.Settings.General=\"Ընդհանուր\"\nBasic.Settings.General.Language=\"Լեզու\"\nBasic.Settings.General.Updater=\"Թարմացրեք\"\nBasic.Settings.General.UpdateChannel=\"Արդիական Ալիք\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Անջատված է)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Կանխադրված)\"\nBasic.Settings.General.EnableAutoUpdates=\"Ստուգել մեկնարկի թարմացումները\"\nBasic.Settings.General.OpenStatsOnStartup=\"Բացել վիճակագրության պատուհանը գործարկման ժամանակ\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Թաքցնել OBS պատուհանները էկրանի նկարահանումից\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Այս պարամետրը թաքցնում է OBS Studio-ի բոլոր ոչ պրոյեկտորային պատուհանները OBS-ի կողմից գրավելուց, \\nազդում է նաև այլ տեսագրման ծրագրերի վրա, որոնք օգտագործվում են կոնֆերանսի զանգերի, աշխատասեղանի համօգտագործման, հեռակառավարման, էկրանակադրերի համար և այլն:\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Այս պարամետրը միացնելը կթաքցնի OBS Studio-ի բոլոր ոչ պրոյեկտորային պատուհանները OBS-ի կողմից գրավելուց: այն կազդի նաև այլ տեսագրման ծրագրերի վրա, որոնք օգտագործվում են կոնֆերանսի զանգերի, աշխատասեղանի փոխանակման, հեռակառավարման, էկրանակադրերի համար և այլն:\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Ցույց տալ հաստատման պատուհանը հեռարձակումը սկսելիս\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Ցույց տալ հաստատման պատուհանը, երբ հեռարձակումը դադարեցվի\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Ցույց տալ հաստատման պատուհանը, երբ տեսագրումը դադարեցվի\"\nBasic.Settings.General.Projectors=\"Պրոյեկտորներ\"\nBasic.Settings.General.HideProjectorCursor=\"Թաքցնել կուրսորը պրոյեկտորի հետևում\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Ցույց տալ պրոյեկտորները բոլոր պատուհանների վերևում\"\nBasic.Settings.General.Snapping=\"Աղբյուրի տեղադրության կապը\"\nBasic.Settings.General.ScreenSnapping=\"Կտտացրել էկրանի եզրին\"\nBasic.Settings.General.CenterSnapping=\"Կտտացրել կենտրոնը հորիզոնական և ուղղահայաց\"\nBasic.Settings.General.SourceSnapping=\"Կապը այլ աղբյուրների հետ\"\nBasic.Settings.General.SnapDistance=\"Կապանքների զգայունություն\"\nBasic.Settings.General.SpacingHelpers=\"Ցույց տալ պիքսելների հավասարեցման ուղեցույցները\"\nBasic.Settings.General.RecordWhenStreaming=\"Ավտոմատ կերպով սկսել տեսագրությունը հեռարձակման ընթացքում\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Հեռարձակման ավարտից հետո շարունակեք տեսագրումը\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Հեռարձակման ընթացքում ավտոմատ կերպով միացրեք վերարտադրման բուֆերը\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Թարգմանության ավարտից հետո ակտիվ պահեք վերարտադրման բուֆերը\"\nBasic.Settings.General.SysTray=\"Համակարգի սկուտեղ\"\nBasic.Settings.General.SysTrayWhenStarted=\"Սկսելիս թաքցնել պատուհանը դեպի համակարգային սկուտեղ\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Միշտ նվազագույնի հասցնել սկուտեղին՝ առաջադրանքների տողի փոխարեն\"\nBasic.Settings.General.SaveProjectors=\"Պահպանել պրոյեկտորները ելքի ժամանակ\"\nBasic.Settings.General.CloseExistingProjectors=\"Սահմանափակեք մեկ ամբողջական էկրանով պրոյեկտոր մեկ էկրանին\"\nBasic.Settings.General.Preview=\"Նախադիտում\"\nBasic.Settings.General.OverflowHidden=\"Թաքցնել վարարումը\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Հորդառատությունը միշտ տեսանելի է\"\nBasic.Settings.General.OverflowSelectionHidden=\"Ցույց տալ արտահոսքը նույնիսկ երբ աղբյուրն անտեսանելի է:\"\nBasic.Settings.General.Importers=\"Ներմուծողներ\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Ներմուծման ժամանակ տեսարանների հավաքածուների հայտնի վայրերի որոնում\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Անցնել տեսարան կրկնակի սեղմումով\"\nBasic.Settings.General.StudioPortraitLayout=\"Միացնել դիմանկարը/ուղղահայաց դասավորությունը\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Ցույց տալ «Նախադիտում» և «Ծրագիր» նշանները\"\nBasic.Settings.General.Multiview=\"Բազմակի դիտում\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Փոխել տեսարանները մկնիկի սեղմումով\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Ցույց տալ տեսարանների վերնագրերը\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Ցույց տալ անվտանգության գոտիները (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Բազմաֆունկցիոնալ տեղադրություն\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Հորիզոնական, վերև (8 տեսարան)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Հորիզոնական, ներքևի (8 տեսարան)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Ուղղահայաց, ձախ (8 տեսարան)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Ուղղահայաց, աջ (8 տեսարան)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Հորիզոնական, վերև (18 տեսարան)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Հորիզոնական, վերև (24 տեսարան)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Միայն տեսարաններ (4 տեսարան)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Միայն տեսարաններ (9 տեսարան)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Միայն տեսարաններ (16 տեսարան)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Միայն տեսարաններ (25 տեսարան)\"\nBasic.Settings.General.ChannelName.stable=\"Կայուն\"\nBasic.Settings.General.ChannelDescription.stable=\"Վերջին կայուն թողարկումը\"\nBasic.Settings.General.ChannelName.beta=\"Բետա / Թողարկվողները\"\nBasic.Settings.General.ChannelDescription.beta=\"Պոտենցիալ ոչ կայուն նախաթողարկվղ տարբերակները\"\nBasic.Settings.Appearance=\"Տեսք\"\nBasic.Settings.Appearance.General=\"Ընդհանուր\"\nBasic.Settings.Appearance.General.Theme=\"Թեմա\"\nBasic.Settings.Appearance.General.Variant=\"Ոճ\"\nBasic.Settings.Stream=\"Հեռարձակում\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Օգտագործե; թույլտվությունը\"\nBasic.Settings.Stream.Custom.Username=\"Օգտանուն\"\nBasic.Settings.Stream.Custom.Password=\"Գաղտնաբառ\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST․ մուտքագրեք srp_username։\\nRTMP․ մուտքագրեք մուտքանունը։\\nSRT․ չի օգտագործվում։\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST․ մուտքագրեք srp_password։\\nRTMP․ մուտքագրեք գաղտնաբառը։\\nSRT․ մուտքագրեք ծածկագրված գաղտնաբառը։\"\nBasic.Settings.Stream.BandwidthTestMode=\"Միացնել թողունակության փորձարկման ռեժիմը\"\nBasic.Settings.Stream.TTVAddon=\"Twitch զրուցարան հավելումներ\"\nBasic.Settings.Stream.TTVAddon.None=\"Ոչ մի\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV ու FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Հեռարձակումը կազմաձևված չէ\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Բացել կարգավորումները\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL-ը և հոսքի բանալին բացակայում են:\\n\\nԲացեք կարգավորումները՝ URL-ը և հոսքի բանալին «Հեռարձակում» ներդիրում մուտքագրելու համար:\"\nBasic.Settings.Stream.MissingUrl=\"Հեռարձակման URL-ը բացակայում է:\\n\\nԲացեք կարգավորումները՝ «Հեռարձակում» ներդիրում URL մուտքագրելու համար:\"\nBasic.Settings.Stream.MissingStreamKey=\"Հեռարձակման բանալին բացակայում է:\\n\\nԲացեք կարգավորումները՝ «Հեռարձակում» ներդիրում «Հոսքի բանալին» մուտքագրելու համար:\"\nBasic.Settings.Stream.IgnoreRecommended=\"Մի հետևեք հոսքային ծառայության կողմից սահմանված սահմանափակումներին\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Չեղարկել առաջարկվող կարգավորումները\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Առաջարկվող արժեքներից դուրս արժեքներ օգտագործելը կարող է բացասաբար ազդել հոսքի որակի և/կամ կայունության վրա:\\n\\nՑանկանու՞մ եք շարունակել:\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Տեսանյութի բիթրեյթի առավելագույնը: %1 կբ/վ\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Աուդիոի բիթրեյթի առավելագույնը: %1 կբ/վ\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Առավելագույն թույլտվություն: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Առավելագույն ԿՄՎ: %1\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Միացնել %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Ինքնաշխատ\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Ինքնաշխատ\"\nBasic.Settings.Output=\"Արդյունք\"\nBasic.Settings.Output.Format=\"Տեսագրման ձևաչափ\"\nBasic.Settings.Output.Encoder.Video=\"Տեսանյութի կոդավորիչ\"\nBasic.Settings.Output.Encoder.Audio=\"Աուդիո կոդավորիչ\"\nBasic.Settings.Output.SelectDirectory=\"Ընտրել տեսագրման գրացուցակը\"\nBasic.Settings.Output.DynamicBitrate=\"Դինամիկ փոխել բիթրեյթի՝ ցանցի գերբեռնվածությունը կառավարելու համար\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Դինամիկ փոխեք բիթերի արագությունը՝ ցանցի գերբեռնվածությունը կառավարելու համար (բետա)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Կադրերն իջեցնելու փոխարեն կօգտագործվի արագընթաց բիթրեյթ դինամիկ փոփոխություն՝ ցանցի գերբեռնվածությունը նվազեցնելու համար:\\n\\nԽնդրում ենք նկատի ունենալ, որ դա կարող է մեծացնել հեռուստադիտողների հապաղումը ալիքի հանկարծակի խցանման դեպքում:\\nԲիթրեյթը նվազեցնելուց հետո, վերականգնման համար կարող է տևել մի քանի րոպե:\\n\\nՆերկայումս աջակցվում է միայն RTMP-ի համար:\"\nBasic.Settings.Output.Mode=\"Արդյունքի ռեժիմ\"\nBasic.Settings.Output.Mode.Simple=\"Պարզ\"\nBasic.Settings.Output.Mode.Adv=\"Երկարացված\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg արդյունք\"\nBasic.Settings.Output.UseReplayBuffer=\"Միացնել կրկնվող բուֆերը\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Ննջելու առավելագույն տևողությունը\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Հիշողության գնահատված օգտագործում: %1 ՄԲ\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Ուշադրություն․ Հիշողության գնահատված չափը %1 ՄԲ է և մեծ է քան խորհուրդ է տրվում %2 ՄԲ-ից։\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Հիշողության սպառման կոպիտ գնահատում հնարավոր չէ: Խնդրում ենք ձեռքով սահմանել հիշողության օգտագործման սահմանաչափը:\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Կրկնել Ֆայլի անվան նախածանցը\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Վերջածանց\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Replay Buffer-ը չի կարող օգտագործվել, երբ ձայնագրման տեսակը սահմանված է Այլ ելք (FFmpeg):\"\nBasic.Settings.Output.Simple.SavePath=\"Տեսագրման ուղին\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Տեսագրման որակը\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Նույնը, ինչ հեռարձակումը\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Բարձր որակ, միջին չափի ֆայլ\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Անտարբեր որակ, մեծ ֆայլի չափ\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Որակի կորուստ չկա, ֆայլի չափազանց մեծ չափս\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Զգուշացում. Տեսահոսքի բիթային արագությունը կսահմանվի %1, որը ընթացիկ հոսքային ծառայության վերին սահմանն է:\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Զգուշացում. Աուդիո հոսքի բիթրեյթը կսահմանվի %1, որը ընթացիկ հոսքային ծառայության վերին սահմանն է:\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Զգուշացում․ ձայնագրությունները չեն կարող դադարեցվել, եթե ձայնագրման կոդավորիչը դրված է «(Օգտագործել հոսքի կոդավորիչ)» արժեքը։\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Զգուշացում: Հեռարձակման որակից տարբերվող ծրագրային կոդավորիչով ձայնագրելու համար կպահանջվի լրացուցիչ պրոցեսորի օգտագործում՝ միաժամանակ ձայնագրման և հեռարձակման դեպքում:\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Իսկապե՞ս ուզում եք տեսագրել առանց որակի կորստի:\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Անկորուստ որակի նախազգուշացում:\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Ծրագրային ապահովում (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Սարքավորում (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Սարքավորում (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Սարքավորում (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Սարքավորումներ (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Սարքավորում (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Սարքավորում (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Սարքավորում (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Սարքավորում (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Սարքավորում (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Սարքավորում (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Ծրագրային ապահովում (x264 ցածր պրոցեսորի օգտագործմամբ, մեծացնում է ֆայլի չափը)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Լռելյայն)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD-ի արահետը (օգտագործում է 2-րդ ուղին)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Ձայնանյութ\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Անհամատեղելի լուծում կամ կադրի արագություն\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Այս հոսքային ծառայությունը չի աջակցում ձեր ընթացիկ ելքային լուծաչափը և/կամ կադրերի արագությունը: Դրանք կփոխվեն ամենամոտ համատեղելի արժեքով.\\n\\n%1\\n\\nՑանկանու՞մ եք շարունակել:\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Թույլտվություն: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"ԿՄՎ: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Անհամատեղելի կոդավորիչ\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"«%1» հոսքային ծառայությունը չի աջակցում «%2» կոդավորիչը: Կոդավորիչը կփոխվի «%3»-ի:\\n\\nՑանկանու՞մ եք շարունակել:\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"%1 հոսքային ծառայությունը չի աջակցում %2 և %3 կոդավորիչը: Կոդավորիչը կփոխվի %4 և %5-ի:\\n\\nՑանկանու՞մ եք շարունակել:\"\nBasic.Settings.Output.VideoBitrate=\"Վիդիո բիթրեյթ\"\nBasic.Settings.Output.AudioBitrate=\"Աուդիո բիթրեյթ\"\nBasic.Settings.Output.Reconnect=\"Ավտոմատ վերամիացում\"\nBasic.Settings.Output.RetryDelay=\"Հետաձգման կրկնություն\"\nBasic.Settings.Output.MaxRetries=\"Առավելագույն կրկնվող փորձեր\"\nBasic.Settings.Output.Advanced=\"Միացնել հատուկ կոդավորիչի կարգավորումները (Ընդլայնված)\"\nBasic.Settings.Output.EncoderPreset=\"Կոդավորիչը նախադրված է\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (ցածր պրոցեսորի օգտագործում, ամենացածր որակ)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (Ի սկզբանե)(միջին պրոցեսորի օգտագործում, կանխադրված որակ)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (բարձր պրոցեսորի օգտագործում, բարձր որակ)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Անհատականացված կոդավորման կարգավորումներ\"\nBasic.Settings.Output.CustomMuxerSettings=\"Մուլտիպլեքսոր օգտագործողի կարգավորումները\"\nBasic.Settings.Output.NoSpaceFileName=\"Ստեղծել ֆայլի անունը առանց բացատ\"\nBasic.Settings.Output.Adv.Rescale=\"Վերամասշտաբ արդյունք\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Անջատված է\"\nBasic.Settings.Output.Adv.AudioTrack=\"Ձայնային ուղի\"\nBasic.Settings.Output.Adv.Streaming=\"Հեռարձակում\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Հեռարձակման կարգավորումներ\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Ուղի 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Ուղի 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Ուղի 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Ուղի 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Ուղի 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Ուղի 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch-ում VOD-ի ուղին\"\nBasic.Settings.Output.Adv.Encoder=\"Կոդավորիչի կարգավորումներ\"\nBasic.Settings.Output.Adv.Recording=\"Տեսագրությունը\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Տեսագրման կարգավորումներ\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Տեսագրման տեսակը\"\nBasic.Settings.Output.Adv.Recording.Type=\"Տեսակը\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Սովորական\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Հաճախորդի արդյունք (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Օգտագործել հոսքի կոդավորիչը)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Ֆայլի անվան ձևաչափ\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Փոխարինել, եթե ֆայլն արդեն գոյություն ունի\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg արդյունքի տեսակը\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Նշված հասցեով (URL)\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Արդյունք դեպի Ֆայլ\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Տեսագրման ընդհանուր ձևաչափեր\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Բոլոր ֆայլերը\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Ֆայլի ուղի կամ URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Կոնտեյների ձևաչափ\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Աուդիո\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Վիդեո\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Կանխադրված ձևաչափ\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Կոնտեյների ձևաչափի նկարագրությունը\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Աուդիո և վիդեո կոդեկները կկռահվեն URL-ով կամ ֆայլի ճանապարհով\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Կանխադրված կոդավորիչ\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Անջատել կոդավորիչը\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Տեսանյութի կոդավորիչ\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Տեսանյութի կոդավորման կարգավորումներ (եթե այդպիսիք կան)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Աուդիո կոդավորիչ\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Աուդիո կոդավորման կարգավորումներ (եթե այդպիսիք կան)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Մուլտիպլեքսորի կարգավորումներ (եթե այդպիսիք կան)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Հիմնական շրջանակների միջակայք (կադրեր)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Ցույց տալ բոլոր կոդեկները (ներառյալ հնարավոր անհամատեղելիները)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg Կարգավորումներ\"\nBasic.Settings.Output.EnableSplitFile=\"Ֆայլերի ավտոմատ բաժանում\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Բաժանվել ժամանակով\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Բաժանել ըստ չափի\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Առանձնացնել միայն ձեռքով\"\nBasic.Settings.Output.SplitFile.Time=\"Ժամանակի բաժանում\"\nBasic.Settings.Output.SplitFile.Size=\"Պառակտման չափը\"\nScreenshot=\"էկրանակադրը անել արդյունքը\"\nScreenshot.SourceHotkey=\"Էկրանակադրը անել այս աղբյուրը\"\nScreenshot.StudioProgram=\"Էկրանակադր անել (Ծրագիր)\"\nScreenshot.Preview=\"Էկրանակադր անել (Նախադիտում)\"\nScreenshot.Scene=\"Էկրանակադր անել (տեսարաններ)\"\nScreenshot.Source=\"Էկրանակադր անել (աղբյուր)\"\nFilenameFormatting.TT.CCYY=\"Տարի, չորս նիշ\"\nFilenameFormatting.TT.YY=\"Տարի, վերջին երկու նիշը (00-99)\"\nFilenameFormatting.TT.MM=\"Ամիսը որպես թիվ (01-12)\"\nFilenameFormatting.TT.DD=\"Ամսաթիվը, զրոյով (01-31)\"\nFilenameFormatting.TT.hh=\"24 ժամային ձևաչափ (00-23)\"\nFilenameFormatting.TT.mm=\"Րոպե (00-59)\"\nFilenameFormatting.TT.ss=\"Վայրկյան (00-59)\"\nFilenameFormatting.TT.Percent=\"% նշան\"\nFilenameFormatting.TT.a=\"Շաբաթվա օրվա կրճատ անվանումը\"\nFilenameFormatting.TT.A=\"Շաբաթվա օրվա լրիվ անվանումը\"\nFilenameFormatting.TT.b=\"Ամսվա կրճատ անվանունը\"\nFilenameFormatting.TT.B=\"Ամսվա ամբողջ անվանումը\"\nFilenameFormatting.TT.H=\"24 ժամային ձևաչափ (00-23)\"\nFilenameFormatting.TT.I=\"12 ժամային ձևաչափ (01-12)\"\nFilenameFormatting.TT.m=\"Ամիսը որպես թիվ (01-12)\"\nFilenameFormatting.TT.M=\"Րոպե (00-59)\"\nFilenameFormatting.TT.p=\"AM կամ PM\"\nFilenameFormatting.TT.S=\"Վայրկյան (00-59)\"\nFilenameFormatting.TT.y=\"Տարի, վերջին երկու նիշը (00-99)\"\nFilenameFormatting.TT.Y=\"Տարի\"\nFilenameFormatting.TT.z=\"ISO 8601-ի տեղաշարժը UTC-ից ժամային գոտում\"\nFilenameFormatting.TT.Z=\"Ժամային գոտու անվանումը կամ հապավումը\"\nFilenameFormatting.TT.FPS=\"Կադր մեկ վայրկյանում\"\nFilenameFormatting.TT.CRES=\"Հիմնական լուծաչափը (կտավ)\"\nFilenameFormatting.TT.ORES=\"Ելքային (մասշտաբավորված) լուծաչափ\"\nFilenameFormatting.TT.VF=\"Տեսանյութի ձևաչափը\"\nBasic.Settings.Video=\"Տեսանյութ\"\nBasic.Settings.Video.Adapter=\"Վիդեո ադապտեր\"\nBasic.Settings.Video.BaseResolution=\"Հիմնական լուծում (կտավ)\"\nBasic.Settings.Video.ScaledResolution=\"Արդյունք (սանդղակ) լուծում\"\nBasic.Settings.Video.DownscaleFilter=\"Մեծացնելու զտիչ\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Թույլտվությունները համընկնում են, մասշտաբի նվազեցում չի պահանջվում]\"\nBasic.Settings.Video.FPS=\"ԿՄՎ\"\nBasic.Settings.Video.FPSCommon=\"Ընդհանուր ԿՄՎ արժեքներ\"\nBasic.Settings.Video.FPSInteger=\"Ամբողջական ԿՄՎ\"\nBasic.Settings.Video.FPSFraction=\"Կոտորակային ԿՄՎ\"\nBasic.Settings.Video.Numerator=\"Համարիչ\"\nBasic.Settings.Video.Denominator=\"Հայտարար\"\nBasic.Settings.Video.Renderer=\"Ռենդեր\"\nBasic.Settings.Video.InvalidResolution=\"Անվավեր թույլտվություն։ Պետք է լինի [Լայնություն]x[Բարձրություն] (օրինակ՝ 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Տեսանյութի ելքը ներկայումս ակտիվ է: Խնդրում ենք անջատել բոլոր ելքերը՝ տեսանյութի կարգավորումները փոխելու համար:\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Երկգծային (ամենաարագ, բայց մեծացնելու դեպքում մշուշոտ)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Երկխորանարդ (նուրբ մասշտաբով, 16 նմուշ)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Լանցոսի մեթոդ (նուրբ մասշտաբավորում, 36 նմուշ)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Տարածք (կշռված գումար, 4/6/9 նմուշներ)\"\nBasic.Settings.Audio=\"Աուդիո\"\nBasic.Settings.Audio.SampleRate=\"Նմուշառման հաճախականությունը\"\nBasic.Settings.Audio.Channels=\"Ալիքներ\"\nBasic.Settings.Audio.Meters=\"Հաշվիչներ\"\nBasic.Settings.Audio.MeterDecayRate=\"Նվազման տոկոսադրույքը\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Արագ\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Միջին (PPM տեսակ I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Դանդաղ (PPM տեսակ II)\"\nBasic.Settings.Audio.PeakMeterType=\"Պիկ հաշվիչի տեսակը\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Պարզեցված\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Ճշգրիտ (ԿՄՀ-ի օգտագործման ավելացում)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ՈՒՇԱԴՐՈՒԹՅՈՒՆ՝ շրջապատող ձայնը միացված է:\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Միացնե՞լ շրջապատող ձայնը:\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Ցանկանու՞մ եք միացնել շրջապատող ձայնը:\"\nBasic.Settings.Audio.Devices=\"Համաշխարհային աուդիո սարքեր\"\nBasic.Settings.Audio.DesktopDevice=\"Աշխատասեղանի աուդիո\"\nBasic.Settings.Audio.DesktopDevice2=\"Աշխատասեղանի աուդիո 2\"\nBasic.Settings.Audio.AuxDevice=\"Խոսափող/Լրացուցիչ աուդիո\"\nBasic.Settings.Audio.AuxDevice2=\"Խոսափող/Լրացուցիչ աուդիո 2\"\nBasic.Settings.Audio.AuxDevice3=\"Խոսափող/Լրացուցիչ աուդիո 3\"\nBasic.Settings.Audio.AuxDevice4=\"Խոսափող/Լրացուցիչ աուդիո 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Անջատել խոսափողը սեղմելով\"\nBasic.Settings.Audio.PushToMuteDelay=\"Անջատել խոսափողի հետաձգումը\"\nBasic.Settings.Audio.EnablePushToTalk=\"Միացրեք խոսափողը սեղմելով\"\nBasic.Settings.Audio.PushToTalkDelay=\"Խոսափողի միացման ուշացում\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Սարքը միացված չէ կամ հասանելի չէ]\"\nBasic.Settings.Audio.Disabled=\"Անջատված է\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Ցածր հետաձգման տեսագրման բուֆերային ռեժիմ (Decklink/NDI ելքերի համար)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ՈՒՇԱԴՐՈՒԹՅՈՒՆ: Ցածր հետաձգման ձայնային բուֆերավորումը միացված է:\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Աուդիո բուֆերացման ցածր ուշացման ռեժիմը կարող է աղավաղում կամ կակազություն առաջացնել որոշ աղբյուրներից:\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Միացնե՞լ ցածր ուշացման ձայնի բուֆերացման ռեժիմը:\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Իսկապե՞ս ուզում եք միացնել ցածր ուշացման ձայնային բուֆերավորումը:\"\nBasic.Settings.Accessibility=\"Կարողություն\"\nBasic.Settings.Accessibility.ColorOverrides=\"Օգտագործել այլ գույներ\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Աղբյուրի սահմանը (ընտրություն)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Աղբյուրի եզրագիծ (կտրում)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Աղբյուրի սահմանը (բարձրացում)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Խառնիչի ձայնի ուժգնության գոտի (-60-ից -20 դԲ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Խառնիչի ձայնի ուժգնության գոտի (-20-ից -9 դԲ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Խառնիչի ձայնի ուժգնության գոտի (-9-ից 0դԲ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Խառնիչի ձայնի ուժգնության գոտի (-60-ից -20դԲ) (ակտիվացում)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Խառնիչի ձայնի ուժգնության գոտի (-20-ից -9դԲ) (ակտիվացում)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Խառնիչի ձայնի ուժգնության գոտի (-9-ից 0դԲ) (ակտիվացում)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Գույնի նախադրված\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Կանխադրված\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Հաճախորդ\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Մյուսները գունավոր կուրության համար\"\nBasic.Settings.Advanced=\"Երկարացված\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Ցույց տալ նախազգուշացում ակտիվ դուրսբերումների մասին ելքի ժամանակ\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Գործընթացի առաջնահերթություն\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Բարձր\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Միջինից բարձր\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Միջին\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Միջինից ցածր\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Ցածր\"\nBasic.Settings.Advanced.FormatWarning=\"Զգուշացում. NV12/P010-ից տարբեր գունային ձևաչափերը հիմնականում նախատեսված են ձայնագրման համար և խորհուրդ չեն տրվում հեռարձակման ժամանակ: Հոսքային հոսքը կարող է առաջացնել պրոցեսորի ավելացված օգտագործում՝ գունային ձևաչափի փոխակերպման պատճառով:\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Զգուշացում. բարձր ճշգրտության ձևաչափերն ավելի հաճախ օգտագործվում են HDR գունային տարածքներում:\"\nBasic.Settings.Advanced.FormatWarning2100=\"Զգուշացում՝ Միջ. 2100-ը պետք է օգտագործի ավելի ճշգրիտ ձևաչափ:\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Գունավոր ձևաչափ\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 բիթ, 4:2:0, 2 մակարդակ)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 բիթ, 4:2:0, 3 մակարդակ)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 բիթ, 4:4:4, 3 մակարդակ)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 բիթ, 4:2:0, 2 մակարդակ)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 բիթ, 4:2:0, 3 մակարդակ)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-բիթ, 4:2:2, 2 մակարդակ)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-բիթ, 4:4:4, 2 մակարդակ)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-բիթ)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Գունային տարածություն\"\nBasic.Settings.Advanced.Video.ColorRange=\"Գույնի տիրույթ\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Սահմանափակ\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Ամբողջ\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR սպիտակ մակարդակ\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Անվանական HDR գագաթնակետային մակարդակ\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Լսող սարք\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Կանխադրված\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Անջատեք Windows-ի ձայների խլացումը\"\nBasic.Settings.Advanced.StreamDelay=\"Հոսքի հետաձգում\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Տևողությունը\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Պահպանել անջատման կետը (ավելացնել հետաձգումը) վերամիացման ժամանակ\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Հիշողության գնահատված օգտագործումը՝ %1 ՄԲ\"\nBasic.Settings.Advanced.Network=\"Ցանց\"\nBasic.Settings.Advanced.Network.Disabled=\"Ներկայիս ընտրված հոսքային արձանագրությունը չի աջակցում ցանցի կարգավորումները փոխելը:\"\nBasic.Settings.Advanced.Network.BindToIP=\"Կապել IP-ին\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP ընտանիք\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Միացնել ցանցի օպտիմիզացումը\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Միացնել TCP ռիթմը\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Փորձում է նվազեցնել բացասական ազդեցությունը այլ հավելվածների վրա՝ օգտագործելով RTMP ելքը՝ նվազեցնելով բիթերի արագությունը:\\nԿարող է մեծացնել անկայուն միացումների դեպքում շրջանակի անկման վտանգը:\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Թեժ ստեղի կենտրոնացման վարքագիծ\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Երբեք մի անջատեք թեժ ստեղները\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Անջատել թեժ ստեղները, երբ հիմնական պատուհանը կենտրոնացված է\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Անջատել թեժ ստեղները, երբ հիմնական պատուհանը կենտրոնացված չէ\"\nBasic.Settings.Advanced.AutoRemux=\"Ավտոմատ կերպով ռեմուքս %1-ին\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(տեսագրել որպես mkv)\"\nBasic.AdvAudio=\"Ընդլայնված աուդիո հատկություններ\"\nBasic.AdvAudio.ActiveOnly=\"Միայն ակտիվ աղբյուրներ\"\nBasic.AdvAudio.Name=\"Անվանումը\"\nBasic.AdvAudio.Volume=\"Ձայնի ուժգնություն\"\nBasic.AdvAudio.VolumeSource=\"Ձայնի ուժգունությունը «%1»-ի համար\"\nBasic.AdvAudio.Mono=\"Մոնո\"\nBasic.AdvAudio.MonoSource=\"Միքս «%1»-ի համար\"\nBasic.AdvAudio.Balance=\"Հաշվեկշիռ\"\nBasic.AdvAudio.BalanceSource=\"«%1»-ի մնացորդը\"\nBasic.AdvAudio.SyncOffset=\"Ժամկետային օֆսեթ\"\nBasic.AdvAudio.SyncOffsetSource=\"Համաժամացնել օֆսեթ «%1»-ի համար\"\nBasic.AdvAudio.Monitoring=\"Աուդիո լսում\"\nBasic.AdvAudio.Monitoring.None=\"Անջատել լսումը\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Միայն լսել (անջատել ելքը)\"\nBasic.AdvAudio.Monitoring.Both=\"Լսում և արդյունք\"\nBasic.AdvAudio.MonitoringSource=\"Ձայնային մոնիտորինգ «%1»-ի համար\"\nBasic.AdvAudio.AudioTracks=\"Բաշխման գոտի\"\nBasic.Settings.Hotkeys=\"Թեժ ստեղներ\"\nBasic.Settings.Hotkeys.Pair=\"Ստեղնաշարի դյուրանցումները «%1»-ի հետ միասին գործում են որպես անջատիչներ\"\nBasic.Settings.Hotkeys.Filter=\"Զտիչ\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Զտել թեժ ստեղնով\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Այս թեժ ստեղնը օգտագործվում է մեկ կամ մի քանի այլ գործողություններով, սեղմեք՝ կոնֆլիկտները ցուցադրելու համար\"\nBasic.Settings.Hotkeys.PleaseWait=\"Դյուրանցումների բեռնում, սպասեք․․․\"\nBasic.Hotkeys.SelectScene=\"Անցնել բեմ\"\nBasic.SystemTray.Show=\"Ցույց տալ\"\nBasic.SystemTray.Hide=\"Թաքցնել\"\nBasic.SystemTray.Message.Reconnecting=\"Կապը կորել է: Փորձում է նորից միանալ...\"\nHotkeys.Insert=\"Տեղադրել\"\nHotkeys.Delete=\"Ջնջել\"\nHotkeys.Home=\"Գլխավոր էջ\"\nHotkeys.End=\"Մինչեւ վերջ\"\nHotkeys.PageUp=\"Նախորդ\"\nHotkeys.PageDown=\"Հաջորդը\"\nHotkeys.Left=\"Սլաքը դեպի ձախ\"\nHotkeys.Right=\"Սլաքը դեպի աջ\"\nHotkeys.Up=\"Սլաքը դեպի վեր\"\nHotkeys.Down=\"Սլաքը դեպի ներքև\"\nHotkeys.Menu=\"Մենյու\"\nHotkeys.Space=\"Բացատ\"\nHotkeys.NumpadNum=\"%1 (Թվային ստեղնաշար)\"\nHotkeys.NumpadMultiply=\"* (Թվային ստեղնաշար)\"\nHotkeys.NumpadDivide=\"/ (Թվային ստեղնաշար)\"\nHotkeys.NumpadAdd=\"+ (Թվային ստեղնաշար)\"\nHotkeys.NumpadSubtract=\"- (Թվային ստեղնաշար)\"\nHotkeys.NumpadDecimal=\". (Թվային ստեղնաշար)\"\nHotkeys.MouseButton=\"Մկնիկ %1\"\nMute=\"Անձայն\"\nUnmute=\"Միաձայն\"\nPush-to-mute=\"Անջատել սեղմումով\"\nPush-to-talk=\"Միացրել ձայնը սեղմելով\"\nSceneItemShow=\"Ցույց տալ '%1'\"\nSceneItemHide=\"Թաքցնել '%1'\"\nOutputWarnings.NoTracksSelected=\"Դուք պետք է ընտրեք առնվազն մեկ աուդիո կատարում\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Արդյունքի պարամետրերի սխալը\"\nOutputWarnings.MP4Recording=\"Ուշադրություն. MP4/MOV-ում պահպանված ձայնագրությունները անընթեռնելի կլինեն, եթե ֆայլը ավարտված չէ (օրինակ՝ BSOD, հոսանքի կորստի և այլնի պատճառով): Եթե ​​ցանկանում եք մի քանի աուդիո հետքեր ձայնագրել, մտածեք MKV-ի օգտագործման մասին, այնուհետև ձայնագրման ավարտից հետո նորից միացնել MP4/MOV-ին (Ֆայլ → Ռեմուքսի տեսագրում)\"\nOutputWarnings.CannotPause=\"Զգուշացում․ ձայնագրությունները չեն կարող դադարեցվել, եթե «Ձայնագրման կոդավորիչը» դրված է «(Օգտագործել հոսքի կոդավորիչը)»\"\nCodecCompat.Incompatible=\"(Անհամատեղելի է %1-ի հետ)\"\nCodecCompat.CodecPlaceholder=\"Ընտրել կոդավորիչը․..\"\nCodecCompat.ContainerPlaceholder=\"Ընտրել ձևաչափը...\"\nCodecCompat.CodecMissingOnExit.Title=\"Կոդավորիչը ընտրված չէ\"\nCodecCompat.ContainerMissingOnExit.Title=\"Ձևաչափը ընտրված չէ\"\nFinalScene.Title=\"Ջնջել տեսարանը\"\nFinalScene.Text=\"Դուք չեք կարող ջնջել վերջին տեսարանը:\"\nNoSources.Title=\"Աղբյուրներ չկան\"\nNoSources.Text=\"Կարծես թե դու դեռ ոչ մի աղբյուր չես ավելացրել։ Դուք կցուցադրեք միայն դատարկ էկրան: Վստա՞հ եք, որ ուզում եք սա:\"\nNoSources.Text.AddSource=\"Դուք կարող եք ավելացնել աղբյուրներ՝ ցանկացած ժամանակ սեղմելով հիմնական պատուհանի Աղբյուրների տուփի տակ գտնվող + պատկերակը:\"\nNoSources.Label=\"Դուք աղբյուրներ չունեք:\\nՍեղմեք ներքևում գտնվող + կոճակը,\\nև աջ սեղմեք այստեղ՝ այն ավելացնելու համար:\"\nChangeBG=\"Սահմանել գույնը\"\nCustomColor=\"Հաճախորդի գույն\"\nBrowserSource.EnableHardwareAcceleration=\"Միացնել ապարատային արագացումը «Բրաուզերի» աղբյուրի համար\"\nAbout=\"Մեր մասին\"\nAbout.Info=\"OBS Studio-ն անվճար և բաց կոդով վիդեո ձայնագրման և հոսքային ծրագիր է:\"\nAbout.Donate=\"Կատարել ավանդ\"\nAbout.GetInvolved=\"Մասնակցել\"\nAbout.Authors=\"Հեղինակները\"\nAbout.License=\"Լիցենզիա\"\nAbout.Contribute=\"Աջակցել OBS նախագծին\"\nAddUrl.Title=\"Ավելացրեք աղբյուրը URL-ի միջոցով\"\nAddUrl.Text=\"Դուք URL եք քաշել OBS-ում: Սա ավտոմատ կերպով կավելացնի այդ հղումը որպես աղբյուր: Շարունակե՞լ\"\nAddUrl.Text.Url=\"URL-հասցե: %1\"\nResizeOutputSizeOfSource=\"Չափափոխել ելքը (աղբյուրի չափը)\"\nResizeOutputSizeOfSource.Text=\"Բազային և ելքային չափերը կչափափոխվեն ընթացիկ աղբյուրի չափին:\"\nResizeOutputSizeOfSource.Continue=\"Ցանկանու՞մ եք շարունակել։\"\nPreviewTransition=\"Անցումային նախադիտում\"\nImporter=\"Տեսարանների հավաքածուի ներմուծող\"\nImporter.SelectCollection=\"Ընտրեք տեսարանների հավաքածու\"\nImporter.Collection=\"Տեսարանների հավաքածու\"\nImporter.HelpText=\"Ավելացրեք ֆայլեր այս պատուհանում՝ OBS-ից կամ այլ աջակցվող ծրագրերից հավաքածուներ ներմուծելու համար:\"\nImporter.Path=\"Հավաքածուի ուղի\"\nImporter.Program=\"Հայտնաբերված հավելված\"\nImporter.AutomaticCollectionPrompt=\"Տեսարանների հավաքածուների ավտոմատ որոնում\"\nImporter.AutomaticCollectionText=\"OBS-ը կարող է ավտոմատ կերպով հայտնաբերել տեսարանների ներմուծված հավաքածուները աջակցվող երրորդ կողմի ծրագրերից: Ցանկանու՞մ եք, որ OBS-ն ավտոմատ կերպով գտնի ձեզ համար հավաքածուներ:\\n\\nԴուք կարող եք դա փոխել ավելի ուշ՝ անցնելով Կարգավորումներ > Ընդհանուր > Ներմուծիչներ:\"\nRestart=\"Վերսկսել\"\nNeedsRestart=\"OBS Studio-ն պետք է վերագործարկվի: Ցանկանու՞մ եք վերագործարկել հիմա:\"\nLoadProfileNeedsRestart=\"Պրոֆիլը պարունակում է կարգավորումներ, որոնք պահանջում են OBS-ի վերագործարկում.\\n%1\\n\\nՑանկանու՞մ եք վերագործարկել OBS-ը, որպեսզի այս կարգավորումներն ուժի մեջ մտնեն:\"\nContextBar.NoSelectedSource=\"Աղբյուրը ընտրված չէ\"\nContextBar.MediaControls.PlayMedia=\"Նվագարկել մեդիան\"\nContextBar.MediaControls.PauseMedia=\"Դադարեցնել մեդիան\"\nContextBar.MediaControls.StopMedia=\"Ավարտել մեդիան\"\nContextBar.MediaControls.RestartMedia=\"Վերագործարկել մեդիան\"\nContextBar.MediaControls.PlaylistNext=\"Հաջորդը տեսացանկում\"\nContextBar.MediaControls.PlaylistPrevious=\"Նախորդը տեսացանկում\"\nContextBar.MediaControls.BlindSeek=\"Մեդիա հետադարձ վիջեթ\"\nYouTube.Auth.Ok=\"Թույլտվությունը հաջողությամբ ավարտվեց:\\nԱյժմ կարող եք փակել այս էջը:\"\nYouTube.Auth.NoCode=\"Թույլտվության գործընթացը չի ավարտվել։\"\nYouTube.Auth.NoChannels=\"Ընտրված հաշվում հասանելի ալիքներ չեն գտնվել\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube օգտագործողի թույլտվություն\"\nYouTube.Auth.WaitingAuth.Text=\"Խնդրում ենք լրացնել թույլտվությունը արտաքին դիտարկիչում:<br>Եթե արտաքին զննարկիչը չի բացվում, խնդրում ենք հետևել այս հղմանը և լրացնել թույլտվությունը.<br>%1\"\nYouTube.AuthError.Text=\"Չհաջողվեց ստանալ ալիքի մասին տեղեկություն՝ %1:\"\nYouTube.Actions.WindowTitle=\"YouTube հեռարձակման կարգավորում - Ալիք՝ %1\"\nYouTube.Actions.CreateNewEvent=\"Ստեղծեք նոր հեռարձակում\"\nYouTube.Actions.ChooseEvent=\"Ընտրել գոյություն ունեցող հեռարձակում\"\nYouTube.Actions.Title=\"Անվանումը*\"\nYouTube.Actions.MyBroadcast=\"Իմ հեռարձակումը\"\nYouTube.Actions.Description=\"Նկարագրություն\"\nYouTube.Actions.Privacy=\"Գաղտնիություն*\"\nYouTube.Actions.Privacy.Private=\"Մասնավոր\"\nYouTube.Actions.Privacy.Public=\"Տեսանելի է բոլորի համար (հանրային)\"\nYouTube.Actions.Privacy.Unlisted=\"Չգրանցված\"\nYouTube.Actions.Category=\"Կատեգորիա\"\nYouTube.Actions.Thumbnail=\"Էկրանապահիչ\"\nYouTube.Actions.Thumbnail.SelectFile=\"Ընտրել ֆայլ...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Ֆայլը ընտրված չէ\"\nYouTube.Actions.Thumbnail.ClearFile=\"Մաքրել\"\nYouTube.Actions.MadeForKids=\"Նշե՞լ հեռարձակումը որպես երեխաների համար ստեղծված:\"\nYouTube.Actions.MadeForKids.Yes=\"Այո, այն ստեղծված է երեխաների համար:\"\nYouTube.Actions.MadeForKids.No=\"Ոչ, դա երեխաների համար չէ\"\nYouTube.Actions.AdditionalSettings=\"Լրացուցիչ ընտրանքներ\"\nYouTube.Actions.Latency=\"Հետաձգում\"\nYouTube.Actions.Latency.Normal=\"Նորմալ\"\nYouTube.Actions.Latency.Low=\"Ցածր\"\nYouTube.Actions.Latency.UltraLow=\"Շատ ցածր\"\nYouTube.Actions.EnableAutoStart=\"Միացնել ավտոմատ մեկնարկը\"\nYouTube.Actions.EnableAutoStop=\"Միացնել ավտոմատ կանգառը\"\nYouTube.Actions.AutoStartStop.TT=\"Նշում է, թե արդյոք այս պլանավորված հեռարձակումը պետք է ինքնաբերաբար սկսվի\"\nYouTube.Actions.EnableDVR=\"Միացնել DVR\"\nYouTube.Actions.360Video=\"360 տեսանյութ\"\nYouTube.Actions.ScheduleForLater=\"Պլանավորել ավելի ուշ\"\nYouTube.Actions.RememberSettings=\"Հիշել այս կարգավորումները\"\nYouTube.Actions.Create_Ready=\"Ստեղծել հեռարձակում\"\nYouTube.Actions.Create_GoLive=\"Ստեղծեք հեռարձակում և սկսեք ուղիղ հեռարձակում\"\nYouTube.Actions.Choose_Ready=\"Ընտրել հեռարձակումը\"\nYouTube.Actions.Choose_GoLive=\"Ընտրել հեռարձակումը և սկսեք ուղիղ հեռարձակում\"\nYouTube.Actions.Create_Schedule=\"Պլանավորել հեռարձակում\"\nYouTube.Actions.Create_Schedule_Ready=\"Պլանավորել և ընտրեք հեռարձակում\"\nYouTube.Actions.Dashboard=\"Բացել YouTube Studio\"\nYouTube.Actions.Error.Title=\"Հեռարձակումի ստեղծման սխալ\"\nYouTube.Actions.Error.Text=\"YouTube մուտքի սխալ '%1':<br/>Սխալի մանրամասն նկարագրությունը կարող եք գտնել <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTube մուտք չկա: Խնդրում ենք ստուգել ձեր ցանցային կապը կամ YouTube սերվերի հասանելիությունը:\"\nYouTube.Actions.Error.NoBroadcastCreated=\"'%1' հեռարձակման ստեղծման սխալ:<br/>Սխալի մանրամասն նկարագրությունը կարող եք գտնել <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Չհաջողվեց ստեղծել հեռարձակում: Խնդրում ենք անջատել և նորից կապել ձեր հաշիվը:\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API-ի սխալ: Լրացուցիչ մանրամասների համար տես մատյան ֆայլը:\"\nYouTube.Actions.Error.BroadcastNotFound=\"Ընտրված հեռարձակումը չի գտնվել:\"\nYouTube.Actions.Error.FileMissing=\"Ընտրված ֆայլը գոյություն չունի:\"\nYouTube.Actions.Error.FileOpeningFailed=\"Ընտրված ֆայլը գոյություն չունի:\"\nYouTube.Actions.Error.FileTooLarge=\"Ընտրված ֆայլը չափազանց մեծ է (սահմանաչափը՝ 2 ՄԲ):\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Ուղիղ հեռարձակման անցումը ձախողվեց. %1<br/><br/>Եթե սխալը չվերանա <a href='https://studio.youtube.com/video/%2/livestreaming'>բացեք ուղիղ հեռարձակումը YouTube Studio-ում</a> և փորձեք ձեռքով:\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Հեռարձակումը տեղափոխվում է փորձնական փուլ, դա կարող է որոշ ժամանակ տևել: Խնդրում ենք կրկին փորձել 10-30 վայրկյանից:\"\nYouTube.Actions.EventsLoading=\"Միջոցառումների ցանկը բեռնվում է...\"\nYouTube.Actions.EventCreated.Title=\"Ստեղծվել է իրադարձություն\"\nYouTube.Actions.EventCreated.Text=\"Միջոցառումը հաջողությամբ ստեղծվեց:\"\nYouTube.Actions.Stream=\"Հեռարձակում\"\nYouTube.Actions.Stream.ScheduledFor=\"Պլանավորված է %1-ի համար\"\nYouTube.Actions.Stream.Resume=\"Վերսկսել ընդհատված հոսքը\"\nYouTube.Actions.Stream.YTStudio=\"Ավտոմատ ստեղծվել է YouTube Studio-ի կողմից\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Ստեղծվում է նոր հեռարձակում։ Խնդրում ենք սպասել...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Պետք է սկսել ձեռքով\"\nYouTube.Actions.AutoStartStreamingWarning=\"Ավտոմատ նվագարկումն անջատված է այս իրադարձության համար, հեռարձակումը սկսելու համար սեղմեք «Ուղիղ եթեր»:\"\nYouTube.Actions.AutoStopStreamingWarning=\"Դուք չեք կարողանա նորից միանալ:<br>Ձեր հեռարձակումը կդադարեցվի, և դուք այլևս չեք լինի ուղիղ եթերում:\"\nYouTube.Errors.liveStreamingNotEnabled=\"Ուղիղ հեռարձակումն անջատված է այս YouTube ալիքի համար:<br/><br/>Մանրամասների համար տես <a href='https://www.youtube.com/features'>youtube.com/features</a>:\"\nYouTube.Errors.livePermissionBlocked=\"Ուղիղ հեռարձակումը հասանելի չէ ընտրված YouTube ալիքում:<br/>Խնդրում ենք նկատի ունենալ, որ ուղիղ հեռարձակումը կարող է տևել մինչև 24 ժամ՝ ալիքի կարգավորումներում միացնելուց հետո:<br/><br/>Տես <a href='https://www.youtube.com/features'>youtube.com/features</a> մանրամասների համար:\"\nYouTube.Errors.errorExecutingTransition=\"Անցումը ձախողվեց հետին պլանի սխալի պատճառով: Խնդրում ենք կրկին փորձել մի քանի վայրկյանից:\"\nYouTube.Errors.errorStreamInactive=\"YouTube-ը տվյալներ չի ստանում ձեր հոսքի համար: Խնդրում ենք ստուգել ձեր կարգավորումները և նորից փորձել:\"\nYouTube.Errors.invalidTransition=\"Անցումային փորձն անվավեր էր: Դա կարող է պայմանավորված լինել մի թեմայով, որը չի ավարտել նախորդ անցումը: Խնդրում ենք սպասել մի քանի վայրկյան և կրկին փորձել:\"\n"
  },
  {
    "path": "frontend/data/locale/id-ID.ini",
    "content": "Language=\"Bahasa Indonesia\"\nOK=\"Oke\"\nApply=\"Terapkan\"\nCancel=\"Batal\"\nClose=\"Tutup\"\nSave=\"Simpan\"\nDiscard=\"Buang\"\nDisable=\"Nonaktif\"\nYes=\"Ya\"\nNo=\"Tidak\"\nAdd=\"Tambahkan\"\nRemove=\"Hapus\"\nRename=\"Ubah Nama\"\nInteract=\"Interaksi\"\nFilters=\"Filter\"\nProperties=\"Properti\"\nMoveUp=\"Pindah Ke Atas\"\nMoveDown=\"Pindah Ke Bawah\"\nSettings=\"Pengaturan\"\nDisplay=\"Layar\"\nName=\"Nama\"\nExit=\"Keluar\"\nMixer=\"Mixer Audio\"\nBrowse=\"Telusuri\"\nDroppedFrames=\"Frame Anjlok %1 (%2%)\"\nProjector.Open.Program=\"Buka Proyektor Program\"\nProjector.Open.Preview=\"Buka Proyektor Pratinjau\"\nProjector.Open.Scene=\"Buka Proyektor Adegan\"\nProjector.Open.Source=\"Buka Proyektor Sumber\"\nProjector.Open.Multiview=\"Buka Tampilan Ganda\"\nProjector.Display=\"Layar: %1\"\nProjector.Window=\"Jendela baru\"\nProjector.Title=\"Proyektor\"\nProjector.Title.Scene=\"Adegan: %1\"\nProjector.Title.Source=\"Sumber: %1\"\nProjector.Title.Multiview=\"Tampilan Ganda\"\nProjector.ResizeWindowToContent=\"Sesuaikan jendela ke konten\"\nClear=\"Bersihkan\"\nRevert=\"Pulihkan\"\nShow=\"Tampilkan\"\nHide=\"Sembunyikan\"\nUnhideAll=\"Tampilkan Semua\"\nUntitled=\"Tanpa Judul\"\nNew=\"Baru\"\nDuplicate=\"Duplikat\"\nEnable=\"Aktifkan\"\nDisableOSXVSync=\"Nonaktifkan macOS V-Sync\"\nResetOSXVSyncOnExit=\"Setel ulang macOS V-Sync saat Keluar\"\nHighResourceUsage=\"Pengkodean kelebihan beban! Pertimbangkan untuk menurunkan pengaturan video atau gunakan preset pengkodean yang lebih cepat.\"\nTransition=\"Transisi\"\nQuickTransitions=\"Transisi Cepat\"\nFadeToBlack=\"Pudar Hitam\"\nLeft=\"Kiri\"\nRight=\"Kanan\"\nTop=\"Atas\"\nBottom=\"Bawah\"\nReset=\"Atur Ulang\"\nHours=\"Jam\"\nMinutes=\"Menit\"\nSeconds=\"Detik\"\nDeprecated=\"Usang\"\nReplayBuffer=\"Buffer Replay\"\nImport=\"Impor\"\nExport=\"Ekspor\"\nCopy=\"Salin\"\nPaste=\"Tempel\"\nPasteReference=\"Tempel (Referensi)\"\nPasteDuplicate=\"Tempel (Duplikat)\"\nRemuxRecordings=\"Remux Rekaman\"\nNext=\"Berikutnya\"\nBack=\"Kembali\"\nDefaults=\"Bawaan\"\nRestoreDefaults=\"Bawaan\"\nHideMixer=\"Sembunyikan di Mixer\"\nTransitionOverride=\"Menimpa Transisi\"\nShowTransition=\"Tampilkan Transisi\"\nHideTransition=\"Sembunyikan Transisi\"\nNone=\"Tidak ada\"\nStudioMode.Preview=\"Pratinjau\"\nStudioMode.PreviewSceneName=\"Pratinjau: %1\"\nShowInMultiview=\"Tampilkan Berganda\"\nVerticalLayout=\"Tata Letak Vertikal\"\nGroup=\"Grup\"\nDoNotShowAgain=\"Jangan tampilkan lagi\"\nDefault=\"(Bawaan)\"\nCalculating=\"Menghitung...\"\nFullscreen=\"Layar Penuh\"\nWindowed=\"Berjendela\"\nRefreshBrowser=\"Muat Ulang\"\nAspectRatio=\"Rasio Aspek <b>%1:%2</b>\"\nLockVolume=\"Kunci Volume\"\nLogViewer=\"Penampil Catatan\"\nShowOnStartup=\"Tampilkan saat memulai\"\nOpenFile=\"Buka berkas\"\nAddScene=\"Tambahkan Adegan\"\nAddSource=\"Tambahkan Sumber\"\nRemoveScene=\"Hapus Adegan yang Dipilih\"\nRemoveSource=\"Hapus Adegan-Adegan yang Dipilih\"\nMoveSceneUp=\"Pindah Adegan Ke Atas\"\nMoveSceneDown=\"Pindah Adegan Ke Bawah\"\nMoveSourceUp=\"Pindah Adegan-Adegan Ke Atas\"\nMoveSourceDown=\"Pindah Adegan-Adegan Ke Bawah\"\nSourceProperties=\"Buka Properti Adegan\"\nSourceFilters=\"Buka Properti Filter\"\nMixerToolbarMenu=\"Menu Mixer Audio\"\nSceneFilters=\"Buka Filter Adegan\"\nList=\"Daftar\"\nGrid=\"Kisi-kisi\"\nAutomatic=\"Otomatis\"\nComingSoon=\"Segera Hadir\"\nPluginsFailedToLoad.Title=\"Kesalahan Memuat Plugin\"\nPluginsFailedToLoad.Text=\"Plugin OBS berikut ini gagal untuk memuat:\\n\\n%1\\nMohon perbarui atau hapus plugin tersebut.\"\nAlreadyRunning.Title=\"OBS sudah berjalan\"\nAlreadyRunning.Text=\"OBS sudah berjalan! Kecuali jika Anda bermaksud melakukan ini, harap matikan semua instansi-instansi OBS yang ada sebelum mencoba menjalankan instansi baru. Jika Anda memiliki OBS yang diatur untuk di minimalkan ke baki sistem, mohon diperiksa apakah OBS masih berjalan atau tidak.\"\nAlreadyRunning.LaunchAnyway=\"Tetap Luncurkan\"\nCrashHandling.Dialog.Title=\"Galat OBS Studio Terdeteksi\"\nCrashHandling.Labels.Text=\"OBS Studio tidak dinonaktifkan dengan benar.\\n\\nJalankan dalam Mode Aman (plugin pihak ketiga, skriping, dan WebSockets dinonaktifkan)?\"\nCrashHandling.Labels.PrivacyNotice=\"Anda juga bisa memilih untuk mengunggah otomatis laporan galat terkini ke OBSProject.<br /><br />Mohon membaca <a href='https://obsproject.com/privacy-policy'>Kebijakan Privasi</a> sebelum mengunggah berkas apapun dan perhatikan secara seksama khusus pada bagian yang terkait dengan pengunggahan berkas.\"\nCrashHandling.Checkbox.SendReport=\"Saya sudah membaca dan menyetujui kebijakan privasinya untuk mengunggah. \"\nCrashHandling.Buttons.LaunchSafe=\"Jalankan dalam Mode Aman\"\nCrashHandling.Buttons.LaunchNormal=\"Jalankan dalam Mode Biasa\"\nCrashHandling.Errors.UploadJSONError=\"Sebuah galat terjadi saat mencoba mengunggah laporan galat terkini. Mohon coba lagi nanti.\"\nCrashHandling.Errors.Title=\"Galat Unggah Laporan Galat\"\nSafeMode.Restart=\"Apakah Anda ingin mengaktifkan ulang OBS dalam Mode Aman (plugin pihak ketiga, skriping, dan WebSockets dinonaktifkan)?\"\nSafeMode.RestartNormal=\"Apakah Anda ingin mengaktifkan ulang OBS dalam Mode Biasa?\"\nChromeOS.Title=\"Platform Tidak Didukung\"\nChromeOS.Text=\"Tampaknya OBS berjalan di dalam kontainer ChromeOS. Platform ini tidak didukung.\"\nWine.Title=\"Terdeteksi Wine\"\nWine.Text=\"Menjalankan OBS di Wine sedang tidak didukung, dan fitur lain seperti menangkap layar atau sumber perangkat tidak akan bisa atau terbatas.<br><br>Kami anjurkan untuk menjalankan OBS yang versi natif saja, contohnya <a href='https://flathub.org/apps/details/com.obsproject.Studio'>versi Flatpak kami</a> atau untuk sistem operasi Anda.\"\nDockCloseWarning.Title=\"Menutup Jendela yang Bisa di Dok\"\nDockCloseWarning.Text=\"Anda baru saja menutup jendela yang bisa di-dok. Jika Anda ingin menampilkannya lagi, gunakan menu Dok pada bar menu.\"\nExtraBrowsers=\"Penyesuaian Dok Peramban\"\nExtraBrowsers.Info=\"Tambahkan dok dengan memberi nama dan tautan URL, lalu klik Terapkan atau Tutup untuk membuka dok. Anda dapat menambahkan atau menghapus dok kapan saja.\"\nExtraBrowsers.DockName=\"Nama dok\"\nAuth.Authing.Title=\"Mengautentikasi...\"\nAuth.Authing.Text=\"Mengautentikasi dengan %1, silakan tunggu...\"\nAuth.AuthFailure.Title=\"Autentikasi Gagal\"\nAuth.AuthFailure.Text=\"Gagal mengautentikasi dengan %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Autentikasi Diperlukan\"\nAuth.InvalidScope.Text=\"Persyaratan autentikasi untuk %1 telah berubah. Beberapa fitur mungkin tidak tersedia.\"\nAuth.LoadingChannel.Title=\"Memuat informasi saluran...\"\nAuth.LoadingChannel.Text=\"Memuat informasi saluran untuk %1, silakan tunggu...\"\nAuth.LoadingChannel.Error=\"Tidak dapat mendapatkan informasi saluran.\"\nAuth.ChannelFailure.Title=\"Gagal memuat saluran\"\nAuth.ChannelFailure.Text=\"Gagal memuat informasi saluran untuk %1\\n\\n%2: %3\"\nAuth.Chat=\"Obrolan\"\nAuth.StreamInfo=\"Informasi Streaming\"\nTwitchAuth.Stats=\"Statistik Twitch\"\nTwitchAuth.Feed=\"Umpan Aktivitas Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Tidak bisa meminta kunci stream\"\nTwitchAuth.TwoFactorFail.Text=\"OBS tidak dapat terhubung ke akun Twitch Anda. Mohon pastikan autentikasi dua faktor diatur di <a href='https://www.twitch.tv/settings/security'>Twitch security settings</a> Anda karena ini diperlukan untuk streaming.\"\nRestreamAuth.Channels=\"Saluran Restream\"\nCopy.Filters=\"Salin Filter\"\nPaste.Filters=\"Tempel Filter\"\nBrowserPanelInit.Title=\"Menginisialisasi Peramban...\"\nBrowserPanelInit.Text=\"Menginisialisasi peramban, mohon tunggu...\"\nBandwidthTest.Region=\"Wilayah\"\nBandwidthTest.Region.US=\"Amerika Serikat\"\nBandwidthTest.Region.EU=\"Eropa\"\nBandwidthTest.Region.Other=\"Lainnya\"\nBasic.AutoConfig=\"Wisaya Konfigurasi Otomatis\"\nBasic.AutoConfig.ApplySettings=\"Terapkan Pengaturan\"\nBasic.AutoConfig.StartPage=\"Informasi Penggunaan\"\nBasic.AutoConfig.StartPage.SubTitle=\"Tentukan untuk apa Anda ingin menggunakan program ini\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Dioptimalkan untuk streaming, rekaman adalah opsi kedua\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Dioptimalkan hanya untuk merekam, saya tidak akan streaming\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Saya hanya akan menggunakan kamera virtual\"\nBasic.AutoConfig.VideoPage=\"Pengaturan Video\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Tentukan pengaturan video yang ingin Anda gunakan\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Gunakan Saat Ini (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Layar %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Gunakan Saat Ini (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Antara 60 atau 30, tetapi lebih memilih 60 jika memungkinkan\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Antara 60 atau 30, tetapi lebih memilih beresolusi tinggi\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Catatan: Resolusi kanvas (dasar) tidak harus sama dengan resolusi yang akan Anda stream atau rekam. Resolusi streaming/rekaman Anda yang sebenarnya mungkin dapat diturunkan dari resolusi kanvas untuk mengurangi penggunaan sumber daya atau bitrate yang diperlukan.\"\nBasic.AutoConfig.StreamPage=\"Informasi Streaming\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Silakan masukkan informasi streaming Anda\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Hubungkan Akun (disarankan)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Putuskan Akun\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Putuskan Akun?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Perubahan ini akan langsung diterapkan. Apakah Anda yakin ingin memutuskan koneksi akun Anda?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Dapatkan Kunci Stream\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Info lebih lanjut\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Gunakan Kunci Stream\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Gunakan Kunci Stream (lanjutan)\"\nBasic.AutoConfig.StreamPage.Service=\"Layanan\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Tampilkan Semua...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Sesuaikan...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Kunci Stream\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: masukkan frasa sandi enkripsi.\\nRTMP: masukkan kunci yang disediakan layanan.\\nSRT: masukkan streamid jika layanan menggunakannya.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kunci Pengkodean\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Akun yang terhubung\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Perkirakan bitrate dengan uji bandwidth (mungkin perlu beberapa menit)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Lebih memilih pengkodean perangkat keras\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Pengkodean Perangkat Keras mengurangi sebagian besar penggunaan CPU, namun mungkin memerlukan bitrate lebih besar untuk mendapatkan tingkat kualitas yang sama.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Peringatan streaming\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Pengujian bandwidth akan melakukan streaming data video acak tanpa audio ke saluran Anda. Jika bisa, disarankan untuk menonaktifkan sementara menyimpan video stream dan atur streaming ke privasi pribadi hingga tes selesai. Lanjutkan?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Uji %1\"\nBasic.AutoConfig.TestPage=\"Hasil Akhir\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Sekarang program menjalankan serangkaian pengujian untuk memperkirakan pengaturan yang paling ideal\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Pengujian selesai\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Melakukan pengujian bandwidth, ini mungkin memerlukan waktu beberapa menit...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Tidak ada output untuk protokol layanan ini yang ditemukan\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Menghubungkan ke: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Gagal terhubung ke server mana pun, periksa koneksi internet Anda, lalu coba lagi.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Menguji bandwidth untuk: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Menguji pengkodean streaming, ini mungkin memerlukan waktu satu menit...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Menguji pengkodean rekaman, ini mungkin memerlukan waktu satu menit...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Gagal memulai pengkodean\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Menguji %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Pengkodean Streaming\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Pengkodean Rekaman\"\nBasic.AutoConfig.TestPage.Result.Header=\"Program telah menentukan bahwa pengaturan berikut ini adalah yang paling ideal untuk Anda:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Untuk menggunakan pengaturan ini, klik Terapkan Pengaturan. Untuk mengonfigurasi ulang wisaya dan mencobanya lagi, klik Kembali. Untuk mengonfigurasi sendiri pengaturan secara manual, klik Batal dan buka Pengaturan.\"\nBasic.AutoConfig.Info=\"Wisaya konfigurasi otomatis akan menentukan pengaturan terbaik berdasarkan spesifikasi komputer dan kecepatan internet Anda.\"\nBasic.AutoConfig.RunAnytime=\"Ini dapat dijalankan kapan saja dengan membuka menu Alat.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Resolusi Streaming (Diskalakan)\"\nBasic.Stats=\"Statistik\"\nBasic.Stats.CPUUsage=\"Penggunaan CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Ketersediaan ruang simpan\"\nBasic.Stats.MemoryUsage=\"Penggunaan Memori\"\nBasic.Stats.AverageTimeToRender=\"Waktu rata-rata untuk me-render frame\"\nBasic.Stats.SkippedFrames=\"Frame yang terlewat karena kelambatan pengkodean\"\nBasic.Stats.MissedFrames=\"Frame yang hilang karena kelambatan rendering\"\nBasic.Stats.Output.Stream=\"Streaming\"\nBasic.Stats.Output.Recording=\"Rekaman\"\nBasic.Stats.Status.Recording=\"Merekam\"\nBasic.Stats.Status.Live=\"LANGSUNG\"\nBasic.Stats.Status.Reconnecting=\"Menghubungkan kembali\"\nBasic.Stats.Status.Inactive=\"Tidak aktif\"\nBasic.Stats.Status.Active=\"Aktif\"\nBasic.Stats.DroppedFrames=\"Frame Ter-drop (Jaringan)\"\nBasic.Stats.MegabytesSent=\"Total Output Data\"\nBasic.Stats.DiskFullIn=\"Penyimpanan penuh dalam (kira-kira)\"\nBasic.Stats.DiskFullIn.Text=\"%1 Jam, %2 Menit\"\nBasic.Stats.ResetStats=\"Atur Ulang Statistik\"\nResetUIWarning.Title=\"Apakah Anda yakin ingin mengatur ulang UI?\"\nResetUIWarning.Text=\"Mengatur ulang UI akan menyembunyikan dok tambahan. Anda perlu menampilkan kembali dok ini dari menu Dok jika Anda menginginkannya terlihat.\\n\\nApakah Anda yakin ingin mengatur ulang UI?\"\nUpdater.Title=\"Pembaruan terbaru tersedia\"\nUpdater.Text=\"Ada pembaruan terbaru yang tersedia:\"\nUpdater.UpdateNow=\"Perbarui sekarang\"\nUpdater.RemindMeLater=\"Ingatkan saya nanti\"\nUpdater.Skip=\"Lewati Versi\"\nUpdater.NoUpdatesAvailable.Title=\"Pembaruan belum tersedia\"\nUpdater.NoUpdatesAvailable.Text=\"Saat ini pembaruan belum tersedia\"\nUpdater.BranchNotFound.Title=\"Pembaruan Saluran Dihapus\"\nUpdater.BranchNotFound.Text=\"Saluran pembaruan yang Anda pilih tidak lagi tersedia, OBS telah mengatur nya kembali ke bawaan nya.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Pengecekan Integritas Tidak Tersedia\"\nUpdater.RepairButUpdatesAvailable.Text=\"Pengecekan integritas berkas hanya bisa dilakukan jika ada versi terbaru tersedia. Gunakan Bantuan → Periksa Pembaruan untuk memverifikasi dan memperbarui pemasangan OBS Anda.\"\nUpdater.RepairConfirm.Title=\"Konfirmasi Pengecekan Integritas\"\nUpdater.RepairConfirm.Text=\"Memulai pengecekan integritas akan memindai pemasangan OBS Anda jika ada yang korup dan mengunduh kembali berkas yang rusak/diubah. Ini membutuhkan waktu beberapa saat.\\n\\nAnda yakin ingin lanjut?\"\nUpdater.FailedToLaunch=\"Gagal menjalankan pembaru\"\nQuickTransitions.SwapScenes=\"Tukar Pratinjau/Program Adegan Setelah Transisi\"\nQuickTransitions.SwapScenesTT=\"Tukar pratinjau dan program adegan setelah transisi (jika program adegan asli masih ada).\\nIni tidak akan dapat membatalkan perubahan apa pun yang mungkin telah dilakukan pada program adegan asli.\"\nQuickTransitions.DuplicateScene=\"Duplikat Adegan\"\nQuickTransitions.DuplicateSceneTT=\"Saat mengubah adegan yang sama, Anda dapat mengubah transformasi/visibilitas sumber tanpa memodifikasikan output program.\\nUntuk mengubah properti sumber tanpa memodifikasi output program, aktifkan 'Duplikat Sumber'.\\nMengubah pilihan ini akan mengatur ulang program adegan saat ini (jika masih ada).\"\nQuickTransitions.EditProperties=\"Duplikat Sumber\"\nQuickTransitions.EditPropertiesTT=\"Saat mengubah adegan yang sama, Anda dapat mengubah properti sumber tanpa memodifikasikan output program.\\nIni hanya dapat digunakan jika 'Duplikat Adegan' diaktifkan.\\nSumber-sumber tertentu (seperti sumber tangkapan atau media) tidak mendukung ini dan tidak dapat di ubah secara terpisah.\\nMengubah nilai ini akan mengatur ulang program adegan saat ini (jika masih ada).\\n\\nPeringatan: Karena sumber akan di duplikat, ini mungkin memerlukan sumber daya sistem atau video tambahan.\"\nQuickTransitions.HotkeyName=\"Transisi Cepat: %1\"\nBasic.AddTransition=\"Tambahkan Transisi yang Bisa Diatur\"\nBasic.RemoveTransition=\"Hapus Transisi yang Bisa Diatur\"\nBasic.TransitionProperties=\"Properti Transisi\"\nBasic.SceneTransitions=\"Transisi Adegan\"\nBasic.TransitionDuration=\"Durasi\"\nBasic.TogglePreviewProgramMode=\"Mode Studio\"\nBasic.EnablePreviewProgramMode=\"Aktifkan Mode Studio\"\nBasic.DisablePreviewProgramMode=\"Nonaktifkan Mode Studio\"\nUndo.Undo=\"Mundur\"\nUndo.Redo=\"Ulangi\"\nUndo.Add=\"Tambah '%1'\"\nUndo.Delete=\"Hapus '%1'\"\nUndo.Rename=\"Ubah Nama '%1'\"\nUndo.SceneCollection.Switch=\"Beralih ke '%1'\"\nUndo.Item.Undo=\"Mundur %1\"\nUndo.Item.Redo=\"Ulangi %1\"\nUndo.Sources.Multi=\"Hapus %1 Sumber\"\nUndo.Filters=\"Perubahan Filter pada '%1'\"\nUndo.Filters.Paste.Single=\"Tempel Filter '%1' ke '%2'\"\nUndo.Filters.Paste.Multiple=\"Salin Filter dari '%1' ke '%2'\"\nUndo.Transform=\"Transformasi sumber Di '%1'\"\nUndo.Transform.Paste=\"Tempel Transformasi di '%1'\"\nUndo.Transform.Rotate=\"Rotasi Di '%1'\"\nUndo.Transform.Reset=\"Atur Ulang Transformasi Di '%1'\"\nUndo.Transform.HFlip=\"Berbalik Horizontal Di '%1'\"\nUndo.Transform.VFlip=\"Berbalik Vertikal Di '%1'\"\nUndo.Transform.FitToScreen=\"Pas dengan Layar Di '%1'\"\nUndo.Transform.StretchToScreen=\"Regangkan ke Layar Di '%1'\"\nUndo.Transform.Center=\"Tengahkan ke Layar di '%1'\"\nUndo.Transform.VCenter=\"Tengah Vertikal ke Layar di '%1'\"\nUndo.Transform.HCenter=\"Tengah Horizontal ke Layar di '%1'\"\nUndo.Volume.Change=\"Perubahan Volume pada '%1'\"\nUndo.Volume.Mute=\"Senyapkan suara '%1'\"\nUndo.Volume.Unmute=\"Hidupkan suara '%1'\"\nUndo.Balance.Change=\"Perubahan Keseimbangan Audio pada '%1'\"\nUndo.SyncOffset.Change=\"Perubahan Offset Sinkronisasi Audio pada '%1'\"\nUndo.MonitoringType.Change=\"Ubah Pemantauan Audio pada '%1'\"\nUndo.Mixers.Change=\"Ubah Mixer Audio pada '%1'\"\nUndo.ForceMono.On=\"Aktifkan Paksa suara Mono pada '%1'\"\nUndo.ForceMono.Off=\"Nonaktifkan Paksa suara Mono pada '%1'\"\nUndo.Properties=\"Perubahan Properti pada '%1'\"\nUndo.Scene.Duplicate=\"Duplikat Adegan '%1'\"\nUndo.ShowTransition=\"Tampilkan Transisi pada '%1'\"\nUndo.HideTransition=\"Sembunyikan Transisi pada '%1'\"\nUndo.ShowSceneItem=\"Tampilkan '%1' di '%2'\"\nUndo.HideSceneItem=\"Sembunyikan '%1' di '%2'\"\nUndo.ReorderSources=\"Susun ulang posisi sumber di '%1'\"\nUndo.MoveUp=\"Pindahkan '%1' ke atas di '%2'\"\nUndo.MoveDown=\"Pindahkan '%1' ke bawah di '%2'\"\nUndo.MoveToTop=\"Pindahkan '%1' ke paling atas di '%2'\"\nUndo.MoveToBottom=\"Pindahkan '%1' ke paling bawah di '%2'\"\nUndo.PasteSource=\"Tempel Sumber di '%1'\"\nUndo.PasteSourceRef=\"Tempel Referensi Sumber di '%1'\"\nUndo.GroupItems=\"Kelompokkan Item ke dalam '%1'\"\nTransitionNameDlg.Text=\"Silakan masukkan nama transisi\"\nTransitionNameDlg.Title=\"Nama Transisi\"\nTitleBar.SafeMode=\"MODE AMAN\"\nTitleBar.PortableMode=\"Mode Portabel\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Adegan\"\nNameExists.Title=\"Nama sudah ada\"\nNameExists.Text=\"Nama tersebut sudah digunakan.\"\nNoNameEntered.Title=\"Masukkan nama yang sah\"\nNoNameEntered.Text=\"Anda tidak dapat menggunakan nama yang kosong.\"\nConfirmStart.Title=\"Mulai Streaming?\"\nConfirmStart.Text=\"Apakah Anda yakin ingin memulai streaming?\"\nConfirmStop.Title=\"Berhenti Streaming?\"\nConfirmStop.Text=\"Apakah Anda yakin ingin berhenti streaming?\"\nConfirmStopRecord.Title=\"Berhenti Merekam?\"\nConfirmStopRecord.Text=\"Apakah Anda yakin ingin berhenti merekam?\"\nConfirmBWTest.Title=\"Mulai Pengujian Bandwidth?\"\nConfirmBWTest.Text=\"Anda telah mengonfigurasi OBS dalam mode pengujian bandwidth. Mode ini memungkinkan Anda untuk pengujian jaringan tanpa membuat saluran Anda disiarkan secara langsung. Setelah Anda selesai menguji, Anda harus menonaktifkannya agar penonton dapat melihat tayangan streaming Anda. \\n\\nApakah Anda ingin melanjutkan?\"\nConfirmExit.Title=\"Output yang Aktif\"\nConfirmExit.Text=\"Saat ini OBS masih dalam keadaan aktif. Semua rekaman/streaming akan dinonaktifkan.\"\nConfirmRemove.Title=\"Konfirmasi Hapus\"\nConfirmRemove.Text=\"Apakah Anda yakin ingin menghapus '%1'?\"\nConfirmRemove.TextMultiple=\"Apakah Anda yakin ingin menghapus item-item %1?\"\nConfirmReset.Title=\"Atur Ulang Properti\"\nConfirmReset.Text=\"Apakah Anda yakin Anda ingin mengatur ulang properti saat ini kembali ke bawaan nya?\"\nOutput.StartStreamFailed=\"Gagal memulai streaming\"\nOutput.StartRecordingFailed=\"Gagal memulai rekaman\"\nOutput.StartReplayFailed=\"Gagal memulai buffer replay\"\nOutput.StartVirtualCamFailed=\"Gagal untuk memulai kamera virtual\"\nOutput.StartFailedGeneric=\"Gagal memulai output. Silakan periksa catatan untuk detail lebih lanjut. \\n\\nCatatan: Jika Anda menggunakan pengkodean NVENC atau AMD, pastikan versi driver video Anda adalah yang terbaru.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Tidak dapat menyimpan tayangan ulang saat dijeda\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Peringatan: Tayangan ulang tidak dapat disimpan saat rekaman dijeda.\"\nOutput.ConnectFail.Title=\"Gagal terhubung\"\nOutput.ConnectFail.BadPath=\"Jalur atau Sambungan URL tidak sah. Mohon periksa pengaturan Anda untuk memastikan bahwa jalur/URL nya sudah benar.\"\nOutput.ConnectFail.ConnectFailed=\"Gagal terhubung ke server\"\nOutput.ConnectFail.InvalidStream=\"Tidak bisa mengakses saluran atau kunci stream yang ditentukan, mohon periksa kembali dengan benar kunci stream Anda. Jika sudah benar, kemungkinan ada masalah saat menghubungkan ke server.\"\nOutput.ConnectFail.HdrDisabled=\"Output HDR saat ini dinonaktifkan untuk output ini.\"\nOutput.ConnectFail.Error=\"Kesalahan yang tak terduga terjadi ketika mencoba menghubungkan ke server. Info lebih lanjut ada di catatan berkas.\"\nOutput.ConnectFail.Disconnected=\"Terputus dari sever.\"\nOutput.StreamEncodeError.Title=\"Galat enkode\"\nOutput.StreamEncodeError.Msg=\"Kesalahan pengkodean terjadi saat streaming.\"\nOutput.StreamEncodeError.Msg.LastError=\"Sebuah kesalahan pengkodean terjadi saat streaming:<br><br>%1\"\nOutput.RecordFail.Title=\"Gagal memulai rekaman\"\nOutput.RecordFail.Unsupported=\"Format outputnya antara tidak didukung atau tidak mendukung lebih dari satu trek audio. Mohon periksa pengaturan Anda dan coba lagi.\"\nOutput.RecordNoSpace.Title=\"Ruang penyimpanan tidak cukup\"\nOutput.RecordNoSpace.Msg=\"Ruang penyimpanan tidak cukup untuk melanjutkan rekaman.\"\nOutput.RecordError.Title=\"Kesalahan rekaman\"\nOutput.RecordError.Msg=\"Terjadi kesalahan yang tidak spesifik saat merekam.\"\nOutput.RecordError.EncodeErrorMsg=\"Kesalahan pengkodean terjadi saat merekam.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Sebuah kesalahan pengkodean terjadi saat merekam:<br><br>%1\"\nOutput.BadPath.Title=\"Jalur Berkas Tidak Sah\"\nOutput.BadPath.Text=\"Jalur Rekaman terkonfigurasi tidak bisa dibuka. Mohon periksa Jalur Rekaman Anda di Pengaturan → Output → Rekaman.\"\nOutput.NoBroadcast.Title=\"Tidak Ada Siaran yang Terkonfigurasi\"\nOutput.NoBroadcast.Text=\"Anda perlu menyiapkan sebuah siaran sebelum Anda memulai streaming.\"\nOutput.BroadcastStartFailed=\"Gagal memulai siaran\"\nOutput.BroadcastStopFailed=\"Gagal menghentikan siaran\"\nLogUploadDialog.Title=\"Unggah Berkas Laporan OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Mohon membaca <a href='https://obsproject.com/privacy-policy'>Kebijakan Privasi</a> beserta bagian-bagiannya terkait pengunggahan berkas sebelum mengunggah berkas apapun.\"\nLogUploadDialog.Labels.Progress=\"Laporan sedang diunggah. Mohon menunggu...\"\nLogUploadDialog.Labels.Description.AppLog=\"Berkas laporan Anda sudah diunggah. Sekarang Anda dapat membagikan tautan URL untuk tujuan pengawakutuan atau dukungan.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Laporan galat Anda sudah diunggah. Sekarang Anda dapat membagikan tautan URL untuk tujuan pengawakutuan.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Unggah\"\nLogUploadDialog.Buttons.CopyURL=\"Salin Tautan URL Laporan\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analisa Berkas Laporan\"\nLogUploadDialog.Buttons.RetryButton=\"Coba Lagi\"\nLogUploadDialog.Errors.Template=\"Sebuah galat terjadi saat mencoba mengunggah berkas:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Tidak ada berkas ditemukan atau berkasnya kosong untuk diunggah.\"\nRemux.SourceFile=\"Rekaman OBS\"\nRemux.TargetFile=\"Target Berkas\"\nRemux.Stop=\"Berhenti Me-Remux\"\nRemux.ClearFinished=\"Bersihkan Item Jadi\"\nRemux.ClearAll=\"Bersihkan Semua\"\nRemux.OBSRecording=\"Rekaman OBS\"\nRemux.FinishedTitle=\"Remuxing selesai\"\nRemux.Finished=\"Rekaman telah di-remux\"\nRemux.FinishedError=\"Rekaman telah di-remux, tetapi berkas mungkin tidak lengkap\"\nRemux.SelectRecording=\"Pilih Rekaman OBS...\"\nRemux.SelectTarget=\"Pilih target berkas...\"\nRemux.FileExistsTitle=\"Berkas target sudah ada\"\nRemux.FileExists=\"Berkas target tersebut sudah ada. Apakah Anda ingin menggantikannya?\"\nRemux.ExitUnfinishedTitle=\"Remuxing sedang diproses\"\nRemux.ExitUnfinished=\"Remuxing belum selesai, menghentikan sekarang dapat membuat berkas targetnya tidak bisa digunakan.\\nApakah Anda yakin ingin menghentikan remuxing?\"\nRemux.HelpText=\"Seret berkas ke jendela ini untuk di-remux, atau pilih baris \\\"Rekaman OBS\\\" yang kosong untuk memilih berkas.\"\nRemux.NoFilesAddedTitle=\"Tidak ada berkas remux-an yang ditambahkan\"\nRemux.NoFilesAdded=\"Tidak ada berkas yang ditambahkan ke remux. Letakkan sebuah folder yang berisi satu atau lebih berkas video.\"\nMissingFiles=\"Berkas Tidak Ditemukan\"\nMissingFiles.MissingFile=\"Berkas yang Hilang\"\nMissingFiles.NewFile=\"Berkas yang Baru\"\nMissingFiles.HelpText=\"Beberapa berkas hilang sejak terakhir kali Anda menggunakan OBS.\"\nMissingFiles.Clear=\"<dihapus>\"\nMissingFiles.NumFound=\"Ditemukan %1 dari %2\"\nMissingFiles.Search=\"Cari Direktori...\"\nMissingFiles.SelectFile=\"Pilih berkas...\"\nMissingFiles.SelectDir=\"Pilih Folder untuk Mencari\"\nMissingFiles.State=\"Keadaan\"\nMissingFiles.Missing=\"Hilang\"\nMissingFiles.Replaced=\"Diganti\"\nMissingFiles.Cleared=\"Bersih\"\nMissingFiles.Found=\"Ditemukan\"\nMissingFiles.AutoSearch=\"Kecocokan berkas tambahan ditemukan\"\nMissingFiles.AutoSearchText=\"OBS telah menemukan kecocokan tambahan pada berkas yang hilang dalam direktori tersebut. Apakah Anda ingin menambahkannya?\"\nMissingFiles.NoMissing.Title=\"Pemeriksaan Kehilangan Berkas\"\nMissingFiles.NoMissing.Text=\"Tidak ada berkas yang terlihat menghilang.\"\nMacPermissions.MenuAction=\"Tinjau Perizinan Aplikasi...\"\nMacPermissions.Title=\"Tinjau Perizinan Aplikasi\"\nMacPermissions.Description=\"OBS Studio perlu izin Anda untuk dapat menyediakan fitur-fitur tertentu. Disarankan untuk mengaktifkan izin berikut ini, tetapi tidak diperlukan untuk penggunaan aplikasi. Anda dapat selalu mengaktifkannya nanti.\"\nMacPermissions.Description.OpenDialog=\"Anda dapat membuka kembali dialog ini melalui menu OBS Studio.\"\nMacPermissions.AccessGranted=\"Akses Diberikan\"\nMacPermissions.RequestAccess=\"Meminta Akses\"\nMacPermissions.OpenPreferences=\"Buka Preferensi %1\"\nMacPermissions.Item.ScreenRecording=\"Rekaman Layar\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS perlu izin ini agar dapat menangkap layar Anda.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Izin ini diperlukan agar dapat menangkap konten dari kamera web (webcam) atau kartu penangkap.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS perlu izin ini jika Anda ingin menangkap suara mikrofon atau sebuah perangkat audio eksternal Anda.\"\nMacPermissions.Item.InputMonitoring=\"Pemantauan Input\"\nMacPermissions.Item.InputMonitoring.Details=\"Izin ini dibutuhkan agar pintasan dapat bekerja selagi OBS berjalan di latar belakang.\"\nMacPermissions.Item.Accessibility=\"Aksesibilitas\"\nMacPermissions.Item.Accessibility.Details=\"Pada pemasangan versi sebelumnya, OBS mungkin terdaftar di \\\"Aksesibilitas\\\" daripada \\\"Pemantauan Input\\\".\"\nMacPermissions.Continue=\"Selanjutnya\"\nSourceLeak.Title=\"Pembersihan Sumber Galat\"\nSourceLeak.Text=\"Ada masalah saat mengubah koleksi adegan & beberapa sumber juga tidak bisa dibongkar muat. Masalah ini biasanya disebabkan oleh plugin yang tidak benar melepas sumber dayanya. Pastikan semua plugin yang digunakan sudah terbaru.\\n\\nOBS Studio sekarang akan ditutup untuk mencegah potensi kerusakan data.\"\nBasic.DesktopDevice1=\"Audio Desktop\"\nBasic.DesktopDevice2=\"Audio Desktop 2\"\nBasic.Scene=\"Adegan\"\nBasic.DisplayCapture=\"Penangkap Layar\"\nBasic.Main.PreviewConextMenu.Enable=\"Aktifkan Pratinjau\"\nBasic.Main.Preview.Disable=\"Nonaktifkan Pratinjau\"\nScaleFiltering=\"Skala Filter\"\nScaleFiltering.Point=\"Titik\"\nScaleFiltering.Bicubic=\"Bikubik\"\nBlendingMethod=\"Metode Pencampuran\"\nBlendingMethod.Default=\"Bawaan\"\nBlendingMethod.SrgbOff=\"SRGB Nonaktif\"\nBlendingMode=\"Mode Pencampuran\"\nBlendingMode.Additive=\"Tambah\"\nBlendingMode.Subtract=\"Kurangi\"\nBlendingMode.Screen=\"Saring\"\nBlendingMode.Multiply=\"Menggandakan\"\nBlendingMode.Lighten=\"Menerangkan\"\nBlendingMode.Darken=\"Mengelamkan\"\nDeinterlacing.TopFieldFirst=\"Bagian Atas Dahulu\"\nDeinterlacing.BottomFieldFirst=\"Bagian Bawah Dahulu\"\nVolControl.SliderUnmuted=\"Penggeser volume untuk '%1':\"\nVolControl.SliderMuted=\"Penggeser volume untuk '%1': (saat ini di senyapkan)\"\nVolControl.Mute=\"Senyapkan '%1'\"\nVolControl.Properties=\"Properti untuk '%1'\"\nVolControl.UnassignedWarning.Title=\"Sumber Audio Belum Ditetapkan\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" tidak ditetapkan ke trek audio manapun dan itu tidak akan bersuara dalam streaming maupun rekaman.\\n\\nUntuk menetapkan sebuah sumber suara ke sebuah trek, buka Properti Lanjutan Audio melalui menu klik kanan atau tombol roda gigi di bilah alat dok mixer.\"\nBasic.Main.AddSceneDlg.Title=\"Tambahkan Adegan\"\nBasic.Main.AddSceneDlg.Text=\"Silakan masukkan nama adegan\"\nBasic.Main.DefaultSceneName.Text=\"Adegan %1\"\nBasic.Main.AddSceneCollection.Title=\"Tambahkan Koleksi Adegan\"\nBasic.Main.AddSceneCollection.Text=\"Silakan masukkan nama koleksi adegan\"\nBasic.Main.RenameSceneCollection.Title=\"Ubah Nama Koleksi Adegan\"\nBasic.Main.RemigrateSceneCollection.Title=\"Perbarui Resolusi Koleksi Adegan\"\nBasic.Main.RemigrateSceneCollection.Text=\"Apakah ingin memperbarui resolusi koleksi adegan \\\"%1\\\" agar cocok dengan profil resolusi kanvas %2x%3 saat ini?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Tidak dapat memperbarui resolusi koleksi adegan saat output sedang aktif.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Gagal memperbarui resolusi koleksi adegan. Resolusi asli tidak diketahui.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Atur ulang mustahil: Gagal mengubah resolusi OBS.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Atur ulang mustahil: Resolusi saat ini sudah menjadi resolusi dasar koleksi adegan.\"\nAddProfile.Title=\"Tambahkan Profil\"\nAddProfile.Text=\"Silakan masukkan nama profil\"\nAddProfile.WizardCheckbox=\"Tampilkan Wisaya Konfigurasi Otomatis\"\nRenameProfile.Title=\"Ubah Nama Profil\"\nBasic.Main.MixerRename.Title=\"Ubah Nama Sumber Audio\"\nBasic.Main.MixerRename.Text=\"Silakan masukkan nama sumber audio\"\nBasic.Main.PreviewDisabled=\"Pratinjau saat ini dinonaktifkan\"\nBasic.SourceSelect=\"Buat/Pilih Sumber\"\nBasic.SourceSelect.CreateNew=\"Buat baru\"\nBasic.SourceSelect.AddExisting=\"Tambah Dengan Yang Ada\"\nBasic.SourceSelect.AddVisible=\"Buat sumber terlihat\"\nBasic.Main.Sources.Visibility=\"Visibilitas\"\nBasic.Main.Sources.VisibilityDescription=\"Kontrol visibilitas '%1' di kanvas\"\nBasic.Main.Sources.Lock=\"Kunci\"\nBasic.Main.Sources.LockDescription=\"Kunci posisi dan skalakan '%1' di kanvas\"\nBasic.PropertiesWindow=\"Properti untuk '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (pilih-otomatis: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Pilih warna\"\nBasic.PropertiesWindow.SelectFont=\"Pilih fon\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Pilih Fon\"\nBasic.PropertiesWindow.ConfirmTitle=\"Pengaturan Berubah\"\nBasic.PropertiesWindow.Confirm=\"Ada perubahan yang belum disimpan. Apakah Anda ingin menyimpannya?\"\nBasic.PropertiesWindow.NoProperties=\"Tidak ada properti-properti yang tersedia\"\nBasic.PropertiesWindow.AddFiles=\"Tambah Berkas\"\nBasic.PropertiesWindow.AddDir=\"Tambah Direktori\"\nBasic.PropertiesWindow.AddURL=\"Tambah Jalur/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Tambahkan direktori ke '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Tambahkan berkas ke '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Tambahkan entri ke '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Ubah entri dari '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Nilai FPS Sederhana\"\nBasic.PropertiesView.FPS.Rational=\"Nilai FPS Rasional\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Rentang FPS yang Sah:\"\nBasic.PropertiesView.UrlButton.Text=\"Buka tautan ini di peramban web bawaan Anda?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Buka URL\"\nBasic.InteractionWindow=\"Berinteraksi dengan '%1'\"\nBasic.StatusBar.Reconnecting=\"Terputus, menghubungkan kembali dalam %2 detik (percobaan %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Mencoba menghubungkan kembali... (percobaan %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Berhasil menghubung ulang\"\nBasic.StatusBar.Delay=\"Delay (%1 detik)\"\nBasic.StatusBar.DelayStartingIn=\"Delay (dimulai dalam %1 dtk)\"\nBasic.StatusBar.DelayStoppingIn=\"Delay (berhenti dalam %1 dtk)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Delay (berhenti dalam %1 dtk, dimulai dalam %2 dtk)\"\nBasic.StatusBar.RecordingSavedTo=\"Rekaman tersimpan ke '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Buffer replay tersimpan ke '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Tangkapan Layar tersimpan ke '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Rekaman ter-remux otomatis ke '%1'\"\nBasic.Filters=\"Filter\"\nBasic.Filters.AsyncFilters=\"Filter Audio/Video\"\nBasic.Filters.AudioFilters=\"Filter Audio\"\nBasic.Filters.EffectFilters=\"Filter Efek\"\nBasic.Filters.Title=\"Filter untuk '%1'\"\nBasic.Filters.AddFilter.Title=\"Nama filter\"\nBasic.Filters.AddFilter.Text=\"Silakan tentukan nama filter nya\"\nBasic.TransformWindow=\"Transformasi Item Adegan\"\nBasic.TransformWindow.Position=\"Posisi\"\nBasic.TransformWindow.PositionX=\"Posisi X\"\nBasic.TransformWindow.PositionY=\"Posisi Y\"\nBasic.TransformWindow.Rotation=\"Rotasi\"\nBasic.TransformWindow.Size=\"Ukuran\"\nBasic.TransformWindow.Width=\"Lebar\"\nBasic.TransformWindow.Height=\"Tinggi\"\nBasic.TransformWindow.Alignment=\"Penjajaran Posisi\"\nBasic.TransformWindow.BoundsType=\"Jenis Kotak Pembatas\"\nBasic.TransformWindow.BoundsAlignment=\"Penjajaran dalam Kotak Pembatas\"\nBasic.TransformWindow.Bounds=\"Ukuran Kotak Pembatas\"\nBasic.TransformWindow.BoundsWidth=\"Lebar Kotak Pembatas\"\nBasic.TransformWindow.BoundsHeight=\"Tinggi Kotak Pembatas\"\nBasic.TransformWindow.CropToBounds=\"Potong ke Kotak Pembatas\"\nBasic.TransformWindow.Crop=\"Potong\"\nBasic.TransformWindow.CropLeft=\"Potong Kiri\"\nBasic.TransformWindow.CropRight=\"Potong Kanan\"\nBasic.TransformWindow.CropTop=\"Potong Atas\"\nBasic.TransformWindow.CropBottom=\"Potong Bawah\"\nBasic.TransformWindow.Alignment.TopLeft=\"Kiri Atas\"\nBasic.TransformWindow.Alignment.TopCenter=\"Tengah Atas\"\nBasic.TransformWindow.Alignment.TopRight=\"Kanan Atas\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Kiri Tengah\"\nBasic.TransformWindow.Alignment.Center=\"Tengah\"\nBasic.TransformWindow.Alignment.CenterRight=\"Kanan Tengah\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Kiri Bawah\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Tengah Bawah\"\nBasic.TransformWindow.Alignment.BottomRight=\"Kanan Bawah\"\nBasic.TransformWindow.BoundsType.None=\"Tidak ada pembatas\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Ukuran maksimum saja\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skalakan ke batas dalam\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skalakan ke batas luar\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skalakan ke lebar batas\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skalakan ke tinggi batas\"\nBasic.TransformWindow.BoundsType.Stretch=\"Regangkan ke batas\"\nBasic.TransformWindow.Title=\"Ubah Transformasi untuk '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Tidak ada sumber yang dipilih\"\nBasic.Main.AddSourceHelp.Title=\"Tidak Dapat Menambahkan Sumber\"\nBasic.Main.AddSourceHelp.Text=\"Anda harus memiliki setidaknya 1 adegan untuk menambahkan sumber.\"\nBasic.Main.Scenes=\"Adegan\"\nBasic.Main.Sources=\"Sumber\"\nBasic.Main.Source=\"Sumber\"\nBasic.Main.Controls=\"Kontrol\"\nBasic.Main.PreparingStream=\"Mempersiapkan...\"\nBasic.Main.Connecting=\"Menghubungkan...\"\nBasic.Main.StartRecording=\"Mulai Merekam\"\nBasic.Main.StartReplayBuffer=\"Mulai Buffer Replay\"\nBasic.Main.SaveReplay=\"Simpan Tayangan Ulang\"\nBasic.Main.StartStreaming=\"Mulai Streaming\"\nBasic.Main.StartBroadcast=\"Mulai Siaran\"\nBasic.Main.StartVirtualCam=\"Mulai Kamera Virtual\"\nBasic.Main.StopRecording=\"Berhenti Merekam\"\nBasic.Main.PauseRecording=\"Jeda Rekaman\"\nBasic.Main.UnpauseRecording=\"Lanjut Merekam\"\nBasic.Main.SplitFile=\"Pisahkan Berkas Rekaman\"\nBasic.Main.AddChapterMarker=\"Tambahkan Penanda Bab (Khusus Hybrid MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Menghentikan Perekaman...\"\nBasic.Main.StopReplayBuffer=\"Hentikan Buffer Replay\"\nBasic.Main.StoppingReplayBuffer=\"Menghentikan Buffer Replay...\"\nBasic.Main.SetupBroadcast=\"Kelola Siaran\"\nBasic.Main.StopStreaming=\"Berhenti Streaming\"\nBasic.Main.StopBroadcast=\"Akhiri Siaran\"\nBasic.Main.AutoStopEnabled=\"(Berhenti Otomatis)\"\nBasic.Main.StoppingStreaming=\"Menghentikan Streaming...\"\nBasic.Main.ForceStopStreaming=\"Berhenti Streaming (buang delay)\"\nBasic.Main.ShowContextBar=\"Tampilkan Bilah Alat Sumber\"\nBasic.Main.HideContextBar=\"Sembunyikan Bilah Alat Sumber\"\nBasic.Main.StopVirtualCam=\"Hentikan Kamera Virtual\"\nBasic.Main.Group=\"Grup %1\"\nBasic.Main.GroupItems=\"Kelompokkan Item yang Dipilih\"\nBasic.Main.Ungroup=\"Pisahkan Kelompok\"\nBasic.Main.GridMode=\"Mode Grid\"\nBasic.Main.ListMode=\"Mode Daftar\"\nBasic.Main.VirtualCamConfig=\"Atur Kamera Virtual\"\nBasic.VCam.VirtualCamera=\"Kamera Virtual\"\nBasic.VCam.OutputType=\"Tipe Output\"\nBasic.VCam.OutputSelection=\"Pilih Output\"\nBasic.VCam.OutputType.Program=\"Program (Bawaan)\"\nBasic.VCam.OutputSelection.NoSelection=\"Tidak ada pilihan untuk jenis output ini\"\nBasic.VCam.RestartWarning=\"Kamera virtual akan diaktifkan ulang untuk menerapkan perubahan ini\"\nBasic.MainMenu.File=\"Berkas (&F)\"\nBasic.MainMenu.File.Export=\"&Ekspor\"\nBasic.MainMenu.File.Import=\"&Impor\"\nBasic.MainMenu.File.ShowRecordings=\"Tampilkan &Rekaman\"\nBasic.MainMenu.File.Remux=\"Re&mux Rekaman\"\nBasic.MainMenu.File.Settings=\"Pengaturan (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Tampilkan Folder Pengaturan\"\nBasic.MainMenu.File.ShowProfileFolder=\"Tampilkan Folder Profil\"\nBasic.MainMenu.File.ShowMissingFiles=\"Periksa Berkas yang Hilang\"\nBasic.MainMenu.File.Exit=\"Keluar (&X)\"\nBasic.MainMenu.Edit=\"Ubah (&E)\"\nBasic.MainMenu.Edit.Undo=\"M&undur\"\nBasic.MainMenu.Edit.Redo=\"Ulangi (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Kunci Pratinjau (&L)\"\nBasic.MainMenu.Edit.Scale=\"Pratinjau Pen&skalaan\"\nBasic.MainMenu.Edit.Scale.Window=\"Skalakan ke Jendela\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Kanvas (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Diskalakan (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Perbesar\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Perkecil\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Atur Ulang Perbesaran\"\nBasic.MainMenu.Edit.Transform=\"&Transformasi\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Ubah Transformasi... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Salin Transformasi\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Tempel Transformasi\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Atu&r Ulang Transformasi\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Putar 90 derajat SJJ\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Putar 90 derajat BAJJ\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Putar 180 derajat\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Berbalik &Horizontal\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Berbalik &Vertikal\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Pas dengan layar (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Regangkan ke layar (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Tengahkan ke layar (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Tengah Vertikal\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Tengah Horizontal\"\nBasic.MainMenu.Edit.Order=\"Atur Urutan P&osisi\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Pindah Ke Atas (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Pindah Ke Bawah (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Pindah Ke Paling A&tas\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Pindah Ke Paling &Bawah\"\nBasic.MainMenu.Edit.AdvAudio=\"Properti Lanjutan &Audio\"\nBasic.MainMenu.View=\"Tampilan (&V)\"\nBasic.MainMenu.View.Toolbars=\"Bilah Ala&t\"\nBasic.MainMenu.View.ListboxToolbars=\"Dok Bilah Alat\"\nBasic.MainMenu.View.ContextBar=\"Bilah Alat Sumber\"\nBasic.MainMenu.View.SourceIcons=\"&Ikon Sumber\"\nBasic.MainMenu.View.StatusBar=\"Bar &Status\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Antarmuka Layar Penuh\"\nBasic.MainMenu.View.ResetUI=\"Atu&r Ulang UI\"\nBasic.MainMenu.View.AlwaysOnTop=\"Selalu di &Atas\"\nBasic.MainMenu.View.SceneListMode=\"Mode Daftar Adegan\"\nBasic.MainMenu.Docks=\"&Dok\"\nBasic.MainMenu.Docks.ResetDocks=\"Atu&r Ulang Dok\"\nBasic.MainMenu.Docks.LockDocks=\"Kunci Dok (&L)\"\nBasic.MainMenu.Docks.SideDocks=\"Dok tinggi penuh (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Penyesuaian Dok Peramban...\"\nBasic.MainMenu.Import=\"Impor...\"\nBasic.MainMenu.Export=\"Ekspor...\"\nBasic.MainMenu.SceneCollection=\"Kolek&si Adegan\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Impor Profil\"\nBasic.MainMenu.Profile.Export=\"Ekspor Profil\"\nBasic.MainMenu.SceneCollection.Import=\"Impor Koleksi Adegan\"\nBasic.MainMenu.SceneCollection.Export=\"Ekspor Koleksi Adegan\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Atur Ulang Resolusi Dasar\"\nBasic.MainMenu.SceneCollection.Migrate=\"Atur Resolusi Dasar\"\nBasic.MainMenu.Profile.Exists=\"Profil sudah ada\"\nBasic.MainMenu.Tools=\"Ala&t\"\nBasic.MainMenu.Help=\"Bantuan (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal Bantuan\"\nBasic.MainMenu.Help.Website=\"Kunjungi Situs &Web\"\nBasic.MainMenu.Help.Discord=\"Gabung Server &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Apa yang Baru\"\nBasic.MainMenu.Help.Logs=\"Catatan Berkas (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Tampilkan Catatan Berka&s\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Unggah &Catatan Berkas Saat Ini\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Unggah Berkas Catatan Sebelumnya (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Tampilkan Catatan Saat Ini (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Catatan Rilis\"\nBasic.MainMenu.Help.CheckForUpdates=\"Periksa Pembaruan\"\nBasic.MainMenu.Help.Repair=\"Cek Integritas Berkas\"\nBasic.MainMenu.Help.RestartSafeMode=\"Aktifkan Ulang dalam Mode Aman\"\nBasic.MainMenu.Help.RestartNormal=\"Aktifkan Ulang dalam Mode Biasa\"\nBasic.MainMenu.Help.CrashLogs=\"Lapo&ran Galat\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Tampilkan Laporan Galat (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Unggah La&poran Galat Sebelumnya\"\nBasic.MainMenu.Help.About=\"Tent&ang\"\nBasic.Settings.ProgramRestart=\"Program harus diaktifkan ulang agar pengaturan ini berlaku.\"\nBasic.Settings.ConfirmTitle=\"Konfirmasi Perubahan\"\nBasic.Settings.Confirm=\"Anda memiliki perubahan yang belum disimpan. Simpan perubahan?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 mengendalikan beberapa setelan streaming Anda\"\nBasic.Settings.General=\"Umum\"\nBasic.Settings.General.Language=\"Bahasa\"\nBasic.Settings.General.Updater=\"Pembaruan\"\nBasic.Settings.General.UpdateChannel=\"Pembaruan Saluran\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Nonaktif)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Bawaan)\"\nBasic.Settings.General.EnableAutoUpdates=\"Otomatis memeriksa pembaruan saat membuka aplikasi\"\nBasic.Settings.General.OpenStatsOnStartup=\"Buka dialog statistik saat aplikasi dijalankan\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Sembunyikan jendela OBS dari tangkapan layar\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Pengaturan ini akan menyembunyikan semua jendela non-proyektor OBS Studio dari tangkapan oleh OBS \\ndan mempengaruhi aplikasi lain, seperti konferensi, berbagi layar, dukungan remote, tangkapan layar, dan perangkat lunak tangkapan lainnya.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Mengaktifkan opsi ini akan menyembunyikan semua jendela non-proyektor OBS Studio dari tangkapan oleh OBS dan mempengaruhi aplikasi lain, seperti konferensi, berbagi layar, dukungan remote, tangkapan layar, dan perangkat lunak tangkapan lainnya.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Tampilkan dialog konfirmasi saat memulai streaming\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Tampilkan dialog konfirmasi saat menghentikan streaming\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Tampilkan dialog konfirmasi saat menghentikan rekaman\"\nBasic.Settings.General.Projectors=\"Proyektor\"\nBasic.Settings.General.HideProjectorCursor=\"Sembunyikan kursor pada Proyektor\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Jadikan proyektor selalu di atas\"\nBasic.Settings.General.Snapping=\"Sentakan Penjajaran Sumber\"\nBasic.Settings.General.ScreenSnapping=\"Sentakkan Sumber ke tepi layar\"\nBasic.Settings.General.CenterSnapping=\"Sentakkan Sumber ke pusat horizontal dan vertikal\"\nBasic.Settings.General.SourceSnapping=\"Sentakkan Sumber ke sumber lain\"\nBasic.Settings.General.SnapDistance=\"Sensitivitas Sentakan\"\nBasic.Settings.General.SpacingHelpers=\"Tampilkan pemandu penjajaran piksel\"\nBasic.Settings.General.RecordWhenStreaming=\"Otomatis merekam saat streaming\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Terus merekam saat streaming berhenti\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Otomatis memulai buffer replay saat streaming\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Tetap aktifkan buffer replay saat streaming berhenti\"\nBasic.Settings.General.SysTray=\"Baki Sistem\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimalkan ke baki sistem saat aplikasi dijalankan\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Selalu minimalkan ke baki sistem daripada ke bilah tugas\"\nBasic.Settings.General.SaveProjectors=\"Simpan proyektor saat keluar\"\nBasic.Settings.General.CloseExistingProjectors=\"Batasi satu layar penuh proyektor per layar\"\nBasic.Settings.General.Preview=\"Pratinjau\"\nBasic.Settings.General.OverflowHidden=\"Sembunyikan yang tertumpah\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Yang tertumpah selalu terlihat\"\nBasic.Settings.General.OverflowSelectionHidden=\"Tampilkan yang tertumpah bahkan ketika sumber tidak terlihat\"\nBasic.Settings.General.Importers=\"Pengimpor\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Cari lokasi yang diketahui untuk koleksi adegan saat mengimpor\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Transisi ke adegan ketika di klik dua kali\"\nBasic.Settings.General.StudioPortraitLayout=\"Aktifkan tata letak potret/vertikal\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Tampilkan label pratinjau/program\"\nBasic.Settings.General.Multiview=\"Tampilan Ganda\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Klik untuk beralih antar adegan\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Tampilkan nama adegan\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Gambar area aman (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Tata Letak Tampilan Ganda\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontal, Atas (8 Adegan)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontal, Bawah (8 Adegan)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertikal, Kiri (8 Adegan)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertikal, Kanan (8 Adegan)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontal, Atas (18 Adegan)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontal, Atas (24 Adegan)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Adegan saja (4 Adegan)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Adegan saja (9 Adegan)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Adegan saja (16 Adegan)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Adegan saja (25 Adegan)\"\nBasic.Settings.General.ChannelName.stable=\"Stabil\"\nBasic.Settings.General.ChannelDescription.stable=\"Rilisan stabil mutakhir\"\nBasic.Settings.General.ChannelName.beta=\"Percobaan / Kandidat Rilisan\"\nBasic.Settings.General.ChannelDescription.beta=\"Potensi versi pra-rilisan tidak stabil\"\nBasic.Settings.Appearance=\"Tampilan\"\nBasic.Settings.Appearance.General=\"Umum\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Gaya\"\nBasic.Settings.Appearance.General.NoVariant=\"Tidak Ada Gaya yang Tersedia\"\nBasic.Settings.Appearance.FontScale=\"Ukuran Fon\"\nBasic.Settings.Appearance.Density=\"Kepadatan\"\nBasic.Settings.Appearance.Classic=\"Klasik\"\nBasic.Settings.Appearance.Compact=\"Padat\"\nBasic.Settings.Appearance.Comfortable=\"Nyaman\"\nBasic.Settings.Appearance.OptionsWarning=\"Beberapa opsi tampilan tidak tersedia untuk gaya ini.\"\nBasic.Settings.Stream=\"Streaming\"\nBasic.Settings.Stream.Destination=\"Tujuan\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Gunakan autentikasi\"\nBasic.Settings.Stream.Custom.Username=\"Nama Pengguna\"\nBasic.Settings.Stream.Custom.Password=\"Kata Sandi\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: masukkan srp_username nya.\\nRTMP: masukkan nama pengguna nya.\\nSRT: tidak digunakan.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: masukkan srp_password nya.\\nRTMP: masukkan kata sandi nya.\\nSRT: masukkan frasa sandi enkripsi nya.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Aktifkan Mode Pengujian Bandwidth\"\nBasic.Settings.Stream.TTVAddon=\"Add-On Obrolan Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Tidak ada\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV dan FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Aturan Streaming Tidak Ada\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Buka Pengaturan\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL dan Kunci Stream tidak ditemukan.\\n\\nBuka pengaturan untuk memasukkan URL dan Kunci Stream di tab 'streaming'.\"\nBasic.Settings.Stream.MissingUrl=\"URL Streaming tidak ditemukan.\\n\\nBuka pengaturan untuk memasukkan URL di tab 'Streaming'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Kunci Stream tidak ditemukan.\\n\\nBuka pengaturan untuk memasukkan kunci stream di tab 'Streaming'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Abaikan rekomendasi pengaturan layanan streaming\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Mengesampinkan Pengaturan Yang Direkomendasi\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Peringatan: Mengabaikan batasan layanan dapat menurunkan kualitas streaming atau mencegah Anda dari aktivitas streaming. \\n\\nLanjutkan?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maksimum Bitrate Video: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maksimum Bitrate Audio: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksimum Resolusi: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksimum FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Tentukan Penyesuaian Server...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Sesuaikan Server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Aktifkan %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maksimum Bandwidth Streaming\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Otomatis\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maksimum Trek Video\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Otomatis\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Aktifkan timbunan streaming ke FLV (menggunakn berkas pengaturan rekaman sederhana)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Timpa Konfigurasi (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Aktifkan Penimpa Konfigurasi\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Video Multitrack\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Kanvas Tambahan\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Siarsimul\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Siarsimul memungkinkan Anda untuk mengkode dan mengirimkan beberapa kualitas video. <a href='https://obsproject.com/kb/whip-streaming-guide'>Pelajari Lebih Lanjut</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Total Lapisan\"\nBasic.Settings.Stream.AdvancedOptions=\"Opsi Lanjutan\"\nBasic.Settings.Output.Format=\"Format Rekaman\"\nBasic.Settings.Output.Format.MKV=\"Video Matroska (.mkv)\"\nBasic.Settings.Output.Format.fMP4=\"Pecahan MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Pecahan MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Pecahan MOV menulis rekaman dalam potongan chunk dan tidak memerlukan finalisasi yang sama seperti berkas MOV tradisional.\\nIni memastikan berkas tetap dapat diputar bahkan jika menulis ke penyimpanan nya mengalami interupsi. Contohnya, akibat BSOD atau mati listrik.\\n\\nIni mungkin tidak kompatibel dengan semua pemutar dan penyunting. Gunakan Berkas → Remux Rekaman untuk mengubah berkas ke dalam format yang kompatibel jika diperlukan.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Pecahan MP4 menulis rekaman dalam potongan chunk dan tidak memerlukan finalisasi yang sama seperti berkas MP4 tradisional.\\nIni memastikan berkas tetap dapat diputar bahkan jika menulis ke penyimpanan nya mengalami interupsi. Contohnya, akibat BSOD atau mati listrik.\\n\\nIni mungkin tidak kompatibel dengan semua pemutar dan penyunting. Gunakan Berkas → Remux Rekaman untuk mengubah berkas ke dalam format yang kompatibel jika diperlukan.\"\nBasic.Settings.Output.Encoder.Video=\"Pengkodean Video\"\nBasic.Settings.Output.Encoder.Audio=\"Pengkodean Audio\"\nBasic.Settings.Output.SelectDirectory=\"Pilih Direktori Rekaman\"\nBasic.Settings.Output.DynamicBitrate=\"Ubah bitrate secara dinamis untuk mengelola kemacetan\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Ubah bitrate secara dinamis untuk mengelola kemacetan (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Alih-alih menurunkan frame untuk mengurangi kemacetan, Anda dapat mengubah bitrate langsung secara dinamis.\\n\\nMohon diingat bahwa ini dapat meningkatkan waktu tunda (delay) bagi penonton jika tiba-tiba ada kemacetan yang signifikan.\\nKetika bitrate berkurang, diperlukan waktu beberapa menit untuk pulih kembali.\\n\\nSaat ini hanya mendukung RTMP.\"\nBasic.Settings.Output.Mode=\"Mode Output\"\nBasic.Settings.Output.Mode.Simple=\"Sederhana\"\nBasic.Settings.Output.Mode.Adv=\"Lanjutan\"\nBasic.Settings.Output.Mode.FFmpeg=\"Output FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Aktifkan Buffer Replay\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maksimum Waktu Tayangan Ulang\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Memori Maksimum\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Perkiraan penggunaan memori: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Peringatan: Estimasi penggunaan memori nya %1 MiB lebih besar daripada rekomendasi maksimum nya %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Tidak dapat memperkirakan penggunaan memori. Mohon atur batas maksimum memori.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Awalan Nama Berkas Buffer Replay\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Akhiran\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Buffer Replay tidak dapat digunakan ketika jenis rekaman nya diatur ke Modifikasi Output (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Jalur Rekaman\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Kualitas Rekaman\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Sama seperti streaming\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Kualitas Tinggi, Ukuran Berkas Sedang\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Kualitas Tidak Bisa Dibedakan, Ukuran Berkas Besar\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Kualitas Lossless, Ukuran Berkas Sangat Besar\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Peringatan: Bitrate video streaming akan diatur ke %1, yang merupakan batas tertinggi untuk layanan streaming saat ini.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Peringatan: Bitrate audio streaming akan diatur ke %1, yang merupakan batas tertinggi untuk layanan streaming saat ini.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Peringatan: Rekaman tidak dapat dijeda jika kualitas rekaman diatur ke \\\"Sama seperti streaming\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Peringatan: Format rekaman yang saat ini dipilih tidak kompatibel dengan pengkodean-pengkodean stream yang dipilih.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Peringatan: Perekaman dengan pengkodean perangkat lunak pada kualitas yang berbeda dengan streaming akan membutuhkan penggunaan CPU tambahan jika Anda melakukan streaming dan rekaman secara bersamaan.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Peringatan: Kualitas lossless menghasilkan ukuran berkas yang amat sangat besar! Kualitas lossless dapat mengkonsumsi ruang penyimpanan lebih dari 7 gigabyte per menit pada resolusi dan framerate yang tinggi. Lossless tidak disarankan untuk rekaman yang panjang. Kecuali, Anda memiliki jumlah ruang penyimpanan tersedia yang sangat besar. Buffer replay tidak tersedia ketika menggunakan kualitas lossless.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Apakah Anda yakin ingin menggunakan kualitas lossless?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Peringatan kualitas lossless!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Perangkat Lunak (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Perangkat Keras (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Perangkat Keras (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Perangkat Keras (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Perangkat Keras (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Perangkat Keras (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Perangkat Keras (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Perangkat Keras (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Perangkat Keras (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Perangkat Keras (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Perangkat Keras (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Perangkat Lunak (preset penggunaan CPU rendah x264, ukuran berkas meningkat)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Bawaan)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Trek VOD Twitch (Menggunakan Trek 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Trek Audio\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Resolusi/Framerate Tidak Cocok\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Layanan streaming ini tidak mendukung resolusi dan/atau framerate Anda saat ini. Ini akan diubah ke nilai yang paling dekat dan cocok:\\n\\n%1\\n\\nApakah Anda ingin melanjutkan?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolusi: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Pengkodean Tidak Kompatibel\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Layanan streaming \\\"%1\\\" tidak mendukung pengkodean \\\"%2\\\". Pengkodean akan diganti dengan \\\"%3\\\".\\n\\nAnda yakin ingin lanjut?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Layanan streaming \\\"%1\\\" tidak mendukung pengkodean \\\"%2\\\" dan \\\"%3\\\". Pengkodean tersebut akan diganti ke \\\"%4\\\" dan \\\"%5\\\".\\n\\nAnda yakin ingin lanjut?\"\nBasic.Settings.Output.VideoBitrate=\"Bitrate Video\"\nBasic.Settings.Output.AudioBitrate=\"Bitrate Audio\"\nBasic.Settings.Output.Reconnect=\"Otomatis Menyambungkan Ulang\"\nBasic.Settings.Output.RetryDelay=\"Pengulangan Delay\"\nBasic.Settings.Output.MaxRetries=\"Maksimum Pengulangan\"\nBasic.Settings.Output.Advanced=\"Aktifkan Setelan Penyesuaian Pengkodean (Lanjutan)\"\nBasic.Settings.Output.EncoderPreset=\"Preset Pengkodean\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (penggunaan CPU rendah, kualitas rendah)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (bawaan) (penggunaan CPU sedang, kualitas standar)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (penggunaan CPU tinggi, kualitas tinggi)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Setelan Penyesuaian Pengkodean\"\nBasic.Settings.Output.CustomMuxerSettings=\"Setelan Penyesuaian Muxer\"\nBasic.Settings.Output.NoSpaceFileName=\"Buat Nama Berkas tanpa Spasi\"\nBasic.Settings.Output.Adv.Rescale=\"Skala Ulang Output\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Dinonaktifkan\"\nBasic.Settings.Output.Adv.AudioTrack=\"Trek Audio\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Pengaturan Streaming\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Trek 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Trek 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Trek 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Trek 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Trek 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Trek 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Trek VOD Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"Pengaturan Pengkodean\"\nBasic.Settings.Output.Adv.Recording=\"Rekaman\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Pengaturan Perekaman\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Jenis Rekaman\"\nBasic.Settings.Output.Adv.Recording.Type=\"Jenis\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standar\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Modifikasi Output (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Gunakan pengkodean streaming)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Pemformatan Nama Berkas\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Timpa berkas jika berkas sudah ada\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Output Kustom (FFmpeg) disediakan tanpa penjagaan.\\nSebaiknya gunakan jenis perekaman \\\"Standar\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Jenis Output FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Output ke URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Output ke Berkas\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Format umum rekaman\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Semua Berkas\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Jalur Berkas atau URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format Kontainer\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Format Bawaan\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Deskripsi Format Kontainer\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Kodek Audio/Video menebak dari jalur Berkas atau URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Pengkodean Bawaan\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Nonaktifkan Pengkodean\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Pengkodean Video\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Setelan Pengkodean Video (jika ada)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Pengkodean Audio\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Setelan Pengkodean Audio (jika ada)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Setelan Muxer (jika ada)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Interval keyframe (frame)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Tampilkan semua kodek (meskipun jika berpotensi tidak kompatibel sekalipun)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Pengaturan FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Pemecah Berkas Otomatis\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Pemecah berdasarkan Waktu\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Pemecah berdasarkan Ukuran\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Hanya dipisah secara manual\"\nBasic.Settings.Output.SplitFile.Time=\"Pemecah Waktu\"\nBasic.Settings.Output.SplitFile.Size=\"Pemecah Ukuran\"\nScreenshot=\"Output Tangkapan Layar\"\nScreenshot.SourceHotkey=\"Sumber Tangkapan Layar yang Dipilih\"\nScreenshot.StudioProgram=\"Tangkapan Layar (Program)\"\nScreenshot.Preview=\"Tangkapan Layar (Pratinjau)\"\nScreenshot.Scene=\"Tangkapan Layar (Adegan)\"\nScreenshot.Source=\"Tangkapan Layar (Sumber)\"\nFilenameFormatting.TT.CCYY=\"Tahun, empat digit\"\nFilenameFormatting.TT.YY=\"Tahun, dua digit terakhir (00-99)\"\nFilenameFormatting.TT.MM=\"Bulan sebagai angka desimal (01-12)\"\nFilenameFormatting.TT.DD=\"Hari dalam sebulan, berawalan angka nol (01-31)\"\nFilenameFormatting.TT.hh=\"Jam dalam format 24j (00-23)\"\nFilenameFormatting.TT.mm=\"Menit (00-59)\"\nFilenameFormatting.TT.ss=\"Detik (00-59)\"\nFilenameFormatting.TT.Percent=\"Tanda %\"\nFilenameFormatting.TT.a=\"Nama singkat hari kerja\"\nFilenameFormatting.TT.A=\"Nama lengkap hari kerja\"\nFilenameFormatting.TT.b=\"Nama singkat bulan\"\nFilenameFormatting.TT.B=\"Nama lengkap bulan\"\nFilenameFormatting.TT.d=\"Hari dalam sebulan, berawalan angka nol (01-31)\"\nFilenameFormatting.TT.H=\"Jam dalam format 24j (00-23)\"\nFilenameFormatting.TT.I=\"Jam dalam format 12j (01-12)\"\nFilenameFormatting.TT.m=\"Bulan sebagai angka desimal (01-12)\"\nFilenameFormatting.TT.M=\"Menit (00-59)\"\nFilenameFormatting.TT.p=\"Penunjuk AM atau PM\"\nFilenameFormatting.TT.s=\"Waktu dalam detik sejak UNIX epoch\"\nFilenameFormatting.TT.S=\"Detik (00-59)\"\nFilenameFormatting.TT.y=\"Tahun, dua digit terakhir (00-99)\"\nFilenameFormatting.TT.Y=\"Tahun\"\nFilenameFormatting.TT.z=\"Offset ISO 8601 dari UTC dalam zona waktu\"\nFilenameFormatting.TT.Z=\"Nama zona waktu atau singkatan\"\nFilenameFormatting.TT.FPS=\"Frame per detik\"\nFilenameFormatting.TT.CRES=\"Resolusi dasar (kanvas)\"\nFilenameFormatting.TT.ORES=\"Resolusi output (diskalakan)\"\nFilenameFormatting.TT.VF=\"Format video\"\nBasic.Settings.Video.Adapter=\"Adaptor Video\"\nBasic.Settings.Video.BaseResolution=\"Resolusi Dasar (Kanvas)\"\nBasic.Settings.Video.ScaledResolution=\"Resolusi Output (Diskalakan)\"\nBasic.Settings.Video.DownscaleFilter=\"Filter Downscale\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Resolusi sudah pas, tidak perlu menurunkan skala]\"\nBasic.Settings.Video.FPSCommon=\"Nilai FPS Umum\"\nBasic.Settings.Video.FPSInteger=\"Nilai FPS Pembulatan\"\nBasic.Settings.Video.FPSFraction=\"Nilai FPS Pecahan\"\nBasic.Settings.Video.Numerator=\"Pembilang\"\nBasic.Settings.Video.Denominator=\"Penyebut\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Percobaan)\"\nBasic.Settings.Video.InvalidResolution=\"Nilai resolusi tidak sah. Harus [lebar]x[tinggi] (mis. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Saat ini output video sedang aktif. Mohon nonaktifkan semua output untuk mengubah pengaturan video.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (Tercepat, namun buram jika diskalakan)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bikubik (Diskalakan tajam, 16 sampel)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Diskalakan tajam, 36 sampel)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Area (Jumlah tertimbang, 4/6/9 sampel)\"\nBasic.Settings.Audio.Channels=\"Saluran\"\nBasic.Settings.Audio.Meters=\"Meter\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Cepat\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Sedang (PPM Tipe I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lambat (PPM Tipe II)\"\nBasic.Settings.Audio.PeakMeterType=\"Tipe Peak Meter\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True Peak (Penggunaan CPU lebih tinggi)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"PERINGATAN: Audio suara surround diaktifkan.\"\nBasic.Settings.Audio.MultichannelWarning=\"Jika streaming, periksa untuk melihat apakah layanan streaming Anda mendukung dua fitur ini. Yakni, ingest suara surround dan pemutaran suara surround. Contohnya, Facebook 360 Live mendukung penuh suara surround; YouTube Live mendukung ingest audio 5.1 (dan pemutaran nya di TV).\\n\\nFilter audio OBS kompatibel dengan suara surround, meskipun dukungan plugin VST tidak dijamin.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Aktifkan audio suara surround?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Anda yakin ingin mengaktifkan audio suara surround?\"\nBasic.Settings.Audio.Devices=\"Perangkat Global Audio\"\nBasic.Settings.Audio.DesktopDevice=\"Audio Desktop\"\nBasic.Settings.Audio.DesktopDevice2=\"Audio Desktop 2\"\nBasic.Settings.Audio.AuxDevice=\"Audio Mic/Auxiliary\"\nBasic.Settings.Audio.AuxDevice2=\"Audio Mic/Auxiliary 2\"\nBasic.Settings.Audio.AuxDevice3=\"Audio Mic/Auxiliary 3\"\nBasic.Settings.Audio.AuxDevice4=\"Audio Mic/Auxiliary 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Aktifkan Push-to-mute\"\nBasic.Settings.Audio.PushToMuteDelay=\"Delay Push-to-mute\"\nBasic.Settings.Audio.EnablePushToTalk=\"Aktifkan Push-to-talk\"\nBasic.Settings.Audio.PushToTalkDelay=\"Delay Push-to-talk\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Perangkat tidak terhubung atau tidak tersedia]\"\nBasic.Settings.Audio.Disabled=\"Dinonaktifkan\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Mode Audio Buffering Latensi Rendah (Untuk output Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"PERINGATAN: Audio buffering latensi rendah sedang aktif.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Mode audio buffering latensi rendah mungkin dapat menyebabkan gangguan audio atau berhenti terdengar dari beberapa sumber.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Aktifkan mode audio buffering latensi rendah?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Anda yakin ingin mengaktifkan mode audio buffering latensi rendah?\"\nBasic.Settings.Accessibility=\"Aksesibilitas\"\nBasic.Settings.Accessibility.ColorOverrides=\"Gunakan Warna yang Berbeda\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Sumber Garis Batas (Seleksi)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Sumber Garis Batas (Potong)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Sumber Garis Batas (Mengambang)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Pita Pengaduk Volume (-60 ke -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Pita Pengaduk Volume (-20 ke -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Pita Pengaduk Volume (-9 ke 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Pita Pengaduk Volume (-60 ke -20dB) (aktif)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Pita Pengaduk Volume (-20 ke -9dB) (aktif)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Pita Pengaduk Volume (-9 ke 0dB) (aktif)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Preset Warna\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Bawaan\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Sesuaikan\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternatif Buta Warna\"\nBasic.Settings.Advanced=\"Lanjutan\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Tampilkan peringatan output yang aktif saat keluar\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prioritas Proses\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Tinggi\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Diatas Normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Dibawah Normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Diam\"\nBasic.Settings.Advanced.FormatWarning=\"Peringatan: Format warna selain NV12/P010 utamanya ditujukan untuk perekaman, dan tidak dianjurkan saat streaming. Streaming dapat meningkatkan penggunaan CPU karena konversi format warna.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Peringatan: Format berpresisi tinggi lebih umum digunakan dengan ruang warna HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Peringatan: Rec. 2100 semestinya menggunakan format yang lebih presisi.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Format Warna\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 bidang)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 bidang)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 bidang)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 bidang)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 bidang)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 bidang)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 bidang)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Ruang Warna\"\nBasic.Settings.Advanced.Video.ColorRange=\"Rentang Warna\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Terbatas\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Penuh\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Tingkat Keputihan SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Tingkat Puncak Nominal HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Perangkat Pemantauan\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Bawaan\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Nonaktifkan peredaman audio Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Delay Streaming\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Durasi\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Pertahankan titik potong (meningkatkan delay) saat menghubungkan kembali\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Perkiraan Penggunaan Memori: %1 MB\"\nBasic.Settings.Advanced.Network=\"Jaringan\"\nBasic.Settings.Advanced.Network.Disabled=\"Protokol streaming yang saat ini dipilih tidak mendukung pengaturan mengubah jaringan.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Kaitkan ke IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Keluarga IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Aktifkan optimisasi jaringan\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Aktifkan TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Mencoba membuat output RTMP lebih ramah untuk aplikasi lain yang sensitif latensi di jaringan dengan mengatur laju transmisi.\\nIni dapat meningkatkan risiko frame yang drop pada koneksi yang tidak stabil.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Perilaku Fokus Pintasan\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Jangan pernah menonaktifkan pintasan\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Nonaktifkan pintasan saat jendela utama sedang fokus\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Nonaktifkan pintasan saat jendela utama sedang tidak fokus\"\nBasic.Settings.Advanced.AutoRemux=\"Remux otomatis ke %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(rekam sebagai mkv)\"\nBasic.AdvAudio=\"Properti Lanjutan Audio\"\nBasic.AdvAudio.ActiveOnly=\"Hanya Sumber Aktif Saja\"\nBasic.AdvAudio.Name=\"Nama\"\nBasic.AdvAudio.VolumeSource=\"Volume untuk '%1'\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix untuk '%1'\"\nBasic.AdvAudio.Balance=\"Keseimbangan\"\nBasic.AdvAudio.BalanceSource=\"Keseimbangan untuk '%1'\"\nBasic.AdvAudio.SyncOffset=\"Offset Sinkronisasi\"\nBasic.AdvAudio.SyncOffsetSource=\"Offset Sinkronisasi untuk '%1'\"\nBasic.AdvAudio.Monitoring=\"Pemantauan Audio\"\nBasic.AdvAudio.Monitoring.None=\"Monitor Nonaktif\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Monitor Saja (senyapkan output)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitor dan Output\"\nBasic.AdvAudio.MonitoringSource=\"Pemantauan Audio untuk '%1'\"\nBasic.AdvAudio.AudioTracks=\"Trek\"\nBasic.Settings.Hotkeys=\"Pintasan\"\nBasic.Settings.Hotkeys.Pair=\"Kombinasi tombol yang berbagi dengan '%1' bertindak sebagai pengalih\"\nBasic.Settings.Hotkeys.Filter=\"Saring\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Saring berdasarkan Pintasan\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Pintasan ini digunakan oleh satu atau beberapa tindakan lainnya, klik untuk melihat permasalahan nya\"\nBasic.Settings.Hotkeys.PleaseWait=\"Memuat pintasan, harap tunggu...\"\nBasic.Hotkeys.SelectScene=\"Beralih ke adegan\"\nBasic.SystemTray.Show=\"Tampilkan\"\nBasic.SystemTray.Hide=\"Sembunyikan\"\nBasic.SystemTray.Message.Reconnecting=\"Terputus. Menghubungkan ulang...\"\nHotkeys.Left=\"Kiri\"\nHotkeys.Right=\"Kanan\"\nHotkeys.Up=\"Atas\"\nHotkeys.Down=\"Bawah\"\nHotkeys.Space=\"Spasi\"\nHotkeys.NumpadMultiply=\"Numpad Kali\"\nHotkeys.NumpadDivide=\"Numpad Bagi\"\nHotkeys.NumpadAdd=\"Numpad Tambah\"\nHotkeys.NumpadSubtract=\"Numpad Kurang\"\nHotkeys.NumpadDecimal=\"Numpad Koma\"\nHotkeys.AppleKeypadNum=\"%1 (Papan Tombol)\"\nHotkeys.AppleKeypadMultiply=\"* (Papan Tombol)\"\nHotkeys.AppleKeypadDivide=\"/ (Papan Tombol)\"\nHotkeys.AppleKeypadAdd=\"+ (Papan Tombol)\"\nHotkeys.AppleKeypadSubtract=\"- (Papan Tombol)\"\nHotkeys.AppleKeypadDecimal=\". (Papan Tombol)\"\nHotkeys.AppleKeypadEqual=\"= (Papan Tombol)\"\nHotkeys.MouseButton=\"Tetikus %1\"\nMute=\"Senyapkan suara\"\nUnmute=\"Hidupkan suara\"\nSceneItemShow=\"Tampilkan '%1'\"\nSceneItemHide=\"Sembunyikan '%1'\"\nOutputWarnings.NoTracksSelected=\"Anda harus memilih setidaknya satu trek\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Galat Pengaturan Output\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Semua output harus ada sedikitnya satu trek audio yang dipilih.\"\nOutputWarnings.MP4Recording=\"Peringatan: Rekaman yang disimpan ke format MP4/MOV tidak akan dapat dipulihkan jika berkas tidak dapat diselesaikan (mis. Akibat BSOD, kehilangan daya, dll.). Jika Anda ingin merekam beberapa trek audio, pertimbangkan memakai format MKV dan remux rekaman ke MP4/MOV setelah selesai (File → Remux Rekaman)\"\nOutputWarnings.CannotPause=\"Peringatan: Rekaman tidak dapat dijeda jika pengkodean rekaman diatur ke \\\"(Gunakan pengkodean streaming)\\\"\"\nOutputWarnings.CodecIncompatible=\"Pilihan pengkodean audio atau video telah di atur ulang dikarenakan tidak kompatibel. Mohon pilih pengkodean yang kompatibel dari daftar berikut ini.\"\nCodecCompat.Incompatible=\"(Tidak kompatibel dengan %1)\"\nCodecCompat.CodecPlaceholder=\"Pilih Pengkodean...\"\nCodecCompat.ContainerPlaceholder=\"Pilih Format...\"\nCodecCompat.CodecMissingOnExit.Title=\"Tidak Ada Pengkodean Yang Dipilih\"\nCodecCompat.CodecMissingOnExit.Text=\"Setidaknya satu pengkodean video atau audio tidak diatur. Mohon pastikan untuk memilih pengkodean untuk kedua perekaman dan streaming nya.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Tidak Ada Format Yang Dipilih\"\nCodecCompat.ContainerMissingOnExit.Text=\"Tidak adak format perekaman yang dipilih. Mohon pilih sebuah format perekaman yang kompatibel dengan pengkodean stream yang dipilih.\"\nFinalScene.Title=\"Hapus Adegan\"\nFinalScene.Text=\"Harus ada setidaknya satu adegan.\"\nNoSources.Title=\"Tidak Ada Sumber\"\nNoSources.Text=\"Sepertinya Anda belum menambahkan sumber video apa pun, jadi Anda hanya akan menghasilkan layar kosong. Anda yakin ingin melakukan ini?\"\nNoSources.Text.AddSource=\"Anda dapat menambahkan sumber dengan meng-klik ikon + di bawah kotak Sumber di jendela utama kapan saja.\"\nNoSources.Label=\"Anda tidak memiliki sumber apapun.\\nKlik tombol + di bawah ini,\\natau klik kanan disini untuk menambahkan.\"\nChangeBG=\"Atur Warna\"\nCustomColor=\"Sesuaikan Warna\"\nBrowserSource.EnableHardwareAcceleration=\"Aktifkan Akselerasi Perangkat Keras Sumber Peramban\"\nAbout=\"Tentang\"\nAbout.Info=\"OBS Studio adalah perangkat lunak perekam video dan live streaming gratis dan sumber terbuka.\"\nAbout.Donate=\"Buat Sebuah Kontribusi\"\nAbout.GetInvolved=\"Ayo terlibat\"\nAbout.Authors=\"Pencipta\"\nAbout.License=\"Lisensi\"\nAbout.Error=\"Galat! Berkas tidak bisa dibaca.\\n\\nMasuk ke: %1\"\nAbout.Contribute=\"Dukung proyek OBS\"\nAddUrl.Title=\"Tambahkan Sumber melalui URL\"\nAddUrl.Text=\"Anda telah menyeret URL ke OBS. Ini secara otomatis akan menambahkan tautan sebagai sumber. Lanjutkan?\"\nResizeOutputSizeOfSource=\"Ubah ukuran output (ukuran sumber)\"\nResizeOutputSizeOfSource.Text=\"Resolusi dasar dan output akan diubah ukuran nya menjadi ukuran sumber saat ini.\"\nResizeOutputSizeOfSource.Continue=\"Apakah Anda ingin melanjutkan?\"\nPreviewTransition=\"Pratinjau Transisi\"\nImporter=\"Pengimpor Koleksi Adegan\"\nImporter.SelectCollection=\"Pilih Koleksi Adegan\"\nImporter.Collection=\"Koleksi Adegan\"\nImporter.HelpText=\"Tambahkan berkas ke jendela ini untuk mengimpor koleksi dari OBS atau program lain yang didukung.\"\nImporter.Path=\"Jalur Koleksi\"\nImporter.Program=\"Aplikasi yg Terdeteksi\"\nImporter.AutomaticCollectionPrompt=\"Cari Koleksi Adegan Secara Otomatis\"\nImporter.AutomaticCollectionText=\"OBS dapat mencari koleksi adegan yang dapat diimpor secara otomatis dari program pihak ketiga yang didukung. Apakah Anda ingin OBS menemukan koleksi untuk Anda secara otomatis?\\n\\nAnda dapat mengubah ini nanti di Pengaturan > Umum > Pengimpor.\"\nImporter.SelectFile=\"Telusuri...\"\nRestart=\"Aktifkan Ulang\"\nNeedsRestart=\"OBS Studio perlu diaktifkan ulang. Apakah Anda ingin mengaktifkan ulang sekarang?\"\nLoadProfileNeedsRestart=\"Profil mengandung pengaturan yang membutuhkan pengaktifan ulang OBS:\\n%1\\n\\nApakah Anda ingin mengaktifkan ulang OBS agar pengaturan tersebut di terapkan?\"\nContextBar.NoSelectedSource=\"Tak ada sumber yg dipilih\"\nContextBar.MediaControls.PlayMedia=\"Putar Media\"\nContextBar.MediaControls.PauseMedia=\"Jeda Media\"\nContextBar.MediaControls.StopMedia=\"Hentikan Media\"\nContextBar.MediaControls.RestartMedia=\"Putar Ulang Media\"\nContextBar.MediaControls.PlaylistNext=\"Selanjutnya dalam Daftar Putar\"\nContextBar.MediaControls.PlaylistPrevious=\"Sebelumnya dalam Daftar Putar\"\nContextBar.MediaControls.BlindSeek=\"Widget Media Seek\"\nYouTube.Auth.Ok=\"Otorisasi berhasil diselesaikan.\\nSekarang Anda dapat menutup halaman ini.\"\nYouTube.Auth.NoCode=\"Proses otorisasi tidak selesai.\"\nYouTube.Auth.NoChannels=\"Tidak ada saluran-saluran yang tersedia pada akun yang dipilih\"\nYouTube.Auth.WaitingAuth.Title=\"Otorisasi Pengguna YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Mohon selesaikan otorisasi di peramban luar Anda.<br>Jika peramban luar tidak terbuka, ikuti tautan ini dan selesaikan otorisasinya:<br>%1\"\nYouTube.AuthError.Text=\"Gagal memperoleh informasi saluran: %1.\"\nYouTube.Actions.WindowTitle=\"Pengaturan Siaran YouTube - Saluran: %1\"\nYouTube.Actions.CreateNewEvent=\"Buat Siaran Baru\"\nYouTube.Actions.ChooseEvent=\"Pilih Siaran Yang Ada\"\nYouTube.Actions.Title=\"Judul*\"\nYouTube.Actions.MyBroadcast=\"Siaran Saya\"\nYouTube.Actions.Description=\"Deskripsi\"\nYouTube.Actions.Privacy=\"Privasi*\"\nYouTube.Actions.Privacy.Private=\"Pribadi\"\nYouTube.Actions.Privacy.Public=\"Publik\"\nYouTube.Actions.Privacy.Unlisted=\"Tidak Publik\"\nYouTube.Actions.Category=\"Kategori\"\nYouTube.Actions.Thumbnail.SelectFile=\"Pilih berkas...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Tidak ada berkas yang dipilih\"\nYouTube.Actions.Thumbnail.ClearFile=\"Bersihkan\"\nYouTube.Actions.MadeForKids=\"Apakah video ini dibuat untuk anak-anak?*\"\nYouTube.Actions.MadeForKids.Yes=\"Ya, video ini dibuat untuk anak-anak\"\nYouTube.Actions.MadeForKids.No=\"Tidak, video ini tidak dibuat untuk anak-anak\"\nYouTube.Actions.AdditionalSettings=\"Pengaturan tambahan\"\nYouTube.Actions.Latency=\"Latensi\"\nYouTube.Actions.Latency.Low=\"Rendah\"\nYouTube.Actions.Latency.UltraLow=\"Ultra rendah\"\nYouTube.Actions.EnableAutoStart=\"Aktifkan Mulai Otomatis\"\nYouTube.Actions.EnableAutoStop=\"Aktifkan Berhenti Otomatis\"\nYouTube.Actions.AutoStartStop.TT=\"Menunjukkan apakah siaran terjadwal ini sebaiknya di mulai otomatis\"\nYouTube.Actions.EnableDVR=\"Aktifkan DVR\"\nYouTube.Actions.360Video=\"Video 360\"\nYouTube.Actions.ScheduleForLater=\"Jadwalkan nanti\"\nYouTube.Actions.RememberSettings=\"Ingat pengaturan ini\"\nYouTube.Actions.Create_Ready=\"Buat siaran\"\nYouTube.Actions.Create_GoLive=\"Buat siaran dan mulai streaming\"\nYouTube.Actions.Choose_Ready=\"Pilih siaran\"\nYouTube.Actions.Choose_GoLive=\"Pilih siaran dan mulai streaming\"\nYouTube.Actions.Create_Schedule=\"Jadwalkan siaran\"\nYouTube.Actions.Create_Schedule_Ready=\"Jadwalkan dan pilih siaran\"\nYouTube.Actions.Dashboard=\"Buka YouTube Studio\"\nYouTube.Actions.Error.Title=\"Kesalahan pembuatan siaran langsung\"\nYouTube.Actions.Error.Text=\"Kesalahan akses YouTube '%1'.<br/>Deskripsi detail kesalahan dapat ditemukan di <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Kesalahan akses YouTube. Mohon periksa koneksi jaringan Anda atau akses server YouTube Anda.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Kesalahan pembuatan siaran '%1'.<br/>Deskripsi detail kesalahan dapat ditemukan di <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Tidak ada streaming yang dibuat. Silakan hubungkan kembali akun Anda.\"\nYouTube.Actions.Error.YouTubeApi=\"Kesalahan API YouTube. Silakan lihat catatan berkas untuk informasi lebih lanjut.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Siaran yang dipilih tidak ditemukan.\"\nYouTube.Actions.Error.FileMissing=\"Berkas yang dipilih tidak ada.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Gagal membuka berkas yang dipilih.\"\nYouTube.Actions.Error.FileTooLarge=\"Berkas yang dipilih terlalu besar (Batas: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Gagal men-transisi siaran: %1<br/><br/>Jika galat ini masih terjadi <a href='https://studio.youtube.com/video/%2/livestreaming'>buka siaran di YouTube Studio</a> dan lakukan secara manual.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Siaran sedang transisi ke tahap percobaan, ini bisa memakan waktu. Mohon coba lagi dalam 10-30 detik.\"\nYouTube.Actions.EventsLoading=\"Memuat daftar acara...\"\nYouTube.Actions.EventCreated.Title=\"Acara Dibuat\"\nYouTube.Actions.EventCreated.Text=\"Acara berhasil dibuat.\"\nYouTube.Actions.Stream=\"Streaming\"\nYouTube.Actions.Stream.ScheduledFor=\"Dijadwalkan untuk %1\"\nYouTube.Actions.Stream.Resume=\"Lanjutkan streaming yang terputus\"\nYouTube.Actions.Stream.YTStudio=\"Otomatis dibuat oleh YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Membuat sebuah Siaran Langsung baru, silakan tunggu...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Diperlukan mulai manual\"\nYouTube.Actions.AutoStartStreamingWarning=\"Mulai otomatis dinonaktifkan untuk acara ini, klik \\\"Mulai Siaran\\\" untuk memulai siaran Anda.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Anda tidak akan dapat menghubungkannya kembali.<br>Streaming Anda akan dihentikan dan Anda tidak akan dalam siaran langsung lagi.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Live streaming tidak diaktifkan pada saluran YouTube yang dipilih.<br/><br/>Lihat <a href='https://www.youtube.com/features'>youtube.com/features</a> untuk informasi lebih lanjut.\"\nYouTube.Errors.livePermissionBlocked=\"Live streaming tidak tersedia pada saluran YouTube yang dipilih.<br/>Perlu diingat bahwa diperlukan waktu hingga 24 jam agar live streaming bisa dilakukan setelah mengaktifkannya di pengaturan saluran Anda.<br/><br/>Lihat <a href='https://www.youtube.com/features'>youtube.com/features</a> untuk detail lebih lanjut.\"\nYouTube.Errors.errorExecutingTransition=\"Gagal men-transisi karena kesalahan backend. Mohon coba lagi dalam beberapa detik kemudian.\"\nYouTube.Errors.errorStreamInactive=\"YouTube tidak menerima data untuk streaming Anda. Mohon periksa pengaturan Anda dan coba lagi.\"\nYouTube.Errors.invalidTransition=\"Transisi yang dicoba tidak sah. Ini mungkin terjadi karena streaming nya tidak menyelesaikan sebuah transisi sebelumnya. Mohon menunggu beberapa detik kemudian dan coba lagi.\"\nYouTube.DocksRemoval.Title=\"Bersihkan Dok Peramban Lawas YouTube\"\nYouTube.DocksRemoval.Text=\"Dok peramban berikut ini akan dihapus sebagai fitur usang:\\n\\n%1\\nSebagai gantinya, gunakan \\\"Dok/Ruang Live Control YouTube\\\".\"\nConfigDownload.WarningMessageTitle=\"Peringatan\"\nFailedToStartStream.MissingConfigURL=\"Tidak ada URL konfigurasi yang tersedia untuk layanan saat ini\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Kustomisasi URL RTMP tidak ditentukan\"\nFailedToStartStream.InvalidCustomConfig=\"Penyesuaian konfigurasi tidak sah\"\nFailedToStartStream.MissingCanvas=\"Sebuah konfigurasi kanvas tambahan tidak ditemukan\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Gagal untuk membuat layanan video multitrack\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Gagal untuk membuat video multitrack ber-output RTMP\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC tidak tersedia.\\n\\nGagal untuk mencari tipe pengkodean '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Gagal untuk membuat pengkodean video '%1' (tipe: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Gagal untuk mendapatkan info video OBS ketika membuat pengkodean '%1' (type: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Gagal untuk membuat pengkodean audio\"\nFailedToStartStream.NoRTMPURLInConfig=\"Konfigurasi tidak mengandung target URL streaming RTMP(S) \"\nFailedToStartStream.FallbackToDefault=\"Memulai streaming menggunakan %1 gagal; Anda ingin mencoba lagi menggunakan pengaturan mengkodekan tunggal?\"\nFailedToStartStream.ConfigRequestFailed=\"Tidak bisa mengambil konfigurasi dari %1<br><br>Galat HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Menerima nilai status yang tidak diketahui '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nAnda ingin melanjutkan streaming tanpa %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nAnda ingin melanjutkan streaming?\"\nFailedToStartStream.MissingEncoderConfigs=\"Konfigurasi mulai siaran tidak termasuk konfigurasi pengkodean\"\nFailedToStartStream.InvalidEncoderConfig=\"Konfigurasi mulai siaran termasuk sebuah konfigurasi pengkodean tidak sah\"\nFailedToStartStream.StatusMissingHTML=\"Permintaan mulai siaran membalas dengan sebuah galat yang tidak disebutkan\"\nFailedToStartStream.NoConfigSupplied=\"Kehilangan Konfigurasi\"\nMultitrackVideo.Info=\"%1 otomatis mengoptimisasikan setelan Anda untuk mengkodekan dan mengirimkan beberapa kualitas video. Memilih opsi ini akan mengirimkan informasi %2 tentang komputer dan pengaturan perangkat lunak Anda.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Pengaturan Tidak Cocok\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 saat ini tidak cocok dengan:\\n\\n%2\\nUntuk melanjutkan streaming dengan %1, nonaktifkan pengaturan yang tidak cocok:\\n\\n%3\\ndan Mulai Streaming lagi.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Nonaktifkan untuk streaming ini dan Mulai Streaming\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Perbarui Pengaturan dan Mulai Streaming\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"Saat ini %1 tidak kompatibel dengan set [Audio → Umum → Saluran] ke '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Audio → Umum → Saluran] perlu diatur ke set '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 membutuhkan berbagai pengaturan yang berbeda untuk [Audio → Umum → Saluran]\"\nBasic.OpenPluginManager=\"Pengelola Plugin\"\nPluginManager=\"Pengelola Plugin\"\nPluginManager.Restart=\"OBS Studio harus diaktifkan ulang untuk menerapkan perubahan plugin\"\nPluginManager.MissingPlugin=\"[PLUGIN TIDAK DITEMUKAN]\"\nPluginManager.Section.Discover=\"Telusuri\"\nPluginManager.Section.Manage=\"Terpasang\"\nPluginManager.Section.Updates=\"Pembaruan\"\nPluginManager.Section.Manage.Title=\"Kelola Plugin yang Aktif\"\n"
  },
  {
    "path": "frontend/data/locale/is-IS.ini",
    "content": "Language=\"Íslenska\"\nOK=\"Í Lagi\"\nApply=\"Nota\"\nCancel=\"Hætta við\"\nClose=\"Loka\"\nSave=\"Vista\"\nDiscard=\"Hætta við\"\nDisable=\"Óvirkja\"\nYes=\"Já\"\nNo=\"Nei\"\nAdd=\"Bæta við\"\nRemove=\"Fjarlægja\"\nRename=\"Endernefna\"\nSettings=\"Stillingar\"\nDisplay=\"Skjár\"\nName=\"Nafn\"\nExit=\"Hætta við\"\nShow=\"Sýna\"\nHide=\"Fela\"\nNew=\"Nýtt\"\nDuplicate=\"Afrita\"\nBack=\"Til baka\"\nDefaults=\"Sjálfgildir\"\nRestoreDefaults=\"Sjálfgildir\"\nNone=\"Enginn\"\nStudioMode.Program=\"Forrit\"\nDefault=\"(Sjálfgildi)\"\nFullscreen=\"Fullt Skjár\"\nOpenFile=\"Opna skrá\"\nList=\"Listi\"\nGrid=\"Grind\"\nTitleBar.SafeMode=\"ÖRUGGT MODE\"\nMacPermissions.Item.Microphone.Details=\"OBS krefst þessa leyfis ef þú vilt taka hljóðnemann þinn eða ytra hljóðtæki.\"\nSourceLeak.Title=\"Upprunakóðavillahreinsa \"\nSourceLeak.Text=\"Vandamál kom upp þegar skipt var um senusafn og ekki var hægt að afhlaða sumum þeira. Þetta vandamál stafar venjulega af viðbótum sem gefa ekki út á réttan hátt. Gakktu úr skugga um að allar viðbætur sem þú notar séu uppfærðar.\\n\\nOBS Studio mun nú hætta til að koma í veg fyrir hugsanlega ógögn\"\nBasic.VCam.OutputType.Program=\"Forrit(Sjálfval)\"\nBasic.VCam.OutputSelection.NoSelection=\"Ekkert valið fyrir útgáng gerð\"\nBasic.VCam.RestartWarning=\"Sýndarmyndavél verður endurræst til að taka við beitingum\"\nBasic.MainMenu.View.SceneListMode=\"Senustilling\"\nBasic.MainMenu.Help.ReleaseNotes=\"Útgefið minnismiðar\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Útgángstilingar villa\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Allir útgángar verða að hafa einn hjóðrás valinn\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Mistókst að búa til RTMP vídeó útgáng\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Mistókst að fá upplýsingar um OBS vídeó við að búa til kóðara\"\n"
  },
  {
    "path": "frontend/data/locale/it-IT.ini",
    "content": "Language=\"Italiano\"\nApply=\"Applica\"\nCancel=\"Annulla\"\nClose=\"Chiudi\"\nSave=\"Salva\"\nDiscard=\"Non salvare\"\nDisable=\"Disabilita\"\nYes=\"Sì\"\nAdd=\"Aggiungi\"\nRemove=\"Rimuovi\"\nRename=\"Rinomina\"\nInteract=\"Interagisci\"\nFilters=\"Filtri\"\nProperties=\"Proprietà\"\nMoveUp=\"Sposta su\"\nMoveDown=\"Sposta giù\"\nSettings=\"Impostazioni\"\nDisplay=\"Schermo\"\nName=\"Nome\"\nExit=\"Esci\"\nMixer=\"Mixer audio\"\nBrowse=\"Sfoglia\"\nDroppedFrames=\"Fotogrammi persi %1 (%2%)\"\nProjector.Open.Program=\"Apri programma proiettore\"\nProjector.Open.Preview=\"Apri anteprima proiettore\"\nProjector.Open.Scene=\"Apri scena proiettore\"\nProjector.Open.Source=\"Apri sorgente proiettore\"\nProjector.Open.Multiview=\"Apri vista multipla\"\nProjector.Display=\"Schermo: %1\"\nProjector.Window=\"Nuova finestra\"\nProjector.Title=\"Proiettore\"\nProjector.Title.Scene=\"Scena: %1\"\nProjector.Title.Source=\"Sorgente: %1\"\nProjector.Title.Multiview=\"Vista multipla\"\nProjector.ResizeWindowToContent=\"Adatta finestra al contenuto\"\nClear=\"Rimuovi\"\nRevert=\"Ripristina\"\nShow=\"Mostra\"\nHide=\"Nascondi\"\nUnhideAll=\"Mostra tutto\"\nUntitled=\"Senza titolo\"\nNew=\"Nuovo\"\nDuplicate=\"Duplica\"\nEnable=\"Abilita\"\nDisableOSXVSync=\"Disattiva il V-Sync di macOS\"\nResetOSXVSyncOnExit=\"Ripristina il V-Sync di macOS alla chiusura\"\nHighResourceUsage=\"Codifica in sovraccarico! È consigliabile diminuire i parametri delle impostazioni video o utilizzare un preset di codifica più veloce.\"\nTransition=\"Transizione\"\nQuickTransitions=\"Transizioni rapide\"\nFadeToBlack=\"Dissolvenza sul nero\"\nLeft=\"A sinistra\"\nRight=\"A destra\"\nTop=\"In alto\"\nBottom=\"In basso\"\nReset=\"Ripristina\"\nHours=\"Ore\"\nMinutes=\"Minuti\"\nSeconds=\"Secondi\"\nDeprecated=\"Obsoleto\"\nReplayBuffer=\"Buffer di replay\"\nImport=\"Importa\"\nExport=\"Esporta\"\nCopy=\"Copia\"\nPaste=\"Incolla\"\nPasteReference=\"Incolla (riferimento)\"\nPasteDuplicate=\"Incolla (duplicato)\"\nRemuxRecordings=\"Converti le registrazioni\"\nNext=\"Avanti\"\nBack=\"Indietro\"\nDefaults=\"Predefinite\"\nRestoreDefaults=\"Predefiniti\"\nHideMixer=\"Nascondi nel mixer\"\nTransitionOverride=\"Sovrascrivi transizione\"\nShowTransition=\"Visualizza transizione\"\nHideTransition=\"Transizione quando invisibile\"\nNone=\"Nessuno\"\nStudioMode.Preview=\"Anteprima\"\nStudioMode.Program=\"Programma\"\nStudioMode.PreviewSceneName=\"Anteprima: %1\"\nStudioMode.ProgramSceneName=\"Programma: %1\"\nShowInMultiview=\"Mostra in Vista multipla\"\nVerticalLayout=\"Layout verticale\"\nGroup=\"Gruppo\"\nDoNotShowAgain=\"Non mostrare più\"\nDefault=\"(predefinito)\"\nCalculating=\"Calcolo in corso...\"\nFullscreen=\"Schermo intero\"\nWindowed=\"In finestra\"\nRefreshBrowser=\"Aggiorna\"\nAspectRatio=\"Proporzioni <b>%1:%2</b>\"\nLockVolume=\"Blocca volume\"\nLogViewer=\"Visualizzatore Log\"\nShowOnStartup=\"Mostra all'avvio\"\nOpenFile=\"Apri file\"\nAddScene=\"Aggiungi scena\"\nAddSource=\"Aggiungi fonte\"\nRemoveScene=\"Rimuovi scena selezionata\"\nRemoveSource=\"Rimuovi fonti selezionate\"\nMoveSceneUp=\"Sposta scena su\"\nMoveSceneDown=\"Sposta scena giù\"\nMoveSourceUp=\"Sposta fonti su\"\nMoveSourceDown=\"Sposta fonti giù\"\nSourceProperties=\"Apri proprietà della fonte\"\nSourceFilters=\"Apri filtri della fonte\"\nMixerToolbarMenu=\"Menu mixer audio\"\nSceneFilters=\"Apri filtri scena\"\nList=\"Elenco\"\nGrid=\"Griglia\"\nAutomatic=\"Automatico\"\nComingSoon=\"Prossimamente\"\nPluginsFailedToLoad.Title=\"Errore caricamento plugin\"\nPluginsFailedToLoad.Text=\"Non sono stati caricati i seguenti plugin OBS :\\n\\n%1\\nAggiorna o rimuovi questi plugin.\"\nAlreadyRunning.Title=\"OBS è già in esecuzione\"\nAlreadyRunning.Text=\"OBS è già in esecuzione! A meno che non si intendeva effettuare questa operazione, chiudere tutte le istanze esistenti di OBS prima di provare a eseguirne una nuova. Se avete OBS impostato per minimizzarsi nell'area di notifica, si prega di controllare per vedere se è ancora in esecuzione.\"\nAlreadyRunning.LaunchAnyway=\"Avvia comunque\"\nCrashHandling.Dialog.Title=\"Rilevato crash OBS Studio\"\nCrashHandling.Labels.Text=\"OBS Studio non si è chiuso correttamente.\\n\\nVuoi eseguirlo in modalità provvisoria (plug-in di terze parti, scripting e websocket disabilitati)?\"\nCrashHandling.Labels.PrivacyNotice=\"È inoltre possibile scegliere di caricare automaticamente il rapporto di crash più recente su OBSProject.<br /><br />Prima di caricare eventuali file leggi l'<a href = 'https: //obsproject.com/privacy-policy'>iInformativa sulla privacy</a> e presta particolare attenzione alle parti relative ai caricamenti di file .\"\nCrashHandling.Checkbox.SendReport=\"Ho letto l'informativa sulla privacy e do il consenso al caricamento.\"\nCrashHandling.Buttons.LaunchSafe=\"Esegui in modalità provvisoria\"\nCrashHandling.Buttons.LaunchNormal=\"Esegui in modalità normale\"\nCrashHandling.Errors.UploadJSONError=\"Si è verificato un errore durante il tentativo di caricare il registro di crash più recente. Riprova più tardi.\"\nCrashHandling.Errors.Title=\"Errore caricamento del registro crash\"\nSafeMode.Restart=\"Vuoi riavviare OBS in Modalità sicura (verranno disattivati plugin di terze parti, script, e websocket)?\"\nSafeMode.RestartNormal=\"Vuoi riavviare OBS in modalità normale?\"\nChromeOS.Title=\"Piattaforma non supportata\"\nChromeOS.Text=\"OBS sembra essere in esecuzione all'interno di un contenitore ChromeOS. Questa piattaforma non è supportata.\"\nWine.Title=\"Rilevato Wine\"\nWine.Text=\"L'esecuzione di OBS in Wine non è supportata e molte funzionalità, come l'acquisizione o alcune sorgenti, non funzioneranno o avranno una capacità limitata.<br><br>Ti consigliamo invece di eseguire una versione nativa di OBS, ad esempio <a href='https://flathub.org/apps/details/com.obsproject.Studio'>la nostra versione Flatpak</a> o i pacchetti per il tuo sistema operativo.\"\nDockCloseWarning.Title=\"Hai chiuso la finestra di un pannello\"\nDockCloseWarning.Text=\"Hai appena chiuso una finestra agganciabile. Se vuoi mostrarla di nuovo, usa il menu Pannelli nella barra dei menu.\"\nExtraBrowsers=\"Pannelli Browser Personalizzati\"\nExtraBrowsers.Info=\"Aggiungi pannelli dando loro un nome e un URL, quindi clicca su Applica o Chiudi per aprire i pannelli. Puoi aggiungere o rimuovere pannelli in qualsiasi momento.\"\nExtraBrowsers.DockName=\"Nome del Pannello\"\nAuth.Authing.Title=\"Accesso in corso...\"\nAuth.Authing.Text=\"Accesso in corso con %1, attendi..\"\nAuth.AuthFailure.Title=\"Autenticazione non riuscita\"\nAuth.AuthFailure.Text=\"Impossibile eseguire l'accesso con %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Autenticazione richiesta\"\nAuth.InvalidScope.Text=\"I requisiti di autenticazione per %1 sono cambiati. Alcune funzionalità potrebbero non essere disponibili.\"\nAuth.LoadingChannel.Title=\"Caricamento informazioni del canale..\"\nAuth.LoadingChannel.Text=\"Caricamento informazioni del canale per %1, attendere..\"\nAuth.LoadingChannel.Error=\"Impossibile ottenere le informazioni del canale.\"\nAuth.ChannelFailure.Title=\"Impossibile caricare il canale\"\nAuth.ChannelFailure.Text=\"Impossibile caricare le informazioni del canale per %1\\n\\n%2: %3\"\nAuth.StreamInfo=\"Informazioni della diretta\"\nTwitchAuth.Stats=\"Statistiche di Twitch\"\nTwitchAuth.Feed=\"Feed attività di Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Impossibile interrogare il codice della diretta\"\nTwitchAuth.TwoFactorFail.Text=\"OBS non è stato in grado di connettersi al tuo account di Twitch. Assicurati che l'autenticazione a due fattori sia impostata nelle tue <a href='https://www.twitch.tv/settings/security'>impostazioni di sicurezza di Twitch</a> perché è necessaria per lo stream.\"\nRestreamAuth.Channels=\"Canali Restream\"\nCopy.Filters=\"Copia i filtri\"\nPaste.Filters=\"Incolla i filtri\"\nBrowserPanelInit.Title=\"Avvio del browser in corso...\"\nBrowserPanelInit.Text=\"Avvio del browser, attendi...\"\nBandwidthTest.Region=\"Regione\"\nBandwidthTest.Region.US=\"Stati Uniti\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Other=\"Altro\"\nBasic.AutoConfig=\"Configurazione automatica guidata\"\nBasic.AutoConfig.ApplySettings=\"Applica impostazioni\"\nBasic.AutoConfig.StartPage=\"Informazioni sull'utilizzo\"\nBasic.AutoConfig.StartPage.SubTitle=\"Specifica per cosa desideri utilizzare il programma\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Ottimizza per le dirette, le registrazioni sono meno importanti\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Ottimizza solo per le registrazioni, non faccio dirette\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Userò solo la fotocamera virtuale\"\nBasic.AutoConfig.VideoPage=\"Impostazioni video\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Specifica le impostazioni video che vorresti utilizzare\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Attuale (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Schermo %1 (%2×%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Attuale (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 o 30, ma preferisco 60 quando possibile\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 o 30, ma preferisco un'alta risoluzione\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Nota: la risoluzione (di base) dell'inquadratura non sarà necessariamente la stessa risoluzione per le dirette o per le registrazioni. La risoluzione delle dirette/registrazioni effettiva potrebbe essere scalata dalla risoluzione dell'inquadratura per ridurre le risorse utilizzate o i requisiti di velocità in bit.\"\nBasic.AutoConfig.StreamPage=\"Informazioni Diretta\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Per favore inserisci le informazioni per le dirette\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Connetti account (consigliato)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Disconnetti account\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Vuoi disconnettere l'account?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Questa modifica verrà applicata immediatamente. Sei sicuro di voler disconnettere il tuo account?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Ottieni Chiave Stream\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Altre Informazioni\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Usa la chiave Stream\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Usa chiave Stream (avanzato)\"\nBasic.AutoConfig.StreamPage.Service=\"Servizio\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Mostra tutti...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Personalizzato...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Chiave Stream\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: inserisci la frase segreta di cifratura.\\nRTMP: inserisci la chiave fornita dal servizio.\\nSRT: inserisci lo streamid se il servizio ne usa una.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Chiave del codificatore\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Portatore del token\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Account collegato\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Calcola la velocità in bit tramite un test di larghezza di banda (richiede alcuni minuti)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferisci la codifica hardware\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"La codifica hardware elimina la maggior parte dell'utilizzo della CPU, ma può richiedere più velocità in bit per ottenere lo stesso livello di qualità.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Avviso per la diretta\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Il test di larghezza di banda consiste nel trasmettere dati video casuali senza audio sul tuo canale. Se possibile, è consigliabile disattivare temporaneamente il salvataggio delle dirette e di impostare le dirette in modalità privata fino a quando non finisce il test. Vuoi continuare?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Prova %1\"\nBasic.AutoConfig.TestPage=\"Risultato finale\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Il programma sta ora eseguendo una serie di test per stimare le impostazioni ideali\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Test completato\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Esecuzione del test della larghezza di banda in corso, potrebbe richiedere alcuni minuti...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Nessun output trovato per il protocollo di questo servizio\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Connessione a: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Impossibile connettersi a qualunque server, verifica la tua connessione internet e riprova.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Test della larghezza di banda per: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Test della codifica delle dirette, può richiedere circa un minuto...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Test della codifica di registrazione, può richiedere circa un minuto...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Impossibile avviare la codifica\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Test della risoluzione %1×%2 a %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Codifica per le dirette\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Codificatore per la registrazione\"\nBasic.AutoConfig.TestPage.Result.Header=\"Il programma ha rilevato che queste impostazioni sono le più adatte per questo dispositivo:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Per utilizzare queste impostazioni, fai clic su «Applica le impostazioni». Per riprovare a configurare la procedura guidata, fai clic su «Indietro». Per configurare manualmente le impostazioni, fai clic su «Annulla» e apri le «Impostazioni».\"\nBasic.AutoConfig.Info=\"La configurazione guidata automatica determinerà le migliori impostazioni in base alle specifiche del computer e alla velocità di Internet.\"\nBasic.AutoConfig.RunAnytime=\"Questo può essere eseguito in qualsiasi momento andando nel menu Strumenti.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Risoluzione (scalata) della trasmissione  \"\nBasic.Stats=\"Statistiche\"\nBasic.Stats.CPUUsage=\"Utilizzo della CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Spazio su disco disponibile\"\nBasic.Stats.MemoryUsage=\"Memoria utilizzata\"\nBasic.Stats.AverageTimeToRender=\"Tempo medio creazione di un fotogramma\"\nBasic.Stats.SkippedFrames=\"Fotogrammi saltati (lag della codifica)\"\nBasic.Stats.MissedFrames=\"Fotogrammi persi (lag del rendering)\"\nBasic.Stats.Output.Stream=\"Diretta\"\nBasic.Stats.Output.Recording=\"Registrazione\"\nBasic.Stats.Status=\"Stato\"\nBasic.Stats.Status.Recording=\"Registrazione\"\nBasic.Stats.Status.Live=\"IN DIRETTA\"\nBasic.Stats.Status.Reconnecting=\"Riconnessione in corso\"\nBasic.Stats.Status.Inactive=\"Inattiva\"\nBasic.Stats.Status.Active=\"Attivo\"\nBasic.Stats.DroppedFrames=\"Fotogrammi persi (rete)\"\nBasic.Stats.MegabytesSent=\"Uscita dati totale\"\nBasic.Stats.Bitrate=\"Velocità in bit\"\nBasic.Stats.DiskFullIn=\"Disco pieno in (appross.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 ore, %2 minuti\"\nBasic.Stats.ResetStats=\"Ripristina le statistiche\"\nResetUIWarning.Title=\"Sei sicuro di voler ripristinare l'interfaccia utente?\"\nResetUIWarning.Text=\"La reimpostazione dell'interfaccia utente nasconderà i pannelli aggiuntivi.\\nSe vuoi che siano nuovamente visibili dovrai mostrare questi pannelli dal menu 'Pannelli'.\\n\\nSei sicuro di voler ripristinare l'interfaccia utente?\"\nUpdater.Title=\"Nuovo aggiornamento disponibile\"\nUpdater.Text=\"È disponibile un nuovo aggiornamento:\"\nUpdater.UpdateNow=\"Aggiorna ora\"\nUpdater.RemindMeLater=\"Ricordamelo più tardi\"\nUpdater.Skip=\"Salta versione\"\nUpdater.NoUpdatesAvailable.Title=\"Nessun aggiornamento disponibile\"\nUpdater.NoUpdatesAvailable.Text=\"Nessun aggiornamento attualmente disponibile\"\nUpdater.BranchNotFound.Title=\"Canale aggiornamenti rimosso\"\nUpdater.BranchNotFound.Text=\"Il canale di aggiornamento selezionato non è più disponibile, OBS è stato ripristinato al valore predefinito.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Controllo integrità non disponibile\"\nUpdater.RepairButUpdatesAvailable.Text=\"Il controllo dell'integrità del file è possibile solo per l'ultima versione disponibile.\\nPer verificare e aggiornare l'installazione di OBS usa Aiuto → Verifica aggiornamenti.\"\nUpdater.RepairConfirm.Title=\"Conferma controllo integrità\"\nUpdater.RepairConfirm.Text=\"L'avvio del controllo di integrità eseguirà la scansione dell'installazione di OBS alla ricerca di danneggiamenti e scaricherà nuovamente i file danneggiati/modificati.\\nPotrebbe volerci un momento.\\n\\nVuoi procedere?\"\nUpdater.FailedToLaunch=\"Avvio dello strumento di aggiornamento non riuscito\"\nQuickTransitions.SwapScenes=\"Scambia scene anteprima/programma dopo la transizione\"\nQuickTransitions.SwapScenesTT=\"Scambia l'anteprima e le scene del programma dopo la transizione (se la scena originale del programma esiste ancora).\\nQuesto non annullerà le modifiche che potrebbero essere state apportate alla scena originale del programma.\"\nQuickTransitions.DuplicateScene=\"Duplica la scena\"\nQuickTransitions.DuplicateSceneTT=\"Quando si modifica la stessa scena, consente di modificare la trasformazione/visibilità delle fonti senza modificare l'output del programma.\\nPer modificare le proprietà delle fonti senza modificare l'output del programma, abilitare 'Duplica fonti'.\\nLa modifica di questo valore ripristinerà la scena del programma corrente (se esiste ancora).\"\nQuickTransitions.EditProperties=\"Duplica le fonti\"\nQuickTransitions.EditPropertiesTT=\"Quando si modifica la stessa scena, consente di modificare le proprietà delle sorgenti senza modificare l'output del programma.\\nPuò essere utilizzato solo se è abilitato 'Duplica scena'.\\nAlcune fonti (come acquisizione o sorgenti multimediali) non lo supportano e non possono essere modificate separatamente.\\nLa modifica di questo valore ripristinerà la scena del programma attuale (se esiste ancora).\\n\\nAttenzione: poiché le sorgenti verranno duplicate, ciò potrebbe richiedere risorse video o di sistema aggiuntive.\"\nQuickTransitions.HotkeyName=\"Transizione rapida «%1»\"\nBasic.AddTransition=\"Aggiungi una transizione configurabile\"\nBasic.RemoveTransition=\"Rimuovi una transizione configurabile\"\nBasic.TransitionProperties=\"Proprietà di transizione\"\nBasic.SceneTransitions=\"Transizioni di scena\"\nBasic.TransitionDuration=\"Durata\"\nBasic.TogglePreviewProgramMode=\"Modalità studio\"\nBasic.EnablePreviewProgramMode=\"Abilita modalità Studio\"\nBasic.DisablePreviewProgramMode=\"Disabilita modalità Studio\"\nUndo.Undo=\"Annulla\"\nUndo.Redo=\"Ripeti\"\nUndo.Add=\"Aggiungi '%1'\"\nUndo.Delete=\"Elimina '%1'\"\nUndo.Rename=\"Rinomina %1\"\nUndo.SceneCollection.Switch=\"Passa a '%1'\"\nUndo.Item.Undo=\"Annulla %1\"\nUndo.Item.Redo=\"Ripeti %1\"\nUndo.Sources.Multi=\"Elimina %1 fonti\"\nUndo.Filters=\"Modifiche Filtro su '%1'\"\nUndo.Filters.Paste.Single=\"Incolla filtro '%1' a '%2'\"\nUndo.Filters.Paste.Multiple=\"Copia i filtri da '%1' a '%2'\"\nUndo.Transform=\"Trasforma fonti in '%1'\"\nUndo.Transform.Paste=\"Incolla trasformazione in '%1'\"\nUndo.Transform.Rotate=\"Rotazione in '%1'\"\nUndo.Transform.Reset=\"Reimposta trasformazione in '%1'\"\nUndo.Transform.HFlip=\"Rifletti orizzontalmente '%1'\"\nUndo.Transform.VFlip=\"Capovolgi verticalmente '%1'\"\nUndo.Transform.FitToScreen=\"Adatta allo schermo '%1'\"\nUndo.Transform.StretchToScreen=\"Allunga allo schermo in '%1'\"\nUndo.Transform.Center=\"Centra sullo schermo in '%1'\"\nUndo.Transform.VCenter=\"Centro verticale allo schermo in '%1'\"\nUndo.Transform.HCenter=\"Centro orizzontale allo schermo in '%1'\"\nUndo.Volume.Change=\"Cambio volume su '%1'\"\nUndo.Volume.Mute=\"Silenzia '%1'\"\nUndo.Volume.Unmute=\"Riattiva audio '%1'\"\nUndo.Balance.Change=\"Bilanciamento audio su '%1'\"\nUndo.SyncOffset.Change=\"Cambio sincronizzazione audio su '%1'\"\nUndo.MonitoringType.Change=\"Cambia monitoraggio audio su '%1'\"\nUndo.Mixers.Change=\"Cambia mixer audio su '%1'\"\nUndo.ForceMono.On=\"Abilita Forza mono su '%1'\"\nUndo.ForceMono.Off=\"Disabilita Mono forzato su '%1'\"\nUndo.Properties=\"Cambio proprietà su '%1'\"\nUndo.Scene.Duplicate=\"Duplica la scena '%1'\"\nUndo.ShowTransition=\"Transizione quando visibile su '%1'\"\nUndo.HideTransition=\"Nascondi transizione in '%1'\"\nUndo.ShowSceneItem=\"Mostra '%1' in '%2'\"\nUndo.HideSceneItem=\"Nascondi '%1' in '%2'\"\nUndo.ReorderSources=\"Riordina fonti in '%1'\"\nUndo.MoveUp=\"Sposta '%1' in su in '%2'\"\nUndo.MoveDown=\"Sposta '%1' giù in '%2'\"\nUndo.MoveToTop=\"Sposta '%1' in cima in '%2'\"\nUndo.MoveToBottom=\"Sposta '%1' in basso in '%2'\"\nUndo.PasteSource=\"Incolla fonti in '%1'\"\nUndo.PasteSourceRef=\"Incolla riferimenti fonte in '%1'\"\nUndo.GroupItems=\"Raggruppa elementi in '%1'\"\nTransitionNameDlg.Text=\"Inserisci il nome della transizione\"\nTransitionNameDlg.Title=\"Nome della transizione\"\nTitleBar.SafeMode=\"MODALITÀ SICURA\"\nTitleBar.PortableMode=\"Modalità portatile\"\nTitleBar.Profile=\"Profilo\"\nTitleBar.Scenes=\"Scene\"\nNameExists.Title=\"Il nome esiste già\"\nNameExists.Text=\"Il nome è già in uso.\"\nNoNameEntered.Title=\"Inserisci un nome valido\"\nNoNameEntered.Text=\"Non è possibile utilizzare nomi vuoti.\"\nConfirmStart.Title=\"Vuoi avviare la diretta?\"\nConfirmStart.Text=\"Sei sicuro di voler avviare una diretta?\"\nConfirmStop.Title=\"Vuoi interrompere la diretta?\"\nConfirmStop.Text=\"Sei sicuro di voler interrompere questa trasmissione?\"\nConfirmStopRecord.Title=\"Interrompere la registrazione?\"\nConfirmStopRecord.Text=\"Sei sicuro di voler interrompere la registrazione?\"\nConfirmBWTest.Title=\"Vuoi avviare il test della larghezza di banda?\"\nConfirmBWTest.Text=\"Hai configurato OBS in modalità di test di larghezza di banda. Questa modalità ti permette di eseguire i test della rete senza dover avviare una diretta. Quando hai finito di eseguire i test, ricordati di disattivare la modalità per permettere ai tuoi spettatori di guardare la diretta.\\n\\nVuoi continuare?\"\nConfirmExit.Title=\"Outpit attivi\"\nConfirmExit.Text=\"OBS è ancora attualmente attivo. Tutti gli streaming/registrazioni verranno interrotti.\"\nConfirmRemove.Title=\"Conferma la rimozione\"\nConfirmRemove.Text=\"Sei sicuro di voler rimuovere '%1'?\"\nConfirmRemove.TextMultiple=\"Sei sicuro di voler rimuovere %1 elementi?\"\nConfirmReset.Title=\"Ripristina proprietà\"\nConfirmReset.Text=\"Sei sicuro di voler ripristinare i valori delle proprietà ai loro valori predefiniti?\"\nOutput.StartStreamFailed=\"Impossibile avviare la diretta\"\nOutput.StartRecordingFailed=\"Avvio della registrazione non riuscito\"\nOutput.StartReplayFailed=\"Impossibile avviare il buffer di replay\"\nOutput.StartVirtualCamFailed=\"Impossibile avviare la videocamera virtuale\"\nOutput.StartFailedGeneric=\"Impossibile creare il file di uscita. Controlla il log per i dettagli.\\n\\nNota: se utilizzi la codifica NVENC o AMD, assicurati che i driver video siano aggiornati all'ultima versione.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Impossibile salvare i replay mentre si è in pausa\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Attenzione: i Replay non possono essere salvati mentre la registrazione è in pausa.\"\nOutput.ConnectFail.Title=\"Impossibile connettersi\"\nOutput.ConnectFail.BadPath=\"Percorso o URL di connessione non valido. Controlla le tue impostazioni per confermare che siano valide.\"\nOutput.ConnectFail.ConnectFailed=\"Connessione al server fallita\"\nOutput.ConnectFail.InvalidStream=\"Impossibile accedere al canale o al codice delle dirette specificato, controlla di aver inserito il codice delle dirette correttamente. Se è corretto, potrebbe esserci un problema con la connessione con al server.\"\nOutput.ConnectFail.HdrDisabled=\"L'output HDR è attualmente disabilitato per questo output.\"\nOutput.ConnectFail.Error=\"Si è verificato un errore non previsto durante la connessione al server. Controlla il file di log per più informazioni.\"\nOutput.ConnectFail.Disconnected=\"Disconnesso dal server.\"\nOutput.StreamEncodeError.Title=\"Errore di codifica\"\nOutput.StreamEncodeError.Msg=\"Si è verificato un errore di codifica durante lo streaming.\"\nOutput.StreamEncodeError.Msg.LastError=\"Durante lo streaming si è verificato un errore del codificatore:<br><br>%1\"\nOutput.RecordFail.Title=\"Avvio della registrazione non riuscito\"\nOutput.RecordFail.Unsupported=\"Il formato del file di uscita non è supportato o non supporta più di una traccia audio. Controlla le tue impostazioni e riprova.\"\nOutput.RecordNoSpace.Title=\"Spazio su disco insufficiente\"\nOutput.RecordNoSpace.Msg=\"Non c'è abbastanza spazio sul disco per continuare la registrazione.\"\nOutput.RecordError.Title=\"Errore di registrazione\"\nOutput.RecordError.Msg=\"Si è verificato un errore non specificato durante la registrazione.\"\nOutput.RecordError.EncodeErrorMsg=\"Si è verificato un errore di codifica durante la registrazione.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Si è verificato un errore del codificatore durante la registrazione:<br><br>%1\"\nOutput.BadPath.Title=\"Percorso del file non valido\"\nOutput.BadPath.Text=\"Impossibile aprire il percorso di registrazione configurato. Controlla il percorso di registrazione in Impostazioni → Output → Recording.\"\nOutput.NoBroadcast.Title=\"Nessuna trasmissione configurata\"\nOutput.NoBroadcast.Text=\"È necessario impostare una trasmissione prima di poter iniziare la diretta.\"\nOutput.BroadcastStartFailed=\"Impossibile avviare la trasmissione\"\nOutput.BroadcastStopFailed=\"Impossibile interrompere la trasmissione\"\nLogUploadDialog.Title=\"Caricamento registro eventi OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Prima di caricare qualsiasi file leggi l'<a href='https://obsproject.com/privacy-policy'>informativa sulla privacy</a> e la specifica sezione relativa ai caricamenti dei file.\"\nLogUploadDialog.Labels.Progress=\"Caricamento registro eventi. Attendi...\"\nLogUploadDialog.Labels.Description.AppLog=\"Il file del registro eventi è stato caricato. Ora puoi condividere l'URL per scopi di debug o supporto.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Il rapporto sul crash è stato caricato. Ora puoi condividere l'URL per scopi di debug.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Carica\"\nLogUploadDialog.Buttons.CopyURL=\"Copia URL registro eventi\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analizza file regsitro eventi\"\nLogUploadDialog.Buttons.RetryButton=\"Riprova\"\nLogUploadDialog.Errors.Template=\"Durante il tentativo di caricare il file si è verificato un errore:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Nessun file da caricare trovato o il file era vuoto.\"\nRemux.SourceFile=\"Registrazione di OBS\"\nRemux.TargetFile=\"File di destinazione\"\nRemux.Remux=\"Converti\"\nRemux.Stop=\"Interrompi la conversione\"\nRemux.ClearFinished=\"Rimuovi gli elementi completati\"\nRemux.ClearAll=\"Rimuovi tutti gli elementi\"\nRemux.OBSRecording=\"Registrazioni di OBS\"\nRemux.FinishedTitle=\"Conversione completata\"\nRemux.Finished=\"La registrazione è stata convertita\"\nRemux.FinishedError=\"La registrazione è stata convertita, ma il file potrebbe essere incompleto\"\nRemux.SelectRecording=\"Seleziona una registrazione di OBS…\"\nRemux.SelectTarget=\"Seleziona il file di destinazione…\"\nRemux.FileExistsTitle=\"Il file di destinazione è già presente\"\nRemux.FileExists=\"I seguenti file di destinazione sono già presenti. Vuoi sostituirli?\"\nRemux.ExitUnfinishedTitle=\"Conversione in corso\"\nRemux.ExitUnfinished=\"La conversione non è finita, interromperla ora potrebbe rendere il file di destinazione inutilizzabile.\\nSei sicuro di voler interrompere la conversione?\"\nRemux.HelpText=\"Trascina i file in questa finestra per convertirli, oppure seleziona una cella «Registrazione di OBS» vuota per cercare i file.\"\nRemux.NoFilesAddedTitle=\"Nessun remuxing per i file aggiunti\"\nRemux.NoFilesAdded=\"Nessun file è stato aggiunto nel remux. Scelgi una cartella contenente uno o più file video.\"\nMissingFiles=\"File non trovati\"\nMissingFiles.MissingFile=\"File non trovato\"\nMissingFiles.NewFile=\"Nuovo file\"\nMissingFiles.HelpText=\"Alcuni file risultano mancanti da quando hai usato per l'ultima volta OBS\"\nMissingFiles.Clear=\"<vuoto>\"\nMissingFiles.NumFound=\"Trovato %1 di %2\"\nMissingFiles.Search=\"Cerca cartella...\"\nMissingFiles.SelectFile=\"Seleziona file...\"\nMissingFiles.SelectDir=\"Seleziona la cartella in cui cercare\"\nMissingFiles.State=\"Stato\"\nMissingFiles.Missing=\"Mancante\"\nMissingFiles.Replaced=\"Sostituito\"\nMissingFiles.Cleared=\"Cancellato\"\nMissingFiles.Found=\"Trovato\"\nMissingFiles.AutoSearch=\"File simili trovati\"\nMissingFiles.AutoSearchText=\"OBS ha trovato file somiglianti ai file mancanti nella stessa cartella. Vuoi aggiungerli?\"\nMissingFiles.NoMissing.Title=\"Controllo file mancanti\"\nMissingFiles.NoMissing.Text=\"Nessun file sembra mancare.\"\nMacPermissions.MenuAction=\"Controlla autorizzazioni app...\"\nMacPermissions.Title=\"Controlla autorizzazioni app\"\nMacPermissions.Description=\"OBS Studio richiedel'autorizzazione per essere in grado di fornire alcune funzionalità.\\nTi consigliamo di abilitare questi permessi, ma non sono necessari per usare l'app.\\nÈ sempre possibile attivarli più tardi.\"\nMacPermissions.Description.OpenDialog=\"È possibile riaprire questa finestra di dialogo tramite il menu OBS Studio.\"\nMacPermissions.AccessGranted=\"Accesso concesso\"\nMacPermissions.RequestAccess=\"Richiesta accesso\"\nMacPermissions.OpenPreferences=\"Apri preferenze %1\"\nMacPermissions.Item.ScreenRecording=\"Registrazione dello schermo\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS richiede questo autorizzazione per poter catturare lo schermo.\"\nMacPermissions.Item.Camera=\"Fotocamera\"\nMacPermissions.Item.Camera.Details=\"Questa autorizzazione è necessaria per catturare contenuti da una webcam o da una scheda di cattura.\"\nMacPermissions.Item.Microphone=\"Microfono\"\nMacPermissions.Item.Microphone.Details=\"Se vuoi acquisire il segnale dal microfono o dispositivo audio esterno OBS richiede questa autorizzazione .\"\nMacPermissions.Item.InputMonitoring=\"Monitoraggio ingresso\"\nMacPermissions.Item.InputMonitoring.Details=\"Questo autorizzazione è necessaria affinché i tasti scelta rapida funzionino mentre OBS è in background.\"\nMacPermissions.Item.Accessibility=\"Accessibilità\"\nMacPermissions.Item.Accessibility.Details=\"Nelle installazioni più vecchie, OBS può essere elencato in \\\"Accessibilità\\\" anziché in \\\"Monitoraggio ingresso\\\".\"\nMacPermissions.Continue=\"Continua\"\nSourceLeak.Title=\"Errore pulizia sorgente\"\nSourceLeak.Text=\"Si è verificato un problema durante la modifica delle raccolte di scene e non è stato possibile scaricare alcune sorgenti.\\nQuesto problema è in genere causato da plug-in che non rilasciano correttamente le risorse.\\nAssicurati che tutti i plugin che stai usando siano aggiornati.\\n\\nOBS Studio ora verrà chiuso per prevenire qualsiasi potenziale danneggiamento dei dati.\"\nBasic.DesktopDevice1=\"Audio del desktop\"\nBasic.DesktopDevice2=\"Audio del desktop 2\"\nBasic.AuxDevice1=\"Microfono/disp. ausiliario\"\nBasic.AuxDevice2=\"Microfono/disp. ausiliario 2\"\nBasic.AuxDevice3=\"Microfono/disp. ausiliario 3\"\nBasic.AuxDevice4=\"Microfono/disp. ausiliario 4\"\nBasic.Scene=\"Scena\"\nBasic.DisplayCapture=\"Cattura lo schermo\"\nBasic.Main.PreviewConextMenu.Enable=\"Attiva l'anteprima\"\nBasic.Main.Preview.Disable=\"Disattiva l'anteprima\"\nScaleFiltering=\"Scala di filtraggio\"\nScaleFiltering.Point=\"Punto\"\nScaleFiltering.Bilinear=\"Bilineare\"\nScaleFiltering.Bicubic=\"Bicubico\"\nScaleFiltering.Area=\"Zona\"\nBlendingMethod=\"Metodo fusione\"\nBlendingMethod.Default=\"Predefinito\"\nBlendingMethod.SrgbOff=\"SRGB OFF\"\nBlendingMode=\"Modalità Di Sfumatura\"\nBlendingMode.Normal=\"Normale\"\nBlendingMode.Additive=\"Aggiungi\"\nBlendingMode.Subtract=\"Sottrai\"\nBlendingMode.Screen=\"Schermo\"\nBlendingMode.Multiply=\"Moltiplica\"\nBlendingMode.Lighten=\"Illuminato\"\nBlendingMode.Darken=\"Scuro\"\nDeinterlacing=\"Deinterlacciamento\"\nDeinterlacing.Discard=\"Scarta\"\nDeinterlacing.TopFieldFirst=\"Priorità nei livelli superiori\"\nDeinterlacing.BottomFieldFirst=\"Priorità nei livelli inferiori\"\nVolControl.SliderUnmuted=\"Barra del volume per «%1»:\"\nVolControl.SliderMuted=\"Barra del volume per «%1»: (attualmente muto)\"\nVolControl.Mute=\"Silenzia «%1'»\"\nVolControl.Properties=\"Proprietà di «%1»\"\nVolControl.UnassignedWarning.Title=\"Sorgente audio non assegnata\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" non è assegnato ad alcuna traccia audio e non sarà udibile nelle trasmissioni o nelle registrazioni.\\n\\nPer assegnare una sorgente audio a una traccia, apri 'Proprietà avanzate dell'audio' tramite il menu dal tasto destro o il pulsante ingranaggio nella \\\"barra degli strumenti\\\" del mixer.\"\nBasic.Main.AddSceneDlg.Title=\"Aggiunta di una scena\"\nBasic.Main.AddSceneDlg.Text=\"Inserisci il nome della scena\"\nBasic.Main.DefaultSceneName.Text=\"Scena %1\"\nBasic.Main.AddSceneCollection.Title=\"Aggiunta di una collezione di scene\"\nBasic.Main.AddSceneCollection.Text=\"Inserisci il nome della collezione di scene\"\nBasic.Main.RenameSceneCollection.Title=\"Rinomina della collezione di scene\"\nBasic.Main.RemigrateSceneCollection.Title=\"Aggiorna la risoluzione della collezione di scene\"\nBasic.Main.RemigrateSceneCollection.Text=\"Vuoi aggiornare la risoluzione della collezione di scene di \\\"%1\\\" per farla combaciare alla risoluzione del riquadro del profilo attuale %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Impossibile aggiornare la risoluzione collezione scene mentre le uscite sono attive.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Impossibile aggiornare la risoluzione delle collezioni di scene. La risoluzione originale è sconosciuta.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Riavvio non possibile: Modifica della risoluzione di OBS fallita.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Riavvio non possibile: La risoluzione attuale è già la risoluzione di base della collezione di scene.\"\nAddProfile.Title=\"Aggiunta di un profilo\"\nAddProfile.Text=\"Digita il nome del profilo\"\nAddProfile.WizardCheckbox=\"Mostra procedura guidata di configurazione automatica\"\nRenameProfile.Title=\"Rinomina del profilo\"\nBasic.Main.MixerRename.Title=\"Rinomina della fonte audio\"\nBasic.Main.MixerRename.Text=\"Inserisci il nome della fonte audio\"\nBasic.Main.PreviewDisabled=\"L'anteprima è attualmente disattivata\"\nBasic.SourceSelect=\"Crea/seleziona fonte\"\nBasic.SourceSelect.CreateNew=\"Crea nuova\"\nBasic.SourceSelect.AddExisting=\"Aggiungine una esistente\"\nBasic.SourceSelect.AddVisible=\"Rendi visibile la fonte\"\nBasic.Main.Sources.Visibility=\"Visibilità\"\nBasic.Main.Sources.VisibilityDescription=\"Controlla la visibilità di «%1» nella superficie\"\nBasic.Main.Sources.Lock=\"Blocca\"\nBasic.Main.Sources.LockDescription=\"Blocca la posizione e la scala di «%1» nella superficie\"\nBasic.PropertiesWindow=\"Proprietà di «%1»\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (autoseleziona: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Seleziona il colore\"\nBasic.PropertiesWindow.SelectFont=\"Seleziona il tipo di carattere\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Scegli un font\"\nBasic.PropertiesWindow.ConfirmTitle=\"Impostazioni modificate\"\nBasic.PropertiesWindow.Confirm=\"Sono state apportate delle modifiche. Vuoi salvarle?\"\nBasic.PropertiesWindow.NoProperties=\"Nessuna proprietà disponibile\"\nBasic.PropertiesWindow.AddFiles=\"Aggiungi dei file\"\nBasic.PropertiesWindow.AddDir=\"Aggiungi una cartella\"\nBasic.PropertiesWindow.AddURL=\"Aggiungi un percorso/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Aggiungi una cartella a «%1»\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Aggiungi dei file a «%1»\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Aggiungi una voce a «%1»\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Modifica la voce da «%1»\"\nBasic.PropertiesView.FPS.Simple=\"Valori FPS semplici\"\nBasic.PropertiesView.FPS.Rational=\"Valori FPS razionali\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Intervalli di FPS validi:\"\nBasic.PropertiesView.UrlButton.Text=\"Vuoi aprire questo collegamento nel browser web predefinito?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Apri URL\"\nBasic.InteractionWindow=\"Interazione con «%1»\"\nBasic.StatusBar.Reconnecting=\"Disconnesso, riconnessione in %2 secondi (tentativo %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Tentativo di riconnessione in corso... (tentativo %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Riconnessione eseguita\"\nBasic.StatusBar.Delay=\"Ritardo (%1 sec)\"\nBasic.StatusBar.DelayStartingIn=\"Ritardo (avvio dopo %1 sec)\"\nBasic.StatusBar.DelayStoppingIn=\"Ritardo (interruzione dopo %1 sec)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Ritardo (interruzione dopo %1 sec, avvio dopo %2 sec)\"\nBasic.StatusBar.RecordingSavedTo=\"Registrazione salvata in '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Buffer di replay salvato in '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Schermata salvata in '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Registrazione convertita automaticamente in '%1'\"\nBasic.Filters=\"Filtri\"\nBasic.Filters.AsyncFilters=\"Filtri audio/video\"\nBasic.Filters.AudioFilters=\"Filtri audio\"\nBasic.Filters.EffectFilters=\"Filtri degli effetti\"\nBasic.Filters.Title=\"Filtri per «%1»\"\nBasic.Filters.AddFilter.Title=\"Nome del filtro\"\nBasic.Filters.AddFilter.Text=\"Specifica il nome del filtro\"\nBasic.TransformWindow=\"Trasforma l'oggetto della scena\"\nBasic.TransformWindow.Position=\"Posizione\"\nBasic.TransformWindow.PositionX=\"Posizione X\"\nBasic.TransformWindow.PositionY=\"Posizione Y\"\nBasic.TransformWindow.Rotation=\"Rotazione\"\nBasic.TransformWindow.Size=\"Grandezza\"\nBasic.TransformWindow.Width=\"Larghezza\"\nBasic.TransformWindow.Height=\"Altezza\"\nBasic.TransformWindow.Alignment=\"Allinea posizioni\"\nBasic.TransformWindow.BoundsType=\"Tipo di casella di delimitazione\"\nBasic.TransformWindow.BoundsAlignment=\"Allineamento nel riquadro di delimitazione\"\nBasic.TransformWindow.Bounds=\"Dimensione casella di delimitazione\"\nBasic.TransformWindow.BoundsWidth=\"Larghezza Casella di Delimitazione\"\nBasic.TransformWindow.BoundsHeight=\"Altezza Casella di Delimitazione\"\nBasic.TransformWindow.CropToBounds=\"Ritaglia nel riquadro di delimitazione\"\nBasic.TransformWindow.Crop=\"Ritaglia\"\nBasic.TransformWindow.CropLeft=\"Ritaglia a sinistra\"\nBasic.TransformWindow.CropRight=\"Ritaglia a destra\"\nBasic.TransformWindow.CropTop=\"Ritaglia in alto\"\nBasic.TransformWindow.CropBottom=\"Ritaglia in basso\"\nBasic.TransformWindow.Alignment.TopLeft=\"In alto a sinistra\"\nBasic.TransformWindow.Alignment.TopCenter=\"In alto al centro\"\nBasic.TransformWindow.Alignment.TopRight=\"In alto a destra\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Al centro a sinistra\"\nBasic.TransformWindow.Alignment.Center=\"Al centro\"\nBasic.TransformWindow.Alignment.CenterRight=\"Al centro a destra\"\nBasic.TransformWindow.Alignment.BottomLeft=\"In basso a sinistra\"\nBasic.TransformWindow.Alignment.BottomCenter=\"In basso al centro\"\nBasic.TransformWindow.Alignment.BottomRight=\"In basso a destra\"\nBasic.TransformWindow.BoundsType.None=\"Nessun limite\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Soltanto la grandezza maggiore\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Scala all'interno dei limiti\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Scala ai limiti esterni\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Scala alla larghezza dei limiti\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Scala all'altezza dei limiti\"\nBasic.TransformWindow.BoundsType.Stretch=\"Allungare ai limiti\"\nBasic.TransformWindow.Title=\"Modifica trasformazione per '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Nessuna fonte selezionata\"\nBasic.Main.AddSourceHelp.Title=\"Impossibile aggiungere la fonte\"\nBasic.Main.AddSourceHelp.Text=\"Devi avere almeno una scena per aggiungere una fonte.\"\nBasic.Main.Scenes=\"Scene\"\nBasic.Main.Sources=\"Fonti\"\nBasic.Main.Source=\"Fonte\"\nBasic.Main.Controls=\"Controlli\"\nBasic.Main.PreparingStream=\"Preparazione...\"\nBasic.Main.Connecting=\"Connessione in corso...\"\nBasic.Main.StartRecording=\"Avvia la registrazione\"\nBasic.Main.StartReplayBuffer=\"Avvia il buffer di replay\"\nBasic.Main.SaveReplay=\"Salva il replay\"\nBasic.Main.StartStreaming=\"Avvia la trasmissione\"\nBasic.Main.StartBroadcast=\"Vai in diretta\"\nBasic.Main.StartVirtualCam=\"Avvia la fotocamera virtuale\"\nBasic.Main.StopRecording=\"Termina la registrazione\"\nBasic.Main.PauseRecording=\"Sospendi la registrazione\"\nBasic.Main.UnpauseRecording=\"Riprendi la registrazione\"\nBasic.Main.SplitFile=\"Dividi il file registrazione\"\nBasic.Main.AddChapterMarker=\"Aggiungi marcatore capitolo (solo ibrido MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Arresto della registrazione...\"\nBasic.Main.StopReplayBuffer=\"Termina il buffer di replay\"\nBasic.Main.StoppingReplayBuffer=\"Terminazione del buffer di replay in corso...\"\nBasic.Main.SetupBroadcast=\"Gestisci trasmissione\"\nBasic.Main.StopStreaming=\"Termina la diretta\"\nBasic.Main.StopBroadcast=\"Ferma trasmissione\"\nBasic.Main.AutoStopEnabled=\"(Stop automatico)\"\nBasic.Main.StoppingStreaming=\"Terminazione della diretta in corso...\"\nBasic.Main.ForceStopStreaming=\"Termina la diretta (annulla ritardo)\"\nBasic.Main.ShowContextBar=\"Mostra la barra degli strumenti delle fonti\"\nBasic.Main.HideContextBar=\"Nascondi la barra degli strumenti delle fonti\"\nBasic.Main.StopVirtualCam=\"Ferma la fotocamera virtuale\"\nBasic.Main.Group=\"Gruppo %1\"\nBasic.Main.GroupItems=\"Raggruppa gli elementi selezionati\"\nBasic.Main.Ungroup=\"Separa\"\nBasic.Main.GridMode=\"Modalità Griglia\"\nBasic.Main.ListMode=\"Modalità elenco\"\nBasic.Main.VirtualCamConfig=\"Configura fotocamera virtuale\"\nBasic.VCam.VirtualCamera=\"Fotocamera virtuale\"\nBasic.VCam.OutputType=\"Tipo output\"\nBasic.VCam.OutputSelection=\"Selezione output\"\nBasic.VCam.OutputType.Program=\"Programma (predefinito)\"\nBasic.VCam.OutputSelection.NoSelection=\"Nessuna selezione per questa tipologia di output\"\nBasic.VCam.RestartWarning=\"Per applicare la modifica sarà riavviata la camera virtuale \"\nBasic.MainMenu.File=\"&Archivio\"\nBasic.MainMenu.File.Export=\"&Esporta\"\nBasic.MainMenu.File.Import=\"&Importa\"\nBasic.MainMenu.File.ShowRecordings=\"Mostra le &registrazioni\"\nBasic.MainMenu.File.Remux=\"&Converti le registrazioni\"\nBasic.MainMenu.File.Settings=\"Impo&stazioni\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Mostra la cartella delle impostazioni\"\nBasic.MainMenu.File.ShowProfileFolder=\"Mostra la cartella dei profili\"\nBasic.MainMenu.File.ShowMissingFiles=\"Controlla i file mancanti\"\nBasic.MainMenu.File.Exit=\"&Esci\"\nBasic.MainMenu.Edit=\"&Modifica\"\nBasic.MainMenu.Edit.Undo=\"Ann&ulla\"\nBasic.MainMenu.Edit.Redo=\"&Ripeti\"\nBasic.MainMenu.Edit.LockPreview=\"B&locca l'anteprima\"\nBasic.MainMenu.Edit.Scale=\"Ridimen&sionamento dell'anteprima\"\nBasic.MainMenu.Edit.Scale.Window=\"Scala in base alle dimensioni della finestra\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Riquadro (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Uscita (%1×%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Scalato (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Zoom +\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Zoom -\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Ripristina zoom\"\nBasic.MainMenu.Edit.Transform=\"&Trasforma\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Modifica trasformazion&e...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Copia trasformazione\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Incolla trasformazione\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Ripristina trasformazione\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Ruota di 90 gradi in senso orario\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Ruota di 90 gradi in senso antiorario\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Ruota di 180 gradi\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Capovol&gi orizzontalmente\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Capovolgi &verticalmente\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"A&datta allo schermo\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Allunga per riempire lo &schermo\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centra nello schermo\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centra verticalmente\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centra orizzontalmente\"\nBasic.MainMenu.Edit.Order=\"&Ordina\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Sposta s&u\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Sposta &giù\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Spos&ta in cima\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Sposta in &fondo\"\nBasic.MainMenu.Edit.AdvAudio=\"Proprietà &audio avanzate\"\nBasic.MainMenu.View=\"&Visualizza\"\nBasic.MainMenu.View.Toolbars=\"Barre degli s&trumenti\"\nBasic.MainMenu.View.ListboxToolbars=\"Barre degli strumenti dei pannelli\"\nBasic.MainMenu.View.ContextBar=\"Barra degli strumenti delle fonti\"\nBasic.MainMenu.View.SourceIcons=\"&Icone fonti\"\nBasic.MainMenu.View.StatusBar=\"Barra di &stato\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Interfaccia a schermo intero\"\nBasic.MainMenu.View.ResetUI=\"&Ripristina UI\"\nBasic.MainMenu.View.AlwaysOnTop=\"Sempre in primo pi&ano\"\nBasic.MainMenu.View.SceneListMode=\"Modalità elenco scene\"\nBasic.MainMenu.Docks=\"&Pannelli\"\nBasic.MainMenu.Docks.ResetDocks=\"&Ripristina pannelli\"\nBasic.MainMenu.Docks.LockDocks=\"&Blocca pannelli\"\nBasic.MainMenu.Docks.SideDocks=\"&Pannelli ad altezza intera\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Pannelli browser personalizzati...\"\nBasic.MainMenu.Import=\"Importa...\"\nBasic.MainMenu.Export=\"Esporta...\"\nBasic.MainMenu.SceneCollection=\"Collezione &scene\"\nBasic.MainMenu.Profile=\"P&rofilo\"\nBasic.MainMenu.Profile.Import=\"Importa un profilo\"\nBasic.MainMenu.Profile.Export=\"Esporta il profilo\"\nBasic.MainMenu.SceneCollection.Import=\"Importa una collezione di scene\"\nBasic.MainMenu.SceneCollection.Export=\"Esporta la collezione di scene\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Ripristina risoluzione base\"\nBasic.MainMenu.SceneCollection.Migrate=\"Imposta risoluzione base\"\nBasic.MainMenu.Profile.Exists=\"Il profilo esiste già\"\nBasic.MainMenu.Tools=\"S&trumenti\"\nBasic.MainMenu.Help=\"&Aiuto\"\nBasic.MainMenu.Help.HelpPortal=\"&Portale di assistenza\"\nBasic.MainMenu.Help.Website=\"Visita il sito &web\"\nBasic.MainMenu.Help.Discord=\"Unisciti al server &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Novità del programma\"\nBasic.MainMenu.Help.Logs=\"File di &log\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Vi&sualizza i file di log\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"&Carica il file di log attuale\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Carica il file di log &precedente\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Visualizza il file di log attuale\"\nBasic.MainMenu.Help.ReleaseNotes=\"Note di rilascio\"\nBasic.MainMenu.Help.CheckForUpdates=\"Controlla gli aggiornamenti\"\nBasic.MainMenu.Help.Repair=\"Controlla integrità file\"\nBasic.MainMenu.Help.RestartSafeMode=\"Riavvia in modalità sicura\"\nBasic.MainMenu.Help.RestartNormal=\"Riavvia in modalità normale\"\nBasic.MainMenu.Help.CrashLogs=\"&Registro degli arresti anomali\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Vi&sualizza i registri dei crash\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Carica &Precedente Rapporto Crash\"\nBasic.MainMenu.Help.About=\"Inform&azioni\"\nBasic.Settings.ProgramRestart=\"Il programma deve essere riavviato perché questi cambiamenti abbiano effetto.\"\nBasic.Settings.ConfirmTitle=\"Conferma i cambiamenti\"\nBasic.Settings.Confirm=\"Hai dei cambiamenti non salvati. Vuoi salvarli?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 sta controllando alcune delle impostazioni della tua trasmissione\"\nBasic.Settings.General=\"Generale\"\nBasic.Settings.General.Language=\"Lingua\"\nBasic.Settings.General.Updater=\"Aggiornamenti\"\nBasic.Settings.General.UpdateChannel=\"Canale aggiornamento\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Disabilitato)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Predefinito)\"\nBasic.Settings.General.EnableAutoUpdates=\"Controlla automaticamente gli aggiornamenti all'avvio\"\nBasic.Settings.General.OpenStatsOnStartup=\"Apri la finestra delle statistiche all'avvio\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Nascondi finestre OBS dalla cattura dello schermo\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Questa impostazione nasconderà tutte le finestre OBS Studio (che non siano proiettori) dall'essere catturate da OBS e influenzerà \\naltre applicazioni, come conferenze, condivisione dello schermo, supporto remoto, screenshot e altri software di acquisizione.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Abilitando questa opzione nasconderai tutte le finestre di OBS Studio (che non siano proiettori) dall'essere catturate da OBS e influirà su altre applicazioni, come conferenze, condivisione dello schermo, supporto remoto, screenshot e altri software di acquisizione.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Mostra una finestra di conferma quando avvii una diretta\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Quando vuoi terminare una diretta visualizza finestra di conferma\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Mostra finestra di conferma quando si interrompe la registrazione\"\nBasic.Settings.General.Projectors=\"Proiettori\"\nBasic.Settings.General.HideProjectorCursor=\"Nascondi il cursore sopra i proiettori\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"I proiettori devono essere sempre in primo piano?\"\nBasic.Settings.General.Snapping=\"Aggancio delle fonti\"\nBasic.Settings.General.ScreenSnapping=\"Aggancia le fonti quando sono vicine ai bordi dello schermo\"\nBasic.Settings.General.CenterSnapping=\"Aggancia le fonti quando sono vicine al centro orizzontale e verticale\"\nBasic.Settings.General.SourceSnapping=\"Aggancia le fonti quando sono vicine ad altre fonti\"\nBasic.Settings.General.SnapDistance=\"Sensibilità dell'aggancio\"\nBasic.Settings.General.SpacingHelpers=\"Visualizza guide di allineamento pixel\"\nBasic.Settings.General.RecordWhenStreaming=\"Registra automaticamente quando sei in diretta\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Continua a registrare quando la diretta viene terminata\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Avvia automaticamente il buffer di replay durante le dirette\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Mantieni il buffer di replay attivo quando la diretta viene terminata\"\nBasic.Settings.General.SysTray=\"Area di notifica\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimizza nell'area di notifica all'avvio\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Riduci sempre il programma nell'area di notifica invece che nella barra delle applicazioni\"\nBasic.Settings.General.SaveProjectors=\"Salva i proiettori alla chiusura\"\nBasic.Settings.General.CloseExistingProjectors=\"Limita un proiettore a schermo intero per schermo\"\nBasic.Settings.General.Preview=\"Anteprima\"\nBasic.Settings.General.OverflowHidden=\"Nascondi overflow\"\nBasic.Settings.General.OverflowAlwaysVisible=\"L'overflow deve essere sempre visibile\"\nBasic.Settings.General.OverflowSelectionHidden=\"Visualizza l'overflow anche quando la fonte è invisibile\"\nBasic.Settings.General.Importers=\"Importatori\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Cerca luoghi conosciuti per le collezioni di scene durante l'importazione\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Esegui la transizione quando fai doppio clic sulla scena\"\nBasic.Settings.General.StudioPortraitLayout=\"Attiva il layout verticale\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Mostra le etichette di anteprima/programma\"\nBasic.Settings.General.Multiview=\"Vista multipla\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Fai clic per passare da una scena all'altra\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Visualizza i nomi delle scene\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Evidenzia le aree sicure (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Disposizione della vista multipla\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Orizzontale, in alto (8 scene)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Orizzontale, in basso (8 scene)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Verticale, a sinistra (8 scene)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Verticale, a destra (8 scene)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Orizzontale, in alto (18 scene)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Orizzontale, in alto (24 scene)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Solo scene (4 Scene)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Solo scene (9 Scene)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Solo scene (16 Scene)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Solo scene (25 Scene)\"\nBasic.Settings.General.ChannelName.stable=\"Stabile\"\nBasic.Settings.General.ChannelDescription.stable=\"Ultimo rilascio stabile\"\nBasic.Settings.General.ChannelName.beta=\"Beta / versioni Candidate\"\nBasic.Settings.General.ChannelDescription.beta=\"Versioni pre-rilascio potenzialmente instabili\"\nBasic.Settings.Appearance=\"Aspetto\"\nBasic.Settings.Appearance.General=\"Generale\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Stile\"\nBasic.Settings.Appearance.General.NoVariant=\"Nessuno stile disponibile\"\nBasic.Settings.Appearance.FontScale=\"Dimensione font\"\nBasic.Settings.Appearance.Density=\"Densità\"\nBasic.Settings.Appearance.Classic=\"Classica\"\nBasic.Settings.Appearance.Compact=\"Compatta\"\nBasic.Settings.Appearance.Normal=\"Normale\"\nBasic.Settings.Appearance.Comfortable=\"Confortevole\"\nBasic.Settings.Appearance.OptionsWarning=\"Alcune opzioni di aspetto non sono disponibili per questo stile.\"\nBasic.Settings.Stream=\"Dirette\"\nBasic.Settings.Stream.Destination=\"Destinazione\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Usa autenticazione\"\nBasic.Settings.Stream.Custom.Username=\"Nome utente\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: inserisci srp_username.\\nRTMP: inserisci il nome utente.\\nSRT: non utilizzato.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: inserisci srp_password.\\nRTMP: inserisci la password.\\nSRT: inserisci la passphrase.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Attiva la modalità test della larghezza di banda\"\nBasic.Settings.Stream.TTVAddon=\"Plugins per la chat di Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Nessuno\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV e FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Configurazione flusso mancante\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Apri le impostazioni\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL e Codice delle dirette sono mancanti.\\n\\nApri le impostazioni per inserire URL e codice delle dirette nella scheda 'stream'.\"\nBasic.Settings.Stream.MissingUrl=\"URL dello stream mancante.\\n\\nApri le impostazione per inserire l'URL nella scheda \\\"Stream\\\".\"\nBasic.Settings.Stream.MissingStreamKey=\"Chiave stream mancante.\\n\\nApri le impostazioni per inserire la chiave stream nella scheda \\\"Stream\\\".\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignora opzioni raccomandate di streaming\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Ignora le impostazioni raccomandate\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Attenzione: ignorare le limitazioni del servizio può degradare la qualità della trasmissione o impedirla completamente.\\n\\nContinuare?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Bitrate video massimo: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Bitrate audio massimo: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Risoluzione massima: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"FPS Massimi: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Specifica un Server personalizzato...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Server personalizzato\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Abilita %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Larghezza di banda della trasmissione massima\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automatica\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Numero massimo di traccie video\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automatico\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Abilita la scrittura della diretta su un file FLV (utilizza impostazioni di registrazione semplici)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Sovrascrivi la configurazione (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Abilita la sovrascrittura della configurazione\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Video multi traccia\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Tele aggiuntive...\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Simulcast  consente di codificare e inviare più qualità video. <a href='https://obsproject.com/kb/whip-streaming-guide'>Maggiori info</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Livelli totali\"\nBasic.Settings.Stream.AdvancedOptions=\"Opzioni avanzate\"\nBasic.Settings.Output=\"Uscita\"\nBasic.Settings.Output.Format=\"Formato di registrazione\"\nBasic.Settings.Output.Format.MKV=\"Video Matroska (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"MP4 ibrido (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV ibrido (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 frammentato (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV frammentato (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Un file MOV frammentato scrive la registrazione in pezzi e non richiede la stessa finalizzazione dei file MOV tradizionali.\\nQuesto assicura che il file rimanga riproducibile anche se la scrittura su disco viene interrotta, ad esempio, a causa di un BSOD o di una mancanza alimentazione.\\n\\nIl file frammentato porebbe non essere compatibile con tutti i programmi di riproduzione/modifica.\\nSe necessario usa 'File' → 'Remux registrazioni' per convertire il file in un formato più compatibile.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Un file MP4 frammentato scrive la registrazione in pezzi e non richiede la stessa finalizzazione dei file MP4 tradizionali.\\nQuesto assicura che il file rimanga riproducibile anche se la scrittura su disco viene interrotta, per esempio, a causa di un BSOD o di una mancanza di alimentazione.\\n\\nIl file frammentato potrebbe non essere compatibile con tutti i programmi di riproduzione/modifica.\\nSe necessario usa 'File' → 'Remux registrazioni' per convertire il file in un formato più compatibile.\"\nBasic.Settings.Output.Encoder.Video=\"Encoder video\"\nBasic.Settings.Output.Encoder.Audio=\"Encoder audio\"\nBasic.Settings.Output.SelectDirectory=\"Seleziona la cartella di registrazione\"\nBasic.Settings.Output.DynamicBitrate=\"Cambia in modo dinamico il bitrate per gestire la congestione\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Cambia in modo dinamico il bitrate per gestire la congestione (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Invece di saltare fotogrammi per ridurre la congestione, cambia dinamicamente il bitrate al volo.\\n\\nNota che questo potrebbe aumentare il ritardo agli spettatori se c'è una significante congestione improvvisa.\\nQuando il bitrate diminuisce, potrebbero volerci fino ad alcuni minuti per ripristinarlo.\\n\\nAttualmente è solo supportato per RTMP.\"\nBasic.Settings.Output.Mode=\"Modalità di uscita\"\nBasic.Settings.Output.Mode.Simple=\"Semplice\"\nBasic.Settings.Output.Mode.Adv=\"Avanzate\"\nBasic.Settings.Output.Mode.FFmpeg=\"Uscita FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Attiva il buffer di replay\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Tempo massimo di Replay\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Memoria Massima\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Uso della memoria stimato: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Attenzione: l'uso stimato della memoria di %1 MiB è più grande del massimo raccomandato di %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Impossibile stimare la memoria utilizzata. Imposta un limite massimo di memoria.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefisso del nome dei file del buffer di replay\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Suffisso\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Il buffer di riproduzione non può essere usato quando il tipo di registrazione è impostato su Uscita personalizzata (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Percorso della registrazione\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Qualità della registrazione\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"La stessa della diretta\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Alta qualità, dimensioni dei file medie\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Qualità indistinguibile, dimensioni dei file elevate\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Senza perdita di qualità (lossless), dimensioni dei file enormi\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Attenzione: il bitrate video in streaming sarà impostato a %1, che è il limite superiore per il servizio di streaming corrente.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Attenzione: il bitrate audio in streaming sarà impostato a %1, che è il limite superiore per il servizio di streaming corrente.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Attenzione: Le registrazioni non possono essere sospese se la qualità di registrazione è impostata su \\\"La stessa della diretta\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Attenzione: il formato di registrazione attualmente selezionato è incompatibile con il/i codificatore/i selezionato/i per la diretta.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Attenzione: registrare con un codificatore software a una qualità diversa dalla diretta richiederà un utilizzo aggiuntivo della CPU se esegui dirette e registri allo stesso tempo.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Attenzione: la qualità senza perdita di dati genera file di dimensioni enormi! La qualità senza perdite può occupare fino a 7 gigabyte di spazio sul disco al minuto a risoluzioni e framerate elevati. La modalità senza perdita dati non è consigliata per registrazioni lunghe, a meno che non si disponga di una quantità di spazio su disco molto grande. Il buffer di riproduzione non è disponibile quando si utilizza la qualità senza perdita dati.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Sei sicuro di volere utilizzare la qualità senza perdita?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Avviso sulla qualità senza perdita!\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264, preset con basso utilizzo della CPU, aumenta le dimensioni dei file)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Predefinito)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Incrocia la traccia VOD (usa traccia 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Traccia audio\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Risoluzione/framerate incompatibili\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Questo servizio di streaming non supporta la risoluzione e/o il framerate in uscita correnti. Verranno cambiati al valore più vicino compatibile:\\n\\n%1\\n\\nContinuare?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Risoluzione: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Encoder non compatibile\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Il servizio di streaming \\\"%1\\\" non supporta l'encoder \\\"%2\\\". \\nL'encoder verrà modificato in \\\"%3\\\".\\n\\nVuoi continuare?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Il servizio di streaming \\\"%1\\\" non supporta gli encoder \\\"%2\\\" e \\\"%3\\\".\\nQuesti encoder verranno modificati in \\\"%4\\\" e \\\"%5\\\".\\n\\nVuoi continuare?\"\nBasic.Settings.Output.VideoBitrate=\"Velocità in bit video\"\nBasic.Settings.Output.AudioBitrate=\"Velocità in bit audio\"\nBasic.Settings.Output.Reconnect=\"Riconnessione automatica\"\nBasic.Settings.Output.RetryDelay=\"Ritardo dei tentativi\"\nBasic.Settings.Output.MaxRetries=\"Tentativi massimi\"\nBasic.Settings.Output.Advanced=\"Abilita impostazioni personalizzate encoder (avanzate)\"\nBasic.Settings.Output.EncoderPreset=\"Preset della codifica\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (utilizzo della CPU basso, qualità minima)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (predefinito) (utilizzo della CPU medio, qualità standard)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (utilizzo della CPU elevato, alta qualità)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Parametri della codifica personalizzati\"\nBasic.Settings.Output.CustomMuxerSettings=\"Parametri del muxer personalizzati\"\nBasic.Settings.Output.NoSpaceFileName=\"Genera il nome dei file senza spazi\"\nBasic.Settings.Output.Adv.Rescale=\"Riscala l'uscita\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Disattivato\"\nBasic.Settings.Output.Adv.AudioTrack=\"Traccia audio\"\nBasic.Settings.Output.Adv.Streaming=\"Diretta\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Impostazioni della trasmissione\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Traccia 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Traccia 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Traccia 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Traccia 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Traccia 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Traccia 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Traccia VOD Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"Impostazioni del codificatore\"\nBasic.Settings.Output.Adv.Recording=\"Registrazione\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Impostazioni della registrazione\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tipo di registrazione\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tipo\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Uscita personalizzata (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(utilizza la codifica delle dirette)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Formattazione del nome dei file\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Sovrascrivi il file se già esistente\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Un uscita personalizzata (FFmpeg) è fornita senza protezioni.\\nConsidera di usare la tipologia di registrazione \\\"Standard\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Tipo di uscita FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Uscita verso un URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Output su file\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Formati di registrazione comuni\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Tutti i file\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Percorso del file o URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Formato del contenitore\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Formato predefinito\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Descrizione del formato del contenitore\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Il codec audio/video è stato intuito dal percorso del file o dall'URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Codifica predefinita\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Codifica disattivata\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Codifica video\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Parametri di codifica video (se presenti)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Codifica audio\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Parametri di codifica audio (se presenti)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Parametri del muxer (se presenti)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Intervallo fotogrammi chiave (in fotogrammi)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Visualizza tutti i codec (anche se potenzialmente incompatibili)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Impostazioni FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Divisione file automatica\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Dividi per durata\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Dividi per dimensione\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Dividi solo manualmente\"\nBasic.Settings.Output.SplitFile.Time=\"Durata divisione\"\nBasic.Settings.Output.SplitFile.Size=\"Dimensione divisione\"\nScreenshot=\"Screenshot Uscita\"\nScreenshot.SourceHotkey=\"Screenshot fonte selezionata\"\nScreenshot.StudioProgram=\"Screenshot (Programma)\"\nScreenshot.Preview=\"Screenshot (Anteprima)\"\nScreenshot.Scene=\"Screenshot (scena)\"\nScreenshot.Source=\"Schermata (fonte)\"\nFilenameFormatting.TT.CCYY=\"Anno, 4 cifre\"\nFilenameFormatting.TT.YY=\"Anno, ultime due cifre (00-99)\"\nFilenameFormatting.TT.MM=\"Mese come un numero decimale (01-12)\"\nFilenameFormatting.TT.DD=\"Giorno del mese, riempito con zeri (01-31)\"\nFilenameFormatting.TT.hh=\"Ora in formato 24h (00-23)\"\nFilenameFormatting.TT.mm=\"Minuti (00-59)\"\nFilenameFormatting.TT.ss=\"Secondi (00-59)\"\nFilenameFormatting.TT.Percent=\"Un segno %\"\nFilenameFormatting.TT.a=\"Nome abbreviato del giorno della settimana\"\nFilenameFormatting.TT.A=\"Nome completo del giorno della settimana\"\nFilenameFormatting.TT.b=\"Nome abbreviato del mese\"\nFilenameFormatting.TT.B=\"Nome del mese completo\"\nFilenameFormatting.TT.d=\"Giorno del mese, riempito con zeri (01-31)\"\nFilenameFormatting.TT.H=\"Ora in formato 24h (00-23)\"\nFilenameFormatting.TT.I=\"Ora in formato 12h (01-12)\"\nFilenameFormatting.TT.m=\"Mese come numero decimale (01-12)\"\nFilenameFormatting.TT.M=\"Minuto (00-59)\"\nFilenameFormatting.TT.p=\"AM o PM\"\nFilenameFormatting.TT.s=\"Tempo in secondi dall'epoca UNIX\"\nFilenameFormatting.TT.S=\"Secondi (00-59)\"\nFilenameFormatting.TT.y=\"Anno, ultime due cifre (00-99)\"\nFilenameFormatting.TT.Y=\"Anno\"\nFilenameFormatting.TT.z=\"ISO 8601 offset dall'ora UTC nel fuso orario\"\nFilenameFormatting.TT.Z=\"Nome del fuso orario o abbreviazione\"\nFilenameFormatting.TT.FPS=\"Fotogrammi al secondo\"\nFilenameFormatting.TT.CRES=\"Risoluzione di base (riquadro)\"\nFilenameFormatting.TT.ORES=\"Risoluzione in uscita (scalato)\"\nFilenameFormatting.TT.VF=\"Formato video\"\nBasic.Settings.Video.Adapter=\"Adattatore video\"\nBasic.Settings.Video.BaseResolution=\"Risoluzione di base (inquadratura)\"\nBasic.Settings.Video.ScaledResolution=\"Risoluzione effettiva di uscita (scalata)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtro di ridimensionamento della scalatura\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Risoluzioni corrispondono, nessun downscaling richiesto]\"\nBasic.Settings.Video.FPSCommon=\"Valori FPS comuni\"\nBasic.Settings.Video.FPSInteger=\"Valore FPS con numero intero\"\nBasic.Settings.Video.FPSFraction=\"Valore FPS con numero frazionario\"\nBasic.Settings.Video.Numerator=\"Numeratore\"\nBasic.Settings.Video.Denominator=\"Denominatore\"\nBasic.Settings.Video.Renderer=\"Modalità di rendering\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (sperimentale)\"\nBasic.Settings.Video.InvalidResolution=\"Valore della risoluzione non valido. Deve essere [larghezza]x[altezza] (ad esempio 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"L'uscita video è attualmente attiva. Interrompi qualunque uscita video per poter cambiare le impostazioni.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineare (il più veloce, ma sfocato se ridimensionato)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubica (scalatura accentuata, 16 campioni)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (scalatura accentuata, 36 campioni)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Area (importo peso, 4/6/9 campioni)\"\nBasic.Settings.Audio.SampleRate=\"Frequenza di campionamento\"\nBasic.Settings.Audio.Channels=\"Canali\"\nBasic.Settings.Audio.Meters=\"Metrica\"\nBasic.Settings.Audio.MeterDecayRate=\"Tasso di decadimento\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Veloce\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Medio (Tipo I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lento (Tipo II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Tipo di misuratore di picco\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Picco a campionamento\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Picco esatto (alto utilizzo della CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ATTENZIONE: L'audio surround è attivato.\"\nBasic.Settings.Audio.MultichannelWarning=\"In caso di streaming, controlla se il servizio di streaming supporta sia l'acquisizione del suono surround che la riproduzione del suono surround. Ad esempio, Facebook 360 Live supporta completamente il suono surround e YouTube Live supporta l'importazione audio 5.1 (e la riproduzione su TV).\\n\\nI filtri audio OBS sono compatibili con l'audio surround, anche se il supporto del plug-in VST non è garantito.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Vuoi attivare l'audio surround?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Sei sicuro di voler attivare l'audio surround?\"\nBasic.Settings.Audio.Devices=\"Dispositivi audio globali\"\nBasic.Settings.Audio.DesktopDevice=\"Audio desktop\"\nBasic.Settings.Audio.DesktopDevice2=\"Audio desktop 2\"\nBasic.Settings.Audio.AuxDevice=\"Audio mic/ausiliario\"\nBasic.Settings.Audio.AuxDevice2=\"Audio mic/ausiliario 2\"\nBasic.Settings.Audio.AuxDevice3=\"Audio mic/ausiliario 3\"\nBasic.Settings.Audio.AuxDevice4=\"Audio mic/ausiliario 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Attiva il push-to-mute\"\nBasic.Settings.Audio.PushToMuteDelay=\"Ritardo del push-to-mute\"\nBasic.Settings.Audio.EnablePushToTalk=\"Attiva il push-to-talk\"\nBasic.Settings.Audio.PushToTalkDelay=\"Ritardo del push-to-talk\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[il dispositivo non è collegato o non è disponibile]\"\nBasic.Settings.Audio.Disabled=\"Disattivato\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Modalità buffer audio a bassa latenza (per uscite Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ATTENZIONE: il buffer audio a bassa latenza è abilitato.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"La modalità di buffering audio a bassa latenza può causare il glitch dell'audio o interrompere la riproduzione da alcune fonti.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Vuoi attivare la modalità buffer audio a bassa latenza?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Sei sicuro di voler abilitare la modalità buffer audio a bassa latenza?\"\nBasic.Settings.Accessibility=\"Accessibilità\"\nBasic.Settings.Accessibility.ColorOverrides=\"Usa colori differenti\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Bordo sorgente (selezione)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Bordo sorgente (ritaglio)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Bordo sorgente (passaggio mouse)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Banda volume mixer (da -60 a -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Banda volume mixer (da -20 a -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Banda volume mixer (da -9 a 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Banda di volume del mixer (da -60 a -20dB) (attiva)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Banda volume mixer (da -20 a -9dB) (attiva)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Banda volume mixer (da -9 a 0dB) (attiva)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Profilo colore\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Predefinito\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Personalizzato\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternativa per daltonici\"\nBasic.Settings.Advanced=\"Avanzate\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"All'uscita del programma visualizza avvertimento output attivi\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Priorità del processo\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Alta\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Superiore al normale\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normale\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Inferiore al normale\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Bassa\"\nBasic.Settings.Advanced.FormatWarning=\"Attenzione: i formati di colore diversi da NV12/P010 sono principalmente destinati alla registrazione e non sono raccomandati durante la trasmissione. A causa della conversione del formato colore la trasmissione potrebbe comportare un maggiore utilizzo della CPU.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Avviso: i formati ad alta precisione sono più comunemente usati con gli spazi dei colori HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Attenzione: Rec. 2100 dovrebbe usare un formato con più precisione.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Formato colore\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8bit, 4.2:0, 2 piani)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8bit, 4.2:0, 3 piani)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8bit, 4.4:4, 3 piani)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10bit, 4.2:0, 2 piani)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10bit, 4.2:0, 3 piani)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 bit, 4:2:2, 2 piani)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 bit, 4:4:4, 2 piani)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8bit)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Spazio dei colori\"\nBasic.Settings.Advanced.Video.ColorSpace.601=\"Reg. 601\"\nBasic.Settings.Advanced.Video.ColorSpace.709=\"Reg. 709\"\nBasic.Settings.Advanced.Video.ColorRange=\"Gamma di colori\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limitato\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Intero\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Livello bianco SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Livello di picco nominale HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Dispositivo di monitoraggio\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Predefinito\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Disattiva la riduzione del volume di Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Ritardo delle dirette\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Durata\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Mantieni il punto di taglio (aumenta il ritardo) durante la riconnessione\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Utilizzo della memoria stimato: %1 MB\"\nBasic.Settings.Advanced.Network=\"Rete\"\nBasic.Settings.Advanced.Network.Disabled=\"Il protocollo di streaming attualmente selezionato non supporta la modifica delle impostazioni di rete.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Associa all'indirizzo IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Famiglia dell'IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Abilita ottimizzazioni di rete\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Abilita pacing TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Tentativi di rendere l'output RTMP compatibile ad altre applicazioni sensibili alla latenza sulla rete regolando il tasso di trasmissione.\\nPuò aumentare il rischio di fotogrammi persi su connessioni instabili.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Comportamento Focus Scorciatoie\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Non disabilitare mai le scorciatoie\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Disattiva le scorciatoie quando la finestra principale è in primo piano\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Disattiva le scorciatoie quando la finestra principale non è in primo piano\"\nBasic.Settings.Advanced.AutoRemux=\"Remuxa automaticamente in %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(registra in mkv)\"\nBasic.AdvAudio=\"Proprietà audio avanzate\"\nBasic.AdvAudio.ActiveOnly=\"Solo fonti attive\"\nBasic.AdvAudio.Name=\"Nome\"\nBasic.AdvAudio.VolumeSource=\"Volume per «%1»\"\nBasic.AdvAudio.MonoSource=\"Mix in mono per «%1»\"\nBasic.AdvAudio.Balance=\"Bilanciamento\"\nBasic.AdvAudio.BalanceSource=\"Bilanciamento per «%1»\"\nBasic.AdvAudio.SyncOffset=\"Ritardo di sincronizzazione\"\nBasic.AdvAudio.SyncOffsetSource=\"Scostamento di sincronizzazione per «%1»\"\nBasic.AdvAudio.Monitoring=\"Monitoraggio audio\"\nBasic.AdvAudio.Monitoring.None=\"Disattivato\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Monitora solamente (uscita audio disattivata)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitora l'audio e invia all'uscita\"\nBasic.AdvAudio.MonitoringSource=\"Monitoraggio audio per '%1'\"\nBasic.AdvAudio.AudioTracks=\"Tracce\"\nBasic.Settings.Hotkeys=\"Scorciatoie\"\nBasic.Settings.Hotkeys.Pair=\"Consigliamo di utilizzare la stessa combinazione di tasti insieme a «%1»\"\nBasic.Settings.Hotkeys.Filter=\"Filtro\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtra per Scorciatoia\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Questo tasto di scelta rapida è condiviso da una o più altre azioni, fare clic per mostrare i conflitti\"\nBasic.Settings.Hotkeys.PleaseWait=\"Caricamento tasti scelta rapida...\"\nBasic.Hotkeys.SelectScene=\"Scorri tra le scene\"\nBasic.SystemTray.Show=\"Visualizza\"\nBasic.SystemTray.Hide=\"Nascondi la finestra\"\nBasic.SystemTray.Message.Reconnecting=\"Disconnesso. Riconnessione in corso...\"\nHotkeys.Insert=\"Ins\"\nHotkeys.Delete=\"Canc\"\nHotkeys.Home=\"Inizio\"\nHotkeys.End=\"Fine\"\nHotkeys.PageUp=\"Pagina su\"\nHotkeys.PageDown=\"Pagina giù\"\nHotkeys.NumLock=\"Bloc num\"\nHotkeys.ScrollLock=\"Bloc scorr\"\nHotkeys.CapsLock=\"Bloc maiusc\"\nHotkeys.Backspace=\"Indietro\"\nHotkeys.Print=\"Stampa\"\nHotkeys.Pause=\"Pausa\"\nHotkeys.Left=\"Sinistra\"\nHotkeys.Right=\"Destra\"\nHotkeys.Up=\"Su\"\nHotkeys.Down=\"Giù\"\nHotkeys.Space=\"Barra spaziatrice\"\nHotkeys.NumpadNum=\"%1 (tn)\"\nHotkeys.NumpadMultiply=\"× (tn)\"\nHotkeys.NumpadDivide=\"÷ (tn)\"\nHotkeys.NumpadAdd=\"+ (tn)\"\nHotkeys.NumpadSubtract=\"- (tn)\"\nHotkeys.NumpadDecimal=\", (tn)\"\nHotkeys.AppleKeypadNum=\"%1 (tn)\"\nHotkeys.AppleKeypadMultiply=\"* (tastierino numerico)\"\nHotkeys.AppleKeypadDivide=\"÷ (tn)\"\nHotkeys.AppleKeypadAdd=\"+ (tn)\"\nHotkeys.AppleKeypadSubtract=\"- (tn)\"\nHotkeys.AppleKeypadDecimal=\", (tn)\"\nHotkeys.AppleKeypadEqual=\"= (tn)\"\nMute=\"Silenzia\"\nUnmute=\"Riattiva\"\nPush-to-talk=\"Premi-per-parlare\"\nSceneItemShow=\"Mostra «%1»\"\nSceneItemHide=\"Nascondi «%1»\"\nOutputWarnings.NoTracksSelected=\"Devi selezionare almeno una traccia\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Errore impostazioni output\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Tutti gli output devono avere almeno una traccia audio selezionata.\"\nOutputWarnings.MP4Recording=\"Attenzione: le registrazioni salvate in MP4/MOV non saranno recuperabili se il file non può essere completato (ad es. a seguito di un BSOD, perdite di corrente, ecc.). Se desideri registrare più tracce audio, consigliamo l'utilizzo di MKV e, una volta terminato, eseguire la conversione della registrazione in MP4/MOV (File → Converti le registrazioni)\"\nOutputWarnings.CannotPause=\"Attenzione: Le registrazioni non possono essere sospese se la codifica di registrazione è impostata su \\\"(Utilizza codifica flusso)\\\"\"\nOutputWarnings.CodecIncompatible=\"L'encoder audio o video è stata reimpostato a causa di una incompatibilità.\\nSeleziona nell'elenco un encoder compatibile.\"\nCodecCompat.Incompatible=\"(non compatibile con %1)\"\nCodecCompat.CodecPlaceholder=\"Seleziona encoder...\"\nCodecCompat.ContainerPlaceholder=\"Seleziona formato...\"\nCodecCompat.CodecMissingOnExit.Title=\"Nessun codificatore selezionato\"\nCodecCompat.CodecMissingOnExit.Text=\"Almeno un codificatore per video o audio non è impostato. Assicurarti di selezionare i codificatori sia per la registrazione che per la diretta.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Nessun formato selezionato\"\nCodecCompat.ContainerMissingOnExit.Text=\"Nessun formato di registrazione è stato selezionato. Seleziona un formato di registrazione che sia compatibile con il codificatore per la diretta selezionato.\"\nFinalScene.Title=\"Eliminazione della scena\"\nFinalScene.Text=\"Deve esserci almeno una scena.\"\nNoSources.Title=\"Nessuna fonte\"\nNoSources.Text=\"Sembra che tu non abbia ancora aggiunto nessuna sorgente video, quindi vedrai solo una schermata nera. Sei sicuro di volere fare questo?\"\nNoSources.Text.AddSource=\"Puoi aggiungere fonti facendo clic sull'icona + sotto il pannello delle fonti nella finestra principale in qualsiasi momento.\"\nNoSources.Label=\"Non hai nessuna fonte.\\nClicca il pulsante + qui sotto,\\no clicca col tasto destro per aggiungerne una.\"\nChangeBG=\"Seleziona il colore\"\nCustomColor=\"Colore personalizzato\"\nBrowserSource.EnableHardwareAcceleration=\"Attiva l'accelerazione hardware per la fonte browser\"\nAbout=\"Info sul programma\"\nAbout.Info=\"OBS Studio è un software gratuito e open source per la registrazione di video e trasmissioni in diretta.\"\nAbout.Donate=\"Effettua una donazione\"\nAbout.GetInvolved=\"Partecipa allo sviluppo\"\nAbout.Authors=\"Autori\"\nAbout.License=\"Licenza\"\nAbout.Error=\"Errore! Impossibile leggere il file.\\n\\nVai a: %1\"\nAbout.Contribute=\"Sostieni il progetto OBS\"\nAddUrl.Title=\"Aggiungi origine tramite URL\"\nAddUrl.Text=\"Hai spostato un URL in OBS. Questo aggiungerà automaticamente il collegamento come fonte. Continuare?\"\nResizeOutputSizeOfSource=\"Ridimensiona destinazione (dimensione fonte)\"\nResizeOutputSizeOfSource.Text=\"Le risoluzioni di base e destinazione saranno ridimensionate in base alle dimensioni della fonte attuale.\"\nResizeOutputSizeOfSource.Continue=\"Vuoi continuare?\"\nPreviewTransition=\"Anteprima transizione\"\nImporter=\"Importatore raccolta scene\"\nImporter.SelectCollection=\"Seleziona una collezione di scene\"\nImporter.Collection=\"Galleria Scene\"\nImporter.HelpText=\"Aggiungi file a questa finestra per importare le collezioni da OBS o da altri programmi supportati.\"\nImporter.Path=\"Percorso di raccolta\"\nImporter.Program=\"Applicazione rilevata\"\nImporter.AutomaticCollectionPrompt=\"Cerca automaticamente le collezioni di scena\"\nImporter.AutomaticCollectionText=\"OBS può trovare automaticamente le collezioni di scene importabili da programmi di terze parti supportati. Vuoi che OBS trovi automaticamente le collezioni per te?\\n\\nPuoi modificarlo successivamente in Impostazioni > Generali > Importatori.\"\nImporter.SelectFile=\"Sfoglia\"\nRestart=\"Riavvia\"\nNeedsRestart=\"OBS Studio deve essere riavviato. Vuoi riavviarlo ora?\"\nLoadProfileNeedsRestart=\"Il profilo contiene impostazioni che richiedono il riavvio di OBS:\\n%1\\n\\nVuoi riavviare OBS affinché queste impostazioni vengano applicate?\"\nContextBar.NoSelectedSource=\"Nessuna fonte selezionata\"\nContextBar.MediaControls.PlayMedia=\"Riproduci media\"\nContextBar.MediaControls.PauseMedia=\"Sospendi media\"\nContextBar.MediaControls.StopMedia=\"Ferma media\"\nContextBar.MediaControls.RestartMedia=\"Riavvia media\"\nContextBar.MediaControls.PlaylistNext=\"Successivo nella scaletta\"\nContextBar.MediaControls.PlaylistPrevious=\"Precedente nella scaletta\"\nContextBar.MediaControls.BlindSeek=\"Widget ricerca multimediale\"\nYouTube.Auth.Ok=\"Autorizzazione completata con successo.\\nOra puoi chiudere questa pagina.\"\nYouTube.Auth.NoCode=\"Il processo di autorizzazione non è stato completato.\"\nYouTube.Auth.NoChannels=\"Nessun canale disponibile sull'account selezionato\"\nYouTube.Auth.WaitingAuth.Title=\"Autorizzazione utente YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Completa l'autorizzazione nel browser esterno.<br>Se il browser esterno non si apre, apri questo collegamento e completa l'autorizzazione:<br>%1\"\nYouTube.AuthError.Text=\"Recupero delle informazioni del canale non riuscito: %1.\"\nYouTube.Actions.WindowTitle=\"Configurazione di trasmissione di YouTube - Canale: %1\"\nYouTube.Actions.CreateNewEvent=\"Crea nuova trasmissione\"\nYouTube.Actions.ChooseEvent=\"Seleziona trasmissione esistente\"\nYouTube.Actions.Title=\"Titolo*\"\nYouTube.Actions.MyBroadcast=\"Mia trasmissione\"\nYouTube.Actions.Description=\"Descrizione\"\nYouTube.Actions.Privacy=\"Riservatezza*\"\nYouTube.Actions.Privacy.Private=\"Privato\"\nYouTube.Actions.Privacy.Public=\"Pubblico\"\nYouTube.Actions.Privacy.Unlisted=\"Non in elenco\"\nYouTube.Actions.Category=\"Categoria\"\nYouTube.Actions.Thumbnail=\"Anteprima\"\nYouTube.Actions.Thumbnail.SelectFile=\"Seleziona file...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Nessun file selezionato\"\nYouTube.Actions.Thumbnail.ClearFile=\"Pulisci\"\nYouTube.Actions.MadeForKids=\"Questo video è adatto per bambini?*\"\nYouTube.Actions.MadeForKids.Yes=\"Sì, è adatto per bambini\"\nYouTube.Actions.MadeForKids.No=\"No, non è adatto per bambini\"\nYouTube.Actions.AdditionalSettings=\"Impostazioni aggiuntive\"\nYouTube.Actions.Latency=\"Latenza\"\nYouTube.Actions.Latency.Normal=\"Normale\"\nYouTube.Actions.Latency.Low=\"Bassa\"\nYouTube.Actions.Latency.UltraLow=\"Ultra bassa\"\nYouTube.Actions.EnableAutoStart=\"Abilita avvio automatico\"\nYouTube.Actions.EnableAutoStop=\"Abilita stop automatico\"\nYouTube.Actions.AutoStartStop.TT=\"Indica se questa trasmissione pianificata dovrebbe iniziare automaticamente\"\nYouTube.Actions.EnableDVR=\"Abilita DVR\"\nYouTube.Actions.360Video=\"Video 360\"\nYouTube.Actions.ScheduleForLater=\"Pianificato più tardi\"\nYouTube.Actions.RememberSettings=\"Ricorda queste impostazioni\"\nYouTube.Actions.Create_Ready=\"Crea trasmissione\"\nYouTube.Actions.Create_GoLive=\"Crea trasmissione e avvia la diretta\"\nYouTube.Actions.Choose_Ready=\"Seleziona trasmissione\"\nYouTube.Actions.Choose_GoLive=\"Seleziona trasmissione e avvia la diretta\"\nYouTube.Actions.Create_Schedule=\"Pianifica trasmissione\"\nYouTube.Actions.Create_Schedule_Ready=\"Pianifica e seleziona trasmissione\"\nYouTube.Actions.Dashboard=\"Apri YouTube Studio\"\nYouTube.Actions.Error.Title=\"Errore creazione trasmissione in diretta\"\nYouTube.Actions.Error.Text=\"Errore d'accesso a YouTube '%1'.<br/>Una descrizione dettagliata dell'errore è disponibile qui: <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Errore di accesso a YouTube.\\nControlla la connessione di rete o l'accesso al server YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Errore creazione trasmissione '%1'.<br/>Descrizione dettagliata dell'errore disponibile qui: <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Nessun flusso creato.\\nRi-collegati all'account.\"\nYouTube.Actions.Error.YouTubeApi=\"Errore API YouTube.\\nPer maggiori informazioni, consulta il file del regsitro eventi.\"\nYouTube.Actions.Error.BroadcastNotFound=\"La trasmissione selezionata non è stata trovata.\"\nYouTube.Actions.Error.FileMissing=\"Il file selezionato non esiste.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Apertura del file selezionato non riuscita.\"\nYouTube.Actions.Error.FileTooLarge=\"Il file selezionato è troppo grande (Limite: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"La transizione della trasmissione non è riuscita: %1<br/><br/>Se questo errore persiste <a href='https://studio.youtube.com/video/%2/livestreaming'>apri la trasmissione in YouTube Studio</a> e prova manualmente.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"La trasmissione sta per passare alla fase di prova, questo può richiedere un po' di tempo. Riprova tra 10-30 secondi.\"\nYouTube.Actions.EventsLoading=\"Caricamento elenco degli eventi...\"\nYouTube.Actions.EventCreated.Title=\"Evento creato\"\nYouTube.Actions.EventCreated.Text=\"Evento creato con successo.\"\nYouTube.Actions.Stream=\"Trasmissione\"\nYouTube.Actions.Stream.ScheduledFor=\"Pianificato per %1\"\nYouTube.Actions.Stream.Resume=\"Riprendi lo stream interrotto\"\nYouTube.Actions.Stream.YTStudio=\"Creato automaticamente da YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Creazione di una nuova trasmissione in diretta, attendi...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Avvio manuale richiesto\"\nYouTube.Actions.AutoStartStreamingWarning=\"L'avvio automatico è disabilitato per questo evento, per avviare la trasmissione fai clic su \\\"Vai in diretta\\\" .\"\nYouTube.Actions.AutoStopStreamingWarning=\"Non potrai riconnetterti.<br>La tua trasmissione sarà interrotta e non sarai più in diretta.\"\nYouTube.Errors.liveStreamingNotEnabled=\"La trasmissione in diretta non è abilitata sul canale YouTube selezionato.<br/><br/>Vedi <a href='https://www.youtube.com/features'>youtube.com/features</a> per ulteriori informazioni.\"\nYouTube.Errors.livePermissionBlocked=\"La trasmissione in diretta non è disponibile sul canale YouTube selezionato.<br/>Nota che potrebbero essere necessarie fino a 24 ore perché la trasmissione in diretta diventi disponibile dopo averlo abilitato nelle impostazioni del canale.<br/><br/>Vedi <a href='https://www.youtube.com/features'>youtube.com/features</a> per i dettagli.\"\nYouTube.Errors.errorExecutingTransition=\"Transizione fallita a causa di un errore di backend. Riprova tra pochi secondi.\"\nYouTube.Errors.errorStreamInactive=\"YouTube non sta ricevendo dati per la tua trasmissione. Controlla la tua configurazione e riprova.\"\nYouTube.Errors.invalidTransition=\"Il tentativo di transizione non è valido. Questo potrebbe essere dovuto al fatto che lo stream non ha terminato una transizione precedente. Si prega di attendere alcuni secondi e riprovare.\"\nYouTube.DocksRemoval.Title=\"Rimuovi i panelli YouTube legacy del browser\"\nYouTube.DocksRemoval.Text=\"Questi pannelli del browser verranno rimossi perché obsoleti:\\n\\n%1\\nUsa invece \\\"Pannello/Controllo regia YouTube Live\\\".\"\nConfigDownload.WarningMessageTitle=\"Attenzione\"\nFailedToStartStream.MissingConfigURL=\"Nessun URL di configurazione disponibile per il servizio corrente\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"L'URL RTMP personalizzato non è specificato\"\nFailedToStartStream.InvalidCustomConfig=\"Configurazione personalizzata non valida\"\nFailedToStartStream.MissingCanvas=\"Manca una tela extra configurata\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Creazione servizio video multitraccia fallito\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Errore nella creazione dell'output RTMP del video multi traccia\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC non disponibile.\\n\\nFallita la ricerca del tipo di codificatore '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Creazione del codificatore video '%1' (tipo: '%2') non riuscita\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Errore nell'ottenere le informazioni sul video di OBS durante la creazione del codificatore '%1' (tipo: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Creazione del codificatore audio non riuscita\"\nFailedToStartStream.NoRTMPURLInConfig=\"La configurazione non contiene l'URL RTMP(S) della destinazione della trasmissione\"\nFailedToStartStream.FallbackToDefault=\"L'avvio della trasmissione usando %1 è fallito; vuoi riprovare usando le impostazioni con la codifica singola?\"\nFailedToStartStream.ConfigRequestFailed=\"Non è stato possibile recuperare le configurazioni da %1<br><br>errore HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Ricevuto il valore di stato sconosciuto '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nVuoi continuare la diretta senza %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nVuoi continuare la diretta?\"\nFailedToStartStream.MissingEncoderConfigs=\"La configurazione di \\\"Vai in diretta\\\" non includeva le configurazioni del codificatore\"\nFailedToStartStream.InvalidEncoderConfig=\"GO Live Config ha incluso una configurazione dell'encoder non valida\"\nFailedToStartStream.StatusMissingHTML=\"La richiesta di \\\"Vai in diretta\\\" ha restituito un errore non specificato\"\nFailedToStartStream.NoConfigSupplied=\"Configurazione mancante\"\nMultitrackVideo.Info=\"%1 ottimizza automaticamente le tue impostazioni per codificare e inviare molteplici qualità di video. Selezionando questa opzione saranno inviate a %2 informazioni sul tuo computer e sulle impostazioni del software.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Impostazioni incompatibili\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 al momento non è compatibile con:\\n\\n%2\\nPer continuare la trasmissione con %1, disabilita le impostazioni incompatibili:\\n\\n%3\\ne Avvia la diretta di nuovo.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Disabilita per questa diretta e inizia la trasmissione\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Aggiorna le impostazioni e inizia la trasmissione\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 non è attualmente compatibile con [Audio → Generale → Canali] impostato su '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Audio → Generale → Canali] deve essere impostato su '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 richiede più configurazioni differenti per [Audio → Generale → Canali]\"\nBasic.OpenPluginManager=\"Gestione plugin\"\nPluginManager=\"Gestione plugin\"\nPluginManager.Restart=\"Per applicare le modifiche al plugin è necessario riavviare OBS Studio\"\nPluginManager.MissingPlugin=\"[PLUGIN NON TROVATO]\"\nPluginManager.Section.Discover=\"Sfoglia\"\nPluginManager.Section.Manage=\"Installato\"\nPluginManager.Section.Updates=\"Aggiornamenti\"\nPluginManager.Section.Manage.Title=\"Gestisci plugin abilitati\"\n"
  },
  {
    "path": "frontend/data/locale/ja-JP.ini",
    "content": "Language=\"日本語\"\nApply=\"適用\"\nCancel=\"キャンセル\"\nClose=\"閉じる\"\nSave=\"保存\"\nDiscard=\"破棄\"\nDisable=\"無効\"\nYes=\"はい\"\nNo=\"いいえ\"\nAdd=\"追加\"\nRemove=\"削除\"\nRename=\"名前を変更...\"\nInteract=\"対話 (操作)\"\nFilters=\"フィルタ\"\nProperties=\"プロパティ\"\nMoveUp=\"上へ移動\"\nMoveDown=\"下へ移動\"\nSettings=\"設定\"\nDisplay=\"ディスプレイ\"\nName=\"名称\"\nExit=\"終了\"\nMixer=\"音声ミキサー\"\nBrowse=\"参照\"\nMono=\"モノラル\"\nStereo=\"ステレオ\"\nDroppedFrames=\"ドロップしたフレーム %1 (%2%)\"\nProjector.Open.Program=\"プログラムプロジェクターを開く\"\nProjector.Open.Preview=\"プレビュープロジェクターを開く\"\nProjector.Open.Scene=\"シーンプロジェクターを開く\"\nProjector.Open.Source=\"ソースプロジェクターを開く\"\nProjector.Open.Multiview=\"マルチビューを開く\"\nProjector.Display=\"ディスプレイ: %1\"\nProjector.Window=\"新しいウィンドウ\"\nProjector.Title=\"プロジェクター\"\nProjector.Title.Scene=\"シーン: %1\"\nProjector.Title.Source=\"ソース: %1\"\nProjector.Title.Multiview=\"マルチビュー\"\nProjector.ResizeWindowToContent=\"ウィンドウをコンテンツに合わせる\"\nClear=\"クリア\"\nRevert=\"元に戻す\"\nShow=\"表示\"\nHide=\"非表示\"\nUnhideAll=\"すべて再表示\"\nUntitled=\"無題\"\nNew=\"新規...\"\nDuplicate=\"複製...\"\nEnable=\"有効にする\"\nDisableOSXVSync=\"macOS の V-Sync を無効にする\"\nResetOSXVSyncOnExit=\"終了時に macOS の V-Sync をリセットする\"\nHighResourceUsage=\"エンコードが高負荷です！ 映像設定を下げるかより高速なエンコードプリセットの使用を検討してください。\"\nTransition=\"トランジション\"\nQuickTransitions=\"クイックトランジション\"\nFadeToBlack=\"黒にフェード\"\nLeft=\"左\"\nRight=\"右\"\nTop=\"上\"\nBottom=\"下\"\nReset=\"リセット\"\nHours=\"時間\"\nMinutes=\"分\"\nSeconds=\"秒\"\nDeprecated=\"非推奨\"\nReplayBuffer=\"リプレイバッファ\"\nImport=\"インポート\"\nExport=\"エクスポート\"\nCopy=\"コピー\"\nPaste=\"貼り付け\"\nPasteReference=\"貼り付け (参照)\"\nPasteDuplicate=\"貼り付け (複製)\"\nRemuxRecordings=\"録画の再多重化\"\nNext=\"次へ\"\nBack=\"戻る\"\nDefaults=\"既定値\"\nRestoreDefaults=\"既定値\"\nHideMixer=\"ミキサーから非表示\"\nTransitionOverride=\"トランジションを上書き\"\nShowTransition=\"表示トランジション\"\nHideTransition=\"非表示トランジション\"\nNone=\"未設定\"\nStudioMode.Preview=\"プレビュー\"\nStudioMode.Program=\"プログラム\"\nStudioMode.PreviewSceneName=\"プレビュー: %1\"\nStudioMode.ProgramSceneName=\"プログラム: %1\"\nShowInMultiview=\"マルチビューで表示\"\nVerticalLayout=\"垂直レイアウト\"\nGroup=\"グループ化\"\nDoNotShowAgain=\"次回から表示しない\"\nDefault=\"(既定)\"\nCalculating=\"計算中...\"\nFullscreen=\"全画面表示\"\nWindowed=\"ウィンドウ表示\"\nRefreshBrowser=\"再読み込み\"\nAspectRatio=\"アスペクト比 <b>%1:%2</b>\"\nLockVolume=\"音量をロック\"\nLogViewer=\"ログビューア\"\nShowOnStartup=\"起動時に表示\"\nOpenFile=\"ファイルを開く\"\nAddScene=\"シーン追加\"\nAddSource=\"ソース追加\"\nRemoveScene=\"選択したシーンを削除\"\nRemoveSource=\"選択したソースを削除\"\nMoveSceneUp=\"シーンを上へ移動\"\nMoveSceneDown=\"シーンを下へ移動\"\nMoveSourceUp=\"ソースを上へ移動\"\nMoveSourceDown=\"ソースを下へ移動\"\nSourceProperties=\"ソースのプロパティを開く\"\nSourceFilters=\"ソースのフィルタを開く\"\nMixerToolbarMenu=\"音声ミキサーメニュー\"\nSceneFilters=\"シーンのフィルタを開く\"\nList=\"リスト\"\nGrid=\"グリッド\"\nAutomatic=\"自動\"\nComingSoon=\"近日公開\"\nPluginsFailedToLoad.Title=\"プラグイン読み込みエラー\"\nPluginsFailedToLoad.Text=\"以下のOBSプラグインの読み込みに失敗しました:\\n\\n%1\\nこれらのプラグインを更新または削除してください。\"\nAlreadyRunning.Title=\"OBSは既に実行中です\"\nAlreadyRunning.Text=\"OBSは既に実行中です！ 意図的でない限り新しいインスタンスを実行する前に既存のOBSインスタンスをシャットダウンしてください。 もしOBSをシステムトレイに最小化されるように設定されている場合は、まだ実行中であるかどうかを確認してください。\"\nAlreadyRunning.LaunchAnyway=\"とにかく起動する\"\nCrashHandling.Dialog.Title=\"OBS Studioのクラッシュが検出されました\"\nCrashHandling.Labels.Text=\"OBS Studioは正常にシャットダウンされませんでした。\\n\\nセーフモード (サードパーティプラグイン、スクリプト、WebSocketは無効) で実行しますか？\"\nCrashHandling.Labels.PrivacyNotice=\"最新のクラッシュレポートをOBSProjectに自動的にアップロードすることもできます。<br /><br />ファイルをアップロードする前に <a href='https://obsproject.com/privacy-policy'>プライバシーポリシー</a> をお読みいただき特にファイルのアップロードに関する部分に注意してください。\"\nCrashHandling.Checkbox.SendReport=\"プライバシーポリシーを読みアップロードに同意します。\"\nCrashHandling.Buttons.LaunchSafe=\"セーフモードで実行\"\nCrashHandling.Buttons.LaunchNormal=\"通常モードで実行\"\nCrashHandling.Errors.UploadJSONError=\"最新のクラッシュログのアップロード中にエラーが発生しました。 後でもう一度お試しください。\"\nCrashHandling.Errors.Title=\"クラッシュログアップロードエラー\"\nSafeMode.Restart=\"OBSをセーフモード (サードパーティプラグイン、スクリプト、WebSocketは無効) で再起動しますか？\"\nSafeMode.RestartNormal=\"OBSを通常モードで再起動しますか？\"\nChromeOS.Title=\"サポートされていないプラットフォーム\"\nChromeOS.Text=\"OBSはChromeOSのコンテナ内で実行されているようです。このプラットフォームはサポートされていません。\"\nWine.Title=\"Wineが検出されました\"\nWine.Text=\"WineでOBSを実行することはサポートされておらず、キャプチャやデバイスソースなどの多くの機能は動作しないか、限定されます。<br><br>代わりにOBSのネイティブバージョン、例えば私たちの <a href='https://flathub.org/apps/details/com.obsproject.Studio'>Flatpakバージョン</a> やお使いのオペレーティングシステムのパッケージを実行することをお勧めします。\"\nDockCloseWarning.Title=\"ドッキング可能ウィンドウを閉じる\"\nDockCloseWarning.Text=\"ドッキング可能なウィンドウを閉じました。もう一度表示したい場合は、メニューバーのドックメニューを使用してください。\"\nExtraBrowsers=\"カスタムブラウザドック\"\nExtraBrowsers.Info=\"名前とURLを指定してドックを追加し 適用 または 閉じる をクリックすることでドックが開きます。 ドックはいつでも追加または削除できます。\"\nExtraBrowsers.DockName=\"ドック名\"\nAuth.Authing.Title=\"認証中...\"\nAuth.Authing.Text=\"%1 で認証中、お待ちください...\"\nAuth.AuthFailure.Title=\"認証失敗\"\nAuth.AuthFailure.Text=\"%1 の認証に失敗しました:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"認証が必要\"\nAuth.InvalidScope.Text=\"%1 の認証要件が変更されました。一部の機能が利用できない場合があります。\"\nAuth.LoadingChannel.Title=\"チャンネル情報を読み込み中...\"\nAuth.LoadingChannel.Text=\"%1 のチャンネル情報を読み込み中、しばらくお待ちください...\"\nAuth.LoadingChannel.Error=\"チャンネル情報を取得できません。\"\nAuth.ChannelFailure.Title=\"チャンネルの読み込みに失敗\"\nAuth.ChannelFailure.Text=\"%1 のチャンネル情報の読み込みに失敗しました\\n\\n%2: %3\"\nAuth.Chat=\"チャット\"\nAuth.StreamInfo=\"配信情報\"\nTwitchAuth.Stats=\"Twitch統計\"\nTwitchAuth.Feed=\"Twitchアクティビティフィード\"\nTwitchAuth.TwoFactorFail.Title=\"ストリームキーを照会できませんでした\"\nTwitchAuth.TwoFactorFail.Text=\"OBSはあなたのTwitchアカウントに接続できませんでした。 配信をするには2段階認証が必要なので <a href='https://www.twitch.tv/settings/security'>Twitchセキュリティ設定</a> で2段階認証が設定されていることを確認してください。\"\nCopy.Filters=\"フィルタをコピー\"\nPaste.Filters=\"フィルタを貼り付け\"\nBrowserPanelInit.Title=\"ブラウザを初期化中...\"\nBrowserPanelInit.Text=\"ブラウザを初期化中、しばらくお待ちください...\"\nBandwidthTest.Region=\"地域\"\nBandwidthTest.Region.US=\"アメリカ\"\nBandwidthTest.Region.EU=\"ヨーロッパ\"\nBandwidthTest.Region.Asia=\"アジア\"\nBandwidthTest.Region.Other=\"その他\"\nBasic.AutoConfig=\"自動構成ウィザード\"\nBasic.AutoConfig.ApplySettings=\"設定を適用\"\nBasic.AutoConfig.StartPage=\"使用情報\"\nBasic.AutoConfig.StartPage.SubTitle=\"プログラムの使用目的を指定する\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"配信のために最適化し、録画は二次的なものとする\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"録画のために最適化し、配信はしない\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"仮想カメラのみ使用する\"\nBasic.AutoConfig.VideoPage=\"映像設定\"\nBasic.AutoConfig.VideoPage.SubTitle=\"使用する映像設定を指定\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"現在の値を使用 (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"ディスプレイ %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"現在の値を使用 (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60または30のいずれか、可能なら60を優先\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60または30のいずれか、高解像度を優先\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"注: キャンバス (基本) 解像度は必ずしも配信や録画の解像度と同じではありません。 実際の配信/録画の解像度はリソースの使用量やビットレート要求を減らすためにキャンバス解像度から縮小される場合があります。\"\nBasic.AutoConfig.StreamPage=\"配信情報\"\nBasic.AutoConfig.StreamPage.SubTitle=\"配信情報を入力してください\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"アカウント接続 (推奨)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"アカウントを切断\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"アカウントを切断しますか？\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"この変更はすぐに適用されます。 アカウントを切断してもよろしいですか？\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"ストリームキーを取得\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"詳細情報\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"ストリームキーを使用\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"ストリームキーを使用する (高度)\"\nBasic.AutoConfig.StreamPage.Service=\"サービス\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"すべて表示...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"カスタム...\"\nBasic.AutoConfig.StreamPage.Server=\"サーバー\"\nBasic.AutoConfig.StreamPage.StreamKey=\"ストリームキー\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: 暗号化パスフレーズ を入力します。\\nRTMP: サービスから提供された キー を入力します。\\nSRT: サービスが streamid を使用する場合は streamid を入力します。\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"エンコーダキー\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Bearerトークン\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"接続されたアカウント\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"帯域幅のテストでビットレートを推定する (数分かかることがあります)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"ハードウェアエンコードを優先する\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"ハードウェアエンコードはCPU使用率がほとんどなくなりますが、同レベルの品質を得るためにはより多くのビットレートが必要になります。\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"配信の警告\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"帯域幅のテストがランダム化された音声なしの映像データを自分のチャンネルに配信しようとしています。可能であれば、一時的に配信の映像保存をオフにしてテストが完了するまで配信を非公開に設定することをおすすめします。 続行しますか？\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"%1 をテスト\"\nBasic.AutoConfig.TestPage=\"最終結果\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"プログラムは最も理想的な設定を推定するための一連のテストを現在実行中です\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"テスト完了\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"帯域幅テストの実行、これには数分かかることがあります...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"このサービスのプロトコルの出力が見つかりませんでした\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"接続先: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"サーバーに接続できませんでした。インターネット接続を確認してからもう一度お試しください。\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"帯域幅のテスト: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"配信エンコーダのテスト中、これには数分かかることがあります...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"録画エンコーダのテスト中、これには数分かかることがあります...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"エンコーダの起動に失敗しました\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"テスト中 %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"配信エンコーダ\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"録画エンコーダ\"\nBasic.AutoConfig.TestPage.Result.Header=\"プログラムはこれらの推定設定があなたにとって最も理想的であると判断しました:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"これらの設定を使用するには「設定を適用」をクリックしてください。ウィザードを再設定してやり直すには「戻る」をクリックしてください。設定を手動で行うには「キャンセル」をクリックして設定を開いてください。\"\nBasic.AutoConfig.Info=\"自動構成ウィザードはお使いのコンピュータのスペックとインターネットの速度に基づいて最適な設定を決定します。\"\nBasic.AutoConfig.RunAnytime=\"これはツールメニューからいつでも実行することができます。\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"配信 (スケーリング) 解像度\"\nBasic.Stats=\"統計\"\nBasic.Stats.CPUUsage=\"CPU使用率\"\nBasic.Stats.HDDSpaceAvailable=\"ディスク空き容量\"\nBasic.Stats.MemoryUsage=\"メモリ使用量\"\nBasic.Stats.AverageTimeToRender=\"フレームのレンダリングにかかる平均時間\"\nBasic.Stats.SkippedFrames=\"エンコードラグによりスキップされたフレーム\"\nBasic.Stats.MissedFrames=\"レンダリングラグにより逃したフレーム\"\nBasic.Stats.Output.Stream=\"配信\"\nBasic.Stats.Output.Recording=\"録画\"\nBasic.Stats.Status=\"状態\"\nBasic.Stats.Status.Recording=\"録画中\"\nBasic.Stats.Status.Live=\"配信中\"\nBasic.Stats.Status.Reconnecting=\"再接続中\"\nBasic.Stats.Status.Inactive=\"非アクティブ\"\nBasic.Stats.Status.Active=\"アクティブ\"\nBasic.Stats.DroppedFrames=\"ドロップしたフレーム (ネットワーク)\"\nBasic.Stats.MegabytesSent=\"出力データの合計\"\nBasic.Stats.Bitrate=\"ビットレート\"\nBasic.Stats.DiskFullIn=\"ディスクが一杯になるまで (約)\"\nBasic.Stats.DiskFullIn.Text=\"%1 時間 %2 分\"\nBasic.Stats.ResetStats=\"統計をリセット\"\nResetUIWarning.Title=\"UIをリセットしてもよろしいですか？\"\nResetUIWarning.Text=\"UIをリセットすると追加のドックは非表示になります。 これらのドックを表示させたい場合はドックメニューから表示を有効にする必要があります。\\n\\n本当にUIをリセットしてもよろしいですか？\"\nUpdater.Title=\"新しい更新が利用可能\"\nUpdater.Text=\"利用可能な新しい更新があります:\"\nUpdater.UpdateNow=\"今すぐ更新\"\nUpdater.RemindMeLater=\"後で通知\"\nUpdater.Skip=\"バージョンをスキップする\"\nUpdater.NoUpdatesAvailable.Title=\"更新はありません\"\nUpdater.NoUpdatesAvailable.Text=\"現在利用可能な更新はありません\"\nUpdater.BranchNotFound.Title=\"更新チャンネルが削除されました\"\nUpdater.BranchNotFound.Text=\"選択した更新チャンネルは利用できなくなったので、OBSは既定値にリセットされました。\"\nUpdater.RepairButUpdatesAvailable.Title=\"整合性の確認が利用できません\"\nUpdater.RepairButUpdatesAvailable.Text=\"ファイルの整合性確認は利用可能な最新バージョンでのみ可能です。 OBSのインストールを確認して更新するには ヘルプ → 更新を確認 を使用してください。\"\nUpdater.RepairConfirm.Title=\"整合性の確認\"\nUpdater.RepairConfirm.Text=\"整合性の確認を開始するとOBSのインストールに破損がないかスキャンし壊れた/変更されたファイルが再ダウンロードされます。 これには少し時間がかかる場合があります。\\n\\n続行しますか？\"\nUpdater.FailedToLaunch=\"アップデータの起動に失敗しました\"\nQuickTransitions.SwapScenes=\"トランジション後に プレビュー/プログラム シーンを入れ替え\"\nQuickTransitions.SwapScenesTT=\"トランジション後にプレビューとプログラムシーンを入れ替えます。(プログラムのオリジナルシーンがまだ存在する場合)\\nこれはプログラムのオリジナルシーンに加えられた可能性があるすべての変更を元に戻しません。\"\nQuickTransitions.DuplicateScene=\"シーンを複製\"\nQuickTransitions.DuplicateSceneTT=\"同じシーンを編集する場合、プログラム出力を変更することなくソースの変換/表示設定を編集することができます。\\nプログラム出力を変更せずにソースのプロパティを編集するには 'ソースを複製' を有効にします。\\nこの値の変更は (もしまだ存在する場合) 現在のプログラムシーンをリセットします。\"\nQuickTransitions.EditProperties=\"ソースを複製\"\nQuickTransitions.EditPropertiesTT=\"同じシーンを編集する場合、プログラム出力を変更することなくソースの変換/表示設定を編集することができます。\\nこの機能は 'シーンを複製' が有効になっている場合にのみ使用可能です。\\n一部のソース (キャプチャやメディアソース) はこの機能をサポートしておらず個別に編集することはできません。\\nこの値の変更は (もしまだ存在する場合) 現在のプログラムシーンをリセットします。\\n\\n警告: ソースが複製されるため、余分なシステムまたはビデオリソースを必要になる場合があります。\"\nQuickTransitions.HotkeyName=\"クイックトランジション: %1\"\nBasic.AddTransition=\"設定可能なトランジションを追加\"\nBasic.RemoveTransition=\"設定可能なトランジションを削除\"\nBasic.TransitionProperties=\"トランジションのプロパティ\"\nBasic.SceneTransitions=\"シーントランジション\"\nBasic.TransitionDuration=\"期間\"\nBasic.TogglePreviewProgramMode=\"スタジオモード\"\nBasic.EnablePreviewProgramMode=\"スタジオモードを有効化\"\nBasic.DisablePreviewProgramMode=\"スタジオモードを無効化\"\nUndo.Undo=\"元に戻す\"\nUndo.Redo=\"やり直し\"\nUndo.Add=\"'%1' を追加\"\nUndo.Delete=\"'%1' の削除\"\nUndo.Rename=\"'%1' の名前を変更\"\nUndo.SceneCollection.Switch=\"'%1' 切り替え\"\nUndo.Item.Undo=\"%1 を元に戻す\"\nUndo.Item.Redo=\"%1 をやり直す\"\nUndo.Sources.Multi=\"%1 個のソース削除\"\nUndo.Filters=\"'%1' のフィルタ変更\"\nUndo.Filters.Paste.Single=\"フィルタ '%1' を '%2' に貼り付け\"\nUndo.Filters.Paste.Multiple=\"'%1' から '%2' にフィルタをコピー\"\nUndo.Transform=\"'%1' のソース変換\"\nUndo.Transform.Paste=\"'%1' の変換を貼り付け\"\nUndo.Transform.Rotate=\"'%1' の回転\"\nUndo.Transform.Reset=\"'%1' の変換をリセット\"\nUndo.Transform.HFlip=\"'%1' の水平反転\"\nUndo.Transform.VFlip=\"'%1' の垂直反転\"\nUndo.Transform.FitToScreen=\"'%1' の画面に合わせる\"\nUndo.Transform.StretchToScreen=\"'%1' の画面に引き延ばして置く\"\nUndo.Transform.Center=\"'%1' の画面中央に置く\"\nUndo.Transform.VCenter=\"'%1' の垂直方向に中央揃え\"\nUndo.Transform.HCenter=\"'%1' の水平方向に中央揃え\"\nUndo.Volume.Change=\"'%1' の音量変更\"\nUndo.Volume.Mute=\"'%1' のミュート\"\nUndo.Volume.Unmute=\"'%1' のミュート解除\"\nUndo.Balance.Change=\"'%1' の音声バランス変更\"\nUndo.SyncOffset.Change=\"'%1' の同期オフセット変更\"\nUndo.MonitoringType.Change=\"'%1' の音声モニタリング変更\"\nUndo.Mixers.Change=\"'%1' の音声ミキサー変更\"\nUndo.ForceMono.On=\"'%1' の強制モノラル有効\"\nUndo.ForceMono.Off=\"'%1' の強制モノラル無効\"\nUndo.Properties=\"'%1' のプロパティ変更\"\nUndo.Scene.Duplicate=\"シーン '%1' の複製\"\nUndo.ShowTransition=\"'%1' の表示トランジション\"\nUndo.HideTransition=\"'%1' の非表示トランジション\"\nUndo.ShowSceneItem=\"'%2' の '%1' 表示\"\nUndo.HideSceneItem=\"'%2' の '%1' 非表示\"\nUndo.ReorderSources=\"'%1' 内のソースの並び替え\"\nUndo.MoveUp=\"'%2' の '%1' を上へ移動\"\nUndo.MoveDown=\"'%2' の '%1' を下へ移動\"\nUndo.MoveToTop=\"'%2' の '%1' を最上部に移動\"\nUndo.MoveToBottom=\"'%2' の '%1' を最下部に移動\"\nUndo.PasteSource=\"'%1' にソースの貼り付け\"\nUndo.PasteSourceRef=\"'%1' にソース参照の貼り付け\"\nUndo.GroupItems=\"アイテムを '%1' にグループ化\"\nTransitionNameDlg.Text=\"トランジションの名前を入力してください\"\nTransitionNameDlg.Title=\"トランジション名\"\nTitleBar.SafeMode=\"セーフモード\"\nTitleBar.PortableMode=\"ポータブルモード\"\nTitleBar.Profile=\"プロファイル\"\nTitleBar.Scenes=\"シーン\"\nNameExists.Title=\"名前が使用中\"\nNameExists.Text=\"この名前は既に使用されています。\"\nNoNameEntered.Title=\"有効な名前を入力してください\"\nNoNameEntered.Text=\"空の名前を使用することはできません。\"\nConfirmStart.Title=\"配信開始？\"\nConfirmStart.Text=\"配信を開始しますか？\"\nConfirmStop.Title=\"配信停止？\"\nConfirmStop.Text=\"配信を停止しますか？\"\nConfirmStopRecord.Title=\"録画停止？\"\nConfirmStopRecord.Text=\"録画を停止しますか？\"\nConfirmBWTest.Title=\"帯域幅テストを開始しますか？\"\nConfirmBWTest.Text=\"OBSが帯域幅テストモードに設定されています。 このモードではチャンネルを配信中にしなくてもネットワークテストをすることが可能です。 テストが終了したら視聴者があなたの配信を見ることができるようにするために無効にする必要があります。\\n\\n続行しますか？\"\nConfirmExit.Title=\"アクティブな出力\"\nConfirmExit.Text=\"OBSは現在アクティブです。 すべての配信/録画はシャットダウンされます。\"\nConfirmRemove.Title=\"削除の確認\"\nConfirmRemove.Text=\"'%1' を削除してもよろしいですか？\"\nConfirmRemove.TextMultiple=\"選択した %1 項目を削除してもよろしいですか？\"\nConfirmReset.Title=\"プロパティのリセット\"\nConfirmReset.Text=\"現在のプロパティを既定値にリセットしてもよろしいですか？\"\nOutput.StartStreamFailed=\"配信開始に失敗しました\"\nOutput.StartRecordingFailed=\"録画開始に失敗しました\"\nOutput.StartReplayFailed=\"リプレイバッファの開始に失敗しました\"\nOutput.StartVirtualCamFailed=\"仮想カメラの開始に失敗しました\"\nOutput.StartFailedGeneric=\"出力開始に失敗しました。詳細はログを確認してください。\\n\\n注: NVENC または AMDエンコーダ を使用している場合は、ビデオドライバーが最新のものであるかを確認してください。\"\nOutput.ReplayBuffer.PauseWarning.Title=\"一時停止中はリプレイを保存できません\"\nOutput.ReplayBuffer.PauseWarning.Text=\"警告: 録画の一時停止中はリプレイを保存できません。\"\nOutput.ConnectFail.Title=\"接続失敗\"\nOutput.ConnectFail.BadPath=\"パス または 接続URL が無効です。設定を確認してそれらが有効であるか確認してください。\"\nOutput.ConnectFail.ConnectFailed=\"サーバーへの接続に失敗しました\"\nOutput.ConnectFail.InvalidStream=\"指定したチャンネルまたはストリームキーにアクセスできませんでした。ストリームキーを再確認してください。 それが正しい場合は、サーバーへの接続に問題がある可能性があります。\"\nOutput.ConnectFail.HdrDisabled=\"現在この出力では HDR出力 は無効になっています。\"\nOutput.ConnectFail.Error=\"サーバーへの接続中に予期しないエラーが発生しました。詳細はログファイルを確認してください。\"\nOutput.ConnectFail.Disconnected=\"サーバーから切断されました。\"\nOutput.StreamEncodeError.Title=\"エンコードエラー\"\nOutput.StreamEncodeError.Msg=\"配信中にエンコーダでエラーが発生しました。\"\nOutput.StreamEncodeError.Msg.LastError=\"配信中にエンコーダでエラーが発生しました:<br><br>%1\"\nOutput.RecordFail.Title=\"録画を開始できませんでした\"\nOutput.RecordFail.Unsupported=\"出力フォーマットがサポートされていないか複数の音声トラックをサポートしていません。 設定を確認してもう一度やり直してください。\"\nOutput.RecordNoSpace.Title=\"ディスク領域の不足\"\nOutput.RecordNoSpace.Msg=\"録画を継続するのに十分なディスク領域がありません。\"\nOutput.RecordError.Title=\"録画エラー\"\nOutput.RecordError.Msg=\"録画中に不明なエラーが発生しました。\"\nOutput.RecordError.EncodeErrorMsg=\"録画中にエンコーダでエラーが発生しました。\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"録画中にエンコーダでエラーが発生しました:<br><br>%1\"\nOutput.BadPath.Title=\"無効なパス\"\nOutput.BadPath.Text=\"設定された録画ファイルのパスを開くことができませんでした。 設定 → 出力 → 録画 で録画ファイルのパスを確認してください。\"\nOutput.NoBroadcast.Title=\"配信が設定されていません\"\nOutput.NoBroadcast.Text=\"配信を開始する前に配信設定をする必要があります。\"\nOutput.BroadcastStartFailed=\"配信開始に失敗しました\"\nOutput.BroadcastStopFailed=\"配信停止に失敗しました\"\nLogUploadDialog.Title=\"OBS Studioログファイルのアップロード\"\nLogUploadDialog.Labels.PrivacyNotice=\"ファイルをアップロードする前に <a href='https://obsproject.com/privacy-policy'>プライバシーポリシー</a> とファイルのアップロードに関するセクションをお読みください。\"\nLogUploadDialog.Labels.Progress=\"ログのアップロード中。 しばらくお待ちください...\"\nLogUploadDialog.Labels.Description.AppLog=\"ログファイルがアップロードされました。デバッグやサポートのためにURLを共有できます。\"\nLogUploadDialog.Labels.Description.CrashLog=\"クラッシュレポートがアップロードされました。デバッグのためにURLを共有できます。\"\nLogUploadDialog.Buttons.ConfirmUpload=\"アップロード\"\nLogUploadDialog.Buttons.CopyURL=\"ログURLをコピー\"\nLogUploadDialog.Buttons.AnalyzeURL=\"ログファイルの分析\"\nLogUploadDialog.Buttons.RetryButton=\"再試行\"\nLogUploadDialog.Errors.Template=\"ファイルのアップロード中にエラーが発生しました:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"アップロードするファイルが見つからないかファイルが空です。\"\nRemux.SourceFile=\"OBS 録画\"\nRemux.TargetFile=\"対象ファイル\"\nRemux.Remux=\"再多重化\"\nRemux.Stop=\"再多重化を停止\"\nRemux.ClearFinished=\"完了した項目をクリア\"\nRemux.ClearAll=\"すべての項目をクリア\"\nRemux.OBSRecording=\"OBS 録画\"\nRemux.FinishedTitle=\"再多重化完了\"\nRemux.Finished=\"録画が再多重化されました\"\nRemux.FinishedError=\"録画が再多重化されましたが、ファイルが未完成の可能性があります\"\nRemux.SelectRecording=\"OBS録画の選択...\"\nRemux.SelectTarget=\"対象ファイルを選択...\"\nRemux.FileExistsTitle=\"対象ファイルが存在します\"\nRemux.FileExists=\"次の対象ファイルは既に存在します。 それらを置き換えますか？\"\nRemux.ExitUnfinishedTitle=\"再多重化が進行中です\"\nRemux.ExitUnfinished=\"再多重化が完了しておらず、今停止すると対象ファイルが使用不能になる可能性があります。\\n再多重化を停止しますか？\"\nRemux.HelpText=\"再多重化するにはファイルをこのウィンドウにドロップするか、空の \\\"OBS 録画\\\" セルを選択してファイルを参照してください。\"\nRemux.NoFilesAddedTitle=\"再多重化ファイルは追加されていません\"\nRemux.NoFilesAdded=\"再多重化にファイルは追加されませんでした。 1つ以上のビデオファイルを含むフォルダをドロップしてください。\"\nMissingFiles=\"不足しているファイル\"\nMissingFiles.MissingFile=\"不足ファイル\"\nMissingFiles.NewFile=\"新規ファイル\"\nMissingFiles.HelpText=\"最後にOBSを使用してからいくつかのファイルが不足しています。\"\nMissingFiles.Clear=\"<クリア>\"\nMissingFiles.NumFound=\"%2 個中 %1 個見つかりました\"\nMissingFiles.Search=\"ディレクトリを検索...\"\nMissingFiles.SelectFile=\"ファイルを選択...\"\nMissingFiles.SelectDir=\"検索するフォルダを選択\"\nMissingFiles.State=\"状態\"\nMissingFiles.Missing=\"行方不明\"\nMissingFiles.Replaced=\"置換済み\"\nMissingFiles.Cleared=\"クリア\"\nMissingFiles.Found=\"発見\"\nMissingFiles.AutoSearch=\"追加で一致するファイルが見つかりました\"\nMissingFiles.AutoSearchText=\"OBSはそのディレクトリで不足しているファイルに一致するものを追加で検出しました。それらを追加しますか？\"\nMissingFiles.NoMissing.Title=\"不足ファイルの確認\"\nMissingFiles.NoMissing.Text=\"不足しているファイルはありません。\"\nMacPermissions.MenuAction=\"アプリの権限を確認する...\"\nMacPermissions.Title=\"アプリの権限を確認する\"\nMacPermissions.Description=\"OBS Studioで特定の機能を使用するには権限の許可が必要です。 これらの権限を有効にすることをお勧めしますが、アプリを使用するために必須ではありません。 後でいつでも有効にすることができます。\"\nMacPermissions.Description.OpenDialog=\"このダイアログはOBS Studioメニューから再度開くことができます。\"\nMacPermissions.AccessGranted=\"アクセスが許可されました\"\nMacPermissions.RequestAccess=\"アクセス権を要求\"\nMacPermissions.OpenPreferences=\"%1 の環境設定を開く\"\nMacPermissions.Item.ScreenRecording=\"画面収録\"\nMacPermissions.Item.ScreenRecording.Details=\"OBSで画面キャプチャするには、この権限が必要です。\"\nMacPermissions.Item.Camera=\"カメラ\"\nMacPermissions.Item.Camera.Details=\"Webカメラやキャプチャカードからコンテンツをキャプチャするにはこの権限が必須です。\"\nMacPermissions.Item.Microphone=\"マイク\"\nMacPermissions.Item.Microphone.Details=\"OBSでマイクまたは外部音声デバイスをキャプチャするにはこの権限が必要です。\"\nMacPermissions.Item.InputMonitoring=\"入力監視\"\nMacPermissions.Item.InputMonitoring.Details=\"OBSがバックグラウンドで動作しているときにホットキーを機能させるにはこの権限が必要です。\"\nMacPermissions.Item.Accessibility=\"アクセシビリティ\"\nMacPermissions.Item.Accessibility.Details=\"古いインストールでは、OBSが \\\"入力監視\\\" ではなく \\\"アクセシビリティ\\\" にリストされている場合があります。\"\nMacPermissions.Continue=\"続行\"\nSourceLeak.Title=\"ソースクリーンアップエラー\"\nSourceLeak.Text=\"シーンコレクションの変更中に問題が発生し一部のソースをアンロードできませんでした。 この問題は通常プラグインがリソースを適切に解放していないことが原因で発生します。 使用しているプラグインが最新であることを確認してください。\\n\\n潜在的なデータ破損を防ぐためにOBS Studioを終了します。\"\nBasic.DesktopDevice1=\"デスクトップ音声\"\nBasic.DesktopDevice2=\"デスクトップ音声 2\"\nBasic.AuxDevice1=\"マイク\"\nBasic.AuxDevice2=\"マイク 2\"\nBasic.AuxDevice3=\"マイク 3\"\nBasic.AuxDevice4=\"マイク 4\"\nBasic.Scene=\"シーン\"\nBasic.DisplayCapture=\"画面キャプチャ\"\nBasic.Main.PreviewConextMenu.Enable=\"プレビュー有効化\"\nBasic.Main.Preview.Disable=\"プレビュー無効化\"\nScaleFiltering=\"スケールフィルタ\"\nScaleFiltering.Point=\"ポイント\"\nScaleFiltering.Bilinear=\"バイリニア\"\nScaleFiltering.Bicubic=\"バイキュービック\"\nScaleFiltering.Lanczos=\"ランチョス\"\nScaleFiltering.Area=\"エリア\"\nBlendingMethod=\"ブレンド方法\"\nBlendingMethod.Default=\"既定\"\nBlendingMethod.SrgbOff=\"SRGBオフ\"\nBlendingMode=\"ブレンドモード\"\nBlendingMode.Normal=\"通常\"\nBlendingMode.Additive=\"加算\"\nBlendingMode.Subtract=\"減算\"\nBlendingMode.Screen=\"スクリーン\"\nBlendingMode.Multiply=\"乗算\"\nBlendingMode.Lighten=\"比較 (明)\"\nBlendingMode.Darken=\"比較 (暗)\"\nDeinterlacing=\"インターレース解除\"\nDeinterlacing.Discard=\"破棄\"\nDeinterlacing.TopFieldFirst=\"トップフィールドが先\"\nDeinterlacing.BottomFieldFirst=\"ボトムフィールドが先\"\nVolControl.SliderUnmuted=\"'%1' の音量スライダー:\"\nVolControl.SliderMuted=\"'%1' の音量スライダー: (現在ミュート)\"\nVolControl.Mute=\"'%1' をミュート\"\nVolControl.Properties=\"'%1' のプロパティ\"\nVolControl.UnassignedWarning.Title=\"未割り当て音声ソース\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" はどの音声トラックにも割り当てられていないため配信や録画で聞くことはできません。\\n\\nトラックに音声ソースを割り当てるには、右クリックメニューまたはミキサードックツールバーの歯車ボタンからオーディオの詳細プロパティを開きます。\"\nBasic.Main.AddSceneDlg.Title=\"シーン追加\"\nBasic.Main.AddSceneDlg.Text=\"シーンの名前を入力してください\"\nBasic.Main.DefaultSceneName.Text=\"シーン %1\"\nBasic.Main.AddSceneCollection.Title=\"シーンコレクションに追加\"\nBasic.Main.AddSceneCollection.Text=\"シーンコレクションの名前を入力してください\"\nBasic.Main.RenameSceneCollection.Title=\"シーンコレクションの名前を変更\"\nBasic.Main.RemigrateSceneCollection.Title=\"シーンコレクションの解像度を更新\"\nBasic.Main.RemigrateSceneCollection.Text=\"シーンコレクション \\\"%1\\\" の解像度を現在のプロファイルのキャンバス解像度 %2x%3 に合わせて更新しますか？\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"出力がアクティブな間はシーンコレクションの解像度を更新できません。\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"シーンコレクションの解像度更新に失敗しました。 元の解像度は不明です。\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"リセットできません: OBSの解像度変更に失敗しました。\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"リセットできません: 現在の解像度は既にシーンコレクションの基本解像度です。\"\nAddProfile.Title=\"プロファイルを追加\"\nAddProfile.Text=\"プロファイルの名前を入力してください\"\nAddProfile.WizardCheckbox=\"自動構成ウィザードを表示する\"\nRenameProfile.Title=\"プロファイルの名前を変更\"\nBasic.Main.MixerRename.Title=\"音声ソースの名前を変更\"\nBasic.Main.MixerRename.Text=\"音声ソースの名前を入力してください\"\nBasic.Main.PreviewDisabled=\"プレビューは現在無効です\"\nBasic.SourceSelect=\"ソースを作成/選択\"\nBasic.SourceSelect.CreateNew=\"新規作成\"\nBasic.SourceSelect.AddExisting=\"既存を追加\"\nBasic.SourceSelect.AddVisible=\"ソースが表示された状態で作成\"\nBasic.Main.Sources.Visibility=\"可視性\"\nBasic.Main.Sources.VisibilityDescription=\"キャンバス内の '%1' の可視性を制御します\"\nBasic.Main.Sources.Lock=\"ロック\"\nBasic.Main.Sources.LockDescription=\"キャンバス内の '%1' の位置とスケールをロックします\"\nBasic.PropertiesWindow=\"'%1' のプロパティ\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (自動選択: %2)\"\nBasic.PropertiesWindow.SelectColor=\"色を選択\"\nBasic.PropertiesWindow.SelectFont=\"フォントを選択\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"フォントを選択\"\nBasic.PropertiesWindow.ConfirmTitle=\"設定の変更\"\nBasic.PropertiesWindow.Confirm=\"保存されていない変更があります。それらを維持しますか？\"\nBasic.PropertiesWindow.NoProperties=\"有効なプロパティがありません\"\nBasic.PropertiesWindow.AddFiles=\"ファイルを追加\"\nBasic.PropertiesWindow.AddDir=\"ディレクトリを追加\"\nBasic.PropertiesWindow.AddURL=\"パス/URLを追加\"\nBasic.PropertiesWindow.AddEditableListDir=\"'%1' にディレクトリを追加\"\nBasic.PropertiesWindow.AddEditableListFiles=\"ファイルを '%1' に追加\"\nBasic.PropertiesWindow.AddEditableListEntry=\"'%1' にエントリを追加\"\nBasic.PropertiesWindow.EditEditableListEntry=\"'%1' からのエントリを編集\"\nBasic.PropertiesView.FPS.Simple=\"単純なFPS値\"\nBasic.PropertiesView.FPS.Rational=\"有理数のFPS値\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"有効なFPS範囲:\"\nBasic.PropertiesView.UrlButton.Text=\"このリンクを既定のウェブブラウザで開きますか？\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"URLを開く\"\nBasic.InteractionWindow=\"'%1' との相互作用\"\nBasic.StatusBar.Reconnecting=\"切断されました、%2 秒で再接続 (試行 %1)\"\nBasic.StatusBar.AttemptingReconnect=\"再接続しています... (試行 %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"再接続に成功\"\nBasic.StatusBar.Delay=\"遅延 (%1 秒)\"\nBasic.StatusBar.DelayStartingIn=\"遅延 (%1 秒で開始)\"\nBasic.StatusBar.DelayStoppingIn=\"遅延 (%1 秒で停止)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"遅延 (%1 秒で停止、%2 秒で開始)\"\nBasic.StatusBar.RecordingSavedTo=\"録画が '%1' に保存されました\"\nBasic.StatusBar.ReplayBufferSavedTo=\"リプレイバッファが '%1' に保存されました\"\nBasic.StatusBar.ScreenshotSavedTo=\"スクリーンショットが '%1' に保存されました\"\nBasic.StatusBar.AutoRemuxedTo=\"録画の再多重化が '%1' に保存されました\"\nBasic.Filters=\"フィルタ\"\nBasic.Filters.AsyncFilters=\"音声/映像フィルタ\"\nBasic.Filters.AudioFilters=\"音声フィルタ\"\nBasic.Filters.EffectFilters=\"エフェクトフィルタ\"\nBasic.Filters.Title=\"'%1' のフィルタ\"\nBasic.Filters.AddFilter.Title=\"フィルタ名\"\nBasic.Filters.AddFilter.Text=\"フィルタの名前を指定してください\"\nBasic.TransformWindow=\"シーン アイテム 変換\"\nBasic.TransformWindow.Position=\"位置\"\nBasic.TransformWindow.PositionX=\"X座標\"\nBasic.TransformWindow.PositionY=\"Y座標\"\nBasic.TransformWindow.Rotation=\"回転\"\nBasic.TransformWindow.Size=\"大きさ\"\nBasic.TransformWindow.Width=\"幅\"\nBasic.TransformWindow.Height=\"高さ\"\nBasic.TransformWindow.Alignment=\"位置揃え\"\nBasic.TransformWindow.BoundsType=\"バウンディングボックスの種類\"\nBasic.TransformWindow.BoundsAlignment=\"バウンディングボックス内の配置\"\nBasic.TransformWindow.Bounds=\"バウンディングボックスのサイズ\"\nBasic.TransformWindow.BoundsWidth=\"バウンディングボックスの幅\"\nBasic.TransformWindow.BoundsHeight=\"バウンディングボックスの高さ\"\nBasic.TransformWindow.CropToBounds=\"バウンディングボックスにクロップ\"\nBasic.TransformWindow.Crop=\"クロップ\"\nBasic.TransformWindow.CropLeft=\"左をクロップ\"\nBasic.TransformWindow.CropRight=\"右をクロップ\"\nBasic.TransformWindow.CropTop=\"上をクロップ\"\nBasic.TransformWindow.CropBottom=\"下をクロップ\"\nBasic.TransformWindow.Alignment.TopLeft=\"左上\"\nBasic.TransformWindow.Alignment.TopCenter=\"中央上\"\nBasic.TransformWindow.Alignment.TopRight=\"右上\"\nBasic.TransformWindow.Alignment.CenterLeft=\"左中央\"\nBasic.TransformWindow.Alignment.Center=\"中央\"\nBasic.TransformWindow.Alignment.CenterRight=\"右中央\"\nBasic.TransformWindow.Alignment.BottomLeft=\"左下\"\nBasic.TransformWindow.Alignment.BottomCenter=\"中央下\"\nBasic.TransformWindow.Alignment.BottomRight=\"右下\"\nBasic.TransformWindow.BoundsType.None=\"境界無し\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"最大サイズのみ\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"境界の内側に合わせる\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"境界の外側に合わせる\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"境界の幅に合わせる\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"境界の高さに合わせる\"\nBasic.TransformWindow.BoundsType.Stretch=\"境界まで引き伸ばす\"\nBasic.TransformWindow.Title=\"'%1' の変換の編集\"\nBasic.TransformWindow.NoSelectedSource=\"ソースが選択されていません\"\nBasic.Main.AddSourceHelp.Title=\"ソースの追加不可\"\nBasic.Main.AddSourceHelp.Text=\"ソースを追加するには少なくとも1つのシーンが必要です。\"\nBasic.Main.Scenes=\"シーン\"\nBasic.Main.Sources=\"ソース\"\nBasic.Main.Source=\"ソース\"\nBasic.Main.Controls=\"コントロール\"\nBasic.Main.PreparingStream=\"準備中...\"\nBasic.Main.Connecting=\"接続中...\"\nBasic.Main.StartRecording=\"録画開始\"\nBasic.Main.StartReplayBuffer=\"リプレイバッファ開始\"\nBasic.Main.SaveReplay=\"リプレイを保存\"\nBasic.Main.StartStreaming=\"配信開始\"\nBasic.Main.StartBroadcast=\"今すぐ配信\"\nBasic.Main.StartVirtualCam=\"仮想カメラ開始\"\nBasic.Main.StopRecording=\"録画終了\"\nBasic.Main.PauseRecording=\"録画一時停止\"\nBasic.Main.UnpauseRecording=\"録画再開\"\nBasic.Main.SplitFile=\"録画ファイルの分割\"\nBasic.Main.AddChapterMarker=\"チャプターマーカーを追加 (Hybrid MP4/MOV のみ)\"\nBasic.Main.StoppingRecording=\"録画停止処理中...\"\nBasic.Main.StopReplayBuffer=\"リプレイバッファ停止\"\nBasic.Main.StoppingReplayBuffer=\"リプレイバッファ停止処理中...\"\nBasic.Main.SetupBroadcast=\"配信の管理\"\nBasic.Main.StopStreaming=\"配信終了\"\nBasic.Main.StopBroadcast=\"ライブ配信を終了\"\nBasic.Main.AutoStopEnabled=\"(自動停止)\"\nBasic.Main.StoppingStreaming=\"配信停止処理中...\"\nBasic.Main.ForceStopStreaming=\"配信停止 (遅延破棄)\"\nBasic.Main.ShowContextBar=\"ソース ツールバーを表示\"\nBasic.Main.HideContextBar=\"ソース ツールバーを非表示\"\nBasic.Main.StopVirtualCam=\"仮想カメラ停止\"\nBasic.Main.Group=\"グループ化 %1\"\nBasic.Main.GroupItems=\"選択したアイテムをグループ化\"\nBasic.Main.Ungroup=\"グループ化の解除\"\nBasic.Main.GridMode=\"グリッドモード\"\nBasic.Main.ListMode=\"リストモード\"\nBasic.Main.VirtualCamConfig=\"仮想カメラの設定\"\nBasic.VCam.VirtualCamera=\"仮想カメラ\"\nBasic.VCam.OutputType=\"出力の種類\"\nBasic.VCam.OutputSelection=\"出力の選択\"\nBasic.VCam.OutputType.Program=\"プログラム (既定)\"\nBasic.VCam.OutputSelection.NoSelection=\"この出力タイプには選択がありません\"\nBasic.VCam.RestartWarning=\"この変更を適用するために仮想カメラは再起動されます\"\nBasic.MainMenu.File=\"ファイル(&F)\"\nBasic.MainMenu.File.Export=\"エクスポート(&E)\"\nBasic.MainMenu.File.Import=\"インポート(&I)\"\nBasic.MainMenu.File.ShowRecordings=\"録画を表示(&R)\"\nBasic.MainMenu.File.Remux=\"録画の再多重化(&M)\"\nBasic.MainMenu.File.Settings=\"設定(&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"設定フォルダを表示\"\nBasic.MainMenu.File.ShowProfileFolder=\"プロファイルフォルダを表示\"\nBasic.MainMenu.File.ShowMissingFiles=\"不足ファイルを確認\"\nBasic.MainMenu.File.Exit=\"終了(&X)\"\nBasic.MainMenu.Edit=\"編集(&E)\"\nBasic.MainMenu.Edit.Undo=\"元に戻す(&U)\"\nBasic.MainMenu.Edit.Redo=\"やり直し(&R)\"\nBasic.MainMenu.Edit.LockPreview=\"プレビューをロック(&L)\"\nBasic.MainMenu.Edit.Scale=\"プレビュースケーリング(&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"ウィンドウに合わせて拡大縮小\"\nBasic.MainMenu.Edit.Scale.Canvas=\"基本(キャンバス) (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"出力(スケーリング) (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"スケール (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"ズームイン\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"ズームアウト\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"ズームをリセット\"\nBasic.MainMenu.Edit.Transform=\"変換(&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"変換の編集(&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"変換をコピー\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"変換を貼り付け\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"変換をリセット(&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"時計回りに 90 度回転\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"反時計回りに 90 度回転\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180 度回転\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"水平反転(&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"垂直反転(&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"画面に合わせる(&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"画面に引き延ばして置く(&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"画面中央に置く(&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"垂直方向に中央揃え\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"水平方向に中央揃え\"\nBasic.MainMenu.Edit.Order=\"順序(&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"上に移動(&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"下に移動(&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"最上部に移動(&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"最下部に移動(&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"オーディオの詳細プロパティ(&A)\"\nBasic.MainMenu.View=\"表示(&V)\"\nBasic.MainMenu.View.Toolbars=\"ツールバー(&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"ドック ツールバー\"\nBasic.MainMenu.View.ContextBar=\"ソース ツールバー\"\nBasic.MainMenu.View.SourceIcons=\"ソース アイコン(&I)\"\nBasic.MainMenu.View.StatusBar=\"ステータスバー(&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"全画面インターフェイス\"\nBasic.MainMenu.View.ResetUI=\"UIをリセット(&R)\"\nBasic.MainMenu.View.AlwaysOnTop=\"常に手前に表示(&A)\"\nBasic.MainMenu.View.SceneListMode=\"シーン リストモード\"\nBasic.MainMenu.Docks=\"ドック(&D)\"\nBasic.MainMenu.Docks.ResetDocks=\"ドックをリセット(&R)\"\nBasic.MainMenu.Docks.LockDocks=\"ドックをロック(&L)\"\nBasic.MainMenu.Docks.SideDocks=\"フルハイト ドック(&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"カスタムブラウザドック(&D)\"\nBasic.MainMenu.Import=\"インポート...\"\nBasic.MainMenu.Export=\"エクスポート...\"\nBasic.MainMenu.SceneCollection=\"シーンコレクション(&S)\"\nBasic.MainMenu.Profile=\"プロファイル(&P)\"\nBasic.MainMenu.Profile.Import=\"プロファイルをインポート\"\nBasic.MainMenu.Profile.Export=\"プロファイルをエクスポート\"\nBasic.MainMenu.SceneCollection.Import=\"シーンコレクションをインポート\"\nBasic.MainMenu.SceneCollection.Export=\"シーンコレクションをエクスポート\"\nBasic.MainMenu.SceneCollection.Remigrate=\"基本解像度をリセット\"\nBasic.MainMenu.SceneCollection.Migrate=\"基本解像度を設定\"\nBasic.MainMenu.Profile.Exists=\"プロファイルは既に存在します\"\nBasic.MainMenu.Tools=\"ツール(&T)\"\nBasic.MainMenu.Help=\"ヘルプ(&H)\"\nBasic.MainMenu.Help.HelpPortal=\"ヘルプポータル(&P)\"\nBasic.MainMenu.Help.Website=\"ウェブサイト(&W)\"\nBasic.MainMenu.Help.Discord=\"Discordサーバーに参加(&D)\"\nBasic.MainMenu.Help.WhatsNew=\"新着情報\"\nBasic.MainMenu.Help.Logs=\"ログファイル(&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"ログファイルを表示(&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"現在のログファイルをアップロード(&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"前回のログファイルをアップロード(&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"現在のログを表示(&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"リリースノート\"\nBasic.MainMenu.Help.CheckForUpdates=\"更新を確認\"\nBasic.MainMenu.Help.Repair=\"ファイルの整合性を確認\"\nBasic.MainMenu.Help.RestartSafeMode=\"セーフモードで再起動\"\nBasic.MainMenu.Help.RestartNormal=\"通常モードで再起動\"\nBasic.MainMenu.Help.CrashLogs=\"クラッシュレポート(&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"クラッシュレポートを表示(&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"前回のクラッシュレポートをアップロード(&P)\"\nBasic.MainMenu.Help.About=\"OBS Studioについて(&A)\"\nBasic.Settings.ProgramRestart=\"これらの設定を有効にするためにはプログラムの再起動が必要です。\"\nBasic.Settings.ConfirmTitle=\"変更確認\"\nBasic.Settings.Confirm=\"保存していない変更があります。変更を保存しますか？\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 は配信設定の一部を制御しています\"\nBasic.Settings.General=\"一般\"\nBasic.Settings.General.Language=\"言語\"\nBasic.Settings.General.Updater=\"更新\"\nBasic.Settings.General.UpdateChannel=\"更新チャンネル\"\nBasic.Settings.General.UpdateChannelDisabled=\"(無効)\"\nBasic.Settings.General.UpdateChannelDefault=\"(既定値)\"\nBasic.Settings.General.EnableAutoUpdates=\"起動時に自動的に更新を確認する\"\nBasic.Settings.General.OpenStatsOnStartup=\"起動時に統計ダイアログを開く\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"スクリーンキャプチャからOBSウィンドウを非表示にする\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"この設定はプロジェクター以外のすべてのOBS StudioウィンドウをOBSによってキャプチャされなくなり\\n会議、画面共有、リモートサポート、スクリーンショット、その他のキャプチャソフトウェアなどの他のアプリケーションに影響します。\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"このオプションを有効にするとプロジェクター以外のすべてのOBS StudioウィンドウがOBSによってキャプチャされなくなり、会議、画面共有、リモートサポート、スクリーンショット、その他のキャプチャソフトウェアなどの他のアプリケーションに影響します。\"\nBasic.Settings.General.WarnBeforeStartingStream=\"配信を開始するときに確認ダイアログを表示する\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"配信を停止するときに確認ダイアログを表示する\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"録画を停止するときに確認ダイアログを表示する\"\nBasic.Settings.General.Projectors=\"プロジェクター\"\nBasic.Settings.General.HideProjectorCursor=\"プロジェクター上のカーソルを非表示にする\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"プロジェクターを常に手前に表示する\"\nBasic.Settings.General.Snapping=\"ソース配置のスナップ\"\nBasic.Settings.General.ScreenSnapping=\"画面の端にソースをスナップする\"\nBasic.Settings.General.CenterSnapping=\"水平方向および垂直方向の中心にソースをスナップする\"\nBasic.Settings.General.SourceSnapping=\"他のソースにソースをスナップする\"\nBasic.Settings.General.SnapDistance=\"スナップ感度\"\nBasic.Settings.General.SpacingHelpers=\"ピクセル配置ガイドを表示する\"\nBasic.Settings.General.RecordWhenStreaming=\"配信時に自動的に録画する\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"配信が停止しても録画を継続する\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"配信時に自動的にリプレイバッファを開始する\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"配信停止時にリプレイバッファをアクティブにしておく\"\nBasic.Settings.General.SysTray=\"システムトレイ\"\nBasic.Settings.General.SysTrayWhenStarted=\"起動時にシステムトレイへ最小化する\"\nBasic.Settings.General.SystemTrayHideMinimize=\"タスクバーの代わりにシステムトレイに常に最小化する\"\nBasic.Settings.General.SaveProjectors=\"終了時にプロジェクターを保存する\"\nBasic.Settings.General.CloseExistingProjectors=\"画面ごとに1つの全画面プロジェクターに制限する\"\nBasic.Settings.General.Preview=\"プレビュー\"\nBasic.Settings.General.OverflowHidden=\"オーバーフローを非表示にする\"\nBasic.Settings.General.OverflowAlwaysVisible=\"オーバーフローを常に表示する\"\nBasic.Settings.General.OverflowSelectionHidden=\"ソースが表示されていない場合もオーバーフローを表示する\"\nBasic.Settings.General.Importers=\"インポーター\"\nBasic.Settings.General.AutomaticCollectionSearch=\"インポート時にシーンコレクションの既知の場所を検索する\"\nBasic.Settings.General.SwitchOnDoubleClick=\"ダブルクリックしたときにシーンに遷移する\"\nBasic.Settings.General.StudioPortraitLayout=\"縦長/垂直レイアウトを有効にする\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"プレビュー/プログラム ラベルを表示する\"\nBasic.Settings.General.Multiview=\"マルチビュー\"\nBasic.Settings.General.Multiview.MouseSwitch=\"クリックするとシーンを切り替える\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"シーン名を表示する\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"セーフエリアを描画する (EBU R95)\"\nBasic.Settings.General.MultiviewLayout=\"マルチビューレイアウト\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"水平上側 (8 シーン)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"水平下側 (8 シーン)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"垂直左側 (8 シーン)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"垂直右側 (8 シーン)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"水平上側 (18 シーン)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"水平上側 (24 シーン)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"シーンのみ (4 シーン)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"シーンのみ (9 シーン)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"シーンのみ (16 シーン)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"シーンのみ (25 シーン)\"\nBasic.Settings.General.ChannelName.stable=\"安定\"\nBasic.Settings.General.ChannelDescription.stable=\"最新の安定版リリース\"\nBasic.Settings.General.ChannelName.beta=\"ベータ / リリース候補\"\nBasic.Settings.General.ChannelDescription.beta=\"潜在的に不安定なプレリリースバージョン\"\nBasic.Settings.Appearance=\"外観\"\nBasic.Settings.Appearance.General=\"一般\"\nBasic.Settings.Appearance.General.Theme=\"テーマ\"\nBasic.Settings.Appearance.General.Variant=\"スタイル\"\nBasic.Settings.Appearance.General.NoVariant=\"利用可能なスタイルはありません\"\nBasic.Settings.Appearance.FontScale=\"フォントサイズ\"\nBasic.Settings.Appearance.Density=\"密度\"\nBasic.Settings.Appearance.Classic=\"クラシック\"\nBasic.Settings.Appearance.Compact=\"コンパクト\"\nBasic.Settings.Appearance.Normal=\"通常\"\nBasic.Settings.Appearance.Comfortable=\"快適\"\nBasic.Settings.Appearance.OptionsWarning=\"このスタイルでは一部の外観オプションが使用できません。\"\nBasic.Settings.Stream=\"配信\"\nBasic.Settings.Stream.Destination=\"宛先\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"認証を使用する\"\nBasic.Settings.Stream.Custom.Username=\"ユーザー名\"\nBasic.Settings.Stream.Custom.Password=\"パスワード\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: srp_ユーザー名 を入力します。\\nRTMP: ユーザー名 を入力します。\\nSRT: 使用しません。\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: srp_パスワード を入力します。\\nRTMP: パスワード を入力します。\\nSRT: 暗号化パスフレーズ を入力します。\"\nBasic.Settings.Stream.BandwidthTestMode=\"帯域幅テストモードを有効にする\"\nBasic.Settings.Stream.TTVAddon=\"Twitch チャットのアドオン\"\nBasic.Settings.Stream.TTVAddon.None=\"なし\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV と FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"配信設定がありません\"\nBasic.Settings.Stream.StreamSettingsWarning=\"設定を開く\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"配信用URLとストリームキーがありません。\\n\\n設定の '配信' タブで配信用URLとストリームキーを入力してください。\"\nBasic.Settings.Stream.MissingUrl=\"配信用URLがありません。\\n\\n設定の '配信' タブで配信用URLを入力してください。\"\nBasic.Settings.Stream.MissingStreamKey=\"ストリームキーがありません。\\n\\n設定の '配信' タブでストリームキーを入力してください。\"\nBasic.Settings.Stream.IgnoreRecommended=\"配信サービスの推奨設定値を無視する\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"推奨設定を上書きする\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"警告: 配信サービスの制限を無視すると配信の品質が低下したり配信ができなくなったりする可能性があります。\\n\\n続行しますか？\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"最大映像ビットレート: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"最大音声ビットレート: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"最大解像度: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"最大FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"カスタムサーバーを指定...\"\nBasic.Settings.Stream.ServiceCustomServer=\"カスタムサーバー\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1 を有効にする\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"最大配信帯域幅\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"自動\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"最大ビデオトラック数\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"自動\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"FLVへのストリームダンプを有効にする (基本録画ファイル設定を使用)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"設定を上書き (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"設定の上書きを有効にする\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"マルチトラックビデオ\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"追加キャンバス\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"サイマルキャスト\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"サイマルキャストでは複数のビデオ品質をエンコードして送信することができます。 <a href='https://obsproject.com/kb/whip-streaming-guide'>詳しくはこちら</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"合計レイヤー数\"\nBasic.Settings.Stream.AdvancedOptions=\"詳細オプション\"\nBasic.Settings.Output=\"出力\"\nBasic.Settings.Output.Format=\"録画フォーマット\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmented MOVは録画をチャンクで書き込み従来のMOVファイルのようなファイナライズ(完成処理)を必要としません。\\nこれにより、例えばBSODや停電などの結果としてディスクへの書き込みが中断された場合でも、ファイルの再生が可能な状態を維持します。\\n\\nこれはすべてのプレーヤーやエディターで互換性があるとは限りません。 必要に応じて「ファイル → 録画の再多重化」を使用してより互換性の高い形式に変換してください。\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmented MP4は録画をチャンクで書き込み従来のMP4ファイルのようなファイナライズ(完成処理)を必要としません。\\nこれにより、例えばBSODや停電などの結果としてディスクへの書き込みが中断された場合でも、ファイルの再生が可能な状態を維持します。\\n\\nこれはすべてのプレーヤーやエディターで互換性があるとは限りません。 必要に応じて「ファイル → 録画の再多重化」を使用してより互換性の高い形式に変換してください。\"\nBasic.Settings.Output.Encoder.Video=\"映像エンコーダ\"\nBasic.Settings.Output.Encoder.Audio=\"音声エンコーダ\"\nBasic.Settings.Output.SelectDirectory=\"録画ディレクトリを選択\"\nBasic.Settings.Output.DynamicBitrate=\"輻輳を管理するためにビットレートを動的に変更する\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"輻輳を管理するためにビットレートを動的に変更する (ベータ版)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"フレームをドロップして輻輳を減らす代わりに、ビットレートを動的に変更します。\\n\\n突然の大幅な輻輳が発生した場合には視聴者の遅延が増大する可能性があることに注意してください。\\nビットレートが低下すると、復元に数分かかることがあります。\\n\\n現時点ではRTMPでのみサポートされています。\"\nBasic.Settings.Output.Mode=\"出力モード\"\nBasic.Settings.Output.Mode.Simple=\"基本\"\nBasic.Settings.Output.Mode.Adv=\"詳細\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg の出力\"\nBasic.Settings.Output.UseReplayBuffer=\"リプレイバッファを有効にする\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"最大リプレイ時間\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"最大メモリ\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"推定メモリ使用量: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"警告: 推定メモリ使用量 %1 MiB は推奨される最大値 %2 MiB を超えています\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"メモリ使用量を見積もることができません。 最大メモリ制限を設定してください。\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"リプレイバッファのファイル名の接頭辞\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"接尾辞\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"録画種別が カスタム出力 (FFmpeg) に設定されている場合 リプレイバッファ は使用できません。\"\nBasic.Settings.Output.Simple.SavePath=\"録画ファイルのパス\"\nBasic.Settings.Output.Simple.RecordingQuality=\"録画品質\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"配信と同じ\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"高品質、ファイルサイズ中\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"超高品質、ファイルサイズ大\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"無損失品質、ファイルサイズ特大\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"警告: 配信の映像ビットレートは、現在の配信サービスの上限である %1 に設定されます。\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"警告: 配信の音声ビットレートは、現在の配信サービスの上限である %1 に設定されます。\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"警告: 録画品質が \\\"配信と同じ\\\" に設定されている場合は録画を一時停止することはできません。\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"警告: 現在選択されている録画フォーマットは選択されている配信エンコーダと互換性がありません。\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"警告: 配信と録画を同時に行う場合に配信と異なる品質でソフトウェアエンコーダで録画する場合にはさらなるCPU使用率が必要になります。\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"警告: 無損失品質は非常に大きなサイズのファイルが生成されます！ 無損失品質は高解像度と高フレームレートの場合1分あたり7GB以上のディスク容量を使用します。 非常に大きなディスク空き容量がない限り、長時間の録画には無損失設定の使用はお勧めできません。 無損失品質の使用中はリプレイバッファを使用できません。\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"無損失品質を使用してもよろしいですか？\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"無損失品質警告！\"\nBasic.Settings.Output.Simple.Encoder.Software=\"ソフトウェア (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"ハードウェア (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"ハードウェア (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"ハードウェア (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"ハードウェア (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"ハードウェア (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"ハードウェア (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"ハードウェア (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"ハードウェア (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"ハードウェア (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"ハードウェア (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"ソフトウェア (x264 CPU使用率の低いプリセット、ファイルサイズ増加)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (既定値)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VODトラック (トラック 2を使用)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"音声トラック\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"互換性のない解像度/フレームレート\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"この配信サービスは現在の出力解像度やフレームレートをサポートしていません。それらは最も近い互換性のある値に変更されます:\\n\\n%1\\n\\n続行しますか？\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"解像度: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"互換性のないエンコーダ\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"配信サービス \\\"%1\\\" はエンコーダ \\\"%2\\\" をサポートしていません。 エンコーダは \\\"%3\\\" に変更されます。\\n\\n続行しますか？\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"配信サービス \\\"%1\\\" はエンコーダ \\\"%2\\\" および \\\"%3\\\" をサポートしていません。 エンコーダは \\\"%4\\\" と \\\"%5\\\" に変更されます。\\n\\n続行しますか？\"\nBasic.Settings.Output.VideoBitrate=\"映像ビットレート\"\nBasic.Settings.Output.AudioBitrate=\"音声ビットレート\"\nBasic.Settings.Output.Reconnect=\"自動的に再接続\"\nBasic.Settings.Output.RetryDelay=\"再試行の遅延\"\nBasic.Settings.Output.MaxRetries=\"最大再試行回数\"\nBasic.Settings.Output.Advanced=\"カスタムエンコーダ設定を有効にする (高度)\"\nBasic.Settings.Output.EncoderPreset=\"エンコーダプリセット\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (低CPU使用率、最低品質)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (既定値) (中程度のCPU使用率、標準品質)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (高いCPU使用率、高品質)\"\nBasic.Settings.Output.CustomEncoderSettings=\"カスタムエンコーダ設定\"\nBasic.Settings.Output.CustomMuxerSettings=\"カスタムマルチプレクサ設定\"\nBasic.Settings.Output.NoSpaceFileName=\"スペースなしのファイル名を生成する\"\nBasic.Settings.Output.Adv.Rescale=\"出力をリスケールする\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"無効\"\nBasic.Settings.Output.Adv.AudioTrack=\"音声トラック\"\nBasic.Settings.Output.Adv.Streaming=\"配信\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"配信設定\"\nBasic.Settings.Output.Adv.Audio.Track1=\"トラック 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"トラック 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"トラック 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"トラック 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"トラック 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"トラック 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VODトラック\"\nBasic.Settings.Output.Adv.Encoder=\"エンコーダ設定\"\nBasic.Settings.Output.Adv.Recording=\"録画\"\nBasic.Settings.Output.Adv.Recording.Settings=\"録画設定\"\nBasic.Settings.Output.Adv.Recording.RecType=\"録画の種類\"\nBasic.Settings.Output.Adv.Recording.Type=\"種別\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"標準\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"カスタム出力 (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(配信エンコーダを使用)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"ファイル名書式設定\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"ファイルが存在する場合は上書きする\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"カスタム出力 (FFmpeg) は保護機能なしで提供されています。\\n録画種別 \\\"標準\\\" の使用を検討してください。\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg出力の種類\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"URLに出力\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"ファイルに出力\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"共通の録画フォーマット\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"すべてのファイル\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"ファイルパス または URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"コンテナフォーマット\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"音声\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"映像\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"既定のフォーマット\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"コンテナフォーマットの説明\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"ファイルパスまたはURLから推測された音声/映像コーデック\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"既定のエンコーダ\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"エンコーダを無効にする\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"映像エンコーダ\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"映像エンコーダ設定 (ある場合)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"音声エンコーダ\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"音声エンコーダ設定 (ある場合)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"マルチプレクサーの設定 (ある場合)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"キーフレーム間隔 (フレーム)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"すべてのコーデックを表示 (潜在的に互換性がない場合でも)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg設定\"\nBasic.Settings.Output.EnableSplitFile=\"自動ファイル分割\"\nBasic.Settings.Output.SplitFile.TypeTime=\"時間で分割\"\nBasic.Settings.Output.SplitFile.TypeSize=\"サイズで分割\"\nBasic.Settings.Output.SplitFile.TypeManual=\"手動で分割\"\nBasic.Settings.Output.SplitFile.Time=\"分割時間\"\nBasic.Settings.Output.SplitFile.Size=\"分割サイズ\"\nScreenshot=\"出力をスクリーンショット\"\nScreenshot.SourceHotkey=\"選択したソースをスクリーンショット\"\nScreenshot.StudioProgram=\"プログラムのスクリーンショットを保存\"\nScreenshot.Preview=\"プレビューのスクリーンショットを保存\"\nScreenshot.Scene=\"シーンのスクリーンショットを保存\"\nScreenshot.Source=\"ソースのスクリーンショットを保存\"\nFilenameFormatting.TT.CCYY=\"年, 4桁\"\nFilenameFormatting.TT.YY=\"年, 下2桁 (00-99)\"\nFilenameFormatting.TT.MM=\"月, 数値 (01-12)\"\nFilenameFormatting.TT.DD=\"日, 0埋め (01-31)\"\nFilenameFormatting.TT.hh=\"時, 24時間形式 (00-23)\"\nFilenameFormatting.TT.mm=\"分 (00-59)\"\nFilenameFormatting.TT.ss=\"秒 (00-59)\"\nFilenameFormatting.TT.Percent=\"パーセント記号 (%)\"\nFilenameFormatting.TT.a=\"曜日, 略称\"\nFilenameFormatting.TT.A=\"曜日, 完全名\"\nFilenameFormatting.TT.b=\"月, 略称\"\nFilenameFormatting.TT.B=\"月, 完全名\"\nFilenameFormatting.TT.d=\"日, 0埋め (01-31)\"\nFilenameFormatting.TT.H=\"時, 24時間形式 (00-23)\"\nFilenameFormatting.TT.I=\"時, 12時間形式 (01-12)\"\nFilenameFormatting.TT.m=\"月, 数値 (01-12)\"\nFilenameFormatting.TT.M=\"分 (00-59)\"\nFilenameFormatting.TT.p=\"午前・午後の標識\"\nFilenameFormatting.TT.s=\"UNIXエポックからの秒数\"\nFilenameFormatting.TT.S=\"秒 (00-59)\"\nFilenameFormatting.TT.y=\"年, 下2桁 (00-99)\"\nFilenameFormatting.TT.Y=\"年\"\nFilenameFormatting.TT.z=\"UTCとの時差, ISO 8601形式\"\nFilenameFormatting.TT.Z=\"タイムゾーンの名称または略称\"\nFilenameFormatting.TT.FPS=\"フレーム毎秒\"\nFilenameFormatting.TT.CRES=\"基本 (キャンバス) 解像度\"\nFilenameFormatting.TT.ORES=\"出力 (スケーリング) 解像度\"\nFilenameFormatting.TT.VF=\"映像フォーマット\"\nBasic.Settings.Video=\"映像\"\nBasic.Settings.Video.Adapter=\"ビデオアダプター\"\nBasic.Settings.Video.BaseResolution=\"基本 (キャンバス) 解像度\"\nBasic.Settings.Video.ScaledResolution=\"出力 (スケーリング) 解像度\"\nBasic.Settings.Video.DownscaleFilter=\"縮小フィルタ\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[解像度が一致、縮小フィルタは不要です]\"\nBasic.Settings.Video.FPSCommon=\"FPS 共通値\"\nBasic.Settings.Video.FPSInteger=\"FPS 整数値\"\nBasic.Settings.Video.FPSFraction=\"FPS 分数値\"\nBasic.Settings.Video.Numerator=\"分子\"\nBasic.Settings.Video.Denominator=\"分母\"\nBasic.Settings.Video.Renderer=\"レンダラー\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (実験的)\"\nBasic.Settings.Video.InvalidResolution=\"解像度の値が不正です。[幅]x[高さ] (例 1920x1080)にしてください\"\nBasic.Settings.Video.CurrentlyActive=\"映像出力は現在アクティブです。映像設定を変更するには出力を停止してください。\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"バイリニア (最速だが、スケーリングするとぼやける)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"バイキュービック (先鋭化スケーリング、16サンプル)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"ランチョス (先鋭化スケーリング、36サンプル)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"エリア (加重合計、4/6/9サンプル)\"\nBasic.Settings.Audio=\"音声\"\nBasic.Settings.Audio.SampleRate=\"サンプルレート\"\nBasic.Settings.Audio.Channels=\"チャンネル\"\nBasic.Settings.Audio.Meters=\"メーター\"\nBasic.Settings.Audio.MeterDecayRate=\"減衰率\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"速い\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"中 (タイプ I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"遅い (タイプ II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"ピークメーターの種類\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"サンプルピーク\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"トゥルーピーク (CPU使用率高い)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"警告: サラウンド音声が有効になっています。\"\nBasic.Settings.Audio.MultichannelWarning=\"配信の場合、配信サービスがサラウンド音声の取り込みとサラウンド音声の再生の両方をサポートしているかどうかを確認してください。 例えば、Facebook 360 Liveはサラウンド音声を完全にサポートし、YouTube Liveは 5.1 音声の取り込み (およびテレビでの再生) をサポートしています。\\n\\nOBS音声フィルタはサラウンド音声と互換性がありますが、VSTプラグインのサポートは保証されていません。\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"サラウンド音声を有効にしますか？\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"サラウンド音声を有効にしてもよろしいですか？\"\nBasic.Settings.Audio.Devices=\"グローバル音声デバイス\"\nBasic.Settings.Audio.DesktopDevice=\"デスクトップ音声\"\nBasic.Settings.Audio.DesktopDevice2=\"デスクトップ音声 2\"\nBasic.Settings.Audio.AuxDevice=\"マイク音声\"\nBasic.Settings.Audio.AuxDevice2=\"マイク音声 2\"\nBasic.Settings.Audio.AuxDevice3=\"マイク音声 3\"\nBasic.Settings.Audio.AuxDevice4=\"マイク音声 4\"\nBasic.Settings.Audio.EnablePushToMute=\"プッシュツーミュートを有効にする\"\nBasic.Settings.Audio.PushToMuteDelay=\"プッシュツーミュート遅延\"\nBasic.Settings.Audio.EnablePushToTalk=\"プッシュツートークを有効にする\"\nBasic.Settings.Audio.PushToTalkDelay=\"プッシュツートーク遅延\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[デバイスが接続されていないまたは利用不可]\"\nBasic.Settings.Audio.Disabled=\"無効\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"低遅延音声バッファリングモード (Decklink/NDI 出力用)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"警告: 低遅延音声バッファリングが有効になっています。\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"低遅延音声バッファリングモードでは、一部ソースからの音声に不具合が発生したり再生が停止したりする場合があります。\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"低遅延音声バッファリングモードを有効にしますか？\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"低遅延音声バッファリングモードを有効にしてもよろしいですか？\"\nBasic.Settings.Accessibility=\"アクセシビリティ\"\nBasic.Settings.Accessibility.ColorOverrides=\"異なる色を使用する\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"ソースの枠線 (選択時)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"ソースの枠線 (クロップ時)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"ソースの枠線 (ホバー時)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"ミキサー音量バンド (-60 から -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"ミキサー音量バンド (-20 から -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"ミキサー音量バンド (-9 から 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"ミキサー音量バンド (-60 から -20dB) (アクティブ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"ミキサー音量バンド (-20 から -9dB) (アクティブ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"ミキサー音量バンド (-9 から 0dB) (アクティブ)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"カラープリセット\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"既定\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"カスタム\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"色覚異常の代替案\"\nBasic.Settings.Advanced=\"詳細設定\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"終了時にアクティブな出力の警告を表示する\"\nBasic.Settings.Advanced.General.ProcessPriority=\"プロセスの優先度\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"高\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"通常以上\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"通常\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"通常以下\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"低\"\nBasic.Settings.Advanced.FormatWarning=\"警告: NV12/P010 以外のカラーフォーマットは主に録画を想定しており、配信には非推奨です。配信ではカラーフォーマットの変換によるCPU使用率の増加が発生する可能性があります。\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"警告: 高精度フォーマットは HDR色空間 でより一般的に使用されます。\"\nBasic.Settings.Advanced.FormatWarning2100=\"警告: Rec. 2100はより精度の高いフォーマットを使用する必要があります。\"\nBasic.Settings.Advanced.Video.ColorFormat=\"カラーフォーマット\"\nBasic.Settings.Advanced.Video.ColorSpace=\"色空間\"\nBasic.Settings.Advanced.Video.ColorRange=\"色範囲\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"リミテッド\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"フル\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDRホワイトレベル\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR公称ピークレベル\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"モニタリングデバイス\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"既定\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Windowsの音量を自動で下げる機能を無効にする\"\nBasic.Settings.Advanced.StreamDelay=\"遅延配信\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"期間\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"再接続時にカットオフポイントを保持する (増加遅延)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"推定メモリ使用量: %1 MB\"\nBasic.Settings.Advanced.Network=\"ネットワーク\"\nBasic.Settings.Advanced.Network.Disabled=\"現在選択されている配信プロトコルはネットワーク設定の変更をサポートしていません。\"\nBasic.Settings.Advanced.Network.BindToIP=\"IPにバインド\"\nBasic.Settings.Advanced.Network.IPFamily=\"IPファミリー\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"ネットワークの最適化を有効にする\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"TCPペーシングを有効にする\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"遅延の影響を受けやすいネットワーク上の他のアプリケーションに対応するため、RTMP出力の伝送速度を制限します。\\n接続が不安定な場合、コマ落ちするリスクを高めることがあります。\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"ホットキーフォーカスの動作\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"ホットキーを無効にしない\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"メインウィンドウにフォーカスがあるときはホットキーを無効にする\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"メインウィンドウにフォーカスがないときはホットキーを無効にする\"\nBasic.Settings.Advanced.AutoRemux=\"自動的に %1 に再多重化する\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(mkvとして録画)\"\nBasic.AdvAudio=\"オーディオの詳細プロパティ\"\nBasic.AdvAudio.ActiveOnly=\"アクティブなソースのみ\"\nBasic.AdvAudio.Name=\"名称\"\nBasic.AdvAudio.Volume=\"音量\"\nBasic.AdvAudio.VolumeSource=\"'%1' の音量\"\nBasic.AdvAudio.Mono=\"モノラル\"\nBasic.AdvAudio.MonoSource=\"'%1' のモノラルダウンミックス\"\nBasic.AdvAudio.Balance=\"バランス\"\nBasic.AdvAudio.BalanceSource=\"'%1' のバランス\"\nBasic.AdvAudio.SyncOffset=\"同期オフセット\"\nBasic.AdvAudio.SyncOffsetSource=\"'%1' の同期オフセット\"\nBasic.AdvAudio.Monitoring=\"音声モニタリング\"\nBasic.AdvAudio.Monitoring.None=\"モニターオフ\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"モニターのみ (出力はミュート)\"\nBasic.AdvAudio.Monitoring.Both=\"モニターと出力\"\nBasic.AdvAudio.MonitoringSource=\"'%1' の音声モニタリング\"\nBasic.AdvAudio.AudioTracks=\"トラック\"\nBasic.Settings.Hotkeys=\"ホットキー\"\nBasic.Settings.Hotkeys.Pair=\"'%1' との組合せでトグルスイッチとして機能します\"\nBasic.Settings.Hotkeys.Filter=\"絞り込み\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"ホットキーで絞り込み\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"このホットキーは1つ以上の他のアクションと共有されています。クリックすると競合が表示されます。\"\nBasic.Settings.Hotkeys.PleaseWait=\"ホットキーを読み込み中、しばらくお待ちください...\"\nBasic.Hotkeys.SelectScene=\"シーン切り替え\"\nBasic.SystemTray.Show=\"表示\"\nBasic.SystemTray.Hide=\"非表示\"\nBasic.SystemTray.Message.Reconnecting=\"切断されました。 再接続中...\"\nHotkeys.Insert=\"Insertキー\"\nHotkeys.Delete=\"Deleteキー\"\nHotkeys.Home=\"Homeキー\"\nHotkeys.End=\"Endキー\"\nHotkeys.PageUp=\"PageUpキー\"\nHotkeys.PageDown=\"PageDownキー\"\nHotkeys.NumLock=\"NumLockキー\"\nHotkeys.ScrollLock=\"ScrollLockキー\"\nHotkeys.CapsLock=\"CapsLockキー\"\nHotkeys.Backspace=\"Backspaceキー\"\nHotkeys.Tab=\"Tabキー\"\nHotkeys.Print=\"Printキー\"\nHotkeys.Pause=\"Pauseキー\"\nHotkeys.Left=\"Leftキー\"\nHotkeys.Right=\"Rightキー\"\nHotkeys.Up=\"Upキー\"\nHotkeys.Down=\"Downキー\"\nHotkeys.Windows=\"Windowsキー\"\nHotkeys.Super=\"Superキー\"\nHotkeys.Menu=\"メニュー\"\nHotkeys.Space=\"Spaceキー\"\nHotkeys.NumpadNum=\"テンキー %1\"\nHotkeys.NumpadMultiply=\"* (テンキー)\"\nHotkeys.NumpadDivide=\"/(テンキー)\"\nHotkeys.NumpadAdd=\"+ (テンキー)\"\nHotkeys.NumpadSubtract=\"-(テンキー)\"\nHotkeys.NumpadDecimal=\".(テンキー)\"\nHotkeys.AppleKeypadNum=\"%1 (キーパッド)\"\nHotkeys.AppleKeypadMultiply=\"* (キーパッド)\"\nHotkeys.AppleKeypadDivide=\"/ (キーパッド)\"\nHotkeys.AppleKeypadAdd=\"+ (キーパッド)\"\nHotkeys.AppleKeypadSubtract=\"- (キーパッド)\"\nHotkeys.AppleKeypadDecimal=\". (キーパッド)\"\nHotkeys.AppleKeypadEqual=\"= (キーパッド)\"\nHotkeys.MouseButton=\"マウス %1\"\nHotkeys.Escape=\"Esc キー\"\nMute=\"ミュート\"\nUnmute=\"ミュート解除\"\nPush-to-mute=\"プッシュツーミュート\"\nPush-to-talk=\"プッシュツートーク\"\nSceneItemShow=\"'%1' を表示\"\nSceneItemHide=\"'%1' を非表示\"\nOutputWarnings.NoTracksSelected=\"少なくとも1つのトラックを選択する必要があります\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"出力設定エラー\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"すべての出力には少なくとも1つの音声トラックが選択されている必要があります。\"\nOutputWarnings.MP4Recording=\"警告: MP4/MOV で保存された録画はファイルをファイナライズ(完成処理)できない場合 (例えば、BSODや停電など) には復元できなくなります。 複数の音声トラックを録画したい場合は MKV を使用することを検討し録画終了後に MP4/MOV に再多重化することを検討してください。 (ファイル → 録画の再多重化)\"\nOutputWarnings.CannotPause=\"警告: 録画エンコーダが \\\"(配信エンコーダを使用)\\\" に設定されている場合は録画を一時停止することはできません\"\nOutputWarnings.CodecIncompatible=\"互換性がないため音声または映像エンコーダの選択がリセットされました。 リストから互換性のあるエンコーダを選択してください。\"\nCodecCompat.Incompatible=\"(%1 と互換性がありません)\"\nCodecCompat.CodecPlaceholder=\"エンコーダを選択...\"\nCodecCompat.ContainerPlaceholder=\"フォーマットを選択...\"\nCodecCompat.CodecMissingOnExit.Title=\"エンコーダが選択されていません\"\nCodecCompat.CodecMissingOnExit.Text=\"少なくとも1つの映像または音声エンコーダが設定されていません。 録画と配信の両方で必ずエンコーダを選択してください。\"\nCodecCompat.ContainerMissingOnExit.Title=\"フォーマットが選択されていません\"\nCodecCompat.ContainerMissingOnExit.Text=\"録画フォーマットが選択されていません。 選択した配信エンコーダと互換性のある録画フォーマットを選択してください。\"\nFinalScene.Title=\"シーンを削除\"\nFinalScene.Text=\"1つ以上のシーンが必要です。\"\nNoSources.Title=\"ソース無し\"\nNoSources.Text=\"映像ソースをまだ追加していないようなので、空白の画面だけが出力されます。よろしいですか？\"\nNoSources.Text.AddSource=\"メインウィンドウのソースボックスの下にある + アイコンをクリックすると、いつでもソースを追加することができます。\"\nNoSources.Label=\"ソースがありません。\\n下の + ボタンをクリックするか、\\nまたはここを右クリックして追加してください。\"\nChangeBG=\"色の設定\"\nCustomColor=\"カスタム色\"\nBrowserSource.EnableHardwareAcceleration=\"ブラウザソースのハードウェアアクセラレーションを有効にする\"\nAbout=\"OBS Studioについて\"\nAbout.Info=\"OBS Studioは無料でオープンソースのビデオ録画と生放送用のソフトウェアです。\"\nAbout.Donate=\"貢献する\"\nAbout.GetInvolved=\"参加する\"\nAbout.Authors=\"作者\"\nAbout.License=\"ライセンス\"\nAbout.Error=\"エラー！ ファイルを読み取れませんでした。\\n\\n次へ移動: %1\"\nAbout.Contribute=\"OBS Projectを支援する\"\nAddUrl.Title=\"URL経由でソースを追加\"\nAddUrl.Text=\"URLをOBSにドラッグしました。これによりリンクがソースとして自動的に追加されます。続行しますか？\"\nResizeOutputSizeOfSource=\"出力サイズ変更 (ソースサイズ)\"\nResizeOutputSizeOfSource.Text=\"基本解像度 と 出力解像度 は現在のソースサイズにリサイズされます。\"\nResizeOutputSizeOfSource.Continue=\"続行しますか？\"\nPreviewTransition=\"トランジションのプレビュー\"\nImporter=\"シーンコレクションをインポート\"\nImporter.SelectCollection=\"シーンコレクションを選択\"\nImporter.Collection=\"シーンコレクション\"\nImporter.HelpText=\"このウィンドウにファイルを追加してOBSまたは他のサポートされているプログラムからコレクションをインポートします。\"\nImporter.Path=\"コレクションのパス\"\nImporter.Program=\"検出されたアプリケーション\"\nImporter.AutomaticCollectionPrompt=\"シーンコレクションの自動検索\"\nImporter.AutomaticCollectionText=\"OBSはサポートしているサードパーティプログラムからインポート可能なシーンコレクションを自動的に見つけることができます。 OBSでコレクションを自動的に検索しますか？\\n\\nこれは後で設定 > 一般 > インポーターで変更できます。\"\nImporter.SelectFile=\"参照...\"\nRestart=\"再起動\"\nNeedsRestart=\"OBS Studioを再起動する必要があります。 今すぐ再起動しますか？\"\nLoadProfileNeedsRestart=\"プロファイルにはOBSの再起動を必要とする設定が含まれています:\\n%1\\n\\nこれらの設定を有効にするためにOBSを再起動しますか？\"\nContextBar.NoSelectedSource=\"ソースが選択されていません\"\nContextBar.MediaControls.PlayMedia=\"メディアを再生\"\nContextBar.MediaControls.PauseMedia=\"メディアを一時停止\"\nContextBar.MediaControls.StopMedia=\"メディアを停止\"\nContextBar.MediaControls.RestartMedia=\"メディアを再開\"\nContextBar.MediaControls.PlaylistNext=\"プレイリスト内の次\"\nContextBar.MediaControls.PlaylistPrevious=\"プレイリスト内の前\"\nContextBar.MediaControls.BlindSeek=\"メディアシークウィジェット\"\nYouTube.Auth.Ok=\"認証が正常に完了しました。\\nこのページを閉じることができます。\"\nYouTube.Auth.NoCode=\"認証プロセスが完了しませんでした。\"\nYouTube.Auth.NoChannels=\"選択したアカウントに利用可能なチャンネルがありません\"\nYouTube.Auth.WaitingAuth.Title=\"YouTubeユーザー認証\"\nYouTube.Auth.WaitingAuth.Text=\"外部ブラウザで認証を完了してください。<br>外部ブラウザが開かない場合は、このリンクに従って認証を完了してください:<br>%1\"\nYouTube.AuthError.Text=\"チャンネル情報の取得に失敗しました: %1.\"\nYouTube.Actions.WindowTitle=\"YouTube 配信の設定 - チャンネル: %1\"\nYouTube.Actions.CreateNewEvent=\"新しい配信を作成\"\nYouTube.Actions.ChooseEvent=\"既存の配信を選択\"\nYouTube.Actions.Title=\"タイトル*\"\nYouTube.Actions.MyBroadcast=\"自分の配信\"\nYouTube.Actions.Description=\"説明\"\nYouTube.Actions.Privacy=\"プライバシー*\"\nYouTube.Actions.Privacy.Private=\"非公開\"\nYouTube.Actions.Privacy.Public=\"公開\"\nYouTube.Actions.Privacy.Unlisted=\"限定公開\"\nYouTube.Actions.Category=\"カテゴリ\"\nYouTube.Actions.Thumbnail=\"サムネイル\"\nYouTube.Actions.Thumbnail.SelectFile=\"ファイルを選択...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"ファイルが選択されていません\"\nYouTube.Actions.Thumbnail.ClearFile=\"クリア\"\nYouTube.Actions.MadeForKids=\"この動画は子ども向けに作られていますか？*\"\nYouTube.Actions.MadeForKids.Yes=\"はい、子ども向けです\"\nYouTube.Actions.MadeForKids.No=\"いいえ、子ども向けではありません\"\nYouTube.Actions.AdditionalSettings=\"詳細設定\"\nYouTube.Actions.Latency=\"遅延\"\nYouTube.Actions.Latency.Normal=\"通常の遅延\"\nYouTube.Actions.Latency.Low=\"低遅延\"\nYouTube.Actions.Latency.UltraLow=\"超低遅延\"\nYouTube.Actions.EnableAutoStart=\"自動スタートを有効にする\"\nYouTube.Actions.EnableAutoStop=\"自動ストップを有効にする\"\nYouTube.Actions.AutoStartStop.TT=\"この予約された配信を自動的に開始するかどうか\"\nYouTube.Actions.EnableDVR=\"DVR を有効にする\"\nYouTube.Actions.360Video=\"360° 動画\"\nYouTube.Actions.ScheduleForLater=\"予約する\"\nYouTube.Actions.RememberSettings=\"これらの設定を保存する\"\nYouTube.Actions.Create_Ready=\"配信を作成\"\nYouTube.Actions.Create_GoLive=\"配信を作成して配信開始\"\nYouTube.Actions.Choose_Ready=\"配信を選択\"\nYouTube.Actions.Choose_GoLive=\"配信を選択して配信開始\"\nYouTube.Actions.Create_Schedule=\"配信を予約\"\nYouTube.Actions.Create_Schedule_Ready=\"配信を予約して選択\"\nYouTube.Actions.Dashboard=\"YouTube Studioを開く\"\nYouTube.Actions.Error.Title=\"ライブ配信作成エラー\"\nYouTube.Actions.Error.Text=\"YouTube アクセスエラー '%1'<br/>エラーの詳細はこちらを参照してください <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTubeアクセスエラー。 ネットワーク接続またはYouTubeサーバーへのアクセスを確認してください。\"\nYouTube.Actions.Error.NoBroadcastCreated=\"配信作成エラー '%1'<br/>エラーの詳細はこちらを参照してください <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"配信が作成されていません。アカウントを再リンクしてください。\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API エラー。詳細はログファイルを確認してください。\"\nYouTube.Actions.Error.BroadcastNotFound=\"選択した配信が見つかりませんでした。\"\nYouTube.Actions.Error.FileMissing=\"選択したファイルが存在しません。\"\nYouTube.Actions.Error.FileOpeningFailed=\"選択したファイルを開けませんでした。\"\nYouTube.Actions.Error.FileTooLarge=\"選択したファイルが大きすぎます。(制限: 2 MiB)\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"配信の遷移に失敗しました: %1<br/><br/>このエラーが続く場合は <a href='https://studio.youtube.com/video/%2/livestreaming'>YouTube Studio</a> でライブ配信を開き手動でお試しください。\"\nYouTube.Actions.Error.BroadcastTestStarting=\"配信はテスト段階に移行しています。これには時間がかかる場合があります。 10〜30秒後にもう一度お試しください。\"\nYouTube.Actions.EventsLoading=\"イベントリストを読み込み中...\"\nYouTube.Actions.EventCreated.Title=\"イベントが作成されました\"\nYouTube.Actions.EventCreated.Text=\"イベントが正常に作成されました。\"\nYouTube.Actions.Stream=\"配信\"\nYouTube.Actions.Stream.ScheduledFor=\"%1 に公開予約\"\nYouTube.Actions.Stream.Resume=\"中断された配信を再開\"\nYouTube.Actions.Stream.YTStudio=\"YouTube Studioによって自動的に作成\"\nYouTube.Actions.Notify.CreatingBroadcast=\"新しい配信を作成しています。しばらくお待ちください...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"手動で開始する必要があります\"\nYouTube.Actions.AutoStartStreamingWarning=\"このイベントは自動開始が無効になっています。 \\\"今すぐ配信\\\" をクリックして配信を開始してください。\"\nYouTube.Actions.AutoStopStreamingWarning=\"再接続することはできません。<br>配信が停止しライブではなくなります。\"\nYouTube.Errors.liveStreamingNotEnabled=\"選択したYouTubeチャンネルでライブ配信が有効になっていません。<br/> <br/>詳細については <a href='https://www.youtube.com/features'>youtube.com/features</a> をご覧ください。\"\nYouTube.Errors.livePermissionBlocked=\"選択したYouTubeチャンネルではライブ配信を利用できません。<br/>チャンネル設定でライブ配信を有効にしてから利用できるようになるまで最大24時間かかる場合があります。<br/><br/>詳細については <a href='https://www.youtube.com/features'>youtube.com/features</a> をご覧ください。\"\nYouTube.Errors.errorExecutingTransition=\"バックエンドのエラーにより遷移に失敗しました。数秒後に再度お試しください。\"\nYouTube.Errors.errorStreamInactive=\"YouTubeがあなたの配信データを受信していません。設定を確認してもう一度お試しください。\"\nYouTube.Errors.invalidTransition=\"試行された遷移は無効でした。これは配信が前回の遷移を終了していないことが原因の可能性があります。数秒待ってからもう一度お試しください。\"\nYouTube.DocksRemoval.Title=\"従来のYouTubeブラウザドックをクリア\"\nYouTube.DocksRemoval.Text=\"これらのブラウザドックは非推奨として削除されます。\\n\\n%1\\n代わりに \\\"ドック/YouTube Live Control Room\\\" を使用してください。\"\nConfigDownload.WarningMessageTitle=\"警告\"\nFailedToStartStream.MissingConfigURL=\"現在のサービスで利用可能な設定URLはありません\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"カスタムRTMP URLが指定されていません\"\nFailedToStartStream.InvalidCustomConfig=\"無効なカスタム設定\"\nFailedToStartStream.MissingCanvas=\"設定された追加キャンバスがありません\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"マルチトラックビデオサービスの作成に失敗しました\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"マルチトラックビデオRTMP出力の作成に失敗しました\"\nFailedToStartStream.EncoderNotAvailable=\"NVENCが利用できません。\\n\\nエンコーダタイプ '%1' が見つかりません\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"映像エンコーダ '%1' (タイプ: '%2') の作成に失敗しました\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"エンコーダ '%1' (タイプ: '%2') の作成中にOBSの映像情報を取得できませんでした\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"音声エンコーダの作成に失敗しました\"\nFailedToStartStream.NoRTMPURLInConfig=\"設定に配信ターゲットRTMP(S)のURLが含まれていません\"\nFailedToStartStream.FallbackToDefault=\"%1 を使用した配信の開始に失敗しました。シングルエンコード設定を使用して再試行しますか？\"\nFailedToStartStream.ConfigRequestFailed=\"%1 から設定を取得できませんでした<br><br>HTTP エラー: %2\"\nFailedToStartStream.WarningUnknownStatus=\"不明なステータス値 '%1' を受信しました\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\n%1 を使用せずに配信を続行しますか？\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\n配信を続行しますか？\"\nFailedToStartStream.MissingEncoderConfigs=\"配信設定にエンコーダ設定が含まれていませんでした\"\nFailedToStartStream.InvalidEncoderConfig=\"Go live設定に無効なエンコーダ設定が含まれています\"\nFailedToStartStream.StatusMissingHTML=\"配信開始リクエストで不明なエラーが返されました\"\nFailedToStartStream.NoConfigSupplied=\"設定が見つかりません\"\nMultitrackVideo.Info=\"%1 は設定を自動的に最適化し複数のビデオ品質をエンコードして送信します。 このオプションを選択するとコンピュータとソフトウェアのセットアップに関する情報が %2 に送信されます。\"\nMultitrackVideo.IncompatibleSettings.Title=\"互換性のない設定\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 は現在以下と互換性がありません:\\n\\n%2\\n%1 で配信を続行するには、互換性のない設定を無効にして:\\n\\n%3\\n再度配信を開始してください。\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"この配信では無効にして配信を開始する\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"設定を更新して配信を開始する\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 は現在 [音声 → 一般 → チャンネル] を '%2' に設定した場合、互換性がありません %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[音声 → 一般 → チャンネル] を '%1' に設定する必要があります\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 は [音声 → 一般 → チャンネル] に複数の異なる設定が必要です\"\nBasic.OpenPluginManager=\"プラグインマネージャー\"\nPluginManager=\"プラグインマネージャー\"\nPluginManager.Restart=\"プラグインの変更を適用するにはOBS Studioを再起動する必要があります\"\nPluginManager.MissingPlugin=\"[プラグインが見つかりません]\"\nPluginManager.Section.Discover=\"参照\"\nPluginManager.Section.Manage=\"インストール済み\"\nPluginManager.Section.Updates=\"更新\"\nPluginManager.Section.Manage.Title=\"有効プラグインの管理\"\n"
  },
  {
    "path": "frontend/data/locale/ka-GE.ini",
    "content": "Language=\"ქართული\"\nOK=\"კარგი\"\nApply=\"მიღება\"\nCancel=\"გაუქმება\"\nClose=\"დახურვა\"\nSave=\"შენახვა\"\nDiscard=\"მოცილება\"\nDisable=\"გამორთვა\"\nYes=\"დიახ\"\nNo=\"არა\"\nAdd=\"დამატება\"\nRemove=\"წაშლა\"\nRename=\"გადარქმევა\"\nInteract=\"ზემოქმედება\"\nFilters=\"ფილტრები\"\nProperties=\"პარამეტრები\"\nMoveUp=\"აწევა\"\nMoveDown=\"ჩამოწევა\"\nSettings=\"პარამეტრები\"\nDisplay=\"ეკრანი\"\nName=\"სახელი\"\nExit=\"გასვლა\"\nMixer=\"ხმის შემრევი\"\nBrowse=\"მოძიება\"\nMono=\"მონო\"\nStereo=\"სტერეო\"\nDroppedFrames=\"კადრების ვარდნა %1 (%2%)\"\nProjector.Open.Program=\"პროგრამის საჩვენებლად გაიხსნას\"\nProjector.Open.Preview=\"შეთვალიერების საჩვენებლად გაიხსნას\"\nProjector.Open.Scene=\"სცენის საჩვენებლად გაიხსნას\"\nProjector.Open.Source=\"წყაროს საჩვენებლად გაიხსნას\"\nProjector.Open.Multiview=\"მრავალხედიანის გახსნა\"\nProjector.Display=\"ეკრანი: %1\"\nProjector.Window=\"ახალი ფანჯარა\"\nProjector.Title=\"ჩვენება\"\nProjector.Title.Scene=\"სცენა: %1\"\nProjector.Title.Source=\"წყარო: %1\"\nProjector.Title.Multiview=\"მრავალხედიანი\"\nProjector.ResizeWindowToContent=\"ფანჯრის მორგება შიგთავსის ზომაზე\"\nClear=\"გასუფთავება\"\nRevert=\"დაბრუნება\"\nShow=\"ჩვენება\"\nHide=\"დამალვა\"\nUnhideAll=\"ყველას გამოჩენა\"\nUntitled=\"უსათაურო\"\nNew=\"ახალი\"\nDuplicate=\"ასლის შექმნა\"\nEnable=\"ჩართვა\"\nDisableOSXVSync=\"macOS V-Sync-ის გამორთვა\"\nResetOSXVSyncOnExit=\"macOS V-Sync-ის გადატვირთვა გასვლისას\"\nHighResourceUsage=\"დამშიფრავი გადაიტვირთა! სცადეთ ვიდეოს პარამეტრების შემცირება, ან უფრო სწრაფი შიფრაციის პარამეტრების გამოყენება.\"\nTransition=\"გადასვლა\"\nQuickTransitions=\"სწრაფი გადასვლები\"\nFadeToBlack=\"შავში გადასვლა\"\nLeft=\"მარცხნივ\"\nRight=\"მარჯვნივ\"\nTop=\"ზემოთ\"\nBottom=\"ქვემოთ\"\nReset=\"განულება\"\nHours=\"საათი\"\nMinutes=\"წუთი\"\nSeconds=\"წამი\"\nDeprecated=\"მოძველებული\"\nReplayBuffer=\"გადახვევის მარაგი\"\nImport=\"შემოტანა\"\nExport=\"შენახვა\"\nCopy=\"ასლი\"\nPaste=\"ჩასმა\"\nPasteReference=\"ჩასმა (ბმული)\"\nPasteDuplicate=\"ჩასმა (ასლი)\"\nRemuxRecordings=\"ჩანაწერების გადაფუთვა\"\nNext=\"შემდეგ\"\nBack=\"უკან\"\nDefaults=\"ნაგულისხმევი\"\nRestoreDefaults=\"ნაგულისხმევი\"\nHideMixer=\"ხმის შემრევში დამალვა\"\nTransitionOverride=\"გადასვლა გადაფარვით\"\nShowTransition=\"გადასვლების ჩვენება\"\nHideTransition=\"გადასვლების დამალვა\"\nNone=\"არცერთი\"\nStudioMode.Preview=\"შეთვალიერება\"\nStudioMode.Program=\"შედეგი\"\nStudioMode.PreviewSceneName=\"შეთვალიერება: %1\"\nStudioMode.ProgramSceneName=\"შედეგი: %1\"\nShowInMultiview=\"მრავალხედიანი ჩვენება\"\nVerticalLayout=\"შვეული განლაგება\"\nGroup=\"დაჯგუფება\"\nDoNotShowAgain=\"მომავალში ჩვენების გარეშე\"\nDefault=\"(ნაგულისხმევი)\"\nCalculating=\"ითვლება...\"\nFullscreen=\"სრულეკრანიანი\"\nWindowed=\"ფანჯარიანი\"\nRefreshBrowser=\"განახლება\"\nAspectRatio=\"თანაფარდობა <b>%1:%2</b>\"\nLockVolume=\"ხმის ჩაკეტვა\"\nLogViewer=\"აღრიცხვის მნახველი\"\nShowOnStartup=\"ჩვენება გაშვებისას\"\nOpenFile=\"ფაილის გახსნა\"\nAddScene=\"სცენის დამატება\"\nAddSource=\"წყაროს დამატება\"\nRemoveScene=\"შერჩეული სცენის მოცილება\"\nRemoveSource=\"შერჩეული წყარო(ები)ს მოცილება\"\nMoveSceneUp=\"სცენის აწევა\"\nMoveSceneDown=\"სცენის ჩამოწევა\"\nMoveSourceUp=\"წყარო(ები)ს აწევა\"\nMoveSourceDown=\"წყარო(ები)ს ჩამოწევა\"\nSourceProperties=\"თვისებები ღია საწყისი კოდით\"\nSourceFilters=\"ფილტრები ღია საწყისი კოდით\"\nMixerToolbarMenu=\"ხმის შემრევის მენიუ\"\nSceneFilters=\"სცენის ფილტრების გახსნა\"\nList=\"სია\"\nGrid=\"ცხრილი\"\nAutomatic=\"ავტომატური\"\nComingSoon=\"მალე\"\nPluginsFailedToLoad.Title=\"დანამატის ჩატვირთვის შეცდომა\"\nPluginsFailedToLoad.Text=\"მოცემული OBS-დანამატები ვერ ჩაიტვირთა:\\n\\n%1\\nგთხოვთ, განაახლოთ ან მოაცილოთ აღნიშნული დანამატები.\"\nAlreadyRunning.Title=\"OBS უკვე გაშვებულია\"\nAlreadyRunning.Text=\"OBS უკვე გაშვებულია! გთხოვთ, ჯერ დახუროთ OBS-ის ყველა გაშვებული პროცესი, სანამ ახლის გაშვებას შეეცდებით. თუ მითითებული გაქვთ, რომ დახურვის ნაცვლად, OBS სისტემურ არეში უნდა ჩაიკეცოს, გთხოვთ მანდაც გადაამოწმოთ, დარჩენილი ხომ არაა.\"\nAlreadyRunning.LaunchAnyway=\"მაინც გაშვება\"\nCrashHandling.Dialog.Title=\"OBS Studio უეცრად გაითიშა\"\nCrashHandling.Labels.Text=\"OBS Studio სათანადოდ არ დახურულა.\\n\\nგსურთ გაეშვას უსაფრთხო რეჟიმში (გაითიშება გარეშე დანამატები, სკრიპტები და ქსელის მაკავშირებლები)?\"\nCrashHandling.Labels.PrivacyNotice=\"აგრეთვე, თუ გსურთ, თავისით აიტვირთება გათიშვის უახლესი მოხსენება და განიხილავს OBSProject.<br /><br />გთხოვთ, გაეცნოთ <a href='https://obsproject.com/privacy-policy'>პირადულობის დებულებას</a> ნებისმიერი ფაილის ატვირთვამდე და ყურადღება მიაქციოთ შესაბამის მუხლებს ატვირთვის შესახებ.\"\nCrashHandling.Checkbox.SendReport=\"გავეცანი პირადულობის დებულებას და მსურს, აიტვირთოს.\"\nCrashHandling.Buttons.LaunchSafe=\"გაეშვას უსაფრთხო რეჟიმში\"\nCrashHandling.Buttons.LaunchNormal=\"გაეშვას ჩვეულებრივ რეჟიმში\"\nCrashHandling.Errors.UploadJSONError=\"შეცდომა წარმოიშვა ბოლოდროინდელი გათიშვის მოხსენების ატვირთვისას. გთხოვთ კვლავ სცადოთ მოგვიანებით.\"\nCrashHandling.Errors.Title=\"გათიშვის აღრიცხვის ატვირთვისას შეცდომა\"\nSafeMode.Restart=\"გსურთ გაეშვას OBS უსაფრთხო რეჟიმში (გარეშე დანამატები, სკრიპტები და ქსელის მაკავშირებლები გაითიშება)?\"\nSafeMode.RestartNormal=\"გსურთ ახლიდან გაეშვას OBS ჩვეულებრივ რეჟიმში?\"\nChromeOS.Title=\"მხარდაუჭერელი პლატფორმა\"\nChromeOS.Text=\"OBS ჩანს, გაშვებულია ChromeOS-სათავსში. ეს პლატფორმა არაა მხარდაჭერილი.\"\nWine.Title=\"აღმოჩენილია Wine\"\nWine.Text=\"OBS ვერ გაეშვება Wine-ით და შესაძლებლობების უმეტესობა, მათ შორის გადაღება ან აპარატურული წყაროები ვერ იმუშავებს ნაწილობრივ ან სრულად.<br><br>უმჯობესია, გაუშვათ საგანგებოდ შემუშავებული OBS, მაგალითად <a href='https://flathub.org/apps/details/com.obsproject.Studio'>ჩვენი Flatpak-ვერსიაა</a> ან თქვენი სისტემისთვის განკუთვნილი კრებულები.\"\nDockCloseWarning.Title=\"იერსახის ნაწილის დამალვა\"\nDockCloseWarning.Text=\"თქვენ ახლახან დამალეთ იერსახის ნაწილი. თუ გსურთ მისი კვლავ გამოჩენა, მენიუს ზოლიდან გახსენით იერსახის ნაწილები.\"\nExtraBrowsers=\"შერჩეული ვებგვერდის ნაწილები\"\nExtraBrowsers.Info=\"დაამატეთ იერსახის ცალკეული ნაწილები სახელისა და ბმულის მითითებით, შემდეგ კი დაწკაპეთ მიღებასა და დახურვაზე, მათ განსათავსებლად. იერსახის ნაწილების დამატება და მოცილება ნებისმიერ დროს შეგიძლიათ.\"\nExtraBrowsers.DockName=\"ნაწილის სახელი\"\nAuth.Authing.Title=\"ანგარიშზე შესვლა...\"\nAuth.Authing.Text=\"მიმდინარეობს შესვლა %1-ზე, გთხოვთ, მოითმინოთ...\"\nAuth.AuthFailure.Title=\"შესვლა ვერ მოხერხდა\"\nAuth.AuthFailure.Text=\"ვერ მოხერხდა შესვლა %1-ზე:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"საჭიროებს ანგარიშზე შესვლას\"\nAuth.InvalidScope.Text=\"შესვლის მოთხოვნები შეცვლილია მომსახურებისთვის %1. ზოგი შესაძლებლობა, შეიძლება აღარ იყოს ხელმისაწვდომი.\"\nAuth.LoadingChannel.Title=\"არხის მონაცემების ჩატვირთვა...\"\nAuth.LoadingChannel.Text=\"%1-ის არხის მონაცემები იტვირთება, გთხოვთ მოითმინოთ...\"\nAuth.LoadingChannel.Error=\"არხის მონაცემები ვერ ჩაიტვირთა.\"\nAuth.ChannelFailure.Title=\"მონაცემების ჩატვირთვა ვერ მოხერხდა\"\nAuth.ChannelFailure.Text=\"%1-ის არხის მონაცემების ჩატვირთვა ვერ მოხერხდა\\n\\n%2: %3\"\nAuth.Chat=\"სასაუბრო\"\nAuth.StreamInfo=\"ნაკადის მონაცემები\"\nTwitchAuth.Stats=\"Twitch-გაზომვები\"\nTwitchAuth.Feed=\"Twitch მოქმედებების არხი\"\nTwitchAuth.TwoFactorFail.Title=\"ნაკადის გასაღების მოთხოვნა ვერ მოხერხდა\"\nTwitchAuth.TwoFactorFail.Text=\"OBS ვერ უკავშირდება თქვენს Twitch-ანგარიშს. გთხოვთ გადაამოწმოთ, ნამდვილად ჩართული გაქვთ თუ არა ორსაფეხურიანი დადასტურება შესვლისას, თქვენი <a href='https://www.twitch.tv/settings/security'>Twitch-ის უსაფრთხოების პარამეტრებიდან</a>, რაც მოითხოვება ნაკადის გასაშვებად.\"\nRestreamAuth.Channels=\"Restream‐არხები\"\nCopy.Filters=\"ფილტრების ასლი\"\nPaste.Filters=\"ფილტრების ჩასმა\"\nBrowserPanelInit.Title=\"ეშვება ბრაუზერი...\"\nBrowserPanelInit.Text=\"ეშვება ბრაუზერი, გთხოვთ მოითმინოთ...\"\nBandwidthTest.Region=\"რეგიონი\"\nBandwidthTest.Region.US=\"შეერთებული შტატები\"\nBandwidthTest.Region.EU=\"ევროპა\"\nBandwidthTest.Region.Asia=\"აზია\"\nBandwidthTest.Region.Other=\"სხვა\"\nBasic.AutoConfig=\"თვითგამართვის მეგზური\"\nBasic.AutoConfig.ApplySettings=\"პარამეტრების მიღება\"\nBasic.AutoConfig.StartPage=\"გამოყენების შესახებ\"\nBasic.AutoConfig.StartPage.SubTitle=\"მიუთითეთ, თუ რა მიზნით გსურთ პროგრამის გამოყენება\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"ნაკადების გაშვებისთვის მორგება, ვიდეოს ჩაწერა მეორეხარისხოვანია\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"ვიდეოების ჩაწერისთვის მორგება, ნაკადების გაშვებას არ ვაპირებ\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"მხოლოდ წარმოსახვითი კამერის გამოსაყენებლად\"\nBasic.AutoConfig.VideoPage=\"ვიდეოს პარამეტრები\"\nBasic.AutoConfig.VideoPage.SubTitle=\"მიუთითეთ ვიდეოს სასურველი პარამეტრები\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"მიმდინარეს გამოყენება (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"ეკრანი %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"მიმდინარეს გამოყენება (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ან 30, თუმცა უმჯობესია 60, როცა შესაძლებელია\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ან 30, თუმცა უმჯობესია მაღალი გარჩევადობით\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"შენიშვნა: ეკრანის ფონის (ძირითადი) გაფართოება არაა აუცილებელი, გაშვებული ნაკადის ან გადაღებული ვიდეოს გაფართოებას ემთხვეოდეს. ცალკეული ნაკადის/ვიდეოს ზომები შეიძლება შემცირდეს დატვირთვის ან ბიტსიხშირის მიხედვით.\"\nBasic.AutoConfig.StreamPage=\"ნაკადის მონაცემები\"\nBasic.AutoConfig.StreamPage.SubTitle=\"გთხოვთ მიუთითოთ ნაკადის მონაცემები\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"ანგარიშთან დაკავშირება (სასურველია)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"ანგარიშიდან გამოერთება\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"გსურთ ანგარიშიდან გამოერთება?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"ცვლილება დაუყოვნებლივ აისახება. ნამდვილად გსურთ, ანგარიშიდან გამოერთება?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"ნაკადის გასაღების მიღება\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"ვრცლად\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"ნაკადის გასაღების გამოყენება\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"ნაკადის გასაღები (გამოცდილთათვის)\"\nBasic.AutoConfig.StreamPage.Service=\"მომსახურება\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"ყველას ჩვენება...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"მითითებული...\"\nBasic.AutoConfig.StreamPage.Server=\"სერვერი\"\nBasic.AutoConfig.StreamPage.StreamKey=\"ნაკადის გასაღები\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: შეიყვანეთ დაშიფვრის პაროლი.\\nRTMP: შეიყვანეთ მომსახურების მიერ მოწოდებული გასაღები.\\nSRT: შეიყვანეთ ნაკადის კოდი, თუ გააჩნია მომსახურებას.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"დამშიფრავის გასაღები\"\nBasic.AutoConfig.StreamPage.BearerToken=\"საშუამავლო საცნობი\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"დაკავშირებული ანაგრიში\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"მიახლოებითი ბიტსიხშირე, გამოთვლილი ქსელის გამტარუნარიანობის შემოწმებით (რამდენიმე წუთს შესაძლოა გასტანოს)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"აპარატურული დაშიფვრის გამოყენება\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"აპარატურული დაშიფვრა შეამცირებს პროცესორზე დატვირთვას, მაგრამ შესაძლოა მეტი ბიტსიხშირე დასჭირდეს იმავე ხარისხის მისაღწევად.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"გაფრთხილება ნაკადის გაშვებისას\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"ქსელის გამტარუნარიანობის შემოწმება გულისხმობს, თქვენს არხზე შემთხვევითი ვიდეოფაილების ხმის გაშერე ნაკადად გაშვებას. სასურველია, თუ დროებით გათიშავთ ნაკადის შენახვის შესაძლებლობას და შემოწმების დასრულებამდე ამ ნაკადს გადაიყვანთ პირად რეჟიმში. გსურთ, განაგრძოთ?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"ტესტი %1\"\nBasic.AutoConfig.TestPage=\"საბოლოო შედეგები\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"პროგრამა ახლა უშვებს სხვადასხვა სახის შემოწმებებს, სასურველი პარამეტრების დადგენის მიზნით\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"შემოწმება დასრულებულია\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"მოწმდება ქსელის გამტარუნარიანობა, შესაძლოა, რამდენიმე წუთს გასტანოს...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"გამოტანა მიუწვდომელი ამ ოქმის მქონე მომსახურებისთვის\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"უკავშირდება სერვერს: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"ვერცერთ სერვერთან დაკავშირება ვერ მოხერხდა. შეამოწმეთ თქვენი ინტერნეტთან კავშირი და სცადეთ ხელახლა.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"მოწმდება გამტარუნარიანობა სერვერისთვის: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"ნაკადის დამშიფრავის შემოწმება, შესაძლოა ერთ წუთამდე გასტანოს...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"ჩანაწერის დამშიფრავის შემოწმება, შესაძლოა ერთ წუთამდე გასტანოს...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"დამშიფრავის ჩართვა ვერ მოხერხდა\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"მოწმდება %1x%2 %3 FPS (კადრ/წმ)...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"ნაკადის მშიფრავი\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"ჩანაწერის მშიფრავი\"\nBasic.AutoConfig.TestPage.Result.Header=\"პროგრამის მიერ დადგენილი მიახლოებითი პარამეტრები, რომელიც მეტად გამოსადეგია თქვენთვის:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"თუ გსურთ ამ პარამეტრების გამოყენება, დააწკაპეთ „პარამეტრების მიღებას“. თუ გსურთ პარამეტრების ხელახლა დადგენა, დააწკაპეთ ღილაკს „უკან“. ხოლო, თუ პარამეტრების ხელით გამართვა გსურთ, დააწკაპეთ „გაუქმებას“ და გადადით პარამეტრებზე.\"\nBasic.AutoConfig.Info=\"თვითგამართვის მეგზური ირჩევს საუკეთესო პარამეტრებს თქვენი კომპიუტერის სიმძლავრისა და ინტერნეტსიჩქარის მიხედვით.\"\nBasic.AutoConfig.RunAnytime=\"მისი გაშვება ნებისმიერ დროს შეიძლება ხელსაწყოების მენიუდან.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"ნაკადის (ზომაცვლილის) გაფართოება\"\nBasic.Stats=\"გაზომვები\"\nBasic.Stats.CPUUsage=\"CPU დატვირთვა\"\nBasic.Stats.HDDSpaceAvailable=\"ხელმისაწვდომი ადგილი დისკზე\"\nBasic.Stats.MemoryUsage=\"მეხსიერების დატვირთვა\"\nBasic.Stats.AverageTimeToRender=\"კადრის დამუშავების საშუალო დრო\"\nBasic.Stats.SkippedFrames=\"დაშიფვრის შეყოვნებით დაკლებული კადრები\"\nBasic.Stats.MissedFrames=\"დამუშავების შეყოვნებით დაკლებული კადრები\"\nBasic.Stats.Output.Stream=\"ნაკადი\"\nBasic.Stats.Output.Recording=\"ჩაწერა\"\nBasic.Stats.Status=\"მდგომარეობა\"\nBasic.Stats.Status.Recording=\"მიმდინარეობს\"\nBasic.Stats.Status.Live=\"ეთერშია\"\nBasic.Stats.Status.Reconnecting=\"ხელახლა დაკავშირება\"\nBasic.Stats.Status.Inactive=\"უმოქმედო\"\nBasic.Stats.Status.Active=\"მოქმედი\"\nBasic.Stats.DroppedFrames=\"კადრების ვარდნა (ქსელი)\"\nBasic.Stats.MegabytesSent=\"საერთო მოცულობა\"\nBasic.Stats.Bitrate=\"ბიტსიხშირე\"\nBasic.Stats.DiskFullIn=\"დარჩენილი ადგილი დისკზე (დაახლ.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 საათი, %2 წუთი\"\nBasic.Stats.ResetStats=\"გაზომვების განულება\"\nResetUIWarning.Title=\"ნამდვილად გსურთ იერსახის პარამეტრების ჩამოყრა?\"\nResetUIWarning.Text=\"შედეგად, დამატებითი ნაწილები დაიმალება. მათი ხელახლა გამოჩენა, შესაძლებელი იქნება იერსახის ნაწილების მენიუდან, სურვილის შემთხვევაში.\\n\\nნამდვილად გსურთ იერსახის პარამეტრების ჩამოყრა?\"\nUpdater.Title=\"ხელმისაწვდომია განახლება\"\nUpdater.Text=\"ხელმისაწვდომია განახლება:\"\nUpdater.UpdateNow=\"ახლავე განახლება\"\nUpdater.RemindMeLater=\"მოგვიანებით შეხსენება\"\nUpdater.Skip=\"ვერსიის გამოტოვება\"\nUpdater.NoUpdatesAvailable.Title=\"განახლებები არაა ხელმისაწვდომი\"\nUpdater.NoUpdatesAvailable.Text=\"ამჟამად, განახლებები არაა ხელმისაწვდომი\"\nUpdater.BranchNotFound.Title=\"განახლების არხი მოცილებულია\"\nUpdater.BranchNotFound.Text=\"თქვენ მიერ შერჩეული განახლების არხი აღარაა ხელმისაწვდომი, OBS დაბრუნდება ნაგულისხმევზე.\"\nUpdater.RepairButUpdatesAvailable.Title=\"მთლიანობის შემოწმება მიუწვდომელია\"\nUpdater.RepairButUpdatesAvailable.Text=\"მთლიანობის გადამოწმება შესაძლებელია მხოლოდ ბოლო ხელმისაწვდომი ვერსიისთვის. იხილეთ „დახმარება“ → „განახლებებზე შემოწმება“ და დარწმუნდით, რომ უახლესი OBS აყენია.\"\nUpdater.RepairConfirm.Title=\"მთლიანობის გადამოწმების დასტური\"\nUpdater.RepairConfirm.Text=\"მთლიანობის შემოწმებით მოინახება დაყენებული OBS-ის დაზიანებული ფაილები და ახლიდან ჩამოიტვირთება მათი შემცვლელი. გარკვეულ ხანს შეიძლება გასტანოს.\\n\\nგსურთ, განაგრძოთ?\"\nUpdater.FailedToLaunch=\"განახლების გაშვება ვერ მოხერხდა\"\nQuickTransitions.SwapScenes=\"გადასვლისას შეთვალიერება/გადაცემული სცენების შენაცვლება\"\nQuickTransitions.SwapScenesTT=\"გადასვლისას შესათვალიერებელი და გამოტანილი სცენები ადგილებს გაცვლის (თუ გადაცემულის თავდაპირველი სცენა ჯერ კიდევ შემორჩენილია).\\გადაცემულის თავდაპირველ სცენაზე გაკეთებული ცვლილებები შენარჩუნდება.\"\nQuickTransitions.DuplicateScene=\"სცენის გაორკეცება\"\nQuickTransitions.DuplicateSceneTT=\"იმავე სცენის ჩასწორებისას, წყაროების გარდაქმნა/გამოჩენას შეძლებთ გამოტანილის ხელუხლებლად.\\წყაროს პარამეტრების ჩასასწორებლად კი ჩართეთ „წყაროს გაორკეცება“.\\nამ მნიშვნელობის ცვლილებისას გამოტანილი სცენა ბრუნდება საწყისზე (თუ კიდევ შერმოჩენილია).\"\nQuickTransitions.EditProperties=\"წყაროს გაორკეცება\"\nQuickTransitions.EditPropertiesTT=\"იმავე სცენის ჩასწორებისას, წყაროს პარამეტრების შეცვლას შეძლებთ გამოტანილის ხელუხლებლად.\\nიმუშავებს მხოლოდ „სცენის გაორკეცებისას“.\\nზოგიერთი წყარო (მაგალითად გადაღებისას ან ფაილების ჩვენებისას) მხარდაუჭერელია და სათითაოდ ვერ ჩასწორდება.\\nამ მნიშვნელობის ცვლილებისას გამოტანილი სცენა ბრუნდება საწყისზე (თუ კიდევ შემორჩენილია).\\n\\nგაფრთხილება: წყაროს გაორკეცებისას შესაძლოა სისტემის და ვიდეოდაფის დატვირთვამ მოიმატოს.\"\nQuickTransitions.HotkeyName=\"სწრაფი გადასვლა: %1\"\nBasic.AddTransition=\"გასამართი გადასვლის დამატება\"\nBasic.RemoveTransition=\"გასამართი გადასვლის ამოშლა\"\nBasic.TransitionProperties=\"გადასვლის პარამეტრები\"\nBasic.SceneTransitions=\"სცენების გადასვლები\"\nBasic.TransitionDuration=\"ხანგრძლივობა\"\nBasic.TogglePreviewProgramMode=\"სტუდიური რეჟიმი\"\nBasic.EnablePreviewProgramMode=\"სტუდიური რეჟიმის ჩართვა\"\nBasic.DisablePreviewProgramMode=\"სტუდიური რეჟიმის გამორთვა\"\nUndo.Undo=\"გაუქმება\"\nUndo.Redo=\"კვლავ\"\nUndo.Add=\"დამატება – „%1“\"\nUndo.Delete=\"წაშლა – „%1“\"\nUndo.Rename=\"გადარქმევა – „%1“\"\nUndo.SceneCollection.Switch=\"გადართვა – „%1“\"\nUndo.Item.Undo=\"გაუქმდეს %1\"\nUndo.Item.Redo=\"კვლავ %1\"\nUndo.Sources.Multi=\"%1 წყაროს წაშლა\"\nUndo.Filters=\"ცვლილებები ფილტრში „%1“\"\nUndo.Filters.Paste.Single=\"ჩასმა ფილტრისა „%1“ წყაროში „%2“\"\nUndo.Filters.Paste.Multiple=\"ფილტრების გადატანა წყაროებში „%1“ და „%2“\"\nUndo.Transform=\"გარდაიქმნას წყარო(ები), „%1“\"\nUndo.Transform.Paste=\"გარდაქმნის ჩასმა, „%1“\"\nUndo.Transform.Rotate=\"მობრუნება, „%1“\"\nUndo.Transform.Reset=\"გარდაქმნის აღდგენა, „%1“\"\nUndo.Transform.HFlip=\"თარაზულად შეტრიალება, „%1“\"\nUndo.Transform.VFlip=\"შვეულად შეტრიალება, „%1“\"\nUndo.Transform.FitToScreen=\"ეკრანზე მორგება, „%1“\"\nUndo.Transform.StretchToScreen=\"ეკრანზე გაწელვა, „%1“\"\nUndo.Transform.Center=\"ეკრანის შუაში გასწორება, „%1“\"\nUndo.Transform.VCenter=\"შვეულად შუაში გასწორება, „%1“\"\nUndo.Transform.HCenter=\"თარაზულად შუაში გასწორება, „%1“\"\nUndo.Volume.Change=\"ხმის დონის ცვლილება, „%1“\"\nUndo.Volume.Mute=\"დადუმება, „%1“\"\nUndo.Volume.Unmute=\"ახმოვანება, „%1“\"\nUndo.Balance.Change=\"ხმის წონასწორობის შეცვლა, „%1“\"\nUndo.SyncOffset.Change=\"სინქრონიზაციის გასწორება, „%1“\"\nUndo.MonitoringType.Change=\"ხმის მოსმენის ცვლილება, „%1“\"\nUndo.Mixers.Change=\"ხმის შემრევების ცვლილება, „%1“\"\nUndo.ForceMono.On=\"მონოს იძულებითი ჩართვა, „%1“\"\nUndo.ForceMono.Off=\"მონოს იძულებითი გამორთვა, „%1“\"\nUndo.Properties=\"პარამეტრების ცვლილებები, „%1“\"\nUndo.Scene.Duplicate=\"„%1“ სცენის გაორკეცება\"\nUndo.ShowTransition=\"გადასვლის ჩვენება, „%1“\"\nUndo.HideTransition=\"გადასვლის დამალვა, „%1“\"\nUndo.ShowSceneItem=\"გამოჩენა წყაროსი „%1“, „%2“\"\nUndo.HideSceneItem=\"დამალვა წყაროსი „%1“, „%2“\"\nUndo.ReorderSources=\"წყაროების გადალაგება, „%1“\"\nUndo.MoveUp=\"„%1“ წყაროს აწევა, „%2“\"\nUndo.MoveDown=\"„%1“ წყაროს ჩამოწევა, „%2“\"\nUndo.MoveToTop=\"„%1“ წყაროს სულ ზემოთ მოქცევა, „%2“\"\nUndo.MoveToBottom=\"„%1“ წყაროს სულ ქვემოთ მოქცევა, „%2“\"\nUndo.PasteSource=\"წყარო(ები)ს ჩასმა, „%1“\"\nUndo.PasteSourceRef=\"წყაროს პარამეტრ(ებ)ის ჩასმა, „%1“\"\nUndo.GroupItems=\"დაჯგუფება „%1“\"\nTransitionNameDlg.Text=\"გთხოვთ, მიუთითოთ გადასვლის სახელი\"\nTransitionNameDlg.Title=\"გადასვლის სახელი\"\nTitleBar.SafeMode=\"უსაფრთხო რეჟიმი\"\nTitleBar.PortableMode=\"გადასატანი რეჟიმი\"\nTitleBar.Profile=\"პროფილი\"\nTitleBar.Scenes=\"სცენები\"\nNameExists.Title=\"სახელი უკვე არსებობს\"\nNameExists.Text=\"სახელი უკვე გამოყენებულია.\"\nNoNameEntered.Title=\"გთხოვთ, შეიყვანოთ მართებული სახელი\"\nNoNameEntered.Text=\"ცარიელი სახელი დაუშვებელია.\"\nConfirmStart.Title=\"გაეშვას ნაკადი?\"\nConfirmStart.Text=\"ნამდვილად გსურთ პირდაპირი ეთერის გაშვება?\"\nConfirmStop.Title=\"შეწყდეს ნაკადი?\"\nConfirmStop.Text=\"ნამდვილად გსურთ პირდაპირი ეთერის შეწყვეტა?\"\nConfirmStopRecord.Title=\"შეწყდეს ჩაწერა?\"\nConfirmStopRecord.Text=\"ნამდვილად გსურთ ჩაწერის შეწყვეტა?\"\nConfirmBWTest.Title=\"შემოწმდეს ქსელის გამტარუნარიანობა?\"\nConfirmBWTest.Text=\"OBS-ისთვის მითითებული გაქვთ ქსელის გამტარუნარიანობის რეჟიმი. ეს რეჟიმი საშუალებას გაძლევთ, შეამოწმოთ ქსელის მუშაობა თქვენს არხზე პირდაპირ ეთერში გასვლის გარეშე. შემოწმების დასრულებისთანავე საჭიროა მისი გათიშვა, რომ მაყურებლებმა შეძლონ გაშვებული ნაკადის ხილვა\\n\\nგსურთ, განაგრძოთ?\"\nConfirmExit.Title=\"მოქმედი გამოტანა\"\nConfirmExit.Text=\"OBS ჯერ კიდევ მოქმედია. ყველა მიმდინარე ნაკადი/ჩაწერა შეწყდება.\"\nConfirmRemove.Title=\"წაშლის დადასტურება\"\nConfirmRemove.Text=\"ნამდვილად გსურთ, წაიშალოს „%1“?\"\nConfirmRemove.TextMultiple=\"ნამდვილად გსურთ, წაიშალოს %1 მათგანი?\"\nConfirmReset.Title=\"პარამეტრების ჩამოყრა\"\nConfirmReset.Text=\"ნამდვილად გსურთ არსებული პარამეტრების ნაგულისხმევზე დაბრუნება?\"\nOutput.StartStreamFailed=\"ნაკადის გაშვება ვერ მოხერხდა\"\nOutput.StartRecordingFailed=\"ჩაწერის დაწყება ვერ მოხერხდა\"\nOutput.StartReplayFailed=\"გადახვევის ჩართვა ვერ მოხერხდა\"\nOutput.StartVirtualCamFailed=\"ვერ გაეშვა წარმოსახვითი კამერა\"\nOutput.StartFailedGeneric=\"სიგნალის გაშვება ვერ მოხერხდა. გთხოვთ, შეამოწმეთ აღრიცხვის ფაილი, დამატებითი ინფორმაციისთვის.\\n\\nშენიშვნა: თუ იყენებთ NVENC ან AMD დამშიფრავებს, დარწმუნდით რომ თქვენი ვიდეოდაფის პროგრამა განახლებულია.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"როცა შეჩერებულია, გადასახვევი მასალა ვერ შეინახება\"\nOutput.ReplayBuffer.PauseWarning.Text=\"შენიშვნა: უკან გადასახვევი მასალა ვერ შეინახება, როცა ჩაწერა შეჩერებულია.\"\nOutput.ConnectFail.Title=\"დაკავშირება ვერ მოხერხდა\"\nOutput.ConnectFail.BadPath=\"არამართებული მისამართი ან დასაკავშირებელი ბმული. გთხოვთ, გადაამოწმოთ თქვენი პარამეტრების სისწორე.\"\nOutput.ConnectFail.ConnectFailed=\"სერვერთან დაკავშირება ვერ მოხერხდა\"\nOutput.ConnectFail.InvalidStream=\"ვერ ხერხდება მითითებულ არხთან ან ნაკადის გასაღებთან დაკავშირება, გთხოვთ, გადაამოწმოთ თქვენი ნაკადის გასაღები. თუ სწორია, მაშინ შესაძლოა ხარვეზი იყოს სერვერთან დაკავშირებისას.\"\nOutput.ConnectFail.HdrDisabled=\"HDR-გამოტანა ამჟამად გათიშულია ამ გამოტანისთვის.\"\nOutput.ConnectFail.Error=\"მოულოდნელი შეცდომა წარმოიქმნა სერვერთან დაკავშირებისას. დამატებითი ინფორმაციისთვის იხილეთ აღრიცხვის ფაილი.\"\nOutput.ConnectFail.Disconnected=\"სერვერთან კავშირი გაწყვეტილია.\"\nOutput.StreamEncodeError.Title=\"შეცდომა დაშიფვრისას\"\nOutput.StreamEncodeError.Msg=\"დამშიფრავის შეცდომა ნაკადის გაშვებისას.\"\nOutput.StreamEncodeError.Msg.LastError=\"დამშიფრავის შეცდომა ნაკადის გაშვებისას:<br><br>%1\"\nOutput.RecordFail.Title=\"ჩაწერის დაწყება ვერ მოხერხდა\"\nOutput.RecordFail.Unsupported=\"გამოტანის ეს სახეობა ან არაა მხარდაჭერილი, ან არ იძლევა ერთზე მეტი ხმოვანი ფაილის გამოყენების საშუალებას. გთხოვთ, გადაამოწმოთ თქვენი პარამეტრები და სცადოთ ხელახლა.\"\nOutput.RecordNoSpace.Title=\"არასაკმარისი ადგილი დისკზე\"\nOutput.RecordNoSpace.Msg=\"დისკზე აღარაა საკმარისი ადგილი ჩაწერის გასაგრძელებლად.\"\nOutput.RecordError.Title=\"შეცდომა ჩაწერისას\"\nOutput.RecordError.Msg=\"ჩაწერის დროს დაუდგენელი სახის შეცდომა წარმოიშვა.\"\nOutput.RecordError.EncodeErrorMsg=\"დამშიფრავის შეცდომა წარმოიშვა ჩაწერისას.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"დამშიფრავის შეცდომა ჩაწერისას:<br><br>%1\"\nOutput.BadPath.Title=\"ფაილის არამართებული მისამართი\"\nOutput.BadPath.Text=\"ჩაწერისთვის შერჩეული საქაღალდე ვერ იხსნება. გთხოვთ გადაამოწმოთ მისამართი – პარამეტრები → გამოტანა → ჩაწერა.\"\nOutput.NoBroadcast.Title=\"გადაცემა არაა გამართული\"\nOutput.NoBroadcast.Text=\"ჯერ უნდა გამართოთ გადაცემა ეთერში გაშვებამდე.\"\nOutput.BroadcastStartFailed=\"ვერ მოხერხდა გადაცემის წამოწყება\"\nOutput.BroadcastStopFailed=\"ვერ მოხერხდა გადაცემის შეწყვეტა\"\nLogUploadDialog.Title=\"OBS Studio – აღრიცხვის ფაილის ატვირთვა\"\nLogUploadDialog.Labels.PrivacyNotice=\"გთხოვთ, გაეცნოთ <a href='https://obsproject.com/privacy-policy'>პირადულობის დებულებას</a> შესაბამის მუხლებს ატვირთვის შესახებ ნებისმიერი ფაილის ატვირთვამდე.\"\nLogUploadDialog.Labels.Progress=\"აღრიცხვის ჩანაწერი აიტვირთება. მოითმინეთ...\"\nLogUploadDialog.Labels.Description.AppLog=\"აღრიცხვის ფაილი გაიგზავნა. შეგიძლიათ ბმულის გაზიარება ხარვეზების გამოსარკვევად ან მხარდაჭერის მისაღებად.\"\nLogUploadDialog.Labels.Description.CrashLog=\"უეცარი გათიშვის მოხსენება აიტვირთა. შეგიძლიათ ბმულის გაზიარება ხარვეზის გამოსარკვევად.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"ატვირთვა\"\nLogUploadDialog.Buttons.CopyURL=\"აღრიცხვის ბმულის ასლი\"\nLogUploadDialog.Buttons.AnalyzeURL=\"აღრიცხვის ფაილის დამუშავება\"\nLogUploadDialog.Buttons.RetryButton=\"გამეორება\"\nLogUploadDialog.Errors.Template=\"შეცდომა წარმოიშვა ატვირთვის მცდელობისას:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"ასატვირთი ფაილი ვერ მოიძებნა ან ცარიელია.\"\nRemux.SourceFile=\"OBS-ჩანაწერი\"\nRemux.TargetFile=\"საბოლოო ფაილი\"\nRemux.Remux=\"გადაფუთვა\"\nRemux.Stop=\"გადაფუთვის შეწყვეტა\"\nRemux.ClearFinished=\"დასრულებულების მოცილება\"\nRemux.ClearAll=\"ყველას მოცილება\"\nRemux.OBSRecording=\"OBS-ჩანაწერი\"\nRemux.FinishedTitle=\"გადაფუთვა დასრულებულია\"\nRemux.Finished=\"ჩანაწერის გადაფუთვა დასრულდა\"\nRemux.FinishedError=\"ჩანაწერის გადაფუთვა დასრულდა, თუმცა ფაილი, შესაძლოა არასრული იყოს\"\nRemux.SelectRecording=\"OBS-ის ჩანაწერის არჩევა...\"\nRemux.SelectTarget=\"საბოლოო ფაილის არჩევა...\"\nRemux.FileExistsTitle=\"საბოლოო ფაილი უკვე არჩეულია\"\nRemux.FileExists=\"მოცემული საბოლოო ფაილები უკვე არსებობს. გსურთ მათი ჩანაცვლება?\"\nRemux.ExitUnfinishedTitle=\"მიმდინარეობს გადაფუთვა\"\nRemux.ExitUnfinished=\"გადაფუთვა ჯერ არ დასრულებულა, ახლავე შეწყვეტის შედეგად, დასამუშავებელი საბოლოო ფაილი გამოუსადეგარი გახდება.\\nნამდვილად გსურთ გადაფუთვის შეწყვეტა?\"\nRemux.HelpText=\"გადმოიტანეთ ფაილები გადასაფუთად ამ ფანჯარაში ან დაწკაპეთ „OBS-ჩანაწერის“ ველის მარჯვენა კიდეზე ფაილის ასარჩევად.\"\nRemux.NoFilesAddedTitle=\"გადასაფუთი ფაილი არ დამატებულა\"\nRemux.NoFilesAdded=\"გადასაფუთი ფაილი არაა. მოათავსეთ ერთი ან რამდენიმე ვიდეოს შემცველი საქაღალდე.\"\nMissingFiles=\"ფაილები აკლია\"\nMissingFiles.MissingFile=\"დაკარგული ფაილი\"\nMissingFiles.NewFile=\"ახალი ფაილი\"\nMissingFiles.HelpText=\"ზოგი ფაილი აკლია OBS-ის წინა გამოყენებიდან.\"\nMissingFiles.Clear=\"<გასუფთავებული>\"\nMissingFiles.NumFound=\"%1 ნაპოვნია %2-იდან\"\nMissingFiles.Search=\"საქაღალდის ძიება...\"\nMissingFiles.SelectFile=\"ფაილის არჩევა...\"\nMissingFiles.SelectDir=\"საქაღალდის არჩევა ძიებისთვის\"\nMissingFiles.State=\"მდგომარეობა\"\nMissingFiles.Missing=\"აკლია\"\nMissingFiles.Replaced=\"შეცვლილია\"\nMissingFiles.Cleared=\"წაშლილია\"\nMissingFiles.Found=\"ნაპოვნია\"\nMissingFiles.AutoSearch=\"ნაპოვნია სხვა შესაბამისი ფაილებიც\"\nMissingFiles.AutoSearchText=\"OBS-მა ამ საქაღალდეში დაკარგული ფაილების შესაბამისი სხვა ფაილებიც იპოვა. გსურთ მათი დამატება?\"\nMissingFiles.NoMissing.Title=\"ნაკლული ფაილების შემოწმება\"\nMissingFiles.NoMissing.Text=\"ფაილები, წესით არ აკლია.\"\nMacPermissions.MenuAction=\"პროგრამის ნებართვების გადახედვა...\"\nMacPermissions.Title=\"პროგრამის ნებართვების გადახედვა\"\nMacPermissions.Description=\"OBS Studio საჭიროებს თქვენგან ნებართვის მინიჭებას სხვადასხვა შესაძლებლობის შემოთავაზებლად. ამიტომ სასურველია, რომ დართოთ ნება, თუმცა აუცილებელი არაა პროგრამის გამოსაყენებლად. შეგიძლიათ, მოგვიანებით ჩართოთ.\"\nMacPermissions.Description.OpenDialog=\"ეს ფანჯარა შეგიძლიათ კვლავ გახსნათ OBS Studio-ს მენიუდან.\"\nMacPermissions.AccessGranted=\"წვდომა ნებადართულია\"\nMacPermissions.RequestAccess=\"წვდომის მოთხოვნა\"\nMacPermissions.OpenPreferences=\"%1 – პარამეტრების გახსნა\"\nMacPermissions.Item.ScreenRecording=\"ეკრანის ჩაწერა\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS საჭიროებს ამ ნებართვას ეკრანის გადასაღებად.\"\nMacPermissions.Item.Camera=\"კამერა\"\nMacPermissions.Item.Camera.Details=\"ამ ნებართვას საჭიროებს ვებკამერიდან ან ჩამწერი დაფიდან ვიდეოს გადასაღებად.\"\nMacPermissions.Item.Microphone=\"მიკროფონი\"\nMacPermissions.Item.Microphone.Details=\"OBS საჭიროებს ამ ნებართვას მიკროფონიდან ან სხვა გარე მოწყობილობიდან ხმის ჩასაწერად.\"\nMacPermissions.Item.InputMonitoring=\"შეტანის მეთვალყურეობა\"\nMacPermissions.Item.InputMonitoring.Details=\"ეს ნებართვა საჭიროა, რომ ღილაკებმა იმუშაოს, როცა OBS ფონურადაა გაშვებული.\"\nMacPermissions.Item.Accessibility=\"ხელმისაწვდომობა\"\nMacPermissions.Item.Accessibility.Details=\"ძველად დაყენებისას, OBS შეიძლება აღნუსხული იყოს „ხელმისაწვდომობის“ არეში „შეტანის მეთვალყურეობის“ ნაცვლად.\"\nMacPermissions.Continue=\"განაგრძეთ\"\nSourceLeak.Title=\"წყაროს გასუფთავების შეცდომა\"\nSourceLeak.Text=\"ხარვეზი წარმოიშვა სცენის კრებულის ცვლილებისას და ზოგიერთი წყაროს მოცილება ვერ მოხერხდა. ჩვეულებრივ, ამის მიზეზია ხოლმე დანამატები, რომლებიც ვერ ახერხებს მასალების სათანადოდ გამოთავისუფლებას. გთხოვთ დარწმუნდეთ, რომ ყველა დანამატი განახლებულია.\\n\\nOBS Studio ახლა დაიხურება მონაცემთა დაზიანების ასარიდებლად.\"\nBasic.DesktopDevice1=\"სამუშაო ეკრანის ხმა\"\nBasic.DesktopDevice2=\"სამუშაო ეკრანის ხმა 2\"\nBasic.Scene=\"სცენა\"\nBasic.DisplayCapture=\"ეკრანის გადაღება\"\nBasic.Main.PreviewConextMenu.Enable=\"შეთვალიერების ჩართვა\"\nBasic.Main.Preview.Disable=\"შეთვალიერების გამორთვა\"\nScaleFiltering=\"ზომების შეცვლის ფილტრი\"\nScaleFiltering.Point=\"წერტილოვანი\"\nScaleFiltering.Bilinear=\"ორხაზოვანი\"\nScaleFiltering.Bicubic=\"ბიკუბური\"\nScaleFiltering.Area=\"სივრცითი\"\nBlendingMethod=\"შერევის რეჟიმი\"\nBlendingMethod.Default=\"ნაგულისხმევი\"\nBlendingMethod.SrgbOff=\"sRGB გამორთ.\"\nBlendingMode=\"შერევის რეჟიმი\"\nBlendingMode.Normal=\"ჩვეულებრივი\"\nBlendingMode.Additive=\"დამატება\"\nBlendingMode.Subtract=\"გამოკლება\"\nBlendingMode.Screen=\"ეკრანი\"\nBlendingMode.Multiply=\"გამრავლება\"\nBlendingMode.Lighten=\"გაღიავება\"\nBlendingMode.Darken=\"გამუქება\"\nDeinterlacing=\"ხაზგამოუშვებლად (Deinterlacing)\"\nDeinterlacing.Discard=\"გაუქმება\"\nDeinterlacing.TopFieldFirst=\"ზედა ველიდან\"\nDeinterlacing.BottomFieldFirst=\"ქვედა ველიდან\"\nVolControl.SliderUnmuted=\"ხმის სამართავი „%1“:\"\nVolControl.SliderMuted=\"ხმის სამართავი „%1“: (ამჟამად დადუმებული)\"\nVolControl.Mute=\"'%1' დადუმება\"\nVolControl.Properties=\"„%1“ – პარამეტრები\"\nVolControl.UnassignedWarning.Title=\"უმოქმედო ხმოვანი წყარო\"\nVolControl.UnassignedWarning.Text=\"„%1“ არაა მინიჭებული არცერთი ხმოვანი ჩანაწერისთვის და ვერ გამოსცემს ხმას ჩაწერისას და ნაკადის ეთერში გაშვებისას.\\n\\nხმოვანი წყაროს ჩანაწერთან დასაკავშირებლად გახსენით ხმის გაფართოებული პარამეტრები მარჯვენა წკაპის მენიუდან ან ხრახნის ნიშნით ხმის შემრევის განყოფილების ხელსაწყოებიდან.\"\nBasic.Main.AddSceneDlg.Title=\"სცენის დამატება\"\nBasic.Main.AddSceneDlg.Text=\"გთხოვთ, მიუთითოთ სცენის დასახელება\"\nBasic.Main.DefaultSceneName.Text=\"სცენა %1\"\nBasic.Main.AddSceneCollection.Title=\"სცენის კრებულის დამატება\"\nBasic.Main.AddSceneCollection.Text=\"გთხოვთ, მიუთითოთ სცენის კრებულის დასახელება\"\nBasic.Main.RenameSceneCollection.Title=\"სცენის კრებულის გადარქმევა\"\nBasic.Main.RemigrateSceneCollection.Title=\"სცენის კრებულის გაფართოების განახლება\"\nBasic.Main.RemigrateSceneCollection.Text=\"სცენის კრებულის განახლებულ გაფართოებად მიეთითოს „%1“, რომ დაემთხვეს მიმდინარე პროფილის ფონის გაფართოებას, რომელიცაა %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"ვერ განახლდება სცენის კრებულის გაფართოება, როცა გამოტანა მოქმედია.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"ვერ განახლდა სცენის კრებულის გაფართოება. თავდაპირველი გაფართოება უცნობია.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"ჩამოყრა შეუძლებელია: ვერ ახერხებს OBS გაფართოების ცვლილებას.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"ჩამოყრა შეუძლებელია: მიმდინარე გაფართოება ისედაც ძირითადი გაფართოებაა სცენის კრებულისთვის.\"\nAddProfile.Title=\"პროფილის დამატება\"\nAddProfile.Text=\"გთხოვთ, მიუთითოთ პროფილის დასახელება\"\nAddProfile.WizardCheckbox=\"თვითგამართვის მეგზურის ჩვენება\"\nRenameProfile.Title=\"პროფილის გადარქმევა\"\nBasic.Main.MixerRename.Title=\"ხმის წყაროს გადარქმევა\"\nBasic.Main.MixerRename.Text=\"გთხოვთ, მიუთითოთ ხმის წყაროს დასახელება\"\nBasic.Main.PreviewDisabled=\"შეთვალიერება მიუწვდომელია\"\nBasic.SourceSelect=\"წყაროს შექმნა/მითითება\"\nBasic.SourceSelect.CreateNew=\"ახლის შექმნა\"\nBasic.SourceSelect.AddExisting=\"არსებულის დამატება\"\nBasic.SourceSelect.AddVisible=\"წყაროს გამოჩენა\"\nBasic.Main.Sources.Visibility=\"ხილვადობა\"\nBasic.Main.Sources.VisibilityDescription=\"განსაზღვრავს, ხილული იქნება თუ არა „%1“ საერთო ფონზე\"\nBasic.Main.Sources.Lock=\"ჩაკეტვა\"\nBasic.Main.Sources.LockDescription=\"„%1“ არ შეიცვლის ზომასა და მდებარეობას საერთო ფონზე\"\nBasic.PropertiesWindow=\"„%1“ – პარამეტრები\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (თვითშერჩევა: %2)\"\nBasic.PropertiesWindow.SelectColor=\"ფერის შერჩევა\"\nBasic.PropertiesWindow.SelectFont=\"შრიფტის შერჩევა\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"შრიფტის შერჩევა\"\nBasic.PropertiesWindow.ConfirmTitle=\"პარამეტრები შეცვლილია\"\nBasic.PropertiesWindow.Confirm=\"ცვლილებები არაა დამახსოვრებული. გსურთ მათი შენახვა?\"\nBasic.PropertiesWindow.NoProperties=\"პარამეტრები მიუწვდომელია\"\nBasic.PropertiesWindow.AddFiles=\"ფაილების დამატება\"\nBasic.PropertiesWindow.AddDir=\"საქაღალდის დამატება\"\nBasic.PropertiesWindow.AddURL=\"მისამართი/ბმულის დამატება\"\nBasic.PropertiesWindow.AddEditableListDir=\"საქაღალდის დამატება – „%1“\"\nBasic.PropertiesWindow.AddEditableListFiles=\"ფაილების დამატება – „%1“\"\nBasic.PropertiesWindow.AddEditableListEntry=\"ჩანაწერის დამატება – „%1“\"\nBasic.PropertiesWindow.EditEditableListEntry=\"ჩანაწერის ჩასწორება – „%1“\"\nBasic.PropertiesView.FPS.Simple=\"ჩვეულებრივი FPS-მნიშვნელობები\"\nBasic.PropertiesView.FPS.Rational=\"რაციონალურ რიცხვიანი FPS-მნიშვნელობები\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"დაშვებული FPS-შუალედები:\"\nBasic.PropertiesView.UrlButton.Text=\"გაიხსნას ბმული ნაგულისხმევ ბრაუზერში?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"ბმული: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"ბმულის გახსნა\"\nBasic.InteractionWindow=\"თაგვით ზემოქმედება ფანჯარაში – „%1“\"\nBasic.StatusBar.Reconnecting=\"კავშირი გაწყვეტილია. ხელახლა დაკავშირება %2 წამში (%1 მცდელობა)\"\nBasic.StatusBar.AttemptingReconnect=\"ხელახლა დაკავშირება... (%1 მცდელობა)\"\nBasic.StatusBar.ReconnectSuccessful=\"ხელახლა დაუკავშირდა წარმატებით\"\nBasic.StatusBar.Delay=\"დაყოვნება (%1 წმ)\"\nBasic.StatusBar.DelayStartingIn=\"დაყოვნება (დაიწყება %1 წამში)\"\nBasic.StatusBar.DelayStoppingIn=\"დაყოვნება (შეწყდება %1 წამში)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"დაყოვნება (შეწყდება %1 წამში, დაიწყება %2 წამში)\"\nBasic.StatusBar.RecordingSavedTo=\"ჩანაწერი შენახულია – „%1“\"\nBasic.StatusBar.ReplayBufferSavedTo=\"უკან გადასახვევი მონაკვეთი შენახულია – „%1“\"\nBasic.StatusBar.ScreenshotSavedTo=\"ეკრანის სურათი შენახულია – „%1“\"\nBasic.StatusBar.AutoRemuxedTo=\"ჩანაწერი გადაიფუთა – „%1“\"\nBasic.Filters=\"ფილტრები\"\nBasic.Filters.AsyncFilters=\"ხმის/ვიდეოს ფილტრები\"\nBasic.Filters.AudioFilters=\"ხმის ფილტრები\"\nBasic.Filters.EffectFilters=\"დასამუშავებელი ფილტრები\"\nBasic.Filters.Title=\"„%1“ – ფილტრები\"\nBasic.Filters.AddFilter.Title=\"ფილტრის დასახელება\"\nBasic.Filters.AddFilter.Text=\"გთხოვთ, მიუთითოთ ფილტრის დასახელება\"\nBasic.TransformWindow=\"სცენის ელემენტის გარდაქმნა\"\nBasic.TransformWindow.Position=\"მდებარეობა\"\nBasic.TransformWindow.PositionX=\"მდებარეობა X\"\nBasic.TransformWindow.PositionY=\"მდებარეობა Y\"\nBasic.TransformWindow.Rotation=\"მობრუნება\"\nBasic.TransformWindow.Size=\"ზომა\"\nBasic.TransformWindow.Width=\"სიგანე\"\nBasic.TransformWindow.Height=\"სიმაღლე\"\nBasic.TransformWindow.Alignment=\"განლაგების გასწორება\"\nBasic.TransformWindow.BoundsType=\"შემომსაზღვრელი ჩარჩოს სახეები\"\nBasic.TransformWindow.BoundsAlignment=\"განლაგების გასწორება შემომსაზღვრელ ჩარჩოში\"\nBasic.TransformWindow.Bounds=\"შემომსაზღვრელი ჩარჩოს ზომა\"\nBasic.TransformWindow.BoundsWidth=\"შემომსაზღვრელი ჩარჩოს სიგანე\"\nBasic.TransformWindow.BoundsHeight=\"შემომსაზღვრელი ჩარჩოს სიმაღლე\"\nBasic.TransformWindow.CropToBounds=\"მოჭრა შემომსაზღვრელ ჩარჩომდე\"\nBasic.TransformWindow.Crop=\"შემოჭრა\"\nBasic.TransformWindow.CropLeft=\"მარცხნივ შემოჭრა\"\nBasic.TransformWindow.CropRight=\"მარჯვნივ შემოჭრა\"\nBasic.TransformWindow.CropTop=\"ზემოთ შემოჭრა\"\nBasic.TransformWindow.CropBottom=\"ქვემოთ შემოჭრა\"\nBasic.TransformWindow.Alignment.TopLeft=\"მარცხნივ ზემოთ\"\nBasic.TransformWindow.Alignment.TopCenter=\"შუაში ზემოთ\"\nBasic.TransformWindow.Alignment.TopRight=\"მარჯვნივ ზემოთ\"\nBasic.TransformWindow.Alignment.CenterLeft=\"მარცხნივ შუაში\"\nBasic.TransformWindow.Alignment.Center=\"შუაში\"\nBasic.TransformWindow.Alignment.CenterRight=\"მარჯვნივ შუაში\"\nBasic.TransformWindow.Alignment.BottomLeft=\"მარცხნივ ქვემოთ\"\nBasic.TransformWindow.Alignment.BottomCenter=\"შუაში ქვემოთ\"\nBasic.TransformWindow.Alignment.BottomRight=\"მარჯვნივ ქვემოთ\"\nBasic.TransformWindow.BoundsType.None=\"საზღვრების გარეშე\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"მხოლოდ უმაღლესი ზომა\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"ზომების მიყვანა შიდა საზღვრებამდე\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"ზომების მიყვანა გარე საზღვრებამდე\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"ზომების დაყვანა საზღვრების სიგანემდე\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"ზომების დაყვანა საზღვრების სიმაღლემდე\"\nBasic.TransformWindow.BoundsType.Stretch=\"გაწელვა საზღვრებამდე\"\nBasic.TransformWindow.Title=\"გარდაქმნის შესწორება – „%1“\"\nBasic.TransformWindow.NoSelectedSource=\"წყარო არაა შერჩეული\"\nBasic.Main.AddSourceHelp.Title=\"წყარო ვერ დაემატება\"\nBasic.Main.AddSourceHelp.Text=\"საჭიროა, სულ მცირე 1 სცენა წყაროს დასამატებლად.\"\nBasic.Main.Scenes=\"სცენები\"\nBasic.Main.Sources=\"წყაროები\"\nBasic.Main.Source=\"წყარო\"\nBasic.Main.Controls=\"სამართავი\"\nBasic.Main.PreparingStream=\"მომზადება\"\nBasic.Main.Connecting=\"უკავშირდება...\"\nBasic.Main.StartRecording=\"ჩაწერის დაწყება\"\nBasic.Main.StartReplayBuffer=\"გადახვევის ჩართვა\"\nBasic.Main.SaveReplay=\"გადახვევის შენახვა\"\nBasic.Main.StartStreaming=\"ნაკადის გაშვება\"\nBasic.Main.StartBroadcast=\"ეთერში გასვლა\"\nBasic.Main.StartVirtualCam=\"წარმოსახვითი კამერის ჩართვა\"\nBasic.Main.StopRecording=\"ჩაწერის შეწყვეტა\"\nBasic.Main.PauseRecording=\"ჩაწერის შეჩერება\"\nBasic.Main.UnpauseRecording=\"ჩაწერის გაგრძელება\"\nBasic.Main.SplitFile=\"ჩანაწერისთვის ფაილის დანაწევრება\"\nBasic.Main.AddChapterMarker=\"თავებად მონიშვნა (შერეული MP4/MOV მხოლოდ)\"\nBasic.Main.StoppingRecording=\"ჩაწერა წყდება...\"\nBasic.Main.StopReplayBuffer=\"გადახვევის გამორთვა\"\nBasic.Main.StoppingReplayBuffer=\"გადახვევა გამოირთვება...\"\nBasic.Main.SetupBroadcast=\"გადაცემის მართვა\"\nBasic.Main.StopStreaming=\"ნაკადის შეწყვეტა\"\nBasic.Main.StopBroadcast=\"გადაცემის დასრულება\"\nBasic.Main.AutoStopEnabled=\"(თვითშეწყვეტა)\"\nBasic.Main.StoppingStreaming=\"ნაკადი წყდება...\"\nBasic.Main.ForceStopStreaming=\"ნაკადის შეწყვეტა (დაუყოვნებლივ)\"\nBasic.Main.ShowContextBar=\"წყაროს ზოლის ჩვენება\"\nBasic.Main.HideContextBar=\"წყაროს ზოლის დამალვა\"\nBasic.Main.StopVirtualCam=\"წარმოსახვითი კამერის გათიშვა\"\nBasic.Main.Group=\"ჯგუფი %1\"\nBasic.Main.GroupItems=\"შერჩეულების დაჯგუფება\"\nBasic.Main.Ungroup=\"განჯგუფება\"\nBasic.Main.GridMode=\"ცხრილის სახით\"\nBasic.Main.ListMode=\"სიის სახით\"\nBasic.Main.VirtualCamConfig=\"წარმოსახვითი კამერის გამართვა\"\nBasic.VCam.VirtualCamera=\"წარმოსახვითი კამერა\"\nBasic.VCam.OutputType=\"გამოტანის სახეობა\"\nBasic.VCam.OutputSelection=\"გამოტანის არჩევა\"\nBasic.VCam.OutputType.Program=\"პროგრამა (ნაგულისხმევი)\"\nBasic.VCam.OutputSelection.NoSelection=\"ასარჩევი არაა გამოტანის ამ სახეობისთვის\"\nBasic.VCam.RestartWarning=\"წარმოსახვითი კამერა კვლავ გაეშვება ცვლილების ასახვისთვის\"\nBasic.MainMenu.File=\"ფაილი (&F)\"\nBasic.MainMenu.File.Export=\"შენახვა (&E)\"\nBasic.MainMenu.File.Import=\"შემოტანა (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"ჩანაწერების ნახვა (&R)\"\nBasic.MainMenu.File.Remux=\"ჩანაწერების გადაფუთვა (&M)\"\nBasic.MainMenu.File.Settings=\"პარამეტრები (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"პარამეტრების საქაღალდის ჩვენება\"\nBasic.MainMenu.File.ShowProfileFolder=\"პროფილის საქაღალდის ჩვენება\"\nBasic.MainMenu.File.ShowMissingFiles=\"ნაკლული ფაილების შემოწმება\"\nBasic.MainMenu.File.Exit=\"გამოსვლა (&X)\"\nBasic.MainMenu.Edit=\"ჩასწორება (&E)\"\nBasic.MainMenu.Edit.Undo=\"დაბრუნება (&U)\"\nBasic.MainMenu.Edit.Redo=\"კვლავ შესრულება (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"შეთვალიერების ჩაკეტვა (&L)\"\nBasic.MainMenu.Edit.Scale=\"შეთვალიერების ზომების ცვლა (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"ფანჯრის ზომამდე\"\nBasic.MainMenu.Edit.Scale.Canvas=\"ფონის ზომამდე (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"გამოტანის ზომამდე (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"ზომაცვლილი (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"მოახლოება\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"დაშორება\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"საწყისი დაშორება\"\nBasic.MainMenu.Edit.Transform=\"გარდაქმნა (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"გარდაქმნის ჩასწორება... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"გარდაქმნის ასლი\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"გარდაქმნის ჩასმა\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"გარდაქმნის გაუქმება (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"მობრუნება 90 გრადუსით საათის მიმართ.\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"მობრუნება 90 გრადუსით საათის საპირ.\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"მობრუნება 180 გრადუსით\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"თარაზულად შეტრიალება (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"შვეულად შეტრიალება (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"ეკრანის ზომაზე მორგება (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"ეკრანის ზომაზე გაწელვა (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"ეკრანის შუაში განთავსება (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"შუაში შვეულად\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"შუაში თარაზულად\"\nBasic.MainMenu.Edit.Order=\"დალაგება (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"ზემოთ აწევა (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"ქვემოთ ჩამოწევა (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"თავში გადატანა (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"ბოლოში გადატანა (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"ხმის გაფართოებული პარამეტრები (&A)\"\nBasic.MainMenu.View=\"ხედი (&V)\"\nBasic.MainMenu.View.Toolbars=\"ხელსაწყოები (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"იერსახის დანაწილება\"\nBasic.MainMenu.View.ContextBar=\"წყაროს ხელსაწყოთა ზოლი\"\nBasic.MainMenu.View.SourceIcons=\"წყაროს ხატულები (&I)\"\nBasic.MainMenu.View.StatusBar=\"მდგომარეობის ზოლი (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"სრულეკრანიანი\"\nBasic.MainMenu.View.ResetUI=\"&იერსახის აღდგენა\"\nBasic.MainMenu.View.AlwaysOnTop=\"ყოველთვის ზემოთ (&A)\"\nBasic.MainMenu.View.SceneListMode=\"სცენა სიის სახით\"\nBasic.MainMenu.Docks=\"&იერსახის ნაწილები\"\nBasic.MainMenu.Docks.ResetDocks=\"&იერსახის აღდგენა\"\nBasic.MainMenu.Docks.LockDocks=\"&იერსახის ჩაკეტვა\"\nBasic.MainMenu.Docks.SideDocks=\"&ნაწილები სრული სიმაღლით\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&შერჩეული ვებგვერდის ნაწილები...\"\nBasic.MainMenu.Import=\"შემოტანა...\"\nBasic.MainMenu.Export=\"გატანა...\"\nBasic.MainMenu.SceneCollection=\"სცენის კრებული (&S)\"\nBasic.MainMenu.Profile=\"პროფილი (&P)\"\nBasic.MainMenu.Profile.Import=\"პროფილის შემოტანა\"\nBasic.MainMenu.Profile.Export=\"პროფილის შენახვა\"\nBasic.MainMenu.SceneCollection.Import=\"სცენის კრებულის შემოტანა\"\nBasic.MainMenu.SceneCollection.Export=\"სცენის კრებულის შენახვა\"\nBasic.MainMenu.SceneCollection.Remigrate=\"ჩამოყრა ძირითად გაფართოებაზე\"\nBasic.MainMenu.SceneCollection.Migrate=\"ძირითადი გაფართოების მითითება\"\nBasic.MainMenu.Profile.Exists=\"ასეთი პროფილი უკვე არსებობს\"\nBasic.MainMenu.Tools=\"ხელსაწყოები (&T)\"\nBasic.MainMenu.Help=\"დახმარება (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"დახმარების გვერდი (&P)\"\nBasic.MainMenu.Help.Website=\"ეწვიეთ ვებსაიტს (&W)\"\nBasic.MainMenu.Help.Discord=\"&Discord სერვერზე შესვლა\"\nBasic.MainMenu.Help.WhatsNew=\"რა სიახლეებია\"\nBasic.MainMenu.Help.Logs=\"აღრიცხვის ფაილები (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"აღრიცხვის ფაილების ჩვენება (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"მიმდინარე აღრიცხვის ფაილის ატვირთვა (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"წინა აღრიცხვის ფაილის ატვირთვა (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"მიმდინარე აღრიცხვის ნახვა (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"გამოშვების ცნობები\"\nBasic.MainMenu.Help.CheckForUpdates=\"განახლებებზე შემოწმება\"\nBasic.MainMenu.Help.Repair=\"ფაილის მთლიანობის შემოწმება\"\nBasic.MainMenu.Help.RestartSafeMode=\"კვლავ გაეშვას უსაფრთხო რეჟიმში\"\nBasic.MainMenu.Help.RestartNormal=\"კვლავ გაეშვას ჩვეულებრივ რეჟიმში\"\nBasic.MainMenu.Help.CrashLogs=\"უეცარი გათიშვების მოხსენებები (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"გათიშვების მოხსენებების ჩვენება (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"წინა მოხსენების ატვირთვა (&P)\"\nBasic.MainMenu.Help.About=\"შესახებ (&A)\"\nBasic.Settings.ProgramRestart=\"ამ ცვლილებების ასახვისთვის, საჭიროა პროგრამის ხელახლა გაშვება.\"\nBasic.Settings.ConfirmTitle=\"ცვლილებების დადასტურება\"\nBasic.Settings.Confirm=\"ცვლილებები არაა დამახსოვრებული. გსურთ მათი შენახვა?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 განსაზღვრავს გაშვებული ნაკადის ზოგიერთ პარამეტრს\"\nBasic.Settings.General=\"მთავარი\"\nBasic.Settings.General.Language=\"ენა\"\nBasic.Settings.General.Updater=\"განახლებები\"\nBasic.Settings.General.UpdateChannel=\"განახლების არხი\"\nBasic.Settings.General.UpdateChannelDisabled=\"(გათიშული)\"\nBasic.Settings.General.UpdateChannelDefault=\"(ნაგულისხმევი)\"\nBasic.Settings.General.EnableAutoUpdates=\"განახლებებზე შემოწმება ჩართვისას\"\nBasic.Settings.General.OpenStatsOnStartup=\"აღრიცხვების ფანჯრის გახსნა ჩართვისას\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"OBS-ფანჯრების დამალვა ეკრანის გადაღებისას\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"შედეგად, ყველა არასაჩვენებელი OBS-ფანჯარა დაიფარება გადაღებისგან და ეს შეეხება \\nსხვა პროგრამებსაც, როგორიცაა თათბირის მოსაწყობი, ეკრანის გასაზიარებელი, დაშორებულად სამართავი, ეკრანის სურათის აღსაბეჭდი და მსგავსი საშუალებები.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"ამ შესაძლებლობის მეშვეობით ყველა არასაჩვენებელი OBS-ფანჯარა დაიფარება გადაღებისგან და ეს შეეხება სხვა პროგრამებსაც, როგორიცაა თათბირის მოსაწყობი, ეკრანის გასაზიარებელი, დაშორებულად სამართავი, ეკრანის სურათის გადამღები და მსგავსი საშუალებები.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"დასტურის მოთხოვნა ეთერში გაშვებისას\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"დასტურის მოთხოვნა ნაკადის შეწყვეტისას\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"დასტურის მოთხოვნა ჩაწერის შეწყვეტისას\"\nBasic.Settings.General.Projectors=\"საჩვენებელი გამოტანა\"\nBasic.Settings.General.HideProjectorCursor=\"მაჩვენებლის დამალვა ჩვენებებზე\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"საჩვენებელი ფანჯრის ზემოთ შენარჩუნება\"\nBasic.Settings.General.Snapping=\"წყაროს ჩარჩოს მიზიდვა განთავსებისას\"\nBasic.Settings.General.ScreenSnapping=\"წყაროს ჩარჩოს მიზიდვა ეკრანის კიდესთან\"\nBasic.Settings.General.CenterSnapping=\"წყაროს ჩარჩოს შუაწერტილისკენ მიზიდვა, თარაზულად და შვეულად\"\nBasic.Settings.General.SourceSnapping=\"წყაროს ჩარჩოს სხვა წყაროს ჩარჩოსთან მიზიდვა\"\nBasic.Settings.General.SnapDistance=\"მიზიდვის ძალა\"\nBasic.Settings.General.SpacingHelpers=\"პიქსელთა განლაგების მიმთითებლები\"\nBasic.Settings.General.RecordWhenStreaming=\"პირდაპირი ეთერის თვითჩაწერა\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"ჩაწერის გაგრძელება ნაკადის შეწყვეტის შემდეგ\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"გადახვევის ჩართვა ავტომატურად ნაკადის გაშვებისას\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"გადახვევის შენარჩუნება, ნაკადის შეწყვეტისას\"\nBasic.Settings.General.SysTray=\"სისტემის არე\"\nBasic.Settings.General.SysTrayWhenStarted=\"სისტემის არეში ჩაკეცვა გაშვებისთანავე\"\nBasic.Settings.General.SystemTrayHideMinimize=\"ყოველთვის სისტემის არეში ჩაკეცვა, ამოცანათა ზოლის ნაცვლად\"\nBasic.Settings.General.SaveProjectors=\"ჩვენებების დამახსოვრება გასვლისას\"\nBasic.Settings.General.CloseExistingProjectors=\"მხოლოდ თითო სრულეკრანიანი ჩვენება თითო ეკრანზე.\"\nBasic.Settings.General.Preview=\"შეთვალიერება\"\nBasic.Settings.General.OverflowHidden=\"ზედმეტი არეების დამალვა\"\nBasic.Settings.General.OverflowAlwaysVisible=\"ზედმეტი არეების ყოველთვის გამოჩენა\"\nBasic.Settings.General.OverflowSelectionHidden=\"ზედმეტი არეების გამოჩენა უხილავი წყაროს შემთხვევაშიც\"\nBasic.Settings.General.Importers=\"შემომტანები\"\nBasic.Settings.General.AutomaticCollectionSearch=\"ნაცნობი ადგილების მოძიება, სცენის კრებულების შემოტანისას\"\nBasic.Settings.General.SwitchOnDoubleClick=\"სცენაზე გადასვლა ორჯერ დაწკაპებისას\"\nBasic.Settings.General.StudioPortraitLayout=\"შვეული განლაგების ჩართვა\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"შეთვალიერების/პროგრამის წარწერების ჩვენება\"\nBasic.Settings.General.Multiview=\"მრავალხედიანი ჩვენება\"\nBasic.Settings.General.Multiview.MouseSwitch=\"სცენებს შორის გადართვა დაწკაპებით\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"სცენის სახელების ჩვენება\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"უსაფრთხო არეების გამოსახვა (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"მრავალხედიანი განლაგება\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"თარაზულად, ზემოთ (8 სცენა)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"თარაზულად, ქვემოთ (8 სცენა)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"შვეულად, მარცხნივ (8 სცენა)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"შვეულად, მარჯვნივ (8 სცენა)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"თარაზულად, ზემოთ (18 სცენა)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"თარაზულად, ზემოთ (24 სცენა)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"მხოლოდ სცენები (4 სცენა)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"მხოლოდ სცენები (9 სცენა)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"მხოლოდ სცენები (16 სცენა)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"მხოლოდ სცენები (25 სცენა)\"\nBasic.Settings.General.ChannelName.stable=\"მდგრადი\"\nBasic.Settings.General.ChannelDescription.stable=\"უახლესი მდგრადი გამოშვება\"\nBasic.Settings.General.ChannelName.beta=\"საცდელი / გამოსაშვებად მომზადებული\"\nBasic.Settings.General.ChannelDescription.beta=\"სავარაუდოდ არამდგრადი გამოშვებამდელი ვერსიები\"\nBasic.Settings.Appearance=\"იერსახე\"\nBasic.Settings.Appearance.General=\"ზოგადი\"\nBasic.Settings.Appearance.General.Theme=\"თემა\"\nBasic.Settings.Appearance.General.Variant=\"გაფორმება\"\nBasic.Settings.Appearance.General.NoVariant=\"სტილები არაა\"\nBasic.Settings.Appearance.FontScale=\"შრიფტის ზომა\"\nBasic.Settings.Appearance.Density=\"სიმჭიდროვე\"\nBasic.Settings.Appearance.Classic=\"ძველებური\"\nBasic.Settings.Appearance.Compact=\"მჭიდრო\"\nBasic.Settings.Appearance.Normal=\"საშუალო\"\nBasic.Settings.Appearance.Comfortable=\"მოსახერხებელი\"\nBasic.Settings.Appearance.OptionsWarning=\"ზოგიერთი პარამეტრი მიუწვდომელია ამ იერსახისთვის.\"\nBasic.Settings.Stream=\"ნაკადი\"\nBasic.Settings.Stream.Destination=\"დანიშნულება\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"ანგარიშზე შესვლით\"\nBasic.Settings.Stream.Custom.Username=\"მომხმარებლის სახელი\"\nBasic.Settings.Stream.Custom.Password=\"პაროლი\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: შეიყვანეთ srp_username.\\nRTMP: შეიყვანეთ მეტსახელი.\\nSRT: არაა გამოყენებული.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: შეიყვანეთ srp_password.\\nRTMP: შეიყვანეთ პაროლი.\\nSRT: შეიყვანეთ დაშიფვრის პაროლი.\"\nBasic.Settings.Stream.BandwidthTestMode=\"ქსელის გამტარუნარიანობის შემოწმების რეჟიმი\"\nBasic.Settings.Stream.TTVAddon=\"Twitch-სასაუბროს დამატებები\"\nBasic.Settings.Stream.TTVAddon.None=\"არცერთი\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV და FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"ნაკადი არასრულადაა გამართული\"\nBasic.Settings.Stream.StreamSettingsWarning=\"პარამეტრების გახსნა\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"ბმული და ნაკადის გასაღები ვერ მოიძებნა.\\n\\nგახსენით პარამეტრები და „ნაკადის“ განყოფილებაში შეიყვანეთ ბმული შესაბამისი გასაღებით.\"\nBasic.Settings.Stream.MissingUrl=\"ნაკადის ბმული ვერ მოიძებნა.\\n\\nგახსენით პარამეტრები და „ნაკადის“ განყოფილებაში მიუთითეთ ბმული.\"\nBasic.Settings.Stream.MissingStreamKey=\"ნაკადის გასაღები ვერ მოიძებნა.\\n\\nგახსენით პარამეტრები და მიუთითეთ გასაღები „ნაკადის“ განყოფილებაში.\"\nBasic.Settings.Stream.IgnoreRecommended=\"ნაკადის მომსახურების შემოთავაზებული პარამეტრების უგულებელყოფა\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"შემოთავაზებული პარამეტრების ჩანაცვლება\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"გაფრთხილება: მომსახურების ზღვრების უგულებელყოფამ, შესაძლოა გააფუჭოს ნაკადის ხარისხი ან არ მოგცეთ ეთერში გაშვების საშუალება.\\n\\nგანაგრძობთ?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"ვიდეოს უმაღლესი ბიტსიხშირე: %1 კბწმ\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"ხმის უმაღლესი ბიტსიხშირე: %1 კბწმ\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"გაფართოება არაუმეტეს: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"FPS არაუმეტეს: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"მორგებული სერვერის მითითება...\"\nBasic.Settings.Stream.ServiceCustomServer=\"მორგებული სერვერი\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"ჩართვა %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"მაქსიმალური სტრიმინგის გამტარობა\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"ავტომატური\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"მაქსიმალური ვიდეო გზა\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"ავტომატური\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"გაშვებული ნაკადის გადანახვა FLV-სახით (გამოიყენებს ფაილში ჩაწერის მარტივ პარამეტრებს)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"კონფიგურაციის გადაწერა (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"კონფიგურაციის გადაწერის ჩართვა\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"მრავალჩანაწერიანი ვიდეო\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"დამატებითი ფონი\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"თანადროული გადაცემა\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"თანაგადაცემის მეშვეობით ვიდეო იშიფრება და იგზავნება რამდენიმე ხარისხით. <a href='https://obsproject.com/kb/whip-streaming-guide'>ვრცლად</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"შრეები სულ\"\nBasic.Settings.Stream.AdvancedOptions=\"დამატებითი პარამეტრები\"\nBasic.Settings.Output=\"გამოტანა\"\nBasic.Settings.Output.Format=\"ჩაწერის ფორმატი\"\nBasic.Settings.Output.Format.MKV=\"Matroska-ვიდეო (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"შერეული MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"შერეული MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"დანაწევრებული MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"დანაწევრებული MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"დანაწევრებული MOV-ჩანაწერი შედგება ნაწილებისგან და არ ითხოვს სრული სახის მიცემას ძველებური MOV-ფაილებისგან განსხვავებით.\\nშედეგად, გასაშვებად ვარგისი რჩება ჩაწერის უეცრად შეწყვეტის შემთხვევაშიც, მაგალითად, ლურჯი ეკრანის ან ძაბვის ვარდნის გამო.\\n\\nამიტომ შეიძლება არ იყოს თავსებადი ყველა დამკვრელ და სამონტაჟო პროგრამასთან. თუ მეტად თავსებადი ფორმატია საჭირო, იხილეთ ფაილი → ჩანაწერების გადაფუთვა.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"დანაწევრებული MP4-ჩანაწერი შედგება ნაწილებისგან და არ ითხოვს სრული სახის მიცემას ძველებური MP4-ფაილებისგან განსხვავებით.\\nშედეგად, გასაშვებად ვარგისი რჩება ჩაწერის უეცრად შეწყვეტის შემთხვევაშიც, მაგალითად, ლურჯი ეკრანის ან ძაბვის ვარდნის გამო.\\n\\nამიტომ შეიძლება თავსებადი არ იყოს ყველა დამკვრელ და სამონტაჟო პროგრამასთან. თუ მეტად თავსებადი ფორმატია საჭირო, იხილეთ ფაილი → ჩანაწერების გადაფუთვა.\"\nBasic.Settings.Output.Encoder.Video=\"ვიდეომშიფრავი\"\nBasic.Settings.Output.Encoder.Audio=\"ხმის მშიფრავი\"\nBasic.Settings.Output.SelectDirectory=\"საქაღალდის არჩევა ჩანაწერისთვის\"\nBasic.Settings.Output.DynamicBitrate=\"ბიტსიხშირის ცვლა ქსელის დატვირვის ასარიდებლად\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"ბიტსიხშირის ცვლა ქსელის დატვირთვის ასარიდებლად (საცდელი)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"ქსელის დატვირთვის შესამცირებლად კადრების გამოტოვების ნაცვლად მიმდინარე ბიტსიხშირის ცვლა.\\n\\nგაითვალისწინეთ, რომ შედეგად შესაძლოა გაიზარდოს დაყოვნება მაყურებლებისთვის უეცარი დატვირთვისას.\\nბიტური სიხშირის ვარდნის შემდეგ აღდგენა რამდენიმე წუთს გასტანს.\\n\\nამჟამად მხარდაჭერილია მხოლოდ RTMP.\"\nBasic.Settings.Output.Mode=\"გამოტანის რეჟიმი\"\nBasic.Settings.Output.Mode.Simple=\"მარტივი\"\nBasic.Settings.Output.Mode.Adv=\"გაფართოებული\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg-გამოტანა\"\nBasic.Settings.Output.UseReplayBuffer=\"უკან გადახვევის შესაძლებლობა\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"გადახვევის დრო არაუმეტეს\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"მეხსიერება არაუმეტეს\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"მეხსიერების მიახლოებითი მოხმარება: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"გაფრთხილება: მეხსიერების მიახლოებითი დატვირთვაა %1 MiB, სასურველია არაუმეტეს %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"შეუძლებელია მეხსიერების მიახლოებითი მოხმარების დადგენა. გთხოვთ, მიუთითოთ მეხსიერების ზედა ზღვარი.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"უკან გადასახვევი ფაილის თავსართი\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"ბოლოსართი\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"გამეორების ბუფერის გამოყენება შეუძლებელია, როდესაც ჩაწერის ტიპი დაყენებულია მორგებულ გამომავალზე (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"ჩანაწერის მდებარეობა\"\nBasic.Settings.Output.Simple.RecordingQuality=\"ჩაწერის ხარისხი\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"გაშვებული ნაკადის შესაბამისი\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"მაღალი ხარისხი, საშუალო ზომის ფაილი\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"განუსაზღვრელი ხარისხი, დიდი ზომის ფაილი\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"უდანაკარგო ხარისხი, მეტისმეტად დიდი ზომის ფაილი\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"გაფრთხილება: ნაკადის ვიდეოს სიხშირედ მიეთითება %1, რომელიც მოცემული მომსახურების უმაღლესი ზღვრული მნიშვნელობაა.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"გაფრთხილება: ნაკადის ხმის სიხშირედ მიეთითება %1, რომელიც მოცემული მომსახურების უმაღლესი ზღვრული მნიშვნელობაა.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"შენიშვნა: ჩაწერა ვერ შეჩერდება, თუ ჩაწერის ხარისხად მითითებულია „გაშვებული ნაკადის შესაბამისი“.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"გაფრთხილება: ჩაწერის მითითებული ფორმატი შეუთავსებელია ნაკადის შერჩეულ დამშიფრავ(ებ)თან.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"გაფრთხილება: გაშვებული ნაკადისგან განსხვავებულ ხარისხში ჩაწერის დაშიფვრა ზრდის პროცესორის დატვირთვას, როცა ნაკადის გაშვება და ჩაწერა ერთდროულად მიმდინარეობს.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"გაფრთხილება: უდანაკარგო ხარისხით იქმნება მეტისმეტად დიდი ზომის ფაილები! უდანაკარგო ხარისხის ვიდეოს თითოეულმა წუთმა დისკზე შეიძლება 7 გიგაბაიტს გადააჭარბოს მაღალი გარჩევადობისა და კადრის სიხშირისას. ხანგრძლივი ჩანაწერებისთვის უდანაკარგო ხარისხი არაა მიზანშეწონილი, თუ არ გაქვთ საკმარისად დიდი თავისუფალი ადგილი დისკზე. უკან გადახვევის მომარაგება მიუწვდომელია უდანაკარგო ჩაწერისას.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"ნამდვილად გსურთ უდანაკარგო ხარისხის მითითება?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"გაფრთხილება უდანაკარგო ხარისხის შესახებ!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"პროგრამული (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"აპარატურული (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"აპარატურული (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"აპარატურული (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"აპარატურული (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"აპარატურული (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"აპარატურული (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"აპარატურული (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"აპარატურული (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"აპარატურული (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"აპარატურული (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"პროგრამული (x264 ნაკლები CPU-მოხმარების მზა პარამ. მეტი ფაილის ზომით)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (ნაგულისხმევი)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch-VOD-ჩანაწერი (გამოიყენება ჩანაწერი 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"ხმოვანი ჩანაწერი\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"არათავსებადი გაფართოება/კადრის სიხშირე\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"ნაკადის გაშვების ამ მომსახურებაზე, მითითებული გამოტანის გაფართოება და/ან კადრის სიხშირე არაა მხარდაჭერილი. შეცვლიან უახლოესი თავსებადი მნიშვნელობით:\\n\\n%1\\n\\nგსურთ, განაგრძოთ?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"გაფართოება: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"FPS (კადრი/წმ): %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"შეუთავსებელი დამშიფრავი\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"ნაკადის გამშვები მომსახურება „%1“ და დამშიფრავი „%2\\\" შეუთავსებელია. სანაცვლოდ მიეთითება „%3“.\\n\\nგსურთ, განაგრძოთ?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"ნაკადის გამშვები მომსახურება „%1“ შეუთავსებელია დამშიფრავებთან „%2\\\" და „%3“. სანაცვლოდ მიეთითება „%4“ და „%5“.\\n\\nგსურთ, განაგრძოთ?\"\nBasic.Settings.Output.VideoBitrate=\"ვიდეოს ბიტსიხშირე\"\nBasic.Settings.Output.AudioBitrate=\"ხმის ბიტსიხშირე\"\nBasic.Settings.Output.Reconnect=\"თვითდაკავშირება ხელახლა\"\nBasic.Settings.Output.RetryDelay=\"დაყოვნება მცდელობამდე\"\nBasic.Settings.Output.MaxRetries=\"მცდელობა არაუმეტეს\"\nBasic.Settings.Output.Advanced=\"დამშიფრავის გამართვა (გაფართოებული)\"\nBasic.Settings.Output.EncoderPreset=\"მშიფრავის მზა პარამ.\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (პროცესორის ნაკლებად დატვირთვა, დაბალი ხარისხი)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (ნაგულისხმევი) (პროცესორის ზომიერად დატვირთვა, საშუალო ხარისხი)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (პროცესორის მეტად დატვირთვა, მაღალი ხარისხი)\"\nBasic.Settings.Output.CustomEncoderSettings=\"მორგებული პარამ. მითითება\"\nBasic.Settings.Output.CustomMuxerSettings=\"მფუთავის პარამ. მითითება\"\nBasic.Settings.Output.NoSpaceFileName=\"ფაილის სახელის შექმნა გამოტოვებული ადგილების გარეშე\"\nBasic.Settings.Output.Adv.Rescale=\"გამოტანის ზომაცვლა\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"გამორთული\"\nBasic.Settings.Output.Adv.AudioTrack=\"ხმოვანი ჩანაწერი\"\nBasic.Settings.Output.Adv.Streaming=\"ნაკადი\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"ნაკადის პარამეტრები\"\nBasic.Settings.Output.Adv.Audio.Track1=\"ჩანაწერი 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"ჩანაწერი 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"ჩანაწერი 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"ჩანაწერი 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"ჩანაწერი 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"ჩანაწერი 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch-VOD-ჩანაწერი\"\nBasic.Settings.Output.Adv.Encoder=\"დამშიფრავის პარამეტრები\"\nBasic.Settings.Output.Adv.Recording=\"ჩაწერა\"\nBasic.Settings.Output.Adv.Recording.Settings=\"ჩაწერის პარამეტრები\"\nBasic.Settings.Output.Adv.Recording.RecType=\"ჩანაწერის სახეობა\"\nBasic.Settings.Output.Adv.Recording.Type=\"სახეობა\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"ჩვეულებრივი\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"მითითებული გამოტანა (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(გაშვებული ნაკადის მშიფრავის გამოყენება)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"ფაილის დასახელების ფორმატი\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"არსებულ ფაილზე გადაწერა\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"მითითებული გამოტანა (FFmpeg) არ ითვალისწინებს საფრთხეებს.\\nუმჯობესია გამოიყენოთ „ჩვეულებრივი“ სახის ჩაწერა.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg-გამოტანის სახეობა\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"გამოტანა ბმულზე\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"ფაილში\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"ჩაწერის ცნობილი ფორმატები\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"ყველა ფაილი\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"ფაილის მისამართი ან ბმული\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"სათავსის ფორმატი\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"ხმა\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"ვიდეო\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"ნაგულისხმევი ფორმატი\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"სათავსის ფორმატის აღწერა\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"ხმის/ვიდეოს მშიფრავის ამოცნობა მითითებული ფაილის მისამართიდან ან ბმულიდან\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"ნაგულისხმევი დამშიფრავი\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"დამშიფრავის გამორთვა\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"ვიდეოს დამშიფრავი\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"ვიდეოს მშიფრავის პარამ. (თუ არის)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"ხმის დამშიფრავი\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"ხმის მშიფრავის პარამ. (თუ არის)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"მფუთავის პარამ. (თუ აქვს)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"საკვანძო კადრ. შუალედი (კადრი)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"ყველა მშიფრავის ჩვენება (მათ შორის შეუთავსებლებისაც)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg-ის პარამეტრები\"\nBasic.Settings.Output.EnableSplitFile=\"ფაილის თვითდანაწევრება\"\nBasic.Settings.Output.SplitFile.TypeTime=\"დროის მიხედვით დაყოფა\"\nBasic.Settings.Output.SplitFile.TypeSize=\"ზომის მიხედვით დაყოფა\"\nBasic.Settings.Output.SplitFile.TypeManual=\"მხოლოდ ხელით დანაწევრება\"\nBasic.Settings.Output.SplitFile.Time=\"დაყოფის დრო\"\nBasic.Settings.Output.SplitFile.Size=\"დაყოფის ზომა\"\nScreenshot=\"გამოტანილის სურათის გადაღება\"\nScreenshot.SourceHotkey=\"შერჩეული წყაროს სურათის გადაღება\"\nScreenshot.StudioProgram=\"სურათის გადაღება (პროგრამის)\"\nScreenshot.Preview=\"სურათის გადაღება (შეთვალიერების)\"\nScreenshot.Scene=\"სურათის გადაღება (სცენის)\"\nScreenshot.Source=\"სურათის გადაღება (წყაროსი)\"\nFilenameFormatting.TT.CCYY=\"წელიწადი, ოთხი ციფრი\"\nFilenameFormatting.TT.YY=\"წელიდადი, ბოლო ორი ციფრი (00-99)\"\nFilenameFormatting.TT.MM=\"თვე ათეული რიცხვით (01-12)\"\nFilenameFormatting.TT.DD=\"თვის დღე ნულის ჩამატებით (01-31)\"\nFilenameFormatting.TT.hh=\"საათი 24-იანი ფორმატით (00-23)\"\nFilenameFormatting.TT.mm=\"წუთი (00-59)\"\nFilenameFormatting.TT.ss=\"წამი (00-59)\"\nFilenameFormatting.TT.Percent=\"%-ნიშანი\"\nFilenameFormatting.TT.a=\"კვირის დღე შემოკლებით\"\nFilenameFormatting.TT.A=\"კვირის დღე სრულად\"\nFilenameFormatting.TT.b=\"თვე შემოკლებით\"\nFilenameFormatting.TT.B=\"თვე სრულად\"\nFilenameFormatting.TT.d=\"თვის დღე ნულის ჩამატებით (01-31)\"\nFilenameFormatting.TT.H=\"საათი 24-იანი ფორმატით (00-23)\"\nFilenameFormatting.TT.I=\"საათი 12-იანი ფორმატით (01-12)\"\nFilenameFormatting.TT.m=\"თვე ათეული რიცხვით (01-12)\"\nFilenameFormatting.TT.M=\"წუთი (00-59)\"\nFilenameFormatting.TT.p=\"AM- ან PM-აღნიშვნით\"\nFilenameFormatting.TT.s=\"დრო წამებში UNIX-ათვლით\"\nFilenameFormatting.TT.S=\"წამი (00-59)\"\nFilenameFormatting.TT.y=\"წელიდადი, ბოლო ორი ციფრი (00-99)\"\nFilenameFormatting.TT.Y=\"წელიწადი\"\nFilenameFormatting.TT.z=\"ISO-8601-შესწორება დროის UTC-სარტყელში\"\nFilenameFormatting.TT.Z=\"დროის სარტყელი სრულად ან შემოკლებით\"\nFilenameFormatting.TT.FPS=\"კადრი წამში\"\nFilenameFormatting.TT.CRES=\"ძირითადი (ფონის) გაფართოება\"\nFilenameFormatting.TT.ORES=\"გამოტანის (ზომაცვლილის) გაფართოება\"\nFilenameFormatting.TT.VF=\"ვიდეოს სახეობა\"\nBasic.Settings.Video=\"ვიდეო\"\nBasic.Settings.Video.Adapter=\"ვიდეოდაფა\"\nBasic.Settings.Video.BaseResolution=\"ძირითადი (ფონის) გაფართოება\"\nBasic.Settings.Video.ScaledResolution=\"გამოტანის (ზომაცვლილის) გაფართოება\"\nBasic.Settings.Video.DownscaleFilter=\"ზომის შემცირების ფილტრი\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[გარჩევადობა ემთხვევა, შემცირების საჭიროება არაა]\"\nBasic.Settings.Video.FPSCommon=\"ცნობილი FPS-მნიშვნელობები\"\nBasic.Settings.Video.FPSInteger=\"მთელრიცხვიანი FPS-მნიშვნელობები\"\nBasic.Settings.Video.FPSFraction=\"წილადიანი FPS-მნიშვნელობები\"\nBasic.Settings.Video.Numerator=\"მრიცხველი\"\nBasic.Settings.Video.Denominator=\"მნიშვნელი\"\nBasic.Settings.Video.Renderer=\"დამმუშავებელი\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (საცდელი)\"\nBasic.Settings.Video.InvalidResolution=\"გაფართოების არასწორი მნიშვნელობა. უნდა იყოს [width]x[height] (მაგ. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"მიმდინარეობს ვიდეოს გამოტანა. გთხოვთ, გათიშოთ ყველა გამოტანილი სიგნალი, ვიდეოს პარამეტრების ჩასასწორებლად.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"ორხაზოვანი (უსწრაფესი, მაგრამ ბუნდოვანი ზომაცვლა)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"ბიკუბური (მკვეთრი ზომაცვლა, 16 შერჩევა)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (მკვეთრი ზომაცვლა, 36 შერჩევა)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"სივრცული (შეწონილი ჯამი, 4/6/9 შერჩევა)\"\nBasic.Settings.Audio=\"ხმა\"\nBasic.Settings.Audio.SampleRate=\"შერჩევის სიხშირე\"\nBasic.Settings.Audio.Channels=\"არხი\"\nBasic.Settings.Audio.Meters=\"საზომები\"\nBasic.Settings.Audio.MeterDecayRate=\"მილევის სიჩქარე\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"სწრაფი\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"საშუალო (I სახის PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"ნელი (II სახის PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"ხმის სიმაღლის მზომის სახეები\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"გამარტივებული\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"ზუსტი (პროცესორის მაღალი მოხმარებით)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"გაფრთხილება: ჩართულია მოცულობითი ხმა.\"\nBasic.Settings.Audio.MultichannelWarning=\"ნაკადის გაშვებისას გადაამოწმეთ, მომსახურების მომწოდებელი იძლევა თუ არა მოცულობითი ხმოვანი სიგნალის როგორც მიღების, ასევე მოსმენის საშუალებას. მაგალითად, Facebook 360 Live სრულად უჭერს მხარს მოცულობითი ხმის გამოყენებას; YouTube Live შეძლებს მიიღოს 5.1 ხმოვანი სიგნალი (და გაუშვას ტელევიზორში).\\n\\nOBS-ის ხმოვანი ფილტრები თავსებადია მოცულობით ხმოვან სიგნალთან, მაგრამ VST-დანამატის მხარდაჭერა შეიძლება არ იყოს უზრუნველყოფილი.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"ჩაირთოს მოცულობითი ხმოვანი სიგნალი?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"ნამდვილად გსურთ, ჩართოთ მოცულობითი ხმოვანი სიგნალი?\"\nBasic.Settings.Audio.Devices=\"საერთო ხმის მოწყობილობები\"\nBasic.Settings.Audio.DesktopDevice=\"სამუშაო ეკრანის ხმა\"\nBasic.Settings.Audio.DesktopDevice2=\"სამუშაო ეკრანის ხმა 2\"\nBasic.Settings.Audio.AuxDevice=\"მიკროფონი/დამატებითი მოწყობილობა\"\nBasic.Settings.Audio.AuxDevice2=\"მიკროფონი/დამატებითი მოწყობილობა 2\"\nBasic.Settings.Audio.AuxDevice3=\"მიკროფონი/დამატებითი მოწყობილობა 3\"\nBasic.Settings.Audio.AuxDevice4=\"მიკროფონი/დამატებითი მოწყობილობა 4\"\nBasic.Settings.Audio.EnablePushToMute=\"დაჭერით დადუმება\"\nBasic.Settings.Audio.PushToMuteDelay=\"დაყოვნება დადუმებამდე\"\nBasic.Settings.Audio.EnablePushToTalk=\"დაჭერით საუბარი\"\nBasic.Settings.Audio.PushToTalkDelay=\"დაყოვნება საუბრამდე\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[მოწყობილობა არაა დაკავშირებული ან მიუწვდომელია]\"\nBasic.Settings.Audio.Disabled=\"გამორთული\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"მცირე დაყოვნების ხმის მომარაგების რეჟიმი (Decklink/NDI-გამოტანისთვის)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"გაფრთხილება: მცირე დაყოვნების ხმის მომარაგება ჩართულია.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"მცირე დაყოვნების ხმის მომარაგების რეჟიმმა შესაძლოა წყვეტები და ხარვეზები გამოიწვიოს ზოგიერთი წყაროს გამოყენებისას.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"ჩაირთოს მცირე დაყოვნების ხმის მომარაგების რეჟიმი?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"ნამდვილად გსურთ, ჩართოთ მცირე დაყოვნების ხმის მომარაგების რეჟიმი?\"\nBasic.Settings.Accessibility=\"ხელმისაწვდომობა\"\nBasic.Settings.Accessibility.ColorOverrides=\"სხვადასხვა ფერის მითითება\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"წყაროს ჩარჩო (მონიშვნისას)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"წყაროს ჩარჩო (მოჭრისას)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"წყაროს ჩარჩო (ზედ გადატარებისას)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"შემრევის ხმის სიხშირე (-60 და -20დბ შუალედით)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"შემრევის ხმის სიხშირე (-20 და -9დბ შუალედით)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"შემრევის ხმის სიხშირე (-9 და 0დბ შუალედით)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"შემრევის ხმის სიხშირე (-60 და -20დბ შუალედით) (მოქმედი)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"შემრევის ხმის სიხშირე (-20 და -9დბ შუალედით) (მოქმედი)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"შემრევის ხმის სიხშირე (-9 და 0დბ შუალედით) (მოქმედი)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"მზა ფერები\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"ნაგულისხმევი\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"მითითებული\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"დაქვეითებული ფერითი მხედველობისთვის\"\nBasic.Settings.Advanced=\"დამატებითი\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"მოქმედი გამოტანის შესახებ გაფრთხილება გათიშვისას\"\nBasic.Settings.Advanced.General.ProcessPriority=\"უპირატესობა დამუშავებისას\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"მაღალი\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"საშუალოზე მაღალი\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"საშუალო\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"საშუალოზე დაბალი\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"უმოქმედო\"\nBasic.Settings.Advanced.FormatWarning=\"გაფრთხილება: ფერთა სახეობები, გარდა NV12/P010, ძირითადად განკუთვნილია ჩაწერისთვის და არა ეთერში გასასვლელად. ნაკადის ეთერში გაშვებისას, ფერთა სახეების მუდმივმა გარდაქმნამ, შესაძლოა გამოიწვიოს პროცესორის ზედმეტად დატვირთვა.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"გაფრთხილება: მაღალი სიზუსტის ფორმატები უფრო ხშირად გამოიყენება HDR ფერთა სივრცეებში.\"\nBasic.Settings.Advanced.FormatWarning2100=\"გაფრთხილება: Rec. 2100 მეტი სიზუსტის ფორმატს უნდა იყენებდეს.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"ფერთა ფორმატი\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-ბიტიანი, 4:2:0, 2-განზომილებიანი)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-ბიტიანი, 4:2:0, 3-განზომილებიანი)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-ბიტიანი, 4:4:4, 3-განზომილებიანი)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-ბიტიანი, 4:2:0, 2-განზომილებიანი)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-ბიტიანი, 4:2:0, 3-განზომილებიანი)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-ბიტიანი, 4:2:2, 2-განზომილებიანი)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-ბიტიანი, 4:4:4, 2-განზომილებიანი)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-ბიტიანი)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"ფერთა სივრცე\"\nBasic.Settings.Advanced.Video.ColorRange=\"ფერთა გამა\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"შეზღუდული\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"სრული\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR-არეში სითეთრის დონე\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR-ის პირობითი უკიდურესი დონე\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"მოსასმენი მოწყობილობა\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"ნაგულისხმევი\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Windows-ის მიერ ხმის დონის დადაბლების არიდება\"\nBasic.Settings.Advanced.StreamDelay=\"ნაკადის დაყოვნება\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"ხანგრძლივობა\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"მოსაჭრელი წერტილის მომარაგება (დაყოვნების გაზრდა) ხელახლა დაკავშირებისას\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"მეხსიერების მიახლოებითი მოხმარება: %1 MB\"\nBasic.Settings.Advanced.Network=\"ქსელი\"\nBasic.Settings.Advanced.Network.Disabled=\"ამჟამად მითითებული ნაკადის ოქმი არ იძლევა ქსელის პარამეტრების ცვლილების საშუალებას.\"\nBasic.Settings.Advanced.Network.BindToIP=\"დაკავშირება IP-მისამართზე\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP-ჯგუფი\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"ქსელის გამოყენების გაუმჯობესება\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"TCP-კავშირების გამართვა\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"მცდელობა, რომ RTMP-გამოტანა ნაკლებ უშლიდეს ხელს ქსელში დაყოვნებაზე დამოკიდებულ სხვა პროგრამებს გადაცემის სიხშირის მოწესრიგებით.\\nშედეგად შესაძლოა გაიზარდოს კადრების გამოტოვების ალბათობა არამდგრადი კავშირებისას.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"სწრაფი ღილაკების მოქმედება\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"არასდროს გაითიშოს\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"გაითიშოს მთავარ ფანჯარაზე გადასვლისას\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"გაითიშოს მთავარი ფანჯრიდან გადასვლისას\"\nBasic.Settings.Advanced.AutoRemux=\"თვითშეფუთვა %1 ფაილად\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(ჩაწერა mkv-ფაილად)\"\nBasic.AdvAudio=\"ხმის გაფართოებული პარამეტრები\"\nBasic.AdvAudio.ActiveOnly=\"მხოლოდ მოქმედი წყაროები\"\nBasic.AdvAudio.Name=\"სახელი\"\nBasic.AdvAudio.Volume=\"ხმის დონე\"\nBasic.AdvAudio.VolumeSource=\"ხმოვანება „%1“\"\nBasic.AdvAudio.Mono=\"მონო\"\nBasic.AdvAudio.MonoSource=\"ხმის დაყვანა მონოზე „%1“\"\nBasic.AdvAudio.Balance=\"წონასწორობა\"\nBasic.AdvAudio.BalanceSource=\"წონასწორობა „%1“\"\nBasic.AdvAudio.SyncOffset=\"სინქრ. წანაცვლება\"\nBasic.AdvAudio.SyncOffsetSource=\"სინქრ. წანაცვლება – „%1“\"\nBasic.AdvAudio.Monitoring=\"ხმის მოსმენა\"\nBasic.AdvAudio.Monitoring.None=\"მოსმენის გარეშე\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"მხოლოდ მოსმენა (უხმო გამოტანა)\"\nBasic.AdvAudio.Monitoring.Both=\"მოსმენა და გამოტანა\"\nBasic.AdvAudio.MonitoringSource=\"ხმის მოსასმენი „%1“\"\nBasic.AdvAudio.AudioTracks=\"ჩანაწერები\"\nBasic.Settings.Hotkeys=\"ღილაკები\"\nBasic.Settings.Hotkeys.Pair=\"გადამრთველის სახით იმუშავებს ღილაკთან „%1“\"\nBasic.Settings.Hotkeys.Filter=\"გამორჩევა\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"ღილაკებით გამორჩევა\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"ამ ღილაკებზე რამდენიმე მოქმედებაა მიბმული, დაწკაპეთ დამთხვევების სანახავად\"\nBasic.Settings.Hotkeys.PleaseWait=\"ცხელი კლავიშების ჩატვირთვა, გთხოვთ დაელოდოთ...\"\nBasic.Hotkeys.SelectScene=\"სცენაზე გადასვლა\"\nBasic.SystemTray.Show=\"ჩვენება\"\nBasic.SystemTray.Hide=\"დამალვა\"\nBasic.SystemTray.Message.Reconnecting=\"კავშირი გაწყდა. უკავშირდება ხელახლა...\"\nHotkeys.Pause=\"პაუზა\"\nHotkeys.Left=\"მარცხენა ისარი\"\nHotkeys.Right=\"მარჯვენა ისარი\"\nHotkeys.Up=\"ზედა ისარი\"\nHotkeys.Down=\"ქვედა ისარი\"\nHotkeys.Menu=\"მენიუ\"\nHotkeys.NumpadMultiply=\"გამრავლება ციფრების დაფაზე\"\nHotkeys.NumpadDivide=\"გაყოფა ციფრების დაფაზე\"\nHotkeys.NumpadAdd=\"მიმატება ციფრების დაფაზე\"\nHotkeys.NumpadSubtract=\"გამოკლება ციფრების დაფაზე\"\nHotkeys.NumpadDecimal=\"ათწილადის ნიშანი ციფრების დაფაზე\"\nHotkeys.AppleKeypadNum=\"%1 (კლავიატურაზე)\"\nHotkeys.AppleKeypadMultiply=\"* (კლავიატურაზე)\"\nHotkeys.AppleKeypadDivide=\"/ (კლავიატურაზე)\"\nHotkeys.AppleKeypadAdd=\"+ (კლავიატურაზე)\"\nHotkeys.AppleKeypadSubtract=\"- (კლავიატურაზე)\"\nHotkeys.AppleKeypadDecimal=\". (კლავიატურაზე)\"\nHotkeys.AppleKeypadEqual=\"= (კლავიატურაზე)\"\nHotkeys.MouseButton=\"თაგვი %1\"\nMute=\"დადუმება\"\nUnmute=\"ხმის ჩართვა\"\nPush-to-mute=\"დაჭერისას დადუმება\"\nPush-to-talk=\"დაჭერისას საუბარი\"\nSceneItemShow=\"გამოჩნდეს „%1“\"\nSceneItemHide=\"დაიმალოს „%1“\"\nOutputWarnings.NoTracksSelected=\"უნდა მიუთითოთ ერთი ხმოვანი ჩანაწერი მაინც\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"გამოტანის პარამეტრების შეცდომა\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"თითოეული გამოტანისთვის ერთი ხმოვანი ჩანაწერი მაინც უნდა იყოს მითითებული.\"\nOutputWarnings.MP4Recording=\"გაფრთხილება: MP4/MOV სახით შენახული ჩანაწერები ვეღარ აღდგება მუშაობის შეწყვეტისას (მაგ. ლურჯი ეკრანის, ძაბვის ვარდნის გამო და ა.შ.). თუ გსურთ რამდენიმე ხმოვანი ფაილის ჩაწერა, სასურველია გამოიყენოთ MKV და დასრულების შემდეგ გადაფუთოთ MP4/MOV-ფაილად. (ფაილი → ჩანაწერების გადაფუთვა)\"\nOutputWarnings.CannotPause=\"შენიშვნა: ჩაწერა ვერ შეჩერდება, თუ ჩაწერისთვის მითითებულია „(გაშვებული ნაკადის მშიფრავის გამოყენება)“\"\nOutputWarnings.CodecIncompatible=\"ხმისა და ვიდეოს დამშიფრავების შერჩევა გაუქმებულია შეუთავსებლობის გამო. გთხოვთ, მიუთითოთ თავსებადი დამშიფრავი სიიდან.\"\nCodecCompat.Incompatible=\"(შეუთავსებელი %1)\"\nCodecCompat.CodecPlaceholder=\"დამშიფრავის არჩევა...\"\nCodecCompat.ContainerPlaceholder=\"სახეობის არჩევა...\"\nCodecCompat.CodecMissingOnExit.Title=\"დამშიფრავი არაა არჩეული\"\nCodecCompat.CodecMissingOnExit.Text=\"რომელიღაც ვიდეოს ან ხმის დამშიფრავები არ აყენია. გადაამოწმეთ, რომ დამშიფრავები მითითებულია ორივესთვის, ჩაწერისა და ეთერში გაშვებისთვის.\"\nCodecCompat.ContainerMissingOnExit.Title=\"სახეობა არაა შერჩეული\"\nCodecCompat.ContainerMissingOnExit.Text=\"ჩაწერის სახეობა არაა მითითებული. აირჩიეთ ჩაწერის ისეთი ფორმატი, რომელიც ნაკადის შერჩეულ დამშიფრავთან თავსებადია.\"\nFinalScene.Title=\"სცენის წაშლა\"\nFinalScene.Text=\"აუცილებელია, სულ მცირე ერთი სცენა.\"\nNoSources.Title=\"წყაროები არაა\"\nNoSources.Text=\"როგორც ჩანს, თქვენ ჯერ არ დაგიმატებიათ ვიდეოს არცერთი წყარო, შედეგად მიიღებთ ცარიელ ეკრანს. ნამდვილად გსურთ, განაგრძოთ?\"\nNoSources.Text.AddSource=\"წყაროს დამატება შეგიძლიათ ნებისმიერ დროს + ნიშანზე დაწკაპებით, მთავარი ფანჯრის წყაროების არეში.\"\nNoSources.Label=\"წყაროები არ მიგითითებიათ.\\nდაწკაპეთ + ღილაკზე ქვემოთ\\nან დაამატეთ მარჯვენა წკაპით.\"\nChangeBG=\"ფერის დაყენება\"\nCustomColor=\"ფერის მითითება\"\nBrowserSource.EnableHardwareAcceleration=\"ბრაუზერის აპარატურული აჩქარების ჩართვა\"\nAbout=\"შესახებ\"\nAbout.Info=\"OBS Studio არის უფასო, ღია წყაროს მქონე პროგრამა ვიდეოების ჩასაწერად და პირდაპირი ეთერით გადასაცემად.\"\nAbout.Donate=\"გაიღეთ შემოწირულობა\"\nAbout.GetInvolved=\"ჩაერთეთ შემუშავებაში\"\nAbout.Authors=\"შემქმნელები\"\nAbout.License=\"ლიცენზია\"\nAbout.Error=\"შეცდომა! ფაილის წაკითხვა ვერ მოხერხდა.\\n\\nიხილეთ: %1\"\nAbout.Contribute=\"მხარი დაუჭირეთ OBS-პროექტს\"\nAddUrl.Title=\"წყაროს დამატება ბმულიდან\"\nAddUrl.Text=\"ბმული გადმოტანილია OBS-ში. შედეგად, ბმული წყაროს სახით დაემატება. გსურთ, განაგრძოთ?\"\nAddUrl.Text.Url=\"ბმული: %1\"\nResizeOutputSizeOfSource=\"გამოტანის ზომების შეცვლა (წყაროს ზომები)\"\nResizeOutputSizeOfSource.Text=\"ეკრანის ძირითადი და გამოტანილი ვიდეოს გაფართოება ჩანაცვლდება მიმდინარე წყაროს ზომებით.\"\nResizeOutputSizeOfSource.Continue=\"გსურთ, განაგრძოთ?\"\nPreviewTransition=\"გადასვლის შეთვალიერება\"\nImporter=\"სცენის კრებულის შემომტანი\"\nImporter.SelectCollection=\"აირჩიეთ სცენის კრებული\"\nImporter.Collection=\"სცენის კრებული\"\nImporter.HelpText=\"დაამატეთ ფაილები ამ ფანჯარაში, სცენის კრებულების შემოსატანად OBS-დან ან სხვა მხარდაჭერილი პროგრამებიდან.\"\nImporter.Path=\"კრებულის მდებარეობა\"\nImporter.Program=\"აღმოჩენილი პროგრამა\"\nImporter.AutomaticCollectionPrompt=\"სცენის კრებულების ავტომატური მოძიება\"\nImporter.AutomaticCollectionText=\"OBS-ს შეუძლია თავადვე მონახოს შემოსატანი სცენის კრებულები, მხარდაჭერილი გარეშე პროგრამებიდან. გსურთ, OBS-მა ავტომატურად მოიძიოს კრებულები?\\n\\nარჩევანის მომავალში შესაცვლელად, იხილეთ პარამეტრები > მთავარი > შემომტანები.\"\nImporter.SelectFile=\"მოძიება...\"\nRestart=\"ხელახლა გაშვება\"\nNeedsRestart=\"OBS Studio საჭიროებს ხელახლა გაშვებას. გსურთ, ხელახლა გაშვება ახლავე?\"\nLoadProfileNeedsRestart=\"პროფილი შეიცავს პარამეტრებს, რომლებიც საჭიროებს, ახლიდან გაეშვას OBS:\\n%1\\n\\nგსურთ, ხელახლა ჩაირთოს OBS ამ პარამეტრების ასამოქმედებლად?\"\nContextBar.NoSelectedSource=\"წყარო არაა შერჩეული\"\nContextBar.MediaControls.PlayMedia=\"მასალის გაშვება\"\nContextBar.MediaControls.PauseMedia=\"გაშვებულის შეჩერება\"\nContextBar.MediaControls.StopMedia=\"გაშვებულის შეწყვეტა\"\nContextBar.MediaControls.RestartMedia=\"მასალის ხელახლა გაშვება\"\nContextBar.MediaControls.PlaylistNext=\"მომდევნო, სიაში\"\nContextBar.MediaControls.PlaylistPrevious=\"წინა, სიაში\"\nContextBar.MediaControls.BlindSeek=\"მასალის ძიების ხელსაწყო\"\nYouTube.Auth.Ok=\"შესვლა წარმატებით დამოწმდა.\\nშეგიძლიათ დახუროთ ეს გვერდი.\"\nYouTube.Auth.NoCode=\"შესვლა ვერ დამოწმდა.\"\nYouTube.Auth.NoChannels=\"არხ(ებ)ი ვერ მოიძებნა მითითებულ ანგარიშზე\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube-მომხმარებლის დამოწმება\"\nYouTube.Auth.WaitingAuth.Text=\"გთხოვთ, დამოწმებისთვის გამოიყენოთ ცალკე ბრაუზერი.<br>თუ ბრაუზერი არ იხსნება, მიჰყევით ამ ბმულს, დამოწმების დასასრულებლად:<br>%1\"\nYouTube.AuthError.Text=\"არხის მონაცემები ვერ ჩაიტვირთა: %1.\"\nYouTube.Actions.WindowTitle=\"YouTube-გადაცემის გამართვა - არხი: %1\"\nYouTube.Actions.CreateNewEvent=\"ახალი გადაცემის შექმნა\"\nYouTube.Actions.ChooseEvent=\"არსებული გადაცემის არჩევა\"\nYouTube.Actions.Title=\"სათაური*\"\nYouTube.Actions.MyBroadcast=\"ჩემი გადაცემა\"\nYouTube.Actions.Description=\"აღწერილობა\"\nYouTube.Actions.Privacy=\"ხილვადობა*\"\nYouTube.Actions.Privacy.Private=\"პირადი\"\nYouTube.Actions.Privacy.Public=\"საჯარო\"\nYouTube.Actions.Privacy.Unlisted=\"ბმულით\"\nYouTube.Actions.Category=\"კატეგორია\"\nYouTube.Actions.Thumbnail=\"გამოსახულება\"\nYouTube.Actions.Thumbnail.SelectFile=\"ფაილის არჩევა...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"ფაილი არაა არჩეული\"\nYouTube.Actions.Thumbnail.ClearFile=\"გასუფთავება\"\nYouTube.Actions.MadeForKids=\"ეს ვიდეო ბავშვებისთვისაა?\"\nYouTube.Actions.MadeForKids.Yes=\"დიახ, ბავშვებისთვისაა შექმნილი\"\nYouTube.Actions.MadeForKids.No=\"არა, ბავშვებისთვის არაა შექმნილი\"\nYouTube.Actions.AdditionalSettings=\"დამატებითი პარამეტრები\"\nYouTube.Actions.Latency=\"დაყოვნება\"\nYouTube.Actions.Latency.Normal=\"ჩვეულებრივი\"\nYouTube.Actions.Latency.Low=\"დაბალი\"\nYouTube.Actions.Latency.UltraLow=\"უდაბლესი\"\nYouTube.Actions.EnableAutoStart=\"თვითდაწყების ჩართვა\"\nYouTube.Actions.EnableAutoStop=\"თვითდასრულების ჩართვა\"\nYouTube.Actions.AutoStartStop.TT=\"მიუთითებს, დაგეგმილი გადაცემა თავადვე დაიწყოს თუ არა\"\nYouTube.Actions.EnableDVR=\"ჩაირთოს DVR\"\nYouTube.Actions.360Video=\"360 ვიდეო\"\nYouTube.Actions.ScheduleForLater=\"სამომავლოდ დაგეგმვა\"\nYouTube.Actions.RememberSettings=\"ამ პარამეტრების დამახსოვრება\"\nYouTube.Actions.Create_Ready=\"გადაცემის შექმნა\"\nYouTube.Actions.Create_GoLive=\"გადაცემის შექმნა და ეთერში გაშვება\"\nYouTube.Actions.Choose_Ready=\"გადაცემის არჩევა\"\nYouTube.Actions.Choose_GoLive=\"გადაცემის არჩევა და ეთერში გაშვება\"\nYouTube.Actions.Create_Schedule=\"გადაცემის დაგეგმვა\"\nYouTube.Actions.Create_Schedule_Ready=\"დაგეგმვა და გადაცემის არჩევა\"\nYouTube.Actions.Dashboard=\"გაიხსნას YouTube Studio\"\nYouTube.Actions.Error.Title=\"შეცდომა პირდაპირის ეთერის წამოწყებისას\"\nYouTube.Actions.Error.Text=\"YouTube-თან წვდომის შეცდომა „%1“.<br/>შეცდომის ვრცელი აღწერისთვის, იხილეთ <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTube-თან წვდომის შეცდომა. გთხოვთ, შეამოწმოთ ქსელთან ან YouTube-სერვერთან კავშირი.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"შეცდომა, გადაცემის წამოწყებისას „%1“.<br/>შეცდომის ვრცელი აღწერისთვის, იხილეთ <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"ნაკადი ვერ გაეშვა. გთხოვთ, ხელახლა მიუერთდეთ ანგარიშს.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube-მაკავშირებლის შეცდომა. ვრცლად, იხილეთ აღრიცხვების ჩანაწერში.\"\nYouTube.Actions.Error.BroadcastNotFound=\"მითითებული გადაცემა ვერ მოიძებნა.\"\nYouTube.Actions.Error.FileMissing=\"შერჩეული ფაილი არ არსებობს.\"\nYouTube.Actions.Error.FileOpeningFailed=\"შერჩეული ფაილი ვერ გაიხსნა\"\nYouTube.Actions.Error.FileTooLarge=\"შერჩეული ფაილი მეტად დიდია (ზღვარი: 2 მიბ).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"გადაცემის გადასვლა ვერ მოხერხდა: %1<br/><br/>თუ შეცდომა არ გამოსწორდება <a href='https://studio.youtube.com/video/%2/livestreaming'>გახსენით გადაცემა YouTube Studio-ში</a> და ხელით სცადეთ.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"გადაცემის გადასვლა საცდელ საფეხურზეა და გარკვეულ ხანს გასტანს. გთხოვთ, კვლავ სცადოთ 10-30 წამში.\"\nYouTube.Actions.EventsLoading=\"იტვირთება ღონისძიებების სია...\"\nYouTube.Actions.EventCreated.Title=\"ღონისძიება შექმნილია\"\nYouTube.Actions.EventCreated.Text=\"ღონისძიება წარმატებით შეიქმნა.\"\nYouTube.Actions.Stream=\"ნაკადი\"\nYouTube.Actions.Stream.ScheduledFor=\"დაგეგმილი %1\"\nYouTube.Actions.Stream.Resume=\"გაწყვეტილი ნაკადის გაგრძელება\"\nYouTube.Actions.Stream.YTStudio=\"თავად უზრუნველყოფს YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"იქმნება ახალი პირდაპირი ეთერი, გთხოვთ მოითმინოთ...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"საჭიროებს ხელით წამოწყებას\"\nYouTube.Actions.AutoStartStreamingWarning=\"თვითდაწყება გათიშულია ამ ღონისძიებისთვის, დაწკაპეთ „ეთერში გასვლა“ გადაცემის წამოსაწყებად.\"\nYouTube.Actions.AutoStopStreamingWarning=\"ხელახლა მიერთებას ვერ შეძლებთ.<br>თქვენი ნაკადი შეწყდება და ეთერიდან გამოხვალთ.\"\nYouTube.Errors.liveStreamingNotEnabled=\"პირდაპირ ეთერში გასვლის შესაძლებლობა არაა ამოქმედებული მითითებულ YouTube-არხზე.<br/><br/>ვრცლად, იხილეთ ბმულზე <a href='https://www.youtube.com/features'>youtube.com/features</a>.\"\nYouTube.Errors.livePermissionBlocked=\"პირდაპირი ეთერი მიუწვდომელია მითითებულ YouTube-არხზე.<br/>გაითვალისწინეთ, რომ ამ შესაძლებლობის ამოქმედებამ 24 საათამდე შეიძლება გასტანოს არხის პარამეტრებში ჩართვის შემდგომ.<br/><br/>ვრცლად, იხილეთ <a href='https://www.youtube.com/features'>youtube.com/features</a>.\"\nYouTube.Errors.errorExecutingTransition=\"გადასვლა ვერ მოხერხდა შიდა შეცდომის გამო. გთხოვთ, კვლავ სცადოთ რამდენიმე წამში.\"\nYouTube.Errors.errorStreamInactive=\"YouTube ვერ იღებს მონაცემებს თქვენი ეთერიდან. გთხოვთ, გადაამოწმოთ ყველაფერი გამართულია თუ არა და კვლავ სცადოთ.\"\nYouTube.Errors.invalidTransition=\"გადასვლის მცდელობა უშედეგო იყო. შესაძლოა, იმიტომ რომ წინა გადასვლა ჯერ არ დასრულებულა. გთხოვთ, მოითმინეთ რამდენიმე წამი და კვლავ სცადეთ.\"\nYouTube.DocksRemoval.Title=\"YouTube-ბრაუზერის მოძველებული ნაწილების მოცილება\"\nYouTube.DocksRemoval.Text=\"ბრაუზერის ეს ნაწილები მოცილდება, როგორც მოძველებული:\\n\\n%1\\nსანაცვლოდ იხილეთ „იერსახის ნაწილები/YouTube Live Control Room“.\"\nConfigDownload.WarningMessageTitle=\"გაფრთხილება\"\nFailedToStartStream.MissingConfigURL=\"გამართვის ბმული არაა ხელმისაწვდომი მოცემული მომსახურებისთვის\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"მორგებული RTMP-ბმული არ მითითებულა\"\nFailedToStartStream.InvalidCustomConfig=\"უმართებულო გამართვა\"\nFailedToStartStream.MissingCanvas=\"დამატებით გამართული ფონი ამოკლებულია\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"ვერ შეიქმნა მრავალჩანაწერიანი ვიდეო მომსახურება\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"ვერ შეიქმნა მრავალჩანაწერიანი ვიდეო RTMP-გამოტანით\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC მიუწვდომელია.\\n\\nვერ მოიძებნა დაშიფვრის სახეობა „%1“\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"ვერ შეიქმნა ვიდეოს დამშიფრავი „%1“ (სახეობა: „%2“)\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"ვერ მოხერხდა OBS-ვიდეოს მონაცემების მიღება, როცა იქმნებოდა დამშიფრავი „%1“ (სახეობა: „%2“)\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"ვერ შეიქმნა ხმის დამშიფრავი\"\nFailedToStartStream.NoRTMPURLInConfig=\"გამართვა არ შეიცავს ნაკადის დანიშნულების წერტილის RTMP(ების)-მისამართს\"\nFailedToStartStream.FallbackToDefault=\"ნაკადის წამოსაწყებად ვერ გამოიყენა %1; გსურთ გაიმეოროს ცალმაგი დაშიფვრის პარამეტრებით?\"\nFailedToStartStream.ConfigRequestFailed=\"გამართვის მისაღებად ვერ გამოიყენა %1<br><br>HTTP-შეცდომა: %2\"\nFailedToStartStream.WarningUnknownStatus=\"მიღებულია მდგომარეობის უცნობი მნიშვნელობა „%1“\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nგსურთ გაარძელოთ სტრიმი %1-ის გაერეშე?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nგსურთ გააგრძელოთ სტრიმი?\"\nFailedToStartStream.MissingEncoderConfigs=\"პირდაპირი ეთერის გამართვა არ შეიცავდა დამშიფრავის მახასიათებლებს\"\nFailedToStartStream.InvalidEncoderConfig=\"პირდაპირ ეთერში გასასვლელი გამართვა შეიცავს უმართებულოდ გამართულ დამშიფრავს\"\nFailedToStartStream.StatusMissingHTML=\"პირდაპირი ეთერის მოთხოვნამ მიიღო გაურკვეველი შეცდომა\"\nFailedToStartStream.NoConfigSupplied=\"კონფიგურაცია არ არის\"\nMultitrackVideo.Info=\"%1 თავისით შეარჩევს საუკეთესო პარამეტრებს დასაშიფრად და გაგზავნის რამდენიმე ხარისხის ვიდეოს. ამ პარამეტრის მითითებით გაგზავნისას %2 აგრეთვე მიიღებს თქვენი კომპიუტერისა და პროგრამის გამართვის მონაცემებსაც.\"\nMultitrackVideo.IncompatibleSettings.Title=\"შეუთავსებელი პარამტერები\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 შეუთავსებელია მახასიათებლებთან:\\n\\n%2\\nნაკადის გასაშვებად რომ გამოიყენოს %1, უნდა გაითიშოს შეუთავსებელი პარამეტრები:\\n\\n%3\\nდა ახლიდან გაეშვას ნაკადი.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"გათიშვა ამ ნაკადზე და ეთერში გაშვება\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"განახლება და ეთერში გაშვება\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 ჯერჯერობით შეუთავსებელია [ხმა → მთავარი → არხები] მახასიათებელთან „%2“, %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[ხმა → მთავარი → არხები] უნდა იყოს „%1“\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 საჭიროებს რამდენიმე სხვადასხვა მითითებას [ხმა → მთავარი → არხები]\"\nBasic.OpenPluginManager=\"დანამატების სამართავი\"\nPluginManager=\"დანამატების სამართავი\"\nPluginManager.Restart=\"OBS Studio კვლავ უნდა გაეშვას დანამატის ცვლილების ასახვისთვის\"\nPluginManager.MissingPlugin=\"[ᲓᲐᲜᲐᲛᲐᲢᲘ ᲕᲔᲠ ᲛᲝᲘᲫᲔᲑᲜᲐ]\"\nPluginManager.Section.Discover=\"მოძიება\"\nPluginManager.Section.Manage=\"ჩადგმული\"\nPluginManager.Section.Updates=\"განახლებები\"\nPluginManager.Section.Manage.Title=\"მოქმედი დანამატების მართვა\"\n"
  },
  {
    "path": "frontend/data/locale/kaa.ini",
    "content": "Language=\"Қарақалпақша\"\nApply=\"Қолланыў\"\nCancel=\"Бийкарлаў\"\nClose=\"Жабыў\"\nSave=\"Сақлаў\"\nDiscard=\"Biykaw etiw\"\nDisable=\"Өшириў\"\nYes=\"Аўа\"\nNo=\"Яқ\"\nAdd=\"Қосыў\"\nRemove=\"Өшириў\"\nRename=\"Qayta ataw\"\nInteract=\"Óz-ara háreket etiw\"\nFilters=\"Фильтр\"\nProperties=\"Qásiyetler\"\nMoveUp=\"Joqarıǵa kóshiriw\"\nMoveDown=\"Tómenge kóshiriw\"\nSettings=\"Сазлаўлар\"\nDisplay=\"Экран\"\nName=\"Атамасы\"\nExit=\"Шығыў\"\nMixer=\"Audio aralastırģısh\"\nBrowse=\"Tańlaw\"\nMono=\"Моно\"\nStereo=\"Стерео\"\nDroppedFrames=\"Túsirip qaldırılģan kadrlar %1 (%2%)\"\nClear=\"Тазалаў\"\nRevert=\"Qaytarıw\"\nShow=\"Көрсетиў\"\nHide=\"Жасырыў\"\nUnhideAll=\"Barlıǵın kórsetiw\"\nUntitled=\"Атамасыз\"\nNew=\"Жаратыў\"\nDuplicate=\"Нусқалаў\"\nEnable=\"Qosıw\"\nDisableOSXVSync=\"MacOS vertical sáykeslestiriwin óshiriw\"\nResetOSXVSyncOnExit=\"Shıǵıwda macOS V-Sync tazalaw\"\nHighResourceUsage=\"Kodlaw hádden tısqarı júklengen! Video sazlamaların páseytiń yáki tez kodlawshı sazlamadan paydalanıń.\"\nTransition=\"Ótiw\"\nQuickTransitions=\"Tez ótiwler\"\nFadeToBlack=\"Qara reńge sóniw\"\nLeft=\"Shepten\"\nRight=\"Ońnan\"\nTop=\"Joqarıdan\"\nBottom=\"Tómennen\"\nReset=\"Qayta ornatıw\"\nHours=\"Saat\"\nMinutes=\"Minut\"\nSeconds=\"Sekund\"\nDeprecated=\"Eskirgen\"\nReplayBuffer=\"Qayta qoyıw bufferi\"\nImport=\"Импорт\"\nExport=\"Экспорт\"\nCopy=\"Көширип алыў\"\nPaste=\"Jaylastırıw\"\nPasteReference=\"Jaylastırıw (Silteme)\"\nPasteDuplicate=\"Jaylastırıw (Kóbeytiw)\"\nRemuxRecordings=\"Jazıwlardı Remux qılıw\"\nNext=\"Кейинги\"\nBack=\"Артқа\"\nDefaults=\"Әдепки\"\nRestoreDefaults=\"Әдепки\"\nHideMixer=\"Aralastırģıshta jasırıw\"\nTransitionOverride=\"Ótiwdi ózgertiw\"\nShowTransition=\"Ótiwdi kórsetiw\"\nHideTransition=\"Ótiw effektin kórsetpew\"\nNone=\"Жоқ\"\nStudioMode.Preview=\"Aldınnan kóriw\"\nStudioMode.Program=\"Бағдарлама\"\nStudioMode.PreviewSceneName=\"Aldınnan kóriw:%1\"\nStudioMode.ProgramSceneName=\"Baǵdarlama: %1\"\nShowInMultiview=\"Multi-kóriniste kórsetiw\"\nVerticalLayout=\"Tik ornalastırıw (vertikal)\"\nGroup=\"Топар\"\nDoNotShowAgain=\"Басқа көрсетпеў\"\nDefault=\"(Әдепки)\"\nCalculating=\"Esaplamaqta...\"\nFullscreen=\"Толық экран режимы\"\nWindowed=\"Айна режимы\"\nRefreshBrowser=\"Жаңалаў\"\nAspectRatio=\"Tárepler sáykesligi <b>%1:%2</b>\"\nLockVolume=\"Dawıstı quliplaw\"\nLogViewer=\"Jurnaldı qaraw quralı\"\nShowOnStartup=\"Iske túsirgende kórsetiw\"\nOpenFile=\"Файлды ашыў\"\nAddSource=\"Дерек қосыў\"\nRemoveScene=\"Tańlanǵan saxnanı óshiriw\"\nRemoveSource=\"Tańlanǵan dereklerdi óshiriw\"\nMoveSceneUp=\"Saxnanı joqarıǵa kóshiriw\"\nMoveSceneDown=\"Saxnanı tómenge kóshiriw\"\nMoveSourceUp=\"Derek(ler)di joqarıǵa kóshiriw\"\nMoveSourceDown=\"Derek(ler)di tómenge kóshiriw\"\nSourceProperties=\"Derektıń qásiyetlerin ashıw\"\nSourceFilters=\"Derek filterlerin ashıw\"\nMixerToolbarMenu=\"Audio mikser bólimi\"\nSceneFilters=\"Saxna filtrlerin ashıw\"\nList=\"Дизим\"\nPluginsFailedToLoad.Title=\"Плагинди жүклеўдеги қәтелик\"\nPluginsFailedToLoad.Text=\"Usı plaginlerdi júklep bolmadı:\\n\\n%1\\n Iltmas, plaginlerdi jańalań yáki óshiriń.\"\nAlreadyRunning.Title=\"OBS әллеқашан иске түсирилген\"\nAlreadyRunning.LaunchAnyway=\"Hárqanday jaǵdayda da iske qosıw\"\nSafeMode.RestartNormal=\"OBStı ádettegi rejimde iske túsiriwdi qáleysiz be?\"\nChromeOS.Title=\"Qollap-quwatlanbaytuǵın platforma\"\nChromeOS.Text=\"OBS baǵdarlaması ChromeOS ishinde iske qosılǵan. Bul platforma qollap-quwatlanbaydı.\"\nWine.Title=\"Wine anıqlandı\"\nDockCloseWarning.Title=\"Bekitilgen aynanı jabıw\"\nDockCloseWarning.Text=\"Siz házir ǵana bekitilgen aynanı jawdıńız. Eger de onı qayta ashıwdı qáleseńiz, bólimler qatarındaǵı Dok-panelinen paydalanıń.\"\nExtraBrowsers=\"Paydalanıwshınıń brauzerdegi Dok-paneli\"\nExtraBrowsers.Info=\"Dok panellerge atama hám silteme beriw arqalı qosıń, keyin olardı ashıw ushın «Qollanıw» yamasa «Jabıw» túymesin basıń. Siz dok panellerdi qálegen waqtıńızda qosıwıńız yamasa óshiriwińiz múmkin.\"\nExtraBrowsers.DockName=\"Panel ataması\"\nAuth.Authing.Title=\"Kiriw ámelge aspaqta...\"\nAuth.Authing.Text=\"%1 ge kiriw orınlanbaqta. Ótinish, kútiń...\"\nAuth.AuthFailure.Title=\"Kiriw qátesi\"\nAuth.AuthFailure.Text=\"%1:\\n\\n%2: %3 ge kiriw qáteligi\"\nAuth.InvalidScope.Title=\"Kiriw talap etiledi\"\nAuth.InvalidScope.Text=\"%1 ushın kiriwge talaplar ózgertilgen. Birqansha funkciyalar joq bolıwı múmkin.\"\nAuth.LoadingChannel.Title=\"Kanal haqqında maǵlıwmat júklenbekte...\"\nAuth.LoadingChannel.Text=\"%1 ushın kanal haqqında maǵlıwmat júklenbekte, ótinish, kútiń...\"\nAuth.LoadingChannel.Error=\"Kanal haqqında maǵlıwmattı júklew ámelge aspadı.\"\nAuth.ChannelFailure.Title=\"Kanaldı júklew ámelge aspadı\"\nAuth.ChannelFailure.Text=\"%1\\n\\n%2: %3 ushın kanal haqqında maǵlıwmattı júklew ámelge aspadı\"\nAuth.Chat=\"Чат\"\nAuth.StreamInfo=\"Трансляция мағлыўматы\"\nTwitchAuth.Stats=\"Twitch статистикасы\"\nTwitchAuth.TwoFactorFail.Title=\"Esittiriw giltin soraw ámelge aspadı\"\nTwitchAuth.TwoFactorFail.Text=\"Twitch akkauntıńızǵa qosılıw múmkin emes. <a href='https://www.twitch.tv/settings/security'>Twitch qáwipsizlik sazlawları</a>nda Eki faktorlı autentifikaciya qosılǵanına isenim kámil etiń; tikkeley efir júrgiziw ushın bul talap etiledi.\"\nRestreamAuth.Channels=\"Restream каналлары\"\nCopy.Filters=\"Фильтрлерди көширип алыў\"\nPaste.Filters=\"Фильтрлерди қойыў\"\nBrowserPanelInit.Title=\"Браузер иске түспекте...\"\nBrowserPanelInit.Text=\"Браузер иске түспекте, өтиниш, күтиң...\"\nBandwidthTest.Region=\"Аймақ\"\nBandwidthTest.Region.US=\"АҚШ\"\nBandwidthTest.Region.EU=\"Европа\"\nBandwidthTest.Region.Asia=\"Азия\"\nBandwidthTest.Region.Other=\"Басқа\"\nBasic.AutoConfig=\"Автоматикалық сазлаў устасы\"\nBasic.AutoConfig.ApplySettings=\"Сазлаўларды қолланыў\"\nBasic.AutoConfig.StartPage=\"Qollanıw haqqındaǵı maǵlıwmat\"\nBasic.AutoConfig.StartPage.SubTitle=\"Baǵdarlamanı ne ushın qollanǵıńız keletuǵının kórsetiń\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Tek esittiriw ushın tezlestiriw, jazıp alıw ekinshi gezekte\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Tek jazıp alıw ushın tezlestiriw, men esittiriw júrgizbeymen\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Men tek virtual kameranı qollanaman\"\nBasic.AutoConfig.VideoPage=\"Видео сазлаўлары\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Qollanıwdı qáleytuǵın video sazlawların kórsetiń\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Ámeldegin qollanıw (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Ekran %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Ámeldegin qollanıw (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 yamasa 30, biraq múmkinligi bolǵanınsha 60 tı abzal kóriw\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 yamasa 30, biraq joqarı sapanı abzal kóriw\"\nBasic.AutoConfig.StreamPage=\"Трансляция мағлыўматы\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Ótinish, esittiriw haqqındaǵı maǵlıwmattı kirgizıń\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Akkaunttı jalǵaw (usınıs etiledi)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Akkaunttan shıǵiw\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Akkaunttan shıǵiw?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Bul ózgerisler dárriw kúshke kiredi. Shıǵıwǵa isenimińiz kámil me?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Esittiriw giltin alıw\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Толығырақ\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Esittiriw giltin qollanıw\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Esittiriw giltin qollanıw (sheber qollanıwshılar ushın)\"\nBasic.AutoConfig.StreamPage.Service=\"Хызмет\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Barlıǵın kórsetiw...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Арнаўлы...\"\nBasic.AutoConfig.StreamPage.Server=\"Сервер\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Esittiriw gilti\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kodlawshı gilti\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Paydalanıwshı tokeni\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Жалғанған аккаунт\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Apparatlıq kodlawshını abzal kóriw\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Трансляция ескертиўи\"\nBasic.AutoConfig.TestPage=\"Соңғы нәтийжелер\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Baǵdarlama házirgi waqıtta bir qatar ideal sazlawlardı tekserip atır\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Тестлеў жуўмақланды\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Ótkeriw qábiliyeti tekserilip atır, bul bir neshe minut waqıttı alıwı múmkin ...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Bul xızmet protokolı ushın shıǵıw maǵlıwmatları tabılmadı\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Jalǵanıp atır: %1ǵa\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Heshbir serverge jalǵanıwdıń imkanı bolmadı, internet penen baylanıstı tekserip, qayta urınıp kóriń.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Ótkeriw qábiliyeti tekserilip atır: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Esittiriw kodlawı tekserilmekte, buǵan 1 minut waqıt ketiwi múmkin...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Jazıw kodlawı tekserilmekte, buǵan bir minut waqıt ketiwi múmkin...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Kodlawdı baslawda qátelik\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Kadrlar tezligi tekserilmekte %1x%2 %3\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Esittiriw kodlawı\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Jazıw kodlawı\"\nBasic.AutoConfig.TestPage.Result.Header=\"Baǵdarlama usı sazlawlar siz ushın ideal ekenligin anıqladı:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Usı sazlawlardı qollanıw ushın «Sazlawlar»dan «Qabıllaw» túymesin basıń. Qayta sazlaw ushın «Arqaǵa» túymesin basıń. Jeke ózińiz ushın sazlawlardı engiziw ushın «Biykar etiw» túymesin basıp «Sazlawlar»dı ashıń.\"\nBasic.AutoConfig.Info=\"Avtomat tárizde sazlaw ustası siziń kompyuterińiz qásiyetleri hám internet tezligińiz tiykarında optimal sazlawlardı anıqlaydı.\"\nBasic.AutoConfig.RunAnytime=\"Onı «Xızmet» menyui arqalı qayta iske túsiriw múmkin.\"\nBasic.Stats=\"Статистика\"\nBasic.Stats.CPUUsage=\"OPdan paydalanıw\"\nBasic.Stats.HDDSpaceAvailable=\"Disktegi bos orın\"\nBasic.Stats.MemoryUsage=\"Yadtan paydalanıw\"\nBasic.Stats.AverageTimeToRender=\"Kadrdı kórsetiw ushın ortasha waqıt\"\nBasic.Stats.Output.Stream=\"Трансляция\"\nBasic.Stats.Output.Recording=\"Жазып алыў\"\nBasic.Stats.Status=\"Жағдай\"\nBasic.Stats.Status.Recording=\"Jazılmaqta\"\nBasic.Stats.Status.Live=\"ЭФИР\"\nBasic.Stats.Status.Reconnecting=\"Қайта жалғанбақта\"\nBasic.Stats.Status.Inactive=\"Aktiv emes\"\nBasic.Stats.Status.Active=\"Актив\"\nBasic.Stats.DroppedFrames=\"Ótkerip jiberilgen kadrlar (tarmaq)\"\nBasic.Stats.MegabytesSent=\"Jámi jiberildi\"\nBasic.Stats.Bitrate=\"Bit tezligi\"\nBasic.Stats.DiskFullIn=\"Disk tolǵanǵa deyin waqıt (shama menen)\"\nBasic.Stats.ResetStats=\"Statistikanı qayta ornatıw\"\nResetUIWarning.Title=\"Paydalanıwshı interfeysın qayta ornatıwǵa isenimińiz kámil me?\"\nUpdater.Title=\"Jańa jańalanıw bar\"\nUpdater.Text=\"Jańa jańalanıw bar:\"\nUpdater.UpdateNow=\"Házir jańalaw\"\nUpdater.RemindMeLater=\"Keyinirek esletiw\"\nUpdater.Skip=\"Versiyanı ótkizip jiberiw\"\nUpdater.NoUpdatesAvailable.Title=\"Jańalanıw joq\"\nUpdater.NoUpdatesAvailable.Text=\"Jańalanıwlar tabılmadı\"\nUpdater.BranchNotFound.Title=\"Jańalaw kanalı alıp taslandı\"\nUpdater.RepairButUpdatesAvailable.Title=\"Pútinlikti tekseriw múmkin emes\"\nUpdater.RepairButUpdatesAvailable.Text=\"Fayllardıń pútinligin tek ǵana sońǵı versiyada tekseriw múmkin. OBS ornatıwın tekseriw hám jańalaw ushın: Járdem → Jańalanıwlardı tekseriw.\"\nUpdater.RepairConfirm.Title=\"Pútinlik tekseriwin tastıyıqlań\"\nUpdater.RepairConfirm.Text=\"Pútinlikti tekseriw OBS ornatıwın tekseredi hám buzılǵan/ózgertilgen fayllardı qayta júkleydi. Bul birqansha waqıt alıwı múmkin.\\n\\nDawam etiwdi qáleysiz be?\"\nUpdater.FailedToLaunch=\"Jańalanıwlardı tekseriw ámelge aspadı\"\nQuickTransitions.DuplicateScene=\"Saxnanı nusqalaw\"\nQuickTransitions.EditProperties=\"Dereklerdi nusqalaw\"\nQuickTransitions.HotkeyName=\"Tez ótiw: %1\"\nBasic.TransitionProperties=\"Ótiw qásiyletleri\"\nBasic.SceneTransitions=\"Saxna ótiwleri\"\nBasic.TransitionDuration=\"Uzaqlıq\"\nBasic.TogglePreviewProgramMode=\"Studio rejimi\"\nBasic.EnablePreviewProgramMode=\"Studio rejimin qosıw\"\nBasic.DisablePreviewProgramMode=\"Studio rejimin óshiriw\"\nUndo.Undo=\"Biykarlaw\"\nUndo.Redo=\"Qaytalaw\"\nUndo.Add=\"Qosıw: %1'\"\nUndo.Delete=\"Óshiriw \\\"%1\\\"\"\nUndo.Rename=\"Qayta ataw: '%1'\"\nUndo.SceneCollection.Switch=\"'%1' almastırıwdı\"\nUndo.Item.Undo=\"Biykarlaw %1\"\nUndo.Item.Redo=\"Qaytalaw %1\"\nUndo.Sources.Multi=\"%1 derekti óshiriw\"\nUndo.Filters=\"'%1' filtrindegi ózgerislerdi\"\nUndo.Filters.Paste.Single=\"'%1' filtrin '%2' qoyıwdı\"\nUndo.Filters.Paste.Multiple=\"Filtrlerdi '%1' den '%2' ǵa kóshirip alıwdı\"\nUndo.Volume.Change=\"'%1' Dawıs bálentliginıń ózgeriwi\"\nUndo.Volume.Mute=\"'%1' dawısın óshiriw\"\nUndo.Volume.Unmute=\"'%1' dawısın qosıw\"\nUndo.Scene.Duplicate=\"'%1' saxnasın nusqalaw\"\nUndo.ReorderSources=\"'%1' dereklerdi qayta tártiplestiriwdi\"\nUndo.PasteSource=\"dereklerdi qoyıwdı '%1'\"\nTransitionNameDlg.Text=\"Ótinish, ótiw atamasın kirgiziń\"\nTransitionNameDlg.Title=\"Ótiw ataması\"\nTitleBar.SafeMode=\"QÁWIPSIZ REJIM\"\nTitleBar.PortableMode=\"Qolaylı rejim\"\nTitleBar.Profile=\"Профиль\"\nTitleBar.Scenes=\"Saxnalar\"\nNameExists.Title=\"Atama álleqashan bar\"\nNameExists.Text=\"Bul atama álleqashan paydalanılmaqta.\"\nNoNameEntered.Title=\"Ótinish, jaramlı atama kirgiziń\"\nNoNameEntered.Text=\"Siz bos atamalardı qollana alamaysız.\"\nConfirmStart.Title=\"Esittiriw baslansın ba?\"\nConfirmStart.Text=\"Esittiriwdi baslawǵa isenimińiz kámil me?\"\nConfirmStop.Title=\"Esittiriw toqtatılsın ba?\"\nConfirmStop.Text=\"Esittiriwdi toqtatıwǵa isenimińiz kámil me?\"\nConfirmStopRecord.Title=\"Jazıp alıwdı toqtatılsın ba?\"\nConfirmStopRecord.Text=\"Jazıp alıwdı toqtatıwǵa isenimińiz kámil me?\"\nConfirmBWTest.Title=\"Ótkeriw qábiletin tekseriwdi baslaw kerek pe?\"\nConfirmRemove.Title=\"Óshiriwdi tastıyıqlaw\"\nConfirmRemove.Text=\"%1 óshiriwge isenimińiz kámil me'?\"\nConfirmRemove.TextMultiple=\"%1 elementti óshiriwge isenimińiz kámil me?\"\nConfirmReset.Title=\"Qásiyetlerdi qayta ornatıw\"\nOutput.StartStreamFailed=\"Esittiriwdi baslaw ámelge aspadı\"\nOutput.StartRecordingFailed=\"Jazıp alıwdı baslaw ámelge aspadı\"\nOutput.StartReplayFailed=\"Qaytalaw buferin iske túsiriw ámelge aspadı\"\nOutput.StartVirtualCamFailed=\"Virtual kameranı iske túsiriw ámelge aspadı\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Pauza waqtında qaytalawlardı saqlaw múmkin emes\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Eskertiw: Jazıwdı pauzaǵa qoyǵan waqtında qaytalawlardı saqlaw múmkin emes.\"\nOutput.ConnectFail.Title=\"Jalǵanıw ámelge aspadı\"\nOutput.ConnectFail.BadPath=\"Nadurıs jol yamasa baylanıs siltemesi. Ótinish, olar jaramlı ekenin tastıyıqlaw ushın sazlawlardı tekseriń.\"\nOutput.ConnectFail.ConnectFailed=\"Serverge jalǵanıw ámelge aspadı\"\nOutput.ConnectFail.InvalidStream=\"Kórsetilgen kanal yamasa esittiriw giltine kiriw múmkinshiligi bolmadı, ótinish, giltti qayta tekserip kóriń. Eger ol durıs bolsa, serverge jalǵanıw mashqalası bolıwı múmkin.\"\nOutput.ConnectFail.HdrDisabled=\"Házirde bul shıǵıw ushın HDR shıǵıw óshirilgen.\"\nOutput.ConnectFail.Error=\"Serverge jalǵanıwda kútilmegen qátelik júz berdi. Kóbirek maǵlıwmat esap faylında.\"\nOutput.ConnectFail.Disconnected=\"Serverden baylanıs úzildi.\"\nOutput.StreamEncodeError.Title=\"Kodlaw qátesi\"\nOutput.StreamEncodeError.Msg=\"Esittiriw waqtında kodlawshı qáteligi kelip shıqtı.\"\nOutput.StreamEncodeError.Msg.LastError=\"Esittiriw waqtında kodlawshı qáteligi kelip shıqtı:<br><br>%1\"\nOutput.RecordFail.Title=\"Jazıwdı baslaw ámelge aspadı\"\nOutput.RecordNoSpace.Title=\"Diskte bos orın jeterli emes\"\nOutput.RecordNoSpace.Msg=\"Jazıp alıwdı dawam ettiriw ushın diskte bos orın jeterli emes.\"\nOutput.RecordError.Title=\"Jazıp alıw qátesi\"\nOutput.RecordError.Msg=\"Jazıp alıw waqtında belgisiz qátelik júz berdi.\"\nOutput.RecordError.EncodeErrorMsg=\"Jazıp alıw waqtında kodlawshı qáteligi kelip shıqtı.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Jazıp alıw waqtında kodlawshı qáteligi kelip shıqtı:<br><br>%1\"\nOutput.BadPath.Title=\"Faylǵa qáte jol\"\nOutput.BadPath.Text=\"Sazlanǵan joldı ashıw múmkin emes. Ótinish, menyude jazbalarǵa joldı tekseriń «Sazlawlar → Shıǵıw → Jazıw».\"\nOutput.NoBroadcast.Title=\"Esittiriw sazlanbaǵan\"\nOutput.NoBroadcast.Text=\"Tuwrıdan-tuwrı efirdi baslaw ushın, siz aldın ala esettiriwdi sazlawıńız kerek.\"\nOutput.BroadcastStartFailed=\"Esittiriwdi baslaw ámelge aspadı\"\nOutput.BroadcastStopFailed=\"Esittiriwdi toqtatıw ámelge aspadı\"\nRemux.SourceFile=\"OBS jazba faylı\"\nRemux.ClearFinished=\"Juwmaqlanǵanlardı dizimnen óshiriw\"\nRemux.ClearAll=\"Dizimdi tazalaw\"\nRemux.OBSRecording=\"OBS jazba faylı\"\nRemux.SelectRecording=\"OBS jazba faylın tańlań...\"\nRemux.SelectTarget=\"Fayldı tańlań...\"\nMissingFiles=\"Joq fayllar\"\nMissingFiles.MissingFile=\"Joq fayl\"\nMissingFiles.NewFile=\"Jańa fayl\"\nMissingFiles.HelpText=\"OBStı sońǵı ret qollanǵanıńızdan baslap birqansha fayllar joq.\"\nMissingFiles.Clear=\"<tazalanǵan>\"\nMissingFiles.NumFound=\"%1 den tabıldı %2\"\nMissingFiles.Search=\"Katalogtı izlew...\"\nMissingFiles.SelectFile=\"Fayldı tańlań...\"\nMissingFiles.SelectDir=\"Izlew ushın papkanı tańlań\"\nMissingFiles.State=\"Jaǵday\"\nMissingFiles.Missing=\"Joq\"\nMissingFiles.Replaced=\"Almastırıldı\"\nMissingFiles.Cleared=\"Tazalanǵan\"\nMissingFiles.Found=\"Tawıldı\"\nMissingFiles.AutoSearch=\"Qosımsha uqsas fayllar tabıldı\"\nMissingFiles.AutoSearchText=\"OBS bul papkada jetispeytuǵın fayllar ushın qosımsha uqsaslıqlardı taptı. Olardı qosıwdı qáleysiz be?\"\nMissingFiles.NoMissing.Title=\"Joǵalǵan fayllardı tekseriw\"\nMissingFiles.NoMissing.Text=\"Joǵalǵan fayllar tabılmadı.\"\nMacPermissions.MenuAction=\"Qosımshanıń ruqsatların kóriw...\"\nMacPermissions.Title=\"Qosımshanıń ruqsatların kóriw\"\nMacPermissions.Description.OpenDialog=\"Bul menyudı OBS menyuinen qayta ashıw múmkin.\"\nMacPermissions.AccessGranted=\"Ruqsat berildi\"\nMacPermissions.RequestAccess=\"Ruqsat soraw\"\nMacPermissions.OpenPreferences=\"%1 sazlawların ashıw\"\nMacPermissions.Item.ScreenRecording=\"Ekrannan jazıp alıw\"\nMacPermissions.Item.ScreenRecording.Details=\"Siziń ekranıńızdı qamtıp alıw ushın OBS bul ruqsattı talap etedi.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Accessibility=\"Arnawlı imkaniyatlar\"\nMacPermissions.Continue=\"Dawam etiw\"\nSourceLeak.Title=\"Derekti tazalaw qátesi\"\nSourceLeak.Text=\"Saxnalar toplamın ózgertiwde qátelik kelip shıqtı hám birqansha dereklerdi júklew ámelge aspadı. Bul mashqala ádette xordı durıs bosatpaytuǵın plaginlerden kelip shıǵadı. Ótinish, barlıq qollanıp atırǵan plaginler jańalanǵanın tekseriń.\\n\\nMaǵlıwmatlardıń buzılıwın aldın alıw ushın OBS jumısın toqtatadı.\"\nBasic.DesktopDevice1=\"Jumıs stolı dawısı\"\nBasic.DesktopDevice2=\"Jumıs stolı dawısı 2\"\nBasic.AuxDevice1=\"Mikrofon/Aux\"\nBasic.AuxDevice2=\"Mikrofon/Aux 2\"\nBasic.AuxDevice3=\"Mikrofon/Aux 3\"\nBasic.AuxDevice4=\"Mikrofon/Aux 4\"\nBasic.Scene=\"Saxna\"\nBasic.DisplayCapture=\"Ekrandı qamtıp alıw\"\nBasic.Main.PreviewConextMenu.Enable=\"Aldınnan kóriwdi qosıw\"\nBasic.Main.Preview.Disable=\"Aldınnan kóriwdi óshiriw\"\nScaleFiltering=\"Kólem filtrı\"\nScaleFiltering.Point=\"Noqat\"\nScaleFiltering.Area=\"Maydan\"\nBlendingMethod=\"Aralastırıw usılı\"\nBlendingMethod.Default=\"Aldınnan ornatılǵan\"\nBlendingMethod.SrgbOff=\"SRGB óshiriw\"\nBlendingMode=\"Aralastırıw rejimi\"\nBlendingMode.Normal=\"Ádettegidey\"\nBlendingMode.Additive=\"Qosıw\"\nBlendingMode.Subtract=\"Alıp tastaw\"\nBlendingMode.Screen=\"Ekran\"\nBlendingMode.Multiply=\"Kóbeytiw\"\nBlendingMode.Lighten=\"Jaqtılandırıw\"\nBlendingMode.Darken=\"Qarańǵılatıw\"\nDeinterlacing.Discard=\"Biykarlaw\"\nDeinterlacing.TopFieldFirst=\"Joqarǵı maydan birinshi\"\nDeinterlacing.BottomFieldFirst=\"Tómengi maydan birinshi\"\nVolControl.Mute=\"'%1' dawısın óshiriw\"\nBasic.Main.AddSceneDlg.Title=\"Saxna qosıw\"\nBasic.Main.AddSceneDlg.Text=\"Ótinish, saxna atamasın kirgizıń\"\nBasic.Main.DefaultSceneName.Text=\"Saxna %1\"\nBasic.Main.AddSceneCollection.Title=\"Saxnalar toplamın qosıw\"\nBasic.Main.AddSceneCollection.Text=\"Ótinish, saxnalar toplamınıń atamasın kirgizıń\"\nBasic.Main.RenameSceneCollection.Title=\"Saxna toplamın qayta ataw\"\nAddProfile.Title=\"Profil qosıw\"\nAddProfile.Text=\"Ótinish, profil atamasın kirgizıń\"\nAddProfile.WizardCheckbox=\"Avtomat tárizde sazlaw ustasın kórsetiw\"\nRenameProfile.Title=\"Profildi qayta ataw\"\nBasic.Main.MixerRename.Title=\"Audio derekti qayta ataw\"\nBasic.Main.MixerRename.Text=\"Ótinish, audio derektiń atamasın kirgizıń\"\nBasic.Main.PreviewDisabled=\"Aldınnan kóriw házirgi waqıtta óshirilgen\"\nBasic.SourceSelect=\"Derekti Jaratıw/Tańlaw\"\nBasic.SourceSelect.CreateNew=\"Jańasın jaratıw\"\nBasic.SourceSelect.AddExisting=\"Barın tańlaw\"\nBasic.SourceSelect.AddVisible=\"Derekti kórinetuǵın etiw\"\nBasic.Main.Sources.Visibility=\"Kórinis\"\nBasic.Main.Sources.Lock=\"Bloklaw\"\nBasic.PropertiesWindow.SelectColor=\"Reńdi tańlaw\"\nBasic.PropertiesWindow.SelectFont=\"Shrifttı tańlaw\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Shriftti tańlaw\"\nBasic.PropertiesWindow.ConfirmTitle=\"Sazlawlar ózgertildi\"\nBasic.PropertiesWindow.Confirm=\"Saqlanbaǵan ózgerisler bar. Olardı saqlawdı qáleysiz be?\"\nBasic.PropertiesWindow.AddFiles=\"Fayllardı qosıw\"\nBasic.PropertiesWindow.AddDir=\"Papka qosıw\"\nBasic.PropertiesWindow.AddURL=\"Jol/URL qosıw\"\nBasic.PropertiesView.UrlButton.Text.Url=\"URL mánzil: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"URL mánzildi ashıw\"\nBasic.StatusBar.Reconnecting=\"Baylanıs úzildi, qayta jalǵanıw %2 sekunttan (urınıw sanı: %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Qayta jalǵanıw urınısı... (urınıw sanı: %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Qayta jalǵanıw tabıslı\"\nBasic.StatusBar.Delay=\"Irkilis (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Irkilis (baslanıwı %1 sekunttan keyin)\"\nBasic.StatusBar.DelayStoppingIn=\"Irkilis (toqtatılıwı %1 sekunttan keyin)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Irkilis (toqtatılıwı %1 sekunttan keyin, baslanıwı %2 sekunttan keyin)\"\nBasic.StatusBar.RecordingSavedTo=\"Jazba '%1' jolına saqlandı\"\nBasic.Filters=\"Filtrler\"\nBasic.Filters.AsyncFilters=\"Audio/Video filtrleri\"\nBasic.Filters.AudioFilters=\"Audio filtrler\"\nBasic.Filters.EffectFilters=\"Effekt filtrleri\"\nBasic.Filters.Title=\"'%1' ushın filtrler\"\nBasic.Filters.AddFilter.Title=\"Filtr ataması\"\nBasic.Filters.AddFilter.Text=\"Ótinish, filtr atamasın kirgizıń\"\nBasic.TransformWindow.Position=\"Orın\"\nBasic.TransformWindow.Rotation=\"Aylanıw\"\nBasic.TransformWindow.Size=\"Kólemi\"\nBasic.TransformWindow.Width=\"Eni\"\nBasic.TransformWindow.Height=\"Uzınlıǵı\"\nBasic.TransformWindow.Alignment=\"Ornın tegislew\"\nBasic.TransformWindow.BoundsType=\"Sheklew qutısı túri\"\nBasic.TransformWindow.BoundsAlignment=\"Sheklew qutısınıń ishinde tegislew\"\nBasic.TransformWindow.Bounds=\"Sheklew qutısı ólshemi\"\nBasic.TransformWindow.BoundsWidth=\"Sheklew qutısı biyikligi\"\nBasic.TransformWindow.BoundsHeight=\"Sheklewshiniń biyikligi\"\nBasic.TransformWindow.Crop=\"Kesiw\"\nBasic.TransformWindow.CropLeft=\"Shep tárepti qıyıw\"\nBasic.TransformWindow.CropRight=\"Oń tárepti qıyıw\"\nBasic.TransformWindow.CropTop=\"Joqarı tárepti qıyıw\"\nBasic.TransformWindow.CropBottom=\"Tómengi tárepti qıyıw\"\nBasic.TransformWindow.Alignment.TopLeft=\"Joqarı shep tárep\"\nBasic.TransformWindow.Alignment.TopCenter=\"Joqarı oray\"\nBasic.TransformWindow.Alignment.TopRight=\"Joqarı oń tárep\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Oray shep tárepi\"\nBasic.TransformWindow.Alignment.Center=\"Oray\"\nBasic.TransformWindow.Alignment.CenterRight=\"Oray oń tárepi\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Tómengi shep tárep\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Tómengi oray\"\nBasic.TransformWindow.Alignment.BottomRight=\"Tómengi oń tárep\"\nBasic.TransformWindow.BoundsType.None=\"Shegarasız\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Maksimal kólem\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Ishki shegaraǵa deyin úlkeytiw\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Sırtqı shegaraǵa deyin úlkeytiw\"\nBasic.TransformWindow.BoundsType.Stretch=\"Shegaraǵa deyin sozıw\"\nBasic.TransformWindow.NoSelectedSource=\"Derek tańlanbaǵan\"\nBasic.Main.AddSourceHelp.Title=\"Derek qosıw múmkin emes\"\nBasic.Main.AddSourceHelp.Text=\"Derek qosıw ushın keminde bir saxna jaratıwıńız kerek.\"\nBasic.Main.Scenes=\"Saxnalar\"\nBasic.Main.Sources=\"Derekler\"\nBasic.Main.Source=\"Derek\"\nBasic.Main.Controls=\"Basqarıw\"\nBasic.Main.Connecting=\"Jalǵanbaqta...\"\nBasic.Main.StartRecording=\"Jazıp alıwdı baslaw\"\nBasic.Main.SaveReplay=\"Qayta qoyıwdı saqlaw\"\nBasic.Main.StartStreaming=\"Esittiriwdi baslaw\"\nBasic.Main.StartBroadcast=\"Efirge shıǵıw\"\nBasic.Main.StartVirtualCam=\"Virtual kameranı iske túsiriw\"\nBasic.Main.StopRecording=\"Jazıp alıwdı toqtatıw\"\nBasic.Main.PauseRecording=\"Jazıp alıwdı toqtatıp turıw\"\nBasic.Main.UnpauseRecording=\"Jazıp alıwdı dawam etiw\"\nBasic.Main.SplitFile=\"Jazba faylın bóliw\"\nBasic.Main.StoppingRecording=\"Jazıw toqtatılmaqta...\"\nBasic.Main.SetupBroadcast=\"Esittiriwdi basqarıw\"\nBasic.Main.StopStreaming=\"Esittiriwdi toqtatıw\"\nBasic.Main.StopBroadcast=\"Esittiriwdi tamamlaw\"\nBasic.Main.AutoStopEnabled=\"(Avtomat tárizde toqtatıw)\"\nBasic.Main.StoppingStreaming=\"Esittiriw toqtatımaqta...\"\nBasic.Main.ShowContextBar=\"Derektiń ásbaplar panelin kórsetiw\"\nBasic.Main.HideContextBar=\"Derektiń ásbaplar panelin jasırıw\"\nBasic.Main.StopVirtualCam=\"Virtual kameranı toqtatıw\"\nBasic.Main.Group=\"Topar %1\"\nBasic.Main.GridMode=\"Keste rejimi\"\nBasic.Main.ListMode=\"Dizim rejimi\"\nBasic.Main.VirtualCamConfig=\"Virtual kameranı sazlaw\"\nBasic.VCam.VirtualCamera=\"Virtual kamera\"\nBasic.VCam.OutputType=\"Shıǵıw túri\"\nBasic.VCam.OutputType.Program=\"Baǵdarlama (ádepki)\"\nBasic.VCam.OutputSelection.NoSelection=\"Bul shıǵıw túri ushın tańlaw joq\"\nBasic.VCam.RestartWarning=\"Bul ózgerislerdi qollanıw ushın virtual kamera qayta iske túsiriledi\"\nBasic.MainMenu.File=\"Fayl (&F)\"\nBasic.MainMenu.File.Export=\"Eksportlaw (&E)\"\nBasic.MainMenu.File.Import=\"Importlaw (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Jazbalardı kórsetiw (&R)\"\nBasic.MainMenu.File.Settings=\"Sazlawlar (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Sazlawlar papkasın ashıw\"\nBasic.MainMenu.File.ShowProfileFolder=\"Profil papkasın ashıw\"\nBasic.MainMenu.File.ShowMissingFiles=\"Joǵalǵan fayllardı tekseriw\"\nBasic.MainMenu.File.Exit=\"Shıǵıw (&X)\"\nBasic.MainMenu.Edit=\"Ózgeris kirgiziw (&E)\"\nBasic.MainMenu.Edit.Undo=\"Biykarlaw (&U)\"\nBasic.MainMenu.Edit.Redo=\"Qaytalaw (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Aldınnan kóriwdi bloklaw (&L)\"\nBasic.MainMenu.Edit.Scale=\"Aldınnan kóriw kólemi (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"Ayna kólemi\"\nBasic.MainMenu.Edit.Scale.Output=\"Shıǵıw (%1x%2)\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180 gradusqa aylandırıw\"\nBasic.MainMenu.Edit.Order=\"Tártip (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Joqarıǵa kóshiriw (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Tómenge kóshiriw (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Eń joqarıǵa kóshiriw (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Eń tomenge kóshiriw (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Dawıstıń keńeytilgen qásiyetleri (&A)\"\nBasic.MainMenu.View=\"Kórinis (&V)\"\nBasic.MainMenu.View.Toolbars=\"Ásbaplar paneli (&T)\"\nBasic.MainMenu.View.ContextBar=\"Derektiń ásbaplar paneli\"\nBasic.MainMenu.View.StatusBar=\"Jaǵday qatarı (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Tolıq ekran\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Hámiyshe basqa aynalar ústinde\"\nBasic.MainMenu.View.SceneListMode=\"Saxnalar dizimi rejimi\"\nBasic.MainMenu.Docks=\"&Paneller\"\nBasic.MainMenu.SceneCollection=\"&Saxnalar toplamı\"\nBasic.MainMenu.Profile=\"Profil (&P)\"\nBasic.MainMenu.Profile.Import=\"Profildi importlaw\"\nBasic.MainMenu.Profile.Export=\"Profildi eksportlaw\"\nBasic.MainMenu.SceneCollection.Import=\"Saxna toplamın import qılıw\"\nBasic.MainMenu.SceneCollection.Export=\"Saxna toplamın eksport qılıw\"\nBasic.MainMenu.Profile.Exists=\"Profil álleqashan bar\"\nBasic.MainMenu.Tools=\"Ásbaplar (&T)\"\nBasic.MainMenu.Help=\"Járdem (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Maǵlıwmatlar portalı (&P)\"\nBasic.MainMenu.Help.Website=\"Veb-saytqa kiriw (&W)\"\nBasic.MainMenu.Help.WhatsNew=\"Ne jańalıq\"\nBasic.MainMenu.Help.Logs=\"Esap faylları (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Esap faylların kórsetiw (&S)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Ózgerisler jurnalı\"\nBasic.MainMenu.Help.CheckForUpdates=\"Jańalanıwlardı tekseriw\"\nBasic.MainMenu.Help.Repair=\"Fayllardıń pútinligin tekseriw\"\nBasic.MainMenu.Help.RestartSafeMode=\"Qáwipsiz rejimde qayta iske túsiriw\"\nBasic.MainMenu.Help.RestartNormal=\"Ádettegi rejimde qayta iske túsiriw\"\nBasic.MainMenu.Help.CrashLogs=\"Qátelikler tuwralı esaplar (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Qátelikler tuwralı esaplardı kórsetiw\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Qátelik tuwralı aldıńǵı esaptı jiberiw (&P)\"\nBasic.MainMenu.Help.About=\"Baǵdarlama haqqında (&A)\"\nBasic.Settings.ProgramRestart=\"Bul sazlawlar kúshke kiriwi ushın baǵdarlamanı qayta iske túsiriw talap etiledi.\"\nBasic.Settings.ConfirmTitle=\"Ózgerislerdi tastıyıqlaw\"\nBasic.Settings.Confirm=\"Sizde saqlanbaǵan ózgerisler bar. Ózgerisler saqlansın ba?\"\nBasic.Settings.General=\"Ulıwmalıq\"\nBasic.Settings.General.Language=\"Til\"\nBasic.Settings.General.Updater=\"Jańalanıwlar\"\nBasic.Settings.General.UpdateChannel=\"Jańalaw kanalı\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Óshirilgen)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Aldınnan ornatılǵan)\"\nBasic.Settings.General.EnableAutoUpdates=\"Iske túsirgende jańalanıwlardı tekseriw\"\nBasic.Settings.General.OpenStatsOnStartup=\"Iske túsirgende statistika aynasın ashıw\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Qamtıp alıwdan OBS aynaların jasırıw\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Esittiriwdi baslaǵanda, tastıyıqlaw aynasın kórsetiw\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Esittiriwdi toqtatqanda, tastıyıqlaw aynasın kórsetiw\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Jazıp alıwdı toqtatqanda, tastıyıqlaw aynasın kórsetiw\"\nBasic.Settings.General.Projectors=\"Proektorlar\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Proektorlardı hámiyshe barlıq aynalar ústinde kórsetiw\"\nBasic.Settings.General.RecordWhenStreaming=\"Esittiriw waqtında avtomat tárizde jazıp alıw\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Esittiriwdi juwmaqlaǵanda jazıp alıwdı dawam etiw\"\nBasic.Settings.General.SysTray=\"Xabarlamalar maydanı\"\nBasic.Settings.General.SaveProjectors=\"Shıqqanda proektorlardı saqlaw\"\nBasic.Settings.General.Preview=\"Aldınnan kóriw\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Aldınnan kóriw/Baǵdarlama belgilerin kórsetiw\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Saxna atamaların kórsetiw\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Qáwipsiz zonalardı sızıw (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Tek saxnalar (4 saxna)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Tek saxnalar (9 saxna)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Tek saxnalar (16 saxna)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Tek saxnalar (25 saxna)\"\nBasic.Settings.General.ChannelName.stable=\"Turaqlı\"\nBasic.Settings.General.ChannelDescription.stable=\"Sońǵı turaqlı versiya\"\nBasic.Settings.Stream=\"Esittiriw\"\nBasic.Settings.Stream.Custom.Username=\"Paydalanıwshı atı\"\nBasic.Settings.Stream.Custom.Password=\"Parol\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: kirgiziń srp_username.\\nRTMP: paydalanıwshı atın kirgiziń.\\nSRT: qollanılmaydı.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: kirgiziń srp_password.\\nRTMP: paroldı kirgiziń.\\nSRT: shifrlaw parolin kirgiziń.\"\nBasic.Settings.Stream.TTVAddon=\"Twitch chatınıń qosımshaları\"\nBasic.Settings.Stream.TTVAddon.None=\"Joq\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV hám FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Esittiriw sazlanbaǵan\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Sazlawlardı ashıw\"\nBasic.Settings.Stream.IgnoreRecommended=\"Esittiriw xızmetinıń sazlawlar usınısın esapqa almaw\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Usınılǵan sazlawlardı qayta belgilew\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Eskertiw: Esittiriw xızmetlerinıń sheklewlerin esapqa almaw onı júrgiziwge yamasa onıń sapasına jaman tásir etiwi múmkin.\\n\\nDawam ettiriwdi qáleysiz be?\"\nBasic.Settings.Stream.ServiceCustomServer=\"Arnawlı Server\"\nBasic.Settings.Output=\"Shıǵıw\"\nBasic.Settings.Output.Format=\"Jazıp alıw formatı\"\nBasic.Settings.Output.Format.fMP4=\"Bóleklengen MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Bóleklengen MOV (.mov)\"\nBasic.Settings.Output.Encoder.Video=\"Video kodlawshısı\"\nBasic.Settings.Output.Encoder.Audio=\"Dawıs kodlawshısı\"\nBasic.Settings.Output.SelectDirectory=\"Jazbalar ushın papkanı tańlań\"\nBasic.Settings.Output.Mode=\"Shıǵıw rejimi\"\nBasic.Settings.Output.Mode.Simple=\"Ápiwayı\"\nBasic.Settings.Output.Mode.Adv=\"Qosımsha\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg shıǵıwı\"\nBasic.Settings.Output.UseReplayBuffer=\"Qaytalaw buferin qosıw\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Suffiks\"\nBasic.Settings.Output.Simple.SavePath=\"Jazıw jolı\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Jazıw sapası\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Esittiriwdegidey\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Joqarı sapa, ortasha fayl kólemi\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Programmalıq (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Apparatlıq (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Apparatlıq (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Apparatlıq (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Apparatlıq (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Apparatlıq (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Apparatlıq (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Apparatlıq (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Apparatlıq (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Apparatlıq (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Apparatlıq (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (ádepki)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Kólem: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Kadrlar tezligi (FPS): %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Sáykes emes kodlawshı\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"\\\"%1\\\" esittiriw xızmeti \\\"%2\\\" kodlawshısın jebemeydi. \\\"%3\\\" kodlawshısına ózgertiledi.\\n\\nDawam etiwdi qáleysiz be?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"\\\"%1\\\" esittiriw xızmeti \\\"%2\\\" hám \\\"%3\\\" kodlawshıların jebemeydi. Olar \\\"%4\\\" hám \\\"%5\\\" kodlawshılarına ózgertiledi.\\n\\nDawam etiwdi qáleysiz be?\"\nBasic.Settings.Output.Reconnect=\"Avtomat tárizde qayta jalǵanıw\"\nBasic.Settings.Output.MaxRetries=\"Maksimal qayta urınıw\"\nBasic.Settings.Output.Advanced=\"Kodlawshınıń qosımsha sazlawların qosıw\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (OPdan paydalanıw tómen, tómen sapa)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (ádepki) (OPdan paydalanıw ortasha, standart sapa)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (OPdan paydalanıw joqarı, joqarı sapa)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Kodlawshınıń arnawlı sazlawları\"\nBasic.Settings.Output.NoSpaceFileName=\"Fayl atamasın bos orınsız jaratıw\"\nBasic.Settings.Output.Adv.Streaming=\"Esittiriw\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Esittiriw sazlawları\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Qosıq 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Qosıq 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Qosıq 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Qosıq 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Qosıq 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Qosıq 6\"\nBasic.Settings.Output.Adv.Encoder=\"Kodlawshı sazlawları\"\nBasic.Settings.Output.Adv.Recording=\"Jazıp alıw\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Jazıp alıw sazlawları\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Jazıp alıw túri\"\nBasic.Settings.Output.Adv.Recording.Type=\"Túr\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standart\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Arnawlı shıǵıw (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Esittiriw kodlawshısın qollanıw)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Fayl atamasınıń formatı\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg shıǵıw túri\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Kórsetilgen mánzilge (URL)\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Faylǵa shıǵarıw\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Barlıq fayllar\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Fayl jolı yamasa URL mánzil\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Konteyner format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Standart format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Konteyner format táriypi\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Audio hám video kodekler URL mánzil yamasa fayl jolı arqalı tabıladı\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Ádepki kodlawshı\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Kodlawshını óshiriw\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Video kodlawshı\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Video kodlawshısınıń sazlawları (eger bar bolsa)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Dawıs kodlawshısı\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Dawıs kodlawshısınıń sazlawları (eger bar bolsa)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg sazlawları\"\nBasic.Settings.Output.EnableSplitFile=\"Avtomat tárizde fayldı bóliw\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Waqıt boyınsha bóliw\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Kólem boyınsha bóliw\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Tek qolda bóliw\"\nBasic.Settings.Output.SplitFile.Time=\"Bóliw waqtı\"\nBasic.Settings.Output.SplitFile.Size=\"Bóliw kólemi\"\nFilenameFormatting.TT.CCYY=\"Jıl, tórt san\"\nFilenameFormatting.TT.YY=\"Jıl, sońǵı eki san (00-99)\"\nFilenameFormatting.TT.hh=\"Saat 24 saat formatında (00-23)\"\nFilenameFormatting.TT.mm=\"Minut (00-59)\"\nFilenameFormatting.TT.ss=\"Sekund (00-59)\"\nFilenameFormatting.TT.Percent=\"% belgisi\"\nFilenameFormatting.TT.a=\"Hápte kuninıń qısqa ataması\"\nFilenameFormatting.TT.A=\"Hápte kuninıń tolıq ataması\"\nFilenameFormatting.TT.b=\"Aydıń qısqa ataması\"\nFilenameFormatting.TT.B=\"Aydıń tolıq ataması\"\nFilenameFormatting.TT.H=\"Saat 24 saat formatında (00-23)\"\nFilenameFormatting.TT.I=\"Saat 12 saat formatında (01-12)\"\nFilenameFormatting.TT.M=\"Minut (00-59)\"\nFilenameFormatting.TT.S=\"Sekund (00-59)\"\nFilenameFormatting.TT.y=\"Jıl, sońǵı eki san (00-99)\"\nFilenameFormatting.TT.Y=\"Jıl\"\nFilenameFormatting.TT.FPS=\"Kadrlar tezligi\"\nBasic.Settings.Video.Adapter=\"Video adapter\"\nBasic.Settings.Video.FPS=\"Kadrlar tezligi (FPS)\"\nBasic.Settings.Audio.Channels=\"Kanallar\"\nBasic.Settings.Audio.Meters=\"Ólshewishler\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Tez\"\nBasic.Settings.Audio.Devices=\"Ulıwmalıq dawıs qurılmaları\"\nBasic.Settings.Audio.DesktopDevice=\"Jumıs stolı dawısı\"\nBasic.Settings.Audio.DesktopDevice2=\"Jumıs stolı dawısı 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/qosımsha dawıs\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/qosımsha dawıs 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/qosımsha dawıs 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/qosımsha dawıs 4\"\nBasic.Settings.Audio.Disabled=\"Óshirilgen\"\nBasic.Settings.Accessibility=\"Arnawlı imkaniyatlar\"\nBasic.Settings.Accessibility.ColorOverrides=\"Basqa reńlerdi qollanıw\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Aldınnan ornatılǵan\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Arnawlı\"\nBasic.Settings.Advanced=\"Qosımsha\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Joqarı\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Ortasha\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Tómen\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Reń formatı\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 bit, 4:2:0, 2 dáreje)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 bit, 4:2:0, 3 dáreje)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 bit, 4:4:4, 3 dáreje)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 bit, 4:2:0, 2 dáreje)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 bit, 4:2:0, 3 dáreje)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 bit, 4:2:2, 2 dáreje)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 bit, 4:4:4, 2 dáreje)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 bit)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Reń maydanı\"\nBasic.Settings.Advanced.Video.ColorRange=\"Reń aralıǵı\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Sheklengen\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Tolıq\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR aqlıq dárejesi\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Aldınnan ornatılǵan\"\nBasic.Settings.Advanced.Network=\"Tarmaq\"\nBasic.Settings.Advanced.Network.Disabled=\"Ámeldegi tańlanǵan esittiriw protokolı tarmaq sazlawların ózgertiwdi jebemeydi.\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(mkv retinde jazıp alıw)\"\nBasic.AdvAudio=\"Dawıstıń keńeytilgen qásiyetleri\"\nBasic.AdvAudio.ActiveOnly=\"Tek aktiv derekler\"\nBasic.AdvAudio.Name=\"Ataması\"\nBasic.AdvAudio.Volume=\"Dawıs bálentligi\"\nBasic.AdvAudio.VolumeSource=\"'%1' ushın dawıs bálentligi\"\nBasic.AdvAudio.AudioTracks=\"Qosıqlar\"\nBasic.Settings.Hotkeys.Filter=\"Filtr\"\nBasic.Hotkeys.SelectScene=\"Saxnaǵa ótiw\"\nBasic.SystemTray.Show=\"Kórsetıw\"\nBasic.SystemTray.Hide=\"Jasırıw\"\nBasic.SystemTray.Message.Reconnecting=\"Baylanıs úzildi. Qayta jalǵanbaqta...\"\nHotkeys.Insert=\"Ornalastırıw\"\nHotkeys.Delete=\"Óshiriw\"\nHotkeys.Home=\"Bas bet\"\nHotkeys.End=\"Juwmaqlaw\"\nHotkeys.PageUp=\"1 bet joqarıǵa\"\nHotkeys.PageDown=\"1 bet tómenge\"\nHotkeys.Pause=\"Pauza\"\nHotkeys.Left=\"Shep\"\nHotkeys.Right=\"Oń\"\nHotkeys.Up=\"Joqarıǵa\"\nHotkeys.Down=\"Tómenge\"\nHotkeys.Menu=\"Menyu\"\nHotkeys.Space=\"Bos orın\"\nHotkeys.MouseButton=\"Tıshqansha %1\"\nMute=\"Dawıstı óshiriw\"\nUnmute=\"Dawıstı qosıw\"\nSceneItemShow=\"Kórsetiw '%1'\"\nSceneItemHide=\"Jasırıw '%1'\"\nOutputWarnings.NoTracksSelected=\"Siz keminde bir qosıqtı tańlawıńız kerek\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Shıǵıw sazlawları qáteligi\"\nCodecCompat.Incompatible=\"(%1 penen sáykes emes)\"\nCodecCompat.CodecPlaceholder=\"Kodlawshını tańlaw...\"\nCodecCompat.ContainerPlaceholder=\"Formattı tańlaw...\"\nCodecCompat.CodecMissingOnExit.Title=\"Kodlawshı tańlanbaǵan\"\nCodecCompat.ContainerMissingOnExit.Title=\"Format tańlanbaǵan\"\nCodecCompat.ContainerMissingOnExit.Text=\"Jazıp alıw formatı tańlanbaǵan. Ótinish, tańlanǵan esittiriw kodlawshısı menen sáykes jazıp alıw formatın tańlań.\"\nFinalScene.Title=\"Saxnanı óshiriw\"\nFinalScene.Text=\"Sońǵı saxnanı óshiriwge bolmaydı.\"\nNoSources.Title=\"Derekler joq\"\nNoSources.Label=\"Sizde derekler joq\\nQosıw ushın tómendegi + túymesine yamasa usı jerge tıshqanshanıń oń túymesin basıń.\"\nChangeBG=\"Reń ornatıw\"\nCustomColor=\"Arnawlı reń\"\nBrowserSource.EnableHardwareAcceleration=\"«Brauzer» deregi ushın apparatlıq tezlestiriwdi qosıw\"\nAbout=\"Baǵdarlama haqqında\"\nAbout.Donate=\"Úles qosıw\"\nAbout.GetInvolved=\"Qatnasıw\"\nAbout.Authors=\"Avtorlar\"\nAbout.License=\"Licenziya\"\nAbout.Error=\"Qátelik! Fayldı oqıw múmkin emes.\\n\\nGit: %1\"\nAbout.Contribute=\"OBS jobasın qollap-quwatlaw\"\nAddUrl.Title=\"URL mánzil arqalı derek qosıw\"\nAddUrl.Text.Url=\"URL mánzil: %1\"\nResizeOutputSizeOfSource.Continue=\"Dawam etiwdi qáleysiz be?\"\nPreviewTransition=\"Ótiwdi aldınnan kóriw\"\nImporter.SelectCollection=\"Saxnalar toplamın tańlań\"\nImporter.Collection=\"Saxnalar toplamı\"\nImporter.Path=\"Toplam jolı\"\nImporter.Program=\"Tabılǵan qosımsha\"\nImporter.AutomaticCollectionPrompt=\"Saxnalar toplamların avtomat tárizde izlew\"\nRestart=\"Qayta iske túsiriw\"\nNeedsRestart=\"OBS qayta iske túsiriliwi zárúr. Házir qayta iske túsiriwdi qáleysiz be?\"\nContextBar.NoSelectedSource=\"Derek tańlanbaǵan\"\nContextBar.MediaControls.PlayMedia=\"Media qoyıw\"\nContextBar.MediaControls.PauseMedia=\"Medianı toqtatıp turıw\"\nContextBar.MediaControls.StopMedia=\"Medianı toqtatıw\"\nContextBar.MediaControls.RestartMedia=\"Medianı qayta qoyıw\"\nContextBar.MediaControls.PlaylistPrevious=\"Pleylistte aldınǵısı\"\nYouTube.Auth.WaitingAuth.Text=\"Ótinish, kiriwdi sırtqı brauzerge juwmaqlań.<br>Eger sırtqı brauzer ashılmasa, usı siltemege ótiń hám kiriwdi juwmaqlań:<br>%1\"\nYouTube.AuthError.Text=\"Kanal haqqında maǵlıwmattı alıw ámelge aspadı: %1.\"\nYouTube.Actions.CreateNewEvent=\"Jańa esittiriw jaratıw\"\nYouTube.Actions.ChooseEvent=\"Bar esittiriwdi tańlaw\"\nYouTube.Actions.Title=\"Atama*\"\nYouTube.Actions.MyBroadcast=\"Meniń esittiriwim\"\nYouTube.Actions.Description=\"Táripi\"\nYouTube.Actions.Privacy=\"Qupıyalıq*\"\nYouTube.Actions.Privacy.Private=\"Jekelik\"\nYouTube.Actions.Privacy.Public=\"Hámmege kórinetuǵın\"\nYouTube.Actions.Privacy.Unlisted=\"Silteme arqalı kiriw\"\nYouTube.Actions.Category=\"Kategoriya\"\nYouTube.Actions.Thumbnail.SelectFile=\"Fayldı tańlaw...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Fayl tańlanbaǵan\"\nYouTube.Actions.Thumbnail.ClearFile=\"Tazalaw\"\nYouTube.Actions.MadeForKids=\"Bul video balalar ushın tayarlanǵan ba?*\"\nYouTube.Actions.MadeForKids.Yes=\"Awa, bul balalar ushın tayarlanǵan\"\nYouTube.Actions.MadeForKids.No=\"Yaq, bul balalar ushın tayarlanbaǵan\"\nYouTube.Actions.AdditionalSettings=\"Qosımsha sazlawlar\"\nYouTube.Actions.Latency=\"Iskilis\"\nYouTube.Actions.Latency.Normal=\"Ortasha\"\nYouTube.Actions.Latency.Low=\"Tómen\"\nYouTube.Actions.Latency.UltraLow=\"Júda tómen\"\nYouTube.Actions.EnableAutoStart=\"Avtomat tárizde iske túsiriwdi qosıw\"\nYouTube.Actions.EnableAutoStop=\"Avtomat tárizde toqtatıwdı qosıw\"\nYouTube.Actions.EnableDVR=\"DVRdı qosıw\"\nYouTube.Actions.ScheduleForLater=\"Keyinirekke rejelestiriw\"\nYouTube.Actions.RememberSettings=\"Bul sazlawlardı este saqlaw\"\nYouTube.Actions.Create_Ready=\"Esittiriw jaratıw\"\nYouTube.Actions.Choose_Ready=\"Esittiriwdi tańlaw\"\nYouTube.Actions.Dashboard=\"YouTube Studionı Ashıw\"\nYouTube.Actions.Error.Title=\"Esittiriwdi jaratıw qátesi\"\nYouTube.Actions.Error.BroadcastNotFound=\"Tańlanǵan esittiriw tabılmadı.\"\nYouTube.Actions.Error.FileMissing=\"Tańlanǵan fayl joq.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Tańlanǵan fayldı ashıw ámelge aspadı.\"\nYouTube.Actions.Error.FileTooLarge=\"Tańlanǵan fayl júdá úlken (sheklew: 2 Mb)\"\nYouTube.Actions.Stream=\"Esittiriw\"\nYouTube.Actions.Stream.YTStudio=\"YouTube Studio tárepinen avtomat tárizde jaratıldı\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Qolda iske túsiriw talap etiledi\"\nYouTube.Errors.errorStreamInactive=\"Esittiriwińiz ushın YouTube maǵlıwmatlardı almay atır. Ótinish, sazlawlardı teskeriń hám qayta urınıp kóriń.\"\n"
  },
  {
    "path": "frontend/data/locale/kab-KAB.ini",
    "content": "Language=\"Taqbaylit\"\nOK=\"Ih\"\nApply=\"Snes\"\nCancel=\"Sefsex\"\nClose=\"Mdel\"\nSave=\"Sekles\"\nDiscard=\"Sefsex\"\nDisable=\"Sens\"\nYes=\"Ih\"\nNo=\"Ala\"\nAdd=\"Rnu\"\nRemove=\"Kkes\"\nRename=\"Snifel isem\"\nInteract=\"Tamyigawt\"\nFilters=\"Imzizdigen\"\nProperties=\"Iraten\"\nMoveUp=\"Smutti d asawen\"\nMoveDown=\"Smutti d akessar\"\nSettings=\"Iɣewwaren\"\nDisplay=\"Beqqeḍ\"\nName=\"Isem\"\nExit=\"Ffeɣ\"\nMixer=\"Asexlaḍ n umeslaw\"\nBrowse=\"Snirem\"\nStereo=\"Stiryu\"\nClear=\"Sfeḍ\"\nRevert=\"Err-d\"\nShow=\"Sken\"\nHide=\"Ffer\"\nUnhideAll=\"Sban-d kulec\"\nUntitled=\"War azwel\"\nNew=\"Amaynut\"\nDuplicate=\"Sisleg\"\nEnable=\"Sermed\"\nDisableOSXVSync=\"Sens macOS V-Sync\"\nResetOSXVSyncOnExit=\"Ales awennez n macOS V-Sync mi ara teffeɣeḍ\"\nTransition=\"Asaka\"\nQuickTransitions=\"Isuka iruraden\"\nFadeToBlack=\"Tifsit ɣer uberkan\"\nLeft=\"Azelmaḍ\"\nRight=\"Ayfus\"\nTop=\"Ukessawen\"\nBottom=\"Uksar\"\nReset=\"Ales awennez\"\nHours=\"Isragen\"\nMinutes=\"Tisdatin\"\nSeconds=\"Tasinin\"\nDeprecated=\"D aqbur\"\nReplayBuffer=\"Tallunt n weḥraz n tulsa n tɣuri\"\nImport=\"Kter\"\nExport=\"Sifeḍ\"\nCopy=\"Nɣel\"\nPaste=\"Senṭeḍ\"\nPasteReference=\"Senṭeḍ (tamselɣut)\"\nPasteDuplicate=\"Senṭeḍ (Sisleg)\"\nRemuxRecordings=\"Selket ikalasen\"\nNext=\"Win ɣers\"\nBack=\"Tuɣalin\"\nDefaults=\"Amezwar\"\nRestoreDefaults=\"Imezwer\"\nHideMixer=\"Ffer deg usexlaḍ\"\nTransitionOverride=\"Ḥettem asaka\"\nShowTransition=\"Sken asayas\"\nHideTransition=\"Ffer asayas\"\nNone=\"Ulac\"\nStudioMode.Preview=\"Tadlayt\"\nStudioMode.Program=\"Ahil\"\nStudioMode.PreviewSceneName=\"Taskant: %1\"\nStudioMode.ProgramSceneName=\"Ahil: %1\"\nShowInMultiview=\"Beqqeḍ di tmuɣli tusgitt\"\nVerticalLayout=\"Taneɣruft tubdidt\"\nGroup=\"Agraw\"\nDoNotShowAgain=\"Ur d-sskan ara tikelt-nniḍen\"\nDefault=\"(Amezwar)\"\nCalculating=\"Asiḍen...\"\nFullscreen=\"Agdil aččuran\"\nWindowed=\"S usfaylu\"\nRefreshBrowser=\"Sismeḍ\"\nAspectRatio=\"Assaɣ n tmeẓri <b>%1:%2</b>\"\nLockVolume=\"Sekkeṛ ableɣ\"\nLogViewer=\"Wali aɣmmis\"\nShowOnStartup=\"Sken di tnekra\"\nOpenFile=\"Ldi afaylu\"\nAddSource=\"Rnu aɣbalu\"\nAutomatic=\"Awurman\"\nAlreadyRunning.Title=\"OBS itteddu yakan\"\nAlreadyRunning.Text=\"OBS itteddu yakan! Ma yella ur teɛmideḍ ara i waya, ttxil-k msens kra n tummanin n OBS send ad tɛerḍeḍ ad tsekkereḍ tummant tamaynutt. Ma tesɛiḍ OBS yettusɣewren ɣer talɣa taddayt di teɣzut n telɣa, ttxil-k m selken ma yella itteddu dina.\"\nAlreadyRunning.LaunchAnyway=\"Sekker akken yebɣu yili\"\nChromeOS.Title=\"Tiɣerɣert ur nettwadhel ara\"\nAuth.Authing.Title=\"Asesteb...\"\nAuth.Authing.Text=\"Asesteb s %1, ma ulac uɣilif arǧu...\"\nAuth.AuthFailure.Title=\"Tuccḍa n usesteb\"\nAuth.AuthFailure.Text=\"Asesteb s %1:\\n\\n%2: %3 ur yeddi ara\"\nAuth.InvalidScope.Title=\"Yettwasra usesteb\"\nAuth.InvalidScope.Text=\"Israyen n wesesteb i %1 beddelen. Kra n twuriwin izmer ur wjident ara.\"\nAuth.LoadingChannel.Title=\"Asader n telɣut n ubadu...\"\nAuth.LoadingChannel.Text=\"Asader n telɣut n ubadu i %1, ma ulac uɣilif arǧu...\"\nAuth.ChannelFailure.Title=\"Asali n ubadu ur yeddi ara\"\nAuth.ChannelFailure.Text=\"Asali n telɣut n ubadu i %1\\n\\n%2: %3 ur yeddi ara\"\nAuth.Chat=\"Ameslay\"\nAuth.StreamInfo=\"Talɣut n usuddem\"\nTwitchAuth.Stats=\"Tiddadanin n Twitch\"\nTwitchAuth.Feed=\"Asuddem n wermud Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Ulamek asuter n tsarut n usuddem\"\nTwitchAuth.TwoFactorFail.Text=\"OBS igguma ad iqqen ɣer umiḍan-inek n Twitch. Ma ulac uɣilif, tḥeqqeq belli asesteb s snat n tarrayin yettusbadu deg <a href='https://www.twitch.tv/settings/security'>yiɣewwaren n tɣellist n Twitch</a>acku ayagi ilaq i usekker n usuddem.\"\nRestreamAuth.Channels=\"Ibuda n Restream\"\nCopy.Filters=\"Nɣel imzizdgen\"\nPaste.Filters=\"Senṭeḍ imzizdgen\"\nBrowserPanelInit.Title=\"Tulsa n uwennez n yiminig...\"\nBrowserPanelInit.Text=\"Tulsa n uwennez n yiminig, ma ulac uɣilif arǧu...\"\nBandwidthTest.Region=\"Tamnaḍt\"\nBandwidthTest.Region.US=\"Iwanaken yeddukklen\"\nBandwidthTest.Region.EU=\"Turuft\"\nBandwidthTest.Region.Asia=\"Asya\"\nBandwidthTest.Region.Other=\"Wayeḍ\"\nBasic.AutoConfig=\"Amallal n uswel awurman\"\nBasic.AutoConfig.ApplySettings=\"Snes iɣewwaren\"\nBasic.AutoConfig.StartPage=\"Talɣut n useqdec\"\nBasic.AutoConfig.StartPage.SubTitle=\"Ini-d acu iwumi ara tesqedceḍ ahil\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Sekk i usuddem, asekles d asnaw\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Sekk kan i usekles, ulac asuddem\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Ad sqedceɣ kan takamiṛat tuhlist\"\nBasic.AutoConfig.VideoPage=\"Iɣewwaren n uvidyu\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Fk-d iɣewwaren n uvidyu i tebɣiḍ ad tesqedceḍ\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Seqdec (%1x%2) amiran\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Abeqqeḍ %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Seqdec (%1) amiran\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Ama 60 ama 30, maca smenyif 60 ma yella wamek\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Ama 60 ama 30, maca smenyif tabadut tafellayt\"\nBasic.AutoConfig.StreamPage=\"Talɣut n usuddem\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Ma ulac uɣilif, sekcem talɣut n usuddem-inek m\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Qqen amiḍan (yettwasemter)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Senser amiḍan\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Senser amiḍan?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Abeddel-agi ad ittusnes tura kan. D tidet tebɣiḍ ad tsensreḍ amiḍan-inek m?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Awi-d tasarut n usuddem\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Ugar n telɣut\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Seqdec tasarut n usuddem\"\nBasic.AutoConfig.StreamPage.Service=\"Ameẓlu\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Sken akk...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Yugen...\"\nBasic.AutoConfig.StreamPage.Server=\"Aqeddac\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Tasarut n usuddem\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Amiḍan yeqqnen\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Smenyif asettengel n warrum\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Alɣu n usuddem\"\nBasic.AutoConfig.TestPage=\"Igmaḍ n taggara\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Akayad yemmed\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Ala yetteqqen ɣer: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Igguma ad iqqen ɣer uqeddac, ma ulac uɣilif selken tuqqna-inek m ɣer internet sakin eɛreḍ tikkelt-nniḍen.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Asekyed n tehri n tesfift i: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Asekyed n usettengal n usuddem, ayagi yezmer ad yeṭṭef tasdidt...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Asekyed n usettengal n usekles, ayagi yezmer ad yeṭṭef tasdidt...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Asekker n usettengal ur yeddi ara\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Asekyed n %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Asettengal n usuddem\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Asettengal n usekles\"\nBasic.AutoConfig.RunAnytime=\"Ayagi yezmer ad yettwaselekm melmi tebɣiḍ ma teddiḍ ɣer wumuɣ Ifecka.\"\nBasic.Stats=\"Tiddadanin\"\nBasic.Stats.CPUUsage=\"Aseqdec n CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Tallunt n uḍebsi iwejden\"\nBasic.Stats.MemoryUsage=\"Aseqdec n tkatut\"\nBasic.Stats.Output.Stream=\"Asuddem\"\nBasic.Stats.Output.Recording=\"Asekles\"\nBasic.Stats.Status=\"Addad\"\nBasic.Stats.Status.Recording=\"Asekles\"\nBasic.Stats.Status.Live=\"Srid\"\nBasic.Stats.Status.Reconnecting=\"Tulsa n tuqqna\"\nBasic.Stats.Status.Inactive=\"Yensa\"\nBasic.Stats.Status.Active=\"Yermed\"\nBasic.Stats.DroppedFrames=\"Ikataren iṛuḥen (azeḍḍa)\"\nBasic.Stats.MegabytesSent=\"Aɣrud n yisefka n tuffɣa\"\nBasic.Stats.Bitrate=\"Aktum\"\nBasic.Stats.DiskFullIn=\"Aḍebsi ad yaččar di (tasmekta n)\"\nBasic.Stats.ResetStats=\"Ales awennez n tddadanin\"\nResetUIWarning.Title=\"D tidet tebɣiḍ ad talseḍ awennez n ugrudem n useqdac?\"\nUpdater.Title=\"Yewjed lqem amaynut\"\nUpdater.Text=\"Yella lqem amaynut iwejden:\"\nUpdater.UpdateNow=\"Leqqem Tura\"\nUpdater.RemindMeLater=\"Smekti-yi-d areḍqal\"\nUpdater.Skip=\"Ttu lqem\"\nUpdater.NoUpdatesAvailable.Title=\"Ulac ileqman iwejden\"\nUpdater.NoUpdatesAvailable.Text=\"Ulac ileqman iwejden akka tura\"\nUpdater.FailedToLaunch=\"Ulamek asekker n uleqqem\"\nQuickTransitions.DuplicateScene=\"Sisleg asayes\"\nQuickTransitions.EditProperties=\"Sisleg iɣbula\"\nQuickTransitions.HotkeyName=\"Asaka arurad: %1\"\nBasic.TransitionProperties=\"Iraten n usaka\"\nBasic.SceneTransitions=\"Asaka n yisuyas\"\nBasic.TransitionDuration=\"Tanzagt\"\nBasic.TogglePreviewProgramMode=\"Askar n ustudyu\"\nUndo.Undo=\"Sefsex\"\nUndo.Redo=\"Uɣal\"\nUndo.Add=\"Rnu '%1'\"\nUndo.Delete=\"kkes '%1'\"\nUndo.SceneCollection.Switch=\"Senfel ɣer '%1'\"\nTransitionNameDlg.Text=\"Ma ulac uɣilif sekcem isem i usayes\"\nTransitionNameDlg.Title=\"Isem n usaka\"\nTitleBar.Profile=\"Amaɣnu\"\nTitleBar.Scenes=\"Isuyas\"\nNameExists.Title=\"Isem-agi yella yakan\"\nNameExists.Text=\"Isem-agi yettwaseqdec yakan.\"\nNoNameEntered.Title=\"Ma ulac uɣilif, sekcem isem ameɣtu\"\nNoNameEntered.Text=\"Ur tezmireḍ ara ad tesqedceḍ ismawen ilmawen.\"\nConfirmStart.Title=\"Ad tebduḍ asuddem?\"\nConfirmStart.Text=\"D tidet tebɣiḍ ad tebduḍ asuddem?\"\nConfirmStop.Title=\"Ad tḥebseḍ asuddem?\"\nConfirmStop.Text=\"D tidet tebɣiḍ ad tḥebseḍ asuddem?\"\nConfirmStopRecord.Title=\"Ad tḥebseḍ asekles?\"\nConfirmStopRecord.Text=\"D tidet tebɣiḍ ad tḥebseḍ asekles?\"\nConfirmBWTest.Title=\"Sekker akayad n tehri n tesfift?\"\nConfirmRemove.Title=\"Sentem tukksa\"\nConfirmRemove.TextMultiple=\"D tidet tebɣiḍ ad tekkseḍ %1 n yiferdisen?\"\nOutput.StartStreamFailed=\"D awezɣi asekker n usuddem\"\nOutput.StartRecordingFailed=\"D awezɣi asekker n usekles\"\nOutput.StartReplayFailed=\"Ulamek asekker n tallunt nweḥraz n tulsa n tɣuri\"\nOutput.ConnectFail.Title=\"Ulamek tuqqna\"\nOutput.ConnectFail.BadPath=\"Abrid neɣ URL n tuqqna d armeɣtu. Ma ulac uɣilif selken iɣewwaren-inek m akken ad tsentmeḍ belli d imeɣta.\"\nOutput.ConnectFail.ConnectFailed=\"D awezɣi tuqqna s aqedac\"\nOutput.ConnectFail.InvalidStream=\"D awezɣi adduf ɣer ubadu neɣ ɣer tsarut n usuddem, ma ulac uɣilif selken tasarut n usuddem-inek m. Ma d tameɣtut, yezmer ad yili wugur di tuqqna s aqeddac.\"\nOutput.ConnectFail.Error=\"Teḍra-d tuccḍa ur netturaǧu ara mi neɛreḍ ad nqqen s aqeddac. Ugar n telɣut deg ufaylu n uɣmis.\"\nOutput.ConnectFail.Disconnected=\"Yenser seg uqeddac.\"\nOutput.StreamEncodeError.Title=\"Tuccḍa n usettengel\"\nOutput.StreamEncodeError.Msg=\"Asettengal yemlal-d tuccḍa deg usuddem.\"\nOutput.RecordFail.Title=\"D awezɣi asekker n usekles\"\nOutput.RecordFail.Unsupported=\"Amasal n tuffɣa ur yettwadhel ara neɣ ur yedhil ara ugar n yiwet n tezribt tameslawt. Ma ulac uɣilif selken iɣewwaren-ik m sakin eɛreḍ tikkelt-nniḍen.\"\nOutput.RecordNoSpace.Title=\"Ulac ddeqs n tallunt n uḍebsi\"\nOutput.RecordNoSpace.Msg=\"Ulac ddeqs n tallunt n uḍebsi akken ad ikemmel usekles.\"\nOutput.RecordError.Title=\"Tuccḍa n usekles\"\nOutput.RecordError.Msg=\"Teḍra-d tucḍa deg usekles.\"\nOutput.RecordError.EncodeErrorMsg=\"Asettengal yemlal-d tuccḍa deg usekles.\"\nOutput.BadPath.Title=\"Yir abrid n ufaylu\"\nRemux.SourceFile=\"Asekles n OBS\"\nRemux.TargetFile=\"Afaylu anican\"\nRemux.Remux=\"Selket\"\nRemux.Stop=\"Ḥebes aselket\"\nRemux.ClearFinished=\"Sfeḍ iferdisen ifukken\"\nRemux.ClearAll=\"Sfeḍ akk iferdisen\"\nRemux.OBSRecording=\"Asekles n OBS\"\nRemux.SelectRecording=\"Fren asekles n OBS...\"\nRemux.SelectTarget=\"Fren afaylu anican...\"\nRemux.FileExistsTitle=\"Afaylu anican yella\"\nRemux.FileExists=\"Ifuyla-agi inicanen llan yakan. Tebɣiḍ ad ten-tsemselsiḍ?\"\nRemux.HelpText=\"Sers ifuyla deg usfaylu-agi akken ad ten-tselkteḍ, neɣ fren tabniqt \\\"Asekles OBS\\\" akken ad tnadiḍ afaylu.\"\nMissingFiles.NewFile=\"Afaylu amaynut\"\nBasic.DesktopDevice1=\"Ameslaw n tnarit\"\nBasic.DesktopDevice2=\"Ameslaw n tnarit 2\"\nBasic.Scene=\"Asayes\"\nBasic.DisplayCapture=\"Tuṭṭfa n ubeqqeḍ\"\nBasic.Main.PreviewConextMenu.Enable=\"Sermed tadlayt\"\nBasic.Main.Preview.Disable=\"Sens tadlayt\"\nScaleFiltering.Point=\"Tanqiḍt\"\nScaleFiltering.Area=\"Taɣzut\"\nBlendingMethod.Default=\"Amezwer\"\nDeinterlacing.TopFieldFirst=\"Urti n ufella d amezwaru\"\nVolControl.SliderUnmuted=\"Taḥnaccaḍt n ubleɣ i '%1':\"\nVolControl.SliderMuted=\"Taḥnaccaḍt n ubleɣ i '%1': (akka tura yessusem)\"\nVolControl.Mute=\"'%1' d asusam\"\nVolControl.Properties=\"Iraten i '%1'\"\nBasic.Main.AddSceneDlg.Title=\"Rnu asayes\"\nBasic.Main.AddSceneDlg.Text=\"Sekcem isem n usayes\"\nBasic.Main.DefaultSceneName.Text=\"Asayes %1\"\nBasic.Main.AddSceneCollection.Title=\"Rnu talkensit n yisuyas\"\nBasic.Main.AddSceneCollection.Text=\"Ma ulac uɣilif sekcem isem i telkensit n yisuyas\"\nBasic.Main.RenameSceneCollection.Title=\"Snifel isem n telkensit n yisuyas\"\nAddProfile.Title=\"Rnu amaɣnu\"\nAddProfile.Text=\"Ma ulac uɣilif sekcem isem i umaɣnu\"\nAddProfile.WizardCheckbox=\"Sken amallal n uswel awurman\"\nRenameProfile.Title=\"Snifel isem n umaɣnu\"\nBasic.Main.MixerRename.Title=\"Snifel isem n uɣbalu ameslaw\"\nBasic.Main.MixerRename.Text=\"Ma ulac uɣilif sekcem isem i uɣbalu ameslaw\"\nBasic.Main.PreviewDisabled=\"Tadlayt tensa akka tura\"\nBasic.SourceSelect=\"Snulfu-d/fren aɣbalu\"\nBasic.SourceSelect.CreateNew=\"Snulfu-d amaynut\"\nBasic.SourceSelect.AddExisting=\"Rnu aɣbalu yellan\"\nBasic.SourceSelect.AddVisible=\"Err aɣbalu ad yettban\"\nBasic.Main.Sources.Visibility=\"Tawalit\"\nBasic.Main.Sources.VisibilityDescription=\"Ad isenqed tawalit n '%1' di teɣzut n usuneɣ\"\nBasic.Main.Sources.Lock=\"Sekkeṛ\"\nBasic.PropertiesWindow=\"Iraten i '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (tafrayt tawurmant: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Fren ini\"\nBasic.PropertiesWindow.SelectFont=\"Fren tasefsit\"\nBasic.PropertiesWindow.ConfirmTitle=\"Iɣewwaren ttwabeddelen\"\nBasic.PropertiesWindow.Confirm=\"Llan yibeddilen ur yettwaskelsen ara. Tebɣiḍ ad ten-teskelseḍ?\"\nBasic.PropertiesWindow.NoProperties=\"Ulac iraten iwejden\"\nBasic.PropertiesWindow.AddFiles=\"Rnu ifuyla\"\nBasic.PropertiesWindow.AddDir=\"Rnu akaram\"\nBasic.PropertiesWindow.AddURL=\"Rnu abrid/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Rnu akaram ɣer '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Rnu ifuyla ɣer '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Rnu anekcum ɣer '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Ẓreg anekcum si %1\"\nBasic.PropertiesView.FPS.Simple=\"Azalen n FPS iḥerfiyen\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Tigrummiwin FPS timeɣta:\"\nBasic.InteractionWindow=\"Myeg akked '%1'\"\nBasic.StatusBar.Reconnecting=\"Tegzem tuqqna, tulsa n tuqqna di %2 n tasinit (tasinin) (aneɛruḍ%1)\"\nBasic.StatusBar.AttemptingReconnect=\"Aneɛruḍ n tulsa n tuqqna...(aneɛruḍ %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Tulsa n tuqqna tedda akken iwata\"\nBasic.StatusBar.Delay=\"Asemmezger (%1 tasint)\"\nBasic.StatusBar.DelayStartingIn=\"Asemmezger (beddu di %1 tasint)\"\nBasic.StatusBar.DelayStoppingIn=\"Asemmezger (aḥbas di %1 tasint)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Asemmezger (aḥbas di %1 tasint, beddu di %2 tasint)\"\nBasic.Filters=\"Imzizdigen\"\nBasic.Filters.AsyncFilters=\"Imzizedgen ameslaw/avidyu\"\nBasic.Filters.AudioFilters=\"Imzizedgen n umeslaw\"\nBasic.Filters.EffectFilters=\"Imzizedgen n usemdu\"\nBasic.Filters.Title=\"Imzizedgen i '%1'\"\nBasic.Filters.AddFilter.Title=\"Isem n umzizdeg\"\nBasic.Filters.AddFilter.Text=\"Ma ulac uɣilif sekcem isem i umzizdeg\"\nBasic.TransformWindow=\"Abeddel n uferdis n usayes\"\nBasic.TransformWindow.Position=\"Ideg\"\nBasic.TransformWindow.Rotation=\"Tuzzya\"\nBasic.TransformWindow.Size=\"Tiddi\"\nBasic.TransformWindow.Crop=\"Ɣeẓ\"\nBasic.TransformWindow.Alignment.TopLeft=\"Ukessawen s azelmaḍ\"\nBasic.TransformWindow.Alignment.TopCenter=\"Ukessawen di tlemmast\"\nBasic.TransformWindow.Alignment.TopRight=\"Uksawen s ayfus\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Di tlemmast s azelmaḍ\"\nBasic.TransformWindow.Alignment.Center=\"Alemmas\"\nBasic.TransformWindow.Alignment.CenterRight=\"Di tlemmast s ayfus\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Ukessar s azelmaḍ\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Ukessar di tlemmast\"\nBasic.TransformWindow.Alignment.BottomRight=\"Ukessar s ayfus\"\nBasic.TransformWindow.BoundsType.None=\"War azizdew\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Tiddi tafellayt kan\"\nBasic.TransformWindow.BoundsType.Stretch=\"Snejbad ɣer yiran\"\nBasic.Main.AddSourceHelp.Title=\"Ulamek timerna n usayes\"\nBasic.Main.AddSourceHelp.Text=\"Isefk ad tesɛuḍ ma drus 1 n usayes akken ad ternuḍ aɣbalu.\"\nBasic.Main.Scenes=\"Isuyas\"\nBasic.Main.Sources=\"Iɣbula\"\nBasic.Main.Controls=\"Isenqaden\"\nBasic.Main.Connecting=\"Ala yetteqqen...\"\nBasic.Main.StartRecording=\"Bdu asekles\"\nBasic.Main.StartReplayBuffer=\"Sekker taɣuri n tallunt n weḥraz\"\nBasic.Main.SaveReplay=\"Sekles tulsa n tɣuri\"\nBasic.Main.StartStreaming=\"Bdu asuddem\"\nBasic.Main.StartVirtualCam=\"Sekker takamiṛat tuhlist\"\nBasic.Main.StopRecording=\"Ḥbes asekles\"\nBasic.Main.PauseRecording=\"Sbedd asekles\"\nBasic.Main.UnpauseRecording=\"Kemmel asekles\"\nBasic.Main.StoppingRecording=\"Aseḥbes n usekles...\"\nBasic.Main.StopReplayBuffer=\"Seḥbes taɣuri n tallunt n weḥraz\"\nBasic.Main.StoppingReplayBuffer=\"Aseḥbes taɣuri n tallunt n weḥraz...\"\nBasic.Main.StopStreaming=\"Ḥbes asuddem\"\nBasic.Main.StoppingStreaming=\"Aḥbas n usuddem...\"\nBasic.Main.ForceStopStreaming=\"Seḥbes asuddem (sefsex asemmezger)\"\nBasic.Main.ShowContextBar=\"Beqqeḍ afeggag n yifecka Iɣbula\"\nBasic.Main.HideContextBar=\"Ffer afeggag n yifecka Iɣbula\"\nBasic.Main.StopVirtualCam=\"Ḥbes takamirat tuhlist\"\nBasic.Main.Group=\"Agraw %1\"\nBasic.Main.GroupItems=\"Segrew iferdisen yettwafernen\"\nBasic.Main.Ungroup=\"Freq\"\nBasic.Main.GridMode=\"Askar n uferrug\"\nBasic.Main.ListMode=\"Askar n tebdart\"\nBasic.MainMenu.File=\"A&faylu\"\nBasic.MainMenu.File.Export=\"Sif&eḍ\"\nBasic.MainMenu.File.Import=\"Kter (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Beqqeḍ ikalasen (&R)\"\nBasic.MainMenu.File.Settings=\"Iɣewwaren (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Beqqeḍ akaram n yiɣewwaren\"\nBasic.MainMenu.File.ShowProfileFolder=\"Beqqeḍ akaram n umaɣnu\"\nBasic.MainMenu.File.Exit=\"Ffeɣ (&X)\"\nBasic.MainMenu.Edit=\"Ẓr&eg\"\nBasic.MainMenu.Edit.Undo=\"Sefsex (&U)\"\nBasic.MainMenu.Edit.Redo=\"E&rr-d\"\nBasic.MainMenu.Edit.LockPreview=\"Sekkeṛ tad&layt\"\nBasic.MainMenu.Edit.Scale.Window=\"Sezg ɣer usfaylu\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Taɣzut n usuneɣ (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Tuffɣa (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"Selke&t\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Ẓr&eg aselket...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Nɣel aselket\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Senṭeḍ aselket\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Ales awennez n uselket (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Zzi s 90 n tfesniwin di tnila n temrilt\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Zzi s 90 n tfesniwin mgal tamrilt\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Zzi s 180 n tfesniwin\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Tuttya taglawant (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Tuttya tubdidt (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Sezg ɣer ugdil (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&Snejbad ɣer ugdil\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Di tlemmast n wegdil (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Di tlemmast s ibeddi\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Di tlemmast s uglawi\"\nBasic.MainMenu.Edit.Order=\"Amizzwer (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Sm&utti d asawen\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Smutti &d akessar\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Smu&tti d asawen maḍi\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Smutti d akessar (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Ir&aten inaẓiyen n uvidyu\"\nBasic.MainMenu.View=\"Tamuɣli (&V)\"\nBasic.MainMenu.View.Toolbars=\"Afeggag n yifecka (&T)\"\nBasic.MainMenu.View.ContextBar=\"Afeggag n yifecka Iɣbula\"\nBasic.MainMenu.View.SourceIcons=\"T&ignitin n uɣbalu\"\nBasic.MainMenu.View.StatusBar=\"Afeggag n waddad (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Agrudem n ugdil aččuran\"\nBasic.MainMenu.SceneCollection=\"Talken&sit n yisuyas\"\nBasic.MainMenu.Profile=\"Amaɣnu (&P)\"\nBasic.MainMenu.Profile.Import=\"Kter amaɣnu\"\nBasic.MainMenu.Profile.Export=\"Sifeḍ amaɣnu\"\nBasic.MainMenu.SceneCollection.Import=\"Kter talkensit n yisuyas\"\nBasic.MainMenu.SceneCollection.Export=\"Sifeḍ talkensit n yisuyas\"\nBasic.MainMenu.Profile.Exists=\"Amaɣnu yella yakan\"\nBasic.MainMenu.Tools=\"Ifecka (&T)\"\nBasic.MainMenu.Help=\"Tallelt (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Tawwurt n tallelt (&P)\"\nBasic.MainMenu.Help.Website=\"Rzu ar usmel &web\"\nBasic.MainMenu.Help.Discord=\"Rnu ɣer uqe&ddac Discord\"\nBasic.MainMenu.Help.Logs=\"Ifuy&la n uɣmis\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Beqqeḍ ifuyla n uɣmi&s\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Sili afaylu n uɣmis amiran (&C)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Beqqeḍ aɣmis amiran (&V)\"\nBasic.MainMenu.Help.CheckForUpdates=\"Nadi ileqman\"\nBasic.MainMenu.Help.CrashLogs=\"Ineqqisen n uɣelluy (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Beqqeḍ ineqqi&sen n uɣelluy\"\nBasic.MainMenu.Help.About=\"Ɣef (&A)\"\nBasic.Settings.ProgramRestart=\"Ahil isefk ad yales tanekkra akken ad ddun yiɣewwaren-agi.\"\nBasic.Settings.ConfirmTitle=\"Sentem ibeddilen\"\nBasic.Settings.Confirm=\"Tesɛiḍ ibeddilen ur nettwasekles ara. Ad teskelseḍ ibeddilen?\"\nBasic.Settings.General=\"Amatu\"\nBasic.Settings.General.Language=\"Tutlayt\"\nBasic.Settings.General.EnableAutoUpdates=\"Nadi ileqman s wudem awurman di tnekra\"\nBasic.Settings.General.OpenStatsOnStartup=\"Ldi adiwenni n tddadanin deg usekker\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Beqqeḍ adiwenni n usentem mi ara yekker usuddem\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Beqqeḍ adiwenni n usentem mi ara yeḥbes usuddem\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Beqqeḍ adiwenni n usentem mi ara yeḥbes usekles\"\nBasic.Settings.General.Projectors=\"Imagaren\"\nBasic.Settings.General.HideProjectorCursor=\"Ffer taḥnaccaḍt ɣef yimagaren\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Yals ttarra imagaren nnig\"\nBasic.Settings.General.RecordWhenStreaming=\"Sekles s wudem awurman mi ara yetteddu usuddem\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Kemmel asekles mi ara yeḥbes usuddem\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Sekker s wudem awurman tallunt n weḥraz n tulsa n tɣuri mi ara yetteddu usuddem\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Eǧǧ tallunt n wephraz n tulsa n tɣuri termed mi ara yeḥbes usuddem\"\nBasic.Settings.General.SysTray=\"Taɣzut n telɣa\"\nBasic.Settings.General.SysTrayWhenStarted=\"Simẓi ɣer teɣzut n telɣa di tnekra\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Yalas ssimẓi ɣer teɣzut n telɣa deg umɣiq n ufeggag n tmahilin\"\nBasic.Settings.General.SaveProjectors=\"Sekles imagaren mi ara teffɣeḍ\"\nBasic.Settings.General.Preview=\"Tadlayt\"\nBasic.Settings.General.OverflowHidden=\"Ffer tafuli\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Taful tettban yalas\"\nBasic.Settings.General.OverflowSelectionHidden=\"Sken tafuli ɣas ma aɣbalu ur yettban ara\"\nBasic.Settings.General.Importers=\"Imaktaren\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Nadi imeḍqan yettwassnen n telkensi n isuyas di tektert\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Eg asaka ɣer usayes ma yella ussiti uslig\"\nBasic.Settings.General.StudioPortraitLayout=\"Sermed taneɣruft s tehri/s ibeddi\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Beqqeḍ tibzimin tamuɣli/ahil\"\nBasic.Settings.General.Multiview=\"Aṭas n tmuɣliwin\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Ssit akken ad tbedeleḍ gar yisuyas\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Beqqeḍ ismawen n yisuyas\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Suneɣ tiɣezza iḍemnen (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Taneɣruft n waṭas n tmuɣliwin\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Aglawan, Afella (8 n yisuyas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Aglawan, Adda (8 n yisuyas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Ubdid, Azelmaḍ (8 n yisuyas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Ubdid, Ayfus (8 n yisuyas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Aglawan, Afella (24 n yisuyas)\"\nBasic.Settings.Stream=\"Asuddem\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Seqdec asesteb\"\nBasic.Settings.Stream.Custom.Username=\"Isem n useqdac\"\nBasic.Settings.Stream.Custom.Password=\"Awal uffir\"\nBasic.Settings.Stream.BandwidthTestMode=\"Sermed askar n usekyed n tehri n tesfift\"\nBasic.Settings.Stream.TTVAddon.None=\"Ulac\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV akked FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Txuṣ twila n usuddem\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Ldi iɣewwaren\"\nBasic.Settings.Stream.MissingUrl=\"URL n usuddem ixuṣ.\\n\\nLdi iɣewwaren akken ad tsekcmeḍ URL deg yiccer 'Asuddem'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Tasarut n usuddem txuṣ.\\n\\nLdi iɣewwaren akken ad tsekcmeḍ tasarut deg yiccer 'Asuddem'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ttu isemtar n umeẓlu n usuddem\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Semselsi iɣewwaren yettwasmetren\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Aktum afellay n uvidyu: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Aktum afellay n umeslaw: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Tabadut tafellayt: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"FPS (ikataren i tasint) afellay: %1\"\nBasic.Settings.Output=\"Tuffɣa\"\nBasic.Settings.Output.Format=\"Amasal n usekles\"\nBasic.Settings.Output.SelectDirectory=\"Fren akaram n usekles\"\nBasic.Settings.Output.Mode=\"Askar n tuffɣa\"\nBasic.Settings.Output.Mode.Simple=\"Aḥerfi\"\nBasic.Settings.Output.Mode.Adv=\"Anaẓi\"\nBasic.Settings.Output.Mode.FFmpeg=\"Tuffɣa n FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Sermed tallunt n weḥraz n tulsa n tɣuri\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Tanzagt tafellayt n tulsa n tɣuri\"\nBasic.Settings.Output.Simple.SavePath=\"Abrid n usekles\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Taɣara n usekles\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Kifkif akked usuddem\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Taɣara tafellayt, tiddi n ukaram talemmast\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"D tidet tebɣiḍ ad tesqedceḍ taɣara war asṛuḥu?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Alɣu ɣef tɣara war asṛuḥu!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Aseɣẓan (x264)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Aseɣẓan (x264 azwirɣewwar n useqdec amecṭuḥ n CPU, ad isimɣur tiddi n ufaylu)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Tazribt n Twitch VOD (Isseqdac tazribt 2)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Tabadut/Framerate ur nemṣada ara\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Tabadut: %1\"\nBasic.Settings.Output.VideoBitrate=\"Aktum n uvidyu\"\nBasic.Settings.Output.AudioBitrate=\"Aktum n umeslaw\"\nBasic.Settings.Output.Reconnect=\"Tulsa n tuqqna s wudem awurman\"\nBasic.Settings.Output.RetryDelay=\"Amenḍar send aneɛruḍ amaynut\"\nBasic.Settings.Output.MaxRetries=\"Amḍan afellay n yineɛruḍen\"\nBasic.Settings.Output.EncoderPreset=\"Azwirɣewwar n usettengel\"\nBasic.Settings.Output.CustomEncoderSettings=\"Iɣewwaren yugnen n usettengal\"\nBasic.Settings.Output.NoSpaceFileName=\"Sirew isem n ufaylu war tallunt\"\nBasic.Settings.Output.Adv.AudioTrack=\"Tazribt n umeslaw\"\nBasic.Settings.Output.Adv.Streaming=\"Asuddem\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Tazribt 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Tazribt 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Tazribt 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Tazribt 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Tazribt 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Tazribt 6\"\nBasic.Settings.Output.Adv.Recording=\"Asekles\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Anaw n usekles\"\nBasic.Settings.Output.Adv.Recording.Type=\"Anaw\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Aslugan\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Tuffɣa yugnen (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Seqdec asettengal n usuddem)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Amasal n yisem n ufaylu\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Sefsex afaylu yellan\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Anaw n tuffɣa n FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Tuffɣa ɣer URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Tuffɣa ɣer Ufaylu\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Akk ifuyla\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Abrid n ufaylu neɣ URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Amasal n unagbar\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Ameslaw\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Avidyu\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Amsal n lexṣas\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Aglam n umasal n lexṣas\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Akudak Ameslaw/avidyu yettwaf-d seg ubrid neɣ URL n ufaylu\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Asettengal n lexṣas\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Sens asettengal\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Asettengal n uvidyu\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Iɣewwaren n usettengal n uvidyu (ma yella)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Asettengal n umeslaw\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Iɣewwaren n usettengal n umeslaw(ma yella)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Azilal n yikataren yufraren (ikataren)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Beqqeḍ akk ikudaken (ɣas ma zemren ad ilin ur mṣadan ara)\"\nScreenshot=\"Tuṭṭfa n ugdil n tuffɣa\"\nScreenshot.SourceHotkey=\"Tuṭṭfa n ugdil n uɣbalu yettwafernen\"\nScreenshot.StudioProgram=\"Tuṭṭfa n ugdil (ahil)\"\nScreenshot.Preview=\"Tuṭṭfa n ugdil (tadlayt)\"\nScreenshot.Scene=\"Tuṭṭfa n ugdil (asayes)\"\nScreenshot.Source=\"Tuṭṭfa n ugdil (aɣbalu)\"\nBasic.Settings.Video=\"Avidyu\"\nBasic.Settings.Video.BaseResolution=\"Tabadut tanaddayt (taɣzut n usuneɣ)\"\nBasic.Settings.Video.InvalidResolution=\"Azal n tbadut d armeɣtu. Isefk ad yili [width]x[height] (i.e. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Tuffɣa n uvidyu termed akka tura. Ma ulac uɣilif sens kra n tuffɣiwin akken ad tbeddeleḍ iɣewwaren n uvidyu.\"\nBasic.Settings.Audio=\"Ameslaw\"\nBasic.Settings.Audio.SampleRate=\"Atug n ulemmec\"\nBasic.Settings.Audio.Channels=\"Ibuda\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Arurad\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Alemmas (Anaw I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Ẓẓay (Anaw II PPM)\"\nBasic.Settings.Audio.Devices=\"Ibenkan n umeslaw ijemmalen\"\nBasic.Settings.Audio.DesktopDevice=\"Ameslaw n tnarit\"\nBasic.Settings.Audio.DesktopDevice2=\"Ameslaw n tnarit 2\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Ibenk ur yeqqin ara neɣ ur yewjid ara]\"\nBasic.Settings.Audio.Disabled=\"Yensa\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Amezwer\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Sagen\"\nBasic.Settings.Advanced=\"Anaẓi\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Afellay\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Nnig n umagnu\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Amagnu\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Ddaw n umagnu\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Amasal n yini\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Tallunt n yini\"\nBasic.Settings.Advanced.Video.ColorRange=\"Tagrumma n yini\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Ummid\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Amezwar\"\nBasic.Settings.Advanced.StreamDelay=\"Asemmezger n usuddem\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Tanzagt\"\nBasic.Settings.Advanced.Network=\"Azeḍḍa\"\nBasic.Settings.Advanced.Network.BindToIP=\"Qqen ɣer IP\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Werǧin ad tsenseḍ inegzumen n unasiw\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Sens inegzumen n unasiw ticki yella usaḍes ɣef usfaylu agejdan\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Sens inegzumen n unasiw ticki ulac asaḍes ɣef usfaylu agejdan\"\nBasic.AdvAudio=\"Iraten inaẓiyen n uvidyu\"\nBasic.AdvAudio.ActiveOnly=\"Iɣbula urmiden kan\"\nBasic.AdvAudio.Name=\"Isem\"\nBasic.AdvAudio.Volume=\"Ableɣ\"\nBasic.AdvAudio.VolumeSource=\"Ableɣ i '%1'\"\nBasic.AdvAudio.Balance=\"Awliwal\"\nBasic.AdvAudio.BalanceSource=\"Awliwal i '%1'\"\nBasic.AdvAudio.SyncOffset=\"Asekḥer n umtawi\"\nBasic.AdvAudio.SyncOffsetSource=\"Asekḥer n umtawi i '%1'\"\nBasic.AdvAudio.AudioTracks=\"Tizribin\"\nBasic.Settings.Hotkeys=\"Inegzumen n unasiw\"\nBasic.Settings.Hotkeys.Filter=\"Imzizdeg\"\nBasic.Hotkeys.SelectScene=\"Ɛeddi ɣer usayes\"\nBasic.SystemTray.Show=\"Sken\"\nBasic.SystemTray.Hide=\"Ffer\"\nBasic.SystemTray.Message.Reconnecting=\"Yenser. Ala yettales tuqqna...\"\nHotkeys.Insert=\"Ger\"\nHotkeys.Delete=\"Kkes\"\nHotkeys.Home=\"Agejdan\"\nHotkeys.End=\"Taggara\"\nHotkeys.PageUp=\"Asebter d asawen\"\nHotkeys.PageDown=\"Asebter d akessar\"\nHotkeys.ScrollLock=\"Sekkeṛ adrurem\"\nHotkeys.Print=\"Siggez\"\nHotkeys.Pause=\"Bedd\"\nHotkeys.Left=\"Azelmaḍ\"\nHotkeys.Right=\"Ayfus\"\nHotkeys.Up=\"Asawen\"\nHotkeys.Down=\"Akessar\"\nHotkeys.Windows=\"Asfaylu\"\nHotkeys.Menu=\"Umuɣ\"\nHotkeys.Space=\"Tallunt\"\nHotkeys.MouseButton=\"Amumed %1\"\nMute=\"Sens imesli\"\nUnmute=\"Sermed imesli\"\nSceneItemShow=\"Beqqeḍ '%1'\"\nSceneItemHide=\"Ffer '%1'\"\nOutputWarnings.NoTracksSelected=\"Isefk ad tferneḍ ma drus yiwet n tezribt\"\nFinalScene.Title=\"Kkes asayes\"\nFinalScene.Text=\"Isefk ad yili ma drus yiwen n usayes.\"\nNoSources.Title=\"Ulac iɣbula\"\nNoSources.Text=\"Yettban-d am wakken ur terniḍ ara yakan kra n yiɣbula n uvidyu, ihi d agdil aberkan ara tsuffɣeḍ. D tidet tebɣiḍ ad tgeḍ ayagi?\"\nNoSources.Text.AddSource=\"Tzemreḍ ad ternuḍ iɣbula s usiti ɣef tignit + ddaw n tenkult n yiɣbula deg usfaylu agejdan di yal taswiɛt.\"\nNoSources.Label=\"Ur tesɛiḍ ara yakan iɣbula.\\nSsit ɣef tqeffalt + ukessar-agi,\\nneɣ ssit s tqeffalt tayfust dagi akken ad ternuḍ yiwen.\"\nChangeBG=\"Sbadu ini\"\nCustomColor=\"Ini yugnen\"\nAbout=\"Ɣef\"\nAbout.Info=\"OBS Studio d aseɣẓan ilelli n uɣbalu yeldin (open source) i wesekles n uvidyu akked usuddem s wakud ilaw.\"\nAbout.Donate=\"Ɛiwen asenfaṛ\"\nAbout.GetInvolved=\"Ttekki\"\nAbout.Authors=\"Imeskaren\"\nAbout.License=\"Turagt\"\nAbout.Contribute=\"Dhel asenfar n OBS\"\nAddUrl.Title=\"Rnu aɣbali si URL\"\nAddUrl.Text=\"Tzuɣreḍ URL ɣer OBS. Ayagi ad yernu s wudem awurman aseɣwen am uɣbalu. Ad tkemmeleḍ?\"\nPreviewTransition=\"Wali asaka\"\nImporter=\"Amektar n telkensiyin n yisuyas\"\nImporter.SelectCollection=\"Fren talkensit n yisuyas\"\nImporter.Collection=\"Talkensit n yisuyas\"\nImporter.HelpText=\"Rnu ifuyla ɣer usfaylu-agi akken ad tketreḍ tilkensitin si OBS neɣ ihalen-nniḍen yettwadehlen.\"\nImporter.Path=\"Abrid n telkensit\"\nImporter.Program=\"Yettwaf usnas\"\nImporter.AutomaticCollectionPrompt=\"Nadi s wudem awurman ɣef telkensiyin n yisuyas\"\nImporter.AutomaticCollectionText=\"OBS yezmer ad inadi s wudem awurman ɣef telkensiyin n yisuyas deg yihalen n wis kraḍ yettwadehlen. Tebɣiḍ OBS ad inadi s wudem awurman?\\n\\nTzemreḍ ad tbeddeleḍ areḍqal ayagi di Iɣewwaren >Amatu Imaktaren.\"\nRestart=\"Ales asekker\"\nNeedsRestart=\"OBS Studio yesra ad yales tanekra. Tebɣiḍ ad talseḍ asekker tura?\"\nContextBar.NoSelectedSource=\"Ulac aɣbalu yettwafernen\"\nContextBar.MediaControls.PlayMedia=\"Ɣer amidya\"\nContextBar.MediaControls.PauseMedia=\"Ḥbes amidya\"\nContextBar.MediaControls.StopMedia=\"Seḥbes amidya\"\nContextBar.MediaControls.RestartMedia=\"Ales asekker n umidya\"\nContextBar.MediaControls.PlaylistNext=\"Uḍfir di tebdart n tɣuri\"\nContextBar.MediaControls.PlaylistPrevious=\"D uzwir di tebdart n tɣuri\"\nContextBar.MediaControls.BlindSeek=\"Awiǧit n tunigin deg umidya\"\nYouTube.Actions.Title=\"Azwel*\"\nYouTube.Actions.Description=\"Aglam\"\nYouTube.Actions.Privacy=\"Tabaḍnit*\"\nYouTube.Actions.Privacy.Private=\"Uslig\"\nYouTube.Actions.Privacy.Public=\"Azayaz\"\nYouTube.Actions.Category=\"Taggayt\"\nYouTube.Actions.Thumbnail=\"Tanfult\"\nYouTube.Actions.Thumbnail.SelectFile=\"Fren afaylu...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Ulac afaylu i yettwafernen\"\nYouTube.Actions.Thumbnail.ClearFile=\"Sfeḍ\"\nYouTube.Actions.Latency.Normal=\"Amagnu\"\nYouTube.Actions.Latency.Low=\"Yuder\"\nYouTube.Actions.360Video=\"360 tvidyutin\"\nYouTube.Actions.Stream=\"Asuddem\"\n"
  },
  {
    "path": "frontend/data/locale/kmr-TR.ini",
    "content": "Language=\"Kurdî\"\nOK=\"BAŞ E\"\nApply=\"Bisepîne\"\nCancel=\"Dev jê berde\"\nClose=\"Bigire\"\nSave=\"Tomar bike\"\nDiscard=\"Biavêje\"\nDisable=\"Neçalak bike\"\nYes=\"Erê\"\nNo=\"Na\"\nAdd=\"Tevlî bike\"\nRemove=\"Rake\"\nRename=\"Ji nû ve nav lê bike\"\nInteract=\"Bandor\"\nFilters=\"Parzûn\"\nProperties=\"Taybetmendî\"\nMoveUp=\"Bibe jor\"\nMoveDown=\"Bibe jêr\"\nSettings=\"Sazkarî\"\nDisplay=\"Dîmender\"\nName=\"Nav\"\nExit=\"Derkeve\"\nMixer=\"Tevlihevkera dengê\"\nBrowse=\"Bigere\"\nDroppedFrames=\"Ketina çarçoveyan %1 (%2%)\"\nProjector.Open.Program=\"Projektora bernameyê veke\"\nProjector.Open.Preview=\"Projektora pêşdîtinê veke\"\nProjector.Open.Scene=\"Projektorê dîmenê veke\"\nProjector.Open.Source=\"Projektorê çavkaniyê veke\"\nProjector.Display=\"Dîmender: %1\"\nProjector.Window=\"Çarçoveya nû\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Dîmen: %1\"\nProjector.Title.Source=\"Çavkanî: %1\"\nProjector.Title.Multiview=\"Pirdîmen\"\nProjector.ResizeWindowToContent=\"Çarçoveyê li naverokê guncav bike\"\nClear=\"Pak bike\"\nRevert=\"Vegerîne rewşa berê\"\nShow=\"Nîşan bide\"\nHide=\"Veşêre\"\nUnhideAll=\"Hemûyan veneşêre\"\nUntitled=\"Bê nav\"\nNew=\"Nû\"\nDuplicate=\"Dûbare bike\"\nEnable=\"Çalak bike\"\nDisableOSXVSync=\"macOS V-Sync neçalak bike\"\nResetOSXVSyncOnExit=\"macOS V-Sync ji nû ve saz bike li ser derketinê\"\nHighResourceUsage=\"Kodkirin pir barkirî ye! an sazkariyên vîdyoyê daxîne an jî pêşdanasînek kodkirina zûtir bi kar bîne.\"\nTransition=\"Derbasbûn\"\nQuickTransitions=\"Derbasbûna bilez\"\nFadeToBlack=\"Bo reş wenda dibê\"\nLeft=\"Çep\"\nRight=\"Rast\"\nTop=\"Jor\"\nBottom=\"Jêr\"\nReset=\"Ji nû ve saz bike\"\nHours=\"Demjimêr\"\nMinutes=\"Xulek\"\nSeconds=\"Çirke\"\nDeprecated=\"Paşguhkirî\"\nReplayBuffer=\"Pêşbîra demkî\"\nImport=\"Têxistin\"\nExport=\"Derxistin\"\nCopy=\"Jê bigire\"\nPaste=\"Pêve bike\"\nPasteReference=\"Pêve bike (Çavkanî)\"\nPasteDuplicate=\"Pêve bike (Dûbarekirî)\"\nRemuxRecordings=\"Guhertina formatên tomarkirinê\"\nNext=\"Pêş\"\nBack=\"Paş\"\nDefaults=\"Berdest\"\nRestoreDefaults=\"Berdest\"\nHideMixer=\"Di tevlihevkerê de veşêre\"\nTransitionOverride=\"Borandina derbasbûnê\"\nShowTransition=\"Derbasbûnê nîşan bide\"\nHideTransition=\"Derbasbûnê veşêre\"\nNone=\"Ne yek\"\nStudioMode.Preview=\"Pêşdîtin\"\nStudioMode.Program=\"Bername\"\nStudioMode.PreviewSceneName=\"Pêşdîtina: %1\"\nStudioMode.ProgramSceneName=\"Bername: %1\"\nShowInMultiview=\"Di pirdîmen de nîşan bide\"\nVerticalLayout=\"Rêzkirina stûnî\"\nGroup=\"Kom\"\nDoNotShowAgain=\"Careke din nîşan nede\"\nDefault=\"(Berdest)\"\nCalculating=\"Tê hejmartin...\"\nFullscreen=\"Dîmendera tijî\"\nWindowed=\"Çarçoveyî\"\nRefreshBrowser=\"Nû bike\"\nAspectRatio=\"Rêjeya bilindbûn û pehnbûnê <b>%1:%2</b>\"\nLockVolume=\"Dengê kilît bike\"\nLogViewer=\"Nîşanderê têketinê\"\nShowOnStartup=\"Li ser destpêkirinê nîşan bide\"\nOpenFile=\"Pelê veke\"\nAddScene=\"Dîmenê tevlî bike\"\nAddSource=\"Çavkanî tevlî bike\"\nRemoveScene=\"Dîmena hilbijartî rake\"\nRemoveSource=\"Dîmenên hilbijartî rake\"\nMoveSceneUp=\"Dîmenê bilivîne jor\"\nMoveSceneDown=\"Dîmenê bilivîne jêr\"\nMoveSourceUp=\"Dîmenan bilivîne jor\"\nMoveSourceDown=\"Dîmenan bilivîne jêr\"\nSourceProperties=\"Taybetmendiyên çavkaniya vekirî\"\nSourceFilters=\"Parzûnên çavkaniya vekirî\"\nMixerToolbarMenu=\"Menuya tevlihevkara dengê\"\nSceneFilters=\"Parzûnên çavkaniya vekirî\"\nList=\"Lîste\"\nGrid=\"Tor\"\nAutomatic=\"Xweber\"\nComingSoon=\"Di nêzik de\"\nPluginsFailedToLoad.Title=\"Di barkirina pêvekê de çewtî derket\"\nPluginsFailedToLoad.Text=\"Barkirina pêvekên OBS:\\n\\n%1\\nTêk çû jkx rojane bike an jî van pêvekan jê bibe.\"\nAlreadyRunning.Title=\"OBS jixwe dixebite\"\nAlreadyRunning.Text=\"OBS jixwe dixebite! Ku te nexwest vê yekê bike, ji kerema xwe berî ku tu hewl bidî ku tomarkirinekî nû bixebitnî hemû tomarkirinekê heyî yên OBS-ê bigire. Ku te OBS saz kiriye ku li pêşpirtika pergalê piçûk bibe, ji kerema xwe li wir kontrol bike ka ew hin jî dixebite.\"\nAlreadyRunning.LaunchAnyway=\"Bi her awayî dest pê bike\"\nCrashHandling.Dialog.Title=\"Têkçûna OBS Studio hat dîtin\"\nCrashHandling.Checkbox.SendReport=\"Min polîtika taybetiyê xwendiye û ji bo barkirinê dipejirînim.\"\nCrashHandling.Buttons.LaunchSafe=\"Di moda ewle de bixebitîne\"\nCrashHandling.Buttons.LaunchNormal=\"Di moda normal de bixebitîne\"\nChromeOS.Title=\"Platforma nepiştgirî\"\nChromeOS.Text=\"Xuye ku OBS di hindirê konteynirek ChromeOS de dixebite. Ev platform nayê piştgirîkirin.\"\nWine.Title=\"Wine hate dîtin\"\nWine.Text=\"Di Wine de xebitandina OBS nayê piştgirîkirin, û gelek taybetmendiyên wekî girtina an çavkaniyên amûrê wê nexebitin an jî tenê di kapasîteya sînorkirî de wê bixebitin.<br><br>Tê pêşniyarkirin ku li şûna wê guhertoyek resen a OBS werê xebitandin, Bo mînak <a href='https://flathub.org/apps/details/com.obsproject.Studio'>guhertoya me ya Flatpak</a> an jî pakêtên pergala xebitandina yê te.\"\nDockCloseWarning.Title=\"Girtina çarçoveya Benderî\"\nDockCloseWarning.Text=\"Te çarçoveyeke Benderî girt. Ku tu dixwazî careke din nîşan bidî, ji kulîna Dîtinê → Kulîna Bender di kulîna alavdankê bi kar bîne.\"\nExtraBrowsers=\"Bendera gerokê kesanekirî\"\nExtraBrowsers.Info=\"Benderan tevlî bike bi riya dayîna nav û girêdanekê, paşê bisepîne an jî bigire bitikîne ji bo vekirina Benderê. Tu dikarî di her demê de Benderan tevlî bike an jî rake.\"\nExtraBrowsers.DockName=\"Navê Bender\"\nAuth.Authing.Title=\"Tê rastandin\"\nAuth.Authing.Text=\"Bi %1 re tê rastandin, ji kerema xwe li bendê bimîne...\"\nAuth.AuthFailure.Title=\"Rastandin têkçû\"\nAuth.AuthFailure.Text=\"Rastandin bi %1:\\n\\n%2: %3 re têkçû\"\nAuth.InvalidScope.Title=\"Rastandin pêdivî ye\"\nAuth.InvalidScope.Text=\"Pêdiviyên rastandinê ji bo %1 hatine guhertin. Hinek taybetmendî belkî tune bin.\"\nAuth.LoadingChannel.Title=\"Zanyariyên kanalê tên barkirin...\"\nAuth.LoadingChannel.Text=\"Zaniyariyên kanalê ji bo %1 tê barkirin, ji kerema xwe li bendê be...\"\nAuth.LoadingChannel.Error=\"Nikare zanyariyên kanalê bi dest bixê.\"\nAuth.ChannelFailure.Title=\"Barkirina kanalê têkçû\"\nAuth.ChannelFailure.Text=\"Barkirina zanyariyên kanalê ji bo%1\\n\\n%2: %3 têkçû\"\nAuth.Chat=\"Axaftin\"\nAuth.StreamInfo=\"Zanyariyên weşanê\"\nTwitchAuth.Stats=\"Rewşa Twitch\"\nTwitchAuth.Feed=\"Çalakiya Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Nikarî kilîta weşana jê bipirse\"\nTwitchAuth.TwoFactorFail.Text=\"OBS nikaribû bi ajimêra Twitch-ê ve girê bide. Ji kerema xwe pê ewle be ku pejirandina du-gavî di <a href='https://www.twitch.tv/settings/security'> Sazkariyên ewlehiyê yên Twitch-ê hatiye sazkirin</a> ji ber ku ev pêdivî ye bo weşanê.\"\nRestreamAuth.Channels=\"Kanalên Restream\"\nCopy.Filters=\"Pazrûnan jê bigire\"\nPaste.Filters=\"Pazrûnan pêve bike\"\nBrowserPanelInit.Title=\"Gerok tê destpêkirin...\"\nBrowserPanelInit.Text=\"Gerok tê destpêkirin, ji kerema xwe li bendê bimîne...\"\nBandwidthTest.Region=\"Herêm\"\nBandwidthTest.Region.US=\"Dewletên yekbûyî\"\nBandwidthTest.Region.EU=\"Ewropa\"\nBandwidthTest.Region.Asia=\"Asya\"\nBandwidthTest.Region.Other=\"Yên din\"\nBasic.AutoConfig=\"Pevsazkirina pêkanînê ya bixweber\"\nBasic.AutoConfig.ApplySettings=\"Sazkariyan bisepîne\"\nBasic.AutoConfig.StartPage=\"Zanyariyên bikaranînê\"\nBasic.AutoConfig.StartPage.SubTitle=\"Tu dixwazî bernameyê ji bo çi bi kar bîne diyar bike\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Ji bo weşane çêtir bike, tomarkirin duyemîn e\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Tenê ji bo tomarkirinê çêtir bikin, ez ê weşane nekim\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Ez ê bi tenê kamera ya rastekî bi kar bînim\"\nBasic.AutoConfig.VideoPage=\"Sazkariyên vîdyoyê\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Sazkariyên vîdyoyê yên ku tu dixwazî bi kar bînî diyar bike\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Yê heyî bi kar bîne (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Dîmender %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Yê heyî bi kar bîne (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 an jî 30, lê gava ku gengaz be 60 tercîh bike\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 an jî 30, lê gava ku gengaz be qalîteya bilind tercîh bike\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Nîşe: Çareserkirina taplo (bingeh) ne merc e ku wekî çareseriya ku tu yê pê weşan bike an tomar bike. Dibe ku çareseriya rastîn a weşana/tomarkirinê ji çareseriya destgeh ê were daxistin da ku karanîna çavkaniyê an pêdiyên rêjeya bîtê kêm bike.\"\nBasic.AutoConfig.StreamPage=\"Zanyariyên weşanê\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Ji kerema xwe zaniyariyên weşanê xwe binivîse\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Ajimêrê girê bide (tê pêşniyazkirin)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Girêdanê ajimêr qut bike\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Girêdanê ajimêr qut bike?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Ev guhertin dê tavilê were sepandin. Ma tu dixwazî girêdanê ajimêra xwe qut bikî?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Kilîdê weşanê bistîne\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Bêhtir zanyarî\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Kilîdê weşanê bi kar bîne\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Kilîdê weşanê bi kar bîne (pêşketî)\"\nBasic.AutoConfig.StreamPage.Service=\"Raje\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Hemû nîşan bide\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Kesanekirî\"\nBasic.AutoConfig.StreamPage.Server=\"Rajekar\"\nBasic.AutoConfig.StreamPage.StreamKey=\"kilîdê weşanê\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: borînpeyva şîfrekirinê têxe.\\nRTMP: kilîta ku ji aliyê rajeyên ve hatiye peydakirin têxe.\\nSRT: ku raje yekê bi kar tîne nasnameya weşanê têxe.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kilîdê şîfreker\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Ajimêrên girêdayî\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Pîvandina rêjeya herîkê bi riya karanîna kontrolkirina leza înternetê (dibe ku hin çend xulekan dirêj bike)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Kodkirin bi riya reqalavê tercîh bike\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Kodkirina reqalavê piranîya bikaranîna CPU yê ji holê radike, lê ji bo bidestxistina heman asta kalîteyê dibe ku rêjeya bîtê hîn bêtir be.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Hişyariya weşanê\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Testa pîvandina herîkê wê dest bi weşana daneyan ji vîdyoyek rasthatî ya bê deng ji bo kanala te bikê. ku gengaz be, tê pêşniyarkirin ku tu tomarkirina vîdyoyê zindî rawestînî û nihêniya nîşandana zindî li ser taybetî saz bikî heya ku testkirin diqede. Tu dixwazî bidomînî?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"%1 test bike\"\nBasic.AutoConfig.TestPage=\"Encamên dawî\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Bername niha komek testan pêk tîne da ku sazkariyên îdeal binirxîne\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testkirin qediya\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Testkirina leza înternetê didome, dibe ku çend xulekan dirêj bike\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Ti deran ji bo protokola vê rajkarê nehat dîtin\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Tê girêdan bi: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Girêdana bi rajekaran re têk çû, ji kerema xwe girêdana xwe ya înternetê kontrol bike û dîsa biceribîne.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Pahniya bandê tê testkirin ji bo: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Kodkerê weşanê tê testkirin, ev dibe ku xulekekê bistîne...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Kodkerê tomarkirinê tê testkirin, ev dibe ku xulekekê bistîne...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Destpêkirina kodker bi ser neket\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Çareseriya %1x%2 di çarçoveya %3 de tê testkirin ...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Kodkerê weşanê\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Kodkerê tomarkirinê\"\nBasic.AutoConfig.TestPage.Result.Header=\"Bernamê diyar kir ku ev sazkariyên lêhatî bo te ev in :\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Da ku van sazkariyan bi kar bînî, sazkariyan bisepîne bitikîne. Ji bo ji nû ve pevsaz bikî û dîsa biceribînî, Vegere bitikîne. Ji bo ku tu sazkariyan bi destan pevsaz bikî, dev jê berde bitikîne û sazkariyan veke.\"\nBasic.AutoConfig.Info=\"Pevsazkarê xweber wê li gorî taybetmendiyên kombersa te û leza înternetê sazkariyên herî baş diyar bike.\"\nBasic.AutoConfig.RunAnytime=\"Ev dikare her gav bi çûyîna bo kulîna amûran werê xebitandin.\"\nBasic.Stats=\"Rêjejimar\"\nBasic.Stats.CPUUsage=\"Bikaranîna YKN\"\nBasic.Stats.HDDSpaceAvailable=\"Cîhê dîskê heyî\"\nBasic.Stats.MemoryUsage=\"Bikaranîna bîrê\"\nBasic.Stats.AverageTimeToRender=\"Dema navînî bo çêkirina çarçoveyê (fps)\"\nBasic.Stats.SkippedFrames=\"Çarçoveyên ku hatine avêtin ji ber derengbûna kodkirinê\"\nBasic.Stats.MissedFrames=\"Çarçoveyên ku wendabûne ji ber derengbûna kodkirinê\"\nBasic.Stats.Output.Stream=\"Weşan\"\nBasic.Stats.Output.Recording=\"Tomarkirin\"\nBasic.Stats.Status=\"Rewş\"\nBasic.Stats.Status.Recording=\"Tê tomarkirin\"\nBasic.Stats.Status.Live=\"Zindî\"\nBasic.Stats.Status.Reconnecting=\"Tê girêdan...\"\nBasic.Stats.Status.Inactive=\"Neçalak\"\nBasic.Stats.Status.Active=\"Çalak\"\nBasic.Stats.DroppedFrames=\"Çarçoveyên ku hatine avêtin (ji sedema torê)\"\nBasic.Stats.MegabytesSent=\"Derana giştî ya daneyan\"\nBasic.Stats.Bitrate=\"Rêjeya bitê\"\nBasic.Stats.DiskFullIn=\"Dîsk wê tijî be (nêzik de.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 demjimêr, %2 xulek\"\nBasic.Stats.ResetStats=\"Rêjejimaran ji nû ve saz bike\"\nResetUIWarning.Title=\"Tu dixwazî navrûya bikarhêneriyê ji nû ve saz bikî?\"\nResetUIWarning.Text=\"Ji nû ve sazkirina UI dê Bender din veşêre. Ku tu dixwazî nîşan bide, divê tu van deran ji menuya dîtinê veşêrî.\\n\\n Ji dil dixwazî UI ji nû ve saz bikî?\"\nUpdater.Title=\"Rojanekirina nû heye\"\nUpdater.Text=\"Rojanekirina nû heye:\"\nUpdater.UpdateNow=\"Niha rojane bike\"\nUpdater.RemindMeLater=\"Paşê bîne bîra min\"\nUpdater.Skip=\"Guhertoyê derbas bike\"\nUpdater.NoUpdatesAvailable.Title=\"Tu rojanekirin tune ne\"\nUpdater.NoUpdatesAvailable.Text=\"Niha tu rojanekirin tune ne\"\nUpdater.BranchNotFound.Title=\"Kanala rojanekirinê hate rakirin\"\nUpdater.BranchNotFound.Text=\"Kanala te ya rojanekirinê ya hilbijartî êdî ne berdest e, OBS bo sazkariyên berdest hate vegerandin.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Kontrola destpakî ne berdest e\"\nUpdater.RepairButUpdatesAvailable.Text=\"Kontrolkirina destpakî ya pelê tenê ji bo guhertoya herî dawî ya berdest berdest e. Alîkariyê bi kar bîne → Ji bo rojankirinann binêre da ku bipejirînî û sazkarina OBS rojane bikî.\"\nUpdater.RepairConfirm.Title=\"Kontrola destpakiyê bipejirîne\"\nUpdater.RepairConfirm.Text=\"Destpêkirina kontrolkirina destpakiyê wê sazkirina OBS a te bişopîne ji bo pelên têkçûyî û pelên şikestî/guhertî ji nû ve daxînne. Dibe ku ev demekê bidome.\\n\\nTu dixwazî bidomînî?\"\nUpdater.FailedToLaunch=\"Vekirina rojaneker bi ser neket\"\nQuickTransitions.SwapScenes=\"Di navbera dîmenên pêşdîtin/bername de biguherîne piştî pêvajoya derbasbûnê\"\nQuickTransitions.SwapScenesTT=\"Di navbera dîmenên pêşdîtin û bername de diguherîne piştî pêvajoya derbasbûnê (heke bernameyên dîmenê resen hebin hebê.\\nEv wê guhartinên ku te li ser dîmenê bernameyên resen pêk aniye venagerînê.\"\nQuickTransitions.DuplicateScene=\"Dîmenê cêwî bike\"\nQuickTransitions.DuplicateSceneTT=\"Dema ku heman dîmen de serrastkirin pêk tê, dihêle ku serrastkirina ya guhartin/dîtin ê çavkaniyan bêyî guhertina deranê werê kirin.\\nJi bo guhertina taybetiyên çavkaniyan bêyî guhertina deranê, 'Dubarekirina çavkaniyên' çalak be.\\nGuhertina vê nirxê wê dîmena deranê ya heyî ji nû ve saz bike (heke hîn jî hebe).\"\nQuickTransitions.EditProperties=\"Çavkaniyan bike cêwî\"\nQuickTransitions.EditPropertiesTT=\"Dema ku heman dîmen de serrastkirin pêk tê, dihêle ku serrastkirina çavkaniyan bêyî guhertina deranê werê kirin. \\nEv tenê ku 'dubarekirina çavkaniyan' çalak be dikare werê bikaranîn.\\n Hinek çavkaniyên diyar (wekî girtin an jî çavkaniyên medyayê) piştgiriya vê yekê nake û nikare bi awayekî cuda werê serrastkirin.\\nGuhertina vê nirxê wê dîmena deranê ya heyî ji nû ve saz bike (ku hîn hebe).\\n\\nHişyarî: Ji ber ku çavkanî wê dubare bibin, ev dibe ku pêdiviya pergalê bi bêtir çavkaniyên vîdyoyê hebe.\"\nQuickTransitions.HotkeyName=\"Derbasbûna lezgîn: %1\"\nBasic.AddTransition=\"Derbasbûnê pevsazbar tevlî bike\"\nBasic.RemoveTransition=\"Derbasbûnê pevsazbar rake\"\nBasic.TransitionProperties=\"Taybetmendiyên derbasbûnê\"\nBasic.SceneTransitions=\"Derbasbûnên dîmenan\"\nBasic.TransitionDuration=\"Dema derbasbûnê\"\nBasic.TogglePreviewProgramMode=\"Awayê studyo\"\nBasic.EnablePreviewProgramMode=\"Awaya studioyê çalak bike\"\nBasic.DisablePreviewProgramMode=\"Awaya studioyê çalak neke\"\nUndo.Undo=\"Vegerîne\"\nUndo.Redo=\"Dûbare bike\"\nUndo.Add=\"'%1' Tevlî bike\"\nUndo.Delete=\"'%1' Jê bibe\"\nUndo.Rename=\"'%1' Ji nû ve bi nav bike\"\nUndo.SceneCollection.Switch=\"Biguherîne bo '%1'\"\nUndo.Item.Undo=\"%1 Vegerîne\"\nUndo.Item.Redo=\"%1 Dûbare bike\"\nUndo.Sources.Multi=\"%1 Çavkaniyan jê bibe\"\nUndo.Filters=\"Guhertinên parzûnê li ser %1\"\nUndo.Filters.Paste.Single=\"Parzûnan pevê bike '%1' bo '%2'\"\nUndo.Filters.Paste.Multiple=\"Parzûnan jê bigire ji '%1' bo '%2'\"\nUndo.Transform=\"Çavkaniyan biguherîne di '%1' de\"\nUndo.Transform.Paste=\"Guhertinan di '%1' de pêve bike\"\nUndo.Transform.Rotate=\"Zivirandin di '%1' de\"\nUndo.Transform.Reset=\"Guhertinan ji nû ve saz bike di '%1' de\"\nUndo.Transform.HFlip=\"Zivirandina asoyî di '%1' de\"\nUndo.Transform.VFlip=\"Zivirandina stûnî di '%1' de\"\nUndo.Transform.FitToScreen=\"'%1' di dîmenderê de bicîh bike\"\nUndo.Transform.StretchToScreen=\"'%1' di dîmenderê de dirêj bike\"\nUndo.Transform.Center=\"'%1' di dîmenderê de navendî bike\"\nUndo.Transform.VCenter=\"Navenda stûnî bo dîmenderê di '%1' de\"\nUndo.Transform.HCenter=\"Navenda asoyî bo dîmenderê di '%1' de\"\nUndo.Volume.Change=\"Guhertina dengê di '%1' de\"\nUndo.Volume.Mute=\"Bêdeng bike '%1'\"\nUndo.Volume.Unmute=\"Bêdeng neke '%1'\"\nUndo.Balance.Change=\"Di '%1' de guhertina hevsengiya dengê\"\nUndo.SyncOffset.Change=\"Guhartina dûrxisitina hevdemkirina dengê di '%1' de\"\nUndo.MonitoringType.Change=\"Çavdêriya dengê biguherîne di '%1' de\"\nUndo.Mixers.Change=\"Tevlihevkerên dengê di '%1' de biguherîne\"\nUndo.ForceMono.On=\"Dengê Force Mono çalak bike di '%1' de\"\nUndo.ForceMono.Off=\"Dengê Force Mono çalak neke di '%1' de\"\nUndo.Properties=\"Guhertina taybetiyê di '%1' de\"\nUndo.Scene.Duplicate=\"Dîmena '%1' cêwî bike\"\nUndo.ShowTransition=\"Derbasbûnê nîşan bide li ser '%1'\"\nUndo.HideTransition=\"Derbasbûnê veşêre li ser '%1'\"\nUndo.ShowSceneItem=\"'%1' Nîşan bide di '%2' de\"\nUndo.HideSceneItem=\"'%1' Veşêre di '%2' de\"\nUndo.ReorderSources=\"Çavkaniyên di '%1' serrast bike\"\nUndo.MoveUp=\"'%1' ber bi jor ve di '%2' de rake\"\nUndo.MoveDown=\"'%1' ber bi jêr ve di '%2' de daxe\"\nUndo.MoveToTop=\"'%1' ber bi pêşî ve di '%2' de bibe\"\nUndo.MoveToBottom=\"'%1' ber bi paşî ve di '%2' de bibe\"\nUndo.PasteSource=\"Çavkaniyên di '%1' de pêve bike\"\nUndo.PasteSourceRef=\"Referansa çavkaniyên di '%1' de pêve bike\"\nUndo.GroupItems=\"Hêmanan di '%1' de kom bike\"\nTransitionNameDlg.Text=\"Ji kerema xwe navê derbasbûnê binivsîne\"\nTransitionNameDlg.Title=\"Navê derbasbûnê\"\nTitleBar.SafeMode=\"AWAYA PARASTÎ\"\nTitleBar.Profile=\"Profîl\"\nTitleBar.Scenes=\"Dîmen\"\nNameExists.Title=\"Nav jixwe heye\"\nNameExists.Text=\"Nav jixwe tê bikaranîn.\"\nNoNameEntered.Title=\"Ji kerema xwe navekî derbasdar binvsîne\"\nNoNameEntered.Text=\"Tu nikarî navên vala bi kar bîne.\"\nConfirmStart.Title=\"Weşanê dest pê bike?\"\nConfirmStart.Text=\"Ma tu bi rastî dixwazî weşanê dest pê bikî?\"\nConfirmStop.Title=\"Weşanê rawestîne\"\nConfirmStop.Text=\"Ma tu bi rastî dixwazî weşanê rawestînî?\"\nConfirmStopRecord.Title=\"Tomarkirinê rawestîne\"\nConfirmStopRecord.Text=\"Ma tu bi rastî dixwazî tomarkirinê rawestînî?\"\nConfirmBWTest.Title=\"Dest bi testa leza herikê bike?\"\nConfirmBWTest.Text=\"Tu OBS-ê di moda testa leza herikê bi rê ve dibî. Ev mod dihêle ku tu bêyî ku kanala te biçe weşana zindî torê test bikî. Gava te test qedand, ji bo ku temaşevan bikaribin herika te bibînin, tu yê wê neçalak bikî.\\n \\nTu dixwazî bidomînî?\"\nConfirmExit.Title=\"Deranên çalak\"\nConfirmExit.Text=\"OBS hîn çalak e. Hemû weşan/tomarkirin wê werin girtin.\"\nConfirmRemove.Title=\"Jêbirinê bipejirîne\"\nConfirmRemove.Text=\"Tu bi rastî dixwazî '%1' rakî?\"\nConfirmRemove.TextMultiple=\"Tu dixwazî hêmanên %1 jê bibî?\"\nConfirmReset.Title=\"Taybetmendiyan ji nû ve saz bike\"\nConfirmReset.Text=\"Tu dixwazî taybetmendiyên heyî ji nû ve saz bikî bo berdestî?\"\nOutput.StartStreamFailed=\"Destpêkirina weşanê têkçû\"\nOutput.StartRecordingFailed=\"Destpêkirina tomarkirinê têkçû\"\nOutput.StartReplayFailed=\"Destpêkirina pêşbîra demkî têkçû\"\nOutput.StartFailedGeneric=\"Destpêkirina deranê têk çû. Jkx ji bo hûrgiliyan têketinê kontrol bike.\\n\\nNîşe: Ku tu kodkerên NVENC an jî AMD bi kar tînî, bawer be ku ajokarên vîdyoyê rojanekirî ne an na.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Dema ku tê rawestandin nikare dubareyan tomar bike\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Hişyarî: Dema tomar werê rawestandin ji nû ve lêdan nayê tomarkirin.\"\nOutput.ConnectFail.Title=\"Girêdan pêk nehat\"\nOutput.ConnectFail.BadPath=\"Girêdanan URL an jî Riya girêdanê ne rast e. Jkx sazkariyên xwe kontrol bike da ku piştrast bikî ku derbasdarin an na.\"\nOutput.ConnectFail.ConnectFailed=\"Girêdana bi rajekar re têkçû\"\nOutput.ConnectFail.InvalidStream=\"Nikare bigihîje kenala diyarkirî an kilîta weşanê, jkx du caran kilîta weşanê kontrol bike. Ku rast be, dibe ku di girêdana bi rajekar re pirsgirêkek hebê.\"\nOutput.ConnectFail.HdrDisabled=\"Derana HDR niha ji bo vê deranê neçalak e.\"\nOutput.ConnectFail.Error=\"Çewtiyek nehêvîkirî rû da gava ku hewldan pêk dihat bo ku bi rajekar ve girê bidê. Bêtir zanyarî di pelê tomarkirinê de.\"\nOutput.ConnectFail.Disconnected=\"Girêdan ji rajekar ve qut bû.\"\nOutput.StreamEncodeError.Title=\"Çewtiya kodkirinê\"\nOutput.StreamEncodeError.Msg=\"Di dema weşanê de çewtiyeke kodkirinê çêbû.\"\nOutput.RecordFail.Title=\"Destpêkirina tomarkirinê têkçû\"\nOutput.RecordFail.Unsupported=\"Formata deranê an nayê piştgirîkirin an jî ji herîkek bêtir piştgirî nade. Jkx sazkariyên xwe kontrol bike û dîsa biceribîne.\"\nOutput.RecordNoSpace.Title=\"Cihê dîskê têrê nake\"\nOutput.RecordNoSpace.Msg=\"Ji bo domandina tomarkirinê cihê dîskê têr nake.\"\nOutput.RecordError.Title=\"Çewtiya tomarkirinê\"\nOutput.RecordError.Msg=\"Di dema tomarkirinê de çewtiyeke nediyar çêbû.\"\nOutput.RecordError.EncodeErrorMsg=\"Di dema tomarkirinê de çewtiyeke kodker çêbû.\"\nOutput.BadPath.Title=\"Riya pelê çewt e\"\nOutput.BadPath.Text=\"Riya tomarkirinê ya pevsazkirî nehate vekirin. Ji kerema xwe rêgeha tomarkirin kontrol bike di beşa Sazkarî → Deran → Tomarkirin.\"\nOutput.NoBroadcast.Title=\"Weşan çê nebû\"\nOutput.NoBroadcast.Text=\"Berî ku tu dest bi weşanê bikî, divê ku tu weşanekê saz bikî.\"\nOutput.BroadcastStartFailed=\"Destpêkirina weşanê têkçû\"\nOutput.BroadcastStopFailed=\"Rawestandina weşanê têkçû\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Bar bike\"\nLogUploadDialog.Buttons.CopyURL=\"Girêdanên têketinan jê bigire\"\nRemux.SourceFile=\"Tomarkirina OBS\"\nRemux.TargetFile=\"Pelê armanc\"\nRemux.Remux=\"Guhertina formatê\"\nRemux.Stop=\"Guhertina formatê rawestîne\"\nRemux.ClearFinished=\"Hêmanên qediyayî pak bike\"\nRemux.ClearAll=\"Hemû hêmanan pak bike\"\nRemux.OBSRecording=\"Tomarkirina OBS\"\nRemux.FinishedTitle=\"Guhertina formatê qediya\"\nRemux.Finished=\"Guhertina formatê tê tomarkirin\"\nRemux.FinishedError=\"Guhertina formatê tê tomarkirin, lê dibe ku pel netemam be\"\nRemux.SelectRecording=\"Tomarkirina OBS hilbijêre...\"\nRemux.SelectTarget=\"Pelê armanc hilbijêre...\"\nRemux.FileExistsTitle=\"Pelên armancê hene\"\nRemux.FileExists=\"Ev pelên armanc jixwe hene. Tu dixwazî wan biguherînî?\"\nRemux.ExitUnfinishedTitle=\"Guhertina formatê didome...\"\nRemux.ExitUnfinished=\"Guhertina formatê ne qediya ye, rawestandina guhertina formatê niha dibe ku pel ne neyê bikaranîn.\\nTu bawer î ku tu dixwazî ​​guhertina formatê rawestîne?\"\nRemux.HelpText=\"Pelan biavêje vê çarçoveyê ji bo ku guhertina formatê bikî, an jî ji bo peleke vala ya \\\"Tomarkirnên OBS \\\" hilbijêre.\"\nMissingFiles=\"Pelên winda\"\nMissingFiles.MissingFile=\"Pelê winda\"\nMissingFiles.NewFile=\"Pela nû\"\nMissingFiles.HelpText=\"Ji dema herî dawî ku te OBS bi kar aniye û vir ve hinek pel winda ne.\"\nMissingFiles.Clear=\"<pakkirî>\"\nMissingFiles.NumFound=\"%1 ji %2 hate dîtin\"\nMissingFiles.Search=\"Rêgehê de bigere...\"\nMissingFiles.SelectFile=\"Pelê hilbijêre...\"\nMissingFiles.SelectDir=\"Peldankekê hilbijêre ji bo lêgerînê li\"\nMissingFiles.State=\"Rewş\"\nMissingFiles.Missing=\"Tune\"\nMissingFiles.Replaced=\"Hatiye guhartin\"\nMissingFiles.Cleared=\"Hatiye pakkirin\"\nMissingFiles.Found=\"Hate dîtin\"\nMissingFiles.AutoSearch=\"Bêtir pelên lihevhatî hatin dîtin\"\nMissingFiles.AutoSearchText=\"OBS pelên lihevhatî ji bo pelên windayî di vê rêgehê de dîte. Tu dixwî wan tevlî bikî?\"\nMissingFiles.NoMissing.Title=\"Kontrolkirina pelên windayî\"\nMissingFiles.NoMissing.Text=\"Nayê xuyakirin ku tu pel windabûne\"\nMacPermissions.Description=\"OBS Studio mafdayîna te daxwaz dike ku tu bikaribî hinek taybetmendiyan pêkan bin. Tê pêşniyar kirin ku van mafdayînan çalak bikî, lê ne pêdivî ye ku ew sepan were bikaranîn. Tu dikarî her dem wan paşê çalak bikî.\"\nMacPermissions.Description.OpenDialog=\"Tu dikarî vê gotûbêjê ji menuya OBS Studio ji nû ve vekî\"\nMacPermissions.AccessGranted=\"Mafê gihîştinê hate dayîn\"\nMacPermissions.RequestAccess=\"Daxwaza gihîştinê\"\nMacPermissions.OpenPreferences=\"Hevyazên %1 veke\"\nMacPermissions.Item.ScreenRecording=\"Tomarkirina dîmenderê\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS vê mafdayînê daxwaz dike ku bikaribe dîmendera te tomar bike.\"\nMacPermissions.Item.Camera=\"Kamîra\"\nMacPermissions.Item.Camera.Details=\"Ji bo girtina naverokê ji kamerayê yan kerta girtina naverokê ev mafdayîn pêdivî ye.\"\nMacPermissions.Item.Microphone=\"Mîkrofon\"\nMacPermissions.Item.Accessibility=\"Gihiştin\"\nMacPermissions.Continue=\"Bidomîne\"\nBasic.DesktopDevice1=\"Dengê sermaseyê\"\nBasic.DesktopDevice2=\"Dengê sermaseyê 2\"\nBasic.Scene=\"Dîmen\"\nBasic.DisplayCapture=\"Girtina dîmenderê\"\nBasic.Main.PreviewConextMenu.Enable=\"Pêşdîtin çalak bike\"\nBasic.Main.Preview.Disable=\"Pêşdîtin neçalak bike\"\nScaleFiltering=\"Parzûnkirina mezinahiyê\"\nScaleFiltering.Point=\"Xal\"\nScaleFiltering.Bicubic=\"Bikubik\"\nScaleFiltering.Area=\"Dever\"\nBlendingMethod=\"Rêbaza tevlîkirinê\"\nBlendingMethod.Default=\"Berdest\"\nBlendingMethod.SrgbOff=\"SRGB neçalak e\"\nBlendingMode=\"Moda tevlihevkirinê\"\nBlendingMode.Additive=\"Tevlî bike\"\nBlendingMode.Subtract=\"Jêkêmkirin\"\nBlendingMode.Screen=\"Dîmender\"\nBlendingMode.Multiply=\"Lêxistin\"\nBlendingMode.Lighten=\"Ronîkirin\"\nBlendingMode.Darken=\"Tarîkirin\"\nDeinterlacing.Discard=\"Paşguh bike\"\nDeinterlacing.TopFieldFirst=\"Jorê zeviya pêşîn\"\nDeinterlacing.BottomFieldFirst=\"Jêrê zeviya pêşîn\"\nVolControl.SliderUnmuted=\"Şemitokê dengê ji bo '%1':\"\nVolControl.SliderMuted=\"Şemitokê dengê ji bo '%1': (niha bê deng e)\"\nVolControl.Mute=\"\\\"%1\\\" Bê deng bike\"\nVolControl.Properties=\"Taybetmendiyên '%1'\"\nVolControl.UnassignedWarning.Title=\"Çavkaniya dengê nenaskirî\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" bo her kîjan herîka dengê re nehatiye diyarkirin û ew ê di weşan û tomaran de neyê bihîstin.\\n\\nBo diyarkirina çavkaniyeke dengî bo herîkekê re, taybetmendiyên dengê pêşketî veke bi riya menu rast-bitikîne yan jî bişkoka dirankirî ve di bendera alavbenda tevlihevkerê de.\"\nBasic.Main.AddSceneDlg.Title=\"Dîmenê tevlî bike\"\nBasic.Main.AddSceneDlg.Text=\"Ji kerema xwe navê dîmenê binivîse\"\nBasic.Main.DefaultSceneName.Text=\"Dîmen %1\"\nBasic.Main.AddSceneCollection.Title=\"Berhevoka dîmenan tevlî bike\"\nBasic.Main.AddSceneCollection.Text=\"Ji kerema xwe navê berhevoka dîmenê binivîsine\"\nBasic.Main.RenameSceneCollection.Title=\"Ji nû ve nav li berhevoka dîmenê bike\"\nAddProfile.Title=\"Profîlê tevlî bike\"\nAddProfile.Text=\"Ji kerema xwe navê profîlê binivîse\"\nAddProfile.WizardCheckbox=\"Pevsazkirina pêkhatiyên xweber nîşan bide\"\nRenameProfile.Title=\"Profîlê ji nû ve bi nav bike\"\nBasic.Main.MixerRename.Title=\"Çavkaniya dengê ji nû ve bi nav bike\"\nBasic.Main.MixerRename.Text=\"Ji kerema xwe navê çavkaniya dengê binivîsine\"\nBasic.Main.PreviewDisabled=\"Pêşdîtin niha neçalak e\"\nBasic.SourceSelect=\"Çavkanî biafirîne/Hilbijêre\"\nBasic.SourceSelect.CreateNew=\"Nû biafirîne\"\nBasic.SourceSelect.AddExisting=\"Yê heyî tevlî bike\"\nBasic.SourceSelect.AddVisible=\"Çavkaniyê dîtbar bike\"\nBasic.Main.Sources.Visibility=\"Xuyanî\"\nBasic.Main.Sources.VisibilityDescription=\"Dîtina di tabloya '%1' de kontrol dike\"\nBasic.Main.Sources.Lock=\"Kilît bike\"\nBasic.Main.Sources.LockDescription=\"Cîh û pîvandina di '%1' de tabloyê de kilît dike\"\nBasic.PropertiesWindow=\"Taybetmendiyên '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (hilbijartina bixweber: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Reng hilbijêre\"\nBasic.PropertiesWindow.SelectFont=\"Curenivîs hilbijêre\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Cûrenivîsekê hilbijêre\"\nBasic.PropertiesWindow.ConfirmTitle=\"Sazkarî hatin guhertin\"\nBasic.PropertiesWindow.Confirm=\"Guhertinên nehatine tomarkirin hene. Tu dixwazî wan tomar bikî?\"\nBasic.PropertiesWindow.NoProperties=\"Tu taybetmendiyên heyî tune\"\nBasic.PropertiesWindow.AddFiles=\"Pelan tevlî bike\"\nBasic.PropertiesWindow.AddDir=\"Pelrêç tevlî bike\"\nBasic.PropertiesWindow.AddURL=\"Rêgeh/Girêdan tevlî bike\"\nBasic.PropertiesWindow.AddEditableListDir=\"Peldankê tevlî '%1' bike\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Pelan tevlî '%1' bike\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Têketinê tevlî '%1' bike\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Têketinê ji '%1' biguherîne\"\nBasic.PropertiesView.FPS.Simple=\"Nirxên FPS ên sade\"\nBasic.PropertiesView.FPS.Rational=\"Nirxên FPS ên rasyonel\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Rêjeyên FPS-ên derbasdar:\"\nBasic.PropertiesView.UrlButton.Text=\"Vê girêdanê di geroka xwe ya tevne ya berdest de veke?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"Girêdan %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Girêdanê veke\"\nBasic.InteractionWindow=\"Bi '%1' re çalakîlidarxistin\"\nBasic.StatusBar.Reconnecting=\"Girêdan qut bû, di %2 çirkeyan de tê girêdan (hewldana %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Ji nû ve tê girêdan... (hewldana %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Ji nû ve girêdan serkeftî bû\"\nBasic.StatusBar.Delay=\"Derengxistin (%1 çirke)\"\nBasic.StatusBar.DelayStartingIn=\"Derengxistin (di %1 çirke de tê destpêkirin)\"\nBasic.StatusBar.DelayStoppingIn=\"Derengxistin (di %1 çirke de tê rawestan)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Derengxistin (di %2 çirke de tê rawestan, di %1 çirke de tê destpêkirin)\"\nBasic.StatusBar.RecordingSavedTo=\"Tomarkirin di '%1' de hate tomarkirin\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Pêşbîra demkî di '%1' de hate tomarkirin\"\nBasic.StatusBar.ScreenshotSavedTo=\"Wêneya dîmenderê di '%1' de hate tomarkirin\"\nBasic.StatusBar.AutoRemuxedTo=\"Guhertina formatê bi xweberî bo '%1' hate guhertin\"\nBasic.Filters=\"Parzûn\"\nBasic.Filters.AsyncFilters=\"Parzûnên deng/vîdyo\"\nBasic.Filters.AudioFilters=\"Parzûnên dengê\"\nBasic.Filters.EffectFilters=\"Parzûnên bandorê\"\nBasic.Filters.Title=\"Parzûnên ji bo '%1'\"\nBasic.Filters.AddFilter.Title=\"Navê parzûnê\"\nBasic.Filters.AddFilter.Text=\"Ji kerema xwe navê parzûnê diyar bike\"\nBasic.TransformWindow=\"Guhertina hêmanê dîmenê\"\nBasic.TransformWindow.Position=\"Şon\"\nBasic.TransformWindow.PositionX=\"Şona X\"\nBasic.TransformWindow.PositionY=\"Şona Y\"\nBasic.TransformWindow.Rotation=\"Zivirandin\"\nBasic.TransformWindow.Size=\"Mezinahî\"\nBasic.TransformWindow.Width=\"Pehnî\"\nBasic.TransformWindow.Height=\"Bilindî\"\nBasic.TransformWindow.Alignment=\"Bicîkirina şûnê\"\nBasic.TransformWindow.BoundsType=\"Cureya dorpêçkirina çargoşeyê\"\nBasic.TransformWindow.BoundsAlignment=\"Bicîkirina dorpêçkirina çargoşeyê\"\nBasic.TransformWindow.Bounds=\"Mezinahiya dorpêçkirina çargoşeyê\"\nBasic.TransformWindow.BoundsWidth=\"Pehniya dorpêçkirina çargoşeyê\"\nBasic.TransformWindow.BoundsHeight=\"Bilindahiya dorpêçkirina çargoşeyê\"\nBasic.TransformWindow.Crop=\"Qutkirin\"\nBasic.TransformWindow.CropLeft=\"Çepê qut bike\"\nBasic.TransformWindow.CropRight=\"Rastê qut bike\"\nBasic.TransformWindow.CropTop=\"Jorê qut bike\"\nBasic.TransformWindow.CropBottom=\"Jêrê qut bike\"\nBasic.TransformWindow.Alignment.TopLeft=\"Çepê jor\"\nBasic.TransformWindow.Alignment.TopCenter=\"Navenda jor\"\nBasic.TransformWindow.Alignment.TopRight=\"Rastê jor\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Navenda çepê\"\nBasic.TransformWindow.Alignment.Center=\"Navend\"\nBasic.TransformWindow.Alignment.CenterRight=\"Navenda rastê\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Çepê jêr\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Navenda jêr\"\nBasic.TransformWindow.Alignment.BottomRight=\"Rastê jêr\"\nBasic.TransformWindow.BoundsType.None=\"Sînor tune\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Tenê mezinahiya herî mezin\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Bo sînorên hundir re bipîve\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Bo sînorên derva re bipîve\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Bo pehniya sînoran re bipîve\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Bo bilindahiya sînoran re bipîve\"\nBasic.TransformWindow.BoundsType.Stretch=\"Bi sînoran re dirêj bike\"\nBasic.TransformWindow.Title=\"Serrastkirina derbasbûnê bo '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Ti çavkanî nehate hilbijartin\"\nBasic.Main.AddSourceHelp.Title=\"Nikare çavkanî tevlî bike\"\nBasic.Main.AddSourceHelp.Text=\"Ji bo ku tu çavkaniyekê tevlî bikî, divê herî kêm 1 dîmen hebe.\"\nBasic.Main.Scenes=\"Dîmen\"\nBasic.Main.Sources=\"Çavkanî\"\nBasic.Main.Source=\"Çavkanî\"\nBasic.Main.Controls=\"Kontrol\"\nBasic.Main.PreparingStream=\"Tê amedekirin...\"\nBasic.Main.Connecting=\"Tê girêdan...\"\nBasic.Main.StartRecording=\"Dest bi tomarkirinê bike\"\nBasic.Main.StartReplayBuffer=\"Pêşbîra demkî bide destpêkirin\"\nBasic.Main.SaveReplay=\"Ji nû ve lêdanê tomar bike\"\nBasic.Main.StartStreaming=\"Dest bi weşanê bike\"\nBasic.Main.StartBroadcast=\"Biçe zindî\"\nBasic.Main.StartVirtualCam=\"Kameraya rastekî dest pê bike\"\nBasic.Main.StopRecording=\"Tomarkirinê rawestîne\"\nBasic.Main.PauseRecording=\"Tomarkirinê bi awayekî demkî rawestîne\"\nBasic.Main.UnpauseRecording=\"Tomarkirinê bidomîne\"\nBasic.Main.SplitFile=\"Pelê tomarkirinê dabeş bike\"\nBasic.Main.StoppingRecording=\"Tomarkirin tê rawestandin...\"\nBasic.Main.StopReplayBuffer=\"Pêşbîra demkî rawestîne\"\nBasic.Main.StoppingReplayBuffer=\"Pêşbîra demkî tê rawestandin...\"\nBasic.Main.SetupBroadcast=\"Weşanê bi rê ve bibe\"\nBasic.Main.StopStreaming=\"Weşanê rawestîne\"\nBasic.Main.StopBroadcast=\"Weşanê biqedîne\"\nBasic.Main.AutoStopEnabled=\"(Rawestandina bi xweberî)\"\nBasic.Main.StoppingStreaming=\"Rawestandina weşanê...\"\nBasic.Main.ForceStopStreaming=\"Weşane rawestîne (derengûnê paşguh bike)\"\nBasic.Main.ShowContextBar=\"Alavdankê çavkaniyan nîşan bide\"\nBasic.Main.HideContextBar=\"Alavdankê çavkaniyan veşêre\"\nBasic.Main.StopVirtualCam=\"Kameraya rastekî rawestîne\"\nBasic.Main.Group=\"Kom %1\"\nBasic.Main.GroupItems=\"Hêmanên hilbijartî kom bike\"\nBasic.Main.Ungroup=\"Komê belav bike\"\nBasic.Main.GridMode=\"Moda torê\"\nBasic.Main.ListMode=\"Moda rêzokê\"\nBasic.Main.VirtualCamConfig=\"Kameraya rastekî pevsaz bike\"\nBasic.VCam.VirtualCamera=\"Kameraya rastekî\"\nBasic.VCam.OutputType=\"Cureya deranê\"\nBasic.VCam.OutputSelection=\"Hilbijartina deranê\"\nBasic.VCam.OutputType.Program=\"Bername(Berdest)\"\nBasic.MainMenu.File=\"Pel (&F)\"\nBasic.MainMenu.File.Export=\"D&erxistin\"\nBasic.MainMenu.File.Import=\"Têx&istin\"\nBasic.MainMenu.File.ShowRecordings=\"Toma&rkirinan nîşan bide\"\nBasic.MainMenu.File.Remux=\"Gu&hertina formatên tomarkirinê\"\nBasic.MainMenu.File.Settings=\"&Sazkarî\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Peldanka sazkariyan nîşan bide\"\nBasic.MainMenu.File.ShowProfileFolder=\"Peldanka profîlê nîşan bide\"\nBasic.MainMenu.File.ShowMissingFiles=\"Bo pelên wendayî kontrol bike\"\nBasic.MainMenu.File.Exit=\"Derkeve (&X)\"\nBasic.MainMenu.Edit=\"Biguh&erîne\"\nBasic.MainMenu.Edit.Undo=\"Vegerîne (&U)\"\nBasic.MainMenu.Edit.Redo=\"Dûba&re bike\"\nBasic.MainMenu.Edit.LockPreview=\"Pêşdîtinê ki&lît bike\"\nBasic.MainMenu.Edit.Scale=\"Pêşdîtin & pîvandin (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"Bo çarçoveyê bipîve\"\nBasic.MainMenu.Edit.Scale.Output=\"Deran (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Nêzîk bike\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Mezinkirinê ji nû ve saz bike\"\nBasic.MainMenu.Edit.Transform=\"Biguherîne (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Guh&ertinê serrast bike...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Guhertinê jê bigire\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Guhertinê pêve bike\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Guhe&rtinê ji nû ve saz bike\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"90 pile CW bizivirîne\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"90 pile CCW bizivirîne\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180 pile bizivirîne\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Asoyî bizivrîne (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Stûnî bizivrîne (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Li dîmenderê bicîh bike (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Bo dîmenderê dirêj bike (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Bo dîmenderê navend bike (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Navendîkirina stûnî\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Navendîkirina asoyî\"\nBasic.MainMenu.Edit.Order=\"Ferman (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Bilivîne jor (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Bilivîne jêr (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Bilivîne pêşî (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Bilivîne binî (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"T&aybetiyên pêşketî yên dengê\"\nBasic.MainMenu.View=\"Dîtin (&V)\"\nBasic.MainMenu.View.Toolbars=\"Alavdank (&T)\"\nBasic.MainMenu.View.ContextBar=\"Alavdankê çavkaniyan\"\nBasic.MainMenu.View.SourceIcons=\"Sembolê çavkan&iyan\"\nBasic.MainMenu.View.StatusBar=\"Darika rewşê (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Navrûya dîmendera tije\"\nBasic.MainMenu.View.ResetUI=\"&Navrûya bikarhêneriyê ji nû ve saz bike\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Her dem li jor\"\nBasic.MainMenu.Docks=\"&Bender\"\nBasic.MainMenu.Docks.ResetDocks=\"&Benderê ji nû ve saz bike\"\nBasic.MainMenu.Docks.LockDocks=\"&Benderê kilît bike\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Benderên gerokê kesanekirî...\"\nBasic.MainMenu.Import=\"Têxistin...\"\nBasic.MainMenu.Export=\"Derxistin...\"\nBasic.MainMenu.SceneCollection=\"Berhevoka dîmenan (&S)\"\nBasic.MainMenu.Profile=\"&Profîl\"\nBasic.MainMenu.Profile.Import=\"Profîlê têxê\"\nBasic.MainMenu.Profile.Export=\"Profîlê derxe\"\nBasic.MainMenu.SceneCollection.Import=\"Berhevoka dîmenan têxe\"\nBasic.MainMenu.SceneCollection.Export=\"Berhevoka dîmenan derxe\"\nBasic.MainMenu.Profile.Exists=\"Profîl jixwe heye\"\nBasic.MainMenu.Tools=\"Alav (&T)\"\nBasic.MainMenu.Help=\"Alîkarî (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Alîkariya dergevan (&P)\"\nBasic.MainMenu.Help.Website=\"Serdanê malperê bike (&W)\"\nBasic.MainMenu.Help.Discord=\"Beşdarî rajekarê &Discord bibe\"\nBasic.MainMenu.Help.WhatsNew=\"Çi nû ye\"\nBasic.MainMenu.Help.Logs=\"Pe&lên têketinê\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Pelên têketinê nîşan bide (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Pelê têketina heyî bar bike (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Pelê têketinê &paş bar bike (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Têketina heyî nîşan bide (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Nîşeyên weşanê\"\nBasic.MainMenu.Help.CheckForUpdates=\"Ji bo rojanekirinê kontrol bike\"\nBasic.MainMenu.Help.Repair=\"Yekbûna pelê kontrol bike\"\nBasic.MainMenu.Help.RestartSafeMode=\"Di moda ewle de ji nû ve dest pê bike\"\nBasic.MainMenu.Help.RestartNormal=\"Di moda normal de ji nû ve dest pê bike\"\nBasic.MainMenu.Help.CrashLogs=\"Ragîhandinê &têkçûnan\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Ragihandinên têkçûyînê nîşan bide (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Ragihandina têkçûnê &paş bar bike (&P)\"\nBasic.MainMenu.Help.About=\"Derb&ar\"\nBasic.Settings.ProgramRestart=\"Ji bo ku ev sazkarî bi bandor be divê bername ji nû ve werê destpêkirin.\"\nBasic.Settings.ConfirmTitle=\"Guhertinan piştrast bike\"\nBasic.Settings.Confirm=\"Guhertinên nehatine tomarkirî hene. Guhertinan tomar bike?\"\nBasic.Settings.General=\"Giştî\"\nBasic.Settings.General.Language=\"Ziman\"\nBasic.Settings.General.Updater=\"Rojanekirin\"\nBasic.Settings.General.UpdateChannel=\"Kanalê rojane bike\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Neçalak)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Berdest)\"\nBasic.Settings.General.EnableAutoUpdates=\"Ji bo rojanekirinan li ser destpêkirinê bi xweberî kontrol bike\"\nBasic.Settings.General.OpenStatsOnStartup=\"Di destpêkê de gotûbêja amaran veke\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Çarçoveyên OBS ji dîmendera girtinê veşêre\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Ev sazkarî wê hemû çarçoveyên OBS Studio yên ne-projektorî veşêre ku ji hêla OBS ve neyên girtin û bandorê li \\nsepanên din bike, wek konfêrans, parvekirina dîmenderê, piştgirîya ji dûr ve, dîmen girtin, û bernameyên din ên giritinê.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Çalakkirina vê vebijêrkê hemû çarçoveyên OBS Studio yên ne-projektor ji hêla OBS ve têne girtin vedişêre û bandorê li ser sepanên din dike, wek konfêrans, parvekirina dîmenderê, piştgirîya ji dûr ve, dîmen girtin, û bernameyên din ên giritinê.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Di destpêkirina weşanê de gotûbêja piştrastkirinê nîşan bide\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Di rawestandina weşanê de gotûbêja piştrastkirinê nîşan bide\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Di rawestandina tomarkirinê de gotûbêja piştrastkirinê nîşan bide\"\nBasic.Settings.General.Projectors=\"Projektor\"\nBasic.Settings.General.HideProjectorCursor=\"Nîşankera mîşkê li ser projektoran veşêre\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Projektoran her dem di pêşî de bihêle\"\nBasic.Settings.General.Snapping=\"Bicîkirina çavkaniyê\"\nBasic.Settings.General.ScreenSnapping=\"Çavkaniyan ber bi qeraxa dîmenderê bikişîne\"\nBasic.Settings.General.CenterSnapping=\"Çavkaniyan ber bi navenda asoyî û stûnî bikişîne\"\nBasic.Settings.General.SourceSnapping=\"Çavkaniyan ber bi çavkaniyên din bikşîne\"\nBasic.Settings.General.SnapDistance=\"Hestiyariya kaşkirinê\"\nBasic.Settings.General.SpacingHelpers=\"Rêbernameyên bicîhkirina pixel nîşan bide\"\nBasic.Settings.General.RecordWhenStreaming=\"Dema weşanê de bi xweberî tomar bike\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Dema weşan tê rawestandin tomarkirinê bidomîne\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Dema weşan bi rê ve diçê pêşbîra demkî bi xweberî bide destpêkirin\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Dema weşan tê rawestandin pêşbîra demkî çalak bidomîne\"\nBasic.Settings.General.SysTray=\"Devera agahiyê\"\nBasic.Settings.General.SysTrayWhenStarted=\"Bernameyê bo devera agahiyan biçûk bike gava dest pê dike\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Bernameyê bo devera agahiyan biçûk bike ji devla darika erkê\"\nBasic.Settings.General.SaveProjectors=\"Li ser derketinê projektoran tomar bike\"\nBasic.Settings.General.CloseExistingProjectors=\"Ji bo her dîmenderekê yek projektor tijî dîmender bi sînor bike\"\nBasic.Settings.General.Preview=\"Pêşdîtin\"\nBasic.Settings.General.OverflowHidden=\"Pêşdîtinê veşêre\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Pêşdîtin her dem xuya ye\"\nBasic.Settings.General.OverflowSelectionHidden=\"Pêşdîtinê nîşan bide tevî ku çavkanî ne xuya be\"\nBasic.Settings.General.Importers=\"Têxistiner\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Di têxisitnê de li cihên naskirî yên ji bo berhevoka dîmenan bigere\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Di du-tikandin de derbasî dîmenê bibe\"\nBasic.Settings.General.StudioPortraitLayout=\"Pergala asoyî/stûnî çalak bike\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Nîşanên pêşdîtin/bername nîşan bide\"\nBasic.Settings.General.Multiview=\"Pirdîmen\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Bitikîne bo guhertinê navbera dîmanan de\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Navê dîmenan nîşan bide\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Devera aram xêz bike (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Plankirina pirdîmen\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Asoyî, jor (8 Dîmen)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Asoyî, jêr (8 Dîmen)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Stûnî, çep (8 Dîmen)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Stûnî, rast (8 Dîmen)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Asoyî, jor (18 Dîmen)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Asoyî, jor (24 Dîmen)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Tenê dîmen (4 Dîmen)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Tenê dîmen (9 Dîmen)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Tenê dîmen (16 Dîmen)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Tenê dîmen (25 Dîmen)\"\nBasic.Settings.General.ChannelName.stable=\"Stabîl\"\nBasic.Settings.General.ChannelDescription.stable=\"Weşana stabîl a herî dawî\"\nBasic.Settings.General.ChannelName.beta=\"Beta / Namzetên weşanê\"\nBasic.Settings.General.ChannelDescription.beta=\"Guhertoyên pêş-weşanê yên potansiyel ne stabîl in\"\nBasic.Settings.Appearance=\"Xuyang\"\nBasic.Settings.Appearance.General=\"Giştî\"\nBasic.Settings.Appearance.General.Theme=\"Rûkar\"\nBasic.Settings.Appearance.FontScale=\"Meznihaiya çûrenivîsê\"\nBasic.Settings.Appearance.Classic=\"Klasîk\"\nBasic.Settings.Stream=\"Weşan\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Rastandinê bi kar bîne\"\nBasic.Settings.Stream.Custom.Username=\"Navê bikarhêneriyê\"\nBasic.Settings.Stream.Custom.Password=\"Pêborîn\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: navê srp_username têxe.\\nRTMP: navê bikarhêner têxe.\\nSRT: nehatiye bikaranîn.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: srp_password têxe.\\nRTMP: borînpeyvê têxe.\\nSRT: borînpeyva şîfrekirinê têxe.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Moda leza herikê çalak bike\"\nBasic.Settings.Stream.TTVAddon=\"Pêvekên axaftinê yên Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Ne yek\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV û FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Sazkirina weşane wenda ye\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Sazkariyan veke\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"girêdan û kilîta weşanê winda ye ne.\\n\\nSazkariyan veke ku URL û kilîta weşanê têxî di rûgera 'weşan' ê.\"\nBasic.Settings.Stream.MissingUrl=\"Girêdana weşanê winda ye.\\n\\nSazkariyan veke ku girêdana weşanê têxî di rûgera 'weşan' ê.\"\nBasic.Settings.Stream.MissingStreamKey=\"kilîta weşanê winda ye.\\n\\nSazkariyan veke ku girêdan û kilîta weşanê têxî di rûgera 'weşan' ê.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Pêşniyarên sazkirina karûbarê weşane paşguh bike\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Sazkarîyên pêşniyarkirî hilînin\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Hişyarî: Dibe ku paşguhxistina sînorên karûbarê bibe sedema qalîteya weşanê ya xerabkirî an rê li ber weşana we bigire.\\n\\nDerdewam?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Leza bîta vîdyoyê ya herî pir: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Leza bîta dengê ya herî pir: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Çareseriya herî pir: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Çarçoveya (FPS) ya herî pir: %1\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1 çalak bike\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Xweber\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Xweber\"\nBasic.Settings.Stream.AdvancedOptions=\"Vebijêrkên pêşketî\"\nBasic.Settings.Output=\"Deran\"\nBasic.Settings.Output.Format=\"Formata tomarkirinê\"\nBasic.Settings.Output.Format.MKV=\"Vîdyoya Matroska (.mkv)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 beşkirî (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV beşkirî (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"MOV a beşkirî tomarê bi beşan dinivîse û wekî pelên MOV a kevneşopî ne û pêdivî heman dawî nake ye.\\nEv piştrast dike ku pel tê lêdan her çend nivîsandina li ser dîskê were qut kirin, mînakî, ji ber BSOD an windabûna hêzê.\\n\\nEv dibe ku bi hemû lîstikvan û edîtoran re lihevhatî be. Pel → Guhertina formatên tomarkirinê bi kar bîne da ku pelê bo formateke bêtir lihevhatî be biguherîne ku pêdivî hebe.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"MP4 a beşkirî tomarê bi beşan dinivîse û wekî pelên MP4 a kevneşopî ne û pêdivî heman dawî nake ye.\\nEv piştrast dike ku pel tê lêdan her çend nivîsandina li ser dîskê were qut kirin, mînakî, ji ber BSOD an windabûna hêzê.\\n\\nEv dibe ku bi hemû lîstikvan û edîtoran re lihevhatî be. Pel → Guhertina formatên tomarkirinê bi kar bîne da ku pelê bo formateke bêtir lihevhatî be biguherîne ku pêdivî hebe.\"\nBasic.Settings.Output.Encoder.Video=\"Kodkerê vîdyo\"\nBasic.Settings.Output.Encoder.Audio=\"Kodkerê dengê\"\nBasic.Settings.Output.SelectDirectory=\"Peldanka tomarkirinê hilbijêre\"\nBasic.Settings.Output.DynamicBitrate=\"Rêjeya bitê biguherîne da ku bargiraniyê birêve bibe\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Rêjaya bît biguherîne da ku bargiraniyê birêve bibe (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Li şûna ku çarçoveyên kêmkirina tevliheviyê kêm bike, bi awayekî dînamîk bitrate di firînê de diguherîne.\\n\\nNîşe ku heke ji nişka ve tevliheviyek mezin hebe ev dikare derengbûnê li temaşevanan zêde bike.\\nDema ku rêjeya bît dakeve, ji bo sererastkirinê dikare heta çend deqeyan berdewam bike.\\n\\nNiha tenê ji bo RTMP tê piştgirî kirin.\"\nBasic.Settings.Output.Mode=\"Moda deranê\"\nBasic.Settings.Output.Mode.Simple=\"Hêsan\"\nBasic.Settings.Output.Mode.Adv=\"Pêşketî\"\nBasic.Settings.Output.Mode.FFmpeg=\"Derana FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Pêşbîra demkî çalak bike\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Dema pêşbîra demkî ya herî pir\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Bikaranîna bîra ya pîvandî: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Hişyarî: Bikaranîna bîra texmînkirî ya %1 MiB ji ya pêşniyarkirî ya %2 MiB mezintir e\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Nikare bikaranîna bîrê bipîvê. Jkx sînorê herî pir yê bîrê diyar bike.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Pêşgira navê pelê pêşbîra demkî\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Paşgir\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Pêşbîra demkî nayê bikaranîn dema ku cureya tomarkirinê li ser derana kesanekirî (FFmpeg) sazkirî be.\"\nBasic.Settings.Output.Simple.SavePath=\"Riya tomarkirinê\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Kalîteya tomarkirinê\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Wekî weşanê\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Kalîteya bilind, mezinahiya pelê ya navîn\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Kalîteya nediyarkirî, mezinahiya pelê yamezin\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Kalîteya bêkêmasî, mezinahiya pelê ya pir mezin\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Hişyarî: Rêjeya bîta vîdyoyê ya ku tê weşandin wê ji bo %1 werê danîn, û ev asta herî bilind e ji karûbarên ku ev weşan pêşkêş dike di dema heyî de.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Hişyarî: Rêjeya bîta dengê ya ku tê weşandin wê ji bo %1 werê danîn, û ev asta herî bilind e ji karûbarên ku ev weşan pêşkêş dike di dema heyî de.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Hişyarî: Ku kalîteya tomarkirinê ku wekî \\\"wekî weşanê\\\" hatiye sazkirin, tomarkirin nayê rawestandin bi awayekî demkî.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Hişyarî: Formata tomarkirinê heyî ya hilbijartî bi (kodkera) weşanê a hilbijartî re ne lihevhatî ye.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Hişyarî: Tomarkirin bi riya bernameyeke kodker di kalîteyeke cuda ji weşanê, dibe sedema ku CPU bêtir werê bikaranîn heke di heman demê tu weşan û tomarkirinê tê bi rê ve bibî.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Tu bawer î ku tu dixwazî kalîteya bêkêmasî bi kar bîne?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Hişyariya kalîteya bêkêmasî!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Nermalav (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Reqalav (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Reqalav (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Reqalav (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Reqalav (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Reqalav (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Reqalav (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Reqalav (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Reqalav (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Reqalav (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Reqalav (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Bernameya (x264 pêşsazkirî ku kêm CPU bi kar bînê, mezinahiyapelê bêtir dike)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Berdest)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD Track (2 herîk bi kar tîne)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Herîka dengê\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Çareserî /rêjeya çarçoveyê (fps) lihevnehatiye\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Ev weşan piştgirî nade çareseriya ku te hilbijartiye di dema heyî de û/an jî rêjeya çarçoveyê. Ew ê werê guherin bo nirxa herî nêzîk a lihevhatî:\\n\\n%1\\n\\nTu dixwazî bidomînî?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Çareserî: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Kodker ne lihevhatî ye\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Ev rajeya weşanê \\\"%1\\\" piştgiriyê nade kodkerê \\\"%2\\\". Kodker wê were guhertin bo \\\"%3\\\".\\n\\nTu dixwazî bidomînî?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Rajeya weşanê \\\"%1\\\" piştgirî nade kodkerên\\\"%2\\\" û \\\"%3\\\". Ev kodker wê werin guhertin bo \\\"%4\\\" û \\\"%5\\\".\\n\\nTu dixwazî ​​bidomînî?\"\nBasic.Settings.Output.VideoBitrate=\"Rêjeya bita vîdyoyê\"\nBasic.Settings.Output.AudioBitrate=\"Rêjeya bita dengê\"\nBasic.Settings.Output.Reconnect=\"Bi xweberî ji nû ve girê bide\"\nBasic.Settings.Output.RetryDelay=\"Derengxistinê ji nû ve biceribîne\"\nBasic.Settings.Output.MaxRetries=\"Ji nû ve ceribandina herî pir\"\nBasic.Settings.Output.Advanced=\"Sazkariyên kodkera kesane çalak bike (Pêşketî)\"\nBasic.Settings.Output.EncoderPreset=\"Pêşsazkirina kodker\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (karanîna YKN ya kêm, kalîteya herî kêm)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (berdest) (karanîna YKN ya navîn, kalîteya standart)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (karanîna YKN ya bilind, kalîteya bilind)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Sazkariyên Muxer ên kesanekirî\"\nBasic.Settings.Output.CustomMuxerSettings=\"Sazkariyên Muxer ên kesanekirî\"\nBasic.Settings.Output.NoSpaceFileName=\"Bêyî valahî navê pelan çê bike\"\nBasic.Settings.Output.Adv.Rescale=\"Deranê ji nû ve bipîve\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Neçalakkirî\"\nBasic.Settings.Output.Adv.AudioTrack=\"Herîka dengê\"\nBasic.Settings.Output.Adv.Streaming=\"Weşan\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Sazkariyên weşanê\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Herîk 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Herîk 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Herîk 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Herîk 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Herîk 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Herîk 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Herîka Twitch VOD\"\nBasic.Settings.Output.Adv.Encoder=\"Sazkariyên kodker\"\nBasic.Settings.Output.Adv.Recording=\"Tomarkirin\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Sazkariyên tomarkirinê\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Cureyê tomarkirinê\"\nBasic.Settings.Output.Adv.Recording.Type=\"Cure\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Derana kesanekirî (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Kodkera weşanê bi kar bîne)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Formûla navê pelan\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Ku pel hebe li ser binivîse\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Cureya deranê FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Deran ji URL re\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Deran ji pelê re\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Formatên tomarkirinê yên hevpar\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Hemû pel\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Riya pelan an jî girêdan\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Formata konteyneran\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Deng\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Vîdyo\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Formata jixweberî\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Danasîna formata konteyneran\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Deng/Vîdyo Codec ji riya pelan an URL tê texmînkirin\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Kodkerê berdest\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Kodker neçalak bike\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Kodkerê vîdyo\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Sazkariyên kodkerê vîdyo (heke hebe)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Kodkerê dengê\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Sazkariyên kodkerê deng (heke hebe)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Sazkariyên guhertina formatê (heke hebê)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Dabeşkera demkî di navbera çarçoveyan de\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Hemî kodekan nîşan bide (her çend potansiyel li hev neke)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Sazkariyên FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Dabeşkirina pelan bi xweberî\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Dabeşkirin bi ya demê\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Dabeşkirin bi ya mezinahiyê\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Dabeşkirin tenê bi destan\"\nBasic.Settings.Output.SplitFile.Time=\"Dema dabeşkirinê\"\nBasic.Settings.Output.SplitFile.Size=\"Mezinahiya dabeşkirinê\"\nScreenshot=\"Derana wêneya dîmenderê\"\nScreenshot.SourceHotkey=\"Wêneya dîmenderê çavkaniya hilbijartî\"\nScreenshot.StudioProgram=\"Wêneya dîmenderê (bername)\"\nScreenshot.Preview=\"Wêneya dîmenderê (pêşdîtin)\"\nScreenshot.Scene=\"Wêneya dîmenderê (dîmen)\"\nScreenshot.Source=\"Wêneya dîmenderê (çavkanî)\"\nFilenameFormatting.TT.CCYY=\"Sal, çar jimar\"\nFilenameFormatting.TT.YY=\"Sal, du jimarên dawî (00-99)\"\nFilenameFormatting.TT.MM=\"Meh wek jimareke dehî (01-12)\"\nFilenameFormatting.TT.DD=\"Roja mehê, bê qat (01-31)\"\nFilenameFormatting.TT.hh=\"Demjimêr di formata 24d de (00-23)\"\nFilenameFormatting.TT.mm=\"Xulek (00-59)\"\nFilenameFormatting.TT.ss=\"Çirke (00-59)\"\nFilenameFormatting.TT.Percent=\"Nîşanek %\"\nFilenameFormatting.TT.a=\"Kurte navê roja hefteyê\"\nFilenameFormatting.TT.A=\"Navê tevahiya hefteyê\"\nFilenameFormatting.TT.b=\"Navê mehê ya kurtkirî\"\nFilenameFormatting.TT.B=\"Navê meha tevahî\"\nFilenameFormatting.TT.d=\"Roja mehê, bê qat (01-31)\"\nFilenameFormatting.TT.H=\"Demjimêr di formata 24d de (00-23)\"\nFilenameFormatting.TT.I=\"Demjimêr di formata 12d de (01-12)\"\nFilenameFormatting.TT.m=\"Meh wek jimareke dehî (01-12)\"\nFilenameFormatting.TT.M=\"Xulek (00-59)\"\nFilenameFormatting.TT.p=\"Nîşankirin BN an jî PN\"\nFilenameFormatting.TT.s=\"Dem di çirkeyan de ji serdema UNIX ve\"\nFilenameFormatting.TT.S=\"Çirke (00-59)\"\nFilenameFormatting.TT.y=\"Sal, du jimarên dawî (00-99)\"\nFilenameFormatting.TT.Y=\"Sal\"\nFilenameFormatting.TT.z=\"ISO 8601 ji UTC di devera demê de veqetandî ye\"\nFilenameFormatting.TT.Z=\"Navê devera demê yan kurtenivîs\"\nFilenameFormatting.TT.FPS=\"Çarçove bo çirke\"\nFilenameFormatting.TT.CRES=\"Çareseriya (Canvas) bingehîn\"\nFilenameFormatting.TT.ORES=\"Derana çareseriyê (Pîvandî)\"\nFilenameFormatting.TT.VF=\"Formata vîdyoyê\"\nBasic.Settings.Video=\"Vîdyo\"\nBasic.Settings.Video.Adapter=\"Adapterê vîdyoyê\"\nBasic.Settings.Video.BaseResolution=\"Çareseriya (Canvas) bingehîn\"\nBasic.Settings.Video.ScaledResolution=\"Derana çareseriyê (Pîvandî)\"\nBasic.Settings.Video.DownscaleFilter=\"Parzûna Downscale\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Çareserî lihevhatî ne, kêmkirina pîvanê pêdivî nake]\"\nBasic.Settings.Video.FPSCommon=\"Nirxên giştî yên FPS\"\nBasic.Settings.Video.FPSInteger=\"Nirxa FPS ya rast\"\nBasic.Settings.Video.FPSFraction=\"Nirxên FPS ên rasyonel\"\nBasic.Settings.Video.Numerator=\"Serbeş\"\nBasic.Settings.Video.Denominator=\"Binbeş\"\nBasic.Settings.Video.InvalidResolution=\"Nirxa çareseriyê nederbasdar. divê wa be [width]x[height] (Mînak: 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Derana vîdyoyê niha çalak e. Ji kerema xwe ji bo guhertina sazkariyên vîdyoyê her kîjan deranan bigire.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (zûtirîn, lê mijbarî ye heke werê pîvandin)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (pîvana tûjkirî, 16 mînak)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (pîvana tûjkirî, 36 mînak)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Area ( Tevahiya giraniyê, nimûneyên 4/6/9)\"\nBasic.Settings.Audio=\"Deng\"\nBasic.Settings.Audio.SampleRate=\"Rêjeya mînaka\"\nBasic.Settings.Audio.Channels=\"Kanalên\"\nBasic.Settings.Audio.Meters=\"Metre\"\nBasic.Settings.Audio.MeterDecayRate=\"Rêjeya kêmbûnê\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Lezdar\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Navîn (Tîpa I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Hêdî (Tîpa II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Cureya metre ya serî\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Mînaka serî\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True Peak (Bikaranîna CPU ya bilind)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"HIŞYARÎ: Denga derdorê hatiye çalakkirin.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Denga derdorê çalak bike?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Ma tu dixwazî denga derdorê çalak bikî?\"\nBasic.Settings.Audio.Devices=\"Amûrên dengê yên gerdûnî\"\nBasic.Settings.Audio.DesktopDevice=\"Dengê sermaseyê\"\nBasic.Settings.Audio.DesktopDevice2=\"Dengê sermaseyê 2\"\nBasic.Settings.Audio.AuxDevice=\"Dengê Mic/Auxiliary\"\nBasic.Settings.Audio.AuxDevice2=\"Dengê Mic/Auxiliary 2\"\nBasic.Settings.Audio.AuxDevice3=\"Dengê Mic/Auxiliary 3\"\nBasic.Settings.Audio.AuxDevice4=\"Dengê Mic/Auxiliary 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Push-to-mute çalak bike\"\nBasic.Settings.Audio.PushToMuteDelay=\"Push-to-mute dereng bixe\"\nBasic.Settings.Audio.EnablePushToTalk=\"Push-to-Talk çalak bike\"\nBasic.Settings.Audio.PushToTalkDelay=\"Push-to-talk dereng bixe\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Amûr negirêdayî ye an jî ne berdest e]\"\nBasic.Settings.Audio.Disabled=\"Neçalak\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Awayê pêşbîra demkî ya derengiya dengê kêm (Ji bo deranên Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"Hişyarî: Awayê pêşbîra demkî ya derengiya dengê kêm çalak e.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Awayê pêşbîra demkî ya derengiya dengê kêm dibe ku bibe sedem ku deng ji hinek çavkaniyan qut bibe an lêdanê rawestîne.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Awayê pêşbîra demkî ya derengiya dengê kêm çalak bike?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Tu dixwazî awayê pêşbîra demkî ya derengiya dengê kêm çalak bikî?\"\nBasic.Settings.Accessibility=\"Gihiştin\"\nBasic.Settings.Accessibility.ColorOverrides=\"Rengên cuda bi kar bîne\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Sînorê çavkaniyê (Hilbijartin)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Sînorê çavkaniyê (Qutkirin)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Sînorê çavkaniyê (Rengê çarçoveyê)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Rêjeya dengê tevlihevkar (-60 heya -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Rêjeya dengê tevlihevkar (-20 heya -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Rêjeya dengê tevlihevkar (-9 heya -0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Rêjeya dengê tevlihevkar (-60 heya -20dB) (çalak)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Rêjeya dengê tevlihevkar (-20 heya -9dB) (çalak)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Rêjeya dengê tevlihevkar (-9 heya 0dB) (çalak)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Pêşsazkirina rengê\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Berdest\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Kesanekirî\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternatîva reng kor\"\nBasic.Settings.Advanced=\"Pêşketî\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Di derketinê de hişyariya deranan çalak nîşan bide\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Pêşengiya pêvajoyê\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Bilind\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Ji normal bêhtir\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Ji normal kêmtîr\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Bêkar\"\nBasic.Settings.Advanced.FormatWarning=\"Hişyarî: Formatên rengan ên ji bilî NV12/P010 di serî de ji bo tomarkirinê têne armanckirin, û di dema weşanê de nayê pêşniyarkirin. Weşan dibe sedema pirbûna karanîna YKN ji ber guhertina formatên rengan.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Hişyarî: Formatên pêbawer bi gelemperî bi cihên rengê HDR re têne bikaranîn.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Hişyarî: Rec. 2100 divê formateke lihevhatî bi kar bîne.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Formata rengê\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 duz)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 duz)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 duz)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 duz)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 duz)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 duz)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 duz)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Cihê rengê\"\nBasic.Settings.Advanced.Video.ColorRange=\"Rêjeya rengê\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Sînordar\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Tijî\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Asta spî SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Asta herî bilind a HDR Nominal\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Amûra çavdêriyê\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Berdest\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Windows audio ducking neçalak bike\"\nBasic.Settings.Advanced.StreamDelay=\"Derengxistina weşanê\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Dem\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Dema ku mirov ji nû ve tê girêdan xala qutkirinê (derengbûnê zêde bike) biparêze\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Bikaranîna bîra ya pîvandî: %1 MB\"\nBasic.Settings.Advanced.Network=\"Tor\"\nBasic.Settings.Advanced.Network.Disabled=\"Protokola weşana hilbijartî ya niha piştgirî nade guhertina sazkariyên torê.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Bi IP ve girê bide\"\nBasic.Settings.Advanced.Network.IPFamily=\"Malbata IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Baştirînkirina torê çalak bike\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"TCP pacing çalak bike\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Hewl dide ku rtMP hilberîna hevaltiyê ji bo sepanên din ên hesas ên derengbûnê yên li ser torê bi rêjeya vegûhestinê re bike.\\nDibe ku rîska çarçoveyên hatine avêtin li ser pêwendiyên nediyar zêde bike.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Tevgera balkişandinê ya bişkoka kurterê\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Tu carî bişkoka kurterê neçalak neke\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Dema çarçoveya sereke di balkêşiyê de be, bişkokên kurterê neçalak bike\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Dema çarçoveya sereke ne di balkêşiyê de be, bişkokên kurterê neçalak bike\"\nBasic.Settings.Advanced.AutoRemux=\"Guhertina formatê bi xweberî bo %1 bike\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(wekî mkv tomar bike)\"\nBasic.AdvAudio=\"Taybetiyên dengê yên pêşketî\"\nBasic.AdvAudio.ActiveOnly=\"Tenê çavkaniyên çalak\"\nBasic.AdvAudio.Name=\"Nav\"\nBasic.AdvAudio.Volume=\"Bilindahiya dengê\"\nBasic.AdvAudio.VolumeSource=\"Bilindahiya dengê bo '%1'\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix ji bo '%1'\"\nBasic.AdvAudio.Balance=\"Hevsengî\"\nBasic.AdvAudio.BalanceSource=\"Hevsengî bo '%1'\"\nBasic.AdvAudio.SyncOffset=\"Dûrxisitna hevdemkirinê\"\nBasic.AdvAudio.SyncOffsetSource=\"Ji bo '%1' dûrxisitina hevdemkirinê\"\nBasic.AdvAudio.Monitoring=\"Çavdêriya Dengê\"\nBasic.AdvAudio.Monitoring.None=\"Çavdêrî girtî ye\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Çavdêrî tenê (derana bêdeng)\"\nBasic.AdvAudio.Monitoring.Both=\"Çavdêrî û deran\"\nBasic.AdvAudio.MonitoringSource=\"Çavdêriya dengê ji bo '%1'\"\nBasic.AdvAudio.AudioTracks=\"Herîk\"\nBasic.Settings.Hotkeys=\"Bişkoka kurterê\"\nBasic.Settings.Hotkeys.Pair=\"Kilîtên hevpar ku bi '%1' re hatine parvekirin wekî alavên guhertinê tevdigerin\"\nBasic.Settings.Hotkeys.Filter=\"Parzûn\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Parizandin bi riya kurteyên klavyê\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Ev kurteya klavyê ji hêla yek an çend kiryarên din ve tê parvekirin, bitikîne ku pevçûnan nîşan bidî\"\nBasic.Settings.Hotkeys.PleaseWait=\"Bişkokên kurteriyan tê barkirin, jkx bendê bimîne...\"\nBasic.Hotkeys.SelectScene=\"Biguherîne bo dîmenê\"\nBasic.SystemTray.Show=\"Nîşan bide\"\nBasic.SystemTray.Hide=\"Veşêre\"\nBasic.SystemTray.Message.Reconnecting=\"Girêdan qût bû. Dîsa tê girêdan...\"\nHotkeys.Insert=\"Têxistin\"\nHotkeys.Delete=\"Jêbirin\"\nHotkeys.Home=\"Serrûpel\"\nHotkeys.End=\"Dawî\"\nHotkeys.PageUp=\"Jor de\"\nHotkeys.PageDown=\"Jêr de\"\nHotkeys.NumLock=\"Kilîtê hejmaran\"\nHotkeys.Tab=\"Rûger\"\nHotkeys.Print=\"Çap bike\"\nHotkeys.Pause=\"Rawestîne\"\nHotkeys.Left=\"Çep\"\nHotkeys.Right=\"Rast\"\nHotkeys.Up=\"Jor\"\nHotkeys.Down=\"Jêr\"\nHotkeys.Menu=\"Kulîn\"\nHotkeys.Space=\"Valahî\"\nHotkeys.NumpadNum=\"Klavya hejmara %1\"\nHotkeys.NumpadMultiply=\"Numpad *\"\nHotkeys.NumpadDivide=\"Numpad /\"\nHotkeys.NumpadAdd=\"Numpad +\"\nHotkeys.NumpadSubtract=\"Numpad -\"\nHotkeys.NumpadDecimal=\"Numpad dehekî\"\nHotkeys.MouseButton=\"Mişk %1\"\nMute=\"Bêdeng bike\"\nUnmute=\"Bêdeng neke\"\nPush-to-mute=\"Pêlê bike bo bêdeng bikî\"\nPush-to-talk=\"Pêlê bike bo biaxivî\"\nSceneItemShow=\"'%1' Nîşan bide\"\nSceneItemHide=\"'%1' Veşêre\"\nOutputWarnings.NoTracksSelected=\"Divê herî kêm yek rê hilbijêrî\"\nOutputWarnings.MP4Recording=\"Hişyarî: Tomarkirinên ku di formata MP4/MOV de hatine tomarkirin wê ji nû ve neyên rizgarkirin heke bi dawî nebê (mînakî wekî encama BSOD, windabûna hêzê û hwd.). Ku tu dixwazî gelek herîkê dengê tomar bikî bifikire ku MKV bi kar bîne û piştî qedandina tomarê formata wêê biguherîne bo MP4/MOV (Pel → Guhertina formatên tomarkirinê)\"\nOutputWarnings.CannotPause=\"Hişyarî: Dema ku kodkera tomarkirinê hatiye sazkirin bo\\\"(Kodkera weşanê bi kar bîne)\\\", tomarkirin nayên rawesandin\"\nOutputWarnings.CodecIncompatible=\"Hilbijartina kodkera deng an vîdyo ji ber ku lihevnehatinê hate ji nû ve sazkirin. Ji kerema xwe kodkerek lihevhatî ji rêzokê hilbijêre.\"\nCodecCompat.Incompatible=\"(Bi %1 re ne lihevhatî ye)\"\nCodecCompat.CodecPlaceholder=\"Kodker hilbijêre...\"\nCodecCompat.ContainerPlaceholder=\"Format hilbijêre...\"\nCodecCompat.CodecMissingOnExit.Title=\"Ti kodker nehatiye hilbijartin\"\nCodecCompat.CodecMissingOnExit.Text=\"Herî kêm kodkereke vîdyo an deng nehatiye sazkirin. Jkx pê ewle be ku ji bo tomarkirin û weşanê kodkeran hilbijêrî.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Ti format nehatiye hilbijartin\"\nCodecCompat.ContainerMissingOnExit.Text=\"Ti formata tomarkirinê nehatiye hilbijartin. Jkx formateke tomarkirinê hilbijêre ku bi hilbijartî kodkera weşanê re lihevhatî be.\"\nFinalScene.Title=\"Jêbirina dîmenê\"\nFinalScene.Text=\"Divê herî kêm yek dîmen hebê\"\nNoSources.Title=\"Çavkanî tune\"\nNoSources.Text=\"Xuya dike ku te hin tu çavkaniyên vîdyoyê tevlî nekiriye, ji ber vê yekê tu yê tenê dîmenderekê vala derbixî. Tu dixwazî vê bikî?\"\nNoSources.Text.AddSource=\"Tu dikarî çavkaniyan tevlî bikî û di her demê de di çarçoveya serekede de li bin kodika çavkaniyan de bi riya tikandinê li ser îkona +.\"\nNoSources.Label=\"Tu çavkanî tune.\\n Bişkojka + jêrîn bitikîne ,\\nan jî ji bo tevlîkirina yekê li vir bitikine.\"\nChangeBG=\"Rengê saz bike\"\nCustomColor=\"Rengê kesanekirî\"\nBrowserSource.EnableHardwareAcceleration=\"Lezbûna reqalavê ya gerokê çalak bike\"\nAbout=\"Derbar\"\nAbout.Info=\"OBS Studio nermalava tomarkirin û weşandina zindî ya belaş û çavkaniya azad e.\"\nAbout.Donate=\"Beşdariyekê bike\"\nAbout.GetInvolved=\"Tevlî bibe\"\nAbout.Authors=\"Nivîskar\"\nAbout.License=\"Lîsans\"\nAbout.Contribute=\"Piştgriya projeya OBS bike\"\nAddUrl.Title=\"Çavkanî bi riya girêdanê tevlî bike\"\nAddUrl.Text=\"Te girêdanek kêşand OBS. Ev ê bixweber girêdanê wekî çavkanî tevlî bike. Bidomîne?\"\nAddUrl.Text.Url=\"Girêdan %1\"\nResizeOutputSizeOfSource=\"Mezinahiya deranê ji nû ve saz bike (mezinahiya çavkaniyê)\"\nResizeOutputSizeOfSource.Text=\"Pîvana bingehîn û çareseriyên deranê wê ji nû ve werin sazkirin bo mezinahiya çavkaniya heyî.\"\nResizeOutputSizeOfSource.Continue=\"Tu dixwazî bidomînî?\"\nPreviewTransition=\"Pêşdîtina derbasbûnê\"\nImporter=\"Têxistinera berhevoka dîmenê\"\nImporter.SelectCollection=\"Berhevokeke dîmenan hilbijêre\"\nImporter.Collection=\"Berhevoka dîmenan\"\nImporter.HelpText=\"Pelan tevlî vê çarçoveyê bike da ku têxisitinê bikî ji berhevokên OBS an bernameyên din ên ku tên piştgirîkirin.\"\nImporter.Path=\"Riya berhevokê\"\nImporter.Program=\"Sepana dîtî\"\nImporter.AutomaticCollectionPrompt=\"Bi xweberî li berhevokên dîmenan bigere\"\nImporter.AutomaticCollectionText=\"OBS dikare bi xwe berhevokên dîmenan ên ji bernameyên sêyemîn ên alîgir bibîne. Tu dixwezî OBS bi xweber berhevokên ji bo te bibîne?\\n\\nTu dikarî vê paşê di Sazkarî > Giştî > Têxistin de biguherînî.\"\nImporter.SelectFile=\"Bigere...\"\nRestart=\"Ji nû ve destpêkirin\"\nNeedsRestart=\"OBS Studio divê ji nû ve bê destpêkirin. tu dixwazî ji nû ve dest pê bikî niha?\"\nLoadProfileNeedsRestart=\"Di profîlê de sazkarî hene ku pêdiviya bi ji nû ve destpêkirina OBS dike :\\n%1\\n\\n Ma tu dixwazî OBS ji nû ve bidî destpêkirin ji bo ku ev sazkarî bi bandorbar be?\"\nContextBar.NoSelectedSource=\"Ti çavkanî nehate hilbijartin\"\nContextBar.MediaControls.PlayMedia=\"Medya lê de\"\nContextBar.MediaControls.PauseMedia=\"Medya rawestîne\"\nContextBar.MediaControls.StopMedia=\"Medya bi dawî bike\"\nContextBar.MediaControls.RestartMedia=\"Medya ji nû ve dest pê bike\"\nContextBar.MediaControls.PlaylistNext=\"A pêş di rêzoka lêdanan de\"\nContextBar.MediaControls.PlaylistPrevious=\"A paş di rêzoka lêdanan de\"\nContextBar.MediaControls.BlindSeek=\"Alava lêgerînê bo medyayê\"\nYouTube.Auth.Ok=\"Mafdayîn bi awayekî serkeftî qediya.\\nTu niha dikarî vê rûpelê bigire.\"\nYouTube.Auth.NoCode=\"Pêvajoya mafdayînê ne qediya ye.\"\nYouTube.Auth.NoChannels=\"Tu kanalên heyî tune ne li ser ajimêra hilbijartî\"\nYouTube.Auth.WaitingAuth.Title=\"Rastandina bikarhêner a YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Ji kerema xwe rastandinê di geroka xwe ya derveyî de bidomîne.<br>Ku geroka derve veneke, vê girêdanê bişopîne û rastandinê bidomîne:<br>%1\"\nYouTube.AuthError.Text=\"Nikare zanyariyên kanalê bi dest bixê: %1.\"\nYouTube.Actions.WindowTitle=\"Sazkirina Weşana YouTube - Kanal: %1\"\nYouTube.Actions.CreateNewEvent=\"Weşaneke nû biafrîne\"\nYouTube.Actions.ChooseEvent=\"Weşaneke heyî hilbijêre\"\nYouTube.Actions.Title=\"Sernav\"\nYouTube.Actions.MyBroadcast=\"Weşana min\"\nYouTube.Actions.Description=\"Danasîn\"\nYouTube.Actions.Privacy=\"Nihênî\"\nYouTube.Actions.Privacy.Private=\"Taybet\"\nYouTube.Actions.Privacy.Public=\"Gelemperî\"\nYouTube.Actions.Privacy.Unlisted=\"Nerêzokkirî\"\nYouTube.Actions.Category=\"Beş:\"\nYouTube.Actions.Thumbnail=\"Wêneya biçûk\"\nYouTube.Actions.Thumbnail.SelectFile=\"Pelê hilbijêre...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Tu pel nehatiye hilbijartin\"\nYouTube.Actions.Thumbnail.ClearFile=\"Pak bike\"\nYouTube.Actions.MadeForKids=\"Ma ev vîdyo ji bo zarokan hatiye çêkirin? *\"\nYouTube.Actions.MadeForKids.Yes=\"Erê, ji bo zarokan hatiye çêkirin\"\nYouTube.Actions.MadeForKids.No=\"Na, ji bo zarokan nehatiye çêkirin\"\nYouTube.Actions.AdditionalSettings=\"Sazkariyên vebijêrkî\"\nYouTube.Actions.Latency=\"Derengxistin\"\nYouTube.Actions.Latency.Low=\"Kêm\"\nYouTube.Actions.Latency.UltraLow=\"Pir kêm\"\nYouTube.Actions.EnableAutoStart=\"Destpêkirina bi xweberî çalak bike\"\nYouTube.Actions.EnableAutoStop=\"Rawestandina bi xweberî çalak bike\"\nYouTube.Actions.AutoStartStop.TT=\"Diyar dike ku divê ev weşana demsazkirîkirî bixweber dest pê bike\"\nYouTube.Actions.EnableDVR=\"DVR çalak bike\"\nYouTube.Actions.360Video=\"360 vîdyo\"\nYouTube.Actions.ScheduleForLater=\"Demsaz bike bo paşê\"\nYouTube.Actions.RememberSettings=\"Van sazkariyan bi bîr bîne\"\nYouTube.Actions.Create_Ready=\"Weşanê biafirîne\"\nYouTube.Actions.Create_GoLive=\"Weşanê biafirîne û dest bi weşana zindî bike\"\nYouTube.Actions.Choose_Ready=\"Weşanê hilbijêre\"\nYouTube.Actions.Choose_GoLive=\"Weşanê hilbijêre û dest bi weşana zindî bike\"\nYouTube.Actions.Create_Schedule=\"Weşanê demsaz bike\"\nYouTube.Actions.Create_Schedule_Ready=\"Weşanê demsaz bike û dest bi weşana zindî bike\"\nYouTube.Actions.Dashboard=\"YouTube Studio veke\"\nYouTube.Actions.Error.Title=\"Çewtiya afirandina weşana zindî\"\nYouTube.Actions.Error.Text=\"Çewtiya gihîştina YouTube '%1'.<br/>Danasîna çewtiyê ya berfireh dikarin bêne dîtin li <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Çewtiya gihîştina YouTube. Ji kerema xwe girêdana torê an gihiştîna rajekera YouTube-ê venêre.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Çewtiya afirandina weşana zindî '%1'.<br/>Danasîna çewtiyê ya berfireh dikarin bêne dîtin li <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Tu weşan nehatine afirandin. Ji kerema xwe ajimêra xwe dîsa girê bide\"\nYouTube.Actions.Error.YouTubeApi=\"Çewtiya API ya YouTube-ê. Jkx bo bêhtir zanyarî pêlê têketinan binêre.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Weşana bijartî nehate dîtin.\"\nYouTube.Actions.Error.FileMissing=\"Pelê hilbijartî tune ye.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Vekirina pelê hilbijartî têkçû\"\nYouTube.Actions.Error.FileTooLarge=\"Pelê hilbijartî pir mezin e (Sînor: 2MB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Derbasbûna weşanê têkçû: %1 <br/><br/>heke ev çewtî bidome <a href='https://studio.youtube.com/video/%2/livestreaming'>weşanê di Youtube Studio de veke</a> û bi destan bicerbîne.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Weşan di qonaxa ceribandinê de derbas dibe, ev dikare hin dem bigire. Ji kerema xwe di 10-30 çirkeyan de dîsa biceribîne.\"\nYouTube.Actions.EventsLoading=\"Rêzoka bûyeran tê barkirin...\"\nYouTube.Actions.EventCreated.Title=\"Bûyerek hate afirandin\"\nYouTube.Actions.EventCreated.Text=\"Bûyerek bi awayekî serkeftî hate afirandin\"\nYouTube.Actions.Stream=\"Weşan\"\nYouTube.Actions.Stream.ScheduledFor=\"Demsazkirî bo %1\"\nYouTube.Actions.Stream.Resume=\"Weşana hatiye qutkirin ji nû ve bidomîne\"\nYouTube.Actions.Stream.YTStudio=\"Ji hêla Studyoya YouTube ve bixweber tê afirandin.\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Weşana zindî ya nû tê afirandin, ji kerema xwe re li bendê bimîne...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Destpêkirina bi destan pêdivî dike\"\nYouTube.Actions.AutoStartStreamingWarning=\"Destpêkirina bi xweberî neçalak e bo vê bûyerê, li ser \\\"Biçe zindî\\\" bitikîne da ku tu weşana xwe bidî destpêkirin\"\nYouTube.Actions.AutoStopStreamingWarning=\"Tu nikarî ji nû ve girê bidî. <br> Wê weşana te rawestê û weşana te ya zindî qut bibe.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Weşana zindî li ser kanala YouTube a bijarte nayê çalakkirin.<br/><br/>Binêre<a href='https://www.youtube.com/features'>youtube.com/features</a> bo bêhtir zanyarî.\"\nYouTube.Errors.livePermissionBlocked=\"Weşana zindî li ser Kanala YouTube ya bijartî tune. <br/> Bizane vê yekê dibe ku 24 demjimêran bidome da ku weşana zindî piştî ku tu di sazkariyên kanala xwe de çalak bikî peyda bibe.<br/><br/> Binêre <a href='https://www.youtube.com/features'>youtube.com/features</a> ji bo hûrguliyan.\"\nYouTube.Errors.errorExecutingTransition=\"Derbasbûn ji ber çewtiyeke paşrûyî têk çû. Ji kerema xwe di çend çirkeyan de dîsa biceribîne.\"\nYouTube.Errors.errorStreamInactive=\"Toutube daneyan ji weşanê te nastîne. Jkx pevsazkirina xwe kontrol bike.\"\nYouTube.Errors.invalidTransition=\"Hewldana derbasbûnê ne derbasdar bû. Ev dibe ku ji ber ku weşanê derbasbûneke berê neqedandiye. Ji kerema xwe çend çirkeyan bimîne û dîsa biceribîne.\"\nConfigDownload.WarningMessageTitle=\"Hişyarî\"\nFailedToStartStream.NoConfigSupplied=\"Rêkxistinê wendayî\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Deng → Giştî → Kenal] divê bo '%1' were sazkirin\"\nBasic.OpenPluginManager=\"Rêveberiya pêvekê\"\nPluginManager=\"Rêveberiya pêvekê\"\nPluginManager.Restart=\"Ji bo sepandina guhertinên pêvekan divê OBS Studio ji nû ve were destpêkirin\"\nPluginManager.MissingPlugin=\"[PÊVEK NEHAT DÎTIN]\"\nPluginManager.Section.Discover=\"Bigere\"\nPluginManager.Section.Manage=\"Sazkirî\"\nPluginManager.Section.Updates=\"Rojanekirin\"\nPluginManager.Section.Manage.Title=\"Pêvekên çalakkirî bi rê ve bibe\"\n"
  },
  {
    "path": "frontend/data/locale/ko-KR.ini",
    "content": "Language=\"한국어\"\nOK=\"확인\"\nApply=\"적용\"\nCancel=\"취소\"\nClose=\"닫기\"\nSave=\"저장\"\nDiscard=\"저장 안함\"\nDisable=\"비활성화\"\nYes=\"예\"\nNo=\"아니오\"\nAdd=\"추가\"\nRemove=\"제거\"\nRename=\"이름 변경\"\nInteract=\"상호작용\"\nFilters=\"필터\"\nProperties=\"속성\"\nMoveUp=\"위로 올리기\"\nMoveDown=\"아래로 내리기\"\nSettings=\"설정\"\nDisplay=\"디스플레이\"\nName=\"이름\"\nExit=\"끝내기\"\nMixer=\"오디오 믹서\"\nBrowse=\"찾아보기\"\nMono=\"모노\"\nStereo=\"스테레오\"\nDroppedFrames=\"손실된 프레임 %1 (%2%)\"\nProjector.Open.Program=\"프로그램 프로젝터 열기\"\nProjector.Open.Preview=\"미리보기 프로젝터 열기\"\nProjector.Open.Scene=\"신 프로젝터 열기\"\nProjector.Open.Source=\"소스 프로젝터 열기\"\nProjector.Open.Multiview=\"멀티뷰 열기\"\nProjector.Display=\"디스플레이: %1\"\nProjector.Window=\"새 창\"\nProjector.Title=\"프로젝터\"\nProjector.Title.Scene=\"장면: %1\"\nProjector.Title.Source=\"소스: %1\"\nProjector.Title.Multiview=\"다중화면\"\nProjector.ResizeWindowToContent=\"창 크기를 컨텐츠에 맞춤\"\nClear=\"삭제\"\nRevert=\"복구\"\nShow=\"보이기\"\nHide=\"숨기기\"\nUnhideAll=\"모두 표시\"\nUntitled=\"제목 없음\"\nNew=\"새로 만들기\"\nDuplicate=\"복제\"\nEnable=\"활성화\"\nDisableOSXVSync=\"macOS 수직동기화 끄기\"\nResetOSXVSyncOnExit=\"종료 시 macOS 수직동기화 재설정\"\nHighResourceUsage=\"인코딩 과부하! 비디오 설정을 낮추거나 더 빠른 인코딩 사전설정을 사용하는 것을 고려하세요.\"\nTransition=\"화면 전환\"\nQuickTransitions=\"빠른 화면 전환\"\nFadeToBlack=\"검정색으로 사라지기\"\nLeft=\"왼쪽\"\nRight=\"오른쪽\"\nTop=\"위\"\nBottom=\"아래\"\nReset=\"초기화\"\nHours=\"시간\"\nMinutes=\"분\"\nSeconds=\"초\"\nDeprecated=\"사용되지 않음\"\nReplayBuffer=\"리플레이 버퍼\"\nImport=\"가져오기\"\nExport=\"내보내기\"\nCopy=\"복사\"\nPaste=\"붙여넣기\"\nPasteReference=\"붙여넣기 (참조)\"\nPasteDuplicate=\"붙여넣기 (복제)\"\nRemuxRecordings=\"녹화본 Remux\"\nNext=\"다음\"\nBack=\"이전\"\nDefaults=\"기본값\"\nRestoreDefaults=\"기본값\"\nHideMixer=\"믹서에서 숨기기\"\nTransitionOverride=\"이 전환을 우선 적용\"\nShowTransition=\"표시 화면전환\"\nHideTransition=\"숨김 화면전환\"\nNone=\"없음\"\nStudioMode.Preview=\"미리보기\"\nStudioMode.Program=\"프로그램\"\nStudioMode.PreviewSceneName=\"미리보기: %1\"\nStudioMode.ProgramSceneName=\"프로그램: %1\"\nShowInMultiview=\"다중화면으로 표시\"\nVerticalLayout=\"수직 배치\"\nGroup=\"그룹\"\nDoNotShowAgain=\"다시 표시하지 않기\"\nDefault=\"(기본값)\"\nCalculating=\"계산중...\"\nFullscreen=\"전체화면\"\nWindowed=\"창 모드\"\nRefreshBrowser=\"새로고침\"\nAspectRatio=\"화면비율 <b>%1:%2</b>\"\nLockVolume=\"음량 잠그기\"\nLogViewer=\"로그 보기\"\nShowOnStartup=\"시작 시 보기\"\nOpenFile=\"파일 열기\"\nAddScene=\"장면 추가\"\nAddSource=\"소스 추가\"\nRemoveScene=\"선택한 장면 제거\"\nRemoveSource=\"선택한 소스 제거\"\nMoveSceneUp=\"장면 위로 이동\"\nMoveSceneDown=\"장면 아래로 이동\"\nMoveSourceUp=\"소스 위로 이동\"\nMoveSourceDown=\"소스 아래로 이동\"\nSourceProperties=\"소스 속성 열기\"\nSourceFilters=\"소스 필터 열기\"\nMixerToolbarMenu=\"오디오 믹서 메뉴\"\nSceneFilters=\"장면 필터 열기\"\nList=\"목록\"\nGrid=\"격자\"\nAutomatic=\"자동\"\nComingSoon=\"곧 출시\"\nPluginsFailedToLoad.Title=\"플러그인 로딩 오류\"\nPluginsFailedToLoad.Text=\"다음 플러그인을 로딩하는데 실패했습니다:\\n\\n%1\\n이 플러그인을 업데이트 하거나 삭제하십시오.\"\nAlreadyRunning.Title=\"OBS가 이미 실행 중입니다\"\nAlreadyRunning.Text=\"OBS가 이미 실행 중입니다! 의도한 것이 아니라면 새로운 OBS를 실행하기 전에 이미 동작 중인 프로그램을 종료하십시오. OBS가 시스템 트레이에 최소화되어 있는지도 확인하십시오.\"\nAlreadyRunning.LaunchAnyway=\"경고를 무시하고 실행\"\nCrashHandling.Dialog.Title=\"OBS Studio의 비정상 종료가 감지됨\"\nCrashHandling.Labels.Text=\"OBS Studio가 비정상적으로 종료되었습니다.\\n\\n안전 모드로 실행하시겠습니까? (서드파티 플러그인, 스크립트 및 WebSocket 비활성화됨) \"\nCrashHandling.Labels.PrivacyNotice=\"비정상 종료 보고서를 OBSProject에 자동으로 업로드하도록 선택할 수도 있습니다. .<br /><br />파일을 업로드하기 전에 반드시 <a href='https://obsproject.com/privacy-policy'>개인정보 보호정책</a>을 읽어보시고, 파일 업로드와 관련된 부분을 특히 주의 깊게 확인하시기 바랍니다.\"\nCrashHandling.Checkbox.SendReport=\"개인정보 처리방침을 읽었으며 업로드에 동의합니다.\"\nCrashHandling.Buttons.LaunchSafe=\"안전 모드로 실행\"\nCrashHandling.Buttons.LaunchNormal=\"일반 모드로 실행\"\nCrashHandling.Errors.UploadJSONError=\"가장 최근의 비정상 종료 로그를 업로드하는 중 오류가 발생했습니다. 나중에 다시 시도해 주세요.\"\nCrashHandling.Errors.Title=\"비정상 종료 업로드 오류\"\nSafeMode.Restart=\"안전모드에서 OBS를 다시 시작하시겠습니까? (3자 제공 플러그인, 스크립트, 웹소켓 꺼짐)\"\nSafeMode.RestartNormal=\"OBS를 일반 모드로 다시 실행하시겠습니까?\"\nChromeOS.Title=\"지원하지 않는 플랫폼\"\nChromeOS.Text=\"OBS가 ChromeOS 컨테이너 안에서 작동 중입니다. 이 플랫폼은 지원되지 않습니다.\"\nWine.Title=\"Wine 감지됨\"\nWine.Text=\"Wine에서 OBS를 구동하는 것은 지원되지 않으며, 캡처나 장치 소스 등의 많은 기능이 동작하지 않거나 제한됩니다.<br><br>OBS를 네이티브 버전으로 이용하는 것을 권장합니다. <a href='https://flathub.org/apps/details/com.obsproject.Studio'>Flatpak 버전</a> 혹은 사용 중인 운영 체제에 해당하는 패키지를 이용하세요.\"\nDockCloseWarning.Title=\"도킹 가능한 창을 닫는 중입니다\"\nDockCloseWarning.Text=\"도킹 가능한 창을 닫았습니다. 다시 보려면 메뉴 바에서 독 메뉴를 확인하세요.\"\nExtraBrowsers=\"사용자 브라우저 독\"\nExtraBrowsers.Info=\"이름과 URL을 부여하여 독을 추가할 수 있으며, 작업 후 적용 혹은 닫기 단추를 누르면 됩니다. 언제든지 독을 추가하거나 제거할 수 있습니다.\"\nExtraBrowsers.DockName=\"독 이름\"\nAuth.Authing.Title=\"인증 처리 중...\"\nAuth.Authing.Text=\"%1 인증 처리 중, 잠시만 기다려 주세요...\"\nAuth.AuthFailure.Title=\"인증 실패\"\nAuth.AuthFailure.Text=\"%1에서 인증을 실패했습니다:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"인증이 필요합니다\"\nAuth.InvalidScope.Text=\"%1 의 인증 요구사항이 변경되었습니다. 일부 기능이 제한될 수 있습니다.\"\nAuth.LoadingChannel.Title=\"채널 정보를 읽는 중...\"\nAuth.LoadingChannel.Text=\"%1 채널 정보를 읽는 중, 잠시만 기다려 주세요...\"\nAuth.LoadingChannel.Error=\"채널 정보를 불러올 수 없음.\"\nAuth.ChannelFailure.Title=\"채널을 불러오지 못했습니다\"\nAuth.ChannelFailure.Text=\"%1의 채널 정보를 불러오는 데 실패했습니다\\n\\n%2: %3\"\nAuth.Chat=\"채팅\"\nAuth.StreamInfo=\"스트림 정보\"\nTwitchAuth.Stats=\"Twitch 통계\"\nTwitchAuth.Feed=\"트위치 활동 피드\"\nTwitchAuth.TwoFactorFail.Title=\"스트림 키를 질의할 수 없습니다\"\nTwitchAuth.TwoFactorFail.Text=\"OBS가 사용자의 Twitch 계정에 접속할 수 없습니다. <a href='https://www.twitch.tv/settings/security'>Twitch 보안 설정</a> 에서 이중 인증을 설정해야 방송이 가능합니다.\"\nRestreamAuth.Channels=\"Restream 채널\"\nCopy.Filters=\"필터 복사\"\nPaste.Filters=\"필터 붙여넣기\"\nBrowserPanelInit.Title=\"브라우저를 시작합니다...\"\nBrowserPanelInit.Text=\"브라우저를 초기화하는 중, 잠시만 기다려 주세요...\"\nBandwidthTest.Region=\"지역\"\nBandwidthTest.Region.US=\"미국\"\nBandwidthTest.Region.EU=\"유럽\"\nBandwidthTest.Region.Asia=\"아시아\"\nBandwidthTest.Region.Other=\"기타\"\nBasic.AutoConfig=\"구성 마법사\"\nBasic.AutoConfig.ApplySettings=\"설정 적용\"\nBasic.AutoConfig.StartPage=\"사용 정보\"\nBasic.AutoConfig.StartPage.SubTitle=\"이 프로그램으로 무엇을 할지 알려주세요\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"스트림 최적화, 녹화는 부차적으로 사용\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"녹화 최적화, 방송은 하지 않음\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"가상 카메라만 사용할 예정입니다\"\nBasic.AutoConfig.VideoPage=\"비디오 설정\"\nBasic.AutoConfig.VideoPage.SubTitle=\"사용하고자 하는 영상 설정을 지정하세요\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"현재 설정 사용 (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"디스플레이 %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"현재 설정 사용 (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 혹은 30, 하지만 가능하면 60을 선호\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 혹은 30, 하지만 높은 해상도를 선호\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"참고: 캔버스 (기본) 해상도는 방송 혹은 녹화하려는 해상도와 반드시 같을 필요는 없습니다. 실제 방송/녹화 해상도는 시스템 리소스 혹은 비트레이트 요구 기준에 맞춰서 캔버스 해상도가 축소될 수도 있습니다.\"\nBasic.AutoConfig.StreamPage=\"스트림 정보\"\nBasic.AutoConfig.StreamPage.SubTitle=\"스트림 정보를 입력하세요\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"계정 연결 (권장)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"계정 연결 끊기\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"해당 계정과 연결을 끊겠습니까?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"이 변경 사항은 즉시 적용됩니다. 정말로 계정과 연결을 중단하겠습니까?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"스트림 키 받기\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"자세한 정보\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"스트림 키 사용\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"스트림 키 사용 (고급)\"\nBasic.AutoConfig.StreamPage.Service=\"서비스\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"모두 보기...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"사용자 지정...\"\nBasic.AutoConfig.StreamPage.Server=\"서버\"\nBasic.AutoConfig.StreamPage.StreamKey=\"스트림 키\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: 암호화 구문을 입력하십시오. \\nRTMP: 서비스에서 제공하는 키를 입력하십시오. \\nSRT: 서비스에서 사용하는 streamid가 있다면 입력하십시오.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"인코더 키\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Bearer 토큰\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"연결된 계정\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"대역폭 검사로 비트레이트 계산 (몇 분 정도 걸릴 수 있음)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"하드웨어 인코딩 선호\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"하드웨어 인코딩은 CPU 사용량을 대폭 줄일 수 있지만, 소프트웨어 인코딩과 동등한 품질을 달성하려면 더 많은 비트 전송률이 필요합니다.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"방송 경고\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"대역폭 검사 작업은 무작위 영상 데이터를 사용자의 방송 채널로 송출합니다. 이 검사가 끝날 때까지 방송 서비스에서 저장 기능을 잠시 끄거나 방송을 비공개로 전환하는 것을 추천합니다. 계속할까요?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"%1 테스트\"\nBasic.AutoConfig.TestPage=\"최종 결과\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"이 프로그램은 가장 이상적인 설정을 찾기 위해서 몇 가지 검사를 실행합니다.\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"검사 완료\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"대역폭 검사 중, 몇 분 정도 걸릴 수 있습니다...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"이 서비스의 프로토콜을 위한 출력 장치를 찾을 수 없음\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"접속 시도 중: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"서버 접속에 실패했습니다. 인터넷 연결을 점검한 다음 다시 시도하세요.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"대역폭 검사를 수행합니다: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"스트림 인코더 시험 중, 몇 분 정도 걸릴 수 있습니다...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"녹화 인코더 시험 중, 몇 분 정도 걸릴 수 있습니다...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"인코더 시작 실패\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"%1x%2 %3 FPS 검사 중...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"스트리밍 인코더\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"녹화 인코더\"\nBasic.AutoConfig.TestPage.Result.Header=\"프로그램에서 선택한 가장 이상적 설정은 다음과 같습니다:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"이 설정을 사용하려면 설정 적용을 누르세요. 구성 마법사를 다시 시작하려면 뒤로 가기를 누르세요. 수동으로 설정하고 싶다면 취소를 누른 다음 설정 창을 확인하세요.\"\nBasic.AutoConfig.Info=\"자동 설정 마법사는 컴퓨터의 사양과 인터넷 속도에 따라 최적의 설정을 선택할 것입니다.\"\nBasic.AutoConfig.RunAnytime=\"이것은 아무 때나 도구 메뉴에서 실행할 수 있습니다.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"스트리밍 (조정된) 해상도\"\nBasic.Stats=\"통계\"\nBasic.Stats.CPUUsage=\"CPU 이용률\"\nBasic.Stats.HDDSpaceAvailable=\"사용 가능 저장 공간\"\nBasic.Stats.MemoryUsage=\"메모리 사용량\"\nBasic.Stats.AverageTimeToRender=\"프레임을 렌더링하는데 필요한 평균 시간\"\nBasic.Stats.SkippedFrames=\"인코딩 작업이 지연되어 건너 뛴 프레임\"\nBasic.Stats.MissedFrames=\"렌더링 작업이 지연되어 손실된 프레임\"\nBasic.Stats.Output.Stream=\"스트림\"\nBasic.Stats.Output.Recording=\"녹화\"\nBasic.Stats.Status=\"상태\"\nBasic.Stats.Status.Recording=\"녹화 중\"\nBasic.Stats.Status.Live=\"라이브\"\nBasic.Stats.Status.Reconnecting=\"다시 연결 중\"\nBasic.Stats.Status.Inactive=\"멈춤\"\nBasic.Stats.Status.Active=\"활성화\"\nBasic.Stats.DroppedFrames=\"손실된 프레임 (네트워크)\"\nBasic.Stats.MegabytesSent=\"데이터 출력 합계\"\nBasic.Stats.Bitrate=\"비트레이트\"\nBasic.Stats.DiskFullIn=\"디스크가 꽉차기 전까지 (추정)\"\nBasic.Stats.DiskFullIn.Text=\"%1 시간, %2 분\"\nBasic.Stats.ResetStats=\"통계 초기화\"\nResetUIWarning.Title=\"정말로 모든 UI를 초기화하겠습니까?\"\nResetUIWarning.Text=\"사용자 인터페이스을 초기화하면 추가 독 설정이 보이지 않습니다. 독 메뉴에서 다시 나타나게 설정할 수 있습니다.\\n\\n사용자 인터페이스를 초기화하겠습니까?\"\nUpdater.Title=\"새로운 업데이트를 사용할 수 있습니다\"\nUpdater.Text=\"새 업데이트가 준비되었습니다:\"\nUpdater.UpdateNow=\"지금 업데이트\"\nUpdater.RemindMeLater=\"나중에 다시 알림\"\nUpdater.Skip=\"업데이트 건너뛰기\"\nUpdater.NoUpdatesAvailable.Title=\"사용 가능한 업데이트가 없습니다\"\nUpdater.NoUpdatesAvailable.Text=\"현재 사용 가능한 업데이트가 없습니다\"\nUpdater.BranchNotFound.Title=\"업데이트 채널 제거됨\"\nUpdater.BranchNotFound.Text=\"선택한 업데이트 채널을 더 이상 사용할 수 없습니다. OBS 기본값으로 재설정되었습니다.\"\nUpdater.RepairButUpdatesAvailable.Title=\"무결성 점검을 할 수 없습니다.\"\nUpdater.RepairButUpdatesAvailable.Text=\"파일 무결성 점검은 가장 최신 버전에서만 가능합니다. 도움말 → 점검 업데이트에서 최신 버전을 확인하고 OBS 설치를 업데이트 하기 바랍니다.\"\nUpdater.RepairConfirm.Title=\"무결성 점검 확인\"\nUpdater.RepairConfirm.Text=\"무결성 점검을 시작하면 OBS 설치를 스캔하여 잘못되거나 수정된 파일들을 다시 다운로드하게 됩니다. 이 작업에는 시간이 소요됩니다.\\n\\n계속 하시겠습니까?\"\nUpdater.FailedToLaunch=\"업데이트 관리자를 실행할 수 없습니다\"\nQuickTransitions.SwapScenes=\"전환 후 미리 보기/프로그램 장면을 교체\"\nQuickTransitions.SwapScenesTT=\"(만약 프로그램 쪽 원본 장면이 있을 때) 전환 작업 이후 미리 보기와 프로그램 장면을 교체합니다.\\n프로그램 쪽 원본 장면에서 변경한 내용은 사라지지 않습니다.\"\nQuickTransitions.DuplicateScene=\"장면 복제\"\nQuickTransitions.DuplicateSceneTT=\"동일한 장면을 편집할 때, 현재 프로그램의 출력 영상에 영향을 미치지 않고 소스를 변형하거나 표시 여부를 변경할 수 있습니다.\\n소스 속성을 프로그램 출력에 영향 없이 편집하려면 '소스 복제'를 활성화하세요.\\n이 값을 변경하면 (이미 존재하는 경우) 현재 프로그램 장면을 재설정합니다.\"\nQuickTransitions.EditProperties=\"소스 복제\"\nQuickTransitions.EditPropertiesTT=\"동일한 장면을 편집할 때, 현재 프로그램의 출력 영상에 영향을 미치지 않고 소스 속성을 수정할 수 있습니다.\\n이는 '장면 복제'가 활성화된 경우에만 사용 가능합니다.\\n특정 소스(캡처/미디어 소스 등)의 경우 이 방식을 지원하지 않으며, 개별 수정이 불가능합니다.\\n이 값을 변경하면 (이미 존재하는 경우) 현재 프로그램 장면을 재설정합니다.\\n\\n경고: 소스가 복제되므로, 추가적 시스템/비디오 자원이 필요할 수 있습니다.\"\nQuickTransitions.HotkeyName=\"빠른 화면 전환: %1\"\nBasic.AddTransition=\"설정 가능한 화면 전환 추가\"\nBasic.RemoveTransition=\"설정 가능한 화면 전환 제거\"\nBasic.TransitionProperties=\"화면 전환 속성\"\nBasic.SceneTransitions=\"장면 전환\"\nBasic.TransitionDuration=\"지속 시간\"\nBasic.TogglePreviewProgramMode=\"스튜디오 모드\"\nBasic.EnablePreviewProgramMode=\"스튜디오 모드 켜기\"\nBasic.DisablePreviewProgramMode=\"스튜디오 모드 끄기\"\nUndo.Undo=\"실행 취소\"\nUndo.Redo=\"다시 실행\"\nUndo.Add=\"'%1' 추가\"\nUndo.Delete=\"'%1' 삭제\"\nUndo.Rename=\"'%1' 이름 변경\"\nUndo.SceneCollection.Switch=\"'%1'(으)로 전환\"\nUndo.Item.Undo=\"실행 취소: %1\"\nUndo.Item.Redo=\"다시 실행: %1\"\nUndo.Sources.Multi=\"영상 소스 %1개 삭제\"\nUndo.Filters=\"'%1' 효과 필터 변경\"\nUndo.Filters.Paste.Single=\"필터 '%1'을(를) '%2'(으)로 붙여넣기\"\nUndo.Filters.Paste.Multiple=\"'%1'의 필터를 '%2'(으)로 복사하기\"\nUndo.Transform=\"'%1' 영상 소스 변환\"\nUndo.Transform.Paste=\"'%1'에 변환 붙여넣기\"\nUndo.Transform.Rotate=\"'%1' 회전\"\nUndo.Transform.Reset=\"'%1' 변환 초기화\"\nUndo.Transform.HFlip=\"'%1' 수평 반전\"\nUndo.Transform.VFlip=\"'%1' 수직 반전\"\nUndo.Transform.FitToScreen=\"'%1' 화면에 맞추기\"\nUndo.Transform.StretchToScreen=\"'%1' 화면 맞춤\"\nUndo.Transform.Center=\"'%1' 화면 중앙 맞춤\"\nUndo.Transform.VCenter=\"'%1' 화면 수직 중앙 맞춤\"\nUndo.Transform.HCenter=\"'%1' 화면 수평 중앙 맞춤\"\nUndo.Volume.Change=\"'%1' 소리 크기 변경\"\nUndo.Volume.Mute=\"'%1' 음소거\"\nUndo.Volume.Unmute=\"'%1' 음소거 해제\"\nUndo.Balance.Change=\"'%1' 오디오 밸런스 변경\"\nUndo.SyncOffset.Change=\"'%1' 오디오 싱크 오프셋 변경\"\nUndo.MonitoringType.Change=\"'%1' 오디오 모니터링 변경\"\nUndo.Mixers.Change=\"'%1' 오디오 믹서 변경\"\nUndo.ForceMono.On=\"'%1' 모노 강제 활성화\"\nUndo.ForceMono.Off=\"'%1' 모노 강제 비활성화\"\nUndo.Properties=\"'%1' 속성 변경\"\nUndo.Scene.Duplicate=\"'%1' 장면 복제\"\nUndo.ShowTransition=\"'%1' 표시 화면 전환\"\nUndo.HideTransition=\"'%1' 숨김 화면 전환\"\nUndo.ShowSceneItem=\"'%2'에서 '%1' 보이기\"\nUndo.HideSceneItem=\"'%2'에서 '%1' 숨기기\"\nUndo.ReorderSources=\"'%1'의 소스 재정렬\"\nUndo.MoveUp=\"'%2' 에서 '%1' 한 칸 올리기\"\nUndo.MoveDown=\"'%2' 에서 '%1' 한 칸 내리기\"\nUndo.MoveToTop=\"'%2' 에서 '%1' 맨 위로 올리기\"\nUndo.MoveToBottom=\"'%2' 에서 '%1' 맨 아래로 내리기\"\nUndo.PasteSource=\"'%1'에 소스 붙여넣기\"\nUndo.PasteSourceRef=\"'%1'에 소스 참조 붙여넣기\"\nUndo.GroupItems=\"'%1'(으)로 항목 묶기\"\nTransitionNameDlg.Text=\"화면 전환 이름을 입력하세요\"\nTransitionNameDlg.Title=\"화면 전환 이름\"\nTitleBar.SafeMode=\"안전 모드\"\nTitleBar.PortableMode=\"호환성 모드\"\nTitleBar.Profile=\"프로파일\"\nTitleBar.Scenes=\"장면\"\nNameExists.Title=\"해당 이름이 이미 존재합니다\"\nNameExists.Text=\"이미 사용 중인 이름입니다.\"\nNoNameEntered.Title=\"올바른 이름을 입력하세요\"\nNoNameEntered.Text=\"이름을 반드시 지정해야 합니다.\"\nConfirmStart.Title=\"방송 송출을 시작할까요?\"\nConfirmStart.Text=\"정말로 방송을 시작하시겠습니까?\"\nConfirmStop.Title=\"방송 송출을 중지할까요?\"\nConfirmStop.Text=\"정말로 방송을 중단하시겠습니까?\"\nConfirmStopRecord.Title=\"녹화를 중단할까요?\"\nConfirmStopRecord.Text=\"정말로 녹화를 중단하시겠습니까?\"\nConfirmBWTest.Title=\"대역폭 점검 시작 확인\"\nConfirmBWTest.Text=\"OBS가 대역폭 시험 상태입니다. 방송을 송출하지 않고 네트워크를 점검할 수 있습니다. 시험을 종료해야 시청자가 방송을 볼 수 있습니다.\\n\\n계속하겠습니까?\"\nConfirmExit.Title=\"활성 출력\"\nConfirmExit.Text=\"OBS는 아직 활성입니다. 모든 스트림/레코딩은 꺼질 것입니다.\"\nConfirmRemove.Title=\"제거 확인\"\nConfirmRemove.Text=\"'%1'을(를) 정말로 제거할까요?\"\nConfirmRemove.TextMultiple=\"정말로 %1 개의 항목을 제거하겠습니까?\"\nConfirmReset.Title=\"속성 초기화\"\nConfirmReset.Text=\"정말 현재 속성을 기본값으로 초기화하시겠습니까?\"\nOutput.StartStreamFailed=\"방송을 시작하지 못했습니다\"\nOutput.StartRecordingFailed=\"녹화를 시작하지 못했습니다\"\nOutput.StartReplayFailed=\"리플레이 버퍼를 시작하지 못했습니다\"\nOutput.StartVirtualCamFailed=\"가상 카메라 시작 실패\"\nOutput.StartFailedGeneric=\"출력을 시작하지 못했습니다. 로그 파일을 확인하세요.\\n\\n참고: NVENC나 AMD 인코더를 사용 중이라면 드라이버가 최신 버전인지 확인하세요.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"일시정지 상태에서는 리플레이를 저장할 수 없습니다\"\nOutput.ReplayBuffer.PauseWarning.Text=\"경고: 녹화가 일시정지되어 있는 상태에서는 리플레이를 저장할 수 없습니다.\"\nOutput.ConnectFail.Title=\"연결 실패\"\nOutput.ConnectFail.BadPath=\"잘못된 경로 혹은 연결 주소입니다. 유효한 값인지 설정을 확인하시기 바랍니다.\"\nOutput.ConnectFail.ConnectFailed=\"서버에 연결하지 못했습니다\"\nOutput.ConnectFail.InvalidStream=\"지정한 채널 혹은 스트림 키에 접근할 수 없습니다. 스트림 키를 다시 확인하세요. 키가 올바르다면 서버 접속에 문제가 있을 수 있습니다.\"\nOutput.ConnectFail.HdrDisabled=\"현재 이 출력에 대한 HDR 출력을 사용할 수 없습니다.\"\nOutput.ConnectFail.Error=\"서버에 접속하는 중 예기치 않은 오류가 발생했습니다. 자세한 정보는 로그 파일을 참고해주세요.\"\nOutput.ConnectFail.Disconnected=\"서버로부터 접속이 끊겼습니다.\"\nOutput.StreamEncodeError.Title=\"인코딩 오류\"\nOutput.StreamEncodeError.Msg=\"방송 중 인코더 오류가 발생했습니다.\"\nOutput.StreamEncodeError.Msg.LastError=\"스트리밍 중 인코더 오류가 발생했습니다:<br><br>%1\"\nOutput.RecordFail.Title=\"녹화에 실패했습니다\"\nOutput.RecordFail.Unsupported=\"이 출력 형식은 지원되지 않거나, 둘 이상의 오디오 트랙을 지원하지 않습니다. 설정을 확인하고 다시 시도하세요.\"\nOutput.RecordNoSpace.Title=\"디스크 공간 부족\"\nOutput.RecordNoSpace.Msg=\"녹화를 계속하기 위한 디스크 공간이 부족합니다.\"\nOutput.RecordError.Title=\"녹화 오류\"\nOutput.RecordError.Msg=\"녹화 중 예기치 못한 오류가 발생했습니다.\"\nOutput.RecordError.EncodeErrorMsg=\"녹화 중 인코더 오류가 발생했습니다.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"녹화 중 인코더 오류가 발생했습니다:<br><br>%1\"\nOutput.BadPath.Title=\"잘못된 파일 경로\"\nOutput.BadPath.Text=\"설정한 녹화본 경로를 열 수 없습니다. 설정 → 출력 → 녹화에서 녹화 경로를 확인하세요.\"\nOutput.NoBroadcast.Title=\"설정된 방송 없음\"\nOutput.NoBroadcast.Text=\"방송을 진행하기 전에 방송 설정이 필요합니다\"\nOutput.BroadcastStartFailed=\"방송 시작에 실패했습니다\"\nOutput.BroadcastStopFailed=\"방송 종료에 실패했습니다\"\nLogUploadDialog.Title=\"OBS Studio 로그 파일 업로드\"\nLogUploadDialog.Labels.PrivacyNotice=\"파일을 업로드하기 전에 <a href='https://obsproject.com/privacy-policy'>개인정보 보호정책</a> 및 파일 업로드 관련 섹션을 읽어주세요.\"\nLogUploadDialog.Labels.Progress=\"로그 업로드 진행중. 기다려 주세요...\"\nLogUploadDialog.Labels.Description.AppLog=\"로그 파일을 올렸습니다. 이 URL을 디버깅 혹은 문제 해결을 위해 공유할 수 있습니다.\"\nLogUploadDialog.Labels.Description.CrashLog=\"비정상 종료 현상에 관한 로그가 업로드되었습니다. 이 URL을 디버깅 목적으로 공유할 수 있습니다.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"업로드\"\nLogUploadDialog.Buttons.CopyURL=\"로그 URL 복사\"\nLogUploadDialog.Buttons.AnalyzeURL=\"로그 파일 분석\"\nLogUploadDialog.Buttons.RetryButton=\"재시도\"\nLogUploadDialog.Errors.Template=\"파일을 업로드하는 중 오류가 발생했습니다:\\n\\n %1\"\nLogUploadDialog.Errors.NoLogFile=\"업로드 할 파일을 찾지 못했거나, 파일이 비어 있습니다.\"\nRemux.SourceFile=\"OBS 녹화본\"\nRemux.TargetFile=\"대상 파일\"\nRemux.Stop=\"Remux 중단\"\nRemux.ClearFinished=\"완료한 항목 지우기\"\nRemux.ClearAll=\"모든 항목 지우기\"\nRemux.OBSRecording=\"OBS 녹화본\"\nRemux.FinishedTitle=\"Remux 작업 완료\"\nRemux.Finished=\"녹화를 Remux 변환 완료\"\nRemux.FinishedError=\"녹화본이 Remux 변환되었으나 파일이 불완전할 수 있습니다\"\nRemux.SelectRecording=\"OBS 녹화본 선택...\"\nRemux.SelectTarget=\"대상 파일 선택...\"\nRemux.FileExistsTitle=\"대상 파일이 존재합니다\"\nRemux.FileExists=\"다음 목록의 대상 파일이 이미 존재합니다. 대체하시겠습니까?\"\nRemux.ExitUnfinishedTitle=\"Remux 변환 작업 중\"\nRemux.ExitUnfinished=\"Remux 변환 작업이 끝나지 않았습니다. 지금 중단하면 대상 파일은 사용할 수 없는 상태가 됩니다.\\n정말로 변환을 중단하시겠습니까?\"\nRemux.HelpText=\"Remux 작업할 파일을 이 창에 드래그하거나, 비어 있는 'OBS 녹화' 칸을 눌러 파일을 지정하세요.\"\nRemux.NoFilesAddedTitle=\"Remux 작업할 파일이 추가되지 않음\"\nRemux.NoFilesAdded=\"Remux 작업할 파일이 추가되지 않았습니다. 하나 이상의 동영상 파일을 담은 폴더를 끌어다 놓으세요.\"\nMissingFiles=\"누락된 파일\"\nMissingFiles.MissingFile=\"누락된 파일\"\nMissingFiles.NewFile=\"새 파일\"\nMissingFiles.HelpText=\"마지막으로 OBS를 사용한 후 누락된 파일이 감지되었습니다\"\nMissingFiles.Clear=\"<삭제됨>\"\nMissingFiles.NumFound=\"%2개 중 %1개 찾음\"\nMissingFiles.Search=\"폴더에서 찾기...\"\nMissingFiles.SelectFile=\"파일 선택...\"\nMissingFiles.SelectDir=\"탐색할 폴더 위치 선택\"\nMissingFiles.State=\"상태\"\nMissingFiles.Missing=\"찾을 수 없음\"\nMissingFiles.Replaced=\"교체됨\"\nMissingFiles.Cleared=\"삭제됨\"\nMissingFiles.Found=\"발견됨\"\nMissingFiles.AutoSearch=\"일치하는 파일 추가 확인됨\"\nMissingFiles.AutoSearchText=\"OBS 가 해당 디렉토리에서 누락된 파일과 일치하는 건을 발견했습니다. 추가하시겠습니까?\"\nMissingFiles.NoMissing.Title=\"무결성 검사\"\nMissingFiles.NoMissing.Text=\"문제가 있는 파일이 없습니다\"\nMacPermissions.MenuAction=\"앱 권한 검토...\"\nMacPermissions.Title=\"앱 권한 검토\"\nMacPermissions.Description=\"OBS Studio에서 특정 기능을 제공하기 위해 권한이 필요합니다. 권한을 부여하는 것을 권장드리지만, 부여하지 않더라도 앱을 사용할 수는 있습니다. 나중에 언제든지 권한을 부여할 수 있습니다.\"\nMacPermissions.Description.OpenDialog=\"OBS Studio 메뉴에서 이 대화 상자를 다시 열 수 있습니다.\"\nMacPermissions.AccessGranted=\"접근 허가됨\"\nMacPermissions.RequestAccess=\"접근 요청\"\nMacPermissions.OpenPreferences=\"%1 설정 열기\"\nMacPermissions.Item.ScreenRecording=\"화면 녹화\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS가 화면을 캡처하기 위해 이 권한이 필요합니다.\"\nMacPermissions.Item.Camera=\"카메라\"\nMacPermissions.Item.Camera.Details=\"웹캠, 캡처 장비의 콘텐츠를 녹화하려면 이 권한이 필요합니다.\"\nMacPermissions.Item.Microphone=\"마이크\"\nMacPermissions.Item.Microphone.Details=\"OBS가 마이크 또는 외부 오디오 장치를 캡처하려면 이 권한이 필요합니다.\"\nMacPermissions.Item.InputMonitoring=\"입력 모니터링\"\nMacPermissions.Item.InputMonitoring.Details=\"OBS가 백그라운드에서 실행되는 동안 단축키가 작동하기 위해서는 이 권한이 필요합니다.\"\nMacPermissions.Item.Accessibility=\"접근성\"\nMacPermissions.Item.Accessibility.Details=\"이전 설치에서는 OBS가 \\\"입력 모니터링\\\" 대신에 \\\"접근성\\\"에 표시될 수 있습니다.\"\nMacPermissions.Continue=\"계속\"\nSourceLeak.Title=\"소스 정리 오류\"\nSourceLeak.Text=\"장면 모음을 바꾸는 동안 문제가 발생하여 일부 소스를 불러오기 해제할 수 없습니다. 보통 플러그인이 리소스를 올바르게 송출하지 않아 문제가 발생합니다. 사용 중인 플러그인이 최신 버전인지 확인하십시오.\\n\\n데이터 손상을 막기 위해 OBS Studio를 종료합니다.\"\nBasic.DesktopDevice1=\"데스크탑 오디오\"\nBasic.DesktopDevice2=\"데스크탑 오디오 2\"\nBasic.AuxDevice1=\"마이크/Aux\"\nBasic.AuxDevice2=\"마이크/Aux 2\"\nBasic.AuxDevice3=\"마이크/Aux 3\"\nBasic.AuxDevice4=\"마이크/Aux 4\"\nBasic.Scene=\"장면\"\nBasic.DisplayCapture=\"캡처 표시\"\nBasic.Main.PreviewConextMenu.Enable=\"미리보기 켜기\"\nBasic.Main.Preview.Disable=\"미리보기 끄기\"\nScaleFiltering=\"비율 필터링\"\nScaleFiltering.Bilinear=\"이중선형\"\nScaleFiltering.Bicubic=\"바이큐빅\"\nScaleFiltering.Lanczos=\"란초스\"\nScaleFiltering.Area=\"영역\"\nBlendingMethod=\"혼합 방법\"\nBlendingMethod.Default=\"기본\"\nBlendingMethod.SrgbOff=\"SRGB 끄기\"\nBlendingMode=\"혼합 모드\"\nBlendingMode.Normal=\"표준\"\nBlendingMode.Additive=\"추가\"\nBlendingMode.Subtract=\"빼기\"\nBlendingMode.Screen=\"스크린\"\nBlendingMode.Multiply=\"곱하기\"\nBlendingMode.Lighten=\"밝게\"\nBlendingMode.Darken=\"어둡게\"\nDeinterlacing=\"디인터레이싱\"\nDeinterlacing.Discard=\"버리기\"\nDeinterlacing.TopFieldFirst=\"위 필드 우선\"\nDeinterlacing.BottomFieldFirst=\"아래 필드 우선\"\nVolControl.SliderUnmuted=\"'%1'의 음량 조절:\"\nVolControl.SliderMuted=\"'%1'의 음량 조절: (현재 음소거)\"\nVolControl.Mute=\"'%1' 음소거\"\nVolControl.Properties=\"'%1' 속성\"\nVolControl.UnassignedWarning.Title=\"오디오 소스가 할당되지 않음\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" 소스가 오디오 트랙에 할당되지 않아서 스트림이나 녹화본에서는 들을 수 없습니다.\\n\\n오디오 소스를 트랙에 할당하려면, 우클릭 메뉴나 믹서 독 도구 모음의 톱니 버튼을 통해 오디오 고급 설정을 엽니다.\"\nBasic.Main.AddSceneDlg.Title=\"장면 추가\"\nBasic.Main.AddSceneDlg.Text=\"장면 이름을 입력하세요\"\nBasic.Main.DefaultSceneName.Text=\"장면 %1\"\nBasic.Main.AddSceneCollection.Title=\"장면 모음 추가\"\nBasic.Main.AddSceneCollection.Text=\"장면 모음 이름을 입력하세요\"\nBasic.Main.RenameSceneCollection.Title=\"장면 모음 이름 바꾸기\"\nBasic.Main.RemigrateSceneCollection.Title=\"장면 모음 해상도 업데이트\"\nBasic.Main.RemigrateSceneCollection.Text=\"장면 모음 해상도 \\\"%1\\\"을(를) 현재 프로파일의 캔버스 해상도인 %2x%3에 맞추시겠습니까?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"출력이 사용 중인 동안 장면 모음 해상도를 업데이트할 수 없습니다.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"장면 모음 해상도를 업데이트 할 수 없습니다. 원본 해상도를 알 수 없습니다.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"초기화 불가: OBS 해상도를 변경할 수 없습니다.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"초기화 불가: 현재 해상도가 이미 장면 모음의 기본 해상도입니다.\"\nAddProfile.Title=\"프로파일 추가\"\nAddProfile.Text=\"프로파일 이름을 입력하세요\"\nAddProfile.WizardCheckbox=\"자동 설정 마법사 보이기\"\nRenameProfile.Title=\"프로파일 이름 바꾸기\"\nBasic.Main.MixerRename.Title=\"오디오 소스 이름 바꾸기\"\nBasic.Main.MixerRename.Text=\"오디오 소스 이름을 입력하세요\"\nBasic.Main.PreviewDisabled=\"미리보기가 꺼져 있습니다\"\nBasic.SourceSelect=\"소스 만들기/선택\"\nBasic.SourceSelect.CreateNew=\"새로 만들기\"\nBasic.SourceSelect.AddExisting=\"기존 추가\"\nBasic.SourceSelect.AddVisible=\"소스를 표시\"\nBasic.Main.Sources.Visibility=\"투명도\"\nBasic.Main.Sources.VisibilityDescription=\"캔버스 내에 '%1'의 투명도를 조절합니다\"\nBasic.Main.Sources.Lock=\"잠금\"\nBasic.Main.Sources.LockDescription=\"캔버스 내에 '%1'의 위치와 비율을 고정합니다\"\nBasic.PropertiesWindow=\"'%1' 속성\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (자동 선택: %2)\"\nBasic.PropertiesWindow.SelectColor=\"색상 지정\"\nBasic.PropertiesWindow.SelectFont=\"글꼴 선택\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"글꼴을 선택하세요\"\nBasic.PropertiesWindow.ConfirmTitle=\"설정이 변경됨\"\nBasic.PropertiesWindow.Confirm=\"아직 저장하지 않은 변경 사항이 있습니다. 변경을 유지하겠습니까?\"\nBasic.PropertiesWindow.NoProperties=\"설정 가능한 속성이 없음\"\nBasic.PropertiesWindow.AddFiles=\"파일 추가\"\nBasic.PropertiesWindow.AddDir=\"디렉토리 추가\"\nBasic.PropertiesWindow.AddURL=\"경로 또는 URL 추가\"\nBasic.PropertiesWindow.AddEditableListDir=\"'%1'에 디렉터리 항목 추가\"\nBasic.PropertiesWindow.AddEditableListFiles=\"'%1'에 파일 추가\"\nBasic.PropertiesWindow.AddEditableListEntry=\"'%1'에 항목 추가\"\nBasic.PropertiesWindow.EditEditableListEntry=\"'%1' 내 항목 수정\"\nBasic.PropertiesView.FPS.Simple=\"단순 FPS 값\"\nBasic.PropertiesView.FPS.Rational=\"유리수 FPS 값\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"유효 FPS 범위:\"\nBasic.PropertiesView.UrlButton.Text=\"기본 브라우저에서 링크를 여시겠습니까?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"URL 열기\"\nBasic.InteractionWindow=\"'%1'과 상호 작용 중\"\nBasic.StatusBar.Reconnecting=\"연결이 끊겼습니다, %2초 뒤에 다시 연결합니다 (%1번 시도)\"\nBasic.StatusBar.AttemptingReconnect=\"재접속 중... (%1번 시도)\"\nBasic.StatusBar.ReconnectSuccessful=\"재접속 성공\"\nBasic.StatusBar.Delay=\"지연 (%1초)\"\nBasic.StatusBar.DelayStartingIn=\"지연 (시작까지 %1초 남음)\"\nBasic.StatusBar.DelayStoppingIn=\"지연 (중단까지 %1초 남음)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"지연 (중단까지 %1초, 시작까지 %2초)\"\nBasic.StatusBar.RecordingSavedTo=\"'%1'에 녹화 파일 저장됨\"\nBasic.StatusBar.ReplayBufferSavedTo=\"'%1'에 리플레이 버퍼 저장됨\"\nBasic.StatusBar.ScreenshotSavedTo=\"'%1'에 스크린샷 저장됨\"\nBasic.StatusBar.AutoRemuxedTo=\"'%1'(으)로 녹화본 자동 Remux 변환됨\"\nBasic.Filters=\"필터\"\nBasic.Filters.AsyncFilters=\"오디오/비디오 필터\"\nBasic.Filters.AudioFilters=\"오디오 필터\"\nBasic.Filters.EffectFilters=\"효과 필터\"\nBasic.Filters.Title=\"'%1'에 대한 필터\"\nBasic.Filters.AddFilter.Title=\"필터 이름\"\nBasic.Filters.AddFilter.Text=\"필터 이름을 지정하세요\"\nBasic.TransformWindow=\"장면의 항목 변환\"\nBasic.TransformWindow.Position=\"위치\"\nBasic.TransformWindow.PositionX=\"수평 위치\"\nBasic.TransformWindow.PositionY=\"수직 위치\"\nBasic.TransformWindow.Rotation=\"회전\"\nBasic.TransformWindow.Size=\"크기\"\nBasic.TransformWindow.Width=\"너비\"\nBasic.TransformWindow.Height=\"높이\"\nBasic.TransformWindow.Alignment=\"위치 정렬\"\nBasic.TransformWindow.BoundsType=\"경계 상자 유형\"\nBasic.TransformWindow.BoundsAlignment=\"경계 상자 내 정렬\"\nBasic.TransformWindow.Bounds=\"경계 상자 크기\"\nBasic.TransformWindow.BoundsWidth=\"경계 상자 너비\"\nBasic.TransformWindow.BoundsHeight=\"경계 상자 높이\"\nBasic.TransformWindow.CropToBounds=\"경계 상자로 자르기\"\nBasic.TransformWindow.Crop=\"자르기\"\nBasic.TransformWindow.CropLeft=\"왼쪽 자르기\"\nBasic.TransformWindow.CropRight=\"오른쪽 자르기\"\nBasic.TransformWindow.CropTop=\"위쪽 자르기\"\nBasic.TransformWindow.CropBottom=\"아래쪽 자르기\"\nBasic.TransformWindow.Alignment.TopLeft=\"왼쪽 상단\"\nBasic.TransformWindow.Alignment.TopCenter=\"중앙 상단\"\nBasic.TransformWindow.Alignment.TopRight=\"오른쪽 상단\"\nBasic.TransformWindow.Alignment.CenterLeft=\"왼쪽\"\nBasic.TransformWindow.Alignment.Center=\"정중앙\"\nBasic.TransformWindow.Alignment.CenterRight=\"오른쪽\"\nBasic.TransformWindow.Alignment.BottomLeft=\"왼쪽 하단\"\nBasic.TransformWindow.Alignment.BottomCenter=\"중앙 하단\"\nBasic.TransformWindow.Alignment.BottomRight=\"오른쪽 하단\"\nBasic.TransformWindow.BoundsType.None=\"경계 없음\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"최대 크기만\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"내부 경계에 맞추기\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"외부 경계에 맞추기\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"경계 너비에 맞추기\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"경계 높이에 맞추기\"\nBasic.TransformWindow.BoundsType.Stretch=\"경계에 맞춰 늘리기\"\nBasic.TransformWindow.Title=\"'%1'에 대한 변환 수정\"\nBasic.TransformWindow.NoSelectedSource=\"선택된 소스 없음\"\nBasic.Main.AddSourceHelp.Title=\"소스를 추가할 수 없음\"\nBasic.Main.AddSourceHelp.Text=\"소스를 추가하려면 최소 하나의 장면이 있어야 합니다.\"\nBasic.Main.Scenes=\"장면 목록\"\nBasic.Main.Sources=\"소스 목록\"\nBasic.Main.Source=\"소스\"\nBasic.Main.Controls=\"제어\"\nBasic.Main.PreparingStream=\"준비 중...\"\nBasic.Main.Connecting=\"연결 중...\"\nBasic.Main.StartRecording=\"녹화 시작\"\nBasic.Main.StartReplayBuffer=\"리플레이 버퍼 시작\"\nBasic.Main.SaveReplay=\"리플레이 저장\"\nBasic.Main.StartStreaming=\"방송 시작\"\nBasic.Main.StartBroadcast=\"방송 시작\"\nBasic.Main.StartVirtualCam=\"가상 카메라 시작\"\nBasic.Main.StopRecording=\"녹화 중단\"\nBasic.Main.PauseRecording=\"녹화 일시정지\"\nBasic.Main.UnpauseRecording=\"녹화 재개\"\nBasic.Main.SplitFile=\"녹화 파일 분할\"\nBasic.Main.AddChapterMarker=\"챕터 마커 추가 (하이브리드 MP4/MOV 전용)\"\nBasic.Main.StoppingRecording=\"녹화를 중단합니다....\"\nBasic.Main.StopReplayBuffer=\"리플레이 버퍼 중단\"\nBasic.Main.StoppingReplayBuffer=\"리플레이 버퍼 중단중...\"\nBasic.Main.SetupBroadcast=\"방송 설정 관리\"\nBasic.Main.StopStreaming=\"방송 중단\"\nBasic.Main.StopBroadcast=\"방송 종료\"\nBasic.Main.AutoStopEnabled=\"(자동 정지)\"\nBasic.Main.StoppingStreaming=\"방송 중단 중...\"\nBasic.Main.ForceStopStreaming=\"방송 중지 (지연된 분량 버림)\"\nBasic.Main.ShowContextBar=\"소스 도구 모음 사용\"\nBasic.Main.HideContextBar=\"소스 도구 모음 숨기기\"\nBasic.Main.StopVirtualCam=\"가상 카메라 중단\"\nBasic.Main.Group=\"그룹: %1\"\nBasic.Main.GroupItems=\"선택한 항목 묶기\"\nBasic.Main.Ungroup=\"그룹 해제\"\nBasic.Main.GridMode=\"격자 방식\"\nBasic.Main.ListMode=\"목록 방식\"\nBasic.Main.VirtualCamConfig=\"가상 카메라 설정\"\nBasic.VCam.VirtualCamera=\"가상 카메라\"\nBasic.VCam.OutputType=\"출력 유형\"\nBasic.VCam.OutputSelection=\"출력 선택\"\nBasic.VCam.OutputType.Program=\"프로그램 (기본값)\"\nBasic.VCam.OutputSelection.NoSelection=\"이 출력 형식에는 선택할 항목이 없습니다\"\nBasic.VCam.RestartWarning=\"변경 사항을 적용하기 위해 가상 카메라가 다시 시작됩니다\"\nBasic.MainMenu.File=\"파일(&F)\"\nBasic.MainMenu.File.Export=\"내보내기(&E)\"\nBasic.MainMenu.File.Import=\"가져오기(&I)\"\nBasic.MainMenu.File.ShowRecordings=\"녹화본 보이기(&R)\"\nBasic.MainMenu.File.Remux=\"녹화본 Remux(&M)\"\nBasic.MainMenu.File.Settings=\"설정(&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"설정 폴더 보기\"\nBasic.MainMenu.File.ShowProfileFolder=\"프로파일 폴더 보기\"\nBasic.MainMenu.File.ShowMissingFiles=\"누락된 파일 검사\"\nBasic.MainMenu.File.Exit=\"끝내기(&X)\"\nBasic.MainMenu.Edit=\"편집(&E)\"\nBasic.MainMenu.Edit.Undo=\"실행 취소(&U)\"\nBasic.MainMenu.Edit.Redo=\"다시 실행(&R)\"\nBasic.MainMenu.Edit.LockPreview=\"미리보기 잠금(&L)\"\nBasic.MainMenu.Edit.Scale=\"미리보기 비율(&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"창에 맞추기\"\nBasic.MainMenu.Edit.Scale.Canvas=\"캔버스 (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"출력 (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"크기 조절됨 (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"확대\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"축소\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"배율 초기화\"\nBasic.MainMenu.Edit.Transform=\"변환(&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"변환 편집(&E)...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"변환 복사\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"변환 붙여넣기\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"변환 초기화(&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"시계 방향으로 90도 회전\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"반시계 방향으로 90도 회전\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180도 회전\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"수평으로 뒤집기(&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"수직으로 뒤집기(&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"화면에 맞추기(&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"화면에 맞춰 늘리기(&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"화면 가운데(&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"수직 중앙으로\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"수평 중앙으로\"\nBasic.MainMenu.Edit.Order=\"순서(&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"위로 올리기(&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"아래로 내리기(&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"맨 위로 올리기(&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"맨 아래로 내리기(&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"오디오 고급 설정(&A)\"\nBasic.MainMenu.View=\"보기(&V)\"\nBasic.MainMenu.View.Toolbars=\"도구 모음(&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"도구모음 도킹\"\nBasic.MainMenu.View.ContextBar=\"소스 도구 모음\"\nBasic.MainMenu.View.SourceIcons=\"소스 아이콘(&I)\"\nBasic.MainMenu.View.StatusBar=\"상태 표시줄(&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"전체화면 인터페이스\"\nBasic.MainMenu.View.ResetUI=\"UI 초기화(&R)\"\nBasic.MainMenu.View.AlwaysOnTop=\"항상 맨 위(&A)\"\nBasic.MainMenu.View.SceneListMode=\"장면 목록 방식\"\nBasic.MainMenu.Docks=\"독(&D)\"\nBasic.MainMenu.Docks.ResetDocks=\"독 초기화(&R)\"\nBasic.MainMenu.Docks.LockDocks=\"독 잠그기(&L)\"\nBasic.MainMenu.Docks.SideDocks=\"최대 높이 도킹(&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"사용자 브라우저 독(&C)...\"\nBasic.MainMenu.Import=\"가져오기...\"\nBasic.MainMenu.Export=\"내보내기...\"\nBasic.MainMenu.SceneCollection=\"장면 모음(&S)\"\nBasic.MainMenu.Profile=\"프로파일(&P)\"\nBasic.MainMenu.Profile.Import=\"프로파일 가져오기\"\nBasic.MainMenu.Profile.Export=\"프로파일 내보내기\"\nBasic.MainMenu.SceneCollection.Import=\"장면 모음 가져오기\"\nBasic.MainMenu.SceneCollection.Export=\"장면 모음 내보내기\"\nBasic.MainMenu.SceneCollection.Remigrate=\"기본 해상도 리셋\"\nBasic.MainMenu.SceneCollection.Migrate=\"기본 해상도 설정\"\nBasic.MainMenu.Profile.Exists=\"이미 존재하는 프로파일입니다.\"\nBasic.MainMenu.Tools=\"도구(&T)\"\nBasic.MainMenu.Help=\"도움말(&H)\"\nBasic.MainMenu.Help.HelpPortal=\"도움말 포털(&P)\"\nBasic.MainMenu.Help.Website=\"웹사이트 방문(&W)\"\nBasic.MainMenu.Help.Discord=\"&Discord 서버에 참여하기\"\nBasic.MainMenu.Help.WhatsNew=\"새로운 소식\"\nBasic.MainMenu.Help.Logs=\"로그 파일(&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"로그 파일 표시(&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"현재 로그 파일 올리기(&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"이전 로그 파일 올리기(&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"현재 로그 보기(&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"릴리스 노트\"\nBasic.MainMenu.Help.CheckForUpdates=\"업데이트 확인\"\nBasic.MainMenu.Help.Repair=\"파일 무결성 점검\"\nBasic.MainMenu.Help.RestartSafeMode=\"안전 모드로 다시 시작\"\nBasic.MainMenu.Help.RestartNormal=\"일반 모드로 다시 시작\"\nBasic.MainMenu.Help.CrashLogs=\"오류 보고서(&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"오류 보고서 보기(&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"이전 오류 보고서 전송하기(&P)\"\nBasic.MainMenu.Help.About=\"정보(&A)\"\nBasic.Settings.ProgramRestart=\"설정을 적용하려면 프로그램을 다시 시작해야 합니다.\"\nBasic.Settings.ConfirmTitle=\"변경사항 확인\"\nBasic.Settings.Confirm=\"저장하지 않은 설정이 있습니다. 저장하시겠습니까?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 이(가) 일부 스트림 설정을 제어하고 있음\"\nBasic.Settings.General=\"일반\"\nBasic.Settings.General.Language=\"언어\"\nBasic.Settings.General.Updater=\"업데이트\"\nBasic.Settings.General.UpdateChannel=\"업데이트 채널\"\nBasic.Settings.General.UpdateChannelDisabled=\"(비활성화)\"\nBasic.Settings.General.UpdateChannelDefault=\"(기본값)\"\nBasic.Settings.General.EnableAutoUpdates=\"프로그램을 시작할 때 자동으로 업데이트가 있나 확인하기\"\nBasic.Settings.General.OpenStatsOnStartup=\"시작 시 통계 창 열기\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"화면 캡처에서 OBS 창 숨기기\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"이 설정은 프로젝터가 아닌 모든 OBS Studio 창을 OBS가 캡처하지 못하도록 숨깁니다.\\n회의, 화면 공유, 원격 지원, 스크린샷 및 기타 캡처 소프트웨어와 같은 다른 응용 프로그램에도 영향을 줍니다.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"이 옵션을 활성화하면 프로젝터가 아닌 모든 OBS Studio 창이 OBS에 의해 캡처되지 않도록 숨겨집니다. 화상 회의, 화면 공유, 원격 지원, 스크린샷 및 기타 캡처 소프트웨어와 같은 다른 응용 프로그램에도 영향을 줍니다.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"방송을 시작할 때 확인 대화 상자 표시\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"방송을 중단할 때 확인 대화 상자 표시\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"녹화를 중단할 때 확인 대화 상자 표시\"\nBasic.Settings.General.Projectors=\"프로젝터\"\nBasic.Settings.General.HideProjectorCursor=\"프로젝터 위 커서 숨기기\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"프로젝터를 항상 위로\"\nBasic.Settings.General.Snapping=\"소스를 자석처럼 달라붙여서 정렬\"\nBasic.Settings.General.ScreenSnapping=\"소스를 화면 가장자리에 붙임\"\nBasic.Settings.General.CenterSnapping=\"소스를 수평과 수직 중앙에 붙임\"\nBasic.Settings.General.SourceSnapping=\"소스를 다른 소스에 붙임\"\nBasic.Settings.General.SnapDistance=\"붙이기 감도\"\nBasic.Settings.General.SpacingHelpers=\"픽셀 정렬 가이드 표시\"\nBasic.Settings.General.RecordWhenStreaming=\"방송 시 자동으로 녹화\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"방송을 중단하더라도 계속 녹화\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"방송 시 리플레이 버퍼를 자동으로 시작\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"방송을 중단해도 리플레이 버퍼를 중단하지 않기\"\nBasic.Settings.General.SysTray=\"시스템 트레이\"\nBasic.Settings.General.SysTrayWhenStarted=\"시작할 때 시스템 트레이로 최소화\"\nBasic.Settings.General.SystemTrayHideMinimize=\"항상 작업 표시줄 대신 시스템 트레이로 최소화\"\nBasic.Settings.General.SaveProjectors=\"종료 시 프로젝터 저장\"\nBasic.Settings.General.CloseExistingProjectors=\"스크린 당 프로젝터를 한 개만 전체화면으로 제한\"\nBasic.Settings.General.Preview=\"미리보기\"\nBasic.Settings.General.OverflowHidden=\"오버플로우 숨기기\"\nBasic.Settings.General.OverflowAlwaysVisible=\"오버플로우를 항상 표시\"\nBasic.Settings.General.OverflowSelectionHidden=\"소스가 보이지 않을 때도 오버플로우를 표시\"\nBasic.Settings.General.Importers=\"가져오기\"\nBasic.Settings.General.AutomaticCollectionSearch=\"장면 모음을 불러올 때 알려진 위치를 검색\"\nBasic.Settings.General.SwitchOnDoubleClick=\"더블클릭 시 장면으로 전환\"\nBasic.Settings.General.StudioPortraitLayout=\"수직 배치 사용하기\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"미리보기/프로그램 이름표 보기\"\nBasic.Settings.General.Multiview=\"다중화면\"\nBasic.Settings.General.Multiview.MouseSwitch=\"클릭으로 장면 간 전환\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"장면 이름 표시\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"안전 영역 표시 (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"다중화면 배치\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"수평, 상단 (8 장면)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"수평, 하단 (8 장면)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"수직, 좌측 (8 장면)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"수직, 우측 (8 장면)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"수평, 상단 (18 장면)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"수평, 상단 (24 장면)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"장면만 (4개)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"장면만 (9개)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"장면만 (16개)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"장면만 (25개)\"\nBasic.Settings.General.ChannelName.stable=\"안정 버전\"\nBasic.Settings.General.ChannelDescription.stable=\"최신 안정 버전\"\nBasic.Settings.General.ChannelName.beta=\"베타 / 릴리스 후보\"\nBasic.Settings.General.ChannelDescription.beta=\"불안정할 수 있는 사전 릴리스 버전\"\nBasic.Settings.Appearance=\"표시\"\nBasic.Settings.Appearance.General=\"일반\"\nBasic.Settings.Appearance.General.Theme=\"테마\"\nBasic.Settings.Appearance.General.Variant=\"스타일\"\nBasic.Settings.Appearance.General.NoVariant=\"사용 가능한 스타일 없음\"\nBasic.Settings.Appearance.FontScale=\"글꼴 크기\"\nBasic.Settings.Appearance.Density=\"밀도\"\nBasic.Settings.Appearance.Classic=\"클래식\"\nBasic.Settings.Appearance.Compact=\"간단\"\nBasic.Settings.Appearance.Normal=\"보통\"\nBasic.Settings.Appearance.Comfortable=\"편안한\"\nBasic.Settings.Appearance.OptionsWarning=\"이 스타일에서는 일부 표시 옵션을 사용할 수 없습니다.\"\nBasic.Settings.Stream=\"방송\"\nBasic.Settings.Stream.Destination=\"대상\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"인증 기능 사용\"\nBasic.Settings.Stream.Custom.Username=\"사용자 이름\"\nBasic.Settings.Stream.Custom.Password=\"비밀번호\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: srp_username을 입력하십시오. \\nRTMP: 사용자 이름을 입력하십시오. \\nSRT: 사용하지 않음.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: srp_password를 입력하십시오.\\nRTMP: 비밀번호를 입력하십시오.\\nSRT: 암호화 구문을 입력하십시오\"\nBasic.Settings.Stream.BandwidthTestMode=\"대역폭 시험 상태 활성화\"\nBasic.Settings.Stream.TTVAddon=\"Twitch 채팅 추가 기능\"\nBasic.Settings.Stream.TTVAddon.None=\"없음\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV 및 FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"방송 설정이 누락되었습니다\"\nBasic.Settings.Stream.StreamSettingsWarning=\"설정 열기\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL과 스트림 키가 없습니다.\\n\\n설정에 들어가 '방송' 탭에서 URL과 스트림 키를 입력하세요.\"\nBasic.Settings.Stream.MissingUrl=\"방송 URL이 없습니다.\\n\\n설정에 들어가 '방송' 탭에서 URL을 입력하세요.\"\nBasic.Settings.Stream.MissingStreamKey=\"스트림 키가 없습니다.\\n\\n설정에 들어가 '방송' 탭에서 스트림 키를 입력하세요.\"\nBasic.Settings.Stream.IgnoreRecommended=\"권장 스트리밍 서비스 설정 무시\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"권장 설정 덮어쓰기\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"경고: 서비스 제한을 넘는 경우 방송의 품질이 저하되거나 방송이 중단될 수 있습니다.\\n\\n계속하시겠습니까?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"최대 비디오 비트레이트: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"최대 오디오 비트레이트: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"최대 해상도: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"최대 FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"사용자 정의 서버 지정...\"\nBasic.Settings.Stream.ServiceCustomServer=\"사용자 지정 서버\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1 활성화\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"최대 스트리밍 대역폭\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"자동\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"최대 비디오 트랙 수\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"자동\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"FLV로 스트리밍 덤프 활성화 (단순 녹화 파일 설정 사용)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"구성 재정의 (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"구성 재정의 활성화\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"멀티트랙 비디오\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"추가 캔바스\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"시뮬캐스트\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"시뮬캐스트는 복수의 비디오 품질로 인코딩하고 전송할 수 있도록 해줍니다. <a href='https://obsproject.com/kb/whip-streaming-guide'>자세히 알아보기</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"전체 레이어\"\nBasic.Settings.Stream.AdvancedOptions=\"고급 옵션\"\nBasic.Settings.Output=\"출력\"\nBasic.Settings.Output.Format=\"녹화 형식\"\nBasic.Settings.Output.Format.MKV=\"Matroska 비디오 (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"하이브리드 MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"하이브리드 MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmented MP4 비디오 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmented MOV 비디오 (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmented MOV는 녹화본을 청크 단위로 기록하며 기존의 MOV 파일에서 필요하던 마무리 작업이 필요하지 않습니다.\\n따라서, 블루 스크린이 발생하거나 전원이 끊겨 파일 기록이 중단되더라도 녹화본을 이용할 수 있습니다.\\n\\n이는 모든 영상 플레이어 및 에디터와는 호환되지 않을 수 있습니다. 필요하다면 파일 → 녹화본 Remux를 이용하여, 보다 호환성 있는 형식으로 변환하세요.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmented MP4는 녹화본을 청크 단위로 기록하며 기존의 MP4 파일에서 필요하던 마무리 작업이 필요하지 않습니다.\\n따라서, 블루 스크린이 발생하거나 전원이 끊겨 파일 기록이 중단되더라도 녹화본을 이용할 수 있습니다.\\n\\n이는 모든 영상 플레이어 및 에디터와는 호환되지 않을 수 있습니다. 필요하다면 파일 → 녹화본 Remux를 이용하여, 보다 호환성 있는 형식으로 변환하세요.\"\nBasic.Settings.Output.Encoder.Video=\"비디오 인코더\"\nBasic.Settings.Output.Encoder.Audio=\"오디오 인코더\"\nBasic.Settings.Output.SelectDirectory=\"녹화 경로 선택\"\nBasic.Settings.Output.DynamicBitrate=\"네트워크 혼잡 상태를 관리하기 위해서 비트레이트를 동적으로 변경합니다\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"네트워크 혼잡 상태를 관리하기 위해 비트레이트를 동적으로 변경 (베타)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"혼잡을 줄이기 위해 프레임을 떨어뜨리는 대신, 적용 중인 비트레이트를 동적으로 조절합니다.\\n\\n중대한 혼잡 상태가 급작스럽게 발생할 때 시청자들이 접하는 화면이 어느 정도 지연될 수 있습니다.\\n비트레이트가 감소하면 복구하는데 수 분이 소요될 수 있습니다.\\n\\n이 기능은 현재 RTMP에서만 지원됩니다.\"\nBasic.Settings.Output.Mode=\"출력 방식\"\nBasic.Settings.Output.Mode.Simple=\"단순\"\nBasic.Settings.Output.Mode.Adv=\"고급\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg 출력\"\nBasic.Settings.Output.UseReplayBuffer=\"리플레이 버퍼 활성화\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"최대 리플레이 시간\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"최대 메모리\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"예상되는 메모리 사용량: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"경고: 예상 메모리 사용량 %1MiB가 권장 최대값 %2MiB 보다 큽니다.\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"메모리 사용량을 계산할 수 없습니다. 최대 메모리 사용량을 직접 설정하세요.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"리플레이 버퍼 파일명 접두사\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"접미\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"녹화 유형을 사용자 지정 출력(FFmpeg)으로 설정한 경우 리플레이 버퍼를 이용할 수 없습니다.\"\nBasic.Settings.Output.Simple.SavePath=\"녹화 저장 경로\"\nBasic.Settings.Output.Simple.RecordingQuality=\"녹화 품질\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"방송 품질과 동일하게\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"높은 품질, 중간 파일 크기\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"무손실에 가까운 품질, 큰 파일 크기\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"무손실 품질, 아주 큰 파일 크기\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"경고: 송출 영상의 비트레이트가 현재 스트리밍 서비스의 최대값을 넘는 %1 으로 설정됩니다.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"경고: 송출 오디오의 비트레이트가 현재 스트리밍 서비스의 최대값을 넘는 %1 으로 설정됩니다.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"경고: 녹화 품질이 \\\"방송과 동일\\\"로 설정되어 있으면 녹화를 일시정지할 수 없습니다.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"경고: 현재 선택한 녹화 형식이 스트림 인코더와 호환되지 않습니다.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"경고: 소프트웨어 인코더로 방송과 녹화를 동시에 진행할 때, 품질 설정이 서로 다르면 CPU에 더 큰 부하가 발생합니다.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"경고: 무손실 품질로 녹화하면 파일 크기가 매우 커집니다! 해당 설정은 높은 해상도 및 프레임에서 분당 7기가바이트 이상 필요합니다. 따라서 디스크에 아주 큰 공간을 확보하지 않는 이상 장시간 녹화에는 추천하지 않습니다. 무손실 품질로 녹화하는 중에는 리플레이 버퍼를 이용할 수 없습니다.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"정말로 무손실 품질로 녹화하겠습니까?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"무손실 품질 설정 경고!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"소프트웨어 (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"하드웨어 (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"하드웨어 (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"하드웨어 (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"하드웨어 (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"하드웨어 (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"하드웨어 (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"하드웨어 (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"하드웨어 (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"하드웨어 (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"하드웨어 (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"소프트웨어 (x264 CPU 부담이 적은 사전 설정, 파일 크기 증가)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (기본값)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch 다시보기 트랙 (2번 트랙 사용)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"오디오 트랙\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"호환되지 않는 해상도/프레임레이트\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"이 스트리밍 서비스는 현재 출력 해상도 혹은 프레임레이트를 지원하지 않습니다. 계속할 경우 호환 가능한 가장 가까운 값으로 변경됩니다:\\n\\n%1\\n\\n계속하시겠습니까?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"해상도: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"미호환 인코더\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"\\\"%1\\\" 스트리밍 서비스가 \\\"%2\\\" 인코더를 지원하지 않습니다. 계속할 경우 \\\"%3\\\" 인코더로 변경됩니다.\\n\\n계속하시겠습니까?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"\\\"%1\\\" 스트리밍 서비스가 \\\"%2\\\"와 \\\"%3\\\" 인코더를 지원하지 않습니다. 계속할 경우 \\\"%4\\\"와 \\\"%5\\\" 인코더로 변경됩니다.\\n\\n계속하시겠습니까?\"\nBasic.Settings.Output.VideoBitrate=\"비디오 비트레이트\"\nBasic.Settings.Output.AudioBitrate=\"오디오 비트레이트\"\nBasic.Settings.Output.Reconnect=\"자동으로 다시 연결\"\nBasic.Settings.Output.RetryDelay=\"재시도 지연\"\nBasic.Settings.Output.MaxRetries=\"최대 재시도 횟수\"\nBasic.Settings.Output.Advanced=\"사용자 인코더 설정 활성화 (고급)\"\nBasic.Settings.Output.EncoderPreset=\"인코더 사전 설정\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (적은 CPU 사용량, 최저 품질)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (기본값) (중간 CPU 사용량, 표준 품질)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (높은 CPU 사용량, 고품질)\"\nBasic.Settings.Output.CustomEncoderSettings=\"사용자 인코더 설정\"\nBasic.Settings.Output.CustomMuxerSettings=\"사용자 Muxer 설정\"\nBasic.Settings.Output.NoSpaceFileName=\"공백 없이 파일 이름 짓기\"\nBasic.Settings.Output.Adv.Rescale=\"출력 배율 재조정\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"사용 안 함\"\nBasic.Settings.Output.Adv.AudioTrack=\"오디오 트랙\"\nBasic.Settings.Output.Adv.Streaming=\"스트리밍\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"스트리밍 설정\"\nBasic.Settings.Output.Adv.Audio.Track1=\"트랙 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"트랙 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"트랙 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"트랙 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"트랙 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"트랙 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD 트랙\"\nBasic.Settings.Output.Adv.Encoder=\"인코딩 설정\"\nBasic.Settings.Output.Adv.Recording=\"녹화\"\nBasic.Settings.Output.Adv.Recording.Settings=\"녹화 설정\"\nBasic.Settings.Output.Adv.Recording.RecType=\"녹화 형식\"\nBasic.Settings.Output.Adv.Recording.Type=\"형식\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"표준\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"임의 출력 (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(스트림 인코더 사용)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"파일명 형식\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"파일이 있는 경우 덮어쓰기\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"임의 출력(FFmpeg)은 보호 기능 없이 제공됩니다.\\n녹화 형식 \\\"표준\\\" 사용을 고려하세요.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg 출력 형식\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"출력을 URL로\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"출력을 파일로\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"표준 녹화 형식\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"모든 파일\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"파일 경로 또는 URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"컨테이너 형식\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"오디오\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"비디오\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"기본 형식\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"컨테이너 형식 설명\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"파일 경로 혹은 URL에서 짐작할 수 있는 오디오/비디오 코덱\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"기본 인코더\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"인코더 비활성화\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"비디오 인코더\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"비디오 인코더 설정 (지원되는 경우)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"오디오 인코더\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"오디오 인코더 설정 (지원되는 경우)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxer 설정 (제공되는 경우)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"키프레임 간격 (프레임 단위)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"모든 코덱 표시 (호환되지 않는 것도 포함)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg 설정\"\nBasic.Settings.Output.EnableSplitFile=\"자동 파일 분할\"\nBasic.Settings.Output.SplitFile.TypeTime=\"시간으로 나누기\"\nBasic.Settings.Output.SplitFile.TypeSize=\"크기로 나누기\"\nBasic.Settings.Output.SplitFile.TypeManual=\"수동으로만 분할\"\nBasic.Settings.Output.SplitFile.Time=\"시간 분할\"\nBasic.Settings.Output.SplitFile.Size=\"크기 분할\"\nScreenshot=\"스크린샷 출력\"\nScreenshot.SourceHotkey=\"선택한 소스를 스크린샷\"\nScreenshot.StudioProgram=\"스크린샷 (프로그램)\"\nScreenshot.Preview=\"스크린샷 (미리보기)\"\nScreenshot.Scene=\"스크린샷 (장면)\"\nScreenshot.Source=\"스크린샷 (소스)\"\nFilenameFormatting.TT.CCYY=\"연도, 네 자리\"\nFilenameFormatting.TT.YY=\"연도, 마지막 두 자리 (00~99)\"\nFilenameFormatting.TT.MM=\"십진수 형식 월 (01~12)\"\nFilenameFormatting.TT.DD=\"해당 월의 일, 자릿수 채움 (01~31)\"\nFilenameFormatting.TT.hh=\"24시 형식 시간 (00~23)\"\nFilenameFormatting.TT.mm=\"분 (00~59)\"\nFilenameFormatting.TT.ss=\"초 (00~59)\"\nFilenameFormatting.TT.Percent=\"% 기호\"\nFilenameFormatting.TT.a=\"축약된 요일 이름\"\nFilenameFormatting.TT.A=\"전체 요일 이름\"\nFilenameFormatting.TT.b=\"축약된 월 이름\"\nFilenameFormatting.TT.B=\"전체 월 이름\"\nFilenameFormatting.TT.d=\"해당 월의 일, 자릿수 채움 (01~31)\"\nFilenameFormatting.TT.H=\"24시 형식 시간 (00~23)\"\nFilenameFormatting.TT.I=\"12시 형식 시간 (01~12)\"\nFilenameFormatting.TT.m=\"십진수 형식 월 (01~12)\"\nFilenameFormatting.TT.M=\"분 (00~59)\"\nFilenameFormatting.TT.p=\"오전/오후 표기\"\nFilenameFormatting.TT.s=\"UNIX 시간\"\nFilenameFormatting.TT.S=\"초 (00~59)\"\nFilenameFormatting.TT.y=\"연도, 마지막 두 자리 (00~99)\"\nFilenameFormatting.TT.Y=\"연도\"\nFilenameFormatting.TT.z=\"ISO 8601 UTC 시간대 오프셋\"\nFilenameFormatting.TT.Z=\"시간대 이름 혹은 축약된 시간대 이름\"\nFilenameFormatting.TT.FPS=\"초당 프레임\"\nFilenameFormatting.TT.CRES=\"기본 (캔버스) 해상도\"\nFilenameFormatting.TT.ORES=\"출력 (조정된) 해상도\"\nFilenameFormatting.TT.VF=\"비디오 형식\"\nBasic.Settings.Video=\"비디오\"\nBasic.Settings.Video.Adapter=\"비디오 어댑터\"\nBasic.Settings.Video.BaseResolution=\"기본 (캔버스) 해상도\"\nBasic.Settings.Video.ScaledResolution=\"출력 (조정된) 해상도\"\nBasic.Settings.Video.DownscaleFilter=\"축소 필터\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[해상도 일치, 축소 불필요]\"\nBasic.Settings.Video.FPS=\"초당 프레임 수 (FPS)\"\nBasic.Settings.Video.FPSCommon=\"표준 FPS 값\"\nBasic.Settings.Video.FPSInteger=\"정수 FPS 값\"\nBasic.Settings.Video.FPSFraction=\"분수 FPS 값\"\nBasic.Settings.Video.Numerator=\"분자\"\nBasic.Settings.Video.Denominator=\"분모\"\nBasic.Settings.Video.Renderer=\"렌더러\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (실험적)\"\nBasic.Settings.Video.InvalidResolution=\"유효하지 않은 해상도 값입니다. 반드시 [너비]x[높이] 형식이어야 합니다. (예시: 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"현재 비디오가 출력되고 있습니다. 비디오 설정을 변경하려면 관련 작업을 중단해야 합니다.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"이중선형 (빠름, 크기가 바뀌면 흐려짐)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"바이큐빅 (조금 더 날카롭게 처리, 16 샘플)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"란초스 (조금 더 날카롭게 처리, 36 샘플)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"영역 (가중치 합, 4/6/9 샘플)\"\nBasic.Settings.Audio=\"오디오\"\nBasic.Settings.Audio.SampleRate=\"샘플 레이트\"\nBasic.Settings.Audio.Channels=\"채널\"\nBasic.Settings.Audio.Meters=\"측정기\"\nBasic.Settings.Audio.MeterDecayRate=\"감쇄 속도\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"빠름\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"중간 (Type I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"느림 (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"피크 미터 형식\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"샘플 피크\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"트루 피크 (CPU 사용률 더 높음)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"경고: 서라운드 음향이 켜져 있습니다.\"\nBasic.Settings.Audio.MultichannelWarning=\"방송 중이라면 서비스에서 서라운드 음향에 대한 입력 및 재생을 지원하는지 확인하세요. 예를 들어 Facebook 360 Live는 해당 기능을 완전히 지원하며, YouTube Live는 5.1채널 입력 (및 TV 재생)을 지원합니다.\\n\\nOBS 오디오 필터는 서라운드 음향을 지원하지만 VST 플러그인 지원은 보장하지 않습니다.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"서라운드 음향을 활성화할까요?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"정말로 서라운드 음향을 사용하겠습니까?\"\nBasic.Settings.Audio.Devices=\"전역 오디오 장치 목록\"\nBasic.Settings.Audio.DesktopDevice=\"데스크탑 오디오\"\nBasic.Settings.Audio.DesktopDevice2=\"데스크탑 오디오 2\"\nBasic.Settings.Audio.AuxDevice=\"마이크/Aux 오디오\"\nBasic.Settings.Audio.AuxDevice2=\"마이크/Aux 오디오 2\"\nBasic.Settings.Audio.AuxDevice3=\"마이크/Aux 오디오 3\"\nBasic.Settings.Audio.AuxDevice4=\"마이크/Aux 오디오 4\"\nBasic.Settings.Audio.EnablePushToMute=\"눌러서 음소거하기\"\nBasic.Settings.Audio.PushToMuteDelay=\"음소거 시작 지연\"\nBasic.Settings.Audio.EnablePushToTalk=\"눌러서 말하기 활성화\"\nBasic.Settings.Audio.PushToTalkDelay=\"말하기 시작 지연\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[장치가 연결되지 않았거나 사용할 수 없음]\"\nBasic.Settings.Audio.Disabled=\"비활성화\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"지연 시간이 짧은 오디오 버퍼링 모드 (Decklink/NDI 출력 전용)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"경고: 지연 시간이 짧은 오디오 버퍼링이 활성화되어 있습니다.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"지연 시간이 짧은 오디오 버퍼링 모드는 일부 소스에서 오디오에 문제가 생기거나 재생이 되지 않는 원인이 될 수 있습니다.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"지연 시간이 짧은 오디오 버퍼링 모드를 활성화하시겠습니까?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"지연 시간이 짧은 오디오 버퍼링 모드를 활성화하시겠습니까?\"\nBasic.Settings.Accessibility=\"접근성\"\nBasic.Settings.Accessibility.ColorOverrides=\"다른 색상 사용\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"소스 테두리 (선택)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"소스 테두리 (자르기)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"소스 테두리 (마우스 오버)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"믹서 음량 대역 (-60dB ~ -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"믹서 음량 대역 (-20dB ~ -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"믹서 음량 대역 (-9dB ~ 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"믹서 음량 대역 (-60dB ~ -20dB) (활성화)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"믹서 음량 대역 (-20dB ~ -9dB) (활성화)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"믹서 음량 띠 (-9dB ~ 0dB) (활성화)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"색상 사전 설정\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"기본\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"사용자 정의\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"색맹 대체용\"\nBasic.Settings.Advanced=\"고급\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"녹화나 스트리밍 도중 종료 시 경고 표시\"\nBasic.Settings.Advanced.General.ProcessPriority=\"프로세스 우선 순위 설정\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"높음\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"높은 우선 순위\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"보통\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"낮은 우선 순위\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"낮음\"\nBasic.Settings.Advanced.FormatWarning=\"경고: NV12/P010 이외의 색상 형식은 주로 녹화를 위한 것이며, 방송에 적합하지 않습니다. 이대로 방송을 하게 되면 형식 전환을 위해 CPU 사용량이 증가할 수 있습니다.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"경고: 고정밀 형식은 HDR 색상 공간에서 더 일반적으로 사용됩니다.\"\nBasic.Settings.Advanced.FormatWarning2100=\"경고: Rec. 2100은 더 정확한 형식을 사용해야 합니다.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"색상 형식\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8비트, 4:2:0, 2 평면)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8비트, 4:2:0, 3 평면)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8비트, 4:4:4, 3 평면)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10비트, 4:2:0, 2 평면)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10비트, 4:2:0, 3 평면)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16비트, 4:2:2, 2 평면)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16비트, 4:4:4, 2 평면)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8비트)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"색 공간\"\nBasic.Settings.Advanced.Video.ColorRange=\"색상 범위\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"제한됨\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"전체\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR 화이트 레벨\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR 정격 피크 레벨\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"모니터링 장치\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"기본값\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Windows 오디오 더킹 비활성화\"\nBasic.Settings.Advanced.StreamDelay=\"스트림 지연\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"지속 시간\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"재접속 시 잘려나간 지점 보관 (지연시간 증가)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"예상 메모리 사용량: %1 MB\"\nBasic.Settings.Advanced.Network=\"네트워크\"\nBasic.Settings.Advanced.Network.Disabled=\"현재 선택한 스트리밍 프로토콜에서는 네트워크 설정 변경을 지원하지 않습니다.\"\nBasic.Settings.Advanced.Network.BindToIP=\"바인드할 IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP 체계\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"네트워크 최적화 켜기\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"TCP 페이싱 켜기\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"RTMP 출력의 전송 속도를 조절하여 지연 시간에 예민한 프로그램에 도움이 될 수 있도록 조율합니다.\\n연결 상태가 불안정할 시 프레임이 떨어질 수 있습니다.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"단축키 작동 방식\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"항상 활성화\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"주요 창에 초점이 있을 때 단축키를 비활성화\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"주요 창에 초점이 없을 때 단축키를 비활성화\"\nBasic.Settings.Advanced.AutoRemux=\"%1(으)로 자동 Remux\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(mkv 형식으로 녹화)\"\nBasic.AdvAudio=\"오디오 고급 설정\"\nBasic.AdvAudio.ActiveOnly=\"활성화된 소스만\"\nBasic.AdvAudio.Name=\"이름\"\nBasic.AdvAudio.Volume=\"음량\"\nBasic.AdvAudio.VolumeSource=\"'%1' 소리 크기\"\nBasic.AdvAudio.Mono=\"모노\"\nBasic.AdvAudio.MonoSource=\"'%1' 모노 다운믹싱\"\nBasic.AdvAudio.Balance=\"균형\"\nBasic.AdvAudio.BalanceSource=\"'%1' 밸런싱\"\nBasic.AdvAudio.SyncOffset=\"싱크 오프셋\"\nBasic.AdvAudio.SyncOffsetSource=\"'%1' 싱크 오프셋\"\nBasic.AdvAudio.Monitoring=\"오디오 모니터링\"\nBasic.AdvAudio.Monitoring.None=\"모니터링 끔\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"모니터링 전용 (출력 음소거)\"\nBasic.AdvAudio.Monitoring.Both=\"모니터링과 출력\"\nBasic.AdvAudio.MonitoringSource=\"'%1' 오디오 모니터링\"\nBasic.AdvAudio.AudioTracks=\"트랙\"\nBasic.Settings.Hotkeys=\"단축키\"\nBasic.Settings.Hotkeys.Pair=\"'%1'과 공유하는 키 조합을 토글 형식으로 설정\"\nBasic.Settings.Hotkeys.Filter=\"필터\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"단축키로 검색\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"이 단축키는 한 개 이상의 작업과 연결되어 있습니다. 클릭하여 확인하십시오\"\nBasic.Settings.Hotkeys.PleaseWait=\"단축키를 불러오는 중, 잠시만 기다려 주세요...\"\nBasic.Hotkeys.SelectScene=\"장면 전환\"\nBasic.SystemTray.Show=\"보이기\"\nBasic.SystemTray.Hide=\"숨기기\"\nBasic.SystemTray.Message.Reconnecting=\"연결 끊김. 재접속 시도 중...\"\nHotkeys.Left=\"왼쪽 방향키\"\nHotkeys.Right=\"오른쪽 방향키\"\nHotkeys.Up=\"위쪽 방향키\"\nHotkeys.Down=\"아래쪽 방향키\"\nHotkeys.Windows=\"Windows 키\"\nHotkeys.Menu=\"메뉴\"\nHotkeys.Space=\"스페이스\"\nHotkeys.NumpadNum=\"숫자키 패드 %1\"\nHotkeys.NumpadMultiply=\"키패드 *\"\nHotkeys.NumpadDivide=\"키패드 /\"\nHotkeys.NumpadAdd=\"숫자키 패드 +\"\nHotkeys.NumpadSubtract=\"키패드 -\"\nHotkeys.NumpadDecimal=\"키패드 .\"\nHotkeys.AppleKeypadNum=\"%1 (키패드)\"\nHotkeys.AppleKeypadMultiply=\"* (키패드)\"\nHotkeys.AppleKeypadDivide=\"/ (키패드)\"\nHotkeys.AppleKeypadAdd=\"+ (키패드)\"\nHotkeys.AppleKeypadSubtract=\"- (키패드)\"\nHotkeys.AppleKeypadDecimal=\". (키패드)\"\nHotkeys.AppleKeypadEqual=\"= (키패드)\"\nHotkeys.MouseButton=\"마우스 %1\"\nMute=\"음소거\"\nUnmute=\"음소거 해제\"\nPush-to-mute=\"눌러서 음소거\"\nPush-to-talk=\"눌러서 말하기\"\nSceneItemShow=\"'%1' 표시\"\nSceneItemHide=\"'%1' 숨기기\"\nOutputWarnings.NoTracksSelected=\"하나 이상의 트랙을 선택해야 합니다\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"출력 설정 오류\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"모든 출력은 최소한 한개 이상의 오디오 트랙이 선택되어 있어야 합니다\"\nOutputWarnings.MP4Recording=\"경고: MP4/MOV로 녹화하는 경우, 녹화가 예기치 못하게 중단되면 (예시: 갑자기 컴퓨터가 꺼지거나 블루 스크린 오류가 발생하는 경우) 파일을 복구할 수 없습니다. 여러 개의 오디오 트랙을 녹화하려면 MKV로 녹화한 뒤 Remux 작업을 통해 MP4/MOV로 변환하세요. (파일 → 녹화본 Remux)\"\nOutputWarnings.CannotPause=\"경고: 녹화 인코더가 \\\"(스트림 인코더 사용)\\\"으로 설정되어 있으면 녹화를 일시정지할 수 없습니다.\"\nOutputWarnings.CodecIncompatible=\"선택한 오디오 혹은 비디오 인코더가 호환되지 않아 초기화되었습니다. 목록에서 호환되는 인코더를 선택해주십시오.\"\nCodecCompat.Incompatible=\"(%1 미호환)\"\nCodecCompat.CodecPlaceholder=\"인코더 선택...\"\nCodecCompat.ContainerPlaceholder=\"포맷 선택...\"\nCodecCompat.CodecMissingOnExit.Title=\"선택된 인코더 없음\"\nCodecCompat.CodecMissingOnExit.Text=\"비디오 혹은 오디오 인코더를 선택하지 않았습니다. 녹화 및 스트리밍용 인코더를 모두 선택했는지 확인하십시오.\"\nCodecCompat.ContainerMissingOnExit.Title=\"선택된 형식 없음\"\nCodecCompat.ContainerMissingOnExit.Text=\"녹화 형식을 선택하지 않았습니다. 선택한 스트림 인코더와 호환되는 녹화 형식을 선택하십시오.\"\nFinalScene.Title=\"장면 삭제\"\nFinalScene.Text=\"적어도 하나의 장면은 존재해야 합니다.\"\nNoSources.Title=\"소스 없음\"\nNoSources.Text=\"영상 소스를 추가하지 않아서 빈 화면만 송출될 것입니다. 그래도 계속하시겠습니까?\"\nNoSources.Text.AddSource=\"주 화면 소스 목록에 있는 + 아이콘을 누르면 소스를 추가할 수 있습니다.\"\nNoSources.Label=\"방송이나 녹화에 송출할 소스가 지정되어 있지 않습니다.\\n아래 + 버튼을 누르거나\\n여기를 우클릭하여 소스를 추가하십시오.\"\nChangeBG=\"색상 지정\"\nCustomColor=\"사용자 색상\"\nBrowserSource.EnableHardwareAcceleration=\"브라우저 소스에 하드웨어 가속 활성화\"\nAbout=\"정보\"\nAbout.Info=\"OBS Studio는 비디오 녹화 및 실시간 스트리밍을 위한 자유 오픈소스 소프트웨어입니다.\"\nAbout.Donate=\"후원하기\"\nAbout.GetInvolved=\"참여하기\"\nAbout.Authors=\"제작자들\"\nAbout.License=\"라이센스\"\nAbout.Error=\"오류! 파일을 읽을 수 없습니다.\\n\\n이동: %1\"\nAbout.Contribute=\"OBS 프로젝트를 후원하세요\"\nAddUrl.Title=\"URL을 통해 소스 추가\"\nAddUrl.Text=\"URL을 OBS로 가져왔습니다. 이 링크를 소스로 자동으로 추가할 것입니다. 계속할까요?\"\nResizeOutputSizeOfSource=\"출력 크기 조정 (소스 크기)\"\nResizeOutputSizeOfSource.Text=\"기본 및 출력 해상도가 현재 소스 크기로 조정됩니다.\"\nResizeOutputSizeOfSource.Continue=\"계속하시겠습니까?\"\nPreviewTransition=\"화면 전환 미리보기\"\nImporter=\"장면 모음 불러오기\"\nImporter.SelectCollection=\"장면 모음 선택\"\nImporter.Collection=\"장면 모음\"\nImporter.HelpText=\"파일을 이 창에 추가하여 OBS 또는 다른 지원 프로그램에서 모음을 불러올 수 있습니다.\"\nImporter.Path=\"모음 위치\"\nImporter.Program=\"감지된 프로그램\"\nImporter.AutomaticCollectionPrompt=\"장면 모음을 자동으로 검색\"\nImporter.AutomaticCollectionText=\"OBS는 외부 프로그램의 장면 모음을 자동적으로 검색하여 볼러올 수 있습니다. OBS에서 모음을 자동으로 검색하겠습니까?\\n\\n이 동작을 '설정 > 일반 > 불러오기'에서 나중에 바꿀 수도 있습니다.\"\nImporter.SelectFile=\"찾아보기...\"\nRestart=\"재시작\"\nNeedsRestart=\"OBS Studio를 다시 시작해야 합니다. 지금 재시작할까요?\"\nLoadProfileNeedsRestart=\"OBS를 재시작해야 적용되는 설정이 있습니다:\\n%1\\n\\nOBS를 재시작하여 설정을 적용하시겠습니까?\"\nContextBar.NoSelectedSource=\"선택된 소스 없음\"\nContextBar.MediaControls.PlayMedia=\"미디어 재생\"\nContextBar.MediaControls.PauseMedia=\"미디어 일시정지\"\nContextBar.MediaControls.StopMedia=\"미디어 정지\"\nContextBar.MediaControls.RestartMedia=\"미디어 다시 재생\"\nContextBar.MediaControls.PlaylistNext=\"재생목록 내 다음 항목\"\nContextBar.MediaControls.PlaylistPrevious=\"재생목록 내 이전 항목\"\nContextBar.MediaControls.BlindSeek=\"미디어 탐색 도구\"\nYouTube.Auth.Ok=\"성공적으로 인증을 완료했습니다.\\n이 창을 닫아도 좋습니다.\"\nYouTube.Auth.NoCode=\"인증이 완료되지 않았습니다.\"\nYouTube.Auth.NoChannels=\"선택한 계정에서 채널을 찾을 수 없음\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube 계정 인증\"\nYouTube.Auth.WaitingAuth.Text=\"외부 브라우저를 이용하여 계정 인증을 완료하십시오.<br>만약 외부 브라우저가 열리지 않으면 이 링크를 통해 인증을 완료하십시오:<br>%1\"\nYouTube.AuthError.Text=\"채널 정보를 불러올 수 없음: %1.\"\nYouTube.Actions.WindowTitle=\"YouTube 방송 설정 - 채널: %1\"\nYouTube.Actions.CreateNewEvent=\"새 방송 생성\"\nYouTube.Actions.ChooseEvent=\"기존 방송 선택\"\nYouTube.Actions.Title=\"제목*\"\nYouTube.Actions.MyBroadcast=\"내 방송\"\nYouTube.Actions.Description=\"설명\"\nYouTube.Actions.Privacy=\"공개 범위*\"\nYouTube.Actions.Privacy.Private=\"비공개\"\nYouTube.Actions.Privacy.Public=\"공개\"\nYouTube.Actions.Privacy.Unlisted=\"일부 공개\"\nYouTube.Actions.Category=\"카테고리\"\nYouTube.Actions.Thumbnail=\"미리보기\"\nYouTube.Actions.Thumbnail.SelectFile=\"파일 선택...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"선택된 파일 없음\"\nYouTube.Actions.Thumbnail.ClearFile=\"삭제\"\nYouTube.Actions.MadeForKids=\"아동용 영상인가요?*\"\nYouTube.Actions.MadeForKids.Yes=\"예, 아동용입니다\"\nYouTube.Actions.MadeForKids.No=\"아니요, 아동용이 아닙니다\"\nYouTube.Actions.AdditionalSettings=\"추가 설정\"\nYouTube.Actions.Latency=\"지연 시간\"\nYouTube.Actions.Latency.Normal=\"기본\"\nYouTube.Actions.Latency.Low=\"짧음\"\nYouTube.Actions.Latency.UltraLow=\"매우 짧음\"\nYouTube.Actions.EnableAutoStart=\"자동 시작 사용\"\nYouTube.Actions.EnableAutoStop=\"자동 중단 사용\"\nYouTube.Actions.AutoStartStop.TT=\"예약된 방송이 자동으로 시작될지 선택\"\nYouTube.Actions.EnableDVR=\"DVR 사용\"\nYouTube.Actions.360Video=\"360° 동영상\"\nYouTube.Actions.ScheduleForLater=\"나중을 위해 예약하기\"\nYouTube.Actions.RememberSettings=\"이 설정 기억하기\"\nYouTube.Actions.Create_Ready=\"방송 생성\"\nYouTube.Actions.Create_GoLive=\"방송 생성 후 생방송 시작하기\"\nYouTube.Actions.Choose_Ready=\"방송 선택\"\nYouTube.Actions.Choose_GoLive=\"방송 선택 후 생방송 시작하기\"\nYouTube.Actions.Create_Schedule=\"방송 예약\"\nYouTube.Actions.Create_Schedule_Ready=\"방송 선택 후 예약\"\nYouTube.Actions.Dashboard=\"YouTube 스튜디오 열기\"\nYouTube.Actions.Error.Title=\"실시간 스트리밍 생성 오류\"\nYouTube.Actions.Error.Text=\"YouTube 접근 오류 '%1'.<br/>자세한 사항은 다음에서 확인하십시오: <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTube 접근 오류. 연결 상태나 YouTube 서버 상태를 확인하십시오.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"방송 생성 오류 '%1'.<br/>자세한 사항은 다음에서 확인하십시오: <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"스트림이 생성되지 않았습니다. 계정을 다시 연결하십시오.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API 오류. 추가 정보를 확인하려면 로그 파일을 확인하십시오.\"\nYouTube.Actions.Error.BroadcastNotFound=\"선택한 방송을 찾을 수 없습니다.\"\nYouTube.Actions.Error.FileMissing=\"선택한 파일이 존재하지 않습니다.\"\nYouTube.Actions.Error.FileOpeningFailed=\"선택한 파일을 여는 데 실패했습니다.\"\nYouTube.Actions.Error.FileTooLarge=\"선택한 파일의 용량이 너무 큽니다 (최대 2MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"방송 전환에 실패했습니다: %1<br/><br/>이 오류가 계속될 경우 <a href='https://studio.youtube.com/video/%2/livestreaming'>Youtube 스튜디오에서 방송을 열고</a> 수동으로 전환하십시오.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"방송이 테스트 단계로 전환 중이며, 시간이 소요될 수 있습니다. 10-30초 후 다시 시도하십시오.\"\nYouTube.Actions.EventsLoading=\"이벤트 목록을 불러오는 중...\"\nYouTube.Actions.EventCreated.Title=\"이벤트 생성 완료\"\nYouTube.Actions.EventCreated.Text=\"이벤트가 성공적으로 생성되었습니다.\"\nYouTube.Actions.Stream=\"스트림\"\nYouTube.Actions.Stream.ScheduledFor=\"예정: %1\"\nYouTube.Actions.Stream.Resume=\"중단된 송출 재개\"\nYouTube.Actions.Stream.YTStudio=\"YouTube Studio에 의해 자동으로 생성됨\"\nYouTube.Actions.Notify.CreatingBroadcast=\"새로운 실시간 스트리밍을 생성하는 중입니다. 기다려 주십시오...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"수동 시작 필요\"\nYouTube.Actions.AutoStartStreamingWarning=\"이 이벤트는 자동 시작이 꺼져 있습니다. \\\"방송 시작\\\"을 눌러 방송을 시작하십시오.\"\nYouTube.Actions.AutoStopStreamingWarning=\"지금 중단하면 다시 연결할 수 없게 됩니다.<br>방송은 정지되고 라이브 상태가 중단됩니다.\"\nYouTube.Errors.liveStreamingNotEnabled=\"선택한 YouTube 채널에서 실시간 스트리밍이 활성화되지 않았습니다.<br/><br/><a href='https://www.youtube.com/features'>youtube.com/features</a>에서 자세한 정보를 확인하세요.\"\nYouTube.Errors.livePermissionBlocked=\"선택한 YouTube 채널에서 실시간 스트리밍을 사용할 수 없습니다.<br/>채널 설정에서 실시간 스트리밍을 활성화해도 실제로 스트리밍이 가능하기까지 최대 24시간이 걸릴 수 있습니다.<br/><br/><a href='https://www.youtube.com/features'>youtube.com/features</a>에서 자세한 정보를 확인하세요.\"\nYouTube.Errors.errorExecutingTransition=\"백엔드 오류로 인해 전환에 실패했습니다. 잠시 후 다시 시도하십시오.\"\nYouTube.Errors.errorStreamInactive=\"YouTube가 방송 데이터를 수신받지 않고 있습니다. 설정을 확인한 후 다시 시도하십시오.\"\nYouTube.Errors.invalidTransition=\"시도한 전환이 유효하지 않습니다. 이는 방송에서 이전 전환이 아직 끝나지 않았기 때문일 수도 있습니다. 잠시 후에 다시 시도하십시오.\"\nYouTube.DocksRemoval.Title=\"레거시 YouTube 브라우저 독 제거\"\nYouTube.DocksRemoval.Text=\"다음 브라우저 독은 지원 중단으로 제거됩니다:\\n\\n%1\\n대신 '독/YouTube 라이브 관제실'을 사용하세요.\"\nConfigDownload.WarningMessageTitle=\"경고\"\nFailedToStartStream.MissingConfigURL=\"현재 서비스에 사용 가능한 구성 URL 없음\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"사용자 지정 RTMP 주소가 지정되지 않음\"\nFailedToStartStream.InvalidCustomConfig=\"유효하지 않은 사용자 지정 구성\"\nFailedToStartStream.MissingCanvas=\"구성된 추가 캔바스가 없습니다\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"멀티트랙 비디오 서비스 생성에 실패했습니다\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"멀티트랙 비디오 RTMP 출력 생성에 실패했습니다\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC를 사용할 수 없습니다.\\n\\n인코더 유형 '%1'을(를) 찾을 수 없습니다\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"비디오 인코더 '%1' 생성에 실패했습니다 (유형: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"인코더 '%1' 생성 중 OBS 비디오 정보를 불러오는 데 실패했습니다 (유형: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"오디오 인코더 생성에 실패했습니다\"\nFailedToStartStream.NoRTMPURLInConfig=\"구성에 스트림 대상 RTMP(S) 주소가 없습니다\"\nFailedToStartStream.FallbackToDefault=\"%1 을(를) 사용하여 스트림을 시작하지 못했습니다. 단일 인코딩 설정을 사용하여 다시 시도하겠습니까?\"\nFailedToStartStream.ConfigRequestFailed=\"%1에서 구성을 가져오지 못했습니다<br><br>HTTP 오류: %2\"\nFailedToStartStream.WarningUnknownStatus=\"알 수 없는 상태 값 '%1'을(를) 받았습니다\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\n%1 없이 스트리밍을 계속하시겠습니까?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\n스트리밍을 계속하시겠습니까?\"\nFailedToStartStream.MissingEncoderConfigs=\"방송 시작 구성에 인코더 설정이 포함되지 않았습니다\"\nFailedToStartStream.InvalidEncoderConfig=\"유효하지 않은 인코더 구성을 포함하고 있는 라이브 구성으로 이동\"\nFailedToStartStream.StatusMissingHTML=\"방송 시작 요청에서 알 수 없는 오류를 반환했습니다\"\nFailedToStartStream.NoConfigSupplied=\"구성을 찾을 수 없습니다\"\nMultitrackVideo.Info=\"%1 (은)는 설정을 자동으로 최적화하여 여러 비디오 품질을 인코딩 및 전송합니다. 이 옵션을 선택하면 컴퓨터 사양 및 소프트웨어 설정에 대한 정보가 %2 (으)로 전송됩니다.\"\nMultitrackVideo.IncompatibleSettings.Title=\"호환되지 않는 설정\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 은(는) 현재 다음과 호환되지 않습니다:\\n\\n%2\\n%1 (으)로 스트리밍을 계속하시려면, 다음 호환되지 않는 설정을 비활성화하세요:\\n\\n%3\\n그리고 스트리밍을 다시 시작하세요.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"이번 스트림에서만 비활성화하고 스트리밍 시작\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"설정 업데이트 및 스트리밍 시작\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1은(는) [오디오 → 일반 → 채널]에 설정된 '%2', %3와(과) 호환하지 않습니다\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[오디오 → 일반 → 채널]을 '%1'(으)로 설정해야 합니다.\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1은(는) [오디오 → 일반 → 채널]에 대한 여러 다른 설정이 필요합니다.\"\nBasic.OpenPluginManager=\"플러그인 관리\"\nPluginManager=\"플러그인 관리\"\nPluginManager.Restart=\"플러그인 변경을 적용하려면 OBS Studio를 다시 시작해야 합니다\"\nPluginManager.MissingPlugin=\"[플러그인을 찾을 수 없음]\"\nPluginManager.Section.Discover=\"탐색\"\nPluginManager.Section.Manage=\"설치함\"\nPluginManager.Section.Updates=\"업데이트\"\nPluginManager.Section.Manage.Title=\"켜진 플러그인 관리\"\n"
  },
  {
    "path": "frontend/data/locale/lo-LA.ini",
    "content": "Language=\"ພາສາລາວ\"\nOK=\"ໂອເຄ\"\nApply=\"ນຳໃຊ້\"\nCancel=\"ຍົກເລີກ\"\nClose=\"ປິດ\"\nSave=\"ບັນທຶກ\"\nDiscard=\"ຖິ້ມ\"\nDisable=\"ປິດການໃຊ້ງານ\"\nYes=\"ແມ່ນ\"\nNo=\"ບໍ່\"\nAdd=\"ເພີ່ມ\"\nRemove=\"ເອົາອອກ\"\nRename=\"ປ່ຽນຊື່\"\nInteract=\"ໂຕ້ຕອບ\"\nFilters=\"ຟິວເຕີ\"\nProperties=\"ຄຸນສົມບັດ\"\nMoveUp=\"ຍັບຂຶ້ນ\"\nMoveDown=\"ຍັບລົງ\"\nSettings=\"ຕັ້ງຄ່າ\"\nDisplay=\"ຈໍສະແດງຜົນ\"\nName=\"ຊື່\"\nExit=\"ອອກ\"\nMixer=\"ມິກເຊີ (Mixer)\"\nBrowse=\"ເປີດຫາ\"\nMono=\"ໂມໂນ\"\nStereo=\"ສະເຕີຣິໂອ\"\nDroppedFrames=\"ເຟຣມຕົກ %1 (%2%)\"\nProjector.Open.Program=\"ເປີດ ໂປຣເຈັກເຕີ (Program)\"\nProjector.Open.Preview=\"ເປີດ ໂປຣເຈັກເຕີ (Preview)\"\nProjector.Open.Scene=\"ເປີດ ໂປຣເຈັກເຕີ (Scene)\"\nProjector.Open.Source=\"ເປີດ ໂປຣເຈັກເຕີ (Source)\"\nProjector.Open.Multiview=\"ເປີດ ມຸມມອງລວມ (Multiview)\"\nProjector.Display=\"ຈໍສະແດງຜົນ: %1\"\nProjector.Window=\"ໜ້າຕ່າງໃໝ່\"\nProjector.Title=\"ໂປຣເຈັກເຕີ\"\nProjector.Title.Scene=\"ສາກ (Scene): %1\"\nProjector.Title.Source=\"ແຫຼ່ງທີ່ມາ (Source): %1\"\nProjector.Title.Multiview=\"ມຸມມອງລວມ (Multiview)\"\nProjector.ResizeWindowToContent=\"ປັບຂະໜາດໜ້າຕ່າງໃຫ້ພໍດີກັບເນື້ອຫາ\"\nClear=\"ລຶບລ້າງ\"\nRevert=\"ກັບຄືນຄ່າເດີມ\"\nShow=\"ສະແດງ\"\nHide=\"ເຊື່ອງ\"\nUnhideAll=\"ສະແດງທັງໝົດ\"\nUntitled=\"ບໍ່ມີຊື່\"\nNew=\"ສ້າງໃໝ່\"\nDuplicate=\"ສຳເນົາ\"\nEnable=\"ເປີດໃຊ້ງານ\"\nDisableOSXVSync=\"ປິດໃຊ້ງານ macOS V-Sync\"\nResetOSXVSyncOnExit=\"ຣີເຊັດ macOS V-Sync ເມື່ອອອກຈາກໂປຣແກຣມ\"\nHighResourceUsage=\"ການເຂົ້າລະຫັດເກີນຂີດຈຳກັດ! ພິຈາລະນາລົດການຕັ້ງຄ່າວິດີໂອລົງ ຫຼື ໃຊ້ຄ່າທີ່ຕັ້ງໄວ້ກ່ອນຂອງຕົວເຂົ້າລະຫັດທີ່ໄວຂຶ້ນ.\"\nTransition=\"ການປ່ຽນສາກ\"\nQuickTransitions=\"ການປ່ຽນສາກດ່ວນ\"\nFadeToBlack=\"ຈາງລົງເປັນສີດຳ\"\nLeft=\"ຊ້າຍ\"\nRight=\"ຂວາ\"\nTop=\"ເທິງ\"\nBottom=\"ລຸ່ມ\"\nReset=\"ຣີເຊັດ\"\nHours=\"ຊົ່ວໂມງ\"\nMinutes=\"ນາທີ\"\nSeconds=\"ວິນາທີ\"\nDeprecated=\"ເລີກໃຊ້ແລ້ວ\"\nReplayBuffer=\"ບັບເຟີການຫຼິ້ນຍ້ອນຫຼັງ\"\nImport=\"ນຳເຂົ້າ\"\nExport=\"ສົ່ງອອກ\"\nCopy=\"ສຳເນົາ\"\nPaste=\"ວາງ\"\nPasteReference=\"ວາງ (ການອ້າງອີງ)\"\nPasteDuplicate=\"ວາງ (ຊໍ້າກັນ)\"\nRemuxRecordings=\"ຣີມັກສ໌ (Remux) ການບັນທຶກ\"\nNext=\"ຕໍ່ໄປ\"\nBack=\"ກັບຄືນ\"\nDefaults=\"ຄ່າເລີ່ມຕົ້ນ\"\nRestoreDefaults=\"ຄືນຄ່າເລີ່ມຕົ້ນ\"\nHideMixer=\"ເຊື່ອງໃນ ມິກເຊີ\"\nTransitionOverride=\"ລົບລ້າງການປ່ຽນສາກ\"\nShowTransition=\"ສະແດງ ການປ່ຽນສາກ\"\nHideTransition=\"ເຊື່ອງ ການປ່ຽນສາກ\"\nNone=\"ບໍ່ມີ\"\nStudioMode.Preview=\"ຕົວຢ່າງ (Preview)\"\nStudioMode.Program=\"ໂປຣແກຣມ (Program)\"\nStudioMode.PreviewSceneName=\"ຕົວຢ່າງ: %1\"\nStudioMode.ProgramSceneName=\"ໂປຣແກຣມ: %1\"\nShowInMultiview=\"ສະແດງໃນ ມຸມມອງລວມ\"\nVerticalLayout=\"ການຈັດວາງແນວຕັ້ງ\"\nGroup=\"ຈັດກຸ່ມ\"\nDoNotShowAgain=\"ບໍ່ຕ້ອງສະແດງອີກ\"\nDefault=\"(ຄ່າເລີ່ມຕົ້ນ)\"\nCalculating=\"ກຳລັງຄິດໄລ່...\"\nFullscreen=\"ເຕັມຈໍ\"\nWindowed=\"ແບບໜ້າຕ່າງ\"\nRefreshBrowser=\"ຣີເຟຣຊ\"\nAspectRatio=\"ອັດຕາສ່ວນພາບ <b>%1:%2</b>\"\nLockVolume=\"ລັອກລະດັບສຽງ\"\nLogViewer=\"ຕົວເບິ່ງ Log\"\nShowOnStartup=\"ສະແດງເມື່ອເປີດໂປຣແກຣມ\"\nOpenFile=\"ເປີດໄຟລ໌\"\nAddScene=\"ເພີ່ມສາກ\"\nAddSource=\"ເພີ່ມແຫຼ່ງທີ່ມາ\"\nRemoveScene=\"ລຶບສາກທີ່ເລືອກ\"\nRemoveSource=\"ລຶບແຫຼ່ງທີ່ມາທີ່ເລືອກ\"\nMoveSceneUp=\"ຍັບສາກຂຶ້ນ\"\nMoveSceneDown=\"ຍັບສາກລົງ\"\nMoveSourceUp=\"ຍັບແຫຼ່ງທີ່ມາຂຶ້ນ\"\nMoveSourceDown=\"ຍັບແຫຼ່ງທີ່ມາລົງ\"\nSourceProperties=\"ເປີດຄຸນສົມບັດຂອງແຫຼ່ງທີ່ມາ\"\nSourceFilters=\"ເປີດຟິວເຕີຂອງແຫຼ່ງທີ່ມາ\"\nMixerToolbarMenu=\"ເມນູ ມິກເຊີສຽງ\"\nSceneFilters=\"ເປີດຟິວເຕີຂອງສາກ\"\nList=\"ລາຍການ\"\nGrid=\"ຕາຕະລາງ\"\nAutomatic=\"ອັດຕະໂນມັດ\"\nComingSoon=\"ໄວໆນີ້\"\nPluginsFailedToLoad.Title=\"ເກີດຂໍ້ຜິດພາດໃນການໂຫຼດປັກອິນ\"\nPluginsFailedToLoad.Text=\"OBS ບໍ່ສາມາດໂຫຼດປັກອິນຕໍ່ໄປນີ້ໄດ້:\\n\\n%1\\nກະລຸນາອັບເດດ ຫຼື ລຶບປັກອິນເຫຼົ່ານີ້ອອກ.\"\nAlreadyRunning.Title=\"OBS ເປີດຢູ່ແລ້ວ\"\nAlreadyRunning.Text=\"OBS ກຳລັງເຮັດວຽກຢູ່! ຫາກທ່ານບໍ່ໄດ້ຕັ້ງໃຈຈະເຮັດແບບນີ້, ກະລຸນາປິດ OBS ທີ່ເປີດຄ້າງໄວ້ກ່ອນ ທີ່ຈະເປີດອັນໃໝ່ຂຶ້ນມາ. ຖ້າທ່ານຕັ້ງຄ່າໃຫ້ OBS ຫຍໍ້ລົງໄປໃນ System Tray, ກະລຸນາກວດເບິ່ງວ່າ ມັນຍັງເຮັດວຽກຢູ່ບ່ອນນັ້ນຫຼືບໍ່.\"\nAlreadyRunning.LaunchAnyway=\"ເປີດຕໍ່ໄປ\"\nCrashHandling.Dialog.Title=\"ກວດພົບການປິດໂປຣແກຣມຜິດປົກກະຕິຂອງ OBS Studio\"\nCrashHandling.Labels.Text=\"OBS Studio ບໍ່ໄດ້ປິດລົງຢ່າງຖືກຕ້ອງ.\\n\\nຕ້ອງການເປີດໃນ ໂໝດປອດໄພ (Safe Mode) ຫຼື ບໍ່ (ປິດການໃຊ້ງານປັກອິນພາຍນອກ, ສະຄຣິບ ແລະ WebSockets)?\"\nCrashHandling.Labels.PrivacyNotice=\"ທ່ານສາມາດເລືອກທີ່ຈະອັບໂຫຼດລາຍງານການແຄຣຊ (Crash report) ຫຼ້າສຸດໄປຍັງ OBSProject ໄດ້ໂດຍອັດຕະໂນມັດ.<br /><br />ກະລຸນາອ່ານ <a href='https://obsproject.com/privacy-policy'>ນະໂຍບາຍຄວາມເປັນສ່ວນຕົວ</a> ກ່ອນທີ່ຈະອັບໂຫຼດໄຟລ໌ ແລະ ໃສ່ໃຈເປັນພິເສດໃນສ່ວນທີ່ກ່ຽວກັບການອັບໂຫຼດໄຟລ໌.\"\nCrashHandling.Checkbox.SendReport=\"ຂ້ອຍໄດ້ອ່ານນະໂຍບາຍຄວາມເປັນສ່ວນຕົວ ແລະ ຍິນຍອມໃຫ້ອັບໂຫຼດ.\"\nCrashHandling.Buttons.LaunchSafe=\"ເປີດໃນ ໂໝດປອດໄພ (Safe Mode)\"\nCrashHandling.Buttons.LaunchNormal=\"ເປີດໃນ ໂໝດປົກກະຕິ\"\nCrashHandling.Errors.UploadJSONError=\"ເກີດຂໍ້ຜິດພາດຂະນະອັບໂຫຼດບັນທຶກການແຄຣຊ (Crash log) ຫຼ້າສຸດ. ກະລຸນາລອງໃໝ່ພາຍຫຼັງ.\"\nCrashHandling.Errors.Title=\"ເກີດຂໍ້ຜິດພາດໃນການອັບໂຫຼດ Crash Log\"\nSafeMode.Restart=\"ທ່ານຕ້ອງການຣີສະຕາດ OBS ໃນໂໝດປອດໄພ (Safe Mode) ຫຼື ບໍ່ (ປິດການໃຊ້ງານປັກອິນພາຍນອກ, ສະຄຣິບ ແລະ WebSockets)?\"\nSafeMode.RestartNormal=\"ທ່ານຕ້ອງການຣີສະຕາດ OBS ໃນໂໝດປົກກະຕິ ຫຼື ບໍ່?\"\nChromeOS.Title=\"ແພລັດຟອມທີ່ບໍ່ຮອງຮັບ\"\nChromeOS.Text=\"ເບິ່ງຄືວ່າ OBS ກຳລັງເຮັດວຽກຢູ່ໃນ ChromeOS container. ແພລັດຟອມນີ້ຍັງບໍ່ຮອງຮັບ.\"\nWine.Title=\"ກວດພົບ Wine\"\nWine.Text=\"ການເປີດ OBS ໃນ Wine ແມ່ນບໍ່ໄດ້ຮັບການຮອງຮັບ, ແລະ ຫຼາຍຟີເຈີ ເຊັ່ນ: ການບັນທຶກ (Capture) ຫຼື ແຫຼ່ງທີ່ມາຂອງອຸປະກອນ ຈະບໍ່ເຮັດວຽກ ຫຼື ເຮັດວຽກໄດ້ຢ່າງຈຳກັດ.<br><br>ແນະນຳໃຫ້ໃຊ້ OBS ເວີຊັນ Native ແທນ, ຕົວຢ່າງເຊັ່ນ <a href='https://flathub.org/apps/details/com.obsproject.Studio'>ເວີຊັນ Flatpak</a> ຫຼື ແພັກເກດຂອງລະບົບປະຕິບັດການຂອງທ່ານ.\"\nDockCloseWarning.Title=\"ກຳລັງປິດໜ້າຕ່າງ Dock\"\nDockCloseWarning.Text=\"ທ່ານຫາກໍ່ປິດໜ້າຕ່າງແບບ Dock. ຫາກທ່ານຕ້ອງການສະແດງມັນອີກຄັ້ງ, ໃຫ້ໃຊ້ເມນູ Docks ຢູ່ເທິງແຖບເມນູ.\"\nExtraBrowsers.Info=\"ເພີ່ມ Docks ໂດຍການໃສ່ຊື່ ແລະ URL, ຈາກນັ້ນກົດ ນຳໃຊ້ ຫຼື ປິດ ເພື່ອເປີດ Docks. ທ່ານສາມາດເພີ່ມ ຫຼື ລຶບ Docks ໄດ້ທຸກເວລາ.\"\nExtraBrowsers.DockName=\"ຊື່ Dock\"\nAuth.Authing.Title=\"ກຳລັງຢືນຢັນຕົວຕົນ...\"\nAuth.Authing.Text=\"ກຳລັງຢືນຢັນຕົວຕົນກັບ %1, ກະລຸນາລໍຖ້າ...\"\nAuth.AuthFailure.Title=\"ການຢືນຢັນຕົວຕົນລົ້ມເຫຼວ\"\nAuth.AuthFailure.Text=\"ການຢືນຢັນຕົວຕົນກັບ %1 ລົ້ມເຫຼວ:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"ຕ້ອງການການຢືນຢັນຕົວຕົນ\"\nAuth.InvalidScope.Text=\"ຂໍ້ກຳນົດການຢືນຢັນຕົວຕົນສຳລັບ %1 ມີການປ່ຽນແປງ. ບາງຟີເຈີອາດຈະໃຊ້ງານບໍ່ໄດ້.\"\nAuth.LoadingChannel.Title=\"ກຳລັງໂຫຼດຂໍ້ມູນຊ່ອງ...\"\nAuth.LoadingChannel.Text=\"ກຳລັງໂຫຼດຂໍ້ມູນຊ່ອງສຳລັບ %1, ກະລຸນາລໍຖ້າ...\"\nAuth.LoadingChannel.Error=\"ບໍ່ສາມາດດຶງຂໍ້ມູນຊ່ອງໄດ້.\"\nAuth.ChannelFailure.Title=\"ໂຫຼດຊ່ອງລົ້ມເຫຼວ\"\nAuth.ChannelFailure.Text=\"ການໂຫຼດຂໍ້ມູນຊ່ອງສຳລັບ %1 ລົ້ມເຫຼວ\\n\\n%2: %3\"\nAuth.Chat=\"ແຊັດ\"\nAuth.StreamInfo=\"ຂໍ້ມູນການສະຕຣີມ\"\nTwitchAuth.Stats=\"ສະຖິຕິ Twitch\"\nTwitchAuth.Feed=\"ຟີດກິດຈະກຳ Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"ບໍ່ສາມາດດຶງຄ່າ Stream key ໄດ້\"\nTwitchAuth.TwoFactorFail.Text=\"OBS ບໍ່ສາມາດເຊື່ອມຕໍ່ກັບບັນຊີ Twitch ຂອງທ່ານໄດ້. ກະລຸນາກວດສອບວ່າໄດ້ຕັ້ງຄ່າການຢືນຢັນຕົວຕົນສອງຊັ້ນ (2FA) ໃນ <a href='https://www.twitch.tv/settings/security'>ການຕັ້ງຄ່າຄວາມປອດໄພ Twitch</a> ຂອງທ່ານແລ້ວ, ເນື່ອງຈາກຈຳເປັນຕ້ອງໃຊ້ສຳລັບການສະຕຣີມ.\"\nRestreamAuth.Channels=\"ຊ່ອງ Restream\"\nCopy.Filters=\"ສຳເນົາ ຟິວເຕີ\"\nPaste.Filters=\"ວາງ ຟິວເຕີ\"\nBrowserPanelInit.Title=\"ກຳລັງເລີ່ມຕົ້ນລະບົບ ບຣາວເຊີ...\"\nBrowserPanelInit.Text=\"ກຳລັງເລີ່ມຕົ້ນລະບົບ ບຣາວເຊີ, ກະລຸນາລໍຖ້າ...\"\nBandwidthTest.Region=\"ພາກພື້ນ\"\nBandwidthTest.Region.US=\"ສະຫະລັດອາເມຣິກາ\"\nBandwidthTest.Region.EU=\"ເອີຣົບ\"\nBandwidthTest.Region.Asia=\"ອາຊີ\"\nBandwidthTest.Region.Other=\"ອື່ນໆ\"\nBasic.AutoConfig=\"ຕົວຊ່ວຍການຕັ້ງຄ່າອັດຕະໂນມັດ\"\nBasic.AutoConfig.ApplySettings=\"ນຳໃຊ້ການຕັ້ງຄ່າ\"\nBasic.AutoConfig.StartPage=\"ຂໍ້ມູນການໃຊ້ງານ\"\nBasic.AutoConfig.StartPage.SubTitle=\"ລະບຸສິ່ງທີ່ທ່ານຕ້ອງການໃຊ້ໂປຣແກຣມນີ້ເຮັດ\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"ປັບໃຫ້ເໝາະສົມສຳລັບການສະຕຣີມ, ການບັນທຶກເປັນຮອງ\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"ປັບໃຫ້ເໝາະສົມສຳລັບການບັນທຶກເທົ່ານັ້ນ, ຂ້ອຍຈະບໍ່ສະຕຣີມ\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"ຂ້ອຍຈະໃຊ້ພຽງແຕ່ກ້ອງສະເໝືອນ (Virtual camera)\"\nBasic.AutoConfig.VideoPage=\"ການຕັ້ງຄ່າວິດີໂອ\"\nBasic.AutoConfig.VideoPage.SubTitle=\"ລະບຸການຕັ້ງຄ່າວິດີໂອທີ່ທ່ານຕ້ອງການໃຊ້\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"ໃຊ້ຄ່າປັດຈຸບັນ (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"ຈໍສະແດງຜົນ %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"ໃຊ້ຄ່າປັດຈຸບັນ (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ຫຼື 30, ແຕ່ເນັ້ນ 60 ຖ້າເປັນໄປໄດ້\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ຫຼື 30, ແຕ່ເນັ້ນຄວາມລະອຽດສູງ\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"ໝາຍເຫດ: ຄວາມລະອຽດຂອງ Canvas (Base) ບໍ່ຈຳເປັນຕ້ອງຄືກັບຄວາມລະອຽດທີ່ທ່ານຈະສະຕຣີມ ຫຼື ບັນທຶກ. ຄວາມລະອຽດການສະຕຣີມ/ບັນທຶກ ຕົວຈິງຂອງທ່ານອາດຈະຖືກຫຍໍ້ລົງຈາກຄວາມລະອຽດ Canvas ເພື່ອຫຼຸດການໃຊ້ຊັບພະຍາກອນ ຫຼື ຄວາມຕ້ອງການບິດເຣດ (Bitrate).\"\nBasic.AutoConfig.StreamPage=\"ຂໍ້ມູນການສະຕຣີມ\"\nBasic.AutoConfig.StreamPage.SubTitle=\"ກະລຸນາໃສ່ຂໍ້ມູນການສະຕຣີມຂອງທ່ານ\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"ເຊື່ອມຕໍ່ບັນຊີ (ແນະນຳ)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"ຕັດການເຊື່ອມຕໍ່ບັນຊີ\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"ຕັດການເຊື່ອມຕໍ່ບັນຊີ?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"ການປ່ຽນແປງນີ້ຈະມີຜົນທັນທີ. ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການຕັດການເຊື່ອມຕໍ່ບັນຊີຂອງທ່ານ?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"ຮັບ Stream Key\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"ຂໍ້ມູນເພີ່ມເຕີມ\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"ໃຊ້ Stream Key\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"ໃຊ້ Stream Key (ຂັ້ນສູງ)\"\nBasic.AutoConfig.StreamPage.Service=\"ຜູ້ໃຫ້ບໍລິການ\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"ສະແດງທັງໝົດ...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"ກຳນົດເອງ...\"\nBasic.AutoConfig.StreamPage.Server=\"ເຊີບເວີ\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: ໃສ່ encryption passphrase.\\nRTMP: ໃສ່ຄ່າ key ທີ່ໄດ້ຈາກຜູ້ໃຫ້ບໍລິການ.\\nSRT: ໃສ່ streamid ຖ້າຜູ້ໃຫ້ບໍລິການຕ້ອງໃຊ້.\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"ບັນຊີທີ່ເຊື່ອມຕໍ່\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"ປະເມີນບິດເຣດດ້ວຍການທົດສອບແບນວິດ (ອາດໃຊ້ເວລາສອງສາມນາທີ)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"ເນັ້ນໃຊ້ການເຂົ້າລະຫັດດ້ວຍຮາດແວ (Hardware encoding)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Hardware Encoding ຊ່ວຍຫຼຸດການໃຊ້ງານ CPU ໄດ້ຫຼາຍ, ແຕ່ອາດຈະຕ້ອງການບິດເຣດທີ່ສູງກວ່າເພື່ອໃຫ້ໄດ້ຄຸນນະພາບລະດັບດຽວກັນ.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"ຄຳເຕືອນການສະຕຣີມ\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"ການທົດສອບແບນວິດກຳລັງຈະສະຕຣີມຂໍ້ມູນວິດີໂອແບບສຸ່ມທີ່ບໍ່ມີສຽງໄປຍັງຊ່ອງຂອງທ່ານ. ຖ້າເປັນໄປໄດ້, ແນະນຳໃຫ້ປິດການບັນທຶກວິດີໂອການສະຕຣີມຊົ່ວຄາວ ແລະ ຕັ້ງຄ່າການສະຕຣີມເປັນສ່ວນຕົວ ຈົນກວ່າການທົດສອບຈະສຳເລັດ. ດຳເນີນການຕໍ່ບໍ່?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"ທົດສອບ %1\"\nBasic.AutoConfig.TestPage=\"ຜົນລັບສຸດທ້າຍ\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"ໂປຣແກຣມກຳລັງດຳເນີນການທົດສອບຊຸດໜຶ່ງ ເພື່ອປະເມີນຄ່າທີ່ເໝາະສົມທີ່ສຸດ\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"ການທົດສອບສຳເລັດ\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"ກຳລັງທົດສອບແບນວິດ, ອາດໃຊ້ເວລາສອງສາມນາທີ...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"ບໍ່ພົບ Output ສຳລັບໂປຣໂຕຄໍຂອງບໍລິການນີ້\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"ກຳລັງເຊື່ອມຕໍ່ໄປຍັງ: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"ບໍ່ສາມາດເຊື່ອມຕໍ່ກັບເຊີບເວີໃດໆໄດ້, ກະລຸນາກວດສອບການເຊື່ອມຕໍ່ອິນເຕີເນັດຂອງທ່ານ ແລະ ລອງໃໝ່ອີກຄັ້ງ.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"ກຳລັງທົດສອບແບນວິດສຳລັບ: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"ກຳລັງທົດສອບຕົວເຂົ້າລະຫັດການສະຕຣີມ, ອາດໃຊ້ເວລາໜຶ່ງນາທີ...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"ກຳລັງທົດສອບຕົວເຂົ້າລະຫັດການບັນທຶກ, ອາດໃຊ້ເວລາໜຶ່ງນາທີ...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"ບໍ່ສາມາດເລີ່ມຕົ້ນຕົວເຂົ້າລະຫັດໄດ້\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"ກຳລັງທົດສອບ %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"ຕົວເຂົ້າລະຫັດການສະຕຣີມ\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"ຕົວເຂົ້າລະຫັດການບັນທຶກ\"\nBasic.AutoConfig.TestPage.Result.Header=\"ໂປຣແກຣມໄດ້ກຳນົດວ່າ ຄ່າປະເມີນເຫຼົ່ານີ້ ເໝາະສົມທີ່ສຸດສຳລັບທ່ານ:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"ເພື່ອໃຊ້ການຕັ້ງຄ່າເຫຼົ່ານີ້, ໃຫ້ກົດ ນຳໃຊ້ການຕັ້ງຄ່າ. ເພື່ອຕັ້ງຄ່າຕົວຊ່ວຍໃໝ່ ແລະ ລອງອີກຄັ້ງ, ໃຫ້ກົດ ກັບຄືນ. ເພື່ອກຳນົດການຕັ້ງຄ່າດ້ວຍຕົວເອງ, ໃຫ້ກົດ ຍົກເລີກ ແລະ ເປີດ ການຕັ້ງຄ່າ.\"\nBasic.AutoConfig.Info=\"ຕົວຊ່ວຍການຕັ້ງຄ່າອັດຕະໂນມັດ ຈະກຳນົດການຕັ້ງຄ່າທີ່ດີທີ່ສຸດ ໂດຍອີງໃສ່ສະເປັກຄອມພິວເຕີ ແລະ ຄວາມໄວອິນເຕີເນັດຂອງທ່ານ.\"\nBasic.AutoConfig.RunAnytime=\"ສາມາດສັ່ງງານໄດ້ທຸກເມື່ອ ໂດຍໄປທີ່ເມນູ ເຄື່ອງມື.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"ຄວາມລະອຽດການສະຕຣີມ (Scaled)\"\nBasic.Stats=\"ສະຖິຕິ\"\nBasic.Stats.CPUUsage=\"ການໃຊ້ງານ CPU\"\nBasic.Stats.HDDSpaceAvailable=\"ພື້ນທີ່ດິສກ໌ທີ່ຫວ່າງ\"\nBasic.Stats.MemoryUsage=\"ການໃຊ້ງານໜ່ວຍຄວາມຈຳ\"\nBasic.Stats.AverageTimeToRender=\"ເວລາສະເລ່ຍໃນການ Render ເຟຣມ\"\nBasic.Stats.SkippedFrames=\"ເຟຣມທີ່ຂ້າມໄປ ເນື່ອງຈາກການເຂົ້າລະຫັດຊັກຊ້າ\"\nBasic.Stats.MissedFrames=\"ເຟຣມທີ່ພາດໄປ ເນື່ອງຈາກການ Render ຊັກຊ້າ\"\nBasic.Stats.Output.Stream=\"ສະຕຣີມ\"\nBasic.Stats.Output.Recording=\"ການບັນທຶກ\"\nBasic.Stats.Status=\"ສະຖານະ\"\nBasic.Stats.Status.Recording=\"ກຳລັງບັນທຶກ\"\nBasic.Stats.Status.Live=\"ຖ່າຍທອດສົດ\"\nBasic.Stats.Status.Reconnecting=\"ກຳລັງເຊື່ອມຕໍ່ໃໝ່\"\nBasic.Stats.Status.Inactive=\"ບໍ່ໄດ້ເຮັດວຽກ\"\nBasic.Stats.Status.Active=\"ກຳລັງເຮັດວຽກ\"\nBasic.Stats.DroppedFrames=\"ເຟຣມຕົກ (ເຄືອຂ່າຍ)\"\nBasic.Stats.MegabytesSent=\"ຂໍ້ມູນສົ່ງອອກທັງໝົດ\"\nBasic.Stats.Bitrate=\"ບິດເຣດ\"\nBasic.Stats.DiskFullIn=\"ດິສກ໌ຈະເຕັມໃນອີກ (ປະມານ)\"\nBasic.Stats.DiskFullIn.Text=\"%1 ຊົ່ວໂມງ, %2 ນາທີ\"\nBasic.Stats.ResetStats=\"ຣີເຊັດສະຖິຕິ\"\nResetUIWarning.Title=\"ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການຣີເຊັດ UI?\"\nResetUIWarning.Text=\"ການຣີເຊັດ UI ຈະເຊື່ອງ Docks ເພີ່ມເຕີມຕ່າງໆ. ທ່ານຈະຕ້ອງຍົກເລີກການເຊື່ອງ Docks ເຫຼົ່ານີ້ຈາກເມນູ Docks ຫາກທ່ານຕ້ອງການໃຫ້ມັນສະແດງຂຶ້ນມາ.\\n\\nທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການຣີເຊັດ UI?\"\nUpdater.Title=\"ມີອັບເດດໃໝ່\"\nUpdater.Text=\"ມີອັບເດດໃໝ່ພ້ອມໃຫ້ໃຊ້ງານ:\"\nUpdater.UpdateNow=\"ອັບເດດດຽວນີ້\"\nUpdater.RemindMeLater=\"ເຕືອນຂ້ອຍພາຍຫຼັງ\"\nUpdater.Skip=\"ຂ້າມເວີຊັນນີ້\"\nUpdater.NoUpdatesAvailable.Title=\"ບໍ່ມີອັບເດດ\"\nUpdater.NoUpdatesAvailable.Text=\"ບໍ່ມີອັບເດດໃຫ້ໃຊ້ງານໃນຂະນະນີ້\"\nUpdater.BranchNotFound.Title=\"ຊ່ອງທາງການອັບເດດຖືກລຶບ\"\nUpdater.BranchNotFound.Text=\"ຊ່ອງທາງການອັບເດດທີ່ທ່ານເລືອກບໍ່ສາມາດໃຊ້ໄດ້ອີກຕໍ່ໄປ, OBS ຖືກຣີເຊັດເປັນຄ່າເລີ່ມຕົ້ນ.\"\nUpdater.RepairButUpdatesAvailable.Title=\"ບໍ່ສາມາດກວດສອບຄວາມສົມບູນໄດ້\"\nUpdater.RepairButUpdatesAvailable.Text=\"ການກວດສອບຄວາມສົມບູນຂອງໄຟລ໌ ສາມາດເຮັດໄດ້ສະເພາະກັບເວີຊັນຫຼ້າສຸດເທົ່ານັ້ນ. ໃຫ້ໃຊ້ ວິທີໃຊ້ → ກວດຫາການອັບເດດ ເພື່ອກວດສອບ ແລະ ອັບເດດການຕິດຕັ້ງ OBS ຂອງທ່ານ.\"\nUpdater.RepairConfirm.Title=\"ຢືນຢັນການກວດສອບຄວາມສົມບູນ\"\nUpdater.RepairConfirm.Text=\"ການເລີ່ມກວດສອບຄວາມສົມບູນ ຈະສະແກນການຕິດຕັ້ງ OBS ຂອງທ່ານ ເພື່ອຫາໄຟລ໌ທີ່ເສຍຫາຍ ແລະ ດາວໂຫຼດໄຟລ໌ທີ່ເສຍ/ຖືກດັດແປງ ມາໃໝ່. ຂັ້ນຕອນນີ້ອາດໃຊ້ເວລາຄາວໜຶ່ງ.\\n\\nທ່ານຕ້ອງການດຳເນີນການຕໍ່ບໍ່?\"\nUpdater.FailedToLaunch=\"ເປີດຕົວອັບເດດບໍ່ສຳເລັດ\"\nQuickTransitions.SwapScenes=\"ສັບປ່ຽນສາກ ຕົວຢ່າງ/ໂປຣແກຣມ ຫຼັງຈາກປ່ຽນສາກ\"\nQuickTransitions.SwapScenesTT=\"ສັບປ່ຽນສາກຕົວຢ່າງ ແລະ ສາກໂປຣແກຣມ ຫຼັງຈາກປ່ຽນສາກ (ຫາກສາກເດີມຂອງໂປຣແກຣມຍັງມີຢູ່).\\nສິ່ງນີ້ຈະບໍ່ຍົກເລີກການປ່ຽນແປງໃດໆ ທີ່ອາດຈະເກີດຂຶ້ນກັບສາກເດີມຂອງໂປຣແກຣມ.\"\nQuickTransitions.DuplicateScene=\"ສຳເນົາສາກ\"\nQuickTransitions.DuplicateSceneTT=\"ເມື່ອແກ້ໄຂສາກດຽວກັນ, ອະນຸຍາດໃຫ້ແກ້ໄຂ ການແປງ (transform)/ການເບິ່ງເຫັນ ຂອງແຫຼ່ງທີ່ມາ ໂດຍບໍ່ແກ້ໄຂ output ຂອງໂປຣແກຣມ.\\nເພື່ອແກ້ໄຂຄຸນສົມບັດຂອງແຫຼ່ງທີ່ມາໂດຍບໍ່ແກ້ໄຂ output ຂອງໂປຣແກຣມ, ໃຫ້ເປີດໃຊ້ 'ສຳເນົາ ແຫຼ່ງທີ່ມາ'.\\nການປ່ຽນແປງຄ່ານີ້ຈະຣີເຊັດສາກໂປຣແກຣມປັດຈຸບັນ (ຫາກຍັງມີຢູ່).\"\nQuickTransitions.EditProperties=\"ສຳເນົາ ແຫຼ່ງທີ່ມາ\"\nQuickTransitions.EditPropertiesTT=\"ເມື່ອແກ້ໄຂສາກດຽວກັນ, ອະນຸຍາດໃຫ້ແກ້ໄຂຄຸນສົມບັດຂອງແຫຼ່ງທີ່ມາ ໂດຍບໍ່ແກ້ໄຂ output ຂອງໂປຣແກຣມ.\\nສາມາດໃຊ້ໄດ້ສະເພາະເມື່ອເປີດໃຊ້ 'ສຳເນົາສາກ'.\\nແຫຼ່ງທີ່ມາບາງຢ່າງ (ເຊັ່ນ: ການ capture ຫຼື ສື່) ບໍ່ຮອງຮັບສິ່ງນີ້ ແລະ ບໍ່ສາມາດແກ້ໄຂແຍກກັນໄດ້.\\nການປ່ຽນແປງຄ່ານີ້ຈະຣີເຊັດສາກໂປຣແກຣມປັດຈຸບັນ (ຫາກຍັງມີຢູ່).\\n\\nຄຳເຕືອນ: ເນື່ອງຈາກແຫຼ່ງທີ່ມາຈະຖືກສຳເນົາ, ສິ່ງນີ້ອາດຕ້ອງການຊັບພະຍາກອນລະບົບ ຫຼື ວິດີໂອເພີ່ມເຕີມ.\"\nQuickTransitions.HotkeyName=\"ການປ່ຽນສາກດ່ວນ: %1\"\nBasic.AddTransition=\"ເພີ່ມ ການປ່ຽນສາກທີ່ກຳນົດຄ່າໄດ້\"\nBasic.RemoveTransition=\"ລຶບ ການປ່ຽນສາກທີ່ກຳນົດຄ່າໄດ້\"\nBasic.TransitionProperties=\"ຄຸນສົມບັດການປ່ຽນສາກ\"\nBasic.SceneTransitions=\"ການປ່ຽນສາກ\"\nBasic.TransitionDuration=\"ໄລຍະເວລາ\"\nBasic.TogglePreviewProgramMode=\"ໂໝດສະຕູດິໂອ\"\nBasic.EnablePreviewProgramMode=\"ເປີດໃຊ້ ໂໝດສະຕູດິໂອ\"\nBasic.DisablePreviewProgramMode=\"ປິດໃຊ້ ໂໝດສະຕູດິໂອ\"\nUndo.Undo=\"ກັບຄືນ (Undo)\"\nUndo.Redo=\"ເຮັດຊ້ຳ (Redo)\"\nUndo.Add=\"ເພີ່ມ '%1'\"\nUndo.Delete=\"ລຶບ '%1'\"\nUndo.Rename=\"ປ່ຽນຊື່ '%1'\"\nUndo.SceneCollection.Switch=\"ສະລັບໄປ '%1'\"\nUndo.Item.Undo=\"ກັບຄືນ %1\"\nUndo.Item.Redo=\"ເຮັດຊ້ຳ %1\"\nUndo.Sources.Multi=\"ລຶບ %1 ແຫຼ່ງທີ່ມາ\"\nUndo.Filters=\"ການປ່ຽນແປງຟິວເຕີໃນ '%1'\"\nUndo.Filters.Paste.Single=\"ວາງ ຟິວເຕີ '%1' ໃສ່ '%2'\"\nUndo.Filters.Paste.Multiple=\"ສຳເນົາ ຟິວເຕີ ຈາກ '%1' ໄປ '%2'\"\nUndo.Transform=\"ແປງ (Transform) ແຫຼ່ງທີ່ມາ ໃນ '%1'\"\nUndo.Transform.Paste=\"ວາງ ການແປງ (Transformation) ໃນ '%1'\"\nUndo.Transform.Rotate=\"ໝຸນ ໃນ '%1'\"\nUndo.Transform.Reset=\"ຣີເຊັດ ການແງ ໃນ '%1'\"\nUndo.Transform.HFlip=\"ພິກແນວນອນ ໃນ '%1'\"\nUndo.Transform.VFlip=\"ພິກແນວຕັ້ງ ໃນ '%1'\"\nUndo.Transform.FitToScreen=\"ປັບໃຫ້ພໍດີໜ້າຈໍ ໃນ '%1'\"\nUndo.Transform.StretchToScreen=\"ຢືດໃຫ້ເຕັມໜ້າຈໍ ໃນ '%1'\"\nUndo.Transform.Center=\"ຈັດເຄິ່ງກາງໜ້າຈໍ ໃນ '%1'\"\nUndo.Transform.VCenter=\"ຈັດເຄິ່ງກາງແນວຕັ້ງ ໃນ '%1'\"\nUndo.Transform.HCenter=\"ຈັດເຄິ່ງກາງແນວນອນ ໃນ '%1'\"\nUndo.Volume.Change=\"ການປ່ຽນແປງລະດັບສຽງໃນ '%1'\"\nUndo.Volume.Mute=\"ປິດສຽງ '%1'\"\nUndo.Volume.Unmute=\"ເປີດສຽງ '%1'\"\nUndo.Balance.Change=\"ການປ່ຽນແປງຄວາມສົມດຸນສຽງໃນ '%1'\"\nUndo.SyncOffset.Change=\"ການປ່ຽນແປງ Audio Sync Offset ໃນ '%1'\"\nUndo.MonitoringType.Change=\"ປ່ຽນການ Monitor ສຽງໃນ '%1'\"\nUndo.Mixers.Change=\"ປ່ຽນ ມິກເຊີສຽງ ໃນ '%1'\"\nUndo.ForceMono.On=\"ເປີດໃຊ້ ບັງຄັບໂມໂນ ໃນ '%1'\"\nUndo.ForceMono.Off=\"ປິດໃຊ້ ບັງຄັບໂມໂນ ໃນ '%1'\"\nUndo.Properties=\"ການປ່ຽນແປງຄຸນສົມບັດໃນ '%1'\"\nUndo.Scene.Duplicate=\"ສຳເນົາ ສາກ '%1'\"\nUndo.ShowTransition=\"ສະແດງ ການປ່ຽນສາກ ໃນ '%1'\"\nUndo.HideTransition=\"ເຊື່ອງ ການປ່ຽນສາກ ໃນ '%1'\"\nUndo.ShowSceneItem=\"ສະແດງ '%1' ໃນ '%2'\"\nUndo.HideSceneItem=\"ເຊື່ອງ '%1' ໃນ '%2'\"\nUndo.ReorderSources=\"ຈັດລຳດັບແຫຼ່ງທີ່ມາໃໝ່ໃນ '%1'\"\nUndo.MoveUp=\"ຍັບ '%1' ຂຶ້ນ ໃນ '%2'\"\nUndo.MoveDown=\"ຍັບ '%1' ລົງ ໃນ '%2'\"\nUndo.MoveToTop=\"ຍັບ '%1' ໄປເທິງສຸດ ໃນ '%2'\"\nUndo.MoveToBottom=\"ຍັບ '%1' ໄປລຸ່ມສຸດ ໃນ '%2'\"\nUndo.PasteSource=\"ວາງ ແຫຼ່ງທີ່ມາ ໃນ '%1'\"\nUndo.PasteSourceRef=\"ວາງ ການອ້າງອີງແຫຼ່ງທີ່ມາ ໃນ '%1'\"\nUndo.GroupItems=\"ຈັດກຸ່ມລາຍການເຂົ້າໃນ '%1'\"\nTransitionNameDlg.Text=\"ກະລຸນາໃສ່ຊື່ຂອງການປ່ຽນສາກ\"\nTransitionNameDlg.Title=\"ຊື່ການປ່ຽນສາກ\"\nTitleBar.SafeMode=\"ໂໝດປອດໄພ (SAFE MODE)\"\nTitleBar.PortableMode=\"ໂໝດພົກພາ (Portable Mode)\"\nTitleBar.Profile=\"ໂປຣໄຟລ໌\"\nTitleBar.Scenes=\"ສາກ\"\nNameExists.Title=\"ມີຊື່ນີ້ຢູ່ແລ້ວ\"\nNameExists.Text=\"ຊື່ນີ້ຖືກໃຊ້ງານແລ້ວ.\"\nNoNameEntered.Title=\"ກະລຸນາໃສ່ຊື່ທີ່ຖືກຕ້ອງ\"\nNoNameEntered.Text=\"ທ່ານບໍ່ສາມາດໃຊ້ຊື່ຫວ່າງເປົ່າໄດ້.\"\nConfirmStart.Title=\"ເລີ່ມສະຕຣີມ?\"\nConfirmStart.Text=\"ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການເລີ່ມສະຕຣີມ?\"\nConfirmStop.Title=\"ຢຸດສະຕຣີມ?\"\nConfirmStop.Text=\"ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການຢຸດສະຕຣີມ?\"\nConfirmStopRecord.Title=\"ຢຸດບັນທຶກ?\"\nConfirmStopRecord.Text=\"ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການຢຸດບັນທຶກ?\"\nConfirmBWTest.Title=\"ເລີ່ມທົດສອບແບນວິດ?\"\nConfirmBWTest.Text=\"ທ່ານໄດ້ຕັ້ງຄ່າ OBS ໃນໂໝດທົດສອບແບນວິດ. ໂໝດນີ້ອະນຸຍາດໃຫ້ທົດສອບເຄືອຂ່າຍໂດຍທີ່ຊ່ອງຂອງທ່ານບໍ່ໄດ້ຖ່າຍທອດສົດອອກໄປແທ້ໆ. ເມື່ອທ່ານທົດສອບສຳເລັດແລ້ວ, ທ່ານຈະຕ້ອງປິດການໃຊ້ງານມັນ ເພື່ອໃຫ້ຜູ້ຊົມສາມາດເຫັນການສະຕຣີມຂອງທ່ານໄດ້.\\n\\nທ່ານຕ້ອງການດຳເນີນການຕໍ່ບໍ່?\"\nConfirmExit.Title=\"Output ຍັງເຮັດວຽກຢູ່\"\nConfirmExit.Text=\"OBS ຍັງເຮັດວຽກຢູ່. ການສະຕຣີມ/ການບັນທຶກ ທັງໝົດຈະຖືກປິດລົງ.\"\nConfirmRemove.Title=\"ຢືນຢັນການລຶບ\"\nConfirmRemove.Text=\"ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບ '%1'?\"\nConfirmRemove.TextMultiple=\"ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບ %1 ລາຍການ?\"\nConfirmReset.Title=\"ຣີເຊັດຄຸນສົມບັດ\"\nConfirmReset.Text=\"ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການຣີເຊັດຄຸນສົມບັດປັດຈຸບັນ ເປັນຄ່າເລີ່ມຕົ້ນ?\"\nOutput.StartStreamFailed=\"ເລີ່ມການສະຕຣີມບໍ່ສຳເລັດ\"\nOutput.StartRecordingFailed=\"ເລີ່ມການບັນທຶກບໍ່ສຳເລັດ\"\nOutput.StartReplayFailed=\"ເລີ່ມ Replay buffer ບໍ່ສຳເລັດ\"\nOutput.StartVirtualCamFailed=\"ເລີ່ມ ກ້ອງສະເໝືອນ (Virtual camera) ບໍ່ສຳເລັດ\"\nOutput.StartFailedGeneric=\"ການເລີ່ມ Output ລົ້ມເຫຼວ. ກະລຸນາກວດເບິ່ງ log ສຳລັບລາຍລະອຽດ.\\n\\nໝາຍເຫດ: ຖ້າທ່ານກຳລັງໃຊ້ຕົວເຂົ້າລະຫັດ NVENC ຫຼື AMD, ກະລຸນາກວດສອບໃຫ້ແນ່ໃຈວ່າ ໄດຣເວີວິດີໂອຂອງທ່ານເປັນເວີຊັນຫຼ້າສຸດ.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"ບໍ່ສາມາດບັນທຶກ Replay ໃນຂະນະທີ່ຢຸດຊົ່ວຄາວໄດ້\"\nOutput.ReplayBuffer.PauseWarning.Text=\"ຄຳເຕືອນ: ບໍ່ສາມາດບັນທຶກ Replay ໃນຂະນະທີ່ການບັນທຶກຖືກຢຸດຊົ່ວຄາວ.\"\nOutput.ConnectFail.Title=\"ການເຊື່ອມຕໍ່ລົ້ມເຫຼວ\"\nOutput.ConnectFail.BadPath=\"Path ຫຼື URL ການເຊື່ອມຕໍ່ ບໍ່ຖືກຕ້ອງ. ກະລຸນາກວດສອບການຕັ້ງຄ່າຂອງທ່ານເພື່ອຢືນຢັນວ່າຖືກຕ້ອງ.\"\nOutput.ConnectFail.ConnectFailed=\"ການເຊື່ອມຕໍ່ກັບເຊີບເວີລົ້ມເຫຼວ\"\nOutput.ConnectFail.InvalidStream=\"ບໍ່ສາມາດເຂົ້າເຖິງ ຊ່ອງ ຫຼື stream key ທີ່ລະບຸໄດ້, ກະລຸນາກວດສອບ stream key ຂອງທ່ານອີກຄັ້ງ. ຖ້າມັນຖືກຕ້ອງ, ອາດຈະມີບັນຫາໃນການເຊື່ອມຕໍ່ກັບເຊີບເວີ.\"\nOutput.ConnectFail.HdrDisabled=\"Output ແບບ HDR ຖືກປິດການໃຊ້ງານຢູ່ໃນຂະນະນີ້ສຳລັບ Output ນີ້.\"\nOutput.ConnectFail.Error=\"ເກີດຂໍ້ຜິດພາດທີ່ບໍ່ຄາດຄິດຂະນະພະຍາຍາມເຊື່ອມຕໍ່ກັບເຊີບເວີ. ຂໍ້ມູນເພີ່ມເຕີມຢູ່ໃນໄຟລ໌ log.\"\nOutput.ConnectFail.Disconnected=\"ຕັດການເຊື່ອມຕໍ່ຈາກເຊີບເວີແລ້ວ.\"\nOutput.StreamEncodeError.Title=\"ຂໍ້ຜິດພາດການເຂົ້າລະຫັດ\"\nOutput.StreamEncodeError.Msg=\"ເກີດຂໍ້ຜິດພາດຂອງຕົວເຂົ້າລະຫັດຂະນະສະຕຣີມ.\"\nOutput.StreamEncodeError.Msg.LastError=\"ເກີດຂໍ້ຜິດພາດຂອງຕົວເຂົ້າລະຫັດຂະນະສະຕຣີມ:<br><br>%1\"\nOutput.RecordFail.Title=\"ເລີ່ມການບັນທຶກບໍ່ສຳເລັດ\"\nOutput.RecordFail.Unsupported=\"ຮູບແບບ Output ບໍ່ຮອງຮັບ ຫຼື ບໍ່ຮອງຮັບແທຣັກສຽງຫຼາຍກວ່າໜຶ່ງແທຣັກ. ກະລຸນາກວດສອບການຕັ້ງຄ່າຂອງທ່ານ ແລະ ລອງໃໝ່ອີກຄັ້ງ.\"\nOutput.RecordNoSpace.Title=\"ພື້ນທີ່ດິສກ໌ບໍ່ພຽງພໍ\"\nOutput.RecordNoSpace.Msg=\"ພື້ນທີ່ດິສກ໌ບໍ່ພຽງພໍທີ່ຈະດຳເນີນການບັນທຶກຕໍ່.\"\nOutput.RecordError.Title=\"ຂໍ້ຜິດພາດການບັນທຶກ\"\nOutput.RecordError.Msg=\"ເກີດຂໍ້ຜິດພາດທີ່ບໍ່ລະບຸຂະນະບັນທຶກ.\"\nOutput.RecordError.EncodeErrorMsg=\"ເກີດຂໍ້ຜິດພາດຂອງຕົວເຂົ້າລະຫັດຂະນະບັນທຶກ.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"ເກີດຂໍ້ຜິດພາດຂອງຕົວເຂົ້າລະຫັດຂະນະບັນທຶກ:<br><br>%1\"\nOutput.BadPath.Title=\"File Path ບໍ່ຖືກຕ້ອງ\"\nOutput.BadPath.Text=\"ບໍ່ສາມາດເປີດ Recording Path ທີ່ຕັ້ງຄ່າໄວ້ໄດ້. ກະລຸນາກວດສອບ Recording Path ຂອງທ່ານໃນ ການຕັ້ງຄ່າ → ສົ່ງອອກ → ການບັນທຶກ.\"\nOutput.NoBroadcast.Title=\"ບໍ່ໄດ້ຕັ້ງຄ່າການອອກອາກາດ\"\nOutput.NoBroadcast.Text=\"ທ່ານຈຳເປັນຕ້ອງຕັ້ງຄ່າການອອກອາກາດ ກ່ອນທີ່ທ່ານຈະສາມາດເລີ່ມສະຕຣີມໄດ້.\"\nOutput.BroadcastStartFailed=\"ເລີ່ມການອອກອາກາດບໍ່ສຳເລັດ\"\nOutput.BroadcastStopFailed=\"ຢຸດການອອກອາກາດບໍ່ສຳເລັດ\"\nLogUploadDialog.Title=\"ອັບໂຫຼດໄຟລ໌ Log ຂອງ OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"ກະລຸນາອ່ານ <a href='https://obsproject.com/privacy-policy'>ນະໂຍບາຍຄວາມເປັນສ່ວນຕົວ</a> ແລະ ພາກສ່ວນທີ່ກ່ຽວກັບການອັບໂຫຼດໄຟລ໌ ກ່ອນທີ່ຈະອັບໂຫຼດໄຟລ໌ໃດໆ.\"\nLogUploadDialog.Labels.Progress=\"ກຳລັງອັບໂຫຼດ Log. ກະລຸນາລໍຖ້າ...\"\nLogUploadDialog.Labels.Description.AppLog=\"ໄຟລ໌ Log ຂອງທ່ານຖືກອັບໂຫຼດແລ້ວ. ຕອນນີ້ທ່ານສາມາດແບ່ງປັນ URL ເພື່ອຈຸດປະສົງໃນການແກ້ໄຂບັນຫາ ຫຼື ການຊັບພອດ.\"\nLogUploadDialog.Labels.Description.CrashLog=\"ລາຍງານການ Crash ຂອງທ່ານຖືກອັບໂຫຼດແລ້ວ. ຕອນນີ້ທ່ານສາມາດແບ່ງປັນ URL ເພື່ອຈຸດປະສົງໃນການແກ້ໄຂບັນຫາ.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"ອັບໂຫຼດ\"\nLogUploadDialog.Buttons.CopyURL=\"ສຳເນົາ Log URL\"\nLogUploadDialog.Buttons.AnalyzeURL=\"ວິເຄາະໄຟລ໌ Log\"\nLogUploadDialog.Buttons.RetryButton=\"ລອງໃໝ່\"\nLogUploadDialog.Errors.Template=\"ເກີດຂໍ້ຜິດພາດຂະນະພະຍາຍາມອັບໂຫຼດໄຟລ໌:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"ບໍ່ພົບໄຟລ໌ທີ່ຈະອັບໂຫຼດ ຫຼື ໄຟລ໌ຫວ່າງເປົ່າ.\"\nRemux.SourceFile=\"ການບັນທຶກ OBS\"\nRemux.TargetFile=\"ໄຟລ໌ເປົ້າໝາຍ\"\nRemux.Remux=\"ຣີມັກສ໌ (Remux)\"\nRemux.Stop=\"ຢຸດການຣີມັກສ໌\"\nRemux.ClearFinished=\"ລຶບລາຍການທີ່ສຳເລັດ\"\nRemux.ClearAll=\"ລຶບລາຍການທັງໝົດ\"\nRemux.OBSRecording=\"ການບັນທຶກ OBS\"\nRemux.FinishedTitle=\"ການຣີມັກສ໌ສຳເລັດ\"\nRemux.Finished=\"ຣີມັກສ໌ການບັນທຶກແລ້ວ\"\nRemux.FinishedError=\"ຣີມັກສ໌ການບັນທຶກແລ້ວ, ແຕ່ໄຟລ໌ອາດຈະບໍ່ສົມບູນ\"\nRemux.SelectRecording=\"ເລືອກ ການບັນທຶກ OBS...\"\nRemux.SelectTarget=\"ເລືອກ ໄຟລ໌ເປົ້າໝາຍ...\"\nRemux.FileExistsTitle=\"ມີໄຟລ໌ເປົ້າໝາຍຢູ່ແລ້ວ\"\nRemux.FileExists=\"ມີໄຟລ໌ເປົ້າໝາຍຕໍ່ໄປນີ້ຢູ່ແລ້ວ. ທ່ານຕ້ອງການຂຽນທັບພວກມັນບໍ່?\"\nRemux.ExitUnfinishedTitle=\"ກຳລັງຣີມັກສ໌\"\nRemux.ExitUnfinished=\"ການຣີມັກສ໌ຍັງບໍ່ສຳເລັດ, ການຢຸດຕອນນີ້ອາດເຮັດໃຫ້ໄຟລ໌ເປົ້າໝາຍໃຊ້ງານບໍ່ໄດ້.\\nທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການຢຸດການຣີມັກສ໌?\"\nRemux.HelpText=\"ລາກໄຟລ໌ໃສ່ໜ້າຕ່າງນີ້ເພື່ອຣີມັກສ໌, ຫຼື ເລືອກຊ່ອງ \\\"ການບັນທຶກ OBS\\\" ທີ່ຫວ່າງຢູ່ ເພື່ອເປີດຫາໄຟລ໌.\"\nRemux.NoFilesAddedTitle=\"ບໍ່ໄດ້ເພີ່ມໄຟລ໌ຣີມັກສ໌\"\nRemux.NoFilesAdded=\"ບໍ່ມີໄຟລ໌ຖືກເພີ່ມເພື່ອຣີມັກສ໌. ໃຫ້ລາກໂຟນເດີທີ່ມີໄຟລ໌ວິດີໂອໜຶ່ງ ຫຼື ຫຼາຍໄຟລ໌ເຂົ້າມາ.\"\nMissingFiles=\"ໄຟລ໌ທີ່ຫາຍໄປ\"\nMissingFiles.MissingFile=\"ໄຟລ໌ທີ່ຫາຍໄປ\"\nMissingFiles.NewFile=\"ໄຟລ໌ໃໝ່\"\nMissingFiles.HelpText=\"ບາງໄຟລ໌ຫາຍໄປຕັ້ງແຕ່ຄັ້ງລ່າສຸດທີ່ທ່ານໃຊ້ OBS.\"\nMissingFiles.Clear=\"<ລຶບລ້າງ>\"\nMissingFiles.NumFound=\"ພົບ %1 ຈາກ %2\"\nMissingFiles.Search=\"ຄົ້ນຫາ ໄດເຣັກທໍຣີ...\"\nMissingFiles.SelectFile=\"ເລືອກໄຟລ໌...\"\nMissingFiles.SelectDir=\"ເລືອກໂຟນເດີທີ່ຈະຄົ້ນຫາ\"\nMissingFiles.State=\"ສະຖານະ\"\nMissingFiles.Missing=\"ຫາຍໄປ\"\nMissingFiles.Replaced=\"ແທນທີ່ແລ້ວ\"\nMissingFiles.Cleared=\"ລຶບລ້າງແລ້ວ\"\nMissingFiles.Found=\"ພົບແລ້ວ\"\nMissingFiles.AutoSearch=\"ພົບໄຟລ໌ເພີ່ມເຕີມທີ່ກົງກັນ\"\nMissingFiles.AutoSearchText=\"OBS ພົບໄຟລ໌ທີ່ກົງກັນເພີ່ມເຕີມສຳລັບໄຟລ໌ທີ່ຫາຍໄປໃນໄດເຣັກທໍຣີນັ້ນ. ທ່ານຕ້ອງການເພີ່ມພວກມັນບໍ່?\"\nMissingFiles.NoMissing.Title=\"ກວດສອບໄຟລ໌ທີ່ຫາຍໄປ\"\nMissingFiles.NoMissing.Text=\"ບໍ່ມີໄຟລ໌ໃດຫາຍໄປ.\"\nMacPermissions.MenuAction=\"ກວດສອບການອະນຸຍາດແອັບ...\"\nMacPermissions.Title=\"ກວດສອບການອະນຸຍາດແອັບ\"\nMacPermissions.Description=\"OBS Studio ຕ້ອງການການອະນຸຍາດຈາກທ່ານ ເພື່ອໃຫ້ສາມາດໃຫ້ບໍລິການບາງຟີເຈີໄດ້. ແນະນຳໃຫ້ເປີດໃຊ້ການອະນຸຍາດເຫຼົ່ານີ້, ແຕ່ບໍ່ຈຳເປັນຕ້ອງໃຊ້ເພື່ອໃຊ້ງານແອັບ. ທ່ານສາມາດເປີດໃຊ້ມັນພາຍຫຼັງໄດ້ສະເໝີ.\"\nMacPermissions.Description.OpenDialog=\"ທ່ານສາມາດເປີດກ່ອງຂໍ້ຄວາມນີ້ໄດ້ໃໝ່ຜ່ານເມນູ OBS Studio.\"\nMacPermissions.AccessGranted=\"ອະນຸຍາດການເຂົ້າເຖິງແລ້ວ\"\nMacPermissions.RequestAccess=\"ຮ້ອງຂໍການເຂົ້າເຖິງ\"\nMacPermissions.OpenPreferences=\"ເປີດ %1 Preferences\"\nMacPermissions.Item.ScreenRecording=\"ການບັນທຶກໜ້າຈໍ\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS ຕ້ອງການການອະນຸຍາດນີ້ ເພື່ອໃຫ້ສາມາດບັນທຶກໜ້າຈໍຂອງທ່ານໄດ້.\"\nMacPermissions.Item.Camera=\"ກ້ອງ\"\nMacPermissions.Item.Camera.Details=\"ການອະນຸຍາດນີ້ຈຳເປັນ ເພື່ອບັນທຶກເນື້ອຫາຈາກ webcam ຫຼື capture card.\"\nMacPermissions.Item.Microphone=\"ໄມໂຄຣໂຟນ\"\nMacPermissions.Item.Microphone.Details=\"OBS ຕ້ອງການການອະນຸຍາດນີ້ ຫາກທ່ານຕ້ອງການບັນທຶກສຽງໄມໂຄຣໂຟນ ຫຼື ອຸປະກອນສຽງພາຍນອກຂອງທ່ານ.\"\nMacPermissions.Item.InputMonitoring=\"ການ Monitor Input\"\nMacPermissions.Item.InputMonitoring.Details=\"ການອະນຸຍາດນີ້ຈຳເປັນສຳລັບໃຫ້ຄີລັດ (Hotkeys) ເຮັດວຽກໄດ້ ໃນຂະນະທີ່ OBS ຢູ່ໃນພື້ນຫຼັງ.\"\nMacPermissions.Item.Accessibility=\"ການເຂົ້າເຖິງ (Accessibility)\"\nMacPermissions.Item.Accessibility.Details=\"ໃນການຕິດຕັ້ງທີ່ເກົ່າກວ່າ, OBS ອາດຈະຢູ່ໃນລາຍການ \\\"Accessibility\\\" ແທນທີ່ຈະເປັນ \\\"Input Monitoring\\\".\"\nMacPermissions.Continue=\"ດຳເນີນການຕໍ່\"\nSourceLeak.Title=\"ຂໍ້ຜິດພາດການລ້າງ Source\"\nSourceLeak.Text=\"ເກີດບັນຫາຂະນະປ່ຽນ Scene collection ແລະ ບາງ sources ບໍ່ສາມາດ unload ໄດ້. ບັນຫານີ້ມັກເກີດຈາກປັກອິນທີ່ບໍ່ຄືນຊັບພະຍາກອນຢ່າງຖືກຕ້ອງ. ກະລຸນາກວດສອບໃຫ້ແນ່ໃຈວ່າປັກອິນທີ່ທ່ານໃຊ້ເປັນເວີຊັນຫຼ້າສຸດ.\\n\\nOBS Studio ຈະປິດລົງດຽວນີ້ ເພື່ອປ້ອງກັນຂໍ້ມູນເສຍຫາຍ.\"\nBasic.DesktopDevice1=\"ສຽງເດັສທັອບ (Desktop Audio)\"\nBasic.DesktopDevice2=\"ສຽງເດັສທັອບ 2\"\nBasic.AuxDevice1=\"ໄມ/Aux\"\nBasic.AuxDevice2=\"ໄມ/Aux 2\"\nBasic.AuxDevice3=\"ໄມ/Aux 3\"\nBasic.AuxDevice4=\"ໄມ/Aux 4\"\nBasic.Scene=\"ສາກ (Scene)\"\nBasic.DisplayCapture=\"ການບັນທຶກໜ້າຈໍ\"\nBasic.Main.PreviewConextMenu.Enable=\"ເປີດໃຊ້ ຕົວຢ່າງ (Preview)\"\nBasic.Main.Preview.Disable=\"ປິດ ຕົວຢ່າງ (Preview)\"\nScaleFiltering=\"ການກອງສະເກລ (Scale Filtering)\"\nBlendingMethod=\"ວິທີການປະສົມ (Blending Method)\"\nBlendingMethod.Default=\"ຄ່າເລີ່ມຕົ້ນ\"\nBlendingMethod.SrgbOff=\"ປິດ SRGB\"\nBlendingMode=\"ໂໝດການປະສົມ (Blending Mode)\"\nVolControl.SliderUnmuted=\"ແຖບເລື່ອນລະດັບສຽງສຳລັບ '%1':\"\nVolControl.SliderMuted=\"ແຖບເລື່ອນລະດັບສຽງສຳລັບ '%1': (ປິດສຽງຢູ່)\"\nVolControl.Mute=\"ປິດສຽງ '%1'\"\nVolControl.Properties=\"ຄຸນສົມບັດສຳລັບ '%1'\"\nVolControl.UnassignedWarning.Title=\"ແຫຼ່ງສຽງທີ່ບໍ່ໄດ້ກຳນົດ\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" ບໍ່ໄດ້ຖືກກຳນົດໃຫ້ກັບແທຣັກສຽງໃດໆ ແລະ ຈະບໍ່ໄດ້ຍິນໃນການສະຕຣີມ ຫຼື ການບັນທຶກ.\\n\\nເພື່ອກຳນົດແຫຼ່ງສຽງໃຫ້ກັບແທຣັກ, ໃຫ້ເປີດ ຄຸນສົມບັດສຽງຂັ້ນສູງ ຜ່ານເມນູຄລິກຂວາ ຫຼື ປຸ່ມຮູບເຟືອງໃນແຖບເຄື່ອງມື mixer dock.\"\nBasic.Main.AddSceneDlg.Title=\"ເພີ່ມສາກ\"\nBasic.Main.AddSceneDlg.Text=\"ກະລຸນາໃສ່ຊື່ຂອງສາກ\"\nBasic.Main.DefaultSceneName.Text=\"ສາກ %1\"\nBasic.Main.AddSceneCollection.Title=\"ເພີ່ມ Scene Collection\"\nBasic.Main.AddSceneCollection.Text=\"ກະລຸນາໃສ່ຊື່ຂອງ Scene collection\"\nBasic.Main.RenameSceneCollection.Title=\"ປ່ຽນຊື່ Scene Collection\"\nBasic.Main.RemigrateSceneCollection.Title=\"ອັບເດດຄວາມລະອຽດ Scene Collection\"\nBasic.Main.RemigrateSceneCollection.Text=\"ທ່ານຕ້ອງການອັບເດດຄວາມລະອຽດ scene collection ຂອງ \\\"%1\\\" ໃຫ້ກົງກັບຄວາມລະອຽດ canvas ຂອງໂປຣໄຟລ໌ປັດຈຸບັນ %2x%3 ຫຼື ບໍ່?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"ບໍ່ສາມາດອັບເດດຄວາມລະອຽດ scene collection ຂະນະທີ່ outputs ກຳລັງເຮັດວຽກ.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"ອັບເດດຄວາມລະອຽດ scene collection ລົ້ມເຫຼວ. ບໍ່ຮູ້ຄວາມລະອຽດເດີມ.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"ຣີເຊັດບໍ່ໄດ້: ການປ່ຽນຄວາມລະອຽດ OBS ລົ້ມເຫຼວ.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"ຣີເຊັດບໍ່ໄດ້: ຄວາມລະອຽດປັດຈຸບັນກົງກັບ base resolution ຂອງ scene collection ແລ້ວ.\"\nAddProfile.Title=\"ເພີ່ມໂປຣໄຟລ໌\"\nAddProfile.Text=\"ກະລຸນາໃສ່ຊື່ຂອງໂປຣໄຟລ໌\"\nAddProfile.WizardCheckbox=\"ສະແດງຕົວຊ່ວຍການຕັ້ງຄ່າອັດຕະໂນມັດ\"\nRenameProfile.Title=\"ປ່ຽນຊື່ໂປຣໄຟລ໌\"\nBasic.Main.MixerRename.Title=\"ປ່ຽນຊື່ແຫຼ່ງສຽງ\"\nBasic.Main.MixerRename.Text=\"ກະລຸນາໃສ່ຊື່ຂອງແຫຼ່ງສຽງ\"\nBasic.Main.PreviewDisabled=\"ຕົວຢ່າງ (Preview) ຖືກປິດການໃຊ້ງານຢູ່\"\nBasic.SourceSelect=\"ສ້າງ/ເລືອກ ແຫຼ່ງທີ່ມາ\"\nBasic.SourceSelect.CreateNew=\"ສ້າງໃໝ່\"\nBasic.SourceSelect.AddExisting=\"ເພີ່ມທີ່ມີຢູ່ແລ້ວ\"\nBasic.SourceSelect.AddVisible=\"ເຮັດໃຫ້ແຫຼ່ງທີ່ມາເບິ່ງເຫັນໄດ້\"\nBasic.Main.Sources.Visibility=\"ການເບິ່ງເຫັນ\"\nBasic.Main.Sources.VisibilityDescription=\"ຄວບຄຸມການເບິ່ງເຫັນຂອງ '%1' ໃນ canvas\"\nBasic.Main.Sources.Lock=\"ລັອກ\"\nBasic.Main.Sources.LockDescription=\"ລັອກຕຳແໜ່ງ ແລະ ຂະໜາດຂອງ '%1' ໃນ canvas\"\nBasic.PropertiesWindow=\"ຄຸນສົມບັດສຳລັບ '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (ເລືອກອັດຕະໂນມັດ: %2)\"\nBasic.PropertiesWindow.SelectColor=\"ເລືອກສີ\"\nBasic.PropertiesWindow.SelectFont=\"ເລືອກຟອນ\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"ເລືອກຟອນ\"\nBasic.PropertiesWindow.ConfirmTitle=\"ການຕັ້ງຄ່າປ່ຽນແປງ\"\nBasic.PropertiesWindow.Confirm=\"ມີການປ່ຽນແປງທີ່ຍັງບໍ່ໄດ້ບັນທຶກ. ທ່ານຕ້ອງການເກັບມັນໄວ້ບໍ່?\"\nBasic.PropertiesWindow.NoProperties=\"ບໍ່ມີຄຸນສົມບັດ\"\nBasic.PropertiesWindow.AddFiles=\"ເພີ່ມໄຟລ໌\"\nBasic.PropertiesWindow.AddDir=\"ເພີ່ມ ໄດເຣັກທໍຣີ\"\nBasic.PropertiesWindow.AddURL=\"ເພີ່ມ Path/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"ເພີ່ມ ໄດເຣັກທໍຣີ ໃສ່ '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"ເພີ່ມໄຟລ໌ໃສ່ '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"ເພີ່ມລາຍການໃສ່ '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"ແກ້ໄຂລາຍການຈາກ '%1'\"\nBasic.PropertiesView.FPS.Simple=\"ຄ່າ FPS ແບບງ່າຍ\"\nBasic.PropertiesView.FPS.Rational=\"ຄ່າ FPS ແບບ Rational\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"ຊ່ວງ FPS ທີ່ໃຊ້ໄດ້:\"\nBasic.PropertiesView.UrlButton.Text=\"ເປີດລິງກ໌ນີ້ໃນເວັບບຣາວເຊີເລີ່ມຕົ້ນຂອງທ່ານບໍ່?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"ເປີດ URL\"\nBasic.InteractionWindow=\"ກຳລັງໂຕ້ຕອບກັບ '%1'\"\nBasic.StatusBar.Reconnecting=\"ຕັດການເຊື່ອມຕໍ່, ກຳລັງເຊື່ອມຕໍ່ໃໝ່ໃນ %2 ວິນາທີ (ພະຍາຍາມຄັ້ງທີ %1)\"\nBasic.StatusBar.AttemptingReconnect=\"ກຳລັງພະຍາຍາມເຊື່ອມຕໍ່ໃໝ່... (ພະຍາຍາມຄັ້ງທີ %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"ເຊື່ອມຕໍ່ໃໝ່ສຳເລັດ\"\nBasic.StatusBar.Delay=\"ດີເລ (%1 ວິນາທີ)\"\nBasic.StatusBar.DelayStartingIn=\"ດີເລ (ຈະເລີ່ມໃນ %1 ວິນາທີ)\"\nBasic.StatusBar.DelayStoppingIn=\"ດີເລ (ຈະຢຸດໃນ %1 ວິນາທີ)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"ດີເລ (ຈະຢຸດໃນ %1 ວິນາທີ, ຈະເລີ່ມໃນ %2 ວິນາທີ)\"\nBasic.StatusBar.RecordingSavedTo=\"ບັນທຶກໄວ້ທີ່ '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Replay buffer ບັນທຶກໄວ້ທີ່ '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"ພາບໜ້າຈໍບັນທຶກໄວ້ທີ່ '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Auto remux ການບັນທຶກໄປທີ່ '%1'\"\nBasic.Filters=\"ຟິວເຕີ\"\nBasic.Filters.AsyncFilters=\"ຟິວເຕີ ສຽງ/ວິດີໂອ\"\nBasic.Filters.AudioFilters=\"ຟິວເຕີ ສຽງ\"\nBasic.Filters.EffectFilters=\"ຟິວເຕີ ເອັບເຟັກ\"\nBasic.Filters.Title=\"ຟິວເຕີ ສຳລັບ '%1'\"\nBasic.Filters.AddFilter.Title=\"ຊື່ຟິວເຕີ\"\nBasic.Filters.AddFilter.Text=\"ກະລຸນາລະບຸຊື່ຂອງຟິວເຕີ\"\nBasic.TransformWindow=\"ການປັບແຕ່ງສາກ (Scene Item Transform)\"\nBasic.TransformWindow.Position=\"ຕຳແໜ່ງ\"\nBasic.TransformWindow.PositionX=\"ຕຳແໜ່ງ X\"\nBasic.TransformWindow.PositionY=\"ຕຳແໜ່ງ Y\"\nBasic.TransformWindow.Rotation=\"ການໝຸນ\"\nBasic.TransformWindow.Size=\"ຂະໜາດ\"\nBasic.TransformWindow.Width=\"ຄວາມກວ້າງ\"\nBasic.TransformWindow.Height=\"ຄວາມສູງ\"\nBasic.TransformWindow.Alignment=\"ການຈັດຕຳແໜ່ງ\"\nBasic.TransformWindow.BoundsType=\"ປະເພດ Bounding Box\"\nBasic.TransformWindow.BoundsAlignment=\"ການຈັດຕຳແໜ່ງໃນ Bounding Box\"\nBasic.TransformWindow.Bounds=\"ຂະໜາດ Bounding Box\"\nBasic.TransformWindow.BoundsWidth=\"ຄວາມກວ້າງ Bounding Box\"\nBasic.TransformWindow.BoundsHeight=\"ຄວາມສູງ Bounding Box\"\nBasic.TransformWindow.CropToBounds=\"ຕັດຂອບ (Crop) ຕາມ Bounding Box\"\nBasic.TransformWindow.Crop=\"ຕັດຂອບ (Crop)\"\nBasic.TransformWindow.CropLeft=\"ຕັດຂອບ ຊ້າຍ\"\nBasic.TransformWindow.CropRight=\"ຕັດຂອບ ຂວາ\"\nBasic.TransformWindow.CropTop=\"ຕັດຂອບ ເທິງ\"\nBasic.TransformWindow.CropBottom=\"ຕັດຂອບ ລຸ່ມ\"\nBasic.TransformWindow.Alignment.TopLeft=\"ເທິງ ຊ້າຍ\"\nBasic.TransformWindow.Alignment.TopCenter=\"ເທິງ ເຄິ່ງກາງ\"\nBasic.TransformWindow.Alignment.TopRight=\"ເທິງ ຂວາ\"\nBasic.TransformWindow.Alignment.CenterLeft=\"ເຄິ່ງກາງ ຊ້າຍ\"\nBasic.TransformWindow.Alignment.Center=\"ເຄິ່ງກາງ\"\nBasic.TransformWindow.Alignment.CenterRight=\"ເຄິ່ງກາງ ຂວາ\"\nBasic.TransformWindow.Alignment.BottomLeft=\"ລຸ່ມ ຊ້າຍ\"\nBasic.TransformWindow.Alignment.BottomCenter=\"ລຸ່ມ ເຄິ່ງກາງ\"\nBasic.TransformWindow.Alignment.BottomRight=\"ລຸ່ມ ຂວາ\"\nBasic.TransformWindow.BoundsType.None=\"ບໍ່ມີຂອບເຂດ\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"ຂະໜາດສູງສຸດເທົ່ານັ້ນ\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Scale ຕາມຂອບເຂດດ້ານໃນ\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Scale ຕາມຂອບເຂດດ້ານນອກ\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Scale ຕາມຄວາມກວ້າງຂອງຂອບເຂດ\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Scale ຕາມຄວາມສູງຂອງຂອບເຂດ\"\nBasic.TransformWindow.BoundsType.Stretch=\"ຢືດ (Stretch) ຕາມຂອບເຂດ\"\nBasic.TransformWindow.Title=\"ແກ້ໄຂ Transform ສຳລັບ '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"ບໍ່ໄດ້ເລືອກແຫຼ່ງທີ່ມາ\"\nBasic.Main.AddSourceHelp.Title=\"ບໍ່ສາມາດເພີ່ມແຫຼ່ງທີ່ມາ\"\nBasic.Main.AddSourceHelp.Text=\"ທ່ານຈຳເປັນຕ້ອງມີຢ່າງໜ້ອຍ 1 ສາກ ເພື່ອເພີ່ມແຫຼ່ງທີ່ມາ.\"\nBasic.Main.Scenes=\"ສາກ (Scenes)\"\nBasic.Main.Sources=\"ແຫຼ່ງທີ່ມາ (Sources)\"\nBasic.Main.Source=\"ແຫຼ່ງທີ່ມາ\"\nBasic.Main.Controls=\"ການຄວບຄຸມ\"\nBasic.Main.PreparingStream=\"ກຳລັງກະກຽມ...\"\nBasic.Main.Connecting=\"ກຳລັງເຊື່ອມຕໍ່...\"\nBasic.Main.StartRecording=\"ເລີ່ມບັນທຶກ\"\nBasic.Main.StartReplayBuffer=\"ເລີ່ມ Replay Buffer\"\nBasic.Main.SaveReplay=\"ບັນທຶກ Replay\"\nBasic.Main.StartStreaming=\"ເລີ່ມສະຕຣີມ\"\nBasic.Main.StartBroadcast=\"ອອກອາກາດສົດ\"\nBasic.Main.StartVirtualCam=\"ເລີ່ມ ກ້ອງສະເໝືອນ (Virtual Camera)\"\nBasic.Main.StopRecording=\"ຢຸດບັນທຶກ\"\nBasic.Main.PauseRecording=\"ຢຸດບັນທຶກຊົ່ວຄາວ\"\nBasic.Main.UnpauseRecording=\"ກັບມາບັນທຶກຕໍ່\"\nBasic.Main.SplitFile=\"ແຍກໄຟລ໌ບັນທຶກ\"\nBasic.Main.AddChapterMarker=\"ເພີ່ມ Chapter Marker (Hybrid MP4/MOV ເທົ່ານັ້ນ)\"\nBasic.Main.StoppingRecording=\"ກຳລັງຢຸດບັນທຶກ...\"\nBasic.Main.StopReplayBuffer=\"ຢຸດ Replay Buffer\"\nBasic.Main.StoppingReplayBuffer=\"ກຳລັງຢຸດ Replay Buffer...\"\nBasic.Main.SetupBroadcast=\"ຈັດການການອອກອາກາດ\"\nBasic.Main.StopStreaming=\"ຢຸດສະຕຣີມ\"\nBasic.Main.StopBroadcast=\"ຈົບການອອກອາກາດ\"\nBasic.Main.AutoStopEnabled=\"(ຢຸດອັດຕະໂນມັດ)\"\nBasic.Main.StoppingStreaming=\"ກຳລັງຢຸດສະຕຣີມ...\"\nBasic.Main.ForceStopStreaming=\"ຢຸດສະຕຣີມ (ຖິ້ມ delay)\"\nBasic.Main.ShowContextBar=\"ສະແດງ Source Toolbar\"\nBasic.Main.HideContextBar=\"ເຊື່ອງ Source Toolbar\"\nBasic.Main.StopVirtualCam=\"ຢຸດ ກ້ອງສະເໝືອນ (Virtual Camera)\"\nBasic.Main.Group=\"ກຸ່ມ %1\"\nBasic.Main.GroupItems=\"ຈັດກຸ່ມລາຍການທີ່ເລືອກ\"\nBasic.Main.Ungroup=\"ຍົກເລີກການຈັດກຸ່ມ\"\nBasic.Main.GridMode=\"ໂໝດຕາຕະລາງ\"\nBasic.Main.ListMode=\"ໂໝດລາຍການ\"\nBasic.Main.VirtualCamConfig=\"ຕັ້ງຄ່າ ກ້ອງສະເໝືອນ (Virtual Camera)\"\nBasic.VCam.VirtualCamera=\"ກ້ອງສະເໝືອນ (Virtual Camera)\"\nBasic.VCam.OutputType=\"ປະເພດ Output\"\nBasic.VCam.OutputSelection=\"ການເລືອກ Output\"\nBasic.VCam.OutputType.Program=\"ໂປຣແກຣມ (ຄ່າເລີ່ມຕົ້ນ)\"\nBasic.VCam.OutputSelection.NoSelection=\"ບໍ່ມີການເລືອກສຳລັບປະເພດ output ນີ້\"\nBasic.VCam.RestartWarning=\"ກ້ອງສະເໝືອນຈະຖືກຣີສະຕາດເພື່ອນຳໃຊ້ການປ່ຽນແປງນີ້\"\nBasic.MainMenu.File=\"&ໄຟລ໌\"\nBasic.MainMenu.File.Export=\"&ສົ່ງອອກ\"\nBasic.MainMenu.File.Import=\"&ນຳເຂົ້າ\"\nBasic.MainMenu.File.ShowRecordings=\"ສະແດງ &ການບັນທຶກ\"\nBasic.MainMenu.File.Remux=\"ຣີ&ມັກສ໌ (Remux) ການບັນທຶກ\"\nBasic.MainMenu.File.Settings=\"&ການຕັ້ງຄ່າ\"\nBasic.MainMenu.File.ShowSettingsFolder=\"ສະແດງໂຟນເດີການຕັ້ງຄ່າ\"\nBasic.MainMenu.File.ShowProfileFolder=\"ສະແດງໂຟນເດີໂປຣໄຟລ໌\"\nBasic.MainMenu.File.ShowMissingFiles=\"ກວດສອບໄຟລ໌ທີ່ຫາຍໄປ\"\nBasic.MainMenu.File.Exit=\"&ອອກ\"\nBasic.MainMenu.Edit=\"&ແກ້ໄຂ\"\nBasic.MainMenu.Edit.LockPreview=\"&ລັອກຕົວຢ່າງ (Preview)\"\nBasic.MainMenu.Edit.Scale=\"&Scaling ຕົວຢ່າງ\"\nBasic.MainMenu.Edit.Scale.Window=\"Scale ຕາມໜ້າຕ່າງ\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"ຊູມເຂົ້າ\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"ຊູມອອກ\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"ຣີເຊັດການຊູມ\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&ແກ້ໄຂ Transform\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"ສຳເນົາ Transform\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"ວາງ Transform\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&ຣີເຊັດ Transform\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"ໝຸນ 90 ອົງສາ ຕາມເຂັມໂມງ\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"ໝຸນ 90 ອົງສາ ທວນເຂັມໂມງ\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"ໝຸນ 180 ອົງສາ\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"ພິກ &ແນວນອນ\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"ພິກ &ແນວຕັ້ງ\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"&ປັບໃຫ້ພໍດີໜ້າຈໍ\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&ຢືດໃຫ້ເຕັມໜ້າຈໍ\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&ຈັດເຄິ່ງກາງໜ້າຈໍ\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"ຈັດເຄິ່ງກາງແນວຕັ້ງ\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"ຈັດເຄິ່ງກາງແນວນອນ\"\nBasic.MainMenu.Edit.Order=\"&ລຳດັບ\"\nBasic.MainMenu.Edit.Order.MoveUp=\"ຍັບ &ຂຶ້ນ\"\nBasic.MainMenu.Edit.Order.MoveDown=\"ຍັບ &ລົງ\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"ຍັບໄປ &ເທິງສຸດ\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"ຍັບໄປ &ລຸ່ມສຸດ\"\nBasic.MainMenu.Edit.AdvAudio=\"&ຄຸນສົມບັດສຽງຂັ້ນສູງ\"\nBasic.MainMenu.View=\"&ມຸມມອງ\"\nBasic.MainMenu.View.Toolbars=\"&ແຖບເຄື່ອງມື\"\nBasic.MainMenu.View.ListboxToolbars=\"ແຖບເຄື່ອງມື Dock\"\nBasic.MainMenu.View.ContextBar=\"ແຖບເຄື່ອງມື Source\"\nBasic.MainMenu.View.SourceIcons=\"&ໄອຄອນ Source\"\nBasic.MainMenu.View.StatusBar=\"&ແຖບສະຖານະ\"\nBasic.MainMenu.View.Fullscreen.Interface=\"ອິນເຕີເຟດແບບເຕັມຈໍ\"\nBasic.MainMenu.View.ResetUI=\"&ຣີເຊັດ UI\"\nBasic.MainMenu.View.AlwaysOnTop=\"&ຢູ່ເທິງສຸດສະເໝີ\"\nBasic.MainMenu.View.SceneListMode=\"ໂໝດລາຍການສາກ\"\nBasic.MainMenu.Docks.ResetDocks=\"&ຣີເຊັດ Docks\"\nBasic.MainMenu.Docks.LockDocks=\"&ລັອກ Docks\"\nBasic.MainMenu.Docks.SideDocks=\"&Docks ຄວາມສູງເຕັມ\"\nBasic.MainMenu.Import=\"ນຳເຂົ້າ...\"\nBasic.MainMenu.Export=\"ສົ່ງອອກ...\"\nBasic.MainMenu.Profile=\"&ໂປຣໄຟລ໌\"\nBasic.MainMenu.Profile.Import=\"ນຳເຂົ້າໂປຣໄຟລ໌\"\nBasic.MainMenu.Profile.Export=\"ສົ່ງອອກໂປຣໄຟລ໌\"\nBasic.MainMenu.SceneCollection.Import=\"ນຳເຂົ້າ Scene Collection\"\nBasic.MainMenu.SceneCollection.Export=\"ສົ່ງອອກ Scene Collection\"\nBasic.MainMenu.SceneCollection.Remigrate=\"ຣີເຊັດ Base Resolution\"\nBasic.MainMenu.SceneCollection.Migrate=\"ຕັ້ງຄ່າ Base Resolution\"\nBasic.MainMenu.Profile.Exists=\"ໂປຣໄຟລ໌ນີ້ມີຢູ່ແລ້ວ\"\nBasic.MainMenu.Tools=\"&ເຄື່ອງມື\"\nBasic.MainMenu.Help=\"&ວິທີໃຊ້\"\nBasic.MainMenu.Help.HelpPortal=\"&ສູນຊ່ວຍເຫຼືອ\"\nBasic.MainMenu.Help.Website=\"ເຂົ້າເບິ່ງ &ເວັບໄຊ\"\nBasic.MainMenu.Help.Discord=\"ເຂົ້າຮ່ວມເຊີບເວີ &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"ມີຫຍັງໃໝ່\"\nBasic.MainMenu.Help.Logs=\"&ໄຟລ໌ Log\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"&ສະແດງໄຟລ໌ Log\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"ອັບໂຫຼດໄຟລ໌ Log &ປັດຈຸບັນ\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"ອັບໂຫຼດໄຟລ໌ Log &ກ່ອນໜ້າ\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&ເບິ່ງ Log ປັດຈຸບັນ\"\nBasic.MainMenu.Help.ReleaseNotes=\"ບັນທຶກເວີຊັນ (Release Notes)\"\nBasic.MainMenu.Help.CheckForUpdates=\"ກວດຫາການອັບເດດ\"\nBasic.MainMenu.Help.Repair=\"ກວດສອບຄວາມສົມບູນຂອງໄຟລ໌\"\nBasic.MainMenu.Help.RestartSafeMode=\"ຣີສະຕາດໃນ ໂໝດປອດໄພ (Safe Mode)\"\nBasic.MainMenu.Help.RestartNormal=\"ຣີສະຕາດໃນ ໂໝດປົກກະຕິ\"\nBasic.MainMenu.Help.CrashLogs=\"&ລາຍງານການ Crash\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"&ສະແດງລາຍງານການ Crash\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"ອັບໂຫຼດລາຍງານການ Crash &ກ່ອນໜ້າ\"\nBasic.MainMenu.Help.About=\"&ກ່ຽວກັບ\"\nBasic.Settings.ProgramRestart=\"ໂປຣແກຣມຕ້ອງຖືກຣີສະຕາດ ເພື່ອໃຫ້ການຕັ້ງຄ່າເຫຼົ່ານີ້ມີຜົນ.\"\nBasic.Settings.ConfirmTitle=\"ຢືນຢັນການປ່ຽນແປງ\"\nBasic.Settings.Confirm=\"ທ່ານມີການປ່ຽນແປງທີ່ຍັງບໍ່ໄດ້ບັນທຶກ. ບັນທຶກການປ່ຽນແປງບໍ່?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 ກຳລັງຄວບຄຸມການຕັ້ງຄ່າການສະຕຣີມບາງຢ່າງຂອງທ່ານ\"\nBasic.Settings.General=\"ທົ່ວໄປ\"\nBasic.Settings.General.Language=\"ພາສາ\"\nBasic.Settings.General.Updater=\"ການອັບເດດ\"\nBasic.Settings.General.UpdateChannel=\"ຊ່ອງທາງການອັບເດດ\"\nBasic.Settings.General.UpdateChannelDisabled=\"(ປິດໃຊ້ງານ)\"\nBasic.Settings.General.UpdateChannelDefault=\"(ຄ່າເລີ່ມຕົ້ນ)\"\nBasic.Settings.General.EnableAutoUpdates=\"ກວດຫາການອັບເດດອັດຕະໂນມັດເມື່ອເປີດໂປຣແກຣມ\"\nBasic.Settings.General.OpenStatsOnStartup=\"ເປີດໜ້າຕ່າງສະຖິຕິເມື່ອເປີດໂປຣແກຣມ\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"ເຊື່ອງໜ້າຕ່າງ OBS ຈາກການບັນທຶກໜ້າຈໍ (Screen capture)\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"ການຕັ້ງຄ່ານີ້ຈະເຊື່ອງໜ້າຕ່າງ OBS Studio ທັງໝົດ (ຍົກເວັ້ນໂປຣເຈັກເຕີ) ຈາກການຖືກບັນທຶກໂດຍ OBS ແລະ ມີຜົນກັບ \\nແອັບພລິເຊຊັນອື່ນໆ ເຊັ່ນ: ການປະຊຸມ, ການແບ່ງປັນໜ້າຈໍ, ການ Support ທາງໄກ, ການຖ່າຍພາບໜ້າຈໍ ແລະ ຊອບແວການບັນທຶກອື່ນໆ.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"ການເປີດໃຊ້ຕົວເລືອກນີ້ ຈະເຊື່ອງໜ້າຕ່າງ OBS Studio ທັງໝົດ (ຍົກເວັ້ນໂປຣເຈັກເຕີ) ຈາກການຖືກບັນທຶກໂດຍ OBS ແລະ ມີຜົນກັບແອັບພລິເຄຊັນອື່ນໆ ເຊັ່ນ: ການປະຊຸມ, ການແບ່ງປັນໜ້າຈໍ, ການ Support ທາງໄກ, ການຖ່າຍພາບໜ້າຈໍ ແລະ ຊອບແວການບັນທຶກອື່ນໆ.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"ສະແດງກ່ອງຢືນຢັນເມື່ອເລີ່ມສະຕຣີມ\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"ສະແດງກ່ອງຢືນຢັນເມື່ອຢຸດສະຕຣີມ\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"ສະແດງກ່ອງຢືນຢັນເມື່ອຢຸດບັນທຶກ\"\nBasic.Settings.General.Projectors=\"ໂປຣເຈັກເຕີ\"\nBasic.Settings.General.HideProjectorCursor=\"ເຊື່ອງເຄີເຊີເທິງໂປຣເຈັກເຕີ\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"ໃຫ້ໂປຣເຈັກເຕີຢູ່ເທິງສຸດສະເໝີ\"\nBasic.Settings.General.ScreenSnapping=\"Snap Sources ໃສ່ຂອບຂອງໜ້າຈໍ\"\nBasic.Settings.General.CenterSnapping=\"Snap Sources ໃສ່ເຄິ່ງກາງແນວນອນ ແລະ ແນວຕັ້ງ\"\nBasic.Settings.General.SourceSnapping=\"Snap Sources ໃສ່ Source ອື່ນໆ\"\nBasic.Settings.General.SnapDistance=\"ຄວາມໄວໃນການ Snap\"\nBasic.Settings.General.SpacingHelpers=\"ສະແດງເສັ້ນບອກແນວການຈັດວາງ (Pixel alignment guides)\"\nBasic.Settings.General.RecordWhenStreaming=\"ບັນທຶກອັດຕະໂນມັດເມື່ອສະຕຣີມ\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"ບັນທຶກຕໍ່ໄປເມື່ອຢຸດສະຕຣີມ\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"ເລີ່ມ Replay buffer ອັດຕະໂນມັດເມື່ອສະຕຣີມ\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"ໃຫ້ Replay buffer ເຮັດວຽກຕໍ່ໄປເມື່ອຢຸດສະຕຣີມ\"\nBasic.Settings.General.SysTrayWhenStarted=\"ຫຍໍ້ລົງ System tray ເມື່ອເປີດໂປຣແກຣມ\"\nBasic.Settings.General.SystemTrayHideMinimize=\"ຫຍໍ້ລົງ System tray ສະເໝີແທນ Task bar\"\nBasic.Settings.General.SaveProjectors=\"ບັນທຶກໂປຣເຈັກເຕີເມື່ອອອກ\"\nBasic.Settings.General.CloseExistingProjectors=\"ຈຳກັດໂປຣເຈັກເຕີເຕັມຈໍ ໜຶ່ງອັນຕໍ່ໜຶ່ງໜ້າຈໍ\"\nBasic.Settings.General.Preview=\"ຕົວຢ່າງ (Preview)\"\nBasic.Settings.General.OverflowHidden=\"ເຊື່ອງສ່ວນເກີນ (Overflow)\"\nBasic.Settings.General.OverflowAlwaysVisible=\"ສະແດງສ່ວນເກີນສະເໝີ\"\nBasic.Settings.General.OverflowSelectionHidden=\"ສະແດງສ່ວນເກີນແມ້ວ່າ source ຈະເບິ່ງບໍ່ເຫັນກໍຕາມ\"\nBasic.Settings.General.Importers=\"ຕົວນຳເຂົ້າ (Importers)\"\nBasic.Settings.General.AutomaticCollectionSearch=\"ຄົ້ນຫາ scene collections ໃນຕຳແໜ່ງທີ່ຮູ້ຈັກ ເມື່ອນຳເຂົ້າ\"\nBasic.Settings.General.SwitchOnDoubleClick=\"ປ່ຽນສາກເມື່ອດັບເບິນຄລິກ\"\nBasic.Settings.General.StudioPortraitLayout=\"ເປີດໃຊ້ການຈັດວາງແນວຕັ້ງ/Portrait\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"ສະແດງປ້າຍຊື່ ຕົວຢ່າງ/ໂປຣແກຣມ\"\nBasic.Settings.General.Multiview=\"ມຸມມອງລວມ (Multiview)\"\nBasic.Settings.General.Multiview.MouseSwitch=\"ຄລິກເພື່ອສະລັບລະຫວ່າງສາກ\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"ສະແດງຊື່ສາກ\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"ແຕ້ມພື້ນທີ່ປອດໄພ (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"ການຈັດວາງ ມຸມມອງລວມ\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"ແນວນອນ, ເທິງ (8 ສາກ)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"ແນວນອນ, ລຸ່ມ (8 ສາກ)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"ແນວຕັ້ງ, ຊ້າຍ (8 ສາກ)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"ແນວຕັ້ງ, ຂວາ (8 ສາກ)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"ແນວນອນ, ເທິງ (18 ສາກ)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"ແນວນອນ, ເທິງ (24 ສາກ)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"ສາກເທົ່ານັ້ນ (4 ສາກ)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"ສາກເທົ່ານັ້ນ (9 ສາກ)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"ສາກເທົ່ານັ້ນ (16 ສາກ)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"ສາກເທົ່ານັ້ນ (25 ສາກ)\"\nBasic.Settings.General.ChannelDescription.stable=\"ເວີຊັນ Stable ຫຼ້າສຸດ\"\nBasic.Settings.General.ChannelDescription.beta=\"ເວີຊັນກ່ອນປ່ອຍຈິງທີ່ອາດບໍ່ສະຖຽນ\"\nBasic.Settings.Appearance=\"ຮູບລັກສະນະ\"\nBasic.Settings.Appearance.General=\"ທົ່ວໄປ\"\nBasic.Settings.Appearance.General.Variant=\"ຮູບແບບ\"\nBasic.Settings.Appearance.General.NoVariant=\"ບໍ່ມີຮູບແບບໃຫ້ເລືອກ\"\nBasic.Settings.Appearance.FontScale=\"ຂະໜາດຟອນ\"\nBasic.Settings.Appearance.Density=\"ຄວາມໜາແໜ້ນ\"\nBasic.Settings.Appearance.OptionsWarning=\"ບາງຕົວເລືອກຮູບລັກສະນະບໍ່ສາມາດໃຊ້ໄດ້ກັບຮູບແບບນີ້.\"\nBasic.Settings.Stream=\"ສະຕຣີມ\"\nBasic.Settings.Stream.Destination=\"ປາຍທາງ\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"ໃຊ້ການຢືນຢັນຕົວຕົນ\"\nBasic.Settings.Stream.Custom.Username=\"ຊື່ຜູ້ໃຊ້\"\nBasic.Settings.Stream.Custom.Password=\"ລະຫັດຜ່ານ\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: ໃສ່ srp_username.\\nRTMP: ໃສ່ຊື່ຜູ້ໃຊ້.\\nSRT: ບໍ່ໄດ້ໃຊ້.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: ໃສ່ srp_password.\\nRTMP: ໃສ່ລະຫັດຜ່ານ.\\nSRT: ໃສ່ encryption passphrase.\"\nBasic.Settings.Stream.BandwidthTestMode=\"ເປີດໃຊ້ໂໝດທົດສອບແບນວິດ\"\nBasic.Settings.Stream.TTVAddon.None=\"ບໍ່ມີ\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV ແລະ FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"ການຕັ້ງຄ່າສະຕຣີມບໍ່ຄົບຖ້ວນ\"\nBasic.Settings.Stream.StreamSettingsWarning=\"ເປີດການຕັ້ງຄ່າ\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL ແລະ Stream Key ຂາດຫາຍໄປ.\\n\\nເປີດການຕັ້ງຄ່າເພື່ອໃສ່ URL ແລະ Stream Key ໃນແຖບ 'ສະຕຣີມ'.\"\nBasic.Settings.Stream.MissingUrl=\"Stream URL ຂາດຫາຍໄປ.\\n\\nເປີດການຕັ້ງຄ່າເພື່ອໃສ່ URL ໃນແຖບ 'ສະຕຣີມ'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Stream key ຂາດຫາຍໄປ.\\n\\nເປີດການຕັ້ງຄ່າເພື່ອໃສ່ stream key ໃນແຖບ 'ສະຕຣີມ'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"ບໍ່ສົນໃຈຄຳແນະນຳການຕັ້ງຄ່າບໍລິການສະຕຣີມ\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"ລົບລ້າງການຕັ້ງຄ່າທີ່ແນະນຳ\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"ຄຳເຕືອນ: ການບໍ່ສົນໃຈຂໍ້ຈຳກັດຂອງບໍລິການ ອາດສົ່ງຜົນໃຫ້ຄຸນນະພາບການສະຕຣີມຫຼຸດລົງ ຫຼື ປ້ອງກັນບໍ່ໃຫ້ທ່ານສະຕຣີມໄດ້.\\n\\nດຳເນີນການຕໍ່?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Video Bitrate ສູງສຸດ: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Audio Bitrate ສູງສຸດ: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"ຄວາມລະອຽດສູງສຸດ: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"FPS ສູງສຸດ: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"ລະບຸເຊີບເວີກຳນົດເອງ...\"\nBasic.Settings.Stream.ServiceCustomServer=\"ເຊີບເວີກຳນົດເອງ\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"ເປີດໃຊ້ %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Streaming Bandwidth ສູງສຸດ\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"ອັດຕະໂນມັດ\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Video Tracks ສູງສຸດ\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"ອັດຕະໂນມັດ\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"ເປີດໃຊ້ stream dump ເປັນ FLV (ໃຊ້ການຕັ້ງຄ່າໄຟລ໌ບັນທຶກແບບ Simple)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"ເປີດໃຊ້ Config Override\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Canvas ເເພີ່ມເຕີມ\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Simulcast ຊ່ວຍໃຫ້ທ່ານສາມາດເຂົ້າລະຫັດ ແລະ ສົ່ງວິດີໂອໄດ້ຫຼາຍລະດັບຄຸນນະພາບ. <a href='https://obsproject.com/kb/whip-streaming-guide'>ຮຽນຮູ້ເພີ່ມເຕີມ</a>\"\nBasic.Settings.Stream.AdvancedOptions=\"ຕົວເລືອກຂັ້ນສູງ\"\nBasic.Settings.Output=\"ສົ່ງອອກ (Output)\"\nBasic.Settings.Output.Format=\"ຮູບແບບການບັນທຶກ\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmented MOV ຂຽນການບັນທຶກເປັນກ້ອນ ແລະ ບໍ່ຈຳເປັນຕ້ອງມີການ Finalize ຄືກັບໄຟລ໌ MOV ທົ່ວໄປ.\\nສິ່ງນີ້ຮັບປະກັນວ່າໄຟລ໌ຈະຍັງສາມາດຫຼິ້ນໄດ້ ແມ້ວ່າການຂຽນລົງດິສກ໌ຈະຖືກຂັດຈັງຫວະ, ຕົວຢ່າງ: ຜົນຈາກ BSOD ຫຼື ໄຟມອດ.\\n\\nອາດຈະບໍ່ເຂົ້າກັນໄດ້ກັບເຄື່ອງຫຼິ້ນ ແລະ ໂປຣແກຣມຕັດຕໍ່ທັງໝົດ. ໃຊ້ ໄຟລ໌ → Remux ການບັນທຶກ ເພື່ອແປງໄຟລ໌ເປັນຮູບແບບທີ່ເຂົ້າກັນໄດ້ຫຼາຍຂຶ້ນ ຫາກຈຳເປັນ.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmented MP4 ຂຽນການບັນທຶກເປັນກ້ອນ ແລະ ບໍ່ຈຳເປັນຕ້ອງມີການ Finalize ຄືກັບໄຟລ໌ MP4 ທົ່ວໄປ.\\nສິ່ງນີ້ຮັບປະກັນວ່າໄຟລ໌ຈະຍັງສາມາດຫຼິ້ນໄດ້ ແມ້ວ່າການຂຽນລົງດິສກ໌ຈະຖືກຂັດຈັງຫວະ, ຕົວຢ່າງ: ຜົນຈາກ BSOD ຫຼື ໄຟມອດ.\\n\\nອາດຈະບໍ່ເຂົ້າກັນໄດ້ກັບເຄື່ອງຫຼິ້ນ ແລະ ໂປຣແກຣມຕັດຕໍ່ທັງໝົດ. ໃຊ້ ໄຟລ໌ → Remux ການບັນທຶກ ເພື່ອແປງໄຟລ໌ເປັນຮູບແບບທີ່ເຂົ້າກັນໄດ້ຫຼາຍຂຶ້ນ ຫາກຈຳເປັນ.\"\nBasic.Settings.Output.Encoder.Video=\"ຕົວເຂົ້າລະຫັດວິດີໂອ\"\nBasic.Settings.Output.Encoder.Audio=\"ຕົວເຂົ້າລະຫັດສຽງ\"\nBasic.Settings.Output.SelectDirectory=\"ເລືອກ ໄດເຣັກທໍຣີ ການບັນທຶກ\"\nBasic.Settings.Output.DynamicBitrate=\"ປ່ຽນບິດເຣດແບບໄດນາມິກ ເພື່ອຈັດການຄວາມແອອັດຂອງເຄືອຂ່າຍ\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"ປ່ຽນບິດເຣດແບບໄດນາມິກ ເພື່ອຈັດການຄວາມແອອັດຂອງເຄືອຂ່າຍ (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"ແທນທີ່ຈະຖິ້ມເຟຣມເພື່ອຫຼຸດຄວາມແອອັດ, ຈະປ່ຽນບິດເຣດທັນທີໃນຂະນະນັ້ນເລີຍ.\\n\\nໝາຍເຫດ: ສິ່ງນີ້ອາດເພີ່ມ Delay ໃຫ້ກັບຜູ້ຊົມ ຫາກມີຄວາມແອອັດຢ່າງກະທັນຫັນ.\\nເມື່ອບິດເຣດຫຼຸດລົງ, ອາດໃຊ້ເວລາສອງສາມນາທີໃນການກູ້ຄືນ.\\n\\nປັດຈຸບັນຮອງຮັບສະເພາະ RTMP.\"\nBasic.Settings.Output.Mode=\"ໂໝດ Output\"\nBasic.Settings.Output.Mode.Simple=\"ແບບງ່າຍ (Simple)\"\nBasic.Settings.Output.Mode.Adv=\"ຂັ້ນສູງ (Advanced)\"\nBasic.Settings.Output.UseReplayBuffer=\"ເປີດໃຊ້ Replay Buffer\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"ເວລາ Replay ສູງສຸດ\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Memory ສູງສຸດ\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"ການໃຊ້ Memory ໂດຍປະມານ: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"ຄຳເຕືອນ: ການໃຊ້ Memory ໂດຍປະມານ %1 MiB ໃຫຍ່ກວ່າຄ່າສູງສຸດທີ່ແນະນຳ %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"ບໍ່ສາມາດປະເມີນການໃຊ້ Memory ໄດ້. ກະລຸນາຕັ້ງຄ່າຈຳກັດ Memory ສູງສຸດ.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"ຄຳນຳໜ້າຊື່ໄຟລ໌ Replay Buffer\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"ຄຳຕໍ່ທ້າຍ\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Replay Buffer ບໍ່ສາມາດໃຊ້ໄດ້ເມື່ອປະເພດການບັນທຶກຖືກຕັ້ງເປັນ Custom Output (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Path ການບັນທຶກ\"\nBasic.Settings.Output.Simple.RecordingQuality=\"ຄຸນນະພາບການບັນທຶກ\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"ຄືກັບສະຕຣີມ\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"ຄຸນນະພາບສູງ, ຂະໜາດໄຟລ໌ປານກາງ\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"ຄຸນນະພາບແຍກບໍ່ອອກ, ຂະໜາດໄຟລ໌ໃຫຍ່\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"ຄຸນນະພາບ Lossless, ຂະໜາດໄຟລ໌ໃຫຍ່ຫຼາຍ\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"ຄຳເຕືອນ: Video bitrate ການສະຕຣີມຈະຖືກຕັ້ງເປັນ %1, ເຊິ່ງເປັນຂີດຈຳກັດສູງສຸດສຳລັບບໍລິການສະຕຣີມປັດຈຸບັນ.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"ຄຳເຕືອນ: Audio bitrate ການສະຕຣີມຈະຖືກຕັ້ງເປັນ %1, ເຊິ່ງເປັນຂີດຈຳກັດສູງສຸດສຳລັບບໍລິການສະຕຣີມປັດຈຸບັນ.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"ຄຳເຕືອນ: ການບັນທຶກບໍ່ສາມາດຢຸດຊົ່ວຄາວໄດ້ ຫາກຄຸນນະພາບການບັນທຶກຖືກຕັ້ງເປັນ \\\"ຄືກັບສະຕຣີມ\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"ຄຳເຕືອນ: ຮູບແບບການບັນທຶກທີ່ເລືອກຢູ່ ບໍ່ເຂົ້າກັນກັບຕົວເຂົ້າລະຫັດສະຕຣີມທີ່ເລືອກ.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"ຄຳເຕືອນ: ການບັນທຶກດ້ວຍຕົວເຂົ້າລະຫັດຊອບແວ (Software encoder) ໃນຄຸນນະພາບທີ່ຕ່າງຈາກສະຕຣີມ ຈະຕ້ອງການການໃຊ້ງານ CPU ເພີ່ມເຕີມ ຫາກທ່ານສະຕຣີມ ແລະ ບັນທຶກໃນເວລາດຽວກັນ.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"ຄຳເຕືອນ: ຄຸນນະພາບ Lossless ສ້າງຂະໜາດໄຟລ໌ທີ່ໃຫຍ່ຫຼາຍ! ຄຸນນະພາບ Lossless ສາມາດໃຊ້ພື້ນທີ່ດິສກ໌ສູງເຖິງ 7 ກິກະໄບຕໍ່ນາທີ ໃນຄວາມລະອຽດ ແລະ ເຟຣມເຣດສູງ. ບໍ່ແນະນຳ Lossless ສຳລັບການບັນທຶກຍາວໆ ເວັ້ນແຕ່ທ່ານຈະມີພື້ນທີ່ດິສກ໌ຈຳນວນຫຼາຍ. Replay buffer ບໍ່ສາມາດໃຊ້ໄດ້ເມື່ອໃຊ້ຄຸນນະພາບ Lossless.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການໃຊ້ຄຸນນະພາບ Lossless?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"ຄຳເຕືອນຄຸນນະພາບ Lossless!\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264 low CPU usage preset, ເພີ່ມຂະໜາດໄຟລ໌)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (ຄ່າເລີ່ມຕົ້ນ)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD Track (ໃຊ້ Track 2)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"ຄວາມລະອຽດ/ເຟຣມເຣດ ບໍ່ເຂົ້າກັນ\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"ບໍລິການສະຕຣີມນີ້ ບໍ່ຮອງຮັບຄວາມລະອຽດ output ແລະ/ຫຼື ເຟຣມເຣດປັດຈຸບັນຂອງທ່ານ. ພວກມັນຈະຖືກປ່ຽນເປັນຄ່າທີ່ເຂົ້າກັນໄດ້ທີ່ໃກ້ຄາມທີ່ສຸດ:\\n\\n%1\\n\\nທ່ານຕ້ອງການດຳເນີນການຕໍ່ບໍ່?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"ຄວາມລະອຽດ: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"ຕົວເຂົ້າລະຫັດບໍ່ເຂົ້າກັນ\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"ບໍລິການສະຕຣີມ \\\"%1\\\" ບໍ່ຮອງຮັບຕົວເຂົ້າລະຫັດ \\\"%2\\\". ຕົວເຂົ້າລະຫັດຈະຖືກປ່ຽນເປັນ \\\"%3\\\".\\n\\nທ່ານຕ້ອງການດຳເນີນການຕໍ່ບໍ່?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"ບໍລິການສະຕຣີມ \\\"%1\\\" ບໍ່ຮອງຮັບຕົວເຂົ້າລະຫັດ \\\"%2\\\" ແລະ \\\"%3\\\". ຕົວເຂົ້າລະຫັດເຫຼົ່ານີ້ຈະຖືກປ່ຽນເປັນ \\\"%4\\\" ແລະ \\\"%5\\\".\\n\\nທ່ານຕ້ອງການດຳເນີນການຕໍ່ບໍ່?\"\nBasic.Settings.Output.Reconnect=\"ເຊື່ອມຕໍ່ໃໝ່ອັດຕະໂນມັດ\"\nBasic.Settings.Output.RetryDelay=\"ດີເລການລອງໃໝ່\"\nBasic.Settings.Output.MaxRetries=\"ຈຳນວນລອງໃໝ່ສູງສຸດ\"\nBasic.Settings.Output.Advanced=\"ເປີດໃຊ້ການຕັ້ງຄ່າຕົວເຂົ້າລະຫັດແບບກຳນົດເອງ (ຂັ້ນສູງ)\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (low CPU usage, ຄຸນນະພາບຕໍ່າສຸດ)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (default) (medium CPU usage, ຄຸນນະພາບມາດຕະຖານ)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (high CPU usage, ຄຸນນະພາບສູງ)\"\nBasic.Settings.Output.CustomEncoderSettings=\"ການຕັ້ງຄ່າຕົວເຂົ້າລະຫັດແບບກຳນົດເອງ\"\nBasic.Settings.Output.CustomMuxerSettings=\"ການຕັ້ງຄ່າ Muxer ແບບກຳນົດເອງ\"\nBasic.Settings.Output.NoSpaceFileName=\"ສ້າງຊື່ໄຟລ໌ໂດຍບໍ່ມີຍະຫວ່າງ\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"ປິດໃຊ້ງານ\"\nBasic.Settings.Output.Adv.Streaming=\"ສະຕຣີມ\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"ການຕັ້ງຄ່າສະຕຣີມ\"\nBasic.Settings.Output.Adv.Encoder=\"ການຕັ້ງຄ່າຕົວເຂົ້າລະຫັດ\"\nBasic.Settings.Output.Adv.Recording=\"ການບັນທຶກ\"\nBasic.Settings.Output.Adv.Recording.Settings=\"ການຕັ້ງຄ່າການບັນທຶກ\"\nBasic.Settings.Output.Adv.Recording.RecType=\"ປະເພດການບັນທຶກ\"\nBasic.Settings.Output.Adv.Recording.Type=\"ປະເພດ\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"ມາດຕະຖານ (Standard)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(ໃຊ້ຕົວເຂົ້າລະຫັດສະຕຣີມ)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"ຮູບແບບຊື່ໄຟລ໌\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"ຂຽນທັບຖ້າມີໄຟລ໌ຢູ່ແລ້ວ\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Custom Output (FFmpeg) ບໍ່ມີລະບົບປ້ອງກັນ.\\nພິຈາລະນາໃຊ້ປະເພດການບັນທຶກ \\\"Standard\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"ປະເພດ FFmpeg Output\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Output ໄປທີ່ URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Output ໄປທີ່ ໄຟລ໌\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"ຮູບແບບການບັນທຶກທົ່ວໄປ\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"ໄຟລ໌ທັງໝົດ\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"File path ຫຼື URL\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"ສຽງ\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"ວິດີໂອ\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"ຮູບແບບເລີ່ມຕົ້ນ\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"ຄຳອະທິບາຍ Container Format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Audio/Video Codec ຄາດເດົາຈາກ File path ຫຼື URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"ຕົວເຂົ້າລະຫັດເລີ່ມຕົ້ນ\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"ປິດໃຊ້ງານຕົວເຂົ້າລະຫັດ\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"ຕົວເຂົ້າລະຫັດວິດີໂອ\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"ການຕັ້ງຄ່າຕົວເຂົ້າລະຫັດວິດີໂອ (ຖ້າມີ)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"ຕົວເຂົ້າລະຫັດສຽງ\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"ການຕັ້ງຄ່າຕົວເຂົ້າລະຫັດສຽງ (ຖ້າມີ)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"ການຕັ້ງຄ່າ Muxer (ຖ້າມີ)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Keyframe interval (ເຟຣມ)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"ສະແດງ codecs ທັງໝົດ (ແມ້ວ່າອາດຈະບໍ່ເຂົ້າກັນ)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"ການຕັ້ງຄ່າ FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"ແບ່ງໄຟລ໌ອັດຕະໂນມັດ\"\nBasic.Settings.Output.SplitFile.TypeTime=\"ແບ່ງຕາມເວລາ\"\nBasic.Settings.Output.SplitFile.TypeSize=\"ແບ່ງຕາມຂະໜາດ\"\nBasic.Settings.Output.SplitFile.TypeManual=\"ແບ່ງດ້ວຍຕົນເອງເທົ່ານັ້ນ\"\nBasic.Settings.Output.SplitFile.Time=\"ເວລາແບ່ງ\"\nBasic.Settings.Output.SplitFile.Size=\"ຂະໜາດແບ່ງ\"\nScreenshot=\"Output ພາບໜ້າຈໍ\"\nScreenshot.SourceHotkey=\"ຖ່າຍພາບໜ້າຈໍ ແຫຼ່ງທີ່ມາທີ່ເລືອກ\"\nScreenshot.StudioProgram=\"ບັນທຶກພາບໜ້າຈໍ Program\"\nScreenshot.Preview=\"ບັນທຶກພາບໜ້າຈໍ Preview\"\nScreenshot.Scene=\"ບັນທຶກພາບໜ້າຈໍ Scene\"\nScreenshot.Source=\"ບັນທຶກພາບໜ້າຈໍ Source\"\nFilenameFormatting.TT.CCYY=\"ປີ, ຕົວເລກສີ່ຫຼັກ\"\nFilenameFormatting.TT.YY=\"ປີ, ສອງຫຼັກສຸດທ້າຍ (00-99)\"\nFilenameFormatting.TT.MM=\"ເດືອນ ເປັນຕົວເລກ (01-12)\"\nFilenameFormatting.TT.DD=\"ວັນທີຂອງເດືອນ, ມີສູນນຳໜ້າ (01-31)\"\nFilenameFormatting.TT.hh=\"ຊົ່ວໂມງ ໃນຮູບແບບ 24h (00-23)\"\nFilenameFormatting.TT.mm=\"ນາທີ (00-59)\"\nFilenameFormatting.TT.ss=\"ວິນາທີ (00-59)\"\nFilenameFormatting.TT.Percent=\"ເຄື່ອງໝາຍ %\"\nFilenameFormatting.TT.a=\"ຊື່ວັນຫຍໍ້\"\nFilenameFormatting.TT.A=\"ຊື່ວັນເຕັມ\"\nFilenameFormatting.TT.b=\"ຊື່ເດືອນຫຍໍ້\"\nFilenameFormatting.TT.B=\"ຊື່ເດືອນເຕັມ\"\nFilenameFormatting.TT.d=\"ວັນທີຂອງເດືອນ, ມີສູນນຳໜ້າ (01-31)\"\nFilenameFormatting.TT.H=\"ຊົ່ວໂມງ ໃນຮູບແບບ 24h (00-23)\"\nFilenameFormatting.TT.I=\"ຊົ່ວໂມງ ໃນຮູບແບບ 12h (01-12)\"\nFilenameFormatting.TT.m=\"ເດືອນ ເປັນຕົວເລກ (01-12)\"\nFilenameFormatting.TT.M=\"ນາທີ (00-59)\"\nFilenameFormatting.TT.p=\"AM ຫຼື PM\"\nFilenameFormatting.TT.s=\"ເວລາເປັນວິນາທີ ຕັ້ງແຕ່ UNIX epoch\"\nFilenameFormatting.TT.S=\"ວິນາທີ (00-59)\"\nFilenameFormatting.TT.y=\"ປີ, ສອງຫຼັກສຸດທ້າຍ (00-99)\"\nFilenameFormatting.TT.Y=\"ປີ\"\nFilenameFormatting.TT.z=\"ISO 8601 offset ຈາກ UTC ໃນເຂດເວລາ\"\nFilenameFormatting.TT.Z=\"ຊື່ເຂດເວລາ ຫຼື ຕົວຫຍໍ້\"\nFilenameFormatting.TT.FPS=\"ເຟຣມຕໍ່ວິນາທີ\"\nFilenameFormatting.TT.VF=\"ຮູບແບບວິດີໂອ\"\nBasic.Settings.Video=\"ວິດີໂອ\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Resolutions ກົງກັນ, ບໍ່ຈຳເປັນຕ້ອງ downscale]\"\nBasic.Settings.Video.FPSCommon=\"ຄ່າ FPS ທົ່ວໄປ\"\nBasic.Settings.Video.FPSInteger=\"ຄ່າ FPS ຈຳນວນເຕັມ\"\nBasic.Settings.Video.FPSFraction=\"ຄ່າ FPS ເສດສ່ວນ\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (ທົດລອງ)\"\nBasic.Settings.Video.InvalidResolution=\"ຄ່າຄວາມລະອຽດບໍ່ຖືກຕ້ອງ. ຕ້ອງເປັນ [width]x[height] (ເຊັ່ນ: 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Video output ກຳລັງເຮັດວຽກຢູ່. ກະລຸນາປິດ output ໃດໆກ່ອນທີ່ຈະປ່ຽນການຕັ້ງຄ່າວິດີໂອ.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (ໄວທີ່ສຸດ, ແຕ່ມົວຖ້າ scaling)\"\nBasic.Settings.Audio=\"ສຽງ\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True Peak (ໃຊ້ CPU ສູງກວ່າ)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ຄຳເຕືອນ: ສຽງ Surround sound ເປີດໃຊ້ງານຢູ່.\"\nBasic.Settings.Audio.MultichannelWarning=\"ຫາກກຳລັງສະຕຣີມ, ກວດເບິ່ງວ່າບໍລິການສະຕຣີມຂອງທ່ານຮອງຮັບທັງການຮັບ surround sound ແລະ ການຫຼິ້ນ surround sound ຫຼື ບໍ່. ຕົວຢ່າງເຊັ່ນ Facebook 360 Live ຮອງຮັບ surround sound ຢ່າງເຕັມທີ່; YouTube Live ຮອງຮັບການຮັບສຽງ 5.1 (ແລະ ຫຼິ້ນໃນ TVs).\\n\\nOBS audio filters ເຂົ້າກັນໄດ້ກັບ surround sound, ແຕ່ບໍ່ຮັບປະກັນການຮອງຮັບປັກອິນ VST.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"ເປີດໃຊ້ສຽງ surround sound?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການເປີດໃຊ້ສຽງ surround sound?\"\nBasic.Settings.Audio.DesktopDevice=\"ສຽງເດັສທັອບ (Desktop Audio)\"\nBasic.Settings.Audio.DesktopDevice2=\"ສຽງເດັສທັອບ 2\"\nBasic.Settings.Audio.EnablePushToMute=\"ເປີດໃຊ້ Push-to-mute\"\nBasic.Settings.Audio.EnablePushToTalk=\"ເປີດໃຊ້ Push-to-talk\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[ບໍ່ໄດ້ເຊື່ອມຕໍ່ອຸປະກອນ ຫຼື ບໍ່ສາມາດໃຊ້ໄດ້]\"\nBasic.Settings.Audio.Disabled=\"ປິດໃຊ້ງານ\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Low Latency Audio Buffering Mode (ສຳລັບ output Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ຄຳເຕືອນ: Low latency audio buffering ເປີດໃຊ້ງານຢູ່.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Low latency audio buffering mode ອາດເຮັດໃຫ້ສຽງຂາດຫາຍ ຫຼື ຢຸດຫຼິ້ນຈາກບາງແຫຼ່ງ.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"ເປີດໃຊ້ low latency audio buffering mode?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການເປີດໃຊ້ low latency audio buffering mode?\"\nBasic.Settings.Accessibility=\"ການເຂົ້າເຖິງ (Accessibility)\"\nBasic.Settings.Accessibility.ColorOverrides=\"ໃຊ້ສີທີ່ແຕກຕ່າງ\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"ຂອບ Source (Selection)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"ຂອບ Source (Crop)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"ຂອບ Source (Hover)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mixer Volume Band (-60 ຫາ -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mixer Volume Band (-20 ຫາ -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mixer Volume Band (-9 ຫາ 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mixer Volume Band (-60 ຫາ -20dB) (active)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mixer Volume Band (-20 ຫາ -9dB) (active)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mixer Volume Band (-9 ຫາ 0dB) (active)\"\nBasic.Settings.Advanced=\"ຂັ້ນສູງ\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"ສະແດງຄຳເຕືອນ output ທີ່ຍັງເຮັດວຽກຢູ່ ເມື່ອອອກ\"\nBasic.Settings.Advanced.FormatWarning=\"ຄຳເຕືອນ: ຮູບແບບສີ (Color formats) ອື່ນໆນອກຈາກ NV12/P010 ມີຈຸດປະສົງຫຼັກສຳລັບການບັນທຶກ, ແລະ ບໍ່ແນະນຳເມື່ອສະຕຣີມ. ການສະຕຣີມອາດເຮັດໃຫ້ເກີດການໃຊ້ CPU ເພີ່ມຂຶ້ນ ເນື່ອງຈາກການແປງຮູບແບບສີ.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"ຄຳເຕືອນ: High-precision formats ມັກຈະໃຊ້ກັບ HDR color spaces.\"\nBasic.Settings.Advanced.FormatWarning2100=\"ຄຳເຕືອນ: Rec. 2100 ຄວນໃຊ້ຮູບແບບທີ່ມີຄວາມລະອຽດຫຼາຍກວ່າ.\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"ອຸປະກອນ Monitoring\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"ຄ່າເລີ່ມຕົ້ນ\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"ປິດໃຊ້ Windows audio ducking\"\nBasic.Settings.Advanced.StreamDelay=\"ສະຕຣີມ Delay\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"ໄລຍະເວລາ\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"ຮັກສາ cutoff point (ເພີ່ມ delay) ເມື່ອເຊື່ອມຕໍ່ໃໝ່\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"ການໃຊ້ Memory ໂດຍປະມານ: %1 MB\"\nBasic.Settings.Advanced.Network=\"ເຄືອຂ່າຍ\"\nBasic.Settings.Advanced.Network.Disabled=\"ໂປຣໂຕຄໍສະຕຣີມທີ່ເລືອກໃນປັດຈຸບັນ ບໍ່ຮອງຮັບການປ່ຽນການຕັ້ງຄ່າເຄືອຂ່າຍ.\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"ເປີດໃຊ້ການເພີ່ມປະສິດທິພາບເຄືອຂ່າຍ\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"ເປີດໃຊ້ TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"ພະຍາຍາມເຮັດໃຫ້ RTMP output ເປັນມິດກັບແອັບພລິເຄຊັນອື່ນທີ່ອ່ອນໄຫວກັບ latency ໃນເຄືອຂ່າຍ ໂດຍການຄວບຄຸມອັດຕາການສົ່ງ.\\nມັນອາດຈະເພີ່ມຄວາມສ່ຽງຂອງ dropped frames ໃນການເຊື່ອມຕໍ່ທີ່ບໍ່ສະຖຽນ.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"ພຶດຕິກຳ Focus ຂອງຄີລັດ\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"ບໍ່ປິດໃຊ້ງານຄີລັດ\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"ປິດໃຊ້ງານຄີລັດ ເມື່ອໜ້າຕ່າງຫຼັກຖືກ Focus\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"ປິດໃຊ້ງານຄີລັດ ເມື່ອໜ້າຕ່າງຫຼັກບໍ່ຖືກ Focus\"\nBasic.Settings.Advanced.AutoRemux=\"Remux ອັດຕະໂນມັດເປັນ %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(ບັນທຶກເປັນ mkv)\"\nBasic.AdvAudio=\"ຄຸນສົມບັດສຽງຂັ້ນສູງ\"\nBasic.AdvAudio.ActiveOnly=\"ສະເພາະແຫຼ່ງທີ່ມາທີ່ເຮັດວຽກຢູ່\"\nBasic.AdvAudio.Name=\"ຊື່\"\nBasic.AdvAudio.Volume=\"ລະດັບສຽງ\"\nBasic.AdvAudio.VolumeSource=\"ລະດັບສຽງສຳລັບ '%1'\"\nBasic.AdvAudio.Mono=\"ໂມໂນ\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix ສຳລັບ '%1'\"\nBasic.AdvAudio.BalanceSource=\"Balance ສຳລັບ '%1'\"\nBasic.AdvAudio.SyncOffsetSource=\"Sync Offset ສຳລັບ '%1'\"\nBasic.AdvAudio.Monitoring.Both=\"Monitor ແລະ Output\"\nBasic.AdvAudio.MonitoringSource=\"Audio Monitoring ສຳລັບ '%1'\"\nBasic.Settings.Hotkeys=\"ຄີລັດ (Hotkeys)\"\nBasic.Settings.Hotkeys.Pair=\"Key combinations ທີ່ແບ່ງປັນກັບ '%1' ຈະເຮັດໜ້າທີ່ເປັນຕົວສະລັບ\"\nBasic.Settings.Hotkeys.Filter=\"ຟິວເຕີ\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"ກອງຕາມ ຄີລັດ\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"ຄີລັດນີ້ຖືກໃຊ້ຮ່ວມກັນກັບການກະທຳອື່ນໜຶ່ງຢ່າງ ຫຼື ຫຼາຍກວ່າ, ຄລິກເພື່ອສະແດງຂໍ້ຂັດແຍ່ງ\"\nBasic.Settings.Hotkeys.PleaseWait=\"ກຳລັງໂຫຼດຄີລັດ, ກະລຸນາລໍຖ້າ...\"\nBasic.Hotkeys.SelectScene=\"ສະລັບໄປສາກ\"\nBasic.SystemTray.Show=\"ສະແດງ\"\nBasic.SystemTray.Hide=\"ເຊື່ອງ\"\nBasic.SystemTray.Message.Reconnecting=\"ຕັດການເຊື່ອມຕໍ່. ກຳລັງເຊື່ອມຕໍ່ໃໝ່...\"\nMute=\"ປິດສຽງ\"\nUnmute=\"ເປີດສຽງ\"\nSceneItemShow=\"ສະແດງ '%1'\"\nSceneItemHide=\"ເຊື່ອງ '%1'\"\nOutputWarnings.NoTracksSelected=\"ທ່ານຕ້ອງເລືອກຢ່າງໜ້ອຍໜຶ່ງແທຣັກ\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"ຂໍ້ຜິດພາດການຕັ້ງຄ່າ Output\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"ທຸກ Output ຕ້ອງມີ audio track ຢ່າງໜ້ອຍໜຶ່ງແທຣັກທີ່ຖືກເລືອກ.\"\nOutputWarnings.MP4Recording=\"ຄຳເຕືອນ: ການບັນທຶກທີ່ບັນທຶກເປັນ MP4/MOV ຈະບໍ່ສາມາດກູ້ຄືນໄດ້ ຫາກໄຟລ໌ບໍ່ສາມາດ finalize ໄດ້ (ເຊັ່ນ: ຜົນມາຈາກ BSODs, ໄຟມອດ, ແລະອື່ນໆ). ຫາກທ່ານຕ້ອງການບັນທຶກ audio tracks ຫຼາຍແທຣັກ ພິຈາລະນາໃຊ້ MKV ແລະ remux ການບັນທຶກເປັນ MP4/MOV ຫຼັງຈາກມັນສຳເລັດ (ໄຟລ໌ → Remux ການບັນທຶກ)\"\nOutputWarnings.CannotPause=\"ຄຳເຕືອນ: ການບັນທຶກບໍ່ສາມາດຢຸດຊົ່ວຄາວໄດ້ ຫາກຕົວເຂົ້າລະຫັດການບັນທຶກຖືກຕັ້ງເປັນ \\\"(Use stream encoder)\\\"\"\nOutputWarnings.CodecIncompatible=\"ການເລືອກຕົວເຂົ້າລະຫັດສຽງ ຫຼື ວິດີໂອ ຖືກຣີເຊັດເນື່ອງຈາກຄວາມບໍ່ເຂົ້າກັນ. ກະລຸນາເລືອກຕົວເຂົ້າລະຫັດທີ່ເຂົ້າກັນໄດ້ຈາກລາຍການ.\"\nCodecCompat.Incompatible=\"(ບໍ່ເຂົ້າກັນກັບ %1)\"\nCodecCompat.CodecPlaceholder=\"ເລືອກຕົວເຂົ້າລະຫັດ...\"\nCodecCompat.ContainerPlaceholder=\"ເລືອກຮູບແບບ...\"\nCodecCompat.CodecMissingOnExit.Title=\"ບໍ່ໄດ້ເລືອກຕົວເຂົ້າລະຫັດ\"\nCodecCompat.CodecMissingOnExit.Text=\"ບໍ່ໄດ້ຕັ້ງຄ່າຕົວເຂົ້າລະຫັດວິດີໂອ ຫຼື ສຽງ ຢ່າງໜ້ອຍໜຶ່ງຕົວ. ກະລຸນາກວດສອບໃຫ້ແນ່ໃຈວ່າໄດ້ເລືອກຕົວເຂົ້າລະຫັດສຳລັບທັງການບັນທຶກ ແລະ ການສະຕຣີມ.\"\nCodecCompat.ContainerMissingOnExit.Title=\"ບໍ່ໄດ້ເລືອກຮູບແບບ\"\nCodecCompat.ContainerMissingOnExit.Text=\"ບໍ່ໄດ້ເລືອກຮູບແບບການບັນທຶກ. ກະລຸນາເລືອກຮູບແບບການບັນທຶກທີ່ເຂົ້າກັນໄດ້ກັບຕົວເຂົ້າລະຫັດສະຕຣີມທີ່ເລືອກ.\"\nFinalScene.Title=\"ລຶບສາກ\"\nFinalScene.Text=\"ຈຳເປັນຕ້ອງມີຢ່າງໜ້ອຍໜຶ່ງສາກ.\"\nNoSources.Title=\"ບໍ່ມີແຫຼ່ງທີ່ມາ\"\nNoSources.Text=\"ເບິ່ງຄືວ່າທ່ານຍັງບໍ່ໄດ້ເພີ່ມແຫຼ່ງວິດີໂອໃດໆ, ດັ່ງນັ້ນທ່ານຈະສົ່ງອອກໄດ້ພຽງໜ້າຈໍຫວ່າງເປົ່າ. ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການເຮັດແບບນີ້?\"\nNoSources.Text.AddSource=\"ທ່ານສາມາດເພີ່ມແຫຼ່ງທີ່ມາໄດ້ ໂດຍການຄລິກໄອຄອນ + ພາຍໃຕ້ກ່ອງ Sources ໃນໜ້າຕ່າງຫຼັກໄດ້ທຸກເວລາ.\"\nNoSources.Label=\"ທ່ານບໍ່ມີແຫຼ່ງທີ່ມາໃດໆ.\\nຄລິກປຸ່ມ + ດ້ານລຸ່ມ,\\nຫຼື ຄລິກຂວາບ່ອນນີ້ເພື່ອເພີ່ມ.\"\nChangeBG=\"ຕັ້ງຄ່າສີ\"\nCustomColor=\"ສີກຳນົດເອງ\"\nBrowserSource.EnableHardwareAcceleration=\"ເປີດໃຊ້ Browser Source Hardware Acceleration\"\nAbout=\"ກ່ຽວກັບ\"\nAbout.Info=\"OBS Studio ເປັນຊອບແວການບັນທຶກວິດີໂອ ແລະ ຖ່າຍທອດສົດແບບຟຣີ ແລະ ໂອເພນຊອດ.\"\nAbout.Donate=\"ຮ່ວມບໍລິຈາກ\"\nAbout.GetInvolved=\"ມີສ່ວນຮ່ວມ\"\nAbout.Authors=\"ຜູ້ຂຽນ\"\nAbout.License=\"ໃບອະນຸຍາດ\"\nAbout.Error=\"ຜິດພາດ! ບໍ່ສາມາດອ່ານໄຟລ໌ໄດ້.\\n\\nໄປທີ່: %1\"\nAbout.Contribute=\"ສະໜັບສະໜູນໂຄງການ OBS\"\nAddUrl.Title=\"ເພີ່ມແຫຼ່ງທີ່ມາຜ່ານ URL\"\nAddUrl.Text=\"ທ່ານໄດ້ລາກ URL ເຂົ້າໄປໃນ OBS. ສິ່ງນີ້ຈະເພີ່ມລິງກ໌ເປັນແຫຼ່ງທີ່ມາໂດຍອັດຕະໂນມັດ. ດຳເນີນການຕໍ່?\"\nResizeOutputSizeOfSource=\"ປັບຂະໜາດ output (ຂະໜາດ source)\"\nResizeOutputSizeOfSource.Text=\"Base ແລະ output resolutions ຈະຖືກປັບຂະໜາດໃຫ້ເທົ່າກັບຂະໜາດຂອງ source ປັດຈຸບັນ.\"\nResizeOutputSizeOfSource.Continue=\"ທ່ານຕ້ອງການດຳເນີນການຕໍ່ບໍ່?\"\nImporter=\"ນຳເຂົ້າ Scene Collection\"\nImporter.SelectCollection=\"ເລືອກ Scene Collection\"\nImporter.HelpText=\"ເພີ່ມໄຟລ໌ໃສ່ໜ້າຕ່າງນີ້ ເພື່ອນຳເຂົ້າ collections ຈາກ OBS ຫຼື ໂປຣແກຣມອື່ນໆທີ່ຮອງຮັບ.\"\nImporter.Program=\"ແອັບພລິເຄຊັນທີ່ກວດພົບ\"\nImporter.AutomaticCollectionPrompt=\"ຄົ້ນຫາ Scene Collections ໂດຍອັດຕະໂນມັດ\"\nImporter.AutomaticCollectionText=\"OBS ສາມາດຄົ້ນຫາ scene collections ທີ່ນຳເຂົ້າໄດ້ ຈາກໂປຣແກຣມພາຍນອກທີ່ຮອງຮັບ ໂດຍອັດຕະໂນມັດ. ທ່ານຕ້ອງການໃຫ້ OBS ຄົ້ນຫາ collections ໃຫ້ທ່ານອັດຕະໂນມັດບໍ່?\\n\\nທ່ານສາມາດປ່ຽນຄ່ານີ້ພາຍຫຼັງໄດ້ໃນ ການຕັ້ງຄ່າ > ທົ່ວໄປ > Importers.\"\nImporter.SelectFile=\"ເປີດຫາ...\"\nRestart=\"ຣີສະຕາດ\"\nNeedsRestart=\"OBS Studio ຈຳເປັນຕ້ອງຖືກຣີສະຕາດ. ທ່ານຕ້ອງການຣີສະຕາດດຽວນີ້ເລີຍບໍ່?\"\nLoadProfileNeedsRestart=\"ໂປຣໄຟລ໌ມີການຕັ້ງຄ່າທີ່ຕ້ອງການການຣີສະຕາດ OBS:\\n%1\\n\\nທ່ານຕ້ອງການຣີສະຕາດ OBS ເພື່ອໃຫ້ການຕັ້ງຄ່າເຫຼົ່ານີ້ມີຜົນບໍ່?\"\nContextBar.NoSelectedSource=\"ບໍ່ໄດ້ເລືອກແຫຼ່ງທີ່ມາ\"\nContextBar.MediaControls.PlayMedia=\"ຫຼິ້ນສື່\"\nContextBar.MediaControls.PauseMedia=\"ຢຸດສື່ຊົ່ວຄາວ\"\nContextBar.MediaControls.StopMedia=\"ຢຸດສື່\"\nContextBar.MediaControls.RestartMedia=\"ຣີສະຕາດສື່\"\nContextBar.MediaControls.PlaylistNext=\"ຕໍ່ໄປໃນ Playlist\"\nContextBar.MediaControls.PlaylistPrevious=\"ກ່ອນໜ້າໃນ Playlist\"\nYouTube.Auth.Ok=\"ການຢືນຢັນຕົວຕົນສຳເລັດແລ້ວ.\\nຕອນນີ້ທ່ານສາມາດປິດໜ້ານີ້ໄດ້.\"\nYouTube.Auth.NoCode=\"ຂັ້ນຕອນການຢືນຢັນຕົວຕົນບໍ່ສຳເລັດ.\"\nYouTube.Auth.NoChannels=\"ບໍ່ມີຊ່ອງທີ່ສາມາດໃຊ້ໄດ້ໃນບັນຊີທີ່ເລືອກ\"\nYouTube.Auth.WaitingAuth.Title=\"ການຢືນຢັນຕົວຕົນຜູ້ໃຊ້ YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"ກະລຸນາສຳເລັດການຢືນຢັນຕົວຕົນໃນໂປຣແກຣມທ່ອງເວັບພາຍນອກຂອງທ່ານ.<br>ຖ້າໂປຣແກຣມທ່ອງເວັບພາຍນອກບໍ່ເປີດ, ໃຫ້ໄປທີ່ລິ້ງນີ້ ແລະ ສຳເລັດການຢືນຢັນຕົວຕົນ:<br>%1\"\nYouTube.AuthError.Text=\"ການດຶງຂໍ້ມູນຊ່ອງລົ້ມເຫຼວ: %1.\"\nYouTube.Actions.WindowTitle=\"ຕັ້ງຄ່າການອອກອາກາດ YouTube - ຊ່ອງ: %1\"\nYouTube.Actions.CreateNewEvent=\"ສ້າງການອອກອາກາດໃໝ່\"\nYouTube.Actions.ChooseEvent=\"ເລືອກການອອກອາກາດທີ່ມີຢູ່\"\nYouTube.Actions.Title=\"ຫົວຂໍ້*\"\nYouTube.Actions.MyBroadcast=\"ການອອກອາກາດຂອງຂ້ອຍ\"\nYouTube.Actions.Description=\"ຄຳອະທິບາຍ\"\nYouTube.Actions.Privacy=\"ຄວາມເປັນສ່ວນຕົວ*\"\nYouTube.Actions.Privacy.Private=\"ສ່ວນຕົວ\"\nYouTube.Actions.Privacy.Public=\"ສາທາລະນະ\"\nYouTube.Actions.Privacy.Unlisted=\"ບໍ່ສະແດງໃນລາຍການ\"\nYouTube.Actions.Category=\"ໝວດໝູ່\"\nYouTube.Actions.Thumbnail=\"ຮູບຕົວຢ່າງ\"\nYouTube.Actions.Thumbnail.SelectFile=\"ເລືອກໄຟລ໌...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"ບໍ່ໄດ້ເລືອກໄຟລ໌\"\nYouTube.Actions.Thumbnail.ClearFile=\"ລ້າງ\"\nYouTube.Actions.MadeForKids=\"ວິດີໂອນີ້ສ້າງມາເພື່ອເດັກນ້ອຍບໍ່?*\"\nYouTube.Actions.MadeForKids.Yes=\"ແມ່ນ, ມັນສ້າງມາເພື່ອເດັກນ້ອຍ\"\nYouTube.Actions.MadeForKids.No=\"ບໍ່, ມັນບໍ່ໄດ້ສ້າງມາເພື່ອເດັກນ້ອຍ\"\nYouTube.Actions.AdditionalSettings=\"ການຕັ້ງຄ່າເພີ່ມເຕີມ\"\nYouTube.Actions.Latency=\"ຄວາມຊັກຊ້າ\"\nYouTube.Actions.Latency.Normal=\"ປົກກະຕິ\"\nYouTube.Actions.Latency.Low=\"ຕ່ຳ\"\nYouTube.Actions.Latency.UltraLow=\"ຕ່ຳສຸດ\"\nYouTube.Actions.EnableAutoStart=\"ເປີດໃຊ້ການເລີ່ມຕົ້ນອັດຕະໂນມັດ\"\nYouTube.Actions.EnableAutoStop=\"ເປີດໃຊ້ການຢຸດອັດຕະໂນມັດ\"\nYouTube.Actions.AutoStartStop.TT=\"ບົ່ງບອກວ່າການອອກອາກາດທີ່ກຳນົດເວລານີ້ຄວນຈະເລີ່ມຕົ້ນໂດຍອັດຕະໂນມັດຫຼືບໍ່\"\nYouTube.Actions.EnableDVR=\"ເປີດໃຊ້ DVR\"\nYouTube.Actions.360Video=\"ວິດີໂອ 360 ອົງສາ\"\nYouTube.Actions.ScheduleForLater=\"ກຳນົດເວລາໄວ້ພາຍຫຼັງ\"\nYouTube.Actions.RememberSettings=\"ຈື່ການຕັ້ງຄ່ານີ້ໄວ້\"\nYouTube.Actions.Create_Ready=\"ສ້າງການອອກອາກາດ\"\nYouTube.Actions.Create_GoLive=\"ສ້າງການອອກອາກາດ ແລະ ເລີ່ມສະຕຣີມ\"\nYouTube.Actions.Choose_Ready=\"ເລືອກການອອກອາກາດ\"\nYouTube.Actions.Choose_GoLive=\"ເລືອກການອອກອາກາດ ແລະ ເລີ່ມສະຕຣີມ\"\nYouTube.Actions.Create_Schedule=\"ກຳນົດເວລາການອອກອາກາດ\"\nYouTube.Actions.Create_Schedule_Ready=\"ກຳນົດເວລາ ແລະ ເລືອກການອອກອາກາດ\"\nYouTube.Actions.Dashboard=\"ເປີດ YouTube Studio\"\nYouTube.Actions.Error.Title=\"ຂໍ້ຜິດພາດໃນການສ້າງການອອກອາກາດສົດ\"\nYouTube.Actions.Error.Text=\"ຂໍ້ຜິດພາດການເຂົ້າເຖິງ YouTube '%1'.<br/>ສາມາດເບິ່ງລາຍລະອຽດຂໍ້ຜິດພາດໄດ້ທີ່ <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"ຂໍ້ຜິດພາດການເຂົ້າເຖິງ YouTube. ກະລຸນາກວດສອບການເຊື່ອມຕໍ່ເຄືອຂ່າຍ ຫຼື ການເຂົ້າເຖິງເຊີບເວີ YouTube ຂອງທ່ານ.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"ຂໍ້ຜິດພາດການສ້າງການອອກອາກາດ '%1'.<br/>ສາມາດເບິ່ງລາຍລະອຽດຂໍ້ຜິດພາດໄດ້ທີ່ <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"ບໍ່ມີການສ້າງສະຕຣີມ. ກະລຸນາເຊື່ອມຕໍ່ບັນຊີຂອງທ່ານໃໝ່.\"\nYouTube.Actions.Error.YouTubeApi=\"ຂໍ້ຜິດພາດ YouTube API. ກະລຸນາເບິ່ງໄຟລ໌ບັນທຶກສຳລັບຂໍ້ມູນເພີ່ມເຕີມ.\"\nYouTube.Actions.Error.BroadcastNotFound=\"ບໍ່ພົບການອອກອາກາດທີ່ເລືອກ.\"\nYouTube.Actions.Error.FileMissing=\"ບໍ່ມີໄຟລ໌ທີ່ເລືອກຢູ່.\"\nYouTube.Actions.Error.FileOpeningFailed=\"ລົ້ມເຫຼວໃນການເປີດໄຟລ໌ທີ່ເລືອກ.\"\nYouTube.Actions.Error.FileTooLarge=\"ໄຟລ໌ທີ່ເລືອກມີຂະໜາດໃຫຍ່ເກີນໄປ (ຈຳກັດ: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"ການປ່ຽນສະຖານະການຖ່າຍທອດລົ້ມເຫຼວ: %1<br/><br/>ຖ້າຂໍ້ຜິດພາດນີ້ຍັງເກີດຂຶ້ນຢູ່ ໃຫ້ໄປທີ່ YouTube Studio ແລະ ລອງເຮັດດ້ວຍຕົນເອງ.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"ການຖ່າຍທອດກຳລັງປ່ຽນໄປສູ່ຂັ້ນຕອນການທົດສອບ, ອາດໃຊ້ເວລາຈັກໜ້ອຍ. ກະລຸນາລອງໃໝ່ອີກຄັ້ງໃນ 10-30 ວິນາທີ.\"\nYouTube.Actions.EventsLoading=\"ກຳລັງໂຫຼດລາຍການເຫດການ...\"\nYouTube.Actions.EventCreated.Title=\"ສ້າງເຫດການແລ້ວ\"\nYouTube.Actions.EventCreated.Text=\"ສ້າງເຫດການສຳເລັດແລ້ວ.\"\nYouTube.Actions.Stream=\"ສະຕຣີມ\"\nYouTube.Actions.Stream.ScheduledFor=\"ກຳນົດເວລາໄວ້ສຳລັບ %1\"\nYouTube.Actions.Stream.Resume=\"ສືບຕໍ່ການສະຕຣີມທີ່ຖືກຂັດຈັງຫວະ\"\nYouTube.Actions.Stream.YTStudio=\"ສ້າງໂດຍອັດຕະໂນມັດໂດຍ YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"ກຳລັງສ້າງການຖ່າຍທອດສົດໃໝ່, ກະລຸນາລໍຖ້າ...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"ຕ້ອງການການເລີ່ມຕົ້ນດ້ວຍຕົນເອງ\"\nYouTube.Actions.AutoStartStreamingWarning=\"ການເລີ່ມຕົ້ນອັດຕະໂນມັດຖືກປິດໄວ້ສຳລັບເຫດການນີ້, ກົດ \\\"ຖ່າຍທອດສົດ\\\" ເພື່ອເລີ່ມການຖ່າຍທອດຂອງທ່ານ.\"\nYouTube.Actions.AutoStopStreamingWarning=\"ທ່ານຈະບໍ່ສາມາດເຊື່ອມຕໍ່ກັບຄືນໄດ້.<br>ການສະຕຣີມຂອງທ່ານຈະຢຸດ ແລະ ທ່ານຈະບໍ່ໄດ້ຖ່າຍທອດສົດອີກຕໍ່ໄປ.\"\nYouTube.Errors.liveStreamingNotEnabled=\"ການຖ່າຍທອດສົດບໍ່ໄດ້ເປີດໃຊ້ງານໃນຊ່ອງ YouTube ທີ່ເລືອກໄວ້.<br/><br/>ເບິ່ງຂໍ້ມູນເພີ່ມຕື່ມໃນການຕັ້ງຄ່າຊ່ອງ.\"\nYouTube.Errors.livePermissionBlocked=\"ການຖ່າຍທອດສົດບໍ່ສາມາດໃຊ້ງານໄດ້ໃນຊ່ອງ YouTube ທີ່ເລືອກ.<br/>ກະລຸນາຮັບຊາບວ່າ ອາດຈະໃຊ້ເວລາເຖິງ 24 ຊົ່ວໂມງ ເພື່ອໃຫ້ການຖ່າຍທອດສົດສາມາດໃຊ້ງານໄດ້ ຫຼັງຈາກເປີດໃຊ້ງານໃນການຕັ້ງຄ່າຊ່ອງຂອງທ່ານ.\"\nYouTube.Errors.errorExecutingTransition=\"ການປ່ຽນສາກຫຼົ້ມເຫຼວເນື່ອງຈາກຂໍ້ຜິດພາດຂອງ backend. ກະລຸນາລອງໃໝ່ໃນອີກບໍ່ເທົ່າໃດວິນາທີ.\"\nYouTube.Errors.errorStreamInactive=\"YouTube ບໍ່ໄດ້ຮັບຂໍ້ມູນສຳລັບການສະຕຣີມຂອງທ່ານ. ກະລຸນາກວດສອບການຕັ້ງຄ່າ ແລ້ວລອງໃໝ່ອີກຄັ້ງ.\"\nYouTube.Errors.invalidTransition=\"ການປ່ຽນສາກທີ່ພະຍາຍາມຢູ່ນັ້ນບໍ່ຖືກຕ້ອງ. ອາດເປັນຍ້ອນການສະຕຣີມຍັງເຮັດວຽກການປ່ຽນສາກກ່ອນໜ້າບໍ່ສຳເລັດ. ກະລຸນາລໍຖ້າຈັກໜ້ອຍ ແລ້ວລອງໃໝ່.\"\nYouTube.DocksRemoval.Title=\"ລ້າງ Dock ຂອງ Browser YouTube ແບບເກົ່າ\"\nYouTube.DocksRemoval.Text=\"Dock ຂອງ Browser ເຫຼົ່ານີ້ຈະຖືກລຶບອອກເນື່ອງຈາກບໍ່ໃຊ້ແລ້ວ (deprecated):\\n\\n%1\\nໃຫ້ໃຊ້ \\\"Docks/YouTube Live Control Room\\\" ແທນ.\"\nConfigDownload.WarningMessageTitle=\"ຄຳເຕືອນ\"\nFailedToStartStream.MissingConfigURL=\"ບໍ່ມີ URL ການຕັ້ງຄ່າສຳລັບບໍລິການປັດຈຸບັນ\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"ບໍ່ໄດ້ລະບຸ URL RTMP ແບບກຳນົດເອງ\"\nFailedToStartStream.InvalidCustomConfig=\"ການຕັ້ງຄ່າກຳນົດເອງບໍ່ຖືກຕ້ອງ\"\nFailedToStartStream.MissingCanvas=\"ຂາດ Canvas ເພີ່ມເຕີມທີ່ຕັ້ງຄ່າໄວ້\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"ສ້າງບໍລິການວິດີໂອຫຼາຍແທຣັກລົ້ມເຫຼວ\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"ສ້າງຜົນຜະລິດ RTMP ວິດີໂອຫຼາຍແທຣັກລົ້ມເຫຼວ\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC ບໍ່ພ້ອມໃຊ້ງານ.\\n\\nຄົ້ນຫາປະເພດຕົວເຂົ້າລະຫັດ '%1' ບໍ່ພົບ\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"ສ້າງຕົວເຂົ້າລະຫັດວິດີໂອ '%1' ລົ້ມເຫຼວ (ປະເພດ: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"ດຶງຂໍ້ມູນວິດີໂອ OBS ລົ້ມເຫຼວ ໃນຂະນະສ້າງຕົວເຂົ້າລະຫັດ '%1' (ປະເພດ: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"ສ້າງຕົວເຂົ້າລະຫັດສຽງລົ້ມເຫຼວ\"\nFailedToStartStream.NoRTMPURLInConfig=\"ການຕັ້ງຄ່າບໍ່ມີ URL ເປົ້າໝາຍການສະຕຣີມ RTMP(S)\"\nFailedToStartStream.FallbackToDefault=\"ການເລີ່ມຕົ້ນສະຕຣີມໂດຍໃຊ້ %1 ລົ້ມເຫຼວ; ທ່ານຕ້ອງການລອງໃໝ່ໂດຍໃຊ້ການຕັ້ງຄ່າເຂົ້າລະຫັດດຽວບໍ່?\"\nFailedToStartStream.ConfigRequestFailed=\"ບໍ່ສາມາດດຶງການຕັ້ງຄ່າຈາກ %1<br><br>ຂໍ້ຜິດພາດ HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"ໄດ້ຮັບຄ່າສະຖານະທີ່ບໍ່ຮູ້ຈັກ '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nທ່ານຕ້ອງການສະຕຣີມຕໍ່ໂດຍບໍ່ມີ %1 ບໍ່?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nທ່ານຕ້ອງການສະຕຣີມຕໍ່ບໍ່?\"\nFailedToStartStream.MissingEncoderConfigs=\"ການຕັ້ງຄ່າ Go live ບໍ່ລວມເອົາການຕັ້ງຄ່າຕົວເຂົ້າລະຫັດ\"\nFailedToStartStream.InvalidEncoderConfig=\"ການຕັ້ງຄ່າ Go live ມີການຕັ້ງຄ່າຕົວເຂົ້າລະຫັດທີ່ບໍ່ຖືກຕ້ອງ\"\nFailedToStartStream.StatusMissingHTML=\"ຄຳຮ້ອງຂໍ Go live ສົ່ງຄືນຂໍ້ຜິດພາດທີ່ບໍ່ລະບຸ\"\nFailedToStartStream.NoConfigSupplied=\"ຂາດການຕັ້ງຄ່າ\"\nMultitrackVideo.Info=\"%1 ຈະປັບແຕ່ງການຕັ້ງຄ່າຂອງທ່ານໂດຍອັດຕະໂນມັດ ເພື່ອເຂົ້າລະຫັດ ແລະ ສົ່ງວິດີໂອຫຼາຍລະດັບຄຸນນະພາບ. ການເລືອກຕົວເລືອກນີ້ຈະສົ່ງຂໍ້ມູນ %2 ກ່ຽວກັບຄອມພິວເຕີ ແລະ ການຕັ້ງຄ່າຊອບແວຂອງທ່ານ.\"\nMultitrackVideo.IncompatibleSettings.Title=\"ການຕັ້ງຄ່າທີ່ບໍ່ເຂົ້າກັນ\"\nMultitrackVideo.IncompatibleSettings.Text=\"ປັດຈຸບັນ %1 ບໍ່ເຂົ້າກັນກັບ:\\n\\n%2\\nເພື່ອສະຕຣີມຕໍ່ດ້ວຍ %1, ໃຫ້ປິດການຕັ້ງຄ່າທີ່ບໍ່ເຂົ້າກັນ:\\n\\n%3\\nແລ້ວເລີ່ມສະຕຣີມອີກຄັ້ງ.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"ປິດໃຊ້ງານສຳລັບການສະຕຣີມນີ້ ແລະ ເລີ່ມສະຕຣີມ\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"ອັບເດດການຕັ້ງຄ່າ ແລະ ເລີ່ມສະຕຣີມ\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"ປັດຈຸບັນ %1 ບໍ່ເຂົ້າກັນກັບການຕັ້ງຄ່າ [ສຽງ → ທົ່ວໄປ → ຊ່ອງສຽງ] ທີ່ຕັ້ງເປັນ '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[ສຽງ → ທົ່ວໄປ → ຊ່ອງສຽງ] ຈຳເປັນຕ້ອງຕັ້ງເປັນ '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 ຕ້ອງການການຕັ້ງຄ່າທີ່ແຕກຕ່າງກັນຫຼາຍອັນສຳລັບ [ສຽງ → ທົ່ວໄປ → ຊ່ອງສຽງ]\"\nBasic.OpenPluginManager=\"ຕົວຈັດການປັກອິນ\"\nPluginManager=\"ຕົວຈັດການປັກອິນ\"\nPluginManager.Restart=\"ຕ້ອງເລີ່ມ OBS Studio ໃໝ່ເພື່ອໃຫ້ນຳໃຊ້ການປ່ຽນແປງຂອງປັກອິນ\"\nPluginManager.MissingPlugin=\"[ບໍ່ພົບປັກອິນ]\"\nPluginManager.Section.Discover=\"ເລືອກເບິ່ງ\"\nPluginManager.Section.Manage=\"ຕິດຕັ້ງແລ້ວ\"\nPluginManager.Section.Updates=\"ການອັບເດດ\"\nPluginManager.Section.Manage.Title=\"ຈັດການປັກອິນທີ່ເປີດໃຊ້ງານ\"\n"
  },
  {
    "path": "frontend/data/locale/lt-LT.ini",
    "content": "Language=\"Lietuvių\"\nOK=\"ОК\"\nApply=\"Pritaikyti\"\nCancel=\"Atšaukti\"\nClose=\"Užverti\"\nSave=\"Išsaugoti\"\nDiscard=\"Atmesti\"\nDisable=\"Išjungti\"\nYes=\"Taip\"\nNo=\"Ne\"\nAdd=\"Pridėti\"\nRemove=\"Pašalinti\"\nRename=\"Pervardinti\"\nInteract=\"Sąveikauti\"\nFilters=\"Filtrai\"\nProperties=\"Savybės\"\nMoveUp=\"Perkelti aukštyn\"\nMoveDown=\"Perkelti žemyn\"\nSettings=\"Nustatymai\"\nDisplay=\"Ekranas\"\nName=\"Vardas\"\nExit=\"Išeiti\"\nMixer=\"Audio mikseris\"\nBrowse=\"Naršyti\"\nDroppedFrames=\"Atmesti kadrai %1 (%2%)\"\nClear=\"Išvalyti\"\nRevert=\"Atstatyti\"\nShow=\"Parodyti\"\nHide=\"Paslėpti\"\nUnhideAll=\"Atslėpti visus\"\nUntitled=\"Nepavadintas\"\nNew=\"Naujas\"\nDuplicate=\"Dubliuoti\"\nEnable=\"Įjungti\"\nDisableOSXVSync=\"Išjungti macOS V-Sync\"\nResetOSXVSyncOnExit=\"Iš naujo nustatyti macOS V-Sync išeinant\"\nHighResourceUsage=\"Kodavimas perkrautas! Mažinkite vaizdo parametrus arba naudokite greitesnę kodavimo parengtį.\"\nTransition=\"Perėjimas\"\nQuickTransitions=\"Greitieji perėjimai\"\nFadeToBlack=\"Pereiti į juodą\"\nLeft=\"Iš kairės\"\nRight=\"Iš dešinės\"\nTop=\"Iš viršaus\"\nBottom=\"Iš apačios\"\nReset=\"Atstatyti\"\nHours=\"Valandos\"\nMinutes=\"Minutės\"\nSeconds=\"Sekundės\"\nDeprecated=\"Nebenaudojamas\"\nReplayBuffer=\"Pakartojimo ribotuvas\"\nImport=\"Importuoti\"\nExport=\"Eksportuoti\"\nCopy=\"Kopijuoti\"\nPaste=\"Įklijuoti\"\nPasteReference=\"Įklijuoti (Nuoroda)\"\nPasteDuplicate=\"Įklijuoti (Duplikatas)\"\nRemuxRecordings=\"Remux įrašymas\"\nNext=\"Kitas\"\nBack=\"Atgal\"\nDefaults=\"Numatytieji\"\nRestoreDefaults=\"Numatytieji nustatymai\"\nHideMixer=\"Paslėpti maišyklėje\"\nTransitionOverride=\"Perėjimo perrašymas\"\nShowTransition=\"Rodymo perėjimas\"\nHideTransition=\"Paslėpimo perrėjimas\"\nNone=\"Nieko\"\nStudioMode.Preview=\"Peržiūra\"\nStudioMode.Program=\"Programa\"\nStudioMode.PreviewSceneName=\"Peržiūra: %1\"\nStudioMode.ProgramSceneName=\"Programa: %1\"\nShowInMultiview=\"Rodyti daugialypėje peržiūroje\"\nVerticalLayout=\"Vertikalus išdėstymas\"\nGroup=\"Grupė\"\nDoNotShowAgain=\"Daugiau nerodyti\"\nDefault=\"(Numatytas)\"\nCalculating=\"Skaičiuojama...\"\nFullscreen=\"Visas ekranas\"\nWindowed=\"Lango rėžimas\"\nRefreshBrowser=\"Atnaujinti\"\nAspectRatio=\"Kraštinių santykis <b>%1:%2</b>\"\nLockVolume=\"Užrakinti garsą\"\nLogViewer=\"Peržiūrėti žurnalą\"\nShowOnStartup=\"Rodyti paleidžiant\"\nOpenFile=\"Atidaryti failą\"\nAddSource=\"Pridėkite šaltinį\"\nRemoveScene=\"Pašalinti pasirinktą sceną?\"\nRemoveSource=\"Pašalinti pasirinktą šaltinį\"\nMoveSceneUp=\"Perkelti sceną aukštyn\"\nMoveSceneDown=\"Perkelti sceną žemyn\"\nMoveSourceUp=\"Perkelti šaltinį aukštyn\"\nMoveSourceDown=\"Perkelti šaltinį žemyn\"\nSourceProperties=\"Atidaryti šaltinio savybes\"\nSourceFilters=\"Atidaryti šaltinio filterius\"\nMixerToolbarMenu=\"Garso Maišytojo Meniu\"\nSceneFilters=\"Atidaryti šaltinio filterius\"\nList=\"Sąrašas\"\nGrid=\"Tinklelis\"\nPluginsFailedToLoad.Title=\"Papildinio įkėlimo klaida\"\nPluginsFailedToLoad.Text=\"Nepavyko įkelti šių OBS papildinių:\\n\\n%1\\nAtnaujinkite arba pašalinkite šiuos papildinius.\"\nAlreadyRunning.Title=\"OBS jau yra ijungtas\"\nAlreadyRunning.Text=\"OBS jau paleistas! Nebent jūs norėjote taip padaryti, prašome išjungti esančius OBS procesus prieš paleidžiant naują. Jeigu turite nusistatę OBS susimažinti į paslėptas piktogramas, prašome pažiūrėti ar programa vis dar veikia ten.\"\nAlreadyRunning.LaunchAnyway=\"Vistiek paleisti\"\nSafeMode.Restart=\"Ar norite perkrauti OBS Saugiame Režime (Trečios partijos įskiepiai, scenarijai ir \\\"WebSockets\\\" isjungti)?\"\nSafeMode.RestartNormal=\"Ar norite perkrauti OBS Normaliame režime?\"\nChromeOS.Title=\"Nepalaikoma platforma\"\nChromeOS.Text=\"OBS yra paleistas ChromeOS sistemoje. Ši platforma yra nepalaikoma.\"\nWine.Title=\"Wine aptiktas\"\nWine.Text=\"OBS paleidimas programoje „Wine“ nepalaikomas, o daugelis funkcijų, pvz., fiksavimo ar įrenginio šaltinių, neveiks arba bus tik ribotos talpos.<br><br>Rekomenduojame paleisti savąją OBS versiją, pvz., <a href='https://flathub.org/apps/details/com.obsproject.Studio'>mūsų Flatpak versija</a> arba operacinės sistemos paketus.\"\nDockCloseWarning.Title=\"Uždaromas prijungiamas langas\"\nDockCloseWarning.Text=\"Jūs ką tik uždarėte prijungiamą langą. Jei norėtumete vėl jį rodyti, naudokite Rodymas, Prijungimo meniu juostoje.\"\nExtraBrowsers=\"Pritaikyti naršyklės prijungimai\"\nExtraBrowsers.Info=\"Pridėkite prijungiamą langą duodami jam vardą ir nuoroda (URL). Tada paspauskite taikyti arba uždaryti. Jūs galite pridėti ar ištrinti prijungiamus langus betkada.\"\nExtraBrowsers.DockName=\"Prijungiamo lango pavadinimas\"\nAuth.Authing.Title=\"Autentifikuojama...\"\nAuth.Authing.Text=\"Authentikuojama su %1, prašome palaukti...\"\nAuth.AuthFailure.Title=\"Autentifikacijos klaida\"\nAuth.AuthFailure.Text=\"Nepavyko authentikuotis su %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Reikalingas autentifikavimas\"\nAuth.InvalidScope.Text=\"Authentikavimo reikalavimai %1 buvo pakeisti. Kai kurios funkcijos gali būti neveikiančios.\"\nAuth.LoadingChannel.Title=\"Kraunama kanalo informacija\"\nAuth.LoadingChannel.Text=\"Kraunama %1 kanalo informacija, prašome palaukti...\"\nAuth.LoadingChannel.Error=\"Nepavyko gauti kanalo informacijos\"\nAuth.ChannelFailure.Title=\"Nepavyko užkrauti kanalo\"\nAuth.ChannelFailure.Text=\"Nepavyko užkrauti kanalo informacijos %1\\n\\n%2: %3\"\nAuth.Chat=\"chat'as\"\nAuth.StreamInfo=\"Transliacijos informacija\"\nTwitchAuth.Stats=\"Twitch statistika\"\nTwitchAuth.Feed=\"Twitch aktyvumo sąrašas\"\nTwitchAuth.TwoFactorFail.Title=\"Start Streaming\"\nTwitchAuth.TwoFactorFail.Text=\"OBS negalėjo prisijungti prie jūsų Twitch paskyros. Prašome įsitikinti, kad dviejų veiksmų autentifikacija yra įjungta per jūsų <a href='https://www.twitch.tv/settings/security'>Twitch apsaugos nustatymus</a> nes tai yra reikalingas žingsnis, kad gyvai transliuoti.\"\nRestreamAuth.Channels=\"Restream kanalai\"\nCopy.Filters=\"Kopijuoti filtrus\"\nPaste.Filters=\"Įklijuoti filtrus\"\nBrowserPanelInit.Title=\"Inicijuojama naršyklė...\"\nBrowserPanelInit.Text=\"Inicijuojama naršyklė, prašome palaukti...\"\nBandwidthTest.Region=\"Regionas\"\nBandwidthTest.Region.US=\"Jungtinės Valstijos\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Azija\"\nBandwidthTest.Region.Other=\"Kita\"\nBasic.AutoConfig=\"Automatinės configuracijos vedlys\"\nBasic.AutoConfig.ApplySettings=\"Taikyti nustatymus\"\nBasic.AutoConfig.StartPage=\"Naudojimo informacija\"\nBasic.AutoConfig.StartPage.SubTitle=\"išsirinkite kam jūs naudosite programą\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimizuoti srautiniam perdavimui, įrašymas bus antroje vietoje.\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimizuoti tik įrašymui, aš netransliuosiu\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"aš naudosiu tik virtualią kamerą\"\nBasic.AutoConfig.VideoPage=\"Video nustatymai\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Specifikuokite video nustatymus, kuriuos norite naudoti\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Naudoti dabartinį (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Ekranas %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Naudoti dabartinį (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Arba 60 arba 30, bet teikite pirmenybę 60 jeigu įmanoma\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Arba 60 arba 30, bet teikti pirmenybę didesniai rezoliucijai\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Pastaba: drobės (bazinė) skiriamoji geba nebūtinai sutampa su skiriamąja geba, kuria transliuosite arba įrašinėsite. Jūsų faktinė srauto ir (arba) įrašymo skiriamoji geba gali būti mažesnė už drobės skiriamąją gebą, kad būtų sumažintas išteklių naudojimas arba bitų srauto reikalavimai.\"\nBasic.AutoConfig.StreamPage=\"Transliacijos informacija\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Įveskite savo transliacijos duomenis\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Prijungti paskyrą (rekomenduojama)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Atjungti paskyrą\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Atjungti paskyrą?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Šis pakeitimas įsigalios iš karto. Ar esate įsitikinę, kad norite atjungti paskyrą?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Gauti transliacijos raktą\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Daugiau informacijos\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Naudoti transliacijos raktą\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Nadoti translacijos raktą (pažengęs)\"\nBasic.AutoConfig.StreamPage.Service=\"Platforma\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Rodyti visus...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Pasirinktinis...\"\nBasic.AutoConfig.StreamPage.Server=\"Serveris\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Transliacijos raktas\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: įveskite šifravimo slaptažodį.\\nRTMP: įveskite paslaugos teikiamą raktą.\\nSRT: įveskite streamid, jei paslauga jį naudoja.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kodavimo raktas\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Nešiotojo žetonas\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Prijungta paskyra\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Apskaičiuoti bitų perdavimo spartą naudojant pralaidumo testą (gali užtrukti kelias minutes)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Pirmenybę teikti aparatiniam kodavimui\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Aparatinis kodavimas pašalina didžiąją dalį procesoriaus sąnaudų, bet gali prireikti didesnės bitų perdavimo spartos tam pačiam kokybės lygiui pasiekti.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Transliacijos įspėjimas\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Atliekant pralaidumo testą į Jūsų kanalą bus transliuojami atsitiktiniai vaizdo duomenys be garso. Jei galite, rekomenduojama laikinai išjungti transliacijos įrašų išsaugojimą ir nustatyti transliaciją į privačią, kol baigsis testas. Tęsti?\"\nBasic.AutoConfig.TestPage=\"Galutinis resultatas\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Dabar programa atlieka testus idealių nustatymų parinkimui\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testavimas baigtas\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Atliekamas pralaidumo testas. Tai gali užtrukti kelias minutes...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Prisijungiama prie: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Nepavyko prisijungti prie visų įmanomų serverių, prašome patikrinti savo interneto jungtį ir bandykite iš naujo.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testuojamas pralaidumas: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testuojamas transliacijos koduotuvas, tai gali užtrukti minutėlę...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testuojamas įrašymo koduotuvas, tai gali užtrukti minutėlę...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Nepavyko startuodi koduotuvo\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testuojami %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Transliacijos koduotuvas\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Įrašymo koduotuvas\"\nBasic.AutoConfig.TestPage.Result.Header=\"Programa nustatė, kad šie apytikslūs nustatymai yra jums idealūs:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Kad naudoti šiuos nustatymus, paspauskite Pritaikyti Nustatymus. Kad perkonfigūruoti vedlį ir pabandyti iš naujo, paspauskite Grįžti. Kad ranka konfigūruoti nustatymus pačiam, paspauskite Atšaukti ir atsidarykite Nustatymus.\"\nBasic.AutoConfig.Info=\"Automatinės konfigūracijos vedlys nustatys geriausius nustatymus atsižiūrint į jūsų kompiuterio specifikacijas ir interneto greitį.\"\nBasic.AutoConfig.RunAnytime=\"Tai gali būti paleista bet kada per Įrankių meniu.\"\nBasic.Stats=\"Statistika\"\nBasic.Stats.CPUUsage=\"CPU naudojimas\"\nBasic.Stats.HDDSpaceAvailable=\"Laisva disko vieta\"\nBasic.Stats.MemoryUsage=\"Atminties naudojimas\"\nBasic.Stats.AverageTimeToRender=\"Vidutinis kadro atvaizdavimo laikas\"\nBasic.Stats.SkippedFrames=\"Praleisti kadrai dėl kodavimo vėlavimo\"\nBasic.Stats.MissedFrames=\"Kadrai praleisti dėl perteikimo strigimo.\"\nBasic.Stats.Output.Stream=\"Transliacija\"\nBasic.Stats.Output.Recording=\"Įrašymas\"\nBasic.Stats.Status=\"Statusas\"\nBasic.Stats.Status.Recording=\"Įrašoma\"\nBasic.Stats.Status.Live=\"TIESIOGIAI\"\nBasic.Stats.Status.Reconnecting=\"Persijungiama\"\nBasic.Stats.Status.Inactive=\"Neaktyvus\"\nBasic.Stats.Status.Active=\"Aktyvus\"\nBasic.Stats.DroppedFrames=\"Atmesti kadrai (tinklas)\"\nBasic.Stats.MegabytesSent=\"Bendras perduotų duomenų kiekis\"\nBasic.Stats.Bitrate=\"Bitų sparta\"\nBasic.Stats.DiskFullIn=\"Disko užpildymas už (apytiksliai)\"\nBasic.Stats.ResetStats=\"Iš naujo nustatyti statistiką\"\nResetUIWarning.Title=\"Ar tikrai norite iš naujo nustatyti vartotojo sąsają?\"\nUpdater.Title=\"Galimas atnaujinimas\"\nUpdater.Text=\"Yra galimas naujas atnaujinimas:\"\nUpdater.UpdateNow=\"Atnaujinti dabar\"\nUpdater.RemindMeLater=\"Priminti vėliau\"\nUpdater.Skip=\"Praleisti versiją\"\nUpdater.NoUpdatesAvailable.Title=\"Nėra naujų atnaujinimų\"\nUpdater.NoUpdatesAvailable.Text=\"Nėra galimų atnaujinimų\"\nUpdater.BranchNotFound.Title=\"Atnaujinimų Kanalas Pašalintas\"\nQuickTransitions.DuplicateScene=\"Dubliuoti Sceną\"\nQuickTransitions.EditProperties=\"Dubliuoti šaltinius\"\nQuickTransitions.HotkeyName=\"Greitasis perėjimas: %1\"\nBasic.AddTransition=\"Pridėti konfigūruojamą perėjimą\"\nBasic.RemoveTransition=\"Pašalinti konfigūruojamą perėjimą\"\nBasic.TransitionProperties=\"Perėjimo savybės\"\nBasic.SceneTransitions=\"Scenos perėjimai\"\nBasic.TransitionDuration=\"Trukmė\"\nBasic.TogglePreviewProgramMode=\"Studijos režimas\"\nUndo.Add=\"Pridėti %1'\"\nUndo.Delete=\"Ištrinti '%1'\"\nUndo.Rename=\"Pervadinti '%1'\"\nUndo.SceneCollection.Switch=\"Persijungti į '%1'\"\nUndo.Item.Undo=\"Anuliuoti %1\"\nUndo.Item.Redo=\"Perdaryti %1\"\nUndo.Sources.Multi=\"Ištrinti %1 Šaltinius\"\nUndo.Filters=\"Filtro Pakeitimus ant '%1'\"\nUndo.Filters.Paste.Single=\"Įklijuoti Filtrą '%1' į '%2'\"\nUndo.Volume.Change=\"Garso Pakeitimą ant '%1'\"\nUndo.Volume.Mute=\"Užtildyti '%1'\"\nUndo.Volume.Unmute=\"Atitildyti '%1'\"\nUndo.Balance.Change=\"Audio Balanso Pakeitimą ant '%1'\"\nUndo.SyncOffset.Change=\"Garso Sinchronizacijos Poslinkio Pakeitimus ant '%1'\"\nUndo.MonitoringType.Change=\"Garso Stebėjimo Pakeitimus ant '%1'\"\nUndo.Mixers.Change=\"Garso Mikserio Pakeitimus ant '%1'\"\nTransitionNameDlg.Text=\"Prašome įvesti perėjimo pavadinimą\"\nTransitionNameDlg.Title=\"Perėjimo pavadinimas\"\nTitleBar.Profile=\"Profilis\"\nTitleBar.Scenes=\"Scenos\"\nNameExists.Title=\"Pavadinimas jau egzistuoja\"\nNameExists.Text=\"Toks pavadinimas jau naudojamas.\"\nNoNameEntered.Title=\"Prašome įvesti tinkamą pavadinimą\"\nNoNameEntered.Text=\"Negalite naudoti tuščio pavadinimo.\"\nConfirmStart.Title=\"Pradėti transliaciją?\"\nConfirmStart.Text=\"Ar esate įsitikinęs, kad norite pradėti transliavimą?\"\nConfirmStop.Title=\"Stabdyti transliaciją?\"\nConfirmStop.Text=\"Ar esate įsitikinęs, kad norite stabdyti transliavimą?\"\nConfirmRemove.Title=\"Pašalinimo patvirtinimas\"\nOutput.StartStreamFailed=\"Nepavyko pradėti transliuoti\"\nOutput.StartRecordingFailed=\"Nepavyko pradėti įrašyti\"\nOutput.ConnectFail.Title=\"Nepavyko prisijungti\"\nOutput.ConnectFail.BadPath=\"Neteisingas kelias arba jungimosi URL. Prašome patikrinti nustatymus ir įsitikinti, kad jie teisingi.\"\nOutput.ConnectFail.ConnectFailed=\"Nepavyko prisijungti prie serverio\"\nOutput.ConnectFail.Error=\"Netikėta klaida bandant jungtis į serverį. Daugiau informacijos žurnalo faile.\"\nOutput.ConnectFail.Disconnected=\"Atjungtas nuo serverio.\"\nOutput.StreamEncodeError.Title=\"Kodavimo klaida\"\nOutput.StreamEncodeError.Msg=\"Įvyko koduotojo klaida betransliuojant.\"\nOutput.RecordFail.Title=\"Nepavyko pradėti įrašo\"\nOutput.RecordFail.Unsupported=\"Išvesties formatas nepalaikomas arba nepalaiko daugiau vieno garso takelio. Prašome patikrinti nustatymus ir bandyti vėl.\"\nOutput.RecordNoSpace.Title=\"Nepakanka vietos diske\"\nOutput.RecordNoSpace.Msg=\"Nėra pakankamai vietos diske, kad tęsti įrašymą.\"\nOutput.RecordError.Title=\"Įrašymo klaida\"\nOutput.RecordError.Msg=\"Nenustatyta klaida įrašant.\"\nOutput.RecordError.EncodeErrorMsg=\"Įvyko koduotojo klaida įrašinėjant.\"\nOutput.BadPath.Title=\"Blogas failo kelias\"\nOutput.NoBroadcast.Title=\"Nekonfigūruotos Jokios Transliacijos\"\nOutput.BroadcastStartFailed=\"Nepavyko pradėti transliacijos\"\nOutput.BroadcastStopFailed=\"Nepavyko užbaigti transliacijos\"\nRemux.SourceFile=\"OBS įrašas\"\nRemux.TargetFile=\"Paskirties failas\"\nRemux.Remux=\"Permiksuoti\"\nRemux.OBSRecording=\"OBS įrašas\"\nRemux.FinishedTitle=\"Permiksavimas baigtas\"\nRemux.Finished=\"Įrašas permiksuotas\"\nRemux.FinishedError=\"Įrašas permiksuotas, tačiau failas gali būti neužbaigtas\"\nRemux.SelectRecording=\"Pasirinkti OBS įrašą...\"\nRemux.ExitUnfinishedTitle=\"Vyksta permiksavimimas\"\nRemux.ExitUnfinished=\"Permiksavimas nebaigtas. Sustabdžius dabar, paskirites failas gali būti nepanaudojamas.\\nAr esate tikras, kad norite sustabdyti permiksavimą?\"\nBasic.DesktopDevice1=\"Darbastalio garsas\"\nBasic.DesktopDevice2=\"Darbastalio garsas 2\"\nBasic.Scene=\"Scena\"\nBasic.DisplayCapture=\"Ekrano perėmimas\"\nBasic.Main.PreviewConextMenu.Enable=\"Įjungti peržiūrą\"\nScaleFiltering=\"Mastelio filtras\"\nScaleFiltering.Point=\"Taškas\"\nScaleFiltering.Bilinear=\"Dvilinijinis\"\nScaleFiltering.Bicubic=\"Bikubinis\"\nDeinterlacing=\"Perėjimo šalinimas\"\nDeinterlacing.Discard=\"Atmesti\"\nDeinterlacing.TopFieldFirst=\"Piršutinis puskadris pirmas\"\nDeinterlacing.BottomFieldFirst=\"Apatinis puskadris pirmas\"\nBasic.Main.AddSceneDlg.Title=\"Pridėti sceną\"\nBasic.Main.AddSceneDlg.Text=\"Įveskite pasirinktą scenos pavadinimą\"\nBasic.Main.DefaultSceneName.Text=\"Scena %1\"\nBasic.Main.AddSceneCollection.Title=\"Pridėti scenų rinkinį\"\nBasic.Main.AddSceneCollection.Text=\"Įveskite pasirinktą scenų rinkinio pavadinimą\"\nBasic.Main.RenameSceneCollection.Title=\"Pervardinti scenų rinkinį\"\nAddProfile.Title=\"Pridėti profilį\"\nAddProfile.Text=\"Įveskite pasirinktą profilio pavadinimą\"\nRenameProfile.Title=\"Pervardinti profilį\"\nBasic.Main.PreviewDisabled=\"Peržiūra šiuo metu išjungta\"\nBasic.SourceSelect=\"Sukurti/Pasirinkti šaltinį\"\nBasic.SourceSelect.CreateNew=\"Sukurti naują\"\nBasic.SourceSelect.AddExisting=\"Pridėti esamą\"\nBasic.SourceSelect.AddVisible=\"Padaryti šaltinį matomu\"\nBasic.PropertiesWindow=\"'%1' savybės\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (automatinis pasirinkimas: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Pasirinkti spalvą\"\nBasic.PropertiesWindow.SelectFont=\"Pasirinkti šriftą\"\nBasic.PropertiesWindow.ConfirmTitle=\"Nustatymai pakeisti\"\nBasic.PropertiesWindow.Confirm=\"Yra neišsaugotų pakeitimų. Ar norite juos išlaikyti?\"\nBasic.PropertiesWindow.NoProperties=\"Savybių nėra\"\nBasic.PropertiesWindow.AddFiles=\"Pridėti failus\"\nBasic.PropertiesWindow.AddDir=\"Pridėti katalogą\"\nBasic.PropertiesWindow.AddURL=\"Pridėti kelią/URL\"\nBasic.Filters=\"Filtrai\"\nBasic.Filters.AsyncFilters=\"Audio/Video filtrai\"\nBasic.Filters.AudioFilters=\"Audio filtrai\"\nBasic.Filters.EffectFilters=\"Efektų filtrai\"\nBasic.TransformWindow.Position=\"Padėtis\"\nBasic.TransformWindow.Rotation=\"Pasukimas\"\nBasic.TransformWindow.Size=\"Dydis\"\nBasic.TransformWindow.Alignment.TopLeft=\"Viršuje kairėje\"\nBasic.TransformWindow.Alignment.TopCenter=\"Viršuje per vidurį\"\nBasic.TransformWindow.Alignment.TopRight=\"Viršuje dešinėje\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Centruota kairėje\"\nBasic.TransformWindow.Alignment.Center=\"Centre\"\nBasic.TransformWindow.Alignment.CenterRight=\"Centruoda dešinįje\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Apačioje kairėje\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Apačioje per vidurį\"\nBasic.TransformWindow.Alignment.BottomRight=\"Apačioje dešinėje\"\nBasic.Main.AddSourceHelp.Text=\"Turite turėti bent 1 sceną, kad galėtumėte pridėti šaltinį.\"\nBasic.Main.Scenes=\"Scenos\"\nBasic.Main.Sources=\"Šaltiniai\"\nBasic.Main.Connecting=\"Jungiamasi...\"\nBasic.Main.StartRecording=\"Pradėti įrašymą\"\nBasic.Main.StartStreaming=\"Pradėti transliavimą\"\nBasic.Main.StopRecording=\"Stabdyti įrašymą\"\nBasic.Main.StopStreaming=\"Stabdyti transliavimą\"\nBasic.Main.ForceStopStreaming=\"Stabdyti transliavimą (atmesti vėlavimą)\"\nBasic.MainMenu.File=\"&Failas\"\nBasic.MainMenu.File.Export=\"&Eksportuoti\"\nBasic.MainMenu.File.Import=\"&Importuoti\"\nBasic.MainMenu.File.ShowRecordings=\"Pa&rodyti Įrašus\"\nBasic.MainMenu.File.Remux=\"Per&miksuoti įrašus\"\nBasic.MainMenu.File.Settings=\"Nu&statymai\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Parodyti nustatymų katalogą\"\nBasic.MainMenu.File.ShowProfileFolder=\"Parodyti profilių katalogą\"\nBasic.MainMenu.File.Exit=\"Išeiti (&X)\"\nBasic.MainMenu.Edit=\"R&edaguoti\"\nBasic.MainMenu.Edit.Undo=\"Atša&ukti\"\nBasic.MainMenu.Edit.Redo=\"G&ražinti atšaukimą\"\nBasic.MainMenu.Edit.Transform=\"&Transformuoti\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"R&edaguoti transformaciją...\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Atšaukti t&ransformaciją\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Pasukti 90 laipsnių pagal LL\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Pasukti 90 laipsnių prieš LL\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Pasukti 180 laipsnių\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Apversti &horizontaliai\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Apversti &vertikaliai\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Sutalpinti į ekraną (&F)\"\nBasic.MainMenu.Edit.Order=\"Rikiavimas (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Perkelti a&ukštyn\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Perkelti žemyn (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Perkel&ti į viršų\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Perkelti į apačią (&B)\"\nBasic.MainMenu.View=\"&Rodymas\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Didžiausia video bitų sparta: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Didžiausia garso bitų sparta: %1 kbps\"\nBasic.Settings.Output.DynamicBitrate=\"Dinamiškai keisti bitų spartą, kad valdytumėte perkrovą\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dinamiškai keisti bitų spartą, kad valdytumėte perkrovą (Beta)\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Aukštas\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Virš vidutionio\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Vidutinis\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Žemiau vidutinio\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Tolygus\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Spalvos formatas\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-bitų)\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Pilnas\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Numatytas\"\nBasic.Settings.Advanced.StreamDelay=\"Srauto Vėlavimas\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Išsaugoti apkarpymo tašką (padidina vėlavimą) įrašinėjant\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Numatomas atminties naudojimas: %1 MB\"\nBasic.Settings.Advanced.Network=\"Tinklas\"\nBasic.Settings.Advanced.Network.BindToIP=\"Priskirti prie IP\"\nBasic.AdvAudio.Name=\"Prietaisas\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtruoti pagal mygtukus\"\nHotkeys.Print=\"Spausdinti\"\nHotkeys.Menu=\"Meniu\"\nHotkeys.Space=\"Tarpas\"\nMute=\"Užtildyti\"\nUnmute=\"Atitildyti\"\nPush-to-mute=\"Nutildyti paspaudus\"\nPush-to-talk=\"Kalbėti paspaudus\"\nFinalScene.Title=\"Ištrinti sceną\"\nFinalScene.Text=\"Turi būti bent viena scena.\"\nResizeOutputSizeOfSource.Continue=\"Ar jūs norite tęsti?\"\n"
  },
  {
    "path": "frontend/data/locale/lv-LV.ini",
    "content": "Language=\"Latviešu\"\nApply=\"Piemērot\"\nCancel=\"Atcelt\"\nClose=\"Aizvērt\"\nSave=\"Saglabāt\"\nDiscard=\"Atcelt\"\nDisable=\"Atspējot\"\nYes=\"Jā\"\nNo=\"Nē\"\nAdd=\"Pievienot\"\nRemove=\"Noņemt\"\nRename=\"Pārdēvēt\"\nInteract=\"Mijiedarboties\"\nFilters=\"FIltri\"\nProperties=\"Rekvizīti\"\nMoveUp=\"Pārvietot augšup\"\nMoveDown=\"Pārvietot lejup\"\nSettings=\"Iestatījumi\"\nDisplay=\"Parādīt/atainot\"\nName=\"Nosaukums\"\nExit=\"Iziet\"\nMixer=\"Audio mikseris\"\nBrowse=\"Pārlūkot\"\nDroppedFrames=\"Izkritušie/zudušie kadri %1 (%2%)\"\nProjector.Open.Program=\"Atvērt Programmas Projektoru\"\nProjector.Open.Preview=\"Atvērt Priekšskatījuma Projektoru\"\nProjector.Open.Scene=\"Atvērt Ainas Projektoru\"\nProjector.Open.Source=\"Atvērt Avota Projektoru\"\nProjector.Open.Multiview=\"Atvērt Multiskatu\"\nProjector.Display=\"Ekrāns: %1\"\nProjector.Window=\"Jauns logs\"\nProjector.Title=\"Projektors\"\nProjector.Title.Scene=\"Aina: %1\"\nProjector.Title.Source=\"Avots: %1\"\nProjector.Title.Multiview=\"Multiskats\"\nProjector.ResizeWindowToContent=\"Pielāgot logu saturam\"\nClear=\"Attīrīt\"\nRevert=\"Atgriezt atpakaļ\"\nShow=\"Rādīt\"\nHide=\"Nerādīt\"\nUnhideAll=\"Rādīt visus\"\nUntitled=\"Beznosaukuma\"\nNew=\"Jauns\"\nDuplicate=\"Dublikāts\"\nEnable=\"Iespējot\"\nDisableOSXVSync=\"Atspējot macOSa V-Sync\"\nResetOSXVSyncOnExit=\"Atstatīt macOSa V-Sync izejot\"\nHighResourceUsage=\"Enkoderis pārslogots! Apsveriet iespēju izmantot 'vieglākus' video iestatījumus vai izmantot ātrāku kodēšanas iestatījumu.\"\nTransition=\"Pāreja\"\nQuickTransitions=\"Ātrās pārejas\"\nFadeToBlack=\"Izgaist melnā\"\nLeft=\"kreisais\"\nRight=\"labais\"\nTop=\"augšējais\"\nBottom=\"apakšējais\"\nReset=\"Atstatīt/atcelt\"\nHours=\"stundas\"\nMinutes=\"minūtes\"\nSeconds=\"sekundes\"\nDeprecated=\"Neieteiktie\"\nReplayBuffer=\"Replay bufferis\"\nImport=\"Importēt\"\nExport=\"Eksportēt\"\nCopy=\"Kopēt\"\nPaste=\"Ievietot\"\nPasteReference=\"Ievietot (atsauci)\"\nPasteDuplicate=\"Ievietot (dublikātu)\"\nRemuxRecordings=\"Remux-ēt ierakstus\"\nNext=\"Uz priekšu\"\nBack=\"Atpakaļ\"\nDefaults=\"Noklusējumi\"\nRestoreDefaults=\"Noklusējumi\"\nHideMixer=\"Nerādīt mikserī\"\nTransitionOverride=\"Pārejas ignorēšana (override)\"\nShowTransition=\"Parādīt pāreju\"\nHideTransition=\"Nerādīt pāreju\"\nNone=\"Nekas\"\nStudioMode.Preview=\"Priekšskats\"\nStudioMode.Program=\"Programma\"\nStudioMode.PreviewSceneName=\"Priekšskats: %1\"\nStudioMode.ProgramSceneName=\"Programma: %1\"\nShowInMultiview=\"Rādīt multiskatā\"\nVerticalLayout=\"Vertikāls izkārtojums\"\nGroup=\"Grupa\"\nDoNotShowAgain=\"Vairs nerādīt\"\nDefault=\"(noklusējuma)\"\nCalculating=\"Tiek aprēķināts...\"\nFullscreen=\"Pilnekrānā\"\nWindowed=\"Logā\"\nRefreshBrowser=\"Atsvaidzināt\"\nAspectRatio=\"Proporcija <b>%1:%2</b>\"\nLockVolume=\"Bloķēt skaļumu\"\nLogViewer=\"Žurnālu skatītājs\"\nShowOnStartup=\"Rādīt palaišanas laikā\"\nOpenFile=\"Atvērt failu\"\nAddScene=\"Pievienot Ainu\"\nAddSource=\"Pievienot avotu\"\nRemoveScene=\"Dzēst izvēlēto ainu\"\nRemoveSource=\"Dzēst izvēlēto(-ās) ainu(-as)\"\nMoveSceneUp=\"Pārvietot ainu uz augšu\"\nMoveSceneDown=\"Pārvietot ainu uz leju\"\nMoveSourceUp=\"Pārvietot avotu(-us) uz augšu\"\nMoveSourceDown=\"Pārvietot avotu(-us) uz leju\"\nSourceProperties=\"Atvērt satura rekvizīti\"\nSourceFilters=\"Atvērt satura filtrus\"\nMixerToolbarMenu=\"Skaļuma pulta izvēlne\"\nSceneFilters=\"Atvērt ainas filtrus\"\nList=\"Saraksts\"\nGrid=\"Režģis\"\nAutomatic=\"Automātiski\"\nComingSoon=\"Drīzumā\"\nPluginsFailedToLoad.Title=\"Spraudņu ielādes kļūda\"\nPluginsFailedToLoad.Text=\"Sekojošie OBS spraudņi neizdevās ielādēt:\\n\\n%1\\nLūdzu, atjauniniet vai noņemiet šos spraudņus.\"\nAlreadyRunning.Title=\"OBS jau tiek palaists\"\nAlreadyRunning.Text=\"OBS jau darbojas! Ja nevēlaties vairākas paralēli darbojošās OBS kopijas, lūdzu, aiztaisiet/izslēdziet pašreiz darbojošās. Ja jums iestatīts, ka OBS tiek minimizēts sistēmas teknē, lūdzu, pārbaudiet, vai tas tur joprojām darbojas.\"\nAlreadyRunning.LaunchAnyway=\"Palaist jebkurā gadījumā\"\nCrashHandling.Dialog.Title=\"Konstatēta OBS Studio Avārija\"\nCrashHandling.Labels.Text=\"OBS Studio neizslēdzās pareizi.\\n\\nVai palaist Drošajā Režīmā (trešo pušu spraudņi, skriptēšana un WebSockets ir atspējoti)?\"\nCrashHandling.Labels.PrivacyNotice=\"Jūs varat arī izvēlēties automātiski augšupielādēt jaunāko avārijas ziņojumu pakalpojumā OBSProject.<br /><br />Lūdzu, izlasiet <a href='https://obsproject.com/privacy-policy'>konfidencialitātes politiku</a> pirms jebkuru failu augšupielādes un pievērsiet īpašu uzmanību sadaļām par failu augšupielādi.\"\nCrashHandling.Checkbox.SendReport=\"Esmu izlasījis privātuma politiku un piekrītu augšupielādei.\"\nCrashHandling.Buttons.LaunchSafe=\"Palaist Drošajā Režīmā\"\nCrashHandling.Buttons.LaunchNormal=\"Palaist Normālā Režīmā\"\nCrashHandling.Errors.UploadJSONError=\"Mēģinot augšupielādēt jaunāko avārijas žurnālu, radās kļūda. Lūdzu, mēģiniet vēlreiz vēlāk.\"\nCrashHandling.Errors.Title=\"Avārijas žurnāla Augšupielādes Kļūda\"\nSafeMode.Restart=\"Vai vēlies restartēt OBS Drošajā Režīmā (trešo pušu spraudņi, skripti un WebSockets atspējoti)?\"\nSafeMode.RestartNormal=\"Vai vēlies restartēt OBS Normālajā Režīmā?\"\nChromeOS.Title=\"Neatbalstīta operētājsistēma\"\nChromeOS.Text=\"Šķiet, ka OBS darbojas ChromeOS konteinerā. Šī platforma netiek atbalstīta.\"\nWine.Title=\"Ir atklāts Wine\"\nWine.Text=\"OBS darbināšana Wine nav atbalstīta, un daudzas iespējas, piemēram, ierakstīšana vai ierīču avoti nestrādās vai strādās ierobežotā apjomā.<br><br>Ieteicams palaist OBS dzimto versiju, piemēram <a href='https://flathub.org/apps/details/com.obsproject.Studio'>mūsu Flatpak versiju</a> vai tavas operētājsistēmas pakotnes.\"\nDockCloseWarning.Title=\"Dokojamā loga aizvēršana\"\nDockCloseWarning.Text=\"Jūs tikko aizvērāt dokojamu logu. Ja vēlaties to darīt redzamu, izmantojiet izvēlnes joslas izvēlni Skats → Doki.\"\nExtraBrowsers=\"Pielāgoti pārlūka doki\"\nExtraBrowsers.Info=\"Pievienojiet dokus, piešķirot tiem nosaukumu un URL. Pēc tam noklikšķiniet uz Pielietot vai Aizvērt, lai atvērtu dokus. Dokus var pievienot vai noņemt jebkurā laikā.\"\nExtraBrowsers.DockName=\"Doka nosaukums\"\nAuth.Authing.Title=\"Autentificē...\"\nAuth.Authing.Text=\"Autentificē ar %1, lūdzu gaidiet...\"\nAuth.AuthFailure.Title=\"Autentifikācijas ķibele\"\nAuth.AuthFailure.Text=\"Neizdevās autentificēt ar %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Nepieciešama autentifikācija\"\nAuth.InvalidScope.Text=\"%1 autentifikācijas prasības ir mainījušās. Dažas funkcijas var nebūt pieejamas.\"\nAuth.LoadingChannel.Title=\"Ielādējas kanāla informācija...\"\nAuth.LoadingChannel.Text=\"Notiek kanāla informācijas ielāde %1, lūdzu, uzgaidiet...\"\nAuth.LoadingChannel.Error=\"Neizdevās iegūt kanāla informāciju.\"\nAuth.ChannelFailure.Title=\"Neizdevās ielādēt kanālu\"\nAuth.ChannelFailure.Text=\"Neizdevās ielādēt kanāla informāciju priekš %1\\n\\n%2: %3\"\nAuth.Chat=\"Tērziņš/klača\"\nAuth.StreamInfo=\"Straumes informācija\"\nTwitchAuth.Stats=\"Twitch statistika\"\nTwitchAuth.Feed=\"Twitch darbību plūsma\"\nTwitchAuth.TwoFactorFail.Title=\"Nevarēja iegūt straumes atslēgu\"\nTwitchAuth.TwoFactorFail.Text=\"OBS nespēja izveidot savienojumu ar jūsu Twitch kontu. Lūdzu, pārliecinieties, ka jūsu <a href='https://www.twitch.tv/settings/security'> Twitch drošības iestatījumi (security settings) </a> ir iestatīta 2 faktoru autentifikācija (2FA), jo tas ir obligāts priekšnosacījums straumēšanai.\"\nRestreamAuth.Channels=\"PārStraumēt kanālus\"\nCopy.Filters=\"Kopēt filtrus\"\nPaste.Filters=\"Ievietot filtrus\"\nBrowserPanelInit.Title=\"Tiek inicializēts pārlūks...\"\nBrowserPanelInit.Text=\"Tiek inicializēts pārlūks, lūdzu uzgaidiet...\"\nBandwidthTest.Region=\"Reģions\"\nBandwidthTest.Region.US=\"Amerikas apvienotās valstis\"\nBandwidthTest.Region.EU=\"Eiropa\"\nBandwidthTest.Region.Asia=\"Āzija\"\nBandwidthTest.Region.Other=\"Cits\"\nBasic.AutoConfig=\"Auto-konfigurācijas pavedējs/vednis\"\nBasic.AutoConfig.ApplySettings=\"Pielietot iestatījumus\"\nBasic.AutoConfig.StartPage=\"Lietojuma informācija\"\nBasic.AutoConfig.StartPage.SubTitle=\"Norādiet, kādiem nolūkiem vēlaties izmantot programmu\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimizēt straumēšanai, ierakstīšana ir sekundāra\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimizēt tikai ierakstīšanai, es nestraumēšu\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Es izmantošu tikai virtuālo kameru\"\nBasic.AutoConfig.VideoPage=\"Video iestatījumi\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Iestatiet vēlamos video iestatījumus\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Izmantot pašreizējo (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Ekrāns %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Izmantot pašreizējo (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 vai 30, bet, ja iespējams, priekšroka 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 vai 30, bet priekšroka augstākai izšķirtspējai\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Piezīme. Kanvas (bāzes) izšķirtspēja ne vienmēr ir tāda pati kā izšķirtspēja, ar kuru straumēsiet vai ierakstīsiet. Jūsu faktiskā straumes/ieraksta izšķirtspēja var tikt samazināta no kanvas izšķirtspējas, lai samazinātu resursu izmantošanu vai nodrošinātu \\\"bitrate\\\" prasības (pārraides ātrumu?).\"\nBasic.AutoConfig.StreamPage=\"Straumes informācija\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Lūdzu ievadiet savu straumes informāciju\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Piesaistīt kontu (ieteicams)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Atsaistīt kontu\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Atsaistīt kontu?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Šīs izmaiņas stāsies spēkā nekavējoties. Tiešām vēlaties atsaistīt savu kontu?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Iegūt straumes atslēgu\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Vairāk info\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Izmantot straumēšanas atslēgu\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Izmantot straumēšanas atslēgu (advancēts)\"\nBasic.AutoConfig.StreamPage.Service=\"Pakalpojums\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Rādīt visus\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Pielāgots/individuāls\"\nBasic.AutoConfig.StreamPage.Server=\"Serveris\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Straumes atslēga\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: ievadiet šifrēšanas paroli.\\nRTMP: ievadiet atslēgu, ko norādījis pakalpojums.\\nSRT: ievadiet streamid, ja pakalpojums tādu lieto.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Enkodera atslēga\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Nesēja Žetons\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Piesaistītais konts\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Notestēt bitrate (bitu pārraides ātrumu) ar interneta ātruma testu (var aizņemt dažas minūtes)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Prioritāte aparatūras enkodēšanai\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Aparatūriskā kodēšana mazāk noslogo CPU, bet var pieprasīt lielāku bitreitu, lai iegūtu tādu pašu kvalitātes līmeni.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Straumēnu brīdinājums\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Interneta ātruma tests gatavojas straumēt pēc nejaušības principa izvēlētus video datus bez audio uz jūsu kanālu. Ja jums ir iespēja, iesakām īslaicīgi izslēgt video straumju saglabāšanu un iestatīt straumēšanu kā privātu līdz testa pabeigšanas. Turpināt?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Tests %1\"\nBasic.AutoConfig.TestPage=\"Rezultāti\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Programma pašreiz veic testu kopumu, lai piemeklētu ideālākos iestatījumus\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Tests pabeigts\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Tiek veikta interneta pieslēguma ātruma pārbaude, tas var aizņemt dažas minūtes ...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Šī pakalpojuma protokolam netika atrasta izvade.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Savienojums ar: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Neizdevās izveidot savienojumu ar serveriem. Lūdzu, pārbaudiet interneta pieslēgumu un mēģiniet vēlreiz.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Ātruma tests savienojumam ar: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Notiek straumes enkodera testēšana, tas var aizņemt minūti ...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Notiek ierakstīšanas enkodera testēšana, tas var aizņemt minūti ...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Neizdevās palaist enkoderi\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Tiek testēts %1x%2 ar %3 kadriem sekundē...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Straumēšanas enkoders\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Ieraksta enkoders\"\nBasic.AutoConfig.TestPage.Result.Header=\"Programma ir noteikusi, ka šai sistēmai vislabāk piemērotie ir šādi iestatījumi:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Lai saglabātu šos iestatījumus, noklikšķiniet uz Pielietot iestatījumus. Lai pārkonfigurētu pavedēju un mēģinātu vēlreiz, noklikšķiniet uz Atpakaļ. Lai manuāli konfigurētu iestatījumus, noklikšķiniet uz Atcelt un atveriet Iestatījumi.\"\nBasic.AutoConfig.Info=\"Automātiskās konfigurācijas pavedējs noteiks labākos iestatījumus, pamatojoties uz datora specifikāciju un interneta ātrumu.\"\nBasic.AutoConfig.RunAnytime=\"To var palaist jebkurā laikā, dodoties uz izvēlni Rīki.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Straumēšanas (Mērogota) Izšķirtspēja\"\nBasic.Stats=\"Statistika\"\nBasic.Stats.CPUUsage=\"CPU lietojums\"\nBasic.Stats.HDDSpaceAvailable=\"Diska vieta (pieejamā)\"\nBasic.Stats.MemoryUsage=\"RAM (operatīvās atmiņas) lietojums\"\nBasic.Stats.AverageTimeToRender=\"Vidējais kadra renderēšanas laiks\"\nBasic.Stats.SkippedFrames=\"Izlaisti kadri kavētas/novilcinātas enkodēšanas dēļ\"\nBasic.Stats.MissedFrames=\"Izlaisti kadri kavētas/novilcinātas renderēšanas dēļ\"\nBasic.Stats.Output.Stream=\"Straume\"\nBasic.Stats.Output.Recording=\"Ieraksts\"\nBasic.Stats.Status=\"Statuss\"\nBasic.Stats.Status.Recording=\"Ieraksta\"\nBasic.Stats.Status.Live=\"Ēterā (dzīvajā)\"\nBasic.Stats.Status.Reconnecting=\"Atkārtoti savienojas\"\nBasic.Stats.Status.Inactive=\"Neaktīvs\"\nBasic.Stats.Status.Active=\"Aktīvs\"\nBasic.Stats.DroppedFrames=\"Izlaistie/izkritušie kadri (tīkls)\"\nBasic.Stats.MegabytesSent=\"Kopējā datu izvade\"\nBasic.Stats.Bitrate=\"Bitrate (bitu pārraides ātrums)\"\nBasic.Stats.DiskFullIn=\"Disks pilns\"\nBasic.Stats.DiskFullIn.Text=\"%1 Stundas, %2 Minūtes\"\nBasic.Stats.ResetStats=\"Atstatīt (nonullēt) statistiku\"\nResetUIWarning.Title=\"Tiešām vēlaties atstatīt lietotāja saskarni (GUI)?\"\nResetUIWarning.Text=\"Atstatot lietotāja saskarni, kļūs neredzami papildu doki. Ja tos vēlēsieties, tie būs Skata izvēlnē jāpadara redzami. \\n\\nTiešām vēlaties atstatīt lietotāja saskarni?\"\nUpdater.Title=\"Pieejama programmas aktualizācija (update)\"\nUpdater.Text=\"Ir pieejams jauns programmas aktualizējums (update):\"\nUpdater.UpdateNow=\"Aktualizēt tagad\"\nUpdater.RemindMeLater=\"Atgādināt vēlāk\"\nUpdater.Skip=\"Izlaist šo versiju\"\nUpdater.NoUpdatesAvailable.Title=\"Aktualizācijas (updates) nav pieejamas\"\nUpdater.NoUpdatesAvailable.Text=\"Aktualizācijas (updates) pašreiz nav pieejamas\"\nUpdater.BranchNotFound.Title=\"Atjauninājumu Kanāls Noņemts\"\nUpdater.BranchNotFound.Text=\"Tevis izvēlētais atjauninājumu kanāls vairs nav pieejams, OBS ir atiestatīts uz noklusējumu.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Integritātes pārbaude nav pieejama\"\nUpdater.RepairButUpdatesAvailable.Text=\"Datņu integritātes pārbaude ir pieejama tikai jaunākajai versijai. Ej uz Palīdzība → Meklēt atjauninājumus, lai verificētu un atjauninātu tavu OBS instalāciju.\"\nUpdater.RepairConfirm.Title=\"Apstiprināt integritātes pārbaudi\"\nUpdater.RepairConfirm.Text=\"Integritātes pārbaude skenēs tavu OBS instalāciju, lai atrastu bojājumus un atkārtoti lejuplādētu salauztas/mainītas datnes. Šis process var aizņemt kādu laiku.\\n\\nVai vēlies turpināt?\"\nUpdater.FailedToLaunch=\"Neizdevās palaist aktualizatoru\"\nQuickTransitions.SwapScenes=\"Samainiet vietām Priekšskata/Izvada Ainas pēc Pārejas\"\nQuickTransitions.SwapScenesTT=\"Pēc pārejas maina vietām priekšskata un izvadu ainas (ja izvada sākotnējā aina joprojām pastāv).\\nTas neatgriezīs visas izmaiņas, kas varētu būt veiktas izvada sākotnējā ainā.\"\nQuickTransitions.DuplicateScene=\"Dublicēt/dublēt ainu\"\nQuickTransitions.DuplicateSceneTT=\"Kad tiek rediģēta to pati aina, var rediģēt avotu transformāciju/redzamību, nemainot izvadu.\\nLai rediģētu avotu rekvizītus, nemainot izvadu, iespējojiet 'Dublēt avotus'.\\nMainot šo vērtību, tiks atstatīta pašreizējā izvada aina (ja tā joprojām pastāv).\"\nQuickTransitions.EditProperties=\"Dublēt avotus\"\nQuickTransitions.EditPropertiesTT=\"Rediģējot vienu un to pašu ainu, tiek ļauts rediģēt avotu rekvizītus, nemainot izvadu.\\nTo var izmantot tikai tad, ja ir iespējota opcija Dublēt ainu.\\nDaži avoti (piemēram, tveršanas vai mediju avoti) to neatbalsta, un tos nevar rediģēt atsevišķi.\\nMainot šo vērtību, tiks atstatīta pašreizējā izvades aina (ja tā joprojām pastāv).\\n\\nBrīdinājums: tā kā avoti tiks dublēti, tas var prasīt papildu sistēmas vai video resursus.\"\nQuickTransitions.HotkeyName=\"Ātra pāreja: %1\"\nBasic.AddTransition=\"Pielikt konfigurējamu pāreju\"\nBasic.RemoveTransition=\"Noņemt konfigurējamo pāreju\"\nBasic.TransitionProperties=\"Pārejas rekvizīti\"\nBasic.SceneTransitions=\"Ainas pārejas\"\nBasic.TransitionDuration=\"Ilgums\"\nBasic.TogglePreviewProgramMode=\"Studio režīms\"\nBasic.EnablePreviewProgramMode=\"Iespējot studijas režīmu\"\nBasic.DisablePreviewProgramMode=\"Atspējot studijas režīmu\"\nUndo.Undo=\"Atgriezt\"\nUndo.Redo=\"Atatgriezt (redo)\"\nUndo.Add=\"Pielikt '%1'\"\nUndo.Delete=\"Dzēst '%1'\"\nUndo.Rename=\"Pārdēvēt '%1'\"\nUndo.SceneCollection.Switch=\"Pāriet uz '%1'\"\nUndo.Item.Undo=\"Atgriezt/atsaukt %1\"\nUndo.Item.Redo=\"Atatgriezt (redo) %1\"\nUndo.Sources.Multi=\"Dzēst %1 avotus\"\nUndo.Filters=\"Filtra izmaiņas uz '%1'\"\nUndo.Filters.Paste.Single=\"Ievietot filtru '%1' uz '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopēt filtru '%1' uz '%2'\"\nUndo.Transform=\"Pārveidot avotu(s) iekš '%1'\"\nUndo.Transform.Paste=\"Ielikt pārveidojumu iekš '%1'\"\nUndo.Transform.Rotate=\"Rotāciju/pagriešanu iekš '%1'\"\nUndo.Transform.Reset=\"Pārveidot atstatīšanu iekš '%1' '\"\nUndo.Transform.HFlip=\"Horizontālo pārmešanu iekš '%1'\"\nUndo.Transform.VFlip=\"Vertikālo pārmešanu iekš '%1'\"\nUndo.Transform.FitToScreen=\"Pielāgot ekrānam iekš '%1'\"\nUndo.Transform.StretchToScreen=\"Izstiept uz ekrāna ainā '%1'\"\nUndo.Transform.Center=\"Centrēt ekrānā ainā '%1'\"\nUndo.Transform.VCenter=\"Vertikāli centrēt ekrānā ainā '%1'\"\nUndo.Transform.HCenter=\"Vertikāli centrēt ekrānā ainā '%1'\"\nUndo.Volume.Change=\"Skaļuma maiņa '%1'\"\nUndo.Volume.Mute=\"Noklusināt '%1'\"\nUndo.Volume.Unmute=\"Atgriezt skaņu '%1'\"\nUndo.Balance.Change=\"Audio stabilitātes maiņa '%1'\"\nUndo.SyncOffset.Change=\"Audio sinhronizācijas novirzes maiņa '%1'\"\nUndo.MonitoringType.Change=\"Audio monitoringa maiņa '%1'\"\nUndo.Mixers.Change=\"Audio mikseru maiņa '%1'\"\nUndo.ForceMono.On=\"Piepiedu mono iespējošana '%1'\"\nUndo.ForceMono.Off=\"Piespiedu mono atspējošana '%1'\"\nUndo.Scene.Duplicate=\"Dubultot ainu '%1'\"\nUndo.ShowTransition=\"Rādīt pāreju '%1'\"\nUndo.HideTransition=\"Slēpt pāreju '%1'\"\nUndo.ShowSceneItem=\"Rādīt '%1' iekš '%2'\"\nUndo.HideSceneItem=\"Slēpt '%1' iekš '%2'\"\nUndo.ReorderSources=\"Pārkārtot avotus '%1'\"\nTransitionNameDlg.Text=\"Lūdzu ievadiet pārejas nosaukumu\"\nTransitionNameDlg.Title=\"Pārejas nosaukums\"\nTitleBar.SafeMode=\"DROŠAIS REŽĪMS\"\nTitleBar.PortableMode=\"Portatīvais režīms\"\nTitleBar.Profile=\"Profils\"\nTitleBar.Scenes=\"Ainas\"\nNameExists.Title=\"Nosaukums jau pastāv\"\nNameExists.Text=\"Šis nosaukums jau ir aizņemts.\"\nNoNameEntered.Title=\"Lūdzu ievadiet derīgu nosaukumu\"\nNoNameEntered.Text=\"Nosaukums ir obligāts.\"\nConfirmStart.Title=\"Sākt straumēt?\"\nConfirmStart.Text=\"Vēlaties sākt straumēt?\"\nConfirmStop.Title=\"Beigt straumēt?\"\nConfirmStop.Text=\"Vēlaties beigt straumēt?\"\nConfirmStopRecord.Title=\"Beigt ierakstu?\"\nConfirmStopRecord.Text=\"Vēlaties beigt ierakstīt?\"\nConfirmBWTest.Title=\"Sākt interneta ātruma testu?\"\nConfirmBWTest.Text=\"OBS ir konfigurēts interneta ātruma pārbaudes režīmam. Šis režīms ļauj veikt tīkla testēšanu, jūsu kanālam neejot tiešraidē. Kad būsiet pabeidzis testēšanu, jums šis režīms būs jāatspējo, lai skatītāji varētu redzēt jūsu straumi.\\n\\nVēlaties turpināt?\"\nConfirmRemove.Title=\"Dzēšanas apstiprinājums\"\nConfirmRemove.Text=\"Vai tiešām vēlies noņemt '%1'?\"\nConfirmRemove.TextMultiple=\"Tiešām vēlaties noņemt %1 gabalus?\"\nConfirmReset.Title=\"Atiestatīt rekvizītus\"\nConfirmReset.Text=\"Vai tiešām vēlies atiestatīt pašreizējās rekvizītu vērtības uz to noklusējuma vērtībām?\"\nOutput.StartStreamFailed=\"Neizdevās uzsākt straumēšanu\"\nOutput.StartRecordingFailed=\"Neizdevās uzsākt ierakstīšanu\"\nOutput.StartReplayFailed=\"Neizdevās sākt replay bufera ierakstu\"\nOutput.StartVirtualCamFailed=\"Neizdevās startēt virtuālo kameru\"\nOutput.StartFailedGeneric=\"Neizdevās sākt izvadu. Lūdzu, skatiet žurnālu, lai iegūtu sīkāku informāciju.\\n\\nPiezīme: Ja izmantojat NVENC vai AMD enkoderus, pārliecinieties, ka ir svaigi video draiveri.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Nevar saglabāt replay-us, kamēr uz pauzes\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Brīdinājums: Replay-i nevar tikt saglabāti, kad ieraksts ir pauzēts.\"\nOutput.ConnectFail.Title=\"Neizdevās savienoties\"\nOutput.ConnectFail.BadPath=\"Nederīgs ceļš vai savienojuma URL. Lūdzu, pārbaudiet vai iestatījumi ir korekti.\"\nOutput.ConnectFail.ConnectFailed=\"Neizdevās savienoties ar serveri\"\nOutput.ConnectFail.InvalidStream=\"Nevarēja piekļūt norādītajam kanālam vai straumēšanas atslēgai. Lūdzu, pārbaudiet straumēšanas atslēgu. Ja tā derīga, pie vainas var būt problēma pievienoties serverim.\"\nOutput.ConnectFail.HdrDisabled=\"HDR izvade pašreiz ir atspējota šai izvadei.\"\nOutput.ConnectFail.Error=\"Mēģinot izveidot savienojumu ar serveri, radās negaidīta kļūme. Vairāk informācijas žurnālfailā.\"\nOutput.ConnectFail.Disconnected=\"Zuda savienojums ar serveri.\"\nOutput.StreamEncodeError.Title=\"Enkodēšanas kļūme\"\nOutput.StreamEncodeError.Msg=\"Straumēšanas laikā atgadījās enkodera kļūme.\"\nOutput.StreamEncodeError.Msg.LastError=\"Straumēšanas laikā radās kodētāja kļūda:<br><br>%1\"\nOutput.RecordFail.Title=\"Neizdevās sākt ierakstu\"\nOutput.RecordFail.Unsupported=\"Izvada formāts ir vai nu neatbalstīts vai arī tas neatbalsta vairāk nekā vienu audio celiņu. Lūdzu, pārbaudiet iestatījumus un mēģiniet vēlreiz.\"\nOutput.RecordNoSpace.Title=\"Nepietiek diska vietas (noliktavas)\"\nOutput.RecordNoSpace.Msg=\"Ieraksta turpināšanai nepietiek diska vietas (failu noliktavas).\"\nOutput.RecordError.Title=\"Ierakstīšanas kļūme\"\nOutput.RecordError.Msg=\"Ieraksta laikā radās nenoteikta kļūme.\"\nOutput.RecordError.EncodeErrorMsg=\"Ieraksta laikā radās enkodera kļūda.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Ierakstīšanas laikā radās kodētāja kļūda:<br><br>%1\"\nOutput.BadPath.Title=\"Nederīgs faila ceļš\"\nLogUploadDialog.Labels.Progress=\"Notiek žurnāla augšupielāde. Lūdzu, uzgaidiet...\"\nLogUploadDialog.Labels.Description.AppLog=\"Jūsu žurnālfails ir augšuplādēts. Tagad jūs varat koplietot tā URL atkļūdošanas vai atbalsta nolūkiem.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Jūsu avārijas ziņojums ir augšuplādēts. Tagad jūs varat koplietot tā URL atkļūdošanas nolūkiem.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Augšupielādēt\"\nLogUploadDialog.Buttons.CopyURL=\"Kopēt Žurnāla URL\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analizēt Žurnālfailu\"\nLogUploadDialog.Buttons.RetryButton=\"Mēģināt Vēlreiz\"\nLogUploadDialog.Errors.Template=\"Mēģinot augšupielādēt failu, radās kļūda:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Nav atrasts augšupielādējams fails vai fails ir tukšs.\"\nRemux.SourceFile=\"OBS ieraksts\"\nRemux.TargetFile=\"Mērķa (izvada) fails\"\nRemux.Remux=\"Remux-ēt\"\nRemux.Stop=\"Beigt remux-ēšanu\"\nRemux.ClearFinished=\"Notīrīt pabeigtos\"\nRemux.ClearAll=\"Notīrīt visus\"\nRemux.OBSRecording=\"OBS ieraksts\"\nRemux.FinishedTitle=\"Remux-ēšana pabeigta\"\nRemux.Finished=\"Ieraksts remux-ēts\"\nRemux.FinishedError=\"Ieraksts remux-ēts, bet fails varētu būt nepilnīgs\"\nRemux.SelectRecording=\"Izvēlies OBS ierakstu...\"\nRemux.SelectTarget=\"Izvēlies izvada/mērķa failu...\"\nRemux.FileExistsTitle=\"Izvada/mērķa fails eksistē\"\nRemux.FileExists=\"Šādi izvada/mērķa faili jau pastāv. Vēlaties tos aizstāt?\"\nRemux.ExitUnfinishedTitle=\"Remux-ēšana procesā\"\nRemux.ExitUnfinished=\"Remux-ēšana nav pabeigta un tās apstādināšana tagad var padarīt izvada/mērķa failu nelietojamu.\\nTomēr vēlaties to apstādināt?\"\nRemux.HelpText=\"Remux-ējamos failus iemetiet šajā logā vai izvēlieties tukšu \\\"OBS ieraksta\\\" šūnu, lai failu pārlūkā atlasītu ievada failu.\"\nMissingFiles=\"Iztrūkstoši faili\"\nMissingFiles.MissingFile=\"Trūkstošais fails\"\nMissingFiles.NewFile=\"Jaunais fails\"\nMissingFiles.HelpText=\"Kopš jūsu pēdējās OBS izmantošanas reizes daži faili ir pazuduši.\"\nMissingFiles.Clear=\"<notīrīti>\"\nMissingFiles.NumFound=\"Atrasti %1 no %2\"\nMissingFiles.Search=\"Meklēt mapē...\"\nMissingFiles.SelectFile=\"Izvēlieties failu...\"\nMissingFiles.SelectDir=\"Izvēlieties mapi, kurā meklēt\"\nMissingFiles.State=\"Stāvoklis\"\nMissingFiles.Missing=\"Zudis\"\nMissingFiles.Replaced=\"Aizstāts\"\nMissingFiles.Cleared=\"Notīrīts\"\nMissingFiles.Found=\"Atrasts\"\nMissingFiles.AutoSearch=\"Atrastas papildu failu atbilstības\"\nMissingFiles.AutoSearchText=\"OBS šajā mapē ir atradis papildu atbilstības trūkstošajiem failiem. Vai vēlaties tās pievienot?\"\nMacPermissions.Description.OpenDialog=\"Šo dialoglodziņu var atkārtoti atvērt, izmantojot OBS Studio izvēlni.\"\nMacPermissions.AccessGranted=\"Pieeja Atļauta\"\nMacPermissions.RequestAccess=\"Pieprasīt Piekļuvi\"\nMacPermissions.OpenPreferences=\"Atvērt %1 Preferences\"\nMacPermissions.Item.ScreenRecording=\"Ekrāna Ierakstīšana\"\nMacPermissions.Item.ScreenRecording.Details=\"OBs vajadzīga Jūsu atļauja, lai varētu tvert ekrānu.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Šī atļauja ir vajadzīga, lai varētu tvert saturu no tīmekļa kamera vai tveršanas kartes.\"\nMacPermissions.Item.Microphone=\"Mikrofons\"\nMacPermissions.Item.Microphone.Details=\"OBS ir nepieciešama šī atļauja, ja vēlaties ierakstīt mikrofonu vai ārēju audioierīci.\"\nMacPermissions.Item.InputMonitoring=\"Ievades Monitorings\"\nMacPermissions.Item.InputMonitoring.Details=\"Šī atļauja ir nepieciešama, lai karstie taustiņi darbotos, kamēr OBS darbojas fonā.\"\nMacPermissions.Item.Accessibility=\"Pieejamība\"\nMacPermissions.Item.Accessibility.Details=\"Vecākās instalācijās OBS var būt norādīts sadaļā “Pieejamība”, nevis “Ievades Monitorings”.\"\nMacPermissions.Continue=\"Turpināt\"\nSourceLeak.Title=\"Avota Tīrīšanas Kļūda\"\nBasic.DesktopDevice1=\"Darbvirsmas audio\"\nBasic.DesktopDevice2=\"Darbvirsmas audio 2\"\nBasic.AuxDevice1=\"Mic/Aux (mikrofons/papildu)\"\nBasic.AuxDevice2=\"Mic/Aux (mikrofons/papildu) 2\"\nBasic.AuxDevice3=\"Mic/Aux (mikrofons/papildu) 3\"\nBasic.AuxDevice4=\"Mic/Aux (mikrofons/papildu) 4\"\nBasic.Scene=\"Aina\"\nBasic.DisplayCapture=\"Ekrāna tveršana/uzņemšana\"\nBasic.Main.PreviewConextMenu.Enable=\"Iespējot priekšskatu\"\nBasic.Main.Preview.Disable=\"Atspējot priekšskatu\"\nScaleFiltering=\"Mēroga filtrēšana\"\nScaleFiltering.Point=\"Punkta\"\nScaleFiltering.Bilinear=\"Bilineāra\"\nScaleFiltering.Bicubic=\"Bikubiska\"\nScaleFiltering.Area=\"Apgabals\"\nBlendingMethod=\"Sajaukšanas Metode\"\nBlendingMethod.Default=\"Noklusējums\"\nBlendingMethod.SrgbOff=\"SRGB izslēgts\"\nBlendingMode=\"Sajaukšanas Režīms\"\nBlendingMode.Normal=\"Normāls\"\nBlendingMode.Additive=\"Pievienot\"\nBlendingMode.Subtract=\"Atņemt\"\nBlendingMode.Screen=\"Ekrāns\"\nBlendingMode.Multiply=\"Reizināšana\"\nBlendingMode.Lighten=\"Apgaismojums\"\nBlendingMode.Darken=\"Aptumšošana\"\nDeinterlacing=\"Deinterleisings\"\nDeinterlacing.Discard=\"Atmest\"\nDeinterlacing.TopFieldFirst=\"Augšējais lauks vispirms\"\nDeinterlacing.BottomFieldFirst=\"Apakšējais lauks vispirms\"\nVolControl.SliderUnmuted=\"Skaļuma slīdnis priekš '%1':\"\nVolControl.SliderMuted=\"Skaļuma slīdnis priekš '%1': (pašreiz noklusināts)\"\nVolControl.Mute=\"Noklusināt '%1'\"\nVolControl.Properties=\"Rekvizīti '%1'\"\nVolControl.UnassignedWarning.Title=\"Nepiešķirts Audio Avots\"\nBasic.Main.AddSceneDlg.Title=\"Pievienot ainu\"\nBasic.Main.AddSceneDlg.Text=\"Lūdzu ievadiet ainas nosaukumu\"\nBasic.Main.DefaultSceneName.Text=\"Aina %1\"\nBasic.Main.AddSceneCollection.Title=\"Pievienot ainu kolekciju\"\nBasic.Main.AddSceneCollection.Text=\"Lūdzu ievadiet ainu kolekcijas nosaukumu\"\nBasic.Main.RenameSceneCollection.Title=\"Pārdēvēt ainu kolekciju\"\nAddProfile.Title=\"Pievienot profilu\"\nAddProfile.Text=\"Lūdzu ievadiet profila nosaukumu\"\nAddProfile.WizardCheckbox=\"Rādīt automātisko konfigurātoru\"\nRenameProfile.Title=\"Pārdēvēt profilu\"\nBasic.Main.MixerRename.Title=\"Pārdēvēt audio avotu\"\nBasic.Main.MixerRename.Text=\"Lūdzu ievadiet audio avota nosaukumu\"\nBasic.Main.PreviewDisabled=\"Priekšskats pašlaik ir atspējots\"\nBasic.SourceSelect=\"Izveidot/atlasīt avotu\"\nBasic.SourceSelect.CreateNew=\"Izveidot jaunu\"\nBasic.SourceSelect.AddExisting=\"Pielikt esošu\"\nBasic.SourceSelect.AddVisible=\"Padarīt avotu redzamu\"\nBasic.Main.Sources.Visibility=\"Redzamība\"\nBasic.Main.Sources.VisibilityDescription=\"Kontrolē '%1' redzamību kanvās\"\nBasic.Main.Sources.Lock=\"Fiksēt\"\nBasic.Main.Sources.LockDescription=\"Fiksē '%1' pozīciju un mērogu kanvās\"\nBasic.PropertiesWindow=\"'%1'rekvizīti\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (automātiski atzīmēt: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Izvēlēties krāsu\"\nBasic.PropertiesWindow.SelectFont=\"Izvēlēties šriftu\"\nBasic.PropertiesWindow.ConfirmTitle=\"Iestatījumu izmaiņa\"\nBasic.PropertiesWindow.Confirm=\"Ir nesaglabātas izmaiņas. Vai vēlaties tās paturēt?\"\nBasic.PropertiesWindow.NoProperties=\"Rekvizīti nav pieejami\"\nBasic.PropertiesWindow.AddFiles=\"Pievienot failus\"\nBasic.PropertiesWindow.AddDir=\"Pievienot mapi\"\nBasic.PropertiesWindow.AddURL=\"Pievienot ceļu/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Pievienot mapi pie '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Pievienot failus pie '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Pievienot ierakstu pie '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Rediģēt '%1' ierakstu\"\nBasic.PropertiesView.FPS.Simple=\"Vienkāršas FPS vērtības\"\nBasic.PropertiesView.FPS.Rational=\"Racionālas FPS vērtības\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Derīgie FPS diapazoni:\"\nBasic.PropertiesView.UrlButton.Text=\"Atvērt šo saiti noklusējuma tīmekļa pārlūkā?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Atvērt URL\"\nBasic.InteractionWindow=\"Mijiedarbojas ar '%1'\"\nBasic.StatusBar.Reconnecting=\"Atvienots. Mēģinājums pievienoties pēc %2 sekundēm (mēģinājums #%1)\"\nBasic.StatusBar.AttemptingReconnect=\"Tiek mēģināts atjaunot savienojumu... (mēģinājums #%1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Atkalsavienošanās mēģinājums sekmīgs\"\nBasic.StatusBar.Delay=\"Aizkave (%1 sek)\"\nBasic.StatusBar.DelayStartingIn=\"Aizkave (sākas pēc %1 sek.)\"\nBasic.StatusBar.DelayStoppingIn=\"Aizkave (beidzas pēc %1 sek.)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Aizkave (beidzas pēc %1 sek., sākas pēc %2 sek.)\"\nBasic.StatusBar.RecordingSavedTo=\"Ieraksts saglabāts '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Replay bufferis saglabāts '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Ekrānšāviņš saglabāts '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Ieraksts automātiski remux-ēts/saglabāts '%1'\"\nBasic.Filters=\"Filtri\"\nBasic.Filters.AsyncFilters=\"Audio/Video filtri\"\nBasic.Filters.AudioFilters=\"Audio filtri\"\nBasic.Filters.EffectFilters=\"Efektu filtri\"\nBasic.Filters.Title=\"Filtri priekš '%1'\"\nBasic.Filters.AddFilter.Title=\"Filtra nosaukums\"\nBasic.Filters.AddFilter.Text=\"Lūdzu norādiet filtra nosaukumu\"\nBasic.TransformWindow=\"Ainas elementa transformēšana\"\nBasic.TransformWindow.Position=\"Pozīcija\"\nBasic.TransformWindow.PositionX=\"Pozīcija X\"\nBasic.TransformWindow.PositionY=\"Pozīcija Y\"\nBasic.TransformWindow.Rotation=\"Pagriešana/rotācija\"\nBasic.TransformWindow.Size=\"Izmērs\"\nBasic.TransformWindow.Width=\"Platums\"\nBasic.TransformWindow.Height=\"Augstums\"\nBasic.TransformWindow.Alignment=\"Pozicionālā izlīdzināšana\"\nBasic.TransformWindow.BoundsType=\"Ierobežojošās kastes tips\"\nBasic.TransformWindow.BoundsAlignment=\"Līdzinājums ierobežojošajā kastē\"\nBasic.TransformWindow.Bounds=\"Ierobežojošās kastes izmērs\"\nBasic.TransformWindow.BoundsWidth=\"Ierobežojošās kastes platums\"\nBasic.TransformWindow.BoundsHeight=\"Ierobežojošās kastes augstums\"\nBasic.TransformWindow.Crop=\"Apcirpt\"\nBasic.TransformWindow.CropLeft=\"Apcirpt kreiso\"\nBasic.TransformWindow.CropRight=\"Apcirpt labo\"\nBasic.TransformWindow.CropTop=\"Apcirpt augšu\"\nBasic.TransformWindow.CropBottom=\"Apcirpt apakšu\"\nBasic.TransformWindow.Alignment.TopLeft=\"Augšā pa kreisi\"\nBasic.TransformWindow.Alignment.TopCenter=\"Augšā centrēts\"\nBasic.TransformWindow.Alignment.TopRight=\"Augšā pa labi\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Centrā pa kreisi\"\nBasic.TransformWindow.Alignment.Center=\"Centrā\"\nBasic.TransformWindow.Alignment.CenterRight=\"Centrā pa labi\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Apakšā pa kreisi\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Apakšā centrēts\"\nBasic.TransformWindow.Alignment.BottomRight=\"Apakšā pa labi\"\nBasic.TransformWindow.BoundsType.None=\"Bez robežām\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Tikai maksimālais izmērs\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Mērogot atbilstoši iekšējām robežām\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Mērogot atbilstoši ārējām robežām\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Mērogot atbilstoši robežu platumam\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Robežot atbilstoši robežu augstumam\"\nBasic.TransformWindow.BoundsType.Stretch=\"Izstiept līdz robežām\"\nBasic.Main.AddSourceHelp.Title=\"Nevar pielikt avotu\"\nBasic.Main.AddSourceHelp.Text=\"Lai pieliktu avotu, ir jābūt vismaz vienai ainai.\"\nBasic.Main.Scenes=\"Ainas\"\nBasic.Main.Sources=\"Avoti\"\nBasic.Main.Source=\"Avots\"\nBasic.Main.Controls=\"Vadīklas\"\nBasic.Main.PreparingStream=\"Sagatavo...\"\nBasic.Main.Connecting=\"Notiek savienošanās...\"\nBasic.Main.StartRecording=\"Sākt ierakstīt\"\nBasic.Main.StartReplayBuffer=\"Sākt 'replay' bufera darbību\"\nBasic.Main.SaveReplay=\"Saglabāt 'replay' buferi\"\nBasic.Main.StartStreaming=\"Sākt straumēt\"\nBasic.Main.StartVirtualCam=\"Sākt virtuālo kameru\"\nBasic.Main.StopRecording=\"Beigt ierakstu\"\nBasic.Main.PauseRecording=\"Pauzēt ierakstu\"\nBasic.Main.UnpauseRecording=\"Atsākt ierakstu\"\nBasic.Main.StoppingRecording=\"Tiek beigts ieraksts...\"\nBasic.Main.StopReplayBuffer=\"Beigt 'replay' bufera darbību\"\nBasic.Main.StoppingReplayBuffer=\"Tiek apturēts 'replay' bufferis\"\nBasic.Main.StopStreaming=\"Beigt straumēt\"\nBasic.Main.AutoStopEnabled=\"(Automātiska Apturēšana)\"\nBasic.Main.StoppingStreaming=\"Aptur straumi...\"\nBasic.Main.ForceStopStreaming=\"Pārtraukt Straumēšanu (atmešanas aizkave)\"\nBasic.Main.ShowContextBar=\"Rādīt Avotu rīkjoslu\"\nBasic.Main.HideContextBar=\"Slēpt Avotu rīkjoslu\"\nBasic.Main.StopVirtualCam=\"Apturēt virtuālo kameru\"\nBasic.Main.Group=\"Grupa %1\"\nBasic.Main.GroupItems=\"Grupēt Atlasītos Vienumus\"\nBasic.Main.Ungroup=\"Atgrupēt\"\nBasic.Main.GridMode=\"Režģa Režīms\"\nBasic.Main.ListMode=\"Saraksta Režīms\"\nBasic.Main.VirtualCamConfig=\"Konfigurēt virtuālo kameru\"\nBasic.VCam.VirtualCamera=\"Virtuālā kamera\"\nBasic.VCam.OutputType=\"Izvades Tips\"\nBasic.MainMenu.File=\"&Fails\"\nBasic.MainMenu.File.Export=\"&Eksports\"\nBasic.MainMenu.File.Import=\"&Imports\"\nBasic.MainMenu.File.ShowRecordings=\"Parādīt ie&rakstus\"\nBasic.MainMenu.File.Remux=\"Re&mux-ēt ierakstus\"\nBasic.MainMenu.File.Settings=\"Ie&statījumi\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Parādīt iestatījumu mapi\"\nBasic.MainMenu.File.ShowProfileFolder=\"Parādīt profila mapi\"\nBasic.MainMenu.File.ShowMissingFiles=\"Pārbaudīt Trūkstošos Failus\"\nBasic.MainMenu.File.Exit=\"Aizvērt\"\nBasic.MainMenu.Edit=\"R&ediģēt\"\nBasic.MainMenu.Edit.Undo=\"Atgriezt (&undo)\"\nBasic.MainMenu.Edit.Redo=\"Atatgriezt (&redo)\"\nBasic.MainMenu.Edit.LockPreview=\"&Fiksēt priekšskatu\"\nBasic.MainMenu.Edit.Scale=\"Priešskata mērogošana\"\nBasic.MainMenu.Edit.Scale.Window=\"Mērogot loga izmērā\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Kanvas (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Izvads (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Mērogots (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Tuvināt\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Tālināt\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Atiestatīt Tālummaiņu\"\nBasic.MainMenu.Edit.Transform=\"&Transformēt\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"R&ediģēt transformējumu\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopēt transformējumu\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Ievietot transformējumu\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Atstatīt t&ransformējumu\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Rotēt 90 gr. PV\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Rotēt 90 gr. PPV\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Rotēt 180 gr.\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Apmest &horizontāli\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Apmest &vertikāli\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Pielāgo&t ekrānam\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Iz&stiept pa ekrānu\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centrēt uz ekrāna\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centrēt vertikāli\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centrēt horizontāli\"\nBasic.MainMenu.Edit.Order=\"Kārt&ot\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Virzīt augš&up\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Virzīt &lejup\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Virzī&t uz pašu augšu\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"&Braukt uz pašu apakšu\"\nBasic.MainMenu.Edit.AdvAudio=\"&Advancētie audio rekvizīti\"\nBasic.MainMenu.View=\"Skats\"\nBasic.MainMenu.View.Toolbars=\"Rīkjoslas\"\nBasic.MainMenu.View.ContextBar=\"Avotu rīkjosla\"\nBasic.MainMenu.View.SourceIcons=\"Avotu &ikonas\"\nBasic.MainMenu.View.StatusBar=\"&Statusa josla\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Pilnekrāna saskarne\"\nBasic.MainMenu.View.ResetUI=\"&Atstatīt lietotāja saskarni\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Vienmēr Virspusē\"\nBasic.MainMenu.Docks=\"&Doki\"\nBasic.MainMenu.Import=\"Importēt...\"\nBasic.MainMenu.Export=\"Eksportēt...\"\nBasic.MainMenu.SceneCollection=\"Ainu kolekcija\"\nBasic.MainMenu.Profile=\"&Profils\"\nBasic.MainMenu.Profile.Import=\"Importēt profilu\"\nBasic.MainMenu.Profile.Export=\"Eksportēt profilu\"\nBasic.MainMenu.SceneCollection.Import=\"Importēt ainu kolekciju\"\nBasic.MainMenu.SceneCollection.Export=\"Eksportēt ainu kolekciju\"\nBasic.MainMenu.Profile.Exists=\"Šāds profils jau pastāv\"\nBasic.MainMenu.Tools=\"Rīki\"\nBasic.MainMenu.Help=\"Palīdzība\"\nBasic.MainMenu.Help.HelpPortal=\"Palīdzība &portāla\"\nBasic.MainMenu.Help.Website=\"Apmeklēt tīmekļvietni\"\nBasic.MainMenu.Help.Discord=\"Pievienoties &Discord serverim\"\nBasic.MainMenu.Help.WhatsNew=\"Kas Jauns\"\nBasic.MainMenu.Help.Logs=\"Žurnālfaili (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Parādīt žurnālfailus (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Augšuplādēt tekošo žurnālfailu\"\nBasic.MainMenu.Help.CheckForUpdates=\"Pārbaudīt Atjauninājumus\"\nBasic.MainMenu.Help.Repair=\"Pārbaudīt Failu Integritāti\"\nBasic.MainMenu.Help.RestartSafeMode=\"Restartēt Drošajā Režīmā\"\nBasic.MainMenu.Help.RestartNormal=\"Restartēt Normālā Režīmā\"\nBasic.MainMenu.Help.CrashLogs=\"Avāriju Ziņojumi (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Rādīt Avāriju Ziņojumus (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Augšupielādēt Iepriekšējo Avārijas Ziņojumu (&P)\"\nBasic.MainMenu.Help.About=\"&Par\"\nBasic.Settings.ProgramRestart=\"Lai šie iestatījumi stātos spēkā, programma ir jārestartē.\"\nBasic.Settings.ConfirmTitle=\"Apstiprināt izmaiņas\"\nBasic.Settings.Confirm=\"Jums ir nesaglabātās izmaiņas. Saglabās tos?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 kontrolē dažus jūsu straumes iestatījumus\"\nBasic.Settings.General=\"Vispārīgi\"\nBasic.Settings.General.Language=\"Valoda\"\nBasic.Settings.General.Updater=\"Atjauninājumi\"\nBasic.Settings.General.UpdateChannel=\"Atjauninājuma kanāls\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Atslēgts)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Noklusējums)\"\nBasic.Settings.General.EnableAutoUpdates=\"Automātiski pārbaudīt atjauninājumus startēšanas laikā\"\nBasic.Settings.General.OpenStatsOnStartup=\"Atvērt statistikas logu startēšanas laikā\"\nBasic.Settings.General.Projectors=\"Projektori\"\nBasic.Settings.General.HideProjectorCursor=\"Nerādīt kursoru virs projektoriem\"\nBasic.Settings.General.SaveProjectors=\"Saglabāt projektorus izejot\"\nBasic.Settings.General.CloseExistingProjectors=\"Ierobežojums līdz vienam pilnekrāna projektoram uz ekrāna\"\nBasic.Settings.General.Preview=\"Priekšskats\"\nBasic.Settings.General.OverflowHidden=\"Slēpt pārplūdi\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Pārplūde vienmēr redzama\"\nBasic.Settings.General.OverflowSelectionHidden=\"Rādīt pārpildi pat tad, ja avots nav redzams\"\nBasic.Settings.General.Importers=\"Importētāji\"\nBasic.Settings.General.Multiview=\"Multiskats\"\nBasic.Settings.General.MultiviewLayout=\"Multiskata Izkārtojums\"\nBasic.Settings.General.ChannelName.stable=\"Stabīlie\"\nBasic.Settings.General.ChannelDescription.stable=\"Jaunākā stabilā versija\"\nBasic.Settings.General.ChannelName.beta=\"Beta / Izlaiduma kandidāti\"\nBasic.Settings.General.ChannelDescription.beta=\"Potenciāli nestabilas pirmsizlaides versijas\"\nBasic.Settings.Appearance=\"Izskats\"\nBasic.Settings.Appearance.General=\"Vispārīgi\"\nBasic.Settings.Appearance.General.Theme=\"Motīvs\"\nBasic.Settings.Appearance.General.Variant=\"Stils\"\nBasic.Settings.Appearance.General.NoVariant=\"Nav Pieejamu Stilu\"\nBasic.Settings.Appearance.FontScale=\"Fonta Izmērs\"\nBasic.Settings.Appearance.Density=\"Blīvums\"\nBasic.Settings.Appearance.Classic=\"Klasisks\"\nBasic.Settings.Appearance.Compact=\"Kompakts\"\nBasic.Settings.Appearance.Normal=\"Normāls\"\nBasic.Settings.Appearance.Comfortable=\"Komfortabls\"\nBasic.Settings.Appearance.OptionsWarning=\"Dažas izskata opcijas šim stilam nav pieejamas.\"\nBasic.Settings.Stream=\"Straume\"\nBasic.Settings.Stream.Destination=\"Galamērķis\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Izmantot autentifikāciju\"\nBasic.Settings.Stream.Custom.Username=\"Lietotājvārds\"\nBasic.Settings.Stream.Custom.Password=\"Parole\"\nBasic.Settings.Stream.TTVAddon=\"Twitch Čata Papildinājumi\"\nBasic.Settings.Stream.TTVAddon.None=\"Nav\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Atvērt iestatījumus\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maksimālais video bitreits: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maksimālais audio bitreits: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksimālā izšķirtspēja: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksimālais FPS: %1\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Iespējot %1\"\nBasic.Settings.Stream.AdvancedOptions=\"Paplašinātās Opcijas\"\nBasic.Settings.Output=\"Izvads\"\nBasic.Settings.Output.Format=\"Ieraksta formāts\"\nBasic.Settings.Output.Format.hMP4=\"Hibrīds MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hibrīds MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmentēts MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmentēts MOV (.mov)\"\nBasic.Settings.Output.Encoder.Video=\"Video Enkoders\"\nBasic.Settings.Output.Encoder.Audio=\"Audio Enkoders\"\nBasic.Settings.Output.SelectDirectory=\"Norādiet ierakstu mapi\"\nBasic.Settings.Output.DynamicBitrate=\"Dinamiski mainīt bitreitu, lai izvairītos no pārslodzēm\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dinamiski maina bitreitu, lai izvairītos no pārslodzēm (beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Tā vietā, lai mazinot sastrēgumus, izmestu kadrus, dinamiski tiek mainīts bitreits.\\n\\nŅemiet vērā, ka tas var palielināt aizturi skatītājiem, ja pēkšņi rodas ievērojami pārslodzes gadījumi.\\nKad bitreits samazinjies, tā atjaunošana var ilgt dažas minūtes.\\n\\nŠobrīd tiek atbalstīts tikai RTMP.\"\nBasic.Settings.Output.Mode=\"Izvada režīms\"\nBasic.Settings.Output.Mode.Simple=\"Vienkāršs\"\nBasic.Settings.Output.Mode.Adv=\"Advancēts\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg izvads\"\nBasic.Settings.Output.UseReplayBuffer=\"Iespējot 'replay' buferi\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maksimālais 'replay' laiks\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maksimālā Atmiņa\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Paredzamais atmiņas izlietojums: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Nevar novērtēt potenciālo atmiņas izmantošanu. Lūdzu iestatiet maksimālo atmiņas ierobežojumu.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"'Replay' bufera faila nosaukuma prefikss\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufikss\"\nBasic.Settings.Output.Simple.SavePath=\"Ierakstu ceļš\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Ieraksta kvalitāte\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Tāda pati kā straumei\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Augsta kvalitāte, vidējs faila izmērs\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Neatšķirama kvalitāte, liels faila izmērs\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Bezzudumu kvalitāte, ārkārtīgi liels faila izmērs\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Brīdinājums: Straumēšanas video bitreits tiks iestatīts uz %1, kas ir pašreizējā straumēšanas pakalpojuma augšējā robeža.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Brīdinājums: Straumēšanas audio bitreits tiks iestatīts uz %1, kas ir pašreizējā straumēšanas pakalpojuma augšējā robeža.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Brīdinājums: Ierakstīšanu nevar pauzēt, ja ierakstīšanas kvalitāte ir iestatīta uz “Tāda pati kā straumei”.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Brīdinājums: Ierakstīšana ar programmatūras enkoderi kvalitātē, kas atšķiras no straumes kvalitātes, prasīs procesora papildu noslodzi, ja straumēsiet un ierakstīsiet vienlaikus.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Tiešām vēlaties izmantot bezzuduma kvalitāti?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Brīdinājums bezzuduma kvalitātes sakarā!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Programmatūras (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Aparatūra (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Aparatūra (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Aparatūra (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Aparatūra (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Aparatūra (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Aparatūra (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Aparatūra (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Aparatūra (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Aparatūra (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Aparatūra (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Programmatūras (x264 zema CPU lietojuma iestatījums, paliels faila izmērs)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Noklusējums)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD celiņš (izmanto celiņu #2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Audio celiņš\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Nesaderīga izšķirtspēja/freimreits (kadru nomaiņas ātrums)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Šis straumēšanas pakalpojums neatbalsta jūsu pašreizējo izvada izšķirtspēju un/vai freimreitu (kadru nomaiņas ātrumu). Tie tiks mainīti uz tuvāko saderīgo vērtību:\\n\\n%1\\n\\nVai vēlaties turpināt?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Izšķirtspēja: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"FPS (kadri sekundē): %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Nesaderīgs Enkoders\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Straumēšanas pakalpojums \\\"%1\\\" neatbalsta enkoderus \\\"%2\\\" un \\\"%3\\\". Šie enkoderi tiks mainīti uz \\\"%4\\\" un \\\"%5\\\".\\n\\nVai vēlaties turpināt?\"\nBasic.Settings.Output.VideoBitrate=\"Video bitreits (bitu pārraides ātrums)\"\nBasic.Settings.Output.AudioBitrate=\"Audio bitreits (bitu pārraides ātrums)\"\nBasic.Settings.Output.Reconnect=\"Automātiski atkalsavienoties\"\nBasic.Settings.Output.RetryDelay=\"Aizkave pēc atkārtota mēģinājuma\"\nBasic.Settings.Output.MaxRetries=\"Maksimālais mēģinājumu skaits\"\nBasic.Settings.Output.EncoderPreset=\"Enkodera priekšiestatījumi\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (zema CPU noslodze, zemākā kvalitāte)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (pēc noklusējuma) (vidēja CPU noslodze, standarta kvalitāte)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (augsta CPU noslodze, augsta kvalitāte)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Enkodera pielāgoti iestatījumi\"\nBasic.Settings.Output.CustomMuxerSettings=\"Muxer-a pielāgoti iestatījumi\"\nBasic.Settings.Output.NoSpaceFileName=\"Veidot faila nosaukumus bez atstarpēm\"\nBasic.Settings.Output.Adv.Rescale=\"Mērogot izvadu\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Atspējots\"\nBasic.Settings.Output.Adv.AudioTrack=\"Audio celiņš\"\nBasic.Settings.Output.Adv.Streaming=\"Straumēšana\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Straumēšanas iestatījumi\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Celiņš #1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Celiņš #2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Celiņš #3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Celiņš #4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Celiņš #5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Celiņš #6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD celiņš\"\nBasic.Settings.Output.Adv.Recording=\"Ieraksts\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Ierakstīšanas iestatījumi\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Ieraksta tips\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tips\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standarta\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Pielāgots izvads (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Izmantot straumes enkoderi)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Faila nosaukuma formatējums\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Pārrakstīt, ja fails eksistē\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg izvada tips\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Izvads uz URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Izvads failā\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Populārākie ierakstu formāti\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Visi faili\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Faila ceļš vai URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Konteinera formāts\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Formāts pēc noklusējuma\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Konteinera formāta apraksts\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Audio/video kodeks uzminēts pēc faila ceļa vai URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Enkoders pēc noklusējuma\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Atspējot enkoderi\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Video enkoders\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Video enkodera iestatījumi (ja kādi)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Audio enkoders\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Audio enkodera iestatījumi (ja kādi)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxer-a iestatījumi (ja kādi)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Atslēgas kadru intervāls (kadri)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Rādīt visus kodekus (pat ja tie ir potenciāli nesaderīgi)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg iestatījumi\"\nBasic.Settings.Output.EnableSplitFile=\"Automātiskā faila dalīšana\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Dalīt pēc laika\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Dalīt pēc izmēra\"\nScreenshot=\"Taisīt izvada ekrānuzņēmumu\"\nScreenshot.SourceHotkey=\"Taisīt izvēlētā avota ekrānuzņēmumu\"\nScreenshot.StudioProgram=\"Taisīt ekrānuzņēmumu (programma)\"\nScreenshot.Preview=\"Taisīt ekrānuzņēmumu (priekšskats)\"\nScreenshot.Scene=\"Taisīt ekrānuzņēmumu (aina)\"\nScreenshot.Source=\"Ekrānuzņēmums (avots)\"\nFilenameFormatting.TT.CCYY=\"Gads, četri cipari\"\nFilenameFormatting.TT.YY=\"Gads, pēdējie divi cipari (00-99)\"\nFilenameFormatting.TT.MM=\"Mēnesis kā decimāls skaitlis (01-12)\"\nFilenameFormatting.TT.hh=\"Stunda 24h formātā (00-23)\"\nFilenameFormatting.TT.mm=\"Minūte (00-59)\"\nFilenameFormatting.TT.ss=\"Sekunde (00-59)\"\nFilenameFormatting.TT.Percent=\"% zīme\"\nFilenameFormatting.TT.B=\"Pilns mēneša nosaukums\"\nFilenameFormatting.TT.H=\"Stunda 24h formātā (00-23)\"\nFilenameFormatting.TT.I=\"Stunda 12h formātā (01–12)\"\nFilenameFormatting.TT.m=\"Mēnesis kā decimāls skaitlis (01-12)\"\nFilenameFormatting.TT.M=\"Minūte (00-59)\"\nFilenameFormatting.TT.p=\"AM vai PM apzīmējums\"\nFilenameFormatting.TT.S=\"Sekunde (00-59)\"\nFilenameFormatting.TT.y=\"Gads, pēdējie divi cipari (00-99)\"\nFilenameFormatting.TT.Y=\"Gads\"\nFilenameFormatting.TT.Z=\"Laika zonas nosaukums vai saīsinājums\"\nFilenameFormatting.TT.FPS=\"Kadri sekundē\"\nFilenameFormatting.TT.VF=\"Video formāts\"\nBasic.Settings.Video.Adapter=\"Video adapteris\"\nBasic.Settings.Video.BaseResolution=\"Bāzes (kanvas) izšķirtspēja\"\nBasic.Settings.Video.ScaledResolution=\"Izvada (mērogota) izšķirtspēja\"\nBasic.Settings.Video.DownscaleFilter=\"Mērogošanas filtrs\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Izšķirtspējas atbilst, samazināšana nav nepieciešama]\"\nBasic.Settings.Video.FPSCommon=\"Vienkāršas FPS vērtības\"\nBasic.Settings.Video.FPSInteger=\"FPS kā vesels skaitlis\"\nBasic.Settings.Video.FPSFraction=\"FPS kā daļskaitlis\"\nBasic.Settings.Video.Numerator=\"Skaitītājs\"\nBasic.Settings.Video.Denominator=\"Denominators/saucējs\"\nBasic.Settings.Video.Renderer=\"Rendereris\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Eksperimentāls)\"\nBasic.Settings.Video.InvalidResolution=\"Nederīga izšķirtspējas vērtība. Jābūt [platums]x[augstums] (piemēram, 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Video izvads pašlaik ir aktīvs. Lai mainītu video iestatījumus, izslēdziet visus izvadus.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (ātrākais, bet izplūdis, ja mērogo)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (precīza mērogošana, 16 paraugi)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (precīza mērogošana, 36 paraugi)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Platība/nogabals (svērtā summa, 4/6/9 paraugi)\"\nBasic.Settings.Audio.SampleRate=\"Diskretizācijas frekvence /frekvenčbiežums (sample rate)\"\nBasic.Settings.Audio.Channels=\"Kanāli\"\nBasic.Settings.Audio.Meters=\"Mērītāji\"\nBasic.Settings.Audio.MeterDecayRate=\"Sabrukšanas ātrums\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Ātrs\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Vidējs (tips: I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lēns (tips: II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Virsotņu skaitītāja tips\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Vienkāršots - Sample Peak\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Precīzs - True Peak (augstāka CPU noslodze)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"BRĪDINĀJUMS: Telpiskās skaņas (surround sound) audio ir iespējots.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Vai iespējot telpiskās skaņas (surround sound) audio?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Tiešām vēlaties iespējot telpiskās skaņas (surround sound) audio?\"\nBasic.Settings.Audio.Devices=\"Globālās audio ierīces\"\nBasic.Settings.Audio.DesktopDevice=\"Darbvirsmas audio\"\nBasic.Settings.Audio.DesktopDevice2=\"Darbvirsmas audio 2\"\nBasic.Settings.Audio.AuxDevice=\"Mic/Aux (mikrofons/papildu) audio\"\nBasic.Settings.Audio.AuxDevice2=\"Mic/Aux (mikrofons/papildu) audio 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mic/Aux (mikrofons/papildu) audio 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mic/Aux (mikrofons/papildu) audio 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Iespējot funkciju \\\"Nospiest, lai noklusinātu mikrofonu\\\"\"\nBasic.Settings.Audio.PushToMuteDelay=\"“Nospiest, lai noklusinātu” funkcijas aizture\"\nBasic.Settings.Audio.EnablePushToTalk=\"Iespējot funkciju \\\"Nospiest, lai runātu\\\"\"\nBasic.Settings.Audio.PushToTalkDelay=\"“Nospiest, lai runātu” funkcijas aizture\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Ierīce nav pievienota vai nav pieejama]\"\nBasic.Settings.Audio.Disabled=\"Atspējots\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Iespējot zema latentuma audio buferizācijas režīmu?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Vai tiešām vēlaties iespējot zema latentuma audio buferizācijas režīmu?\"\nBasic.Settings.Accessibility=\"Pieejamība\"\nBasic.Settings.Accessibility.ColorOverrides=\"Izmantot Citas Krāsas\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Noklusējums\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Pielāgots\"\nBasic.Settings.Advanced=\"Advancēts\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Procesa prioritāte\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Augsta\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Virs vidējā\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Vidēja\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Zem vidējā\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Zema\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Krāsu formāts\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-bitu)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Krāsu telpa\"\nBasic.Settings.Advanced.Video.ColorRange=\"Krāsu diapazons\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Ierobežots\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Pilns\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR Baltais Līmenis\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Monitoringa/noklausīšanās ierīce\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Noklusējuma\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Atspējot nejēdzīgā Windows audio savienošanu\"\nBasic.Settings.Advanced.StreamDelay=\"Straumes aizkave\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Ilgums\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Saglabāt robežpunktu (palielināt aizkavi), atkārtoti pievienojoties\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Paredzamais atmiņas lietojums: %1 MB\"\nBasic.Settings.Advanced.Network=\"Tīkls\"\nBasic.Settings.Advanced.Network.BindToIP=\"Piesaiste IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Iespējot tīkla optimizāciju\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Iespējot TCP stimulēšanu\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Mēģinājumi padarīt RTMP izvadu draudzīgāku citām pret tīkla aizkavi jutīgām programmām, regulējot pārraides ātrumu,\\nnestabilos savienojumos var palielināt kadru izkrišanas risku.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Saīsnes fokusa uzvedība\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nekad neatspējot saīsnes\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Atspējot saīsnes, kad galvenais logs ir fokusā\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Atspējot saīsnes, kad galvenais logs nav fokusā\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(ierakstīt .mkv formātā)\"\nBasic.AdvAudio=\"Advancētie audio rekvizīti\"\nBasic.AdvAudio.ActiveOnly=\"Tikai aktīvie avoti\"\nBasic.AdvAudio.Name=\"Nosaukums\"\nBasic.AdvAudio.Volume=\"Skaļums\"\nBasic.AdvAudio.VolumeSource=\"'%1' skaļums\"\nBasic.AdvAudio.MonoSource=\"Mono downmiksēt/lejupmaisīt '%1'\"\nBasic.AdvAudio.Balance=\"Balanss\"\nBasic.AdvAudio.BalanceSource=\"Balanss '%1'\"\nBasic.AdvAudio.SyncOffset=\"Sinhronizācijas nobīde\"\nBasic.AdvAudio.SyncOffsetSource=\"Sinhronizācijas nobīde '%1'\"\nBasic.AdvAudio.Monitoring=\"Audio monitorings\"\nBasic.AdvAudio.Monitoring.None=\"Monitors izslēgts\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Tikai monitorēt (bez skaņas)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitorēt ar skaņas izvadi\"\nBasic.AdvAudio.MonitoringSource=\"Audio monitorings '%1'\"\nBasic.AdvAudio.AudioTracks=\"Celiņi\"\nBasic.Settings.Hotkeys=\"Saīsnes\"\nBasic.Settings.Hotkeys.Pair=\"Taustiņu kombinācijas, kas kopīgotas ar '%1', darbojas kā pārslēgi\"\nBasic.Settings.Hotkeys.Filter=\"Filtrs\"\nBasic.Hotkeys.SelectScene=\"Pāriet uz ainu\"\nBasic.SystemTray.Show=\"Rādīt\"\nBasic.SystemTray.Hide=\"Nerādīt\"\nBasic.SystemTray.Message.Reconnecting=\"Atvienojies. Notiek pievienošanās...\"\nHotkeys.PageUp=\"PageUp\"\nHotkeys.PageDown=\"PageDown\"\nHotkeys.Backspace=\"Backspace (atpakaļdzēšana)\"\nHotkeys.Print=\"Print (drukāt)\"\nHotkeys.Pause=\"Pauze\"\nHotkeys.MouseButton=\"Pele %1\"\nMute=\"Noklusināt\"\nUnmute=\"Atjaunot skaņu\"\nPush-to-mute=\"Nospiežot noklusināt\"\nPush-to-talk=\"Nospiežot ieslēgt skaņu\"\nSceneItemShow=\"Rādīt '%1'\"\nSceneItemHide=\"Nerādīt '%1'\"\nOutputWarnings.NoTracksSelected=\"Jums jāizvēlas vismaz viens celiņš\"\nOutputWarnings.MP4Recording=\"Brīdinājums: ierakstus, kas saglabāti MP4/MOV, nebūs iespējams atgūt, ja failu nevarēs pabeigt (piemēram, Windows zilā nāves ekrāna, elektrības zudumu u.c. dēļ). Ja vēlaties ierakstīt vairākus audio celiņus, apsveriet iespēju izmantot MKV un, vajadzības gadījumā, pēcāk ierakstu pārtaisīt/pārkonvertēt uz MP4/MOV (Fails → Remux ieraksti)\"\nOutputWarnings.CannotPause=\"Brīdinājums: ierakstus nevar nopauzēt, ja ierakstīšanas enkoders ir iestatīts uz \\\"(Lietot straumes enkoderi)\\\"\"\nCodecCompat.Incompatible=\"(Nesaderīgs ar %1)\"\nCodecCompat.CodecPlaceholder=\"Izvēlēties Enkoderi...\"\nCodecCompat.ContainerPlaceholder=\"Izvēlēties Formātu...\"\nCodecCompat.CodecMissingOnExit.Title=\"Nav Izvēlēts Enkoders\"\nCodecCompat.ContainerMissingOnExit.Title=\"Nav Izvēlēts Formāts\"\nFinalScene.Title=\"Dzēst ainu\"\nFinalScene.Text=\"Jābūt vismaz vienai ainai.\"\nNoSources.Title=\"Avotu nav\"\nNoSources.Text=\"Šķiet, ka vēl neesat pievienojis nevienu video avotu, tāpēc tiks izvadīts/parādīts tikai tukšs ekrāns. Vēlaties to darīt?\"\nNoSources.Text.AddSource=\"Varat pievienot avotus, jebkurā laikā noklikšķinot uz + ikonas lodziņā 'Avoti' galvenajā logā.\"\nNoSources.Label=\"Jums nav neviena avota.\\nNoklikšķiniet uz zemāk esošās pogas +,\\n vai arī ar peles labo pogu noklikšķiniet šeit, lai to pievienotu.\"\nChangeBG=\"Iestatīt krāsu\"\nCustomColor=\"Pielāgota krāsa\"\nBrowserSource.EnableHardwareAcceleration=\"Iespējot pārlūka avota aparatūras paātrinājumu\"\nAbout=\"Par\"\nAbout.Info=\"OBS Studio ir brīva un atklātā koda video ierakstīšanas un tiešraides straumēšanas programmatūra.\"\nAbout.Donate=\"Sniedziet ieguldījumu/atbalstu\"\nAbout.GetInvolved=\"Iesaistieties\"\nAbout.Authors=\"Autori\"\nAbout.License=\"Licence\"\nAbout.Error=\"Kļūda! Failu nevarēja nolasīt.\\n\\nDodieties uz: %1\"\nAbout.Contribute=\"Atbalstiet OBS projektu\"\nAddUrl.Title=\"Pievienot avotu, izmantojot URL\"\nAddUrl.Text=\"Jūs esat OBSā ievilcis URL. Tas automātiski pievienos šo saiti kā avotu. Turpināt?\"\nResizeOutputSizeOfSource=\"Izvades izmēra maiņa (avota lielums)\"\nResizeOutputSizeOfSource.Text=\"Bāzes un izvades izšķirtspēja tiks mainīta atbilstoši pašreizējā avota izmēram.\"\nResizeOutputSizeOfSource.Continue=\"Vēlaties turpināt?\"\nPreviewTransition=\"Priekšskatīt pāreju\"\nImporter=\"Ainu kolekcijas importētājs\"\nImporter.SelectCollection=\"Izvēlieties ainu kolekciju\"\nImporter.Collection=\"Ainu kolekcija\"\nImporter.HelpText=\"Pievienojiet failus šim logam, lai importētu kolekcijas no OBS vai citām atbalstītajām programmām.\"\nImporter.Path=\"Kolekcijas ceļš\"\nImporter.Program=\"Identificētā programma\"\nImporter.AutomaticCollectionPrompt=\"Automātiski meklēt ainu kolekcijas\"\nImporter.AutomaticCollectionText=\"OBS var automātiski atrast importējamas ainu kolekcijas no atbalstītās trešo personu programmatūras. Vai vēlaties, lai OBS automātiski atrastu kolekcijas jūsu vietā?\\n\\nVarat šo vēlāk mainīt izvēlnē Iestatījumi> Vispārīgi> Importētāji.\"\nImporter.SelectFile=\"Pārlūkot...\"\nRestart=\"Restarts\"\nNeedsRestart=\"OBS Studija ir jāpārlaiž. Vai vēlaties pārlaist tūlīt?\"\nLoadProfileNeedsRestart=\"Profilā ir iestatījumi, kuriem nepieciešama OBS restartēšana:\\n%1\\n\\nVai vēlaties restartēt OBS, lai šie iestatījumi stātos spēkā?\"\nContextBar.NoSelectedSource=\"Nav neviena avota\"\nContextBar.MediaControls.PlayMedia=\"Spēlēt mediju\"\nContextBar.MediaControls.PauseMedia=\"Pauzēt mediju\"\nContextBar.MediaControls.StopMedia=\"Apturēt (stopēt) mediju\"\nContextBar.MediaControls.RestartMedia=\"Pārlaist mediju\"\nContextBar.MediaControls.PlaylistNext=\"Nākošais spēlējamo sarakstā\"\nContextBar.MediaControls.PlaylistPrevious=\"Iepriekšējais spēlējamo sarakstā\"\nContextBar.MediaControls.BlindSeek=\"Medija pārtīšanas vidžets\"\nYouTube.Auth.Ok=\"Autorizācija veiksmīgi pabeigta.\\nTagad varat aizvērt šo lapu.\"\nYouTube.Auth.NoCode=\"Autorizācijas process netika pabeigts.\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube Lietotāja Autorizācija\"\nYouTube.AuthError.Text=\"Neizdevās iegūt kanāla informāciju: %1.\"\nYouTube.Actions.Description=\"Apraksts\"\nYouTube.Actions.Category=\"Kategorija\"\nYouTube.Actions.Thumbnail=\"Sīktēls\"\nYouTube.Actions.Thumbnail.SelectFile=\"Izvēlēties failu...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Neviens fails nav izvēlēts\"\nYouTube.Actions.Thumbnail.ClearFile=\"Attīrīt\"\nYouTube.Actions.MadeForKids=\"Vai šis video ir paredzēts bērniem?*\"\nYouTube.Actions.MadeForKids.Yes=\"Jā, tas ir paredzēts bērniem\"\nYouTube.Actions.MadeForKids.No=\"Nē, tas nav paredzēts bērniem\"\nYouTube.Actions.Latency=\"Latentums\"\nYouTube.Actions.Latency.Normal=\"Normāls\"\nYouTube.Actions.Latency.Low=\"Zems\"\nYouTube.Actions.Latency.UltraLow=\"Īpaši zems\"\nYouTube.Actions.EnableAutoStart=\"Iespējot automātisko startēšanu\"\nYouTube.Actions.EnableAutoStop=\"Iespējot automātisko apturēšanu\"\nYouTube.Actions.EnableDVR=\"Iespējot DVR\"\nYouTube.Actions.ScheduleForLater=\"Ieplānot spēlēt vēlāk\"\nYouTube.Actions.RememberSettings=\"Atcerēties šos iestatījumus\"\nYouTube.Actions.Dashboard=\"Atvērt Youtube Studio\"\nYouTube.Actions.Error.Title=\"Tiešraides izveides kļūme\"\nYouTube.Actions.Error.Text=\"YouTube piekļuves kļūme '%1'. <br/> Detalizētu kļūdas aprakstu var atrast vietnē <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTube piekļuves kļūda. Lūdzu, pārbaudiet tīkla savienojumu vai YouTube servera piekļuvi.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Apraides izveides kļūme '%1'. <br/> Detalizētu kļūmes aprakstu var atrast vietnē <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Nav izveidota straume. Lūdzu atkārtoti piesaistiet kontu.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API kļūda. Lūdzu, skatiet žurnālfailu, lai iegūtu plašāku informāciju.\"\nYouTube.Actions.Error.FileMissing=\"Atlasītais fails neeksistē.\"\nYouTube.Actions.Error.FileTooLarge=\"Atlasītais fails ir pārāk liels (ierobežojums: 2 MiB).\"\nYouTube.Actions.EventCreated.Title=\"Notikums izveidots\"\nYouTube.Actions.EventCreated.Text=\"Notikums sekmīgi izveidots.\"\nYouTube.Actions.Stream=\"Straumi\"\nYouTube.Actions.Stream.ScheduledFor=\"Ieplānots %1\"\nYouTube.Actions.Stream.YTStudio=\"Automātiski izveidots YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Notiek jaunas tiešraides izveide, lūdzu, uzgaidiet ...\"\nConfigDownload.WarningMessageTitle=\"Brīdinājums\"\nMultitrackVideo.IncompatibleSettings.Title=\"Nesaderīgi Iestatījumi\"\nPluginManager=\"Spraudņu Pārvaldnieks\"\nPluginManager.MissingPlugin=\"[SPRAUDNIS NAV ATRASTS]\"\nPluginManager.Section.Updates=\"Atjauninājumi\"\n"
  },
  {
    "path": "frontend/data/locale/mn-MN.ini",
    "content": "Language=\"Монгол\"\nOK=\"За\"\nApply=\"Хэрэглэх\"\nCancel=\"Цуцлах\"\nClose=\"Хаах\"\nSave=\"Хадгалах\"\nDiscard=\"Болих\"\nDisable=\"Хориглох\"\nYes=\"Тийм\"\nNo=\"Үгүй\"\nAdd=\"Нэмэх\"\nRemove=\"Хасах\"\nRename=\"Шинээр нэрлэх\"\nInteract=\"Харилцан ажиллах\"\nFilters=\"Шүүлтүүрүүд\"\nProperties=\"Шинж чанар\"\nMoveUp=\"Дээшээ зөө\"\nMoveDown=\"Доошоо зөө\"\nSettings=\"Тохиргоо\"\nDisplay=\"Харагдах байдал\"\nName=\"Нэр\"\nExit=\"Гарах\"\nMixer=\"Дуу холигч\"\nBrowse=\"Хайх\"\nMono=\"Моно\"\nStereo=\"Стерео\"\nDroppedFrames=\"Орхигдсон дүрсүүд %1 (%2%)\"\nClear=\"Арилгах\"\nRevert=\"Буцаах\"\nShow=\"Харуулах\"\nHide=\"Нуух\"\nUnhideAll=\"Бүгдийг Харуулах\"\nUntitled=\"Гарчиггүй\"\nNew=\"Шинэ\"\nDuplicate=\"Хувилах\"\nEnable=\"Идэвхжүүлэх\"\nDisableOSXVSync=\"macOS V-Sync-ийг хориглох\"\nResetOSXVSyncOnExit=\"macOS V-Sync-ийг гарахдаа хэвэнд нь оруулах\"\nHighResourceUsage=\"Хөрвүүлэх явц нь хэт их ачаалсан! Видеоны тохиргоог бууруулах эсвэл хурдасгасан хөрвүүлэх тохиргоог ашиглана уу.\"\nTransition=\"Шилжилт\"\nQuickTransitions=\"Түргэн шилжилт\"\nFadeToBlack=\"Хар өнгөөр бүдгэрэх\"\nLeft=\"Зүүн\"\nRight=\"Баруун\"\nTop=\"Дээд хэсэг\"\nBottom=\"Доод хэсэг\"\nReset=\"Дахин тохируулах\"\nHours=\"Цаг\"\nMinutes=\"Минут\"\nSeconds=\"Секунд\"\nDeprecated=\"Хуучин зүйлс\"\nReplayBuffer=\"Нөөц дүрс дахин тоглуулах\"\nImport=\"Импортлох\"\nExport=\"Экспортлох\"\nCopy=\"Хуулах\"\nPaste=\"Зөөж тавих\"\nPasteReference=\"Буулгах (Лавлагаа)\"\nPasteDuplicate=\"Буулгах (Хувилах)\"\nRemuxRecordings=\"Remux бичлэгүүд\"\nNext=\"Дараачийх\"\nBack=\"Буцах\"\nDefaults=\"Үндсэн\"\nRestoreDefaults=\"Үндсэн тохиргоо\"\nHideMixer=\"Холигч дотор нуух\"\nTransitionOverride=\"Шилжүүлэлтийг, нэмэлтээр гүйцэтгэх\"\nNone=\"Байхгүй\"\nStudioMode.Preview=\"Урьдчилж харах\"\nStudioMode.Program=\"Програм\"\nStudioMode.PreviewSceneName=\"шалгаж үзэх%1: \"\nShowInMultiview=\"Олон харагдацаар үзүүлэх\"\nVerticalLayout=\"Босоо Байрлал\"\nGroup=\"Групп\"\nDoNotShowAgain=\"Дахин харуулах хэрэггүй\"\nDefault=\"(Үндсэн)\"\nCalculating=\"Тооцоолж байна...\"\nFullscreen=\"Дэлгэц дүүрэн\"\nWindowed=\"Цонхтой\"\nRefreshBrowser=\"Сэргээх\"\nLockVolume=\"Дууг түгжих\"\nLogViewer=\"Лог Харагч\"\nShowOnStartup=\"Эхлэх үед харуулах\"\nOpenFile=\"Файлыг нээх\"\nAlreadyRunning.Title=\"OBS нь аль хэдийнээ ажиллаж байна\"\nAlreadyRunning.LaunchAnyway=\"Юутай ч эхлүүл\"\nChromeOS.Title=\"Дэмжигдээгүй тавцан\"\nDockCloseWarning.Title=\"Залгагддаг цонхыг хааж байна\"\nExtraBrowsers=\"Хувийн Хөтчийн Холбоос\"\nExtraBrowsers.DockName=\"Холбогдсон Нэр\"\nAuth.Authing.Title=\"Баталгаажуулж нэвтрүүлж байна...\"\nAuth.AuthFailure.Title=\"Баталгаажуулж нэвтрүүлэх явцад алдаа гарлаа\"\nAuth.InvalidScope.Title=\"Баталгаажуулж нэвтрүүлэх шаардлагатай\"\nAuth.LoadingChannel.Title=\"Сувгийн мэдээлэл уншиж байна...\"\nAuth.LoadingChannel.Text=\"%1 -ын сувгийн мэдээлэл уншиж байна, түр хүлээнэ үү...\"\nAuth.LoadingChannel.Error=\"Сувгийн мэдээлэл авч чадсангүй.\"\nAuth.ChannelFailure.Title=\"Суваг уншихад алдаа гарлаа\"\nAuth.Chat=\"Чат\"\nAuth.StreamInfo=\"Цацалтын Мэдээлэл\"\nTwitchAuth.Stats=\"Twitch статистик\"\nRestreamAuth.Channels=\"Сувагыг Дахин Цацах\"\nCopy.Filters=\"Шүүлтүүрийг хуулах\"\nPaste.Filters=\"Шүүлтүүрийг буулгах\"\nBandwidthTest.Region=\"Бүс нутаг\"\nBandwidthTest.Region.US=\"Нэгдсэн Улс\"\nBandwidthTest.Region.EU=\"Европ\"\nBandwidthTest.Region.Asia=\"Ази\"\nBandwidthTest.Region.Other=\"Бусад\"\nBasic.AutoConfig=\"Автоматаар Тохируулах Шидтэн\"\nBasic.AutoConfig.ApplySettings=\"Тохиргоог хэрэглэх\"\nBasic.AutoConfig.StartPage=\"Хэрэглээний Мэдээлэл\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Би зөвхөн виртуал камер ашиглана\"\nBasic.AutoConfig.VideoPage=\"Видео Тохиргоо\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Одоо байгааг ашиглах (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Харуулах %1(%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Одоогийнхыг ашиглах (%1)\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Өөрийн цацалтын мэдээллийг оруулна уу\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Хэрэглэгчийг холбох (зөвлөж байна)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Хэрэглэгчийг салгах\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Хэрэглэгчийг салгах уу?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Цацалтын түлхүүр үгс авах\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Илүү мэдээлэл\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Цацалтын түлхүүр үгс хэрэглэх\"\nBasic.AutoConfig.StreamPage.Service=\"Үйлчилгээ\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Бүгдийг харуул...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Өөр...\"\nBasic.AutoConfig.StreamPage.Server=\"Сервер\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Цацалтын түлхүүр үгс\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Холбогдсон бүртгэл\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Цацалтын анхааруулга\"\nBasic.AutoConfig.TestPage=\"Эцсийн үр дүн\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Туршилт дууссан\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Холбогдож байна: %1...\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"%1x%2 %3 FPS Туршилт хийх байна...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Цацалтыг Кодлох төрөл\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Бичлэгийг Кодлох төрөл\"\nBasic.Stats=\"Статистикууд\"\nBasic.Stats.CPUUsage=\"CPU-ний хэрэглээ\"\nBasic.Stats.HDDSpaceAvailable=\"Дискний боломжтой зай\"\nBasic.Stats.MemoryUsage=\"Санах ойн хэрэглээ\"\nBasic.Stats.Output.Stream=\"Цацалт\"\nBasic.Stats.Output.Recording=\"Бичлэг хийж байна\"\nBasic.Stats.Status=\"Статус\"\nBasic.Stats.Status.Recording=\"Бичлэг хийж байна\"\nBasic.Stats.Status.Live=\"Шууд\"\nBasic.Stats.Status.Reconnecting=\"Дахин холбогдож байна\"\nBasic.Stats.Status.Inactive=\"Идэвхигүй\"\nBasic.Stats.Status.Active=\"Идэвхтэй\"\nBasic.Stats.Bitrate=\"Битийн хурд\"\nBasic.Stats.ResetStats=\"Статистикийг дахин тохируулах\"\nUpdater.Title=\"Шинж шинэчлэлт билэн байна\"\nUpdater.Text=\"Шинэ шинэчлэл нь билэн байна:\"\nUpdater.UpdateNow=\"Одоо шинэчлэх\"\nUpdater.RemindMeLater=\"Дараа нь надад сануул\"\nUpdater.Skip=\"Хувилбар алгасах\"\nUpdater.NoUpdatesAvailable.Title=\"Бэлэн шинэчлэл алга байна\"\nUpdater.NoUpdatesAvailable.Text=\"Одоогоор бэлэн шинэчлэл алга байна\"\nQuickTransitions.DuplicateScene=\"Давхардсан хөшиг\"\nQuickTransitions.HotkeyName=\"Түргэн шилжилт: %1\"\nBasic.SceneTransitions=\"Хөшигний шилжилт\"\nBasic.TransitionDuration=\"Үргэлжлэх хугацаа\"\nBasic.TogglePreviewProgramMode=\"Студи Горим\"\nUndo.Undo=\"Буцаах\"\nUndo.Redo=\"Дахин хийх\"\nUndo.Add=\"'%1' нэмэх\"\nUndo.Delete=\"'%1' устгах\"\nUndo.Rename=\"'%1'-ны нэрийг өөрчлөх\"\nUndo.SceneCollection.Switch=\"'%1'-руу шилжих\"\nUndo.Item.Undo=\"'%1' буцаах\"\nUndo.Item.Redo=\"'%1' дахин хийх\"\nUndo.Volume.Mute=\"'%1' дууг хаах\"\nUndo.Volume.Unmute=\"'%1' дууг нээх\"\nTransitionNameDlg.Title=\"Шилжүүлэгчийн нэр\"\nTitleBar.Profile=\"Профайл\"\nTitleBar.Scenes=\"Хөшиг\"\nNameExists.Title=\"Нэр нь аль хэдийн байна\"\nNameExists.Text=\"Нэр нь аль хэдийнээ хэрэглэгдэж байна.\"\nNoNameEntered.Text=\"Та хоосон нэр ашиглах боломжгүй.\"\nConfirmStart.Title=\"Цацаж эхлэх үү?\"\nConfirmStop.Title=\"Цацалтыг зогсоох?\"\nConfirmStopRecord.Title=\"Бичлэгийг зогсоох?\"\nConfirmRemove.Title=\"Хасалтыг батлах\"\nOutput.StartStreamFailed=\"Цацалт хийж эхлэхгээд амжилтгүй болсон\"\nOutput.StartRecordingFailed=\"Бичлэг хийж эхэлсэнгүй\"\nOutput.ConnectFail.Disconnected=\"Серверээс салгагдсан байна.\"\nOutput.RecordFail.Title=\"Бичлэг хийх эхлэхгээд амжилтгүй болсон\"\nOutput.RecordError.Title=\"Бичлэгийн алдаа\"\nOutput.RecordError.Msg=\"Бичлэг хийж байх үед тодорхойгүй алдаа гарлаа.\"\nRemux.SourceFile=\"OBS-ийн бичлэг\"\nRemux.ClearAll=\"Бүх зүйлсийг арилгах\"\nRemux.OBSRecording=\"OBS-ийн бичлэг\"\nRemux.SelectRecording=\"OBS-ийн бичлэгийг сонгох...\"\nMissingFiles.Clear=\"<цэвэрлэсэн>\"\nMissingFiles.SelectFile=\"Файл сонгох\"\nMissingFiles.SelectDir=\"Хайх хавтас сонгох\"\nMissingFiles.State=\"Төлөв\"\nMissingFiles.Missing=\"Алга\"\nMissingFiles.Replaced=\"Орлосон\"\nMissingFiles.Cleared=\"Цэвэрлэсэн\"\nMissingFiles.Found=\"Олдсон\"\nBasic.DesktopDevice1=\"Дэлгэцний дуу\"\nBasic.DesktopDevice2=\"Дэлгэцний дуу 2\"\nBasic.AuxDevice1=\"Мик/Aux\"\nBasic.AuxDevice2=\"Мик/Aux 2\"\nBasic.AuxDevice3=\"Мик/Aux 3\"\nBasic.AuxDevice4=\"Мик/Aux 4\"\nBasic.Scene=\"Хөшиг\"\nBasic.DisplayCapture=\"Дэлгэц Чагнагч\"\nBasic.Main.PreviewConextMenu.Enable=\"Урьдчилан харуулахыг идэвхжүүлэх\"\nBasic.Main.Preview.Disable=\"Урьдчилан харуулахыг идэвхгүй болгох\"\nVolControl.Mute=\"'%1' дууг хаах\"\nBasic.Main.AddSceneDlg.Title=\"Хөшиг нэмэх\"\nBasic.Main.AddSceneDlg.Text=\"Хөшигний нэрийг оруулна уу\"\nBasic.Main.DefaultSceneName.Text=\"Хөшиг %1\"\nBasic.Main.AddSceneCollection.Title=\"Хөшигний Цуглуулга Нэмэх\"\nBasic.Main.AddSceneCollection.Text=\"Хөшигний цуглуулганы нэрийг оруулна уу\"\nBasic.Main.RenameSceneCollection.Title=\"Хөшигний цуглуулганы нэрийг өөрчлөх\"\nAddProfile.Title=\"Профайл Нэмэх\"\nAddProfile.Text=\"Профайлын нэрийг оруулна уу\"\nRenameProfile.Title=\"Профайлыг дахин нэрлэх\"\nBasic.Main.PreviewDisabled=\"Урьдчилан харуулах одоогоор идэвхгүй байна\"\nBasic.SourceSelect.CreateNew=\"Шинийг үүсгэх\"\nBasic.Main.Sources.Visibility=\"Харагдах байдал\"\nBasic.PropertiesWindow.SelectColor=\"Өнгө сонгох\"\nBasic.PropertiesWindow.SelectFont=\"Фонт сонгох\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"URL нээх\"\nBasic.Filters=\"Шүүлтүүрүүд\"\nBasic.Filters.AddFilter.Title=\"Нэр шүүх\"\nBasic.TransformWindow=\"Хөшигний зүйлийг өөрчлөх\"\nBasic.TransformWindow.Position=\"Байрлал\"\nBasic.TransformWindow.Size=\"Хэмжээ\"\nBasic.TransformWindow.Crop=\"Тайрах\"\nBasic.TransformWindow.Alignment.TopLeft=\"Зүүн дээд тал\"\nBasic.TransformWindow.Alignment.TopCenter=\"Дээтлийн Төвдөн\"\nBasic.TransformWindow.Alignment.TopRight=\"Баруун дээд тал\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Зүүн төв\"\nBasic.TransformWindow.Alignment.Center=\"Төвд нь\"\nBasic.TransformWindow.Alignment.CenterRight=\"Баруун төв\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Зүүн доод\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Доотлын Төвдөн\"\nBasic.TransformWindow.Alignment.BottomRight=\"Баруун доод\"\nBasic.TransformWindow.BoundsType.None=\"Хил хязгааргүй\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Зөвхөн хамгийн их хэмжээ\"\nBasic.Main.AddSourceHelp.Title=\"Эх үүсвэр нэмэх боломжгүй\"\nBasic.Main.AddSourceHelp.Text=\"Та \\\"Эх үүсвэр\\\" нэмэхийн тулд дор хаяж 1 \\\"Хөшигтэй\\\" байх ёстой.\"\nBasic.Main.Scenes=\"Хөшигнүүд\"\nBasic.Main.Sources=\"Эх үүсвэр\"\nBasic.Main.Source=\"Эх сурвалж\"\nBasic.Main.Controls=\"Удирдлага\"\nBasic.Main.Connecting=\"Холбогдож байна...\"\nBasic.Main.StartRecording=\"Бичлэг хийж эхлэх\"\nBasic.Main.StartStreaming=\"Цацаж эхлэх\"\nBasic.Main.StartVirtualCam=\"Виртуал камерыг эхлүүлэх\"\nBasic.Main.StopRecording=\"Бичлэгийг зогсоох\"\nBasic.Main.PauseRecording=\"Бичлэгийг түр зогсоох\"\nBasic.Main.UnpauseRecording=\"Бичлэгийг түр зогсооcныг болиулах\"\nBasic.Main.StoppingRecording=\"Бичлэгийг зогсоох...\"\nBasic.Main.SetupBroadcast=\"Шууд дамжуулалт хянах\"\nBasic.Main.StopStreaming=\"Цацалтыг зогсоох\"\nBasic.Main.StopBroadcast=\"Шууд дамжуулалт дуусгах\"\nBasic.Main.Group=\"Групп %1\"\nBasic.Main.Ungroup=\"Груплэхгүй\"\nBasic.MainMenu.File=\"&Хавтас\"\nBasic.MainMenu.File.ShowRecordings=\"Хийсэн бичлэгүүдийг харуулах\"\nBasic.MainMenu.File.Remux=\"Re&mux бичлэгүүд\"\nBasic.MainMenu.File.Settings=\"Тохиргоо (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Тохиргооны хавтасыг харуулах\"\nBasic.MainMenu.File.ShowProfileFolder=\"Профайлын хавтасыг харуулах\"\nBasic.MainMenu.File.Exit=\"Гарах (&X)\"\nBasic.MainMenu.Edit=\"&Засах\"\nBasic.MainMenu.Edit.LockPreview=\"Урьдчилан харуулахыг түгжих\"\nBasic.MainMenu.Edit.Scale=\"Урьдчилан харуулахыг өргөжүүлэх\"\nBasic.MainMenu.Edit.Scale.Window=\"Цонх дүүрэн болгох\"\nBasic.MainMenu.Edit.Scale.Output=\"Гаралтын (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"Хувиргах\"\nBasic.MainMenu.Edit.Order=\"Зөөх\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Дээшээ зөө\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Доошоо зөө\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Хамгийн дээр тулгах\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Хамгийн доош тулгах\"\nBasic.MainMenu.Edit.AdvAudio=\"Нарийвчилсан аудио шинж чанарууд\"\nBasic.MainMenu.View=\"&Харах\"\nBasic.MainMenu.View.ContextBar=\"Эх сурвалжын Самбар\"\nBasic.MainMenu.View.SourceIcons=\"Эх сурвалжын Дүрснүүд\"\nBasic.MainMenu.View.StatusBar=\"Статусын самбар\"\nBasic.MainMenu.SceneCollection=\"Хөшигний цуглуулга (&S)\"\nBasic.MainMenu.Profile=\"&Профайл\"\nBasic.MainMenu.Profile.Import=\"Профайлыг Импортлох\"\nBasic.MainMenu.Profile.Export=\"Профайлыг Экспортлох\"\nBasic.MainMenu.SceneCollection.Import=\"Хөшигний цуглуулга импортлох\"\nBasic.MainMenu.SceneCollection.Export=\"Хөшигний цуглуулагыг экспортлох\"\nBasic.MainMenu.Profile.Exists=\"Профайл аль хэдийн байна\"\nBasic.MainMenu.Tools=\"Багаж (&T)\"\nBasic.MainMenu.Help=\"Тусламж (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Портал Тусламж (&P)\"\nBasic.MainMenu.Help.Website=\"Вэбсайтаар Зочлох (&W)\"\nBasic.MainMenu.Help.Discord=\"&Дискорд Серверт нэгдэх\"\nBasic.MainMenu.Help.Logs=\"&Бүртгэл файлууд\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"&Бүртгэлийн файлыг харуулах\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"&Одоогийн бүртгэлийн файлыг байршуулах\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Одоогийн бүртгэлийг харах\"\nBasic.MainMenu.Help.CheckForUpdates=\"Шинэчлэл шалгах\"\nBasic.MainMenu.Help.CrashLogs=\"Эвдрэлийн мэдээлэл (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Эвдрэлийн мэдээлэлүүдийг харуулах (&S)\"\nBasic.MainMenu.Help.About=\"Тухай (&A)\"\nBasic.Settings.ConfirmTitle=\"Өөрчлөлтийг батлах\"\nBasic.Settings.General=\"Ерөнхий\"\nBasic.Settings.General.Language=\"Хэл\"\nBasic.Settings.General.EnableAutoUpdates=\"Эхлэх үед шинэчлэлийг автоматаар шалгах\"\nBasic.Settings.General.Snapping=\"Эх сурвалжийг Буулган Тэгшлэх\"\nBasic.Settings.General.ScreenSnapping=\"Эх сурвалжийг дэлгэцийн ирмэгт буулгах\"\nBasic.Settings.General.CenterSnapping=\"Эх сурвалжийг хэвтээ ба босоо төв рүү буулгах\"\nBasic.Settings.General.SourceSnapping=\"Эх сурвалжийг бусад эх сурвалжтай холбох\"\nBasic.Settings.General.SnapDistance=\"Буландуулах Мэдрэмж\"\nBasic.Settings.General.Preview=\"Урьдчилж харах\"\nBasic.Settings.Stream=\"Цацалт\"\nBasic.Settings.Stream.Custom.Username=\"Хэрэглэгчийн нэр\"\nBasic.Settings.Stream.Custom.Password=\"Нууц үг\"\nBasic.Settings.Stream.TTVAddon.None=\"Байхгүй\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV ба FrankerFaceZ\"\nBasic.Settings.Stream.IgnoreRecommended=\"Цацалт хийх үйлчилгээний тохиргооны зөвлөмжийг үл тоомсорлох\"\nBasic.Settings.Output=\"Гаралт\"\nBasic.Settings.Output.Format=\"Бичлэгийн формат\"\nBasic.Settings.Output.SelectDirectory=\"Бичлэгийн Лавлахыг Сонгох\"\nBasic.Settings.Output.Mode=\"Гаралтын Горим\"\nBasic.Settings.Output.Mode.Simple=\"Энгийн\"\nBasic.Settings.Output.Mode.Adv=\"Дэвшилттэй\"\nBasic.Settings.Output.Simple.SavePath=\"Бичлэгийн зам\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Бичлэгийн Чанар\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Цацалттай адилхан\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD Горим (2-аар горимийг хэрэглэдэг)\"\nBasic.Settings.Output.VideoBitrate=\"Видео Битийн хурд\"\nBasic.Settings.Output.AudioBitrate=\"Аудио Битийн хурд\"\nBasic.Settings.Output.Reconnect=\"Автоматаар дахин холбогдох\"\nBasic.Settings.Output.MaxRetries=\"Олон удаа оролдлого хийх\"\nBasic.Settings.Output.Adv.AudioTrack=\"Дууны горим\"\nBasic.Settings.Output.Adv.Streaming=\"Цацалт\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Горим 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Горим 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Горим 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Горим 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Горим 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Горим 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD Горим\"\nBasic.Settings.Output.Adv.Recording=\"Бичлэг хийх\"\nBasic.Settings.Output.Adv.Recording.Type=\"Төрөл\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Стандарт\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Бичлэгийн нийтлэг өргөтгөлүүд\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Бүх файлууд\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Аудио\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Видео\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Видео Кодлогч\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Аудио Кодлогч\"\nBasic.Settings.Video=\"Видео\"\nBasic.Settings.Video.Adapter=\"Видео Адаптер\"\nBasic.Settings.Video.BaseResolution=\"Суурийн (Canvas) Хэмжээс\"\nBasic.Settings.Video.ScaledResolution=\"Гаралтын (Scaled) Хэмжээс\"\nBasic.Settings.Video.DownscaleFilter=\"Бууруулах Шүүлтүүр\"\nBasic.Settings.Video.FPSCommon=\"Түгээмэл өгөгдсөн FPS\"\nBasic.Settings.Video.Numerator=\"Тоонжуулагч\"\nBasic.Settings.Video.Denominator=\"Деноматор\"\nBasic.Settings.Video.Renderer=\"Рэндэрлэгч\"\nBasic.Settings.Audio=\"Аудио\"\nBasic.Settings.Audio.Channels=\"Сувгууд\"\nBasic.Settings.Audio.Devices=\"Ерөнхий аудио төхөөрөмжүүд\"\nBasic.Settings.Audio.EnablePushToMute=\"Дарж-хаахыг Идэвхжүүлэх\"\nBasic.Settings.Audio.EnablePushToTalk=\"Дарж-ярихыг Идэвхжүүлэх\"\nBasic.Settings.Advanced=\"Дэвшилттэй\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Үйл явцын чиглэл\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Их\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Хэвийнээс Дээгүүр\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Хэвийн\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Хэвийнээс Доогуур\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Идэвхигүй\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Дүүрэн\"\nBasic.Settings.Advanced.StreamDelay=\"Цацалтын хоцролт\"\nBasic.Settings.Advanced.Network=\"Сүлжээ\"\nBasic.Settings.Advanced.Network.BindToIP=\"IP холболт хийх\"\nBasic.AdvAudio=\"Нарийвчилсан аудио шинж чанарууд\"\nBasic.AdvAudio.Name=\"Нэр\"\nBasic.AdvAudio.Balance=\"Тэнцвэржүүлэх\"\nBasic.AdvAudio.AudioTracks=\"Горимууд\"\nBasic.Settings.Hotkeys=\"Богино холбооc\"\nBasic.Settings.Hotkeys.Filter=\"Шүүлтүүр\"\nBasic.Hotkeys.SelectScene=\"Хөшиг рүү солих\"\nBasic.SystemTray.Show=\"Харуулах\"\nBasic.SystemTray.Hide=\"Нуух\"\nHotkeys.Insert=\"Оруулах\"\nHotkeys.Delete=\"Устгах\"\nHotkeys.Home=\"Гэр\"\nHotkeys.End=\"Төгсгөл\"\nHotkeys.PageUp=\"Нэг хуудас дээш\"\nHotkeys.PageDown=\"Нэг хуудас доош\"\nHotkeys.Print=\"Хэвлэх\"\nHotkeys.Pause=\"Пауз авах\"\nHotkeys.Left=\"Зүүн\"\nHotkeys.Right=\"Баруун\"\nHotkeys.Up=\"Дээшээ\"\nHotkeys.Down=\"Доошоо\"\nHotkeys.Windows=\"Цонх\"\nHotkeys.Menu=\"Цэс\"\nHotkeys.Space=\"Зай\"\nMute=\"Хаах\"\nUnmute=\"Нээх\"\nPush-to-mute=\"Дарж хаах\"\nPush-to-talk=\"Дарж ярих\"\nSceneItemShow=\"'%1' харуулах\"\nSceneItemHide=\"'%1' нуух\"\nOutputWarnings.NoTracksSelected=\"Та дор хаяж нэг горимийг сонгох ёстой\"\nFinalScene.Title=\"Хөшиг устгах\"\nNoSources.Title=\"Эх сурвалж байхгүй\"\nChangeBG=\"Өнгө өгөх\"\nCustomColor=\"Өөр өнгө\"\nAbout=\"Тухай\"\nAbout.Info=\"OBS Studio нь үнэгүй ба нээлттэй хөгжүүлэгдэж байгаа видео бичлэг хийж мөн шууд цацалт хийдэг програм хангамж юм.\"\nAbout.Donate=\"Хувь нэмэрээ оруулна уу\"\nAbout.GetInvolved=\"Үүнд оролцох\"\nAbout.Authors=\"Зохиогчид\"\nAbout.License=\"Лиценз\"\nAbout.Contribute=\"OBS төслийг дэмжнэ үү\"\nContextBar.NoSelectedSource=\"Ямар ч эх сурвалж сонгоогүй байна\"\n"
  },
  {
    "path": "frontend/data/locale/ms-MY.ini",
    "content": "Language=\"Bahasa Melayu\"\nApply=\"Terap\"\nCancel=\"Batal\"\nClose=\"Tutup\"\nSave=\"Simpan\"\nDiscard=\"Singkir\"\nDisable=\"Lumpuhkan\"\nYes=\"Ya\"\nNo=\"Tidak\"\nAdd=\"Tambah\"\nRemove=\"Buang\"\nRename=\"Nama Semula\"\nInteract=\"Interaksi\"\nFilters=\"Penapis\"\nProperties=\"Sifat\"\nMoveUp=\"Alih ke Atas\"\nMoveDown=\"Alih ke Bawah\"\nSettings=\"Tetapan\"\nDisplay=\"Paparan\"\nName=\"Nama\"\nExit=\"Keluar\"\nMixer=\"Pengadun\"\nBrowse=\"Layar\"\nDroppedFrames=\"Bingkai Hilang %1 (%2%)\"\nProjector.Open.Program=\"Buka Projektor Program\"\nProjector.Open.Preview=\"Buka Projektor Pratonton\"\nProjector.Open.Scene=\"Buka Projektor Adegan\"\nProjector.Open.Source=\"Buka Projektor Sumber\"\nProjector.Open.Multiview=\"Buka Paparan Pelbagai\"\nProjector.Display=\"Papar: %1\"\nProjector.Window=\"Tetingkap baru\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Adegan: %1\"\nProjector.Title.Source=\"Sumber: %1\"\nProjector.Title.Multiview=\"Paparan Pelbagai\"\nProjector.ResizeWindowToContent=\"Padankan tetingkap dengan konten\"\nClear=\"Kosongkan\"\nRevert=\"Kembali\"\nShow=\"Tunjuk\"\nHide=\"Sembunyi\"\nUnhideAll=\"Tunjuk Semua\"\nUntitled=\"Tiada Bertajuk\"\nNew=\"Baharu\"\nDuplicate=\"Duplikasi\"\nEnable=\"Benarkan\"\nDisableOSXVSync=\"Lumpuhkan V-Sync macOS\"\nResetOSXVSyncOnExit=\"Tetap semula macOS V-Sync apabila keluar\"\nHighResourceUsage=\"Pengekodan terlebih muat! Cuba rendahkan tetapan video ataupun gunakan pratetap pengekodan yang lebih pantas.\"\nTransition=\"Peralihan\"\nQuickTransitions=\"Peralihan Pantas\"\nFadeToBlack=\"Resap Gelap\"\nLeft=\"Kiri\"\nRight=\"Kanan\"\nTop=\"Atas\"\nBottom=\"Bawah\"\nReset=\"Tetap Semula\"\nHours=\"Jam\"\nMinutes=\"Minit\"\nSeconds=\"Saat\"\nDeprecated=\"Telah lapuk\"\nReplayBuffer=\"Penimbal Main Semula\"\nExport=\"Eksport\"\nCopy=\"Salin\"\nPaste=\"Tampal\"\nPasteReference=\"Tampal (rujukan)\"\nPasteDuplicate=\"Tampal (Duplikasi)\"\nRemuxRecordings=\"Rakaman Remux\"\nNext=\"Maju\"\nBack=\"Undur\"\nDefaults=\"Lalai\"\nRestoreDefaults=\"Lalai\"\nHideMixer=\"Sembunyi dalam Pengadun\"\nTransitionOverride=\"Pembatalan Peralihan\"\nShowTransition=\"Tunjuk Peralihan\"\nHideTransition=\"Sembunyi Peralihan\"\nNone=\"Tiada\"\nStudioMode.Preview=\"Pratonton\"\nStudioMode.PreviewSceneName=\"Pratonton: %1\"\nShowInMultiview=\"Tunjuk dalam Pandangan Berbilang\"\nVerticalLayout=\"Bentangan Menegak\"\nGroup=\"Kumpulan\"\nDoNotShowAgain=\"Jangan tunjuk lagi\"\nDefault=\"(Lalai)\"\nCalculating=\"Mengira...\"\nFullscreen=\"Skrin penuh\"\nWindowed=\"Bertetingkap\"\nRefreshBrowser=\"Segar semula\"\nAspectRatio=\"Nisbah Bidang <b>%1:%2</b>\"\nLockVolume=\"Kunci Volum\"\nLogViewer=\"Pelihat Log\"\nShowOnStartup=\"Tunjuk pada permulaan\"\nOpenFile=\"Buka fail\"\nAddScene=\"Tambah adegan\"\nAddSource=\"Tambah Sumber\"\nRemoveScene=\"Buang Adegan Terpilih\"\nRemoveSource=\"Buang Sumber Terpilih\"\nMoveSceneUp=\"Alih Adegan ke Atas\"\nMoveSceneDown=\"Alih Adegan ke Bawah\"\nMoveSourceUp=\"Alih Sumber ke Atas\"\nMoveSourceDown=\"Alih Sumber ke Bawah\"\nSourceProperties=\"Buka Sifat Sumber\"\nSourceFilters=\"Buka Penapis Sumber\"\nMixerToolbarMenu=\"Menu Pengadun Audio\"\nSceneFilters=\"Buka Penapis Adegan\"\nList=\"Senarai\"\nAutomatic=\"Automatik\"\nComingSoon=\"Akan Datang\"\nPluginsFailedToLoad.Title=\"Ralat Muat Pemalam\"\nPluginsFailedToLoad.Text=\"Pemalam OBS berikut gagal dimuatkan:\\n\\n%1\\nSila kemas kini atau buang pemalam tersebut.\"\nAlreadyRunning.Title=\"OBS sedang dijalankan\"\nAlreadyRunning.Text=\"OBS sedang dijalankan! Melainkan anda mahu membuatnya, sila matikan apa jua kejadian OSB sebelum cuba menjalankan kejadian yang baharu. Jika anda telah menetapkan OBS terus diminimumkan ke dalam talam sistem, sila periksa sama ada ia masih ada di sana.\"\nAlreadyRunning.LaunchAnyway=\"Lancar Jua\"\nCrashHandling.Dialog.Title=\"OBS Studio Dikesan Terhenti\"\nCrashHandling.Labels.Text=\"OBS Studio tidak ditutup dengan baik.\\n\\nJalankan dalam Mod Selamat (pemalam pihak ketiga, skrip dan WebSockets dilumpuhkan)?\"\nCrashHandling.Labels.PrivacyNotice=\"Anda juga boleh memilih untuk memuat-naik laporan kerosakan terbaharu secara automatik ke OBSProject.<br /><br />Sila baca <a href='https://obsproject.com/privacy-policy'>Dasar Privasi</a> sebelum memuat-naik sebarang fail dan beri perhatian khusus kepada bahagian berkenaan muat-naik fail.\"\nCrashHandling.Checkbox.SendReport=\"Saya telah membaca dasar privasi dan persetujuan untuk muat-naik.\"\nCrashHandling.Buttons.LaunchSafe=\"Jalankan dalam Mod Selamat\"\nCrashHandling.Buttons.LaunchNormal=\"Jalankan dalam Mod Biasa\"\nCrashHandling.Errors.UploadJSONError=\"Ralat berlaku semasa cuba memuat-naik log kerosakan sistem terkini. Sila cuba lagi.\"\nCrashHandling.Errors.Title=\"Ralat Memuat-naik Log Kerosakan\"\nSafeMode.Restart=\"Adakah anda mahu mulakan OBS dalam Mod Selamat (pemalam pihak ketiga, skrip, dan WebSockets dimatikan)? \"\nSafeMode.RestartNormal=\"Anda pasti mahu mulakan semula OBS dalam Mod Biasa?\"\nChromeOS.Title=\"Platform Tidak Disokong\"\nChromeOS.Text=\"OBS nampaknya telah berjalan di dalam satu bekas ChromeOS. Platform ini malangnya tidak disokong.\"\nWine.Title=\"Wine dikesan\"\nWine.Text=\"Menggunakan OBS di dalam Wine adalah tidak disokong, dan banyak fungsi seperti tangkapan atau sumber peranti tidak akan berfungsi atau pun hanya berfungsi dalam kadar yang terhad.<br><br>IAmat disyorkan untuk menggunakan versi biasa OBS, contohnya <a href='https://flathub.org/apps/details/com.obsproject.Studio'>versi Flatpak kami</a> atau pun pakej sistem operasi anda.\"\nDockCloseWarning.Title=\"Menutup Tetingkap Boleh Labuh\"\nDockCloseWarning.Text=\"Anda baru sahaja menutup satu tetingkap boleh labuh. Jika anda mahu tunjuk ia sekali lagi, guna menu Labuh dalam palang menu.\"\nExtraBrowsers=\"Labuh Pelayar Suai\"\nExtraBrowsers.Info=\"Tambah labuh dengan memberi satu nama dan URL, kemudian klik Terap atau Tutup untuk membuka labuh. Anda boleh tambah atau buang labuh pada bila-bi;a masa.\"\nExtraBrowsers.DockName=\"Nama Labuh\"\nAuth.Authing.Title=\"Mengesahihkan...\"\nAuth.Authing.Text=\"Mengesahihkan dengan %1, tunggu sebentar...\"\nAuth.AuthFailure.Title=\"Pengesahihan Gagal\"\nAuth.AuthFailure.Text=\"Gagal disahihkan dengan %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Pengesahihan Diperlukan\"\nAuth.InvalidScope.Text=\"Keperluan pengesahihan untuk %1 telah berubah. Sesetengah fitur mungkin tidak tersedia.\"\nAuth.LoadingChannel.Title=\"Memuatkan maklumat saluran...\"\nAuth.LoadingChannel.Text=\"Memuatkan maklumat saluran untuk %1, tunggu sebentar...\"\nAuth.LoadingChannel.Error=\"Tidak memperoleh maklumat saluran.\"\nAuth.ChannelFailure.Title=\"Gagal memuatkan saluran\"\nAuth.ChannelFailure.Text=\"Gagal memuatkan maklumat saluran untuk %1\\n\\n%2: %3\"\nAuth.Chat=\"Sembang\"\nAuth.StreamInfo=\"Maklumat Strim\"\nTwitchAuth.Stats=\"Stat Twitch\"\nTwitchAuth.Feed=\"Suapan Aktiviti Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Tidak dapat menanya kunci strim\"\nTwitchAuth.TwoFactorFail.Text=\"OBS gagal bersambung dengan akaun Twitch anda. Sila pastikan pengesahihan dua-faktor telah ditetapkan dalam <a href='https://www.twitch.tv/settings/security'>tetapan keselamatan Twitch</a> anda kerana ia diperlukan semasa penstriman.\"\nRestreamAuth.Channels=\"Strim Semula Saluran\"\nCopy.Filters=\"Salin Penapis\"\nPaste.Filters=\"Tampal Penapis\"\nBrowserPanelInit.Title=\"Mengasalkan Pelayar...\"\nBrowserPanelInit.Text=\"Mengasalkan pelayar, tunggu sebentar...\"\nBandwidthTest.Region=\"Rantau\"\nBandwidthTest.Region.US=\"Amerika Syarikat\"\nBandwidthTest.Region.EU=\"Eropah\"\nBandwidthTest.Region.Other=\"Lain-lain\"\nBasic.AutoConfig=\"Bestari Auto-Konfigurasi\"\nBasic.AutoConfig.ApplySettings=\"Terap Tetapan\"\nBasic.AutoConfig.StartPage=\"Maklumat Penggunaan\"\nBasic.AutoConfig.StartPage.SubTitle=\"Nyatakan tujuan program digunakan\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimumkan untuk penstriman, rakaman tujuan kedua\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimumkan untuk rakaman, Saya tidak mahu menstrim\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Saya cuma guna kamera maya\"\nBasic.AutoConfig.VideoPage=\"Tetapan Video\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Nyatakan tetapan video yang mahu digunakan\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Guna Semasa (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Paparan %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Guna Semasa (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Sama ada 60 atau 30, tetapi lebih suka 60 jika boleh\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Sama ada 60 atau 30, tetapi lebih gemar resolusi tinggi\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Perhatian: Resolusi kanvas (tapak) tidak semestinya sama dengan resolusi penstriman atau rakaman anda. Resolusi strim/rakaman sebenar anda akan turunkan berbandingkan resolusi kanvas untuk mengurangkan penggunaan sumber atau keperluan kadar bit.\"\nBasic.AutoConfig.StreamPage=\"Maklumat Strim\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Sila masukkan maklumat strim anda\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Sambung Akaun (disarankan)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Putuskan Akaun\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Putuskan Akaun?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Perubahan ini akan diterap serta-merta. Anda pasti mahu putuskan akaun anda?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Dapatkan Kunci Strim\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Maklumat Lanjut\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Guna Kunci Strim\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Guna Kunci Strim (lanjutan)\"\nBasic.AutoConfig.StreamPage.Service=\"Perkhidmatan\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Tunjuk Semua...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Suai...\"\nBasic.AutoConfig.StreamPage.Server=\"Pelayan\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Kunci Strim\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: masukkan frasa laluan penyulitan.\\nRTMP: masukkan kunci yang disediakan oleh perkhidmatan.\\nSRT: masukkan streamid jika perkhidmatan telah menggunakannya.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kunci Pengekod\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Akaun bersambung\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Anggar kadar bit menerusi ujian lebar jalur (ia mengambil masa beberapa minit)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Utamakan pengekodan perkakasan\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Pengekodan Perkakasan mengelakkan penggunaan CPU yang banyak, tetapi mungkin memerlukan lebih kadar bit untuk memperolehi tahap kualiti yang serupa.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Amaran strim\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Ujian lebar jalur cuba menstrim data video secara rawak tanpa audio ke dalam saluran anda. Jika anda boleh, disarankan matikan buat sementara penyimpanan video-video strim dan tetapkan strim menjadi persendirian sehingga ujian selesai. Teruskan?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Cubaan %1\"\nBasic.AutoConfig.TestPage=\"Keputusan\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Program kini melakukan satu set ujian untuk menganggar tetapan ideal\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Ujian selesai\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Melakukan ujian lebar jalur, proses ini mengambil masa beberapa minit...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Tiada hasil keluar bagi servis protokol ini dijumpai\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Menyambung ke: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Gagal bersambung dengan mana-mana pelayan, sila periksa sambungan internet anda dan cuba lagi.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Menguji lebar jalur untuk: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Menguji pengekod strim, ia mungkin mengambil masa seminit...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Menguji pengekod rakaman, ia mungkin mengambil masa seminit...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Gagal memulakan pengekod\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Menguji %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Pengekod Penstriman\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Pengekod Rakaman\"\nBasic.AutoConfig.TestPage.Result.Header=\"Program telah menentukan tetapan anggaran ini adalah sesuai untuk anda:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Untuk menggunakan tetapan ini, klik Terap Tetapan. Untuk mengkonfigur semula bestari dan cuba sekali lagi, klik Undur. Untuk mengkonfigur tetapan secara manual, klik Batal dan buka Tetapan.\"\nBasic.AutoConfig.Info=\"Bestari auto-konfigurasi akan menentukan tetapan terbaik berdasarkan spesifikasi komputer dan kelajuan internet anda.\"\nBasic.AutoConfig.RunAnytime=\"Proses ini boleh dijalankan bila-bila masa melalui menu Alat.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Strim (Berskala) Resolusi\"\nBasic.Stats=\"Statistik\"\nBasic.Stats.CPUUsage=\"Penggunaan CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Ruang cakera yang ada\"\nBasic.Stats.MemoryUsage=\"Penggunaan Ingatan\"\nBasic.Stats.AverageTimeToRender=\"Masa purata untuk mengemas gabung bingkai\"\nBasic.Stats.SkippedFrames=\"Bingkai dilangkau kerana sela pengekodan\"\nBasic.Stats.MissedFrames=\"Bingkai-bingkai terlepas kerana sela pengekodan\"\nBasic.Stats.Output.Stream=\"Strim\"\nBasic.Stats.Output.Recording=\"Rakaman\"\nBasic.Stats.Status.Recording=\"Merakam\"\nBasic.Stats.Status.Live=\"LANGSUNG\"\nBasic.Stats.Status.Reconnecting=\"Menyambung semula\"\nBasic.Stats.Status.Inactive=\"Tidak Aktif\"\nBasic.Stats.Status.Active=\"Aktif\"\nBasic.Stats.DroppedFrames=\"Bingkai Terlepas (Rangkaian)\"\nBasic.Stats.MegabytesSent=\"Jumlah Output Data\"\nBasic.Stats.Bitrate=\"Kadar Bit\"\nBasic.Stats.DiskFullIn=\"Cakera penuh dalam tempoh (kira-kira)\"\nBasic.Stats.DiskFullIn.Text=\"%1 Jam, %2 Minit\"\nBasic.Stats.ResetStats=\"Tetap Semula Stat\"\nResetUIWarning.Title=\"Anda pasti mahu menetap semula UI?\"\nResetUIWarning.Text=\"Penetapan semula UI akan menyembunyikan labuh tambahan. Anda perlu menunjukkan semula labuh-labuh ini melalui menu Labuh sekiranya anda mahu ia kelihatan.\\n\\nAnda pasti mahu menetap semula UI?\"\nUpdater.Title=\"Kemas kini baharu telah tersedia\"\nUpdater.Text=\"Terdapat satu kemas kini baharu:\"\nUpdater.UpdateNow=\"Kemas Kini Sekarang\"\nUpdater.RemindMeLater=\"Ingatkan saya Kemudian\"\nUpdater.Skip=\"Langkau Versi\"\nUpdater.NoUpdatesAvailable.Title=\"Tiada kemas kini tersedia\"\nUpdater.NoUpdatesAvailable.Text=\"Tiada kemas kini tersedia buat masa ini\"\nUpdater.BranchNotFound.Title=\"Kemaskini Saluran Telah Dibuang\"\nUpdater.BranchNotFound.Text=\"Saluran yang anda pilih tidak lagi wujud, OBS telah ditetapkan kepada asal.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Semakan Integriti Tidak Tersedia\"\nUpdater.RepairButUpdatesAvailable.Text=\"Pemeriksaan integriti fail yang mungkin bagi versi terkini yang ada. Guna Bantuan → Semak Kemas Kini untuk mengesahkan dan mengemas kini pemasangan OBS anda.\"\nUpdater.RepairConfirm.Title=\"Sahkan Semakan Integriti\"\nUpdater.RepairConfirm.Text=\"Permulaan semakan integriti akan mengimbas pemasangan OBS anda jika berlaku kerosakan dan memuat turun kembali fail-fail yang telah rosak/diubah suai. Ia mengambil masa.\\n\\nAnda pasti mahu teruskan?\"\nUpdater.FailedToLaunch=\"Gagal melancarkan pengemas kini\"\nQuickTransitions.SwapScenes=\"Silih Adegan Pratonton/Program Selepas Peralihan\"\nQuickTransitions.SwapScenesTT=\"Silih adegan-adegan pratonton dan program selepas peralihan (jika adegan asal program masih wujud).\\nTindakan ini tidak akan membuat asal sebarang perubahan yang mungkin telah dibuat kepada adegan asal program.\"\nQuickTransitions.DuplicateScene=\"Duplikasi Adegan\"\nQuickTransitions.DuplicateSceneTT=\"Ketika menyunting adegan yang sama, benarkan jelmaan/ketampakan sumber tanpa mengubah suai output program.\\nUntuk menyunting sifat-sifat sumber tanpa mengubah suai output pogram, benarkan 'Duplikasi Sumber'.\\nPenukaran nilai ini akan menetapkan semula adegan program semasa (jika ia masih wujud).\"\nQuickTransitions.EditProperties=\"Duplikasi Sumber\"\nQuickTransitions.EditPropertiesTT=\"Ketika menyunting adegan yang sama, membolehkan penyuntingan sifat-sifat sumber tanpa mengubah suai output program.\\nTindakan ini hanya boleh digunakan jika 'Duplikasi Adegan' dibenarkan.\\nSesetengah sumber (seperti sumber tangkapan atau media) tidak menyokong tetapan ini dan tidak boleh disunting secara berasingan.\\nPenukaran nilai ini akan menetapkan semula adegan program semasa (jika ia masih wujud).\\n\\nAmaran: Oleh kerana sumber-sumber ini akan diduplikasikan, ia mungkin memerlukan sumber-sumber sistem atau video tambahan.\"\nQuickTransitions.HotkeyName=\"Peralihan Pantas: %1\"\nBasic.AddTransition=\"Tambah Peralihan Boleh Konfigur\"\nBasic.RemoveTransition=\"Buang Peralihan Boleh Konfigur\"\nBasic.TransitionProperties=\"Sifat Peralihan\"\nBasic.SceneTransitions=\"Peralihan Adegan\"\nBasic.TransitionDuration=\"Tempoh\"\nBasic.TogglePreviewProgramMode=\"Mod Studio\"\nBasic.EnablePreviewProgramMode=\"Benarkan Mod Studio\"\nBasic.DisablePreviewProgramMode=\"Lumpuhkan Mod Studio\"\nUndo.Undo=\"Buat Asal\"\nUndo.Redo=\"Buat Semula\"\nUndo.Add=\"Tambah '%1'\"\nUndo.Delete=\"Padam '%1'\"\nUndo.Rename=\"Nama Semula '%1'\"\nUndo.SceneCollection.Switch=\"Alih ke '%1'\"\nUndo.Item.Undo=\"Buat Asal %1\"\nUndo.Item.Redo=\"Buat Semula %1\"\nUndo.Sources.Multi=\"Padam %1 Sumber\"\nUndo.Filters=\"Perubahan Penapis pada '%1'\"\nUndo.Filters.Paste.Single=\"Tampal Penapis '%1' ke '%2'\"\nUndo.Filters.Paste.Multiple=\"Salin Penapis dari '%1' ke '%2'\"\nUndo.Transform=\"Jelma sumber Dalam '%1'\"\nUndo.Transform.Paste=\"Tampal Penjelmaan dalam '%1'\"\nUndo.Transform.Rotate=\"Putaran Dalam '%1'\"\nUndo.Transform.Reset=\"Tetap Semula Jelma Dalam '%1'\"\nUndo.Transform.HFlip=\"Kalih Mengufuk Dalam '%1'\"\nUndo.Transform.VFlip=\"Kalih Menegak Dalam '%1'\"\nUndo.Transform.FitToScreen=\"Suai Muat Skrin Dalam '%1'\"\nUndo.Transform.StretchToScreen=\"Regang ikut Skrin dalam '%1'\"\nUndo.Transform.Center=\"Tengah Skrin dalam '%1'\"\nUndo.Transform.VCenter=\"Tengah Menegak ke Skrin dalam '%1'\"\nUndo.Transform.HCenter=\"Tengah Mengufuk ke Skrin dalam '%1'\"\nUndo.Volume.Change=\"Perubahan Volum pada '%1'\"\nUndo.Volume.Mute=\"Bisukan '%1'\"\nUndo.Volume.Unmute=\"Suarakan '%1'\"\nUndo.Balance.Change=\"Perubahan Imbangan Audio dalam '%1'\"\nUndo.SyncOffset.Change=\"Perubahan Ofset Segerak Audio pada '%1'\"\nUndo.MonitoringType.Change=\"Ubah Pemantauan Audio dalam '%1'\"\nUndo.Mixers.Change=\"Ubah Pengadun Audio dalam '%1'\"\nUndo.ForceMono.On=\"Benarkan Paksa Mono pada '%1'\"\nUndo.ForceMono.Off=\"Lumpuhkan Paksa Mono pada '%1'\"\nUndo.Properties=\"Perubahan Sifat pada '%1'\"\nUndo.Scene.Duplicate=\"Duplikasi Adegan '%1'\"\nUndo.ShowTransition=\"Tunjuk Peralihan pada '%1'\"\nUndo.HideTransition=\"Sembunyi Peralihan pada '%1'\"\nUndo.ShowSceneItem=\"Tunjuk '%1' dalam '%2'\"\nUndo.HideSceneItem=\"Sembunyi '%1' dalam '%2'\"\nUndo.ReorderSources=\"Tertib semula sumber dalam '%1'\"\nUndo.MoveUp=\"Alih '%1' naik dalam '%2'\"\nUndo.MoveDown=\"Alih '%1' turun dalam '%2'\"\nUndo.MoveToTop=\"Alih '%1' ke teratas dalam '%2'\"\nUndo.MoveToBottom=\"Alih '%1' ke terbawah dalam '%2'\"\nUndo.PasteSource=\"Tampal Sumber dalam '%1'\"\nUndo.PasteSourceRef=\"Tampal Rujukan Sumber dalam '%1'\"\nUndo.GroupItems=\"Kumpul Item-item menjadi '%1'\"\nTransitionNameDlg.Text=\"Sila masukkan nama peralihan\"\nTransitionNameDlg.Title=\"Nama Peralihan\"\nTitleBar.SafeMode=\"MOD SELAMAT\"\nTitleBar.PortableMode=\"Mod Mudah Alih\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Adegan\"\nNameExists.Title=\"Nama sudah wujud\"\nNameExists.Text=\"Nama ini sudah digunakan.\"\nNoNameEntered.Title=\"Sila masukkan satu nama yang sah\"\nNoNameEntered.Text=\"Anda tidak boleh biarkan ruang nama kosong.\"\nConfirmStart.Title=\"Mulakan Strim?\"\nConfirmStart.Text=\"Anda pasti mahu memulakan strim?\"\nConfirmStop.Title=\"Henti Strim?\"\nConfirmStop.Text=\"Anda pasti mahu menghentikan strim?\"\nConfirmStopRecord.Title=\"Henti Rakaman?\"\nConfirmStopRecord.Text=\"Anda pasti mahu menghentikan rakaman?\"\nConfirmBWTest.Title=\"Mula Ujian Lebar Jalur?\"\nConfirmBWTest.Text=\"OBS anda telah dikonfigur dalam mod ujian lebar jalur. Mod ini membolehkan pengujian rangkaian tanpa saluran anda bersiaran langsung. Setelah selesai menguji, anda perlu lumpuhkannya supaya para penonton dapat melihat strim anda.\\n\\nAnda pasti mahu teruskan?\"\nConfirmExit.Title=\"Output Aktif\"\nConfirmExit.Text=\"OBS masih aktif pada masa ini. Semua strim/rakaman akan ditutup.\"\nConfirmRemove.Title=\"Sahkan Pembuangan\"\nConfirmRemove.Text=\"Anda pasti mahu membuang '%1'?\"\nConfirmRemove.TextMultiple=\"Anda pasti mahu membuang %1 item?\"\nConfirmReset.Title=\"Tetap Semula Sifat\"\nConfirmReset.Text=\"Anda pasti mahu menetap semula sifat semasa ke nilai lalainya?\"\nOutput.StartStreamFailed=\"Gagal memulakan penstriman\"\nOutput.StartRecordingFailed=\"Gagal memulakan rakaman\"\nOutput.StartReplayFailed=\"Gagal memulakan penimbal main semula\"\nOutput.StartVirtualCamFailed=\"Gagal memulakan kamera maya\"\nOutput.StartFailedGeneric=\"Gagal memulakan output. Sila periksa log untuk perincian/\\n\\nPerhatian: Jika anda menggunakan pengekod NVENC atau AMD, pastikan pemacu video anda adalah terkini.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Tidak dapat menyimpan main semula ketika dijeda\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Amaran: Main semula tidak dapat disimpan ketika rakaman dijeda.\"\nOutput.ConnectFail.Title=\"Gagal bersambung\"\nOutput.ConnectFail.BadPath=\"Laluan atau URL Sambungan tidak sah. Sila periksa tetapan anda untuk mengesahkannya.\"\nOutput.ConnectFail.ConnectFailed=\"Gagal bersambung dengan pelayan\"\nOutput.ConnectFail.InvalidStream=\"Tidak dapat mencapai saluran atau kunci strim yang dinyatakan, sila semak semula kunci strim anda. Jika ia betul, kemungkinan ada masalah sambungan dengan pelayan.\"\nOutput.ConnectFail.HdrDisabled=\"Pengeluaran HDR dinyahaktif bagi pengeluaran ini.\"\nOutput.ConnectFail.Error=\"Satu ralat tidak dijangka berlaku ketika cuba bersambung dengan pelayan. Maklumat lanjut ada dalam fail log.\"\nOutput.ConnectFail.Disconnected=\"Terputus dengan pelayan.\"\nOutput.StreamEncodeError.Title=\"Ralat pengekodan\"\nOutput.StreamEncodeError.Msg=\"Satu ralat pengekod telah muncul ketika menstrim.\"\nOutput.StreamEncodeError.Msg.LastError=\"Satu ralat pengekod telah muncul ketika menstrim:<br><br>%1\"\nOutput.RecordFail.Title=\"Gagal memulakan rakaman\"\nOutput.RecordFail.Unsupported=\"Format output sama ada tidak disokong atau tidak menyokong lebih daripada satu trek audio. Sila semak tetapan anda dan cuba lagi.\"\nOutput.RecordNoSpace.Title=\"Ruang cakera tidak mencukupi\"\nOutput.RecordNoSpace.Msg=\"Ruang cakera tidak cukup untuk meneruskan rakaman.\"\nOutput.RecordError.Title=\"Ralat rakaman\"\nOutput.RecordError.Msg=\"Satu ralat yang tidak dinyatakan berlaku ketika rakaman.\"\nOutput.RecordError.EncodeErrorMsg=\"Satu ralat pengekod telah muncul ketika merakam.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Satu ralat pengekod telah muncul ketika merekod:<br><br>%1\"\nOutput.BadPath.Title=\"Laluan Fail Teruk\"\nOutput.BadPath.Text=\"Laluan Rakaman terkonfigur tidak dapat dibuka. Sila periksa Laluan Rakaman anda di bawah Tetapan → Output → Rakaman.\"\nOutput.NoBroadcast.Title=\"Tiada Siaran Dikonfigur\"\nOutput.NoBroadcast.Text=\"Anda perlu sediakan siaran sebelum anda boleh memulakan penstriman.\"\nOutput.BroadcastStartFailed=\"Gagal memulakan siaran\"\nOutput.BroadcastStopFailed=\"Gagal menghentikan siaran\"\nLogUploadDialog.Title=\"Muat-naik Fail Log OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Sila baca <a href='https://obsproject.com/privacy-policy'>Dasar Privasi</a> dan bahagian mengenai muat-naik fail sebelum memuat-naik sebarang fail.\"\nLogUploadDialog.Labels.Progress=\"Muat-naik log sedang dijalankan. Sila tunggu...\"\nLogUploadDialog.Labels.Description.AppLog=\"Fail log anda telah dimuat-naik. Anda kini boleh berkongsi URL untuk tujuan penyahpepijatan atau sokongan.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Laporan ranap sistem anda telah dimuat-naik. Anda kini boleh berkongsi URL untuk tujuan penyahpepijatan.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Muat-naik\"\nLogUploadDialog.Buttons.CopyURL=\"Salin Log URL\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analisis Fail Log\"\nLogUploadDialog.Buttons.RetryButton=\"Cuba Semula\"\nLogUploadDialog.Errors.Template=\"Ralat berlaku semasa cuba memuat-naik fail:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Tiada fail untuk dimuat-naik ditemui atau fail kosong.\"\nRemux.SourceFile=\"Rakaman OBS\"\nRemux.TargetFile=\"Fail Sasaran\"\nRemux.Stop=\"Henti Remux\"\nRemux.ClearFinished=\"Kosongkan Item-item Selesai\"\nRemux.ClearAll=\"Kosongkan Semua Item\"\nRemux.OBSRecording=\"Rakaman OBS\"\nRemux.FinishedTitle=\"Remuxing selesai\"\nRemux.Finished=\"Rakaman diremux\"\nRemux.FinishedError=\"Rakaman diremux, tetapi fail mungkin tidak lengkap\"\nRemux.SelectRecording=\"Pilih Rakaman OBS...\"\nRemux.SelectTarget=\"Pilih fail sasaran...\"\nRemux.FileExistsTitle=\"Fail sasaran sudah wujud\"\nRemux.FileExists=\"Fail sasaran berikut sudah wujud. Anda mahu menggantikannya?\"\nRemux.ExitUnfinishedTitle=\"Remuxing masih berlangsung\"\nRemux.ExitUnfinished=\"Remuxing masih belum selesai, menghentikannya sekarang boleh menyebabkan fail sasaran tidak boleh digunakan.\\nAnda pasti mahu menghentikan remuxing?\"\nRemux.HelpText=\"Lepas fail ke dalam tetingkap ini untuk remux, atau pilih satu sel \\\"Rakaman OBS\\\" kosong untuk melayari satu fail.\"\nRemux.NoFilesAddedTitle=\"Tiada fail remuxing ditambah\"\nRemux.NoFilesAdded=\"Tiada fail ditambah ke remux. Lepaskan satu folder yang mengandungi satu atau lebih fail video.\"\nMissingFiles=\"Fail-fail Hilang\"\nMissingFiles.MissingFile=\"Fail Hilang\"\nMissingFiles.NewFile=\"Fail Baharu\"\nMissingFiles.HelpText=\"Sesetengah fail telah hilang semenjak anda menggunakan OBS kali terakhir.\"\nMissingFiles.Clear=\"<dikosongkan>\"\nMissingFiles.NumFound=\"Temui %1 daripada %2\"\nMissingFiles.Search=\"Gelintar Direktori...\"\nMissingFiles.SelectFile=\"Pilih fail...\"\nMissingFiles.SelectDir=\"Pilih Folder untuk Digelintar\"\nMissingFiles.State=\"Keadaan\"\nMissingFiles.Missing=\"Hilang\"\nMissingFiles.Replaced=\"Diganti\"\nMissingFiles.Cleared=\"Dikosongkan\"\nMissingFiles.Found=\"Temui\"\nMissingFiles.AutoSearch=\"Padanan fail tambahan ditemui\"\nMissingFiles.AutoSearchText=\"OBS telah menemui padanan-padanan tambahan bagi fail-fail hilang dalam direktori tersebut. Anda mahu menambahnya?\"\nMissingFiles.NoMissing.Title=\"Semakan Fail Hilang\"\nMissingFiles.NoMissing.Text=\"Tiada fail yang hilang.\"\nMacPermissions.MenuAction=\"Lihat Keizinan Apl...\"\nMacPermissions.Title=\"Lihat Keizinan Apl\"\nMacPermissions.Description=\"OBS Studio memerlukan keizinan anda untuk menyediakan beberapa ciri. Disarankan anda membenarkan beberapa keizinan ini, tetapi ia tidak diperlukan untuk memulakan apl. Anda boleh membenarkannya nanti.\"\nMacPermissions.Description.OpenDialog=\"Anda boleh buka-semula dialog ini melalui menu OBS Studio.\"\nMacPermissions.AccessGranted=\"Capaian Diberi\"\nMacPermissions.RequestAccess=\"Pinta Capaian\"\nMacPermissions.OpenPreferences=\"Buka Keutamaan %1\"\nMacPermissions.Item.ScreenRecording=\"Rakaman Skrin\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS memerlukan keizinan ini supaya dapat menangkap skrin anda.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Keizinan ini diperlukan supaya dapat menangkap kandungan daripada kamera sesawang atau kad tangkap.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS memerlukan keizinan ini jika anda mahu menggunakan mikrofon anda atau sumber audio luaran.\"\nMacPermissions.Item.InputMonitoring=\"Pemantauan Input\"\nMacPermissions.Item.InputMonitoring.Details=\"Kebenaran ini diperlukan untuk kekunci pintas berfungsi semasa OBS berada di latar belakang.\"\nMacPermissions.Item.Accessibility=\"Kebolehcapaian\"\nMacPermissions.Item.Accessibility.Details=\"Pada pemasangan yang lebih lama, OBS mungkin disenaraikan dalam \\\"Kebolehaksesan\\\" dan bukannya \\\"Pemantauan Input\\\".\"\nMacPermissions.Continue=\"Teruskan\"\nSourceLeak.Title=\"Ralat Pembersihan Sumber\"\nSourceLeak.Text=\"Terdapat masalah ketika cuba mengubah koleksi adegan dan beberapa sumber tidak dapat dinyahmuatkan. Isu ini biasanya disebabkan oleh pemalam yang tidak dilepaskan dengan betul. Sila pastikan mana-mana pemalam yang digunakan telah dikemas kini.\\n\\nStudio OBS akan ditutup untuk menghindari apa-apa kemungkinan kerosakan data.\"\nBasic.DesktopDevice1=\"Audio Atas Meja\"\nBasic.DesktopDevice2=\"Audio Atas Meja 2\"\nBasic.Scene=\"Adegan\"\nBasic.DisplayCapture=\"Tangkapan Paparan\"\nBasic.Main.PreviewConextMenu.Enable=\"Benarkan Pratonton\"\nBasic.Main.Preview.Disable=\"Lumpuhkan Pratonton\"\nScaleFiltering=\"Penapisan Skala\"\nScaleFiltering.Bilinear=\"Dwilinear\"\nScaleFiltering.Bicubic=\"Dwikiub\"\nScaleFiltering.Area=\"Kawasan\"\nBlendingMethod=\"Kaedah Pengadunan\"\nBlendingMethod.Default=\"Lalai\"\nBlendingMethod.SrgbOff=\"SRGB Mati\"\nBlendingMode=\"Mod Adun\"\nBlendingMode.Normal=\"Biasa\"\nBlendingMode.Additive=\"Tambah\"\nBlendingMode.Subtract=\"Kurang\"\nBlendingMode.Screen=\"Skrin\"\nBlendingMode.Multiply=\"Ganda\"\nBlendingMode.Lighten=\"Cerah\"\nBlendingMode.Darken=\"Gelap\"\nDeinterlacing=\"Nyahjalin\"\nDeinterlacing.Discard=\"Singkir\"\nDeinterlacing.TopFieldFirst=\"Bahagian Atas Dahulu\"\nDeinterlacing.BottomFieldFirst=\"Bahagian Bawah Dahulu\"\nVolControl.SliderUnmuted=\"Pelungsur volum untuk '%1':\"\nVolControl.SliderMuted=\"Pelungsur volum untuk '%1': (buat masa ini dibisukan)\"\nVolControl.Mute=\"Bisukan '%1'\"\nVolControl.Properties=\"Sifat-sifat untuk '%1'\"\nVolControl.UnassignedWarning.Title=\"Sumber Audio Tidak Diassigned\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" tidak ditetapkan kepada mana-mana trek audio dan ia tidak akan mengeluarkan suara dalam stream atau rakaman.\\n\\nUntuk menetapkan sumber audio kepada trek, buka Sifat Audio Lanjutan melalui menu klik kanan atau butang cog dalam bar alat dok pengadun.\"\nBasic.Main.AddSceneDlg.Title=\"Tambah Adegan\"\nBasic.Main.AddSceneDlg.Text=\"Sila masukkan nama adegan\"\nBasic.Main.DefaultSceneName.Text=\"Adegan %1\"\nBasic.Main.AddSceneCollection.Title=\"Tambah Koleksi Adegan\"\nBasic.Main.AddSceneCollection.Text=\"Sila masukkan nama koleksi adegan\"\nBasic.Main.RenameSceneCollection.Title=\"Nama Semula Koleksi Adegan\"\nBasic.Main.RemigrateSceneCollection.Title=\"Kemaskini Resolusi Koleksi Adegan\"\nBasic.Main.RemigrateSceneCollection.Text=\"Adakah anda mahu mengemaskini resolusi koleksi adegan \\\"%1\\\" untuk memadankan resolusi kanvas profil semasa bagi %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Tidak dapat mengemaskini koleksi resolusi adegan semasa output aktif.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Gagal untuk mengemaskini resolusi koleksi adegan. Resolusi asal tidak diketahui.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Tetapan semula tidak boleh dilakukan: Menukar resolusi OBS gagal.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Tetapan semula tidak boleh dilakukan: Resolusi semasa sudah pun menjadi resolusi asas koleksi adegan.\"\nAddProfile.Title=\"Tambah Profil\"\nAddProfile.Text=\"Sila masukkan nama profil\"\nAddProfile.WizardCheckbox=\"Tunjuk bestari auto-konfigurasi\"\nRenameProfile.Title=\"Nama Semula Profil\"\nBasic.Main.MixerRename.Title=\"Nama Semula Sumber Audio\"\nBasic.Main.MixerRename.Text=\"Sila masukkan nama sumber audio\"\nBasic.Main.PreviewDisabled=\"Pratonton buat masa ini dilumpuhkan\"\nBasic.SourceSelect=\"Cipta/Pilih Sumber\"\nBasic.SourceSelect.CreateNew=\"Cipta baharu\"\nBasic.SourceSelect.AddExisting=\"Tambah Sedia Ada\"\nBasic.SourceSelect.AddVisible=\"Biar sumber tampak\"\nBasic.Main.Sources.Visibility=\"Ketampakan\"\nBasic.Main.Sources.VisibilityDescription=\"Kawal ketampakan '%1' dalam kanvas\"\nBasic.Main.Sources.Lock=\"Kunci\"\nBasic.Main.Sources.LockDescription=\"Kunci kedudukan dan skala '%1' dalam kanvas\"\nBasic.PropertiesWindow=\"Sifat-sifat untuk '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (auto-pilih: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Pilih warna\"\nBasic.PropertiesWindow.SelectFont=\"Pilih fon\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Pilih satu Fon\"\nBasic.PropertiesWindow.ConfirmTitle=\"Tetapan Berubah\"\nBasic.PropertiesWindow.Confirm=\"Masih terdapat perubahan belum disimpan. Anda pasti mahu menyimpannya?\"\nBasic.PropertiesWindow.NoProperties=\"Tiada sifat tersedia\"\nBasic.PropertiesWindow.AddFiles=\"Tambah Fail\"\nBasic.PropertiesWindow.AddDir=\"Tambah Direktori\"\nBasic.PropertiesWindow.AddURL=\"Tambah Laluan/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Tambah direktori ke '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Tambah fail ke '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Tambah masukan ke '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Sunting masukan daripada '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Nilai FPS Mudah\"\nBasic.PropertiesView.FPS.Rational=\"Nilai-nilai FPS Pecahan\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Julat FPS Sah:\"\nBasic.PropertiesView.UrlButton.Text=\"Buka pautan ini dalam pelayar sesawang lalai anda?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Buka URL\"\nBasic.InteractionWindow=\"Berinteraksi dengan '%1'\"\nBasic.StatusBar.Reconnecting=\"Terputus, bersambung semula dalam tempoh %2 saat(percubaan %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Cubaan menyambung semula... (percubaan %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Sambungan semula berjaya\"\nBasic.StatusBar.Delay=\"Lengah (%1 saat)\"\nBasic.StatusBar.DelayStartingIn=\"Lengah (mula dalam tempoh %1 saat)\"\nBasic.StatusBar.DelayStoppingIn=\"Lengah (berhenti dalam tempoh %1 saat)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Lengah (berhenti dalam tempoh %1 saat, bermula dalam tempoh %2 saat)\"\nBasic.StatusBar.RecordingSavedTo=\"Rakaman telah disimpan ke '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Penimbal main semula telah disimpan ke '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Tangkap layar telah disimpan ke '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Auto rakaman diremux ke '%1'\"\nBasic.Filters=\"Penapis\"\nBasic.Filters.AsyncFilters=\"Penapis Audio/Video\"\nBasic.Filters.AudioFilters=\"Penapis Audio\"\nBasic.Filters.EffectFilters=\"Penapis Kesan\"\nBasic.Filters.Title=\"Penapis untuk %1\"\nBasic.Filters.AddFilter.Title=\"Nama penapis\"\nBasic.Filters.AddFilter.Text=\"Sila nyatakan nama penapis\"\nBasic.TransformWindow=\"Jelmaam Item Adegan\"\nBasic.TransformWindow.Position=\"Kedudukan\"\nBasic.TransformWindow.PositionX=\"Kedudukan X\"\nBasic.TransformWindow.PositionY=\"Kedudukan Y\"\nBasic.TransformWindow.Rotation=\"Putaran\"\nBasic.TransformWindow.Size=\"Saiz\"\nBasic.TransformWindow.Width=\"Lebar\"\nBasic.TransformWindow.Height=\"Tinggi\"\nBasic.TransformWindow.Alignment=\"Jajaran Kedudukan\"\nBasic.TransformWindow.BoundsType=\"Jenis Kotak Pembatas\"\nBasic.TransformWindow.BoundsAlignment=\"Jajaran dalam Kotak Pembatas\"\nBasic.TransformWindow.Bounds=\"Saiz Kotak Pembatas\"\nBasic.TransformWindow.BoundsWidth=\"Lebar Kotak Pembatas\"\nBasic.TransformWindow.BoundsHeight=\"Tinggi Kotak Pembatas\"\nBasic.TransformWindow.CropToBounds=\"Pangkas ke Kotak Sempadan\"\nBasic.TransformWindow.Crop=\"Kerat\"\nBasic.TransformWindow.CropLeft=\"Kerat Kiri\"\nBasic.TransformWindow.CropRight=\"Kerat Kanan\"\nBasic.TransformWindow.CropTop=\"Kerat Atas\"\nBasic.TransformWindow.CropBottom=\"Kerat Bawah\"\nBasic.TransformWindow.Alignment.TopLeft=\"Kiri Atas\"\nBasic.TransformWindow.Alignment.TopCenter=\"Tengah Atas\"\nBasic.TransformWindow.Alignment.TopRight=\"Kanan Atas\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Kiri Tengah\"\nBasic.TransformWindow.Alignment.Center=\"Tengah\"\nBasic.TransformWindow.Alignment.CenterRight=\"Kanan Tengah\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Kiri Bawah\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Tengah Bawah\"\nBasic.TransformWindow.Alignment.BottomRight=\"Kanan Bawah\"\nBasic.TransformWindow.BoundsType.None=\"Tiada batas\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Saiz maksimum sahaja\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skala ke batas dalaman\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skala ke batas luaran\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skala ke lebar batas\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skala ke tinggi batas\"\nBasic.TransformWindow.BoundsType.Stretch=\"Regang ikut batas\"\nBasic.TransformWindow.Title=\"Sunting Jelmaan untuk '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Tiada sumber dipilih\"\nBasic.Main.AddSourceHelp.Title=\"Tidak Dapat Tambah Sumber\"\nBasic.Main.AddSourceHelp.Text=\"Anda perlu mempunyai sekurang-kurangnya 1 adegan untuk menambah satu sumber.\"\nBasic.Main.Scenes=\"Adegan\"\nBasic.Main.Sources=\"Sumber\"\nBasic.Main.Source=\"Sumber\"\nBasic.Main.Controls=\"Kawalan\"\nBasic.Main.PreparingStream=\"Menyediakan...\"\nBasic.Main.Connecting=\"Menyambung...\"\nBasic.Main.StartRecording=\"Mula Rakaman\"\nBasic.Main.StartReplayBuffer=\"Mula Penimbal Main Semula\"\nBasic.Main.SaveReplay=\"Simpan Main Semula\"\nBasic.Main.StartStreaming=\"Mula Menstrim\"\nBasic.Main.StartVirtualCam=\"Mula Kamera Maya\"\nBasic.Main.StopRecording=\"Henti Rakaman\"\nBasic.Main.PauseRecording=\"Jeda Rakaman\"\nBasic.Main.UnpauseRecording=\"Nyahjeda Rakaman\"\nBasic.Main.SplitFile=\"Pisah Fail Rakaman\"\nBasic.Main.AddChapterMarker=\"Tambah Penanda Bab (MP4 Hibrid/MOV sahaja)\"\nBasic.Main.StoppingRecording=\"Menghentikan Rakaman...\"\nBasic.Main.StopReplayBuffer=\"Henti Penimbal Main Semula\"\nBasic.Main.StoppingReplayBuffer=\"Menghentikan Penimbal Main Semula...\"\nBasic.Main.SetupBroadcast=\"Urus Siaran\"\nBasic.Main.StopStreaming=\"Henti Menstrim\"\nBasic.Main.StopBroadcast=\"Henti Siaran\"\nBasic.Main.AutoStopEnabled=\"(Auto Henti)\"\nBasic.Main.StoppingStreaming=\"Menghentikan Penstriman...\"\nBasic.Main.ForceStopStreaming=\"Henti Menstrim (singkir lengah)\"\nBasic.Main.ShowContextBar=\"Tunjuk Palang Alat Sumber\"\nBasic.Main.HideContextBar=\"Sembunyi Palang Alat Sumber\"\nBasic.Main.StopVirtualCam=\"Henti Kamera Maya\"\nBasic.Main.Group=\"Kumpulan %1\"\nBasic.Main.GroupItems=\"Kumpul Item-item Terpilih\"\nBasic.Main.Ungroup=\"Nyahkumpul\"\nBasic.Main.GridMode=\"Mod Grid\"\nBasic.Main.ListMode=\"Mod Senarai\"\nBasic.Main.VirtualCamConfig=\"Konfigur Kamera Maya\"\nBasic.VCam.VirtualCamera=\"Kamera Maya\"\nBasic.VCam.OutputType=\"Jenis Output\"\nBasic.VCam.OutputSelection=\"Pemilihan Output\"\nBasic.VCam.OutputType.Program=\"Program (Lalai)\"\nBasic.VCam.OutputSelection.NoSelection=\"Tiada pemilihan untuk jenis output ini\"\nBasic.VCam.RestartWarning=\"Kamera maya akan dimulakan semula untuk menerapkan perubahan ini\"\nBasic.MainMenu.File=\"&Fail\"\nBasic.MainMenu.File.Export=\"&Eksport\"\nBasic.MainMenu.File.ShowRecordings=\"Tunjuk &Rakaman\"\nBasic.MainMenu.File.Remux=\"For&mat Rakaman\"\nBasic.MainMenu.File.Settings=\"T&etapan\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Tunjuk Folder Tetapan\"\nBasic.MainMenu.File.ShowProfileFolder=\"Tunjuk Folder Profil\"\nBasic.MainMenu.File.ShowMissingFiles=\"Periksa Fail yang Hilang\"\nBasic.MainMenu.File.Exit=\"&Keluar\"\nBasic.MainMenu.Edit=\"&Sunting\"\nBasic.MainMenu.Edit.Undo=\"Buat &Asal\"\nBasic.MainMenu.Edit.Redo=\"Buat &Semula\"\nBasic.MainMenu.Edit.LockPreview=\"&Kunci Pratonton\"\nBasic.MainMenu.Edit.Scale=\"Pratonton Pen&skalaan\"\nBasic.MainMenu.Edit.Scale.Window=\"Skala ikut Tetingkap\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Kanvas (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Berskala (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Zoom Ke Dalam\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Zoom Keluar\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Set Semula Zoom\"\nBasic.MainMenu.Edit.Transform=\"&Jelma\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Sunting Jelmaan...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Salin Jelmaan\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Tampal Jelmaan\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"T&etap Semula Jelmaan\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Putar 90 darjah Ikut Jam\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Putar 90 darjah Lawan Jam\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Putar 180 darjah\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Kalih Men&gufuk\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Kalih Meneg&ak\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Suai &Muat dengan Skrin\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Regangkan ke &skrin\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Tengah skrin\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Tengah secara Menegak\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Tengah secara Mengufuk\"\nBasic.MainMenu.Edit.Order=\"Te&rtib\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Alih ke &Atas\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Alih ke &Bawah\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Alih ke Te&ratas\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Alih ke Ter&bawah\"\nBasic.MainMenu.Edit.AdvAudio=\"Sifat-sifat Audio L&anjutan\"\nBasic.MainMenu.View=\"&Lihat\"\nBasic.MainMenu.View.Toolbars=\"Palang A&lat\"\nBasic.MainMenu.View.ListboxToolbars=\"Labuh Palang Alat\"\nBasic.MainMenu.View.ContextBar=\"Palang Alat Sumber\"\nBasic.MainMenu.View.SourceIcons=\"&Ikon-ikon Sumber\"\nBasic.MainMenu.View.StatusBar=\"Palang &Status\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Antara Muka Skrin Penuh\"\nBasic.MainMenu.View.ResetUI=\"&Tetap Semula UI\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Sentiasa Di Atas\"\nBasic.MainMenu.View.SceneListMode=\"Mod Senarai Adegan\"\nBasic.MainMenu.Docks=\"&Labuh\"\nBasic.MainMenu.Docks.ResetDocks=\"&Tetap Semula Labuhan\"\nBasic.MainMenu.Docks.LockDocks=\"&Kunci Labuhan\"\nBasic.MainMenu.Docks.SideDocks=\"Labuh Tinggi-&Penuh\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Labuh Pelayar Suai...\"\nBasic.MainMenu.Export=\"Eksport...\"\nBasic.MainMenu.SceneCollection=\"Koleksi Ade&gan\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Import Profil\"\nBasic.MainMenu.Profile.Export=\"Eksport Profil\"\nBasic.MainMenu.SceneCollection.Import=\"Import Koleksi Adegan\"\nBasic.MainMenu.SceneCollection.Export=\"Eksport Koleksi Adegan\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Tetapkan Semula Resolusi Pangkalan\"\nBasic.MainMenu.SceneCollection.Migrate=\"Tetapkan Resolusi Asas\"\nBasic.MainMenu.Profile.Exists=\"Profil sudah wujud\"\nBasic.MainMenu.Tools=\"Ala&tan\"\nBasic.MainMenu.Help=\"&Bantuan\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal Bantuan\"\nBasic.MainMenu.Help.Website=\"Lawati Tapak Sesa&wang\"\nBasic.MainMenu.Help.Discord=\"Sertai Pelayan &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Apa Yang Terbaharu\"\nBasic.MainMenu.Help.Logs=\"Fail &Log\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"&Tunjuk Fail Log\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Muat &Naik Fail Log Semasa\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Muat-naik &Log File Terdahulu\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Lihat Log Semasa\"\nBasic.MainMenu.Help.ReleaseNotes=\"Nota Keluaran\"\nBasic.MainMenu.Help.CheckForUpdates=\"Periksa Kemas Kini\"\nBasic.MainMenu.Help.Repair=\"Semak Integriti Fail\"\nBasic.MainMenu.Help.RestartSafeMode=\"Mula Semula dalam Mod Selamat\"\nBasic.MainMenu.Help.RestartNormal=\"Mula Semula dalam Mod Biasa\"\nBasic.MainMenu.Help.CrashLogs=\"&Laporan Kerosakan\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"&Tunjuk Laporan Kerosakan\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Muat Naik Laporan Kerosakan Te&rakhir\"\nBasic.MainMenu.Help.About=\"Perih&al\"\nBasic.Settings.ProgramRestart=\"Program mesti dimulakan semula supaya tetapan ini berkesan.\"\nBasic.Settings.ConfirmTitle=\"Sahkan Perubahan\"\nBasic.Settings.Confirm=\"Anda mempunyai perubahan yang tidak disimpan. Simpan perubahan?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 mengawal beberapa tetapan strim anda\"\nBasic.Settings.General=\"Am\"\nBasic.Settings.General.Language=\"Bahasa\"\nBasic.Settings.General.Updater=\"Kemaskini\"\nBasic.Settings.General.UpdateChannel=\"Kemas Kini Saluran\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Dilumpuhkan)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Lalai)\"\nBasic.Settings.General.EnableAutoUpdates=\"Periksa kemas kini secara automatik semasa permulaan\"\nBasic.Settings.General.OpenStatsOnStartup=\"Buka dialog statistik semasa permulaan\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Sembunyikan tetingkap OBS daripada tangkapan paparan\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Pilihan ini akan menyembunyikan semua tetingkap OBS Studio bukan projektor daripada ditangkap oleh OBS dan mempengaruhi \\naplikasi lain, seperti persidangan, perkongsian skrin, sokongan jauh, tangkapan skrin dan perisian tangkapan lain.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Menghidupkan plihan ini akan menyembunyikan semua tetingkap OBS Studio bukan projektor daripada ditangkap oleh OBS dan mempengaruhi aplikasi lain, seperti persidangan, perkongsian skrin, sokongan jauh, tangkapan skrin dan perisian tangkapan lain.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Tunjuk dialog pengesahan ketika memulakan penstriman\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Tunjuk dialog pengesahan ketika menghentikan penstriman\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Tunjukkan dialog pengesahan ketika menghentikan rakaman\"\nBasic.Settings.General.Projectors=\"Projektor\"\nBasic.Settings.General.HideProjectorCursor=\"Sembunyi tetikus atas unjuran\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Biar unjuran sentiasa di atas\"\nBasic.Settings.General.Snapping=\"Pelekapan Jajaran Sumber\"\nBasic.Settings.General.ScreenSnapping=\"Lekap Sumber-sumber ke pinggir skrin\"\nBasic.Settings.General.CenterSnapping=\"Lekap Sumber-sumber ke tengah mengufuk dan menegak\"\nBasic.Settings.General.SourceSnapping=\"Lekap Sumber-sumber dengan sumber lain\"\nBasic.Settings.General.SnapDistance=\"Kepekaan Lekap\"\nBasic.Settings.General.SpacingHelpers=\"Tunjuk panduan jajaran piksel\"\nBasic.Settings.General.RecordWhenStreaming=\"Rakam secara automatik ketika menstrim\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Tetap rakam setelah penstriman berhenti\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Mulakan penimbal main semula secara automatik ketika menstrim\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Kekalkan penimbal main semula aktif ketika strim berhenti\"\nBasic.Settings.General.SysTray=\"Talam Sistem\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimum ke talam sistem apabila bermula\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Sentiasa diminimumkan ke talam sistem selain dari palang tugas\"\nBasic.Settings.General.SaveProjectors=\"Simpan projektor bila keluar\"\nBasic.Settings.General.CloseExistingProjectors=\"Had satu unjuran skrin penuh per skrin\"\nBasic.Settings.General.Preview=\"Pratonton\"\nBasic.Settings.General.OverflowHidden=\"Sembunyi limpahan\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Limpahan sentiasa kelihatan\"\nBasic.Settings.General.OverflowSelectionHidden=\"Tunjuk limpahan walaupun ketika sumber kelihatan\"\nBasic.Settings.General.Importers=\"Pengimport\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Gelintar lokasi yang diketahui untuk koleksi-koleksi adegan ketika mengimport\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Peralihan ke adegan bila didwi-klik\"\nBasic.Settings.General.StudioPortraitLayout=\"Benarkan bentangan potret/menegak\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Tunjuk label pratonton/program\"\nBasic.Settings.General.Multiview=\"Pandangan Berbilang\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Klik untuk beralih antara adegan\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Tunjuk nama-nama adegan\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Lukis kawasan selamat (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Bentangan Pandangan Berbilang\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Mengufuk, Atas (8 Adegan)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Mengufuk, Bawah (8 Adegan)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Menegak, Kiri (8 Adegan)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Menegak, Kanan (8 Adegan)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Mengufuk, Atas (18 Adegan)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Menegak, Atas (24 Adegan)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Scene sahaja (4 Scene)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Scene sahaja (9 Scene)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Scene sahaja (16 Scene)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Scene sahaja (25 Scene)\"\nBasic.Settings.General.ChannelName.stable=\"Stabil\"\nBasic.Settings.General.ChannelDescription.stable=\"Keluaran terbaru yang stabil\"\nBasic.Settings.General.ChannelName.beta=\"Cubaan / Release Candidates\"\nBasic.Settings.General.ChannelDescription.beta=\"Berkemungkinan siri pre-lancar tidak stabil\"\nBasic.Settings.Appearance=\"Penampilan\"\nBasic.Settings.Appearance.General=\"Umum\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Gaya\"\nBasic.Settings.Appearance.General.NoVariant=\"Tiada Gaya Tersedia\"\nBasic.Settings.Appearance.FontScale=\"Saiz Tulisan\"\nBasic.Settings.Appearance.Density=\"Ketumpatan\"\nBasic.Settings.Appearance.Classic=\"Klasik\"\nBasic.Settings.Appearance.Compact=\"Padat\"\nBasic.Settings.Appearance.Normal=\"Biasa\"\nBasic.Settings.Appearance.Comfortable=\"Selesa\"\nBasic.Settings.Appearance.OptionsWarning=\"Beberapa pilihan penampilan tidak tersedia untuk gaya ini.\"\nBasic.Settings.Stream=\"Strim\"\nBasic.Settings.Stream.Destination=\"Lokasi\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Guna pengesahihan\"\nBasic.Settings.Stream.Custom.Username=\"Nama Pengguna\"\nBasic.Settings.Stream.Custom.Password=\"Kata Laluan\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: masukkan srp_username.\\nRTMP: masukkan nama pengguna.\\nSRT: tidak digunakan.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: masukkan srp_password.\\nRTMP: masukkan kata laluan.\\nSRT: masukkan frasa laluan penyulitan.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Benarkan Mod Uji Lebar Jalur\"\nBasic.Settings.Stream.TTVAddon=\"Tambahan Sembang Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Tiada\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV dan FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Persediaan Strim Hilang\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Buka Tetapan\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL dan Kunci Strim sudah hilang.\\n\\nBuka tetapan untuk masukkan URL dan Kunci Strim dalam tab 'strim'.\"\nBasic.Settings.Stream.MissingUrl=\"URL strim sudah hilang.\\n\\nBuka tetapan untuk masukkan URL dalam tab 'Strim'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Kunci strim sudah hilang.\\n\\nBuka tetapan untuk masukkan kunci strim dalam tab 'Strim'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Abai cadangan tetapan perkhidmatan penstriman\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Batalkan Tetapan Cadangan\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Amaran: Mengabaikan batasan perkhidmatan boleh mengakibatkan kualiti strim menurun atau menghalang anda melakukan penstriman.\\n\\nTeruskan?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Kadar Bit Video Maksimum: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Kadar Bit Audio Maksimum: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Resolusi Maksimum: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"FPS Maksimum: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Tentukan Server Sendiri\"\nBasic.Settings.Stream.ServiceCustomServer=\"Server Sendiri\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Benarkan %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Lebar Jalur Penstriman Maksimum\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Trek Video Maksimum\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Benarkan pembuangan strim ke FLV (menggunakan tetapan fail rakaman mudah)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Tetapan Dilapik (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Benarkan Tetapan Dilapik\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Video Pelbagai-trek\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Kanvas Tambahan\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Siaran-Serentak\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Siaran-Serentak membolehkan anda mengekod dan menghantar berbilang kualiti video. <a href='https://obsproject.com/kb/whip-streaming-guide'>Lebih Lanjut</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Jumlah Lapisan\"\nBasic.Settings.Stream.AdvancedOptions=\"Tetapan Lanjutan\"\nBasic.Settings.Output.Format=\"Format Rakaman\"\nBasic.Settings.Output.Format.MKV=\"Video Matroska (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"MP4 Hybrid (.mp4)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 Terserpih (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV Terserpih (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"MOV yang difragment menulis rakaman dalam ketulan dan tidak memerlukan pengakhiran yang sama seperti fail MOV tradisional.\\nIni memastikan fail kekal boleh dimainkan walaupun menulis ke cakera terganggu, contohnya, akibat BSOD atau kehilangan kuasa.\\n\\nIni mungkin tidak serasi dengan semua pemain dan editor. Gunakan Fail → Remux Recordings untuk menukar fail kepada format yang lebih serasi jika perlu.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"MP4 yang difragment menulis rakaman dalam ketulan dan tidak memerlukan pengakhiran yang sama seperti fail MOV tradisional.\\nIni memastikan fail kekal boleh dimainkan walaupun menulis ke cakera terganggu, contohnya, akibat BSOD atau kehilangan kuasa.\\n\\nIni mungkin tidak serasi dengan semua pemain dan editor. Gunakan Fail → Remux Recordings untuk menukar fail kepada format yang lebih serasi jika perlu.\"\nBasic.Settings.Output.Encoder.Video=\"Pengekod Video\"\nBasic.Settings.Output.Encoder.Audio=\"Pengekod Audio\"\nBasic.Settings.Output.SelectDirectory=\"Pilih Direktori Rakaman\"\nBasic.Settings.Output.DynamicBitrate=\"Ubah kadar bit secara dinamik untuk mengendalikan kesesakan\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Ubah kadar bit secara dinamik untuk mengendalikan kesesakan (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Selain dari melepaskan bingkai untuk mengurangkan kesesakan, terus ubah kadar bit secara dinamik.\\n\\nPerhatian tindakan ini boleh menyebabkan kelengahan kepada para penonton jika tiba-tiba berlaku kesesakan mendadak.\\nApabila kadar bit jatuh, ia mengambil masa beberapa minit untuk pulih.\\n\\nBuat masa ini hanya RTMP disokong.\"\nBasic.Settings.Output.Mode=\"Mod Output\"\nBasic.Settings.Output.Mode.Simple=\"Mudah\"\nBasic.Settings.Output.Mode.Adv=\"Mahir\"\nBasic.Settings.Output.Mode.FFmpeg=\"Output FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Benarkan Penimbal Main Semula\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Masa Main Semula Maksimum\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Memori Maksima\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Penggunaan ingatan anggaran: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Amaran: Anggaran penggunaan memori %1 MiB adalah terlebih besar dari saiz maksimum yang disyorkan iaitu %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Tidak dapat menganggar penggunaan ingatan. Sila tetapkan had ingatan maksimum.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Awalan Nama Fail Penimbal Main Semula\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Akhiran\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Penimbal main balik tidak dapat digunakan ketika jenis rakaman ditetapkan menjadi Output Suai (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Laluan Rakaman\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Kualiti Rakaman\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Sama seperti strim\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Kualiti Tinggi, Saiz Fail Sederhana\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Kualiti Terbaik, Saiz Fail Besar\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Kualiti Tak Hilang, Saiz Fail Amat Besar\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Amaran: Kadar bit video penstriman akan ditetapkan ke nilai %1, iaitu had atas bagi perkhidmatan penstriman semasa.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Amaran: Kadar bit audio penstriman akan ditetapkan ke nilai %1, iaitu had atas bagi perkhidmatan penstriman semasa.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Amaran: Rakaman tidak dapat dijeda jika kualiti rakaman ditetapkan pada \\\"Sama dengan strim\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Amaran: Format rakaman yang dipilih pada masa ini tidak serasi dengan pengekod strim yang dipilih.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Amaran: Rakaman dengan satu pengekod perisian pada kualiti berbeza berbanding strim akan memerlukan lebih penggunaan CPU jika strim dan rakam pada masa yang sama.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Amaran: Kualiti tak-hilang akan menjana saiz fail yang amat besar! Kualiti tak-hilang menggunakan sehingga 7 gigabait ruang cakera per minit pada resolusi dan kadar bingkai tinggi. Tak-hilang tidak disarankan untuk rakaman panjang melainkan anda mempunyai ruang cakera yang sangat besar. Penampan main semula tidak tersedia apabila menggunakan kualiti tak-hilang.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Anda pasti mahu guna kualiti tak hilang?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Amaran kualiti tak hilang!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Perisian (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Perkakasan (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Perkakasan (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Perkakasan (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Perkakasan (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Perkakasan (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Perkakasan (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Perkakasan (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Perkakasan (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Perkakasan (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Perkakasan (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Perisian (Praset x264 pengunaan CPU rendah, meningkatkan saiz fail)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Asal)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Trek VOD Twitch (Guna Trek 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Trek Audio\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Resolusi/Kadar Bingkai Tidak Serasi\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Perkhidmatan penstriman ini tidak menyokong resolusi dan/atau kadar bit output semasa anda. Ia akan diubah dengan nilai serasi terhampir:\\n\\n%1\\n\\nAnda pasti mahu teruskan?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolusi: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Pengekod Tidak Serasi\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Perkhidmatan penstriman \\\"%1\\\" tidak menyokong pengekod \\\"%2\\\". Pen gekod akan bertukar kepada \\\"%3\\\".\\n\\nAnda pasti mahu teruskan?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Perkhidmatan penstriman \\\"%1\\\" tidak menyokong pengekod \\\"%2\\\" dan \\\"%3\\\". Pengekod ini akan ditukar kepada \\\"%4\\\" dan \\\"%5\\\".\\n\\nAnda pasti mahu teruskan?\"\nBasic.Settings.Output.VideoBitrate=\"Kadar Bit Video\"\nBasic.Settings.Output.AudioBitrate=\"Kadar Bit Audio\"\nBasic.Settings.Output.Reconnect=\"Sambung Semula secara Automatik\"\nBasic.Settings.Output.RetryDelay=\"Lengah Cuba Lagi\"\nBasic.Settings.Output.MaxRetries=\"Percubaan Maksimum\"\nBasic.Settings.Output.Advanced=\"Benarkan Tetapan Pengekod Suai (Lanjutan)\"\nBasic.Settings.Output.EncoderPreset=\"Praset Pengekod\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (penggunaan CPU rendah, kualiti terendah)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (lalai) (penggunaan CPU sederhana, kualiti piawai)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (penggunaam CPU tinggi, kualiti tinggi)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Tetapan Pengekod Suai\"\nBasic.Settings.Output.CustomMuxerSettings=\"Tetapan Muxer Suai\"\nBasic.Settings.Output.NoSpaceFileName=\"Jana Nama Fail tanpa Jarak\"\nBasic.Settings.Output.Adv.Rescale=\"Skala Semula Output\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Dinyah-aktifkan\"\nBasic.Settings.Output.Adv.AudioTrack=\"Trek Audio\"\nBasic.Settings.Output.Adv.Streaming=\"Penstriman\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Tetapan Penstriman\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Trek 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Trek 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Trek 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Trek 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Trek 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Trek 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Trek VOD Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"Tetapan Pengekod\"\nBasic.Settings.Output.Adv.Recording=\"Rakaman\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Tetapan Rakaman\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Jenis Rakaman\"\nBasic.Settings.Output.Adv.Recording.Type=\"Jenis\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Piawai\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Output Suai (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Guna pengekod strim)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Pemformatan Nama Fail\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Tulis ganti jika fail wujud\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Output Tersuai (FFmpeg) disediakan tanpa perlindungan.\\nPertimbangkan untuk menggunakan jenis rakaman \\\"Standard\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Jenis Output FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Output ke URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Output ke Fail\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Format rakaman umum\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Semua Fail\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Laluan atau URL fail\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format Bekas\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Format Lalai\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Keterangan Format Bekas\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Kodeks Audio/Video diteka daripada laluan Fail atau URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Pengekod Lalai\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Lumpuhkan Pengekod\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Pengekod Video\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Tetapan Pengekod Video (jika ada)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Pengekod Audio\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Tetapan Pengekod Audio (jika ada)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Tetapan Muxer (jika ada)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Sela bingkai kunci (bingkai)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Tunjuk semua kodeks (walaupun jika berpotensi tidak serasi)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Tetapan FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Pecah Fail Secara Auto\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Pisah mengikut Waktu\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Pisah mengikut Saiz\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Hanya pisah secara manual\"\nBasic.Settings.Output.SplitFile.Time=\"Pisah Waktu\"\nBasic.Settings.Output.SplitFile.Size=\"Pisah Saiz\"\nScreenshot=\"Tangkap Skrin Output\"\nScreenshot.SourceHotkey=\"Tangkap Skrin Sumber Terpilih\"\nScreenshot.StudioProgram=\"Tangkap Skrin (Program)\"\nScreenshot.Preview=\"Tangkap Skrin (Pratonton)\"\nScreenshot.Scene=\"Tangkap Skrin (Adegan)\"\nScreenshot.Source=\"Tangkap Skrin (Sumber)\"\nFilenameFormatting.TT.CCYY=\"Tahun, empat digit\"\nFilenameFormatting.TT.YY=\"Tahun, dua digit terakhir (00-99)\"\nFilenameFormatting.TT.MM=\"Bulan sebagai nombor desimal (01-12)\"\nFilenameFormatting.TT.DD=\"Hari dalam bulan, diawali-sifar (01-31)\"\nFilenameFormatting.TT.hh=\"Jam dalam format 24j (00-23)\"\nFilenameFormatting.TT.mm=\"Minit (00-59)\"\nFilenameFormatting.TT.ss=\"Saat (00-59)\"\nFilenameFormatting.TT.Percent=\"Tanda %\"\nFilenameFormatting.TT.a=\"Nama hari bekerja singkatan\"\nFilenameFormatting.TT.A=\"Nama hari bekerja\"\nFilenameFormatting.TT.b=\"Nama bulan ringkasan\"\nFilenameFormatting.TT.B=\"Nama bulan penuh\"\nFilenameFormatting.TT.d=\"Hari dalam bulan, diawali-sifar (01-31)\"\nFilenameFormatting.TT.H=\"Jam dalam format 24j (00-23)\"\nFilenameFormatting.TT.I=\"Jam dalam format 12j (01-12)\"\nFilenameFormatting.TT.m=\"Bulan dalam nombor desimal (01-12)\"\nFilenameFormatting.TT.M=\"Minit (00-59)\"\nFilenameFormatting.TT.p=\"Tanda AM atau PM\"\nFilenameFormatting.TT.s=\"Masa dalam saat semenjak epoch UNIX\"\nFilenameFormatting.TT.S=\"Saat (00-59)\"\nFilenameFormatting.TT.y=\"Tahun, dua digit terakhir (00-99)\"\nFilenameFormatting.TT.Y=\"Tahun\"\nFilenameFormatting.TT.z=\"Ofset  ISO 8601 dari UTC dalam zon waktu\"\nFilenameFormatting.TT.Z=\"Nama atau singkatan zon waktu\"\nFilenameFormatting.TT.FPS=\"Bingkai sesaat\"\nFilenameFormatting.TT.CRES=\"Resolusi (kanvas) asas\"\nFilenameFormatting.TT.ORES=\"Resolusi (berskala) output\"\nFilenameFormatting.TT.VF=\"Format video\"\nBasic.Settings.Video.Adapter=\"Penyesuai Video\"\nBasic.Settings.Video.BaseResolution=\"Resolusi (Kanvas) Asas\"\nBasic.Settings.Video.ScaledResolution=\"Resolusi (Berskala) Output\"\nBasic.Settings.Video.DownscaleFilter=\"Penapis Downscale\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Resilusi padan, tidak memerlukan downscaling]\"\nBasic.Settings.Video.FPSCommon=\"Nilai-nilai FPS Umum\"\nBasic.Settings.Video.FPSInteger=\"Nilai FPS Integer\"\nBasic.Settings.Video.FPSFraction=\"Nilai FPS Pecahan\"\nBasic.Settings.Video.Numerator=\"Pengangka\"\nBasic.Settings.Video.Denominator=\"Penyebut\"\nBasic.Settings.Video.Renderer=\"Pengemas Gabung\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Percubaan)\"\nBasic.Settings.Video.InvalidResolution=\"Nilai resolusi tidak sah. Mestilah [width]x[height] (iaitu 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Output video buat masa ini masih aktif. Sila matikan mana-mana output untuk mengubah tetapan video.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Dwi-linear (Terpantas, tetapi kabur jika diskalakan)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Dwi-kiub (Penskalaan diperjelaskan, 16 sampel)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Penskalaan diperjelaskan, 36 sampel)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Kawasan (Hasil tambah terwajar, 4/6/9 sampel)\"\nBasic.Settings.Audio.SampleRate=\"Kadar Sampel\"\nBasic.Settings.Audio.Channels=\"Saluran\"\nBasic.Settings.Audio.Meters=\"Meter\"\nBasic.Settings.Audio.MeterDecayRate=\"Kadar Susutan\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Pantas\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Sederhana (Jenis PPM I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Perlahan (Jenis PPM II)\"\nBasic.Settings.Audio.PeakMeterType=\"Jenis Meter Puncak\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Puncak Sampel\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Puncak Benar (Penggunaan CPU lebih tinggi)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"AMARAN: Audio bunyi keliling dibenarkan.\"\nBasic.Settings.Audio.MultichannelWarning=\"Jika menstrim, sila periksa sama ada perkhidmatan penstriman anda menyokong kedua-dua cernaan bunyi keliling dan main balik bunyi keliling. Facebook 360 Live merupakan salah satu contoh bunyi keliling disokong sepenuhnya. YouTube Live menyokong cernaan audio 5.1 (dan main balik pada TV).\\n\\nPenapis audio OBS adalah serasi dengan bunyi keliling, walaupun tiada jaminan sokongan pemalam VST.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Benarkan audio bunyi keliling?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Anda pasti mahu membenarkan audio bunyi keliling?\"\nBasic.Settings.Audio.Devices=\"Peranti Audio Sejagat\"\nBasic.Settings.Audio.DesktopDevice=\"Audio Atas Meja\"\nBasic.Settings.Audio.DesktopDevice2=\"Audio Atas Meja 2\"\nBasic.Settings.Audio.AuxDevice=\"Audio Mic/Sampingan\"\nBasic.Settings.Audio.AuxDevice2=\"Audio Mic/Sampingan 2\"\nBasic.Settings.Audio.AuxDevice3=\"Audio Mic/Sampingan 3\"\nBasic.Settings.Audio.AuxDevice4=\"Audio Mic/Sampingan 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Benarkan Tekan-untuk-senyap\"\nBasic.Settings.Audio.PushToMuteDelay=\"Lengah Tekan-untuk-senyap\"\nBasic.Settings.Audio.EnablePushToTalk=\"Benarkan Tekan-untuk-cakap\"\nBasic.Settings.Audio.PushToTalkDelay=\"Lengah Tekan-untuk-cakap\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Peranti tidak bersambung atau tidak tersedia]\"\nBasic.Settings.Audio.Disabled=\"Dilumpuhkan\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Mod Penimbalan Audio Kependaman Rendah (untuk output Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"AMARAN: Penimbalan audio kependaman rendah dibenarkan.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Mod penimbalan audio kependaman rendah boleh menyebabkan audio berglic atau berhenti main dari beberapa sumber.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Mahu benarkan mod penimbalan audio kependaman rendah?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Andapasti mahu benarkan mod penimbalan audio kependaman rendah?\"\nBasic.Settings.Accessibility=\"Kebolehcapaian\"\nBasic.Settings.Accessibility.ColorOverrides=\"Guna Warna Berbeza\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Sempadan Sumber (Pemilihan)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Sempadan Sumber (Kerat)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Sempadan Sumber (Apung)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Jalur Volum Pengadun (-60 hingga -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Jalur Volum Pengadun (-20 hingga -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Jalur Volum Pengadun (-9 hingga 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Jalur Volum Pengadun (-60 hingga -20dB) (aktif)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Jalur Volum Pengadun (-20 hingga -9dB) (aktif)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Jalur Volum Pengadun (-9 hingga 0dB) (aktif)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Praset Warna\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Lalai\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Suai\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternatif Buta Warna\"\nBasic.Settings.Advanced=\"Lanjutan\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Paparkan amaran output aktif semasa keluar.\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Keutamaan Proses\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Tinggi\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Atas Normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Bawah Normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Melahu\"\nBasic.Settings.Advanced.FormatWarning=\"Amaran: Format warna selain daripada NV12/P010 lebih digunakan untuk rakaman, dan tidak disyorkan ketika menstrim. Penstriman mungkin meningkatkan penggunaan CPU kerana penukaran format warna.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Amaran: Format ketepatan-tinggi lebih biasa digunakan dengan ruang warna HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Amaran: Rakaman 2100 perlu mengikut format yang lebih tepat.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Format Warna\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 satah)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 satah)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 satah)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 satah)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 satah)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 satah)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:2:2, 2 satah)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Ruang Warna\"\nBasic.Settings.Advanced.Video.ColorRange=\"Julat Warna\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Terhad\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Penuh\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Aras Putih SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Aras Puncak Nominal HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Peranti Pemantauan\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Lalai\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Lumpuhkan ducking audio Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Lengah Strim\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Tempoh\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Kekal titik penggal (tingkatkan lengah) ketika menyambung semula\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Anggaran Penggunaan Ingatan: %1 MB\"\nBasic.Settings.Advanced.Network=\"Rangkaian\"\nBasic.Settings.Advanced.Network.Disabled=\"Protokol penstriman terpilih semasa tidak menyokong perubahan tetapan rangkaian.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Ikat ke IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Keluarga IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Benarkan pengoptimuman rangkaian\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Benarkan kecepatan TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Percubaan untuk membuat output RTMP lebih mesra berbanding aplikasi-aplikasi peka kependaman lain dalam rangkaian dengan mengatur kadar penghantaran.\\nIa mungkin dapat meningkatkan risiko bingkai terlepas dalam sambungan tidak stabil.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Kelakuan Fokus Kekunci Panas\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Tidak sesekali lumpuhkan kekunci panas\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Lumpuhkan kekunci panas ketika fokus tetingkap utama\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Lumpuhkan kekunci panas ketika tidak fokus tetingkap utama\"\nBasic.Settings.Advanced.AutoRemux=\"Remux secara automatik ke %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(rakam sebagai mkv)\"\nBasic.AdvAudio=\"Sifat-sifat Audio Lanjutan\"\nBasic.AdvAudio.ActiveOnly=\"Sumber-sumber Aktif Sahaja\"\nBasic.AdvAudio.Name=\"Nama\"\nBasic.AdvAudio.Volume=\"Volum\"\nBasic.AdvAudio.VolumeSource=\"Volum untuk '%1'\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix untuk '%1'\"\nBasic.AdvAudio.Balance=\"Imbangan\"\nBasic.AdvAudio.BalanceSource=\"Imbangan untuk '%1'\"\nBasic.AdvAudio.SyncOffset=\"Ofset Segerak\"\nBasic.AdvAudio.SyncOffsetSource=\"Ofset Segerak untuk '%1'\"\nBasic.AdvAudio.Monitoring=\"Pemantauan Audio\"\nBasic.AdvAudio.Monitoring.None=\"Pantu Mati\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Pantau Sahaja (output bisu)\"\nBasic.AdvAudio.Monitoring.Both=\"Pantau dan Output\"\nBasic.AdvAudio.MonitoringSource=\"Pemantauan Audio untuk '%1'\"\nBasic.AdvAudio.AudioTracks=\"Trek\"\nBasic.Settings.Hotkeys=\"Kekunci Panas\"\nBasic.Settings.Hotkeys.Pair=\"Gabungan kekunci terkongsi dengan '%1' bertindak sebagai togol\"\nBasic.Settings.Hotkeys.Filter=\"Penapis\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Tapis mengikut Kekunci Panas\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Kekunci panas ini dikongsi lebih daripada satu tindakan, klik untuk tunjukkan konflik\"\nBasic.Settings.Hotkeys.PleaseWait=\"Memuatkan kekunci panas, tunggu sebentar...\"\nBasic.Hotkeys.SelectScene=\"Alih ke adegan\"\nBasic.SystemTray.Show=\"Tunjuk\"\nBasic.SystemTray.Hide=\"Sembunyi\"\nBasic.SystemTray.Message.Reconnecting=\"Terputus. Menyambung semula...\"\nHotkeys.Left=\"Kiri\"\nHotkeys.Right=\"Kanan\"\nHotkeys.Up=\"Atas\"\nHotkeys.Down=\"Bawah\"\nHotkeys.Space=\"Ruang\"\nHotkeys.NumpadMultiply=\"Numpad Darab\"\nHotkeys.NumpadDivide=\"Numpad Bahagi\"\nHotkeys.NumpadAdd=\"Numpad Tambah\"\nHotkeys.NumpadSubtract=\"Numpad Tolak\"\nHotkeys.NumpadDecimal=\"Perpuluhan Numpad\"\nHotkeys.AppleKeypadNum=\"%1 (Pad Kekunci)\"\nHotkeys.AppleKeypadMultiply=\"* (Pad Kekunci)\"\nHotkeys.AppleKeypadDivide=\"/ (Pad Kekunci)\"\nHotkeys.AppleKeypadAdd=\"+ (Pad Kekunci)\"\nHotkeys.AppleKeypadSubtract=\"- (Pad Kekunci)\"\nHotkeys.AppleKeypadEqual=\"= (Pad Kekunci)\"\nHotkeys.MouseButton=\"Tetikus %1\"\nMute=\"Senyap\"\nUnmute=\"Nyahbisukan\"\nPush-to-mute=\"Tekan-untuk-senyap\"\nPush-to-talk=\"Tekan-untuk-cakap\"\nSceneItemShow=\"Tunjuk '%1'\"\nSceneItemHide=\"Sembunyi '%1'\"\nOutputWarnings.NoTracksSelected=\"Anda mesti memilih sekurang-kurangnya satu trek\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Ralat Tetapan Output\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Semua output mesti sekurang-kurangnya telah memilih satu trek audio.\"\nOutputWarnings.MP4Recording=\"Amaran: Rakaman disimpan ke MP4/MOV tidak boleh dipulihkan jika fail gagal dimuktamadkan (iaitu disebabkan oleh BSOD, ketiadaan kuasa, dll). Jika anda mahu rakam trek-trek audio berbilang menggunakan MKV dan remux rakaman ke MP4/MOV setelah ia selesai (Fail → Rakaman Remux)\"\nOutputWarnings.CannotPause=\"Amaran: Rakaman tidak dapat dijeda jika pengekod rakaman ditetapkan pada \\\"(Guna pengekod strim)\\\"\"\nOutputWarnings.CodecIncompatible=\"Pemilihan pengekod audio atau video telah ditetapkan semula kerana ketidakserasian. Sila pilih pengekod yang serasi daripada senarai.\"\nCodecCompat.Incompatible=\"(Tidak serasi dengan %1)\"\nCodecCompat.CodecPlaceholder=\"Pilih Enkoder...\"\nCodecCompat.ContainerPlaceholder=\"Pilih Format...\"\nCodecCompat.CodecMissingOnExit.Title=\"Tiada Enkoder Dipilih\"\nCodecCompat.CodecMissingOnExit.Text=\"Sekurangnya satu video atau audio enkoder tidak ditetapkan. Sila pastikan untuk memilih enkoder bagi kedua-dua rakaman dan striming.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Tiada Format Dipilih\"\nCodecCompat.ContainerMissingOnExit.Text=\"Tiada format rakaman telah dipilih. Sila pilih format rakaman yang bersesuaian dengan strim enkoder yang telah dipilih.\"\nFinalScene.Title=\"Padam Adegan\"\nFinalScene.Text=\"Perlu ada sekurang-kurangnya satu adegan.\"\nNoSources.Title=\"Tiada Sumber\"\nNoSources.Text=\"Nampaknya anda belum menambah apa-apa sumber video lagi, oleh itu anda hanya memasukkan satu skrin hitam. Anda pasti mahu membuatnya?\"\nNoSources.Text.AddSource=\"Anda boleh menambah sumber dengan mengklik ikon + di bawah kotak Sumber dalam tetingkap utama pada bila-bila masa.\"\nNoSources.Label=\"Anda tidak mempunyai apa-apa sumber.\\nKlik butang + di bawah,\\nor klik kanan di sini untuk menambah satu.\"\nChangeBG=\"Tetapkan Warna\"\nCustomColor=\"Warna Suai\"\nBrowserSource.EnableHardwareAcceleration=\"Benarkan Pemecutan Perkakasan Sumber Pelajar\"\nAbout=\"Perihal\"\nAbout.Info=\"OBS Studio merupakan perisian rakaman video dan penstriman langsung bersumber terbuka dan bebas.\"\nAbout.Donate=\"Beri Sumbangan\"\nAbout.GetInvolved=\"Libatkan Diri\"\nAbout.Authors=\"Para Pengarang\"\nAbout.License=\"Lesen\"\nAbout.Error=\"Ralat! Fail gagal dibaca.\\n\\nPergi ke: %1\"\nAbout.Contribute=\"Sokong Projek OBS\"\nAddUrl.Title=\"Tambah Sumber melalui URL\"\nAddUrl.Text=\"Anda telah menyeret satu URL ke dalam OBS. Ia akan menambah pautan sebagai sumber secara automatik. Teruskan?\"\nResizeOutputSizeOfSource=\"Saiz semula output (saiz sumber)\"\nResizeOutputSizeOfSource.Text=\"Resolusi dasar dan output akan disaiz semula ke saiz sumber semasa.\"\nResizeOutputSizeOfSource.Continue=\"Anda mahu teruskan?\"\nPreviewTransition=\"Pratonton Peralihan\"\nImporter=\"Pengimport Koleksi Adegan\"\nImporter.SelectCollection=\"Pilih satu Koleksi Adegan\"\nImporter.Collection=\"Koleksi Adegan\"\nImporter.HelpText=\"Tambah fail ke dalam tetingkap ini untuk mengimport koleksi daripada OBS atau lain-lain program yang disokong.\"\nImporter.Path=\"Laluan Koleksi\"\nImporter.Program=\"Aplikasi Dikesan\"\nImporter.AutomaticCollectionPrompt=\"Gelintar Koleksi-koleksi Adegan secara Automatik\"\nImporter.AutomaticCollectionText=\"OBS boleh mencari koleksi-koleksi adegan boleh import menerusi program-program pihak ketiga. Adakah anda mahu OBS mencari koleksi secara automatik untuk anda?\\n\\nAnda boleh mengubahnya kemudian dalam Tetapan > Am > Pengimport.\"\nImporter.SelectFile=\"Layari...\"\nRestart=\"Mula Semula\"\nNeedsRestart=\"Studio OBS perlu dimulakan semula. Anda mahu memulakan semula sekarang?\"\nLoadProfileNeedsRestart=\"Profil mengandungi tetapan yang memerlukan memulakan semula OBS:\\n%1\\n\\nAdakah anda mahu memulakan semula OBS agar tetapan ini dapat dilaksanakan?\"\nContextBar.NoSelectedSource=\"Tiada sumber dipilih\"\nContextBar.MediaControls.PlayMedia=\"Main Media\"\nContextBar.MediaControls.PauseMedia=\"Jeda Media\"\nContextBar.MediaControls.StopMedia=\"Henti Media\"\nContextBar.MediaControls.RestartMedia=\"Mula Semula Media\"\nContextBar.MediaControls.PlaylistNext=\"Berikutnya dalam Senarai Main\"\nContextBar.MediaControls.PlaylistPrevious=\"Terdahulu dalam Senarai Main\"\nContextBar.MediaControls.BlindSeek=\"Widget Jangkau Media\"\nYouTube.Auth.Ok=\"Pengesahihan berjaya diselesaikan.\\nAnda boleh tutup halaman ini sekarang.\"\nYouTube.Auth.NoCode=\"Proses pengesahihan tidak selesai.\"\nYouTube.Auth.NoChannels=\"Tiada saluran tersedia untuk akaun terpilih\"\nYouTube.Auth.WaitingAuth.Title=\"Keizinan Pengguna YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Sila selesaikan keizinan dalam pelayar luar anda.<br>Jika pelayar luar tidak dibuka, ikut pautan ini dan selesaikan keizinan:<br>%1\"\nYouTube.AuthError.Text=\"Gagal mendapatkan maklumat saluran: %1.\"\nYouTube.Actions.WindowTitle=\"Persediaan Siaran YouTube - Saluran: %1\"\nYouTube.Actions.CreateNewEvent=\"Cipta Siaran Baharu\"\nYouTube.Actions.ChooseEvent=\"Pilih Siaran Sedia Ada\"\nYouTube.Actions.Title=\"Tajuk*\"\nYouTube.Actions.MyBroadcast=\"Siaran Saya\"\nYouTube.Actions.Description=\"Keterangan\"\nYouTube.Actions.Privacy=\"Privasi*\"\nYouTube.Actions.Privacy.Private=\"Peribadi\"\nYouTube.Actions.Privacy.Public=\"Awam\"\nYouTube.Actions.Privacy.Unlisted=\"Tidak tersenarai\"\nYouTube.Actions.Category=\"Kategori\"\nYouTube.Actions.Thumbnail=\"Imej Kenit\"\nYouTube.Actions.Thumbnail.SelectFile=\"Pilih fail...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Tiada fail dipilih\"\nYouTube.Actions.Thumbnail.ClearFile=\"Kosongkan\"\nYouTube.Actions.MadeForKids=\"Adakah video untuk dihasilkan untuk tontonan kanak-kanak?*\"\nYouTube.Actions.MadeForKids.Yes=\"Ya, ia khusus untuk kanak-kanak\"\nYouTube.Actions.MadeForKids.No=\"Tidak, bukan untuk tontonan kanak-kanak\"\nYouTube.Actions.AdditionalSettings=\"Tetapan tambahan\"\nYouTube.Actions.Latency=\"Kependaman\"\nYouTube.Actions.Latency.Normal=\"Biasa\"\nYouTube.Actions.Latency.Low=\"Rendah\"\nYouTube.Actions.Latency.UltraLow=\"Teramat rendah\"\nYouTube.Actions.EnableAutoStart=\"Benarkan Auto-mula\"\nYouTube.Actions.EnableAutoStop=\"Benarkan Auto-henti\"\nYouTube.Actions.AutoStartStop.TT=\"Menunjukkan sama ada siaran berjadual ini patut dimulakan secara automatik\"\nYouTube.Actions.EnableDVR=\"Benarkan DVR\"\nYouTube.Actions.360Video=\"Video 360\"\nYouTube.Actions.ScheduleForLater=\"Dijadualkan kemudian\"\nYouTube.Actions.RememberSettings=\"Ingat tetapan ini\"\nYouTube.Actions.Create_Ready=\"Cipta siaran\"\nYouTube.Actions.Create_GoLive=\"Cipta siaran dan mulakan penstriman\"\nYouTube.Actions.Choose_Ready=\"Pilih siaran\"\nYouTube.Actions.Choose_GoLive=\"Pilih siaran dan mulakan penstriman\"\nYouTube.Actions.Create_Schedule=\"Jadualkan siaran\"\nYouTube.Actions.Create_Schedule_Ready=\"Jadualkan dan pilih siaran\"\nYouTube.Actions.Dashboard=\"Buka YouTube Studio\"\nYouTube.Actions.Error.Title=\"Ralat penciptaan siaran langsung\"\nYouTube.Actions.Error.Text=\"Ralat capaian YouTube '%1'.<br/>Keterangan terperinci ralat boleh ditemui di <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Ralat capaian YouTube. Sila periksa sambungan rangkaian anda atau capaian pelayan YouTube anda.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Ralat penciptaan siaran '%1'.<br/>Keterangan terperinci boleh ditemui di <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Tiada strim dicipta. Sila paut semula akaun anda.\"\nYouTube.Actions.Error.YouTubeApi=\"Ralat API YouTube. Sila lihat fail log untuk maklumat lanjut.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Siaran terpilih tidak ditemui.\"\nYouTube.Actions.Error.FileMissing=\"Fail terpilih tidak wujud.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Gagal membuka fail terpilih.\"\nYouTube.Actions.Error.FileTooLarge=\"Fail terpilih terlalu besar (Had: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Peralihan siaran mengalami kegagalan: %1<br/><br/>Jika ralat ini masih berlaku <a href='https://studio.youtube.com/video/%2/livestreaming'>buka siaran dengan Studio YouTube</a> dan cuba secara manual.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Siaran beralih ke tahap ujian, ia mengambil sedikit masa. Cuba lagi dalam tempoh 10-30 saat.\"\nYouTube.Actions.EventsLoading=\"Memuatkan senarai acara...\"\nYouTube.Actions.EventCreated.Title=\"Acara Dicipta\"\nYouTube.Actions.EventCreated.Text=\"Acara berjaya dicipta.\"\nYouTube.Actions.Stream=\"Strim\"\nYouTube.Actions.Stream.ScheduledFor=\"Dijadualkan untuk %1\"\nYouTube.Actions.Stream.Resume=\"Sambung semula strim disampuk\"\nYouTube.Actions.Stream.YTStudio=\"Dicipta secara automatik oleh YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Mencipta satu Siaran Langsung baharu, tunggu sebentar...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Permulaan manual diperlukan\"\nYouTube.Actions.AutoStartStreamingWarning=\"Auto-mula dilumpuhkan untuk acara ini, klik \\\"Go Live\\\" untuk memulakan siaran anda.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Anda tidak dapat menyambung semula.<br>Stream anda akan terhenti dan anda tidak dapat bersiaran langsung lagi.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Penstriman langsung tidak dibenarkan untuk saluran YouTube terpilih.<br/><br/>Sila rujuk <a href='https://www.youtube.com/features'>youtube.com/features</a> untuk maklumat lanjut.\"\nYouTube.Errors.livePermissionBlocked=\"Penstriman langsung tidak tersedia untuk Saluran YouTube terpilih.<br/>Penstriman langsung mengambil masa sehingga 24 jam setelah dibenarkan dalam tetapan saluran anda.<br/><br/>Sila rujuk <a href='https://www.youtube.com/features'>youtube.com/features</a> untuk perincian mengenainya.\"\nYouTube.Errors.errorExecutingTransition=\"Peralihan gagal kerana ada ralat bahagian belakang. Cuba sekali lagi dalam tempoh beberapa saat.\"\nYouTube.Errors.errorStreamInactive=\"YouTube tidak menerima data untuk strim anda. Sila periksa konfigurasi anda dan cuba lagi.\"\nYouTube.Errors.invalidTransition=\"Percubaan peralihan adalah tidak sah. Ia mungkin disebabkan oleh strim yang gagal menyelesaikan peralihan terdahulu. Sila tunggu beberapa saat dan cuba sekali lagi.\"\nYouTube.DocksRemoval.Title=\"Kosongkan Dock Pelayar Legasi YouTube\"\nYouTube.DocksRemoval.Text=\"Playar dock ini akan dibuang sebagai tidak digunakan:\\n\\n%1\\nGunakan \\\"Docks/YouTube Live Control Room\\\" sebaliknya.\"\nConfigDownload.WarningMessageTitle=\"Amaran\"\nFailedToStartStream.MissingConfigURL=\"Tiada konfigurasi URL tersedia untuk perkhidmatan ini\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"RTMP URL sendiri tidak dinyatakan\"\nFailedToStartStream.InvalidCustomConfig=\"Konfigurasi sendiri tidak sah\"\nFailedToStartStream.MissingCanvas=\"Kanvas tambahan yang dikonfigurasikan hilang\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Gagal untuk mencipta servis video trek-pelbagai\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Gagal untuk mencipta pengeluaran video trek-pelbagai RTMP\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC tidak tersedia.\\n\\nGagal untuk mencari jenis encoder '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Gagal untuk mencipta enkoder video '%1' ('jenis: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Gagal mendapatkan info video OBS ketika mencipta enkoder '%1' (jenis: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Gagal untuk mencipta enkoder audio\"\nFailedToStartStream.NoRTMPURLInConfig=\"Konfigurasi tidak mengandungi target strim RTMP(S) URL\"\nFailedToStartStream.FallbackToDefault=\"Gagal memulakan strim menggunakan %1; adakah anda mahu mencuba semua menggunakan tetapan enkod? \"\nFailedToStartStream.ConfigRequestFailed=\"Tidak dapat mengambil konfigurasi daripada %1<br><br>Ralat HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Menerima nilai status yang tidak diketahui '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nAdakah anda mahu meneruskan strim tanpa %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nAdakah anda mahu meneruskan strim?\"\nFailedToStartStream.MissingEncoderConfigs=\"Konfigurasi siaran langsung tidak termasuk konfigurasi pengekod\"\nFailedToStartStream.InvalidEncoderConfig=\"Konfigurasi siaran langsung termasuk konfigurasi pengekod yang tidak sah\"\nFailedToStartStream.StatusMissingHTML=\"Permintaan siaran langsung mengembalikan ralat yang tidak dapat ditentukan\"\nFailedToStartStream.NoConfigSupplied=\"Konfigurasi hilang\"\nMultitrackVideo.Info=\"%1 mengoptimumkan tetapan anda secara automatik untuk mengekod dan menghantar berbilang kualiti video. Memilih pilihan ini akan menghantar %2 maklumat tentang komputer dan tetapan perisian anda.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Tetapan Tidak Serasi\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 tidak serasi dengan:\\n\\n%2\\nUntuk meneruskan penstriman dengan %1, matikan tetapan yang tidak serasi:\\n\\n%3\\ndan Mulakan Penstriman sekali lagi.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Matikan untuk strim ini dan Mulakan Penstriman\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Kemaskini Tetapan dan Mulakan Strim\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 pada masa ini tidak serasi dengan [Audio → Umum → Saluran] ditetapkan kepada '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Audio → Umum → Saluran] perlu ditetapkan kepada '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 memerlukan berbilang tetapan berbeza untuk [Audio → Umum → Saluran]\"\nBasic.OpenPluginManager=\"Pengurus Plugin\"\nPluginManager=\"Pengurus Plugin\"\nPluginManager.Restart=\"OBS Studio mesti dimulakan semula untuk menggunakan perubahan pemalam\"\nPluginManager.MissingPlugin=\"[PLUGIN TIDAK DITEMUI]\"\nPluginManager.Section.Discover=\"Layari\"\nPluginManager.Section.Manage=\"Dipasang\"\nPluginManager.Section.Updates=\"Kemaskini\"\nPluginManager.Section.Manage.Title=\"Urus Pemalam Yang Dihidupkan\"\n"
  },
  {
    "path": "frontend/data/locale/nb-NO.ini",
    "content": "Language=\"Norsk bokmål\"\nOK=\"Ok\"\nApply=\"Bruk\"\nCancel=\"Avbryt\"\nClose=\"Lukk\"\nSave=\"Lagre\"\nDiscard=\"Forkast\"\nDisable=\"Deaktiver\"\nYes=\"Ja\"\nNo=\"Nei\"\nAdd=\"Legg til\"\nRemove=\"Fjern\"\nRename=\"Gi nytt navn\"\nInteract=\"Samhandle\"\nFilters=\"Filtre\"\nProperties=\"Egenskaper\"\nMoveUp=\"Flytt opp\"\nMoveDown=\"Flytt ned\"\nSettings=\"Innstillinger\"\nDisplay=\"Skjerm\"\nName=\"Navn\"\nExit=\"Avslutt\"\nMixer=\"Lydmikser\"\nBrowse=\"Bla gjennom\"\nDroppedFrames=\"Mistede bilder %1 (%2%)\"\nClear=\"Tøm\"\nRevert=\"Tilbakestill\"\nShow=\"Vis\"\nHide=\"Skjul\"\nUnhideAll=\"Vis alle\"\nUntitled=\"Uten navn\"\nNew=\"Ny\"\nDuplicate=\"Dupliser\"\nEnable=\"Aktiver\"\nDisableOSXVSync=\"Deaktiver macOS v-sync\"\nResetOSXVSyncOnExit=\"Tilbakestill macOS sin V-sync ved avslutning\"\nHighResourceUsage=\"Koderen er overbelastet! Vurder nedjustering av videokvaliteten eller en raskere forhåndsinnstilling for koderen.\"\nTransition=\"Overgang\"\nQuickTransitions=\"Hurtig overganger\"\nFadeToBlack=\"Ton til svart\"\nLeft=\"Venstre\"\nRight=\"Høyre\"\nTop=\"Topp\"\nBottom=\"Bunn\"\nReset=\"Tilbakestill\"\nHours=\"Timer\"\nMinutes=\"Minutter\"\nSeconds=\"Sekunder\"\nDeprecated=\"Foreldet\"\nReplayBuffer=\"Omspillingsbuffer\"\nImport=\"Importer\"\nExport=\"Eksporter\"\nCopy=\"Kopier\"\nPaste=\"Lim inn\"\nPasteReference=\"Lim inn (Referanse)\"\nPasteDuplicate=\"Lim inn (Duplikat)\"\nRemuxRecordings=\"Remux Opptak\"\nNext=\"Neste\"\nBack=\"Tilbake\"\nDefaults=\"Standard\"\nRestoreDefaults=\"Standard\"\nHideMixer=\"Skjul i mikser\"\nTransitionOverride=\"Overstyr overgang\"\nShowTransition=\"Vis overgang\"\nHideTransition=\"Skjul overgang\"\nNone=\"Ingen\"\nStudioMode.Preview=\"Forhåndsvisning\"\nStudioMode.PreviewSceneName=\"Forhåndsvisning: %1\"\nShowInMultiview=\"Vis i flervisningen\"\nVerticalLayout=\"Loddrett oppsett\"\nGroup=\"Gruppe\"\nDoNotShowAgain=\"Ikke vis igjen\"\nDefault=\"(Standard)\"\nCalculating=\"Kalkulerer...\"\nFullscreen=\"Fullskjerm\"\nWindowed=\"Vindusbasert\"\nRefreshBrowser=\"Oppdater\"\nAspectRatio=\"Størrelsesforhold <b>%1:%2</b>\"\nLockVolume=\"Lås volum\"\nLogViewer=\"Logg leser\"\nShowOnStartup=\"Vis ved oppstart\"\nOpenFile=\"Åpne fil\"\nAddSource=\"Legg til kilde\"\nRemoveScene=\"Fjern den valgte scenen\"\nRemoveSource=\"Fjern valgt(e) kilde(r)\"\nMoveSceneUp=\"Flytt scene opp\"\nMoveSceneDown=\"Flytt scene ned\"\nMoveSourceUp=\"Flytt kilde(r) opp\"\nMoveSourceDown=\"Flytt kilde(r) ned\"\nSourceProperties=\"Åpne kildeegenskaper\"\nSourceFilters=\"Åpne kildefiltre\"\nMixerToolbarMenu=\"Lydmikser-meny\"\nSceneFilters=\"Åpne scenefiltre\"\nList=\"Liste\"\nGrid=\"Rutenett\"\nAutomatic=\"Automatisk\"\nPluginsFailedToLoad.Title=\"Utvidelsesinnlastingsfeil\"\nPluginsFailedToLoad.Text=\"Følgende utvidelser kunne ikke lastes: \\n\\n%1\\n Vennligst oppdater eller fjern disse utvidelsene.\"\nAlreadyRunning.Title=\"OBS kjører allerede\"\nAlreadyRunning.Text=\"OBS kjører allerede! Hvis dette ikke var bevisst, vennligst lukk alle eksisterende kjørende tilfeller av OBS før du kjører noen nye. Hvis du har satt OBS til å minimere til oppgavelinjen, vennligst sjekk om den fortsatt kjører der.\"\nAlreadyRunning.LaunchAnyway=\"Start uansett\"\nSafeMode.Restart=\"Ønsker du å starte OBS i sikker-modus (tredjeparts utvidelser, skripting og WebSockets deaktivert)?\"\nSafeMode.RestartNormal=\"Ønsker du å starte OBS på nytt som normalt?\"\nChromeOS.Title=\"Usupportert platform\"\nChromeOS.Text=\"OBS ser ut til å kjøre i en Chrome OS-kontainer. Denne plattformen støttes ikke.\"\nWine.Title=\"Wine oppdaget\"\nWine.Text=\"Å kjøre OBS i Wine er ikke støttet, og mange funksjoner som opptak eller enhetskilder vil ikke fungere eller bare i begrenset kapasitet.<br><br>Det anbefales å kjøre en opprinnelig versjon av OBS i stedet. for eksempel <a href='https://flathub.org/apps/details/com.obsproject.Studio'>vår Flatpak versjon</a> eller operativsystemets pakker.\"\nDockCloseWarning.Title=\"Lukker festbart vindu\"\nDockCloseWarning.Text=\"Du stengte nettopp et dokkbart vindu. Hvis du vil vise det igjen, bruk Dokker-menyen i menylinjen.\"\nExtraBrowsers=\"Tilpassede nettleseroppgavelinjer\"\nExtraBrowsers.Info=\"Legg til oppgavelinjer ved å gi dem et navn og en URL. Klikk deretter på Bruk eller Lukk for å åpne oppgavelinjene. Du kan når som helst legge til eller fjerne en oppgavelinje.\"\nExtraBrowsers.DockName=\"Oppgavelinjenavn\"\nAuth.Authing.Title=\"Autentiserer...\"\nAuth.Authing.Text=\"Autentiserer med %1, vennligst vent...\"\nAuth.AuthFailure.Title=\"Autentiseringsfeil\"\nAuth.AuthFailure.Text=\"Mislyktes i å autentiseres opp mot %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Autentisering er påkrevd\"\nAuth.InvalidScope.Text=\"Autentiseringskravene til %1 har blitt forandret. Noen egenskaper vil kanskje ikke være tilgjengelige.\"\nAuth.LoadingChannel.Title=\"Laster informasjon om kanalen...\"\nAuth.LoadingChannel.Text=\"Laster kanalinformasjon for %1, vennligst vent...\"\nAuth.LoadingChannel.Error=\"Fikk ikke tak i kanalinformasjonen.\"\nAuth.ChannelFailure.Title=\"Mislyktes i å laste inn kanalen\"\nAuth.ChannelFailure.Text=\"Mislyktes i å laste ned kanalinformasjon om %1\\n\\n%2: %3\"\nAuth.Chat=\"Nettprat\"\nAuth.StreamInfo=\"Informasjon om sendingen\"\nTwitchAuth.Stats=\"Twitch-statistikker\"\nTwitchAuth.Feed=\"Twitch-aktivitetsfeed\"\nTwitchAuth.TwoFactorFail.Title=\"Kunne ikke etterspørre strømme-nøkkel\"\nTwitchAuth.TwoFactorFail.Text=\"OBS kunne ikke koble til din Twitch-konto. Vennligst forsikre at to-faktorautentisering er satt opp i dine <a href='https://www.twitch.tv/settings/security'>Twitch sikkerhetsinstillinger</a>, siden dette er nødvendig for å strømme.\"\nRestreamAuth.Channels=\"Restream-kanaler\"\nCopy.Filters=\"Kopier filtre\"\nPaste.Filters=\"Lim inn filtre\"\nBrowserPanelInit.Title=\"Initialiserer nettleser...\"\nBrowserPanelInit.Text=\"Initialiserer nettleser, vennligst vent...\"\nBandwidthTest.Region.US=\"USA\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Other=\"Andre\"\nBasic.AutoConfig=\"Auto-oppsettsveiviser\"\nBasic.AutoConfig.ApplySettings=\"Bruk innstillinger\"\nBasic.AutoConfig.StartPage=\"Bruksinformasjon\"\nBasic.AutoConfig.StartPage.SubTitle=\"Angi hva du vil bruke programmet til\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimaliser for strømming, innspilling kommer i andre rekke\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimaliser for innspilling, jeg skal ikke streame\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Jeg har kun behov for det virtuelle kamera\"\nBasic.AutoConfig.VideoPage=\"Videoinnstillinger\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Velg video innstillingene du ønsker å bruke\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Bruk Gjeldende (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Skjerm %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Bruk Gjeldende (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 eller 30, men foretrekk 60 når mulig\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 eller 30, men foretrekk høy oppløsning\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"NB: Oppløsningen på lerretet er ikke nødvendigvis den samme oppløsningen som du vil kringkaste eller ta opp med. Den faktiske kringkastings- eller opptaks-oppløsningen kan bli nedskalert fra størrelsen på lerretet for å redusere ressursbruken eller bitraten.\"\nBasic.AutoConfig.StreamPage=\"Strømmens informasjon\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Vennligst skriv inn strømmens informasjon\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Koble til med konto (anbefalt)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Koble fra kontoen\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Vil du koble fra kontoen?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Endringen vil bli benyttet umiddelbart. Er du sikker på at du vil koble fra kontoen din?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Få strømmenøkkel\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Mer informasjon\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Bruk strømmenøkkel\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Bruk strømmenøkkel (avansert)\"\nBasic.AutoConfig.StreamPage.Service=\"Tjeneste\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Vis alle...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Egendefinert …\"\nBasic.AutoConfig.StreamPage.Server=\"Tjener\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Strømmenøkkel\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: skriv inn krypterings-passordet.\\nRTMP: skriv in nøkkel levert av tjenesten.\\nSRT: skriv inn strømmings-ID om tjenesten bruker dette.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kodernøkkel\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Tilkobla konto\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Beregn bitraten med en båndbreddetest (Kan ta noen minutter)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Foretrekk maskinvarekoding\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Maskinvarekoding reduserer mesteparten av CPU-bruken, men kan kreve mer bitrate for å få samme nivå av kvalitet.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Direktesendingsadvarsel\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Båndbreddetesten kommer til å kringkaste tilfeldig videodata uten lyd til kanalen din. Om du kan, er det anbefalt at du midlertidig skrur av alternativet for å lagre videoer, samt sette kanalen din som privat til testen er ferdig. Vil du fortsette?\"\nBasic.AutoConfig.TestPage=\"Sluttresultater\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Programmet kjører en rekke tester for å beregne de mest ideelle innstillingene\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testingen er fullført\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Utfører båndbreddetest. Dette kan ta noen minutter...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Ingen utgang for protokollen av denne tjenesten var funnet\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Kobler til: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Kan ikke koble til noen servere, kontroller Internett-tilkoblingen og prøv på nytt.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Tester båndbredde til: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Tester kringkastingskoder, dette kan ta litt tid ...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Tester opptakskoder, dette kan ta litt tid...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Kunne ikke starte koder\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Tester %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Kringkastingskoder\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Opptakskoder\"\nBasic.AutoConfig.TestPage.Result.Header=\"Programmet har estimert at disse innstillingene er den mest ideelle for deg:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"For å bruke disse innstillingene, klikker du Bruk innstillinger. Å konfigurere veiviseren og prøv på nytt, klikker du tilbake. For å konfigurer innstillinger manuelt selv, klikk Avbryt og åpne innstillinger.\"\nBasic.AutoConfig.Info=\"Veiviseren for automatisk konfigurasjon finner de beste innstillingene basert på din datamaskins spesifikasjon og internetthastighet.\"\nBasic.AutoConfig.RunAnytime=\"Denne kan kjøre når som helst ved å gå til verktøy menyen.\"\nBasic.Stats=\"Statistikk\"\nBasic.Stats.CPUUsage=\"CPU-bruk\"\nBasic.Stats.HDDSpaceAvailable=\"Lagringsplass tilgjengelig\"\nBasic.Stats.MemoryUsage=\"Minnebruk\"\nBasic.Stats.AverageTimeToRender=\"Gjennomsnittlig tid for å rendre ett bilde\"\nBasic.Stats.SkippedFrames=\"Hoppet over bilder på grunn av problemer med enkoding\"\nBasic.Stats.MissedFrames=\"Noen bilder blir borte på grunn av problemer med enkoding\"\nBasic.Stats.Output.Stream=\"Strøm\"\nBasic.Stats.Output.Recording=\"Tar opp\"\nBasic.Stats.Status.Recording=\"Tar opp\"\nBasic.Stats.Status.Live=\"DIREKTE\"\nBasic.Stats.Status.Reconnecting=\"Kobler til på nytt\"\nBasic.Stats.Status.Inactive=\"Inaktiv\"\nBasic.Stats.Status.Active=\"Aktiv\"\nBasic.Stats.DroppedFrames=\"Tapte bilder (nettverk)\"\nBasic.Stats.MegabytesSent=\"Total datamengde ut\"\nBasic.Stats.Bitrate=\"Bitfrekvens\"\nBasic.Stats.DiskFullIn=\"Lagringsplass fullt om (ca.)\"\nBasic.Stats.ResetStats=\"Tilbakestill statistikk\"\nResetUIWarning.Title=\"Er du sikker på at du vil tilbakestille grensesnittet?\"\nResetUIWarning.Text=\"Tilbakestilling av grensesnittet vil skjule andre kildevisninger. Du må velge å vise disse fra Docks-menyen hvis du ønsker at disse skal være synlige.\\n\\nEr du sikker på at du vil tilbakestille grensesnittet?\"\nUpdater.Title=\"Ny oppdatering tilgjengelig\"\nUpdater.Text=\"Det finnes en ny oppdatering:\"\nUpdater.UpdateNow=\"Oppdater nå\"\nUpdater.RemindMeLater=\"Minn meg på det senere\"\nUpdater.Skip=\"Hopp over versjonen\"\nUpdater.NoUpdatesAvailable.Title=\"Ingen oppdateringer er tilgjengelig\"\nUpdater.NoUpdatesAvailable.Text=\"Ingen oppdateringer er tilgjengelig\"\nUpdater.BranchNotFound.Title=\"Oppdateringskanalen ble fjernet\"\nUpdater.BranchNotFound.Text=\"Din valgte oppdateringskanal er ikke lengre tilgjengelig og OBS har blitt tilbakestilt til standard.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Integritetssjekken er utilgjengelig\"\nUpdater.RepairButUpdatesAvailable.Text=\"Integritetssjekk er bare tilgjengelig for den nyeste versjonen av OBS. Bruk Hjelp → Se etter oppdateringer for å verifisere og oppdatere OBS.\"\nUpdater.RepairConfirm.Title=\"Bekreft integritetssjekk\"\nUpdater.RepairConfirm.Text=\"Integritetssjekken vil skanne OBS-installasjonen for korrupte filer og erstatte ødelagte/modifiserte filer. Dette kan ta noen minutter.\\n\\nØnsker du å fortsette?\"\nUpdater.FailedToLaunch=\"Kunne ikke starte oppdaterer\"\nQuickTransitions.SwapScenes=\"Bytt forhåndsvisning/programscener etter overgang\"\nQuickTransitions.SwapScenesTT=\"Bytter forhåndsvisnings- og programscenen etter overgang (hvis programmets opprinnelige scene fortsatt eksisterer).\\nDette vil ikke annullere endringer som kan ha blitt gjort på programmets opprinnelige scene.\"\nQuickTransitions.DuplicateScene=\"Dupliser scene\"\nQuickTransitions.DuplicateSceneTT=\"Ved redigering av samme scene kan du endre kilders transformering/synlighet uten å endre programvisningen.\\nFor å redigere kilders egenskaper uten å endre programvisningen, må du aktivere 'Dupliserte kilder'.\\nEndring av denne verdien vil tilbakestille den gjeldende programscenen (hvis den fortsatt eksisterer).\"\nQuickTransitions.EditProperties=\"Dupliser kilder\"\nQuickTransitions.HotkeyName=\"Hurtigovergang: %1\"\nBasic.AddTransition=\"Legg til konfigurerbar overgang\"\nBasic.RemoveTransition=\"Fjern konfigurerbar overgang\"\nBasic.TransitionProperties=\"Overgangsegenskaper\"\nBasic.SceneTransitions=\"Sceneoverganger\"\nBasic.TransitionDuration=\"Varighet\"\nBasic.TogglePreviewProgramMode=\"Studiomodus\"\nBasic.EnablePreviewProgramMode=\"Skru på studiomodus\"\nBasic.DisablePreviewProgramMode=\"Skru av studiomodus\"\nUndo.Undo=\"Angre\"\nUndo.Redo=\"Gjør om\"\nUndo.Add=\"Legg til \\\"%1\\\"\"\nUndo.Delete=\"Slett \\\"%1\\\"\"\nUndo.Rename=\"Gi nytt navn '%1'\"\nUndo.SceneCollection.Switch=\"Bytt til '%1'\"\nUndo.Item.Undo=\"Angre %1\"\nUndo.Item.Redo=\"Gjør om %1\"\nUndo.Sources.Multi=\"Slett %1 Kilder\"\nUndo.Filters=\"Filtrer endringer på '%1'\"\nUndo.Filters.Paste.Single=\"Lim inn filter '%1' til '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopier filtre fra '%1' til '%2'\"\nUndo.Transform=\"Omform kilde(r) i '%1'\"\nUndo.Transform.Paste=\"Lim in Omforming i '%1'\"\nUndo.Transform.Rotate=\"Rotering i '%1'\"\nUndo.Transform.Reset=\"Tilbakestill transformering i '%1'\"\nUndo.Transform.HFlip=\"Snu horisontalt i '%1'\"\nUndo.Transform.VFlip=\"Snu vertikalt i '%1'\"\nUndo.Transform.FitToScreen=\"Tilpass til skjermen i '%1'\"\nUndo.Transform.StretchToScreen=\"Strekk til skjerm i '%1'\"\nUndo.Transform.Center=\"Midtstill til skjerm i '%1'\"\nUndo.Transform.VCenter=\"Sentrer vertikalt til skjerm i '%1'\"\nUndo.Transform.HCenter=\"Sentrer horisontalt til skjerm i '%1'\"\nUndo.Volume.Change=\"Volum endring i '%1'\"\nUndo.Volume.Mute=\"Demp '%1'\"\nUndo.Volume.Unmute=\"Skru av demping '%1'\"\nUndo.Balance.Change=\"Lydbalanse endring i '%1'\"\nUndo.SyncOffset.Change=\"Lyd synkroniseringsforskyvning på '%1'\"\nUndo.MonitoringType.Change=\"Endre Lydovervåking i '%1'\"\nUndo.Mixers.Change=\"Endre lydmikser i '%1'\"\nUndo.ForceMono.On=\"Aktiver tvungen mono på '%1'\"\nUndo.ForceMono.Off=\"Deaktiver tvungen mono på '%1'\"\nUndo.Properties=\"Endring av egenskap i '%1'\"\nUndo.Scene.Duplicate=\"Dupliser scene '%1'\"\nUndo.ShowTransition=\"Vis overgang på '%1'\"\nUndo.HideTransition=\"Skjul overgang på '%1'\"\nUndo.ShowSceneItem=\"Vis '%1' i '%2'\"\nUndo.HideSceneItem=\"Skjul '%1' i '%2'\"\nUndo.ReorderSources=\"Omorganiser kilder i '%1'\"\nUndo.MoveUp=\"Flytt '%1' opp i '%2'\"\nUndo.MoveDown=\"Flytt '%1 ned i '%2'\"\nUndo.MoveToTop=\"Flytt '%1' til toppen i '%2'\"\nUndo.MoveToBottom=\"Flytt '%1' til bunnen i '%2'\"\nUndo.PasteSource=\"Lim inn kilde(r) i '%1'\"\nUndo.PasteSourceRef=\"Lim inn kildereferanse(r) i '%1'\"\nUndo.GroupItems=\"Grupper elementer i '%1'\"\nTransitionNameDlg.Text=\"Gi et navn til overgangen\"\nTransitionNameDlg.Title=\"Overgangsnavn\"\nTitleBar.SafeMode=\"SIKKER MODUS\"\nTitleBar.PortableMode=\"Bærbar modus\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Scener\"\nNameExists.Title=\"Navnet eksisterer allerede\"\nNameExists.Text=\"Dette navnet er allerede i bruk.\"\nNoNameEntered.Title=\"Vennligst oppgi et gyldig navn\"\nNoNameEntered.Text=\"Du kan ikke bruke et tomt navn.\"\nConfirmStart.Title=\"Vil du starte sendingen?\"\nConfirmStart.Text=\"Er du sikker på at du vil begynne å sende?\"\nConfirmStop.Title=\"Vil du avslutte sendingen?\"\nConfirmStop.Text=\"Er du sikker på at du vil avslutte sendingen?\"\nConfirmStopRecord.Title=\"Vil du stoppe opptaket?\"\nConfirmStopRecord.Text=\"Er du sikker på at du vil stoppe innspillingen?\"\nConfirmBWTest.Title=\"Start bredbåndstest?\"\nConfirmBWTest.Text=\"Du har konfigurert OBS i bredbåndstest-modus. Denne modusen tillater nettverkstesting uten at din kanal går live. Du må deaktivere modusen når du er ferdig å teste for at seere vil kunne se din strøm. \\n\\nVil du fortsette?\"\nConfirmRemove.Title=\"Bekreft Fjerning\"\nConfirmRemove.Text=\"Er du sikker på at du vil fjerne '%1'?\"\nConfirmRemove.TextMultiple=\"Er du sikker du ønsker å fjerne %1 filer?\"\nConfirmReset.Title=\"Tilbakestill egenskaper\"\nConfirmReset.Text=\"Er du sikker du ønsker å tilbakestille gjeldende innstillinger?\"\nOutput.StartStreamFailed=\"Kan ikke starte streaming\"\nOutput.StartRecordingFailed=\"Kan ikke starte innspillingen\"\nOutput.StartReplayFailed=\"Kunne ikke å starte opp omspillingsbufferen\"\nOutput.StartVirtualCamFailed=\"Mislyktes i å starte opp virtuelt kamera\"\nOutput.StartFailedGeneric=\"Oppstart av utdata har mislyktes. Vennligst se loggen for detaljer.\\n\\nBemerk at dersom du bruker NVENC- eller AMD-kodere, pass på at skjermdriverne er oppdatert.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Kan ikke lagre repriser mens innspillingen er på pause\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Advarsel: Repriser kan ikke lagres mens innspillingen er satt på pause.\"\nOutput.ConnectFail.Title=\"Tilkobling mislyktes\"\nOutput.ConnectFail.BadPath=\"Ugyldig filbane eller tilkoblings-URL. Vennligst sjekk at innstillingene dine er riktige.\"\nOutput.ConnectFail.ConnectFailed=\"Klarte ikke å koble til tjeneren\"\nOutput.ConnectFail.InvalidStream=\"Kunne ikke få adgang til den angitte kanalen eller strømmenøkkelen. Strømmenøkkelen kan være feil eller det kan være et problem med tilkoblingen til tjeneren.\"\nOutput.ConnectFail.HdrDisabled=\"HDR er deaktivert for denne utgangen.\"\nOutput.ConnectFail.Error=\"En uventet feil oppstod ved tilkobling til serveren. Detaljert informasjon kan du finne i loggfila.\"\nOutput.ConnectFail.Disconnected=\"Koblet fra tjeneren.\"\nOutput.StreamEncodeError.Title=\"Kodingsfeil\"\nOutput.StreamEncodeError.Msg=\"En kodingsfeil skjedde under strømmingen.\"\nOutput.StreamEncodeError.Msg.LastError=\"En kodingsfeil oppstod under strømming:<br><br>%1\"\nOutput.RecordFail.Title=\"Kunne ikke starte opptak\"\nOutput.RecordFail.Unsupported=\"Utgangsformatet støttes enten ikke eller støtter ikke mer enn ett lydspor. Kontroller innstillingene og prøv igjen.\"\nOutput.RecordNoSpace.Title=\"Ikke nok diskplass\"\nOutput.RecordNoSpace.Msg=\"Det er ikke nok diskplass til å fortsette opptaket.\"\nOutput.RecordError.Title=\"Innspillingsfeil\"\nOutput.RecordError.Msg=\"Det oppstod en uspesifisert feil under opptaket.\"\nOutput.RecordError.EncodeErrorMsg=\"En kodingsfeil skjedde under opptaket.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"En kodingsfeil oppstod under opptak:<br><br>%1\"\nOutput.BadPath.Title=\"Ugyldig Filbane\"\nOutput.BadPath.Text=\"Konfigurert opptaksbane kunne ikke bli åpnet. Vennligst sjekk opptaksbane-feltet i Innstillinger → Utgang → Opptak.\"\nOutput.NoBroadcast.Title=\"Ingen Sending Konfigurert\"\nOutput.NoBroadcast.Text=\"Du må sette opp strømming før du kan strømme.\"\nOutput.BroadcastStartFailed=\"Kunne ikke starte kringkastingen\"\nOutput.BroadcastStopFailed=\"Kunne ikke stoppe kringkastingen\"\nRemux.SourceFile=\"OBS-opptak\"\nRemux.TargetFile=\"Målfil\"\nRemux.Remux=\"Remuks\"\nRemux.Stop=\"Stopp remuksing\"\nRemux.ClearFinished=\"Tøm fullførte elementer\"\nRemux.ClearAll=\"Tøm alle elementer\"\nRemux.OBSRecording=\"OBS-opptak\"\nRemux.FinishedTitle=\"Remuksing ferdig\"\nRemux.Finished=\"Opptak remukset\"\nRemux.FinishedError=\"Opptak remukset, men filen kan være ufullstendig.\"\nRemux.SelectRecording=\"Velg OBS-opptak...\"\nRemux.SelectTarget=\"Velg målfil...\"\nRemux.FileExistsTitle=\"Målfiler finnes allerede\"\nRemux.FileExists=\"De følgende målfilene finnes allerede. Vil du erstatte dem?\"\nRemux.ExitUnfinishedTitle=\"Remuksing pågår\"\nRemux.ExitUnfinished=\"Remuks er ikke ferdig. Om du avbryter kan målfilen bli ubrukelig.\\nEr du sikker på at du vil avbryte remuksingen?\"\nRemux.HelpText=\"Slipp filer ned i dette vinduet for å remukse, eller velg en tom «OBS-opptak»-celle for å lete etter en fil.\"\nRemux.NoFilesAddedTitle=\"Ingen remuks-fil er lagt til\"\nRemux.NoFilesAdded=\"Ingen fil er lagt til remuks. Legg til en mappe med en eller flere videofiler.\"\nMissingFiles=\"Manglende Filer\"\nMissingFiles.MissingFile=\"Manglende Fil\"\nMissingFiles.NewFile=\"Ny fil\"\nMissingFiles.HelpText=\"Noen filer mangler siden du sist brukte OBS.\"\nMissingFiles.Clear=\"<ryddet>\"\nMissingFiles.NumFound=\"Fant %1 of %2\"\nMissingFiles.Search=\"Søk Katalog...\"\nMissingFiles.SelectFile=\"Velg fil...\"\nMissingFiles.SelectDir=\"Velg mappe for Søk i\"\nMissingFiles.State=\"Tilstand\"\nMissingFiles.Missing=\"Mangler\"\nMissingFiles.Replaced=\"Erstattet\"\nMissingFiles.Cleared=\"Ryddet\"\nMissingFiles.Found=\"Funnet\"\nMissingFiles.AutoSearch=\"Ytterligere samsvarende filer funnet.\"\nMissingFiles.AutoSearchText=\"OBS har funnet flere treff for manglende filer i den mappen. Ønsker du å legge de til?\"\nMissingFiles.NoMissing.Title=\"Sjekk for manglende filer\"\nMissingFiles.NoMissing.Text=\"Ingen filer ser ut til å mangle.\"\nMacPermissions.MenuAction=\"Gå gjennom apptillatelser …\"\nMacPermissions.Title=\"Gå gjennom apptillatelser\"\nMacPermissions.Description=\"OBS Studio trenger din tillatelse for å tilby noen funksjoner. Det er anbefalt å tillate disse, men det er ikke nødvendig for å bruke programmet. Du kan gi tillatelse senere.\"\nMacPermissions.Description.OpenDialog=\"Du kan åpne denne dialogen på nytt i OBS Studio-menyen.\"\nMacPermissions.AccessGranted=\"Tilgang innvilget\"\nMacPermissions.RequestAccess=\"Be om tilgang\"\nMacPermissions.OpenPreferences=\"Åpne %1-innstillinger\"\nMacPermissions.Item.ScreenRecording=\"Skjermopptak\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS krever denne tillatelseen for å kunne ta opptak av skjermen din.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Denne tillatelsen er nødvendig for å ta opptak fra webkamera eller opptakskort.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS trenger denne tillatelsen hvis du ønsker å ta opptak av mikrofonen din eller eksternt lydkort.\"\nMacPermissions.Item.Accessibility=\"Tilgjengelighet\"\nMacPermissions.Continue=\"Fortsett\"\nSourceLeak.Title=\"Kildeopprydningsfeil\"\nBasic.DesktopDevice1=\"Skrivebordlyd\"\nBasic.DesktopDevice2=\"Skrivebordlyd 2\"\nBasic.AuxDevice1=\"Mikrofon/Aux\"\nBasic.AuxDevice2=\"Mikrofon/Aux 2\"\nBasic.AuxDevice3=\"Mikrofon/Aux 3\"\nBasic.AuxDevice4=\"Mikrofon/Aux 4\"\nBasic.DisplayCapture=\"Skjermopptak\"\nBasic.Main.PreviewConextMenu.Enable=\"Aktiver forhåndsvisning\"\nBasic.Main.Preview.Disable=\"Deaktiver forhåndsvisning\"\nScaleFiltering=\"Skaleringsfiltrering\"\nScaleFiltering.Point=\"Punkt\"\nScaleFiltering.Bilinear=\"Bilineær\"\nScaleFiltering.Bicubic=\"Bikubisk\"\nScaleFiltering.Area=\"Område\"\nBlendingMethod=\"Blandemetode\"\nBlendingMethod.Default=\"Standard\"\nBlendingMethod.SrgbOff=\"SRGB Av\"\nBlendingMode=\"Blandemodus\"\nBlendingMode.Additive=\"Legg til\"\nBlendingMode.Subtract=\"Trekk fra\"\nBlendingMode.Screen=\"Skjerm\"\nBlendingMode.Multiply=\"Multipliser\"\nBlendingMode.Lighten=\"Lysere\"\nBlendingMode.Darken=\"Mørkere\"\nDeinterlacing=\"Avsammenfletting\"\nDeinterlacing.Discard=\"Forkast\"\nDeinterlacing.TopFieldFirst=\"Øverste felt først\"\nDeinterlacing.BottomFieldFirst=\"Nederste felt først\"\nVolControl.SliderUnmuted=\"Volumskyveknappen for '%1':\"\nVolControl.SliderMuted=\"Volumskyveknappen for '%1': (dempet)\"\nVolControl.Mute=\"Demp '%1'\"\nVolControl.Properties=\"Egenskaper for '%1'\"\nVolControl.UnassignedWarning.Title=\"Utildelt lydkilde\"\nVolControl.UnassignedWarning.Text=\"«%1» er ikke tildelt noen lydspor og vil ikke kunne høres i strømmer eller opptak.\\n\\nFor å tildele en lydkilde til et spor, kan du åpne Avanserte lydinnstillinger via høyre-klikkmenyen eller ved å trykke på tannhjulet i mixer-dokkens verktøylinje.\"\nBasic.Main.AddSceneDlg.Title=\"Ny Scene\"\nBasic.Main.AddSceneDlg.Text=\"Vennligst gi et navn til scenen.\"\nBasic.Main.AddSceneCollection.Title=\"Legg til scenesamling\"\nBasic.Main.AddSceneCollection.Text=\"Skriv inn navnet på scenesamlingen\"\nBasic.Main.RenameSceneCollection.Title=\"Gi nytt navn til scenesamlingen\"\nBasic.Main.RemigrateSceneCollection.Title=\"Oppdater oppløsningen til scenesamlingen\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Kan ikke oppdatere oppløsning til scenesamling imens utganger er aktive.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Oppdatering av scenesamlingens oppløsning mislyktes. Den opprinnelige oppløsningen er ukjent.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Tilbakestilling er ikke mulig: Endring av OBS-oppløsning mislyktes.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Tilbakestilling ikke mulig: Nåværende oppløsning er allerede lik basisoppløsningen til scenesamlingen.\"\nAddProfile.Title=\"Legg til profil\"\nAddProfile.Text=\"Skriv inn navn på profilen\"\nAddProfile.WizardCheckbox=\"Vis autokonfigurasjons veiviser\"\nRenameProfile.Title=\"Gi nytt navn til profil\"\nBasic.Main.MixerRename.Title=\"Gi nytt navn til lydkilde\"\nBasic.Main.MixerRename.Text=\"Vennligst skriv inn navnet til lydkilden\"\nBasic.Main.PreviewDisabled=\"Forhåndsvisning er deaktivert\"\nBasic.SourceSelect=\"Opprett eller velg kilde\"\nBasic.SourceSelect.CreateNew=\"Opprett\"\nBasic.SourceSelect.AddExisting=\"Legg til eksisterende\"\nBasic.SourceSelect.AddVisible=\"Gjør kilden synlig\"\nBasic.Main.Sources.Visibility=\"Synlighet\"\nBasic.Main.Sources.VisibilityDescription=\"Kontrollerer synligheten til '%1' i lerretet\"\nBasic.Main.Sources.Lock=\"Lås\"\nBasic.Main.Sources.LockDescription=\"Låser posisjonen og størrelsen av '%1' i lerretet\"\nBasic.PropertiesWindow=\"Egenskaper for '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (selvvalg: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Velg farge\"\nBasic.PropertiesWindow.SelectFont=\"Velg skrifttype\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Velg en skrifttype\"\nBasic.PropertiesWindow.ConfirmTitle=\"Innstillingene er endret\"\nBasic.PropertiesWindow.Confirm=\"Det er ulagrede endringer. Vil du beholde dem?\"\nBasic.PropertiesWindow.NoProperties=\"Ingen egenskaper er tilgjengelige\"\nBasic.PropertiesWindow.AddFiles=\"Legg til filer\"\nBasic.PropertiesWindow.AddDir=\"Legg til mappe\"\nBasic.PropertiesWindow.AddURL=\"Legg til fil- eller URL-adresse\"\nBasic.PropertiesWindow.AddEditableListDir=\"Legg til mappe i «%1»\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Legg filer til '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Legg oppføring til '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Endre oppføring fra '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Bildefrekvens (enkel)\"\nBasic.PropertiesView.FPS.Rational=\"Bildefrekvens (rasjonalt tall)\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Gyldige bildefrekvensrekkevidder:\"\nBasic.PropertiesView.UrlButton.Text=\"Åpne denne linken i standard nettleser?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Åpne URL\"\nBasic.InteractionWindow=\"Kommuniserer med «%1»\"\nBasic.StatusBar.Reconnecting=\"Frakoblet. Kobler til om %2 sekund(er) (Forsøk %1).\"\nBasic.StatusBar.AttemptingReconnect=\"Prøver tilkobling… (Forsøk %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Ny tilkobling vellykket\"\nBasic.StatusBar.Delay=\"Forsinkelse (%1 sek)\"\nBasic.StatusBar.DelayStartingIn=\"Forsinkelse (starter om %1 sek)\"\nBasic.StatusBar.DelayStoppingIn=\"Forsinkelse (stopper om %1 sek)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Forsinkelse (stopper om %1 sek, starter om %2 sek)\"\nBasic.StatusBar.RecordingSavedTo=\"Opptaket er lagret i '%1\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Omspillingsbufferen lagret i '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Skjermbilde lagret i '%1\"\nBasic.StatusBar.AutoRemuxedTo=\"Opptak automatisk remukset til '%1'\"\nBasic.Filters=\"Filtre\"\nBasic.Filters.AsyncFilters=\"Lyd- og videofiltre\"\nBasic.Filters.AudioFilters=\"Lydfiltre\"\nBasic.Filters.EffectFilters=\"Effektfiltre\"\nBasic.Filters.Title=\"Filtre for '%1'\"\nBasic.Filters.AddFilter.Title=\"Filternavn\"\nBasic.Filters.AddFilter.Text=\"Angi navnet på filteret\"\nBasic.TransformWindow=\"Transformer sceneelement\"\nBasic.TransformWindow.Position=\"Posisjon\"\nBasic.TransformWindow.PositionX=\"Posisjon X\"\nBasic.TransformWindow.PositionY=\"Posisjon Y\"\nBasic.TransformWindow.Rotation=\"Rotasjon\"\nBasic.TransformWindow.Size=\"Størrelse\"\nBasic.TransformWindow.Width=\"Bredde\"\nBasic.TransformWindow.Height=\"Høyde\"\nBasic.TransformWindow.Alignment=\"Posisjonsoppstilling\"\nBasic.TransformWindow.BoundsType=\"Innholdsavgrensningstype\"\nBasic.TransformWindow.BoundsAlignment=\"Oppstilling i innholdsavgrensning\"\nBasic.TransformWindow.Bounds=\"Innholdsavgrensningsstørrelse\"\nBasic.TransformWindow.BoundsWidth=\"Innholdsavgrensning bredde\"\nBasic.TransformWindow.BoundsHeight=\"Innholdsavgrensning høyde\"\nBasic.TransformWindow.CropToBounds=\"Beskjær til innholdsavgrensning\"\nBasic.TransformWindow.Crop=\"Beskjæring\"\nBasic.TransformWindow.CropLeft=\"Beskjær venstre\"\nBasic.TransformWindow.CropRight=\"Beskjær høyre\"\nBasic.TransformWindow.CropTop=\"Beskjær topp\"\nBasic.TransformWindow.CropBottom=\"Beskjær bunn\"\nBasic.TransformWindow.Alignment.TopLeft=\"Øverst til venstre\"\nBasic.TransformWindow.Alignment.TopCenter=\"Øverst i midten\"\nBasic.TransformWindow.Alignment.TopRight=\"Øverst til høyre\"\nBasic.TransformWindow.Alignment.CenterLeft=\"I midten til venstre\"\nBasic.TransformWindow.Alignment.Center=\"I midten\"\nBasic.TransformWindow.Alignment.CenterRight=\"I midten til høyre\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Nederst til venstre\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Nederst i midten\"\nBasic.TransformWindow.Alignment.BottomRight=\"Nederst til høyre\"\nBasic.TransformWindow.BoundsType.None=\"Ingen avgrensning\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Kun maksimal størrelse\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skalér etter innsiden av avgrensningen\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skalér etter utkanten av avgrensningen\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skalér etter bredden av avgrensningen\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skalér etter høyden av avgrensningen\"\nBasic.TransformWindow.BoundsType.Stretch=\"Strekk etter avgrensningen\"\nBasic.TransformWindow.Title=\"Rediger transformering for '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Ingen kilde valgt\"\nBasic.Main.AddSourceHelp.Title=\"Kan ikke legge til kilde\"\nBasic.Main.AddSourceHelp.Text=\"Du må ha minst én scene for å legge til en kilde.\"\nBasic.Main.Scenes=\"Scener\"\nBasic.Main.Sources=\"Kilder\"\nBasic.Main.Source=\"Kilde\"\nBasic.Main.Controls=\"Kontrollere\"\nBasic.Main.PreparingStream=\"Forbereder ...\"\nBasic.Main.Connecting=\"Kobler til…\"\nBasic.Main.StartRecording=\"Start Opptak\"\nBasic.Main.StartReplayBuffer=\"Start omspillingsbuffer\"\nBasic.Main.SaveReplay=\"Lagre reprise\"\nBasic.Main.StartStreaming=\"Start Strømming\"\nBasic.Main.StartBroadcast=\"Gå Live\"\nBasic.Main.StartVirtualCam=\"Start virtuelt kamera\"\nBasic.Main.StopRecording=\"Stopp Opptak\"\nBasic.Main.PauseRecording=\"Sett opptaket på pause\"\nBasic.Main.UnpauseRecording=\"Fortsett opptaket\"\nBasic.Main.SplitFile=\"Del opptaksfil\"\nBasic.Main.StoppingRecording=\"Stanser innspilling…\"\nBasic.Main.StopReplayBuffer=\"Stopp omspillingsbufferen\"\nBasic.Main.StoppingReplayBuffer=\"Stopper omspillingsbufferen...\"\nBasic.Main.SetupBroadcast=\"Administrere sending\"\nBasic.Main.StopStreaming=\"Stopp Strømming\"\nBasic.Main.StopBroadcast=\"Avslutt sendingen\"\nBasic.Main.AutoStopEnabled=\"(Autostopp)\"\nBasic.Main.StoppingStreaming=\"Avslutter sendingen …\"\nBasic.Main.ForceStopStreaming=\"Avslutt sendingen (forkast forsinkelse)\"\nBasic.Main.ShowContextBar=\"Vis verktøylinje for kilde\"\nBasic.Main.HideContextBar=\"Skjul verktøylinje for kilde\"\nBasic.Main.StopVirtualCam=\"Stopp virtuelt kamera\"\nBasic.Main.Group=\"Gruppe %1\"\nBasic.Main.GroupItems=\"Gruppér merkede gjenstander\"\nBasic.Main.Ungroup=\"Adskill\"\nBasic.Main.GridMode=\"Rutenettmodus\"\nBasic.Main.ListMode=\"Listemodus\"\nBasic.Main.VirtualCamConfig=\"Sett opp virtuelt kamera\"\nBasic.VCam.VirtualCamera=\"Virtuelt kamera\"\nBasic.VCam.OutputType=\"Utdatatype\"\nBasic.VCam.OutputSelection=\"Utdatavalg\"\nBasic.VCam.OutputType.Program=\"Program (standard)\"\nBasic.VCam.OutputSelection.NoSelection=\"Intet valg for denne utgangstypen\"\nBasic.VCam.RestartWarning=\"Det virtuelle kameraet vil startes på nytt for å ta i bruk denne endringen\"\nBasic.MainMenu.File=\"&Fil\"\nBasic.MainMenu.File.Export=\"&Eksportér\"\nBasic.MainMenu.File.Import=\"&Importér\"\nBasic.MainMenu.File.ShowRecordings=\"Vis opptak (&R)\"\nBasic.MainMenu.File.Remux=\"Re&muks opptak\"\nBasic.MainMenu.File.Settings=\"Inn&stillinger\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Vis innstillingsmappen\"\nBasic.MainMenu.File.ShowProfileFolder=\"Vis profilmappen\"\nBasic.MainMenu.File.ShowMissingFiles=\"Sjekk for manglende filer\"\nBasic.MainMenu.File.Exit=\"Avslutt (&X)\"\nBasic.MainMenu.Edit=\"R&edigér\"\nBasic.MainMenu.Edit.Undo=\"Angre (&U)\"\nBasic.MainMenu.Edit.Redo=\"Gjø&r om\"\nBasic.MainMenu.Edit.LockPreview=\"&Lås Forhåndsvisning\"\nBasic.MainMenu.Edit.Scale=\"Forhånd&svisnings-skalering\"\nBasic.MainMenu.Edit.Scale.Window=\"Tilpass til vindu\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Lerret (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Utgang (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"&Transformer\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"R&edigér transformering...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopiere transformering\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Lim inn transformering\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Ang&re transformering\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Rotér 90 grader med klokka\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Rotér 90 grader mot klokka\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Rotér 180 grader\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Vend &horisontalt\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Vend &vertikalt\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Tilpass skjerm (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&Strekk til skjerm\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Midstill på skjermen (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Sentrer vertikalt\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Sentrer horisontalt\"\nBasic.MainMenu.Edit.Order=\"Arrangér (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Flytt opp (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Flytt ne&d\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Legg på &toppen\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Legg på &bunnen\"\nBasic.MainMenu.Edit.AdvAudio=\"&Avanserte lydinnstillinger\"\nBasic.MainMenu.View=\"&Vis\"\nBasic.MainMenu.View.Toolbars=\"Verk&tøylinjer\"\nBasic.MainMenu.View.ListboxToolbars=\"Fest fast verktøylinjer\"\nBasic.MainMenu.View.ContextBar=\"Verktøylinje for kilde\"\nBasic.MainMenu.View.SourceIcons=\"Kilde &ikoner\"\nBasic.MainMenu.View.StatusBar=\"&Statuslinje\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Fullskjermsgrensesnitt\"\nBasic.MainMenu.View.ResetUI=\"&Tilbakestill grensesnitt\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Alltid fremst\"\nBasic.MainMenu.View.SceneListMode=\"Scenelistemodus\"\nBasic.MainMenu.Docks=\"&Dokker\"\nBasic.MainMenu.Docks.ResetDocks=\"&Tilbakestill dokker\"\nBasic.MainMenu.Docks.LockDocks=\"&Lås dokker\"\nBasic.MainMenu.Docks.SideDocks=\"&Fullhøydedokker\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Tilpassede &nettleserdokker …\"\nBasic.MainMenu.Export=\"Eksporter...\"\nBasic.MainMenu.SceneCollection=\"&Scenesamling\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Importer profil\"\nBasic.MainMenu.Profile.Export=\"Eksporter Profil\"\nBasic.MainMenu.SceneCollection.Import=\"Importer Scenesamling\"\nBasic.MainMenu.SceneCollection.Export=\"Eksporter scenesamling\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Tilbakestill grunnoppløsning\"\nBasic.MainMenu.SceneCollection.Migrate=\"Sett grunnoppløsning\"\nBasic.MainMenu.Profile.Exists=\"Profilen eksisterer allerede\"\nBasic.MainMenu.Tools=\"Verk&tøy\"\nBasic.MainMenu.Help=\"&Hjelp\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal for hjelp\"\nBasic.MainMenu.Help.Website=\"Besøk nettstedet (&W)\"\nBasic.MainMenu.Help.Discord=\"Bli med i &Discord-serveren\"\nBasic.MainMenu.Help.WhatsNew=\"Hva er nytt\"\nBasic.MainMenu.Help.Logs=\"&Loggfiler\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Vi&s loggfiler\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Last opp nåværende loggfil (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Last opp den &forrige loggfilen\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Vis gjeldende logg\"\nBasic.MainMenu.Help.ReleaseNotes=\"Lanseringsnotater\"\nBasic.MainMenu.Help.CheckForUpdates=\"Se etter oppdateringer\"\nBasic.MainMenu.Help.Repair=\"Sjekk filintegritet\"\nBasic.MainMenu.Help.RestartSafeMode=\"Omstart i Sikkerhetsmodus\"\nBasic.MainMenu.Help.RestartNormal=\"Start på nytt i normal modus\"\nBasic.MainMenu.Help.CrashLogs=\"K&rasjrapporter\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Vi&s krasjrapporter\"\nBasic.MainMenu.Help.About=\"Om (&A)\"\nBasic.Settings.ProgramRestart=\"Programmet må startes på nytt for at disse innstillingene skal tre i kraft.\"\nBasic.Settings.ConfirmTitle=\"Bekreft endringer\"\nBasic.Settings.Confirm=\"Du har endringer som ikke er lagret. Vil du lagre?\"\nBasic.Settings.General=\"Generelt\"\nBasic.Settings.General.Language=\"Språk\"\nBasic.Settings.General.Updater=\"Oppdateringer\"\nBasic.Settings.General.UpdateChannel=\"Oppdateringskanal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Skrudd av)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Standard)\"\nBasic.Settings.General.EnableAutoUpdates=\"Automatisk se etter oppdateringer ved oppstart\"\nBasic.Settings.General.OpenStatsOnStartup=\"Åpne statistikkvindu ved oppstart\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Skjul OBS-vinduer i skjermopptak\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Vis bekreftelsesdialogboks når du starter strømming\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Vis bekreftelsesdialogboks når stanser strømming\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Vis et bekreftelsevindu når du stopper innspilling\"\nBasic.Settings.General.Projectors=\"Projektorer\"\nBasic.Settings.General.HideProjectorCursor=\"Skjul musepekeren over projektorer\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Alltid vis projektorer øverst\"\nBasic.Settings.General.Snapping=\"Festing ved kildejustering\"\nBasic.Settings.General.ScreenSnapping=\"Fest kilder til kanten av skjermen\"\nBasic.Settings.General.CenterSnapping=\"Fest kilder til vannrett og loddrett midtpunkt\"\nBasic.Settings.General.SourceSnapping=\"Fest kilder til andre kilder\"\nBasic.Settings.General.SnapDistance=\"Festingfølsomhet\"\nBasic.Settings.General.SpacingHelpers=\"Vis pixeljusteringsguider\"\nBasic.Settings.General.RecordWhenStreaming=\"Spill inn automatisk ved strømming\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Fortsett innspilling etter strømming\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Start omspillingsbufferen automatisk når du strømmer\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Hold omspillingsbufferen gående når strømmen stoppes\"\nBasic.Settings.General.SysTray=\"Systemstatusfeltet\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimer til systemstatusfelt ved oppstart\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Alltid minimere til systemstatusfeltet istedet for oppgavelinjen\"\nBasic.Settings.General.SaveProjectors=\"Lagre projektorer ved avslutning\"\nBasic.Settings.General.CloseExistingProjectors=\"Begrens til én fullskjermprojektor per skjerm\"\nBasic.Settings.General.Preview=\"Forhåndsvisning\"\nBasic.Settings.General.OverflowHidden=\"Skjul overflytende områder\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Overflyten skal alltid være synlig\"\nBasic.Settings.General.OverflowSelectionHidden=\"Vis overflyt selv når kilden er usynlig\"\nBasic.Settings.General.Importers=\"Importverktøy\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Søk etter kjente steder etter scenesamlinger når du importerer\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Dobbeltklikking vil gå til scenen\"\nBasic.Settings.General.StudioPortraitLayout=\"Aktiver portrett/loddrett vindu\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Vis etikett for forhåndsvisning/program\"\nBasic.Settings.General.Multiview=\"Flervisning\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Klikk for å bytte mellom scener\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Vis scenenes navn\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Tegn trygge områder (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Flervisningsplassering\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Vannrett, topp (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Vannrett, bunn (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Loddrett, venstre (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Loddrett, høyre (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horisontalt, Topp (18 scener)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Vannrett, topp (24 scener)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Kun scener (4 scener)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Kun scener (9 scener)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Kun scener (16 scener)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Kun scener (25 scener)\"\nBasic.Settings.General.ChannelName.stable=\"Stabil\"\nBasic.Settings.General.ChannelDescription.stable=\"Nyeste stabile versjon\"\nBasic.Settings.General.ChannelName.beta=\"Betaer / utgivelseskandidater\"\nBasic.Settings.General.ChannelDescription.beta=\"Potensielt ustabile testutgivelser\"\nBasic.Settings.Appearance=\"Utseende\"\nBasic.Settings.Appearance.General=\"Generelt\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Stil\"\nBasic.Settings.Appearance.General.NoVariant=\"Ingen stiler tilgjengelig\"\nBasic.Settings.Stream=\"Strøm\"\nBasic.Settings.Stream.Destination=\"Destinasjon\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Bruk autentisering\"\nBasic.Settings.Stream.Custom.Username=\"Brukernavn\"\nBasic.Settings.Stream.Custom.Password=\"Passord\"\nBasic.Settings.Stream.BandwidthTestMode=\"Aktiver båndbreddetest-modus\"\nBasic.Settings.Stream.TTVAddon=\"Twitch chattetillegg\"\nBasic.Settings.Stream.TTVAddon.None=\"Ingen\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV og FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Manglende strømmeoppsett\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Åpne innstillinger\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL og strømmenøkkel mangler.\\n\\nÅpne innstillinger for å angi URL og strømmenøkkel i fanen 'strøm'.\"\nBasic.Settings.Stream.MissingUrl=\"Strømme-URL mangler.\\n\\nÅpne innstillinger for å angi URL-en i fanen 'Strøm'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Strømmenøkkel mangler.\\n\\nÅpne innstillinger for å angi strømmenøkkelen i fanen 'Strøm'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorer strømmetjenestens anbefalinger\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Overstyr anbefalte innstillinger\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Advarsel: Hvis du ignorerer begrensningene for tjenesten, kan det føre til degradert strømmekvalitet eller hindre deg i å strømme.\\n\\nFortsett?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maksimal video-bitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maksimal lyd-bitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksimal oppløsning: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksimal FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Spesifiser egendefinert tjener ...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Egendefinert tjener\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Skru på %1\"\nBasic.Settings.Stream.AdvancedOptions=\"Avanserte innstillinger\"\nBasic.Settings.Output=\"Utdata\"\nBasic.Settings.Output.Format=\"Opptaksformat\"\nBasic.Settings.Output.Format.fMP4=\"Fragmentert MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmentert MOV (.mov)\"\nBasic.Settings.Output.Encoder.Video=\"Videokoder\"\nBasic.Settings.Output.Encoder.Audio=\"Lydkoder\"\nBasic.Settings.Output.SelectDirectory=\"Velg opptaksmappe\"\nBasic.Settings.Output.DynamicBitrate=\"Endre bitrate dynamisk for å håndtere overbelastning\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Endre bitrate dynamisk for å håndtere overbelastning (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"I stedet for å droppe bilderuter for å redusere overbelastning, endres bitrate dynamisk underveis.\\n\\nMerk at dette kan øke forsinkelsen for seerne hvis det plutselig er en betydelig overbelastning.\\nNår bitraten synker, kan gjenopprettingen ta opp mot noen minutter.\\n\\nFor øyeblikket støttes bare RTMP.\"\nBasic.Settings.Output.Mode=\"Utgangsmodus\"\nBasic.Settings.Output.Mode.Simple=\"Enkel\"\nBasic.Settings.Output.Mode.Adv=\"Avansert\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg-utgang\"\nBasic.Settings.Output.UseReplayBuffer=\"Aktiver omspillingsbufferen\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maksimal reprisetid\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maksimalt minne\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Anslått minnebruk: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Advarsel: Estimert minnebruk på %1 MiB er større enn den anbefalte maksstørrelsen på %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Kan ikke beregne minnebruk. Vennligst sett maksimalt minnebrukgrense.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Reprisebufferens filnavnprefiks\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Suffiks\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Omspillingsbufferen kan ikke benyttes når opptakstypen er satt til egendefinert utdata (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Opptaksbane\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Opptakskvalitet\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Samme som strøm\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Høy kvalitet, middels filstørrelse\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Veldig høy kvalitet, stor filstørrelse\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Tapsfri kvalitet, veldig stor filstørrelse\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Advarsel: Strømmens video bitrate har blitt satt til '%1'. Dette er over grensen til den valgte strømmeplatformen.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Advarsel: Strømmens bitrate for lyd har blitt satt til '%1'. Dette er over grensen til valgt strømmeplatform.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Advarsel: Innspillinger kan ikke pauses hvis opptakskvaliteten er satt til \\\"Samme som strøm\\\".\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Advarsel: Opptak med programvarekoder i en annen kvalitetsinnstilling enn strømmingen vil kreve ekstra prosessorressurser om du strømmer og tar opp på samme tid.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Advarsel: Tapsfri kvalitet produserer enorm store filer! Denne innstillingen kan bruke oppimot 7 GB diskplass per minutt ved bruk av høy oppløsning og bildefrekvens. Tapsfri kvalitet anbefales ikke for lengre opptak med mindre du har store mengder diskplass tilgjengelig. Omspillingsbufferen er utilgjengelig når tapsfri kvalitet benyttes.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Er du sikker på at du vil bruke tapsfri kvalitet?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Tapsfri kvalitet advarsel!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Programvare (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Maskinvare (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Maskinvare (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Maskinvare (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Maskinvare (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Maskinvare (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Maskinvare (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Maskinvare (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Maskinvare (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Maskinvare (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Maskinvare (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Programvare (Forhåndsinnstilt til x264 med lav CPU-bruk, øker filstørrelsen)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (standard)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD spor (Bruker spor 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Lydspor\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Inkompatibel oppløsning / bildefrekvens\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Strømmeplatformen støtter ikke din valgte utgangsoppløsning og/eller bildefrekvens. De vil bli endret til næreste kompatible verdier: \\n\\n%1\\n\\n Ønsker du å fortsette?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Oppløsning: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Inkompatibel koder\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Strømmetjenesten «%1» støtter ikke koderen «%2». Koderen vil endres til «%3».\\n\\nØnsker du å fortsette?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Strømmetjenesten «%1» støtter ikke koderene «%2» og «%3». Disse vil endres til «%4» og «%5».\\n\\nØnsker du å fortsette?\"\nBasic.Settings.Output.VideoBitrate=\"Videobitrate\"\nBasic.Settings.Output.AudioBitrate=\"Lydbitrate\"\nBasic.Settings.Output.Reconnect=\"Koble automatisk til på nytt\"\nBasic.Settings.Output.RetryDelay=\"Gjenforsøksintervall\"\nBasic.Settings.Output.MaxRetries=\"Høyst antall tilkoblingsforsøk\"\nBasic.Settings.Output.Advanced=\"Aktiver tilpassede koderinnstillinger (avansert)\"\nBasic.Settings.Output.EncoderPreset=\"Kodingsinstillinger\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (lav CPU-bruk, laveste kvalitet)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (standard) (middels CPU-bruk, standard kvalitet)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (høy CPU-bruk, høy kvalitet)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Egendefinerte koderinnstillinger\"\nBasic.Settings.Output.CustomMuxerSettings=\"Egendefinerte mukserinnstillinger\"\nBasic.Settings.Output.NoSpaceFileName=\"Lag filnavn uten mellomrom\"\nBasic.Settings.Output.Adv.Rescale=\"Reskaler utgang\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Skrudd av\"\nBasic.Settings.Output.Adv.AudioTrack=\"Lydspor\"\nBasic.Settings.Output.Adv.Streaming=\"Strømming\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Strømmeinnstillinger\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Spor 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Spor 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Spor 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Spor 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Spor 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Spor 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD spor\"\nBasic.Settings.Output.Adv.Encoder=\"Koderinnstillinger\"\nBasic.Settings.Output.Adv.Recording=\"Opptak\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Opptaksinnstillinger\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Opptakstype\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Egendefinert utdata (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Bruk strømkoder)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Filnavnformat\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Skriv over om filen eksisterer\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Egendefinert utdata (FFmpeg) tilbys uten noen sikkerhetsfunksjoner.\\nVurder å bruke standard opptakstype.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg-utgangstype\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Utgang til URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Utgang til fil\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Vanlige opptaksformater\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Alle filer\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Filbane eller URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Innpakkingsformat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Lyd\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Standardformat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Innpakkingsformatets beskrivelse\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Lyd- og videokodek gjettet fra filbane eller URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Standardkoder\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Deaktiver koder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Videokoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Videokoderinnstillinger (om noen)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Lydkoder\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Lydkoderinnstillinger (om noen)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Mukserinnstillinger (om noen)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Nøkkelbildeintervall (bilder)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Vis alle kodeker (selv om potensielt ikke-kompatibel)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg-innstillinger\"\nBasic.Settings.Output.EnableSplitFile=\"Automatisk fil-oppdeling\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Del opp etter tid\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Del opp etter størrelse\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Del kun manuelt\"\nBasic.Settings.Output.SplitFile.Time=\"Oppdelt tid\"\nBasic.Settings.Output.SplitFile.Size=\"Delingsstørrelse\"\nScreenshot=\"Skjermbilde av forhåndsvisning\"\nScreenshot.SourceHotkey=\"Skjermbilde av valgt kilde\"\nScreenshot.StudioProgram=\"Skjermbilde (program)\"\nScreenshot.Preview=\"Skjermbilde (forhåndsvisning)\"\nScreenshot.Scene=\"Skjermbilde (Scene)\"\nScreenshot.Source=\"Skjermbilde (Kilde)\"\nFilenameFormatting.TT.CCYY=\"Årstall, fire sifre\"\nFilenameFormatting.TT.YY=\"Årstall, de to siste sifrene (00-99)\"\nFilenameFormatting.TT.MM=\"Måned som et 2-sifret tall (01-12)\"\nFilenameFormatting.TT.DD=\"Dagen i måneden, 2-sifret (01-31)\"\nFilenameFormatting.TT.hh=\"Time i 24-timers format (00-23)\"\nFilenameFormatting.TT.mm=\"Minutt (00-59)\"\nFilenameFormatting.TT.ss=\"Sekund (00-59)\"\nFilenameFormatting.TT.Percent=\"Et %-tegn\"\nFilenameFormatting.TT.a=\"Forkortet ukedagsnavn\"\nFilenameFormatting.TT.A=\"Full ukedagsnavn\"\nFilenameFormatting.TT.b=\"Forkortet månedsnavn\"\nFilenameFormatting.TT.B=\"Fullt månedsnavn\"\nFilenameFormatting.TT.d=\"Dagen i måneden, 2-sifret (01-31)\"\nFilenameFormatting.TT.H=\"Timer i 24-timers format (00-23)\"\nFilenameFormatting.TT.I=\"Time i 12t-format (01-12)\"\nFilenameFormatting.TT.m=\"Måned som et 2-sifret tall (01-12)\"\nFilenameFormatting.TT.M=\"Minutt (00-59)\"\nFilenameFormatting.TT.p=\"AM- eller PM-betegnelse\"\nFilenameFormatting.TT.s=\"Tid i sekunder siden UNIX-epoken startet\"\nFilenameFormatting.TT.S=\"Sekund (00-59)\"\nFilenameFormatting.TT.y=\"Årstall, de to siste sifrene (00-99)\"\nFilenameFormatting.TT.Y=\"År\"\nFilenameFormatting.TT.z=\"ISO 8601-forskyvning fra UTC i tidssone\"\nFilenameFormatting.TT.Z=\"Tidssonenavn eller -forkortelse\"\nFilenameFormatting.TT.FPS=\"Bilder per sekund (FPS)\"\nFilenameFormatting.TT.CRES=\"Grunnoppløsning (lerret)\"\nFilenameFormatting.TT.ORES=\"Utdataoppløsning (skalert)\"\nFilenameFormatting.TT.VF=\"Videoformat\"\nBasic.Settings.Video.Adapter=\"Videoadapter\"\nBasic.Settings.Video.BaseResolution=\"Grunnoppløsning (lerret)\"\nBasic.Settings.Video.ScaledResolution=\"Utgangsoppløsning (skalert)\"\nBasic.Settings.Video.DownscaleFilter=\"Nedskaleringsfilter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Oppløsningene er like, ingen nedskalering nødvendig]\"\nBasic.Settings.Video.FPSCommon=\"Vanlige FPS-verdier\"\nBasic.Settings.Video.FPSInteger=\"FPS-verdier (heltall)\"\nBasic.Settings.Video.FPSFraction=\"FPS-verdier (brøk)\"\nBasic.Settings.Video.Numerator=\"Teller\"\nBasic.Settings.Video.Denominator=\"Nevner\"\nBasic.Settings.Video.InvalidResolution=\"Ugyldig oppløsningsverdi. Må være [bredde]x[høyde] (f.eks. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Videoutdata er aktivt. Vennligst stans alle utganger for å endre bildeinnstillingene.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineær (raskest, men uskarp ved skalering)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bikubisk (skjerpet skalering, 16 prøver)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (skjerpet skalering, 36 prøver)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Område (Vektet sum, 4/6/9 målinger)\"\nBasic.Settings.Audio=\"Lyd\"\nBasic.Settings.Audio.SampleRate=\"Samplingsfrekvens\"\nBasic.Settings.Audio.Channels=\"Kanaler\"\nBasic.Settings.Audio.Meters=\"Metre\"\nBasic.Settings.Audio.MeterDecayRate=\"Avfallshastighet\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Raskt\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Middels (Type I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Tregt (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Toppunktmålertype\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Samplingstoppunkt\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Ekte toppunkt (Høyere CPU-bruk)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"Advarsel: Surroundlyd er aktivert.\"\nBasic.Settings.Audio.MultichannelWarning=\"Ved strømming, sjekk om strømmetjenesten både støtter surround-lydinntak og -avspilling. For eksempel har Facebook 360 Live full støtte for surround-lyd; YouTube Live støtter 5.1 surround inntak (og avspilling på TV-er).\\n\\nOBS lydfiltre er kompatible med surround-lyd, men VST-pluginstøtte kan ikke garanteres.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Vil du aktivere surround-lyd?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Er du sikker på at du vil aktivere surround-lyd?\"\nBasic.Settings.Audio.Devices=\"Globale lydenheter\"\nBasic.Settings.Audio.DesktopDevice=\"Skrivebordslyd\"\nBasic.Settings.Audio.DesktopDevice2=\"Skrivebordslyd 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/Aux\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/Aux\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/Aux\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/Aux\"\nBasic.Settings.Audio.EnablePushToMute=\"Aktiver demp-ved-trykk\"\nBasic.Settings.Audio.PushToMuteDelay=\"Demp-ved-trykk forsinkelse\"\nBasic.Settings.Audio.EnablePushToTalk=\"Aktiver snakk-ved-trykk\"\nBasic.Settings.Audio.PushToTalkDelay=\"Snakk-ved-trykk forsinkelse\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Enhet ikke tilkoblet eller ikke tilgjengelig]\"\nBasic.Settings.Audio.Disabled=\"Deaktivert\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ADVARSEL: Lavforsinkelsbufring for lyd er aktivert.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Lavforsinkelsesbufring for lyd kan forsårsake glitcher i lyden eller at enkelte kilder slutter å spille.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Aktiver lavforsinkelsesbufring for lyd?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Er du sikker på at du vil aktivere lavforsinkelsesbufring for lyd?\"\nBasic.Settings.Accessibility=\"Tilgjengelighet\"\nBasic.Settings.Accessibility.ColorOverrides=\"Bruk forskjellige farger\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mixervolumbånd (-60 til -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mixervolumbånd (-20 til -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mixervolumbånd (-9 til 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mixervolumbånd (-60 til -20dB) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mixervolumbånd (-20 til -9dB) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mixervolumbånd (-9 til 0dB) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Farge-forhåndsvalg\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Standard\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Tilpasset\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternativ for fargeblinde\"\nBasic.Settings.Advanced=\"Avansert\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prosessprioritet\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Høy\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Over Normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Under normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inaktiv\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Fargeformat\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Fargerom\"\nBasic.Settings.Advanced.Video.ColorRange=\"Fargespekter\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Begrenset\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR-hvitfargenivå\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Lydenhet for overvåking\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Standard\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Deaktiver Windows-lyddukking\"\nBasic.Settings.Advanced.StreamDelay=\"Direktesendingsforsinkelse\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Varighet\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Bevar avkuttingspunktet (øk forsinkelse) ved tilbakekobling\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Anslått minnebruk: %1 MB\"\nBasic.Settings.Advanced.Network=\"Nettverk\"\nBasic.Settings.Advanced.Network.BindToIP=\"Bind til IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP-familie\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Aktiver nettverksoptimaliseringer\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Aktiver TCP-pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Forsøk på å gjøre RTMP-utdata mildere med andre latensfølsomme applikasjoner i nettverket ved å regulere overføringshastigheten.\\nDet kan øke risikoen for tapte bilderuter på ustabile tilkoblinger.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Hotkey-fokusatferd\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Skru aldri av hurtigtaster\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Deaktiver hurtigtaster når hovedvinduet er i fokus\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Deaktiver hurtigtaster når hovedvinduet er i fokus\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(Gjør opptak som MKV)\"\nBasic.AdvAudio=\"Avanserte lydinnstillinger\"\nBasic.AdvAudio.ActiveOnly=\"Kun aktive kilder\"\nBasic.AdvAudio.Name=\"Navn\"\nBasic.AdvAudio.Volume=\"Volum\"\nBasic.AdvAudio.VolumeSource=\"Volum for '%1'\"\nBasic.AdvAudio.MonoSource=\"Mono nedmiksing for '%1'\"\nBasic.AdvAudio.Balance=\"Balanse\"\nBasic.AdvAudio.BalanceSource=\"Balanse for '%1'\"\nBasic.AdvAudio.SyncOffset=\"Synkroniser avvik\"\nBasic.AdvAudio.SyncOffsetSource=\"Synk forskyvning for '%1'\"\nBasic.AdvAudio.Monitoring=\"Hør på kilde\"\nBasic.AdvAudio.Monitoring.None=\"Ikke hør kilden\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Kun hør kilden (Demp utdataen)\"\nBasic.AdvAudio.Monitoring.Both=\"Hør og output kilde\"\nBasic.AdvAudio.MonitoringSource=\"Lydovervåking for '%1\"\nBasic.AdvAudio.AudioTracks=\"Spor\"\nBasic.Settings.Hotkeys=\"Hurtigtaster\"\nBasic.Settings.Hotkeys.Pair=\"Tastekombinasjoner delt med '%1' vil veksle\"\nBasic.Settings.Hotkeys.Filter=\"Filtrer\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrer etter hurtigtast\"\nBasic.Hotkeys.SelectScene=\"Bytt til scene\"\nBasic.SystemTray.Show=\"Vis\"\nBasic.SystemTray.Hide=\"Skjul\"\nBasic.SystemTray.Message.Reconnecting=\"Frakoblet. Kobler til på nytt...\"\nHotkeys.Left=\"Piltast venstre\"\nHotkeys.Right=\"Piltast høyre\"\nHotkeys.Up=\"Piltast opp\"\nHotkeys.Down=\"Piltast ned\"\nHotkeys.Super=\"Kommando (Super)\"\nHotkeys.Menu=\"Meny\"\nHotkeys.Space=\"Mellomrom\"\nHotkeys.NumpadMultiply=\"* (Numpad)\"\nHotkeys.NumpadDivide=\"/ (Numpad)\"\nHotkeys.NumpadAdd=\"+ (Numpad)\"\nHotkeys.NumpadSubtract=\"- (Numpad)\"\nHotkeys.NumpadDecimal=\", (Numpad)\"\nHotkeys.AppleKeypadNum=\"%1\"\nHotkeys.AppleKeypadMultiply=\"* (talltastatur)\"\nHotkeys.AppleKeypadDivide=\"/ (talltastatur)\"\nHotkeys.AppleKeypadAdd=\"+\"\nHotkeys.AppleKeypadSubtract=\"-\"\nHotkeys.AppleKeypadDecimal=\".\"\nHotkeys.AppleKeypadEqual=\"= (talltastatur)\"\nHotkeys.MouseButton=\"Museknapp %1\"\nMute=\"Demp\"\nUnmute=\"Udemp\"\nPush-to-mute=\"Hold-for-å-dempe\"\nPush-to-talk=\"Hold-for-å-snakke\"\nSceneItemShow=\"Vis '%1'\"\nSceneItemHide=\"Gjem '%1'\"\nOutputWarnings.NoTracksSelected=\"Du må velge minst ett spor\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Utdatainnstillingsfeil\"\nOutputWarnings.MP4Recording=\"Advarsel: Opptak lagret i MP4/MOV vil bli slettet dersom filen ikke kan fullføres (f.eks. som følge av BSOD, tap av strøm, osv.) Hvis du vil registere flere lydspor, vurder å bruke MKV og remuks inspillingen til MP4/MOV etter at det er ferdig (Fil → Remuks opptak)\"\nOutputWarnings.CannotPause=\"Advarsel: Innspillinger kan ikke pauses hvis opptakskoderen er satt til \\\"(Bruk strømkoder)\\\"\"\nCodecCompat.Incompatible=\"(Inkompatibel med %1)\"\nCodecCompat.CodecPlaceholder=\"Velg kodek…\"\nCodecCompat.ContainerPlaceholder=\"Velg format …\"\nCodecCompat.CodecMissingOnExit.Title=\"Ingen kodek er valgt\"\nCodecCompat.ContainerMissingOnExit.Title=\"Ingen format er valgt\"\nFinalScene.Title=\"Slett scene\"\nFinalScene.Text=\"Det må være minst én scene.\"\nNoSources.Title=\"Ingen kilder\"\nNoSources.Text=\"Det ser ut som du ikke har lagt til noen videokilder ennå, så du vil kun sende en blank skjerm. Er du sikker på at du vil gjøre dette?\"\nNoSources.Text.AddSource=\"Du kan legge til kilder ved å klikke på +-ikonet under Kilder-boksen i hovedvinduet til enhver tid.\"\nNoSources.Label=\"Du har ingen kilder. \\nKlikk + knappen nedenfor,\\neller høyre-klikk her for å legge til.\"\nChangeBG=\"Velg farge\"\nCustomColor=\"Egendefinert farge\"\nBrowserSource.EnableHardwareAcceleration=\"Aktiver «Nettleserkilde-maskinvareakselerasjon»\"\nAbout=\"Om\"\nAbout.Info=\"OBS Studio er en fri og åpen-kildet videoopptaks- og direktesendings-programvare.\"\nAbout.Donate=\"Bidra til projektet\"\nAbout.GetInvolved=\"Involver deg\"\nAbout.Authors=\"Skapere\"\nAbout.License=\"Lisens\"\nAbout.Contribute=\"Støtt OBS-prosjektet\"\nAddUrl.Title=\"Legg til kilde via URL\"\nAddUrl.Text=\"Du har dratt en URL til OBS. Dette vil automatisk legge til lenken som kilde. Fortsette?\"\nResizeOutputSizeOfSource=\"Endre utdatastørrelsen (kildestørrelse)\"\nResizeOutputSizeOfSource.Text=\"Grunn- og utgangsoppløsningen vil justeres slik at den tilsvarer den aktive kilden.\"\nResizeOutputSizeOfSource.Continue=\"Vil du fortsette?\"\nPreviewTransition=\"Forhåndsvis overgang\"\nImporter=\"Importverktøy for scenesamlinger\"\nImporter.SelectCollection=\"Velg en scenesamling\"\nImporter.Collection=\"Scenesamling\"\nImporter.HelpText=\"Legg til filer i dette vinduet for å importere samlinger fra OBS eller andre støttede programmer.\"\nImporter.Path=\"Lagringssted for samling\"\nImporter.Program=\"Oppdaget applikasjon\"\nImporter.AutomaticCollectionPrompt=\"Søk automatisk etter scenesamlinger\"\nImporter.AutomaticCollectionText=\"OBS kan automatisk finne importerbare scenekolleksjoner fra støttede tredjepartsprogrammer. Vil du at OBS automatisk skal finne samlinger for deg?\\n\\nDu kan endre dette senere i Innstillinger > Generelt > Importverktøy.\"\nRestart=\"Omstart\"\nNeedsRestart=\"OBS Studio må startes på nytt. Vil du starte på nytt nå?\"\nContextBar.NoSelectedSource=\"Ingen kilde er valgt\"\nContextBar.MediaControls.PlayMedia=\"Spill av media\"\nContextBar.MediaControls.PauseMedia=\"Pause media\"\nContextBar.MediaControls.StopMedia=\"Stop media\"\nContextBar.MediaControls.RestartMedia=\"Start media på nytt\"\nContextBar.MediaControls.PlaylistNext=\"Neste i Spilleliste\"\nContextBar.MediaControls.PlaylistPrevious=\"Forrige i spilleliste\"\nContextBar.MediaControls.BlindSeek=\"Widget for media søker\"\nYouTube.Auth.Ok=\"Autorisasjon fullført.\\nDu kan nå lukke denne siden.\"\nYouTube.Auth.NoCode=\"Autoriseringsprosessen ble ikke fullført.\"\nYouTube.Auth.NoChannels=\"Ingen kanal(er) tilgjengelig på valgt konto\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube brukerautorisasjon\"\nYouTube.Auth.WaitingAuth.Text=\"Vennligst fullfør autorisasjonen i din eksterne nettleser.<br>Hvis den eksterne nettleseren ikke åpnes, følg denne lenken og fullfør autorisasjonen:<br>%1\"\nYouTube.AuthError.Text=\"Fikk ikke tak i kanalinformasjonen: %1.\"\nYouTube.Actions.WindowTitle=\"YouTube-sendingsoppsett - Kanal: %1\"\nYouTube.Actions.CreateNewEvent=\"Opprett ny sending\"\nYouTube.Actions.ChooseEvent=\"Velg eksisterende sending\"\nYouTube.Actions.Title=\"Tittel*\"\nYouTube.Actions.MyBroadcast=\"Min Strøm\"\nYouTube.Actions.Description=\"Beskrivelse\"\nYouTube.Actions.Privacy=\"Personvern*\"\nYouTube.Actions.Privacy.Private=\"Privat\"\nYouTube.Actions.Privacy.Public=\"Offentlig\"\nYouTube.Actions.Privacy.Unlisted=\"Ikke listet\"\nYouTube.Actions.Category=\"Kategori\"\nYouTube.Actions.Thumbnail=\"Miniatyrbilde\"\nYouTube.Actions.Thumbnail.SelectFile=\"Velg fil …\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Ingen fil valgt\"\nYouTube.Actions.Thumbnail.ClearFile=\"Tøm\"\nYouTube.Actions.MadeForKids=\"Er denne videoen laget for barn?*\"\nYouTube.Actions.MadeForKids.Yes=\"Ja, den er laget for barn\"\nYouTube.Actions.MadeForKids.No=\"Nei, den er ikke laget for barn\"\nYouTube.Actions.AdditionalSettings=\"Flere innstillinger\"\nYouTube.Actions.Latency=\"Forsinkelse\"\nYouTube.Actions.Latency.Low=\"Lav\"\nYouTube.Actions.Latency.UltraLow=\"Ultra-lav\"\nYouTube.Actions.EnableAutoStart=\"Aktiver automatisk start\"\nYouTube.Actions.EnableAutoStop=\"Aktiver auto-stopp\"\nYouTube.Actions.EnableDVR=\"Aktiver DVR\"\nYouTube.Actions.ScheduleForLater=\"Planlegg for senere\"\nYouTube.Actions.RememberSettings=\"Husk disse innstillingene\"\nYouTube.Actions.Create_Ready=\"Opprett sending\"\nYouTube.Actions.Create_GoLive=\"Opprett sending og begynn å strømme\"\nYouTube.Actions.Choose_Ready=\"Velg sending\"\nYouTube.Actions.Choose_GoLive=\"Velg sending og begynn å strømme\"\nYouTube.Actions.Create_Schedule=\"Planlegg sending\"\nYouTube.Actions.Create_Schedule_Ready=\"Planlegg og velg sending\"\nYouTube.Actions.Dashboard=\"Åpne YouTube Studio\"\nYouTube.Actions.Error.Title=\"Feil ved opprettelse av strøm\"\nYouTube.Actions.Error.Text=\"YouTube tilgangsfeil '%1'.<br/>En detaljert feilbeskrivelse kan finnes på <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Youtube tilgangsfeil. Vennligst sjekk nettverks tilkoblingen og youtube tilgangen din.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Feil ved oppretting av strømm '%1'.<br/>En detaljert feilbeskrivelse kan finnes på <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Ingen strøm opprettet. Vennligst koblet til kontoen din på nytt.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API feil. Se loggfilen for mer informasjon.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Den valgte strømmen finnes ikke.\"\nYouTube.Actions.Error.FileMissing=\"Den valgte filen finnes ikke.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Mislyktes i å åpne den valgte filen.\"\nYouTube.Actions.Error.FileTooLarge=\"Den valgte filen er for stor (grense: 2 MiB).\"\nYouTube.Actions.EventsLoading=\"Laster liste over Eventer...\"\nYouTube.Actions.EventCreated.Title=\"Event opprettet\"\nYouTube.Actions.EventCreated.Text=\"Event ble opprettet.\"\nYouTube.Actions.Stream=\"Strøm\"\nYouTube.Actions.Stream.ScheduledFor=\"Planlagt for %1\"\nYouTube.Actions.Stream.Resume=\"Gjenoppta avbrutt strøm\"\nYouTube.Actions.Stream.YTStudio=\"Automatisk opprettet av YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Oppretter en ny Live-sending, vennligst vent...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Manuell start kreves\"\nYouTube.Actions.AutoStopStreamingWarning=\"Du vil ikke kunne koblet til på nytt. <br>Strømmen din vil stoppe og ikke lengre være live.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Live streaming er ikke aktivert på den valgte YouTube kanalen.<br/><br/>Se <a href='https://www.youtube.com/features'>youtube.com/features</a> for mer informasjon.\"\nYouTube.Errors.livePermissionBlocked=\"Live streaming er ikke tilgjengelig på den valgte YouTube Channel.<br/>Vær oppmerksom på at det kan ta opptil 24 timer før direktesendinger blir tilgjengelig etter at du har aktivert det i dine kanalinnstillinger.<br/><br/>Se <a href='https://www.youtube.com/features'>youtube.com/features</a> for detaljer.\"\nYouTube.DocksRemoval.Title=\"Fjern foreldede YouTube-nettleserdokker\"\nYouTube.DocksRemoval.Text=\"Disse nettlseserdokkene vil fjernes fortløpende etterhvert som de foreldes:\\n\\n%1\\nBruk «Dokker/YouTube Live Kontrollpanel» istedenfor.\"\nConfigDownload.WarningMessageTitle=\"Advarsel\"\nFailedToStartStream.InvalidCustomConfig=\"Ugyldig tilpasset konfigurasjon\"\nFailedToStartStream.WarningUnknownStatus=\"Mottok ukjent statusverdi «%1»\"\n"
  },
  {
    "path": "frontend/data/locale/nl-NL.ini",
    "content": "Language=\"Nederlands\"\nApply=\"Toepassen\"\nCancel=\"Annuleren\"\nClose=\"Sluiten\"\nSave=\"Opslaan\"\nDiscard=\"Negeren\"\nDisable=\"Uitschakelen\"\nYes=\"Ja\"\nNo=\"Nee\"\nAdd=\"Toevoegen\"\nRemove=\"Verwijderen\"\nRename=\"Hernoemen\"\nInteract=\"Interactie\"\nProperties=\"Eigenschappen\"\nMoveUp=\"Omhoog verplaatsen\"\nMoveDown=\"Omlaag verplaatsen\"\nSettings=\"Instellingen\"\nDisplay=\"Beeldscherm\"\nName=\"Naam\"\nExit=\"Afsluiten\"\nMixer=\"Audiomixer\"\nBrowse=\"Bladeren\"\nDroppedFrames=\"Gedropte Frames %1 (%2%)\"\nProjector.Open.Program=\"Open programma projector\"\nProjector.Open.Preview=\"Open preview projector\"\nProjector.Open.Scene=\"Open scene projector\"\nProjector.Open.Source=\"Open source projector\"\nProjector.Open.Multiview=\"Open multiview\"\nProjector.Display=\"Beeldscherm: %1\"\nProjector.Window=\"Nieuw venster\"\nProjector.Title.Scene=\"Scène: %1\"\nProjector.Title.Source=\"Bron: %1\"\nProjector.ResizeWindowToContent=\"Venster aanpassen aan de inhoud\"\nClear=\"Wissen\"\nRevert=\"Terugzetten\"\nShow=\"Weergeven\"\nHide=\"Verbergen\"\nUnhideAll=\"Allemaal zichtbaar maken\"\nUntitled=\"Naamloos\"\nNew=\"Nieuw\"\nDuplicate=\"Dupliceren\"\nEnable=\"Inschakelen\"\nDisableOSXVSync=\"macOS V-Sync uitschakelen\"\nResetOSXVSyncOnExit=\"macOS V-Sync resetten bij afsluiten\"\nHighResourceUsage=\"Encoden overbelast! Overweeg de video-instellingen te verlagen of een snellere encoder preset te gebruiken.\"\nTransition=\"Overgang\"\nQuickTransitions=\"Snelle overgangen\"\nFadeToBlack=\"Naar zwart vervagen\"\nLeft=\"Links\"\nRight=\"Rechts\"\nTop=\"Boven\"\nBottom=\"Onder\"\nReset=\"Herstellen\"\nHours=\"Uren\"\nMinutes=\"Minuten\"\nSeconds=\"Seconden\"\nDeprecated=\"Verouderd\"\nImport=\"Importeer\"\nExport=\"Exporteer\"\nCopy=\"Kopiëren\"\nPaste=\"Plakken\"\nPasteReference=\"Plakken (referentie)\"\nPasteDuplicate=\"Plakken (dupliceren)\"\nRemuxRecordings=\"Remux opnames\"\nNext=\"Volgende\"\nBack=\"Terug\"\nDefaults=\"Standaardwaarden\"\nRestoreDefaults=\"Standaardwaarden\"\nHideMixer=\"Verbergen in mixer\"\nTransitionOverride=\"Overgang overschrijven\"\nShowTransition=\"Weergeven Transitie\"\nHideTransition=\"Verbergen Transitie\"\nNone=\"Geen\"\nStudioMode.Preview=\"Voorvertoning\"\nStudioMode.Program=\"Programma\"\nStudioMode.PreviewSceneName=\"Voorbeeld: %1\"\nStudioMode.ProgramSceneName=\"Programma: %1\"\nShowInMultiview=\"Weergeven in Multiview\"\nVerticalLayout=\"Verticale layout\"\nGroup=\"Groep\"\nDoNotShowAgain=\"Niet opnieuw tonen\"\nDefault=\"(Standaard)\"\nCalculating=\"Berekenen...\"\nFullscreen=\"Volledig scherm\"\nWindowed=\"Venster\"\nRefreshBrowser=\"Verversen\"\nAspectRatio=\"Beeldverhouding <b>%1:%2</b>\"\nLockVolume=\"Volume vergrendelen\"\nLogViewer=\"Log Weergave\"\nShowOnStartup=\"Tonen tijdens het opstarten\"\nOpenFile=\"Bestand openen\"\nAddScene=\"Voeg scène toe\"\nAddSource=\"Bron toevoegen\"\nRemoveScene=\"Geselecteerde scène verwijderen\"\nRemoveSource=\"Geselecteerde scène(s) verwijderen\"\nMoveSceneUp=\"Scène omhoog verplaatsen\"\nMoveSceneDown=\"Scène omlaag verplaatsen\"\nMoveSourceUp=\"Verplaats bron(nen) naar boven\"\nMoveSourceDown=\"Verplaats bron(nen) naar beneden\"\nSourceProperties=\"Open bron eigenschappen\"\nSourceFilters=\"Open bron filters\"\nMixerToolbarMenu=\"Audio mixer menu\"\nList=\"Lijst\"\nGrid=\"Raster\"\nAutomatic=\"Automatisch\"\nComingSoon=\"Binnenkort beschikbaar\"\nPluginsFailedToLoad.Title=\"Plugin laad fout\"\nPluginsFailedToLoad.Text=\"De volgende OBS plugins konden niet laden:\\n\\n%1\\nGelieve deze plugins bij te werken of te verwijderen.\"\nAlreadyRunning.Title=\"OBS is al actief\"\nAlreadyRunning.Text=\"OBS is al actief! Tenzij je dit wilde doen, sluit a.u.b. alle reeds draaiende instanties van OBS voor je een nieuwe instantie opstart. Als je OBS hebt ingesteld om naar het systeemvak te minimaliseren, controleer dan of hij daar nog uitgevoerd wordt.\"\nAlreadyRunning.LaunchAnyway=\"Toch starten\"\nCrashHandling.Dialog.Title=\"OBS Studio crash gedetecteerd\"\nCrashHandling.Labels.Text=\"OBS Studio is niet correct afgesloten.\\n\\nUitvoeren in de veilige modus (plug-ins van derden, scripting en WebSockets uitgeschakeld)?\"\nCrashHandling.Labels.PrivacyNotice=\"U kunt er ook voor kiezen om automatisch het meest recente crashrapport te uploaden naar het OBSProject.<br /><br />Lees het <a href='https://obsproject.com/privacy-policy'>Privacybeleid</a> voordat u bestanden uploadt en besteed speciale aandacht aan de gedeelten over het uploaden van bestanden.\"\nCrashHandling.Checkbox.SendReport=\"Ik heb het privacybeleid gelezen en ga akkoord met het uploaden.\"\nCrashHandling.Buttons.LaunchSafe=\"Starten in veilige modus\"\nCrashHandling.Buttons.LaunchNormal=\"Starten in normale modus\"\nCrashHandling.Errors.UploadJSONError=\"Er is een fout opgetreden bij het uploaden van het meest recente crashlog. Probeer het later opnieuw.\"\nCrashHandling.Errors.Title=\"Crash log upload fout\"\nSafeMode.Restart=\"Wil je OBS herstarten in veilige modus (plugins van derden, scripting en WebSockets uitgeschakeld)?\"\nSafeMode.RestartNormal=\"Wil je OBS herstarten in normale modus?\"\nChromeOS.Title=\"Niet-ondersteund platform\"\nChromeOS.Text=\"OBS lijkt te worden uitgevoerd in een ChromeOS container. Dit platform wordt niet ondersteund.\"\nWine.Title=\"Wine gedetecteerd\"\nWine.Text=\"OBS draaien in Wine wordt niet ondersteund en veel functies zoals opname- of apparaatbronnen zullen niet werken of slechts in beperkte capaciteit.<br><br>Het wordt aangeraden om een native versie van OBS uit te voeren zoals bijvoorbeeld <a href='https://flathub.org/apps/details/com.obsproject.Studio'>onze Flatpak versie</a> of pakketten van je besturingssysteem.\"\nDockCloseWarning.Title=\"Dockbaar venster sluiten\"\nDockCloseWarning.Text=\"Je hebt net een dokbaar venster gesloten. Als je het opnieuw wilt zien, gebruik dan het menu Docks in de menubalk.\"\nExtraBrowsers=\"Aangepaste Browser Docks\"\nExtraBrowsers.Info=\"Voeg docks toe door ze een naam en URL te geven, klik vervolgens op Toepassen of Sluiten om de docks te openen. U kunt op elk moment docks toevoegen of verwijderen.\"\nExtraBrowsers.DockName=\"Dock Naam\"\nAuth.Authing.Title=\"Authenticeren...\"\nAuth.Authing.Text=\"Authenticeren bij %1, even geduld...\"\nAuth.AuthFailure.Title=\"Authenticatie fout\"\nAuth.AuthFailure.Text=\"Authenticeren met %1 is mislukt:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Authenticatie vereist\"\nAuth.InvalidScope.Text=\"De authenticatie vereisten voor %1 zijn gewijzigd. Sommige functies zijn mogelijk niet beschikbaar.\"\nAuth.LoadingChannel.Title=\"Kanaalinformatie laden...\"\nAuth.LoadingChannel.Text=\"Kanaalinformatie voor %1 laden, even geduld a.u.b...\"\nAuth.LoadingChannel.Error=\"Kon geen kanaalinformatie krijgen.\"\nAuth.ChannelFailure.Title=\"Laden van kanaal mislukt\"\nAuth.ChannelFailure.Text=\"Laden van kanaalinformatie mislukt voor %1\\n\\n%2: %3\"\nAuth.StreamInfo=\"Stream-informatie\"\nTwitchAuth.Stats=\"Twitch statistieken\"\nTwitchAuth.Feed=\"Twitch activiteitenfeed\"\nTwitchAuth.TwoFactorFail.Title=\"Stream key opvragen mislukt\"\nTwitchAuth.TwoFactorFail.Text=\"OBS kon geen verbinding maken met het Twitch-account; zorg ervoor dat er tweestappen-authenticatie ingesteld is in de <a href='https://www.twitch.tv/settings/security'>Twitch-beveiligingsinstellingen</a>, omdat dit vereist is om te kunnen streamen.\"\nRestreamAuth.Channels=\"Kanalen opnieuw streamen\"\nCopy.Filters=\"Kopieer Filters\"\nPaste.Filters=\"Plak filters\"\nBrowserPanelInit.Title=\"De browser wordt gestart...\"\nBrowserPanelInit.Text=\"De browser wordt gestart, even geduld...\"\nBandwidthTest.Region=\"Gebied\"\nBandwidthTest.Region.US=\"Verenigde Staten\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Azië\"\nBandwidthTest.Region.Other=\"Overig\"\nBasic.AutoConfig=\"Automatische configuratie-wizard\"\nBasic.AutoConfig.ApplySettings=\"Instellingen toepassen\"\nBasic.AutoConfig.StartPage=\"Gebruiksinformatie\"\nBasic.AutoConfig.StartPage.SubTitle=\"Geef aan waarvoor je het programma wilt gebruiken\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimaliseren voor streamen, opnemen is minder belangrijk\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimaliseren voor opnemen, ik ga niet streamen\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Ik ga alleen de virtuele camera gebruiken\"\nBasic.AutoConfig.VideoPage=\"Video instellingen\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Specifieer welke video-instellingen je wilt gebruiken\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Gebruik huidige (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Beeldscherm %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Gebruik huidige (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 of 30, maar geef de voorkeur aan 60 wanneer mogelijk\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 of 30, maar geef de voorkeur aan een hoge resolutie\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Let op: de basisresolutie (canvas) hoeft niet gelijk te zijn als de resolutie waarmee je streamt of opneemt. Je stream/opnameresolutie kan omlaag bijgeschaald worden vanaf de basisresolutie om de belasting op de computer of de benodigde bitrate te verlagen.\"\nBasic.AutoConfig.StreamPage=\"Stream-informatie\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Voer alstublieft uw stream-informatie in\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Maak verbinding met het account (aanbevolen)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Account loskoppelen\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Account loskoppelen?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Deze wijziging zal onmiddellijk van toepassing zijn. Weet u zeker dat u uw account wilt loskoppelen?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Haal een streamsleutel op\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Meer informatie\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Gebruik Stream Key\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Gebruik streamsleutel (geavanceerd)\"\nBasic.AutoConfig.StreamPage.Service=\"Dienst\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Toon alles...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Aangepast...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Stream key\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: voer het wachtwoord voor het coderen in.\\nRTMP: voer de door de dienst opgegeven sleutel in.\\nSRT: voer de streamid in als de service er een gebruikt.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Encodersleutel\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Verbonden account\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Bandbreedte inschatten met bandbreed test (kan enkele minuten duren)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Geef de voorkeur aan hardware-encoding\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Hardware-encoding elimineert het meeste CPU-gebruik, maar heeft wellicht een hogere bitrate nodig om hetzelfde kwaliteitsniveau te bereiken.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Streamwaarschuwing\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"De bandbreedte-test gaat willekeurige videogegevens zonder audio naar uw kanaal streamen. Indien mogelijk is het aanbevolen om tijdelijk het opslaan van video's van streams uit te schakelen en de stream privé te maken tot de test is voltooid. Doorgaan?\"\nBasic.AutoConfig.TestPage=\"Eindresultaat\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Het programma voert nu een reeks testen uit om de meest ideale instellingen in te schatten\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testen is afgerond\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Bandbreedte-test aan het uitvoeren, dit kan enkele minuten duren...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Geen output voor de protocol van deze service gevonden\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Verbinden met: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Kon met geen enkele server verbinden, controleer je internetverbinding en probeer het opnieuw.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Bandbreedte testen voor: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Stream encoder testen, dit kan even duren...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Opname encoder testen, dit kan even duren...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Kon de encoder niet starten\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"%1x%2 %3 FPS testen...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Streamingencoder\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Opname-encoder\"\nBasic.AutoConfig.TestPage.Result.Header=\"Het programma heeft vastgesteld dat deze geschatte instellingen het meest ideaal zijn voor jou:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Om deze instellingen te gebruiken, klik op Instellingen Toepassen. Om de configuratieassistent aan te passen, klik op Terug. Om de instellingen handmatig te bepalen, klik op Annuleren en open de instellingen.\"\nBasic.AutoConfig.Info=\"De automatische configuratiewizard bepaalt de beste instellingen op basis van de computerspecificaties en de snelheid van de internetverbinding.\"\nBasic.AutoConfig.RunAnytime=\"Dit kan op elk moment uitgevoerd worden door naar het Tools-menu te gaan.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Streaming (geschaalde) resolutie\"\nBasic.Stats=\"Statistieken\"\nBasic.Stats.CPUUsage=\"Processorgebruik\"\nBasic.Stats.HDDSpaceAvailable=\"Harde-schijfruimte beschikbaar\"\nBasic.Stats.MemoryUsage=\"Geheugengebruik\"\nBasic.Stats.AverageTimeToRender=\"Gemiddelde tijd om een frame te genereren\"\nBasic.Stats.SkippedFrames=\"Overgeslagen frames als gevolg van encodervertraging\"\nBasic.Stats.MissedFrames=\"Frames gemist als gevolg van rendervertraging\"\nBasic.Stats.Output.Recording=\"Lokale opname\"\nBasic.Stats.Status.Recording=\"Opname actief\"\nBasic.Stats.Status.Reconnecting=\"Opnieuw verbinden\"\nBasic.Stats.Status.Inactive=\"Inactief\"\nBasic.Stats.Status.Active=\"Actief\"\nBasic.Stats.DroppedFrames=\"Gedropte Frames (Netwerk)\"\nBasic.Stats.MegabytesSent=\"Totale Gegevensuitvoer\"\nBasic.Stats.DiskFullIn=\"Schijf vol na (ca.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 uren, %2 minuten\"\nBasic.Stats.ResetStats=\"Statistieken resetten\"\nResetUIWarning.Title=\"Weet u zeker dat u de UI wilt herstellen?\"\nResetUIWarning.Text=\"Herstellen van de UI zal extra docks verbergen. Als u deze docks toch wilt zien, dan moet u ze in via het Docks menu weer zichtbaar maken.\\n\\nWeet u zeker dat u de UI wilt herstellen?\"\nUpdater.Title=\"Update beschikbaar\"\nUpdater.Text=\"Er is een update beschikbaar:\"\nUpdater.UpdateNow=\"Nu updaten\"\nUpdater.RemindMeLater=\"Herinner mij later\"\nUpdater.Skip=\"Sla deze versie over\"\nUpdater.NoUpdatesAvailable.Title=\"Geen updates beschikbaar\"\nUpdater.NoUpdatesAvailable.Text=\"Er zijn momenteel geen updates beschikbaar\"\nUpdater.BranchNotFound.Title=\"Update kanaal verwijderd\"\nUpdater.BranchNotFound.Text=\"Het geselecteerde update kanaal is niet langer beschikbaar, OBS is teruggezet naar het standaard update kanaal.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Integriteit controle niet beschikbaar\"\nUpdater.RepairButUpdatesAvailable.Text=\"Het controleren van de integriteit van het bestand is alleen mogelijk voor de nieuwste versie die beschikbaar is. Gebruik Help → Controleren op Updates om uw OBS-installatie te controleren en bij te werken.\"\nUpdater.RepairConfirm.Title=\"Bevestig controleren van Integriteit\"\nUpdater.RepairConfirm.Text=\"Bij het starten van de integriteitscontrole worden uw OBS-installatie gescand voor corruptie en opnieuw downloaden van beschadigde / gewijzigde bestanden. Dit kan even duren.\\n\\nWilt u doorgaan?\"\nUpdater.FailedToLaunch=\"Starten van updater is mislukt\"\nQuickTransitions.SwapScenes=\"Verwissel Voorbeeld-/Programmascènes na de overgang\"\nQuickTransitions.SwapScenesTT=\"Verwisselt de voorbeeld- en programmascènes na de overgang (als de oorspronkelijke programmascene nog bestaat).\\nDit zal geen veranderingen ongedaan maken die zijn aangebracht aan de oorspronkelijke programmascène.\"\nQuickTransitions.DuplicateScene=\"Scène dupliceren\"\nQuickTransitions.DuplicateSceneTT=\"Bij het bewerken van dezelfde scène, kunt u transformatie/zichtbaarheid van bronnen bewerken zonder de programmauitvoer aan te passen.\\nOm eigenschappen van bronnen aan te passen zonder de uitvoer van het programma aan te passen, schakel 'Bronnen dupliceren' in.\\nHet veranderen van deze waarde zal de huidige programmacène resetten (als deze nog bestaat).\"\nQuickTransitions.EditProperties=\"Bronnen dupliceren\"\nQuickTransitions.EditPropertiesTT=\"Bij het bewerken van dezelfde scène, staat het toe om de eigenschappen van bronnen aan te passen, zonder het programma uitvoer aan te passen.\\nDit kan alleen worden gebruikt als 'Scène Dupliceren' is ingeschakeld.\\nBepaalde bronnen (zoals opname- of mediabronnen) ondersteunen dit niet en kunnen niet afzonderlijk worden bewerkt.\\nHet veranderen van deze waarde zal de huidige programmacène resetten (als deze nog bestaat).\\n\\nWaarschuwing: Omdat bronnen worden gedupliceerd, kan dit extra systeem of videobronnen vereisen.\"\nQuickTransitions.HotkeyName=\"Snelle overgang: %1\"\nBasic.AddTransition=\"Instelbare overgang toevoegen\"\nBasic.RemoveTransition=\"Instelbare overgang verwijderen\"\nBasic.TransitionProperties=\"Overgangseigenschappen\"\nBasic.SceneTransitions=\"Scène-overgangen\"\nBasic.TransitionDuration=\"Duur\"\nBasic.TogglePreviewProgramMode=\"Studiomodus\"\nBasic.EnablePreviewProgramMode=\"Studio modus aanzetten\"\nBasic.DisablePreviewProgramMode=\"Studio modus uitzetten\"\nUndo.Undo=\"Ongedaan maken\"\nUndo.Redo=\"Opnieuw uitvoeren\"\nUndo.Add=\"Voeg '%1' toe\"\nUndo.Delete=\"Verwijder '%1'\"\nUndo.Rename=\"Hernoem '%1'\"\nUndo.SceneCollection.Switch=\"Wisselen naar '%1'\"\nUndo.Item.Undo=\"%1 ongedaan maken\"\nUndo.Item.Redo=\"%1 opnieuw uitvoeren\"\nUndo.Sources.Multi=\"Verwijder %1 bronnen\"\nUndo.Filters=\"Filter wijzigingen op '%1'\"\nUndo.Filters.Paste.Single=\"Plak filter '%1' in '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopiëren filters van '%1' naar '%2'\"\nUndo.Transform=\"Transformeer bron(nen) in '%1'\"\nUndo.Transform.Paste=\"Plak Transformatie in '%1'\"\nUndo.Transform.Rotate=\"Rotatie in '%1'\"\nUndo.Transform.Reset=\"Transformatie reset in '%1'\"\nUndo.Transform.HFlip=\"Horizontale spiegelen in '%1'\"\nUndo.Transform.VFlip=\"Verticale spiegeling in '%1'\"\nUndo.Transform.FitToScreen=\"Aanpassen aan scherm in '%1'\"\nUndo.Transform.StretchToScreen=\"Uitrekken naar scherm in '%1'\"\nUndo.Transform.Center=\"Centreer naar scherm in '%1'\"\nUndo.Transform.VCenter=\"Verticaal centreren naar scherm in '%1'\"\nUndo.Transform.HCenter=\"Horizontaal centreren naar scherm in '%1'\"\nUndo.Volume.Change=\"Volume wijziging in '%1'\"\nUndo.Volume.Mute=\"'%1' dempen\"\nUndo.Volume.Unmute=\"'%1' dempen opheffen\"\nUndo.Balance.Change=\"Audiobalans wijzigen op '%1'\"\nUndo.SyncOffset.Change=\"Audio Sync Offset Wijziging op '%1'\"\nUndo.MonitoringType.Change=\"Audiomonitoring wijziging op '%1'\"\nUndo.Mixers.Change=\"Audiomixers wijziging op '%1'\"\nUndo.ForceMono.On=\"Mono geforceerd op '%1'\"\nUndo.ForceMono.Off=\"Mono niet meer geforceerd op '%1'\"\nUndo.Properties=\"Eigenschap verandering op '%1'\"\nUndo.Scene.Duplicate=\"Scene dupliceren '%1'\"\nUndo.ShowTransition=\"Weergeven transitie van '%1'\"\nUndo.HideTransition=\"Verbergen transitie van '%1'\"\nUndo.ShowSceneItem=\"Geef '%1' weer in '%2'\"\nUndo.HideSceneItem=\"Verberg '%1' in '%2'\"\nUndo.ReorderSources=\"Bronnen herschikken in '%1'\"\nUndo.MoveUp=\"Verplaats '%1' omhoog in '%2'\"\nUndo.MoveDown=\"Verplaats '%1' omlaag in '%2'\"\nUndo.MoveToTop=\"Verplaats '%1' naar bovenaan in '%2'\"\nUndo.MoveToBottom=\"Verplaats '%1' naar onderaan in '%2'\"\nUndo.PasteSource=\"Plak bron(nen) in '%1'\"\nUndo.PasteSourceRef=\"Plak Bronreferentie(s) in '%1'\"\nUndo.GroupItems=\"Groepeer items in '%1'\"\nTransitionNameDlg.Text=\"Voer de naam van de transitie in\"\nTransitionNameDlg.Title=\"Naam van de overgang\"\nTitleBar.SafeMode=\"VEILIGE MODUS\"\nTitleBar.PortableMode=\"Draagbaar modus\"\nTitleBar.Profile=\"Profiel\"\nTitleBar.Scenes=\"Scènes\"\nNameExists.Title=\"Naam bestaat al\"\nNameExists.Text=\"De naam is al in gebruik.\"\nNoNameEntered.Title=\"Voer een geldige naam in\"\nNoNameEntered.Text=\"Je kunt geen lege namen gebruiken.\"\nConfirmStart.Title=\"De stream starten?\"\nConfirmStart.Text=\"Weet je zeker dat je de stream wilt starten?\"\nConfirmStop.Title=\"De stream stoppen?\"\nConfirmStop.Text=\"Weet je zeker dat je de stream wilt stoppen?\"\nConfirmStopRecord.Title=\"Opnemen stoppen?\"\nConfirmStopRecord.Text=\"Weet je zeker dat je het opnemen wilt stoppen?\"\nConfirmBWTest.Title=\"Bandbreedte-test starten?\"\nConfirmBWTest.Text=\"Je hebt OBS geconfigureerd in bandbreedte-testmodus; deze modus maakt het mogelijk het netwerk te testen zonder live te gaan. Zodra je klaar bent met testen, moet je hem uitschakelen om de kijkers in staat te stellen de stream te bekijken.\\n\\nWil je doorgaan?\"\nConfirmExit.Title=\"Actieve outputs\"\nConfirmExit.Text=\"OBS is nog steeds actief. Alle streams/opnames worden stopgezet.\"\nConfirmRemove.Title=\"Verwijderen bevestigen\"\nConfirmRemove.Text=\"Weet je zeker dat je '%1' wil verwijderen?\"\nConfirmRemove.TextMultiple=\"Weet je zeker dat je %1 elementen wil verwijderen?\"\nConfirmReset.Title=\"Reset eigenschappen\"\nConfirmReset.Text=\"Weet u zeker dat u de huidige eigenschappen naar de standaardwaarden wilt herstellen?\"\nOutput.StartStreamFailed=\"Het starten van de stream is mislukt\"\nOutput.StartRecordingFailed=\"Het starten van de opname is mislukt\"\nOutput.StartReplayFailed=\"Het starten van de replay buffer is mislukt\"\nOutput.StartVirtualCamFailed=\"Starten virtuele camera mislukt\"\nOutput.StartFailedGeneric=\"Het starten van de uitvoer is mislukt. Controleer de logbestanden voor meer informatie.\\n\\nLet op: Als je gebruik maakt van de NVENC of AMD encoders, controleer of de drivers up to date zijn.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Herhalingen kunnen niet worden opgeslagen tijdens het pauzeren\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Waarschuwing: herhalingen kunnen niet worden opgeslagen als opnemen gepauzeerd is.\"\nOutput.ConnectFail.Title=\"Kan geen verbinding maken\"\nOutput.ConnectFail.BadPath=\"Ongeldig pad of verbindings-url. Controleer a.u.b. of je instellingen geldig zijn.\"\nOutput.ConnectFail.ConnectFailed=\"Kan geen verbinding maken met de server\"\nOutput.ConnectFail.InvalidStream=\"Kon het gespecificeerde kanaal of streamkey niet openen. Mogelijk is het kanaal of de key ongeldig of denkt de server dat je nog steeds aangemeld bent.\"\nOutput.ConnectFail.HdrDisabled=\"HDR output is op dit moment uitgeschakeld voor deze output.\"\nOutput.ConnectFail.Error=\"Er is een onverwachte fout opgetreden bij het verbinden met de server. Meer informatie in het logbestand.\"\nOutput.ConnectFail.Disconnected=\"Verbinding met de server is gesloten.\"\nOutput.StreamEncodeError.Title=\"Coderingsfout\"\nOutput.StreamEncodeError.Msg=\"Er is een coderingsfout opgetreden tijdens het streamen.\"\nOutput.StreamEncodeError.Msg.LastError=\"Er is een coderingsfout opgetreden tijdens het streamen:<br><br>%1\"\nOutput.RecordFail.Title=\"Kon de opname niet starten\"\nOutput.RecordFail.Unsupported=\"Het outputformaat wordt niet ondersteund, of ondersteunt niet meer dan een audiotrack. Controleer je instellingen en probeer het opnieuw.\"\nOutput.RecordNoSpace.Title=\"Onvoldoende schijfruimte\"\nOutput.RecordNoSpace.Msg=\"Er is onvoldoende schijfruimte om door te gaan met opnemen.\"\nOutput.RecordError.Title=\"Opnamefout\"\nOutput.RecordError.Msg=\"Er is een onbekende fout opgetreden tijdens het opnemen.\"\nOutput.RecordError.EncodeErrorMsg=\"Er is een coderingsfout opgetreden tijdens het opnemen.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Er is een coderingsfout opgetreden tijdens het opnemen:<br><br>%1\"\nOutput.BadPath.Title=\"Ongeldig bestandspad\"\nOutput.BadPath.Text=\"Het geconfigureerde opnamepad kon niet worden geopend. Controleer uw opnamepad onder Instellingen → Uitvoer → Opname.\"\nOutput.NoBroadcast.Title=\"Geen uitzending geconfigureerd\"\nOutput.NoBroadcast.Text=\"Je moet een uitzending instellen voordat je kunt streamen.\"\nOutput.BroadcastStartFailed=\"Uitzending starten mislukt\"\nOutput.BroadcastStopFailed=\"Uitzending stoppen mislukt\"\nLogUploadDialog.Title=\"OBS Studio Log Bestand Upload\"\nLogUploadDialog.Labels.PrivacyNotice=\"Lees het <a href='https://obsproject.com/privacy-policy'>Privacybeleid</a> en het gedeelte over het uploaden van bestanden voordat u bestanden uploadt.\"\nLogUploadDialog.Labels.Progress=\"Het uploaden van het logbestand is bezig. Even geduld...\"\nLogUploadDialog.Labels.Description.AppLog=\"Uw logbestand is geüpload. U kunt nu de URL delen voor debugging of ondersteuningsdoeleinden.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Uw crashrapport is geüpload. U kunt nu de URL delen voor debugging doeleinden.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Uploaden\"\nLogUploadDialog.Buttons.CopyURL=\"Kopieer de log-URL\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analyseer logbestand\"\nLogUploadDialog.Buttons.RetryButton=\"Opnieuw proberen\"\nLogUploadDialog.Errors.Template=\"Er is een fout opgetreden bij het uploaden van het bestand:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Er is geen bestand gevonden om te uploaden of het bestand is leeg.\"\nRemux.SourceFile=\"OBS Opname\"\nRemux.TargetFile=\"Doelbestand\"\nRemux.Remux=\"Remuxen\"\nRemux.Stop=\"Stop met remuxen\"\nRemux.ClearFinished=\"Verwijder voltooide items\"\nRemux.ClearAll=\"Verwijder alle items\"\nRemux.OBSRecording=\"OBS-opnemen\"\nRemux.FinishedTitle=\"Remuxen voltooid\"\nRemux.Finished=\"Opname geremuxt\"\nRemux.FinishedError=\"Opname geremuxt, maar het bestand zou incompleet kunnen zijn\"\nRemux.SelectRecording=\"Selecteer OBS-opnemen...\"\nRemux.SelectTarget=\"Selecteer het doelbestand...\"\nRemux.FileExistsTitle=\"Doelbestanden bestaan al\"\nRemux.FileExists=\"Het volgende doelbestanden bestaan al. Wil je deze vervangen?\"\nRemux.ExitUnfinishedTitle=\"Remuxen is bezig\"\nRemux.ExitUnfinished=\"Het remuxen is nog niet afgerond; als je nu stopt kan het doelbestand onbruikbaar worden.\\nWeet je zeker dat je wil stoppen met remuxen?\"\nRemux.HelpText=\"Laat bestanden in dit scherm vallen om ze te remuxen, of selecteer een lege 'OBS-opname'-cel om te zoeken naar een bestand.\"\nRemux.NoFilesAddedTitle=\"Geen remuxing-bestand toegevoegd\"\nRemux.NoFilesAdded=\"Geen bestand is toegevoegd om te remuxen. Zet een map met een of meerdere video bestanden neer.\"\nMissingFiles=\"Ontbrekende bestanden\"\nMissingFiles.MissingFile=\"Ontbrekend bestand\"\nMissingFiles.NewFile=\"Nieuw bestand\"\nMissingFiles.HelpText=\"Sommige bestanden ontbreken sinds je voor het laatst OBS hebt gebruikt.\"\nMissingFiles.NumFound=\"%1 van de %2 gevonden\"\nMissingFiles.Search=\"Doorzoek map...\"\nMissingFiles.SelectFile=\"Selecteer bestand...\"\nMissingFiles.SelectDir=\"Selecteer de map waarin u wilt zoeken\"\nMissingFiles.State=\"Toestand\"\nMissingFiles.Missing=\"Ontbrekende\"\nMissingFiles.Replaced=\"Vervangen\"\nMissingFiles.Cleared=\"Gewist\"\nMissingFiles.Found=\"Gevonden\"\nMissingFiles.AutoSearch=\"Extra bestand overeenkomsten gevonden\"\nMissingFiles.AutoSearchText=\"OBS heeft extra overeenkomsten gevonden voor ontbrekende bestanden in die directory. Wilt u ze toevoegen?\"\nMissingFiles.NoMissing.Title=\"Ontbrekende bestanden controle\"\nMissingFiles.NoMissing.Text=\"Er lijken geen bestanden te ontbreken.\"\nMacPermissions.MenuAction=\"Beoordeel App Permissies...\"\nMacPermissions.Title=\"Beoordeel App Permissies\"\nMacPermissions.Description=\"OBS Studio heeft je toestemming nodig om bepaalde functies te kunnen geven. Het is aanbevolen om deze machtigingen in te schakelen, maar ze zijn niet nodig om de app te gebruiken. Je kan ze later altijd weer inschakelen.\"\nMacPermissions.Description.OpenDialog=\"U kunt dit dialoogvenster heropenen via het OBS Studio menu.\"\nMacPermissions.AccessGranted=\"Toegang verleend\"\nMacPermissions.RequestAccess=\"Toegang aanvragen\"\nMacPermissions.OpenPreferences=\"Open %1 voorkeuren\"\nMacPermissions.Item.ScreenRecording=\"Scherm opnemen\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS vereist deze toestemming om je scherm vast te leggen.\"\nMacPermissions.Item.Camera.Details=\"Deze machtiging is nodig om inhoud van een webcam of capture card vast te leggen.\"\nMacPermissions.Item.Microphone=\"Microfoon\"\nMacPermissions.Item.Microphone.Details=\"OBS vereist deze toestemming als je je microfoon of een externe audioapparaat wilt vastleggen.\"\nMacPermissions.Item.InputMonitoring=\"Controle van de input\"\nMacPermissions.Item.InputMonitoring.Details=\"Deze permissie is vereist voor sneltoetsen als OBS in de achtergrond draait\"\nMacPermissions.Item.Accessibility=\"Toegankelijkheid\"\nMacPermissions.Item.Accessibility.Details=\"Bij oudere installaties, OBS is mogelijk terug te vinden onder \\\"Toegankelijkheid\\\" in plaats. van \\\"Controle van de input\\\".\"\nMacPermissions.Continue=\"Doorgaan\"\nSourceLeak.Title=\"Bron opschoon fout\"\nSourceLeak.Text=\"Er is een probleem opgetreden bij het wisselen van scèneverzamelingen en sommige bronnen konden niet worden verwijderd. Dit probleem wordt meestal veroorzaakt door plug-ins die bronnen niet correct vrijgeven. Zorg ervoor dat alle plug-ins die u gebruikt up-to-date zijn.\\n\\nOBS Studio wordt nu afgesloten om mogelijke gegevensbeschadiging te voorkomen.\"\nBasic.DesktopDevice1=\"Desktop-audio\"\nBasic.DesktopDevice2=\"Desktop-audio 2\"\nBasic.Scene=\"Scène\"\nBasic.DisplayCapture=\"Beeldschermopname\"\nBasic.Main.PreviewConextMenu.Enable=\"Preview inschakelen\"\nBasic.Main.Preview.Disable=\"Preview uitschakelen\"\nScaleFiltering=\"Schaalfiltering\"\nBlendingMethod=\"Blending methode\"\nBlendingMethod.Default=\"Standaard\"\nBlendingMethod.SrgbOff=\"SRGB uit\"\nBlendingMode=\"Blending modus\"\nBlendingMode.Normal=\"Normaal\"\nBlendingMode.Additive=\"Toevoegen\"\nBlendingMode.Subtract=\"Aftrekken\"\nBlendingMode.Screen=\"Scherm\"\nBlendingMode.Multiply=\"Vermenigvuldigen\"\nBlendingMode.Lighten=\"Lichter maken\"\nBlendingMode.Darken=\"Donkerder maken\"\nDeinterlacing=\"Deïnterlacen\"\nDeinterlacing.Discard=\"Negeren\"\nDeinterlacing.TopFieldFirst=\"Bovenste veld eerst\"\nDeinterlacing.BottomFieldFirst=\"Onderste veld eerst\"\nVolControl.SliderUnmuted=\"Volumeregelaar voor '%1':\"\nVolControl.SliderMuted=\"Volumeregelaar voor '%1': (momenteel gedempt)\"\nVolControl.Mute=\"'%1' dempen\"\nVolControl.Properties=\"Eigenschappen van '%1'\"\nVolControl.UnassignedWarning.Title=\"Niet toegewezen audio bron\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" is niet toegewezen aan een audio kanaal en zal niet hoorbaar zijn in de streams of opnames.\\n\\nOm een audio bron toe te wijzen aan een kanaal, open de Geavanceerde Audio Eigenschappen via het rechts-klik menu of de tandwiel knop in de mixer dock toolbalk.\"\nBasic.Main.AddSceneDlg.Title=\"Voeg scène toe\"\nBasic.Main.AddSceneDlg.Text=\"Voer de naam van de scène in\"\nBasic.Main.DefaultSceneName.Text=\"Scène %1\"\nBasic.Main.AddSceneCollection.Title=\"Scèneverzameling toevoegen\"\nBasic.Main.AddSceneCollection.Text=\"Voer de naam van de scèneverzameling in\"\nBasic.Main.RenameSceneCollection.Title=\"Hernoem de scèneverzameling\"\nBasic.Main.RemigrateSceneCollection.Title=\"Resolutie van scèneverzameling bijwerken\"\nBasic.Main.RemigrateSceneCollection.Text=\"Wilt u de resolutie van de scèneverzameling van \\\"%1\\\" bijwerken zodat deze overeenkomt met de canvasresolutie van het huidige profiel van %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Kan de resolutie van de scèneverzameling niet bijwerken terwijl uitgangen actief zijn.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Kan de resolutie van de scèneverzameling niet updaten. De oorspronkelijke resolutie is onbekend.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Resetten niet mogelijk: het wijzigen van de OBS-resolutie is mislukt.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Resetten niet mogelijk: huidige resolutie is al de basisresolutie van de scèneverzameling.\"\nAddProfile.Title=\"Voeg profiel toe\"\nAddProfile.Text=\"Voer de naam van het profiel in\"\nAddProfile.WizardCheckbox=\"Toon automatische configuratie-wizard\"\nRenameProfile.Title=\"Profiel hernoemen\"\nBasic.Main.MixerRename.Title=\"Hernoem Audiobron\"\nBasic.Main.MixerRename.Text=\"Voer de naam van de audiobron in\"\nBasic.Main.PreviewDisabled=\"Preview is momenteel uitgeschakeld\"\nBasic.SourceSelect=\"Maak/Selecteer Bron\"\nBasic.SourceSelect.CreateNew=\"Nieuwe maken\"\nBasic.SourceSelect.AddExisting=\"Bestaande toevoegen\"\nBasic.SourceSelect.AddVisible=\"Maak bron zichtbaar\"\nBasic.Main.Sources.Visibility=\"Zichtbaarheid\"\nBasic.Main.Sources.VisibilityDescription=\"Bepaalt de zichtbaarheid van '%1' op het canvas\"\nBasic.Main.Sources.Lock=\"Vergrendel\"\nBasic.Main.Sources.LockDescription=\"Vergrendelt de positie en schaal van '%1' op het canvas\"\nBasic.PropertiesWindow=\"Eigenschappen van '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (automatisch geselecteerd: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Selecteer kleur\"\nBasic.PropertiesWindow.SelectFont=\"Selecteer een lettertype\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Kies een lettertype\"\nBasic.PropertiesWindow.ConfirmTitle=\"Instellingen aangepast\"\nBasic.PropertiesWindow.Confirm=\"Er zijn niet opgeslagen aanpassingen. Wil je deze bewaren?\"\nBasic.PropertiesWindow.NoProperties=\"Geen eigenschappen beschikbaar\"\nBasic.PropertiesWindow.AddFiles=\"Voeg bestanden toe\"\nBasic.PropertiesWindow.AddDir=\"Voeg de directory toe\"\nBasic.PropertiesWindow.AddURL=\"Voeg pad/URL toe\"\nBasic.PropertiesWindow.AddEditableListDir=\"Voeg een directory toe aan '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Voeg bestanden toe aan '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Voeg invoer toe aan '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Bewerk de invoer van '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Simpele FPS Waarden\"\nBasic.PropertiesView.FPS.Rational=\"Rationale FPS-waarden\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Geldige FPS-bereiken:\"\nBasic.PropertiesView.UrlButton.Text=\"Deze link in uw standaard webbrowser openen?\"\nBasic.InteractionWindow=\"Interactie met '%1'\"\nBasic.StatusBar.Reconnecting=\"Verbinding verbroken, opnieuw verbinden over %2 second(en) (poging %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Poging opnieuw te verbinden... (poging %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Opnieuw verbinden gelukt\"\nBasic.StatusBar.Delay=\"Vertraging (%1 sec)\"\nBasic.StatusBar.DelayStartingIn=\"Vertraging (start over %1 sec)\"\nBasic.StatusBar.DelayStoppingIn=\"Vertraging (stopt over %1 sec)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Vertraging (stopt over %1 sec, start over %2 sec)\"\nBasic.StatusBar.RecordingSavedTo=\"Opname opgeslagen in '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Replay buffer opgeslagen in '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Schermopname opgeslagen in '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Opname automatisch geremuxed naar '%1'\"\nBasic.Filters.AsyncFilters=\"Audio-/video-filters\"\nBasic.Filters.AudioFilters=\"Audio-filters\"\nBasic.Filters.EffectFilters=\"Effect-filters\"\nBasic.Filters.Title=\"Filters voor '%1'\"\nBasic.Filters.AddFilter.Title=\"Filternaam\"\nBasic.Filters.AddFilter.Text=\"Geef een naam voor de filter\"\nBasic.TransformWindow=\"Scèneobject-transformatie\"\nBasic.TransformWindow.Position=\"Positie\"\nBasic.TransformWindow.PositionX=\"Positie X\"\nBasic.TransformWindow.PositionY=\"Positie Y\"\nBasic.TransformWindow.Rotation=\"Rotatie\"\nBasic.TransformWindow.Size=\"Grootte\"\nBasic.TransformWindow.Width=\"Breedte\"\nBasic.TransformWindow.Height=\"Hoogte\"\nBasic.TransformWindow.Alignment=\"Positionele uitlijning\"\nBasic.TransformWindow.BoundsAlignment=\"Uitlijning in Bounding Box\"\nBasic.TransformWindow.Bounds=\"Bounding Box Grootte\"\nBasic.TransformWindow.BoundsWidth=\"Selectiekader breedte\"\nBasic.TransformWindow.BoundsHeight=\"Selectiekader hoogte\"\nBasic.TransformWindow.CropToBounds=\"Bijsnijden tot grenzen\"\nBasic.TransformWindow.Crop=\"Bijsnijden\"\nBasic.TransformWindow.CropLeft=\"Bijsnijden links\"\nBasic.TransformWindow.CropRight=\"Bijsnijden rechts\"\nBasic.TransformWindow.CropTop=\"Bijsnijden boven\"\nBasic.TransformWindow.CropBottom=\"Bijsnijden onder\"\nBasic.TransformWindow.Alignment.TopLeft=\"Linksboven\"\nBasic.TransformWindow.Alignment.TopCenter=\"Middenboven\"\nBasic.TransformWindow.Alignment.TopRight=\"Rechtsboven\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Linksmidden\"\nBasic.TransformWindow.Alignment.Center=\"Midden\"\nBasic.TransformWindow.Alignment.CenterRight=\"Rechtsmidden\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Linksonder\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Middenonder\"\nBasic.TransformWindow.Alignment.BottomRight=\"Rechtsonder\"\nBasic.TransformWindow.BoundsType.None=\"Geen grenzen\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Alleen maximumgrootte\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Schaal naar binnenste grenzen\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Schaal naar buitenste grenzen\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Schaal naar de breedte van de grenzen\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Schaal naar de hoogte van de grenzen\"\nBasic.TransformWindow.BoundsType.Stretch=\"Uitrekken naar de grenzen\"\nBasic.TransformWindow.Title=\"Wijzig Transformatie voor '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Geen bron geselecteerd\"\nBasic.Main.AddSourceHelp.Title=\"Bron kan niet toegevoegd worden\"\nBasic.Main.AddSourceHelp.Text=\"Je moet ten minste één scène hebben om een bron toe te kunnen voegen.\"\nBasic.Main.Scenes=\"Scènes\"\nBasic.Main.Sources=\"Bronnen\"\nBasic.Main.Source=\"Bron\"\nBasic.Main.Controls=\"Bedieningselementen\"\nBasic.Main.PreparingStream=\"Voorbereiden...\"\nBasic.Main.Connecting=\"Verbinden...\"\nBasic.Main.StartRecording=\"Opname starten\"\nBasic.Main.StartReplayBuffer=\"Replaybuffer starten\"\nBasic.Main.SaveReplay=\"Replay opslaan\"\nBasic.Main.StartStreaming=\"Streamen starten\"\nBasic.Main.StartBroadcast=\"Ga Live\"\nBasic.Main.StartVirtualCam=\"Virtuele camera starten\"\nBasic.Main.StopRecording=\"Opname stoppen\"\nBasic.Main.PauseRecording=\"Pauzeer de opname\"\nBasic.Main.UnpauseRecording=\"Vervolg de opname\"\nBasic.Main.SplitFile=\"Opnamebestand splitsen\"\nBasic.Main.AddChapterMarker=\"Voeg hoofdstuk markering toe (Alleen hybride MP4/MOV)\"\nBasic.Main.StoppingRecording=\"De opname stoppen...\"\nBasic.Main.StopReplayBuffer=\"Replaybuffer stoppen\"\nBasic.Main.StoppingReplayBuffer=\"De replaybuffer aan het stoppen...\"\nBasic.Main.SetupBroadcast=\"Uitzending beheren\"\nBasic.Main.StopStreaming=\"Streamen stoppen\"\nBasic.Main.StopBroadcast=\"Beëindigd uitzending\"\nBasic.Main.StoppingStreaming=\"De stream stoppen...\"\nBasic.Main.ForceStopStreaming=\"Stop streamen (vertraging negeren)\"\nBasic.Main.ShowContextBar=\"Toon de bron-taakbalk\"\nBasic.Main.HideContextBar=\"Verberg de brontaakbalk\"\nBasic.Main.StopVirtualCam=\"Stop virtuele camera\"\nBasic.Main.Group=\"Groep %1\"\nBasic.Main.GroupItems=\"Groepeer de geselecteerde items\"\nBasic.Main.Ungroup=\"Groep opheffen\"\nBasic.Main.GridMode=\"Rastermodus\"\nBasic.Main.ListMode=\"Lijstmodus\"\nBasic.Main.VirtualCamConfig=\"Virtuele Camera configureren\"\nBasic.VCam.VirtualCamera=\"Virtuele Camera\"\nBasic.VCam.OutputType=\"Uitvoer Type\"\nBasic.VCam.OutputSelection=\"Uitvoer Selectie\"\nBasic.VCam.OutputType.Program=\"Programma (Standaard)\"\nBasic.VCam.OutputSelection.NoSelection=\"Geen selectie voor dit uitvoertype\"\nBasic.VCam.RestartWarning=\"De virtuele camera wordt herstart om deze wijziging toe te passen\"\nBasic.MainMenu.File=\"Bestand (&F)\"\nBasic.MainMenu.File.Export=\"&Exporteren\"\nBasic.MainMenu.File.Import=\"&Importeren\"\nBasic.MainMenu.File.ShowRecordings=\"Toon de &opnames\"\nBasic.MainMenu.File.Remux=\"Re&mux de opnames\"\nBasic.MainMenu.File.Settings=\"Instellingen (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Toon de instellingenmap\"\nBasic.MainMenu.File.ShowProfileFolder=\"Toon de profielmap\"\nBasic.MainMenu.File.ShowMissingFiles=\"Controleren op ontbrekende bestanden\"\nBasic.MainMenu.File.Exit=\"&Afsluiten\"\nBasic.MainMenu.Edit=\"B&ewerken\"\nBasic.MainMenu.Edit.Undo=\"Ongedaan maken (&U)\"\nBasic.MainMenu.Edit.Redo=\"Opnieuw uitvoeren (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Voorvertoning vergrendelen (&L)\"\nBasic.MainMenu.Edit.Scale=\"Voorvertoning &schalen\"\nBasic.MainMenu.Edit.Scale.Window=\"Schaal naar het venster\"\nBasic.MainMenu.Edit.Scale.Output=\"Uitvoer (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Geschaald (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Zoom in\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Zoom uit\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Reset zoom\"\nBasic.MainMenu.Edit.Transform=\"&Transformeren\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Transformati&e bewerken...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopieer de transformatie\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Plak de transformatie\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Reset de transformatie\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Draai 90º rechtsom\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Draai 90º linksom\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Draai 180º\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"&Horizontaal spiegelen\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"&Verticaal spiegelen\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Aan het scherm aanpassen (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Uitrekken naar het &scherm\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centeren op het scherm\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Verticaal centreren\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Horizontaal centreren\"\nBasic.MainMenu.Edit.Order=\"V&olgorde\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Omhoog verplaatsen (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Omlaag verplaatsen (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Naar boven verplaa&tsen\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Naar de onderkant verplaatsen (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Ge&avanceerde audio-eigenschappen\"\nBasic.MainMenu.View=\"Beeld (&V)\"\nBasic.MainMenu.View.Toolbars=\"&Taakbalken\"\nBasic.MainMenu.View.ListboxToolbars=\"Dock werkbalken\"\nBasic.MainMenu.View.ContextBar=\"Bron-taakbalk\"\nBasic.MainMenu.View.SourceIcons=\"Bron-&iconen\"\nBasic.MainMenu.View.StatusBar=\"&Statusbalk\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Volledig scherminterface\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Altijd op de voorgrond\"\nBasic.MainMenu.View.SceneListMode=\"Scene lijst modus\"\nBasic.MainMenu.Docks.ResetDocks=\"&Dokken opnieuw instellen\"\nBasic.MainMenu.Docks.LockDocks=\"&Docks vergrendelen\"\nBasic.MainMenu.Docks.SideDocks=\"Volledige hoogte docks (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Aangepaste browser-docks...\"\nBasic.MainMenu.Import=\"Importeer...\"\nBasic.MainMenu.Export=\"Exporteer...\"\nBasic.MainMenu.SceneCollection=\"&Scèneverzameling\"\nBasic.MainMenu.Profile=\"&Profiel\"\nBasic.MainMenu.Profile.Import=\"Importeer profiel\"\nBasic.MainMenu.Profile.Export=\"Exporteer profiel\"\nBasic.MainMenu.SceneCollection.Import=\"Importeer scèneverzameling\"\nBasic.MainMenu.SceneCollection.Export=\"Exporteer scèneverzameling\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Reset basisresolutie\"\nBasic.MainMenu.SceneCollection.Migrate=\"Stel basisresolutie in\"\nBasic.MainMenu.Profile.Exists=\"Het profiel bestaat al\"\nBasic.MainMenu.Help=\"&Hulp\"\nBasic.MainMenu.Help.HelpPortal=\"Help-portal\"\nBasic.MainMenu.Help.Website=\"Ga naar de &website\"\nBasic.MainMenu.Help.Discord=\"Word lid van onze &Discord-server\"\nBasic.MainMenu.Help.WhatsNew=\"Wat is nieuw\"\nBasic.MainMenu.Help.Logs=\"&Logbestanden\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Toon logbe&standen\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Upload het huidige logbestand (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Upload &Vorige logbestand\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Bekijk het huidige logbestand (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Versie informatie\"\nBasic.MainMenu.Help.CheckForUpdates=\"Controleer op updates\"\nBasic.MainMenu.Help.Repair=\"Controleer Bestandsintegriteit\"\nBasic.MainMenu.Help.RestartSafeMode=\"Herstarten in veilige modus\"\nBasic.MainMenu.Help.RestartNormal=\"Herstarten in normale modus\"\nBasic.MainMenu.Help.CrashLogs=\"Crash-&rapporten\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Toon cra&shrapporten\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Upload &vorige crashrapport\"\nBasic.MainMenu.Help.About=\"Over (&A)\"\nBasic.Settings.ProgramRestart=\"Het programma moet opnieuw worden opgestart om deze instellingen effectief te maken.\"\nBasic.Settings.ConfirmTitle=\"Bevestig de wijzigingen\"\nBasic.Settings.Confirm=\"Er zijn niet-opgeslagen wijzigingen; wijzigingen opslaan?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 beheert enkele van uw streaminstellingen \"\nBasic.Settings.General=\"Algemeen\"\nBasic.Settings.General.Language=\"Taal\"\nBasic.Settings.General.UpdateChannel=\"Update kanaal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Uitgeschakeld)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Standaard)\"\nBasic.Settings.General.EnableAutoUpdates=\"Automatisch op updates controleren tijdens het opstarten\"\nBasic.Settings.General.OpenStatsOnStartup=\"Open het statistiekenvenster tijdens het opstarten\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"OBS-vensters verbergen in scherm opname\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Deze instelling verbergt alle niet-projector OBS Studio vensters voor opname door OBS en beïnvloedt \\nandere applicaties, zoals conferentie, schermdelen, externe ondersteuning, screenshots en andere opnamesoftware.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Het inschakelen van deze optie verbergt alle niet-projector OBS Studio vensters zodat die niet worden vastgelegd door OBS en beïnvloedt andere toepassingen, zoals conferentie, schermdelen, externe ondersteuning, screenshots en andere opnamesoftware.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Toon het bevestigingsvenster bij het starten van streams\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Toon het bevestigingsvenster bij het stoppen van streams\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Toon het bevestigingsvenster bij het stoppen van de opname\"\nBasic.Settings.General.HideProjectorCursor=\"Verberg cursor boven projectors\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Houd projectors altijd op de voorgrond\"\nBasic.Settings.General.Snapping=\"Bronuitlijning vastklikken\"\nBasic.Settings.General.ScreenSnapping=\"Klik bronnen vast aan de rand van het scherm\"\nBasic.Settings.General.CenterSnapping=\"Klik bronnen vast aan het horizontale en verticale midden\"\nBasic.Settings.General.SourceSnapping=\"Klik bronnen vast aan andere bronnen\"\nBasic.Settings.General.SnapDistance=\"Vastklik-gevoeligheid\"\nBasic.Settings.General.SpacingHelpers=\"Toon pixel uitlijningsgidsen\"\nBasic.Settings.General.RecordWhenStreaming=\"Automatisch opnemen tijdens het streamen\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Blijf opnemen als de stream stopt\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Start de replaybuffer automatisch tijdens het streamen\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Houd de replaybuffer actief als de stream stopt\"\nBasic.Settings.General.SysTray=\"Systeemvak\"\nBasic.Settings.General.SysTrayWhenStarted=\"Naar systeemvak minimaliseren bij opstarten\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Altijd minimaliseren naar het systeemvak in plaats van naar de taakbalk\"\nBasic.Settings.General.SaveProjectors=\"Projectors opslaan bij afsluiten\"\nBasic.Settings.General.CloseExistingProjectors=\"Limiteer tot één volledig scherm projector per scherm\"\nBasic.Settings.General.Preview=\"Voorvertoning\"\nBasic.Settings.General.OverflowHidden=\"Overloop verbergen\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Overloop altijd zichtbaar\"\nBasic.Settings.General.OverflowSelectionHidden=\"Overloop weergeven, zelfs als de bron onzichtbaar is\"\nBasic.Settings.General.Importers=\"Importeerders\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Zoek bekende locaties voor scèneverzamelingen bij het importeren\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Ga over naar scène bij dubbelklik\"\nBasic.Settings.General.StudioPortraitLayout=\"Portret/verticale layout inschakelen\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Toon voorbeeld/programma labels\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Klik hier om tussen scènes te wisselen\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Toon namen van scènes\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Veilige gebieden (EBU R 95) tekenen\"\nBasic.Settings.General.MultiviewLayout=\"Multiview-indeling\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontaal, boven (8 scènes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontaal, onder (8 scènes)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Verticaal, links (8 scènes)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Verticaal, rechts (8 scènes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontaal, boven (18 scènes)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontaal, boven (24 scènes)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Alleen scènes (4 scènes)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Alleen scènes (9 scènes)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Alleen scènes (16 scènes)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Alleen scènes (25 scènes)\"\nBasic.Settings.General.ChannelName.stable=\"Stabiel\"\nBasic.Settings.General.ChannelDescription.stable=\"Laatste stabiele versie\"\nBasic.Settings.General.ChannelName.beta=\"Beta's / Release Kandidaten\"\nBasic.Settings.General.ChannelDescription.beta=\"Mogelijk onstabiele pre-release versies\"\nBasic.Settings.Appearance=\"Uiterlijk\"\nBasic.Settings.Appearance.General=\"Algemeen\"\nBasic.Settings.Appearance.General.Theme=\"Thema\"\nBasic.Settings.Appearance.General.Variant=\"Stijl\"\nBasic.Settings.Appearance.General.NoVariant=\"Geen styles beschikbaar\"\nBasic.Settings.Appearance.FontScale=\"Lettertypegrootte\"\nBasic.Settings.Appearance.Density=\"Dichtheid\"\nBasic.Settings.Appearance.Classic=\"Klassiek\"\nBasic.Settings.Appearance.Normal=\"Normaal\"\nBasic.Settings.Appearance.Comfortable=\"Comfortabel\"\nBasic.Settings.Appearance.OptionsWarning=\"Sommige weergave opties zijn niet beschikbaar voor deze stijl.\"\nBasic.Settings.Stream.Destination=\"Bestemming\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Gebruik authenticatie\"\nBasic.Settings.Stream.Custom.Username=\"Gebruikersnaam\"\nBasic.Settings.Stream.Custom.Password=\"Wachtwoord\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: voer de srp_username in.\\nRTMP: voer de gebruikersnaam in.\\nSRT: niet gebruikt.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: voer het srp_password in.\\nRTMP: voer het wachtwoord in.\\nSRT: voer de encryptie wachtwoord in.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Bandbreedte testmodus inschakelen\"\nBasic.Settings.Stream.TTVAddon=\"Twitch Chat toevoegingen\"\nBasic.Settings.Stream.TTVAddon.None=\"Geen\"\nBasic.Settings.Stream.TTVAddon.Both=\"BeterTTV en FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Ontbrekende stream-configuratie\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Instellingen openen\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL en streamsleutel ontbreken.\\n\\nOpen de instellingen om de URL en streamsleutel in het tabblad 'Stream' in te voeren.\"\nBasic.Settings.Stream.MissingUrl=\"De stream-URL ontbreekt.\\n\\nOpen de instellingen om de URL in te voeren in het tabblad 'Stream'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Stream-sleutel ontbreekt.\\n\\nOpen instellingen om de stream-sleutel in te voeren in het tabblad 'Stream'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Negeer de instellingsaanbevelingen van de streamingservice\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Overschrijf aanbevolen instellingen\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Waarschuwing: het negeren van de beperkingen van de service kan leiden tot verminderde kwaliteit van de stream of kan voorkomen dat je kunt streamen.\\n\\nDoorgaan?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maximale Video Bitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maximale Audio Bitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maximale Resolutie: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maximale FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Aangepaste server opgeven...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Aangepaste server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1 inschakelen\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maximale streaming bandbreedte\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maximale videotracks\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Schakel streamdump naar FLV in (gebruikt eenvoudige instellingen voor opnamebestanden)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Configuratie overschrijven (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Configuratie overschrijven inschakelen\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Multitrack video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Extra canvas\"\\nBasic.Settings.Stream.WHIPSimulcastLabel=\"\\nSimulcast\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Simulcast biedt de mogelijkheid om meerdere videokwaliteiten te coderen en te verzenden. <a href='https://obsproject.com/kb/whip-streaming-guide'>Meer informatie</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Totaal aantal lagen\"\nBasic.Settings.Stream.AdvancedOptions=\"Geavanceerde opties\"\nBasic.Settings.Output=\"Uitvoer\"\nBasic.Settings.Output.Format=\"Opnameformaat\"\nBasic.Settings.Output.Format.hMP4=\"Hybride MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hybride MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Gefragmenteerde MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Gefragmenteerde MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Gefragmenteerde MOV schrijft de opname in delen en vereist niet dezelfde afsluiting als de traditionele MOV bestanden.\\nDit zorgt ervoor dat het bestand speelbaar blijft, zelfs als het schrijven naar schijf wordt onderbroken, bijvoorbeeld als gevolg van een BSOD of stroomverlies.\\n\\nDit is mogelijk niet compatibel met alle spelers en editors. Gebruik Bestand → Remux opnamen om het bestand indien nodig in een meer compatibele indeling om te zetten.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Gefragmenteerde MP4 schrijft de opname in delen en vereist niet dezelfde afsluiting als de traditionele MP4 bestanden.\\nDit zorgt ervoor dat het bestand speelbaar blijft, zelfs als het schrijven naar schijf wordt onderbroken, bijvoorbeeld als gevolg van een BSOD of stroomverlies.\\n\\nDit is mogelijk niet compatibel met alle spelers en editors. Gebruik Bestand → Remux opnamen om het bestand indien nodig in een meer compatibele indeling om te zetten.\"\nBasic.Settings.Output.SelectDirectory=\"Selecteer Opnamemap\"\nBasic.Settings.Output.DynamicBitrate=\"Dynamisch wijzigen van de bitrate om congestie te beheren\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dynamisch wijzigen van de bitrate om congestie te beheren (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"In plaats van frames te laten vallen om congestie te verminderen, verandert de bitrate dynamisch à la minute.\\n\\nMerk op dat dit de vertraging voor kijkers kan verhogen als er significante plotselinge congestie optreedt.\\nAls de bitrate daalt, kan het tot een paar minuten duren om het te herstellen.\\n\\nMomenteel wordt het alleen ondersteund voor RTMP.\"\nBasic.Settings.Output.Mode=\"Uitvoermodus\"\nBasic.Settings.Output.Mode.Simple=\"Simpel\"\nBasic.Settings.Output.Mode.Adv=\"Geavanceerd\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg-uitvoer\"\nBasic.Settings.Output.UseReplayBuffer=\"Replay Buffer Inschakelen\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maximale herhalingstijd\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maximale hoeveelheid geheugen\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Geschat geheugengebruik: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Waarschuwing: Geschatte geheugengebruik van %1 MiB is groter dan de aanbevolen maximum van %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Kan geheugengebruik niet inschatten. Stel een limiet in op het geheugengebruik.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Replay Buffer Bestandsnaam Prefix\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Achtervoegsel\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Replay Buffer kan niet worden gebruikt wanneer opname-type is ingesteld op Aangepaste Uitvoer (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Opnamepad\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Opnamekwaliteit\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Hetzelfde als de stream\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Hoge Kwaliteit, Gemiddelde Bestandsgrootte\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Ononderscheidbare Kwaliteit, Grote Bestandsgrootte\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Lossless Kwaliteit, Enorm Grote Bestandsgrootte\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Waarschuwing: de video-bitrate van de stream zal worden ingesteld op %1, wat de bovengrens is van de huidige streamingservice.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Waarschuwing: De audio-bitrate van de stream zal worden ingesteld op %1, wat de bovengrens is van de huidige streamingservice.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Waarschuwing: opnames kunnen niet worden onderbroken als de opnamekwaliteit is ingesteld op 'Hetzelfde als de stream'.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Waarschuwing: De op dit moment geselecteerde opname formaat is niet ondersteund met de geselecteerde stream encoder(s).\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Waarschuwing: het opnemen met een software-encoder op een andere kwaliteit dan de stream zal extra CPU-gebruik vergen als je zowel streamt als opneemt.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Waarschuwing: Lossless kwaliteit genereert erg grote bestanden! Lossless kwaliteit kan tot wel 7 GB aan schijfruimte per minuut gebruiken bij hoge resoluties en framerates. Lossless kwaliteit is niet aanbevolen voor lange opnames tenzij er een grote hoeveelheid schijfruimte beschikbaar is. Replay buffer is niet beschikbaar bij gebruik van lossless kwaliteit. \"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Weet je zeker dat je lossless kwaliteit wilt gebruiken?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Lossless kwaliteit waarschuwing!\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Hardware (Apple, HVEC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264 laag cpu gebruik, verhoogt bestandsgrootte)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Standaard)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD Track (Gebruikt Track 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Audio kanaal\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Incompatibele Resolutie/Framerate\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Deze streamingservice ondersteunt de huidige uitvoerresolutie en/of beeldsnelheid niet; ze zullen worden gewijzigd in de dichtstbijzijnde compatibele waarde:\\n\\n%1\\n\\nWil je doorgaan?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolutie: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Niet compatible encoder\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"De streaming dienst \\\"%1\\\" ondersteunt de encoder \\\"%2\\\" niet. De encoder wordt aangepast naar: \\\"%3\\\".\\n\\n Wil je doorgaan?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"De streaming dienst \\\"%1\\\" ondersteunt de encoders \\\"%2\\\" en \\\"%3\\\" niet. Deze encoders worden aangepast naar \\\"%4\\\" en \\\"%5\\\".\\n\\n Wil je doorgaan?\"\nBasic.Settings.Output.Reconnect=\"Automatisch Opnieuw Verbinden\"\nBasic.Settings.Output.RetryDelay=\"Vertraging nieuwe poging\"\nBasic.Settings.Output.MaxRetries=\"Maximaal aantal pogingen\"\nBasic.Settings.Output.Advanced=\"Aangepaste Encoderinstellingen inschakelen (Geavanceerd)\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (laag CPU-gebruik, laagste kwaliteit)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (standaard) (gemiddeld CPU-gebruik, standaard kwaliteit)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (hoog CPU-gebruik, hoge kwaliteit)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Aangepaste encoderinstellingen\"\nBasic.Settings.Output.CustomMuxerSettings=\"Aangepaste muxer-instellingen\"\nBasic.Settings.Output.NoSpaceFileName=\"Bestandsnaam zonder spatie genereren\"\nBasic.Settings.Output.Adv.Rescale=\"Uitvoer Schalen\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Uitgeschakeld\"\nBasic.Settings.Output.Adv.AudioTrack=\"Geluidsspoor\"\nBasic.Settings.Output.Adv.Streaming=\"Streamen\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Streaming instellingen\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Spoor 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Spoor 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Spoor 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Spoor 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Spoor 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Spoor 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD Spoor\"\nBasic.Settings.Output.Adv.Encoder=\"Encoder-instellingen\"\nBasic.Settings.Output.Adv.Recording=\"Opnemen\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Opname instellingen\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Opnametype\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standaard\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Aangepaste Uitvoer (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Gebruik stream-encoder)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Bestandsnaamformaat\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Overschrijven als bestand bestaat\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Aangepaste uitvoer (FFmpeg) wordt zonder beschermingen geleverd.\\nOverweeg het gebruik van het opnametype \\\"Standaard\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg Uitvoertype\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Uitvoer naar URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Uitvoer naar Bestand\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Gebruikelijke opnameformaten\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Alle Bestanden\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Bestandspad of URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Containerformaat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Standaardformaat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Containerformaatbeschrijving\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Audio-/Videocodec geschat aan de hand van het bestandspad of de URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Standaardencoder\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Encoder Uitschakelen\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Video Encoderinstellingen (indien gewenst)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Audio Encoderinstellingen (indien gewenst)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxerinstellingen (indien aanwezig)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Tijd tussen keyframes (frames)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Toon alle codecs (ongeacht compatibiliteit)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg instellingen\"\nBasic.Settings.Output.EnableSplitFile=\"Automatische bestandssplitsing\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Gesplitst op tijd\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Gesplitst op grootte\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Alleen handmatig splitsen\"\nBasic.Settings.Output.SplitFile.Time=\"Splits Tijd\"\nBasic.Settings.Output.SplitFile.Size=\"Splits grootte\"\nScreenshot=\"Schermopname Uitvoer\"\nScreenshot.SourceHotkey=\"Schermopname Geselecteerde Bron\"\nScreenshot.StudioProgram=\"Schermopname (Programma)\"\nScreenshot.Preview=\"Schermopname (Preview)\"\nScreenshot.Scene=\"Schermopname (Scene)\"\nScreenshot.Source=\"Schermopname (Bron)\"\nFilenameFormatting.TT.CCYY=\"Jaar, 4 getallen\"\nFilenameFormatting.TT.YY=\"Jaar, laatste twee getallen (00-99)\"\nFilenameFormatting.TT.MM=\"Maand als een decimaal getal (01-12)\"\nFilenameFormatting.TT.DD=\"Dag van de maand, nul-opgevuld (01-31)\"\nFilenameFormatting.TT.hh=\"Uur in 24 uur formaat (00-23)\"\nFilenameFormatting.TT.mm=\"Minuut (00-59)\"\nFilenameFormatting.TT.ss=\"Seconde (00-59)\"\nFilenameFormatting.TT.Percent=\"Een % teken\"\nFilenameFormatting.TT.a=\"Afgekortte weekdag naam\"\nFilenameFormatting.TT.A=\"Volledige weekdag naam\"\nFilenameFormatting.TT.b=\"Afgekortte maand naam\"\nFilenameFormatting.TT.B=\"Volledige maand naam\"\nFilenameFormatting.TT.d=\"Dag van de maand, nul-opgevuld (01-31)\"\nFilenameFormatting.TT.H=\"Uur in 24 uur formaat (00-23)\"\nFilenameFormatting.TT.I=\"Uur in 12 uur formaat (01-12)\"\nFilenameFormatting.TT.m=\"Maand als een decimaal getal (01-12)\"\nFilenameFormatting.TT.M=\"Minuut (00-59)\"\nFilenameFormatting.TT.p=\"AM of PM aanduiding\"\nFilenameFormatting.TT.s=\"Tijd in seconden vanaf UNIX epoch\"\nFilenameFormatting.TT.S=\"Seconde (00-59)\"\nFilenameFormatting.TT.y=\"Jaar, laatste twee getallen (00-99)\"\nFilenameFormatting.TT.Y=\"Jaar\"\nFilenameFormatting.TT.z=\"ISO 8601 afwijking van UTC in tijdzone\"\nFilenameFormatting.TT.Z=\"Tijdzone naam of afkorting\"\nFilenameFormatting.TT.FPS=\"Frames per seconde\"\nFilenameFormatting.TT.CRES=\"Basisresolutie (Canvas)\"\nFilenameFormatting.TT.ORES=\"Uitvoerresolutie (Geschaald)\"\nFilenameFormatting.TT.VF=\"Videoformaat\"\nBasic.Settings.Video.Adapter=\"Videoadapter\"\nBasic.Settings.Video.BaseResolution=\"Basisresolutie (Canvas)\"\nBasic.Settings.Video.ScaledResolution=\"Uitvoerresolutie (Geschaald)\"\nBasic.Settings.Video.DownscaleFilter=\"Resolutieverlagingsfilter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Resoluties zijn gelijk, geen downscaling nodig]\"\nBasic.Settings.Video.FPSCommon=\"Gebruikelijke FPS-waardes\"\nBasic.Settings.Video.FPSInteger=\"Geheel-getal FPS-waarde\"\nBasic.Settings.Video.FPSFraction=\"Breuk FPS-waarde\"\nBasic.Settings.Video.Numerator=\"Teller\"\nBasic.Settings.Video.Denominator=\"Noemer\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Experimenteel)\"\nBasic.Settings.Video.InvalidResolution=\"Ongeldige resolutiewaarde. Moet [breedte] x [hoogte] zijn (bijv. 1920 x 1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Video-uitvoer is momenteel actief. Schakel a.u.b. alle uitvoeren uit om video-instellingen aan te passen.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (Snelste, maar wazig bij schalen)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (Scherper schalen, 16 samples)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Scherper schalen, 36 samples)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Gebied (gewogen bedrag, 4/6/9 voorbeelden)\"\nBasic.Settings.Audio.Channels=\"Kanalen\"\nBasic.Settings.Audio.MeterDecayRate=\"Verval snelheid\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Snel\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Gemiddeld (Type I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Traag (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Piek Meter Type\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Sample piek\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True piek (hogere CPU-gebruik)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"Waarschuwing: Surround sound audio is ingeschakeld.\"\nBasic.Settings.Audio.MultichannelWarning=\"Als je streamt, controleer dan of je streamingdienst zowel surround sound-opname als surround sound-weergave ondersteunt. Facebook 360 Live ondersteunt bijvoorbeeld volledig surround sound; YouTube Live ondersteunt 5.1 audio-opname (en weergave op tv's).\\n\\nOBS-audiofilters zijn compatibel met surround sound, hoewel ondersteuning voor VST-plug-ins niet gegarandeerd is.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Surround sound audio inschakelen?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Weet je zeker dat je surround sound audio wil inschakelen?\"\nBasic.Settings.Audio.Devices=\"Globale Audio-apparaten\"\nBasic.Settings.Audio.AuxDevice=\"Mic/Aux Audioapparaat\"\nBasic.Settings.Audio.AuxDevice2=\"Mic/Aux Audioapparaat 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mic/Aux Audioapparaat 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mic/Aux Audioapparaat 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Push-to-mute inschakelen\"\nBasic.Settings.Audio.PushToMuteDelay=\"Push-to-mute vertraging\"\nBasic.Settings.Audio.EnablePushToTalk=\"Push-to-talk inschakelen\"\nBasic.Settings.Audio.PushToTalkDelay=\"Push-to-talk vertraging\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Apparaat niet verbonden of niet beschikbaar]\"\nBasic.Settings.Audio.Disabled=\"Uitgeschakeld\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Low Latency Audio Buffering Modus (voor Decklink/NDI outputs)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"WAARSCHUWING: Lage latency audiobuffering is ingeschakeld.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Lage latency audiobuffering modus kan audio storingen veroorzaken of stoppen met afspelen van sommige bronnen.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Lage latency geluidsbuffering modus inschakelen?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Weet u zeker dat u de lage latency audiobuffering wilt inschakelen?\"\nBasic.Settings.Accessibility=\"Toegankelijkheid\"\nBasic.Settings.Accessibility.ColorOverrides=\"Verschillende kleuren gebruiken\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Bronranden (selectie)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Bronranden (bijsnijden)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Bronranden (mouseover)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mixer volumebereik (-60 to -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mixer volumebereik (-20 to -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mixer volumebereik (-9 to 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mixer volumebereik (-60 to -20dB) (actief)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mixer volumebereik (-20 to -9dB) (actief)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mixer volumebereik (-9 to 0dB) (actief)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Kleurenpalet\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Standaard\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Aangepast\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Vriendelijk voor kleurenblindheid\"\nBasic.Settings.Advanced=\"Geavanceerd\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Toon actieve uitvoer waarschuwing bij afsluiten\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Procesprioriteit\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Hoog\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Boven Normaal\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normaal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Onder Normaal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Laagst\"\nBasic.Settings.Advanced.FormatWarning=\"Waarschuwing: Andere kleurformaten dan NV12/P010 zijn hoofdzakelijk bedoeld voor opnemen en worden niet aanbevolen bij streamen. Streamen kan verhoogd CPU-gebruik veroorzaken als gevolg van kleurformaat-conversie.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Waarschuwing: Hoge precisie formaten worden vaker gebruikt met HDR kleurenruimtes.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Waarschuwing: Rec. 2100 moet een formaat gebruiken met meer precisie.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Kleurindeling\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Kleurruimte\"\nBasic.Settings.Advanced.Video.ColorRange=\"Kleurbereik\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Beperkt\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR wit niveau\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR Nominaal piek niveau\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Monitoring apparaat\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Standaard\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Schakel Windows audio ducking uit\"\nBasic.Settings.Advanced.StreamDelay=\"Streamvertraging\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Duur\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Hervat op het eindpunt (verhoog vertraging) bij opnieuw verbinden\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Geschat Geheugengebruik: %1 MB\"\nBasic.Settings.Advanced.Network=\"Netwerk\"\nBasic.Settings.Advanced.Network.Disabled=\"Het momenteel geselecteerde streaming protocol ondersteunt het veranderen van de netwerkinstellingen niet.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Bind aan IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP fanilie\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Netwerk optimalisaties inschakelen\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"'TCP pacing' inschakelen\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Probeert RTMP output vriendelijker te maken voor andere latency gevoelige applicaties op het netwerk door de transmissiesnelheid te reguleren.\\nHet vergroot de kans verloren frames op onstabiele verbindingen.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Sneltoets Focus Gedrag\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nooit sneltoetsen uitschakelen\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Hotkeys uitschakelen wanneer hoofdvenster in focus is\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Schakel sneltoetsen uit wanneer hoofdvenster niet in focus is\"\nBasic.Settings.Advanced.AutoRemux=\"Remux automatisch naar %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(opnemen als mkv)\"\nBasic.AdvAudio=\"Geavanceerde Audioinstellingen\"\nBasic.AdvAudio.ActiveOnly=\"Alleen actieve bronnen\"\nBasic.AdvAudio.Name=\"Naam\"\nBasic.AdvAudio.VolumeSource=\"Volume voor '%1'\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix voor '%1'\"\nBasic.AdvAudio.Balance=\"Balans\"\nBasic.AdvAudio.BalanceSource=\"Balans voor '%1'\"\nBasic.AdvAudio.SyncOffsetSource=\"Sync Offset voor '%1'\"\nBasic.AdvAudio.Monitoring=\"Audio monitoring\"\nBasic.AdvAudio.Monitoring.None=\"Niet monitoren\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Alleen monitoren (uitvoer gedempt)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitoren en uitvoeren\"\nBasic.AdvAudio.MonitoringSource=\"Audio monitoring voor '%1'\"\nBasic.AdvAudio.AudioTracks=\"Sporen\"\nBasic.Settings.Hotkeys=\"Sneltoetsen\"\nBasic.Settings.Hotkeys.Pair=\"Toetsencombinaties gedeeld met '%1' werken als toggle\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filteren op sneltoets\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Deze sneltoets wordt gedeeld door een of meer andere acties, klik om conflicten te tonen\"\nBasic.Settings.Hotkeys.PleaseWait=\"Hotkeys laden, even geduld...\"\nBasic.Hotkeys.SelectScene=\"Wissel naar scène\"\nBasic.SystemTray.Show=\"Weergeven\"\nBasic.SystemTray.Hide=\"Verbergen\"\nBasic.SystemTray.Message.Reconnecting=\"Verbinding verbroken. Opnieuw verbinden...\"\nHotkeys.Left=\"Links\"\nHotkeys.Right=\"Rechts\"\nHotkeys.Up=\"Omhoog\"\nHotkeys.Down=\"Omlaag\"\nHotkeys.Space=\"Spatie\"\nHotkeys.NumpadMultiply=\"Numpad Vermenigvuldigen\"\nHotkeys.NumpadDivide=\"Numpad Delen\"\nHotkeys.NumpadAdd=\"Numpad Plus\"\nHotkeys.NumpadSubtract=\"Numpad Min\"\nHotkeys.NumpadDecimal=\"Numpad Punt\"\nHotkeys.MouseButton=\"Muis %1\"\nSceneItemShow=\"Toon '%1'\"\nSceneItemHide=\"Verberg '%1'\"\nOutputWarnings.NoTracksSelected=\"Selecteer a.u.b. ten minste een track\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Fout uitvoerinstellingen\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Alle uitvoeren moeten ten minste één audiotrack hebben geselecteerd.\"\nOutputWarnings.MP4Recording=\"Waarschuwing: opnames opgeslagen als MP4/MOV zijn onherstelbaar als het bestand niet correct afgesloten kan worden (bijvoorbeeld door BSOD's, stroomuitval enz.). Als je meersporen-audiotracks wilt opnemen, overweeg dan het gebruik van MKV en remux de opname naar MP4/MOV nadat hij afgerond is (Bestand → Remux opnames)\"\nOutputWarnings.CannotPause=\"Waarschuwing: opnames kunnen niet worden gepauzeerd als de opname-encoder is ingesteld op '(Gebruik de stream-encoder)'\"\nOutputWarnings.CodecIncompatible=\"De audio of video encoder selectie is gereset vanwege incompatibiliteit. Kies een ondersteunde encoder van de lijst.\"\nCodecCompat.Incompatible=\"(Niet ondersteund met %1)\"\nCodecCompat.CodecPlaceholder=\"Selecteer Encoder...\"\nCodecCompat.ContainerPlaceholder=\"Selecteer Formaat...\"\nCodecCompat.CodecMissingOnExit.Title=\"Geen encoder geselecteerd\"\nCodecCompat.CodecMissingOnExit.Text=\"Ten minste één video of audio encoder is niet ingesteld. Zorg ervoor dat u encoders selecteert voor zowel opnemen als streamen.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Geen formaat geselecteerd\"\nCodecCompat.ContainerMissingOnExit.Text=\"Er is geen opnameformaat geselecteerd. Selecteer een opnameformaat dat compatibel is met de geselecteerde stream-encoder.\"\nFinalScene.Title=\"Verwijder scène\"\nFinalScene.Text=\"Er moet tenminste één scène zijn.\"\nNoSources.Title=\"Geen bronnen\"\nNoSources.Text=\"Er zijn nog geen video bronnen toegevoegd, er zal dus een leeg scherm weergegeven worden. Weet je zeker dat je dit wilt doen?\"\nNoSources.Text.AddSource=\"Bronnen kunnen toegevoegd worden door op het + icoon onder het vak \\\"Bronnen\\\" in het hoofdvenster te klikken.\"\nNoSources.Label=\"U heeft geen bronnen.\\nKlik op de + knop hieronder\\nof rechtsklik hier om er toe te voegen.\"\nChangeBG=\"Kleur instellen\"\nCustomColor=\"Aangepaste kleur\"\nBrowserSource.EnableHardwareAcceleration=\"Browser bron hardwareversnelling inschakelen\"\nAbout=\"Over\"\nAbout.Info=\"OBS-studio is gratis en opensource video-opname- en livestreamsoftware.\"\nAbout.Donate=\"Maak een bijdrage\"\nAbout.GetInvolved=\"Raak betrokken\"\nAbout.Authors=\"Auteurs\"\nAbout.License=\"Licentie\"\nAbout.Error=\"Fout! Bestand kon niet worden gelezen.\\n\\nGa naar: %1\"\nAbout.Contribute=\"Steun het OBS project\"\nAddUrl.Title=\"Bron toevoegen via URL\"\nAddUrl.Text=\"U heeft een URL naar OBS gesleept. Dit zal automatisch de link als bron toevoegen. Doorgaan?\"\nResizeOutputSizeOfSource=\"Uitvoer grootte aanpassen (brongrootte)\"\nResizeOutputSizeOfSource.Text=\"De basis en uitvoer resoluties worden aangepast naar de grootte van de huidige bron.\"\nResizeOutputSizeOfSource.Continue=\"Wil je doorgaan?\"\nPreviewTransition=\"Voorbeeld overgang\"\nImporter=\"Scèneverzameling importeur\"\nImporter.SelectCollection=\"Selecteer scèneverzameling\"\nImporter.Collection=\"Scèneverzameling\"\nImporter.HelpText=\"Bestanden toevoegen aan dit venster om verzamelingen van OBS of andere ondersteunde programma's te importeren.\"\nImporter.Path=\"Verzamelingspad\"\nImporter.Program=\"Gedetecteerde Applicatie\"\nImporter.AutomaticCollectionPrompt=\"Scèneverzamelingen automatisch zoeken\"\nImporter.AutomaticCollectionText=\"OBS kan automatisch importeerbare scèneverzamelingen vinden van ondersteunde programma's van derden. Wilt u dat OBS automatisch verzamelingen voor u vindt?\\n\\nU kunt dit later wijzigen in Instellingen > Algemeen > Importeurs.\"\nImporter.SelectFile=\"Bladeren...\"\nRestart=\"Opnieuw opstarten\"\nNeedsRestart=\"OBS Studio moet opnieuw worden opgestart. Wil je nu opnieuw opstarten?\"\nLoadProfileNeedsRestart=\"Profiel bevat instellingen waarvoor OBS opnieuw moet worden opgestart.\\n%1\\n\\nWilt u OBS opnieuw opstarten om deze instellingen te laten werken?\"\nContextBar.NoSelectedSource=\"Geen bron geselecteerd\"\nContextBar.MediaControls.PlayMedia=\"Media afspelen\"\nContextBar.MediaControls.PauseMedia=\"Media pauzeren\"\nContextBar.MediaControls.RestartMedia=\"Media herstarten\"\nContextBar.MediaControls.PlaylistNext=\"Volgende in de afspeellijst\"\nContextBar.MediaControls.PlaylistPrevious=\"Vorige in de afspeellijst\"\nContextBar.MediaControls.BlindSeek=\"Media Zoek Widget\"\nYouTube.Auth.Ok=\"Autorisatie succesvol afgerond.\\nJe kunt nu deze pagina sluiten.\"\nYouTube.Auth.NoCode=\"Het autorisatieproces is niet voltooid.\"\nYouTube.Auth.NoChannels=\"Geen kanaal/kanalen beschikbaar op geselecteerde account\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube Gebruiker Autorisatie\"\nYouTube.Auth.WaitingAuth.Text=\"Voltooi alstublieft de autorisatie in uw externe browser.<br>Als de externe browser niet opent, volg dan deze link en voltooi de autorisatie:<br>%1\"\nYouTube.AuthError.Text=\"Laden van kanaal informatie mislukt: %1.\"\nYouTube.Actions.WindowTitle=\"YouTube uitzending configuratie - Kanaal: %1\"\nYouTube.Actions.CreateNewEvent=\"Nieuwe uitzending aanmaken\"\nYouTube.Actions.ChooseEvent=\"Selecteer bestaande uitzending\"\nYouTube.Actions.Title=\"Titel*\"\nYouTube.Actions.MyBroadcast=\"Mijn Uitzending\"\nYouTube.Actions.Description=\"Omschrijving\"\nYouTube.Actions.Privacy.Private=\"Privé\"\nYouTube.Actions.Privacy.Public=\"Publiek\"\nYouTube.Actions.Privacy.Unlisted=\"Niet openbaar\"\nYouTube.Actions.Category=\"Categorie\"\nYouTube.Actions.Thumbnail.SelectFile=\"Selecteer bestand...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Geen bestand geselecteerd\"\nYouTube.Actions.Thumbnail.ClearFile=\"Verwijderen\"\nYouTube.Actions.MadeForKids=\"Is deze video gemaakt voor kinderen?*\"\nYouTube.Actions.MadeForKids.Yes=\"Ja, het is gemaakt voor kinderen\"\nYouTube.Actions.MadeForKids.No=\"Nee, het is niet gemaakt voor kinderen\"\nYouTube.Actions.AdditionalSettings=\"Aanvullende instellingen\"\nYouTube.Actions.Latency=\"Vertraging\"\nYouTube.Actions.Latency.Normal=\"Normaal\"\nYouTube.Actions.Latency.Low=\"Laag\"\nYouTube.Actions.Latency.UltraLow=\"Ultra laag\"\nYouTube.Actions.EnableAutoStart=\"Automatisch starten inschakelen\"\nYouTube.Actions.EnableAutoStop=\"Automatisch stoppen inschakelen\"\nYouTube.Actions.AutoStartStop.TT=\"Geeft aan of deze geplande uitzending automatisch moet starten\"\nYouTube.Actions.EnableDVR=\"DVR inschakelen\"\nYouTube.Actions.ScheduleForLater=\"Plannen voor later\"\nYouTube.Actions.RememberSettings=\"Onthoud deze instellingen\"\nYouTube.Actions.Create_Ready=\"Uitzending aanmaken\"\nYouTube.Actions.Create_GoLive=\"Uitzending aanmaken en begin met streamen\"\nYouTube.Actions.Choose_Ready=\"Selecteer een uitzending\"\nYouTube.Actions.Choose_GoLive=\"Selecteer een uitzending en begin met streamen\"\nYouTube.Actions.Create_Schedule=\"Plan een uitzending\"\nYouTube.Actions.Create_Schedule_Ready=\"Plan en selecteer een uitzending\"\nYouTube.Actions.Dashboard=\"YouTube Studio Openen\"\nYouTube.Actions.Error.Title=\"Fout bij aanmaken live uitzending\"\nYouTube.Actions.Error.Text=\"YouTube toegangsfout '%1'.<br/>Een gedetailleerde foutbeschrijving kan worden gevonden op <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"YouTube toegangsfout. Controleer de netwerkverbinding of toegang tot YouTube server.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Fout bij aanmaken van stream '%1'.<br/>Gedetailleerde foutbeschrijving kan gevonden worden op <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Geen stream aangemaakt. Koppel het account opnieuw.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API-fout. Bekijk het logbestand voor meer informatie.\"\nYouTube.Actions.Error.BroadcastNotFound=\"De geselecteerde uitzending is niet gevonden.\"\nYouTube.Actions.Error.FileMissing=\"Het geselecteerde bestand bestaat niet.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Kan geselecteerd bestand niet openen.\"\nYouTube.Actions.Error.FileTooLarge=\"Geselecteerde bestand is te groot (Max: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Omzetten van de stream mislukt: %1<br/><br/>Als deze fout zich blijft voordoen, <a href='https://studio.youtube.com/video/%2/livestreaming'>open dan de stream in YouTube Studio</a> en probeer het handmatig.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Stream is bezig om om te schakelen naar het teststadium, dit kan enige tijd duren. Probeer het over 10-30 seconden opnieuw.\"\nYouTube.Actions.EventsLoading=\"Lijst met gebeurtenissen wordt geladen...\"\nYouTube.Actions.EventCreated.Title=\"Event aangemaakt\"\nYouTube.Actions.EventCreated.Text=\"Event succesvol aangemaakt.\"\nYouTube.Actions.Stream.ScheduledFor=\"Gepland voor %1\"\nYouTube.Actions.Stream.Resume=\"Onderbroken stream hervatten\"\nYouTube.Actions.Stream.YTStudio=\"Automatisch gemaakt door YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Een nieuwe Live Broadcast aan het aanmaken, even geduld...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Handmatige start vereist\"\nYouTube.Actions.AutoStartStreamingWarning=\"Automatisch starten is uitgeschakeld voor dit evenement, klik op \\\"Ga Live\\\" om uw uitzending te starten.\"\nYouTube.Actions.AutoStopStreamingWarning=\"U kunt geen nieuwe verbinding maken.<br>Uw stream zal stoppen en u zult niet langer live zijn.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Live streaming is niet ingeschakeld op het geselecteerde YouTube-kanaal.<br/><br/>Zie <a href='https://www.youtube.com/features'>youtube.com/features</a> voor meer informatie.\"\nYouTube.Errors.livePermissionBlocked=\"Live streaming is niet beschikbaar op het geselecteerde YouTube-kanaal.<br/>Houd er rekening mee dat het tot 24 uur kan duren voordat live streaming beschikbaar is na het inschakelen van het programma in je kanaalinstellingen.<br/><br/>Zie <a href='https://www.youtube.com/features'>youtube.com/features</a> voor details.\"\nYouTube.Errors.errorExecutingTransition=\"Overgang is mislukt door een backend fout. Probeer het over een paar seconden opnieuw.\"\nYouTube.Errors.errorStreamInactive=\"YouTube ontvangt geen gegevens voor uw stream. Controleer uw configuratie en probeer het opnieuw.\"\nYouTube.Errors.invalidTransition=\"De poging tot overgang was ongeldig. Dit kan komen doordat de stream geen eerdere transitie heeft voltooid. Wacht een paar seconden en probeer het opnieuw.\"\nYouTube.DocksRemoval.Title=\"Wis legacy YouTube browser docks\"\nYouTube.DocksRemoval.Text=\"Deze browser docks worden als verouderd verwijderd:\\n\\n%1\\nGebruik in plaats daarvan \\\"Docks/YouTube Live Control Room\\\".\"\nConfigDownload.WarningMessageTitle=\"Waarschuwing\"\nFailedToStartStream.MissingConfigURL=\"Geen configuratie url bescchikbaar voor de huidige service\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Aangepaste RTMP url niet gespecificeerd\"\nFailedToStartStream.InvalidCustomConfig=\"Incorrecte aangepaste configuratie\"\nFailedToStartStream.MissingCanvas=\"Een geconfigureerde extra canvas ontbreekt\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Aanmaken van multitrack video service mislukt\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Aanmaken van multitrack video RTMP output mislukt\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC niet beschikbaar.\\n\\nEncoder type '%1' niet gevonden\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Aanmaken video encoder '%1' (type: '%2') mislukt\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Opvragen OBS video informatie mislukt tijdens aanmaken van encoder '%1' (type: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Audio encoder aanmaken mislukt\"\nFailedToStartStream.NoRTMPURLInConfig=\"Configuratie bevat niet de stream doel RTMP(S) url\"\nFailedToStartStream.FallbackToDefault=\"Het starten van de stream met %1 is mislkt; wil je opnieuw proberen met een encoder instellingen?\"\nFailedToStartStream.ConfigRequestFailed=\"Kon geen configuratie ophalen van %1<br><br>HTTP fout: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Onbekende status waarde '%1' ontvangen\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nWil je doorgaan met streamen zonder %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nWil je doorgaan met streamen?\"\nFailedToStartStream.MissingEncoderConfigs=\"De live gaan configuratie bevat geen encoder configuraties\"\nFailedToStartStream.InvalidEncoderConfig=\"De live gaan configuratie bevat incorrecte encoder configuratie\"\nFailedToStartStream.StatusMissingHTML=\"Live gaan verzoek gaf een niet-gespecificeerde fout terug\"\nFailedToStartStream.NoConfigSupplied=\"Ontbrekende configuratie\"\nMultitrackVideo.Info=\"%1 optimaliseert automatisch jouw instellingen voor encoden en versturen van meerdere video kwaliteiten. Selecteren van deze optie stuurt informatie over jouw computer en drivers naar %2.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Incompatible instellingen\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 is op dit moment niet compatible met:\\n\\n%2\\nOm door te gaan met streamen met %1, disable de niet compatible instellingen:\\n\\n%3\\nen start streamen opnieuw.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Uitschakelen voor deze stream en start streamen\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Update instellingen en start streamen\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 is op dit moment niet compatible met [Audio →  Algemeen → kanalen] ingesteld op '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Audio →  Algemeen → kanalen] moet ingesteld staan op '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 vereist meerdere andere instellingen voor [Audio →  Algemeen → kanalen] \"\nPluginManager.Restart=\"OBS Studio moet opnieuw worden opgestart om de plugin wijzigingen toe te passen\"\nPluginManager.MissingPlugin=\"[PLUGIN NIET GEVONDEN]\"\nPluginManager.Section.Discover=\"Bladeren\"\nPluginManager.Section.Manage=\"Geïnstalleerd\"\nPluginManager.Section.Manage.Title=\"Beheer ingeschakelde plugins\"\n"
  },
  {
    "path": "frontend/data/locale/nn-NO.ini",
    "content": "Language=\"Norsk nynorsk\"\nApply=\"Bruk\"\nCancel=\"Avbryt\"\nClose=\"Lukk\"\nSave=\"Lagre\"\nDiscard=\"Bryt av\"\nDisable=\"Deaktiver\"\nYes=\"Ja\"\nNo=\"Nei\"\nAdd=\"Legg til\"\nRemove=\"Fjern\"\nRename=\"Gje nytt namn\"\nInteract=\"Samhandle\"\nFilters=\"Filtre\"\nProperties=\"Eigenskapar\"\nMoveUp=\"Flytt opp\"\nMoveDown=\"Flytt ned\"\nSettings=\"Innstillingar\"\nDisplay=\"Skjerm\"\nName=\"Namn\"\nExit=\"Avslutt\"\nMixer=\"Lydmiksar\"\nBrowse=\"Bla gjennom\"\nDroppedFrames=\"Slopne bilete %1 (%2%)\"\nClear=\"Tøm\"\nShow=\"Vis\"\nHide=\"Skjul\"\nUnhideAll=\"Vis alle\"\nUntitled=\"Utan namn\"\nNew=\"Ny\"\nDuplicate=\"Dupliser\"\nEnable=\"Aktiver\"\nDisableOSXVSync=\"Slå av macOS V-Sync\"\nResetOSXVSyncOnExit=\"Tilbakestill macOS V-Sync når du avsluttar\"\nTransition=\"Overgang\"\nQuickTransitions=\"Raske overgangar\"\nFadeToBlack=\"Falme til svart\"\nLeft=\"Venstre\"\nRight=\"Høgre\"\nTop=\"Topp\"\nBottom=\"Botn\"\nReset=\"Attendeset\"\nHours=\"Timar\"\nMinutes=\"Minutt\"\nSeconds=\"Sekund\"\nImport=\"Innfør\"\nExport=\"Utfør\"\nCopy=\"Kopier\"\nPaste=\"Lim inn\"\nNext=\"Neste\"\nBack=\"Attende\"\nDefaults=\"Standardar\"\nHideMixer=\"Skjul i miksar\"\nNone=\"Ingen\"\nStudioMode.Preview=\"Førehandsvising\"\nStudioMode.PreviewSceneName=\"Førehandsvising: %1\"\nShowInMultiview=\"Vis i fleirvisinga\"\nVerticalLayout=\"Loddrett oppsett\"\nGroup=\"Gruppe\"\nDoNotShowAgain=\"Ikkje syn att\"\nDefault=\"(Standard)\"\nCalculating=\"Reknar ut...\"\nFullscreen=\"Fullskjerm\"\nRefreshBrowser=\"Oppdater\"\nShowOnStartup=\"Vis ved igangsetjing\"\nOpenFile=\"Opne fil\"\nAddSource=\"Legg til kjelde\"\nAutomatic=\"Automatisk\"\nAlreadyRunning.Title=\"OBS køyrer allereie\"\nAuth.Chat=\"Chatt\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Other=\"Andre stader\"\nBasic.AutoConfig.ApplySettings=\"Bruk innstillingane\"\nBasic.AutoConfig.StartPage=\"Bruksinformasjon\"\nBasic.AutoConfig.VideoPage=\"Videoinnstillingar\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Bruk noverande (%1)\"\nBasic.AutoConfig.StreamPage=\"Straum-informasjon\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Ver snill å skriv inn din straum-informasjon\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Meir info\"\nBasic.AutoConfig.StreamPage.Service=\"Teneste\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Vis alle...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Tilpassa...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Straumelykel\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Straumeåtvaring\"\nBasic.AutoConfig.TestPage=\"Endeleg resultat\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Kopler til: %1...\"\nBasic.Stats.Output.Stream=\"Straum\"\nBasic.Stats.Output.Recording=\"Opptak\"\nBasic.Stats.Status.Recording=\"Opptak\"\nBasic.Stats.Status.Inactive=\"Inaktiv\"\nBasic.Stats.Status.Active=\"Aktiv\"\nBasic.Stats.Bitrate=\"Bitfrekvens\"\nUpdater.UpdateNow=\"Oppdater no\"\nUpdater.RemindMeLater=\"Minn meg på det seinare\"\nUpdater.Skip=\"Hopp over denne versjonen\"\nUpdater.NoUpdatesAvailable.Title=\"Ingen oppdateringar tilgjengeleg\"\nUpdater.BranchNotFound.Title=\"Oppdateringskanal fjerna\"\nBasic.SceneTransitions=\"Sceneovergangar\"\nBasic.TransitionDuration=\"Tidslengd\"\nBasic.TogglePreviewProgramMode=\"Studiomodus\"\nUndo.Undo=\"Angre\"\nUndo.Redo=\"Gjer om\"\nUndo.Add=\"Legg til '%1'\"\nUndo.Delete=\"Slett '%1'\"\nUndo.SceneCollection.Switch=\"Byt til '%1'\"\nUndo.Item.Undo=\"Angre %1\"\nUndo.Item.Redo=\"Gjør om %1\"\nUndo.Sources.Multi=\"Slett %1 kjelder\"\nUndo.Volume.Mute=\"Demp '%1'\"\nUndo.Volume.Unmute=\"Ikkje demp '%1'\"\nTransitionNameDlg.Title=\"Overgangsnamn\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Scener\"\nNameExists.Title=\"Namnet finns allereie\"\nNameExists.Text=\"Namnet er allereie i bruk.\"\nNoNameEntered.Title=\"Ver snill å skriv inn eit gyldig namn\"\nNoNameEntered.Text=\"Du kan ikkje bruke tomme namn.\"\nConfirmStart.Title=\"Starting av straum?\"\nConfirmStop.Title=\"Stoppe straumen?\"\nConfirmStopRecord.Title=\"Stoppe opptak?\"\nConfirmBWTest.Title=\"Starte bandbreiddetest?\"\nConfirmRemove.Title=\"Stadfest fjerning\"\nRemux.SourceFile=\"OBS-opptak\"\nRemux.TargetFile=\"Målfil\"\nRemux.SelectTarget=\"Vel målfil...\"\nRemux.FileExistsTitle=\"Målfila finst frå før\"\nMissingFiles=\"Manglande filer\"\nMissingFiles.MissingFile=\"Manglande fil\"\nMissingFiles.NewFile=\"Ny fil\"\nMissingFiles.SelectFile=\"Vel fil...\"\nMissingFiles.SelectDir=\"Vel mappe å søkje i\"\nMissingFiles.Missing=\"Manglar\"\nMissingFiles.Replaced=\"Erstatta\"\nMissingFiles.Cleared=\"Fjerna\"\nMissingFiles.Found=\"Funne\"\nMacPermissions.AccessGranted=\"Tilgang innvilga\"\nMacPermissions.RequestAccess=\"Be om tilgang\"\nMacPermissions.Item.ScreenRecording=\"Skjerminnspeling\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Accessibility=\"Tilgjenge\"\nMacPermissions.Continue=\"Hald fram\"\nBasic.DesktopDevice1=\"Skrivebordslyd\"\nBasic.AuxDevice1=\"Mikrofon/Aux\"\nBasic.AuxDevice2=\"Mikrofon/Aux 2\"\nBasic.AuxDevice3=\"Mikrofon/Aux 3\"\nBasic.AuxDevice4=\"Mikrofon/Aux 4\"\nBlendingMethod.Default=\"Standard\"\nBlendingMode.Additive=\"Legg til\"\nBlendingMode.Screen=\"Skjerm\"\nAddProfile.Title=\"Legg til profil\"\nBasic.SourceSelect.CreateNew=\"Opprett ny(tt)\"\nBasic.PropertiesWindow.AddFiles=\"Legg til filer\"\nBasic.Filters=\"Filter\"\nBasic.Filters.AsyncFilters=\"Lyd/video-filter\"\nBasic.Filters.AudioFilters=\"Lydfilter\"\nBasic.Filters.EffectFilters=\"Effektfilter\"\nBasic.Filters.AddFilter.Title=\"Filternamn\"\nBasic.TransformWindow.Width=\"Breidde\"\nBasic.TransformWindow.Height=\"Høgde\"\nBasic.TransformWindow.Crop=\"Skjer til\"\nBasic.Main.Scenes=\"Scener\"\nBasic.Main.Sources=\"Kjelder\"\nBasic.Main.Source=\"Kjelde\"\nBasic.Main.Controls=\"Kontrollere\"\nBasic.Main.PreparingStream=\"Førebur...\"\nBasic.Main.Connecting=\"Koplar til...\"\nBasic.Main.StartRecording=\"Byrje å ta opp\"\nBasic.Main.StartStreaming=\"Byrje å strøyme\"\nBasic.MainMenu.File=\"&Fil\"\nBasic.MainMenu.File.Export=\"&Eksporter\"\nBasic.MainMenu.File.Import=\"&Importer\"\nBasic.MainMenu.File.Settings=\"Inn&stillingar\"\nBasic.MainMenu.File.Exit=\"Avslutt (&X)\"\nBasic.MainMenu.Edit=\"R&ediger\"\nBasic.MainMenu.Edit.Undo=\"Angre (&U)\"\nBasic.MainMenu.Edit.Redo=\"Omgje&re\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Flytt &opp\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Flytt &ned\"\nBasic.MainMenu.View=\"&Vis\"\nBasic.MainMenu.View.Toolbars=\"Verk&tøyslinjar\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Importer profil\"\nBasic.MainMenu.Profile.Export=\"Eksporter profil\"\nBasic.MainMenu.Tools=\"Verk&tøy\"\nBasic.MainMenu.Help=\"&Hjelp\"\nBasic.MainMenu.Help.WhatsNew=\"Kva er nytt\"\nBasic.MainMenu.Help.About=\"&Om\"\nBasic.Settings.General=\"Generelt\"\nBasic.Settings.General.Language=\"Språk\"\nBasic.Settings.General.Updater=\"Oppdateringar\"\nBasic.Settings.General.UpdateChannel=\"Oppdateringskanal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Avslått)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Standard)\"\nBasic.Settings.General.Preview=\"Førehandsvising\"\nBasic.Settings.General.ChannelName.stable=\"Stabil\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Stream=\"Straum\"\nBasic.Settings.Stream.Custom.Username=\"Brukarnamn\"\nBasic.Settings.Stream.Custom.Password=\"Passord\"\nBasic.Settings.Stream.TTVAddon.None=\"Ingen\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Opne innstillingar\"\nBasic.Settings.Output.Encoder.Video=\"Videokodar\"\nBasic.Settings.Output.Encoder.Audio=\"Lydkodar\"\nBasic.Settings.Output.Mode.Simple=\"Enkel\"\nBasic.Settings.Output.Mode.Adv=\"Avansert\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (standard)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Oppløysing: %1\"\nBasic.Settings.Output.Adv.AudioTrack=\"Lydspor\"\nBasic.Settings.Output.Adv.Streaming=\"Strøyming\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Innstillingar for strøyming\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Spor 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Spor 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Spor 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Spor 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Spor 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Spor 6\"\nBasic.Settings.Output.Adv.Recording=\"Opptak\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Innstillingar for opptak\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Opptakstype\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Alle filer\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Lyd\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Standardformat\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Standardkodar\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Deaktiver kodar\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Videokodar\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Lydkodar\"\nFilenameFormatting.TT.mm=\"Minutt (00-59)\"\nFilenameFormatting.TT.M=\"Minutt (00-59)\"\nFilenameFormatting.TT.Y=\"År\"\nFilenameFormatting.TT.VF=\"Videoformat\"\nBasic.Settings.Audio=\"Lyd\"\nBasic.Settings.Audio.SampleRate=\"Samplingsfrekvens\"\nBasic.Settings.Audio.Channels=\"Kanalar\"\nBasic.Settings.Audio.Meters=\"Meter\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rask\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Langsam (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Peak-metertype\"\nBasic.Settings.Audio.DesktopDevice=\"Skrivebordslyd\"\nBasic.Settings.Audio.DesktopDevice2=\"Skrivebordslyd 2\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Standard\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Tilpassa\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Høg\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Over normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Under normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inaktiv\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Fargeformat\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Avgrensa\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Standard\"\nBasic.Settings.Advanced.Network=\"Nettverk\"\nBasic.AdvAudio.Name=\"Namn\"\nBasic.Settings.Hotkeys=\"Hurtigtastar\"\nBasic.SystemTray.Show=\"Vis\"\nBasic.SystemTray.Hide=\"Skjul\"\nHotkeys.Insert=\"Set inn\"\nHotkeys.Delete=\"Slett\"\nHotkeys.Home=\"Heim\"\nHotkeys.End=\"Slutt\"\nHotkeys.Left=\"Venstre\"\nHotkeys.Right=\"Høgre\"\nHotkeys.Menu=\"Meny\"\nAbout=\"Om\"\nAbout.License=\"Lisens\"\nYouTube.Actions.Privacy.Private=\"Privat\"\nYouTube.Actions.Privacy.Public=\"Offentleg\"\nYouTube.Actions.Category=\"Kategori\"\nYouTube.Actions.Thumbnail.ClearFile=\"Tøm\"\nYouTube.Actions.Latency=\"Forseinking\"\nYouTube.Actions.EnableDVR=\"Aktiver DVR\"\nYouTube.Actions.EventCreated.Title=\"Hending oppretta\"\n"
  },
  {
    "path": "frontend/data/locale/oc-FR.ini",
    "content": "Language=\"Occitan\"\nOK=\"D’acòrdi\"\nApply=\"Aplicar\"\nCancel=\"Anullar\"\nClose=\"Tampar\"\nSave=\"Enregistrar\"\nDiscard=\"Ignorar\"\nDisable=\"Desactivar\"\nYes=\"Òc\"\nNo=\"Non\"\nAdd=\"Apondre\"\nRemove=\"Suprimir\"\nRename=\"Renomenar\"\nFilters=\"Filtres\"\nProperties=\"Proprietats\"\nMoveUp=\"Montar\"\nMoveDown=\"Baissar\"\nSettings=\"Paramètres\"\nDisplay=\"Afichatge\"\nName=\"Nom\"\nExit=\"Sortir\"\nMixer=\"Mixador àudio\"\nBrowse=\"Percórrer\"\nClear=\"Escafar\"\nShow=\"Mostrar\"\nHide=\"Amagar\"\nUntitled=\"Sens títol\"\nNew=\"Novèl\"\nDuplicate=\"Duplicar\"\nEnable=\"Activar\"\nTransition=\"Transicion\"\nLeft=\"Esquèrra\"\nRight=\"Drecha\"\nTop=\"Naut\"\nBottom=\"Bas\"\nReset=\"Reïnicializar\"\nHours=\"Oras\"\nMinutes=\"Minutas\"\nSeconds=\"Segondas\"\nImport=\"Importar\"\nExport=\"Exportar\"\nCopy=\"Copiar\"\nPaste=\"Pegar\"\nNext=\"Seguent\"\nBack=\"Precedent\"\nDefaults=\"Predefinits\"\nNone=\"Pas cap\"\nStudioMode.Preview=\"Apercebut\"\nStudioMode.Program=\"Programa\"\nGroup=\"Grop\"\nDoNotShowAgain=\"Mostrar pas mai\"\nDefault=\"(per defaut)\"\nRefreshBrowser=\"Actualizar\"\nLockVolume=\"Verrolhar volum\"\nOpenFile=\"Dobrir fichièr\"\nAlreadyRunning.Title=\"OBS es ja lançat\"\nCopy.Filters=\"Copiar los filtres\"\nPaste.Filters=\"Pegar los filtres\"\nBandwidthTest.Region.US=\"Estats units\"\nBandwidthTest.Region.EU=\"Euròpa\"\nBandwidthTest.Region.Other=\"Autre\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Utilizarai pas que la camèra virtuala\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Afichatge %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Utilizar l’actual (%1)\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Mai d‘informacions\"\nBasic.AutoConfig.StreamPage.Service=\"Servici\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Personalizat...\"\nBasic.AutoConfig.StreamPage.Server=\"Servidor\"\nBasic.Stats=\"Estat\"\nBasic.Stats.Output.Recording=\"Enregistrament\"\nBasic.Stats.Status=\"Estat\"\nBasic.Stats.Status.Recording=\"Enregistrament\"\nBasic.Stats.Status.Live=\"DIRÈCTE\"\nBasic.Stats.Status.Reconnecting=\"Reconnexion\"\nBasic.Stats.Status.Inactive=\"Inactiu\"\nBasic.Stats.Status.Active=\"Actiu\"\nUpdater.Title=\"Novèla mesa a jorn disponibla\"\nUpdater.UpdateNow=\"Metre a jorn ara\"\nQuickTransitions.DuplicateScene=\"Duplicar la scèna\"\nQuickTransitions.EditProperties=\"Duplicar la font\"\nBasic.SceneTransitions=\"Transicions de scèna\"\nBasic.TransitionDuration=\"Durada\"\nBasic.TogglePreviewProgramMode=\"Mòde studio\"\nUndo.Sources.Multi=\"Suprimir %1 fonts\"\nTitleBar.Profile=\"Perfil\"\nTitleBar.Scenes=\"Scènas\"\nConfirmStart.Title=\"Començar la difusion ?\"\nBasic.Scene=\"Scèna\"\nVolControl.Mute=\"Amudir « %1 »\"\nVolControl.Properties=\"Proprietats de « %1 »\"\nBasic.Main.AddSceneDlg.Title=\"Apondre scèna\"\nBasic.Main.DefaultSceneName.Text=\"Scèna %1\"\nBasic.SourceSelect=\"Crear/Seleccionar font\"\nBasic.SourceSelect.AddVisible=\"Rendre la font visibla\"\nBasic.Main.Sources.Visibility=\"Visibilitat\"\nBasic.Main.Sources.Lock=\"Verrolhar\"\nBasic.PropertiesWindow=\"Proprietats de « %1 »\"\nBasic.PropertiesWindow.SelectColor=\"Seleccionar una color\"\nBasic.PropertiesWindow.SelectFont=\"Seleccionar una poliça\"\nBasic.PropertiesWindow.ConfirmTitle=\"Paramètres cambiats\"\nBasic.PropertiesWindow.AddFiles=\"Apondre de fichièrs\"\nBasic.PropertiesWindow.AddDir=\"Apondre un repertòri\"\nBasic.Filters=\"Filtres\"\nBasic.Filters.AsyncFilters=\"Filtres àudio/vidèo\"\nBasic.Filters.AudioFilters=\"Filtres àudio\"\nBasic.Filters.EffectFilters=\"Filtres d’efèit\"\nBasic.Filters.AddFilter.Title=\"Nom del filtre\"\nBasic.TransformWindow.Position=\"Posicion\"\nBasic.TransformWindow.PositionX=\"Posicion X\"\nBasic.TransformWindow.PositionY=\"Posicion Y\"\nBasic.TransformWindow.Rotation=\"Rotacion\"\nBasic.TransformWindow.Size=\"Talha\"\nBasic.TransformWindow.Width=\"Largor\"\nBasic.TransformWindow.Height=\"Nautor\"\nBasic.Main.AddSourceHelp.Title=\"Apondon impossible de la font\"\nBasic.Main.Scenes=\"Scènas\"\nBasic.Main.Sources=\"Fonts\"\nBasic.Main.Source=\"Font\"\nBasic.Main.Controls=\"Contraròtles\"\nBasic.Main.StartRecording=\"Començar l'enregistrament\"\nBasic.Main.StartStreaming=\"Començar la difusion\"\nBasic.Main.StartVirtualCam=\"Aviar la camèra virtuala\"\nBasic.MainMenu.File=\"&Fichièr\"\nBasic.MainMenu.File.Settings=\"&Paramètres\"\nBasic.MainMenu.Edit=\"&Edicion\"\nBasic.MainMenu.View=\"&Afichatge\"\nBasic.MainMenu.View.Toolbars=\"&Barra d’aisinas\"\nBasic.MainMenu.View.ContextBar=\"Barra d’aisina font\"\nBasic.MainMenu.Profile=\"&Perfil\"\nBasic.MainMenu.Help=\"&Ajuda\"\nBasic.MainMenu.Help.About=\"&A prepaus\"\nBasic.Settings.General.Language=\"Lenga\"\nBasic.Settings.General.Preview=\"Apercebut\"\nBasic.Settings.Stream.Custom.Username=\"Nom d’utilizaire\"\nBasic.Settings.Stream.Custom.Password=\"Senhals\"\nBasic.Settings.Output.Mode.Adv=\"Avançat\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Pista 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Pista 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Pista 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Pista 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Pista 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Pista 6\"\nBasic.Settings.Output.Adv.Recording=\"Enregistrament\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tipe d’enregistrament\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tipe\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Àudio\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Vidèo\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Format per defaut\"\nScreenshot.Source=\"Captura d’ecran (font)\"\nBasic.Settings.Video=\"Vidèo\"\nBasic.Settings.Audio=\"Àudio\"\nBasic.Settings.Audio.Channels=\"Canals\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Durada\"\nBasic.Settings.Advanced.Network=\"Ret\"\nBasic.AdvAudio.Name=\"Nom\"\nBasic.AdvAudio.Volume=\"Volum\"\nBasic.AdvAudio.VolumeSource=\"Volum per « %1 »\"\nBasic.AdvAudio.AudioTracks=\"Pistas\"\nBasic.SystemTray.Show=\"Mostrar\"\nBasic.SystemTray.Hide=\"Amagar\"\nHotkeys.Insert=\"Inserir\"\nHotkeys.Delete=\"Suprimir\"\nHotkeys.Home=\"Acuèlh\"\nHotkeys.End=\"Fin\"\nHotkeys.Print=\"Imprimir\"\nMute=\"Amudir\"\nSceneItemShow=\"Afichar « %1 »\"\nSceneItemHide=\"Rescondre « %1 »\"\nFinalScene.Title=\"Suprimir scèna\"\nNoSources.Title=\"Cap de font\"\nNoSources.Label=\"Avètz pas cap de font.\\nClicatz lo boton + çai-jos,\\no clic-drech aicí per n’apondre una.\"\nAbout=\"A prepaus\"\nAbout.GetInvolved=\"Implicatz-vos\"\nAbout.Authors=\"Autors\"\nAbout.License=\"Licéncia\"\nContextBar.NoSelectedSource=\"Cap de font pas seleccionada\"\n"
  },
  {
    "path": "frontend/data/locale/pa-IN.ini",
    "content": "Language=\"ਪੰਜਾਬੀ\"\nOK=\"ਠੀਕ ਹੈ\"\nApply=\"ਲਾਗੂ ਕਰੋ\"\nCancel=\"ਰੱਦ ਕਰੋ\"\nClose=\"ਬੰਦ ਕਰੋ\"\nSave=\"ਰੱਖਓ\"\nDiscard=\"ਛੱਡੋ\"\nDisable=\"ਬੰਦ ਕਰੋ\"\nYes=\"ਹਾਂਜੀ\"\nNo=\"ਨਹੀਂ\"\nAdd=\"ਸ਼ਾਮਿਲ ਕਰੋ\"\nRemove=\"ਮਿਟਾਓ\"\nRename=\"ਨਾਮ ਬਦਲੋ\"\nInteract=\"ਅਸਰ ਕਰੋ\"\nFilters=\"ਫਿਲਟਰ\"\nProperties=\"ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ\"\nMoveUp=\"ਉੱਪਰ ਕਰੋ\"\nMoveDown=\"ਹੇਠਾਂ ਕਰੋ\"\nSettings=\"ਸੈਟਿੰਗ\"\nDisplay=\"ਸਕ੍ਰੀਨ\"\nName=\"ਨਾਮ\"\nExit=\"ਬੰਦ ਕਰੋ\"\nMixer=\"ਆਵਾਜ਼ ਮਿਕਸਰ\"\nBrowse=\"ਖੋਜ ਕਰੋ\"\nMono=\"ਮੋਨੋ - ਕਲਾ ਇੱਕ ਟਰੈਕ\"\nStereo=\"ਸਟੀਰੀਓ - ਦੋ ਟਰੈਕ\"\nDroppedFrames=\"ਖੁੰਝ ਗਏ ਫਰੇਮ %1 (%2%)\"\nClear=\"ਹਟਾਓ\"\nRevert=\"ਵਾਪਸ\"\nShow=\"ਦੇਖੋ\"\nHide=\"ਸ਼ਪਾਓ\"\nUnhideAll=\"ਸਾਰੇ ਦਖਾਓ\"\nUntitled=\"ਸਿਰਲੇਖ-ਰਹਿਤ\"\nNew=\"ਨਵਾਂ\"\nDuplicate=\"ਨਕਲ\"\nEnable=\"ਚਾਲੂ\"\nDisableOSXVSync=\"macOS V-Sync ਨੂੰ ਬੰਦ ਕਰੋ\"\nResetOSXVSyncOnExit=\"ਪ੍ਰੋਗਰਾਮ ਬੰਦ ਹੋਣ ਤੇ macOS V-Sync ਨੂੰ ਫਿਰ ਚਾਲੂ\"\nHighResourceUsage=\"ਇਨਕੋਡਰ ਜਿਆਦਾ ਬੋਝ ਤੇ ਹੈ! ਵੀਡੀਓ ਸੈਟਿੰਗ ਨੂੰ ਘਟਾਓਨ ਜਾਂ ਤੇਜ ਪ੍ਰੀਸੈਟ ਵਰਤਣ ਬਾਰੇ ਵੀਚਾਰੋ!\"\nTransition=\"ਸੀਨ ਬਦਲੋ\"\nQuickTransitions=\"ਤੇਜੀ ਨਾਲ ਤਬਦੀਲ ਕਰੋ\"\nFadeToBlack=\"ਕਾਲੀ ਸਕ੍ਰੀਨ ਦਖਾਓ\"\nLeft=\"ਖਬਾ\"\nRight=\"ਸਜਾ\"\nTop=\"ਉਤੇ\"\nBottom=\"ਥਲੇ\"\nReset=\"ਮਿਟਾਓ\"\nHours=\"ਘੰਟੇ\"\nMinutes=\"ਮਿੰਟ\"\nSeconds=\"ਸਕਿੰਟ\"\nDeprecated=\"ਪੁਰਾਣੇ\"\nImport=\"ਆਯਾਤ\"\nExport=\"ਨਿਰਯਾਤ\"\nCopy=\"ਕਾਪੀ ਕਰੋ\"\nPaste=\"ਚੇਪੋ\"\nPasteDuplicate=\"ਚੇਪ (ਦੂਪਲੀਕਟ)\"\nNext=\"ਅਗੇ\"\nBack=\"ਪਿਸ਼ੇ\"\nDefaults=\"ਮੂਲ\"\nHideMixer=\"ਮਿਕਸਰ ਵਿੱਚ ਲੁਕੋ\"\nNone=\"ਕੋਈ ਨਹੀਂ\"\nStudioMode.Preview=\"ਪ੍ਰੀਵਿਊ\"\nStudioMode.Program=\"ਪ੍ਰੋਗਰਾਮ\"\nShowInMultiview=\"ਮੁਲਟੀ ਵੀਓ ਦੇ ਵਿੱਚ ਦਖਓ\"\nGroup=\"ਗਰੁੱਪ\"\nDoNotShowAgain=\"ਇਸ ਨੂੰ ਦੁਬਾਰਾ ਨਾ ਦਿਖਾਓ\"\nDefault=\"(ਮੂਲ)\"\nCalculating=\"ਗਿਣਤੀ ਹੋ ਰਹੀ ਹੈ...\"\nFullscreen=\"ਪੂਰੀ ਸਕ੍ਰੀਨ\"\nWindowed=\"ਵਿੰਡੋ\"\nAlreadyRunning.Title=\"OBS ਪਹਿਲਾਂ ਹੀ ਚੱਲ ਰਿਹਾ ਹੈ\"\nBandwidthTest.Region=\"ਜ੍ਗਾ\"\nBandwidthTest.Region.US=\"ਅਮਰੀਕਾ\"\nBandwidthTest.Region.EU=\"ਯੋਰਪ\"\nBandwidthTest.Region.Asia=\"ਏਸ਼ੀਆ\"\nBandwidthTest.Region.Other=\"ਹੋਰ\"\nBasic.AutoConfig.ApplySettings=\"ਲਾਗੂ ਕਰੋ\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"ਮੌਜੂਦਾ ਵਰਤੋ (%1x%2)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"ਮੌਜੂਦਾ ਵਰਤੋ (%1)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"ਇਹ ਖਾਤਾ ਡਿਸਕਨੈਕਟ ਕਰੋ\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"ਇਹ ਖਾਤਾ ਡਿਸਕਨੈਕਟ ਕਰਨਾ ਹੈ?\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"ਸਾਰੇ ਦਿਖਾਓ...\"\nBasic.AutoConfig.TestPage=\"ਆਖਰੀ ਨਤੀਜੇ\"\nBasic.Stats=\"ਅੰਕੜੇ\"\nBasic.Stats.CPUUsage=\"CPU ਦੀ ਵਰਤੋਂ\"\nBasic.Stats.Output.Recording=\"ਰਿਕਾਰਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ\"\nBasic.Stats.Status.Recording=\"ਰਿਕਾਰਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ\"\nBasic.Stats.Status.Live=\"ਲਾਈਵ\"\nUpdater.Title=\"ਨਵੇਂ ਅਪਡੇਟ ਉਪਲਬਧ ਹਨ!\"\nUpdater.UpdateNow=\"ਹੁਣ ਅਪਡੇਟ ਕਰੋ\"\n"
  },
  {
    "path": "frontend/data/locale/pl-PL.ini",
    "content": "Language=\"polski\"\nApply=\"Zastosuj\"\nCancel=\"Anuluj\"\nClose=\"Zamknij\"\nSave=\"Zapisz\"\nDiscard=\"Odrzuć\"\nDisable=\"Wyłącz\"\nYes=\"Tak\"\nNo=\"Nie\"\nAdd=\"Dodaj\"\nRemove=\"Usuń\"\nRename=\"Zmień nazwę\"\nInteract=\"Włącz interakcję\"\nFilters=\"Filtry\"\nProperties=\"Właściwości\"\nMoveUp=\"Przenieś w górę\"\nMoveDown=\"Przenieś w dół\"\nSettings=\"Ustawienia\"\nDisplay=\"Ekran\"\nName=\"Nazwa\"\nExit=\"Zamknij\"\nMixer=\"Mikser dźwięku\"\nBrowse=\"Przeglądaj\"\nDroppedFrames=\"Zgubione klatki %1 (%2%)\"\nProjector.Open.Program=\"Otwórz projektor programu\"\nProjector.Open.Preview=\"Otwórz projektor podglądu\"\nProjector.Open.Scene=\"Otwórz projektor scen\"\nProjector.Open.Source=\"Otwórz projektor źródeł\"\nProjector.Open.Multiview=\"Otwórz Multiview\"\nProjector.Display=\"Monitor: %1\"\nProjector.Window=\"Nowe okno\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Scena: %1\"\nProjector.Title.Source=\"Źródło: %1\"\nProjector.ResizeWindowToContent=\"Dopasuj okno do zawartości\"\nClear=\"Wyczyść\"\nRevert=\"Przywróć\"\nShow=\"Pokaż\"\nHide=\"Ukryj\"\nUnhideAll=\"Pokaż wszystkie\"\nUntitled=\"Bez tytułu\"\nNew=\"Nowy\"\nDuplicate=\"Utwórz duplikat\"\nEnable=\"Włącz\"\nDisableOSXVSync=\"Wyłącz synchronizację pionową macOS\"\nResetOSXVSyncOnExit=\"Przywróć synchronizację pionową macOS po zamknięciu aplikacji\"\nHighResourceUsage=\"Kodowanie przeciążone! Dostosuj ustawienia nagrywania lub użyj szybszego trybu kodowania.\"\nTransition=\"Przejście\"\nQuickTransitions=\"Szybkie Przejścia\"\nFadeToBlack=\"Zanikanie do czerni\"\nLeft=\"Od lewej\"\nRight=\"Od prawej\"\nTop=\"Od góry\"\nBottom=\"Od dołu\"\nReset=\"Resetuj\"\nHours=\"Godziny\"\nMinutes=\"Minuty\"\nSeconds=\"Sekundy\"\nDeprecated=\"Wycofywane\"\nReplayBuffer=\"Nagrywanie powtórek\"\nImport=\"Importuj\"\nExport=\"Eksportuj\"\nCopy=\"Kopiuj\"\nPaste=\"Wklej\"\nPasteReference=\"Wklej (odniesienie)\"\nPasteDuplicate=\"Wklej (duplikat)\"\nRemuxRecordings=\"Przepakuj nagrania\"\nNext=\"Dalej\"\nBack=\"Wstecz\"\nDefaults=\"Domyślne\"\nRestoreDefaults=\"Domyślne\"\nHideMixer=\"Ukryj w mikserze\"\nTransitionOverride=\"Nadpisywanie efektu przejścia\"\nShowTransition=\"Pokaż efekt przejścia\"\nHideTransition=\"Ukryj efekt przejścia\"\nNone=\"Brak\"\nStudioMode.Preview=\"Podgląd\"\nStudioMode.Program=\"Na żywo\"\nStudioMode.PreviewSceneName=\"Podgląd: %1\"\nStudioMode.ProgramSceneName=\"Na żywo: %1\"\nShowInMultiview=\"Pokaż w Multiview\"\nVerticalLayout=\"Układ pionowy\"\nGroup=\"Grupa\"\nDoNotShowAgain=\"Nie pokazuj ponownie\"\nDefault=\"(Domyślnie)\"\nCalculating=\"Obliczanie...\"\nFullscreen=\"Pełny ekran\"\nWindowed=\"W oknie\"\nRefreshBrowser=\"Odśwież\"\nAspectRatio=\"Współczynnik proporcji <b>%1:%2</b>\"\nLockVolume=\"Zablokuj głośność\"\nLogViewer=\"Podgląd logów\"\nShowOnStartup=\"Pokaż przy starcie\"\nOpenFile=\"Otwórz plik\"\nAddScene=\"Dodaj scenę\"\nAddSource=\"Dodaj źródło\"\nRemoveScene=\"Usuń zaznaczoną scenę\"\nRemoveSource=\"Usuń zaznaczone źródła\"\nMoveSceneUp=\"Przenieś scenę do góry\"\nMoveSceneDown=\"Przenieś scenę w dół\"\nMoveSourceUp=\"Przenieś źródła do góry\"\nMoveSourceDown=\"Przenieś źródła w dół\"\nSourceProperties=\"Otwórz właściwości źródła\"\nSourceFilters=\"Otwórz filtry źródła\"\nMixerToolbarMenu=\"Menu miksera audio\"\nSceneFilters=\"Otwórz filtry scen\"\nList=\"Lista\"\nGrid=\"Siatka\"\nAutomatic=\"Automatycznie\"\nComingSoon=\"Już wkrótce\"\nPluginsFailedToLoad.Title=\"Błąd ładowania wtyczki\"\nPluginsFailedToLoad.Text=\"Poniższe wtyczki OBS nie załadowały się:\\n\\n%1\\n Proszę zaktualizuj lub usuń te wtyczki.\"\nAlreadyRunning.Title=\"OBS jest już uruchomiony\"\nAlreadyRunning.Text=\"OBS jest już uruchomiony! Sprawdź wszystkie uruchomione wystąpienia OBS zanim uruchomisz go jeszcze raz. Jeżeli OBS jest zminimalizowany do zasobnika systemowego, sprawdź czy nie jest uruchomiony także w tym miejscu.\"\nAlreadyRunning.LaunchAnyway=\"Uruchom mimo to\"\nCrashHandling.Dialog.Title=\"Wykryto awarię OBS Studio\"\nCrashHandling.Labels.Text=\"Program OBS Studio nie został poprawnie wyłączony.\\n\\nCzy uruchomić w trybie awaryjnym (z wyłączonymi wtyczkami innych dostawców, skryptami i WebSockets)?\"\nCrashHandling.Labels.PrivacyNotice=\"Możesz również ustawić automatyczne przesyłanie najnowszego raportu o awariach do OBSProject.<br /><br />Przed przesłaniem plików zapoznaj się z <a href='https://obsproject.com/privacy-policy'>polityką prywatności</a> i zwróć szczególną uwagę na części dotyczące przesyłania plików.\"\nCrashHandling.Checkbox.SendReport=\"Zapoznałem się z polityką prywatności i wyrażam zgodę na przesłanie.\"\nCrashHandling.Buttons.LaunchSafe=\"Uruchom w trybie awaryjnym\"\nCrashHandling.Buttons.LaunchNormal=\"Uruchom w trybie normalnym\"\nCrashHandling.Errors.UploadJSONError=\"Wystąpił błąd podczas próby przesłania najnowszego dziennika awarii. Spróbuj ponownie później.\"\nCrashHandling.Errors.Title=\"Błąd przesyłania dziennika awarii\"\nSafeMode.Restart=\"Czy chcesz uruchomić ponownie OBS w trybie awaryjnym (wyłączone wtyczki, skrypty i websockety)?\"\nSafeMode.RestartNormal=\"Czy chcesz uruchomić OBS ponownie w normalnym trybie?\"\nChromeOS.Title=\"Platforma nieobsługiwana\"\nChromeOS.Text=\"OBS uruchomiony jest najprawdopodobniej jako kontener w systemie ChromeOS. Platforma ta nie jest oficjalnie obsługiwana.\"\nWine.Title=\"Wykryto Wine\"\nWine.Text=\"Uruchamianie OBS w Wine nie jest obsługiwane a wiele funkcji, takich jak przechwytywanie lub źródła urządzeń, nie będzie działać w ogóle lub w ograniczonym zakresie.<br><br>Zamiast tego zaleca się uruchomienie natywnej wersji OBS, na przykład <a href='https://flathub.org/apps/details/com.obsproject.Studio'>naszą wersję Flatpack</a> lub wersję z pakietów Twojego systemu operacyjnego.\"\nDockCloseWarning.Title=\"Zamykanie dokowalnego okna\"\nDockCloseWarning.Text=\"Zostało zamknięte dokowalne okno/panel. Jeżeli chce je ponownie pokazać, wybierz menu Panele z paska menu.\"\nExtraBrowsers=\"Panele przeglądarki\"\nExtraBrowsers.Info=\"Dodaj panele, określając ich nazwę oraz adres URL, a następnie kliknij Zastosuj lub Zamknij, aby otworzyć je. Panele można dodawać i usuwać w każdej chwili\"\nExtraBrowsers.DockName=\"Nazwa panelu\"\nAuth.Authing.Title=\"Uwierzytelnianie...\"\nAuth.Authing.Text=\"Uwierzytelnianie z %1, proszę czekać...\"\nAuth.AuthFailure.Title=\"Błąd uwierzytelnienia\"\nAuth.AuthFailure.Text=\"Nie można uwierzytelnić w %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Wymagane uwierzytelnienie\"\nAuth.InvalidScope.Text=\"Zmieniły się wymagania uwierzytelniania dla %1. Niektóre funkcje mogą nie być dostępne.\"\nAuth.LoadingChannel.Title=\"Trwa ładowanie informacji o kanale...\"\nAuth.LoadingChannel.Text=\"Ładowanie informacji o kanale z %1, proszę czekać...\"\nAuth.LoadingChannel.Error=\"Nie udało się pobrać informacji o kanale.\"\nAuth.ChannelFailure.Title=\"Nie udało się załadować informacji o kanale\"\nAuth.ChannelFailure.Text=\"Nie udało się załadować informacji o kanale w serwisie %1\\n\\n%2: %3\"\nAuth.Chat=\"Czat\"\nAuth.StreamInfo=\"Informacje o strumieniu\"\nTwitchAuth.Stats=\"Statystyki Twitch\"\nTwitchAuth.Feed=\"Lista aktywności Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Nie można pobrać klucza strumienia\"\nTwitchAuth.TwoFactorFail.Text=\"Powiązanie Twojego konta Twitch w OBS nie udało się. Sprawdź, czy masz włączone uwierzytelnianie dwuskładnikowe w <a href='https://www.twitch.tv/settings/security'>ustawieniach bezpieczeństwa Twitcha</a>, ponieważ jest to wymagane do nadawania na tej platformie.\"\nRestreamAuth.Channels=\"Kanały Restream\"\nCopy.Filters=\"Kopiuj filtry\"\nPaste.Filters=\"Wklej filtry\"\nBrowserPanelInit.Title=\"Inicjowanie przeglądarki...\"\nBrowserPanelInit.Text=\"Inicjowanie przeglądarki, proszę czekać...\"\nBandwidthTest.Region.US=\"Stany Zjednoczone\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Azja\"\nBandwidthTest.Region.Other=\"Inny\"\nBasic.AutoConfig=\"Kreator konfiguracji automatycznej\"\nBasic.AutoConfig.ApplySettings=\"Zastosuj ustawienia\"\nBasic.AutoConfig.StartPage=\"Sposób użytkowania\"\nBasic.AutoConfig.StartPage.SubTitle=\"Określ w jaki sposób chcesz używać aplikacji\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optymalizuj pod kątem przesyłania strumieniowego, nagrywanie lokalne jest mniej istotne\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optymalizuj pod nagrywanie lokalne, nie będę strumieniować\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Będę używać tylko wirtualnej kamery\"\nBasic.AutoConfig.VideoPage=\"Ustawienia wideo\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Określ wybrane ustawienia wideo, których chcesz użyć\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Użyj bieżącej (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Monitor %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Użyj bieżącego (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 lub 30 ale preferuj 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 lub 30 ale preferuj większą rozdzielczość\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Rozdzielczość bazowa niekoniecznie musi być rozdzielczością wynikowego nagrania lub streamu. Ta druga może być efektem skalowania w dół w celu zmniejszenia wymagań poziomu przepływności lub dostępności zasobów potrzebnych do kompresji.\"\nBasic.AutoConfig.StreamPage=\"Informacje o strumieniu\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Proszę podać informacje dotyczące transmisji\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Podłącz konto (zalecane)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Odłącz konto\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Odłączyć konto?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Ta zmiana zostanie zastosowana natychmiast. Czy na pewno chcesz odłączyć konto?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Pobierz klucz\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Więcej informacji\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Użyj klucza strumienia\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Użyj klucza strumienia (zaawansowane)\"\nBasic.AutoConfig.StreamPage.Service=\"Serwis\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Pokaż wszystkie...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Własny...\"\nBasic.AutoConfig.StreamPage.Server=\"Serwer\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Klucz strumienia\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: wprowadź hasło szyfrowania.\\nRTMP: wprowadź klucz używany przez usługę.\\nSRT: wprowadź streamid, jeśli usługa z takowego korzysta.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Klucz enkodera\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Token okaziciela\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Połączone konto\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Oszacuj bitrate przy pomocy testu prędkości łącza (może potrwać parę minut)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferuj enkodowanie sprzętowe\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Enkodowanie sprzętowe odciąża procesor ale może wymagać większej przepustowości dla osiągnięcia porównywalnej jakości.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Ostrzeżenie\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"W celu przetestowania transmisji na Twój kanał zostaną wysyłane losowo generowane dane wideo bez dźwięku. Jeżeli to możliwe, zaleca się na czas testu tymczasowo wyłączyć zapisywanie transmisji na serwerze i ustawienie jej widoczności na prywatną. Kontynuować?\"\nBasic.AutoConfig.TestPage=\"Wyniki końcowe\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Aplikacja przeprowadza obecnie serię testów w celu ustalenia najlepszych ustawień\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testowanie zakończone\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Wykonywanie testu przepustowości, może to potrwać kilka minut...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Nie znaleziono wyjścia dla protokołu tej usługi\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Łączenie z: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Nie można połączyć z żadnym serwerem. Sprawdź połączenie internetowe i spróbuj ponownie.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testowanie przepustowości dla: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testowanie enkodera strumienia, może to chwilę potrwać...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testowanie enkodera nagrywania, może to chwilę potrwać...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Uruchomienie enkodera nie powiodło się\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testuję %1x%2 %3 FPS (kl./s)...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Enkoder strumienia\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Enkoder nagrywania\"\nBasic.AutoConfig.TestPage.Result.Header=\"Aplikacja ustaliła poniższe ustawienia jako najbardziej idealne dla Ciebie:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Aby użyć tych ustawień, kliknij przycisk Zastosuj ustawienia. Aby ponownie skonfigurować kreatora i spróbować ponownie, kliknij przycisk Wstecz. Aby ręcznie skonfigurować ustawienia, kliknij przycisk Anuluj i otwórz Ustawienia.\"\nBasic.AutoConfig.Info=\"Kreator automatycznej konfiguracji określi najlepsze ustawienia w oparciu o specyfikację komputera i szybkość połączenia.\"\nBasic.AutoConfig.RunAnytime=\"Możesz to uruchomić w dowolnym momencie, przechodząc do menu Narzędzia.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Rozdzielczość streamu (skalowana)\"\nBasic.Stats=\"Statystyki\"\nBasic.Stats.CPUUsage=\"Użycie procesora\"\nBasic.Stats.HDDSpaceAvailable=\"Dostępne miejsce na dysku\"\nBasic.Stats.MemoryUsage=\"Wykorzystanie pamięci\"\nBasic.Stats.AverageTimeToRender=\"Średni czas renderowania klatki\"\nBasic.Stats.SkippedFrames=\"Pominięte klatki z powodu opóźnień enkodera\"\nBasic.Stats.MissedFrames=\"Pominięte klatki z powodu opóźnień renderowania\"\nBasic.Stats.Output.Stream=\"Transmisja\"\nBasic.Stats.Output.Recording=\"Nagrywanie\"\nBasic.Stats.Status.Recording=\"Nagrywanie\"\nBasic.Stats.Status.Live=\"NA ŻYWO\"\nBasic.Stats.Status.Reconnecting=\"Wznawianie połączenia\"\nBasic.Stats.Status.Inactive=\"Nieaktywne\"\nBasic.Stats.Status.Active=\"Aktywne\"\nBasic.Stats.DroppedFrames=\"Zgubione klatki (sieć)\"\nBasic.Stats.MegabytesSent=\"Całkowite dane wyjściowe\"\nBasic.Stats.Bitrate=\"Przepływność (bitrate)\"\nBasic.Stats.DiskFullIn=\"Dysk pełny za (około)\"\nBasic.Stats.DiskFullIn.Text=\"Godziny: %1, Minuty: %2\"\nBasic.Stats.ResetStats=\"Zresetuj statystyki\"\nResetUIWarning.Title=\"Czy na pewno chcesz zresetować wygląd aplikacji?\"\nResetUIWarning.Text=\"Przywrócenie domyślnego interfejsu ukryje dodatkowe panele. Aby były widoczne, należy przywrócić ich widoczność w menu Panele.\\n\\nCzy na pewno chcesz przywrócić domyślny interfejs?\"\nUpdater.Title=\"Dostępna jest nowa aktualizacja\"\nUpdater.Text=\"Dostępna jest nowa aktualizacja:\"\nUpdater.UpdateNow=\"Uaktualnij teraz\"\nUpdater.RemindMeLater=\"Przypomnij mi później\"\nUpdater.Skip=\"Pomiń wersję\"\nUpdater.NoUpdatesAvailable.Title=\"Brak dostępnych aktualizacji\"\nUpdater.NoUpdatesAvailable.Text=\"Brak dostępnych aktualizacji\"\nUpdater.BranchNotFound.Title=\"Kanał aktualizacji usunięty\"\nUpdater.BranchNotFound.Text=\"Twój wybrany kanał aktualizacji nie jest już dostępny. OBS został zresetowany do korzystania z domyślnego kanału aktualizacji.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Sprawdzanie integralności niedostępne\"\nUpdater.RepairButUpdatesAvailable.Text=\"Sprawdzanie integralności plików jest możliwe tylko dla najnowszej dostępnej wersji. Użyj Pomoc → Sprawdź aktualizacje, aby zweryfikować i zaktualizować Twoją instalację OBS.\"\nUpdater.RepairConfirm.Title=\"Potwierdź sprawdzenie integralności plików\"\nUpdater.RepairConfirm.Text=\"Rozpoczęcie sprawdzania integralności plików skanuje instalację OBS w celu wykrycia uszkodzeń i ponownie pobiera poprawne wersje uszkodzonych/zmodyfikowanych plików. To może zająć chwilę.\\n\\nCzy chcesz kontynuować?\"\nUpdater.FailedToLaunch=\"Nie udało się uruchomić aktualizacji\"\nQuickTransitions.SwapScenes=\"Zamień sceny Podglądu/Na żywo po przejściu\"\nQuickTransitions.SwapScenesTT=\"Zamienia sceny z \\\"Podglądu\\\" i \\\"Na żywo\\\" po wykonaniu przejścia, pod warunkiem, że oryginalne sceny z sekcji \\\"Na żywo\\\" ciągle istnieją.\\nNie przywróci to zmian, które zostały dokonane w oryginalnych scenach z \\\"Na żywo\\\".\"\nQuickTransitions.DuplicateScene=\"Duplikuj scenę\"\nQuickTransitions.DuplicateSceneTT=\"Edytując tę samą scenę, opcja ta pozwala na edycję przejść/widoczności źródeł bez modyfikowania oryginalnych scen z sekcji \\\"Na żywo\\\".\\nAby edytować właściwości źródeł bez modyfikacji wyjścia \\\"Na żywo\\\" włącz opcję \\\"Duplikuj źródła\\\".\\nZmiana tej opcji zresetuje sceny w sekcji \\\"Na żywo\\\", jeżeli takowe istnieją.\"\nQuickTransitions.EditProperties=\"Duplikuj źródła\"\nQuickTransitions.EditPropertiesTT=\"Edytując tę samą scenę, opcja ta pozwala na edycję źródeł bez modyfikacji wyjścia sekcji \\\"Na żywo\\\".\\nMożliwe to jest jedynie przy włączonej opcji 'Duplikuj scenę'.\\nNiektóre źródła (np. przechwytywanie obrazu lub źródła mediów) nie obsługują tej funkcjonalności i nie mogą być edytowane oddzielnie.\\nZmiana tej opcji zresetuje sceny sekcji \\\"Na żywo\\\", jeżeli takowe istnieją.\\n\\nUwaga: Ponieważ źródła są duplikowane, może to oznaczać zwiększenie obciążenia komputera lub procesora graficznego.\"\nQuickTransitions.HotkeyName=\"Szybkie przejście: %1\"\nBasic.AddTransition=\"Dodaj konfigurowalne przejście\"\nBasic.RemoveTransition=\"Usuń konfigurowalne przejście\"\nBasic.TransitionProperties=\"Właściwości przejścia\"\nBasic.SceneTransitions=\"Efekty przejścia scen\"\nBasic.TransitionDuration=\"Czas trwania\"\nBasic.TogglePreviewProgramMode=\"Tryb studia\"\nBasic.EnablePreviewProgramMode=\"Włącz tryb studia\"\nBasic.DisablePreviewProgramMode=\"Wyłącz tryb studia\"\nUndo.Undo=\"Cofnij\"\nUndo.Redo=\"Ponów\"\nUndo.Add=\"Dodaj '%1'\"\nUndo.Delete=\"Usuń '%1'\"\nUndo.Rename=\"Zmień nazwę '%1'\"\nUndo.SceneCollection.Switch=\"Przełącz na '%1'\"\nUndo.Item.Undo=\"Cofnij %1\"\nUndo.Item.Redo=\"Ponów %1\"\nUndo.Sources.Multi=\"Usuń źródła: %1\"\nUndo.Filters=\"Zmiany filtra w '%1'\"\nUndo.Filters.Paste.Single=\"Wklej filtr '%1' do '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopiuj filtry z '%1' do '%2'\"\nUndo.Transform=\"Przekształć źródło(a) w '%1'\"\nUndo.Transform.Paste=\"Wklej przekształcenie w '%1'\"\nUndo.Transform.Rotate=\"Obrót w '%1'\"\nUndo.Transform.Reset=\"Reset przekształceń w '%1'\"\nUndo.Transform.HFlip=\"Odwróć w poziomie w '%1'\"\nUndo.Transform.VFlip=\"Odwróć w pionie w '%1'\"\nUndo.Transform.FitToScreen=\"Dopasuj do ekranu w '%1'\"\nUndo.Transform.StretchToScreen=\"Rozciągnij do ekranu w '%1'\"\nUndo.Transform.Center=\"Wyśrodkuj na ekranie w '%1'\"\nUndo.Transform.VCenter=\"Wyśrodkuj w pionie w '%1'\"\nUndo.Transform.HCenter=\"Wyśrodkuj w poziomie w '%1'\"\nUndo.Volume.Change=\"Zmiana głośności na '%1'\"\nUndo.Volume.Mute=\"Wycisz '%1'\"\nUndo.Volume.Unmute=\"Wyłącz wyciszenie '%1'\"\nUndo.Balance.Change=\"Zmiana balansu dźwięku na '%1'\"\nUndo.SyncOffset.Change=\"Zmiana przesunięcia dźwięku na '%1'\"\nUndo.MonitoringType.Change=\"Zmiana monitorowania dźwięku na '%1'\"\nUndo.Mixers.Change=\"Zmiana w mikserze dźwięku na '%1'\"\nUndo.ForceMono.On=\"Włącz wymuszenie dźwięku mono na '%1'\"\nUndo.ForceMono.Off=\"Wyłącz wymuszenie dźwięku mono na '%1'\"\nUndo.Properties=\"Zmiana właściwości w '%1'\"\nUndo.Scene.Duplicate=\"Duplikuj scenę '%1'\"\nUndo.ShowTransition=\"Pokaż przejście na '%1'\"\nUndo.HideTransition=\"Ukryj przejście na '%1'\"\nUndo.ShowSceneItem=\"Pokaż '%1' w '%2'\"\nUndo.HideSceneItem=\"Ukryj '%1' w '%2'\"\nUndo.ReorderSources=\"Zmień kolejność źródeł w '%1'\"\nUndo.MoveUp=\"Przesuń '%1' w górę w '%2'\"\nUndo.MoveDown=\"Przesuń '%1' w dół w '%2'\"\nUndo.MoveToTop=\"Przesuń '%1' na górę w '%2'\"\nUndo.MoveToBottom=\"Przesuń '%1' na dół w '%2'\"\nUndo.PasteSource=\"Wklej źródło(a) w '%1'\"\nUndo.PasteSourceRef=\"Wklej odniesienie(a) do źródła w '%1'\"\nUndo.GroupItems=\"Zgrupuj w '%1'\"\nTransitionNameDlg.Text=\"Proszę podać nazwę przejścia\"\nTransitionNameDlg.Title=\"Nazwa przejścia\"\nTitleBar.SafeMode=\"TRYB AWARYJNY\"\nTitleBar.PortableMode=\"Tryb przenośny\"\nTitleBar.Scenes=\"Sceny\"\nNameExists.Title=\"Nazwa już istnieje\"\nNameExists.Text=\"Nazwa jest już używana.\"\nNoNameEntered.Title=\"Proszę wprowadzić prawidłową nazwę\"\nNoNameEntered.Text=\"Nazwa nie może być pusta.\"\nConfirmStart.Title=\"Uruchomić transmisję?\"\nConfirmStart.Text=\"Czy na pewno chcesz uruchomić transmisję?\"\nConfirmStop.Title=\"Zatrzymać transmisję?\"\nConfirmStop.Text=\"Czy na pewno chcesz uruchomić transmisję?\"\nConfirmStopRecord.Title=\"Zatrzymać nagrywanie?\"\nConfirmStopRecord.Text=\"Czy na pewno chcesz zatrzymać nagrywanie?\"\nConfirmBWTest.Title=\"Nadawanie w trybie testu przepustowości\"\nConfirmBWTest.Text=\"OBS skonfigurowany jest w trybie testu przepustowości. Tryb ten pozwala na test połączenia sieciowego bez streamowania \\\"na żywo\\\". Po zakończeniu testów wyłącz tryb, aby móc streamować \\\"na żywo\\\".\\n\\nCzy chcesz kontynuować?\"\nConfirmExit.Title=\"Aktywne wyjścia\"\nConfirmExit.Text=\"OBS jest wciąż aktywny. Wszystkie transmisje/nagrania zostaną zakończone.\"\nConfirmRemove.Title=\"Potwierdź usunięcie\"\nConfirmRemove.Text=\"Czy na pewno chcesz usunąć '%1'?\"\nConfirmRemove.TextMultiple=\"Liczba elementów do usunięcia: %1. Czy na pewno chcesz je usunąć?\"\nConfirmReset.Title=\"Reset właściwości\"\nConfirmReset.Text=\"Czy na pewno chcesz zresetować bieżące właściwości do wartości domyślnych?\"\nOutput.StartStreamFailed=\"Nie udało się rozpocząć nadawania\"\nOutput.StartRecordingFailed=\"Nie udało się rozpocząć nagrywania\"\nOutput.StartReplayFailed=\"Nie udało się rozpocząć nagrywania powtórek\"\nOutput.StartVirtualCamFailed=\"Nie udało się uruchomić kamery wirtualnej\"\nOutput.StartFailedGeneric=\"Nie udało się uruchomić wyjścia. Sprawdź szczegóły w plikach dziennika.\\n\\nUwaga: Sprawdź, czy posiadasz aktualne sterowniki karty graficznej, jeżeli używasz enkodera NVENC lub AMD.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Nie można zapisać powtórek podczas pauzy nagrywania\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Ostrzeżenie: Powtórki nie mogą być zapisane, gdy nagrywanie jest spauzowane.\"\nOutput.ConnectFail.Title=\"Nie udało się połączyć\"\nOutput.ConnectFail.BadPath=\"Nieprawidłowa ścieżka lub adres URL połączenia. Sprawdź poprawność ustawień.\"\nOutput.ConnectFail.ConnectFailed=\"Nie udało się połączyć z serwerem\"\nOutput.ConnectFail.InvalidStream=\"Nie można uzyskać dostępu do wybranego kanału lub klucza strumienia. Proszę sprawdzić klucz strumienia. Jeżeli jest poprawny, to problemem może być połączenie do serwera.\"\nOutput.ConnectFail.HdrDisabled=\"Format HDR jest obecnie niedostępny dla tego wyjścia.\"\nOutput.ConnectFail.Error=\"Wystąpił nieoczekiwany błąd podczas próby połączenia z serwerem. Więcej informacji w pliku dziennika.\"\nOutput.ConnectFail.Disconnected=\"Utracono połączenie z serwerem.\"\nOutput.StreamEncodeError.Title=\"Błąd enkodowania\"\nOutput.StreamEncodeError.Msg=\"W trakcie transmisji nastąpił błąd enkodowania.\"\nOutput.StreamEncodeError.Msg.LastError=\"Wystąpił błąd enkodera podczas strumieniowania:<br><br>%1\"\nOutput.RecordFail.Title=\"Nie udało się rozpocząć nagrywania\"\nOutput.RecordFail.Unsupported=\"Format wyjściowy jest nieobsługiwany lub nie obsługuje więcej niż jednej ścieżki dźwiękowej. Sprawdź ustawienia i spróbuj ponownie.\"\nOutput.RecordNoSpace.Title=\"Za mało miejsca na dysku\"\nOutput.RecordNoSpace.Msg=\"Za mało miejsca na dysku, aby kontynuować nagrywanie.\"\nOutput.RecordError.Title=\"Błąd nagrywania\"\nOutput.RecordError.Msg=\"Wystąpił nieokreślony błąd podczas nagrywania.\"\nOutput.RecordError.EncodeErrorMsg=\"W trakcie nagrywania nastąpił błąd enkodowania.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Wystąpił błąd enkodera podczas nagrywania:<br><br>%1\"\nOutput.BadPath.Title=\"Nieprawidłowa ścieżka pliku\"\nOutput.BadPath.Text=\"Nie można zlokalizować nastawionej ścieżki dla zapisu. Sprawdź jej poprawność w Ustawienia →Wyjście → Nagrywanie.\"\nOutput.NoBroadcast.Title=\"Nie skonfigurowano transmisji\"\nOutput.NoBroadcast.Text=\"Musisz skonfigurować transmisję zanim zaczniesz nadawać.\"\nOutput.BroadcastStartFailed=\"Nie udało się uruchomić transmisji\"\nOutput.BroadcastStopFailed=\"Nie udało się zatrzymać transmisji\"\nLogUploadDialog.Title=\"Przesyłanie pliku dziennika OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Przed przesłaniem jakichkolwiek plików zapoznaj się z <a href='https://obsproject.com/privacy-policy'>polityką prywatności</a> oraz jej sekcją dotyczącą przesyłania plików.\"\nLogUploadDialog.Labels.Progress=\"Trwa przesyłanie dziennika. Proszę czekać...\"\nLogUploadDialog.Labels.Description.AppLog=\"Twój plik dziennika został przesłany. Możesz teraz udostępnić adres URL do celów debugowania lub pomocy technicznej.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Twój raport o awarii został przesłany. Możesz teraz udostępnić adres URL do celów debugowania.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Prześlij\"\nLogUploadDialog.Buttons.CopyURL=\"Skopiuj adres URL dziennika\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Przeanalizuj plik dziennika\"\nLogUploadDialog.Buttons.RetryButton=\"Ponów\"\nLogUploadDialog.Errors.Template=\"Wystąpił błąd podczas próby przesłania pliku:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Nie znaleziono pliku do przesłania lub plik jest pusty.\"\nRemux.SourceFile=\"Plik nagrania OBS\"\nRemux.TargetFile=\"Plik docelowy\"\nRemux.Remux=\"Przepakowanie (remux)\"\nRemux.Stop=\"Zatrzymaj przepakowywanie\"\nRemux.ClearFinished=\"Usuń zakończone zadania\"\nRemux.ClearAll=\"Usuń wszystkie zadania\"\nRemux.OBSRecording=\"Plik nagrania z OBS\"\nRemux.FinishedTitle=\"Przepakowanie zakończone\"\nRemux.Finished=\"Nagranie przepakowane\"\nRemux.FinishedError=\"Nagranie przepakowane ale może być niekompletne\"\nRemux.SelectRecording=\"Wybierz nagranie OBS...\"\nRemux.SelectTarget=\"Wybierz plik docelowy...\"\nRemux.FileExistsTitle=\"Docelowy plik istnieje\"\nRemux.FileExists=\"Poniższe pliki już istnieją. Czy chcesz je nadpisać?\"\nRemux.ExitUnfinishedTitle=\"Przepakowywanie w toku\"\nRemux.ExitUnfinished=\"Przepakowywanie nie zostało zakończone. Zatrzymując operację teraz plik wynikowy może być bezużyteczny.\\nCzy na pewno chcesz zatrzymać przepakowywanie?\"\nRemux.HelpText=\"Przeciągnij pliki do tego okienka, aby je przepakować, lub kliknij wolne pole w kolumnie „Plik nagrania z OBS”, aby wybrać plik z dysku.\"\nRemux.NoFilesAddedTitle=\"Brak pliku do przepakowania\"\nRemux.NoFilesAdded=\"Nie dodano żadnego pliku do przepakowania. Przeciągnij folder zawierający jeden lub więcej plik wideo.\"\nMissingFiles=\"Brakujące pliki\"\nMissingFiles.MissingFile=\"Brakujący plik\"\nMissingFiles.NewFile=\"Nowy plik\"\nMissingFiles.HelpText=\"Od czasu ostatniego użycia OBS brakuje niektórych plików.\"\nMissingFiles.Clear=\"<usunięty>\"\nMissingFiles.NumFound=\"Znaleziono %1 z %2\"\nMissingFiles.Search=\"Przeszukaj katalog...\"\nMissingFiles.SelectFile=\"Wybierz plik...\"\nMissingFiles.SelectDir=\"Wybierz folder do przeszukania\"\nMissingFiles.State=\"Stan\"\nMissingFiles.Missing=\"Brakujący\"\nMissingFiles.Replaced=\"Zastąpiony\"\nMissingFiles.Cleared=\"Usunięty\"\nMissingFiles.Found=\"Znaleziony\"\nMissingFiles.AutoSearch=\"Znaleziono dodatkowe dopasowanie plików\"\nMissingFiles.AutoSearchText=\"OBS znalazł dodatkowe dopasowania brakujących plików w tym katalogu. Czy chcesz je dodać?\"\nMissingFiles.NoMissing.Title=\"Brakujące pliki\"\nMissingFiles.NoMissing.Text=\"Wygląda na to, że nie brakuje żadnych plików.\"\nMacPermissions.MenuAction=\"Przejrzyj uprawnienia aplikacji...\"\nMacPermissions.Title=\"Przejrzyj uprawnienia aplikacji\"\nMacPermissions.Description=\"OBS Studio wymaga uprawnień, aby móc dostarczać określone funkcje. Zalecane jest włączenie tych uprawnień, ale nie są one wymagane do korzystania z aplikacji. Zawsze możesz je włączyć później.\"\nMacPermissions.Description.OpenDialog=\"Możesz ponownie otworzyć to okno za pomocą menu w OBS Studio.\"\nMacPermissions.AccessGranted=\"Dostęp przyznany\"\nMacPermissions.RequestAccess=\"Żądaj dostępu\"\nMacPermissions.OpenPreferences=\"Otwórz preferencje %1\"\nMacPermissions.Item.ScreenRecording=\"Nagrywanie ekranu\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS wymaga tego uprawnienia, aby móc przechwytwać ekran.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"To uprawnienie jest potrzebne do przechwytywania treści z kamery internetowej lub karty przechwytywania.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS wymaga tego uprawnienia, jeśli chcesz przechwycić dźwięk z mikrofonu lub z zewnętrznego urządzenia audio.\"\nMacPermissions.Item.InputMonitoring=\"Monitorowanie wprowadzania\"\nMacPermissions.Item.InputMonitoring.Details=\"To uprawnienie wymagane jest, aby działały skróty klawiszowe, gdy OBS działa w tle.\"\nMacPermissions.Item.Accessibility=\"Ułatwienia dostępu\"\nMacPermissions.Item.Accessibility.Details=\"W starszych instalacjach OBS może znajdować się na liście \\\"Ułatwienia dostępu\\\" zamiast \\\"Monitorowanie wprowadzania\\\".\"\nMacPermissions.Continue=\"Dalej\"\nSourceLeak.Title=\"Błąd odłączenia źródła\"\nSourceLeak.Text=\"Wystąpił problem w trakcie przełączania kolekcji scen - niektóre ze źródeł nie mogły być usunięte z pamięci. Problem ten najczęściej wynika z wtyczek, które niepoprawnie uwalniają zajęte zasoby. Proszę sprawdzić, czy wtyczki są aktualne.\\n\\nOBS Studio zostanie zamknięte, aby uniknąć potencjalnej utraty lub uszkodzenia danych.\"\nBasic.DesktopDevice1=\"Urządzenie audio\"\nBasic.DesktopDevice2=\"Urządzenie audio 2\"\nBasic.AuxDevice1=\"Mikrofon/Wejście liniowe\"\nBasic.AuxDevice2=\"Mikrofon/Wejście liniowe 2\"\nBasic.AuxDevice3=\"Mikrofon/Wejście liniowe 3\"\nBasic.AuxDevice4=\"Mikrofon/Wejście liniowe 4\"\nBasic.Scene=\"Scena\"\nBasic.DisplayCapture=\"Przechwytywanie obrazu\"\nBasic.Main.PreviewConextMenu.Enable=\"Włącz podgląd\"\nBasic.Main.Preview.Disable=\"Wyłącz podgląd\"\nScaleFiltering=\"Filtrowanie skalowania\"\nScaleFiltering.Point=\"Punktowe\"\nScaleFiltering.Bilinear=\"Dwuliniowe\"\nScaleFiltering.Bicubic=\"Dwusześcienne\"\nScaleFiltering.Area=\"Obszar\"\nBlendingMethod=\"Metoda mieszania\"\nBlendingMethod.Default=\"Domyślna\"\nBlendingMode=\"Tryb mieszania\"\nBlendingMode.Normal=\"Normalny\"\nBlendingMode.Additive=\"Dodawanie\"\nBlendingMode.Subtract=\"Odejmowanie\"\nBlendingMode.Screen=\"Nakładanie\"\nBlendingMode.Multiply=\"Iloczyn\"\nBlendingMode.Lighten=\"Rozjaśnienie\"\nBlendingMode.Darken=\"Przyciemnienie\"\nDeinterlacing=\"Usuwanie przeplotu\"\nDeinterlacing.Discard=\"Odrzuć\"\nDeinterlacing.TopFieldFirst=\"Najpierw pole górne\"\nDeinterlacing.BottomFieldFirst=\"Najpierw pole dolne\"\nVolControl.SliderUnmuted=\"Suwak głośności dla '%1':\"\nVolControl.SliderMuted=\"Suwak głośności dla '%1': (obecnie wyciszony)\"\nVolControl.Mute=\"Wycisz '%1'\"\nVolControl.Properties=\"Właściwości dla '%1'\"\nVolControl.UnassignedWarning.Title=\"Nieprzypisane źródło dźwięku\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" nie jest przypisane do żadnej ścieżki audio i nie będzie słyszalne na streamie lub nagraniu.\\n\\nAby przypisać źródło dźwięku do ścieżki audio, otwórz Zaawansowane ustawienia dźwięku prawym przyciskiem myszy lub klikając na ikonę zębatki w panelu Miksera dźwięku.\"\nBasic.Main.AddSceneDlg.Title=\"Dodaj scenę\"\nBasic.Main.AddSceneDlg.Text=\"Podaj nazwę sceny\"\nBasic.Main.DefaultSceneName.Text=\"Scena %1\"\nBasic.Main.AddSceneCollection.Title=\"Dodaj zbiór scen\"\nBasic.Main.AddSceneCollection.Text=\"Podaj nazwę zbioru scen\"\nBasic.Main.RenameSceneCollection.Title=\"Zmień nazwę zbioru scen\"\nBasic.Main.RemigrateSceneCollection.Title=\"Zmień rozdzielczość kolekcji scen\"\nBasic.Main.RemigrateSceneCollection.Text=\"Czy chcesz zmienić rozdzielczość kolekcji scen \\\"%1\\\", aby zgadzała się z rozdzielczością aktualnego profilu %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Nie można zaktualizować rozdzielczości kolekcji scen, gdy wyjścia są aktywne.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Nie udało się zmienić rozdzielczości kolekcji scen. Nieznana rozdzielczość oryginalna.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Reset niemożliwy: Zmiana rozdzielczości OBS nieudana.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Reset niemożliwy: Akutalna rozdzielczość jest już taka sama, jak rozdzielczość kolekcji scen.\"\nAddProfile.Title=\"Dodaj profil\"\nAddProfile.Text=\"Podaj nazwę profilu\"\nAddProfile.WizardCheckbox=\"Pokaż kreatora automatycznej konfiguracji\"\nRenameProfile.Title=\"Zmień nazwę profilu\"\nBasic.Main.MixerRename.Title=\"Zmiana nazwy źródła dźwięku\"\nBasic.Main.MixerRename.Text=\"Podaj nazwę źródła dźwięku\"\nBasic.Main.PreviewDisabled=\"Podgląd jest nieaktywny\"\nBasic.SourceSelect=\"Stwórz/Wybierz źródło\"\nBasic.SourceSelect.CreateNew=\"Stwórz nową\"\nBasic.SourceSelect.AddExisting=\"Dodaj istniejącą\"\nBasic.SourceSelect.AddVisible=\"Źródło widoczne\"\nBasic.Main.Sources.Visibility=\"Widoczność\"\nBasic.Main.Sources.VisibilityDescription=\"Określa widoczność '%1' na wynikowym ekranie\"\nBasic.Main.Sources.Lock=\"Blokada\"\nBasic.Main.Sources.LockDescription=\"Blokuje pozycję i wielkość '%1' na wynikowym ekranie\"\nBasic.PropertiesWindow=\"Właściwości dla '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (wybór automatyczny: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Wybierz kolor\"\nBasic.PropertiesWindow.SelectFont=\"Wybierz czcionkę\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Wybierz czcionkę\"\nBasic.PropertiesWindow.ConfirmTitle=\"Ustawienia zmienione\"\nBasic.PropertiesWindow.Confirm=\"Czy chcesz zachować niezapisane zmiany?\"\nBasic.PropertiesWindow.NoProperties=\"Brak właściwości\"\nBasic.PropertiesWindow.AddFiles=\"Dodaj pliki\"\nBasic.PropertiesWindow.AddDir=\"Dodaj folder\"\nBasic.PropertiesWindow.AddURL=\"Dodaj ścieżkę/adres URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Dodaj folder do '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Dodaj pliki do '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Dodaj wpis do '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Edytuj wpis w '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Proste wartości FPS (kl./s)\"\nBasic.PropertiesView.FPS.Rational=\"Ułamkowe wartości FPS (kl./s)\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Prawidłowe zakresy FPS (kl./s):\"\nBasic.PropertiesView.UrlButton.Text=\"Otworzyć ten link w domyślnej przeglądarce internetowej?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Otwórz URL\"\nBasic.InteractionWindow=\"Interakcja z '%1'\"\nBasic.StatusBar.Reconnecting=\"Rozłączono, próba ponownego połączenia za %2s (próba %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Próba ponownego połączenia... (próba %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Ponowne podłączenie udane\"\nBasic.StatusBar.Delay=\"Opóźnienie (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Opóźnienie (początek za %1 s)\"\nBasic.StatusBar.DelayStoppingIn=\"Opóźnienie (koniec za %1 s)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Opóźnienie (koniec za %1 s, początek za %2 s)\"\nBasic.StatusBar.RecordingSavedTo=\"Nagranie zapisane w '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Bufor powtórki zapisany w '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Zrzut ekranu zapisany w '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Nagranie przepakowane do '%1'\"\nBasic.Filters=\"Filtry\"\nBasic.Filters.AsyncFilters=\"Filtry audio/wideo\"\nBasic.Filters.AudioFilters=\"Filtry audio\"\nBasic.Filters.EffectFilters=\"Filtry efektów\"\nBasic.Filters.Title=\"Filtry dla '%1'\"\nBasic.Filters.AddFilter.Title=\"Nazwa filtra\"\nBasic.Filters.AddFilter.Text=\"Proszę wpisać nazwę filtra\"\nBasic.TransformWindow=\"Przekształcanie elementu sceny\"\nBasic.TransformWindow.Position=\"Pozycja\"\nBasic.TransformWindow.PositionX=\"Pozycja X\"\nBasic.TransformWindow.PositionY=\"Pozycja Y\"\nBasic.TransformWindow.Rotation=\"Obrót\"\nBasic.TransformWindow.Size=\"Rozmiar\"\nBasic.TransformWindow.Width=\"Szerokość\"\nBasic.TransformWindow.Height=\"Wysokość\"\nBasic.TransformWindow.Alignment=\"Wyrównanie\"\nBasic.TransformWindow.BoundsType=\"Typ ograniczenia\"\nBasic.TransformWindow.BoundsAlignment=\"Wyrównanie w obwiedni\"\nBasic.TransformWindow.Bounds=\"Rozmiar obwiedni\"\nBasic.TransformWindow.BoundsWidth=\"Szerokość prostokąta ograniczającego\"\nBasic.TransformWindow.BoundsHeight=\"Wysokość prostokąta ograniczającego\"\nBasic.TransformWindow.CropToBounds=\"Przytnij do granic obszaru\"\nBasic.TransformWindow.Crop=\"Kadrowanie\"\nBasic.TransformWindow.CropLeft=\"Przytnij z lewej\"\nBasic.TransformWindow.CropRight=\"Przytnij z prawej\"\nBasic.TransformWindow.CropTop=\"Przytnij od góry\"\nBasic.TransformWindow.CropBottom=\"Przytnij od spodu\"\nBasic.TransformWindow.Alignment.TopLeft=\"Lewy/górny\"\nBasic.TransformWindow.Alignment.TopCenter=\"Środek/górny\"\nBasic.TransformWindow.Alignment.TopRight=\"Prawy/górny\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Lewy/środek\"\nBasic.TransformWindow.Alignment.Center=\"Wyśrodkowane\"\nBasic.TransformWindow.Alignment.CenterRight=\"Prawy/środek\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Lewy/dolny\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Środek/dolny\"\nBasic.TransformWindow.Alignment.BottomRight=\"Prawy/dolny\"\nBasic.TransformWindow.BoundsType.None=\"Brak obwiedni\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Tylko maksymalny rozmiar\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skaluj do granic wewnętrznych\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skaluj do granic zewnętrznych\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skaluj do długości granic\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skaluj do wysokości granic\"\nBasic.TransformWindow.BoundsType.Stretch=\"Rozciągnij do granic\"\nBasic.TransformWindow.Title=\"Edytuj transformację dla '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Nie wybrano źródła\"\nBasic.Main.AddSourceHelp.Title=\"Nie można dodać źródła\"\nBasic.Main.AddSourceHelp.Text=\"Wymagana jest przynajmniej jedna scena, aby dodać źródło.\"\nBasic.Main.Scenes=\"Sceny\"\nBasic.Main.Sources=\"Źródła obrazu\"\nBasic.Main.Source=\"Źródło\"\nBasic.Main.Controls=\"Panel sterujący\"\nBasic.Main.PreparingStream=\"Przygotowywanie...\"\nBasic.Main.Connecting=\"Łączenie...\"\nBasic.Main.StartRecording=\"Rozpocznij nagrywanie\"\nBasic.Main.StartReplayBuffer=\"Rozpocznij nagrywanie powtórek\"\nBasic.Main.SaveReplay=\"Zapisz powtórkę\"\nBasic.Main.StartStreaming=\"Rozpocznij transmisję\"\nBasic.Main.StartBroadcast=\"Nadawaj\"\nBasic.Main.StartVirtualCam=\"Uruchom kamerę wirtualną\"\nBasic.Main.StopRecording=\"Zatrzymaj nagrywanie\"\nBasic.Main.PauseRecording=\"Pauzuj nagrywanie\"\nBasic.Main.UnpauseRecording=\"Wznów nagrywanie\"\nBasic.Main.SplitFile=\"Podziel plik nagrywania\"\nBasic.Main.AddChapterMarker=\"Dodaj znacznik rozdziału (tylko hybrydowy MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Zatrzymywanie nagrywania...\"\nBasic.Main.StopReplayBuffer=\"Zatrzymaj nagrywanie powtórek\"\nBasic.Main.StoppingReplayBuffer=\"Zatrzymywanie nagrywania powtórek...\"\nBasic.Main.SetupBroadcast=\"Zarządzaj transmisją\"\nBasic.Main.StopStreaming=\"Zatrzymaj transmisję\"\nBasic.Main.StopBroadcast=\"Zakończ transmisję\"\nBasic.Main.StoppingStreaming=\"Zatrzymywanie transmisji...\"\nBasic.Main.ForceStopStreaming=\"Zatrzymaj transmisję (zignoruj opóźnienie)\"\nBasic.Main.ShowContextBar=\"Pokaż pasek narzędziowy źródła\"\nBasic.Main.HideContextBar=\"Ukryj pasek narzędziowy źródła\"\nBasic.Main.StopVirtualCam=\"Zatrzymaj kamerę wirtualną\"\nBasic.Main.Group=\"Grupa %1\"\nBasic.Main.GroupItems=\"Grupuj wybrane elementy\"\nBasic.Main.Ungroup=\"Rozgrupuj\"\nBasic.Main.GridMode=\"Widok siatki\"\nBasic.Main.ListMode=\"Widok listy\"\nBasic.Main.VirtualCamConfig=\"Skonfiguruj kamerę wirtualną\"\nBasic.VCam.VirtualCamera=\"Kamera wirtualna\"\nBasic.VCam.OutputType=\"Typ wyjścia\"\nBasic.VCam.OutputSelection=\"Wybór wyjścia\"\nBasic.VCam.OutputType.Program=\"Wyjście programu (domyślne)\"\nBasic.VCam.OutputSelection.NoSelection=\"Brak wyboru dla wyjścia tego typu\"\nBasic.VCam.RestartWarning=\"Aby wyprowadzić zmiany, wirtualna kamera zostanie uruchomiona ponownie\"\nBasic.MainMenu.File=\"&Plik\"\nBasic.MainMenu.File.Export=\"&Eksport\"\nBasic.MainMenu.File.ShowRecordings=\"Pokaż nag&rania\"\nBasic.MainMenu.File.Remux=\"Przepakuj nagrania (re&mux)\"\nBasic.MainMenu.File.Settings=\"U&stawienia\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Pokaż folder z ustawieniami\"\nBasic.MainMenu.File.ShowProfileFolder=\"Pokaż folder z profilami\"\nBasic.MainMenu.File.ShowMissingFiles=\"Sprawdź brakujące pliki\"\nBasic.MainMenu.File.Exit=\"&Zakończ\"\nBasic.MainMenu.Edit=\"&Edycja\"\nBasic.MainMenu.Edit.Undo=\"Cofnij (&U)\"\nBasic.MainMenu.Edit.Redo=\"Wykonaj ponownie (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Zab&lokuj podgląd\"\nBasic.MainMenu.Edit.Scale=\"&Skalowanie widoku\"\nBasic.MainMenu.Edit.Scale.Window=\"Skaluj do okna\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Powierzchnia robocza (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Powierzchnia wyjściowa (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Skalowanie (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Przybliż\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Oddal\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Reset przybliżenia\"\nBasic.MainMenu.Edit.Transform=\"Przeksz&tałcanie obrazu\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Edycja przekształceń...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Skopiuj transformacje\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Wklej transformacje\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Reset przekształceń\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Obróć o 90 stopni zgodnie z ruchem wskazówkek zegara\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Obróć o 90 stopni przeciwnie do ruchu wskazówek zegara\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Obróć o 180 stopni\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Prze&rzuć w poziomie\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Przer&zuć w pionie\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"&Dopasuj do ekranu\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Rozcią&gnij do ekranu\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Wyśro&dkuj na ekranie\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Wyśrodkuj w pionie\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Wyśrodkuj w poziomie\"\nBasic.MainMenu.Edit.Order=\"K&olejność\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Przes&uń w górę\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Przesuń w &dół\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Przesuń na samą &górę\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Przesuń na sa&m dół\"\nBasic.MainMenu.Edit.AdvAudio=\"Z&aawansowane ustawienia dźwięku\"\nBasic.MainMenu.View=\"&Widok\"\nBasic.MainMenu.View.Toolbars=\"Paski &narzędzi\"\nBasic.MainMenu.View.ListboxToolbars=\"Paski narzędzi paneli\"\nBasic.MainMenu.View.ContextBar=\"Pasek narzędziowy źródła\"\nBasic.MainMenu.View.SourceIcons=\"&Ikony źródłowe\"\nBasic.MainMenu.View.StatusBar=\"Pasek &stanu\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Pełnoekranowy interfejs\"\nBasic.MainMenu.View.ResetUI=\"P&rzywróć domyślny interfejs\"\nBasic.MainMenu.View.AlwaysOnTop=\"Z&awsze na wierzchu\"\nBasic.MainMenu.View.SceneListMode=\"Tryb listy scen\"\nBasic.MainMenu.Docks=\"Pane&le\"\nBasic.MainMenu.Docks.ResetDocks=\"&Resetuj panele\"\nBasic.MainMenu.Docks.LockDocks=\"Zab&lokuj panele\"\nBasic.MainMenu.Docks.SideDocks=\"Panele &boczne\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Panele przeglądarki...\"\nBasic.MainMenu.Import=\"Importuj...\"\nBasic.MainMenu.Export=\"Eksportuj...\"\nBasic.MainMenu.SceneCollection=\"Zbiór &scen\"\nBasic.MainMenu.Profile=\"P&rofil\"\nBasic.MainMenu.Profile.Import=\"Importuj profil\"\nBasic.MainMenu.Profile.Export=\"Eksportuj profil\"\nBasic.MainMenu.SceneCollection.Import=\"Importuj kolekcje scen\"\nBasic.MainMenu.SceneCollection.Export=\"Eksportuj kolekcję scen\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Resetuj rozdzielczość bazową\"\nBasic.MainMenu.SceneCollection.Migrate=\"Ustaw rozdzielczość bazową\"\nBasic.MainMenu.Profile.Exists=\"Profil już istnieje\"\nBasic.MainMenu.Tools=\"&Narzędzia\"\nBasic.MainMenu.Help=\"Po&moc\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal pomocy\"\nBasic.MainMenu.Help.Website=\"Od&wiedź naszą stronę\"\nBasic.MainMenu.Help.Discord=\"Dołącz do serwera &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Nowości\"\nBasic.MainMenu.Help.Logs=\"P&liki dziennika\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Pokaż pliki &dziennika\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"&Wyślij aktualny plik dziennika\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Prześlij &poprzedni plik dziennika\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Podgląd &aktywnego pliku dziennika\"\nBasic.MainMenu.Help.ReleaseNotes=\"Informacje o wersji\"\nBasic.MainMenu.Help.CheckForUpdates=\"Sprawdź dostępność aktualizacji\"\nBasic.MainMenu.Help.Repair=\"Sprawdź integralność plików\"\nBasic.MainMenu.Help.RestartSafeMode=\"Uruchom ponownie w trybie awaryjnym\"\nBasic.MainMenu.Help.RestartNormal=\"Uruchom ponownie w trybie normalnym\"\nBasic.MainMenu.Help.CrashLogs=\"&Raporty o awariach\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Pokaż &raporty o awariach\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Prześlij &poprzedni raport o awariach\"\nBasic.MainMenu.Help.About=\"O progr&amie\"\nBasic.Settings.ProgramRestart=\"Aby te ustawienia zaczęły obowiązywać, należy ponownie uruchomić program.\"\nBasic.Settings.ConfirmTitle=\"Potwierdź zmiany\"\nBasic.Settings.Confirm=\"Zmiany nie zostały zapisane. Czy zapisać zmiany?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"Niektóre ustawienia strumieniowania kontrolowane są przez %1 %2\"\nBasic.Settings.General=\"Główne\"\nBasic.Settings.General.Language=\"Język\"\nBasic.Settings.General.Updater=\"Aktualizacje\"\nBasic.Settings.General.UpdateChannel=\"Kanał aktualizacji\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Wyłączony)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Domyślny)\"\nBasic.Settings.General.EnableAutoUpdates=\"Automatycznie sprawdzaj dostępność aktualizacji\"\nBasic.Settings.General.OpenStatsOnStartup=\"Otwórz statystyki przy starcie aplikacji\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Ukryj okna OBS przed przechwytywaniem ekranu\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Ta opcja ukryje wszystkie okna OBS Studio przed ich przechwytywaniem przez OBS (oprócz okien z projekcją) i wpływa na inne aplikacje,\\n takie jak wideokonferencyjne, udostępniania ekranu, zdalnego dostępu, do zrzutów ekranu i inne oprogramowanie do przechwytywania.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Włączenie tej opcji ukryje wszystkie okna OBS Studio przed ich przechwytywaniem przez OBS (oprócz okien z projekcją) i wpływa na inne aplikacje, takie jak wideokonferencyjne, udostępniania ekranu, zdalnego dostępu, do zrzutów ekranu i inne oprogramowanie do przechwytywania.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Pokaż komunikat potwierdzenia uruchomienia transmisji\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Pokaż komunikat potwierdzenia zatrzymania transmisji\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Pokaż komunikat potwierdzenia zatrzymania nagrywania\"\nBasic.Settings.General.Projectors=\"Projektory\"\nBasic.Settings.General.HideProjectorCursor=\"Ukryj kursor podglądu na pełnym ekranie\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Podgląd na pełnym ekranie zawsze na wierzchu\"\nBasic.Settings.General.Snapping=\"Przyciąganie elementów źródłowych\"\nBasic.Settings.General.ScreenSnapping=\"Przyciągaj do krawędzi ekranu\"\nBasic.Settings.General.CenterSnapping=\"Przyciągaj do poziomego i pionowego środka\"\nBasic.Settings.General.SourceSnapping=\"Przyciągaj źródła do innych źródeł\"\nBasic.Settings.General.SnapDistance=\"Czułość przyciągania\"\nBasic.Settings.General.SpacingHelpers=\"Pokaż odległości elementów scen w pikselach\"\nBasic.Settings.General.RecordWhenStreaming=\"Automatyczne nagrywanie podczas transmisji\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Kontynuuj nagrywanie po zatrzymaniu transmisji\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Automatycznie rozpocznij nagrywanie powtórek podczas transmisji\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Kontynuuj nagrywanie powtórek po zatrzymaniu transmisji\"\nBasic.Settings.General.SysTray=\"Zasobnik systemowy\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimalizuj do zasobnika systemowego podczas uruchamiania\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Zawsze minimalizuj do zasobnika systemowego zamiast do paska zadań\"\nBasic.Settings.General.SaveProjectors=\"Zapisz konfigurację podglądów na pełnym ekranie przy zamknięciu aplikacji\"\nBasic.Settings.General.CloseExistingProjectors=\"Ogranicz projekcję pełnoekranową do jednej na ekran\"\nBasic.Settings.General.Preview=\"Podgląd\"\nBasic.Settings.General.OverflowHidden=\"Ukryj elementy poza granicami widoku\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Elementy poza granicami widoku zawsze widoczne\"\nBasic.Settings.General.OverflowSelectionHidden=\"Elementy poza granicami widoczne nawet, gdy źródło jest ukryte\"\nBasic.Settings.General.Importers=\"Importowanie\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Przeszukaj znane lokalizacje na dysku przy imporcie zbiorów scen\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Przejdź do sceny po dwukrotnym kliknięciu\"\nBasic.Settings.General.StudioPortraitLayout=\"Włącz układ pionowy\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Pokaż etykiety sekcji Podgląd/Na żywo\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Kliknij, aby przełączać się między scenami\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Pokaż nazwy scen\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Rysowanie bezpiecznych obszarów (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Układ Multiview\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Poziomo, góra (8 scen)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Poziomo, dół (8 scen)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Pionowo, lewo (8 scen)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Pionowo, prawo (8 scen)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Poziomo, góra (18 scen)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Poziomo, góra (24 sceny)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Tylko sceny (4 sceny)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Tylko sceny (9 scen)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Tylko sceny (16 scen)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Tylko sceny (25 scen)\"\nBasic.Settings.General.ChannelName.stable=\"Stabilna\"\nBasic.Settings.General.ChannelDescription.stable=\"Najnowsze stabilne wydanie\"\nBasic.Settings.General.ChannelName.beta=\"Beta / RC\"\nBasic.Settings.General.ChannelDescription.beta=\"Potencjalnie niestabilne wersje testowe\"\nBasic.Settings.Appearance=\"Wygląd\"\nBasic.Settings.Appearance.General=\"Ogólny\"\nBasic.Settings.Appearance.General.Theme=\"Motyw\"\nBasic.Settings.Appearance.General.Variant=\"Styl\"\nBasic.Settings.Appearance.General.NoVariant=\"Brak dostępnych stylów\"\nBasic.Settings.Appearance.FontScale=\"Rozmiar czcionki\"\nBasic.Settings.Appearance.Density=\"Gęstość\"\nBasic.Settings.Appearance.Classic=\"Klasyczny\"\nBasic.Settings.Appearance.Compact=\"Kompaktowy\"\nBasic.Settings.Appearance.Normal=\"Normalny\"\nBasic.Settings.Appearance.Comfortable=\"Wygodny\"\nBasic.Settings.Appearance.OptionsWarning=\"Niektóre opcje wyglądu nie są dostępne dla tego stylu.\"\nBasic.Settings.Stream=\"Transmisja\"\nBasic.Settings.Stream.Destination=\"Cel\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Użyj uwierzytelniania\"\nBasic.Settings.Stream.Custom.Username=\"Nazwa użytkownika\"\nBasic.Settings.Stream.Custom.Password=\"Hasło\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: wprowadź srp_username.\\nRTMP: wprowadź nazwę użytkownika.\\nSRT: nieużywana.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: wprowadź srp_password.\\nRTMP: wprowadź hasło.\\nSRT: wprowadź hasło szyfrowania.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Włącz tryb testu przepustowości\"\nBasic.Settings.Stream.TTVAddon=\"Dodatki czata Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Brak\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV i FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Brak konfiguracji strumienia\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Otwórz Ustawienia\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Brakuje adresu URL i klucza strumienia.\\n\\nOtwórz ustawienia, aby w zakładce 'Stream' wprowadzić adres URL i klucz strumienia.\"\nBasic.Settings.Stream.MissingUrl=\"Brakuje adresu URL.\\n\\nOtwórz ustawienia, aby w zakładce 'Stream' wprowadzić adres URL.\"\nBasic.Settings.Stream.MissingStreamKey=\"Brakuje klucza strumienia.\\n\\nOtwórz ustawienia, aby w zakładce 'Stream' wprowadzić klucz strumienia.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignoruj zalecenia dotyczące ustawień usługi strumieniowej\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Nadpisz rekomendowane ustawienia\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Ostrzeżenie: Ignorowanie ograniczeń usługi może spowodować pogorszenie jakości strumieniowania lub uniemożliwić przesyłanie strumieniowe.\\n\\nKontynuować?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maksymalny bitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Minimalny bitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksymalna rozdzielczość: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksymalna liczba FPS (kl./s): %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Zdefiniuj własny serwer...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Własny serwer\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Aktywuj %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maksymalny bitrate\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automatycznie\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maksymalna liczba ścieżek video\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automatycznie\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Zapis streamu do pliku FLV (korzysta z podstawowych ustawień nagrywania do pliku)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Nadpisanie konfiguracji (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Włącz nadpisywanie konfiguracji\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Wideo z wieloma ścieżkami\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Dodatkowe widoki\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Simulcast umożliwia enkodowanie i przesyłanie materiału wideo o różnej jakości. <a href='https://obsproject.com/kb/whip-streaming-guide'>Dowiedz się więcej</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Liczba warstw\"\nBasic.Settings.Stream.AdvancedOptions=\"Opcje zaawansowane\"\nBasic.Settings.Output=\"Wyjście\"\nBasic.Settings.Output.Format=\"Format nagrywania\"\nBasic.Settings.Output.Format.hMP4=\"Hybrydowy MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hybrydowy MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmentowane MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmentowane MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmentaryczny MOV zapisuje nagranie w kawałkach i nie wymaga takiej samej finalizacji jak tradycyjne pliki MOV.\\nDzięki temu plik pozostaje odtwarzalny nawet wtedy, gdy zapis na dysku zostanie przerwany, na przykład w wyniku wystąpienia \\\"niebieskiego ekranu\\\" lub utraty zasilania.\\n\\nMoże nie być kompatybilny ze wszystkimi odtwarzaczami i edytorami. Użyj opcji Plik → Przepakuj nagrania, aby w razie potrzeby przekonwertować plik na bardziej kompatybilny format.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmentaryczne MP4 zapisuje nagranie w kawałkach i nie wymaga takiej samej finalizacji jak tradycyjne pliki MP4.\\nTo zapewnia, że plik pozostaje odtwarzany nawet jeśli zapis na dysku zostanie przerwany, na przykład w wyniku wystąpienia \\\"niebieskiego ekranu\\\" lub utraty zasilania.\\n\\nTo może nie być kompatybilne ze wszystkimi odtwarzaczami i edytorami. Użyj opcji Plik → Przepakuj nagrania, aby w razie potrzeby przekonwertować plik na bardziej kompatybilny format.\"\nBasic.Settings.Output.Encoder.Video=\"Enkoder obrazu\"\nBasic.Settings.Output.Encoder.Audio=\"Enkoder dźwięku\"\nBasic.Settings.Output.SelectDirectory=\"Wybierz katalog nagrywania\"\nBasic.Settings.Output.DynamicBitrate=\"Dynamicznie zmień bitrate, aby zapobiec zapychaniu łącza\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dynamicznie zmień bitrate, aby zapobiec zapychaniu łącza (beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Zamiast odrzucać klatki przy zapchanym łączu dynamicznie zmienia bitrate enkodowania.\\n\\nW przypadku dużego i nagłego przytkania łącza zwiększa to opóźnienie streamu dla oglądających.\\nPo zmniejszeniu bitrate, jego powrót do normalnych wartości może potrwać kilka minut.\\n\\nMechanizm obsługiwany jest obecnie jedynie przy protokole RTMP.\"\nBasic.Settings.Output.Mode=\"Tryb wyjścia\"\nBasic.Settings.Output.Mode.Simple=\"Proste\"\nBasic.Settings.Output.Mode.Adv=\"Zaawansowane\"\nBasic.Settings.Output.Mode.FFmpeg=\"Wyjście FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Włącz nagrywanie powtórek\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maksymalny czas nagrania\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maksymalna pamięć\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Szacowane użycie pamięci: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Uwaga: Szacowane zużycie pamięci %1 MiB przewyższa rekomendowane maksimum %2 MiB.\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Nie można oszacować użycia pamięci. Należy ustawić limit maksymalnej pamięci.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefiks nazwy pliku buforu replay\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufiks\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Nie można korzystać z Nagrywania Powtórek, gdy typ nagrywania jest ustawiony na Niestandardowe Ustawienia (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Ścieżka pliku\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Jakość nagrywania\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Taka sama jak dla transmisji\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Wysoka jakość, średni rozmiar pliku\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Jakość nie do odróżnienia, duża wielkość pliku\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Jakość bezstratna, ogromna wielkość pliku\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Ostrzeżenie: Bitrate obrazu zostanie ustawiony na %1, co jest górnym limitem dla bieżącej usługi strumieniowej.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Ostrzeżenie: Bitrate dźwięku zostanie ustawiony na %1, co jest górnym limitem dla bieżącej usługi strumieniowej.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Ostrzeżenie: Nagrywanie nie może być spauzowane, gdy jakość nagrywania ustawiona jest na \\\"Taka sama jak stream\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Uwaga: obecnie wybrany format nagrywania nie jest kompatybilny z wybranym enkoderem streamu.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Ostrzeżenie: nagrywanie przy użyciu enkodera programowego z jakością inną niż stream wymagać będzie dodatkowej mocy procesora w przypadku jednoczesnego streamowania i nagrywania.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Ostrzeżenie: Jakość bezstratna generuje bardzo duże pliki! Przy dużych rozdzielczościach i wysokich wartościach FPS (kl./s) rozmiar pliku może sięgać 7GB na minutę nagrania. Jakość bezstratna nie jest polecana w przypadku długich nagrań, chyba że masz bardzo dużo wolnego miejsca na dysku. Bufor powtórek nie jest dostępny przy jakości bezstratnej.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Czy na pewno chcesz użyć bezstratnej jakości?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Ostrzeżenie o bezstratnej jakości!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Programowy (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Sprzętowy (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Sprzętowy (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Sprzętowy (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Sprzętowy (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Sprzętowy (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Sprzętowy (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Sprzętowy (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Sprzętowy (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Sprzętowy (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Sprzętowy (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Programowy (x264 ustawienia małego zużycia procesora, zwiększa wielkość pliku)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (domyślne)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Ścieżka VOD Twitch (korzysta ze ścieżki 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Ścieżka audio\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Niekompatybilna rozdzielczość/liczba klatek\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Ta usługa strumieniowania nie obsługuje Twojej aktualnej rozdzielczości wyjściowej i/lub liczby FPS (klatek na sekundę). Zostaną one zmienione na najbliższą kompatybilną wartość:\\n\\n%1\\n\\nCzy chcesz kontynuować?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Rozdzielczość: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"kl./s: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Niekompatybilny enkoder\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Usługa \\\"%1\\\" nie obsługuje enkodera \\\"%2\\\". Enkoder zostanie zmieniony na \\\"%3\\\".\\n\\nCzy chcesz kontynuować?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Usługa \\\"%1\\\" nie obsługuje enkoderów \\\"%2\\\" i \\\"%3\\\". Enkodery zostaną zmienione na \\\"%4\\\" i \\\"%5\\\".\\n\\nCzy chcesz kontynuować?\"\nBasic.Settings.Output.VideoBitrate=\"Bitrate obrazu\"\nBasic.Settings.Output.AudioBitrate=\"Bitrate dźwięku\"\nBasic.Settings.Output.Reconnect=\"Ponowne łączenie\"\nBasic.Settings.Output.RetryDelay=\"Próbuj połączyć co\"\nBasic.Settings.Output.MaxRetries=\"Maksymalna liczba ponownych prób\"\nBasic.Settings.Output.Advanced=\"Włącz niestandardowe ustawienia enkodera (Zaawansowane)\"\nBasic.Settings.Output.EncoderPreset=\"Ustawienia enkodera\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (niskie użycie procesora, najniższa jakość)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (domyślne) (średnie użycie procesora, standardowa jakość)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (wysokie użycie procesora, wysoka jakość)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Ustawienia niestandardowe enkodera\"\nBasic.Settings.Output.CustomMuxerSettings=\"Własne ustawienia muxera\"\nBasic.Settings.Output.NoSpaceFileName=\"Generuj nazwę pliku bez spacji\"\nBasic.Settings.Output.Adv.Rescale=\"Przeskaluj wyjściowy obraz\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Wyłączone\"\nBasic.Settings.Output.Adv.AudioTrack=\"Ścieżki dźwiękowe\"\nBasic.Settings.Output.Adv.Streaming=\"Transmisja\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Ustawienia strumieniowania\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Ścieżka 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Ścieżka 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Ścieżka 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Ścieżka 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Ścieżka 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Ścieżka 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Ścieżka Twitch VOD\"\nBasic.Settings.Output.Adv.Encoder=\"Ustawienia enkodera\"\nBasic.Settings.Output.Adv.Recording=\"Nagrywanie\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Ustawienia nagrywania\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Typ nagrania\"\nBasic.Settings.Output.Adv.Recording.Type=\"Typ\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standardowe\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Niestandardowe ustawienia (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Użyj enkodera strumieniowania)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Format nazwy pliku\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Nadpisz istniejący plik\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Niestandardowe ustawienia (FFmpeg) nie podlegają żadnym ograniczeniom.\\n Rozważ użycie typu \\\"Standard\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Tryb wyjścia FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Wyjście do adresu URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Wyjście do pliku\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Popularne formaty plików nagrań\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Wszystkie pliki\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Ścieżka lub adres URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format pliku kontenera\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Dźwięk\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Obraz\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Format domyślny\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Opis formatu pliku kontenera\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Kodeki dźwięku i obrazu odczytane z pliku lub adresu URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Domyślny enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Enkoder nieaktywny\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Enkoder obrazu\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Ustawienia enkodera video (jeśli są)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Enkoder dźwięku\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Ustawienia enkodera audio (jeśli są)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Ustawienia muxera (jeżeli są)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Odstęp między klatkami kluczowymi (klatki)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Pokaż wszystkie kodeki (nawet potencjalnie niezgodne)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Ustawienia FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Automatyczny podział plików\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Podziel według czasu\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Podziel według rozmiaru\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Podziel ręcznie\"\nBasic.Settings.Output.SplitFile.Time=\"Czas podzielenia\"\nBasic.Settings.Output.SplitFile.Size=\"Rozmiar części\"\nScreenshot=\"Zrzut ekranu - ekran wyjściowy\"\nScreenshot.SourceHotkey=\"Zrzut ekranu - wybrane źródło\"\nScreenshot.StudioProgram=\"Zrzut ekranu (na żywo)\"\nScreenshot.Preview=\"Zrzut ekranu (podgląd)\"\nScreenshot.Scene=\"Zrzut ekranu (scena)\"\nScreenshot.Source=\"Zrzut ekranu (źródło)\"\nFilenameFormatting.TT.CCYY=\"Rok, cztery cyfry\"\nFilenameFormatting.TT.YY=\"Rok, ostatnie dwie cyfry (00-99)\"\nFilenameFormatting.TT.MM=\"Miesiąc, liczba dziesiętna (01-12)\"\nFilenameFormatting.TT.DD=\"Dzień miesiąca dwucyfrowo (01-31)\"\nFilenameFormatting.TT.hh=\"Godzina w formacie 24-godzinnym (00-23)\"\nFilenameFormatting.TT.mm=\"Minuta (00-59)\"\nFilenameFormatting.TT.ss=\"Sekundy (00-59)\"\nFilenameFormatting.TT.Percent=\"Znak %\"\nFilenameFormatting.TT.a=\"Dzień tygodnia - skrócony\"\nFilenameFormatting.TT.A=\"Dzień tygodnia - pełna nazwa\"\nFilenameFormatting.TT.b=\"Miesiąc - skrócona nazwa\"\nFilenameFormatting.TT.B=\"Miesiąc - pełna nazwa\"\nFilenameFormatting.TT.d=\"Dzień miesiąca dwucyfrowo (01-31)\"\nFilenameFormatting.TT.H=\"Godzina w formacie 24-godzinnym (00-23)\"\nFilenameFormatting.TT.I=\"Godzina w formacie 12-godzinnym (01-12)\"\nFilenameFormatting.TT.m=\"Miesiąc, liczba dziesiętna (01-12)\"\nFilenameFormatting.TT.M=\"Minuta (00-59)\"\nFilenameFormatting.TT.p=\"Oznaczenie przed/popołudnie (AM/PM)\"\nFilenameFormatting.TT.s=\"Znacznik czasu UNIX\"\nFilenameFormatting.TT.S=\"Sekundy (00-59)\"\nFilenameFormatting.TT.y=\"Rok, ostatnie dwie cyfry (00-99)\"\nFilenameFormatting.TT.Y=\"Rok\"\nFilenameFormatting.TT.z=\"Przesunięcie czasu ISO 8601 w stosunku do UTC\"\nFilenameFormatting.TT.Z=\"Nazwa lub skrót strefy czasowej\"\nFilenameFormatting.TT.FPS=\"Klatki na sekundę\"\nFilenameFormatting.TT.CRES=\"Rozdzielczość bazowa (obraz)\"\nFilenameFormatting.TT.ORES=\"Rozdzielczość wynikowa (skalowana)\"\nFilenameFormatting.TT.VF=\"Format wideo\"\nBasic.Settings.Video=\"Obraz\"\nBasic.Settings.Video.Adapter=\"Karta graficzna\"\nBasic.Settings.Video.BaseResolution=\"Rozdzielczość bazowa (obraz)\"\nBasic.Settings.Video.ScaledResolution=\"Rozdzielczość wynikowa (skalowana)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtr skalujący\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Rozdzielczości pasują, skalowanie nie jest wymagane]\"\nBasic.Settings.Video.FPS=\"FPS (klatki na sekundę)\"\nBasic.Settings.Video.FPSCommon=\"Typowe wartości FPS (kl./s)\"\nBasic.Settings.Video.FPSInteger=\"Całkowite wartości FPS (kl./s)\"\nBasic.Settings.Video.FPSFraction=\"Ułamkowe wartości FPS (kl./s)\"\nBasic.Settings.Video.Numerator=\"Licznik\"\nBasic.Settings.Video.Denominator=\"Mianownik\"\nBasic.Settings.Video.Renderer=\"Renderowanie\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (eksperymentalny)\"\nBasic.Settings.Video.InvalidResolution=\"Nieprawidłowa rozdzielczość. Wartość powinna mieć format [szerokość]x[wysokość] (np. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Wyjście wideo jest aktywne. Należy wyłączyć wszelkie wyjścia, aby zmienić ustawienia wideo.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (najszybsze, obraz rozmazany w przypadku skalowania)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (skalowanie ostrzejsze, 16 próbek)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (skalowanie ostrzejsze, 36 próbki)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Area (suma ważona, 4/6/9 próbek)\"\nBasic.Settings.Audio=\"Dźwięk\"\nBasic.Settings.Audio.SampleRate=\"Częstotliwość próbkowania\"\nBasic.Settings.Audio.Channels=\"Kanały\"\nBasic.Settings.Audio.Meters=\"Mierniki\"\nBasic.Settings.Audio.MeterDecayRate=\"Czas zanikania\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Szybko\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Średnio (PPM typu I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Wolno (PPM typu II)\"\nBasic.Settings.Audio.PeakMeterType=\"Typ szczytowego poziomu paska audio\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Poziom szczytowy próbkowany\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Rzeczywisty szczytowy poziom (większe użycie procesora)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"Uwaga: Dźwięk przestrzenny jest włączony.\"\nBasic.Settings.Audio.MultichannelWarning=\"W przypadku korzystania z transmisji strumieniowej należy sprawdzić, czy usługa ta obsługuje zarówno przesyłanie dźwięku przestrzennego surround, jak i jego odtwarzanie. Na przykład, Facebook 360 Live w pełni obsługuje dźwięk przestrzenny. YouTube Live obsługuje przesyłanie dźwięku 5.1 (i odtwarzanie na telewizorach). Filtry audio OBS są kompatybilne z dźwiękiem przestrzennym, choć obsługa wtyczek VST nie jest gwarantowana.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Włączyć dźwięk przestrzenny?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Czy na pewno chcesz włączyć dźwięk przestrzenny?\"\nBasic.Settings.Audio.Devices=\"Globalne urządzenia audio\"\nBasic.Settings.Audio.DesktopDevice=\"Urządzenie audio\"\nBasic.Settings.Audio.DesktopDevice2=\"Urządzenie audio 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/dodatkowe urządzenie audio\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/dodatkowe urządzenie audio 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/dodatkowe urządzenie audio 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/dodatkowe urządzenie audio 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Włącz \\\"naciśnij-aby-wyciszyć\\\"\"\nBasic.Settings.Audio.PushToMuteDelay=\"Opóźnienie \\\"naciśnij-aby-wyciszyć\\\"\"\nBasic.Settings.Audio.EnablePushToTalk=\"Włącz \\\"naciśnij-aby-mówić\\\"\"\nBasic.Settings.Audio.PushToTalkDelay=\"Opóźnienie \\\"naciśnij-aby-mówić\\\"\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Urządzenie niedostępne lub niepodłączone]\"\nBasic.Settings.Audio.Disabled=\"Wyłączone\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Tryb buforowania dźwięku o niskim opóźnieniu (dla wyjścia Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"UWAGA: Włączone buforowanie dźwięku z niskim opóźnieniem.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Tryb buforowania dźwięku o niskim opóźnieniu może spowodować błędy lub zatrzymanie odtwarzania z niektórych źródeł.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Włączyć tryb buforowania dźwięku o niskim opóźnieniu?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Czy na pewno chcesz włączyć tryb buforowania dźwięku o niskim opóźnieniu?\"\nBasic.Settings.Accessibility=\"Ułatwienia dostępu\"\nBasic.Settings.Accessibility.ColorOverrides=\"Użyj innych kolorów\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Granica źródła (wybrane)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Granica źródła (przycinanie)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Granica źródła (pod kursorem)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Kolor głośności dźwięku (-60 do -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Kolor głośności dźwięku (-20 do -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Kolor głośności dźwięku (-9 do 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Kolor głośności dźwięku (-60 do -20dB) (aktywny)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Kolor głośności dźwięku (-20 do -9dB) (aktywny)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Kolor głośności dźwięku (-9 do 0dB) (aktywny)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Zestaw kolorów\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Domyślny\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Własny\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Dla daltonistów\"\nBasic.Settings.Advanced=\"Zaawansowane\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Pokaż ostrzeżenie o aktywnych wyjściach przy zamykaniu aplikacji\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Priorytet procesu\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Wysoki\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Powyżej normalnego\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normalny\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Poniżej normalnego\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Bezczynny\"\nBasic.Settings.Advanced.FormatWarning=\"Ostrzeżenie: Formaty koloru inne niż NV12/P010 przeznaczone są głównie do nagrywania i nie są zalecane podczas przesyłania strumieniowego. Przekształcenie formatu kolorów w locie podczas przesyłania strumieniowego może zwiększyć obciążenie procesora.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Ostrzeżenie: Formaty o wysokiej precyzji są częściej używane z przestrzeniami kolorów HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Ostrzeżenie: Rec. 2100 powinno użyć formatu z większą dokładnością.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Format koloru\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bitowy, 4:2:0, 2 płaszczyzny)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bitowy, 4:2:0, 3 płaszczyzny)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bitowy, 4:4:4, 3 płaszczyzny)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bitowy, 4:2:0, 2 płaszczyzny)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bitowy, 4:2:0, 3 płaszczyzny)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bitowy, 4:2:2, 2 płaszczyzny)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bitowy, 4:4:4, 2 płaszczyzny)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-bitowy)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Przestrzeń kolorów\"\nBasic.Settings.Advanced.Video.ColorRange=\"Zakres kolorów\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Ograniczony\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Pełny\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR poziom bieli\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR nominalny poziom szczytowy\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Urządzenie do monitorowania audio\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Domyślne\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Wyłącz systemowe obniżanie głośności innych źródeł dźwięków\"\nBasic.Settings.Advanced.StreamDelay=\"Opóźnienie streamu\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Opóźnienie\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Zachowuj punkt przerwania (zwiększ opóźnienie) podczas ponownego łączenia\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Szacowane zużycie pamięci: %1 MB\"\nBasic.Settings.Advanced.Network=\"Sieć\"\nBasic.Settings.Advanced.Network.Disabled=\"Aktualnie wybrany protokół strumieniowania nie obsługuje zmiany ustawień sieci.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Przypisane IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Protokół IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Włącz sieciowe optymalizacje\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Włącz regulację tempa TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Próbuje sprawić, aby ruch RTMP był bardziej przyjazny innym aplikacjom wrażliwym na opóźnienie w sieci poprzez regulację szybkości transmisji.\\nMoże to zwiększyć ryzyko pomijania ramek na niestabilnych połączeniach.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Skróty klawiszowe w aktywnym oknie\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nigdy nie deaktywuj skrótów klawiszowych\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Wyłącz skróty klawiszowe, gdy główne okno programu jest aktywne na pierwszym planie\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Deaktywuj skróty klawiszowe, gdy okno nie jest aktywne na pierwszym planie\"\nBasic.Settings.Advanced.AutoRemux=\"Automatycznie przepakuj (remux) do %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(nagrywaj jako mkv)\"\nBasic.AdvAudio=\"Zaawansowane ustawienia dźwięku\"\nBasic.AdvAudio.ActiveOnly=\"Tylko aktywne źródła\"\nBasic.AdvAudio.Name=\"Nazwa\"\nBasic.AdvAudio.Volume=\"Głośność\"\nBasic.AdvAudio.VolumeSource=\"Głośność dla '%1'\"\nBasic.AdvAudio.MonoSource=\"Miks do mono dla '%1'\"\nBasic.AdvAudio.Balance=\"Balans\"\nBasic.AdvAudio.BalanceSource=\"Balans dla '%1'\"\nBasic.AdvAudio.SyncOffset=\"Przesunięcie dźwięku\"\nBasic.AdvAudio.SyncOffsetSource=\"Przesunięcie synchronizacji dla '%1'\"\nBasic.AdvAudio.Monitoring=\"Monitorowanie urządzenia audio\"\nBasic.AdvAudio.Monitoring.None=\"Wyłączone\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Tylko monitorowanie (wyjście wyłączone)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitorowanie i przekazywanie na wyjście\"\nBasic.AdvAudio.MonitoringSource=\"Monitorowanie dźwięku dla '%1'\"\nBasic.AdvAudio.AudioTracks=\"Ścieżki\"\nBasic.Settings.Hotkeys=\"Skróty klawiszowe\"\nBasic.Settings.Hotkeys.Pair=\"Komibnacje klawiszy wspólne z '%1' działają jako przełączniki\"\nBasic.Settings.Hotkeys.Filter=\"Filtruj\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtruj według skrótu klawiszowego\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Ten skrót klawiszowy jest przydzielony do jeszcze jednej lub więcej funkcji. Kliknij, aby wyświetlić konflikty.\"\nBasic.Settings.Hotkeys.PleaseWait=\"Ładowanie skrótów klawiszowych, proszę czekać...\"\nBasic.Hotkeys.SelectScene=\"Przełącz na scenę\"\nBasic.SystemTray.Show=\"Pokaż\"\nBasic.SystemTray.Hide=\"Ukryj\"\nBasic.SystemTray.Message.Reconnecting=\"Rozłączony. Łączę ponownie...\"\nHotkeys.Left=\"Strzałka w lewo\"\nHotkeys.Right=\"Strzałka w prawo\"\nHotkeys.Up=\"Strzałka w górę\"\nHotkeys.Down=\"Strzałka w dół\"\nHotkeys.Space=\"Spacja\"\nHotkeys.NumpadNum=\"%1 (Klawiatura numeryczna)\"\nHotkeys.NumpadMultiply=\"* (Klawiatura numeryczna)\"\nHotkeys.NumpadDivide=\"/ (Klawiatura numeryczna)\"\nHotkeys.NumpadAdd=\"+ (Klawiatura numeryczna)\"\nHotkeys.NumpadSubtract=\"- (Klawiatura numeryczna)\"\nHotkeys.NumpadDecimal=\". (Klawiatura numeryczna)\"\nHotkeys.MouseButton=\"Mysz %1\"\nMute=\"Wyciszenie\"\nUnmute=\"Wyłącz wyciszenie\"\nPush-to-mute=\"Naciśnij-aby-wyciszyć\"\nPush-to-talk=\"Naciśnij-aby-mówić\"\nSceneItemShow=\"Pokaż '%1'\"\nSceneItemHide=\"Ukryj '%1'\"\nOutputWarnings.NoTracksSelected=\"Musisz wybrać przynajmniej jedną ścieżkę\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Błąd ustawień wyjścia\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Wszystkie wyjścia muszą mieć wybraną przynajmniej jedną ścieżkę dźwiękową.\"\nOutputWarnings.MP4Recording=\"Ostrzeżenie: nagrania zapisanego w formacie MP4/MOV nie będzie można odzyskać, jeśli plik nie zostanie zakończony poprawnie (np. w wyniku BSOD, braku prądu, itp.). Jeśli chcesz nagrać wiele ścieżek audio, należy rozważyć użycie formatu MKV i przepakowanie nagrania do MP4/MOV po zakończeniu (Plik → Przepakuj nagrania).\"\nOutputWarnings.CannotPause=\"Ostrzeżenie: nagrywanie nie może być wstrzymane, gdy enkoder nagrywania ustawiony jest na \\\"(użyj enkodera streamu)\\\"\"\nOutputWarnings.CodecIncompatible=\"Wybór enkodera audio lub wideo został zresetowany ze względu na niekompatybilność z formatem nagrywania. Wybierz z listy obsługiwany enkoder.\"\nCodecCompat.Incompatible=\"(niekompatybilny z %1)\"\nCodecCompat.CodecPlaceholder=\"Wybierz enkoder...\"\nCodecCompat.ContainerPlaceholder=\"Wybierz format...\"\nCodecCompat.CodecMissingOnExit.Title=\"Nie wybrano enkodera\"\nCodecCompat.CodecMissingOnExit.Text=\"Przynajmniej jeden z enkoderów nie został wybrany. Sprawdź, czy wybrano enkoder zarówno do streamowania jak i nagrywania.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Nie wybrano formatu\"\nCodecCompat.ContainerMissingOnExit.Text=\"Nie wybrano formatu. Wybierz format nagrywania kompatybilny z enkoderem stramowania.\"\nFinalScene.Title=\"Usuń scenę\"\nFinalScene.Text=\"Musi być co najmniej jedna scena.\"\nNoSources.Title=\"Brak źródeł\"\nNoSources.Text=\"Wygląda na to, że nie dodano żadnych źródeł video. Na wyjściu otrzymasz tylko czarny ekran. Czy na pewno chcesz to zrobić?\"\nNoSources.Text.AddSource=\"Źródła dodać można w każdym momencie klikając przycisk + w okienku Źródła głównego ekranu aplikacji.\"\nNoSources.Label=\"Nie masz żadnych źródeł.\\nKliknij przycisk + poniżej lub kliknij tutaj\\n prawym przyciskiem myszy, aby dodać źródło.\"\nChangeBG=\"Ustaw kolor\"\nCustomColor=\"Kolor niestandardowy\"\nBrowserSource.EnableHardwareAcceleration=\"Włącz akcelerację sprzętową w pluginie przeglądarki\"\nAbout=\"O programie\"\nAbout.Info=\"OBS Studio to darmowe i otwarte oprogramowanie do nagrywania i transmitowania materiału wideo.\"\nAbout.Donate=\"Wpłać donację\"\nAbout.GetInvolved=\"Wesprzyj nas\"\nAbout.Authors=\"Autorzy\"\nAbout.License=\"Licencja\"\nAbout.Error=\"Błąd! Nie można odczytać pliku.\\n\\nIdź do: %1\"\nAbout.Contribute=\"Wesprzyj projekt\"\nAddUrl.Title=\"Dodaj źródło z adresu URL\"\nAddUrl.Text=\"Przeciągnąłeś adres URL do OBS. To automatycznie doda link jako źródło. Kontynuować?\"\nResizeOutputSizeOfSource=\"Zmień rozdzielczość wynikową (rozmiar źródła)\"\nResizeOutputSizeOfSource.Text=\"Rozdzielczość bazowa i wyjściowa zostaną zmienione do rozdzielczości bieżącego źródła.\"\nResizeOutputSizeOfSource.Continue=\"Czy chcesz kontynuować?\"\nPreviewTransition=\"Podgląd efektu przejścia\"\nImporter=\"Import zbiorów scen\"\nImporter.SelectCollection=\"Wybierz zbiór scen\"\nImporter.Collection=\"Zbiór scen\"\nImporter.HelpText=\"Dodaj pliki do tego okna, aby importować zbiory scen z OBS lub innych wspieranych programów.\"\nImporter.Path=\"Ścieżka\"\nImporter.Program=\"Wykryta aplikacja\"\nImporter.AutomaticCollectionPrompt=\"Automatycznie wyszukuj zbiory scen\"\nImporter.AutomaticCollectionText=\"OBS może automatycznie znaleźć importowalne zbiory scen z obsługiwanych aplikacji innych firm. Czy chcesz, aby OBS automatycznie znalazł takie zbiory?\\n\\nMożesz to zmienić później w Ustawienia > Główne > Importowanie.\"\nImporter.SelectFile=\"Przeglądaj...\"\nNeedsRestart=\"Należy ponownie uruchomić OBS Studio. Czy chcesz teraz zrestartować?\"\nLoadProfileNeedsRestart=\"Profil zawiera ustawienia, które wymagają ponownego uruchomienia OBS:\\n%1\\n\\nCzy chcesz zrestartować OBS, aby te ustawienia zaczęły obowiązywać?\"\nContextBar.NoSelectedSource=\"Nie wybrano źródła\"\nContextBar.MediaControls.PlayMedia=\"Odtwarzaj\"\nContextBar.MediaControls.PauseMedia=\"Pauza\"\nContextBar.MediaControls.StopMedia=\"Zatrzymaj\"\nContextBar.MediaControls.RestartMedia=\"Restartuj\"\nContextBar.MediaControls.PlaylistNext=\"Następny na liście odtwarzania\"\nContextBar.MediaControls.PlaylistPrevious=\"Poprzedni na liście odtwarzania\"\nContextBar.MediaControls.BlindSeek=\"Pasek postępu odtwarzania\"\nYouTube.Auth.Ok=\"Autoryzacja zakończona pomyślnie.\\nMożesz teraz zamknąć tę stronę.\"\nYouTube.Auth.NoCode=\"Proces autoryzacji nie został zakończony.\"\nYouTube.Auth.NoChannels=\"Brak dostępnych kanałów na wybranym koncie\"\nYouTube.Auth.WaitingAuth.Title=\"Autoryzacja użytkownika YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Dokonaj autoryzacji w zewnętrznej przeglądarce.<br>Jeśli zewnętrzna przeglądarka nie otworzyła się, użyj poniższego linku i dokończ autoryzację:<br>%1\"\nYouTube.AuthError.Text=\"Nie udało się pobrać informacji o kanale: %1.\"\nYouTube.Actions.WindowTitle=\"Konfiguracja transmisji YouTube - Kanał: %1\"\nYouTube.Actions.CreateNewEvent=\"Utwórz nową transmisję\"\nYouTube.Actions.ChooseEvent=\"Wybierz istniejącą transmisję\"\nYouTube.Actions.Title=\"Tytuł*\"\nYouTube.Actions.MyBroadcast=\"Moja transmisja\"\nYouTube.Actions.Description=\"Opis\"\nYouTube.Actions.Privacy=\"Widoczność*\"\nYouTube.Actions.Privacy.Private=\"Prywatny\"\nYouTube.Actions.Privacy.Public=\"Publiczny\"\nYouTube.Actions.Privacy.Unlisted=\"Niepubliczny\"\nYouTube.Actions.Category=\"Kategoria\"\nYouTube.Actions.Thumbnail=\"Miniaturka\"\nYouTube.Actions.Thumbnail.SelectFile=\"Wybierz plik...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Nie wybrano pliku\"\nYouTube.Actions.Thumbnail.ClearFile=\"Wyczyść\"\nYouTube.Actions.MadeForKids=\"Czy film przeznaczony jest dla dzieci?*\"\nYouTube.Actions.MadeForKids.Yes=\"Tak, jest przeznaczony dla dzieci\"\nYouTube.Actions.MadeForKids.No=\"Nie, nie jest przeznaczony dla dzieci\"\nYouTube.Actions.AdditionalSettings=\"Ustawienia dodatkowe\"\nYouTube.Actions.Latency=\"Opóźnienie\"\nYouTube.Actions.Latency.Normal=\"Normalne\"\nYouTube.Actions.Latency.Low=\"Niskie\"\nYouTube.Actions.Latency.UltraLow=\"Ultra niskie\"\nYouTube.Actions.EnableAutoStart=\"Włącz auto-start\"\nYouTube.Actions.EnableAutoStop=\"Włącz auto-stop\"\nYouTube.Actions.AutoStartStop.TT=\"Wskazuje, czy ta zaplanowana transmisja powinna uruchamiać się automatycznie\"\nYouTube.Actions.EnableDVR=\"Włącz DVR\"\nYouTube.Actions.360Video=\"Wideo 360 stopni\"\nYouTube.Actions.ScheduleForLater=\"Zaplanuj na później\"\nYouTube.Actions.RememberSettings=\"Zapamiętaj ustawienia\"\nYouTube.Actions.Create_Ready=\"Utwórz transmisję\"\nYouTube.Actions.Create_GoLive=\"Utwórz transmisję i zacznij nadawać\"\nYouTube.Actions.Choose_Ready=\"Wybierz transmisję\"\nYouTube.Actions.Choose_GoLive=\"Wybierz transmisję i zacznij nadawać\"\nYouTube.Actions.Create_Schedule=\"Zaplanuj transmisję\"\nYouTube.Actions.Create_Schedule_Ready=\"Zaplanuj i wybierz transmisję\"\nYouTube.Actions.Dashboard=\"Otwórz YouTube Studio\"\nYouTube.Actions.Error.Title=\"Błąd tworzenia transmisji na żywo\"\nYouTube.Actions.Error.Text=\"Błąd dostępu do YouTube '%1'.<br/>Dokładny opis błędu znajdziesz pod <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Błąd dostępu do YouTube. Sprawdź połączenie sieciowe lub dostęp do serwera YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Błąd tworzenia transmisji '%1'.<br/>Dokładny opis błędu znajdziesz pod <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Nie utworzono strumienia. Proszę połącz ponownie swoje konto.\"\nYouTube.Actions.Error.YouTubeApi=\"Błąd API YouTube. Aby uzyskać więcej informacji, przeglądnij treść pliku dziennika.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Wybrana transmisja nie została odnaleziona.\"\nYouTube.Actions.Error.FileMissing=\"Wybrany plik nie istnieje.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Nie udało się otworzyć wybranego pliku.\"\nYouTube.Actions.Error.FileTooLarge=\"Wybrany plik jest zbyt duży (limit: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Zmiana stanu transmisji nie powiodła się: %1<br/><br/>Jeśli ten błąd będzie się powtarzał, <a href='https://studio.youtube.com/video/%2/livestreaming'>otwórz transmisję w YouTube Studio</a> i spróbuj zmiany ręcznie.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Transmisja zmienia stan na testowy. Może to zająć trochę czasu. Spróbuj ponownie za 10-30 sekund.\"\nYouTube.Actions.EventsLoading=\"Ładuję listę wydarzeń...\"\nYouTube.Actions.EventCreated.Title=\"Wydarzenie zostało utworzone\"\nYouTube.Actions.EventCreated.Text=\"Wydarzenie zostało utworzone.\"\nYouTube.Actions.Stream=\"Transmisja\"\nYouTube.Actions.Stream.ScheduledFor=\"Zaplanowane na %1\"\nYouTube.Actions.Stream.Resume=\"Wznów przerwaną transmisję\"\nYouTube.Actions.Stream.YTStudio=\"Automatycznie utworzone przez YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Tworzenie nowej transmisji na żywo, proszę czekać...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Wymagany ręczny start\"\nYouTube.Actions.AutoStartStreamingWarning=\"Automatyczny start dla tego wydarzenia jest wyłączony. Kliknij \\\"Nadawaj\\\", aby rozpocząć transmisję.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Nie będzie możliwe wznowienie połączenia.<br>Twoja transmisja zatrzyma się i nie będziesz już nadawać.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Nadawanie na żywo nie jest włączone na wybranym kanale YouTube.<br/><br/>Przejdź na <a href='https://www.youtube.com/features'>youtube.com/features</a> po więcej informacji.\"\nYouTube.Errors.livePermissionBlocked=\"Nadawanie na żywo jest niedostępne na wybranym kanale YouTube.<br/>Pamiętaj, że po włączeniu możliwości nadawania na żywo w ustawieniach kanału może minąć do 24 godzin zanim opcja będzie dostępna.<br/><br/>Przejdź na <a href='https://www.youtube.com/features'>youtube.com/features</a> po szczegóły.\"\nYouTube.Errors.errorExecutingTransition=\"Zmiana stanu streamu nie powiodła się z powodu błędu serwera. Spróbuj ponownie za kilka sekund.\"\nYouTube.Errors.errorStreamInactive=\"YouTube nie odbiera danych Twojego strumienia. Sprawdź konfigurację i spróbuj ponownie.\"\nYouTube.Errors.invalidTransition=\"Próba zmiany stanu nie powiodła się. Może to być spowodowane faktem, iż nie zakończyła się jeszcze poprzednia zmiana stanu. Poczekaj kilka sekund i spróbuj ponownie.\"\nYouTube.DocksRemoval.Title=\"Wyczyść przestarzałe panele przeglądarkowe YouTube \"\nYouTube.DocksRemoval.Text=\"Poniższe przestarzałe panele przeglądarkowe zostaną usunięte.\\n\\n%1\\nZamiast tego użyj \\\"Panele/YouTube Live Control Room\\\".\"\nConfigDownload.WarningMessageTitle=\"Uwaga\"\nFailedToStartStream.MissingConfigURL=\"Brak adresu URL z konfiguracją dla wybranego serwisu\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Nie określono własnego adresu URL RTMP\"\nFailedToStartStream.InvalidCustomConfig=\"Nieprawidłowa własna konfiguracja\"\nFailedToStartStream.MissingCanvas=\"Brak skonfigurowanego widoku\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Nie udało się stworzyć usługi wideo z wieloma ścieżkami\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Nie udało się stworzyć wyjścia RTMP z wideo z wieloma ścieżkami\"\nFailedToStartStream.EncoderNotAvailable=\"Brak NVENC.\\n\\nNie znaleziono enkodera typu '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Nie udało się stworzyć enkodera wideo '%1' (typ: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Nie udało się pobrać informacji o wideo OBS przy tworzeniu enkodera '%1' (typ: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Nie udało się stworzyć enkodera audio\"\nFailedToStartStream.NoRTMPURLInConfig=\"Konfiguracja nie zawiera adresu URL z serwerem RTMP(S).\"\nFailedToStartStream.FallbackToDefault=\"Nie udało się uruchomić strumieniowania korzystając z %1. Czy chcesz spróbować korzystając z ustawień dla pojedynczego enkodowania?\"\nFailedToStartStream.ConfigRequestFailed=\"Nie udało się pobrać konfiguracji z %1<br><br>Błąd HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Otrzymano nieznany status: '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nCzy chcesz kontynuować bez %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nCzy chesz kontynuować?\"\nFailedToStartStream.MissingEncoderConfigs=\"Konfiguracja nie zawiera opcji dla enkodera\"\nFailedToStartStream.InvalidEncoderConfig=\"Konfiguracja nie zawiera poprawnych ustawień enkodera\"\nFailedToStartStream.StatusMissingHTML=\"Request zwrócił nieokreślony błąd\"\nFailedToStartStream.NoConfigSupplied=\"Brak konfiguracji\"\nMultitrackVideo.Info=\"%1 automatycznie optymalizuje ustawienia, aby enkodować i wysyłać wideo w różnych poziomach jakości. Wybranie tej opcji wyśle do %2 informacje o konfiguracji Twojego komputera i oprogramowania.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Niezgodne ustawienia\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 nie jest kompatybilny z:\\n\\n%2\\nWyłącz nieprawidłowe opcje w ustawieniach, aby kontynuować korzystanie z %1:\\n\\n%3\\ni uruchom transmisję jeszcze raz.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Wyłącz dla tego streamu i uruchom transmisję\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Aktualizuj ustawienia i uruchom transmisję\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"Brak kompatybilności %1 z ustawieniem [Dźwięk →  Główne → Kanały] na '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Dźwięk →  Główne → Kanały] powinno być ustawione na '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 wymaga wielu różnych ustawień w [Dźwięk →  Główne → Kanały]\"\nBasic.OpenPluginManager=\"Menedżer wtyczek\"\nPluginManager=\"Menedżer wtyczek\"\nPluginManager.Restart=\"Aby zastosować zmiany wtyczki, należy ponownie uruchomić OBS Studio\"\nPluginManager.MissingPlugin=\"[NIE ZNALEZIONO WTYCZKI]\"\nPluginManager.Section.Discover=\"Przeglądaj\"\nPluginManager.Section.Manage=\"Zainstalowana\"\nPluginManager.Section.Updates=\"Aktualizacje\"\nPluginManager.Section.Manage.Title=\"Zarządzaj włączonymi wtyczkami\"\n"
  },
  {
    "path": "frontend/data/locale/pt-BR.ini",
    "content": "Language=\"Português brasileiro\"\nApply=\"Aplicar\"\nCancel=\"Cancelar\"\nClose=\"Fechar\"\nSave=\"Salvar\"\nDiscard=\"Descartar\"\nDisable=\"Desativar\"\nYes=\"Sim\"\nNo=\"Não\"\nAdd=\"Adicionar\"\nRemove=\"Remover\"\nRename=\"Renomear\"\nInteract=\"Interagir\"\nFilters=\"Filtros\"\nProperties=\"Propriedades\"\nMoveUp=\"Mover para cima\"\nMoveDown=\"Mover para baixo\"\nSettings=\"Configurações\"\nDisplay=\"Monitor\"\nName=\"Nome\"\nExit=\"Encerrar OBS\"\nMixer=\"Mixer de áudio\"\nBrowse=\"Explorar\"\nStereo=\"Estéreo\"\nDroppedFrames=\"Quadros perdidos %1 (%2%)\"\nProjector.Open.Program=\"Projetar programa\"\nProjector.Open.Preview=\"Projetar prévia\"\nProjector.Open.Scene=\"Projetar cena\"\nProjector.Open.Source=\"Projetar fonte\"\nProjector.Open.Multiview=\"Abrir visualização múltipla\"\nProjector.Display=\"Monitor: %1\"\nProjector.Window=\"Nova janela\"\nProjector.Title=\"Projetor\"\nProjector.Title.Scene=\"Cena: %1\"\nProjector.Title.Source=\"Fonte: %1\"\nProjector.Title.Multiview=\"Visualização múltipla\"\nProjector.ResizeWindowToContent=\"Ajustar janela ao conteúdo\"\nClear=\"Limpar\"\nRevert=\"Desfazer\"\nShow=\"Mostrar\"\nHide=\"Ocultar\"\nUnhideAll=\"Mostrar tudo\"\nUntitled=\"Sem nome\"\nNew=\"Novo\"\nDuplicate=\"Duplicar\"\nEnable=\"Ativar\"\nDisableOSXVSync=\"Desativar sincronização vertical do macOS\"\nResetOSXVSyncOnExit=\"Redefinir sincronização vertical do macOS ao encerrar o OBS\"\nHighResourceUsage=\"Codificação sobrecarregada! Considere reduzir as configurações de vídeo ou usar um padrão de codificação mais rápido.\"\nTransition=\"Transição\"\nQuickTransitions=\"Transições rápidas\"\nFadeToBlack=\"Desvanecer\"\nLeft=\"Esquerda\"\nRight=\"Direita\"\nTop=\"Cima\"\nBottom=\"Baixo\"\nReset=\"Redefinir\"\nHours=\"Horas\"\nMinutes=\"Minutos\"\nSeconds=\"Segundos\"\nDeprecated=\"Obsoleto\"\nReplayBuffer=\"Buffer de repetição\"\nImport=\"Importar\"\nExport=\"Exportar\"\nCopy=\"Copiar\"\nPaste=\"Colar\"\nPasteReference=\"Colar (referência)\"\nPasteDuplicate=\"Colar (duplicar)\"\nRemuxRecordings=\"Converter gravações\"\nNext=\"Avançar\"\nBack=\"Voltar\"\nDefaults=\"Redefinir\"\nRestoreDefaults=\"Redefinir\"\nHideMixer=\"Ocultar no mixer\"\nTransitionOverride=\"Sobrepor transição\"\nShowTransition=\"Transição ao mostrar\"\nHideTransition=\"Transição ao ocultar\"\nNone=\"Nenhuma\"\nStudioMode.Preview=\"Pré-visualização\"\nStudioMode.Program=\"Programa\"\nStudioMode.PreviewSceneName=\"Pré-visualização: %1\"\nStudioMode.ProgramSceneName=\"Programa: %1\"\nShowInMultiview=\"Mostrar em visualização múltipla\"\nVerticalLayout=\"Disposição vertical\"\nGroup=\"Grupo\"\nDoNotShowAgain=\"Não mostrar novamente\"\nDefault=\"(Padrão)\"\nCalculating=\"Calculando...\"\nFullscreen=\"Tela cheia\"\nWindowed=\"Janela\"\nRefreshBrowser=\"Atualizar\"\nAspectRatio=\"Proporção da tela <b>%1:%2</b>\"\nLockVolume=\"Travar volume\"\nLogViewer=\"Visualizador de registros\"\nShowOnStartup=\"Mostrar na inicialização\"\nOpenFile=\"Abrir arquivo\"\nAddScene=\"Adicionar cena\"\nAddSource=\"Adicionar fonte\"\nRemoveScene=\"Excluir cena selecionada\"\nRemoveSource=\"Remover fonte(s) selecionada(s)\"\nMoveSceneUp=\"Mover cena para cima\"\nMoveSceneDown=\"Mover cena para baixo\"\nMoveSourceUp=\"Mover fonte(s) para cima\"\nMoveSourceDown=\"Mover fonte(s) para baixo\"\nSourceProperties=\"Abrir propriedades da fonte\"\nSourceFilters=\"Abrir propriedades do filtro\"\nMixerToolbarMenu=\"Menu de mixagem\"\nSceneFilters=\"Abrir filtros de cena\"\nList=\"Lista\"\nGrid=\"Grade\"\nAutomatic=\"Automático\"\nComingSoon=\"Em breve\"\nPluginsFailedToLoad.Title=\"Erro ao carregar o plugin\"\nPluginsFailedToLoad.Text=\"Não foi possível carregar os seguintes plugins do OBS:\\n\\n%1\\nPor favor, atualize ou remova estes plugins.\"\nAlreadyRunning.Title=\"OBS já está em execução\"\nAlreadyRunning.Text=\"OBS já está em execução! A menos que você tenha a intenção de fazer isso, por favor, feche todas as instâncias existentes do OBS antes de tentar executar uma nova. Se você tiver definido para minimizar o OBS na bandeja do sistema, verifique se ainda está lá em execução.\"\nAlreadyRunning.LaunchAnyway=\"Executar mesmo assim\"\nCrashHandling.Dialog.Title=\"Falha detectada no OBS Studio\"\nCrashHandling.Labels.Text=\"O OBS Studio não foi fechado corretamente.\\n\\nDeseja executá-lo no Modo Seguro (plugins de terceiros, scripts e WebSockets desativados)?\"\nCrashHandling.Labels.PrivacyNotice=\"Você também pode enviar automaticamente o mais recente relatório de falha para o OBSProject.<br /><br />Leia a <a href='https://obsproject.com/privacy-policy'>Política de Privacidade</a> antes de enviar qualquer arquivo e preste atenção nas partes referentes ao envio de arquivos.\"\nCrashHandling.Checkbox.SendReport=\"Eu li a Política de Privacidade e permito o envio.\"\nCrashHandling.Buttons.LaunchSafe=\"Executar no modo de segurança\"\nCrashHandling.Buttons.LaunchNormal=\"Executar no modo normal\"\nCrashHandling.Errors.UploadJSONError=\"Houve um erro ao tentar enviar o registro de falha mais recente. Por favor, tente de novo mais tarde.\"\nCrashHandling.Errors.Title=\"Erro no envio do registro de falha\"\nSafeMode.Restart=\"Deseja reiniciar o OBS no modo de segurança (plugins de terceiros, scripts e WebSockets desativados)?\"\nSafeMode.RestartNormal=\"Deseja reiniciar o OBS no modo normal?\"\nChromeOS.Title=\"Plataforma não suportada\"\nChromeOS.Text=\"Aparentemente, o OBS está sendo executado em um Chrome OS. Esta plataforma não tem suporte.\"\nWine.Title=\"Wine detectado\"\nWine.Text=\"Não há suporte para executar OBS no Wine e muitos recursos como fontes de captura ou dispositivo não funcionarão ou terão capacidade limitada.<br><br>É recomendado executar uma versão nativa do OBS em vez disso, por exemplo <a href='https://flathub.org/apps/details/com.obsproject.Studio'>nossa versão do Flatpak</a> ou instalando a partir de pacotes do seu sistema operacional.\"\nDockCloseWarning.Title=\"Fechar janela de painel\"\nDockCloseWarning.Text=\"Você acabou de fechar um painel que estava em uma janela. Se você gostaria de mostrá-lo novamente, acesse o menu de Painéis na barra de menu.\"\nExtraBrowsers=\"Painéis personalizáveis com URL\"\nExtraBrowsers.Info=\"Adicione painéis fornecendo um nome e URL e então clique em Aplicar ou Fechar para abri-lo. Você pode adicionar ou remover os painéis a qualquer momento.\"\nExtraBrowsers.DockName=\"Nome do painel\"\nAuth.Authing.Title=\"Autenticando...\"\nAuth.Authing.Text=\"Autenticando com %1, aguarde...\"\nAuth.AuthFailure.Title=\"Falha na autenticação\"\nAuth.AuthFailure.Text=\"Não foi possível autenticar com %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Autenticação necessária\"\nAuth.InvalidScope.Text=\"Os requisitos de autenticação do(a) %1 foram alterados. Alguns recursos podem não estar disponíveis.\"\nAuth.LoadingChannel.Title=\"Carregando informações do canal...\"\nAuth.LoadingChannel.Text=\"Carregando informações do canal do(a) %1, aguarde...\"\nAuth.LoadingChannel.Error=\"Não foi possível obter informações do canal.\"\nAuth.ChannelFailure.Title=\"Não foi possível carregar o canal\"\nAuth.ChannelFailure.Text=\"Não foi possível carregar informações do canal do(a) %1\\n\\n%2: %3\"\nAuth.StreamInfo=\"Informações da transmissão\"\nTwitchAuth.Stats=\"Estatísticas da Twitch\"\nTwitchAuth.Feed=\"Feed de atividade da Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Não foi possível consultar a chave de transmissão\"\nTwitchAuth.TwoFactorFail.Text=\"Não foi possível conectar à sua conta do Twitch. Por favor, certifique-se de que a autenticação de dois fatores está configurada em suas <a href='https://www.twitch.tv/settings/security'>Configurações de segurança da Twitch</a> pois isso é necessário para transmitir.\"\nRestreamAuth.Channels=\"Canais do Restream\"\nCopy.Filters=\"Copiar filtros\"\nPaste.Filters=\"Colar filtros\"\nBrowserPanelInit.Title=\"Inicializando o navegador...\"\nBrowserPanelInit.Text=\"Inicializando o navegador, aguarde...\"\nBandwidthTest.Region=\"Região\"\nBandwidthTest.Region.US=\"Estados Unidos\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Ásia\"\nBandwidthTest.Region.Other=\"Outra\"\nBasic.AutoConfig=\"Assistente de configuração\"\nBasic.AutoConfig.ApplySettings=\"Aplicar configurações\"\nBasic.AutoConfig.StartPage=\"Informações de uso\"\nBasic.AutoConfig.StartPage.SubTitle=\"Marque para que você deseja usar o programa\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Otimizar para transmissão, gravação é opcional\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Otimizar somente para gravação, eu não farei transmissão\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Usarei apenas a câmera virtual\"\nBasic.AutoConfig.VideoPage=\"Configurações de vídeo\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Especifique as configurações de vídeo que você gostaria de usar\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Usar atual (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Monitor %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Usar atual (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ou 30, mas prefiro 60 quando possível\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ou 30, mas prefiro alta resolução\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Nota: A resolução da área de edição (base) não é necessariamente a mesma resolução que você transmitirá ou gravará. A resolução final da sua transmissão ou gravação pode ser reduzida em relação à resolução da tela para reduzir o uso de recursos ou taxa de bits necessários.\"\nBasic.AutoConfig.StreamPage=\"Informações da transmissão\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Por favor, digite suas informações de transmissão\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Conectar conta (recomendado)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Desconectar conta\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Desconectar conta?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Esta mudança será aplicada imediatamente. Deseja mesmo desconectar sua conta?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Obter chave de transmissão\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Mais informações\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Usar chave de transmissão\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Usar chave de transmissão (avançado)\"\nBasic.AutoConfig.StreamPage.Service=\"Serviço\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Mostrar todos...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Personalizado...\"\nBasic.AutoConfig.StreamPage.Server=\"Servidor\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Chave de transmissão\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: insira a frase de criptografia.\\nRTMP: insira a chave fornecida pelo serviço.\\nSRT: insira o streamid se o serviço usar um.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Chave do codificador\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Token Bearer\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Conta conectada\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Estimar taxa de bits com teste de largura de banda (pode levar uns minutos)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferir codificação por hardware\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Codificação por hardware elimina boa parte do uso de CPU, mas pode exigir uma taxa de bits maior para obter o mesmo nível de qualidade.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Aviso de transmissão\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"O teste de largura de banda está prestes a transmitir imagens aleatórias e sem áudio para o seu canal. Se for possível, é recomendável desativar temporariamente a gravação da transmissão e definir a transmissão como privada até que o teste seja concluído. Continuar?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Testar %1\"\nBasic.AutoConfig.TestPage=\"Resultados\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"O programa está executando um conjunto de testes para estimar as configurações ideais\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Teste completo\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Executando teste de largura de banda, isto pode levar alguns minutos...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Não foi encontrada nenhuma saída para o protocolo deste serviço\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Conectando a: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Não foi possível conectar nenhum servidor, verifique sua conexão com a internet e tente novamente.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testando largura de banda para: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testando codificador de transmissão, isto pode levar um minuto...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testando codificador de gravação, isto pode levar um minuto...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Falha ao iniciar o codificador\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testando %1x%2 a %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Codificador da transmissão\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Codificador da gravação\"\nBasic.AutoConfig.TestPage.Result.Header=\"O programa recomenda as seguintes configurações pra você:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Para usar essas configurações, clique em Aplicar configurações. Para reconfigurar o assistente e tentar novamente, clique em Voltar. Para definir manualmente, clique em Cancelar e abra as Configurações.\"\nBasic.AutoConfig.Info=\"O assistente de configuração irá determinar as melhores configurações baseadas nas especificações do seu computador e na velocidade da internet.\"\nBasic.AutoConfig.RunAnytime=\"Isto pode ser executado a qualquer momento no menu Ferramentas.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Resolução de transmissão (escalonada)\"\nBasic.Stats=\"Estatísticas\"\nBasic.Stats.CPUUsage=\"Uso de CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Espaço em disco disponível\"\nBasic.Stats.MemoryUsage=\"Uso de memória\"\nBasic.Stats.AverageTimeToRender=\"Tempo médio para renderizar um quadro\"\nBasic.Stats.SkippedFrames=\"Quadros ignorados devido à demora na codificação\"\nBasic.Stats.MissedFrames=\"Quadros perdidos devido à demora na renderização\"\nBasic.Stats.Output.Stream=\"Transmissão\"\nBasic.Stats.Output.Recording=\"Gravação\"\nBasic.Stats.Status.Recording=\"Gravando\"\nBasic.Stats.Status.Live=\"NO AR\"\nBasic.Stats.Status.Reconnecting=\"Reconectando\"\nBasic.Stats.Status.Inactive=\"Inativa\"\nBasic.Stats.Status.Active=\"Ativa\"\nBasic.Stats.DroppedFrames=\"Quadros perdidos (rede)\"\nBasic.Stats.MegabytesSent=\"Saída total de dados\"\nBasic.Stats.Bitrate=\"Taxa de bits\"\nBasic.Stats.DiskFullIn=\"Disco cheio em (aprox.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 horas e %2 minutos\"\nBasic.Stats.ResetStats=\"Redefinir status\"\nResetUIWarning.Title=\"Deseja mesmo redefinir a interface?\"\nResetUIWarning.Text=\"Redefinir a interface irá ocultar os painéis extras. Você precisará ir no menu Painéis se quiser que eles se tornem visíveis novamente.\\n\\nDeseja mesmo redefinir a interface?\"\nUpdater.Title=\"Nova atualização disponível\"\nUpdater.Text=\"Há uma nova atualização disponível:\"\nUpdater.UpdateNow=\"Atualizar agora\"\nUpdater.RemindMeLater=\"Lembre-me depois\"\nUpdater.Skip=\"Ignorar\"\nUpdater.NoUpdatesAvailable.Title=\"Nenhuma atualização disponível\"\nUpdater.NoUpdatesAvailable.Text=\"Nenhuma atualização disponível no momento\"\nUpdater.BranchNotFound.Title=\"Canal de atualização removido\"\nUpdater.BranchNotFound.Text=\"Seu canal de atualização selecionado não está mais disponível e o OBS foi redefinido para o padrão.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Verificação de integridade indisponível\"\nUpdater.RepairButUpdatesAvailable.Text=\"Verificar a integridade do arquivo só é possível para a versão mais recente disponível. Vá até Ajuda → Verificar atualizações para verificar e atualizar sua instalação do OBS.\"\nUpdater.RepairConfirm.Title=\"Confirmar verificação de integridade\"\nUpdater.RepairConfirm.Text=\"Iniciar a verificação de integridade determinará se a instalação do OBS está corrompida e baixará novamente os arquivos corrompidos/modificados. Isso pode demorar um pouco.\\n\\nDeseja continuar?\"\nUpdater.FailedToLaunch=\"Falha ao iniciar o atualizador\"\nQuickTransitions.SwapScenes=\"Alternar cenas de pré-visualização/programa após uma transição\"\nQuickTransitions.SwapScenesTT=\"Troca as cenas de pré-visualização e programa após a transição (se a cena original do programa ainda existir).\\nIsso não desfaz nenhuma alteração que possa ter sido feita na cena original do programa.\"\nQuickTransitions.DuplicateScene=\"Duplicar cena\"\nQuickTransitions.DuplicateSceneTT=\"Ao editar a mesma cena, permite editar a transformação/visibilidade das fontes sem modificar a saída do programa.\\nPara editar as propriedades das fontes sem modificar a saída do programa, habilite 'Duplicar fontes'.\\nAlterar este valor irá redefinir a cena atual do programa (se ainda existir).\"\nQuickTransitions.EditProperties=\"Duplicar fontes\"\nQuickTransitions.EditPropertiesTT=\"Ao editar a mesma cena, permite editar as propriedades das fontes sem modificar a saída do programa.\\nIsso só pode ser usado se 'Duplicar fontes' estiver ativada.\\nAlgumas fontes (como captura ou fontes de mídia) não suportam isso e não podem ser editadas separadamente.\\nAlterar este valor irá redefinir a cena atual do programa (se ainda existir).\\n\\nAviso: Como as fontes serão duplicadas, isso pode exigir recursos extras de sistema ou vídeo.\"\nQuickTransitions.HotkeyName=\"Transição rápida: %1\"\nBasic.AddTransition=\"Adicionar transição configurável\"\nBasic.RemoveTransition=\"Remover transição configurável\"\nBasic.TransitionProperties=\"Propriedades da transição\"\nBasic.SceneTransitions=\"Transições de cena\"\nBasic.TransitionDuration=\"Duração\"\nBasic.TogglePreviewProgramMode=\"Modo estúdio\"\nBasic.EnablePreviewProgramMode=\"Ativar modo estúdio\"\nBasic.DisablePreviewProgramMode=\"Desativar modo estúdio\"\nUndo.Undo=\"Desfazer\"\nUndo.Redo=\"Refazer\"\nUndo.Add=\"adição de \\\"%1\\\"\"\nUndo.Delete=\"exclusão de \\\"%1\\\"\"\nUndo.Rename=\"renomeação de \\\"%1\\\"\"\nUndo.SceneCollection.Switch=\"alteração para \\\"%1\\\"\"\nUndo.Item.Undo=\"Desfazer %1\"\nUndo.Item.Redo=\"Refazer %1\"\nUndo.Sources.Multi=\"exclusão de %1 fontes\"\nUndo.Filters=\"mudanças filtradas em \\\"%1\\\"\"\nUndo.Filters.Paste.Single=\"filtro copiado de \\\"%1\\\" para \\\"%2\\\"\"\nUndo.Filters.Paste.Multiple=\"filtros copiados de \\\"%1\\\" para \\\"%2\\\"\"\nUndo.Transform=\"fonte(s) transformada(s) em \\\"%1\\\"\"\nUndo.Transform.Paste=\"transformação colada em \\\"%1\\\"\"\nUndo.Transform.Rotate=\"rotação em \\\"%1\\\"\"\nUndo.Transform.Reset=\"redefinição de transformação em \\\"%1\\\"\"\nUndo.Transform.HFlip=\"inversão horizontal em \\\"%1\\\"\"\nUndo.Transform.VFlip=\"inversão vertical em \\\"%1\\\"\"\nUndo.Transform.FitToScreen=\"ajuste à tela em \\\"%1\\\"\"\nUndo.Transform.StretchToScreen=\"esticada à tela em \\\"%1\\\"\"\nUndo.Transform.Center=\"centralização na tela em \\\"%1\\\"\"\nUndo.Transform.VCenter=\"centralização vertical na tela em \\\"%1\\\"\"\nUndo.Transform.HCenter=\"centralização horizontal na tela em \\\"%1\\\"\"\nUndo.Volume.Change=\"mudança de volume em \\\"%1\\\"\"\nUndo.Volume.Mute=\"ativar mudo de \\\"%1\\\"\"\nUndo.Volume.Unmute=\"desativar mudo de \\\"%1\\\"\"\nUndo.Balance.Change=\"alteração no balanço de áudio em '%1'\"\nUndo.SyncOffset.Change=\"alteração no atraso na sincronização do áudio em \\\"%1\\\"\"\nUndo.MonitoringType.Change=\"alteração do monitoramento de áudio em \\\"%1\\\"\"\nUndo.Mixers.Change=\"alteração dos mixers em \\\"%1\\\"\"\nUndo.ForceMono.On=\"ativação do mono forçado em \\\"%1\\\"\"\nUndo.ForceMono.Off=\"desativação do mono forçado em \\\"%1\\\"\"\nUndo.Properties=\"mudança de propriedade em \\\"%1\\\"\"\nUndo.Scene.Duplicate=\"duplicação da cena \\\"%1\\\"\"\nUndo.ShowTransition=\"mostrar transição em \\\"%1\\\"\"\nUndo.HideTransition=\"ocultar transição em \\\"%1\\\"\"\nUndo.ShowSceneItem=\"mostrar \\\"%1\\\" em \\\"%2\\\"\"\nUndo.HideSceneItem=\"ocultar \\\"%1\\\" em \\\"%2\\\"\"\nUndo.ReorderSources=\"reordenamento das fontes em '%1'\"\nUndo.MoveUp=\"mover \\\"%1\\\" para cima em \\\"%2\\\"\"\nUndo.MoveDown=\"mover \\\"%1\\\" para baixo em \\\"%2\\\"\"\nUndo.MoveToTop=\"mover \\\"%1\\\" para a frente em \\\"%2\\\"\"\nUndo.MoveToBottom=\"mover \\\"%1\\\" para trás em \\\"%2\\\"\"\nUndo.PasteSource=\"cópia da(s) origem(ns) para \\\"%1\\\"\"\nUndo.PasteSourceRef=\"cópia da(s) referência(s) da origem para \\\"%1\\\"\"\nUndo.GroupItems=\"agrupamento dos itens em \\\"%1\\\"\"\nTransitionNameDlg.Text=\"Por favor, insira o nome da transição\"\nTransitionNameDlg.Title=\"Nome da transição\"\nTitleBar.SafeMode=\"MODO DE SEGURANÇA\"\nTitleBar.PortableMode=\"Modo portátil\"\nTitleBar.Profile=\"Perfil\"\nTitleBar.Scenes=\"Cenas\"\nNameExists.Title=\"Nome existente\"\nNameExists.Text=\"Esse nome já está em uso.\"\nNoNameEntered.Title=\"Por favor, insira um nome válido\"\nNoNameEntered.Text=\"Você não pode usar nomes vazios.\"\nConfirmStart.Title=\"Iniciar transmissão?\"\nConfirmStart.Text=\"Deseja mesmo iniciar a transmissão?\"\nConfirmStop.Title=\"Interromper transmissão?\"\nConfirmStop.Text=\"Deseja mesmo interromper a transmissão?\"\nConfirmStopRecord.Title=\"Interromper gravação?\"\nConfirmStopRecord.Text=\"Deseja mesmo interromper a gravação?\"\nConfirmBWTest.Title=\"Iniciar teste de largura de banda?\"\nConfirmBWTest.Text=\"Você está com o OBS configurado no modo de teste de largura de banda. Este modo permite o teste de rede sem que o seu canal entre em funcionamento. Uma vez terminado o teste, você precisará desativá-lo para que os espectadores possam ver sua transmissão.\\n\\nVocê quer continuar?\"\nConfirmExit.Title=\"Saídas ativas\"\nConfirmExit.Text=\"O OBS ainda está ativo. Todas as transmissões e gravações serão encerradas.\"\nConfirmRemove.Title=\"Confirmar a remoção\"\nConfirmRemove.Text=\"Deseja mesmo remover '%1'?\"\nConfirmRemove.TextMultiple=\"Deseja mesmo remover %1 itens?\"\nConfirmReset.Title=\"Redefinir propriedades\"\nConfirmReset.Text=\"Deseja mesmo redefinir as propriedades atuais para os padrões?\"\nOutput.StartStreamFailed=\"Falha ao iniciar a transmissão\"\nOutput.StartRecordingFailed=\"Falha ao iniciar a gravação\"\nOutput.StartReplayFailed=\"Falha ao iniciar o buffer de repetição\"\nOutput.StartVirtualCamFailed=\"Falha ao iniciar a câmera virtual\"\nOutput.StartFailedGeneric=\"Não foi possível criar o arquivo de saída. Verifique o registro para mais detalhes do erro.\\n\\nAviso: se estiver utilizando os codificadores NVENC ou AMD, certifique-se de que os seus drivers estão atualizados.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Não é possível salvar replays enquanto pausado\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Aviso: replays não podem ser salvos enquanto a gravação estiver pausada.\"\nOutput.ConnectFail.Title=\"Falha ao conectar\"\nOutput.ConnectFail.BadPath=\"Caminho ou URL inválida. Por favor, verifique suas configurações para confirmar que estão válidas.\"\nOutput.ConnectFail.ConnectFailed=\"Não foi possível conectar ao servidor\"\nOutput.ConnectFail.InvalidStream=\"Não foi possível acessar o canal especificado ou a chave de transmissão, verifique a sua chave de transmissão. Se estiver correta, pode haver um problema de conexão com o servidor.\"\nOutput.ConnectFail.HdrDisabled=\"Saída HDR está atualmente desativada para esta saída.\"\nOutput.ConnectFail.Error=\"Houve um erro inesperado ao tentar conexão com o servidor. Verifique o arquivo de registro para mais informações.\"\nOutput.ConnectFail.Disconnected=\"Desconectado do servidor.\"\nOutput.StreamEncodeError.Title=\"Erro de codificação\"\nOutput.StreamEncodeError.Msg=\"Houve um erro de codificação durante a transmissão.\"\nOutput.StreamEncodeError.Msg.LastError=\"Houve um erro de codificação durante a transmissão:<br><br>%1\"\nOutput.RecordFail.Title=\"Falha ao iniciar a gravação\"\nOutput.RecordFail.Unsupported=\"O formato de saída ou não é compatível ou não suporta mais do que uma faixa de áudio. Por favor, verifique suas configurações e tente novamente.\"\nOutput.RecordNoSpace.Title=\"Espaço em disco insuficiente\"\nOutput.RecordNoSpace.Msg=\"Não há espaço em disco suficiente para continuar a gravação.\"\nOutput.RecordError.Title=\"Erro de gravação\"\nOutput.RecordError.Msg=\"Houve um erro não especificado durante a gravação.\"\nOutput.RecordError.EncodeErrorMsg=\"Houve um erro de codificação durante a gravação.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Houve um erro de codificação durante a gravação:<br><br>%1\"\nOutput.BadPath.Title=\"Caminho de arquivo inválido\"\nOutput.BadPath.Text=\"O caminho de gravação configurado não pode ser aberto. Por favor, verifique o caminho de gravação em Configurações → Saída → Gravação.\"\nOutput.NoBroadcast.Title=\"Nenhuma transmissão configurada\"\nOutput.NoBroadcast.Text=\"Você precisa configurar uma transmissão antes de começar a transmitir.\"\nOutput.BroadcastStartFailed=\"Falha ao iniciar a transmissão\"\nOutput.BroadcastStopFailed=\"Falha ao interromper a transmissão\"\nLogUploadDialog.Title=\"Envio do arquivo de registro do OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Leia a <a href='https://obsproject.com/privacy-policy'>Política de Privacidade</a> e a seção referente ao envio de arquivos antes de enviar qualquer arquivo.\"\nLogUploadDialog.Labels.Progress=\"Envio do registro em andamento. Aguarde...\"\nLogUploadDialog.Labels.Description.AppLog=\"Seu arquivo de registro foi enviado. Agora você pode compartilhar a URL para fins de depuração ou suporte.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Seu relatório de falha foi enviado. Agora você pode compartilhar a URL para fins de depuração.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Enviar\"\nLogUploadDialog.Buttons.CopyURL=\"Copiar URL do registro\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analisar arquivo de registro\"\nLogUploadDialog.Buttons.RetryButton=\"Repetir\"\nLogUploadDialog.Errors.Template=\"Houve um erro ao tentar enviar o arquivo:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"O arquivo para envio não foi encontrado ou está vazio.\"\nRemux.SourceFile=\"Gravação do OBS\"\nRemux.TargetFile=\"Arquivo de destino\"\nRemux.Remux=\"Converter\"\nRemux.Stop=\"Interromper conversão\"\nRemux.ClearFinished=\"Dispensar itens concluídos\"\nRemux.ClearAll=\"Dispensar todos os itens\"\nRemux.OBSRecording=\"Gravação do OBS\"\nRemux.FinishedTitle=\"Conversão concluída\"\nRemux.Finished=\"Gravação convertida\"\nRemux.FinishedError=\"A gravação foi convertida, mas o arquivo pode estar incompleto\"\nRemux.SelectRecording=\"Selecione uma gravação do OBS...\"\nRemux.SelectTarget=\"Selecione o arquivo de destino...\"\nRemux.FileExistsTitle=\"O arquivo de destino já existe\"\nRemux.FileExists=\"Os seguintes arquivos já existem. Você deseja substituí-los?\"\nRemux.ExitUnfinishedTitle=\"Conversão em andamento\"\nRemux.ExitUnfinished=\"Conversão não concluída, parar agora pode tornar o arquivo de destino inutilizável.\\nTem certeza que deseja parar a conversão?\"\nRemux.HelpText=\"Arraste arquivos para esta janela para converter ou clique em \\\"Gravação do OBS\\\" para localizar um arquivo.\"\nRemux.NoFilesAddedTitle=\"Nenhum arquivo adicionado para conversão\"\nRemux.NoFilesAdded=\"Nenhum arquivo foi adicionado ao conversor. Arraste e solte uma pasta contendo um ou mais arquivos de vídeo.\"\nMissingFiles=\"Arquivos em falta\"\nMissingFiles.MissingFile=\"Arquivo em falta\"\nMissingFiles.NewFile=\"Novo arquivo\"\nMissingFiles.HelpText=\"Alguns arquivos estão em falta desde a última vez que o OBS foi usado.\"\nMissingFiles.Clear=\"<vazio>\"\nMissingFiles.NumFound=\"Encontrado %1 de %2\"\nMissingFiles.Search=\"Procurar pasta...\"\nMissingFiles.SelectFile=\"Selecionar arquivo...\"\nMissingFiles.SelectDir=\"Selecionar pasta para pesquisar\"\nMissingFiles.State=\"Estado\"\nMissingFiles.Missing=\"Em falta\"\nMissingFiles.Replaced=\"Substituído\"\nMissingFiles.Cleared=\"Excluído\"\nMissingFiles.Found=\"Encontrado\"\nMissingFiles.AutoSearch=\"Correspondências de arquivos adicionais encontradas\"\nMissingFiles.AutoSearchText=\"O OBS encontrou outras correspondências para arquivos em falta na pasta. Gostaria de adicioná-las?\"\nMissingFiles.NoMissing.Title=\"Verificação de arquivos em falta\"\nMissingFiles.NoMissing.Text=\"Nenhum arquivo parece estar em falta.\"\nMacPermissions.MenuAction=\"Revisar as permissões do aplicativo...\"\nMacPermissions.Title=\"Revisar permissões do aplicativo\"\nMacPermissions.Description=\"O OBS Studio requer sua permissão para que alguns recursos funcionem. É recomendável habilitar estas permissões, mas elas não são necessárias para usar o aplicativo. Você pode sempre habilitá-las mais tarde.\"\nMacPermissions.Description.OpenDialog=\"Você pode reabrir este caixa de diálogo através do menu do OBS Studio.\"\nMacPermissions.AccessGranted=\"Acesso concedido\"\nMacPermissions.RequestAccess=\"Solicitar acesso\"\nMacPermissions.OpenPreferences=\"Abrir preferências de %1\"\nMacPermissions.Item.ScreenRecording=\"Gravação de tela\"\nMacPermissions.Item.ScreenRecording.Details=\"Ative esta permissão para ser possível capturar sua tela.\"\nMacPermissions.Item.Camera=\"Câmera\"\nMacPermissions.Item.Camera.Details=\"Ative esta permissão para capturar conteúdo de uma câmera conectada ou placa de captura.\"\nMacPermissions.Item.Microphone=\"Microfone\"\nMacPermissions.Item.Microphone.Details=\"Ative esta permissão se você deseja capturar seu microfone ou um dispositivo de áudio externo.\"\nMacPermissions.Item.InputMonitoring=\"Monitoração de Entrada\"\nMacPermissions.Item.InputMonitoring.Details=\"Essa permissão é necessária para que as teclas de atalho funcionem enquanto o OBS estiver em segundo plano.\"\nMacPermissions.Item.Accessibility=\"Acessibilidade\"\nMacPermissions.Item.Accessibility.Details=\"Em instalações mais antigas, o OBS pode ser listado em \\\"Acessibilidade\\\" em vez de \\\"Monitoração de Entrada\\\".\"\nMacPermissions.Continue=\"Continuar\"\nSourceLeak.Title=\"Erro de limpeza de fonte(s)\"\nSourceLeak.Text=\"Não foi possível alterar as coleções de cenas e algumas fontes não puderam ser descarregadas. Esse problema geralmente é causado por plug-ins que não estão liberando os recursos corretamente. Por favor, certifique-se de que todos os plug-ins que você está usando estão atualizados.\\n\\nO OBS Studio agora será encerrado para evitar qualquer possível corrupção de dados.\"\nBasic.DesktopDevice1=\"Áudio do desktop\"\nBasic.DesktopDevice2=\"Áudio do desktop 2\"\nBasic.Scene=\"Cena\"\nBasic.DisplayCapture=\"Captura de monitor\"\nBasic.Main.PreviewConextMenu.Enable=\"Ativar pré-visualização\"\nBasic.Main.Preview.Disable=\"Desativar pré-visualização\"\nScaleFiltering=\"Filtragem de escala\"\nScaleFiltering.Point=\"Ponto\"\nScaleFiltering.Bicubic=\"Bicúbico\"\nScaleFiltering.Area=\"Área\"\nBlendingMethod=\"Método de mescla\"\nBlendingMethod.Default=\"Padrão\"\nBlendingMethod.SrgbOff=\"SRGB desligado\"\nBlendingMode=\"Modo de mescla\"\nBlendingMode.Additive=\"Adicionar\"\nBlendingMode.Subtract=\"Subtrair\"\nBlendingMode.Screen=\"Tela\"\nBlendingMode.Multiply=\"Multiplicar\"\nBlendingMode.Lighten=\"Clarear\"\nBlendingMode.Darken=\"Escurecer\"\nDeinterlacing=\"Desentrelaçamento\"\nDeinterlacing.Discard=\"Descartar\"\nDeinterlacing.TopFieldFirst=\"Campo superior primeiro\"\nDeinterlacing.BottomFieldFirst=\"Campo inferior primeiro\"\nVolControl.SliderUnmuted=\"Barra de volume de '%1':\"\nVolControl.SliderMuted=\"Barra de volume de '%1': (atualmente sem áudio)\"\nVolControl.Mute=\"Ativar mudo de '%1'\"\nVolControl.Properties=\"Propriedades de '%1'\"\nVolControl.UnassignedWarning.Title=\"Fonte de áudio não atribuída\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" não está atribuído a nenhuma faixa e não estará audível em transmissões ou gravações.\\n\\nPara atribuir uma fonte de áudio a uma faixa, abra \\\"propriedades de áudio avançadas\\\" clicando com o botão direito do mouse ou nas ferramentas do painel do mixer.\"\nBasic.Main.AddSceneDlg.Title=\"Adicionar cena\"\nBasic.Main.AddSceneDlg.Text=\"Por favor, digite o nome da cena\"\nBasic.Main.DefaultSceneName.Text=\"Cena %1\"\nBasic.Main.AddSceneCollection.Title=\"Adicionar coleção de cenas\"\nBasic.Main.AddSceneCollection.Text=\"Por favor, insira o nome da coleção de cenas\"\nBasic.Main.RenameSceneCollection.Title=\"Renomear coleção de cenas\"\nBasic.Main.RemigrateSceneCollection.Title=\"Atualizar resolução da coleção de cenas\"\nBasic.Main.RemigrateSceneCollection.Text=\"Você deseja atualizar a resolução da coleção de cenas de \\\"%1\\\" para corresponder à resolução da área de edição do perfil atual, que é de %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Não é possível atualizar a resolução da coleção de cenas enquanto as saídas estão ativas.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Não foi possível atualizar a resolução da coleção de cenas. A resolução original é desconhecida.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Não foi possível redefinir: houve uma falha ao alterar a resolução do OBS.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Não foi possível redefinir: a resolução atual já é a resolução base da coleção de cenas.\"\nAddProfile.Title=\"Adicionar perfil\"\nAddProfile.Text=\"Por favor, insira o nome do perfil\"\nAddProfile.WizardCheckbox=\"Mostrar assistente de configuração\"\nRenameProfile.Title=\"Renomear perfil\"\nBasic.Main.MixerRename.Title=\"Renomear fonte de áudio\"\nBasic.Main.MixerRename.Text=\"Por favor, digite o nome da fonte de áudio\"\nBasic.Main.PreviewDisabled=\"A pré-visualização esta desativada\"\nBasic.SourceSelect=\"Criar ou reutilizar fonte\"\nBasic.SourceSelect.CreateNew=\"Criar nova\"\nBasic.SourceSelect.AddExisting=\"Reutilizar existente\"\nBasic.SourceSelect.AddVisible=\"Tornar a fonte visível\"\nBasic.Main.Sources.Visibility=\"Visibilidade\"\nBasic.Main.Sources.VisibilityDescription=\"Controla a visibilidade de '%1' na área de edição\"\nBasic.Main.Sources.Lock=\"Bloquear\"\nBasic.Main.Sources.LockDescription=\"Bloqueia a posição e a escala de '%1' na área de edição\"\nBasic.PropertiesWindow=\"Propriedades de '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (seleção automática: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Escolher cor\"\nBasic.PropertiesWindow.SelectFont=\"Escolher fonte\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Escolha uma fonte\"\nBasic.PropertiesWindow.ConfirmTitle=\"Configurações alteradas\"\nBasic.PropertiesWindow.Confirm=\"Há alterações não salvas. Deseja salvar agora?\"\nBasic.PropertiesWindow.NoProperties=\"Sem propriedades disponíveis\"\nBasic.PropertiesWindow.AddFiles=\"Adicionar arquivos\"\nBasic.PropertiesWindow.AddDir=\"Adicionar pasta\"\nBasic.PropertiesWindow.AddURL=\"Adicionar caminho ou URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Adicionar pasta para '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Adicionar arquivos para '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Adicionar entrada a '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Editar a entrada de '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Valores de FPS simples\"\nBasic.PropertiesView.FPS.Rational=\"Valores de FPS racionais\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Intervalos válidos de FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"Abrir este link no seu navegador padrão?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Abrir URL\"\nBasic.InteractionWindow=\"Interagindo com '%1'\"\nBasic.StatusBar.Reconnecting=\"Desconectado, reconectando-se em %2 segundo(s) (tentativa %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Tentando reconectar... (tentativa %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Reconexão bem-sucedida\"\nBasic.StatusBar.Delay=\"Atraso (%1 seg)\"\nBasic.StatusBar.DelayStartingIn=\"Atraso (começando em %1 seg)\"\nBasic.StatusBar.DelayStoppingIn=\"Atraso (parando em %1 seg)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Atraso (parando em %1 seg, começando em %2 seg)\"\nBasic.StatusBar.RecordingSavedTo=\"Gravação salva em \\\"%1\\\"\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Buffer de reprodução salvo em \\\"%1\\\"\"\nBasic.StatusBar.ScreenshotSavedTo=\"Captura de tela salva em \\\"%1\\\"\"\nBasic.StatusBar.AutoRemuxedTo=\"Gravação convertida automaticamente para '%1'\"\nBasic.Filters=\"Filtros\"\nBasic.Filters.AsyncFilters=\"Filtros de áudio/vídeo\"\nBasic.Filters.AudioFilters=\"Filtros de áudio\"\nBasic.Filters.EffectFilters=\"Filtros de efeito\"\nBasic.Filters.Title=\"Filtros de '%1'\"\nBasic.Filters.AddFilter.Title=\"Nome do filtro\"\nBasic.Filters.AddFilter.Text=\"Por favor, especifique o nome do filtro\"\nBasic.TransformWindow=\"Transformação de elemento da cena\"\nBasic.TransformWindow.Position=\"Posição\"\nBasic.TransformWindow.PositionX=\"Posição X\"\nBasic.TransformWindow.PositionY=\"Posição Y\"\nBasic.TransformWindow.Rotation=\"Rotação\"\nBasic.TransformWindow.Size=\"Tamanho\"\nBasic.TransformWindow.Width=\"Largura\"\nBasic.TransformWindow.Height=\"Altura\"\nBasic.TransformWindow.Alignment=\"Alinhamento\"\nBasic.TransformWindow.BoundsType=\"Tipo da caixa delimitadora\"\nBasic.TransformWindow.BoundsAlignment=\"Alinhamento na caixa delimitadora\"\nBasic.TransformWindow.Bounds=\"Tamanho da caixa delimitadora\"\nBasic.TransformWindow.BoundsWidth=\"Largura da caixa delimitadora\"\nBasic.TransformWindow.BoundsHeight=\"Altura da caixa delimitadora\"\nBasic.TransformWindow.CropToBounds=\"Cortar para caixa delimitadora\"\nBasic.TransformWindow.Crop=\"Cortar\"\nBasic.TransformWindow.CropLeft=\"Cortar à esquerda\"\nBasic.TransformWindow.CropRight=\"Cortar à direita\"\nBasic.TransformWindow.CropTop=\"Cortar para cima\"\nBasic.TransformWindow.CropBottom=\"Cortar para baixo\"\nBasic.TransformWindow.Alignment.TopLeft=\"Superior esquerdo\"\nBasic.TransformWindow.Alignment.TopCenter=\"Superior ao centro\"\nBasic.TransformWindow.Alignment.TopRight=\"Superior direito\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Centro à esquerda\"\nBasic.TransformWindow.Alignment.Center=\"Centro\"\nBasic.TransformWindow.Alignment.CenterRight=\"Centro à direita\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Inferior esquerdo\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Inferior ao centro\"\nBasic.TransformWindow.Alignment.BottomRight=\"Inferior direito\"\nBasic.TransformWindow.BoundsType.None=\"Sem bordas\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Apenas tamanho máximo\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Redimensionar até as bordas internas\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Redimensionar até as bordas externas\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Redimensionar até a largura das bordas\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Redimensionar até a altura das bordas\"\nBasic.TransformWindow.BoundsType.Stretch=\"Esticar até as bordas\"\nBasic.TransformWindow.Title=\"Editar transformação de '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Nenhuma fonte selecionada\"\nBasic.Main.AddSourceHelp.Title=\"Não foi possível adicionar a fonte\"\nBasic.Main.AddSourceHelp.Text=\"É necessário pelo menos uma cena para adicionar uma fonte.\"\nBasic.Main.Scenes=\"Cenas\"\nBasic.Main.Sources=\"Fontes\"\nBasic.Main.Source=\"Fonte\"\nBasic.Main.Controls=\"Controles\"\nBasic.Main.PreparingStream=\"Preparando...\"\nBasic.Main.Connecting=\"Conectando...\"\nBasic.Main.StartRecording=\"Iniciar gravação\"\nBasic.Main.StartReplayBuffer=\"Iniciar buffer de repetição\"\nBasic.Main.SaveReplay=\"Salvar replay\"\nBasic.Main.StartStreaming=\"Iniciar transmissão\"\nBasic.Main.StartBroadcast=\"Transmitir ao vivo\"\nBasic.Main.StartVirtualCam=\"Iniciar câmera virtual\"\nBasic.Main.StopRecording=\"Interromper gravação\"\nBasic.Main.PauseRecording=\"Pausar gravação\"\nBasic.Main.UnpauseRecording=\"Continuar gravação\"\nBasic.Main.SplitFile=\"Dividir arquivo de gravação\"\nBasic.Main.AddChapterMarker=\"Adicionar marcador de capítulo (apenas em MP4/MOV híbrido)\"\nBasic.Main.StoppingRecording=\"Interrompendo gravação...\"\nBasic.Main.StopReplayBuffer=\"Interromper buffer de repetição\"\nBasic.Main.StoppingReplayBuffer=\"Interrompendo buffer de repetição...\"\nBasic.Main.SetupBroadcast=\"Gerenciar transmissão\"\nBasic.Main.StopStreaming=\"Interromper transmissão\"\nBasic.Main.StopBroadcast=\"Encerrar transmissão\"\nBasic.Main.AutoStopEnabled=\"(parada automática)\"\nBasic.Main.StoppingStreaming=\"Interrompendo a transmissão...\"\nBasic.Main.ForceStopStreaming=\"Interromper transmissão (ignorar atraso)\"\nBasic.Main.ShowContextBar=\"Mostrar barra de ferramentas da fonte\"\nBasic.Main.HideContextBar=\"Ocultar barra de ferramentas da fonte\"\nBasic.Main.StopVirtualCam=\"Interromper câmera virtual\"\nBasic.Main.Group=\"Grupo %1\"\nBasic.Main.GroupItems=\"Agrupar itens selecionados\"\nBasic.Main.Ungroup=\"Desagrupar\"\nBasic.Main.GridMode=\"Modo grade\"\nBasic.Main.ListMode=\"Modo lista\"\nBasic.Main.VirtualCamConfig=\"Configurar câmera virtual\"\nBasic.VCam.VirtualCamera=\"Câmera virtual\"\nBasic.VCam.OutputType=\"Tipo de saída\"\nBasic.VCam.OutputSelection=\"Seleção de saída\"\nBasic.VCam.OutputType.Program=\"Programa (padrão)\"\nBasic.VCam.OutputSelection.NoSelection=\"Nenhuma seleção para este tipo de saída\"\nBasic.VCam.RestartWarning=\"A câmera virtual será reiniciada para aplicar esta alteração\"\nBasic.MainMenu.File=\"Arquivo (&F)\"\nBasic.MainMenu.File.Export=\"&Exportar\"\nBasic.MainMenu.File.Import=\"&Importar\"\nBasic.MainMenu.File.ShowRecordings=\"Mostrar g&ravações\"\nBasic.MainMenu.File.Remux=\"Converter gravações (&M)\"\nBasic.MainMenu.File.Settings=\"Configuraçõe&s\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Mostrar pasta de configurações\"\nBasic.MainMenu.File.ShowProfileFolder=\"Mostrar pasta de perfis\"\nBasic.MainMenu.File.ShowMissingFiles=\"Verificar arquivos em falta\"\nBasic.MainMenu.File.Exit=\"Encerrar OBS (&X)\"\nBasic.MainMenu.Edit=\"&Editar\"\nBasic.MainMenu.Edit.Undo=\"Desfazer (&U)\"\nBasic.MainMenu.Edit.Redo=\"&Refazer\"\nBasic.MainMenu.Edit.LockPreview=\"B&loquear pré-visualização\"\nBasic.MainMenu.Edit.Scale=\"Tamanho da pré-vi&sualização\"\nBasic.MainMenu.Edit.Scale.Window=\"Dimensionar à janela\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Área de edição (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Saída (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Dimensionado (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Ampliar\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Reduzir\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Redefinir zoom\"\nBasic.MainMenu.Edit.Transform=\"&Transformar\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Editar transformação...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Copiar transformação\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Colar transformação\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Limpa&r transformação\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Girar 90º sentido horário\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Girar 90º sentido anti-horário\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Girar 180º\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Inverter na &horizontal\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Inverter na &vertical\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Ajustar à tela (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"E&stender à tela\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centralizar à tela\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centralizar na vertical\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centralizar na horizontal\"\nBasic.MainMenu.Edit.Order=\"&Ordenar\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Mover para cima (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Mover para baixo (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Mover para o &topo\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Mover para o fundo (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Propriedades de áudio &avançadas\"\nBasic.MainMenu.View=\"&Visualizar\"\nBasic.MainMenu.View.Toolbars=\"Barras de ferramen&tas\"\nBasic.MainMenu.View.ListboxToolbars=\"Ferramentas de painéis\"\nBasic.MainMenu.View.ContextBar=\"Barra de ferramentas da fonte\"\nBasic.MainMenu.View.SourceIcons=\"Ícones em fontes (&I)\"\nBasic.MainMenu.View.StatusBar=\"Barra de &status\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Interface em tela cheia\"\nBasic.MainMenu.View.ResetUI=\"&Redefinir interface\"\nBasic.MainMenu.View.AlwaysOnTop=\"Sempre &ativo\"\nBasic.MainMenu.View.SceneListMode=\"Modo de lista de cenas\"\nBasic.MainMenu.Docks=\"Painéis (&D)\"\nBasic.MainMenu.Docks.ResetDocks=\"&Redefinir painéis\"\nBasic.MainMenu.Docks.LockDocks=\"B&loquear painéis\"\nBasic.MainMenu.Docks.SideDocks=\"Expandir painéis na vertical (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Painéis personalizáveis &com URL...\"\nBasic.MainMenu.Import=\"Importar...\"\nBasic.MainMenu.Export=\"Exportar...\"\nBasic.MainMenu.SceneCollection=\"Coleção de cena&s\"\nBasic.MainMenu.Profile=\"&Perfil\"\nBasic.MainMenu.Profile.Import=\"Importar perfil\"\nBasic.MainMenu.Profile.Export=\"Exportar perfil\"\nBasic.MainMenu.SceneCollection.Import=\"Importar coleção de cenas\"\nBasic.MainMenu.SceneCollection.Export=\"Exportar coleção de cenas\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Redefinir a resolução de base\"\nBasic.MainMenu.SceneCollection.Migrate=\"Definir a resolução de base\"\nBasic.MainMenu.Profile.Exists=\"Esse perfil já existe\"\nBasic.MainMenu.Tools=\"Ferramentas (&T)\"\nBasic.MainMenu.Help=\"Ajuda (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal de ajuda\"\nBasic.MainMenu.Help.Website=\"Visitar &website\"\nBasic.MainMenu.Help.Discord=\"Entrar no servidor do &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Novidades\"\nBasic.MainMenu.Help.Logs=\"Arquivos de registro (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Mo&strar arquivos de registro\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"&Carregar arquivo de registro atual\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Carregar arquivo de registro anterior (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Ver arquivo de registro atual\"\nBasic.MainMenu.Help.ReleaseNotes=\"Notas de lançamento\"\nBasic.MainMenu.Help.CheckForUpdates=\"Verificar se há atualizações\"\nBasic.MainMenu.Help.Repair=\"Verificar integridade dos arquivos\"\nBasic.MainMenu.Help.RestartSafeMode=\"Reiniciar no modo de segurança\"\nBasic.MainMenu.Help.RestartNormal=\"Reiniciar no modo normal\"\nBasic.MainMenu.Help.CrashLogs=\"&Relatórios de erros\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Mo&strar relatórios de erros\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Carregar relatório de erros anterior (&P)\"\nBasic.MainMenu.Help.About=\"Sobre (&A)\"\nBasic.Settings.ProgramRestart=\"Reinicie o programa para aplicar as configurações.\"\nBasic.Settings.ConfirmTitle=\"Confirmar alterações\"\nBasic.Settings.Confirm=\"Há alterações não salvas. Deseja salvar agora?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 está controlando algumas das configurações da sua transmissão\"\nBasic.Settings.General=\"Geral\"\nBasic.Settings.General.Language=\"Idioma\"\nBasic.Settings.General.Updater=\"Atualizações\"\nBasic.Settings.General.UpdateChannel=\"Canal de atualização\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Desativado)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Padrão)\"\nBasic.Settings.General.EnableAutoUpdates=\"Buscar novas atualizações ao inicializar\"\nBasic.Settings.General.OpenStatsOnStartup=\"Abrir janela de estatísticas ao inicializar\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Esconder janelas do OBS na captura de tela\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Esta configuração impedirá que todas as janelas do OBS Studio que não sejam projetores sejam \\ncapturadas pelo OBS. Isso também afetará outros aplicativos, como softwares de chamada de vídeo, compartilhamento \\nde tela, suporte remoto, capturas de tela e outros softwares de gravação.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Esta configuração impedirá que todas as janelas do OBS Studio que não sejam projetores sejam capturadas pelo OBS. Isso também afetará outros aplicativos, como softwares de chamada de vídeo, compartilhamento de tela, suporte remoto, capturas de tela e outros softwares de gravação.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Perguntar antes de iniciar transmissões\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Perguntar antes de interromper transmissões\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Perguntar antes de interromper gravações\"\nBasic.Settings.General.Projectors=\"Projetores\"\nBasic.Settings.General.HideProjectorCursor=\"Ocultar o cursor sobre projetores\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Colocar projetores sempre no topo\"\nBasic.Settings.General.Snapping=\"Encaixe das fontes na tela\"\nBasic.Settings.General.ScreenSnapping=\"Encaixar fontes nas bordas da tela\"\nBasic.Settings.General.CenterSnapping=\"Encaixar fontes no centro horizontal e vertical\"\nBasic.Settings.General.SourceSnapping=\"Encaixar fontes com outras fontes\"\nBasic.Settings.General.SnapDistance=\"Sensibilidade do encaixe\"\nBasic.Settings.General.SpacingHelpers=\"Mostrar guias de alinhamento por pixel\"\nBasic.Settings.General.RecordWhenStreaming=\"Gravar automaticamente quando estiver transmitindo\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Continuar gravando quando a transmissão parar\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Iniciar automaticamente o buffer de repetição durante a transmissão\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Manter o buffer de repetição ativo quando a transmissão parar\"\nBasic.Settings.General.SysTray=\"Bandeja do sistema\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimizar para a bandeja do sistema quando iniciar\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Minimizar sempre para a bandeja do sistema em vez da barra de tarefas\"\nBasic.Settings.General.SaveProjectors=\"Salvar projetores ao encerrar\"\nBasic.Settings.General.CloseExistingProjectors=\"Limitar a um projetor em tela cheia por tela\"\nBasic.Settings.General.Preview=\"Pré-visualização\"\nBasic.Settings.General.OverflowHidden=\"Ocultar excesso\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Excesso sempre visível\"\nBasic.Settings.General.OverflowSelectionHidden=\"Mostrar excesso mesmo quando a fonte estiver escondida\"\nBasic.Settings.General.Importers=\"Importadores\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Procurar em locais conhecidos por coleções de cena ao importar\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Usar transição para a cena quando clicar duas vezes\"\nBasic.Settings.General.StudioPortraitLayout=\"Ativar a disposição horizontal/vertical\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Mostrar rótulos de pré-visualização/programa\"\nBasic.Settings.General.Multiview=\"Visualização múltipla\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Clique para alternar entre cenas\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Mostrar nome das cenas\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Mostrar áreas de segurança (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Disposição da visualização múltipla\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontal, acima (8 cenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontal, abaixo (8 cenas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertical, à esquerda (8 cenas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertical, à direita (8 cenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontal, acima (18 cenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontal, acima (24 cenas)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Cenas apenas (4 cenas)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Cenas apenas (9 cenas)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Cenas apenas (16 cenas)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Cenas apenas (25 cenas)\"\nBasic.Settings.General.ChannelName.stable=\"Estável\"\nBasic.Settings.General.ChannelDescription.stable=\"Último lançamento estável\"\nBasic.Settings.General.ChannelName.beta=\"Betas / Candidatas a lançamento\"\nBasic.Settings.General.ChannelDescription.beta=\"Versões prévias potencialmente instáveis\"\nBasic.Settings.Appearance=\"Aparência\"\nBasic.Settings.Appearance.General=\"Geral\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Estilo\"\nBasic.Settings.Appearance.General.NoVariant=\"Nenhum estilo disponível\"\nBasic.Settings.Appearance.FontScale=\"Tamanho da letra\"\nBasic.Settings.Appearance.Density=\"Densidade\"\nBasic.Settings.Appearance.Classic=\"Clássica\"\nBasic.Settings.Appearance.Compact=\"Compacta\"\nBasic.Settings.Appearance.Comfortable=\"Confortável\"\nBasic.Settings.Appearance.OptionsWarning=\"Algumas opções de aparência não estão disponíveis para este estilo.\"\nBasic.Settings.Stream=\"Transmissão\"\nBasic.Settings.Stream.Destination=\"Destino\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Utilizar autenticação\"\nBasic.Settings.Stream.Custom.Username=\"Nome de usuário\"\nBasic.Settings.Stream.Custom.Password=\"Senha\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: insira o srp_username.\\nRTMP: insira o nome de usuário.\\nSRT: não usado.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: insira o srp_password.\\nRTMP: insira a senha.\\nSRT: insira a senha de criptografia.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Ativar modo de teste de largura de banda\"\nBasic.Settings.Stream.TTVAddon=\"Complementos do chat da Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Nenhum\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV e FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Configuração incompleta\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Abrir configurações\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"A URL e a chave de transmissão estão em branco.\\n\\nAbra as configurações e insira-os na aba 'Transmissão'.\"\nBasic.Settings.Stream.MissingUrl=\"A URL da transmissão está em branco.\\n\\nAbra as configurações e insira a URL na aba 'Transmissão'.\"\nBasic.Settings.Stream.MissingStreamKey=\"A chave de transmissão está em branco.\\n\\nAbra as configurações e insira a chave de transmissão na aba 'Transmissão'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorar recomendações de configuração do serviço de streaming\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Sobrepor configurações recomendadas\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Aviso: ignorar limitações do serviço pode impedir ou reduzir a qualidade da sua transmissão.\\n\\nContinuar?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Taxa de bits máxima de vídeo: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Taxa de bits máxima de áudio: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Resolução máxima: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Taxa de quadros máxima: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Definir servidor personalizado...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Servidor personalizado\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Habilitar %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Largura de banda máxima na transmissão\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automático\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Máximo de faixas de vídeo\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automático\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Descarregar transmissão para FLV (usando configs. simples para o arquivo)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Sobreposição de configs. (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Ativar sobreposição de configurações\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Vídeo multifaixa\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Área de edição adicional\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Transmissão simultânea\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"A transmissão simultânea permite codificar e enviar vídeo em várias qualidades. <a href='https://obsproject.com/kb/whip-streaming-guide'>Saiba mais</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Quantidade de camadas\"\nBasic.Settings.Stream.AdvancedOptions=\"Opções avançadas\"\nBasic.Settings.Output=\"Saída\"\nBasic.Settings.Output.Format=\"Formato de gravação\"\nBasic.Settings.Output.Format.hMP4=\"MP4 híbrido (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV híbrido (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 fragmentado (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV fragmentado (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"O MOV fragmentado efetua a gravação em partes e não requer a mesma finalização que os arquivos MOV tradicionais.\\nIsso garante que o arquivo permaneça reproduzível mesmo que a gravação no disco seja interrompida, por exemplo, como resultado de uma tela azul ou falta de energia.\\n\\nIsso pode não ser compatível com todos os reprodutores e editores. Use \\\"Arquivo\\\" → \\\"Converter gravações\\\" para converter o arquivo em um formato mais compatível, se necessário.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"O MP4 fragmentado efetua a gravação em pedaços e não requer a mesma finalização que os arquivos MP4 tradicionais.\\nIsso garante que o arquivo permaneça reproduzível mesmo que a gravação no disco seja interrompida, por exemplo, como resultado de uma tela azul ou falta de energia.\\n\\nIsso pode não ser compatível com todos os players e editores. Use \\\"Arquivo\\\" → \\\"Converter gravações\\\" para converter o arquivo em um formato mais compatível, se necessário.\"\nBasic.Settings.Output.Encoder.Video=\"Codificador de vídeo\"\nBasic.Settings.Output.Encoder.Audio=\"Codificador de áudio\"\nBasic.Settings.Output.SelectDirectory=\"Selecione a pasta de gravação\"\nBasic.Settings.Output.DynamicBitrate=\"Alterar taxa de bits dinamicamente para gerenciar o congestionamento\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Alterar taxa de bits dinamicamente para gerenciar o congestionamento (beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Altera dinamicamente a taxa de bits em tempo real, em vez de perder frames para reduzir o congestionamento.\\n\\nNote que isso pode aumentar o atraso para os espectadores se houver um congestionamento repentino significativo.\\nQuando a taxa de bits cair, pode levar até alguns minutos para restaurar.\\n\\nAtualmente suportado apenas para RTMP.\"\nBasic.Settings.Output.Mode=\"Modo de saída\"\nBasic.Settings.Output.Mode.Simple=\"Simples\"\nBasic.Settings.Output.Mode.Adv=\"Avançado\"\nBasic.Settings.Output.Mode.FFmpeg=\"Saída de FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Ativar buffer de repetição\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Tempo máximo do replay\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Máximo de memória\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Uso de memória estimado: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Aviso: o uso de memória estimado de %1 MB é maior que o máximo recomendado de %2 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Não foi possível estimar o uso de memória. Por favor, defina o limite máximo de memória.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefixo do buffer de repetição\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufixo\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"O buffer de repetição não pode ser utilizado quando o tipo de gravação estiver como saída personalizada (FFmpeg)\"\nBasic.Settings.Output.Simple.SavePath=\"Caminho de gravação\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Qualidade da gravação\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"A mesma da transmissão\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Qualidade alta, arquivo normal\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Qualidade indistinguível, arquivo pesado\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Sem perda de qualidade, arquivo muito pesado\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Aviso: a taxa de bits do vídeo transmitido será definida como %1, que é o limite máximo para o serviço de transmissão atual.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Aviso: a taxa de bits do áudio transmitido será definida como %1, que é o limite máximo para o serviço de transmissão atual.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Aviso: gravações não podem ser pausadas se a qualidade da gravação estiver definida como \\\"A mesma da transmissão\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Aviso: o formato de gravação atualmente selecionado é incompatível com o(s) codificador(es) de transmissão selecionado(s).\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Aviso: gravar com um codificador de software em uma qualidade diferente da transmissão vai exigir mais da CPU se você transmitir e gravar ao mesmo tempo.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Aviso: o modo sem perda de qualidade gera arquivos extremamente grandes! Este modo pode usar até 7 gigabytes de espaço por minuto em altas resoluções e taxas de quadro. Não é recomendado para gravações longas a não ser que você tenha muito espaço disponível no disco. O buffer de repetição fica indisponível quando o modo sem perda de qualidade está ativo.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Deseja mesmo usar a qualidade sem perdas?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Aviso de qualidade muito alta!\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (preset x264 de baixa utilização de CPU, aumenta o tamanho do arquivo)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Padrão)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Faixa de VOD da Twitch (Usa faixa 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Faixa de áudio\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Resolução ou taxa de quadros incompatíveis\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Este serviço de streaming não suporta sua atual resolução e/ou taxa de quadros de saída. Eles serão alterados para o valor compatível mais próximo:\\n\\n%1\\n\\nVocê deseja continuar?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolução: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Taxa de quadros: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Codificador incompatível\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"O serviço de streaming \\\"%1\\\" não suporta o codificador \\\"%2\\\". Em vez disso, o codificador será alterado para \\\"%3\\\".\\n\\nDeseja continuar?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"O serviço de streaming \\\"%1\\\" não suporta os codificadores \\\"%2\\\" e \\\"%3\\\". Em vez disso, os codificadores serão alterados para \\\"%4\\\" e \\\"%5\\\".\\n\\nDeseja continuar?\"\nBasic.Settings.Output.VideoBitrate=\"Taxa de bits do vídeo\"\nBasic.Settings.Output.AudioBitrate=\"Taxa de bits do áudio\"\nBasic.Settings.Output.Reconnect=\"Reconectar automaticamente\"\nBasic.Settings.Output.RetryDelay=\"Atraso de repetição\"\nBasic.Settings.Output.MaxRetries=\"Número máximo de tentativas\"\nBasic.Settings.Output.Advanced=\"Ativar as configurações personalizadas de codificador (avançado)\"\nBasic.Settings.Output.EncoderPreset=\"Padrão de codificação\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (baixo uso da CPU, menor qualidade)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (padrão) (uso médio da CPU, qualidade padrão)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (alto uso da CPU, alta qualidade)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Configurações de codificador personalizadas\"\nBasic.Settings.Output.CustomMuxerSettings=\"Configs. do conversor de gravações\"\nBasic.Settings.Output.NoSpaceFileName=\"Gerar nome de arquivo sem espaços\"\nBasic.Settings.Output.Adv.Rescale=\"Redimensionar a saída\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Desativado\"\nBasic.Settings.Output.Adv.AudioTrack=\"Faixa de áudio\"\nBasic.Settings.Output.Adv.Streaming=\"Transmissão\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Configurações de transmissão\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Faixa 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Faixa 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Faixa 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Faixa 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Faixa 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Faixa 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Faixa de VOD da Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"Configurações do codificador\"\nBasic.Settings.Output.Adv.Recording=\"Gravação\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Configurações de gravação\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tipo de gravação\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tipo\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Padrão\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Saída personalizada (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Usar o codificador da transmissão)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Formatação do nome do arquivo\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Sobrescrever caso o arquivo exista\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"A saída personalizada (FFmpeg) é fornecida sem salvaguardas.\\nConsidere usar o tipo de gravação \\\"Padrão\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Tipo de saída FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Saída para URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Saída para um arquivo\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Formatos de gravação comuns\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Todos os arquivos\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Caminho do arquivo ou URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Formato do contêiner\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Áudio\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Vídeo\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Formato padrão\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Descrição do formato do contêiner\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"O codificador de áudio/vídeo é uma suposição a partir do arquivo ou URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Codificador padrão\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Desativar o codificador\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Codificador de vídeo\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Configurações do codificador de vídeo (se houver)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Codificador de áudio\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Configurações do codificador de áudio (se houver)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Configurações do conversor de gravações (se houver)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Intervalo de keyframes (quadros)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Mostrar todos os codificadores (incluir potencialmente incompatíveis)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Configurações de FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Divisão automática de arquivos\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Dividir por tempo\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Dividir por tamanho\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Dividir apenas manualmente\"\nBasic.Settings.Output.SplitFile.Time=\"Tempo de divisão\"\nBasic.Settings.Output.SplitFile.Size=\"Tamanho de divisão\"\nScreenshot=\"Caminho da captura de tela\"\nScreenshot.SourceHotkey=\"Capturar imagem de fonte selecionada\"\nScreenshot.StudioProgram=\"Capturar imagem (programa)\"\nScreenshot.Preview=\"Capturar imagem (pré-visualização)\"\nScreenshot.Scene=\"Capturar imagem (cena)\"\nScreenshot.Source=\"Capturar imagem (fonte)\"\nFilenameFormatting.TT.CCYY=\"Ano, quatro dígitos\"\nFilenameFormatting.TT.YY=\"Ano, últimos dois dígitos (00-99)\"\nFilenameFormatting.TT.MM=\"Mês como um número decimal (01-12)\"\nFilenameFormatting.TT.DD=\"Dia do mês, precedido por zero (01-31)\"\nFilenameFormatting.TT.hh=\"Hora no formato 24h (00-23)\"\nFilenameFormatting.TT.mm=\"Minuto (00-59)\"\nFilenameFormatting.TT.ss=\"Segundo (00-59)\"\nFilenameFormatting.TT.Percent=\"Um símbolo de %\"\nFilenameFormatting.TT.a=\"Nome abreviado do dia da semana\"\nFilenameFormatting.TT.A=\"Nome completo do dia da semana\"\nFilenameFormatting.TT.b=\"Nome abreviado do mês\"\nFilenameFormatting.TT.B=\"Nome completo do mês\"\nFilenameFormatting.TT.d=\"Dia do mês, precedido por zero (01-31)\"\nFilenameFormatting.TT.H=\"Hora no formato 24h (00-23)\"\nFilenameFormatting.TT.I=\"Hora no formato 12h (01-12)\"\nFilenameFormatting.TT.m=\"Mês como um número decimal (01-12)\"\nFilenameFormatting.TT.M=\"Minuto (00-59)\"\nFilenameFormatting.TT.p=\"Designação AM ou PM\"\nFilenameFormatting.TT.s=\"Tempo em segundos desde a era UNIX\"\nFilenameFormatting.TT.S=\"Segundo (00-59)\"\nFilenameFormatting.TT.y=\"Ano, últimos dois dígitos (00-99)\"\nFilenameFormatting.TT.Y=\"Ano\"\nFilenameFormatting.TT.z=\"Compensação ISO 8601 do fuso horário UTC\"\nFilenameFormatting.TT.Z=\"Nome ou abreviação do fuso horário\"\nFilenameFormatting.TT.FPS=\"Taxa de quadros\"\nFilenameFormatting.TT.CRES=\"Resolução base (área de edição)\"\nFilenameFormatting.TT.ORES=\"Resolução de saída (escala)\"\nFilenameFormatting.TT.VF=\"Formato de vídeo\"\nBasic.Settings.Video=\"Vídeo\"\nBasic.Settings.Video.Adapter=\"Adaptador de vídeo\"\nBasic.Settings.Video.BaseResolution=\"Resolução base (área de edição)\"\nBasic.Settings.Video.ScaledResolution=\"Resolução de saída (escala)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtro de redução\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Resoluções correspondem, não é necessário reduzir]\"\nBasic.Settings.Video.FPSCommon=\"Valores comuns de FPS\"\nBasic.Settings.Video.FPSInteger=\"Valores inteiros de FPS\"\nBasic.Settings.Video.FPSFraction=\"Valores fracionários de FPS\"\nBasic.Settings.Video.Numerator=\"Numerador\"\nBasic.Settings.Video.Denominator=\"Denominador\"\nBasic.Settings.Video.Renderer=\"Renderizador\"\nBasic.Settings.Video.InvalidResolution=\"Resolução inválida. Precisa ser [largura]x[altura] (ex.: 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"A saída de vídeo está atualmente ativa. Por favor, desligue quaisquer saídas para alterar as configurações de vídeo.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (mais rápido, porém embaça se redimensionado)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicúbico (redimensionamento nítido, 16 amostras)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (redimensionamento nítido, 36 amostras)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Área (soma ponderada, 4/6/9 amostras)\"\nBasic.Settings.Audio=\"Áudio\"\nBasic.Settings.Audio.SampleRate=\"Taxa de amostragem\"\nBasic.Settings.Audio.Channels=\"Canais\"\nBasic.Settings.Audio.Meters=\"Métricas\"\nBasic.Settings.Audio.MeterDecayRate=\"Taxa de decaimento\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rápida\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Média (PPM Tipo I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Devagar (PPM Tipo II)\"\nBasic.Settings.Audio.PeakMeterType=\"Tipo do medidor de pico\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Exemplo de pico\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"True Peak (Uso elevado de CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"AVISO: áudio Surround está ativado.\"\nBasic.Settings.Audio.MultichannelWarning=\"Se for transmitir, verifique se o serviço de transmissão suporta receber e reproduzir som surround. Por exemplo, o Facebook 360 Live suporta totalmente som surround e o YouTube Live suporta receber áudio 5.1 (e de reprodução em TVs).\\n\\n Os filtros de áudio do OBS Studio são compatíveis com som surround, contudo, o suporte de plugins VST não é garantido.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Ativar o som surround?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Deseja mesmo ativar o som surround?\"\nBasic.Settings.Audio.Devices=\"Dispositivos de áudio globais\"\nBasic.Settings.Audio.DesktopDevice=\"Áudio do desktop\"\nBasic.Settings.Audio.DesktopDevice2=\"Áudio do desktop 2\"\nBasic.Settings.Audio.AuxDevice=\"Áudio do microfone ou auxiliar\"\nBasic.Settings.Audio.AuxDevice2=\"Áudio do microfone ou auxiliar 2\"\nBasic.Settings.Audio.AuxDevice3=\"Áudio do microfone ou auxiliar 3\"\nBasic.Settings.Audio.AuxDevice4=\"Áudio do microfone ou auxiliar 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Ativar pressionar-para-ativar-mudo\"\nBasic.Settings.Audio.PushToMuteDelay=\"Atraso do pressionar-para-ativar-mudo\"\nBasic.Settings.Audio.EnablePushToTalk=\"Ativar pressionar-para-falar\"\nBasic.Settings.Audio.PushToTalkDelay=\"Atraso do pressionar-para-falar\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Dispositivo desconectado ou indisponível]\"\nBasic.Settings.Audio.Disabled=\"Desativado\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Modo de buffer de áudio de baixa latência (para saídas Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"AVISO: buffer de áudio de baixa latência está ativado.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"O modo de buffer de áudio de baixa latência pode causar falhas no áudio ou parar a reprodução de algumas fontes.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Habilitar o modo de buffer de áudio de baixa latência?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Deseja mesmo habilitar o modo de buffer de áudio de baixa latência?\"\nBasic.Settings.Accessibility=\"Acessibilidade\"\nBasic.Settings.Accessibility.ColorOverrides=\"Usar cores diferentes\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Borda da fonte (ao selecionar)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Borda da fonte (ao recortar)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Borda da fonte (ao passar o mouse)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Faixa de volume do mixer (-60 até -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Faixa de volume do mixer (-20 até -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Faixa de volume do mixer (-9 até 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Faixa de volume do mixer (-60 até -20dB) (ativo)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Faixa de volume do mixer (-20 até -9dB) (ativo)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Faixa de volume do mixer (-9 até 0dB) (ativo)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Predefinição de cores\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Padrão\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Personalizada\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Sugestão para daltônicos\"\nBasic.Settings.Advanced=\"Avançado\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Avisar sobre saídas ativas ao encerrar OBS\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prioridade do processo\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Alta\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Acima do normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Abaixo do normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inativa\"\nBasic.Settings.Advanced.FormatWarning=\"Aviso: os formatos de cores diferentes de NV12/P010 destinam-se principalmente a gravação e não são recomendados para uso em transmissões. O uso de CPU podem aumentar em transmissões devido à conversão do formato de cor.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Aviso: formatos de alta precisão são mais comumente usados com espaços de cores HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Aviso: Rec. 2100 deve usar um formato com mais precisão.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Formato de cor\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 bits, 4:2:0, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 bits, 4:2:0, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 bits, 4:4:4, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 bits, 4:2:0, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 bits, 4:2:0, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 bits, 4:2:2, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 bits, 4:4:4, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 bits)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Espaço de cores\"\nBasic.Settings.Advanced.Video.ColorRange=\"Intervalo de cores\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limitado\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Completo\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Nível de brancos SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nível de pico nominal do HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Dispositivo de monitoramento\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Padrão\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Desativar a oscilação de áudio do Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Atraso da transmissão\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Duração\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Preservar o ponto de corte ao reconectar (aumenta o atraso)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Uso de memória estimado: %1 MB\"\nBasic.Settings.Advanced.Network=\"Rede\"\nBasic.Settings.Advanced.Network.Disabled=\"O protocolo de transmissão atualmente selecionado não suporta alterações nas configurações de rede.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Ligar pelo IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Família IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Ativar otimizações de rede\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Ativar modo de baixa latência\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Tenta regular a taxa de transmissão para tornar a saída RTMP mais \\\"amigável\\\" para outras aplicações sensíveis à latência na rede.\\nIsso pode aumentar o risco de perda de quadros em conexões instáveis.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Foco nas teclas de atalho\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nunca desativar teclas de atalho\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Desativar teclas de atalho quando a janela principal estiver em foco\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Desativar teclas de atalho quando a janela principal não estiver em foco\"\nBasic.Settings.Advanced.AutoRemux=\"Converter automaticamente para %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(gravar como mkv)\"\nBasic.AdvAudio=\"Propriedades de áudio avançadas\"\nBasic.AdvAudio.ActiveOnly=\"Apenas fontes ativas\"\nBasic.AdvAudio.Name=\"Nome\"\nBasic.AdvAudio.VolumeSource=\"Volume de '%1'\"\nBasic.AdvAudio.MonoSource=\"Downmix de mono de '%1'\"\nBasic.AdvAudio.Balance=\"Balanço\"\nBasic.AdvAudio.BalanceSource=\"Balanço de '%1'\"\nBasic.AdvAudio.SyncOffset=\"Atraso na sincronização\"\nBasic.AdvAudio.SyncOffsetSource=\"Atraso na sincronização de '%1'\"\nBasic.AdvAudio.Monitoring=\"Monitoramento de áudio\"\nBasic.AdvAudio.Monitoring.None=\"Não monitorar\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Só monitorar (saída muda)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitorar e enviar áudio\"\nBasic.AdvAudio.MonitoringSource=\"Monitoramento de áudio de '%1'\"\nBasic.AdvAudio.AudioTracks=\"Faixas\"\nBasic.Settings.Hotkeys=\"Teclas de atalho\"\nBasic.Settings.Hotkeys.Pair=\"Combinações de teclas compartilhadas com '%1' atuam como interruptores\"\nBasic.Settings.Hotkeys.Filter=\"Procurar\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrar por atalho\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Este atalho é compartilhado por uma ou mais ações, clique para mostrar conflitos\"\nBasic.Settings.Hotkeys.PleaseWait=\"Carregando teclas de atalho, por favor aguarde...\"\nBasic.Hotkeys.SelectScene=\"Mudar de cena\"\nBasic.SystemTray.Show=\"Mostrar\"\nBasic.SystemTray.Hide=\"Ocultar\"\nBasic.SystemTray.Message.Reconnecting=\"Desconectado. Tentando reconectar...\"\nHotkeys.Left=\"Esquerda\"\nHotkeys.Right=\"Direita\"\nHotkeys.Up=\"Cima\"\nHotkeys.Down=\"Baixo\"\nHotkeys.Space=\"Espaço\"\nHotkeys.NumpadNum=\"Tecla numérica %1\"\nHotkeys.NumpadMultiply=\"Tecla numérica de multiplicar\"\nHotkeys.NumpadDivide=\"Tecla numérica de dividir\"\nHotkeys.NumpadAdd=\"Tecla numérica de mais\"\nHotkeys.NumpadSubtract=\"Tecla numérica de menos\"\nHotkeys.NumpadDecimal=\"Tecla numérica de decimal\"\nHotkeys.AppleKeypadNum=\"%1 (teclado)\"\nHotkeys.AppleKeypadMultiply=\"* (teclado)\"\nHotkeys.AppleKeypadDivide=\"/ (teclado)\"\nHotkeys.AppleKeypadAdd=\"+ (teclado)\"\nHotkeys.AppleKeypadSubtract=\"/ (teclado)\"\nHotkeys.AppleKeypadDecimal=\". (teclado)\"\nHotkeys.AppleKeypadEqual=\"= (teclado)\"\nHotkeys.MouseButton=\"Botão %1 do mouse\"\nMute=\"Ativar mudo\"\nUnmute=\"Desativar mudo\"\nPush-to-mute=\"Pressionar-para-ativar-mudo\"\nPush-to-talk=\"Pressione-para-falar\"\nSceneItemShow=\"Mostrar '%1'\"\nSceneItemHide=\"Ocultar '%1'\"\nOutputWarnings.NoTracksSelected=\"Você deve selecionar pelo menos uma faixa\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Erro nas configurações de saída\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Todas as saídas devem ter pelo menos uma faixa de áudio selecionada.\"\nOutputWarnings.MP4Recording=\"Atenção: as gravações salvas em MP4/MOV não serão recuperáveis se o arquivo não puder ser concluído (por exemplo, como resultado de um BSOD, perdas de energia, etc.). Se você quiser gravar várias faixas de áudio, aconselhamos utilizar o MKV e, uma vez terminado, converter a gravação para MP4/MOV (Arquivo → Converter gravações)\"\nOutputWarnings.CannotPause=\"Aviso: gravações não podem ser pausadas se o codificador de gravação estiver definido para \\\"(Usar o codificador da transmissão)\\\"\"\nOutputWarnings.CodecIncompatible=\"A seleção de encoder de áudio ou vídeo foi redefinida devido a incompatibilidades. Por favor, selecione um encoder compatível da lista.\"\nCodecCompat.Incompatible=\"(Incompatível com %1)\"\nCodecCompat.CodecPlaceholder=\"Selecionar codificador...\"\nCodecCompat.ContainerPlaceholder=\"Selecionar formato...\"\nCodecCompat.CodecMissingOnExit.Title=\"Nenhum codificador selecionado\"\nCodecCompat.CodecMissingOnExit.Text=\"Nenhum codificador de vídeo ou áudio foi definido. Por favor, certifique-se de selecionar codificadores para gravação e transmissão.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Nenhum formato selecionado\"\nCodecCompat.ContainerMissingOnExit.Text=\"Nenhum formato de gravação foi selecionado. Por favor, selecione um formato de gravação que seja compatível com o codificador de transmissão selecionado.\"\nFinalScene.Title=\"Excluir cena\"\nFinalScene.Text=\"É preciso haver pelo menos uma cena.\"\nNoSources.Title=\"Sem fontes\"\nNoSources.Text=\"Parece que você ainda não adicionou nenhuma fonte de vídeo, portanto, você só exibirá uma tela em branco. Deseja mesmo fazer isso?\"\nNoSources.Text.AddSource=\"Você pode adicionar fontes clicando no ícone + sob a aba de Fontes na janela principal, a qualquer momento.\"\nNoSources.Label=\"Você não tem nenhuma fonte. Clique com o botão direito para adicionar uma ou use o botão + abaixo.\"\nChangeBG=\"Definir Cor\"\nCustomColor=\"Cor personalizada\"\nBrowserSource.EnableHardwareAcceleration=\"Ativar a aceleração por hardware do navegador\"\nAbout=\"Sobre\"\nAbout.Info=\"OBS Studio é um software livre e de código aberto para gravação de vídeo e transmissão em tempo real.\"\nAbout.Donate=\"Fazer uma contribuição\"\nAbout.GetInvolved=\"Participar\"\nAbout.Authors=\"Autores\"\nAbout.License=\"Licenças\"\nAbout.Error=\"Erro! Não foi possível ler o arquivo.\\n\\nVá para: %1\"\nAbout.Contribute=\"Apoie o OBS Project\"\nAddUrl.Title=\"Adicionar fonte via URL\"\nAddUrl.Text=\"Você arrastou uma URL para o OBS. Isso irá adicionar automaticamente o link como fonte. Deseja continuar?\"\nResizeOutputSizeOfSource=\"Redimensionar saída (tamanho da fonte)\"\nResizeOutputSizeOfSource.Text=\"As resoluções base e de saída serão redimensionadas para o tamanho da fonte atual.\"\nResizeOutputSizeOfSource.Continue=\"Você quer continuar?\"\nPreviewTransition=\"Pré-visualizar transição\"\nImporter=\"Importador de coleção de cenas\"\nImporter.SelectCollection=\"Selecione uma coleção de cenas\"\nImporter.Collection=\"Coleção de cenas\"\nImporter.HelpText=\"Arraste arquivos a esta janela para importar coleções do OBS ou de outros programas suportados.\"\nImporter.Path=\"Caminho da coleção\"\nImporter.Program=\"Aplicação detectada\"\nImporter.AutomaticCollectionPrompt=\"Procurar automaticamente por coleções de cenas\"\nImporter.AutomaticCollectionText=\"O OBS pode encontrar automaticamente coleções de cena importáveis de programas de terceiros suportados. Você gostaria que o OBS encontrasse automaticamente coleções para você?\\n\\nVocê pode alterar isso mais tarde em Configurações > Geral > Importadores.\"\nImporter.SelectFile=\"Explorar...\"\nRestart=\"Reiniciar\"\nNeedsRestart=\"OBS Studio precisa ser reiniciado. Deseja reiniciá-lo agora?\"\nLoadProfileNeedsRestart=\"O perfil contém configurações que precisam que o OBS seja reiniciado:\\n%1\\n\\nVocê deseja reiniciar o OBS para que essas configurações sejam aplicadas?\"\nContextBar.NoSelectedSource=\"Nenhuma fonte selecionada\"\nContextBar.MediaControls.PlayMedia=\"Reproduzir mídia\"\nContextBar.MediaControls.PauseMedia=\"Pausar mídia\"\nContextBar.MediaControls.StopMedia=\"Parar mídia\"\nContextBar.MediaControls.RestartMedia=\"Reiniciar mídia\"\nContextBar.MediaControls.PlaylistNext=\"Próxima na lista de reprodução\"\nContextBar.MediaControls.PlaylistPrevious=\"Anterior na lista de reprodução\"\nContextBar.MediaControls.BlindSeek=\"Widget de procura de mídia\"\nYouTube.Auth.Ok=\"Autorização concluída com sucesso.\\nVocê pode fechar esta página.\"\nYouTube.Auth.NoCode=\"O processo de autorização não foi concluído.\"\nYouTube.Auth.NoChannels=\"Nenhum canal disponível na conta selecionada\"\nYouTube.Auth.WaitingAuth.Title=\"Autorização de usuário do YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Por favor, complete a autorização no seu navegador externo.<br>Se o navegador externo não abrir, siga este link e complete a autorização:<br>%1\"\nYouTube.AuthError.Text=\"Não foi possível obter informações do canal: %1.\"\nYouTube.Actions.WindowTitle=\"Configuração de transmissão do YouTube — Canal: %1\"\nYouTube.Actions.CreateNewEvent=\"Nova transmissão\"\nYouTube.Actions.ChooseEvent=\"Selecionar transmissão existente\"\nYouTube.Actions.Title=\"Título*\"\nYouTube.Actions.MyBroadcast=\"Minha transmissão\"\nYouTube.Actions.Description=\"Descrição\"\nYouTube.Actions.Privacy=\"Privacidade*\"\nYouTube.Actions.Privacy.Private=\"Privado\"\nYouTube.Actions.Privacy.Public=\"Público\"\nYouTube.Actions.Privacy.Unlisted=\"Não listado\"\nYouTube.Actions.Category=\"Categoria\"\nYouTube.Actions.Thumbnail=\"Miniatura\"\nYouTube.Actions.Thumbnail.SelectFile=\"Selecionar arquivo...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Nenhum arquivo selecionado\"\nYouTube.Actions.Thumbnail.ClearFile=\"Limpar\"\nYouTube.Actions.MadeForKids=\"Este vídeo é feito para crianças?*\"\nYouTube.Actions.MadeForKids.Yes=\"Sim, é feito para as crianças\"\nYouTube.Actions.MadeForKids.No=\"Não, não é feito para crianças\"\nYouTube.Actions.AdditionalSettings=\"Configurações adicionais\"\nYouTube.Actions.Latency=\"Latência\"\nYouTube.Actions.Latency.Low=\"Baixa\"\nYouTube.Actions.Latency.UltraLow=\"Extremamente baixa\"\nYouTube.Actions.EnableAutoStart=\"Habilitar a inicialização automática\"\nYouTube.Actions.EnableAutoStop=\"Habilitar o encerramento automático\"\nYouTube.Actions.AutoStartStop.TT=\"Indica se essa transmissão programada deve começar automaticamente\"\nYouTube.Actions.EnableDVR=\"Habilitar DVR\"\nYouTube.Actions.360Video=\"Vídeo 360\"\nYouTube.Actions.ScheduleForLater=\"Agendar para mais tarde\"\nYouTube.Actions.RememberSettings=\"Lembrar configurações\"\nYouTube.Actions.Create_Ready=\"Criar transmissão\"\nYouTube.Actions.Create_GoLive=\"Criar e iniciar transmissão\"\nYouTube.Actions.Choose_Ready=\"Selecionar transmissão\"\nYouTube.Actions.Choose_GoLive=\"Selecionar e iniciar transmissão\"\nYouTube.Actions.Create_Schedule=\"Agendar transmissão\"\nYouTube.Actions.Create_Schedule_Ready=\"Agendar e selecionar transmissão\"\nYouTube.Actions.Dashboard=\"Abrir o YouTube Studio\"\nYouTube.Actions.Error.Title=\"Erro na criação da transmissão ao vivo\"\nYouTube.Actions.Error.Text=\"Erro de acesso ao YouTube \\\"%1\\\".<br/>Descrição detalhada do erro pode ser encontrada em <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Não foi possível acessar o YouTube. Por favor, verifique sua conexão de rede ou seu acesso ao servidor do YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Erro de criação de transmissão '%1'.<br/>Descrição de erro detalhada pode ser encontrada em <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Nenhuma transmissão criada. Por favor, reconecte sua conta.\"\nYouTube.Actions.Error.YouTubeApi=\"Erro na API do YouTube. Por favor, consulte o arquivo de registros para obter mais informações.\"\nYouTube.Actions.Error.BroadcastNotFound=\"A transmissão selecionada não foi encontrada.\"\nYouTube.Actions.Error.FileMissing=\"O arquivo selecionado não existe.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Não foi possível abrir o arquivo selecionado.\"\nYouTube.Actions.Error.FileTooLarge=\"O arquivo selecionado é muito grande (limite: 2 MB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Transição da transmissão falhou: %1<br/><br/>Se este erro persistir, <a href='https://studio.youtube.com/video/%2/livestreaming'>abra a transmissão no YouTube Studio</a> e tente manualmente.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Transmissão está passando para o estágio de teste, isso pode levar algum tempo. Por favor, tente novamente em 10 a 30 segundos.\"\nYouTube.Actions.EventsLoading=\"Carregando lista de eventos...\"\nYouTube.Actions.EventCreated.Title=\"Evento criado\"\nYouTube.Actions.EventCreated.Text=\"Evento criado com sucesso.\"\nYouTube.Actions.Stream=\"Transmissão\"\nYouTube.Actions.Stream.ScheduledFor=\"Agendado para %1\"\nYouTube.Actions.Stream.Resume=\"Retomar transmissão interrompida\"\nYouTube.Actions.Stream.YTStudio=\"Criado automaticamente pelo YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Criando uma nova transmissão ao vivo, por favor aguarde...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Início manual necessário\"\nYouTube.Actions.AutoStartStreamingWarning=\"O início automático está desativado para este evento, clique em \\\"Transmitir ao vivo\\\" para iniciar a sua transmissão.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Não será possível reconectar.<br>Sua transmissão será interrompida e você não estará mais ao vivo.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Não é possível transmitir ao vivo no canal do YouTube selecionado, pois a função está desabilitada.<br/><br/>Consulte <a href='https://www.youtube.com/features'>youtube.com/features</a> para mais informações.\"\nYouTube.Errors.livePermissionBlocked=\"Não é possível transmitir ao vivo no canal do YouTube selecionado.<br/>Pode demorar até 24 horas para que a transmissão ao vivo fique disponível depois de habilitada nas configurações do seu canal.<br/><br/>Consulte <a href='https://www.youtube.com/features'>youtube.com/features</a> para mais detalhes.\"\nYouTube.Errors.errorExecutingTransition=\"A transição falhou devido a um erro de back-end. Por favor, tente novamente em alguns segundos.\"\nYouTube.Errors.errorStreamInactive=\"O YouTube não está recebendo dados da sua transmissão. Por favor, verifique suas configurações e tente novamente.\"\nYouTube.Errors.invalidTransition=\"A tentativa de transição foi invalidada. Isto pode estar relacionado com uma transição ainda pendente. Aguarde alguns segundos e tente novamente.\"\nYouTube.DocksRemoval.Title=\"Excluir painéis legados do YouTube\"\nYouTube.DocksRemoval.Text=\"Os seguintes painéis foram descontinuados e serão removidos:\\n\\n%1\\nEm vez disso, use \\\"Painéis/Sala de controle ao vivo do YouTube\\\".\"\nConfigDownload.WarningMessageTitle=\"Aviso\"\nFailedToStartStream.MissingConfigURL=\"Nenhuma URL de configuração disponível para o serviço atual\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"URL RTMP personalizada não especificada\"\nFailedToStartStream.InvalidCustomConfig=\"Configuração personalizada inválida\"\nFailedToStartStream.MissingCanvas=\"Uma área extra de edição está faltando\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Falha ao criar serviço de vídeo multifaixa\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Falha ao criar saída RTMP de vídeo multifaixa\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC não disponível.\\n\\nNão foi possível encontrar o tipo de codificador '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Não foi possível criar o codificador de vídeo '%1' (tipo: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Não foi possível obter informações do vídeo do OBS durante criação do codificador '%1' (tipo: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Não foi possível criar o codificador de áudio\"\nFailedToStartStream.NoRTMPURLInConfig=\"A configuração não contém URL RTMP(S) de destino da transmissão\"\nFailedToStartStream.FallbackToDefault=\"Não foi possível iniciar a transmissão usando %1. Você deseja tentar novamente usando configurações de codificação única?\"\nFailedToStartStream.ConfigRequestFailed=\"Não foi possível buscar as configurações em %1<br><br>Erro HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Recebido um valor de status desconhecido: '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nVocê deseja continuar transmitindo sem %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nVocê deseja continuar transmitindo?\"\nFailedToStartStream.MissingEncoderConfigs=\"A configuração de transmissão ao vivo não incluiu as configurações do codificador\"\nFailedToStartStream.InvalidEncoderConfig=\"A configuração de transmissão ao vivo incluiu uma configuração inválida do codificador\"\nFailedToStartStream.StatusMissingHTML=\"Solicitação de transmissão ao vivo retornou um erro não especificado\"\nFailedToStartStream.NoConfigSupplied=\"Configuração ausente\"\nMultitrackVideo.Info=\"%1 otimiza automaticamente suas configurações para codificar e enviar video em múltiplas qualidades. Selecionar esta opção enviará %2 informações sobre a configuração do seu computador e software.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Configurações incompatíveis\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 ainda não é compatível com:\\n\\n%2\\nPara continuar transmitindo com %1, desabilite as configurações incompatíveis:\\n\\n%3\\ne inicie a transmissão novamente.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Desativar para esta transmissão e iniciar transmissão\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Atualizar configurações e iniciar transmissão\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 atualmente não é compatível com [Áudio → Geral → Canais] definido para '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Áudio → Geral → Canais] precisa ser definido para '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 requer múltiplas configurações diferentes para [Áudio → Geral → Canais]\"\nBasic.OpenPluginManager=\"Gerenciador de plugins\"\nPluginManager=\"Gerenciador de plugins\"\nPluginManager.Restart=\"O OBS Studio precisa ser reiniciado para aplicar as alterações de plugin\"\nPluginManager.MissingPlugin=\"[PLUGIN NÃO ENCONTRADO]\"\nPluginManager.Section.Discover=\"Explorar\"\nPluginManager.Section.Manage=\"Instalados\"\nPluginManager.Section.Updates=\"Atualizações\"\nPluginManager.Section.Manage.Title=\"Gerenciar plugins ativos\"\n"
  },
  {
    "path": "frontend/data/locale/pt-PT.ini",
    "content": "Language=\"Português (Portugal)\"\nOK=\"Aceitar\"\nApply=\"Aplicar\"\nCancel=\"Cancelar\"\nClose=\"Fechar\"\nSave=\"Guardar\"\nDiscard=\"Descartar\"\nDisable=\"Desativar\"\nYes=\"Sim\"\nNo=\"Não\"\nAdd=\"Adicionar\"\nRemove=\"Remover\"\nRename=\"Renomear\"\nInteract=\"Interagir\"\nFilters=\"Filtros\"\nProperties=\"Propriedades\"\nMoveUp=\"Mover acima\"\nMoveDown=\"Mover abaixo\"\nSettings=\"Definições\"\nDisplay=\"Monitor\"\nName=\"Nome\"\nExit=\"Sair\"\nMixer=\"Misturador de áudio\"\nBrowse=\"Navegar\"\nStereo=\"Estéreo\"\nDroppedFrames=\"Fotogramas perdidos %1 (%2%)\"\nProjector.Open.Program=\"Abrir projetor de programa\"\nProjector.Open.Preview=\"Abrir projetor de pré-visualização\"\nProjector.Open.Scene=\"Abrir projetor de cena\"\nProjector.Open.Source=\"Abrir projetor de fonte\"\nProjector.Open.Multiview=\"Abrir multivisualização\"\nProjector.Display=\"Monitor: %1\"\nProjector.Window=\"Nova janela\"\nProjector.Title=\"Projetor\"\nProjector.Title.Scene=\"Cena: %1\"\nProjector.Title.Source=\"Fonte: %1\"\nProjector.Title.Multiview=\"Multivisualização\"\nProjector.ResizeWindowToContent=\"Ajustar janela ao conteúdo\"\nClear=\"Limpar\"\nRevert=\"Reverter\"\nShow=\"Mostrar\"\nHide=\"Ocultar\"\nUnhideAll=\"Mostrar tudo\"\nUntitled=\"Sem título\"\nNew=\"Novo\"\nDuplicate=\"Duplicar\"\nEnable=\"Ativar\"\nDisableOSXVSync=\"Desativar macOS V-Sync\"\nResetOSXVSyncOnExit=\"Repor macOS V-Sync ao sair\"\nHighResourceUsage=\"Codificação sobrecarregada! Tente diminuir as definições de vídeo ou usar uma predefinição de codificação mais rápida.\"\nTransition=\"Transição\"\nQuickTransitions=\"Transições rápidas\"\nFadeToBlack=\"Desvanecer para preto\"\nLeft=\"Esquerda\"\nRight=\"Direita\"\nTop=\"Topo\"\nBottom=\"Fundo\"\nReset=\"Repor\"\nHours=\"Horas\"\nMinutes=\"Minutos\"\nSeconds=\"Segundos\"\nDeprecated=\"Descontinuado\"\nReplayBuffer=\"Memória de repetição\"\nImport=\"Importar\"\nExport=\"Exportar\"\nCopy=\"Copiar\"\nPaste=\"Colar\"\nPasteReference=\"Colar (referência)\"\nPasteDuplicate=\"Colar (duplicado)\"\nRemuxRecordings=\"Converter gravações\"\nNext=\"Seguinte\"\nBack=\"Anterior\"\nDefaults=\"Predefinições\"\nRestoreDefaults=\"Predefinições\"\nHideMixer=\"Ocultar no misturador\"\nTransitionOverride=\"Sobrepor transição\"\nShowTransition=\"Transição de mostrar\"\nHideTransition=\"Transição de ocultar\"\nNone=\"Nenhuma\"\nStudioMode.Preview=\"Antevisão\"\nStudioMode.Program=\"Programa\"\nStudioMode.PreviewSceneName=\"Antevisão: %1\"\nStudioMode.ProgramSceneName=\"Programa: %1\"\nShowInMultiview=\"Mostrar em multivisualização\"\nVerticalLayout=\"Esquema vertical\"\nGroup=\"Grupo\"\nDoNotShowAgain=\"Não voltar a mostrar\"\nDefault=\"(predefinição)\"\nCalculating=\"A calcular...\"\nFullscreen=\"Ecrã total\"\nWindowed=\"Em janela\"\nRefreshBrowser=\"Atualizar\"\nAspectRatio=\"Proporção <b>%1:%2</b>\"\nLockVolume=\"Bloquear volume\"\nLogViewer=\"Visualizador de diários\"\nShowOnStartup=\"Mostrar no arranque\"\nOpenFile=\"Abrir ficheiro\"\nAddScene=\"Adicionar cena\"\nAddSource=\"Adicionar fonte\"\nRemoveScene=\"Remover cena selecionada\"\nRemoveSource=\"Remover cena(s) selecionada(s)\"\nMoveSceneUp=\"Mover cena para cima\"\nMoveSceneDown=\"Mover cena para baixo\"\nMoveSourceUp=\"Mover fonte(s) para cima\"\nMoveSourceDown=\"Mover fonte(s) para baixo\"\nSourceProperties=\"Propriedades Código Aberto\"\nSourceFilters=\"Filtros de Código Aberto\"\nMixerToolbarMenu=\"Menu do misturador de áudio\"\nSceneFilters=\"Abrir filtros de cena\"\nList=\"Lista\"\nGrid=\"Grelha\"\nAutomatic=\"Automático\"\nComingSoon=\"Brevemente\"\nPluginsFailedToLoad.Title=\"Erro ao carregar o plugin\"\nPluginsFailedToLoad.Text=\"Falha ao carregar os seguintes plugins do OBS:\\n\\n%1\\nPor favor, atualize ou remova estes plugins.\"\nAlreadyRunning.Title=\"O OBS já está em execução\"\nAlreadyRunning.Text=\"O OBS já está em execução! Tem de desligar a instância existente do OBS antes de iniciar uma nova. Se o OBS estiver definido para correr na bandeja do sistema, por favor, verifique se ainda lá está em execução.\"\nAlreadyRunning.LaunchAnyway=\"Iniciar mesmo assim\"\nCrashHandling.Dialog.Title=\"Falha detetada no OBS Studio\"\nCrashHandling.Labels.Text=\"O OBS Studio não encerrou corretamente.\\n\\nExexutar em modo de segurança (plugins externos, scripts, e WebSockets desativados)?\"\nCrashHandling.Labels.PrivacyNotice=\"Também pode escolher enviar automaticamente para o OBSProject o relatório de falhas mais recente.<br /><br />Por favor, leia a <a href='https://obsproject.com/privacy-policy'>Política de privacidade</a> antes de enviar qualquer ficheiro e tenha atenção às secções acerca de carregamentos de ficheiros.\"\nCrashHandling.Checkbox.SendReport=\"Li a política de privacidade e consinto o carregamento.\"\nCrashHandling.Buttons.LaunchSafe=\"Executar em modo de segurança\"\nCrashHandling.Buttons.LaunchNormal=\"Excecutar em Modo normal\"\nCrashHandling.Errors.UploadJSONError=\"Ocorreu um erro ao tentar carregar o ficheiro de registo de falhas mais recente. Por favor, tente novamente mais tarde\"\nCrashHandling.Errors.Title=\"Erro as carregar o ficheiro de registo de falhas\"\nSafeMode.Restart=\"Deseja reiniciar o OBS em modo de segurança (plugins de terceiros, scripts e WebSockets desativados)?\"\nSafeMode.RestartNormal=\"Deseja reiniciar o OBS em modo normal?\"\nChromeOS.Title=\"Plataforma não suportada\"\nChromeOS.Text=\"O OBS parece estar a ser executado dentro de um contentor ChromeOS. Esta plataforma não é suportada.\"\nWine.Title=\"Wine detetado\"\nWine.Text=\"Executar o OBS em Wine não é suportado e muitas funcionalidades, tais como fontes de captura ou de dispositivos, não irão funcionar ou terão a sua capacidade limitada.<br><br>Em vez disso, é recomendado executar uma versão nativa do OBS. Pode usar, por exemplo, a nossa <a href='https://flathub.org/apps/details/com.obsproject.Studio'>Versão Flatpak</a> ou os pacotes destinados ao seu sistema operativo.\"\nDockCloseWarning.Title=\"Fechar janela de doca\"\nDockCloseWarning.Text=\"Acabou de fechar uma janela de doca. Se quiser abri-la novamente, use o menu Docas na barra de menus.\"\nExtraBrowsers=\"Docas de navegador personalizadas\"\nExtraBrowsers.Info=\"Adicione docas dando-lhes um nome e URL, depois clique em Aplicar ou Fechar para as abrir. Pode adicionar ou remover docas a qualquer altura.\"\nExtraBrowsers.DockName=\"Nome da doca\"\nAuth.Authing.Title=\"A autenticar...\"\nAuth.Authing.Text=\"A autenticar com %1, por favor, aguarde...\"\nAuth.AuthFailure.Title=\"Falha na autenticação\"\nAuth.AuthFailure.Text=\"Falha na autenticação com %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Autenticação necessária\"\nAuth.InvalidScope.Text=\"Os requisitos de autenticação para %1 mudaram. Algumas funcionalidades podem não estar disponíveis.\"\nAuth.LoadingChannel.Title=\"A carregar informação do canal...\"\nAuth.LoadingChannel.Text=\"A carregar informação do canal para %1, por favor, aguarde...\"\nAuth.LoadingChannel.Error=\"Impossível obter informações do canal.\"\nAuth.ChannelFailure.Title=\"Falha ao carregar o canal\"\nAuth.ChannelFailure.Text=\"Falha ao carregar informação do canal para %1\\n\\n%2: %3\"\nAuth.StreamInfo=\"Informação da transmissão\"\nTwitchAuth.Stats=\"Estatísticas da Twitch\"\nTwitchAuth.Feed=\"Fonte de atividade do Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Impossível obter a chave da transmissão\"\nTwitchAuth.TwoFactorFail.Text=\"O OBS não conseguiu ligação à conta Twitch. Por favor, certifique-se de que a autenticação a dois passos está configurada nas <a href='https://www.twitch.tv/settings/security'>definições de segurança da Twitch</a>, pois é necessária para transmissão.\"\nRestreamAuth.Channels=\"Canais de Restream\"\nCopy.Filters=\"Copiar filtros\"\nPaste.Filters=\"Colar filtros\"\nBrowserPanelInit.Title=\"A iniciar o navegador...\"\nBrowserPanelInit.Text=\"A iniciar o navegador, por favor, aguarde...\"\nBandwidthTest.Region=\"Região\"\nBandwidthTest.Region.US=\"Estados Unidos\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Ásia\"\nBandwidthTest.Region.Other=\"Outra\"\nBasic.AutoConfig=\"Assistente de configuração automática\"\nBasic.AutoConfig.ApplySettings=\"Aplicar definições\"\nBasic.AutoConfig.StartPage=\"Informação de utilização\"\nBasic.AutoConfig.StartPage.SubTitle=\"Especifique qual vai ser a utilização do programa\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Otimizar para transmissão, a gravação é secundária\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Otimizar para gravação, não irei transmitir\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Só será utilizada a câmera virtual\"\nBasic.AutoConfig.VideoPage=\"Definições de vídeo\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Especifique as definições de vídeo que pretende usar\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Usar atual (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Monitor %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Usar atual (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ou 30, com prioridade a 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ou 30, com prioridade a alta resolução\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Nota: a resolução base da área de trabalho não é necessariamente a mesma que a resolução com que irá gravar ou transmitir. A resolução real da sua gravação/transmissão pode ser reduzida a partir da resolução da área de trabalho para diminuir o uso de recursos ou os requisitos de taxa de bits.\"\nBasic.AutoConfig.StreamPage=\"Informação da transmissão\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Por favor, insira a informação da sua transmissão\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Associar conta (recomendado)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Desligar conta\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Desligar conta?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Esta alteração será aplicada imediatamente. Tem a certeza de que deseja desligar a conta?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Obter chave de transmissão\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Mais informações\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Usar chave de transmissão\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Usar chave da transmissão (avançado)\"\nBasic.AutoConfig.StreamPage.Service=\"Serviço\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Mostrar tudo...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Personalizado...\"\nBasic.AutoConfig.StreamPage.Server=\"Servidor\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Chave de transmissão\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: insira a frase-passe de encriptação.\\nRTMP: insira a chave fornecida pelo seu serviço.\\nSRT: insira o ID de transmissão se o serviço usar um.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Chave de codificação\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Chave Bearer\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Conta ligada\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Estimar taxa de bits com teste de largura de banda (pode demorar alguns minutos)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferir codificação de equipamento\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"A codificação por equipamento elimina a maior parte da utilização da CPU, mas pode necessitar de uma taxa de bits maior para obter o mesmo nível de qualidade.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Aviso de transmissão\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"O teste de largura de banda vai transmitir dados de vídeo aleatórios sem áudio para o seu canal. É recomendado desligar temporariamente gravações de transmissões e definir a transmissão como privada até o teste estar completo. Continuar?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Testar %1\"\nBasic.AutoConfig.TestPage=\"Resultado final\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"O programa está agora a executar testes para estimar as definições ideais\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Teste completo\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"A executar teste de largura de banda, pode demorar alguns minutos...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Nenhuma saída encontrada para o protocolo deste serviço\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"A ligar a: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Falha ao ligar a qualquer servidor. Por favor, verifique a ligação à Internet e tente novamente.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"A testar largura de banda para: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"A testar codificador de transmissão, poderá demorar algum tempo...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"A testar codificador de gravação, poderá demorar algum tempo...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Falha ao iniciar o codificador\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"A testar %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Codificador de transmissão\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Codificador de gravação\"\nBasic.AutoConfig.TestPage.Result.Header=\"O programa determinou que estas são as definições ideais para si:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Para utilizar estas definições, clique em Aplicar definições. Para reconfigurar o assistente e tentar novamente, clique em Anterior. Para configurar manualmente as definições, clique em Cancelar e abra as definições.\"\nBasic.AutoConfig.Info=\"O assistente de configuração automática vai determinar as melhores definições baseadas nas especificações do computador e na velocidade da Internet.\"\nBasic.AutoConfig.RunAnytime=\"Isto pode ser executado a qualquer momento através do menu Ferramentas.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Resolução de transmissão (redimensionada)\"\nBasic.Stats=\"Estatísticas\"\nBasic.Stats.CPUUsage=\"Utilização de CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Espaço em disco disponível\"\nBasic.Stats.MemoryUsage=\"Utilização de memória\"\nBasic.Stats.AverageTimeToRender=\"Tempo médio para processar um fotograma\"\nBasic.Stats.SkippedFrames=\"Fotogramas saltados por atraso na codificação\"\nBasic.Stats.MissedFrames=\"Fotogramas perdidos por atraso no processamento\"\nBasic.Stats.Output.Stream=\"Transmissão\"\nBasic.Stats.Output.Recording=\"Gravação\"\nBasic.Stats.Status=\"Estado\"\nBasic.Stats.Status.Recording=\"A gravar\"\nBasic.Stats.Status.Live=\"AO VIVO\"\nBasic.Stats.Status.Reconnecting=\"A ligar novamente\"\nBasic.Stats.Status.Inactive=\"Inativo\"\nBasic.Stats.Status.Active=\"Ativo\"\nBasic.Stats.DroppedFrames=\"Fotogramas perdidos (rede)\"\nBasic.Stats.MegabytesSent=\"Saída de dados total\"\nBasic.Stats.Bitrate=\"Taxa de bits\"\nBasic.Stats.DiskFullIn=\"Disco cheio em (aprox.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 h. e %2 min.\"\nBasic.Stats.ResetStats=\"Repor estatísticas\"\nResetUIWarning.Title=\"Tem a certeza de que deseja reiniciar a interface de utilizador?\"\nResetUIWarning.Text=\"Reiniciar a interface de utilizador vai ocultar as docas adicionais. Terá de as ativar novamente no menu Docas se as quiser tornar visíveis.\\n\\nTem a certeza que deseja reiniciar a interface?\"\nUpdater.Title=\"Nova atualização disponível\"\nUpdater.Text=\"Está disponível uma nova atualização:\"\nUpdater.UpdateNow=\"Atualizar agora\"\nUpdater.RemindMeLater=\"Lembrar-me mais tarde\"\nUpdater.Skip=\"Ignorar versão\"\nUpdater.NoUpdatesAvailable.Title=\"Nenhuma atualização disponível\"\nUpdater.NoUpdatesAvailable.Text=\"Não existem atualizações disponíveis\"\nUpdater.BranchNotFound.Title=\"Canal de atualização removido\"\nUpdater.BranchNotFound.Text=\"O seu canal de atualização já não se encontra disponível, o OBS foi redefinido para o canal predefinido.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Verificação de integridade indisponível\"\nUpdater.RepairButUpdatesAvailable.Text=\"A verificação da integridade de ficheiro só é possível na versão mais recente. Use Ajuda → Verificar atualizações para verificar e atualizar a sua instalação do OBS.\"\nUpdater.RepairConfirm.Title=\"Confirmar verificação da integridade\"\nUpdater.RepairConfirm.Text=\"Ao iniciar a verificação de integridade irá verificar a sua instalação OBS ao nível de corrupção de ficheiros e descarregará novamente ficheiros que sejam identificados como danificados ou alterados. Isto pode demorar algum tempo.\\n\\nPretende continuar?\"\nUpdater.FailedToLaunch=\"Falha ao iniciar as atualizações\"\nQuickTransitions.SwapScenes=\"Trocar cenas de antevisão/programa após uma transição\"\nQuickTransitions.SwapScenesTT=\"Troca as cenas de antevisão/programa após a transição (se a cena de saída original ainda existir).\\nIsto não desfaz qualquer alteração que possa ter havido com a cena original do programa.\"\nQuickTransitions.DuplicateScene=\"Duplicar cena\"\nQuickTransitions.DuplicateSceneTT=\"Ao editar a mesma cena, permite editar a transformação/visibilidade de fontes sem modificar a saída do programa.\\nPara editar propriedades de fontes sem modificar a saída do programa, ative \\\"Duplicar fontes\\\".\\nMudar este valor vai reiniciar a cena do programa atual (se ainda existir).\"\nQuickTransitions.EditProperties=\"Duplicar fontes\"\nQuickTransitions.EditPropertiesTT=\"Ao editar a mesma cena, permite editar as propriedades de fontes sem modificar a saída do programa.\\nIsto só pode ser usado se \\\"Duplicar cena\\\" estiver ativado.\\nAlgumas fontes (como a captura e fontes multimédia) não o suportam e não podem ser editadas separadamente.\\nModificar este valor redefine a cena atual do programa (se ainda existir).\\n\\nAviso: como as fontes vão ser duplicadas, pode implicar mais recursos de sistema ou vídeo.\"\nQuickTransitions.HotkeyName=\"Transição rápida: %1\"\nBasic.AddTransition=\"Adicionar transição configurável\"\nBasic.RemoveTransition=\"Remover transição configurável\"\nBasic.TransitionProperties=\"Propriedades da transição\"\nBasic.SceneTransitions=\"Transições de cena\"\nBasic.TransitionDuration=\"Duração\"\nBasic.TogglePreviewProgramMode=\"Modo de estúdio\"\nBasic.EnablePreviewProgramMode=\"Ativar o modo estúdio\"\nBasic.DisablePreviewProgramMode=\"Desativar o modo estúdio\"\nUndo.Undo=\"Desfazer\"\nUndo.Redo=\"Refazer\"\nUndo.Add=\"Adicionar '%1'\"\nUndo.Delete=\"Eliminar '%1'\"\nUndo.Rename=\"Renomear '%1'\"\nUndo.SceneCollection.Switch=\"Mudar para '%1'\"\nUndo.Item.Undo=\"Desfazer %1\"\nUndo.Item.Redo=\"Refazer %1\"\nUndo.Sources.Multi=\"Eliminar %1 fontes\"\nUndo.Filters=\"Alterações de filtro em '%1'\"\nUndo.Filters.Paste.Single=\"Colar filtro '%1' em '%2'\"\nUndo.Filters.Paste.Multiple=\"Copiar filtros de '%1' para '%2'\"\nUndo.Transform=\"Transformar fonte(s) em '%1'\"\nUndo.Transform.Paste=\"Colar transformação em '%1'\"\nUndo.Transform.Rotate=\"Rotação em '%1'\"\nUndo.Transform.Reset=\"Repor transformação em '%1'\"\nUndo.Transform.HFlip=\"Inversão horizontal em '%1'\"\nUndo.Transform.VFlip=\"Inversão vertical em '%1'\"\nUndo.Transform.FitToScreen=\"Ajustar ao ecrã em '%1'\"\nUndo.Transform.StretchToScreen=\"Esticar ao ecrã em '%1'\"\nUndo.Transform.Center=\"Centrar no ecrã em '%1'\"\nUndo.Transform.VCenter=\"Centrar verticalmente no ecrã em '%1'\"\nUndo.Transform.HCenter=\"Centrar horizontalmente no ecrã em '%1'\"\nUndo.Volume.Change=\"Alteração de volume em '%1'\"\nUndo.Volume.Mute=\"Silenciar '%1'\"\nUndo.Volume.Unmute=\"Desativar silêncio em '%1'\"\nUndo.Balance.Change=\"Alteração de equilíbrio áudio em '%1'\"\nUndo.SyncOffset.Change=\"Alteração de desvio de sincronização áudio em '%1'\"\nUndo.MonitoringType.Change=\"Alterar monitorização de áudio em '%1'\"\nUndo.Mixers.Change=\"Alterar misturadores áudio em '%1'\"\nUndo.ForceMono.On=\"Ativar Mono forçado em '%1'\"\nUndo.ForceMono.Off=\"Desativar Mono forçado em '%1'\"\nUndo.Properties=\"Alteração de propriedade em '%1'\"\nUndo.Scene.Duplicate=\"Duplicar cena '%1'\"\nUndo.ShowTransition=\"Transição de mostrar em '%1'\"\nUndo.HideTransition=\"Transição de ocultar em '%1'\"\nUndo.ShowSceneItem=\"Mostrar '%1' em '%2'\"\nUndo.HideSceneItem=\"Ocultar '%1' em '%2'\"\nUndo.ReorderSources=\"Reordenar fontes em '%1'\"\nUndo.MoveUp=\"Mover '%1' acima em '%2'\"\nUndo.MoveDown=\"Mover '%1' abaixo em '%2'\"\nUndo.MoveToTop=\"Mover '%1' para o topo em '%2'\"\nUndo.MoveToBottom=\"Mover '%1' para o fundo em '%2'\"\nUndo.PasteSource=\"Colar fonte(s) em '%1'\"\nUndo.PasteSourceRef=\"Colar referência(s) de fonte em '%1'\"\nUndo.GroupItems=\"Agrupar itens em '%1'\"\nTransitionNameDlg.Text=\"Por favor, escreva o nome da transição\"\nTransitionNameDlg.Title=\"Nome da transição\"\nTitleBar.SafeMode=\"Modo de segurança\"\nTitleBar.PortableMode=\"Modo portátil\"\nTitleBar.Profile=\"Perfil\"\nTitleBar.Scenes=\"Cenas\"\nNameExists.Title=\"O nome já existe\"\nNameExists.Text=\"O nome já está em uso.\"\nNoNameEntered.Title=\"Por favor, insira um nome válido\"\nNoNameEntered.Text=\"Não pode utilizar nomes vazios.\"\nConfirmStart.Title=\"Começar a transmitir?\"\nConfirmStart.Text=\"Tem a certeza de que quer começar a transmissão?\"\nConfirmStop.Title=\"Parar a transmissão?\"\nConfirmStop.Text=\"Tem a certeza de que quer parar a transmissão?\"\nConfirmStopRecord.Title=\"Parar a gravação?\"\nConfirmStopRecord.Text=\"Tem a certeza de que deseja parar a gravação?\"\nConfirmBWTest.Title=\"Iniciar teste de largura de banda?\"\nConfirmBWTest.Text=\"O OBS está configurado em modo de teste de largura de banda. Este modo permite o teste de rede sem que o seu canal seja emitido. Uma vez terminado o teste terá de o desativar para que os espetadores possam ver a transmissão.\\n\\nDeseja continuar?\"\nConfirmExit.Title=\"Saídas ativas\"\nConfirmExit.Text=\"O OBS ainda está ativo. Todas as transmissões/gravações serão encerradas.\"\nConfirmRemove.Title=\"Confirmar remoção\"\nConfirmRemove.Text=\"Tem a certeza que quer remover '%1'?\"\nConfirmRemove.TextMultiple=\"Tem a certeza de que pretende remover %1 itens?\"\nConfirmReset.Title=\"Repor propriedades\"\nConfirmReset.Text=\"Tem certeza que deseja redefinir as propriedades atuais para as suas predefinições?\"\nOutput.StartStreamFailed=\"Falha ao iniciar a transmissão\"\nOutput.StartRecordingFailed=\"Falha ao iniciar a gravação\"\nOutput.StartReplayFailed=\"Falha ao iniciar a memória de repetição\"\nOutput.StartVirtualCamFailed=\"Falha ao iniciar a câmera virtual\"\nOutput.StartFailedGeneric=\"Falha ao iniciar a saída. Verifique o ficheiro de diário para mais detalhes.\\n\\nNota: se está a usar codificadores NVENC ou AMD, certifique-se de que os controladores da placa gráfica estão atualizados.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Não é possível guardar repetições quando pausado\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Aviso: as repetições não podem ser guardadas quando a gravação está em pausa.\"\nOutput.ConnectFail.Title=\"Falha ao ligar\"\nOutput.ConnectFail.BadPath=\"Caminho ou URL de ligação inválidos. Por favor, confirme que as suas definições são válidas.\"\nOutput.ConnectFail.ConnectFailed=\"Falha ao ligar ao servidor\"\nOutput.ConnectFail.InvalidStream=\"Impossível aceder ao canal ou chave de transmissão especificados, por favor, verifique a chave de transmissão. Se estiver correta, pode existir um problema de ligação ao servidor.\"\nOutput.ConnectFail.HdrDisabled=\"A saída HDR está atualmente desativada para esta saída.\"\nOutput.ConnectFail.Error=\"Ocorreu um erro inesperado ao ligar ao servidor. Mais informação no ficheiro de registo diário.\"\nOutput.ConnectFail.Disconnected=\"Desligado do servidor.\"\nOutput.StreamEncodeError.Title=\"Erro de codificação\"\nOutput.StreamEncodeError.Msg=\"Ocorreu um erro de codificação ao transmitir.\"\nOutput.StreamEncodeError.Msg.LastError=\"Ocorreu um erro de codificação ao transmitir:<br><br>%1\"\nOutput.RecordFail.Title=\"Falha ao iniciar a gravação\"\nOutput.RecordFail.Unsupported=\"O formato de saída não é suportado ou não suporta mais do que uma pista de áudio. Por favor, verifique as suas definições e tente novamente.\"\nOutput.RecordNoSpace.Title=\"Espaço em disco insuficiente\"\nOutput.RecordNoSpace.Msg=\"Não há espaço em disco suficiente para continuar a gravação.\"\nOutput.RecordError.Title=\"Erro de gravação\"\nOutput.RecordError.Msg=\"Ocorreu um erro desconhecido durante a gravação.\"\nOutput.RecordError.EncodeErrorMsg=\"Ocorreu um erro de codificação ao gravar.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Ocorreu um erro de codificação ao gravar:<br><br>%1\"\nOutput.BadPath.Title=\"Caminho de ficheiro inválido\"\nOutput.BadPath.Text=\"Não foi possível abrir o caminho de gravação configurado. Por favor, verifique o caminho de gravação em Definições → Saída → Gravação.\"\nOutput.NoBroadcast.Title=\"Nenhuma emissão configurada\"\nOutput.NoBroadcast.Text=\"Tem de configurar uma emissão antes de começar a transmitir.\"\nOutput.BroadcastStartFailed=\"Falha ao iniciar a emissão\"\nOutput.BroadcastStopFailed=\"Falha ao parar a emissão\"\nLogUploadDialog.Title=\"Carregamento de ficheiro de registos do OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Por favor, leia a <a href='https://obsproject.com/privacy-policy'>Política de privacidade</a> e a sua secção sobre carregamentos, antes de enviar qualquer ficheiro\"\nLogUploadDialog.Labels.Progress=\"Carregamento do ficheiro de registo em curso. Por favor, aguarde...\"\nLogUploadDialog.Labels.Description.AppLog=\"O seu ficheiro de registos foi carregado. Pode agora partilhar o URL para fins de depuração ou suporte.\"\nLogUploadDialog.Labels.Description.CrashLog=\"O seu relatório de falhas foi carregado. Pode agora partilhar o URL para fins de depuração.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Carregar\"\nLogUploadDialog.Buttons.CopyURL=\"Copiar URL do ficheiro de registo\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analisar ficheiro de registo\"\nLogUploadDialog.Buttons.RetryButton=\"Repetir\"\nLogUploadDialog.Errors.Template=\"Ocorreu um erro ao tentar carregar o ficheiro:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Não foi encontrado qualquer ficheiro para carregar ou o ficheiro está vazio.\"\nRemux.SourceFile=\"Gravação do OBS\"\nRemux.TargetFile=\"Ficheiro de destino\"\nRemux.Remux=\"Converter\"\nRemux.Stop=\"Parar conversão\"\nRemux.ClearFinished=\"Limpar itens terminados\"\nRemux.ClearAll=\"Limpar todos os itens\"\nRemux.OBSRecording=\"Gravação do OBS\"\nRemux.FinishedTitle=\"Conversão concluída\"\nRemux.Finished=\"Gravação convertida\"\nRemux.FinishedError=\"Gravação convertida, mas o ficheiro pode estar incompleto\"\nRemux.SelectRecording=\"Selecionar gravação do OBS...\"\nRemux.SelectTarget=\"Selecionar ficheiro de destino...\"\nRemux.FileExistsTitle=\"Ficheiros já existentes\"\nRemux.FileExists=\"Os seguintes ficheiros de destino já existem. Deseja substituí-los?\"\nRemux.ExitUnfinishedTitle=\"Conversão em curso\"\nRemux.ExitUnfinished=\"A conversão não está concluída. Ao parar agora pode tornar o ficheiro de destino inutilizável.\\nTem a certeza de que pretende parar a conversão?\"\nRemux.HelpText=\"Solte ficheiros nesta janela para os converter, ou selecione uma célula \\\"Gravação do OBS\\\" vazia para procurar um ficheiro.\"\nRemux.NoFilesAddedTitle=\"Nenhum ficheiro de conversão adicionado\"\nRemux.NoFilesAdded=\"Nenhum ficheiro de conversão adicionado ao conversor. Solte aqui uma pasta contendo um ou mais ficheiros de vídeo.\"\nMissingFiles=\"Ficheiros em falta\"\nMissingFiles.MissingFile=\"Ficheiro em falta\"\nMissingFiles.NewFile=\"Novo ficheiro\"\nMissingFiles.HelpText=\"Alguns ficheiros estão em falta desde a última utilização do OBS Studio.\"\nMissingFiles.Clear=\"<vazio>\"\nMissingFiles.NumFound=\"Encontrado %1 de %2\"\nMissingFiles.Search=\"Procurar nas pastas...\"\nMissingFiles.SelectFile=\"Selecionar ficheiro...\"\nMissingFiles.SelectDir=\"Selecionar pasta onde procurar\"\nMissingFiles.State=\"Estado\"\nMissingFiles.Missing=\"Em falta\"\nMissingFiles.Replaced=\"Substituído\"\nMissingFiles.Cleared=\"Limpo\"\nMissingFiles.Found=\"Encontrado\"\nMissingFiles.AutoSearch=\"Outros ficheiros correspondentes encontrados\"\nMissingFiles.AutoSearchText=\"O OBS encontrou correspondências adicionais para ficheiros em falta na pasta. Deseja adicioná-los?\"\nMissingFiles.NoMissing.Title=\"Procurar ficheiros em falta\"\nMissingFiles.NoMissing.Text=\"Não parece haver ficheiros em falta\"\nMacPermissions.MenuAction=\"Rever permissões da aplicação...\"\nMacPermissions.Title=\"Rever permissões da aplicação\"\nMacPermissions.Description=\"O OBS Studio requer algumas permissões explícitas para poder disponibilizar certas funcionalidades. É recomendável fornecer essas permissões embora não sejam necessárias para usar a aplicação. Pode sempre fornecer essas permissões mais tarde.\"\nMacPermissions.Description.OpenDialog=\"Pode reabrir esta caixa de diálogo através do menu OBS Studio.\"\nMacPermissions.AccessGranted=\"Acesso concedido\"\nMacPermissions.RequestAccess=\"Solicitar acesso\"\nMacPermissions.OpenPreferences=\"Abrir preferências de %1\"\nMacPermissions.Item.ScreenRecording=\"Gravação de ecrã\"\nMacPermissions.Item.ScreenRecording.Details=\"O OBS necessita desta permissão para poder capturar o seu ecrã.\"\nMacPermissions.Item.Camera=\"Câmera\"\nMacPermissions.Item.Camera.Details=\"Esta permissão é necessária para poder capturar conteúdo de uma webcam ou cartão de captura.\"\nMacPermissions.Item.Microphone=\"Microfone\"\nMacPermissions.Item.Microphone.Details=\"O OBS necessita desta permissão se você pretender capturar o seu microfone ou um dispositivo de áudio externo.\"\nMacPermissions.Item.InputMonitoring=\"Monitorização de entrada\"\nMacPermissions.Item.InputMonitoring.Details=\"Esta permissão é necessária para que as teclas de atalho funcionem enquanto o OBS esteja em segundo plano.\"\nMacPermissions.Item.Accessibility=\"Acessibilidade\"\nMacPermissions.Item.Accessibility.Details=\"Em instalações antigas, o OBS pode estar listado em \\\"Acessibilidade\\\" em vez de \\\"Monitorização de entrada\\\".\"\nMacPermissions.Continue=\"Continuar\"\nSourceLeak.Title=\"Erro de limpeza da fonte\"\nSourceLeak.Text=\"Ocorreu um problema a trocar de coleção de cenas e algumas fontes não foram desativadas. Este problema é normalmente causado por plugins que não libertam recursos corretamente. Por favor, verifique se todos os plugins utilizam a versão mais recente.\\n\\nOBS Studio irá agora fechar para prevenir qualquer corrupção de dados.\"\nBasic.DesktopDevice1=\"Áudio do ambiente de trabalho\"\nBasic.DesktopDevice2=\"Áudio do ambiente de trabalho 2\"\nBasic.AuxDevice1=\"Microfone/Auxiliar\"\nBasic.AuxDevice2=\"Microfone/Auxiliar 2\"\nBasic.AuxDevice3=\"Microfone/Auxiliar 3\"\nBasic.AuxDevice4=\"Microfone/Auxiliar 4\"\nBasic.Scene=\"Cena\"\nBasic.DisplayCapture=\"Captura de monitor\"\nBasic.Main.PreviewConextMenu.Enable=\"Ativar antevisão\"\nBasic.Main.Preview.Disable=\"Desativar antevisão\"\nScaleFiltering=\"Filtragem de redimensionamento\"\nScaleFiltering.Point=\"Ponto\"\nScaleFiltering.Bicubic=\"Bicúbico\"\nScaleFiltering.Area=\"Área\"\nBlendingMethod=\"Método de mesclagem\"\nBlendingMethod.Default=\"Predefinido\"\nBlendingMethod.SrgbOff=\"SRGB desligado\"\nBlendingMode=\"Modo de mesclagem\"\nBlendingMode.Additive=\"Adicionar\"\nBlendingMode.Subtract=\"Subtrair\"\nBlendingMode.Screen=\"Reticular\"\nBlendingMode.Multiply=\"Multiplicar\"\nBlendingMode.Lighten=\"Clarear\"\nBlendingMode.Darken=\"Escurecer\"\nDeinterlacing=\"Desentrelace\"\nDeinterlacing.Discard=\"Descartar\"\nDeinterlacing.TopFieldFirst=\"Campo superior primeiro\"\nDeinterlacing.BottomFieldFirst=\"Campo inferior primeiro\"\nVolControl.SliderUnmuted=\"Barra de volume para '%1':\"\nVolControl.SliderMuted=\"Barra de volume para '%1': (atualmente silenciado)\"\nVolControl.Mute=\"Silenciar '%1'\"\nVolControl.Properties=\"Propriedades de '%1'\"\nVolControl.UnassignedWarning.Title=\"Fonte de áudio não atribuído\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" não está atribuído a nenhuma pista de áudio e não será audível em transmissões ou gravações.\\n\\nPara atribuir uma fonte de áudio a uma pista, abra as propriedades de áudio avançadas clicando com o botão direito do rato ou no botão da roda dentada na barra de ferramentas da doca do misturador.\"\nBasic.Main.AddSceneDlg.Title=\"Adicionar cena\"\nBasic.Main.AddSceneDlg.Text=\"Por favor, insira o nome da cena\"\nBasic.Main.DefaultSceneName.Text=\"Cena %1\"\nBasic.Main.AddSceneCollection.Title=\"Adicionar coleção de cenas\"\nBasic.Main.AddSceneCollection.Text=\"Por favor, insira o nome da coleção de cenas\"\nBasic.Main.RenameSceneCollection.Title=\"Renomear a coleção de cenas\"\nBasic.Main.RemigrateSceneCollection.Title=\"Atualizar resolução da coleção de cenas\"\nBasic.Main.RemigrateSceneCollection.Text=\"Pretende atualizar a resolução da coleção de cenas de \\\"%1\\\" para corresponder à resolução da área de trabalho do perfil atual que é de %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Não é possível atualizar a resolução da coleção de cenas enquanto as saídas estejam ativas.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Falha ao atualizar a resolução da coleção de cenas. A resolução original é desconhecida.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Não é possível a reposição: falha na alteração da resolução do OBS.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Não é possível a reposição: a resolução atual já é a resolução base da coleção de cenas.\"\nAddProfile.Title=\"Adicionar perfil\"\nAddProfile.Text=\"Por favor, insira o nome do perfil\"\nAddProfile.WizardCheckbox=\"Mostrar assistente de configuração automática\"\nRenameProfile.Title=\"Renomear perfil\"\nBasic.Main.MixerRename.Title=\"Renomear fonte de áudio\"\nBasic.Main.MixerRename.Text=\"Por favor, insira o nome da fonte de áudio\"\nBasic.Main.PreviewDisabled=\"A antevisão está desativada\"\nBasic.SourceSelect=\"Criar/Selecionar fonte\"\nBasic.SourceSelect.CreateNew=\"Criar nova\"\nBasic.SourceSelect.AddExisting=\"Adicionar existente\"\nBasic.SourceSelect.AddVisible=\"Tornar a fonte visível\"\nBasic.Main.Sources.Visibility=\"Visibilidade\"\nBasic.Main.Sources.VisibilityDescription=\"Controla a visibilidade de '%1' na área de trabalho\"\nBasic.Main.Sources.Lock=\"Bloquear\"\nBasic.Main.Sources.LockDescription=\"Bloqueia a posição e a escala de '%1' na área de trabalho\"\nBasic.PropertiesWindow=\"Propriedades de '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (seleção automática: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Selecionar cor\"\nBasic.PropertiesWindow.SelectFont=\"Selecionar letra\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Escolha um tipo de letra\"\nBasic.PropertiesWindow.ConfirmTitle=\"Definições alteradas\"\nBasic.PropertiesWindow.Confirm=\"Há alterações não gravadas. Deseja gravá-las agora?\"\nBasic.PropertiesWindow.NoProperties=\"Sem propriedades disponíveis\"\nBasic.PropertiesWindow.AddFiles=\"Adicionar ficheiros\"\nBasic.PropertiesWindow.AddDir=\"Adicionar pasta\"\nBasic.PropertiesWindow.AddURL=\"Adicionar caminho/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Adicionar pasta a '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Adicionar ficheiros a '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Adicionar entrada a '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Editar entrada de '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Valores de FPS simples\"\nBasic.PropertiesView.FPS.Rational=\"Valores de FPS racionais\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Intervalos de FPS válidos:\"\nBasic.PropertiesView.UrlButton.Text=\"Abrir a ligação no seu navegador predefinido?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Abrir URL\"\nBasic.InteractionWindow=\"A interagir com '%1'\"\nBasic.StatusBar.Reconnecting=\"Desligado. A ligar em %2 segundo(s) (tentativa %1)\"\nBasic.StatusBar.AttemptingReconnect=\"A tentar nova ligação... (tentativa %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Nova ligação bem sucedida\"\nBasic.StatusBar.Delay=\"Desfasamento (%1 seg)\"\nBasic.StatusBar.DelayStartingIn=\"Desfasamento (a iniciar em %1 seg)\"\nBasic.StatusBar.DelayStoppingIn=\"Desfasamento (a parar em %1 seg)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Atraso (a parar em %1 seg, a iniciar em %2 seg)\"\nBasic.StatusBar.RecordingSavedTo=\"Gravação guardada em '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Memória de repetição guardada em '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Captura de ecrã gravada em '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Gravação convertida automaticamente para '%1'\"\nBasic.Filters=\"Filtros\"\nBasic.Filters.AsyncFilters=\"Filtros de áudio/vídeo\"\nBasic.Filters.AudioFilters=\"Filtros de áudio\"\nBasic.Filters.EffectFilters=\"Filtros de efeitos\"\nBasic.Filters.Title=\"Filtros para '%1'\"\nBasic.Filters.AddFilter.Title=\"Nome do filtro\"\nBasic.Filters.AddFilter.Text=\"Por favor, especifique o nome do filtro\"\nBasic.TransformWindow=\"Transformação de item de cena\"\nBasic.TransformWindow.Position=\"Posição\"\nBasic.TransformWindow.PositionX=\"Posição X\"\nBasic.TransformWindow.PositionY=\"Posição Y\"\nBasic.TransformWindow.Rotation=\"Rotação\"\nBasic.TransformWindow.Size=\"Tamanho\"\nBasic.TransformWindow.Width=\"Largura\"\nBasic.TransformWindow.Height=\"Altura\"\nBasic.TransformWindow.Alignment=\"Alinhamento posicional\"\nBasic.TransformWindow.BoundsType=\"Tipo de caixa delimitadora\"\nBasic.TransformWindow.BoundsAlignment=\"Alinhamento na caixa delimitadora\"\nBasic.TransformWindow.Bounds=\"Tamanho da caixa delimitadora\"\nBasic.TransformWindow.BoundsWidth=\"Largura da caixa delimitadora\"\nBasic.TransformWindow.BoundsHeight=\"Altura da caixa delimitadora\"\nBasic.TransformWindow.CropToBounds=\"Cortar para caixa delimitadora\"\nBasic.TransformWindow.Crop=\"Recortar\"\nBasic.TransformWindow.CropLeft=\"Cortar à esquerda\"\nBasic.TransformWindow.CropRight=\"Cortar à direita\"\nBasic.TransformWindow.CropTop=\"Cortar em cima\"\nBasic.TransformWindow.CropBottom=\"Cortar em baixo\"\nBasic.TransformWindow.Alignment.TopLeft=\"Superior esquerdo\"\nBasic.TransformWindow.Alignment.TopCenter=\"Superior centro\"\nBasic.TransformWindow.Alignment.TopRight=\"Superior direito\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Centro esquerdo\"\nBasic.TransformWindow.Alignment.Center=\"Centro\"\nBasic.TransformWindow.Alignment.CenterRight=\"Centro direito\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Inferior esquerdo\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Inferior centro\"\nBasic.TransformWindow.Alignment.BottomRight=\"Inferior direito\"\nBasic.TransformWindow.BoundsType.None=\"Sem limites\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Apenas tamanho máximo\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Redimensionar ao limite interno\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Redimensionar ao limite externo\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Redimensionar à largura dos limites\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Redimensionar à altura dos limites\"\nBasic.TransformWindow.BoundsType.Stretch=\"Esticar aos limites\"\nBasic.TransformWindow.Title=\"Editar transformação para '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Nenhuma fonte selecionada\"\nBasic.Main.AddSourceHelp.Title=\"Impossível adicionar fonte\"\nBasic.Main.AddSourceHelp.Text=\"Precisa de pelo menos uma cena para adicionar uma fonte.\"\nBasic.Main.Scenes=\"Cenas\"\nBasic.Main.Sources=\"Fontes\"\nBasic.Main.Source=\"Fonte\"\nBasic.Main.Controls=\"Controlos\"\nBasic.Main.PreparingStream=\"A preparar...\"\nBasic.Main.Connecting=\"A ligar...\"\nBasic.Main.StartRecording=\"Iniciar gravação\"\nBasic.Main.StartReplayBuffer=\"Iniciar memória de repetição\"\nBasic.Main.SaveReplay=\"Guardar repetição\"\nBasic.Main.StartStreaming=\"Iniciar transmissão\"\nBasic.Main.StartBroadcast=\"Emitir em direto\"\nBasic.Main.StartVirtualCam=\"Iniciar câmera virtual\"\nBasic.Main.StopRecording=\"Parar gravação\"\nBasic.Main.PauseRecording=\"Pausar gravação\"\nBasic.Main.UnpauseRecording=\"Retomar gravação\"\nBasic.Main.SplitFile=\"Dividir arquivo de gravação\"\nBasic.Main.AddChapterMarker=\"Adicionar marcador de capítulo (apenas em MP4/MOV híbrido)\"\nBasic.Main.StoppingRecording=\"A parar gravação...\"\nBasic.Main.StopReplayBuffer=\"Parar memória de repetição\"\nBasic.Main.StoppingReplayBuffer=\"A parar memória de repetição...\"\nBasic.Main.SetupBroadcast=\"Gerir emissão\"\nBasic.Main.StopStreaming=\"Parar transmissão\"\nBasic.Main.StopBroadcast=\"Terminar emissão\"\nBasic.Main.AutoStopEnabled=\"(paragem automática)\"\nBasic.Main.StoppingStreaming=\"A parar transmissão...\"\nBasic.Main.ForceStopStreaming=\"Parar transmissão (ignorar desfasamento)\"\nBasic.Main.ShowContextBar=\"Mostrar barra de ferramentas da fonte\"\nBasic.Main.HideContextBar=\"Ocultar barra de ferramentas da fonte\"\nBasic.Main.StopVirtualCam=\"Parar câmera virtual\"\nBasic.Main.Group=\"Grupo %1\"\nBasic.Main.GroupItems=\"Agrupar itens selecionados\"\nBasic.Main.Ungroup=\"Desagrupar\"\nBasic.Main.GridMode=\"Modo grelha\"\nBasic.Main.ListMode=\"Modo lista\"\nBasic.Main.VirtualCamConfig=\"Configurar câmera virtual\"\nBasic.VCam.VirtualCamera=\"Câmera virtual\"\nBasic.VCam.OutputType=\"Tipo de saída\"\nBasic.VCam.OutputSelection=\"Seleção de saída\"\nBasic.VCam.OutputType.Program=\"Programa (predefinição)\"\nBasic.VCam.OutputSelection.NoSelection=\"Nenhuma seleção para este tipo de saída\"\nBasic.VCam.RestartWarning=\"A câmera virtual será reiniciada para aplicar esta alteração\"\nBasic.MainMenu.File=\"&Ficheiro\"\nBasic.MainMenu.File.Export=\"&Exportar\"\nBasic.MainMenu.File.Import=\"&Importar\"\nBasic.MainMenu.File.ShowRecordings=\"&Mostrar gravações\"\nBasic.MainMenu.File.Remux=\"Con&verter gravações\"\nBasic.MainMenu.File.Settings=\"Definiçõe&s\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Mostrar pasta das definições\"\nBasic.MainMenu.File.ShowProfileFolder=\"Mostrar pasta do perfil\"\nBasic.MainMenu.File.ShowMissingFiles=\"Procurar ficheiros em falta\"\nBasic.MainMenu.File.Exit=\"&Sair\"\nBasic.MainMenu.Edit=\"&Editar\"\nBasic.MainMenu.Edit.Undo=\"&Desfazer\"\nBasic.MainMenu.Edit.Redo=\"&Refazer\"\nBasic.MainMenu.Edit.LockPreview=\"B&loquear antevisão\"\nBasic.MainMenu.Edit.Scale=\"Antever redimen&sionamento\"\nBasic.MainMenu.Edit.Scale.Window=\"Redimensionar à janela\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Área (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Saída (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Redimensionado (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Aproximar\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Afastar\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Repor zoom\"\nBasic.MainMenu.Edit.Transform=\"&Transformar\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Editar transformação...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Copiar transformação\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Colar transformação\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Repor transformação\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Rodar 90 graus à direita\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Rodar 90 graus à esquerda\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Rodar 180 graus\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Inverter &horizontalmente\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Inverter &verticalmente\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"A&justar ao ecrã\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"E&sticar ao ecrã\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centrar no ecrã\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centrar verticalmente\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centrar horizontalmente\"\nBasic.MainMenu.Edit.Order=\"&Ordenar\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Mover a&cima\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Mover a&baixo\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Mover para o &topo\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Mover para o &fundo\"\nBasic.MainMenu.Edit.AdvAudio=\"Propriedades &avançadas de áudio\"\nBasic.MainMenu.View=\"&Ver\"\nBasic.MainMenu.View.Toolbars=\"Barras de ferramen&tas\"\nBasic.MainMenu.View.ListboxToolbars=\"Barras de ferramentas de docas\"\nBasic.MainMenu.View.ContextBar=\"Barra de ferramenta das fontes\"\nBasic.MainMenu.View.SourceIcons=\"Ícones de &fonte\"\nBasic.MainMenu.View.StatusBar=\"Barra de e&stado\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Interface em ecrã total\"\nBasic.MainMenu.View.ResetUI=\"&Repor interface\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Sempre no topo\"\nBasic.MainMenu.View.SceneListMode=\"Modo de lista de cenas\"\nBasic.MainMenu.Docks=\"&Docas\"\nBasic.MainMenu.Docks.ResetDocks=\"&Repor docas\"\nBasic.MainMenu.Docks.LockDocks=\"&Bloquear docas\"\nBasic.MainMenu.Docks.SideDocks=\"Docas de altura total (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Docas de navegador &personalizadas...\"\nBasic.MainMenu.Import=\"Importar...\"\nBasic.MainMenu.Export=\"Exportar...\"\nBasic.MainMenu.SceneCollection=\"&Coleção de cenas\"\nBasic.MainMenu.Profile=\"&Perfil\"\nBasic.MainMenu.Profile.Import=\"Importar perfil\"\nBasic.MainMenu.Profile.Export=\"Exportar perfil\"\nBasic.MainMenu.SceneCollection.Import=\"Importar coleção de cenas\"\nBasic.MainMenu.SceneCollection.Export=\"Exportar coleção de cenas\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Repor a resolução de base\"\nBasic.MainMenu.SceneCollection.Migrate=\"Definir a resolução de base\"\nBasic.MainMenu.Profile.Exists=\"O perfil já existe\"\nBasic.MainMenu.Tools=\"Ferramen&tas\"\nBasic.MainMenu.Help=\"A&juda\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal de ajuda\"\nBasic.MainMenu.Help.Website=\"Visitar sítio &Web\"\nBasic.MainMenu.Help.Discord=\"Juntar-se ao servidor &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Novidades\"\nBasic.MainMenu.Help.Logs=\"Fic&heiros de diário\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Mo&strar ficheiros de diário\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Enviar fi&cheiro de diário atual\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Carregar ficheiro de diário &anterior\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Ver diário atual\"\nBasic.MainMenu.Help.ReleaseNotes=\"Notas da versão\"\nBasic.MainMenu.Help.CheckForUpdates=\"Procurar atualizações\"\nBasic.MainMenu.Help.Repair=\"Verificar integridade de ficheiros\"\nBasic.MainMenu.Help.RestartSafeMode=\"Reiniciar em modo de segurança\"\nBasic.MainMenu.Help.RestartNormal=\"Reiniciar no modo normal\"\nBasic.MainMenu.Help.CrashLogs=\"&Relatórios de falhas\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Mo&strar relatórios de falhas\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Carregar relatório de falhas &anterior\"\nBasic.MainMenu.Help.About=\"&Sobre\"\nBasic.Settings.ProgramRestart=\"O programa necessita de ser reiniciado para estas alterações terem efeito.\"\nBasic.Settings.ConfirmTitle=\"Confirmar alterações\"\nBasic.Settings.Confirm=\"Tem alterações não guardadas. Deseja guardá-las?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 está a controlar algumas das configurações da sua transmissão\"\nBasic.Settings.General=\"Geral\"\nBasic.Settings.General.Language=\"Idioma\"\nBasic.Settings.General.Updater=\"Atualizações\"\nBasic.Settings.General.UpdateChannel=\"Canal de atualização\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Desativado)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Predefinido)\"\nBasic.Settings.General.EnableAutoUpdates=\"Procurar atualizações no arranque\"\nBasic.Settings.General.OpenStatsOnStartup=\"Abrir diálogo de estatísticas no arranque\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Ocultar janelas OBS da captura de ecrã\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Esta opção irá ocultar qualquer janela, que não seja projetor do OBS Studio, de ser capturada pelo OBS. \\nAfeta outras aplicações, como conferências, partilhas de ecrã, suporte remoto, capturas de ecrã e outros programas de captura.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"A ativação desta opção irá ocultar qualquer janela, que não seja projetor do OBS Studio, de ser capturada pelo OBS. Afeta outras aplicações, como conferências, partilhas de ecrã, suporte remoto, capturas de ecrã e outros programas de captura.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Mostrar diálogo de confirmação ao iniciar transmissões\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Mostrar diálogo de confirmação ao parar transmissões\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Mostrar diálogo de confirmação ao parar gravações\"\nBasic.Settings.General.Projectors=\"Projetores\"\nBasic.Settings.General.HideProjectorCursor=\"Esconder o cursor em projetores\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Pôr os projetores sempre por cima\"\nBasic.Settings.General.Snapping=\"Ajuste de alinhamento da fonte\"\nBasic.Settings.General.ScreenSnapping=\"Alinhar fontes ao canto do ecrã\"\nBasic.Settings.General.CenterSnapping=\"Alinhar fontes ao centro horizontal e vertical\"\nBasic.Settings.General.SourceSnapping=\"Alinhar fontes a outras fontes\"\nBasic.Settings.General.SnapDistance=\"Sensibilidade do alinhamento\"\nBasic.Settings.General.SpacingHelpers=\"Mostrar guias de alinhamento por pixel\"\nBasic.Settings.General.RecordWhenStreaming=\"Gravar automaticamente ao transmitir\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Continuar a gravar quando a transmissão parar\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Iniciar automaticamente a memória de repetição ao transmitir\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Manter memória de repetição ativa quando a transmissão parar\"\nBasic.Settings.General.SysTray=\"Bandeja do sistema\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimizar para a bandeja de sistema quando iniciar\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Minimizar sempre para a bandeja do sistema em vez da barra de tarefas\"\nBasic.Settings.General.SaveProjectors=\"Guardar projetores ao sair\"\nBasic.Settings.General.CloseExistingProjectors=\"Limitar a um projetor de ecrã total por ecrã\"\nBasic.Settings.General.Preview=\"Antevisão\"\nBasic.Settings.General.OverflowHidden=\"Ocultar excesso\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Excesso sempre visível\"\nBasic.Settings.General.OverflowSelectionHidden=\"Mostrar excesso mesmo quando a fonte está invisível\"\nBasic.Settings.General.Importers=\"Importadores\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Procurar coleções de cena em locais conhecidos ao importar\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Transição para cena ao fazer duplo clique\"\nBasic.Settings.General.StudioPortraitLayout=\"Ativar esquema horizontal/vertical\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Mostrar etiquetas de antevisão/programa\"\nBasic.Settings.General.Multiview=\"Multivisualização\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Clique para alternar entre cenas\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Mostrar nome das cenas\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Desenhar áreas seguras (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Esquema de multivisualização\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontal, topo (8 cenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontal, fundo (8 cenas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertical, esquerda (8 cenas)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertical, direita (8 cenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontal, topo (18 cenas)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontal, topo (24 cenas)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Apenas cenas (4 cenas)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Apenas cenas (9 cenas)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Apenas cenas (16 cenas)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Apenas cenas (25 cenas)\"\nBasic.Settings.General.ChannelName.stable=\"Estável\"\nBasic.Settings.General.ChannelDescription.stable=\"Última versão estável\"\nBasic.Settings.General.ChannelName.beta=\"Betas / Candidatos a lançamento\"\nBasic.Settings.General.ChannelDescription.beta=\"Versões de pré-lançamento potencialmente instáveis\"\nBasic.Settings.Appearance=\"Visual\"\nBasic.Settings.Appearance.General=\"Geral\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Estilo\"\nBasic.Settings.Appearance.General.NoVariant=\"Nenhum estilo disponível\"\nBasic.Settings.Appearance.FontScale=\"Tamanho da letra\"\nBasic.Settings.Appearance.Density=\"Densidade\"\nBasic.Settings.Appearance.Classic=\"Clássico\"\nBasic.Settings.Appearance.Compact=\"Compacto\"\nBasic.Settings.Appearance.Comfortable=\"Confortável\"\nBasic.Settings.Appearance.OptionsWarning=\"Algumas opções de aparência não estão disponíveis para este estilo.\"\nBasic.Settings.Stream=\"Transmissão\"\nBasic.Settings.Stream.Destination=\"Destino\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Utilizar autenticação\"\nBasic.Settings.Stream.Custom.Username=\"Nome de utilizador\"\nBasic.Settings.Stream.Custom.Password=\"Senha\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: digite o srp_username.\\nRTMP: digite o nome de utilizador.\\nSRT: não usado.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: digite o srp_password.\\nRTMP: digite a senha.\\nSRT: digite a senha de criptografia.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Ativar modo de teste de largura de banda\"\nBasic.Settings.Stream.TTVAddon=\"Extensões para chat da Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Nenhum\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV e FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Definições de transmissão em falta\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Abrir definições\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"O URL e a chave de transmissão estão em falta.\\n\\nAbra as definições e insira a chave e o URL na aba \\\"Transmissão\\\".\"\nBasic.Settings.Stream.MissingUrl=\"O URL da transmissão está em falta.\\n\\nAbra as definições e insira o URL na aba \\\"Transmissão\\\".\"\nBasic.Settings.Stream.MissingStreamKey=\"A chave de transmissão está em falta.\\n\\nAbra as definições e insira a chave na aba \\\"Transmissão\\\".\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorar recomendações de definições do serviço de transmissão\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Sobrepor a definições recomendadas\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Aviso: ignorar as limitações do serviço pode resultar na redução de qualidade da transmissão ou mesmo impedi-la.\\n\\nContinuar?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Taxa de bits de vídeo máxima: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Taxa de bits de áudio máxima: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Resolução máxima: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Máximo FPS:%1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Especificar o servidor personalizado\"\nBasic.Settings.Stream.ServiceCustomServer=\"Servidor personalizado\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Ativar %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Largura de banda máxima na transmissão\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automático\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Máximo de pistas vídeo\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automático\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Ativar descarga de transmissão para FLV (usa configurações simples de ficheiro de gravação)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Sobreposição de configuração (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Ativar sobreposição de configuração\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Video multipista\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Área adicional\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Transmissão simultânea\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Transmissão simultânea permite codificar e enviar várias qualidades de vídeo. <a href='https://obsproject.com/kb/whip-streaming-guide'>Saber mais</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Total de camadas\"\nBasic.Settings.Stream.AdvancedOptions=\"Opções avançadas\"\nBasic.Settings.Output=\"Saída\"\nBasic.Settings.Output.Format=\"Formato de gravação\"\nBasic.Settings.Output.Format.MKV=\"Vídeo Matroska (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"MP4 híbrido (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV híbrido (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 fragmentado (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV fragmentado (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"MOV fragmentado escreve a gravação em partes e não requer a mesma finalização que os ficheiros MOV tradicionais.\\nIsto garante que o ficheiro poderá ser executado mesmo se a gravação no disco for interrompida, por exemplo, como resultado de um BSOD ou perda de energia.\\n\\nPode não ser compatível com todas as aplicações de leitura e edição de vídeo. Utilize Ficheiro → Converter gravações, para converter o ficheiro para um formato compatível, se necessário.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"MP4 fragmentado escreve a gravação em partes e não requer a mesma finalização que os ficheiros MP4 tradicionais.\\nIsto garante que o ficheiro poderá ser executado mesmo se a gravação no disco for interrompida, por exemplo, como resultado de um BSOD ou perda de energia.\\n\\nPode não ser compatível com todas as aplicações de leitura e edição de vídeo. Utilize Ficheiro → Converter gravações, para converter o ficheiro para um formato compatível, se necessário.\"\nBasic.Settings.Output.Encoder.Video=\"Codificador de vídeo\"\nBasic.Settings.Output.Encoder.Audio=\"Codificador de áudio\"\nBasic.Settings.Output.SelectDirectory=\"Selecionar pasta de gravação\"\nBasic.Settings.Output.DynamicBitrate=\"Alterar dinamicamente a taxa de bits para gerir congestionamento\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Alterar dinamicamente a taxa de bits para gerir congestionamento (beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Em vez de perder fotogramas para reduzir o congestionamento, muda dinamicamente a taxa de bits.\\n\\nNote que isto pode aumentar o desfasamento para os espetadores se houver um congestionamento súbito significativo.\\nQuando a taxa de bits cai pode levar alguns minutos a restaurar.\\n\\nAtualmente só é suportado em RTMP.\"\nBasic.Settings.Output.Mode=\"Modo de saída\"\nBasic.Settings.Output.Mode.Simple=\"Simples\"\nBasic.Settings.Output.Mode.Adv=\"Avançado\"\nBasic.Settings.Output.Mode.FFmpeg=\"Saída FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Ativar memória de repetição\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Tempo máximo de repetição\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Máximo de memória\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Utilização estimada de memória: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Aviso: O uso de memória estimado de %1 MB é maior que o máximo recomendado de %2 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Impossível estimar a utilização de memória. Defina o limite máximo de memória.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefixo do nome do ficheiro de memória de repetição\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufixo\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Memória intermédia de repetição não pode ser usada quando o tipo de gravação está definido para saída personalizada (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Caminho da gravação\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Qualidade da gravação\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Igual à da transmissão\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Alta qualidade, tamanho médio de ficheiro\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Qualidade indistinguível, tamanho grande de ficheiro\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Qualidade sem perdas, tamanho enorme de ficheiro\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Aviso: a taxa de bits do vídeo da transmissão será definida para %1, que é o limite superior do serviço de transmissão atual.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Aviso: a taxa de bits do áudio da transmissão será definida para %1, que é o limite superior para o serviço de transmissão atual.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Aviso: as gravações não podem ser pausadas se a qualidade de gravação estiver definida como \\\"Igual à da transmissão\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Aviso: o formato de gravação atualmente selecionado é incompatível com o(s) codificador(es) de transmissão selecionado(s).\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Aviso: gravar com um codificador de software, numa qualidade diferente da transmissão, vai exigir mais da CPU caso esteja a transmitir e gravar ao mesmo tempo.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Aviso: a qualidade sem perdas cria ficheiros com tamanhos enormes! Tal pode implicar ocupar até 7 gigabytes de espaço em disco por minuto a altas resoluções e taxas de fotogramas. Não é recomendado usar qualidade sem perdas para gravações longas a não ser que tenha muito espaço disponível no disco. A memória de repetição fica indisponível quando se usa qualidade sem\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Tem a certeza de que pretende utilizar a qualidade sem perdas?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Aviso de qualidade sem perdas!\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Hardware (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (predefinição x264 de baixo uso do CPU, aumenta o tamanho do ficheiro)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Predefinido)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Faixa Twitch VOD (usa a faixa 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Faixa de áudio\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Resolução/Taxa de fotogramas incompatível\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Este serviço de transmissão não suporta a atual resolução de saída e/ou taxa de fotogramas. Serão alteradas para o valor compatível mais próximo:\\n\\n%1\\n\\nDeseja continuar?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Resolução: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Codificador incompatível\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"O serviço de transmissão \\\"%1\\\" não suporta o codificador \\\"%2\\\". O codificador será alterado para \\\"%3\\\".\\n\\nPretende continuar?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"O serviço de transmissão \\\"%1\\\" não suporta os codificadores \\\"%2\\\" e \\\"%3\\\". Esses codificadores serão alterados para \\\"%4\\\" e \\\"%5\\\".\\n\\nPretende continuar?\"\nBasic.Settings.Output.VideoBitrate=\"Taxa de bits do vídeo\"\nBasic.Settings.Output.AudioBitrate=\"Taxa de bits do áudio\"\nBasic.Settings.Output.Reconnect=\"Religar automaticamente\"\nBasic.Settings.Output.RetryDelay=\"Desfasamento antes de nova tentativa\"\nBasic.Settings.Output.MaxRetries=\"Máximo de tentativas\"\nBasic.Settings.Output.Advanced=\"Ativar definições personalizadas do codificador (Avançado)\"\nBasic.Settings.Output.EncoderPreset=\"Predefinição do codificador\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (baixo uso da CPU, qualidade mais baixa)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (predefinido) (uso médio da CPU, qualidade padrão)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (uso intensivo da CPU, alta qualidade)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Definições personalizadas do codificador\"\nBasic.Settings.Output.CustomMuxerSettings=\"Definições personalizadas do conversor\"\nBasic.Settings.Output.NoSpaceFileName=\"Gerar nome do ficheiro sem espaço\"\nBasic.Settings.Output.Adv.Rescale=\"Redimensionar saída\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Desativado\"\nBasic.Settings.Output.Adv.AudioTrack=\"Pista de áudio\"\nBasic.Settings.Output.Adv.Streaming=\"Transmissão\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Configurações de transmissão\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Pista 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Pista 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Pista 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Pista 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Pista 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Pista 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Pista Twitch VOD\"\nBasic.Settings.Output.Adv.Encoder=\"Configurações do codificador\"\nBasic.Settings.Output.Adv.Recording=\"Gravação\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Configurações de gravação\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tipo de gravação\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tipo\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Padrão\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Saída personalizada (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(utilizar o codificador da transmissão)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Formatação do nome do ficheiro\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Sobrepor se o ficheiro existir\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"A saída personalizada (FFmpeg) é fornecida sem salvaguardas.\\nConsidere utilizar o tipo de gravação \\\"Padrão\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Tipo de saída FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Saída para URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Saída para ficheiro\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Formatos de gravação comuns\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Todos os ficheiros\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Caminho ou URL do ficheiro\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Formato do contentor\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Áudio\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Vídeo\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Formato predefinido\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Descrição do formato do contentor\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Codificador de áudio/vídeo adivinhado a partir do caminho ou endereço do ficheiro\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Codificador predefinido\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Desativar codificador\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Codificador de vídeo\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Definições do codificador de vídeo (se houver)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Codificador de áudio\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Definições do codificador de áudio (se houver)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Definições do conversor (se houver)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Intervalo de fotogramas-chave (em fotogramas)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Mostrar todos os codecs (mesmo que potencialmente incompatíveis)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Configurações de FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Divisão automática de ficheiros\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Dividir por tempo\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Dividir por tamanho\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Dividir apenas manualmente\"\nBasic.Settings.Output.SplitFile.Time=\"Tempo para divisão\"\nBasic.Settings.Output.SplitFile.Size=\"Tamanho para divisão\"\nScreenshot=\"Saída da captura de ecrã\"\nScreenshot.SourceHotkey=\"Capturar de fonte selecionada\"\nScreenshot.StudioProgram=\"Captura de ecrã (programa)\"\nScreenshot.Preview=\"Captura de ecrã (antevisão)\"\nScreenshot.Scene=\"Captura de ecrã (cena)\"\nScreenshot.Source=\"Captura de ecrã (fonte)\"\nFilenameFormatting.TT.CCYY=\"Ano, 4 dígitos\"\nFilenameFormatting.TT.YY=\"Ano, 2 últimos dígitos (00-99)\"\nFilenameFormatting.TT.MM=\"Mês como número decimal (01-12)\"\nFilenameFormatting.TT.DD=\"Dia do mês - com zeros (01-31)\"\nFilenameFormatting.TT.hh=\"Hora em formato de 24h (00-23)\"\nFilenameFormatting.TT.mm=\"Minutos (00-59)\"\nFilenameFormatting.TT.ss=\"Segundos (00-59)\"\nFilenameFormatting.TT.Percent=\"O carácter %\"\nFilenameFormatting.TT.a=\"Dia da semana - Nome abreviado\"\nFilenameFormatting.TT.A=\"Dia da semana - Nome longo\"\nFilenameFormatting.TT.b=\"Mês - Nome abreviado\"\nFilenameFormatting.TT.B=\"Mês - Nome longo\"\nFilenameFormatting.TT.d=\"Dia do mês - com zeros (01-31)\"\nFilenameFormatting.TT.H=\"Hora em formato de 24h (00-23)\"\nFilenameFormatting.TT.I=\"Hora em formato de 12h (01-12)\"\nFilenameFormatting.TT.m=\"Mês como número decimal (01-12)\"\nFilenameFormatting.TT.M=\"Minutos (00-59)\"\nFilenameFormatting.TT.p=\"Designação AM ou PM\"\nFilenameFormatting.TT.s=\"Hora em segundos desde a era UNIX\"\nFilenameFormatting.TT.S=\"Segundos (00-59)\"\nFilenameFormatting.TT.y=\"Ano, 2 últimos dígitos (00-99)\"\nFilenameFormatting.TT.Y=\"Ano\"\nFilenameFormatting.TT.z=\"Desfasamento ISO 8601 do UTC no fuso horário\"\nFilenameFormatting.TT.Z=\"Nome ou abreviatura do fuso horário\"\nFilenameFormatting.TT.FPS=\"Fotogramas por segundo\"\nFilenameFormatting.TT.CRES=\"Resolução base (área)\"\nFilenameFormatting.TT.ORES=\"Resolução de saída (redimensionado)\"\nFilenameFormatting.TT.VF=\"Formato de vídeo\"\nBasic.Settings.Video=\"Vídeo\"\nBasic.Settings.Video.Adapter=\"Adaptador de vídeo\"\nBasic.Settings.Video.BaseResolution=\"Resolução base (área)\"\nBasic.Settings.Video.ScaledResolution=\"Resolução de saída (redimensionado)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtro de redução de dimensões\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Resoluções correspondem, não é necessário diminuir dimensões]\"\nBasic.Settings.Video.FPSCommon=\"Valores de FPS comuns\"\nBasic.Settings.Video.FPSInteger=\"Valor de FPS inteiro\"\nBasic.Settings.Video.FPSFraction=\"Valor de FPS fracional\"\nBasic.Settings.Video.Numerator=\"Numerador\"\nBasic.Settings.Video.Denominator=\"Denominador\"\nBasic.Settings.Video.Renderer=\"Processador\"\nBasic.Settings.Video.InvalidResolution=\"Resolução inválida. Tem de ser [largura]x[altura] (i.e. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Uma saída de vídeo encontra-se ativa. Por favor, desligue-a para alterar as definições de vídeo.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (o mais rápido, mas desfocado se redimensionar)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicúbico (redimensionamento nítido, 16 amostras)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (redimensionamento nítido, 36 amostras)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Área (soma ponderada, 4/6/9 amostras)\"\nBasic.Settings.Audio=\"Áudio\"\nBasic.Settings.Audio.SampleRate=\"Taxa de amostragem\"\nBasic.Settings.Audio.Channels=\"Canais\"\nBasic.Settings.Audio.Meters=\"Medidores\"\nBasic.Settings.Audio.MeterDecayRate=\"Taxa de decaimento\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rápida\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Média (tipo I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lenta (tipo II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Tipo de medidor de pico\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Pico de amostra\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Pico verdadeiro (uso mais elevado da CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"AVISO: o áudio multicanal está ativo.\"\nBasic.Settings.Audio.MultichannelWarning=\"Se estiver a transmitir, verifique se o seu serviço de transmissão suporta tanto ingestão de áudio surround como execução de áudio surround. Por exemplo, o Facebook 360 Live suporta totalmente o áudio surround; YouTube Live suporta ingestão de áudio 5.1 (e execução em TV). \\n\\nOs filtros de áudio do OBS são compatíveis com áudio surround, embora o suporte do plugin VST não seja garantido.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Ativar áudio multicanal?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Tem a certeza de que deseja ativar o áudio multicanal?\"\nBasic.Settings.Audio.Devices=\"Dispositivos de áudio globais\"\nBasic.Settings.Audio.DesktopDevice=\"Áudio do ambiente de trabalho\"\nBasic.Settings.Audio.DesktopDevice2=\"Áudio do ambiente de trabalho 2\"\nBasic.Settings.Audio.AuxDevice=\"Áudio Mic./Auxiliar\"\nBasic.Settings.Audio.AuxDevice2=\"Áudio Mic./Auxiliar 2\"\nBasic.Settings.Audio.AuxDevice3=\"Áudio Mic./Auxiliar 3\"\nBasic.Settings.Audio.AuxDevice4=\"Áudio Mic./Auxiliar 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Ativar Premir-para-silenciar\"\nBasic.Settings.Audio.PushToMuteDelay=\"Desfasamento do Premir-para-silenciar\"\nBasic.Settings.Audio.EnablePushToTalk=\"Ativar Premir-para-falar\"\nBasic.Settings.Audio.PushToTalkDelay=\"Desfasamento do Premir-para-falar\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[dispositivo não ligado ou indisponível]\"\nBasic.Settings.Audio.Disabled=\"Desativado\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Modo de memória temporária de áudio de baixa latência (para saídas Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ALERTA: memória temporária de áudio de baixa latência está ativado.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"O modo de memória temporária de áudio de baixa latência pode fazer com que o áudio falhe ou pare a reprodução de algumas fontes.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Ativar modo de memória temporária de áudio de baixa latência?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Tem a certeza de que deseja ativar o modo de memória temporária de áudio de baixa latência?\"\nBasic.Settings.Accessibility=\"Acessibilidade\"\nBasic.Settings.Accessibility.ColorOverrides=\"Usar cores diferentes\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Borda da origem (seleção)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Borda da origem (Cortar)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Borda da origem (Flutuar)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Faixa de volume do misturador (-60 até -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Faixa de volume do misturador (-20 até -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Faixa de volume do misturador (-9 até -0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Faixa de volume do misturador (-60 até -20dB) (ativo)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Faixa de volume do misturador (-20 até -9dB) (ativo)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Faixa de volume do misturador (-9 até -0dB) (ativo)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Predefinição de cores\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Predefinido\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Personalizado\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternativa para daltónicos\"\nBasic.Settings.Advanced=\"Avançado\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Mostrar aviso de saída ativa ao sair\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prioridade do processo\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Alta\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Acima do normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Abaixo do normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Baixa\"\nBasic.Settings.Advanced.FormatWarning=\"Aviso: formatos de cor diferentes de NV12/P010 destinam-se principalmente a gravação e não são recomendados durante a transmissão. A transmissão pode levar a uma maior utilização do processador devido à conversão do formato de cor.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Aviso: Formatos de alta precisão são usados normalmente em espaços de cores HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Aviso: Rec. 2100 deve usar um formato com mais exatidão.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Formato de cor\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 planos)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Espaço de cor\"\nBasic.Settings.Advanced.Video.ColorRange=\"Intervalo de cores\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limitado\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Completo\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Nível de brancos SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nível de pico nominal do HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Dispositivo de monitorização\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Predefinição\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Desativar redução de áudio do Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Desfasamento da transmissão\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Duração\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Preservar o ponto de corte (aumenta desfasamento) quando religar\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Utilização estimada de memória: %1 MB\"\nBasic.Settings.Advanced.Network=\"Rede\"\nBasic.Settings.Advanced.Network.Disabled=\"O protocolo de transmissão atualmente selecionado não suporta a alteração das configurações de rede.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Vincular a IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Família de IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Ativar otimizações de rede\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Ativar regulação TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Tenta tornar a saída RTMP mais amigável para outras aplicações sensíveis a latência na rede, regulando a taxa de transmissão.\\nAumentará o risco de fotogramas perdidos em ligações instáveis.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Comportamento do foco nas teclas de atalho\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nunca desativar teclas de atalho\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Desativar teclas de atalho quando a janela principal estiver em foco\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Desativar teclas de atalho quando a janela principal não estiver em foco\"\nBasic.Settings.Advanced.AutoRemux=\"Converter automaticamente para %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(gravar como mkv)\"\nBasic.AdvAudio=\"Propriedades avançadas de áudio\"\nBasic.AdvAudio.ActiveOnly=\"Apenas fontes ativas\"\nBasic.AdvAudio.Name=\"Nome\"\nBasic.AdvAudio.VolumeSource=\"Volume para '%1'\"\nBasic.AdvAudio.MonoSource=\"Redução multicanal para Mono para '%1'\"\nBasic.AdvAudio.Balance=\"Equilíbrio\"\nBasic.AdvAudio.BalanceSource=\"Saldo para  '%1'\"\nBasic.AdvAudio.SyncOffset=\"Atraso na sincronização\"\nBasic.AdvAudio.SyncOffsetSource=\"Atraso na sincronização para '%1'\"\nBasic.AdvAudio.Monitoring=\"Monitorização de áudio\"\nBasic.AdvAudio.Monitoring.None=\"Monitor desligado\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Só monitor (silenciar saída)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitor e saída\"\nBasic.AdvAudio.MonitoringSource=\"Monitorização de áudio para '%1'\"\nBasic.AdvAudio.AudioTracks=\"Pistas\"\nBasic.Settings.Hotkeys=\"Atalhos\"\nBasic.Settings.Hotkeys.Pair=\"Combinações de teclas partilhadas com '%1' atuam como comutadores\"\nBasic.Settings.Hotkeys.Filter=\"Filtro\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrar por tecla de atalho\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Esta atalho já é partilhado por outras ações, clique para mostrar conflitos\"\nBasic.Settings.Hotkeys.PleaseWait=\"A carregar teclas de atalho, por favor, aguarde...\"\nBasic.Hotkeys.SelectScene=\"Mudar para cena\"\nBasic.SystemTray.Show=\"Mostrar\"\nBasic.SystemTray.Hide=\"Ocultar\"\nBasic.SystemTray.Message.Reconnecting=\"Desligado. A religar...\"\nHotkeys.Tab=\"Tabulador\"\nHotkeys.Left=\"Esquerda\"\nHotkeys.Right=\"Direita\"\nHotkeys.Up=\"Cima\"\nHotkeys.Down=\"Baixo\"\nHotkeys.Space=\"Barra de espaços\"\nHotkeys.NumpadNum=\"Numérico %1\"\nHotkeys.NumpadMultiply=\"Multiplicar (do numérico)\"\nHotkeys.NumpadDivide=\"Dividir (do numérico)\"\nHotkeys.NumpadAdd=\"Somar (do numérico)\"\nHotkeys.NumpadSubtract=\"Subtrair (do numérico)\"\nHotkeys.NumpadDecimal=\"Decimal (do numérico)\"\nHotkeys.AppleKeypadNum=\"%1 (teclado)\"\nHotkeys.AppleKeypadMultiply=\"* (teclado)\"\nHotkeys.AppleKeypadDivide=\"/ (teclado)\"\nHotkeys.AppleKeypadAdd=\"+ (teclado)\"\nHotkeys.AppleKeypadSubtract=\"- (teclado)\"\nHotkeys.AppleKeypadDecimal=\". (teclado)\"\nHotkeys.AppleKeypadEqual=\"= (teclado)\"\nHotkeys.MouseButton=\"Rato %1\"\nMute=\"Silenciar\"\nUnmute=\"Ativar som\"\nPush-to-mute=\"Premir-para-silenciar\"\nPush-to-talk=\"Premir-para-falar\"\nSceneItemShow=\"Mostrar '%1'\"\nSceneItemHide=\"Ocultar '%1'\"\nOutputWarnings.NoTracksSelected=\"Tem de selecionar pelo menos uma pista\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Erro nas configurações de saída\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Todas as saídas devem ter pelo menos uma pista de som selecionada.\"\nOutputWarnings.MP4Recording=\"Aviso: gravações em MP4/MOV serão irrecuperáveis se o ficheiro não puder ser finalizado (e.g., como resultado de um BSOD, perdas de energia, etc.). Se quiser gravar múltiplas pistas de áudio, considere usar MKV e converter a gravação para MP4/MOV depois de ficar concluída (Ficheiro → Converter gravações)\"\nOutputWarnings.CannotPause=\"Aviso: as gravações não podem ser paradas se o codificador estiver definido como \\\"(usar codificador da transmissão)\\\"\"\nOutputWarnings.CodecIncompatible=\"A seleção do codificador de áudio ou vídeo foi redefinida devido a incompatibilidades. Selecione, na lista, um codificador compatível.\"\nCodecCompat.Incompatible=\"(Incompatível com %1)\"\nCodecCompat.CodecPlaceholder=\"Selecionar codificador...\"\nCodecCompat.ContainerPlaceholder=\"Selecionar formato...\"\nCodecCompat.CodecMissingOnExit.Title=\"Nenhum codificador selecionado\"\nCodecCompat.CodecMissingOnExit.Text=\"Não está definido um codificador de vídeo ou áudio. Certifique-se de que selecionou codificadores para a gravação e para a emissão.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Nenhum formato selecionado\"\nCodecCompat.ContainerMissingOnExit.Text=\"Não foi selecionado um formato de gravação. Por favor, selecione um formato de gravação compatível com o codificador de emissão selecionado.\"\nFinalScene.Title=\"Eliminar cena\"\nFinalScene.Text=\"É preciso que exista pelo menos uma cena.\"\nNoSources.Title=\"Sem fontes\"\nNoSources.Text=\"Parece que ainda não adicionou nenhuma fonte de vídeo, só vai ser produzido um ecrã em branco. Deseja realmente fazer isto?\"\nNoSources.Text.AddSource=\"Pode adicionar fontes clicando no ícone + na caixa de fontes, na janela principal, em qualquer altura.\"\nNoSources.Label=\"Não tem nenhuma fonte.\\nClique no botão + abaixo,\\nou clique aqui com o botão direito para adicionar uma.\"\nChangeBG=\"Definir cor\"\nCustomColor=\"Cor personalizada\"\nBrowserSource.EnableHardwareAcceleration=\"Ativar aceleração por hardware para fontes de navegador\"\nAbout=\"Sobre\"\nAbout.Info=\"O OBS Studio é um programa livre e de código aberto para gravação e transmissão de vídeo.\"\nAbout.Donate=\"Faça um donativo\"\nAbout.GetInvolved=\"Participe\"\nAbout.Authors=\"Autores\"\nAbout.License=\"Licença\"\nAbout.Error=\"Erro! Não foi possível ler o ficheiro.\\n\\nIr para: %1\"\nAbout.Contribute=\"Apoie o projeto OBS\"\nAddUrl.Title=\"Adicionar fonte via URL\"\nAddUrl.Text=\"Arrastou um URL para o OBS. Isto adiciona automaticamente a ligação como uma fonte. Continuar?\"\nResizeOutputSizeOfSource=\"Redimensionar saída (tamanho da fonte)\"\nResizeOutputSizeOfSource.Text=\"As resoluções base e de saída serão redimensionadas para o tamanho da fonte atual.\"\nResizeOutputSizeOfSource.Continue=\"Deseja continuar?\"\nPreviewTransition=\"Antever transição\"\nImporter=\"Importador de coleção de cenas\"\nImporter.SelectCollection=\"Selecione uma coleção de cenas\"\nImporter.Collection=\"Coleção de cenas\"\nImporter.HelpText=\"Adicione ficheiros nesta janela para importar coleções do OBS ou de outros programas suportados.\"\nImporter.Path=\"Caminho da coleção\"\nImporter.Program=\"Aplicação detetada\"\nImporter.AutomaticCollectionPrompt=\"Procurar automaticamente coleções de cenas\"\nImporter.AutomaticCollectionText=\"O OBS pode encontrar automaticamente coleções de cenas importáveis de programas de terceiros suportados. Gostaria que o OBS encontrasse automaticamente coleções?\\n\\nPode alterar isto mais tarde em Definições > Geral > Importadores.\"\nImporter.SelectFile=\"Navegar...\"\nRestart=\"Reiniciar\"\nNeedsRestart=\"O OBS Studio tem de ser reiniciado. Deseja reiniciar agora?\"\nLoadProfileNeedsRestart=\"O perfil contém definições que requerem o reinício do OBS:\\n%1\\n\\nDeseja reiniciar o OBS para aplicar estas definições?\"\nContextBar.NoSelectedSource=\"Sem fontes selecionadas\"\nContextBar.MediaControls.PlayMedia=\"Reproduzir conteúdo\"\nContextBar.MediaControls.PauseMedia=\"Pausar conteúdo\"\nContextBar.MediaControls.StopMedia=\"Parar conteúdo\"\nContextBar.MediaControls.RestartMedia=\"Reiniciar conteúdo\"\nContextBar.MediaControls.PlaylistNext=\"Seguinte na lista de reprodução\"\nContextBar.MediaControls.PlaylistPrevious=\"Anterior na lista de reprodução\"\nContextBar.MediaControls.BlindSeek=\"Ferramenta de procura de conteúdo\"\nYouTube.Auth.Ok=\"Autorização concluída com sucesso.\\nPode agora fechar esta página.\"\nYouTube.Auth.NoCode=\"O processo de autorização não foi concluído.\"\nYouTube.Auth.NoChannels=\"Nenhum canal disponível na conta selecionada\"\nYouTube.Auth.WaitingAuth.Title=\"Autorização de utilizador YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Por favor, complete a autorização no seu navegador externo.<br>Se o navegador externo não abrir, siga esta ligação e complete a autorização:<br>%1\"\nYouTube.AuthError.Text=\"Falha ao obter informações do canal: %1.\"\nYouTube.Actions.WindowTitle=\"Configuração de emissão no YouTube - Canal: %1\"\nYouTube.Actions.CreateNewEvent=\"Criar emissão\"\nYouTube.Actions.ChooseEvent=\"Selecionar emissão existente\"\nYouTube.Actions.Title=\"Título*\"\nYouTube.Actions.MyBroadcast=\"Minha emissão\"\nYouTube.Actions.Description=\"Descrição\"\nYouTube.Actions.Privacy=\"Privacidade*\"\nYouTube.Actions.Privacy.Private=\"Privado\"\nYouTube.Actions.Privacy.Public=\"Público\"\nYouTube.Actions.Privacy.Unlisted=\"Não listado\"\nYouTube.Actions.Category=\"Categoria\"\nYouTube.Actions.Thumbnail=\"Miniatura\"\nYouTube.Actions.Thumbnail.SelectFile=\"Selecionar ficheiro...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Nenhum ficheiro selecionado\"\nYouTube.Actions.Thumbnail.ClearFile=\"Limpar\"\nYouTube.Actions.MadeForKids=\"Este vídeo é feito para crianças?*\"\nYouTube.Actions.MadeForKids.Yes=\"Sim, é feito para crianças\"\nYouTube.Actions.MadeForKids.No=\"Não, não é feito para crianças\"\nYouTube.Actions.AdditionalSettings=\"Definições adicionais\"\nYouTube.Actions.Latency=\"Latência\"\nYouTube.Actions.Latency.Low=\"Baixa\"\nYouTube.Actions.Latency.UltraLow=\"Ultra baixa\"\nYouTube.Actions.EnableAutoStart=\"Ativar início automático\"\nYouTube.Actions.EnableAutoStop=\"Ativar paragem automática\"\nYouTube.Actions.AutoStartStop.TT=\"Indica se esta emissão programada deve começar automaticamente\"\nYouTube.Actions.EnableDVR=\"Ativar DVR\"\nYouTube.Actions.360Video=\"Vídeo 360\"\nYouTube.Actions.ScheduleForLater=\"Agendar para mais tarde\"\nYouTube.Actions.RememberSettings=\"Memorizar estas definições\"\nYouTube.Actions.Create_Ready=\"Criar emissão\"\nYouTube.Actions.Create_GoLive=\"Criar emissão e começar a transmitir\"\nYouTube.Actions.Choose_Ready=\"Selecionar emissão\"\nYouTube.Actions.Choose_GoLive=\"Selecionar emissão e começar a transmitir\"\nYouTube.Actions.Create_Schedule=\"Agendar emissão\"\nYouTube.Actions.Create_Schedule_Ready=\"Agendar e selecionar emissão\"\nYouTube.Actions.Dashboard=\"Abrir o YouTube Studio\"\nYouTube.Actions.Error.Title=\"Erro na criação da emissão ao vivo\"\nYouTube.Actions.Error.Text=\"Erro de acesso ao YouTube '%1'.<br/>Pode encontrar uma descrição detalhada do erro em <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Falha ao aceder ao YouTube. Por favor, verifique a ligação à rede ou o acesso ao servidor do YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Erro de criação de emissão '%1'.<br/>Pode encontrar uma descrição detalhada do erro em <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Nenhuma transmissão criada. Por favor, religue a conta.\"\nYouTube.Actions.Error.YouTubeApi=\"Erro da API do YouTube. Por favor, veja o ficheiro de diário para mais informações.\"\nYouTube.Actions.Error.BroadcastNotFound=\"A emissão selecionada não foi encontrada.\"\nYouTube.Actions.Error.FileMissing=\"O ficheiro selecionado não existe.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Falha ao abrir o ficheiro selecionado.\"\nYouTube.Actions.Error.FileTooLarge=\"Ficheiro selecionado é demasiado grande (limite: 2 MB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Falha na transição da emissão: %1<br/><br/>Se este erro persistir <a href='https://studio.youtube.com/video/%2/livestreaming'>abra a transmissão no YouTube Studio</a> e tente manualmente.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"A emissão está a fazer a transição para a etapa de teste, isto pode levar algum tempo. Por favor, tente novamente daqui a 10-30 segundos.\"\nYouTube.Actions.EventsLoading=\"A carregar a lista de eventos...\"\nYouTube.Actions.EventCreated.Title=\"Evento criado\"\nYouTube.Actions.EventCreated.Text=\"Evento criado com sucesso.\"\nYouTube.Actions.Stream=\"Transmissão\"\nYouTube.Actions.Stream.ScheduledFor=\"Agendado para %1\"\nYouTube.Actions.Stream.Resume=\"Retomar transmissão interrompida\"\nYouTube.Actions.Stream.YTStudio=\"Criado automaticamente pelo YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"A criar emissão ao vivo, por favor, aguarde...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Início manual necessário\"\nYouTube.Actions.AutoStartStreamingWarning=\"O início automático está desativado para este evento, clique em \\\"Emitir em direto\\\" para iniciar a emissão.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Não poderá voltar a ligar.<br>A sua transmissão parará e deixará de estar em direto.\"\nYouTube.Errors.liveStreamingNotEnabled=\"A transmissão ao vivo não está ativa no canal do YouTube selecionado.<br/><br/>Consultar <a href='https://www.youtube.com/features'>youtube.com/features</a> para mais informação.\"\nYouTube.Errors.livePermissionBlocked=\"A transmissão ao vivo não está disponível no canal do YouTube selecionado.<br/>Por favor, note que pode demorar até 24 horas para que a transmissão ao vivo fique disponível após a ativar nas definições do canal.<br/><br/>Consulte <a href='https://www.youtube.com/features'>youtube.com/features</a> para obter detalhes.\"\nYouTube.Errors.errorExecutingTransition=\"A transição falhou devido a um erro da área administrativa. Por favor, tente novamente daqui a alguns segundos.\"\nYouTube.Errors.errorStreamInactive=\"O YouTube não está a receber dados da sua transmissão. Por favor, verifique a sua configuração e tente novamente.\"\nYouTube.Errors.invalidTransition=\"A transição tentada foi inválida. Isto pode ocorrer devido ao fluxo não ter terminado uma transição anterior. Por favor, aguarde alguns segundos e tente novamente.\"\nYouTube.DocksRemoval.Title=\"Limpar docas de navegador legadas do YouTube\"\nYouTube.DocksRemoval.Text=\"Estas docas de navegador serão removidas por serem obsoletas:\\n\\n%1\\nEm vez disto, use \\\"Docas/Sala de controle ao vivo do YouTube\\\".\"\nConfigDownload.WarningMessageTitle=\"Aviso\"\nFailedToStartStream.MissingConfigURL=\"Nenhum URL de configuração disponível para o serviço atual\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"URL de RTMP personalizado não especificada\"\nFailedToStartStream.InvalidCustomConfig=\"Configuração personalizada inválida\"\nFailedToStartStream.MissingCanvas=\"Está em falta configurar uma área de trabalho adicional\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Falha ao criar um serviço de vídeo multipista\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Falha ao criar uma saída RTMP de vídeo multipista\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC não disponível.\\n\\nFalha em encontrar o tipo de codificador '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Falha ao criar o codificador de vídeo '%1' (tipo: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Falha ao obter informações do vídeo OBS enquanto se criava o codificador '%1' (tipo: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Falha ao criar o codificador de áudio\"\nFailedToStartStream.NoRTMPURLInConfig=\"A configuração não contém URL de RTMP de destino da transmissão\"\nFailedToStartStream.FallbackToDefault=\"Falha ao iniciar a transmissão usando %1. Deseja tentar novamente usando as configurações de codificação única?\"\nFailedToStartStream.ConfigRequestFailed=\"Não foi possível obter a configuração de  %1<br><br>Erro HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Recebido um valor de estado desconhecido: '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nDeseja continuar a transmitir mesmo sem %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nDeseja continuar a transmitir?\"\nFailedToStartStream.MissingEncoderConfigs=\"A configuração de transmissão ao vivo não incluiu as configurações do codificador\"\nFailedToStartStream.InvalidEncoderConfig=\"A configuração de transmissão ao vivo incluiu uma configuração inválida do codificador\"\nFailedToStartStream.StatusMissingHTML=\"Solicitação de transmissão ao vivo devolveu um erro não especificado\"\nFailedToStartStream.NoConfigSupplied=\"Configuração em falta\"\nMultitrackVideo.Info=\"%1 otimiza automaticamente as suas configurações para codificar e enviar vídeo em múltiplas qualidades. Selecionar esta opção enviará %2 informações sobre a configuração do seu computador e software.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Configurações incompatíveis\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 ainda não é compatível com:\\n\\n%2:\\nPara continuar a transmitir com %1, desative as configurações incompatíveis::\\n\\n%3:\\ne inicie a transmissão novamente.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Desativar para esta transmissão e iniciar transmissão\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Atualizar configurações e iniciar transmissão\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 não é atualmente compatível com [Áudio → Geral → Canais] definido para '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Áudio → Geral → Canais] deve ser definido para '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 necessita de várias configurações diferentes para  [Áudio → Geral → Canais]\"\nBasic.OpenPluginManager=\"Gestor de plugins\"\nPluginManager=\"Gestor de plugins\"\nPluginManager.Restart=\"O OBS Studio tem de ser reiniciado para aplicar as alterações ao plugin\"\nPluginManager.MissingPlugin=\"[PLUGIN NÃO ENCONTRADO]\"\nPluginManager.Section.Discover=\"Navegar\"\nPluginManager.Section.Manage=\"Instalado\"\nPluginManager.Section.Updates=\"Atualizações\"\nPluginManager.Section.Manage.Title=\"Gerir plugins ativados\"\n"
  },
  {
    "path": "frontend/data/locale/ro-RO.ini",
    "content": "Language=\"Română\"\nApply=\"Aplică\"\nCancel=\"Anulează\"\nClose=\"Închide\"\nSave=\"Salvează\"\nDiscard=\"Înlătură\"\nDisable=\"Dezactivează\"\nYes=\"Da\"\nNo=\"Nu\"\nAdd=\"Adaugă\"\nRemove=\"Elimină\"\nRename=\"Redenumește\"\nInteract=\"Interacționează\"\nFilters=\"Filtre\"\nProperties=\"Proprietăți\"\nMoveUp=\"Mută în sus\"\nMoveDown=\"Mută în jos\"\nSettings=\"Setări\"\nName=\"Nume\"\nExit=\"Ieși\"\nMixer=\"Mixer audio\"\nBrowse=\"Răsfoiește\"\nDroppedFrames=\"Cadre pierdute %1 (%2%)\"\nProjector.Window=\"Fereastră nouă\"\nProjector.Title=\"Proiector\"\nProjector.Title.Scene=\"Scenă: %1\"\nProjector.Title.Source=\"Sursă: %1\"\nProjector.Title.Multiview=\"Vizualizare multiplă\"\nProjector.ResizeWindowToContent=\"Potrivește fereastra pe conținut\"\nClear=\"Șterge\"\nRevert=\"Inversează\"\nShow=\"Afișează\"\nHide=\"Ascunde\"\nUnhideAll=\"Reafișează toate\"\nUntitled=\"Fără nume\"\nNew=\"Nou\"\nDuplicate=\"Duplică\"\nEnable=\"Activează\"\nDisableOSXVSync=\"Dezactivează V-Sync în macOS\"\nResetOSXVSyncOnExit=\"Resetează V-Sync în macOS la ieșire\"\nHighResourceUsage=\"Codificare supraîncărcată! Ia în considerare reducerea setărilor video sau folosirea unei presetări de codare mai rapidă.\"\nTransition=\"Tranziție\"\nQuickTransitions=\"Tranziții rapide\"\nFadeToBlack=\"Estompare la negru\"\nLeft=\"Stânga\"\nRight=\"Dreapta\"\nTop=\"Sus\"\nBottom=\"Jos\"\nReset=\"Resetează\"\nHours=\"Ore\"\nMinutes=\"Minute\"\nSeconds=\"Secunde\"\nDeprecated=\"Perimat\"\nReplayBuffer=\"Buffer de reluări\"\nImport=\"Importă\"\nExport=\"Exportă\"\nCopy=\"Copiază\"\nPaste=\"Lipește\"\nPasteReference=\"Lipește (Referință)\"\nPasteDuplicate=\"Lipește (Duplicare)\"\nRemuxRecordings=\"Remuxează înregistrări\"\nNext=\"Următorul\"\nBack=\"Înapoi\"\nDefaults=\"Valori implicite\"\nRestoreDefaults=\"Restaurează la valorile implicite\"\nHideMixer=\"Ascunde în mixer\"\nTransitionOverride=\"Suprascrierea tranziției\"\nShowTransition=\"Tranziția la afișare\"\nHideTransition=\"Tranziția la ascundere\"\nNone=\"Fără\"\nStudioMode.Preview=\"Previzualizare\"\nStudioMode.PreviewSceneName=\"Previzualizare: %1\"\nShowInMultiview=\"Afișează în vizualizarea multiplă\"\nVerticalLayout=\"Aranjament vertical\"\nGroup=\"Grup\"\nDoNotShowAgain=\"Nu afișa din nou\"\nDefault=\"(Implicit)\"\nCalculating=\"Se calculează...\"\nFullscreen=\"Ecran complet\"\nWindowed=\"Ferestră\"\nRefreshBrowser=\"Reîmprospătează\"\nAspectRatio=\"Raport de aspect <b>%1:%2</b>\"\nLockVolume=\"Blochează volumul\"\nLogViewer=\"Vizualizator de jurnale\"\nShowOnStartup=\"Afișează la pornire\"\nOpenFile=\"Deschide fișierul\"\nAddScene=\"Adaugă o scenă\"\nAddSource=\"Adaugă o sursă\"\nRemoveScene=\"Elimină scena selectată\"\nRemoveSource=\"Elimină sursele selectate\"\nMoveSceneUp=\"Mută scena în sus\"\nMoveSceneDown=\"Mută scena în jos\"\nMoveSourceUp=\"Mută sursele în sus\"\nMoveSourceDown=\"Mută sursele în jos\"\nSourceProperties=\"Deschide proprietățile sursei\"\nSourceFilters=\"Deschide filtrele sursei\"\nMixerToolbarMenu=\"Meniu Mixer Audio\"\nSceneFilters=\"Deschide filtrele scenei\"\nList=\"Listă\"\nGrid=\"Grilă\"\nAutomatic=\"Automat\"\nComingSoon=\"În Curând\"\nPluginsFailedToLoad.Title=\"Eroare la încărcarea pluginului\"\nPluginsFailedToLoad.Text=\"Următoarele pluginuri OBS nu au putut fi încărcate:\\n\\n%1\\nTe rugăm să actualizezi sau să elimini aceste pluginuri.\"\nAlreadyRunning.Title=\"OBS rulează deja\"\nAlreadyRunning.Text=\"OBS rulează deja! Cu excepția cazului în care intenționezi să faci acest lucru, te rugăm să închizi orice instanță OBS înainte de a încerca să rulezi o instanță nouă. Dacă ai OBS-ul setat pentru a fi minimizat în bara de sistem, te rugăm să verifici dacă încă rulează acolo.\"\nAlreadyRunning.LaunchAnyway=\"Lansează oricum\"\nCrashHandling.Labels.Text=\"OBS Studio nu s-a închis în mod corect.\\n\\nRulezi în modul sigur (pluginurile terțe, scripturile și WebSocketurile dezactivate)?\"\nCrashHandling.Buttons.LaunchSafe=\"Rulează în modul sigur\"\nCrashHandling.Buttons.LaunchNormal=\"Rulează în modul normal\"\nSafeMode.Restart=\"Dorești să repornești OBS în modul sigur (pluginurile terțe, scripturile și WebSocketurile dezactivate)?\"\nSafeMode.RestartNormal=\"Vrei să repornești OBS în modul normal?\"\nChromeOS.Title=\"Platformă nesuportată\"\nChromeOS.Text=\"OBS pare să ruleze în interiorul unui container ChromeOS. Această platformă nu este suportată.\"\nWine.Title=\"Wine detectat\"\nWine.Text=\"Rularea lui OBS în Wine nu este suportată și multe caracteristici, cum ar fi sursele de captură sau de dispozitiv, nu vor funcționa sau nu vor funcționa decât într-o capacitate limitată.<br><br>Se recomandă să rulezi în schimb o versiune nativă de OBS, de exemplu, <a href='https://flathub.org/apps/details/com.obsproject.Studio'>versiunea noastră de Flatpak</a> sau pachetele sistemului tău de operare.\"\nDockCloseWarning.Title=\"Închiderea ferestrei andocabile\"\nDockCloseWarning.Text=\"Tocmai ai închis o fereastră andocabilă. Dacă dorești să o afișezi din nou, folosește meniul Andocări din bara de meniu.\"\nExtraBrowsers=\"Andocări personalizate de browser\"\nExtraBrowsers.Info=\"Adaugă andocări dându-le un nume și un URL, apoi dă clic pe Aplică sau Închide pentru a deschide andocările. Poți adăuga sau elimina andocări oricând.\"\nExtraBrowsers.DockName=\"Numele andocării\"\nAuth.Authing.Title=\"Se autentifică...\"\nAuth.Authing.Text=\"Se autentifică cu %1, te rugăm să aștepți...\"\nAuth.AuthFailure.Title=\"Eșec la autentificare\"\nAuth.AuthFailure.Text=\"Autentificarea cu %1 a eșuat:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Autentificare necesară\"\nAuth.InvalidScope.Text=\"Cerințele de autentificare pentru %1 s-au schimbat. Unele funcții pot să nu fie disponibile.\"\nAuth.LoadingChannel.Title=\"Se încarcă informațiile canalului...\"\nAuth.LoadingChannel.Text=\"Se încarcă informațiile canalului pentru %1, te rugăm să aștepți...\"\nAuth.LoadingChannel.Error=\"Nu s-au putut obține informații despre canal.\"\nAuth.ChannelFailure.Title=\"Încărcarea canalului a eșuat\"\nAuth.ChannelFailure.Text=\"Încărcarea informațiilor canalului pentru %1 a eșuat\\n\\n%2: %3\"\nAuth.StreamInfo=\"Informații privind streamul\"\nTwitchAuth.Stats=\"Statistici Twitch\"\nTwitchAuth.Feed=\"Flux de activitate Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Nu se poate interoga cheia de stream\"\nTwitchAuth.TwoFactorFail.Text=\"OBS nu a putut să se conecteze la contul tău Twitch. Te rugăm să te asiguri că autentificarea cu doi factori este configurată în <a href='https://www.twitch.tv/settings/security'>setările de securitate din Twitch</a>, deoarece acest lucru este necesar pentru a transmite.\"\nRestreamAuth.Channels=\"Canale Restream\"\nCopy.Filters=\"Copiază filtrele\"\nPaste.Filters=\"Lipește filtrele\"\nBrowserPanelInit.Title=\"Se inițializează browserul...\"\nBrowserPanelInit.Text=\"Se inițializează browserul, te rugăm să aștepți...\"\nBandwidthTest.Region=\"Regiune\"\nBandwidthTest.Region.US=\"Statele Unite\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Other=\"Alta\"\nBasic.AutoConfig=\"Asistent de configurare automată\"\nBasic.AutoConfig.ApplySettings=\"Aplică setările\"\nBasic.AutoConfig.StartPage=\"Informații de utilizare\"\nBasic.AutoConfig.StartPage.SubTitle=\"Specifică pentru ce vrei să folosești programul\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimizează pentru streaming, înregistrarea este pe plan secund\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimizează numai pentru înregistrare, nu voi face streaming\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Voi folosi doar camera virtuală\"\nBasic.AutoConfig.VideoPage=\"Setări video\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Specifică setările video pe care dorești să le folosești\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Folosește-o pe cea actuală (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Displayul %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Folosește valoarea actuală (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Fie 60 sau 30, însă prefer 60 când este posibil\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Fie 60 sau 30, însă prefer rezoluție înaltă\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Notă: Rezoluția (de bază) a planșei nu este neapărat aceeași cu rezoluția cu care vei face stream sau înregistra. Rezoluția efectivă de stream/înregistrare poate fi sub-scalată de la rezoluția planșei pentru a reduce utilizarea de resurse sau cerințele privind rata de biți.\"\nBasic.AutoConfig.StreamPage=\"Informații privind streamul\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Te rugăm să introduci informațiile pentru stream\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Conectează un cont (recomandat)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Deconectează contul\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Deconectezi contul?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Această modificare se va aplica imediat. Sigur vrei să deconectezi contul?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Obține cheia de stream\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Mai multe informații\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Folosește o cheie de stream\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Folosește o cheie de stream (avansat)\"\nBasic.AutoConfig.StreamPage.Service=\"Serviciu\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Afișează toate...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Personalizat...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Cheie de stream\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: introduceți fraza de criptare.\\nRTMP: introduceți cheia furnizată de serviciu.\\nSRT: introduceți streamid-ul, dacă serviciul utilizează unul.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Cheie de codificare\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Token Bearer\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Cont conectat\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Estimează rata de biți cu o testare a lățimii de bandă (poate dura câteva minute)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferă codificarea hardware\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Codificarea hardware elimină cea mai mare parte a utilizării CPU, însă poate necesita o rată de biți mai mare pentru a obține același nivel de calitate.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Avertisment privind streamul\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Testarea lățimii de bandă este pe cale să transmită date video aleatorii fără audio către canalul tău. Dacă ai posibilitatea, este recomandat să oprești temporar salvarea videoclipurilor transmisiunilor și să setezi transmisiunea pe privat până la încheierea testării. Continui?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Text %1\"\nBasic.AutoConfig.TestPage=\"Rezultate finale\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Programul execută acum o serie de teste pentru estimarea celor mai ideale setări\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testare completă\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Se efectuează testarea lățimii de bandă, acest lucru poate dura câteva minute...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Nicio ieșire nu a fost găsită pentru acest protocol\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Se conectează la: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Nu s-a putut conecta la niciun server. Te rugăm să verifici conexiunea la Internet și să încerci din nou.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Se testează lățimea de bandă pentru: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Se testează codificatorul de transmisiune, acest lucru poate dura un minut...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Se testează codificatorul de înregistrare, acest lucru poate dura un minut...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Eșec la pornirea codificatorului\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Se testează %1x%2, %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Codificator de streaming\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Codificator de înregistrare\"\nBasic.AutoConfig.TestPage.Result.Header=\"Programul a determinat că aceste setări sunt cele mai ideale:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Pentru a folosi aceste setări, dă clic pe Aplică setările. Pentru a reconfigura și a încerca din nou, dă clic pe Înapoi. Pentru a configura manual setările, apasă Anulează și deschide Setările.\"\nBasic.AutoConfig.Info=\"Asistentul de configurare automată va determina cele mai bune setări în funcție de specificațiile calculatorului și viteza la internet.\"\nBasic.AutoConfig.RunAnytime=\"Acesta poate fi rulat oricând mergând la meniul Unelte.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Rezoluție Scalata de Streaming\"\nBasic.Stats=\"Statistici\"\nBasic.Stats.CPUUsage=\"Utilizare CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Spațiu disponibil pe disc\"\nBasic.Stats.MemoryUsage=\"Utilizarea memoriei\"\nBasic.Stats.AverageTimeToRender=\"Timp mediu de randare a unui cadru\"\nBasic.Stats.SkippedFrames=\"Cadre sărite din pricina întârzierii de codificare\"\nBasic.Stats.MissedFrames=\"Cadre sărite din pricina întârzierii de redare\"\nBasic.Stats.Output.Stream=\"Transmisiune\"\nBasic.Stats.Output.Recording=\"Înregistrare\"\nBasic.Stats.Status=\"Stare\"\nBasic.Stats.Status.Recording=\"Se înregistrează\"\nBasic.Stats.Status.Reconnecting=\"Se reconectează\"\nBasic.Stats.Status.Inactive=\"Inactiv(ă)\"\nBasic.Stats.Status.Active=\"Activ(ă)\"\nBasic.Stats.DroppedFrames=\"Cadre pierdute (Rețea)\"\nBasic.Stats.MegabytesSent=\"Date transmise în total\"\nBasic.Stats.Bitrate=\"Rată de biți\"\nBasic.Stats.DiskFullIn=\"Disc plin în (aprox.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 ore, %2 minute\"\nBasic.Stats.ResetStats=\"Resetează statisticile\"\nResetUIWarning.Title=\"Sigur vrei să resetezi UI-ul?\"\nResetUIWarning.Text=\"Resetarea UI-ului va ascunde andocările suplimentare. Va trebui să reafișezi aceste andocări din meniul Vizualizare dacă vrei să fie vizibile.\\n\\nSigur vrei să resetezi UI-ul?\"\nUpdater.Title=\"Actualizare nouă disponibilă\"\nUpdater.Text=\"Există o nouă actualizare disponibilă:\"\nUpdater.UpdateNow=\"Actualizează acum\"\nUpdater.RemindMeLater=\"Reamintește-mi mai târziu\"\nUpdater.Skip=\"Omite versiunea\"\nUpdater.NoUpdatesAvailable.Title=\"Nu există actualizări disponibile\"\nUpdater.NoUpdatesAvailable.Text=\"În prezent nu sunt disponibile actualizări\"\nUpdater.BranchNotFound.Title=\"Canal de actualizare eliminat\"\nUpdater.BranchNotFound.Text=\"Canalul de actualizare selectat nu mai este disponibil, OBS a fost resetat la valoarea implicită.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Verificarea integrității indisponibilă\"\nUpdater.RepairButUpdatesAvailable.Text=\"Verificarea integrității fișierelor este posibilă numai pentru cea mai recentă versiune disponibilă. Folosește Ajutor → Caută actualizări pentru a verifica și actualiza instalarea OBS.\"\nUpdater.RepairConfirm.Title=\"Confirmă verificarea integrității\"\nUpdater.RepairConfirm.Text=\"Pornirea verificării integrității va scana instalarea OBS pentru a detecta orice corupție și va redescărca fișierele deteriorate/modificate.\\n\\nDorești să continui?\"\nUpdater.FailedToLaunch=\"Lansarea actualizatorului a eșuat\"\nQuickTransitions.SwapScenes=\"Schimbați scenele de previzualizare/program după tranziție\"\nQuickTransitions.SwapScenesTT=\"Schimbă scenele de previzualizare și de program după tranziție (dacă scena originală a programului încă există).\\nAceasta nu va anula nicio modificare care ar fi putut fi făcută la scena originală a programului.\"\nQuickTransitions.DuplicateScene=\"Duplică scena\"\nQuickTransitions.DuplicateSceneTT=\"La editarea aceleiași scene, permite editarea transformării/vizibilității surselor fără a modifica outputul programului.\\nPentru a edita proprietățile surselor fără a modifica outputul programului, activează „Duplică scenele”.\\nModificarea acestei valori va reseta scena actuală a programului (dacă mai există).\"\nQuickTransitions.EditProperties=\"Duplică scenele\"\nQuickTransitions.EditPropertiesTT=\"La editarea aceleiași scene, permite editarea proprietăților surselor fără a modifica outputul programului.\\nAceasta poate fi folosită numai dacă „Duplică scena” este activată.\\nAnumite surse (cum ar fi sursele de captură sau media) nu suportă acest lucru și nu pot fi editate separat.\\nModificarea acestei valori va reseta scena actuală a programului (dacă mai există).\\n\\nAvertisment: Deoarece sursele vor fi duplicate, acest lucru poate necesita resurse suplimentare de sistem sau video.\"\nQuickTransitions.HotkeyName=\"Tranziție rapidă: %1\"\nBasic.AddTransition=\"Adaugă o tranziție configurabilă\"\nBasic.RemoveTransition=\"Elimină tranziția configurabilă\"\nBasic.TransitionProperties=\"Proprietăți pentru tranziție\"\nBasic.SceneTransitions=\"Tranziții pentru scene\"\nBasic.TransitionDuration=\"Durată\"\nBasic.TogglePreviewProgramMode=\"Mod studio\"\nBasic.EnablePreviewProgramMode=\"Activează modul studio\"\nBasic.DisablePreviewProgramMode=\"Dezactivează modul studio\"\nUndo.Undo=\"Anulează\"\nUndo.Redo=\"Refă\"\nUndo.Add=\"adăugarea '%1'\"\nUndo.Delete=\"ștergerea '%1'\"\nUndo.Rename=\"redenumirea '%1'\"\nUndo.SceneCollection.Switch=\"comutarea la '%1'\"\nUndo.Item.Undo=\"Anulează %1\"\nUndo.Item.Redo=\"Refă %1\"\nUndo.Sources.Multi=\"ștergerea a %1 surse\"\nUndo.Filters=\"modificarea filtrelor la '%1'\"\nUndo.Filters.Paste.Single=\"lipirea filtrului '%1' la '%2'\"\nUndo.Filters.Paste.Multiple=\"copierea filtrelor de la '%1' la '%2'\"\nUndo.Transform=\"transformarea sursei(elor) în '%1'\"\nUndo.Transform.Paste=\"lipirea transformării în '%1'\"\nUndo.Transform.Rotate=\"rotația în '%1'\"\nUndo.Transform.Reset=\"resetarea transformării în '%1'\"\nUndo.Transform.HFlip=\"răsturnarea orizontală în '%1'\"\nUndo.Transform.VFlip=\"răsturnarea verticală în '%1'\"\nUndo.Transform.FitToScreen=\"potrivirea la ecran în '%1'\"\nUndo.Transform.StretchToScreen=\"întinderea pe ecran în '%1'\"\nUndo.Transform.Center=\"centrarea pe ecran în '%1'\"\nUndo.Transform.VCenter=\"centrarea verticală pe ecran în '%1'\"\nUndo.Transform.HCenter=\"centrarea orizontală pe ecran în '%1'\"\nUndo.Volume.Change=\"modificarea volumului la '%1'\"\nUndo.Volume.Mute=\"amuțirea '%1'\"\nUndo.Volume.Unmute=\"dezmuțirea '%1'\"\nUndo.Balance.Change=\"modificarea balansului audio la '%1'\"\nUndo.SyncOffset.Change=\"modificarea decalajului sincronizării audio la '%1'\"\nUndo.MonitoringType.Change=\"modificarea monitorizării audio la '%1'\"\nUndo.Mixers.Change=\"modificarea mixerelor audio la '%1'\"\nUndo.ForceMono.On=\"activarea sunetul mono forțat la '%1'\"\nUndo.ForceMono.Off=\"dezactivarea sunetul mono forțat la '%1'\"\nUndo.Properties=\"modificarea proprietății la '%1'\"\nUndo.Scene.Duplicate=\"duplicarea scenei '%1'\"\nUndo.ShowTransition=\"tranziția la afișare pentru '%1'\"\nUndo.HideTransition=\"tranziția la ascundere pentru '%1'\"\nUndo.ShowSceneItem=\"afișarea '%1' în '%2'\"\nUndo.HideSceneItem=\"ascunderea '%1' în '%2'\"\nUndo.ReorderSources=\"reordonarea surselor în '%1'\"\nUndo.MoveUp=\"mutarea '%1' în sus în '%2'\"\nUndo.MoveDown=\"mutarea '%1' în jos în '%2'\"\nUndo.MoveToTop=\"mutarea '%1' în vârf în '%2'\"\nUndo.MoveToBottom=\"mutarea '%1' la fund în '%2'\"\nUndo.PasteSource=\"lipirea surselor în '%1'\"\nUndo.PasteSourceRef=\"lipirea referințelor sursei în '%1'\"\nUndo.GroupItems=\"gruparea elementelor în '%1'\"\nTransitionNameDlg.Text=\"Te rugăm să introduci numele tranziției\"\nTransitionNameDlg.Title=\"Numele tranziției\"\nTitleBar.SafeMode=\"MOD SIGUR\"\nTitleBar.PortableMode=\"Mod portabil\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Scene\"\nNameExists.Title=\"Numele există deja\"\nNameExists.Text=\"Numele este deja în uz.\"\nNoNameEntered.Title=\"Te rugăm să introduci un nume valid\"\nNoNameEntered.Text=\"Nu poți folosi nume necompletate.\"\nConfirmStart.Title=\"Pornești streamul?\"\nConfirmStart.Text=\"Sigur vrei să pornești streamul?\"\nConfirmStop.Title=\"Oprești streamul?\"\nConfirmStop.Text=\"Sigur vrei să oprești streamul?\"\nConfirmStopRecord.Title=\"Oprești înregistrarea?\"\nConfirmStopRecord.Text=\"Sigur vrei să oprești înregistrarea?\"\nConfirmBWTest.Title=\"Pornești testarea lățimii de bandă?\"\nConfirmBWTest.Text=\"Ai configurat OBS în modul de testare a lățimii de bandă. Acest mod permite testarea rețelei fără să transmiți live pe canal. Odată ce ai terminat testarea, va trebui să îl dezactivezi pentru ca spectatorii să îți poată vedea transmisiunea.\\n\\nVrei să continui?\"\nConfirmRemove.Title=\"Confirmă eliminarea\"\nConfirmRemove.Text=\"Sigur dorești să elimini '%1'?\"\nConfirmRemove.TextMultiple=\"Sigur vrei să elimini %1 elemente?\"\nConfirmReset.Title=\"Resetează proprietățile\"\nConfirmReset.Text=\"Sigur dorești să resetezi proprietățile actuale la valorile implicite?\"\nOutput.StartStreamFailed=\"Eșec la pornirea streamingului\"\nOutput.StartRecordingFailed=\"Eșec la pornirea înregistrării\"\nOutput.StartReplayFailed=\"Eșec la pornirea bufferului de reluări\"\nOutput.StartVirtualCamFailed=\"Eșec la pornirea camerei virtuale\"\nOutput.StartFailedGeneric=\"Pornirea outputului a eșuat. Te rugăm să verifici jurnalul pentru detalii.\\n\\nNotă: Dacă folosești codificatoare NVENC sau AMD, asigură-te că driverele video sunt la zi.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Nu se pot salva reluările cât timp înregistrarea este pusă pe pauză\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Avertisment: Reluările nu pot fi salvate cât timp înregistrarea este pusă pe pauză.\"\nOutput.ConnectFail.Title=\"Conectarea a eșuat\"\nOutput.ConnectFail.BadPath=\"URL-ul conexiunii sau calea este nevalidă. Te rugăm să verifici setările pentru a confirma că acestea sunt valide.\"\nOutput.ConnectFail.ConnectFailed=\"Conectarea la server a eșuat\"\nOutput.ConnectFail.InvalidStream=\"Nu a putut fi accesat canalul sau cheia de transmisiune specificată, te rugăm să verifici cheia de transmisiune. Dacă aceasta este corectă, poate fi o problemă cu conectarea la server.\"\nOutput.ConnectFail.HdrDisabled=\"Ieșirea HDR este oprită pentru această ieșire\"\nOutput.ConnectFail.Error=\"A apărut o eroare neașteptată la încercarea de conectare la server. Mai multe informații în fișierul jurnal.\"\nOutput.ConnectFail.Disconnected=\"Deconectat de la server.\"\nOutput.StreamEncodeError.Title=\"Eroare privind codificarea\"\nOutput.StreamEncodeError.Msg=\"A apărut o eroare de codificare în timpul streamingului.\"\nOutput.StreamEncodeError.Msg.LastError=\"S-a produs o eroare cu codificatorul în timpul streamingului:<br><br>%1\"\nOutput.RecordFail.Title=\"Eșec la pornirea înregistrării\"\nOutput.RecordFail.Unsupported=\"Formatul de output este fie nesuportat, fie nu suportă mai mult de o pistă audio. Te rugăm să verifici setările şi să încerci din nou.\"\nOutput.RecordNoSpace.Title=\"Spațiu insuficient pe disc\"\nOutput.RecordNoSpace.Msg=\"Nu există spațiu suficient pe disc pentru a continua înregistrarea.\"\nOutput.RecordError.Title=\"Eroare privind înregistrarea\"\nOutput.RecordError.Msg=\"A apărut o eroare nespecificată în timpul înregistrării.\"\nOutput.RecordError.EncodeErrorMsg=\"A apărut o eroare de codificare în timpul înregistrării.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Codificatorul a eșuat in timpul înregistrării:<br><br>%1\"\nOutput.BadPath.Title=\"Calea fișierului greșită\"\nOutput.BadPath.Text=\"Calea configurată de înregistrare nu a putut fi deschisă. Verificați calea de înregistrare din meniul Setări → Ieșire → Înregistrare.\"\nOutput.NoBroadcast.Title=\"Nicio difuzare configurată\"\nOutput.NoBroadcast.Text=\"Trebuie să configurezi o difuzare înainte de a putea porni streamingul.\"\nOutput.BroadcastStartFailed=\"Eșec la pornirea difuzării\"\nOutput.BroadcastStopFailed=\"Eșec la oprirea difuzării\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Încarcă\"\nLogUploadDialog.Buttons.RetryButton=\"Reîncearcă\"\nRemux.SourceFile=\"Înregistrare OBS\"\nRemux.TargetFile=\"Fișier țintă\"\nRemux.Remux=\"Remuxează\"\nRemux.Stop=\"Oprește remuxarea\"\nRemux.ClearFinished=\"Șterge elementele finalizate\"\nRemux.ClearAll=\"Șterge toate elementele\"\nRemux.OBSRecording=\"Înregistrare OBS\"\nRemux.FinishedTitle=\"Remuxare încheiată\"\nRemux.Finished=\"Înregistrare remuxată\"\nRemux.FinishedError=\"Înregistrare remuxată, însă fișierul poate fi incomplet\"\nRemux.SelectRecording=\"Selectează înregistrarea OBS...\"\nRemux.SelectTarget=\"Selectează fișierul țintă...\"\nRemux.FileExistsTitle=\"Fișierele țintă există\"\nRemux.FileExists=\"Următoarele fișiere țintă există deja. Vrei să le înlocuiești?\"\nRemux.ExitUnfinishedTitle=\"Remuxare în desfășurare\"\nRemux.ExitUnfinished=\"Remuxarea nu este încheiată, oprirea în acest moment poate face fișierul țintă inutilizabil.\\nSigur vrei să oprești remuxarea?\"\nRemux.HelpText=\"Plasează fișierele în această fereastră pentru a remuxa ori selectează o celulă „Înregistrare OBS” goală pentru a răsfoi un fișier.\"\nRemux.NoFilesAddedTitle=\"Niciun fișier remux adăugat\"\nRemux.NoFilesAdded=\"Niciun fișier adăugat la remux. Lasă un folder ce conține 1 sau mai multe fișiere video.\"\nMissingFiles=\"Fișiere lipsă\"\nMissingFiles.MissingFile=\"Fișier lipsă\"\nMissingFiles.NewFile=\"Fișier nou\"\nMissingFiles.HelpText=\"Niște fișiere lipsesc de ultima dată de când ai folosit OBS.\"\nMissingFiles.Clear=\"<șters>\"\nMissingFiles.NumFound=\"S-a găsit %1 din %2\"\nMissingFiles.Search=\"Caută în director...\"\nMissingFiles.SelectFile=\"Selectează fișierul...\"\nMissingFiles.SelectDir=\"Selectează folderul în care să cauți\"\nMissingFiles.State=\"Stare\"\nMissingFiles.Missing=\"Lipsește\"\nMissingFiles.Replaced=\"Înlocuit\"\nMissingFiles.Cleared=\"Curățat\"\nMissingFiles.Found=\"Găsit\"\nMissingFiles.AutoSearch=\"Au fost găsite potriviri de fișiere suplimentare\"\nMissingFiles.AutoSearchText=\"OBS a găsit potriviri suplimentare pentru fișierele lipsă din acest director. Dorești să le adaugi?\"\nMissingFiles.NoMissing.Title=\"Verificarea fișierelor lipsă\"\nMissingFiles.NoMissing.Text=\"Nu pare să lipsească niciun fișier.\"\nMacPermissions.MenuAction=\"Verifică Permisiunile Aplicației...\"\nMacPermissions.Title=\"Verifică Permisiunile Aplicației...\"\nMacPermissions.Description=\"OBS Studio are nevoie de permisiunea dvs. pentru a putea oferi anumite caracteristici. Este recomandat să activați aceste permisiuni, dar nu sunt necesare pentru a utiliza aplicația. Le puteți activa oricând mai târziu.\"\nMacPermissions.Description.OpenDialog=\"Puteți redeschide acest dialog prin intermediul meniului OBS Studio.\"\nMacPermissions.AccessGranted=\"Acces permis\"\nMacPermissions.RequestAccess=\"Solicită acces\"\nMacPermissions.OpenPreferences=\"Deschide %1 Preferințe\"\nMacPermissions.Item.ScreenRecording=\"Înregistrarea ecranului\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS are nevoie de această permisiune pentru a vă putea captura ecranul.\"\nMacPermissions.Item.Camera=\"Cameră\"\nMacPermissions.Item.Camera.Details=\"Această permisiune este necesară pentru a captura conținut de la o cameră web sau de la o placă de captură.\"\nMacPermissions.Item.Microphone=\"Microfon\"\nMacPermissions.Item.Microphone.Details=\"OBS are nevoie de această permisiune dacă doriți să vă capturați microfonul sau un dispozitiv audio extern.\"\nMacPermissions.Item.InputMonitoring=\"Monitorizare intrare\"\nMacPermissions.Item.InputMonitoring.Details=\"Această permisiune este necesară pentru funcționarea tastelor rapide în timp ce OBS rulează în fundal.\"\nMacPermissions.Item.Accessibility=\"Accesibilitate\"\nMacPermissions.Item.Accessibility.Details=\"În instalări mai vechi, OBS ar putea fi listat în \\\"Accesibilitate\\\" în loc de \\\"Monitorizare intrare\\\".\"\nMacPermissions.Continue=\"Continuă\"\nSourceLeak.Title=\"Eroare la curatarea sursei\"\nSourceLeak.Text=\"A existat o problemă la schimbarea colecțiilor de scene și unele surse nu au putut fi descărcate. Această problemă este cauzată de obicei de pluginuri care nu eliberează resursele în mod corespunzător. Vă rugăm să vă asigurați că toate plugin-urile pe care le utilizați sunt actualizate.\\n\\nOBS Studio va ieși acum pentru a preveni orice potențială corupere a datelor.\"\nBasic.DesktopDevice1=\"Audio desktop\"\nBasic.DesktopDevice2=\"Audio desktop 2\"\nBasic.AuxDevice1=\"Microfon/Aux\"\nBasic.AuxDevice2=\"Microfon/Aux 2\"\nBasic.AuxDevice3=\"Microfon/Aux 3\"\nBasic.AuxDevice4=\"Microfon/Aux 4\"\nBasic.Scene=\"Scenă\"\nBasic.DisplayCapture=\"Captură de display\"\nBasic.Main.PreviewConextMenu.Enable=\"Activează previzualizarea\"\nBasic.Main.Preview.Disable=\"Dezactivează previzualizarea\"\nScaleFiltering=\"Filtrare scalară\"\nScaleFiltering.Point=\"Punct\"\nScaleFiltering.Bilinear=\"Biliniară\"\nScaleFiltering.Bicubic=\"Bicubică\"\nScaleFiltering.Area=\"Zonă\"\nBlendingMethod=\"Metodă de amestec\"\nBlendingMethod.Default=\"Implicit\"\nBlendingMethod.SrgbOff=\"SRGB dezactivat\"\nBlendingMode=\"Mod de amestec\"\nBlendingMode.Additive=\"Adaugă\"\nBlendingMode.Subtract=\"Sustragere\"\nBlendingMode.Screen=\"Suprapunere\"\nBlendingMode.Multiply=\"Multiplică\"\nBlendingMode.Lighten=\"Luminează\"\nBlendingMode.Darken=\"Întunecă\"\nDeinterlacing=\"Deîntrețesere\"\nDeinterlacing.Discard=\"Semicadru înlăturat\"\nDeinterlacing.TopFieldFirst=\"Câmpul superior prima oară\"\nDeinterlacing.BottomFieldFirst=\"Câmpul inferior prima oară\"\nVolControl.SliderUnmuted=\"Glisor de volum pentru „%1”:\"\nVolControl.SliderMuted=\"Glisor de volum pentru „%1”: (în prezent amuțit)\"\nVolControl.Mute=\"Mut '%1'\"\nVolControl.Properties=\"Proprietăți pentru „%1”\"\nVolControl.UnassignedWarning.Title=\"Sursă audio nealocată\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" nu este asociată cu nicio pistă audio și nu va fi auzibliă în stream-uri sau înregistrări \\n\\n Pentru a asocia o sursă audio la o pistă, deschide Proprietăți Audio Avansate din meniul de click-dreapta sau din butonul cu rotița din bara de mixaj.\"\nBasic.Main.AddSceneDlg.Title=\"Adaugă o scenă\"\nBasic.Main.AddSceneDlg.Text=\"Te rugăm să introduci numele scenei\"\nBasic.Main.DefaultSceneName.Text=\"Scena %1\"\nBasic.Main.AddSceneCollection.Title=\"Adaugă colecție de scene\"\nBasic.Main.AddSceneCollection.Text=\"Te rugăm să introduci numele colecției de scene\"\nBasic.Main.RenameSceneCollection.Title=\"Redenumește colecția de scene\"\nBasic.Main.RemigrateSceneCollection.Title=\"Actualizați Rezoluția Colecției de Scene\"\nBasic.Main.RemigrateSceneCollection.Text=\"Dorești să actualizezi rezoluția colecției de scene de „%1” pentru a potrivi cu rezoluția profilului curent de %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Nu se poate actualiza rezoluția colecției scenei în timp ce ieșirile sunt active.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Resetare imposibilă: Modificarea rezoluției OBS a eșuat.\"\nAddProfile.Title=\"Adaugă un profil\"\nAddProfile.Text=\"Te rugăm să introduci numele profilului\"\nAddProfile.WizardCheckbox=\"Afișează asistentul de configurare automată\"\nRenameProfile.Title=\"Redenumește profilul\"\nBasic.Main.MixerRename.Title=\"Redenumește sursa audio\"\nBasic.Main.MixerRename.Text=\"Te rugăm să introduci numele sursei audio\"\nBasic.Main.PreviewDisabled=\"Previzualizarea este dezactivată în prezent\"\nBasic.SourceSelect=\"Creează/Selectează sursa\"\nBasic.SourceSelect.CreateNew=\"Creează una nouă\"\nBasic.SourceSelect.AddExisting=\"Adaugă una existentă\"\nBasic.SourceSelect.AddVisible=\"Fă sursa vizibilă\"\nBasic.Main.Sources.Visibility=\"Vizibilitate\"\nBasic.Main.Sources.VisibilityDescription=\"Controlează vizibilitatea '%1' în suprafață\"\nBasic.Main.Sources.Lock=\"Blocat\"\nBasic.Main.Sources.LockDescription=\"Blochează poziția și scara '%1' în suprafața de lucru\"\nBasic.PropertiesWindow=\"Proprietăți pentru „%1”\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (selectare automată: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Selectează culoarea\"\nBasic.PropertiesWindow.SelectFont=\"Selectează fontul\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Alege un Font\"\nBasic.PropertiesWindow.ConfirmTitle=\"Setări schimbate\"\nBasic.PropertiesWindow.Confirm=\"Există modificări nesalvate. Vrei să le păstrezi?\"\nBasic.PropertiesWindow.NoProperties=\"Nicio proprietate disponibilă\"\nBasic.PropertiesWindow.AddFiles=\"Adaugă fișiere\"\nBasic.PropertiesWindow.AddDir=\"Adaugă un director\"\nBasic.PropertiesWindow.AddURL=\"Adaugă cale/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Adaugă directorul în „%1”\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Adaugă fișiere la „%1”\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Adaugă intrare la „%1”\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Editează intrarea de la '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Valori FPS simple\"\nBasic.PropertiesView.FPS.Rational=\"Valori FPS raționale\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Intervale FPS valide:\"\nBasic.PropertiesView.UrlButton.Text=\"Deschizi acest link în browserul web implicit?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Deschide URL-ul\"\nBasic.InteractionWindow=\"Interacționează cu '%1'\"\nBasic.StatusBar.Reconnecting=\"Deconectat, se reconectează în %2 secundă(e) (încercarea %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Se încearcă reconectarea... (încercarea %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Reconectare cu succes\"\nBasic.StatusBar.Delay=\"Întârziere (%1 secunde)\"\nBasic.StatusBar.DelayStartingIn=\"Întârziere (pornește în %1 sec)\"\nBasic.StatusBar.DelayStoppingIn=\"Întârziere (se oprește în %1 sec)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Întârziere (se oprește în %1 sec, pornește în %2 sec)\"\nBasic.StatusBar.RecordingSavedTo=\"Înregistrare salvată în '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Buffer de reluare salvat în '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Captură de ecran salvată în '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Înregistrare remuxată automat în '%1'\"\nBasic.Filters=\"Filtre\"\nBasic.Filters.AsyncFilters=\"Filtre audio/video\"\nBasic.Filters.AudioFilters=\"Filtre audio\"\nBasic.Filters.EffectFilters=\"Filtre de efecte\"\nBasic.Filters.Title=\"Filtre pentru „%1”\"\nBasic.Filters.AddFilter.Title=\"Numele filtrului\"\nBasic.Filters.AddFilter.Text=\"Te rugăm să specifici numele filtrului\"\nBasic.TransformWindow=\"Transformarea elementelor pentru scene\"\nBasic.TransformWindow.Position=\"Poziție\"\nBasic.TransformWindow.PositionX=\"Poziție X\"\nBasic.TransformWindow.PositionY=\"Poziție Y\"\nBasic.TransformWindow.Rotation=\"Rotație\"\nBasic.TransformWindow.Size=\"Dimensiune\"\nBasic.TransformWindow.Width=\"Lățime\"\nBasic.TransformWindow.Height=\"Înălțime\"\nBasic.TransformWindow.Alignment=\"Aliniere pozițională\"\nBasic.TransformWindow.BoundsType=\"Tipul casetei de încadrare\"\nBasic.TransformWindow.BoundsAlignment=\"Aliniere în caseta de încadrare\"\nBasic.TransformWindow.Bounds=\"Dimensiunea casetei de încadrare\"\nBasic.TransformWindow.BoundsWidth=\"Lățimea casetei de încadrare\"\nBasic.TransformWindow.BoundsHeight=\"Înălțimea casetei de încadrare\"\nBasic.TransformWindow.Crop=\"Trunchiere\"\nBasic.TransformWindow.CropLeft=\"Trunchiază stânga\"\nBasic.TransformWindow.CropRight=\"Trunchiază dreapta\"\nBasic.TransformWindow.CropTop=\"Trunchiază partea superioară\"\nBasic.TransformWindow.CropBottom=\"Trunchiază partea inferioară\"\nBasic.TransformWindow.Alignment.TopLeft=\"Stânga sus\"\nBasic.TransformWindow.Alignment.TopCenter=\"Centru sus\"\nBasic.TransformWindow.Alignment.TopRight=\"Dreapta sus\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Centru stânga\"\nBasic.TransformWindow.Alignment.Center=\"Centru\"\nBasic.TransformWindow.Alignment.CenterRight=\"Centru dreapta\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Stânga jos\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Centru jos\"\nBasic.TransformWindow.Alignment.BottomRight=\"Dreapta jos\"\nBasic.TransformWindow.BoundsType.None=\"Fără limite\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Doar dimensiunea maximă\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Scalează la limitele interioare\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Scalează la limitele exterioare\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Scalează la lățimea limitei\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Scalează la înălțimea limitei\"\nBasic.TransformWindow.BoundsType.Stretch=\"Întinde la limite\"\nBasic.TransformWindow.Title=\"Editează transformarea pentru '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Nici o sursă selectată\"\nBasic.Main.AddSourceHelp.Title=\"Nu se poate adăuga sursa\"\nBasic.Main.AddSourceHelp.Text=\"Trebuie să ai cel puțin 1 scenă pentru a adăuga o sursă.\"\nBasic.Main.Scenes=\"Scene\"\nBasic.Main.Sources=\"Surse\"\nBasic.Main.Source=\"Sursă\"\nBasic.Main.Controls=\"Comenzi\"\nBasic.Main.PreparingStream=\"Se pregătește...\"\nBasic.Main.Connecting=\"Se conectează...\"\nBasic.Main.StartRecording=\"Pornește înregistrarea\"\nBasic.Main.StartReplayBuffer=\"Pornește bufferul de reluări\"\nBasic.Main.SaveReplay=\"Salvează reluarea\"\nBasic.Main.StartStreaming=\"Pornește streamingul\"\nBasic.Main.StartBroadcast=\"Intră live\"\nBasic.Main.StartVirtualCam=\"Pornește camera virtuală\"\nBasic.Main.StopRecording=\"Oprește înregistrarea\"\nBasic.Main.PauseRecording=\"Pune pe pauză înregistrarea\"\nBasic.Main.UnpauseRecording=\"Scoate de pe pauză înregistrarea\"\nBasic.Main.SplitFile=\"Separă fișierul înregistrării\"\nBasic.Main.StoppingRecording=\"Se oprește înregistrarea...\"\nBasic.Main.StopReplayBuffer=\"Oprește bufferul de reluări\"\nBasic.Main.StoppingReplayBuffer=\"Se oprește bufferul de reluări...\"\nBasic.Main.SetupBroadcast=\"Gestionează difuzarea\"\nBasic.Main.StopStreaming=\"Oprește streamingul\"\nBasic.Main.StopBroadcast=\"Încheie difuzarea\"\nBasic.Main.AutoStopEnabled=\"(Oprire automată)\"\nBasic.Main.StoppingStreaming=\"Se oprește streamul...\"\nBasic.Main.ForceStopStreaming=\"Oprește streamingul (renunță la întârziere)\"\nBasic.Main.ShowContextBar=\"Afișează bara de unelte pentru sursă\"\nBasic.Main.HideContextBar=\"Ascunde bara de unelte pentru sursă\"\nBasic.Main.StopVirtualCam=\"Oprește camera virtuală\"\nBasic.Main.Group=\"Grup %1\"\nBasic.Main.GroupItems=\"Grupează elementele selectate\"\nBasic.Main.Ungroup=\"Anulează grupul\"\nBasic.Main.GridMode=\"Mod grilă\"\nBasic.Main.ListMode=\"Mod listă\"\nBasic.Main.VirtualCamConfig=\"Configurare cameră virtuală\"\nBasic.VCam.VirtualCamera=\"Cameră virtuală\"\nBasic.VCam.OutputType=\"Tipul outputului\"\nBasic.VCam.OutputSelection=\"Selectarea outputului\"\nBasic.VCam.OutputType.Program=\"Program (implicit)\"\nBasic.VCam.RestartWarning=\"Camera virtuală va fi repornită pentru a aplica această modificare\"\nBasic.MainMenu.File=\"&Fișier\"\nBasic.MainMenu.File.Export=\"&Exportă\"\nBasic.MainMenu.File.Import=\"&Importă\"\nBasic.MainMenu.File.ShowRecordings=\"Afișează în&registrările\"\nBasic.MainMenu.File.Remux=\"Re&muxează înregistrări\"\nBasic.MainMenu.File.Settings=\"&Setări\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Afișează folderul cu setări\"\nBasic.MainMenu.File.ShowProfileFolder=\"Afișează folderul cu profiluri\"\nBasic.MainMenu.File.ShowMissingFiles=\"Caută fișiere lipsă\"\nBasic.MainMenu.File.Exit=\"Ieși (&X)\"\nBasic.MainMenu.Edit=\"&Editare\"\nBasic.MainMenu.Edit.Undo=\"An&ulează acțiunea\"\nBasic.MainMenu.Edit.Redo=\"&Refă acțiunea\"\nBasic.MainMenu.Edit.LockPreview=\"B&lochează previzualizarea\"\nBasic.MainMenu.Edit.Scale=\"Previzualizează &scalarea\"\nBasic.MainMenu.Edit.Scale.Window=\"Scalează la fereastră\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Planșă (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"&Transformare\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Editează transformarea...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Copiază transformarea\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Lipește transformarea\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Resetează transformarea\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Rotește la 90° în sensul acelor de ceasornic\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Rotește la 90° în sensul invers acelor de ceasornic\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Rotește cu 180°\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Răstoarnă orizontal (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Răstoarnă &vertical\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Potrivește la ecran (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Întinde pe ecran (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centrează pe ecran\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centrează vertical\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centrează orizontal\"\nBasic.MainMenu.Edit.Order=\"&Ordonare\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Mută în s&us\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Mută în jos (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Mu&tă în vârf\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Mută la fund (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Proprietăți audio &avansate\"\nBasic.MainMenu.View=\"&Vizualizare\"\nBasic.MainMenu.View.Toolbars=\"Bare de unel&te\"\nBasic.MainMenu.View.ListboxToolbars=\"Andochează barele de unelte\"\nBasic.MainMenu.View.ContextBar=\"Bară de unelte pentru scenă\"\nBasic.MainMenu.View.SourceIcons=\"P&ictograme pentru surse\"\nBasic.MainMenu.View.StatusBar=\"Bară de &stare\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Interfață în ecran complet\"\nBasic.MainMenu.View.ResetUI=\"Resetează interfața\"\nBasic.MainMenu.View.AlwaysOnTop=\"Întotde&auna în față\"\nBasic.MainMenu.View.SceneListMode=\"Modul listei de scene\"\nBasic.MainMenu.Docks=\"An&docări\"\nBasic.MainMenu.Docks.ResetDocks=\"&Resetează andocările\"\nBasic.MainMenu.Docks.LockDocks=\"B&lochează andocările\"\nBasic.MainMenu.Docks.SideDocks=\"Andocări pe toată înălțimea (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Ando&cări personalizate de browser...\"\nBasic.MainMenu.SceneCollection=\"Colecție de &scene\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Importă un profil\"\nBasic.MainMenu.Profile.Export=\"Exportă un profil\"\nBasic.MainMenu.SceneCollection.Import=\"Importă colecție de scene\"\nBasic.MainMenu.SceneCollection.Export=\"Exportă colecție de scene\"\nBasic.MainMenu.Profile.Exists=\"Profilul există deja\"\nBasic.MainMenu.Tools=\"Unel&te\"\nBasic.MainMenu.Help=\"Ajutor (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal pentru ajutor\"\nBasic.MainMenu.Help.Website=\"Vizitează site-ul &web\"\nBasic.MainMenu.Help.Discord=\"Alătură-te serverului de &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Ce este nou\"\nBasic.MainMenu.Help.Logs=\"Fișiere jurna&l\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Afișează fișierele jurnal (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Încarcă a&ctualul fișier jurnal\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Încarcă fișierul jurnal anterior (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Vezi fișierul log actual\"\nBasic.MainMenu.Help.ReleaseNotes=\"Note privind versiunea\"\nBasic.MainMenu.Help.CheckForUpdates=\"Caută actualizări\"\nBasic.MainMenu.Help.Repair=\"Verifică integritatea fișierelor\"\nBasic.MainMenu.Help.RestartSafeMode=\"Repornește în modul sigur\"\nBasic.MainMenu.Help.RestartNormal=\"Repornește în modul normal\"\nBasic.MainMenu.Help.CrashLogs=\"&Rapoarte de defecțiuni\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Afișează rapoartele de defecțiuni (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Încarcă raportul de defecțiune anterior (&P)\"\nBasic.MainMenu.Help.About=\"Despre (&A)\"\nBasic.Settings.ProgramRestart=\"Programul trebuie repornit pentru ca aceste setări să aibă efect.\"\nBasic.Settings.ConfirmTitle=\"Confirmă modificările\"\nBasic.Settings.Confirm=\"Ai modificări nesalvate. Salvezi modificările?\"\nBasic.Settings.General=\"Generale\"\nBasic.Settings.General.Language=\"Limbă\"\nBasic.Settings.General.Updater=\"Actualizări\"\nBasic.Settings.General.UpdateChannel=\"Canal de actualizare\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Dezactivat)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Implicit)\"\nBasic.Settings.General.EnableAutoUpdates=\"Caută automat actualizări la pornire\"\nBasic.Settings.General.OpenStatsOnStartup=\"Deschide caseta de dialog pentru statistici la pornire\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Ascunde ferestrele OBS din captura de ecran\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Această setare va ascunde toate ferestrele OBS Studio, care nu se află pe un proiector, de la capturarea de către OBS și afectează \\nalte aplicații, cum ar fi conferințele, partajarea ecranului, asistența la distanță, capturile de ecran și alte programe de captare.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Activarea acestei opțiuni ascunde toate ferestrele OBS Studio, care nu se află pe un proiector, de la capturarea de către OBS și afectează alte aplicații, cum ar fi conferințele, partajarea ecranului, asistența la distanță, capturile de ecran și alte programe de captare.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Afișează caseta de dialog pentru confirmare la pornirea streamurilor\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Afișează caseta de dialog pentru confirmare la oprirea streamurilor\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Afișează caseta de dialog pentru confirmare la oprirea înregistrării\"\nBasic.Settings.General.Projectors=\"Proiectoare\"\nBasic.Settings.General.HideProjectorCursor=\"Ascunde cursorul peste proiectoare\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Pune proiectoarele mereu deasupra\"\nBasic.Settings.General.Snapping=\"Alipire la alinierea cu sursele\"\nBasic.Settings.General.ScreenSnapping=\"Aliniază sursele la marginea ecranului\"\nBasic.Settings.General.CenterSnapping=\"Aliniază sursele la centrul vertical și orizontal\"\nBasic.Settings.General.SourceSnapping=\"Aliniază sursele la alte surse\"\nBasic.Settings.General.SnapDistance=\"Sensibilitatea alinierii\"\nBasic.Settings.General.SpacingHelpers=\"Afișează ghidurile de aliniere a pixelilor\"\nBasic.Settings.General.RecordWhenStreaming=\"Înregistrează automat atunci când se face streaming\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Continuă înregistrarea când streamul se oprește\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Pornește automat bufferul de reluări atunci când se face streaming\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Păstrează activ bufferul de reluări când streamul se oprește\"\nBasic.Settings.General.SysTray=\"Bară de sistem\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimizează în bara de sistem la pornire\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Întotdeauna minimizează în bara de sistem în loc de bara de activități\"\nBasic.Settings.General.SaveProjectors=\"Salvează proiectoarele la ieșire\"\nBasic.Settings.General.CloseExistingProjectors=\"Limitează un proiector pe ecran complet\"\nBasic.Settings.General.Preview=\"Previzualizare\"\nBasic.Settings.General.OverflowHidden=\"Ascunde zona excedentară\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Zona excedentară întotdeauna vizibilă\"\nBasic.Settings.General.OverflowSelectionHidden=\"Afișează zona excedentară chiar și când sursa este invizibilă\"\nBasic.Settings.General.Importers=\"Importatoare\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Caută locații cunoscute pentru colecțiile de scene atunci când le imporți\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Efectuează tranziția către scenă la dublu clic\"\nBasic.Settings.General.StudioPortraitLayout=\"Activează aranjamentul de tip portret/vertical\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Afișează etichete pentru previzualizare/program\"\nBasic.Settings.General.Multiview=\"Vizualizare multiplă\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Efectuează clic pentru a comuta între scene\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Afișează numele scenelor\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Afișează zonele sigure (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Aranjament pentru vizualizare multiplă\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Orizontal, sus (8 scene)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Orizontal, jos (8 scene)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertical, stânga (8 scene)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertical, dreapta (8 scene)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Orizontal, sus (18 scene)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Orizontal, sus (24 de scene)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Doar scene (4 scene)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Doar scene (9 scene)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Doar scene (16 scene)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Doar scene (25 de scene)\"\nBasic.Settings.General.ChannelName.stable=\"Stabil\"\nBasic.Settings.General.ChannelDescription.stable=\"Ultima versiune stabilă\"\nBasic.Settings.General.ChannelName.beta=\"Versiuni beta / release candidate\"\nBasic.Settings.General.ChannelDescription.beta=\"Versiuni de pre-lansare potențial instabile\"\nBasic.Settings.Appearance=\"Aspect\"\nBasic.Settings.Appearance.General.Theme=\"Temă\"\nBasic.Settings.Appearance.General.Variant=\"Stil\"\nBasic.Settings.Appearance.General.NoVariant=\"Niciun stil disponibil\"\nBasic.Settings.Appearance.FontScale=\"Dimensiunea fontului\"\nBasic.Settings.Appearance.Density=\"Densitate\"\nBasic.Settings.Appearance.Classic=\"Clasic\"\nBasic.Settings.Appearance.Comfortable=\"Confortabil\"\nBasic.Settings.Appearance.OptionsWarning=\"Unele opțiuni de aspect nu sunt disponibile pentru acest stil.\"\nBasic.Settings.Stream.Destination=\"Destinație\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Folosește autentificarea\"\nBasic.Settings.Stream.Custom.Username=\"Nume de utilizator\"\nBasic.Settings.Stream.Custom.Password=\"Parolă\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: introduceți srp_username.\\nRTMP: introduceți numele de utilizator.\\nSRT: nu este utilizat.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: introduceți srp_password.\\nRTMP: introduceți parola.\\nSRT: introduceți fraza de criptare.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Activează modul de testare a lățimii de bandă\"\nBasic.Settings.Stream.TTVAddon=\"Suplimente pentru chatul Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Niciunul\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV și FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Lipsește configurarea pentru stream\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Deschide Setările\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL-ul și cheia de transmisiune lipsesc.\\n\\nDeschide setările pentru a introduce URL-ul și cheia de transmisiune în fila Transmisiune.\"\nBasic.Settings.Stream.MissingUrl=\"URL-ul de transmisiune lipsește.\\n\\nDeschide setările pentru a introduce URL-ul în fila Transmisiune.\"\nBasic.Settings.Stream.MissingStreamKey=\"Cheia de transmisiune lipsește.\\n\\nDeschide setările pentru a introduce cheia de transmisiune în fila Transmisiune.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignoră recomandările pentru setările serviciilor de transmisiune\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Suprascrie setările recomandate\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Avertisment: Ignorarea limitărilor serviciului poate duce la o calitate degradată a transmisiunii sau te poate împiedica să faci transmisiuni.\\n\\nContinui?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Rată de biți video maximă: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Rată de biți audio maximă: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Rezoluție maximă: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"FPS maxim: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Specifică un server personalizat...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Server personalizat\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Activează %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Lățime de bandă maximă pentru transmisiune\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Număr maxim de piste video\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Piste video multiple\"\nBasic.Settings.Stream.AdvancedOptions=\"Opțiuni avansate\"\nBasic.Settings.Output.Format=\"Format de înregistrare\"\nBasic.Settings.Output.Format.MKV=\"Video Matroska (.mkv)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 fragmentat (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV fragmentat (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"MOV fragmentat scrie înregistrarea în bucăți și nu necesită aceeași finalizare ca și fișierele MOV tradiționale.\\nAcest lucru asigură faptul că fișierul rămâne reproductibil chiar dacă scrierea pe disc este întreruptă, de exemplu, ca urmare a unui BSOD sau a unei pierderi de curent.\\n\\nEste posibil ca acest lucru să nu fie compatibil cu toate playerele și programele de editare. Folosește Fișier → Remuxează înregistrări pentru a converti fișierul într-un format mai compatibil, dacă este necesar.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"MP4 fragmentat scrie înregistrarea în bucăți și nu necesită aceeași finalizare ca și fișierele MP4 tradiționale.\\nAcest lucru asigură faptul că fișierul rămâne reproductibil chiar dacă scrierea pe disc este întreruptă, de exemplu, ca urmare a unui BSOD sau a unei pierderi de curent.\\n\\nEste posibil ca acest lucru să nu fie compatibil cu toate playerele și programele de editare. Folosește Fișier → Remuxează înregistrări pentru a converti fișierul într-un format mai compatibil, dacă este necesar.\"\nBasic.Settings.Output.Encoder.Video=\"Codificator video\"\nBasic.Settings.Output.Encoder.Audio=\"Codificator audio\"\nBasic.Settings.Output.SelectDirectory=\"Selectează directorul de înregistrări\"\nBasic.Settings.Output.DynamicBitrate=\"Schimbă dinamic rata de biți pentru a gestiona congestionarea\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Schimbă dinamic rata de biți pentru a gestiona congestionarea (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"În locul pierderii de cadre pentru a reduce congestionarea, schimbă dinamic rata de biți din mers.\\n\\nReține că acest lucru poate crește întârzierea față de vizualizatori dacă există o congestionare bruscă\\nsemnificativă. Când rata de biți scade, poate dura până la câteva minute pentru a reveni la normal.\\n\\nÎn prezent opțiunea este suportată doar pentru RTMP.\"\nBasic.Settings.Output.Mode=\"Mod de output\"\nBasic.Settings.Output.Mode.Simple=\"Simplu\"\nBasic.Settings.Output.Mode.Adv=\"Avansat\"\nBasic.Settings.Output.Mode.FFmpeg=\"Output FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Activează bufferul de reluări\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Timp maxim pentru reluare\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Memorie Maximă\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Utilizare estimată a memoriei: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Avertisment: Utilizarea estimată a memoriei de %1 MiB este mai mare decât maximul recomandat de %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Nu se poate estima utilizarea memoriei. Te rugăm să setezi limita maximă de memorie.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefixul numelor fișierelor pentru bufferul de reluări\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufix\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Bufferul de reluări nu poate fi folosit atunci când tipul înregistrării este setat pe Output personalizat (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Cale de înregistrare\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Calitatea înregistrării\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"La fel cu cea a transmisiunii\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Calitate înaltă, dimensiune medie pentru fișiere\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Calitate imposibil de distins, dimensiune mare pentru fișiere\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Calitate fără pierderi, dimensiune extrem de mare pentru fișiere\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Avertisment: Rata de biți pentru transmisiunea video va fi setată la %1, care este limita superioară pentru serviciul actual de transmisiune.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Avertisment: Rata de biți pentru transmisiunea audio va fi setată la %1, care este limita superioară pentru serviciul actual de transmisiune.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Avertisment: Înregistrările nu pot fi puse pe pauză dacă calitatea înregistrării este setată pe „La fel cu cea a transmisiunii”.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Avertisment: formatul de înregistrare selectat în prezent este incompatibil cu codificatorul de streaming selectat.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Avertisment: Înregistrarea cu un codificator software la o calitate diferită de cea pentru stream va necesita o utilizare CPU extra dacă faci streaming și înregistrezi în același timp.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Avertisment: Calitatea fără pierderi generează dimensiuni extrem de mari de fișiere! Calitatea fără pierderi poate folosi până la 7GB spațiu de disc per minut la frecvențe de cadre și rezoluții ridicate. Această calitate nu este recomandată pentru înregistrări lungi decât dacă ai o cantitate foarte mare de spațiu disponibil pe disc. Bufferul de reluări nu este disponibil atunci când se utilizează calitatea fără pierderi.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Sigur vrei să folosești calitatea fără pierderi?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Avertizare privind calitatea fără pierderi!\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (presetare x264 cu utilizare CPU scăzută, crește dimensiunea fișierelor)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Implicit)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Pistă VOD Twitch (folosește pista 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Pistă audio\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Rezoluție/Frecvența cadrelor incompatibilă\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Acest serviciu de transmisiune nu suportă rezoluția actuală a outputului și/sau frecvența cadrelor. Acestea vor fi schimbate la cea mai apropiată valoare compatibilă:\\n\\n%1\\n\\nVrei să continui?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Rezoluție: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Codificator incompatibil\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Serviciul de streaming „%1” nu suportă codificatorul „%2”. Codificatorul va fi schimbat în „%3”.\\n\\nVrei să continui?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Serviciul de streaming „%1” nu suportă codificatoarele „%2” și „%3”. Aceste codificatoare vor fi schimbate în „%4” și „%5”.\\n\\nVrei să continui?\"\nBasic.Settings.Output.VideoBitrate=\"Rată de biți video\"\nBasic.Settings.Output.AudioBitrate=\"Rată de biți audio\"\nBasic.Settings.Output.Reconnect=\"Reconectare automată\"\nBasic.Settings.Output.RetryDelay=\"Întârziere la reîncercare\"\nBasic.Settings.Output.MaxRetries=\"Reîncercări maxime\"\nBasic.Settings.Output.Advanced=\"Activează setările personalizate ale codificatorului (Avansat)\"\nBasic.Settings.Output.EncoderPreset=\"Presetare pentru codificator\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (utilizare CPU scăzută, cea mai scăzută calitate)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (implicit) (utilizare CPU medie, calitate standard)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (utilizare CPU ridicată, calitate ridicată)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Setări personalizate pentru codificator\"\nBasic.Settings.Output.CustomMuxerSettings=\"Setări personalizate pentru muxer\"\nBasic.Settings.Output.NoSpaceFileName=\"Generează nume de fișiere fără spațiu\"\nBasic.Settings.Output.Adv.Rescale=\"Rescalează outputul\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Dezactivat\"\nBasic.Settings.Output.Adv.AudioTrack=\"Pistă audio\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Setări pentru streaming\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Pistă 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Pistă 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Pistă 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Pistă 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Pistă 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Pistă 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Pistă VOD Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"Setări ale codificatorului\"\nBasic.Settings.Output.Adv.Recording=\"Înregistrare\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Setări de înregistrare\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tip de înregistrare\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tip\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Output personalizat (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Folosește codificatorul de stream)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Formatarea numelui pentru fișiere\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Suprascrie în cazul în care fișierul există\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Tipul outpului FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Output spre URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Output spre fișier\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Formate comune de înregistrare\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Toate fișierele\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Calea fișierului sau URL-ul\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format pentru container\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Format implicit\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Descriere pentru formatul containerului\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Codecul audio/video ghicit din calea fișierului sau URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Codificator implicit\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Dezactivează codificatorul\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Codificator video\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Setările codificatorului video (dacă există)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Codificator audio\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Setările codificatorului audio (dacă există)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Setările muxerului (dacă există)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Interval de cadre cheie (cadre)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Afișează toate codecurile (chiar dacă sunt potențial incompatibile)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Setări FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Separarea automată a fișierelor\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Separă după durată de timp\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Separă după dimensiune\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Separă numai manual\"\nBasic.Settings.Output.SplitFile.Time=\"Durata pentru separare\"\nBasic.Settings.Output.SplitFile.Size=\"Dimensiunea pentru separare\"\nScreenshot=\"Realizează o captură de ecran a outputului\"\nScreenshot.SourceHotkey=\"Realizează o captură de ecran a sursei selectate\"\nScreenshot.StudioProgram=\"Realizează o captură de ecran (Program)\"\nScreenshot.Preview=\"Realizează o captură de ecran (Previzualizare)\"\nScreenshot.Scene=\"Realizează o captură de ecran (Scenă)\"\nScreenshot.Source=\"Realizează o captură de ecran (Sursă)\"\nFilenameFormatting.TT.CCYY=\"An, patru cifre\"\nFilenameFormatting.TT.YY=\"An, ultimele două cifre (00-99)\"\nFilenameFormatting.TT.MM=\"Luna ca număr zecimal (01-12)\"\nFilenameFormatting.TT.DD=\"Ziua lunii, cu zero în față (01-31)\"\nFilenameFormatting.TT.hh=\"Ora în format de 24h (00-23)\"\nFilenameFormatting.TT.ss=\"Secunde (00-59)\"\nFilenameFormatting.TT.Percent=\"Semnul %\"\nFilenameFormatting.TT.a=\"Ziua din săptămână abreviată\"\nFilenameFormatting.TT.A=\"Ziua săptămânii completă\"\nFilenameFormatting.TT.b=\"Numele lunii abreviate\"\nFilenameFormatting.TT.B=\"Numele complet al lunii\"\nFilenameFormatting.TT.d=\"Ziua lunii, cu zero în față (01-31)\"\nFilenameFormatting.TT.H=\"Ora în format de 24h (00-23)\"\nFilenameFormatting.TT.I=\"Ora în format de 12h (01-12)\"\nFilenameFormatting.TT.m=\"Luna ca număr zecimal (01-12)\"\nFilenameFormatting.TT.p=\"Desemnarea AM sau PM\"\nFilenameFormatting.TT.s=\"Timpul în secunde de la UNIX epoch\"\nFilenameFormatting.TT.S=\"Secunde (00-59)\"\nFilenameFormatting.TT.y=\"An, ultimele două cifre (00-99)\"\nFilenameFormatting.TT.Y=\"An\"\nFilenameFormatting.TT.z=\"ISO 8601 modificat din UTC în fusul orar.\"\nFilenameFormatting.TT.Z=\"Numele fusului orar sau abreviere\"\nFilenameFormatting.TT.FPS=\"Cadre pe secundă\"\nFilenameFormatting.TT.CRES=\"Rezoluție (canvas) de bază\"\nFilenameFormatting.TT.ORES=\"Rezoluție (scalată) la ieșire\"\nFilenameFormatting.TT.VF=\"Format video\"\nBasic.Settings.Video.Adapter=\"Adaptor video\"\nBasic.Settings.Video.BaseResolution=\"Rezoluție de bază (a planșei)\"\nBasic.Settings.Video.ScaledResolution=\"Rezoluție a outputului (scalată)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtru pentru sub-scalare\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Rezoluțiile se potrivesc, nu este nevoie de sub-scalare]\"\nBasic.Settings.Video.FPSCommon=\"Valori FPS comune\"\nBasic.Settings.Video.FPSInteger=\"Valoare cu număr întreg pentru FPS\"\nBasic.Settings.Video.FPSFraction=\"Valoare FPS fracționată\"\nBasic.Settings.Video.Numerator=\"Numărător\"\nBasic.Settings.Video.Denominator=\"Numitor\"\nBasic.Settings.Video.InvalidResolution=\"Valoare nevalidă pentru rezoluție. Trebuie să fie [lățime]x[înălțime] (de ex., 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Outputul video este în prezent activ. Te rugăm să oprești orice output pentru a schimba setările video.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Biliniar (cel mai rapid, dar neclar la scalare)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (Scalare ascuțită, 16 mostre)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Scalare ascuțită, 36 de mostre)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Zonă (Sumă ponderată, 4/6/9 mostre)\"\nBasic.Settings.Audio.SampleRate=\"Rată de eșantionare\"\nBasic.Settings.Audio.Channels=\"Canale\"\nBasic.Settings.Audio.Meters=\"Metrică\"\nBasic.Settings.Audio.MeterDecayRate=\"Rată de atenuare\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rapidă\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Medie (Type I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Lentă (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Tipul măsurătorii vârfului\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Vârf eșantion\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Vârf adevărat (Utilizare CPU mai mare)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"AVERTISMENT: Sunetul audio cu sunet surround este activat.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Activezi sistemul audio cu sunet surround?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Sigur vrei să activezi sistemul audio cu sunet surround?\"\nBasic.Settings.Audio.Devices=\"Dispozitive audio globale\"\nBasic.Settings.Audio.DesktopDevice=\"Audio desktop\"\nBasic.Settings.Audio.DesktopDevice2=\"Audio desktop 2\"\nBasic.Settings.Audio.AuxDevice=\"Microfon/Audio auxiliar\"\nBasic.Settings.Audio.AuxDevice2=\"Microfon/Audio auxiliar 2\"\nBasic.Settings.Audio.AuxDevice3=\"Microfon/Audio auxiliar 3\"\nBasic.Settings.Audio.AuxDevice4=\"Microfon/Audio auxiliar 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Activează push-to-mute\"\nBasic.Settings.Audio.PushToMuteDelay=\"Întârziere la push-to-mute\"\nBasic.Settings.Audio.EnablePushToTalk=\"Activează push-to-talk\"\nBasic.Settings.Audio.PushToTalkDelay=\"Întârziere la push-to-talk\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Dispozitiv neconectat sau indisponibil]\"\nBasic.Settings.Audio.Disabled=\"Dezactivat\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Modul de buffering audio cu latență scăzută (pentru outputuri Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"AVERTISMENT: Este activat bufferingul audio cu latență scăzută.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Modul de buffering audio cu latență scăzută poate cauza apariția unor defecțiuni audio sau oprirea redării de la unele surse.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Activezi modul de buffering audio cu latență scăzută?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Sigur vrei să activezi modul de buffering audio cu latență scăzută?\"\nBasic.Settings.Accessibility=\"Accesibilitate\"\nBasic.Settings.Accessibility.ColorOverrides=\"Folosește culori diferite\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Marginea sursei (selecție)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Marginea sursei (trunchiere)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Marginea sursei (survolare)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Banda de volum a mixerului (de la -60 la -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Banda de volum a mixerului (de la -20 la -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Banda de volum a mixerului (de la -9 la -0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Banda de volum a mixerului (de la -60 la -20dB) (activă)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Banda de volum a mixerului (de la -20 la -9dB) (activă)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Banda de volum a mixerului (de la -9 la -0dB) (activă)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Presetarea culorii\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Implicită\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Personalizată\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternativă daltonistă\"\nBasic.Settings.Advanced=\"Avansate\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Afișează avertismentul de outputuri active la ieșire\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prioritatea procesului\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Ridicată\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Peste normală\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normală\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Sub normală\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inactivă\"\nBasic.Settings.Advanced.FormatWarning=\"Avertisment: Formatele de culoare, altele decât NV12/P010, sunt destinate în primul rând înregistrării și nu sunt recomandate pentru streaming. Streamingul poate genera o utilizare crescută de CPU din cauza conversiei formatului de culoare.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Avertisment: Formatele de mare precizie sunt utilizate mai frecvent cu spațiile de culoare HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Atenție: Rec. 2100 trebuie să folosească un format cu multă precizie.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Format de culori\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 biți, 4:2:0, 2 planuri)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 biți, 4:2:0, 3 planuri)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 biți, 4:4:4:4, 3 planuri)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 biți, 4:2:0, 2 planuri)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 biți, 4:2:0, 3 planuri)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 biți, 4:2:2:2, 2 planuri)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 biți, 4:4:4:4, 2 planuri)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 biți)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Spațiu de culori\"\nBasic.Settings.Advanced.Video.ColorRange=\"Gamă de culori\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Limitată\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Completă\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Nivel alb SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nivel de vârf nominal HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Dispozitiv de monitorizare\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Implicit\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Dezactivează efectul audio de ducking din Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Întârziere pentru stream\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Durată\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Păstrează punctul de tăiere (crește întârzierea) la reconectare\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Utilizare estimată a memoriei: %1 MB\"\nBasic.Settings.Advanced.Network=\"Rețea\"\nBasic.Settings.Advanced.Network.Disabled=\"Protocolul de streaming selectat în prezent nu suportă modificarea setărilor de rețea.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Leagă de IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Familie IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Activează optimizările pentru rețea\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Activează pacingul pentru TCP\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Încearcă să facă RTMP mai prietenos cu alte aplicații, sensibile la latență, din rețea prin regularea ratei de transmisie.\\nEste posibil să crească riscul de cadre pierdute pe conexiunile instabile.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Comportamentul focalizării pentru tastele rapide\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nu dezactiva niciodată tastele rapide\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Dezactivează tastele rapide când fereastra principală este focalizată\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Dezactivează tastele rapide când fereastra principală nu este focalizată\"\nBasic.Settings.Advanced.AutoRemux=\"Remuxează automat în %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(înregistrează ca mkv)\"\nBasic.AdvAudio=\"Proprietăți audio avansate\"\nBasic.AdvAudio.ActiveOnly=\"Numai surse active\"\nBasic.AdvAudio.Name=\"Nume\"\nBasic.AdvAudio.Volume=\"Volum\"\nBasic.AdvAudio.VolumeSource=\"Volum pentru '%1'\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix pentru '%1'\"\nBasic.AdvAudio.Balance=\"Balans\"\nBasic.AdvAudio.BalanceSource=\"Balans pentru '%1'\"\nBasic.AdvAudio.SyncOffset=\"Decalajul sincronizării\"\nBasic.AdvAudio.SyncOffsetSource=\"Decalajul sincronizării pentru '%1'\"\nBasic.AdvAudio.Monitoring=\"Monitorizare audio\"\nBasic.AdvAudio.Monitoring.None=\"Monitorizare dezactivată\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Doar monitorizare (amuțește outputul)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitorizare și output\"\nBasic.AdvAudio.MonitoringSource=\"Monitorizare audio pentru „%1”\"\nBasic.AdvAudio.AudioTracks=\"Piste\"\nBasic.Settings.Hotkeys=\"Taste rapide\"\nBasic.Settings.Hotkeys.Pair=\"Combinațiile de taste partajate cu '%1' acționează ca comutatoare\"\nBasic.Settings.Hotkeys.Filter=\"Filtrează\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrează după tasta rapidă\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Acest hotkey este folosit de una sau mai multe acțiuni, click pentru a afișa conflictele\"\nBasic.Settings.Hotkeys.PleaseWait=\"Se încarcă tastele rapide, te rugăm să aștepți...\"\nBasic.Hotkeys.SelectScene=\"Comută la scenă\"\nBasic.SystemTray.Show=\"Afișează\"\nBasic.SystemTray.Hide=\"Ascunde\"\nBasic.SystemTray.Message.Reconnecting=\"Deconectat. Reconectare...\"\nHotkeys.Insert=\"Inserează\"\nHotkeys.Delete=\"Șterge\"\nHotkeys.CapsLock=\"Caps lock\"\nHotkeys.Pause=\"Pauză\"\nHotkeys.Left=\"Stânga\"\nHotkeys.Right=\"Dreapta\"\nHotkeys.Up=\"Sus\"\nHotkeys.Down=\"Jos\"\nHotkeys.Menu=\"Meniu\"\nHotkeys.Space=\"Spațiu\"\nHotkeys.NumpadNum=\"Tasta numerică %1\"\nHotkeys.NumpadMultiply=\"Numpad Multiplica\"\nHotkeys.NumpadAdd=\"Numpad Aduna\"\nHotkeys.NumpadSubtract=\"Numpad Scade\"\nHotkeys.NumpadDecimal=\"Numpad Zecimal\"\nHotkeys.AppleKeypadNum=\"%1 (Tastatură numerică)\"\nHotkeys.AppleKeypadMultiply=\"* (Tastatură numerică)\"\nHotkeys.AppleKeypadDivide=\"/ (Tastatură numerică)\"\nHotkeys.AppleKeypadAdd=\"+ (Tastatură numerică)\"\nHotkeys.AppleKeypadSubtract=\"- (Tastatură numerică)\"\nHotkeys.AppleKeypadDecimal=\". (Tastatură numerică)\"\nHotkeys.AppleKeypadEqual=\"= (Tastatură numerică)\"\nMute=\"Pune pe mut\"\nUnmute=\"Scoate de pe mut\"\nSceneItemShow=\"Afișează „%1”\"\nSceneItemHide=\"Ascunde „%1”\"\nOutputWarnings.NoTracksSelected=\"Trebuie să selectezi cel puțin o pistă\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Eroare setări output\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Toate output-urile trebuie să aibă cel puțin o pistă audio selectată.\"\nOutputWarnings.MP4Recording=\"Avertisment: Înregistrările salvate în MP4/MOV nu vor putea fi recuperate dacă fișierul nu poate fi finalizat (de ex., ca urmare a BSOD-urilor, întreruperilor de alimentare etc.). Dacă vrei să înregistrezi mai multe piste audio, ia în considerare folosirea MKV și remuxează înregistrarea în MP4/MOV după terminare (Fișier → Remuxează înregistrări)\"\nOutputWarnings.CannotPause=\"Avertisment: Înregistrările nu pot fi puse pe pauză dacă codificatorul de înregistrare este setat pe „(Folosește codificatorul de stream)”\"\nOutputWarnings.CodecIncompatible=\"Selecția codificatorului audio sau video a fost resetată din cauza unei incompatibilități. Vă rugăm să selectați un codificator compatibil din listă.\"\nCodecCompat.Incompatible=\"(Incompatibil cu %1)\"\nCodecCompat.CodecPlaceholder=\"Selectați Codificatorul...\"\nCodecCompat.ContainerPlaceholder=\"Selectați Formatul...\"\nCodecCompat.CodecMissingOnExit.Title=\"Nu este selectat niciun codificator\"\nCodecCompat.CodecMissingOnExit.Text=\"Cel puțin un codificator video sau audio nu este setat. Te rugăm să te asiguri că selectezi codificatoare atât pentru înregistrare, cât și pentru streaming.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Nu este selectat niciun format\"\nCodecCompat.ContainerMissingOnExit.Text=\"Nu a fost selectat niciun format de înregistrare. Te rugăm să selectezi un format de înregistrare care este compatibil cu codificatorul de stream selectat.\"\nFinalScene.Title=\"Șterge scena\"\nFinalScene.Text=\"Trebuie să existe cel puțin o scenă.\"\nNoSources.Title=\"Nicio sursă\"\nNoSources.Text=\"Se pare că nu ai adăugat încă nicio sursă video, așa că se va afișa doar un ecran negru. Sigur vrei să faci asta?\"\nNoSources.Text.AddSource=\"Poți adăuga oricând surse dând clic pe pictograma + de sub caseta Surse din fereastra principală.\"\nNoSources.Label=\"Nu ai nicio sursă. Dă clic pe\\nbutonul + de mai jos sau\\nclic dreapta aici pentru a adăuga una.\"\nChangeBG=\"Setează culoarea\"\nCustomColor=\"Culoare personalizată\"\nBrowserSource.EnableHardwareAcceleration=\"Activează accelerarea hardware pentru Browser Source\"\nAbout=\"Despre\"\nAbout.Info=\"OBS Studio este un program gratuit și open-source pentru înregistrare video și streaming live.\"\nAbout.Donate=\"Contribuie\"\nAbout.GetInvolved=\"Implică-te\"\nAbout.Authors=\"Autori\"\nAbout.License=\"Licență\"\nAbout.Error=\"Eroare! Fisierul nu s-a putut citi. \\n\\n Mergi la: %1\"\nAbout.Contribute=\"Sprijină proiectul OBS\"\nAddUrl.Title=\"Adaugă o sursă via URL\"\nAddUrl.Text=\"Ai tras un URL în OBS. Acest lucru va adăuga automat linkul ca sursă. Continui?\"\nResizeOutputSizeOfSource=\"Redimensionează outputul (dimensiunea sursei)\"\nResizeOutputSizeOfSource.Text=\"Rezoluțiile de bază și ale outputului vor fi redimensionate la dimensiunea sursei actuale.\"\nResizeOutputSizeOfSource.Continue=\"Vrei să continui?\"\nPreviewTransition=\"Previzualizează tranziția\"\nImporter=\"Importator pentru colecții de scene\"\nImporter.SelectCollection=\"Selectează o colecție de scene\"\nImporter.Collection=\"Colecție de scene\"\nImporter.HelpText=\"Adaugă fișiere în această fereastră pentru a importa colecții din OBS sau alte programe suportate.\"\nImporter.Path=\"Calea colecției\"\nImporter.Program=\"Aplicație detectată\"\nImporter.AutomaticCollectionPrompt=\"Caută automat colecții de scene\"\nImporter.AutomaticCollectionText=\"OBS poate găsi automat colecții de scene importabile din programe terțe suportate. Dorești ca OBS să găsească automat colecții pentru tine?\\n\\nPoți modifica acest lucru mai târziu în Setări > Generale > Importatoare.\"\nImporter.SelectFile=\"Răsfoiește...\"\nRestart=\"Repornește\"\nNeedsRestart=\"OBS Studio trebuie să fie repornit. Vrei să repornești acum?\"\nLoadProfileNeedsRestart=\"Profilul conține setări care necesită repornirea OBS:\\n%1\\n\\nDoriți să reporniți OBS pentru ca aceste setări să aibă efect?\"\nContextBar.NoSelectedSource=\"Nicio sursă selectată\"\nContextBar.MediaControls.PlayMedia=\"Redă conținutul media\"\nContextBar.MediaControls.PauseMedia=\"Pune pe pauză conținutul media\"\nContextBar.MediaControls.StopMedia=\"Oprește conținutul media\"\nContextBar.MediaControls.RestartMedia=\"Repornește conținutul media\"\nContextBar.MediaControls.PlaylistNext=\"Următorul în playlist\"\nContextBar.MediaControls.PlaylistPrevious=\"Anteriorul în playlist\"\nContextBar.MediaControls.BlindSeek=\"Widget de căutare media\"\nYouTube.Auth.Ok=\"Autorizarea a fost finalizată cu succes.\\nAcum poți închide această pagină.\"\nYouTube.Auth.NoCode=\"Procesul de autorizare nu a fost finalizat.\"\nYouTube.Auth.NoChannels=\"Nici un canal(e) disponibil(e) în contul selectat\"\nYouTube.Auth.WaitingAuth.Title=\"Autorizare pentru utilizatorul YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Te rugăm să completezi autorizarea în browserul extern.<br>Dacă browserul extern nu se deschide, urmează acest link și completează autorizarea:<br>%1\"\nYouTube.AuthError.Text=\"Eșuare obținere informații despre canal: %1\"\nYouTube.Actions.WindowTitle=\"Configurarea difuzării pe YouTube - Canal: %1\"\nYouTube.Actions.CreateNewEvent=\"Creează o nouă difuzare\"\nYouTube.Actions.ChooseEvent=\"Selectează o difuzare existentă\"\nYouTube.Actions.Title=\"Titlu*\"\nYouTube.Actions.MyBroadcast=\"Difuzarea mea\"\nYouTube.Actions.Description=\"Descriere\"\nYouTube.Actions.Privacy=\"Confidențialitate*\"\nYouTube.Actions.Privacy.Private=\"Privat\"\nYouTube.Actions.Privacy.Unlisted=\"Nelistat\"\nYouTube.Actions.Category=\"Categorie\"\nYouTube.Actions.Thumbnail=\"Miniatură\"\nYouTube.Actions.Thumbnail.SelectFile=\"Selectează un fișier...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Niciun fișier selectat\"\nYouTube.Actions.Thumbnail.ClearFile=\"Șterge\"\nYouTube.Actions.MadeForKids=\"Acest videoclip este destinat copiilor?*\"\nYouTube.Actions.MadeForKids.Yes=\"Da, este destinat copiilor.\"\nYouTube.Actions.MadeForKids.No=\"Nu, nu este destinat copiilor.\"\nYouTube.Actions.AdditionalSettings=\"Setări adiționale\"\nYouTube.Actions.Latency=\"Latență\"\nYouTube.Actions.Latency.Normal=\"Normală\"\nYouTube.Actions.Latency.Low=\"Scăzută\"\nYouTube.Actions.Latency.UltraLow=\"Ultrascăzută\"\nYouTube.Actions.EnableAutoStart=\"Activează pornirea automată\"\nYouTube.Actions.EnableAutoStop=\"Activează oprirea automată\"\nYouTube.Actions.AutoStartStop.TT=\"Indică dacă această difuzare programată ar trebui să pornească automat\"\nYouTube.Actions.EnableDVR=\"Activează DVR\"\nYouTube.Actions.360Video=\"Videoclip la 360°\"\nYouTube.Actions.ScheduleForLater=\"Programează pentru mai târziu\"\nYouTube.Actions.RememberSettings=\"Ține minte aceste setări\"\nYouTube.Actions.Create_Ready=\"Creează difuzarea\"\nYouTube.Actions.Create_GoLive=\"Creează difuzarea și pornește streamingul\"\nYouTube.Actions.Choose_Ready=\"Selectează o difuzare\"\nYouTube.Actions.Choose_GoLive=\"Selectează difuzarea și pornește streamingul\"\nYouTube.Actions.Create_Schedule=\"Programează difuzarea\"\nYouTube.Actions.Create_Schedule_Ready=\"Programează și selectează difuzarea\"\nYouTube.Actions.Dashboard=\"Deschide YouTube Studio\"\nYouTube.Actions.Error.Title=\"Eroare la crearea difuzării live\"\nYouTube.Actions.Error.Text=\"Eroare acces YouTube '%1'. <br/>Descrierea detaliată a erorii poate fi găsită la <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Eroare de acces YouTube. Verificați conexiunea la rețea sau accesul la serverul YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Eroare la crearea difuzării '%1'.<br/>Descrierea detaliată a erorii poate fi găsită la <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Niciun stream creat. Te rugăm să reconectezi contul.\"\nYouTube.Actions.Error.YouTubeApi=\"Eroare API YouTube. Consultați fișierul jurnal pentru mai multe informații.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Difuzarea selectată nu a fost găsită.\"\nYouTube.Actions.Error.FileMissing=\"Fișierul selectat nu există.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Deschiderea fișierului selectat nu a reușit.\"\nYouTube.Actions.Error.FileTooLarge=\"Fișierul selectat este prea mare (Limita: 2MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Tranziția spre difuzare a eșuat: %1<br/><br/>Dacă această eroare persistă, <a href='https://studio.youtube.com/video/%2/livestreaming'>deschide difuzarea în YouTube Studio</a> și încearcă manual.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Difuzarea face tranziția la etapa de testare, acest lucru poate dura ceva timp. Te rugăm să încerci din nou în 10-30 de secunde.\"\nYouTube.Actions.EventsLoading=\"Încarcare listă de evenimente...\"\nYouTube.Actions.EventCreated.Title=\"Eveniment creat\"\nYouTube.Actions.EventCreated.Text=\"Eveniment creat cu succes.\"\nYouTube.Actions.Stream.ScheduledFor=\"Programat(ă) pentru %1\"\nYouTube.Actions.Stream.Resume=\"Reluare flux întrerupt\"\nYouTube.Actions.Stream.YTStudio=\"Creat(ă) automat de YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Se creează o nouă difuzare live, te rugăm să aștepți...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Pornire manuală necesară\"\nYouTube.Actions.AutoStartStreamingWarning=\"Pornirea automată este dezactivată pentru acest eveniment, dă clic pe „Intră live” pentru a porni difuzarea.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Nu te vei putea reconecta.<br>Streamul tău se va opri și nu vei mai fi live.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Streamingul live nu este activat pe canalul de YouTube selectat.<br/><br/> Vezi<a href='https://www.youtube.com/features'>youtube.com/features</a> pentru mai multe informații.\"\nYouTube.Errors.livePermissionBlocked=\"Streamingul live este indisponibil pe canalul de YouTube selectat.<br/>Te rugăm să reții că poate dura până la 24 de ore pentru a deveni disponibil după activarea acesteia în setările canalului tău.<br/><br/>Vezi <a href='https://www.youtube.com/features'>youtube.com/features</a> pentru detalii.\"\nYouTube.Errors.errorExecutingTransition=\"Tranziția a eșuat din cauza unei erori din fundal. Încercați din nou în câteva secunde.\"\nYouTube.Errors.errorStreamInactive=\"YouTube nu primește date pentru stream-ul tău. Verificați configurația și încercați din nou.\"\nYouTube.Errors.invalidTransition=\"Încercarea de tranziție a fost nevalidă. Acest lucru se poate datora faptului că streamul nu a încheiat o tranziție anterioară. Te rugăm să aștepți câteva secunde și să încerci din nou.\"\nYouTube.DocksRemoval.Title=\"Șterge vechile andocări de browser ale YouTube\"\nYouTube.DocksRemoval.Text=\"Aceste andocări de browser vor fi eliminate ca fiind depreciate:\\n\\n%1\\nFolosește „Andocări/YouTube Live Control Room” în schimb.\"\nConfigDownload.WarningMessageTitle=\"Avertisment\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"URL particularizat pentru RTMP nu este specificat\"\nFailedToStartStream.InvalidCustomConfig=\"Configurare particularizată invalidă\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC indisponibil.\\n\\nGăsirea tipului de encoder '%1' a eșuat.\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Eșec la crearea encoder-ului audio\"\nFailedToStartStream.NoConfigSupplied=\"Configurare lipsă\"\nMultitrackVideo.IncompatibleSettings.Title=\"Setări incompatibile\"\n"
  },
  {
    "path": "frontend/data/locale/ru-RU.ini",
    "content": "Language=\"Русский\"\nApply=\"Применить\"\nCancel=\"Отмена\"\nClose=\"Закрыть\"\nSave=\"Сохранить\"\nDiscard=\"Отклонить\"\nDisable=\"Отключить\"\nYes=\"Да\"\nNo=\"Нет\"\nAdd=\"Добавить\"\nRemove=\"Удалить\"\nRename=\"Переименовать\"\nInteract=\"Взаимодействовать\"\nFilters=\"Фильтры\"\nProperties=\"Свойства\"\nMoveUp=\"Выше\"\nMoveDown=\"Ниже\"\nSettings=\"Настройки\"\nDisplay=\"Экран\"\nName=\"Название\"\nExit=\"Выход\"\nMixer=\"Микшер звука\"\nBrowse=\"Обзор\"\nMono=\"Моно\"\nStereo=\"Стерео\"\nDroppedFrames=\"Пропуск кадров %1 (%2%)\"\nProjector.Open.Program=\"Открыть проектор программы\"\nProjector.Open.Preview=\"Открыть проектор предпросмотра\"\nProjector.Open.Scene=\"Открыть проектор сцены\"\nProjector.Open.Source=\"Открыть проектор источника\"\nProjector.Open.Multiview=\"Открыть мультипросмотр\"\nProjector.Display=\"Экран: %1\"\nProjector.Window=\"Новое окно\"\nProjector.Title=\"Проектор\"\nProjector.Title.Scene=\"Сцена: %1\"\nProjector.Title.Source=\"Источник: %1\"\nProjector.Title.Multiview=\"Мультипросмотр\"\nProjector.ResizeWindowToContent=\"Окно по размеру содержимого\"\nClear=\"Очистить\"\nRevert=\"Вернуть\"\nShow=\"Показать\"\nHide=\"Скрыть\"\nUnhideAll=\"Показать все\"\nUntitled=\"Безымянный\"\nNew=\"Создать\"\nDuplicate=\"Дублировать\"\nEnable=\"Включить\"\nDisableOSXVSync=\"Отключить вертикальную синхронизацию macOS\"\nResetOSXVSyncOnExit=\"Сброс вертикальной синхронизации macOS при выходе\"\nHighResourceUsage=\"Кодировка перегружена! Попробуйте понизить настройки видео или использовать более быструю предустановку кодировки.\"\nTransition=\"Переход\"\nQuickTransitions=\"Быстрые переходы\"\nFadeToBlack=\"Затухание в чёрный\"\nLeft=\"Слева\"\nRight=\"Справа\"\nTop=\"Сверху\"\nBottom=\"Снизу\"\nReset=\"Сброс\"\nHours=\"Часов\"\nMinutes=\"Минут\"\nSeconds=\"Секунд\"\nDeprecated=\"Устаревшее\"\nReplayBuffer=\"Буфер повтора\"\nImport=\"Импорт\"\nExport=\"Экспортировать\"\nCopy=\"Копировать\"\nPaste=\"Вставить\"\nPasteReference=\"Вставить (ссылка)\"\nPasteDuplicate=\"Вставить (дубликат)\"\nRemuxRecordings=\"Ремультиплексирование записей\"\nNext=\"Далее\"\nBack=\"Назад\"\nDefaults=\"По умолчанию\"\nRestoreDefaults=\"По умолчанию\"\nHideMixer=\"Не показывать в микшере\"\nTransitionOverride=\"Указать другой переход\"\nShowTransition=\"Переход появления\"\nHideTransition=\"Переход скрытия\"\nNone=\"Нет\"\nStudioMode.Preview=\"Предпросмотр\"\nStudioMode.Program=\"Передача\"\nStudioMode.PreviewSceneName=\"Предпросмотр: %1\"\nStudioMode.ProgramSceneName=\"Передача: %1\"\nShowInMultiview=\"Показать в мультипросмотре\"\nVerticalLayout=\"Расположить вертикально\"\nGroup=\"Группа\"\nDoNotShowAgain=\"Не показывать снова\"\nDefault=\"(По умолчанию)\"\nCalculating=\"Вычисляется…\"\nFullscreen=\"Полноэкранный режим\"\nWindowed=\"Оконный режим\"\nRefreshBrowser=\"Обновить\"\nAspectRatio=\"Соотношение сторон <b>%1:%2</b>\"\nLockVolume=\"Блокировать громкость\"\nLogViewer=\"Просмотр журнала\"\nShowOnStartup=\"Показывать при запуске\"\nOpenFile=\"Открыть файл\"\nAddScene=\"Добавить сцену\"\nAddSource=\"Добавить источник\"\nRemoveScene=\"Удалить сцену\"\nRemoveSource=\"Удалить источник(и)\"\nMoveSceneUp=\"Перенести сцену вверх\"\nMoveSceneDown=\"Перенести сцену вниз\"\nMoveSourceUp=\"Перенести источник(и) вверх\"\nMoveSourceDown=\"Перенести источник(и) вниз\"\nSourceProperties=\"Открыть свойства источника\"\nSourceFilters=\"Открыть фильтры источника\"\nMixerToolbarMenu=\"Меню микшера звука\"\nSceneFilters=\"Открыть фильтры сцены\"\nList=\"Список\"\nGrid=\"Сетка\"\nAutomatic=\"Автоматически\"\nComingSoon=\"В разработке\"\nPluginsFailedToLoad.Title=\"Ошибка загрузки плагина\"\nPluginsFailedToLoad.Text=\"Следующие плагины OBS не удалось загрузить:\\n\\n%1\\nПожалуйста, обновите или удалите эти плагины.\"\nAlreadyRunning.Title=\"OBS уже запущен\"\nAlreadyRunning.Text=\"OBS уже запущен! Пожалуйста, закройте все запущенные экземпляры OBS перед попыткой запустить новые (только если вы не хотели именно этого). Если вы настроили OBS на сворачивание в область уведомлений, пожалуйста, проверьте, возможно, он до сих пор запущен.\"\nAlreadyRunning.LaunchAnyway=\"Запустить в любом случае\"\nCrashHandling.Dialog.Title=\"Обнаружена ошибка OBS Studio\"\nCrashHandling.Labels.Text=\"OBS Studio не был отключен должным образом.\\n\\nЗапустить в безопасном режиме? (с отключением сторонних плагинов, скриптов и веб-сокетов)?\"\nCrashHandling.Labels.PrivacyNotice=\"Вы также можете автоматически выгрузить последний отчет о сбое на OBSProject.<br /><br />Перед выгрузкой файлов ознакомьтесь с <a href='https://obsproject.com/privacy-policy'>политикой конфиденциальности</a>, обратив особое внимание на части, касающиеся отправки файлов.\"\nCrashHandling.Checkbox.SendReport=\"Я прочитал политику конфиденциальности и соглашаюсь на отправку.\"\nCrashHandling.Buttons.LaunchSafe=\"Запустить в безопасном режиме\"\nCrashHandling.Buttons.LaunchNormal=\"Запустить в обычном режиме\"\nCrashHandling.Errors.UploadJSONError=\"Произошла ошибка при попытке отправки последнего журнала. Пожалуйста, повторите попытку через несколько секунд.\"\nCrashHandling.Errors.Title=\"Ошибка отправки журнала ошибки\"\nSafeMode.Restart=\"Хотите перезапустить OBS в безопасном режиме (с отключением сторонних плагинов, скриптов и веб-сокетов)?\"\nSafeMode.RestartNormal=\"Хотите перезапустить OBS в обычном режиме?\"\nChromeOS.Title=\"Неподдерживаемая платформа\"\nChromeOS.Text=\"Похоже, что OBS работает внутри контейнера ChromeOS. Эта платформа не поддерживается.\"\nWine.Title=\"Обнаружен Wine\"\nWine.Text=\"Запуск OBS в Wine не поддерживается, и многие функции, такие как захват или источники устройств, не будут работать или будут, но ограниченно.<br><br>Рекомендуется запустить собственную версию OBS, например, <a href='https://flathub.org/apps/details/com.obsproject.Studio'>нашу Flatpak-версию</a>, или воспользоваться пакетами вашей операционной системы.\"\nDockCloseWarning.Title=\"Закрытие закрепляемого окна\"\nDockCloseWarning.Text=\"Вы только что закрыли закрепляемое окно. Если вы хотите показать его снова, используйте меню «Док-панели» в строке меню.\"\nExtraBrowsers=\"Пользовательские док-панели браузера\"\nExtraBrowsers.Info=\"Добавьте док-панели, указав имя или ссылку, затем нажмите «Принять» или «Закрыть», чтобы открыть док-панели. Вы можете добавлять или удалять док-панели когда угодно.\"\nExtraBrowsers.DockName=\"Имя док-панели\"\nAuth.Authing.Title=\"Выполняется вход...\"\nAuth.Authing.Text=\"Выполняется вход на %1. Пожалуйста, подождите...\"\nAuth.AuthFailure.Title=\"Ошибка входа\"\nAuth.AuthFailure.Text=\"Ошибка входа на %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Требуется вход\"\nAuth.InvalidScope.Text=\"Требования к входу для %1 изменились. Некоторые функции могут быть недоступны.\"\nAuth.LoadingChannel.Title=\"Загрузка информации о канале...\"\nAuth.LoadingChannel.Text=\"Загрузка информации о канале для %1, пожалуйста подождите...\"\nAuth.LoadingChannel.Error=\"Не удалось получить информацию о канале.\"\nAuth.ChannelFailure.Title=\"Не удалось загрузить канал\"\nAuth.ChannelFailure.Text=\"Не удалось загрузить информацию о канале для %1\\n\\n%2: %3\"\nAuth.Chat=\"Чат\"\nAuth.StreamInfo=\"Информация о трансляции\"\nTwitchAuth.Stats=\"Статистика Twitch\"\nTwitchAuth.Feed=\"Лента активности Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Не удалось запросить ключ трансляции\"\nTwitchAuth.TwoFactorFail.Text=\"Не удалось подключиться к вашей учётной записи Twitch. Убедитесь, что двухфакторная аутентификация включена в <a href='https://www.twitch.tv/settings/security'>«Настройках безопасности Twitch»</a>; это требуется для начала прямых трансляций.\"\nRestreamAuth.Channels=\"Каналы Restream\"\nCopy.Filters=\"Копировать фильтры\"\nPaste.Filters=\"Вставить фильтры\"\nBrowserPanelInit.Title=\"Инициализация браузера...\"\nBrowserPanelInit.Text=\"Инициализация браузера, пожалуйста, подождите...\"\nBandwidthTest.Region=\"Регион\"\nBandwidthTest.Region.US=\"США\"\nBandwidthTest.Region.EU=\"Европа\"\nBandwidthTest.Region.Asia=\"Азия\"\nBandwidthTest.Region.Other=\"Другой\"\nBasic.AutoConfig=\"Мастер автоматической настройки\"\nBasic.AutoConfig.ApplySettings=\"Применить настройки\"\nBasic.AutoConfig.StartPage=\"Информация об использовании\"\nBasic.AutoConfig.StartPage.SubTitle=\"Укажите, для чего вы хотите использовать программу\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Оптимизировать для трансляции, вторично для записи\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Оптимизировать только для записи, я не буду вести трансляции\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Я буду использовать только виртуальную камеру\"\nBasic.AutoConfig.VideoPage=\"Настройки видео\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Укажите настройки видео, которые вы хотите использовать\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Использовать текущее (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Экран %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Использовать текущую (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 или 30, но предпочитать 60 по возможности\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 или 30, но предпочитать высокое разрешение\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Примечание: Разрешение холста (базовое) необязательно должно совпадать с разрешением трансляции или записи. Реальное разрешение вашей трансляции или записи может быть снижено для уменьшения использования ресурсов или битрейта.\"\nBasic.AutoConfig.StreamPage=\"Информация о трансляции\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Пожалуйста, введите вашу информацию о трансляции\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Привязать учётную запись (рекомендуется)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Выйти из аккаунта\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Выйти из аккаунта/учётной записи?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Это изменение вступит в силу немедленно. Уверены, что хотите выйти?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Получить ключ трансляции\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Подробности\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Использовать ключ потока\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Использовать ключ потока (для продвинутых пользователей)\"\nBasic.AutoConfig.StreamPage.Service=\"Служба\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Показать всё...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Настраиваемый...\"\nBasic.AutoConfig.StreamPage.Server=\"Сервер\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Ключ потока\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: введите пароль шифрования.\\nRTMP: введите ключ, предоставленный службой.\\nSRT: введите ID потока, если служба использует его.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Ключ кодировщика\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Токен предъявителя\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Привязанная учётная запись\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Рассчитать битрейт при проверке пропускной способности (может занять пару минут)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Предпочитать аппаратное кодирование\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Аппаратное кодирование снижает нагрузку на процессор, но может требовать больший битрейт для достижения того же уровня качества.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Предупреждение трансляции\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Проверка пропускной способности трансляции случайных видеоданных без звука на вашем канале. Рекомендуется временно отключить запись трансляции в файл и сделать трансляцию закрытой, пока проверка не завершится. Продолжить?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Тест %1\"\nBasic.AutoConfig.TestPage=\"Конечные результаты\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Сейчас программа выполняет набор тестов для оценки лучших настроек\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Проверка завершена\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Испытание пропускной способности, это может занять несколько минут...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Выходные данные для протокола этой службы не найдены\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Подключение к: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Ошибка подключения к серверам. Пожалуйста, проверьте подключение к Интернету и повторите попытку.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Проверка пропускной способности для: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Проверка кодировщика трансляции, это может занять минуту...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Проверка кодировщика записи, это может занять минуту...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Не удалось запустить кодировщик\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Тестирование %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Потоковый кодировщик\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Кодировщик записи\"\nBasic.AutoConfig.TestPage.Result.Header=\"Программа определила, что это примерные настройки, которые вам подходят:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Для использования этих настроек нажмите «Применить настройки». Для повторной настройки с помощью мастера нажмите «Назад». Чтобы задать настройки самостоятельно, нажмите «Отмена» и откройте «Настройки».\"\nBasic.AutoConfig.Info=\"Мастер автонастройки определит оптимальные параметры на основе особенностей вашего компьютера и скорости Интернета.\"\nBasic.AutoConfig.RunAnytime=\"Его можно запустить снова из меню «Сервис».\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Разрешение трансляции (масштабированное)\"\nBasic.Stats=\"Статистика\"\nBasic.Stats.CPUUsage=\"Использование ЦП\"\nBasic.Stats.HDDSpaceAvailable=\"Доступно места на диске\"\nBasic.Stats.MemoryUsage=\"Использование памяти\"\nBasic.Stats.AverageTimeToRender=\"Среднее время рендеринга кадра\"\nBasic.Stats.SkippedFrames=\"Пропуск кадров (кодирование)\"\nBasic.Stats.MissedFrames=\"Пропуск кадров (рендеринг)\"\nBasic.Stats.Output.Stream=\"Трансляция\"\nBasic.Stats.Output.Recording=\"Запись\"\nBasic.Stats.Status=\"Состояние\"\nBasic.Stats.Status.Recording=\"Запись\"\nBasic.Stats.Status.Live=\"ЭФИР\"\nBasic.Stats.Status.Reconnecting=\"Переподключение\"\nBasic.Stats.Status.Inactive=\"Неактивна\"\nBasic.Stats.Status.Active=\"Активно\"\nBasic.Stats.DroppedFrames=\"Пропуск кадров (сеть)\"\nBasic.Stats.MegabytesSent=\"Всего отправлено\"\nBasic.Stats.Bitrate=\"Битрейт\"\nBasic.Stats.DiskFullIn=\"Время до заполнения диска\"\nBasic.Stats.DiskFullIn.Text=\"%1 ч, %2 мин.\"\nBasic.Stats.ResetStats=\"Сбросить статистику\"\nResetUIWarning.Title=\"Уверены, что хотите сбросить настройки пользовательского интерфейса?\"\nResetUIWarning.Text=\"Сброс пользовательского интерфейса скроет дополнительные доки. Вам нужно будет отобразить эти доки в меню «Док-панели», если вы хотите, чтобы они были видны.\\n\\nУверены, что хотите сбросить интерфейс?\"\nUpdater.Title=\"Доступно новое обновление\"\nUpdater.Text=\"Доступно новое обновление:\"\nUpdater.UpdateNow=\"Обновить сейчас\"\nUpdater.RemindMeLater=\"Напомнить позже\"\nUpdater.Skip=\"Пропустить версию\"\nUpdater.NoUpdatesAvailable.Title=\"Нет доступных обновлений\"\nUpdater.NoUpdatesAvailable.Text=\"Обновления не обнаружены\"\nUpdater.BranchNotFound.Title=\"Канал обновления удалён\"\nUpdater.BranchNotFound.Text=\"Выбранный вами канал обновления больше недоступен, OBS был сброшен к настройкам по умолчанию.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Проверка целостности недоступна\"\nUpdater.RepairButUpdatesAvailable.Text=\"Проверка целостности файлов возможна только для последней версии. Используйте «Справка» → «Проверить обновления» для проверки и обновления вашей установки OBS.\"\nUpdater.RepairConfirm.Title=\"Подтвердите проверку целостности\"\nUpdater.RepairConfirm.Text=\"Проверка целостности выполнит сканирование вашей установки OBS и перекачает повреждённые/изменённые файлы. Это может занять некоторое время.\\n\\nХотите продолжить?\"\nUpdater.FailedToLaunch=\"Не удалось проверить обновления\"\nQuickTransitions.SwapScenes=\"Менять местами сцены предпросмотра и передачи по переходу\"\nQuickTransitions.SwapScenesTT=\"Меняет местами сцены предпросмотра и передачи после перехода (если изначальная сцена передачи до сих пор существует).\\nЭто не отменяет никаких изменений, которые возможно были сделаны изначально в сцене передачи.\"\nQuickTransitions.DuplicateScene=\"Дублировать сцену\"\nQuickTransitions.DuplicateSceneTT=\"При редактировании одной и той же сцены функция позволяет править преобразование/видимость источников без изменения вывода передачи.\\nДля редактирования свойств источников без изменения вывода передачи, включите «Дублировать источники».\\nИзменение этого параметра приведёт к сбросу сцены передачи (если она ещё существует).\"\nQuickTransitions.EditProperties=\"Дублировать источники\"\nQuickTransitions.EditPropertiesTT=\"При редактировании одной и той же сцены, функция позволяет править свойства источников без изменения вывода передачи.\\nЭто можно использовать, только если параметр «Дублировать сцену» включён.\\nНекоторые источники (такие как захват или медиа-источники) не поддерживаются и не могут быть отредактированы отдельно.\\nИзменение этого параметра приведёт к сбросу сцены передачи (если она ещё существует).\\n\\nПредупреждение: поскольку источники будут дублироваться, это может потребовать дополнительных системных ресурсов или видео.\"\nQuickTransitions.HotkeyName=\"Быстрый переход: %1\"\nBasic.AddTransition=\"Добавить настраиваемый переход\"\nBasic.RemoveTransition=\"Удалить настраиваемый переход\"\nBasic.TransitionProperties=\"Параметры перехода\"\nBasic.SceneTransitions=\"Переходы между сценами\"\nBasic.TransitionDuration=\"Длительность\"\nBasic.TogglePreviewProgramMode=\"Режим студии\"\nBasic.EnablePreviewProgramMode=\"Включить режим студии\"\nBasic.DisablePreviewProgramMode=\"Отключить режим студии\"\nUndo.Undo=\"Отменить\"\nUndo.Redo=\"Повторить\"\nUndo.Add=\"добавление «%1»\"\nUndo.Delete=\"удаление «%1»\"\nUndo.Rename=\"переименование «%1»\"\nUndo.SceneCollection.Switch=\"переключение на «%1»\"\nUndo.Item.Undo=\"Отменить %1\"\nUndo.Item.Redo=\"Повторить %1\"\nUndo.Sources.Multi=\"удалить источники %1\"\nUndo.Filters=\"настройку фильтра в «%1»\"\nUndo.Filters.Paste.Single=\"вставку фильтра «%1» в «%2»\"\nUndo.Filters.Paste.Multiple=\"копирование фильтров из «%1» в «%2»\"\nUndo.Transform=\"преобразование источников в «%1»\"\nUndo.Transform.Paste=\"вставку настроек преобразования в «%1»\"\nUndo.Transform.Rotate=\"поворот в «%1»\"\nUndo.Transform.Reset=\"сброс преобразования в «%1»\"\nUndo.Transform.HFlip=\"преобразование по горизонтали в «%1»\"\nUndo.Transform.VFlip=\"преобразование по вертикали в «%1»\"\nUndo.Transform.FitToScreen=\"преобразование по размеру экрана в «%1»\"\nUndo.Transform.StretchToScreen=\"растяжение по размеру экрана в «%1»\"\nUndo.Transform.Center=\"по центру экрана в «%1»\"\nUndo.Transform.VCenter=\"выравнивание по вертикали в «%1»\"\nUndo.Transform.HCenter=\"выравнивание по горизонтали в «%1»\"\nUndo.Volume.Change=\"изменение громкости у «%1»\"\nUndo.Volume.Mute=\"заглушение «%1»\"\nUndo.Volume.Unmute=\"включение звука «%1»\"\nUndo.Balance.Change=\"изменение баланса аудио на «%1»\"\nUndo.SyncOffset.Change=\"изменение сдвига синхронизации звука «%1»\"\nUndo.MonitoringType.Change=\"смена прослушивания аудио для «%1»\"\nUndo.Mixers.Change=\"настройка микшеров звука в «%1»\"\nUndo.ForceMono.On=\"включение принудительного моно «%1»\"\nUndo.ForceMono.Off=\"отключение принудительного моно «%1»\"\nUndo.Properties=\"настройки свойств источника «%1»\"\nUndo.Scene.Duplicate=\"дублирование сцены «%1»\"\nUndo.ShowTransition=\"переход появления на «%1»\"\nUndo.HideTransition=\"переход скрытия на «%1»\"\nUndo.ShowSceneItem=\"отображение «%1» в «%2»\"\nUndo.HideSceneItem=\"скрытие «%1» в «%2»\"\nUndo.ReorderSources=\"пересортировку источников в «%1»\"\nUndo.MoveUp=\"перенос «%1» выше в «%2»\"\nUndo.MoveDown=\"перенос «%1» ниже в «%2»\"\nUndo.MoveToTop=\"перенос «%1» в верхнюю часть «%2»\"\nUndo.MoveToBottom=\"перенос «%1» в нижнюю часть «%2»\"\nUndo.PasteSource=\"вставку источника(ов) в \\\"%1\\\"\"\nUndo.PasteSourceRef=\"вставку ссылки(ок) на источники в «%1»\"\nUndo.GroupItems=\"сгруппированные элементы в «%1»\"\nTransitionNameDlg.Text=\"Пожалуйста, введите имя перехода\"\nTransitionNameDlg.Title=\"Имя перехода\"\nTitleBar.SafeMode=\"БЕЗОПАСНЫЙ РЕЖИМ\"\nTitleBar.PortableMode=\"Переносной режим\"\nTitleBar.Profile=\"Профиль\"\nTitleBar.Scenes=\"Сцены\"\nNameExists.Title=\"Название уже существует\"\nNameExists.Text=\"Это название уже используется.\"\nNoNameEntered.Title=\"Пожалуйста, укажите действительное имя\"\nNoNameEntered.Text=\"Вы не можете использовать пустые названия.\"\nConfirmStart.Title=\"Начать трансляцию?\"\nConfirmStart.Text=\"Уверены, что хотите начать трансляцию?\"\nConfirmStop.Title=\"Остановить трансляцию?\"\nConfirmStop.Text=\"Уверены, что хотите остановить трансляцию?\"\nConfirmStopRecord.Title=\"Остановить запись?\"\nConfirmStopRecord.Text=\"Уверены, что хотите остановить запись?\"\nConfirmBWTest.Title=\"Начать проверку пропускной способности?\"\nConfirmBWTest.Text=\"Вы настроили OBS в режиме проверки пропускной способности. Этот режим позволяет проверять сеть без выхода вашего канала в эфир. После того, как вы закончите проверку, вам необходимо отключить эту функцию для того, чтобы зрители могли видеть вашу трансляцию.\\n\\nХотите продолжить?\"\nConfirmExit.Title=\"Действующие выводы\"\nConfirmExit.Text=\"OBS всё ещё активен. Все стримы/записи будут приостановлены.\"\nConfirmRemove.Title=\"Подтвердить удаление\"\nConfirmRemove.Text=\"Уверены, что хотите удалить «%1»?\"\nConfirmRemove.TextMultiple=\"Уверены, что хотите удалить %1 элем.?\"\nConfirmReset.Title=\"Сбросить свойства\"\nConfirmReset.Text=\"Вы действительно хотите сбросить текущие свойства по умолчанию?\"\nOutput.StartStreamFailed=\"Не удалось запустить трансляцию\"\nOutput.StartRecordingFailed=\"Не удалось начать запись\"\nOutput.StartReplayFailed=\"Не удалось запустить буфер повтора\"\nOutput.StartVirtualCamFailed=\"Не удалось запустить виртуальную камеру\"\nOutput.StartFailedGeneric=\"Сбой вывода. Подробности отражены в журнале.\\n\\nПримечание: Если вы используете кодировщики NVENC или AMD, убедитесь, что у вас установлена последняя версия видеодрайвера.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Во время паузы нельзя сохранять повторы\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Предупреждение: Повторы не могут быть сохранены во время паузы.\"\nOutput.ConnectFail.Title=\"Не удалось подключиться\"\nOutput.ConnectFail.BadPath=\"Неправильный путь или ссылка. Проверьте, правильно ли вы задали настройки.\"\nOutput.ConnectFail.ConnectFailed=\"Не удалось подключиться к серверу\"\nOutput.ConnectFail.InvalidStream=\"Не удалось получить доступ к указанному ключу канала или стрима, пожалуйста, перепроверьте ключ. Если он правильный, проблема может быть с подключением к серверу.\"\nOutput.ConnectFail.HdrDisabled=\"HDR-выход в данный момент отключён для этого вывода.\"\nOutput.ConnectFail.Error=\"Произошла непредвиденная ошибка при попытке подключения к серверу. Подробности в файле журнала.\"\nOutput.ConnectFail.Disconnected=\"Отключён от сервера.\"\nOutput.StreamEncodeError.Title=\"Ошибка кодирования\"\nOutput.StreamEncodeError.Msg=\"Произошла ошибка кодировщика во время трансляции.\"\nOutput.StreamEncodeError.Msg.LastError=\"Произошла ошибка кодировщика во время трансляции:<br><br>%1\"\nOutput.RecordFail.Title=\"Не удалось начать запись\"\nOutput.RecordFail.Unsupported=\"Выходной формат либо не поддерживается, либо не поддерживает более одной звуковой дорожки. Пожалуйста, проверьте настройки и повторите попытку.\"\nOutput.RecordNoSpace.Title=\"Недостаточно места на диске\"\nOutput.RecordNoSpace.Msg=\"На диске недостаточно места для продолжения записи.\"\nOutput.RecordError.Title=\"Ошибка записи\"\nOutput.RecordError.Msg=\"Во время записи произошла неопознанная ошибка.\"\nOutput.RecordError.EncodeErrorMsg=\"Произошла ошибка кодировщика во время записи.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Произошла ошибка кодировщика во время записи:<br><br>%1\"\nOutput.BadPath.Title=\"Неправильный путь к файлу\"\nOutput.BadPath.Text=\"Настроенный путь для записей нельзя открыть. Пожалуйста, проверьте путь к записям в меню «Настройки → Выход → Запись».\"\nOutput.NoBroadcast.Title=\"Трансляция не настроена\"\nOutput.NoBroadcast.Text=\"Вам нужно настроить трансляцию, прежде чем вы сможете начать её.\"\nOutput.BroadcastStartFailed=\"Не удалось начать трансляцию\"\nOutput.BroadcastStopFailed=\"Не удалось остановить трансляцию\"\nLogUploadDialog.Title=\"Отправка файла журнала OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Перед загрузкой любых файлов ознакомьтесь с <a href='https://obsproject.com/privacy-policy'>политикой конфиденциальности</a> и её разделом, касающимся отправки файлов.\"\nLogUploadDialog.Labels.Progress=\"Выполняется отправка журнала. Пожалуйста, подождите...\"\nLogUploadDialog.Labels.Description.AppLog=\"Ваш файл журнала был загружен. Теперь вы можете поделиться ссылкой с целью отладки или поддержки.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Ваш отчёт об ошибке был отправлен. Теперь вы можете поделиться ссылкой с целью отладки.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Отправить\"\nLogUploadDialog.Buttons.CopyURL=\"Скопировать ссылку журнала\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Анализ файла журнала\"\nLogUploadDialog.Buttons.RetryButton=\"Повторить\"\nLogUploadDialog.Errors.Template=\"Произошла ошибка при попытке отправки файла:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Файлы для отправки не были найдены, или файл был пустым.\"\nRemux.SourceFile=\"Файл записи OBS\"\nRemux.TargetFile=\"Конечный файл\"\nRemux.Remux=\"Ремультиплексировать\"\nRemux.Stop=\"Остановить ремультиплексирование\"\nRemux.ClearFinished=\"Убрать завершённые из списка\"\nRemux.ClearAll=\"Очистить список\"\nRemux.OBSRecording=\"Файл записи OBS\"\nRemux.FinishedTitle=\"Ремультиплексирование завершено\"\nRemux.Finished=\"Запись ремультиплексирована\"\nRemux.FinishedError=\"Запись ремультиплексирована, но файл может быть неполон\"\nRemux.SelectRecording=\"Выберите запись OBS...\"\nRemux.SelectTarget=\"Выберите файл...\"\nRemux.FileExistsTitle=\"Файлы существуют\"\nRemux.FileExists=\"Следующие файлы уже существуют. Хотите их перезаписать?\"\nRemux.ExitUnfinishedTitle=\"Выполняется ремультиплексирование\"\nRemux.ExitUnfinished=\"Ремультиплексирование не закончено, остановка процесса сейчас может сделать файл непригодным.\\nВы уверены, что хотите остановить ремультиплексирование?\"\nRemux.HelpText=\"Перетащите файлы в это окно для ремультиплексирования или выберите пустую ячейку «Файл записи OBS» для выбора файла.\"\nRemux.NoFilesAddedTitle=\"Не добавлен файл для ремультиплексирования\"\nRemux.NoFilesAdded=\"Ни один файл не добавлен для ремультиплексирования. Перетащите папку, содержащую один или несколько видеофайлов.\"\nMissingFiles=\"Отсутствующие файлы\"\nMissingFiles.MissingFile=\"Отсутствующий файл\"\nMissingFiles.NewFile=\"Новый файл\"\nMissingFiles.HelpText=\"Некоторые файлы отсутствуют с момента последнего запуска OBS.\"\nMissingFiles.Clear=\"<очищен>\"\nMissingFiles.NumFound=\"Найдено %1 из %2\"\nMissingFiles.Search=\"Поиск каталога...\"\nMissingFiles.SelectFile=\"Выберите файл...\"\nMissingFiles.SelectDir=\"Выберите папку для поиска\"\nMissingFiles.State=\"Состояние\"\nMissingFiles.Missing=\"Отсутствует\"\nMissingFiles.Replaced=\"Заменён\"\nMissingFiles.Cleared=\"Очищен\"\nMissingFiles.Found=\"Найден\"\nMissingFiles.AutoSearch=\"Найдены дополнительные совпадения файлов\"\nMissingFiles.AutoSearchText=\"OBS нашёл в этой папке дополнительные совпадения для недостающих файлов. Хотите их добавить?\"\nMissingFiles.NoMissing.Title=\"Проверка пропавших файлов\"\nMissingFiles.NoMissing.Text=\"Пропавшие файлы не обнаружены.\"\nMacPermissions.MenuAction=\"Просмотр разрешений приложения…\"\nMacPermissions.Title=\"Просмотр разрешений приложения\"\nMacPermissions.Description=\"OBS Studio требует вашего разрешения для предоставления определённых возможностей. Желательно включить эти разрешения, но они не требуются для использования приложения. Вы всегда можете включить их позже.\"\nMacPermissions.Description.OpenDialog=\"Это меню можно открыть заново из меню OBS Studio.\"\nMacPermissions.AccessGranted=\"Доступ предоставлен\"\nMacPermissions.RequestAccess=\"Запросить доступ\"\nMacPermissions.OpenPreferences=\"Открыть настройки %1\"\nMacPermissions.Item.ScreenRecording=\"Запись экрана\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS требует этого разрешения, чтобы захватить ваш экран.\"\nMacPermissions.Item.Camera=\"Камера\"\nMacPermissions.Item.Camera.Details=\"Это разрешение необходимо для того, чтобы захватывать содержимое с веб-камеры или карты захвата.\"\nMacPermissions.Item.Microphone=\"Микрофон\"\nMacPermissions.Item.Microphone.Details=\"OBS требуется это разрешение, если вы хотите захватывать ваши микрофон или внешнее звуковое устройство.\"\nMacPermissions.Item.InputMonitoring=\"Мониторинг ввода\"\nMacPermissions.Item.InputMonitoring.Details=\"Это разрешение необходимо для того, чтобы горячие клавиши работали, пока OBS находится в фоновом режиме.\"\nMacPermissions.Item.Accessibility=\"Спец. возможности\"\nMacPermissions.Item.Accessibility.Details=\"В более старых установках OBS может быть указан в разделе \\\"Специальные возможности\\\" вместо \\\"Мониторинг ввода\\\".\"\nMacPermissions.Continue=\"Продолжить\"\nSourceLeak.Title=\"Ошибка очистки источника\"\nSourceLeak.Text=\"При изменении коллекций сцен возникла проблема, и некоторые источники не удалось выгрузить. Эта проблема обычно вызвана плагинами, которые не освобождают ресурсы должным образом. Пожалуйста, убедитесь, что все используемые вами плагины обновлены.\\n\\nOBS Studio завершает работу, чтобы предотвратить возможное повреждение данных.\"\nBasic.DesktopDevice1=\"Звук раб. стола\"\nBasic.DesktopDevice2=\"Звук раб. стола 2\"\nBasic.AuxDevice1=\"Микр/доп\"\nBasic.AuxDevice2=\"Микр/доп 2\"\nBasic.AuxDevice3=\"Микр/доп 3\"\nBasic.AuxDevice4=\"Микр/доп 4\"\nBasic.Scene=\"Сцена\"\nBasic.DisplayCapture=\"Захват экрана\"\nBasic.Main.PreviewConextMenu.Enable=\"Включить предпросмотр\"\nBasic.Main.Preview.Disable=\"Отключить предпросмотр\"\nScaleFiltering=\"Фильтр масштабирования\"\nScaleFiltering.Point=\"Точечный\"\nScaleFiltering.Bilinear=\"Билинейный\"\nScaleFiltering.Bicubic=\"Бикубический\"\nScaleFiltering.Lanczos=\"Метод Ланцоша\"\nScaleFiltering.Area=\"Область\"\nBlendingMethod=\"Метод смешивания\"\nBlendingMethod.Default=\"По умолчанию\"\nBlendingMethod.SrgbOff=\"SRGB откл.\"\nBlendingMode=\"Метод смешивания\"\nBlendingMode.Normal=\"Обычный\"\nBlendingMode.Additive=\"Добавить\"\nBlendingMode.Subtract=\"Вычитание\"\nBlendingMode.Screen=\"Экран\"\nBlendingMode.Multiply=\"Умножение\"\nBlendingMode.Lighten=\"Осветление\"\nBlendingMode.Darken=\"Затемнение\"\nDeinterlacing=\"Устранение чересстрочности\"\nDeinterlacing.Discard=\"Отклонить\"\nDeinterlacing.TopFieldFirst=\"Верхнее поле первое\"\nDeinterlacing.BottomFieldFirst=\"Нижнее поле первое\"\nVolControl.SliderUnmuted=\"Регулятор громкости «%1»:\"\nVolControl.SliderMuted=\"Регулятор громкости «%1»: (сейчас заглушён)\"\nVolControl.Mute=\"Заглушить «%1»\"\nVolControl.Properties=\"Свойства «%1»\"\nVolControl.UnassignedWarning.Title=\"Неназначенный источник звука\"\nVolControl.UnassignedWarning.Text=\"«%1» не назначен ни для одной аудиодорожки и не будет слышен во время трансляции или записи.\\n\\nЧтобы назначить источник звука для дорожки, откройте «Расширенные свойства звука» из меню правой кнопки мыши или с помощью кнопки шестерёнки на док-панели инструментов микшера.\"\nBasic.Main.AddSceneDlg.Title=\"Добавить сцену\"\nBasic.Main.AddSceneDlg.Text=\"Пожалуйста, введите название сцены\"\nBasic.Main.DefaultSceneName.Text=\"Сцена %1\"\nBasic.Main.AddSceneCollection.Title=\"Добавить коллекцию сцен\"\nBasic.Main.AddSceneCollection.Text=\"Пожалуйста, введите имя коллекции сцен\"\nBasic.Main.RenameSceneCollection.Title=\"Переименовать коллекцию сцен\"\nBasic.Main.RemigrateSceneCollection.Title=\"Обновить разрешение коллекции сцен\"\nBasic.Main.RemigrateSceneCollection.Text=\"Хотите обновить разрешение коллекции сцен «%1», чтобы оно соответствовало разрешению холста текущего профиля %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Невозможно обновить разрешение коллекции сцен при активных выводах.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Не удалось обновить разрешение коллекции сцен. Исходное разрешение неизвестно.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Сброс невозможен: изменение разрешения OBS не удалось.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Сброс невозможен: текущее разрешение уже является базовым для коллекции сцен.\"\nAddProfile.Title=\"Добавить профиль\"\nAddProfile.Text=\"Пожалуйста, введите имя профиля\"\nAddProfile.WizardCheckbox=\"Показать мастера автонастройки\"\nRenameProfile.Title=\"Переименовать профиль\"\nBasic.Main.MixerRename.Title=\"Переименование источника аудио\"\nBasic.Main.MixerRename.Text=\"Пожалуйста, введите имя источника аудио\"\nBasic.Main.PreviewDisabled=\"Предпросмотр в данный момент отключён\"\nBasic.SourceSelect=\"Создать/выбрать источник\"\nBasic.SourceSelect.CreateNew=\"Создать новый\"\nBasic.SourceSelect.AddExisting=\"Добавить существующий\"\nBasic.SourceSelect.AddVisible=\"Сделать источник видимым\"\nBasic.Main.Sources.Visibility=\"Видимость\"\nBasic.Main.Sources.VisibilityDescription=\"Управление видимостью «%1» на холсте\"\nBasic.Main.Sources.Lock=\"Заблокировать\"\nBasic.Main.Sources.LockDescription=\"Блокирует позицию и масштаб «%1» на холсте\"\nBasic.PropertiesWindow=\"Свойства «%1»\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (автовыбор: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Выбрать цвет\"\nBasic.PropertiesWindow.SelectFont=\"Выбрать шрифт\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Выбор шрифта\"\nBasic.PropertiesWindow.ConfirmTitle=\"Настройки изменены\"\nBasic.PropertiesWindow.Confirm=\"Имеются несохранённые изменения. Желаете сохранить их?\"\nBasic.PropertiesWindow.NoProperties=\"Нет доступных параметров\"\nBasic.PropertiesWindow.AddFiles=\"Добавить файлы\"\nBasic.PropertiesWindow.AddDir=\"Добавить папку\"\nBasic.PropertiesWindow.AddURL=\"Добавить путь/ссылку\"\nBasic.PropertiesWindow.AddEditableListDir=\"Добавить папку в «%1»\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Добавить файлы в «%1»\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Добавить запись в «%1»\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Изменить запись из «%1»\"\nBasic.PropertiesView.FPS.Simple=\"Простые значения частоты кадров\"\nBasic.PropertiesView.FPS.Rational=\"Рациональные значения частоты кадров\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Допустимые диапазоны частоты кадров:\"\nBasic.PropertiesView.UrlButton.Text=\"Открыть эту ссылку в браузере по умолчанию?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"Ссылка: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Открыть ссылку\"\nBasic.InteractionWindow=\"Взаимодействие с «%1»\"\nBasic.StatusBar.Reconnecting=\"Потеряно соединение, переподключение через %2 с (попытка %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Попытка подключения... (попытка %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Переподключение успешно\"\nBasic.StatusBar.Delay=\"Задержка (%1 с)\"\nBasic.StatusBar.DelayStartingIn=\"Задержка (начало через %1 с)\"\nBasic.StatusBar.DelayStoppingIn=\"Задержка (остановка через %1 с)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Задержка (остановка через %1 с, начало через %2 с)\"\nBasic.StatusBar.RecordingSavedTo=\"Запись сохранена в «%1»\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Буфер повтора записан в «%1»\"\nBasic.StatusBar.ScreenshotSavedTo=\"Снимок экрана сохранён как «%1»\"\nBasic.StatusBar.AutoRemuxedTo=\"Запись была ремультиплексирована в «%1»\"\nBasic.Filters=\"Фильтры\"\nBasic.Filters.AsyncFilters=\"Фильтры аудио/видео\"\nBasic.Filters.AudioFilters=\"Фильтры аудио\"\nBasic.Filters.EffectFilters=\"Фильтры эффектов\"\nBasic.Filters.Title=\"Фильтры для «%1»\"\nBasic.Filters.AddFilter.Title=\"Название фильтра\"\nBasic.Filters.AddFilter.Text=\"Пожалуйста, укажите название фильтра\"\nBasic.TransformWindow=\"Преобразование элемента сцены\"\nBasic.TransformWindow.Position=\"Позиция\"\nBasic.TransformWindow.PositionX=\"Позиция по горизонтали (X)\"\nBasic.TransformWindow.PositionY=\"Позиция по вертикали (Y)\"\nBasic.TransformWindow.Rotation=\"Вращение\"\nBasic.TransformWindow.Size=\"Размер\"\nBasic.TransformWindow.Width=\"Ширина\"\nBasic.TransformWindow.Height=\"Высота\"\nBasic.TransformWindow.Alignment=\"Выравнивание позиции\"\nBasic.TransformWindow.BoundsType=\"Тип ограничителя\"\nBasic.TransformWindow.BoundsAlignment=\"Выравнивание внутри ограничителя\"\nBasic.TransformWindow.Bounds=\"Размер ограничителя\"\nBasic.TransformWindow.BoundsWidth=\"Ширина ограничителя\"\nBasic.TransformWindow.BoundsHeight=\"Высота ограничителя\"\nBasic.TransformWindow.CropToBounds=\"Обрезать до ограничителя\"\nBasic.TransformWindow.Crop=\"Обрезка\"\nBasic.TransformWindow.CropLeft=\"Обрезать влево\"\nBasic.TransformWindow.CropRight=\"Обрезать вправо\"\nBasic.TransformWindow.CropTop=\"Обрезка сверху\"\nBasic.TransformWindow.CropBottom=\"Обрезать снизу\"\nBasic.TransformWindow.Alignment.TopLeft=\"Сверху слева\"\nBasic.TransformWindow.Alignment.TopCenter=\"Сверху по центру\"\nBasic.TransformWindow.Alignment.TopRight=\"Сверху справа\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Слева по центру\"\nBasic.TransformWindow.Alignment.Center=\"По центру\"\nBasic.TransformWindow.Alignment.CenterRight=\"Справа по центру\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Снизу слева\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Снизу по центру\"\nBasic.TransformWindow.Alignment.BottomRight=\"Снизу справа\"\nBasic.TransformWindow.BoundsType.None=\"Без границ\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Максимальный размер\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Масштабирование до внутренней границы\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Масштабирование до внешней границы\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Масштабирование до ширины границы\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Масштабирование до высоты границы\"\nBasic.TransformWindow.BoundsType.Stretch=\"Растянуть до границы\"\nBasic.TransformWindow.Title=\"Изменить преобразование для «%1»\"\nBasic.TransformWindow.NoSelectedSource=\"Источник не выбран\"\nBasic.Main.AddSourceHelp.Title=\"Невозможно добавить источник\"\nBasic.Main.AddSourceHelp.Text=\"Вы должны создать по крайней мере одну сцену, чтобы добавить источник.\"\nBasic.Main.Scenes=\"Сцены\"\nBasic.Main.Sources=\"Источники\"\nBasic.Main.Source=\"Источник\"\nBasic.Main.Controls=\"Управление\"\nBasic.Main.PreparingStream=\"Подготовка...\"\nBasic.Main.Connecting=\"Соединение...\"\nBasic.Main.StartRecording=\"Начать запись\"\nBasic.Main.StartReplayBuffer=\"Запустить повтор\"\nBasic.Main.SaveReplay=\"Сохранить повтор\"\nBasic.Main.StartStreaming=\"Начать трансляцию\"\nBasic.Main.StartBroadcast=\"Выйти в эфир\"\nBasic.Main.StartVirtualCam=\"Запуск виртуальной камеры\"\nBasic.Main.StopRecording=\"Остановить запись\"\nBasic.Main.PauseRecording=\"Приостановить запись\"\nBasic.Main.UnpauseRecording=\"Возобновить запись\"\nBasic.Main.SplitFile=\"Разделить файл записи\"\nBasic.Main.AddChapterMarker=\"Добавить метку главы (только гибридные MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Остановка записи...\"\nBasic.Main.StopReplayBuffer=\"Остановить повтор\"\nBasic.Main.StoppingReplayBuffer=\"Остановка повтора...\"\nBasic.Main.SetupBroadcast=\"Управлять трансляцией\"\nBasic.Main.StopStreaming=\"Остановить трансляцию\"\nBasic.Main.StopBroadcast=\"Завершить трансляцию\"\nBasic.Main.AutoStopEnabled=\"(Автоостановка)\"\nBasic.Main.StoppingStreaming=\"Остановка трансляции...\"\nBasic.Main.ForceStopStreaming=\"Остановить трансляцию (сброс задержки)\"\nBasic.Main.ShowContextBar=\"Показать панель источников\"\nBasic.Main.HideContextBar=\"Скрыть панель источников\"\nBasic.Main.StopVirtualCam=\"Остановить виртуальную камеру\"\nBasic.Main.Group=\"Группа %1\"\nBasic.Main.GroupItems=\"Группировать выбранные объекты\"\nBasic.Main.Ungroup=\"Разгруппировать\"\nBasic.Main.GridMode=\"Режим сетки\"\nBasic.Main.ListMode=\"Режим списка\"\nBasic.Main.VirtualCamConfig=\"Настроить виртуальную камеру\"\nBasic.VCam.VirtualCamera=\"Виртуальная камера\"\nBasic.VCam.OutputType=\"Тип вывода\"\nBasic.VCam.OutputSelection=\"Выбрать вывод\"\nBasic.VCam.OutputType.Program=\"Передача (по умолчанию)\"\nBasic.VCam.OutputSelection.NoSelection=\"Отсутствует выбор для этого типа вывода\"\nBasic.VCam.RestartWarning=\"Виртуальная камера будет перезапущена для применения этого изменения\"\nBasic.MainMenu.File=\"Файл (&F)\"\nBasic.MainMenu.File.Export=\"Экспортировать (&E)\"\nBasic.MainMenu.File.Import=\"Импорт (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Показать записи (&R)\"\nBasic.MainMenu.File.Remux=\"Ремультиплексировать записи видео (&M)\"\nBasic.MainMenu.File.Settings=\"Настройки (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Открыть папку настроек\"\nBasic.MainMenu.File.ShowProfileFolder=\"Открыть папку профиля\"\nBasic.MainMenu.File.ShowMissingFiles=\"Проверить пропавшие файлы\"\nBasic.MainMenu.File.Exit=\"Выход (&X)\"\nBasic.MainMenu.Edit=\"Правка (&E)\"\nBasic.MainMenu.Edit.Undo=\"Отменить (&U)\"\nBasic.MainMenu.Edit.Redo=\"Повторить (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Заблокировать предпросмотр (&L)\"\nBasic.MainMenu.Edit.Scale=\"Масштабирование предпросмотра (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"По размеру окна\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Холст (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Вывод (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Масштабируемый (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Увеличить масштаб\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Уменьшить масштаб\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Сбросить масштаб\"\nBasic.MainMenu.Edit.Transform=\"Преобразовать (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Изменить преобразование... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Копировать преобразование\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Вставить преобразование\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Сбросить преобразование (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Повернуть на 90 градусов по часовой\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Повернуть на 90 градусов против часовой\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Повернуть на 180 градусов\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Отразить горизонтально (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Отразить вертикально (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Подогнать по размеру экрана (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Растянуть на весь экран (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Разместить по центру экрана (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Выровнять по вертикали\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Выровнять по горизонтали\"\nBasic.MainMenu.Edit.Order=\"Порядок (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Перенести выше (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Перенести ниже (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"В самый верх (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"В самый низ (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Расширенные свойства звука (&A)\"\nBasic.MainMenu.View=\"Вид (&V)\"\nBasic.MainMenu.View.Toolbars=\"Панель инструментов (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"Док-панели инструментов\"\nBasic.MainMenu.View.ContextBar=\"Панель источников\"\nBasic.MainMenu.View.SourceIcons=\"Значки источников (&I)\"\nBasic.MainMenu.View.StatusBar=\"Строка состояния (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Полноэкранный режим\"\nBasic.MainMenu.View.ResetUI=\"&Сбросить интерфейс\"\nBasic.MainMenu.View.AlwaysOnTop=\"Поверх других окон (&A)\"\nBasic.MainMenu.View.SceneListMode=\"Режим списка сцен\"\nBasic.MainMenu.Docks=\"Док-панели (&D)\"\nBasic.MainMenu.Docks.ResetDocks=\"Сбросить док-панели (&R)\"\nBasic.MainMenu.Docks.LockDocks=\"Заблокировать док-панели (&L)\"\nBasic.MainMenu.Docks.SideDocks=\"Док-панели на всю высоту (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Пользовательские док-панели браузера... (&C)\"\nBasic.MainMenu.Import=\"Импорт…\"\nBasic.MainMenu.Export=\"Экспорт…\"\nBasic.MainMenu.SceneCollection=\"Коллекция сцен (&S)\"\nBasic.MainMenu.Profile=\"Профиль (&P)\"\nBasic.MainMenu.Profile.Import=\"Импортировать профиль\"\nBasic.MainMenu.Profile.Export=\"Экспортирование профиля\"\nBasic.MainMenu.SceneCollection.Import=\"Импортировать коллекцию сцен\"\nBasic.MainMenu.SceneCollection.Export=\"Экспортирование коллекции сцен\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Сбросить базовое разрешение\"\nBasic.MainMenu.SceneCollection.Migrate=\"Задать базовое разрешение\"\nBasic.MainMenu.Profile.Exists=\"Профиль уже существует\"\nBasic.MainMenu.Tools=\"Сервис (&T)\"\nBasic.MainMenu.Help=\"Справка (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Справочный портал (&P)\"\nBasic.MainMenu.Help.Website=\"Посетить сайт (&W)\"\nBasic.MainMenu.Help.Discord=\"Посетить Discord-сервер (&D)\"\nBasic.MainMenu.Help.WhatsNew=\"Новости\"\nBasic.MainMenu.Help.Logs=\"Файлы журналов (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Показать файлы журналов (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Отправить текущий файл журнала (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Отправить предыдущий файл журнала (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Просмотреть текущий журнал (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Примечания к выпуску\"\nBasic.MainMenu.Help.CheckForUpdates=\"Проверить обновления\"\nBasic.MainMenu.Help.Repair=\"Проверить целостность файлов\"\nBasic.MainMenu.Help.RestartSafeMode=\"Перезапустить в безопасном режиме\"\nBasic.MainMenu.Help.RestartNormal=\"Перезапустить в обычном режиме\"\nBasic.MainMenu.Help.CrashLogs=\"Отчёты об ошибках (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Показать отчёты об ошибках (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Отправить предыдущий отчёт об ошибке (&P)\"\nBasic.MainMenu.Help.About=\"О приложении (&A)\"\nBasic.Settings.ProgramRestart=\"Чтобы эти настройки вступили в силу, требуется перезапустить программу.\"\nBasic.Settings.ConfirmTitle=\"Подтверждение изменений\"\nBasic.Settings.Confirm=\"У вас есть несохранённые изменения. Сохранить их?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 контролирует некоторые настройки Вашей трансляции\"\nBasic.Settings.General=\"Общие\"\nBasic.Settings.General.Language=\"Язык\"\nBasic.Settings.General.Updater=\"Обновления\"\nBasic.Settings.General.UpdateChannel=\"Канал обновления\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Отключено)\"\nBasic.Settings.General.UpdateChannelDefault=\"(По умолчанию)\"\nBasic.Settings.General.EnableAutoUpdates=\"Проверять наличие обновлений при запуске\"\nBasic.Settings.General.OpenStatsOnStartup=\"Открывать окно статистики при запуске\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Не захватывать OBS при захвате экрана\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Эта настройка скрывает все окна OBS Studio, не являющиеся проектором, от захвата в OBS; она также влияет\\nна прочие программы захвата, используемые для конференции, общего доступа к рабочему столу, удалённой поддержки и снимков экрана.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Включение этого параметра скроет все окна OBS Studio, не являющиеся проектором, от захвата в OBS; он также повлияет на прочие программы захвата, используемые для конференции, общего доступа к рабочему столу, удалённой поддержки и снимков экрана.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Показывать окно подтверждения при запуске трансляции\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Показывать окно подтверждения при остановке трансляции\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Показывать окно подтверждения при остановке записи\"\nBasic.Settings.General.Projectors=\"Проекторы\"\nBasic.Settings.General.HideProjectorCursor=\"Скрыть курсор за проекторы\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Показывать проекторы поверх всех окон\"\nBasic.Settings.General.Snapping=\"Прилипание источника\"\nBasic.Settings.General.ScreenSnapping=\"Прилипание к краю экрана\"\nBasic.Settings.General.CenterSnapping=\"Прилипание к центру по горизонтали и вертикали\"\nBasic.Settings.General.SourceSnapping=\"Прилипание к другим источникам\"\nBasic.Settings.General.SnapDistance=\"Сила прилипания\"\nBasic.Settings.General.SpacingHelpers=\"Показывать направляющие с пикселями для выравнивания\"\nBasic.Settings.General.RecordWhenStreaming=\"Автоматически включать запись во время трансляции\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Продолжать запись по завершении трансляции\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Автоматически запускать буфер повтора во время трансляции\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Хранить буфер повтора активным по завершении трансляции\"\nBasic.Settings.General.SysTray=\"Область уведомлений\"\nBasic.Settings.General.SysTrayWhenStarted=\"Сворачивать в область уведомлений при запуске\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Всегда сворачивать в область уведомлений вместо панели задач\"\nBasic.Settings.General.SaveProjectors=\"Сохранять проекторы при выходе\"\nBasic.Settings.General.CloseExistingProjectors=\"Ограничить одним полноэкранным проектором на экран\"\nBasic.Settings.General.Preview=\"Предпросмотр\"\nBasic.Settings.General.OverflowHidden=\"Скрыть переполнение\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Переполнение всегда видно\"\nBasic.Settings.General.OverflowSelectionHidden=\"Показывать переполнение, даже если источник невидим\"\nBasic.Settings.General.Importers=\"Импортёры\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Поиск известных мест для коллекций сцен при импорте\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Переход к сцене по двойному щелчку\"\nBasic.Settings.General.StudioPortraitLayout=\"Включить портретное/вертикальное расположение\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Показывать отметки «Предпросмотр» и «Передача»\"\nBasic.Settings.General.Multiview=\"Мультипросмотр\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Переключение сцен щелчком мыши\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Показывать названия сцен\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Показывать зоны безопасности (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Разметка мультипросмотра\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Горизонтально, верх (8 сцен)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Горизонтально, низ (8 сцен)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Вертикально, слева (8 сцен)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Вертикально, справа (8 сцен)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Горизонтально, верх (18 сцен)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Горизонтально, верх (24 сцены)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Только сцены (4 сцены)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Только сцены (9 сцен)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Только сцены (16 сцен)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Только сцены (25 сцен)\"\nBasic.Settings.General.ChannelName.stable=\"Стабильный\"\nBasic.Settings.General.ChannelDescription.stable=\"Последний стабильный выпуск\"\nBasic.Settings.General.ChannelName.beta=\"Бета-версии / кандидаты\"\nBasic.Settings.General.ChannelDescription.beta=\"Предварительные нестабильные версии\"\nBasic.Settings.Appearance=\"Внешний вид\"\nBasic.Settings.Appearance.General=\"Общие\"\nBasic.Settings.Appearance.General.Theme=\"Тема\"\nBasic.Settings.Appearance.General.Variant=\"Стиль\"\nBasic.Settings.Appearance.General.NoVariant=\"Нет доступных стилей\"\nBasic.Settings.Appearance.FontScale=\"Размер шрифта\"\nBasic.Settings.Appearance.Density=\"Плотность\"\nBasic.Settings.Appearance.Classic=\"Классическая\"\nBasic.Settings.Appearance.Compact=\"Компактная\"\nBasic.Settings.Appearance.Normal=\"Обычная\"\nBasic.Settings.Appearance.Comfortable=\"Удобная\"\nBasic.Settings.Appearance.OptionsWarning=\"Некоторые настройки внешнего вида не доступны для этого стиля.\"\nBasic.Settings.Stream=\"Трансляция\"\nBasic.Settings.Stream.Destination=\"Назначение\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Использовать вход в аккаунт\"\nBasic.Settings.Stream.Custom.Username=\"Имя пользователя\"\nBasic.Settings.Stream.Custom.Password=\"Пароль\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: введите srp_username.\\nRTMP: введите имя пользователя.\\nSRT: не используется.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: введите srp_password.\\nRTMP: введите пароль.\\nSRT: введите пароль шифрования.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Включить режим проверки пропускной способности\"\nBasic.Settings.Stream.TTVAddon=\"Дополнения чата Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Нет\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV и FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Поток не настроен\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Открыть настройки\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL-адрес и ключ потока отсутствуют.\\n\\nОткройте настройки, чтобы ввести URL-адрес и ключ потока на вкладке «Трансляция».\"\nBasic.Settings.Stream.MissingUrl=\"Отсутствует URL потока.\\n\\nОткройте настройки, чтобы ввести URL на вкладке «Трансляция».\"\nBasic.Settings.Stream.MissingStreamKey=\"Ключ потока отсутствует.\\n\\nОткройте настройки, чтобы ввести ключ потока на вкладке «Трансляция».\"\nBasic.Settings.Stream.IgnoreRecommended=\"Не учитывать рекомендуемые настройки службы вещания\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Обойти рекомендуемые настройки\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Внимание! Использование значений свыше рекомендуемых может отрицательно повлиять на качество или стабильность потока.\\n\\nПродолжить?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Максимальный битрейт видео: %1 кбит/с\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Максимальный битрейт аудио: %1 кбит/с\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Максимальное разрешение: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Максимум частоты кадров: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Укажите пользовательский сервер...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Пользовательский сервер\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Включить %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Максимальная пропускная способность трансляции\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Авто\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Максимум видеодорожек\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Авто\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Включить дамп трансляции в FLV (использует простые настройки записи)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Перезаписывать файл конфигурации (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Включить перезапись конфигурации\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Многодорожечное видео\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Дополнительный холст\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Одновременная трансляция\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Одновременная трансляция позволяет кодировать и отправлять видео с разным качеством. <a href='https://obsproject.com/kb/whip-streaming-guide'>Подробности</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Всего слоёв\"\nBasic.Settings.Stream.AdvancedOptions=\"Расширенные настройки\"\nBasic.Settings.Output=\"Вывод\"\nBasic.Settings.Output.Format=\"Формат записи\"\nBasic.Settings.Output.Format.MKV=\"Видеоформат «Матрёшка» (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"Гибридный MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Гибридный MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Фрагментированный MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Фрагментированный MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Фрагментированные MOV производят запись кусочно и не требуют такой же финализации как традиционные файлы MOV.\\nЭто гарантирует, что файл будет проигрываться, даже если запись на диск будет прервана, например, в результате «синего экрана смерти» или потери питания.\\n\\nМожет быть несовместимо со всеми плеерами и редакторами. При необходимости используйте «Файл→ Перепаковать записи» для преобразования файла в более совместимый формат.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Фрагментированные MP4 производят запись кусочно и не требуют такой же доработки как традиционные файлы MP4.\\nЭто гарантирует, что файл будет проигрываться, даже если запись на диск будет прервана, например, в результате «синего экрана смерти» или потери питания.\\n\\nМожет быть несовместимо со всеми проигрывателями и редакторами. При необходимости используйте «Файл→ Перепаковать записи» для преобразования файла в более совместимый формат.\"\nBasic.Settings.Output.Encoder.Video=\"Кодировщик видео\"\nBasic.Settings.Output.Encoder.Audio=\"Кодировщик звука\"\nBasic.Settings.Output.SelectDirectory=\"Выбрать каталог записи\"\nBasic.Settings.Output.DynamicBitrate=\"Регулировать битрейт в зависимости от перегруженности сети\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Регулировать битрейт в зависимости от перегруженности сети (бета)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Вместо пропуска кадров будет задействовано динамическое изменение битрейта на лету, чтобы уменьшить нагрузку сети.\\n\\nОбратите внимание, что это может увеличить задержку для зрителей в случае внезапного забивания канала.\\nПосле уменьшения битрейта может потребоваться несколько минут для его восстановления.\\n\\nВ настоящее время поддерживается только для RTMP.\"\nBasic.Settings.Output.Mode=\"Режим вывода\"\nBasic.Settings.Output.Mode.Simple=\"Простой\"\nBasic.Settings.Output.Mode.Adv=\"Расширенный\"\nBasic.Settings.Output.Mode.FFmpeg=\"Вывод FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Включить буфер повтора\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Максимальная длительность повтора\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Максимум памяти\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Расчётное потребление памяти: %1 МБ\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Предупреждение: Расчётное использование памяти %1 Мб больше, чем рекомендуемый максимум — %2 МБ\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Приблизительная оценка расхода памяти невозможна. Пожалуйста, укажите ограничение расхода памяти вручную.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Приставка имени файла повтора\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Окончание\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Буфер повтора нельзя использовать, если типом записи является пользовательский вывод (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Путь записи\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Качество записи\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"То же, что у трансляции\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Высокое качество, средний размер файла\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Неотличимое качество, большой размер файла\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Без потери качества, чрезвычайно большой размер файла\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Предупреждение: битрейт потокового видео будет установлен на %1, что является верхним пределом для текущей потоковой службы.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Предупреждение: битрейт потокового аудио будет установлен на %1, что является верхним пределом для текущей потоковой службы.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Предупреждение: Записи нельзя приостановить, если кодировщик записи установлен на «(Использовать кодировщик потока)».\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Предупреждение: Текущий выбранный формат записи несовместим с выбранным кодировщиком потока.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Предупреждение: Запись с программным кодировщиком в качестве, отличным от качества трансляции, потребует дополнительной нагрузки на процессор, если записывать и транслировать одновременно.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Предупреждение: Качество без потерь создаёт чрезвычайно большие файлы! Такое качество может использовать свыше 7 гигабайт дискового пространства в минуту при высоких разрешении и частоте кадров. Оно не рекомендуется для долгой записи, если у вас нет очень много места на диске.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Уверены, что хотите записывать без потери качества?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Предупреждение о качестве без потерь!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Программный (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Аппаратный (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Аппаратный (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Аппаратный (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Аппаратный (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Аппаратный (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Аппаратный (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Аппаратный (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Аппаратный (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Аппаратный (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Аппаратный (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Программный (x264 с низкой нагрузкой на ЦП, увеличивает размер файла)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (по умолчанию)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Дорожка Twitch VOD (использует дорожку 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Звуковая дорожка\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Несовместимое разрешение или частота кадров\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Эта потоковая служба не поддерживает ваше текущее выходное разрешение и/или частоту кадров. Они будут изменены на наиболее близкое совместимое значение:\\n\\n%1\\n\\nХотите продолжить?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Разрешение: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Частота кадров: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Несовместимый кодировщик\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Служба вещания «%1» не поддерживает кодировщик «%2». Кодировщик будет изменён на «%3».\\n\\nХотите продолжить?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Потоковая служба «%1» не поддерживает кодировщики «%2» и «%3». Эти кодировщики будут сменены на «%4» и «%5».\\n\\nХотите продолжить?\"\nBasic.Settings.Output.VideoBitrate=\"Битрейт видео\"\nBasic.Settings.Output.AudioBitrate=\"Битрейт аудио\"\nBasic.Settings.Output.Reconnect=\"Автоматическое переподключение\"\nBasic.Settings.Output.RetryDelay=\"Задержка повтора\"\nBasic.Settings.Output.MaxRetries=\"Число попыток\"\nBasic.Settings.Output.Advanced=\"Включить дополнительные настройки кодировщика\"\nBasic.Settings.Output.EncoderPreset=\"Предустановка кодировщика\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (низкая нагрузка на ЦП, низшее качество)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (по умолчанию) (средняя нагрузка на ЦП, стандартное качество)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (высокая нагрузка на ЦП, высокое качество)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Дополнительные настройки кодировщика\"\nBasic.Settings.Output.CustomMuxerSettings=\"Свои настройки паковщика\"\nBasic.Settings.Output.NoSpaceFileName=\"Генерировать имя файла без пробела\"\nBasic.Settings.Output.Adv.Rescale=\"Масштабирование на выводе\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Нет\"\nBasic.Settings.Output.Adv.AudioTrack=\"Звуковая дорожка\"\nBasic.Settings.Output.Adv.Streaming=\"Трансляция\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Настройки трансляции\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Дорожка 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Дорожка 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Дорожка 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Дорожка 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Дорожка 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Дорожка 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Дорожка VOD на Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"Настройки кодировщика\"\nBasic.Settings.Output.Adv.Recording=\"Запись\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Настройки записи\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Тип записи\"\nBasic.Settings.Output.Adv.Recording.Type=\"Тип\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Обычный\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Пользовательский вывод (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Использовать кодировщик потока)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Формат имени файла\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Заменять, если файл уже существует\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Пользовательский вывод (FFmpeg) предоставляется без гарантии работоспособности.\\nРекомендуется использовать «Обычный» тип записи.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Тип вывода FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Вывод по ссылке\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Вывод в файл\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Общие форматы записи\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Все файлы\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Путь к файлу или ссылка\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Формат контейнера\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Звук\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Видео\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Формат по умолчанию\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Описание формата контейнера\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Кодеки аудио и видео будут угаданы по URL-адресу или пути к файлу\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Кодировщик по умолчанию\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Отключить кодировщик\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Кодировщик видео\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Настройки кодировщика видео (если есть)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Кодировщик аудио\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Настройки кодировщика аудио (если есть)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Настройки паковщика (если есть)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Интервал ключевых кадров (кадры)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Показать все кодеки (в т.ч. потенциально несовместимые)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Настройки FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Авторазделение на отрезки\"\nBasic.Settings.Output.SplitFile.TypeTime=\"По времени\"\nBasic.Settings.Output.SplitFile.TypeSize=\"По размеру\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Вручную\"\nBasic.Settings.Output.SplitFile.Time=\"Время отрезка\"\nBasic.Settings.Output.SplitFile.Size=\"Размер отрезка\"\nScreenshot=\"Сделать снимок вывода\"\nScreenshot.SourceHotkey=\"Сделать снимок выбранного источника\"\nScreenshot.StudioProgram=\"Сделать снимок (передачи)\"\nScreenshot.Preview=\"Сделать снимок (предпросмотра)\"\nScreenshot.Scene=\"Сделать снимок (сцены)\"\nScreenshot.Source=\"Сделать снимок (источника)\"\nFilenameFormatting.TT.CCYY=\"Год, четыре цифры\"\nFilenameFormatting.TT.YY=\"Год, последние две цифры (00-99)\"\nFilenameFormatting.TT.MM=\"Месяц в виде десятичного числа (01-12)\"\nFilenameFormatting.TT.DD=\"День месяца с добавлением нуля (01-31)\"\nFilenameFormatting.TT.hh=\"Час в формате 24 часов (00-23)\"\nFilenameFormatting.TT.mm=\"Минута (00-59)\"\nFilenameFormatting.TT.ss=\"Секунда (00-59)\"\nFilenameFormatting.TT.Percent=\"Символ %\"\nFilenameFormatting.TT.a=\"Краткое название дня недели\"\nFilenameFormatting.TT.A=\"Полное название дня недели\"\nFilenameFormatting.TT.b=\"Краткое название месяца\"\nFilenameFormatting.TT.B=\"Полное название месяца\"\nFilenameFormatting.TT.d=\"День месяца с добавлением нуля (01-31)\"\nFilenameFormatting.TT.H=\"Час в формате 24 часов (00-23)\"\nFilenameFormatting.TT.I=\"Час в формате 12 часов (01-12)\"\nFilenameFormatting.TT.m=\"Месяц в виде десятичного числа (01-12)\"\nFilenameFormatting.TT.M=\"Минута (00-59)\"\nFilenameFormatting.TT.p=\"Обозначение AM или PM\"\nFilenameFormatting.TT.s=\"Время в секундах с начала эпохи Unix\"\nFilenameFormatting.TT.S=\"Секунда (00-59)\"\nFilenameFormatting.TT.y=\"Год, последние две цифры (00-99)\"\nFilenameFormatting.TT.Y=\"Год\"\nFilenameFormatting.TT.z=\"Смещение ISO 8601 от UTC в часовом поясе\"\nFilenameFormatting.TT.Z=\"Название часового пояса или аббревиатура\"\nFilenameFormatting.TT.FPS=\"Частота кадров\"\nFilenameFormatting.TT.CRES=\"Разрешение предпросмотра (холст)\"\nFilenameFormatting.TT.ORES=\"Разрешение выхода (масштабное)\"\nFilenameFormatting.TT.VF=\"Формат видео\"\nBasic.Settings.Video=\"Видео\"\nBasic.Settings.Video.Adapter=\"Видеоадаптер\"\nBasic.Settings.Video.BaseResolution=\"Разрешение предпросмотра (холст)\"\nBasic.Settings.Video.ScaledResolution=\"Разрешение выхода (масштабное)\"\nBasic.Settings.Video.DownscaleFilter=\"Фильтр масштабирования\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Разрешения совпадают, уменьшение масштаба не требуется]\"\nBasic.Settings.Video.FPS=\"Частота кадров\"\nBasic.Settings.Video.FPSCommon=\"Общие значения частоты кадров\"\nBasic.Settings.Video.FPSInteger=\"Целое значение частоты кадров\"\nBasic.Settings.Video.FPSFraction=\"Дробное значение частоты кадров\"\nBasic.Settings.Video.Numerator=\"Числитель\"\nBasic.Settings.Video.Denominator=\"Знаменатель\"\nBasic.Settings.Video.Renderer=\"Отрисовщик\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Экспериментально)\"\nBasic.Settings.Video.InvalidResolution=\"Недопустимое значение разрешения. Должно быть [ширина]x[высота] (например, 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Видео выход запущен. Пожалуйста, отключите все выходы, чтобы изменить настройки видео.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Билинейный (быстрейший, но размытый при масштабировании)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Бикубический (чёткое масштабирование, 16 выборок)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Метод Ланцоша (чёткое масштабирование, 36 выборок)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Область (взвешенная сумма, 4/6/9 выборок)\"\nBasic.Settings.Audio=\"Аудио\"\nBasic.Settings.Audio.SampleRate=\"Частота дискретизации\"\nBasic.Settings.Audio.Channels=\"Каналы\"\nBasic.Settings.Audio.Meters=\"Измерители\"\nBasic.Settings.Audio.MeterDecayRate=\"Скорость спада\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Быстро\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Средне (PPM типа I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Медленно (PPM типа II)\"\nBasic.Settings.Audio.PeakMeterType=\"Тип измерителя пиков\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Упрощённый\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Точный (повышенная нагрузка на ЦП)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ВНИМАНИЕ: Объёмный звук включён.\"\nBasic.Settings.Audio.MultichannelWarning=\"При трансляции, удостоверьтесь что ваша служба вещания одновременно поддерживает приём и воспроизведение объёмного звука. Например, Facebook 360 Live полностью поддерживает объёмный звук; YouTube Live поддерживает приём звука 5.1 (и его воспроизведение на телевизорах).\\n\\nOBS аудио фильтры поддерживают объёмный звук, но его поддержка не гарантирована в плагинах VST.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Включить объёмный звук?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Хотите включить объёмный звук?\"\nBasic.Settings.Audio.Devices=\"Общие устройства аудио\"\nBasic.Settings.Audio.DesktopDevice=\"Звук рабочего стола\"\nBasic.Settings.Audio.DesktopDevice2=\"Звук рабочего стола 2\"\nBasic.Settings.Audio.AuxDevice=\"Микрофон/дополнительный звук\"\nBasic.Settings.Audio.AuxDevice2=\"Микрофон/дополнительный звук 2\"\nBasic.Settings.Audio.AuxDevice3=\"Микрофон/дополнительный звук 3\"\nBasic.Settings.Audio.AuxDevice4=\"Микрофон/дополнительный звук 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Отключать микрофон по нажатии\"\nBasic.Settings.Audio.PushToMuteDelay=\"Задержка отключения звука по нажатии\"\nBasic.Settings.Audio.EnablePushToTalk=\"Включать микрофон по нажатии\"\nBasic.Settings.Audio.PushToTalkDelay=\"Задержка включения микрофона\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Устройство не подключено или недоступно]\"\nBasic.Settings.Audio.Disabled=\"Отключено\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Режим буфера аудио с низкой задержкой (для выходов Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ВНИМАНИЕ: Включена буферизация аудио с низкой задержкой.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Режим буфера аудио с низкой задержкой может привести к искажению или прекращению воспроизведения из некоторых источников.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Включить режим буфера аудио с низкой задержкой?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Уверены, что хотите включить режим буфера аудио с низкой задержкой?\"\nBasic.Settings.Accessibility=\"Спец. возможности\"\nBasic.Settings.Accessibility.ColorOverrides=\"Использовать другие цвета\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Граница источника (выделение)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Граница источника (обрезка)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Граница источника (поднятие)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Полоса громкости микшера (от -60 до -20 дБ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Полоса громкости микшера (от -20 до -9 дБ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Полоса громкости микшера (от -9 до 0 дБ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Полоса громкости микшера (от -60 до -20 дБ) (активная)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Полоса громкости микшера (от -20 до -9 дБ) (активная)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Полоса громкости микшера (от -9 до 0 дБ) (активная)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Предустановка цвета\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"По умолчанию\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Пользовательский\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Цвета для дальтоников\"\nBasic.Settings.Advanced=\"Расширенные\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Показывать предупреждение об активных операциях вывода при выходе\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Приоритет процесса\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Высокий\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Выше среднего\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Средний\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Ниже среднего\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Низкий\"\nBasic.Settings.Advanced.FormatWarning=\"Предупреждение: Цветовые форматы, отличные от NV12/P010, предназначены в первую очередь для записи и не рекомендуются при трансляции. При трансляции может увеличиться загрузка процессора из-за преобразования цветового формата.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Предупреждение: Высокоточные форматы чаще используются с цветовыми пространствами HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Предупреждение: Rec. 2100 должен использовать формат с большей точностью.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Цветовой формат\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 бит, 4:2:0, 2 уровня)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 бит, 4:2:0, 3 уровня)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 бит, 4:4:4, 3 уровня)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 бит, 4:2:0, 2 уровня)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 бит, 4:2:0, 3 уровня)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 бит, 4:2:2, 2 уровня)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 бит, 4:4:4, 2 уровня)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 бит)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Цветовое пространство\"\nBasic.Settings.Advanced.Video.ColorRange=\"Цветовой диапазон\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Ограниченный\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Полный\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Уровень белого SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Номинальный уровень пика HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Устройство прослушивания\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"По умолчанию\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Отключить приглушение звуков Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Задержка потока\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Длительность\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Сохранить точку отсечки (увеличить задержку) при переподключении\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Расчётное потребление памяти: %1 МБ\"\nBasic.Settings.Advanced.Network=\"Сеть\"\nBasic.Settings.Advanced.Network.Disabled=\"Текущий выбранный протокол потокового вещания не поддерживает изменение сетевых настроек.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Привязать к IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Семейство IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Включить сетевую оптимизацию\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Включить прореживание пакетов (TCP pacing)\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Снижает негативное воздействие вывода RTMP на другие сетевые приложения, чувствительные к задержкам, путём регулировки частоты передачи.\\nМожет увеличить риск потери кадров при нестабильном соединении.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Поведение фокуса горячих клавиш\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Никогда не отключать горячие клавиши\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Отключать горячие клавиши, когда главное окно в фокусе\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Отключать горячие клавиши, когда главное окно вне фокуса\"\nBasic.Settings.Advanced.AutoRemux=\"Автоматически ремультиплексировать в %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(записывать в mkv)\"\nBasic.AdvAudio=\"Расширенные свойства звука\"\nBasic.AdvAudio.ActiveOnly=\"Только активные источники\"\nBasic.AdvAudio.Name=\"Название\"\nBasic.AdvAudio.Volume=\"Громкость\"\nBasic.AdvAudio.VolumeSource=\"Громкость звука для «%1»\"\nBasic.AdvAudio.Mono=\"Моно\"\nBasic.AdvAudio.MonoSource=\"Смешение в моно для «%1»\"\nBasic.AdvAudio.Balance=\"Баланс\"\nBasic.AdvAudio.BalanceSource=\"Баланс для «%1»\"\nBasic.AdvAudio.SyncOffset=\"Сдвиг синхронизации\"\nBasic.AdvAudio.SyncOffsetSource=\"Сдвиг синхронизации для «%1»\"\nBasic.AdvAudio.Monitoring=\"Прослушивание аудио\"\nBasic.AdvAudio.Monitoring.None=\"Отключить прослушивание\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Только прослушивание (заглушить вывод)\"\nBasic.AdvAudio.Monitoring.Both=\"Прослушивание и вывод\"\nBasic.AdvAudio.MonitoringSource=\"Прослушивание аудио для «%1»\"\nBasic.AdvAudio.AudioTracks=\"Дорожки\"\nBasic.Settings.Hotkeys=\"Горячие клавиши\"\nBasic.Settings.Hotkeys.Pair=\"Сочетания клавиш вместе с «%1» действуют как переключатели\"\nBasic.Settings.Hotkeys.Filter=\"Фильтр\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Фильтр горячих клавиш\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Эта горячая клавиша используется одним или несколькими другими действиями, нажмите для отображения конфликтов\"\nBasic.Settings.Hotkeys.PleaseWait=\"Загрузка горячих клавиш, пожалуйста, подождите...\"\nBasic.Hotkeys.SelectScene=\"Перейти на сцену\"\nBasic.SystemTray.Show=\"Показать\"\nBasic.SystemTray.Hide=\"Скрыть\"\nBasic.SystemTray.Message.Reconnecting=\"Соединение разорвано. Переподключение...\"\nHotkeys.Insert=\"Вставить\"\nHotkeys.Delete=\"Удалить\"\nHotkeys.Home=\"Главная страница\"\nHotkeys.End=\"В конец\"\nHotkeys.PageUp=\"Предыдущий\"\nHotkeys.PageDown=\"Следующий\"\nHotkeys.Backspace=\"←\"\nHotkeys.Tab=\"↹\"\nHotkeys.Print=\"Print Screen\"\nHotkeys.Pause=\"Pause Break\"\nHotkeys.Left=\"Стрелка влево\"\nHotkeys.Right=\"Стрелка вправо\"\nHotkeys.Up=\"Стрелка вверх\"\nHotkeys.Down=\"Стрелка вниз\"\nHotkeys.Windows=\"⊞ Win\"\nHotkeys.Menu=\"Меню\"\nHotkeys.Space=\"Пробел\"\nHotkeys.NumpadNum=\"%1 (циф. кл.)\"\nHotkeys.NumpadMultiply=\"* (циф. кл.)\"\nHotkeys.NumpadDivide=\"/ (циф. кл.)\"\nHotkeys.NumpadAdd=\"+ (циф. кл.)\"\nHotkeys.NumpadSubtract=\"- (циф. кл.)\"\nHotkeys.NumpadDecimal=\". (циф. кл.)\"\nHotkeys.MouseButton=\"Кнопка мыши %1\"\nMute=\"Отключить звук\"\nUnmute=\"Включить звук\"\nPush-to-mute=\"Отключать звук по нажатии\"\nPush-to-talk=\"Включать звук по нажатии\"\nSceneItemShow=\"Показать «%1»\"\nSceneItemHide=\"Скрыть «%1»\"\nOutputWarnings.NoTracksSelected=\"Вы должны выбрать хотя бы одну звуковую дорожку\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Ошибка настроек вывода\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Во всех выводах должна быть выбрана хотя бы одна звуковая дорожка.\"\nOutputWarnings.MP4Recording=\"Внимание: записи, сохраняемые в форматах MP4 и MOV, могут стать нечитаемыми, если запись прервётся (к примеру, из-за синего экрана смерти, потери питания и т. д.). Если вы хотите записывать несколько аудиодорожек, рекомендуется использовать MKV и по завершении записи конвертировать её в MP4 или MOV («Файл» → «Ремультиплексировать записи видео»).\"\nOutputWarnings.CannotPause=\"Предупреждение: Записи нельзя приостановить, если кодировщик записи установлен на «(Использовать кодировщик потока)»\"\nOutputWarnings.CodecIncompatible=\"Выбранный кодировщик аудио или видео был сброшен из-за несовместимости. Пожалуйста, выберите совместимый кодировщик из списка.\"\nCodecCompat.Incompatible=\"(Несовместим с %1)\"\nCodecCompat.CodecPlaceholder=\"Выбрать кодировщик…\"\nCodecCompat.ContainerPlaceholder=\"Выбрать формат…\"\nCodecCompat.CodecMissingOnExit.Title=\"Кодировщик не выбран\"\nCodecCompat.CodecMissingOnExit.Text=\"По крайней мере, один кодировщик видео или аудио не настроен. Пожалуйста, обязательно выберите кодировщики как для записи, так и для трансляции.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Формат не выбран\"\nCodecCompat.ContainerMissingOnExit.Text=\"Формат записи не был выбран. Пожалуйста, выберите формат записи, совместимый с выбранным потоковым кодировщиком.\"\nFinalScene.Title=\"Удалить сцену\"\nFinalScene.Text=\"Нельзя удалить последнюю сцену.\"\nNoSources.Title=\"Нет источников\"\nNoSources.Text=\"Похоже, вы ещё не добавили ни одного источника. Вы будете выводить только пустой экран. Уверены, что хотите этого?\"\nNoSources.Text.AddSource=\"Вы можете добавить источники, нажав значок «+» под доком «Источники» в главном окне в любое время.\"\nNoSources.Label=\"У вас нет источников.\\nНажмите кнопку + ниже\\nили щёлкните правой кнопкой здесь для добавления.\"\nChangeBG=\"Установить цвет\"\nCustomColor=\"﻿Пользовательский цвет\"\nBrowserSource.EnableHardwareAcceleration=\"Включить аппаратное ускорение для источника «Браузер»\"\nAbout=\"О приложении\"\nAbout.Info=\"OBS Studio — это бесплатная программа с открытым исходным кодом для записи видео и прямых трансляций.\"\nAbout.Donate=\"Сделать вклад\"\nAbout.GetInvolved=\"Принять участие\"\nAbout.Authors=\"Авторы\"\nAbout.License=\"Лицензия\"\nAbout.Error=\"Ошибка! Файл нельзя прочесть.\\n\\nПерейти к: %1\"\nAbout.Contribute=\"Поддержать проект OBS\"\nAddUrl.Title=\"Добавление источника по ссылке\"\nAddUrl.Text=\"Вы перетащили ссылку в OBS. Это автоматически добавит ссылку в качестве источника. Продолжить?\"\nAddUrl.Text.Url=\"Ссылка: %1\"\nResizeOutputSizeOfSource=\"Подогнать вывод (к размеру источника)\"\nResizeOutputSizeOfSource.Text=\"Разрешения холста и вывода будут изменены до размера выбранного источника.\"\nResizeOutputSizeOfSource.Continue=\"Хотите продолжить?\"\nPreviewTransition=\"Посмотреть переход\"\nImporter=\"Импортёр коллекции сцен\"\nImporter.SelectCollection=\"Выберите коллекцию сцен\"\nImporter.Collection=\"Коллекция сцен\"\nImporter.HelpText=\"Добавьте файлы в это окно для импорта коллекций из OBS или других поддерживаемых программ.\"\nImporter.Path=\"Путь коллекции\"\nImporter.Program=\"Обнаруженное приложение\"\nImporter.AutomaticCollectionPrompt=\"Автоматический поиск коллекций сцен\"\nImporter.AutomaticCollectionText=\"OBS может автоматически находить импортируемые коллекции сцен из поддерживаемых сторонних программ. Хотели бы вы, чтобы OBS автоматически находил для вас коллекции?\\n\\nВы можете изменить это позже в «Настройки > Основные > Импортёры».\"\nImporter.SelectFile=\"Обзор…\"\nRestart=\"Перезапуск\"\nNeedsRestart=\"Требуется перезапуск OBS Studio. Хотите перезапустить сейчас?\"\nLoadProfileNeedsRestart=\"Профиль содержит настройки, требующие перезапуска OBS:\\n%1\\n\\nВы хотите перезапустить OBS, чтобы эти настройки вступили в силу?\"\nContextBar.NoSelectedSource=\"Источник не выбран\"\nContextBar.MediaControls.PlayMedia=\"Воспроизвести медиа\"\nContextBar.MediaControls.PauseMedia=\"Приостановить медиа\"\nContextBar.MediaControls.StopMedia=\"Остановить медиа\"\nContextBar.MediaControls.RestartMedia=\"Перезапустить медиа\"\nContextBar.MediaControls.PlaylistNext=\"Следующий в плейлисте\"\nContextBar.MediaControls.PlaylistPrevious=\"Предыдущий в плейлисте\"\nContextBar.MediaControls.BlindSeek=\"Виджет перемотки медиа\"\nYouTube.Auth.Ok=\"Вход успешно завершён.\\nТеперь вы можете закрыть эту страницу.\"\nYouTube.Auth.NoCode=\"Процесс входа не был завершён.\"\nYouTube.Auth.NoChannels=\"Не найдено доступных каналов в выбранном аккаунте\"\nYouTube.Auth.WaitingAuth.Title=\"Авторизация пользователя YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Пожалуйста, завершите вход во внешнем браузере.<br>Если внешний браузер не открывается, перейдите по этой ссылке и завершите вход:<br>%1\"\nYouTube.AuthError.Text=\"Не удалось получить информацию о канале: %1.\"\nYouTube.Actions.WindowTitle=\"Настройка трансляции YouTube - Канал: %1\"\nYouTube.Actions.CreateNewEvent=\"Создать новую трансляцию\"\nYouTube.Actions.ChooseEvent=\"Выбрать существующую трансляцию\"\nYouTube.Actions.Title=\"Название*\"\nYouTube.Actions.MyBroadcast=\"Моя трансляция\"\nYouTube.Actions.Description=\"Описание\"\nYouTube.Actions.Privacy=\"Конфиденциальность*\"\nYouTube.Actions.Privacy.Private=\"Закрытое\"\nYouTube.Actions.Privacy.Public=\"Видно всем (публичное)\"\nYouTube.Actions.Privacy.Unlisted=\"Доступ по ссылке (Unlisted)\"\nYouTube.Actions.Category=\"Категория\"\nYouTube.Actions.Thumbnail=\"Миниатюра\"\nYouTube.Actions.Thumbnail.SelectFile=\"Выбрать файл...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Файл не выбран\"\nYouTube.Actions.Thumbnail.ClearFile=\"Очистить\"\nYouTube.Actions.MadeForKids=\"Это видео предназначено для детей?*\"\nYouTube.Actions.MadeForKids.Yes=\"Да, это сделано для детей\"\nYouTube.Actions.MadeForKids.No=\"Нет, это не для детей\"\nYouTube.Actions.AdditionalSettings=\"Дополнительные настройки\"\nYouTube.Actions.Latency=\"Задержка\"\nYouTube.Actions.Latency.Normal=\"Нормально\"\nYouTube.Actions.Latency.Low=\"Низко\"\nYouTube.Actions.Latency.UltraLow=\"Очень низко\"\nYouTube.Actions.EnableAutoStart=\"Включить автозапуск\"\nYouTube.Actions.EnableAutoStop=\"Включить автоостановку\"\nYouTube.Actions.AutoStartStop.TT=\"Указывает, должна ли эта запланированная трансляция запускаться автоматически\"\nYouTube.Actions.EnableDVR=\"Включить DVR\"\nYouTube.Actions.360Video=\"Панорамное видео 360°\"\nYouTube.Actions.ScheduleForLater=\"Запланировать на потом\"\nYouTube.Actions.RememberSettings=\"Запомнить эти настройки\"\nYouTube.Actions.Create_Ready=\"Создать трансляцию\"\nYouTube.Actions.Create_GoLive=\"Создать трансляцию и начать эфир\"\nYouTube.Actions.Choose_Ready=\"Выбрать трансляцию\"\nYouTube.Actions.Choose_GoLive=\"Выбрать трансляцию и начать эфир\"\nYouTube.Actions.Create_Schedule=\"Запланировать трансляцию\"\nYouTube.Actions.Create_Schedule_Ready=\"Назначить и выбрать трансляцию\"\nYouTube.Actions.Dashboard=\"Открыть YouTube Studio\"\nYouTube.Actions.Error.Title=\"Ошибка создания трансляции\"\nYouTube.Actions.Error.Text=\"Ошибка доступа к YouTube «%1».<br/>Подробное описание ошибки можно найти на <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Нет доступа к YouTube. Пожалуйста, проверьте подключение к сети или доступ к серверу YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Ошибка создания трансляции «%1».<br/>Подробное описание ошибки можно найти на <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Не удалось создать трансляцию. Пожалуйста, отсоедините и повторно присоедините свой аккаунт.\"\nYouTube.Actions.Error.YouTubeApi=\"Ошибка YouTube API. Более подробную информацию см. в файле журнала.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Выбранная трансляция не найдена.\"\nYouTube.Actions.Error.FileMissing=\"Выбранный файл не существует.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Не удалось открыть выбранный файл.\"\nYouTube.Actions.Error.FileTooLarge=\"Выбранный файл слишком большой (предел: 2 МБ).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Переход трансляции не удался: %1<br/><br/>Если ошибка повторится <a href='https://studio.youtube.com/video/%2/livestreaming'>откройте трансляцию в YouTube Studio</a> и попробуйте вручную.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Трансляция переходит к проверочному этапу, это может занять некоторое время. Пожалуйста, попробуйте снова через 10-30 секунд.\"\nYouTube.Actions.EventsLoading=\"Загрузка списка событий...\"\nYouTube.Actions.EventCreated.Title=\"Событие создано\"\nYouTube.Actions.EventCreated.Text=\"Событие успешно создано.\"\nYouTube.Actions.Stream=\"Трансляция\"\nYouTube.Actions.Stream.ScheduledFor=\"Запланировано на %1\"\nYouTube.Actions.Stream.Resume=\"Возобновить прерванный поток\"\nYouTube.Actions.Stream.YTStudio=\"Создано автоматически YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Создаётся новая трансляция. Пожалуйста, подождите...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Требуется запустить вручную\"\nYouTube.Actions.AutoStartStreamingWarning=\"Автозапуск отключён для этого события, нажмите «Выйти в эфир», чтобы начать трансляцию.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Вы не сможете переподключиться.<br>Ваша прямая трансляция будет прекращена.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Прямые эфиры отключены для данного канала YouTube.<br/><br/>Смотрите <a href='https://www.youtube.com/features'>youtube.com/features</a> для подробной информации.\"\nYouTube.Errors.livePermissionBlocked=\"Прямая трансляция недоступна на выбранном канале YouTube.<br/>Пожалуйста, обратите внимание, что может потребоваться до 24 часов для того, чтобы прямые трансляции стали доступны после их включения в настройках канала.<br/><br/>Смотрите <a href='https://www.youtube.com/features'>youtube.com/features</a> для подробностей.\"\nYouTube.Errors.errorExecutingTransition=\"Не удалось выполнить переход из-за внутренней ошибки. Пожалуйста, повторите попытку через несколько секунд.\"\nYouTube.Errors.errorStreamInactive=\"YouTube не получает данные для вашего потока. Пожалуйста, проверьте настройки и повторите попытку.\"\nYouTube.Errors.invalidTransition=\"Попытка перехода была недействительной. Это может произойти из-за потока, не завершившего предыдущий переход. Пожалуйста, подождите несколько секунд и повторите попытку.\"\nYouTube.DocksRemoval.Title=\"Очистить устаревшие доки браузера YouTube\"\nYouTube.DocksRemoval.Text=\"Эти доки браузера будут удалены как устаревшие:\\n\\n%1\\nИспользуйте вместо них «Доки / Комнату управления эфиром YouTube».\"\nConfigDownload.WarningMessageTitle=\"Внимание\"\nFailedToStartStream.MissingConfigURL=\"Для текущей службы нет доступного URL-адреса\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Пользовательский URL-адрес RTMP не указан\"\nFailedToStartStream.InvalidCustomConfig=\"Неверная пользовательская конфигурация\"\nFailedToStartStream.MissingCanvas=\"Отсутствует настроенный дополнительный холст\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Не удалось создать сервис многодорожечного видео\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Не удалось создать многодорожечный RTMP-вывод видео\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC недоступен.\\n\\nНе удалось найти тип кодировщика «%1»\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Не удалось создать кодировщик видео «%1» (тип: «%2»)\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Не удалось получить сведения о видео OBS во время создания кодировщика «%1» (тип: «%2»)\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Не удалось создать кодировщик звука\"\nFailedToStartStream.NoRTMPURLInConfig=\"В конфигурации отсутствует URL-адрес целевого потока RTMP(S) \"\nFailedToStartStream.FallbackToDefault=\"Не удалось начать трансляцию используя %1; хотите ли Вы попробовать снова используя одиночные настройки кодирования?\"\nFailedToStartStream.ConfigRequestFailed=\"Не удалось получить конфигурацию с %1<br><br>Ошибка HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Получено неизвестное значение состояния «%1»\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nХотите продолжить трансляцию без %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nХотите ли Вы продолжить трансляцию?\"\nFailedToStartStream.MissingEncoderConfigs=\"В конфигурации трансляции отсутствуют настройки кодировщика\"\nFailedToStartStream.InvalidEncoderConfig=\"В конфигурации трансляции есть недоспустимые настройки кодировщика\"\nFailedToStartStream.StatusMissingHTML=\"Запрос о начале трансляции привел к неизвестной ошибке\"\nFailedToStartStream.NoConfigSupplied=\"Отсутствует конфигурация\"\nMultitrackVideo.Info=\"%1 автоматически оптимизирует Ваши настройки для кодирования и отправки видео разного качества. Выбор этого параметра приведет к отправке на %2 информации о конфигурации Вашего компьютера и настройках программы.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Несовместимые настройки\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 на данный момент не совместим с:\\n\\n%2\\nЧтобы продолжить вести трансляцию с %1, отключите несовместимые настройки:\\n\\n%3\\nи начните трансляцию снова.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Отключить для этого потока и начать трансляцию\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Обновить настройки и начать трансляцию\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 сейчас не совместимо с [Аудио → Общие → Каналы] равным «%2», %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Аудио → Общие → Каналы] надо настроить на «%1»\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 требует несколько разных настроек для [Аудио → Общие → Каналы]\"\nBasic.OpenPluginManager=\"Менеджер плагинов\"\nPluginManager=\"Менеджер плагинов\"\nPluginManager.Restart=\"Для применения изменений плагинов необходимо перезапустить OBS Studio\"\nPluginManager.MissingPlugin=\"[ПЛАГИН НЕ НАЙДЕН]\"\nPluginManager.Section.Discover=\"Поиск\"\nPluginManager.Section.Manage=\"Установленные\"\nPluginManager.Section.Updates=\"Обновления\"\nPluginManager.Section.Manage.Title=\"Управление включенными плагинами\"\n"
  },
  {
    "path": "frontend/data/locale/si-LK.ini",
    "content": "Language=\"සිංහල\"\nOK=\"හරි\"\nApply=\"යොදන්න\"\nCancel=\"අවලංගු\"\nClose=\"වසන්න\"\nSave=\"සුරකින්න\"\nDiscard=\"ඉවතලන්න\"\nDisable=\"අබල කරන්න\"\nYes=\"ඔව්\"\nNo=\"නැහැ\"\nAdd=\"එකතු කිරීම\"\nRemove=\"ඉවත් කරන්න\"\nRename=\"නම් කරන්න\"\nInteract=\"මැදිහත් වන්න\"\nFilters=\"පෙරහන්\"\nProperties=\"ගුණාංග\"\nMoveUp=\"ඉහලට යන්න\"\nMoveDown=\"පහලට යන්න\"\nSettings=\"සැකසුම්\"\nDisplay=\"දර්ශකය\"\nName=\"නම\"\nExit=\"පිටවන්න\"\nMixer=\"හඬ මිශ්‍රකය\"\nBrowse=\"පිරික්සන්න\"\nMono=\"ඒකීය\"\nStereo=\"ත්‍රිමාණ\"\nDroppedFrames=\"අත්හැරුණු රාමු %1 (%2%)\"\nProjector.Open.Multiview=\"බහුදැක්ම අරින්න\"\nClear=\"මකන්න\"\nRevert=\"පෙරසේ\"\nShow=\"පෙන්වන්න\"\nHide=\"සඟවන්න\"\nUnhideAll=\"සියල්ල නොසඟවන්න\"\nUntitled=\"සිරැසිය නැත\"\nNew=\"නව\"\nDuplicate=\"අනුපිටපතක්\"\nEnable=\"සබල කරන්න\"\nDisableOSXVSync=\"මැක්ඕඑස් V-Sync අබල කරන්න\"\nResetOSXVSyncOnExit=\"පිටවීමේදී මැක්ඕඑස් V-Sync යළි සකසන්න\"\nHighResourceUsage=\"ආකේතනය අතිබැරයි! දෘශ්‍ය සැකසුම් පහළ දැමීමට හෝ වේගවත් ආකේතන පෙරවරණයක් භාවිතයට සලකා බලන්න.\"\nTransition=\"සංක්‍රාන්තිය\"\nQuickTransitions=\"ඉක්මන් සංක්‍රාන්තිය\"\nFadeToBlack=\"කළු පාටට මැලවීම\"\nLeft=\"වම\"\nRight=\"දකුණ\"\nTop=\"මුදුන\"\nBottom=\"පතුල\"\nReset=\"යළි සකසන්න\"\nHours=\"පැය\"\nMinutes=\"විනාඩි\"\nSeconds=\"තත්පර\"\nReplayBuffer=\"යළි වාදන අන්තරාචය\"\nImport=\"ආයාත කරන්න\"\nExport=\"නිර්යාත කරන්න\"\nCopy=\"පිටපතක්\"\nPaste=\"අලවන්න\"\nPasteReference=\"අලවන්න (යොමුව)\"\nPasteDuplicate=\"අලවන්න (අනුපිටපත)\"\nNext=\"ඊළඟ\"\nBack=\"ආපසු\"\nDefaults=\"පෙරනිමි\"\nRestoreDefaults=\"පෙරනිමි\"\nHideMixer=\"මිශ්‍රකයේ සඟවන්න\"\nTransitionOverride=\"සංක්‍රාන්තිය අභිබවීම\"\nShowTransition=\"සංක්‍රාන්තිය පෙන්වන්න\"\nHideTransition=\"සංක්‍රාන්තිය සඟවන්න\"\nNone=\"කිසිත් නැත\"\nStudioMode.Preview=\"පෙරදසුන\"\nStudioMode.Program=\"වැඩසටහන\"\nStudioMode.PreviewSceneName=\"පෙරදසුන: %1\"\nStudioMode.ProgramSceneName=\"වැඩසටහන: %1\"\nShowInMultiview=\"බහුදැක්මෙහි පෙන්වන්න\"\nVerticalLayout=\"සිරස් පිරිසැලසුම\"\nGroup=\"සමූහය\"\nDoNotShowAgain=\"නැවත නොපෙන්වන්න\"\nDefault=\"(පෙරනිමි)\"\nCalculating=\"ගණනය වෙමින්...\"\nFullscreen=\"පූර්ණ තිරය\"\nWindowed=\"කවුළුවක\"\nRefreshBrowser=\"නැවුම් කරන්න\"\nAspectRatio=\"දර්ශන අනුපාතය <b>%1:%2</b>\"\nLockVolume=\"ඝනඵලයට අගුල\"\nLogViewer=\"සටහන් දක්වනය\"\nShowOnStartup=\"ආරම්භයේදී පෙන්වන්න\"\nOpenFile=\"ගොනුව අරින්න\"\nAddSource=\"මූලාශ්‍රය යොදන්න\"\nRemoveScene=\"තේරූ සිද්ධිය ඉවතලන්න\"\nRemoveSource=\"තේරූ මූලාශ්‍ර(ය) ඉවතලන්න\"\nMoveSceneUp=\"සිද්ධිය ඉහළට\"\nMoveSceneDown=\"සිද්ධිය පහළට\"\nMoveSourceUp=\"සිද්ධි(ය) ඉහළට\"\nMoveSourceDown=\"සිද්ධි(ය) පහළට\"\nSourceProperties=\"විවෘත මූලාශ්‍ර ගුණාංග\"\nSourceFilters=\"විවෘත මූලාශ්‍ර පෙරහන්\"\nMixerToolbarMenu=\"හඬ මිශ්‍රක වට්ටෝරුව\"\nSceneFilters=\"සිද්ධි පෙරහන් අරින්න\"\nList=\"ලැයිස්තුවක්\"\nGrid=\"ජාලකයක්\"\nAutomatic=\"ස්වයංක්‍රීය\"\nPluginsFailedToLoad.Title=\"පේනුව පූරණ දෝෂයකි\"\nAlreadyRunning.Title=\"ඕබීඑස් දැනටමත් ධාවනය වෙමින්\"\nAlreadyRunning.Text=\"ඕබීඑස් දැනටමත් ක්‍රියාත්මකයි! මෙය ඔබගේ අදහසක් නම් මිස, නව එකක් ධාවනයට පෙර අනෙකුත් පවතින ඒවා වසා දමන්න. පද්ධතියේ තැටියට හැකිළීම සඳහා ඕබීඑස් සකසා තිබේ නම්, තවමත් එහි ක්‍රියාත්මක දැයි පරීක්‍ෂා කරන්න.\"\nAlreadyRunning.LaunchAnyway=\"දියත් කරන්න\"\nSafeMode.RestartNormal=\"ඔබට OBS සාමාන්‍ය ප්‍රකාරයේ යළි ඇරඹීමට වුවමනාද?\"\nChromeOS.Title=\"සහාය නොදක්වන වේදිකාවකි\"\nWine.Title=\"වයින් අනාවරණය විය\"\nDockCloseWarning.Title=\"තටාකගත කවුළුව වැසීම\"\nDockCloseWarning.Text=\"ඔබ තටාකගත කවුළුවක් වසා ඇත. එය නැවත දැකීමට කැමති නම්, වට්ටෝරු තීරුවේ තිබෙන තටාක බොත්තම් භාවිතා කරන්න.\"\nExtraBrowsers=\"අභිරුචි අතිරික්සු තටාක\"\nExtraBrowsers.Info=\"නමක් සහ ඒ.ස.නි. යෙදීමෙන් තටාක එකතු කරන්න. ඉන්පසුව, තටාක ඇරීමට යොදන්න හෝ වසන්න ඔබන්න. ඔබට ඕනෑම වේලාවක තටාක එකතු කිරීමට හෝ ඉවත් කිරීමට හැකිය.\"\nExtraBrowsers.DockName=\"තටාකයේ නම\"\nAuth.Authing.Title=\"සත්‍යාපනය වෙමින්...\"\nAuth.Authing.Text=\"%1 සමඟ සත්‍යාපනය වෙමින්, රැඳී සිටින්න...\"\nAuth.AuthFailure.Title=\"සත්‍යාපනයට අසමත්!\"\nAuth.AuthFailure.Text=\"%1:\\n\\n%2: %3 සමඟ සත්‍යාපනයට අසමත්!\"\nAuth.InvalidScope.Title=\"සත්‍යාපනය අවශ්‍යයි\"\nAuth.InvalidScope.Text=\"%1 සඳහා සත්‍යාපන අවශ්‍යතා වෙනස් වී ඇත. සමහර විශේෂාංග නොතිබෙනු ඇත.\"\nAuth.LoadingChannel.Title=\"නාලිකාවේ තොරතුරු පූරණය...\"\nAuth.LoadingChannel.Text=\"%1 සඳහා නාලිකාවේ තොරතුරු පූරණය වෙමින්, රැඳෙන්න...\"\nAuth.LoadingChannel.Error=\"නාලිකාවේ තොරතුරු ගැනීමට නොහැකිය.\"\nAuth.ChannelFailure.Title=\"නාලිකාව පූරණයට අසමත්!\"\nAuth.ChannelFailure.Text=\"%1\\n\\n%2: %3 සඳහා නාලිකාවේ තොරතුරු පූරණයට අසමත්!\"\nAuth.Chat=\"කතාබහ\"\nAuth.StreamInfo=\"ප්‍රචාරයේ තොරතුරු\"\nTwitchAuth.Stats=\"ට්විච් සංඛ්‍යාලේඛන\"\nTwitchAuth.Feed=\"ට්විච් ක්‍රියාකාරකම් පෝෂණය\"\nTwitchAuth.TwoFactorFail.Title=\"ප්‍රචාර යතුර විමසීමට නොහැකි විය\"\nTwitchAuth.TwoFactorFail.Text=\"ඔබගේ ට්විච් ගිණුමට සම්බන්ධ වීමට ඕබීඑස් සඳහා නොහැකි විය. ඔබගේ <a href='https://www.twitch.tv/settings/security'>ට්ව්ච් ආරක්‍ෂණ සැකසුම්</a> තුළ ද්වි-සාධක සත්‍යාපනය පිහිටුවා ඇති බවට වග බලා ගන්න. මෙය ප්‍රචාරය සඳහා අවශ්‍යයි.\"\nRestreamAuth.Channels=\"රීස්ට්‍රීම් නාලිකා\"\nCopy.Filters=\"පෙරහන් පිටපතක්\"\nPaste.Filters=\"පෙරහන් අලවන්න\"\nBrowserPanelInit.Title=\"අතිරික්සුව අරඹමින්...\"\nBrowserPanelInit.Text=\"අතිරික්සුව අරඹමින්, රැඳී සිටින්න...\"\nBandwidthTest.Region=\"කලාපය\"\nBandwidthTest.Region.US=\"එක්සත් ජනපදය\"\nBandwidthTest.Region.EU=\"යුරෝපය\"\nBandwidthTest.Region.Asia=\"ආසියාව\"\nBandwidthTest.Region.Other=\"වෙනත්\"\nBasic.AutoConfig=\"ස්වයං වින්‍යාස පිහිටුම\"\nBasic.AutoConfig.ApplySettings=\"සැකසුම් යොදන්න\"\nBasic.AutoConfig.StartPage=\"භාවිත තොරතුරු\"\nBasic.AutoConfig.StartPage.SubTitle=\"මෙම වැඩසටහනෙහි භාවිතය දක්වන්න\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"ප්‍රචාරයට ප්‍රශස්තිකරණය, පටිගතය දෙවනුව\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"පටිගතයට ප්‍රශස්තිකරණය, ප්‍රචාරය නොකරමි\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"අතථ්‍ය රූගතයට පමණක් භාවිතා කරමි\"\nBasic.AutoConfig.VideoPage=\"දෘශ්‍යයක සැකසුම්\"\nBasic.AutoConfig.VideoPage.SubTitle=\"ඔබ භාවිතයට කැමති දෘශ්‍ය සැකසුම් දක්වන්න\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"වත්මන් භාවිතය (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"දර්ශකය %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"වත්මන් භාවිතය (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 හෝ 30, නමුත් හැකි විට 60 ට ප්‍රියයි\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 හෝ 30, නමුත් ඉහළ විභේදනයකට ප්‍රියයි\"\nBasic.AutoConfig.StreamPage=\"ප්‍රචාරයේ තොරතුරු\"\nBasic.AutoConfig.StreamPage.SubTitle=\"ඔබගේ ප්‍රචාරයේ තොරතුරු යොදන්න\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"ගිණුම සබඳින්න (නිර්දේශිතයි)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"ගිණුම විසන්ධිකරන්න\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"ගිණුම විසන්ධිකරන්නද?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"මෙම වෙනස වහාම යෙදේ. ඔබගේ ගිණුම විසන්ධි කිරීමට වුවමනා ද?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"ප්‍රචාර යතුර ගන්න\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"තව තොරතුරු\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"ප්‍රචාර යතුර භාවිතය\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"ප්‍රචාර යතුර භාවිතය (වැඩිදුර)\"\nBasic.AutoConfig.StreamPage.Service=\"සේවාව\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"සියල්ල පෙන්වන්න...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"අභිරුචි...\"\nBasic.AutoConfig.StreamPage.Server=\"සේවාදායකය\"\nBasic.AutoConfig.StreamPage.StreamKey=\"ප්‍රචාර යතුර\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"ආකේතක යතුර\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"සම්බන්ධිත ගිණුම\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"දෘඩාංග ආකේතනයට ප්‍රියයි\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"දෘඩාංග ආකේතනය බොහෝ සෙයින් ම.සැ.ඒ. (CPU) භාවිතය අවම කරයි, නමුත් සමාන මට්ටමේ ගුණත්‍වයක් අත්කර ගැනීමට වැඩි බිටුඅනුපාතයක් අවශ්‍ය විය හැකිය.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"ප්‍රචාර අවවාදය\"\nBasic.AutoConfig.TestPage=\"අවසාන ප්‍රතිඵලය\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"අත්හදා බැලීම සම්පූර්ණයි\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"%1 වෙත සම්බන්ධ වෙමින්...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"කිසිදු සේවාදායකයකට සබැඳීමට අසමත් විය, ඔබගේ අන්තර්ජාල සම්බන්ධතාවය පරීක්‍ෂා කර නැවත බලන්න.\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"ප්‍රචාර ආකේතකය පරීක්‍ෂාව, විනාඩියක් පමණ ගතවේ...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"පටිගත ආකේතකය පරීක්‍ෂාව, විනාඩියක් පමණ ගතවේ...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"ආකේතකය ඇරඹීමට අසමත්!\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"රාමු/ත. %1x%2 %3 පරීක්‍ෂාව...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"ප්‍රචාර ආකේතකය\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"පටිගත ආකේතකය\"\nBasic.AutoConfig.TestPage.Result.Header=\"මෙම තක්සේරුගත සැකසුම් ඔබට වඩාත් සුදුසු බව වැඩසටහන තීන්දු කර ඇත:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"මෙම සැකසුම් භාවිතයට, සැකසුම් යොදන්න ඔබන්න. පිහිටුවීම යළි සකස් කර නැවත උත්සාහ කිරීමට, ආපසු ඔබන්න. සැකසුම් අතින් වින්‍යාස කිරීමට, අවලංගු කරන්න එබීමෙන් පසු සැකසුම් විවෘත කරන්න.\"\nBasic.AutoConfig.Info=\"ස්වයං-වින්‍යාස පිහිටුම ඔබගේ පරිගණකයේ පිරිවිතර හා අන්තර්ජාල වේගය මත පදනම්ව හොඳම සැකසුම් තීන්දු කෙරේ.\"\nBasic.AutoConfig.RunAnytime=\"මෙවලම් වට්ටෝරුවට යාමෙන් ඕනෑම විටෙක ධාවනය කළ හැකිය.\"\nBasic.Stats=\"සංඛ්‍යාලේඛන\"\nBasic.Stats.CPUUsage=\"ම.සැ.ඒ. භාවිතය\"\nBasic.Stats.HDDSpaceAvailable=\"තැටියේ තිබෙන ඉඩ\"\nBasic.Stats.MemoryUsage=\"මතකය භාවිතය\"\nBasic.Stats.AverageTimeToRender=\"රාමුව විදහාපෑමට සාමා. කාලය\"\nBasic.Stats.SkippedFrames=\"ආකේතන පමාව නිසා මඟහැරි රාමු\"\nBasic.Stats.MissedFrames=\"විදහාපෑම පමාව නිසා මඟහැරුණු රාමු\"\nBasic.Stats.Output.Stream=\"ප්‍රචාරය\"\nBasic.Stats.Output.Recording=\"පටිගතය\"\nBasic.Stats.Status=\"තත්‍වය\"\nBasic.Stats.Status.Recording=\"පටිගත වෙමින්\"\nBasic.Stats.Status.Live=\"සජීව\"\nBasic.Stats.Status.Reconnecting=\"යළි සම්බන්ධ වෙමින්\"\nBasic.Stats.Status.Inactive=\"අක්‍රිය\"\nBasic.Stats.Status.Active=\"සක්‍රිය\"\nBasic.Stats.DroppedFrames=\"ගිලිහුණු රාමු (ජාලය)\"\nBasic.Stats.MegabytesSent=\"මුළු දත්ත ප්‍රතිදානය\"\nBasic.Stats.Bitrate=\"බිටුඅනුපාතය\"\nBasic.Stats.ResetStats=\"සංඛ්‍යාලේඛන යළි සැකසීම\"\nResetUIWarning.Title=\"ඔබට අතුරුමුහුණත යළි සැකසීමට වුවමනා ද?\"\nResetUIWarning.Text=\"අතුරු මුහුණත නැවත සැකසීම අමතර තටාක සඟවනු ඇත. ඔබට ඒවා දැකීමට අවශ්‍ය නම් තටාක වට්ටෝරුවෙන් මෙම තටාක නොසැඟවිය යුතුය.\\n\\n අතුරු මුහුණත නැවත සැකසීමට වුවමනා ද?\"\nUpdater.Title=\"නව යාවත්කාලයක් තිබේ\"\nUpdater.Text=\"නව යාවත්කාලයක් තිබේ:\"\nUpdater.UpdateNow=\"යාවත්කාල කරන්න\"\nUpdater.RemindMeLater=\"පසුව මතක් කරන්න\"\nUpdater.Skip=\"අනුවාදය මඟහරින්න\"\nUpdater.NoUpdatesAvailable.Title=\"යාවත්කාල නැත\"\nUpdater.NoUpdatesAvailable.Text=\"දැනට යාවත්කාල නැත\"\nUpdater.FailedToLaunch=\"යාවත්කාලය දියත්කිරීමට අසමත්!\"\nQuickTransitions.DuplicateScene=\"සිද්ධියේ අනුපිටපත\"\nQuickTransitions.EditProperties=\"මූලාශ්‍ර අනුපිටපතක්\"\nQuickTransitions.HotkeyName=\"ඉක්මන් සංක්‍රාන්තිය: %1\"\nBasic.AddTransition=\"වින්‍යාසගත කළ හැකි සංක්‍රාන්තිය\"\nBasic.RemoveTransition=\"වින්‍යාසගත කළ හැකි සංක්‍රාන්තිය ඉවතලන්න\"\nBasic.TransitionProperties=\"සංක්‍රාන්ති ගුණාංග\"\nBasic.SceneTransitions=\"සිද්ධියේ සංක්‍රාන්ති\"\nBasic.TransitionDuration=\"පරාසය\"\nBasic.TogglePreviewProgramMode=\"මැදිරිය ප්‍රකාරය\"\nBasic.EnablePreviewProgramMode=\"මැදිරිය ප්‍රකාරය සබල කරන්න\"\nBasic.DisablePreviewProgramMode=\"මැදිරිය ප්‍රකාරය අබල කරන්න\"\nUndo.Undo=\"පෙරසේ\"\nUndo.Redo=\"පසුසේ\"\nUndo.Add=\"'%1' එක් කරන්න\"\nUndo.Delete=\"'%1' මකන්න\"\nUndo.Rename=\"'%1' ලෙස නම් කිරීම\"\nUndo.SceneCollection.Switch=\"'%1' වෙත මාරුවන්න\"\nUndo.Item.Undo=\"%1 පෙරසේ\"\nUndo.Item.Redo=\"%1 පසුසේ\"\nUndo.Sources.Multi=\"මූලාශ්‍ර %1ක් මකන්න\"\nUndo.Filters=\"'%1' හි පෙරහනේ වෙනස්කම්\"\nUndo.Filters.Paste.Single=\"'%1' පෙරහන '%2'ට අලවන්න\"\nUndo.Filters.Paste.Multiple=\"පෙරහන් '%1' වෙතින් '%2' ට අලවන්න\"\nUndo.Transform.Rotate=\"'%1' හි කරකැවීම\"\nUndo.Transform.HFlip=\"'%1' හි තිරස් පෙරලීම\"\nUndo.Transform.VFlip=\"'%1' හි සිරස් පෙරලීම\"\nUndo.Transform.FitToScreen=\"'%1' හි තිරයට ගැළපීම\"\nUndo.Transform.StretchToScreen=\"'%1' හි තිරයට පැතිරවීම\"\nUndo.Transform.Center=\"'%1' හි තිරයේ මැදට\"\nUndo.Transform.VCenter=\"'%1' හි තිරයේ සිරස්ව මැදට\"\nUndo.Transform.HCenter=\"'%1' හි තිරයේ තිරස්ව මැදට\"\nUndo.Volume.Change=\"'%1' හි හඬ වෙනස\"\nUndo.Volume.Mute=\"'%1' නිහඬකරන්න\"\nUndo.Volume.Unmute=\"'%1' හි නොනිඬ කිරීම\"\nUndo.Balance.Change=\"'%1' හි හඬ සංතුලනය වෙනස් කිරීම\"\nUndo.MonitoringType.Change=\"'%1' හි හඬ නිරීක්‍ෂණය වෙනස් කිරීම\"\nUndo.Mixers.Change=\"'%1' හි හඬ මිශ්‍රණය වෙනස් කිරීම\"\nUndo.Properties=\"'%1' වෙනස්කම\"\nUndo.Scene.Duplicate=\"'%1' සිද්ධියේ අනුපිටපතක්\"\nUndo.ShowTransition=\"'%1' හි සංක්‍රාන්තිය පෙන්වන්න\"\nUndo.HideTransition=\"'%1' හි සංක්‍රාන්තිය සැඟවීම\"\nUndo.ShowSceneItem=\"'%2' හි '%1' පෙන්වීම\"\nUndo.HideSceneItem=\"'%2' හි '%1' සැඟවීම\"\nUndo.ReorderSources=\"'%1' හි මූලාශ්‍ර පෙළගැස්ම\"\nUndo.MoveUp=\"'%2' හි '%1' ඉහළට ගෙනයාම\"\nUndo.MoveDown=\"'%2' හි '%1' පහළට ගෙනයාම\"\nUndo.MoveToTop=\"'%2' හි මුදුනට '%1' ගෙනයාම\"\nUndo.MoveToBottom=\"'%2' හි පතුලට '%1' ගෙනයාම\"\nUndo.PasteSource=\"'%1' හි මූලාශ්‍ර(ය) ඇලවීම\"\nUndo.PasteSourceRef=\"'%1' හි මූලාශ්‍ර යොමු(ව) ඇලවීම\"\nUndo.GroupItems=\"'%1' වෙත අංග සමූහනය\"\nTransitionNameDlg.Text=\"සංක්‍රාන්තියේ නම ඇතුල් කරන්න\"\nTransitionNameDlg.Title=\"සංක්‍රාන්තියේ නම\"\nTitleBar.SafeMode=\"නිරාපද ප්‍රකාරය\"\nTitleBar.Profile=\"පැතිකඩ\"\nTitleBar.Scenes=\"සිද්ධි\"\nNameExists.Title=\"නම දැනටමත් පවතී\"\nNameExists.Text=\"නම දැනටමත් භාවිතයේ පවතී.\"\nNoNameEntered.Title=\"වලංගු නමක් ඇතුල් කරන්න\"\nNoNameEntered.Text=\"හිස් නම් භාවිතා කළ නොහැකිය.\"\nConfirmStart.Title=\"ප්‍රචාරය අරඹන්න ද?\"\nConfirmStart.Text=\"ඔබට ප්‍රචාරය ආරම්භ කිරීමට වුවමනා ද?\"\nConfirmStop.Title=\"ප්‍රචාරය නවතන්න ද?\"\nConfirmStop.Text=\"ඔබට ප්‍රචාරය නැවැත්වීමට වුවමනා ද?\"\nConfirmStopRecord.Title=\"පටිගතය නවතන්නද?\"\nConfirmStopRecord.Text=\"ඔබට පටිගත වීම නැවැත්වීමට වුවමනා ද?\"\nConfirmRemove.Title=\"ඉවතලීම තහවුරුව\"\nConfirmRemove.Text=\"'%1' ඉවත් කිරීමට වුවමනා ද?\"\nConfirmRemove.TextMultiple=\"අංග %1 ක් ඉවත් කිරීමට වුවමනා ද?\"\nConfirmReset.Title=\"ගුණාංග යළි සකසන්න\"\nOutput.StartStreamFailed=\"ප්‍රචාරය ඇරඹීමට අසමත්!\"\nOutput.StartRecordingFailed=\"පටිගතය ඇරඹීමට අසමත්!\"\nOutput.StartReplayFailed=\"යළි වාදන අන්තරාචය ඇරඹීමට අසමත් විය\"\nOutput.StartVirtualCamFailed=\"අතථ්‍ය රූගතය ඇරඹීමට අසමත් විය\"\nOutput.StartFailedGeneric=\"ප්‍රතිදානය ඇරඹීමට අසමත් විය. විස්තර සඳහා සටහන බලන්න.\\n\\nඅමතර: ඔබ NVENC හෝ AMD ආකේතක භාවිතා කරන්නේ නම්, ඔබගේ දෘශ්‍ය ධාවක යාවත්කාලීන දැයි බලන්න.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"විරාමයකදී යළි වාදන සුරැකීමට නොහැකිය\"\nOutput.ReplayBuffer.PauseWarning.Text=\"අවවාදයයි: පටිගත විරාමයකදී යළි වාදන සුරැකීමට නොහැකිය.\"\nOutput.ConnectFail.Title=\"සබැඳීමට අසමත්!\"\nOutput.ConnectFail.BadPath=\"පෙත හෝ සම්බන්ධතාවේ ඒ.ස.නි. වලංගු නොවේ. සැකසුම් පරීක්‍ෂා කර ඒවා වලංගු දැයි තහවුරු කරගන්න.\"\nOutput.ConnectFail.ConnectFailed=\"සේවාදායකයට සබැඳීමට අසමත්!\"\nOutput.ConnectFail.InvalidStream=\"නිරූපිත නාලිකාව හෝ ප්‍රචාර යතුර වෙත ප්‍රවේශ වීමට නොහැකියි, ඔබගේ ප්‍රචාර යතුර යළිත් පරීක්‍ෂා කරන්න. එය නිවැරදි නම්, සේවාදායකයට සම්බන්ධ වීමේ ගැටළුවක් විය හැකිය.\"\nOutput.ConnectFail.Error=\"සේවාදායකයට සම්බන්ධ වීමට තැත් කිරීමේදී අනපේක්‍ෂිත දෝෂයක් සිදුවිය. සටහන් ගොනුවෙහි වැඩි විස්තර ඇත.\"\nOutput.ConnectFail.Disconnected=\"සේවාදායකයෙන් විසන්ධි විය.\"\nOutput.StreamEncodeError.Title=\"ආකේතන දෝෂයකි\"\nOutput.StreamEncodeError.Msg=\"ප්‍රචාරය අතරතුර ආකේතන දෝෂයක් සිදුවිය.\"\nOutput.RecordFail.Title=\"පටිගතය ඇරඹීමට අසමත් විය\"\nOutput.RecordFail.Unsupported=\"ප්‍රතිදාන ආකෘතියට සහාය නොදක්වයි හෝ තනි ශ්‍රව්‍ය පථයකට වඩා සහාය නොදක්වයි. ඔබගේ සැකසුම් පරීක්‍ෂා කර නැවත උත්සාහ කරන්න.\"\nOutput.RecordNoSpace.Title=\"ප්‍රමාණවත් ඉඩක් තැටියේ නැත\"\nOutput.RecordNoSpace.Msg=\"දිගටම පටිගතයට සෑහෙන ඉඩක් තැටියේ නැත.\"\nOutput.RecordError.Title=\"පටිගත වීමේ දෝෂයකි\"\nOutput.RecordError.Msg=\"පටිගතයේ දී අවිනිශ්චිත දෝෂයක් සිදුවිය.\"\nOutput.RecordError.EncodeErrorMsg=\"පටිගතයේ දී ආකේතන දෝෂයක් සිදුවිය.\"\nOutput.BadPath.Title=\"ගොනුවේ පෙත නරකයි\"\nOutput.BadPath.Text=\"වින්‍යාසගත පටිගත පෙත ඇරීමට නොහැකිය. සැකසුම් → ප්‍රතිදානය → පටිගත යටතේ ඔබගේ පටිගත පෙත පරීක්‍ෂා කරන්න.\"\nOutput.NoBroadcast.Title=\"විකාශනයක් වින්‍යාස කර නැත\"\nOutput.NoBroadcast.Text=\"ප්‍රචාරය ආරම්භයට පෙර ඔබ විකාශනයක් පිහිටුවිය යුතුය.\"\nOutput.BroadcastStartFailed=\"විකාශනය ඇරඹීමට අසමත්!\"\nOutput.BroadcastStopFailed=\"විකාශනය නැවතීමට අසමත්!\"\nRemux.SourceFile=\"ඕබීඑස් පටිගතය\"\nRemux.TargetFile=\"ඉලක්කගත ගොනුව\"\nRemux.ClearFinished=\"නිමවූ අංග මකන්න\"\nRemux.ClearAll=\"සියළු අංග මකන්න\"\nRemux.OBSRecording=\"ඕබීඑස් පටිගතය\"\nRemux.SelectRecording=\"ඕබීඑස් පටිගත තෝරන්න...\"\nRemux.SelectTarget=\"ඉලක්කගත ගොනුව තෝරන්න...\"\nRemux.FileExistsTitle=\"ඉලක්කගත ගොනු පවතී\"\nRemux.FileExists=\"පහත ඉලක්කගත ගොනු දැනටමත් පවතී. ඒවා ප්‍රතිස්ථාපනය කිරීමට අවශ්‍යද?\"\nMissingFiles=\"මඟහැරුණු ගොනු\"\nMissingFiles.MissingFile=\"මඟහැරුණු ගොනුව\"\nMissingFiles.NewFile=\"නව ගොනුව\"\nMissingFiles.HelpText=\"ඕබීඑස් අවසාන භාවිතයෙන් පසු සමහර ගොනු දක්නට නැත.\"\nMissingFiles.Clear=\"<මැකිණි>\"\nMissingFiles.NumFound=\"%2 න් %1 ක් හමු විය\"\nMissingFiles.Search=\"නාමාවලිය සොයන්න...\"\nMissingFiles.SelectFile=\"ගොනුව තෝරන්න...\"\nMissingFiles.SelectDir=\"සෙවීමට බහලුමක් තෝරන්න\"\nMissingFiles.State=\"තත්‍වය\"\nMissingFiles.Missing=\"මඟහැරී\"\nMissingFiles.Replaced=\"ප්‍රතිස්ථාපිතයි\"\nMissingFiles.Cleared=\"මැකිණි\"\nMissingFiles.Found=\"හමු විය\"\nMissingFiles.AutoSearch=\"අතිරේක ගොනු ගැළපුමක් තිබේ\"\nMissingFiles.AutoSearchText=\"නාමාවලියෙහි දක්නට නොලත් ගොනු සඳහා අතිරේක ගැළපීම් හමු වී ඇත. ඔබ ඒවා එකතු කිරීමට කැමතිද?\"\nMissingFiles.NoMissing.Title=\"මඟහැරුණු ගොනු පරීක්‍ෂාව\"\nMissingFiles.NoMissing.Text=\"ගොනු අස්ථානගත වී නැති සෙයකි.\"\nMacPermissions.MenuAction=\"යෙදුමේ අවසර සමාලෝචනය...\"\nMacPermissions.Title=\"යෙදුමේ අවසර සමාලෝචනය\"\nMacPermissions.AccessGranted=\"ප්‍රවේශය ප්‍රදානය කර ඇත\"\nMacPermissions.RequestAccess=\"ප්‍රවේශය ඉල්ලන්න\"\nMacPermissions.OpenPreferences=\"%1 අභිප්‍රේත අරින්න\"\nMacPermissions.Item.ScreenRecording=\"තිරය පටිගතය\"\nMacPermissions.Item.Camera=\"රූගතය\"\nMacPermissions.Item.Microphone=\"ශබ්දවාහිනිය\"\nMacPermissions.Item.Accessibility=\"ප්‍රවේශ්‍යතාව\"\nMacPermissions.Continue=\"ඉදිරියට\"\nBasic.DesktopDevice1=\"වැඩතලයේ හඬ\"\nBasic.DesktopDevice2=\"වැඩතලයේ හඬ 2\"\nBasic.Scene=\"සිද්ධිය\"\nBasic.DisplayCapture=\"දර්ශකය ග්‍රහණය\"\nBasic.Main.PreviewConextMenu.Enable=\"පෙරදසුන සබල කරන්න\"\nBasic.Main.Preview.Disable=\"පෙරදසුන අබල කරන්න\"\nScaleFiltering=\"පරිමාණනය පෙරීම\"\nScaleFiltering.Bilinear=\"බයිලයිනියර්\"\nScaleFiltering.Bicubic=\"බයිකියුබික්\"\nScaleFiltering.Lanczos=\"ලැන්ක්සෝස්\"\nScaleFiltering.Area=\"පෙදෙස\"\nBlendingMethod.Default=\"පෙරනිමි\"\nBlendingMethod.SrgbOff=\"SRGB අක්‍රියයි\"\nBlendingMode.Normal=\"සාමාන්‍ය\"\nBlendingMode.Additive=\"එකතු\"\nBlendingMode.Screen=\"තිරය\"\nDeinterlacing.Discard=\"ඉවතලන්න\"\nDeinterlacing.TopFieldFirst=\"ඉහළ ක්‍ෂේත්‍රය පළමුව\"\nDeinterlacing.BottomFieldFirst=\"පහළ ක්‍ෂේත්‍රය පළමුව\"\nVolControl.Mute=\"'%1' නිහඬව\"\nVolControl.Properties=\"'%1' සඳහා ගුණාංග\"\nVolControl.UnassignedWarning.Text=\"කිසිදු ශ්‍රව්‍ය පථයකට \\\"%1\\\" පවරා නැති අතර එය ප්‍රචාර හෝ පටිගත වලදී නොඇසෙනු ඇත. \\n\\nපථයකට ශ්‍රව්‍ය මූලාශ්‍රයක් පැවරීමට, දකුණ එබීමෙන් ලැබෙන වට්ටෝරුවෙන් හෝ මිශ්‍රක තටාකයේ මෙවලම් තීරුවේ තිබෙන දැති බොත්තම හරහා සංකීර්ණ ශ්‍රව්‍ය ගුණාංග අරින්න.\"\nBasic.Main.AddSceneDlg.Title=\"සිද්ධියක් යොදන්න\"\nBasic.Main.AddSceneDlg.Text=\"සිද්ධියෙහි නම ඇතුල් කරන්න\"\nBasic.Main.DefaultSceneName.Text=\"සිද්ධිය %1\"\nBasic.Main.AddSceneCollection.Title=\"සිද්ධි එකතුව යොදන්න\"\nBasic.Main.AddSceneCollection.Text=\"සිද්ධි එකතුවේ නම ඇතුල් කරන්න\"\nBasic.Main.RenameSceneCollection.Title=\"සිද්ධි එකතුව යළි නම් කිරීම\"\nAddProfile.Title=\"පැතිකඩක් එක් කරන්න\"\nAddProfile.Text=\"පැතිකඩෙහි නම ඇතුල් කරන්න\"\nAddProfile.WizardCheckbox=\"ස්වයං වින්‍යාසකරනය පෙන්වන්න\"\nRenameProfile.Title=\"පැතිකඩ නම් කරන්න\"\nBasic.Main.MixerRename.Title=\"ශ්‍රව්‍ය මූලාශ්‍රය නම් කරන්න\"\nBasic.Main.MixerRename.Text=\"ශ්‍රව්‍ය මූලාශ්‍රයේ නම යොදන්න\"\nBasic.Main.PreviewDisabled=\"පෙරදසුන දැනට අබල කර ඇත\"\nBasic.SourceSelect=\"සාදන්න/මූලාශ්‍රය තෝරන්න\"\nBasic.SourceSelect.CreateNew=\"අලුතින් සාදන්න\"\nBasic.SourceSelect.AddExisting=\"පවතින දෑ යොදන්න\"\nBasic.SourceSelect.AddVisible=\"මූලාශ්‍රය පෙන්වන්න\"\nBasic.Main.Sources.Visibility=\"දෘශ්‍යතාව\"\nBasic.Main.Sources.Lock=\"අගුල\"\nBasic.PropertiesWindow=\"'%1' සඳහා ගුණාංග\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (ස්වයංතේරීම: %2)\"\nBasic.PropertiesWindow.SelectColor=\"වර්ණය තෝරන්න\"\nBasic.PropertiesWindow.SelectFont=\"මුද්‍රණඅකුර තෝරන්න\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"රුවකුරක් තෝරන්න\"\nBasic.PropertiesWindow.ConfirmTitle=\"සැකසුම් වෙනස්ව ඇත\"\nBasic.PropertiesWindow.Confirm=\"නොසුරැකි වෙනස්කම් තිබේ. ඔබට ඒවා තබා ගැනීමට අවශ්‍යද?\"\nBasic.PropertiesWindow.NoProperties=\"ගුණාංග නොතිබේ\"\nBasic.PropertiesWindow.AddFiles=\"ගොනු යොදන්න\"\nBasic.PropertiesWindow.AddDir=\"නාමාවලිය යොදන්න\"\nBasic.PropertiesWindow.AddURL=\"පෙත/ඒ.ස.නි. යොදන්න\"\nBasic.PropertiesWindow.AddEditableListDir=\"'%1' ට නාමාවලිය යොදන්න\"\nBasic.PropertiesWindow.AddEditableListFiles=\"'%1' ට ගොනු යොදන්න\"\nBasic.PropertiesWindow.AddEditableListEntry=\"'%1' ට නිවේශිතය යොදන්න\"\nBasic.PropertiesWindow.EditEditableListEntry=\"'%1' න් නිවේශිතය සංස්කරණය\"\nBasic.PropertiesView.FPS.Simple=\"සරල රාමු/ත. අගයන්\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"වලංගු රාමු/ත. පරාස:\"\nBasic.PropertiesView.UrlButton.Text=\"ඔබගේ පෙරනිමි අතිරික්සුවෙන් මෙම සබැඳිය අරින්නද?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"ඒ.ස.නි.: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"ඒ.ස.නි. අරින්න\"\nBasic.StatusBar.Reconnecting=\"විසන්ධියි, තත්. %2 කින් යළි සම්බන්ධ වේ (උත්සාහය %1)\"\nBasic.StatusBar.AttemptingReconnect=\"යළි සබැඳීමට තැත් කරමින්... (උත්සාහය %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"නැවත සම්බන්ධ විය\"\nBasic.StatusBar.Delay=\"ප්‍රමාදය (තත්. %1)\"\nBasic.StatusBar.DelayStartingIn=\"ප්‍රමාදය (තත්. %1 කින් ඇරඹෙයි)\"\nBasic.StatusBar.DelayStoppingIn=\"ප්‍රමාදය (තත්. %1 කින් නතර වේ)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"ප්‍රමාදය (තත්. %1 කින් නතර වේ, තත්. %2 කින් ඇරඹෙයි)\"\nBasic.StatusBar.RecordingSavedTo=\"පටිගත වීම '%1' වෙත සුරැකිණි\"\nBasic.StatusBar.ReplayBufferSavedTo=\"යළි වාදන අන්තරාචය '%1' ට සුරැකිණි\"\nBasic.StatusBar.ScreenshotSavedTo=\"තිර සේයාව '%1' වෙත සුරැකිණි\"\nBasic.Filters=\"පෙරහන්\"\nBasic.Filters.AsyncFilters=\"ශ්‍රව්‍ය/දෘශ්‍ය පෙරහන්\"\nBasic.Filters.AudioFilters=\"ශ්‍රව්‍ය පෙරහන්\"\nBasic.Filters.Title=\"'%1' සඳහා පෙරහන්\"\nBasic.Filters.AddFilter.Title=\"පෙරහනේ නම\"\nBasic.Filters.AddFilter.Text=\"පෙරහනේ නම සඳහන් කරන්න\"\nBasic.TransformWindow=\"සිද්ධි අංගය පරිණාමනය\"\nBasic.TransformWindow.Position=\"පිහිටුම\"\nBasic.TransformWindow.PositionX=\"X පිහිටුම\"\nBasic.TransformWindow.PositionY=\"Y පිහිටුම\"\nBasic.TransformWindow.Rotation=\"කරකැවීම\"\nBasic.TransformWindow.Size=\"ප්‍රමාණය\"\nBasic.TransformWindow.Width=\"පළල\"\nBasic.TransformWindow.Height=\"උස\"\nBasic.TransformWindow.Alignment=\"ස්ථානීය පෙළගැන්නුම\"\nBasic.TransformWindow.BoundsType=\"පර්යන්ත පෙට්ටියේ වර්ගය\"\nBasic.TransformWindow.BoundsAlignment=\"පර්යන්ත පෙට්ටියේ පෙළගැන්නුම\"\nBasic.TransformWindow.Bounds=\"පර්යන්ත පෙට්ටියේ තරම\"\nBasic.TransformWindow.BoundsWidth=\"පර්යන්ත පෙට්ටියේ පළල\"\nBasic.TransformWindow.BoundsHeight=\"පර්යන්ත පෙට්ටියේ උස\"\nBasic.TransformWindow.Crop=\"කප්පාදුව\"\nBasic.TransformWindow.CropLeft=\"වම කප්පාදුව\"\nBasic.TransformWindow.CropRight=\"දකුණ කප්පාදුව\"\nBasic.TransformWindow.CropTop=\"මුදුන කප්පාදුව\"\nBasic.TransformWindow.CropBottom=\"පතුළ කප්පාදුව\"\nBasic.TransformWindow.Alignment.TopLeft=\"මුදුන වම\"\nBasic.TransformWindow.Alignment.TopCenter=\"මුදුන මැද\"\nBasic.TransformWindow.Alignment.TopRight=\"මුදුන දකුණ\"\nBasic.TransformWindow.Alignment.CenterLeft=\"වම මැදට\"\nBasic.TransformWindow.Alignment.Center=\"මැද\"\nBasic.TransformWindow.Alignment.CenterRight=\"දකුණ මැදට\"\nBasic.TransformWindow.Alignment.BottomLeft=\"පහළ වමට\"\nBasic.TransformWindow.Alignment.BottomCenter=\"පහළ මැදට\"\nBasic.TransformWindow.Alignment.BottomRight=\"පහළ දකුණට\"\nBasic.TransformWindow.BoundsType.None=\"මායිම් නැත\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"උප. ප්‍රමාණය පමණි\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"ඇතුළු මායිම්වලට පරිමාණය\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"පිටත මායිම්වලට පරිමාණය\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"මායිම්වල පළලට පරිමාණය\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"මායිම්වල උසට පරිමාණය\"\nBasic.TransformWindow.BoundsType.Stretch=\"මායිම්වලට පතුරන්න\"\nBasic.TransformWindow.Title=\"'%1' සඳහා රූපාන්තරය සංශෝධනය\"\nBasic.TransformWindow.NoSelectedSource=\"මූලාශ්‍රයක් තෝරා නැත\"\nBasic.Main.AddSourceHelp.Title=\"මූලාශ්‍රය එක්කළ නොහැකිය\"\nBasic.Main.AddSourceHelp.Text=\"මූලාශ්‍රයක් එක් කිරීමට අවම වශයෙන් සිද්ධියක් තිබිය යුතුය.\"\nBasic.Main.Scenes=\"සිද්ධි\"\nBasic.Main.Sources=\"මූලාශ්‍ර\"\nBasic.Main.Source=\"මූලාශ්‍රය\"\nBasic.Main.Controls=\"පාලන\"\nBasic.Main.PreparingStream=\"සූදානම් වෙමින්...\"\nBasic.Main.Connecting=\"සබැඳෙමින්...\"\nBasic.Main.StartRecording=\"පටිගතය අරඹන්න\"\nBasic.Main.StartReplayBuffer=\"ය.වා. අන්තරාචය අරඹන්න\"\nBasic.Main.SaveReplay=\"යළි වාදනය සුරකින්න\"\nBasic.Main.StartStreaming=\"ප්‍රචාරය අරඹන්න\"\nBasic.Main.StartBroadcast=\"සජීව යන්න\"\nBasic.Main.StartVirtualCam=\"අතථ්‍ය රූගතය අරඹන්න\"\nBasic.Main.StopRecording=\"පටිගතය නවතන්න\"\nBasic.Main.PauseRecording=\"පටිගතය විරාමය\"\nBasic.Main.UnpauseRecording=\"පටිගතය නැවතත්\"\nBasic.Main.StoppingRecording=\"පටිගත වීම නැවතෙමින්...\"\nBasic.Main.StopReplayBuffer=\"අන්තරාචය නවතන්න\"\nBasic.Main.StoppingReplayBuffer=\"අන්තරාචය නැවතෙමින්...\"\nBasic.Main.SetupBroadcast=\"විකාශනය කළමනාකරණය\"\nBasic.Main.StopStreaming=\"ප්‍රචාරය නවතන්න\"\nBasic.Main.StopBroadcast=\"විකාශනය නිමාව\"\nBasic.Main.AutoStopEnabled=\"(ස්වයං නැවතීම)\"\nBasic.Main.StoppingStreaming=\"ප්‍රචාරය නවතමින්...\"\nBasic.Main.ForceStopStreaming=\"ප්‍රචාරය නවතන්න (ප්‍රමාදය ඉවතට)\"\nBasic.Main.ShowContextBar=\"මූලාශ්‍ර මෙවලම් පෙන්වන්න\"\nBasic.Main.HideContextBar=\"මූලාශ්‍ර මෙවලම් සඟවන්න\"\nBasic.Main.StopVirtualCam=\"අතථ්‍ය රූගතය නවතන්න\"\nBasic.Main.Group=\"සමූහය %1\"\nBasic.Main.GroupItems=\"තේරූ අංග සමූහනය\"\nBasic.Main.Ungroup=\"අසමූහනය\"\nBasic.Main.GridMode=\"ජාලක ප්‍රකාරය\"\nBasic.Main.ListMode=\"ලැයිස්තු ප්‍රකාරය\"\nBasic.Main.VirtualCamConfig=\"අතථය රූගතය සකසන්න\"\nBasic.VCam.VirtualCamera=\"අතථ්‍ය රූගතය\"\nBasic.VCam.OutputType=\"ප්‍රතිදාන වර්ගය\"\nBasic.VCam.OutputSelection=\"ප්‍රතිදාන තේරීම\"\nBasic.VCam.OutputType.Program=\"වැඩසටහන (පෙරනිමි)\"\nBasic.VCam.OutputSelection.NoSelection=\"මෙම ප්‍රතිදානය සඳහා තේරීමක් නැත\"\nBasic.MainMenu.File=\"ගොනුව (&F)\"\nBasic.MainMenu.File.Export=\"නිර්යාතය (&E)\"\nBasic.MainMenu.File.Import=\"ආයාතය (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"පටිගතයන් පෙන්වන්න (&R)\"\nBasic.MainMenu.File.Settings=\"සැකසුම් (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"සැකසුම් බහාලුම පෙන්වන්න\"\nBasic.MainMenu.File.ShowProfileFolder=\"පැතිකඩ බහාලුම පෙන්වන්න\"\nBasic.MainMenu.File.ShowMissingFiles=\"නැතිවූ ගොනු සොයා බලන්න\"\nBasic.MainMenu.File.Exit=\"පිටවන්න\"\nBasic.MainMenu.Edit=\"සංස්කරණය\"\nBasic.MainMenu.Edit.Undo=\"&පෙරසේ\"\nBasic.MainMenu.Edit.Redo=\"&පසුසේ\"\nBasic.MainMenu.Edit.LockPreview=\"පෙරදසුන අගුළු ලන්න (&L)\"\nBasic.MainMenu.Edit.Scale=\"පෙරදසුන පරිමාණනය (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"කවුළුවට පරිමාණනය\"\nBasic.MainMenu.Edit.Scale.Output=\"ප්‍රතිදානය (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"විශාලනය\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"කුඩාලනය\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"යළි සකසන්න\"\nBasic.MainMenu.Edit.Transform=\"රූපාන්තරය (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"රූපාන්තරය සංශෝධනය\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"රූපාන්තරයේ පිටපතක්\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"රූපාන්තරය අලවන්න\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"රූපාන්තරය යළි සකසන්න\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"90° ක් කරකවන්න (විරු.)\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"90°ක් කරකවන්න (ඔර.විරු.)\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180° කරකවන්න\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"තිරස්ව පෙරළන්න (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"සිරස්ව පෙරළන්න (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"තිරයට ගළපන්න (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"තිරයට පතුරන්න (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&තිරය මැදට (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"සිරස්ව මැදට\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"තිරස්ව මැදට\"\nBasic.MainMenu.Edit.Order=\"අනුපිළිවෙළ (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"ඉහළට ගෙනයන්න\"\nBasic.MainMenu.Edit.Order.MoveDown=\"පහළට ගෙනයන්න\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"මුදුනට ගෙනයන්න\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"පතුළට ගෙනයන්න\"\nBasic.MainMenu.Edit.AdvAudio=\"වැඩිදුර ශ්‍රව්‍ය ගුණාංග (&A)\"\nBasic.MainMenu.View=\"දැක්ම (&V)\"\nBasic.MainMenu.View.Toolbars=\"මෙවලම් තීරු (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"මෙවලම් තීරු තටාකයට\"\nBasic.MainMenu.View.ContextBar=\"මූලාශ්‍ර මෙවලම් තීරුව\"\nBasic.MainMenu.View.SourceIcons=\"මූලාශ්‍ර නිරූපක (&I)\"\nBasic.MainMenu.View.StatusBar=\"තත්‍ව තීරුව (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"පූර්ණතිර අතුරුමුහුණත\"\nBasic.MainMenu.View.ResetUI=\"අ.මු.(UI) යළි සකසන්න\"\nBasic.MainMenu.View.AlwaysOnTop=\"සැමවිට මුදුනින් (&A)\"\nBasic.MainMenu.View.SceneListMode=\"සිද්ධි ලැයිස්තු ප්‍රකාරය\"\nBasic.MainMenu.Docks=\"තටාක (&D)\"\nBasic.MainMenu.Docks.ResetDocks=\"තටාක යළි සකසන්න (&R)\"\nBasic.MainMenu.Docks.LockDocks=\"තටාක අගුළුලන්න (&L)\"\nBasic.MainMenu.Docks.SideDocks=\"පූර්ණ උසට තටාක (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"අභිරුචි අතිරික්සු තටාක...\"\nBasic.MainMenu.Import=\"ආයාතය...\"\nBasic.MainMenu.Export=\"නිර්යාතය...\"\nBasic.MainMenu.SceneCollection=\"සිද්ධි එකතුව (&S)\"\nBasic.MainMenu.Profile=\"පැතිකඩ (&P)\"\nBasic.MainMenu.Profile.Import=\"පැතිකඩ ආයාතය\"\nBasic.MainMenu.Profile.Export=\"පැතිකඩ නිර්යාතය\"\nBasic.MainMenu.SceneCollection.Import=\"සිද්ධි එකතුව ආයාතය\"\nBasic.MainMenu.SceneCollection.Export=\"සිද්ධි එකතුව නිර්යාතය\"\nBasic.MainMenu.SceneCollection.Remigrate=\"මූලික විභේදනය යළි සකසන්න\"\nBasic.MainMenu.SceneCollection.Migrate=\"මූලික විභේදනය සකසන්න\"\nBasic.MainMenu.Profile.Exists=\"පැතිකඩ දැනටමත් පවතී\"\nBasic.MainMenu.Tools=\"මෙවලම් (&T)\"\nBasic.MainMenu.Help=\"උදව් (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"උදව් ද්වාරය\"\nBasic.MainMenu.Help.Website=\"අඩවියට ගොඩවදින්න\"\nBasic.MainMenu.Help.Discord=\"ඩිස්කෝඩ් හි බලන්න\"\nBasic.MainMenu.Help.WhatsNew=\"අළුත් දෑ\"\nBasic.MainMenu.Help.Logs=\"සටහන් ගොනු (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"සටහන් ගොනු පෙන්වන්න\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"වත්මන් සටහන් ගොනුව උඩුගත කරන්න\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"පෙර සටහන් ගොනුව උඩුගත කරන්න\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"වත්මන් සටහන බලන්න\"\nBasic.MainMenu.Help.ReleaseNotes=\"නිකුතු සටහන්\"\nBasic.MainMenu.Help.CheckForUpdates=\"යාවත්කාල සඳහා පරීක්‍ෂාව\"\nBasic.MainMenu.Help.RestartSafeMode=\"නිරාපද ප්‍රකාරයේ අරඹන්න\"\nBasic.MainMenu.Help.RestartNormal=\"සාමාන්‍ය ප්‍රකාරයේ යළි අරඹන්න\"\nBasic.MainMenu.Help.CrashLogs=\"බිඳවැටීමේ වාර්තා (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"බිඳවැටීමේ වාර්තා පෙන්වන්න\"\nBasic.MainMenu.Help.About=\"පිළිබඳව (&A)\"\nBasic.Settings.ProgramRestart=\"මෙම සැකසුම් යෙදීම සඳහා වැඩසටහන නැවත ආරම්භ කළ යුතුය.\"\nBasic.Settings.ConfirmTitle=\"වෙනස්කම් තහවුරුව\"\nBasic.Settings.Confirm=\"නොසුරැකි වෙනස්කම් තිබේ. සුරකින්න ද?\"\nBasic.Settings.General=\"පොදු\"\nBasic.Settings.General.Language=\"භාෂාව\"\nBasic.Settings.General.Updater=\"යාවත්කාල\"\nBasic.Settings.General.UpdateChannel=\"නාලිකාව යාවත්කාලය\"\nBasic.Settings.General.UpdateChannelDisabled=\"(අබලයි)\"\nBasic.Settings.General.UpdateChannelDefault=\"(පෙරනිමි)\"\nBasic.Settings.General.EnableAutoUpdates=\"ආරම්භයේ දී යාවත්කාල පරීක්‍ෂා කරන්න\"\nBasic.Settings.General.OpenStatsOnStartup=\"ආරම්භයේදී තත්‍ව කවුළුව අරින්න\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"OBS කවුළු තිර ග්‍රහණයෙන් සඟවන්න\"\nBasic.Settings.General.WarnBeforeStartingStream=\"ප්‍රචාර ඇරඹීමේදී තහවුරු කිරීමේ කවුළුව පෙන්වන්න\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"ප්‍රචාර නැවතීමේදී තහවුරු කිරීමේ කවුළුව පෙන්වන්න\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"පටිගතය නැවතීමේදී තහවුරු කිරීමේ කවුළුව පෙන්වන්න\"\nBasic.Settings.General.Projectors=\"ප්‍රක්‍ෂේපක\"\nBasic.Settings.General.HideProjectorCursor=\"ප්‍රක්‍ෂේපණ මත ඊතලය සඟවන්න\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"ප්‍රක්‍ෂේපක සැමවිට මුදුනින් තබන්න\"\nBasic.Settings.General.SnapDistance=\"සේයාවේ සංවේදීතාව\"\nBasic.Settings.General.RecordWhenStreaming=\"ප්‍රචාරය වන විට ස්වයංක්‍රීයව පටිගත කරන්න\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"ප්‍රචාරය නතර වුවද දිගටම පටිගත කරන්න\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"ප්‍රචාරය වන විට ස්වයංක්‍රීයව යළි වාදන අන්තරාචය අරඹන්න\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"ප්‍රචාරය නතර වන විටද යළි වාදන අන්තරාචය සක්‍රියව තබන්න\"\nBasic.Settings.General.SysTray=\"පද්ධතියේ තැටිය\"\nBasic.Settings.General.SysTrayWhenStarted=\"ආරම්භයේදී පද්ධතියේ තැටියට හකුළන්න\"\nBasic.Settings.General.SystemTrayHideMinimize=\"සෑමවිට කාර්ය තීරුවට නොව පද්ධතියේ තැටියට හකුළන්න\"\nBasic.Settings.General.SaveProjectors=\"පිටවීමේදී ප්‍රක්‍ෂේපක සුරකින්න\"\nBasic.Settings.General.CloseExistingProjectors=\"තිරයකට එක් පූර්ණතිර ප්‍රක්ක්‍ෂේපකයක් සීමා කිරීම\"\nBasic.Settings.General.Preview=\"පෙරදසුන\"\nBasic.Settings.General.Importers=\"ආයාතක\"\nBasic.Settings.General.AutomaticCollectionSearch=\"ආයාත කිරීමේදී සිද්ධි එකතුව සඳහා දන්නා ස්ථාන සොයන්න\"\nBasic.Settings.General.SwitchOnDoubleClick=\"ද්විත්ව එබීමේදී සිද්ධියට සංක්‍රාන්තිය\"\nBasic.Settings.General.StudioPortraitLayout=\"තිරස්/සිරස් පිරිසැලසුම සබල කරන්න\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"පෙරදසුන/වැඩසටහන නම්පත් පෙන්වන්න\"\nBasic.Settings.General.Multiview=\"බහු දැක්ම\"\nBasic.Settings.General.Multiview.MouseSwitch=\"සිද්ධි අතර මාරු වීමට ඔබන්න\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"සිද්ධිවල නම් පෙන්වන්න\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"ආරක්‍ෂිත පෙදෙස් අඳින්න (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"බහුදැක්ම පිරිසැලසුම\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"තිරස්, මුදුන (සිද්ධි 8)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"තිරස්, පතුල (සිද්ධි 8)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"සිරස්, වම (සිද්ධි 8)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"සිරස්, දකුණ (සිද්ධි 8)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"තිරස්, මුදුන (සිද්ධි 18)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"තිරස්, මුදුන (සිද්ධි 24)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"සිද්ධි පමණි (සිද්ධි 4)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"සිද්ධි පමණි (සිද්ධි 9)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"සිද්ධි පමණි (සිද්ධි 16)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"සිද්ධි පමණි (සිද්ධි 25)\"\nBasic.Settings.General.ChannelName.stable=\"ස්ථායී\"\nBasic.Settings.General.ChannelDescription.stable=\"නවතම ස්ථායී නිකුතුව\"\nBasic.Settings.Appearance=\"පෙනුම\"\nBasic.Settings.Appearance.General=\"පොදු\"\nBasic.Settings.Appearance.General.Theme=\"තේමාව\"\nBasic.Settings.Appearance.General.Variant=\"ශෛලිය\"\nBasic.Settings.Appearance.General.NoVariant=\"ශෛලි නොතිබේ\"\nBasic.Settings.Stream=\"ප්‍රචාරය\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"සත්‍යාපනය භාවිතය\"\nBasic.Settings.Stream.Custom.Username=\"පරිශීලක නාමය\"\nBasic.Settings.Stream.Custom.Password=\"මුරපදය\"\nBasic.Settings.Stream.TTVAddon=\"ට්විච් සංවාද දිගු\"\nBasic.Settings.Stream.TTVAddon.None=\"කිසිත් නැත\"\nBasic.Settings.Stream.TTVAddon.Both=\"බෙටර්ටීටීවී හා ෆ්රැන්කර්ෆේස්\"\nBasic.Settings.Stream.MissingSettingAlert=\"මඟහැරුණු ප්‍රචාරය පිහිටුම\"\nBasic.Settings.Stream.StreamSettingsWarning=\"සැකසුම් අරින්න\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"ඒ.ස.නි. සහ ප්‍රචාරයේ යතුර නැත.\\n\\n'ප්‍රචාර' පටිත්තෙහි ඒ.ස.නි. හා ප්‍රචාරයේ යතුර යෙදීමට සැකසුම් විවෘත කරන්න.\"\nBasic.Settings.Stream.MissingUrl=\"ප්‍රචාරයේ ඒ.ස.නි. නැත.\\n\\n'ප්‍රචාරය' පටිය තුළ ඒ.ස.නි. යෙදීමට සැකසුම් විවෘත කරන්න.\"\nBasic.Settings.Stream.MissingStreamKey=\"ප්‍රචාරයේ යතුර නැත.\\n\\n'ප්‍රචාරය' පටිය තුළ ප්‍රචාරයේ යතුර යෙදීමට සැකසුම් විවෘත කරන්න.\"\nBasic.Settings.Stream.IgnoreRecommended=\"ප්‍රචාර සේවාවේ නිර්දේශිත සැකසුම නොසලකන්න\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"නිර්දේශිත සැකසුම් අභිබවන්න\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"අවවාදයයි: සේවාවේ සීමා නොසලකා හැරීම නිසා ප්‍රචාරයේ ගුණත්වය අඩුවීමට හෝ බාධා ඇති වීමට හැකිය.\\n\\nඉදිරියට?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"උපරිම දෘශ්‍ය බිටුඅනු.: %1 කි.බ./ත.\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"උපරිම ශ්‍රව්‍ය බිටුඅනු.: %1 කි.බ./ත.\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"උපරිම විභේදනය: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"උපරිම රාමු/ත.: %1\"\nBasic.Settings.Stream.ServiceCustomServer=\"අභිරුචි සේවාදායකය\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1 සබල කරන්න\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"ස්වයං\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"උපරිම දෘශ්‍ය පථ\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"ස්වයං\"\nBasic.Settings.Stream.AdvancedOptions=\"වැඩිදුර සැකසුම්\"\nBasic.Settings.Output=\"ප්‍රතිදානය\"\nBasic.Settings.Output.Format=\"පටිගත ආකෘතිය\"\nBasic.Settings.Output.Format.MKV=\"මාට්‍රෝක්‍ෂා දෘශ්‍යකය (.mkv)\"\nBasic.Settings.Output.Encoder.Video=\"දෘශ්‍ය ආකේතකය\"\nBasic.Settings.Output.Encoder.Audio=\"හඬ ආකේතකය\"\nBasic.Settings.Output.SelectDirectory=\"පටිගත නාමාවලිය තෝරන්න\"\nBasic.Settings.Output.DynamicBitrate=\"හිරවීම පාලනයට බිටුඅනු. ගතිකව වෙනස් කරන්න\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"හිරවීම පාලනයට බිටුඅනු. ගතිකව වෙනස් කරන්න (බීටා)\"\nBasic.Settings.Output.Mode=\"ප්‍රතිදාන ප්‍රකාරය\"\nBasic.Settings.Output.Mode.Simple=\"සරල\"\nBasic.Settings.Output.Mode.Adv=\"වැඩිදුර\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg ප්‍රතිදානය\"\nBasic.Settings.Output.UseReplayBuffer=\"යළි වාදන අන්තරාචය සබල කරන්න\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"උපරිම යළි වාදන කාලය\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"උපරිම මතකය\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"තක්සේරුගත මතක භාවිතය: මෙ.බ. %1\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"මතක භාවිතය තක්සේරු කළ නොහැකිය. උපරිම මතක සීමාව සකසන්න.\"\nBasic.Settings.Output.Simple.SavePath=\"පටිගත පෙත\"\nBasic.Settings.Output.Simple.RecordingQuality=\"පටිගත ගුණත්‍වය\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"ප්‍රචාරයේ ලෙසම\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"ඉහළ ගුණත්‍වය, මධ්‍යම ගොනුවක්\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"අතිසම ගුණත්‍වය, විශාල ගොනුවක්\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"අවවාදයයි: ප්‍රචාරයේ දෘශ්‍ය බිටුඅනු. %1 ලෙස සකසා ඇත, එය වත්මන් ප්‍රවාර සේවාව සඳහා ඉහළ සීමාවකි.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"අවවාදයයි: ප්‍රචාරයේ ශ්‍රව්‍ය බිටුඅනු. %1 ලෙස සකසා ඇත, එය වත්මන් ප්‍රවාර සේවාව සඳහා ඉහළ සීමාවකි.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"අවවාදයයි: පටිගතයේ ගුණත්වය “ප්‍රචාරයට සමාන” ලෙස සකසා ඇත්නම් පටිගතයට විරාම තැබීමට නොහැකියි.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"අවවාදයයි: ඔබ එකවර ප්‍රචාර සහ පටිගත කරන විට දී ප්‍රචාරයට වඩා වෙනස් ගුණත්වයකින් මෘදුකාංග ආකේතකයක් සමඟ පටිගත කරයි නම් අමතර ම.සැ.ඒ. (CPU) භාවිතයක් සිදු වේ.\"\nBasic.Settings.Output.Simple.Encoder.Software=\"මෘදුකාංගය (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"දෘඩාංගය (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"දෘඩාංගය (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"දෘඩාංගය (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"දෘඩාංගය (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"දෘඩාංගය (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"දෘඩාංගය (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"දෘඩාංගය (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"දෘඩාංගය (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"දෘඩාංගය (ඇපල්, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"දෘඩාංගය (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"මෘදුකාංගය (x264 අඩු ම.සැ.ඒ. භාවිතය පෙරසැකසීම, ගොනුවේ තරම විශාලයි)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (පෙරනිමි)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"ට්විච් ඉ.ම.දෘ.(VOD) පථය (පථ 2ක් ඇත)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"ශ්‍රව්‍ය පථය\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"නොගැළපෙන විභේදනය / රාමුඅනුපා.\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"මෙම ප්‍රචාර සේවාව ඔබගේ වත්මන් ප්‍රතිදාන විභේදනයට හා/හෝ රාමු අනුපාතයට සහාය නොදක්වයි. ඒවා ආසන්නතම ගැළපෙන අගයකට වෙනස් කෙරේ:\\n\\n%1\\n\\nඉදිරියට යාමට අවශ්‍යද?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"විභේදනය: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"රාමු/ත.: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"නොගැළපෙන ආකේතකයකි\"\nBasic.Settings.Output.VideoBitrate=\"දෘශ්‍ය බිටුඅනු.\"\nBasic.Settings.Output.AudioBitrate=\"ශ්‍රව්‍ය බිටුඅනු.\"\nBasic.Settings.Output.Reconnect=\"ස්වයංක්‍රීයව යළි සබැඳීම\"\nBasic.Settings.Output.RetryDelay=\"යළි උත්සාහය ප්‍රමාදය\"\nBasic.Settings.Output.MaxRetries=\"උපරිම යළි උත්සාහ\"\nBasic.Settings.Output.EncoderPreset=\"ආකේතක පෙරසැකසීම\"\nBasic.Settings.Output.CustomEncoderSettings=\"අභිරුචි ආකේතක සැකසුම්\"\nBasic.Settings.Output.NoSpaceFileName=\"හිස්තැන් නැතිව ගොනුවේ නම උත්පාදනය\"\nBasic.Settings.Output.Adv.Rescale=\"ප්‍රතිදාන යළිපරිමාණනය\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"අබලයි\"\nBasic.Settings.Output.Adv.AudioTrack=\"ශ්‍රව්‍ය පථය\"\nBasic.Settings.Output.Adv.Streaming=\"ප්‍රචාරය\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"ප්‍රචාර සැකසුම්\"\nBasic.Settings.Output.Adv.Audio.Track1=\"පථය 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"පථය 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"පථය 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"පථය 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"පථය 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"පථය 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"ට්විච් ඉ.ම.දෘ.(VOD) පථය\"\nBasic.Settings.Output.Adv.Encoder=\"ආකේතක සැකසුම්\"\nBasic.Settings.Output.Adv.Recording=\"පටිගතය\"\nBasic.Settings.Output.Adv.Recording.Settings=\"පටිගත සැකසුම්\"\nBasic.Settings.Output.Adv.Recording.RecType=\"පටිගත වර්ගය\"\nBasic.Settings.Output.Adv.Recording.Type=\"වර්ගය\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"සම්මත\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"අභිරුචි ප්‍රතිදානය (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(ප්‍රචාර ආකේතකය භාවිතය)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"ගොනු නාමයේ ආකෘතිය\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"ගොනුව තිබේ නම් උඩින් ලියන්න\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg ප්‍රතිදාන වර්ගය\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"ඒ.ස.නි. කට ප්‍රතිදානය\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"ගොනුවට ප්‍රතිදානය\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"සුලබ පටිගත ආකෘති\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"සියළු ගොනු\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"ගොනුවේ පෙත / ඒ.ස.නි.\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"අඩංගුවේ ආකෘතිය\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"ශ්‍රව්‍ය\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"දෘශ්‍ය\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"පෙරනිමි ආකෘතිය\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"අඩංගුවේ ආකෘතියෙහි විස්තරය\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"පෙරනිමි ආකේතකය\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"ආකේතකය අබලව\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"දෘශ්‍ය ආකේතකය\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"දෘශ්‍ය ආකේතක සැකසුම් (ඇත්නම්)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"හඬ ආකේතකය\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"ශ්‍රව්‍ය ආකේතක සැකසුම් (ඇත්නම්)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg සැකසුම්\"\nScreenshot=\"තිරසේයාව ප්‍රතිදානය\"\nScreenshot.SourceHotkey=\"තේරූ මූලාශ්‍රයේ තිරසේයාව\"\nScreenshot.StudioProgram=\"තිරසේයාව (වැඩසටහන)\"\nScreenshot.Preview=\"තිරසේයාව (පෙරදසුන)\"\nScreenshot.Scene=\"තිරසේයාව (සිද්ධිය)\"\nScreenshot.Source=\"තිරසේයාව (මූලාශ්‍රය)\"\nFilenameFormatting.TT.hh=\"පැය 𑇫𑇤 (24) ආකෘතිය (00-23)\"\nFilenameFormatting.TT.mm=\"විනාඩි (00-59)\"\nFilenameFormatting.TT.ss=\"තත්පර (00-59)\"\nFilenameFormatting.TT.A=\"සතියේ පූර්ණ නම\"\nFilenameFormatting.TT.B=\"මාසයේ පූර්ණ නම\"\nFilenameFormatting.TT.H=\"පැය 𑇫𑇤 (24) ආකෘතිය (00-23)\"\nFilenameFormatting.TT.I=\"පැය 𑇪𑇢 (12) ආකෘතිය (00-12)\"\nFilenameFormatting.TT.M=\"විනාඩි (00-59)\"\nFilenameFormatting.TT.p=\"පෙ.ව. හෝ ප.ව. සැකසීම\"\nFilenameFormatting.TT.S=\"තත්පර (00-59)\"\nFilenameFormatting.TT.Y=\"අවුරුද්ද\"\nFilenameFormatting.TT.FPS=\"තත්පරයකට රාමු\"\nFilenameFormatting.TT.VF=\"දෘශ්‍යක ආකෘතිය\"\nBasic.Settings.Video=\"දෘශ්‍ය\"\nBasic.Settings.Video.Adapter=\"දෘශ්‍ය අනුහුරුව\"\nBasic.Settings.Video.ScaledResolution=\"ප්‍රතිදානය (පරිමාණිතයි) විභේදනය\"\nBasic.Settings.Video.DownscaleFilter=\"අවපරිමාණන පෙරහන\"\nBasic.Settings.Video.FPS=\"රාමු/ත.\"\nBasic.Settings.Video.FPSCommon=\"පොදු රාමු/ත. අගයන්\"\nBasic.Settings.Video.Renderer=\"විදැහුම්කරු\"\nBasic.Settings.Video.InvalidResolution=\"වලංගු නොවන විභේදන අගයකි. [width]x[height] (එනම් 1920x1080) විය යුතුය\"\nBasic.Settings.Video.CurrentlyActive=\"දෘශ්‍ය ප්‍රතිදානය දැනට සක්‍රියයි. දෘශ්‍ය සැකසුම් වෙනස් කිරීමට සියළුම ප්‍රතිදාන අක්‍රිය කරන්න.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"බයිලයිනියර් (වේගවත්ම, පරිමාණනයේදී අපැහැදිලියි)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"බයිකියුබික් (තියුණු පරිමාණනය, නිදසුන් 16)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"ලැන්ක්සෝස් (තියුණු පරිමාණනය, නිදසුන් 36)\"\nBasic.Settings.Audio=\"ශ්‍රව්‍ය\"\nBasic.Settings.Audio.SampleRate=\"අනුපාත නිදසුන\"\nBasic.Settings.Audio.Channels=\"නාලිකා\"\nBasic.Settings.Audio.MeterDecayRate=\"ක්‍ෂය අනුපාතය\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"වේගවත්\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"මධ්‍යම (වර්ගය | PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"සෙමින් (වර්ගය | PPM)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"අවවාදයයි: වටේට ශබ්දයේ හඬ සබලයි.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"වටේට ශබ්දයේ හඬ සබල කරන්න ද?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"ඔබට වටේට ශබ්දයේ හඬ සබල කිරීමට වුවමනා ද?\"\nBasic.Settings.Audio.Devices=\"ගෝලීය ශ්‍රව්‍ය උපාංග\"\nBasic.Settings.Audio.DesktopDevice=\"වැඩතලයේ හඬ\"\nBasic.Settings.Audio.DesktopDevice2=\"වැඩතලයේ හඬ 2\"\nBasic.Settings.Audio.EnablePushToMute=\"එබීමෙන්-නිශ්ශබ්ද සබල කිරීම\"\nBasic.Settings.Audio.PushToMuteDelay=\"එබීමෙන්-නිශ්ශබ්ද ප්‍රමාදය\"\nBasic.Settings.Audio.EnablePushToTalk=\"එබීමෙන්-කථාව සබල කිරීම\"\nBasic.Settings.Audio.PushToTalkDelay=\"එබීමෙන්-කථාව ප්‍රමාදය\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[උපාංගය සම්බන්ධ නැත හෝ නොතිබේ]\"\nBasic.Settings.Audio.Disabled=\"අබලයි\"\nBasic.Settings.Accessibility=\"ප්‍රවේශ්‍යතාව\"\nBasic.Settings.Accessibility.ColorOverrides=\"වෙනස් පාට භාවිතා කරන්න\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"මූලාශ්‍ර මායිම (තේරීම)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"මූලාශ්‍ර මායිම (කප්පාදුව)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"පෙරනිමි\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"අභිරුචි\"\nBasic.Settings.Advanced=\"වැඩිදුර\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"පිටවීමේ දී සක්‍රිය ප්‍රතිදාන පෙන්වන්න\"\nBasic.Settings.Advanced.General.ProcessPriority=\"සැකසීමේ ප්‍රමුඛත්වය\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"ඉහළ\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"සාමාන්‍යට වඩා\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"සාමාන්‍ය\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"සාමාන්‍යට අඩු\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"නිද්‍රාශීලී\"\nBasic.Settings.Advanced.Video.ColorFormat=\"වර්ණ ආකෘතිය\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (බිටු-8)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"වර්ණ අවකාශය\"\nBasic.Settings.Advanced.Video.ColorRange=\"වර්ණ පරාසය\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"සීමාසහිත\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"පූර්ණ\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR සුදු මට්ටම\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"නිරීක්‍ෂණ උපාංගය\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"පෙරනිමි\"\nBasic.Settings.Advanced.StreamDelay=\"ප්‍රචාර ප්‍රමාදය\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"පරාසය\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"තක්සේරුගත මතක භාවිතය: මෙ.බ. %1\"\nBasic.Settings.Advanced.Network=\"ජාලය\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"ජාලය ප්‍රශස්තිකරණය සබල කරන්න\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"සම්ප්‍රේෂණ වේගය නියාමනය කිරීමෙන් ජාලයේ වෙනත් ගුප්තතාව සංවේදී යෙදුම් සඳහා RTMP ප්‍රතිදාන හිනෛෂී බවට පත් කිරීමට තැත් කරයි.\\nමෙය අස්ථිර සම්බන්ධතාවල රාමු පහත වැටීමේ අවදානම වැඩි කළ හැකිය.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"කෙටිමඟ නාභිගත හැසිරීම\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"කෙටිමං අබල නොකරන්න\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"ප්‍රධාන කවුළුවට නාභිගත වූ විට කෙටිමං අබල කරන්න\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"ප්‍රධාන කවුළුවට නාභිගත නැති විට කෙටිමං අබල කරන්න\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(mkv ලෙස පටිගතය)\"\nBasic.AdvAudio=\"වැඩිදුර ශ්‍රව්‍ය ගුණාංග\"\nBasic.AdvAudio.ActiveOnly=\"සක්‍රිය ප්‍රභව පමණි\"\nBasic.AdvAudio.Name=\"නම\"\nBasic.AdvAudio.Volume=\"ශබ්දය\"\nBasic.AdvAudio.VolumeSource=\"'%1' සඳහා ශබ්දය\"\nBasic.AdvAudio.Mono=\"ඒකීය\"\nBasic.AdvAudio.MonoSource=\"'%1' සඳහා ඒකීය අවමිශ්‍රණය\"\nBasic.AdvAudio.Balance=\"සංතුලනය\"\nBasic.AdvAudio.BalanceSource=\"'%1' සඳහා සංතුලනය\"\nBasic.AdvAudio.Monitoring=\"ශ්‍රව්‍ය නිරීක්‍ෂණය\"\nBasic.AdvAudio.Monitoring.None=\"දර්ශකය අක්‍රියයි\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"නිරීක්‍ෂණය පමණි (ප්‍රතිදානය නැත)\"\nBasic.AdvAudio.Monitoring.Both=\"දර්ශකය සහ ප්‍රතිදානය\"\nBasic.AdvAudio.MonitoringSource=\"'%1' සඳහා හඬ නිරීක්‍ෂණය\"\nBasic.AdvAudio.AudioTracks=\"පථ\"\nBasic.Settings.Hotkeys=\"කෙටිමං\"\nBasic.Settings.Hotkeys.Filter=\"පෙරහන\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"කෙටිමඟ අනුව පෙරන්න\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"මෙම කෙටිමඟ අන් ක්‍රියාමාර්ග සඳහා ද බෙදාගනු ලැබේ. ඝට්ටනය පෙන්වීමට ඔබන්න\"\nBasic.Settings.Hotkeys.PleaseWait=\"කෙටිමං පූරණය වෙමින්, රැඳෙන්න...\"\nBasic.Hotkeys.SelectScene=\"සිද්ධියට මාරුවීම\"\nBasic.SystemTray.Show=\"පෙන්වන්න\"\nBasic.SystemTray.Hide=\"සඟවන්න\"\nBasic.SystemTray.Message.Reconnecting=\"විසන්ධියි. යළි සම්බන්ධ වෙමින්...\"\nHotkeys.Insert=\"ඇතුළු කරන්න\"\nHotkeys.Delete=\"මකන්න\"\nHotkeys.Home=\"මුල\"\nHotkeys.End=\"නිමාව\"\nHotkeys.PageUp=\"පිටුව ඉහළට\"\nHotkeys.PageDown=\"පිටුව පහළට\"\nHotkeys.NumLock=\"අංක අගුල\"\nHotkeys.ScrollLock=\"අනුචලන අගුල\"\nHotkeys.CapsLock=\"ලොකු අකුරු\"\nHotkeys.Backspace=\"ආපසුයවනය\"\nHotkeys.Tab=\"පටිත්ත\"\nHotkeys.Print=\"මුද්‍රණය\"\nHotkeys.Pause=\"විරාමය\"\nHotkeys.Left=\"වම\"\nHotkeys.Right=\"දකුණ\"\nHotkeys.Up=\"ඉහළ\"\nHotkeys.Down=\"පහළ\"\nHotkeys.Windows=\"කවුළු\"\nHotkeys.Menu=\"වට්ටෝරුව\"\nHotkeys.Space=\"හිස්තැන\"\nHotkeys.NumpadNum=\"%1 ලකුණ\"\nHotkeys.NumpadMultiply=\"ගුණන ලකුණ\"\nHotkeys.NumpadDivide=\"බෙදීම ලකුණ\"\nHotkeys.NumpadAdd=\"එකතු ලකුණ\"\nHotkeys.NumpadSubtract=\"අඩු කිරීම\"\nHotkeys.NumpadDecimal=\"දශම ලකුණ\"\nHotkeys.AppleKeypadNum=\"%1 (යතුර)\"\nHotkeys.AppleKeypadMultiply=\"* (යතුර)\"\nHotkeys.AppleKeypadDivide=\"/ (යතුර)\"\nHotkeys.AppleKeypadAdd=\"+ (යතුර)\"\nHotkeys.AppleKeypadSubtract=\"- (යතුර)\"\nHotkeys.AppleKeypadDecimal=\". (යතුර)\"\nHotkeys.AppleKeypadEqual=\"= (යතුර)\"\nHotkeys.MouseButton=\"ඊතලය %1\"\nMute=\"නිහඬ\"\nUnmute=\"නොනිහඬ\"\nPush-to-mute=\"එබීමෙන්-නිශ්ශබ්ද\"\nPush-to-talk=\"එබීමෙන්-කථාව\"\nSceneItemShow=\"'%1' පෙන්වන්න\"\nSceneItemHide=\"'%1' සඟවන්න\"\nOutputWarnings.NoTracksSelected=\"ඔබ අවම වශයෙන් එක් පථයක් තේරිය යුතුය\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"ප්‍රතිදාන සැකසුම් දෝෂයකි\"\nOutputWarnings.CannotPause=\"අවවාදයයි: පටිගතයේ ආකේතකය “(ප්‍රචාර ආකේතකය භාවිතයෙන්)” ලෙස සකසා ඇත්නම් පටිගතයට විරාම තැබීමට නොහැකිය\"\nCodecCompat.Incompatible=\"(%1 සමඟ නොගැළපෙයි)\"\nCodecCompat.CodecPlaceholder=\"ආකේතකය තෝරන්න...\"\nCodecCompat.ContainerPlaceholder=\"ආකෘතිය තෝරන්න...\"\nCodecCompat.CodecMissingOnExit.Title=\"ආකේතකයක් තෝරා නැත\"\nCodecCompat.ContainerMissingOnExit.Title=\"ආකෘතියක් තෝරා නැත\"\nFinalScene.Title=\"සිද්ධිය මකන්න\"\nFinalScene.Text=\"අවම වශයෙන් එක් සිද්ධියක් තිබිය යුතුය.\"\nNoSources.Title=\"මූලාශ්‍ර නැත\"\nNoSources.Text=\"ඔබ තවමත් කිසිදු දෘශ්‍ය ප්‍රභවයක් එකතු කර නැති සෙයකි, එබැවින් ඔබට හිස් තිරයක් ප්‍රතිදානය කරනු ඇත. මෙය සිදු කිරීමට වුවමනා ද?\"\nNoSources.Text.AddSource=\"ඕනෑම විටෙක ප්‍රධාන කවුළුවෙහි ප්‍රභව කොටුව යටතේ තිබෙන + නිරූපකය එබීමෙන් ඔබට ප්‍රභව එකතු කළ හැකිය.\"\nNoSources.Label=\"ඔබ සතුව කිසිදු මූලාශ්‍රයක් නැත.\\nඑකක් එක් කිරීමට පහත + බොත්තම ඔබන්න\\nහෝ මෙහි දකුණින් ඔබන්න.\"\nChangeBG=\"වර්ණය සකසන්න\"\nCustomColor=\"අභිරුචි වර්ණය\"\nBrowserSource.EnableHardwareAcceleration=\"අතිරික්සු මූලාශ්‍ර දෘඩාංග ත්‍වරණය සබල කරන්න\"\nAbout=\"පිලිබඳව\"\nAbout.Info=\"ඕබීඑස් ස්ටුඩියෝ යනු නොමිලේ හා විවෘත මූලාශ්‍ර දෘශ්‍ය පටිගත හා සජීව විකාශ මෘදුකාංගයකි.\"\nAbout.Donate=\"දායකත්වයක් සපයන්න\"\nAbout.GetInvolved=\"නිමග්න වන්න\"\nAbout.Authors=\"කතුවරුන්\"\nAbout.License=\"බලපත්‍රය\"\nAbout.Contribute=\"ඕබීඑස් ව්‍යාපෘතියට සහාය වන්න\"\nAddUrl.Title=\"ඒ.ස.නි. න් මූලාශ්‍රය යොදන්න\"\nAddUrl.Text=\"ඒ.ස.නි. ක් ඕබීඑස් වෙත ඇද දමා ඇත. මෙය ස්වයංක්‍රීයව මූලාශ්‍රයක් ලෙස සබැඳිය යොදනු ඇත. ඉදිරියට?\"\nAddUrl.Text.Url=\"ඒ.ස.නි.: %1\"\nResizeOutputSizeOfSource.Continue=\"ඉදිරියට යාමට වුවමනාද?\"\nPreviewTransition=\"සංක්‍රාන්තියෙහි පෙරදසුන\"\nImporter=\"සිද්ධි එකතුව ආයාතකය\"\nImporter.SelectCollection=\"සිද්ධි එකතුවක් තෝරන්න\"\nImporter.Collection=\"සිද්ධි එකතුව\"\nImporter.HelpText=\"ඕබීඑස් හෝ වෙනත් වැඩසටහන් වලින් එකතු ආයාතයට මෙතනට ගොනු දමන්න.\"\nImporter.Path=\"එකතුවෙහි පෙත\"\nImporter.Program=\"අනාවරණය වූ යෙදුම\"\nImporter.AutomaticCollectionPrompt=\"ස්වයංක්‍රීයව සිද්ධි එකතු සෙවීම\"\nImporter.AutomaticCollectionText=\"සහාය දක්වන තෙවන පාර්ශවීය වැඩසටහන් වෙතින් ආයාත කළ හැකි සිද්ධි එකතු ස්වයංක්‍රීයව සොයා ගැනීමට හැකිය. ඕබීඑස් විසින් ඔබ සඳහා මෙය සිදු කරනවාට කැමතිද?\\n\\nපසුව සැකසුම් > පොදු > අයාතක තුළ මෙය වෙනස් කළ හැකිය.\"\nImporter.SelectFile=\"පිරික්සන්න...\"\nRestart=\"නැවත අරඹන්න\"\nNeedsRestart=\"ඕබීඑස් මැදිරිය යළි ඇරඹිය යුතුය. දැන් සිදු කිරීමට අවශ්‍යද?\"\nLoadProfileNeedsRestart=\"පැතිකඩෙහි ඕබීඑස් යළි ඇරඹීමට වුවමනා සැකසුම් අඩංගුය:\\n%1\\n\\n ඔබට මෙම සැකසුම් යෙදීම සඳහා නැවත ඇරඹීමට වුවමනා ද?\"\nContextBar.NoSelectedSource=\"මූලාශ්‍රයක් තෝරා නැත\"\nContextBar.MediaControls.PlayMedia=\"මාධ්‍ය වාදනය\"\nContextBar.MediaControls.PauseMedia=\"මාධ්‍ය විරාමය\"\nContextBar.MediaControls.StopMedia=\"මාධ්‍ය නවත්වන්න\"\nContextBar.MediaControls.RestartMedia=\"මාධ්‍ය යළි අරඹන්න\"\nContextBar.MediaControls.PlaylistNext=\"වාදන ලැයිස්තුවේ ඊළඟ\"\nContextBar.MediaControls.PlaylistPrevious=\"වාදන ලැයිස්තුවේ පෙර\"\nYouTube.Auth.Ok=\"සත්‍යාපනය සාර්ථකව නිමා විය.\\nදැන් මෙම පිටුව වසා දැමීමට හැකිය.\"\nYouTube.Auth.NoCode=\"සත්‍යාපන ක්‍රියාවලිය සම්පූර්ණ වී නැත.\"\nYouTube.Auth.NoChannels=\"තෝරාගත් ගිණුමෙහි කිසිදු නාලිකාවක් නැත\"\nYouTube.Auth.WaitingAuth.Title=\"යූටියුබ් පරිශීලක සත්‍යාපනය\"\nYouTube.Auth.WaitingAuth.Text=\"ඔබගේ බාහිර අතිරික්සුවෙන් සත්‍යාපනය සම්පූර්ණ කරන්න.<br>බාහිර අතිරික්සුව විවෘත නොවේ නම්, මෙම සබැඳිය අනුගමනය කර එය සම්පූර්ණ කරන්න:<br>%1\"\nYouTube.AuthError.Text=\"නාලිකාවේ තොරතුරු ගැනීමට අසමත් විය: %1.\"\nYouTube.Actions.WindowTitle=\"යූටියුබ් විකාශන සැකසුම - නාලිකාව: %1\"\nYouTube.Actions.CreateNewEvent=\"නව විකාශනයක් සාදන්න\"\nYouTube.Actions.ChooseEvent=\"පවතින විකාශනය තෝරන්න\"\nYouTube.Actions.Title=\"සිරැසිය*\"\nYouTube.Actions.MyBroadcast=\"මාගේ විකාශනය\"\nYouTube.Actions.Description=\"සවිස්තරය\"\nYouTube.Actions.Privacy=\"රහස්‍යතාව*\"\nYouTube.Actions.Privacy.Private=\"පෞද්ගලික\"\nYouTube.Actions.Privacy.Public=\"ප්‍රසිද්ධ\"\nYouTube.Actions.Privacy.Unlisted=\"ලැයිස්තුගත නොකළ\"\nYouTube.Actions.Category=\"ප්‍රවර්ගය\"\nYouTube.Actions.Thumbnail=\"සිඟිති රුව\"\nYouTube.Actions.Thumbnail.SelectFile=\"ගොනුව තෝරන්න...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"ගොනුවක් තෝරා නැත\"\nYouTube.Actions.Thumbnail.ClearFile=\"මකන්න\"\nYouTube.Actions.MadeForKids=\"මෙම දෘශ්‍යකය ළමුන් සඳහා ද?*\"\nYouTube.Actions.MadeForKids.Yes=\"ඔව්, එය ළමුන් සඳහා තනා ඇත\"\nYouTube.Actions.MadeForKids.No=\"නැහැ, එය ළමුන් සඳහා තැනූවක් නොවේ\"\nYouTube.Actions.AdditionalSettings=\"අතිරේක සැකසුම්\"\nYouTube.Actions.Latency=\"ගුප්තතාව\"\nYouTube.Actions.Latency.Normal=\"සාමාන්‍ය\"\nYouTube.Actions.Latency.Low=\"අඩු\"\nYouTube.Actions.Latency.UltraLow=\"අතිශය අඩු\"\nYouTube.Actions.EnableAutoStart=\"ස්වයං-ආරම්භය සබල කරන්න\"\nYouTube.Actions.EnableAutoStop=\"ස්වයං-නැවතීම සබල කරන්න\"\nYouTube.Actions.AutoStartStop.TT=\"මෙම නියමිත විකාශනය ස්වයංක්‍රීයව ආරම්භ කළ යුතුදැයි පෙන්නුම් කරයි\"\nYouTube.Actions.EnableDVR=\"ඩීවීආර් සබල කරන්න\"\nYouTube.Actions.360Video=\"360 දෘශ්‍යක\"\nYouTube.Actions.ScheduleForLater=\"පසුවට කල්තබන්න\"\nYouTube.Actions.RememberSettings=\"සැකසුම් මතක තබාගන්න\"\nYouTube.Actions.Create_Ready=\"විකාශනය සාදන්න\"\nYouTube.Actions.Create_GoLive=\"විකාශනය සාදා ප්‍රචාරය අරඹන්න\"\nYouTube.Actions.Choose_Ready=\"විකාශනය තෝරන්න\"\nYouTube.Actions.Choose_GoLive=\"විකාශනය තෝරා ප්‍රචාරය අරඹන්න\"\nYouTube.Actions.Create_Schedule=\"විකාශනය කල්තබන්න\"\nYouTube.Actions.Create_Schedule_Ready=\"කල්තබා විකාශනය තෝරන්න\"\nYouTube.Actions.Dashboard=\"යූටියුබ් ස්ටුඩියෝ අරින්න\"\nYouTube.Actions.Error.Title=\"සජීව විකාශනය සෑදීමේ දෝෂයකි\"\nYouTube.Actions.Error.Text=\"යූටියුබ් වෙත ප්‍රවේශ වීමේ දෝෂය '%1'.<br/>සවිස්තරාත්මක දෝෂ විස්තරයක් <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> හි තිබේ\"\nYouTube.Actions.Error.General=\"යූටියුබ් වෙත ප්‍රවේශ වීමේ දෝෂයකි. ඔබගේ ජාලයේ සම්බන්ධතාවය හෝ යූටියුබ් සේවාදායකය වෙත ප්‍රවේශය පරීක්‍ෂා කරන්න.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"විකාශනය සෑදීමේ දෝෂය '%1'.<br/>සවිස්තරාත්මක දෝෂ විස්තරයක් <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> හි තිබේ\"\nYouTube.Actions.Error.NoStreamCreated=\"ප්‍රචාරයක් නොසෑදිණි. ඔබගේ ගිණුම යළි සබඳින්න.\"\nYouTube.Actions.Error.YouTubeApi=\"යූටියුබ් යෙ.ක්‍ර.මු. දෝෂයකි. වැඩි විස්තර සඳහා සටහන් බලන්න.\"\nYouTube.Actions.Error.BroadcastNotFound=\"තෝරාගත් විකාශනය හමු නොවිණි.\"\nYouTube.Actions.Error.FileMissing=\"තෝරාගත් ගොනුව නොපවතී.\"\nYouTube.Actions.Error.FileOpeningFailed=\"තේරූ ගොනුව විවෘත කිරීමට අසමත්!\"\nYouTube.Actions.Error.FileTooLarge=\"තේරූ ගොනුව ඉතා විශාලයි (සීමාව: මෙ.බ. 2).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"විකාශනය සංක්‍රාන්තියට අසමත් විය: %1<br/><br/>මෙම දෝෂය දිගටම පවතී නම් <a href='https://studio.youtube.com/video/%2/livestreaming'>යූටියුබ් ස්ටුඩියෝ හි විකාශනය</a> විවෘත කර අතින් උත්සාහ කරන්න.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"විකාශනය අත්හදා බැලීමේ අදියරට මාරු වේ, යම් කාලයක් ගත වීමට හැකිය. තත්පර 10-30 කින් නැවත උත්සාහ කරන්න.\"\nYouTube.Actions.EventsLoading=\"සිදුවීම් ලැයිස්තුව පූරණය වෙමින්...\"\nYouTube.Actions.EventCreated.Title=\"සිදුවීම සෑදිණි\"\nYouTube.Actions.EventCreated.Text=\"සිදුවීම සාර්ථකව සෑදිණි.\"\nYouTube.Actions.Stream=\"ප්‍රචාරය\"\nYouTube.Actions.Stream.ScheduledFor=\"%1 සඳහා කල්තබා ඇත\"\nYouTube.Actions.Stream.Resume=\"බිඳ වැටුණු ප්‍රචාරය නැවතත්\"\nYouTube.Actions.Stream.YTStudio=\"යූටියුබ් ස්ටුඩියෝ වෙතින් ස්වයංක්‍රීයව සෑදිණි\"\nYouTube.Actions.Notify.CreatingBroadcast=\"නව සජීව විකාශනයක් සෑදෙමින්, රැඳී සිටින්න...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"අතින් ආරම්භයක් වුවමනාය\"\nYouTube.Actions.AutoStartStreamingWarning=\"මෙම සිදුවීම සඳහා ස්වයං ආරම්භය අබල කර ඇත, ඔබගේ විකාශනය ඇරඹීමට “සජීව යන්න” ඔබන්න.\"\nYouTube.Actions.AutoStopStreamingWarning=\"නැවත සම්බන්ධ වීමට නොහැකි වනු ඇත.<br>ඔබගේ ප්‍රචාරය නතර වන අතර ඔබ තවදුරටත් මාර්ගගත නොවේ.\"\nYouTube.Errors.liveStreamingNotEnabled=\"තෝරාගත් යූටියුබ් නාලිකාවෙහි සජීව ප්‍රචාර පහසුකම සබල කර නැත.<br/><br/>වැඩි විස්තර සඳහා <a href='https://www.youtube.com/features'>youtube.com/features</a> බලන්න.\"\nYouTube.Errors.livePermissionBlocked=\"තෝරාගත් යූටියුබ් නාලිකාවට සජීව පුචාර පහසුකම නොතිබේ.<br/>ඔබගේ නාලිකාවේ සැකසුම් තුළ එය සබල කිරීමෙන් පසු සජීව ප්‍රචාර පහසුකම ලැබීමට පැය 24 ක් දක්වා ගත වීමට හැකි බව කරුණාවෙන් සලකන්න.<br/><br/>විස්තර සඳහා <a href='https://www.youtube.com/features'>youtube.com/features</a> බලන්න.\"\nYouTube.Errors.errorStreamInactive=\"යූටියුබ් වෙත ඔබගේ ප්‍රචාරයේ දත්ත නොලැබෙයි. වින්‍යාසය පරීක්‍ෂා කර උත්සාහ කරන්න.\"\nConfigDownload.WarningMessageTitle=\"අවවාදයයි\"\n"
  },
  {
    "path": "frontend/data/locale/sk-SK.ini",
    "content": "Language=\"Slovenčina\"\nApply=\"Použiť\"\nCancel=\"Zrušiť\"\nClose=\"Zatvoriť\"\nSave=\"Uložiť\"\nDiscard=\"Zahodiť\"\nDisable=\"Zakázať\"\nYes=\"Áno\"\nNo=\"Nie\"\nAdd=\"Pridať\"\nRemove=\"Odobrať\"\nRename=\"Premenovať\"\nInteract=\"Interakcia\"\nFilters=\"Filtre\"\nProperties=\"Vlastnosti\"\nMoveUp=\"Posunúť vyššie\"\nMoveDown=\"Posunúť nižšie\"\nSettings=\"Nastavenia\"\nDisplay=\"Monitor\"\nName=\"Meno\"\nExit=\"Ukončiť\"\nMixer=\"Zvukový zmiešavač\"\nBrowse=\"Prehľadávať\"\nDroppedFrames=\"Zahodené snímky %1 (%2%)\"\nProjector.Open.Program=\"Otvoriť programový projektor\"\nProjector.Open.Preview=\"Otvoriť náhľadový projektor\"\nProjector.Open.Scene=\"Otvoriť scénový projektor\"\nProjector.Open.Source=\"Otvoriť zdrojový projektor\"\nProjector.Open.Multiview=\"Otvoriť Multiview\"\nProjector.Display=\"Monitor: %1\"\nProjector.Window=\"Nové okno\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Scéna %1\"\nProjector.Title.Source=\"Zdroj: %1\"\nProjector.ResizeWindowToContent=\"Upraviť veľkosť okna podľa obsahu\"\nClear=\"Vyčistiť\"\nRevert=\"Vrátiť späť\"\nShow=\"Ukázať\"\nHide=\"Skryť\"\nUnhideAll=\"Odkryť Všetky\"\nUntitled=\"Nepomenovaný\"\nNew=\"Nový\"\nDuplicate=\"Duplikovať\"\nEnable=\"Povoliť\"\nDisableOSXVSync=\"Vypnúť macOS V-Sync\"\nResetOSXVSyncOnExit=\"Resetovať macOS V-Sync po zavretí\"\nHighResourceUsage=\"Kódovanie preťažené! Zvážte nižšie nastavenia videa alebo použitie rýchlejšieho presetu kódovania.\"\nTransition=\"Prechod\"\nQuickTransitions=\"Rýchle prechody\"\nFadeToBlack=\"Prechod do čiernej\"\nLeft=\"Vľavo\"\nRight=\"Vpravo\"\nTop=\"Hore\"\nBottom=\"Dole\"\nReset=\"Resetovať\"\nHours=\"Hodín\"\nMinutes=\"Minút\"\nSeconds=\"Sekúnd\"\nDeprecated=\"Zastaralé\"\nReplayBuffer=\"Záznam do pamäte\"\nImport=\"Importovať\"\nExport=\"Exportovať\"\nCopy=\"Kopírovať\"\nPaste=\"Vložiť\"\nPasteReference=\"Vložiť (odkaz)\"\nPasteDuplicate=\"Vložiť (duplikát)\"\nRemuxRecordings=\"Remuxovať nahrávky\"\nNext=\"Ďalej\"\nBack=\"Späť\"\nDefaults=\"Predvolené\"\nRestoreDefaults=\"Predvolené hodnoty\"\nHideMixer=\"Skryť v zmiešavači\"\nTransitionOverride=\"Nahradiť prechod\"\nShowTransition=\"Ukázať prechod\"\nHideTransition=\"Skryť prechod\"\nNone=\"Nič\"\nStudioMode.Preview=\"Náhľad\"\nStudioMode.PreviewSceneName=\"Ukážka: %1\"\nShowInMultiview=\"Zobraziť v Multiview\"\nVerticalLayout=\"Vertikálne rozloženie\"\nGroup=\"Skupina\"\nDoNotShowAgain=\"Znovu neukazovať\"\nDefault=\"(Predvolené)\"\nCalculating=\"Kalkulovanie...\"\nFullscreen=\"Celá obrazovka\"\nWindowed=\"Okno\"\nRefreshBrowser=\"Obnoviť\"\nAspectRatio=\"Pomer strán <b>%1:%2</b>\"\nLockVolume=\"Uzamknúť hlasitosť\"\nLogViewer=\"Prehliadač logov\"\nShowOnStartup=\"Ukázať pri spustení\"\nOpenFile=\"Otvoriť súbor\"\nAddScene=\"Pridať scénu\"\nAddSource=\"Pridať zdroj\"\nRemoveScene=\"Odstrániť vybranú scénu\"\nRemoveSource=\"Odstrániť vybraný zdroj(e)\"\nMoveSceneUp=\"Posunúť scénu nahor\"\nMoveSceneDown=\"Posunúť scénu nadol\"\nMoveSourceUp=\"Posunúť zdroj(e) nahor\"\nMoveSourceDown=\"Posunúť zdroj(e) nadol\"\nSourceProperties=\"Otvoriť vlastnosti zdroja\"\nSourceFilters=\"Otvoriť filtre zdroja\"\nMixerToolbarMenu=\"Menu zvukového zmiešavača\"\nSceneFilters=\"Otvoriť filtre scény\"\nList=\"Zoznam\"\nGrid=\"Mriežka\"\nAutomatic=\"Automatický\"\nComingSoon=\"Už čoskoro\"\nPluginsFailedToLoad.Title=\"Chyba pri načítaní pluginu\"\nPluginsFailedToLoad.Text=\"Nasledujúce OBS pluginy sa nepodarilo načítať:\\n\\n%1\\nProsím aktualizujte alebo odstráňte tieto pluginy.\"\nAlreadyRunning.Title=\"OBS je už spustený\"\nAlreadyRunning.Text=\"Aplikácia OBS je už spustená! Prosím vypnite všetky existujúce inštancie OBS pred pokusom o spustenie novej inštancie. Ak máte OBS minimalizovaný do systémovej lišty, prosím skontrolujte, či tam stále beží.\"\nAlreadyRunning.LaunchAnyway=\"Napriek tomu spustiť\"\nCrashHandling.Dialog.Title=\"OBS Štúdio havária zistená\"\nCrashHandling.Labels.Text=\"OBS Štúdio nebolo poriadne vypnuté.\\n\\nSpustiť v núdzovom režime (pluginy tretích strán, skriptovanie, a websockety vypnuté)?\"\nCrashHandling.Labels.PrivacyNotice=\"Môžete tiež zvoliť automatické nahrávanie najnovšej správy o havárií pre OBSProject.<br /><br />Prosím prečítajte si <a href='https://obsproject.com/privacy-policy'>Zásady ochrany súkromia</a> pred nahrávaním akýchkoľvek súborov a venujte zvláštnu pozornosť častiam týkajúcim sa nahrávania súborov.\"\nCrashHandling.Checkbox.SendReport=\"Prečítal(a) som si zásady ochrany osobných údajov a súhlasím s nahraním.\"\nCrashHandling.Buttons.LaunchSafe=\"Spustiť v núdzovom režime\"\nCrashHandling.Buttons.LaunchNormal=\"Spustiť v normálnom režime\"\nCrashHandling.Errors.UploadJSONError=\"Pri nahrávaní najnovšieho logu o havárií nastala chyba. Prosím skúste znova neskôr.\"\nCrashHandling.Errors.Title=\"Chyba pri nahrávaní logu havárie\"\nSafeMode.Restart=\"Chcete reštartovať OBS v núdzovom režime (pluginy tretích strán, skriptovanie, a websockety vypnuté)?\"\nSafeMode.RestartNormal=\"Chcete reštartovať OBS v normálnom režime?\"\nChromeOS.Title=\"Nepodporovaná platforma\"\nChromeOS.Text=\"Vyzerá to tak, že OBS beží v ChromeOS kontajneri. Táto platforma nie je podporovaná.\"\nWine.Title=\"Wine zistený\"\nWine.Text=\"Spúšťanie OBS cez Wine nie je podporované a veľa funkcií ako zachytávanie alebo zdroje zariadení nebudú fungovať alebo iba čiastočne.<br><br>Je odporúčané používať natívnu verziu OBS, napríklad <a href='https://flathub.org/apps/details/com.obsproject.Studio'>našu Flatpak verziu</a> alebo balíky vášho operačného systému.\"\nDockCloseWarning.Title=\"Zatvorenie dokovateľného okna\"\nDockCloseWarning.Text=\"Práve ste zatvorili dokovateľné okno. Ak ho chcete znovu zobraziť, použite menu Doky v menu paneli.\"\nExtraBrowsers=\"Vlastné doky prehliadača\"\nExtraBrowsers.Info=\"Pridajte doky zadaním mena a adresy, potom kliknite Aplikovať alebo Zavrieť pre otvorenie dokov. Pridať alebo odstrániť doky môžete kedykoľvek.\"\nExtraBrowsers.DockName=\"Názov doku\"\nAuth.Authing.Title=\"Autentifikácia...\"\nAuth.Authing.Text=\"Prebieha overovanie so službou %1, čakajte...\"\nAuth.AuthFailure.Title=\"Overenie zlyhalo\"\nAuth.AuthFailure.Text=\"Zlyhalo overenie so službou %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Požadovaná autentifikácia\"\nAuth.InvalidScope.Text=\"Požiadavky na overenie pre %1 sa zmenili. Niektoré funkcie nemusia byť k dispozícii.\"\nAuth.LoadingChannel.Title=\"Načítavajú sa informácie o kanáli...\"\nAuth.LoadingChannel.Text=\"Načítavajú sa informácie o kanáli pre službu %1, prosím čakajte...\"\nAuth.LoadingChannel.Error=\"Nedali sa získať informácie o kanáli.\"\nAuth.ChannelFailure.Title=\"Načítanie kanálu zlyhalo\"\nAuth.ChannelFailure.Text=\"Nepodarilo sa načítať informácie o kanáli pre službu %1\\n\\n%2: %3\"\nAuth.StreamInfo=\"Informácie o vysielaní\"\nTwitchAuth.Stats=\"Twitch štatistiky\"\nTwitchAuth.Feed=\"Kanál aktivít v službe Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Nemožno získať vysielací kľúč\"\nTwitchAuth.TwoFactorFail.Text=\"OBS sa nedokáže pripojiť k vášmu Twitch účtu. Prosím, uistite sa, že máte v <a href='https://www.twitch.tv/settings/security'>bezpečnostných nastaveniach služby Twitch</a> nastavené dvojfaktorové overenie, ktoré je nutné pre vysielanie.\"\nRestreamAuth.Channels=\"Kanály Restream\"\nCopy.Filters=\"Kopírovať filtre\"\nPaste.Filters=\"Vložiť filtre\"\nBrowserPanelInit.Title=\"Načítavam prehliadač...\"\nBrowserPanelInit.Text=\"Prehliadač sa načítava, prosím čakajte...\"\nBandwidthTest.Region=\"Región\"\nBandwidthTest.Region.US=\"Spojené Štáty Americké\"\nBandwidthTest.Region.EU=\"Európa\"\nBandwidthTest.Region.Asia=\"Ázia\"\nBandwidthTest.Region.Other=\"Iné\"\nBasic.AutoConfig=\"Sprievodca automatickou konfiguráciou\"\nBasic.AutoConfig.ApplySettings=\"Použiť nastavenia\"\nBasic.AutoConfig.StartPage=\"Informácie o používaní\"\nBasic.AutoConfig.StartPage.SubTitle=\"Vyberte na čo chcete program použiť\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimalizovať pre streamovanie, nahrávanie je sekundárne\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimalizovať len pre nahrávanie, streamovať nebudem\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Budem používať iba virtuálnu kameru\"\nBasic.AutoConfig.VideoPage=\"Nastavenia videa\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Vyberte nastavenia videa, ktoré by ste chceli použiť\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Použiť aktuálne (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Monitor %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Použiť aktuálne (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 alebo 30, ale radšej 60, ak je to možné\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 alebo 30, ale radšej vysoké rozlíšenie\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Poznámka: rozlíšenie (základné) plátna nie je nevyhnutne rovnaké ako rozlíšenie, v ktorom budete vysielať alebo nahrávať. Skutočné vysielacie/nahrávacie rozlíšenie môže byť zmenšené preto, aby sa znížilo používanie dát alebo zdrojov.\"\nBasic.AutoConfig.StreamPage=\"Informácie o streame\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Prosím, zadajte svoje údaje o vysielaní\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Pripojiť účet (odporúčané)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Odpojiť účet\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Odpojiť účet?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Táto zmena sa uplatní okamžite. Naozaj chcete odpojiť svoj účet?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Dostať Streamovací Kľúč\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Viac informácií\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Použiť kľúč vysielania\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Použiť streamovací kľúč (pokročilé)\"\nBasic.AutoConfig.StreamPage.Service=\"Služba\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Zobraziť všetky...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Vlastné...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Streamovací kľúč\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: zadajte prístupovú frázu šifrovania.\\nRTMP: zadajte kľúč poskytnutý danou službou.\\nSRT: zadajte streamid ak ho daná služba používa.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kľúč enkodéra\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Nositeľský token\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Pripojený účet\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Odhadnúť bitrate pomocou testu rýchlosti pripojenia (môže to trvať pár minút)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Preferovať hardvérové enkódovanie\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Hardvérové enkódovanie eliminuje väčšinu využitia CPU, ale na dosiahnutie rovnakej kvality videa môže vyžadovať väčší bitrate.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Upozornenie o streame\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Test rýchlosti pripojenia vysiela obraz bez zvuku s náhodnými dátami. Odporúčame dočasne zakázať záznam vysielaní a nastaviť živý prenos ako súkromný, kým sa celý test nedokončí. Pokračovať?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Testovať %1\"\nBasic.AutoConfig.TestPage=\"Konečné výsledky\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Program práve vykonáva sériu testov pre odhad optimálneho nastavenia\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testovanie dokončené\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Prebieha testovanie rýchlosti pripojenia. Toto môže chvíľku trvať...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Žiaden výstup pre protokol tejto služby nebol nájdený\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Pripájanie k: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Nepodarilo sa pripojiť k žiadnemu serveru. Skontrolujte vaše internetové pripojenie a skúste to znovu.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testovanie rýchlosti pripojenia pre: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testovanie streamovacieho enkodéra, toto môže minútku trvať...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testovanie nahrávacieho enkodéra, toto môže minútku trvať...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Nepodarilo sa spustiť enkodér\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testovanie %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Kodér vysielania\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Kodér nahrávania\"\nBasic.AutoConfig.TestPage.Result.Header=\"Program zistil, že nasledujúce nastavenia by mohli byť pre vás najvhodnejšie:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Pre použitie týchto nastavení kliknite na Použiť. Pre úpravu nastavení sprievodcu kliknite na Späť. Ak si želáte nastaviť program manuálne, kliknite na Zrušiť a následne na tlačidlo Nastavenia.\"\nBasic.AutoConfig.Info=\"Sprievodca automatickou konfiguráciou určí najlepšie nastavenia na základe špecifikácií vášho počítača a rýchlosti internetu.\"\nBasic.AutoConfig.RunAnytime=\"Túto funkciu môžete spustiť kedykoľvek v ponuke Nástroje.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Streamovacie (škálované) rozlíšenie\"\nBasic.Stats=\"Štatistiky\"\nBasic.Stats.CPUUsage=\"Využitie CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Dostupné miesto na disku\"\nBasic.Stats.MemoryUsage=\"Využitie pamäte\"\nBasic.Stats.AverageTimeToRender=\"Priemerný čas vykreslenia snímku\"\nBasic.Stats.SkippedFrames=\"Preskočené snímky kvôli chybe kódovania\"\nBasic.Stats.MissedFrames=\"Nevyužité snímky kvôli chybe vykresľovania\"\nBasic.Stats.Output.Recording=\"Nahrávanie\"\nBasic.Stats.Status=\"Stav\"\nBasic.Stats.Status.Recording=\"Nahrávanie\"\nBasic.Stats.Status.Live=\"NAŽIVO\"\nBasic.Stats.Status.Reconnecting=\"Opätovné pripájanie\"\nBasic.Stats.Status.Inactive=\"Neaktívne\"\nBasic.Stats.Status.Active=\"Aktívne\"\nBasic.Stats.DroppedFrames=\"Stratené snímky (sieť)\"\nBasic.Stats.MegabytesSent=\"Celkový dátový výstup\"\nBasic.Stats.DiskFullIn=\"Disk plný za (približne)\"\nBasic.Stats.DiskFullIn.Text=\"%1 Hodín, %2 Minút\"\nBasic.Stats.ResetStats=\"Vynulovať štatistiky\"\nResetUIWarning.Title=\"Naozaj chcete obnoviť používateľské rozhranie?\"\nResetUIWarning.Text=\"Obnovením používateľského rozhrania sa skryjú ďalšie doky. Pre ich opätovné zobrazenie ich budete musieť odkryť v ponuke Doky.\\n\\nNaozaj chcete resetovať používateľské rozhranie?\"\nUpdater.Title=\"Nová aktualizácia k dispozícii\"\nUpdater.Text=\"Je dostupná nová aktualizácia:\"\nUpdater.UpdateNow=\"Aktualizovať teraz\"\nUpdater.RemindMeLater=\"Pripomenúť neskôr\"\nUpdater.Skip=\"Preskočiť verziu\"\nUpdater.NoUpdatesAvailable.Title=\"Neboli nájdené žiadne aktualizácie\"\nUpdater.NoUpdatesAvailable.Text=\"Nie sú momentálne k dispozícii žiadne aktualizácie\"\nUpdater.BranchNotFound.Title=\"Kanál aktualizácií odstránený\"\nUpdater.BranchNotFound.Text=\"Váš vybraný kanál aktualizácií už nie je dostupný, OBS bol obnovený na štandardné nastavenie.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Kontrola integrity nedostupná\"\nUpdater.RepairButUpdatesAvailable.Text=\"Kontrola súborovej integrity je možná iba pre poslednú dostupnú verziu. Použite Pomoc → Skontrolovať aktualizácie pre overenie a aktualizovanie OBS inštalácie.\"\nUpdater.RepairConfirm.Title=\"Potvrdenie kontroly integrity\"\nUpdater.RepairConfirm.Text=\"Spustenie kontroly integrity oskenuje vašu OBS inštaláciu pre poškodenie a znovustiahne poškodené/upravené súbory. Toto môže chvíľu trvať.\\n\\nPrajete si pokračovať?\"\nUpdater.FailedToLaunch=\"Nepodarilo sa spustiť aktualizátor\"\nQuickTransitions.SwapScenes=\"Vymeniť náhľad/programové scény po prechode\"\nQuickTransitions.SwapScenesTT=\"Vymení náhľad a programové scény po prechode (ak pôvodná programová scéna stále existuje).\\nToto nevráti žiadne zmeny vykonané v pôvodnej programovej scéne.\"\nQuickTransitions.DuplicateScene=\"Duplikovať scénu\"\nQuickTransitions.DuplicateSceneTT=\"Pri úprave rovnakej scény umožňuje úpravu pozície/viditeľnosti zdrojov bez úpravy programového výstupu.\\nPre úpravu vlastností zdrojov bez úpravy programového výstupu povoľte možnosť \\\"Duplikovať zdroje\\\".\\nZmena tejto hodnoty spôsobí resetovanie súčasnej programovej scény (ak stále existuje).\"\nQuickTransitions.EditProperties=\"Duplikovať zdroje\"\nQuickTransitions.EditPropertiesTT=\"Pri úprave rovnakej scény umožňuje nastavenie zdrojov bez úpravy programového výstupu.\\nTáto funkcia môže byť použitá, len ak je zapnutá možnosť \\\"Duplikovať scénu\\\".\\nNiektoré zdroje (napr. zachytávacie zdroje či mediálne zdroje) túto funkciu nepodporujú a nemôžu byť upravované samostatne.\\nPo zmene tejto hodnoty bude súčasná programová scéna resetovaná (ak stále existuje).\\n\\nVarovanie: Z dôvodu duplikácie zdrojov môže táto funkcia vyžadovať viac systémových prostriedkov.\"\nQuickTransitions.HotkeyName=\"Rýchly prechod: %1\"\nBasic.AddTransition=\"Pridať nastaviteľný prechod\"\nBasic.RemoveTransition=\"Odstrániť nastaviteľný prechod\"\nBasic.TransitionProperties=\"Vlastnosti prechodu\"\nBasic.SceneTransitions=\"Prechody medzi scénami\"\nBasic.TransitionDuration=\"Trvanie\"\nBasic.TogglePreviewProgramMode=\"Štúdiový režim\"\nBasic.EnablePreviewProgramMode=\"Zapnúť štúdiový režim\"\nBasic.DisablePreviewProgramMode=\"Vypnúť štúdiový režim\"\nUndo.Undo=\"Vrátiť späť\"\nUndo.Redo=\"Zopakovať\"\nUndo.Add=\"Pridanie '%1'\"\nUndo.Delete=\"Zmazanie '%1'\"\nUndo.Rename=\"Premenovanie '%1'\"\nUndo.SceneCollection.Switch=\"Prepnutie na '%1'\"\nUndo.Item.Undo=\"Vrátiť späť %1\"\nUndo.Item.Redo=\"Zopakovať %1\"\nUndo.Sources.Multi=\"Zmazanie %1 zdroja/ov\"\nUndo.Filters=\"Zmeny filtrov na '%1'\"\nUndo.Filters.Paste.Single=\"Vloženie filtra '%1' do '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopírovanie filtrov z '%1' do '%2'\"\nUndo.Transform=\"Transformáciu zdroja(ov) v '%1'\"\nUndo.Transform.Paste=\"Vloženie transformácie v '%1'\"\nUndo.Transform.Rotate=\"Rotáciu v '%1'\"\nUndo.Transform.Reset=\"Reset transformácie v '%1'\"\nUndo.Transform.HFlip=\"Horizontálne preklopenie v '%1'\"\nUndo.Transform.VFlip=\"Vertikálne preklopenie v '%1'\"\nUndo.Transform.FitToScreen=\"Prispôsobenie obrazovky v '%1'\"\nUndo.Transform.StretchToScreen=\"Roztiahnutie na celú obrazovku v '%1'\"\nUndo.Transform.Center=\"Centrovanie v '%1'\"\nUndo.Transform.VCenter=\"Vertikálne centrovanie v '%1'\"\nUndo.Transform.HCenter=\"Horizontálne centrovanie v '%1'\"\nUndo.Volume.Change=\"Zmenu hlasitosti na '%1'\"\nUndo.Volume.Mute=\"Stlmiť '%1'\"\nUndo.Volume.Unmute=\"Odtlmiť '%1'\"\nUndo.Balance.Change=\"Zmena vyváženia zvuku na '%1'\"\nUndo.SyncOffset.Change=\"Zmena oneskorenia synch. zvuku na '%1'\"\nUndo.MonitoringType.Change=\"Zmenu monitorovania zvuku pre '%1'\"\nUndo.Mixers.Change=\"Zmenu zvukových zmiešavačov pre '%1'\"\nUndo.ForceMono.On=\"Zapnutie vynútenia mono na '%1'\"\nUndo.ForceMono.Off=\"Vypnutie vynútenia mono na '%1'\"\nUndo.Properties=\"Zmenu vlastností na '%1'\"\nUndo.Scene.Duplicate=\"Duplikáciu scény '%1'\"\nUndo.ShowTransition=\"Zobrazenie prechodu on '%1'\"\nUndo.HideTransition=\"Skrytie prechodu on '%1'\"\nUndo.ShowSceneItem=\"Zobrazenie '%1' v '%2'\"\nUndo.HideSceneItem=\"Skrytie '%1' v '%2'\"\nUndo.ReorderSources=\"Zoradenie zdrojov v '%1'\"\nUndo.MoveUp=\"Presunutie '%1' hore v '%2'\"\nUndo.MoveDown=\"Presunutie '%1' dole v '%2'\"\nUndo.MoveToTop=\"Presunutie '%1' navrch v '%2'\"\nUndo.MoveToBottom=\"Presunutie '%1' nadol v '%2'\"\nUndo.PasteSource=\"Vloženie zdroja(ov) do ' %1'\"\nUndo.PasteSourceRef=\"Vloženie referencie(í) zdroja do '%1'\"\nUndo.GroupItems=\"Zoskupenie položiek do '%1'\"\nTransitionNameDlg.Text=\"Prosím, zadajte názov prechodu\"\nTransitionNameDlg.Title=\"Názov prechodu\"\nTitleBar.SafeMode=\"NÚDZOVÝ REŽIM\"\nTitleBar.PortableMode=\"Prenosný režim\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Scéna\"\nNameExists.Title=\"Názov už existuje\"\nNameExists.Text=\"Tento názov sa už používa.\"\nNoNameEntered.Title=\"Prosím, zadajte platný názov\"\nNoNameEntered.Text=\"Nemôžete použiť prázdne názvy.\"\nConfirmStart.Title=\"Spustiť stream?\"\nConfirmStart.Text=\"Naozaj chcete spustiť stream?\"\nConfirmStop.Title=\"Zastaviť stream?\"\nConfirmStop.Text=\"Naozaj chcete zastaviť stream?\"\nConfirmStopRecord.Title=\"Zastaviť nahrávanie?\"\nConfirmStopRecord.Text=\"Naozaj si želáte zastaviť nahrávanie?\"\nConfirmBWTest.Title=\"Spustiť test šírky pásma siete?\"\nConfirmBWTest.Text=\"Vaše OBS je nakonfigurované pre režim testovania šírky pásma. Toto umožní zistiť parametre vašej siete bez spustenia živého vysielania na vašom kanáli. Po dokončení testovania musíte tento režim zakázať, ináč diváci neuvidia vami prenášaný obsah.\\n\\nNaozaj si želáte pokračovať?\"\nConfirmExit.Title=\"Aktívne výstupy\"\nConfirmExit.Text=\"OBS je momentálne stále aktívne. Všetky streamy/nahrávania budu zastavené.\"\nConfirmRemove.Title=\"Potvrdenie odobratia\"\nConfirmRemove.Text=\"Ste si istí, že chcete odstrániť '%1'?\"\nConfirmRemove.TextMultiple=\"Naozaj si prajete odstrániť %1 položiek?\"\nConfirmReset.Title=\"Obnoviť vlastnosti\"\nConfirmReset.Text=\"Ste si istí, že chcete obnoviť aktuálne vlastnosti na ich pôvodné hodnoty?\"\nOutput.StartStreamFailed=\"Nepodarilo sa spustiť streamovanie\"\nOutput.StartRecordingFailed=\"Nepodarilo sa spustiť nahrávanie\"\nOutput.StartReplayFailed=\"Nepodarilo sa spustiť záznam do pamäte\"\nOutput.StartVirtualCamFailed=\"Nepodarilo sa spustiť virtuálnu kameru\"\nOutput.StartFailedGeneric=\"Nastala chyba pri spúšťaní nahrávania. Podrobnosti nájdete v textovom logu.\\n\\nPoznámka: Ak používate NVENC alebo AMD enkodér, uistite sa, že používate najnovšiu verziu grafického ovládača.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Nemožno ukladať záznamy pri zapauzovaní\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Upozornenie: Záznamy nemôžu byť ukladané, keď je pozastavené nahrávanie.\"\nOutput.ConnectFail.Title=\"Spojenie sa nepodarilo\"\nOutput.ConnectFail.BadPath=\"Neplatná cesta alebo URL. Prosím, skontrolujte, či sú vaše nastavenia správne.\"\nOutput.ConnectFail.ConnectFailed=\"Spojenie so serverom sa nepodarilo\"\nOutput.ConnectFail.InvalidStream=\"K nastavenému kanálu alebo kľúču sa nedá pristupovať. Skontrolujte, prosím, či je váš vysielací kľúč správny. Ak áno, mohol nastať problém s pripojením k serveru.\"\nOutput.ConnectFail.HdrDisabled=\"HDR výstup je momentálne vypnutý pre tento výstup.\"\nOutput.ConnectFail.Error=\"Pri pokuse o pripojenie k serveru nastala neočakávaná chyba. Ďalšie informácie nájdete v textovom logu.\"\nOutput.ConnectFail.Disconnected=\"Odpojený od servera.\"\nOutput.StreamEncodeError.Title=\"Chyba kódovania\"\nOutput.StreamEncodeError.Msg=\"Nastala chyba pri kódovaní počas vysielania.\"\nOutput.StreamEncodeError.Msg.LastError=\"Nastala chyba enkodéra počas streamovania:<br><br>%1\"\nOutput.RecordFail.Title=\"Nepodarilo sa spustiť nahrávanie\"\nOutput.RecordFail.Unsupported=\"Výstupný formát nie je podporovaný alebo nepodporuje viac ako jednu zvukovú stopu. Skontrolujte nastavenia a skúste to znovu.\"\nOutput.RecordNoSpace.Title=\"Nedostatok miesta na disku\"\nOutput.RecordNoSpace.Msg=\"Pre pokračovanie nahrávania nie je dostatok miesta na disku.\"\nOutput.RecordError.Title=\"Chyba nahrávania\"\nOutput.RecordError.Msg=\"Pri nahrávaní došlo k nešpecifikovanej chybe.\"\nOutput.RecordError.EncodeErrorMsg=\"Nastala chyba pri kódovaní počas nahrávania.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Nastala chyba enkodéra počas nahrávania:<br><br>%1\"\nOutput.BadPath.Title=\"Nesprávna cesta k súboru\"\nOutput.BadPath.Text=\"Nakonfigurovanú cestu pre nahrávanie nebolo možné otvoriť. Prosím skontrolujte svoju cestu pre nahrávanie pod Nastavenia → Výstup → Nahrávanie.\"\nOutput.NoBroadcast.Title=\"Nie je nastavené žiadne vysielanie\"\nOutput.NoBroadcast.Text=\"Najprv musíte nastaviť vysielanie predtým než začnete streamovať.\"\nOutput.BroadcastStartFailed=\"Spustenie vysielania zlyhalo\"\nOutput.BroadcastStopFailed=\"Zastavenie vysielania zlyhalo\"\nLogUploadDialog.Title=\"Nahrávanie log súboru OBS Štúdia\"\nLogUploadDialog.Labels.PrivacyNotice=\"Prosím prečítajte si <a href='https://obsproject.com/privacy-policy'>Zásady ochrany súkromia</a> a ich sekciu ohľadom nahrávania súborov pred nahrávaním akýchkoľvek súborov.\"\nLogUploadDialog.Labels.Progress=\"Nahrávanie log súboru prebieha. Prosím počkajte...\"\nLogUploadDialog.Labels.Description.AppLog=\"Váš log súbor bol nahratý. Môžete teraz zdieľať URL pre potreby ladenia alebo podpory.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Vaša správa o havárií aplikácie bola nahratá. Môžete teraz zdieľať URL pre potreby ladenia.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Nahrať\"\nLogUploadDialog.Buttons.CopyURL=\"Kopírovať URL logu\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analyzovať log súbor\"\nLogUploadDialog.Buttons.RetryButton=\"Skúsiť znova\"\nLogUploadDialog.Errors.Template=\"Nastala chyba pri pokuse nahrať súbor:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Nenašiel sa súbor pre nahratie alebo je súbor prázdny.\"\nRemux.SourceFile=\"OBS nahrávka\"\nRemux.TargetFile=\"Cieľový súbor\"\nRemux.Remux=\"Previesť\"\nRemux.Stop=\"Zastaviť prevod\"\nRemux.ClearFinished=\"Odstrániť dokončené položky\"\nRemux.ClearAll=\"Odstrániť všetky položky\"\nRemux.OBSRecording=\"OBS nahrávanie\"\nRemux.FinishedTitle=\"Prevod dokončený\"\nRemux.Finished=\"Nahrávka prevedená\"\nRemux.FinishedError=\"Nahrávka prevedená, ale súbor nemusí byť kompletný\"\nRemux.SelectRecording=\"Vyberte OBS nahrávku...\"\nRemux.SelectTarget=\"Vyberte cieľový súbor...\"\nRemux.FileExistsTitle=\"Cieľový súbor existuje\"\nRemux.FileExists=\"Nasledujúce súbory už existujú. Chcete ich nahradiť?\"\nRemux.ExitUnfinishedTitle=\"Prebieha prevod\"\nRemux.ExitUnfinished=\"Prevod nie je dokončený. Zastavenie môže spôsobiť poškodenie výstupného súboru.\\nNaozaj chcete prevod prerušiť?\"\nRemux.HelpText=\"Presuňte súbory do tohto okna k prevodu alebo vyberte prázdne pole \\\"OBS nahrávka\\\" pre výber súboru.\"\nRemux.NoFilesAddedTitle=\"Žiaden pridaný súbor na prevod\"\nRemux.NoFilesAdded=\"Nebol pridaný žiaden súbor na prevod. Vyberte priečinok obsahujúci jeden alebo viac video súborov.\"\nMissingFiles=\"Chýbajúce súbory\"\nMissingFiles.MissingFile=\"Chýbajúci súbor\"\nMissingFiles.NewFile=\"Nový súbor\"\nMissingFiles.HelpText=\"Niektoré súbory chýbajú, odkedy ste naposledy použili OBS.\"\nMissingFiles.Clear=\"<vyčistené>\"\nMissingFiles.NumFound=\"Nájdených %1 z %2\"\nMissingFiles.Search=\"Prehľadať zložku...\"\nMissingFiles.SelectFile=\"Vybrať súbor...\"\nMissingFiles.SelectDir=\"Vyberte priečinok v ktorom sa má hľadať\"\nMissingFiles.State=\"Stav\"\nMissingFiles.Missing=\"Chýbajúci\"\nMissingFiles.Replaced=\"Nahradený\"\nMissingFiles.Cleared=\"Vyčistené\"\nMissingFiles.Found=\"Najdený\"\nMissingFiles.AutoSearch=\"Nájdených viacero zhôd\"\nMissingFiles.AutoSearchText=\"OBS našlo viacero zhôd pre chýbajúce súbory v tejto zložke. Chceli by ste ich pridať?\"\nMissingFiles.NoMissing.Title=\"Kontrola chýbajúcich súborov\"\nMissingFiles.NoMissing.Text=\"Vyzerá to tak, že žiadne súbory nechýbajú.\"\nMacPermissions.MenuAction=\"Kontrola povolení aplikácie...\"\nMacPermissions.Title=\"Kontrola povolení aplikácie\"\nMacPermissions.Description=\"OBS Štúdio vyžaduje vaše povolenie aby bolo schopné poskytnúť určité funkcie. Odporúča sa povoliť tieto povolenia, ale pre používanie tejto aplikácie nie sú vyžadované. Vždy ich môžete povoliť neskôr.\"\nMacPermissions.Description.OpenDialog=\"Môžete otvoriť toto okno neskôr cez menu OBS Štúdia.\"\nMacPermissions.AccessGranted=\"Prístup povolený\"\nMacPermissions.RequestAccess=\"Vyžiadať prístup\"\nMacPermissions.OpenPreferences=\"Otvoriť preferencie %1\"\nMacPermissions.Item.ScreenRecording=\"Nahrávanie obrazovky\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS vyžaduje toto povolenie aby bolo schopné zachytiť vašu obrazovku.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Toto povolenie je potrebné na to aby bolo možné zachytiť obsah z webkamery alebo zachytávacej karty.\"\nMacPermissions.Item.Microphone=\"Mikrofón\"\nMacPermissions.Item.Microphone.Details=\"OBS vyžaduje toto povolenie ak chcete zachytávať váš mikrofón alebo externé zvukové zariadenie.\"\nMacPermissions.Item.InputMonitoring=\"Monitorovanie vstupu\"\nMacPermissions.Item.InputMonitoring.Details=\"Toto povolenie je vyžadované pre fungovanie klávesových skratiek, keď je OBS v pozadí.\"\nMacPermissions.Item.Accessibility=\"Zjednodušenie ovládania\"\nMacPermissions.Item.Accessibility.Details=\"Pri starších inštaláciách, OBS môže byť uvedené v \\\"Prístupnosť\\\" namiesto \\\"Monitorovanie vstupu\\\"\"\nMacPermissions.Continue=\"Pokračovať\"\nSourceLeak.Title=\"Chyba pri čistení zdroja\"\nSourceLeak.Text=\"Nastala chyba počas zmeny scénových kolekcií a niektoré zdroje nemohli byť uvoľnené. Tento problém je zvyčajne spôsobený pluginmi, ktoré neuvoľňujú prostriedky poriadne. Prosím uistite sa, že akékoľvek pluginy, ktoré používate, sú aktuálne.\\n\\nOBS Štúdio sa teraz ukončí, aby sa zabránilo akémukoľvek potenciálnemu poškodeniu údajov.\"\nBasic.DesktopDevice1=\"Zvuk plochy\"\nBasic.DesktopDevice2=\"Zvuk plochy 2\"\nBasic.AuxDevice1=\"Mikrofón/Aux\"\nBasic.AuxDevice2=\"Mikrofón/Aux 2\"\nBasic.AuxDevice3=\"Mikrofón/Aux 3\"\nBasic.AuxDevice4=\"Mikrofón/Aux 4\"\nBasic.Scene=\"Scéna\"\nBasic.DisplayCapture=\"Zachytávanie monitora\"\nBasic.Main.PreviewConextMenu.Enable=\"Zapnúť náhľad\"\nBasic.Main.Preview.Disable=\"Vypnúť nahľad\"\nScaleFiltering=\"Filter škálovania\"\nScaleFiltering.Point=\"Bodový\"\nScaleFiltering.Bilinear=\"Bilineárny\"\nScaleFiltering.Bicubic=\"Bikubický\"\nScaleFiltering.Area=\"Oblastný\"\nBlendingMethod=\"Metóda zmiešavania\"\nBlendingMethod.Default=\"Predvolená\"\nBlendingMethod.SrgbOff=\"SRGB vypnuté\"\nBlendingMode=\"Režim zmiešavania\"\nBlendingMode.Normal=\"Normálny\"\nBlendingMode.Additive=\"Pridať\"\nBlendingMode.Subtract=\"Odpočítať\"\nBlendingMode.Screen=\"Clona\"\nBlendingMode.Multiply=\"Znásobiť\"\nBlendingMode.Lighten=\"Zosvetliť\"\nBlendingMode.Darken=\"Stmaviť\"\nDeinterlacing=\"Odstránenie prekladania\"\nDeinterlacing.Discard=\"Zahodenie\"\nDeinterlacing.TopFieldFirst=\"Najprv vrchný riadok\"\nDeinterlacing.BottomFieldFirst=\"Najprv spodný riadok\"\nVolControl.SliderUnmuted=\"Posuvník hlasitosti pre '%1':\"\nVolControl.SliderMuted=\"Ovládanie hlasitosti pre '%1': (momentálne umlčané)\"\nVolControl.Mute=\"Umlčať '%1'\"\nVolControl.Properties=\"Vlastnosti pre '%1'\"\nVolControl.UnassignedWarning.Title=\"Nepriradený zvukový zdroj\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" nie je priradený k žiadnej zvukovej stope a nebude počuteľný v streamoch a nahrávkach.\\n\\nPre priradenie zvukového zdroja k stope, otvorte Rozšírené vlastnosti zvuku cez menu pravého tlačidla myši alebo tlačidlo ozubené koleso na paneli nástrojov doku mixéra.\"\nBasic.Main.AddSceneDlg.Title=\"Pridať scénu\"\nBasic.Main.AddSceneDlg.Text=\"Prosím, zadajte názov scény\"\nBasic.Main.DefaultSceneName.Text=\"Scéna %1\"\nBasic.Main.AddSceneCollection.Title=\"Pridať sadu scén\"\nBasic.Main.AddSceneCollection.Text=\"Prosím, zadajte názov sady scén\"\nBasic.Main.RenameSceneCollection.Title=\"Premenovať sadu scén\"\nBasic.Main.RemigrateSceneCollection.Title=\"Aktualizovať rozlíšenie kolekcie scén\"\nBasic.Main.RemigrateSceneCollection.Text=\"Prajete si zmeniť rozlíšenie zbierky scén z \\\"%1\\\" tak aby súhlasila s aktuálnym rozlíšením plátna of %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Nedá sa zmeniť rozlíšenie zbierky scén pokým sú aktívne výstupy.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Zmena rozlíšenie zbierky scén zlyhala. Pôvodné rozlíšenie je neznámy.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Reset nie je možný: Zmena OBS rozlíšenia zlyhala.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Reset nie je možný: Aktuálne rozlíšenie už je základným rozlíšením zbierky scén.\"\nAddProfile.Title=\"Pridanie profilu\"\nAddProfile.Text=\"Prosím, zadajte názov profilu\"\nAddProfile.WizardCheckbox=\"Ukázať sprievodcu automatickou konfiguráciou\"\nRenameProfile.Title=\"Premenovať profil\"\nBasic.Main.MixerRename.Title=\"Premenovať zdroj zvuku\"\nBasic.Main.MixerRename.Text=\"Prosím, zadajte názov zdroja zvuku\"\nBasic.Main.PreviewDisabled=\"Náhľad je momentálne vypnutý\"\nBasic.SourceSelect=\"Vytvoriť/Vybrať zdroj\"\nBasic.SourceSelect.CreateNew=\"Vytvoriť nový\"\nBasic.SourceSelect.AddExisting=\"Pridať existujúci\"\nBasic.SourceSelect.AddVisible=\"Zviditeľniť zdroj\"\nBasic.Main.Sources.Visibility=\"Viditeľnosť\"\nBasic.Main.Sources.VisibilityDescription=\"Ovláda viditeľnosť '%1' na plátne\"\nBasic.Main.Sources.Lock=\"Zámok\"\nBasic.Main.Sources.LockDescription=\"Uzamkne pozíciu a škálu '%1' na plátne\"\nBasic.PropertiesWindow=\"Vlastnosti pre '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (Automatický výber: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Vybrať farbu\"\nBasic.PropertiesWindow.SelectFont=\"Vybrať písmo\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Vyberte písmo\"\nBasic.PropertiesWindow.ConfirmTitle=\"Nastavenia zmenené\"\nBasic.PropertiesWindow.Confirm=\"Existujú neuložené zmeny. Chcete ich ponechať?\"\nBasic.PropertiesWindow.NoProperties=\"Žiadne dostupné vlastnosti\"\nBasic.PropertiesWindow.AddFiles=\"Pridať súbory\"\nBasic.PropertiesWindow.AddDir=\"Pridať adresár\"\nBasic.PropertiesWindow.AddURL=\"Pridať cestu/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Pridať adresár do '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Pridať súbory do '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Pridať položku do '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Upraviť položku z '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Jednoduché hodnoty FPS\"\nBasic.PropertiesView.FPS.Rational=\"Rozumné hodnoty FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Platné rozsahy FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"Otvoriť tento odkaz v predvolenom webovom prehliadači?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Otvoriť URL\"\nBasic.InteractionWindow=\"V interakcii s '%1'\"\nBasic.StatusBar.Reconnecting=\"Odpojený, prebieha znovu-pripájanie za %2 sekúnd(y) (pokus č. %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Pokus o znovu-pripojenie... (pokus č. %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Znovupripájanie úspešné\"\nBasic.StatusBar.Delay=\"Oneskorenie (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Oneskorenie (začínajúci za %1 s)\"\nBasic.StatusBar.DelayStoppingIn=\"Oneskorenie (zastavenie za %1 s)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Oneskorenie (zastavenie za %1 s, začínajúci za %2 s)\"\nBasic.StatusBar.RecordingSavedTo=\"Nahrávka uložená do '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Záznam do pamäte uložený do '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Snímka obrazovky uložená do '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Nahrávka automaticky prevedená do '%1'\"\nBasic.Filters=\"Filtre\"\nBasic.Filters.AsyncFilters=\"Zvukové/obrazové filtre\"\nBasic.Filters.AudioFilters=\"Zvukové filtre\"\nBasic.Filters.EffectFilters=\"Filtre efektov\"\nBasic.Filters.Title=\"Filtre pre '%1'\"\nBasic.Filters.AddFilter.Title=\"Názov filtra\"\nBasic.Filters.AddFilter.Text=\"Prosím, zadajte názov filtra\"\nBasic.TransformWindow=\"Transformácia položky scény\"\nBasic.TransformWindow.Position=\"Pozícia\"\nBasic.TransformWindow.PositionX=\"Pozícia X\"\nBasic.TransformWindow.PositionY=\"Pozícia Y\"\nBasic.TransformWindow.Rotation=\"Otočenie\"\nBasic.TransformWindow.Size=\"Veľkosť\"\nBasic.TransformWindow.Width=\"Šírka\"\nBasic.TransformWindow.Height=\"Výška\"\nBasic.TransformWindow.Alignment=\"Zarovnanie\"\nBasic.TransformWindow.BoundsType=\"Typ ohraničenia\"\nBasic.TransformWindow.BoundsAlignment=\"Zarovnanie v ohraničení\"\nBasic.TransformWindow.Bounds=\"Veľkosť ohraničenia\"\nBasic.TransformWindow.BoundsWidth=\"Šírka ohraničovacieho boxu\"\nBasic.TransformWindow.BoundsHeight=\"Výška ohraničovacieho boxu\"\nBasic.TransformWindow.CropToBounds=\"Orezať na ohraničujúci rámček\"\nBasic.TransformWindow.Crop=\"Orezať\"\nBasic.TransformWindow.CropLeft=\"Orezať vľavo\"\nBasic.TransformWindow.CropRight=\"Orezať vpravo\"\nBasic.TransformWindow.CropTop=\"Orezať hore\"\nBasic.TransformWindow.CropBottom=\"Orezať dole\"\nBasic.TransformWindow.Alignment.TopLeft=\"Hore/vľavo\"\nBasic.TransformWindow.Alignment.TopCenter=\"Hore/na stred\"\nBasic.TransformWindow.Alignment.TopRight=\"Hore/vpravo\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Na stred/vľavo\"\nBasic.TransformWindow.Alignment.Center=\"Na stred\"\nBasic.TransformWindow.Alignment.CenterRight=\"Na stred/vpravo\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Dole/vľavo\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Dole/na stred\"\nBasic.TransformWindow.Alignment.BottomRight=\"Dole/vpravo\"\nBasic.TransformWindow.BoundsType.None=\"Bez ohraničenia\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Len maximálna veľkosť\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Škálovanie podľa vnútornej hranice\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Škálovanie podľa vonkajšej hranice\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Škálovať podľa šírky hranice\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Škálovať podľa výšky hranice\"\nBasic.TransformWindow.BoundsType.Stretch=\"Natiahnuť podľa celej hranice\"\nBasic.TransformWindow.Title=\"Úprava transformácie pre '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Žiaden vybratý zdroj\"\nBasic.Main.AddSourceHelp.Title=\"Nie je možné pridať zdroj\"\nBasic.Main.AddSourceHelp.Text=\"Na pridanie zdroja je potrebná aspoň 1 scéna.\"\nBasic.Main.Scenes=\"Scény\"\nBasic.Main.Sources=\"Zdroje\"\nBasic.Main.Source=\"Zdroj\"\nBasic.Main.Controls=\"Ovládacie prvky\"\nBasic.Main.PreparingStream=\"Pripravuje sa...\"\nBasic.Main.Connecting=\"Pripájanie...\"\nBasic.Main.StartRecording=\"Spustiť nahrávanie\"\nBasic.Main.StartReplayBuffer=\"Spustiť záznam do pamäte\"\nBasic.Main.SaveReplay=\"Uložiť záznam\"\nBasic.Main.StartStreaming=\"Spustiť stream\"\nBasic.Main.StartBroadcast=\"Začať vysielať\"\nBasic.Main.StartVirtualCam=\"Spustiť virtuálnu kameru\"\nBasic.Main.StopRecording=\"Ukončiť nahrávanie\"\nBasic.Main.PauseRecording=\"Pozastaviť nahrávanie\"\nBasic.Main.UnpauseRecording=\"Pokračovať v nahrávaní\"\nBasic.Main.SplitFile=\"Rozdeliť nahrávací súbor\"\nBasic.Main.AddChapterMarker=\"Pridať značku kapitoly (iba pre Hybridný MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Zastavenie nahrávania...\"\nBasic.Main.StopReplayBuffer=\"Zastaviť záznam do pamäte\"\nBasic.Main.StoppingReplayBuffer=\"Zastavujem záznam do pamäte...\"\nBasic.Main.SetupBroadcast=\"Spravovať vysielanie\"\nBasic.Main.StopStreaming=\"Ukončiť stream\"\nBasic.Main.StopBroadcast=\"Ukončiť vysielanie\"\nBasic.Main.AutoStopEnabled=\"(Auto stop)\"\nBasic.Main.StoppingStreaming=\"Zastavenie streamu...\"\nBasic.Main.ForceStopStreaming=\"Zastaviť vysielanie (bez oneskorenia)\"\nBasic.Main.ShowContextBar=\"Zobraziť panel nástrojov zdroja\"\nBasic.Main.HideContextBar=\"Skryť panel nástrojov zdroja\"\nBasic.Main.StopVirtualCam=\"Vypnúť virtuálnu kameru\"\nBasic.Main.Group=\"Skupina %1\"\nBasic.Main.GroupItems=\"Zoskupiť vybrané položky\"\nBasic.Main.Ungroup=\"Rozdeliť skupinu\"\nBasic.Main.GridMode=\"Režim mriežky\"\nBasic.Main.ListMode=\"Režim zoznamu\"\nBasic.Main.VirtualCamConfig=\"Nastaviť virtuálnu kameru\"\nBasic.VCam.VirtualCamera=\"Virtuálna kamera\"\nBasic.VCam.OutputType=\"Type výstupu\"\nBasic.VCam.OutputSelection=\"Výber výstupu\"\nBasic.VCam.OutputType.Program=\"Výstup programu (Predvolené)\"\nBasic.VCam.OutputSelection.NoSelection=\"Žiaden výber pre tento typ výstupu\"\nBasic.VCam.RestartWarning=\"Virtuálna kamera bude reštartovaná pre použitie zmien\"\nBasic.MainMenu.File=\"Súbor (&F)\"\nBasic.MainMenu.File.Export=\"&Exportovať\"\nBasic.MainMenu.File.Import=\"&Importovať\"\nBasic.MainMenu.File.ShowRecordings=\"Zobraziť nah&rávky\"\nBasic.MainMenu.File.Remux=\"Previesť nahrávky (&M)\"\nBasic.MainMenu.File.Settings=\"Na&stavenia\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Zobraziť priečinok nastavení\"\nBasic.MainMenu.File.ShowProfileFolder=\"Zobraziť priečinok profilu\"\nBasic.MainMenu.File.ShowMissingFiles=\"Kontrolovať či nechýbajú súbory\"\nBasic.MainMenu.File.Exit=\"Ukončiť (&x)\"\nBasic.MainMenu.Edit=\"Upraviť (&E)\"\nBasic.MainMenu.Edit.Undo=\"Vrátiť späť (&U)\"\nBasic.MainMenu.Edit.Redo=\"Opakovať v&rátené\"\nBasic.MainMenu.Edit.LockPreview=\"Zámknúť náhľad (&L)\"\nBasic.MainMenu.Edit.Scale=\"Rozmer náhľadu (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"Vtesnať do okna\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Plátno (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Výstup (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Škálované (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Priblížiť\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Vzdialiť\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Obnoviť priblíženie\"\nBasic.MainMenu.Edit.Transform=\"&Transformácia\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Upraviť transformáciu... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopírovať transformáciu\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Vložiť transformáciu\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Obnoviť pôvodnú (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Otočiť o 90 stupňov doprava\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Otočiť o 90 stupňov doľava\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Otočiť o 180 stupňov\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Preklopiť vodorovne (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Preklopiť z&visle\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Prispôsobiť obrazovke (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Roztiahnuť na celú obrazovku (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Umiestniť na stred obrazovky (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centrovať zvislo\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centrovať vodorovne\"\nBasic.MainMenu.Edit.Order=\"P&oradie\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Pos&unúť vyššie\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Posunúť nižšie (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Premies&tniť navrch\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Premiestniť naspodok (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Rozšírené vl&astnosti zvuku\"\nBasic.MainMenu.View=\"Zobraziť (&V)\"\nBasic.MainMenu.View.Toolbars=\"Panely s nás&trojmi\"\nBasic.MainMenu.View.ListboxToolbars=\"Dokovať panel nástrojov\"\nBasic.MainMenu.View.ContextBar=\"Panel nástrojov zdroja\"\nBasic.MainMenu.View.SourceIcons=\"Zdroj a ikony\"\nBasic.MainMenu.View.StatusBar=\"&Stavový riadok\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Na celú obrazovku\"\nBasic.MainMenu.View.ResetUI=\"&Resetovať používateľské rozhranie\"\nBasic.MainMenu.View.AlwaysOnTop=\"Vždy n&avrchu\"\nBasic.MainMenu.View.SceneListMode=\"Režim zoznamu scén\"\nBasic.MainMenu.Docks=\"&Doky\"\nBasic.MainMenu.Docks.ResetDocks=\"&Resetovať doky\"\nBasic.MainMenu.Docks.LockDocks=\"&Zamknúť doky\"\nBasic.MainMenu.Docks.SideDocks=\"Celovýškové doky (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Vlastné doky prehliadača...\"\nBasic.MainMenu.Import=\"Importovať...\"\nBasic.MainMenu.Export=\"Exportovať...\"\nBasic.MainMenu.SceneCollection=\"&Sada scén\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Importovať profil\"\nBasic.MainMenu.Profile.Export=\"Exportovať profil\"\nBasic.MainMenu.SceneCollection.Import=\"Importovať sadu scén\"\nBasic.MainMenu.SceneCollection.Export=\"Exportovať sadu scén\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Obnoviť základné rozlíšenie\"\nBasic.MainMenu.SceneCollection.Migrate=\"Nastaviť základné rozlíšenie\"\nBasic.MainMenu.Profile.Exists=\"Tento profil už existuje\"\nBasic.MainMenu.Tools=\"Nás&troje\"\nBasic.MainMenu.Help=\"Pomoc (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portál pomoci\"\nBasic.MainMenu.Help.Website=\"Navštíviť &webové stránky\"\nBasic.MainMenu.Help.Discord=\"Pripojiť sa na &Discord server\"\nBasic.MainMenu.Help.WhatsNew=\"Čo je nové\"\nBasic.MainMenu.Help.Logs=\"&Log súbory\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Zobraziť log &súbory\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Nahrať súčasný log súbor (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Nahrať &predchádzajúci log súbor\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Zobraziť aktuálny log (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Poznámky k vydaniu\"\nBasic.MainMenu.Help.CheckForUpdates=\"Skontrolovať aktualizácie\"\nBasic.MainMenu.Help.Repair=\"Kontrola súborovej integrity\"\nBasic.MainMenu.Help.RestartSafeMode=\"Reštartovať v núdzovom režime\"\nBasic.MainMenu.Help.RestartNormal=\"Reštartovať v normálnom režime\"\nBasic.MainMenu.Help.CrashLogs=\"Správy o zlyhaní (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Zobraziť &správy o zlyhaniach\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Nahrať &predchádzajúcu správu o zlyhaní\"\nBasic.MainMenu.Help.About=\"O &aplikácii\"\nBasic.Settings.ProgramRestart=\"Tieto nastavenia sa prejavia až po reštarte programu.\"\nBasic.Settings.ConfirmTitle=\"Potvrdenie zmien\"\nBasic.Settings.Confirm=\"Máte neuložené zmeny. Chcete uložiť zmeny?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 ovláda niektoré z vašich streamovacích nastavení\"\nBasic.Settings.General=\"Všeobecné\"\nBasic.Settings.General.Language=\"Jazyk\"\nBasic.Settings.General.Updater=\"Aktualizácie\"\nBasic.Settings.General.UpdateChannel=\"Kanál aktualizácií\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Vypnutý)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Predvolený)\"\nBasic.Settings.General.EnableAutoUpdates=\"Automaticky kontrolovať aktualizácie pri štarte\"\nBasic.Settings.General.OpenStatsOnStartup=\"Otvoriť dialógové okno štatistík po štarte\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Skryť okná OBS zo zachytávania monitora\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Toto nastavenie skryje všetky neprojektorové okná OBS Štúdia zo zachytávania OBS a ostatných \\naplikácií, ako napríklad konferencie, zdieľanie obrazovky, vzdialená podpora, snímky obrazovky a iný zachytávací softvér.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Zapnutím tohto nastavenia skryjete všetky neprojektorové okná OBS Štúdia zo zachytávania OBS a ostatných aplikácií, ako napríklad konferencie, zdieľanie obrazovky, vzdialená podpora, snímky obrazovky a iný zachytávací softvér.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Ukázať potvrdzovacie okno pri začatí vysielania\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Ukázať potvrdzovacie okno pri zastavení vysielania\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Ukázať potvrdzovacie okno pri zastavení nahrávania\"\nBasic.Settings.General.Projectors=\"Projektory\"\nBasic.Settings.General.HideProjectorCursor=\"Skryť kurzor pred náhľadmi\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Náhľady vždy na vrchu\"\nBasic.Settings.General.Snapping=\"Prichytávanie zdrojov\"\nBasic.Settings.General.ScreenSnapping=\"Prichytávať zdroje k okraju obrazovky\"\nBasic.Settings.General.CenterSnapping=\"Prichytávať zdroje ku stredu vodorovnej a zvislej osi\"\nBasic.Settings.General.SourceSnapping=\"Prichytávať zdroje k iným zdrojom\"\nBasic.Settings.General.SnapDistance=\"Prichytávať citlivosť\"\nBasic.Settings.General.SpacingHelpers=\"Zobraziť zarovnávacie čiary pixelov\"\nBasic.Settings.General.RecordWhenStreaming=\"Automaticky nahrávať pri vysielaní\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Nahrávať aj po ukončení vysielania\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Automaticky zapnúť záznam do pamäte pri začatí vysielania\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Ponechať záznam do pamäte aktívny i po zastavení vysielania\"\nBasic.Settings.General.SysTray=\"Systémová lišta\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimalizovať do systémovej lišty pri spustení\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Vždy minimalizovať do systémovej lišty namiesto panela úloh\"\nBasic.Settings.General.SaveProjectors=\"Uložiť projektory pri skončení\"\nBasic.Settings.General.CloseExistingProjectors=\"Limitovať jeden celo-obrazovkový projektor na obrazovku\"\nBasic.Settings.General.Preview=\"Náhľad\"\nBasic.Settings.General.OverflowHidden=\"Skryť pretečenie\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Pretečenie vždy viditeľné\"\nBasic.Settings.General.OverflowSelectionHidden=\"Ukázať pretečenie aj keď je zdroj neviditeľný\"\nBasic.Settings.General.Importers=\"Importéri\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Pri importovaní vyhľadajte známe miesta pre zbierky scén\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Prejsť do scény pri dvojkliku\"\nBasic.Settings.General.StudioPortraitLayout=\"Zapnúť portrétové/vertikálne rozloženie\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Zobraziť popisy okien náhľadu a programu\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Povoliť prechod medzi scénami kliknutím\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Zobraziť názvy scén\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Vykresliť bezpečné zóny (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Rozloženie Multiview\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontálne, Hore (8 scén)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontálne, Dole (8 scén)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertikálne, Vľavo (8 scén)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertikálne, Vpravo (8 scén)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontálne, Hore (18 scén)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontálne, Hore (24 scén)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Iba scény (4 scény)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Iba scény (9 scén)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Iba scény (16 scén)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Iba scény (25 scén)\"\nBasic.Settings.General.ChannelName.stable=\"Stabilný\"\nBasic.Settings.General.ChannelDescription.stable=\"Najnovšia stabilná verzia\"\nBasic.Settings.General.ChannelName.beta=\"Beta verzie / Výdajní kandidáti\"\nBasic.Settings.General.ChannelDescription.beta=\"Potenciálne nestabilné pred-výdajné verzie\"\nBasic.Settings.Appearance=\"Vzhľad\"\nBasic.Settings.Appearance.General=\"Všeobecné\"\nBasic.Settings.Appearance.General.Theme=\"Téma\"\nBasic.Settings.Appearance.General.Variant=\"Štýl\"\nBasic.Settings.Appearance.General.NoVariant=\"Žiadne dostupné štýly\"\nBasic.Settings.Appearance.FontScale=\"Veľkosť písma\"\nBasic.Settings.Appearance.Density=\"Hustota\"\nBasic.Settings.Appearance.Classic=\"Klasická\"\nBasic.Settings.Appearance.Compact=\"Kompaktná\"\nBasic.Settings.Appearance.Normal=\"Normálna\"\nBasic.Settings.Appearance.Comfortable=\"Pohodlná\"\nBasic.Settings.Appearance.OptionsWarning=\"Niektoré možnosti výzoru nie sú dostupné pri tomto štýle.\"\nBasic.Settings.Stream.Destination=\"Destinácia\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Použiť overenie\"\nBasic.Settings.Stream.Custom.Username=\"Užívateľské meno\"\nBasic.Settings.Stream.Custom.Password=\"Heslo\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: zadajte srp_username.\\nRTMP: zadajte užívateľské meno.\\nSRT: nepoužité.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: zadajte srp_password.\\nRTMP: zadajte heslo.\\nSRT: zadajte prístupovú frázu šifrovania.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Povoliť režim testovania šírky pásma\"\nBasic.Settings.Stream.TTVAddon=\"Rozšírenia chatu v službe Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Žiadne\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV a FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Chýbajúce Nastavenia Vysielania\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Otvoriť nastavenia\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Chýba vysielací kľúč a adresa.\\n\\nOtvorte nastavenia pre zadanie vysielacieho kľúča a adresy v záložke 'Vysielanie'.\"\nBasic.Settings.Stream.MissingUrl=\"Chýba vysielacia adresa.\\n\\nOtvorte nastavenia pre zadanie vysielacej adresy v záložke 'Vysielanie'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Chýba vysielací kľúč.\\n\\nOtvorte nastavenia pre zadanie vysielacieho kľúča v záložke 'Vysielanie'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorovať odporučené nastavenia streamovacej služby\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Prepísať Odporúčané Nastavenia\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Varovanie: Ignorovanie obmedzení služby môže mať za následok zníženú kvalitu streamu alebo môže úplne zabrániť streamovaniu.\\n\\nPokračovať?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maximálny Video Bitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maximálny Bitrate zvuku: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maximálne rozlíšenie: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maximálne FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Špecifikovať vlastný server…\"\nBasic.Settings.Stream.ServiceCustomServer=\"Vlastný server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Zapnúť %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maximálna šírka pásma streamovania\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Automaticky\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maximálny počet video stôp\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Automaticky\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Zapnúť výpis streamu do FLV (používa jednoduché nahrávacie nastavenia súboru)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Prepísanie konfigurácie (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Zapnúť prepísanie konfigurácie\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Viacstopé video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Dodatočné plátno\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Simulcast umožňuje enkódovať a posielať viacero video kvalít. <a href='https://obsproject.com/kb/whip-streaming-guide'>Zistiť viac</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Všetky vrstvy\"\nBasic.Settings.Stream.AdvancedOptions=\"Pokročilé možnosti\"\nBasic.Settings.Output=\"Výstup\"\nBasic.Settings.Output.Format=\"Formát nahrávania\"\nBasic.Settings.Output.Format.hMP4=\"Hybridný MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hybridný MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmentovaný MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmentovaný MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmentovaný MOV zapisuje nahrávku v kusoch a nevyžaduje rovnakú finalizáciu ako tradičné MOV súbory.\\nToto zabezpečí, že súbor ostane prehrávateľný aj keď je zapisovanie na disk prerušené, napríklad pri BSOD alebo strate napájania.\\n\\nToto však nemusí byť kompatibilné so všetkými prehrávačmi a editormi. Použite Súbor -> Previesť nahrávky pre konvertovanie súboru do viac kompatibilného formátu ak je to potrebné.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmentovaný MP4 zapisuje nahrávku v kusoch a nevyžaduje rovnakú finalizáciu ako tradičné MP4 súbory.\\nToto zabezpečí, že súbor ostane prehrávateľný aj keď je zapisovanie na disk prerušené, napríklad pri BSOD alebo strate napájania.\\n\\nToto však nemusí byť kompatibilné so všetkými prehrávačmi a editormi. Použite Súbor -> Previesť nahrávky pre konvertovanie súboru do viac kompatibilného formátu ak je to potrebné.\"\nBasic.Settings.Output.Encoder.Video=\"Video enkodér\"\nBasic.Settings.Output.Encoder.Audio=\"Audio enkodér\"\nBasic.Settings.Output.SelectDirectory=\"Vybrať adresár pre nahrávky\"\nBasic.Settings.Output.DynamicBitrate=\"Dynamicky meniť bitrate v závislosti na zahltení siete\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dynamicky meniť bitrate v závislosti na zahltení siete (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Dynamicky mení bitrate počas behu, namiesto odhadzovania snímkov pre redukciu zahltenia siete.\\n\\nToto ale môže mať za následok zvýšenie odozvy pre divákov, keď dôjde k náhlemu zahlteniu siete.\\nKeď sa bitrate zníži, môže pár minúť trvať než sa obnoví na pôvodnú hodnotu.\\n\\nMomentálne podporované iba pre RTMP.\"\nBasic.Settings.Output.Mode=\"Režim výstupu\"\nBasic.Settings.Output.Mode.Simple=\"Jednoduchý\"\nBasic.Settings.Output.Mode.Adv=\"Pokročilý\"\nBasic.Settings.Output.Mode.FFmpeg=\"Výstup FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Zapnúť záznam do pamäte\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maximálny čas záznamu\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maximum pamäte\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Odhadované využitie pamäte: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Varovanie: Odhadované využitie pamäte %1 MiB je väčšie než odhadované maximum %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Nemožno odhadnúť využitie pamäte. Prosím, nastavte maximálny pamäťový limit.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Predpona názvu súboru záznamu do pamäte\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Prípona\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Záznam do pamäte nemôže byť použitý pri type nahrávania nastavenom na Custom Output (FFmpeg)\"\nBasic.Settings.Output.Simple.SavePath=\"Nahrávacia cesta\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Kvalita nahrávania\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Rovnaká ako pre vysielanie\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Vysoká kvalita, stredná veľkosť súboru\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Nerozoznateľná kvalita, veľká veľkosť súboru\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Bezstratová kvalita, ohromne veľké súbory\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Varovanie: Streamovací video bitrate bude nastavený na %1, čo je horný limit pre momentálnu streamovaciu službu.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Varovanie: Streamovací bitrate zvuku bude nastavený na %1, čo je horný limit pre momentálnu streamovaciu službu.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Upozornenie: Nahrávanie nemôže byť pozastavené, ak je kvalita nahrávania nastavená na \\\"Rovnaké ako pre vysielanie\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Varovanie: Aktuálne vybraný nahrávací formát je nekompatibilný s vybraným streamovacím enkodérom(mi).\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Upozornenie: Nahrávanie softvérovým kodérom s rozdielnou kvalitou než vysielanie spôsobí zvýšenú záťaž CPU pri nahrávaní a vysielaní zároveň.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Varovanie: Bezstratová kvalita generuje ohromne veľké súbory! Bezstratová kvalita môže použiť viac ako 7 gigabajtov za minútu na disku pri vysokých rozlíšeniach a snímkoch za sekundu. Bezstratová kvalita nie je odporúčaná pre dlhé nahrávky pokiaľ nemáte veľmi veľa priestoru na disku. Záznam do pamäte je nedostupný pri použití beztratovej kvality.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Ste si istí, že chcete použiť bezstratovú kvalitu?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Varovanie bezstratovej kvality!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Softvérový (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Hardvérový (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Hardvérový (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Hardvérový (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Hardvérový (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Hardvérový (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Hardvérový (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Hardvérový (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Hardvérový (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Hardvérový (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Hardvérový (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Softvérový (x264, nízke zaťaženie CPU, zvyšuje veľkosť súboru)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Predvolený)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD záznam (používa stopu 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Zvuková stopa\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Nekompatibilné rozlíšenie/snímková frekvencia\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Táto streamovacia služba nepodporuje vaše momentálne výstupne rozlíšenie a/alebo snímkovú frekvenciu. Budú zmenené na najbližšiu kompatibilnú hodnotu:\\n\\n%1\\n\\nChcete pokračovať?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Rozlíšenie: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Nekompatibilný enkodér\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Streamovacia služba \\\"%1\\\" nepodporuje enkodér \\\"%2\\\". Tento enkodér bude zmenený na \\\"%3\\\".\\n\\nChcete pokračovať?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Streamovacia služba \\\"%1\\\" nepodporuje enkodéri \\\"%2\\\" a \\\"%3\\\". Tieto enkodéri budu zmenené na \\\"%4\\\" a \\\"%5\\\".\\n\\nChcete pokračovať?\"\nBasic.Settings.Output.VideoBitrate=\"Bitrate videa\"\nBasic.Settings.Output.AudioBitrate=\"Bitrate zvuku\"\nBasic.Settings.Output.Reconnect=\"Automatické znovupripojenie\"\nBasic.Settings.Output.RetryDelay=\"Oneskorenie opakovania\"\nBasic.Settings.Output.MaxRetries=\"Maximálny počet pokusov\"\nBasic.Settings.Output.Advanced=\"Zapnúť vlastné nastavenia enkodéra (pokročilé)\"\nBasic.Settings.Output.EncoderPreset=\"Predvoľba kodéra\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (nízke využitie CPU, nízka kvalita)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (predvolený) (stredné využitie CPU, štandardná kvalita)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (vysoké využitie CPU, vysoká kvalita)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Vlastné nastavenie enkodéra\"\nBasic.Settings.Output.CustomMuxerSettings=\"Vlastné nastavenia muxera\"\nBasic.Settings.Output.NoSpaceFileName=\"Vygenerovať názov súboru bez medzier\"\nBasic.Settings.Output.Adv.Rescale=\"Škálovať výstup\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Vypnuté\"\nBasic.Settings.Output.Adv.AudioTrack=\"Zvuková stopa\"\nBasic.Settings.Output.Adv.Streaming=\"Vysielanie\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Streamovacie nastavenia\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Stopa 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Stopa 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Stopa 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Stopa 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Stopa 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Stopa 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD záznam\"\nBasic.Settings.Output.Adv.Encoder=\"Nastavenia enkodéra\"\nBasic.Settings.Output.Adv.Recording=\"Nahrávanie\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Nahrávacie nastavenia\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Typ nahrávania\"\nBasic.Settings.Output.Adv.Recording.Type=\"Typ\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Štandardný\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Vlastný výstup (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Použiť enkodér vysielania)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Formát názvu súboru\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Prepísať, ak súbor existuje\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Vlastný výstup (FFmpeg) je bez ochranných zábran.\\nZvážte použitie \\\"Štandardného\\\" spôsobu nahrávania.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Typ výstupu FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Výstup na URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Výstup do súboru\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Bežné formáty nahrávania\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Všetky súbory\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Cesta k súboru alebo URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Formát kontajneru\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Zvuk\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Predvolený formát\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Popis formátu\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Kodek zvuku/obrazu uhádnutý z cesty súboru alebo URL adresy\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Predvolený enkodér\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Vypnúť enkodér\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Kodér obrazu\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Nastavenie kodéra obrazu (ak existuje)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Enkodér zvuku\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Nastavenie kodéra zvuku (ak existuje)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Nastavenia muxéra (ak nejaké sú)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Interval kľúčových snímkov (snímky)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Zobraziť všetky kodeky (aj potenciálne nekompatibilné)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg nastavenia\"\nBasic.Settings.Output.EnableSplitFile=\"Automatické rozdelenie súborov\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Rozdelenie podľa času\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Rozdelenie podľa veľkosti\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Rozdeľovať iba manuálne\"\nBasic.Settings.Output.SplitFile.Time=\"Čas rozdelenia\"\nBasic.Settings.Output.SplitFile.Size=\"Veľkosť rozdelenia\"\nScreenshot=\"Výstup snímkov obrazovky\"\nScreenshot.SourceHotkey=\"Vybratý zdroj snímkov obrazovky\"\nScreenshot.StudioProgram=\"Snímok obrazovky (Program)\"\nScreenshot.Preview=\"Snímok obrazovky (Náhľad)\"\nScreenshot.Scene=\"Snímok obrazovky (Scéna)\"\nScreenshot.Source=\"Snímok obrazovky (Zdroj)\"\nFilenameFormatting.TT.CCYY=\"Rok, štyri číslice\"\nFilenameFormatting.TT.YY=\"Rok, posledné dve číslice (00-99)\"\nFilenameFormatting.TT.MM=\"Mesiac ako číslo (01-12)\"\nFilenameFormatting.TT.DD=\"Deň v mesiaci, vyplnený nulou (01-31)\"\nFilenameFormatting.TT.hh=\"Hodina v 24hodinovom formáte (00-23)\"\nFilenameFormatting.TT.mm=\"Minúta (00-59)\"\nFilenameFormatting.TT.ss=\"Sekunda (00-59)\"\nFilenameFormatting.TT.Percent=\"Znak %\"\nFilenameFormatting.TT.a=\"Skrátený názov dňa v týždni\"\nFilenameFormatting.TT.A=\"Celý názov dňa v týždni\"\nFilenameFormatting.TT.b=\"Skrátený názov mesiaca\"\nFilenameFormatting.TT.B=\"Celý názov mesiaca\"\nFilenameFormatting.TT.d=\"Deň v mesiaci, vyplnený nulou (01-31)\"\nFilenameFormatting.TT.H=\"Hodina v 24hodinovom formáte (00-23)\"\nFilenameFormatting.TT.I=\"Hodina v 12hodinovom formáte (01-12)\"\nFilenameFormatting.TT.m=\"Mesiac ako číslo (01-12)\"\nFilenameFormatting.TT.M=\"Minúta (00-59)\"\nFilenameFormatting.TT.p=\"AM alebo PM dezignácia\"\nFilenameFormatting.TT.s=\"Čas v sekundách od UNIX epochy\"\nFilenameFormatting.TT.S=\"Sekunda (00-59)\"\nFilenameFormatting.TT.y=\"Rok, posledné dve číslice (00-99)\"\nFilenameFormatting.TT.Y=\"Rok\"\nFilenameFormatting.TT.z=\"ISO 8601 odstup od časovej zóny UTC\"\nFilenameFormatting.TT.Z=\"Názov alebo skratka časovej zóny\"\nFilenameFormatting.TT.FPS=\"Snímky za sekundu\"\nFilenameFormatting.TT.CRES=\"Základné (Plátnové) rozlíšenie\"\nFilenameFormatting.TT.ORES=\"Výstupné (škálované) rozlíšenie\"\nFilenameFormatting.TT.VF=\"Formát videa\"\nBasic.Settings.Video.Adapter=\"Video adaptér\"\nBasic.Settings.Video.BaseResolution=\"Základné rozlíšenie (plátno)\"\nBasic.Settings.Video.ScaledResolution=\"Výstupné (škálované) rozlíšenie\"\nBasic.Settings.Video.DownscaleFilter=\"Zmenšovací filter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Rozlíšenia sa zhodujú, žiadne zmenšovanie nie je potrebné]\"\nBasic.Settings.Video.FPSCommon=\"Bežné hodnoty FPS\"\nBasic.Settings.Video.FPSInteger=\"Celočíselná hodnota FPS\"\nBasic.Settings.Video.FPSFraction=\"Zlomková hodnota FPS\"\nBasic.Settings.Video.Numerator=\"Čitateľ\"\nBasic.Settings.Video.Denominator=\"Menovateľ\"\nBasic.Settings.Video.Renderer=\"Vykresľovač\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Experimentálny)\"\nBasic.Settings.Video.InvalidResolution=\"Neplatné rozlíšenie. Správne je [šírka]x[výška] (napr. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Výstup videa je práve aktívny. Prosím, vypnite všetky výstupy na zmenu nastavení videa.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilineárny (Najrýchlejší, ale rozmazaný pri škálovaní)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bikubický (Zaostrené škálovanie, 16 vzoriek)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Zaostrené škálovanie, 36 vzoriek)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Oblastný (Váhová suma, 4/6/9 vzoriek)\"\nBasic.Settings.Audio=\"Zvuk\"\nBasic.Settings.Audio.SampleRate=\"Vzorkovacia frekvencia\"\nBasic.Settings.Audio.Channels=\"Kanály\"\nBasic.Settings.Audio.Meters=\"Merače\"\nBasic.Settings.Audio.MeterDecayRate=\"Rýchlosť rozpadu\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Rýchlo\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Stredne (typ I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Pomaly (typ II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Typ merača vrcholov\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Vzorkový vrchol\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Pravý vrchol (Vyššie využitie CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"VAROVANIE: Priestorový zvuk je zapnutý.\"\nBasic.Settings.Audio.MultichannelWarning=\"Ak vysielate, uistite sa že vaša vysielacia služba podporuje prijímanie a aj prehrávanie priestorového zvuku. Facebook 360 Live je príklad služby, ktorá priestorový zvuk plne podporuje. I keď Facebook Live a YouTube Live podporujú prijímanie priestorového zvuku, Facebook Live ho prevádza do stereo zvuku a YouTub Live prehráva iba 2 kanály.\\n\\nOBS zvukové filtre sú kompatibilné s priestorovým zvukom, no podpora u VST pluginu nie je garantovaná.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Povoliť priestorový zvuk?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Naozaj si želáte povoliť priestorový zvuk?\"\nBasic.Settings.Audio.Devices=\"Globálne zvukové zariadenia\"\nBasic.Settings.Audio.DesktopDevice=\"Zvuk plochy\"\nBasic.Settings.Audio.DesktopDevice2=\"Zvuk plochy 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofón/Aux - Zvukové zariadenie\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofón/Aux - Zvukové zariadenie 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofón/Aux - Zvukové zariadenie 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofón/Aux - Zvukové zariadenie 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Povoliť push-to-mute\"\nBasic.Settings.Audio.PushToMuteDelay=\"Oneskorenie push-to-mute\"\nBasic.Settings.Audio.EnablePushToTalk=\"Povoliť push-to-talk\"\nBasic.Settings.Audio.PushToTalkDelay=\"Oneskorenie push-to-talk\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Zariadenie nie je pripojené alebo dostupné]\"\nBasic.Settings.Audio.Disabled=\"Zakázané\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Mód ukladania do vyrovnávacej pamäte pre zvuk s nízkou odozvou (pre výstupy Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"VAROVANIE: Ukladanie do vyrovnávacej pamäte pre zvuk s nízkou odozvou je zapnuté.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Mód ukladania do vyrovnávacej pamäte pre zvuk s nízkou odozvou môže spôsobiť trhanie zvuku alebo zastavenie prehrávania z niektorých zdrojov.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Zapnúť mód ukladania do vyrovnávacej pamäte pre zvuk s nízkou odozvou?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Ste si istí, že chcete zapnúť mód ukladania do vyrovnávacej pamäte pre zvuk s nízkou odozvou?\"\nBasic.Settings.Accessibility=\"Zjednodušenie ovládania\"\nBasic.Settings.Accessibility.ColorOverrides=\"Použiť iné farby\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Okraj zdroja (označenie)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Okraj zdroja (orezanie)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Okraj zdroja (ukázanie myšou)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Pásmo zmiešavača hlasitosti (od -60 do -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Pásmo zmiešavača hlasitosti (od -20 do -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Pásmo zmiešavača hlasitosti (od -9 do 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Pásmo zmiešavača hlasitosti (od -60 do -20dB) (aktívne)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Pásmo zmiešavača hlasitosti (od -20 do -9dB) (aktívne)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Pásmo zmiešavača hlasitosti (od -9 do 0dB) (aktívne)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Farebná predvoľba\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Predvolená\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Vlastná\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Farboslepá alternatíva\"\nBasic.Settings.Advanced=\"Pokročilé\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Ukázať varovanie pri aktívnych výstupoch pri ukončení programu\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Priorita procesu\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Vysoká\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Zvýšená\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normálna\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Znížená\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Nízka\"\nBasic.Settings.Advanced.FormatWarning=\"Upozornenie: Formáty farieb iné ako NV12/P010 sú primárne určené na nahrávanie a nie sú odporúčane pre streamovanie. Streamovanie môže spôsobiť zvýšené využitie CPU kvôli prevodu medzi farebnými formátmi.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Varovanie: Formáty s vysokou presnosťou sú zvyčajne používané s HDR farebnými priestormi.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Varovanie: Rec. 2100 by malo používať formát s väčšou presnosťou.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Formát farieb\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bitový, 4:2:0, 2 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bitový, 4:2:0, 3 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bitový, 4:4:4, 3 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bitový, 4:2:0, 2 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bitový, 4:2:0, 3 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bitový, 4:2:2, 2 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bitový, 4:4:4, 2 roviny)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-bitový)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Farebný priestor\"\nBasic.Settings.Advanced.Video.ColorRange=\"Farebný rozsah\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Obmedzený\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Úplný\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR Úroveň bielej\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nominálna vrcholová úroveň HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Monitorovacie zariadenie\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Predvolené\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Vypnúť zvukové utišovanie Windowsu\"\nBasic.Settings.Advanced.StreamDelay=\"Oneskorenie vysielania\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Trvanie\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Zachovať bod prerušenia (zväčšiť oneskorenie) pri znovupripojení\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Odhadované využitie pamäte: %1 MB\"\nBasic.Settings.Advanced.Network=\"Sieť\"\nBasic.Settings.Advanced.Network.Disabled=\"Momentálne zvolený streamovací protokol nepodporuje zmenu sieťových nastavení.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Zviazať s IP adresou\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP Rodina\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Zapnúť sieťové optimalizácie\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Zapnúť TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Pokúsi sa prispôsobiť RTMP výstup tak, aby bol priateľskejší k ďalším aplikáciám, ktoré sú citlivé na odozvu a to reguláciou rýchlosti odosielania.\\nMôže zvýšiť počet zahodených snímkov na nestabilných pripojeniach.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Chovanie Klávesových Skratiek\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nikdy nevypínať klávesové skratky\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Zakázať klávesové skratky, keď je hlavné okno aktívne\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Zakázať klávesové skratky, keď je hlavné okno neaktívne\"\nBasic.Settings.Advanced.AutoRemux=\"Automaticky previesť na %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(nahrať ako mkv)\"\nBasic.AdvAudio=\"Pokročilé nastavenia zvuku\"\nBasic.AdvAudio.ActiveOnly=\"Iba Aktívne Zdroje\"\nBasic.AdvAudio.Name=\"Názov\"\nBasic.AdvAudio.Volume=\"Hlasitosť\"\nBasic.AdvAudio.VolumeSource=\"Hlasitosť pre '%1'\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix pre '%1'\"\nBasic.AdvAudio.Balance=\"Vyváženie\"\nBasic.AdvAudio.BalanceSource=\"Vyváženie pre '%1'\"\nBasic.AdvAudio.SyncOffset=\"Oneskorenie synchronizácie\"\nBasic.AdvAudio.SyncOffsetSource=\"Oneskorenie synchronizácie pre '%1'\"\nBasic.AdvAudio.Monitoring=\"Monitorovanie zvuku\"\nBasic.AdvAudio.Monitoring.None=\"Vypnuté monitorovanie\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Iba monitorovanie (žiaden výstup)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitorovanie a výstup\"\nBasic.AdvAudio.MonitoringSource=\"Monitorovanie zvuku pre '%1'\"\nBasic.AdvAudio.AudioTracks=\"Stopy\"\nBasic.Settings.Hotkeys=\"Klávesové skratky\"\nBasic.Settings.Hotkeys.Pair=\"Klávesové skratky pre \\\"%1\\\" slúžia ako prepínače\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrovanie podľa klávesovej skratky\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Táto klávesová skratka je zdieľaná jednou alebo viacerými inými akciami, kliknutím zobrazíte konflikty\"\nBasic.Settings.Hotkeys.PleaseWait=\"Načítavajú sa klávesové skratky, čakajte prosím...\"\nBasic.Hotkeys.SelectScene=\"Prepnúť na scénu\"\nBasic.SystemTray.Show=\"Ukázať\"\nBasic.SystemTray.Hide=\"Skryť\"\nBasic.SystemTray.Message.Reconnecting=\"Odpojený. Znovu-pripájanie...\"\nHotkeys.Left=\"Vľavo\"\nHotkeys.Right=\"Vpravo\"\nHotkeys.Up=\"Hore\"\nHotkeys.Down=\"Dole\"\nHotkeys.Space=\"Medzerník\"\nHotkeys.NumpadNum=\"Numerická klávesa %1\"\nHotkeys.NumpadMultiply=\"Číselník *\"\nHotkeys.NumpadDivide=\"Číselník /\"\nHotkeys.NumpadAdd=\"Číselník +\"\nHotkeys.NumpadSubtract=\"Číselník -\"\nHotkeys.NumpadDecimal=\"Číselník ,\"\nHotkeys.AppleKeypadNum=\"%1 (num. klávesnica)\"\nHotkeys.AppleKeypadMultiply=\"* (num. klávesnica)\"\nHotkeys.AppleKeypadDivide=\"/ (num. klávesnica)\"\nHotkeys.AppleKeypadAdd=\"+ (num. klávesnica)\"\nHotkeys.AppleKeypadSubtract=\"- (num. klávesnica)\"\nHotkeys.AppleKeypadDecimal=\". (num. klávesnica)\"\nHotkeys.AppleKeypadEqual=\"= (num. klávesnica)\"\nHotkeys.MouseButton=\"Tlačidlo myši %1\"\nMute=\"Stlmiť\"\nUnmute=\"Zrušiť stlmenie\"\nPush-to-mute=\"Stlač-pre-stíšenie\"\nPush-to-talk=\"Stlač-pre-rozprávanie\"\nSceneItemShow=\"Zobraz '%1'\"\nSceneItemHide=\"Skry '%1'\"\nOutputWarnings.NoTracksSelected=\"Musíte vybrať aspoň jednu zvukovú stopu\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Chyba nastavení výstupu\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Všetky výstupy musia mať aspoň jednu zvukovú stopu vybranú.\"\nOutputWarnings.MP4Recording=\"Upozornenie: Nahrávky uložené v MP4/MOV nebude možné obnoviť, ak súbor nebude korektne ukončený (napr. po BSOD, výpadku napájania atp.). Ak chcete nahrávať viacero zvukových stôp, skúste použiť MKV a po dokončení súbor previesť do MP4/MOV (Súbor -> Previesť nahrávky)\"\nOutputWarnings.CannotPause=\"Upozornenie: Nahrávanie nemôže byť pozastavené, ak je kvalita nahrávania nastavená na \\\"Rovnaké ako pre vysielanie\\\"\"\nOutputWarnings.CodecIncompatible=\"Vybratý zvukový alebo obrazový enkodér bol obnovený kvôli nekompatibilite. Prosím vyberte kompatibilný enkodér zo zoznamu.\"\nCodecCompat.Incompatible=\"(Nekompatibilné s %1)\"\nCodecCompat.CodecPlaceholder=\"Vybrať enkodér...\"\nCodecCompat.ContainerPlaceholder=\"Vybrať formát...\"\nCodecCompat.CodecMissingOnExit.Title=\"Žiaden vybratý enkodér\"\nCodecCompat.CodecMissingOnExit.Text=\"Aspoň jeden obrazový alebo zvukový enkodér nie je vybratý. Prosím uistite sa že ste vybrali enkodéri pre nahrávanie a streamovanie.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Žiaden vybratý formát\"\nCodecCompat.ContainerMissingOnExit.Text=\"Nebol vybratý žiaden nahrávací formát. Prosím vyberte nahrávací formát, ktorý je kompatibilný s vybratým streamovacím enkodérom.\"\nFinalScene.Title=\"Odstrániť scénu\"\nFinalScene.Text=\"Musí existovať aspoň jedna scéna.\"\nNoSources.Title=\"Žiadne zdroje\"\nNoSources.Text=\"Nepridali ste žiadne zdroje obrazu, takže výstup bude prázdny. Naozaj chcete pokračovať?\"\nNoSources.Text.AddSource=\"Zdroje môžete kedykoľvek pridať kliknutím na ikonu + v zozname zdrojov v hlavnom okne.\"\nNoSources.Label=\"Nemáte žiadne zdroje.\\nKliknite na tlačidlo + nižšie\\nalebo pravým kliknite sem a nejaký pridajte.\"\nChangeBG=\"Nastaviť farbu\"\nCustomColor=\"Vlastná farba\"\nBrowserSource.EnableHardwareAcceleration=\"Zapnúť hardvérovú akceleráciu pre zdroje prehliadača\"\nAbout=\"O programe\"\nAbout.Info=\"OBS Štúdio je softvér pre nahrávanie videa a živé vysielanie. Má otvorený zdroj a je zdarma.\"\nAbout.Donate=\"Prispieť\"\nAbout.GetInvolved=\"Zapojiť sa\"\nAbout.Authors=\"Autori\"\nAbout.License=\"Licencia\"\nAbout.Error=\"Chyba! Súbor nemohol byť prečítaný.\\n\\nChoďte na: %1\"\nAbout.Contribute=\"Podporte projekt OBS\"\nAddUrl.Title=\"Pridať Zdroj cez URL\"\nAddUrl.Text=\"Pretiahli ste nejaký URL do OBS. Toto automaticky pridá daný link ako zdroj. Pokračovať?\"\nResizeOutputSizeOfSource=\"Zmeniť veľkosť výstupu (veľkosť zdroja)\"\nResizeOutputSizeOfSource.Text=\"Základné a výstupné rozlíšenie budú zmenené na veľkosť súčasného zdroja.\"\nResizeOutputSizeOfSource.Continue=\"Chcete pokračovať?\"\nPreviewTransition=\"Ukážka prechodu\"\nImporter=\"Dovozca scény\"\nImporter.SelectCollection=\"Vyberte zbierku scény\"\nImporter.Collection=\"Zbierka scén\"\nImporter.HelpText=\"Do tohto okna pridajte súbory a importujte zbierky z programu OBS alebo iných podporovaných programov.\"\nImporter.Path=\"Cesta zbierky\"\nImporter.Program=\"Zistená aplikácia\"\nImporter.AutomaticCollectionPrompt=\"Automatické vyhľadávanie zbierok scén\"\nImporter.AutomaticCollectionText=\"OBS dokáže automaticky nájsť importovateľné kolekcie scén z podporovaných programov tretích strán. Chcete, aby spoločnosť OBS automaticky vyhľadávala zbierky?\\n\\nMôžete to zmeniť neskôr v časti Nastavenia > Všeobecné > Dovozcovia.\"\nImporter.SelectFile=\"Prehľadávať...\"\nRestart=\"Reštartovať\"\nNeedsRestart=\"Aplikáciu OBS Štúdio je potrebné reštartovať. Chcete reštartovať teraz?\"\nLoadProfileNeedsRestart=\"Profil obsahuje nastavenia, ktoré vyžadujú reštart OBS:\\n%1\\n\\nChcete reštartovať OBS pre použitie týchto nastavení?\"\nContextBar.NoSelectedSource=\"Žiaden vybratý zdroj\"\nContextBar.MediaControls.PlayMedia=\"Prehrať médiá\"\nContextBar.MediaControls.PauseMedia=\"Pozastaviť médiá\"\nContextBar.MediaControls.StopMedia=\"Zastaviť médiá\"\nContextBar.MediaControls.RestartMedia=\"Reštartovať médiá\"\nContextBar.MediaControls.PlaylistNext=\"Ďalšia v zozname skladieb\"\nContextBar.MediaControls.PlaylistPrevious=\"Predchádzajúca v zozname skladieb\"\nContextBar.MediaControls.BlindSeek=\"Widget posuvníka médií\"\nYouTube.Auth.Ok=\"Autorizácia dokončená úspešne.\\nTeraz môžete zavrieť túto stránku.\"\nYouTube.Auth.NoCode=\"Autorizačný proces nebol dokončený.\"\nYouTube.Auth.NoChannels=\"Žiaden dostupný kanál na vybranom účte\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube užívateľská autorizácia\"\nYouTube.Auth.WaitingAuth.Text=\"Prosím dokončite autorizáciu vo svojom externom prehliadači.<br>Ak sa externý prehliadač neotvára, nasledujte tento odkaz a dokončite autorizáciu:<br>%1\"\nYouTube.AuthError.Text=\"Nepodarilo sa získať informácie o kanáli: %1.\"\nYouTube.Actions.WindowTitle=\"Nastavenie YouTube vysielania - Kanál: %1\"\nYouTube.Actions.CreateNewEvent=\"Vytvoriť nové vysielanie\"\nYouTube.Actions.ChooseEvent=\"Vybrať existujúce vysielanie\"\nYouTube.Actions.Title=\"Názov*\"\nYouTube.Actions.MyBroadcast=\"Moje Vysielanie\"\nYouTube.Actions.Description=\"Popis\"\nYouTube.Actions.Privacy=\"Súkromie*\"\nYouTube.Actions.Privacy.Private=\"Súkromný\"\nYouTube.Actions.Privacy.Public=\"Verejný\"\nYouTube.Actions.Privacy.Unlisted=\"Nezaradený\"\nYouTube.Actions.Category=\"Kategória\"\nYouTube.Actions.Thumbnail=\"Náhľad\"\nYouTube.Actions.Thumbnail.SelectFile=\"Vybrať súbor...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Žiaden vybratý súbor\"\nYouTube.Actions.Thumbnail.ClearFile=\"Vyčistiť\"\nYouTube.Actions.MadeForKids=\"Je toto video robené pre deti?*\"\nYouTube.Actions.MadeForKids.Yes=\"Áno, je robené pre deti\"\nYouTube.Actions.MadeForKids.No=\"Nie, nie je robené pre deti\"\nYouTube.Actions.AdditionalSettings=\"Ďalšie nastavenia\"\nYouTube.Actions.Latency=\"Odozva\"\nYouTube.Actions.Latency.Normal=\"Normálna\"\nYouTube.Actions.Latency.Low=\"Nízka\"\nYouTube.Actions.Latency.UltraLow=\"Ultra nízka\"\nYouTube.Actions.EnableAutoStart=\"Zapnúť Auto-štart\"\nYouTube.Actions.EnableAutoStop=\"Zapnúť Auto-stop\"\nYouTube.Actions.AutoStartStop.TT=\"Indikuje, či by sa toto plánované vysielanie malo spustiť automaticky.\"\nYouTube.Actions.EnableDVR=\"Zapnúť DVR\"\nYouTube.Actions.ScheduleForLater=\"Naplánovať na neskôr\"\nYouTube.Actions.RememberSettings=\"Zapamätať tieto nastavenia\"\nYouTube.Actions.Create_Ready=\"Vytvoriť vysielanie\"\nYouTube.Actions.Create_GoLive=\"Vytvoriť vysielanie a začať streamovať\"\nYouTube.Actions.Choose_Ready=\"Vybrať vysielanie\"\nYouTube.Actions.Choose_GoLive=\"Vybrať vysielanie a začať streamovať\"\nYouTube.Actions.Create_Schedule=\"Naplánovať vysielanie\"\nYouTube.Actions.Create_Schedule_Ready=\"Naplánovať a vybrať vysielanie\"\nYouTube.Actions.Dashboard=\"Otvoriť YouTube Štúdio\"\nYouTube.Actions.Error.Title=\"Chyba pri vytvorení živého vysielania\"\nYouTube.Actions.Error.Text=\"Chyba prístupu na YouTube '%1'.<br/>Podrobný popis chyby môžete nájsť na <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Chyba prístupu na YouTube. Prosím skontrolujte sieťové pripojenie alebo prístup na YouTube server.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Chyba pri vytváraní vysielania '%1'.<br/>Podrobný popis chyby môžete nájsť na <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Nebol vytvorený žiaden stream. Prosím znova pripojte svoj účet.\"\nYouTube.Actions.Error.YouTubeApi=\"Chyba YouTube API. Prosím pozrite si log súbor pre viac informácií.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Vybraté vysielanie nebolo nájdené.\"\nYouTube.Actions.Error.FileMissing=\"Vybratý súbor neexistuje.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Otvorenie vybratého súboru zlyhalo.\"\nYouTube.Actions.Error.FileTooLarge=\"Vybratý súbor je príliš veľký (Limit: 2MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Prechod vysielania zlyhal: %1<br/><br/>Ak táto chyba pretrváva <a href='https://studio.youtube.com/video/%2/livestreaming'>otvorte vysielanie v YouTube Štúdiu</a> a skúste to ručne.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Vysielanie prechádza do testovacej fáze, toto môže chvíľu trvať. Prosím skúste znova za 10-30 sekúnd.\"\nYouTube.Actions.EventsLoading=\"Načítavanie zoznamu udalostí...\"\nYouTube.Actions.EventCreated.Title=\"Udalosť vytvorená\"\nYouTube.Actions.EventCreated.Text=\"Udalosť úspešne vytvorená.\"\nYouTube.Actions.Stream.ScheduledFor=\"Naplánovaný na %1\"\nYouTube.Actions.Stream.Resume=\"Obnoviť prerušený stream\"\nYouTube.Actions.Stream.YTStudio=\"Automaticky vytvorené cez Štúdio YouTube\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Vytváranie nového živého vysielania, prosím počkajte...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Manuálny štart vyžadovaný\"\nYouTube.Actions.AutoStartStreamingWarning=\"Auto-štart je vypnutý pre túto udalosť, kliknite \\\"Začať vysielať\\\" pre spustenie vášho vysielania.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Nebudete môcť obnoviť pripojenie.<br>Váš stream sa zastaví a nebudete vysielať naživo.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Živé streamovanie nie je zapnuté na vybratom YouTube kanáli.<br/><br/>Pozrite si <a href='https://www.youtube.com/features'>youtube.com/features</a> pre viac informácií.\"\nYouTube.Errors.livePermissionBlocked=\"Živé streamovanie nie je dostupné na vybratom YouTube kanáli.<br/>Prosím uvedomte si že môže trvať až 24 hodín aby sa živé streamovanie stalo dostupné po aktivácií v nastaveniach kanála.<br/><br/>Pozrite si <a href='https://www.youtube.com/features'>youtube.com/features</a> pre detaily.\"\nYouTube.Errors.errorExecutingTransition=\"Prechod zlyhal kvôli chybe servera. Prosím skúste znova za pár sekúnd.\"\nYouTube.Errors.errorStreamInactive=\"YouTube neprijíma žiadne dáta z vášho streamu. Prosím skontrolujte vašu konfiguráciu a skúste znova.\"\nYouTube.Errors.invalidTransition=\"Pokus o prechod bol neplatný. Toto môže byť kvôli predchádzajúcemu nedokončenému prechodu streamu. Prosím počkajte zopár sekúnd a skúste znova.\"\nYouTube.DocksRemoval.Title=\"Odstrániť pôvodné doky YouTube prehliadača\"\nYouTube.DocksRemoval.Text=\"Tieto doky prehliadača budú odstránené ako zastaralé:\\n\\n%1\\nPoužite \\\"Doky/YouTube Live Control Room\\\" namiesto toho.\"\nConfigDownload.WarningMessageTitle=\"Upozornenie\"\nFailedToStartStream.MissingConfigURL=\"Žiadna dostupná konfiguračná URL adresa pre aktuálnu službu\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Vlastná RTMP URL adresa nebola špecifikovaná\"\nFailedToStartStream.InvalidCustomConfig=\"Neplatná vlastná konfigurácia\"\nFailedToStartStream.MissingCanvas=\"Nakonfigurované extra plátno chýba\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Zlyhalo vytvorenie viacstopovej video služby\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Zlyhalo vytvorenie viacstopového RTMP video výstupu\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC nedostupný.\\n\\nNebolo možné nájsť enkodér typu '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Zlyhalo vytvorenie video enkodéra '%1' (typ: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Zlyhalo získanie OBS video informácií počas vytvárania enkodéra '%1' (typ: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Zlyhalo vytvorenie audio enkodéra\"\nFailedToStartStream.NoRTMPURLInConfig=\"Konfigurácia neobsahuje cieľovú streamovaciu RTMP(S) URL adresu\"\nFailedToStartStream.FallbackToDefault=\"Spustenie streamu s použitím %1 zlyhalo; chcete to skúsiť znova s použitím nastavení jedného kódovania?\"\nFailedToStartStream.ConfigRequestFailed=\"Nebolo možné získať konfiguráciu z %1<br><br>HTTP chyba: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Prijatá neznáma hodnota stavu '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nChcete pokračovať v streamovaní bez %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nChcete pokračovať v streamovaní?\"\nFailedToStartStream.MissingEncoderConfigs=\"Konfigurácia spustenia streamovania neobsahovala konfigurácie enkodéra\"\nFailedToStartStream.InvalidEncoderConfig=\"Konfigurácia spustenia streamovania neobsahovala konfigurácie enkodéra\"\nFailedToStartStream.StatusMissingHTML=\"Požiadavka pre začatie vysielania vrátila nešpecifikovanú chybu\"\nFailedToStartStream.NoConfigSupplied=\"Chýbajúca konfigurácia\"\nMultitrackVideo.Info=\"%1 automaticky optimalizuje vaše nastavenia pre enkódovanie a odosielanie viacerých kvalít videa. Vybratím tejto možnosti pošle %2 informácie o vašom počítači a nastavenie softvéru.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Nekompatibilné nastavenia\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 nie je momentálne kompatibilný s:\\n\\n%2\\nPre pokračovanie streamovania s %1, vypnite nekompatibilné nastavenia:\\n\\n%3\\na spustite streamovanie znova.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Vypnúť pre tento stream a začať streamovanie\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Aktualizovať nastavenia a začať streamovanie\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 nie je momentálne kompatibilný s [Zvuk → Všeobecné → Kanály] nastavené na '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Zvuk → Všeobecné → Kanály] je potrebné nastaviť na '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 vyžaduje viacero rôznych nastavení pre [Zvuk → Všeobecné → Kanály]\"\nBasic.OpenPluginManager=\"Správca pluginov\"\nPluginManager=\"Správca pluginov\"\nPluginManager.Restart=\"OBS Štúdio musí byť reštartované pre použitie zmien\"\nPluginManager.MissingPlugin=\"[PLUGIN NENÁJDENÝ]\"\nPluginManager.Section.Discover=\"Prehliadať\"\nPluginManager.Section.Manage=\"Nainštalovaný\"\nPluginManager.Section.Updates=\"Aktualizácie\"\nPluginManager.Section.Manage.Title=\"Spravovať zapnuté pluginy\"\n"
  },
  {
    "path": "frontend/data/locale/sl-SI.ini",
    "content": "Language=\"Slovenščina\"\nOK=\"V redu\"\nApply=\"Uporabi\"\nCancel=\"Prekliči\"\nClose=\"Zapri\"\nSave=\"Shrani\"\nDiscard=\"Opusti\"\nDisable=\"Onemogoči\"\nYes=\"Da\"\nNo=\"Ne\"\nAdd=\"Dodaj\"\nRemove=\"Odstrani\"\nRename=\"Preimenuj\"\nInteract=\"Interakcija\"\nFilters=\"Filtri\"\nProperties=\"Lastnosti\"\nMoveUp=\"Premakni gor\"\nMoveDown=\"Premakni dol\"\nSettings=\"Nastavitve\"\nDisplay=\"Zaslon\"\nName=\"Ime\"\nExit=\"Izhod\"\nMixer=\"Zvočna mešalka\"\nBrowse=\"Brskaj\"\nDroppedFrames=\"Spuščeni okvirji %1 (%2 %)\"\nProjector.Open.Program=\"Odpri projektor programa\"\nProjector.Open.Preview=\"Odpri projektor predogleda\"\nProjector.Open.Scene=\"Odpri projektor scene\"\nProjector.Open.Source=\"Odpri projektor vira\"\nProjector.Open.Multiview=\"Odpri več-prikazno\"\nProjector.Display=\"Zaslon: %1\"\nProjector.Window=\"Novo okno\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Scena: %1\"\nProjector.Title.Source=\"Vir: %1\"\nProjector.Title.Multiview=\"Več-prikazno\"\nProjector.ResizeWindowToContent=\"Prilagodi okno vsebini\"\nClear=\"Počisti\"\nRevert=\"Povrni\"\nShow=\"Prikaži\"\nHide=\"Skrij\"\nUnhideAll=\"Odkrij vse\"\nUntitled=\"Neimenovan\"\nNew=\"Nov\"\nDuplicate=\"Podvoji\"\nEnable=\"Omogoči\"\nDisableOSXVSync=\"Onemogoči macOS V-Sync\"\nResetOSXVSyncOnExit=\"Ponastavi macOS V-Sync ob izhodu\"\nHighResourceUsage=\"Preobremenjeno kodiranje! Zmanjšajte zahtevnost videa v nastavitvah ali za kodiranje uporabite hitrejšo prednastavitev.\"\nTransition=\"Prehod\"\nQuickTransitions=\"Hitri prehodi\"\nFadeToBlack=\"Preliv v črno\"\nLeft=\"Levo\"\nRight=\"Desno\"\nTop=\"Zgoraj\"\nBottom=\"Spodaj\"\nReset=\"Ponastavi\"\nHours=\"urah\"\nMinutes=\"minutah\"\nSeconds=\"sekundah\"\nDeprecated=\"Opuščeno\"\nReplayBuffer=\"Medpomn. za pon. predv.\"\nImport=\"Uvozi\"\nExport=\"Izvozi\"\nCopy=\"Kopiraj\"\nPaste=\"Prilepi\"\nPasteReference=\"Prilepi (referenco)\"\nPasteDuplicate=\"Prilepi (dvojnika)\"\nRemuxRecordings=\"Prepakiranje posnetkov\"\nNext=\"Naprej\"\nBack=\"Nazaj\"\nDefaults=\"Privzeto\"\nRestoreDefaults=\"Privzeto\"\nHideMixer=\"Skrij v mešalniku\"\nTransitionOverride=\"Vsili prehod\"\nShowTransition=\"Prikaži prehod\"\nHideTransition=\"Skrij prehod\"\nNone=\"Brez\"\nStudioMode.Preview=\"Predogled\"\nStudioMode.PreviewSceneName=\"Predogled: %1\"\nShowInMultiview=\"Prikaži v več pogledih\"\nVerticalLayout=\"Navpična razporeditev\"\nGroup=\"Skupina\"\nDoNotShowAgain=\"Ne prikaži ponovno\"\nDefault=\"(privzeto)\"\nCalculating=\"Računanje...\"\nFullscreen=\"Celozaslonsko\"\nWindowed=\"Okensko\"\nRefreshBrowser=\"Osveži\"\nAspectRatio=\"Razmerje slike <b>%1%2</b>\"\nLockVolume=\"Zakleni glasnost\"\nLogViewer=\"Pregledovalnik dnevnika\"\nShowOnStartup=\"Pokaži ob zagonu\"\nOpenFile=\"Odpri datoteko\"\nAddScene=\"Dodaj sceno\"\nAddSource=\"Dodaj vir\"\nRemoveScene=\"Odstrani izbrani prizor\"\nRemoveSource=\"Odstrani izbrani vir\"\nMoveSceneUp=\"Premakni prizor navzgor\"\nMoveSceneDown=\"Premakni prizor navzdol\"\nMoveSourceUp=\"Premakni vir(e) navzgor\"\nMoveSourceDown=\"Premakni vir(e) navzdol\"\nSourceProperties=\"Odpri lastnosti vira\"\nSourceFilters=\"Odpri filtre vira\"\nMixerToolbarMenu=\"Meni mešalnika zvoka\"\nSceneFilters=\"Odpti filtre prizora\"\nList=\"Seznam\"\nGrid=\"Mreža\"\nAutomatic=\"Samodejno\"\nComingSoon=\"Kmalu na voljo\"\nPluginsFailedToLoad.Title=\"Napaka pri nalaganju vstavka\"\nPluginsFailedToLoad.Text=\"Naslednji vstavki OBS se niso uspešno naložili.\\n\\n%1\\nTe vstavke posodobite ali odstranite.\"\nAlreadyRunning.Title=\"OBS se že izvaja\"\nAlreadyRunning.Text=\"OBS se že izvaja! Če tega niste že storili, zaprite vse obstoječe primerke OBS, preden poskusite zagnati nov primerek. Če imate OBS nastavljen, da se zmanjša v sistemsko vrstico, preverite, ali se tam še vedno izvaja.\"\nAlreadyRunning.LaunchAnyway=\"Vseeno zaženi\"\nCrashHandling.Dialog.Title=\"OBS se je sesul\"\nCrashHandling.Labels.Text=\"Ali želite ponovno zagnati OBS v varnem načinu (z onemogočenimi vstavki, skriptami in spletnimi vtičnicami tretjih strani)?\"\nCrashHandling.Labels.PrivacyNotice=\"Lahko izberete tudi samodejno nalaganja poročila o sesutju na OBSProject.<br /><br />Preberite <a href='https://obsproject.com/privacy-policy'>pravilnik o zasebnosti</a> preden naložite katerokoli datoteko in bodite še posebej pozorni na razdelke, ki govorijo o nalaganju datotek.\"\nCrashHandling.Checkbox.SendReport=\"Prebral sem pravilnik o zasebnosti in stirnjam se z nalaganjem.\"\nCrashHandling.Buttons.LaunchSafe=\"Zagon v varnem načinu\"\nCrashHandling.Buttons.LaunchNormal=\"Zagon v normalnem načinu\"\nCrashHandling.Errors.UploadJSONError=\"Pri nalaganju zadnjega dnevnika o sesutju se je zgodila napaka. Znova poskusite kasneje.\"\nCrashHandling.Errors.Title=\"Napaka pri nalaganju dnevnika o sesutju\"\nSafeMode.Restart=\"Ali želite ponovno zagnati OBS v varnem načinu (z onemogočenimi vstavki, skriptami in spletnimi vtičnicami tretjih strani)?\"\nSafeMode.RestartNormal=\"Ali želite ponovno zagnati OBS v navadnem načinu?\"\nChromeOS.Title=\"Nepodprta platforma\"\nChromeOS.Text=\"Kaže, da se OBS izvaja znotraj vsebnika ChromeOS. Ta platforma ni podprta.\"\nWine.Title=\"Wine zaznan\"\nWine.Text=\"Izvajanje OBS v Wine ni podprto in veliko funkcionalnosti, kot je zajem ali viri naprav ne bodo delovali ali pa bodo delovali v omejenem načinu.<br><br>Priporočamo, da zaženete domorodno izajo OBS, npr. <a href='https://flathub.org/apps/details/com.obsproject.Studio'>našo različico Flatpak</a> ali pakete za vaš operacijski sistem.\"\nDockCloseWarning.Title=\"Zapiranje sidrnega okna\"\nDockCloseWarning.Text=\"Pravkar ste zaprli okno sidrišča. Če bi ga radi zopet prikazali, uporabite meni sidrišča iz vrstice z menijem.\"\nExtraBrowsers=\"Brskalnikova sidrišča po meri\"\nExtraBrowsers.Info=\"Dodajte sidrišča tako, da jim dodate ime in URL, in nato kliknite Uporabi ali Zapri, da jih odprete. Sidrišča lahko dodate ali odstranite kadarkoli.\"\nExtraBrowsers.DockName=\"Ime sidrišča\"\nAuth.Authing.Title=\"Overjanje...\"\nAuth.Authing.Text=\"Overjanje preko sistema %1. Prosimo, počakajte...\"\nAuth.AuthFailure.Title=\"Neuspešna overitev\"\nAuth.AuthFailure.Text=\"Overitev s/z %1 je spodletela:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Potrebna je overitev\"\nAuth.InvalidScope.Text=\"Overitvene zahteve za %1 so se spremenile. Nekatere funkcije morda ne bodo na voljo.\"\nAuth.LoadingChannel.Title=\"Nalaganje informacij o kanalu...\"\nAuth.LoadingChannel.Text=\"Nalaganje informacij o kanalu za %1. Prosimo, počakajte...\"\nAuth.LoadingChannel.Error=\"Ni bilo mogoče pridobiti podatkov o kanalu.\"\nAuth.ChannelFailure.Title=\"Nalaganje kanala neuspešno\"\nAuth.ChannelFailure.Text=\"Nalaganje informacij o kanalu za %1 je spodletelo\\n\\n%2: %3\"\nAuth.Chat=\"Klepet\"\nAuth.StreamInfo=\"Informacije o pretoku\"\nTwitchAuth.Stats=\"Twitch statistika\"\nTwitchAuth.Feed=\"Vir dejavnosti na Twitchu\"\nTwitchAuth.TwoFactorFail.Title=\"Ni bilo mogoče izvesti poizvedbe za ključ pretoka\"\nTwitchAuth.TwoFactorFail.Text=\"OBS se ni mogel povezati z vašim Twitch računom. Prepričajte se, da je v vaših <a href='https://www.twitch.tv/settings/security'>varnostnih nastavitvah Twitcha</a> nastavljeno dvostopenjsko overjanje, kot ga zahteva pretakanje.\"\nRestreamAuth.Channels=\"Restream kanali\"\nCopy.Filters=\"Kopiraj filtre\"\nPaste.Filters=\"Prilepi filtre\"\nBrowserPanelInit.Title=\"Zagon brskalnika...\"\nBrowserPanelInit.Text=\"Brskalnik se zaganja. Prosimo, počakajte...\"\nBandwidthTest.Region=\"Regija\"\nBandwidthTest.Region.US=\"Združene države Amerike\"\nBandwidthTest.Region.EU=\"Evropa\"\nBandwidthTest.Region.Asia=\"Azija\"\nBandwidthTest.Region.Other=\"Ostalo\"\nBasic.AutoConfig=\"Čarovnik za samodejno nastavitev\"\nBasic.AutoConfig.ApplySettings=\"Uporabi nastavitve\"\nBasic.AutoConfig.StartPage=\"Informacije o uporabi\"\nBasic.AutoConfig.StartPage.SubTitle=\"Navedite, za kaj želite program uporabljati\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimiziraj za pretakanje - snemanje je drugotnega pomena\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimiziraj samo za snemanje - ne bom pretakal\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Uporabljala se bo samo virtualna kamera\"\nBasic.AutoConfig.VideoPage=\"Nastavitve videa\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Določite želene nastavitve videa, ki bi jih radi uporabili\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Uporabi trenutno (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Zaslon %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Uporabi trenutno (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ali 30, vendar raje uporabi 60, če je to mogoče\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ali 30, vendar raje uporabi višjo ločljivost\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Opomba: ločljivost platna (osnovna ločljivost) ni nujno enaka ločljivosti, s katero boste pretakali ali snemali. Dejanska ločljivost pretakanja/snemanja je lahko nižja od ločljivosti platna, da se zmanjša poraba virov ali bitna hitrost.\"\nBasic.AutoConfig.StreamPage=\"Podatki o pretoku\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Vnesite podatke o pretoku\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Poveži račun (priporočljivo)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Odklopi račun\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Odklopim račun?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Ta sprememba bo začela veljati takoj. Ali res želite odklopiti račun?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Dobi ključ pretoka\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Več informacij\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Uporabi ključ pretoka\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Uporabi pretočni ključ (napredno)\"\nBasic.AutoConfig.StreamPage.Service=\"Storitev\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Prikaži vse...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Po meri...\"\nBasic.AutoConfig.StreamPage.Server=\"Strežnik\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Ključ pretoka\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: vnesite šifrirno geslo.\\nRTMP: vnesite ključ, ki ga zagotovi storitev.\\nSRT: vnesite id toka streamid, če ga storitev uporablja.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Ključ kodirnika\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Žeton prenašalca\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Povezan račun\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Oceni bitno hitrost s preizkusom pasovne širine (lahko traja nekaj minut)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Daj prednost strojnemu kodiranju\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Strojno kodiranje zelo razbremeni procesor, a lahko za dosego enake kakovosti potrebuje večjo bitno hitrost.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Opozorilo pretoka\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Preizkus pasovne širine bo pretakal naključne slikovne podatke brez zvoka na vaš kanal. Če je možno, je priporočljivo začasno izklopiti shranjevanje video pretoka in ga nastaviti na zasebno, dokler se preizkus ne konča. Nadaljujem?\"\nBasic.AutoConfig.TestPage=\"Končni rezultat\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Program trenutno izvaja vrsto preizkusov, da lahko oceni, katere nastavitve so najprimernejše\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Preizkušanje je končano\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Izvajanje preizkusa pasovne širine. To lahko traja nekaj minut...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Za protokol te storitve ni najden noben izhod\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Povezovanje na: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Povezava s strežniki je spodletela. Preverite spletno povezavo in poizkusite znova.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Preizkušanje pasovne širine za: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Preizkušanje kodirnika pretoka. To lahko traja nekaj minut...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Preizkušanje snemalnega kodirnika. To lahko traja nekaj minut …\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Zagon kodirnika je spodletel\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Preizkušanje %1x%2 %3 sl./s...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Kodirnik pretoka\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Snemalni kodirnik\"\nBasic.AutoConfig.TestPage.Result.Header=\"Program je ocenil, da so te nastavitve najprimernejše za vas:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Za uporabo teh nastavitev kliknite 'Uporabi nastavitve'. Za ponovno nastavitev čarovnika in vnovičen poskus kliknite 'Nazaj'. Za ročno nastavitev kliknite 'Prekliči' in odprite nastavitve.\"\nBasic.AutoConfig.Info=\"Namestitveni čarovnik bo določil najboljše nastavitve glede na tehnične specifikacije računalnika in na internetno povezavo.\"\nBasic.AutoConfig.RunAnytime=\"To lahko kadarkoli zaženete iz menija Orodja.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Ločljivost pretoka (povečana)\"\nBasic.Stats=\"Statistika\"\nBasic.Stats.CPUUsage=\"Obremenitev procesorja\"\nBasic.Stats.HDDSpaceAvailable=\"Razpoložljiv prostor na disku\"\nBasic.Stats.MemoryUsage=\"Poraba pomnilnika\"\nBasic.Stats.AverageTimeToRender=\"Povprečen čas za izris sličice\"\nBasic.Stats.SkippedFrames=\"Preskočene sličice zaradi prepočasnega kodiranja\"\nBasic.Stats.MissedFrames=\"Zgrešene sličice zaradi prepočasnega izrisovanja\"\nBasic.Stats.Output.Stream=\"Pretok\"\nBasic.Stats.Output.Recording=\"Snemanje\"\nBasic.Stats.Status=\"Stanje\"\nBasic.Stats.Status.Recording=\"Snemanje\"\nBasic.Stats.Status.Live=\"V ŽIVO\"\nBasic.Stats.Status.Reconnecting=\"Ponovno vzpostavljanje povezave\"\nBasic.Stats.Status.Inactive=\"Nedejavno\"\nBasic.Stats.Status.Active=\"Dejaven\"\nBasic.Stats.DroppedFrames=\"Izpuščene sličice (omrežje)\"\nBasic.Stats.MegabytesSent=\"Skupaj izhodnih podatkov\"\nBasic.Stats.Bitrate=\"Bitna hitrost\"\nBasic.Stats.DiskFullIn=\"Disk bo poln v (pribl.)\"\nBasic.Stats.DiskFullIn.Text=\"ur: %1, minut: %2\"\nBasic.Stats.ResetStats=\"Ponastavi statistiko\"\nResetUIWarning.Title=\"Ali res želite ponastaviti uporabniški vmesnik?\"\nResetUIWarning.Text=\"Ponastavitev uporabniškega vmesnika bo skrila dodatna sidrišča. Če jih želite spet prikazati, morate to storiti iz menija sidrišča.\\n\\nŽelite res ponastaviti uporabniški vmesnik?\"\nUpdater.Title=\"Na voljo so nove posodobitve\"\nUpdater.Text=\"Na voljo je nova posodobitev:\"\nUpdater.UpdateNow=\"Posodobi zdaj\"\nUpdater.RemindMeLater=\"Opomni me kasneje\"\nUpdater.Skip=\"Preskoči različico\"\nUpdater.NoUpdatesAvailable.Title=\"Na voljo ni nobenih posodobitev\"\nUpdater.NoUpdatesAvailable.Text=\"Na voljo ni novih posodobitev\"\nUpdater.BranchNotFound.Title=\"Kanal za posodobitev je odstranjen\"\nUpdater.BranchNotFound.Text=\"Izbrani kanal za posodobitev ni več na voljo. OBS je bil ponastavljen na privzeto.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Preverjanje celovitosti ni na voljo\"\nUpdater.RepairButUpdatesAvailable.Text=\"Preverjanje celovitosti datoteke je možno le za najnovejše različice. Uporabite Pomoč → Preveri obstoj posodobitev, da preverite in posodobite svojo namestitev OBS.\"\nUpdater.RepairConfirm.Title=\"Potrdite preverjanje celovitosti\"\nUpdater.RepairConfirm.Text=\"Zagon preverjanja celovitosti preišče vašo namestitev OBS glede okvarjenosti in ponovno prenese okvarjene/spremenjene datoteke. To lahko traja.\\n\\nAli želite nadaljevati?\"\nUpdater.FailedToLaunch=\"Zagon programa za posodabljanje je spodletel\"\nQuickTransitions.SwapScenes=\"Po prehodu zamenjaj predogledne/programske prizore\"\nQuickTransitions.SwapScenesTT=\"Po prehodu zamenja predogledne in programske prizore (če izvirni prizor programa še obstaja).\\nTo ne bo razveljavilo sprememb, ki so bile narejene na izvirnem prizoru programa.\"\nQuickTransitions.DuplicateScene=\"Podvoji prizor\"\nQuickTransitions.DuplicateSceneTT=\"Med montažo istega prizora omogoča urejanje preoblikovanja/vidnosti virov brez spreminjanja programskega izhoda.\\nZa urejanje lastnosti virov brez spreminjanja programska izhoda omogočite 'Podvoji vire'.\\nSprememba te vrednosti bo ponastavila trenutni izhodni programski prizor (če še obstaja).\"\nQuickTransitions.EditProperties=\"Podvoji vire\"\nQuickTransitions.EditPropertiesTT=\"Med montažo istega prizora omogoča urejanje lastnosti virov brez spreminjanja programskega izhoda.\\nTo lahko uporabite samo, če je možnost 'Podvoji prizor' potrjena.\\nDoločeni viri (kot je zajem ali vir predstavnosti) tega ne podpirajo in jih ni mogoče ločeno montirati.\\nSprememba te vrednosti bo ponastavila izhodni prizor programa (če še obstaja).\\n\\nOpozorilo: ker bodo viri podvojeni, lahko to zahteva dodatne sistemske ali slikovne vire.\"\nQuickTransitions.HotkeyName=\"Hiter prehod: %1\"\nBasic.AddTransition=\"Dodaj nastavljiv prehod\"\nBasic.RemoveTransition=\"Odstrani nastavljiv prehod\"\nBasic.TransitionProperties=\"Lastnosti prehoda\"\nBasic.SceneTransitions=\"Prehodi prizorov\"\nBasic.TransitionDuration=\"Trajanje\"\nBasic.TogglePreviewProgramMode=\"Studijski način\"\nBasic.EnablePreviewProgramMode=\"Omogoči studijski način\"\nBasic.DisablePreviewProgramMode=\"Onemogoči studijski način\"\nUndo.Undo=\"Razveljavi\"\nUndo.Redo=\"Obnovi\"\nUndo.Add=\"Dodaj '%1'\"\nUndo.Delete=\"Izbriši '%1'\"\nUndo.Rename=\"Preimenuj '%1'\"\nUndo.SceneCollection.Switch=\"Preklopi na '%1'\"\nUndo.Item.Undo=\"Razveljavi %1\"\nUndo.Item.Redo=\"Obnovi %1\"\nUndo.Sources.Multi=\"Izbriši %1 virov\"\nUndo.Filters=\"Sprememba filtra na '%1'\"\nUndo.Filters.Paste.Single=\"Prilepi filter '%1' na '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopiraj filter iz '%1' na '%2'\"\nUndo.Transform=\"Preoblikovanje vira/virov v '%1'\"\nUndo.Transform.Paste=\"Prilepi preoblikovanje v '%1'\"\nUndo.Transform.Rotate=\"Zasuk v '%1'\"\nUndo.Transform.Reset=\"Ponastavitev preoblikovanja v '%1'\"\nUndo.Transform.HFlip=\"Vodoravno zrcaljenje v '%1'\"\nUndo.Transform.VFlip=\"Navpično zrcaljenje v '%1'\"\nUndo.Transform.FitToScreen=\"Prilagodi zaslonu v '%1'\"\nUndo.Transform.StretchToScreen=\"Raztegni na velikost zaslona v '%1'\"\nUndo.Transform.Center=\"Na sredino zaslona v '%1'\"\nUndo.Transform.VCenter=\"Na navpično sredino zaslona v '%1'\"\nUndo.Transform.HCenter=\"Na vodoravno sredino zaslona v '%1'\"\nUndo.Volume.Change=\"Spremeni glasnost na '%1'\"\nUndo.Volume.Mute=\"Utišaj '%1'\"\nUndo.Volume.Unmute=\"Vklopi zvok za '%1'\"\nUndo.Balance.Change=\"Zamenjava ravnovesje zvoka za '%1'\"\nUndo.SyncOffset.Change=\"Sprememba sinhronizacijskega zamika za '%1'\"\nUndo.MonitoringType.Change=\"Sprememba nadzora zvoka za '%1'\"\nUndo.Mixers.Change=\"Sprememba mešalke zvoka za '%1'\"\nUndo.ForceMono.On=\"Vklop prisiljenega mono za '%1'\"\nUndo.ForceMono.Off=\"Izklop prisiljenega mono za '%1'\"\nUndo.Properties=\"Sprememba lastnosti za '%1'\"\nUndo.Scene.Duplicate=\"Podvoji prizor '%1'\"\nUndo.ShowTransition=\"Prikaži prehod na '%1'\"\nUndo.HideTransition=\"Skrij prehod na '%1'\"\nUndo.ShowSceneItem=\"Prikaži '%1' v '%2'\"\nUndo.HideSceneItem=\"Skrij '%1' v '%2'\"\nUndo.ReorderSources=\"Ponovno razvrsti vire v '%1'\"\nUndo.MoveUp=\"Premakni '%1' navzgor v '%2'\"\nUndo.MoveDown=\"Premakni '%1' navzdol v '%2'\"\nUndo.MoveToTop=\"Premakni '%1' na vrh v '%2'\"\nUndo.MoveToBottom=\"Premakni '%1' na dno v '%2'\"\nUndo.PasteSource=\"Prilepi vir/vire v '%1'\"\nUndo.PasteSourceRef=\"Prilepi referenco/-e v '%1'\"\nUndo.GroupItems=\"Združi predmete v '%1'\"\nTransitionNameDlg.Text=\"Vnesite ime prehoda\"\nTransitionNameDlg.Title=\"Ime prehoda\"\nTitleBar.SafeMode=\"VARNI NAČIN\"\nTitleBar.PortableMode=\"Prenosni način\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Prizori\"\nNameExists.Title=\"Ime že obstaja\"\nNameExists.Text=\"Ime je že v uporabi.\"\nNoNameEntered.Title=\"Prosimo, vnesite veljavno ime\"\nNoNameEntered.Text=\"Ne morete uporabljati praznih imen.\"\nConfirmStart.Title=\"Začnem pretakati?\"\nConfirmStart.Text=\"Ali bi res radi začeli pretakati?\"\nConfirmStop.Title=\"Zaustavim pretok?\"\nConfirmStop.Text=\"Ali bi res radi zaustavili pretok?\"\nConfirmStopRecord.Title=\"Prenehaj snemati?\"\nConfirmStopRecord.Text=\"Ali res želite prenehati snemati?\"\nConfirmBWTest.Title=\"Zaženi preizkus pasovne širine?\"\nConfirmBWTest.Text=\"OBS imate nastavljen v načinu preizkusa pasovne širine. Ta način omogoča preizkušanje omrežja brez oddajanja v živo. Ko končate s preizkusom, ga boste morali onemogočiti, da si bodo gledalci lahko ogledali vaš pretok.\\n\\nŽelite nadaljevati?\"\nConfirmRemove.Title=\"Potrdite odstranitev\"\nConfirmRemove.Text=\"Ali res želite odstraniti '%1'?\"\nConfirmRemove.TextMultiple=\"Ali res želite odstraniti %1 predmetov?\"\nConfirmReset.Title=\"Ponastavi lastnosti\"\nConfirmReset.Text=\"Ali ste prepričani, da želite ponastaviti trenutne lastnosti na njihove privzete vrednosti?\"\nOutput.StartStreamFailed=\"Neuspešen pričetek pretakanja\"\nOutput.StartRecordingFailed=\"Zagon snemanja je spodletel\"\nOutput.StartReplayFailed=\"Zagon medpomnilnika za ponovitev je spodletel\"\nOutput.StartVirtualCamFailed=\"Zagon navidezne kamere je spodletel\"\nOutput.StartFailedGeneric=\"Zagon izhoda je spodletel. Za podrobnosti preverite dnevnik.\\n\\nOpomba: če uporabljate kodirnik NVENC ali AMD, se prepričajte, da so vaši grafični gonilniki posodobljeni.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Ponovnih predvajanj ni mogoče shraniti med premorom\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Opozorilo: ponovnih predvajanj ni mogoče shraniti, ko je snemanje v premoru.\"\nOutput.ConnectFail.Title=\"Povezava ni uspela\"\nOutput.ConnectFail.BadPath=\"Neveljavna pot ali URL povezava. Prosimo, preverite vaše nastavitve in se prepričajte, da so veljavne.\"\nOutput.ConnectFail.ConnectFailed=\"Povezava s strežnikom je spodletela\"\nOutput.ConnectFail.InvalidStream=\"Dostopanje do navedenega kanala ali ključa pretoka ni bilo uspešno. Preverite ključ pretoka. Če je pravilen, je lahko težava pri povezovanju na strežnik.\"\nOutput.ConnectFail.HdrDisabled=\"Odvod HDR je trenutno onemogočen za ta izhod.\"\nOutput.ConnectFail.Error=\"Med povezovanjem s strežnikom je prišlo do nepričakovane napake. Več informacij se nahaja v datoteki dnevnika.\"\nOutput.ConnectFail.Disconnected=\"Povezava s strežnikom je prekinjena.\"\nOutput.StreamEncodeError.Title=\"Napaka pri kodiranju\"\nOutput.StreamEncodeError.Msg=\"Med pretakanjem se je pojavila napaka kodirnika.\"\nOutput.StreamEncodeError.Msg.LastError=\"Med pretakanjem se je pojavila napaka kodirnika:<br><br>%1\"\nOutput.RecordFail.Title=\"Zagon snemanja je spodletel\"\nOutput.RecordFail.Unsupported=\"Izhodni format ni podprt ali pa ne podpira več kot ene zvočne steze. Preverite nastavitve in poizkusite znova.\"\nOutput.RecordNoSpace.Title=\"Premalo prostora na disku\"\nOutput.RecordNoSpace.Msg=\"Za nadaljevanje snemanja na disku ni dovolj prostora.\"\nOutput.RecordError.Title=\"Napaka pri snemanju\"\nOutput.RecordError.Msg=\"Med snemanjem se je pojavila nedoločena napaka.\"\nOutput.RecordError.EncodeErrorMsg=\"Med snemanjem se je pojavila napaka kodirnika.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Med snemanjem se je pojavila napaka kodirnika:<br><br>%1\"\nOutput.BadPath.Title=\"Nepravilna pot do datoteke\"\nOutput.BadPath.Text=\"Nastavljene poti za snemanje ni bilo mogoče odpreti. Preverite Pot snemanja v Nastavitve → Izhod → Snemanje.\"\nOutput.NoBroadcast.Title=\"Ni nastavljenega oddajanja\"\nOutput.NoBroadcast.Text=\"Pred začtekom pretakanja morate nastaviti oddajanje.\"\nOutput.BroadcastStartFailed=\"Neuspešen zagon oddajanja\"\nOutput.BroadcastStopFailed=\"Neuspešna zaustavitev oddajanja\"\nLogUploadDialog.Title=\"Nalaganje OBS Studio dnevnika\"\nLogUploadDialog.Labels.PrivacyNotice=\"Preberite razdelek o nalaganju datotek v <a href='https://obsproject.com/privacy-policy'>Pravilniku o zasebnosti</a> še preden naložite kakšno datoteko.\"\nLogUploadDialog.Labels.Progress=\"Nalaganje dnevnika se izvaja. Počakajte...\"\nLogUploadDialog.Labels.Description.AppLog=\"Dnevnik je bil naložen. Sedaj lahko uporabite URL naslov za razhroščevanje ali podporo.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Poročilo o sesutju je bilo naloženo. URL naslov je mogoče uporabiti za razhroščevanje.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Naloži\"\nLogUploadDialog.Buttons.CopyURL=\"Kopiraj URL dnevnika\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analiziraj dnevniško datoteko\"\nLogUploadDialog.Buttons.RetryButton=\"Ponovno\"\nLogUploadDialog.Errors.Template=\"Prišlo je do napake pri nalaganju datoteke:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Datoteka za nalaganje ne obstaja ali pa je prazna.\"\nRemux.SourceFile=\"Posnetek OBS\"\nRemux.TargetFile=\"Ciljna datoteka\"\nRemux.Remux=\"Prepakiranje\"\nRemux.Stop=\"Zaustavi prepakiranje\"\nRemux.ClearFinished=\"Počisti dokončane predmete\"\nRemux.ClearAll=\"Počisti vse predmete\"\nRemux.OBSRecording=\"Posnetek OBS\"\nRemux.FinishedTitle=\"Prepakiranje je končano\"\nRemux.Finished=\"Posnetek je bil prepakiran\"\nRemux.FinishedError=\"Posnetek je bil prepakiran, a datoteka je lahko nedokončana\"\nRemux.SelectRecording=\"Izberite posnetek OBS...\"\nRemux.SelectTarget=\"Izberite ciljno datoteko...\"\nRemux.FileExistsTitle=\"Ciljne datoteke že obstajajo\"\nRemux.FileExists=\"Naslednje ciljne datoteke že obstajajo. Ali jih želite zamenjati?\"\nRemux.ExitUnfinishedTitle=\"Prepakiranje se izvaja\"\nRemux.ExitUnfinished=\"Prepakiranje ni dokončano. Če ga sedaj zaustavite, bo lahko ciljna datoteka neuporabna.\\n Res želite zaustaviti prepakiranje?\"\nRemux.HelpText=\"Za prepakiranje v to okno potegnite datoteke ali izberite prazno celico \\\"Posnetek OBS\\\", za iskanje datoteke.\"\nRemux.NoFilesAddedTitle=\"Datoteka za prepakiranje ni bila dodana\"\nRemux.NoFilesAdded=\"Dodana ni bila nobena datoteka za prepakiranje. Spustite mapo, ki vsebuje eno ali več videodatotek.\"\nMissingFiles=\"Manjkajoče datoteke\"\nMissingFiles.MissingFile=\"Manjkajoča datoteka\"\nMissingFiles.NewFile=\"Nova datoteka\"\nMissingFiles.HelpText=\"Nekatere datoteke manjkajo od zadnje uporabe OBS-a.\"\nMissingFiles.Clear=\"<očiščeno>\"\nMissingFiles.NumFound=\"Najdeno %1 od %2\"\nMissingFiles.Search=\"Prešči mapo....\"\nMissingFiles.SelectFile=\"Izberi datoteko ...\"\nMissingFiles.SelectDir=\"Izberi mapo v kateri se bo iskalo\"\nMissingFiles.State=\"Status\"\nMissingFiles.Missing=\"Manjka\"\nMissingFiles.Replaced=\"Zamenjano\"\nMissingFiles.Cleared=\"Počiščeno\"\nMissingFiles.Found=\"Najdeno\"\nMissingFiles.AutoSearch=\"Najdene so bile dodatne ustrezne datoteke\"\nMissingFiles.AutoSearchText=\"OBS je našel dodatne ustrezne datoteke za manjkajoče v tej mapi. Bi jih radi dodali?\"\nMissingFiles.NoMissing.Title=\"Preverjanje manjkajočih datotek\"\nMissingFiles.NoMissing.Text=\"Ni manjkajočih datotek\"\nMacPermissions.MenuAction=\"Preglej dovoljenja programa ...\"\nMacPermissions.Title=\"Preglej dovoljenja programa\"\nMacPermissions.Description=\"OBS Studio potrebuje vaše dovoljenje, da lahko zagotavlja nekatere funkcije. Priporočamo, da podate ta dovoljenja, niso pa potrebna za zagon programa. Vedno jih lahko omogočite tudi kasneje.\"\nMacPermissions.Description.OpenDialog=\"To pogovorno okno lahko ponovno odprete prek menija OBS Studio.\"\nMacPermissions.AccessGranted=\"Dostop odobren\"\nMacPermissions.RequestAccess=\"Zahtevaj dostop\"\nMacPermissions.OpenPreferences=\"Odpri nastavitve %1\"\nMacPermissions.Item.ScreenRecording=\"Snemanje zaslona\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS potrebuje to dovoljenje, da lahko zajema oz. snema vaš zaslon.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"To dovoljenje je potrebno za zajem vsebbine iz spletne kamere ali kartice za zajem videa.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS potrebuje to dovoljenje, če želite zajeti zvok z mikrofona ali zunanje zvokovne naprave.\"\nMacPermissions.Item.InputMonitoring=\"Nadzorovanje vhoda\"\nMacPermissions.Item.InputMonitoring.Details=\"Hitre tipke potrebujejo to dovoljenje, da bi lahko delovale z OBS v ozadju.\"\nMacPermissions.Item.Accessibility=\"Dostopnost\"\nMacPermissions.Item.Accessibility.Details=\"Na starejših različicah se lahko OBS nahaja v \\\"Dostopnost\\\" in ne \\\"Nadzorovanje vhoda\\\".\"\nMacPermissions.Continue=\"Nadaljuj\"\nSourceLeak.Title=\"Napaka pri čiščenju virov\"\nSourceLeak.Text=\"Pri spreminjanju scenskih zbirk je prišlo do težav in nekaterih virov ni bilo mogoče sprostiti. Razlog je ponavadi v vtičnikih, ki virov ne sprostijo pravilno. Poskrbite, da so vtičniki ažurirani.\\n\\nOBS Studi se bo sedaj zaprl, da prepreči morebitno okvaro podatkov.\"\nBasic.DesktopDevice1=\"Namizni zvok\"\nBasic.DesktopDevice2=\"Namizni zvok 2\"\nBasic.AuxDevice1=\"Mikrofon/Pomožni vhod\"\nBasic.AuxDevice2=\"Mikrofon/Pomožni vhod 2\"\nBasic.AuxDevice3=\"Mikrofon/Pomožni vhod 3\"\nBasic.AuxDevice4=\"Mikrofon/Pomožni vhod 4\"\nBasic.Scene=\"Prizor\"\nBasic.DisplayCapture=\"Zajemanje zaslona\"\nBasic.Main.PreviewConextMenu.Enable=\"Omogoči predogled\"\nBasic.Main.Preview.Disable=\"Onemogoči predogled\"\nScaleFiltering=\"Filtriranje sprem. velikosti\"\nScaleFiltering.Point=\"Točkovno\"\nScaleFiltering.Bilinear=\"Dvovrstno\"\nScaleFiltering.Bicubic=\"Dvoprostor.\"\nScaleFiltering.Area=\"Območje\"\nBlendingMethod=\"Metoda mešanja\"\nBlendingMethod.Default=\"Privzeto\"\nBlendingMethod.SrgbOff=\"SRGB izključen\"\nBlendingMode=\"Način mešanja\"\nBlendingMode.Normal=\"Običajno\"\nBlendingMode.Additive=\"Dodaj\"\nBlendingMode.Subtract=\"Odšteto\"\nBlendingMode.Screen=\"Zaslon\"\nBlendingMode.Multiply=\"Pomnoženo\"\nBlendingMode.Lighten=\"Posvetljeno\"\nBlendingMode.Darken=\"Potemnjeno\"\nDeinterlacing=\"Razpletanje\"\nDeinterlacing.Discard=\"Opusti\"\nDeinterlacing.TopFieldFirst=\"Najprej zgornje polje\"\nDeinterlacing.BottomFieldFirst=\"Najprej spodnje polje\"\nVolControl.SliderUnmuted=\"Drsnik glasnosti za '%1':\"\nVolControl.SliderMuted=\"Drsnik glasnosti za '%1': (trenutno je utišan)\"\nVolControl.Mute=\"Utišaj '%1'\"\nVolControl.Properties=\"Lastnosti za '%1'\"\nVolControl.UnassignedWarning.Title=\"Nedodeljen zvočni vir\"\nVolControl.UnassignedWarning.Text=\"»%1« ni dodeljen nobeni zvočni stezi in ne bo slišen v pretokih ali posnetkih.\\n\\nČe želite dodeliti zvočni vir stezi, odprite Napredne lastnosti zvoka prek kontekstnega menija ali gumba zobnika v orodni vrstici zloženega okna mešalnika.\"\nBasic.Main.AddSceneDlg.Title=\"Dodaj prizor\"\nBasic.Main.AddSceneDlg.Text=\"Vnesite ime prizora\"\nBasic.Main.DefaultSceneName.Text=\"%1. prizor\"\nBasic.Main.AddSceneCollection.Title=\"Dodaj zbirko prizorov\"\nBasic.Main.AddSceneCollection.Text=\"Vnesite ime zbirke prizorov\"\nBasic.Main.RenameSceneCollection.Title=\"Preimenuj zbirko prizorov\"\nBasic.Main.RemigrateSceneCollection.Title=\"Posodobi ločljivost zbirke scen\"\nBasic.Main.RemigrateSceneCollection.Text=\"Ali želite posodobiti ločljivost zbirke scen za \\\"%1\\\", da ustreza ločljivosti platna %2x%3 trenutnega profila?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Ko so izhodi aktivni, ni mogoče posodobiti ločljivosti zbirke scen.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Ločljivosti zbirke scen ni bilo mogoče posodobiti. Izvirna ločljivost ni poznana.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Ponastavitev ni možna. Sprememba ločljivosti OBS ni uspela.\"\nAddProfile.Title=\"Dodaj profil\"\nAddProfile.Text=\"Vnesite ime profila\"\nAddProfile.WizardCheckbox=\"Prikaži čarovnika za samodejno konfiguracijo\"\nRenameProfile.Title=\"Preimenuj profil\"\nBasic.Main.MixerRename.Title=\"Preimenuj zvočni vir\"\nBasic.Main.MixerRename.Text=\"Vnesite ime zvočnega vira\"\nBasic.Main.PreviewDisabled=\"Predogled je trenutno onemogočen\"\nBasic.SourceSelect=\"Ustvarite/izberite vir\"\nBasic.SourceSelect.CreateNew=\"Ustvari novega\"\nBasic.SourceSelect.AddExisting=\"Dodaj obstoječega\"\nBasic.SourceSelect.AddVisible=\"Prikaži vir\"\nBasic.Main.Sources.Visibility=\"Vidnost\"\nBasic.Main.Sources.VisibilityDescription=\"Nadzira vidnost '%1' v platnu\"\nBasic.Main.Sources.Lock=\"Zakleni\"\nBasic.Main.Sources.LockDescription=\"Zaklene položaj in velikost '%1' v platnu\"\nBasic.PropertiesWindow=\"Lastnosti za '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (samodejno izberi: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Izberi barvo\"\nBasic.PropertiesWindow.SelectFont=\"Izberi pisavo\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Izberite pisavo\"\nBasic.PropertiesWindow.ConfirmTitle=\"Nastavitve so se spremenile\"\nBasic.PropertiesWindow.Confirm=\"Obstajajo neshranjene spremembe. Ali jih želite obdržati?\"\nBasic.PropertiesWindow.NoProperties=\"Lastnosti niso na voljo\"\nBasic.PropertiesWindow.AddFiles=\"Dodaj datoteke\"\nBasic.PropertiesWindow.AddDir=\"Dodaj mapo\"\nBasic.PropertiesWindow.AddURL=\"Dodaj pot/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Dodaj mapo na '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Dodaj datoteke na '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Dodaj vnos na '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Uredi vnos s/z '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Preproste vrednosti sl./s\"\nBasic.PropertiesView.FPS.Rational=\"Racionalne vrednosti sl./s\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Veljavni razpon sl./s:\"\nBasic.PropertiesView.UrlButton.Text=\"Odprem to povezavo v privzetem brskalniku?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Odpri URL\"\nBasic.InteractionWindow=\"V stiku z '%1'\"\nBasic.StatusBar.Reconnecting=\"Povezava je bila prekinjena. Ponovno povezava čez %2 sekund (%1. poizkus)\"\nBasic.StatusBar.AttemptingReconnect=\"Poizkušanje ponovne povezave … (%1. poizkus)\"\nBasic.StatusBar.ReconnectSuccessful=\"Ponovna povezava uspešna\"\nBasic.StatusBar.Delay=\"Zakasnitev (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Zakasnitev (začetek čez %1 s)\"\nBasic.StatusBar.DelayStoppingIn=\"Zakasnitev (konec čez %1 s)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Zakasnitev (začetek čez %1 s, konec čez %2 s)\"\nBasic.StatusBar.RecordingSavedTo=\"Posnetek je shranjen v '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Medpomnilnik za ponovitev shranjen v '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Posnetek zaslona je shranjen v '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Posnetek je bil samodejno prepakiran v »%1«\"\nBasic.Filters=\"Filtri\"\nBasic.Filters.AsyncFilters=\"Zvočni/slikovni filtri\"\nBasic.Filters.AudioFilters=\"Zvočni filtri\"\nBasic.Filters.EffectFilters=\"Filtri učinkov\"\nBasic.Filters.Title=\"Filtri za '%1'\"\nBasic.Filters.AddFilter.Title=\"Ime filtra\"\nBasic.Filters.AddFilter.Text=\"Navedite ime filtra\"\nBasic.TransformWindow=\"Preoblikovanje predmeta v prizoru\"\nBasic.TransformWindow.Position=\"Položaj\"\nBasic.TransformWindow.PositionX=\"Položaj X\"\nBasic.TransformWindow.PositionY=\"Položaj Y\"\nBasic.TransformWindow.Rotation=\"Zasuk\"\nBasic.TransformWindow.Size=\"Velikost\"\nBasic.TransformWindow.Width=\"Širina\"\nBasic.TransformWindow.Height=\"Višina\"\nBasic.TransformWindow.Alignment=\"Poravnava položaja\"\nBasic.TransformWindow.BoundsType=\"Vrsta okvirja\"\nBasic.TransformWindow.BoundsAlignment=\"Poravnava v okvirju\"\nBasic.TransformWindow.Bounds=\"Velikost okvirja\"\nBasic.TransformWindow.BoundsWidth=\"Širina okvirja\"\nBasic.TransformWindow.BoundsHeight=\"Višina okvirja\"\nBasic.TransformWindow.CropToBounds=\"Obreži na okvir\"\nBasic.TransformWindow.Crop=\"Obreži\"\nBasic.TransformWindow.CropLeft=\"Obreži levo\"\nBasic.TransformWindow.CropRight=\"Obreži desno\"\nBasic.TransformWindow.CropTop=\"Obreži zgoraj\"\nBasic.TransformWindow.CropBottom=\"Obreži spodaj\"\nBasic.TransformWindow.Alignment.TopLeft=\"Zgoraj levo\"\nBasic.TransformWindow.Alignment.TopCenter=\"Sredina zgoraj\"\nBasic.TransformWindow.Alignment.TopRight=\"Zgoraj desno\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Na sredini levo\"\nBasic.TransformWindow.Alignment.Center=\"Sredina\"\nBasic.TransformWindow.Alignment.CenterRight=\"Na sredini desno\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Spodaj levo\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Sredina spodaj\"\nBasic.TransformWindow.Alignment.BottomRight=\"Spodaj desno\"\nBasic.TransformWindow.BoundsType.None=\"Neomejeno\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Samo največja velikost\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Prilagodi notranji meji\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Prilagodi zunanji meji\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Prilagodi širini meje\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Prilagodi višini meje\"\nBasic.TransformWindow.BoundsType.Stretch=\"Raztegni do meje\"\nBasic.TransformWindow.Title=\"Uredi transformacijo za '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Noben vir ni izbran\"\nBasic.Main.AddSourceHelp.Title=\"Ni mogoče dodati vira\"\nBasic.Main.AddSourceHelp.Text=\"Potrebujete vsaj 1 prizor, da lahko dodate vir.\"\nBasic.Main.Scenes=\"Prizori\"\nBasic.Main.Sources=\"Viri\"\nBasic.Main.Source=\"Vir\"\nBasic.Main.Controls=\"Nadzorne tipke\"\nBasic.Main.PreparingStream=\"V pripravi ...\"\nBasic.Main.Connecting=\"Povezovanje …\"\nBasic.Main.StartRecording=\"Začni snemati\"\nBasic.Main.StartReplayBuffer=\"Zaženi medpomnilnik za ponovitev\"\nBasic.Main.SaveReplay=\"Shrani ponovitev\"\nBasic.Main.StartStreaming=\"Začni pretakati\"\nBasic.Main.StartBroadcast=\"Pojdi v živo\"\nBasic.Main.StartVirtualCam=\"Zagon virtualne kamere\"\nBasic.Main.StopRecording=\"Zaustavi snemanje\"\nBasic.Main.PauseRecording=\"Premor snemanja\"\nBasic.Main.UnpauseRecording=\"Nadaljuj snemanje\"\nBasic.Main.SplitFile=\"Razdeli datoteko posnetka\"\nBasic.Main.StoppingRecording=\"Zaustavljanje snemanja …\"\nBasic.Main.StopReplayBuffer=\"Zaustavi medpomnilnik za ponovitve\"\nBasic.Main.StoppingReplayBuffer=\"Zaustavljanje medp. ponovitve...\"\nBasic.Main.SetupBroadcast=\"Upravljaj oddajanje\"\nBasic.Main.StopStreaming=\"Prenehaj pretakati\"\nBasic.Main.StopBroadcast=\"Zaključi oddajanje\"\nBasic.Main.AutoStopEnabled=\"(samodejna zaustavitev)\"\nBasic.Main.StoppingStreaming=\"Ustavljanje pretoka …\"\nBasic.Main.ForceStopStreaming=\"Prenehaj pretakati (zavrzi zakasnitev)\"\nBasic.Main.ShowContextBar=\"Pokaži orodno vrstico vira\"\nBasic.Main.HideContextBar=\"Skrij orodno vrstico vira\"\nBasic.Main.StopVirtualCam=\"Zaustavi virtualne kamere\"\nBasic.Main.Group=\"%1. skupina\"\nBasic.Main.GroupItems=\"Združi izbrane predmete\"\nBasic.Main.Ungroup=\"Razdruži\"\nBasic.Main.GridMode=\"Mrežni način\"\nBasic.Main.ListMode=\"Način s seznamom\"\nBasic.Main.VirtualCamConfig=\"Prilagodi virtualno kamero\"\nBasic.VCam.VirtualCamera=\"Virtualna kamera\"\nBasic.VCam.OutputType=\"Vrsta izhoda\"\nBasic.VCam.OutputSelection=\"Izbor izhoda\"\nBasic.VCam.OutputType.Program=\"Program (privzeto)\"\nBasic.VCam.OutputSelection.NoSelection=\"Ni izbora za to vrsto izhoda\"\nBasic.VCam.RestartWarning=\"Virtualna kamera bo ponovno zagnana, da se ta sprememba uveljavi\"\nBasic.MainMenu.File=\"Datoteka (&F)\"\nBasic.MainMenu.File.Export=\"Izvozi (&E)\"\nBasic.MainMenu.File.Import=\"Uvoz&i\"\nBasic.MainMenu.File.ShowRecordings=\"P&rikaži posnetke\"\nBasic.MainMenu.File.Remux=\"Prepakiraj posnetke (&M)\"\nBasic.MainMenu.File.Settings=\"Na&stavitve\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Prikaži mapo z nastavitvami\"\nBasic.MainMenu.File.ShowProfileFolder=\"Prikaži mapo s profili\"\nBasic.MainMenu.File.ShowMissingFiles=\"Preveri manjkajoče datoteke\"\nBasic.MainMenu.File.Exit=\"Izhod (&X)\"\nBasic.MainMenu.Edit=\"Ur&edi\"\nBasic.MainMenu.Edit.Undo=\"Razveljavi (&U)\"\nBasic.MainMenu.Edit.Redo=\"Uveljavi (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Zak&leni predogled\"\nBasic.MainMenu.Edit.Scale=\"&Spremeni velikost predogleda\"\nBasic.MainMenu.Edit.Scale.Window=\"Prilagodi oknu\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Platno (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Izhod (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"Preoblikuj (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Ur&edi preobliko …\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopiraj preobliko\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Prilepi preobliko\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Ponastavi p&reobliko\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Zasukaj za 90 stopinj v desno\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Zasukaj za 90 stopinj v levo\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Zasukaj za 180 stopinj\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Zrcali vodoravno (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Zrcali na&vpično\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Prilagodi zaslonu (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Raztegni na veliko&st zaslona\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Na %sredino zaslona\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Usredini navpično\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Usredini vodoravno\"\nBasic.MainMenu.Edit.Order=\"Zap&oredje\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Premakni gor (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Premakni &dol\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Premakni na vrh (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Premakni na dno (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"N&apredne lastnosti zvoka\"\nBasic.MainMenu.View=\"Pogled (&V)\"\nBasic.MainMenu.View.Toolbars=\"Orodne vrs&tice\"\nBasic.MainMenu.View.ListboxToolbars=\"Zasidraj orodne vrstice\"\nBasic.MainMenu.View.ContextBar=\"Orodna vrstica vira\"\nBasic.MainMenu.View.SourceIcons=\"&Ikone virov\"\nBasic.MainMenu.View.StatusBar=\"Vr&stica stanja\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Celozaslonski vmesnik\"\nBasic.MainMenu.View.ResetUI=\"&Ponastavi uporabniški vmesnik\"\nBasic.MainMenu.View.AlwaysOnTop=\"Vedno n&a vrhu\"\nBasic.MainMenu.View.SceneListMode=\"Način seznama prizorov\"\nBasic.MainMenu.Docks=\"&Sidrišča\"\nBasic.MainMenu.Docks.ResetDocks=\"&Ponastavi sidrišča\"\nBasic.MainMenu.Docks.LockDocks=\"&Zakleni sidrišča\"\nBasic.MainMenu.Docks.SideDocks=\"&Sidrišča polne višine\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Sidrišča &brskalnika po meri ...\"\nBasic.MainMenu.SceneCollection=\"Zbirka prizorov (&S)\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Uvozi profil\"\nBasic.MainMenu.Profile.Export=\"Izvozi profil\"\nBasic.MainMenu.SceneCollection.Import=\"Uvozi zbirko prizorov\"\nBasic.MainMenu.SceneCollection.Export=\"Izvozi zbirko prizorov\"\nBasic.MainMenu.Profile.Exists=\"Profil že obstaja\"\nBasic.MainMenu.Tools=\"Orodja (&T)\"\nBasic.MainMenu.Help=\"Pomoč (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal za pomoč\"\nBasic.MainMenu.Help.Website=\"Obišči spletno mesto (&W)\"\nBasic.MainMenu.Help.Discord=\"Pridruži se strežniku &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Kaj je novega\"\nBasic.MainMenu.Help.Logs=\"Dnevniške datoteke (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Prikaži dnevniške datoteke (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Naloži trenutno dnevniško datoteko (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Naloži &prejšnjo datoteko dnevnika\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Pokaži trenutni dne&vnik\"\nBasic.MainMenu.Help.ReleaseNotes=\"Opombe ob izdaji\"\nBasic.MainMenu.Help.CheckForUpdates=\"Preveri za posodobitve\"\nBasic.MainMenu.Help.Repair=\"Preveri celovitost datoteke\"\nBasic.MainMenu.Help.RestartSafeMode=\"Ponovno zaženi v varnem načinu\"\nBasic.MainMenu.Help.RestartNormal=\"Ponovno zaženi v navadnem načinu\"\nBasic.MainMenu.Help.CrashLogs=\"Po&ročila o zrušitvi\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Prikaži poročila o zrušitvi (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Naloži &prejšnje poročilo o sesutju\"\nBasic.MainMenu.Help.About=\"O progr&amu\"\nBasic.Settings.ProgramRestart=\"Program je treba ponovno zagnati, da se te nastavitve uveljavijo.\"\nBasic.Settings.ConfirmTitle=\"Potrdi spremembe\"\nBasic.Settings.Confirm=\"Imate neshranjene spremembe. Shrani spremembe?\"\nBasic.Settings.General=\"Splošno\"\nBasic.Settings.General.Language=\"Jezik\"\nBasic.Settings.General.Updater=\"Posodobitve\"\nBasic.Settings.General.UpdateChannel=\"Kanal za posodobitev\"\nBasic.Settings.General.UpdateChannelDisabled=\"(onemogočeno)\"\nBasic.Settings.General.UpdateChannelDefault=\"(privzeto)\"\nBasic.Settings.General.EnableAutoUpdates=\"Ob zagonu samodejno preveri za posodobitve\"\nBasic.Settings.General.OpenStatsOnStartup=\"Ob zagonu odpri pogovorno okno s statistiko\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Skrij okna OBS pred zajemom zaslona\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Ta možnost skrije vsa okna OBS Studio, ki niso projekcijska, da jih OBS ne zajame, in s tem ne vpliva na druge \\nprograme, kot so programi za skupno raba zaslona, ​​podporo na daljavo, snemanje zaslona, konference in drugi programi za zajemanje.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Če omogočite to možnost, skrijete vsa okna OBS Studia, ki niso projekcijska, da jih OBS ne zajame, in s tem ne vpliva na druge \\nprograme, kot so programi za skupno raba zaslona, ​​podporo na daljavo, snemanje zaslona, konference in drugi programi za zajemanje.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Prikaži potrditveno okno pred začetkom pretakanja\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Prikaži potrditveno okno pred zaustavitvijo pretoka\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Prikaži potrditveno okno ob zaustavljanju snemanja\"\nBasic.Settings.General.Projectors=\"Projektorji\"\nBasic.Settings.General.HideProjectorCursor=\"Skrij kazalec nad projektorji\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Projektorje prikaži vedno na vrhu\"\nBasic.Settings.General.Snapping=\"Pripenjanje virov ob poravnavi\"\nBasic.Settings.General.ScreenSnapping=\"Pripni vire na rob zaslona\"\nBasic.Settings.General.CenterSnapping=\"Pripni vire na vodoravno in navpično sredino\"\nBasic.Settings.General.SourceSnapping=\"Pripni vire na druge vire\"\nBasic.Settings.General.SnapDistance=\"Občutljivost pripenjanja\"\nBasic.Settings.General.SpacingHelpers=\"Pokaži vodila poravnave slikovnih točk\"\nBasic.Settings.General.RecordWhenStreaming=\"Samodejno snemaj med pretakanjem\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Obdrži posnetek, ko se pretok zaustavi\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Med pretakanjem samodejno zaženi medpomnilnik za ponovno predvajanje\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Medpomnilnik za ponovitve naj bo po zaustavitvi pretakanja še naprej dejaven\"\nBasic.Settings.General.SysTray=\"Sistemska vrstica\"\nBasic.Settings.General.SysTrayWhenStarted=\"Skrči v sistemsko vrstico ob zagonu\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Vedno skrči v sistemsko vrstico namesto v opravilno\"\nBasic.Settings.General.SaveProjectors=\"Shrani projektorje on izhodu\"\nBasic.Settings.General.CloseExistingProjectors=\"Omejitev na en celozaslonski projektor po zaslonu\"\nBasic.Settings.General.Preview=\"Predogled\"\nBasic.Settings.General.OverflowHidden=\"Skrij prekoračitev\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Prekoračitev je vedno vidna\"\nBasic.Settings.General.OverflowSelectionHidden=\"Prikaži prekoračitev, tudi ko je vir neviden\"\nBasic.Settings.General.Importers=\"Uvozniki\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Med uvažanjem išči zbirke scen v znanih lokacijah\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Ob dvokliku izvedi prehod na prizor\"\nBasic.Settings.General.StudioPortraitLayout=\"Omogoči pokončno/navpično razporeditev\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Prikaži oznake za predogled/program\"\nBasic.Settings.General.Multiview=\"Več pogledov\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Klikni za preklop med prizori\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Prikaži imena prizorov\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Nariši varna območja (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Razporeditev več pogledov\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Vodoravno zgoraj (8 prizorov)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Vodoravno spodaj (8 prizorov)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Navpično levo (8 prizorov)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Navpično desno (8 prizorov)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Vodoravno, zgoraj (18 prizorov)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Vodoravno zgoraj (24 prizorov)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Samo prizori (4 prizori)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Samo prizori (9 prizorov)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Samo prizori (16 prizorov)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Samo prizori (25 prizorov)\"\nBasic.Settings.General.ChannelName.stable=\"Stabilno\"\nBasic.Settings.General.ChannelDescription.stable=\"Zadnja stabilna izdaja\"\nBasic.Settings.General.ChannelName.beta=\"Beta / kandidat za izdajo\"\nBasic.Settings.General.ChannelDescription.beta=\"Morebiti nestabilna različica pred izdajo\"\nBasic.Settings.Appearance=\"Videz\"\nBasic.Settings.Appearance.General=\"Splošno\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Slog\"\nBasic.Settings.Appearance.General.NoVariant=\"Slogi niso na voljo\"\nBasic.Settings.Stream=\"Pretok\"\nBasic.Settings.Stream.Destination=\"Cilj\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Uporabi overitev\"\nBasic.Settings.Stream.Custom.Username=\"Uporabniško ime\"\nBasic.Settings.Stream.Custom.Password=\"Geslo\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: vnesite uporabniško ime srp_username.\\nRTMP: vnesite uporabniško ime.\\nSRT: se ne uporablja.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: vnesite geslo srp_password.\\nRTMP: vnesite geslo.\\nSRT: vnesite šifrirno geslo.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Omogoči način preverjanja pasovne širine\"\nBasic.Settings.Stream.TTVAddon=\"Dodatek za Twitch-Chat\"\nBasic.Settings.Stream.TTVAddon.None=\"Brez\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV in FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Pomanjkljiva nastavitev pretoka\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Odpri nastavitve\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Manjkata URL in ključ pretoka.\\n\\nOdprite nastavitve za vnos URLja in ključa pretoka v zavihek 'Pretok'.\"\nBasic.Settings.Stream.MissingUrl=\"Manjka URL pretoka.\\n\\nOdprite nastavitve za vnos URLja v zavihek 'Pretok'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Manjka ključ pretoka.\\n\\nOdprite nastavitve za vnos ključa pretoka v zavihek 'Pretok'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Prezri priporočila za nastavitev storitve pretakanja\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Preglasite priporočene nastavitve\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Opozorilo: neupoštevanje omejitev storitve lahko povzroči poslabšanje kakovosti pretočnega predvajanja ali prepreči pretakanje.\\n\\nNadaljujem?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Največja bitna hitrost videa: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Največja bitna hitrost zvoka: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Največja ločljivost: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Največje število sl./s: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Določi strežnik po meri ...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Strežnik po meri\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Omogoči %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Največja pasovna širina pretakanja ...\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Samodejno\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Največje število stez videa\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Samodejno\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Večstezni video\"\nBasic.Settings.Stream.AdvancedOptions=\"Napredne možnosti\"\nBasic.Settings.Output=\"Izhod\"\nBasic.Settings.Output.Format=\"Format posnetkov\"\nBasic.Settings.Output.Format.MKV=\"Video Matroska (.mkv)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmentirani MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmentirani MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmentiran MOV zapisuje posnetek v gručah, zato ne potrebuje enake finalizacije, kot tradicionalna MOV datoteka.\\nTo omogoča, da je datoteko mogoče predvajati tudi kadar je zapisovanje na disk prekinjeno ob npr. sesutju računalnika ali izpadu elektrike.\\n\\nTo ni vedno združljivo z vsemi predvajalniki in urejevalniki. Če obstaja potreba po bolj združljivem zapisu, uporabite Datoteka → Prepakiraj posnetke.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmentiran MP4 zapisuje posnetek v gručah, zato ne potrebuje enake finalizacije, kot tradicionalna MP4 datoteka.\\nTo omogoča, da je datoteko mogoče predvajati tudi kadar je zapisovanje na disk prekinjeno ob npr. sesutju računalnika ali izpadu elektrike.\\n\\nTo ni vedno združljivo z vsemi predvajalniki in urejevalniki. Če obstaja potreba po bolj združljivem zapisu, uporabite Datoteka → Prepakiraj posnetke.\"\nBasic.Settings.Output.Encoder.Video=\"Slikovni kodirnik\"\nBasic.Settings.Output.Encoder.Audio=\"Zvočni kodirnik\"\nBasic.Settings.Output.SelectDirectory=\"Izberite mapo za posnetke\"\nBasic.Settings.Output.DynamicBitrate=\"Dinamično spreminjaj bitno hitrost za odpravo zastojev\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dinamično spreminjaj bitno hitrost za odpravo zastojev (beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Namesto izpuščanja sličic za odpravo zastojev sproti dinamično spreminja bitno hitrost.\\n\\nUpoštevajte, da to lahko poveča zakasnitve pri gledalcih, če pride do nenadnega znatnega zastoja.\\nKo se bitna hitrost zniža, lahko obnovitev traja nekaj minut.\\n\\nTrenutno podprto samo za RMTP.\"\nBasic.Settings.Output.Mode=\"Način izhoda\"\nBasic.Settings.Output.Mode.Simple=\"Preprost\"\nBasic.Settings.Output.Mode.Adv=\"Napreden\"\nBasic.Settings.Output.Mode.FFmpeg=\"Izhod FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Omogoči medpomnilnik za ponovitev\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Najdaljši čas ponovitve\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Največ pomnilnika\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Ocenjena poraba pomnilnika: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Opozorilo: ocenjena raba pomnilnika %1 MiB je večja od največje priporočene %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Porabe pomnilnika ni mogoče oceniti. Nastavite zgornjo omejitev porabe.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Predpona datoteke medp. ponovitve\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Pripona\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Medpomnilnika za ponovno predvajanje ni mogoče uporabiti, ko je vrsta snemanja nastavljena na Izhod po meri (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Pot za snemanje\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Kakovost posnetkov\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Enaka kot za pretok\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Visoka kakovost, srednja velikost datoteke\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Nerazločljiva kakovost, zelo velika velikost datoteke\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Brezizgubna kakovost, izjemno velika velikost datoteke\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Opozorilo: bitna hitrost pretočnega videa bo nastavljena na %1, kar je zgornja meja za trenutno storitev pretakanja.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Opozorilo: bitna hitrost pretočnega zvoka bo nastavljena na %1, kar je zgornja meja za trenutno storitev pretakanja.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Opozorilo: posnetkov ni mogoče začasno ustaviti, če je kakovost posnetka nastavljena na \\\"Enaka kot pri pretakanju\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Opozorilo: trenutno izbran zapis posnetkov ni združljiv z izbranimi kodirniki pretoka.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Opozorilo: snemanje s programskim kodirnikom z drugačno kakovostjo, kot jo ima pretok zahteva dodatno obremenitev procesorja, kadar hkrati pretakate in snemate.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Opozorilo: Kakovost brez izgub ustvari izjemno velike datoteke! Kakovost brez izgub lahko zasede več kot 7 GB prostora na disku na minuto pri visokih ločljivostih in hitrostih. Kakovost brez izgub ni priporočljiva za dolge posnetke, razen če imate na voljo veliko prostora na disku. Medpomnilnik za ponovno predvajanje ni na voljo, če uporabljate kakovost brez izgub.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Ali res želite uporabiti brezizgubno kakovost?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Opozorilo o brezizgubni kakovosti!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Programski (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Strojna oprema (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Strojna oprema (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Strojna oprema (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Strojna oprema (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Strojna oprema (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Strojna oprema (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Strojna oprema (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Strojna oprema (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Strojna oprema (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Strojna oprema (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Programski (prednastavitev x264 s nizko porabo CPE-ja, poveča velikost datoteke)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (privzeto)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD Track (Uporablja stezo 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Zvočna steza\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Ločljivost ali št. sl./s nista podprta\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Ta pretočna storitev ne podpira trenutne izhodne ločljivosti in/ali števila slik na sekundo. Spremenjena bosta na najbližjo podprto vrednost:\\n\\n%1\\n\\nAli želite nadaljevati?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Ločljivost: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"sl./s: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Nezdružljivi kodirnik\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Pretočna storitev %1 ne podpira kodirnika %2. Kodirnik bo spremenjen v %3.\\n\\nAli želite nadaljevati?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Pretočna storitev %1 ne podpira kodirnikov %2 in %3. Ta kodirnika bosta spremenjena v %4 in %5.\\n\\nAli želite nadaljevati?\"\nBasic.Settings.Output.VideoBitrate=\"Bitna hitrost slike\"\nBasic.Settings.Output.AudioBitrate=\"Bitna hitrost zvoka\"\nBasic.Settings.Output.Reconnect=\"Samodejno ponovno poveži\"\nBasic.Settings.Output.RetryDelay=\"Zakasn. pon. poizkusa\"\nBasic.Settings.Output.MaxRetries=\"Število ponovnih poizkusov\"\nBasic.Settings.Output.Advanced=\"Omogoči nastavitve kodirnika po meri (napredno)\"\nBasic.Settings.Output.EncoderPreset=\"Prednastavitev kodirnika\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (majhna obremenitev procesorja, nizka kakovost)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (privzeto; srednja obremenitev procesorja, standardna kakovost)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (visoka obremenitev procesorja, visoka kakovost)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Nastavitve po meri za kodirnik\"\nBasic.Settings.Output.CustomMuxerSettings=\"Prilagojene nastavitve za prepakiranje\"\nBasic.Settings.Output.NoSpaceFileName=\"Ustvari ime datoteke brez presledkov\"\nBasic.Settings.Output.Adv.Rescale=\"Spremeni velikost izhoda\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Onemogočeno\"\nBasic.Settings.Output.Adv.AudioTrack=\"Zvočna steza\"\nBasic.Settings.Output.Adv.Streaming=\"Pretakanje\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Nastavitve pretakanja\"\nBasic.Settings.Output.Adv.Audio.Track1=\"1. steza\"\nBasic.Settings.Output.Adv.Audio.Track2=\"2. steza\"\nBasic.Settings.Output.Adv.Audio.Track3=\"3. steza\"\nBasic.Settings.Output.Adv.Audio.Track4=\"4. steza\"\nBasic.Settings.Output.Adv.Audio.Track5=\"5. steza\"\nBasic.Settings.Output.Adv.Audio.Track6=\"6. steza\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD posnetek\"\nBasic.Settings.Output.Adv.Encoder=\"Nastavitve kodirnika\"\nBasic.Settings.Output.Adv.Recording=\"Snemanje\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Nastavitve snemanja\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Vrsta snemanja\"\nBasic.Settings.Output.Adv.Recording.Type=\"Vrsta\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Običajno\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Izhod po meri (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(uporabi pretočni kodirnik)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Oblika zapisa imena datoteke\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Prepiši, če datoteka že obstaja\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Vrsta izhoda FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Odvajaj v URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Odvajaj v datoteko\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Pogoste oblike posnetkov\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Vse datoteke\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Pot do datoteke ali URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format zabojnika\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Zvok\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Slika\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Privzeti format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Opis formata zabojnika\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Zvočni/slikovni kodek se skuša določiti iz poti datoteke ali URL naslova\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Privzeti kodirnik\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Onemogoči kodirnik\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Slikovni kodirnik\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Nastavitve slikovnega kodirnika (če obstaja)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Zvočni kodirnik\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Nastavitve zvočnega kodirnika (če obstaja)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Nastavitve za prepakiranje (če so)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Razmik med ključnimi sličicami (sličic)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Prikaže vse kodeke (tudi če so mogoče nezdružljivi)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Nastavitve FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Samodejna delitev datotek\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Deli po času\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Deli po velikosti\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Razdeli samo ročno\"\nBasic.Settings.Output.SplitFile.Time=\"Čas delitve\"\nBasic.Settings.Output.SplitFile.Size=\"Velikost delitve\"\nScreenshot=\"Izhod posnetka zaslona\"\nScreenshot.SourceHotkey=\"Izbrani vir posnetka zaslona\"\nScreenshot.StudioProgram=\"Posnetek zaslona (Program)\"\nScreenshot.Preview=\"Posnetek zaslona (Predogled)\"\nScreenshot.Scene=\"Posnetek zaslona (Scena)\"\nScreenshot.Source=\"Posnetek zaslona (Vir)\"\nFilenameFormatting.TT.CCYY=\"Letnica, štiri števke\"\nFilenameFormatting.TT.YY=\"Letnica, le zadnji števki (00-99)\"\nFilenameFormatting.TT.MM=\"Mesec kot desetiško število (01-12)\"\nFilenameFormatting.TT.DD=\"Dan v mesecu, s predpono 0 (01-31)\"\nFilenameFormatting.TT.hh=\"24-urni zapis ur (00-23)\"\nFilenameFormatting.TT.ss=\"Sekunde (00-59)\"\nFilenameFormatting.TT.Percent=\"Znak %\"\nFilenameFormatting.TT.a=\"Okrajšano ime dneva v tednu\"\nFilenameFormatting.TT.A=\"Polno ime dneva v tednu\"\nFilenameFormatting.TT.b=\"Okrajšano ime meseca\"\nFilenameFormatting.TT.B=\"Polno ime meseca\"\nFilenameFormatting.TT.d=\"Dan v mesecu, s predpono 0 (01-31)\"\nFilenameFormatting.TT.H=\"24-urni zapis ur (00-23)\"\nFilenameFormatting.TT.I=\"12-urni zapis ur (01-12)\"\nFilenameFormatting.TT.m=\"Mesec kot desetiško število (01-12)\"\nFilenameFormatting.TT.p=\"Navedba AM/PM\"\nFilenameFormatting.TT.s=\"Čas v sekundah POSIX (čas UNIX)\"\nFilenameFormatting.TT.S=\"Sekunde (00-59)\"\nFilenameFormatting.TT.y=\"Letnica, le zadnji števki (00-99)\"\nFilenameFormatting.TT.Y=\"Leto\"\nFilenameFormatting.TT.z=\"Zamik ISO 8601 od UTC v časovnem pasu\"\nFilenameFormatting.TT.Z=\"Ime ali okrajšava časovnega pasu\"\nFilenameFormatting.TT.FPS=\"Sličic na sekundo\"\nFilenameFormatting.TT.CRES=\"Osnovna ločljivost (platno)\"\nFilenameFormatting.TT.ORES=\"Izhodna ločljivost (raztegnjena)\"\nFilenameFormatting.TT.VF=\"Video zapis\"\nBasic.Settings.Video=\"Slika\"\nBasic.Settings.Video.Adapter=\"Grafična kartica\"\nBasic.Settings.Video.BaseResolution=\"Osnovna ločljivost\"\nBasic.Settings.Video.ScaledResolution=\"Izhodna ločljivost\"\nBasic.Settings.Video.DownscaleFilter=\"Pomanjševalni filter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Ločljivosti se ujemata, pomanjšanje ni potrebno]\"\nBasic.Settings.Video.FPS=\"sl./s\"\nBasic.Settings.Video.FPSCommon=\"Pogoste vrednosti sl./s\"\nBasic.Settings.Video.FPSInteger=\"Cela števila sl./s\"\nBasic.Settings.Video.FPSFraction=\"Decimalne vrednosti sl./s\"\nBasic.Settings.Video.Numerator=\"Števec\"\nBasic.Settings.Video.Denominator=\"Imenovalec\"\nBasic.Settings.Video.Renderer=\"Izrisovalnik\"\nBasic.Settings.Video.InvalidResolution=\"Neveljavna vrednost ločljivosti. Biti mora [širina] x [višina] (npr. 1920 x 1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Video izhod je trenutno aktiven. Prosimo, izklopite vse izhode za spreminjanje video nastavitev.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Dvovrsten (najhitrejši, vendar ob znižanju ločljivosti povzroči megleno sliko)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Dvoprostorninski (izostreno spreminjanje ločljivosti, 16 vzorcev)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (izostreno spreminjanje ločljivosti, 36 vzorcev)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Območje (utežena vsota, 4/6/9 vzorcev)\"\nBasic.Settings.Audio=\"Zvok\"\nBasic.Settings.Audio.SampleRate=\"Hitrost vzorčenja\"\nBasic.Settings.Audio.Channels=\"Kanali\"\nBasic.Settings.Audio.Meters=\"Merilci\"\nBasic.Settings.Audio.MeterDecayRate=\"Hitrost razkroja\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Hitra\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Srednja (vrsta I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Počasna (vrsta II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Vrsta merilca najvišjih točk\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Primer najvišje točke\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Resnična najvišja točka (večja poraba CPE-ja)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"OPOZORILO: prostorski zvok je omogočen.\"\nBasic.Settings.Audio.MultichannelWarning=\"Med pretakanjem preveri, ali pretočna storitev podpira tako snemanje kot tudi predvajanje prostorskega zvoka. Facebook 360 Live npr. v celoti podpira prostorski zvok; You Tube Live podpira vnos zvoka 5.1 (in predvajanje na TV zaslonih).\\n\\nZvočni filtri OBS so združljivi s prostorskim zvokom, čeprav podpora vtičnikom VST ni zajamčena.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Omogoči prostorski zvok?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Ali res želite omogočiti prostorski zvok?\"\nBasic.Settings.Audio.Devices=\"Globalne avdio naprave\"\nBasic.Settings.Audio.DesktopDevice=\"Namizni zvok\"\nBasic.Settings.Audio.DesktopDevice2=\"Namizni zvok 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/Dodatni zvok\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/Dodatni zvok 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/Dodatni zvok 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/Dodatni zvok 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Omogoči 'Pritisni in utišaj'\"\nBasic.Settings.Audio.PushToMuteDelay=\"Zakasn. funk. 'Pritisni in utišaj'\"\nBasic.Settings.Audio.EnablePushToTalk=\"Omogoči 'Pritisni in govori'\"\nBasic.Settings.Audio.PushToTalkDelay=\"Zakasn. funk. 'Pritisni in govori'\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[naprava ni povezana ali ni na voljo]\"\nBasic.Settings.Audio.Disabled=\"Onemogočeno\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Način medpomnjenja zvoka z nizko zakasnitvijo (za izhode Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"OPOZORILO: Omogočeno je medpomnjenje zvoka z nizko zakasnitvijo.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Način medpomnjenja zvoka z nizko zakasnitvijo lahko povzroči napake v zvoku ali zaustavi predvajanje iz nekaterih virov.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Ali želite omogočiti način medpomnjenja zvoka z nizko zakasnitvijo?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Ali ste prepričani, da želite omogočiti način medpomnjenja zvoka z nizko zakasnitvijo?\"\nBasic.Settings.Accessibility=\"Dostopnost\"\nBasic.Settings.Accessibility.ColorOverrides=\"Uporabi različne barve\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Obroba vira (izbor)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Obroba vira (obrezano)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Obroba vira (nad predmetom)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Pas glasnosti mešalnika (-60 do -20 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Pas glasnosti mešalnika (-20 do -9 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Pas glasnosti mešalnika (-9 do 0 dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Pas glasnosti mešalnika (-60 do -20 dB; aktivno)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Pas glasnosti mešalnika (-20 do -9 dB, aktivno)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Pas glasnosti mešalnika (-9 do 0 dB; aktivno)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Barvna prednastavitev\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Privzeto\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Po meri\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternativa za barvno slepe\"\nBasic.Settings.Advanced=\"Napredno\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Ob zapiranju prikaži opozorila o aktivnih izhodih\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prednost opravila\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Visoka\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Nad običajno\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Običajna\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Pod običajno\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Nedejavno\"\nBasic.Settings.Advanced.FormatWarning=\"Opozorilo: barvni formati, ki niso NV12/P010, so v glavnem namenjeni snemanju in niso priporočljivi za pretakanje. Pretakanje lahko poveča obremenitev procesorja zaradi pretvarjanja barvne oblike.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Opozorilo: visoko natančni formati so običajno uporabljeni v barvnih prostorih HDR\"\nBasic.Settings.Advanced.FormatWarning2100=\"Opozorilo: Rec. 2100 naj uporablja format z višjo natančnostjo.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Barvni format\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bitno, 4:2:0, 2 ravnini)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bitno, 4:2:0, 3 ravnine)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bitno, 4:4:4, 3 ravnine)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bitno, 4:2:0, 2 ravnini)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bitno, 4:2:0, 3 ravnine)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bitno, 4:2:2, 2 ravnini)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bitno, 4:4:4, 2 ravnini)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-bitno)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Barvni prostor\"\nBasic.Settings.Advanced.Video.ColorRange=\"Barvni razpon\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Omejeno\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Celotno\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Raven beline SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Raven nominalnega vrha HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Nadzorna naprava\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Privzeto\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Onemogoči zmanjšanje glasnosti zvoka sistema Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Zakasnitev pretoka\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Trajanje\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Ohrani točko prekinitve (povečaj zakasnitev) ob ponovni povezavi\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Ocenjena poraba pomnilnika: %1 MB\"\nBasic.Settings.Advanced.Network=\"Omrežje\"\nBasic.Settings.Advanced.Network.Disabled=\"Trenutno izbrani protokol pretakanja ne podpira spremembe omrežnih nastavitev.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Veži na IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Družina IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Vklopi omrežne optimatizacije\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Vklopi TCP spodujanje\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Poskus, da izhod RTMP postane prijaznejši za druge programe, ki so občutljivi na latenco, tako da uravnava stopnjo prenosa.\\nLahko poveča možnost za izpuščanje slik na nezanesljivih povezavah.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Vedenje hitrih tipk ob osredotočanju\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nikoli ne onemogoči hitrih tipk\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Onemogoči hitre tipke, ko je glavno okno v žarišču\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Onemogoči hitre tipke, ko glavno okno ni v žarišču\"\nBasic.Settings.Advanced.AutoRemux=\"Samodejno prepakiraj v %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(posnemi kot mkv)\"\nBasic.AdvAudio=\"Napredne lastnosti zvoka\"\nBasic.AdvAudio.ActiveOnly=\"Zgolj aktivni viri\"\nBasic.AdvAudio.Name=\"Ime\"\nBasic.AdvAudio.Volume=\"Glasnost\"\nBasic.AdvAudio.VolumeSource=\"Glasnost za '%1'\"\nBasic.AdvAudio.MonoSource=\"Mono miks za '%1'\"\nBasic.AdvAudio.Balance=\"Ravnovesje\"\nBasic.AdvAudio.BalanceSource=\"Ravnovesje za '%1'\"\nBasic.AdvAudio.SyncOffset=\"Zamik sinhronizacije\"\nBasic.AdvAudio.SyncOffsetSource=\"Odmik sinhroizacije za '%1'\"\nBasic.AdvAudio.Monitoring=\"Nadzor zvoka\"\nBasic.AdvAudio.Monitoring.None=\"Nadzor je izklopljen\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Samo nadziraj (utišaj izhod)\"\nBasic.AdvAudio.Monitoring.Both=\"Nadzor in izhod\"\nBasic.AdvAudio.MonitoringSource=\"Nador zvoka za '%1'\"\nBasic.AdvAudio.AudioTracks=\"Steze\"\nBasic.Settings.Hotkeys=\"Hitre tipke\"\nBasic.Settings.Hotkeys.Pair=\"Kombinacije tipk, deljene s/z '%1', delujejo kot preklopniki\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtriranje po hitri tipki\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"To hitro tipko si deli več dejanj. Kliknite za prikaz sporov\"\nBasic.Settings.Hotkeys.PleaseWait=\"Nalaganje hitrih tipk je v teku ...\"\nBasic.Hotkeys.SelectScene=\"Preklopi na prizor\"\nBasic.SystemTray.Show=\"Prikaži\"\nBasic.SystemTray.Hide=\"Skrij\"\nBasic.SystemTray.Message.Reconnecting=\"Povezava je bila prekinjena. Ponovno povezovanje …\"\nHotkeys.Insert=\"Vstavi\"\nHotkeys.Delete=\"Izbriši\"\nHotkeys.Home=\"Domov\"\nHotkeys.End=\"Ustavi\"\nHotkeys.PageUp=\"Stran gor\"\nHotkeys.PageDown=\"Stran dol\"\nHotkeys.Backspace=\"Vračalka\"\nHotkeys.Tab=\"Zamik.\"\nHotkeys.Print=\"Natisni\"\nHotkeys.Pause=\"Premor\"\nHotkeys.Left=\"Levo\"\nHotkeys.Right=\"Desno\"\nHotkeys.Up=\"Gor\"\nHotkeys.Down=\"Dol\"\nHotkeys.Windows=\"Okna\"\nHotkeys.Menu=\"Meni\"\nHotkeys.Space=\"Presledek\"\nHotkeys.NumpadNum=\"Tipka številč. %1\"\nHotkeys.NumpadMultiply=\"Tipka * na številč.\"\nHotkeys.NumpadDivide=\"Tipka / na številč.\"\nHotkeys.NumpadAdd=\"Tipka + na številč.\"\nHotkeys.NumpadSubtract=\"Tipka - na številč.\"\nHotkeys.NumpadDecimal=\"Tipka . na številč.\"\nHotkeys.AppleKeypadNum=\"%1 (številčnica)\"\nHotkeys.AppleKeypadMultiply=\"* (številčnica)\"\nHotkeys.AppleKeypadDivide=\"/ (številčnica)\"\nHotkeys.AppleKeypadAdd=\"+ (številčnica)\"\nHotkeys.AppleKeypadSubtract=\"- (številčnica)\"\nHotkeys.AppleKeypadDecimal=\". (številčnica)\"\nHotkeys.AppleKeypadEqual=\"= (številčnica)\"\nHotkeys.MouseButton=\"Miška %1\"\nMute=\"Utišaj\"\nUnmute=\"Vklopi zvok\"\nPush-to-mute=\"Pritisni in utišaj\"\nPush-to-talk=\"Pritisni in govori\"\nSceneItemShow=\"Prikaži '%1'\"\nSceneItemHide=\"Skrij '%1'\"\nOutputWarnings.NoTracksSelected=\"Izbrati morate vsaj eno stezo\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Napaka nastavitev izhoda\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Vsi izhodi morajo imeti izbrano vsaj eno zvočno stezo.\"\nOutputWarnings.MP4Recording=\"Opozorilo: posnetkov, shranjenih v MP4/MOV, ne bo mogoče obnoviti, če datoteka ni dokončana (npr. kot posledica \\\"modrega zaslona smrti\\\", izgube napajanja itd.). Če želite posneti več zvočnih stez, lahko uporabite MKV-ja in ga na koncu prepakirate v MP4/MOV (Datoteka → Prepakiraj posnetke)\"\nOutputWarnings.CannotPause=\"Opozorilo: posnetkov ni mogoče začasno ustaviti, če je snemalni kodirnik nastavljen na \\\"(uporabi pretočni kodirnik)\\\"\"\nOutputWarnings.CodecIncompatible=\"Izbor kodirnika zvoka ali slike je ponastavljen zaradi nezdružljivosti. S seznama izberite združljiv kodirnik.\"\nCodecCompat.Incompatible=\"(ni združljivo z %1)\"\nCodecCompat.CodecPlaceholder=\"Izberi kodirnik ...\"\nCodecCompat.ContainerPlaceholder=\"Izberi zapis ...\"\nCodecCompat.CodecMissingOnExit.Title=\"Izbran ni noben kodirnik\"\nCodecCompat.CodecMissingOnExit.Text=\"Vsaj en kodirnik slike ali zvoka ni nastavljen. Zagotovite, da ste izbrali kodirnike tako za snemanje kot za pretakanje.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Izbrana ni nobena vrsta zapisa\"\nCodecCompat.ContainerMissingOnExit.Text=\"Izbrana ni nobena vrsta zapisa za snemanje. Izberite vrsto zapisa za snemanje, ki je združljiva z izbranim kodirnikom toka.\"\nFinalScene.Title=\"Izbriši prizor\"\nFinalScene.Text=\"Obstajati mora vsaj en prizor.\"\nNoSources.Title=\"Ni virov\"\nNoSources.Text=\"Videti je, da še niste dodali nobenega slikovnega vira, tako da boste odvajali samo prazen zaslon. Ali res želite to storiti?\"\nNoSources.Text.AddSource=\"Vire lahko kadarkoli dodate s klikom na ikono + pod poljem Viri v glavnem oknu.\"\nNoSources.Label=\"Nimate virov.\\nKliknite na spodnji gumb\\nali tukaj z desno tipko na miški, da enega dodate.\"\nChangeBG=\"Nastavi barvo\"\nCustomColor=\"Barva po meri\"\nBrowserSource.EnableHardwareAcceleration=\"Omogoči strojno pospeševanje brskalnika\"\nAbout=\"O programu\"\nAbout.Info=\"OBS Studio je prosta in odprtokodna programska oprema za snemanje videoposnetkov in pretakanje v živo.\"\nAbout.Donate=\"Prispevaj\"\nAbout.GetInvolved=\"Pridruži se\"\nAbout.Authors=\"Avtorji\"\nAbout.License=\"Licenca\"\nAbout.Error=\"Napaka! Datoteke ni moč prebrati.\\n\\nGlejte: %1\"\nAbout.Contribute=\"Podprite projekt OBS\"\nAddUrl.Title=\"Dodaj vir preko URL naslova\"\nAddUrl.Text=\"V OBS ste potegnili URL naslov. To ga bo samodejno dodalo kot vire. Nadaljujem?\"\nResizeOutputSizeOfSource=\"Spremeni izhod (velikost vira)\"\nResizeOutputSizeOfSource.Text=\"Osnovna in izhodna ločljivost bosta spremenjeni na velikost trenutnega vira.\"\nResizeOutputSizeOfSource.Continue=\"Ali želite nadaljevati?\"\nPreviewTransition=\"Predogled prehoda\"\nImporter=\"Uvoz zbirk prizorov\"\nImporter.SelectCollection=\"Izberi zbirko prizorov\"\nImporter.Collection=\"Zbirka prizorov\"\nImporter.HelpText=\"Dodaj datoteke v ta seznam, da uvoziš zbirke iz OBS-a ali drugih podprtih programov.\"\nImporter.Path=\"Pot do zbirke\"\nImporter.Program=\"Zaznan program\"\nImporter.AutomaticCollectionPrompt=\"Samodejno iskanje zbirk prizorov\"\nImporter.AutomaticCollectionText=\"OBS lahko samodejno najde zbirke prizorov, ki se ji lahko uvozi, od podprtih programov tretjih razvijalcev. Ali želiš, da OBS samodejno poišče zbirke za vas?\\n\\nTo lahko spremeniš pozneje v Nastavitve > Splošno > Uvozniki.\"\nRestart=\"Ponovni zagon\"\nNeedsRestart=\"OBS Studio zahteva ponovni zagon? Ponovno zaženem zdaj?\"\nLoadProfileNeedsRestart=\"Profil vsebuje nastavitve, ki zahtevajo ponovni zagon OBS:\\n%1\\n\\nAli želite ponovno zagnati OBS, da bodo te nastavitve delovale?\"\nContextBar.NoSelectedSource=\"Ni izbranih virov\"\nContextBar.MediaControls.PlayMedia=\"Predvajaj\"\nContextBar.MediaControls.PauseMedia=\"Pavziraj\"\nContextBar.MediaControls.StopMedia=\"Zaustavi\"\nContextBar.MediaControls.RestartMedia=\"Ponovno žaženi\"\nContextBar.MediaControls.PlaylistNext=\"Naprej na seznamu predvajanja\"\nContextBar.MediaControls.PlaylistPrevious=\"Prejšnje na seznamu predvajanja\"\nContextBar.MediaControls.BlindSeek=\"Pripomoček za iskanje medijev\"\nYouTube.Auth.Ok=\"Uspešna overitev.\\nZdaj lahko zaprete to stran\"\nYouTube.Auth.NoCode=\"Postopek overitve je bil zaključen.\"\nYouTube.Auth.NoChannels=\"Za izbrani račun ni kanalov\"\nYouTube.Auth.WaitingAuth.Title=\"Overjanje Youtube uporabnika\"\nYouTube.Auth.WaitingAuth.Text=\"Zaključite postopek overjanja v zunanjem brskalniku.<br>Če se zunanji brskalnik ne odpre, sledite tej povezavi in zaključite overitev:<br>%1\"\nYouTube.AuthError.Text=\"Ni bilo mogoče pridobiti podatkov o kanalu: %1.\"\nYouTube.Actions.WindowTitle=\"Nastavljanje oddajanja preko You Tube - Kanal: %1\"\nYouTube.Actions.CreateNewEvent=\"Ustvari novo oddajanje\"\nYouTube.Actions.ChooseEvent=\"Izberi obstoječe oddajanje\"\nYouTube.Actions.Title=\"Naslov*\"\nYouTube.Actions.MyBroadcast=\"Moje oddajanje\"\nYouTube.Actions.Description=\"Opis\"\nYouTube.Actions.Privacy=\"Zasebnost*\"\nYouTube.Actions.Privacy.Private=\"Zasebno\"\nYouTube.Actions.Privacy.Public=\"Javno\"\nYouTube.Actions.Privacy.Unlisted=\"Ni na seznamu\"\nYouTube.Actions.Category=\"Kategorija\"\nYouTube.Actions.Thumbnail=\"Predogledna slika\"\nYouTube.Actions.Thumbnail.SelectFile=\"Izberi datoteko ...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Nobena datoteka ni izbrana\"\nYouTube.Actions.Thumbnail.ClearFile=\"Počisti\"\nYouTube.Actions.MadeForKids=\"Je ta video narejen za otroke?*\"\nYouTube.Actions.MadeForKids.Yes=\"Da, narejen je za otroke\"\nYouTube.Actions.MadeForKids.No=\"Ne, ni narejen za otroke\"\nYouTube.Actions.AdditionalSettings=\"Dodatne nastavitve\"\nYouTube.Actions.Latency=\"Zakasnitev\"\nYouTube.Actions.Latency.Normal=\"običajna\"\nYouTube.Actions.Latency.Low=\"majhna\"\nYouTube.Actions.Latency.UltraLow=\"zelo majhna\"\nYouTube.Actions.EnableAutoStart=\"Omogoči samodejni zagon\"\nYouTube.Actions.EnableAutoStop=\"Omogoči samodejno zaustavitev\"\nYouTube.Actions.AutoStartStop.TT=\"Prikazuje ali naj se načrtovano oddajanje začne samodejno\"\nYouTube.Actions.EnableDVR=\"Omogoči DVR\"\nYouTube.Actions.ScheduleForLater=\"Načrtuj za kasneje\"\nYouTube.Actions.RememberSettings=\"Zapomni si te nastavitve\"\nYouTube.Actions.Create_Ready=\"Ustvari oddajanje\"\nYouTube.Actions.Create_GoLive=\"Ustvari oddajanje in zaženi pretok\"\nYouTube.Actions.Choose_Ready=\"Izberi oddajanje\"\nYouTube.Actions.Choose_GoLive=\"Izberi oddajanje in zaženi pretok\"\nYouTube.Actions.Create_Schedule=\"Načrtuj oddajanje\"\nYouTube.Actions.Create_Schedule_Ready=\"Načrtuj in izberi oddajanje\"\nYouTube.Actions.Dashboard=\"Odpri YouTube Studio\"\nYouTube.Actions.Error.Title=\"Napaka pri ustvarjanju oddajanja v živo\"\nYouTube.Actions.Error.Text=\"Napaka pri dostopu do Youtube '%1'.<br/>Podroben opis napake je mogoče najti na <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Napak pri dostopu do Youtube. Preverite omrežno povezavo ali dostonost Youtube strežnika.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Napaka pri ustvarjanju oddaje '%1'.<br/>Podroben opis napake je mogoče najti na <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Pretok ni bil ustvarjen. Ponovno povežite svoj račun.\"\nYouTube.Actions.Error.YouTubeApi=\"Youtube API napaka. Poglejte si beležke dnevnika za več podatkov.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Izbrano oddajanje ni bilo najdeno.\"\nYouTube.Actions.Error.FileMissing=\"Izbrana datoteka ne obstaja\"\nYouTube.Actions.Error.FileOpeningFailed=\"Napaka pri odpiranju izbrane datoteke\"\nYouTube.Actions.Error.FileTooLarge=\"Izbrana datoteka je prevelika (Omejitev: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Prehod na oddajanje ni uspelo: %1<br/><br/>Če se bo ta napaka ponoavljala, <a href='https://studio.youtube.com/video/%2/livestreaming'>odprite oddajanje v YouTube Studio</a> in poskusite ročno.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Oddjanje prehaja v testno fazo, kar lahko traja nekaj časa. Poskusite znova v 10-30 sekundah.\"\nYouTube.Actions.EventsLoading=\"Nalaganje seznama dogodkov...\"\nYouTube.Actions.EventCreated.Title=\"Dogodek je ustvarjen\"\nYouTube.Actions.EventCreated.Text=\"Dogodek je bil uspešno ustvarjen\"\nYouTube.Actions.Stream=\"Pretok\"\nYouTube.Actions.Stream.ScheduledFor=\"Načrtovan za %1\"\nYouTube.Actions.Stream.Resume=\"Ponovno zaženi prekinjen pretok\"\nYouTube.Actions.Stream.YTStudio=\"Samodejno ustvarjeno z Youtube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Ustvarjanje novega oddajanja v živo. Počakajte...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Zahteva ročni zagon\"\nYouTube.Actions.AutoStartStreamingWarning=\"Samodejni zagon za ta dogodek je onemogočen. Klknite na \\\"Pojdi v živo\\\", da začnete z oddajanjem.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Ponovne povezave ne bo mogoče vzpostaviti.<br>Vaš pretokse bo zaustavil in ne boste več oddajali v živo.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Pretakanje v živo ni omogočeno na izbranem Youtube kanalu.<br/><br/>Glej<a href='https://www.youtube.com/features'>youtube.com/features</a> za več informacij.\"\nYouTube.Errors.livePermissionBlocked=\"Pretakanje v živo ni na voljo na izbranem Youtube kanalu.<br/>Lahko traja do 24 ur po vklopu funkcije v nastavitvah kanala, da postane aktivno.<br/><br/>Glej<a href='https://www.youtube.com/features'>youtube.com/features</a> za podrobnosti.\"\nYouTube.Errors.errorExecutingTransition=\"Prehod ni bil uspešen zaradi napake na hrbtenici. Poskusite znova čez nakaj sekund.\"\nYouTube.Errors.errorStreamInactive=\"YoutTube ne sprejema podatkov vašega pretoka. Preverite nastavitve in poskusite znova.\"\nYouTube.Errors.invalidTransition=\"Poskus prehoda ni bil veljaven. Razlog je lahko nedokončan prehod v pretoku. Počakajte nekaj sekund in poskusite znova.\"\nYouTube.DocksRemoval.Title=\"Počisti opuščene zložene zavihke brskalnika YouTube\"\nYouTube.DocksRemoval.Text=\"Ti zloženi zavihki brskalnika bodo odstranjeni kot opuščeni:\\n\\n%1\\nNamesto tega uporabite »Zavihki/režija YouTube«.\"\nConfigDownload.WarningMessageTitle=\"Opozorilo\"\nMultitrackVideo.IncompatibleSettings.Title=\"Nezdružljive nastavitve\"\n"
  },
  {
    "path": "frontend/data/locale/sq-AL.ini",
    "content": "Language=\"Shqip\"\nApply=\"Aplikoni\"\nCancel=\"Anuloni\"\nClose=\"Mbylle\"\nSave=\"Ruaj\"\nDiscard=\"Mos e ruaj\"\nDisable=\"Çaktivizo\"\nYes=\"Po\"\nNo=\"Jo\"\nAdd=\"Shto\"\nRemove=\"Largoje\"\nRename=\"Riemërto\"\nInteract=\"Bashkëveprim\"\nFilters=\"Filterar\"\nProperties=\"Vetitë\"\nMoveUp=\"Lëvize lart\"\nMoveDown=\"Lëvize poshtë\"\nSettings=\"Cilësimet\"\nDisplay=\"Ekrani\"\nName=\"Emri\"\nExit=\"Dil\"\nMixer=\"Mikseri Audio\"\nBrowse=\"Shfleto\"\nDroppedFrames=\"Kornizat e rëna %1 (%2%)\"\nProjector.Window=\"Dritare e re\"\nProjector.Title.Scene=\"Skena : %1\"\nProjector.Title.Source=\"Burimi: %1\"\nProjector.ResizeWindowToContent=\"Përshtat dritaren sipas përmbajtjes\"\nClear=\"Pastro\"\nRevert=\"Rikthe\"\nShow=\"Shfaq\"\nHide=\"Fsheh\"\nUnhideAll=\"Zbuloni të gjitha\"\nUntitled=\"I patitulluar\"\nNew=\"I ri\"\nDuplicate=\"Dublikatë\"\nEnable=\"Aktivizo\"\nDisableOSXVSync=\"Çaktivizo macOS V-Sync\"\nResetOSXVSyncOnExit=\"Rivendos macOS V-Sync në Mbyllje\"\nHighResourceUsage=\"Kodimi i mbingarkuar! Merrni parasysh uljen e cilësimeve të videos ose duke përdorur një parazgjedhje më të shpejtë të kodimit.\"\nTransition=\"Tranzicion\"\nQuickTransitions=\"Tranzicione të shpejta\"\nFadeToBlack=\"Venite në të zezë\"\nLeft=\"Majtas\"\nRight=\"Djathtas\"\nTop=\"Kreu\"\nBottom=\"Fundi\"\nReset=\"Rivë\"\nHours=\"Orë\"\nMinutes=\"Minuta\"\nSeconds=\"Sekonda\"\nDeprecated=\"I pazëvëndësueshëm\"\nReplayBuffer=\"Rifillimi Lustrues\"\nImport=\"Importo\"\nExport=\"Eksporto\"\nCopy=\"Kopjo\"\nPaste=\"Ngjit\"\nPasteReference=\"Ngjit (Referim)\"\nPasteDuplicate=\"Ngjit (Duplikatë)\"\nRemuxRecordings=\"Regjistrimet e Remux\"\nNext=\"Tjeter\"\nBack=\"Prapa\"\nDefaults=\"Parazgjedhura\"\nRestoreDefaults=\"Parazgjedhjet\"\nHideMixer=\"Fshiheni në Mixer\"\nTransitionOverride=\"Zhvendosja e tranzicionit\"\nShowTransition=\"Shfaq Përkthimet\"\nHideTransition=\"Fshih Pëerkthimet\"\nNone=\"Asnje\"\nStudioMode.Preview=\"Inspektim\"\nStudioMode.PreviewSceneName=\"Parapamje: %1\"\nShowInMultiview=\"Shfaqe në Shumë Pamje\"\nVerticalLayout=\"Plani vertikal\"\nGroup=\"Grup\"\nDoNotShowAgain=\"Mos e shfaq përsëri\"\nDefault=\"(E parazgjedhur)\"\nCalculating=\"Duke kalkuluar...\"\nFullscreen=\"Ekran i plotë\"\nWindowed=\"Në dritare\"\nRefreshBrowser=\"Rifresko\"\nAspectRatio=\"Raport i aspektit <b>%1:%2</b>\"\nLockVolume=\"Kyc volumin\"\nLogViewer=\"Kycje shikuesi\"\nShowOnStartup=\"Shfaqe në fillim\"\nOpenFile=\"Hap skedën\"\nAddScene=\"Shto skenë\"\nAddSource=\"Shto Burim\"\nRemoveScene=\"Hiq skenën e zgjedhur\"\nRemoveSource=\"Hiq burimin(et) e zgjedhura\"\nMoveSceneUp=\"Zhvendos skenën sipër\"\nMoveSceneDown=\"Zhvendos skenën poshtë\"\nMoveSourceUp=\"Zhvnedos Burimin(et) sipër\"\nMoveSourceDown=\"Zhvnedos Burimin(et) poshtë\"\nSourceProperties=\"Parametrat e burimeve të hapura\"\nSourceFilters=\"Filtrat e burimeve të hapura\"\nMixerToolbarMenu=\"Menuja e mixuesit audio\"\nSceneFilters=\"Filtrat e skenave të hapura\"\nList=\"Listë\"\nAutomatic=\"Automatike\"\nPluginsFailedToLoad.Title=\"Gabim në ngarkimin e Shtojcës\"\nPluginsFailedToLoad.Text=\"Ngarkimi i shtojacave OBS dështoi të ngarkohet::\\n\\n%1\\nTë lutem përditëso ose hiqi këto shtojca.\"\nAlreadyRunning.Title=\"OBS tashmë po funksion\"\nAlreadyRunning.Text=\"OBS tashmë po kandidon! Nëse nuk keni ndërmend ta bëni këtë, mbyllni çdo rast ekzistues të OBS para se të provoni të shkoni në një instancë të re. Nëse keni OBS vendosur për të minimizuar në tabaka e sistemit, ju lutemi kontrolloni për të parë nëse vazhdon të ekzekutohet atje.\"\nAlreadyRunning.LaunchAnyway=\"Filloje ne nje menyre\"\nChromeOS.Title=\"Platformë e pambështetur\"\nWine.Title=\"Wine detektuar\"\nWine.Text=\"Ekzekutimi i OBS në Wine nuk mbështetet dhe shumë funksione si kapja ose burimet e pajisjes nuk do të funksionojnë ose vetëm me kapacitet të kufizuar.<br><br>Rekomandohet të ekzekutoni një version origjinal të OBS, për shembull <a href='https://flathub.org/apps/details/com.obsproject.Studio'>versioniynë Flatpak</a>ose paketat e sistemit tuaj operativ.\"\nDockCloseWarning.Title=\"Duke mbyllur dritaren e ankorueshme\"\nExtraBrowsers.DockName=\"Emri i dokut\"\nAuth.Authing.Title=\"Duke autentifikuar...\"\nAuth.Authing.Text=\"Autentifikimi me %1, ju lutem prisni...\"\nAuth.AuthFailure.Title=\"Autentifikimi Dështoj\"\nAuth.AuthFailure.Text=\"Dështoj autentifikimi me %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Kërkohet Autentifikim\"\nAuth.LoadingChannel.Title=\"Duke ngarkuar të dhënat e kanalit...\"\nAuth.LoadingChannel.Text=\"Duke ngarkuar të dhënat e kanalit për %1, ju lutem prisni...\"\nAuth.ChannelFailure.Title=\"Dështoi në ngarkimin e kanalit\"\nAuth.StreamInfo=\"Informacioni i transmetimit\"\nTwitchAuth.Stats=\"Statusi i Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Nuk mund të lidh çelësin e transmetimit\"\nTwitchAuth.TwoFactorFail.Text=\"OBS nuk mund të lidhet me llogarinë tuaj të Twitch. Ju lutem sigurohuni që verifikimi me dy faktorë është i aktivizuar në <a href='https://www.twitch.tv/settings/security'>cilësimet e sigurisë të Twitch</a> sepse është e nevojshme per të transmetuar.\"\nRestreamAuth.Channels=\"Kanalet e Restream\"\nCopy.Filters=\"Kopjo Filterat\"\nPaste.Filters=\"Ngjit Filterat\"\nBrowserPanelInit.Title=\"Duke inicializuar Browserin...\"\nBrowserPanelInit.Text=\"Duke inicializuar browserin, ju lutem prisni...\"\nBandwidthTest.Region=\"Regjioni\"\nBandwidthTest.Region.US=\"Shtetet e Bashkuara\"\nBandwidthTest.Region.EU=\"Evropë\"\nBandwidthTest.Region.Asia=\"Azi\"\nBandwidthTest.Region.Other=\"Të tjera\"\nBasic.AutoConfig=\"Konfigurimi automatik\"\nBasic.AutoConfig.ApplySettings=\"Aplikoni Parametrat\"\nBasic.AutoConfig.StartPage=\"Informacion mbi perdorimin\"\nBasic.AutoConfig.StartPage.SubTitle=\"Specifikoni perse ju deshironi ta perdorni kete program\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimizo per transmetim, rregjistrimi eshte dytesore\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimizo vetem per rregjistrim, une nuk do te rregjistroj\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Unë do të jem duke përdour vetëm kamerën virtuale\"\nBasic.AutoConfig.VideoPage=\"Parametrat e videos\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Specifikoni parametrat e videos që doni të përdorni\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Perdorni Aktualen (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Ekrani %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Perdor Aktualen (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ose 30, por preferoj 60 kur eshte e mundur\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ose 30, por perferoj rezolucion te larte\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Shenim: Revolucioni i kanvasit (baza) nuk eshte domosdoshmerisht rezolucioni i njetje me te cilin transmeton ose rregjistron. Rezolucioni i transmetimit/rregjistrimit mund te shkallzohet qe te ulen resurset e perdorimit ose kerkesat e bitratit.\"\nBasic.AutoConfig.StreamPage=\"Informacion mbi transmetimin\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Ju lutem shkruani informacionin e transmetimit\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Lidh Llogari (rekomanduar)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Shkëpute Llogarinë\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Shkëpute Llogarinë?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Merr çelësin e Stream-it\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Më shumë informacion\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Përdor çelësin e transmetimit\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Përdor çelësin e transmetimit (e avancuar)\"\nBasic.AutoConfig.StreamPage.Service=\"Sherbim\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Trego të gjitha...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Celesi i transmetimit\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Çelësi i Koduesit\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Token-i Bearer\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Llogari e lidhur\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Vlerso bitratin e testit te bandwithit (mund te vonohet disa minuta)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Enkodim me pajisje\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Enkodimi me pajisje eliminim shumicen e perdorimit te procesorit, por mund te kerkoje me shume bitrate qe te ruaje kualitetin.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Lajmerim mbi transmetimin\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Testi i Bandwithit do te transmetoje te dhena te rastesishme pa audio te kanali juaj. Nese ti mundesh, rekomandohet qe ti te fikesh perkohesisht ruajtjen e transmetimeve si video dhe beje transmetimin private derisa testi te mbaroje. Te vazhdoje?\"\nBasic.AutoConfig.TestPage=\"Rezultat finale\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Programi tani do te ekzekutoje nje set testesh qe te gjeje parametrat ideal\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testi mbaroj\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Duke performuar nje test per bandwithin, kjo mund te vonohet disa menyra...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Asnjë output për protokollin e këtij shërbimi u gjend\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Duke u lidhur tek: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Dështoi të lidhej me serverin. Kontrollo lidhjen tënde në internet dhe provo përsëri.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Duke testuar bandwithin per: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Duke testuar koduesin e transmetimit, kjo mund të marrë një minutë...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Duke testuar koduesin e inçizimit, kjo mund të marrë një minutë...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Dështoj të nisë koduesin\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Duke testuar %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Koduesi i Transmetimit\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Koduesi i inçizimit\"\nBasic.AutoConfig.TestPage.Result.Header=\"Programi përcaktoi që këto parametra janë ideale për ty:\"\nBasic.Stats=\"Statistika\"\nBasic.Stats.CPUUsage=\"Përdorimi i CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Hapësira e disponueshme e diskut\"\nBasic.Stats.MemoryUsage=\"Memoria e perdorur\"\nBasic.Stats.AverageTimeToRender=\"Koha mesatare për të proëesuar një Frame\"\nBasic.Stats.Output.Recording=\"Rregjistrimi\"\nBasic.Stats.Status=\"Statusi\"\nBasic.Stats.Status.Recording=\"Rregjistrimi\"\nBasic.Stats.Status.Live=\"Drejtpërdrejt\"\nBasic.Stats.Status.Reconnecting=\"Duke u rilidhur\"\nBasic.Stats.Status.Inactive=\"Jo aktive\"\nBasic.Stats.Status.Active=\"Aktive\"\nBasic.Stats.DroppedFrames=\"Frame-t e rrëzuara (Rrjeti)\"\nUpdater.RepairConfirm.Title=\"Konifrmo kontrollin e Integritetit\"\nUpdater.FailedToLaunch=\"Dështoi në ekzekutimin e përditësuesit\"\nQuickTransitions.DuplicateScene=\"Dupliko Skenë\"\nQuickTransitions.EditProperties=\"Dupliko Burimet\"\nBasic.TransitionDuration=\"Kohëzgjatja\"\nUndo.ShowSceneItem=\"Shfaq '%1' në '%2'\"\nUndo.HideSceneItem=\"Fshih '%1' në '%2'\"\nUndo.ReorderSources=\"Ri-rendit burimet në  '%1'\"\nUndo.MoveUp=\"Zhvendos '%1' në '%2'\"\nUndo.MoveDown=\"Zhvendos '%1' poshtë në '%2'\"\nUndo.MoveToTop=\"Zhvendos '%1' sipër '%2'\"\nUndo.MoveToBottom=\"Zhvendos '%1' poshtë '%2'\"\nTitleBar.PortableMode=\"Færanlegt-mode \"\nMissingFiles.HelpText=\"Disa skedarë mungojnë që herën e fundit që ju perdorët OBS\"\nMissingFiles.Replaced=\"Zëvëndesuar\"\nSourceLeak.Title=\"Hreinsun á Grunnvillu\"\nSourceLeak.Text=\"Vandamál kom upp þegar skipt var um senusafn og ekki var hægt að afhlaða sumum heimildum. Þetta vandamál stafar venjulega af viðbótum sem gefa ekki út auðlindir á réttan hátt. Gakktu úr skugga um að allar viðbætur sem þú notar séu uppfærðar.\\n\\nOBS Studio mun nú hætta til að koma í veg fyrir hugsanlega gagnaspillingu.\"\nBasic.Main.Source=\"Burimi\"\n"
  },
  {
    "path": "frontend/data/locale/sr-CS.ini",
    "content": "Language=\"srpski\"\nOK=\"U redu\"\nApply=\"Primeni\"\nCancel=\"Otkaži\"\nClose=\"Zatvori\"\nSave=\"Sačuvaj\"\nDiscard=\"Odbaci\"\nDisable=\"Onemogući\"\nYes=\"Da\"\nNo=\"Ne\"\nAdd=\"Dodaj\"\nRemove=\"Ukloni\"\nRename=\"Preimenuj...\"\nInteract=\"Vrši interakciju\"\nFilters=\"Filteri\"\nProperties=\"Svojstva\"\nMoveUp=\"Premesti nagore\"\nMoveDown=\"Premesti nadole\"\nSettings=\"Podešavanja\"\nDisplay=\"Ekran\"\nName=\"Ime\"\nExit=\"Izađi\"\nMixer=\"Audio Mikser\"\nBrowse=\"Pregledaj\"\nDroppedFrames=\"Ispušteni frejmovi %1 (%2%)\"\nProjector.Open.Program=\"Otvori programski projektor\"\nProjector.Open.Preview=\"Otvori pregled projektora\"\nProjector.Open.Scene=\"Projektor otvorene scene\"\nProjector.Open.Source=\"Projektor otvorenog koda\"\nProjector.Open.Multiview=\"Otvori višestruki prikaz\"\nProjector.Display=\"Prikaz: %1\"\nProjector.Window=\"Novi prozor\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Scena: %1\"\nProjector.Title.Source=\"Izvor: %1\"\nProjector.Title.Multiview=\"Višestruki prikaz\"\nProjector.ResizeWindowToContent=\"Prilagodi prozor sadržaju\"\nClear=\"Obriši\"\nRevert=\"Vrati na stara podešavanja\"\nShow=\"Prikaži\"\nHide=\"Sakrij\"\nUnhideAll=\"Prikaži sve\"\nUntitled=\"Nenaslovljeno\"\nNew=\"Novo...\"\nDuplicate=\"Duplicirajte...\"\nEnable=\"Omogući\"\nDisableOSXVSync=\"Onemogući macOS vertikalnu sinhronizaciju\"\nResetOSXVSyncOnExit=\"Povrati macOS vertikalnu sinhronizaciju po izlasku\"\nHighResourceUsage=\"Enkodiranje preopterećeno! Razmotrite snižavanje video podešavanja li korišćenje bržeg šablona za enkodiranje.\"\nTransition=\"Prelaz\"\nQuickTransitions=\"Brzi prelazi\"\nFadeToBlack=\"Prelazak u crno\"\nLeft=\"Sleva\"\nRight=\"Zdesna\"\nTop=\"Odozgo\"\nBottom=\"Odozdo\"\nReset=\"Poništi\"\nHours=\"Sati\"\nMinutes=\"Minuta\"\nSeconds=\"Sekundi\"\nDeprecated=\"Prevaziđeno\"\nReplayBuffer=\"Buffer za ponovljeni prikaz\"\nImport=\"Uvеzi\"\nExport=\"Izvezi\"\nCopy=\"Kopiraj\"\nPaste=\"Paste (Nalepi)\"\nPasteReference=\"Nalepi (Referentno)\"\nPasteDuplicate=\"Nalepi (Duplikat)\"\nRemuxRecordings=\"Snimanje u slabijem kvalitetu (Remux)\"\nNext=\"Napred\"\nBack=\"Nazad\"\nDefaults=\"Standardno\"\nRestoreDefaults=\"Podrazumevane vrednosti\"\nHideMixer=\"Sakrij u mikser panel\"\nTransitionOverride=\"Tip tranzicije\"\nShowTransition=\"Prikaži prelaz\"\nHideTransition=\"Sakrij prelaz\"\nNone=\"Bez\"\nStudioMode.Preview=\"Radna verzija\"\nStudioMode.PreviewSceneName=\"Pregled: %1\"\nShowInMultiview=\"Prikaži kao simultani pregled\"\nVerticalLayout=\"Organizovano po vertikali\"\nGroup=\"Grupa objekata\"\nDoNotShowAgain=\"Ne prikazuj ponovo\"\nDefault=\"(Podrazumevano)\"\nCalculating=\"Izračunavanje...\"\nFullscreen=\"Na celom ekranu\"\nWindowed=\"U prozoru\"\nRefreshBrowser=\"Osveži\"\nAspectRatio=\"Razmera <b>%1:%2</b>\"\nLockVolume=\"Zaključaj jačinu zvuka\"\nLogViewer=\"Preglednik dnevnika\"\nShowOnStartup=\"Prikaži pri pokretanju\"\nOpenFile=\"Otvori datoteku\"\nAddScene=\"Dodaj scenu\"\nAddSource=\"Dodaj izvor\"\nRemoveScene=\"Ukloni odabranu scenu\"\nRemoveSource=\"Ukloni odabran(e) izbor(e)\"\nMoveSceneUp=\"Podigni scenu\"\nMoveSceneDown=\"Spusti scenu\"\nMoveSourceUp=\"Podigni izvor(e)\"\nMoveSourceDown=\"Spusti izvor(e)\"\nSourceProperties=\"Otvori detalje izvora\"\nSourceFilters=\"Otvori filtere izvora\"\nMixerToolbarMenu=\"Meni za audio miksovanje\"\nSceneFilters=\"Otvori scenske filtere\"\nList=\"Spisak\"\nGrid=\"Mrežni prikaz\"\nAutomatic=\"Automatski\"\nPluginsFailedToLoad.Title=\"Greška prilikom učitavanja dodatne komponente\"\nPluginsFailedToLoad.Text=\"Sledeće OBS dodatne komponente nisu uspele da se učitaju:\\n\\n%1\\nMolimo Vas ažurirajte ih ili ih uklonite.\"\nAlreadyRunning.Title=\"OBS je već porkenut\"\nAlreadyRunning.Text=\"OBS je već pokrenut! Osim ako zaista želite da uradite ovo, molimo Vas da prekinete sve procese vezane za OBS pre nego što pokrenete nov zadatak. U slučaju da OBS radi u pozadini, molimo Vas da proverite system tray (ikonice dole desno, pored sata).\"\nAlreadyRunning.LaunchAnyway=\"Pokreni bez obzira na sve\"\nSafeMode.Restart=\"Da li želite da ponovo pokrenete OBS u bezbednom režimu (dodaci treće strane, skripte i VebSockets su ononemogućeni)?\"\nSafeMode.RestartNormal=\"Da li želite da ponovo pokrenete OBS u normalnom režimu?\"\nChromeOS.Title=\"Nepodržani format\"\nChromeOS.Text=\"OBS je naizgled pokrenut unutar ChromeOS kontejnera. Ova platforma nije podržana.\"\nWine.Title=\"Wine detektovan\"\nWine.Text=\"Pokretanje OBS-a u „Vajnu” nije podržano, i mnoge funkcije poput snimanja ili izvora uređaja neće raditi ili će raditi u ograničenom kapacitetu.<br><br>Preporučuje se da umesto toga instalirate nativnu verziju OBS-a, na primer <a href='https://flathub.org/apps/details/com.obsproject.Studio'>našu Flatpak verziju</a> ili paket za Vaš operativni sistem.\"\nDockCloseWarning.Title=\"Zatvaranje usidrivog prozora\"\nDockCloseWarning.Text=\"Upravo ste zatvorili usidriv prozor. Ako želite da ga ponovo otvorite, možete to učiniti pomoću menija „Dokovi” na traci sa menijima.\"\nExtraBrowsers=\"Prilagođene stanice za pregledač\"\nExtraBrowsers.Info=\"Dodajte priključne stanice tako što ćete im dati ime i URL, a zatim kliknite na Primeni ili Zatvori da biste otvorili priključne stanice. Možete dodati ili ukloniti dokove u bilo kom trenutku.\"\nExtraBrowsers.DockName=\"Ime doka\"\nAuth.Authing.Title=\"Autentifikacija...\"\nAuth.Authing.Text=\"Potvrda identiteta pomoću %1-a, sačekajte...\"\nAuth.AuthFailure.Title=\"Greška pri potvrdi identiteta\"\nAuth.AuthFailure.Text=\"Potvrda identiteta pomoću %1-a nije uspela:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Potrebna je potvrda identiteta\"\nAuth.InvalidScope.Text=\"Zahtevi za autentifikaciju pomoću %1 su promenjeni. Neki dodaci možda neće biti dostupni.\"\nAuth.LoadingChannel.Title=\"Učitavanje informacija o kanalu...\"\nAuth.LoadingChannel.Text=\"Učitavanje informacija o kanalu za %1, molim sačekajte...\"\nAuth.LoadingChannel.Error=\"Nije moguće dobiti informacije o kanalu.\"\nAuth.ChannelFailure.Title=\"Nije moguće učitati kanal\"\nAuth.ChannelFailure.Text=\"Nije moguće učitati informacije o kanalu za %1\\n\\n%2: %3\"\nAuth.Chat=\"Ćaskanje\"\nAuth.StreamInfo=\"Informacije o strimovanju\"\nTwitchAuth.Stats=\"Twitch statistika\"\nTwitchAuth.Feed=\"„Tvič” fid aktivnosti\"\nTwitchAuth.TwoFactorFail.Title=\"Nije moguće postaviti upit za ključ strima\"\nTwitchAuth.TwoFactorFail.Text=\"OBS nije uspeo da se poveže sa vašim Tvič nalogom. Uverite se da je dvofaktorska autentifikacija podešena u vašem <a href='https://www.twitch.tv/settings/security'>Tvič bezbednosnim podešavanjima</a> jer je to potrebno za strimovanje.\"\nRestreamAuth.Channels=\"Restrim kanali\"\nCopy.Filters=\"Kopiraj filtere\"\nPaste.Filters=\"Nalepi filtere\"\nBrowserPanelInit.Title=\"Inicijalizacija pregledača...\"\nBrowserPanelInit.Text=\"Pokretanje pretraživača, molim sačekajte...\"\nBandwidthTest.Region.US=\"Sjedinjene Američke Države\"\nBandwidthTest.Region.EU=\"Evropa\"\nBandwidthTest.Region.Asia=\"Azija\"\nBandwidthTest.Region.Other=\"Ostatak sveta\"\nBasic.AutoConfig=\"Čarobnjak za automatsko konfigurisanje\"\nBasic.AutoConfig.ApplySettings=\"Premeni podešavanja\"\nBasic.AutoConfig.StartPage=\"Način upotrebe programa\"\nBasic.AutoConfig.StartPage.SubTitle=\"Navedite za šta tačno želite da koristite program\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimizuj za steaming (prenos uživo), snimanje je sekindarno\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimizuj za snimanje, streaming (prenos uživo) se neće izvršiti\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Koristiću samo virtuelnu kameru\"\nBasic.AutoConfig.VideoPage=\"Podešavanje videa\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Odredite video podešavanja koja želite da koristite\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Koristi trenutna (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Prikaz %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Koristite trenutni (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Ili 60 ili 30, sa akcentom na 60 kad god je moguće\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Ili 60 ili 30, sa akcentom na visoku rezoluciju\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Napomena: Osnovna rezolucija nije nužno ista kao rezolucija kojom ćete strimovati ili kojom ćete snimati. Vaša stvarna rezolucija kojom strimujete/snimate može biti skalirana u odnosu na osnovnu kako bi se smanjila upotreba resursa ili optimizovao protok.\"\nBasic.AutoConfig.StreamPage=\"Informacije o strimovanju\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Molimo unesite Vaše informacije o strimovanju\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Poveži nalog (preporučeno)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Diskonektuj nalog\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Diskonektuj nalog?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Ova promena će biti primenjena odmah. Da li ste sigurni da želite da diskonektujete Vaš nalog?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Nađi ključ za strimovanje\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Više informacija\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Koristi šifru za strimovanje\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Koristi ključ za strimovanje (napredno)\"\nBasic.AutoConfig.StreamPage.Service=\"Usluga\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Prikaži sve...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Prilagođeno...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Šifra za strimovanje\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: unesite lozinku enkripcije.\\nRTMP: unesite ključ koji pruža servis.\\nSRT: unesite streamid ako ga servis koristi.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Ključ enkodera\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Token nosioca\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Povezani nalog\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Proceni brzinu protoka testom propusnog opsega (može potrajati nekoliko minuta)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Sa akcentom na hardversko kodiranje\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Hardversko kodiranje sprečava veliku upotrebu procesora, ali može zahtevati veću brzinu protoka kako bi se održao isti nivo kvaliteta.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Upozorenje o strimovanju\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Test propusnog opsega strimuje nasumične video podatke bez zvuka na Vašem kanalu. Preporučeno je da privremeno isključite čuvanje video snimaka strimova i da podesite strim kao privatni dok se test ne završi, ako je to moguće. Želite li da nastavite?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Testiraj %1\"\nBasic.AutoConfig.TestPage=\"Konačni rezultati\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Program trenutno izvršava set testova kako bi procenio optimalna podešavanja\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testiranje je završeno\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"U toku je izvođenje testa propusnog opsega, ovo može potrajati nekoliko minuta...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Nije pronađen izlaz za protokol ove usluge\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Povezivanje sa: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Neuspеlo povеzivanjе sa sеrvеrom, molimo provеritе Vašu intеrnеt vеzu i pokušajtе ponovo.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testiranje propusnog opsega za: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"U toku je testiranje strim enkodera, ovo može potrajati par minuta...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"U toku je testiranje enkodera za snimanje, ovo može potrajati par minuta...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Neuspelo pokretanje enkodera\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Trenutno se testira %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Enkoder strimovanja\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Enkoder snimanja\"\nBasic.AutoConfig.TestPage.Result.Header=\"Program je odredio da su ova podešavanja najpovoljnija za vas:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Da biste koristili ova podešavanja, kliknite na ''Primenite Podešavanja''. Da opet konfigurišete wizard i pokušate ponovo, kliknite ''Nazad''. Da konfigurišete ručno podešavanja, kliknite ''Poništi'' i otvorite ''Podešavanja''.\"\nBasic.AutoConfig.Info=\"Čarobnjak za automatsku konfiguraciju će utvrditi najbolja podešavanja na osnovu specifikacija Vašeg računara i brzine interneta.\"\nBasic.AutoConfig.RunAnytime=\"Može se pokrenuti u svakom trenutku iz menija „Alatke”.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Rezolucija strimovanja (skalirana)\"\nBasic.Stats=\"Statistika\"\nBasic.Stats.CPUUsage=\"Iskorišćenost procesora\"\nBasic.Stats.HDDSpaceAvailable=\"Dostupno prostora na disku\"\nBasic.Stats.MemoryUsage=\"Iskorišćenost memorije\"\nBasic.Stats.AverageTimeToRender=\"Prosečno vreme potrebno da se prikaže frejm\"\nBasic.Stats.SkippedFrames=\"Preskočeni frejmovi zbog zaostatka kodiranja\"\nBasic.Stats.MissedFrames=\"Propušteni frejmovi zbog zaostatka prikazivanja\"\nBasic.Stats.Output.Stream=\"Strim\"\nBasic.Stats.Output.Recording=\"Snimanje\"\nBasic.Stats.Status.Recording=\"Snimanje je u toku\"\nBasic.Stats.Status.Live=\"UŽIVO\"\nBasic.Stats.Status.Reconnecting=\"Ponovno povezivanje\"\nBasic.Stats.Status.Inactive=\"Neaktivan\"\nBasic.Stats.Status.Active=\"Aktivno\"\nBasic.Stats.DroppedFrames=\"Propušteni frejmovi (Mreža)\"\nBasic.Stats.MegabytesSent=\"Ukupni podaci na izlazu\"\nBasic.Stats.Bitrate=\"Brzina protoka\"\nBasic.Stats.DiskFullIn=\"Disk pun za (približno)\"\nBasic.Stats.DiskFullIn.Text=\"%1 sati, %2 minuta\"\nBasic.Stats.ResetStats=\"Resetuj statistiku\"\nResetUIWarning.Title=\"Da li ste sigurni da želite da resetujete korisnički interfejs?\"\nResetUIWarning.Text=\"Resetovanje korisničkog interfejsa će sakriti dodatne dokove. Ako želite da dokovi ponovo postanu vidljivi, moraćete da ih omogućite u meniju Dokovi.\\n\\nDa li ste sigurni da želite da resetujete korisnički interfejs?\"\nUpdater.Title=\"Dostupna je nova verzija programa\"\nUpdater.Text=\"Izašla je nova, unapređena verzija:\"\nUpdater.UpdateNow=\"Ažuriraj odmah\"\nUpdater.RemindMeLater=\"Podseti me kasnije\"\nUpdater.Skip=\"Preskoči ovu verziju\"\nUpdater.NoUpdatesAvailable.Title=\"Nema novih unapređenja\"\nUpdater.NoUpdatesAvailable.Text=\"Trenutno nema novih unapređenja\"\nUpdater.BranchNotFound.Title=\"Kanal ažuriranja uklonjen\"\nUpdater.BranchNotFound.Text=\"Kanal ažuriranja koji ste odabrali više nije dostupan, OBS je vraćen na podrazumevane vrednosti.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Provera integriteta nedostupna\"\nUpdater.RepairButUpdatesAvailable.Text=\"Provera integriteta fajla je moguća jedino za najskoriju verziju. Verifikujte i ažurirajte svoju OBS instalaciju odlaskom na Pomoć →Proveri ima li ažuriranja.\"\nUpdater.RepairConfirm.Title=\"Potvrdi proveru integriteta\"\nUpdater.RepairConfirm.Text=\"Pokretanje provere integriteta će skenirati Vašu OBS instalaciju, ispitati ima li u njoj korumpiranih fajlova i po potrebi preuzeti ispravne fajlove. Ovo može potrajati trenutak. \\n\\nDa li želite da nastavite?\"\nUpdater.FailedToLaunch=\"Pokretanje ažuriranja software-a nije uspelo\"\nQuickTransitions.SwapScenes=\"Zameni scene Pregled/Program nakon prelaza\"\nQuickTransitions.SwapScenesTT=\"Zamena scena „pregled” i „program” nakon prelaza (ukoliko originalna scena programa i dalje postoji).\\nOvo neće opozvati nijednu promenu učinjenu nad originalnom scenom programa.\"\nQuickTransitions.DuplicateScene=\"Dupliraj scenu\"\nQuickTransitions.DuplicateSceneTT=\"Kada se uređuje ista scena, dopušta uređivanje transformacije/vidljivosti izvora bez uticaja na izlaz programa.\\nDa biste izmenili svojstva izvora bez uticanja na izlaz programa, omogućite „Dupliraj izvore”.\\nMenjanje ove vrednosti će resetovati trenutnu scenu programa (ako ona i dalje postoji).\"\nQuickTransitions.EditProperties=\"Dupliraj izvore\"\nQuickTransitions.EditPropertiesTT=\"Prilikom uređivanja iste scene, omogućava uređivanje svojstava izvora bez izmene izlaznog programa.\\nOvo se može koristiti samo ako je omogućena opcija \\\"Dupliraj scenu\\\".\\nOdređeni izvori (kao što su snimanje ekrana ili medijski izvori) ne podržavaju ovo i uređivanje nije moguće odvojeno.\\nIzmenom ove vrednosti uspostaviće se trenutna scena programa (ako ona i dalje postoji).\\n\\nUpozorenje: Pošto će izvori biti duplirani, ovo može zahtevati dodatne sistemske ili video resurse.\"\nQuickTransitions.HotkeyName=\"Brzi prelaz: %1\"\nBasic.AddTransition=\"Dodaj podesivi prelaz\"\nBasic.RemoveTransition=\"Ukloni podesivi prelaz\"\nBasic.TransitionProperties=\"Svojstva prelaza\"\nBasic.SceneTransitions=\"Prelazi scena\"\nBasic.TransitionDuration=\"Trajanje\"\nBasic.TogglePreviewProgramMode=\"Studijski režim\"\nBasic.EnablePreviewProgramMode=\"Omogući Studio Režim\"\nBasic.DisablePreviewProgramMode=\"Onemogući Studio Režim\"\nUndo.Undo=\"Opozovi\"\nUndo.Redo=\"Ponovi\"\nUndo.Add=\"Dodaj „%1”\"\nUndo.Delete=\"Izbriši „%1”\"\nUndo.Rename=\"Preimenuj „%1”\"\nUndo.SceneCollection.Switch=\"Prebaci se na „%1”\"\nUndo.Item.Undo=\"Opozovi %1\"\nUndo.Item.Redo=\"Ponovi %1\"\nUndo.Sources.Multi=\"Izbriši %1 izvora\"\nUndo.Filters=\"Promene filtera nad „%1”\"\nUndo.Filters.Paste.Single=\"Nalepi filter „%1” na „%2”\"\nUndo.Filters.Paste.Multiple=\"Kopiranje filtera sa „%1” na „%2”\"\nUndo.Transform=\"Transformiši izvor(e) u „%1”\"\nUndo.Transform.Paste=\"Nalepi transformaciju u „%1”\"\nUndo.Transform.Rotate=\"Rotacija u „%1”\"\nUndo.Transform.Reset=\"Otkazivanje transformacije u „%1”\"\nUndo.Transform.HFlip=\"Horizontalno okretanje u „%1”\"\nUndo.Transform.VFlip=\"Vertikalno okretanje u „%1”\"\nUndo.Transform.FitToScreen=\"Prilagodi ekranu u '%1'\"\nUndo.Transform.StretchToScreen=\"Rastegni na ekran u '%1'\"\nUndo.Transform.Center=\"Centriraj na ekran u '%1'\"\nUndo.Transform.VCenter=\"Vertikalno centriranje na ekran u '%1'\"\nUndo.Transform.HCenter=\"Horizontalno centriranje na ekran u '%1'\"\nUndo.Volume.Change=\"Promena jačine zvuka na '%1'\"\nUndo.Volume.Mute=\"Isključi zvuk za „%1“\"\nUndo.Volume.Unmute=\"Uključi zvuk za „%1“\"\nUndo.Balance.Change=\"Promena balansa zvuka na '%1'\"\nUndo.SyncOffset.Change=\"Promena pomeranja sinhronizacije zvuka na '%1'\"\nUndo.MonitoringType.Change=\"Promeni audio praćenje na '%1'\"\nUndo.Mixers.Change=\"Promeni audio miksere na '%1'\"\nUndo.ForceMono.On=\"Omogući prisilni mono režim na '%1'\"\nUndo.ForceMono.Off=\"Onemogući prisilni mono režim na '%1'\"\nUndo.Properties=\"Promena svojstva na '%1'\"\nUndo.Scene.Duplicate=\"Duplicirajte scenu '%1'\"\nUndo.ShowTransition=\"Prikaži prelaz na '%1'\"\nUndo.HideTransition=\"Sakrij prelaz na '%1'\"\nUndo.ShowSceneItem=\"Prikaži '%1' u '%2'\"\nUndo.HideSceneItem=\"Sakrij '%1' u '%2'\"\nUndo.ReorderSources=\"Preuredi izvore u '%1'\"\nUndo.MoveUp=\"Pomeri '%1' nagore u '%2'\"\nUndo.MoveDown=\"Pomeri '%1' nadole u '%2'\"\nUndo.MoveToTop=\"Pomeri '%1' na vrh u '%2'\"\nUndo.MoveToBottom=\"Pomeri '%1' na dno u '%2'\"\nUndo.PasteSource=\"Nalepi izvor(e) u '%1'\"\nUndo.PasteSourceRef=\"Nalepi izvorne reference u '%1'\"\nUndo.GroupItems=\"Grupiši stavke u '%1'\"\nTransitionNameDlg.Text=\"Molim unesite ime prelaza\"\nTransitionNameDlg.Title=\"Ime prelaza\"\nTitleBar.SafeMode=\"BEZBEDNI REŽIM\"\nTitleBar.PortableMode=\"Prenosivi režim\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Scene\"\nNameExists.Title=\"Ime već postoji\"\nNameExists.Text=\"Ime je već u upotrebi.\"\nNoNameEntered.Title=\"Molim unesite ispravno ime\"\nNoNameEntered.Text=\"Ne možete izostaviti ime.\"\nConfirmStart.Title=\"Započni strim?\"\nConfirmStart.Text=\"Da li ste sigurni da želite da započnete strimovanje?\"\nConfirmStop.Title=\"Zaustavi strim?\"\nConfirmStop.Text=\"Da li ste sigurni da želite da zaustavite strimovanje?\"\nConfirmStopRecord.Title=\"Zaustavi snimanje?\"\nConfirmStopRecord.Text=\"Da li ste sigurni da želite zaustaviti snimanje?\"\nConfirmBWTest.Title=\"Započeti probu protoka?\"\nConfirmBWTest.Text=\"OBS Vam je konfigurisan u režimu probe protoka. Ovaj režim Vam dopušta da isprobate mrežnu povezanost bez potrebe da se uključujete uživo na svom kanalu. Kada budete bili gotovi sa testiranjem, moraćete da onemogućite ovaj režim da bi gledaoci mogli da vide Vaš strim.\\n\\nŽelite li da nastavite?\"\nConfirmRemove.Title=\"Potvrdi izbacivanje\"\nConfirmRemove.Text=\"Da li ste sigurni da želite da uklonite '%1'?\"\nConfirmRemove.TextMultiple=\"Da li ste sigurni da želite da izbacite %1 stavke?\"\nConfirmReset.Title=\"Resetuj svojstva\"\nConfirmReset.Text=\"Jeste li sigurni da želite da vratite trenutna svojstva na njihove podrazumevane vrednosti?\"\nOutput.StartStreamFailed=\"Neuspelo pokretanje strimovanja\"\nOutput.StartRecordingFailed=\"Neuspelo pokretanje snimanja\"\nOutput.StartReplayFailed=\"Neuspelo pokretanje bafera za ponovni prikaz\"\nOutput.StartVirtualCamFailed=\"Pokretanje virtuelne kamere nije uspelo\"\nOutput.StartFailedGeneric=\"Neuspelo pokretanje izlaza. Molimo proverite prijavu za detalje.\\n\\n Obaveštenje: Ukoliko koristite NVENC ili AMD enkodere, postarajte se da su Vaši video drajveri ažurirani.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Ne mogu se sačuvati snimci dok je snimanje pauzirano\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Upozorenje: Snimci se ne mogu sačuvati dok je snimanje pauzirano.\"\nOutput.ConnectFail.Title=\"Neuspešno povezivanje\"\nOutput.ConnectFail.BadPath=\"Neispravna putanja ili URL konekcije. Molim proverite vaša podešavanja da potvrdite njihovu ispravnost.\"\nOutput.ConnectFail.ConnectFailed=\"Neuspešno povezivanje na server\"\nOutput.ConnectFail.InvalidStream=\"Nemoguće je pristupiti navedenom kanalu ili strim ključu, molim proverite vaš strim ključ. Ako je ispravan, možda postoji problem pri povezivanju na server.\"\nOutput.ConnectFail.HdrDisabled=\"HDR izlaz je trenutno onemogućen za ovaj izlaz.\"\nOutput.ConnectFail.Error=\"Neočekivana greška u povezivanju sa serverom. Više informacija se nalazi u log datoteci.\"\nOutput.ConnectFail.Disconnected=\"Prekinuta veza sa serverom.\"\nOutput.StreamEncodeError.Title=\"Greška prilikom kodiranja\"\nOutput.StreamEncodeError.Msg=\"Iskrsla je greška sa koderom tokom strimovanja.\"\nOutput.StreamEncodeError.Msg.LastError=\"Došlo je do greške kodera tokom strimovanja:<br><br> %1\"\nOutput.RecordFail.Title=\"Neuspešno započinjanje snimanja\"\nOutput.RecordFail.Unsupported=\"Izlazni format ili nije podržan ili ne podržava više od jedne zvučne trake. Molim proverite podešavanja i pokušajte ponovo.\"\nOutput.RecordNoSpace.Title=\"Nedovoljno prostora na disku\"\nOutput.RecordNoSpace.Msg=\"Nema dovoljno prostora na disku da se nastavi snimanje.\"\nOutput.RecordError.Title=\"Greška pri snimanju\"\nOutput.RecordError.Msg=\"Neodređena greška se dogodila pri snimanju.\"\nOutput.RecordError.EncodeErrorMsg=\"Iskrsla je greška sa koderom tokom snimanja.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Došlo je do greške kodera tokom snimanja:<br><br> %1\"\nOutput.BadPath.Title=\"Neispravna putanja datoteke\"\nOutput.BadPath.Text=\"Konfigurisana putanja za snimanje nije mogla da se otvori. Molimo Vas proverite svoju putanju za snimanje odlaskom na Podešavanja →Izlaz→Snimanje.\"\nOutput.NoBroadcast.Title=\"Nijedan prenos nije konfigurisan\"\nOutput.NoBroadcast.Text=\"Morate da konfigurišete prenos pre početka strimovanja.\"\nOutput.BroadcastStartFailed=\"Neuspešan pokušaj započinjanja prenosa\"\nOutput.BroadcastStopFailed=\"Neuspešan pokušaj zaustavljanja prenosa\"\nRemux.SourceFile=\"OBS snimak\"\nRemux.TargetFile=\"Datoteka\"\nRemux.Remux=\"Remuks\"\nRemux.Stop=\"Zaustavi remuks\"\nRemux.ClearFinished=\"Obriši završene elemente\"\nRemux.ClearAll=\"Obriši sve elemente\"\nRemux.OBSRecording=\"OBS snimak\"\nRemux.FinishedTitle=\"Remuks je završen\"\nRemux.Finished=\"Završen remuks snimka\"\nRemux.FinishedError=\"Remuks je završen, ali datoteka možda nije kompletirana\"\nRemux.SelectRecording=\"Izaberite OBS snimak...\"\nRemux.SelectTarget=\"Izaberite ciljni fajl...\"\nRemux.FileExistsTitle=\"Ciljni fajl već postoji\"\nRemux.FileExists=\"Sledeći ciljni fajlovi već postoje. Da li želite da ih zamenite novim?\"\nRemux.ExitUnfinishedTitle=\"Remuks u toku\"\nRemux.ExitUnfinished=\"Remuks nije završen, ako sada zaustavite proces datoteka može biti neupotrebljiva.\\nDa li ste sigurni da želite zaustaviti remuks?\"\nRemux.HelpText=\"Priložite fajlove u ovaj prozor za remuks, ili izaberite praznu ''OBS Recording'' ćeliju da potražite fajl.\"\nRemux.NoFilesAddedTitle=\"Nije dodata datoteka za remultipleksiranje\"\nRemux.NoFilesAdded=\"Nijedna datoteka nije dodata u remuks. Odbacite fasciklu koja sadrži jednu ili više video datoteka.\"\nMissingFiles=\"Fajlovi nedostaju\"\nMissingFiles.MissingFile=\"Fajl nedostaje\"\nMissingFiles.NewFile=\"Novi fajl\"\nMissingFiles.HelpText=\"Neki fajlovi nedostaju od kada ste poslednji put koristili OBS.\"\nMissingFiles.Clear=\"<očišćeno>\"\nMissingFiles.NumFound=\"Pronađen %1 od %2\"\nMissingFiles.Search=\"Pretraži folder...\"\nMissingFiles.SelectFile=\"Izaberi fajl...\"\nMissingFiles.SelectDir=\"Izaberite folder za pretragu\"\nMissingFiles.State=\"Stanje\"\nMissingFiles.Missing=\"Nestao\"\nMissingFiles.Replaced=\"Zamenjen\"\nMissingFiles.Cleared=\"Očišćen\"\nMissingFiles.Found=\"Nađen\"\nMissingFiles.AutoSearch=\"Naknadni odgovarajući fajlovi su pronađeni\"\nMissingFiles.AutoSearchText=\"OBS je pronašao naknadna poklapanja sa nestalim fajlovima u tom folderu. Želite li da ih dodate?\"\nMissingFiles.NoMissing.Title=\"Provera nestalih fajlova\"\nMissingFiles.NoMissing.Text=\"Nijedan fajl naizgled ne nedostaje.\"\nMacPermissions.MenuAction=\"Pregledaj dozvole za aplikacije...\"\nMacPermissions.Title=\"Pregledaj dozvole aplikacije\"\nMacPermissions.Description=\"OBS zahteva Vašu dozvolu da bi mogao da Vam pruži određene usluge. Preporučuje se da odobrite ove dozvole, ali one nisu neophodne za korišćenje aplikacije. Uvek možete da ih odobrite kasnije.\"\nMacPermissions.Description.OpenDialog=\"Možete ponovo otvoriti ovaj prozor iz menija OBS studija.\"\nMacPermissions.AccessGranted=\"Pristup odobren\"\nMacPermissions.RequestAccess=\"Zatraži pristup\"\nMacPermissions.OpenPreferences=\"Otvori preference za %1\"\nMacPermissions.Item.ScreenRecording=\"Snimanje ekrana\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS zahteva ovu dozvolu da bi mogao da snimi Vaš ekran.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Ova dozvola je potrebna za snimanje sadržaja pomoću veb kamere ili kartice za snimanje.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS zahteva ovu dozvolu ako želiš da snimaš svoj mikrofon ili spoljni zvučni uređaj.\"\nMacPermissions.Item.InputMonitoring=\"Praćenje unosa\"\nMacPermissions.Item.InputMonitoring.Details=\"Ova dozvola je potrebna da bi prečice radile dok je OBS u pozadini.\"\nMacPermissions.Item.Accessibility=\"Pristupačnost\"\nMacPermissions.Item.Accessibility.Details=\"Na starijim instalacijama, OBS može biti naveden u „Pristupačnost“ umesto „Praćenje unosa“.\"\nMacPermissions.Continue=\"Nastavi\"\nSourceLeak.Title=\"Greška pri čišćenju izvora\"\nSourceLeak.Text=\"Došlo je do problema prilikom promene kolekcija scena i neki izvori nisu mogli biti istovareni. Ovaj problem obično uzrokuju dodaci koji ne oslobađaju resurse ispravno. Molimo vas da se uverite da su svi dodaci koje koristite ažurirani.\\n\\nOBS Studio će se sada zatvoriti kako bi se sprečilo potencijalno oštećenje podataka.\"\nBasic.DesktopDevice1=\"Zvuk sa računara\"\nBasic.DesktopDevice2=\"Zvuk sa računara 2\"\nBasic.AuxDevice1=\"Mikrofon/Ulaz\"\nBasic.AuxDevice2=\"Mikrofon/Ulaz 2\"\nBasic.AuxDevice3=\"Mikrofon/Ulaz 3\"\nBasic.AuxDevice4=\"Mikrofon/Ulaz 4\"\nBasic.Scene=\"Scena\"\nBasic.DisplayCapture=\"Prikaži ulaz\"\nBasic.Main.PreviewConextMenu.Enable=\"Omogući pregled\"\nBasic.Main.Preview.Disable=\"Onemogući pregled\"\nScaleFiltering=\"Filter uvećanja\"\nScaleFiltering.Point=\"Tačka\"\nScaleFiltering.Bilinear=\"Bilinearno\"\nScaleFiltering.Bicubic=\"Vikubično\"\nScaleFiltering.Lanczos=\"Lankoz\"\nScaleFiltering.Area=\"Oblast\"\nBlendingMethod=\"Metod mešanja boja\"\nBlendingMethod.Default=\"Podrazumevano\"\nBlendingMethod.SrgbOff=\"Isključen SRGB\"\nBlendingMode=\"Metod mešanja boja\"\nBlendingMode.Normal=\"Normalan\"\nBlendingMode.Additive=\"Dodaj\"\nBlendingMode.Subtract=\"Suptrakovani\"\nBlendingMode.Screen=\"Ekran\"\nBlendingMode.Multiply=\"Množi\"\nBlendingMode.Lighten=\"Posvetli\"\nBlendingMode.Darken=\"Potamni\"\nDeinterlacing=\"Deinterlejsing\"\nDeinterlacing.Discard=\"Odbaci\"\nDeinterlacing.TopFieldFirst=\"Prvo gornje polje\"\nDeinterlacing.BottomFieldFirst=\"Prvo donje polje\"\nVolControl.SliderUnmuted=\"Klizač za jačinu zvuka za '%1':\"\nVolControl.SliderMuted=\"Klizač za jačinu zvuka za „%1“: (trenutno isključen zvuk)\"\nVolControl.Mute=\"Isključite zvuk '%1'\"\nVolControl.Properties=\"Svojstva '%1'\"\nVolControl.UnassignedWarning.Title=\"Nedodeljeni audio izvor\"\nVolControl.UnassignedWarning.Text=\"„%1“ nije dodeljen nijednoj audio numeri i neće se čuti u strimovima ili snimcima.\\n\\nDa biste dodelili audio izvor numeri, otvorite Napredna svojstva zvuka preko menija desnog klika ili dugmeta zupčanika u traci sa alatkama miksera.\"\nBasic.Main.AddSceneDlg.Title=\"Dodaj scenu\"\nBasic.Main.AddSceneDlg.Text=\"Molim unesite ime scene\"\nBasic.Main.DefaultSceneName.Text=\"Scena %1\"\nBasic.Main.AddSceneCollection.Title=\"Dodaj kolekciju scena\"\nBasic.Main.AddSceneCollection.Text=\"Molim unesite ime kolekcije scena\"\nBasic.Main.RenameSceneCollection.Title=\"Promeni ime kolekcije scena\"\nBasic.Main.RemigrateSceneCollection.Title=\"Ažuriraj rezoluciju kolekcije scena\"\nBasic.Main.RemigrateSceneCollection.Text=\"Da li želite da ažurirate rezoluciju kolekcije scena „%1“ tako da odgovara rezoluciji platna trenutnog profila od %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Nije moguće ažurirati rezoluciju kolekcije scena dok su izlazi aktivni.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Ažuriranje rezolucije kolekcije scena nije uspelo. Originalna rezolucija je nepoznata.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Resetovanje nije moguće: Promena OBS rezolucije nije uspela.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Resetovanje nije moguće: Trenutna rezolucija je već osnovna rezolucija kolekcije scena.\"\nAddProfile.Title=\"Dodaj profil\"\nAddProfile.Text=\"Molim unesite ime profila\"\nAddProfile.WizardCheckbox=\"Prikaži čarobnjaka za automatsku konfiguraciju\"\nRenameProfile.Title=\"Promeni ime profila\"\nBasic.Main.MixerRename.Title=\"Preimenujte izvor zvuka\"\nBasic.Main.MixerRename.Text=\"Molimo unesite naziv izvora zvuka\"\nBasic.Main.PreviewDisabled=\"Pregled je trenutno onemogućen\"\nBasic.SourceSelect=\"Napravi ili izaberi izvor\"\nBasic.SourceSelect.CreateNew=\"Napravi novi\"\nBasic.SourceSelect.AddExisting=\"Dodaj postojeći\"\nBasic.SourceSelect.AddVisible=\"Prikaži izvor\"\nBasic.Main.Sources.Visibility=\"Vidljivost\"\nBasic.Main.Sources.VisibilityDescription=\"Kontroliše vidljivost „%1” na platnu\"\nBasic.Main.Sources.Lock=\"Zaključavanje\"\nBasic.Main.Sources.LockDescription=\"Zaključava poziciju i razmeru „%1” na platnu\"\nBasic.PropertiesWindow=\"Svojstva za '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (automatski odabir: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Izaberi boju\"\nBasic.PropertiesWindow.SelectFont=\"Izaberi font\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Izaberite font\"\nBasic.PropertiesWindow.ConfirmTitle=\"Podešavanja promenjena\"\nBasic.PropertiesWindow.Confirm=\"Postoje podešavanja koja nisu sačuvana. Da li želite da ih sačuvate?\"\nBasic.PropertiesWindow.NoProperties=\"Nema dostupnih svojstava\"\nBasic.PropertiesWindow.AddFiles=\"Dodaj datoteke\"\nBasic.PropertiesWindow.AddDir=\"Dodaj direktorijum\"\nBasic.PropertiesWindow.AddURL=\"Dodaj putanju/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Dodaj direktorijum u '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Dodaj datoteke u '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Dodaj zapise u '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Izmeni zapise za '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Jednostavne FPS vrednosti\"\nBasic.PropertiesView.FPS.Rational=\"Racionalne FPS vrednosti\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Ispravni opsezi FPS-a:\"\nBasic.PropertiesView.UrlButton.Text=\"Otvoriti ovaj link u podrazumevanom veb pregledaču?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Otvori URL\"\nBasic.InteractionWindow=\"Interakcija sa '%1'\"\nBasic.StatusBar.Reconnecting=\"Veza je prekinuta, ponovno povezivanje za %2 sekund(i) (%1 pokušaj)\"\nBasic.StatusBar.AttemptingReconnect=\"Pokušavam ponovno povezivanje... (%1 pokušaj)\"\nBasic.StatusBar.ReconnectSuccessful=\"Ponovno povezivanje uspešno\"\nBasic.StatusBar.Delay=\"Kašnjenje (%1 sek)\"\nBasic.StatusBar.DelayStartingIn=\"Kašnjenje (počinje za %1 sek)\"\nBasic.StatusBar.DelayStoppingIn=\"Kašnjenje (zaustavlja se za %1 sek)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Kašnjenje (zaustavlja se za %1 sek, počinje za %2 sek)\"\nBasic.StatusBar.RecordingSavedTo=\"Snimak sačuvan u „%1”\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Bafer snimka sačuvan u „%1”\"\nBasic.StatusBar.ScreenshotSavedTo=\"Snimak ekrana tj. skrinšot sačuvan u „%1”\"\nBasic.StatusBar.AutoRemuxedTo=\"Snimak automatski remuksovan u „%1”\"\nBasic.Filters=\"Filteri\"\nBasic.Filters.AsyncFilters=\"Zvučni/Video filteri\"\nBasic.Filters.AudioFilters=\"Zvučni filteri\"\nBasic.Filters.EffectFilters=\"Filteri efekata\"\nBasic.Filters.Title=\"Filteri za '%1'\"\nBasic.Filters.AddFilter.Title=\"Ime filtera\"\nBasic.Filters.AddFilter.Text=\"Molim naznačite ime filtera\"\nBasic.TransformWindow=\"Transformacija stavke na sceni\"\nBasic.TransformWindow.Position=\"Pozicija\"\nBasic.TransformWindow.PositionX=\"Pozicija iks\"\nBasic.TransformWindow.PositionY=\"Pozicija ipsilon\"\nBasic.TransformWindow.Rotation=\"Rotacija\"\nBasic.TransformWindow.Size=\"Veličina\"\nBasic.TransformWindow.Width=\"Širina\"\nBasic.TransformWindow.Height=\"Visina\"\nBasic.TransformWindow.Alignment=\"Poziciono poravnanje\"\nBasic.TransformWindow.BoundsType=\"Tip okvira\"\nBasic.TransformWindow.BoundsAlignment=\"Poravnanje u okviru\"\nBasic.TransformWindow.Bounds=\"Veličina okvira\"\nBasic.TransformWindow.BoundsWidth=\"Širina okvira\"\nBasic.TransformWindow.BoundsHeight=\"Visina okvira\"\nBasic.TransformWindow.CropToBounds=\"Iseci do graničnog okvira\"\nBasic.TransformWindow.Crop=\"Isecanje\"\nBasic.TransformWindow.CropLeft=\"Odseci s leva\"\nBasic.TransformWindow.CropRight=\"Odseci s desna\"\nBasic.TransformWindow.CropTop=\"Odseci odozgo\"\nBasic.TransformWindow.CropBottom=\"Odseci odozdo\"\nBasic.TransformWindow.Alignment.TopLeft=\"Gore levo\"\nBasic.TransformWindow.Alignment.TopCenter=\"Gore, na sredini\"\nBasic.TransformWindow.Alignment.TopRight=\"Gore desno\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Na sredini, levo\"\nBasic.TransformWindow.Alignment.Center=\"Centar\"\nBasic.TransformWindow.Alignment.CenterRight=\"Na sredini, desno\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Donje levo\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Dole, na sredini\"\nBasic.TransformWindow.Alignment.BottomRight=\"Donje desno\"\nBasic.TransformWindow.BoundsType.None=\"Bez okvira\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Samo najveća veličina\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skaliraj prema unutrašnjim okvirima\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skaliraj prema spoljnjim okvirima\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skaliraj do širine okvira\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skaliraj do visine okvira\"\nBasic.TransformWindow.BoundsType.Stretch=\"Razvuci do okvira\"\nBasic.TransformWindow.Title=\"Izmeni transformaciju za „%1”\"\nBasic.TransformWindow.NoSelectedSource=\"Nije izabran izvor\"\nBasic.Main.AddSourceHelp.Title=\"Nemoguće je dodati izvor\"\nBasic.Main.AddSourceHelp.Text=\"Potrebna je najmanje jedna scena da biste dodali izvor.\"\nBasic.Main.Scenes=\"Scene\"\nBasic.Main.Sources=\"Izvori\"\nBasic.Main.Source=\"Izvor\"\nBasic.Main.Controls=\"Kontrole\"\nBasic.Main.PreparingStream=\"Priprema...\"\nBasic.Main.Connecting=\"Povezivanje...\"\nBasic.Main.StartRecording=\"Započni snimanje\"\nBasic.Main.StartReplayBuffer=\"Pokreni bafer za ponovno prikazivanje\"\nBasic.Main.SaveReplay=\"Sačuvaj ponovno reprodukovanje\"\nBasic.Main.StartStreaming=\"Započni strimovanje\"\nBasic.Main.StartBroadcast=\"Emituj uživo\"\nBasic.Main.StartVirtualCam=\"Pokreni virtualnu kameru\"\nBasic.Main.StopRecording=\"Zaustavi snimanje\"\nBasic.Main.PauseRecording=\"Pauziraj snimanje\"\nBasic.Main.UnpauseRecording=\"Nastavi snimanje\"\nBasic.Main.SplitFile=\"Podeli datoteku snimka na više delova\"\nBasic.Main.StoppingRecording=\"Zaustavljanje snimanja...\"\nBasic.Main.StopReplayBuffer=\"Zaustavi bafer za ponovno prikazivanje\"\nBasic.Main.StoppingReplayBuffer=\"Zaustavljanje bafera za ponovno prikazivanje je u toku...\"\nBasic.Main.SetupBroadcast=\"Upravljanje prenosom\"\nBasic.Main.StopStreaming=\"Zaustavi strimovanje\"\nBasic.Main.StopBroadcast=\"Kraj prenosa\"\nBasic.Main.AutoStopEnabled=\"(Automatsko zaustavljanje)\"\nBasic.Main.StoppingStreaming=\"Zaustavljanje emitovanja...\"\nBasic.Main.ForceStopStreaming=\"Zaustavi strimovanje (poništi kašnjenje)\"\nBasic.Main.ShowContextBar=\"Prikaži traku sa alatkama „Izvori”\"\nBasic.Main.HideContextBar=\"Sakrij traku sa alatkama „Izvori”\"\nBasic.Main.StopVirtualCam=\"Zaustavi virtuelnu kameru\"\nBasic.Main.Group=\"Grupa %1\"\nBasic.Main.GroupItems=\"Grupiši odabrane elemente\"\nBasic.Main.Ungroup=\"Razdvoji elemente grupe\"\nBasic.Main.GridMode=\"Režim koordinatne mreže\"\nBasic.Main.ListMode=\"Režim liste\"\nBasic.Main.VirtualCamConfig=\"Konfiguracija virtuelne kamere\"\nBasic.VCam.VirtualCamera=\"Virtuelna kamera\"\nBasic.VCam.OutputType=\"Tip izlaza\"\nBasic.VCam.OutputSelection=\"Odabir izlaza\"\nBasic.VCam.OutputType.Program=\"Program (Podrazumevani)\"\nBasic.VCam.OutputSelection.NoSelection=\"Nema izbora za ovaj tip izlaza\"\nBasic.VCam.RestartWarning=\"Virtuelna kamera će biti ponovo pokrenuta da bi se primenila ova promena\"\nBasic.MainMenu.File=\"Datoteka\"\nBasic.MainMenu.File.Export=\"Izvezi (E) (&E)\"\nBasic.MainMenu.File.Import=\"Uvoz (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Prikaži snimke\"\nBasic.MainMenu.File.Remux=\"Re&mux snimaka\"\nBasic.MainMenu.File.Settings=\"Podešavanja\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Prikaži foldere sa podešavanjima\"\nBasic.MainMenu.File.ShowProfileFolder=\"Prikaži folder sa podešavanjima\"\nBasic.MainMenu.File.ShowMissingFiles=\"Proveri fale li fajlovi\"\nBasic.MainMenu.File.Exit=\"Izlaz (&X)\"\nBasic.MainMenu.Edit=\"Uredi\"\nBasic.MainMenu.Edit.Undo=\"Vrati unazad (&U)\"\nBasic.MainMenu.Edit.Redo=\"Ponovi (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Zaključaj prikaz (&L)\"\nBasic.MainMenu.Edit.Scale=\"Pregled skaliranja (&s)\"\nBasic.MainMenu.Edit.Scale.Window=\"Skaliraj na veličinu prozora\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Platno (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Izlaz (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Skalirano (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Uvećaj\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Umanji\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Resetuj zumiranje\"\nBasic.MainMenu.Edit.Transform=\"Transformiši (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Uredi transformaciju\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopiraj transformaciju\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Nalepi transformaciju\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Poništi transformaciju (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Okreni za 90 stepeni u smeru kazaljke na satu\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Okreni za 90 stepeni u kontra smeru kazaljke na satu\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Okreni za 180 stepeni\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Prevrni horizontalno (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Prevrni vertikalno (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Raširi po ekranu (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Razvuci po ekranu (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Centriraj na ekranu (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centriraj vertikalno\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centriraj horizontalno\"\nBasic.MainMenu.Edit.Order=\"Redosled (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Pomeri gore (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Pomeri dole (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Pomeri na vrh (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Pomeri na dno (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Napredna podešavanja zvuka (&A)\"\nBasic.MainMenu.View=\"Pregled\"\nBasic.MainMenu.View.Toolbars=\"Linije alatki (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"Doknite trake sa alatkama\"\nBasic.MainMenu.View.ContextBar=\"Izvorska traka sa alatkama\"\nBasic.MainMenu.View.SourceIcons=\"Ikonice izvora\"\nBasic.MainMenu.View.StatusBar=\"Statusna linija (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Interfejs na celom ekranu\"\nBasic.MainMenu.View.ResetUI=\"Resetuj korisnički interfejs\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Uvek na vrhu\"\nBasic.MainMenu.View.SceneListMode=\"Režim liste scena\"\nBasic.MainMenu.Docks=\"&Dokovi\"\nBasic.MainMenu.Docks.ResetDocks=\"&Resetuj dokove\"\nBasic.MainMenu.Docks.LockDocks=\"&Zaključaj dokove\"\nBasic.MainMenu.Docks.SideDocks=\"Dokovi pune visine\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Prilagođeni dokovi pregledača\"\nBasic.MainMenu.Import=\"Uvoz...\"\nBasic.MainMenu.Export=\"Izvoz...\"\nBasic.MainMenu.SceneCollection=\"Kolekcija scena\"\nBasic.MainMenu.Profile=\"Profil\"\nBasic.MainMenu.Profile.Import=\"Uvezi profil\"\nBasic.MainMenu.Profile.Export=\"Izvezi profil\"\nBasic.MainMenu.SceneCollection.Import=\"Uvezi kolekciju scena\"\nBasic.MainMenu.SceneCollection.Export=\"Izvezi kolekciju scena\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Resetuj osnovnu rezoluciju\"\nBasic.MainMenu.SceneCollection.Migrate=\"Podesi osnovnu rezoluciju\"\nBasic.MainMenu.Profile.Exists=\"Ovaj profil već postoji\"\nBasic.MainMenu.Tools=\"Alati\"\nBasic.MainMenu.Help=\"Pomoć\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal za pomoć\"\nBasic.MainMenu.Help.Website=\"Poseti stranicu (&W)\"\nBasic.MainMenu.Help.Discord=\"Pridružite se &Discord serveru\"\nBasic.MainMenu.Help.WhatsNew=\"Šta je novo\"\nBasic.MainMenu.Help.Logs=\"Log datoteke (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Prikaži log datoteke (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Pošalji trenutnu log datoteku (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Otpremi &prethodni fajl izveštaja\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Prikaži trenutni zapisnik (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Napomene o izdanju\"\nBasic.MainMenu.Help.CheckForUpdates=\"Proveri verziju programa\"\nBasic.MainMenu.Help.Repair=\"Provera integriteta fajla\"\nBasic.MainMenu.Help.RestartSafeMode=\"Ponovo pokreni u Bezbednom Režimu\"\nBasic.MainMenu.Help.RestartNormal=\"Ponovo pokreni u Normalnom Režimu\"\nBasic.MainMenu.Help.CrashLogs=\"Izveštaji o prekidima (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Prikaži izveštaje o prekidima (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Otpremi &prethodni izveštaj o rušenju\"\nBasic.MainMenu.Help.About=\"O programu (&A)\"\nBasic.Settings.ProgramRestart=\"Program mora biti ponovo pokrenut da bi ova podešavanja bila prihvaćena.\"\nBasic.Settings.ConfirmTitle=\"Potvrdite promene\"\nBasic.Settings.Confirm=\"Postoje promene koje nisu sačuvane. Sačuvati?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 kontroliše neka od vaših podešavanja strima\"\nBasic.Settings.General=\"Opšte\"\nBasic.Settings.General.Language=\"Jezik\"\nBasic.Settings.General.Updater=\"Ažuriranja\"\nBasic.Settings.General.UpdateChannel=\"Kanal ažuriranja\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Onemogućeno)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Podrazumevano)\"\nBasic.Settings.General.EnableAutoUpdates=\"Automatski potraži ažuriranja pri pokretanju\"\nBasic.Settings.General.OpenStatsOnStartup=\"Prikaži statistiku pri pokretanju\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Sakrijte OBS prozore prilikom snimanja ekrana\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Ovo podešavanje će sakriti sve neprojektorske prozore OBS studija da ih OBS ne bi snimio, što će uticati i na \\ndruge aplikacije, poput aplikacija za video konferencije, deljenje ekrana, podršku na daljinu, skrinšotovanje i drugi softver sa funkcijama snimanja ekrana.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Uključivanjem ove funkcije će biti sakriveni svi neprojektorski prozori OBS studija da ih OBS ne bi snimio, što će uticati i na druge aplikacije, poput aplikacija za video konferencije, deljenje ekrana, podršku na daljinu, skrinšotovanje i drugi softver sa funkcijama snimanja ekrana.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Prikaži prozor za potvrdu kada se započinju strimovi\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Prikaži prozor za potvrdu kada se zaustavljaju strimovi\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Prikaži prozor za potvrdu prilikom prestanka snimanja\"\nBasic.Settings.General.Projectors=\"Projektori\"\nBasic.Settings.General.HideProjectorCursor=\"Sakrij pokazivač na projektorima\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Uvek postavi projektor na vrh prozora\"\nBasic.Settings.General.Snapping=\"Poravnavanje privlačenjem izvora\"\nBasic.Settings.General.ScreenSnapping=\"Privuci izvore ivici ekrana\"\nBasic.Settings.General.CenterSnapping=\"Privuci izvore horizontalnoj i vertikalnoj sredini\"\nBasic.Settings.General.SourceSnapping=\"Privlačenje izvora ka drugim izvorima\"\nBasic.Settings.General.SnapDistance=\"Osetljivost privlačenja\"\nBasic.Settings.General.SpacingHelpers=\"Prikaži pomoćne linije za orijentaciju piksela\"\nBasic.Settings.General.RecordWhenStreaming=\"Automatsko snimanje pri emitovanju\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Nastavi snimati kada se emitovanje zaustavi\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Automatski pokreni bafer ponovnog prikazivanja tokom strimovanja\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Nakon zaustavljanja strimovanja, zadrži bafer ponovnog prikazivanja kao aktivan\"\nBasic.Settings.General.SysTray=\"System tray\"\nBasic.Settings.General.SysTrayWhenStarted=\"Pri pokretanju minimizuj na ikonicu u sistemskom panelu\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Uvek minimizuj system tray umesto task bar-a\"\nBasic.Settings.General.SaveProjectors=\"Sačuvaj projektore pre izlaska iz programa\"\nBasic.Settings.General.CloseExistingProjectors=\"Ograničenje od jednog full-screen projektora po ekranu\"\nBasic.Settings.General.Preview=\"Pregled\"\nBasic.Settings.General.OverflowHidden=\"Sakrij višak\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Višak je uvek vidljiv\"\nBasic.Settings.General.OverflowSelectionHidden=\"Prikaži višak čak i kada je izvor nevidljiv\"\nBasic.Settings.General.Importers=\"Uvoznici\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Pretraži znane lokacije za prikupljanje scena prilikom uvoza\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Prelaz na scenu duplim klikom\"\nBasic.Settings.General.StudioPortraitLayout=\"Uključi portret/vertikalni prikaz\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Prikaži oznake za pregled/program\"\nBasic.Settings.General.Multiview=\"Simultani prikaz\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Promena scene klikom\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Prikaži imena scena\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Prikaži sigurne oblasti (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Raspored simultanog prikaza\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horizontalno, gore (8 scena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horizontalno, dole (8 scena)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertikalno, levo (8 scena)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertikalno, desno (8 scena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horizontalno, Vrh (18 scena)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horizontalno, gore (24 scene)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Samo scene (4 scene)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Samo scene (9 scena)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Samo scene (16 scena)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Samo scene (25 scena)\"\nBasic.Settings.General.ChannelName.stable=\"Stabilno\"\nBasic.Settings.General.ChannelDescription.stable=\"Poslednja stabilna verzija\"\nBasic.Settings.General.ChannelName.beta=\"Beta verzije i kandidati za izdanje\"\nBasic.Settings.General.ChannelDescription.beta=\"Potencijalno nestabilne predrazvojne verzije\"\nBasic.Settings.Appearance=\"Izgled\"\nBasic.Settings.Appearance.General=\"Opšte\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Stil\"\nBasic.Settings.Appearance.General.NoVariant=\"Nema dostupnih stilova\"\nBasic.Settings.Appearance.FontScale=\"Veličina fonta\"\nBasic.Settings.Appearance.Density=\"Gustina\"\nBasic.Settings.Appearance.Classic=\"Klasično\"\nBasic.Settings.Appearance.Compact=\"Kompaktno\"\nBasic.Settings.Appearance.Normal=\"Normalna\"\nBasic.Settings.Appearance.Comfortable=\"Udobno\"\nBasic.Settings.Appearance.OptionsWarning=\"Neke opcije izgleda nisu dostupne za ovaj stil.\"\nBasic.Settings.Stream=\"Strim\"\nBasic.Settings.Stream.Destination=\"Odredište\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Koristi autentifikaciju\"\nBasic.Settings.Stream.Custom.Username=\"Korisničko ime\"\nBasic.Settings.Stream.Custom.Password=\"Šifra\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: unesite srp_username.\\nRTMP: unesite korisničko ime.\\nSRT: nije u upotrebi.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: unesite srp_password.\\nRTMP: unesite lozinku.\\nSRT: unesite šifru za enkripciju.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Omogući režim testiranja protočnog opsega\"\nBasic.Settings.Stream.TTVAddon=\"Dodaci za Tvič čet\"\nBasic.Settings.Stream.TTVAddon.None=\"Nijedan\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV i FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Nedostaje postavka strima\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Otvori podešavanja\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL i ključ strima nedostaju.\\n\\nOtvorite podešavanja i unesite URL i ključ strima u kartici „strim”.\"\nBasic.Settings.Stream.MissingUrl=\"URL strima nedostaje.\\n\\nOtvorite podešavanja i unesite URL u kartici „strim”.\"\nBasic.Settings.Stream.MissingStreamKey=\"Ključ strima nedostaje.\\n\\nOtvorite podešavanja i unesite ključ strima u kartici „strim”.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorišite preporuke za podešavanja striming servisa\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Ispiši preko preporučenih podešavanja\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Upozorenje: Ignorisanje ograničenja servisa može prouzrokovati smanjenje kvaliteta strima ili Vas može sprečiti da strimujete.\\n\\nProdužite dalje?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maksimalni bitrejt videa: %1 kb/s\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maksimalni bitrejt audija: %1 kb/s\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksimalna rezolucija: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksimalni FPS (slike po sekundi): %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Navedite prilagođeni server...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Prilagođeni server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Omogući %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maksimalni propusni opseg strimovanja\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maksimalan broj video zapisa\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Omogući snimanje strima u FLV (koristi jednostavna podešavanja datoteke za snimanje)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Zamena konfiguracije (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Omogući zamenu konfiguracije\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Višekanalni video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Dodatno platno\"\nBasic.Settings.Stream.AdvancedOptions=\"Napredne opcije\"\nBasic.Settings.Output=\"Izlaz\"\nBasic.Settings.Output.Format=\"Format snimanja\"\nBasic.Settings.Output.Format.MKV=\"Matroska video (.mkv)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmentirani MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmentirani MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmentirani MOV zapisuje snimak u delovima i ne zahteva istu finalizaciju kao tradicionalne MOV datoteke.\\nOvo osigurava da datoteka ostane reprodukovana čak i ako je pisanje na disk prekinuto, na primer, kao rezultat BSOD-a ili nestanka struje.\\n\\nOvo možda nije kompatibilno sa svim plejerima i uređivačima. Koristite Datoteka → Remultifikuj snimke da biste konvertovali datoteku u kompatibilniji format ako je potrebno.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmentirani MP4 zapisuje snimak u delovima i ne zahteva istu finalizaciju kao tradicionalne MP4 datoteke.\\nOvo osigurava da datoteka ostane reprodukovana čak i ako je pisanje na disk prekinuto, na primer, kao rezultat BSOD-a ili nestanka struje.\\n\\nOvo možda nije kompatibilno sa svim plejerima i uređivačima. Koristite Datoteka → Remulks snimci da biste konvertovali datoteku u kompatibilniji format ako je potrebno.\"\nBasic.Settings.Output.Encoder.Video=\"Video enkoder\"\nBasic.Settings.Output.Encoder.Audio=\"Audio enkoder\"\nBasic.Settings.Output.SelectDirectory=\"Odaberi direktorijum za snimanje\"\nBasic.Settings.Output.DynamicBitrate=\"Dinamički menjaj bitrejt u cilju izbegavanja zagušenja\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Dinamički menjaj bitrejt u cilju izbegavanja zagušenja (Beta verzija)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Umesto smanjenja slika u sekundi radi smanjenja zagušenja, dinamički će se menjati bitrejt u hodu.\\n\\nImajte na umu da ovo može povećati vremenski zastoj gledaocima u slučaju naglog i velikog zagušenja.\\nKada bitrejt opadne, možda će mu biti potrebno par minuta da se vrati na staro.\\n\\nTrenutno podržano samo za prenos preko RTMP protokola.\"\nBasic.Settings.Output.Mode=\"Režim izlaza\"\nBasic.Settings.Output.Mode.Simple=\"Jednostavno\"\nBasic.Settings.Output.Mode.Adv=\"Napredno\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg izlaz\"\nBasic.Settings.Output.UseReplayBuffer=\"Uključi bafer ponovnog prikazivanja\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maksimalno vreme sačuvanog snimka\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maksimalna memorija\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Procenjena iskorišćenost memorije: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Upozorenje: Aproksimirana upotreba memorije od %1 megabajta je više nego preporučeni maksimum od %2 megabajta\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Nemoguće je proceniti iskorišćenost memorije. Molimo podesite maksimalnu granicu memorije.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Prefiks imena fajla bafera reprodukovanja\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufiks\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Bafer za sačuvani snimak se ne može koristiti dok je tip snimanja podešen na Prilagođeni izlaz (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Putanja za snimanje\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Kvalitet snimanja\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Isto kao strim\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Visoki kvalitet, osrednja veličina datoteke\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Kvalitet sa neprimetnim razlikama, velika datoteka\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Kvalitet bez gubitka, izričito velika datoteka\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Upozorenje: Bitrejt video strima će biti postavljen na %1, što je gornja granica za trenutno odabrani striming servis.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Upozorenje: Bitrejt zvuka strima će biti postavljen na %1, što je gornja granica za trenutno odabrani striming servis.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Upozorenje: Snimanje se ne može pauzirati ako je kvalitet snimka podešen na „Isti kao strim”.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Upozorenje: Trenutno izabrani format snimanja nije kompatibilan sa izabranim koderom(ima) strima.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Upozorenje: Snimanje sa softverskim enkoderom drugačijeg kvaliteta u odnosu na strim će zahtevati dodatnu procesorsku snagu ako strimujete i snimate u isto vreme.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Upozorenje: Kvalitet bez gubitaka generiše izuzetno velike veličine datoteka! Kvalitet bez gubitaka može da koristi i do 7 gigabajta prostora na disku u minuti pri visokim rezolucijama i broju kadrova u sekundi. Snimanje bez gubitaka se ne preporučuje za duge snimke, osim ako nemate veoma veliku količinu raspoloživog prostora na disku. Bafer za reprodukciju nije dostupan kada se koristi kvalitet bez gubitaka.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Da li ste sigurni da želite koristiti kvalitet bez gubitka?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Upozorenje o kvalitetu bez gubitka!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Softverski (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Hardver (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Hardver (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Hardver (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Hardver (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Hardver (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Hardver (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Hardver (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Hardver (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Hardver (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Hardver (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Softverski (x264 niska upotreba procesora, povećava veličinu datoteke)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (podrazumevano)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD traka (koristi traku 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Audio zapis\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Nekompatibilna rezolucija/broj kadrova\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Ova usluga strimovanja ne podržava vašu trenutnu izlaznu rezoluciju i/ili broj frejmova u sekundi. Biće promenjeni na najbližu kompatibilnu vrednost:\\n\\n%1\\n\\nDa li želite da nastavite?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Rezolucija: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Nekompatibilni enkoder\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Striming servis „%1“ ne podržava enkoder „%2“. Enkoder će biti promenjen u „%3“.\\n\\nDa li želite da nastavite?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Striming servis „%1“ ne podržava enkodere „%2“ i „%3“. Ovi enkoderi će biti promenjeni u „%4“ i „%5“.\\n\\nDa li želite da nastavite?\"\nBasic.Settings.Output.VideoBitrate=\"Protok videa\"\nBasic.Settings.Output.AudioBitrate=\"Protok zvuka\"\nBasic.Settings.Output.Reconnect=\"Automatski poveži ponovo\"\nBasic.Settings.Output.RetryDelay=\"Kašnjenje ponovnog pokušaja\"\nBasic.Settings.Output.MaxRetries=\"Maksimalan broj ponovnih pokušaja\"\nBasic.Settings.Output.Advanced=\"Omogući podešavanja prilagođenog enkodera (napredno)\"\nBasic.Settings.Output.EncoderPreset=\"Predefinisano podešavanje enkodera\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (niska potrošnja procesora, najniži kvalitet)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (podrazumevano) (srednja upotreba procesora, standardni kvalitet)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (velika potrošnja procesora, visok kvalitet)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Prilagođena podešavanja enkodera\"\nBasic.Settings.Output.CustomMuxerSettings=\"Posebna podešavanja muksera\"\nBasic.Settings.Output.NoSpaceFileName=\"Stvori ime datoteke bez razmaka\"\nBasic.Settings.Output.Adv.Rescale=\"Skaliraj izlaz\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Onemogućeno\"\nBasic.Settings.Output.Adv.AudioTrack=\"Zvučni izvor\"\nBasic.Settings.Output.Adv.Streaming=\"Strimovanje\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Podešavanja strimovanja\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Izvor 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Izvor 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Izvor 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Izvor 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Traka 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Traka 6\"\nBasic.Settings.Output.Adv.Encoder=\"Podešavanja enkodera\"\nBasic.Settings.Output.Adv.Recording=\"Snimanje\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Podešavanja snimanja\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Tip snimanja\"\nBasic.Settings.Output.Adv.Recording.Type=\"Vrsta\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Uobičajeni\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Prilagođeni izlaz (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Koristi strim enkoder)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Oblikovanje imena datoteke\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Prepiši ako postoji datoteka\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Prilagođeni izlaz (FFmpeg) je obezbeđen bez ikakvih zaštitnih mera.\\nRazmislite o korišćenju tipa snimanja „Standardno“.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg vrsta ispisa\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Ispis na URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Ispis u datoteku\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Uobičajeni formati snimaka\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Sve datoteke\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Putanja datoteke ili URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format kontejnera\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Zvuk\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Podrazumevani format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Opis formata kontejnera\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Zvučni/Video kodek određen putanjom datoteke ili URL-a\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Podrazumevani enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Onemogući enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Video enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Podešavanja video enkodera (ako postoje)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Zvučni enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Podešavanja zvučnog enkodera (ako postoje)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Podešavanja muxer-a (ako postoje)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Interval ključnih frejmova (frejmovi)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Prikaži sve kodeke (čak iako su potencijalno nekompatibilni)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Podešavanja FFmpeg-a\"\nBasic.Settings.Output.EnableSplitFile=\"Automatsko deljenje datoteka\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Podeli po vremenu\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Podeli po veličini\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Podelite samo ručno\"\nBasic.Settings.Output.SplitFile.Time=\"Vreme podela\"\nBasic.Settings.Output.SplitFile.Size=\"Veličina podele\"\nScreenshot=\"Izlaz ekrana\"\nScreenshot.SourceHotkey=\"Snimak ekrana izabranog izvora\"\nScreenshot.StudioProgram=\"Sačuvaj snimak ekrana programa\"\nScreenshot.Preview=\"Sačuvaj pregled ekrana\"\nScreenshot.Scene=\"Sačuvaj snimak ekrana scene\"\nScreenshot.Source=\"Sačuvaj izvorni snimak ekrana\"\nFilenameFormatting.TT.CCYY=\"Godina, četiri cifre\"\nFilenameFormatting.TT.YY=\"Godina, poslednje dve cifre (00-99)\"\nFilenameFormatting.TT.MM=\"Mesec kao decimalni broj (01-12)\"\nFilenameFormatting.TT.DD=\"Dan u mesecu, dopunjen nulama (01-31)\"\nFilenameFormatting.TT.hh=\"Sat u 24-časovnom formatu (00-23)\"\nFilenameFormatting.TT.mm=\"Minut (00-59)\"\nFilenameFormatting.TT.ss=\"Drugi (00-59)\"\nFilenameFormatting.TT.Percent=\"Znak % s\"\nFilenameFormatting.TT.a=\"Skraćeni naziv dana u nedelji\"\nFilenameFormatting.TT.A=\"Pun naziv dana u nedelji\"\nFilenameFormatting.TT.b=\"Skraćeni naziv meseca\"\nFilenameFormatting.TT.B=\"Puno ime meseca\"\nFilenameFormatting.TT.d=\"Dan u mesecu, dopunjen nulama (01-31)\"\nFilenameFormatting.TT.H=\"Sat u 24-časovnom formatu (00-23)\"\nFilenameFormatting.TT.I=\"Sat u 12-časovnom formatu (01-12)\"\nFilenameFormatting.TT.m=\"Mesec kao decimalni broj (01-12)\"\nFilenameFormatting.TT.M=\"Minut (00-59)\"\nFilenameFormatting.TT.p=\"Oznaka AM ili PM\"\nFilenameFormatting.TT.s=\"Vreme u sekundama od UNIX epohe\"\nFilenameFormatting.TT.S=\"Drugi (00-59)\"\nFilenameFormatting.TT.y=\"Godina, poslednje dve cifre (00-99)\"\nFilenameFormatting.TT.Y=\"Godina\"\nFilenameFormatting.TT.z=\"ISO 8601 pomeranje u odnosu na UTC u vremenskoj zoni\"\nFilenameFormatting.TT.Z=\"Naziv ili skraćenica vremenske zone\"\nFilenameFormatting.TT.FPS=\"Kadrova u sekundi\"\nFilenameFormatting.TT.CRES=\"Osnovna (platna) rezolucija\"\nFilenameFormatting.TT.ORES=\"Izlazna (skalirana) rezolucija\"\nBasic.Settings.Video.Adapter=\"Video adapter\"\nBasic.Settings.Video.BaseResolution=\"Osnovna (površinska) rezolucija\"\nBasic.Settings.Video.ScaledResolution=\"Izlazna (skalirana) rezolucija\"\nBasic.Settings.Video.DownscaleFilter=\"Filter za skaliranje\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Rezolucije se poklapaju, nije potrebno smanjivanje]\"\nBasic.Settings.Video.FPSCommon=\"Uobičajene FPS vrednosti\"\nBasic.Settings.Video.FPSInteger=\"Brojevna FPS vrednost\"\nBasic.Settings.Video.FPSFraction=\"Brojevna FPS vrednost sa decimalama\"\nBasic.Settings.Video.Numerator=\"Brojilac\"\nBasic.Settings.Video.Denominator=\"Imenilac\"\nBasic.Settings.Video.InvalidResolution=\"Neispravna vrednost rezolucije. Mora biti [width]x[height] (npr. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Video izlaz je trenutno aktivan. Molim isključite sve izlaze da promenite video podešavanja.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (najbrže, ali mutno pri skaliranju)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (oštrije skaliranje, 16 uzoraka)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (oštrije skaliranje, 36 uzorka)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Površina (ponderisana suma, 4/6/9 uzoraka)\"\nBasic.Settings.Audio=\"Zvuk\"\nBasic.Settings.Audio.SampleRate=\"Protok\"\nBasic.Settings.Audio.Channels=\"Kanali\"\nBasic.Settings.Audio.Meters=\"Metara\"\nBasic.Settings.Audio.MeterDecayRate=\"Stopa propadanja\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Brza\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Srednja (Tip I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Spora (Tip II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Tip merača vrha\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Vrh uzorka\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Pravi vrh (Veća upotreba procesora)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"UPOZORENJE: Zvuk okruženja je uključen.\"\nBasic.Settings.Audio.MultichannelWarning=\"Ako strimujete, proverite da li vaš servis za strimovanje podržava i unos i reprodukciju okružujućeg zvuka. Na primer, Facebook 360 Live u potpunosti podržava okružujući zvuk; YouTube Live podržava unos 5.1 zvuka (i reprodukciju na televizorima).\\n\\nOBS audio filteri su kompatibilni sa okružujućim zvukom, iako podrška za VST dodatke nije garantovana.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Da li želite da uključite zvuk okruženja?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Da li ste sigurni da želite da uključite zvuk okruženja?\"\nBasic.Settings.Audio.Devices=\"Globalni audio uređaji\"\nBasic.Settings.Audio.DesktopDevice=\"Zvuk sa računara\"\nBasic.Settings.Audio.DesktopDevice2=\"Zvuk sa računara 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/Ulaz\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/Ulaz 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/Ulaz 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/Ulaz 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Omogući stisni-za-mutiranje\"\nBasic.Settings.Audio.PushToMuteDelay=\"Kašnjenje opcije stisni-za-mutiranje\"\nBasic.Settings.Audio.EnablePushToTalk=\"Omogući Stisni-za-govor\"\nBasic.Settings.Audio.PushToTalkDelay=\"Kašnjenje opcije stisni-za-govor\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Uređaj nije povezan ili nije dostupan]\"\nBasic.Settings.Audio.Disabled=\"Onemogućeno\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Režim baferovanja zvuka sa malom latencijom (za Decklink/NDI izlaze)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"UPOZORENjE: Omogućeno je baferovanje zvuka sa malom latencijom.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Režim baferovanja zvuka sa malom latencijom može prouzrokovati greške u reprodukciji zvuka ili prekid reprodukcije iz nekih izvora.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Omogućiti režim baferovanja zvuka sa malom latencijom?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Jeste li sigurni da želite da omogućite režim audio baferovanja sa malim kašnjenjem?\"\nBasic.Settings.Accessibility=\"Pristupačnost\"\nBasic.Settings.Accessibility.ColorOverrides=\"Koristite različite boje\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Okvir oko izvora (Izbor)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Okvir oko izvora (Iseci)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Okvir oko izvora (Prelaz mišem)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Jačina zvuka (od -60 do -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Jačina zvuka (od -20 do -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Jačina zvuka (od -9 do 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Jačina zvuka (od -60 do -20dB) (Aktivno)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Jačina zvuka (od -20 do -9dB) (Aktivno)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Jačina zvuka (od -9 do 0dB) (Aktivno)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Paleta boja\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Podrazumevana\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Prilagođena\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternativa za daltoniste\"\nBasic.Settings.Advanced=\"Napredno\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Prikaži upozorenje o aktivnim izlazima pri izlasku\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prioritet procesa\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Visoki\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Iznad normalnog\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normalni\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Niži od normalnog\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Besposleni\"\nBasic.Settings.Advanced.FormatWarning=\"Upozorenje: Formati boja, sa izuzetkom NV12/P010 su prvenstveno predviđeni za snimanje, i nisu preporučljivi prilikom strimovanja. Strimovanje može prouzrokovati povećanu upotrebu procesorskih resursa zbog konverzije formata boja.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Upozorenje: Visokoprecizni formati se češće koriste sa HDR prostorima boja.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Upozorenje: Rec. 2100 bi trebalo da koristi format sa većom preciznošću.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Format boja\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (osmobitni, 4:2:0, 2 ravni)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (osmobitni, 4:2:0, 3 ravni)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (osmobitni, 4:4:4, 3 ravni)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (desetobitni, 4:2:0, 2 ravni)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (desetobitni, 4:2:0, 3 ravni)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (šesnaestobitni, 4:2:2, 2 ravni)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (šesnaestobitni, 4:4:4, 2 ravni)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (osmobitni)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Raspon boja\"\nBasic.Settings.Advanced.Video.ColorRange=\"Opseg boja\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Ograničeno\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Potpuno\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR nivo beline\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nominalni vršni nivo HDR-a\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Uređaj za nadzor\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Podrazumevano\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Isključi Windows audio ducking\"\nBasic.Settings.Advanced.StreamDelay=\"Kašnjenje strimovanja\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Trajanje\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Sačuvaj tačku prekida (povećaj kašnjenje) prilikom ponovnog povezivanja\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Procenjena upotreba memorije: %1 MB\"\nBasic.Settings.Advanced.Network=\"Mreža\"\nBasic.Settings.Advanced.Network.Disabled=\"Trenutno odabrani striming protokol ne podržava menjanje mrežnih podešavanja.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Veži se za IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP porodica\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Omogući mrežne optimizacije\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Omogući TCP pejsing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Pokušaj da se RTMP izlaz učini kompatibilnijim sa drugim aplikacijama osetljivim na kašnjenje, kroz regulaciju stope prenosa.\\nMože povećati rizik od ispuštenih slika i dovesti do „seckanja” prilikom striminga na nestabilnim konekcijama.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Ponašanje prečica tastature\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Nikad ne isključuj prečice tastature\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Isključi prečice kada je glavni prozor u fokusu\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Isključi prečice tastature kada prozor nije u fokusu\"\nBasic.Settings.Advanced.AutoRemux=\"Automatski remuks u %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(snimi kao mkv)\"\nBasic.AdvAudio=\"Napredna podešavanja zvuka\"\nBasic.AdvAudio.ActiveOnly=\"Samo aktivni izvori\"\nBasic.AdvAudio.Name=\"Ime\"\nBasic.AdvAudio.Volume=\"Jačina zvuka\"\nBasic.AdvAudio.VolumeSource=\"Jačina zvuka za „%1”\"\nBasic.AdvAudio.MonoSource=\"Mono daunmiks za '%1'\"\nBasic.AdvAudio.Balance=\"Balans\"\nBasic.AdvAudio.BalanceSource=\"Stanje za '%1'\"\nBasic.AdvAudio.SyncOffset=\"Razlika u sinhronizaciji\"\nBasic.AdvAudio.SyncOffsetSource=\"Razlika u sinhronizaciji za „%1”\"\nBasic.AdvAudio.Monitoring=\"Audio nadzor\"\nBasic.AdvAudio.Monitoring.None=\"Isključi nadzor\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Samo nadzor (izlaz bez zvuka)\"\nBasic.AdvAudio.Monitoring.Both=\"Nadzor i izlaz\"\nBasic.AdvAudio.MonitoringSource=\"Audio nadzor za „%1”\"\nBasic.AdvAudio.AudioTracks=\"Izvori\"\nBasic.Settings.Hotkeys=\"Prečice\"\nBasic.Settings.Hotkeys.Pair=\"Kombinacije tastera deljene sa '%1' se ponašaju kao prekidači\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtriraj prema prečici\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Ova prečica se deli sa jednom ili više drugih radnji, kliknite za prikaz sukobljenih radnji\"\nBasic.Settings.Hotkeys.PleaseWait=\"Učitavanje prečica tastature, molimo sačekajte...\"\nBasic.Hotkeys.SelectScene=\"Prebaci na scenu\"\nBasic.SystemTray.Show=\"Prikaži\"\nBasic.SystemTray.Hide=\"Sakrij\"\nBasic.SystemTray.Message.Reconnecting=\"Veza prekinuta. Ponovno uspostavljanje...\"\nHotkeys.Delete=\"Obriši\"\nHotkeys.Home=\"Početna\"\nHotkeys.PageUp=\"Stranica nagore\"\nHotkeys.PageDown=\"Stranica nadole\"\nHotkeys.NumLock=\"Num Lok\"\nHotkeys.ScrollLock=\"Zaključavanje pomeranja\"\nHotkeys.Backspace=\"Brisanje\"\nHotkeys.Tab=\"Kartica\"\nHotkeys.Print=\"Štampaj\"\nHotkeys.Pause=\"Pauza\"\nHotkeys.Left=\"Strelica levo\"\nHotkeys.Right=\"Strelica desno\"\nHotkeys.Up=\"Strelica gore\"\nHotkeys.Down=\"Strelica dole\"\nHotkeys.Windows=\"Prozori\"\nHotkeys.Menu=\"Meni\"\nHotkeys.Space=\"Razmak\"\nHotkeys.NumpadNum=\"Numerički %1\"\nHotkeys.NumpadMultiply=\"Numerička zvezdica\"\nHotkeys.NumpadDivide=\"Numerička kosa crta\"\nHotkeys.NumpadAdd=\"Numerički plus\"\nHotkeys.NumpadSubtract=\"Numerički minus\"\nHotkeys.NumpadDecimal=\"Numerička tačka\"\nHotkeys.AppleKeypadNum=\"%1 (Tastatura)\"\nHotkeys.AppleKeypadMultiply=\"* (Tastatura)\"\nHotkeys.AppleKeypadDivide=\"/ (Tastatura)\"\nHotkeys.AppleKeypadAdd=\"+ (Tastatura)\"\nHotkeys.AppleKeypadSubtract=\"- (Tastatura)\"\nHotkeys.AppleKeypadDecimal=\". (Tastatura)\"\nHotkeys.AppleKeypadEqual=\"= (Tastatura)\"\nHotkeys.MouseButton=\"Miš %1\"\nHotkeys.Escape=\"Izlaz\"\nMute=\"Mutiraj\"\nUnmute=\"Odmutiraj\"\nPush-to-mute=\"Stisni-za-mutiranje\"\nPush-to-talk=\"Stisni-za-govor\"\nSceneItemShow=\"Prikaži '%1'\"\nSceneItemHide=\"Sakrij '%1'\"\nOutputWarnings.NoTracksSelected=\"Morate odabrati makar jednu traku\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Greška u podešavanjima izlaza\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Svi izlazi moraju imati izabranu bar jednu audio numeru.\"\nOutputWarnings.MP4Recording=\"Upozorenje: Snimci sačuvani u MP4/MOV formatu neće biti moguće sačuvati ako fajl ne bude mogao da se finalizuje (npr. usled BSOD kvara, nestanka struje, itd.). Ako želite da snimite više audio zapisa razmislite o korišćenju MKV-a i remuksujte snimak u MP4/MOV kada snimak bude bio gotov (Fajl → Remuksovanje snimaka)\"\nOutputWarnings.CannotPause=\"Upozorenje: Snimci ne mogu da se pauziraju ako je koder snimka podešen na „(Koristi enkoder strima)”\"\nOutputWarnings.CodecIncompatible=\"Izbor audio ili video kodera je resetovan zbog nekompatibilnosti. Izaberite kompatibilni koder sa liste.\"\nCodecCompat.Incompatible=\"(Nekompatibilno sa %1)\"\nCodecCompat.CodecPlaceholder=\"Izaberite enkoder...\"\nCodecCompat.ContainerPlaceholder=\"Izaberite format...\"\nCodecCompat.CodecMissingOnExit.Title=\"Nije izabran enkoder\"\nCodecCompat.CodecMissingOnExit.Text=\"Barem jedan video ili audio koder nije podešen. Molimo vas da izaberete kodere i za snimanje i za strimovanje.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Nije izabran format\"\nCodecCompat.ContainerMissingOnExit.Text=\"Nije izabran format snimanja. Izaberite format snimanja koji je kompatibilan sa izabranim koderom strima.\"\nFinalScene.Title=\"Obriši scenu\"\nFinalScene.Text=\"Mora da postoji barem jedna scena, kako bi bila obrisana.\"\nNoSources.Title=\"Nema izvora\"\nNoSources.Text=\"Izgleda da još uvek niste dodali ni jedan video izvor, tako da će se prikazivati samo prazan ekran. Da li ste sigurni da želite da nastavite?\"\nNoSources.Text.AddSource=\"Možete da dodate izvore u bilo kom trenutku, klikom na ikonicu ''+'' ispod dugmeta ''Izvori'' u glavnom prozoru.\"\nNoSources.Label=\"Nemate nijedan izvor.\\nKliknite dugme + ispod,\\nili dodajte izvor desnim klikom ovde.\"\nChangeBG=\"Podesi boju\"\nCustomColor=\"Personalizovana boja\"\nBrowserSource.EnableHardwareAcceleration=\"Uključi izvor pretraživača hardverskog ubrzanja\"\nAbout=\"O programu\"\nAbout.Info=\"OBS Studio je besplatan softver otvorenog koda koji omogućava video snimanje i strimovanje uživo.\"\nAbout.Donate=\"Dajte doprinos\"\nAbout.GetInvolved=\"Učestvujte\"\nAbout.Authors=\"Autori\"\nAbout.License=\"Licenca\"\nAbout.Error=\"Greška! Datoteka nije mogla biti pročitana.\\n\\nIdi na: %1\"\nAbout.Contribute=\"Podržite OBS projekat\"\nAddUrl.Title=\"Dodajte izvor putem URL-a\"\nAddUrl.Text=\"Prevukli ste URL u OBS. Ovo će automatski dodati taj link kao izvor. Nastavite?\"\nResizeOutputSizeOfSource=\"Promeni veličinu izlaza (veličina izvora)\"\nResizeOutputSizeOfSource.Text=\"Osnovna rezolucija i rezolucija izlaza će biti promenjene kako bi odgovarale veličini trenutnog izvora.\"\nResizeOutputSizeOfSource.Continue=\"Želite li da nastavite?\"\nPreviewTransition=\"Pregledaj prelaz\"\nImporter=\"Uvoznik kolekcija scena\"\nImporter.SelectCollection=\"Izaberite kolekciju scena\"\nImporter.Collection=\"Kolekcija scena\"\nImporter.HelpText=\"Dodajte fajlove u ovaj prozor da biste uvezli kolekcije iz OBS-a ili drugih podržanih programa.\"\nImporter.Path=\"Putanja kolekcije\"\nImporter.Program=\"Detektovana aplikacija\"\nImporter.AutomaticCollectionPrompt=\"Automatski potraži kolekcije scena\"\nImporter.AutomaticCollectionText=\"OBS može sam da pronađe uvozive kolekcije scena iz podržanih programa treće strane. Želite li da OBS sam potraži kolekcije za Vas?\\n\\nOvo podešavanje možete promeniti kasnije odlaskom na Podešavanja > Opšte > Uvoznici.\"\nImporter.SelectFile=\"Pregledaj...\"\nRestart=\"Ponovo pokreni\"\nNeedsRestart=\"OBS Studio treba ponovo pokrenuti. Da li želite da ga ponovo pokrenete sada?\"\nLoadProfileNeedsRestart=\"Profil sadrži podešavanja koja zahtevaju restartovanje OBS-a:\\n%1\\n\\nŽelite li da restartujete OBS da bi ova podešavanja stupile na snagu?\"\nContextBar.NoSelectedSource=\"Nije izabran izvor\"\nContextBar.MediaControls.PlayMedia=\"Pusti mediju\"\nContextBar.MediaControls.PauseMedia=\"Pauziraj medije\"\nContextBar.MediaControls.StopMedia=\"Zaustavi medije\"\nContextBar.MediaControls.RestartMedia=\"Počni multimediju ponovo\"\nContextBar.MediaControls.PlaylistNext=\"Sledeće na plejlisti\"\nContextBar.MediaControls.PlaylistPrevious=\"Prethodno na plejlisti\"\nContextBar.MediaControls.BlindSeek=\"Vidžet za traženje multimedije\"\nYouTube.Auth.Ok=\"Autorizacija je uspešno obavljena.\\nSada možete da zatvorite ovu stranicu.\"\nYouTube.Auth.NoCode=\"Proces autorizacije nije obavljen.\"\nYouTube.Auth.NoChannels=\"Nijedan kanal nije dostupan na odabranom nalogu\"\nYouTube.Auth.WaitingAuth.Title=\"Autorizacija Jutjub korisnika\"\nYouTube.Auth.WaitingAuth.Text=\"Molimo Vas obavite autorizaciju u eksternom pretraživaču.<br>Ukoliko se pretraživač ne otvori, pratite ovaj link da izvršite autorizaciju:<br>%1\"\nYouTube.AuthError.Text=\"Neuspešan pokušaj prikupljanja informacija o kanalu. %1.\"\nYouTube.Actions.WindowTitle=\"Postavke Jutjub prenosa - Kanal: %1\"\nYouTube.Actions.CreateNewEvent=\"Napravi novi prenos\"\nYouTube.Actions.ChooseEvent=\"Izaberi postojeći prenos\"\nYouTube.Actions.Title=\"Naziv\"\nYouTube.Actions.MyBroadcast=\"Moj prenos\"\nYouTube.Actions.Description=\"Opis\"\nYouTube.Actions.Privacy=\"Privatnost\"\nYouTube.Actions.Privacy.Private=\"Privatno\"\nYouTube.Actions.Privacy.Public=\"Javno\"\nYouTube.Actions.Privacy.Unlisted=\"Neizlistano\"\nYouTube.Actions.Category=\"Kategorija\"\nYouTube.Actions.Thumbnail=\"Sličica\"\nYouTube.Actions.Thumbnail.SelectFile=\"Izaberite datoteku...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Nijedan fajl nije izabran\"\nYouTube.Actions.Thumbnail.ClearFile=\"Očisti\"\nYouTube.Actions.MadeForKids=\"Da li je ovaj video napravljen za decu?*\"\nYouTube.Actions.MadeForKids.Yes=\"Da, napravljeno je za decu\"\nYouTube.Actions.MadeForKids.No=\"Ne, nije napravljeno za decu\"\nYouTube.Actions.AdditionalSettings=\"Dodatna podešavanja\"\nYouTube.Actions.Latency=\"Odziv\"\nYouTube.Actions.Latency.Normal=\"Normalno\"\nYouTube.Actions.Latency.Low=\"Nisko\"\nYouTube.Actions.Latency.UltraLow=\"Ultra nisko\"\nYouTube.Actions.EnableAutoStart=\"Omogući automatsko pokretanje\"\nYouTube.Actions.EnableAutoStop=\"Omogući automatsko zaustavljanje\"\nYouTube.Actions.AutoStartStop.TT=\"Ukazuje na to da li ovaj zakazani prenos treba da počne automatski\"\nYouTube.Actions.EnableDVR=\"Omogući DVR\"\nYouTube.Actions.ScheduleForLater=\"Zakaži za kasnije\"\nYouTube.Actions.RememberSettings=\"Zapamti ova podešavanja\"\nYouTube.Actions.Create_Ready=\"Napravi prenos\"\nYouTube.Actions.Create_GoLive=\"Napravi prenos i započni strim\"\nYouTube.Actions.Choose_Ready=\"Izaberi prenos\"\nYouTube.Actions.Choose_GoLive=\"Izaberi prenos i započni strim\"\nYouTube.Actions.Create_Schedule=\"Zakaži prenos\"\nYouTube.Actions.Create_Schedule_Ready=\"Zakaži i izaberi prenos\"\nYouTube.Actions.Dashboard=\"Otvori YouTube studio\"\nYouTube.Actions.Error.Title=\"Greška prilikom pravljenja prenosa uživo\"\nYouTube.Actions.Error.Text=\"Greška prilikom pristupa Jutjubu „%1”.<br/>Detaljan opis greške možete pogledati na <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Greška prilikom pristupa Jutjubu. Molimo Vas proverite mrežnu vezu ili da li imate pristup Jutjub serveru.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Greška prilikom pravljenja prenosa „%1”.<br/>Detaljan opis greške možete pogledati na <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Nijedan strim nije napravljen. Molimo Vas ponovo povežite svoj nalog.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API greška. Molimo Vas pogledajte fajl izveštaja za više informacija.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Izabrani prenos nije pronađen.\"\nYouTube.Actions.Error.FileMissing=\"Izabrana datoteka ne postoji.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Neuspešno otvaranje izabranog fajla.\"\nYouTube.Actions.Error.FileTooLarge=\"Izabrani fajl je previše veliki (Ograničenje: 2 MiB)\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Premeštanje prenosa nije uspelo: %1<br/><br/>Ukoliko se ova greška ponovi <a href='https://studio.youtube.com/video/%2/livestreaming'>otvorite prenos u Jutjub studiju</a> i pokušajte ručno.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Prenos se prebacuje u test-fazu, ovo može potrajati neko vreme. Molimo Vas pokušajte ponovo za 10-30 sekundi.\"\nYouTube.Actions.EventsLoading=\"Učitavanje liste događaja...\"\nYouTube.Actions.EventCreated.Title=\"Događaj napravljen\"\nYouTube.Actions.EventCreated.Text=\"Događaj je uspešno napravljen.\"\nYouTube.Actions.Stream=\"Prenos\"\nYouTube.Actions.Stream.ScheduledFor=\"Zakazano za %1\"\nYouTube.Actions.Stream.Resume=\"Nastavi prekinuti strim\"\nYouTube.Actions.Stream.YTStudio=\"Automatski napravljeno od strane Jutjub studija\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Pravljenje novog prenosa uživo, molimo Vas sačekajte...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Zahteva ručno pokretanje\"\nYouTube.Actions.AutoStartStreamingWarning=\"Automatski početak je onemogućen za ovaj događaj, kliknite \\\"Go live\\\" da biste započeli svoj prenos.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Nećete moći da se ponovo povežete.<br>Vaš strim će se zaustaviti i više nećete biti uživo.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Striming uživo nije omogućen na odabranom Jutjub kanalu.<br/><br/>Pogledajte <a href='https://www.youtube.com/features'>youtube.com/features</a> za više informacija.\"\nYouTube.Errors.livePermissionBlocked=\"Striming uživo nije omogućen na odabranom Jutjub kanalu.<br/>Molimo Vas imajte na umu da može biti potrebno i do 24 sata da striming uživo postane dostupan nakon što ste ga omogućili u podešavanjima kanala.<br/><br/>Pogledajte <a href='https://www.youtube.com/features'>youtube.com/features</a> za detalje.\"\nYouTube.Errors.errorExecutingTransition=\"Prebacivanje nije uspelo zbog greške na serveru. Molimo Vas pokušajte ponovo za par sekundi.\"\nYouTube.Errors.errorStreamInactive=\"Jutjub ne prima podatke za Vaš strim. Molimo Vas proverite svoju konfiguraciju i pokušajte ponovo.\"\nYouTube.Errors.invalidTransition=\"Pokušano prebacivanje nije bilo validno. Ovo može biti zbog toga što strim nije završio prethodno prebacivanje. Molimo Vas sačekajte par sekundi pa pokušajte ponovo.\"\nYouTube.DocksRemoval.Title=\"Obriši zastarele dokove YouTube pregledača\"\nYouTube.DocksRemoval.Text=\"Ovi dokovi pregledača biće uklonjeni kao zastareli:\\n\\n%1\\nUmesto toga koristite „Dokovi/Kontrolna soba uživo na YouTube-u“.\"\nConfigDownload.WarningMessageTitle=\"Upozorenje\"\nFailedToStartStream.MissingConfigURL=\"Nema dostupnih URL adresa za konfiguraciju za trenutnu uslugu\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Prilagođeni RTMP URL nije naveden\"\nFailedToStartStream.InvalidCustomConfig=\"Nevažeća prilagođena konfiguracija\"\nFailedToStartStream.MissingCanvas=\"Nedostaje konfigurisano dodatno platno\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Nije uspelo kreiranje višekanalne video usluge\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Nije uspelo kreiranje RTMP izlaza za višekanalni video\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC nije dostupan.\\n\\nNije pronađen tip enkodera '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Nije uspelo kreiranje video kodera '%1' (tip: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Nije uspelo preuzimanje informacija o OBS videu prilikom kreiranja kodera '%1' (tip: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Nije uspelo kreiranje audio kodera\"\nFailedToStartStream.NoRTMPURLInConfig=\"Konfiguracija ne sadrži RTMP(S) URL cilja strima\"\nFailedToStartStream.FallbackToDefault=\"Pokretanje strima pomoću %1 failed; da li želite da pokušate ponovo koristeći podešavanja jednog kodiranja?\"\nFailedToStartStream.ConfigRequestFailed=\"Nije moguće preuzeti konfiguraciju iz %1<br><br> HTTP greška: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Primljena nepoznata vrednost statusa '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br> \\nDa li želite da nastavite strimovanje bez %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br> Da li želite da nastavite sa strimovanjem?\"\nFailedToStartStream.MissingEncoderConfigs=\"Konfiguracija za objavljivanje nije uključivala konfiguracije enkodera\"\nFailedToStartStream.InvalidEncoderConfig=\"Konfiguracija za objavljivanje je sadržala nevažeću konfiguraciju enkodera\"\nFailedToStartStream.StatusMissingHTML=\"Zahtev za objavljivanje je vratio neodređenu grešku\"\nFailedToStartStream.NoConfigSupplied=\"Nedostaje konfiguracija\"\nMultitrackVideo.Info=\"%1 automatski optimizuje vaša podešavanja za kodiranje i slanje više kvaliteta videa. Izborom ove opcije poslaćete %2 informacije o vašem računaru i podešavanjima softvera.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Nekompatibilna podešavanja\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 trenutno nije kompatibilan sa:\\n\\n%2\\nDa biste nastavili strimovanje sa %1, onemogućite nekompatibilna podešavanja:\\n\\n%3\\ni ponovo počnite strimovanje.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Onemogući za ovaj strim i počni strimovanje\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Ažurirajte podešavanja i počnite strimovanje\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 trenutno nije kompatibilan sa [Audio → Opšte → Kanali] podešenim na '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Audio → Opšte → Kanali] treba da bude podešeno na '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 zahteva više različitih podešavanja za [Audio → Opšte → Kanali]\"\n"
  },
  {
    "path": "frontend/data/locale/sr-SP.ini",
    "content": "Language=\"српски\"\nOK=\"У реду\"\nApply=\"Примени\"\nCancel=\"Откажи\"\nClose=\"Затвори\"\nSave=\"Сачувај\"\nDiscard=\"Одбаци\"\nDisable=\"Онемогући\"\nYes=\"Да\"\nNo=\"Не\"\nAdd=\"Додај\"\nRemove=\"Уклони\"\nRename=\"Преименуј\"\nInteract=\"Врши интеракцију\"\nFilters=\"Филтери\"\nProperties=\"Својства\"\nMoveUp=\"Премести нагоре\"\nMoveDown=\"Премести надоле\"\nSettings=\"Подешавања\"\nDisplay=\"Екран\"\nName=\"Име\"\nExit=\"Изађи\"\nMixer=\"Аудио Миксер\"\nBrowse=\"Прегледај\"\nMono=\"Моно\"\nStereo=\"Стерео\"\nDroppedFrames=\"Испуштени фрејмови %1 (%2%)\"\nProjector.Open.Program=\"Отвори програмски пројектор\"\nProjector.Open.Preview=\"Отвори преглед пројектора\"\nProjector.Open.Scene=\"Пројектор отворене сцене\"\nProjector.Open.Source=\"Пројектор отвореног кода\"\nProjector.Open.Multiview=\"Отвори вишеструки приказ\"\nProjector.Display=\"Приказ: %1\"\nProjector.Window=\"Нови прозор\"\nProjector.Title=\"Пројектор\"\nProjector.Title.Scene=\"Сцена: %1\"\nProjector.Title.Source=\"Извор: %1\"\nProjector.Title.Multiview=\"Вишеструки приказ\"\nProjector.ResizeWindowToContent=\"Прилагоди прозор садржају\"\nClear=\"Обриши\"\nRevert=\"Врати на стара подешавања\"\nShow=\"Прикажи\"\nHide=\"Сакриј\"\nUnhideAll=\"Откриј све\"\nUntitled=\"Ненасловљено\"\nNew=\"Нови\"\nDuplicate=\"Дуплирај\"\nEnable=\"Омогући\"\nDisableOSXVSync=\"Онемогући macOS вертикалну синхронизацију\"\nResetOSXVSyncOnExit=\"Поврати macOS вертикалну синхронизацију по изласку\"\nHighResourceUsage=\"Енкодирање преоптерећено! Размотрите снижавање видео подешавања ли коришћење бржег шаблона за енкодирање.\"\nTransition=\"Прелаз\"\nQuickTransitions=\"Брзи прелази\"\nFadeToBlack=\"Прелазак у црно\"\nLeft=\"Слева\"\nRight=\"Здесна\"\nTop=\"Одозго\"\nBottom=\"Одоздо\"\nReset=\"Поништи\"\nHours=\"Сати\"\nMinutes=\"Минута\"\nSeconds=\"Секунди\"\nDeprecated=\"Превазиђено\"\nReplayBuffer=\"Бафер за поновљени приказ\"\nImport=\"Увези\"\nExport=\"Извези\"\nCopy=\"Копирај\"\nPaste=\"Налепи\"\nPasteReference=\"Налепи (референцу)\"\nPasteDuplicate=\"Налепи (дупликат)\"\nRemuxRecordings=\"Снимање у слабијем квалитету (Remux)\"\nNext=\"Следеће\"\nBack=\"Назад\"\nDefaults=\"Подразумеване вредности\"\nRestoreDefaults=\"Подразумеване вредности\"\nHideMixer=\"Сакриј у миксер панел\"\nTransitionOverride=\"Тип транзиције\"\nShowTransition=\"Прикажи прелаз\"\nHideTransition=\"Сакриј прелаз\"\nNone=\"Без\"\nStudioMode.Preview=\"Прегледај\"\nStudioMode.Program=\"Програм\"\nStudioMode.PreviewSceneName=\"Преглед: %1\"\nStudioMode.ProgramSceneName=\"Програм: %1\"\nShowInMultiview=\"Прикажи као симултани преглед\"\nVerticalLayout=\"Вертикални распоред\"\nGroup=\"Група\"\nDoNotShowAgain=\"Не приказуј поново\"\nDefault=\"(Подразумевано)\"\nCalculating=\"Израчунавање...\"\nFullscreen=\"На целом екрану\"\nWindowed=\"У прозору\"\nRefreshBrowser=\"Освежи\"\nAspectRatio=\"Размера <b>%1:%2</b>\"\nLockVolume=\"Закључај јачину звука\"\nLogViewer=\"Прегледник дневника\"\nShowOnStartup=\"Прикажи при покретању\"\nOpenFile=\"Отвори датотеку\"\nAddScene=\"Додај сцену\"\nAddSource=\"Додај извор\"\nRemoveScene=\"Уклони одабрану сцену\"\nRemoveSource=\"Уклони одабран(е) избор(е)\"\nMoveSceneUp=\"Подигни сцену\"\nMoveSceneDown=\"Спусти сцену\"\nMoveSourceUp=\"Подигни извор(е)\"\nMoveSourceDown=\"Спусти извор(е)\"\nSourceProperties=\"Отвори детаље извора\"\nSourceFilters=\"Отвори филтере извора\"\nMixerToolbarMenu=\"Мени за aудио миксовање\"\nSceneFilters=\"Отвори сценске филтере\"\nList=\"Списак\"\nGrid=\"Мрежни приказ\"\nAutomatic=\"Аутоматски\"\nComingSoon=\"Ускоро стиже\"\nPluginsFailedToLoad.Title=\"Грешка приликом учитавања додатне компоненте\"\nPluginsFailedToLoad.Text=\"Следеће OBS додатне компоненте нису успеле да се учитају:\\n\\n%1\\nМолимо Вас ажурирајте их или их уклоните.\"\nAlreadyRunning.Title=\"OBS је већ покренут\"\nAlreadyRunning.Text=\"OBS je већ покренут! Осим ако заиста желите да урадите ово, молимо Вас да прекинете све процесе везане за OBS пре него што покренете нов задатак. У случају да OBS ради у позадини, молимо вас да проверите system tray (иконице доле десно, поред сата).\"\nAlreadyRunning.LaunchAnyway=\"Свеједно покрени\"\nCrashHandling.Buttons.LaunchSafe=\"Покрени у безбедном режиму\"\nCrashHandling.Buttons.LaunchNormal=\"Покрени у Нормалном Режиму\"\nSafeMode.Restart=\"Да ли желите да поново покренете ОБС у безбедном режиму (додаци треће стране, скрипте и ВебСоцкетс су ононемогућени)?\"\nSafeMode.RestartNormal=\"Да ли желите да поново покренете ОБС у нормалном режиму?\"\nChromeOS.Title=\"Неподржани формат\"\nChromeOS.Text=\"OBS је наизглед покренут унутар ChromeOS контејнера. Ова платформа није подржана.\"\nWine.Title=\"Wine детектован\"\nWine.Text=\"Покретање OBS-a у „Вајну” није подржано, и многе функције попут снимања или извора уређаја неће радити или ће радити у ограниченом капацитету.<br><br>Препоручује се да уместо тога инсталирате нативну верзију OBS-a, на пример <a href='https://flathub.org/apps/details/com.obsproject.Studio'>нашу Flatpak верзију</a> или пакет за Ваш оперативни систем.\"\nDockCloseWarning.Title=\"Затварање усидривог прозора\"\nDockCloseWarning.Text=\"Управо сте затворили усидрив прозор. Ако желите да га поново отворите, можете то учинити помоћу менија „Докови” на траци са менијима.\"\nExtraBrowsers=\"Прилагођене станице за прегледач\"\nExtraBrowsers.Info=\"Додајте прикључне станице тако што ћете им дати име и УРЛ, а затим кликните на Примени или Затвори да бисте отворили прикључне станице. Можете додати или уклонити докове у било ком тренутку.\"\nExtraBrowsers.DockName=\"Име дока\"\nAuth.Authing.Title=\"Аутентификација...\"\nAuth.Authing.Text=\"Потврда идентитета помоћу %1-а, сачекајте…\"\nAuth.AuthFailure.Title=\"Грешка при потврди идентитета\"\nAuth.AuthFailure.Text=\"Потврда идентитета помоћу %1-а није успела:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Потребна је потврда идентитета\"\nAuth.InvalidScope.Text=\"Захтеви за аутентификацију помоћу %1 су промењени. Неки додаци можда неће бити доступни.\"\nAuth.LoadingChannel.Title=\"Учитавање информација о каналу...\"\nAuth.LoadingChannel.Text=\"Учитавање информација о каналу за %1, молим сачекајте...\"\nAuth.LoadingChannel.Error=\"Није могуће добити информације о каналу.\"\nAuth.ChannelFailure.Title=\"Није могуће учитати канал\"\nAuth.ChannelFailure.Text=\"Није могуће учитати информације о каналу за %1\\n\\n%2: %3\"\nAuth.Chat=\"Ћаскање\"\nAuth.StreamInfo=\"Информације о стримовању\"\nTwitchAuth.Stats=\"Twitch статистика\"\nTwitchAuth.Feed=\"„Твич” фид активности\"\nTwitchAuth.TwoFactorFail.Title=\"Није могуће поставити упит за кључ стрима\"\nTwitchAuth.TwoFactorFail.Text=\"ОБС није успео да се повеже са вашим Твич налогом. Уверите се да је двофакторска аутентификација подешена у вашем <a href='https://www.twitch.tv/settings/security'>Твич безбедносним подешавањима</a> јер је то потребно за стримовање.\"\nRestreamAuth.Channels=\"Рестрим канали\"\nCopy.Filters=\"Копирај филтере\"\nPaste.Filters=\"Налепи филтере\"\nBrowserPanelInit.Title=\"Иницијализација прегледача...\"\nBrowserPanelInit.Text=\"Покретање претраживача, молим сачекајте...\"\nBandwidthTest.Region=\"Регион\"\nBandwidthTest.Region.US=\"Сједињене Америчке Државе\"\nBandwidthTest.Region.EU=\"Европа\"\nBandwidthTest.Region.Asia=\"Азија\"\nBandwidthTest.Region.Other=\"Други\"\nBasic.AutoConfig=\"Чаробњак за аутоматско конфигурисање (Бета верзија)\"\nBasic.AutoConfig.ApplySettings=\"Промени подешавања\"\nBasic.AutoConfig.StartPage=\"Информације о коришћењу\"\nBasic.AutoConfig.StartPage.SubTitle=\"Наведите за шта тачно желите да користите програм\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Оптимизуј за стримовање, снимање је секундарно\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Оптимизуј за снимање, стримовање се неће извршити\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Користићу само виртуелну камеру\"\nBasic.AutoConfig.VideoPage=\"Подешавања видео-снимака\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Одредите видео подешавања која желите да користите\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Користи тренутну (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Приказ %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Користите тренутни (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Или 60 или 30, са акцентом на 60 кад год је могуће\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Или 60 или 30, са акцентом на високу резолуцију\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Напомена: основна резолуција није нужно иста као резолуција којом ћете стримовати или којом ћете снимати. Ваша стварна резолуција којом стримујете/снимате може бити скалирана у односу на основну како би се смањила употреба ресурса или оптимизовао проток.\"\nBasic.AutoConfig.StreamPage=\"Информације о стримовању\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Молимо унесите ваше информације о стримовању\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Повежи налог (препоручено)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Дисконектуј налог\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Дисконектуј налог?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Ова промена ће бити примењена одмах. Да ли сте сигурни да желите да дисконектујете Ваш налог?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Нађи кључ за стримовање\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Више информација\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Користи шифру за стримовање\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Користи кључ за стримовање (напредно)\"\nBasic.AutoConfig.StreamPage.Service=\"Услуга\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Прикажи све...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Прилагођено...\"\nBasic.AutoConfig.StreamPage.Server=\"Сервер\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Шифра за стримовање\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: унесите лозинку енкрипције.\\nRTMP: унесите кључ који пружа сервис.\\nSRT: унесите streamid ако га сервис користи.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Кључ енкодера\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Токен носиоца\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Повезани налог\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Процени брзину протока тестом пропусног опсега (може потрајати неколико минута)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Са акцентом на хардверско кодирање\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Хардверско кодирање спречава велику употребу процесора, али може захтевати већу брзину протока како би се одржао исти ниво квалитета.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Упозорење о стримовању\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Тест пропусног опсега стримује насумичне видео податке без звука на Вашем каналу. Препоручено је да привремено искључите чување видео снимака стримова и да подесите стрим као приватни док се тест не заврши, ако је то могуће. Желите ли да наставите?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Тестирај %1\"\nBasic.AutoConfig.TestPage=\"Коначни резултати\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Програм тренутно извршава сет тестова како би проценио оптимална подешавања\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Тестирање је завршено\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"У току је извођење теста пропусног опсега, ово може потрајати неколико минута...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Није пронађен излаз за протокол ове услуге\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Повезивање са: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Неуспело повезивање са сервером, молимо проверите Вашу интернет везу и покушајте поново.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Тестирање пропусног опсега за: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"У току је тестирање стрим енкодера, ово може потрајати неколико минута...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"У току је тестирање енкодера за снимање, ово може потрајати пар минута...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Неуспело покретање енкодера\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Тренутно се тестира %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Енкодер стримовања\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Енкодер снимања\"\nBasic.AutoConfig.TestPage.Result.Header=\"Програм је одредио да су ова подешавања најповољнија за Вас:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Да бисте користили ова подешавања, кликните на ''Примените Подешавања''. Да опет конфигуришете wizard и покушате поново, кликните ''Назад''. Да конфигуришете ручно подешавања, кликните ''Поништи'' и отворите ''Подешавања''.\"\nBasic.AutoConfig.Info=\"Чаробњак за аутоматску конфигурацију ће утврдити најбоља подешавања на основу спецификација Вашег рачунара и брзине интернета.\"\nBasic.AutoConfig.RunAnytime=\"Може се покренути у сваком тренутку из менија „Алатке”.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Резолуција стримовања (скалирана)\"\nBasic.Stats=\"Статистика\"\nBasic.Stats.CPUUsage=\"Искоришћеност процесора\"\nBasic.Stats.HDDSpaceAvailable=\"Доступно простора на диску\"\nBasic.Stats.MemoryUsage=\"Искоришћеност меморије\"\nBasic.Stats.AverageTimeToRender=\"Просечно време потребно да се прикаже фрејм\"\nBasic.Stats.SkippedFrames=\"Прескочени фрејмови због заостатка кодирања\"\nBasic.Stats.MissedFrames=\"Пропуштени фрејмови због заостатка приказивања\"\nBasic.Stats.Output.Stream=\"Стрим\"\nBasic.Stats.Output.Recording=\"Снимање\"\nBasic.Stats.Status=\"Статус\"\nBasic.Stats.Status.Recording=\"Снимање\"\nBasic.Stats.Status.Live=\"УЖИВО\"\nBasic.Stats.Status.Reconnecting=\"Поновно повезивање\"\nBasic.Stats.Status.Inactive=\"Неактивно\"\nBasic.Stats.Status.Active=\"Активно\"\nBasic.Stats.DroppedFrames=\"Пропуштени фрејмови (Мрежа)\"\nBasic.Stats.MegabytesSent=\"Укупни подаци на излазу\"\nBasic.Stats.Bitrate=\"Брзина протока\"\nBasic.Stats.DiskFullIn=\"Диск пун за (приближно)\"\nBasic.Stats.DiskFullIn.Text=\"%1 сати, %2 минута\"\nBasic.Stats.ResetStats=\"Ресетуј статистику\"\nResetUIWarning.Title=\"Да ли сте сигурни да желите да ресетујете кориснички интерфејс?\"\nResetUIWarning.Text=\"Ресетовање корисничког интерфејса ће сакрити додатне докове. Ако желите да докови поново постану видљиви, мораћете да их омогућите у менију Докови.\\n\\nДа ли сте сигурни да желите да ресетујете кориснички интерфејс?\"\nUpdater.Title=\"Доступна је нова верзија програма\"\nUpdater.Text=\"Изашла је нова, унапређена верзија:\"\nUpdater.UpdateNow=\"Aжурирај одмах\"\nUpdater.RemindMeLater=\"Подсети ме касније\"\nUpdater.Skip=\"Прескочи верзију\"\nUpdater.NoUpdatesAvailable.Title=\"Ажурирања нису доступна\"\nUpdater.NoUpdatesAvailable.Text=\"Нема нових унапређења\"\nUpdater.BranchNotFound.Title=\"Канал ажурирања уклоњен\"\nUpdater.BranchNotFound.Text=\"Канал ажурирања који сте одабрали више није доступан, OBS је враћен на подразумеване вредности.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Провера интегритета недоступна\"\nUpdater.RepairButUpdatesAvailable.Text=\"Провера интегритета фајла је могућа једино за најскорију верзију. Верификујте и ажурирајте своју OBS инсталацију одласком на Помоћ →Провери има ли ажурирања.\"\nUpdater.RepairConfirm.Title=\"Потврди проверу интегритета\"\nUpdater.RepairConfirm.Text=\"Покретање провере интегритета ће скенирати Вашу OBS инсталацију, испитати има ли у њој корумпираних фајлова и по потреби преузети исправне фајлове. Ово може потрајати тренутак. \\n\\nДа ли желите да наставите?\"\nUpdater.FailedToLaunch=\"Покретање ажурирања софтвера није успело\"\nQuickTransitions.SwapScenes=\"Замени сцене Преглед/Програм након прелаза\"\nQuickTransitions.SwapScenesTT=\"Замена сцена „преглед” и „програм” након прелаза (уколико оригинална сцена програма и даље постоји).\\nОво неће опозвати ниједну промену учињену над оригиналном сценом програма.\"\nQuickTransitions.DuplicateScene=\"Дуплирај сцену\"\nQuickTransitions.DuplicateSceneTT=\"Када се уређује иста сцена, допушта уређивање трансформације/видљивости извора без утицаја на излаз програма.\\nДа бисте изменили својства извора без утицања на излаз програма, омогућите „Дуплирај изворе”.\\nМењање ове вредности ће ресетовати тренутну сцену програма (ако она и даље постоји).\"\nQuickTransitions.EditProperties=\"Дуплирај изворе\"\nQuickTransitions.EditPropertiesTT=\"Приликом уређивања исте сцене, омогућава уређивање својстава извора без измене излазног програма.\\nОво се може користити само ако је омогућена опција \\\"Дуплирај сцену\\\".\\nОдређени извори (као што су снимање екрана или медијски извори) не подржавају ово и уређивање није могуће одвојено.\\nИзменом ове вредности успоставиће се тренутна сцена програма (ако она и даље постоји).\\n\\nУпозорење: Пошто ће извори бити дуплирани, ово може захтевати додатне системске или видео ресурсе.\"\nQuickTransitions.HotkeyName=\"Брзи прелаз: %1\"\nBasic.AddTransition=\"Додај подесиви прелаз\"\nBasic.RemoveTransition=\"Уклони подесиви прелаз\"\nBasic.TransitionProperties=\"Својства прелаза\"\nBasic.SceneTransitions=\"Прелази сцена\"\nBasic.TransitionDuration=\"Трајање\"\nBasic.TogglePreviewProgramMode=\"Студијски режим\"\nBasic.EnablePreviewProgramMode=\"Омогући Студио Режим\"\nBasic.DisablePreviewProgramMode=\"Онемогући Студио Режим\"\nUndo.Undo=\"Опозови\"\nUndo.Redo=\"Понови\"\nUndo.Add=\"Додај „%1”\"\nUndo.Delete=\"Избриши „%1”\"\nUndo.Rename=\"Преименуј „%1”\"\nUndo.SceneCollection.Switch=\"Пребаци се на „%1”\"\nUndo.Item.Undo=\"Опозови %1\"\nUndo.Item.Redo=\"Понови %1\"\nUndo.Sources.Multi=\"Избриши %1 извора\"\nUndo.Filters=\"Промене филтера над „%1”\"\nUndo.Filters.Paste.Single=\"Налепи филтер „%1” на „%2”\"\nUndo.Filters.Paste.Multiple=\"Копирање филтера са „%1” на „%2”\"\nUndo.Transform=\"Трансформиши извор(е) у „%1”\"\nUndo.Transform.Paste=\"Налепи трансформацију у „%1”\"\nUndo.Transform.Rotate=\"Ротација у „%1”\"\nUndo.Transform.Reset=\"Отказивање трансформације у „%1”\"\nUndo.Transform.HFlip=\"Хоризонтално окретање у „%1”\"\nUndo.Transform.VFlip=\"Вертикално окретање у „%1”\"\nUndo.Transform.FitToScreen=\"Прилагоди екрану у '%1'\"\nUndo.Transform.StretchToScreen=\"Растегни на екран у '%1'\"\nUndo.Transform.Center=\"Центрирај на екран у '%1'\"\nUndo.Transform.VCenter=\"Вертикално центрирање на екран у '%1'\"\nUndo.Transform.HCenter=\"Хоризонтално центрирање на екран у '%1'\"\nUndo.Volume.Change=\"Промена јачине звука на '%1'\"\nUndo.Volume.Mute=\"Искључи звук за „%1“\"\nUndo.Volume.Unmute=\"Укључи звук за „%1“\"\nUndo.Balance.Change=\"Промена баланса звука на '%1'\"\nUndo.SyncOffset.Change=\"Промена померања синхронизације звука на '%1'\"\nUndo.MonitoringType.Change=\"Промени аудио праћење на '%1'\"\nUndo.Mixers.Change=\"Промени аудио миксере на '%1'\"\nUndo.ForceMono.On=\"Омогући присилни моно режим на '%1'\"\nUndo.ForceMono.Off=\"Онемогући присилни моно режим на '%1'\"\nUndo.Properties=\"Промена својства на '%1'\"\nUndo.Scene.Duplicate=\"Дуплицирајте сцену '%1'\"\nUndo.ShowTransition=\"Прикажи прелаз на '%1'\"\nUndo.HideTransition=\"Сакриј прелаз на '%1'\"\nUndo.ShowSceneItem=\"Прикажи '%1' у '%2'\"\nUndo.HideSceneItem=\"Сакриј '%1' у '%2'\"\nUndo.ReorderSources=\"Преуреди изворе у '%1'\"\nUndo.MoveUp=\"Помери '%1' нагоре у '%2'\"\nUndo.MoveDown=\"Помери '%1' надоле у '%2'\"\nUndo.MoveToTop=\"Помери '%1' на врх у '%2'\"\nUndo.MoveToBottom=\"Помери '%1' на дно у '%2'\"\nUndo.PasteSource=\"Налепи извор(е) у '%1'\"\nUndo.PasteSourceRef=\"Налепи изворне референце у '%1'\"\nUndo.GroupItems=\"Групиши ставке у '%1'\"\nTransitionNameDlg.Text=\"Молим унесите име прелаза\"\nTransitionNameDlg.Title=\"Име прелаза\"\nTitleBar.SafeMode=\"БЕЗБЕДНИ РЕЖИМ\"\nTitleBar.PortableMode=\"Преносиви режим\"\nTitleBar.Profile=\"Профил\"\nTitleBar.Scenes=\"Сцене\"\nNameExists.Title=\"Име већ постоји\"\nNameExists.Text=\"Име је већ у употреби.\"\nNoNameEntered.Title=\"Молим унесите исправно име\"\nNoNameEntered.Text=\"Не можете изоставити име.\"\nConfirmStart.Title=\"Започни стрим?\"\nConfirmStart.Text=\"Да ли сте сигурни да желите да започнете стримовање?\"\nConfirmStop.Title=\"Заустави стрим?\"\nConfirmStop.Text=\"Да ли сте сигурни да желите да зауставите стримовање?\"\nConfirmStopRecord.Title=\"Заустави снимање?\"\nConfirmStopRecord.Text=\"Да ли сте сигурни да желите зауставити снимање?\"\nConfirmBWTest.Title=\"Започети пробу протока?\"\nConfirmBWTest.Text=\"OBS Вам је конфигурисан у режиму пробе протока. Овај режим Вам допушта да испробате мрежну повезаност без потребе да се укључујете уживо на свом каналу. Када будете били готови са тестирањем, мораћете да онемогућите овај режим да би гледаоци могли да виде Ваш стрим.\\n\\nЖелите ли да наставите?\"\nConfirmRemove.Title=\"Потврди избацивање\"\nConfirmRemove.Text=\"Да ли сте сигурни да желите да уклоните '%1'?\"\nConfirmRemove.TextMultiple=\"Да ли сте сигурни да желите да избаците %1 ставке?\"\nConfirmReset.Title=\"Ресетуј својства\"\nConfirmReset.Text=\"Јесте ли сигурни да желите да вратите тренутна својства на њихове подразумеване вредности?\"\nOutput.StartStreamFailed=\"Неуспело покретање стримовања\"\nOutput.StartRecordingFailed=\"Неуспело покретање снимања\"\nOutput.StartReplayFailed=\"Неуспело покретање бафера за поновни приказ\"\nOutput.StartVirtualCamFailed=\"Покретање виртуелне камере није успело\"\nOutput.StartFailedGeneric=\"Неуспело покретање излаза. Молимо проверите пријaву за детаље.\\n\\n Обавештење: уколико користите NVENC или AMD енкодере, постарајте се да су Ваши видео драјвери ажурирани.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Не могу се сачувати снимци док је снимање паузирано\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Упозорење: Снимци се не могу сачувати док је снимање паузирано.\"\nOutput.ConnectFail.Title=\"Неуспешно повезивање\"\nOutput.ConnectFail.BadPath=\"Неисправна путања или URL конекције. Молим проверите ваша подешавања да потврдите њихову исправност.\"\nOutput.ConnectFail.ConnectFailed=\"Неуспешно повезивање на сервер\"\nOutput.ConnectFail.InvalidStream=\"Немогуће је приступити наведеном каналу или стрим кључу, молим проверите ваш стрим кључ. Ако је исправан, можда постоји проблем при повезивању на сервер.\"\nOutput.ConnectFail.HdrDisabled=\"HDR излаз је тренутно онемогућен за овај излаз.\"\nOutput.ConnectFail.Error=\"Неочекивана грешка у повезивању са сервером. Више информација се налази у лог датотеци.\"\nOutput.ConnectFail.Disconnected=\"Прекинута веза са сервером.\"\nOutput.StreamEncodeError.Title=\"Грешка приликом кодирања\"\nOutput.StreamEncodeError.Msg=\"Искрсла је грешка са кодером током стримовања.\"\nOutput.StreamEncodeError.Msg.LastError=\"Дошло је до грешке кодера током стримовања:<br><br> %1\"\nOutput.RecordFail.Title=\"Неуспешно започињање снимања\"\nOutput.RecordFail.Unsupported=\"Излазни формат или није подржан или не подржава више од једне звучне траке. Молим проверите подешавања и покушајте поново.\"\nOutput.RecordNoSpace.Title=\"Недовољно простора на диску\"\nOutput.RecordNoSpace.Msg=\"Нема довољно простора на диску да се настави снимање.\"\nOutput.RecordError.Title=\"Грешка при снимању\"\nOutput.RecordError.Msg=\"Неодређена грешка се догодила при снимању.\"\nOutput.RecordError.EncodeErrorMsg=\"Искрсла је грешка са кодером током снимања.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Дошло је до грешке кодера током снимања:<br><br> %1\"\nOutput.BadPath.Title=\"Неисправна путања датотеке\"\nOutput.BadPath.Text=\"Конфигурисана путања за снимање није могла да се отвори. Молимо Вас проверите своју путању за снимање одласком на Подешавања →Излаз→Снимање.\"\nOutput.NoBroadcast.Title=\"Ниједан пренос није конфигурисан\"\nOutput.NoBroadcast.Text=\"Морате да конфигуришете пренос пре почетка стримовања.\"\nOutput.BroadcastStartFailed=\"Неуспешан покушај започињања преноса\"\nOutput.BroadcastStopFailed=\"Неуспешан покушај заустављања преноса\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Отпреми\"\nLogUploadDialog.Buttons.RetryButton=\"Покушај поново\"\nRemux.SourceFile=\"OBS снимак\"\nRemux.TargetFile=\"Датотека\"\nRemux.Remux=\"Ремукс\"\nRemux.Stop=\"Заустави ремукс\"\nRemux.ClearFinished=\"Обриши завршене елементе\"\nRemux.ClearAll=\"Обриши све елементе\"\nRemux.OBSRecording=\"OBS снимак\"\nRemux.FinishedTitle=\"Ремукс је завршен\"\nRemux.Finished=\"Завршен ремукс снимка\"\nRemux.FinishedError=\"Ремукс је завршен, али датотека можда није комплетирана\"\nRemux.SelectRecording=\"Изаберите OBS снимак...\"\nRemux.SelectTarget=\"Изаберите циљни фајл...\"\nRemux.FileExistsTitle=\"Циљни фајл већ постоји\"\nRemux.FileExists=\"Следећи циљни фајлови већ постоје. Да ли желите да их замените новим?\"\nRemux.ExitUnfinishedTitle=\"Ремукс у току\"\nRemux.ExitUnfinished=\"Ремукс није завршен, ако сада зауставите процес датотека може бити неупотребљива.\\nДа ли сте сигурни да желите зауставити ремукс?\"\nRemux.HelpText=\"Приложите фајлове у овај прозор за ремукс, или изаберите празну \\\"OBS Recording\\\" ћелију да потражите фајл.\"\nRemux.NoFilesAddedTitle=\"Није додата датотека за ремултиплексирање\"\nRemux.NoFilesAdded=\"Ниједна датотека није додата у ремукс. Одбаците фасциклу која садржи једну или више видео датотека.\"\nMissingFiles=\"Фајлови недостају\"\nMissingFiles.MissingFile=\"Фајл недостаје\"\nMissingFiles.NewFile=\"Нови фајл\"\nMissingFiles.HelpText=\"Неки фајлови недостају од када сте последњи пут користили OBS.\"\nMissingFiles.Clear=\"<очишћено>\"\nMissingFiles.NumFound=\"Пронађен %1 од %2\"\nMissingFiles.Search=\"Претражи фолдер...\"\nMissingFiles.SelectFile=\"Изабери фајл...\"\nMissingFiles.SelectDir=\"Изаберите фолдер за претрагу\"\nMissingFiles.State=\"Стање\"\nMissingFiles.Missing=\"Нестао\"\nMissingFiles.Replaced=\"Замењен\"\nMissingFiles.Cleared=\"Очишћен\"\nMissingFiles.Found=\"Нађен\"\nMissingFiles.AutoSearch=\"Накнадни одговарајући фајлови су пронађени\"\nMissingFiles.AutoSearchText=\"OBS је пронашао накнадна поклапања са несталим фајловима у том фолдеру. Желите ли да их додате?\"\nMissingFiles.NoMissing.Title=\"Провера несталих фајлова\"\nMissingFiles.NoMissing.Text=\"Ниједан фајл наизглед не недостаје.\"\nMacPermissions.MenuAction=\"Прегледај дозволе за апликације...\"\nMacPermissions.Title=\"Прегледај дозволе апликације\"\nMacPermissions.Description=\"OBS захтева Вашу дозволу да би могао да Вам пружи одређене услуге. Препоручује се да одобрите ове дозволе, али оне нису неопходне за коришћење апликације. Увек можете да их одобрите касније.\"\nMacPermissions.Description.OpenDialog=\"Можете поново отворити овај прозор из менија OBS студија.\"\nMacPermissions.AccessGranted=\"Приступ одобрен\"\nMacPermissions.RequestAccess=\"Затражи приступ\"\nMacPermissions.OpenPreferences=\"Отвори преференце за %1\"\nMacPermissions.Item.ScreenRecording=\"Снимање екрана\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS захтева ову дозволу да би могао да сними Ваш екран.\"\nMacPermissions.Item.Camera=\"Камера\"\nMacPermissions.Item.Camera.Details=\"Ова дозвола је потребна за снимање садржаја помоћу веб камере или картице за снимање.\"\nMacPermissions.Item.Microphone=\"Микрофон\"\nMacPermissions.Item.Microphone.Details=\"OBS захтева ову дозволу ако желиш да снимаш свој микрофон или спољни звучни уређај.\"\nMacPermissions.Item.InputMonitoring=\"Праћење уноса\"\nMacPermissions.Item.InputMonitoring.Details=\"Ова дозвола је потребна да би пречице радиле док је OBS у позадини.\"\nMacPermissions.Item.Accessibility=\"Приступачност\"\nMacPermissions.Item.Accessibility.Details=\"На старијим инсталацијама, OBS може бити наведен у „Приступачност“ уместо „Праћење уноса“.\"\nMacPermissions.Continue=\"Настави\"\nSourceLeak.Title=\"Грешка при чишћењу извора\"\nSourceLeak.Text=\"Дошло је до проблема приликом промене колекција сцена и неки извори нису могли бити истоварени. Овај проблем обично узрокују додаци који не ослобађају ресурсе исправно. Молимо вас да се уверите да су сви додаци које користите ажурирани.\\n\\nOBS Studio ће се сада затворити како би се спречило потенцијално оштећење података.\"\nBasic.DesktopDevice1=\"Звук са рачунара\"\nBasic.DesktopDevice2=\"Звук са рачунара 2\"\nBasic.AuxDevice1=\"Микрофон/Улаз\"\nBasic.AuxDevice2=\"Микрофон/Улаз 2\"\nBasic.AuxDevice3=\"Микрофон/Улаз 3\"\nBasic.AuxDevice4=\"Микрофон/Улаз 4\"\nBasic.Scene=\"Сцена\"\nBasic.DisplayCapture=\"Прикажи улаз\"\nBasic.Main.PreviewConextMenu.Enable=\"Омогући преглед\"\nBasic.Main.Preview.Disable=\"Онемогући преглед\"\nScaleFiltering=\"Филтер увећања\"\nScaleFiltering.Point=\"Тачка\"\nScaleFiltering.Bilinear=\"Билинеарно\"\nScaleFiltering.Bicubic=\"Викубично\"\nScaleFiltering.Lanczos=\"Ланкоз\"\nScaleFiltering.Area=\"Област\"\nBlendingMethod=\"Метод мешања боја\"\nBlendingMethod.Default=\"Подразумевано\"\nBlendingMethod.SrgbOff=\"Искључен SRGB\"\nBlendingMode=\"Метод мешања боја\"\nBlendingMode.Normal=\"Нормалан\"\nBlendingMode.Additive=\"Додај\"\nBlendingMode.Subtract=\"Суптраковани\"\nBlendingMode.Screen=\"Екран\"\nBlendingMode.Multiply=\"Множи\"\nBlendingMode.Lighten=\"Посветли\"\nBlendingMode.Darken=\"Потамни\"\nDeinterlacing=\"Деинтерлејсинг\"\nDeinterlacing.Discard=\"Одбаци\"\nDeinterlacing.TopFieldFirst=\"Прво горње поље\"\nDeinterlacing.BottomFieldFirst=\"Прво доње поље\"\nVolControl.SliderUnmuted=\"Клизач за јачину звука за '%1':\"\nVolControl.SliderMuted=\"Клизач за јачину звука за „%1“: (тренутно искључен звук)\"\nVolControl.Mute=\"Искључите звук '%1'\"\nVolControl.Properties=\"Својства '%1'\"\nVolControl.UnassignedWarning.Title=\"Недодељени аудио извор\"\nVolControl.UnassignedWarning.Text=\"„%1“ није додељен ниједној аудио нумери и неће се чути у стримовима или снимцима.\\n\\nДа бисте доделили аудио извор нумери, отворите Напредна својства звука преко менија десног клика или дугмета зупчаника у траци са алаткама миксера.\"\nBasic.Main.AddSceneDlg.Title=\"Додај сцену\"\nBasic.Main.AddSceneDlg.Text=\"Молим унесите име сцене\"\nBasic.Main.DefaultSceneName.Text=\"Сцена %1\"\nBasic.Main.AddSceneCollection.Title=\"Додај колекцију сцена\"\nBasic.Main.AddSceneCollection.Text=\"Молим унесите име колекције сцена\"\nBasic.Main.RenameSceneCollection.Title=\"Промени име колекције сцена\"\nBasic.Main.RemigrateSceneCollection.Title=\"Ажурирај резолуцију колекције сцена\"\nBasic.Main.RemigrateSceneCollection.Text=\"Да ли желите да ажурирате резолуцију колекције сцена „%1“ тако да одговара резолуцији платна тренутног профила од %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Није могуће ажурирати резолуцију колекције сцена док су излази активни.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Ажурирање резолуције колекције сцена није успело. Оригинална резолуција је непозната.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Ресетовање није могуће: Промена OBS резолуције није успела.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Ресетовање није могуће: Тренутна резолуција је већ основна резолуција колекције сцена.\"\nAddProfile.Title=\"Додај профил\"\nAddProfile.Text=\"Молим унесите име профила\"\nAddProfile.WizardCheckbox=\"Прикажи чаробњака за аутоматску конфигурацију\"\nRenameProfile.Title=\"Промени име профила\"\nBasic.Main.MixerRename.Title=\"Преименујте извор звука\"\nBasic.Main.MixerRename.Text=\"Молимо унесите назив извора звука\"\nBasic.Main.PreviewDisabled=\"Преглед је тренутно онемогућен\"\nBasic.SourceSelect=\"Направи или изабери извор\"\nBasic.SourceSelect.CreateNew=\"Направи нови\"\nBasic.SourceSelect.AddExisting=\"Додај постојећи\"\nBasic.SourceSelect.AddVisible=\"Прикажи извор\"\nBasic.Main.Sources.Visibility=\"Видљивост\"\nBasic.Main.Sources.VisibilityDescription=\"Контролише видљивост „%1” на платну\"\nBasic.Main.Sources.Lock=\"Закључавање\"\nBasic.Main.Sources.LockDescription=\"Закључава позицију и размеру „%1” на платну\"\nBasic.PropertiesWindow=\"Својства за '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (аутоматски одабир: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Изабери боју\"\nBasic.PropertiesWindow.SelectFont=\"Изабери фонт\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Изаберите фонт\"\nBasic.PropertiesWindow.ConfirmTitle=\"Подешавања промењена\"\nBasic.PropertiesWindow.Confirm=\"Постоје подешавања која нису сачувана. Да ли желите да их сачувате?\"\nBasic.PropertiesWindow.NoProperties=\"Нема доступних својстава\"\nBasic.PropertiesWindow.AddFiles=\"Додај датотеке\"\nBasic.PropertiesWindow.AddDir=\"Додај директоријум\"\nBasic.PropertiesWindow.AddURL=\"Додај путању/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Додај директоријум у '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Додај датотеке у '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Додај записе у '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Измени записе за '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Једноставне FPS вредности\"\nBasic.PropertiesView.FPS.Rational=\"Рационалне FPS вредности\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Исправни опсези FPS-а:\"\nBasic.PropertiesView.UrlButton.Text=\"Отворити овај линк у подразумеваном веб прегледачу?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Отвори URL\"\nBasic.InteractionWindow=\"Интеракција са '%1'\"\nBasic.StatusBar.Reconnecting=\"Веза је прекинута, поновно повезивање за %2 секунд(и) (%1 покушај)\"\nBasic.StatusBar.AttemptingReconnect=\"Покушавам поновно повезивање... (%1 покушај)\"\nBasic.StatusBar.ReconnectSuccessful=\"Поновно повезивање успешно\"\nBasic.StatusBar.Delay=\"Кашњење (%1 сек)\"\nBasic.StatusBar.DelayStartingIn=\"Кашњење (почиње за %1 сек)\"\nBasic.StatusBar.DelayStoppingIn=\"Кашњење (зауставља се за %1 сек)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Кашњење (зауставља се за %1 сек, почиње за %2 сек)\"\nBasic.StatusBar.RecordingSavedTo=\"Снимак сачуван у „%1”\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Бафер снимка сачуван у „%1”\"\nBasic.StatusBar.ScreenshotSavedTo=\"Снимак екрана тј. скриншот сачуван у „%1”\"\nBasic.StatusBar.AutoRemuxedTo=\"Снимак аутоматски ремуксован у „%1”\"\nBasic.Filters=\"Филтери\"\nBasic.Filters.AsyncFilters=\"Звучни/Видео филтери\"\nBasic.Filters.AudioFilters=\"Звучни филтери\"\nBasic.Filters.EffectFilters=\"Филтери ефеката\"\nBasic.Filters.Title=\"Филтери за '%1'\"\nBasic.Filters.AddFilter.Title=\"Име филтера\"\nBasic.Filters.AddFilter.Text=\"Молим назначите име филтера\"\nBasic.TransformWindow=\"Трансформација ставке на сцени\"\nBasic.TransformWindow.Position=\"Позиција\"\nBasic.TransformWindow.PositionX=\"Позиција икс\"\nBasic.TransformWindow.PositionY=\"Позиција ипсилон\"\nBasic.TransformWindow.Rotation=\"Ротација\"\nBasic.TransformWindow.Size=\"Величина\"\nBasic.TransformWindow.Width=\"Ширина\"\nBasic.TransformWindow.Height=\"Висина\"\nBasic.TransformWindow.Alignment=\"Позиционо поравнање\"\nBasic.TransformWindow.BoundsType=\"Тип оквира\"\nBasic.TransformWindow.BoundsAlignment=\"Поравнање у оквиру\"\nBasic.TransformWindow.Bounds=\"Величина оквира\"\nBasic.TransformWindow.BoundsWidth=\"Ширина оквира\"\nBasic.TransformWindow.BoundsHeight=\"Висина оквира\"\nBasic.TransformWindow.CropToBounds=\"Исеци до граничног оквира\"\nBasic.TransformWindow.Crop=\"Исецање\"\nBasic.TransformWindow.CropLeft=\"Одсеци с лева\"\nBasic.TransformWindow.CropRight=\"Одсеци с десна\"\nBasic.TransformWindow.CropTop=\"Одсеци одозго\"\nBasic.TransformWindow.CropBottom=\"Одсеци одоздо\"\nBasic.TransformWindow.Alignment.TopLeft=\"Горе лево\"\nBasic.TransformWindow.Alignment.TopCenter=\"Горе, на средини\"\nBasic.TransformWindow.Alignment.TopRight=\"Горе десно\"\nBasic.TransformWindow.Alignment.CenterLeft=\"На средини, лево\"\nBasic.TransformWindow.Alignment.Center=\"Центар\"\nBasic.TransformWindow.Alignment.CenterRight=\"На средини, десно\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Доње лево\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Доле, на средини\"\nBasic.TransformWindow.Alignment.BottomRight=\"Доње десно\"\nBasic.TransformWindow.BoundsType.None=\"Без оквира\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Само највећа величина\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Скалирај према унутрашњим оквирима\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Скалирај према спољњим оквирима\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Скалирај до ширине оквира\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Скалирај до висине оквира\"\nBasic.TransformWindow.BoundsType.Stretch=\"Развуци до оквира\"\nBasic.TransformWindow.Title=\"Измени трансформацију за „%1”\"\nBasic.TransformWindow.NoSelectedSource=\"Није изабран извор\"\nBasic.Main.AddSourceHelp.Title=\"Немогуће је додати извор\"\nBasic.Main.AddSourceHelp.Text=\"Потребна је најмање једна сцена да бисте додали извор.\"\nBasic.Main.Scenes=\"Сцене\"\nBasic.Main.Sources=\"Извори\"\nBasic.Main.Source=\"Извор\"\nBasic.Main.Controls=\"Контроле\"\nBasic.Main.PreparingStream=\"Припрема...\"\nBasic.Main.Connecting=\"Повезивање...\"\nBasic.Main.StartRecording=\"Започни снимање\"\nBasic.Main.StartReplayBuffer=\"Покрени бафер за поновно приказивање\"\nBasic.Main.SaveReplay=\"Сачувај поновно репродуковање\"\nBasic.Main.StartStreaming=\"Започни стримовање\"\nBasic.Main.StartBroadcast=\"Емитуј уживо\"\nBasic.Main.StartVirtualCam=\"Покрени виртуалну камеру\"\nBasic.Main.StopRecording=\"Заустави снимање\"\nBasic.Main.PauseRecording=\"Паузирај снимање\"\nBasic.Main.UnpauseRecording=\"Настави снимање\"\nBasic.Main.SplitFile=\"Подели датотеку снимка на више делова\"\nBasic.Main.StoppingRecording=\"Заустављање снимања...\"\nBasic.Main.StopReplayBuffer=\"Заустави бафер за поновно приказивање\"\nBasic.Main.StoppingReplayBuffer=\"Заустављање бафера за поновно приказивање је у току...\"\nBasic.Main.SetupBroadcast=\"Управљање преносом\"\nBasic.Main.StopStreaming=\"Заустави стримовање\"\nBasic.Main.StopBroadcast=\"Крај преноса\"\nBasic.Main.AutoStopEnabled=\"(Аутоматско заустављање)\"\nBasic.Main.StoppingStreaming=\"Заустављање емитовања...\"\nBasic.Main.ForceStopStreaming=\"Заустави стримовање (поништи кашњење)\"\nBasic.Main.ShowContextBar=\"Прикажи траку са алаткама „Извори”\"\nBasic.Main.HideContextBar=\"Сакриј траку са алаткама „Извори”\"\nBasic.Main.StopVirtualCam=\"Заустави виртуелну камеру\"\nBasic.Main.Group=\"Група %1\"\nBasic.Main.GroupItems=\"Групиши одабране елементе\"\nBasic.Main.Ungroup=\"Раздвоји елементе групе\"\nBasic.Main.GridMode=\"Режим координатне мреже\"\nBasic.Main.ListMode=\"Режим листе\"\nBasic.Main.VirtualCamConfig=\"Конфигурација виртуелне камере\"\nBasic.VCam.VirtualCamera=\"Виртуелна камера\"\nBasic.VCam.OutputType=\"Тип излаза\"\nBasic.VCam.OutputSelection=\"Одабир излаза\"\nBasic.VCam.OutputType.Program=\"Програм (Подразумевани)\"\nBasic.VCam.OutputSelection.NoSelection=\"Нема избора за овај тип излаза\"\nBasic.VCam.RestartWarning=\"Виртуелна камера ће бити поново покренута да би се применила ова промена\"\nBasic.MainMenu.File=\"Датотека\"\nBasic.MainMenu.File.Export=\"Извези (Е) (&E)\"\nBasic.MainMenu.File.Import=\"Увоз (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Прикажи снимке\"\nBasic.MainMenu.File.Remux=\"Re&mux снимака\"\nBasic.MainMenu.File.Settings=\"Подешавања\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Прикажи фолдере са подешавањима\"\nBasic.MainMenu.File.ShowProfileFolder=\"Прикажи фолдер са подешавањима\"\nBasic.MainMenu.File.ShowMissingFiles=\"Провери фале ли фајлови\"\nBasic.MainMenu.File.Exit=\"Излаз (&X)\"\nBasic.MainMenu.Edit=\"Уреди\"\nBasic.MainMenu.Edit.Undo=\"Врати уназад (&U)\"\nBasic.MainMenu.Edit.Redo=\"Уради поново (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Закључај приказ (&L)\"\nBasic.MainMenu.Edit.Scale=\"Преглед скалирања (&s)\"\nBasic.MainMenu.Edit.Scale.Window=\"Скалирај на величину прозора\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Платно (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Излаз (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Скалирано (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Увећај\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Умањи\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Ресетуј зумирање\"\nBasic.MainMenu.Edit.Transform=\"Трансформиши (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Уреди трансформацију\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Копирај трансформацију\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Налепи трансформацију\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Поништи трансформацију (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Окрени за 90 степени у смеру казаљке на сату\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Окрени за 90 степени у контра смеру казаљке на сату\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Окрени за 180 степени\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Преврни хоризонтално (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Преврни вертикално (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Рашири по екрану (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Развуци по екрану (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Центрирај на екрану (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Центрирај вертикално\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Центрирај хоризонтално\"\nBasic.MainMenu.Edit.Order=\"Редослед (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Помери горе (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Помери доле (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Помери на врх (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Помери на дно (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Напредна подешавања звука (&A)\"\nBasic.MainMenu.View=\"Преглед\"\nBasic.MainMenu.View.Toolbars=\"Линије алатки (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"Докните траке са алаткама\"\nBasic.MainMenu.View.ContextBar=\"Изворска трака са алаткама\"\nBasic.MainMenu.View.SourceIcons=\"Иконице извора\"\nBasic.MainMenu.View.StatusBar=\"Статусна линија (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Интерфејс на целом екрану\"\nBasic.MainMenu.View.ResetUI=\"Ресетуј кориснички интерфејс\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Увек на врху\"\nBasic.MainMenu.View.SceneListMode=\"Режим листе сцена\"\nBasic.MainMenu.Docks=\"&Докови\"\nBasic.MainMenu.Docks.ResetDocks=\"&Ресетуј докове\"\nBasic.MainMenu.Docks.LockDocks=\"&Закључај докове\"\nBasic.MainMenu.Docks.SideDocks=\"Докови пуне висине\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Прилагођени докови прегледача\"\nBasic.MainMenu.Import=\"Увоз...\"\nBasic.MainMenu.Export=\"Извоз...\"\nBasic.MainMenu.SceneCollection=\"Колекција сцена\"\nBasic.MainMenu.Profile=\"Профил\"\nBasic.MainMenu.Profile.Import=\"Увези профил\"\nBasic.MainMenu.Profile.Export=\"Извези профил\"\nBasic.MainMenu.SceneCollection.Import=\"Увези колекцију сцена\"\nBasic.MainMenu.SceneCollection.Export=\"Извези колекцију сцена\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Ресетуј основну резолуцију\"\nBasic.MainMenu.SceneCollection.Migrate=\"Подеси основну резолуцију\"\nBasic.MainMenu.Profile.Exists=\"Овај профил већ постоји\"\nBasic.MainMenu.Tools=\"Алати\"\nBasic.MainMenu.Help=\"Помоћ\"\nBasic.MainMenu.Help.HelpPortal=\"Портал за помоћ (&P)\"\nBasic.MainMenu.Help.Website=\"Посети страницу (&W)\"\nBasic.MainMenu.Help.Discord=\"Придружите се &Discord серверу\"\nBasic.MainMenu.Help.WhatsNew=\"Шта је ново\"\nBasic.MainMenu.Help.Logs=\"Лог датотеке (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Прикажи лог датотеке (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Пошаљи тренутну лог датотеку (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Отпреми &претходни фајл извештаја\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Прикажи тренутни записник (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Напомене о издању\"\nBasic.MainMenu.Help.CheckForUpdates=\"Провери верзију програма\"\nBasic.MainMenu.Help.Repair=\"Провера интегритета фајла\"\nBasic.MainMenu.Help.RestartSafeMode=\"Поново покрени у Безбедном Режиму\"\nBasic.MainMenu.Help.RestartNormal=\"Поново покрени у Нормалном Режиму\"\nBasic.MainMenu.Help.CrashLogs=\"Извештаји о прекидима (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Прикажи извештаје о прекидима (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Отпреми &претходни извештај о рушењу\"\nBasic.MainMenu.Help.About=\"О програму (&A)\"\nBasic.Settings.ProgramRestart=\"Програм мора бити поново покренут да би ова подешавања била прихваћена.\"\nBasic.Settings.ConfirmTitle=\"Потврдите промене\"\nBasic.Settings.Confirm=\"Постоје промене које нису сачуване. Сачувати?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 контролише нека од ваших подешавања стрима\"\nBasic.Settings.General=\"Опште\"\nBasic.Settings.General.Language=\"Језик\"\nBasic.Settings.General.Updater=\"Ажурирања\"\nBasic.Settings.General.UpdateChannel=\"Канал ажурирања\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Онемогућено)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Подразумевано)\"\nBasic.Settings.General.EnableAutoUpdates=\"Аутоматски потражи ажурирања при покрeтању\"\nBasic.Settings.General.OpenStatsOnStartup=\"Прикажи статистику при покретању\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Сакријте OBS прозоре приликом снимања екрана\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Ово подешавање ће сакрити све непројекторске прозоре OBS студија да их OBS не би снимио, што ће утицати и на \\nдруге апликације, попут апликација за видео конференције, дељење екрана, подршку на даљину, скриншотовање и други софтвер са функцијама снимања екрана.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Укључивањем ове функције ће бити сакривени сви непројекторски прозори OBS студија да их OBS не би снимио, што ће утицати и на друге апликације, попут апликација за видео конференције, дељење екрана, подршку на даљину, скриншотовање и други софтвер са функцијама снимања екрана.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Прикажи прозор за потврду када се започињу стримови\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Прикажи прозор за потврду када се заустављају стримови\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Прикажи прозор за потврду приликом престанка снимања\"\nBasic.Settings.General.Projectors=\"Пројектори\"\nBasic.Settings.General.HideProjectorCursor=\"Сакриј показивач на пројекторима\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Увек постави пројектор на врх прозора\"\nBasic.Settings.General.Snapping=\"Поравнавање привлачењем извора\"\nBasic.Settings.General.ScreenSnapping=\"Привуци изворе ивици екрана\"\nBasic.Settings.General.CenterSnapping=\"Привуци изворе хоризонталној и вертикалној средини\"\nBasic.Settings.General.SourceSnapping=\"Привлачење извора ка другим изворима\"\nBasic.Settings.General.SnapDistance=\"Осетљивост привлачења\"\nBasic.Settings.General.SpacingHelpers=\"Прикажи помоћне линије за оријентацију пиксела\"\nBasic.Settings.General.RecordWhenStreaming=\"Аутоматско снимање при емитовању\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Настави снимати када се емитовање заустави\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Аутоматски покрени бафер поновног приказивања током стримовања\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Након заустављања стримовања, задржи бафер поновног приказивања као активан\"\nBasic.Settings.General.SysTrayWhenStarted=\"При покретању минимизуј на иконицу у системском панелу\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Увек минимизуј system tray уместо таск бара\"\nBasic.Settings.General.SaveProjectors=\"Сачувај пројекторе пре изласка из програма\"\nBasic.Settings.General.CloseExistingProjectors=\"Ограничење од једног full-screen пројектора по екрану\"\nBasic.Settings.General.Preview=\"Преглед\"\nBasic.Settings.General.OverflowHidden=\"Сакриј вишак\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Вишак је увек видљив\"\nBasic.Settings.General.OverflowSelectionHidden=\"Прикажи вишак чак и када је извор невидљив\"\nBasic.Settings.General.Importers=\"Увозници\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Претражи знане локације за прикупљање сцена приликом увоза\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Прелаз на сцену дуплим кликом\"\nBasic.Settings.General.StudioPortraitLayout=\"Укључи портрет/вертикални приказ\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Прикажи ознаке за преглед/програм\"\nBasic.Settings.General.Multiview=\"Симултани приказ\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Промена сцене кликом\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Прикажи имена сцена\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Прикажи сигурне области (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Распоред симултаног приказа\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Хоризонтално, горе (8 сцена)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Хоризонтално, доле (8 сцена)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Вертикално, лево (8 сцена)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Вертикално, десно (8 сцена)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Хоризонтално, Врх (18 сцена)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Хоризонтално, горе (24 сцене)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Само сцене (4 сцене)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Само сцене (9 сцена)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Само сцене (16 сцена)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Само сцене (25 сцена)\"\nBasic.Settings.General.ChannelName.stable=\"Стабилно\"\nBasic.Settings.General.ChannelDescription.stable=\"Последња стабилна верзија\"\nBasic.Settings.General.ChannelName.beta=\"Бета верзије и кандидати за издање\"\nBasic.Settings.General.ChannelDescription.beta=\"Потенцијално нестабилне предразвојне верзије\"\nBasic.Settings.Appearance=\"Изглед\"\nBasic.Settings.Appearance.General=\"Опште\"\nBasic.Settings.Appearance.General.Theme=\"Тема\"\nBasic.Settings.Appearance.General.Variant=\"Стил\"\nBasic.Settings.Appearance.General.NoVariant=\"Нема доступних стилова\"\nBasic.Settings.Appearance.FontScale=\"Величина фонта\"\nBasic.Settings.Appearance.Density=\"Густина\"\nBasic.Settings.Appearance.Classic=\"Класично\"\nBasic.Settings.Appearance.Compact=\"Компактно\"\nBasic.Settings.Appearance.Normal=\"Нормална\"\nBasic.Settings.Appearance.Comfortable=\"Удобно\"\nBasic.Settings.Appearance.OptionsWarning=\"Неке опције изгледа нису доступне за овај стил.\"\nBasic.Settings.Stream=\"Стрим\"\nBasic.Settings.Stream.Destination=\"Одредиште\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Користи аутентификацију\"\nBasic.Settings.Stream.Custom.Username=\"Корисничко име\"\nBasic.Settings.Stream.Custom.Password=\"Шифра\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: унесите srp_username.\\nRTMP: унесите корисничко име.\\nSRT: није у употреби.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: унесите srp_password.\\nRTMP: унесите лозинку.\\nSRT: унесите шифру за енкрипцију.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Омогући режим тестирања проточног опсега\"\nBasic.Settings.Stream.TTVAddon=\"Додаци за Твич чет\"\nBasic.Settings.Stream.TTVAddon.None=\"Ниједан\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV и FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Недостаје поставка стрима\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Отвори подешавања\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL и кључ стрима недостају.\\n\\nОтворите подешавања и унесите URL и кључ стрима у картици „стрим”.\"\nBasic.Settings.Stream.MissingUrl=\"URL стрима недостаје.\\n\\nОтворите подешавања и унесите URL у картици „стрим”.\"\nBasic.Settings.Stream.MissingStreamKey=\"Кључ стрима недостаје.\\n\\nОтворите подешавања и унесите кључ стрима у картици „стрим”.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Игноришите препоруке за подешавања стриминг сервиса\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Испиши преко препоручених подешавања\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Упозорење: Игнорисање ограничења сервиса може проузроковати смањење квалитета стрима или Вас може спречити да стримујете.\\n\\nПродужите даље?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Максимални битрејт видеа: %1 kb/s\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Максимални битрејт аудија: %1 kb/s\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Максимална резолуција: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Максимални FPS (слике по секунди): %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Наведите прилагођени сервер...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Прилагођени сервер\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Омогући %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Максимални пропусни опсег стримовања\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Ауто\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Максималан број видео записа\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Ауто\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Омогући снимање стрима у FLV (користи једноставна подешавања датотеке за снимање)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Замена конфигурације (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Омогући замену конфигурације\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Вишеканални видео\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Додатно платно\"\nBasic.Settings.Stream.AdvancedOptions=\"Напредне опције\"\nBasic.Settings.Output=\"Излаз\"\nBasic.Settings.Output.Format=\"Формат снимања\"\nBasic.Settings.Output.Format.MKV=\"Матроска видео (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"Хибридни MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Хибридни MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Фрагментирани MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Фрагментирани MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Фрагментирани MOV записује снимак у деловима и не захтева исту финализацију као традиционалне MOV датотеке.\\nОво осигурава да датотека остане репродукована чак и ако је писање на диск прекинуто, на пример, као резултат BSOD-а или нестанка струје.\\n\\nОво можда није компатибилно са свим плејерима и уређивачима. Користите Датотека → Ремултификуј снимке да бисте конвертовали датотеку у компатибилнији формат ако је потребно.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Фрагментирани MP4 записује снимак у деловима и не захтева исту финализацију као традиционалне MP4 датотеке.\\nОво осигурава да датотека остане репродукована чак и ако је писање на диск прекинуто, на пример, као резултат BSOD-а или нестанка струје.\\n\\nОво можда није компатибилно са свим плејерима и уређивачима. Користите Датотека → Ремулкс снимци да бисте конвертовали датотеку у компатибилнији формат ако је потребно.\"\nBasic.Settings.Output.Encoder.Video=\"Видео енкодер\"\nBasic.Settings.Output.Encoder.Audio=\"Аудио енкодер\"\nBasic.Settings.Output.SelectDirectory=\"Одабери директоријум за снимање\"\nBasic.Settings.Output.DynamicBitrate=\"Динамички мењај битрејт у циљу избегавања загушења\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Динамички мењај битрејт у циљу избегавања загушења (Бета верзија)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Уместо смањења слика у секунди ради смањења загушења, динамички ће се мењати битрејт у ходу.\\n\\nИмајте на уму да ово може повећати временски застој гледаоцима у случају наглог и великог загушења.\\nКада битрејт опадне, можда ће му бити потребно пар минута да се врати на старо.\\n\\nТренутно подржано само за пренос преко RTMP протокола.\"\nBasic.Settings.Output.Mode=\"Режим излаза\"\nBasic.Settings.Output.Mode.Simple=\"Једноставно\"\nBasic.Settings.Output.Mode.Adv=\"Напредно\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg излаз\"\nBasic.Settings.Output.UseReplayBuffer=\"Укључи бафер поновног приказивања\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Максимално време сачуваног снимка\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Максимална меморија\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Процењена искоришћеност меморије: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Упозорење: Апроксимирана употреба меморије од %1 мегабајта је више него препоручени максимум од %2 мегабајта\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Немогуће је проценити искоришћеност меморије. Молимо подесите максималну границу меморије.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Префикс имена фајла бафера репродуковања\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Суфикс\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Бафер за сачувани снимак се не може користити док је тип снимања подешен на Прилагођени излаз (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Путања за снимање\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Квалитет снимања\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Исто као стрим\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Високи квалитет, осредња величина датотеке\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Квалитет са неприметним разликама, велика датотека\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Квалитет без губитка, изричито велика датотека\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Упозорење: Битрејт видео стрима ће бити постављен на %1, што је горња граница за тренутно одабрани стриминг сервис.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Упозорење: Битрејт звука стрима ће бити постављен на %1, што је горња граница за тренутно одабрани стриминг сервис.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Упозорење: Снимање се не може паузирати ако је квалитет снимка подешен на „Исти као стрим”.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Упозорење: Тренутно изабрани формат снимања није компатибилан са изабраним кодером(има) стрима.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Упозорење: Снимање са софтверским енкодером другачијег квалитета у односу на стрим ће захтевати додатну процесорску снагу ако стримујете и снимате у исто време.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Упозорење: Квалитет без губитака генерише изузетно велике величине датотека! Квалитет без губитака може да користи и до 7 гигабајта простора на диску у минути при високим резолуцијама и броју кадрова у секунди. Снимање без губитака се не препоручује за дуге снимке, осим ако немате веома велику количину расположивог простора на диску. Бафер за репродукцију није доступан када се користи квалитет без губитака.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Да ли сте сигурни да желите користити квалитет без губитка?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Упозорење о квалитету без губитка!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Софтверски (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Хардвер (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Хардвер (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Хардвер (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Хардвер (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Хардвер (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Хардвер (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Хардвер (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Хардвер (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Хардвер (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Хардвер (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Софтверски (x264 ниска употреба процесора, повећава величину датотеке)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (подразумевано)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD трака (користи траку 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Аудио запис\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Некомпатибилна резолуција/број кадрова\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Ова услуга стримовања не подржава вашу тренутну излазну резолуцију и/или број фрејмова у секунди. Биће промењени на најближу компатибилну вредност:\\n\\n%1\\n\\nДа ли желите да наставите?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Резолуција: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"ФПС: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Некомпатибилни енкодер\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Стриминг сервис „%1“ не подржава енкодер „%2“. Енкодер ће бити промењен у „%3“.\\n\\nДа ли желите да наставите?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Стриминг сервис „%1“ не подржава енкодере „%2“ и „%3“. Ови енкодери ће бити промењени у „%4“ и „%5“.\\n\\nДа ли желите да наставите?\"\nBasic.Settings.Output.VideoBitrate=\"Проток видеа\"\nBasic.Settings.Output.AudioBitrate=\"Проток звука\"\nBasic.Settings.Output.Reconnect=\"Аутоматски повежи поново\"\nBasic.Settings.Output.RetryDelay=\"Кашњење поновног покушаја\"\nBasic.Settings.Output.MaxRetries=\"Максималан број поновних покушаја\"\nBasic.Settings.Output.Advanced=\"Омогући подешавања прилагођеног енкодера (напредно)\"\nBasic.Settings.Output.EncoderPreset=\"Предефинисано подешавање енкодера\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (ниска потрошња процесора, најнижи квалитет)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (подразумевано) (средња употреба процесора, стандардни квалитет)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (велика потрошња процесора, висок квалитет)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Прилагођена подешавања енкодера\"\nBasic.Settings.Output.CustomMuxerSettings=\"Посебна подешавања муксера\"\nBasic.Settings.Output.NoSpaceFileName=\"Створи име датотеке без размака\"\nBasic.Settings.Output.Adv.Rescale=\"Скалирај излаз\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Онемогућено\"\nBasic.Settings.Output.Adv.AudioTrack=\"Звучни извор\"\nBasic.Settings.Output.Adv.Streaming=\"Стримовање\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Подешавања стримовања\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Извор 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Извор 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Извор 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Извор 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Трака 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Трака 6\"\nBasic.Settings.Output.Adv.Encoder=\"Подешавања енкодера\"\nBasic.Settings.Output.Adv.Recording=\"Снимање\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Подешавања снимања\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Тип снимања\"\nBasic.Settings.Output.Adv.Recording.Type=\"Врста\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Уобичајени\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Прилагођени излаз (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Користи стрим енкодер)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Обликовање имена датотеке\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Препиши ако постоји датотека\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Прилагођени излаз (FFmpeg) је обезбеђен без икаквих заштитних мера.\\nРазмислите о коришћењу типа снимања „Стандардно“.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg врста исписа\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Испис на URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Испис у датотеку\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Уобичајени формати снимака\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Све датотеке\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Путања датотеке или URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Формат контејнера\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Звук\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Видео\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Подразумевани формат\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Опис формата контејнера\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Звучни/Видео кодек одређен путањом датотеке или URL-a\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Подразумевани енкодер\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Онемогући енкодер\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Видео енкодер\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Подешавања видео енкодера (ако постоје)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Звучни енкодер\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Подешавања звучног енкодера (ако постоје)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Подешавања muxer-а (ако постоје)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Интервал кључних фрејмова (фрејмови)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Прикажи све кодеке (чак иако су потенцијално некомпатибилни)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Подешавања FFmpeg-а\"\nBasic.Settings.Output.EnableSplitFile=\"Аутоматско дељење датотека\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Подели по времену\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Подели по величини\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Поделите само ручно\"\nBasic.Settings.Output.SplitFile.Time=\"Време подела\"\nBasic.Settings.Output.SplitFile.Size=\"Величина поделе\"\nScreenshot=\"Излаз екрана\"\nScreenshot.SourceHotkey=\"Снимак екрана изабраног извора\"\nScreenshot.StudioProgram=\"Сачувај снимак екрана програма\"\nScreenshot.Preview=\"Сачувај преглед екрана\"\nScreenshot.Scene=\"Сачувај снимак екрана сцене\"\nScreenshot.Source=\"Сачувај изворни снимак екрана\"\nFilenameFormatting.TT.CCYY=\"Година, четири цифре\"\nFilenameFormatting.TT.YY=\"Година, последње две цифре (00-99)\"\nFilenameFormatting.TT.MM=\"Месец као децимални број (01-12)\"\nFilenameFormatting.TT.DD=\"Дан у месецу, допуњен нулама (01-31)\"\nFilenameFormatting.TT.hh=\"Сат у 24-часовном формату (00-23)\"\nFilenameFormatting.TT.mm=\"Минут (00-59)\"\nFilenameFormatting.TT.ss=\"Други (00-59)\"\nFilenameFormatting.TT.Percent=\"Знак % s\"\nFilenameFormatting.TT.a=\"Скраћени назив дана у недељи\"\nFilenameFormatting.TT.A=\"Пун назив дана у недељи\"\nFilenameFormatting.TT.b=\"Скраћени назив месеца\"\nFilenameFormatting.TT.B=\"Пуно име месеца\"\nFilenameFormatting.TT.d=\"Дан у месецу, допуњен нулама (01-31)\"\nFilenameFormatting.TT.H=\"Сат у 24-часовном формату (00-23)\"\nFilenameFormatting.TT.I=\"Сат у 12-часовном формату (01-12)\"\nFilenameFormatting.TT.m=\"Месец као децимални број (01-12)\"\nFilenameFormatting.TT.M=\"Минут (00-59)\"\nFilenameFormatting.TT.p=\"Ознака AM или PM\"\nFilenameFormatting.TT.s=\"Време у секундама од UNIX епохе\"\nFilenameFormatting.TT.S=\"Други (00-59)\"\nFilenameFormatting.TT.y=\"Година, последње две цифре (00-99)\"\nFilenameFormatting.TT.Y=\"Година\"\nFilenameFormatting.TT.z=\"ISO 8601 померање у односу на UTC у временској зони\"\nFilenameFormatting.TT.Z=\"Назив или скраћеница временске зоне\"\nFilenameFormatting.TT.FPS=\"Кадрова у секунди\"\nFilenameFormatting.TT.CRES=\"Основна (платна) резолуција\"\nFilenameFormatting.TT.ORES=\"Излазна (скалирана) резолуција\"\nFilenameFormatting.TT.VF=\"Видео формат\"\nBasic.Settings.Video=\"Видео\"\nBasic.Settings.Video.Adapter=\"Видео адаптер\"\nBasic.Settings.Video.BaseResolution=\"Основна (површинска) резолуција\"\nBasic.Settings.Video.ScaledResolution=\"Излазна (скалирана) резолуција\"\nBasic.Settings.Video.DownscaleFilter=\"Филтер за скалирање\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Резолуције се поклапају, није потребно смањивање]\"\nBasic.Settings.Video.FPS=\"ФПС\"\nBasic.Settings.Video.FPSCommon=\"Уобичајене FPS вредности\"\nBasic.Settings.Video.FPSInteger=\"Бројевна FPS вредност\"\nBasic.Settings.Video.FPSFraction=\"Бројевна FPS вредност са децималама\"\nBasic.Settings.Video.Numerator=\"Бројилац\"\nBasic.Settings.Video.Denominator=\"Именилац\"\nBasic.Settings.Video.Renderer=\"Рендерер\"\nBasic.Settings.Video.InvalidResolution=\"Неисправна вредност резолуције. Мора бити [width]x[height] (нпр. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Видео излаз је тренутно активан. Молим искључите све излазе да промените видео подешавања.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (најбрже, али мутно при скалирању)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (оштрије скалирање, 16 узорака)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (оштрије скалирање, 36 узорка)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Површина (пондерисана сума, 4/6/9 узорака)\"\nBasic.Settings.Audio=\"Звук\"\nBasic.Settings.Audio.SampleRate=\"Проток\"\nBasic.Settings.Audio.Channels=\"Канали\"\nBasic.Settings.Audio.Meters=\"Метара\"\nBasic.Settings.Audio.MeterDecayRate=\"Стопа пропадања\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Брза\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Средња (Tип I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Спора (Tип II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Тип мерача врха\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Врх узорка\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Прави врх (већа употреба процесора)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"УПОЗОРЕЊЕ: Звук окружења је укључен.\"\nBasic.Settings.Audio.MultichannelWarning=\"Ако стримујете, проверите да ли ваш сервис за стримовање подржава и унос и репродукцију окружујућег звука. На пример, Facebook 360 Live у потпуности подржава окружујући звук; YouTube Live подржава унос 5.1 звука (и репродукцију на телевизорима).\\n\\nOBS аудио филтери су компатибилни са окружујућим звуком, иако подршка за VST додатке није гарантована.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Да ли желите да укључите звук окружења?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Да ли сте сигурни да желите да укључите звук окружења?\"\nBasic.Settings.Audio.Devices=\"Глобални аудио уређаји\"\nBasic.Settings.Audio.DesktopDevice=\"Звук са рачунара\"\nBasic.Settings.Audio.DesktopDevice2=\"Звук са рачунара 2\"\nBasic.Settings.Audio.AuxDevice=\"Микрофон/Улаз\"\nBasic.Settings.Audio.AuxDevice2=\"Микрофон/Улаз 2\"\nBasic.Settings.Audio.AuxDevice3=\"Микрофон/Улаз 3\"\nBasic.Settings.Audio.AuxDevice4=\"Микрофон/Улаз 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Омогући стисни-за-мутирање\"\nBasic.Settings.Audio.PushToMuteDelay=\"Кашњење опције стисни-за-мутирање\"\nBasic.Settings.Audio.EnablePushToTalk=\"Омогући Стисни-за-говор\"\nBasic.Settings.Audio.PushToTalkDelay=\"Кашњење опције стисни-за-говор\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Уређај није повезан или није доступан]\"\nBasic.Settings.Audio.Disabled=\"Онемогућено\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Режим баферовања звука са малом латенцијом (за Decklink/NDI излазе)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"УПОЗОРЕЊЕ: Омогућено је баферовање звука са малом латенцијом.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Режим баферовања звука са малом латенцијом може проузроковати грешке у репродукцији звука или прекид репродукције из неких извора.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Омогућити режим баферовања звука са малом латенцијом?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Јесте ли сигурни да желите да омогућите режим аудио баферовања са малим кашњењем?\"\nBasic.Settings.Accessibility=\"Приступачност\"\nBasic.Settings.Accessibility.ColorOverrides=\"Користите различите боје\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Оквир око извора (Избор)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Оквир око извора (Исеци)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Оквир око извора (Прелаз мишем)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Јачина звука (од -60 до -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Јачина звука (од -20 до -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Јачина звука (од -9 до 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Јачина звука (од -60 до -20dB) (Активно)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Јачина звука (од -20 до -9dB) (Активно)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Јачина звука (од -9 до 0dB) (Активно)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Палета боја\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Подразумевана\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Прилагођена\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Алтернатива за далтонисте\"\nBasic.Settings.Advanced=\"Напредно\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Прикажи упозорење о активним излазима при изласку\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Приоритет процеса\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Високи\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Изнад нормалног\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Нормални\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Нижи од нормалног\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Беспослени\"\nBasic.Settings.Advanced.FormatWarning=\"Упозорење: Формати боја, са изузетком NV12/P010 су првенствено предвиђени за снимање, и нису препоручљиви приликом стримовања. Стримовање може проузроковати повећану употребу процесорских ресурса због конверзије формата боја.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Упозорење: Високопрецизни формати се чешће користе са HDR просторима боја.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Упозорење: Rec. 2100 би требало да користи формат са већом прецизношћу.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Формат боја\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (осмобитни, 4:2:0, 2 равни)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (осмобитни, 4:2:0, 3 равни)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (осмобитни, 4:4:4, 3 равни)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (десетобитни, 4:2:0, 2 равни)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (десетобитни, 4:2:0, 3 равни)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (шеснаестобитни, 4:2:2, 2 равни)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (шеснаестобитни, 4:4:4, 2 равни)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (осмобитни)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Распон боја\"\nBasic.Settings.Advanced.Video.ColorRange=\"Опсег боја\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Ограничено\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Потпуно\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR ниво белине\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Номинални вршни ниво HDR-а\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Уређај за надзор\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Подразумевано\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Искључи Windows audio ducking\"\nBasic.Settings.Advanced.StreamDelay=\"Кашњење стримовања\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Трајање\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Сачувај тачку прекида (повећај кашњење) приликом поновног повезивања\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Процењена употреба меморије: %1 MB\"\nBasic.Settings.Advanced.Network=\"Мрежа\"\nBasic.Settings.Advanced.Network.Disabled=\"Тренутно одабрани стриминг протокол не подржава мењање мрежних подешавања.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Вежи се за IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"ИП породица\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Омогући мрежне оптимизације\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Омогући TCP пејсинг\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Покушај да се RTMP излаз учини компатибилнијим са другим апликацијама осетљивим на кашњење, кроз регулацију стопе преноса.\\nМоже повећати ризик од испуштених слика и довести до „сецкања” приликом стриминга на нестабилним конекцијама.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Понашање пречица тастатуре\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Никад не искључуј пречице тастатуре\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Искључи пречице када је главни прозор у фокусу\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Искључи пречице тастатуре када прозор није у фокусу\"\nBasic.Settings.Advanced.AutoRemux=\"Аутоматски ремукс у %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(сними као mkv)\"\nBasic.AdvAudio=\"Напредна подешавања звука\"\nBasic.AdvAudio.ActiveOnly=\"Само активни извори\"\nBasic.AdvAudio.Name=\"Име\"\nBasic.AdvAudio.Volume=\"Јачина звука\"\nBasic.AdvAudio.VolumeSource=\"Јачина звука за „%1”\"\nBasic.AdvAudio.Mono=\"Моно\"\nBasic.AdvAudio.MonoSource=\"Моно даунмикс за '%1'\"\nBasic.AdvAudio.Balance=\"Баланс\"\nBasic.AdvAudio.BalanceSource=\"Стање за '%1'\"\nBasic.AdvAudio.SyncOffset=\"Разлика у синхронизацији\"\nBasic.AdvAudio.SyncOffsetSource=\"Разлика у синхронизацији за „%1”\"\nBasic.AdvAudio.Monitoring=\"Аудио надзор\"\nBasic.AdvAudio.Monitoring.None=\"Искључи надзор\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Само надзор (излаз без звука)\"\nBasic.AdvAudio.Monitoring.Both=\"Надзор и излаз\"\nBasic.AdvAudio.MonitoringSource=\"Аудио надзор за „%1”\"\nBasic.AdvAudio.AudioTracks=\"Извори\"\nBasic.Settings.Hotkeys=\"Пречице\"\nBasic.Settings.Hotkeys.Pair=\"Комбинације тастера дељене са '%1' се понашају као прекидачи\"\nBasic.Settings.Hotkeys.Filter=\"Филтер\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Филтрирај према пречици\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Ова пречица се дели са једном или више других радњи, кликните за приказ сукобљених радњи\"\nBasic.Settings.Hotkeys.PleaseWait=\"Учитавање пречица тастатуре, молимо сачекајте...\"\nBasic.Hotkeys.SelectScene=\"Пребаци на сцену\"\nBasic.SystemTray.Show=\"Прикажи\"\nBasic.SystemTray.Hide=\"Сакриј\"\nBasic.SystemTray.Message.Reconnecting=\"Веза прекинута. Поновно успостављање...\"\nHotkeys.Insert=\"Убаци\"\nHotkeys.Delete=\"Обриши\"\nHotkeys.Home=\"Почетна\"\nHotkeys.End=\"Крај\"\nHotkeys.PageUp=\"Страница нагоре\"\nHotkeys.PageDown=\"Страница надоле\"\nHotkeys.NumLock=\"Нум Лок\"\nHotkeys.ScrollLock=\"Закључавање померања\"\nHotkeys.Backspace=\"Brisanje\"\nHotkeys.Tab=\"Картица\"\nHotkeys.Print=\"Штампај\"\nHotkeys.Pause=\"Пауза\"\nHotkeys.Left=\"Стрелица лево\"\nHotkeys.Right=\"Стрелица десно\"\nHotkeys.Up=\"Стрелица горе\"\nHotkeys.Down=\"Стрелица доле\"\nHotkeys.Windows=\"Прозори\"\nHotkeys.Super=\"Супер\"\nHotkeys.Menu=\"Мени\"\nHotkeys.Space=\"Размак\"\nHotkeys.NumpadNum=\"Нумерички %1\"\nHotkeys.NumpadMultiply=\"Нумеричка звездица\"\nHotkeys.NumpadDivide=\"Нумеричка коса црта\"\nHotkeys.NumpadAdd=\"Нумерички плус\"\nHotkeys.NumpadSubtract=\"Нумерички минус\"\nHotkeys.NumpadDecimal=\"Нумеричка тачка\"\nHotkeys.AppleKeypadNum=\"%1 (Тастатура)\"\nHotkeys.AppleKeypadMultiply=\"* (Тастатура)\"\nHotkeys.AppleKeypadDivide=\"/ (Тастатура)\"\nHotkeys.AppleKeypadAdd=\"+ (Тастатура)\"\nHotkeys.AppleKeypadSubtract=\"- (Тастатура)\"\nHotkeys.AppleKeypadDecimal=\". (Тастатура)\"\nHotkeys.AppleKeypadEqual=\"= (Тастатура)\"\nHotkeys.MouseButton=\"Миш %1\"\nHotkeys.Escape=\"Излаз\"\nMute=\"Мутирај\"\nUnmute=\"Одмутирај\"\nPush-to-mute=\"Стисни-за-мутирање\"\nPush-to-talk=\"Стисни-за-говор\"\nSceneItemShow=\"Прикажи '%1'\"\nSceneItemHide=\"Сакриј '%1'\"\nOutputWarnings.NoTracksSelected=\"Морате одабрати макар једну траку\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Грешка у подешавањима излаза\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Сви излази морају имати изабрану бар једну аудио нумеру.\"\nOutputWarnings.MP4Recording=\"Упозорење: Снимци сачувани у MP4/MOV формату неће бити могуће сачувати ако фајл не буде могао да се финализује (нпр. услед BSOD квара, нестанка струје, итд.). Ако желите да снимите више аудио записа размислите о коришћењу MKV-а и ремуксујте снимак у MP4/MOV када снимак буде био готов (Фајл → Ремуксовање снимака)\"\nOutputWarnings.CannotPause=\"Упозорење: Снимци не могу да се паузирају ако је кодер снимка подешен на „(Користи енкодер стрима)”\"\nOutputWarnings.CodecIncompatible=\"Избор аудио или видео кодера је ресетован због некомпатибилности. Изаберите компатибилни кодер са листе.\"\nCodecCompat.Incompatible=\"(Некомпатибилно са %1)\"\nCodecCompat.CodecPlaceholder=\"Изаберите енкодер...\"\nCodecCompat.ContainerPlaceholder=\"Изаберите формат...\"\nCodecCompat.CodecMissingOnExit.Title=\"Није изабран енкодер\"\nCodecCompat.CodecMissingOnExit.Text=\"Барем један видео или аудио кодер није подешен. Молимо вас да изаберете кодере и за снимање и за стримовање.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Није изабран формат\"\nCodecCompat.ContainerMissingOnExit.Text=\"Није изабран формат снимања. Изаберите формат снимања који је компатибилан са изабраним кодером стрима.\"\nFinalScene.Title=\"Обриши сцену\"\nFinalScene.Text=\"Мора да постоји барем једна сцена, како би била обрисана.\"\nNoSources.Title=\"Нема извора\"\nNoSources.Text=\"Изгледа да још увек нисте додали ни један видео извор, тако да ће се приказивати само празан екран. Да ли сте сигурни да желите да наставите?\"\nNoSources.Text.AddSource=\"Можете да додате изворе у било ком тренутку, кликом на иконицу ''+'' испод дугмета ''Извори'' у главном прозору.\"\nNoSources.Label=\"Немате ниједан извор.\\nКликните дугме + испод,\\nили додајте извор десним кликом овде.\"\nChangeBG=\"Подеси боју\"\nCustomColor=\"Персонализована боја\"\nBrowserSource.EnableHardwareAcceleration=\"Укључи извор претраживача хардверског убрзања\"\nAbout=\"О програму\"\nAbout.Info=\"OBS Studio је бесплатан софтвер отвореног кода који омогућава видео снимање и стримовање уживо.\"\nAbout.Donate=\"Дајте допринос\"\nAbout.GetInvolved=\"Учествујте\"\nAbout.Authors=\"Аутори\"\nAbout.License=\"Лиценца\"\nAbout.Error=\"Грешка! Датотека није могла бити прочитана.\\n\\nИди на: %1\"\nAbout.Contribute=\"Подржите OBS пројекат\"\nAddUrl.Title=\"Додајте извор путем URL-a\"\nAddUrl.Text=\"Превукли сте URL у OBS. Ово ће аутоматски додати тај линк као извор. Наставите?\"\nAddUrl.Text.Url=\"УРЛ: %1\"\nResizeOutputSizeOfSource=\"Промени величину излаза (величина извора)\"\nResizeOutputSizeOfSource.Text=\"Основна резолуција и резолуција излаза ће бити промењене како би одговарале величини тренутног извора.\"\nResizeOutputSizeOfSource.Continue=\"Желите ли да наставите?\"\nPreviewTransition=\"Прегледај прелаз\"\nImporter=\"Увозник колекција сцена\"\nImporter.SelectCollection=\"Изаберите колекцију сцена\"\nImporter.Collection=\"Колекција сцена\"\nImporter.HelpText=\"Додајте фајлове у овај прозор да бисте увезли колекције из OBS-a или других подржаних програма.\"\nImporter.Path=\"Путања колекције\"\nImporter.Program=\"Детектована апликација\"\nImporter.AutomaticCollectionPrompt=\"Аутоматски потражи колекције сцена\"\nImporter.AutomaticCollectionText=\"OBS може сам да пронађе увозиве колекције сцена из подржаних програма треће стране. Желите ли да OBS сам потражи колекције за Вас?\\n\\nОво подешавање можете променити касније одласком на Подешавања > Опште > Увозници.\"\nImporter.SelectFile=\"Прегледај...\"\nRestart=\"Поново покрени\"\nNeedsRestart=\"ОБС Студио треба поново покренути. Да ли желите да га поново покренете сада?\"\nLoadProfileNeedsRestart=\"Профил садржи подешавања која захтевају рестартовање OBS-a:\\n%1\\n\\nЖелите ли да рестартујете OBS да би ова подешавања ступиле на снагу?\"\nContextBar.NoSelectedSource=\"Није изабран извор\"\nContextBar.MediaControls.PlayMedia=\"Пусти медију\"\nContextBar.MediaControls.PauseMedia=\"Паузирај медије\"\nContextBar.MediaControls.StopMedia=\"Заустави медије\"\nContextBar.MediaControls.RestartMedia=\"Почни мултимедију поново\"\nContextBar.MediaControls.PlaylistNext=\"Следеће на плејлисти\"\nContextBar.MediaControls.PlaylistPrevious=\"Претходно на плејлисти\"\nContextBar.MediaControls.BlindSeek=\"Виџет за тражење мултимедије\"\nYouTube.Auth.Ok=\"Ауторизација је успешно обављена.\\nСада можете да затворите ову страницу.\"\nYouTube.Auth.NoCode=\"Процес ауторизације није обављен.\"\nYouTube.Auth.NoChannels=\"Ниједан канал није доступан на одабраном налогу\"\nYouTube.Auth.WaitingAuth.Title=\"Ауторизација Јутјуб корисника\"\nYouTube.Auth.WaitingAuth.Text=\"Молимо Вас обавите ауторизацију у екстерном претраживачу.<br>Уколико се претраживач не отвори, пратите овај линк да извршите ауторизацију:<br>%1\"\nYouTube.AuthError.Text=\"Неуспешан покушај прикупљања информација о каналу. %1.\"\nYouTube.Actions.WindowTitle=\"Поставке Јутјуб преноса - Канал: %1\"\nYouTube.Actions.CreateNewEvent=\"Направи нови пренос\"\nYouTube.Actions.ChooseEvent=\"Изабери постојећи пренос\"\nYouTube.Actions.Title=\"Назив\"\nYouTube.Actions.MyBroadcast=\"Мој пренос\"\nYouTube.Actions.Description=\"Опис\"\nYouTube.Actions.Privacy=\"Приватност\"\nYouTube.Actions.Privacy.Private=\"Приватно\"\nYouTube.Actions.Privacy.Public=\"Јавно\"\nYouTube.Actions.Privacy.Unlisted=\"Неизлистано\"\nYouTube.Actions.Category=\"Категорија\"\nYouTube.Actions.Thumbnail=\"Сличица\"\nYouTube.Actions.Thumbnail.SelectFile=\"Изаберите датотеку...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Ниједан фајл није изабран\"\nYouTube.Actions.Thumbnail.ClearFile=\"Очисти\"\nYouTube.Actions.MadeForKids=\"Да ли је овај видео направљен за децу?*\"\nYouTube.Actions.MadeForKids.Yes=\"Да, направљено је за децу\"\nYouTube.Actions.MadeForKids.No=\"Не, није направљено за децу\"\nYouTube.Actions.AdditionalSettings=\"Додатна подешавања\"\nYouTube.Actions.Latency=\"Одзив\"\nYouTube.Actions.Latency.Normal=\"Нормално\"\nYouTube.Actions.Latency.Low=\"Ниско\"\nYouTube.Actions.Latency.UltraLow=\"Ултра ниско\"\nYouTube.Actions.EnableAutoStart=\"Омогући аутоматско покретање\"\nYouTube.Actions.EnableAutoStop=\"Омогући аутоматско заустављање\"\nYouTube.Actions.AutoStartStop.TT=\"Указује на то да ли овај заказани пренос треба да почне аутоматски\"\nYouTube.Actions.EnableDVR=\"Омогући DVR\"\nYouTube.Actions.360Video=\"360 видео\"\nYouTube.Actions.ScheduleForLater=\"Закажи за касније\"\nYouTube.Actions.RememberSettings=\"Запамти ова подешавања\"\nYouTube.Actions.Create_Ready=\"Направи пренос\"\nYouTube.Actions.Create_GoLive=\"Направи пренос и започни стрим\"\nYouTube.Actions.Choose_Ready=\"Изабери пренос\"\nYouTube.Actions.Choose_GoLive=\"Изабери пренос и започни стрим\"\nYouTube.Actions.Create_Schedule=\"Закажи пренос\"\nYouTube.Actions.Create_Schedule_Ready=\"Закажи и изабери пренос\"\nYouTube.Actions.Dashboard=\"Отвори YouTube студио\"\nYouTube.Actions.Error.Title=\"Грешка приликом прављења преноса уживо\"\nYouTube.Actions.Error.Text=\"Грешка приликом приступа Јутјубу „%1”.<br/>Детаљан опис грешке можете погледати на <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Грешка приликом приступа Јутјубу. Молимо Вас проверите мрежну везу или да ли имате приступ Јутјуб серверу.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Грешка приликом прављења преноса „%1”.<br/>Детаљан опис грешке можете погледати на <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Ниједан стрим није направљен. Молимо Вас поново повежите свој налог.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API грешка. Молимо Вас погледајте фајл извештаја за више информација.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Изабрани пренос није пронађен.\"\nYouTube.Actions.Error.FileMissing=\"Изабрана датотека не постоји.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Неуспешно отварање изабраног фајла.\"\nYouTube.Actions.Error.FileTooLarge=\"Изабрани фајл је превише велики (Ограничење: 2 МиБ)\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Премештање преноса није успело: %1<br/><br/>Уколико се ова грешка понови <a href='https://studio.youtube.com/video/%2/livestreaming'>отворите пренос у Јутјуб студију</a> и покушајте ручно.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Пренос се пребацује у тест-фазу, ово може потрајати неко време. Молимо Вас покушајте поново за 10-30 секунди.\"\nYouTube.Actions.EventsLoading=\"Учитавање листе догађаја...\"\nYouTube.Actions.EventCreated.Title=\"Догађај направљен\"\nYouTube.Actions.EventCreated.Text=\"Догађај је успешно направљен.\"\nYouTube.Actions.Stream=\"Пренос\"\nYouTube.Actions.Stream.ScheduledFor=\"Заказано за %1\"\nYouTube.Actions.Stream.Resume=\"Настави прекинути стрим\"\nYouTube.Actions.Stream.YTStudio=\"Аутоматски направљено од стране Јутјуб студија\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Прављење новог преноса уживо, молимо Вас сачекајте...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Захтева ручно покретање\"\nYouTube.Actions.AutoStartStreamingWarning=\"Аутоматски почетак је онемогућен за овај догађај, кликните \\\"Go live\\\" да бисте започели свој пренос.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Нећете моћи да се поново повежете.<br>Ваш стрим ће се зауставити и више нећете бити уживо.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Стриминг уживо није омогућен на одабраном Јутјуб каналу.<br/><br/>Погледајте <a href='https://www.youtube.com/features'>youtube.com/features</a> за више информација.\"\nYouTube.Errors.livePermissionBlocked=\"Стриминг уживо није омогућен на одабраном Јутјуб каналу.<br/>Молимо Вас имајте на уму да може бити потребно и до 24 сата да стриминг уживо постане доступан након што сте га омогућили у подешавањима канала.<br/><br/>Погледајте <a href='https://www.youtube.com/features'>youtube.com/features</a> за детаље.\"\nYouTube.Errors.errorExecutingTransition=\"Пребацивање није успело због грешке на серверу. Молимо Вас покушајте поново за пар секунди.\"\nYouTube.Errors.errorStreamInactive=\"Јутјуб не прима податке за Ваш стрим. Молимо Вас проверите своју конфигурацију и покушајте поново.\"\nYouTube.Errors.invalidTransition=\"Покушано пребацивање није било валидно. Ово може бити због тога што стрим није завршио претходно пребацивање. Молимо Вас сачекајте пар секунди па покушајте поново.\"\nYouTube.DocksRemoval.Title=\"Обриши застареле докове YouTube прегледача\"\nYouTube.DocksRemoval.Text=\"Ови докови прегледача биће уклоњени као застарели:\\n\\n%1\\nУместо тога користите „Докови/Контролна соба уживо на YouTube-у“.\"\nConfigDownload.WarningMessageTitle=\"Упозорење\"\nFailedToStartStream.MissingConfigURL=\"Нема доступних URL адреса за конфигурацију за тренутну услугу\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Прилагођени RTMP URL није наведен\"\nFailedToStartStream.InvalidCustomConfig=\"Неважећа прилагођена конфигурација\"\nFailedToStartStream.MissingCanvas=\"Недостаје конфигурисано додатно платно\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Није успело креирање вишеканалне видео услуге\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Није успело креирање RTMP излаза за вишеканални видео\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC није доступан.\\n\\nНије пронађен тип енкодера '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Није успело креирање видео кодера '%1' (тип: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Није успело преузимање информација о OBS видеу приликом креирања кодера '%1' (тип: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Није успело креирање аудио кодера\"\nFailedToStartStream.NoRTMPURLInConfig=\"Конфигурација не садржи RTMP(S) URL циља стрима\"\nFailedToStartStream.FallbackToDefault=\"Покретање стрима помоћу %1 failed; да ли желите да покушате поново користећи подешавања једног кодирања?\"\nFailedToStartStream.ConfigRequestFailed=\"Није могуће преузети конфигурацију из %1<br><br> HTTP грешка: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Примљена непозната вредност статуса '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br> \\nДа ли желите да наставите стримовање без %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br> Да ли желите да наставите са стримовањем?\"\nFailedToStartStream.MissingEncoderConfigs=\"Конфигурација за објављивање није укључивала конфигурације енкодера\"\nFailedToStartStream.InvalidEncoderConfig=\"Конфигурација за објављивање је садржала неважећу конфигурацију енкодера\"\nFailedToStartStream.StatusMissingHTML=\"Захтев за објављивање је вратио неодређену грешку\"\nFailedToStartStream.NoConfigSupplied=\"Недостаје конфигурација\"\nMultitrackVideo.Info=\"%1 аутоматски оптимизује ваша подешавања за кодирање и слање више квалитета видеа. Избором ове опције послаћете %2 информације о вашем рачунару и подешавањима софтвера.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Некомпатибилна подешавања\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 тренутно није компатибилан са:\\n\\n%2\\nДа бисте наставили стримовање са %1, онемогућите некомпатибилна подешавања:\\n\\n%3\\nи поново почните стримовање.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Онемогући за овај стрим и почни стримовање\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Ажурирајте подешавања и почните стримовање\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 тренутно није компатибилан са [Аудио → Опште → Канали] подешеним на '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Аудио → Опште → Канали] треба да буде подешено на '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 захтева више различитих подешавања за [Аудио → Опште → Канали]\"\nBasic.OpenPluginManager=\"Управљач додацима\"\nPluginManager=\"Управљач додацима\"\nPluginManager.Restart=\"OBS Studio мора бити поново покренут да би се примениле промене додатка\"\nPluginManager.MissingPlugin=\"[ДОДАТАК НИЈЕ ПРОНАЂЕН]\"\nPluginManager.Section.Discover=\"Прегледај\"\nPluginManager.Section.Manage=\"Инсталирано\"\nPluginManager.Section.Updates=\"Ажурирања\"\n"
  },
  {
    "path": "frontend/data/locale/sv-SE.ini",
    "content": "Language=\"Svenska\"\nApply=\"Verkställ\"\nCancel=\"Avbryt\"\nClose=\"Stäng\"\nSave=\"Spara\"\nDiscard=\"Ignorera\"\nDisable=\"Inaktivera\"\nYes=\"Ja\"\nNo=\"Nej\"\nAdd=\"Lägg till\"\nRemove=\"Ta bort\"\nRename=\"Byt namn\"\nInteract=\"Interagera\"\nFilters=\"Filter\"\nProperties=\"Egenskaper\"\nMoveUp=\"Flytta upp\"\nMoveDown=\"Flytta ned\"\nSettings=\"Inställningar\"\nDisplay=\"Bildskärm\"\nName=\"Namn\"\nExit=\"Avsluta\"\nMixer=\"Ljudmixer\"\nBrowse=\"Bläddra\"\nDroppedFrames=\"Förorade bildrutor %1 (%2 %)\"\nProjector.Open.Program=\"Öppna programprojektor\"\nProjector.Open.Preview=\"Öppna förhandsgranskningsprojektor\"\nProjector.Open.Scene=\"Öppna scenprojektor\"\nProjector.Open.Source=\"Öppna källprojektor\"\nProjector.Open.Multiview=\"Öppna multivy\"\nProjector.Display=\"Bildskärm: %1\"\nProjector.Window=\"Nytt fönster\"\nProjector.Title=\"Projektor\"\nProjector.Title.Scene=\"Scen: %1\"\nProjector.Title.Source=\"Källa: %1\"\nProjector.Title.Multiview=\"Multivy\"\nProjector.ResizeWindowToContent=\"Anpassa fönstret till innehållet\"\nClear=\"Rensa\"\nRevert=\"Nollställ\"\nShow=\"Visa\"\nHide=\"Dölj\"\nUnhideAll=\"Visa alla\"\nUntitled=\"Namnlös\"\nNew=\"Ny\"\nDuplicate=\"Duplicera\"\nEnable=\"Aktivera\"\nDisableOSXVSync=\"Inaktivera macOS V-Sync\"\nResetOSXVSyncOnExit=\"Återställ macOS V-Sync vid avslutning\"\nHighResourceUsage=\"Kodning överbelastad! Överväg att dra ned videoinställningar eller använd en snabbare kodarförinställning.\"\nTransition=\"Övergång\"\nQuickTransitions=\"Snabba övergångar\"\nFadeToBlack=\"Tona till svart\"\nLeft=\"Vänster\"\nRight=\"Höger\"\nTop=\"Överkant\"\nBottom=\"Nederkant\"\nReset=\"Återställ\"\nHours=\"timmar\"\nMinutes=\"minuter\"\nSeconds=\"sekunder\"\nDeprecated=\"Föråldrade\"\nReplayBuffer=\"Reprisbuffert\"\nImport=\"Importera\"\nExport=\"Exportera\"\nCopy=\"Kopiera\"\nPaste=\"Klistra in\"\nPasteReference=\"Klistra in (referens)\"\nPasteDuplicate=\"Klistra in (dubblett)\"\nRemuxRecordings=\"Remuxa inspelningar\"\nNext=\"Nästa\"\nBack=\"Tillbaka\"\nDefaults=\"Standardvärden\"\nRestoreDefaults=\"Standardvärden\"\nHideMixer=\"Dölj i mixer\"\nTransitionOverride=\"Övergångsåsidosättande\"\nShowTransition=\"Visa övergång\"\nHideTransition=\"Dölj övergång\"\nNone=\"Ingen\"\nStudioMode.Preview=\"Förhandsvisning\"\nStudioMode.PreviewSceneName=\"Förhandsvisning: %1\"\nShowInMultiview=\"Visa i multivy\"\nVerticalLayout=\"Vertikal layout\"\nGroup=\"Grupp\"\nDoNotShowAgain=\"Visa inte igen\"\nDefault=\"(Standard)\"\nCalculating=\"Beräknar...\"\nFullscreen=\"Fullskärm\"\nWindowed=\"Fönsterläge\"\nRefreshBrowser=\"Uppdatera\"\nAspectRatio=\"Bildförhållande <b>%1:%2</b>\"\nLockVolume=\"Lås volym\"\nLogViewer=\"Loggvisare\"\nShowOnStartup=\"Visa vid uppstart\"\nOpenFile=\"Öppna fil\"\nAddScene=\"Lägg till scen\"\nAddSource=\"Lägg till källa\"\nRemoveScene=\"Ta bort markerade scener\"\nRemoveSource=\"Ta bort markerade källor\"\nMoveSceneUp=\"Flytta scen uppåt\"\nMoveSceneDown=\"Flytta scen nedåt\"\nMoveSourceUp=\"Flytta källor uppåt\"\nMoveSourceDown=\"Flytta källor nedåt\"\nSourceProperties=\"Öppna källegenskaper\"\nSourceFilters=\"Öppna källfilter\"\nMixerToolbarMenu=\"Ljudmixermeny\"\nSceneFilters=\"Öppna scenfilter\"\nList=\"Lista\"\nGrid=\"Rutnät\"\nAutomatic=\"Automatisk\"\nComingSoon=\"Kommer snart\"\nPluginsFailedToLoad.Title=\"Fel vid inläsning av insticksprogram\"\nPluginsFailedToLoad.Text=\"Följande OBS-insticksprogram kunde inte läsas in:\\n\\n%1\\nVar god uppdatera eller ta bort dessa insticksprogram.\"\nAlreadyRunning.Title=\"OBS körs redan\"\nAlreadyRunning.Text=\"OBS körs redan! Såvida du gjorde detta med flit, stäng ned alla befintliga instanser av OBS innan du försöker köra en ny instans. Om du har minimerat OBS till systemfältet, kontroller om det fortfarande körs där.\"\nAlreadyRunning.LaunchAnyway=\"Kör ändå\"\nCrashHandling.Dialog.Title=\"Krasch i OBS Studio upptäcktes\"\nCrashHandling.Labels.Text=\"OBS Studio avslutades inte ordentligt.\\n\\nVill du starta i felsäkert läge (insticksprogram från tredje part, skript och WebSockets inaktiveras)?\"\nCrashHandling.Labels.PrivacyNotice=\"Du kan också automatiskt ladda upp den senaste kraschrapporten till OBSProject.<br /><br />Läs <a href='https://obsproject.com/privacy-policy'>sekretesspolicyn</a> innan du laddar upp filer och var särskilt uppmärksam på delarna som rör filuppladdningar.\"\nCrashHandling.Checkbox.SendReport=\"Jag har läst sekretesspolicyn och samtycker till uppladdningen.\"\nCrashHandling.Buttons.LaunchSafe=\"Starta i säkert läge\"\nCrashHandling.Buttons.LaunchNormal=\"Starta i normalt läge\"\nCrashHandling.Errors.UploadJSONError=\"Ett fel uppstod när den senaste kraschloggen skulle laddas upp. Försök igen senare.\"\nCrashHandling.Errors.Title=\"Fel under uppladdning av kraschlogg\"\nSafeMode.Restart=\"Vill du starta om OBS i säkert läge (insticksprogram från tredje part, skript och WebSockets inaktiveras)?\"\nSafeMode.RestartNormal=\"Vill du starta om OBS i normalt läge?\"\nChromeOS.Title=\"Plattformen stöds inte\"\nChromeOS.Text=\"OBS verkar köras inuti en ChromeOS-behållare. Denna plattform stöds inte.\"\nWine.Title=\"Wine upptäcktes\"\nWine.Text=\"Stöd saknas för att köra OBS i Wine och många funktioner som att fånga eller enhetskällor komer inte fungera eller endast i begränsad kapacitet.<br><br>Det rekommenderas att istället köra en inbyggd version av OBS, till exempel <a href='https://flathub.org/apps/details/com.obsproject.Studio'>vår Flatpak-version</a> eller paketen för ditt operativsystem.\"\nDockCloseWarning.Title=\"Stäng flytande fönster\"\nDockCloseWarning.Text=\"Du stängde precis ett flytande fönster. Använd menyn Flytande fönster i menyfältet om du vill visa det igen.\"\nExtraBrowsers=\"Anpassade flytande webbläsarfönster\"\nExtraBrowsers.Info=\"Lägg till flytande fönster genom att ge dem ett namn och en webbadress, klicka sedan på Verkställ eller Stäng för att öppna fönstren. Du kan lägga till eller ta bort flytande fönster när som helst.\"\nExtraBrowsers.DockName=\"Namn på flytande fönster\"\nAuth.Authing.Title=\"Autentiserar...\"\nAuth.Authing.Text=\"Autentiserar med %1, var god vänta...\"\nAuth.AuthFailure.Title=\"Autentiseringsfel\"\nAuth.AuthFailure.Text=\"Misslyckades att autentisera med %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Autentisering krävs\"\nAuth.InvalidScope.Text=\"Autentiseringskraven för %1 har ändrats. Vissa funktioner kanske inte är tillgängliga.\"\nAuth.LoadingChannel.Title=\"Läser in kanalinformation...\"\nAuth.LoadingChannel.Text=\"Läser in kanalinformation för %1, var god vänta...\"\nAuth.LoadingChannel.Error=\"Kunde inte hämta kanalinformation.\"\nAuth.ChannelFailure.Title=\"Misslyckades att läsa in kanal\"\nAuth.ChannelFailure.Text=\"Misslyckades att läsa in kanalinformation för %1\\n\\n%2: %3\"\nAuth.Chat=\"Chatt\"\nAuth.StreamInfo=\"Ströminformation\"\nTwitchAuth.Stats=\"Twitch-statistik\"\nTwitchAuth.Feed=\"Twitch-aktivitetsflöde\"\nTwitchAuth.TwoFactorFail.Title=\"Kunde inte begära strömnyckel\"\nTwitchAuth.TwoFactorFail.Text=\"OBS kunde inte ansluta till ditt Twitch-konto. Se till att tvåfaktorsautentiseringen har ställts in i <a href='https://www.twitch.tv/settings/security'>säkerhetsinställningarna på Twitch</a> då detta är nödvändigt för att kunna strömma.\"\nRestreamAuth.Channels=\"Restream-kanaler\"\nCopy.Filters=\"Kopiera filter\"\nPaste.Filters=\"Klistra in filter\"\nBrowserPanelInit.Title=\"Initierar webbläsare...\"\nBrowserPanelInit.Text=\"Initierar webbläsare, var god vänta...\"\nBandwidthTest.Region.US=\"USA\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Asien\"\nBandwidthTest.Region.Other=\"Övrigt\"\nBasic.AutoConfig=\"Automatisk konfigurationsguide\"\nBasic.AutoConfig.ApplySettings=\"Verkställ inställningar\"\nBasic.AutoConfig.StartPage=\"Användningsinformation\"\nBasic.AutoConfig.StartPage.SubTitle=\"Ange vad du vill använda programmet för\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Optimera för strömning, inspelning sekundärt\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Optimera endast för inspelning, jag kommer inte att strömma\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Jag kommer bara att använda den virtuella kameran\"\nBasic.AutoConfig.VideoPage=\"Videoinställningar\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Ange videoinställningarna du vill använda\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Använd nuvarande (%1 × %2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Bildskärm %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Använd nuvarande (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"Antingen 60 eller 30, men föredra 60 när det är möjligt\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"Antingen 60 eller 30, men föredra hög upplösning\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"OBS: Kanvasens (grundens) upplösning är nödvändigtvis samma som upplösningen upp kommer att strömma eller spela in med. Din riktiga upplösning för strömning/inspelning kan skalas ned från kanvasupplösningen för att reducera användning av resurser eller krav på bithastighet.\"\nBasic.AutoConfig.StreamPage=\"Ströminformation\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Var god ange din ströminformation\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Anslut konto (rekommenderas)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Koppla från konto\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Koppla från konto?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Denna ändring kommer tillämpas direkt. Är du säker på att du vill koppla från ditt konto?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Hämta strömnyckel\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Mer information\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Använd strömnyckel\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Använd strömnyckel (avancerad)\"\nBasic.AutoConfig.StreamPage.Service=\"Tjänst\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Visa alla...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Anpassad...\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Strömnyckel\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: ange den krypterade lösenfrasen.\\nRTMP: ange nyckeln som tillhandahålls av tjänsten.\\nSRT: ange strömmens id om tjänsten använder en.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kodarnyckel\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Ägartoken\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Anslutet konto\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Uppskatta bithastighetens med ett bandbreddstest (kan ta några minuter)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Föredra hårdvarukodning\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Hårdvarukodning eliminerar den mesta processoranvändningen, men kan kräva mer bithastighet för att uppnå samma kvalitetsnivå.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Strömvarning\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Bandbreddstestet kommer att strömma slumpad videodata utan ljud till din kanal. Om det fungerar, är det rekommenderat det att tillfälligt inaktivera att spara videor av strömmar och ändra strömmen till privat tills testet är färdigt. Fortsätta?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Testa %1\"\nBasic.AutoConfig.TestPage=\"Slutgiltiga resultat\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Programmet utför nu en uppsättning tester för att uppskatta de mest idealiska inställningarna\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testet slutfördes\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Utför bandbreddstest, som kan ta några minuter...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Ingen utmatning hittades för protokollet av denna tjänst\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Ansluter till: %1 ...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Misslyckades att ansluta till en server. Kontrollera din internetanslutning och försök igen.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testar bandbredden för: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testar strömkodare, detta kan ta några minuter...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testar inspelningskodare, detta kan ta några minuter...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Misslyckades att starta kodaren\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testar %1x%2 i %3 bilder per sekund...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Strömningskodare\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Inspelningskodare\"\nBasic.AutoConfig.TestPage.Result.Header=\"Programmet har uppskattat att de här inställningar är de mest idealiska för dig:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"För att använda dessa inställningar klickar du på \\\"Verkställ inställningar\\\". För att köra guiden igen klickar du på \\\"Tillbaka\\\". För att konfigurera inställningarna manuellt klickar du på \\\"Avbryt\\\" och öppnar Inställningar.\"\nBasic.AutoConfig.Info=\"Den automatiska konfigurationsguiden kommer att avgöra de bästa inställningarna baserat på din dators specifikation och din internethastighet.\"\nBasic.AutoConfig.RunAnytime=\"Detta kan köras när som helst via verktygsmenyn.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Strömningsupplösning (skalad)\"\nBasic.Stats=\"Statistik\"\nBasic.Stats.CPUUsage=\"Processoranvändning\"\nBasic.Stats.HDDSpaceAvailable=\"Ledigt hårddiskutrymme\"\nBasic.Stats.MemoryUsage=\"Minnesanvändning\"\nBasic.Stats.AverageTimeToRender=\"Medeltid för att färdigställa bildruta\"\nBasic.Stats.SkippedFrames=\"Bildrutor som hoppades över p.g.a. kodningslagg\"\nBasic.Stats.MissedFrames=\"Bildrutor som saknas p.g.a. renderingslagg\"\nBasic.Stats.Output.Stream=\"Ström\"\nBasic.Stats.Output.Recording=\"Inspelning\"\nBasic.Stats.Status.Recording=\"Spelar in\"\nBasic.Stats.Status.Live=\"SÄNDER\"\nBasic.Stats.Status.Reconnecting=\"Återansluter\"\nBasic.Stats.Status.Inactive=\"Inaktiv\"\nBasic.Stats.Status.Active=\"Aktiv\"\nBasic.Stats.DroppedFrames=\"Förlorade bildrutor (nätverk)\"\nBasic.Stats.MegabytesSent=\"Total datautmatning\"\nBasic.Stats.Bitrate=\"Bithastighet\"\nBasic.Stats.DiskFullIn=\"Hårddisk full om (ca.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 timmar, %2 minuter\"\nBasic.Stats.ResetStats=\"Återställ statistik\"\nResetUIWarning.Title=\"Är du säker på att du vill återställa gränssnittet?\"\nResetUIWarning.Text=\"När gränssnittet återställs dölj ytterligare komponenter. Du kommer behöva ta fram dessa komponenter från menyn Flytande fönster om du vill att de ska vara synliga.\\n\\nÄr du säker på att du vill återställa gränssnittet?\"\nUpdater.Title=\"Ny uppdatering tillgänglig\"\nUpdater.Text=\"Det finns en ny tillgänglig uppdatering:\"\nUpdater.UpdateNow=\"Uppdatera nu\"\nUpdater.RemindMeLater=\"Påminn mig senare\"\nUpdater.Skip=\"Hoppa över version\"\nUpdater.NoUpdatesAvailable.Title=\"Inga tillgängliga uppdateringar\"\nUpdater.NoUpdatesAvailable.Text=\"Inga uppdateringar är tillgängliga för närvarande\"\nUpdater.BranchNotFound.Title=\"Uppdateringskanalen är borttagen\"\nUpdater.BranchNotFound.Text=\"Din valda uppdateringskanal är inte längre tillgänglig eftersom OBS har återställts till standardvärden.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Otillgänglig integritetskontroll\"\nUpdater.RepairButUpdatesAvailable.Text=\"Det är endast möjligt att kontrollera filintegriteten för den senast tillgängliga versionen. Använd Hjälp → Sök efter uppdateringar för att verifiera och uppdatera OBS-installationen.\"\nUpdater.RepairConfirm.Title=\"Bekräfta integritetskontroll\"\nUpdater.RepairConfirm.Text=\"Integritetskontrollen kommer skanna din OBS-installation för skador och ladda ned trasiga/modifierade filer på nytt. Detta kan ta ett tag.\\n\\nVill du fortsätta?\"\nUpdater.FailedToLaunch=\"Misslyckades att starta uppdateringsprogrammet\"\nQuickTransitions.SwapScenes=\"Växla mellan förhandsvisning/programscener efter övergång\"\nQuickTransitions.SwapScenesTT=\"Växlar mellan förhandsvisningen och programscener efter övergångar (om progrmamets originalscen fortfarande finns).\\nDetta kommer inte några några ändringar som har gjorts i programmets orignalscen.\"\nQuickTransitions.DuplicateScene=\"Duplicera scen\"\nQuickTransitions.DuplicateSceneTT=\"När du redigerar samma scen kan du redigera transformering/synligheten för källor utan att modifiera programutmatningen.\\nFör att redigera egenskaper för källor utan att modifiera programutmatningen aktiverar du \\\"Duplicera källor\\\".\\nNär detta värde ändras kommer den aktuella programscenen återställas (om den fortfarande finns).\"\nQuickTransitions.EditProperties=\"Duplicera källor\"\nQuickTransitions.EditPropertiesTT=\"När du redigerar samma scen kan du redigera egenskaperna för källor utan att modifiera programutmatningen.\\nDetta fungerar endast om 'Duplicera scen' är aktiverad.\\nVissa källor (som mediakällor) stöder inte detta och kan inte redigeras separat.\\nNär detta värde ändras kommer den aktuella programscenen återställas (om den fortfarande finns).\\n\\nVarning: Eftersom källor kommer att dupliceras kan detta kräva extra system- eller videoresurser.\"\nQuickTransitions.HotkeyName=\"Snabb övergång: %1\"\nBasic.AddTransition=\"Lägg till konfigurerbar övergång\"\nBasic.RemoveTransition=\"Ta bort konfigurerbar övergång\"\nBasic.TransitionProperties=\"Övergångsegenskaper\"\nBasic.SceneTransitions=\"Scenövergångar\"\nBasic.TransitionDuration=\"Varaktighet\"\nBasic.TogglePreviewProgramMode=\"Studioläge\"\nBasic.EnablePreviewProgramMode=\"Aktivera studioläge\"\nBasic.DisablePreviewProgramMode=\"Inaktivera studioläge\"\nUndo.Undo=\"Ångra\"\nUndo.Redo=\"Gör om\"\nUndo.Add=\"Lägg till \\\"%1\\\"\"\nUndo.Delete=\"Radera \\\"%1\\\"\"\nUndo.Rename=\"Byt namn på \\\"%1\\\"\"\nUndo.SceneCollection.Switch=\"Byt till \\\"%1\\\"\"\nUndo.Item.Undo=\"Ångra %1\"\nUndo.Item.Redo=\"Gör om %1\"\nUndo.Sources.Multi=\"Radera %1 källor\"\nUndo.Filters=\"Filtrera ändringar på \\\"%1\\\"\"\nUndo.Filters.Paste.Single=\"Klistra in filtret \\\"%1\\\" till \\\"%2\\\"\"\nUndo.Filters.Paste.Multiple=\"Kopiera filter från \\\"%1\\\" till \\\"%2\\\"\"\nUndo.Transform=\"Omvandla källor i \\\"%1\\\"\"\nUndo.Transform.Paste=\"Klistra in omvandling i \\\"%1\\\"\"\nUndo.Transform.Rotate=\"Rotation i \\\"%1\\\"\"\nUndo.Transform.Reset=\"Återställ omvandling i \\\"%1\\\"\"\nUndo.Transform.HFlip=\"Horisontell spegelvändning i \\\"%1\\\"\"\nUndo.Transform.VFlip=\"Vertikal spegelvändning i \\\"%1\\\"\"\nUndo.Transform.FitToScreen=\"Anpassa till skärm i \\\"%1\\\"\"\nUndo.Transform.StretchToScreen=\"Sträck ut till skärmen i \\\"%1\\\"\"\nUndo.Transform.Center=\"Centrera på skärmen i \\\"%1\\\"\"\nUndo.Transform.VCenter=\"Centrera vertikalt till skärmen i \\\"%1\\\"\"\nUndo.Transform.HCenter=\"Centrera horisontellt till skärmen i \\\"%1\\\"\"\nUndo.Volume.Change=\"Volymförändring på \\\"%1\\\"\"\nUndo.Volume.Mute=\"Stäng av ljud för \\\"%1\\\"\"\nUndo.Volume.Unmute=\"Slå på ljud för \\\"%1\\\"\"\nUndo.Balance.Change=\"Ändra ljudbalans på \\\"%1\\\"\"\nUndo.SyncOffset.Change=\"Ändra synkförskjutning av ljudet på \\\"%1\\\"\"\nUndo.MonitoringType.Change=\"Ändra ljudmonitor på \\\"%1\\\"\"\nUndo.Mixers.Change=\"Ändra ljudmixrar på \\\"%1\\\"\"\nUndo.ForceMono.On=\"Aktivera tvingad mono för \\\"%1\\\"\"\nUndo.ForceMono.Off=\"Inaktivera tvingad mono för \\\"%1\\\"\"\nUndo.Properties=\"Egenskapsändring för \\\"%1\\\"\"\nUndo.Scene.Duplicate=\"Duplicera scen \\\"%1\\\"\"\nUndo.ShowTransition=\"Visa övergång på \\\"%1\\\"\"\nUndo.HideTransition=\"Dölj övergång på \\\"%1\\\"\"\nUndo.ShowSceneItem=\"Visa \\\"%1\\\" i \\\"%2\\\"\"\nUndo.HideSceneItem=\"Dölj \\\"%1\\\" i \\\"%2\\\"\"\nUndo.ReorderSources=\"Ordna om källor i \\\"%1\\\"\"\nUndo.MoveUp=\"Flytta \\\"%1\\\" upp i \\\"%2\\\"\"\nUndo.MoveDown=\"Flytta \\\"%1\\\" ned i \\\"%2\\\"\"\nUndo.MoveToTop=\"Flytta \\\"%1\\\" längst upp i \\\"%2\\\"\"\nUndo.MoveToBottom=\"Flytta \\\"%1\\\" längst ned i \\\"%2\\\"\"\nUndo.PasteSource=\"Klistra in källor i \\\"%1\\\"\"\nUndo.PasteSourceRef=\"Klistra in källreferenser i \\\"%1\\\"\"\nUndo.GroupItems=\"Gruppera objekt till \\\"%1\\\"\"\nTransitionNameDlg.Text=\"Var god ange övergångens namn\"\nTransitionNameDlg.Title=\"Övergångsnamn\"\nTitleBar.SafeMode=\"SÄKERT LÄGE\"\nTitleBar.PortableMode=\"Portabelt läge\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Scener\"\nNameExists.Title=\"Namnet används redan\"\nNameExists.Text=\"Detta namn används redan.\"\nNoNameEntered.Title=\"Var god ange ett giltigt namn\"\nNoNameEntered.Text=\"Du måste ange ett namn.\"\nConfirmStart.Title=\"Börja strömma?\"\nConfirmStart.Text=\"Är du säker på att du vill börja strömma?\"\nConfirmStop.Title=\"Sluta strömma?\"\nConfirmStop.Text=\"Är du säker på att du vill sluta strömma?\"\nConfirmStopRecord.Title=\"Stoppa inspelning?\"\nConfirmStopRecord.Text=\"Är du säker på att du vill stoppa inspelningen?\"\nConfirmBWTest.Title=\"Starta bandbreddstest?\"\nConfirmBWTest.Text=\"Du har konfigurerat OBS i testläge för bandbredd. Detta läge låter dig testa nätverket utan att din kanal direktsänder. När du har testat färdigt behöver du inaktivera den för att att tittare ska kunna se din ström.\\n\\nVill du fortsätta?\"\nConfirmExit.Title=\"Aktiva utmatningar\"\nConfirmExit.Text=\"OBS körs fortfarande. Alla strömmar/inspelningar kommer att stängas ned.\"\nConfirmRemove.Title=\"Bekräfta borttagning\"\nConfirmRemove.Text=\"Är du säker på att du vill ta bort \\\"%1\\\"?\"\nConfirmRemove.TextMultiple=\"Är du säker på att du vill ta bort %1 objekt?\"\nConfirmReset.Title=\"Återställ egenskaper\"\nConfirmReset.Text=\"Är du säker på att du vill återställa nuvarande egenskaper till deras standardvärden?\"\nOutput.StartStreamFailed=\"Misslyckades att starta strömning\"\nOutput.StartRecordingFailed=\"Misslyckades att starta inspelning\"\nOutput.StartReplayFailed=\"Misslyckades att starta reprisbuffert\"\nOutput.StartVirtualCamFailed=\"Misslyckades att starta virtuell kamera\"\nOutput.StartFailedGeneric=\"Misslyckades att starta utmatningen. Kolla loggen för detaljer.\\n\\nOBS: Om du använder kodarna NVENC eller AMD, se till att dina grafikdrivrutiner är uppdaterade.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Kan inte spara repriser under pausning\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Varning: Repriser kan inte sparas när inspelningen är pausad.\"\nOutput.ConnectFail.Title=\"Misslyckades att ansluta\"\nOutput.ConnectFail.BadPath=\"Ogiltig sökväg eller anslutnings-URL. Kontrollera att dina inställningar är korrekta.\"\nOutput.ConnectFail.ConnectFailed=\"Misslyckades att ansluta till server\"\nOutput.ConnectFail.InvalidStream=\"Kunde inte komma åt den valda kanalen eller strömnyckeln, var god dubbelkolla din strömnyckel. Om den stämmer kan det finnas något anslutningsproblem till servern.\"\nOutput.ConnectFail.HdrDisabled=\"HDR-utmatning är för närvarande avstängt för den här utmatningen.\"\nOutput.ConnectFail.Error=\"Ett oväntat fel uppstod vid anslutning till servern. Se loggfilen för ytterligare information.\"\nOutput.ConnectFail.Disconnected=\"Nedkopplad från servern.\"\nOutput.StreamEncodeError.Title=\"Kodningsfel\"\nOutput.StreamEncodeError.Msg=\"Ett kodarfel uppstod under strömmande.\"\nOutput.StreamEncodeError.Msg.LastError=\"Ett kodarfel uppstod under strömning:<br><br>%1\"\nOutput.RecordFail.Title=\"Misslyckades att starta inspelning\"\nOutput.RecordFail.Unsupported=\"Antingen stöds inte utdataformatet eller så har det inte stöd för mer än ett ljudspår. Kontrollera dina inställningar och försök igen.\"\nOutput.RecordNoSpace.Title=\"Otillräckligt diskutrymme\"\nOutput.RecordNoSpace.Msg=\"Det finns inte tillräckligt med diskutrymme för att fortsätta spela in.\"\nOutput.RecordError.Title=\"Inspelningsfel\"\nOutput.RecordError.Msg=\"Ett okänt fel uppstod under inspelningen.\"\nOutput.RecordError.EncodeErrorMsg=\"Ett kodarfel uppstod under inspelning.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Ett kodarfel uppstod under inspelning:<br><br>%1\"\nOutput.BadPath.Title=\"Ogiltig sökväg\"\nOutput.BadPath.Text=\"Den konfigurerade inspelningssökvägen kunde inte öppnas. Kontrollera din inspelningssökväg under Inställningar → Utmatning → Inspelning.\"\nOutput.NoBroadcast.Title=\"Ingen sändning har konfigurerats\"\nOutput.NoBroadcast.Text=\"Du måste konfigurera en sändning innan du kan börja strömma.\"\nOutput.BroadcastStartFailed=\"Misslyckades att starta sändningen\"\nOutput.BroadcastStopFailed=\"Misslyckades att stoppa sändningen\"\nLogUploadDialog.Title=\"Uppladdning av loggfil för OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Läs <a href='https://obsproject.com/privacy-policy'>sekretesspolicyn</a> och avsnittet om filuppladdningar innan du laddar upp några filer.\"\nLogUploadDialog.Labels.Progress=\"Logg laddas upp. Var god vänta...\"\nLogUploadDialog.Labels.Description.AppLog=\"Din loggfil har laddats upp. Du kan nu dela webbadressen för felsökning eller support.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Din kraschrapport har laddats upp. Du kan nu dela webbadressen för felsökning eller support.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Ladda upp\"\nLogUploadDialog.Buttons.CopyURL=\"Kopiera webbadress till logg\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Analysera loggfil\"\nLogUploadDialog.Buttons.RetryButton=\"Försök igen\"\nLogUploadDialog.Errors.Template=\"Ett fel uppstod när filen skulle laddas upp:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Hittade ingen fil att ladda upp eller så var filen tom.\"\nRemux.SourceFile=\"OBS-inspelning\"\nRemux.TargetFile=\"Målfil\"\nRemux.Stop=\"Stoppa remuxing\"\nRemux.ClearFinished=\"Rensa slutförda objekt\"\nRemux.ClearAll=\"Rensa alla objekt\"\nRemux.OBSRecording=\"OBS-inspelning\"\nRemux.FinishedTitle=\"Remuxing färdig\"\nRemux.Finished=\"Inspelning remuxed\"\nRemux.FinishedError=\"Inspelning remuxed, men filen kan vara ofullständig\"\nRemux.SelectRecording=\"Välj OBS-inspelning...\"\nRemux.SelectTarget=\"Välj målfil...\"\nRemux.FileExistsTitle=\"Målfiler finns redan\"\nRemux.FileExists=\"Följande målfiler finns redan. Vill du ersätta dem?\"\nRemux.ExitUnfinishedTitle=\"Remuxing pågår\"\nRemux.ExitUnfinished=\"Remuxing är inte klar, stoppas det nu kan göra målfilen oanvändbar.\\nÄr du säker på att du vill stoppa remuxing?\"\nRemux.HelpText=\"Släpp filer att göra en remux på i detta fönster eller välj en tom tabellcell under \\\"OBS-inspelning\\\" för att bläddra efter en fil.\"\nRemux.NoFilesAddedTitle=\"Inga remuxingsfiler lades till\"\nRemux.NoFilesAdded=\"Inga filer att remuxa lades till. Släpp en mapp som innehåller en eller flera videofiler.\"\nMissingFiles=\"Filer saknas\"\nMissingFiles.MissingFile=\"Fil saknas\"\nMissingFiles.NewFile=\"Ny fil\"\nMissingFiles.HelpText=\"Några filer saknas sedan du senast använde OBS.\"\nMissingFiles.Clear=\"<rensades>\"\nMissingFiles.NumFound=\"Hittade %1 av %2\"\nMissingFiles.Search=\"Sök i katalogen...\"\nMissingFiles.SelectFile=\"Välj fil...\"\nMissingFiles.SelectDir=\"Välj mapp att söka i\"\nMissingFiles.State=\"Tillstånd\"\nMissingFiles.Missing=\"Saknas\"\nMissingFiles.Replaced=\"Ersattes\"\nMissingFiles.Cleared=\"Rensades\"\nMissingFiles.Found=\"Hittades\"\nMissingFiles.AutoSearch=\"Ytterligare filmatchningar hittades\"\nMissingFiles.AutoSearchText=\"OBS har hittat ytterligare matchningar för saknade filer i denna katalog. Vill du lägga till dem?\"\nMissingFiles.NoMissing.Title=\"Kontroll av saknade filer\"\nMissingFiles.NoMissing.Text=\"Inga filer verkar saknas.\"\nMacPermissions.MenuAction=\"Granska appbehörigheter...\"\nMacPermissions.Title=\"Granska appbehörigheter\"\nMacPermissions.Description=\"OBS Studio kräver behörighet från dig för att kunna tillhandahålla vissa funktioner. Det rekommenderas att du aktiverar dessa behörigheter, men de krävs inte för att använda appen. Du kan alltid aktivera dem senare.\"\nMacPermissions.Description.OpenDialog=\"Du kan öppna denna dialogruta igen via OBS Studio-menyn.\"\nMacPermissions.AccessGranted=\"Årkomst beviljad\"\nMacPermissions.RequestAccess=\"Begär åtkomst\"\nMacPermissions.OpenPreferences=\"Öppna inställningarna för %1\"\nMacPermissions.Item.ScreenRecording=\"Skärminspelning\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS kräver denna behörighet för att kunna fånga din skärm.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Denna behörighet behövs för att fånga upp innehåll från en webbkamera eller ett inspelningskort.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"OBS kräver denna behörighet om du vill fånga in din mikrofon eller extern ljudenhet.\"\nMacPermissions.Item.InputMonitoring=\"Övervakning av inmatning\"\nMacPermissions.Item.InputMonitoring.Details=\"Denna behörighet krävs för att kortkommandon ska fungera medan OBS är i bakgrunden.\"\nMacPermissions.Item.Accessibility=\"Tillgänglighet\"\nMacPermissions.Item.Accessibility.Details=\"På äldre installationer kanske OBS ligger under \\\"Tillgänglighet\\\" i stället för \\\"Övervakning av inmatning\\\".\"\nMacPermissions.Continue=\"Fortsätt\"\nSourceLeak.Title=\"Fel vid städning av källor\"\nSourceLeak.Text=\"Ett problem uppstod när scensamlingar skulle ändras och några källor inte kunde avlastas. Detta problem orsakas vanligtvis av insticksprogram som inte frigör resurser ordentligt. Se till att alla insticksprogram du använder är uppdaterade.\\n\\nOBS Studio kommer nu avslutas för att förhindra data från att potentiellt skadas.\"\nBasic.DesktopDevice1=\"Skrivbordsljud\"\nBasic.DesktopDevice2=\"Skrivbordsljud 2\"\nBasic.Scene=\"Scen\"\nBasic.DisplayCapture=\"Bildskärmskälla\"\nBasic.Main.PreviewConextMenu.Enable=\"Aktivera förhandsvisning\"\nBasic.Main.Preview.Disable=\"Inaktivera förhandsvisning\"\nScaleFiltering=\"Skalningsfiltrering\"\nScaleFiltering.Point=\"Punkt\"\nScaleFiltering.Bilinear=\"Bilinjär\"\nScaleFiltering.Bicubic=\"Bikubisk\"\nScaleFiltering.Area=\"Område\"\nBlendingMethod=\"Blandningsmetod\"\nBlendingMethod.Default=\"Standard\"\nBlendingMethod.SrgbOff=\"SRGB Av\"\nBlendingMode=\"Blandningsläge\"\nBlendingMode.Normal=\"Normalt\"\nBlendingMode.Additive=\"Lägg till\"\nBlendingMode.Subtract=\"Subtraktion\"\nBlendingMode.Screen=\"Skärm\"\nBlendingMode.Multiply=\"Multiplikation\"\nBlendingMode.Lighten=\"Ljusare\"\nBlendingMode.Darken=\"Mörkare\"\nDeinterlacing=\"Avflätning\"\nDeinterlacing.Discard=\"Avfärda\"\nDeinterlacing.TopFieldFirst=\"Övre fältet först\"\nDeinterlacing.BottomFieldFirst=\"Nedre fältet först\"\nVolControl.SliderUnmuted=\"Volymreglage för \\\"%1\\\":\"\nVolControl.SliderMuted=\"Volymreglage för \\\"%1\\\": (tyst för tillfället)\"\nVolControl.Mute=\"Tysta \\\"%1\\\"\"\nVolControl.Properties=\"Egenskaper för \\\"%1\\\"\"\nVolControl.UnassignedWarning.Title=\"Ej tilldelad ljudkälla\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" är inte tilldelad något ljudspår och kommer inte höras i stream eller inspelning.\\n\\nFör att tilldela en ljudkälla till ett ljudspår, öppna ljudinställningarna via högerklicksmenyn eller kugghjulet i ljudmixern.\"\nBasic.Main.AddSceneDlg.Title=\"Lägg till scen\"\nBasic.Main.AddSceneDlg.Text=\"Var god ange scenens namn\"\nBasic.Main.DefaultSceneName.Text=\"Scen %1\"\nBasic.Main.AddSceneCollection.Title=\"Lägg till scensamling\"\nBasic.Main.AddSceneCollection.Text=\"Var god ange scensamlingens namn\"\nBasic.Main.RenameSceneCollection.Title=\"Byt namn på scensamling\"\nBasic.Main.RemigrateSceneCollection.Title=\"Uppdatera scensamlingens upplösning\"\nBasic.Main.RemigrateSceneCollection.Text=\"Vill du uppdatera scensamlingens upplösning av \\\"%1\\\" för att matcha den nuvarande profilens kanvasupplösning på %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Kan inte uppdatera scensamlings upplösning efter utmatning är aktiv.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Misslyckades att uppdatera scensamlingens upplösning. Ursprungliga upplösningen är okänd.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Inte möjligt att nollställa: misslyckades att ändra OBS-upplösning.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Inte möjligt att nollställa: nuvarande upplösning är redan scensamlingens grundupplösning.\"\nAddProfile.Title=\"Lägg till profil\"\nAddProfile.Text=\"Var god ange profilens namn\"\nAddProfile.WizardCheckbox=\"Visa automatisk konfigurationsguide\"\nRenameProfile.Title=\"Byt namn på profilen\"\nBasic.Main.MixerRename.Title=\"Byt namn på ljudkälla\"\nBasic.Main.MixerRename.Text=\"Var god ange ljudkällans namn\"\nBasic.Main.PreviewDisabled=\"Förhandsvisning har för tillfället inaktiverats\"\nBasic.SourceSelect=\"Skapa/välj källa\"\nBasic.SourceSelect.CreateNew=\"Skapa ny\"\nBasic.SourceSelect.AddExisting=\"Lägg till befintlig\"\nBasic.SourceSelect.AddVisible=\"Gör källan synlig\"\nBasic.Main.Sources.Visibility=\"Synlighet\"\nBasic.Main.Sources.VisibilityDescription=\"Styr synligheten av \\\"%1\\\" i kanvas\"\nBasic.Main.Sources.Lock=\"Lås\"\nBasic.Main.Sources.LockDescription=\"Låser positionen och skalan av \\\"%1\\\" på kanvas\"\nBasic.PropertiesWindow=\"Egenskaper för \\\"%1\\\"\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (välj automatiskt: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Välj färg\"\nBasic.PropertiesWindow.SelectFont=\"Välj typsnitt\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Välj ett typsnitt\"\nBasic.PropertiesWindow.ConfirmTitle=\"Inställningar har ändrats\"\nBasic.PropertiesWindow.Confirm=\"Det finns osparade ändringar. Vill du behålla dem?\"\nBasic.PropertiesWindow.NoProperties=\"Inga inställningar tillgängliga\"\nBasic.PropertiesWindow.AddFiles=\"Lägg till filer\"\nBasic.PropertiesWindow.AddDir=\"Lägg till mapp\"\nBasic.PropertiesWindow.AddURL=\"Lägg till sökväg/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Lägg till mapp i \\\"%1\\\"\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Lägg till filer i \\\"%1\\\"\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Lägg till post i \\\"%1\\\"\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Redigera post från \\\"%1\\\"\"\nBasic.PropertiesView.FPS.Simple=\"Enkla bildfrekvensvärden\"\nBasic.PropertiesView.FPS.Rational=\"Rationella bildfrekvensvärden\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Giltiga bildfrekvensintervall:\"\nBasic.PropertiesView.UrlButton.Text=\"Öppna denna länk i din standardwebbläsare?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"Webbadress: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Öppna webbadress\"\nBasic.InteractionWindow=\"Interagerar med \\\"%1\\\"\"\nBasic.StatusBar.Reconnecting=\"Frånkopplad, återansluter om %2 sekund(er) (försök %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Försöker att återansluta... (försök %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Återanslutning lyckades\"\nBasic.StatusBar.Delay=\"Fördröjning (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Fördröjning (börjar om %1 s)\"\nBasic.StatusBar.DelayStoppingIn=\"Fördröjning (stoppar om %1 s)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Fördröjning (stoppar efter %1 s, börjar om %2 s)\"\nBasic.StatusBar.RecordingSavedTo=\"Inspelning sparades till \\\"%1\\\"\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Reprisbuffert sparades till \\\"%1\\\"\"\nBasic.StatusBar.ScreenshotSavedTo=\"Skärmdump sparades till \\\"%1\\\"\"\nBasic.StatusBar.AutoRemuxedTo=\"Inspelning remuxades automatiskt till \\\"%1\\\"\"\nBasic.Filters=\"Filter\"\nBasic.Filters.AsyncFilters=\"Ljud-/videofilter\"\nBasic.Filters.AudioFilters=\"Ljudfilter\"\nBasic.Filters.EffectFilters=\"Effektfilter\"\nBasic.Filters.Title=\"Filter för \\\"%1\\\"\"\nBasic.Filters.AddFilter.Title=\"Filternamn\"\nBasic.Filters.AddFilter.Text=\"Vänligen ange namnet på filtret\"\nBasic.TransformWindow=\"Omvandla scenobjekt\"\nBasic.TransformWindow.PositionX=\"X-position\"\nBasic.TransformWindow.PositionY=\"Y-position\"\nBasic.TransformWindow.Size=\"Storlek\"\nBasic.TransformWindow.Width=\"Bredd\"\nBasic.TransformWindow.Height=\"Höjd\"\nBasic.TransformWindow.Alignment=\"Positionsjustering\"\nBasic.TransformWindow.BoundsType=\"Avgränsningsramstyp\"\nBasic.TransformWindow.BoundsAlignment=\"Justering i avgränsningsramen\"\nBasic.TransformWindow.Bounds=\"Avgränsningsramstorlek\"\nBasic.TransformWindow.BoundsWidth=\"Bredd på avgränsningsram\"\nBasic.TransformWindow.BoundsHeight=\"Höjd på avgränsningsram\"\nBasic.TransformWindow.CropToBounds=\"Beskär till avgränsningsfält\"\nBasic.TransformWindow.Crop=\"Beskär\"\nBasic.TransformWindow.CropLeft=\"Beskär vänsterkant\"\nBasic.TransformWindow.CropRight=\"Beskär högerkant\"\nBasic.TransformWindow.CropTop=\"Beskär överkant\"\nBasic.TransformWindow.CropBottom=\"Beskär nederkant\"\nBasic.TransformWindow.Alignment.TopLeft=\"Överst till vänster\"\nBasic.TransformWindow.Alignment.TopCenter=\"Överst i mitten\"\nBasic.TransformWindow.Alignment.TopRight=\"Överst till höger\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Centrerad till vänster\"\nBasic.TransformWindow.Alignment.Center=\"Centrerad\"\nBasic.TransformWindow.Alignment.CenterRight=\"Centrerad till höger\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Nederst till vänster\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Nederst i mitten\"\nBasic.TransformWindow.Alignment.BottomRight=\"Nederst till höger\"\nBasic.TransformWindow.BoundsType.None=\"Ingen avgränsning\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Överskrid inte källans grundstorlek\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skala till inre gränser\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skala till yttre gränser\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skala till avgränsningsramens bredd\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skala till avgränsningsramens höjd\"\nBasic.TransformWindow.BoundsType.Stretch=\"Anpassa till avgränsningsramens storlek\"\nBasic.TransformWindow.Title=\"Redigera omvandling för \\\"%1\\\"\"\nBasic.TransformWindow.NoSelectedSource=\"Ingen vald källa\"\nBasic.Main.AddSourceHelp.Title=\"Kan inte lägga till källa\"\nBasic.Main.AddSourceHelp.Text=\"Du måste ha minst 1 scen för att kunna lägga till en källa.\"\nBasic.Main.Scenes=\"Scener\"\nBasic.Main.Sources=\"Källor\"\nBasic.Main.Source=\"Källa\"\nBasic.Main.Controls=\"Kontroller\"\nBasic.Main.PreparingStream=\"Förbereder...\"\nBasic.Main.Connecting=\"Ansluter...\"\nBasic.Main.StartRecording=\"Börja spela in\"\nBasic.Main.StartReplayBuffer=\"Starta reprisbuffert\"\nBasic.Main.SaveReplay=\"Spara repris\"\nBasic.Main.StartStreaming=\"Börja strömma\"\nBasic.Main.StartBroadcast=\"Direktsänd\"\nBasic.Main.StartVirtualCam=\"Starta virtuell kamera\"\nBasic.Main.StopRecording=\"Sluta spela in\"\nBasic.Main.PauseRecording=\"Pausa inspelning\"\nBasic.Main.UnpauseRecording=\"Återuppta inspelning\"\nBasic.Main.SplitFile=\"Dela upp inspelningsfil\"\nBasic.Main.AddChapterMarker=\"Lägg till kapitelmarkör (endast Hybrid MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Slutar spela in...\"\nBasic.Main.StopReplayBuffer=\"Stoppa reprisbuffert\"\nBasic.Main.StoppingReplayBuffer=\"Stoppar reprisbuffert...\"\nBasic.Main.SetupBroadcast=\"Hantera sändning\"\nBasic.Main.StopStreaming=\"Sluta strömma\"\nBasic.Main.StopBroadcast=\"Avsluta sändning\"\nBasic.Main.AutoStopEnabled=\"(stoppa automatiskt)\"\nBasic.Main.StoppingStreaming=\"Slutar strömma...\"\nBasic.Main.ForceStopStreaming=\"Sluta strömma (ignorera fördröjning)\"\nBasic.Main.ShowContextBar=\"Visa verktygsfält för källor\"\nBasic.Main.HideContextBar=\"Dölj verktygsfält för källor\"\nBasic.Main.StopVirtualCam=\"Stoppa virtuell kamera\"\nBasic.Main.Group=\"Grupp %1\"\nBasic.Main.GroupItems=\"Gruppera markerade objekt\"\nBasic.Main.Ungroup=\"Avgruppera\"\nBasic.Main.GridMode=\"Rutnätsläge\"\nBasic.Main.ListMode=\"Listläge\"\nBasic.Main.VirtualCamConfig=\"Konfigurera virtuell kamera\"\nBasic.VCam.VirtualCamera=\"Virtuell kamera\"\nBasic.VCam.OutputType=\"Utmatningstyp\"\nBasic.VCam.OutputSelection=\"Utmatningsmarkering\"\nBasic.VCam.OutputType.Program=\"Program (standard)\"\nBasic.VCam.OutputSelection.NoSelection=\"Inget val för den här typen av utmatning\"\nBasic.VCam.RestartWarning=\"Den virtuella kameran kommer att startas om för att verkställa den här ändringen\"\nBasic.MainMenu.File=\"&Arkiv\"\nBasic.MainMenu.File.Export=\"&Exportera\"\nBasic.MainMenu.File.Import=\"&Importera\"\nBasic.MainMenu.File.ShowRecordings=\"Visa inspelninga&r\"\nBasic.MainMenu.File.Remux=\"Re&muxa inspelningar\"\nBasic.MainMenu.File.Settings=\"In&ställningar\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Visa inställningsmapp\"\nBasic.MainMenu.File.ShowProfileFolder=\"Visa profilmapp\"\nBasic.MainMenu.File.ShowMissingFiles=\"Leta efter saknade filer\"\nBasic.MainMenu.File.Exit=\"&Avsluta\"\nBasic.MainMenu.Edit=\"&Redigera\"\nBasic.MainMenu.Edit.Undo=\"Ångra (&U)\"\nBasic.MainMenu.Edit.Redo=\"Gö&r om\"\nBasic.MainMenu.Edit.LockPreview=\"&Lås förhandsvisning\"\nBasic.MainMenu.Edit.Scale=\"Förhand&svisa skalning\"\nBasic.MainMenu.Edit.Scale.Window=\"Skala till fönster\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Kanvas (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Utmatning (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Skalad (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Zooma in\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Zooma ut\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Återställ zoom\"\nBasic.MainMenu.Edit.Transform=\"O&mvandla\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"R&edigera omvandling...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Kopiera omvandling\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Klistra in omvandling\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Återställ omvandling\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Rotera 90 grader medsols\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Rotera 90 grader motsols\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Rotera 180 grader\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Spegelvänd vågrätt (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Spegel&vänd vertikalt\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Anpassa till skärmen (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Anpa&ssa storlek till skärmen\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&Centrera på skärmen\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Centrera vertikalt\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Centrera horisontalt\"\nBasic.MainMenu.Edit.Order=\"&Ordning\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Flytta &upp\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Flytta ne&d\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Fly&tta längst upp\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Flytta längst ned (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"&Avancerade ljudinställningar\"\nBasic.MainMenu.View=\"Vi&sa\"\nBasic.MainMenu.View.Toolbars=\"Verk&tygsfält\"\nBasic.MainMenu.View.ListboxToolbars=\"Verktygsfält för dockning\"\nBasic.MainMenu.View.ContextBar=\"Verktygsfält för källor\"\nBasic.MainMenu.View.SourceIcons=\"Käll&ikoner\"\nBasic.MainMenu.View.StatusBar=\"&Statusfält\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Helskärmsgränssnitt\"\nBasic.MainMenu.View.ResetUI=\"&Återställ gränssnitt\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Alltid överst\"\nBasic.MainMenu.View.SceneListMode=\"Läge för scenlista\"\nBasic.MainMenu.Docks=\"&Flytande fönster\"\nBasic.MainMenu.Docks.ResetDocks=\"&Återställ flytande fönster\"\nBasic.MainMenu.Docks.LockDocks=\"&Lås flytande fönster\"\nBasic.MainMenu.Docks.SideDocks=\"&Flytande fönster med full höjd\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Anpassade webbläsarfönster...\"\nBasic.MainMenu.Import=\"Importera...\"\nBasic.MainMenu.Export=\"Exportera...\"\nBasic.MainMenu.SceneCollection=\"&Scensamling\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Importera profil\"\nBasic.MainMenu.Profile.Export=\"Exportera profil\"\nBasic.MainMenu.SceneCollection.Import=\"Importera scensamling\"\nBasic.MainMenu.SceneCollection.Export=\"Exportera scensamling\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Nollställ grundupplösning\"\nBasic.MainMenu.SceneCollection.Migrate=\"Använd som grundupplösning\"\nBasic.MainMenu.Profile.Exists=\"Profilen finns redan\"\nBasic.MainMenu.Tools=\"&Verktyg\"\nBasic.MainMenu.Help=\"&Hjälp\"\nBasic.MainMenu.Help.HelpPortal=\"Hjäl&pportal\"\nBasic.MainMenu.Help.Website=\"Besök &webbplats\"\nBasic.MainMenu.Help.Discord=\"Anslut till &Discord-servern\"\nBasic.MainMenu.Help.WhatsNew=\"Vad är nytt\"\nBasic.MainMenu.Help.Logs=\"&Loggfiler\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Vi&sa loggfiler\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Ladda upp &aktuell loggfil\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Ladda upp &föregående loggfil\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&Visa aktuell logg\"\nBasic.MainMenu.Help.ReleaseNotes=\"Versionsfakta\"\nBasic.MainMenu.Help.CheckForUpdates=\"Sök efter uppdateringar\"\nBasic.MainMenu.Help.Repair=\"Kontrollera filintegritet\"\nBasic.MainMenu.Help.RestartSafeMode=\"Starta om i felsäkert läge\"\nBasic.MainMenu.Help.RestartNormal=\"Starta om i normalt läge\"\nBasic.MainMenu.Help.CrashLogs=\"K&raschrapporter\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Vi&sa kraschrapporter\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Ladda upp &föregående kraschrapport\"\nBasic.MainMenu.Help.About=\"&Om OBS\"\nBasic.Settings.ProgramRestart=\"Du måste starta om programmet för att ändringarna ska träda i kraft.\"\nBasic.Settings.ConfirmTitle=\"Bekräfta ändringar\"\nBasic.Settings.Confirm=\"Du har osparade ändringar. Vill du spara ändringarna?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 kontrollerar några av dina strömningsinställningar\"\nBasic.Settings.General=\"Allmänt\"\nBasic.Settings.General.Language=\"Språk\"\nBasic.Settings.General.Updater=\"Uppdateringar\"\nBasic.Settings.General.UpdateChannel=\"Uppdateringskanal\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Inaktiverad)\"\nBasic.Settings.General.UpdateChannelDefault=\"(standard)\"\nBasic.Settings.General.EnableAutoUpdates=\"Sök efter uppdateringar automatiskt vid uppstart\"\nBasic.Settings.General.OpenStatsOnStartup=\"Öppna statistikfönstret vid uppstart\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Dölj OBS-fönster från bildskärmsinspelning\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Denna inställning kommer att dölja alla icke-projicerande OBS Studio-fönster från att fångas av OBS och påverkar \\nandra program, såsom konferensprogram, skärmdelning, fjärrsupport, skärmdumpar och andra inspelningsprogram.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"När detta alternativ aktiveras döljs alla icke-projicerande OBS Studio-fönster från att fångas av OBS och påverkar \\nandra program, såsom konferensprogram, skärmdelning, fjärrsupport, skärmdumpar och andra inspelningsprogram.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Visa bekräftelsedialog när ström startas\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Visa bekräftelsedialog när ström stoppas\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Visa bekräftelsedialog när inspelning stoppas\"\nBasic.Settings.General.Projectors=\"Projektorer\"\nBasic.Settings.General.HideProjectorCursor=\"Dölj pekaren över projektorer\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Lägg alltid projektorer överst\"\nBasic.Settings.General.Snapping=\"Fäst justerbara källor\"\nBasic.Settings.General.ScreenSnapping=\"Fäst källor till skärmens kant\"\nBasic.Settings.General.CenterSnapping=\"Fäst källor till den horisontala och vertikala mittenlinjen\"\nBasic.Settings.General.SourceSnapping=\"Fäst källor till andra källor\"\nBasic.Settings.General.SnapDistance=\"Fästkänslighet\"\nBasic.Settings.General.SpacingHelpers=\"Visa pixeljusteringsguider\"\nBasic.Settings.General.RecordWhenStreaming=\"Spela automatiskt in vid strömning\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Fortsätt spela in när strömmen stoppas\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Starta reprisbufferten automatiskt vid strömning\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Håll reprisbufferten aktiv när ström stoppas\"\nBasic.Settings.General.SysTray=\"Systemfält\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimera till meddelandefältet vid uppstart\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Minimera alltid till meddelandefältet i stället för aktivitetsfältet\"\nBasic.Settings.General.SaveProjectors=\"Spara projektorer vid avslut\"\nBasic.Settings.General.CloseExistingProjectors=\"Begränsa en fullskärmprojektor per skärm\"\nBasic.Settings.General.Preview=\"Förhandsvisning\"\nBasic.Settings.General.OverflowHidden=\"Dölj överflöd\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Överflöd visas alltid\"\nBasic.Settings.General.OverflowSelectionHidden=\"Visa överflöd även om källan är osynlig\"\nBasic.Settings.General.Importers=\"Importörer\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Sök efter kända platser för scensamlingar under importering\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Övergång till scen vid dubbelklick\"\nBasic.Settings.General.StudioPortraitLayout=\"Aktivera porträtt-/vertikalt utseende\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Visa förhandsvisning/programetiketter\"\nBasic.Settings.General.Multiview=\"Multivy\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Klicka för att byta mellan scener\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Visa scennamn\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Rita ut säkra områden (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Utseende för multivy\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Horisontal, överkant (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Horisontal, nederkant (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Vertikal, vänsterkant (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Vertikal, högerkant (8 scener)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Horisontal, överkant (18 scener)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Horisontal, överkant (24 scener)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Endast scener (4 scener)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Endast scener (9 scener)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Endast scener (16 scener)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Endast scener (25 scener)\"\nBasic.Settings.General.ChannelName.stable=\"Stabil\"\nBasic.Settings.General.ChannelDescription.stable=\"Senaste stabila utgåvan\"\nBasic.Settings.General.ChannelName.beta=\"Beta/frisläppningskandidater\"\nBasic.Settings.General.ChannelDescription.beta=\"Potentiellt instabila förhandsversioner\"\nBasic.Settings.Appearance=\"Utseende\"\nBasic.Settings.Appearance.General=\"Allmänt\"\nBasic.Settings.Appearance.General.Theme=\" Tema\"\nBasic.Settings.Appearance.General.Variant=\"Stil\"\nBasic.Settings.Appearance.General.NoVariant=\"Inga stilar är tillgängliga\"\nBasic.Settings.Appearance.FontScale=\"Typsnittsstorlek\"\nBasic.Settings.Appearance.Density=\"Densitet\"\nBasic.Settings.Appearance.Classic=\"Klassisk\"\nBasic.Settings.Appearance.Compact=\"Kompakt\"\nBasic.Settings.Appearance.Comfortable=\"Bekväm\"\nBasic.Settings.Appearance.OptionsWarning=\"Vissa utseendealternativ är inte tillgängliga för denna stil.\"\nBasic.Settings.Stream=\"Ström\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Använd autentisering\"\nBasic.Settings.Stream.Custom.Username=\"Användarnamn\"\nBasic.Settings.Stream.Custom.Password=\"Lösenord\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: ange srp_username.\\nRTMP: ange användarnamnet.\\nSRT: används inte.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: ange srp_password.\\nRTMP: ange lösenordet.\\nSRT: ange den krypterade lösenfrasen.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Aktivera testläge för bandbredd\"\nBasic.Settings.Stream.TTVAddon=\"Tillägg för Twitch-chatt\"\nBasic.Settings.Stream.TTVAddon.None=\"Inget\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV och FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Ströminställning saknas\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Öppna inställningar\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Webbadress och strömnyckel saknas.\\n\\nÖppna inställningarna för att ange webbadressen och strömnyckeln under fliken \\\"Ström\\\".\"\nBasic.Settings.Stream.MissingUrl=\"Strömadress saknas.\\n\\nÖppna inställningarna för att ange webbadressen under fliken \\\"Ström\\\".\"\nBasic.Settings.Stream.MissingStreamKey=\"Strömnyckel saknas.\\n\\nÖppna inställningarna för att ange strömnyckeln under fliken \\\"Ström\\\".\"\nBasic.Settings.Stream.IgnoreRecommended=\"Ignorera strömningstjänstens rekommendationer\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Åsidosätt rekommenderade inställningar\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Varning: Om du ignorerar tjänstens begränsningar kan det leda till försämrad strömkvalitet eller förhindra dig från att strömma.\\n\\nVill du fortsätta?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maximal videobithastighet: %1 kbit/s\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Maximal ljudbithastighet: %1 kbit/s\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maximal upplösning: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maximal bildfrekvens: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Ange anpassad server...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Anpassad server\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Aktivera %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Maximal strömningsbandbredd\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Max. antal videospår\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Aktivera strömdumpning till FLV (använder enkla inställningar för inspelningsfil)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Överskrid konfiguration (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Aktivera överskridande av konfiguration\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Flerspårsvideo\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Ytterligare kanvas\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Simulcast låter dig koda och skicka flera videokvaliteter. <a href='https://obsproject.com/kb/whip-streaming-guide'>Läs mer</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Totalt antal lager\"\nBasic.Settings.Stream.AdvancedOptions=\"Avancerade alternativ\"\nBasic.Settings.Output=\"Utmatning\"\nBasic.Settings.Output.Format=\"Inspelningsformat\"\nBasic.Settings.Output.Format.MKV=\"Matroska-video (.mkv)\"\nBasic.Settings.Output.Format.fMP4=\"Fragmenterad MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Fragmenterad MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmenterad MOV skriver inspelningen i segment och kräver inte samma slut som traditionella MOV-filer.\\nDetta säkerställer att filen förblir spelbar även om skrivning till disk skulle avbrytas, t.ex. p.g.a. en BSOD eller strömförlust.\\n\\nDetta kanske inte är kompatibelt med alla spelare och redigerare. Använd Arkiv → Remuxa inspelningar för att konvertera filen till ett mer kompatibelt format om det behövs.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmenterad MP4 skriver inspelningen i segment och kräver inte samma slut som traditionella MP4-filer.\\nDetta säkerställer att filen förblir spelbar även om skrivning till disk skulle avbrytas, t.ex. p.g.a. en BSOD eller strömförlust.\\n\\nDetta kanske inte är kompatibelt med alla spelare och redigerare. Använd Arkiv → Remuxa inspelningar för att konvertera filen till ett mer kompatibelt format om det behövs.\"\nBasic.Settings.Output.Encoder.Video=\"Videokodare\"\nBasic.Settings.Output.Encoder.Audio=\"Ljudkodare\"\nBasic.Settings.Output.SelectDirectory=\"Välj inspelningskatalog\"\nBasic.Settings.Output.DynamicBitrate=\"Ändra bithastigheten dynamiskt för att hantera överbelastning\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Ändra bithastigheten dynamiskt för att hantera överbelastning (beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Istället för att kasta bildrutor för att reducera överbelastning kommer bithastigheten ändras dynamiskt.\\n\\nObservera att detta kan öka fördröjningen för tittare om en avsevärd belastning uppstår.\\nNär bithastigheten sjunker kan det ta ett par minuter att återställas.\\n\\nStöds för närvarande endast för RTMP.\"\nBasic.Settings.Output.Mode=\"Utmatningsläge\"\nBasic.Settings.Output.Mode.Simple=\"Enkelt\"\nBasic.Settings.Output.Mode.Adv=\"Avancerat\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg-utmatning\"\nBasic.Settings.Output.UseReplayBuffer=\"Aktivera reprisbuffert\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maximal repristid\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Maximalt minne\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Uppskattad minnesanvändning: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Varning: Beräknad minnesanvändning av %1 MiB är större än rekommenderat maximum på %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Kan inte uppskatta minnesanvändningen. Ange maximal minnesgräns.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Filnamnsprefix för reprisbuffert\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Reprisbufferten kan inte användas när inspelningstypen är anpassad utmatning (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Inspelningssökväg\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Inspelningskvalitet\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Samma som ström\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Hög kvalitet, mellanstor filstorlek\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Oskiljbar kvalitet, stor filstorlek\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Förlustfri kvalitet, oerhört stor filstorlek\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Varning: Strömmens videobithastighet kommer att ändras till %1, vilket är maxgränsen för den nuvarande strömtjänsten.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Varning: Strömmens ljudbithastighet kommer att ändras till %1, vilket är maxgränsen för den nuvarande strömtjänsten.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Varning: Inspelningar kan inte pausas om inspelningskvaliteten är \\\"Samma som ström\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Varning: Det valda inspelningsformatet är inkompatibel med de valda streamenkod(are).\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Varning: Inspelning med en mjukvaru-kodare i annan kvalitet än streamen kräver mer CPU-använding om du streamar och spelar in samtidigt.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Varning: Förlustfri kvalitet generar oerhört stora filstorlekar! Förlustfri kvalitet kan använda upp till 7 gigabyte hårddiskutrymme per minut vid höga upplösningar och bildfrekvenser. Detta rekommenderas inte för långa inspelningar såvida du har riktigt mycket hårddiskutrymme tillgängligt. Reprisbufferten är inte tillgänglig när förlustsfri kvalitet används.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Är du säker på att du vill använda förlustfri kvalitet?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Varning om förlustfri kvalitet!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Mjukvara (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Hårdvara (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Hårdvara (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Hårdvara (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Hårdvara (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Hårdvara (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Hårdvara (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Hårdvara (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Hårdvara (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Hårdvara (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Hårdvara (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Mjukvara (x264 med förinställning för låg processoranvändning, ökar filstorleken)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (standard)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD-spår (använder spår 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Ljudspår\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Inkompatibel upplösning/bildfrekvens\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Den här strömningstjänsten stöder inte din nuvarande utdataupplösning och/eller bildfrekvens. De kommer att ändras till närmaste kompatibla värde:\\n\\n%1\\n\\nVill du fortsätta?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Upplösning: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Bildfrekvens: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Inkompatibel kodare\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Strömningstjänsten \\\"%1\\\" stödjer inte kodaren \\\"%2\\\". Kodaren kommer ändras till \\\"%3\\\".\\n\\nVill du fortsätta?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Strömningstjänsten \\\"%1\\\" har inte stöd för kodarna \\\"%2\\\" och \\\"%3\\\". Dessa kodare kommer ändras till \\\"%4\\\" och \\\"%5\\\".\\n\\nVill du fortsätta?\"\nBasic.Settings.Output.VideoBitrate=\"Bithastighet för video\"\nBasic.Settings.Output.AudioBitrate=\"Bithastighet för ljud\"\nBasic.Settings.Output.Reconnect=\"Automatisk återanslutning\"\nBasic.Settings.Output.RetryDelay=\"Fördröjning för nästa försök\"\nBasic.Settings.Output.MaxRetries=\"Maximalt antal försök\"\nBasic.Settings.Output.Advanced=\"Aktivera anpassade kodarinställningar (Avancerat)\"\nBasic.Settings.Output.EncoderPreset=\"Förinställning för kodare\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (låg processoranvändning, lägst kvalitet)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (standard) (medelhög processoranvändning, standardkvalitet)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (hög processoranvändning, hög kvalitet)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Inställningar för anpassade kodare\"\nBasic.Settings.Output.CustomMuxerSettings=\"Anpassade muxerinställningar\"\nBasic.Settings.Output.NoSpaceFileName=\"Generera filnamn utan mellanrum\"\nBasic.Settings.Output.Adv.Rescale=\"Skala om utmatning\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Inaktiverad\"\nBasic.Settings.Output.Adv.AudioTrack=\"Ljudspår\"\nBasic.Settings.Output.Adv.Streaming=\"Strömning\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Ströminställningar\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Spår 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Spår 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Spår 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Spår 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Spår 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Spår 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD-spår\"\nBasic.Settings.Output.Adv.Encoder=\"Kodarinställningar\"\nBasic.Settings.Output.Adv.Recording=\"Inspelning\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Inspelningsinställningar\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Inspelningstyp\"\nBasic.Settings.Output.Adv.Recording.Type=\"Typ\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Anpassad utmatning (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Använd strömkodare)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Filnamnsformat\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Skriv över om filen finns\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Anpassad utmatning (FFmpeg) tillhandahålls utan skydd.\\nÖverväg att använda inspelningstypen \\\"Standard\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg-utmatningstyp\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Utmatning till webbadress\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Utmatning till fil\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Vanliga inspelningsformat\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Alla filer\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Sökväg eller webbadress\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Behållarformat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Ljud\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Standardformat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Beskrivning av behållarformat\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Ljud-/videoomkodare gissades från sökväg eller webbadress\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Standardkodare\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Inaktivera kodare\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Videokodare\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Videokodarinställningar (om några finns)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Ljudkodare\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Ljudkodar-inställningar (om något)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxerinställningar (om det finns)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Intervall för keyframes (bildrutor)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Visa alla omkodare (även de som eventuellt inte är kompatibla)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg-inställningar\"\nBasic.Settings.Output.EnableSplitFile=\"Automatisk filuppdelning\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Dela efter tidpunkt\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Dela efter storlek\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Dela endast upp manuellt\"\nBasic.Settings.Output.SplitFile.Time=\"Tidpunkt att dela vid\"\nBasic.Settings.Output.SplitFile.Size=\"Storlek att dela vid\"\nScreenshot=\"Utmatning för skärmdump\"\nScreenshot.SourceHotkey=\"Vald källa för skärmdump\"\nScreenshot.StudioProgram=\"Skärmdump (program)\"\nScreenshot.Preview=\"Skärmdump (förhandsvisning)\"\nScreenshot.Scene=\"Skärmdump (scen)\"\nScreenshot.Source=\"Skärmdump (källa)\"\nFilenameFormatting.TT.CCYY=\"År, fyra siffror\"\nFilenameFormatting.TT.YY=\"År, senaste två siffrorna (00-99)\"\nFilenameFormatting.TT.MM=\"Månad som ett decimaltal (01-12)\"\nFilenameFormatting.TT.DD=\"Dag i månaden, inledande nolla (01-31)\"\nFilenameFormatting.TT.hh=\"Timme i 24-timmarsformat (00-23)\"\nFilenameFormatting.TT.mm=\"Minut (00-59)\"\nFilenameFormatting.TT.ss=\"Sekund (00-59)\"\nFilenameFormatting.TT.Percent=\"Ett %-tecken\"\nFilenameFormatting.TT.a=\"Förkortad veckodag\"\nFilenameFormatting.TT.A=\"Fullständig veckodag\"\nFilenameFormatting.TT.b=\"Förkortat månadsnamn\"\nFilenameFormatting.TT.B=\"Fullständigt månadsnamn\"\nFilenameFormatting.TT.d=\"Dag i månaden, inledande nolla (01-31)\"\nFilenameFormatting.TT.H=\"Timme i 24-timmarsformat (00-23)\"\nFilenameFormatting.TT.I=\"Timme i 12-timmarsformat (01-12)\"\nFilenameFormatting.TT.m=\"Månad som ett decimaltal (01-12)\"\nFilenameFormatting.TT.M=\"Minut (00-59)\"\nFilenameFormatting.TT.p=\"AM- eller PM-beteckning\"\nFilenameFormatting.TT.s=\"Tid i sekunder sedan UNIX-epok\"\nFilenameFormatting.TT.S=\"Sekund (00-59)\"\nFilenameFormatting.TT.y=\"År, senaste två siffrorna (00-99)\"\nFilenameFormatting.TT.Y=\"År\"\nFilenameFormatting.TT.z=\"ISO 8601-förskjutning från UTC i tidszon\"\nFilenameFormatting.TT.Z=\"Namn eller förkortning på tidszon\"\nFilenameFormatting.TT.FPS=\"Bilder per sekund\"\nFilenameFormatting.TT.CRES=\"Grundupplösning (kanvas)\"\nFilenameFormatting.TT.ORES=\"Utdataupplösning (skalad)\"\nFilenameFormatting.TT.VF=\"Videoformat\"\nBasic.Settings.Video.Adapter=\"Grafikkort\"\nBasic.Settings.Video.BaseResolution=\"Grundupplösning (kanvas)\"\nBasic.Settings.Video.ScaledResolution=\"Utdataupplösning (skalad)\"\nBasic.Settings.Video.DownscaleFilter=\"Nedskalningsfilter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Upplösningarna matchar, ingen nedskalning krävs]\"\nBasic.Settings.Video.FPS=\"Bildfrekvens\"\nBasic.Settings.Video.FPSCommon=\"Vanliga bildhastighetsvärden\"\nBasic.Settings.Video.FPSInteger=\"Bildfrekvensvärde (heltal)\"\nBasic.Settings.Video.FPSFraction=\"Bildfrekvensvärde (decimaltal)\"\nBasic.Settings.Video.Numerator=\"Täljare\"\nBasic.Settings.Video.Denominator=\"Nämnare\"\nBasic.Settings.Video.Renderer=\"Renderare\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (experimentell)\"\nBasic.Settings.Video.InvalidResolution=\"Ogiltigt upplösningsvärde. Måste vara [bredd]x[höjd] (t.ex. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Videoutmatning är aktiv. Stoppa alla utmatningar för att kunna ändra videoinställningar.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinjär (snabbast, men blir suddigt det skalas)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bikubisk (skarpare skalning, 16 samplingar)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (skarpare skalning, 36 samplingar)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Area (viktad summa, 4/6/9 samplingar)\"\nBasic.Settings.Audio=\"Ljud\"\nBasic.Settings.Audio.SampleRate=\"Samplingsfrekvens\"\nBasic.Settings.Audio.Channels=\"Kanaler\"\nBasic.Settings.Audio.Meters=\"Mätare\"\nBasic.Settings.Audio.MeterDecayRate=\"Sänkningshastighet\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Snabb\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Långsam (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Typ av maxpunktsmätare\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Samplingsmaxpunkt\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Sann maxpunkt (högre CPU-användning)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"VARNING: Surroundljud är aktiverat.\"\nBasic.Settings.Audio.MultichannelWarning=\"Om du strömmar, se till att kolla om din strömtjänst stöder både inmatning och uppspelning av surroundljud. Till exempel har Facebook 360 Live fullt stöd för surround och YouTube Live har stöd för 5.1-ljud (samt uppspelning på TV).\\n\\nLjudfiltren i OBS är kompatibla med surroundljud, fast stöd för VST-insticksprogram garanteras inte.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Aktivera surroundljud?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Är du säker på att du vill aktivera surroundljud?\"\nBasic.Settings.Audio.Devices=\"Globala ljudenheter\"\nBasic.Settings.Audio.DesktopDevice=\"Skrivbordsljud\"\nBasic.Settings.Audio.DesktopDevice2=\"Skrivbordsljud 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/extra ljud\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/extra ljud 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/extra ljud 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/extra ljud 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Aktivera tryck för att tysta\"\nBasic.Settings.Audio.PushToMuteDelay=\"Fördröjning för tryck för att tysta\"\nBasic.Settings.Audio.EnablePushToTalk=\"Aktivera tryck för att tala\"\nBasic.Settings.Audio.PushToTalkDelay=\"Fördröjning för tryck för att tala\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Enheten är inte ansluten eller tillgänglig]\"\nBasic.Settings.Audio.Disabled=\"Inaktiverad\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Ljudbuffringsläge med låg latens (för Decklink/NDI-utmatningar)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"VARNING: Ljudbuffring med låg latens är aktiverad.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Ljudbuffringsläget med låg latens kan orsaka ljudbuggar eller att ljudet slutar spelas från vissa källor.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Aktivera ljudbuffringsläge med låg latens ?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Är du säker på att du vill aktivera ljudbuffringsläge med låg latens?\"\nBasic.Settings.Accessibility=\"Tillgänglighet\"\nBasic.Settings.Accessibility.ColorOverrides=\"Använd olika färger\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Källkant (markering)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Källkant (beskärning)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Källkant (hovring)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Mixervolymband (-60 till -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Mixervolymband (-20 till -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Mixervolymband (-9 till 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Mixervolymband (-60 till -20dB) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Mixervolymband (-20 till -9dB) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Mixervolymband (-9 till 0dB) (aktiv)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Färgförinställning\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Standard\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Anpassad\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Alternativ för färgblinda\"\nBasic.Settings.Advanced=\"Avancerat\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Visa varning för aktiva utmatningar vid avslut\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Processprioritet\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Hög\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Över normal\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Under normal\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Inaktiv\"\nBasic.Settings.Advanced.FormatWarning=\"Varning: Andra färgformat än NV12/P010 är avsedda för inspelning och rekommenderas inte för att strömma. Högre processoranvändning kan uppstå vid strömning p.g.a. konvertering av färgformat.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Varning: Format med hög precision används oftare med HDR-färgrymder.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Varning: Rec. 2100 bör använda ett format med större precision.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Färgformat\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bitar, 4:2:0, 2 plan)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bitar, 4:2:0, 3 plan)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bitar, 4:4:4, 3 plan)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bitar, 4:2:0, 2 plan)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bitar, 4:2:0, 3 plan)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bitar, 4:2:2, 2 plan)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bitar, 4:4:4, 2 plan)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8-bitar)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Färgrymd\"\nBasic.Settings.Advanced.Video.ColorRange=\"Färgintervall\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Begränsat\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Fullständigt\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Vitnivå för SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Nominell toppnivå för HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Uppspelningsenhet\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Standard\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Inaktivera audio ducking i Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Strömfördröjning\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Varaktighet\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Behåll stoppunkten (öka fördröjningen) vid återanslutning\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Uppskattad minnesanvändning: %1 MB\"\nBasic.Settings.Advanced.Network=\"Nätverk\"\nBasic.Settings.Advanced.Network.Disabled=\"Det nu valda strömningsprotokollet stöder inte att ändra nätverksinställningar.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Bind till IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP-familj\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Aktivera nätverksoptimeringar\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Använd TCP-hastighet\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Försöker göra RTMP-utmatning vänligare mot andra latenskänsliga applikationer i nätverket genom att reglera överföringshastigheten.\\nDet kan öka risken för tappade ramar på instabila anslutningar.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Fokusbeteende för kortkommando\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Inaktivera aldrig kortkommandon\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Inaktivera kortkommandon när fokus ligger i huvudfönstret\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Inaktivera kortkommandon när huvudfönstret inte har fokus\"\nBasic.Settings.Advanced.AutoRemux=\"Remuxa automatiskt till %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(spela in som mkv)\"\nBasic.AdvAudio=\"Avancerade ljudegenskaper\"\nBasic.AdvAudio.ActiveOnly=\"Endast aktiva källor\"\nBasic.AdvAudio.Name=\"Namn\"\nBasic.AdvAudio.Volume=\"Volym\"\nBasic.AdvAudio.VolumeSource=\"Volym för \\\"%1\\\"\"\nBasic.AdvAudio.MonoSource=\"Mononedmixning för \\\"%1\\\"\"\nBasic.AdvAudio.Balance=\"Balans\"\nBasic.AdvAudio.BalanceSource=\"Balans för \\\"%1\\\"\"\nBasic.AdvAudio.SyncOffset=\"Synkroniseringsavvikelse\"\nBasic.AdvAudio.SyncOffsetSource=\"Synkroniseringsförskjutning för \\\"%1\\\"\"\nBasic.AdvAudio.Monitoring=\"Ljuduppspelning\"\nBasic.AdvAudio.Monitoring.None=\"Monitor av\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Endast monitor (tysta utgång)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitor och utgång\"\nBasic.AdvAudio.MonitoringSource=\"Ljudövervakning för \\\"%1\\\"\"\nBasic.AdvAudio.AudioTracks=\"Spår\"\nBasic.Settings.Hotkeys=\"Kortkommandon\"\nBasic.Settings.Hotkeys.Pair=\"Tangentkombinationer som delas med \\\"%1\\\" fungerar som strömbrytare\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Filtrera på kortkommando\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Detta kortkommando används av en eller flera andra åtgärder. Klicka för att visa konflikter.\"\nBasic.Settings.Hotkeys.PleaseWait=\"Läser in kortkommandon, var god vänta...\"\nBasic.Hotkeys.SelectScene=\"Byt till scen\"\nBasic.SystemTray.Show=\"Visa\"\nBasic.SystemTray.Hide=\"Dölj\"\nBasic.SystemTray.Message.Reconnecting=\"Frånkopplad. Återansluter...\"\nHotkeys.Backspace=\"Backsteg\"\nHotkeys.Tab=\"Tabb\"\nHotkeys.Left=\"Vänster\"\nHotkeys.Right=\"Höger\"\nHotkeys.Up=\"Upp\"\nHotkeys.Down=\"Ned\"\nHotkeys.Menu=\"Meny\"\nHotkeys.Space=\"Mellanslag\"\nHotkeys.NumpadNum=\"%1 (Numpad)\"\nHotkeys.NumpadMultiply=\"* (Numpad)\"\nHotkeys.NumpadDivide=\"/ (Numpad)\"\nHotkeys.NumpadAdd=\"+ (Numpad)\"\nHotkeys.NumpadSubtract=\"- (Numpad)\"\nHotkeys.NumpadDecimal=\", (Numpad)\"\nHotkeys.AppleKeypadNum=\"%1 (numpad)\"\nHotkeys.AppleKeypadMultiply=\"* (numpad)\"\nHotkeys.AppleKeypadDivide=\"/ (numpad)\"\nHotkeys.AppleKeypadAdd=\"+ (numpad)\"\nHotkeys.AppleKeypadSubtract=\"- (numpad)\"\nHotkeys.AppleKeypadDecimal=\". (numpad)\"\nHotkeys.AppleKeypadEqual=\"= (numpad)\"\nHotkeys.MouseButton=\"Musknapp %1\"\nMute=\"Stäng av ljud\"\nUnmute=\"Slå på ljud\"\nPush-to-mute=\"Tryck för att tysta\"\nPush-to-talk=\"Tryck för att tala\"\nSceneItemShow=\"Visa \\\"%1\\\"\"\nSceneItemHide=\"Dölj \\\"%1\\\"\"\nOutputWarnings.NoTracksSelected=\"Du måste välja minst ett spår\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Fel i utmatningsinställningarna\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Alla utmatningar måste ha minst ett valt ljudspår.\"\nOutputWarnings.MP4Recording=\"Varning: Inspelningar som sparas i MP4/MOV kommer inte kunna återhämtas om filen inte slutförs (t.ex. p.g.a. blåskärmar, strömavbrott, etc.). Om du vill spela in flera ljudspår, överväg att använda MKV och remuxa inspelningen till MP4/MOV när den är färdig (Arkiv → Remuxa inspelningar)\"\nOutputWarnings.CannotPause=\"Varning: Inspelningar kan inte pausas om inspelningskodaren är \\\"(Använd strömkodare)\\\"\"\nOutputWarnings.CodecIncompatible=\"Vald ljud- eller videoenkodare var återställd då den var inkompatibel. Vänligen välj en kompatibel enkodare från listan.\"\nCodecCompat.Incompatible=\"(Inkompatibel med %1)\"\nCodecCompat.CodecPlaceholder=\"Välj kodare...\"\nCodecCompat.ContainerPlaceholder=\"Välj format...\"\nCodecCompat.CodecMissingOnExit.Title=\"Ingen kodare har valts\"\nCodecCompat.CodecMissingOnExit.Text=\"Minst en video- eller ljudkodare har inte valts. Se till att välja kodare för både inspelning och strömning.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Inget format har valts\"\nCodecCompat.ContainerMissingOnExit.Text=\"Inget inspelningsformat har valts. Välj ett inspelningsformat som är kompatibelt med den valda strömkodaren.\"\nFinalScene.Title=\"Radera scen\"\nFinalScene.Text=\"Det måste finnas minst en scen.\"\nNoSources.Title=\"Inga källor\"\nNoSources.Text=\"Det verkar som om du inte har lagt till några videokällor än, så du kommer endast att visa en tom skärm. Är du säker på att du vill göra detta?\"\nNoSources.Text.AddSource=\"Du kan lägga till källor genom att klicka på plusikonen under rutan \\\"källor\\\" i huvudfönstret när som helst.\"\nNoSources.Label=\"Du har inga källor.\\nKlicka på plusknappen nedan eller\\nhögerklicka för att lägga till en.\"\nChangeBG=\"Ändra färg\"\nCustomColor=\"Anpassad färg\"\nBrowserSource.EnableHardwareAcceleration=\"Aktiverar webbläsarkällans hårdvaruaccelerering\"\nAbout=\"Om\"\nAbout.Info=\"OBS Studio är en fri och öppen programvara gjort för att spela in och direktsända video.\"\nAbout.Donate=\"Lämna ett bidrag\"\nAbout.GetInvolved=\"Engagera dig\"\nAbout.Authors=\"Upphovsmän\"\nAbout.License=\"Licens\"\nAbout.Error=\"Fel! Filen kunde inte läsas in.\\n\\nGå till: %1\"\nAbout.Contribute=\"Stöd OBS-projektet\"\nAddUrl.Title=\"Lägg till källa via webbadress\"\nAddUrl.Text=\"Du har dragit en URL till OBS. Detta kommer automatiskt att lägga till länken som källa. Fortsätt?\"\nAddUrl.Text.Url=\"Webbadress: %1\"\nResizeOutputSizeOfSource=\"Storleksändra utmatning (källans storlek)\"\nResizeOutputSizeOfSource.Text=\"Grund- och utmatningsupplösningarna kommer att ändras till storleken för den nuvarande källan.\"\nResizeOutputSizeOfSource.Continue=\"Vill du fortsätta?\"\nPreviewTransition=\"Förhandsvisa övergång\"\nImporter=\"Importering av scensamling\"\nImporter.SelectCollection=\"Välj en scensamling\"\nImporter.Collection=\"Scensamling\"\nImporter.HelpText=\"Lägg till filer i detta fönster för att importera samlingar från OBS eller andra program som stöds.\"\nImporter.Path=\"Sökväg för samling\"\nImporter.Program=\"Upptäckta applikationer\"\nImporter.AutomaticCollectionPrompt=\"Sök automatiskt efter scensamlingar\"\nImporter.AutomaticCollectionText=\"OBS kan automatiskt hitta importerbara scensamlingar från tredjepartsprogram som stöds. Vill du att OBS automatiskt ska hitta samlingar åt dig?\\n\\nDu kan ändra detta senare i Inställningar > Allmänt > Importörer.\"\nImporter.SelectFile=\"Bläddra...\"\nRestart=\"Starta om\"\nNeedsRestart=\"OBS Studio behöver startas om. Vill du starta om nu?\"\nLoadProfileNeedsRestart=\"Profilen innehåller inställningar som kräver att OBS startas om:\\n%1\\n\\nVill du starta om OBS för att dessa inställningar ska träda i kraft?\"\nContextBar.NoSelectedSource=\"Inga valda källor\"\nContextBar.MediaControls.PlayMedia=\"Spela upp media\"\nContextBar.MediaControls.PauseMedia=\"Pausa media\"\nContextBar.MediaControls.StopMedia=\"Stoppa media\"\nContextBar.MediaControls.RestartMedia=\"Starta om media\"\nContextBar.MediaControls.PlaylistNext=\"Nästa i spellistan\"\nContextBar.MediaControls.PlaylistPrevious=\"Föregående i spellistan\"\nContextBar.MediaControls.BlindSeek=\"Widget för mediasökning\"\nYouTube.Auth.Ok=\"Auktoriseringen slutförd.\\nDu kan nu stänga denna sida.\"\nYouTube.Auth.NoCode=\"Auktoriseringsprocessen slutfördes inte.\"\nYouTube.Auth.NoChannels=\"Ingen kanal(er) är tillgängliga på det valda kontot\"\nYouTube.Auth.WaitingAuth.Title=\"Auktorisering av YouTube-användare\"\nYouTube.Auth.WaitingAuth.Text=\"Vänligen slutför auktoriseringen i din externa webbläsare.<br>Om den externa webbläsaren inte öppnas, följ denna länk och slutför auktoriseringen:<br>%1\"\nYouTube.AuthError.Text=\"Misslyckades att hämta kanalinformation: %1.\"\nYouTube.Actions.WindowTitle=\"Konfiguration för Youtube-sändning - Kanal: %1\"\nYouTube.Actions.CreateNewEvent=\"Skapa ny sändning\"\nYouTube.Actions.ChooseEvent=\"Välj befintlig sändning\"\nYouTube.Actions.Title=\"Titel*\"\nYouTube.Actions.MyBroadcast=\"Min sändning\"\nYouTube.Actions.Description=\"Beskrivning\"\nYouTube.Actions.Privacy=\"Integritet*\"\nYouTube.Actions.Privacy.Private=\"Privat\"\nYouTube.Actions.Privacy.Public=\"Offentlig\"\nYouTube.Actions.Privacy.Unlisted=\"Olistad\"\nYouTube.Actions.Category=\"Kategori\"\nYouTube.Actions.Thumbnail=\"Miniatyr\"\nYouTube.Actions.Thumbnail.SelectFile=\"Välj fil...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Ingen fil har valts\"\nYouTube.Actions.Thumbnail.ClearFile=\"Rensa\"\nYouTube.Actions.MadeForKids=\"Är den här videon gjord för barn?*\"\nYouTube.Actions.MadeForKids.Yes=\"Ja, den är gjord för barn\"\nYouTube.Actions.MadeForKids.No=\"Nej, den är inte gjord för barn\"\nYouTube.Actions.AdditionalSettings=\"Ytterligare inställningar\"\nYouTube.Actions.Latency=\"Latens\"\nYouTube.Actions.Latency.Low=\"Låg\"\nYouTube.Actions.Latency.UltraLow=\"Ultralåg\"\nYouTube.Actions.EnableAutoStart=\"Aktivera auto-start\"\nYouTube.Actions.EnableAutoStop=\"Aktivera auto-stopp\"\nYouTube.Actions.AutoStartStop.TT=\"Indikerar om denna schemalagda sändning ska starta automatiskt\"\nYouTube.Actions.EnableDVR=\"Aktivera DVR\"\nYouTube.Actions.360Video=\"360-video\"\nYouTube.Actions.ScheduleForLater=\"Schemalägg till senare\"\nYouTube.Actions.RememberSettings=\"Kom ihåg dessa inställningar\"\nYouTube.Actions.Create_Ready=\"Skapa sändning\"\nYouTube.Actions.Create_GoLive=\"Skapa sändning och börja strömma\"\nYouTube.Actions.Choose_Ready=\"Välj sändning\"\nYouTube.Actions.Choose_GoLive=\"Välj sändning och börja strömma\"\nYouTube.Actions.Create_Schedule=\"Schemalägg sändning\"\nYouTube.Actions.Create_Schedule_Ready=\"Schemalägg och välj sändning\"\nYouTube.Actions.Dashboard=\"Öppna YouTube Studio\"\nYouTube.Actions.Error.Title=\"Fel vid skapande av livesändning\"\nYouTube.Actions.Error.Text=\"YouTube-åtkomstfel \\\"%1\\\".<br/>Detaljerad felbeskrivning kan hittas på <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Fel vid åtkomst till YouTube. Kontrollera din nätverksanslutning eller din åtkomst till YouTube-servern.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Fel vid skapande av sändning \\\"%1\\\".<br/>Detaljerad felbeskrivning kan hittas på <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Ingen ström skapades. Var god länka om ditt konto.\"\nYouTube.Actions.Error.YouTubeApi=\"Fel i YouTubes API. Se loggfilen för mer information.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Den valda sändningen hittades inte.\"\nYouTube.Actions.Error.FileMissing=\"Vald fil finns inte.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Misslyckades att öppna vald fil.\"\nYouTube.Actions.Error.FileTooLarge=\"Vald fil är för stor (max: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Övergång till sändningen misslyckades: %1<br/><br/>Om detta fel kvarstår, <a href='https://studio.youtube.com/video/%2/livestreaming'>öppna sändningen i YouTube Studio</a> och försök manuellt.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Sändningen övergår till teststadiet och kan ta en stund. Försök igen om 10-30 sekunder.\"\nYouTube.Actions.EventsLoading=\"Läser in lista över händelser...\"\nYouTube.Actions.EventCreated.Title=\"Händelse skapades\"\nYouTube.Actions.EventCreated.Text=\"Händelse skapades.\"\nYouTube.Actions.Stream=\"Strömma\"\nYouTube.Actions.Stream.ScheduledFor=\"Schemalagd för %1\"\nYouTube.Actions.Stream.Resume=\"Återuppta avbruten ström\"\nYouTube.Actions.Stream.YTStudio=\"Skapades automatiskt av YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Skapar en ny direktsändning, var god vänta...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Manuell start krävs\"\nYouTube.Actions.AutoStartStreamingWarning=\"Automatisk start har inaktiverats för denna händelse, klicka på \\\"Direktsänd\\\" för att starta din sändning.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Du kommer inte att kunna återansluta.<br>Din ström kommer att stoppas och du kommer inte längre direktsända.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Direktsändning har inte aktiverats på den valda YouTube-kanalen.<br/><br/>Se <a href='https://www.youtube.com/features'>youtube.com/features</a> för mer information.\"\nYouTube.Errors.livePermissionBlocked=\"Direktsändning är inte tillgänglig på den valda YouTube-kanalen.<br/>Observera att det kan ta upp till 24 timmar för direktsändning att bli tillgänglig efter att ha aktiverat det i dina kanalinställningar.<br/><br/>Se <a href='https://www.youtube.com/features'>youtube.com/features</a> för detaljer.\"\nYouTube.Errors.errorExecutingTransition=\"Övergången misslyckades på grund av ett fel i backend. Försök igen om några sekunder.\"\nYouTube.Errors.errorStreamInactive=\"YouTube tar inte emot data för din ström. Kontrollera din konfiguration och försök igen.\"\nYouTube.Errors.invalidTransition=\"Ogiltigt försök till övergång. Detta kan bero på att strömmen inte har avslutat en tidigare övergång. Vänta några sekunder och försök igen.\"\nYouTube.DocksRemoval.Title=\"Rensa bakåtkompatibelt flytande webbläsarfönster för YouTube\"\nYouTube.DocksRemoval.Text=\"Dessa flytande webbläsarfönster kommer att tas bort p.g.a. utfasning:\\n\\n%1\\nAnvänd \\\"Flytande fönster/Kontrollrummet på YouTube\\\" istället.\"\nConfigDownload.WarningMessageTitle=\"Varning\"\nFailedToStartStream.MissingConfigURL=\"Ingen konfigurerings-URL är tillgänglig för den nuvarande tjänsten\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Ingen anpassad webbadress för RTMP har angetts\"\nFailedToStartStream.InvalidCustomConfig=\"Ogiltig anpassad konfiguration\"\nFailedToStartStream.MissingCanvas=\"En konfigurerad extra kanvas saknas.\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Misslyckades att skapa videotjänst med flera spår\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Misslyckades att skapa RTMP-utmatning för video med flera spår\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC är inte tillgänglig.\\n\\nMisslyckades att hitta kodartypen \\\"%1\\\"\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Misslyckades att skapa videokodaren \\\"%1\\\" (typ: \\\"%2\\\")\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Misslyckades att hämta OBS-videoinformation när kodaren \\\"%1\\\" (typ: \\\"%2\\\") skapades\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Misslyckades att skapa ljudkodare\"\nFailedToStartStream.NoRTMPURLInConfig=\"Konfigurationen innehåller inte RTMP(S)-webbadress för strömmålet\"\nFailedToStartStream.FallbackToDefault=\"Misslyckades att starta strömmen med %1. Vill du försöka igen med inställningar för en enda kodare?\"\nFailedToStartStream.ConfigRequestFailed=\"Kunde inte hämta konfiguration från %1<br><br>HTTP-fel: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Fick det okända statusvärdet \\\"%1\\\"\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nVill du fortsätta strömma utan %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nVill du fortsätta strömma?\"\nFailedToStartStream.MissingEncoderConfigs=\"Konfiguration för Direktsänd innehöll inte kodningskonfiguration\"\nFailedToStartStream.InvalidEncoderConfig=\"Konfiguration för Direktsänd innehöll en ogiltig kodningskonfiguration\"\nFailedToStartStream.StatusMissingHTML=\"Begäran om direktsändning returnerade ett ospecificerat fel\"\nFailedToStartStream.NoConfigSupplied=\"Konfiguration saknas\"\nMultitrackVideo.Info=\"%1 optimerar automatiskt dina inställningar för att koda och skicka flera videokvaliteter. Om detta alternativ väljs kommer %2-information om din dator och mjukvaruinstallation att skickas.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Inkompatibla inställningar\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 är för närvarande inkompatibelt med:\\n\\n%2\\nFör att fortsätta strömma med %1, inaktivera inkompatibla inställningar:\\n\\n%3\\noch Börja strömma igen.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Inaktivera för denna ström och börja strömma\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Uppdatera inställningar och börja strömma\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 är för tillfället inkompatibel med [Ljud → Allmänt → Kanaler] inställt på \\\"%2\\\", %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Ljud → Allmänt → Kanaler] måste vara inställt på \\\"%1\\\"\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 kräver flera olika inställningar för [Ljud → Allmänt → Kanaler]\"\nBasic.OpenPluginManager=\"Insticksprogramshanterare\"\nPluginManager=\"Insticksprogramshanterare\"\nPluginManager.Restart=\"OBS Studio måste startas om för att tillämpa ändringar för insticksprogram\"\nPluginManager.MissingPlugin=\"[INSTICKSPROGRAMMET HITTADES INTE]\"\nPluginManager.Section.Discover=\"Bläddra\"\nPluginManager.Section.Manage=\"Installerade\"\nPluginManager.Section.Updates=\"Uppdateringar\"\nPluginManager.Section.Manage.Title=\"Hanterade aktiverade insticksprogram\"\n"
  },
  {
    "path": "frontend/data/locale/szl-PL.ini",
    "content": "Language=\"Ślōnskŏ\"\nApply=\"Zastosuj\"\nCancel=\"Pociep\"\nClose=\"Zawrzij\"\nSave=\"Zapisz\"\nDiscard=\"Ôdciep\"\nDisable=\"wyłōnczōne\"\nYes=\"Ja\"\nNo=\"Niy\"\nAdd=\"Przidej\"\nRemove=\"Wyciep\"\nRename=\"Zmiyń miano\"\nInteract=\"Włōncz interakcyjo\"\nFilters=\"Filtry\"\nProperties=\"Właściwości\"\nMoveUp=\"Przeniyś na wiyrch\"\nMoveDown=\"Przeniyś na spodek\"\nSettings=\"Ôpcyje\"\nDisplay=\"Ekran\"\nName=\"Miano\"\nExit=\"Zawrzij\"\nMixer=\"Mikser klangu\"\nBrowse=\"Przeglōndej\"\nDroppedFrames=\"Stracōne klotki %1 (%2%)\"\nClear=\"Wypucuj\"\nRevert=\"Prziwrōć\"\nShow=\"Pokoż\"\nHide=\"Skryj\"\nUnhideAll=\"Pokoż wszyjske\"\nUntitled=\"Bez tytułu\"\nNew=\"Nowy\"\nDuplicate=\"Zrōb tuplikat\"\nEnable=\"Włōncz\"\nDisableOSXVSync=\"Wyłōncz piōnowo synchrōnizacyjo macOS\"\nResetOSXVSyncOnExit=\"Prziwrōć piōnowo synchrōnizacyjo macOS po zawrzyniu aplikacyje\"\nHighResourceUsage=\"Enkodowanie je przeciōnżōne! Zmiyń ôpcyje wideo abo użyj gibszego presetu kodowania.\"\nTransition=\"Przejście\"\nQuickTransitions=\"Gibke przejścia\"\nFadeToBlack=\"Zanikanie do czyrni\"\nLeft=\"Lewo\"\nRight=\"Prawo\"\nTop=\"Wiyrch\"\nBottom=\"Spodek\"\nHours=\"Godziny\"\nMinutes=\"Minuty\"\nSeconds=\"Sekōndy\"\nDeprecated=\"Wycofowane\"\nReplayBuffer=\"Nagrowanie powtōrek\"\nImport=\"Importuj\"\nExport=\"Eksportuj\"\nCopy=\"Kopiyruj\"\nPaste=\"Wkludź\"\nPasteReference=\"Wkludź (referyncyjo)\"\nPasteDuplicate=\"Wkludź (tuplikat)\"\nRemuxRecordings=\"Przepakuj nagrania\"\nNext=\"Niyskorniyjszy\"\nBack=\"Nazod\"\nDefaults=\"Sztandardowe\"\nHideMixer=\"Skryj we mikserze\"\nTransitionOverride=\"Zastōmpiynie przejścio\"\nShowTransition=\"Pokoż przejście\"\nHideTransition=\"Skryj przejście\"\nNone=\"Brak\"\nStudioMode.Preview=\"Podglōnd\"\nShowInMultiview=\"Pokoż we Multiview\"\nVerticalLayout=\"Ukłod piōnowy\"\nGroup=\"Skupina\"\nDoNotShowAgain=\"Niy pokazuj zaś\"\nDefault=\"(Sztandardowo)\"\nCalculating=\"Rachowanie...\"\nFullscreen=\"Połny ekran\"\nWindowed=\"We ôknie\"\nRefreshBrowser=\"Ôdświyż\"\nAspectRatio=\"Spōłczynnik proporcyje <b>%1:%2</b>\"\nLockVolume=\"Zaszperuj głośność\"\nLogViewer=\"Podglōnd logōw\"\nShowOnStartup=\"Pokoż na sztarcie\"\nOpenFile=\"Ôtwōrz zbiōr\"\nAlreadyRunning.Title=\"OBS już je sztartniynty\"\nAlreadyRunning.Text=\"OBS już je sztartniynty! Dej pozōr na wszyjske sztartniynte wystōmpiynia OBS nim sztartniesz go zaś. Jeźli OBS je zminimalizowany do zasobnika systymowego, ôbejzdrzij eli niyma ôn sztartniynty tyż sam.\"\nAlreadyRunning.LaunchAnyway=\"Sztartnij krōm tego\"\nChromeOS.Title=\"Niypodpiyrano platforma\"\nDockCloseWarning.Title=\"Zawrzij ôkno dokowalne\"\nExtraBrowsers=\"Panele przeziyroczki\"\nExtraBrowsers.Info=\"Przidej panele bez ôkryślynie ônych miana a adresa URL a potym kliknij Zastosuj abo Zawrzij by jy ôtworzić. Panele idzie przidać i wyciepać w kożdyj chwili.\"\nExtraBrowsers.DockName=\"Miano panelu\"\nAuth.Authing.Title=\"Uwierzitelnianie...\"\nAuth.Authing.Text=\"Doczkej. Trwo uwierzitelnianie we %1...\"\nAuth.AuthFailure.Title=\"Feler przi uwierzitelniyniu\"\nAuth.AuthFailure.Text=\"Niy idzie uwierzitelnić we %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Wymogane uwierzitelniynie\"\nAuth.InvalidScope.Text=\"Zmiyniyły sie wymogania uwierzitelnianio dlo %1. Niykere funkcyje mogōm być niydostympne.\"\nAuth.LoadingChannel.Title=\"Ladowanie informacyje ô kanale...\"\nAuth.LoadingChannel.Text=\"Doczkej. Trwo ladowanie informacyje ô kanale w zerwisie %1...\"\nAuth.ChannelFailure.Title=\"Niy idzie zaladować informacyje ô kanale\"\nAuth.ChannelFailure.Text=\"Niy idzie zaladować informacyje ô kanale w zerwisie %1\\n\\n%2: %3\"\nAuth.Chat=\"Czat\"\nAuth.StreamInfo=\"Informacyjo ô streamie\"\nTwitchAuth.Stats=\"Sztatystyki Twitch\"\nTwitchAuth.Feed=\"Wykoz aktywności Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Niy idzie pobrać klucza streamu\"\nTwitchAuth.TwoFactorFail.Text=\"Feler przi powiōnzaniu Twojigo kōnta Twitch we OBS. Ôbejzdrzij, eli mosz włōnczōne uwierzitelnianie dwuskładnikowe we <a href='https://www.twitch.tv/settings/security'>ôpcyjach bezpiyczyństwa Twitcha</a>. Wymogane je to do streamowania bez ta platforma.\"\nRestreamAuth.Channels=\"Kanały Restream\"\nCopy.Filters=\"Kopiyruj filtry\"\nPaste.Filters=\"Wkludź filtry\"\nBrowserPanelInit.Title=\"Inicjacyjo przeziyroczki...\"\nBrowserPanelInit.Text=\"Inicjacyjo przeziyroczki, doczkej...\"\nBandwidthTest.Region=\"Regiōn\"\nBandwidthTest.Region.US=\"Zjednoczōne Sztaty\"\nBandwidthTest.Region.EU=\"Ojropa\"\nBandwidthTest.Region.Asia=\"Azyjo\"\nBandwidthTest.Region.Other=\"Inkszy\"\nBasic.AutoConfig=\"Kryatōr autōmatycznyj kōnfiguracyje\"\nBasic.AutoConfig.ApplySettings=\"Zastosuj ustawiynia\"\nBasic.AutoConfig.StartPage=\"Informacyje ô używaniu\"\nBasic.AutoConfig.StartPage.SubTitle=\"Ôkryśl jak chcesz używać aplikacyje\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Ôptymalizacyjo pod streaming, a nagrowanie je drugorzyndne\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Ôptymalizacyjo pod nagrowanie, niy byda streamować\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Byda używać ino wirtualnyj kamery\"\nBasic.AutoConfig.VideoPage=\"Ôpcyje wideo\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Ôkryśl ustawiynia wideo, kere chcesz użyć\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Użyj teroźnyj (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Mōnitōr %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Użyj teroźnego (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 abo 30, nale wola 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 abo 30, nale wola lepszo rozdzielczość\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Bazowo rozdzielczość niy musi być rozdzielczościōm dlo wynikowego nagranio abo streamu. Ta drugo może być efektym skalowanio ku cylu zmyńszynio wymogań poziōmu przepływności abo dostympności zasobōw przidajnych do kōmpresyje.\"\nBasic.AutoConfig.StreamPage=\"Informacyjo ô streamie\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Podej informacyje ô streamie\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Podłōncz kōnto (rekōmandowane)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Ôdłōncz kōnto\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Ôdłōnczyć kōnto?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Pobier klucz\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Wiyncyj info\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Użyj klucza streamu\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Użyj klucza streamu (zaawansowane)\"\nBasic.AutoConfig.StreamPage.Service=\"Zerwis\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Pokoż wszyjske...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Niysztandardowy...\"\nBasic.AutoConfig.StreamPage.Server=\"Serwer\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Klucz streamu\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Klucz enkodera\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Połōnczōne kōnto\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Wola enkodowanie sprzyntowe\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Ôstrzeżynie\"\nBasic.AutoConfig.TestPage=\"Wyniki kōńcowe\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Testowanie zakōńczōne\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Łōnczynie ze: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Testuja przepustowość dlo: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Testuja enkoder streamu, może to chwila potrwać...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Testuja enkoder nagrowanio, może to chwila potrwać...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Niy idzie sztartnōńć enkodera\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Testuja %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Enkoder streamu\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Enkoder nagrowanio\"\nBasic.AutoConfig.TestPage.Result.Header=\"Aplikacyjo ôbsztalowała te ustawiynia za nojbarzij idyalne dlo Ciebie:\"\nBasic.Stats=\"Sztatystyki\"\nBasic.Stats.CPUUsage=\"Użycie procesōra\"\nBasic.Stats.HDDSpaceAvailable=\"Dostympny plac na dysku\"\nBasic.Stats.MemoryUsage=\"Użycie spamiyńci\"\nBasic.Stats.AverageTimeToRender=\"Strzedni czas na rynderowanie klotki\"\nBasic.Stats.SkippedFrames=\"Pōminiynte klotki skirz ôpōźniyń enkodera\"\nBasic.Stats.MissedFrames=\"Pōminiynte klotki skirz ôpōźniyń rynderowanio\"\nBasic.Stats.Output.Recording=\"Nagrowanie\"\nBasic.Stats.Status=\"Sztatus\"\nBasic.Stats.Status.Recording=\"Nagrowanie\"\nBasic.Stats.Status.Reconnecting=\"Pōnowne łōnczynie\"\nBasic.Stats.Status.Inactive=\"Niyaktywne\"\nBasic.Stats.Status.Active=\"Aktywne\"\nBasic.Stats.DroppedFrames=\"Stracōne klotki (nec)\"\nBasic.Stats.MegabytesSent=\"Cołke dane wychodowe\"\nBasic.Stats.DiskFullIn=\"Dysk bydzie połny za (kole)\"\nBasic.Stats.ResetStats=\"Resetuj sztatystyki\"\nResetUIWarning.Title=\"Chcesz zresetować wyglōnd aplikacyje?\"\nUpdater.Title=\"Nowo aktualizacyjo je dostympno\"\nUpdater.Text=\"Nowo aktualizacyjo je dostympno:\"\nUpdater.UpdateNow=\"Aktualizuj teroz\"\nUpdater.RemindMeLater=\"Spōmnij mi niyskorzij\"\nUpdater.Skip=\"Pōmiń ta wersyjo\"\nUpdater.NoUpdatesAvailable.Title=\"Brak dostympnych aktualizacyji\"\nUpdater.NoUpdatesAvailable.Text=\"Brak dostympnych aktualizacyji\"\nUpdater.FailedToLaunch=\"Feler przi sztartowaniu aktualizacyje\"\nQuickTransitions.DuplicateScene=\"Tupluj bina\"\nQuickTransitions.EditProperties=\"Tupluj zdrzōdła\"\nQuickTransitions.HotkeyName=\"Gibke przejście: %1\"\nBasic.AddTransition=\"Przidej kōnfigurowalne przejście\"\nBasic.RemoveTransition=\"Wyciep kōnfigurowalne przejście\"\nBasic.TransitionProperties=\"Włosności przejścio\"\nBasic.SceneTransitions=\"Przejścia biny\"\nBasic.TransitionDuration=\"Czas trwanio\"\nBasic.TogglePreviewProgramMode=\"Tryb sztudia\"\nUndo.Undo=\"Cofej\"\nUndo.Redo=\"Wrōć\"\nUndo.Add=\"Przidej %1'\"\nUndo.Delete=\"Wyciep '%1'\"\nUndo.Rename=\"Zmiyń miano '%1'\"\nUndo.SceneCollection.Switch=\"Przeszaltruj na '%1'\"\nUndo.Item.Undo=\"Cofej %1\"\nUndo.Item.Redo=\"Wrōć %1\"\nUndo.Sources.Multi=\"Wyciep zdrzōdła: %1\"\nUndo.Filters=\"Zmiany filtra we '%1'\"\nUndo.Filters.Paste.Single=\"Wkludź filtr '%1' do '%2'\"\nUndo.Filters.Paste.Multiple=\"Kopiyruj filtry z '%1' do '%2'\"\nUndo.Transform=\"Przekształć zdrzōdło(a) we '%1'\"\nUndo.Transform.Paste=\"Wkludź przekształcynie we '%1'\"\nUndo.Transform.Rotate=\"Rotacyjo we '%1'\"\nUndo.Transform.Reset=\"Reset przekształcyń we '%1'\"\nUndo.Transform.HFlip=\"Ôdwrōć w poziōmie we '%1'\"\nUndo.Transform.VFlip=\"Ôdwrōć w piōnie we '%1'\"\nUndo.Transform.FitToScreen=\"Napasuj do ekranu we '%1'\"\nUndo.Transform.StretchToScreen=\"Rozciōngnij do ekranu we '%1'\"\nUndo.Transform.Center=\"Wypostrzodkuj na ekranie we '%1'\"\nUndo.Transform.VCenter=\"Wypostrzodkuj w piōnie we '%1'\"\nUndo.Transform.HCenter=\"Wypostrzodkuj w poziōmie we '%1'\"\nUndo.Volume.Change=\"Zmiana głośności na '%1'\"\nUndo.Volume.Mute=\"Wycisz '%1'\"\nUndo.Volume.Unmute=\"Wyłōncz wyciszynie '%1'\"\nUndo.Balance.Change=\"Zmiana balansu klangu na '%1'\"\nUndo.SyncOffset.Change=\"Zmiana cofniyńco klangu na '%1'\"\nUndo.MonitoringType.Change=\"Zmiana mōnitorowanio klangu na '%1'\"\nUndo.Mixers.Change=\"Zmiana we miksyrze klangu na '%1'\"\nUndo.ForceMono.On=\"Włōncz wymuszynie klangu mono na '%1'\"\nUndo.ForceMono.Off=\"Wyłōncz wymuszynie klangu mono na '%1'\"\nUndo.Properties=\"Zmiana włosności we '%1'\"\nUndo.Scene.Duplicate=\"Tupluj bina '%1'\"\nUndo.ShowTransition=\"Pokoż przejście na '%1'\"\nUndo.HideTransition=\"Skryj przejście na '%1'\"\nUndo.ShowSceneItem=\"Pokoż '%1' we '%2'\"\nUndo.HideSceneItem=\"Skryj '%1' we '%2'\"\nUndo.ReorderSources=\"Zmiyń porzōndek zdrzōdeł we '%1'\"\nUndo.MoveUp=\"Przeniyś '%1' w gōra we '%2'\"\nUndo.MoveDown=\"Przeniyś '%1' we dōł we '%2'\"\nUndo.MoveToTop=\"Przeniyś '%1' na wiyrch we '%2'\"\nUndo.MoveToBottom=\"Przeniyś '%1' na spodek we '%2'\"\nUndo.PasteSource=\"Wkludź zdrzōdło(a) we '%1'\"\nUndo.PasteSourceRef=\"Wkludź ôdniesiynie(a) do zdrzōdła we '%1'\"\nUndo.GroupItems=\"Grupuj we '%1'\"\nTransitionNameDlg.Text=\"Podej miano przejścio\"\nTransitionNameDlg.Title=\"Miano przejścio\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Biny\"\nNameExists.Title=\"Miano już istniyje\"\nNameExists.Text=\"Miano je już używane.\"\nNoNameEntered.Title=\"Wkludź dobre miano\"\nNoNameEntered.Text=\"Miano niy może być prōżne.\"\nConfirmStart.Title=\"Sztartnōńć stream?\"\nConfirmStart.Text=\"Chcesz sztartnōńć streamowanie?\"\nConfirmStop.Title=\"Zasztopować stream?\"\nConfirmStop.Text=\"Chcesz zakōńczyć streamowanie?\"\nConfirmStopRecord.Title=\"Zasztopować nagrowanie?\"\nConfirmStopRecord.Text=\"Chcesz zakończyć nagrowanie?\"\nConfirmBWTest.Title=\"Sztartnōńć test przepustowości?\"\nConfirmRemove.Title=\"Potwiyrdź wyciepniyńcie\"\nOutput.StartStreamFailed=\"Niy podarziło sie sztartnōńć streamowanio\"\nOutput.StartRecordingFailed=\"Niy podarziło sie sztartnōńć nagrowanio\"\nOutput.StartReplayFailed=\"Niy podarziło sie sztartnōńć nagrowanio powtōrek\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Niy idzie zapisać powtōrek w czasie pauzy nagrowanio\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Pozōr: Powtōrki niy mogōm być zapisane, kej nagrowanie je spauzowane.\"\nOutput.ConnectFail.Title=\"Niy idzie połōnczyć\"\nOutput.ConnectFail.BadPath=\"Niynoleżno cesta abo adresa URL ôd połōnczynio. Dej pozōr na noleżność ustawiyń.\"\nOutput.ConnectFail.ConnectFailed=\"Niy podarziło sie połōnczyć z serwerym\"\nOutput.ConnectFail.Disconnected=\"Stracōne połōnczynie z serwerym.\"\nOutput.StreamEncodeError.Title=\"Feler przi enkodowaniu\"\nOutput.StreamEncodeError.Msg=\"Feler enkodowanio wystōmpiōł w czasie transmisyje.\"\nOutput.RecordFail.Title=\"Niy idzie sztartnōńć nagrowanio\"\nOutput.RecordNoSpace.Title=\"Za mało placu na dysku\"\nOutput.RecordError.Title=\"Feler przi nagrowaniu\"\nOutput.BadPath.Title=\"Zło ściyżka zbioru\"\nRemux.SourceFile=\"Zbiōr nagranio z OBS\"\nRemux.TargetFile=\"Zbiōr docelowy\"\nRemux.Stop=\"Zasztopuj remux\"\nRemux.ClearFinished=\"Wyciep zakōńczōne auftragi\"\nRemux.ClearAll=\"Wyciep wszyjske auftragi\"\nRemux.OBSRecording=\"Zbiōr nagranio z OBS\"\nRemux.FinishedTitle=\"Remux zakōńczōny\"\nRemux.SelectRecording=\"Ôbier nagranie OBS...\"\nRemux.SelectTarget=\"Ôbier zbiōr docelowy...\"\nRemux.FileExistsTitle=\"Docelowy zbiōr istniyje\"\nRemux.FileExists=\"Te zbiory już istniyjōm. Chcesz jy nadpisać?\"\nRemux.ExitUnfinishedTitle=\"Remux w ciōngu\"\nMissingFiles=\"Brakujōnce zbiory\"\nMissingFiles.MissingFile=\"Brakujōncy zbiōr\"\nMissingFiles.NewFile=\"Nowy zbiōr\"\nMissingFiles.HelpText=\"Ôd piyrwyjszego użycio OBS brakuje niykerych zbiorōw.\"\nMissingFiles.Clear=\"<wyciepniynty>\"\nMissingFiles.Search=\"Przeszukej katalog...\"\nMissingFiles.SelectFile=\"Ôbier zbiōr...\"\nMissingFiles.SelectDir=\"Ôbier folder do przeszukanio\"\nMissingFiles.State=\"Sztatus\"\nMissingFiles.Missing=\"Brakujōncy\"\nMissingFiles.Replaced=\"Zastōmpiōny\"\nMissingFiles.Cleared=\"Wyciepniynty\"\nMissingFiles.Found=\"Znodniynty\"\nMissingFiles.AutoSearch=\"Znodniynte inksze napasowanie zbiorōw\"\nMissingFiles.AutoSearchText=\"OBS znod inksze napasowania zbiorōw, kerych brak w tym katalogu. Chcesz jy przidać?\"\nBasic.DesktopDevice1=\"Maszina audio\"\nBasic.DesktopDevice2=\"Maszina audio 2\"\nBasic.AuxDevice1=\"Mikrofōn/Wchōd linijowy\"\nBasic.AuxDevice2=\"Mikrofōn/Wchōd linijowy 2\"\nBasic.AuxDevice3=\"Mikrofōn/Wchōd linijowy 3\"\nBasic.AuxDevice4=\"Mikrofōn/Wchōd linijowy 4\"\nBasic.Scene=\"Bina\"\nBasic.DisplayCapture=\"Przechytywanie ekranu\"\nBasic.Main.PreviewConextMenu.Enable=\"Włōncz podglōnd\"\nBasic.Main.Preview.Disable=\"Wyłōncz podglōnd\"\nScaleFiltering=\"Filtrowanie skalowanio\"\nScaleFiltering.Point=\"Pōnktowe\"\nScaleFiltering.Bilinear=\"Dwulinijowe\"\nScaleFiltering.Bicubic=\"Dwusześciynne\"\nScaleFiltering.Area=\"Przestrzyństwo\"\nDeinterlacing=\"Wyciepowanie przeplotu\"\nDeinterlacing.Discard=\"Ôdciep\"\nDeinterlacing.TopFieldFirst=\"Nojprzōd pole wiyrchnie\"\nDeinterlacing.BottomFieldFirst=\"Nojprzōd pole spodnie\"\nVolControl.SliderUnmuted=\"Szalter głośności dlo '%1':\"\nVolControl.SliderMuted=\"Szalter głośności dlo %1': (teroźnie wyciszōny)\"\nVolControl.Mute=\"Wycisz '%1'\"\nVolControl.Properties=\"Włosności dlo '%1'\"\nBasic.Main.AddSceneDlg.Title=\"Przidej bina\"\nBasic.Main.AddSceneDlg.Text=\"Podej miano biny\"\nBasic.Main.DefaultSceneName.Text=\"Bina %1\"\nBasic.Main.AddSceneCollection.Title=\"Przidej zbiōr bin\"\nBasic.Main.RenameSceneCollection.Title=\"Zmiyń miano zbioru bin\"\nAddProfile.Title=\"Przidej profil\"\nAddProfile.Text=\"Podej miano profilu\"\nRenameProfile.Title=\"Zmiyń miano profilu\"\nBasic.Main.MixerRename.Title=\"Zmiana miana zdrzōdła klangu\"\nBasic.Main.PreviewDisabled=\"Podglōnd je niyaktywny\"\nBasic.SourceSelect=\"Stwōrz/Ôbier zdrzōdło\"\nBasic.SourceSelect.CreateNew=\"Stwōrz nowo\"\nBasic.SourceSelect.AddExisting=\"Przidej istniyjōnco\"\nBasic.SourceSelect.AddVisible=\"Zdrzōdło widzialne\"\nBasic.Main.Sources.Visibility=\"Widzialność\"\nBasic.Main.Sources.Lock=\"Szpera\"\nBasic.PropertiesWindow=\"Włosności dlo '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (ôbiōr autōmatyczny: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Ôbier farba\"\nBasic.PropertiesWindow.SelectFont=\"Ôbier pismo\"\nBasic.PropertiesWindow.ConfirmTitle=\"Ôpcyje zmiyniōne\"\nBasic.PropertiesWindow.Confirm=\"Mosz niyzapisane zmiany. Chcesz jy zachować?\"\nBasic.PropertiesWindow.NoProperties=\"Brak włosności\"\nBasic.PropertiesWindow.AddFiles=\"Przidej zbiory\"\nBasic.PropertiesWindow.AddDir=\"Przidej folder\"\nBasic.PropertiesWindow.AddURL=\"Przidej ściyżka/adresa URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Przidej folder do '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Przidej zbiory do '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Przidej wpis do '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Edytuj wpis we '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Ajnfachowe wartości FPS\"\nBasic.PropertiesView.FPS.Rational=\"Ułōmkowe wartości FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Noleżne zakrysy FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"Ôtworzić tyn link we internetowyj przeziyroczce?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Ôtwōrz URL\"\nBasic.InteractionWindow=\"Interakcyjo ze '%1'\"\nBasic.StatusBar.Delay=\"Ôpōźniynie (%1 s)\"\nBasic.StatusBar.DelayStartingIn=\"Ôpōźniynie (sztart za %1 s)\"\nBasic.StatusBar.DelayStoppingIn=\"Ôpōźniynie (sztop za %1 s)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Ôpōźniynie (sztop za %1 s, sztart za %2 s)\"\nBasic.StatusBar.RecordingSavedTo=\"Nagranie zapisane we '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Bufōr powtōrki zapisany we '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Screenshot zapisany we '%1'\"\nBasic.Filters=\"Filtry\"\nBasic.Filters.AsyncFilters=\"Filtry audio/wideo\"\nBasic.Filters.AudioFilters=\"Filtry audio\"\nBasic.Filters.EffectFilters=\"Filtry efektōw\"\nBasic.Filters.Title=\"Filtry dlo '%1'\"\nBasic.Filters.AddFilter.Title=\"Miano filtra\"\nBasic.Filters.AddFilter.Text=\"Wpisz miano filtra\"\nBasic.TransformWindow=\"Przekształcanie elymyntu biny\"\nBasic.TransformWindow.Position=\"Pozycyjo\"\nBasic.TransformWindow.PositionX=\"Pozycyjo X\"\nBasic.TransformWindow.PositionY=\"Pozycyjo Y\"\nBasic.TransformWindow.Rotation=\"Rotacyjo\"\nBasic.TransformWindow.Size=\"Miara\"\nBasic.TransformWindow.Width=\"Szyrokość\"\nBasic.TransformWindow.Height=\"Wysokość\"\nBasic.TransformWindow.Alignment=\"Wyrōwnanie\"\nBasic.TransformWindow.BoundsType=\"Zorta ukrōcynio\"\nBasic.TransformWindow.BoundsAlignment=\"Wyrōwnanie we ôbwiedni\"\nBasic.TransformWindow.Bounds=\"Miara ôbwiedni\"\nBasic.TransformWindow.BoundsWidth=\"Szyrokość ôbwiedni\"\nBasic.TransformWindow.BoundsHeight=\"Wysokość ôbwiedni\"\nBasic.TransformWindow.Crop=\"Przicinanie\"\nBasic.TransformWindow.CropLeft=\"Przitnij ôd lewyj\"\nBasic.TransformWindow.CropRight=\"Przitnij ôd prawyj\"\nBasic.TransformWindow.CropTop=\"Przitnij ôd wiyrchni\"\nBasic.TransformWindow.CropBottom=\"Przitnij ôd spodka\"\nBasic.TransformWindow.Alignment.TopLeft=\"Lewy/wiyrchni\"\nBasic.TransformWindow.Alignment.TopCenter=\"Postrzodek/wiyrchni\"\nBasic.TransformWindow.Alignment.TopRight=\"Prawy/wiyrchni\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Lewy/postrzodek\"\nBasic.TransformWindow.Alignment.Center=\"Wypostrzodkuj\"\nBasic.TransformWindow.Alignment.CenterRight=\"Prawy/postrzodek\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Lewy/spodni\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Postrzodek/spodni\"\nBasic.TransformWindow.Alignment.BottomRight=\"Prawy/spodni\"\nBasic.TransformWindow.BoundsType.None=\"Brak ôbwiedni\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Ino maksymalno miara\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Skaluj do granic wnyntrznych\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Skaluj do granic zewnyntrznych\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Skaluj do dugości granic\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Skaluj do wysokości granic\"\nBasic.TransformWindow.BoundsType.Stretch=\"Rozciōngnij do granic\"\nBasic.Main.AddSourceHelp.Title=\"Niy idzie przidać zdrzōdła\"\nBasic.Main.AddSourceHelp.Text=\"Wymogano je chocioż jedna bina, coby przidać zdrzōdło.\"\nBasic.Main.Scenes=\"Biny\"\nBasic.Main.Sources=\"Zdrzōdła ôbrazu\"\nBasic.Main.Source=\"Zdrzōdło\"\nBasic.Main.Controls=\"Panel kerujōncy\"\nBasic.Main.Connecting=\"Łōnczynie...\"\nBasic.Main.StartRecording=\"Sztartnij nagrowanie\"\nBasic.Main.StartReplayBuffer=\"Sztartnij nagrowanie powtōrek\"\nBasic.Main.SaveReplay=\"Zapisz powtōrka\"\nBasic.Main.StartStreaming=\"Sztartnij stream\"\nBasic.Main.StartVirtualCam=\"Sztartnij Wirtualno Kamera\"\nBasic.Main.StopRecording=\"Zasztopuj nagrowanie\"\nBasic.Main.PauseRecording=\"Pauzuj nagrowanie\"\nBasic.Main.UnpauseRecording=\"Znōw nagrowanie\"\nBasic.Main.StoppingRecording=\"Sztopowanie nagrowanio...\"\nBasic.Main.StopReplayBuffer=\"Sztopuj nagrowanie powtōrek\"\nBasic.Main.StoppingReplayBuffer=\"Sztopuja nagrowanie powtōrek...\"\nBasic.Main.StopStreaming=\"Zakōńcz stream\"\nBasic.Main.StoppingStreaming=\"Kōńcza streamowanie...\"\nBasic.Main.ForceStopStreaming=\"Kōńcz stream (pociep ôpōźniynie)\"\nBasic.Main.Group=\"Skupina %1\"\nBasic.Main.Ungroup=\"Rozgrupuj\"\nBasic.Main.GridMode=\"Widok necu\"\nBasic.Main.ListMode=\"Widok wykozu\"\nBasic.MainMenu.File=\"Zbiōr (&F)\"\nBasic.MainMenu.File.Export=\"&Eksport\"\nBasic.MainMenu.File.Settings=\"&Ôpcyje\"\nBasic.MainMenu.File.Exit=\"Zakōńcz (&X)\"\nBasic.MainMenu.Edit=\"&Edycyjo\"\nBasic.MainMenu.Edit.Undo=\"&Cofej\"\nBasic.MainMenu.Edit.Redo=\"&Wrōć\"\nBasic.MainMenu.Edit.LockPreview=\"&Zaszperuj podglōnd\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Cwelich (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Wyjście (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"Przeksz&tałcanie ôbrazu\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"&Edycyjo przekształcyń...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Skopiyruj transformacyje\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Wkludź transformacyje\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"&Reset przekształcyń\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Ôbrōć ô 90 stopiyni zgodnie z ruchym skozōwek zygora\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Ôbrōć ô 90 stopiyni przeciwnie do ruchu skozōwek zygora\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Ôbrōć ô 180 stopiyni\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Przeciep w poziōmie (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Przeciep w piōnie (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Napasuj do ekranu (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Rozciōngnij do ekranu (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Wypostrzodkuj na ekranie (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Wypostrzodkuj w piōnie\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Wypostrzodkuj w poziōmie\"\nBasic.MainMenu.Edit.Order=\"&Porzōndek\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Przeniyś na &wiyrch\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Przeniyś na &spodek\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Przeniyś na &wiyrch\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Przeniyś na &spodek\"\nBasic.MainMenu.Edit.AdvAudio=\"&Zaawansowane ôpcyje klangu\"\nBasic.MainMenu.View=\"&Widok\"\nBasic.MainMenu.View.Toolbars=\"Paski noczyń (&T)\"\nBasic.MainMenu.View.ContextBar=\"Pasek noczyniowy zdrzōdła\"\nBasic.MainMenu.View.SourceIcons=\"&Ikōny zdrzōdłowe\"\nBasic.MainMenu.View.StatusBar=\"Pasek &statusu\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Połnoekranowy interfejs\"\nBasic.MainMenu.SceneCollection=\"Zbiōr &bin\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Importuj profil\"\nBasic.MainMenu.Profile.Export=\"Eksportuj profil\"\nBasic.MainMenu.SceneCollection.Import=\"Importuj kolekcyje biny\"\nBasic.MainMenu.SceneCollection.Export=\"Eksportuj kolekcyjo bin\"\nBasic.MainMenu.Profile.Exists=\"Profil już istniyje\"\nBasic.MainMenu.Tools=\"Noczynia (&T)\"\nBasic.MainMenu.Help=\"Pōmoc (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal pōmocy\"\nBasic.MainMenu.Help.Website=\"Ôbejzdrzij nasza zajta\"\nBasic.MainMenu.Help.Discord=\"Prziwstōń do serwera &Discord\"\nBasic.MainMenu.Help.Logs=\"Z&biory dziynnika\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Pokoż zbiory dziynnika (&s)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Wyślij teroźny zbiōr dziynnika (&C)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Podglōnd aktywnego zbioru dziynnika (&V)\"\nBasic.MainMenu.Help.CheckForUpdates=\"Zobocz czy sōm aktualizacyje\"\nBasic.MainMenu.Help.CrashLogs=\"&Reporty ô awaryjach\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Pokoż reporty ô awaryjach (&S)\"\nBasic.MainMenu.Help.About=\"&Info ô programie\"\nBasic.Settings.ProgramRestart=\"Coby te ustawiynia zaczły ôbowiōnzować, trza resztartnōńć program.\"\nBasic.Settings.ConfirmTitle=\"Potwiyrdź zmiany\"\nBasic.Settings.Confirm=\"Mosz niyzapisane zmiany. Chcesz jy zapisać?\"\nBasic.Settings.General=\"Bazowe\"\nBasic.Settings.General.Language=\"Godka\"\nBasic.Settings.General.EnableAutoUpdates=\"Autōmatycznie wybaduj dostympność aktualizacyji\"\nBasic.Settings.General.OpenStatsOnStartup=\"Ôtwōrz sztatystyki przi sztarcie aplikacyje\"\nBasic.Settings.General.Projectors=\"Projektory\"\nBasic.Settings.General.HideProjectorCursor=\"Skryj kursōr podglōndu na połnym ekranie\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Podglōnd na połnym ekranie dycki na wiyrchu\"\nBasic.Settings.General.SnapDistance=\"Czułość prziciōnganio\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Zachowej nagranie po zasztopowaniu streamu\"\nBasic.Settings.General.SysTray=\"Zasobnik systymowy\"\nBasic.Settings.General.SysTrayWhenStarted=\"Minimalizuj do zasobnika systymowego w czasie sztartowanio\"\nBasic.Settings.General.SaveProjectors=\"Zapisz projektory przi wyjściu\"\nBasic.Settings.General.Preview=\"Podglōnd\"\nBasic.Settings.General.OverflowHidden=\"Skryj przepełniynie\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Przepełniynie dycki widzialne\"\nBasic.Settings.General.Importers=\"Importowanie\"\nBasic.Settings.General.StudioPortraitLayout=\"Włōncz ukłod piōnowy\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Pokoż etykety podglōndu/programu\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Pokoż miana bin\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Rysowanie bezpiycznych przestrzyństw (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Ukłod Multiview\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Poziōmo, Wiyrch (8 bin)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Poziōmo, Spodek (8 bin)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Piōnowo, Lewo (8 bin)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Piōnowo, Prawo (8 bin)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Poziōmo, Wiyrch (18 bin)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Poziōmo, Wiyrch (24 biny)\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Użyj uwierzitelnianio\"\nBasic.Settings.Stream.Custom.Username=\"Miano używocza\"\nBasic.Settings.Stream.Custom.Password=\"Hasło\"\nBasic.Settings.Stream.BandwidthTestMode=\"Włōncz tryb testu przepustowości\"\nBasic.Settings.Stream.TTVAddon=\"Przidowki czata Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Brak\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV i FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Brak kōnfiguracyje streamu\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Ôtwōrz Ôpcyje\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Nadpisz rekōmandowane ustawiynia\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Maksymalny bitrate: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Maksymalno rozdzielczość: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Maksymalno wielość FPS: %1\"\nBasic.Settings.Output=\"Wyjście\"\nBasic.Settings.Output.Format=\"Format nagrowanio\"\nBasic.Settings.Output.Mode=\"Tryb wyjścio\"\nBasic.Settings.Output.Mode.Simple=\"Ajnfachowe\"\nBasic.Settings.Output.Mode.Adv=\"Zaawansowane\"\nBasic.Settings.Output.Mode.FFmpeg=\"Wyjście FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Włōncz nagrowanie powtōrek\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maksymalny czas nagranio\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Szacowane użycie spamiyńci: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Sufiks\"\nBasic.Settings.Output.Simple.SavePath=\"Ściyżka zbioru\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Jakość nagrowanio\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Idyntycznie jak stream\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Wysoko jakość a strzednio woga zbioru\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Jakość niy do ôdrōżniynio, srogo woga zbioru\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Ôstrzeżynie ô bezstratnyj jakości!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Programowy (x264)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Rozdzielczość: %1\"\nBasic.Settings.Output.VideoBitrate=\"Bitrate ôbrazu\"\nBasic.Settings.Output.AudioBitrate=\"Bitrate klangu\"\nBasic.Settings.Output.Reconnect=\"Pōnowne łōnczynie\"\nBasic.Settings.Output.RetryDelay=\"Prōbuj połōnczyć co\"\nBasic.Settings.Output.MaxRetries=\"Maksymalno wielość pōnownych prōb\"\nBasic.Settings.Output.EncoderPreset=\"Ôpcyje enkodera\"\nBasic.Settings.Output.CustomEncoderSettings=\"Niysztandardowe ôpcyje enkodera\"\nBasic.Settings.Output.CustomMuxerSettings=\"Włosne ôpcyje muxera\"\nBasic.Settings.Output.NoSpaceFileName=\"Gyneruj miano zbioru beze spacyje\"\nBasic.Settings.Output.Adv.Rescale=\"Przeskaluj wychodowy ôbroz\"\nBasic.Settings.Output.Adv.AudioTrack=\"Ściyżki klangowe\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Ściyżka 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Ściyżka 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Ściyżka 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Ściyżka 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Ściyżka 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Ściyżka 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Ściyżka Twitch VOD\"\nBasic.Settings.Output.Adv.Recording=\"Nagrowanie\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Zorta nagranio\"\nBasic.Settings.Output.Adv.Recording.Type=\"Zorta\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Sztandardowe\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Niysztandardowe ustawiynia (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Użyj enkodera streamu)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Format miana zbioru\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Nadpisz istniyjōncy zbiōr\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Tryb wyjścio FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Wyjście do adresy URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Wyjście do zbioru\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Popularne formaty zbiorōw nagrań\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Wszyjske Zbiory\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Ściyżka abo adresa URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format zbioru kōntynyra\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Klang\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Ôbroz\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Format sztandardowy\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Ôpis formatu zbioru kōntynyra\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Kodeki klangu i ôbrazu ôdczytane ze zbioru abo adresy URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Sztandardowy enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Niyaktywny enkoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Enkoder ôbrazu\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Ôpcyje enkodera wideo (jeźli sōm)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Enkoder klangu\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Ôpcyje enkodera audio (jeźli sōm)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Ôpcyje muxera (jeźli sōm)\"\nScreenshot=\"Ściepniyńcie ekranu - ekran wychodowy\"\nScreenshot.SourceHotkey=\"Ściepniyńcie ekranu - ôbrane zdrzōdło\"\nScreenshot.StudioProgram=\"Ściepniyńcie ekranu (program)\"\nScreenshot.Preview=\"Ściepniyńcie ekranu (podglōnd)\"\nScreenshot.Scene=\"Ściepniyńcie ekranu (bina)\"\nScreenshot.Source=\"Ściepniyńcie ekranu (zdrzōdło)\"\nBasic.Settings.Video=\"Ôbroz\"\nBasic.Settings.Video.Adapter=\"Szkarta graficzno\"\nBasic.Settings.Video.BaseResolution=\"Bazowo rozdzielczość (ôbroz)\"\nBasic.Settings.Video.DownscaleFilter=\"Filtr skalujōncy\"\nBasic.Settings.Video.FPSCommon=\"Ajnfachowe wartości FPS\"\nBasic.Settings.Video.FPSInteger=\"Cołke wartości FPS\"\nBasic.Settings.Video.FPSFraction=\"Ułōmkowe wartości FPS\"\nBasic.Settings.Video.Numerator=\"Rachownik\"\nBasic.Settings.Video.Denominator=\"Mianownik\"\nBasic.Settings.Video.Renderer=\"Renderowanie\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Dwulinijowy (Nojgibszy, nale rozmyty jeźli skalowany)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (Ôstrzyjsze skalowanie, 16 prōbek)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Ôstrzyjsze skalowanie, 36 prōbki)\"\nBasic.Settings.Audio=\"Klang\"\nBasic.Settings.Audio.SampleRate=\"Frekwyncyjo prōbkowanio\"\nBasic.Settings.Audio.Channels=\"Kanały\"\nBasic.Settings.Audio.Meters=\"Rachowniki\"\nBasic.Settings.Audio.MeterDecayRate=\"Czas zanikanio\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Gibko\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Strzednio (PPM zorty I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Poleku (PPM zorty II)\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Poziōm szpicowy prōbkowany\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Prawo szpica (wiynksze użycie CPU)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"Pozōr: Przestrzynny klang je włōnczōny.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Włōnczyć przestrzynny klang?\"\nBasic.Settings.Audio.DesktopDevice=\"Maszina audio\"\nBasic.Settings.Audio.DesktopDevice2=\"Maszina audio 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofōn/ekstra maszina audio\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofōn/ekstra maszina audio 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofōn/ekstra maszina audio 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofōn/ekstra maszina audio 4\"\nBasic.Settings.Audio.PushToMuteDelay=\"Ôpōźniynie \\\"naciś-coby-wyciszyć\\\"\"\nBasic.Settings.Audio.EnablePushToTalk=\"Włōncz \\\"naciś-coby-godać\\\"\"\nBasic.Settings.Audio.PushToTalkDelay=\"Ôpōźniynie \\\"naciś-coby-godać\\\"\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Maszina niydostympno abo niypodłōnczōno]\"\nBasic.Settings.Audio.Disabled=\"Wyłōnczōne\"\nBasic.Settings.Advanced=\"Zaawansowane\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Priorytet procesu\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Wysoki\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Powyżyj normalnego\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Normalny\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Pōniżyj normalnego\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Bezczynny\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Format farby\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Przestrzyństwo farb\"\nBasic.Settings.Advanced.Video.ColorRange=\"Zakrys farb\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Połny\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Maszina do mōnitorowanio audio\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Sztandardowe\"\nBasic.Settings.Advanced.StreamDelay=\"Ôpōźniynie streamu\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Ôpōźniynie\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Szacowane użycie spamiyńci: %1 MB\"\nBasic.Settings.Advanced.Network=\"Nec\"\nBasic.Settings.Advanced.Network.BindToIP=\"Przipisane IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Włōncz necowe ôptymalizacyje\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Włōncz regulacyjo tympa TCP\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(nagrowej jako mkv)\"\nBasic.AdvAudio.ActiveOnly=\"Ino aktywne zdrzōdła\"\nBasic.AdvAudio.Name=\"Miano\"\nBasic.AdvAudio.Volume=\"Głośność\"\nBasic.AdvAudio.VolumeSource=\"Głośność dlo '%1'\"\nBasic.AdvAudio.MonoSource=\"Miks do mono dlo '%1'\"\nBasic.AdvAudio.Balance=\"Balans\"\nBasic.AdvAudio.BalanceSource=\"Balans dlo '%1'\"\nBasic.AdvAudio.SyncOffset=\"Cofniyńcie klangu\"\nBasic.AdvAudio.SyncOffsetSource=\"Cofniyńcie synchrōnizacyje dlo '%1'\"\nBasic.AdvAudio.Monitoring=\"Mōnitorowanie audio\"\nBasic.AdvAudio.Monitoring.None=\"Wyłōnczōne\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Ino mōnitorowanie (wyjście wyłōnczōne)\"\nBasic.AdvAudio.Monitoring.Both=\"Mōnitorowanie i przekazowanie na wyjście\"\nBasic.AdvAudio.MonitoringSource=\"Mōnitorowanie klangu dlo '%1'\"\nBasic.AdvAudio.AudioTracks=\"Ściyżki\"\nBasic.Settings.Hotkeys=\"Skrōty kneflowe\"\nBasic.Settings.Hotkeys.Pair=\"Kōmbinacyje knefli spōlne ze '%1' fungujōm za szaltry\"\nBasic.Settings.Hotkeys.Filter=\"Filtruj\"\nBasic.Hotkeys.SelectScene=\"Przeszaltruj na bina\"\nBasic.SystemTray.Show=\"Pokoż\"\nBasic.SystemTray.Hide=\"Skryj\"\nBasic.SystemTray.Message.Reconnecting=\"Rozłōnczōny. Łōncza zaś...\"\nHotkeys.Left=\"Fajlik w lewo\"\nHotkeys.Right=\"Fajlik w prawo\"\nHotkeys.Up=\"W gōra\"\nHotkeys.Down=\"W dōł\"\nHotkeys.Menu=\"Myni\"\nHotkeys.Space=\"Spacyjo\"\nHotkeys.NumpadMultiply=\"* (Numpad)\"\nHotkeys.NumpadDivide=\"/ (Numpad)\"\nHotkeys.NumpadAdd=\"+ (Numpad)\"\nHotkeys.NumpadSubtract=\"- (Numpad)\"\nHotkeys.NumpadDecimal=\". (Numpad)\"\nHotkeys.MouseButton=\"Mysza %1\"\nMute=\"Wyciszynie\"\nUnmute=\"Wyłōncz wyciszynie\"\nPush-to-mute=\"Naciś-coby-wyciszyć\"\nPush-to-talk=\"Naciś-coby-godać\"\nSceneItemShow=\"Pokoż '%1'\"\nSceneItemHide=\"Skryj '%1'\"\nOutputWarnings.NoTracksSelected=\"Musisz ôbrać chocioż jedna ściyżka\"\nFinalScene.Title=\"Wyciep bina\"\nNoSources.Title=\"Brak zdrzōdeł\"\nChangeBG=\"Ustow farba\"\nCustomColor=\"Niysztandardowo farba\"\nAbout=\"Info ô programie\"\nAbout.Info=\"OBS Studio to je darmowy program ze ôtwartym zdrzōdłym do nagrowanio i streamowania wideo.\"\nAbout.Donate=\"Dōnacyjo\"\nAbout.GetInvolved=\"Spōmōż nos\"\nAbout.Authors=\"Autory\"\nAbout.License=\"Licyncyjo\"\nAbout.Contribute=\"Spōmōż projekt OBS\"\nAddUrl.Title=\"Przidej zdrzōdło z adresy URL\"\nPreviewTransition=\"Podglōnd przejścio\"\nImporter.Collection=\"Zbiōr bin\"\nImporter.Path=\"Ściyżka kolekcyje\"\nImporter.Program=\"Wykryto aplikacyjo\"\nRestart=\"Resztart\"\nNeedsRestart=\"Trza zaś sztartnōńć OBS Studio. Chcesz teroz resztartować?\"\nContextBar.NoSelectedSource=\"Brak zdrzōdła\"\nContextBar.MediaControls.PlayMedia=\"Ôdtworzej\"\nContextBar.MediaControls.PauseMedia=\"Pauza\"\nContextBar.MediaControls.StopMedia=\"Zasztopej\"\nContextBar.MediaControls.RestartMedia=\"Resztart\"\nContextBar.MediaControls.PlaylistNext=\"Niyskorniyjszy na playliście\"\nContextBar.MediaControls.PlaylistPrevious=\"Piyrwyjszy na playliście\"\nContextBar.MediaControls.BlindSeek=\"Pasek postympu ôdtwŏrzanio\"\nYouTube.Auth.WaitingAuth.Title=\"Autoryzacyjo używocza YouTube\"\nYouTube.Actions.Title=\"Tytuł*\"\nYouTube.Actions.MyBroadcast=\"Moja transmisyjo\"\nYouTube.Actions.Description=\"Ôpis\"\nYouTube.Actions.Privacy=\"Widzialność*\"\nYouTube.Actions.Privacy.Private=\"Prywatny\"\nYouTube.Actions.Privacy.Public=\"Publiczny\"\nYouTube.Actions.Privacy.Unlisted=\"Niypubliczny\"\nYouTube.Actions.Category=\"Kategoryjo\"\nYouTube.Actions.MadeForKids=\"Je film przeznaczōny dlo bajtli?*\"\nYouTube.Actions.MadeForKids.Yes=\"Ja, ôn je przeznaczōny dlo bajtli\"\nYouTube.Actions.MadeForKids.No=\"Niy, ôn niy je przeznaczōny dlo bajtli\"\nYouTube.Actions.Latency=\"Ôpōźniynie\"\nYouTube.Actions.Latency.Normal=\"Normalne\"\nYouTube.Actions.Latency.Low=\"Niske\"\nYouTube.Actions.Latency.UltraLow=\"Ultra niske\"\nYouTube.Actions.EnableAutoStart=\"Włōncz auto-sztart\"\nYouTube.Actions.EnableAutoStop=\"Włōncz auto-sztop\"\nYouTube.Actions.EnableDVR=\"Włōncz DVR\"\nYouTube.Actions.360Video=\"Wideo 360 stopiyni\"\nYouTube.Actions.ScheduleForLater=\"Zaplanuj na niyskorzij\"\nYouTube.Actions.EventCreated.Title=\"Trefiynie utworzōne\"\nYouTube.Actions.EventCreated.Text=\"Trefiynie utworzōne\"\nYouTube.Actions.AutoStopStreamingWarning=\"Niy bydziesz mōg sie zaś połōnczyć.<br>Twōj stream sie zasztopuje i niy bydziesz nadować.\"\n"
  },
  {
    "path": "frontend/data/locale/ta-IN.ini",
    "content": "Language=\"தமிழ்\"\nOK=\"சரி\"\nApply=\"அமல்படுத்து\"\nCancel=\"ரத்துசெய்\"\nClose=\"மூடு\"\nSave=\"சேமி\"\nDiscard=\"புறக்கணி\"\nDisable=\"செயல் நீக்கு\"\nYes=\"ஆம்\"\nNo=\"இல்லை\"\nAdd=\"சேர்க்க\"\nRemove=\"அகற்ற\"\nRename=\"மறுபெயரிடு\"\nInteract=\"ஊடாடு\"\nFilters=\"வடிகட்டிகள்\"\nProperties=\"பண்புகள்\"\nMoveUp=\"மேலே நகர்த்து\"\nMoveDown=\"கீழ் நகர்த்து\"\nSettings=\"அமைப்புகள்\"\nDisplay=\"காட்சித்திரை\"\nName=\"பெயர்\"\nExit=\"வெளியேறு\"\nMixer=\"ஒலி மிக்சர்\"\nBrowse=\"உலாவு\"\nMono=\"ஒற்றை ஒலி\"\nStereo=\"பல ஒலி\"\nDroppedFrames=\"கைவிடப்பட்ட பிரேம்கள் %1 (%2%)\"\nClear=\"நீக்கு\"\nRevert=\"மாற்றியமை\"\nShow=\"காட்டு\"\nHide=\"மறை\"\nUnhideAll=\"அனைத்தும் காண்பி\"\nUntitled=\"தலைப்பிடாதது\"\nNew=\"புதிய\"\nDuplicate=\"நகல்\"\nEnable=\"இயக்கு\"\nDisableOSXVSync=\"OSX V ஒத்திசைவை முடக்கு\"\nResetOSXVSyncOnExit=\"V-ஒத்திசைவு வெளியேறுகையில் macOS மீட்டமை\"\nHighResourceUsage=\"குறியீட்டு சுமை அதிகமாக உள்ளது! வீடியோ அமைப்புகளைத் திருத்துவது அல்லது ஒரு வேகமான குறியீட்டு முன்னுரிமையைப் பயன்படுத்துவதைக் கருத்தில் கொள்ளவும்.\"\nTransition=\"மாற்றம்\"\nQuickTransitions=\"விரைவான மாற்றங்கள்\"\nFadeToBlack=\"கருப்பு நிறத்திற்கு மறைதல்\"\nLeft=\"இடது\"\nRight=\"வலது\"\nTop=\"மேல்\"\nBottom=\"கீழ்\"\nReset=\"மீட்டமை\"\nHours=\"மணி நேரம்\"\nMinutes=\"நிமிடங்கள்\"\nSeconds=\"நொடிகள்\"\nDeprecated=\"அங்கீகரிக்கப்படாதது\"\nReplayBuffer=\"தாங்கல் மறுபிரதி\"\nImport=\"இறக்குமதி\"\nExport=\"ஏற்றுமதி\"\nCopy=\"நகலெடு\"\nPaste=\"ஒட்டு\"\nPasteReference=\"ஒட்டு (குறிப்பு)\"\nPasteDuplicate=\"ஒட்டு (நகல்)\"\nRemuxRecordings=\"பதிவுகள் மாற்றியமைத்தல்\"\nNext=\"அடுத்த\"\nBack=\"பின்\"\nDefaults=\"இயல்புநிலை\"\nRestoreDefaults=\"இயல்புநிலைகள்\"\nHideMixer=\"ஒலி கலவையில் மறைத்தல்\"\nTransitionOverride=\"மாற்றம் புறக்கணிப்பு\"\nShowTransition=\"காட்சி மாற்றம்\"\nHideTransition=\"மாற்றத்தை மறை\"\nNone=\"எதுவுமில்லை\"\nStudioMode.Preview=\"முன்னோட்டம்\"\nStudioMode.Program=\"நிகழ்ச்சி\"\nStudioMode.PreviewSceneName=\"முன்னோட்டம்:%1\"\nStudioMode.ProgramSceneName=\"நிரல்:%1\"\nShowInMultiview=\"பல்பார்வை காண்பி\"\nVerticalLayout=\"செங்குத்து வடிவமைப்பு\"\nGroup=\"குழு\"\nDoNotShowAgain=\"மீண்டும் காட்ட வேண்டாம்\"\nDefault=\"(இயல்புநிலை)\"\nCalculating=\"கணிக்கிறது...\"\nFullscreen=\"முழுத்திரை\"\nWindowed=\"சாளரபட்ட\"\nRefreshBrowser=\"புதுப்பி\"\nAspectRatio=\"விகிதம் <b>%1:%2</b>\"\nLockVolume=\"ஒலியளவை பூட்டு\"\nLogViewer=\"பதிவு பார்வையாளர்\"\nShowOnStartup=\"துவக்கத்தில் காட்டு\"\nOpenFile=\"கோப்பைத் திற\"\nAddSource=\"மூலத்தைச் சேர்க்கவும்\"\nRemoveScene=\"தேர்ந்தெடுக்கப்பட்ட காட்சியை அகற்று\"\nRemoveSource=\"தேர்ந்தெடுக்கப்பட்ட மூலத்தை அகற்று\"\nMoveSceneUp=\"காட்சியை மேலே நகர்த்தவும்\"\nMoveSceneDown=\"காட்சியை கீழே நகர்த்தவும்\"\nMoveSourceUp=\"மூலத்தை(களை) மேலே நகர்த்தவும்\"\nMoveSourceDown=\"மூலத்தை(களை) கீழே நகர்த்தவும்\"\nSourceProperties=\"திறந்த மூல பண்புகள்\"\nSourceFilters=\"திறந்த மூல வடிப்பான்கள்\"\nMixerToolbarMenu=\"ஆடியோ கலவை மெனு\"\nSceneFilters=\"காட்சி வடிப்பான்களைத் திறக்கவும்\"\nList=\"பட்டியல்\"\nGrid=\"கட்டம்\"\nComingSoon=\"விரைவில் வருகிறது\"\nPluginsFailedToLoad.Title=\"செருகுநிரல் ஏற்றுவதில் பிழை\"\nPluginsFailedToLoad.Text=\"பின்வரும் OBS செருகுநிரல்களை ஏற்ற முடியவில்லை:\\n\\n%1\\nஇந்த செருகுநிரல்களைப் புதுப்பிக்கவும் அல்லது அகற்றவும்.\"\nAlreadyRunning.Title=\"OBS ஏற்கனவே இயங்கிக்கொண்டிருக்கிறது\"\nAlreadyRunning.Text=\"OBS ஏற்கனவே இயங்குகிறது! இதை தொடர நீங்கள் விரும்பவில்லை எனில், புதிய நிகழ்வை இயக்க முயற்சிக்கும் முன் OBS இன் எந்தவொரு நிகழ்வுகளையும் மூடவும். நீங்கள் கணினி தட்டில் OBS ஐ குறைத்து அமைத்திருந்தால், அது இன்னமும் இயங்கிக்கொண்டிருக்கிறதா என்பதைப் பார்க்கவும்.\"\nAlreadyRunning.LaunchAnyway=\"எப்படியும் தொடங்கு\"\nChromeOS.Title=\"ஆதரவளிக்காத இயங்குத்தளம்\"\nWine.Title=\"Wine கண்டறியப்பட்டது\"\nDockCloseWarning.Title=\"நகரக்கூடிய சாளரத்தை மூடு\"\nExtraBrowsers.DockName=\"மிதவை பெயர்\"\nAuth.Authing.Title=\"அங்கீகரிக்கிறது...\"\nAuth.Authing.Text=\"%1, உடன் அங்கீகரிக்கிறது, தயவுசெய்து காத்திருக்கவும்...\"\nAuth.AuthFailure.Title=\"அங்கீகரிப்பு தோல்வி\"\nAuth.AuthFailure.Text=\"%1:\\n\\n%2: %3 உடன் அங்கீகரிக்க தோல்வி\"\nAuth.InvalidScope.Title=\"அங்கீகாரம் தேவை\"\nAuth.InvalidScope.Text=\"%1 அங்கீகார தேவைகள் மாறிவிட்டன. சில அம்சங்கள் கிடைக்காது.\"\nAuth.LoadingChannel.Title=\"சேனல் தகவல் ஏற்றுகிறது...\"\nAuth.LoadingChannel.Text=\"சேனல் தகவல் %1ஏற்றுகிறது, தயவுசெய்து காத்திருக்கவும்...\"\nAuth.ChannelFailure.Title=\"சேனல் நினைவேற்றுவது தோல்வியுற்றது\"\nAuth.ChannelFailure.Text=\"%1\\n\\n%2சேனல் தகவல் ஏற்ற முடியவில்லை: %3\"\nAuth.Chat=\"அரட்டை\"\nAuth.StreamInfo=\"அலை தகவல்\"\nTwitchAuth.Stats=\"Twitch விபரம்\"\nTwitchAuth.Feed=\"Twitch செயல்பாடு ஊட்டம்\"\nCopy.Filters=\"வடிகட்டிகள் நகல்\"\nPaste.Filters=\"வடிகட்டிகள் ஒட்டு\"\nBrowserPanelInit.Title=\"உலாவியில் தொடங்குகிறது...\"\nBrowserPanelInit.Text=\"உலாவியில் தொடங்குகிறது, தயவுசெய்து காத்திருக்கவும்...\"\nBandwidthTest.Region=\"பிராந்தியம்\"\nBandwidthTest.Region.US=\"அமெரிக்கா\"\nBandwidthTest.Region.EU=\"ஐரோப்பா\"\nBandwidthTest.Region.Asia=\"ஆசியா\"\nBandwidthTest.Region.Other=\"மற்ற\"\nBasic.AutoConfig=\"தானியங்கு உள்ளமைவு வழிகாட்டி\"\nBasic.AutoConfig.ApplySettings=\"அமைப்புகளைப் பயன்படுத்து\"\nBasic.AutoConfig.StartPage=\"தகவல் பயன்பாடு\"\nBasic.AutoConfig.StartPage.SubTitle=\"நீங்கள் நிகழ்ச்சியை எதற்காக பயன்படுத்துகிறீர் என்பதை குறிப்பிடவும்\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"பாய்விற்கு உகந்ததாகக்கு, பதிவாக்கம் இரண்டாம் நிலையாக\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"பதிவாக்கத்திற்கு மட்டும் உகந்ததாக்கு, பாய்வுசெய்ய மாட்டேன்\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"மெய்நிகர் படக்கருவி மட்டுமே பயன்படுத்துவேன்\"\nBasic.AutoConfig.VideoPage=\"காணொளி அமைப்புகள்\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"தற்போதையதை பயன்படுத்தவும் (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"காட்சி முறை %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"தற்போதையதை பயன்படுத்தவும் (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"ஒன்று 60 அல்லது 30, ஆனால் முடிந்தால் 60 தெரிந்தெடுக்கவும்\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"ஒன்று 60 அல்லது 30, ஆனால் உயர் தெளிவுத்திறனை தெரிந்தெடுக்கவும்\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"குறிப்பு: சுற்றளவு (அடிப்படை) தெளிவுதிறனானது நேரலை அல்லது பதிவுக்கான தெளிவுதிறனுக்கு சமமாக இருக்கவேண்டிய அவசியம் இல்லை. உங்கள் உண்மையான நேரலை அல்லது பதிவுக்கான தெளிவுதிறன் சுற்றளவு தெளிவுதிறனிலிருந்து ஆதார பயன்பாடு அல்லது பிட்ரேட் தேவைகளுக்கு குறைக்கப்பட்டு இருக்கலாம்.\"\nBasic.AutoConfig.StreamPage=\"அலை தகவல்\"\nBasic.AutoConfig.StreamPage.SubTitle=\"உங்கள் அலை தகவலை உள்ளிடவும்\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"கணக்கை இணை (பரிந்துரைக்கப்பட்டது)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"கணக்கைத் துண்டிக்கவும்\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"கணக்கைத் துண்டிக்கவா?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"இந்த மாற்றம் உடனடியாக பொருந்தும். உங்கள் கணக்கு துண்டிக்க விரும்புகிறீர்களா?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"நேரலை குறியீட்டை பெறுக\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"கூடுதல் தகவல்\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"நேரலை குறியீட்டை பயன்படுத்தவும்\"\nBasic.AutoConfig.StreamPage.Service=\"சேவை\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"அனைத்தும் காண்பி...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"தனிபயன்...\"\nBasic.AutoConfig.StreamPage.Server=\"சேவையகம்\"\nBasic.AutoConfig.StreamPage.StreamKey=\"அலை குறியீடு\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"இணைந்த கணக்கு\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"அலைவரிசை சோதனை மூலம் bitrate மதிப்பீடு (சில நிமிடங்கள் ஆகலாம்)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"விரும்பிய வன்பொருள் குறியீடு\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"வன்பொருள் குறியீட்டு முறை அதிக CPU பயன்பாட்டை நீக்குகிறது, ஆனால் அதே அளவு தரத்தை பெற bitrate தேவைப்படலாம்.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"நேரலை எச்சரிக்கை\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"அலைவரிசை சோதனை உங்கள் சேனலுக்கு ஆடியோ இல்லாமல் சீரற்ற வீடியோ தரவை நேரலை செய்ய உள்ளது. நீங்கள் முடிந்தால், சோதனை வீடியோக்களைத் தற்காலிகமாக நிறுத்துமாறு பரிந்துரைக்கிறோம், சோதனையை முடிந்தவரை, தனிப்பட்ட நேரலையாக அமைக்கலாம். தொடர்ந்து?\"\nBasic.AutoConfig.TestPage=\"இறுதி முடிவுகள்\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"இந்த நிகழ்ச்சி மிகவும் சிறந்த அமைப்புகளை மதிப்பிடுவதற்கான சோதனைகளின் தொகுப்பை இப்போது செயல்படுத்துகிறது\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"சோதனை முடிந்தது\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"அலைவரிசை சோதனை செய்கிறது, இதற்கு சில நிமிடங்கள் ஆகலாம்...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"இணைப்பதற்கு: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"எந்த சேவையகங்களுடன் இணைப்பதில் தோல்வி, உங்கள் இணைய இணைப்பைச் சரிபார்த்து, மீண்டும் முயற்சிக்கவும்.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"அலைவரிசை பரிசோதனை: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"அலை குறியாக்கி பரிசோதனை, இது ஒரு நிமிடம் ஆகலாம்...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"பதிவு குறியாக்கி பரிசோதனை, இது ஒரு நிமிடம் ஆகலாம்...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"குறியாக்கி துவக்க இயலவில்லை\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"பரிசோதனை %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"அலை குறியாக்கி\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"பதிவு குறியாக்கி\"\nBasic.AutoConfig.TestPage.Result.Header=\"இந்த நிகழ்ச்சி மதிப்பிடப்பட்ட அமைப்புகள் உங்களுக்கு மிகவும் சிறந்தது என்று தீர்மானித்துள்ளது:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"இந்த அமைப்புகளைப் பயன்படுத்த, அமைப்புகள் பயன்படுத்து என்பதை சொடுக்கிடுக. வழிகாட்டி சீரமைக்க மற்றும் மீண்டும் முயற்சிக்க, பின் என்பதை சொடுக்கிடுக. கைமுறையாக அமைப்புகளை நீங்களாக கட்டமைக்க, ரத்துசெய் மற்றும் திறந்த அமைப்புகள் என்பதை சொடுக்கிடுக.\"\nBasic.Stats=\"புள்ளிவிவரங்கள்\"\nBasic.Stats.CPUUsage=\"CPU பயன்பாடு\"\nBasic.Stats.HDDSpaceAvailable=\"Hdd வட்டு இடம் கிடைக்கிறது\"\nBasic.Stats.MemoryUsage=\"நினைவக உபயோகம்\"\nBasic.Stats.AverageTimeToRender=\"சட்டம் வழங்குவதற்கான சராசரி நேரம்\"\nBasic.Stats.SkippedFrames=\"குறியீட்டு பின்னடைவு காரணமாக தவிர்க்கப்பட்ட சட்டங்கள்\"\nBasic.Stats.MissedFrames=\"வழங்குவதில் பின்னடைவு காரணமாக சட்டங்கள் தவறியது\"\nBasic.Stats.Output.Stream=\"அலை\"\nBasic.Stats.Output.Recording=\"பதிவு\"\nBasic.Stats.Status=\"நிலை\"\nBasic.Stats.Status.Recording=\"பதிவுசெய்கிறது\"\nBasic.Stats.Status.Live=\"நேரலை\"\nBasic.Stats.Status.Reconnecting=\"மறுஇணைப்பு செய்கிறது\"\nBasic.Stats.Status.Inactive=\"செயலற்ற\"\nBasic.Stats.Status.Active=\"செயலில் உள்ளது\"\nBasic.Stats.DroppedFrames=\"கைவிடப்பட்ட சட்டங்கள் (இணையம்)\"\nBasic.Stats.MegabytesSent=\"மொத்த தகவல் வெளியீடு\"\nBasic.Stats.Bitrate=\"Bit விகிதம்\"\nBasic.Stats.ResetStats=\"புள்ளிவிவரங்களை மீட்டமை\"\nResetUIWarning.Title=\"UI மீட்டமைக்க விரும்புகிறீர்களா?\"\nUpdater.Title=\"புதிய மேம்படுத்தல்கள் கிடைக்கிறது\"\nUpdater.Text=\"புதிய மேம்படுத்தல்கள் உள்ளது:\"\nUpdater.UpdateNow=\"இப்போதே மேம்படுத்தவும்\"\nUpdater.RemindMeLater=\"பிறகு நினைவூட்டு\"\nUpdater.Skip=\"பதிப்பு தவிர்க்க\"\nUpdater.NoUpdatesAvailable.Title=\"மேம்படுத்தல்கள் கிடைக்கவில்லை\"\nUpdater.NoUpdatesAvailable.Text=\"மேம்படுத்தல்கள் ஏதும் நடப்பில் இல்லை\"\nUpdater.RepairButUpdatesAvailable.Title=\"மெய்மைத்தன்மை சரிபார்ப்பு கிடைக்கவில்லை\"\nUpdater.FailedToLaunch=\"மேம்படுத்தல் தோல்வியுற்றது\"\nQuickTransitions.DuplicateScene=\"நகல் காட்சி\"\nQuickTransitions.EditProperties=\"நகல் மூலங்கள்\"\nQuickTransitions.HotkeyName=\"விரைவான மாற்றம்: %1\"\nBasic.AddTransition=\"கட்டமைக்கக்கூடிய மாற்றத்தைச் சேர்\"\nBasic.RemoveTransition=\"கட்டமைக்கக்கூடிய மாற்றத்தை நீக்கு\"\nBasic.TransitionProperties=\"மாற்றம் குணங்கள்\"\nBasic.SceneTransitions=\"காட்சி மாற்றங்கள்\"\nBasic.TransitionDuration=\"நேர அளவு\"\nBasic.TogglePreviewProgramMode=\"ஸ்டூடியோ பயன்முறை\"\nUndo.Undo=\"செயல்நீக்கு\"\nUndo.Redo=\"திரும்பச்செய்\"\nUndo.Add=\"'%1'ஐச் சேர்\"\nUndo.Delete=\"'%1'ஐ அழி\"\nUndo.Rename=\"'%1'ஐ மறுபெயரிடு\"\nUndo.SceneCollection.Switch=\"'%1'க்கு மாறு\"\nUndo.Item.Undo=\"%1 செயல்தவிர்\"\nUndo.Item.Redo=\"%1ஐ திரும்பச்செய்\"\nUndo.Sources.Multi=\"%1 ஆதாரங்களை நீக்கவும்\"\nUndo.Filters=\"'%1'மீதான மாற்றங்களை வடிகட்டு\"\nUndo.Transform.Rotate=\"'%1''இன் சுழற்சி\"\nUndo.Transform.Center=\"'%1' திரைக்கு நடுவில்\"\nUndo.Volume.Change=\"'%1' 'வின் ஒலியளவை மாற்றுக\"\nUndo.Volume.Mute=\"'%1' ஒலிதடு\"\nUndo.Volume.Unmute=\"'%1' ஒலியடக்கு\"\nUndo.Balance.Change=\"'%1' ஒலி சமநிலை மாற்றம்\"\nTransitionNameDlg.Text=\"மாற்றத்தின் பெயரை உள்ளிடவும்\"\nTransitionNameDlg.Title=\"மாற்றத்தின் பெயர்\"\nTitleBar.Profile=\"விவரம்\"\nTitleBar.Scenes=\"காட்சிகள்\"\nNameExists.Title=\"பெயர் ஏற்கனவே உள்ளது\"\nNameExists.Text=\"இந்த பெயர் ஏற்கனவே உபயோகத்தில் உள்ளது.\"\nNoNameEntered.Title=\"சரியான பெயரை உள்ளிடுக\"\nNoNameEntered.Text=\"வெற்று பெயர்களை நீங்கள் பயன்படுத்த முடியாது.\"\nConfirmStart.Title=\"அலை தொடக்கம்?\"\nConfirmStart.Text=\"அலை தொடங்க விரும்புகிறீர்களா?\"\nConfirmStop.Title=\"அலை நிறுத்த?\"\nConfirmStop.Text=\"அலை நிறுத்த நீங்கள் உறுதியாக இருக்கிறீர்களா?\"\nConfirmStopRecord.Title=\"பதிவெடுப்பதை நிறுத்து?\"\nConfirmStopRecord.Text=\"பதிவு செய்வதை நிறுத்த விரும்புகிறீர்களா?\"\nConfirmBWTest.Title=\"அலைவரிசை சோதனையைத் தொடங்கவா?\"\nConfirmRemove.Title=\"அகற்ற உறுதி செய்\"\nConfirmRemove.TextMultiple=\"'%1' ஐ அகற்ற விரும்புகிறீர்களா?\"\nOutput.StartStreamFailed=\"பாய்வு துவக்கம் தோல்வி\"\nOutput.StartRecordingFailed=\"பதிவாக்கத் துவக்கம் தோல்வி\"\nOutput.StartReplayFailed=\"மறுபகிர்வு தாங்கலை தொடங்க முடியவில்லை\"\nOutput.ReplayBuffer.PauseWarning.Title=\"நிறுத்தப்பட்ட நிலையில் மறுபார்வைகளை சேமிக்க முடியாது\"\nOutput.ConnectFail.Title=\"இணைக்க இயலவில்லை\"\nOutput.ConnectFail.BadPath=\"தவறான URL பாதை அல்லது இணைப்பு. அவை செல்லுபடியாகிறதா என்று உறுதிப்படுத்த உங்கள் அமைப்புகளை சரிபார்க்கவும்.\"\nOutput.ConnectFail.ConnectFailed=\"சேவகையத்திடம் தொடர்பு கொள்ள முடியவில்லை\"\nOutput.ConnectFail.Error=\"சேவையகத்துடன் இணைக்க முயற்சிக்கும் போது எதிர்பாராத பிழை ஏற்பட்டது. மேலும் தகவல்கள் பதிவு கோப்பில் உள்ளன.\"\nOutput.ConnectFail.Disconnected=\"சேவையகத்திலிருந்து துண்டிக்கப்பட்டது.\"\nOutput.StreamEncodeError.Title=\"குறியாக்கப் பிழை\"\nOutput.RecordFail.Title=\"பதிவாக்கத் துவக்கம் தோல்வி\"\nOutput.RecordFail.Unsupported=\"வெளியீட்டு வடிவமைப்பு ஒன்று ஆதரிக்கப்படவில்லை அல்லது ஒன்றுக்கு மேற்பட்ட ஒலி தடங்கள் ஆதரிக்கவில்லை. தயவுசெய்து உங்கள் அமைப்புகளை சரிபார்த்து மீண்டும் முயற்சிக்கவும்.\"\nOutput.RecordNoSpace.Title=\"போதுமான வட்டு இடம் இல்லை\"\nOutput.RecordNoSpace.Msg=\"பதிவைத் தொடர போதுமான வட்டு இடம் இல்லை.\"\nOutput.RecordError.Title=\"பதிவு பிழை\"\nOutput.RecordError.Msg=\"பதிவு செய்யும் போது ஒரு குறிப்பிடப்படாத பிழை ஏற்பட்டது.\"\nOutput.BadPath.Title=\"தவறான கோப்பு பாதை\"\nOutput.BroadcastStartFailed=\"ஒளிபரப்பு துவக்கம் தோல்வி\"\nRemux.SourceFile=\"OBS பதிவு\"\nRemux.TargetFile=\"இலக்கு கோப்பு\"\nRemux.Remux=\"வடிவமாற்றம்\"\nRemux.ClearFinished=\"முடிக்கப்பட்ட உருப்படிகளை நீக்கு\"\nRemux.ClearAll=\"அனைத்து உருப்படிகளையும் நீக்க\"\nRemux.OBSRecording=\"OBS பதிவு\"\nRemux.FinishedTitle=\"வடிவமாற்றம் முடித்து\"\nRemux.Finished=\"பதிவு வடிவமாற்றம் செய்யப்பட்டது\"\nRemux.FinishedError=\"பதிவு வடிவமாற்றம் செய்யப்பட்டது, ஆனால் கோப்பு முழுமையற்றதாக இருக்கலாம்\"\nRemux.SelectRecording=\"OBS பதிவு தேர்ந்தெடு...\"\nRemux.SelectTarget=\"இலக்குக்கான கோப்புறையை தேர்ந்தெடு...\"\nRemux.FileExistsTitle=\"இலக்கு கோப்புகளை இல்லை\"\nRemux.FileExists=\"பின்வரும் இலக்கு கோப்புகள் ஏற்கனவே உள்ளன. அவற்றை மாற்ற விரும்புகிறீர்களா?\"\nRemux.ExitUnfinishedTitle=\"வடிவமாற்றம் நடைபெற்று வருகிறது\"\nRemux.ExitUnfinished=\"வடிவமாற்றம் முடிக்கப்படவில்லை, இப்போது நிறுத்த இலக்கு கோப்பை பயன்படுத்த முடியாது ஆகலாம். \\n நீங்கள் மறுதொடக்கம் நிறுத்த விரும்புகிறீர்களா?\"\nRemux.HelpText=\"கோப்புகளை வடிவமாற்றம் செய்ய இந்த சாளரத்தில் விடவும், அல்லது கோப்பை உலாவ ஒரு வெற்று \\\"OBS பதிவு\\\" செல்லை தேர்ந்தெடுக்கவும்.\"\nMissingFiles=\"காணாதக் கோப்புகள்\"\nMissingFiles.MissingFile=\"காணாதக் கோப்பு\"\nMissingFiles.NewFile=\"புதியக் கோப்பு\"\nMissingFiles.Search=\"கோப்புறையைத் தேடு...\"\nMissingFiles.SelectFile=\"கோப்பைத் தேர்ந்தெடு...\"\nMissingFiles.SelectDir=\"தேட ஒரு கோப்புறையைத் தேர்வுசெய்க\"\nMissingFiles.State=\"நிலை\"\nMissingFiles.Replaced=\"மாற்றப்பட்டது\"\nMissingFiles.Cleared=\"சுத்தம் செய்யப்பட்டது\"\nBasic.DesktopDevice1=\"திரைப்பலக ஒலி\"\nBasic.DesktopDevice2=\"திரைப்பலக ஒலி 2\"\nBasic.AuxDevice1=\"ஒலிவாங்கி/ஒலி முனையம்\"\nBasic.AuxDevice2=\"ஒலிவாங்கி/ஒலி முனையம் 2\"\nBasic.AuxDevice3=\"ஒலிவாங்கி/ஒலி முனையம் 3\"\nBasic.AuxDevice4=\"ஒலிவாங்கி/ஒலி முனையம் 4\"\nBasic.Scene=\"காட்சி\"\nBasic.DisplayCapture=\"திரை பிடிப்பு\"\nBasic.Main.PreviewConextMenu.Enable=\"முன்னோட்டம் இயக்கு\"\nBasic.Main.Preview.Disable=\"முன்னோட்டத்தை முடக்கு\"\nScaleFiltering=\"அளவு வடிகட்டுதல்\"\nScaleFiltering.Point=\"புள்ளி\"\nScaleFiltering.Bilinear=\"இருநேர்கோடு\"\nScaleFiltering.Bicubic=\"இருபட்டக\"\nScaleFiltering.Area=\"பகுதி\"\nBlendingMode.Normal=\"இயல்பு\"\nBlendingMode.Subtract=\"கழித்தல்\"\nBlendingMode.Multiply=\"பெருக்கல்\"\nBlendingMode.Lighten=\"வெளிரவை\"\nBlendingMode.Darken=\"இருளவை\"\nDeinterlacing=\"இடைப்பின்னலகற்றம்\"\nDeinterlacing.Discard=\"புறக்கணி\"\nDeinterlacing.TopFieldFirst=\"முதலில் மேல் புலம்\"\nDeinterlacing.BottomFieldFirst=\"முதலில் கீழ் புலம்\"\nVolControl.Mute=\"'%1' ஒலிதடு\"\nVolControl.Properties=\"'%1' பண்புகள்\"\nBasic.Main.AddSceneDlg.Title=\"காட்சி சேர்\"\nBasic.Main.AddSceneDlg.Text=\"காட்சி பெயர் உள்ளிடவும்\"\nBasic.Main.DefaultSceneName.Text=\"காட்சி %1\"\nBasic.Main.AddSceneCollection.Title=\"காட்சி தொகுப்பில் சேர்க்க\"\nBasic.Main.AddSceneCollection.Text=\"காட்சி தொகுப்பின் பெயரை உள்ளிடுக\"\nBasic.Main.RenameSceneCollection.Title=\"காட்சி தொகுப்புக்கு மறுபெயரிடு\"\nAddProfile.Title=\"விவரக்குறிப்பை மேலேற்று\"\nAddProfile.Text=\"சுயவிவரத்தில் பெயரை உள்ளிடவும்\"\nAddProfile.WizardCheckbox=\"தானாக உள்ளமைக்கும் வழிகாட்டி காட்டு\"\nRenameProfile.Title=\"சுயவிவரத்தை மறுபெயரிடு\"\nBasic.Main.MixerRename.Title=\"ஒலி மூலம் மறுபெயரிடு\"\nBasic.Main.MixerRename.Text=\"ஒலி மூலம் பெயரை உள்ளிடவும்\"\nBasic.Main.PreviewDisabled=\"முன்னோட்டம் தற்போது முடக்கப்பட்டுள்ளது\"\nBasic.SourceSelect=\"மூலத்தை உருவாக்கு/தேர்ந்தெடு\"\nBasic.SourceSelect.CreateNew=\"புதிதாக உருவாக்கு\"\nBasic.SourceSelect.AddExisting=\"தற்போதுள்ள சேர்\"\nBasic.SourceSelect.AddVisible=\"மூலத்தை காண்பி\"\nBasic.Main.Sources.Lock=\"பூட்டு\"\nBasic.PropertiesWindow=\"'%1' பண்புகள்\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (தானியங்குதேர்வு: %2)\"\nBasic.PropertiesWindow.SelectColor=\"வண்ணம் தேர்ந்தெடு\"\nBasic.PropertiesWindow.SelectFont=\"எழுத்துரு தேர்ந்தெடு\"\nBasic.PropertiesWindow.ConfirmTitle=\"அமைப்புகள் மாற்றப்பட்டன\"\nBasic.PropertiesWindow.Confirm=\"சேமிக்கப்படாத மாற்றங்கள் உள்ளன. அவற்றை வைத்திருக்க விரும்புகிறீர்களா?\"\nBasic.PropertiesWindow.NoProperties=\"பண்புகள் இல்லை\"\nBasic.PropertiesWindow.AddFiles=\"கோப்புகளை இணைக்க\"\nBasic.PropertiesWindow.AddDir=\"அடைவை சேர்\"\nBasic.PropertiesWindow.AddURL=\"URL/பாதை சேர்\"\nBasic.PropertiesWindow.AddEditableListDir=\"'%1 ' அடைவை சேர்\"\nBasic.PropertiesWindow.AddEditableListFiles=\"'%1 ' கோப்புகளை சேர்\"\nBasic.PropertiesWindow.AddEditableListEntry=\"'%1 ' உள்ளீ ̈ட்டைச் சேர்\"\nBasic.PropertiesWindow.EditEditableListEntry=\"'%1' -லிருந்து உள்ளீடுகளைத் திருத்து\"\nBasic.PropertiesView.FPS.Simple=\"எளிய FPS மதிப்புகள்\"\nBasic.PropertiesView.FPS.Rational=\"பகுத்தறிவு FPS மதிப்புகள்\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"செல்லத்தக்க FPS வரம்புகள்:\"\nBasic.PropertiesView.UrlButton.Text.Url=\"உரலி: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"உரலியைத் திற\"\nBasic.InteractionWindow=\"'%1' உடன் தொடர்புகொள்\"\nBasic.StatusBar.Reconnecting=\"துண்டிக்கப்பட்டது, %2 விநாடி(கள்) மீண்டும் இணைக்கிறது ( %1 முயற்சி)\"\nBasic.StatusBar.AttemptingReconnect=\"மீண்டும் இணைக்க முயற்சிக்கிறது... ( %1 முயற்சி)\"\nBasic.StatusBar.ReconnectSuccessful=\"மறு இணைப்பு வெற்றி\"\nBasic.StatusBar.Delay=\"(%1 நொடி) தாமதம்\"\nBasic.StatusBar.DelayStartingIn=\"தாமதம் (%1 நொடியில் தொடங்கி)\"\nBasic.StatusBar.DelayStoppingIn=\"தாமதம் (%1 நொடியில் நிறுத்துகிறது)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"தாமதம் (%1 வினாடிகளில் நிறுத்துகிறது, %2 வினாடிகளில் தொடங்குகிறது)\"\nBasic.StatusBar.RecordingSavedTo=\"பதிவாக்கம் '%1'க்கு சேமிக்கப்பட்டது\"\nBasic.StatusBar.ScreenshotSavedTo=\"திரைப்பிடிப்பு '%1'க்கு சேமிக்கப்பட்டது\"\nBasic.Filters=\"வடிகட்டிகள்\"\nBasic.Filters.AsyncFilters=\"ஒலி/ஒளி வடிகட்டிகள்\"\nBasic.Filters.AudioFilters=\"ஒலி வடிகட்டிகள்\"\nBasic.Filters.EffectFilters=\"விளைவு வடிகட்டிகள்\"\nBasic.Filters.Title=\"'%1' வடிகட்டிகள்\"\nBasic.Filters.AddFilter.Title=\"வடிகட்டி பெயர்\"\nBasic.Filters.AddFilter.Text=\"வடிகட்டியின் பெயர் குறிப்பிடவும்\"\nBasic.TransformWindow=\"காட்சி உருப்படியை மாற்றம்\"\nBasic.TransformWindow.Position=\"நிலை\"\nBasic.TransformWindow.Rotation=\"சுழற்சி\"\nBasic.TransformWindow.Size=\"அளவு\"\nBasic.TransformWindow.Width=\"அகலம்\"\nBasic.TransformWindow.Height=\"உயரம்\"\nBasic.TransformWindow.Alignment=\"நிலை ஒழுங்கமைத்தல்\"\nBasic.TransformWindow.BoundsType=\"எல்லைக்குட்பட்ட பெட்டி வகை\"\nBasic.TransformWindow.BoundsAlignment=\"எல்லைக்குட்பட்ட பெட்டி ஒழுங்கமைத்தல்\"\nBasic.TransformWindow.Bounds=\"எல்லைக்குட்பட்ட பெட்டி அளவு\"\nBasic.TransformWindow.Crop=\"நுனிவெட்டு\"\nBasic.TransformWindow.Alignment.TopLeft=\"மேல் இடது\"\nBasic.TransformWindow.Alignment.TopCenter=\"மேல் மையம்\"\nBasic.TransformWindow.Alignment.TopRight=\"மேல் வலது\"\nBasic.TransformWindow.Alignment.CenterLeft=\"மையம் இடது\"\nBasic.TransformWindow.Alignment.Center=\"மையம்\"\nBasic.TransformWindow.Alignment.CenterRight=\"மையம் வலது\"\nBasic.TransformWindow.Alignment.BottomLeft=\"கீழ் இடது\"\nBasic.TransformWindow.Alignment.BottomCenter=\"கீழ் மையம்\"\nBasic.TransformWindow.Alignment.BottomRight=\"கீழ் வலது\"\nBasic.TransformWindow.BoundsType.None=\"எல்லையற்று\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"அதிகபட்ச அளவு மட்டும்\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"உள் எல்லைகளுக்கு அளவிட\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"வெளி எல்லைகளுக்கு அளவிடு\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"எல்லைகளின் அகலத்திற்கு அளவிடு\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"எல்லைகளின் உயரத்திற்கு அளவிடு\"\nBasic.TransformWindow.BoundsType.Stretch=\"வரம்புக்கு நீட்டு\"\nBasic.Main.AddSourceHelp.Title=\"மூலத்தில் சேர்க்க முடியாது\"\nBasic.Main.AddSourceHelp.Text=\"மூலத்தைச் சேர்க்க நீங்கள் குறைந்தபட்சம் 1 காட்சியைக் கொண்டிருக்க வேண்டும்.\"\nBasic.Main.Scenes=\"காட்சிகள்\"\nBasic.Main.Sources=\"மூலம்\"\nBasic.Main.Source=\"மூலம்\"\nBasic.Main.Controls=\"கட்டுப்பாடு\"\nBasic.Main.Connecting=\"இணைக்கிறது...\"\nBasic.Main.StartRecording=\"பதிவாக்கத் துவக்கு\"\nBasic.Main.StartReplayBuffer=\"மறுபிரதி தாங்கல் தொடங்கு\"\nBasic.Main.StartStreaming=\"பாய்வைத் துவக்கு\"\nBasic.Main.StartVirtualCam=\"மெய்நிகர் படக்கருவி துவக்கு\"\nBasic.Main.StopRecording=\"பதிவெடுப்பதை நிறுத்து\"\nBasic.Main.PauseRecording=\"பதிவை இடைநிறுத்து\"\nBasic.Main.StoppingRecording=\"பதிவு நிறுத்துகிறது...\"\nBasic.Main.StopReplayBuffer=\"மறுபிரதி தாங்கல் நிறுத்து\"\nBasic.Main.StoppingReplayBuffer=\"மறுபிரதி தாங்கல் நிறுத்துகிறது...\"\nBasic.Main.StopStreaming=\"அலை நிறுத்தம்\"\nBasic.Main.StoppingStreaming=\"அலை நிறுத்தம்...\"\nBasic.Main.ForceStopStreaming=\"நேரலை நிறுத்து (தாமதத்தை நிராகரி)\"\nBasic.Main.Group=\"குழு %1\"\nBasic.Main.GroupItems=\"தேர்ந்தெடுத்த உருப்படிகளை குழுப்படுத்த\"\nBasic.Main.Ungroup=\"குழு நீக்கம்\"\nBasic.Main.GridMode=\"கட்டப் பயன்முறை\"\nBasic.Main.ListMode=\"பட்டியல் பயன்முறை\"\nBasic.MainMenu.File=\"கோப்பு (&F)\"\nBasic.MainMenu.File.Export=\"ஏற்றுமதி (&E)\"\nBasic.MainMenu.File.Import=\"இறக்குமதி (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"பதிவுகளை காண்பி (&R)\"\nBasic.MainMenu.File.Remux=\"வடிவமாற்ற பதிவுகள் (&M)\"\nBasic.MainMenu.File.Settings=\"அமைப்புகள் (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"அமைப்புகள் கோப்புறைகளைக் காண்பி\"\nBasic.MainMenu.File.ShowProfileFolder=\"கோப்புறை சுயவிவரம் காண்பி\"\nBasic.MainMenu.File.Exit=\"வெளியேறு (&X)\"\nBasic.MainMenu.Edit=\"திருத்து (&E)\"\nBasic.MainMenu.Edit.Undo=\"செயல்தவிர் (&U)\"\nBasic.MainMenu.Edit.Redo=\"மீண்டும்செய் (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"முன்னோட்டம் பூட்டு (&L)\"\nBasic.MainMenu.Edit.Scale=\"முன்னோட்டம் அளவிடல் (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"சாளரத்தின் அளவிற்கு\"\nBasic.MainMenu.Edit.Scale.Canvas=\"சுற்றளவு (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"வெளியீடு (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"மாற்றும் (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"மாற்றும் திருத்து... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"மாற்றம் நகலெடு\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"மாற்றம் ஒட்டுக\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"மாற்றம் மீட்டமை (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"CW 90 டிகிரி சுழற்று\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"CCW 90 டிகிரி சுழற்று\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180 டிகிரி சுழற்று\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"கிடைமட்டமாக மடக்கு (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"செங்குத்தாக மடக்கு (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"திரைக்கு ஏற்றார் போல் பொருத்து (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"திரைக்கு முழுவதும் இழு (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"திரையின் மையத்திற்கு (&C)\"\nBasic.MainMenu.Edit.Order=\"ஒழுங்கு (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"மேலே நகர்த்து (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"கீழ் நகர்த்து (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"முதலாவதாக நகர்த்து (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"கடைசிக்கு நகர்த்து (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"மேம்பட்ட ஒலி குணங்கள் (&A)\"\nBasic.MainMenu.View=\"பார்வை (&V)\"\nBasic.MainMenu.View.Toolbars=\"கருவிப்பட்டிகள் (&T)\"\nBasic.MainMenu.View.StatusBar=\"நிலை பட்டை (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"முழுத்திரை இடைமுகம்\"\nBasic.MainMenu.SceneCollection=\"காட்சி தொகுப்பு (&S)\"\nBasic.MainMenu.Profile=\"சுயவிவரம் (&P)\"\nBasic.MainMenu.Profile.Import=\"சுயவிவரம் இறக்குமதி\"\nBasic.MainMenu.Profile.Export=\"சுயவிவரம் ஏற்றுமதி\"\nBasic.MainMenu.SceneCollection.Import=\"காட்சி தொகுப்பு இறக்குமதி\"\nBasic.MainMenu.SceneCollection.Export=\"காட்சி தொகுப்பு ஏற்றுமதி\"\nBasic.MainMenu.Profile.Exists=\"இந்த விவரம் ஏற்கனவே உள்ளது\"\nBasic.MainMenu.Tools=\"கருவிகள் (&T)\"\nBasic.MainMenu.Help=\"உதவி (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"உதவி இணையதளம் (&P)\"\nBasic.MainMenu.Help.Website=\"வலைத்தளம் செல்ல (&W)\"\nBasic.MainMenu.Help.Logs=\"பதிவுக் கோப்புகள் (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"பதிவுக் கோப்புகள் காண்பி (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"தற்போதைய பதிவு கோப்பை பதிவேற்றவும் (&C)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"தற்போதைய பதிவு கோப்பை காணவும் (&V)\"\nBasic.MainMenu.Help.CheckForUpdates=\"மேம்படுத்தல்களை சோதிக்க\"\nBasic.MainMenu.Help.CrashLogs=\"C&rash அறிக்கை\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Cra&sh அறிக்கை காட்டு\"\nBasic.MainMenu.Help.About=\"இதைப்பற்றி (&A)\"\nBasic.Settings.ProgramRestart=\"இந்த அமைப்புகள் நடைமுறைக்கு வர நிகழச்சி நிரலை மறுதொடக்கம் செய்ய வேண்டும்.\"\nBasic.Settings.ConfirmTitle=\"மாற்றம் உறுதி செய்தல்\"\nBasic.Settings.Confirm=\"சேமிக்கப்படாத மாற்றங்கள் உள்ளன. மாற்றங்களை சேமிக்கவா?\"\nBasic.Settings.General=\"பொதுவானவை\"\nBasic.Settings.General.Language=\"மொழி\"\nBasic.Settings.General.EnableAutoUpdates=\"தொடக்கத்தில் மேம்படுத்தல்களை தானியங்கு முறையில் சோதிக்க\"\nBasic.Settings.General.OpenStatsOnStartup=\"தொடக்கத்தில் விபரம் திறந்த உரையாடல்\"\nBasic.Settings.General.WarnBeforeStartingStream=\"நேரலை தொடங்கும் போது உறுதிப்படுத்தல் உரையாடலைக் காட்டு\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"நேரலை நிறுத்தும்போது உறுதிப்படுத்தல் உரையாடலை காட்டு\"\nBasic.Settings.General.Projectors=\"பிரதிபலிப்பான்\"\nBasic.Settings.General.HideProjectorCursor=\"பிரதிபலிப்பான் மீது காட்டியை மறை\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"பிரதிபலிப்பானை எப்போதும் மேலே வைக்கவும்\"\nBasic.Settings.General.SysTray=\"கணினி தட்டு\"\nBasic.Settings.General.SysTrayWhenStarted=\"தொடங்கிய போது கணினி தட்டில் சிறிதாக்கு\"\nBasic.Settings.General.Preview=\"முன்னோட்டம்\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"செங்குத்து, இடது (8 காட்சிகள்)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"செங்குத்து, வலது (8 காட்சிகள்)\"\nBasic.Settings.Stream=\"நேரலை\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"அங்கீகாரத்தைப் பயன்படுத்துக\"\nBasic.Settings.Stream.Custom.Username=\"பயனர் பெயர்\"\nBasic.Settings.Stream.Custom.Password=\"கடவுச்சொல்\"\nBasic.Settings.Stream.BandwidthTestMode=\"அலைவரிசை சோதனை பயன்முறையை இயக்கு\"\nBasic.Settings.Stream.TTVAddon.None=\"எதுவுமில்லை\"\nBasic.Settings.Stream.StreamSettingsWarning=\"அமைப்புகளைத் திற\"\nBasic.Settings.Output=\"வெளியீடு\"\nBasic.Settings.Output.Format=\"பதிவு வடிவமைப்பு\"\nBasic.Settings.Output.SelectDirectory=\"பதிவு கோப்பகத்தைத் தேர்ந்தெடுக்கவும்\"\nBasic.Settings.Output.Mode.Simple=\"எளிய\"\nBasic.Settings.Output.Mode.Adv=\"மேம்பட்டது\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg வெளியீட்டு வகை\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"பின்னொட்டு\"\nBasic.Settings.Output.Simple.SavePath=\"பதிவு பாதை\"\nBasic.Settings.Output.Simple.RecordingQuality=\"பதிவின் தரம்\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"உயர்தர, நடுத்தர கோப்பு அளவு\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"எந்தவிதமான வேறுபாட்டையும் காணமுடியாத தரம், பெரிய கோப்பு அளவு\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"இழப்பின்றி தரம், மிகவும் பெரிய கோப்பு அளவு\"\nBasic.Settings.Output.Simple.Encoder.Software=\"மென்பொருள் (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"வன்பொருள் (NVENC, HEVC)\"\nBasic.Settings.Output.VideoBitrate=\"காணொளி பிட்விகிதம்\"\nBasic.Settings.Output.AudioBitrate=\"ஒலி பிட்விகிதம்\"\nBasic.Settings.Output.MaxRetries=\"அதிகபட்ச முயற்சிகள்\"\nBasic.Settings.Output.CustomEncoderSettings=\"விருப்ப குறியாக்கி அமைப்புகள்\"\nBasic.Settings.Output.NoSpaceFileName=\"இடைவெளி இல்லாமல் கோப்பு பெயரினை உருவாக்கவும்\"\nBasic.Settings.Output.Adv.AudioTrack=\"ஒலி தடம்\"\nBasic.Settings.Output.Adv.Streaming=\"நேரலை\"\nBasic.Settings.Output.Adv.Audio.Track1=\"ஒலி தடம் 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"ஒலி தடம் 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"ஒலி தடம் 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"ஒலி தடம் 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"ஒலி தடம் 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"ஒலி தடம் 6\"\nBasic.Settings.Output.Adv.Recording=\"பதிவுசெய்கிறது\"\nBasic.Settings.Output.Adv.Recording.Type=\"வகை\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"தரநிலை\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"தனிப்பயன் விருப்ப வெளியீடு (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(நேரலை குறியாக்கி பயன்படுத்து)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"கோப்பின் பெயர் வடிவமைப்பு\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"கோப்பு இருந்தால் மேலெழுது\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg வெளியீட்டு வகை\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"URLக்கு வெளியிடுக\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"கோப்பிற்கு வெளியிடுக\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"பொதுவான பதிவு வடிவங்கள்\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"அனைத்து கோப்புகள்\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"கோப்பு பாதை அல்லது URL\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"ஒலி\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"காணொளி\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"இயல்புநிலை வடிவமைப்பு\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"இயல்புநிலை குறியாக்கி\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"குறியாக்கி முடக்கு\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"காணொளி குறியாக்கி\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"காணொளி குறியாக்கி அமைப்புகள் (இருந்தால்)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"ஒலி குறியாக்கி\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"ஒலி குறியாக்கி அமைப்புகள் (இருந்தால்)\"\nBasic.Settings.Video=\"காணொளி\"\nBasic.Settings.Video.BaseResolution=\"அடிப்படை (சுற்றளவு) தெளிவுத்திறன்\"\nBasic.Settings.Video.ScaledResolution=\"(அளவிட்ட) வெளியீட்டு தெளிவுத்திறன்\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[தெளிவுத்திறன்கள் பொருந்துகின்றன, ஒப்பளவு குறைப்பு தேவையில்லை]\"\nBasic.Settings.Video.InvalidResolution=\"தவறான தெளிவுத்திறன் மதிப்பு. [width]x[height] இருக்க வேண்டும் (அதாவது 1920 x 1080)\"\nBasic.Settings.Audio=\"ஒலி\"\nBasic.Settings.Audio.SampleRate=\"மாதிரி விகிதம்\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"வேகமாக\"\nBasic.Settings.Audio.DesktopDevice=\"திரைப்பலக ஒலி\"\nBasic.Settings.Audio.DesktopDevice2=\"திரைப்பலக ஒலி 2\"\nBasic.Settings.Audio.Disabled=\"முடக்கப்பட்டது\"\nBasic.Settings.Advanced=\"மேம்பட்டவை\"\nBasic.Settings.Advanced.General.ProcessPriority=\"செயலாக்க முன்னுரிமை\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"உயர்\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"இயல்பு\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"செயலின்றி\"\nBasic.Settings.Advanced.Video.ColorFormat=\"வண்ண வடிவமைப்பு\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"வரம்புள்ளது\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"முழு\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"இயல்புநிலை\"\nBasic.Settings.Advanced.StreamDelay=\"அலை தாமதம்\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"கால அளவு\"\nBasic.Settings.Advanced.Network=\"பிணையம்\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"சுருக்குவிசைகளை ஒருபோதும் முடக்காதே\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(mkv ஆகப் பதிவிடு)\"\nBasic.AdvAudio=\"மேம்பட்ட ஒலி பண்புகள்\"\nBasic.AdvAudio.Name=\"பெயர்\"\nBasic.AdvAudio.Volume=\"ஒலியளவு\"\nBasic.AdvAudio.Mono=\"ஒற்றை ஒலி\"\nBasic.AdvAudio.Balance=\"சமநிலை\"\nBasic.Settings.Hotkeys=\"சுருக்குவிசைகள்\"\nBasic.Settings.Hotkeys.Filter=\"வடிகட்டிகள்\"\nBasic.SystemTray.Show=\"காட்டு\"\nBasic.SystemTray.Hide=\"மறை\"\nHotkeys.Insert=\"செருகு\"\nHotkeys.Delete=\"நீக்கு\"\nHotkeys.Home=\"முகப்பு\"\nHotkeys.End=\"இறுதி\"\nHotkeys.PageUp=\"பக்கத்தில் மேலே\"\nHotkeys.PageDown=\"பக்கத்தில் கீழே\"\nHotkeys.NumLock=\"எண் பூட்டு\"\nHotkeys.Tab=\"Tab விசை\"\nHotkeys.Print=\"அச்சிடு\"\nHotkeys.Pause=\"இடைநிறுத்து\"\nHotkeys.Left=\"இடது\"\nHotkeys.Right=\"வலது\"\nHotkeys.Up=\"மேல்\"\nHotkeys.Down=\"கீழ்\"\nHotkeys.Menu=\"பட்டியல்\"\nHotkeys.Space=\"இடைவெளி\"\nHotkeys.NumpadNum=\"எண் திண்டு %1\"\nHotkeys.Escape=\"Esc விசை\"\nMute=\"ஒலியடக்கு\"\nUnmute=\"ஒலிதடைநீக்கு\"\nOutputWarnings.NoTracksSelected=\"குறைந்தப் பட்சம் ஒரு தடத்தை நீங்கள் தேர்ந்தெடுக்க வேண்டும்\"\nChangeBG=\"வண்ணத்தை அமை\"\nCustomColor=\"தனிப்பயன் நிறம்\"\nBrowserSource.EnableHardwareAcceleration=\"Browser Source மென்பொருள் விரைவுபடுத்துதலை இயக்கு\"\nAbout=\"பற்றி\"\nAbout.Info=\"OBS Studio ஒரு இலவச மற்றும் திற மூல காணொளி பதிவிடுதல் மற்றும் நேரலை ஸ்ட்ரீமிங் மென்பொருள்.\"\nAbout.Donate=\"ஒரு பங்களிப்பைச் செய்க\"\nAbout.GetInvolved=\"உங்களை ஈடுபடுத்திக் கொள்ளுங்கள்\"\nAbout.Authors=\"ஆசிரியர்கள்\"\nAbout.License=\"உரிமம்\"\nAbout.Contribute=\"OBS திட்டப்பணியை ஆதரவி\"\nAddUrl.Title=\"URL மூலம் மூலத்தைச் சேர்\"\nAddUrl.Text=\"நீங்கள் ஒரு URLஐ OBSஇற்குள் இழுத்திருக்கிறீர்கள். இது இந்த இணைப்பைத் தானாக ஒரு மூலமாகச் சேர்க்கும். தொடரவா?\"\nImporter.Path=\"தொகுப்புப் பாதை\"\nImporter.Program=\"கண்டறியப்பட்ட செயலி\"\nNeedsRestart=\"OBS Studio மறுதொடங்க வேண்டும். இப்போது மறுதொடக்கம் செய்ய விரும்புகிறீர்களா?\"\n"
  },
  {
    "path": "frontend/data/locale/te-IN.ini",
    "content": "Language=\"తెలుగు\"\nOK=\"సరే\"\nApply=\"వర్తించు\"\nCancel=\"రద్దుచేయు\"\nClose=\"మూయండి\"\nSave=\"దాచు\"\nDiscard=\"తొలఁగించు\"\nDisable=\"రద్దు చేయబడింది\"\nYes=\"అవును\"\nNo=\"కాదు\"\nAdd=\"కలుపు\"\nRemove=\"తొలఁగించు\"\nRename=\"పేరు మార్చు\"\nInteract=\"ప్రతిస్పందన\"\nFilters=\"వడియఁపోతకాలు\"\nProperties=\"లక్షణములు\"\nMoveUp=\"పైకి ౙరుపు\"\nMoveDown=\"క్రిందికి ౙరుపు\"\nSettings=\"సెట్టింగులు\"\nDisplay=\"ప్రదర్శన\"\nName=\"పేరు\"\nExit=\"బహిర్గమనము\"\nMixer=\"ఆడియో మిక్సర్\"\nBrowse=\"బ్రౌజ్\"\nMono=\"మోనో\"\nStereo=\"స్టీరియో\"\nDroppedFrames=\"పడిపోయిన ఫ్రేములు %1(%2%)\"\nProjector.Open.Program=\"ప్రోగ్రామ్ ప్రొజెక్టర్‌ని తెరవండి\"\nProjector.Open.Preview=\"ప్రివ్యూ ప్రొజెక్టర్‌ని తెరవండి\"\nProjector.Open.Scene=\" సీన్ ప్రొజెక్టర్‌ని తెరవండి\"\nProjector.Open.Source=\"సోర్స్ ప్రొజెక్టర్‌ని తెరవండి\"\nProjector.Open.Multiview=\"మల్టీవ్యూ తెరవండి\"\nProjector.Display=\"డిస్‌ప్లే: %1\"\nProjector.Window=\"కొత్త విండో\"\nProjector.Title=\"ప్రొజెక్టర్ \"\nProjector.Title.Scene=\"సీన్: %1\"\nProjector.Title.Source=\" సోర్స్: %1\"\nProjector.Title.Multiview=\"మల్టీవ్యూ\"\nProjector.ResizeWindowToContent=\"కంటెంట్‌కు విండోని సరిపోల్చండి\"\nClear=\"క్లియర్ చేయండి\"\nRevert=\"మార్పులను తిరస్కరించు\"\nShow=\"చూపించు\"\nHide=\"దాచు\"\nUnhideAll=\"దాచవద్దు అన్ని\"\nUntitled=\"పేరులేని\"\nNew=\"కొత్త\"\nDuplicate=\"నకిలీ\"\nEnable=\"ఎనేబుల్\"\nDisableOSXVSync=\"macOS V-Sync డిసేబుల్\"\nResetOSXVSyncOnExit=\"నిష్క్రమణపై macOS V-sync రీసెట్ చేయండి\"\nHighResourceUsage=\"ఎన్కోడింగ్ ఓవర్లోడ్! వీడియో సెట్టింగులను తిరస్కరించడం లేదా వేగవంతమైన ఎన్కోడింగ్ ప్రీసెట్‌ను ఉపయోగించడం పరిగణించండి.\"\nTransition=\"పరివర్తన\"\nQuickTransitions=\"శీఘ్ర పరివర్తనాలు\"\nFadeToBlack=\"నలుపు రంగులోకి మారండి\"\nLeft=\"ఎడమ\"\\nRight=\"\\nకుడి\"\nTop=\"టాప్\"\nBottom=\"దిగువ\"\nReset=\"రీసెట్\"\nHours=\"గంటలు\"\nMinutes=\"నిమిషాలు\"\nSeconds=\"సెకన్లు\"\nDeprecated=\"ఆమోదించలేదు\"\\nReplayBuffer=\"\\nమళ్లీ బఫర్‌ని అమలు చేయండి\"\nImport=\"దిగుమతి\"\nExport=\"ఎగుమతి\"\nCopy=\"కాపీ\"\nPaste=\"అతికించండి\"\nPasteReference=\"అతికించండి (సూచన)\"\nPasteDuplicate=\"అతికించండి (నకిలీ)\"\nRemuxRecordings=\"రీమిక్స్ రికార్డింగ్‌లు\"\nNext=\"తరువాత\"\nBack=\"వెనుకకు\"\nDefaults=\"డిఫాల్ట్‌లు\"\nRestoreDefaults=\"డిఫాల్ట్‌లు\"\nAddSource=\"జోడించండి\"\n"
  },
  {
    "path": "frontend/data/locale/th-TH.ini",
    "content": "Language=\"ไทย\"\nOK=\"ตกลง\"\nApply=\"นำไปใช้\"\nCancel=\"ยกเลิก\"\nClose=\"ปิด\"\nSave=\"บันทึก\"\nDiscard=\"ละทิ้ง\"\nDisable=\"ปิดใช้งาน\"\nYes=\"ใช่\"\nNo=\"ไม่\"\nAdd=\"เพิ่ม\"\nRemove=\"ลบ\"\nRename=\"เปลี่ยนชื่อ\"\nInteract=\"โต้ตอบ\"\nFilters=\"ฟิลเตอร์\"\nProperties=\"คุณสมบัติ\"\nMoveUp=\"เลื่อนขึ้น\"\nMoveDown=\"เลื่อนลง\"\nSettings=\"ตั้งค่า\"\nDisplay=\"หน้าจอ\"\nName=\"ชื่อ\"\nExit=\"ออก\"\nMixer=\"มิกเซอร์เสียง\"\nBrowse=\"เรียกดู\"\nMono=\"โมโน\"\nStereo=\"สเตอริโอ\"\nDroppedFrames=\"เฟรมตกหล่น %1 (%2%)\"\nProjector.Open.Program=\"เปิดโปรแกรมโปรเจ็คเตอร์\"\nProjector.Open.Preview=\"พรีวิวโปรเจ็คเตอร์\"\nProjector.Open.Scene=\"โปรเจคเตอร์ฉากเปิด\"\nProjector.Open.Source=\"โปรเจคเตอร์แบบโอเพนซอร์ส\"\nProjector.Open.Multiview=\"เปิดโปรแกรมโปรเจ็คเตอร์\"\nProjector.Display=\"จอภาพ: %1\"\nProjector.Window=\"หน้าต่างใหม่\"\nProjector.Title=\"โปรเจคเตอร์\"\nProjector.Title.Scene=\"ฉาก: %1\"\nProjector.Title.Source=\"แหล่งที่มา: %1\"\nProjector.Title.Multiview=\"หลายมุมมอง\"\nProjector.ResizeWindowToContent=\"ปรับขนาดหน้าต่างให้พอดีกับเนื้อหา\"\nClear=\"ล้าง\"\nRevert=\"เปลี่ยนกลับ\"\nShow=\"แสดง\"\nHide=\"ซ่อน\"\nUnhideAll=\"ยกเลิกการซ่อนทั้งหมด\"\nUntitled=\"ไม่มีชื่อ\"\nNew=\"ใหม่\"\nDuplicate=\"ทำซ้ำ\"\nEnable=\"เปิดใช้งาน\"\nDisableOSXVSync=\"ปิดใช้งาน V-Sync ใน macOS\"\nResetOSXVSyncOnExit=\"คืนค่าเดิมของ V-Sync ใน macOS เมื่อออก\"\nHighResourceUsage=\"การเข้ารหัสเกินขีดจำกัด! โปรดลองปรับลดการตั้งค่าวีดีโอหรือใช้พรีเซ็ตการเข้ารหัสอื่นที่เร็วขึ้น\"\nTransition=\"การเปลี่ยนฉาก\"\nQuickTransitions=\"การเปลี่ยนฉากด่วน\"\nFadeToBlack=\"เฟดเป็นสีดำ\"\nLeft=\"ซ้าย\"\nRight=\"ขวา\"\nTop=\"บน\"\nBottom=\"ล่าง\"\nReset=\"คืนค่าเดิม\"\nHours=\"ชั่วโมง\"\nMinutes=\"นาที\"\nSeconds=\"วินาที\"\nDeprecated=\"เลิกใช้แล้ว\"\nReplayBuffer=\"รีเพลย์บัพเฟอร์\"\nImport=\"นําเข้า\"\nExport=\"ส่งออก\"\nCopy=\"คัดลอก\"\nPaste=\"วาง\"\nPasteReference=\"วาง (อ้างอิง)\"\nPasteDuplicate=\"วาง (ซ้ำ)\"\nRemuxRecordings=\"รีมักซ์สิ่งบันทึก\"\nNext=\"ถัดไป\"\nBack=\"ย้อนกลับ\"\nDefaults=\"ค่าเริ่มต้น\"\nRestoreDefaults=\"ค่าเริ่มต้น\"\nHideMixer=\"ซ่อนในตัวแต่งเสียง\"\nTransitionOverride=\"แทนที่การเปลี่ยนฉาก\"\nShowTransition=\"แสดงการเปลี่ยนฉาก\"\nHideTransition=\"ซ่อนการเปลี่ยนฉาก\"\nNone=\"ไม่มี\"\nStudioMode.Preview=\"แสดงตัวอย่าง\"\nStudioMode.Program=\"โปรแกรม\"\nStudioMode.PreviewSceneName=\"แสดงตัวอย่าง: %1\"\nStudioMode.ProgramSceneName=\"โปรแกรม: %1\"\nShowInMultiview=\"แสดงแบบหลายจอ\"\nVerticalLayout=\"เค้าโครงแนวตั้ง\"\nGroup=\"กลุ่ม\"\nDoNotShowAgain=\"ไม่ต้องแสดงอีก\"\nDefault=\"(ค่าเริ่มต้น)\"\nCalculating=\"กำลังคำนวณ...\"\nFullscreen=\"เต็มจอ\"\nWindowed=\"หน้าต่าง\"\nRefreshBrowser=\"รีเฟรช\"\nAspectRatio=\"อัตราส่วนภาพ <b>%1:%2</b>\"\nLockVolume=\"ล็อกระดับเสียง\"\nLogViewer=\"ตัวแสดงปูม\"\nShowOnStartup=\"แสดงเมื่อเปิดโปรแกรม\"\nOpenFile=\"เปิดไฟล์\"\nAddScene=\"เพิ่มฉาก\"\nAddSource=\"เพิ่มแหล่ง\"\nRemoveScene=\"ลบฉากที่เลือก\"\nRemoveSource=\"ลบแหล่งที่เลือก\"\nMoveSceneUp=\"ย้ายฉากขึ้น\"\nMoveSceneDown=\"ย้ายฉากลง\"\nMoveSourceUp=\"ย้ายแหล่งขึ้น\"\nMoveSourceDown=\"ย้ายแหล่งลง\"\nSourceProperties=\"เปิดคุณสมบัติแหล่ง\"\nSourceFilters=\"เปิดฟิลเตอร์แหล่ง\"\nMixerToolbarMenu=\"เมนูตัวแต่งเสียง\"\nSceneFilters=\"เปิดฟิลเตอร์ฉาก\"\nList=\"รายชื่อ\"\nGrid=\"ตาราง\"\nAutomatic=\"อัตโนมัติ\"\nComingSoon=\"เร็วๆนี้\"\nPluginsFailedToLoad.Title=\"การโหลดปลั๊กอินเกิดข้อผิดพลาด\"\nPluginsFailedToLoad.Text=\"ไม่สามารถโหลดปลั๊กอิน OBS ต่อไปนี้ได้:\\n\\n%1\\nโปรดปรับรุ่นหรือลบปลั๊กอินเหล่านี้\"\nAlreadyRunning.Title=\"OBS กำลังทำงานอยู่\"\nAlreadyRunning.Text=\"โปรแกรม OBS กำลังทำงานอยู่! หากคุณไม่ต้องการดำเนินการดังกล่าว โปรดปิดโปรแกรม OBS ที่มีอยู่ก่อนที่จะพยายามเรียกใช้อินสแตนซ์ใหม่ หากคุณตั้งค่าโปรแกรม OBS ให้ย่อเล็กสุด ให้ดูที่ถาดระบบว่าโปรแกรมยังคงทำงานอยู่หรือไม่\"\nAlreadyRunning.LaunchAnyway=\"ยืนยันเปิดต่อไป\"\nCrashHandling.Dialog.Title=\"ตรวจพบข้อขัดข้องของ OBS Studio\"\nCrashHandling.Labels.Text=\"OBS Studio ไม่ได้ปิดระบบอย่างถูกต้อง\\n\\nเรียกใช้ในเซฟโหมด (ปลั๊กอินของบุคคลที่สาม สคริปต์ และ WebSockets ถูกปิดใช้งาน) หรือไม่\"\nCrashHandling.Labels.PrivacyNotice=\"คุณยังสามารถเลือกที่จะอัปโหลดรายงานข้อขัดข้องล่าสุดไปยัง OBSProject ได้โดยอัตโนมัติ<br /><br />โปรดอ่าน<a href='https://obsproject.com/privacy-policy'>นโยบายความเป็นส่วนตัว</a> ก่อนที่จะอัปโหลดไฟล์ใดๆ และให้ความสนใจเป็นพิเศษกับส่วนที่เกี่ยวข้องกับการอัปโหลดไฟล์\"\nCrashHandling.Checkbox.SendReport=\"ฉันได้อ่านนโยบายความเป็นส่วนตัวแล้วและยินยอมให้ทำการอัปโหลด\"\nCrashHandling.Buttons.LaunchSafe=\"เรียกใช้ในเซฟโหมด\"\nCrashHandling.Buttons.LaunchNormal=\"เรียกใช้ในโหมดปกติ\"\nSafeMode.Restart=\"คุณต้องการรีสตาร์ท OBS ในเซฟโหมด (ปลั๊กอินของบุคคลที่สาม สคริปต์ และ WebSockets ถูกปิดใช้งาน) หรือไม่\"\nSafeMode.RestartNormal=\"คุณต้องการรีสตาร์ท OBS ในโหมดปกติหรือไม่\"\nChromeOS.Title=\"ไม่รองรับแพลตฟอร์มนี้\"\nChromeOS.Text=\"ดูเหมือนว่า OBS จะทำงานภายในคอนเทนเนอร์ ChromeOS ซึ่งเป็นแพลตฟอร์มที่ไม่รองรับ\"\nWine.Title=\"ตรวจพบ Wine\"\nWine.Text=\"OBS ไม่รองรับการทำงานบน Wine และคุณสมบัติหลายๆ อย่าง เช่น การจับภาพหน้าจอ หรือแหล่งอุปกรณ์ อาจไม่สามารถใช้งานได้ หรือได้แต่ไม่สมบูรณ์<br><br>ขอแนะนำให้ใช้ OBS เวอร์ชันที่ตรงกับระบบของคุณ เช่น <a href='https://flathub.org/apps/details/com.obsproject.Studio'>เวอร์ชัน Flatpak</a> หรือเวอร์ชันสำหรับระบบปฏิบัติการของคุณ\"\nDockCloseWarning.Title=\"ปิดหน้าต่างถาดงาน\"\nDockCloseWarning.Text=\"คุณเพิ่งจะปิดหน้าต่างถาดงานไป ถ้าคุณต้องการให้แสดงอีกครั้ง ให้ใช้เมนู ถาดงาน บนแถบเมนู\"\nExtraBrowsers=\"ถาดงานเบราว์เซอร์กำหนดเอง\"\nExtraBrowsers.Info=\"เพิ่มถาดงานโดยกำหนดชื่อกับ URL ให้กับถาดงานเหล่านั้น แล้วคลิก นำไปใช้ หรือ ปิด เพื่อเปิดถาดงาน คุณสามารถเพิ่มหรือลบถาดงานต่างๆ ได้ตลอดเวลา\"\nExtraBrowsers.DockName=\"ชื่อถาดงาน\"\nAuth.Authing.Title=\"กำลังยืนยันตัวตน...\"\nAuth.Authing.Text=\"กำลังยืนยันตัวตนกับ %1 กรุณารอสักครู่...\"\nAuth.AuthFailure.Title=\"การยืนยันตัวตนล้มเหลว\"\nAuth.AuthFailure.Text=\"ไม่สามารถพิสูจน์ตัวตนกับ %1 ได้:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"จำเป็นต้องยืนยันตัวตน\"\nAuth.InvalidScope.Text=\"ข้อกำหนดการยืนยันตัวตนสำหรับ %1 มีการเปลี่ยนแปลง คุณสมบัติบางอย่างอาจไม่สามารถใช้งานได้\"\nAuth.LoadingChannel.Title=\"กำลังโหลดข้อมูลช่อง...\"\nAuth.LoadingChannel.Text=\"กำลังโหลดข้อมูลช่องสำหรับ %1 กรุณารอสักครู่...\"\nAuth.LoadingChannel.Error=\"ไม่สามารถรับข้อมูลช่องได้\"\nAuth.ChannelFailure.Title=\"ไม่สามารถโหลดช่องได้\"\nAuth.ChannelFailure.Text=\"ไม่สามารถโหลดข้อมูลช่องสำหรับ %1 ได้\\n\\n%2: %3\"\nAuth.Chat=\"แชท\"\nAuth.StreamInfo=\"รายละเอียดการสตรีม\"\nTwitchAuth.Stats=\"สถิติ Twitch\"\nTwitchAuth.Feed=\"ฟีดกิจกรรม Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"ไม่สามารถสืบค้นสตรีมคีย์ได้\"\nTwitchAuth.TwoFactorFail.Text=\"OBS ไม่สามารถเชื่อมต่อบัญชีผู้ใช้ Twitch ของคุณได้ กรุณาตรวจให้แน่ใจว่าคุณได้เปิด<a href='https://www.twitch.tv/settings/security'>การยืนยันตัวตนแบบสองขั้นตอน</a>แล้ว เนื่องจากขั้นตอนดังกล่าวจำเป็นต้องทำสำหรับการสตรีม\"\nRestreamAuth.Channels=\"สตรีมช่องอีกครั้ง\"\nCopy.Filters=\"คัดลอกฟิลเตอร์\"\nPaste.Filters=\"วางฟิลเตอร์\"\nBrowserPanelInit.Title=\"กำลังเริ่มต้นเบราว์เซอร์...\"\nBrowserPanelInit.Text=\"กำลังเริ่มต้นเบราว์เซอร์ กรุณารอสักครู่...\"\nBandwidthTest.Region=\"ภูมิภาค\"\nBandwidthTest.Region.US=\"สหรัฐอเมริกา\"\nBandwidthTest.Region.EU=\"ยุโรป\"\nBandwidthTest.Region.Asia=\"เอเชีย\"\nBandwidthTest.Region.Other=\"อื่นๆ\"\nBasic.AutoConfig=\"ตัวช่วยปรับแต่งค่าอัตโนมัติ\"\nBasic.AutoConfig.ApplySettings=\"ใช้การตั้งค่า\"\nBasic.AutoConfig.StartPage=\"ข้อมูลการใช้งาน\"\nBasic.AutoConfig.StartPage.SubTitle=\"ระบุสิ่งที่คุณต้องการใช้โปรแกรมสำหรับ\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"เพิ่มประสิทธิภาพสำหรับการสตรีม บันทึกเป็นรอง\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"เพิ่มประสิทธิภาพสำหรับการบันทึก ฉันจะไม่สามารถส่งข้อมูล Steam\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"ฉันจะใช้เพียงกล้องเสมือนจริงเท่านั้น\"\nBasic.AutoConfig.VideoPage=\"การตั้งค่าวิดีโอ\"\nBasic.AutoConfig.VideoPage.SubTitle=\"ระบุการตั้งค่าวิดีโอที่คุณต้องการใช้\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"ใช้ค่าปัจจุบัน (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"จอภาพ %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"ใช้ค่าปัจจุบัน (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"เลือก 60 หรือ 30, ถ้าเป็นไปได้เลือก 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"เลือก 60 หรือ 30, ต้องการความละเอียดสูง\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"หมายเหตุ: ความละเอียดของพื้นที่หลัก ไม่จำเป็นต้องเหมือนกับความละเอียดที่คุณจะสตรีม หรือบันทึก การสตรีมหรือการบันทึกจริง อาจจะลดขนาดลงจากความละเอียดของพื้นที่หลัก เพื่อลดการใช้ทรัพยากร หรือความต้องการของบิตเรต\"\nBasic.AutoConfig.StreamPage=\"รายละเอียดการสตรีม\"\nBasic.AutoConfig.StreamPage.SubTitle=\"กรุณากรอกข้อมูลสตรีม\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"เชื่อมต่อบัญชี (แนะนำ)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"ตัดการเชื่อมต่อบัญชี\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"ตัดการเชื่อมต่อบัญชี?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"การเปลี่ยนแปลงนี้จะมีผลทันที คุณแน่ใจหรือไม่ว่าต้องการตัดการเชื่อมต่อบัญชีของคุณ?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"รับรหัสสตรีม\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"ข้อมูลเพิ่มเติม\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"ใช้สตรีมคีย์\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"ใช้สตรีมคีย์ (ขั้นสูง)\"\nBasic.AutoConfig.StreamPage.Service=\"บริการ\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"แสดงทั้งหมด...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"กำหนดเอง...\"\nBasic.AutoConfig.StreamPage.Server=\"เซิร์ฟเวอร์\"\nBasic.AutoConfig.StreamPage.StreamKey=\"รหัสสตรีม\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: ป้อนรหัสผ่านการเข้ารหัส\\nRTMP: ป้อนคีย์ที่บริการที่ให้มา\\nSRT: ป้อนสตรีมมิดหากบริการใช้\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"เข้ารหัสคีย์\"\nBasic.AutoConfig.StreamPage.BearerToken=\"โทเค็น Bearer\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"บัญชีเชื่อมต่อแล้ว\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"ทดสอบการประเมินค่าบิตเรตกับแบนด์วิดท์ (อาจใช้เวลาสักพัก)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"กำหนดการเข้ารหัสด้วยฮาร์ดแวร์\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"การเข้ารหัสฮาร์ดแวร์ช่วยขจัดการใช้งานพื้นที่ส่วนใหญ่ของ CPU แต่อาจต้องใช้บิตเรตมากขึ้นเพื่อให้ได้คุณภาพในระดับเดียวกัน\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"คำเตือนถ่ายทอดสด\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"การทดสอบแบนด์วิดท์กำลังจะสตรีมข้อมูลวิดีโอแบบสุ่มโดยไม่มีเสียงไปยังช่องของคุณ หากทำได้ ขอแนะนำให้ปิดการบันทึกวิดีโอสตรีมชั่วคราวและตั้งค่าสตรีมเป็นแบบส่วนตัวจนกว่าการทดสอบจะเสร็จสิ้น ดำเนินการต่อหรือไม่?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"ทดสอบ %1\"\nBasic.AutoConfig.TestPage=\"ผลสรุป\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"ขณะนี้โปรแกรมกำลังดำเนินการทดสอบชุดหนึ่งเพื่อประเมินการตั้งค่าที่เหมาะสมที่สุด\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"การทดสอบเสร็จสมบูรณ์\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"กำลังดำเนินการทดสอบแบนด์วิดท์ อาจใช้เวลาสักครู่...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"ไม่พบผลลัพธ์สำหรับโปรโตคอลของบริการนี้\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"กำลังเชื่อมต่อไปยัง: %1\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"ไม่สามารถเชื่อมต่อกับเซิร์ฟได้, โปรดตรวจสอบการเชื่อมต่ออินเทอร์เน็ตของคุณแล้วลองอีกครั้ง.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"ทดสอบแบนด์วิดท์สำหรับ: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"กำลังทดสอบตัวเข้ารหัสสตรีม อาจใช้เวลาสักครู่...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"กำลังทดสอบการลงรหัสบันทึกเทป อาจใช้เวลาสักครู่...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"เริ่มการทำงานลงรหัสผิดพลาด\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"กำลังทดสอบที่ %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"ตัวเข้ารหัสสตรีมมิง\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"ตัวลงรหัสบันทึกเทป\"\nBasic.AutoConfig.TestPage.Result.Header=\"การประมาณการการตั้งค่าโดยโปรแกรมได้พิจารณาแล้วว่าสิ่งเหล่านี้เหมาะสำหรับคุณ:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"หากต้องการใช้การตั้งค่าเหล่านี้ ให้คลิกใช้การตั้งค่า หากต้องการกำหนดค่าใหม่ ให้คลิกย้อนกลับ หากต้องการกำหนดการตั้งค่าด้วยตนเอง ให้คลิกยกเลิกและเปิดการตั้งค่า\"\nBasic.AutoConfig.Info=\"การกำหนดค่าอัตโนมัติจะกำหนดการตั้งค่าที่ดีที่สุดตามข้อกำหนดคอมพิวเตอร์และความเร็วอินเทอร์เน็ตของคุณ\"\nBasic.AutoConfig.RunAnytime=\"สามารถเรียกใช้ได้ตลอดเวลาโดยไปที่เมนูเครื่องมือ\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"ความละเอียดสตรีมมิ่ง (ปรับสัดส่วน)\"\nBasic.Stats=\"สถานะ\"\nBasic.Stats.CPUUsage=\"การใช้ CPU\"\nBasic.Stats.HDDSpaceAvailable=\"มีพื้นที่ว่างในดิสก์\"\nBasic.Stats.MemoryUsage=\"หน่วยความจำที่ใช้ไป\"\nBasic.Stats.AverageTimeToRender=\"เวลาเฉลี่ยในการเรนเดอร์แต่ละเฟรม\"\nBasic.Stats.SkippedFrames=\"ข้ามเฟรมเนื่องจากการเข้ารหัสล่าช้า\"\nBasic.Stats.MissedFrames=\"ข้ามเฟรมเนื่องจากการเรนเดอร์ล่าช้า\"\nBasic.Stats.Output.Stream=\"สตรีม\"\nBasic.Stats.Output.Recording=\"การบันทึก\"\nBasic.Stats.Status=\"สถานะ\"\nBasic.Stats.Status.Recording=\"กำลังบันทึก\"\nBasic.Stats.Status.Live=\"ออกอากาศ\"\nBasic.Stats.Status.Reconnecting=\"กำลังเชื่อมต่อใหม่\"\nBasic.Stats.Status.Inactive=\"ไม่ใช้งาน\"\nBasic.Stats.Status.Active=\"กำลังใช้งาน\"\nBasic.Stats.DroppedFrames=\"จำนวนเฟรมตกหล่น (เครือข่าย)\"\nBasic.Stats.MegabytesSent=\"ข้อมูลที่ส่งออกทั้งหมด\"\nBasic.Stats.Bitrate=\"บิตเรต\"\nBasic.Stats.DiskFullIn=\"พื้นที่จัดเก็บเต็ม\"\nBasic.Stats.DiskFullIn.Text=\"%1 ชั่วโมง, %2 นาที\"\nBasic.Stats.ResetStats=\"รีเซ็ตสถิติ\"\nResetUIWarning.Title=\"คุณแน่ใจหรือไม่ว่าต้องการคืนค่า UI?\"\nResetUIWarning.Text=\"การรีเซ็ต UI จะซ่อนถาดงานเพิ่มเติม คุณจะต้องยกเลิกการซ่อนถาดงานเหล่านี้จากเมนูถาดงานหากต้องการให้มองเห็นได้\\n\\nคุณแน่ใจหรือไม่ว่าต้องการรีเซ็ต UI?\"\nUpdater.Title=\"มีการอัปเดตใหม่\"\nUpdater.Text=\"มีการอัปเดตใหม่:\"\nUpdater.UpdateNow=\"อัปเดตเดี๋ยวนี้\"\nUpdater.RemindMeLater=\"เตือนฉันในภายหลัง\"\nUpdater.Skip=\"ข้ามรุ่น\"\nUpdater.NoUpdatesAvailable.Title=\"ไม่มีอัพเดทที่พร้อมใช้\"\nUpdater.NoUpdatesAvailable.Text=\"ขณะนี้ยังไม่มีอัพเดทที่พร้อมใช้\"\nUpdater.BranchNotFound.Title=\"ลบช่องทางการอัพเดตแล้ว\"\nUpdater.BranchNotFound.Text=\"ช่องทางการอัพเดตที่เลือกไม่สามารถใช้ได้อีกแล้ว OBS ได้ถูกรีเซ็ตเป็นค่าเริ่มต้นแล้ว\"\nUpdater.RepairButUpdatesAvailable.Title=\"การตรวจสอบความสมบูรณ์ไม่พร้อมใช้งาน\"\nUpdater.RepairButUpdatesAvailable.Text=\"การตรวจสอบความสมบูรณ์ของไฟล์ทำได้เฉพาะเวอร์ชันล่าสุดที่มีเท่านั้น ใช้ Help → Check For Updates เพื่อตรวจสอบและอัปเดตการติดตั้ง OBS ของคุณ\"\nUpdater.RepairConfirm.Title=\"ยืนยันการตรวจสอบ Integrity\"\nUpdater.RepairConfirm.Text=\"การเริ่มต้นการตรวจสอบ integrity จะสแกนการติดตั้ง OBS ของคุณเพื่อหาความเสียหาย และดาวน์โหลดไฟล์ที่เสียหาย/แก้ไขใหม่ อาจใช้เวลาสักครู่\\n\\nคุณต้องการดำเนินการต่อหรือไม่\"\nUpdater.FailedToLaunch=\"ไม่สามารถเปิดตัวอัปเดตได้\"\nQuickTransitions.SwapScenes=\"สลับฉากตัวอย่างและฉากของโปรแกรมหลังจากเปลี่ยน\"\nQuickTransitions.SwapScenesTT=\"สลับฉากตัวอย่างและฉากของโปรแกรมหลังจากเปลี่ยนแล้ว (หากฉากดั้งเดิมของโปรแกรมยังคงอยู่)\\nการดำเนินการนี้จะไม่ยกเลิกการเปลี่ยนแปลงใดๆ ที่อาจเกิดขึ้นกับฉากดั้งเดิมของโปรแกรม\"\nQuickTransitions.DuplicateScene=\"ฉากซ้ำกัน\"\nQuickTransitions.DuplicateSceneTT=\"เมื่อแก้ไขฉากเดียวกัน อนุญาตให้แก้ไขการแปลง/การมองเห็นของแหล่งโดยไม่ต้องแก้ไขเอาต์พุตของโปรแกรม\\nหากต้องการแก้ไขคุณสมบัติของแหล่งโดยไม่ต้องแก้ไขเอาต์พุตของโปรแกรม ให้เปิดใช้งาน 'แหล่งที่ซ้ำกัน'\\nการเปลี่ยนค่านี้จะรีเซ็ตฉากของโปรแกรมปัจจุบัน (ถ้ามีอยู่)\"\nQuickTransitions.EditProperties=\"ทำสำเนาแหล่ง\"\nQuickTransitions.EditPropertiesTT=\"เมื่อแก้ไขฉากเดียวกัน อนุญาตให้แก้ไขคุณสมบัติของแหล่งโดยไม่ต้องแก้ไขเอาต์พุตของโปรแกรม\\nสามารถใช้ได้เฉพาะเมื่อเปิดใช้งาน 'ฉากซ้ำ' เท่านั้น\\nแหล่งบางแห่ง (เช่น แหล่งจับภาพหรือแหล่งสื่อ) ไม่รองรับสิ่งนี้และไม่สามารถแก้ไขได้ แยกกัน\\nการเปลี่ยนค่านี้จะรีเซ็ตฉากของโปรแกรมปัจจุบัน (หากยังคงมีอยู่)\\n\\nคำเตือน: เนื่องจากแหล่งจะถูกทำซ้ำ การดำเนินการนี้อาจต้องใช้ทรัพยากรระบบหรือวิดีโอเพิ่มเติม\"\nQuickTransitions.HotkeyName=\"สลับฉากอย่างรวดเร็ว: %1\"\nBasic.AddTransition=\"เพิ่มการเปลี่ยนฉากที่สามารถกำหนดค่าได้\"\nBasic.RemoveTransition=\"ลบการเปลี่ยนฉากที่สามารถกำหนดค่าได้\"\nBasic.TransitionProperties=\"คุณสมบัติการเปลี่ยนฉาก\"\nBasic.SceneTransitions=\"รูปแบบการเปลี่ยนฉาก\"\nBasic.TransitionDuration=\"ระยะเวลา\"\nBasic.TogglePreviewProgramMode=\"โหมดสตูดิโอ\"\nBasic.EnablePreviewProgramMode=\"เปิดใช้งานโหมดสตูดิโอ\"\nBasic.DisablePreviewProgramMode=\"ปิดใช้งานโหมดสตูดิโอ\"\nUndo.Undo=\"เลิกทำ\"\nUndo.Redo=\"ทำซ้ำ\"\nUndo.Add=\"เพิ่ม '%1'\"\nUndo.Delete=\"ลบ '%1'\"\nUndo.Rename=\"เปลี่ยนชื่อ '%1'\"\nUndo.SceneCollection.Switch=\"เปลี่ยนเป็น '%1'\"\nUndo.Item.Undo=\"ย้อนกลับ %1\"\nUndo.Item.Redo=\"ทำใหม่ %1\"\nUndo.Sources.Multi=\"ลบ %1 แหล่ง\"\nUndo.Filters=\"เปลี่ยนฟิลเตอร์ %1\"\nUndo.Filters.Paste.Single=\"วางฟิลเตอร์ %1 ถึง %2\"\nUndo.Filters.Paste.Multiple=\"คัดลอกฟิลเตอร์จาก %1 ถึง %2\"\nUndo.Transform=\"แปลงค่าแหล่งใน %1\"\nUndo.Transform.Paste=\"วางการแปลงค่า ใน %1\"\nUndo.Transform.Rotate=\"การหมุน ใน %1\"\nUndo.Transform.Reset=\"การแปลงค่ากลับค่าเดิม ใน %1\"\nUndo.Transform.HFlip=\"พลิกแนวนอน %1\"\nUndo.Transform.VFlip=\"พลิกแนวตั้ง%1\"\nUndo.Transform.FitToScreen=\"พอดีจอ %1\"\nUndo.Transform.StretchToScreen=\"ขยายเท่าจอ %1\"\nUndo.Transform.Center=\"กึ่งกลางจอ %1\"\nUndo.Transform.VCenter=\"กึ่งกลางแนวตั้งของจอ %1\"\nUndo.Transform.HCenter=\"กึ่งกลางแนวนอนของจอ %1\"\nUndo.Volume.Change=\"ปรับระดับเสียง %1\"\nUndo.Volume.Mute=\"ปิดเสียง %1\"\nUndo.Volume.Unmute=\"เปิดเสียง %1\"\nUndo.Balance.Change=\"การเปลี่ยนความสมดุลของเสียงใน '%1'\"\nUndo.SyncOffset.Change=\"การเปลี่ยน Sync Offset เสียงใน '%1'\"\nUndo.MonitoringType.Change=\"เปลี่ยนการมอนิเตอร์เสียงใน '%1'\"\nUndo.Mixers.Change=\"เปลี่ยนตัวแต่งเสียงใน '%1'\"\nUndo.ForceMono.On=\"เปิดใช้งานเสียงโมโนใน '%1'\"\nUndo.ForceMono.Off=\"ปิดใช้งานเสียงโมโนใน '%1'\"\nUndo.Properties=\"การเปลี่ยนแปลงคุณสมบัติใน '%1'\"\nUndo.Scene.Duplicate=\"คัดลอกฉาก %1\"\nUndo.ShowTransition=\"แสดงทรานซิชัน %1\"\nUndo.HideTransition=\"ซ่อนทรานซิชัน %1\"\nUndo.ShowSceneItem=\"แสดง %1 ใน %2\"\nUndo.HideSceneItem=\"ซ่อน %1 ใน %2\"\nUndo.ReorderSources=\"เรียงลำดับแหล่งใหม่ใน '%1'\"\nUndo.MoveUp=\"ย้ายขึ้น %1 %2\"\nUndo.MoveDown=\"ย้ายลง %1 %2\"\nUndo.MoveToTop=\"ย้าย '%1' ขึ้นไปบนสุดใน %2\"\nUndo.MoveToBottom=\"ย้าย '%1' ลงไปล่างสุดใน %2\"\nUndo.PasteSource=\"วางแหล่ง %1\"\nUndo.PasteSourceRef=\"วางแหล่งใน '%1'\"\nUndo.GroupItems=\"รวมรายการเป็นกลุ่ม %1\"\nTransitionNameDlg.Text=\"โปรดกำหนดชื่อสำหรับการเปลี่ยนฉาก\"\nTransitionNameDlg.Title=\"ชื่อการเปลี่ยนฉาก\"\nTitleBar.SafeMode=\"โหมดปลอดภัย\"\nTitleBar.PortableMode=\"โหมดพกพา\"\nTitleBar.Profile=\"โปรไฟล์\"\nTitleBar.Scenes=\"ฉาก\"\nNameExists.Title=\"มีผู้ใช้นี้ในระบบแล้ว\"\nNameExists.Text=\"ชื่อนี้ถูกใช้แล้ว\"\nNoNameEntered.Title=\"กรุณาใส่ชื่อให้ถูกต้อง\"\nNoNameEntered.Text=\"คุณจำเป็นต้องกำหนดชื่อ\"\nConfirmStart.Title=\"เริ่มทำการสตรีม?\"\nConfirmStart.Text=\"คุณแน่ใจไหมว่าต้องการเริ่มการสตรีม?\"\nConfirmStop.Title=\"หยุดการสตรีม?\"\nConfirmStop.Text=\"คุณแน่ใจไหมว่าต้องการที่จะหยุดการสตรีม?\"\nConfirmStopRecord.Title=\"หยุดการบันทึก?\"\nConfirmStopRecord.Text=\"คุณแน่ใจหรือว่าคุณต้องการที่จะหยุดการบันทึก?\"\nConfirmBWTest.Title=\"เริ่มการทดสอบแบนด์วิดท์?\"\nConfirmBWTest.Text=\"คุณได้กำหนดค่า OBS ในโหมดทดสอบแบนด์วิดท์ โหมดนี้อนุญาตให้ทำการทดสอบเครือข่ายโดยที่ช่องของคุณไม่ได้ถ่ายทอดสด เมื่อคุณทดสอบเสร็จแล้ว คุณจะต้องปิดการใช้งานเพื่อให้ผู้ดูสามารถเห็นสตรีมของคุณ\\n\\nคุณต้องการดำเนินการต่อหรือไม่\"\nConfirmRemove.Title=\"ยืนยันการลบ\"\nConfirmRemove.Text=\"คุณแน่ใจหรือไม่ว่าต้องการลบ '%1'?\"\nConfirmRemove.TextMultiple=\"คุณแน่ใจแล้วหรือที่จะลบ '%1'?\"\nConfirmReset.Title=\"รีเซ็ตคุณสมบัติ\"\nConfirmReset.Text=\"คุณแน่ใจหรือไม่ว่าต้องการรีเซ็ตคุณสมบัติปัจจุบันเป็นค่าเริ่มต้น?\"\nOutput.StartStreamFailed=\"ไม่สามารถเริ่มสตรีมได้\"\nOutput.StartRecordingFailed=\"ไม่สามารถเริ่มบันทึกได้\"\nOutput.StartReplayFailed=\"ไม่สามารถเล่นบัฟเฟอร์ซ้ำได้\"\nOutput.StartVirtualCamFailed=\"เริ่มกล้องเสมือนไม่สำเร็จ\"\nOutput.StartFailedGeneric=\"การส่งออกเอาต์พุตล้มเหลว โปรดตรวจสอบบันทึกเพื่อดูรายละเอียด\\n\\nหมายเหตุ: หากคุณกำลังใช้ตัวเข้ารหัส NVENC หรือ AMD ตรวจสอบให้แน่ใจว่าไดรเวอร์วิดีโอของคุณเป็นเวอร์ชันล่าสุด\"\nOutput.ReplayBuffer.PauseWarning.Title=\"ไม่สามารถบันทึกไฟล์ที่เล่นซ้ำขณะหยุดชั่วคราวได้\"\nOutput.ReplayBuffer.PauseWarning.Text=\"คำเตือน: ไม่สามารถบันทึกไฟล์ที่เล่นซ้ำขณะหยุดการบันทึกชั่วคราวได้\"\nOutput.ConnectFail.Title=\"ไม่สามารถเชื่อมต่อได้\"\nOutput.ConnectFail.BadPath=\"URL ของเส้นทางหรือการเชื่อมต่อไม่ถูกต้อง โปรดตรวจสอบการตั้งค่าของคุณให้ถูกต้อง\"\nOutput.ConnectFail.ConnectFailed=\"ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์\"\nOutput.ConnectFail.InvalidStream=\"ไม่สามารถเข้าถึงช่องหรือรหัสสตรีมที่ระบุได้ โปรดตรวจสอบรหัสสตรีมของคุณใหม่อีกครั้ง ถ้าถูกต้องแล้ว แสดงว่าอาจมีปัญหาในการเชื่อมต่อกับเซิร์ฟเวอร์\"\nOutput.ConnectFail.HdrDisabled=\"ขณะนี้เอาต์พุต HDR ถูกปิดใช้งานสำหรับเอาต์พุตนี้\"\nOutput.ConnectFail.Error=\"มีข้อผิดพลาดไม่คาดคิดเกิดขึ้นเมื่อพยายามเชื่อมต่อกับเซิร์ฟเวอร์ โปรดดูข้อมูลเพิ่มเติมในไฟล์ log\"\nOutput.ConnectFail.Disconnected=\"ถูกตัดออกจากเซิร์ฟเวอร์\"\nOutput.StreamEncodeError.Title=\"ข้อผิดพลาดในการเข้ารหัส\"\nOutput.StreamEncodeError.Msg=\"ตัวเข้ารหัสเกิดข้อผิดพลาดขณะสตรีม\"\nOutput.StreamEncodeError.Msg.LastError=\"ตัวเข้ารหัสเกิดข้อผิดพลาดขณะสตรีม:<br><br>%1\"\nOutput.RecordFail.Title=\"การเริ่มบันทึกล้มเหลว\"\nOutput.RecordFail.Unsupported=\"ไม่รองรับรูปแบบเอาต์พุตนี้ หรือไม่รองรับแทร็กเสียงมากกว่าหนึ่งแทร็ก โปรดตรวจสอบการตั้งค่าแล้วลองอีกครั้ง\"\nOutput.RecordNoSpace.Title=\"พื้นที่เก็บข้อมูลไม่เพียงพอ\"\nOutput.RecordNoSpace.Msg=\"มีพื้นที่ว่างบนดิสก์ไม่เพียงพอที่จะบันทึกต่อ\"\nOutput.RecordError.Title=\"การบันทึกผิดพลาด\"\nOutput.RecordError.Msg=\"มีข้อผิดพลาดที่ระบุไม่ได้เกิดขึ้นขณะบันทึก\"\nOutput.RecordError.EncodeErrorMsg=\"มีข้อผิดพลาดเกี่ยวกับตัวเข้ารหัสเกิดขึ้นขณะบันทึก\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"ตัวเข้ารหัสเกิดข้อผิดพลาดขณะบันทึก:<br><br>%1\"\nOutput.BadPath.Title=\"เส้นทางของไฟล์ไม่ถูกต้อง\"\nOutput.BadPath.Text=\"ไม่สามารถเปิดเส้นทางการบันทึกที่กำหนดค่าไว้ได้ โปรดตรวจสอบเส้นทางการบันทึกของคุณใน การตั้งค่า → เอาต์พุต → การบันทึก\"\nOutput.NoBroadcast.Title=\"ไม่ได้กำหนดค่าการถ่ายทอด\"\nOutput.NoBroadcast.Text=\"คุณต้องตั้งค่า broadcast ก่อนจึงจะเริ่ม streaming ได้\"\nOutput.BroadcastStartFailed=\"ไม่สามารถเริ่มถ่ายทอดได้\"\nOutput.BroadcastStopFailed=\"ไม่สามารถหยุดถ่ายทอดได้\"\nLogUploadDialog.Labels.Description.CrashLog=\"\"\nLogUploadDialog.Buttons.ConfirmUpload=\"อัปโหลด\"\nRemux.SourceFile=\"การบันทึก OBS\"\nRemux.TargetFile=\"ไฟล์เป้าหมาย\"\nRemux.Remux=\"รีมักซ์\"\nRemux.Stop=\"หยุดการรีมักซ์\"\nRemux.ClearFinished=\"ล้างรายการที่เสร็จแล้ว\"\nRemux.ClearAll=\"ล้างรายการทั้งหมด\"\nRemux.OBSRecording=\"การบันทึก OBS\"\nRemux.FinishedTitle=\"รีมักซ์เสร็จสิ้น\"\nRemux.Finished=\"รีมักซ์การบันทึกแล้ว\"\nRemux.FinishedError=\"รีมักซ์การบันทึกแล้ว แต่ไฟล์อาจไม่สมบูรณ์\"\nRemux.SelectRecording=\"เลือกการบันทึก OBS...\"\nRemux.SelectTarget=\"เลือกไฟล์เป้าหมาย...\"\nRemux.FileExistsTitle=\"ไฟล์เป้าหมายมีอยู่แล้ว\"\nRemux.FileExists=\"ไฟล์เป้าหมายต่อไปนี้มีอยู่แล้ว, คุณต้องการแทนที่เลยไหม?\"\nRemux.ExitUnfinishedTitle=\"กำลังทำการรีมักซ์\"\nRemux.ExitUnfinished=\"การรีมักซ์ยังไม่เสร็จ การหยุดตอนนี้อาจทำให้ไฟล์เสียหาย\\nคุณแน่ใจหรือไม่ว่าต้องการหยุด\"\nRemux.HelpText=\"ลากไฟล์มาไว้ในหน้าต่างนี้เพื่อทำการรีมักซ์ หรือเลือกเซลล์ \\\"การบันทึก OBS\\\" เปล่าเพื่อเปิดหาไฟล์\"\nRemux.NoFilesAddedTitle=\"ไม่มีการเพิ่มไฟล์ remuxing\"\nRemux.NoFilesAdded=\"ไม่มีการเพิ่มไฟล์ลงไปใน remux วางโฟลเดอร์ที่มีไฟล์วิดีโอตั้งแต่หนึ่งไฟล์ขึ้นไป\"\nMissingFiles=\"ไฟล์ที่หายไปทั้งหมด\"\nMissingFiles.MissingFile=\"ไฟล์ที่หายไป\"\nMissingFiles.NewFile=\"ไฟล์ใหม่\"\nMissingFiles.HelpText=\"บางไฟล์หายไปตั้งแต่ที่คุณใช้ OBS ครั้งล่าสุด\"\nMissingFiles.Clear=\"<ถูกล้างแล้ว>\"\nMissingFiles.NumFound=\"พบ %1 จาก %2\"\nMissingFiles.Search=\"ค้นหาไดเรกทอรี...\"\nMissingFiles.SelectFile=\"เลือกไฟล์...\"\nMissingFiles.SelectDir=\"เลือกโฟลเดอร์ที่จะค้นหา\"\nMissingFiles.State=\"สถานะ\"\nMissingFiles.Missing=\"ไม่มี\"\nMissingFiles.Replaced=\"ถูกแทนที่\"\nMissingFiles.Cleared=\"ถูกล้าง\"\nMissingFiles.Found=\"เจอแล้ว\"\nMissingFiles.AutoSearch=\"พบไฟล์เพิ่มเติมที่ตรงกัน\"\nMissingFiles.AutoSearchText=\"OBS พบไฟล์ที่ตรงกันเพิ่มเติมสำหรับไฟล์ที่ขาดหายไปในไดเร็กทอรีนั้น คุณต้องการเพิ่มหรือไม่\"\nMissingFiles.NoMissing.Title=\"ตรวจสอบไฟล์ที่หายไป\"\nMissingFiles.NoMissing.Text=\"ไม่มีไฟล์ใดที่ดูเหมือนจะหายไป\"\nMacPermissions.MenuAction=\"ตรวจสอบสิทธิ์ของแอพ...\"\nMacPermissions.Title=\"ตรวจสอบการอนุญาตแอพ\"\nMacPermissions.Description=\"OBS Studio ต้องได้รับอนุญาตจากคุณจึงจะสามารถให้บริการคุณลักษณะบางอย่างได้ ขอแนะนำให้เปิดใช้งานการอนุญาตเหล่านี้ แต่ไม่จำเป็นต้องใช้แอพ คุณสามารถเปิดใช้งานได้ในภายหลัง\"\nMacPermissions.Description.OpenDialog=\"คุณสามารถเปิดกล่องโต้ตอบนี้อีกครั้งผ่านเมนู OBS Studio\"\nMacPermissions.AccessGranted=\"ได้รับสิทธิ์การเข้าถึง\"\nMacPermissions.RequestAccess=\"ขอการเข้าถึง\"\nMacPermissions.OpenPreferences=\"เปิดการตั้งค่า %1\"\nMacPermissions.Item.ScreenRecording=\"บันทึกหน้าจอ\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS ต้องการ การอนุญาตนี้จึงจะสามารถจับภาพหน้าจอของคุณได้\"\nMacPermissions.Item.Camera=\"กล้อง\"\nMacPermissions.Item.Camera.Details=\"การอนุญาตนี้จำเป็นสำหรับการบันทึกเนื้อหาจากเว็บแคมหรือการ์ดจับภาพ\"\nMacPermissions.Item.Microphone=\"ไมโครโฟน\"\nMacPermissions.Item.Microphone.Details=\"OBS ต้องการสิทธิ์นี้หากคุณต้องการบันทึกเสียงไมโครโฟนหรืออุปกรณ์เสียงภายนอกของคุณ\"\nMacPermissions.Item.InputMonitoring=\"การตรวจสอบอินพุต\"\nMacPermissions.Item.InputMonitoring.Details=\"ต้องได้รับอนุญาตเพื่อให้ปุ่มลัดทำงานขณะ OBS อยู่เบื้องหลัง\"\nMacPermissions.Item.Accessibility=\"การช่วยการเข้าถึง\"\nMacPermissions.Item.Accessibility.Details=\"ในการติดตั้งเวอร์ชันเก่า OBS อาจแสดงอยู่ใน \\\"การช่วยการเข้าถึง\\\" แทนที่จะเป็น \\\"การตรวจสอบอินพุต\\\"\"\nMacPermissions.Continue=\"ดำเนินการต่อ\"\nSourceLeak.Title=\"เกิดข้อผิดพลาดขณะล้างข้อมูลแหล่ง\"\nSourceLeak.Text=\"เกิดปัญหา!ขณะเปลี่ยนคอลเลคชันฉากและบางแหล่งไม่สามารถยกเลิกการโหลดได้ ปัญหา!นี้มักเกิดจากปลั๊กอินที่ไม่ได้ปล่อยทรัพยากรอย่างเหมาะสม โปรดตรวจสอบให้แน่ใจว่าปลั๊กอินที่คุณใช้เป็นเวอร์ชันล่าสุด\\n\\nOBS Studio จะออกเพื่อป้องกันข้อมูลที่อาจเสียหายได้!\"\nBasic.Scene=\"ฉาก\"\nBasic.DisplayCapture=\"จับภาพหน้าจอ\"\nBasic.Main.PreviewConextMenu.Enable=\"เปิดใช้งานตัวอย่าง\"\nBasic.Main.Preview.Disable=\"ปิดใช้งานตัวอย่าง\"\nScaleFiltering=\"มาตราส่วนฟิลเตอร์\"\nScaleFiltering.Point=\"จุด\"\nScaleFiltering.Bilinear=\"ไบลิเนียร์\"\nBlendingMethod=\"วิธีการเบลนด์\"\nBlendingMethod.Default=\"ค่าเริ่มต้น\"\nBlendingMethod.SrgbOff=\"ปิด SRGB\"\nBlendingMode=\"โหมดการเบลนด์\"\nBlendingMode.Normal=\"ปกติ\"\nBlendingMode.Additive=\"เพิ่ม\"\nBlendingMode.Subtract=\"ลบออก\"\nBlendingMode.Screen=\"หน้าจอ\"\nBlendingMode.Multiply=\"จำนวนคริติคอล\"\nBlendingMode.Lighten=\"สว่าง\"\nBlendingMode.Darken=\"มืด\"\nDeinterlacing=\"ดีอินเทอร์เลซ\"\nDeinterlacing.Discard=\"ยกเลิก\"\nDeinterlacing.TopFieldFirst=\"ฟิลด์บนสุดก่อน\"\nDeinterlacing.BottomFieldFirst=\"ฟิลด์ล่างสุดก่อน\"\nVolControl.SliderUnmuted=\"แถบเลื่อนระดับเสียงสำหรับ '%1':\"\nVolControl.SliderMuted=\"แถบเลื่อนระดับเสียงสำหรับ '%1': (ขณะนี้ปิดเสียงอยู่)\"\nVolControl.Mute=\"ปิดเสียง %1\"\nVolControl.Properties=\"คุณสมบัติของ '%1'\"\nVolControl.UnassignedWarning.Title=\"แหล่งเสียงที่ไม่ได้กำหนด\"\nVolControl.UnassignedWarning.Text=\"ไม่ได้กำหนด \\\"%1\\\" ให้กับแทร็กเสียงใดและจะไม่ได้ยินในสตรีมหรือสิ่งบันทึกต่างๆ\\n\\nหากต้องการกำหนดแหล่งเสียงให้กับแทร็กหนึ่ง เปิด คุณสมบัติเสียงขั้นสูง ผ่านเมนูคลิกขวาหรือปุ่มฟันเฟืองในแถบเครื่องมือถาดงานตัวแต่งเสียง\"\nBasic.Main.AddSceneDlg.Title=\"เพิ่มฉาก\"\nBasic.Main.AddSceneDlg.Text=\"โปรดกำหนดชื่อฉาก\"\nBasic.Main.DefaultSceneName.Text=\"ฉาก %1\"\nBasic.Main.AddSceneCollection.Title=\"เพิ่มคอลเลกชัน\"\nBasic.Main.AddSceneCollection.Text=\"กรุณาใส่ชื่อคอลเลกชันฉาก\"\nBasic.Main.RenameSceneCollection.Title=\"เปลี่ยนชื่อคอลเลกชันฉาก\"\nBasic.Main.RemigrateSceneCollection.Title=\"ปรับเปลี่ยนความละเอียดคอลเลกชันฉาก\"\nBasic.Main.RemigrateSceneCollection.Text=\"คุณต้องการปรับเปลี่ยนความละเอียดคอลเลกชันฉากของ \\\"%1\\\" ให้ตรงกับความละเอียดแคนวาสที่ %2x%3 ของโปรไฟล์ปัจจุบันหรือไม่?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"ไม่สามารถปรับเปลี่ยนความละเอียดคอลเลกชันฉากได้ขณะที่เอาต์พุตถูกใช้งานอยู่\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"ไม่สามารถปรับเปลี่ยนความละเอียดคอลเลกชันฉากได้ ไม่ทราบความละเอียดดั้งเดิม\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"ไม่สามารถคืนค่าได้: การเปลี่ยนความละเอียด OBS ล้มเหลว\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"ไม่สามารถคืนค่าได้: ความละเอียดปัจจุบันเป็นความละเอียดพื้นที่หลักของคอลเลกชันฉากอยู่แล้ว\"\nAddProfile.Title=\"เพิ่มโปรไฟล์\"\nAddProfile.Text=\"กรุณาใส่ชื่อของโปรไฟล์\"\nAddProfile.WizardCheckbox=\"แสดงการกำหนดค่าอัตโนมัติ\"\nRenameProfile.Title=\"เปลี่ยนชื่อโปรไฟล์\"\nBasic.Main.MixerRename.Title=\"เปลี่ยนชื่อแหล่งเสียง\"\nBasic.Main.MixerRename.Text=\"โปรดใส่ชื่อให้กับแหล่งเสียง\"\nBasic.Main.PreviewDisabled=\"ภาพตัวอย่างถูกปิดใช้งานในขณะนี้\"\nBasic.SourceSelect=\"สร้าง/เลือกแหล่ง\"\nBasic.SourceSelect.CreateNew=\"สร้างใหม่\"\nBasic.SourceSelect.AddExisting=\"เพิ่มที่มีอยู่\"\nBasic.SourceSelect.AddVisible=\"แสดงแหล่ง\"\nBasic.Main.Sources.Visibility=\"ความชัดเจน\"\nBasic.Main.Sources.VisibilityDescription=\"ควบคุมการมองเห็น '%1' ในแคนวาส\"\nBasic.Main.Sources.Lock=\"ล๊อค\"\nBasic.Main.Sources.LockDescription=\"ล็อกตำแหน่งและมาตราส่วนของ '%1' ในแคนวาส\"\nBasic.PropertiesWindow=\"คุณสมบัติของ '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (เลือกอัตโนมัติ: %2)\"\nBasic.PropertiesWindow.SelectColor=\"เลือกสี\"\nBasic.PropertiesWindow.SelectFont=\"เลือกแบบอักษร\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"เลือกแบบอักษร\"\nBasic.PropertiesWindow.ConfirmTitle=\"การตั้งค่ามีการเปลี่ยนแปลง\"\nBasic.PropertiesWindow.Confirm=\"มีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก, คุณต้องการบันทึกตอนนี้เลยไหม?\"\nBasic.PropertiesWindow.NoProperties=\"ไม่มีคุณสมบัติ\"\nBasic.PropertiesWindow.AddFiles=\"เพิ่มไฟล์\"\nBasic.PropertiesWindow.AddDir=\"เพิ่มไดเรกทอรี\"\nBasic.PropertiesWindow.AddURL=\"เพิ่ม Path/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"เพิ่มไดเรกทอรีไปที่ '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"เพิ่มไฟล์ %1\"\nBasic.PropertiesWindow.AddEditableListEntry=\"เพิ่มไดเรกทอรีไปที่ '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"แก้ไขรายการจาก '%1'\"\nBasic.PropertiesView.FPS.Simple=\"ค่า FPS อย่างง่าย\"\nBasic.PropertiesView.FPS.Rational=\"ค่า FPS ตรรกยะ\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"ช่วง FPS ที่ใช้ได้:\"\nBasic.PropertiesView.UrlButton.Text=\"เปิดลิงค์นี้ในเว็บเบราว์เซอร์เริ่มต้นของคุณ?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"เปิด URL\"\nBasic.InteractionWindow=\"โต้ตอบกับ '%1'\"\nBasic.StatusBar.Reconnecting=\"ตัดการเชื่อมต่อแล้ว, จะเชื่อมต่อใหม่ในอีก %2 วินาที (พยายาม %1)\"\nBasic.StatusBar.AttemptingReconnect=\"กำลังพยายามเชื่อมต่อใหม่... (พยายาม %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"เชื่อมต่อใหม่สำเร็จ\"\nBasic.StatusBar.Delay=\"ดีเลย์ (%1 วินาที)\"\nBasic.StatusBar.DelayStartingIn=\"ดีเลย์ (จะเริ่มใน %1 วินาที)\"\nBasic.StatusBar.DelayStoppingIn=\"ดีเลย์ (จะหยุดใน %1 วินาที)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"ดีเลย์ (จะหยุดใน %1 วินาที, จะเริ่มใน %2 วินาที)\"\nBasic.StatusBar.RecordingSavedTo=\"บันทึกการบันทึกใน '%1' แล้ว\"\nBasic.StatusBar.ReplayBufferSavedTo=\"บันทึกรีเพลย์บัฟเฟอร์ใน '%1' แล้ว\"\nBasic.StatusBar.ScreenshotSavedTo=\"บันทึกภาพหน้าจอใน '%1' แล้ว\"\nBasic.StatusBar.AutoRemuxedTo=\"รีมักซ์การบันทึกอัตโนมัติใน '%1' แล้ว\"\nBasic.Filters=\"ฟิลเตอร์\"\nBasic.Filters.AsyncFilters=\"ฟิลเตอร์ เสียง/วิดีโอ\"\nBasic.Filters.AudioFilters=\"ไฟล์เสียง\"\nBasic.Filters.EffectFilters=\"เอฟเฟกต์ฟิลเตอร์\"\nBasic.Filters.Title=\"ฟิลเตอร์สำหรับ '%1'\"\nBasic.Filters.AddFilter.Title=\"ชื่อฟิลเตอร์\"\nBasic.Filters.AddFilter.Text=\"โปรดระบุชื่อของฟิลเตอร์\"\nBasic.TransformWindow=\"ปรับรูปร่างรายการฉาก\"\nBasic.TransformWindow.Position=\"ตำแหน่ง\"\nBasic.TransformWindow.Rotation=\"การหมุน\"\nBasic.TransformWindow.Size=\"ขนาด\"\nBasic.TransformWindow.Width=\"ความกว้าง\"\nBasic.TransformWindow.Height=\"ความสูง\"\nBasic.TransformWindow.Alignment=\"การจัดตำแหน่ง\"\nBasic.TransformWindow.BoundsType=\"ชนิดกล่องขอบเขต\"\nBasic.TransformWindow.BoundsAlignment=\"การจัดแนวในกล่องขอบเขต\"\nBasic.TransformWindow.Bounds=\"ขนาดกล่องขอบเขต\"\nBasic.TransformWindow.BoundsWidth=\"ความกว้างกล่องขอบเขต\"\nBasic.TransformWindow.BoundsHeight=\"ความสูงกล่องขอบเขต\"\nBasic.TransformWindow.CropToBounds=\"ตัดภาพตามกรอบ\"\nBasic.TransformWindow.Crop=\"ครอบตัด\"\nBasic.TransformWindow.CropLeft=\"ครอบตัดซ้าย\"\nBasic.TransformWindow.CropRight=\"ครอบตัดขวา\"\nBasic.TransformWindow.CropTop=\"ครอบตัดด้านบน\"\nBasic.TransformWindow.CropBottom=\"ครอบตัดด้านล่าง\"\nBasic.TransformWindow.Alignment.TopLeft=\"ด้านบนซ้าย\"\nBasic.TransformWindow.Alignment.TopCenter=\"ด้านบนกลาง\"\nBasic.TransformWindow.Alignment.TopRight=\"ด้านบนขวา\"\nBasic.TransformWindow.Alignment.CenterLeft=\"ตรงกลางด้านซ้าย\"\nBasic.TransformWindow.Alignment.Center=\"ตรงกลาง\"\nBasic.TransformWindow.Alignment.CenterRight=\"ตรงกลางด้านขวา\"\nBasic.TransformWindow.Alignment.BottomLeft=\"ด้านล่างซ้าย\"\nBasic.TransformWindow.Alignment.BottomCenter=\"ตรงกลางด้านล่าง\"\nBasic.TransformWindow.Alignment.BottomRight=\"ขวาล่าง\"\nBasic.TransformWindow.BoundsType.None=\"ไม่มีขอบ\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"สำหรับขนาดสูงสุดเท่านั้น\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"ปรับมาตราส่วนเท่ากับขอบเขตใน\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"ปรับมาตราส่วนเท่ากับขอบเขตนอก\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"ปรับมาตราส่วนเท่ากับความกว้างขอบเขต\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"ปรับมาตราส่วนเท่ากับความสูงขอบเขต\"\nBasic.TransformWindow.BoundsType.Stretch=\"ปรับพอดีกับขอบเขต\"\nBasic.TransformWindow.Title=\"แก้ไขการปรับรูปร่างสำหรับ '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"ไม่ได้เลือกแหล่ง\"\nBasic.Main.AddSourceHelp.Title=\"เพิ่มแหล่งไม่ได้\"\nBasic.Main.AddSourceHelp.Text=\"คุณต้องมีอย่างน้อย 1 ฉากเพื่อเพิ่มแหล่ง\"\nBasic.Main.Scenes=\"ฉาก\"\nBasic.Main.Sources=\"แหล่ง\"\nBasic.Main.Source=\"แหล่ง\"\nBasic.Main.Controls=\"ตัวควบคุม\"\nBasic.Main.PreparingStream=\"กำลังเตรียมพร้อม...\"\nBasic.Main.Connecting=\"กำลังเชื่อมต่อ...\"\nBasic.Main.StartRecording=\"เริ่มบันทึก\"\nBasic.Main.StartReplayBuffer=\"เริ่มรีเพลย์บัฟเฟอร์\"\nBasic.Main.SaveReplay=\"บันทึกการเล่นซ้ำ\"\nBasic.Main.StartStreaming=\"เริ่มสตรีมมิ่ง\"\nBasic.Main.StartBroadcast=\"ถ่ายทอดสด\"\nBasic.Main.StartVirtualCam=\"เริ่มกล้องเสมือนจริง\"\nBasic.Main.StopRecording=\"หยุดบันทึก\"\nBasic.Main.PauseRecording=\"หยุดการบันทึกชั่วคราว\"\nBasic.Main.UnpauseRecording=\"เริ่มการบันทึกต่อ\"\nBasic.Main.SplitFile=\"แยกไฟล์บันทึก\"\nBasic.Main.StoppingRecording=\"กำลังหยุดการบันทึก...\"\nBasic.Main.StopReplayBuffer=\"หยุดรีเพลย์บัฟเฟอร์\"\nBasic.Main.StoppingReplayBuffer=\"กำลังหยุดรีเพลย์บัฟเฟอร์...\"\nBasic.Main.SetupBroadcast=\"จัดการการถ่ายทอด\"\nBasic.Main.StopStreaming=\"หยุดสตรีมมิ่ง\"\nBasic.Main.StopBroadcast=\"สิ้นสุดการถ่ายทอด\"\nBasic.Main.AutoStopEnabled=\"(หยุดอัตโนมัติ)\"\nBasic.Main.StoppingStreaming=\"กำลังหยุดการสตรีม...\"\nBasic.Main.ForceStopStreaming=\"หยุดสตรีม (ละทิ้งการหน่วงเวลา)\"\nBasic.Main.ShowContextBar=\"แสดงแถบเครื่องมือแหล่ง\"\nBasic.Main.HideContextBar=\"ซ่อนแถบเครื่องมือแหล่ง\"\nBasic.Main.StopVirtualCam=\"หยุดกล้องเสมือนจริง\"\nBasic.Main.Group=\"กลุ่ม %1\"\nBasic.Main.GroupItems=\"จัดกลุ่มรายการที่เลือก\"\nBasic.Main.Ungroup=\"นำออกจากกลุ่ม\"\nBasic.Main.GridMode=\"โหมดแบบตาราง\"\nBasic.Main.ListMode=\"โหมดแบบรายการ\"\nBasic.Main.VirtualCamConfig=\"กำหนดค่ากล้องเสมือนจริง\"\nBasic.VCam.VirtualCamera=\"กล้องเสมือนจริง\"\nBasic.VCam.OutputType=\"โหมดเอาต์พุต\"\nBasic.VCam.OutputSelection=\"เลือกเอาต์พุต\"\nBasic.VCam.OutputType.Program=\"โปรแกรม(ค่าเริ่มต้น)\"\nBasic.VCam.OutputSelection.NoSelection=\"เอ้าต์พุตนี้ยังไม่ได้เลือก\"\nBasic.VCam.RestartWarning=\"กล้องเสมือนจริงจะถูกรีสตาร์ทเพื่อให้เข้ากับการตั้งค่านี้\"\nBasic.MainMenu.File=\"ไฟล์ (&F)\"\nBasic.MainMenu.File.Export=\"ส่งออก (&E)\"\nBasic.MainMenu.File.Import=\"นําเข้า (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"หยุดการบันทึก\"\nBasic.MainMenu.File.Remux=\"กำลังบันทึก Remux\"\nBasic.MainMenu.File.Settings=\"ตั้งค่า (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"แสดงโฟลเดอร์การตั้งค่า\"\nBasic.MainMenu.File.ShowProfileFolder=\"แสดงโฟลเดอร์โปรไฟล์\"\nBasic.MainMenu.File.ShowMissingFiles=\"ตรวจหาไฟล์ที่ขาดไป\"\nBasic.MainMenu.File.Exit=\"ออก (&X)\"\nBasic.MainMenu.Edit=\"แก้ไข (&E)\"\nBasic.MainMenu.Edit.Undo=\"&เลิกทำ\"\nBasic.MainMenu.Edit.Redo=\"&ทำซ้ำ\"\nBasic.MainMenu.Edit.LockPreview=\"&ล็อกหน้าตัวอย่าง\"\nBasic.MainMenu.Edit.Scale=\"มาตราส่วนการแสดงตัวอย่าง(&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"ปรับมาตราส่วนเท่ากับหน้าต่าง\"\nBasic.MainMenu.Edit.Scale.Canvas=\"แคนวาส (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"เอาต์พุต (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"ปรับสัดส่วน (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"ซูมเข้า\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"ซูมออก\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"รีเซ็ตการซูม\"\nBasic.MainMenu.Edit.Transform=\"แปลง(&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"แก้ไขการปรับรูปร่าง(&E)...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"คัดลอกการปรับรูปร่าง\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"วางการปรับรูปร่าง\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"ตั้งค่าการปรับรูปร่างใหม่(&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"หมุน 90 องศา ตามเข็มนาฬิกา\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"หมุน 90 องศา ทวนเข็มนาฬิกา\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"หมุน 180 องศา\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"พลิกแนวนอน(&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"พลิกแนวตั้ง(&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"&พอดีจอ\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"&ขยายเต็มจอ\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"&วางกึ่งกลางจอ\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"กึ่งกลางแนวตั้ง\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"กึ่งกลางแนวนอน\"\nBasic.MainMenu.Edit.Order=\"&ลำดับ\"\nBasic.MainMenu.Edit.Order.MoveUp=\"ย้าย &ขึ้น\"\nBasic.MainMenu.Edit.Order.MoveDown=\"ย้าย &ลง\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"ย้ายขึ้นข้างบน\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"ย้ายลงข้างล่าง\"\nBasic.MainMenu.Edit.AdvAudio=\"&คุณสมบัติเสียงขั้นสูง\"\nBasic.MainMenu.View=\"&มุมมอง\"\nBasic.MainMenu.View.Toolbars=\"แถบเครื่องมือ\"\nBasic.MainMenu.View.ListboxToolbars=\"แถบเครื่องมือถาดงาน\"\nBasic.MainMenu.View.ContextBar=\"แถบเครื่องมือแหล่ง\"\nBasic.MainMenu.View.SourceIcons=\"ไ&อคอนแหล่ง\"\nBasic.MainMenu.View.StatusBar=\"แถบสถานะ\"\nBasic.MainMenu.View.Fullscreen.Interface=\"อินเตอร์เฟซแบบเต็มจอ\"\nBasic.MainMenu.View.ResetUI=\"&รีเซ็ต UI\"\nBasic.MainMenu.View.AlwaysOnTop=\"&อยู่ด้านบนเสมอ\"\nBasic.MainMenu.View.SceneListMode=\"โหมดซีนแบบรายการ\"\nBasic.MainMenu.Docks=\"&ถาดงาน\"\nBasic.MainMenu.Docks.ResetDocks=\"&รีเซ็ตถาดงาน\"\nBasic.MainMenu.Docks.LockDocks=\"&ล็อกถาดงาน\"\nBasic.MainMenu.Docks.SideDocks=\"ถาดงานแบบเ&ต็มความสูง\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&ถาดงานเบราว์เซอร์แบบปรับแต่งเอง...\"\nBasic.MainMenu.Import=\"นำเข้า...\"\nBasic.MainMenu.Export=\"ส่งออก...\"\nBasic.MainMenu.SceneCollection=\"&เพิ่มคอลเลกชัน\"\nBasic.MainMenu.Profile=\"&โปรไฟล์\"\nBasic.MainMenu.Profile.Import=\"นำเข้าโปรไฟล์\"\nBasic.MainMenu.Profile.Export=\"ส่งออกโปรไฟล์\"\nBasic.MainMenu.SceneCollection.Import=\"เพิ่มคอลเลกชัน\"\nBasic.MainMenu.SceneCollection.Export=\"เปลี่ยนชื่อคอลเลกชันฉาก\"\nBasic.MainMenu.SceneCollection.Remigrate=\"คืนค่าความละเอียดพื้นที่หลัก\"\nBasic.MainMenu.SceneCollection.Migrate=\"ตั้งค่าความละเอียดพื้นที่หลัก\"\nBasic.MainMenu.Profile.Exists=\"มีโปรไฟล์อยู่แล้ว\"\nBasic.MainMenu.Tools=\"&เครื่องมือ\"\nBasic.MainMenu.Help=\"&ช่วยเหลือ\"\nBasic.MainMenu.Help.HelpPortal=\"Help\"\nBasic.MainMenu.Help.Website=\"เยี่ยมชมเว็บไซต์\"\nBasic.MainMenu.Help.Discord=\"เข้าเซิร์ฟเวอร์ Discord\"\nBasic.MainMenu.Help.WhatsNew=\"มีอะไรใหม่บ้าง\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"&แสดง Log Files\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"อัปโหลดไฟล์ Log &ปัจจุบัน\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"อัปโหลดไฟล์ Log &ก่อนหน้า\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"&ดู Log ปัจจุบัน\"\nBasic.MainMenu.Help.ReleaseNotes=\"บันทึกประจำรุ่น\"\nBasic.MainMenu.Help.CheckForUpdates=\"ตรวจสอบการอัพเดต\"\nBasic.MainMenu.Help.Repair=\"ตรวจสอบความสมบูรณ์ของไฟล์\"\nBasic.MainMenu.Help.RestartSafeMode=\"รีสตาร์ทในเซฟโหมด\"\nBasic.MainMenu.Help.RestartNormal=\"รีสตาร์ทในโหมดปกติ\"\nBasic.MainMenu.Help.CrashLogs=\"รายงานความผิดพลาด\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"&แสดงรายงานความผิดพลาด\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"อัปโหลดรายงานความผิดพลาด&ก่อนหน้า\"\nBasic.MainMenu.Help.About=\"&เกี่ยวกับเรา\"\nBasic.Settings.ProgramRestart=\"ต้องรีสตาร์ทโปรแกรมเพื่อให้การตั้งค่าเหล่านี้มีผล\"\nBasic.Settings.ConfirmTitle=\"ยืนยันการเปลี่ยนแปลง\"\nBasic.Settings.Confirm=\"คุณมีการเปลี่ยนแปลงที่ยังไม่ได้บันทึก บันทึกการเปลี่ยนแปลง?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 กำลังควบคุมการตั้งค่าสตรีมของคุณบางอย่าง\"\nBasic.Settings.General=\"ทั่วไป\"\nBasic.Settings.General.Language=\"ภาษา\"\nBasic.Settings.General.Updater=\"อัปเดต\"\nBasic.Settings.General.UpdateChannel=\"ช่องทางอัปเดต\"\nBasic.Settings.General.UpdateChannelDisabled=\"(ปิดการใช้งาน)\"\nBasic.Settings.General.UpdateChannelDefault=\"(ค่าเริ่มต้น)\"\nBasic.Settings.General.EnableAutoUpdates=\"ตรวจสอบการอัปเดตโดยอัตโนมัติเมื่อเริ่มต้นระบบ\"\nBasic.Settings.General.OpenStatsOnStartup=\"เปิดกล่องโต้ตอบสถิติเมื่อเริ่มต้น\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"ซ่อนหน้าต่าง OBS จากการจับภาพหน้าจอ\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"ซ่อนหน้าต่าง OBS Studio ที่ไม่อยู่ในจอโปรเจคเตอร์ทั้งหมดไม่ให้ถูกจับภาพได้โดย OBS\\nและให้มีผลกับแอพพลิเคชันอื่นด้วย\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"การเปิดใช้ตัวเลือกนี้จะซ่อนหน้าต่าง OBS Studio ที่ไม่อยู่ในจอโปรเจคเตอร์ทั้งหมดไม่ให้ถูกจับภาพได้โดย OBS และให้มีผลกับแอพพลิเคชันอื่น เช่น การประชุม การแชร์หน้าจอ การรองรับระยะไกล การจับภาพหน้าจอ และซอฟต์แวร์จับภาพอื่นๆ ด้วย\"\nBasic.Settings.General.WarnBeforeStartingStream=\"แสดงกล่องโต้ตอบการยืนยันเมื่อเริ่มสตรีม\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"แสดหน้าต่างยืนยันเมื่อหยุดสตรีม\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"แสดหน้าต่างยืนยันเมื่อหยุดบันทึก\"\nBasic.Settings.General.Projectors=\"โปรเจกเตอร์\"\nBasic.Settings.General.HideProjectorCursor=\"ซ่อนเคอร์เซอร์\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"ให้โปรเจคเตอร์อยู่บนสุดตลอดเวลา\"\nBasic.Settings.General.Snapping=\"การแนบติดการจัดวางแหล่ง\"\nBasic.Settings.General.ScreenSnapping=\"แนบแหล่งติดเข้ากับขอบของหน้าจอ\"\nBasic.Settings.General.CenterSnapping=\"แนบแหล่งติดเข้ากับศูนย์กลางตามแนวนอนและแนวตั้ง\"\nBasic.Settings.General.SourceSnapping=\"แนบแหล่งติดกับแหล่งอื่นๆ\"\nBasic.Settings.General.SnapDistance=\"ความไวของการสแนป\"\nBasic.Settings.General.SpacingHelpers=\"แสดงเส้นนำแนวการจัดเรียงพิกเซล\"\nBasic.Settings.General.RecordWhenStreaming=\"บันทึกภาพอัตโนมัติขณะสตรีม\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"บันทึกภาพต่อไปหลังหยุดสตรีมแล้ว\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"เริ่มบัฟเฟอร์เล่นซ้ำอัตโนมัติขณะสตรีม\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"ให้บัฟเฟอร์เล่นซ้ำทำงานต่อไปหลังหยุดสตรีมแล้ว\"\nBasic.Settings.General.SysTray=\"พื้นที่สถานะ\"\nBasic.Settings.General.SysTrayWhenStarted=\"ย่อลงพื้นที่สถานะหลังจากเริ่มทำงาน\"\nBasic.Settings.General.SystemTrayHideMinimize=\"ย่อลงพื้นทีสถานะแทนแถบงาน\"\nBasic.Settings.General.SaveProjectors=\"บันทึกโปรเจคเตอร์เมื่อออก\"\nBasic.Settings.General.CloseExistingProjectors=\"จำกัดโปรเจคเตอร์เต็มจอเพียงตัวเดียวต่อหนึ่งหน้าจอ\"\nBasic.Settings.General.Preview=\"ตัวอย่าง\"\nBasic.Settings.General.OverflowHidden=\"ซ่อนส่วนที่ล้น\"\nBasic.Settings.General.OverflowAlwaysVisible=\"แสดงส่วนที่ล้นตลอด\"\nBasic.Settings.General.OverflowSelectionHidden=\"แสดงส่วนที่ล้นแม้จะมองไม่เห็นแหล่ง\"\nBasic.Settings.General.Importers=\"ตัวนำเข้า\"\nBasic.Settings.General.AutomaticCollectionSearch=\"ค้นหาคอลเลคชันฉากในตำแหน่งต่างๆ ที่ทราบเมื่อนำเข้า\"\nBasic.Settings.General.SwitchOnDoubleClick=\"เปลี่ยนฉากเมื่อดับเบิลคลิก\"\nBasic.Settings.General.StudioPortraitLayout=\"การจัดวางแนวตั้ง/แนวตั้ง\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"แสดงป้ายชื่อตัวอย่าง/โปรแกรม\"\nBasic.Settings.General.Multiview=\"หลายมุมมอง\"\nBasic.Settings.General.Multiview.MouseSwitch=\"คลิกเพื่อสลับระหว่างฉาก\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"แสดงชื่อฉาก\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"วาดพื้นที่มาตรฐาน (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"การรูปแบบหลายมุมมอง\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"แนวนอน, ด้านบน (8 ฉาก)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"แนวนอน, ด้านบน (8 ฉาก)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"แนวตั้ง, ซ้าย (8 ฉาก)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"แนวตั้ง, ขวา (8 ฉาก)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"แนวนอน, ด้านบน (18 ฉาก)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"แนวนอน บน (24 ฉาก)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"เฉพาะฉากเท่านั้น (4 ฉาก)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"เฉพาะฉากเท่านั้น (9 ฉาก)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"เฉพาะฉากเท่านั้น (16 ฉาก)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"เฉพาะฉากเท่านั้น (25 ฉาก)\"\nBasic.Settings.General.ChannelName.stable=\"เสถียร\"\nBasic.Settings.General.ChannelDescription.stable=\"รุ่นเสถียรล่าสุด\"\nBasic.Settings.General.ChannelName.beta=\"รุ่นเบต้า / Release Candidate\"\nBasic.Settings.General.ChannelDescription.beta=\"รุ่นก่อนปล่อยที่อาจไม่เสถียร\"\nBasic.Settings.Appearance=\"รูปร่าง\"\nBasic.Settings.Appearance.General=\"ทั่วไป\"\nBasic.Settings.Appearance.General.Theme=\"ธีม\"\nBasic.Settings.Appearance.General.Variant=\"สไตล์\"\nBasic.Settings.Appearance.General.NoVariant=\"ไม่มีสไตล์ที่พร้อมใช้งาน\"\nBasic.Settings.Appearance.FontScale=\"ขนาดตัวอักษร\"\nBasic.Settings.Appearance.Density=\"ความหนา\"\nBasic.Settings.Appearance.Classic=\"คลาสสิก\"\nBasic.Settings.Appearance.Compact=\"กะทัดรัด\"\nBasic.Settings.Appearance.Normal=\"ปกติ\"\nBasic.Settings.Appearance.Comfortable=\"สะดวกสบาย\"\nBasic.Settings.Appearance.OptionsWarning=\"ตัวเลือกการแสดงผลบางอย่างยังไม่พร้อมใช้งานสำหรับรูปแบบนี้\"\nBasic.Settings.Stream=\"สตรีม\"\nBasic.Settings.Stream.Destination=\"ปลายทาง\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"ใช้การยืนยันตัวตน\"\nBasic.Settings.Stream.Custom.Username=\"ชื่อผู้ใช้\"\nBasic.Settings.Stream.Custom.Password=\"รหัสผ่าน\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: ป้อน srp_username\\nRTMP: ป้อนชื่อผู้ใช้\\nSRT: ไม่ได้ใช้\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: ใส่ srp_password\\nRTMP: ใส่รหัสผ่าน\\nSRT: ใส่วลีรหัสผ่านของการเข้ารหัสลับ\"\nBasic.Settings.Stream.BandwidthTestMode=\"เปิดใช้งานโหมดทดสอบแบนด์วิดท์\"\nBasic.Settings.Stream.TTVAddon=\"ส่วนเสริมแชท Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"ไม่ใช้\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV และ FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"ไม่มีการตั้งค่าสตรีม\"\nBasic.Settings.Stream.StreamSettingsWarning=\"เปิดการตั้งค่า\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"ไม่มี URL และรหัสสตรีม\\n\\nเปิดการตั้งค่าเพื่อป้อน URL และรหัสสตรีมในแท็บ 'สตรีม'\"\nBasic.Settings.Stream.MissingUrl=\"ไม่มี URL ของสตรีม\\n\\nเปิดการตั้งค่าเพื่อป้อน URL ในแท็บ \\\"สตรีม\\\"\"\nBasic.Settings.Stream.MissingStreamKey=\"รหัสสตรีมหายไป\\n\\nเปิดการตั้งค่าเพื่อป้อนรหัสสตรีมในแท็บ 'สตรีม'\"\nBasic.Settings.Stream.IgnoreRecommended=\"ละเว้นคำแนะนำการตั้งค่าบริการสตรีมมิ่ง\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"แทนที่การตั้งค่าที่แนะนำ\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"คำเตือน: การเพิกเฉยต่อข้อจำกัดของบริการอาจส่งผลให้คุณภาพการสตรีมลดลงหรือทำให้คุณไม่สามารถสตรีมได้\\n\\nดำเนินการต่อหรือไม่\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"อัตราบิตสูงสุดของวิดีโอ: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"อัตราบิตสูงสุดของวเสียง: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"ความละเอียดสูงสุด: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"FPS สูงสุด: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"ระบุเซิร์ฟเวอร์กำหนดเอง...\"\nBasic.Settings.Stream.ServiceCustomServer=\"เซิร์ฟเวอร์กำหนดเอง\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"เปิดใช้ %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"แบนด์วิดธ์สตรีมมิ่งสูงสุด\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"อัตโนมัติ\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"จำนวนแทร็กวิดีโอสูงสุด\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"อัตโนมัติ\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"เปิดใช้การดัมพ์สตรีมลงใน FLV (ใช้การตั้งค่าไฟล์สิ่งบันทึกอย่างง่าย)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"การเขียนทับค่ากำหนด (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"เปิดใช้การเขียนทับค่ากำหนด\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"วิดีโอหลายแทร็ก\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"พื้นที่ทำงานเพิ่มเติม\"\nBasic.Settings.Stream.AdvancedOptions=\"ตัวเลือกขั้นสูง\"\nBasic.Settings.Output=\"ผลลัพธ์\"\nBasic.Settings.Output.Format=\"รูปแบบการบันทึก\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Fragmented MOV จะเขียนสิ่งบันทึกโดยแบ่งออกเป็นก้อนๆ และไม่ต้องการการประมวลผลให้เสร็จสมบูรณ์เหมือนไฟล์ MOV แบบดั้งเดิม\\nซึ่งช่วยรับประกันว่าไฟล์จะยังคงสามารถเปิดเล่นได้ แม้ว่าการเขียนข้อมูลลงดิสก์จะถูกขัดจังหวะก็ตาม ตัวอย่างเช่น เกิดจาก BSOD หรือไฟตัด\\n\\nแต่อาจจะเข้ากันไม่ได้กับโปรแกรมเล่นและโปรแกรมแก้ไขทั้งหมด ให้ใช้ ไฟล์ → รีมักซ์สิ่งบันทึก เพื่อทำการแปลงไฟล์ให้เป็นรูปแบบที่เข้ากันได้ดีกว่านี้หากจำเป็น\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Fragmented MP4 จะเขียนสิ่งบันทึกโดยแบ่งออกเป็นก้อนๆ และไม่ต้องการการประมวลผลให้เสร็จสมบูรณ์เหมือนไฟล์ MP4 แบบดั้งเดิม\\nซึ่งช่วยรับประกันว่าไฟล์จะยังคงสามารถเปิดเล่นได้ แม้ว่าการเขียนข้อมูลลงดิสก์จะถูกขัดจังหวะก็ตาม ตัวอย่างเช่น เกิดจาก BSOD หรือไฟตัด\\n\\nแต่อาจจะเข้ากันไม่ได้กับโปรแกรมเล่นและโปรแกรมแก้ไขทั้งหมด ให้ใช้ ไฟล์ → รีมักซ์สิ่งบันทึก เพื่อทำการแปลงไฟล์ให้เป็นรูปแบบที่เข้ากันได้ดีกว่านี้หากจำเป็น\"\nBasic.Settings.Output.Encoder.Video=\"ตัวเข้ารหัสข้อมูลวิดีโอ\"\nBasic.Settings.Output.Encoder.Audio=\"ตัวเข้ารหัสข้อมูลเสียง\"\nBasic.Settings.Output.SelectDirectory=\"เลือกตำแหน่งไดเรกทอรี\"\nBasic.Settings.Output.DynamicBitrate=\"ปรับเปลี่ยนอัตราบิตตามความหนาแน่นของการส่งข้อมูล\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"ปรับเปลี่ยนอัตราบิตตามความหนาแน่นของการส่งข้อมูล (ทดลอง)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"ปรับเปลี่ยนอัตราบิตตามความหนาแน่นของการส่งข้อมูลแทนที่จะใช้วิธีดรอปเฟรม\\n\\nอาจทำให้ผู้ชมรับภาพได้ล่าช้าหากเกิดความหนาแน่นสูงทันทีทันใด\\nและจะต้องใช้เวลาในการกู้คืนสักครู่เมื่อค่าอัตราบิตลดลง\\n\\nรองรับเฉพาะ RTMP เท่านั้น\"\nBasic.Settings.Output.Mode=\"โหมดเอาต์พุต\"\nBasic.Settings.Output.Mode.Simple=\"เรียบง่าย\"\nBasic.Settings.Output.Mode.Adv=\"ขั้นสูง\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg เอาต์พุต\"\nBasic.Settings.Output.UseReplayBuffer=\"เปิดใช้บัฟเฟอร์เล่นซ้ำ\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"เวลาเล่นซ้ำสูงสุด\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"หน่วยความจำสูงสุด\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"การใช้หน่วยความจำโดยประมาณ: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"คำเตือน: การใช้หน่วยความจำโดยประมาณที่ %1 MiB มากกว่าขีดสูงสุดที่แนะนำซึ่งอยู่ที่ %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"ไม่สามารถคำนวณการใช้หน่วยความจำโดยประมาณได้ โปรดกำหนดขีดจำกัดหน่วยความจำสูงสุด\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"ส่วนขึ้นต้นชื่อไฟล์สำหรับบัฟเฟอร์เล่นซ้ำ\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"ส่วนต่อท้าย\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"รีเพลย์บัฟเฟอร์ไม่สามารถใช้ได้เมื่อตั้งชนิดสิ่งบันทึกเป็น เอาต์พุตกำหนดเอง (FFmpeg)\"\nBasic.Settings.Output.Simple.SavePath=\"ประเภทการบันทึก\"\nBasic.Settings.Output.Simple.RecordingQuality=\"คุณภาพการบันทึก\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"เหมือนกับสตรีม\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"คุณภาพสูง ขนาดไฟล์ปานกลาง\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"คุณภาพสูงมาก ขนาดไฟล์ใหญ่\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"คุณภาพแบบไม่สูญเสีย ขนาดไฟล์ใหญ่มหาศาล\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"คำเตือน: อัตราบิตของข้อมูลวิดีโอที่กำลังสตรีมจะถูกตั้งเป็น %1 ซึ่งเป็นขีดจำกัดสูงสุดสำหรับบริการสตรีมมิงปัจจุบัน\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"คำเตือน: อัตราบิตของข้อมูลเสียงที่กำลังสตรีมจะถูกตั้งเป็น %1 ซึ่งเป็นขีดจำกัดสูงสุดสำหรับบริการสตรีมมิงปัจจุบัน\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"คำเตือน: ไม่สามารถหยุดการบันทึกต่างๆ ชั่วคราวได้หากคุณภาพการบันทึกถูกตั้งเป็น \\\"เหมือนกับสตรีม\\\"\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"คำเตือน: รูปแบบการบันทึกที่เลือกปัจจุบันเข้ากันไม่ได้กับตัวเข้ารหัสสตรีมที่เลือก\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"คำเตือน: การบันทึกด้วยตัวเข้ารหัสแบบซอฟต์แวร์ที่คุณภาพต่างจากสตรีมจะต้องใช้ CPU เพิ่มเติมหากคุณทำการสตรีมและบันทึกไปพร้อมกัน\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"คำเตือน: คุณภาพแบบ Lossless สร้างไฟล์ขนาดใหญ่โตมโหฬาร! คุณภาพแบบ Lossless ใช้พื้นที่ดิสก์สูงถึง 7 กิกะไบต์ต่อนาทีที่ความละเอียดและเฟรมเรทสูง การใช้ Lossless ไม่แนะนำสำหรับการบันทึกยาว ๆ เว้นแต่คุณจะมีพื้นที่ดิสก์เหลือเฟือ บัฟเฟอร์สำหรับการย้อนกลับ (Replay buffer) จะไม่สามารถใช้งานได้เมื่อใช้คุณภาพแบบ Lossless\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"คุณแน่ใจหรือไม่ว่าต้องการใช้คุณภาพแบบไม่สูญเสีย?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"คำเตือนคุณภาพแบบไม่สูญเสีย!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"ซอฟต์แวร์ (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"ฮาร์ดแวร์ (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"ฮาร์ดแวร์ (QSV)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"ฮาร์ดแวร์ (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"ฮาร์ดแวร์ (QSV)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"ฮาร์ดแวร์ (QSV)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"ฮาร์ดแวร์ (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"ฮาร์ดแวร์ (QSV)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"ฮาร์ดแวร์ (QSV)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"ฮาร์ดแวร์ (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"ฮาร์ดแวร์ (QSV)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"ซอฟต์แวร์ (x264 การใช้งาน CPU ต่ำ, เพิ่มขนาดไฟล์)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (ค่าเริ่มต้น)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"แทร็ก VOD ใน Twitch (ใช้แทร็ก 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"แทร็กเสียง\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"ความละเอียด/อัตราเฟรมเข้ากันไม่ได้\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"บริการสตรีมมิงนี้ไม่รองรับความละเอียดและ/หรืออัตราเฟรมของเอาต์พุตปัจจุบันของคุณ ค่าเหล่านั้นจะถูกเปลี่ยนเป็นค่าที่เข้ากันได้ซึ่งใกล้เคียงที่สุด:\\n\\n%1\\n\\nคุณต้องการทำต่อไปหรือไม่?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"ความละเอียด: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"ตัวเข้ารหัสเข้ากันไม่ได้\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"บริการสตรีมมิง \\\"%1\\\" ไม่รองรับตัวเข้ารหัส \\\"%2\\\" ตัวเข้ารหัสดังกล่าวจะถูกเปลี่ยนเป็น \\\"%3\\\"\\n\\nคุณต้องการทำต่อไปหรือไม่?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"บริการสตรีมมิง \\\"%1\\\" ไม่รองรับตัวเข้ารหัส \\\"%2\\\" และ \\\"%3\\\" ตัวเข้ารหัสเหล่านี้จะถูกเปลี่ยนเป็น \\\"%4\\\" และ \\\"%5\\\"\\n\\nคุณต้องการทำต่อไปหรือไม่?\"\nBasic.Settings.Output.VideoBitrate=\"อัตราบิตของวิดีโอ\"\nBasic.Settings.Output.AudioBitrate=\"อัตราบิตของเสียง\"\nBasic.Settings.Output.Reconnect=\"เชื่อมต่อใหม่โดยอัตโนมัติ\"\nBasic.Settings.Output.RetryDelay=\"หน่วงเวลาลองอีกครั้ง\"\nBasic.Settings.Output.MaxRetries=\"จำนวนรีไทร์สูงสุด\"\nBasic.Settings.Output.Advanced=\"เปิดใช้งานการตั้งค่าตัวเข้ารหัสแบบกำหนดเอง (ขั้นสูง)\"\nBasic.Settings.Output.EncoderPreset=\"ตัวเข้ารหัสที่ถูกตั้งไว้ล่วงหน้า\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (การใช้งาน CPU ต่ำ คุณภาพต่ำสุด)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (ค่าเริ่มต้น) (การใช้งาน CPU ปานกลาง คุณภาพมาตรฐาน)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (การใช้งาน CPU สูง คุณภาพสูง)\"\nBasic.Settings.Output.CustomEncoderSettings=\"การตั้งค่าตัวเข้ารหัสแบบกำหนดเอง\"\nBasic.Settings.Output.CustomMuxerSettings=\"การตั้งค่า Muxer แบบกำหนดเอง\"\nBasic.Settings.Output.NoSpaceFileName=\"สร้างชื่อไฟล์โดยไม่ต้องเว้นวรรค\"\nBasic.Settings.Output.Adv.Rescale=\"รีสเกลเอาต์พุต\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"ปิดใช้งาน\"\nBasic.Settings.Output.Adv.AudioTrack=\"แทร็กเสียง\"\nBasic.Settings.Output.Adv.Streaming=\"การสตรีม\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"ตั้งค่าการสตรีม\"\nBasic.Settings.Output.Adv.Audio.Track1=\"แทร็ก 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"แทร็ก 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"แทร็ก 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"แทร็ก 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"แทร็ก 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"แทร็ก 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"แทร็ก VOD ใน Twitch\"\nBasic.Settings.Output.Adv.Encoder=\"ตั้งค่าตัวเข้ารหัส\"\nBasic.Settings.Output.Adv.Recording=\"การบันทึก\"\nBasic.Settings.Output.Adv.Recording.Settings=\"ตั้งค่าการบันทึก\"\nBasic.Settings.Output.Adv.Recording.RecType=\"ประเภทการบันทึก\"\nBasic.Settings.Output.Adv.Recording.Type=\"รูปแบบ\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"ทั่วไป\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"เอาต์พุตกำหนดเอง (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(ใช้ตัวเข้ารหัสสตรีม)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"รูปแบบชื่อไฟล์\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"เขียนทับถ้าไฟล์มีอยู่\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"เอาต์พุตกำหนดเอง (FFmpeg) ไม่มีการป้องกันใดๆ\\nโปรดพิจารณาใช้การบันทึกชนิด \\\"มาตรฐาน\\\"\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"ชนิดเอาต์พุต FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"เอาต์พุตไปที่ URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"เอาต์พุตไปที่ไฟล์\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"รูปแบบการบันทึกที่ใช้บ่อย\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"ไฟล์ทั้งหมด\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"เส้นทางไฟล์หรือ URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"รูปแบบคอนเทนเนอร์\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"เสียง\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"วิดีโอ\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"รูปแบบเริ่มต้น\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"คำอธิบายรูปแบบคอนเทนเนอร์\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"ตัวลงรหัสเสียง/วิดีโอถูกคาดเดาจากเส้นทางไฟล์หรือ URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"ตัวเข้ารหัสเริ่มต้น\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"ปิดตัวเข้ารหัส\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"ตัวเข้ารหัสวิดีโอ\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"การตั้งค่าตัวเข้ารหัสวิดีโอ (ถ้ามี)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"ตัวเข้ารหัสเสียง\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"การตั้งค่าตัวเข้ารหัสเสียง (ถ้ามี)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"การตั้งค่า Muxer (ถ้ามี)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"ช่วงคีย์เฟรม (เฟรม)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"แสดงตัวลงรหัสทั้งหมด (แม้อาจจะเข้ากันไม่ได้ก็ตาม)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"ตั้งค่า FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"การแบ่งไฟล์อัตโนมัติ\"\nBasic.Settings.Output.SplitFile.TypeTime=\"แบ่งตามเวลา\"\nBasic.Settings.Output.SplitFile.TypeSize=\"แบ่งตามขนาด\"\nBasic.Settings.Output.SplitFile.TypeManual=\"แบ่งด้วยตนเองเท่านั้น\"\nBasic.Settings.Output.SplitFile.Time=\"เวลาที่แบ่ง\"\nBasic.Settings.Output.SplitFile.Size=\"ขนาดที่แบ่ง\"\nScreenshot=\"ถ่ายภาพหน้าจอของเอาต์พุต\"\nScreenshot.SourceHotkey=\"ถ่ายภาพหน้าจอของแหล่งที่เลือก\"\nScreenshot.StudioProgram=\"ภาพหน้าจอ (โปรแกรม)\"\nScreenshot.Preview=\"ภาพหน้าจอ (ตัวอย่าง)\"\nScreenshot.Scene=\"ภาพหน้าจอ (ฉาก)\"\nScreenshot.Source=\"ภาพหน้าจอ (แหล่ง)\"\nFilenameFormatting.TT.CCYY=\"ปี, สี่หลัก\"\nFilenameFormatting.TT.YY=\"ปี, สองหลักสุดท้าย (00-99)\"\nFilenameFormatting.TT.MM=\"เดือนในรูปแบบเลขฐานสิบ (01-12)\"\nFilenameFormatting.TT.DD=\"วันของเดือน, เติมเลขศูนย์ (01-31)\"\nFilenameFormatting.TT.hh=\"ชั่วโมงในรูปแบบ 24 ชม. (00-23)\"\nFilenameFormatting.TT.mm=\"นาที (00-59)\"\nFilenameFormatting.TT.ss=\"วินาที (00-59)\"\nFilenameFormatting.TT.Percent=\"เครื่องหมาย %\"\nFilenameFormatting.TT.a=\"ชื่อวันในสัปดาห์แบบย่อ\"\nFilenameFormatting.TT.A=\"ชื่อวันในสัปดาห์แบบเต็ม\"\nFilenameFormatting.TT.b=\"ชื่อเดือนแบบย่อ\"\nFilenameFormatting.TT.B=\"ชื่อเดือนแบบเต็ม\"\nFilenameFormatting.TT.d=\"วันของเดือน, เติมเลขศูนย์ (01-31)\"\nFilenameFormatting.TT.H=\"ชั่วโมงในรูปแบบ 24 ชม. (00-23)\"\nFilenameFormatting.TT.I=\"ชั่วโมงในรูปแบบ 12 ชม. (01-12)\"\nFilenameFormatting.TT.m=\"เดือนในรูปแบบเลขฐานสิบ (01-12)\"\nFilenameFormatting.TT.M=\"นาที (00-59)\"\nFilenameFormatting.TT.p=\"คำเรียก AM หรือ PM\"\nFilenameFormatting.TT.s=\"เวลาในหน่วยวินาทีตั้งแต่ UNIX epoch\"\nFilenameFormatting.TT.S=\"วินาที (00-59)\"\nFilenameFormatting.TT.y=\"ปี, สองหลักสุดท้าย (00-99)\"\nFilenameFormatting.TT.Y=\"ปี\"\nFilenameFormatting.TT.z=\"ค่าออฟเซต ISO 8601 จาก UTC ในเขตเวลา\"\nFilenameFormatting.TT.Z=\"ชื่อเต็มหรือชื่อย่อเขตเวลา\"\nFilenameFormatting.TT.FPS=\"จำนวนเฟรมต่อวินาที\"\nFilenameFormatting.TT.CRES=\"ความละเอียดของพื้นที่หลัก (แคนวาส)\"\nFilenameFormatting.TT.ORES=\"ความละเอียดของเอาต์พุต (ปรับสัดส่วน)\"\nFilenameFormatting.TT.VF=\"รูปแบบวิดีโอ\"\nBasic.Settings.Video=\"วีดีโอ\"\nBasic.Settings.Video.Adapter=\"การ์ดจอ\"\nBasic.Settings.Video.BaseResolution=\"ความละเอียดของพื้นที่หลัก (แคนวาส)\"\nBasic.Settings.Video.ScaledResolution=\"ความละเอียดของเอาต์พุต (ปรับสัดส่วน)\"\nBasic.Settings.Video.DownscaleFilter=\"ดาวน์สเกลฟิลเตอร์\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[ความละเอียดตรงกันแล้ว ไม่จำเป็นต้องทำการดาวน์สเกล]\"\nBasic.Settings.Video.FPSCommon=\"ค่า FPS ทั่วไป\"\nBasic.Settings.Video.FPSInteger=\"ค่า FPS จำนวนเต็ม\"\nBasic.Settings.Video.FPSFraction=\"ค่า FPS เศษส่วน\"\nBasic.Settings.Video.Numerator=\"ตัวเศษ\"\nBasic.Settings.Video.Denominator=\"ตัวหาร\"\nBasic.Settings.Video.Renderer=\"ตัวเรนเดอร์\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (อยู่ในช่วงทดลอง)\"\nBasic.Settings.Video.InvalidResolution=\"ค่าความละเอียดไม่ถูกต้อง ต้องมีค่าเป็น [กว้าง]x[สูง] (เช่น 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"เอาต์พุตวิดีโอใช้งานอยู่ในขณะนี้ กรุณาปิดเอาต์พุตทั้งหมดแล้วค่อยเปลี่ยนแปลงการตั้งค่าวิดีโอ\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (เร็วที่สุด แต่จะเบลอถ้าปรับสัดส่วน)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (ปรับสัดส่วนแบบคมชัด, 16 ตัวอย่าง)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (ปรับสัดส่วนแบบคมชัด, 36 ตัวอย่าง)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Area (ค่าผลรวมถ่วงน้ำหนัก, 4/6/9 ตัวอย่าง)\"\nBasic.Settings.Audio=\"เสียง\"\nBasic.Settings.Audio.SampleRate=\"อัตราการสุ่มตัวอย่าง\"\nBasic.Settings.Audio.Channels=\"ช่อง\"\nBasic.Settings.Audio.Meters=\"มิเตอร์\"\nBasic.Settings.Audio.MeterDecayRate=\"อัตราการลดระดับ\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"เร็ว\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"ปานกลาง (Type I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"ช้า (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"ชนืดพีคมิเตอร์\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"ค่าพีคตัวอย่าง\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"ค่าพีคจริง (ใช้ CPU มากขึ้น)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"คำเตือน: ระบบเสียงรอบทิศทางเปิดใช้งานอยู่\"\nBasic.Settings.Audio.MultichannelWarning=\"หากกำลังสตรีมอยู่ โปรดตรวจสอบดูว่าบริการสตรีมมิงของคุณรองรับการส่งผ่านข้อมูลเสียงเซอร์ราวด์และการเล่นข้อมูลเสียงเซอร์ราวด์ทั้งสองอย่างหรือไม่ ตัวอย่างเช่น Facebook 360 Live รองรับเสียงเซอร์ราวด์อย่างสมบูรณ์; YouTube Live รองรับการส่งผ่านข้อมูลเสียง 5.1 (และการเล่นข้อมูลเสียงทางทีวี)\\n\\nฟิลเตอร์เสียงของ OBS สามารถเข้ากันได้กับเสียงเซอร์ราวด์ แม้ว่าการรองรับปลั๊กอิน VST จะไม่รับประกันก็ตาม\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"เปิดใช้งานระบบเสียงรอบทิศทางหรือไม่?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"คุณแน่ใจหรือไม่ว่าต้องการเปิดใช้ระบบเสียงแบบเซอร์ราวด์?\"\nBasic.Settings.Audio.Devices=\"อุปกรณ์เสียงส่วนกลาง\"\nBasic.Settings.Audio.DesktopDevice=\"เสียงเดสก์ท็อป\"\nBasic.Settings.Audio.DesktopDevice2=\"เสียงเดสก์ท็อป 2\"\nBasic.Settings.Audio.AuxDevice=\"เสียงไมค์/ช่องเสริม\"\nBasic.Settings.Audio.AuxDevice2=\"เสียงไมค์/ช่องเสริม 2\"\nBasic.Settings.Audio.AuxDevice3=\"เสียงไมค์/ช่องเสริม 3\"\nBasic.Settings.Audio.AuxDevice4=\"เสียงไมค์/ช่องเสริม 4\"\nBasic.Settings.Audio.EnablePushToMute=\"เปิดใช้งานการกดเพื่อปิดเสียง\"\nBasic.Settings.Audio.PushToMuteDelay=\"เวลาหน่วงการกดเพื่อปิดเสียง\"\nBasic.Settings.Audio.EnablePushToTalk=\"เปิดใช้งานการกดเพื่อพูดคุย\"\nBasic.Settings.Audio.PushToTalkDelay=\"เวลาหน่วงการกดเพื่อพูดคุย\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[อุปกรณ์ไม่ได้เชื่อมต่อหรือไม่พร้อมใช้งาน]\"\nBasic.Settings.Audio.Disabled=\"ปิดการใช้งาน\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"โหมดการบัฟเฟอร์เสียงแบบเวลาแฝงต่ำ (สำหรับเอาต์พุต Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"คำเตือน: การบัฟเฟอร์เสียงแบบเวลาแฝงต่ำถูกเปิดใช้แล้ว\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"โหมดการบัฟเฟอร์เสียงแบบเวลาแฝงต่ำอาจทำให้เสียงเกิดความผิดพร่อง หรือหยุดการเล่นจากบางแหล่งได้\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"เปิดใช้โหมดการบัฟเฟอร์เสียงแบบเวลาแฝงต่ำหรือไม่?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"คุณแน่ใจหรือไม่ว่าต้องการเปิดใช้โหมดการบัฟเฟอร์เสียงแบบเวลาแฝงต่ำ?\"\nBasic.Settings.Accessibility=\"การช่วยการเข้าถึง\"\nBasic.Settings.Accessibility.ColorOverrides=\"ใช้สีแตกต่างกัน\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"ขอบแหล่ง (พื้นที่เลือก)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"ขอบแหล่ง (ครอบตัด)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"ขอบแหล่ง (โฮเวอร์)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"แถบปรับระดับเสียงในตัวแต่งเสียง (-60 ถึง -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"แถบปรับระดับเสียงในตัวแต่งเสียง (-20 ถึง -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"แถบปรับระดับเสียงในตัวแต่งเสียง (-9 ถึง 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"แถบปรับระดับเสียงในตัวแต่งเสียง (-60 ถึง -20dB) (ใช้งาน)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"แถบปรับระดับเสียงในตัวแต่งเสียง (-20 ถึง -9dB) (ใช้งาน)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"แถบปรับระดับเสียงในตัวแต่งเสียง (-9 ถึง 0dB) (ใช้งาน)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"พรีเซ็ตสี\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"เริ่มต้น\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"กำหนดเอง\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"แบบทดแทนสำหรับตาบอดสี\"\nBasic.Settings.Advanced=\"ขั้นสูง\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"แสดงคำเตือนเอาต์พุตที่ใช้งานเมื่อออก\"\nBasic.Settings.Advanced.General.ProcessPriority=\"ลำดับความสำคัญของโปรเซส\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"สูง\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"สูงกว่าปกติ\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"ปกติ\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"ต่ำกว่าปกติ\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"ไม่ทำงาน\"\nBasic.Settings.Advanced.FormatWarning=\"คำเตือน: รูปแบบสีนอกเหนือจาก NV12/P010 นั้นมีจุดประสงค์เพื่อการบันทึกเป็นหลัก และไม่แนะนำให้ใช้งานเมื่อทำการสตรีม การสตรีมอาจทำให้มีการใช้งาน CPU เพิ่มมากขึ้นเนื่องจากการแปลงรูปแบบสี\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"คำเตือน: รูปแบบที่มีความเที่ยงตรงสูงมักใช้กับปริภูมิสี HDR มากกว่า\"\nBasic.Settings.Advanced.FormatWarning2100=\"คำเตือน: Rec. 2100 ควรใช้รูปแบบหนึ่งด้วยความเที่ยงตรงมากกว่านี้\"\nBasic.Settings.Advanced.Video.ColorFormat=\"รูปแบบสี\"\nBasic.Settings.Advanced.Video.ColorSpace=\"ปริภูมิสี\"\nBasic.Settings.Advanced.Video.ColorRange=\"ช่วงสี\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"จำกัด\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"เต็ม\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"ระดับสีขาว SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"ระดับค่ายอดแบบนามบัญญัติ HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"อุปกรณ์มอนิเตอร์\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"ค่าเริ่มต้น\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"ปิดใช้การทำ audio ducking ใน Windows\"\nBasic.Settings.Advanced.StreamDelay=\"ดีเลย์สตรีม\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"ระยะเวลา\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"คงจุดตัด (ค่าหน่วงเวลาการเพิ่ม) เดิมไว้เมื่อทำการเชื่อมต่อใหม่\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"การใช้งานหน่วยความจำโดยประมาณ: %1 MB\"\nBasic.Settings.Advanced.Network=\"เครือข่าย\"\nBasic.Settings.Advanced.Network.Disabled=\"โปรโตคอลการสตรีมที่เลือกขณะนี้ไม่รองรับการเปลี่ยนแปลงการตั้งค่าเครือข่าย\"\nBasic.Settings.Advanced.Network.BindToIP=\"เชื่อมโยงกับ IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"เปิดใช้งานการเพิ่มประสิทธิภาพเครือข่าย\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"เปิดใช้งาน TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"พยายามทำให้เอาต์พุต RTMP เข้าใจง่ายขึ้นสำหรับแอปพลิเคชันตัวอื่นๆ ซึ่งอ่อนไหวต่อเวลาแฝง โดยควบคุมอัตราการส่งข้อมูล\\nซึ่งอาจเพิ่มความเสี่ยงในการเกิดเฟรมตกหล่นเมื่อใช้การเชื่อมต่อที่ไม่เสถียร\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"ลักษณะการโฟกัสแป้นลัด\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"ไม่ปิดใช้งานแป้นลัดเลย\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"ปิดใช้งานแป้นลัดเมื่อหน้าต่างหลักอยู่ในระยะโฟกัส\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"ปิดใช้งานแป้นลัดเมื่อหน้าต่างหลักไม่อยู่ในระยะโฟกัส\"\nBasic.Settings.Advanced.AutoRemux=\"รีมักซ์เป็น %1 โดยอัตโนมัติ\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(บันทึกเป็น mkv)\"\nBasic.AdvAudio=\"คุณสมบัติเสียงขั้นสูง\"\nBasic.AdvAudio.ActiveOnly=\"แหล่งที่ใช้งานเท่านั้น\"\nBasic.AdvAudio.Name=\"ชื่อ\"\nBasic.AdvAudio.Volume=\"ระดับเสียง\"\nBasic.AdvAudio.VolumeSource=\"ระดับเสียงของ '%1'\"\nBasic.AdvAudio.Mono=\"โมโน\"\nBasic.AdvAudio.MonoSource=\"การดาวน์มิกซ์เสียงโมโนสำหรับ '%1'\"\nBasic.AdvAudio.Balance=\"บาลานซ์\"\nBasic.AdvAudio.BalanceSource=\"บาลานซ์สำหรับ '%1'\"\nBasic.AdvAudio.SyncOffset=\"ออฟเซตการซิงค์\"\nBasic.AdvAudio.SyncOffsetSource=\"ออฟเซตการซิงค์สำหรับ '%1'\"\nBasic.AdvAudio.Monitoring=\"การมอนิเตอร์เสียง\"\nBasic.AdvAudio.Monitoring.None=\"ปิดมอนิเตอร์\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"มอนิเตอร์อย่างเดียว (ปิดเสียงเอาต์พุต)\"\nBasic.AdvAudio.Monitoring.Both=\"มอนิเตอร์และเอาต์พุต\"\nBasic.AdvAudio.MonitoringSource=\"การมอนิเตอร์เสียงสำหรับ '%1'\"\nBasic.AdvAudio.AudioTracks=\"แทร็ก\"\nBasic.Settings.Hotkeys=\"ปุ่มลัด\"\nBasic.Settings.Hotkeys.Pair=\"ลำดับแป้นพิมพ์ที่ใช้ร่วมกันกับ '%1' ทำงานเป็นทอกเกิล\"\nBasic.Settings.Hotkeys.Filter=\"ฟิลเตอร์\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"กรองด้วยแป้นลัด\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"แป้นลัดนี้ใช้ร่วมกับการทำงานอื่นๆ อย่างน้อยหนึ่งอย่าง คลิกเพื่อแสดงรายการต่างๆ ที่ขัดแย้งกัน\"\nBasic.Settings.Hotkeys.PleaseWait=\"กำลังโหลดแป้นลัด กรุณารอสักครู่...\"\nBasic.Hotkeys.SelectScene=\"สลับไปที่ฉาก\"\nBasic.SystemTray.Show=\"แสดง\"\nBasic.SystemTray.Hide=\"ซ่อน\"\nBasic.SystemTray.Message.Reconnecting=\"ตัดการเชื่อมต่อแล้ว. กำลังเชื่อมต่อใหม่...\"\nHotkeys.Delete=\"ลบ\"\nHotkeys.Home=\"หน้าหลัก\"\nHotkeys.End=\"สิ้นสุด\"\nHotkeys.Tab=\"แท็บ\"\nHotkeys.Pause=\"หยุดชั่วคราว\"\nHotkeys.Left=\"ซ้าย\"\nHotkeys.Right=\"ขวา\"\nHotkeys.Up=\"บน\"\nHotkeys.Down=\"ลง\"\nHotkeys.Menu=\"เมนู\"\nHotkeys.NumpadNum=\"แป้นตัวเลข %1\"\nHotkeys.NumpadMultiply=\"แป้นตัวเลข คูณ\"\nHotkeys.NumpadDivide=\"แป้นตัวเลข หาร\"\nHotkeys.NumpadAdd=\"แป้นตัวเลข บวก\"\nHotkeys.NumpadSubtract=\"แป้นตัวเลข ลบ\"\nHotkeys.NumpadDecimal=\"แป้นตัวเลข จุด\"\nHotkeys.AppleKeypadNum=\"%1 (แป้นปุ่ม)\"\nHotkeys.AppleKeypadMultiply=\"* (แป้นปุ่ม)\"\nHotkeys.AppleKeypadDivide=\"/ (แป้นปุ่ม)\"\nHotkeys.AppleKeypadAdd=\"+ (แป้นปุ่ม)\"\nHotkeys.AppleKeypadSubtract=\"- (แป้นปุ่ม)\"\nHotkeys.AppleKeypadDecimal=\". (แป้นปุ่ม)\"\nHotkeys.AppleKeypadEqual=\"= (แป้นปุ่ม)\"\nHotkeys.MouseButton=\"เมาส์ %1\"\nMute=\"ปิด​เสียง\"\nUnmute=\"เปิดเสียง\"\nPush-to-mute=\"กดเพื่อปิดเสียง\"\nPush-to-talk=\"กดเพื่อพูด\"\nSceneItemShow=\"แสดง '%1'\"\nSceneItemHide=\"ซ่อน %1\"\nOutputWarnings.NoTracksSelected=\"คุณต้องเลือกอย่างน้อยหนึ่งแทร็ก\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"การตั้งค่าเอาต์พุตผิดพลาด\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"เอาต์พุตทั้งหมดต้องมีแทร็กเสียงเลือกไว้อย่างน้อยหนึ่งแทร็ก\"\nOutputWarnings.MP4Recording=\"คำเตือน: สิ่งบันทึกที่บันทึกเป็น MP4/MOV จะไม่สามารถกู้คืนได้หากไม่สามารถประมวลผลไฟล์ให้เสร็จสมบูรณ์ได้ (เช่น เกิดจาก BSOD, ไฟตัด ฯลฯ) ถ้าต้องการบันทึกแทร็กเสียงหลายๆ แทร็ก ให้ใช้ MKV และรีมักซ์สิ่งบันทึกเป็น MP4/MOV หลังจากที่เสร็จสิ้นแล้ว (ไฟล์ → รีมักซ์สิ่งบันทึก)\"\nOutputWarnings.CannotPause=\"คำเตือน: ไม่สามารถหยุดการบันทึกต่างๆ ชั่วคราวได้หากตัวเข้ารหัสการบันทึกถูกตั้งเป็น \\\"(ใช้ตัวเข้ารหัสสตรีม)\\\"\"\nOutputWarnings.CodecIncompatible=\"การเลือกตัวเข้ารหัสข้อมูลเสียงหรือวิดีโอถูกเปลี่ยนกลับเป็นค่าเดิมเนื่องจากไม่มีความเข้ากันได้ กรุณาเลือกตัวเข้ารหัสที่เข้ากันได้จากในรายชื่อ\"\nCodecCompat.Incompatible=\"(เข้ากันไม่ได้กับ %1)\"\nCodecCompat.CodecPlaceholder=\"เลือกตัวเข้ารหัส...\"\nCodecCompat.ContainerPlaceholder=\"เลือกรูปแบบ...\"\nCodecCompat.CodecMissingOnExit.Title=\"ไม่ได้เลือกตัวเข้ารหัสไว้\"\nCodecCompat.CodecMissingOnExit.Text=\"ไม่ได้กำหนดตัวเข้ารหัสข้อมูลวิดีโอหรือเสียงไว้อย่างน้อยหนึ่งตัว กรุณาตรวจสอบให้แน่ใจว่าได้เลือกตัวเข้ารหัสสำหรับสิ่งบันทึกและการสตรีมทั้งคู่\"\nCodecCompat.ContainerMissingOnExit.Title=\"ไม่ได้เลือกรูปแบบไว้\"\nCodecCompat.ContainerMissingOnExit.Text=\"ไม่ได้เลือกรูปแบบการบันทึกใดไว้ กรุณาเลือกรูปแบบการบันทึกซึ่งเข้ากันได้กับตัวเข้ารหัสสตรีมที่เลือก\"\nFinalScene.Title=\"ลบฉาก\"\nFinalScene.Text=\"จำเป็นต้องมีอย่างน้อยหนึ่งฉาก\"\nNoSources.Title=\"ไม่มีแหล่ง\"\nNoSources.Text=\"ดูเหมือนคุณยังไม่ได้เพิ่มแหล่งวิดีโอใดๆ เลย ถ้าเป็นแบบนั้น คุณจะส่งออกเอาต์พุตเพียงหน้าจอเปล่าเท่านั้น คุณแน่ใจหรือเปล่าว่าต้องการทำแบบนี้?\"\nNoSources.Text.AddSource=\"คุณสามารถเพิ่มแหล่งได้โดยคลิกที่ไอคอน + ใต้กล่อง แหล่ง ในหน้าต่างหลักเมื่อใดก็ได้\"\nNoSources.Label=\"คุณไม่มีแหล่งใดเลย\\nคลิกปุ่ม + ด้านล่าง\\nหรือคลิกขวาตรงนี้เพื่อเพิ่มแหล่ง\"\nChangeBG=\"ตั้งค่าสี\"\nCustomColor=\"กำหนดสีเอง\"\nBrowserSource.EnableHardwareAcceleration=\"เปิดใช้งานการเร่งด้วยฮาร์ดแวร์สำหรับแหล่งเบราว์เซอร์\"\nAbout=\"เกี่ยวกับเรา\"\nAbout.Info=\"OBS Studio เป็นซอฟต์แวร์บันทึกวิดีโอและสตรีมมิงสดที่ไม่มีค่าใช้จ่ายและเป็นโอเพนซอร์ส\"\nAbout.Donate=\"ทำการบริจาค\"\nAbout.GetInvolved=\"เข้ามามีส่วนร่วม\"\nAbout.Authors=\"ผู้เขียน\"\nAbout.License=\"ใบอนุญาต\"\nAbout.Error=\"ผิดพลาด! ไฟล์ไม่สามารถอ่านได้\\n\\nไปที่: %1\"\nAbout.Contribute=\"สนับสนุนโปรเจ็คนี้\"\nAddUrl.Title=\"เพิ่มแหล่งผ่าน URL\"\nAddUrl.Text=\"คุณได้ลาก URL ไปที่ OBS การดำเนินการนี้จะเพิ่มลิงก์เป็นแหล่งข้อมูลโดยอัตโนมัติ คุณจะดําเนินการต่อหรือไม่?\"\nResizeOutputSizeOfSource=\"ปรับขนาดเอาต์พุต (ขนาดแหล่ง)\"\nResizeOutputSizeOfSource.Text=\"ความละเอียดเบื้องต้นและเอาต์พุตจะถูกปรับให้เป็นขนาดเดียวกับแหล่งปัจจุบัน\"\nResizeOutputSizeOfSource.Continue=\"คุณต้องการทำต่อไปหรือไม่?\"\nPreviewTransition=\"แสดงตัวอย่างการเปลี่ยนฉาก\"\nImporter=\"ตัวนำเข้าคอลเลกชันฉาก\"\nImporter.SelectCollection=\"เลือกคอลเลกชันฉาก\"\nImporter.Collection=\"คอลเลกชันฉาก\"\nImporter.HelpText=\"เพิ่มไฟล์มาที่หน้าต่างนี้ เพื่อนำเข้าคอลเลกชันจาก OBS หรือโปรแกรมอื่นๆ ที่รองรับ\"\nImporter.Path=\"เส้นทางคอลเลกชัน\"\nImporter.Program=\"แอปพลิเคชันที่ตรวจพบ\"\nImporter.AutomaticCollectionPrompt=\"ค้นหาคอลเลกชันฉากโดยอัตโนมัติ\"\nImporter.AutomaticCollectionText=\"OBS สามารถค้นหาคอลเลกชันฉากที่สามารถนำเข้าได้จากโปรแกรมของบุคคลที่สามที่รองรับได้โดยอัตโนมัติ คุณต้องการให้ OBS ค้นหาคอลเลกชันให้คุณโดยอัตโนมัติหรือไม่?\\n\\nคุณสามารถเปลี่ยนตัวเลือกนี้ได้ภายหลังใน ตั้งค่า > ทั่วไป > ตัวนำเข้า\"\nImporter.SelectFile=\"เรียกดู...\"\nRestart=\"รีสตาร์ท\"\nNeedsRestart=\"OBS Studio จำเป็นต้องรีสตาร์ท คุณต้องการรีสตาร์ทตอนนี้เลยหรือไม่\"\nLoadProfileNeedsRestart=\"โปรไฟล์มีการตั้งค่าที่ต้องการให้รีสตาร์ท OBS:\\n%1\\n\\nคุณต้องการรีสตาร์ท OBS เพื่อให้การตั้งค่าเหล่านี้มีผลหรือไม่\"\nContextBar.NoSelectedSource=\"ไม่ได้เลือกแหล่งใด\"\nContextBar.MediaControls.PlayMedia=\"เล่นสื่อ\"\nContextBar.MediaControls.PauseMedia=\"หยุดสื่อชั่วคราว\"\nContextBar.MediaControls.StopMedia=\"หยุดสื่อ\"\nContextBar.MediaControls.RestartMedia=\"เริ่มเล่นสื่อใหม่\"\nContextBar.MediaControls.PlaylistNext=\"รายการถัดไปในรายการเล่น\"\nContextBar.MediaControls.PlaylistPrevious=\"รายการก่อนหน้าในรายการเล่น\"\nContextBar.MediaControls.BlindSeek=\"วิดเจ็ตกรอสื่อ\"\nYouTube.Auth.Ok=\"การอนุญาตเสร็จสมบูรณ์\\nคุณสามารถปิดหน้านี้ได้แล้ว\"\nYouTube.Auth.NoCode=\"กระบวนการอนุญาตยังไม่เสร็จสมบูรณ์\"\nYouTube.Auth.NoChannels=\"ไม่มีช่องในบัญชีที่เลือก\"\nYouTube.Auth.WaitingAuth.Title=\"การอนุญาตผู้ใช้ YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"กรุณาทำการอนุญาตให้เสร็จสมบูรณ์ในเบราว์เซอร์ภายนอกของคุณ<br>ถ้าเบราว์เซอร์ภายนอกไม่เปิดขึ้น ให้เข้าลิงก์นี้และทำการอนุญาตให้เสร็จสมบูรณ์:<br>%1\"\nYouTube.AuthError.Text=\"ไม่สามารถรับข้อมูลช่องได้: %1\"\nYouTube.Actions.WindowTitle=\"ตั้งค่าการออกอากาศ YouTube - ช่อง: %1\"\nYouTube.Actions.CreateNewEvent=\"สร้างการออกอากาศใหม่\"\nYouTube.Actions.ChooseEvent=\"เลือกการออกอากาศที่มีอยู่แล้ว\"\nYouTube.Actions.Title=\"ชื่อเรื่อง*\"\nYouTube.Actions.MyBroadcast=\"การออกอากาศของฉัน\"\nYouTube.Actions.Description=\"คำอธิบาย\"\nYouTube.Actions.Privacy=\"ความเป็นส่วนตัว*\"\nYouTube.Actions.Privacy.Private=\"ส่วนตัว\"\nYouTube.Actions.Privacy.Public=\"สาธารณะ\"\nYouTube.Actions.Privacy.Unlisted=\"ไม่เป็นสาธารณะ\"\nYouTube.Actions.Category=\"หมวดหมู่\"\nYouTube.Actions.Thumbnail=\"ภาพขนาดย่อ\"\nYouTube.Actions.Thumbnail.SelectFile=\"เลือกไฟล์...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"ไม่ได้เลือกไฟล์\"\nYouTube.Actions.Thumbnail.ClearFile=\"ล้าง\"\nYouTube.Actions.MadeForKids=\"วิดีโอนี้สร้างขึ้นมาเพื่อเด็กใช่ไหม?*\"\nYouTube.Actions.MadeForKids.Yes=\"ใช่ สร้างขึ้นมาเพื่อเด็ก\"\nYouTube.Actions.MadeForKids.No=\"ไม่ใช่ ไม่ได้สร้างขึ้นมาเพื่อเด็ก\"\nYouTube.Actions.AdditionalSettings=\"การตั้งค่าเพิ่มเติม\"\nYouTube.Actions.Latency=\"เวลาในการตอบสนอง\"\nYouTube.Actions.Latency.Normal=\"ปกติ\"\nYouTube.Actions.Latency.Low=\"ต่ำ\"\nYouTube.Actions.Latency.UltraLow=\"ต่ำมาก\"\nYouTube.Actions.EnableAutoStart=\"เปิดใช้งานการเริ่มต้นอัตโนมัติ\"\nYouTube.Actions.EnableAutoStop=\"เปิดใช้งานการหยุดอัตโนมัติ\"\nYouTube.Actions.AutoStartStop.TT=\"บ่งบอกว่าจะให้การออกอากาศที่กำหนดเวลาไว้นี้เริ่มต้นโดยอัตโนมัติหรือไม่\"\nYouTube.Actions.EnableDVR=\"เปิดใช้งาน DVR\"\nYouTube.Actions.360Video=\"วิดีโอ 360°\"\nYouTube.Actions.ScheduleForLater=\"กำหนดเวลาไว้สำหรับภายหลัง\"\nYouTube.Actions.RememberSettings=\"จําการตั้งค่าเหล่านี้\"\nYouTube.Actions.Create_Ready=\"สร้างการออกอากาศ\"\nYouTube.Actions.Create_GoLive=\"สร้างการออกอากาศและเริ่มสตรีม\"\nYouTube.Actions.Choose_Ready=\"เลือกการออกอากาศ\"\nYouTube.Actions.Choose_GoLive=\"เลือกการออกอากาศและเริ่มสตรีม\"\nYouTube.Actions.Create_Schedule=\"กำหนดเวลาการออกอากาศ\"\nYouTube.Actions.Create_Schedule_Ready=\"กำหนดเวลาและเลือกการออกอากาศ\"\nYouTube.Actions.Dashboard=\"เปิด YouTube Studio\"\nYouTube.Actions.Error.Title=\"การสร้างการออกอากาศสดเกิดข้อผิดพลาด\"\nYouTube.Actions.Error.Text=\"การเข้าถึง YouTube เกิดข้อผิดพลาด '%1'<br/>คำอธิบายข้อผิดพลาดอย่างละเอียดสามารถพบได้ที่ <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"การเข้าถึง YouTube เกิดข้อผิดพลาด กรุณาตรวจสอบการเชื่อมต่อเครือข่ายของคุณหรือการเข้าถึงเซิร์ฟเวอร์ YouTube ของคุณ\"\nYouTube.Actions.Error.NoBroadcastCreated=\"การสร้างการออกอากาศเกิดข้อผิดพลาด '%1'<br/>คำอธิบายข้อผิดพลาดอย่างละเอียดสามารถพบได้ที่ <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"ไม่ได้สร้างสตรีมใด กรุณาทำการลิงก์บัญชีของคุณใหม่\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API เกิดข้อผิดพลาด กรุณาดูไฟล์ปูมสำหรับข้อมูลเพิ่มเติม\"\nYouTube.Actions.Error.BroadcastNotFound=\"ไม่พบการออกอากาศที่เลือก\"\nYouTube.Actions.Error.FileMissing=\"ไฟล์ที่เลือกไม่มีอยู่\"\nYouTube.Actions.Error.FileOpeningFailed=\"ไม่สามารถเปิดไฟล์ที่เลือก\"\nYouTube.Actions.Error.FileTooLarge=\"ไฟล์ที่เลือกมีขนาดใหญ่เกินไป (ขีดจำกัด: 2 MiB)\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"การเปลี่ยนผ่านการออกอากาศล้มเหลว: %1<br/><br/>หากข้อผิดพลาดยังมีอยู่ ให้<a href='https://studio.youtube.com/video/%2/livestreaming'>เปิดการออกอากาศ YouTube Studio</a> และลองด้วยตนเอง\"\nYouTube.Actions.Error.BroadcastTestStarting=\"การออกอากาศกำลังเปลี่ยนผ่านไปสู่ขั้นทดสอบ ขั้นตอนนี้อาจใช้เวลาสักครู่ กรุณาลองอีกครั้งในอีก 10-30 วินาที\"\nYouTube.Actions.EventsLoading=\"กำลังโหลดรายชื่องานกิจกรรม...\"\nYouTube.Actions.EventCreated.Title=\"สร้างงานกิจกรรมแล้ว\"\nYouTube.Actions.EventCreated.Text=\"สร้างงานกิจกรรมสำเร็จแล้ว\"\nYouTube.Actions.Stream=\"สตรีม\"\nYouTube.Actions.Stream.ScheduledFor=\"กำหนดเวลาไว้ที่ %1\"\nYouTube.Actions.Stream.Resume=\"กลับสู่การสตรีมที่ถูกขัดจังหวะต่อ\"\nYouTube.Actions.Stream.YTStudio=\"สร้างขึ้นอัตโนมัติโดย YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"กำลังสร้างการออกอากาศสดใหม่ กรุณารอสักครู่...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"ต้องเริ่มต้นด้วยตนเอง\"\nYouTube.Actions.AutoStartStreamingWarning=\"การเริ่มต้นอัตโนมัติถูกปิดใช้งานสำหรับงานกิจกรรมนี้ คลิก \\\"เริ่มถ่ายทอดสด\\\" เพื่อเริ่มต้นการออกอากาศของคุณ\"\nYouTube.Actions.AutoStopStreamingWarning=\"คุณจะไม่สามารถเชื่อมต่อใหม่อีกครั้งได้<br>การสตรีมของคุณจะหยุดและคุณจะออกจากการถ่ายทอดสด\"\nYouTube.Errors.liveStreamingNotEnabled=\"การสตรีมสดไม่ได้เปิดใช้บนช่อง YouTube ที่เลือก<br/><br/>ดูที่ <a href='https://www.youtube.com/features'>youtube.com/features</a> สำหรับข้อมูลเพิ่มเติม\"\nYouTube.Errors.livePermissionBlocked=\"การสตรีมสดไม่สามารถใช้งานได้บนช่อง YouTube ที่เลือก<br/>โปรดทราบว่าอาจต้องใช้เวลานานถึง 24 ชั่วโมง การสตรีมสดจึงจะสามารถใช้งานได้หลังจากที่เปิดใช้งานในหน้าการตั้งค่าช่องของคุณ<br/><br/>ดูที่ <a href='https://www.youtube.com/features'>youtube.com/features</a> สำหรับรายละเอียด\"\nYouTube.Errors.errorExecutingTransition=\"การเปลี่ยนผ่านล้มเหลวเนื่องจากเกิดข้อผิดพลาดกับแบ็กเอนด์ กรุณาลองอีกครั้งในอีกไม่กี่วินาที\"\nYouTube.Errors.errorStreamInactive=\"YouTube ไม่ได้รับข้อมูลสำหรับการสตรีมของคุณ กรุณาตรวจสอบการปรับแต่งค่าของคุณแล้วลองใหม่อีกครั้ง\"\nYouTube.Errors.invalidTransition=\"การเปลี่ยนผ่านที่ได้พยายามไปไม่ถูกต้อง ซึ่งอาจเป็นเนื่องจากการสตรีมยังไม่เสร็จสิ้นการเปลี่ยนผ่านก่อนหน้านั้น กรุณารออีกไม่กี่วินาทีแล้วลองใหม่อีกครั้ง\"\nYouTube.DocksRemoval.Title=\"ล้างถาดงานเบราว์เซอร์ YouTube แบบดั้งเดิม\"\nYouTube.DocksRemoval.Text=\"ถาดงานเบราว์เซอร์เหล่านี้จะถูกลบออกเนื่องจากเลิกใช้แล้ว:\\n\\n%1\\nให้ใช้ \\\"ถาดงาน/YouTube Live Control Room\\\" แทน\"\nConfigDownload.WarningMessageTitle=\"คำเตือน\"\nFailedToStartStream.MissingConfigURL=\"ไม่มี URL ค่ากำหนดสำหรับบริการปัจจุบัน\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"ไม่ได้ระบุ URL RTMP กำหนดเอง\"\nFailedToStartStream.InvalidCustomConfig=\"ค่ากำหนดเองไม่ถูกต้อง\"\nFailedToStartStream.MissingCanvas=\"พื้นที่ทำงานเพิ่มเติมที่ตั้งค่าไว้หายไป\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"ไม่สามารถสร้างบริการของวิดีโอหลายแทร็ก\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"ไม่สามารถสร้างเอาต์พุต RTMP ของวิดีโอหลายแทร็ก\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC ไม่สามารถใช้ได้\\n\\nไม่พบชนิดตัวเข้ารหัส '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"ไม่สามารถสร้างตัวเข้ารหัสวิดีโอ '%1' (ชนิด: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"ไม่สามารถอ่านข้อมูลวิดีโอ OBS ได้ขณะสร้างตัวเข้ารหัส '%1' (ชนิด: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"ไม่สามารถสร้างตัวเข้ารหัสเสียงได้\"\nFailedToStartStream.NoRTMPURLInConfig=\"ค่ากำหนดไม่มี URL RTMP(S) เป้าหมายของสตรีม\"\nFailedToStartStream.FallbackToDefault=\"การเริ่มสตรีมโดยใช้ %1 ล้มเหลว คุณต้องการลองใหม่โดยใช้การตั้งค่าการเข้ารหัสแบบเดี่ยวหรือไม่?\"\nFailedToStartStream.ConfigRequestFailed=\"ไม่สามารถดึงค่ากำหนดจาก %1 ได้<br><br>ข้อผิดพลาด HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"ได้รับค่าสถานะ '%1' ซึ่งไม่รู้จัก\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nคุณต้องการสตรีมต่อไปโดยไม่มี %1 หรือไม่?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nคุณต้องการสตรีมต่อไปหรือไม่?\"\nFailedToStartStream.MissingEncoderConfigs=\"ค่ากำหนดการถ่ายทอดสดไม่มีการกำหนดค่าตัวเข้ารหัส\"\nFailedToStartStream.InvalidEncoderConfig=\"การตั้งค่าการถ่ายทอดสดมีการกำหนดค่าตัวเข้ารหัสที่ไม่ถูกต้อง\"\nFailedToStartStream.StatusMissingHTML=\"คำขอถ่ายทอดสดส่งกลับข้อผิดพลาดที่ไม่ระบุ\"\nFailedToStartStream.NoConfigSupplied=\"ค่ากำหนดหายไป\"\nMultitrackVideo.Info=\"%1 จะปรับการตั้งค่าของคุณให้เหมาะสมโดยอัตโนมัติเพื่อเข้ารหัสและส่งวิดีโอด้วยคุณภาพหลายแบบ การเลือกตัวเลือกนี้จะส่งข้อมูลเกี่ยวกับคอมพิวเตอร์และการตั้งค่าซอฟต์แวร์ของคุณให้กับ %2\"\nMultitrackVideo.IncompatibleSettings.Title=\"การตั้งค่าไม่เข้ากัน\"\nMultitrackVideo.IncompatibleSettings.Text=\"ขณะนี้ %1 ไม่เข้ากับ:\\n\\n%2\\nหากต้องการสตรีมด้วย %1 ต่อไป ให้ปิดใช้การตั้งค่านี้ซึ่งไม่เข้ากัน:\\n\\n%3\\nแล้วเริ่มสตรีมใหม่อีกครั้ง\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"ปิดใช้สำหรับสตรีมครั้งนี้แล้วเริ่มสตรีม\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"ปรับการตั้งค่าแล้วเริ่มสตรีม\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"ขณะนี้ %1 ไม่เข้ากับ [เสียง → ทั่วไป → ช่องเสียง] ซึ่งกำหนดเป็น '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[เสียง → ทั่วไป → ช่องเสียง] จะต้องกำหนดเป็น '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 ต้องใช้การตั้งค่าที่ต่างกันหลายอย่างสำหรับ [เสียง → ทั่วไป → ช่องเสียง]\"\nBasic.OpenPluginManager=\"ตัวจัดการปลั๊กอิน\"\nPluginManager=\"ตัวจัดการปลั๊กอิน\"\nPluginManager.Restart=\"ต้องรีสตาร์ท OBS Studio เพื่อนำการเปลี่ยนแปลงปลั๊กอินไปใช้\"\nPluginManager.MissingPlugin=\"[ไม่พบเจอปลั๊กอิน]\"\nPluginManager.Section.Discover=\"เรียกดู\"\nPluginManager.Section.Manage=\"ติดตั้งแล้ว\"\nPluginManager.Section.Updates=\"อัปเดต\"\nPluginManager.Section.Manage.Title=\"จัดการปลั๊กอินที่ติดตั้งไว้\"\n"
  },
  {
    "path": "frontend/data/locale/tl-PH.ini",
    "content": "Language=\"Tagalog\"\nOK=\"SIGE\"\nApply=\"Gamitin\"\nCancel=\"Ipagpaliban\"\nClose=\"Ipinid\"\nSave=\"I-imbak\"\nDiscard=\"Alisin\"\nDisable=\"Huwag Paganahin\"\nYes=\"Oo\"\nNo=\"Hindi\"\nAdd=\"Idagdag\"\nRemove=\"Tanggalin\"\nRename=\"Palitan ang pangalan\"\nInteract=\"Makipag-ugnayan\"\nFilters=\"Pansala\"\nProperties=\"Mga Katangian\"\nMoveUp=\"I-taas\"\nMoveDown=\"I-baba\"\nSettings=\"Pagpipilian\"\nDisplay=\"Ipakita\"\nName=\"Pangalan\"\nExit=\"Umalis\"\nMixer=\"Panghalo sa Tunog\"\nBrowse=\"Magsaliksik\"\nDroppedFrames=\"Mga larawang hindi sinali %1 (%2%)\"\nProjector.Open.Program=\"Buksan ang Pangsalintanaw ng Palatuntunin\"\nProjector.Open.Preview=\"Buksan ang Siliping Pangsalintanaw\"\nProjector.Open.Source=\"Bukas na Pinagmulang Pangsalintanaw\"\nProjector.Open.Multiview=\"Buksan ang Damin-tanaw\"\nProjector.Display=\"Tanghal: %1\"\nProjector.Window=\"Bagong durungawan\"\nProjector.Title=\"Pangsalintanaw\"\nProjector.Title.Scene=\"Tagpo: %1\"\nProjector.Title.Source=\"Pagtututok: %1\"\nProjector.Title.Multiview=\"Damin-tanaw\"\nProjector.ResizeWindowToContent=\"Pagkasyahin ang durungawan sa nilalaman\"\nClear=\"Linisin\"\nRevert=\"Ibalik\"\nShow=\"Ipakita\"\nHide=\"Itago\"\nUnhideAll=\"Ipakita Lahat\"\nUntitled=\"Walang pamagat\"\nNew=\"Bago\"\nDuplicate=\"Kahalintulad\"\nEnable=\"Paganahin\"\nDisableOSXVSync=\"Huwag paganahain ang macOS V-Sync\"\nResetOSXVSyncOnExit=\"Ibalik sa dati muli ang macOS V-Sync sa Pag-alis\"\nHighResourceUsage=\"Labis na ang paglululan sa pagkokodigo! Pagisipan ang pagpapababa ng mga pagpipilian sa bidyo o ang pag-gamit ng higit na mabilis na nakahandang pangkodigo.\"\nTransition=\"Pagbabai-baiting\"\nQuickTransitions=\"Mabilisang Pagbabai-baiting\"\nFadeToBlack=\"Kumupas hanggang Itim\"\nLeft=\"Kaliwa\"\nRight=\"Kanan\"\nTop=\"Tuktok\"\nBottom=\"Baba\"\nReset=\"Ibalik sa dati\"\nHours=\"Oras\"\nMinutes=\"Minuto\"\nSeconds=\"Segundo\"\nDeprecated=\"Hindi na ginagamit\"\nReplayBuffer=\"Itala ang Muling Ilaro\"\nImport=\"I-angkat\"\nExport=\"I-luwas\"\nCopy=\"Isipi\"\nPaste=\"Idikit\"\nPasteReference=\"Idikit (Sanggunian)\"\nPasteDuplicate=\"Idikit (Sipi)\"\nRemuxRecordings=\"Isalin ang mga Pagtatala\"\nNext=\"Sunod\"\nBack=\"Bumalik\"\nDefaults=\"Mga Nakahanda\"\nHideMixer=\"Itago sa Tagahalo\"\nTransitionOverride=\"Daiging Pagbabai-baiting\"\nShowTransition=\"Ipapakitang Pagbabai-baiting\"\nHideTransition=\"Itatagong Pagbabai-baitang\"\nNone=\"Wala\"\nStudioMode.Preview=\"Silip\"\nStudioMode.Program=\"Programa\"\nStudioMode.PreviewSceneName=\"Silip: %1\"\nShowInMultiview=\"Ipakita sa Damin-tanaw\"\nVerticalLayout=\"Patayong Ayos\"\nGroup=\"Pangkat\"\nDoNotShowAgain=\"Huwag ipakitang muli\"\nDefault=\"(Nakahanda)\"\nCalculating=\"Tinataya...\"\nFullscreen=\"Buong-Tabing\"\nWindowed=\"Durungawan\"\nRefreshBrowser=\"Ilulan muli\"\nAspectRatio=\"Ayos ng Tumbasan <b>%1:%2</b>\"\nLockVolume=\"Ipinid ang Lakas ng Tunog\"\nLogViewer=\"Tagatingin ng mgs Nakatala\"\nShowOnStartup=\"Ipakita sa pagbukas\"\nOpenFile=\"Buksan ang talaksan\"\nAddScene=\"Magdagdag ng Tagpo\"\nAddSource=\"Magdagdag ng Pagtututok\"\nRemoveSource=\"Alisin ang mga Napiling Pagtututok\"\nMoveSourceUp=\"Ilipat ang mga Pagtututok Pataas\"\nMoveSourceDown=\"Ilipat ang mga Pagtututok Pababa\"\nSourceProperties=\"Buksan ang mga Katangian ng Pagtututok\"\nSourceFilters=\"Buksan ang mga Pagsasala ng Pagtututok\"\nComingSoon=\"Papaparating Na\"\nAlreadyRunning.Title=\"Ang OBS ay pinapagana na\"\nAlreadyRunning.Text=\"Ang OBS ay pinapagana na! Kung hindi mo ito sinasadya, mangyari lamang patayin ang mga umiiral na OBS bago subukang magpatakbo ng bago. Kung ikaw ay may OBS na kailangan ibaba sa batya ng sistema, mangyari lamang tingnan kung ito ay tumatakbo pa doon.\"\nAlreadyRunning.LaunchAnyway=\"Ilunsad pa rin\"\nCrashHandling.Dialog.Title=\"Nalamang Nagkandarapa ang OBS Studio\"\nCrashHandling.Labels.Text=\"Hindi maayos ang pagkapinid ng OBS Studio.\\n\\nIpatakbo sa Paraang Ligtas (hindi ipapagana ang mga third-party plugins, scripting, at WebSockets)?\"\nCrashHandling.Labels.PrivacyNotice=\"Maaari mo ring piliin na sarimo ang pagbabahagi ng pinakabagong ulat ng pagkandarapa sa OBSProject.<br /><br />Mangyaring basahin ang <a href='https://obsproject.com/privacy-policy'>Mga Patakaran sa Pribasya</a> bago magbahagi ng mga talaksan at magbigay pansin sa mga bahaging ukol sa pagbahagi ng mga talaksan.\"\nCrashHandling.Checkbox.SendReport=\"Binasa ko ang patakaran sa pribasya at pinahihintulotan ko ang pagbahagi.\"\nCrashHandling.Buttons.LaunchSafe=\"Ipatakbo sa Paraan Ligtas.\"\nCrashHandling.Buttons.LaunchNormal=\"Ipatakbo sa Karaniwang Paraan.\"\nCrashHandling.Errors.UploadJSONError=\"Isang kamalian ang nangyari habang binabahagi ang kamakailang ulat ng pagkandarapa. Mangyaring pakisubukan muli mamamaya.\"\nChromeOS.Title=\"Hindi Sinusuportahang Plataporma\"\nWine.Text=\"Hindi sinusuporta ang pagpapatakbo ng OBS sa Wine, at maraming tampok ang hindi gagana o magkakakulang tulad ng dakip o mga pagtututok ng device. <br><br>Inimumungkahi ang pagtatakbo ng wastong bersiyon ng OBS, isang halimbawa na ang <a href='https://flathub.org/apps/details/com.obsproject.Studio'>bersiyong Flatpak namin</a> o package ng operating system mo.\"\nDockCloseWarning.Title=\"Ipinid ang Nakadaong na Durungawan\"\nDockCloseWarning.Text=\"Napinid mo ang isang nakadaong na durungawan. Kung nais mong ipakita itong muli, gamitin ang talaan ng Daungan sa talaan.\"\nExtraBrowsers=\"Pasadyang Panginaing Daungan\"\nAuth.Authing.Title=\"Pinapatunayan...\"\nAuth.Authing.Text=\"Pinapatunayan sa %1, mangyaring maghintay...\"\nAuth.AuthFailure.Title=\"Nabigo sa Pagpapatunay\"\nTwitchAuth.Stats=\"Estatistika sa Twitch\"\nCopy.Filters=\"Kopyahin ang mga Panala\"\nPaste.Filters=\"I-paste ang mga Panala\"\nBrowserPanelInit.Title=\"Sinisimulan ang Browser...\"\nBandwidthTest.Region=\"Rehiyon\"\nBandwidthTest.Region.US=\"Estados Unidos\"\nBandwidthTest.Region.EU=\"Europa\"\nBandwidthTest.Region.Asia=\"Asya\"\nBandwidthTest.Region.Other=\"Iba pa\"\nBasic.AutoConfig.ApplySettings=\"Gamitin ang mga Setting\"\nBasic.AutoConfig.StartPage=\"Impormasyon ukol sa Paggamit\"\nBasic.AutoConfig.StartPage.SubTitle=\"Tukuyin kung para saan mo gustong gamitin ang programa\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Isaayos para sa pagsi-stream, pangalawa lamang ang pagtataya\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Isaaayos para sa pagtatala lamang, Hindi ako magsi-stream\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Gagamitin ko lang ang virtual camera\"\nBasic.AutoConfig.VideoPage=\"Mga Setting sa Video\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Gamitin ang Pangkasalukuyang (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"%1 Tanghal (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Gamitin ang Pangkasalukuyang (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 o 30, pero mas piliin ang 60 kung maaari\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 o 30, ngunit piliin ang mas mataas na hisikot\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Tandaan: Hindi kinakailangang magkatulad ang hisikot ng kanbas (batayan) sa hisikot na gagamitin sa pagsi-stream o pagtatala. Maaaring magbawas sa magiging hisikot ng iyong stream/tala galing sa hisikot ng kanbas upang mabawasan ang paggamit ng mapagkukunan o mga kinakailanganing bitrate.\"\nBasic.AutoConfig.StreamPage=\"Mag-stream ng Impormasyon\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Mangyari lamang ilagay ang impormasyon ng iyong pagstream\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"I-connect ang Account (ipinapayo)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"I-disconnect ang Account\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Kunin ang Stream Key\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Karagdagang Impormasyon\"\nBasic.AutoConfig.StreamPage.Service=\"Serbisyo\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Ipakita Lahat...\"\nBasic.AutoConfig.StreamPage.Server=\"Serber\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Estimahin ang bitrate gamit ang bandwith test (maaaring tumagal ng ilang minuto)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Mas piliin ang hardware encoding\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Ang Hardware Encoding ay nagtatanggal ng karamihan sa paggamit ng CPU, ngunit maaaring nangangailangan ito ng mas maraming bitrate upang magkaroon ng katulad na lebel ng kalidad.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Babala sa stream\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Magsisimulang magsi-stream ang pagsubok ng bandwith ng walang tunog na sapalarang malak ng panoorin sa iyong channel. Kung makakaya mo, iminumungkahing pansamantalang ipagpaliban ang pag-iimbak ng mga panoorin ng mga stream at gawing pribado ang stream hanggang matapos ang nitong pagsubok.\"\nBasic.AutoConfig.TestPage=\"Mga Huling Resulta\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Ang programang ito ay nagapapatupad ngayon ng mga pagsusuri upang matantiya ang pinakamainam na mga setting\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Natapos na ang pagsusuri\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Nagsasagaw ng bandwidth test, maaaring itong magtagal ng ilang minuto...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Kumukonekta sa: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Bigong maka-konekta sa kahit anong mga serber, mangyari lamang suriin ang iyong koneksyon sa internet at subukan muli.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Sinusuri ang bandwidth para sa: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Sinusuri ang encoder ng stream, maaari itong magtagal ng isang minuto...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Sinusuri ang encoder ng pagtatala, maaari itong magtagal ng isang minuto...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Bigong mapatakbo ang encoder\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Sinusuri ang %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Encoder para sa Pag-stream\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Encoder ng Pagtatala\"\nBasic.AutoConfig.TestPage.Result.Header=\"Napagtanto ng programa na ang mga setting na ito ang pinakamainam para sa iyo:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Upang magamit ang mga setting, pindutin ang Apply Settings. Upang ma-configure muli ang wizard at subukang muli, pindutin ang Back. Upang ma-configure mo mismo nang mano-mano ang mga setting, pindutin ang Cancel at buksan ang mga Setting.\"\nBasic.Stats=\"Mga Statistika\"\nBasic.Stats.CPUUsage=\"Paggamit ng CPU\"\nBasic.Stats.MemoryUsage=\"Paggamit ng Memorya\"\nBasic.Stats.AverageTimeToRender=\"Karaniwang bilis upang ma-render ang frame\"\nBasic.Stats.SkippedFrames=\"Mga nalaktawang imahe dahil sa antala sa pag-encode\"\nBasic.Stats.MissedFrames=\"Mga imaheng di nakuha dahil sa antala sa pag-render\"\nBasic.Stats.Output.Recording=\"Pagtatala\"\nBasic.Stats.Status=\"Estado\"\nBasic.Stats.Status.Recording=\"Pagtatala\"\nBasic.Stats.Status.Reconnecting=\"Muling kumukunekta\"\nBasic.Stats.Status.Inactive=\"Hindi aktibo\"\nBasic.Stats.Status.Active=\"Aktibo\"\nBasic.Stats.DroppedFrames=\"Mga Imaheng hindi sinali (Network)\"\nBasic.Stats.MegabytesSent=\"Kabuuan ng Output ng mga Datos\"\nBasic.Stats.DiskFullIn.Text=\"%s Oras, %2 Minuto\"\nUpdater.Title=\"Mga bagong update na magagamit\"\nUpdater.Text=\"Mayroong bagong update na magagamit:\"\nUpdater.UpdateNow=\"Iupdate na Ngayon\"\nUpdater.RemindMeLater=\"Paalalahanan ako Maya-maya\"\nUpdater.Skip=\"Laktawan ang Bersyon\"\nUpdater.NoUpdatesAvailable.Title=\"Walang mga update na magagamit\"\nUpdater.NoUpdatesAvailable.Text=\"Walang mga update ang kasalukuyang magagamit\"\nUpdater.FailedToLaunch=\"Bigong malunsad ang updater\"\nQuickTransitions.SwapScenes=\"Makikipagpalitan ang mga Tagpong Silip/Palatuntunan Pagkatapos ang Pagbabai-baitang\"\nQuickTransitions.SwapScenesTT=\"Makikipagpalit ang mga tagpong silip at palatuntunan pagkatapos ng pagbabai-baitang (kapag umiiral pa ang naunang tagpo ng palatuntunan).\\nHindi nito mababalik ang anumang pagbabagong itinakda sa naunang tagpong palatuntunan.\"\nQuickTransitions.DuplicateScene=\"Dumagdag ng Sipi ng Tagpo\"\nQuickTransitions.EditProperties=\"Dumagdag ng Sipi ng mga Pagtututok\"\nQuickTransitions.HotkeyName=\"Mabilis na Pagbabai-baitang: %1\"\nBasic.AddTransition=\"Magdagdag ng Maisasaayos na Pagbabai-baitang\"\nBasic.RemoveTransition=\"Alisin ang Maisasaayos na Pagbabai-baitang\"\nBasic.TransitionProperties=\"Mga Katangian ng Pagbabai-baitang\"\nBasic.SceneTransitions=\"Mga Transisyon ng Eksena\"\nBasic.TransitionDuration=\"Tagal\"\nBasic.TogglePreviewProgramMode=\"Paraang Aral\"\nBasic.EnablePreviewProgramMode=\"Paganahin ang Paraang Aral\"\nBasic.DisablePreviewProgramMode=\"Huwag Paganahin ang Paraang Aral\"\nUndo.Transform=\"Pagsaanyo ng (mga) pagtutok Sa '%1'\"\nUndo.Transform.Paste=\"Pagdikit ng Pagsasaanyo sa '%1'\"\nUndo.Transform.Reset=\"Pagsauli ng Pagsaanyo sa '%1'\"\nUndo.Transform.FitToScreen=\"Ikasya sa Tabing sa '%1'\"\nUndo.Transform.StretchToScreen=\"Ilapad sa Tabing sa '%1'\"\nUndo.Transform.Center=\"I-gitna sa Tabing sa '%1'\"\nUndo.Transform.VCenter=\"I-patayong Gitna sa Tabing sa '%1'\"\nUndo.Transform.HCenter=\"I-pahalang Gitna sa Tabing sa '%1'\"\nUndo.PasteSourceRef=\"Ilapat ang mga Sangguniang Pagtutukoy sa '%1'\"\nTransitionNameDlg.Text=\"Mangyari lamang ilagay ang pangalan ng transisyon\"\nTransitionNameDlg.Title=\"Pangalan ng Transition\"\nTitleBar.Scenes=\"Mga Eksena\"\nNameExists.Title=\"Ang pangalan ay umiiral na\"\nNameExists.Text=\"Ang pangalan na ito ay ginagamit na.\"\nNoNameEntered.Title=\"Mangyari lamang magbigay ng balidong pangalan\"\nNoNameEntered.Text=\"Hindi maaaring walang pangalan.\"\nConfirmStart.Title=\"Umpisahan na ang pag-stream?\"\nConfirmStart.Text=\"Sigurado ka bang gusto mo nang simulan ang pag-stream?\"\nConfirmStop.Title=\"Itigil ang pag-stream?\"\nConfirmStop.Text=\"Sigurado ka bang gusto mong itigil ang pag-stream?\"\nConfirmStopRecord.Title=\"Itigil ang Pagtatala?\"\nConfirmStopRecord.Text=\"Tiyak ka bang nais mong itigil ang pagtatala?\"\nConfirmRemove.Title=\"Kumpirmahin ang pagtanggal\"\nConfirmRemove.TextMultiple=\"Sigurado ka bang gusto mong tanggalin ang %1 na mga item?\"\nOutput.StartStreamFailed=\"Bigong masimulan ang pag-stream\"\nOutput.StartRecordingFailed=\"Bigong masimulan ang pagtatala\"\nOutput.StartReplayFailed=\"Bigong masimulan ang replay buffer\"\nOutput.StartFailedGeneric=\"Bigong masimulan ang output. Mangyari lamang tingnan ang log para sa mga detalye.\\n\\nTandaan: Kung ikaw ay gumagamit ng NVENC o AMD na mga encoder, siguraduhing ang iyong mga video driver ay naka-update.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Hindi makakapag-imbak ng mga baliking pagpapalabas habang nakahinto\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Babala: Hindi maaaring maimbak ang mga baliking pagpapalabas habang nakahinto ang pagtatala.\"\nOutput.ConnectFail.Title=\"Bigong kumonekta\"\nOutput.ConnectFail.BadPath=\"Di-wastong Landas o Connection URL. Mangyaring pakisuri ng iyong mga setting upang mapatunayang wasto ang mga ito.\"\nOutput.ConnectFail.ConnectFailed=\"Bigong kumonekta sa serber\"\nOutput.ConnectFail.InvalidStream=\"Hindi madaanan ang tinutukoy na channel o stream key, mangyari lamang tingnan muli ang iyong stream key. Kung ito ay wasto, maaaring mayroong problema sa pagkonekta sa serber.\"\nOutput.ConnectFail.Error=\"Isang di-inaasahang error ang naganap habang sinusubukang kumonekta sa serber. Karagdagang impormasyon ay nasa log file.\"\nOutput.ConnectFail.Disconnected=\"Nadiskonek mula sa serber.\"\nOutput.StreamEncodeError.Title=\"Pagkakabigo sa encording\"\nOutput.RecordFail.Title=\"Bigong masimulan ang pagtatala.\"\nOutput.RecordFail.Unsupported=\"Maaaring hindi sinusuporta ang output format o ng humigit isang audio track. Mangyaring salisikin ang iyong mga kagustuhan at subukan muli.\"\nOutput.RecordNoSpace.Title=\"Hindi sapat ang lawak ng disk\"\nOutput.RecordNoSpace.Msg=\"Hindi sapat ang lawak ng disk sa pagpatuloy ng pagtatala.\"\nOutput.RecordError.Title=\"Kamalian habang nagtatala\"\nOutput.RecordError.Msg=\"Isang hindi matukoy na kamalian habang nagtatala.\"\nOutput.RecordError.EncodeErrorMsg=\"Isang kamaliang encoder habang nagtatala.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Isang kamaliang encoder habang nagtatala:<br><br>%1\"\nOutput.BadPath.Title=\"Di-wastong Landas ng Talaksan\"\nOutput.BadPath.Text=\"Hindi mabuksan ang itinakdang Landas ng Pagtatala. Maaaring pakisuri sa iyong Landas ng Pagtatala sa loob ng Settings → Output → Pagtatala.\"\nLogUploadDialog.Title=\"Nabahagi na ang Log File ng OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Pakibasa ng <a href='https://obsproject.com/privacy-policy'>Mga Patakaran sa Pribasya</a> at ang pangkat ukol sa pagbahagi ng mga talaksanbago magbahagi ng kahit na anong talaksan.\"\nLogUploadDialog.Labels.Progress=\"Binabahagi ang iyong log file. Mangyaring pakihintay...\"\nLogUploadDialog.Labels.Description.AppLog=\"Nabahagi na ang iyong log file. Maaari mo nang ibahagi ang URL para sa pagdadalisay o pagpapatulong.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Nabahagi na ang iyong ulat ng pagkandarapa. Maaari mo nang ibahagi ang URL para sa pagdadalisay.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Ibahagi\"\nLogUploadDialog.Buttons.CopyURL=\"Kopyahin ang Log URL\"\nLogUploadDialog.Buttons.RetryButton=\"Subukan muli\"\nRemux.SourceFile=\"Pagtatalang OBS\"\nRemux.Stop=\"Ihinto ang Pag-reremuxing\"\nRemux.OBSRecording=\"Pagtatalang OBS\"\nRemux.FinishedTitle=\"Tapos na ang pag-remux\"\nRemux.Finished=\"Naisalin na ang pagtatala\"\nRemux.FinishedError=\"Nai-remux na ang pagtatala, ngunit maaring hindi natapos ang talaksan.\"\nRemux.SelectRecording=\"Pumili ng Pagtatalang OBS...\"\nRemux.ExitUnfinishedTitle=\"Ang pagremux ay tinutuloy pa\"\nRemux.ExitUnfinished=\"Hindi pa tapos ang pag-remux. Kung ihihinto ito ngayon, maaaring hindi na magagamit ang target file.\\nSigurado ka bang gusto mong ihinto ang pagremux?\"\nRemux.HelpText=\"Ilagay ang mga talaksan sa nitong durungawan upang  maisalin, o pumili ng walang-lamang \\\"Pagtatalang OBS\\\" na cell upang maghanap ng talaksan.\"\nMissingFiles=\"Nawawalang mga File\"\nMissingFiles.MissingFile=\"Nawawalang File\"\nMissingFiles.NewFile=\"Bagong File\"\nMissingFiles.SelectFile=\"Pumili ng file...\"\nMissingFiles.Missing=\"Nawawala\"\nMissingFiles.Found=\"Nahanap\"\nMissingFiles.AutoSearch=\"May nahanap na mga magkatulad na file\"\nMacPermissions.Item.ScreenRecording=\"Pagtatala ng Tabing\"\nMacPermissions.Item.ScreenRecording.Details=\"Kinakailangan ng OBS ang pahintulot na ito sa pagdakip ng iyong tabing.\"\nSourceLeak.Title=\"Kamalian Sa Paglilinis ng Pagtututok\"\nBasic.Scene=\"Eksena\"\nBasic.DisplayCapture=\"Dakip ng Tanghal\"\nBasic.Main.PreviewConextMenu.Enable=\"Paganahin ang Silip\"\nBasic.Main.Preview.Disable=\"Huwag Paganahin ang Silip\"\nScaleFiltering=\"I-scale ang pag-fifilter\"\nScaleFiltering.Point=\"Punto\"\nBlendingMode.Screen=\"Tabing\"\nDeinterlacing.TopFieldFirst=\"Itaas ang Patlang Una\"\nDeinterlacing.BottomFieldFirst=\"Ibaba ang Patlang Una\"\nVolControl.Mute=\"Mahina '%1'\"\nVolControl.Properties=\"Ari-arian na para sa '%1'\"\nVolControl.UnassignedWarning.Title=\"Hindi Nakatalagang Pagtututok ng Audio\"\nBasic.Main.AddSceneDlg.Title=\"Idagdag sa Eksena\"\nBasic.Main.AddSceneDlg.Text=\"Pakiusap idagdag ang pangalan ng mga eksena\"\nBasic.Main.DefaultSceneName.Text=\"Eksena %1\"\nBasic.Main.AddSceneCollection.Title=\"Idagdag ang Eksena sa Koleksyon\"\nBasic.Main.AddSceneCollection.Text=\"Mangyari lamang ilagay ang pangalan ng koleksyon ng mga eksena\"\nBasic.Main.RenameSceneCollection.Title=\"Palitan ang pangalan ng Koleksyon ng mga Eksena\"\nAddProfile.Title=\"Idagdag ang Profile\"\nAddProfile.Text=\"Mangyari lamang ilagay ang pangalan ng profile\"\nRenameProfile.Title=\"Palitan ang pangalan ng Profile\"\nBasic.Main.MixerRename.Title=\"Palitan ang pangalan ng Audio Source\"\nBasic.Main.MixerRename.Text=\"Mangyari lamang ilagay ang pangalan ng audio source\"\nBasic.Main.PreviewDisabled=\"Kasalukuyang hindi pinapagana ang silip\"\nBasic.SourceSelect=\"Lumikha/Pumili ng Source\"\nBasic.SourceSelect.CreateNew=\"Lumikha ng bago\"\nBasic.SourceSelect.AddExisting=\"Idagdag ang Umiiral na\"\nBasic.SourceSelect.AddVisible=\"Palitawin ang source\"\nBasic.PropertiesWindow=\"Mga Katangian para sa '%1'\"\nBasic.PropertiesWindow.SelectColor=\"Pumili ng Kulay\"\nBasic.PropertiesWindow.SelectFont=\"Pumili ng font\"\nBasic.PropertiesWindow.ConfirmTitle=\"Binago ang mga Setting\"\nBasic.PropertiesWindow.Confirm=\"Mayroong mga pagbabagong hindi naimbak. Nais mo bang panatilihin ang mga ito?\"\nBasic.PropertiesWindow.NoProperties=\"Walang mga katangiang magagamit\"\nBasic.PropertiesWindow.AddFiles=\"Idagdag ang mga File\"\nBasic.PropertiesWindow.AddDir=\"Idagdag ang Direktory\"\nBasic.PropertiesWindow.AddURL=\"Magdagdag ng Landas/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Idagdag ang directory sa '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Idagdag ang mga file sa '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Magdagdag ng entry sa '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Baguhin ang entry mula sa '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Simpleng mga FPS Value\"\nBasic.PropertiesView.FPS.Rational=\"Rasyonal na mga FPS Value\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Balidong mga FPS Range:\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Buksan ang URL\"\nBasic.InteractionWindow=\"Nakikipag-interact sa '%1'\"\nBasic.StatusBar.Reconnecting=\"Na-diskonek, muling magku-kunek sa loob ng %2 segundo(mga) (pagtangka%1)\"\nBasic.StatusBar.AttemptingReconnect=\"Sinusubukang uling maka-konek... (tangka %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Matagumpay na muling naka-konek\"\nBasic.StatusBar.Delay=\"Antala (%1 segundo)\"\nBasic.StatusBar.DelayStartingIn=\"Antala (magsisimula sa loob ng %1 segundo)\"\nBasic.StatusBar.DelayStoppingIn=\"Antala (hihinto sa loob ng %1 segundo)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Antala (hihinto sa loob ng %1 segundo, magsisimula sa loob ng %2 segundo)\"\nBasic.StatusBar.RecordingSavedTo=\"Naimbak ang Recording sa '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Naimbak ang Replay Buffer sa '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Naimbak ang screenshot sa '%1'\"\nBasic.Filters=\"Mga Filter\"\nBasic.Filters.AsyncFilters=\"Audio/Video na mga Filter\"\nBasic.Filters.AudioFilters=\"Audio na mga Filter\"\nBasic.Filters.EffectFilters=\"Efeect na mga Filter\"\nBasic.Filters.Title=\"Mga Filter para sa '%1'\"\nBasic.Filters.AddFilter.Title=\"Pangalan ng Filter\"\nBasic.Filters.AddFilter.Text=\"Mangyari lamang tukuyin ang pangalan ng filter\"\nBasic.TransformWindow=\"Pagsaanyo ng Bagay sa Tagpo\"\nBasic.TransformWindow.Position=\"Posisyon\"\nBasic.TransformWindow.PositionX=\"Katayuang X\"\nBasic.TransformWindow.PositionY=\"Katayuang Y\"\nBasic.TransformWindow.Rotation=\"Pag-ikot\"\nBasic.TransformWindow.Size=\"Sukat\"\nBasic.TransformWindow.Width=\"Lapad\"\nBasic.TransformWindow.Height=\"Taas\"\nBasic.TransformWindow.Alignment=\"Pagkahanay-hanay ng mga posisyon\"\nBasic.TransformWindow.BoundsType=\"Tipo ng Nakagagapos na Kahon\"\nBasic.TransformWindow.BoundsAlignment=\"Pagkakahanay sa Bounding Kahon\"\nBasic.TransformWindow.Bounds=\"Kahon Sukat ng Bounding\"\nBasic.TransformWindow.BoundsWidth=\"Lapad ng Makaratig na Kahon\"\nBasic.TransformWindow.BoundsHeight=\"Taas ng Makaratig na Kahon\"\nBasic.TransformWindow.Crop=\"I-krop\"\nBasic.TransformWindow.CropLeft=\"Gupitin sa Kaliwa\"\nBasic.TransformWindow.CropRight=\"Gupitin sa Kanan\"\nBasic.TransformWindow.CropTop=\"Gupitin sa Itaas\"\nBasic.TransformWindow.CropBottom=\"Gupitin sa Ibaba\"\nBasic.TransformWindow.Alignment.TopLeft=\"Itaas sa kaliwa\"\nBasic.TransformWindow.Alignment.TopCenter=\"Itaas sa Gitna\"\nBasic.TransformWindow.Alignment.TopRight=\"Itaas sa Kanan\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Gitna sa Kaliwa\"\nBasic.TransformWindow.Alignment.Center=\"Gitna\"\nBasic.TransformWindow.Alignment.CenterRight=\"Gitna sa Kanan\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Baba sa Kaliwa\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Baba sa Gitna\"\nBasic.TransformWindow.Alignment.BottomRight=\"Baba sa Kanan\"\nBasic.TransformWindow.BoundsType.None=\"Walang Hangganan\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Pinakamataas na sukat lamang\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Panloob na hangganan ng scale\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Panlabas na hangganan ng scale\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Lapad ng hangganan ng scale\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Taas ng hangganan ng scale\"\nBasic.TransformWindow.BoundsType.Stretch=\"Kahabaan sa hangganan\"\nBasic.TransformWindow.Title=\"Baguhin ang Pagsaanyo ng '%1'\"\nBasic.Main.AddSourceHelp.Title=\"Hindi pwede idagdag sa Pinagmulan\"\nBasic.Main.AddSourceHelp.Text=\"Kailangan mo na magkaroon ng hindi bababa sa isang eksena na idadagdag sa pinagmulan.\"\nBasic.Main.Scenes=\"Eksena\"\nBasic.Main.Sources=\"Pinagmulan\"\nBasic.Main.Controls=\"Mga kontrol\"\nBasic.Main.Connecting=\"Pagkonekta...\"\nBasic.Main.StartRecording=\"Magsimula sa Pagtatala\"\nBasic.Main.StartReplayBuffer=\"Magsimula Mag replay Buffer\"\nBasic.Main.SaveReplay=\"Iimbak ang Baliking Pagpapalabas\"\nBasic.Main.StartStreaming=\"Magsimula na mag Streaming\"\nBasic.Main.StartVirtualCam=\"Simulan ang Virtual Camera\"\nBasic.Main.StopRecording=\"Huminto sa Pagtatala\"\nBasic.Main.StoppingRecording=\"Pagtigil sa Pagtatala...\"\nBasic.Main.StopReplayBuffer=\"Huminto Mag-replay Buffer\"\nBasic.Main.StoppingReplayBuffer=\"Pagtigil Mag-replay Buffer...\"\nBasic.Main.StopStreaming=\"Ihinto Mag-streaming\"\nBasic.Main.StoppingStreaming=\"Pagtigil ng Daloy...\"\nBasic.Main.ForceStopStreaming=\"Itigil ang pag-stream (tanggalin ang antala)\"\nBasic.Main.Group=\"Pangkat %1\"\nBasic.Main.GroupItems=\"I-pangkat ang napiling mga aytem\"\nBasic.Main.Ungroup=\"Alisin sa Pagkakapangkat\"\nBasic.Main.GridMode=\"Pamamaraang Grid\"\nBasic.Main.ListMode=\"Pamamaraang Pa-lista\"\nBasic.MainMenu.File.Export=\"I-&export\"\nBasic.MainMenu.File.Import=\"&I-angkat\"\nBasic.MainMenu.File.ShowRecordings=\"Ipakita ang mga &Recording\"\nBasic.MainMenu.File.Remux=\"Re&mux na mga Recording\"\nBasic.MainMenu.File.Settings=\"Mga &Setting\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Ipakita ang Folder ng mga Setting\"\nBasic.MainMenu.File.ShowProfileFolder=\"Ipakita ang Profile Folder\"\nBasic.MainMenu.File.ShowMissingFiles=\"Sumuri para sa nawawalang files\"\nBasic.MainMenu.Edit=\"I&edit\"\nBasic.MainMenu.Edit.Undo=\"Ipawalang-bisa (&U)\"\nBasic.MainMenu.Edit.Redo=\"Gawin Muli (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Pa&lagiin ang Silip\"\nBasic.MainMenu.Edit.Scale=\"Pag&sasalinlaki ng Silip\"\nBasic.MainMenu.Edit.Scale.Window=\"Gawing Kasing-Laki ng Window\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Kanbas (%1x%2)\"\nBasic.MainMenu.Edit.Transform=\"Pagsaanyo (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Baguhin ang Pagsaanyo... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Sipiin ang Pagsaanyo\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Idikit ang Pagsaanyo\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Isauli ang Pagsaanyo (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Paikutin ng 90 pataknaang antas\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Paikutin ng 90 balitaknaang antas\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Paikutin ng 180 antas\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Baliktaring Pa&halang\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Baliktaring Patayo (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Pagkasyahin sa tabing (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Banatin sa tabing (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Igitna sa tabing (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"I-patayong Gitna\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"I-pahalang Gitna\"\nBasic.MainMenu.Edit.Order=\"Pagkakasun&od-sunod\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Ilipat Pataas (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Ilipat Pababa (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Ilipa&t sa Pinaka taas\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Ilipat sa Pinaka &baba\"\nBasic.MainMenu.Edit.AdvAudio=\"&Advanced na mga Katangian ng Audio\"\nBasic.MainMenu.View.Toolbars=\"Mga &Toolbar\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Interface gamit ang buong screen\"\nBasic.MainMenu.SceneCollection=\"Koleky&syon ng mga Scene\"\nBasic.MainMenu.Profile.Import=\"I-import ang Profile\"\nBasic.MainMenu.Profile.Export=\"I-export ang Profile\"\nBasic.MainMenu.SceneCollection.Import=\"I-import ang Koleksyon ng mga Eksena\"\nBasic.MainMenu.SceneCollection.Export=\"I-export ang Koleksyon ng mga Eksena\"\nBasic.MainMenu.SceneCollection.Migrate=\"Ilagay ang Batayang Hisikot\"\nBasic.MainMenu.Profile.Exists=\"May ganito ng Profile\"\nBasic.MainMenu.Tools=\"Mga Kasangkapan (&T)\"\nBasic.MainMenu.Help=\"Tulong (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"&Portal Para sa Tulong\"\nBasic.MainMenu.Help.Website=\"Bisitahin ang &Website\"\nBasic.MainMenu.Help.Logs=\"Mga &Log File\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Ipakita ang mga Log File (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"I-upload ang Pangkasalukuyang Log File (&C)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Tingnan ang Pangkasalukuyang Log (&V)\"\nBasic.MainMenu.Help.CheckForUpdates=\"Maghanap ng mga Update\"\nBasic.Settings.ProgramRestart=\"Ang programa ay kailangan i-start muli para gumana ang mga setting na ito.\"\nBasic.Settings.ConfirmTitle=\"Kumpirmahin ang mga Pagbabago\"\nBasic.Settings.Confirm=\"Mayroon kang mga binago na hindi pa naimbak. Iimbak ang mga pagbabago?\"\nBasic.Settings.General=\"Pangkalahatan\"\nBasic.Settings.General.Language=\"Wika\"\nBasic.Settings.General.EnableAutoUpdates=\"Awtomatikong maghanap ng mga update sa pag-start up\"\nBasic.Settings.General.OpenStatsOnStartup=\"Buksan ang dialogong panstatistiko sa pag-startup\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Ipakita ang kumpirmasyon ng dialogo kapag nagumpisang mag-stream\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Ipakita ang kumpirmasyon ng dialogo kapag naghihinto ng pag-stream\"\nBasic.Settings.General.Projectors=\"Mga Projector\"\nBasic.Settings.General.HideProjectorCursor=\"Itago ang cursor sa ibabaw ng mga projector\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Ilagay palagi sa tuktok ang mga projector\"\nBasic.Settings.General.Snapping=\"Pag-snap ng pagkakahanay ng source\"\nBasic.Settings.General.ScreenSnapping=\"I-snap ang mga source sa gilid ng screen\"\nBasic.Settings.General.CenterSnapping=\"I-snap ang mga source sa pahalang at patayong sentro\"\nBasic.Settings.General.SourceSnapping=\"I-snap ang mga Source sa iba pang mga source\"\nBasic.Settings.General.SnapDistance=\"I-snap ang pagka-sensitibo\"\nBasic.Settings.General.RecordWhenStreaming=\"Awtomatikong mag-rekord kapag nag-stream\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Patuloy na mag-rekord kapag tumigil ang pag-stream\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Awtomatikong simulang ang replay buffer kapag nag-stream\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Panatilihing aktibo ang replay buffer kapag huminto ang pagstream\"\nBasic.Settings.General.SysTrayWhenStarted=\"Paliitin sa system tray kapag inumpisahan\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Palaging paliitin sa system tray sa halip na task bar\"\nBasic.Settings.General.SaveProjectors=\"Iimbak ang mga projector sa paglabas\"\nBasic.Settings.General.Preview=\"Silip\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Lumipat sa eksena kapag dalawang beses pinindot\"\nBasic.Settings.General.StudioPortraitLayout=\"Paganahin ang portrait na/patayong ayos\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Ipakita ang tawagan ng mga silip/palatuntunan\"\nBasic.Settings.General.Multiview=\"Damin-tanaw\"\nBasic.Settings.General.MultiviewLayout=\"Damin-tanaw na Ayos\"\nBasic.Settings.Stream=\"Mag-stream\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Pinakahangganan ng Hisikot: %1\"\nBasic.Settings.Output.Format=\"Format ng Recording\"\nBasic.Settings.Output.SelectDirectory=\"Pumili ng Recording Directory\"\nBasic.Settings.Output.Mode=\"Paraan ng Output\"\nBasic.Settings.Output.Mode.Simple=\"Payak\"\nBasic.Settings.Output.Mode.Adv=\"Mas Mahusay\"\nBasic.Settings.Output.UseReplayBuffer=\"Paganahin ang Replay Buffer\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Tantiyang gamit sa memorya: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Hindi matantiya ang gamit sa memorya. Mangyari lamang mag-takda ng pinakamataas na limitasyon sa memorya.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Panlapi para Filename ng Replay Buffer\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Hulapi\"\nBasic.Settings.Output.Simple.SavePath=\"Landas ng Pagtatala\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Kalidad ng Recording\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Katulad ng pag-stream\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Mataas na Kalidad, Katamtamang Laki ng File\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Hindi Matukoy na Kalidad, Malaking File\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Walang nabago sa Kalidad, Lubhang napakalaking File\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Babal: Ang pag-rekord gamit ang isang software encoder na iba ang kalidad sa pag-stream ay mangangailangan ng dagdag na pag-gamit sa CPU kung ikaw ay mag-stream at mag-rekord nang sabay.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Sigurado ka bang gusto mong gamitin ang lossless na kalidad?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Lossless na kalidad babala!\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Software (x264 mababa ang nakatakdang paggamit ng CPU, dagdagan ang laki ng file)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Di-Akmang Hisikot o Framerate\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Hisikot: %1\"\nBasic.Settings.Output.Reconnect=\"Pakusa Makipagkonek muli\"\nBasic.Settings.Output.MaxRetries=\"Pinakamaraming Retries\"\nBasic.Settings.Output.CustomEncoderSettings=\"Pasadyang enkoder Mga setting\"\nBasic.Settings.Output.CustomMuxerSettings=\"Pasadyang mga Setting ng Muxer\"\nBasic.Settings.Output.NoSpaceFileName=\"Gumawa ng Pangalan ng File nang walang Pagitan\"\nBasic.Settings.Output.Adv.Rescale=\"I-re-iskala ang Output\"\nBasic.Settings.Output.Adv.AudioTrack=\"Pangsubaybay ng Audio\"\nBasic.Settings.Output.Adv.Streaming=\"Anod\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Subaybayan 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Subaybayan 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Subaybayan 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Subaybayan 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Subaybayan 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Subaybayan 6\"\nBasic.Settings.Output.Adv.Recording=\"Pagtatala\"\nBasic.Settings.Output.Adv.Recording.Type=\"Uri\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Pamantayan\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Pasadyang Palabas (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Gamitin ang stream encoder)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Pangalan ng File ng Pag-format\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Higit na pasulat kung ang file ay umiiral\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Ang Palabas na Uri ng FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Output sa URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Output sa File\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Mga karaniwang format sa pagtatala\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Lahat ng mga Talaksan\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Landas ng talaksan o URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Format ng Container\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Default na Format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Paglalarawan ng Container Format\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Audio/Video Codec na hinulaan mula sa landas ng Talaksan o URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Huwag paganahin ang Encoder\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Mga Setting para sa Video Encoder (kung mayroon)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Mga Setting ng Audio Encoder(kung mayroon)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Mga Setting ng Muxer (kung mayroon)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Agwat ng Keyframe (mga imahe)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Ipakita ang lahat ng mga code (kahit na maaaring hindi akma)\"\nScreenshot.StudioProgram=\"Iimbak ang Screenshot ng Palatuntunan\"\nScreenshot.Preview=\"Iimbak ang Siliping Screenshot\"\nBasic.Settings.Video.BaseResolution=\"Batayang (Kanbas) Hisikot\"\nBasic.Settings.Video.ScaledResolution=\"Output na (Scaled) Hisikot\"\nBasic.Settings.Video.DownscaleFilter=\"Pababaan ang Filter\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Tugmang mga hisikot, walang kinakailangang pagbabawas]\"\nBasic.Settings.Video.FPSCommon=\"Karaniwang mga FPS Value\"\nBasic.Settings.Video.FPSInteger=\"Integer ng FPS Value\"\nBasic.Settings.Video.FPSFraction=\"Praksyonal na FPS Value\"\nBasic.Settings.Video.Numerator=\"Numerador\"\nBasic.Settings.Video.Denominator=\"Denominador\"\nBasic.Settings.Video.InvalidResolution=\"Walang bisa ang value ng resolusyon. Kailangang [width]x[height] (hal. 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Kasalukuyang aktibo ang video output. Mangyari lamang patayin ang anumang mga output upang mabago ang mga video setting.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (Pinakamabilis, ngunit malabo sa paglaki)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (Matalas na pagsasanlaki, 16 na halimbagay)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Matalas na pagsasanlaki, 36 na halimbagay)\"\nBasic.Settings.Audio.Channels=\"Mga Channel\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Mabilis\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Mabagal (Type II PPM)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"BABALA: Naka-enable ang surround sound audio.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Mapapagana ba ang tunog ng palibot ng audio?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Sigurado ka ba na gusto mong paganahin ang tunog ng palibot ng audio?\"\nBasic.Settings.Audio.EnablePushToMute=\"Paganahin Itulak-para-ma-i-mute\"\nBasic.Settings.Audio.PushToMuteDelay=\"Pag-antala sa pagtulak-sa-walang tunog\"\nBasic.Settings.Audio.EnablePushToTalk=\"Paganahin itulak-sa-usapan\"\nBasic.Settings.Audio.PushToTalkDelay=\"Pag-antala sa pagtulak-sa-usapan\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Ang aparato ay hindi nakakonekta o hindi magagamit]\"\nBasic.Settings.Advanced=\"Pauna\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Prayoridad na Pagproseso\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Mataas\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Sa Taas ng Karaniwan\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Karaniwan\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Sa ibaba ng karaniwan\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Walang ginagawa\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Fomat ng Kulay\"\nBasic.Settings.Advanced.Video.ColorRange=\"Saklaw ng Kulay\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Buo\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Huwag paganahin ang audio ducking ng Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Antala sa Pag-stream\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Tagal\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Balikan kung saan huling pinutol (dagadagan ang antala) pagka konekta muli\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Tantiyang Gamit sa Memorya: %1 MB\"\nBasic.Settings.Advanced.Network.BindToIP=\"Bumigkis sa IP\"\nBasic.AdvAudio=\"Pinahusay na mga Katangian ng Audio\"\nBasic.AdvAudio.Name=\"Pangalan\"\nBasic.AdvAudio.Balance=\"Balanse\"\nBasic.AdvAudio.Monitoring=\"Pagsubaybay sa Audio\"\nBasic.AdvAudio.Monitoring.None=\"Naka-off ang Monitor\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Monitor Lamang (naka-mute na output)\"\nBasic.AdvAudio.Monitoring.Both=\"Monitor at Output\"\nBasic.AdvAudio.AudioTracks=\"Mga Track\"\nBasic.Settings.Hotkeys=\"Mga Hotkey\"\nBasic.Settings.Hotkeys.Pair=\"Ang mga kombinasyon ng mga key kasama ang '%1' ay nagsisilbing mga toggle\"\nBasic.Hotkeys.SelectScene=\"Lumipat sa eksena\"\nBasic.SystemTray.Show=\"Ipakita\"\nBasic.SystemTray.Hide=\"Itago\"\nBasic.SystemTray.Message.Reconnecting=\"Nadiskonekta. Kumukonekta muli...\"\nHotkeys.Insert=\"Isingit\"\nHotkeys.Delete=\"Burahin\"\nHotkeys.End=\"Wakas\"\nHotkeys.PageUp=\"Itaas ng Pahina\"\nHotkeys.PageDown=\"Ibaba ng Pahina\"\nHotkeys.Print=\"Ilimbag\"\nHotkeys.Pause=\"Ihinto\"\nHotkeys.Left=\"Kaliwa\"\nHotkeys.Right=\"Kanan\"\nHotkeys.Up=\"Taas\"\nHotkeys.Down=\"Baba\"\nHotkeys.Space=\"Espasyo\"\nMute=\"I-mute\"\nUnmute=\"Ibalik ang Tunog\"\nPush-to-mute=\"Pindutin-para-i-mute\"\nPush-to-talk=\"Pindutin-para-makipagusap\"\nSceneItemShow=\"Ipakita ang '%1'\"\nSceneItemHide=\"Itago '%1'\"\nOutputWarnings.NoTracksSelected=\"Kailangan mong pumili ng kahit isang track\"\nFinalScene.Title=\"Burahin ang Eksena\"\nFinalScene.Text=\"Kailangan mayroon kahit isang eksena.\"\nNoSources.Title=\"Walang Pinagtinututukan\"\nChangeBG=\"I-takda ang Kulay\"\nAbout=\"Tungkol dito\"\nAbout.Info=\"Ang OBS Studio ay isang libre at open source na software para sa parerercord ng bidyo at pag-iistream nang live.\"\nAbout.Authors=\"Mga May-akda\"\nAbout.License=\"Lisensya\"\nResizeOutputSizeOfSource.Text=\"Magiging magkasinlaki ang batayan at output na hisikot sa kasalukuyang pagtututok.\"\nPreviewTransition=\"Silipin ang Pagbabai-baitang\"\nImporter.Path=\"Landas ng Pagtitipon\"\nContextBar.MediaControls.StopMedia=\"I-tigil ang Media\"\nContextBar.MediaControls.RestartMedia=\"Ulitin ang Media\"\nYouTube.Auth.WaitingAuth.Title=\"Pagpapahintulot sa Paggamit sa Youtube\"\nYouTube.Actions.Stream.ScheduledFor=\"Nakatakda sa %1\"\n"
  },
  {
    "path": "frontend/data/locale/tr-TR.ini",
    "content": "Language=\"Türkçe\"\nOK=\"Tamam\"\nApply=\"Uygula\"\nCancel=\"İptal\"\nClose=\"Kapat\"\nSave=\"Kaydet\"\nDiscard=\"Vazgeç\"\nDisable=\"Devre Dışı Bırak\"\nYes=\"Evet\"\nNo=\"Hayır\"\nAdd=\"Ekle\"\nRemove=\"Kaldır\"\nRename=\"Yeniden Adlandır\"\nInteract=\"Etkileşim\"\nFilters=\"Filtreler\"\nProperties=\"Özellikler\"\nMoveUp=\"Yukarı Taşı\"\nMoveDown=\"Aşağı Taşı\"\nSettings=\"Ayarlar\"\nDisplay=\"Ekran\"\nName=\"İsim\"\nExit=\"Çık\"\nMixer=\"Ses Karıştırıcı\"\nBrowse=\"Gözat\"\nMono=\"Tek Kanallı Ses\"\nDroppedFrames=\"Kaybedilen Kareler %1 (%%2)\"\nProjector.Open.Program=\"Program Projektörünü Aç\"\nProjector.Open.Preview=\"Önizleme Projektörünü Aç\"\nProjector.Open.Scene=\"Sahne Projektörünü Aç\"\nProjector.Open.Source=\"Kaynak Projektörünü Aç\"\nProjector.Open.Multiview=\"Çoklu Görünümü Aç\"\nProjector.Display=\"Ekran: %1\"\nProjector.Window=\"Yeni pencere\"\nProjector.Title=\"Projektör\"\nProjector.Title.Scene=\"Sahne: %1\"\nProjector.Title.Source=\"Kaynak: %1\"\nProjector.Title.Multiview=\"Çoklu görüntü\"\nProjector.ResizeWindowToContent=\"Pencereyi içeriğe sığdır\"\nClear=\"Temizle\"\nRevert=\"Geri Al\"\nShow=\"Göster\"\nHide=\"Gizle\"\nUnhideAll=\"Tümünü Göster\"\nUntitled=\"Başlıksız\"\nNew=\"Yeni\"\nDuplicate=\"Çoğalt\"\nEnable=\"Etkinleştir\"\nDisableOSXVSync=\"macOS V-Sync'i Devre Dışı Bırak\"\nResetOSXVSyncOnExit=\"macOS V-Sync'i Çıkışta Sıfırla\"\nHighResourceUsage=\"Kodlama aşırı yüklendi! Video ayarlarını kapatmayı veya daha hızlı bir kodlama ön ayarını kullanmayı düşünün.\"\nTransition=\"Geçiş\"\nQuickTransitions=\"Hızlı Geçişler\"\nFadeToBlack=\"Karanlığa Geç\"\nLeft=\"Sol\"\nRight=\"Sağ\"\nTop=\"Üst\"\nBottom=\"Alt\"\nReset=\"Sıfırla\"\nHours=\"Saat\"\nMinutes=\"Dakika\"\nSeconds=\"Saniye\"\nDeprecated=\"Kullanım dışı\"\nReplayBuffer=\"Tekrar Oynatma Arabelleği\"\nImport=\"İçe Aktar\"\nExport=\"Dışa Aktar\"\nCopy=\"Kopyala\"\nPaste=\"Yapıştır\"\nPasteReference=\"Yapıştır (Referans)\"\nPasteDuplicate=\"Yapıştır (Çoğalt)\"\nRemuxRecordings=\"Kayıtları Remux Et\"\nNext=\"İleri\"\nBack=\"Geri\"\nDefaults=\"Varsayılanlar\"\nRestoreDefaults=\"Varsayılanlar\"\nHideMixer=\"Karıştırıcıda Gizle\"\nTransitionOverride=\"Geçişi Etkisiz Kıl\"\nShowTransition=\"Geçişi Göster\"\nHideTransition=\"Geçişi Gizle\"\nNone=\"Hiçbiri\"\nStudioMode.Preview=\"Önizleme\"\nStudioMode.PreviewSceneName=\"Önizleme: %1\"\nShowInMultiview=\"Çoklu Ekranda Göster\"\nVerticalLayout=\"Dikey Düzen\"\nGroup=\"Grup\"\nDoNotShowAgain=\"Tekrar gösterme\"\nDefault=\"(Varsayılan)\"\nCalculating=\"Hesaplanıyor...\"\nFullscreen=\"Tam Ekran\"\nWindowed=\"Pencereli\"\nRefreshBrowser=\"Yenile\"\nAspectRatio=\"En-Boy Oranı <b>%1:%2</b>\"\nLockVolume=\"Ses Düzeyini Kilitle\"\nLogViewer=\"Günlük Görüntüleyici\"\nShowOnStartup=\"Başlangıçta göster\"\nOpenFile=\"Dosya aç\"\nAddScene=\"Sahne Ekle\"\nAddSource=\"Kaynak ekle\"\nRemoveScene=\"Seçili Sahneyi Kaldır\"\nRemoveSource=\"Seçili Kaynakları Kaldır\"\nMoveSceneUp=\"Sahneyi Yukarı Taşı\"\nMoveSceneDown=\"Sahneyi Aşağı Taşı\"\nMoveSourceUp=\"Kaynak(lar)ı Yukarı Taşı\"\nMoveSourceDown=\"Kaynak(lar)ı Aşağı Taşı\"\nSourceProperties=\"Kaynak özelliklerini aç\"\nSourceFilters=\"Kaynak filtrelerini aç\"\nMixerToolbarMenu=\"Ses Karıştırıcısı Menüsü\"\nSceneFilters=\"Sahne Filtrelerini Aç\"\nList=\"Liste\"\nGrid=\"Izgara\"\nAutomatic=\"Otomatik\"\nComingSoon=\"Yakında\"\nPluginsFailedToLoad.Title=\"Eklenti Yükleme Hatası\"\nPluginsFailedToLoad.Text=\"Aşağıdaki OBS eklentileri yüklenemedi:\\n\\n%1\\nLütfen bu eklentileri güncelleyin veya silin.\"\nAlreadyRunning.Title=\"OBS zaten çalışıyor\"\nAlreadyRunning.Text=\"OBS zaten çalışıyor! Eğer bunu yapmak istemediyseniz, lütfen yeni bir objeyi çalıştırmayı denemeden önce varolan tüm OBS objelerini kapatın. OBS'yi sistem tepsisine küçülmesi için ayarladıysanız, lütfen hala çalışıp çalışmadığını görmek için orayı kontrol edin.\"\nAlreadyRunning.LaunchAnyway=\"Yine de Başlat\"\nCrashHandling.Dialog.Title=\"OBS Studio Çökmesi Tespit Edildi\"\nCrashHandling.Labels.Text=\"OBS Studio düzgün şekilde kapanmadı.\\n\\nGüvenli Mod'da çalıştırın (üçüncü taraf eklentileri, komut dosyası oluşturma ve WebSockets devre dışı)?\"\nCrashHandling.Labels.PrivacyNotice=\"En son çökme raporunu otomatik olarak OBSProject'e yüklemeyi de seçebilirsiniz.<br /><br />Lütfen herhangi bir dosya yüklemeden önce <a href='https://obsproject.com/privacy-policy'>Gizlilik Politikası</a>'nı okuyun ve dosya yüklemeleriyle ilgili bölümlere özellikle dikkat edin.\"\nCrashHandling.Checkbox.SendReport=\"Gizlilik politikasını okudum ve yüklemeye izin veriyorum.\"\nCrashHandling.Buttons.LaunchSafe=\"Güvenli Mod'da başlat\"\nCrashHandling.Buttons.LaunchNormal=\"Normal Mod'da başlat\"\nCrashHandling.Errors.UploadJSONError=\"En son çökme günlüğünü yüklemeye çalışırken bir hata oluştu.\"\nCrashHandling.Errors.Title=\"Çökme Günlüğü Yükleme Hatası\"\nSafeMode.Restart=\"OBS'i Güvenli Mod'da yeniden başlatmak ister misin? (3. parti eklentiler, betikler ve WebSocketler kapalı şekilde)\"\nSafeMode.RestartNormal=\"OBS'i Normal Mod'da yeniden başlatmak ister misin?\"\nChromeOS.Title=\"Desteklenmeyen Platform\"\nChromeOS.Text=\"OBS bir ChromeOS kapsayıcısının içinde çalışıyor gibi görünüyor. Bu platform desteklenmiyor.\"\nWine.Title=\"Wine algılandı\"\nWine.Text=\"OBS, Wine'da çalıştırılmayı desteklemiyor ve yakalama veya aygıt kaynakları gibi birçok özellik çalışmayacak veya yalnızca sınırlı kapasitede çalışacak.<br><br>Bunun yerine OBS'nin yerel bir sürümünü çalıştırmanız önerilir, örneğin <a href='https://flathub.org/apps/details/com.obsproject.Studio'>Flatpak sürümümüz</a> veya işletim sisteminizin paketleri.\"\nDockCloseWarning.Title=\"Yerleştirilebilir Pencere Kapatma\"\nDockCloseWarning.Text=\"Az önce yerleştirilebilir bir pencere kapattınız. Tekrar göstermek istiyorsanız, menü çubuğundan Paneller menüsünü kullanın.\"\nExtraBrowsers=\"Özel Tarayıcı Yuvaları\"\nExtraBrowsers.Info=\"Bir isim veya bağlantı adresi belirterek yuva ekleyin, ardından yuvaları açmak için Uygula veya Kapat'a tıklayın. İstediğiniz zaman yuva ekleyebilir ya da kaldırabilirsiniz.\"\nExtraBrowsers.DockName=\"Yuva Adı\"\nAuth.Authing.Title=\"Kimlik doğrulanıyor...\"\nAuth.Authing.Text=\"%1 ile doğrulanıyor, lütfen bekleyin...\"\nAuth.AuthFailure.Title=\"Kimlik Doğrulaması Başarısız\"\nAuth.AuthFailure.Text=\"%1 kimliği doğrulanamadı:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Kimlik Doğrulaması Gerekli\"\nAuth.InvalidScope.Text=\"%1 için kimlik doğrulama gereksinimleri değişti. Bazı özellikler kullanılamayabilir.\"\nAuth.LoadingChannel.Title=\"Kanal bilgileri yükleniyor...\"\nAuth.LoadingChannel.Text=\"%1 için kanal bilgileri yükleniyor, lütfen bekleyin...\"\nAuth.LoadingChannel.Error=\"Kanal bilgisi alınamadı.\"\nAuth.ChannelFailure.Title=\"Kanal yüklenemedi\"\nAuth.ChannelFailure.Text=\"%1 için kanal bilgisi yüklenemedi\\n\\n%2: %3\"\nAuth.Chat=\"Sohbet\"\nAuth.StreamInfo=\"Yayın Bilgisi\"\nTwitchAuth.Stats=\"Twitch İstatistikleri\"\nTwitchAuth.Feed=\"Twitch Etkinlik akışı\"\nTwitchAuth.TwoFactorFail.Title=\"Yayın anahtarı sorgulanamadı\"\nTwitchAuth.TwoFactorFail.Text=\"OBS Twitch hesabına bağlanamadı. Lütfen iki-faktörlü doğrulamanın <a href='https://www.twitch.tv/settings/security'>Twitch güvenlik ayarlarında ayarlandığından emin olun </a> Bu yayın yapmanız için gereklidir.\"\nRestreamAuth.Channels=\"Restream Kanalları\"\nCopy.Filters=\"Filtreleri Kopyala\"\nPaste.Filters=\"Filtreleri Yapıştır\"\nBrowserPanelInit.Title=\"Tarayıcı başlatılıyor...\"\nBrowserPanelInit.Text=\"Tarayıcı başlatılıyor, lütfen bekleyin...\"\nBandwidthTest.Region=\"Bölge\"\nBandwidthTest.Region.US=\"Amerika Birleşik Devletleri\"\nBandwidthTest.Region.EU=\"Avrupa\"\nBandwidthTest.Region.Asia=\"Asya\"\nBandwidthTest.Region.Other=\"Diğer\"\nBasic.AutoConfig=\"Otomatik Yapılandırma Sihirbazı\"\nBasic.AutoConfig.ApplySettings=\"Ayarları Uygula\"\nBasic.AutoConfig.StartPage=\"Kullanım Bilgileri\"\nBasic.AutoConfig.StartPage.SubTitle=\"Programı ne için kullanmak istediğinizi belirtin\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Yayın için en iyi hale getir, kayıt ikinci sırada\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Sadece kayıt için en iyi hale getir, yayın yapmayacağım\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Sadece sanal kamerayı kullanacağım\"\nBasic.AutoConfig.VideoPage=\"Video Ayarları\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Kullanmak istediğiniz video ayarlarını belirtin\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Şimdikini Kullan (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Ekran %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Şimdikini Kullan (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ya da 30, ama mümkünse 60'ı tercih et\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ya da 30, ama yüksek çözünürlüğü tercih et\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Not: Tuval (taban) çözünürlüğü yayın veya kayıt yapacağınız çözünürlük ile aynı olmak zorunda değildir. Gerçek yayın/kayıt çözünürlüğünüz kaynak kullanımı ve bit hızı gereksinimlerini düşürmek için tuval çözünürlüğünüzden aşağıya boyutlandırılabilir.\"\nBasic.AutoConfig.StreamPage=\"Yayın Bilgisi\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Lütfen yayın bilginizi girin\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Hesap Bağla (önerilir)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Hesap Bağlantısını Kes\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Hesap Bağlantısı Kesilsin Mi?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Bu değişiklik hemen uygulanacaktır. Hesabınızın bağlantısını kesmek istediğinize emin misiniz?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Yayın Anahtarını Al\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Daha Fazla Bilgi\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Yayın Anahtarını Kullan\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Yayın Anahtarını Kullan (gelişmiş)\"\nBasic.AutoConfig.StreamPage.Service=\"Servis\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Tümünü Göster...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Özel...\"\nBasic.AutoConfig.StreamPage.Server=\"Sunucu\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Yayın Anahtarı\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: şifreleme parolasını girin.\\nRTMP: hizmet tarafından sağlanan anahtarı girin.\\nSRT: hizmette mevcut ise akış kimliğini girin.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Kodlayıcı Anahtarı\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Bağlı hesap\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Bant genişliği testi ile bit hızını tahmin et (birkaç dakika alabilir)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Donanım kodlamayı tercih et\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Donanım Kodlama çoğu CPU kullanımını ortadan kaldırır, ancak aynı kalite düzeyini elde etmek için daha fazla bit hızı gerektirebilir.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Yayın uyarısı\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Bant genişliği testi kanalınızda ses olmayan rastgele video verisi yayını yapmak üzere. Yapabiliyorsanız, test bitene kadar geçici bir süre yayın kaydını kapatmanız ve yayını gizli olarak ayarlamanız önerilir. Devam edilsin mi?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Deneme %1\"\nBasic.AutoConfig.TestPage=\"Sonuçlar\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Program en uygun ayarları tahmin etmek için şimdi bir dizi test yürütüyor\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Test tamamlandı\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Bant genişliği testi yapılıyor, bu birkaç dakika alabilir...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Bu protokol servisi için hiç output bulunamadı\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Bağlanılıyor: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Herhangi bir sunucuya bağlanamıyor, lütfen internet bağlantınızı kontrol edin ve tekrar deneyin.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Bant genişliği test ediliyor: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Yayın kodlayıcı test ediliyor, bu bir dakika alabilir...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Kayıt kodlayıcı test ediliyor, bu bir dakika alabilir...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Kodlayıcı başlatılamadı\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"%1x%2 %3 FPS test ediliyor...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Yayın Kodlayıcı\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Kayıt Kodlayıcı\"\nBasic.AutoConfig.TestPage.Result.Header=\"Program tahmin edilen bu ayarların sizin için en uygunu olduğunu belirledi:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Bu ayarları kullanmak için, Ayarları Uygula'ya tıklayın. Sihirbazı yeniden yapılandırmak ve denemek için Geri'ye tıklayın. Ayarları kendiniz elle yapılandırmak için İptal'e tıklayın ve Ayarlar'ı açın.\"\nBasic.AutoConfig.Info=\"Otomatik yapılandırma sihirbazı, bilgisayar özellikleriniz ve internet hızınıza göre en iyi ayarlara karar verecektir.\"\nBasic.AutoConfig.RunAnytime=\"Bu, Araçlar menüsüne gidilerek herhangi bir zaman çalıştırılabilir.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Yayın (Ölçeklendirilmiş) Çözünürlüğü\"\nBasic.Stats=\"İstatistikler\"\nBasic.Stats.CPUUsage=\"CPU Kullanımı\"\nBasic.Stats.HDDSpaceAvailable=\"Mevcut disk alanı\"\nBasic.Stats.MemoryUsage=\"Bellek Kullanımı\"\nBasic.Stats.AverageTimeToRender=\"Kare işleme için ortalama süre\"\nBasic.Stats.SkippedFrames=\"Kodlama gecikmesi nedeniyle atlanan kareler\"\nBasic.Stats.MissedFrames=\"İşleme gecikmesi nedeniyle kaçan kareler\"\nBasic.Stats.Output.Stream=\"Yayın\"\nBasic.Stats.Output.Recording=\"Kayıt\"\nBasic.Stats.Status=\"Durum\"\nBasic.Stats.Status.Recording=\"Kaydediliyor\"\nBasic.Stats.Status.Live=\"CANLI\"\nBasic.Stats.Status.Reconnecting=\"Yeniden bağlanıyor\"\nBasic.Stats.Status.Inactive=\"Etkin değil\"\nBasic.Stats.Status.Active=\"Etkin\"\nBasic.Stats.DroppedFrames=\"Atlanan Kareler (Ağ)\"\nBasic.Stats.MegabytesSent=\"Toplam Veri Çıkışı\"\nBasic.Stats.Bitrate=\"Bit Hızı\"\nBasic.Stats.DiskFullIn=\"Disk dolacak (yaklaşık)\"\nBasic.Stats.DiskFullIn.Text=\"%1 Saatler, %2 Dakikalar\"\nBasic.Stats.ResetStats=\"İstatistikleri Sıfırla\"\nResetUIWarning.Title=\"Kullanıcı arayüzünü sıfırlamak istediğinize emin misiniz?\"\nResetUIWarning.Text=\"Kullanıcı arayüzünü sıfırlamak, ek panelleri gizleyecektir. Panellerin görünmelerini istiyorsanız, bu panelleri Paneller menüsünden etkinleştirebilirsiniz.\\n\\nKullanıcı arayüzünü sıfırlamak istediğinize emin misiniz?\"\nUpdater.Title=\"Yeni güncelleme mevcut\"\nUpdater.Text=\"Mevcut yeni bir güncelleme var:\"\nUpdater.UpdateNow=\"Şimdi Güncelle\"\nUpdater.RemindMeLater=\"Daha Sonra Hatırlat\"\nUpdater.Skip=\"Sürümü Atla\"\nUpdater.NoUpdatesAvailable.Title=\"Güncelleme mevcut değil\"\nUpdater.NoUpdatesAvailable.Text=\"Şu anda mevcut hiçbir güncelleme yok\"\nUpdater.BranchNotFound.Title=\"Güncelleme Kanalı Kaldırıldı\"\nUpdater.BranchNotFound.Text=\"Seçtiğiniz güncelleme kanalı artık mevcut değil. OBS varsayılan ayarlara döndürüldü.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Bozukluk Kontrolu Kullanılamıyor\"\nUpdater.RepairButUpdatesAvailable.Text=\"Dosya bozukluğu kontrolü sadece en son sürüm için mümkündür. OBS sürümünüzü doğrulamak ve güncellemek için Yardım → Güncellemeleri Kontrol Et seçeneğine gidin.\"\nUpdater.RepairConfirm.Title=\"Bozukluk Kontrolünü Onayla\"\nUpdater.RepairConfirm.Text=\"Bozukluk kontrolünü başlatmak, OBS kurulumunuzu bozulmaya karşı tarayacak ve bozuk/değiştirilmiş dosyaları yeniden indirecektir. Bu biraz zaman alabilir.\\n\\nDevam etmek istiyor musunuz?\"\nUpdater.FailedToLaunch=\"Güncelleyici başlatılamadı\"\nQuickTransitions.SwapScenes=\"Geçişten Sonra Önizleme/Çıkış Sahnelerini Değiştir\"\nQuickTransitions.SwapScenesTT=\"Geçişten sonra çıkış ve ön izleme sahnelerinin yerini değiştirir (çıkışın orijinal sahnesi hala mevcutsa).\\n Bu, çıkışın orijinal sahnesine yapılan değişiklikleri geri almayacaktır.\"\nQuickTransitions.DuplicateScene=\"Sahneyi Çoğalt\"\nQuickTransitions.DuplicateSceneTT=\"Aynı sahneyi düzenlerken, program çıkışını değiştirmeden kaynakların dönüşümünü veya görünülürlüğünü düzenlemeye izin verir.\\nKaynakların özelliklerini program çıkışını değiştirmeden düzenlemek için 'Kaynakları Çoğalt'ı etkinleştirin.\\nBu değeri değiştirmek mevcut program sahnesini sıfırlar (hala varsa).\"\nQuickTransitions.EditProperties=\"Kaynakları Çoğalt\"\nQuickTransitions.EditPropertiesTT=\"Aynı sahneyi düzenlerken, program çıkışını değiştirmeden kaynakların özelliklerinin düzenlenmesine izin verir.\\nSadece 'Sahneyi Çoğalt' etkinse kullanılabilir.\\nBelli kaynaklar (yakalama ve medya kaynakları gibi) bunu desteklemez ve ayrı olarak düzenlenemez.\\nBu değeri değiştirmek mevcut program sahnesini sıfırlar (hala varsa).\\n\\nUyarı: Kaynaklar çoğaltılacağından, ek sistem ve video kaynağı gerektirebilir.\"\nQuickTransitions.HotkeyName=\"Hızlı Geçiş: %1\"\nBasic.AddTransition=\"Yapılandırılabilir Geçiş Ekle\"\nBasic.RemoveTransition=\"Yapılandırılabilir Geçişi Kaldır\"\nBasic.TransitionProperties=\"Geçiş Özellikleri\"\nBasic.SceneTransitions=\"Sahne Geçişleri\"\nBasic.TransitionDuration=\"Süre\"\nBasic.TogglePreviewProgramMode=\"Stüdyo Modu\"\nBasic.EnablePreviewProgramMode=\"Stüdyo Modunu Etkinleştir\"\nBasic.DisablePreviewProgramMode=\"Stüdyo Modunu Devre Dışı Bırak\"\nUndo.Undo=\"Geri Al\"\nUndo.Redo=\"Yinele\"\nUndo.Add=\"`%1` Ekle\"\nUndo.Delete=\"`%1` Sil\"\nUndo.Rename=\"`%1` Yeniden Adlandır\"\nUndo.SceneCollection.Switch=\"`%1` Geçiş Yap\"\nUndo.Item.Undo=\"Geri Al: %1\"\nUndo.Item.Redo=\"%1 Yinele\"\nUndo.Sources.Multi=\"%1 Kaynağı Sil\"\nUndo.Filters=\"`%1` Üzerindeki Filtre Değişiklikleri\"\nUndo.Filters.Paste.Single=\"Filtreleri %1 konumundan %2 konumuna yapıştır\"\nUndo.Filters.Paste.Multiple=\"Filtreleri %1 konumundan %2 konumuna kopyala\"\nUndo.Transform=\"`%1` Üzerindeki Kaynak Değişimi\"\nUndo.Transform.Paste=\"`%1` Üzerinde Geçiş Yapıştırma\"\nUndo.Transform.Rotate=\"`%1` Üzerindeki Döndürme\"\nUndo.Transform.Reset=\"`%1` Üzerindeki Değişim Sıfırlama\"\nUndo.Transform.HFlip=\"`%1` Üzerindeki Yatay Çevirme\"\nUndo.Transform.VFlip=\"`%1` Üzerinde Yatay Çevirme\"\nUndo.Transform.FitToScreen=\"`%1` Üzeride Ekrana Sığdırma\"\nUndo.Transform.StretchToScreen=\"`%1` Üzerinde Ekrana Genişletme\"\nUndo.Transform.Center=\"`%1` Üzerinde Ekrana Ortalama\"\nUndo.Transform.VCenter=\"`%1` Üzeride Ekrana Dikey Ortalama\"\nUndo.Transform.HCenter=\"`%1` Üzerinde Ekrana Yatay Ortalama\"\nUndo.Volume.Change=\"`%1` Üzerinde Ses Seviyesi Değişimi\"\nUndo.Volume.Mute=\"'%1' Sessize Al\"\nUndo.Volume.Unmute=\"'%1' Sesini Aç\"\nUndo.Balance.Change=\"%1'de ses dengesi değişikliği\"\nUndo.SyncOffset.Change=\"%1'de ses senkronizasyon değişikliği\"\nUndo.MonitoringType.Change=\"%1'de ses izlemeyi değiştir\"\nUndo.Mixers.Change=\"%1'de ses karışımını değiştir\"\nUndo.ForceMono.On=\"%1'de Force Mono'yu aç\"\nUndo.ForceMono.Off=\"%1'de Force Mono'yu kapat\"\nUndo.Properties=\"`%1` Üzerindeki Özellik Değişimi\"\nUndo.Scene.Duplicate=\"`%1` Sahnesini Çoğaltmayı\"\nUndo.ShowTransition=\"'%1' Geçişini Göster\"\nUndo.HideTransition=\"'%1' Geçişini Gizle\"\nUndo.ShowSceneItem=\"'%2' içinde '%1' Göster\"\nUndo.HideSceneItem=\"'%2' içinde '%1' Gizle\"\nUndo.ReorderSources=\"Kaynakları %1 içinde yeniden sırala\"\nUndo.MoveUp=\"'%2' içindeki '%1' yukarı taşıması\"\nUndo.MoveDown=\"'%2' içindeki '%1' aşağı taşıması\"\nUndo.MoveToTop=\"'%2' içindeki '%1' en üste taşıması\"\nUndo.MoveToBottom=\"'%2' içindeki '%1' en alta taşıması\"\nUndo.PasteSource=\"'%1' İçindeki Kaynakları Yapıştırma\"\nUndo.PasteSourceRef=\"'%1' İçindeki Kaynak Referanslarını Yapıştırma\"\nUndo.GroupItems=\"Nesneleri '%1' içine grupla\"\nTransitionNameDlg.Text=\"Lütfen geçiş adını girin\"\nTransitionNameDlg.Title=\"Geçiş Adı\"\nTitleBar.SafeMode=\"GÜVENLİ MOD\"\nTitleBar.PortableMode=\"Taşınabilir Mod\"\nTitleBar.Profile=\"Profil\"\nTitleBar.Scenes=\"Sahneler\"\nNameExists.Title=\"Bu ad zaten kullanılıyor\"\nNameExists.Text=\"Bu ad zaten kullanılıyor.\"\nNoNameEntered.Title=\"Lütfen geçerli bir ad girin\"\nNoNameEntered.Text=\"İsim boş olamaz.\"\nConfirmStart.Title=\"Yayın Başlatılsın Mı?\"\nConfirmStart.Text=\"Yayını gerçekten başlatmak istiyor musunuz?\"\nConfirmStop.Title=\"Yayın Durdurulsun Mu?\"\nConfirmStop.Text=\"Yayını gerçekten durdurmak istiyor musunuz?\"\nConfirmStopRecord.Title=\"Kayıt Durdurulsun Mu?\"\nConfirmStopRecord.Text=\"Kaydı durdurmak istediğinizden emin misiniz?\"\nConfirmBWTest.Title=\"Bant genişliği testini başlat?\"\nConfirmBWTest.Text=\"OBS'i bant genişliği test modunda yapılandırdınız. Bu mod, kanalınız yayına girmeden ağ testi yapmanıza olanak sağlar. Test tamamlandıktan sonra, izleyicilerin yayını görebilmesi için bu modu devre dışı bırakmanız gerekecektir.\\n\\nDevam etmek istiyor musunuz?\"\nConfirmExit.Title=\"Aktif Çıktılar\"\nConfirmExit.Text=\"OBS şu anda hala aktif. Tüm yayınlar/kayıtlar kapatılacak.\"\nConfirmRemove.Title=\"Kaldırmayı Onayla\"\nConfirmRemove.Text=\"'%1' ögesini kaldırmak istediğinizden emin misiniz?\"\nConfirmRemove.TextMultiple=\"%1 ögeyi kaldırmak istediğinizden emin misiniz?\"\nConfirmReset.Title=\"Özellikleri Sıfırla\"\nConfirmReset.Text=\"Mevcut özellikleri varsayılan değerlerine sıfırlamak istediğinizden emin misiniz?\"\nOutput.StartStreamFailed=\"Yayın başlatılamadı\"\nOutput.StartRecordingFailed=\"Kayıt başlatılamadı\"\nOutput.StartReplayFailed=\"Tekrar oynatma arabelleği başlatılamadı\"\nOutput.StartVirtualCamFailed=\"Sanal kamera başlatılamadı\"\nOutput.StartFailedGeneric=\"Çıkışı başlatma başarısız oldu. Detaylar için lütfen günlüğe bakın: \\n\\nNVENC veya AMD kodlayıcılarını kullanıyorsanız, video sürücülerinin güncel olduğundan emin olun.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Durdurulduğunda tekrarlar kaydedilemez\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Dikkat: Kayıt durdurulmuşken tekrarlar kaydedilemez.\"\nOutput.ConnectFail.Title=\"Bağlantı kurulamadı\"\nOutput.ConnectFail.BadPath=\"Geçersiz yol veya bağlantı adresi. Geçerli olduğunu doğrulamak için lütfen ayarlarınızı kontrol edin.\"\nOutput.ConnectFail.ConnectFailed=\"Sunucuya bağlanılamadı\"\nOutput.ConnectFail.InvalidStream=\"Belirtilen kanal veya yayın anahtarına erişilemedi, lütfen yayın anahtarınızı iyi kontrol edin. Eğer doğruysa, sunucuya bağlanırken sorun oluyor olabilir.\"\nOutput.ConnectFail.HdrDisabled=\"Bu dışa aktarım için HDR şimdilik kapalı.\"\nOutput.ConnectFail.Error=\"Sunucuya bağlanmaya çalışırken beklenmeyen bir hata oluştu. Daha fazla bilgi için günlük dosyasına bakınız.\"\nOutput.ConnectFail.Disconnected=\"Sunucu bağlantısı kesildi.\"\nOutput.StreamEncodeError.Title=\"Kodlama hatası\"\nOutput.StreamEncodeError.Msg=\"Yayın sırasında bir kodlayıcı hatası oluştu.\"\nOutput.StreamEncodeError.Msg.LastError=\"Akış sırasında bir kodlayıcı hatası oluştu:<br><br>%1\"\nOutput.RecordFail.Title=\"Kayıt başlatılamadı\"\nOutput.RecordFail.Unsupported=\"Çıkış biçimi ya desteklenmiyor ya da birden fazla sesi desteklemiyor. Lütfen ayarlarınızı kontrol edip tekrar deneyin.\"\nOutput.RecordNoSpace.Title=\"Yetersiz disk alanı\"\nOutput.RecordNoSpace.Msg=\"Kayıt'aa devam etmek yeterli disk alanı yok.\"\nOutput.RecordError.Title=\"Kayıt Hatası\"\nOutput.RecordError.Msg=\"Kayıt anında bir hata oluştu.\"\nOutput.RecordError.EncodeErrorMsg=\"Kayıt sırasında bir kodlayıcı hatası oluştu.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Kayıt sırasında bir kodlayıcı hatası oluştu:<br><br>%1\"\nOutput.BadPath.Title=\"Dosya Yolu Geçersiz\"\nOutput.BadPath.Text=\"Yapılandırılmış Kayıt Yolu açılamadı. Lütfen Ayarlar → Çıktı → Kayıt içerisinden Kayıt Yolu'nu denetleyin.\"\nOutput.NoBroadcast.Title=\"Hiçbir yayın ayarlanmadı\"\nOutput.NoBroadcast.Text=\"Yayınlamaya başlamadan önce yayın ayarlarını yapmalısınız.\"\nOutput.BroadcastStartFailed=\"Yayın başlatılamadı\"\nOutput.BroadcastStopFailed=\"Yayın durdurulamadı\"\nLogUploadDialog.Title=\"OBS Studio Günlük Dosyası Yükleme\"\nLogUploadDialog.Labels.PrivacyNotice=\"Lütfen herhangi bir dosya yüklemeden önce <a href='https://obsproject.com/privacy-policy'>Gizlilik Politikası</a> ve dosya yüklemeleriyle ilgili bölümünü okuyun.\"\nLogUploadDialog.Labels.Progress=\"Günlük yükleme işlemi devam ediyor. Lütfen bekleyin...\"\nLogUploadDialog.Labels.Description.AppLog=\"Günlük dosyanız yüklendi. Artık Bağlantı Adresini hata ayıklama ya da destek amacıyla paylaşabilirsiniz.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Çökme raporunuz yüklendi. Artık Bağlantı Adresini hata ayıklama amacıyla paylaşabilirsiniz.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Yükle\"\nLogUploadDialog.Buttons.CopyURL=\"Günlüğün Bağlantı Adresini Kopyala\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Günlük Dosyasını Analiz Et\"\nLogUploadDialog.Buttons.RetryButton=\"Yeniden Dene\"\nLogUploadDialog.Errors.Template=\"Dosya yüklenmeye çalışılırken bir hata oluştu:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Yüklenecek dosya bulunamadı veya dosya boştu\"\nRemux.SourceFile=\"OBS Kaydı\"\nRemux.TargetFile=\"Hedef Dosya\"\nRemux.Stop=\"Remux'u Durdur\"\nRemux.ClearFinished=\"Biten Ögeleri Temizle\"\nRemux.ClearAll=\"Tüm Ögeleri Temizle\"\nRemux.OBSRecording=\"OBS Kaydı\"\nRemux.FinishedTitle=\"Remux tamamlandı\"\nRemux.Finished=\"Kayıt remux edildi\"\nRemux.FinishedError=\"Kayıt remux edildi, ancak dosya tamamlanmamış olabilir\"\nRemux.SelectRecording=\"OBS Kaydı seçin...\"\nRemux.SelectTarget=\"Hedef dosyayı seçin...\"\nRemux.FileExistsTitle=\"Hedef dosyalar var\"\nRemux.FileExists=\"Aşağıdaki hedef dosyalar zaten var. Var olan dosyaları değiştirmek istiyor musunuz?\"\nRemux.ExitUnfinishedTitle=\"Remux devam ediyor\"\nRemux.ExitUnfinished=\"Dönüştürme tamamlanmadı, şu anda durdurmak hedef dosyayı kullanılamaz kılabilir.\\nDönüştürmeyi durdurmak istediğinizden emin misiniz?\"\nRemux.HelpText=\"Remux etmek için dosyaları bu pencereye sürükleyip bırakın veya bir dosyaya göz atmak için boş bir \\\"OBS Kaydı\\\" hücresini seçin.\"\nRemux.NoFilesAddedTitle=\"Remuxing dosyası eklenmedi\"\nRemux.NoFilesAdded=\"Remux'a dosya eklenmez. Bir veya daha fazla video dosyası içeren bir klasör bırakın.\"\nMissingFiles=\"Eksik Dosyalar\"\nMissingFiles.MissingFile=\"Eksik Dosya\"\nMissingFiles.NewFile=\"Yeni Dosya\"\nMissingFiles.HelpText=\"OBS en son kullanıldığından beri bazı dosyalar kayboldu.\"\nMissingFiles.Clear=\"<temizlendi>\"\nMissingFiles.NumFound=\"%2 taneden %1 tanesi bulundu\"\nMissingFiles.Search=\"Arama Yolu...\"\nMissingFiles.SelectFile=\"Dosya Seç...\"\nMissingFiles.SelectDir=\"Aranacak Klasörü Seçin\"\nMissingFiles.State=\"Durum\"\nMissingFiles.Missing=\"Kayıp\"\nMissingFiles.Replaced=\"Değiştirildi\"\nMissingFiles.Cleared=\"Temizlendi\"\nMissingFiles.Found=\"Bulundu\"\nMissingFiles.AutoSearch=\"Ek dosya eşleşmeleri bulundu\"\nMissingFiles.AutoSearchText=\"OBS, bu dizinde kayıp dosyalar için ek eşleşmeler buldu. Onları eklemek ister misin?\"\nMissingFiles.NoMissing.Title=\"Kayıp Dosya Kontrolü\"\nMissingFiles.NoMissing.Text=\"Herhangi bir kayıp dosya görünmüyor.\"\nMacPermissions.MenuAction=\"Uygulama İzinlerini İnceleyin...\"\nMacPermissions.Title=\"Uygulama İzinlerini İnceleyin\"\nMacPermissions.Description=\"OBS Studio, belirli özellikleri sağlayabilmek için izninizi gerektirir. Bu izinleri etkinleştirmeniz önerilir ancak uygulamayı kullanmak için gerekli değildir. Bunları daha sonra istediğiniz zaman etkinleştirebilirsiniz.\"\nMacPermissions.Description.OpenDialog=\"OBS Studio menüsü aracılığıyla bu kutucuğu tekrar açabilirsiniz.\"\nMacPermissions.AccessGranted=\"Erişim İzni Verildi\"\nMacPermissions.RequestAccess=\"Erişim Talep Et\"\nMacPermissions.OpenPreferences=\"%1 Tercihlerini Aç\"\nMacPermissions.Item.ScreenRecording=\"Ekran Kaydı\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS, ekranınızı yakalayabilmek için bu izni gerektirir.\"\nMacPermissions.Item.Camera=\"Kamera\"\nMacPermissions.Item.Camera.Details=\"Bu izin, bir web kamerasından veya yakalama kartından içerik yakalamak için gereklidir.\"\nMacPermissions.Item.Microphone=\"Mikrofon\"\nMacPermissions.Item.Microphone.Details=\"Mikrofonunuz veya harici bir ses cihazından ses yakalamak istiyorsanız OBS bu izne ihtiyaç duyar.\"\nMacPermissions.Item.InputMonitoring=\"Giriş İzleme\"\nMacPermissions.Item.InputMonitoring.Details=\"Bu izin, OBS arka planda çalışırken kısayol tuşlarının çalışması için gereklidir.\"\nMacPermissions.Item.Accessibility=\"Erişebilirlik\"\nMacPermissions.Item.Accessibility.Details=\"Eski sürümlerde, OBS “Giriş İzleme” yerine “Erişilebilirlik” altında listelenebilir.\"\nMacPermissions.Continue=\"Devam Et\"\nSourceLeak.Title=\"Kaynak Temizleme Hatası\"\nSourceLeak.Text=\"Sahne koleksiyonlarını değiştirirken bir sorun oluştu ve bazı kaynakların yüklemesi kaldırılamadı. Bu hata genel olarak eklentilerin düzgün kaynaklar sağlayamamasından dolayı yaşanır. Lütfen eklentilerinizin güncel olduğuna emin olun.\\n\\nOBS Studio herhangi bir veri kaybı olmaması için kendini kapayacak.\"\nBasic.DesktopDevice1=\"Masaüstü Ses\"\nBasic.DesktopDevice2=\"Masaüstü Ses 2\"\nBasic.Scene=\"Sahne\"\nBasic.DisplayCapture=\"Ekran Yakalama\"\nBasic.Main.PreviewConextMenu.Enable=\"Önizlemeyi Etkinleştir\"\nBasic.Main.Preview.Disable=\"Önizlemeyi Devre dışı bırak\"\nScaleFiltering=\"Boyut Filtreleme\"\nScaleFiltering.Point=\"Nokta\"\nScaleFiltering.Bicubic=\"Bikübik\"\nScaleFiltering.Area=\"Alan\"\nBlendingMethod=\"Harmanlama Yöntemi\"\nBlendingMethod.Default=\"Varsayılan\"\nBlendingMethod.SrgbOff=\"SRGB Kapalı\"\nBlendingMode=\"Harmanlama Kipi\"\nBlendingMode.Additive=\"Ekle\"\nBlendingMode.Subtract=\"Çıkart\"\nBlendingMode.Screen=\"Ekran\"\nBlendingMode.Multiply=\"Çarp\"\nBlendingMode.Lighten=\"Aydınlatma\"\nBlendingMode.Darken=\"Karartma\"\nDeinterlacing.TopFieldFirst=\"Önce Üst Alan\"\nDeinterlacing.BottomFieldFirst=\"Önce Alt Alan\"\nVolControl.SliderUnmuted=\"'%1' için ses ayarı:\"\nVolControl.SliderMuted=\"'%1' için ses ayarı: (susturuldu)\"\nVolControl.Mute=\"Sessiz '%1'\"\nVolControl.Properties=\"'%1' için özellikler\"\nVolControl.UnassignedWarning.Title=\"Atanmamış Ses Kaynağı\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" herhangi bir ses parçasına atalı değil, akşılar veya kayıtlarda duyulamaz.\\n\\nSes kaynağını Akış çizgisine atamak için Gelişmiş Ses Özelliklerini sağ tık menüsü veya karıştırıcı yuvası alet çubuğundaki çark düğmesi ile aç.\"\nBasic.Main.AddSceneDlg.Title=\"Sahne Ekle\"\nBasic.Main.AddSceneDlg.Text=\"Lütfen sahne adını giriniz\"\nBasic.Main.DefaultSceneName.Text=\"Sahne %1\"\nBasic.Main.AddSceneCollection.Title=\"Sahne Koleksiyonu Ekle\"\nBasic.Main.AddSceneCollection.Text=\"Sahne koleksiyonunun isimini giriniz\"\nBasic.Main.RenameSceneCollection.Title=\"Sahne Koleksiyonunu Yeniden Adlandır\"\nBasic.Main.RemigrateSceneCollection.Title=\"Sahne Koleksiyonu Çözünürlüğünü Güncelle\"\nBasic.Main.RemigrateSceneCollection.Text=\"\\\"%1\\\" sahne koleksiyonu çözünürlüğünü, geçerli profilin %2x%3 tuval çözünürlüğüyle eşleşecek şekilde güncellemek ister misiniz?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Çıkışlar etkinken sahne koleksiyonu çözünürlüğü güncellenemez.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Sahne koleksiyonu çözünürlüğü güncellenemedi. Orijinal çözünürlük bilinmiyor.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"sıfırlama mümkün değil: OBS çözünürlüğünü değiştirme başarısız oldu.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"sıfırlama mümkün değil: Mevcut çözünürlük zaten sahne koleksiyonunun temel çözünürlüğü.\"\nAddProfile.Title=\"Profil Ekle\"\nAddProfile.Text=\"Lütfen profil isimini girin\"\nAddProfile.WizardCheckbox=\"Otomatik yapılandırma sihirbazını göster\"\nRenameProfile.Title=\"Profili Yeniden Adlandır\"\nBasic.Main.MixerRename.Title=\"Ses Kaynağını Yeniden Adlandır\"\nBasic.Main.MixerRename.Text=\"Lütfen ses kaynağının adını girin\"\nBasic.Main.PreviewDisabled=\"Önizleme şu anda devre dışı\"\nBasic.SourceSelect=\"Kaynak Oluştur/Seç\"\nBasic.SourceSelect.CreateNew=\"Yeni oluştur\"\nBasic.SourceSelect.AddExisting=\"Varolanı Ekle\"\nBasic.SourceSelect.AddVisible=\"Kaynağı görünür yap\"\nBasic.Main.Sources.Visibility=\"Görünürlük\"\nBasic.Main.Sources.VisibilityDescription=\"'%1' 'in tuvaldeki görünebilirliğini ayarlar\"\nBasic.Main.Sources.Lock=\"Kilitle\"\nBasic.Main.Sources.LockDescription=\"'%1' 'in tuvaldeki konumunu ve boyutunu kilitler\"\nBasic.PropertiesWindow=\"'%1' için özellikler\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1(oto.seçim: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Renk seçin\"\nBasic.PropertiesWindow.SelectFont=\"Yazı tipi seç\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Bir Yazı Tipi Seçin\"\nBasic.PropertiesWindow.ConfirmTitle=\"Ayarlar Değiştirildi\"\nBasic.PropertiesWindow.Confirm=\"Kaydedilmeyen değişikler var. Yaptığınız değişikleri kaydetmek istiyor musunuz?\"\nBasic.PropertiesWindow.NoProperties=\"Hiçbir özellik mevcut değil\"\nBasic.PropertiesWindow.AddFiles=\"Dosya Ekle\"\nBasic.PropertiesWindow.AddDir=\"Dizin Ekle\"\nBasic.PropertiesWindow.AddURL=\"Dosya Yolu/Bağlantı Adresi Ekle\"\nBasic.PropertiesWindow.AddEditableListDir=\"'%1' dizinine ekle\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Dosyaları '%1' 'e ekle\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Girişi '%1' 'e ekle\"\nBasic.PropertiesWindow.EditEditableListEntry=\"'%1' konumundan girdiyi düzenle\"\nBasic.PropertiesView.FPS.Simple=\"Basit FPS Değerleri\"\nBasic.PropertiesView.FPS.Rational=\"Rasyonel FPS Değerleri\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Geçerli Kare Aralıkları:\"\nBasic.PropertiesView.UrlButton.Text=\"Bu bağlantı varsayılan web tarayıcınızda açılsın mı?\"\nBasic.PropertiesView.UrlButton.Text.Url=\"Bağlantı Adresi: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Bağlantı Adresini Aç\"\nBasic.InteractionWindow=\"Etkileşime geçilen '%1'\"\nBasic.StatusBar.Reconnecting=\"Bağlantı koptu, %2 saniye içinde yeniden bağlanılacak (%1. deneme)\"\nBasic.StatusBar.AttemptingReconnect=\"Yeniden bağlanmaya çalışılıyor... (%1. deneme)\"\nBasic.StatusBar.ReconnectSuccessful=\"Yeniden bağlantı başarılı\"\nBasic.StatusBar.Delay=\"Gecikme (%1 san)\"\nBasic.StatusBar.DelayStartingIn=\"Gecikme (başlangıcı %1 saniyede)\"\nBasic.StatusBar.DelayStoppingIn=\"Gecikme (bitişi %1 saniyede)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Gecikme (başlangıç %2 saniye, bitiş %1 saniye)\"\nBasic.StatusBar.RecordingSavedTo=\"Kayıt `%1` konumuna kaydedildi\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Tekrar oynatma önbelleği `%1` konumuna kaydedildi\"\nBasic.StatusBar.ScreenshotSavedTo=\"Ekran görüntüsü `%1` konumuna kaydedildi\"\nBasic.StatusBar.AutoRemuxedTo=\"Kayıt `%1` konumuna otomatik remuxlandı\"\nBasic.Filters=\"Filtreler\"\nBasic.Filters.AsyncFilters=\"Ses/Video Filtreleri\"\nBasic.Filters.AudioFilters=\"Ses Filtreleri\"\nBasic.Filters.EffectFilters=\"Efekt Filtreleri\"\nBasic.Filters.Title=\"'%1' için filtreler\"\nBasic.Filters.AddFilter.Title=\"Filtre adı\"\nBasic.Filters.AddFilter.Text=\"Lütfen filtrenin adını belirtin\"\nBasic.TransformWindow=\"Sahne Ögesini Dönüştür\"\nBasic.TransformWindow.Position=\"Konum\"\nBasic.TransformWindow.PositionX=\"X Konumu\"\nBasic.TransformWindow.PositionY=\"Y Konumu\"\nBasic.TransformWindow.Rotation=\"Döndürme\"\nBasic.TransformWindow.Size=\"Boyut\"\nBasic.TransformWindow.Width=\"Genişlik\"\nBasic.TransformWindow.Height=\"Yükseklik\"\nBasic.TransformWindow.Alignment=\"Konuma Göre Hizalama\"\nBasic.TransformWindow.BoundsType=\"Sınırlayıcı Kutu Türü\"\nBasic.TransformWindow.BoundsAlignment=\"Sınırlayıcı Kutuyu Hizala\"\nBasic.TransformWindow.Bounds=\"Sınırlayıcı Kutu Boyutu\"\nBasic.TransformWindow.BoundsWidth=\"Sınırlayıcı Kutu Genişliği\"\nBasic.TransformWindow.BoundsHeight=\"Sınırlayıcı Kutu Yüksekliği\"\nBasic.TransformWindow.CropToBounds=\"Sınır Kutusuna Kes\"\nBasic.TransformWindow.Crop=\"Kırp\"\nBasic.TransformWindow.CropLeft=\"Solu Kırp\"\nBasic.TransformWindow.CropRight=\"Sağı Kırp\"\nBasic.TransformWindow.CropTop=\"Yukarıyı Kırp\"\nBasic.TransformWindow.CropBottom=\"Altı Kırp\"\nBasic.TransformWindow.Alignment.TopLeft=\"Sol Üst\"\nBasic.TransformWindow.Alignment.TopCenter=\"Üst Orta\"\nBasic.TransformWindow.Alignment.TopRight=\"Sağ Üst\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Orta Sol\"\nBasic.TransformWindow.Alignment.Center=\"Ortala\"\nBasic.TransformWindow.Alignment.CenterRight=\"Orta Sağ\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Sol Alt\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Alt Orta\"\nBasic.TransformWindow.Alignment.BottomRight=\"Sağ alt\"\nBasic.TransformWindow.BoundsType.None=\"Sınırsız\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Yalnızca maksimum boyut\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"İç sınırlara boyutlandır\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Dış sınırlara boyutlandır\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Sınır genişliğine boyutlandır\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Sınır yüksekliğine boyutlandır\"\nBasic.TransformWindow.BoundsType.Stretch=\"Sınırlara genişlet\"\nBasic.TransformWindow.Title=\"'%1' için dönüşüm düzenleme\"\nBasic.TransformWindow.NoSelectedSource=\"Kaynak seçilmedi\"\nBasic.Main.AddSourceHelp.Title=\"Kaynak Eklenemiyor\"\nBasic.Main.AddSourceHelp.Text=\"Kaynağa eklemek için en az 1 sahneye ihtiyacın var.\"\nBasic.Main.Scenes=\"Sahneler\"\nBasic.Main.Sources=\"Kaynaklar\"\nBasic.Main.Source=\"Kaynak\"\nBasic.Main.Controls=\"Kontroller\"\nBasic.Main.PreparingStream=\"Hazırlanıyor...\"\nBasic.Main.Connecting=\"Bağlanıyor...\"\nBasic.Main.StartRecording=\"Kaydı Başlat\"\nBasic.Main.StartReplayBuffer=\"Tekrar Oynatma Arabelleğini Başlat\"\nBasic.Main.SaveReplay=\"Yeniden Oynatmayı Kaydet\"\nBasic.Main.StartStreaming=\"Yayını Başlat\"\nBasic.Main.StartBroadcast=\"Canlı Yayına Geç\"\nBasic.Main.StartVirtualCam=\"Sanal Kamerayı Başlat\"\nBasic.Main.StopRecording=\"Kaydı Durdur\"\nBasic.Main.PauseRecording=\"Kaydı Duraklat\"\nBasic.Main.UnpauseRecording=\"Kaydı Devam Ettir\"\nBasic.Main.SplitFile=\"Kayıt Dosyasını Böl\"\nBasic.Main.AddChapterMarker=\"Bölüm İşaretçisi Ekle (sadece Hibrit MP4/MOV türünde)\"\nBasic.Main.StoppingRecording=\"Kayıt Durduruluyor...\"\nBasic.Main.StopReplayBuffer=\"Tekrar Oynatma Arabelleğini Durdur\"\nBasic.Main.StoppingReplayBuffer=\"Tekrar Oynatma Arabelleği Durduruluyor...\"\nBasic.Main.SetupBroadcast=\"Yayını Düzenle\"\nBasic.Main.StopStreaming=\"Yayını Durdur\"\nBasic.Main.StopBroadcast=\"Yayını bitir\"\nBasic.Main.AutoStopEnabled=\"(Otomatik durdur)\"\nBasic.Main.StoppingStreaming=\"Canlı Yayın Durduruluyor...\"\nBasic.Main.ForceStopStreaming=\"Yayını Durdur (gecikmeyi yoksay)\"\nBasic.Main.ShowContextBar=\"Kaynak menüsünü göster\"\nBasic.Main.HideContextBar=\"Kaynak menüsünü kapat\"\nBasic.Main.StopVirtualCam=\"Sanal Kamerayı Durdur\"\nBasic.Main.Group=\"Grup %1\"\nBasic.Main.GroupItems=\"Seçilen Ögeleri Grupla\"\nBasic.Main.Ungroup=\"Grubu Çöz\"\nBasic.Main.GridMode=\"Izgara modu\"\nBasic.Main.ListMode=\"Liste Modu\"\nBasic.Main.VirtualCamConfig=\"Sanal Kamerayı Yapılandır\"\nBasic.VCam.VirtualCamera=\"Sanal Kamera\"\nBasic.VCam.OutputType=\"Çıktı Türü\"\nBasic.VCam.OutputSelection=\"Çıkış Seçimi\"\nBasic.VCam.OutputType.Program=\"Uygulama (Varsayılan)\"\nBasic.VCam.OutputSelection.NoSelection=\"Bu çıkış türü için seçim mevcut değil\"\nBasic.VCam.RestartWarning=\"Sanal kamera değişiklikleri uygulamak için yeniden başlatılacak\"\nBasic.MainMenu.File=\"&Dosya\"\nBasic.MainMenu.File.Export=\"Dışa Aktar (&E)\"\nBasic.MainMenu.File.Import=\"İçe Aktar (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Kayıtla&rı Göster\"\nBasic.MainMenu.File.Remux=\"Kayıtları Re&mux Et\"\nBasic.MainMenu.File.Settings=\"&Ayarlar\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Ayarlar Dosyasını Göster\"\nBasic.MainMenu.File.ShowProfileFolder=\"Profil Dosyasını Göster\"\nBasic.MainMenu.File.ShowMissingFiles=\"Kayıp dosyaları kontrol et\"\nBasic.MainMenu.File.Exit=\"Çı&kış\"\nBasic.MainMenu.Edit=\"Düz&enle\"\nBasic.MainMenu.Edit.Undo=\"&Geri al\"\nBasic.MainMenu.Edit.Redo=\"Tek&rar Yap\"\nBasic.MainMenu.Edit.LockPreview=\"Öniz&lemeyi Kilitle\"\nBasic.MainMenu.Edit.Scale=\"Ö&nizleme Ölçeği\"\nBasic.MainMenu.Edit.Scale.Window=\"Pencereye Boyutlandır\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Tuval (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Çıktı (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Ölçek (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Yakınlaştır\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Uzaklaştır\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Yakınlaştırmayı Sıfırla\"\nBasic.MainMenu.Edit.Transform=\"Dönüş&tür\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Dönüştürm&eyi Düzenle...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Dönüştürmeyi Kopyala\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Dönüştürmeyi Yapıştır\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Dönüştü&rmeyi Sıfırla\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"90 derece saat yönüne döndür\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"90 derece saatin tersi yönüne döndür\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180 derece döndür\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"&Yatay Döndür\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Dikey Çe&vir\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Ekrana &Sığdır\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Ekrana &genişlet\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Ekrana &Ortala\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Dikeye Hizala\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Yataya Hizala\"\nBasic.MainMenu.Edit.Order=\"&Sırala\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Y&ukarı Taşı\"\nBasic.MainMenu.Edit.Order.MoveDown=\"&Aşağı Taşı\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"En Üs&te Taşı\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"&En Alta Taşı\"\nBasic.MainMenu.Edit.AdvAudio=\"&Gelişmiş Ses Özellikleri\"\nBasic.MainMenu.View=\"&Görünüm\"\nBasic.MainMenu.View.Toolbars=\"Araç Çubukları (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"Yuva Araç Çubukları\"\nBasic.MainMenu.View.ContextBar=\"Kaynak Menüsü\"\nBasic.MainMenu.View.SourceIcons=\"Kaynak S&imgeleri\"\nBasic.MainMenu.View.StatusBar=\"&Durum Çubuğu\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Tam Ekran Arayüz\"\nBasic.MainMenu.View.ResetUI=\"&Arayüzü Sıfırla\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Her Zaman Üstte\"\nBasic.MainMenu.View.SceneListMode=\"Sahne Listesi Modu\"\nBasic.MainMenu.Docks=\"&Paneller\"\nBasic.MainMenu.Docks.ResetDocks=\"&Yuvaları Sıfırla\"\nBasic.MainMenu.Docks.LockDocks=\"&Yuvaları Kilitle\"\nBasic.MainMenu.Docks.SideDocks=\"&Tam Yükseklikli Yuvalar\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Özel Tarayıcı Yuvaları...\"\nBasic.MainMenu.Import=\"İçe Aktar...\"\nBasic.MainMenu.Export=\"Dışa Aktar...\"\nBasic.MainMenu.SceneCollection=\"&Sahne Koleksiyonu\"\nBasic.MainMenu.Profile=\"&Profil\"\nBasic.MainMenu.Profile.Import=\"Profili İçe Aktar\"\nBasic.MainMenu.Profile.Export=\"Profili Dışa Aktar\"\nBasic.MainMenu.SceneCollection.Import=\"Sahne Koleksiyonunu İçe Aktar\"\nBasic.MainMenu.SceneCollection.Export=\"Sahne Koleksiyonunu Dışa Aktar\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Temel Çözünürlüğü Sıfırla\"\nBasic.MainMenu.SceneCollection.Migrate=\"Temel Çözünürlüğü Ayarla\"\nBasic.MainMenu.Profile.Exists=\"Profil zaten var\"\nBasic.MainMenu.Tools=\"&Araçlar\"\nBasic.MainMenu.Help=\"&Yardım\"\nBasic.MainMenu.Help.HelpPortal=\"Yardım &Portalı\"\nBasic.MainMenu.Help.Website=\"&Siteyi Ziyaret Et\"\nBasic.MainMenu.Help.Discord=\"&Discord Sunucusuna Katıl\"\nBasic.MainMenu.Help.WhatsNew=\"Yenilikler\"\nBasic.MainMenu.Help.Logs=\"Gün&lük Dosyaları\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Günlük Do&syalarını Göster\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Mev&cut Günlük Dosyasını Karşıya Yükle\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"&Son Kayıt Dosyasını Yükle\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Şimdiki &Günlüğü Göster\"\nBasic.MainMenu.Help.ReleaseNotes=\"Sürüm Notları\"\nBasic.MainMenu.Help.CheckForUpdates=\"Güncellemeleri Denetle\"\nBasic.MainMenu.Help.Repair=\"Dosya Bütünlüğünü Denetle\"\nBasic.MainMenu.Help.RestartSafeMode=\"Güvenli Mod'da yeniden başlat\"\nBasic.MainMenu.Help.RestartNormal=\"Normal Mod'da yeniden başlat\"\nBasic.MainMenu.Help.CrashLogs=\"Çökme &Raporları\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Çökme Raporlarını Gö&ster\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"&Son Çökme Raporunu Yükle\"\nBasic.MainMenu.Help.About=\"H&akkında\"\nBasic.Settings.ProgramRestart=\"Programın, bu ayarların etkinleşmesi için yeniden başlatılması gerekir.\"\nBasic.Settings.ConfirmTitle=\"Değişiklikleri Onayla\"\nBasic.Settings.Confirm=\"Kayıt edilmemiş değişiklikleriniz var. Değişiklikler kayıt edilsin mi?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 yayın ayarlarınızın bazılarını kontrol ediyor\"\nBasic.Settings.General=\"Genel\"\nBasic.Settings.General.Language=\"Dil\"\nBasic.Settings.General.Updater=\"Güncellemeler\"\nBasic.Settings.General.UpdateChannel=\"Güncelleme kanalı\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Devre dışı)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Varsayılan)\"\nBasic.Settings.General.EnableAutoUpdates=\"Başlangıçta güncellemeleri otomatik olarak denetle\"\nBasic.Settings.General.OpenStatsOnStartup=\"Başlangıçta istatistikler iletişim kutusunu aç\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"OBS pencerelerini ekran yakalamasından gizle\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Bu ayar, yansıtma olmayan tüm OBS Studio pencerelerinin OBS tarafından yakalanmasını engelleyecektir\\nve konferans, ekran paylaşımı, uzaktan destek, ekran görüntüleri ve diğer yakalama yazılımları gibi diğer uygulamaları etkiler.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Bu ayarı etkinleştirmek, yansıtma olmayan tüm OBS Studio pencerelerinin OBS tarafından yakalanmasını engeller ve konferans, ekran paylaşımı, uzaktan destek, ekran görüntüleri ve diğer yakalama yazılımları gibi diğer uygulamaları etkiler.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Yayın başlatırken onay iletişim kutusunu göster\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Yayın durdulacağı zaman onay kutusunu göster\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Kaydı durdururken onay kutusunu göster\"\nBasic.Settings.General.Projectors=\"Projektörler\"\nBasic.Settings.General.HideProjectorCursor=\"Projektörler üzerinde imleci gizle\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Projektörleri her zaman üstte tut\"\nBasic.Settings.General.Snapping=\"Kaynak Hizalama\"\nBasic.Settings.General.ScreenSnapping=\"Kaynakları ekranın kenarına yasla\"\nBasic.Settings.General.CenterSnapping=\"Kaynakları dikey ve yatay merkeze yasla\"\nBasic.Settings.General.SourceSnapping=\"Kaynakları diğer kaynaklara yasla\"\nBasic.Settings.General.SnapDistance=\"Yaslama Hassasiyeti\"\nBasic.Settings.General.SpacingHelpers=\"Piksel hizalama kılavuzlarını göster\"\nBasic.Settings.General.RecordWhenStreaming=\"Yayın sırasında otomatik olarak kayıt yap\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Yayın durduğunda kaydı tut\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Yayın sırasında tekrar oynatma arabelleğini otomatik olarak başlat\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Yayın durduğunda tekrar oynatma arabelleğini tut\"\nBasic.Settings.General.SysTray=\"Sistem tepsisi\"\nBasic.Settings.General.SysTrayWhenStarted=\"Başladığında sistem tepsisine küçült\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Her zaman görev çubuğu yerine sistem tepsisine küçült\"\nBasic.Settings.General.SaveProjectors=\"Çıkışta projektörleri kaydet\"\nBasic.Settings.General.CloseExistingProjectors=\"Ekran başına tam ekran projektörünü bire sınırla\"\nBasic.Settings.General.Preview=\"Önizleme\"\nBasic.Settings.General.OverflowHidden=\"Taşmayı gizle\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Taşmayı her zaman görünür yap\"\nBasic.Settings.General.OverflowSelectionHidden=\"Kaynak görünmese bile taşmayı göster\"\nBasic.Settings.General.Importers=\"İçe Aktarılan\"\nBasic.Settings.General.AutomaticCollectionSearch=\"İçe aktarırken bilinen koleksiyonları sahne koleksiyonları için arayın\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Çift tıklamada sahneye geçiş yap\"\nBasic.Settings.General.StudioPortraitLayout=\"Dikey düzeni etkinleştir\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Önizlemeyi göster/program etiketleri\"\nBasic.Settings.General.Multiview=\"Çoklu görüntü\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Sahneler arası geçiş için tıkla\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Sahne adlarını göster\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Güvenli alanları (EBU R 95) çiz\"\nBasic.Settings.General.MultiviewLayout=\"Çoklu Görüntü Düzeni\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Yatay, Üst (8 Sahne)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Yatay, Alt (8 Sahne)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Dikey, Sol (8 Sahne)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Dikey, Sağ (8 Sahne)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Yatay, Üst (18 Sahne)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Yatay, Üst (24 Sahne)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Sadece sahne (4 Sahne)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Sadece sahne (9 Sahne)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Sadece sahne (16 Sahne)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Sadece sahne (25 Sahne)\"\nBasic.Settings.General.ChannelName.stable=\"Stabil\"\nBasic.Settings.General.ChannelDescription.stable=\"En yeni stabil sürüm\"\nBasic.Settings.General.ChannelName.beta=\"Betalar / Sürüm Adayı\"\nBasic.Settings.General.ChannelDescription.beta=\"Potansiyel olarak stabil olmayan yayım-öncesi versiyonlar\"\nBasic.Settings.Appearance=\"Görünüm\"\nBasic.Settings.Appearance.General=\"Genel\"\nBasic.Settings.Appearance.General.Theme=\"Tema\"\nBasic.Settings.Appearance.General.Variant=\"Stil\"\nBasic.Settings.Appearance.General.NoVariant=\"Hiç stil mevcut değil.\"\nBasic.Settings.Appearance.FontScale=\"Yazı Boyutu\"\nBasic.Settings.Appearance.Density=\"Yoğunluk\"\nBasic.Settings.Appearance.Classic=\"Klasik\"\nBasic.Settings.Appearance.Compact=\"Sade\"\nBasic.Settings.Appearance.Comfortable=\"Rahat\"\nBasic.Settings.Appearance.OptionsWarning=\"Bu stil için bazı görünüm seçenekleri kullanılamaz.\"\nBasic.Settings.Stream=\"Yayın\"\nBasic.Settings.Stream.Destination=\"Hedef\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Kimlik doğrulaması kullan\"\nBasic.Settings.Stream.Custom.Username=\"Kullanıcı adı\"\nBasic.Settings.Stream.Custom.Password=\"Şifre\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: srp_kullanıcı adını girin. \\nRTMP: kullanıcı adını girin.\\nSRT: kullanılmıyor.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: srp_şifresini girin.\\nRTMP: şifreyi girin.\\nSRT: şifreleme parolasını girin.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Bant Genişliği Test Modunu Etkinleştir\"\nBasic.Settings.Stream.TTVAddon=\"Twitch Sohbet Eklentileri\"\nBasic.Settings.Stream.TTVAddon.None=\"Yok\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV ve FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Eksik Akış Kurulumu\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Ayarları Aç\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"Bağlantı Adresi ve Akış Anahtarı eksik.\\n\\n ' stream ' sekmesinde Bağlantı Adresi ve Akış Anahtarını girmek için ayarları açın.\"\nBasic.Settings.Stream.MissingUrl=\"Akış Bağlantı Adresi eksik.\\n\\n ' Akış ' sekmesine Bağlantı Adresi'ni girmek için ayarları açın.\"\nBasic.Settings.Stream.MissingStreamKey=\"Akış anahtarı eksik.\\n\\n ' Stream ' sekmesinde stream tuşuna girmek için ayarları açın.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Yayın hizmeti ayar önerilerini yoksay\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Önerilen Ayarları Geçersiz Kıl\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Uyarı: Hizmetin kısıtlamalarını yok saymak, düşürülmüş yayın kalitesine neden olabilir veya yayın yapmanızı engelleyebilir.\\n\\nSürdürülsün mü?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"En Yüksek Vidyo Bit Hızı: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"En Yüksek Ses Bit Hızı: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"En Yüksek Çözünürlük: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"En Yüksek FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Özel Sunucuyu Belirt...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Özel Sunucu\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1'i Etkinleştir\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"En Yüksek Akış Hızı\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Otomatik\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Maksimum Video Parçaları\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Otomatik\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Yayını FLV formatında kaydetmeyi etkinleştir (basit kayıt dosyası ayarlarını kullanır)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Yapılandırmayı Geçersiz Kıl (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Yapılandırmayı Geçersiz Kılmayı Etkinleştir\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Çok kanallı Video\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Ek Tuval\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Eş zamanlı yayın\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Eş zamanlı yayın, birden fazla video kalitesini kodlamanıza ve göndermenize olanak tanır. <a href='https://obsproject.com/kb/whip-streaming-guide'>Daha fazla bilgi edinin</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Toplam Katman\"\nBasic.Settings.Stream.AdvancedOptions=\"Gelişmiş Seçenekler\"\nBasic.Settings.Output=\"Çıkış\"\nBasic.Settings.Output.Format=\"Kayıt Biçimi\"\nBasic.Settings.Output.Format.hMP4=\"Hibrit MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Hibrit MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Parçalı MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Parçalı MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Parçalanmış MOV kaydı parçalar halinde yazar ve geleneksel MOV dosyalarıyla aynı son işlemeyi gerektirmez.\\nBu, örneğin bir BSOD (mavi ekran) veya güç kaybı sonucunda diske yazma işlemi kesintiye uğrasa bile dosyanın oynatılabilir kalmasını sağlar.\\n\\nBu, tüm oynatıcılar ve düzenleyicilerle uyumlu olmayabilir. Gerekirse dosyayı daha uyumlu bir biçime dönüştürmek için Dosya → Kayıtları Remux Et seçeneğini kullanın.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Parçalanmış MP4 kaydı parçalar halinde yazar ve geleneksel MP4 dosyalarıyla aynı son işlemeyi gerektirmez.\\nBu, örneğin bir BSOD (mavi ekran) veya güç kaybı sonucunda diske yazma işlemi kesintiye uğrasa bile dosyanın oynatılabilir kalmasını sağlar.\\n\\nBu, tüm oynatıcılar ve düzenleyicilerle uyumlu olmayabilir. Gerekirse dosyayı daha uyumlu bir biçime dönüştürmek için Dosya → Kayıtları Remux Et seçeneğini kullanın.\"\nBasic.Settings.Output.Encoder.Video=\"Video kodlayıcı\"\nBasic.Settings.Output.Encoder.Audio=\"Ses Kodlayıcı\"\nBasic.Settings.Output.SelectDirectory=\"Kayıt Dizinini Seçin\"\nBasic.Settings.Output.DynamicBitrate=\"Akışı yönetmek için bit hızını değişkenli bir biçimde değiştir\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Akışı yönetmek için bit hızını değişkenli bir biçimde değiştir (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Akışı azaltmak için kare hızını düşürmek yerine, bit hızını değişkenli bir biçimde değiştirir.\\n\\nBunun, eğer ani bir akış olursa izleyiciler için gecikme yaratabileceğini unutmayın.\\nEğer bit hızı düşerse, yenilenmesi bir kaç dakika alabilir.\\n\\nŞu anlık sadece RTMP için destekli.\"\nBasic.Settings.Output.Mode=\"Çıkış modu\"\nBasic.Settings.Output.Mode.Simple=\"Basit\"\nBasic.Settings.Output.Mode.Adv=\"Gelişmiş\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg Çıkışı\"\nBasic.Settings.Output.UseReplayBuffer=\"Tekrar Oynatma Arabelleğini Etkinleştir\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Maksimum Tekrar Süresi\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Azami Bellek\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Tahmini bellek kullanımı: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Uyarı: %1 MiB'lik tahmini bellek kullanımı, önerilen en fazla %2 MiB'den daha büyük\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Bellek kullanımını tahmin edemezsin. Lütfen maksimum bellek sınırını ayarlayın.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Tekrar Oynatma Arabelleği Dosya Adı Ön Eki\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Son Eki\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Kayıt türü Özel Çıkış (FFmpeg) olarak ayarlandığında Yeniden Yürütme Arabelleği kullanılamaz.\"\nBasic.Settings.Output.Simple.SavePath=\"Kayıt Yolu\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Kayıt Kalitesi\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Canlı Yayın ile Aynı\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Yüksek Kalite, Normal Dosya Boyutu\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Aynı Kaliteye Yakın, Büyük Dosya Boyutu\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Kayıpsız Kalite, Çok Büyük Dosya Boyutu\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Uyarı: Yayının vidyo bit hızı %1' e ayarlanacak, ki bu değer mevcut yayın hizmeti için üst sınırdır.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Uyarı: Yayının ses bit hızı %1' e ayarlanacak, ki bu değer mevcut yayın hizmeti için üst sınırdır.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Dikkat: Kayıt kalitesi \\\"Yayın ile aynı\\\"ya ayarlanmışken kayıtlar durdurulamaz.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Uyarı: Seçili olan kayıt biçimi seçili olan yayın kodlayıcı(ları) ile uyumsuz.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Uyarı: Bir yazılım kodlayıcı ile yayın kalitesinden farklı kayıt yapmak eğer aynı anda hem kayıt hem de yayın yapıyorsanız ilave CPU kullanımı gerektirecektir.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Uyarı: Kayıpsız kalite muazzam büyük dosya boyutları oluşturur! Kayıpsız kalite, yüksek çözünürlüklerde ve kare hızlarında, dakikada 7 gigabyte'a kadar disk alanı kullanabilir. Kayıpsız, kullanılabilir disk alanınız çok büyük değilse, uzun kayıtlar için tavsiye edilmez.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Kayıpsız kalitede kullanmak istediğinizden emin misiniz?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Kayıpsız kalite uyarısı!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Yazılım (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Donanım (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Donanım (QSV, H.)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Donanım (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Donanım (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Donanım (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Donanım (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Donanım (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Donanım (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Donanım (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Donanım (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Yazılım (x264 düşük CPU kullanım önayarı, dosya boyutunu artırır)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Varsayılan)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD Parçası (Parça 2'yi kullanır)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Ses Parçası\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Uyumsuz Çözünürlük/Kare Hızı\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Bu yayın hizmeti, mevcut çıkış çözünürlüğünüzü ve/veya kare hızınızı desteklemiyor. Bunlar, bu değerlere en yakın uyumlu değerlerle değiştirilecektir:\\n\\n%1\\n\\nDevam etmek istiyor musunuz?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Çözünürlük: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Uyumsuz Çözücü\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"\\\"%1\\\" yayın hizmeti \\\"%2\\\" kodlayıcısını desteklemiyor. Kodlayıcı \\\"%3\\\" olarak değiştirilecek.\\n\\nDevam etmek istiyor musunuz?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Yayınlama hizmeti \\\"%1\\\", \\\"%2\\\" ve \\\"%3\\\" çözücülerini desteklemiyor. Bu çözücüler \\\"%4\\\" ve \\\"%5\\\" olarak değiştirilecek.\\n\\nDevam etmek istiyor musun?\"\nBasic.Settings.Output.VideoBitrate=\"Video Bit Hızı\"\nBasic.Settings.Output.AudioBitrate=\"Ses Bit Hızı\"\nBasic.Settings.Output.Reconnect=\"Otomatik Yeniden Bağlan\"\nBasic.Settings.Output.RetryDelay=\"Tekrar Gecikmesi\"\nBasic.Settings.Output.MaxRetries=\"Maksimum Deneme Sayısı\"\nBasic.Settings.Output.Advanced=\"Özel Kodlayıcı Ayarlarını Etkinleştir (Gelişmiş)\"\nBasic.Settings.Output.EncoderPreset=\"Kodlayıcı Hazır Ayarı\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (düşük CPU kullanımı, en düşük kalite)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (varsayılan) (orta CPU kullanımı, standart kalite)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (yüksek CPU kullanımı, yüksek kalite)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Özel Kodlayıcı Ayarları\"\nBasic.Settings.Output.CustomMuxerSettings=\"Özel Muxer Ayarları\"\nBasic.Settings.Output.NoSpaceFileName=\"Dosya ismini boşluk olmadan oluştur\"\nBasic.Settings.Output.Adv.Rescale=\"Çıkışı Yeniden Boyutlandır\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Devre Dışı\"\nBasic.Settings.Output.Adv.AudioTrack=\"Ses Parçası\"\nBasic.Settings.Output.Adv.Streaming=\"Yayın\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Yayın Ayarları\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Parça 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Ses İzi 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Ses İzi 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Ses İzi 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Parça 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Parça 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD Parçası\"\nBasic.Settings.Output.Adv.Encoder=\"Kodlayıcı Ayarları\"\nBasic.Settings.Output.Adv.Recording=\"Kayıt\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Kayıt Ayarları\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Kaydetme Türü\"\nBasic.Settings.Output.Adv.Recording.Type=\"Tür\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Standart\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Özel Çıkış (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Yayın kodlayıcı kullan)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Dosya Adı Biçimi\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Dosya varsa üzerine yaz\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Özel çıkış (FFmpeg) hiçbir güvenlik önlemi olmadan sağlanır.\\n\\\"Standart\\\" kayıt türünü kullanmayı düşünün.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg Çıkış Türü\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Bağlantı Adresi Çıktısı\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Dosyaya Çıkış\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Ortak kayıt biçimleri\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Tüm Dosyalar\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Dosya yolu ya da Bağlantı adresi\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Kapsayıcı Biçimi\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Ses\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Varsayılan Biçim\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Kapsayıcı Biçimi Açıklaması\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Ses/Video çözücüsü Dosya yolundan veya URL'den tahmin edildi\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Varsayılan Kodlayıcı\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Kodlayıcıyı Devre Dışı Bırak\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Video Kodlayıcı\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Video Kodlayıcı Ayarları (var ise)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Ses Kodlayıcı\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Ses Kodlayıcı Ayarları (var ise)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxer Ayarları (eğer varsa)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Anahtar Kare Aralığı (kare)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Tüm çözücüleri göster (potansiyel olarak uyumsuz olsa bile)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg Ayarları\"\nBasic.Settings.Output.EnableSplitFile=\"Otomatik Dosya Bölme\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Zamana Göre Böl\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Boyuta Göre Böl\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Sadece elle böl\"\nBasic.Settings.Output.SplitFile.Time=\"Bölme Süresi\"\nBasic.Settings.Output.SplitFile.Size=\"Bölme Boyutu\"\nScreenshot=\"Ekran Görüntüsü Çıktısı\"\nScreenshot.SourceHotkey=\"Seçili Ekran Görüntüsü Kaynağı\"\nScreenshot.StudioProgram=\"Ekran Görüntüsü (Program)\"\nScreenshot.Preview=\"Ekran Görüntüsü (Önizleme)\"\nScreenshot.Scene=\"Ekran Görüntüsü (Sahne)\"\nScreenshot.Source=\"Ekran Görüntüsü (Kaynak)\"\nFilenameFormatting.TT.CCYY=\"Yıl, dört hane\"\nFilenameFormatting.TT.YY=\"Yıl, son iki hane (00-99)\"\nFilenameFormatting.TT.MM=\"Ondalık sayı olarak ay (01-12)\"\nFilenameFormatting.TT.DD=\"Ayın günü, sıfır-doldurma (01-31)\"\nFilenameFormatting.TT.hh=\"24 biçiminde saat (00-23)\"\nFilenameFormatting.TT.mm=\"Dakika (00-59)\"\nFilenameFormatting.TT.ss=\"Saniye (00-59)\"\nFilenameFormatting.TT.Percent=\"Bir % işareti\"\nFilenameFormatting.TT.a=\"Kısaltılmış hafta içi gün adı\"\nFilenameFormatting.TT.A=\"Bütün hafta içi gün adı\"\nFilenameFormatting.TT.b=\"Kısaltılmış ay adı\"\nFilenameFormatting.TT.B=\"Bütün ay adı\"\nFilenameFormatting.TT.d=\"Ayın günü, sıfır-doldurma (01-31)\"\nFilenameFormatting.TT.H=\"24 biçiminde saat (00-23)\"\nFilenameFormatting.TT.I=\"12 biçiminde saat (01-12)\"\nFilenameFormatting.TT.m=\"Ondalık sayı olarak ay (01-12)\"\nFilenameFormatting.TT.M=\"Dakika (00-59)\"\nFilenameFormatting.TT.p=\"ÖÖ veya ÖS gösterme\"\nFilenameFormatting.TT.s=\"UNIX zamanından şu ana kadar geçen saniye olarak zaman\"\nFilenameFormatting.TT.S=\"Saniye (00-59)\"\nFilenameFormatting.TT.y=\"Yıl, son iki hane (00-99)\"\nFilenameFormatting.TT.Y=\"Yıl\"\nFilenameFormatting.TT.z=\"ISO 8601 dengi UTC olarak saat dilimi\"\nFilenameFormatting.TT.Z=\"Saat dilimi adı veya kısaltımı\"\nFilenameFormatting.TT.FPS=\"Saniye başına kare\"\nFilenameFormatting.TT.CRES=\"Temel (tuval) çözünürlüğü\"\nFilenameFormatting.TT.ORES=\"Çıkış (Boyutlandırılmış) Çözünürlüğü\"\nFilenameFormatting.TT.VF=\"Video biçimi\"\nBasic.Settings.Video.Adapter=\"Video Bağdaştırıcı\"\nBasic.Settings.Video.BaseResolution=\"Temel (Tuval) Çözünürlüğü\"\nBasic.Settings.Video.ScaledResolution=\"Çıkış (Boyutlandırılmış) Çözünürlüğü\"\nBasic.Settings.Video.DownscaleFilter=\"Boyut Azaltma Filtresi\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Çözünürlükler eşleşiyor, ölçeği küçültmeye gerek yok]\"\nBasic.Settings.Video.FPSCommon=\"Ortak FPS Değerleri\"\nBasic.Settings.Video.FPSInteger=\"Tamsayı FPS değeri\"\nBasic.Settings.Video.FPSFraction=\"Kesirli FPS değeri\"\nBasic.Settings.Video.Numerator=\"Pay\"\nBasic.Settings.Video.Denominator=\"Payda\"\nBasic.Settings.Video.Renderer=\"İşleyici\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Deneysel)\"\nBasic.Settings.Video.InvalidResolution=\"Geçersiz çözünürlük değeri. [Genişlik]x[Yükseklik] şeklinde olmalıdır. (örnek 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Video çıkışı şu anda etkin durumda. Video ayarlarını değiştirmek için lütfen bütün çıkışları kapalı duruma getirin.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (En hızlı, ancak boyutlandırmada bulanık görüntü)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (Keskinleştirilmiş boyutlandırma, 16 örnek)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (Keskinleştirilmiş boyutlandırma, 36 örnek)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Alan (Genişletilmiş, 4/6/9 örnek)\"\nBasic.Settings.Audio=\"Ses\"\nBasic.Settings.Audio.SampleRate=\"Örnekleme Sıklığı\"\nBasic.Settings.Audio.Channels=\"Kanallar\"\nBasic.Settings.Audio.Meters=\"Metre\"\nBasic.Settings.Audio.MeterDecayRate=\"Bozulma Oranı\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Hızlı\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Orta (Tür I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Yavaş (Tür II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Tepe Ölçer Türü\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Örnek Tepe\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Gerçek Tepe (Daha yüksek CPU kullanımı)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"Uyarı: Surround ses etkin.\"\nBasic.Settings.Audio.MultichannelWarning=\"Yayın yapıyorsanız yayın hizmetinizin hem çevresel ses alımını hem de çevresel ses oynatmayı destekleyip desteklemediğini kontrol edin. Örneğin, Facebook 360 Live, çevresel sesi tam olarak destekler; YouTube Live, 5.1 ses alımını (ve TV'lerde oynatmayı) destekler.\\n\\nOBS ses filtreleri çevresel sesle uyumludur ancak VST eklenti desteğini garanti etmez.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Surround ses etkinleştirilsin mi?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Surround sesi etkinleştirmek istediğinize emin misiniz?\"\nBasic.Settings.Audio.Devices=\"Global Ses Cihazları\"\nBasic.Settings.Audio.DesktopDevice=\"Masaüstü Sesi\"\nBasic.Settings.Audio.DesktopDevice2=\"Masaüstü Sesi 2\"\nBasic.Settings.Audio.AuxDevice=\"Mikrofon/Yardımcı Ses\"\nBasic.Settings.Audio.AuxDevice2=\"Mikrofon/Yardımcı Ses 2\"\nBasic.Settings.Audio.AuxDevice3=\"Mikrofon/Yardımcı Ses 3\"\nBasic.Settings.Audio.AuxDevice4=\"Mikrofon/Yardımcı Ses 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Bas sustur'u etkinleştir\"\nBasic.Settings.Audio.PushToMuteDelay=\"Bas sustur gecikmesi\"\nBasic.Settings.Audio.EnablePushToTalk=\"Bas-Konuş'u Etkinleştir\"\nBasic.Settings.Audio.PushToTalkDelay=\"Bas-Konuş gecikmesi\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Cihaz bağlı ya da kullanılabilir değil]\"\nBasic.Settings.Audio.Disabled=\"Devre Dışı\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Düşük Gecikmeli Ses Arabelleğe Alma Modu (Decklink/NDI çıkışları için)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"UYARI: Düşük gecikmeli ses arabelleğe alma etkinleştirildi.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Düşük gecikmeli ses arabelleğe alma modu, sesin bozulmasına veya bazı kaynaklardan oynatılmamasına neden olabilir.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Düşük gecikmeli ses arabelleğe alma modu etkinleştirilsin mi?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Düşük gecikmeli ses arabelleğe alma modunu etkinleştirmek istediğinizden emin misiniz?\"\nBasic.Settings.Accessibility=\"Erişebilirlik\"\nBasic.Settings.Accessibility.ColorOverrides=\"Farklı Renkler Kullan\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Kaynak Kenarları (Seçim)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Kaynak Kenarları (Kırpma)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Kaynak Kenarları (Vurgu)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Karıştırıcı Ses Bandı (-60'tan -20dB'e kadar)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Karıştırıcı Ses Bandı (-20'den -9dB'e kadar)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Karıştırıcı Ses Bandı (-9'dan 0dB'e kadar)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Karıştırıcı Ses Bandı (-60'tan -20dB'e kadar) (aktif)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Karıştırıcı Ses Bandı (-20'den -9dB'e kadar) (aktif)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Karıştırıcı Ses Bandı (-9'dan 0dB'e kadar) (aktif)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Renk Ön Ayarı\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Varsayılan\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Özel\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Renk Körü Alternatifi\"\nBasic.Settings.Advanced=\"Gelişmiş\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Kapatırken etkin çıkışları göster\"\nBasic.Settings.Advanced.General.ProcessPriority=\"İşlem Önceliği\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Yüksek\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Normalin Üstünde\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Normalin Altında\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Boşta\"\nBasic.Settings.Advanced.FormatWarning=\"Uyarı: NV12/P010 dışındaki renk biçimleri esas olarak kayıt için tasarlanmıştır ve yayın anında kullanılması önerilmez. Yayın nedeni ile renk biçimini dönüştürme çok fazla İŞLEMCİ kullanımına sebep olabilir.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Uyarı: Yüksek hassasiyetli formatlar, HDR renk uzaylarında daha yaygın olarak kullanılır.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Uyarı: Rec. 2100 daha hassas bir biçim kullanmalıdır.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Renk Biçimi\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-Bit, 4:2:0, 2 düzlemsel)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 düzlemsel)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 düzlemsel)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 düzlemsel)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 düzlemsel)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 bit, 4:2:2, 2 düzlem)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 düzlem)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Renk Uzayı\"\nBasic.Settings.Advanced.Video.ColorRange=\"Renk Aralığı\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Sınırlı\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Tam\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR Beyaz Seviyesi\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR Nominal En Üst Seviye\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"İzleme Aygıtı\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Varsayılan\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Windows ses alçaltmasını devre dışı bırak\"\nBasic.Settings.Advanced.StreamDelay=\"Yayın Gecikmesi\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Süre\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Tatbik ederken kesim noktasını (gecikme artışı) koru\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Tahmini Bellek Kullanımı: %1 MB\"\nBasic.Settings.Advanced.Network=\"Ağ\"\nBasic.Settings.Advanced.Network.Disabled=\"Şu anki seçili yayın protokolü ağ ayarlarını değiştirmeyi desteklemiyor.\"\nBasic.Settings.Advanced.Network.BindToIP=\"IP Bağla\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP Ailesi\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Ağ optimizasyonlarını etkinleştir\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"TCP pacing'i etkinleştir\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"İletim hızını düzenleyerek RTMP çıkışını ağdaki diğer gecikmeye duyarlı uygulamalara daha dostça yapmaya çalışır.\\nKararsız bağlantılarda çerçeve bırakma riskini artırabilir.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Kısayol Tuşu Odak Davranışı\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Kısayol tuşlarını asla devre dışı bırakma\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Ana pencere odaktayken kısayol tuşlarını devre dışı bırak\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Pencere odakta değilken kısayol tuşlarını devre dışı bırak\"\nBasic.Settings.Advanced.AutoRemux=\"Otomatik olarak %1 türüne remux et\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(mkv olarak kaydet)\"\nBasic.AdvAudio=\"Gelişmiş Ses Özellikleri\"\nBasic.AdvAudio.ActiveOnly=\"Sadece Etkin Kaynaklar\"\nBasic.AdvAudio.Name=\"İsim\"\nBasic.AdvAudio.Volume=\"Ses\"\nBasic.AdvAudio.VolumeSource=\"'%1' için ses seviyesi\"\nBasic.AdvAudio.MonoSource=\"'%1' için Mono Downmix\"\nBasic.AdvAudio.Balance=\"Denge\"\nBasic.AdvAudio.BalanceSource=\"'%1' için dengeleme\"\nBasic.AdvAudio.SyncOffset=\"Eşitleme Uzaklığı\"\nBasic.AdvAudio.SyncOffsetSource=\"'%1' için çıktıyı senkronize et\"\nBasic.AdvAudio.Monitoring=\"Ses İzleme\"\nBasic.AdvAudio.Monitoring.None=\"Ekran Kapalı\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Sadece Ekran (sessiz çıkış)\"\nBasic.AdvAudio.Monitoring.Both=\"Ekran ve Çıkış\"\nBasic.AdvAudio.MonitoringSource=\"'%1' için ses denetimi\"\nBasic.AdvAudio.AudioTracks=\"Parçalar\"\nBasic.Settings.Hotkeys=\"Kısayollar\"\nBasic.Settings.Hotkeys.Pair=\"'%1' ile paylaşılan tuş kombinasyonu açma/kapama olarak çalışır\"\nBasic.Settings.Hotkeys.Filter=\"Filtre\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Kısayola göre filtrele\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Bu kısayol bir veya daha fazla eylem ile paylaşılıyor, çakışmaları görmek için tıklayın\"\nBasic.Settings.Hotkeys.PleaseWait=\"Kısayollar yükleniyor, lütfen bekleyin...\"\nBasic.Hotkeys.SelectScene=\"Sahneye geçiş yap\"\nBasic.SystemTray.Show=\"Göster\"\nBasic.SystemTray.Hide=\"Gizle\"\nBasic.SystemTray.Message.Reconnecting=\"Bağlantı kesildi. Yeniden bağlanılıyor...\"\nHotkeys.Insert=\"Ekle\"\nHotkeys.Delete=\"Sil\"\nHotkeys.Home=\"Ana Sayfa\"\nHotkeys.Print=\"Yazdır\"\nHotkeys.Pause=\"Duraklat\"\nHotkeys.Left=\"Sol\"\nHotkeys.Right=\"Sağ\"\nHotkeys.Up=\"Yukarı\"\nHotkeys.Down=\"Aşağı\"\nHotkeys.Super=\"Süper\"\nHotkeys.Menu=\"Menü\"\nHotkeys.Space=\"Boşluk\"\nHotkeys.NumpadMultiply=\"Numpad *\"\nHotkeys.NumpadDivide=\"Numpad /\"\nHotkeys.NumpadAdd=\"Numpad Ekle\"\nHotkeys.NumpadSubtract=\"Numpad -\"\nHotkeys.NumpadDecimal=\"Numpad ,\"\nHotkeys.AppleKeypadNum=\"%1 (Tuş takımı)\"\nHotkeys.AppleKeypadMultiply=\"* (Tuş takımı)\"\nHotkeys.AppleKeypadDivide=\"/ (Tuş takımı)\"\nHotkeys.AppleKeypadAdd=\"+ (Tuş takımı)\"\nHotkeys.AppleKeypadSubtract=\"- (Tuş takımı)\"\nHotkeys.AppleKeypadDecimal=\". (Tuş takımı)\"\nHotkeys.AppleKeypadEqual=\"= (Tuş takımı)\"\nHotkeys.MouseButton=\"%1 Fare\"\nMute=\"Sesi Kapat\"\nUnmute=\"Sesi Aç\"\nPush-to-mute=\"Bas-Sessize Al\"\nPush-to-talk=\"Bas konuş\"\nSceneItemShow=\"'%1' Göster\"\nSceneItemHide=\"'%1' Gizle\"\nOutputWarnings.NoTracksSelected=\"En az bir ses parçası seçmelisiniz\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Çıkış Ayarları Hatası\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Tüm çıkışlarda en az bir ses parçası seçilmiş olmalıdır.\"\nOutputWarnings.MP4Recording=\"Uyarı: MP4/MOV için kaydedilen kayıtları dosya (örneğin BSODs sonucu olarak, güç kayıp, vb.) kesinleşmiş değil varsa geri almanız mümkün olmayacaktır. Kaydetmek istediğiniz birden çok ses parçaları bu bittikten sonra MKV ve MP4/MOV için kayıt remux kullanmayı göz önünde bulundurun (dosya → Remux kayıtları)\"\nOutputWarnings.CannotPause=\"Dikkat: Kayıt kodlayıcısı \\\"(Yayın kodlayıcısını kullan)\\\"a ayarlanmışken kayıtlar durdurulamaz\"\nOutputWarnings.CodecIncompatible=\"Ses veya video çözücü seçimi uyumsuzluk yüzünden sıfırlandı. Lütfen listeden uyumlu bir çözücü seçin.\"\nCodecCompat.Incompatible=\"(%1 ile uyumlu değil)\"\nCodecCompat.CodecPlaceholder=\"Çözücü Seç...\"\nCodecCompat.ContainerPlaceholder=\"Yazı tipi seç...\"\nCodecCompat.CodecMissingOnExit.Title=\"Çözücü Seçilmedi\"\nCodecCompat.CodecMissingOnExit.Text=\"En az bir video ya da ses çözücüsü seçili değil. Lütfen hem kayıt hem de yayın için çözücü seçtiğinizden emin olun.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Biçim Seçilmedi\"\nCodecCompat.ContainerMissingOnExit.Text=\"Hiçbir kayıt biçimi seçilmedi. Lütfen seçilmiş yayın çözücüleriyle uyumlu bir kayıt biçimi seçin.\"\nFinalScene.Title=\"Sahneyi Sil\"\nFinalScene.Text=\"En az bir sahne olması gerekiyor.\"\nNoSources.Title=\"Kaynak Yok\"\nNoSources.Text=\"Henüz hiç video kaynağı eklemediniz gibi görünüyor, bu yüzden sadece boş bir ekran çıktısı alacaksınız. Bunu yapmak istediğinize emin misiniz?\"\nNoSources.Text.AddSource=\"Ana pencerede Kaynaklar kutusundaki + simgesine tıklayarak istediğiniz zaman kaynak ekleyebilirsiniz.\"\nNoSources.Label=\"Herhangi bir kaynağınız yok.\\nBir kaynak eklemek için aşağıdaki tuşlara basın ya da sağ tıklayın.\"\nChangeBG=\"Renk Ayarla\"\nCustomColor=\"Özel Renk\"\nBrowserSource.EnableHardwareAcceleration=\"Tarayıcı Kaynak Donanım Hızlandırmasını Etkinleştir\"\nAbout=\"Hakkında\"\nAbout.Info=\"OBS Studio özgür ve açık kaynaklı video kayıt ve canlı yayın yazılımıdır.\"\nAbout.Donate=\"Katkıda Bulunun\"\nAbout.GetInvolved=\"Katkıda Bulun\"\nAbout.Authors=\"Yazarlar\"\nAbout.License=\"Lisans\"\nAbout.Error=\"Hata! Dosya okunamadı.\\n\\nGit: %1\"\nAbout.Contribute=\"OBS Projesini Destekleyin\"\nAddUrl.Title=\"Bağlantı Adresi ile Kaynak Ekle\"\nAddUrl.Text=\"Bir bağlantı adresini OBS'e sürüklediniz. Bu, bağlantı otomatik olarak kaynak olarak eklenecek. Devam edilsin mi?\"\nAddUrl.Text.Url=\"Bağlantı Adresi: %1\"\nResizeOutputSizeOfSource=\"Çıkışı yeniden boyutlandır (kaynak boyutu)\"\nResizeOutputSizeOfSource.Text=\"Temel ve çıkış çözünürlüğü mevcut kaynağın boyutuna göre yeniden boyutlandırılacaktır.\"\nResizeOutputSizeOfSource.Continue=\"Devam etmek istiyor musunuz?\"\nPreviewTransition=\"Geçişi Önizle\"\nImporter=\"Sahne Koleksiyonu içe aktar\"\nImporter.SelectCollection=\"Bir Sahne Koleksiyonu seçin\"\nImporter.Collection=\"Sahne Koleksiyonu\"\nImporter.HelpText=\"OBS veya diğer desteklenen programlardan koleksiyonları içe aktarmak için bu pencereye dosya ekleyin.\"\nImporter.Path=\"Toplama Yolu\"\nImporter.Program=\"Tespit Edilen Uygulama\"\nImporter.AutomaticCollectionPrompt=\"Sahne Koleksiyonlarını Otomatik Olarak Ara\"\nImporter.AutomaticCollectionText=\"OBS, desteklenen üçüncü taraf programlardan içe aktarılabilir sahne koleksiyonlarını otomatik olarak bulabilir. OBS'nin koleksiyonları sizin için otomatik olarak bulmasını ister misiniz? \\n \\n Bunu daha sonra Ayarlar> Genel> İçe Aktarıcılar'dan değiştirebilirsiniz.\"\nImporter.SelectFile=\"Gözat...\"\nRestart=\"Yeniden Başlat\"\nNeedsRestart=\"OBS Studio'nun yeniden başlatılması gerekiyor. Şimdi yeniden başlatmak istiyor musunuz?\"\nLoadProfileNeedsRestart=\"Profil, OBS'nin yeniden başlatılmasını gerektiren ayarlar içeriyor:\\n%1\\n\\nBu değişikliklerin etkili olması için OBS'yi yeniden başlatmak istiyor musunuz?\"\nContextBar.NoSelectedSource=\"Kaynak seçilmemiş\"\nContextBar.MediaControls.PlayMedia=\"Medyayı oynat\"\nContextBar.MediaControls.PauseMedia=\"Medyayı Duraklat\"\nContextBar.MediaControls.StopMedia=\"Medyayı durdur\"\nContextBar.MediaControls.RestartMedia=\"Medyayı yeniden başlat\"\nContextBar.MediaControls.PlaylistNext=\"Diğer oynatma listesi\"\nContextBar.MediaControls.PlaylistPrevious=\"Önceki oynatma listesi\"\nContextBar.MediaControls.BlindSeek=\"Medya Arama Widget'ı\"\nYouTube.Auth.Ok=\"Yetkilendirme başarıyla tamamlandı.\\nArtık bu sayfayı kapatabilirsiniz.\"\nYouTube.Auth.NoCode=\"Yetkilendirme işlemi tamamlanmadı.\"\nYouTube.Auth.NoChannels=\"Seçili hesap için uygun kanal bulunamadı\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube Kullanıcı Yetkilendirmesi\"\nYouTube.Auth.WaitingAuth.Text=\"Lütfen harici tarayıcınızda yetkilendirmeyi tamamlayın.<br>Harici tarayıcı açılmazsa bu bağlantıyı takip edin ve yetkilendirmeyi tamamlayın:<br>%1\"\nYouTube.AuthError.Text=\"Kanal bilgisi alınamadı: %1.\"\nYouTube.Actions.WindowTitle=\"Youtube Yayın Akışı Kurulumu - Kanal: %1\"\nYouTube.Actions.CreateNewEvent=\"Yeni Yayın Oluştur\"\nYouTube.Actions.ChooseEvent=\"Varolan Bir Yayın Seç\"\nYouTube.Actions.Title=\"Başlık*\"\nYouTube.Actions.MyBroadcast=\"Yayınım\"\nYouTube.Actions.Description=\"Açıklama\"\nYouTube.Actions.Privacy=\"Gizlilik*\"\nYouTube.Actions.Privacy.Private=\"Gizli\"\nYouTube.Actions.Privacy.Public=\"Herkese Açık\"\nYouTube.Actions.Privacy.Unlisted=\"Liste dışı\"\nYouTube.Actions.Category=\"Kategori\"\nYouTube.Actions.Thumbnail=\"Önizleme\"\nYouTube.Actions.Thumbnail.SelectFile=\"Dosya seç...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Hiçbir dosya seçilmedi\"\nYouTube.Actions.Thumbnail.ClearFile=\"Temizle\"\nYouTube.Actions.MadeForKids=\"Bu video çocuklar için mi yapılmış?*\"\nYouTube.Actions.MadeForKids.Yes=\"Evet, çocuklar için yapıldı\"\nYouTube.Actions.MadeForKids.No=\"Hayır, çocuklar için yapılmadı\"\nYouTube.Actions.AdditionalSettings=\"Ek ayarlar\"\nYouTube.Actions.Latency=\"Gecikme\"\nYouTube.Actions.Latency.Low=\"Düşük\"\nYouTube.Actions.Latency.UltraLow=\"Ultra Düşük\"\nYouTube.Actions.EnableAutoStart=\"Otomatik Başlatmayı Etkinleştir\"\nYouTube.Actions.EnableAutoStop=\"Otomatik Durdurmayı Etkinleştir\"\nYouTube.Actions.AutoStartStop.TT=\"Bu planlı yayının otomatik başlayıp başlamayacağını gösterir\"\nYouTube.Actions.EnableDVR=\"DVR'ı Etkinleştir\"\nYouTube.Actions.ScheduleForLater=\"Daha sonra planlayın\"\nYouTube.Actions.RememberSettings=\"Bu seçenekleri hatırla\"\nYouTube.Actions.Create_Ready=\"Yayın oluştur\"\nYouTube.Actions.Create_GoLive=\"Yayın oluştur ve yayınlamaya başla\"\nYouTube.Actions.Choose_Ready=\"Yayın seç\"\nYouTube.Actions.Choose_GoLive=\"Yayın seç ve yayınlamaya başla\"\nYouTube.Actions.Create_Schedule=\"Yayını zamanla\"\nYouTube.Actions.Create_Schedule_Ready=\"Yayın zamanla ve seç\"\nYouTube.Actions.Dashboard=\"Youtube Studio'yu aç\"\nYouTube.Actions.Error.Title=\"Canlı yayın oluşturma hatası\"\nYouTube.Actions.Error.Text=\"YouTube erişim hatası '%1'.<br/>Ayrıntılı hata açıklamasına <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> buradan ulaşabilirsiniz\"\nYouTube.Actions.Error.General=\"YouTube erişim hatası. Lütfen ağ bağlantınızı veya YouTube sunucu erişiminizi kontrol edin.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Yayın oluşturma hatası '%1'.<br/> Ayrıntılı hata açıklamasına <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> buradan ulaşabilirsiniz\"\nYouTube.Actions.Error.NoStreamCreated=\"Yayın oluşturulmadı. Lütfen hesabınızı yeniden bağlayın.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API Hatası. Daha fazla bilgi için lütfen günlüğe bakın.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Seçili yayın bulunamadı.\"\nYouTube.Actions.Error.FileMissing=\"Seçilen dosya bulunamadı.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Seçilen dosya açılamadı.\"\nYouTube.Actions.Error.FileTooLarge=\"Seçilen dosya aşırı büyük (Limit: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Yayın geçişi başarısız oldu: %1<br/><br/>Bu hata devam ederse <a href='https://studio.youtube.com/video/%2/livestreaming'>YouTube Studio'da yayını açın</a> ve elle deneyin.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Yayın test aşamasına geçiyor, bu biraz zaman alabilir. Lütfen 10-30 saniye sonra tekrar deneyin.\"\nYouTube.Actions.EventsLoading=\"Etkinlik listesi yükleniyor...\"\nYouTube.Actions.EventCreated.Title=\"Etkinlik oluşturuldu\"\nYouTube.Actions.EventCreated.Text=\"Etkinlik başarıyla oluşturuldu.\"\nYouTube.Actions.Stream=\"Yayın\"\nYouTube.Actions.Stream.ScheduledFor=\"%1 için planlandı\"\nYouTube.Actions.Stream.Resume=\"Kesilmiş yayını devam ettir\"\nYouTube.Actions.Stream.YTStudio=\"Youtube Studio tarafından otomatik olarak oluşturuldu\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Yeni bir Canlı Yayın oluşturuluyor, lütfen bekleyin...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Manuel başlatma gerekli\"\nYouTube.Actions.AutoStartStreamingWarning=\"Bu etkinlik için otomatik başlatma devre dışı, Yayına Geç'e tıklayıp yayınınızı başlatın.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Canlı yayınınız bitecek ve tekrar bağlanamayacaksınız.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Seçili YouTube kanalı için canlı yayın yapma aktif değil.<br/><br/><a href='https://www.youtube.com/features'>youtube.com/features</a>'u inceleyerek daha fazla bilgi edinebilirsiniz.\"\nYouTube.Errors.livePermissionBlocked=\"Seçili YouTube kanalı için canlı yayın yapma uygun değil. <br/>Kanal ayarlarınızda bu özelliği aktifleştirdiğinizde değişikliğin yansıması 24 saati bulabilir.<br/><br/><a href='https://www.youtube.com/features'>youtube.com/features</a>'u inceleyerek daha fazla bilgi edinebilirsiniz.\"\nYouTube.Errors.errorExecutingTransition=\"Bir arka uç hatası nedeniyle geçiş başarısız oldu. Lütfen birkaç saniye sonra tekrar deneyin.\"\nYouTube.Errors.errorStreamInactive=\"YouTube akışınızdan veri almıyor. Lütfen yapılandırmanızı denetleyip tekrar deneyin.\"\nYouTube.Errors.invalidTransition=\"Denenen geçiş geçersizdi. Bu, akış önceki geçişi bitirememesinden dolayı olabilir. Birkaç saniye bekleyip yeniden deneyin.\"\nYouTube.DocksRemoval.Title=\"Eski Youtube Tarayıcı Yuvalarını Temizle\"\nYouTube.DocksRemoval.Text=\"Bu tarayıcı yuvaları kullanım dışı bırakıldığından dolayı kaldırılıcaktır:\\n\\n%1\\nBunun yerine \\\"Yuvalar/Youtube Canlı Kontrol Odası\\\" kullanınız.\"\nConfigDownload.WarningMessageTitle=\"Uyarı\"\nFailedToStartStream.MissingConfigURL=\"Şu anki hizmet için herhangi bir konfigürasyon URL'si bulunamadı.\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Özel RTMP URL'si Belirtilmemiş\"\nFailedToStartStream.InvalidCustomConfig=\"Geçersiz özel yapılandırma\"\nFailedToStartStream.MissingCanvas=\"Yapılandırılmış ek tuval eksik\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Çok kanallı video hizmeti oluşturulamadı\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Çok kanallı video RTMP çıkışı oluşturulamadı\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC kullanılamıyor.\\n\\n'%1' kodlayıcı türü bulunamadı\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"'%1' video kodlayıcı oluşturulamadı (tür: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"'%1' kodlayıcı oluşturulurken OBS video bilgisi alınamadı (tür: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Ses kodlayıcı oluşturulamadı\"\nFailedToStartStream.NoRTMPURLInConfig=\"Yapılandırma, akış hedefi RTMP(S) URL'sini içermiyor\"\nFailedToStartStream.FallbackToDefault=\"Yayını %1 kullanarak başlatmak başarısız oldu; Tek kodlama ayarlarını kullanarak yeniden denemek ister misiniz?\"\nFailedToStartStream.ConfigRequestFailed=\"%1'den yapılandırma getirilemedi<br><br>HTTP hatası: %2\"\nFailedToStartStream.WarningUnknownStatus=\"bilinmeyen durum değeri '%1' alındı\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\n%1 olmadan yayına devam etmek istiyor musunuz?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nYayına devam etmek istiyor musunuz?\"\nFailedToStartStream.MissingEncoderConfigs=\"Canlı yayına geçiş yapılandırması, kodlayıcı yapılandırmalarını içermiyor\"\nFailedToStartStream.InvalidEncoderConfig=\"Canlı yayın yapılandırması geçersiz bir kodlayıcı yapılandırması içeriyordu\"\nFailedToStartStream.StatusMissingHTML=\"Canlı yayına geçme isteği belirtilmemiş bir hata döndürdü\"\nFailedToStartStream.NoConfigSupplied=\"Eksik yapılandırma\"\nMultitrackVideo.Info=\"%1, birden fazla video kalitesini kodlamak ve göndermek için ayarlarınızı otomatik olarak optimize eder. Bu seçeneği seçtiğinizde bilgisayarınız ve yazılım kurulumunuz hakkında %2 bilgi gönderilecektir.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Uyumsuz Ayarlar\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 şu anda şununla uyumlu değil:\\n\\n%2\\n%1 ile yayına devam etmek için uyumsuz ayarları devre dışı bırakın:\\n\\n%3\\nve Yayını yeniden başlatın.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Bu yayın için devre dışı bırakın ve Yayını Başlatın\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Ayarları Güncelleyin ve Yayını Başlatın\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 şu anda '%2', %3 olarak ayarlanmış [Ses → Genel → Kanallar] ile uyumlu değil\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Ses → Genel → Kanallar] '%1' olarak ayarlanmalı\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1, [Ses → Genel → Kanallar] için birden fazla farklı ayar gerektirir\"\nBasic.OpenPluginManager=\"Eklenti Yöneticisi\"\nPluginManager=\"Eklenti Yöneticisi\"\nPluginManager.Restart=\"Eklenti değişikliklerini uygulamak için OBS Studio yeniden başlatılmalıdır.\"\nPluginManager.MissingPlugin=\"[EKLENTI BULUNAMADI]\"\nPluginManager.Section.Discover=\"Gözat\"\nPluginManager.Section.Manage=\"Yüklendi\"\nPluginManager.Section.Updates=\"Güncellemeler\"\nPluginManager.Section.Manage.Title=\"Etkin Eklentileri Yönet\"\n"
  },
  {
    "path": "frontend/data/locale/tt-RU.ini",
    "content": "Language=\"Татарча\"\nOK=\"Ярый\"\nApply=\"Куллану\"\nCancel=\"Баш тарту\"\nClose=\"Ябу\"\nSave=\"Саклау\"\nDiscard=\"Кире кагу\"\nDisable=\"Сүндерү\"\nYes=\"Әйе\"\nNo=\"Юк\"\nAdd=\"Өстәү\"\nRemove=\"Бетерү\"\nRename=\"Яңадан исемләү\"\nInteract=\"Эш итү\"\nFilters=\"Фильтрлар\"\nProperties=\"Үзләкләр\"\nMoveUp=\"Күтәрергә\"\nMoveDown=\"Төшерергә\"\nSettings=\"Көйләүләр\"\nDisplay=\"Экран\"\nName=\"Исем\"\nExit=\"Чыгу\"\nMixer=\"Аудиомикшер\"\nBrowse=\"Күзәтү\"\nMono=\"Моно\"\nStereo=\"Стерео\"\nClear=\"Чистарту\"\nRevert=\"Кайтару\"\nShow=\"Күрсәтү\"\nHide=\"Яшерү\"\nUntitled=\"Исемсез\"\nNew=\"Ясау\"\nDuplicate=\"Кабатлау\"\nEnable=\"Кушу\"\nTransition=\"Күчеш\"\nLeft=\"Сул\"\nRight=\"Уң\"\nTop=\"Өстә\"\nBottom=\"Аста\"\nReset=\"Ташлату\"\nHours=\"сәгатьтән\"\nMinutes=\"минуттан\"\nSeconds=\"секундтан\"\nDeprecated=\"Искергән\"\nReplayBuffer=\"Кабатлау буферы\"\nImport=\"Импортлау\"\nExport=\"Экспортлау\"\nCopy=\"Күчермә алу\"\nPaste=\"Кую\"\nNext=\"Алга\"\nBack=\"Артка\"\nNone=\"Юк\"\nStudioMode.Preview=\"Карап алу\"\nStudioMode.Program=\"Програм\"\nStudioMode.ProgramSceneName=\"Програм: %1\"\nGroup=\"Төркем\"\nDoNotShowAgain=\"Бүтән күрсәтмәскә\"\nDefault=\"(Беренчел)\"\nCalculating=\"Исәпләнү...\"\nFullscreen=\"Тулы экран шарты\"\nWindowed=\"Тәрәзә шарты\"\nRefreshBrowser=\"Яңарту\"\nLockVolume=\"Катылыкны бикләү\"\nLogViewer=\"Журналны карау\"\nOpenFile=\"Файлны ачу\"\nAddSource=\"Чыганакны өстәү\"\nRemoveScene=\"Сайланган сәхнәне бетерү\"\nRemoveSource=\"Сайланган чыганак(лар)ны бетерү\"\nList=\"Исемлек\"\nGrid=\"Ятьмә\"\nAutomatic=\"Автоматик\"\nPluginsFailedToLoad.Title=\"Кушылманы йөкләү хатасы\"\nAlreadyRunning.Title=\"OBS кабызылды инде\"\nAlreadyRunning.LaunchAnyway=\"Барыбер кабызу\"\nSafeMode.RestartNormal=\"Сез OBS'ны гади шартта яңадан кабызырга телисезме?\"\nChromeOS.Title=\"Хупланмаган платформа\"\nWine.Title=\"Wine табылды\"\nAuth.LoadingChannel.Title=\"Канал турында мәгълүматны йөкләү...\"\nAuth.LoadingChannel.Text=\"%1 өчен канал турында мәгълүматны йөкләү, көтегезче...\"\nAuth.LoadingChannel.Error=\"Канал турында мәгълүматны алып булмады.\"\nAuth.ChannelFailure.Title=\"Каналны йөкләп булмады\"\nAuth.Chat=\"Чат\"\nAuth.StreamInfo=\"Агым турында мәгълүмат\"\nTwitchAuth.Stats=\"Twitch статистикасы\"\nTwitchAuth.Feed=\"Twitch активлыгы тасмасы\"\nRestreamAuth.Channels=\"Restream каналлары\"\nPaste.Filters=\"Фильтрларны кую\"\nBrowserPanelInit.Title=\"Браузер әзерләнү...\"\nBrowserPanelInit.Text=\"Браузер әзерләнү, көтегезче...\"\nBandwidthTest.Region=\"Регион\"\nBandwidthTest.Region.US=\"АКШ\"\nBandwidthTest.Region.EU=\"Аурупа\"\nBandwidthTest.Region.Asia=\"Азия\"\nBandwidthTest.Region.Other=\"Башка\"\nBasic.AutoConfig=\"Авто көйләве остасы\"\nBasic.AutoConfig.ApplySettings=\"Көйләүләрне куллану\"\nBasic.AutoConfig.StartPage=\"Кулланылу турында мәгълүмат\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Мин виртуаль камераны гына кулланачакмын\"\nBasic.AutoConfig.VideoPage=\"Видео көйләүләре\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Хәзергене (%1×%2) куллану\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Экран %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Хәзергене (%1) куллану\"\nBasic.AutoConfig.StreamPage=\"Агым турында мәгълүмат\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Аккаунттан чыгу\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Аккаунттан чыгарга?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Агым ачкычын алу\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Күбрәк мәгълүмат\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Агым ачкычын куллану\"\nBasic.AutoConfig.StreamPage.Service=\"Хезмәт\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Барлыкны күрсәтү...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Көйләнелүчән...\"\nBasic.AutoConfig.StreamPage.Server=\"Сервер\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Агым ачкычы\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Тоташылган аккаунт\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Тапшыру кисәтүе\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"%1 тикшерү\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"%1 урынына тоташылу...\"\nBasic.Stats=\"Статистика\"\nBasic.Stats.CPUUsage=\"ҮП-ны куллану\"\nBasic.Stats.MemoryUsage=\"Хәтерне куллану\"\nBasic.Stats.Output.Stream=\"Агым\"\nBasic.Stats.Output.Recording=\"Яздыру\"\nBasic.Stats.Status=\"Халәт\"\nBasic.Stats.Status.Recording=\"Яздырылу\"\nBasic.Stats.Status.Live=\"АГЫМДА\"\nBasic.Stats.Status.Reconnecting=\"Яңадан тоташылу\"\nBasic.Stats.Status.Inactive=\"Активсыз\"\nBasic.Stats.Status.Active=\"Актив\"\nBasic.Stats.Bitrate=\"Битрейт\"\nUpdater.Title=\"Яңа яңарту бар\"\nUpdater.Text=\"Яңа яңарту бар:\"\nUpdater.UpdateNow=\"Хәзер яңарту\"\nUpdater.Skip=\"Версияне калдыру\"\nUpdater.NoUpdatesAvailable.Title=\"Яңарту юк\"\nUpdater.FailedToLaunch=\"Яңарткычны кабызып булмады\"\nBasic.TransitionDuration=\"Озынлык\"\nBasic.EnablePreviewProgramMode=\"Студия шартын кушу\"\nBasic.DisablePreviewProgramMode=\"Студия шартын сүндерү\"\nUndo.Undo=\"Кире кагу\"\nUndo.Redo=\"Кабатлау\"\nUndo.Add=\"«%1» өстәүне\"\nUndo.Delete=\"«%1» бетерүне\"\nUndo.Rename=\"«%1» яңадан исемләүне\"\nUndo.SceneCollection.Switch=\"«%1» күчерүне\"\nUndo.Item.Undo=\"%1 баш тарту\"\nUndo.Sources.Multi=\"%1 чыганакны бетерү\"\nTitleBar.SafeMode=\"САК ШАРТЫ\"\nTitleBar.PortableMode=\"Күчерелмәле шарт\"\nTitleBar.Profile=\"Профиль\"\nTitleBar.Scenes=\"Сәхнәләр\"\nNameExists.Title=\"Иесм бар инде\"\nNameExists.Text=\"Исем кулланыла инде.\"\nConfirmStart.Title=\"Агымны башларгамы?\"\nConfirmStop.Title=\"Агымны туктатыргамы?\"\nConfirmStopRecord.Title=\"Яздыруны туктатыргамы?\"\nConfirmReset.Title=\"Үзләкләрне ташлату\"\nOutput.StartStreamFailed=\"Агымны башлап булмады\"\nOutput.StartRecordingFailed=\"Яздыруны башлап булмады\"\nOutput.StartVirtualCamFailed=\"Виртуаль камераны башлап булмады\"\nOutput.ConnectFail.Title=\"Тоташылып булмады\"\nOutput.ConnectFail.ConnectFailed=\"Серверга тоташылып булмады\"\nOutput.ConnectFail.Disconnected=\"Сервердан өзелде.\"\nOutput.RecordFail.Title=\"Яздыруны башлап булмады\"\nOutput.RecordNoSpace.Title=\"Дисктагы урын җитми\"\nOutput.RecordError.Title=\"Яздыру хатасы\"\nOutput.BadPath.Title=\"Хаталы файл юлы\"\nOutput.BroadcastStartFailed=\"Тапшыруны башлап булмады\"\nOutput.BroadcastStopFailed=\"Тапшыруны туктатып булмады\"\nRemux.SourceFile=\"OBS яздыруы\"\nRemux.TargetFile=\"Чыгыш файл\"\nRemux.OBSRecording=\"OBS яздыруы\"\nRemux.SelectRecording=\"OBS яздыруын сайлагыз...\"\nRemux.SelectTarget=\"Чыгыш файл сайлагыз...\"\nRemux.FileExistsTitle=\"Чыгыш файллар бар\"\nMissingFiles.NewFile=\"Яңа файл\"\nMissingFiles.Clear=\"<чистартылган>\"\nMissingFiles.NumFound=\"%2 нәтиҗәдән %1 табылды\"\nMissingFiles.Search=\"Директорияне эзләү...\"\nMissingFiles.SelectFile=\"Файлны сайлау...\"\nMissingFiles.State=\"Халәт\"\nMissingFiles.Found=\"Табылды\"\nMacPermissions.OpenPreferences=\"%1 көйләүләрен ачу\"\nMacPermissions.Item.ScreenRecording=\"Экранны яздыру\"\nMacPermissions.Item.Camera=\"Камера\"\nMacPermissions.Item.Microphone=\"Микрофон\"\nMacPermissions.Item.Accessibility=\"Махсус мөмкинлекләр\"\nMacPermissions.Continue=\"Дәвам итү\"\nBasic.Scene=\"Сәхнә\"\nScaleFiltering.Bicubic=\"Бикубик\"\nScaleFiltering.Lanczos=\"Ланцош\"\nBlendingMethod.Default=\"(Беренчел)\"\nBlendingMethod.SrgbOff=\"SRGB сүнек\"\nBlendingMode.Additive=\"Өстәү\"\nBlendingMode.Screen=\"Экран\"\nBlendingMode.Multiply=\"Тапкырлау\"\nDeinterlacing.Discard=\"Кире кагу\"\nVolControl.Properties=\"«%1» үзлекләре\"\nBasic.Main.AddSceneDlg.Title=\"Сәхнәне өстәргә\"\nBasic.Main.DefaultSceneName.Text=\"Сәхнә %1\"\nBasic.Main.AddSceneCollection.Title=\"Сәхнә коллекциясен өстәү\"\nBasic.Main.RenameSceneCollection.Title=\"Сәхнә коллекциясен яңадан исемләү\"\nAddProfile.Title=\"Профильне өстәү\"\nRenameProfile.Title=\"Профильне яңадан исемләү\"\nBasic.SourceSelect=\"Чыганакны ясау/сайлау\"\nBasic.SourceSelect.CreateNew=\"Яңаны ясау\"\nBasic.Main.Sources.Lock=\"Бикләү\"\nBasic.PropertiesWindow=\"«%1» үзлекләре\"\nBasic.PropertiesWindow.SelectColor=\"Төсне сайлау\"\nBasic.PropertiesWindow.SelectFont=\"Хәрефләрне сайлау\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Хәрефне сайлау\"\nBasic.PropertiesWindow.ConfirmTitle=\"Көйләүләр үзгәртелде\"\nBasic.PropertiesWindow.NoProperties=\"Үзлекләр юк\"\nBasic.PropertiesWindow.AddFiles=\"Файлларны өстәү\"\nBasic.PropertiesView.UrlButton.Text.Url=\"Сылтама: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"URL-ны ачу\"\nBasic.StatusBar.Delay=\"Тоткарлагыч (%1 сек)\"\nBasic.Filters=\"Фильтрлар\"\nBasic.TransformWindow.Position=\"Урын\"\nBasic.TransformWindow.Rotation=\"Әйләнү\"\nBasic.TransformWindow.Size=\"Зурлык\"\nBasic.TransformWindow.Width=\"Киңлек\"\nBasic.TransformWindow.Height=\"Биеклек\"\nBasic.TransformWindow.NoSelectedSource=\"Чыганак сайланмады\"\nBasic.Main.AddSourceHelp.Title=\"Чыганакны өстәп булмый\"\nBasic.Main.Scenes=\"Сәхнәләр\"\nBasic.Main.Sources=\"Чыганаклар\"\nBasic.Main.Source=\"Чыганак\"\nBasic.Main.Controls=\"Идарә\"\nBasic.Main.PreparingStream=\"Әзерләү...\"\nBasic.Main.Connecting=\"Тоташылу...\"\nBasic.Main.StartRecording=\"Яздыруны башлау\"\nBasic.Main.StartStreaming=\"Агымны башлау\"\nBasic.Main.StartBroadcast=\"Агымда тапшыру\"\nBasic.Main.StartVirtualCam=\"Виртуаль камераны башлау\"\nBasic.Main.StopRecording=\"Яздыруны туктату\"\nBasic.Main.PauseRecording=\"Яздыруны туктатып тору\"\nBasic.Main.UnpauseRecording=\"Яздыруны дәвам итү\"\nBasic.Main.StoppingRecording=\"Яздыру туктатып торылу...\"\nBasic.Main.StopStreaming=\"Агымны туктату\"\nBasic.Main.StopBroadcast=\"Агымда тапшыруны тәмамлау\"\nBasic.Main.StoppingStreaming=\"Агым туктатып торылу...\"\nBasic.Main.Group=\"Төркем %1\"\nBasic.Main.GridMode=\"Ятьмә шарты\"\nBasic.Main.ListMode=\"Исемлек шарты\"\nBasic.VCam.VirtualCamera=\"Виртуаль камера\"\nBasic.VCam.OutputType=\"Чыгыш төре\"\nBasic.MainMenu.File=\"Файл (&F)\"\nBasic.MainMenu.File.Export=\"Экспортлау (&E)\"\nBasic.MainMenu.File.Import=\"Импортлау (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Яздыруларны күрсәтү (&R)\"\nBasic.MainMenu.File.Settings=\"Көйләүләр (&S)\"\nBasic.MainMenu.File.Exit=\"Чыгу (&X)\"\nBasic.MainMenu.Edit=\"Үзгәртү (&E)\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Киндер (%1×%2)\"\nBasic.MainMenu.Edit.Order=\"Тәртип (&O)\"\nBasic.MainMenu.Profile=\"Профиль (&P)\"\nBasic.MainMenu.Profile.Import=\"Профильне импортлау\"\nBasic.MainMenu.Profile.Export=\"Профильне эскпортлау\"\nBasic.MainMenu.SceneCollection.Import=\"Сәхнә коллекциясен импортлау\"\nBasic.MainMenu.SceneCollection.Export=\"Сәхнә коллекциясен экспортлау\"\nBasic.MainMenu.Profile.Exists=\"Профиль бар инде\"\nBasic.MainMenu.Tools=\"Кораллар (&T)\"\nBasic.MainMenu.Help=\"Ярдәм (&H)\"\nBasic.MainMenu.Help.Discord=\"&Discord-серверга кушылу\"\nBasic.MainMenu.Help.WhatsNew=\"Яңалыклар\"\nBasic.MainMenu.Help.About=\"Кушымта турында (&A)\"\nBasic.Settings.General=\"Төп\"\nBasic.Settings.General.Language=\"Тел\"\nBasic.Settings.General.Updater=\"Яңартулар\"\nBasic.Settings.General.UpdateChannel=\"Яңарту каналы\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Сүнек)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Беренчел)\"\nBasic.Settings.General.SysTray=\"Хәбәрләр өлкәсе\"\nBasic.Settings.General.Preview=\"Алдан карау\"\nBasic.Settings.General.ChannelName.stable=\"Тотраклы версия\"\nBasic.Settings.General.ChannelDescription.stable=\"Соңгы тотраклы версия\"\nBasic.Settings.General.ChannelName.beta=\"Ярдәмче версияләр\"\nBasic.Settings.Appearance=\"Тышкы күренеш\"\nBasic.Settings.Appearance.General=\"Төп\"\nBasic.Settings.Appearance.General.Theme=\"Тема\"\nBasic.Settings.Appearance.General.Variant=\"Стиль\"\nBasic.Settings.Appearance.General.NoVariant=\"Стиль юк\"\nBasic.Settings.Stream=\"Агым\"\nBasic.Settings.Stream.Custom.Username=\"Кулланучы исеме\"\nBasic.Settings.Stream.Custom.Password=\"Серсүз\"\nBasic.Settings.Stream.TTVAddon=\"Twitch чаты өстәмәләре\"\nBasic.Settings.Stream.TTVAddon.None=\"Юк\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Көйләүләрне ачу\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1 кушу\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Автоматик\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Автоматик\"\nBasic.Settings.Output=\"Чыгыш\"\nBasic.Settings.Output.Format=\"Яздыру форматы\"\nBasic.Settings.Output.Format.MKV=\"«Матрёшка» видеоформаты (.mkv)\"\nBasic.Settings.Output.Mode=\"Чыгыш шарты\"\nBasic.Settings.Output.Mode.Simple=\"Гади\"\nBasic.Settings.Output.Mode.Adv=\"Киңәйтелгән\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg чыгышы\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Суффикс\"\nBasic.Settings.Output.Simple.SavePath=\"Яздыру юлы\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Яздыру сыйфаты\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Җиһаз (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Җиһаз (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Җиһаз (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Җиһаз (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Җиһаз (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Җиһаз (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Җиһаз (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Җиһаз (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Җиһаз (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Җиһаз (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (Беренчел)\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Ачыклык: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Кадр ешлыгы: %1\"\nBasic.Settings.Output.VideoBitrate=\"Видео битрейты\"\nBasic.Settings.Output.AudioBitrate=\"Аудио битрейты\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Сүнек\"\nBasic.Settings.Output.Adv.Streaming=\"Агым\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Агым көйләүләре\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Тавыш юлы 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Тавыш юлы 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Тавыш юлы 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Тавыш юлы 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Тавыш юлы 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Тавыш юлы 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD юлы\"\nBasic.Settings.Output.Adv.Recording=\"Яздыру\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Яздыру көйләүләре\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Яздыру төре\"\nBasic.Settings.Output.Adv.Recording.Type=\"Төр\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg чыгыш төре\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Барлык файллар\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Файл юлы яки сылтама\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Аудио\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Видео\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg көйләүләре\"\nFilenameFormatting.TT.CCYY=\"Ел, дүрт сан\"\nFilenameFormatting.TT.mm=\"Минут (00-59)\"\nFilenameFormatting.TT.ss=\"Секунд (00-59)\"\nFilenameFormatting.TT.Percent=\"% символы\"\nFilenameFormatting.TT.B=\"Айның туры исеме\"\nFilenameFormatting.TT.M=\"Минут (00-59)\"\nFilenameFormatting.TT.S=\"Секунд (00-59)\"\nFilenameFormatting.TT.Y=\"Ел\"\nFilenameFormatting.TT.FPS=\"Кадр ешлыгы\"\nFilenameFormatting.TT.VF=\"Видео форматы\"\nBasic.Settings.Video=\"Видео\"\nBasic.Settings.Video.FPS=\"Кадр ешлыгы\"\nBasic.Settings.Video.Renderer=\"Күрсәткеч\"\nBasic.Settings.Audio=\"Аудио\"\nBasic.Settings.Audio.Channels=\"Каналлар\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Тиз\"\nBasic.Settings.Audio.Devices=\"Төп аудио җайланмалары\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Җайланма тоташылмады яки ирешерлек түгел]\"\nBasic.Settings.Audio.Disabled=\"Сүнек\"\nBasic.Settings.Accessibility=\"Махсус мөмкинлекләр\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"(Беренчел)\"\nBasic.Settings.Advanced=\"Киңәйтелгән\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Югары\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Төс форматы\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 бит, 4:2:0, 2 яссылык)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 бит, 4:2:0, 3 яссылык)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 бит, 4:4:4, 3 яссылык)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 бит, 4:2:0, 2 яссылык)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 бит, 4:2:0, 3 яссылык)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 бит, 4:2:2, 2 яссылык)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 бит, 4:4:4, 2 яссылык)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 бит)\"\nBasic.Settings.Advanced.Video.ColorRange=\"Төс арасы\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Чикләнгән\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Тулы\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Беренчел\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Озынлык\"\nBasic.Settings.Advanced.Network=\"Челтәр\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(mkv дип яздыру)\"\nBasic.AdvAudio=\"Киңәйтелгән аудио үзлекләре\"\nBasic.AdvAudio.ActiveOnly=\"Актив чыганаклар гына\"\nBasic.AdvAudio.Name=\"Исем\"\nBasic.AdvAudio.Volume=\"Катылык\"\nBasic.AdvAudio.VolumeSource=\"«%1» өчен катылык\"\nBasic.AdvAudio.Mono=\"Моно\"\nBasic.AdvAudio.Balance=\"Баланс\"\nBasic.Settings.Hotkeys=\"Кайнар төймәләре\"\nBasic.SystemTray.Show=\"Күрсәтү\"\nBasic.SystemTray.Hide=\"Яшерү\"\nBasic.SystemTray.Message.Reconnecting=\"Өзелде. Яңадан тоташылу...\"\nHotkeys.Insert=\"Insert (INS)\"\nHotkeys.Delete=\"Delete (DEL)\"\nHotkeys.PageUp=\"Page Up (PGUP)\"\nHotkeys.PageDown=\"Page Down (PGDN)\"\nHotkeys.ScrollLock=\"Scroll Lock (SCRLK)\"\nHotkeys.Backspace=\"Backspace (←)\"\nHotkeys.Tab=\"Tab (↹)\"\nHotkeys.Print=\"Print Screen (PRTSC)\"\nHotkeys.Left=\"Сулга ук\"\nHotkeys.Right=\"Уңга ук\"\nHotkeys.Up=\"Өскә ук\"\nHotkeys.Down=\"Аска ук\"\nHotkeys.Windows=\"⊞ Win\"\nHotkeys.Escape=\"Escape\"\nMute=\"Тавышсыз итү\"\nSceneItemShow=\"«%1» күрсәтү\"\nSceneItemHide=\"«%1» яшерү\"\nCodecCompat.Incompatible=\"(%1 белән ярашлы түгел)\"\nCodecCompat.ContainerPlaceholder=\"Форматны сайлау...\"\nCodecCompat.ContainerMissingOnExit.Title=\"Формат сайланмады\"\nFinalScene.Title=\"Сәхнәне бетерү\"\nNoSources.Title=\"Чыганак юк\"\nAbout=\"Програм турында\"\nAbout.Authors=\"Авторлар\"\nAbout.License=\"Лицензия\"\nAddUrl.Text.Url=\"Сылтама: %1\"\nResizeOutputSizeOfSource.Continue=\"Дәвам итәргә телисезме?\"\nImporter.SelectCollection=\"Сәхнә коллекциясен сайлагыз\"\nImporter.Collection=\"Сәхнә коллекциясе\"\nImporter.Path=\"Коллекция юлы\"\nRestart=\"Яңадан башлау\"\nContextBar.NoSelectedSource=\"Чыганак сайланмады\"\nYouTube.Actions.Title=\"Исем*\"\nYouTube.Actions.MyBroadcast=\"Минем агымым\"\nYouTube.Actions.Description=\"Тасвирлама\"\nYouTube.Actions.Privacy=\"Хосусыйлык*\"\nYouTube.Actions.Privacy.Private=\"Ябык\"\nYouTube.Actions.Privacy.Public=\"Ачык\"\nYouTube.Actions.Category=\"Категория\"\nYouTube.Actions.Thumbnail.SelectFile=\"Файлны сайлау...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Файл сайланмады\"\nYouTube.Actions.Thumbnail.ClearFile=\"Чистарту\"\nYouTube.Actions.AdditionalSettings=\"Өстәмә көйләүләр\"\nYouTube.Actions.Latency=\"Тоткарлык\"\nYouTube.Actions.Latency.Normal=\"Гади\"\nYouTube.Actions.Error.FileMissing=\"Сайланган файл юк.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Сайлаган файлны ачып булмады.\"\nYouTube.Actions.EventCreated.Title=\"Вакыйга ясалды\"\nYouTube.Actions.Stream=\"Агым\"\nConfigDownload.WarningMessageTitle=\"Кисәтү\"\n"
  },
  {
    "path": "frontend/data/locale/ug-CN.ini",
    "content": "Language=\"ئۇيغۇرچە\"\nOK=\"جەزملە\"\nApply=\"قوللان\"\nCancel=\"ۋاز كەچ\"\nClose=\"ياپ\"\nSave=\"ساقلا\"\nDiscard=\"تاشلىۋەت\"\nDisable=\"چەكلە\"\nYes=\"ھەئە\"\nNo=\"ياق\"\nAdd=\"قوش\"\nRemove=\"چىقىرىۋەت\"\nRename=\"ئات ئۆزگەرت\"\nInteract=\"تەسىرلىشىش\"\nFilters=\"سۈزگۈچ\"\nProperties=\"خاسلىق\"\nMoveUp=\"ئۈستىگە\"\nMoveDown=\"ئاستىغا\"\nSettings=\"تەڭشەكلەر\"\nDisplay=\"كۆرسەتكۈچ\"\nName=\"ئاتى\"\nExit=\"چېكىن\"\nMixer=\"ئۈن بىرىكتۈرگۈچ\"\nBrowse=\"كۆز يۈگۈرت\"\nMono=\"يەككە يوللۇق ئاۋاز\"\nStereo=\"ئىستېرېئو\"\nDroppedFrames=\"يوقىتىلغان كاندۇك %1 (%2%)\"\nProjector.Open.Program=\"پىروگرامما پىرويېكتورىنى ئاچ\"\nProjector.Open.Preview=\"ئالدىن كۆزىتىش پىرويېكتورىنى ئاچ\"\nProjector.Open.Scene=\"سەھنە پىرويېكتورىنى ئاچ\"\nProjector.Open.Source=\"مەنبە پىرويېكتورىنى ئاچ\"\nProjector.Open.Multiview=\"كۆپ كۆرۈنۈش ئاچ\"\nProjector.Display=\"ئېكران: %1\"\nProjector.Window=\"يېڭى كۆزنەك\"\nProjector.Title=\"پىرويېكتور\"\nProjector.Title.Scene=\"كۆرۈنۈش: %1\"\nProjector.Title.Source=\"مەنبە: %1\"\nProjector.Title.Multiview=\"كۆپ كۆرۈنۈش\"\nProjector.ResizeWindowToContent=\"كۆزنەكنى مەزمۇنغا ماسلاشتۇر\"\nClear=\"تازىلا\"\nRevert=\"ئەسلىگە ياندۇر\"\nShow=\"كۆرسەت\"\nHide=\"يوشۇر\"\nUnhideAll=\"ھەممىنى ئاشكارىلا\"\nUntitled=\"ماۋزۇسىز\"\nNew=\"يېڭى\"\nDuplicate=\"كۆچۈر\"\nEnable=\"قوزغات\"\nDisableOSXVSync=\"MacOS V-Sync نى چەكلە\"\nResetOSXVSyncOnExit=\"چېكىنگەندە MacOS V-Sync نى ئەسلىگە قايتۇر\"\nHighResourceUsage=\"كودلاش نورمىدىن ئاشۇرۇپ يۈكلەندى! سىن تەڭشىكىنى تۆۋەنلىتىشنى ياكى تېزرەك بولغان ئالدىن كودلاش تەڭشىكى ئىشلىتىشنى ئويلىشڭ.\"\nTransition=\"ئالمىشىش\"\nQuickTransitions=\"تېز ئالمىشىش\"\nFadeToBlack=\"قارىغا سۇسلاشتۇر\"\nLeft=\"سول\"\nRight=\"ئوڭ\"\nTop=\"ئۈستى\"\nBottom=\"ئاستى\"\nReset=\"ئەسلىگە قايتۇر\"\nHours=\"سائەت\"\nMinutes=\"مىنۇت\"\nSeconds=\"سېكۇنت\"\nDeprecated=\"ۋاقتى ئۆتكەن\"\nReplayBuffer=\"يىغلەكنى قايتا قوي\"\nImport=\"ئەكىر\"\nExport=\"چىقار\"\nCopy=\"كۆچۈر\"\nPaste=\"چاپلا\"\nPasteReference=\"چاپلا (نەقىل)\"\nPasteDuplicate=\"چاپلا (تەكرار)\"\nRemuxRecordings=\"خاتىرىنى قايتا كودلا\"\nNext=\"كېيىنكى\"\nBack=\"قايت\"\nDefaults=\"كۆڭۈلدىكى\"\nRestoreDefaults=\"كۆڭۈلدىكى\"\nHideMixer=\"بىرىكتۈرگۈچتە يوشۇر\"\nTransitionOverride=\"ئالمىشىش قاپلاش\"\nShowTransition=\"ئالمىشىشنى كۆرسەت\"\nHideTransition=\"ئالمىشىشنى يوشۇر\"\nNone=\"يوق\"\nStudioMode.Preview=\"ئالدىن كۆزەت\"\nStudioMode.Program=\"پىروگرامما\"\nStudioMode.PreviewSceneName=\"ئالدىن كۆزەت: %1\"\nStudioMode.ProgramSceneName=\"پىروگرامما: %1\"\nShowInMultiview=\"كۆپ كۆرۈنۈشتە كۆرسەت\"\nVerticalLayout=\"بويىغا ئورۇنلاشتۇر\"\nGroup=\"گۇرۇپپا\"\nDoNotShowAgain=\"قايتا كۆرسەتمە\"\nDefault=\"(كۆڭۈلدىكى)\"\nCalculating=\"ھېسابلاۋاتىدۇ…\"\nFullscreen=\"پۈتۈن ئېكران\"\nWindowed=\"كۆزنەكلەندى\"\nRefreshBrowser=\"يېڭىلا\"\nAspectRatio=\"تەرەپ نىسبىتى <b>%1:%2</b>\"\nLockVolume=\"ئاۋازنى قۇلۇپلاش\"\nLogViewer=\"خاتىرە كۆرگۈچ\"\nShowOnStartup=\"قوزغالغاندا كۆرسەت\"\nOpenFile=\"ھۆججەت ئاچ\"\nAddScene=\"كۆرۈنۈش قوش\"\nAddSource=\"مەنبە قوش\"\nRemoveScene=\"تاللانغان كۆرۈنۈشنى چىقىرىۋەت\"\nRemoveSource=\"تاللانغان مەنبەنى چىقىرىۋەت\"\nMoveSceneUp=\"كۆرۈنۈشنى يۇقىرىغا يۆتكە\"\nMoveSceneDown=\"كۆرۈنۈشنى تۆۋەنگە يۆتكە\"\nMoveSourceUp=\"مەنبەنى يۇقىرىغا يۆتكە\"\nMoveSourceDown=\"مەنبەنى تۆۋەنگە يۆتكە\"\nSourceProperties=\"مەنبە خاسلىقىنى ئاچ\"\nSourceFilters=\"مەنبە سۈزگۈچنى ئاچ\"\nMixerToolbarMenu=\"ئۈن بىرىكتۈرگۈچ تىزىملىكى\"\nSceneFilters=\"كۆرۈنۈش سۈزگۈچنى ئاچ\"\nList=\"تىزىم\"\nGrid=\"سېتكا\"\nAutomatic=\"ئاپتوماتىك\"\nComingSoon=\"پات يېقىندا تارقىتىلىدۇ\"\nPluginsFailedToLoad.Title=\"قىستۇرما يۈكلەش خاتالىقى\"\nPluginsFailedToLoad.Text=\"تۆۋەندىكى OBS قىستۇرمىنى يۈكلىيەلمىدى: \\n\\n%1\\nبۇ قىستۇرمىنى يېڭىلاڭ ياكى چىقىرىۋېتىڭ.\"\nAlreadyRunning.Title=\"OBS ئىجرا قىلىنىۋاتىدۇ!\"\nAlreadyRunning.Text=\"OBS ئىجرا قىلىنىۋاتىدۇ! ھەقىقەتەن شۇنداق قىلماقچى بولسىڭىز، يېڭى OBS نى قوزغىتىشتىن ئىلگىرى ئىجرا قىلىنىۋاتقان باشقا OBS نى تاقاڭ. ئەگەر OBS نى سىستېما ۋەزىپە بالداققا كىچىكلەتكەن بولسىڭىز، ئۇنىڭ ئىجرا قىلىنغان ياكى قىلىنمىغانلىقىنى تەكشۈرۈڭ.\"\nAlreadyRunning.LaunchAnyway=\"قوزغىتىۋەر\"\nCrashHandling.Dialog.Title=\"OBS Studio يىمىرىلىشى بايقالدى\"\nCrashHandling.Labels.Text=\"OBS Studio ئادەتتىكىدەك تاقالمىغان.\\n\\nبىخەتەر ھالەتتە قوزغىتامسىز(ئۈچىنچى تەرەپ قىستۇرما، قوليازما ۋە WebSocket چەكلىنىدۇ)؟\"\nCrashHandling.Labels.PrivacyNotice=\"سىز يەنە يېقىنقى يىمىرىلىش دوكلاتىنى OBSProject غا ئۆزلۈكىدىن يۈكلەشنى تاللىيالايسىز.<br /><br />ھەر قانداق ھۆججەتنى يۈكلەشتىن ئىلگىرى <a href='https://obsproject.com/privacy-policy'>شەخسىيەت تۈزۈمى</a>نى ئوقۇڭ، بولۇپمۇ ھۆججەت يۈكلەش ھەققىدىكى بۆلەككە ئالاھىدە دىققەت قىلىڭ.\"\nCrashHandling.Checkbox.SendReport=\"شەخسىيەت تۈزۈمىنى ئوقۇدۇم ھەم يۈكلەشكە قوشۇلىمەن.\"\nCrashHandling.Buttons.LaunchSafe=\"بىخەتەر ھالەتتە ئىجرا قىل\"\nCrashHandling.Buttons.LaunchNormal=\"ئۆلچەملىك ھالەتتە ئىجرا قىل\"\nCrashHandling.Errors.UploadJSONError=\"ئەڭ يېڭى يىمىرىلىش كۈندىلىك خاتىرە ھۆججىتىنى يۈكلەشنى سىناۋاتقاندا خاتالىق كۆرۈلدى. سەل تۇرۇپ قايتا سىناڭ.\"\nCrashHandling.Errors.Title=\"يىمىرىلىش خاتىرىسىنى يۈكلەش خاتالىقى\"\nSafeMode.Restart=\"OBS نى بىخەتەر ھالەتتە قايتا قوزغىتامسىز (ئۈچىنچى تەرەپ قىستۇرما، قوليازما ۋە WebSockets چەكلىنىدۇ)؟ \"\nSafeMode.RestartNormal=\"OBS نى نورمال ھالەتتە قوزغىتامسىز؟\"\nChromeOS.Title=\"قوللىمايدىغان سۇپا\"\nChromeOS.Text=\"OBS قارىماققا ChromeOS يۈكدانىدا ئىجرا قىلىنىۋاتقاندەك قىلىدۇ. بۇ سۇپىنى قوللىمايدۇ.\"\nWine.Title=\"Wine بايقالدى\"\nWine.Text=\"OBS نى Wine دا ئىجرا قىلىشنى قوللىمايدۇ، ئېكراندىن تۇتۇش ياكى ئۈسكۈنە مەنبەسى قاتارلىق ئىقتىدارلارنى ئىشلەتكىلى بولمايدۇ ياكى چەكلىمىگە ئۇچرايدۇ. <br><br>ئەسلى نەشرىدىكى OBS نى ئىجرا قىلىش تەۋسىيە قىلىنىدۇ، مەسىلەن، <a href='https://flathub.org/apps/details/com.obsproject.Studio'>بىزنىڭ Flatpak نەشرىمىز</a> ياكى مەشغۇلات سىستېمىڭىزغا ماس بوغچا.\"\nDockCloseWarning.Title=\"لەڭگەر كۆزنەكنى تاقاش\"\nDockCloseWarning.Text=\"بايىلا لەڭگەرلەيدىغان كۆزنەكنى تاقىدىڭىز. ئەگەر ئۇنى قايتا كۆرسەتمەكچى بولسىڭىز، تىزىملىك بالداقتىكى لەڭگەر تىزىملىكنى ئىشلىتىڭ.\"\nExtraBrowsers=\"ئىختىيارى توركۆرگۈچ لەڭگىرى\"\nExtraBrowsers.Info=\"ئۇلارغا ئىسىم ۋە تور ئادرېسى(URL) بېرىش ئارقىلىق لەڭگەر قوشۇڭ، ئاندىن «قوللان» ياكى «ياپ» نى چېكىپ، لەڭگەرلەرنى ئېچىڭ. خالىغان ۋاقىتتا لەڭگەر قوشۇپ ياكى ئۆچۈرەلەيسىز.\"\nExtraBrowsers.DockName=\"لەڭگەرئاتى\"\nAuth.Authing.Title=\"دەلىللەۋاتىدۇ...\"\nAuth.Authing.Text=\"%1 دا دەلىللەۋاتىدۇ، سەل كۈتۈڭ…\"\nAuth.AuthFailure.Title=\"دەلىللەشتە خاتالىق كۆرۈلدى\"\nAuth.AuthFailure.Text=\"%1 دا دەلىللىيەلمىدى: \\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"دەلىللەش زۆرۈر\"\nAuth.InvalidScope.Text=\"% 1 نىڭ دەلىللەش تەلىپى ئۆزگەردى. بەزى ئىقتىدارلارنى ئىشلەتكىلى بولماسلىقى مۇمكىن.\"\nAuth.LoadingChannel.Title=\"قانال ئۇچۇرىنى يۈكلەۋاتىدۇ…\"\nAuth.LoadingChannel.Text=\"%1 غا قانال ئۇچۇرىنى يۈكلەۋاتىدۇ، سەل كۈتۈڭ…\"\nAuth.LoadingChannel.Error=\"قانال ئۇچۇرىغا ئېرىشەلمىدى\"\nAuth.ChannelFailure.Title=\"قانالنى يۈكلىيەلمىدى\"\nAuth.ChannelFailure.Text=\"%1 ئۈچۈن قانال ئۇچۇرىنى يۈكلىيەلمىدى\"\nAuth.Chat=\"سۆھبەت\"\nAuth.StreamInfo=\"ئېقىم ئۇچۇرى\"\nTwitchAuth.Stats=\"Twitch ئىستاتىستىكا\"\nTwitchAuth.Feed=\"Twitch پائالىيەت مەنبەسى\"\nTwitchAuth.TwoFactorFail.Title=\"ئېقىم ئاچقۇچنى سۈرۈشتۈرەلمىدى\"\nTwitchAuth.TwoFactorFail.Text=\"OBS سىزنىڭ Twitch ھېساباتىڭىزغا باغلىنالمىدى. <a href='https://www.twitch.tv/settings/security'>Twitch بىخەتەرلىك تەڭشىكى</a>دە ئىككى ئامىللىق دەلىللەشنى قوزغاتقانلىقىڭىزنى جەزملەڭ، پەقەت قوزغىتىلغاندىلا ئاندىن ئېقىم تارقىتالايسىز.\"\nRestreamAuth.Channels=\"قانال ئېقىمىنى قايتا باشلا\"\nCopy.Filters=\"سۈزگۈچ كۆچۈر\"\nPaste.Filters=\"سۈزگۈچ چاپلا\"\nBrowserPanelInit.Title=\"توركۆرگۈنى دەسلەپلەشتۈرۈۋاتىدۇ…\"\nBrowserPanelInit.Text=\"توركۆرگۈنى دەسلەپلەشتۈرۈۋاتىدۇ، سەل كۈتۈڭ…\"\nBandwidthTest.Region=\"رايون\"\nBandwidthTest.Region.US=\"ئامېرىكا قوشما شتاتلىرى\"\nBandwidthTest.Region.EU=\"ياۋروپا\"\nBandwidthTest.Region.Asia=\"ئاسىيا\"\nBandwidthTest.Region.Other=\"باشقا\"\nBasic.AutoConfig=\"ئۆزلۈكىدىن سەپلەش يېتەكچىسى\"\nBasic.AutoConfig.ApplySettings=\"تەڭشەكلەرنى قوللان\"\nBasic.AutoConfig.StartPage=\"ئىشلىتىش ئۇچۇرى\"\nBasic.AutoConfig.StartPage.SubTitle=\"پىروگراممىنى نېمىگە ئىشلەتمەكچى ئىكەنلىكىڭىزنى بەلگىلەڭ\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"ئېقىم تارقىتىش، خاتىرىلەشنى ئەلالاشتۇرۇش ئىككىنچى ئورۇندا\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"پەقەت خاتىرىلەشكىلا ئەلالاشتۇرسۇن، مەن ئېقىم تارقاتمايمەن\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"مەن پەقەت مەۋھۇم كامېرا ئىشلىتىمەن\"\nBasic.AutoConfig.VideoPage=\"سىن تەڭشەكلىرى\"\nBasic.AutoConfig.VideoPage.SubTitle=\"ئىشلەتمەكچى بولغان سىن تەڭشىكىنى بەلگىلەڭ\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"نۆۋەتتىكىنى ئىشلەت (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"%1-ئېكران (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"نۆۋەتتىكىنى ئىشلەت (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 ياكى 30 دىن بىرى ئەمما مۇمكىن بولسا 60 بولغىنى ئەۋزەل\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 ياكى 30 دىن بىرى ئەمما ئېنىقلىقى يۇقىرى‎‏ بولغىنى ئەۋزەل\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"دىققەت: كەرگە (ئاساس) نىڭ ئېنىقلىقى چوقۇم ئېقىم تارقىتىش ياكى سىن خاتىرىلەش ئېنىقلىقى بىلەن ئوخشاش بولۇشى زۆرۈر ئەمەس. ئەمەلىي ئېقىم تارقىتىش ياكى سىن خاتىرىلەش ئېنىقلىق دەرىجىسىنى كەرگە ئېنىقلىقىدىن كىچىكلىتىپ، مەنبە ئىشلىتىش ياكى بىت نىسبىتىنىڭ ئېھتىياجىنى تۆۋەنلىتىدۇ.\"\nBasic.AutoConfig.StreamPage=\"ئېقىم ئۇچۇرى\"\nBasic.AutoConfig.StreamPage.SubTitle=\"ئېقىم ئۇچۇرلىرىڭىزنى كىرگۈزۈڭ\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"ھېساباتقا باغلا(تەۋسىيە)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"ھېساباتنى ئۈز\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"ھېساباتنى ئۈزەمدۇ؟\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"بۇ ئۆزگەرتىش دەرھال قوللىنىلىدۇ. ھېساباتىڭىزنى راستىنلا ئۈزۈۋېتىشنى خالامسىز؟\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"ئېقىم ئاچقۇچىغا ئېرىشىش\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"تېخىمۇ كۆپ ئۇچۇر\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"ئېقىم ئاچقۇچى ئىشلەت\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"ئېقىم ئاچقۇچى ئىشلەت\"\nBasic.AutoConfig.StreamPage.Service=\"مۇلازىمەت\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"ھەممىنى كۆرسەت…\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"ئىختىيارى…\"\nBasic.AutoConfig.StreamPage.Server=\"مۇلازىمېتىر\"\nBasic.AutoConfig.StreamPage.StreamKey=\"ئېقىم ئاچقۇچى\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: شىفىرلىق ئاچقۇچ كىرگۈزۈلىدۇ.\\nRTMPRTMP: مۇلازىمەت سۇپىسى تەمىنلىگەن ئاچقۇچ كىرگۈزۈلىدۇ.\\nSRT: ئەگەر مۇلازىمەت سۇپىسى streamid تەڭشىگەن بولسا كىرگۈزۈلىدۇ.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"كودلىغۇچ ئاچقۇچى\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Bearer پەرمان تاختىسى\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"باغلانغان ھېسابات\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"بەلباغ سىنىقى ئارقىلىق بىت نىسبىتىنى مۆلچەرلەيدۇ (بىر قانچە مىنۇت كېتىشى مۇمكىن)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"قاتتىق دېتالدا كودلاش ئەۋزەل\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"قاتتىق دېتالدا كودلاش CPU ئىشلىتىلىشىنى زور دەرىجىدە تۆۋەنلىتىدۇ ئەمما يۇمشاق دېتال كودلاش بىلەن ئوخشاش بولغان سۈپەتكە ئېرىشىش ئۈچۈن تېخىمۇ يۇقىرى بىت نىسبىتىگە ئېھتىياجلىق بولۇشى مۇمكىن.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"ئېقىم ئاگاھلاندۇرۇشى\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"بەلباغ كەڭلىك سىنىقىدا ئاۋازسىز خالىغان سىن سانلىق مەلۇماتىنى ئېقىمدا تارقىتىدۇ. ئەگەر مۇمكىن بولسا، سىناق ئاخىرلاشقۇچە سىن ساقلاش ئىقتىدارىنى ۋاقىتلىق تاقاپ ئېقىم تارقىتىشنى شەخسىي قىلىپ تەڭشەش تەۋسىيە قىلىنىدۇ. داۋاملاشتۇرامدۇ؟\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"%1 نى سىنايدۇ\"\nBasic.AutoConfig.TestPage=\"ئاخىرقى نەتىجە\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"بۇ پىروگرامما ھازىر كۆڭۈلدىكىدەك تەڭشەكلەرنى مۆلچەرلەش ئۈچۈن بىر يۈرۈش سىناقلارنى ئىجرا قىلىۋاتىدۇ\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"سىناق تامام\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"كەڭ بەلۋاغ سىنىقى ئېلىپ بېرىشقا بىر قانچە مىنۇت ۋاقىت كېتىشى مۇمكىن…\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"مەزكۇر مۇلازىمەتنىڭ كېلىشىم چىقىرىشىنى تاپالمىدى\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"باغلىنىۋاتقىنى: %1…\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"ھېچقانداق مۇلازىمېتىرغا باغلىنالمىدى، ئىنتېرنېت باغلىنىشىڭىزنى تەكشۈرۈپ قايتا سىناڭ.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"بەلباغ كەڭلىكىنى سىناۋاتىدۇ: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"ئېقىم كودلىغۇچنى سىناۋاتىدۇ، بۇنىڭغا بىر قانچە مىنۇت ۋاقىت كېتىشى مۇمكىن…\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"خاتىرىلەش كودلىغۇچنى سىناۋاتىدۇ، بۇنىڭغا بىر قانچە مىنۇت ۋاقىت كېتىشى مۇمكىن…\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"كودلىغۇچنى قوزغىتالمىدى\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"سىناۋاتىدۇ %1x%2 %3 FPS…\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"ئېقىم كودلىغۇچ\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"خاتىرىلەش كودلىغۇچ\"\nBasic.AutoConfig.TestPage.Result.Header=\"مەزكۇر مۆلچەر تەڭشەكنىڭ سىزگە ماس كېلىدىغانلىقىنى پىروگرامما جەزملىدى:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"بۇ تەڭشەكنى ئىشلىتىشتە، «تەڭشەكنى قوللان» چېكىلىدۇ. يېتەكچىنى قايتا سەپلەپ قايتا سىناشتا، قايت چېكىلىدۇ. تەڭشەكنى تەڭشەكنى قولدا سەپلەشتە، ۋاز كەچ چېكىلىپ ئاندىن تەڭشەك ئېچىلىدۇ.\"\nBasic.AutoConfig.Info=\"ئاپتوماتىك سەپلەش يېتەكچىسى كومپيۇتېرىڭىزنىڭ سەپلىمىسى ۋە ئىنتېرنېت سۈرئىتىگە ئاساسەن ئەڭ ياخشى تەڭشەكنى جەزملەيدۇ.\"\nBasic.AutoConfig.RunAnytime=\"قورال تىزىملىكتىن خالىغان ۋاقىتتا ئىجرا قىلغىلى بولىدۇ.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"ئېقىم (تەڭشەلگەن) ئېنىقلىقى\"\nBasic.Stats=\"ستاتىستىكىلار\"\nBasic.Stats.CPUUsage=\"CPU ئىشلىتىلىشى\"\nBasic.Stats.HDDSpaceAvailable=\"ئىشلىتىلىشچان دىسكا بوشلۇقى\"\nBasic.Stats.MemoryUsage=\"ئەسلەك ئىشلىتىلىشى\"\nBasic.Stats.AverageTimeToRender=\"كاندۇكنى ھاسىللاشقا كېتىدىغان ئوتتۇرىچە ۋاقىت\"\nBasic.Stats.SkippedFrames=\"كودلاشنىڭ كېچىكىشى سەۋەبىدىن كاندۇكتىن ئاتلاپ كەتتى\"\nBasic.Stats.MissedFrames=\"ھاسىللاشنىڭ كېچىكىشى سەۋەبىدىن كاندۇك يىتتۇردى\"\nBasic.Stats.Output.Stream=\"سانلىق مەلۇمات ئېقىمى\"\nBasic.Stats.Output.Recording=\"خاتىرىلەۋاتىدۇ\"\nBasic.Stats.Status=\"ھالىتى\"\nBasic.Stats.Status.Recording=\"خاتىرىلەۋاتىدۇ\"\nBasic.Stats.Status.Live=\"نەق مەيدان\"\nBasic.Stats.Status.Reconnecting=\"قايتا باغلىنىۋاتىدۇ\"\nBasic.Stats.Status.Inactive=\"ئاكتىپسىز\"\nBasic.Stats.Status.Active=\"ئاكتىپ\"\nBasic.Stats.DroppedFrames=\"تاشلىۋېتىلگەن كاندۇك (تور)\"\nBasic.Stats.MegabytesSent=\"چىقارغان ئومۇمىي سانلىق مەلۇمات\"\nBasic.Stats.Bitrate=\"بايت نىسبىتى\"\nBasic.Stats.DiskFullIn=\"دىسكا توشىدۇ(تەخمىنەن)\"\nBasic.Stats.DiskFullIn.Text=\"%1 سائەت، %2 مىنۇت\"\nBasic.Stats.ResetStats=\"سىتاتىستىكىنى ئەسىگە قايتۇر\"\nResetUIWarning.Title=\"راستىنلا ئارايۈزنى ئەسلىگە قايتۇرامسىز؟\"\nResetUIWarning.Text=\"ئارايۈز قايتا تەڭشەلسە قوشۇمچە لەڭگەرلەر يوشۇرۇلىدۇ. ئەگەر بۇ لەڭگەرلەرنى قايتا كۆرسەتمەكچى بولسىڭىز، لەڭگەر تىزىملىكىدىن يوشۇرۇشنى بىكار قىلسىڭىز بولىدۇ.\\n\\nراستتىنلا ئارايۈزنى ئەسلىگە قايتۇرامسىز؟\"\nUpdater.Title=\"يېڭى نەشرى بار\"\nUpdater.Text=\"يېڭى بىر نەشرى بار:\"\nUpdater.UpdateNow=\"ھازىرلا يېڭىلاش\"\nUpdater.RemindMeLater=\"كېيىنچە ئەسكەرت\"\nUpdater.Skip=\"نەشرىدىن ئاتلا\"\nUpdater.NoUpdatesAvailable.Title=\"يېڭىلانما يوق\"\nUpdater.NoUpdatesAvailable.Text=\"ھازىرچە ھېچقانداق يېڭىلاش يوق\"\nUpdater.BranchNotFound.Title=\"يېڭىلانغان قانال چىقىرىۋېتىلدى\"\nUpdater.BranchNotFound.Text=\"سىز تاللىغان يېڭىلاش قانىلىنى ئەمدى ئىشلەتكىلى بولمايدۇ، OBS كۆڭۈلدىكىگە ئەسلىگە قايتۇرۇلدى.\"\nUpdater.RepairButUpdatesAvailable.Title=\"مۇكەممەللىك تەكشۈرۈشىنى ئىشلەتكىلى بولمايدۇ\"\nUpdater.RepairButUpdatesAvailable.Text=\"ھۆججەتنىڭ مۇكەممەللىكىنى تەكشۈرۈشنى ئاخىرقى نەشرىدىلا ئىشلەتكىلى بولىدۇ. ياردەم ← يېڭىلاشنى تەكشۈرنى چېكىپ دەلىللەپ، OBS نى يېڭىلاڭ.\"\nUpdater.RepairConfirm.Title=\"مۇكەممەللىك تەكشۈرۈشنى جەزملە\"\nUpdater.RepairConfirm.Text=\"مۇكەممەللىك تەكشۈرۈشنى جەزملەش سىز ئورناتقان OBS نى تاراپ، بۇزۇلغان، ئۆزگەرگەن ھۆججەتنى تېپىپ قايتا چۈشۈرىدۇ. بۇ مەشغۇلاتقا ئازراق ۋاقىت كېتىشى مۇمكىن.\\n\\nراستتىنلا داۋاملاشتۇرامسىز؟\"\nUpdater.FailedToLaunch=\"يېڭىلىغۇچنى قوزغىتالمىدى\"\nQuickTransitions.SwapScenes=\"سەھنە ئالماشتۇرۇش جانلاندۇرۇمىدىن كېيىن ئالدىن كۆزىتىش/چىقىرىش كۆرۈنۈشىنى ئالماشتۇرىدۇ\"\nQuickTransitions.SwapScenesTT=\"سەھنە ئالماشتۇرۇش جانلاندۇرۇمىدىن كېيىن، ئالدىن كۆزىتىش كۆرۈنۈشىنى چىقىرىش كۆرۈنۈشىگە ئالماشتۇرىدۇ، چىقىرىش كۆرۈنۈشى (ئەگەر ئەسلى كۆرۈنۈش يەنىلا مەۋجۇت بولسا) نى ئالدىن كۆزىتىش كۆرۈنۈشىگە ئالماشتۇرىدۇ.\\nبۇ ئىقتىدارنى ئەسلى چىقىرىش كۆرۈنۈشىنى ئۆزگەرتىشكە ئىشلەتكىلى بولمايدۇ.\"\nQuickTransitions.DuplicateScene=\"تەكرار كۆرۈنۈش\"\nQuickTransitions.DuplicateSceneTT=\"نۆۋەتتىكى چىقىرىش كۆرۈنۈشى بىلەن ئوخشاش كۆرۈنۈشنى تەھرىرلىگەندە، مەنبەنىڭ ئۆزگىرىش ۋە كۆرۈنۈشچانلىقى ئۆزگەرتىلسە چىقىرىشقا تەسىر كۆرسەتمەيدۇ.\\nئەگەر مەنبەنىڭ خاسلىقىنى ئۆزگەرتىپ چىقىرىشقا تەسىر كۆرسەتمىگەندە، «مەنبەنى كۆپەيت» ئىقتىدارىنى ئىشلىتىش كېرەك.\\nبۇ قىممەت ئۆزگەرسە نۆۋەتتىكى چىقىرىش كۆرۈنۈشى (ئەگەر يەنىلا مەۋجۇت بولسا) نى ئەسلىگە قايتۇرىدۇ.\"\nQuickTransitions.EditProperties=\"مەنبەنى كۆپەيت\"\nQuickTransitions.EditPropertiesTT=\"نۆۋەتتىكى چىقىرىش كۆرۈنۈشى بىلەن ئوخشاش كۆرۈنۈشنى تەھرىرلىگەندە، مەنبەنىڭ خاسلىقى ئۆزگەرتىلسە چىقىرىشقا تەسىر كۆرسەتمەيدۇ.\\nبۇ ئىقتىدارنى «مەنبەنى كۆپەيت» ئىقتىدارىنى قوزغاتقاندىلا ئاندىن ئىشلەتكىلى بولىدۇ.\\nبەزى مەنبە (مەسىلەن سىن تۇتۇش ياكى ۋاسىتە مەنبەسى) بۇ ئىقتىدارنى قوللىمايدۇ ئۇنىڭ ئۈستىگە ئايرىم ئۆزگەرتكىلى بولمايدۇ.\\nبۇ قىممەت ئۆزگەرتىلسە نۆۋەتتىكى چىقىرىش كۆرۈنۈشى (ئەگەر يەنىلا مەۋجۇت بولسا) نى ئەسلىگە قايتۇرىدۇ.\\n\\nئاگاھلاندۇرۇش: مەنبە كۆپەيتىلگەنلىكتىن، نورمىدىن ئارتۇق سىستېما ياكى سىن مەنبەسىگە ئېھتىياجلىق بولۇشى مۇمكىن.\"\nQuickTransitions.HotkeyName=\"تېز ئالمىشىش: %1\"\nBasic.AddTransition=\"سەپلەشچان ئالمىشىش قوش\"\nBasic.RemoveTransition=\"سەپلەشچان ئالمىشىشنى چىقىرىۋەت\"\nBasic.TransitionProperties=\"ئالمىشىش خاسلىقى\"\nBasic.SceneTransitions=\"كۆرۈنۈش ئالمىشىش\"\nBasic.TransitionDuration=\"داۋاملىشىش ۋاقتى\"\nBasic.TogglePreviewProgramMode=\"خىزمەتخانا ھالىتى\"\nBasic.EnablePreviewProgramMode=\"خىزمەتخانا ھالىتىنى قوزغات\"\nBasic.DisablePreviewProgramMode=\"خىزمەتخانا ھالىتىنى چەكلە\"\nUndo.Undo=\"يېنىۋال\"\nUndo.Redo=\"قايتىلا\"\nUndo.Add=\"«%1» نى قوش\"\nUndo.Delete=\"«%1» نى ئۆچۈر\"\nUndo.Rename=\"«%1» نىڭ ئاتىنى ئۆزگەرت\"\nUndo.SceneCollection.Switch=\"«%1» غا ئالماشتۇر\"\nUndo.Item.Undo=\"«%1» دىن يېنىۋال\"\nUndo.Item.Redo=\"«%1» نى قايتىلا\"\nUndo.Sources.Multi=\"%1 مەنبەنى ئۆچۈر\"\nUndo.Filters=\"سۈزگۈچ «%1» ئۆزگىرىدۇ\"\nUndo.Filters.Paste.Single=\"سۈزگۈچ «%1» نى «%2» غا چاپلا\"\nUndo.Filters.Paste.Multiple=\"سۈزگۈچ  «%1» نى «%2» غا كۆچۈر\"\nUndo.Transform=\"«%1» دىكى ئالمىشىش مەنبەسى\"\nUndo.Transform.Paste=\"«%1» دىكى ئالمىشىشنى چاپلا\"\nUndo.Transform.Rotate=\"«%1» ئايلاندۇر\"\nUndo.Transform.Reset=\"«%1» ئالماشتۇرۇشنى ئەسلىگە قايتۇر\"\nUndo.Transform.HFlip=\"«%1» توغرىسىغا ئۆرۈ\"\nUndo.Transform.VFlip=\"«%1» بويىغا ئۆرۈ\"\nUndo.Transform.FitToScreen=\"«%1» ئېكرانغا تولدۇر\"\nUndo.Transform.StretchToScreen=\"«%1» ئېكرانغا ياي\"\nUndo.Transform.Center=\"«%1» ئېكران ئوتتۇرىسىغا توغرىلا\"\nUndo.Transform.VCenter=\"«%1» بويىغا ئېكران ئوتتۇرىسىغا توغرىلا\"\nUndo.Transform.HCenter=\"«%1» توغرىسىغا ئېكران ئوتتۇرىسىغا توغرىلا\"\nUndo.Volume.Change=\"«%1» دىكى ئاۋاز ئۆزگىرىشى\"\nUndo.Volume.Mute=\"«%1» ئۈنسىز\"\nUndo.Volume.Unmute=\"«%1» ئۈنلۈك\"\nUndo.Balance.Change=\"ئاۋاز تەڭپۇڭلۇقى «%1» دا ئۆزگەرت\"\nUndo.SyncOffset.Change=\"ئاۋاز قەدەمداش يۆتكىلىشى «%1» ئۆزگەرت\"\nUndo.MonitoringType.Change=\"ئاۋاز نازارەتچىلىكى «%1» دا ئۆزگەرت\"\nUndo.Mixers.Change=\"ئاۋاز ئارىلاشتۇرغۇچ «%1» دا ئۆزگەرت\"\nUndo.ForceMono.On=\"«%1» مەجبۇرىي تەقلىد ئاۋازنى قوزغات\"\nUndo.ForceMono.Off=\"«%1» مەجبۇرىي تەقلىد ئاۋازنى چەكلە\"\nUndo.Properties=\"«%1» خاسلىق ئۆزگەرت\"\nUndo.Scene.Duplicate=\"«%1» كۆرۈنۈش تەكرارلا\"\nUndo.ShowTransition=\"«%1» ئالمىشىشنى كۆرسەت\"\nUndo.HideTransition=\"«%1» ئالمىشىشنى يوشۇر\"\nUndo.ShowSceneItem=\"«%2» دىكى «%1» كۆرسەت\"\nUndo.HideSceneItem=\"«%2» دىكى «%1»  يوشۇر\"\nUndo.ReorderSources=\"«%1» مەنبەنى رەتلە\"\nUndo.MoveUp=\"«%2» دە «%1» نى ئۈستىگە يۆتكە\"\nUndo.MoveDown=\"«%2» دە «%1» نى ئاستىغا يۆتكە\"\nUndo.MoveToTop=\"«%2» دە «%1» نى چوققىغا يۆتكە\"\nUndo.MoveToBottom=\"«%2» دە «%1» نى ئەڭ ئاستىغا يۆتكە\"\nUndo.PasteSource=\"«%1» دە مەنبەنى چاپلا\"\nUndo.PasteSourceRef=\"«%1» دە مەنبە نەقىلىنى چاپلا\"\nUndo.GroupItems=\"«%1» گۇرۇپپا تۈرلىرى\"\nTransitionNameDlg.Text=\"ئالمىشىش ئىسمىنى كىرگۈزۈڭ\"\nTransitionNameDlg.Title=\"ئالمىشىش ئىسمى\"\nTitleBar.SafeMode=\"بىخەتەر ھالىتى\"\nTitleBar.PortableMode=\"ئەپچىل ھالىتى\"\nTitleBar.Profile=\"تەرجىمىھال\"\nTitleBar.Scenes=\"كۆرۈنۈش\"\nNameExists.Title=\"ئىسىم مەۋجۇت\"\nNameExists.Text=\"بۇ ئات ئىشلىتىلىۋاتىدۇ.\"\nNoNameEntered.Title=\"ئىناۋەتلىك ئىسىم كىرگۈزۈڭ\"\nNoNameEntered.Text=\"ئىسىم بوش قالسا بولمايدۇ.\"\nConfirmStart.Title=\"ئېقىمنى باشلامدۇ؟\"\nConfirmStart.Text=\"ئېقىمنى راستىنلا باشلامسىز؟\"\nConfirmStop.Title=\"ئېقىمنى توختىتامدۇ؟\"\nConfirmStop.Text=\"ئېقىمنى راستىنلا توختىتامسىز؟\"\nConfirmStopRecord.Title=\"خاتىرىلەشنى توختىتامدۇ؟\"\nConfirmStopRecord.Text=\"خاتىرىلەشنى راستىنلا توختىتامسىز؟\"\nConfirmBWTest.Title=\"بەلباغ  كەڭلىكى سىنىقىنى باشلامدۇ؟\"\nConfirmBWTest.Text=\"بەلباغ كەڭلىكىنى سىناش ھالىتىدە OBS نى سەپلىدىڭىز. بۇ ھالەتتە قانالنى ئاچمىغان ئەھۋالدا تور سىنىقى ئېلىپ بېرىشقا يول قويىدۇ. سىناق تاماملانغاندىن كېيىن، كۆرۈرمەنلەرنىڭ سىن ئېقىمىڭىزنى كۆرەلىشى ئۈچۈن ئۇنى چەكلىشىڭىز كېرەك.\\n\\nراستتىنلا داۋاملاشتۇرامسىز؟\"\nConfirmExit.Title=\"چىقىرىۋاتقىنى\"\nConfirmExit.Text=\"OBS يەنىلا ئاكتىپ ھالەتتە. بارلىق ئېقىم/خاتىرىلەر تاقىلىدۇ.\"\nConfirmRemove.Title=\"چىقىرىۋېتىشنى جەزملە\"\nConfirmRemove.Text=\"«%1» نى راستتىنلا چىقىرىۋېتەمسىز؟\"\nConfirmRemove.TextMultiple=\"%1 تۈرنى چىقىرىۋېتىشنى خالامسىز؟\"\nConfirmReset.Title=\"خاسلىقنى ئەسلىگە قايتۇر\"\nConfirmReset.Text=\"نۆۋەتتىكى خاسلىقنى راستتىنلا كۆڭۈلدىكى قىممىتىگە ئەسلىگە قايتۇرامسىز؟\"\nOutput.StartStreamFailed=\"ئېقىمنى باشلىيالمىدى\"\nOutput.StartRecordingFailed=\"خاتىرىلەشنى باشلىيالمىدى\"\nOutput.StartReplayFailed=\"غەملەكنى قايتا قوزغىتالمىدى\"\nOutput.StartVirtualCamFailed=\"مەۋھۇم كامېرانى باشلىيالمىدى\"\nOutput.StartFailedGeneric=\"چىقىرىشنى قوزغىتالمىدى. تەپسىلاتىنى خاتىرە ھۆججەتتىن تەكشۈرۈڭ.\\n\\nدىققەت: ئەگەر ئىشلىتىۋاتقىنىڭىز NVENC ياكى AMD نىڭ كودلىغۇچىسى بولسا، كۆرسەتكۈچ قوزغاتقۇچىنىڭ ئەڭ يېڭىسى ئىكەنلىكىگە كاپالەتلىك قىلىڭ.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"ۋاقىتلىق توختىتىلغاندا قايتا ساقلىغىلى بولمايدۇ\"\nOutput.ReplayBuffer.PauseWarning.Text=\"ئاگاھلاندۇرۇش: خاتىرىلەش ۋاقىتلىق توختىتىلغاندا قايتا ساقلىغىلى بولمايدۇ.\"\nOutput.ConnectFail.Title=\"باغلىنالمىدى\"\nOutput.ConnectFail.BadPath=\"يول ياكى باغلىنىش تور ئادرېسى ئىناۋەتسىز. تەڭشىكىڭىزدىن ئۇلارنىڭ ئىناۋەتلىك ئىكەنلىكىنى تەكشۈرۈپ جەزملەڭ.\"\nOutput.ConnectFail.ConnectFailed=\"مۇلازىمېتىرغا ئۇلىنالمىدى\"\nOutput.ConnectFail.InvalidStream=\"بەلگىلەنگەن قانال ياكى ئېقىم ئاچقۇچىنى زىيارەت قىلالمىدى، ئېقىم ئاچقۇچىنى تەپسىلىي تەكشۈرۈڭ. ئەگەر ئۇ توغرا بولسا، مۇلازىمېتىرغا باغلىنىش مەسىلىسى بولۇشى مۇمكىن.\"\nOutput.ConnectFail.HdrDisabled=\"نۆۋەتتىكى چىقىرىشتا HDR چىقىرىش چەكلەنگەن.\"\nOutput.ConnectFail.Error=\"مۇلازىمېتىرغا باغلىنىۋاتقاندا كۈتۈلمىگەن خاتالىق كۆرۈلدى. تەپسىلىي ئۇچۇر خاتىرە ھۆججىتىدە.\"\nOutput.ConnectFail.Disconnected=\"مۇلازىمېتىردىن ئۈزۈلدى.\"\nOutput.StreamEncodeError.Title=\"كودلاش خاتالىقى\"\nOutput.StreamEncodeError.Msg=\"ئېقىم تارقىتىۋاتقاندا كودلاش خاتالىقى كۆرۈلدى.\"\nOutput.StreamEncodeError.Msg.LastError=\"ئېقىم تارقىتىۋاتقاندا كودلاش خاتالىقى كۆرۈلدى:<br><br>%1\"\nOutput.RecordFail.Title=\"خاتىرىلەشنى باشلىيالمىدى\"\nOutput.RecordFail.Unsupported=\"چىقىرىش پىچىمىنى قوللىمايدۇ ياكى بىردىن ئارتۇق ئاۋاز يولىنى قوللىمايدۇ. تەڭشىكىڭىزنى تەكشۈرۈپ ئاندىن قايتا سىناڭ.\"\nOutput.RecordNoSpace.Title=\"دىسكا بوشلۇقى يېتەرلىك ئەمەس\"\nOutput.RecordNoSpace.Msg=\"داۋاملىق خاتىرىلەشكە يېتەرلىك دىسكا بوشلۇقى يوق.\"\nOutput.RecordError.Title=\"خاتىرىلەش خاتالىقى\"\nOutput.RecordError.Msg=\"خاتىرىلەۋاتقاندا نامەلۇم بىر خاتالىق كۆرۈلدى.\"\nOutput.RecordError.EncodeErrorMsg=\"خاتىرىلەۋاتقاندا كودلىغۇچ خاتالىقى كۆرۈلدى.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"خاتىرىلەۋاتقاندا كودلىغۇچ خاتالىقى كۆرۈلدى:<br><br>%1\"\nOutput.BadPath.Title=\"ھۆججەت يولى خاتا\"\nOutput.BadPath.Text=\"سەپلىگەن خاتىرىلەش يولىنى ئاچالمىدى. تەڭشەكلەر ← چىقىرىش ← خاتىرىلەشنىڭ ئاستىدىكى خاتىرە ساقلاش يولىنى تەكشۈرۈڭ.\"\nOutput.NoBroadcast.Title=\"ھېچقانداق ئاڭلىتىش سەپلەنمىگەن\"\nOutput.NoBroadcast.Text=\"ئېقىم تارقىتىشنى باشلاشتىن بۇرۇن تارقىتىش تەڭشىكىنى تەڭشىشىڭىز كېرەك.\"\nOutput.BroadcastStartFailed=\"تارقىتىشنى باشلىيالمىدى\"\nOutput.BroadcastStopFailed=\"تارقىتىشنى توختىتالمىدى\"\nLogUploadDialog.Title=\"OBS Studio خاتىرە ھۆججىتىنى يۈكلەيدۇ\"\nLogUploadDialog.Labels.PrivacyNotice=\"ھەر قانداق ھۆججەتنى يۈكلەشتىن ئىلگىرى <a href='https://obsproject.com/privacy-policy'>شەخسىيەت تۈزۈمى</a> ۋە ئۇنىڭ ھۆججەت يۈكلەش ھەققىدىكى بۆلىكىنى تەپسىلىي ئوقۇڭ.\"\nLogUploadDialog.Labels.Progress=\"خاتىرە يۈكلەشنى ئىجرا قىلىۋاتىدۇ. سەل كۈتۈڭ…\"\nLogUploadDialog.Labels.Description.AppLog=\"خاتىرە ھۆججىتىڭىز يۈكلەنگەن. سازلاش ياكى قوللاش مەقسىتىدە تور ئادرېسىنى ھەمبەھىرلىيەلەيسىز.\"\nLogUploadDialog.Labels.Description.CrashLog=\"يىمىرىلىش دوكلاتىڭىز يۈكلەنگەن. سازلاش ياكى قوللاش مەقسىتىدە تور ئادرېسىنى ھەمبەھىرلىيەلەيسىز.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"يۈكلە\"\nLogUploadDialog.Buttons.CopyURL=\"خاتىرە تور ئادرېسىنى كۆچۈر\"\nLogUploadDialog.Buttons.AnalyzeURL=\"خاتىرە ھۆججەت تەھلىلى\"\nLogUploadDialog.Buttons.RetryButton=\"قايتا سىنا\"\nLogUploadDialog.Errors.Template=\"ھۆججەت يۈكلەشنى سىناۋاتقاندا خاتالىق كۆرۈلدى:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"يۈكلەيدىغان ھېچقانداق ھۆججەت تېپىلمىدى ياكى ھۆججەت بوش.\"\nRemux.SourceFile=\"OBS خاتىرىلەۋاتىدۇ\"\nRemux.TargetFile=\"نىشان ھۆججەت\"\nRemux.Remux=\"قايتا كودلاش\"\nRemux.Stop=\"قايتا كودلاشنى توختات\"\nRemux.ClearFinished=\"تاماملانغان تۈرلەرنى تازىلا\"\nRemux.ClearAll=\"ھەممە تۈرنى تازىلا\"\nRemux.OBSRecording=\"OBS خاتىرىلەۋاتىدۇ\"\nRemux.FinishedTitle=\"قايتا كودلاش تامام\"\nRemux.Finished=\"خاتىرىلەش قايتا كودلاندى\"\nRemux.FinishedError=\"خاتىرىلەش قايتا كودلاندى ئەمما ھۆججەت كەمتۈك بولۇشى مۇمكىن\"\nRemux.SelectRecording=\"OBS خاتىرىلەشنى تاللاۋاتىدۇ…\"\nRemux.SelectTarget=\"نىشان ھۆججەت تاللاۋاتىدۇ…\"\nRemux.FileExistsTitle=\"نىشان ھۆججەت مەۋجۇت\"\nRemux.FileExists=\"تۆۋەندىكى نىشان ھۆججەتلەر مەۋجۇت. ئۇلارنى ئالماشتۇرامسىز؟\"\nRemux.ExitUnfinishedTitle=\"قايتا كودلىنىۋاتىدۇ\"\nRemux.ExitUnfinished=\"قايتا كودلاش تاماملانمىدى، ھازىر توختىتىش نىشان ھۆججەتنى ئىشلەتكىلى بولمايدىغان قىلىپ قويۇشى مۇمكىن.\\nقايتا كودلاشنى راستىنلا توختىتامسىز؟\"\nRemux.HelpText=\"ھۆججەت بۇ كۆزنەككە تاشلانسا قايتا كودلايدۇ ياكى بوش «OBS خاتىرىلەش».\"\nRemux.NoFilesAddedTitle=\"ھېچقانداق قايتا كودلانغان ھۆججەت قوشۇلمىدى\"\nRemux.NoFilesAdded=\"قايتا كودلاشقا ھېچقانداق ھۆججەت قوشۇلمىدى. بىر ياكى بىر قانچە سىن ھۆججىتى بار قىسقۇچ سۆرەپ تاشلىنىدۇ.\"\nMissingFiles=\"ھۆججەت كەم\"\nMissingFiles.MissingFile=\"ھۆججەت كەم\"\nMissingFiles.NewFile=\"يېڭى ھۆججەت\"\nMissingFiles.HelpText=\"سىز OBS نى ئاخىرقى قېتىم ئىشلەتكەندىن بۇيان بەزى ھۆججەتلەر يوقاپ كەتتى.\"\nMissingFiles.Clear=\"‹تازىلاندى›\"\nMissingFiles.NumFound=\" %1/%2 تېپىلدى\"\nMissingFiles.Search=\"مۇندەرىجە ئىزدە…\"\nMissingFiles.SelectFile=\"ھۆججەت تاللا…\"\nMissingFiles.SelectDir=\"ئىزدەيدىغان قىسقۇچ تاللىنىدۇ\"\nMissingFiles.State=\"ھالەت\"\nMissingFiles.Missing=\"كەم\"\nMissingFiles.Replaced=\"ئالماشتۇرۇلدى\"\nMissingFiles.Cleared=\"تازىلاندى\"\nMissingFiles.Found=\"تېپىلدى\"\nMissingFiles.AutoSearch=\"قوشۇمچە ھۆججەت ماسلىقى تېپىلدى\"\nMissingFiles.AutoSearchText=\"OBS بۇ مۇندەرىجىدە يوقاپ كەتكەن ھۆججەتلەرگە قوشۇمچە ماس كېلىدىغان نەرسىلەرنى تاپتى. ئۇلارنى قوشامسىز؟\"\nMissingFiles.NoMissing.Title=\"يوقاپ كەتكەن ھۆججەتلەرنى تەكشۈرۈش\"\nMissingFiles.NoMissing.Text=\"يوقالغان ھۆججەت كۆرۈنمىدى.\"\nMacPermissions.MenuAction=\"ئەپ ئىجازىتىنى تەكشۈرۈۋاتىدۇ…\"\nMacPermissions.Title=\"ئەپ ئىجازىتىنى تەكشۈرۈۋاتىدۇ\"\nMacPermissions.Description=\"OBS Studio سىزنىڭ ئىجازىتىڭىز بولغاندىلا ئاندىن بەزى ئىقتىدارلارنى تەمىنلىيەلەيدۇ. بۇ ئىجازەتلەرنى قوزغىتىش تەۋسىيە قىلىنىدۇ ئەمما قوزغىتىلمىسا ئادەتتىكىدەك ئىشلىتىشكە تەسىر كۆرسەتمەيدۇ. كېيىن خالىغان ۋاقىتتا ئۇلارنى قوزغىتالايسىز.\"\nMacPermissions.Description.OpenDialog=\"بۇ كۆزنەكنى OBS Studio تىزىملىكىدىن  قايتا ئاچالايسىز.\"\nMacPermissions.AccessGranted=\"زىيارەتكە يول قويۇلدى\"\nMacPermissions.RequestAccess=\"زىيارەت ئىلتىماسى\"\nMacPermissions.OpenPreferences=\"%1 مايىللىقىنى ئاچ\"\nMacPermissions.Item.ScreenRecording=\"ئېكران خاتىرىلەش\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS بۇ ئىجازەتكە ئېرىشسە ئاندىن ئېكراندىن سۈرەت تۇتالايدۇ.\"\nMacPermissions.Item.Camera=\"كامېرا\"\nMacPermissions.Item.Camera.Details=\"بۇ ئىجازەت بولغاندا ئاندىن تور كامېراسى ياكى سۈرەت تۇتۇش كارتىسىدىن مەزمۇن تۇتالايدۇ.\"\nMacPermissions.Item.Microphone=\"مىكروفون\"\nMacPermissions.Item.Microphone.Details=\"ئەگەر مىكروفون ياكى سىرتقى ئاۋاز ئۈسكۈنىسىدىن ئاۋاز ئالماقچى بولسىڭىز OBS نىڭ بۇ ئىجازىتى بولۇشى كېرەك.\"\nMacPermissions.Item.InputMonitoring=\"كىرگۈزۈش نازارەتچىلىكى\"\nMacPermissions.Item.InputMonitoring.Details=\"OBS ئارقا سۇپىدا ئىجرا قىلىنىۋاتقاندا، تېزلەتمە مەزكۇر ئىجازەتكە ئىگە بولغاندىلا ئاندىن ئادەتتىكىدەك ئىشلەيدۇ.\"\nMacPermissions.Item.Accessibility=\"قوشۇمچە ئىقتىدار\"\nMacPermissions.Item.Accessibility.Details=\"كونا نەشرىدىكى ئورنىتىشتا، OBS «قولايلىق» تا تىزىلىپ، «كىرگۈزۈش نازارەتچىلىكى» دە تىزىلمايدۇ\"\nMacPermissions.Continue=\"داۋاملاشتۇر\"\nSourceLeak.Title=\"مەنبە تازىلاش خاتالىقى\"\nSourceLeak.Text=\"كۆرۈنۈش توپلىمىنى ئۆزگەرتىۋاتقاندا خاتالىق كۆرۈلدى، بەزى مەنبەلەرنى يۈكتىن چۈشۈرەلمىدى. بۇنداق ئەھۋال ئادەتتە قىستۇرما مەنبەنى ئادەتتىكىدەك قويۇپ بەرمىگەندە كېلىپ چىقىدۇ. ئىشلىتىۋاتقان بارلىق قىستۇرمىلارنىڭ ئەڭ يېڭى ئەشرى ئىكەنلىكىنى جەزملەڭ.\\n\\nسانلىق مەلۇماتنىڭ يوقۇلۇشىنى كەلتۈرۈپ چىقىرىدىغان ھەر قانداق يوشۇرۇن ئەھۋالنىڭ ئالدىنى ئېلىش ئۈچۈن OBS Studio چېكىنىدۇ.\"\nBasic.DesktopDevice1=\"ئۈستەلئۈستى ئاۋاز\"\nBasic.DesktopDevice2=\"ئۈستەلئۈستى ئاۋازلىق 2\"\nBasic.AuxDevice1=\"مىكروفون/كىرگەن ئاۋاز\"\nBasic.AuxDevice2=\"مىكروفون/كىرگەن ئاۋاز 2\"\nBasic.AuxDevice3=\"مىكروفون/كىرگەن ئاۋاز 3\"\nBasic.AuxDevice4=\"مىكروفون/كىرگەن ئاۋاز 4\"\nBasic.Scene=\"كۆرۈنۈش\"\nBasic.DisplayCapture=\"تۇرۇشنى كۆرسەت\"\nBasic.Main.PreviewConextMenu.Enable=\"ئالدىن كۆزىتىشنى قوزغات\"\nBasic.Main.Preview.Disable=\"ئالدىن كۆزىتىشنى چەكلە\"\nScaleFiltering=\"چوڭلۇق سۈزگۈچى\"\nScaleFiltering.Point=\"نۇقتا\"\nScaleFiltering.Bilinear=\"قوش سىزىقلىق\"\nScaleFiltering.Bicubic=\"قوش كۇب\"\nScaleFiltering.Area=\"دائىرە\"\nBlendingMethod=\"ئارىلاشتۇرۇش ئۇسۇلى\"\nBlendingMethod.Default=\"كۆڭۈلدىكى\"\nBlendingMethod.SrgbOff=\"SRGB تاقاق\"\nBlendingMode=\"ئارىلاشتۇرۇش ھالىتى\"\nBlendingMode.Normal=\"نورمال\"\nBlendingMode.Additive=\"قوش\"\nBlendingMode.Subtract=\"ئازايت\"\nBlendingMode.Screen=\"ئېكران\"\nBlendingMode.Multiply=\"رەڭ قوش\"\nBlendingMode.Lighten=\"يورۇقراق\"\nBlendingMode.Darken=\"قاراڭغۇراق\"\nDeinterlacing=\"قۇرمۇ-قۇر تاراش\"\nDeinterlacing.Discard=\"تاشلىۋەت\"\nDeinterlacing.TopFieldFirst=\"ئالدى بىلەن چوققا بۆلەك\"\nDeinterlacing.BottomFieldFirst=\"ئالدى بىلەن ئاستى بۆلەك\"\nVolControl.SliderUnmuted=\"«%1» نىڭ ئاۋاز سۈرگۈچى:\"\nVolControl.SliderMuted=\"«%1» نىڭ ئاۋاز سۈرگۈچى: (ھازىر ئۈنسىز)\"\nVolControl.Mute=\"«%1» ئۈنسىز\"\nVolControl.Properties=\"«%1» نىڭ خاسلىقى\"\nVolControl.UnassignedWarning.Title=\"تەقسىملەنمىگەن ئاۋاز مەنبەسى\"\nVolControl.UnassignedWarning.Text=\"«%1» ھېچقانداق ئۈن يولىغا تەقسىملەنمىگەن، ئېقىم تارقىتىش ياكى خاتىرىلەشتە ئاڭلىغىلى بولمايدۇ.\\n\\nئۈن مەنبەسىنى يولغا تەقسىملەشتە، ئوڭ چېكىش تىزىملىكى ياكى ئارىلاشتۇرغۇچ لەڭگەر بالداقتىكى چىشلىق چاق توپچە ئارقىلىق«ئالىي ئۈن خاسلىقى» نى ئېچىڭ.\"\nBasic.Main.AddSceneDlg.Title=\"كۆرۈنۈش قوش\"\nBasic.Main.AddSceneDlg.Text=\"كۆرۈنۈش ئىسمىنى كىرگۈزۈڭ\"\nBasic.Main.DefaultSceneName.Text=\"%1-كۆرۈنۈش\"\nBasic.Main.AddSceneCollection.Title=\"كۆرۈنۈش توپلىمى قوش\"\nBasic.Main.AddSceneCollection.Text=\"كۆرۈنۈش توپلىمىنىڭ ئىسمىنى كىرگۈزۈڭ\"\nBasic.Main.RenameSceneCollection.Title=\"كۆرۈنۈش توپلىمىنىڭ ئاتىنى ئۆزگەرت\"\nBasic.Main.RemigrateSceneCollection.Title=\"كۆرۈنۈش توپلىمى ئېنىقلىقىنى يېڭىلايدۇ\"\nBasic.Main.RemigrateSceneCollection.Text=\"«%1» نىڭ كۆرۈنۈش توپلىمى ئېنىقلىقىنى يېڭىلاپ نۆۋەتتىكى تەرجىمىھالنىڭ كەرگە ئېنىقلىقى %2x%3 غا ماسلاشتۇرامسىز؟\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"چىقىرىش ئاكتىپ بولسا كۆرۈنۈش توپلىمى ئېنىقلىقىنى يېڭىلىيالمايدۇ.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"كۆرۈنۈش توپلىمى ئېنىقلىقىنى يېڭىلىيالمىدى. ئەسلى ئېنىقلىق نامەلۇم.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"ئەسلىگە قايتۇرۇش مۇمكىن بولمىدى: OBS ئېنىقلىقىنى ئۆزگەرتەلمىدى.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"ئەسلىگە قايتۇرۇش مۇمكىن بولمىدى: نۆۋەتتىكى ئېنىقلىق كۆرۈنۈش توپلىمىنىڭ ئاساسى ئېنىقلىقى.\"\nAddProfile.Title=\"سەپلىمە ھۆججەت قوش\"\nAddProfile.Text=\"سەپلىمە ھۆججەتنىڭ ئىسمىنى كىرگۈزۈڭ\"\nAddProfile.WizardCheckbox=\"ئۆزلۈكىدىن سەپلەش يېتەكچىسىنى كۆرسەت\"\nRenameProfile.Title=\"سەپلىمە ھۆججەت ئاتىنى ئۆزگەرت\"\nBasic.Main.MixerRename.Title=\"ئۈن مەنبەسىنىڭ ئاتىنى ئۆزگەرت\"\nBasic.Main.MixerRename.Text=\"ئۈن مەنبەسىنىڭ ئىسمىنى كىرگۈزۈڭ\"\nBasic.Main.PreviewDisabled=\"نۆۋەتتە ئالدىن كۆزىتىش چەكلەندى\"\nBasic.SourceSelect=\"مەنبە قۇر/تاللا\"\nBasic.SourceSelect.CreateNew=\"يېڭىدىن قۇر\"\nBasic.SourceSelect.AddExisting=\"مەۋجۇتنى قوش\"\nBasic.SourceSelect.AddVisible=\"مەنبە كۆرۈنۈشچان\"\nBasic.Main.Sources.Visibility=\"كۆرۈشچانلىقى\"\nBasic.Main.Sources.VisibilityDescription=\"كەرگەدىكى «%1» نىڭ كۆرۈنۈشچانلىقىنى تىزگىنلەيدۇ\"\nBasic.Main.Sources.Lock=\"قۇلۇپلا\"\nBasic.Main.Sources.LockDescription=\"كەرگەدىكى «%1» نىڭ ئورنى ۋە ئۆلچىمىنى قۇلۇپلايدۇ\"\nBasic.PropertiesWindow=\"«%1» نىڭ خاسلىقى\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (ئۆزلۈكىدىن تاللاش: %2)\"\nBasic.PropertiesWindow.SelectColor=\"رەڭ تاللا\"\nBasic.PropertiesWindow.SelectFont=\"خەت نۇسخا تاللا\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"خەت نۇسخا تاللاش\"\nBasic.PropertiesWindow.ConfirmTitle=\"تەڭشەك ئۆزگەردى\"\nBasic.PropertiesWindow.Confirm=\"ساقلانمىغان ئۆزگىرىشلەر بار. ئۇلارنى ساقلاپ قالامسىز؟\"\nBasic.PropertiesWindow.NoProperties=\"ئىشلىتىشچان خاسلىق يوق\"\nBasic.PropertiesWindow.AddFiles=\"ھۆججەت قوش\"\nBasic.PropertiesWindow.AddDir=\"مۇندەرىجە قوش\"\nBasic.PropertiesWindow.AddURL=\"يول/URL قوش\"\nBasic.PropertiesWindow.AddEditableListDir=\"«%1» غا مۇندەرىجە قوش\"\nBasic.PropertiesWindow.AddEditableListFiles=\"«%1» غا مۇندەرىجە قوش\"\nBasic.PropertiesWindow.AddEditableListEntry=\"«%1» غا تۈر قوش\"\nBasic.PropertiesWindow.EditEditableListEntry=\"«%1» نىڭ تۈرىنى تەھرىرلە\"\nBasic.PropertiesView.FPS.Simple=\"ئاددىي FPS قىممىتى\"\nBasic.PropertiesView.FPS.Rational=\"مۇۋاپىق FPS قىممىتى\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"ئىناۋەتلىك FPS دائىرىسى:\"\nBasic.PropertiesView.UrlButton.Text=\"كۆڭۈلدىكى توركۆرگۈڭىزدە بۇ ئۇلانمىنى ئاچامدۇ؟\"\nBasic.PropertiesView.UrlButton.Text.Url=\"تور ئادرېسى: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"URL ئاچ\"\nBasic.InteractionWindow=\"«%1» بىلەن تەسىرلىشىۋاتىدۇ\"\nBasic.StatusBar.Reconnecting=\"ئۈزۈلدى، %2 سېكۇنتتا قايتا ئۇلىنىدۇ (سىناق %1)‎‏\"\nBasic.StatusBar.AttemptingReconnect=\"قايتا ئۇلىنىشنى سىناۋاتىدۇ…(سىناق %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"قايتا باغلىنىش مۇۋەپپەقىيەتلىك بولدى\"\nBasic.StatusBar.Delay=\"كېچىكىش (%1 سېكۇنت)\"\nBasic.StatusBar.DelayStartingIn=\"كېچىكىش (%1 سېكۇنتتا باشلىنىدۇ)\"\nBasic.StatusBar.DelayStoppingIn=\"كېچىكىش (%1 سېكۇنتتا توختايدۇ)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"كېچىكىش (%1 سېكۇنتتا توختايدۇ، %2 سېكۇنتتا باشلىنىدۇ)\"\nBasic.StatusBar.RecordingSavedTo=\"خاتىرىلەش «%1» گە ساقلاندى\"\nBasic.StatusBar.ReplayBufferSavedTo=\"قايتا قويىدىغان يىغلەك «%1» غا ساقلاندى\"\nBasic.StatusBar.ScreenshotSavedTo=\"ئېكران كەسمىسى «%1» غا ساقلاندى\"\nBasic.StatusBar.AutoRemuxedTo=\"خاتىرىلەش ئۆزلۈكىدىن «%1» غا قايتا كودلاندى\"\nBasic.Filters=\"سۈزگۈچ\"\nBasic.Filters.AsyncFilters=\"ئۈن/سىن سۈزگۈچ\"\nBasic.Filters.AudioFilters=\"ئۈن سۈزگۈچ\"\nBasic.Filters.EffectFilters=\"ئۈنۈم سۈزگۈچ\"\nBasic.Filters.Title=\"«%1» نىڭ سۈزگۈچى\"\nBasic.Filters.AddFilter.Title=\"سۈزگۈچ ئىسمى\"\nBasic.Filters.AddFilter.Text=\"سۈزگۈچ ئىسمىنى بەلگىلەڭ\"\nBasic.TransformWindow=\"كۆرۈنۈش تۈر ئالمىشىشى\"\nBasic.TransformWindow.Position=\"ئورنى\"\nBasic.TransformWindow.PositionX=\"X ئورنى\"\nBasic.TransformWindow.PositionY=\"Y ئورنى\"\nBasic.TransformWindow.Rotation=\"ئايلاندۇرۇش\"\nBasic.TransformWindow.Size=\"چوڭلۇقى\"\nBasic.TransformWindow.Width=\"كەڭلىك\"\nBasic.TransformWindow.Height=\"ئېگىزلىك\"\nBasic.TransformWindow.Alignment=\"ئورۇن توغرىلىنىشى\"\nBasic.TransformWindow.BoundsType=\"رامكا قۇتا تۈرى\"\nBasic.TransformWindow.BoundsAlignment=\"رامكا قۇتىسىدا توغرىلىنىشى\"\nBasic.TransformWindow.Bounds=\"رامكا قۇتا چوڭلۇقى\"\nBasic.TransformWindow.BoundsWidth=\"رامكا قۇتا كەڭلىكى\"\nBasic.TransformWindow.BoundsHeight=\"رامكا قۇتا ئېگىزلىكى\"\nBasic.TransformWindow.CropToBounds=\"رامكا قۇتىسىغىچە قايچىلا\"\nBasic.TransformWindow.Crop=\"قايچىلا\"\nBasic.TransformWindow.CropLeft=\"سولنى قايچىلا\"\nBasic.TransformWindow.CropRight=\"ئوڭنى قايچىلا\"\nBasic.TransformWindow.CropTop=\"ئۈستىنى قايچىلا\"\nBasic.TransformWindow.CropBottom=\"ئاستىنى قايچىلا\"\nBasic.TransformWindow.Alignment.TopLeft=\"سول ئۈستى\"\nBasic.TransformWindow.Alignment.TopCenter=\"ئوتتۇرا ئۈستى\"\nBasic.TransformWindow.Alignment.TopRight=\"ئوڭ ئۈستى\"\nBasic.TransformWindow.Alignment.CenterLeft=\"ئوتتۇرا سول\"\nBasic.TransformWindow.Alignment.Center=\"مەركەز\"\nBasic.TransformWindow.Alignment.CenterRight=\"ئوتتۇرا ئوڭ\"\nBasic.TransformWindow.Alignment.BottomLeft=\"سول ئاستى\"\nBasic.TransformWindow.Alignment.BottomCenter=\"ئاستى ئوتتۇرا\"\nBasic.TransformWindow.Alignment.BottomRight=\"ئوڭ ئاستى\"\nBasic.TransformWindow.BoundsType.None=\"رامكىسى يوق\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"ئەڭ چوڭ چوڭلۇقىلا\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"ئىچكى رامكىغا نىسبەتلەشتۈرىدۇ\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"تاشقى امكىغا نىسبەتلەشتۈرىدۇ\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"رامكا كەڭلىكىگە نىسبەتلەشتۈرىدۇ\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"رامكا ئېگىزلىكىگە نىسبەتلەشتۈرىدۇ\"\nBasic.TransformWindow.BoundsType.Stretch=\"رامكىغا يايىدۇ\"\nBasic.TransformWindow.Title=\"«%1» نىڭ ئالمىشىشنى تەھرىرلە\"\nBasic.TransformWindow.NoSelectedSource=\"مەنبە تاللانمىغان\"\nBasic.Main.AddSourceHelp.Title=\"مەنبە قوشقىلى بولمىدى\"\nBasic.Main.AddSourceHelp.Text=\"سىز مەنبەگە ئاز دېگەندە بىر كۆرۈنۈش قوشۇشىڭىز كېرەك.\"\nBasic.Main.Scenes=\"كۆرۈنۈش\"\nBasic.Main.Sources=\"مەنبە\"\nBasic.Main.Source=\"مەنبە\"\nBasic.Main.Controls=\"تىزگىنلەشلەر\"\nBasic.Main.PreparingStream=\"تەييارلاۋاتىدۇ…\"\nBasic.Main.Connecting=\"باغلىنىۋاتىدۇ…\"\nBasic.Main.StartRecording=\"خاتىرىلەشنى باشلا\"\nBasic.Main.StartReplayBuffer=\"غەملەكنى قايتا قويۇشنى باشلا\"\nBasic.Main.SaveReplay=\"قايتا قويۇشنى ساقلا\"\nBasic.Main.StartStreaming=\"ئېقىمنى باشلا\"\nBasic.Main.StartBroadcast=\"شۇئان تارقىتىشقا يۆتكەل\"\nBasic.Main.StartVirtualCam=\"مەۋھۇم كامېرانى باشلا\"\nBasic.Main.StopRecording=\"خاتىرىلەشنى توختات\"\nBasic.Main.PauseRecording=\"خاتىرىلەشنى ۋاقىتلىق توختات\"\nBasic.Main.UnpauseRecording=\"خاتىرىلەشنى داۋاملاشتۇر\"\nBasic.Main.SplitFile=\"خاتىرە ھۆججەتنى پارچىلايدۇ\"\nBasic.Main.AddChapterMarker=\"باب بەلگىسىنى قوشىدۇ (ئارىلاش MP4 لا)\"\nBasic.Main.StoppingRecording=\"خاتىرىلەشنى توختىتىۋاتىدۇ…\"\nBasic.Main.StopReplayBuffer=\"غەملەكنى قايتا قويۇشنى توختات\"\nBasic.Main.StoppingReplayBuffer=\"غەملەكنى قايتا قويۇشنى توختىتىۋاتىدۇ…\"\nBasic.Main.SetupBroadcast=\"تارقاتقۇ باشقۇرۇش\"\nBasic.Main.StopStreaming=\"ئېقىمنى توختات\"\nBasic.Main.StopBroadcast=\"تارقاتقۇنى ئاخىرلاشتۇر\"\nBasic.Main.AutoStopEnabled=\"(ئۆزلۈكىدىن توختايدۇ)\"\nBasic.Main.StoppingStreaming=\"ئېقىمنى توختىتىۋاتىدۇ…\"\nBasic.Main.ForceStopStreaming=\"ئېقىمنى توختات (كېچىكىشنى تاشلىۋەت)\"\nBasic.Main.ShowContextBar=\"مەنبە قورال بالداقنى كۆرسەت\"\nBasic.Main.HideContextBar=\"مەنبە قورال بالداقنى يوشۇر\"\nBasic.Main.StopVirtualCam=\"مەۋھۇم كامېرانى توختات\"\nBasic.Main.Group=\"%1-گۇرۇپپا\"\nBasic.Main.GroupItems=\"گۇرۇپپا تاللانغان تۈرلەر\"\nBasic.Main.Ungroup=\"گۇرۇپپا يەش\"\nBasic.Main.GridMode=\"سېتكا ھالىتى\"\nBasic.Main.ListMode=\"تىزىم ھالىتى\"\nBasic.Main.VirtualCamConfig=\"مەۋھۇم كامېرا سەپلىمىسى\"\nBasic.VCam.VirtualCamera=\"مەۋھۇم كامېرا\"\nBasic.VCam.OutputType=\"چىقىرىش تۈرى\"\nBasic.VCam.OutputSelection=\"چىقىرىش تاللاش\"\nBasic.VCam.OutputType.Program=\"پىروگرامما (كۆڭۈلدىكى)\"\nBasic.VCam.OutputSelection.NoSelection=\"بۇ خىل چىقىرىش تۈرى تاللانمىدى\"\nBasic.VCam.RestartWarning=\"بۇ ئۆزگىرىش قوللىنىلسا مەۋھۇم كامېرا قايتا قوزغىلىدۇ\"\nBasic.MainMenu.File=\"ھۆججەت(&F)\"\nBasic.MainMenu.File.Export=\"چىقار(&E)\"\nBasic.MainMenu.File.Import=\"ئەكىر(&I)\"\nBasic.MainMenu.File.ShowRecordings=\"خاتىرىنى كۆرسەت(&R)\"\nBasic.MainMenu.File.Remux=\"قايتا كودلاپ خاتىرىلەش\"\nBasic.MainMenu.File.Settings=\"تەڭشەكلەر(&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"تەڭشەك قىسقۇچىنى كۆرسەت\"\nBasic.MainMenu.File.ShowProfileFolder=\"سەپلىمە ھۆججەت قىسقۇچنى كۆرسەت\"\nBasic.MainMenu.File.ShowMissingFiles=\"كەم ھۆججەتنى تەكشۈر\"\nBasic.MainMenu.File.Exit=\"ﭼﯧﻜﯩﻦ(&X)\"\nBasic.MainMenu.Edit=\"تەھرىر(&E)\"\nBasic.MainMenu.Edit.LockPreview=\"ئالدىن كۆزىتىشنى قۇلۇپلا(&L)\"\nBasic.MainMenu.Edit.Scale=\"ئالدىن كۆزىتىش ۋە نىسبەت\"\nBasic.MainMenu.Edit.Scale.Window=\"كۆزنەككە نىسبەتلەشتۈر\"\nBasic.MainMenu.Edit.Scale.Canvas=\"كەرگە  (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"چىقىرىش (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"چوڭلۇقى (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"يېقىنلات\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"يىراقلات\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"كېڭەيت تارايتنى ئەسلىگە قايتۇر\"\nBasic.MainMenu.Edit.Transform=\"ئالماشتۇر(&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"ئالماشتۇرۇش تەھرىر(&E)…\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"ئالماشتۇرۇشنى كۆچۈر\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"ئالماشتۇرۇشنى چاپلا\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"ئالماشتۇرۇشنى ئەسلىگە قايتۇر(&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"ئوڭغا 90 گىرادۇس ئايلاندۇر\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"سولغا 90 گرادۇس ئايلاندۇر\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"180 گرادۇس ئايلاندۇر\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"توغرىسىغا ئۆرۈ\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"بويىغا ئۆرۈ\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"ئېكرانغا تولدۇر(&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"ئېكرانغا ياي(&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"ئېكران ئوتتۇرىسىغا توغرىلا(&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"بويىغا ئوتتۇرىغا توغرىلا\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"توغرىسىغا ئوتتۇرىغا توغرىلا\"\nBasic.MainMenu.Edit.Order=\"تەرتىپلە(&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"يۇقىرىغا يۆتكە(&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"تۆۋەنگە يۆتكە(&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"چوققىغا يۆتكە(&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"ئاستىغا يۆتكە(&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"ئالىي ئۈن خاسلىقى(&A)\"\nBasic.MainMenu.View=\"ﻛﯚﺭﯛﻧﯜﺵ(&V)\"\nBasic.MainMenu.View.Toolbars=\"قورال بالداق(&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"لەڭگەر قورال بالداق\"\nBasic.MainMenu.View.ContextBar=\"مەنبە قورال بالداق\"\nBasic.MainMenu.View.SourceIcons=\"مەنبە سىنبەلگە(&I)\"\nBasic.MainMenu.View.StatusBar=\"ھالەت بالداق(&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"پۈتۈن ئېكران ئارايۈزى\"\nBasic.MainMenu.View.ResetUI=\"ئارايۈزنى ئەسلىگە قايتۇر(&R)\"\nBasic.MainMenu.View.AlwaysOnTop=\"ھەمىشە ئۈستىدە (&A)\"\nBasic.MainMenu.View.SceneListMode=\"كۆرۈنۈش تىزىم ھالىتى\"\nBasic.MainMenu.Docks=\"لەڭگەر(&D)\"\nBasic.MainMenu.Docks.ResetDocks=\"لەڭگەرنى ئەسلىگە قايتۇر(&R)\"\nBasic.MainMenu.Docks.LockDocks=\"لەڭگەرنى قۇلۇپلا(&L)\"\nBasic.MainMenu.Docks.SideDocks=\"تولۇق ئېگىزلىكتىكى لەڭگەر(&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"ئىختىيارى توركۆرگۈ لەڭگىرى(&C)\"\nBasic.MainMenu.Import=\"ئەكىر...\"\nBasic.MainMenu.Export=\"چىقار...\"\nBasic.MainMenu.SceneCollection=\"كۆرۈنۈش توپلىمى(&S)\"\nBasic.MainMenu.Profile=\"سەپلىمە ھۆججەت(&P)\"\nBasic.MainMenu.Profile.Import=\"سەپلىمە ئەكىر\"\nBasic.MainMenu.Profile.Export=\"سەپلىمە چىقار\"\nBasic.MainMenu.SceneCollection.Import=\"كۆرۈنۈش توپلىمى ئەكىر\"\nBasic.MainMenu.SceneCollection.Export=\"كۆرۈنۈش توپلىمى چىقار\"\nBasic.MainMenu.SceneCollection.Remigrate=\"ئاساسىي ئېنىقلىقنى ئەسلىگە قايتۇرىدۇ\"\nBasic.MainMenu.SceneCollection.Migrate=\"ئاساسىي ئېنىقلىق تەڭشىكى\"\nBasic.MainMenu.Profile.Exists=\"سەپلىمە مەۋجۇت\"\nBasic.MainMenu.Tools=\"قورال(&T)\"\nBasic.MainMenu.Help=\"ياردەم(&H)\"\nBasic.MainMenu.Help.HelpPortal=\"ياردەم ۋە باشلىنىش\"\nBasic.MainMenu.Help.Website=\"زىيارەت ۋە تور بېكەت(&W)\"\nBasic.MainMenu.Help.Discord=\"&Discord مۇلازىمېتىرغا قوشۇل\"\nBasic.MainMenu.Help.WhatsNew=\"يېڭىلىقلار\"\nBasic.MainMenu.Help.Logs=\"خاتىرە ھۆججەت(&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"خاتىرە ھۆججەتنى كۆرسەت(&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"نۆۋەتتىكى خاتىرە ھۆججەتنى يۈكلە(&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"ئالدىنقى خاتىرە ھۆججەتنى يۈكلە(&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"نۆۋەتتىكى خاتىرىنى كۆرسەت(&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"تارقىتىش ئىزاھاتى\"\nBasic.MainMenu.Help.CheckForUpdates=\"يېڭىلانمىلارنى تەكشۈر\"\nBasic.MainMenu.Help.Repair=\"ھۆججەت مۇكەممەللىكىنى تەكشۈر\"\nBasic.MainMenu.Help.RestartSafeMode=\"بىخەتەر ھالەتتە قايتا قوزغات\"\nBasic.MainMenu.Help.RestartNormal=\"ئۆلچەملىك ھالەتتە قايتا قوزغات\"\nBasic.MainMenu.Help.CrashLogs=\"يىمىرىلىش دوكلاتى(&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"يىمىرىلىش دوكلاتىنى كۆرسەت(&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"ئالدىنقى يىمىرىلىش دوكلاتىنى يۈكلە(&P)\"\nBasic.MainMenu.Help.About=\"ھەققىدە(&A)\"\nBasic.Settings.ProgramRestart=\"بۇ تەڭشەكلەرنىڭ كۈچكە ئىگە بولۇشى ئۈچۈن پىروگراممىنى قايتا قوزغىتىش كېرەك.\"\nBasic.Settings.ConfirmTitle=\"ئۆزگەرتىشنى جەزملە\"\nBasic.Settings.Confirm=\"ساقلىمىغان ئۆزگەرتىشلىرىڭىز بار. ئۆزگەرتكەننى ساقلامدۇ؟\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 بەزى ئېقىم تارقىتىش تەڭشەكلىرىڭىزنى تىزگىنلەۋاتىدۇ\"\nBasic.Settings.General=\"ئادەتتىكى\"\nBasic.Settings.General.Language=\"تىل\"\nBasic.Settings.General.Updater=\"يېڭىلانمىلار\"\nBasic.Settings.General.UpdateChannel=\"قانال يېڭىلاش\"\nBasic.Settings.General.UpdateChannelDisabled=\"(چەكلەنگەن)\"\nBasic.Settings.General.UpdateChannelDefault=\"(كۆڭۈلدىكى)\"\nBasic.Settings.General.EnableAutoUpdates=\"قوزغالغاندا يېڭىلاشنى ئۆزلۈكىدىن تەكشۈرىدۇ\"\nBasic.Settings.General.OpenStatsOnStartup=\"قوزغالغاندا ئىستاتىستىكا كۆزنەكنى كۆرسىتىدۇ\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"ئېكران تۇتقاندا OBS كۆزنەكنى يوشۇرىدۇ\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"بۇ تەڭشەك بارلىق غەيرى پىرويېكتورلۇق OBS Studio كۆزنىكىنى يوشۇرىدۇ، OBS ئېكران تۇتمايدۇ، يىغىن، ئېكران ھەمبەھىرلەش، يىراقتىن قوللاش، ئېكران تۇتۇش ۋە باشقا ئېكران تۇتۇش پىروگراممىلىرىغا ئوخشاش باشقا پىروگراممىلارغا تەسىر كۆرسىتىشنىڭ ئالدىنى ئالىدۇ.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"بۇ تەڭشەك قوزغىتىلسا بارلىق غەيرى پىرويېكتورلۇق OBS Studio كۆزنىكىنى يوشۇرىدۇ، OBS ئېكران تۇتمايدۇ، يىغىن، ئېكران ھەمبەھىرلەش، يىراقتىن قوللاش، ئېكران تۇتۇش ۋە باشقا ئېكران تۇتۇش پىروگراممىلىرىغا ئوخشاش باشقا پىروگراممىلارغا تەسىر كۆرسىتىشنىڭ ئالدىنى ئالىدۇ.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"ئېقىم تارقىتىشنى باشلىغاندا جەزملەش كۆزنىكى كۆرۈنىدۇ\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"ئېقىم تارقىتىشنى توختاتقاندا جەزملەش كۆزنىكى كۆرۈنىدۇ\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"خاتىرىلەشنى توختاتقاندا جەزملەش كۆزنىكى كۆرۈنىدۇ\"\nBasic.Settings.General.Projectors=\"پىرويېكتور\"\nBasic.Settings.General.HideProjectorCursor=\"پىرويېكتور كۆزنىكىدە سىنبەلگىنى يوشۇرىدۇ\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"پىرويېكتور كۆزنىكىنى ھەمىشە چوققىلايدۇ\"\nBasic.Settings.General.Snapping=\"مەنبە توغرىلاش چاپلاشتۇرۇش\"\nBasic.Settings.General.ScreenSnapping=\"مەنبەنى ئېكران گىرۋىكىگە چاپلاشتۇرىدۇ\"\nBasic.Settings.General.CenterSnapping=\"مەنبەنى توغرىسىغا ۋە بويىغا ئوتتۇرىغا چاپلاشتۇرىدۇ\"\nBasic.Settings.General.SourceSnapping=\"مەنبەنى باشقا مەنبەگە چاپلاشتۇرىدۇ\"\nBasic.Settings.General.SnapDistance=\"چاپلىشىش سەزگۈرلۈكى\"\nBasic.Settings.General.SpacingHelpers=\"پېكسىل توغرىلاش يېتەكچىسىنى كۆرسىتىدۇ\"\nBasic.Settings.General.RecordWhenStreaming=\"ئېقىم تارقاتقاندا ئۆزلۈكىدىن خاتىرىلەيدۇ\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"ئېقىم تارقىتىش توختىغاندا خاتىرىلەۋېرىدۇ\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"ئېقىم تارقىتىۋاتقاندا ئۆزلۈكىدىن غەملەكنى قايتا قويۇشنى باشلايدۇ\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"ئېقىم تارقىتىش توختىغاندا غەملەكنى قايتا قويۇشنىڭ ئاكتىپلىقىنى داۋاملاشتۇرىدۇ\"\nBasic.Settings.General.SysTray=\"سىستېما قورال تاختا\"\nBasic.Settings.General.SysTrayWhenStarted=\"باشلانغاندا سىستېما قورال تاختىغا كىچىكلىتىدۇ\"\nBasic.Settings.General.SystemTrayHideMinimize=\"ھەمىشە ۋەزىپە بالداققا ئەمەس بەلكى سىستېما قورال تاختىغا كىچىكلىتىدۇ\"\nBasic.Settings.General.SaveProjectors=\"چېكىنگەندە پىرويېكتور تەڭشىكىنى ساقلايدۇ\"\nBasic.Settings.General.CloseExistingProjectors=\"ھەر ئېكراندا بىر پۈتۈن ئېكران  پىرويېكتورى بىلەن چەكلەيدۇ\"\nBasic.Settings.General.Preview=\"ئالدىن كۆزەت\"\nBasic.Settings.General.OverflowHidden=\"تېشىپ كېتىشنى يوشۇرىدۇ\"\nBasic.Settings.General.OverflowAlwaysVisible=\"تېشىپ كېتىش ھەمىشە كۆرۈنىدۇ\"\nBasic.Settings.General.OverflowSelectionHidden=\"مەنبە كۆرۈنمىگەندىمۇ تېشىپ كېتىشنى كۆرسىتىدۇ\"\nBasic.Settings.General.Importers=\"ئەكىرگۈچ\"\nBasic.Settings.General.AutomaticCollectionSearch=\"ئەكىرگەندە مەلۇم بولغان كۆرۈنۈش توپلام ئورنىنى ئىزدەيدۇ\"\nBasic.Settings.General.SwitchOnDoubleClick=\"قوش چېكىلسە كۆرۈنۈشكە ئالمىشىدۇ\"\nBasic.Settings.General.StudioPortraitLayout=\"بويىغا/توغرىسىغا جايلاشتۇرۇشنى قوزغىتىدۇ\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"ئالدىن كۆزىتىش/كۆرگەزمە بەلگىسىنى كۆرسىتىدۇ\"\nBasic.Settings.General.Multiview=\"كۆپ كۆرۈنۈش\"\nBasic.Settings.General.Multiview.MouseSwitch=\"چېكىلسە كۆرۈنۈش ئارىسىدا ئالماشتۇرىدۇ\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"كۆرۈنۈش ئىسمىنى كۆرسىتىدۇ\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"بىخەتەر دائىرىنى كۆرسىتىدۇ (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"كۆپ كۆرۈنۈش جايلاشتۇرۇش\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"توغرىسىغا، ئۈستى (8 كۆرۈنۈش)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"توغرىسىغا، ئاستى (8 كۆرۈنۈش)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"بويىغا، سول (8 كۆرۈنۈش)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"بويىغا، ئوڭ (8 كۆرۈنۈش)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"توغرىسىغا، ئۈستى (18 كۆرۈنۈش)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"توغرىسىغا، ئۈستى (24 كۆرۈنۈش)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"كۆرۈنۈشلا (4 كۆرۈنۈش)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"كۆرۈنۈشلا (9 كۆرۈنۈش)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"كۆرۈنۈشلا (16 كۆرۈنۈش)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"كۆرۈنۈشلا (25 كۆرۈنۈش)\"\nBasic.Settings.General.ChannelName.stable=\"مۇقىم\"\nBasic.Settings.General.ChannelDescription.stable=\"ئاخىرقى مۇقىم تارقىتىلغان نەشرى\"\nBasic.Settings.General.ChannelName.beta=\"سىناق/كاندىدات تارقىتىش نەشرى\"\nBasic.Settings.General.ChannelDescription.beta=\"ئانچە مۇقىم بولمىغان ئالدىن تارقىتىلغان نەشرى\"\nBasic.Settings.Appearance=\"كۆرۈنۈش\"\nBasic.Settings.Appearance.General=\"ئادەتتىكى\"\nBasic.Settings.Appearance.General.Theme=\"ئۆرنەك\"\nBasic.Settings.Appearance.General.Variant=\"ئۇسلۇب\"\nBasic.Settings.Appearance.General.NoVariant=\"ھېچقانداق ئۇسلۇب يوق\"\nBasic.Settings.Appearance.FontScale=\"خەت چوڭلۇقى\"\nBasic.Settings.Appearance.Density=\"زىچلىقى\"\nBasic.Settings.Appearance.Classic=\"كىلاسسىك\"\nBasic.Settings.Appearance.Compact=\"ئىخچام\"\nBasic.Settings.Appearance.Normal=\"ئادەتتىكى\"\nBasic.Settings.Appearance.Comfortable=\"راھەت\"\nBasic.Settings.Appearance.OptionsWarning=\"بۇ ئۇسلۇبتا بەزى كۆرۈنۈش تاللانمىلىرىنى ئىشلەتكىلى بولمايدۇ.\"\nBasic.Settings.Stream=\"سانلىق مەلۇمات ئېقىمى\"\nBasic.Settings.Stream.Destination=\"نىشان\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"كىملىك دەلىللەش ئىشلەت\"\nBasic.Settings.Stream.Custom.Username=\"ئىشلەتكۈچى ئاتى\"\nBasic.Settings.Stream.Custom.Password=\"ئىم\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: srp_username كىرگۈزۈلىدۇ.\\nRTMP: ئىشلەتكۈچى ئاتى كىرگۈزۈلىدۇ.\\nSRT:  ئىشلىتىلمىدى.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: srp_password كىرگۈزۈلىدۇ.\\nRTMP: ئىم كىرگۈزۈلىدۇ.\\nSRT: شىفىرلىق ئىم جۈملىسى كىرگۈزۈلىدۇ.\"\nBasic.Settings.Stream.BandwidthTestMode=\"بەلباغ  كەڭلىكى سىنىقىنى قوزغىتىدۇ\"\nBasic.Settings.Stream.TTVAddon=\"Twitch سۆھبەت قوشۇلما\"\nBasic.Settings.Stream.TTVAddon.None=\"يوق\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV ۋە FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"ئېقىم تەڭشىكى كەم\"\nBasic.Settings.Stream.StreamSettingsWarning=\"تەڭشەكنى ئاچ\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"تور ئادرېسى ۋە ئېقىم ئاچقۇچى كەم.\\n\\nتەڭشەكنى ئېچىپ، ئېقىم بەتكۈچتە تور ئادرېسى ۋە ئېقىم ئاچقۇچىنى كىرگۈزگىلى بولىدۇ.\"\nBasic.Settings.Stream.MissingUrl=\"تور ئادرېسى كەم.\\n\\nتەڭشەكنى ئېچىپ، ئېقىم بەتكۈچتە تور ئادرېسىنى كىرگۈزگىلى بولىدۇ.\"\nBasic.Settings.Stream.MissingStreamKey=\"ئېقىم ئاچقۇچى كەم.\\n\\nتەڭشەكنى ئېچىپ، ئېقىم بەتكۈچتە ئېقىم ئاچقۇچىنى كىرگۈزگىلى بولىدۇ.\"\nBasic.Settings.Stream.IgnoreRecommended=\"ئېقىم مۇلازىمىتى تەڭشىكىنىڭ تەۋسىيەسىگە پەرۋا قىلمايدۇ\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"تەۋسىيە تەڭشەكنى قاپلايدۇ\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"ئاگاھلاندۇرۇش: مۇلازىمەت چەكلىمىسىگە پەرۋا قىلمىغاندا ئېقىم يوللاش سۈپىتىنىڭ تۆۋەنلىشىنى كەلتۈرۈپ چىقىرىشى مۇمكىن ياكى ئېقىم تارقىتىشىڭىزنىڭ ئالدىنى ئېلىشى مۇمكىن.\\n\\nداۋاملاشتۇرامدۇ؟\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"سىننىڭ ئەڭ چوڭ بىت نىسبىتى: %1kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"ئۈننىڭ ئەڭ چوڭ بىت نىسبىتى: %1kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"ئەڭ چوڭ ئېنىقلىقى: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"ئەڭ چوڭ FPS: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"ئىختىيارى مۇلازىمېتىر بەلگىلەش…\"\nBasic.Settings.Stream.ServiceCustomServer=\"ئىختىيارى مۇلازىمېتىر\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"%1 نى قوزغات\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"ئەڭ چوڭ ئېقىم بەلباغ كەڭلىكى\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"ئاپتوماتىك\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"ئەڭ كۆپ سىن يولى\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"ئاپتوماتىك\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"ئېقىمنى FLV ئايلاندۇرۇپ ساقلاشنى قوزغىتىدۇ (ئاددى سىن خاتىرىلەش ھۆججەت تەڭشىكىنى ئىشلىتىدۇ)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"سەپلىمە قاپلاش (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"سەپلىمە قاپلاشنى قوزغىتىدۇ\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"كۆپ يوللۇق سىن\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"قوشۇمچە كەرگە\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"تەڭ تارقىتىش\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"تەڭ تارقىتىش ئىقتىدارى كۆپ خىل سىن سۈپىتىدە كودلاش ۋە سىن تارقىتىشقا يول قويىدۇ. <a href='https://obsproject.com/kb/whip-streaming-guide'>مول بىلىم</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"جەمئىي قەۋەت سانى\"\nBasic.Settings.Stream.AdvancedOptions=\"ئالىي تاللانما\"\nBasic.Settings.Output=\"چىقار\"\nBasic.Settings.Output.Format=\"سىن خاتىرىلەش پىچىمى\"\nBasic.Settings.Output.Format.MKV=\"Matroska  سىن (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"ئارىلاش MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"ئارىلاش MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"پارچىلانغان MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"پارچىلانغان MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"پارچىلانغان MOV بۆلەك شەكلىدە خاتىرىلەيدۇ، ئەنئەنىۋى MOV ھۆججىتىگە ئوخشاش ئەڭ ئاخىرىدا بىر تەرەپ قىلىش كېرەك بولمايدۇ.\\nبۇنداق بولغاندا دىسكىغا يېزىش ئۈزۈلۈپ قالغان تەقدىردىمۇ (مەسىلەن، كۆك ئېكران ياكى توك كېتىش)، ھۆججەتنى يەنىلا قويغىلى بولىدۇ.\\n\\nبۇنى ھەممە قويغۇچ ۋە تەھرىرلەش يۇمشاق دېتالى قوللىشى ناتايىن. زۆرۈر تېپىلغاندا، «ھۆججەت ← خاتىرىنى قايتا كودلا» نى ئىشلىتىپ ھۆججەتنى تېخىمۇ ماسلىشىدىغان پىچىمغا ئۆزگەرتىشكە بولىدۇ.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"پارچىلانغان MP4 بۆلەك شەكلىدە خاتىرىلەيدۇ، ئەنئەنىۋى MP4 ھۆججىتىگە ئوخشاش ئەڭ ئاخىرىدا بىر تەرەپ قىلىش كېرەك بولمايدۇ.\\nبۇنداق بولغاندا دىسكىغا يېزىش ئۈزۈلۈپ قالغان تەقدىردىمۇ (مەسىلەن، كۆك ئېكران ياكى توك كېتىش)، ھۆججەتنى يەنىلا قويغىلى بولىدۇ.\\n\\nبۇنى ھەممە قويغۇچ ۋە تەھرىرلەش يۇمشاق دېتالى قوللىشى ناتايىن. زۆرۈر تېپىلغاندا، «ھۆججەت ← خاتىرىنى قايتا كودلا» نى ئىشلىتىپ ھۆججەتنى تېخىمۇ ماسلىشىدىغان پىچىمغا ئۆزگەرتىشكە بولىدۇ.\"\nBasic.Settings.Output.Encoder.Video=\"سىن كودلىغۇچ\"\nBasic.Settings.Output.Encoder.Audio=\"ئۈن كودلىغۇچ\"\nBasic.Settings.Output.SelectDirectory=\"خاتىرە مۇندەرىجىسى تاللىنىدۇ\"\nBasic.Settings.Output.DynamicBitrate=\"تور قىستاڭچىلىقىغا تاقابىل تۇرۇش ئۈچۈن ھەرىكەتچان تەڭشەلگەن بىت نىسبىتى قوللىنىدۇ\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"تور قىستاڭچىلىقىغا تاقابىل تۇرۇش ئۈچۈن ھەرىكەتچان تەڭشەلگەن بىت نىسبىتى قوللىنىدۇ(سىناق)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"تور قىستاڭچىلىقى يۈز بەرگەندىكى تېز ھەرىكەتچان تەڭشەش بىت نىسبىتى، كاندۇك تاشلاش ئارقىلىق قىستاڭچىلىقنى پەسەيتمەيدۇ.\\n\\nدىققەت: قوزغىتىلغاندىن كېيىن، ئەگەر ئېغىر دەرىجىدىكى تور قىستاڭچىلىقى يۈز بەرسە، كۆرۈرمەنلەرنىڭ كېچىكىشى ئېشىپ كېتىشى مۇمكىن.\\nبىت نىسبىتى تۆۋەن بولغاندا، بىر قانچە مىنۇتتا ئاندىن ئەسلىگە كېلىدۇ.\\n\\nنۆۋەتتە پەقەت RTMP نىلا قوللايدۇ.\"\nBasic.Settings.Output.Mode=\"چىقىرىش ھالىتى\"\nBasic.Settings.Output.Mode.Simple=\"ئاددىي\"\nBasic.Settings.Output.Mode.Adv=\"ئالىي\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg چىقىرىش\"\nBasic.Settings.Output.UseReplayBuffer=\"غەملەكنى قايتا قويۇشنى قوزغىتىدۇ\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"ئەڭ ئۇزۇن قايتا قويۇش ۋاقتى\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"ئەڭ چوڭ ئەسلەك\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"مۆلچەرلەنگەن ئەسلەك ئىشلىتىلىشى: %1MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"ئاگاھلاندۇرۇش: تەخمىنەن %1MiB ئەسلەك ئىشلىتىدۇ، تەۋسىيە قىلىنغان يۇقىرى چەكتىن %2MiB ئېشىپ كەتتى\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"ئىشلىتىدىغان ئەسلەكنى مۆلچەرلىيەلمىدى. ئەسلەك ئىشلىتىشنىڭ يۇقىرى چېكىنى تەڭشەڭ.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"غەملەكنى قايتا قويۇش ھۆججەت ئىسمى ئالدى قوشۇلغۇچىسى\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"كەينى قوشۇلغۇچى\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"ئەگەر سىن خاتىرىلەش تۈرى ئىختىيارى چىقىرىش (FFmpeg) قا تەڭشەلگەندە غەملەكنى قايتا قويۇشنى ئىشلەتكىلى بولمايدۇ.\"\nBasic.Settings.Output.Simple.SavePath=\"سىن خاتىرىلەش يولى\"\nBasic.Settings.Output.Simple.RecordingQuality=\"سىن خاتىرىلەش سۈپىتى\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"ئېقىم بىلەن ئوخشاش\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"سۈپىتى يۇقىرى، ھۆججەت چوڭلۇقى ئوتتۇراھال\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"ئاساسەن پەرقسىز سۈپەت، ھۆججەت چوڭلۇقى چوڭ\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"خورىمىغان سۈپەت، ھۆججەت چوڭلۇقى ئىنتايىن چوڭ\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"ئاگاھلاندۇرۇش: ئېقىم سىن بىت نىسبىتى %1 غا تەڭشەلدى، بۇ نۆۋەتتىكى ئېقىم مۇلازىمىتىنىڭ يۇقىرى چېكى.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"ئاگاھلاندۇرۇش: ئېقىم ئۈن بىت نىسبىتى %1 غا تەڭشەلدى، بۇ نۆۋەتتىكى ئېقىم مۇلازىمىتىنىڭ يۇقىرى چېكى.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"ئاگاھلاندۇرۇش: ئەگەر سىن خاتىرىلەش سۈپىتى «ئېقىم بىلەن ئوخشاش» قىلىپ تەڭشەلگەن بولسا سىن خاتىرىلەشنى ۋاقىتلىق توختاتقىلى بولمايدۇ.\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"ئاگاھلاندۇرۇش: نۆۋەتتە تاللىغان خاتىرىلەش پىچىمى بىلەن تاللىغان ئېقىم كودلىغۇچ ماسلاشمايدۇ.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"ئاگاھلاندۇرۇش:سىن خاتىرىلەش ۋە ئېقىم تارقىتىش بىرلا ۋاقىتتا ئېلىپ بېرىلىپ، يۇمشاق دېتال كودلىغۇچ كودى بىلەن ئېقىمنىڭ سۈپىتى ئوخشاش بولمىسا CPU نىڭ ئىشلىتىلىشى ئارتىپ كېتىدۇ.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"ئاگاھلاندۇرۇش: زەخىمسىز سۈپەتتە ئىنتايىن چوڭ ھۆججەت ھاسىل قىلىدۇ! يۇقىرى ئېنىقلىق ۋە كاندۇك نىسبىتىدىكى زەخىمسىز سۈپەتتە مىنۇتىغا 7 گىگابايت دىسكا بوشلۇقى ئىشلىتىدۇ، ئىنتايىن كۆپ دىسكا بوشلۇقىڭىز بولمىسا ئۇزۇن ۋاقىت زەخىمسىز سىن خاتىرىلىمەڭ. زەخىمسىز سۈپەتتە غەملەكنى قايتا قويۇشنى ئىشلەتكىلى بولمايدۇ.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"سىز راستتىنلا زەخىمسىز سۈپەتنى ئىشلىتەمسىز؟\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"زەخىمسىز سۈپەت ئاگاھلاندۇرۇشى!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"يۇمشاق دېتال (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"قاتتىق دېتال (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"قاتتىق دېتال (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"قاتتىق دېتال (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"قاتتىق دېتال (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"قاتتىق دېتال (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"قاتتىق دېتال (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"قاتتىق دېتال (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"قاتتىق دېتال (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"قاتتىق دېتال (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"قاتتىق دېتال (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"يۇمشاق دېتال (x264، CPU ئىشلىتىشى تۆۋەن، ھۆججەت چوڭىيىدۇ)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (كۆڭۈلدىكى)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD يولى (2-يولنى ئىشلىتىدۇ)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"ئۈن يولى\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"ئېنىقلىقى/كاندۇك نىسىبتى ماسلاشمايدۇ\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"بۇ ئېقىم مۇلازىمىتى نۆۋەتتىكى چىقىرىش ئېنىقلىقى ۋە ياكى كاندۇك نىسبىتىنى قوللىمايدۇ. ئۇلار ئەڭ يېقىن كېلىدىغان ماسلىشىشچان قىممەتكە ئۆزگەرتىلىدۇ:\\n\\n%1\\n\\nراستتىنلا داۋاملاشتۇرامسىز؟\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"ئېنىقلىقى: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"ماسلاشمايدىغان كودلىغۇچ\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\" «%1» ئېقىم مۇلازىمىتى «%2» كودلىغۇچنى قوللىمايدۇ. كودلىغۇچ «%3» غا ئۆزگەرتىلىدۇ.\\n\\nراستتىنلا داۋاملاشتۇرامسىز؟\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\" «%1» ئېقىم مۇلازىمىتى «%2» ۋە  «%3» كودلىغۇچنى قوللىمايدۇ. كودلىغۇچ «%4» ۋە «%5» غا ئۆزگەرتىلىدۇ.\\n\\nراستتىنلا داۋاملاشتۇرامسىز؟\"\nBasic.Settings.Output.VideoBitrate=\"سىن بىت نىسبىتى\"\nBasic.Settings.Output.AudioBitrate=\"ئۈن بىت نىسبىتى\"\nBasic.Settings.Output.Reconnect=\"ئۆزلۈكىدىن قايتا باغلىنىدۇ\"\nBasic.Settings.Output.RetryDelay=\"كېچىكتۈرۈشنى سىناش\"\nBasic.Settings.Output.MaxRetries=\"ئەڭ كۆپ سىناق قېتىم سانى\"\nBasic.Settings.Output.Advanced=\"ئىختىيارى كودلاش تەڭشىكىنى قوزغىتىدۇ (ئالىي)\"\nBasic.Settings.Output.EncoderPreset=\"كودلىغۇچ ئالدىن تەڭشىكى\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (CPU ئىشلىتىشى تۆۋەن، سۈپىتى ئەڭ تۆۋەت)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (CPU ئىشلىتىشى ئوتتۇراھال، سۈپىتى ئۆلچەملىك)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (CPU ئىشلىتىشى يۇقىرى، سۈپىتى يۇقىرى)\"\nBasic.Settings.Output.CustomEncoderSettings=\"ئىختىيارى كودلىغۇچ تەڭشەكلىرى\"\nBasic.Settings.Output.CustomMuxerSettings=\"ئىختىيارى بىرىكتۈرگۈچ تەڭشىكى\"\nBasic.Settings.Output.NoSpaceFileName=\"بوشلۇقسىز ھۆججەت ئىسمى ھاسىللايدۇ\"\nBasic.Settings.Output.Adv.Rescale=\"چىقىرىشنى چوڭلۇقىنى تەڭشەيدۇ\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"چەكلەنگەن\"\nBasic.Settings.Output.Adv.AudioTrack=\"ئۈن يولى\"\nBasic.Settings.Output.Adv.Streaming=\"ئېقىم تارقىتىش\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"ئېقىم تارقىتىش تەڭشىكى\"\nBasic.Settings.Output.Adv.Audio.Track1=\"1-ئاۋاز يولى\"\nBasic.Settings.Output.Adv.Audio.Track2=\"2-ئاۋاز يولى\"\nBasic.Settings.Output.Adv.Audio.Track3=\"3-ئاۋاز يولى\"\nBasic.Settings.Output.Adv.Audio.Track4=\"4-ئاۋاز يولى\"\nBasic.Settings.Output.Adv.Audio.Track5=\"5-ئاۋاز يولى\"\nBasic.Settings.Output.Adv.Audio.Track6=\"6-ئاۋاز يولى\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD ئاۋاز يولى\"\nBasic.Settings.Output.Adv.Encoder=\"كودلىغۇچ تەڭشىكى\"\nBasic.Settings.Output.Adv.Recording=\"خاتىرىلەۋاتىدۇ\"\nBasic.Settings.Output.Adv.Recording.Settings=\"خاتىرىلەش تەڭشىكى\"\nBasic.Settings.Output.Adv.Recording.RecType=\"خاتىرىلەش تۈرى\"\nBasic.Settings.Output.Adv.Recording.Type=\"تىپى\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"ئۆلچەملىك\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"ئىختىيارى چىقىرىش (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(ئېقىم ئاچقۇچى ئىشلەت)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"ھۆججەت ئىسمى پىچىمى\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"ھۆججەت مەۋجۇت بولسا قاپلايدۇ\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"ئىختىيارى چىقىرىش (FFmpeg) ھېچقانداق بىخەتەرلىك تەدبىرى تەمىنلىمىگەن.\\n«ئۆلچەملىك» خاتىرىلەش تۈرىنى ئىشلىتىشنى ئويلىشىڭ.\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg چىقىرىش تۈرى\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"تور ئادرېسىغا چىقىرىدۇ\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"ھۆججەتكە چىقىرىدۇ\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"كۆپ ئۇچرايدىغان خاتىرىلەش پىچىمى\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"بارلىق ھۆججەتلەر\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"ھۆججەت يولى ياكى تور ئادرېسى\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"يۈكدان پىچىمى\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"ئۈن\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"سىن\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"كۆڭۈلدىكى پىچىم\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"يۈكدان پىچىمىنىڭ چۈشەندۈرۈشى\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"ھۆججەت يولى ياكى تور ئادرېسىدىن ئۈن ياكى سىن كودلىغۇچنى پەرەز قىلدى\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"كۆڭۈلدىكى كودلىغۇچ\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"كودلىغۇچنى چەكلە\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"سىن كودلىغۇچ\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"سىن كودلىغۇچ تەڭشىكى (ئەگەر بولسا)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"ئۈن كودلىغۇچ\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"ئۈن كودلىغۇچ تەڭشىكى (ئەگەر بولسا)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"بىرىكتۈرگۈچ تەڭشىكى (ئەگەر بولسا)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"ئاچقۇچلۇق كېشەك ئارىلىقى (كېشەك)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"ھەممە كودلىغۇچنى كۆرسىتىدۇ (گەرچە ماسلاشمايدىغان بولسىمۇ)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg تەڭشەك\"\nBasic.Settings.Output.EnableSplitFile=\"ھۆججەتنى ئۆزلۈكىدىن پارچىلايدۇ\"\nBasic.Settings.Output.SplitFile.TypeTime=\"ۋاقىت بويىچە پارچىلايدۇ\"\nBasic.Settings.Output.SplitFile.TypeSize=\"چوڭلۇقى بويىچە پارچىلايدۇ\"\nBasic.Settings.Output.SplitFile.TypeManual=\"پەقەت قولدا پارچىلايدۇ\"\nBasic.Settings.Output.SplitFile.Time=\"پارچىلاش ۋاقتى\"\nBasic.Settings.Output.SplitFile.Size=\"پارچىلاش چوڭلۇقى\"\nScreenshot=\"ئېكران كەسمىسى چىقىرىش\"\nScreenshot.SourceHotkey=\"ئېكران كەسمىسى تاللىغان مەنبە\"\nScreenshot.StudioProgram=\"ئېكران كەسمىسى (چىقىرىش)\"\nScreenshot.Preview=\"ئېكران كەسمىسى (ئالدىن كۆزەت)\"\nScreenshot.Scene=\"ئېكران كەسمىسى (كۆرۈنۈش)\"\nScreenshot.Source=\"ئېكران كەسمىسى (مەنبە)\"\nFilenameFormatting.TT.CCYY=\"يىل، تۆت خانە\"\nFilenameFormatting.TT.YY=\"يىل، ئاخىرقى ئىككى خانە (00-99)\"\nFilenameFormatting.TT.MM=\"ئاي، ئونلۇق سىستېمىدىكى سان (01-12)\"\nFilenameFormatting.TT.DD=\"ئايدىكى كۈن، بىرىنچى خانىسىغا 0 تولۇقلىنىدۇ (01-31)\"\nFilenameFormatting.TT.hh=\"24 سائەت پىچىمىدىكى سائەت (00-23)\"\nFilenameFormatting.TT.mm=\"مىنۇت (00-59)\"\nFilenameFormatting.TT.ss=\"سېكۇنت (00-59)\"\nFilenameFormatting.TT.Percent=\"% بەلگىسى\"\nFilenameFormatting.TT.a=\"قىسقارتىلغان ھەپتە ئىسمى\"\nFilenameFormatting.TT.A=\"تولۇق ھەپتە ئىسمى\"\nFilenameFormatting.TT.b=\"قىسقارتىلغان ئاي ئىسمى\"\nFilenameFormatting.TT.B=\"تولۇق ئاي ئىسمى\"\nFilenameFormatting.TT.d=\"ئايدىكى كۈن، بىرىنچى خانىسىغا 0 تولۇقلىنىدۇ (01-31)\"\nFilenameFormatting.TT.H=\"24 سائەت پىچىمىدىكى سائەت (00-23)\"\nFilenameFormatting.TT.I=\"12 سائەت پىچىمىدىكى سائەت (01-12)\"\nFilenameFormatting.TT.m=\"ئاي، ئونلۇق سىستېمىدىكى سان (01-12)\"\nFilenameFormatting.TT.M=\"مىنۇت (00-59)\"\nFilenameFormatting.TT.p=\"چ ب ياكى چ ك بەلگىسى\"\nFilenameFormatting.TT.s=\"UNIX دەۋرىدىن بۇيانقى ۋاقىت (سېكۇنت)\"\nFilenameFormatting.TT.S=\"سېكۇنت (00-59)\"\nFilenameFormatting.TT.y=\"يىل، ئاخىرقى ئىككى خانە (00-99)\"\nFilenameFormatting.TT.Y=\"يىل\"\nFilenameFormatting.TT.z=\"خەلقئارا ئۆلچەملىك ۋاقىت يەنى UTC بىلەن ۋاقىت پەرقى - ISO 8601\"\nFilenameFormatting.TT.Z=\"ۋاقىت رايون ئىسمى ياكى قىسقارتىلمىسى\"\nFilenameFormatting.TT.FPS=\"ھەر سېكۇنتىغا كاندۇك\"\nFilenameFormatting.TT.CRES=\"ئاساسى (كەرگە) ئېنىقلىقى\"\nFilenameFormatting.TT.ORES=\"چىقىرىش (تەڭشەلگەن) ئېنىقلىقى\"\nFilenameFormatting.TT.VF=\"سىن پىچىمى\"\nBasic.Settings.Video=\"سىن\"\nBasic.Settings.Video.Adapter=\"سىن ماسلاشتۇرغۇچ\"\nBasic.Settings.Video.BaseResolution=\"ئاساسى (كەرگە) ئېنىقلىقى\"\nBasic.Settings.Video.ScaledResolution=\"چىقىرىش (تەڭشەلگەن) ئېنىقلىقى\"\nBasic.Settings.Video.DownscaleFilter=\"كىچىكلىش سۈزگۈچى\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[ئېنىقلىقى ماس كېلىدۇ، كىچىكلىتىش ھاجەتسىز]\"\nBasic.Settings.Video.FPS=\"fps\"\nBasic.Settings.Video.FPSCommon=\"ئادەتتىكى FPS قىممىتى\"\nBasic.Settings.Video.FPSInteger=\"پۈتۈن سان FPS قىممىتى\"\nBasic.Settings.Video.FPSFraction=\"پارچە FPS قىممىتى\"\nBasic.Settings.Video.Numerator=\"سۈرەت\"\nBasic.Settings.Video.Denominator=\"مەخرەج\"\nBasic.Settings.Video.Renderer=\"رەڭلىگۈچ\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (سىناق)\"\nBasic.Settings.Video.InvalidResolution=\"ئىناۋەتسىز ئېنىقلىق قىممىتى. چوقۇم [كەڭلىك]x[ئېگىزلىك] (مەسىلەن، 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"نۆۋەتتە سىن چىقىرىش ئاكتىپ ھالەتتە. سىن تەڭشىكىنى ئۆزگەرتىشتە خالىغان سىن چىقىرىشنى تاقاڭ.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"قوش سىزىقلىق (ئەڭ تېز، ئەمما چوڭلۇقى تەڭشەلسە غۇۋالىشىدۇ)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"قوش كۇبلۇق (چوڭلۇقى تەڭشەلسە ئېنىقلىشىدۇ، 16 ئەۋرىشكە)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (چوڭلۇقى تەڭشەلسە ئېنىقلىشىدۇ، 36 ئەۋرىشكە)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"دائىرە (ئوتتۇرىچە سالماقتا، 4/6/9 ئەۋرىشكە)\"\nBasic.Settings.Audio=\"ئۈن\"\nBasic.Settings.Audio.SampleRate=\"ئەۋرىشكە نىسبىتى\"\nBasic.Settings.Audio.Channels=\"قانال\"\nBasic.Settings.Audio.Meters=\"ئۆلچىگۈچ\"\nBasic.Settings.Audio.MeterDecayRate=\"ئاجىزلىشىش نىسبىتى\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"تېز\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"ئوتتۇرا (تۈرى I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"ئاستا (تۈرى II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"چوققا قىممەت ئۆلچىگۈچ تۈرى\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"ئەۋرىشكە چوققا قىممىتى\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"ھەقىقىي چوققا قىممىتى (CPU ئىشلىتىشى يۇقىرىراق)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ئاگاھلاندۇرۇش: ئەتراپتىكى ئاۋاز قوزغىتىلدى.\"\nBasic.Settings.Audio.MultichannelWarning=\"ئەگەر ئېقىم تارقاتماقچى بولغاندا، ئېقىم مۇلازىمىتى ئايلانما ئىستېرېئو ئاۋازنى قوبۇللاش ۋە ياندۇرۇپ قويۇشنى قوللايدىغان قوللىمايدىغانلىقىنى تەكشۈرۈڭ. مەسىلەن، Facebook 360 Live ئايلانما ئىستېرېئو ئاۋازنى تولۇق قوللايدۇ؛ YouTube Live مۇلازىمىتى 5.1 ئايلانما ئىستېرېئو ئاۋازنى قوبۇللاش (ۋە تېلېۋىزوردا قايتۇرۇپ قويۇش) نى قوللايدۇ.\\n\\nOBS نىڭ ئاۋاز سۈزگۈچىسى ئايلانما ئىستېرېئو ئاۋاز بىلەن ماسلىشىدۇ، ئەمما VST قىستۇرمىنىڭ قوللايدىغان قوللىمايدىغانلىقىغا كاپالەتلىك قىلالمايدۇ.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"ئايلانما ئىستېرېئو ئاۋازنى قوزغىتامدۇ؟\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"ئايلانما ئىستېرېئو ئاۋازنى راستتىنلا قوزغىتامسىز؟\"\nBasic.Settings.Audio.Devices=\"ئومۇمىيەت ئۈن ئۈسكۈنىسى\"\nBasic.Settings.Audio.DesktopDevice=\"ئۈستەلئۈستى ئاۋاز\"\nBasic.Settings.Audio.DesktopDevice2=\"ئۈستەلئۈستى ئاۋازلىق 2\"\nBasic.Settings.Audio.AuxDevice=\"مىكروفون/قوشۇمچە ئاۋاز\"\nBasic.Settings.Audio.AuxDevice2=\"مىكروفون/قوشۇمچە ئاۋاز 2\"\nBasic.Settings.Audio.AuxDevice3=\"مىكروفون/قوشۇمچە ئاۋاز 3\"\nBasic.Settings.Audio.AuxDevice4=\"مىكروفون/قوشۇمچە ئاۋاز 4\"\nBasic.Settings.Audio.EnablePushToMute=\"بېسىلسا ئۈنسىزلەشنى قوزغىتىدۇ\"\nBasic.Settings.Audio.PushToMuteDelay=\"بېسىلسا ئۈنسىزلەشنى كېچىكتۈرىدۇ\"\nBasic.Settings.Audio.EnablePushToTalk=\"بېسىپ سۆزلەشنى قوزغىتىدۇ\"\nBasic.Settings.Audio.PushToTalkDelay=\"بېسىپ سۆزلەشنى كېچىكتۈرىدۇ\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[ئۈسكۈنە باغلانمىدى ياكى ئىشلەتكىلى بولمايدۇ]\"\nBasic.Settings.Audio.Disabled=\"چەكلەنگەن\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"تۆۋەن كېچىكىش ئۈن غەملەش ھالىتى (Decklink/NDI چىقىرىشقا ئىشلىتىلىدۇ)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ئاگاھلاندۇرۇش: تۆۋەن كېچىكىش ئۈن غەملەش قوزغىتىلدى.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"تۆۋەن كېچىكىش ئۈن غەملەش ھالىتى ئاۋازنىڭ گاچىلىشىشىنى كەلتۈرۈپ چىقىرىشى ياكى بەزى مەنبەلەردىن قويۇش توختىشى مۇمكىن.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"تۆۋەن كېچىكىش ئۈن غەملەش ھالىتى قوزغىتىلامدۇ؟\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"تۆۋەن كېچىكىش ئۈن غەملەش ھالىتىنى راستتىنلا قوزغىتامسىز؟\"\nBasic.Settings.Accessibility=\"قوشۇمچە ئىقتىدار\"\nBasic.Settings.Accessibility.ColorOverrides=\"پەرقلىق رەڭ ئىشلىتىدۇ\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"مەنبە چېگراسى (تاللاش)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"مەنبە چېگراسى (قايچىلاش)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"مەنبە چېگراسى (لەيلىتىش)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"ئارىلاشتۇرغۇچ ئاۋاز بۆلىكى (-60 دىن -20dB غىچە)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"ئارىلاشتۇرغۇچ ئاۋاز بۆلىكى (-20 دىن -9dB غىچە)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"ئارىلاشتۇرغۇچ ئاۋاز بۆلىكى (-9 دىن -0dB غىچە)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"ئارىلاشتۇرغۇچ ئاۋاز بۆلىكى (-60 دىن -20dB غىچە)(ئاكتىپ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"ئارىلاشتۇرغۇچ ئاۋاز بۆلىكى (-20 دىن -9dB غىچە)(ئاكتىپ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"ئارىلاشتۇرغۇچ ئاۋاز بۆلىكى (-9 دىن -0dB غىچە)(ئاكتىپ)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"رەڭنىڭ ئالدىن تەڭشىكى\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"كۆڭۈلدىكى\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"ئىختىيارى\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"رەڭ قارىغۇسى ئورنىغا دەسسىتىدىغان لايىھە\"\nBasic.Settings.Advanced=\"ئالىي\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"ئەگەر ئاكتىپ چىقىرىش بولسا چېكىنگەندە ئاگاھلاندۇرىدۇ\"\nBasic.Settings.Advanced.General.ProcessPriority=\"جەريان ئالدىنلىقى\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"يۇقىرى\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"ئادەتتىكىدىن يۇقىرى\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"نورمال\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"ئادەتتىكىدىن تۆۋەن\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"بىكار\"\nBasic.Settings.Advanced.FormatWarning=\"ئاگاھلاندۇرۇش: NV12/P010 دىن باشقا رەڭ پىچىمى ئاساسلىقى سىن خاتىرىلەشكە ئىشلىتىلىدۇ، ئېقىم تارقىتىشقا ئىشلىتىش تەۋسىيە قىلىنمايدۇ. چۈنكى ئېقىم تارقاتقاندا رەڭ پىچىمىنى ئايلاندۇرۇش كېرەك بولغاچقا، CPU ئىشلىتىشنى ئاشۇرۇۋېتىشى مۇمكىن.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"ئاگاھلاندۇرۇش: يۇقىرى ئېنىقلىقتىدىكى پىچىمى ئادەتتە HDR رەڭ بوشلۇقى بىلەن ماسلاشتۇرۇپ ئىشلىتىلىدۇ.\"\nBasic.Settings.Advanced.FormatWarning2100=\"ئاگاھلاندۇرۇش: Rec. 2100 ئېنىقلىقى تېخىمۇ يۇقىرى بولغان پىچىمنى ئىشلىتىدۇ.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"رەڭ پىچىمى\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0، 2 تەكشىلىك)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 تەكشىلىك)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 تەكشىلىك)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 تەكشىلىك)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 تەكشىلىك)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 تەكشىلىك)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 تەكشىلىك)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"رەڭ بوشلۇقى\"\nBasic.Settings.Advanced.Video.ColorRange=\"رەڭ دائىرىسى\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"چەكلىك\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"لىق\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR ئاق دەرىجىسى\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR ئۆلچەملىك چوققا قىممەت دەرىجىسى\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"نازارەتچىلىك ئۈسكۈنىسى\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"كۆڭۈلدىكى\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Windows ئاۋاز تەستىسىنى چەكلەيدۇ (ئالاقە پائالىيىتى بايقالسا باشقا ئاۋازلارنىڭ ئۈنىنى پەسەيتىدۇ)\"\nBasic.Settings.Advanced.StreamDelay=\"ئېقىم كېچىكتۈرۈش\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"داۋاملىشىش ۋاقتى\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"قايتا باغلانغاندا ئۈزۈش نۇقتىسى (كېچىكىشنى ئاشۇرىدۇ)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"مۆلچەرلەنگەن ئەسلەك ئىشلىتىلىشى: %1MB\"\nBasic.Settings.Advanced.Network=\"تور\"\nBasic.Settings.Advanced.Network.Disabled=\"نۆۋەتتە تاللانغان ئېقىم كېلىشىمى تور تەڭشىكىنى ئۆزگەرتىشنى قوللىمايدۇ.\"\nBasic.Settings.Advanced.Network.BindToIP=\"IP غا باغلا\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP ئائىلىسى\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"تور ئەلالاشتۇرۇشنى قوزغىتىدۇ\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"TCP pacing نى قوزغىتىدۇ\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"يوللاش نىسبىتىنى تەڭشەش ئارقىلىق RTMP ئېقىمىنىڭ چىقىرىشى باشقا تور كېچىكىشىگە سەزگۈر ئەپلەرگە تېخىمۇ دوستانە بولىدۇ.\\nئانچە مۇقىم بولمىغان تور شارائىتىدا  كاندۇك چۈشۈپ قېلىش خەتىرىنى ئاشۇرۇشى مۇمكىن.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"تېزلەتمىگە ئائىت قىلمىش\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"تېزلەتمىنى ھەرگىز چەكلىمەيدۇ\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"ئاساسىي كۆزنەك فوكۇسلانغاندا تېزلەتمىنى چەكلەيدۇ\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"ئاساسىي كۆزنەك فوكۇسلانمىغاندا تېزلەتمىنى چەكلەيدۇ\"\nBasic.Settings.Advanced.AutoRemux=\"ئۆزلۈكىدىن %1 غا قايتا كودلايدۇ\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(mkv سۈپىتىدە خاتىرىلەيدۇ)\"\nBasic.AdvAudio=\"ئالىي ئۈن خاسلىقى\"\nBasic.AdvAudio.ActiveOnly=\"ئاكتىپ مەنبەلا\"\nBasic.AdvAudio.Name=\"ئاتى\"\nBasic.AdvAudio.Volume=\"ئاۋاز مىقدارى\"\nBasic.AdvAudio.VolumeSource=\"«%1» نىڭ ئاۋازى\"\nBasic.AdvAudio.Mono=\"يەككە يوللۇق ئاۋاز\"\nBasic.AdvAudio.MonoSource=\"«%1» يەككە ئاۋاز يولى پەسەيتىپ ئارىلاشتۇرۇش\"\nBasic.AdvAudio.Balance=\"تەڭپۇڭ\"\nBasic.AdvAudio.BalanceSource=\"«%1» نىڭ تەڭپۇڭلۇقى\"\nBasic.AdvAudio.SyncOffset=\"قەدەمداش چەتنىشى\"\nBasic.AdvAudio.SyncOffsetSource=\"«%1» نىڭ قەدەمداش چەتنىشى\"\nBasic.AdvAudio.Monitoring=\"ئۈن نازارەتچىلىك\"\nBasic.AdvAudio.Monitoring.None=\"نازارەتچىلىك تاقاق\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"نازارەتچىلىكلا (چىقىرىش ئۈنسىز)\"\nBasic.AdvAudio.Monitoring.Both=\"نازارەتچىلىك ۋە چىقىرىش\"\nBasic.AdvAudio.MonitoringSource=\"«%1» نىڭ ئۈن نازارەتچىلىكى\"\nBasic.AdvAudio.AudioTracks=\"نەغمىلەر\"\nBasic.Settings.Hotkeys=\"تېزلەتمە كۇنۇپكىلار\"\nBasic.Settings.Hotkeys.Pair=\"بىرىكمە كۇنۇپكا بىلەن «%1» ئوخشاش بولغاندا،  ئۈزچات سۈپىتىدە ئىشلىتىلىدۇ\"\nBasic.Settings.Hotkeys.Filter=\"سۈزگۈچ\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"تېزلەتمە بويىچە سۈزىدۇ\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"بۇ تېزلەتمىنى بىر ياكى بىر قانچە باشقا مەشغۇلات ئىشلىتىۋاتىدۇ، چېكىلسە توقۇنۇشنى كۆرسىتىدۇ\"\nBasic.Settings.Hotkeys.PleaseWait=\"تېزلەتمىنى يۈكلەۋاتىدۇ، سەل كۈتۈڭ…\"\nBasic.Hotkeys.SelectScene=\"كۆرۈنۈشكە ئالماشتۇرىدۇ\"\nBasic.SystemTray.Show=\"كۆرسەت\"\nBasic.SystemTray.Hide=\"يوشۇر\"\nBasic.SystemTray.Message.Reconnecting=\"ئۈزۈلدى. قايتا باغلاۋاتىدۇ…\"\nHotkeys.Insert=\"قىستۇر\"\nHotkeys.Delete=\"ئۆچۈر\"\nHotkeys.Home=\"باش بەت\"\nHotkeys.End=\"تامام\"\nHotkeys.PageUp=\"ئالدى بەت\"\nHotkeys.PageDown=\"كەينى بەت\"\nHotkeys.Print=\"بېسىپ چىقار\"\nHotkeys.Pause=\"ۋاقىتلىق توختا\"\nHotkeys.Left=\"سول\"\nHotkeys.Right=\"ئوڭ\"\nHotkeys.Up=\"ئۈستى\"\nHotkeys.Down=\"ئاستى\"\nHotkeys.Windows=\"كۆزنەكلەر\"\nHotkeys.Menu=\"تىزىملىك\"\nHotkeys.Space=\"بوشلۇق\"\nHotkeys.NumpadNum=\"سانتاختا %1\"\nHotkeys.NumpadMultiply=\"سانتاختا كۆپەيتىش\"\nHotkeys.NumpadDivide=\"سانتاختا بۆلۈش\"\nHotkeys.NumpadAdd=\"سانتاختا قوشۇش\"\nHotkeys.NumpadSubtract=\"سانتاختا ئېلىش\"\nHotkeys.NumpadDecimal=\"سانتاختا چېكىت\"\nHotkeys.AppleKeypadNum=\"%1 (كىچىك ھەرپتاختا)\"\nHotkeys.AppleKeypadMultiply=\"* (كىچىك ھەرپتاختا)\"\nHotkeys.AppleKeypadDivide=\"/ (كىچىك ھەرپتاختا)\"\nHotkeys.AppleKeypadAdd=\"+ (كىچىك ھەرپتاختا)\"\nHotkeys.AppleKeypadSubtract=\"- (كىچىك ھەرپتاختا)\"\nHotkeys.AppleKeypadDecimal=\". (كىچىك ھەرپتاختا)\"\nHotkeys.AppleKeypadEqual=\"= (كىچىك ھەرپتاختا)\"\nHotkeys.MouseButton=\"چاشقىنەك %1\"\nMute=\"ئۈنسىز\"\nUnmute=\"ئۈنلۈك\"\nPush-to-mute=\"بېسىلسا ئۈنسىز\"\nPush-to-talk=\"بېسىپ سۆزلىنىدۇ\"\nSceneItemShow=\"«%1» نى كۆرسىتىدۇ\"\nSceneItemHide=\"«%1»  نى يوشۇرىدۇ\"\nOutputWarnings.NoTracksSelected=\"كەم دېگەندە بىر يولنى تاللىشىڭىز كېرەك\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"چىقىرىش تەڭشەك خاتالىقى\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"بارلىق چىقىرىشنىڭ كەم دېگەندە بىر ئاۋاز يولى تاللىنىشى كېرەك.\"\nOutputWarnings.MP4Recording=\"ئاگاھلاندۇرۇش: ئەگەر ھۆججەتنى ئاخىرلاشتۇرالمىسا (مەسىلەن، كۆك ئېكران ياكى توك كېتىش قاتارلىق سەۋەبتىن) MP4/MOV غا خاتىرىلەپ ساقلانغان ھۆججەتنى ئەسلىگە كەلتۈرگىلى بولمايدۇ. ئەگەر كۆپ يوللۇق ئاۋازنى خاتىرىلىمەكچى بولسىڭىزMKV  ئىشلىتىشنى ئويلىشىڭ ھەمدە تاماملانغاندىن كېيىن خاتىرىنى قايتا كودلانغان MP4/MOV گە ئايلاندۇرۇڭ (ھۆججەت ← خاتىرىنى قايتا كودلا)\"\nOutputWarnings.CannotPause=\"ئاگاھلاندۇرۇش: ئەگەر سىن خاتىرىلەش كودلىغۇچىسى «ئېقىم كودلىغۇچ ئىشلەت» قىلىپ تەڭشەلگەن بولسا سىن خاتىرىلەشنى ۋاقىتلىق توختاتقىلى بولمايدۇ.\"\nOutputWarnings.CodecIncompatible=\"تاللانغان ئۈن ياكى سىن كودلىغۇچ ماسلاشمىغانلىقتىن ئەسلىگە قايتۇرۇلدى. تىزىمدىن ماسلىشىدىغان كودلىغۇچنى تاللاڭ.\"\nCodecCompat.Incompatible=\"(%1 بىلەن ماسلاشمايدۇ)\"\nCodecCompat.CodecPlaceholder=\"كودلىغۇچ تاللىنىدۇ…\"\nCodecCompat.ContainerPlaceholder=\"پىچىم تاللىنىدۇ…\"\nCodecCompat.CodecMissingOnExit.Title=\"ھېچقانداق كودلىغۇچ تاللانمىدى\"\nCodecCompat.CodecMissingOnExit.Text=\"كەم دېگەندە بىر سىن ياكى ئۈن كودلىغۇچ تەڭشەلمىگەن. خاتىرىلەش ۋە ئېقىم تارقىتىشقا كودلىغۇچ تاللانغانلىقىنى جەزملەڭ.\"\nCodecCompat.ContainerMissingOnExit.Title=\"ھېچقانداق پىچىم تاللانمىدى\"\nCodecCompat.ContainerMissingOnExit.Text=\"ھېچقانداق خاتىرىلەش پىچىمى تاللانمىغان. نۆۋەتتە تاللىغان ئېقىم كودلىغۇچ بىلەن ماسلىشىدىغان خاتىرىلەش پىچىمىدىن بىرنى تاللاڭ.\"\nFinalScene.Title=\"كۆرۈنۈش ئۆچۈر\"\nFinalScene.Text=\"كەم دېگەندە بىر كۆرۈنۈش بولۇشى كېرەك.\"\nNoSources.Title=\"مەنبە يوق\"\nNoSources.Text=\"قارىغاندا ھېچقانداق سىن مەنبەسىنى تاللىمىغاندەك قىلىسىز، شۇڭلاشقا قارا ئېكرانلا چىقىدۇ. راستتىنلا شۇنداق قىلامسىز؟\"\nNoSources.Text.AddSource=\"ئاساسىي كۆزنەكتىكى مەنبە رامكىسىنىڭ ئاستىدىكى + سىنبەلگىسىنى چېكىپ خالىغان ۋاقىتتا مەنبە قوشالايسىز.\"\nNoSources.Label=\"ھېچقانداق مەنبە قوشمىدىڭىز.\\nتۆۋەندىكى + توپچىنى چېكىڭ\\nياكى چاشقاندا بۇ جاينى ئوڭ چېكىپ بىرنى قوشۇڭ.\"\nChangeBG=\"رەڭ تەڭشەك\"\nCustomColor=\"ئىختىيارى رەڭ\"\nBrowserSource.EnableHardwareAcceleration=\"توركۆرگۈ مەنبەسى قاتتىق دېتال تېزلەشتۈرگۈچنى قوزغىتىدۇ\"\nAbout=\"ھەققىدە\"\nAbout.Info=\"OBS Studio سىنغا ئالىدىغان ۋە شۇئان ئېقىم تارقىتىدىغان ئەركىن ۋە ئوچۇق كودلۇق يۇمشاق دېتال.\"\nAbout.Donate=\"تۆھپە قوشۇڭ\"\nAbout.GetInvolved=\"قاتناشقان\"\nAbout.Authors=\"يازغۇچىلار\"\nAbout.License=\"ئىجازەتنامە\"\nAbout.Error=\"خاتالىق! ھۆججەتنى ئوقۇيالمايدۇ.\\n\\nيۆتكىلىدۇ: %1\"\nAbout.Contribute=\"OBS قۇرۇلۇشىنى قوللاش\"\nAddUrl.Title=\"تور ئادرېسى ئارقىلىق مەنبە قوشىدۇ\"\nAddUrl.Text=\"تور ئادرېسىنى OBS كە سۆرىدىڭىز. ئۇلانمىنى ئۆزلۈكىدىن مەنبە قىلىپ قوشىدۇ. داۋاملاشتۇرامدۇ؟\"\nAddUrl.Text.Url=\"تور ئادرېسى: %1\"\nResizeOutputSizeOfSource=\"چىقىرىش چوڭلۇقىنى تەڭشە (مەنبە چوڭلۇقى)\"\nResizeOutputSizeOfSource.Text=\"ئاساس ۋە چىقىرىش ئېنىقلىقى نۆۋەتتىكى مەنبەنىڭ چوڭلۇقىغا تەڭشىلىدۇ.\"\nResizeOutputSizeOfSource.Continue=\"داۋاملاشتۇرامسىز؟\"\nPreviewTransition=\"ئالمىشىشنى ئالدىن كۆزىتىدۇ\"\nImporter=\"كۆرۈنۈش توپلىمى ئەكىرگۈچ\"\nImporter.SelectCollection=\"كۆرۈنۈش توپلىمى تاللىنىدۇ\"\nImporter.Collection=\"كۆرۈنۈش توپلىمى\"\nImporter.HelpText=\"ھۆججەتنى مەزكۇر كۆزنەككە قوشۇش ئارقىلىق OBS ياكى باشقا قوللايدىغان پىروگراممىدىن توپلامنى ئەكىرىدۇ.\"\nImporter.Path=\"توپلام يولى\"\nImporter.Program=\"بايقىغان پىروگرامما\"\nImporter.AutomaticCollectionPrompt=\"كۆرۈنۈش توپلىمىنى ئۆزلۈكىدىن ئىزدەيدۇ\"\nImporter.AutomaticCollectionText=\"OBS قوللايدىغان ئۈچىنچى تەرەپ پىروگراممىدىن ئەكىرگىلى بولىدىغان كۆرۈنۈش توپلىمىنى ئۆزلۈكىدىن ئىزدەپ تاپىدۇ. OBS نىڭ ئۆزلۈكىدىن توپلامنى ئىزدىشىنى خالامسىز؟\\n\\nكېيىن تەڭشەك › ئادەتتىكى › ئەكىرگۈچنى تاللاپ ئۆزگەرتەلەيسىز.\"\nImporter.SelectFile=\"كۆز يۈگۈرت...\"\nRestart=\"قايتا قوزغات\"\nNeedsRestart=\"OBS Studio نى قايتا قوزغىتىش كېرەك. ھازىر قايتا قوزغىتامسىز؟\"\nLoadProfileNeedsRestart=\"تەرجىمىھالدا OBS نى قايتا قوزغىتىش كېرەك بولغان تەڭشەك بار.\\n%1\\n\\nبۇ تەڭشەكلەرنىڭ كۈچكە ئىگە بولۇشى ئۈچۈن OBS نى قايتا قوزغىتامسىز؟\"\nContextBar.NoSelectedSource=\"مەنبە تاللانمىغان\"\nContextBar.MediaControls.PlayMedia=\"ۋاسىتە قوي\"\nContextBar.MediaControls.PauseMedia=\"ۋاسىتە ۋاقىتلىق توختات\"\nContextBar.MediaControls.StopMedia=\"ۋاسىتە توختات\"\nContextBar.MediaControls.RestartMedia=\"ۋاسىتە قايتا قوزغات\"\nContextBar.MediaControls.PlaylistNext=\"قويۇش تىزىمىدىكى كېيىنكىسى\"\nContextBar.MediaControls.PlaylistPrevious=\"قويۇش تىزىمىدىكى ئالدىنقىسى\"\nContextBar.MediaControls.BlindSeek=\"ۋاسىتە ئىزدەش ئەپچە\"\nYouTube.Auth.Ok=\"سالاھىيەت دەلىللەش مۇۋەپپەقىيەتلىك تاماملادى.\\nھازىر بۇ بەتنى تاقىسىڭىز بولىدۇ.\"\nYouTube.Auth.NoCode=\"سالاھىيەت دەلىللەش جەريانى تاماملانمىغان.\"\nYouTube.Auth.NoChannels=\"تاللانغان ھېساباتتا ھېچقانداق قانال يوق\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube ئىشلەتكۈچى سالاھىيەت دەلىللەش\"\nYouTube.Auth.WaitingAuth.Text=\"سىرتقى توركۆرگۈڭىزدە سالاھىيەت دەلىللەشنى تاماملاڭ.<br>ئەگەر سىرتقى توركۆرگۈنى ئاچقىلى بولمىسا، بۇ ئۇلانمىغا ئەگىشىپ سالاھىيەت دەلىللەڭ:<br>%1\"\nYouTube.AuthError.Text=\"قانال ئۇچۇرىغا ئېرىشەلمىدى: %1.\"\nYouTube.Actions.WindowTitle=\"YouTube تارقاتقۇ تەڭشىكى - قانال: %1\"\nYouTube.Actions.CreateNewEvent=\"يېڭى تارقاتقۇ قۇر\"\nYouTube.Actions.ChooseEvent=\"مەۋجۇت تارقاتقۇ تاللىنىدۇ\"\nYouTube.Actions.Title=\"ماۋزۇ*\"\nYouTube.Actions.MyBroadcast=\"تارقاتقۇم\"\nYouTube.Actions.Description=\"چۈشەندۈرۈش\"\nYouTube.Actions.Privacy=\"شەخسىيەت*\"\nYouTube.Actions.Privacy.Private=\"شەخسىي\"\nYouTube.Actions.Privacy.Public=\"ھەممىگە\"\nYouTube.Actions.Privacy.Unlisted=\"ئاشكارىلانمىغان\"\nYouTube.Actions.Category=\"سەھىپە\"\nYouTube.Actions.Thumbnail=\"كىچىك سۈرەت\"\nYouTube.Actions.Thumbnail.SelectFile=\"ھۆججەت تاللا…\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"تاللانمىدى\"\nYouTube.Actions.Thumbnail.ClearFile=\"تازىلا\"\nYouTube.Actions.MadeForKids=\"بۇ سىن بالىلار ئۈچۈن تەييارلانغانمۇ؟\"\nYouTube.Actions.MadeForKids.Yes=\"ھەئە، بالىلار ئۈچۈن تەييارلانغان\"\nYouTube.Actions.MadeForKids.No=\"ياق، بالىلار ئۈچۈن تەييارلانمىغان\"\nYouTube.Actions.AdditionalSettings=\"قوشۇمچە تەڭشەكلەر\"\nYouTube.Actions.Latency=\"كېچىكىش\"\nYouTube.Actions.Latency.Normal=\"نورمال\"\nYouTube.Actions.Latency.Low=\"تۆۋەن\"\nYouTube.Actions.Latency.UltraLow=\"دەرىجىدىن تاشقىرى تۆۋەن\"\nYouTube.Actions.EnableAutoStart=\"ئۆزلۈكىدىن باشلاشنى قوزغىتىدۇ\"\nYouTube.Actions.EnableAutoStop=\"ئۆزلۈكىدىن توختىتىشنى قوزغىتىدۇ\"\nYouTube.Actions.AutoStartStop.TT=\"كۈنتەرتىپتىكى تارقاتقۇنىڭ ئۆزلۈكىدىن باشلىنىدىغان ياكى باشلانمايدىغانلىقىنى بىلدۈرىدۇ\"\nYouTube.Actions.EnableDVR=\"DVR نى قوزغات\"\nYouTube.Actions.360Video=\"360 سىن\"\nYouTube.Actions.ScheduleForLater=\"سەل تۇرۇپ كۈنتەرتىپلىنىدۇ\"\nYouTube.Actions.RememberSettings=\"بۇ تەڭشەكنى ئەستە تۇت\"\nYouTube.Actions.Create_Ready=\"تارقاتقۇ قۇر\"\nYouTube.Actions.Create_GoLive=\"تارقاتقۇ قۇرۇپ ئېقىم تارقىتىشنى باشلايدۇ\"\nYouTube.Actions.Choose_Ready=\"تارقاتقۇ تاللىنىدۇ\"\nYouTube.Actions.Choose_GoLive=\"تارقاتقۇ تاللىنىپ ئېقىم تارقىتىشنى باشلايدۇ\"\nYouTube.Actions.Create_Schedule=\"تارقاتقۇ كۈنتەرتىپلىنىدۇ\"\nYouTube.Actions.Create_Schedule_Ready=\"تارقاتقۇ تاللىنىپ كۈنتەرتىپلىنىدۇ\"\nYouTube.Actions.Dashboard=\"YouTube Studio نى ئاچ\"\nYouTube.Actions.Error.Title=\"شۇئان تارقاتقۇ قۇرۇش خاتالىقى\"\nYouTube.Actions.Error.Text=\"YouTube نى زىيارەت قىلغاندا خاتالىق كۆرۈلدى «%1».<br/>خاتالىقنىڭ تەپسىلىي چۈشەندۈرۈشىنى <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> دىن تاپالايسىز\"\nYouTube.Actions.Error.General=\"YouTube نى زىيارەت قىلغاندا خاتالىق كۆرۈلدى. تور باغلىنىشىڭىز ياكى YouTube مۇلازىمىتىنى زىيارەت قىلالايدىغانلىقىڭىزنى تەكشۈرۈڭ.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"تارقاتقۇ قۇرۇش خاتالىقى «%1».<br/>خاتالىقنىڭ تەپسىلىي چۈشەندۈرۈشىنى <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a> دىن تاپالايسىز\"\nYouTube.Actions.Error.NoStreamCreated=\"ھېچقانداق ئېقىم تارقىتىش قۇرۇلمىدى. ھېساباتىڭىزغا قايتا باغلاڭ.\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API خاتالىقى. تەپسىلاتىنى خاتىرە ھۆججەتتىن كۆرۈڭ.\"\nYouTube.Actions.Error.BroadcastNotFound=\"تاللانغان تارقاتقۇ تېپىلمىدى.\"\nYouTube.Actions.Error.FileMissing=\"تاللانغان ھۆججەت مەۋجۇت ئەمەس.\"\nYouTube.Actions.Error.FileOpeningFailed=\"تاللانغان ھۆججەتنى ئاچالمىدى.\"\nYouTube.Actions.Error.FileTooLarge=\"تاللانغان ھۆججەت بەك چوڭ (چېكى: 2MiB)\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"تارقاتقۇنىڭ كۆرۈنۈشىنى ئالماشتۇرالمىدى: %1<br/><br/>ئەگەر خاتالىق يەنىلا مەۋجۇت بولسا، <a href='https://studio.youtube.com/video/%2/livestreaming'>YouTube Studio دا تارقاتقۇنى ئېچىپ</a> ئاندىن قولدا سىناڭ.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"تارقاتقۇ سىناق باسقۇچىغا ئالمىشىۋاتىدۇ، بۇنىڭغا ئازراق ۋاقىت كېتىشى مۇمكىن. 10-30 سېكۇنت كۈتۈپ ئاندىن قايتا سىناڭ.\"\nYouTube.Actions.EventsLoading=\"پائالىيەت تىزىمىنى يۈكلەۋاتىدۇ…\"\nYouTube.Actions.EventCreated.Title=\"پائالىيەت قۇرۇلدى\"\nYouTube.Actions.EventCreated.Text=\"پائالىيەت مۇۋەپپەقىيەتلىك قۇرۇلدى.\"\nYouTube.Actions.Stream=\"سانلىق مەلۇمات ئېقىمى\"\nYouTube.Actions.Stream.ScheduledFor=\"%1 كۈنتەرتىپلەندى\"\nYouTube.Actions.Stream.Resume=\"ئۈزۈلۈپ قالغان ئېقىمنى ئەسلىگە قايتۇرىدۇ\"\nYouTube.Actions.Stream.YTStudio=\"YouTube Studio ئۆزلۈكىدىن قۇردى\"\nYouTube.Actions.Notify.CreatingBroadcast=\"يېڭى شۇئان تارقاتقۇ قۇرۇۋاتىدۇ، سەل كۈتۈڭ…\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"قولدا قايتا باشلاش كېرەك\"\nYouTube.Actions.AutoStartStreamingWarning=\"بۇ پائالىيەت ئۆزلۈكىدىن باشلاشنى چەكلىدى، «شۇئان تارقىتىشقا يۆتكەل» نى چېكىپ تارقاتقۇنى باشلاڭ.\"\nYouTube.Actions.AutoStopStreamingWarning=\"قايتا باغلىنالمايسىز.<br>ئىقىم تارقىتىشىڭىز توختاپ شۇئان ئېقىم تارقالمايدۇ.\"\nYouTube.Errors.liveStreamingNotEnabled=\"تاللانغان YouTube قانالدا شۇئان ئېقىم تارقىتىش قوزغىتىلمىغان. <br/><br/><a href='https://www.youtube.com/features'>youtube.com/features</a> نى زىيارەت قىلىپ تېخىمۇ كۆپ ئۇچۇرغا ئېرىشكىلى بولىدۇ.\"\nYouTube.Errors.livePermissionBlocked=\"تاللانغان YouTube قانالدا شۇئان ئېقىم تارقىتىشنى ئىشلەتكىلى بولمايدۇ.<br/>دىققەت، قانال تەڭشىكىڭىزدە شۇئان ئېقىم تارقىتىش ئىقتىدارىنى قوزغىتىپ 24 سائەتتىن كېين ئاندىن ئىشلىتەلەيسىز.<br/><br/>تەپسىلاتىنى  <a href='https://www.youtube.com/features'>youtube.com/features</a> دىن كۆرۈڭ.\"\nYouTube.Errors.errorExecutingTransition=\"ئارقا ئۇچ خاتالىقى تۈپەيلىدىن ئالمىشىش ئۈنۈمىنى كۆرسىتەلمىدى. بىر قانچە سېكۇنتتىن كېيىن قايتا سىناڭ.\"\nYouTube.Errors.errorStreamInactive=\"YouTube ئېقىم تارقىتىش سانلىق مەلۇماتىڭىزنى تاپشۇرۇۋالمىدى. سەپلىمىڭىزنى تەكشۈرۈپ ئاندىن قايتا سىناڭ.\"\nYouTube.Errors.invalidTransition=\"سىنىماقچى بولغان سەھنە ئالمىشىش ئىناۋەتسىز. بۇ ئالدىنقى قېتىملىق سەھنە ئالمىشىش ئاخىرلاشمىغانلىقتىن بولۇشى مۇمكىن. بىر قانچە سېكۇنتتىن كېيىن قايتا سىناڭ.\"\nYouTube.DocksRemoval.Title=\"كونا YouTube توركۆرگۈ لەڭگەرنى تازىلا\"\nYouTube.DocksRemoval.Text=\"بۇ توركۆرگۈ لەڭگىرى ئەمدى ئىشلىتىلمىگەچكە چىقىرىۋېتىلدى:\\n\\n%1\\nئورنىغا «لەڭگەر/YouTube شۇئان تارقىتىشنى تىزگىنلەش ئۆيى»نى ئىشلىتىڭ.\"\nConfigDownload.WarningMessageTitle=\"ئاگاھلاندۇرۇش\"\nFailedToStartStream.MissingConfigURL=\"نۆۋەتتىكى مۇلازىمەتكە ئىشلەتكىلى بولىدىغان سەپلىمە تور ئادرېسى يوق\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"ئىختىيارى RTMP تور ئادرېسى بەلگىلەنمىگەن\"\nFailedToStartStream.InvalidCustomConfig=\"ئىختىيارى سەپلىمە ئىناۋەتسىز\"\nFailedToStartStream.MissingCanvas=\"سەپلەشچان ئارتۇق كەرگە يوقالغان\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"كۆپ يوللۇق سىن مۇلازىمىتىنى قۇرالمىدى\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"كۆپ يوللۇق سىن RTMP چىقىرىشنى قۇرالمىدى\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC نى ئىشلەتكىلى بولمايدۇ.\\n\\nكودلىغۇچ تۈرى «%1» نى تاپالمىدى\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"«%1» سىن كودلىغۇچنى قۇرالمىدى (تۈرى: «%2»)\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"«%1» كودلىغۇچ قۇرۇۋاتقاندا OBS سىن ئۇچۇرىغا ئېرىشەلمىدى (تۈرى: «%2»)\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"ئۈن كودلىغۇچ قۇرالمىدى\"\nFailedToStartStream.NoRTMPURLInConfig=\"سەپلىمە ئېقىم نىشان RTMP تور ئادرېسىنى ئۆز ئىچىگە ئالمايدۇ\"\nFailedToStartStream.FallbackToDefault=\"ئېقىم تارقىتىشنى %1 ئارقىلىق باشلىيالمىدى؛ يەككە كودلاش تەڭشىكىنى ئىشلىتىپ قايتا سىنامسىز؟\"\nFailedToStartStream.ConfigRequestFailed=\"%1 دىن سەپلىمىگە ئېرىشەلمىدى<br><br>HTTP خاتالىقى: %2\"\nFailedToStartStream.WarningUnknownStatus=\"يوچۇن ھالەت قىممىتى «%1» نى تاپشۇرۇۋالدى\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\n%1 نى ئىشلەتمەي ئېقىم تارقىتىشنى داۋاملاشتۇرامسىز؟\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nئېقىم تارقىتىشنى داۋاملاشتۇرامسىز؟\"\nFailedToStartStream.MissingEncoderConfigs=\"شۇئان تارقىتىشقا يۆتكەل كودلىغۇچ سەپلىمىسىنى ئۆز ئىچىگە ئالمايدۇ\"\nFailedToStartStream.InvalidEncoderConfig=\"شۇئان تارقىتىش سەپلىمىسى ئىناۋەتسىز كودلىغۇچ سەپلىمىسىنى ئۆز ئىچىگە ئالىدۇ\"\nFailedToStartStream.StatusMissingHTML=\"شۇئان تارقىتىشقا يۆتكەل ئىلتىماسى بەلگىلەنمىگەن خاتالىق قايتۇردى.\"\nFailedToStartStream.NoConfigSupplied=\"سەپلىمە كەم\"\nMultitrackVideo.Info=\"%1 تەڭشىكىڭىزنى ئۆزلۈكىدىن ئەلالاشتۇرۇپ كودلاپ كۆپ خىل سىن سۈپىتىدە يوللايدۇ. بۇ تاللانما تاللانسا كومپيۇتېرىڭىز ۋە يۇمشاق دېتال سەپلىمىسىنىڭ %2 ئۇچۇرىنى يوللايدۇ.\"\nMultitrackVideo.IncompatibleSettings.Title=\"ماسلاشمايدىغان تەڭشەك\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 نۆۋەتتە تۆۋەندىكى تەڭشەك بىلەن ماسلاشمايدۇ: \\n\\n%2\\nئەگەر %1 بىلەن ئېقىم تارقىتىشنى داۋاملاشتۇرماقچى بولسىڭىز، ماسلاشمايدىغان تەڭشەكنى چەكلەڭ: \\n\\n%3\\nئاندىن ئېقىم تارقىتىشنى قايتا باشلاڭ.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"بۇ ئېقىم تارقىتىشنى چەكلەپ ئاندىن ئېقىم تارقىتىشنى باشلا\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"تەڭشەكنى يېڭىلاپ ئېقىم تارقىتىشنى باشلا\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 نۆۋەتتىكى تەڭشىكى «%2»، «%3» نىڭ [ئۈن ← ئادەتتىكى ← قانال] بىلەن ماسلاشمايدۇ\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[ئۈن ← ئادەتتىكى ← قانال] نى «%1» غا تەڭشەك كېرەك\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 [ئۈن ← ئادەتتىكى ← قانال] غا كۆپ خىل پەرقلىق تەڭشەك كېرەك\"\nBasic.OpenPluginManager=\"قىستۇرما باشقۇرغۇچ\"\nPluginManager=\"قىستۇرما باشقۇرغۇچ\"\nPluginManager.Restart=\"OBS Studio قايتا قوزغىتىلسا ئاندىن قىستۇرمىنىڭ ئۆزگىرىشىنى قوللىنىدۇ\"\nPluginManager.MissingPlugin=\"[قىستۇرما تېپىلمىدى]\"\nPluginManager.Section.Discover=\"كۆز يۈگۈرت\"\nPluginManager.Section.Manage=\"ئورنىتىلغان\"\nPluginManager.Section.Updates=\"يېڭىلانما\"\nPluginManager.Section.Manage.Title=\"قوزغىتىلغان قىستۇرمىنى باشقۇرۇش\"\n"
  },
  {
    "path": "frontend/data/locale/uk-UA.ini",
    "content": "Language=\"Українська\"\nOK=\"Гаразд\"\nApply=\"Застосувати\"\nCancel=\"Скасувати\"\nClose=\"Закрити\"\nSave=\"Зберегти\"\nDiscard=\"Відхилити\"\nDisable=\"Вимкнено\"\nYes=\"Так\"\nNo=\"Ні\"\nAdd=\"Додати\"\nRemove=\"Вилучити\"\nRename=\"Перейменувати\"\nInteract=\"Взаємодіяти\"\nFilters=\"Фільтри\"\nProperties=\"Властивості\"\nMoveUp=\"Вище\"\nMoveDown=\"Нижче\"\nSettings=\"Налаштування\"\nDisplay=\"Екран\"\nName=\"Назва\"\nExit=\"Вихід\"\nMixer=\"Мікшер аудіо\"\nBrowse=\"Огляд\"\nMono=\"Моно\"\nStereo=\"Стерео\"\nDroppedFrames=\"Пропущено кадрів: %1 (%2%)\"\nProjector.Open.Program=\"Відкрити проєктор програми\"\nProjector.Open.Preview=\"Відкрити проєктор перегляду\"\nProjector.Open.Scene=\"Відкрити проєктор сцени\"\nProjector.Open.Source=\"Відкрити проєктор джерела\"\nProjector.Open.Multiview=\"Відкрити мультиперегляд\"\nProjector.Display=\"Дисплей: %1\"\nProjector.Window=\"Нове вікно\"\nProjector.Title=\"Проєктор\"\nProjector.Title.Scene=\"Сцена: %1\"\nProjector.Title.Source=\"Джерело: %1\"\nProjector.Title.Multiview=\"Багатоекранний перегляд\"\nProjector.ResizeWindowToContent=\"Підігнати вікно до вмісту\"\nClear=\"Очистити\"\nRevert=\"Повернути\"\nShow=\"Показати\"\nHide=\"Сховати\"\nUnhideAll=\"Показувати всі приховані\"\nUntitled=\"Без назви\"\nNew=\"Створити\"\nDuplicate=\"Дублювати\"\nEnable=\"Увімкнути\"\nDisableOSXVSync=\"Вимкнути macOS V-Sync\"\nResetOSXVSyncOnExit=\"Відновити macOS V-Sync під час виходу\"\nHighResourceUsage=\"Кодування перевантажено! Треба знизити вимоги до налаштування відео або спробувати швидші налаштування енкодера.\"\nTransition=\"Монтажний перехід\"\nQuickTransitions=\"Швидкі монтажні переходи\"\nFadeToBlack=\"Поступове затемнення\"\nLeft=\"Ліворуч\"\nRight=\"Праворуч\"\nTop=\"Зверху\"\nBottom=\"Знизу\"\nReset=\"Скинути\"\nHours=\"год.\"\nMinutes=\"хв.\"\nSeconds=\"с\"\nDeprecated=\"Застарілі\"\nReplayBuffer=\"Буфер повторів\"\nImport=\"Імпорт\"\nExport=\"Експорт\"\nCopy=\"Копіювати\"\nPaste=\"Вставити\"\nPasteReference=\"Вставити (посилання)\"\nPasteDuplicate=\"Вставити (дублікат)\"\nRemuxRecordings=\"Ремультиплексування записів\"\nNext=\"Далі\"\nBack=\"Назад\"\nDefaults=\"За замовчуванням\"\nRestoreDefaults=\"Типові\"\nHideMixer=\"Приховати у мікшері\"\nTransitionOverride=\"Перевизначення монтажного переходу\"\nShowTransition=\"Перехід при показі джерела\"\nHideTransition=\"Перехід при приховуванні джерела\"\nNone=\"Немає\"\nStudioMode.Preview=\"Попередній перегляд\"\nStudioMode.Program=\"Програма\"\nStudioMode.PreviewSceneName=\"Попередній перегляд: %1\"\nStudioMode.ProgramSceneName=\"Програма: %1\"\nShowInMultiview=\"Показувати у багатоекранному перегляді\"\nVerticalLayout=\"Вертикальне компонування\"\nGroup=\"Група\"\nDoNotShowAgain=\"Більше не показувати\"\nDefault=\"(Типово)\"\nCalculating=\"Обчислення…\"\nFullscreen=\"На весь екран\"\nWindowed=\"У вікні\"\nRefreshBrowser=\"Оновити\"\nAspectRatio=\"Пропорції <b>%1:%2</b>\"\nLockVolume=\"Зафіксувати гучність\"\nLogViewer=\"Перегляд журналу\"\nShowOnStartup=\"Показувати під час запуску\"\nOpenFile=\"Відкрити файл\"\nAddScene=\"Додати сцену\"\nAddSource=\"Додати джерело\"\nRemoveScene=\"Вилучити вибрану сцену\"\nRemoveSource=\"Вилучити вибрані джерела\"\nMoveSceneUp=\"Перемістити сцену вгору\"\nMoveSceneDown=\"Перемістити сцену вниз\"\nMoveSourceUp=\"Перемістити джерела вгору\"\nMoveSourceDown=\"Перемістити джерела вниз\"\nSourceProperties=\"Відкрити властивості джерела\"\nSourceFilters=\"Відкрити фільтри джерела\"\nMixerToolbarMenu=\"Меню аудіомікшера\"\nSceneFilters=\"Відкрити фільтри сцени\"\nList=\"Список\"\nGrid=\"Сітка\"\nAutomatic=\"Автоматично\"\nComingSoon=\"Незабаром\"\nPluginsFailedToLoad.Title=\"Помилка завантаження плагіна\"\nPluginsFailedToLoad.Text=\"Не вдалося завантажити такі плагіни OBS:\\n\\n%1\\nОновіть або вилучіть ці плагіни.\"\nAlreadyRunning.Title=\"OBS уже запущено\"\nAlreadyRunning.Text=\"OBS вже запущено! Якщо ви дійсно намагаєтесь це зробити – перед тим як запустити нову копію, спершу закрийте всі відкриті екземпляри OBS. Якщо OBS налаштовано згортатися в трей, перевірте чи він там і досі працює.\"\nAlreadyRunning.LaunchAnyway=\"Все одно запустити\"\nCrashHandling.Dialog.Title=\"Виявлено збій OBS Studio\"\nCrashHandling.Labels.Text=\"OBS Studio закінчила роботу неправильно.\\n\\nЗапущено в безпечному режимі (вимкнено сторонні плагіни, скрипти та вебсокети)?\"\nCrashHandling.Labels.PrivacyNotice=\"Ви також можете автоматично вивантажити останній звіт про збій на OBSProject.<br /><br />Перед вивантаженням файлів ознайомтеся з <a href='https://obsproject.com/privacy-policy'>політикою приватності</a>, звернувши особливу увагу на частини, що стосуються вивантаження файлів.\"\nCrashHandling.Checkbox.SendReport=\"Я ознайомився з політикою приватності та даю згоду на вивантаження.\"\nCrashHandling.Buttons.LaunchSafe=\"Запустити в безпечному режимі\"\nCrashHandling.Buttons.LaunchNormal=\"Запустити у звичайному режимі\"\nCrashHandling.Errors.UploadJSONError=\"Під час спроби вивантажити найновіший журнал збоїв сталася помилка. Спробуйте ще раз пізніше.\"\nCrashHandling.Errors.Title=\"Помилка вивантаження звіту про збій\"\nSafeMode.Restart=\"Ви хочете перезапустити OBS у безпечному режимі (вимкнувши сторонні плагіни, скрипти та вебсокети)?\"\nSafeMode.RestartNormal=\"Ви хочете перезапустити OBS у звичайному режимі?\"\nChromeOS.Title=\"Непідтримувана платформа\"\nChromeOS.Text=\"Схоже, OBS запущено всередині контейнера ChromeOS. Ця платформа не підтримується.\"\nWine.Title=\"Виявлено Wine\"\nWine.Text=\"Роботу OBS у Wine не підтримується, і багато функцій, як-от захоплення або джерела з пристроїв не працюватимуть або працюватимуть лише обмежено.<br><br>Радимо запустити нативну версію OBS, наприклад, <a href='https://flathub.org/apps/details/com.obsproject.Studio'>нашу Flatpak-версію</a> або з пакунка вашої операційної системи.\"\nDockCloseWarning.Title=\"Закриття панелі інтерфейсу\"\nDockCloseWarning.Text=\"Ви щойно закрили одну з панелей інтерфейсу. Якщо ви бажаєте її повернути – скористайтеся опцією в головному меню Панелі інтерфейсу.\"\nExtraBrowsers=\"Користувацькі панелі браузера\"\nExtraBrowsers.Info=\"Додавайте панелі вказавши назву та URL-адреси, потім натисніть «Застосувати» або «Закрити», щоб відкрити щойно додані панелі. Список панелей можна редагувати.\"\nExtraBrowsers.DockName=\"Назва панелі\"\nAuth.Authing.Title=\"Автентифікація…\"\nAuth.Authing.Text=\"Автентифікація на %1, зачекайте, будь ласка…\"\nAuth.AuthFailure.Title=\"Помилка автентифікації\"\nAuth.AuthFailure.Text=\"Помилка автентифікації на %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Потрібна автентифікація\"\nAuth.InvalidScope.Text=\"Вимоги до перевірки автентичності з %1 змінилися. Деякі функції можуть бути недоступними.\"\nAuth.LoadingChannel.Title=\"Завантаження інформації про канал…\"\nAuth.LoadingChannel.Text=\"Завантажується інформація про канал з %1, будь ласка, зачекайте…\"\nAuth.LoadingChannel.Error=\"Не вдалося отримати дані про канал.\"\nAuth.ChannelFailure.Title=\"Не вдалося завантажити канал\"\nAuth.ChannelFailure.Text=\"Не вдалося завантажити інформацію про канал з %1\\n\\n%2: %3\"\nAuth.Chat=\"Чат\"\nAuth.StreamInfo=\"Інформація про трансляцію\"\nTwitchAuth.Stats=\"Статистика Twitch\"\nTwitchAuth.Feed=\"Стрічка активності Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Не вдалося запросити ключ трансляції\"\nTwitchAuth.TwoFactorFail.Text=\"OBS не змогла під'єднатися до вашого облікового запису Twitch. Будь ласка, переконайтеся, що у <a href='https://www.twitch.tv/settings/security'>налаштуваннях безпеки Twitch</a> встановлено двофакторну автентифікацію, бо це необхідно для здійснення трансляцій.\"\nRestreamAuth.Channels=\"Restream канали\"\nCopy.Filters=\"Копіювати фільтри\"\nPaste.Filters=\"Вставити фільтри\"\nBrowserPanelInit.Title=\"Ініціалізація браузера…\"\nBrowserPanelInit.Text=\"Триває ініціалізація браузера, зачекайте...\"\nBandwidthTest.Region=\"Регіон\"\nBandwidthTest.Region.US=\"США\"\nBandwidthTest.Region.EU=\"Європа\"\nBandwidthTest.Region.Asia=\"Азія\"\nBandwidthTest.Region.Other=\"Інший\"\nBasic.AutoConfig=\"Майстер з автоналаштування\"\nBasic.AutoConfig.ApplySettings=\"Застосувати налаштування\"\nBasic.AutoConfig.StartPage=\"Інформація про використання\"\nBasic.AutoConfig.StartPage.SubTitle=\"Вкажіть, для чого ви будете використовувати програму\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Оптимізувати для трансляцій, записування на другому плані\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Оптимізувати тільки для запису, трансляції мене не хвилюють\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Я використовуватиму тільки віртуальну камеру\"\nBasic.AutoConfig.VideoPage=\"Налаштування відео\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Вкажіть необхідні налаштування відео\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Поточна (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Екран %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Поточна (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 або 30, але краще 60, коли це можливо\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 або 30, але краще висока роздільна здатність\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Примітка: роздільна здатність полотна (базова), не обов'язково повинна дорівнювати роздільній здатності з якою ви будете транслювати або записувати. Фактичну роздільну здатність трансляції/запису можна зменшити в порівнянні з полотном, це потрібно для зменшення використання ресурсів або зменшення бітрейту.\"\nBasic.AutoConfig.StreamPage=\"Інформація про трансляцію\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Введіть інформацію про трансляцію\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Під'єднати обліковий запис (рекомендовано)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Від'єднати обліковий запис\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Від'єднати обліковий запис?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Ці зміни застосуються негайно. Справді від'єднати обліковий запис?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Отримати ключ трансляції\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Докладніше\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Використати ключ трансляції\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Використати ключ трансляції (розширено)\"\nBasic.AutoConfig.StreamPage.Service=\"Сервіс\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Показати всі…\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Користувацький…\"\nBasic.AutoConfig.StreamPage.Server=\"Сервер\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Ключ трансляції\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: введіть пароль для шифрування.\\nRTMP: введіть ключ, наданий службою.\\nSRT: введіть ідентифікатор трансляції, якщо служба його використовує.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Ключ кодувальника\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Токен автентифікації\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Підключений обліковий запис\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Оцінити бітрейт за допомогою тесту пропускної здатності (може зайняти кілька хвилин)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Віддавати перевагу апаратному кодуванню\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Апаратне кодування зменшує використання ЦП, але для отримання такої ж якості відео може знадобитись більший бітрейт.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Попередження трансляції\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Тест пропускної здатності буде транслювати випадкові відеодані без аудіо на ваш канал. Якщо ви можете, рекомендуємо тимчасово вимкнути збереження відео трансляцій та перемкнути трансляцію на приватну до завершення тесту. Продовжити?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Тестове %1\"\nBasic.AutoConfig.TestPage=\"Остаточні результати\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Зараз програма виконує набір тестів для оцінки найкращих налаштувань\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Тестування завершено\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Виконується тестування пропускної здатності, це може зайняти кілька хвилин…\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Не знайдено виводу для протоколу цієї служби\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Підключення до: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Не вдалося під'єднатися до жодного з серверів, будь ласка, перевірте підключення до Інтернету і спробуйте знову.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Тестування пропускної здатності для: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Тестування кодувальника для трансляції, це може зайняти хвилину…\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Тестування кодувальника для запису, це може зайняти хвилину…\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Не вдалося запустити кодувальник\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Тестування %1x%2 %3 FPS…\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Кодувальник для трансляцій\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Кодувальник для запису\"\nBasic.AutoConfig.TestPage.Result.Header=\"Визначено налаштування, що є оптимальними для вас:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Щоб використовувати ці налаштування, натисніть «Застосувати налаштування». Щоб змінити параметри майстра та спробувати ще раз, натисніть «Назад». Ви також можете скасувати та ввести налаштування самостійно в «Налаштуваннях».\"\nBasic.AutoConfig.Info=\"Майстер з автоналаштування визначить найкращі параметри на основі характеристик вашого комп'ютера та швидкості Інтернету.\"\nBasic.AutoConfig.RunAnytime=\"Його можна запустити в будь-який час, в меню «Засоби».\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Роздільність трансляції (масштабована)\"\nBasic.Stats=\"Статистика\"\nBasic.Stats.CPUUsage=\"Використання ЦП\"\nBasic.Stats.HDDSpaceAvailable=\"Доступно місця на диску\"\nBasic.Stats.MemoryUsage=\"Використання пам'яті\"\nBasic.Stats.AverageTimeToRender=\"Середній час побудови кадру\"\nBasic.Stats.SkippedFrames=\"Кадри пропущені через затримки кодування\"\nBasic.Stats.MissedFrames=\"Кадри, пропущені через затримки рендерингу\"\nBasic.Stats.Output.Stream=\"Трансляція\"\nBasic.Stats.Output.Recording=\"Запис\"\nBasic.Stats.Status=\"Стан\"\nBasic.Stats.Status.Recording=\"Триває записування\"\nBasic.Stats.Status.Live=\"НАЖИВО\"\nBasic.Stats.Status.Reconnecting=\"Перепідключення\"\nBasic.Stats.Status.Inactive=\"Неактивний\"\nBasic.Stats.Status.Active=\"Активовано\"\nBasic.Stats.DroppedFrames=\"Пропущено кадрів (мережа)\"\nBasic.Stats.MegabytesSent=\"Загалом вихідних даних\"\nBasic.Stats.Bitrate=\"Бітрейт\"\nBasic.Stats.DiskFullIn=\"Диск заповниться через (прибл.)\"\nBasic.Stats.DiskFullIn.Text=\"%1 год., %2 хв.\"\nBasic.Stats.ResetStats=\"Скинути статистику\"\nResetUIWarning.Title=\"Скинути інтерфейс?\"\nResetUIWarning.Text=\"Скидання налаштувань інтерфейсу приховує всі додаткові панелі. Якщо ви захочете їх відкрити – потрібно буде знов увімкнути ці панелі з меню Панелі інтерфейсу.\\n\\nВи дійсно бажаєте скинути налаштування інтерфейсу?\"\nUpdater.Title=\"Нове оновлення\"\nUpdater.Text=\"Доступне нове оновлення:\"\nUpdater.UpdateNow=\"Оновити зараз\"\nUpdater.RemindMeLater=\"Нагадати пізніше\"\nUpdater.Skip=\"Пропустити цю версію\"\nUpdater.NoUpdatesAvailable.Title=\"Оновлення відсутні\"\nUpdater.NoUpdatesAvailable.Text=\"Наразі немає доступних оновлень\"\nUpdater.BranchNotFound.Title=\"Канал оновлення вилучено\"\nUpdater.BranchNotFound.Text=\"Вибраний канал оновлення більше недоступний, OBS було відновлено до типових налаштувань.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Перевірка цілісності недоступна\"\nUpdater.RepairButUpdatesAvailable.Text=\"Перевірка цілісності файлу можлива лише для найновішої версії. Перейдіть Довідка → Перевірити наявність оновлень, щоб перевірити та оновити встановлений OBS.\"\nUpdater.RepairConfirm.Title=\"Підтвердити перевірку цілісності\"\nUpdater.RepairConfirm.Text=\"Запуск перевірки цілісності скануватиме встановлений OBS на наявність пошкоджень і повторно завантажить пошкоджені/модифіковані файли. Це може тривати деякий час.\\n\\nБажаєте продовжити?\"\nUpdater.FailedToLaunch=\"Не вдалося перевірити оновлення\"\nQuickTransitions.SwapScenes=\"Поміняти місцями попередній перегляд/програмні сцени після переходу\"\nQuickTransitions.SwapScenesTT=\"Поміняє сцени попереднього перегляду та програми після переходу (якщо вихідна сцена програми все ще існує).\\nЦе не скасує жодних змін, які могли бути внесені до початкової сцени програми.\"\nQuickTransitions.DuplicateScene=\"Дублювати сцену\"\nQuickTransitions.DuplicateSceneTT=\"Під час редагування тієї самої сцени дозволяє редагувати перетворення/видимість джерел, не змінюючи вихід програми.\\nЩоб редагувати властивості джерел без зміни виводу програми, увімкніть \\\"Дублікати джерел\\\".\\nЗміна цього значення призведе до скидання поточної сцени програми (якщо вона все ще існує).\"\nQuickTransitions.EditProperties=\"Дублювати джерела\"\nQuickTransitions.EditPropertiesTT=\"Під час редагування тієї самої сцени дозволяє редагувати властивості джерел, не змінюючи вихід програми.\\nЦе можна використовувати, лише якщо ввімкнено \\\"Дублікат сцени\\\".\\nДеякі джерела (наприклад, джерела запису або медіа) не підтримують це і не можуть бути відредаговані окремо.\\nЗміна цього значення призведе до скидання поточної сцени програми (якщо вона все ще існує).\\n\\nПопередження: оскільки джерела будуть дубльовані, це може потребувати додаткових системних або відеоресурсів.\"\nQuickTransitions.HotkeyName=\"Швидкий монтажний перехід: %1\"\nBasic.AddTransition=\"Додати перехід із налаштуванням\"\nBasic.RemoveTransition=\"Видалити перехід із налаштуванням\"\nBasic.TransitionProperties=\"Властивості переходу\"\nBasic.SceneTransitions=\"Монтажні переходи між сценами\"\nBasic.TransitionDuration=\"Тривалість\"\nBasic.TogglePreviewProgramMode=\"Режим студії\"\nBasic.EnablePreviewProgramMode=\"Увімкнути режим студії\"\nBasic.DisablePreviewProgramMode=\"Вимкнути режим студії\"\nUndo.Undo=\"Скасувати\"\nUndo.Redo=\"Повернути\"\nUndo.Add=\"Додати '%1'\"\nUndo.Delete=\"Видалити '%1'\"\nUndo.Rename=\"Перейменувати '%1'\"\nUndo.SceneCollection.Switch=\"Перейти до '%1'\"\nUndo.Item.Undo=\"Скасувати %1\"\nUndo.Item.Redo=\"Повернути %1\"\nUndo.Sources.Multi=\"Видалити %1 джерел\"\nUndo.Filters=\"Фільтрувати зміни за '%1'\"\nUndo.Filters.Paste.Single=\"Вставити фільтр «%1» до «%2»\"\nUndo.Filters.Paste.Multiple=\"Копіювати фільтри з «%1» до «%2»\"\nUndo.Transform=\"Перетворити джерела в '%1'\"\nUndo.Transform.Paste=\"Вставити перетворення в '%1'\"\nUndo.Transform.Rotate=\"Поворот в '%1'\"\nUndo.Transform.Reset=\"Скинути перетворення в '%1'\"\nUndo.Transform.HFlip=\"Віддзеркалити горизонтально в '%1'\"\nUndo.Transform.VFlip=\"Віддзеркалити вертикально в '%1'\"\nUndo.Transform.FitToScreen=\"Підігнати до екрана в '%1'\"\nUndo.Transform.StretchToScreen=\"Розтягнути на екран у '%1'\"\nUndo.Transform.Center=\"Центрувати на екрані в '%1'\"\nUndo.Transform.VCenter=\"Центрувати на екрані вертикально в '%1'\"\nUndo.Transform.HCenter=\"Центрувати на екрані горизонтально в '%1'\"\nUndo.Volume.Change=\"Зміна гучності в '%1'\"\nUndo.Volume.Mute=\"Заглушити «%1»\"\nUndo.Volume.Unmute=\"Увімкнути звук «%1»\"\nUndo.Balance.Change=\"Зміна аудіобалансу для «%1»\"\nUndo.SyncOffset.Change=\"Зміна аудіосинхронізації для «%1»\"\nUndo.MonitoringType.Change=\"Змінити аудіомоніторинг для «%1»\"\nUndo.Mixers.Change=\"Змінити аудіомікшери для «%1»\"\nUndo.ForceMono.On=\"Увімкнути примусове моно для «%1»\"\nUndo.ForceMono.Off=\"Вимкнути примусове моно для «%1»\"\nUndo.Properties=\"Зміна властивості в '%1'\"\nUndo.Scene.Duplicate=\"Дублювати сцену '%1'\"\nUndo.ShowTransition=\"Показати відеоперехід на '%1'\"\nUndo.HideTransition=\"Приховати відеоперехід на '%1'\"\nUndo.ShowSceneItem=\"Показувати '%1' в '%2'\"\nUndo.HideSceneItem=\"Приховати '%1' в '%2'\"\nUndo.ReorderSources=\"Зміну порядку джерел у '%1'\"\nUndo.MoveUp=\"Підняття'%1' у '%2'\"\nUndo.MoveDown=\"Опускання '%1' у '%2'\"\nUndo.MoveToTop=\"Переміщення '%1' вгору в '%2'\"\nUndo.MoveToBottom=\"Переміщення '%1' вниз у '%2'\"\nUndo.PasteSource=\"Вставляння джерел у '%1'\"\nUndo.PasteSourceRef=\"Вставляння джерел посилання в '%1'\"\nUndo.GroupItems=\"Групувати елементи в '%1'\"\nTransitionNameDlg.Text=\"Введіть назву монтажного переходу\"\nTransitionNameDlg.Title=\"Назва монтажного переходу\"\nTitleBar.SafeMode=\"БЕЗПЕЧНИЙ РЕЖИМ\"\nTitleBar.PortableMode=\"Портативний режим\"\nTitleBar.Profile=\"Профіль\"\nTitleBar.Scenes=\"Сцени\"\nNameExists.Title=\"Назва вже існує\"\nNameExists.Text=\"Ця назва вже використовується.\"\nNoNameEntered.Title=\"Введіть дійсну назву\"\nNoNameEntered.Text=\"Не можна використовувати порожні назви.\"\nConfirmStart.Title=\"Почати трансляцію?\"\nConfirmStart.Text=\"Ви дійсно бажаєте почати трансляцію?\"\nConfirmStop.Title=\"Зупинити трансляцію?\"\nConfirmStop.Text=\"Справді зупинити трансляцію?\"\nConfirmStopRecord.Title=\"Зупинити записування?\"\nConfirmStopRecord.Text=\"Справді зупинити записування?\"\nConfirmBWTest.Title=\"Почати тест пропускної здатності?\"\nConfirmBWTest.Text=\"OBS налаштовано у режимі тестування пропускної здатності. Цей режим дозволяє здійснювати тестування мережі не вмикаючи на вашому каналі трансляцію наживо. Після закінчення тестування, вам потрібно буде вимкнути його, щоб глядачі змогли побачити вашу трансляцію.\\n\\nБажаєте продовжити?\"\nConfirmExit.Title=\"Активні виводи\"\nConfirmExit.Text=\"OBS досі активний. Усі трансляції/записи будуть вимкнені.\"\nConfirmRemove.Title=\"Підтвердження видалення\"\nConfirmRemove.Text=\"Ви впевнені, що хочете вилучити «%1»?\"\nConfirmRemove.TextMultiple=\"Справді видалити ці (%1) елементи?\"\nConfirmReset.Title=\"Скинути властивості\"\nConfirmReset.Text=\"Ви дійсно бажаєте скинути поточні властивості до типових?\"\nOutput.StartStreamFailed=\"Не вдалося розпочати трансляцію\"\nOutput.StartRecordingFailed=\"Не вдалося розпочати записування\"\nOutput.StartReplayFailed=\"Не вдалося запустити буфер повторів\"\nOutput.StartVirtualCamFailed=\"Не вдалося запустити віртуальну камеру\"\nOutput.StartFailedGeneric=\"Помилка виводу. Додаткова інформація знаходиться у файлі журналу.\\n\\nПримітка: якщо ви використовуєте кодувальники NVENC або AMD, переконайтесь, що маєте відеодрайвер найновішої версії.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Неможливо зберегти повтори під час паузи\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Попередження: неможливо зберегти повтори, якщо записування призупинено.\"\nOutput.ConnectFail.Title=\"Помилка підключення\"\nOutput.ConnectFail.BadPath=\"Хибний шлях або URL-адреса підключення. Будь ласка, перевірте налаштування щоб перевірити коректність цих параметрів.\"\nOutput.ConnectFail.ConnectFailed=\"Не вдалося під'єднатися до сервера\"\nOutput.ConnectFail.InvalidStream=\"Не вдалося отримати доступ до вказаного каналу або ключа трансляції, перевірте свій ключ трансляції ще раз. Якщо він дійсний – можливо, виникла проблема з підключенням до сервера.\"\nOutput.ConnectFail.HdrDisabled=\"Вивід HDR для цього виводу наразі вимкнено.\"\nOutput.ConnectFail.Error=\"При спробі підключення до сервера сталася несподівана помилка. Більше інформації у файлі журналу.\"\nOutput.ConnectFail.Disconnected=\"Відключено від сервера.\"\nOutput.StreamEncodeError.Title=\"Помилка кодування\"\nOutput.StreamEncodeError.Msg=\"Під час трансляції відбулася помилка кодувальника.\"\nOutput.StreamEncodeError.Msg.LastError=\"Під час трансляції сталася помилка кодувальника:<br><br>%1\"\nOutput.RecordFail.Title=\"Не вдалося розпочати записування\"\nOutput.RecordFail.Unsupported=\"Формат виводу або не підтримується, або не підтримує більше однієї звукової доріжки. Будь ласка, перевірте налаштування та повторіть спробу.\"\nOutput.RecordNoSpace.Title=\"Недостатньо місця на диску\"\nOutput.RecordNoSpace.Msg=\"Для продовження запису немає достатньо місця на диску.\"\nOutput.RecordError.Title=\"Помилка записування\"\nOutput.RecordError.Msg=\"Під час записування сталася невідома помилка.\"\nOutput.RecordError.EncodeErrorMsg=\"Під час запису відбулася помилка кодувальника.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Під час запису сталася помилка кодувальника:<br><br>%1\"\nOutput.BadPath.Title=\"Неправильний шлях до файлу\"\nOutput.BadPath.Text=\"Не вдалося відкрити налаштований шлях до запису. Перевірте правильність шляху до запису в Налаштування → Вивід → Записування.\"\nOutput.NoBroadcast.Title=\"Трансляції не налаштовано\"\nOutput.NoBroadcast.Text=\"Перш ніж почати трансляцію, необхідно її налаштувати.\"\nOutput.BroadcastStartFailed=\"Не вдалося почати трансляцію\"\nOutput.BroadcastStopFailed=\"Не вдалося зупинити трансляцію\"\nLogUploadDialog.Title=\"Вивантаження журналу OBS Studio\"\nLogUploadDialog.Labels.PrivacyNotice=\"Перед вивантаженням файлів ознайомтеся з <a href='https://obsproject.com/privacy-policy'>політикою приватності</a>, а саме розділ, що стосується вивантаження файлів.\"\nLogUploadDialog.Labels.Progress=\"Триває вивантаження журналу. Зачекайте...\"\nLogUploadDialog.Labels.Description.AppLog=\"Файл журналу вивантажено. Тепер ви можете поділитися URL з метою налагодження або підтримки.\"\nLogUploadDialog.Labels.Description.CrashLog=\"Ваш звіт про збій вивантажено. Тепер можете поділитися URL з метою налагодження.\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Вивантажити\"\nLogUploadDialog.Buttons.CopyURL=\"Копіювати URL журналу\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Аналіз файлу журналу\"\nLogUploadDialog.Buttons.RetryButton=\"Повторити\"\nLogUploadDialog.Errors.Template=\"Під час спроби вивантажити файл сталася помилка:\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"Не знайдено файл для вивантаження або файл порожній.\"\nRemux.SourceFile=\"Запис OBS\"\nRemux.TargetFile=\"Кінцевий файл\"\nRemux.Remux=\"Ремультиплексування\"\nRemux.Stop=\"Зупинити ремультиплексування\"\nRemux.ClearFinished=\"Очистити завершені елементи\"\nRemux.ClearAll=\"Очистити усі елементи\"\nRemux.OBSRecording=\"Запис OBS\"\nRemux.FinishedTitle=\"Ремультиплексування завершено\"\nRemux.Finished=\"Запис ремультиплексовано\"\nRemux.FinishedError=\"Запис ремультиплексовано, але файл може бути неповний\"\nRemux.SelectRecording=\"Виберіть запис OBS…\"\nRemux.SelectTarget=\"Виберіть файл призначення…\"\nRemux.FileExistsTitle=\"Файли призначення існують\"\nRemux.FileExists=\"Такі кінцеві файли вже існують. Замінити їх?\"\nRemux.ExitUnfinishedTitle=\"Ремультиплексування триває\"\nRemux.ExitUnfinished=\"Ремультиплексування не завершено, якщо зупинити зараз, то кінцевий файл може бути пошкодженим.\\nВи впевнені, що хочете зупинити ремультиплексування?\"\nRemux.HelpText=\"Перетягніть файли до цього вікна, щоб ремультиплексувати їх, або виберіть пусту клітинку «Запис OBS», щоб обрати файл.\"\nRemux.NoFilesAddedTitle=\"Файл ремуксингу не додано\"\nRemux.NoFilesAdded=\"До ремуксу не додано жодного файлу. Перетягніть теку з одним або кількома відеофайлами.\"\nMissingFiles=\"Відсутні файли\"\nMissingFiles.MissingFile=\"Відсутній файл\"\nMissingFiles.NewFile=\"Новий файл\"\nMissingFiles.HelpText=\"Деякі файли відсутні після останнього використання OBS.\"\nMissingFiles.Clear=\"<очищено>\"\nMissingFiles.NumFound=\"Знайдено %1 із %2\"\nMissingFiles.Search=\"Пошук у теці...\"\nMissingFiles.SelectFile=\"Вибрати файл...\"\nMissingFiles.SelectDir=\"Виберіть теку для пошуку\"\nMissingFiles.State=\"Стан\"\nMissingFiles.Missing=\"Відсутні\"\nMissingFiles.Replaced=\"Замінено\"\nMissingFiles.Cleared=\"Очищено\"\nMissingFiles.Found=\"Знайдено\"\nMissingFiles.AutoSearch=\"Знайдено додаткові файли\"\nMissingFiles.AutoSearchText=\"OBS знайшов додаткові збіги для файлів, відсутніх у цій теці. Бажаєте додати їх?\"\nMissingFiles.NoMissing.Title=\"Перевірка відсутності файлів\"\nMissingFiles.NoMissing.Text=\"Відсутності файлів не виявлено.\"\nMacPermissions.MenuAction=\"Переглянути дозволи застосунку...\"\nMacPermissions.Title=\"Переглянути дозволи застосунку\"\nMacPermissions.Description=\"OBS Studio потребує вашого дозволу для роботи певних функцій. Радимо увімкнути ці дозволи, але вони не обов'язкові для роботи застосунку. Ви завжди можете увімкнути їх пізніше.\"\nMacPermissions.Description.OpenDialog=\"Це діалогове вікно можна повторно відкрити через меню OBS Studio.\"\nMacPermissions.AccessGranted=\"Доступ дозволено\"\nMacPermissions.RequestAccess=\"Запит доступу\"\nMacPermissions.OpenPreferences=\"Відкрити параметри %1\"\nMacPermissions.Item.ScreenRecording=\"Запис екрана\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS потребує цього дозволу для захоплення екрана.\"\nMacPermissions.Item.Camera=\"Камера\"\nMacPermissions.Item.Camera.Details=\"Цей дозвіл потрібен, щоб зберігати вміст з вебкамери або карти захоплення.\"\nMacPermissions.Item.Microphone=\"Мікрофон\"\nMacPermissions.Item.Microphone.Details=\"OBS потребує цей дозвіл, якщо ви хочете записати звук з мікрофона або зовнішнього аудіопристрою.\"\nMacPermissions.Item.InputMonitoring=\"Моніторинг вводу\"\nMacPermissions.Item.InputMonitoring.Details=\"Цей дозвіл потрібен для того, щоб гарячі клавіші працювали, коли OBS знаходиться у фоновому режимі.\"\nMacPermissions.Item.Accessibility=\"Доступність\"\nMacPermissions.Item.Accessibility.Details=\"У старих інсталяціях OBS може бути вказано у розділі «Доступність» замість «Моніторинг вводу».\"\nMacPermissions.Continue=\"Продовжити\"\nSourceLeak.Title=\"Помилка очищення джерела\"\nSourceLeak.Text=\"Під час зміни колекцій сцен виникла проблема, і деякі джерела не вдалося вивантажити. Цю проблему зазвичай спричиняють плагіни, які не вивільняють ресурси належним чином. Будь ласка, переконайтеся, що всі плагіни, які ви використовуєте, оновлені.\\n\\nOBS Studio буде завершено, щоб запобігти можливому пошкодженню даних.\"\nBasic.DesktopDevice1=\"Пристрій відтворення\"\nBasic.DesktopDevice2=\"Пристрій відтворення 2\"\nBasic.AuxDevice1=\"Мікрофон/Aux\"\nBasic.AuxDevice2=\"Мікрофон/Aux 2\"\nBasic.AuxDevice3=\"Мікрофон/Aux 3\"\nBasic.AuxDevice4=\"Мікрофон/Aux 4\"\nBasic.Scene=\"Сцена\"\nBasic.DisplayCapture=\"Захоплення екрану\"\nBasic.Main.PreviewConextMenu.Enable=\"Увімкнути попередній перегляд\"\nBasic.Main.Preview.Disable=\"Вимкнути попередній перегляд\"\nScaleFiltering=\"Фільтр масштабування\"\nScaleFiltering.Point=\"Точковий\"\nScaleFiltering.Bilinear=\"Білінійний\"\nScaleFiltering.Bicubic=\"Бікубічний\"\nScaleFiltering.Lanczos=\"Ланцош\"\nScaleFiltering.Area=\"Усереднення площ\"\nBlendingMethod=\"Метод змішування\"\nBlendingMethod.Default=\"Типовий\"\nBlendingMethod.SrgbOff=\"Вимкнути SRGB\"\nBlendingMode=\"Режим змішування\"\nBlendingMode.Normal=\"Звичайний\"\nBlendingMode.Additive=\"Додати\"\nBlendingMode.Subtract=\"Віднімання\"\nBlendingMode.Screen=\"Екран\"\nBlendingMode.Multiply=\"Множення\"\nBlendingMode.Lighten=\"Освітлення\"\nBlendingMode.Darken=\"Затемнення\"\nDeinterlacing=\"Деінтерлейсинг\"\nDeinterlacing.TopFieldFirst=\"Спочатку непарні рядки\"\nDeinterlacing.BottomFieldFirst=\"Спочатку парні рядки\"\nVolControl.SliderUnmuted=\"Повзунок гучності для '%1':\"\nVolControl.SliderMuted=\"Повзунок гучності для '%1': (зараз заглушено)\"\nVolControl.Mute=\"Заглушити '%1'\"\nVolControl.Properties=\"Параметри '%1'\"\nVolControl.UnassignedWarning.Title=\"Непризначене джерело звуку\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" не призначено якійсь звуковій доріжці та не буде чутним у трансляціяї та записах.\\n\\nЩоб призначити джерело звуку доріжці, відкрийте Розширені властивості аудіо командою контекстного меню або кнопкою на панелі мікшера.\"\nBasic.Main.AddSceneDlg.Title=\"Додати сцену\"\nBasic.Main.AddSceneDlg.Text=\"Будь ласка, введіть назву сцени\"\nBasic.Main.DefaultSceneName.Text=\"Сцена %1\"\nBasic.Main.AddSceneCollection.Title=\"Додати колекцію сцен\"\nBasic.Main.AddSceneCollection.Text=\"Будь ласка, введіть назву колекції сцен\"\nBasic.Main.RenameSceneCollection.Title=\"Перейменувати колекцію сцен\"\nBasic.Main.RemigrateSceneCollection.Title=\"Оновити роздільність збірки сцен\"\nBasic.Main.RemigrateSceneCollection.Text=\"Хочете оновити роздільність збірки сцен «%1», щоб вона відповідала основній роздільності поточного профілю %2x%3?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Неможливо оновити роздільність збірки сцен, коли виводи активні.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Не вдалося оновити роздільність збірки сцен. Початкова роздільність невідома.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Скидання неможливе: Не вдалося змінити роздільність OBS.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Скидання неможливе: Поточна роздільність уже встановлена базовою роздільністю збірки сцен.\"\nAddProfile.Title=\"Додати профіль\"\nAddProfile.Text=\"Будь ласка, введіть назву профілю\"\nAddProfile.WizardCheckbox=\"Показувати майстер з автоналаштування\"\nRenameProfile.Title=\"Перейменувати профіль\"\nBasic.Main.MixerRename.Title=\"Перейменувати джерело аудіо\"\nBasic.Main.MixerRename.Text=\"Будь ласка, введіть назву джерела аудіо\"\nBasic.Main.PreviewDisabled=\"Попередній перегляд зараз вимкнено\"\nBasic.SourceSelect=\"Створити/вибрати джерело\"\nBasic.SourceSelect.CreateNew=\"Створити нове\"\nBasic.SourceSelect.AddExisting=\"Додати існуюче\"\nBasic.SourceSelect.AddVisible=\"Зробити джерело видимим\"\nBasic.Main.Sources.Visibility=\"Видимість\"\nBasic.Main.Sources.VisibilityDescription=\"Контролює видимість '%1' на полотні\"\nBasic.Main.Sources.Lock=\"Блокувати\"\nBasic.Main.Sources.LockDescription=\"Фіксує позицію та масштаб «%1» на полотні\"\nBasic.PropertiesWindow=\"Параметри '%1'\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (автовибір: %2)\"\nBasic.PropertiesWindow.SelectColor=\"Вибрати колір\"\nBasic.PropertiesWindow.SelectFont=\"Вибрати шрифт\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Вибрати шрифт\"\nBasic.PropertiesWindow.ConfirmTitle=\"Налаштування змінено\"\nBasic.PropertiesWindow.Confirm=\"Деякі зміни не збережено. Хочете їх зберегти?\"\nBasic.PropertiesWindow.NoProperties=\"Немає доступних властивостей\"\nBasic.PropertiesWindow.AddFiles=\"Додати файли\"\nBasic.PropertiesWindow.AddDir=\"Додати каталог\"\nBasic.PropertiesWindow.AddURL=\"Додати шлях/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Додати каталог в '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Додати файли в '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Додати запис в '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Редагувати запис із '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Прості значення FPS\"\nBasic.PropertiesView.FPS.Rational=\"Раціональні значення FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Допустимі діапазони FPS:\"\nBasic.PropertiesView.UrlButton.Text=\"Відкрити це посилання у стандартному браузері?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Відкрити URL\"\nBasic.InteractionWindow=\"Взаємодія із '%1'\"\nBasic.StatusBar.Reconnecting=\"Роз'єднано, повторна спроба під'єднання за %2 секунд(и) (спроба %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Спроба повторного підключення… (спроба %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Повторне під'єднання успішне\"\nBasic.StatusBar.Delay=\"Затримка (%1 с)\"\nBasic.StatusBar.DelayStartingIn=\"Затримка (початок через %1 с)\"\nBasic.StatusBar.DelayStoppingIn=\"Затримка (зупинка через %1 с)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Затримка (зупинка через %1 с, початок через %2 с)\"\nBasic.StatusBar.RecordingSavedTo=\"Запис збережено до '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Буфер повторів збережено до '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Знімок екрана збережено до '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Запис автоматично ремультиплексовано до '%1'\"\nBasic.Filters=\"Фільтри\"\nBasic.Filters.AsyncFilters=\"Фільтри аудіо/відео\"\nBasic.Filters.AudioFilters=\"Аудіо фільтри\"\nBasic.Filters.EffectFilters=\"Фільтри ефектів\"\nBasic.Filters.Title=\"Фільтри для '%1'\"\nBasic.Filters.AddFilter.Title=\"Назва фільтру\"\nBasic.Filters.AddFilter.Text=\"Введіть назву фільтру\"\nBasic.TransformWindow=\"Перетворення елемента сцени\"\nBasic.TransformWindow.Position=\"Позиція\"\nBasic.TransformWindow.PositionX=\"Положення X\"\nBasic.TransformWindow.PositionY=\"Положення Y\"\nBasic.TransformWindow.Rotation=\"Обертання\"\nBasic.TransformWindow.Size=\"Розмір\"\nBasic.TransformWindow.Width=\"Ширина\"\nBasic.TransformWindow.Height=\"Висота\"\nBasic.TransformWindow.Alignment=\"Позиційне вирівнювання\"\nBasic.TransformWindow.BoundsType=\"Тип обмежувача\"\nBasic.TransformWindow.BoundsAlignment=\"Вирівнювання всередині обмежувача\"\nBasic.TransformWindow.Bounds=\"Розмір обмежувача\"\nBasic.TransformWindow.BoundsWidth=\"Ширина обмежувальної рамки\"\nBasic.TransformWindow.BoundsHeight=\"Висота обмежувальної рамки\"\nBasic.TransformWindow.CropToBounds=\"Обрізати до обмежувальної рамки\"\nBasic.TransformWindow.Crop=\"Обрізати\"\nBasic.TransformWindow.CropLeft=\"Обрізати зліва\"\nBasic.TransformWindow.CropRight=\"Обрізати справа\"\nBasic.TransformWindow.CropTop=\"Обрізати зверху\"\nBasic.TransformWindow.CropBottom=\"Обрізати знизу\"\nBasic.TransformWindow.Alignment.TopLeft=\"Зверху ліворуч\"\nBasic.TransformWindow.Alignment.TopCenter=\"Зверху по центру\"\nBasic.TransformWindow.Alignment.TopRight=\"Зверху праворуч\"\nBasic.TransformWindow.Alignment.CenterLeft=\"По центру ліворуч\"\nBasic.TransformWindow.Alignment.Center=\"По центру\"\nBasic.TransformWindow.Alignment.CenterRight=\"По центру праворуч\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Знизу ліворуч\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Знизу по центру\"\nBasic.TransformWindow.Alignment.BottomRight=\"Знизу праворуч\"\nBasic.TransformWindow.BoundsType.None=\"Немає обмежень\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Тільки максимальний розмір\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Масштабувати до внутрішніх границь\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Масштабувати до зовнішніх границь\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Масштабувати до ширини границь\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Масштабувати до висоти границь\"\nBasic.TransformWindow.BoundsType.Stretch=\"Розтягнути до границь\"\nBasic.TransformWindow.Title=\"Редагувати перетворення для '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Джерела не вибрано\"\nBasic.Main.AddSourceHelp.Title=\"Неможливо додати джерело\"\nBasic.Main.AddSourceHelp.Text=\"Щоб додати джерело, потрібно мати принаймні одну сцену.\"\nBasic.Main.Scenes=\"Сцени\"\nBasic.Main.Sources=\"Джерела\"\nBasic.Main.Source=\"Джерело\"\nBasic.Main.Controls=\"Керування\"\nBasic.Main.PreparingStream=\"Приготування...\"\nBasic.Main.Connecting=\"Підʼєднання…\"\nBasic.Main.StartRecording=\"Почати записування\"\nBasic.Main.StartReplayBuffer=\"Запустити буфер повторів\"\nBasic.Main.SaveReplay=\"Зберегти повтор\"\nBasic.Main.StartStreaming=\"Почати трансляцію\"\nBasic.Main.StartBroadcast=\"Розпочати трансляцію\"\nBasic.Main.StartVirtualCam=\"Запустити віртуальну камеру\"\nBasic.Main.StopRecording=\"Зупинити записування\"\nBasic.Main.PauseRecording=\"Призупинити запис\"\nBasic.Main.UnpauseRecording=\"Відновити запис\"\nBasic.Main.SplitFile=\"Розділити файл Запису\"\nBasic.Main.AddChapterMarker=\"Додати мітку розділу (лише гібридний MP4/MOV)\"\nBasic.Main.StoppingRecording=\"Припинення запису…\"\nBasic.Main.StopReplayBuffer=\"Зупинити буфер повторів\"\nBasic.Main.StoppingReplayBuffer=\"Зупинення буферу повторів…\"\nBasic.Main.SetupBroadcast=\"Керування трансляціями\"\nBasic.Main.StopStreaming=\"Зупинити трансляцію\"\nBasic.Main.StopBroadcast=\"Закінчити трансляцію\"\nBasic.Main.AutoStopEnabled=\"(Автозупинка)\"\nBasic.Main.StoppingStreaming=\"Зупинення трансляції…\"\nBasic.Main.ForceStopStreaming=\"Зупинити трансляцію (миттєво)\"\nBasic.Main.ShowContextBar=\"Показати панель джерела\"\nBasic.Main.HideContextBar=\"Приховати панель джерела\"\nBasic.Main.StopVirtualCam=\"Зупинити віртуальну камеру\"\nBasic.Main.Group=\"Група %1\"\nBasic.Main.GroupItems=\"Згрупувати вибрані елементи\"\nBasic.Main.Ungroup=\"Розгрупувати\"\nBasic.Main.GridMode=\"Режим сітки\"\nBasic.Main.ListMode=\"Режим списку\"\nBasic.Main.VirtualCamConfig=\"Конфігурації віртуальної камери\"\nBasic.VCam.VirtualCamera=\"Віртуальна камера\"\nBasic.VCam.OutputType=\"Тип виводу\"\nBasic.VCam.OutputSelection=\"Вибір виводу\"\nBasic.VCam.OutputType.Program=\"Програмний (типово)\"\nBasic.VCam.OutputSelection.NoSelection=\"Не вибрано цей типу виводу\"\nBasic.VCam.RestartWarning=\"Щоб застосувати ці зміни віртуальну камеру буде перезапущено\"\nBasic.MainMenu.File=\"Файл (&F)\"\nBasic.MainMenu.File.Export=\"&Експортувати\"\nBasic.MainMenu.File.Import=\"Імпортувати (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Показати записи (&R)\"\nBasic.MainMenu.File.Remux=\"Ремультиплексувати записи (&M)\"\nBasic.MainMenu.File.Settings=\"Налаштування (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Показати каталог налаштувань\"\nBasic.MainMenu.File.ShowProfileFolder=\"Показати каталог профілю\"\nBasic.MainMenu.File.ShowMissingFiles=\"Перевірити відсутність файлів\"\nBasic.MainMenu.File.Exit=\"Вийти (&X)\"\nBasic.MainMenu.Edit=\"Редагування (&E)\"\nBasic.MainMenu.Edit.Undo=\"Скасувати (&U)\"\nBasic.MainMenu.Edit.Redo=\"Повторити (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Заблокувати попередній перегляд (&L)\"\nBasic.MainMenu.Edit.Scale=\"Масштабування попереднього перегляду (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"Масштабувати до розміру вікна\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Полотно (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Вивід (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Масштабовано (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Набзилити\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Віддалити\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Скинути масштаб\"\nBasic.MainMenu.Edit.Transform=\"Перетворити (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Редагувати перетворення… (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Копіювати перетворення\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Вставити перетворення\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Скинути перетворення (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Повернути ліворуч на 90 градусів\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Повернути праворуч на 90 градусів\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Повернути на 180 градусів\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Віддзеркалити горизонтально (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Віддзеркалити вертикально (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Припасувати до екрана (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Розтягнути до розмірів екрана (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Розташувати по центру екрана (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Центрувати по вертикалі\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Центрувати по горизонталі\"\nBasic.MainMenu.Edit.Order=\"Упорядкувати (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Вище (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Нижче (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Перемістити вгору (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Перемістити вниз (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Розширені властивості аудіо (&A)\"\nBasic.MainMenu.View=\"Вигляд (&V)\"\nBasic.MainMenu.View.Toolbars=\"Панелі інструментів (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"Док-панелі інструментів\"\nBasic.MainMenu.View.ContextBar=\"Панель джерела\"\nBasic.MainMenu.View.SourceIcons=\"&Піктограми джерел\"\nBasic.MainMenu.View.StatusBar=\"Рядок стану (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Повноекранний режим\"\nBasic.MainMenu.View.ResetUI=\"&Скинути інтерфейс\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Завжди зверху\"\nBasic.MainMenu.View.SceneListMode=\"Режим списку сцен\"\nBasic.MainMenu.Docks=\"&Панелі інтерфейсу\"\nBasic.MainMenu.Docks.ResetDocks=\"&Скинути панелі інтерфейсу\"\nBasic.MainMenu.Docks.LockDocks=\"&Заблокувати панелі інтерфейсу\"\nBasic.MainMenu.Docks.SideDocks=\"Док-панелі &повної висоти\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"&Користувацькі панелі браузера...\"\nBasic.MainMenu.Import=\"Імпортувати...\"\nBasic.MainMenu.Export=\"Експортувати...\"\nBasic.MainMenu.SceneCollection=\"Колекція сцен (&S)\"\nBasic.MainMenu.Profile=\"&Профіль\"\nBasic.MainMenu.Profile.Import=\"Імпорт профілю\"\nBasic.MainMenu.Profile.Export=\"Експорт профілю\"\nBasic.MainMenu.SceneCollection.Import=\"Імпорт колекції сцен\"\nBasic.MainMenu.SceneCollection.Export=\"Експорт колекції сцен\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Скинути базову роздільність\"\nBasic.MainMenu.SceneCollection.Migrate=\"Установити базову роздільність\"\nBasic.MainMenu.Profile.Exists=\"Профіль вже існує\"\nBasic.MainMenu.Tools=\"Інструменти (&T)\"\nBasic.MainMenu.Help=\"Довідка (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Довідковий портал (&P)\"\nBasic.MainMenu.Help.Website=\"Вебсайт (&W)\"\nBasic.MainMenu.Help.Discord=\"Сервер &Discord\"\nBasic.MainMenu.Help.WhatsNew=\"Що нового\"\nBasic.MainMenu.Help.Logs=\"&Файли журналів\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"&Показати файли журналів\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Передати поточний файл журналу (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Вивантажити &попередній файл журналу\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Переглянути поточний журнал (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Примітки до випуску\"\nBasic.MainMenu.Help.CheckForUpdates=\"Перевірити наявність оновлень\"\nBasic.MainMenu.Help.Repair=\"Перевірити цілісність файлу\"\nBasic.MainMenu.Help.RestartSafeMode=\"Перезапустити в безпечному режимі\"\nBasic.MainMenu.Help.RestartNormal=\"Перезапустити у звичайному режимі\"\nBasic.MainMenu.Help.CrashLogs=\"Звіти про збої (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Показати звіти про збої (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Вивантажити &попередній звіт про збій\"\nBasic.MainMenu.Help.About=\"Про OBS (&A)\"\nBasic.Settings.ProgramRestart=\"Щоб ці параметри набули чинності – необхідно перезапустити програму.\"\nBasic.Settings.ConfirmTitle=\"Підтвердження змін\"\nBasic.Settings.Confirm=\"У вас є незбережені зміни. Зберегти їх?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 контролює деякі налаштування вашої трансляції\"\nBasic.Settings.General=\"Загальні\"\nBasic.Settings.General.Language=\"Мова\"\nBasic.Settings.General.Updater=\"Оновлення\"\nBasic.Settings.General.UpdateChannel=\"Канал оновлення\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Вимкнено)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Типово)\"\nBasic.Settings.General.EnableAutoUpdates=\"Автоматично перевіряти наявність оновлень при запуску\"\nBasic.Settings.General.OpenStatsOnStartup=\"Відкрийте вікно статистики при запуску\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Сховати вікна OBS із захопленого екрана\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Це налаштування сховає всі вікна OBS Studio без проєктора від захоплення OBS і вплине на інші \\nзастосунки, такі як конференції, спільний доступ до екрана, віддалена підтримка, знімки екрана та інше програмне забезпечення для захоплення.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Увімкнення цієї опції сховає всі вікна OBS Studio без проєктора від захоплення OBS і вплине на інші застосунки, такі як конференції, спільний доступ до екрана, віддалена підтримка, знімки екрана та інше програмне забезпечення для захоплення.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Показати вікно підтвердження при запуску трансляцій\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Показати вікно підтвердження при зупинці трансляцій\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Показати вікно підтвердження при зупинці запису\"\nBasic.Settings.General.Projectors=\"Проєктори\"\nBasic.Settings.General.HideProjectorCursor=\"Приховати курсор над проєкторами\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Показувати проєктори поверх інших вікон\"\nBasic.Settings.General.Snapping=\"Прив’язування та вирівнювання джерел\"\nBasic.Settings.General.ScreenSnapping=\"Прив’язувати джерела до країв екрана\"\nBasic.Settings.General.CenterSnapping=\"Прив’язувати джерела посередині по горизонталі та вертикалі\"\nBasic.Settings.General.SourceSnapping=\"Прив’язувати джерела до інших джерел\"\nBasic.Settings.General.SnapDistance=\"Чутливість прив’язування\"\nBasic.Settings.General.SpacingHelpers=\"Показувати напрямні вирівнювання у пікселях\"\nBasic.Settings.General.RecordWhenStreaming=\"Автоматично записувати під час трансляції\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Продовжувати запис після завершення трансляції\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Автоматично запускати буфер повторів під час трансляції\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Тримати буфер повторів запущеним після завершення трансляції\"\nBasic.Settings.General.SysTray=\"Системний трей\"\nBasic.Settings.General.SysTrayWhenStarted=\"Згорнути в системний трей при запуску\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Завжди згортати в трей замість панелі завдань\"\nBasic.Settings.General.SaveProjectors=\"Зберігати проєктори при виході\"\nBasic.Settings.General.CloseExistingProjectors=\"Обмежити одним повноекранним проєктором на екран\"\nBasic.Settings.General.Preview=\"Попередній перегляд\"\nBasic.Settings.General.OverflowHidden=\"Приховувати вихід за межі\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Вихід за межі завжди видимий\"\nBasic.Settings.General.OverflowSelectionHidden=\"Показувати вихід за межі навіть коли джерело невидиме\"\nBasic.Settings.General.Importers=\"Імпортери\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Шукати відомі розташування для колекцій сцен під час імпорту\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Монтажний перехід до сцени при подвійному клацанні\"\nBasic.Settings.General.StudioPortraitLayout=\"Увімкнути портретне/вертикальне компонування\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Показувати позначки попереднього перегляду/програми\"\nBasic.Settings.General.Multiview=\"Багатоекранний перегляд\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Перемикання між сценами клацанням миші\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Показувати назви сцен\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Малювати зони безпеки (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Компонування багатоекранного перегляду\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Горизонтально, зверху (8 сцен)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Горизонтально, знизу (8 сцен)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Вертикально, ліворуч (8 сцен)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Вертикально, праворуч (8 сцен)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Горизонтально, вгорі (18 сцен)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Горизонтально, зверху (24 сцени)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Лише сцени (4 сцени)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Лише сцени (9 сцен)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Лише сцени (16 сцен)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Лише сцени (25 сцен)\"\nBasic.Settings.General.ChannelName.stable=\"Стабільний\"\nBasic.Settings.General.ChannelDescription.stable=\"Останній стабільний випуск\"\nBasic.Settings.General.ChannelName.beta=\"Бета / кандидати на випуск\"\nBasic.Settings.General.ChannelDescription.beta=\"Потенційно нестабільні передрелізні версії\"\nBasic.Settings.Appearance=\"Вигляд\"\nBasic.Settings.Appearance.General=\"Основне\"\nBasic.Settings.Appearance.General.Theme=\"Тема\"\nBasic.Settings.Appearance.General.Variant=\"Стиль\"\nBasic.Settings.Appearance.General.NoVariant=\"Стилів немає в наявності\"\nBasic.Settings.Appearance.FontScale=\"Розмір шрифту\"\nBasic.Settings.Appearance.Density=\"Щільність\"\nBasic.Settings.Appearance.Classic=\"Класичний\"\nBasic.Settings.Appearance.Compact=\"Компактний\"\nBasic.Settings.Appearance.Normal=\"Звичайний\"\nBasic.Settings.Appearance.Comfortable=\"Зручний\"\nBasic.Settings.Appearance.OptionsWarning=\"Деякі варіанти тем недоступні для цього стилю.\"\nBasic.Settings.Stream=\"Трансляція\"\nBasic.Settings.Stream.Destination=\"Призначення\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Використовувати автентифікацію\"\nBasic.Settings.Stream.Custom.Username=\"Ім’я користувача\"\nBasic.Settings.Stream.Custom.Password=\"Пароль\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: введіть srp_username.\\nRTMP: введіть ім'я користувача.\\nSRT: не використовується.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: введіть srp_password.\\nRTMP: введіть пароль.\\nSRT: введіть таємну фразу шифрування.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Увімкнути режим тестування пропускної здатності\"\nBasic.Settings.Stream.TTVAddon=\"Доповнення Twitch чату\"\nBasic.Settings.Stream.TTVAddon.None=\"Немає\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV та FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Відсутні налаштування трансляції\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Відкрити налаштування\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL-адреса та ключ трансляції відсутні.\\n\\nВідкрийте налаштування, щоб вказати URL-адресу та ключ трансляції на вкладці «Трансляція».\"\nBasic.Settings.Stream.MissingUrl=\"URL-адреса трансляції відсутня.\\n\\nВідкрийте налаштування, щоб вказати URL-адресу трансляції на вкладці «Трансляція».\"\nBasic.Settings.Stream.MissingStreamKey=\"Ключ трансляції відсутній.\\n\\nВідкрийте налаштування, щоб вказати ключ трансляції на вкладці «Трансляція».\"\nBasic.Settings.Stream.IgnoreRecommended=\"Нехтувати порадами служби трансляцій щодо налаштувань\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Перевизначення рекомендованих налаштувань\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Попередження: Нехтування обмеженнями може призвести до погіршення якості потокового відео або завадити потоковій передачі.\\n\\nПродовжити?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Найбільший бітрейт відео: %1 Кбіт/с\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Найбільший бітрейт звуку: %1 Кбіт/с\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Найбільша роздільна здатність: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"Найбільша частота кадрів на секунду: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Вказати власний сервер...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Власний сервер\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Увімкнути %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Максимальна пропускна здатність трансляції\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Автоматично\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Максимальна кількість відеодоріжок\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Автоматично\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Увімкнути дамп трансляції у FLV (використовує прості налаштування файлу запису)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Перевизначення конфігурації (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Увімкнути перевизначення конфігурації\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Мультитрекове відео\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"Додаткове Полотно\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Одночасна трансляція\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Одночасна трансляція дозволяє кодувати та надсилати відео різної якості. <a href='https://obsproject.com/kb/whip-streaming-guide'>Докладніше</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Загальна кількість шарів\"\nBasic.Settings.Stream.AdvancedOptions=\"Розширені налаштування\"\nBasic.Settings.Output=\"Вивід\"\nBasic.Settings.Output.Format=\"Формат запису\"\nBasic.Settings.Output.Format.MKV=\"Відео Matroska (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"Гібридний MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"Гібридний MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"Фрагментований MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"Фрагментований MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"Фрагментований MOV записує запис фрагментами та не потребує такої ж обробки, як звичайні файли MOV.\\nЦе гарантує, що файл можна буде відтворити, навіть якщо запис на диск буде перервано, наприклад, унаслідок аварійного завершення роботи пристрою або втрати живлення.\\n\\nЦе може бути несумісним з деякими програвачами та редакторами. За потреби скористайтеся командою Файл → Ремультиплексувати записи, щоб перетворити файл у більш сумісний формат.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"Фрагментований MP4 розбиває запис на шматки й не вимагає такої фіналізації, як традиційні MP4 файли.\\nЦе забезпечує відтворюваність файлу, навіть якщо запис на диск перервано, наприклад, унаслідок BSOD або зникнення електроенергії.\\n\\nМоже бути сумісним не з усіма програвачами та редакторами. Щоб перетворити файл у сумісніший формат, скористайтеся командою Файл → Ремультиплексувати записи.\"\nBasic.Settings.Output.Encoder.Video=\"Кодувальник відео\"\nBasic.Settings.Output.Encoder.Audio=\"Кодувальник аудіо\"\nBasic.Settings.Output.SelectDirectory=\"Виберіть каталог записів\"\nBasic.Settings.Output.DynamicBitrate=\"Динамічно змінювати бітрейт підлаштовуючись під мережу\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Динамічно змінювати бітрейт підлаштовуючись під мережу (бета)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Замість пропуску кадрів динамічно змінює бітрейт на ходу, підлаштовуючись під мережу.\\n\\nЗауважте, що це може збільшити затримку для глядачів, якщо є значні раптові помилки мережі.\\nВідновлення бітрейту до початкового значення після падіння, може зайняти до кількох хвилин.\\n\\nНаразі підтримується лише для протоколу RTMP.\"\nBasic.Settings.Output.Mode=\"Режим виводу\"\nBasic.Settings.Output.Mode.Simple=\"Простий\"\nBasic.Settings.Output.Mode.Adv=\"Розширений\"\nBasic.Settings.Output.Mode.FFmpeg=\"Вивід FFmpeg\"\nBasic.Settings.Output.UseReplayBuffer=\"Увімкнути буфер повторів\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Максимальна тривалість повтору\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Макс. обсяг пам’яті\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Орієнтовне використання пам'яті: %1 МБ\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Попередження: Використовується приблизно %1 МіБ пам'яті, що перевищує рекомендоване обмеження %2 МіБ\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Прграма не може оцінити використання пам'яті. Необхідно встановити максимальний обсяг пам’яті.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Префікс назви файлу буфера повторів\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Суфікс\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Буфер Повторів не можна використовувати, коли тип запису встановлений у користувачем (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Шлях запису\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Якість запису\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Така ж як у трансляції\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Висока якість, середній розмір файлу\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Надзвичайна якість, великий розмір файлу\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Без втрат якості, надзвичайно великий розмір файлу\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Увага: Буде встановлено бітрейт потокового відео %1, що є верхньою межею поточнї служби трансляцій.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Увага: Буде встановлено бітрейт звуку %1, що є верхньою межею поточнї служби трансляцій.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Попередження: записи не можна призупиняти, якщо для якості запису встановлено значення «Така ж як у трансляції».\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Попередження: поточний вибраний формат запису несумісний із вибраним кодувальником(ами) потокового відео.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Попередження: запис програмним кодувальником з якістю, відмінною від якості трансляції, спричиняє додаткове навантаження на центральний процесор при одночасному використанні трансляції та запису.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Увага: Якість без втрат генерує надзвичайно великі розміри файлів! Якість без втрат може використовувати до 7 гігабайтів дискового простору на хвилину при високій роздільності та частоті кадрів. Не рекомендується використовувати якість без втрат для довгих записів, якщо у вас немає дуже великого обсягу дискового простору. Буфер повторного відтворення недоступний при використанні якості без втрат.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Справді використовувати настройку «Без втрат якості»?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Попередження щодо якості без втрат!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Програмний (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Апаратний (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Апаратний (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Апаратний (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Апаратний (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Апаратний (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Апаратний (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Апаратний (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Апаратний (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Апаратний (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Апаратний (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Програмний (шаблон x264 з низьким навантаженням на ЦП, збільшує розмір файлу)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (типово)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Доріжка Twitch VOD (Використовується доріжка 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Звукова доріжка\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Несумісна роздільна здатність/частота кадрів\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Ця служба трансляцій не підтримує поточну роздільну здатність виводу та/або частоту кадрів. Їх буде змінено на найближче сумісне значення:\\n\\n%1\\n\\nБажаєте продовжити?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Роздільна здатність: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"Кадрів на секунду: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Несумісний кодувальник\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Служба трансляцій \\\"%1\\\" не підтримує кодувальника \\\"%2\\\". Кодувальник буде замінено на \\\"%3\\\".\\n\\nБажаєте продовжити?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Служба трансляцій «%1» не підтримує кодувальників «%2» і «%3». Ці кодувальники будуть замінені на «%4» і «%5».\\n\\nБажаєте продовжити?\"\nBasic.Settings.Output.VideoBitrate=\"Бітрейт відео\"\nBasic.Settings.Output.AudioBitrate=\"Бітрейт аудіо\"\nBasic.Settings.Output.Reconnect=\"Автоматично перепідключатися\"\nBasic.Settings.Output.RetryDelay=\"Затримка повторення\"\nBasic.Settings.Output.MaxRetries=\"Максимальна кількість спроб\"\nBasic.Settings.Output.Advanced=\"Увімкнути користувацькі налаштування енкодера (додатково)\"\nBasic.Settings.Output.EncoderPreset=\"Шаблон кодувальника\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (низьке використання ЦП, найнижча якість)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (типово) (середнє використання ЦП, стандартна якість)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (високе використання ЦП, висока якість)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Власні налаштування кодувальника\"\nBasic.Settings.Output.CustomMuxerSettings=\"Власні налаштування мультиплексора\"\nBasic.Settings.Output.NoSpaceFileName=\"Генерувати назву файлу без пробілів\"\nBasic.Settings.Output.Adv.Rescale=\"Змінити масштаб виводу\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Вимкнено\"\nBasic.Settings.Output.Adv.AudioTrack=\"Звукова доріжка\"\nBasic.Settings.Output.Adv.Streaming=\"Трансляція\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Налаштування трансляції\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Доріжка 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Доріжка 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Доріжка 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Доріжка 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Доріжка 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Доріжка 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Доріжка Twitch VOD\"\nBasic.Settings.Output.Adv.Encoder=\"Налаштування кодувальника\"\nBasic.Settings.Output.Adv.Recording=\"Записування\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Налаштування запису\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Тип запису\"\nBasic.Settings.Output.Adv.Recording.Type=\"Тип\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Стандартний\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Власний вивід (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Використовувати кодувальник трансляції)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Форматування назви файлу\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Перезаписувати, якщо файл існує\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Власний вивід (FFmpeg) надається без жодних гарантій.\\nРозгляньте можливість використовувати тип запису «Стандартний».\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"Тип виводу FFmpeg\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Вивід до URL-адреси\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Вивід у файл\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Поширені формати запису\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Усі файли\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Шлях або URL-адреса файлу\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Формат контейнера\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Аудіо\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"Відео\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Формат за замовчанням\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Опис формату контейнера\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Аудіо/відеокодек вгаданий зі шляху або URL-адреси файлу\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"кодувальник за замовчуванням\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Вимкнути кодувальник\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Кодувальник відео\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Параметри кодувальника (за наявності)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Кодувальник аудіо\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Параметри кодувальника (за наявності)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Параметри мультиплексора (за наявності)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Інтервал ключових кадрів (кадри)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Показати всі кодеки (навіть потенційно несумісні)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Налаштування FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Автоматичне розділення файлів\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Розділити за часом\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Розділити за розміром\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Розділяти лише вручну\"\nBasic.Settings.Output.SplitFile.Time=\"Час розділення\"\nBasic.Settings.Output.SplitFile.Size=\"Розмір розділення\"\nScreenshot=\"Вивід знімку екрана\"\nScreenshot.SourceHotkey=\"Вибране джерело знімку екрана\"\nScreenshot.StudioProgram=\"Знімок екрана (програма)\"\nScreenshot.Preview=\"Знімок екрана (попередній перегляд)\"\nScreenshot.Scene=\"Знімок екрана (сцена)\"\nScreenshot.Source=\"Знімок екрана (джерело)\"\nFilenameFormatting.TT.CCYY=\"Рік, чотири цифри\"\nFilenameFormatting.TT.YY=\"Рік, останні дві цифри (00-99)\"\nFilenameFormatting.TT.MM=\"Місяць як десяткове число (01-12)\"\nFilenameFormatting.TT.DD=\"День місяця, з нулем (01-31)\"\nFilenameFormatting.TT.hh=\"Години у 24-годинному форматі (00-23)\"\nFilenameFormatting.TT.mm=\"Хвилини (00-59)\"\nFilenameFormatting.TT.ss=\"Секунди (00-59)\"\nFilenameFormatting.TT.Percent=\"Знак %\"\nFilenameFormatting.TT.a=\"Скорочена назва дня тижня\"\nFilenameFormatting.TT.A=\"Повна назва дня тижня\"\nFilenameFormatting.TT.b=\"Скорочена назва місяця\"\nFilenameFormatting.TT.B=\"Повна назва місяця\"\nFilenameFormatting.TT.d=\"День місяця, з нулем (01-31)\"\nFilenameFormatting.TT.H=\"Години у 24-годинному форматі (00-23)\"\nFilenameFormatting.TT.I=\"Години у 12-годинному форматі (01-12)\"\nFilenameFormatting.TT.m=\"Місяць як десяткове число (01-12)\"\nFilenameFormatting.TT.M=\"Хвилини (00-59)\"\nFilenameFormatting.TT.p=\"Позначки «ранку» чи «вечора»\"\nFilenameFormatting.TT.s=\"Час у секундах від початку доби UNIX\"\nFilenameFormatting.TT.S=\"Секунди (00-59)\"\nFilenameFormatting.TT.y=\"Рік, останні дві цифри (00-99)\"\nFilenameFormatting.TT.Y=\"Рік\"\nFilenameFormatting.TT.z=\"Відхилення ISO 8601 від UTC у часовому поясі\"\nFilenameFormatting.TT.Z=\"Назва часового поясу або скорочення\"\nFilenameFormatting.TT.FPS=\"Кадрів на секунду\"\nFilenameFormatting.TT.CRES=\"Основна роздільність (canvas)\"\nFilenameFormatting.TT.ORES=\"Роздільність виводу (масштабована)\"\nFilenameFormatting.TT.VF=\"Відеоформат\"\nBasic.Settings.Video=\"Відео\"\nBasic.Settings.Video.Adapter=\"Відеоадаптер\"\nBasic.Settings.Video.BaseResolution=\"Основна роздільність (полотно)\"\nBasic.Settings.Video.ScaledResolution=\"Роздільність виводу (масштабована)\"\nBasic.Settings.Video.DownscaleFilter=\"Фільтр зменшення масштабу\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Роздільності збігаються, масштабування не потрібне]\"\nBasic.Settings.Video.FPSCommon=\"Поширені частоти кадрів\"\nBasic.Settings.Video.FPSInteger=\"Ціла частота кадрів\"\nBasic.Settings.Video.FPSFraction=\"Дробова частота кадрів\"\nBasic.Settings.Video.Numerator=\"Чисельник\"\nBasic.Settings.Video.Denominator=\"Знаменник\"\nBasic.Settings.Video.Renderer=\"Візуалізатор\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Експериментальний)\"\nBasic.Settings.Video.InvalidResolution=\"Неприпустима роздільна здатність. Має бути [ширина]x[висота] (наприклад, 1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Зараз відео виводиться. Вимкніть усі виведення, щоб змінити налаштування відео.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Білінійний (найшвидший, але розмитий якщо масштабувати)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Бікубічний (чітке масштабування, 16 зразків)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Ланцош (чітке масштабування, 36 зразків)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Усереднення площ (зважена сума, 4/6/9 зразків)\"\nBasic.Settings.Audio=\"Аудіо\"\nBasic.Settings.Audio.SampleRate=\"Частота дискретизації\"\nBasic.Settings.Audio.Channels=\"Канали\"\nBasic.Settings.Audio.Meters=\"Вимірювачі\"\nBasic.Settings.Audio.MeterDecayRate=\"Швидкість затухання\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Швидко\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Середня (PPM типу I)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Повільна (PPM типу II)\"\nBasic.Settings.Audio.PeakMeterType=\"Тип вимірювача піків\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Вибірковий піковий\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Точний піковий (більше використання ресурсів ЦП)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"ПОПЕРЕДЖЕННЯ: увімкнено об'ємний звук.\"\nBasic.Settings.Audio.MultichannelWarning=\"У процесі трансляції перевірте, чи підтримує ваш потоковий сервіс як приймання об'ємного звуку, так і відтворення об'ємного звуку. Наприклад, Facebook 360 L Live повністю підтримує об'ємний звук; YouTube Live підтримує прийом звуку у форматі 5.1 (і відтворення на телевізорах).\\n\\nАудіофільтри OBS сумісні з об'ємним звуком, хоча підтримка плагінів VST не гарантується.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Увімкнути об'ємний звук?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Справді увімкнути об'ємний звук?\"\nBasic.Settings.Audio.Devices=\"Загальні пристрої звуку\"\nBasic.Settings.Audio.DesktopDevice=\"Пристрій відтворення\"\nBasic.Settings.Audio.DesktopDevice2=\"Пристрій відтворення 2\"\nBasic.Settings.Audio.AuxDevice=\"Мікрофон/допоміжне аудіо\"\nBasic.Settings.Audio.AuxDevice2=\"Мікрофон/допоміжне аудіо 2\"\nBasic.Settings.Audio.AuxDevice3=\"Мікрофон/допоміжне аудіо 3\"\nBasic.Settings.Audio.AuxDevice4=\"Мікрофон/допоміжне аудіо 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Вимикати утримуванням клавіші\"\nBasic.Settings.Audio.PushToMuteDelay=\"Затримка вимкнення\"\nBasic.Settings.Audio.EnablePushToTalk=\"Вмикати утримуванням клавіші\"\nBasic.Settings.Audio.PushToTalkDelay=\"Затримка увімкнення\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Пристрій не під'єднаний або недоступний]\"\nBasic.Settings.Audio.Disabled=\"Вимкнуто\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Режим буферизації звуку з низькою затримкою (для виходів Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"ПОПЕРЕДЖЕННЯ: Увімкнена низька затримка буферизації звуку.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Низька затримка буферизації звуку може викликати збій звуку або зупинку відтворення з деяких джерел.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Увімкнути режим буферизації звуку з низькою затримкою?\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Ви дійсно хочете увімкнути режим низької затримки буферизації звуку?\"\nBasic.Settings.Accessibility=\"Доступність\"\nBasic.Settings.Accessibility.ColorOverrides=\"Вжити інші кольори\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Рамка джерела (вибір)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Рамка джерела (обрізання)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Рамка джерела (наведення)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Діапазон гучності мікшера (від -60 до -20 дБ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Діапазон гучності мікшера (від -20 до -9 дБ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Діапазон гучності мікшера (від -9 до -0 дБ)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Діапазон гучності мікшера (від -60 до -20 дБ) (активний)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Діапазон гучності мікшера (від -20 до -9 дБ) (активний)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Діапазон гучності мікшера (від -9 до -0 дБ) (активний)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Шаблон кольорів\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Типовий\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Власний\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Для людей зі зміненим кольоросприйняттям\"\nBasic.Settings.Advanced=\"Додатково\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Показувати попередження про активні трансляції при виході\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Пріоритет процесу\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Високий\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Вище нормального\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Нормальний\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Нижче нормального\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Бездіяльність\"\nBasic.Settings.Advanced.FormatWarning=\"Попередження: формати кольору, відмінні від NV12/P010, в першу чергу призначені для запису, і не рекомендуються для трансляцій. Трансляція може збільшити навантаження на ЦП через перетворення формату кольору.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Попередження: високоточні формати зазвичай використовують лише з колірними просторами HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Попередження: Rec. 2100 має використовувати формат з більшою точністю.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Формат кольору\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 біт, 4:2:0, 2 плани)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 біт, 4:2:0, 3 плани)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 біт, 4:4:4, 3 плани)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 біт, 4:2:0, 2 плани)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"І010 (10 біт, 4:2:0, 3 плани)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 біт, 4:2:2, 2 плани)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 біт, 4:4:4, 2 плани)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 біт)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Колірний простір\"\nBasic.Settings.Advanced.Video.ColorRange=\"Колірний діапазон\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Обмежено\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Повний\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Рівень білого SDR\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"Рівні номінального піка HDR\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Пристрій прослуховування\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"За замовчуванням\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Вимкнути приглушення звуку Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Затримка трансляції\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Тривалість\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Зберігати точку роз'єднання (збільшує затримку) під час відновлення з’єднання\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Розрахункове використання пам'яті: %1 МБ\"\nBasic.Settings.Advanced.Network=\"Мережа\"\nBasic.Settings.Advanced.Network.Disabled=\"Вибраний протокол трансляції не підтримує змінення параметрів мережі.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Прив'язати до IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP Родина\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Увімкнути оптимізації мережі\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Увімкнути TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Намагається регулюванням швидкості передачі зменшити вплив RTMP виводу на інші, чутливі до затримки застосунку в мережі.\\nЦе може збільшити шанс втрати кадрів на нестабільних з'єднаннях.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Поведінка фокусу гарячих клавіш\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Ніколи не вимикати гарячі клавіші\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Вимикати гарячі клавіші, коли головне вікно у фокусі\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Відключати гарячі клавіші, коли головне вікно знаходиться не у фокусі\"\nBasic.Settings.Advanced.AutoRemux=\"Автоматично ремультиплексувати в %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(записувати як mkv)\"\nBasic.AdvAudio=\"Розширені налаштування аудіо\"\nBasic.AdvAudio.ActiveOnly=\"Лише активні джерела\"\nBasic.AdvAudio.Name=\"Назва\"\nBasic.AdvAudio.Volume=\"Гучність\"\nBasic.AdvAudio.VolumeSource=\"Гучність для «%1»\"\nBasic.AdvAudio.Mono=\"Моно\"\nBasic.AdvAudio.MonoSource=\"Mono Downmix для «%1»\"\nBasic.AdvAudio.Balance=\"Баланс\"\nBasic.AdvAudio.BalanceSource=\"Баланс для «%1»\"\nBasic.AdvAudio.SyncOffset=\"Зсув синхронізації\"\nBasic.AdvAudio.SyncOffsetSource=\"Синхронізувати зсув для «%1»\"\nBasic.AdvAudio.Monitoring=\"Прослуховування звуку\"\nBasic.AdvAudio.Monitoring.None=\"Прослуховування вимкнено\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Тільки прослуховування (заглушити вивід)\"\nBasic.AdvAudio.Monitoring.Both=\"Прослуховування та вивід\"\nBasic.AdvAudio.MonitoringSource=\"Моніторинг звуку для «%1»\"\nBasic.AdvAudio.AudioTracks=\"Доріжки\"\nBasic.Settings.Hotkeys=\"Гарячі клавіші\"\nBasic.Settings.Hotkeys.Pair=\"Однакові комбінації клавіш задані до '%1' діють як перемикач\"\nBasic.Settings.Hotkeys.Filter=\"Фільтр\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Фільтрувати за гарячою клавішею\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Ці клавіші використовують ще одна або кілька інших дій, натисніть, щоб показати конфлікти\"\nBasic.Settings.Hotkeys.PleaseWait=\"Завантаження гарячих клавіш, будь ласка, зачекайте...\"\nBasic.Hotkeys.SelectScene=\"Перейти до сцени\"\nBasic.SystemTray.Show=\"Показати\"\nBasic.SystemTray.Hide=\"Приховати\"\nBasic.SystemTray.Message.Reconnecting=\"З'єднання розірвано. Відновлюю з’єднання…\"\nHotkeys.Left=\"Стрілка вліво\"\nHotkeys.Right=\"Стрілка вправо\"\nHotkeys.Up=\"Стрілка вверх\"\nHotkeys.Down=\"Стрілка вниз\"\nHotkeys.Menu=\"Меню\"\nHotkeys.Space=\"Пробіл\"\nHotkeys.MouseButton=\"Миша %1\"\nMute=\"Вимкнути звук\"\nUnmute=\"Увімкнути звук\"\nPush-to-mute=\"Вимкнути утримуванням клавіші\"\nPush-to-talk=\"Увімкнути утримуванням клавіші\"\nSceneItemShow=\"Показати '%1'\"\nSceneItemHide=\"Приховати '%1'\"\nOutputWarnings.NoTracksSelected=\"Ви повинні вибрати хоча б одну доріжку\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Помилка налаштувань виводу\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Всі виходи повинні мати принаймні одну вибрану звукову доріжку.\"\nOutputWarnings.MP4Recording=\"Попередження: записи, збережені у форматі MP4/MOV, які не вдасться завершити (наприклад, внаслідок BSOD, втрат електроенергії тощо), не підлягають відновленню. Якщо ви хочете записати кілька аудіозаписів, подумайте про використання MKV та зробіть ремультиплексування запису в MP4/MOV після завершення запису (Файл → Ремультиплексувати записи)\"\nOutputWarnings.CannotPause=\"Попередження: неможливо призупинити записування, якщо вибрано «(Використовувати кодувальник трансляції)»\"\nOutputWarnings.CodecIncompatible=\"Вибір аудіо- чи відеокодувальників скинуто через несумісність. Будь ласка, виберіть зі списку сумісний кодувальник.\"\nCodecCompat.Incompatible=\"(несумісний із %1)\"\nCodecCompat.CodecPlaceholder=\"Вибрати кодувальник...\"\nCodecCompat.ContainerPlaceholder=\"Вибрати формат...\"\nCodecCompat.CodecMissingOnExit.Title=\"Кодувальник не вибрано\"\nCodecCompat.CodecMissingOnExit.Text=\"Принаймні один відео- або аудіокодувальник не встановлений. Переконайтеся, що ви вибрали кодувальники як для запису, так і для трансляцій.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Формат не вибрано\"\nCodecCompat.ContainerMissingOnExit.Text=\"Не вибрано формат запису. Виберіть формат запису, сумісний з вибраним кодувальником трансляції.\"\nFinalScene.Title=\"Видалення сцени\"\nFinalScene.Text=\"Тут має бути хоча б одна сцена.\"\nNoSources.Title=\"Немає джерел\"\nNoSources.Text=\"Схоже, жодних джерел відео ще не додано, тому на виводі буде лише порожній екран. Ви впевнені, що хочете це зробити?\"\nNoSources.Text.AddSource=\"Ви можете будь-коли додати джерела, натискаючи піктограму + під панеллю «Джерела» у головному вікні.\"\nNoSources.Label=\"У вас немає джерел.\\nКлацніть на кнопку + внизу,\\nчи додайте його в контекстному меню.\"\nChangeBG=\"Встановити колір\"\nCustomColor=\"Спеціальний колір\"\nBrowserSource.EnableHardwareAcceleration=\"Увімкнути апаратне прискорення для джерела «Браузер»\"\nAbout=\"Про програму\"\nAbout.Info=\"OBS Studio – це вільне та відкрите програмне забезпечення для запису відео та трансляцій в прямому ефірі.\"\nAbout.Donate=\"Зробити внесок\"\nAbout.GetInvolved=\"Взяти участь\"\nAbout.Authors=\"Автори\"\nAbout.License=\"Ліцензія\"\nAbout.Error=\"Помилка! Неможливо прочитати файл.\\n\\nПерейти до: %1\"\nAbout.Contribute=\"Підтримати проєкт OBS\"\nAddUrl.Title=\"Додати джерело через URL-адресу\"\nAddUrl.Text=\"Ви перетягнули URL-адресу в OBS. Це автоматично додасть посилання як джерело. Продовжити?\"\nResizeOutputSizeOfSource=\"Масштабувати вивід (розмір джерела)\"\nResizeOutputSizeOfSource.Text=\"Роздільна здатність полотна та виводу будуть змінені на роздільність поточного джерела.\"\nResizeOutputSizeOfSource.Continue=\"Бажаєте продовжити?\"\nPreviewTransition=\"Перегляд переходу\"\nImporter=\"Імпортер колекції сцен\"\nImporter.SelectCollection=\"Виберіть колекцію сцен\"\nImporter.Collection=\"Колекція сцен\"\nImporter.HelpText=\"Додайте файли до цього вікна для імпорту колекції сцен з OBS чи інших підтримуваних програм.\"\nImporter.Path=\"Шлях до колекції\"\nImporter.Program=\"Виявлений застосунок\"\nImporter.AutomaticCollectionPrompt=\"Автоматичний пошук колекцій сцен\"\nImporter.AutomaticCollectionText=\"OBS може автоматично знаходити колекції сцен для імпорту з підтримуваних сторонніх програм. Бажаєте, щоб OBS автоматично знаходив для вас такі колекції?\\n\\nВи можете змінити це пізніше у Налаштування > Загальні > Імпортери.\"\nImporter.SelectFile=\"Огляд...\"\nRestart=\"Перезапуск\"\nNeedsRestart=\"OBS Studio потрібно перезапустити. Перезапустити зараз?\"\nLoadProfileNeedsRestart=\"Профіль містить налаштування, які вимагають перезапуску OBS:\\n%1\\n\\nПерезапустити OBS, щоб активувати ці налаштування?\"\nContextBar.NoSelectedSource=\"Джерело не вибрано\"\nContextBar.MediaControls.PlayMedia=\"Відтворити\"\nContextBar.MediaControls.PauseMedia=\"Пауза\"\nContextBar.MediaControls.StopMedia=\"Зупинити\"\nContextBar.MediaControls.RestartMedia=\"Перезапустити\"\nContextBar.MediaControls.PlaylistNext=\"Наступний у списку відтворення\"\nContextBar.MediaControls.PlaylistPrevious=\"Попередній у списку відтворення\"\nContextBar.MediaControls.BlindSeek=\"Віджет пошуку медіафайлів\"\nYouTube.Auth.Ok=\"Авторизацію виконано успішно.\\nМожете закрити цю сторінку.\"\nYouTube.Auth.NoCode=\"Процес авторизації не виконано.\"\nYouTube.Auth.NoChannels=\"Вибраний обліковий запис не має доступних каналів\"\nYouTube.Auth.WaitingAuth.Title=\"Авторизація користувача YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Будь ласка, завершіть авторизацію у браузері.<br>Якщо браузер не відкрито, перейдіть за цим посиланням і завершіть авторизацію:<br>%1\"\nYouTube.AuthError.Text=\"Не вдалося отримати інформацію про канал: %1.\"\nYouTube.Actions.WindowTitle=\"Налаштування YouTube Broadcast - Канал: %1\"\nYouTube.Actions.CreateNewEvent=\"Створити нову трансляцію\"\nYouTube.Actions.ChooseEvent=\"Виберіть наявну трансляцію\"\nYouTube.Actions.Title=\"Назва*\"\nYouTube.Actions.MyBroadcast=\"Моя трансляція\"\nYouTube.Actions.Description=\"Опис\"\nYouTube.Actions.Privacy=\"Приватність*\"\nYouTube.Actions.Privacy.Private=\"Приватне\"\nYouTube.Actions.Privacy.Public=\"Загальнодоступне\"\nYouTube.Actions.Privacy.Unlisted=\"Поза списком\"\nYouTube.Actions.Category=\"Категорія\"\nYouTube.Actions.Thumbnail=\"Мініатюра\"\nYouTube.Actions.Thumbnail.SelectFile=\"Вибрати файл...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Файл не вибрано\"\nYouTube.Actions.Thumbnail.ClearFile=\"Очистити\"\nYouTube.Actions.MadeForKids=\"Це відео створено для дітей?*\"\nYouTube.Actions.MadeForKids.Yes=\"Так, його створено для дітей\"\nYouTube.Actions.MadeForKids.No=\"Ні, його створено не для дітей\"\nYouTube.Actions.AdditionalSettings=\"Додаткові налаштування\"\nYouTube.Actions.Latency=\"Затримка\"\nYouTube.Actions.Latency.Normal=\"Звичайнa\"\nYouTube.Actions.Latency.Low=\"Низька\"\nYouTube.Actions.Latency.UltraLow=\"Дуже низька\"\nYouTube.Actions.EnableAutoStart=\"Увімкнути автозапуск\"\nYouTube.Actions.EnableAutoStop=\"Увімкнути автостоп\"\nYouTube.Actions.AutoStartStop.TT=\"Вказує на те, чи має ця запланована трансляція починатися автоматично\"\nYouTube.Actions.EnableDVR=\"Увімкнути відеозаписувач\"\nYouTube.Actions.360Video=\"360 відео\"\nYouTube.Actions.ScheduleForLater=\"Запланувати на потім\"\nYouTube.Actions.RememberSettings=\"Запам'ятати налаштування\"\nYouTube.Actions.Create_Ready=\"Створити трансляцію\"\nYouTube.Actions.Create_GoLive=\"Створити й почати трансляцію\"\nYouTube.Actions.Choose_Ready=\"Вибрати трансляцію\"\nYouTube.Actions.Choose_GoLive=\"Вибрати й почати трансляцію\"\nYouTube.Actions.Create_Schedule=\"Запланувати трансляцію\"\nYouTube.Actions.Create_Schedule_Ready=\"Запланувати та вибрати трансляцію\"\nYouTube.Actions.Dashboard=\"Відкрити Студію YouTube\"\nYouTube.Actions.Error.Title=\"Помилка при створенні трансляції\"\nYouTube.Actions.Error.Text=\"Помилка доступу до YouTube '%1'.<br/>Докладний опис помилки можна знайти на <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Помилка доступу до YouTube. Перевірте з'єднання з інтернетом чи доступ до сервера YouTube.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Помилка створення трансляції '%1'.<br/>Докладний опис помилок можна знайти на <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Стрім не створено. Повторно під'єднайте обліковий запис.\"\nYouTube.Actions.Error.YouTubeApi=\"Помилка YouTube API. Перегляньте додаткові відомості у файлі журналу.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Вибрану трансляцію не знайдено.\"\nYouTube.Actions.Error.FileMissing=\"Вибраний файл не існує.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Не вдалося відкрити вибраний файл.\"\nYouTube.Actions.Error.FileTooLarge=\"Вибраний файл завеликий (обмеження: 2 МіБ).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Не вдалося виконати перехід у трансляції: %1<br/><br/>Якщо ця помилка залишається, <a href='https://studio.youtube.com/video/%2/livestreaming'>відкрийте трансляцію в Студії YouTube</a> і спробуйте вручну.\"\nYouTube.Actions.Error.BroadcastTestStarting=\"У трансляції виконується перехід до тестової сцени, це може тривати деякий час. Будь ласка, повторіть спробу за 10-30 секунд.\"\nYouTube.Actions.EventsLoading=\"Завантаження списку подій...\"\nYouTube.Actions.EventCreated.Title=\"Подію створено\"\nYouTube.Actions.EventCreated.Text=\"Подію успішно створено.\"\nYouTube.Actions.Stream=\"Трансляція\"\nYouTube.Actions.Stream.ScheduledFor=\"Заплановано на %1\"\nYouTube.Actions.Stream.Resume=\"Поновити перерваний потік\"\nYouTube.Actions.Stream.YTStudio=\"Автоматично створено YouTube Studio\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Створюється нова трансляція наживо, зачекайте...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Потрібен ручний запуск\"\nYouTube.Actions.AutoStartStreamingWarning=\"Автозапуск для цієї події вимкнено, щоб почати трансляцію, натисніть \\\"Розпочати трансляцію\\\".\"\nYouTube.Actions.AutoStopStreamingWarning=\"Ви не зможете відновити з'єднання.<br>Ваш стрім буде зупинено, і ви більше не будете у прямому етері.\"\nYouTube.Errors.liveStreamingNotEnabled=\"На вибраному каналі YouTube пряму трансляцію не увімкнено.<br/><br/>Щоб отримати додаткові відомості, див. <a href='https://www.youtube.com/features'>youtube.com/features</a>.\"\nYouTube.Errors.livePermissionBlocked=\"На вибраному каналі YouTube пряма трансляція недоступна.<br/>Зверніть увагу, що після увімкнення її в налаштуваннях каналу, трансляція стане доступною протягом 24 годин.<br/><br/>Докладніше див. <a href='https://www.youtube.com/features'>youtube.com/features</a>.\"\nYouTube.Errors.errorExecutingTransition=\"Перехід не вдався через помилку в бекенді. Будь ласка, спробуйте ще раз за кілька секунд.\"\nYouTube.Errors.errorStreamInactive=\"YouTube не отримує дані для вашого потоку. Перевірте конфігурацію і спробуйте ще раз.\"\nYouTube.Errors.invalidTransition=\"Спроба переходу була невдалою. Можливо, тому що потік не завершив попереднього переходу. Зачекайте кілька секунд і повторіть спробу.\"\nYouTube.DocksRemoval.Title=\"Очистити застарілі панелі браузера YouTube\"\nYouTube.DocksRemoval.Text=\"Ці панелі браузера буде вилучено як застарілі:\\n\\n%1\\nВикористовуйте замість них \\\"Docks/YouTube Live Control Room\\\".\"\nConfigDownload.WarningMessageTitle=\"Попередження\"\nFailedToStartStream.MissingConfigURL=\"Для поточної служби немає URL-адреси конфігурації\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"Не вказано власну URL-адресу RTMP\"\nFailedToStartStream.InvalidCustomConfig=\"Некоректна власна конфігурація\"\nFailedToStartStream.MissingCanvas=\"Налаштоване додаткове полотно відсутнє\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Для поточного сервісу немає URL-адреси конфігурації\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Не вдалося створити багатодоріжковий відео вивід RTMP\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC недоступний.\\n\\nНе вдалося знайти тип кодувальника '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Не вдалося створити кодувальник відео '%1' (тип: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Не вдалося отримати інформацію про відео OBS під час створення кодувальника '%1' (тип: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Не вдалося створити кодувальник звуку\"\nFailedToStartStream.NoRTMPURLInConfig=\"Конфігурація не містить URL-адреси цільового потоку RTMP(S)\"\nFailedToStartStream.FallbackToDefault=\"Не вдалося запустити трансляцію за допомогою %1; ви хочете спробувати ще раз, використовуючи налаштування одного кодування?\"\nFailedToStartStream.ConfigRequestFailed=\"Не вдалося отримати конфігурацію з %1<br><br>Помилка HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Отримано невідоме значення стану '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nБажаєте продовжити трансляцію без %1?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nБажаєте продовжити трансляцію?\"\nFailedToStartStream.MissingEncoderConfigs=\"Конфігурація для запуску не містить конфігурацій кодувальників\"\nFailedToStartStream.InvalidEncoderConfig=\"Конфігурація для запуску містить недійсну конфігурацію кодувальника\"\nFailedToStartStream.StatusMissingHTML=\"Запит на запуск повернув невизначену помилку\"\nFailedToStartStream.NoConfigSupplied=\"Відсутня конфігурація\"\nMultitrackVideo.Info=\"%1 автоматично оптимізує налаштування для кодування та надсилання відео різної якості. Вибравши цей параметр, ви надішлете %2 інформацію про ваш комп'ютер і налаштування програмного забезпечення.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Несумісні налаштування\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 наразі несумісний з:\\n\\n%2\\nЩоб продовжити трансляцію з %1, вимкніть несумісні налаштування:\\n\\n%3\\nта розпочніть трансляцію заново.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Вимкнути для цієї трансляції та розпочати трансляцію\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Оновити налаштування і почати трансляцію\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 наразі несумісний з [Аудіо → Загальні → Канали], для якого встановлено значення '%2', '%3'.\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Аудіо → Загальні → Канали] потрібно встановити на '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 вимагає декількох різних налаштувань для [Аудіо → Загальні → Канали]\"\nBasic.OpenPluginManager=\"Менеджер плагінів\"\nPluginManager=\"Менеджер плагінів\"\nPluginManager.Restart=\"Для застосування змін плагіну, OBS Studio слід перезапустити\"\nPluginManager.MissingPlugin=\"[ПЛАГІНИ НЕ ЗНАЙДЕНО]\"\nPluginManager.Section.Discover=\"Огляд\"\nPluginManager.Section.Manage=\"Установлено\"\nPluginManager.Section.Updates=\"Оновлення\"\nPluginManager.Section.Manage.Title=\"Керування увімкненими плагінами\"\n"
  },
  {
    "path": "frontend/data/locale/ur-PK.ini",
    "content": "Language=\"اُردُو\"\nOK=\"ٹھیک ہے\"\nApply=\"لاگو کریں\"\nCancel=\"منسوخ\"\nClose=\"بند کریں\"\nSave=\"محفوظ\"\nDiscard=\"رد کریں\"\nDisable=\"غیر فعال\"\nYes=\"جی ہاں\"\nNo=\"نہیں\"\nAdd=\"ڈالیں\"\nRemove=\"مٹانا\"\nRename=\"نام بدلنا\"\nInteract=\"بات چیت\"\nFilters=\"فلٹرز\"\nProperties=\"خصوصیات\"\nMoveUp=\"اوپر لائیں\"\nMoveDown=\"نیچے لائیں\"\nSettings=\"ترتیبات\"\nDisplay=\"ڈسپلے\"\nName=\"نام\"\nExit=\"خارج\"\nMixer=\"آواز مکسر\"\nBrowse=\"تلاش\"\nMono=\"مونو\"\nStereo=\"سٹیریو\"\nDroppedFrames=\"گرائے گئے فریم %1 (%2%)\"\nClear=\"مٹائیں\"\nRevert=\"واپس\"\nShow=\"دکھائیں\"\nHide=\"چھپائیں\"\nUnhideAll=\"سب دکھائیں\"\nUntitled=\"غیر خطاب یافتہ\"\nNew=\"نیا\"\nDuplicate=\"نقل بنائیں\"\nEnable=\"چلائیں\"\nDisableOSXVSync=\"macOS V-Sync کو غیر فعال کریں\"\nResetOSXVSyncOnExit=\"باہر نکلیں پرmacOS V-Sync ری سیٹ کریں\"\nHighResourceUsage=\"انکوڈنگ زیادہ اوورلوڈ! ویڈیو ترتیبات کو تبدیل کرنے یا تیزی سے انکوڈنگ کے پیش سیٹ کا استعمال کرتے ہوئے غور کریں.\"\nTransition=\"منتقلی\"\nLeft=\"بائیں\"\nRight=\"دائیں\"\nTop=\"اوپر\"\nBottom=\"نیچے\"\nReset=\"واپس کریں\"\nHours=\"گھنٹے\"\nMinutes=\"منٹ\"\nSeconds=\"سیکنڈز\"\nImport=\"درآمد کریں\"\nExport=\"برآمد کریں\"\nCopy=\"نقل\"\nPaste=\"پیسٹ کریں\"\nPasteDuplicate=\"جوڑیں (مثنی)\"\nNext=\"اگلا\"\nBack=\"واپس\"\nDefaults=\"طے شدہ\"\nNone=\"کوئی نہیں\"\nStudioMode.Program=\"پروگرام\"\nGroup=\"گروہ\"\nDoNotShowAgain=\"دوبارہ مت دکھائیں\"\nFullscreen=\"پوری اسکرین\"\nAlreadyRunning.Title=\"OBS پہلے سے چالو ہے۔\"\nAlreadyRunning.LaunchAnyway=\"ویسے بھی شروع\"\nAuth.Authing.Title=\"توثیق...\"\nAuth.Authing.Text=\"%1 سے توثیق کیا جا رہا ہے ، براہِ مہربانی انتظار فرمائے...\"\nAuth.AuthFailure.Title=\"تصدیق میں ناکامی\"\nAuth.Chat=\"گفتگو\"\nAuth.StreamInfo=\"نشر یہ معلومات\"\nTwitchAuth.TwoFactorFail.Text=\"OBS آپ کے Twitch کھاتے کے ساتھ سمبندھ کرنے سے ناکام رہا۔ براہ مہربانی اپنی <a href='https://www.twitch.tv/settings/security'>Twitch محفوظ ترتیبات</a> میں دیکھیں اگر two-factor authentication لاگو ہے، چیونکہ نشر کرنے کے لئے یہ لازم ہے۔\"\nBandwidthTest.Region=\"مقام\"\nBandwidthTest.Region.US=\"ریاست متحد امریکہ\"\nBandwidthTest.Region.EU=\"یورپ\"\nBandwidthTest.Region.Asia=\"ایشیا\"\nBandwidthTest.Region.Other=\"دیگر\"\nBasic.AutoConfig.ApplySettings=\"تبدیلیاں لاگو کریں\"\nBasic.AutoConfig.VideoPage=\"تصویر ترتیبات\"\nBasic.AutoConfig.StreamPage=\"نشر یہ معلومات\"\nBasic.AutoConfig.StreamPage.SubTitle=\"اپنی نشریہ کی معلومات ڈالیں\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"کھاتا سمبندھ کریں (بہتر)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"یہ تبدیلی فوراً لاگو ہو گی۔ کیا آپ مطمئن ہیں کہ آپ اپنے کھاتے سے سمبندھ توڑنا چاہتے ہیں؟\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"مزید معلومات\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"سارے دکھائیں...\"\nBasic.AutoConfig.StreamPage.Server=\"سرور\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"نشریات تنبیع\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"%1 سے رابطہ کیا جا رہا ہے...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"کسی بھی سرور سے سمبندھ ناممکن رہا، اپنا انٹرنیٹ کنیکشن کی پڑتال کریں اور دوبارہ کوشش کریں۔\"\nBasic.Stats.Output.Stream=\"نشریہ\"\nBasic.Stats.Status=\"حال\"\nBasic.Stats.Status.Live=\"براہِ راست\"\nBasic.Stats.Status.Reconnecting=\"رابطہ دوبارہ کیا جا رہا ہے\"\nUpdater.UpdateNow=\"تازہ کریں\"\nUpdater.RemindMeLater=\"بعد میں پتائیں\"\nTitleBar.Scenes=\"مناظر\"\nNameExists.Title=\"نام پہلے سے موجود ہے\"\nNameExists.Text=\"یہ نام پہلے سے موجود ہے۔\"\nNoNameEntered.Title=\"برائے مہربانی ایک درست نام درج کریں\"\nNoNameEntered.Text=\"خالی نام درج نہیں ہو سکتے۔\"\nConfirmStart.Title=\"نشریہ آغاز کریں؟\"\nConfirmStart.Text=\"کیا آپ بالکل نشریہ شروع کرنا چاہتے ہیں؟\"\nConfirmStop.Title=\"نشریہ روکیں؟\"\nConfirmStop.Text=\"کیا آپ نشریہ کو بالکل روکنا چاہتے ہیں؟\"\nConfirmStopRecord.Title=\"روکرڈنگ بند کریں؟\"\nConfirmRemove.Title=\"مٹان کی تصدیق\"\nOutput.StartStreamFailed=\"نشریہ شروع کرنے میں ناکامی\"\nOutput.ConnectFail.Title=\"سمبندھ کرنے میں ناکامی\"\nOutput.ConnectFail.ConnectFailed=\"سرور سے سمبندھ کرنے میں ناکامی\"\nOutput.ConnectFail.Disconnected=\"سرور سے رابطہ ترک۔\"\nOutput.RecordFail.Title=\"رکارڈنگ شروع کرنے میں ناکامی\"\nOutput.RecordError.Title=\"رکارڈنگ میں مسلہ\"\nBasic.AuxDevice1=\"گفتار\"\nBasic.AuxDevice2=\"گفتار ۲\"\nBasic.AuxDevice3=\"گفتار ۳\"\nBasic.AuxDevice4=\"گفتار ۴\"\nBasic.Scene=\"منظر\"\nBasic.Main.AddSceneDlg.Title=\"منظر بنائیں\"\nBasic.Main.AddSceneDlg.Text=\"منظر کو نام دیں\"\nBasic.Main.DefaultSceneName.Text=\"منظر %1\"\nBasic.Main.MixerRename.Title=\"ادخال آواز نام تبدیلی\"\nBasic.Main.MixerRename.Text=\"ادخال آواز کا نام تبدیل کریں\"\nBasic.SourceSelect.CreateNew=\"نیا بنائیں\"\nBasic.SourceSelect.AddExisting=\"موجودہ استعمال کریں\"\nBasic.PropertiesWindow.SelectColor=\"رنگ چنیں\"\nBasic.PropertiesWindow.SelectFont=\"لکھائی چنیں\"\nBasic.PropertiesWindow.ConfirmTitle=\"ترتیبات میں تبدیلی\"\nBasic.PropertiesWindow.Confirm=\"کچھ تبدیلیوں کو محفوظ نہیں کیا گیا، کیا آپ ان کو رکھنا چاہتے ہیں؟\"\nBasic.StatusBar.Reconnecting=\"رابطہ ترک، %2 سیکنڈ میں سمبندھ کرنے کی کوشش جاری (کوشش %1)\"\nBasic.StatusBar.AttemptingReconnect=\"سمبندھ کرنے کی کوشش... (کوشش %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"رابطہ قائم\"\nBasic.Main.Scenes=\"مناظر\"\nBasic.Main.StartStreaming=\"نشر کریں\"\nBasic.Main.StopStreaming=\"نشریہ روکیں\"\nBasic.Main.StoppingStreaming=\"نشریہ روکی جا رہی ہے...\"\nBasic.Main.ForceStopStreaming=\"نشریہ روکیں (اسی وقت)\"\nBasic.MainMenu.File.Settings=\"&ترتیبات\"\nBasic.MainMenu.File.Exit=\"خراج (&X)\"\nBasic.MainMenu.Edit=\"&تبدیل\"\nBasic.MainMenu.Edit.Scale.Output=\"مخرج (%1x%2)\"\nBasic.MainMenu.View=\"&دیدار\"\nBasic.MainMenu.Tools=\"&آلات\"\nBasic.MainMenu.Help=\"&مدد\"\nBasic.MainMenu.Help.Website=\"&ویبسائٹ پہ جائیں\"\nBasic.Settings.ConfirmTitle=\"تبدیلیوں کی تصدیق\"\nBasic.Settings.Confirm=\"کچھ تبدیلیاں محفوظ نہیں۔ محفوظ کریں؟\"\nBasic.Settings.General=\"عام\"\nBasic.Settings.General.Language=\"زبان\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"مناظر کے نام دکھائیں\"\nBasic.Settings.Stream.Custom.Username=\"صارف نام\"\nBasic.Settings.Stream.Custom.Password=\"پاسورڈ\"\nBasic.Settings.Stream.TTVAddon.None=\"کوئی نہیں\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV اور FrankerFaceZ\"\nBasic.Settings.Output=\"درآمد\"\nBasic.Settings.Output.Mode=\"درآمد تریقا\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg درآمد\"\nBasic.Settings.Output.Simple.RecordingQuality=\"رکارڈنگ میار\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"نشریہ کے برابر\"\nBasic.Settings.Output.Adv.Streaming=\"براہ راست\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"آواز\"\nBasic.Settings.Video=\"تصویر\"\nBasic.Settings.Audio=\"آواز\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"تیز\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"عام (Type I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"آہستہ (Type II PPM)\"\nBasic.Settings.Audio.AuxDevice=\"گفتار/مدخل آواز\"\nBasic.Settings.Audio.AuxDevice2=\"گفتار/مدخل آواز ۲\"\nBasic.Settings.Audio.AuxDevice3=\"گفتار/مدخل آواز ۳\"\nBasic.Settings.Audio.AuxDevice4=\"گفتار/مدخل آواز ۴\"\nBasic.AdvAudio.Name=\"نام\"\nBasic.AdvAudio.Volume=\"آواز کی سطح\"\nBasic.AdvAudio.VolumeSource=\"'%1' کیلئے شور کی سطح\"\nFinalScene.Title=\"منظر دکھائیں\"\nFinalScene.Text=\"کم از کم ایک منظر لازمی ہے۔\"\n"
  },
  {
    "path": "frontend/data/locale/vi-VN.ini",
    "content": "Language=\"Tiếng Việt\"\nOK=\"Đồng ý\"\nApply=\"Áp dụng\"\nCancel=\"Hủy\"\nClose=\"Đóng\"\nSave=\"Lưu\"\nDiscard=\"Bỏ\"\nDisable=\"Tắt\"\nYes=\"Có\"\nNo=\"Không\"\nAdd=\"Thêm\"\nRemove=\"Xóa\"\nRename=\"Đổi tên\"\nInteract=\"Tương tác\"\nFilters=\"Bộ lọc\"\nProperties=\"Thuộc tính\"\nMoveUp=\"Di chuyển lên\"\nMoveDown=\"Di chuyển xuống\"\nSettings=\"Thiết đặt\"\nDisplay=\"Hiển thị\"\nName=\"Tên\"\nExit=\"Thoát\"\nMixer=\"Bộ lọc âm thanh\"\nBrowse=\"Chọn đường dẫn\"\nMono=\"Âm thanh đơn\"\nStereo=\"Âm thanh nổi\"\nDroppedFrames=\"Khung bị rớt %1 (%2%)\"\nProjector.Open.Program=\"Mở khung chiếu chương trình\"\nProjector.Open.Preview=\"Mở khung chiếu xem trước\"\nProjector.Open.Scene=\"Mở khung chiếu cảnh\"\nProjector.Open.Source=\"Mở khung chiếu nguồn\"\nProjector.Open.Multiview=\"Mở nhiều góc nhìn\"\nProjector.Display=\"Màn hình: %1\"\nProjector.Window=\"Cửa sổ mới\"\nProjector.Title=\"Khung chiếu\"\nProjector.Title.Scene=\"Cảnh: %1\"\nProjector.Title.Source=\"Nguồn: %1\"\nProjector.Title.Multiview=\"Nhiều góc nhìn\"\nProjector.ResizeWindowToContent=\"Chỉnh cửa sổ khít với nội dung\"\nClear=\"Xóa\"\nRevert=\"Phục hồi\"\nShow=\"Hiện\"\nHide=\"Ẩn\"\nUnhideAll=\"Hiện tất cả\"\nUntitled=\"Không tên\"\nNew=\"Mới\"\nDuplicate=\"Tạo bản sao\"\nEnable=\"Bật\"\nDisableOSXVSync=\"Tắt macOS V-Sync\"\nResetOSXVSyncOnExit=\"Tái khởi động macOS V-Sync khi tắt\"\nHighResourceUsage=\"Bộ biên mã đang bị quá tải. Bạn nên căn chỉnh chất lượng video hay sử dụng một bộ chuyển mã nhanh hơn.\"\nTransition=\"Chuyển cảnh\"\nQuickTransitions=\"Bộ cảnh chuyển\"\nFadeToBlack=\"Đen dần\"\nLeft=\"Trái\"\nRight=\"Phải\"\nTop=\"Trên\"\nBottom=\"Dưới\"\nReset=\"Tải lại\"\nHours=\"Giờ\"\nMinutes=\"Phút\"\nSeconds=\"Giây\"\nDeprecated=\"Không còn được hỗ trợ\"\nReplayBuffer=\"Lưu bộ nhớ đệm\"\nImport=\"Nhập\"\nExport=\"Xuất\"\nCopy=\"Sao chép\"\nPaste=\"Dán\"\nPasteReference=\"Dán (tham chiếu)\"\nPasteDuplicate=\"Dán (bản sao)\"\nRemuxRecordings=\"Ghép lại các bản ghi\"\nNext=\"Tiếp tục\"\nBack=\"Quay lại\"\nDefaults=\"Mặc định\"\nRestoreDefaults=\"Mặc định\"\nHideMixer=\"Ẩn trong bộ lọc\"\nTransitionOverride=\"Ghi đè cảnh chuyển\"\nShowTransition=\"Cảnh chuyển đang hiện\"\nHideTransition=\"Cảnh chuyển đang ẩn\"\nNone=\"Không\"\nStudioMode.Preview=\"Xem trước\"\nStudioMode.Program=\"Chương trình\"\nStudioMode.PreviewSceneName=\"Xem trước: %1\"\nStudioMode.ProgramSceneName=\"Chương trình: %1\"\nShowInMultiview=\"Hiển thị đa góc nhìn\"\nVerticalLayout=\"Bố cục dọc\"\nGroup=\"Tạo nhóm\"\nDoNotShowAgain=\"Không hiện thị lại\"\nDefault=\"(Mặc định)\"\nCalculating=\"Đang tính...\"\nFullscreen=\"Toàn màn hình\"\nWindowed=\"Dạng cửa sổ\"\nRefreshBrowser=\"Làm mới\"\nAspectRatio=\"Tỉ lệ khung hình <b>%1:%2</b>\"\nLockVolume=\"Khoá âm lượng\"\nLogViewer=\"Xem trữ liệu\"\nShowOnStartup=\"Hiện thị khi khởi động\"\nOpenFile=\"Mở tệp\"\nAddScene=\"Thêm cảnh\"\nAddSource=\"Thêm nguồn\"\nRemoveScene=\"Xóa phân cảnh đã chọn\"\nRemoveSource=\"Xóa nguồn đã chọn\"\nMoveSceneUp=\"Chuyển phân cảnh lên trên\"\nMoveSceneDown=\"Chuyển phân cảnh xuống dưới\"\nMoveSourceUp=\"Chuyển nguồn lên trên\"\nMoveSourceDown=\"Chuyển nguồn xuống dưới\"\nSourceProperties=\"Mở cài đặt nguồn\"\nSourceFilters=\"Mở cài đặt hiệu ứng nguồn\"\nMixerToolbarMenu=\"Bảng chọn bộ trộn âm thanh\"\nSceneFilters=\"Mở bộ lọc phân cảnh\"\nList=\"Danh sách\"\nGrid=\"Lưới\"\nAutomatic=\"Tự động\"\nComingSoon=\"Sắp ra mắt\"\nPluginsFailedToLoad.Title=\"Lỗi nạp trình cắm\"\nPluginsFailedToLoad.Text=\"Những trình cắm OBS sau đây đang bị lỗi:\\n\\n%1\\nVui lòng cập nhật hoặc xóa các trình cắm đó đi.\"\nAlreadyRunning.Title=\"OBS hiện đang chạy\"\nAlreadyRunning.Text=\"OBS hiện đang chạy. Trừ phi bạn muốn tiếp tục, xin vui lòng tắt các phiên OBS khác trước khi khởi chạy một cửa sổ mới. Nếu OBS đang chạy trong trạng thái thu nhỏ trên thanh tác vụ, hãy kiểm tra lại rồi hẵng khởi chạy.\"\nAlreadyRunning.LaunchAnyway=\"Tiếp tục khởi động\"\nCrashHandling.Buttons.LaunchSafe=\"Chạy trong chế độ an toàn\"\nCrashHandling.Buttons.LaunchNormal=\"Chạy trong chế độ bình thường\"\nCrashHandling.Errors.UploadJSONError=\"Có lỗi xảy ra trong khi cố gắng tải lên nhật kí sự cố gần nhất. Xin vui lòng thử lại sau.\"\nCrashHandling.Errors.Title=\"Lỗi tải lên nhật kí sự cố\"\nSafeMode.Restart=\"Bạn có muốn khởi động lại OBS ở Chế độ an toàn (đã tắt trình cắm, tập lệnh và WebSockets của bên thứ ba) không?\"\nSafeMode.RestartNormal=\"Bạn có muốn khởi động lại OBS trong chế độ bình thường không?\"\nChromeOS.Title=\"Nền tảng không được hỗ trợ\"\nChromeOS.Text=\"Có vẻ như OBS đang được chạy trong ChromeOS. Nền tảng này không được hỗ trợ.\"\nWine.Title=\"Đã phát hiện Wine\"\nWine.Text=\"OBS không được hỗ trợ trên Wine vì nhiều tính năng chẳng hạn như ghi hình sẽ không hoạt động hoặc chỉ hoạt động trong tầm hạn chế.<br><br>Bạn nên sử dụng một phiên bản khác của OBS, ví dụ như <a href='https://flathub.org/apps/details/com.obsproject.Studio'>phiên bản Flatpak</a> hoặc gói cài đặt thuộc hệ điều hành của bạn.\"\nDockCloseWarning.Title=\"Cửa sổ cố định đã đóng\"\nDockCloseWarning.Text=\"Bạn vừa đóng một cửa sổ cố định. Nếu bạn muốn tái hiển thị cửa sổ, hãy sử dụng tuỳ chọn Cố định trên thanh bảng chọn.\"\nExtraBrowsers=\"Tuỳ chỉnh định cố trình duyệt\"\nExtraBrowsers.Info=\"Thêm định cố bằng việc đặt tên và URL, rồi chọn Áp dụng hoặc Đóng để áp dụng định cố. Bạn có thể thêm hoặc xóa định cố bất kỳ lúc nào.\"\nExtraBrowsers.DockName=\"Tên định cố\"\nAuth.Authing.Title=\"Đang xác minh...\"\nAuth.Authing.Text=\"Đang xác minh tài khoản %1, vui lòng chờ...\"\nAuth.AuthFailure.Title=\"Xác minh không thành công\"\nAuth.AuthFailure.Text=\"Không xác thực được tài khoản %1:\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"Bắt buộc đăng nhập để xác minh\"\nAuth.InvalidScope.Text=\"Yêu cầu xác thực tài khoản %1 đã thay đổi. Một số tính năng có thể bị vô hiệu hoá.\"\nAuth.LoadingChannel.Title=\"Đang tải thông tin kênh...\"\nAuth.LoadingChannel.Text=\"Đang tải thông tin về kênh %1, xin vui lòng chờ...\"\nAuth.LoadingChannel.Error=\"Không tải được thông tin về kênh.\"\nAuth.ChannelFailure.Title=\"Thông tin kênh bị lỗi hạ tải\"\nAuth.ChannelFailure.Text=\"Không tải được thông tin kênh cho %1\\n\\n%2: %3\"\nAuth.Chat=\"Trò chuyện\"\nAuth.StreamInfo=\"Thông tin luồng phát\"\nTwitchAuth.Stats=\"Thống kê kênh Twitch\"\nTwitchAuth.Feed=\"Bảng tin kênh Twitch\"\nTwitchAuth.TwoFactorFail.Title=\"Không thể truy vấn khóa luồng\"\nTwitchAuth.TwoFactorFail.Text=\"OBS không thể kết nối với tài khoản Twitch của bạn. Hãy chắc rằng xác minh 2 lớp đã được bật ở phần<a href='https://www.twitch.tv/settings/security'>cài đặt bảo mật Twitch</a> vì điều này là cần để phát luồng.\"\nRestreamAuth.Channels=\"Kênh phát lại\"\nCopy.Filters=\"Sao chép bộ lọc\"\nPaste.Filters=\"Dán bộ lọc\"\nBrowserPanelInit.Title=\"Đang khởi chạy trình duyệt...\"\nBrowserPanelInit.Text=\"Đang khởi chạy trình duyệt, xin vui lòng chờ...\"\nBandwidthTest.Region=\"Khu vực\"\nBandwidthTest.Region.US=\"Hoa Kỳ\"\nBandwidthTest.Region.EU=\"Châu Âu\"\nBandwidthTest.Region.Asia=\"Châu Á\"\nBandwidthTest.Region.Other=\"Khác\"\nBasic.AutoConfig=\"Trình cấu hình tự động\"\nBasic.AutoConfig.ApplySettings=\"Áp dụng các thiết đặt\"\nBasic.AutoConfig.StartPage=\"Mục đích sử dụng\"\nBasic.AutoConfig.StartPage.SubTitle=\"Xác định bạn muốn sử dụng chương trình cho mục đích gì\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"Tối ưu hóa cho việc phát luồng, quay phim là phụ\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"Tối ưu hóa chỉ cho quay hình, tôi sẽ không phát luồng\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"Tôi sẽ chỉ sử dụng máy quay ảo\"\nBasic.AutoConfig.VideoPage=\"Thiết đặt video\"\nBasic.AutoConfig.VideoPage.SubTitle=\"Chỉ rõ các thiết đặt video mà bạn muốn sử dụng\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"Sử dụng hiện tại (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"Màn hình %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"Hiện tại (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 hoặc 30, nhưng ưu tiên 60 khi có thể\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 hoặc 30, nhưng ưu tiên độ phân giải cao\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"Lưu ý: Độ phân giải bức vẽ (cơ sở) không nhất thiết phải bằng với độ phân giải bạn sẽ truyền phát hay ghi hình. Độ phân giải phát luồng/ghi hình thực tế có thể bị giảm tỉ lệ từ độ phân giải của bức vẽ để giảm sử dụng tài nguyên hoặc yêu cầu về tốc độ bit.\"\nBasic.AutoConfig.StreamPage=\"Thông tin luồng\"\nBasic.AutoConfig.StreamPage.SubTitle=\"Vui lòng nhập thông tin máy chủ luồng của bạn\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"Kết nối tài khoản (đề xuất)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"Ngắt kết nối tài khoản\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"Ngắt kết nối tài khoản?\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"Thay đổi này sẽ được áp dụng ngay lập tức. Bạn có chắc là bạn muốn ngắt kết nối tài khoản của bạn?\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"Lấy khóa luồng\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"Thêm thông tin\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"Dùng khóa luồng phát\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"Dùng khoá luồng (nâng cao)\"\nBasic.AutoConfig.StreamPage.Service=\"Dịch vụ\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"Thêm...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"Tự chọn...\"\nBasic.AutoConfig.StreamPage.Server=\"Máy chủ\"\nBasic.AutoConfig.StreamPage.StreamKey=\"Khóa luồng\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: nhập mật khẩu mã hóa. \\nRTMP: nhập khóa được cung cấp bởi dịch vụ. \\nSRT: nhập streamid nếu dịch vụ sử dụng.\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"Khoá của bộ biên mã\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Mã Bearer\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"Tài khoản được kết nối\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"Ước tính tốc độ bit với bài kiểm tra băng thông (có thể mất vài phút)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"Ưu tiên biên mã bằng phần cứng\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"Biên mã bằng phần cứng giúp loại bỏ hầu hết lượng tải CPU, đồng thời có thể yêu cầu thêm tốc độ bit để đạt được chất lượng tương đối.\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"Cảnh báo luồng\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"Bài kiểm tra băng thông sẽ phát luồng dữ liêu video ngẫu nhiên mà không có âm thanh đến kênh của bạn. Nếu được, hãy tạm thời tắt các video luồng đang lưu và đặt luồng thành riêng tư cho đến khi kiểm tra xong. Tiếp tục?\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"Kiểm tra %1\"\nBasic.AutoConfig.TestPage=\"Kết quả cuối cùng\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"Chương trình hiện đang thực thi một số bài kiểm tra để ước lượng các thiết đặt lí tưởng nhất\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"Thử nghiệm hoàn tất\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"Đang kiểm tra băng thông, điều này có thể mất vài phút...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"Không tìm thấy đầu ra cho giao thức của dịch vụ này\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"Kết nối tới: %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"Không thể kết nối tới máy chủ nào. Vui lòng kiểm tra kết nối và thử lại.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"Đang kiểm tra băng thông cho: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"Đang thử bộ biên mã phát luồng, có thể mất vài phút...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"Đang thử bộ biên mã ghi hình, có thể mất vài phút...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"Không thể chạy bộ biên mã\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"Thử nghiệm %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"Bộ biên mã phát luồng\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"Bộ biên mã ghi hình\"\nBasic.AutoConfig.TestPage.Result.Header=\"Chương trình đã xác định được cài đặt lý tưởng nhất dành cho bạn:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"Để sử dụng các thiết đặt này, nhấp vào Áp dụng thiết đặt. Để cấu hình lại và thử lại, bấm Lùi lại. Để cấu hình thiết đặt bằng tay, bấm Hủy bỏ và mở Thiết đặt.\"\nBasic.AutoConfig.Info=\"Thuật sĩ tự cấu hình sẽ xác định các thiết đặt tốt nhất dựa trên đặc tả máy tính và tốc độ internet của bạn.\"\nBasic.AutoConfig.RunAnytime=\"Có thể chạy bất kì lúc nào bằng cách vào bảng chọn Công cụ.\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"Độ phân giải phát trực tuyến (được phóng to)\"\nBasic.Stats=\"Thống kê\"\nBasic.Stats.CPUUsage=\"Lượng tải CPU\"\nBasic.Stats.HDDSpaceAvailable=\"Không gian đĩa còn trống\"\nBasic.Stats.MemoryUsage=\"Lượng tải bộ nhớ\"\nBasic.Stats.AverageTimeToRender=\"Thời gian trung bình để xuất hình\"\nBasic.Stats.SkippedFrames=\"Lượng khung hình đã bỏ qua do lỗi biên mã\"\nBasic.Stats.MissedFrames=\"Khung hình bị mất do đơ kết xuất\"\nBasic.Stats.Output.Stream=\"Phát luồng\"\nBasic.Stats.Output.Recording=\"Quay video\"\nBasic.Stats.Status=\"Trạng thái\"\nBasic.Stats.Status.Recording=\"Đang quay\"\nBasic.Stats.Status.Live=\"TRỰC TIẾP\"\nBasic.Stats.Status.Reconnecting=\"Đang kết nối lại\"\nBasic.Stats.Status.Inactive=\"Không hoạt động\"\nBasic.Stats.Status.Active=\"Đang hoạt động\"\nBasic.Stats.DroppedFrames=\"Khung hình bị rớt (Mạng)\"\nBasic.Stats.MegabytesSent=\"Tổng dung lượng\"\nBasic.Stats.Bitrate=\"Tốc độ bit\"\nBasic.Stats.DiskFullIn=\"Địa đầy trong (xấp xỉ)\"\nBasic.Stats.DiskFullIn.Text=\"%1 giờ, %2 phút\"\nBasic.Stats.ResetStats=\"Đặt lại thống kê\"\nResetUIWarning.Title=\"Có chắc là bạn muốn thiết lập lại giao diện người dùng không?\"\nResetUIWarning.Text=\"Việc đặt lại giao diện người dùng (UI) sẽ ẩn các thanh bạn bổ sung. Bạn sẽ phải cài đặt lại từ bảng chọn Hiển thị nếu muốn dùng chúng.\\n\\nCó chắc là bạn muốn đặt lại giao diện người dùng không?\"\nUpdater.Title=\"Có bản cập nhật mới\"\nUpdater.Text=\" \"\nUpdater.UpdateNow=\"Cập nhật\"\nUpdater.RemindMeLater=\"Hỏi lại sau\"\nUpdater.Skip=\"Bỏ qua phiên bản\"\nUpdater.NoUpdatesAvailable.Title=\"Không có cập nhật mới\"\nUpdater.NoUpdatesAvailable.Text=\"Hiện không bản cập nhật nào\"\nUpdater.BranchNotFound.Title=\"Kênh cập nhật đã bị xóa\"\nUpdater.BranchNotFound.Text=\"Kênh cập nhật bạn đã chọn không còn khả dụng, OBS đã được đặt lại về mặc định.\"\nUpdater.RepairButUpdatesAvailable.Title=\"Không kiểm tra được tính toàn vẹn tệp\"\nUpdater.RepairButUpdatesAvailable.Text=\"Chỉ có thể kiểm tra tính toàn vẹn của tệp đối với phiên bản mới nhất có sẵn. Vào 'Trợ giúp → Kiểm tra cập nhật' để xác minh và cập nhật cài đặt OBS.\"\nUpdater.RepairConfirm.Title=\"Xác nhận kiểm tra tính toàn vẹn\"\nUpdater.RepairConfirm.Text=\"Trình kiểm tra tính toàn vẹn sẽ quét cài đặt OBS của bạn để tìm lỗi và tải lại các tệp bị hỏng/sửa đổi. Quá trình này có thể mất vài phút.\\n\\nCó chắc là bạn muốn kiểm tra không?\"\nUpdater.FailedToLaunch=\"Thất bại khi mở trình cập nhật\"\nQuickTransitions.SwapScenes=\"Hoán đổi cảnh xem trước và cảnh đầu ra\"\nQuickTransitions.SwapScenesTT=\"Hoán đổi cảnh xem trước và cảnh đầu ra sau chuyển cảnh (nếu đầu ra gốc tồn tại).\\nMọi thay đổi với đầu ra gốc sẽ không hoàn tác.\"\nQuickTransitions.DuplicateScene=\"Sao lưu phân cảnh\"\nQuickTransitions.DuplicateSceneTT=\"Khi chỉnh sửa trong cùng một phân cảnh, nó sẽ cho phép biến đổi bản sửa/hiển thị bản gốc mà không làm thay đổi xuất cảnh.\\nĐể chỉnh sửa cấu hình bản gốc trong khi xuất cảnh không bị thay đổi, chọn 'Tạo bản sao cảnh'.\\nThay đổi lựa chọn sẽ đặt lại bản xuất ra hiện tại (nếu tồn tại).\"\nQuickTransitions.EditProperties=\"Tạo bản sao nguồn\"\nQuickTransitions.EditPropertiesTT=\"Khi chỉnh sửa trong cùng một cảnh, cho phép chỉnh sửa cấu hình bản gốc mà không ảnh hưởng đến bản xuất ra.\\nChức năng chỉ được sử dụng khi chọn 'Tạo bản sao cảnh'.\\nMột vài bản gốc (như ảnh chụp hoặc phương tiện gốc) không hỗ trợ chức năng này và không thể chỉnh sửa riêng.\\nThay đổi lựa chọn này sẽ cài đặt lại bản xuất ra hiện tại (nếu nó tồn tại).\\n\\nChú ý: Hệ thống hoặc tài nguyên vi-đê-ô sẽ được sử dụng nhiều hơn nếu bật chức năng sao lưu bản gốc này.\"\nQuickTransitions.HotkeyName=\"Chuyển cảnh: %1\"\nBasic.AddTransition=\"Thêm cấu hình cảnh chuyển\"\nBasic.RemoveTransition=\"Xóa cấu hình cảnh chuyển\"\nBasic.TransitionProperties=\"Thuộc tính chuyển tiếp\"\nBasic.SceneTransitions=\"Chuyển cảnh\"\nBasic.TransitionDuration=\"Thời gian\"\nBasic.TogglePreviewProgramMode=\"Chế độ Studio\"\nBasic.EnablePreviewProgramMode=\"Bật Chế độ Studio\"\nBasic.DisablePreviewProgramMode=\"Tắt Chế độ Studio\"\nUndo.Undo=\"Hoàn tác\"\nUndo.Redo=\"Làm lại\"\nUndo.Add=\"Thêm '%1'\"\nUndo.Delete=\"Xoá '%1'\"\nUndo.Rename=\"Đổi tên %1'\"\nUndo.SceneCollection.Switch=\"Chuyển sang '%1'\"\nUndo.Item.Undo=\"Hoàn tác %1\"\nUndo.Item.Redo=\"Làm lại %1\"\nUndo.Sources.Multi=\"Xoá %1 nguồn\"\nUndo.Filters=\"Lọc các thay đổi trong '%1'\"\nUndo.Filters.Paste.Single=\"Dán bộ lọc từ '%1' tới '%2'\"\nUndo.Filters.Paste.Multiple=\"Sao chép bộ lọc từ '%1' tới '%2'\"\nUndo.Transform=\"Biến đổi (các) nguồn trong '%1'\"\nUndo.Transform.Paste=\"Dán chuyển động trong \\\"%1\\\"\"\nUndo.Transform.Rotate=\"Độ xoay trong '%1'\"\nUndo.Transform.Reset=\"Đặt lại biến hình trong '%1'\"\nUndo.Transform.HFlip=\"Xoay Ngang Trong %1\"\nUndo.Transform.VFlip=\"Xoay Dọc Trong %1\"\nUndo.Transform.FitToScreen=\"Cân đối '%1' trong màn hình\"\nUndo.Transform.StretchToScreen=\"Kéo '%1' trong màn hình\"\nUndo.Transform.Center=\"Căn chỉnh '%1' ra giữa màn hình\"\nUndo.Transform.VCenter=\"Căn '%1' ra giữa dọc trong màn hình\"\nUndo.Transform.HCenter=\"Căn '%1' ra giữa ngang trong màn hình\"\nUndo.Volume.Change=\"Thay đổi âm lượng trên '%1'\"\nUndo.Volume.Mute=\"Tắt tiếng '%1'\"\nUndo.Volume.Unmute=\"Bỏ tắt tiếng '%1'\"\nUndo.Balance.Change=\"Thay đổi cân bằng âm thanh trên '%1'\"\nUndo.SyncOffset.Change=\"Thay đổi phần bù đồng bộ âm thanh trong '%1'\"\nUndo.MonitoringType.Change=\"Thay đổi theo dõi âm thanh '%1'\"\nUndo.Mixers.Change=\"Thay đổi bộ trộn âm thanh trên '%1'\"\nUndo.ForceMono.On=\"Bật bắt buộc âm thanh đơn trong '%1'\"\nUndo.ForceMono.Off=\"Tắt bắt buộc âm thanh đơn trong '%1'\"\nUndo.Properties=\"Thay đổi thuộc tính trên '%1'\"\nUndo.Scene.Duplicate=\"Tạo bản sao Scene '%1'\"\nUndo.ShowTransition=\"hiển thị cảnh \\\"%1\\\"\"\nUndo.HideTransition=\"ẩn cảnh \\\"%1\\\"\"\nUndo.ShowSceneItem=\"Hiện '%1' trong '%2'\"\nUndo.HideSceneItem=\"Ẩn '%1' trong '%2'\"\nUndo.ReorderSources=\"Sắp xếp lại các nguồn trong '%1'\"\nUndo.MoveUp=\"Di chuyển '%1' lên trong '%2'\"\nUndo.MoveDown=\"Di chuyển '%1' xuống trong '%2'\"\nUndo.MoveToTop=\"Di chuyển '%1' lên đầu trong '%2'\"\nUndo.MoveToBottom=\"Di chuyển '%1' xuống dưới cùng trong '%2'\"\nUndo.PasteSource=\"Dán nguồn vào '%1'\"\nUndo.PasteSourceRef=\"Dán tham chiếu nguồn trong '%1'\"\nUndo.GroupItems=\"Nhóm các mục vào '%1'\"\nTransitionNameDlg.Text=\"Vui lòng nhập tên cảnh chuyển\"\nTransitionNameDlg.Title=\"Tên cảnh chuyển\"\nTitleBar.SafeMode=\"CHẾ ĐỘ AN TOÀN\"\nTitleBar.PortableMode=\"Chế độ di động\"\nTitleBar.Profile=\"Cấu hình\"\nTitleBar.Scenes=\"Phân cảnh\"\nNameExists.Title=\"Tên đã tồn tại\"\nNameExists.Text=\"Tên hiện đang sử dụng.\"\nNoNameEntered.Title=\"Vui lòng nhập tên hợp lệ\"\nNoNameEntered.Text=\"Bạn không thể sử dụng tên có sản phẩm nào.\"\nConfirmStart.Title=\"Bắt đầu phát luồng chưa?\"\nConfirmStart.Text=\"Bạn có chắc bạn muốn bắt đầu phát trực tuyển không?\"\nConfirmStop.Title=\"Ngừng phát luồng không?\"\nConfirmStop.Text=\"Có chắc bạn muốn chặn luồng phát không?\"\nConfirmStopRecord.Title=\"Ngừng ghi hình?\"\nConfirmStopRecord.Text=\"Có chắc là bạn muốn dừng ghi hình không?\"\nConfirmBWTest.Title=\"Bắt đầu kiểm tra băng thông?\"\nConfirmBWTest.Text=\"Bạn đã chỉnh OBS thành chế độ kiểm tra băng thông. Chế độ này cho phép kiểm tra mạng mà không cần phát trực tiếp. Sau khi xong việc, bạn sẽ cần tắt nó đi để khán giả có thể xem được luồng của bạn.\\n\\nBạn có muốn tiếp tục không?\"\nConfirmRemove.Title=\"Xác nhận loại bỏ\"\nConfirmRemove.Text=\"Có chắc là bạn muốn loại bỏ \\\"%1\\\" không?\"\nConfirmRemove.TextMultiple=\"Có chắc là bạn muốn xóa %1 nội dung không?\"\nConfirmReset.Title=\"Đặt lại thuộc tính\"\nConfirmReset.Text=\"Bạn có chắc chắn muốn đặt lại các thuộc tính hiện tại về mặc định không?\"\nOutput.StartStreamFailed=\"Không thể phát luồng\"\nOutput.StartRecordingFailed=\"Không thể bắt đầu quay video\"\nOutput.StartReplayFailed=\"Không thể khởi động replay buffer\"\nOutput.StartVirtualCamFailed=\"Không khởi động được máy ảnh ảo\"\nOutput.StartFailedGeneric=\"Không thể ghi đầu ra. Vui lòng kiểm tra các tệp nhật ký để biết thêm chi tiết.\\n\\nGhi chú: nếu bạn đang sử dụng bộ biên mã NVENC hoặc AMD, bạn phải cập nhật GPU lên phiên bản mới nhất.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"Không thể lưu bản phát lại trong khi tạm dừng\"\nOutput.ReplayBuffer.PauseWarning.Text=\"Cảnh báo: Bản phát lại sẽ không được lưu trong khi tạm dừng ghi hình.\"\nOutput.ConnectFail.Title=\"Không thể kết nối\"\nOutput.ConnectFail.BadPath=\"URL không hợp lệ của đường dẫn hoặc kết nối. Xin vui lòng kiểm tra cài đặt của bạn để xác nhận rằng họ là hợp lệ.\"\nOutput.ConnectFail.ConnectFailed=\"Không thể kết nối tới máy chủ\"\nOutput.ConnectFail.InvalidStream=\"Không thể truy cập kênh chỉ định hoặc khóa luồng phát. Vui lòng kiểm tra khóa luồng của bạn. Nếu bạn không nhập sai khóa, có thể luồng kết nối máy chủ có vấn đề.\"\nOutput.ConnectFail.HdrDisabled=\"Đầu ra HDR hiện bị tắt cho đầu ra này.\"\nOutput.ConnectFail.Error=\"1 lỗi bất ngờ xảy ra khi thử kết nối tới máy chủ. Thông tin thêm nằm trong tệp nhật ký.\"\nOutput.ConnectFail.Disconnected=\"Ngắt kết nối từ máy chủ.\"\nOutput.StreamEncodeError.Title=\"Lỗi biên mã\"\nOutput.StreamEncodeError.Msg=\"Xảy ra lỗi biên mã trong khi phát luồng.\"\nOutput.StreamEncodeError.Msg.LastError=\"Một lỗi biên mã đã xảy ra trong quá trình phát luồng:<br><br>%1\"\nOutput.RecordFail.Title=\"Không thể bắt đầu ghi hình\"\nOutput.RecordFail.Unsupported=\"Định dạng đầu ra là một trong hai dạng không được hỗ trợ hoặc không hỗ trợ nhiều hơn một rãnh âm. Xin vui lòng kiểm tra thiết đặt của bạn và thử lại.\"\nOutput.RecordNoSpace.Title=\"Không gian đĩa không đủ\"\nOutput.RecordNoSpace.Msg=\"Không còn đủ không gian đĩa để tiếp tục ghi hình.\"\nOutput.RecordError.Title=\"Lỗi ghi hình\"\nOutput.RecordError.Msg=\"Một lỗi không xác định xảy ra trong khi ghi hình.\"\nOutput.RecordError.EncodeErrorMsg=\"Xảy ra lỗi biên mã trong khi ghi hình.\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"Một lỗi biên mã đã xảy ra trong quá trình ghi hình:<br><br>%1\"\nOutput.BadPath.Title=\"Đường dẫn tệp xấu\"\nOutput.BadPath.Text=\"Không thể mở Đường dẫn ghi hình đã cài đặt. Hãy kiểm tra lại Đường dẫn ghi hình của bạn trong Cài đặt→ Đầu ra→ Ghi hình\"\nOutput.NoBroadcast.Title=\"Không có phát sóng nào được cấu hình\"\nOutput.NoBroadcast.Text=\"Bạn cần thiết lập một phát sóng trước khi bạn có thể bắt đầu phát luồng.\"\nOutput.BroadcastStartFailed=\"Không thể bắt đầu phát sóng\"\nOutput.BroadcastStopFailed=\"Không thể dừng phát sóng\"\nLogUploadDialog.Buttons.ConfirmUpload=\"Tải lên\"\nLogUploadDialog.Buttons.CopyURL=\"Sao chép URL nhật kí\"\nLogUploadDialog.Buttons.AnalyzeURL=\"Phân tích tệp nhật kí\"\nLogUploadDialog.Buttons.RetryButton=\"Thử lại\"\nRemux.TargetFile=\"Tệp đích\"\nRemux.Remux=\"Ghép lại\"\nRemux.Stop=\"Dừng ghép lại\"\nRemux.ClearFinished=\"Xóa thành phẩm\"\nRemux.ClearAll=\"Xóa tất cả các mục\"\nRemux.OBSRecording=\"Bản ghi OBS\"\nRemux.FinishedTitle=\"Việc ghép lại đã hoàn thành\"\nRemux.Finished=\"Bản ghi đã được ghép lại\"\nRemux.FinishedError=\"Bản ghi đã được ghép lại, nhưng các tập tin có thể không đầy đủ\"\nRemux.SelectRecording=\"Chọn bản ghi OBS...\"\nRemux.SelectTarget=\"Chọn tệp đích...\"\nRemux.FileExistsTitle=\"Tệp đích đã tồn tại\"\nRemux.FileExists=\"Các tệp sau đây đã tồn tại. Bạn có muốn thay thế chúng không?\"\nRemux.ExitUnfinishedTitle=\"Đang trong quá trình ghép lại\"\nRemux.ExitUnfinished=\"Việc ghép lại không được hoàn tất, dừng bây giờ có thể kết xuất tệp đích không sử dụng được.\\nBạn có chắc bạn muốn dừng ghép lại không?\"\nRemux.HelpText=\"Thả tệp vào cửa sổ này để ghép lại, hoặc chọn một ô \\\"Bản ghi OBS\\\" rỗng để duyệt tìm một tệp.\"\nRemux.NoFilesAddedTitle=\"Không có tệp remux nào được thêm vào\"\nRemux.NoFilesAdded=\"Không có tệp nào được thêm vào remux. Thả một thư mục chứa một hoặc nhiều tệp video.\"\nMissingFiles=\"Các tập tin bị thiếu\"\nMissingFiles.MissingFile=\"Tập tin bị thiếu\"\nMissingFiles.NewFile=\"Tập tin mới\"\nMissingFiles.HelpText=\"Một số tệp đã bị thiếu từ lần cuối bạn dùng OBS.\"\nMissingFiles.Clear=\"<đã xóa sạch>\"\nMissingFiles.NumFound=\"Phát hiện %1 trong số %2\"\nMissingFiles.Search=\"Tìm kiếm thư mục...\"\nMissingFiles.SelectFile=\"Chọn File...\"\nMissingFiles.SelectDir=\"Chọn tệp để tìm kiếm\"\nMissingFiles.State=\"Tình trạng\"\nMissingFiles.Missing=\"Bị thiếu\"\nMissingFiles.Replaced=\"Đã thay thế\"\nMissingFiles.Cleared=\"Đã dọn\"\nMissingFiles.Found=\"Đã tìm thấy\"\nMissingFiles.AutoSearch=\"Tìm thấy file bổ sung phù hợp\"\nMissingFiles.AutoSearchText=\"OBS đã tìm thấy một tệp thay thế cho tệp bị thiếu trong cùng đường dẫn. Bạn có muốn thêm chúng vào ?\"\nMissingFiles.NoMissing.Title=\"Kiểm tra tệp bị thiếu\"\nMissingFiles.NoMissing.Text=\"Không có tệp nào bị thiếu cả.\"\nMacPermissions.MenuAction=\"Xem lại các quyền của ứng dụng...\"\nMacPermissions.Title=\"Xem lại các quyền của ứng dụng\"\nMacPermissions.Description=\"OBS Studio cần sự đồng ý của bạn để sử dụng được một số chức năng. Và được khuyến cáo là nên mở những chức năng này dù nó không cần thiết để sử dụng cái app này. Bạn có thể mở nó lúc sau cũng được.\"\nMacPermissions.Description.OpenDialog=\"Bạn có thể mở lại hộp thoại này từ bảng chọn của OBS Studio\"\nMacPermissions.AccessGranted=\"Cấp quyền truy cập\"\nMacPermissions.RequestAccess=\"Yêu cầu quyền truy cập\"\nMacPermissions.OpenPreferences=\"Mở tùy chọn của %1\"\nMacPermissions.Item.ScreenRecording=\"Ghi lại màn hình\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS cần quyền này để có thể ghi lại màn hình của bạn\"\nMacPermissions.Item.Camera.Details=\"Quyền này là cần thiết để quay được bằng webcam hay capture card.\"\nMacPermissions.Item.Microphone.Details=\"OBS cần quyền này nếu bạn muốn ghi âm microphone của bạn hoặc một thiết bị âm thanh bên ngoài.\"\nMacPermissions.Item.Accessibility=\"Hỗ trợ tiếp cận\"\nMacPermissions.Continue=\"Tiếp tục\"\nSourceLeak.Title=\"Lỗi dọn dẹp nguồn\"\nSourceLeak.Text=\"Đã xảy ra sự cố khi thay đổi bộ sưu tập cảnh và không thể tải một số nguồn. Sự cố này thường do trình cắm không giải phóng tài nguyên đúng cách. Vui lòng đảm bảo rằng mọi trình cắm bạn đang sử dụng đều được cập nhật.\\n\\nOBS Studio bây giờ sẽ thoát ra để ngăn chặn bất kỳ hỏng hóc dữ liệu tiềm ẩn nào.\"\nBasic.DesktopDevice1=\"Âm thanh máy tính để bàn\"\nBasic.DesktopDevice2=\"Máy tính để bàn âm thanh 2\"\nBasic.Scene=\"Màn hình\"\nBasic.DisplayCapture=\"Ghi màn hình\"\nBasic.Main.PreviewConextMenu.Enable=\"Bật xem trước\"\nBasic.Main.Preview.Disable=\"Tắt xem trước\"\nScaleFiltering.Area=\"Khu vực\"\nBlendingMethod=\"Chế độ Hòa trộn\"\nBlendingMethod.Default=\"Mặc định\"\nBlendingMethod.SrgbOff=\"SRGB đã tắt\"\nBlendingMode=\"Chế độ Blend\"\nBlendingMode.Normal=\"Thường\"\nBlendingMode.Additive=\"Thêm\"\nBlendingMode.Subtract=\"Trừ\"\nBlendingMode.Screen=\"Màn che\"\nBlendingMode.Multiply=\"Nhân\"\nBlendingMode.Lighten=\"Làm sáng\"\nBlendingMode.Darken=\"Làm tối\"\nDeinterlacing.TopFieldFirst=\"Trường đầu tiên\"\nDeinterlacing.BottomFieldFirst=\"Trường dưới cùng trước\"\nVolControl.SliderUnmuted=\"Thanh trượt âm lượng cho '%1':\"\nVolControl.SliderMuted=\"Thanh trượt âm lượng cho '%1': (hiện bị tắt tiếng)\"\nVolControl.Mute=\"Tắt tiếng '%1'\"\nVolControl.Properties=\"Thuộc tính '%1'\"\nVolControl.UnassignedWarning.Title=\"Nguồn âm thanh chưa được chỉ định\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" không được gán cho bất kỳ rãnh âm thanh nào và sẽ không nghe thấy được trong luồng hoặc bản ghi hình.\\n\\nĐể gán nguồn âm thanh cho một rãnh, hãy mở Thuộc tính âm thanh nâng cao thông qua bảng chọn chuột phải hoặc nút răng cưa trong thanh công cụ giá đỡ bộ trộn.\"\nBasic.Main.AddSceneDlg.Title=\"Thêm phân cảnh\"\nBasic.Main.AddSceneDlg.Text=\"Vui lòng nhập tên phân cảnh\"\nBasic.Main.DefaultSceneName.Text=\"Phân cảnh %1\"\nBasic.Main.AddSceneCollection.Title=\"Cài bộ phân cảnh\"\nBasic.Main.AddSceneCollection.Text=\"Vui lòng nhập tên của bộ phân cảnh\"\nBasic.Main.RenameSceneCollection.Title=\"Đổi tên bộ phân cảnh\"\nBasic.Main.RemigrateSceneCollection.Title=\"Cập nhật độ phân giải bộ sưu tập cảnh\"\nBasic.Main.RemigrateSceneCollection.Text=\"Bạn có muốn cập nhật độ phân giải của bộ sưu tập cảnh \\\"%1\\\" để phù hợp với độ phân giải bức vẽ của hồ sơ hiện tại là %2x%3 không?\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"Không thể cập nhật độ phân giải bộ sưu tập cảnh khi đầu ra đang hoạt động.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"Không cập nhật được độ phân giải của bộ sưu tập cảnh. Độ phân giải ban đầu không xác định được.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"Không thể thiết lập lại: Thay đổi độ phân giải OBS không thành công.\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"Không thể thiết lập lại: Độ phân giải hiện tại đã là độ phân giải cơ bản của bộ sưu tập cảnh.\"\nAddProfile.Title=\"Thêm cấu hình\"\nAddProfile.Text=\"Vui lòng nhập tên cấu hình\"\nAddProfile.WizardCheckbox=\"Hiện thuật sĩ tự cấu hình\"\nRenameProfile.Title=\"Đổi tên cấu hình\"\nBasic.Main.MixerRename.Title=\"Đổi tên nguồn âm thanh\"\nBasic.Main.MixerRename.Text=\"Hãy nhập tên nguồn âm thanh\"\nBasic.Main.PreviewDisabled=\"Xem trước hiện đang vô hiệu hoá\"\nBasic.SourceSelect=\"Tạo/chọn nguồn\"\nBasic.SourceSelect.CreateNew=\"Tạo mới\"\nBasic.SourceSelect.AddExisting=\"Thêm sẵn có\"\nBasic.SourceSelect.AddVisible=\"Làm cho nguồn có thể nhìn thấy\"\nBasic.Main.Sources.Visibility=\"Hiển thị\"\nBasic.Main.Sources.VisibilityDescription=\"Kiểm soát khả năng hiển thị của '%1' trong khung hình\"\nBasic.Main.Sources.Lock=\"Khoá\"\nBasic.Main.Sources.LockDescription=\"Khóa vị trí và tỷ lệ của '%1' trong khung hình\"\nBasic.PropertiesWindow=\"Thuộc tính '%1'\"\nBasic.PropertiesWindow.SelectColor=\"Chọn Màu\"\nBasic.PropertiesWindow.SelectFont=\"Chọn font\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"Chọn một phông chữ\"\nBasic.PropertiesWindow.ConfirmTitle=\"Thay đổi thiết đặt\"\nBasic.PropertiesWindow.Confirm=\"Có được lưu thay đổi. Bạn có muốn giữ họ?\"\nBasic.PropertiesWindow.NoProperties=\"Không có tài sản có sẵn\"\nBasic.PropertiesWindow.AddFiles=\"Thêm tập tin\"\nBasic.PropertiesWindow.AddDir=\"Thêm thư mục\"\nBasic.PropertiesWindow.AddURL=\"Thêm đường dẫn/URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"Thêm thư mục vào '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"Thêm các tệp vào '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"Thêm mục nhập vào '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"Chỉnh sửa mục nhập từ '%1'\"\nBasic.PropertiesView.FPS.Simple=\"Giá trị đơn giản FPS\"\nBasic.PropertiesView.FPS.Rational=\"Giá trị hợp lý FPS\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"Phạm vi FPS hợp lệ:\"\nBasic.PropertiesView.UrlButton.Text=\"Mở liên kết này trên trình duyệt web mặc định của bạn nhé?\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"Mở URL\"\nBasic.InteractionWindow=\"Tương tác với '%1'\"\nBasic.StatusBar.Reconnecting=\"Ngắt kết nối, kết nối lại trong %2 second(s) (cố gắng %1)\"\nBasic.StatusBar.AttemptingReconnect=\"Cố gắng kết nối lại... (cố gắng %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"Tái kết nối thành công\"\nBasic.StatusBar.Delay=\"Chậm trễ (%1 giây)\"\nBasic.StatusBar.DelayStartingIn=\"Chậm trễ (bắt đầu trong %1 giây)\"\nBasic.StatusBar.DelayStoppingIn=\"Chậm trễ (dừng lại ở %1 sec)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"Chậm trễ (dừng lại ở %1 sec, bắt đầu trong %2 giây)\"\nBasic.StatusBar.RecordingSavedTo=\"Đã lưu bản ghi hình vào '%1'\"\nBasic.StatusBar.ReplayBufferSavedTo=\"Bộ đệm phát lại đã được lưu vào '%1'\"\nBasic.StatusBar.ScreenshotSavedTo=\"Ảnh chụp màn hình được lưu vào '%1'\"\nBasic.StatusBar.AutoRemuxedTo=\"Bản ghi đã được tự động ghép lại thành '%1'\"\nBasic.Filters=\"Bộ lọc\"\nBasic.Filters.AsyncFilters=\"Bộ lọc âm thanh/Video\"\nBasic.Filters.AudioFilters=\"Bộ lọc âm thanh\"\nBasic.Filters.EffectFilters=\"Bộ lọc có hiệu lực\"\nBasic.Filters.Title=\"Bộ lọc cho '%1'\"\nBasic.Filters.AddFilter.Title=\"Tên bộ lọc\"\nBasic.Filters.AddFilter.Text=\"Hãy chỉ ra tên bộ lọc\"\nBasic.TransformWindow=\"Biểu thị phân tử\"\nBasic.TransformWindow.Position=\"Vị trí\"\nBasic.TransformWindow.PositionX=\"Vị trí X\"\nBasic.TransformWindow.PositionY=\"Vị trí Y\"\nBasic.TransformWindow.Rotation=\"Quay\"\nBasic.TransformWindow.Size=\"Kích thước\"\nBasic.TransformWindow.Width=\"Chiều rộng\"\nBasic.TransformWindow.Height=\"Chiều cao\"\nBasic.TransformWindow.Alignment=\"Vị trí liên kết\"\nBasic.TransformWindow.BoundsType=\"Các giáp ranh hộp loại\"\nBasic.TransformWindow.BoundsAlignment=\"Liên kết trong các giáp ranh hộp\"\nBasic.TransformWindow.Bounds=\"Các giáp ranh hộp kích thước\"\nBasic.TransformWindow.BoundsWidth=\"Chiều rộng hộp biên\"\nBasic.TransformWindow.BoundsHeight=\"Chiều cao hộp biên\"\nBasic.TransformWindow.CropToBounds=\"Cắt thành hộp giới hạn\"\nBasic.TransformWindow.Crop=\"Cắt\"\nBasic.TransformWindow.CropLeft=\"Cắt phần bên trái\"\nBasic.TransformWindow.CropRight=\"Cắt phần bên phải\"\nBasic.TransformWindow.CropTop=\"Cắt phần trên\"\nBasic.TransformWindow.CropBottom=\"Cắt phần dưới\"\nBasic.TransformWindow.Alignment.TopLeft=\"Góc trên trái\"\nBasic.TransformWindow.Alignment.TopCenter=\"Đỉnh Trung tâm\"\nBasic.TransformWindow.Alignment.TopRight=\"Trên cùng bên phải\"\nBasic.TransformWindow.Alignment.CenterLeft=\"Trung tâm trái\"\nBasic.TransformWindow.Alignment.Center=\"Giữa\"\nBasic.TransformWindow.Alignment.CenterRight=\"Trung tâm phải\"\nBasic.TransformWindow.Alignment.BottomLeft=\"Dưới cùng bên trái\"\nBasic.TransformWindow.Alignment.BottomCenter=\"Phía dưới trung tâm\"\nBasic.TransformWindow.Alignment.BottomRight=\"Góc dưới bên phải\"\nBasic.TransformWindow.BoundsType.None=\"Không có giới hạn\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"Chỉ kích thước tối đa\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"Co dãn theo bên trong giới hạn\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"Co dãn theo bên ngoài giới hạn\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"Co dãn theo chiều rộng của giới hạn\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"Co dãn theo chiều cao của giới hạn\"\nBasic.TransformWindow.BoundsType.Stretch=\"Trải dài đến giới hạn\"\nBasic.TransformWindow.Title=\"Sửa phép biến hình cho '%1'\"\nBasic.TransformWindow.NoSelectedSource=\"Không có nguồn nào được chọn\"\nBasic.Main.AddSourceHelp.Title=\"Không thể thêm nguồn\"\nBasic.Main.AddSourceHelp.Text=\"Bạn cần phải có ít nhất 1 phân cảnh để thêm nguồn.\"\nBasic.Main.Scenes=\"Phân cảnh\"\nBasic.Main.Sources=\"Nguồn\"\nBasic.Main.Source=\"Nguồn\"\nBasic.Main.Controls=\"Điều khiển\"\nBasic.Main.PreparingStream=\"Đang chuẩn bị...\"\nBasic.Main.Connecting=\"Đang kết nối...\"\nBasic.Main.StartRecording=\"Bắt đầu ghi\"\nBasic.Main.StartReplayBuffer=\"Bắt đầu Replay Buffer\"\nBasic.Main.SaveReplay=\"Lưu bản phát lại\"\nBasic.Main.StartStreaming=\"Bắt đầu phát luồng\"\nBasic.Main.StartBroadcast=\"Bắt đầu\"\nBasic.Main.StartVirtualCam=\"Bắt đầu máy quay ảo\"\nBasic.Main.StopRecording=\"Dừng ghi\"\nBasic.Main.PauseRecording=\"Tạm dừng ghi hình\"\nBasic.Main.UnpauseRecording=\"Tiếp tục ghi hình\"\nBasic.Main.SplitFile=\"Tách tệp ghi hình\"\nBasic.Main.AddChapterMarker=\"Thêm phần đánh dấu chương (chỉ MP4/MOV lai)\"\nBasic.Main.StoppingRecording=\"Dừng ghi video...\"\nBasic.Main.StopReplayBuffer=\"Dừng Replay Buffer\"\nBasic.Main.StoppingReplayBuffer=\"Đang dừng Replay Buffer...\"\nBasic.Main.SetupBroadcast=\"Quản lý phát sóng\"\nBasic.Main.StopStreaming=\"Ngừng phát luồng\"\nBasic.Main.StopBroadcast=\"Kết thúc phát sóng\"\nBasic.Main.AutoStopEnabled=\"(Tự đông dừng)\"\nBasic.Main.StoppingStreaming=\"Đang dừng phát luồng...\"\nBasic.Main.ForceStopStreaming=\"Ngừng phát luồng (huỷ chậm trễ)\"\nBasic.Main.ShowContextBar=\"Hiển thị Thanh công cụ Nguồn\"\nBasic.Main.HideContextBar=\"Ẩn Thanh công cụ Nguồn\"\nBasic.Main.StopVirtualCam=\"Dừng máy quay ảo\"\nBasic.Main.Group=\"Nhóm %1\"\nBasic.Main.GroupItems=\"Nhóm đã chọn\"\nBasic.Main.Ungroup=\"Tháo nhóm\"\nBasic.Main.GridMode=\"Chế độ lưới\"\nBasic.Main.ListMode=\"Chế độ danh sách\"\nBasic.Main.VirtualCamConfig=\"Chỉnh sửa camera ảo\"\nBasic.VCam.VirtualCamera=\"Máy quay ảo\"\nBasic.VCam.OutputType=\"Kiểu đầu ra\"\nBasic.VCam.OutputSelection=\"Chọn đầu ra\"\nBasic.VCam.OutputType.Program=\"Chương trình (Mặc định)\"\nBasic.VCam.OutputSelection.NoSelection=\"Không có lựa chọn cho loại đầu ra này\"\nBasic.VCam.RestartWarning=\"Máy ảnh ảo sẽ được khởi động lại để áp dụng thay đổi này\"\nBasic.MainMenu.File=\"Tập tin (&F)\"\nBasic.MainMenu.File.Export=\"Xuất (&E)\"\nBasic.MainMenu.File.Import=\"Nhập (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"Hiển thị Bản ghi (&R)\"\nBasic.MainMenu.File.Remux=\"Ghép lại bản ghi (&M)\"\nBasic.MainMenu.File.Settings=\"Thiết đặt (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"Hiển thị thư mục thiết đặt\"\nBasic.MainMenu.File.ShowProfileFolder=\"Hiển thị thư mục cấu hình\"\nBasic.MainMenu.File.ShowMissingFiles=\"Kiểm tra các tệp bị thiếu\"\nBasic.MainMenu.File.Exit=\"Thoát (&X)\"\nBasic.MainMenu.Edit=\"Chỉnh sửa (&E)\"\nBasic.MainMenu.Edit.Undo=\"Hoàn tác (&U)\"\nBasic.MainMenu.Edit.Redo=\"Làm lại (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"Khóa xem trước (&L)\"\nBasic.MainMenu.Edit.Scale=\"Xem trước co dãn (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"Co dãn cửa sổ\"\nBasic.MainMenu.Edit.Scale.Canvas=\"Bức vẽ (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"Đầu ra (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"Đã chia tỷ lệ (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"Phóng to\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"Thu nhỏ\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"Đặt lại thu phóng\"\nBasic.MainMenu.Edit.Transform=\"Biến đổi (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"Chỉnh sửa biến đổi... (&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"Sao chép chuyển đổi\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"Dán chuyển đổi\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"Đặt lại biến đổi (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"Xoay 90 độ bên phải\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"Xoay 90 độ bên trái\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"Xoay 180 độ\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"Lật Ngang (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"Lật Dọc (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"Vừa với màn hình (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"Giãn ra để lấp đầy m.hình (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"Trung tâm màn hình (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"Giữa theo chiều dọc\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"Giữa theo chiều ngang\"\nBasic.MainMenu.Edit.Order=\"Đặt (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"Ch&uyển Lên\"\nBasic.MainMenu.Edit.Order.MoveDown=\"Chuyển Xuống (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"Chuyển đến Đầu (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"Chuyển xuống Dưới (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"Các thuộc tính âm th&anh nâng cao\"\nBasic.MainMenu.View=\"Hiển thị (&V)\"\nBasic.MainMenu.View.Toolbars=\"&Thanh công cụ\"\nBasic.MainMenu.View.ListboxToolbars=\"Thanh công cụ Dock\"\nBasic.MainMenu.View.ContextBar=\"Thanh công cụ Nguồn\"\nBasic.MainMenu.View.SourceIcons=\"B&iểu tượng nguồn\"\nBasic.MainMenu.View.StatusBar=\"Thanh trạng thái (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"Giao diện toàn màn hình\"\nBasic.MainMenu.View.ResetUI=\"&Đặt lại giao diện\"\nBasic.MainMenu.View.AlwaysOnTop=\"&Luôn hiện trên cùng\"\nBasic.MainMenu.View.SceneListMode=\"Biểu thị liệt kê phân cảnh\"\nBasic.MainMenu.Docks=\"Giá đỡ (&D)\"\nBasic.MainMenu.Docks.ResetDocks=\"Đặt lại các giá đỡ (&R)\"\nBasic.MainMenu.Docks.LockDocks=\"Khoá các giá đỡ (&L)\"\nBasic.MainMenu.Docks.SideDocks=\"&Dock có chiều cao đầy đủ\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"Giá đỡ trình duyệt tùy chỉnh... (&C)\"\nBasic.MainMenu.Import=\"Nhập...\"\nBasic.MainMenu.Export=\"Xuất...\"\nBasic.MainMenu.SceneCollection=\"Bộ &sưu tập cảnh\"\nBasic.MainMenu.Profile=\"Cấu hình (&P)\"\nBasic.MainMenu.Profile.Import=\"Nhập cấu hình\"\nBasic.MainMenu.Profile.Export=\"Xuất cấu hình\"\nBasic.MainMenu.SceneCollection.Import=\"Nhập bộ sưu tập cảnh\"\nBasic.MainMenu.SceneCollection.Export=\"Xuất bộ sưu tập cảnh\"\nBasic.MainMenu.SceneCollection.Remigrate=\"Đặt lại độ phân giải cơ bản\"\nBasic.MainMenu.SceneCollection.Migrate=\"Đặt độ phân giải cơ bản\"\nBasic.MainMenu.Profile.Exists=\"Cấu hình này đã tồn tại\"\nBasic.MainMenu.Tools=\"Công cụ (&T)\"\nBasic.MainMenu.Help=\"Trợ giúp (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"Cổng thông tin trợ giúp (&P)\"\nBasic.MainMenu.Help.Website=\"Ghé thăm &Website\"\nBasic.MainMenu.Help.Discord=\"Tham gia &Discord Server\"\nBasic.MainMenu.Help.WhatsNew=\"Có gì mới\"\nBasic.MainMenu.Help.Logs=\"Tập tin nhật ký (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"Hiển thị thư mục nhật ký (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"Tải lên tệp nhật ký hiện tại (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"Tải lên tệp nhật ký trước đó\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"Xem nhật ký hiện tại (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"Ghi chú\"\nBasic.MainMenu.Help.CheckForUpdates=\"Kiểm tra cập nhật mới\"\nBasic.MainMenu.Help.Repair=\"Kiểm tra tính toàn vẹn tệp\"\nBasic.MainMenu.Help.RestartSafeMode=\"Khởi động lại ở Chế độ An toàn\"\nBasic.MainMenu.Help.RestartNormal=\"Khởi động lại ở Chế độ Bình thường\"\nBasic.MainMenu.Help.CrashLogs=\"Báo cáo sự cố (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"Hiện báo cáo hỏng chương trình (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"Tải lên báo cáo lỗi trước đó\"\nBasic.MainMenu.Help.About=\"Giới thiệu (&A)\"\nBasic.Settings.ProgramRestart=\"Chương trình phải được khởi động lại để những thiết đặt có hiệu lực.\"\nBasic.Settings.ConfirmTitle=\"Xác nhận thay đổi\"\nBasic.Settings.Confirm=\"Bạn đã lưu thay đổi. Lưu thay đổi?\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 đang kiểm soát một số cài đặt phát trực tiếp của bạn\"\nBasic.Settings.General=\"Chung\"\nBasic.Settings.General.Language=\"Ngôn ngữ\"\nBasic.Settings.General.Updater=\"Cập nhật\"\nBasic.Settings.General.UpdateChannel=\"Kênh cập nhật\"\nBasic.Settings.General.UpdateChannelDisabled=\"(Tắt)\"\nBasic.Settings.General.UpdateChannelDefault=\"(Mặc định)\"\nBasic.Settings.General.EnableAutoUpdates=\"Tự động kiểm tra cập nhật khi khởi động\"\nBasic.Settings.General.OpenStatsOnStartup=\"Mở hộp thoại thống kê khi khởi động\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"Ẩn các cửa sổ OBS khi ghi hình\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"Cài đặt này sẽ ẩn tất cả cửa sổ OBS Studio bị OBS ghi và ảnh hưởng đến\\nđầu ra, chẳng hạn như các phần mềm hội nghị trực tuyến, chia sẻ màn hình, hỗ trợ từ xa, chụp ảnh màn hình và các phần mềm khác.\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"Đặt tùy chọn này sẽ ẩn tất cả cửa sổ OBS Studio bị OBS ghi và ảnh hưởng đến đầu ra, chẳng hạn như các phần mềm hội nghị trực tuyến, chia sẻ màn hình, hỗ trợ từ xa, chụp ảnh màn hình và các phần mềm khác.\"\nBasic.Settings.General.WarnBeforeStartingStream=\"Hiển thị hộp thoại xác nhận khi bắt đầu phát luồng\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"Hiển thị hộp thoại xác nhận khi dừng phát luồng\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"Hiển thị hộp thoại xác nhận khi dừng ghi hình\"\nBasic.Settings.General.Projectors=\"Khung chiếu\"\nBasic.Settings.General.HideProjectorCursor=\"Ẩn con trỏ chuột trên khung chiếu\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"Làm cho khung chiếu luôn luôn trên cùng\"\nBasic.Settings.General.Snapping=\"Chụp liên kết nguồn\"\nBasic.Settings.General.ScreenSnapping=\"Nguồn Snap đến cạnh màn hình\"\nBasic.Settings.General.CenterSnapping=\"Chụp nguồn vào trung tâm ngang và dọc\"\nBasic.Settings.General.SourceSnapping=\"Kéo nguồn tới các nguồn khác\"\nBasic.Settings.General.SnapDistance=\"Độ nhạy kéo\"\nBasic.Settings.General.SpacingHelpers=\"Mở bảng lưới pixel lên\"\nBasic.Settings.General.RecordWhenStreaming=\"Tự động ghi hình khi phát luồng\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"Quay tiếp khi dừng phát luồng\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"Tự động bắt đầu bộ đệm phát lại khi phát luồng\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"Để bộ đệm phát lại tiếp tục chạy khi dừng phát luồng\"\nBasic.Settings.General.SysTray=\"Khay hệ thống\"\nBasic.Settings.General.SysTrayWhenStarted=\"Thu nhỏ về khay hệ thống khi bắt đầu\"\nBasic.Settings.General.SystemTrayHideMinimize=\"Luôn luôn thu nhỏ về khay hệ thống thay vì thanh tác vụ\"\nBasic.Settings.General.SaveProjectors=\"Lưu khung chiếu khi thoát\"\nBasic.Settings.General.CloseExistingProjectors=\"Giới hạn một khung chiếu toàn màn hình cho mỗi màn hình\"\nBasic.Settings.General.Preview=\"Xem trước\"\nBasic.Settings.General.OverflowHidden=\"Ẩn phần tràn\"\nBasic.Settings.General.OverflowAlwaysVisible=\"Luôn hiện thị tràn khung hình\"\nBasic.Settings.General.OverflowSelectionHidden=\"Hiển thị tràn khung hình khi không thấy nguồn\"\nBasic.Settings.General.Importers=\"Bộ nhập\"\nBasic.Settings.General.AutomaticCollectionSearch=\"Tìm kiếm các bộ sưu tập cảnh tại các địa chỉ được nhập\"\nBasic.Settings.General.SwitchOnDoubleClick=\"Nháy đúp chuột để chuyển cảnh\"\nBasic.Settings.General.StudioPortraitLayout=\"Bật bố cục theo chiều dọc\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"Hiện nhãn của xem trước/chương trình\"\nBasic.Settings.General.Multiview=\"Nhiều góc nhìn\"\nBasic.Settings.General.Multiview.MouseSwitch=\"Nháy chuột để chuyển cảnh\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"Hiển thị tên phân cảnh\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"Vẽ khu vực an toàn (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"Bố cục nhiều góc nhìn\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"Chiều ngang, trên cùng (8 phân cảnh)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"Chiều ngang, dưới cùng (8 phân cảnh)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"Chiều dọc, trái (8 cảnh)\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"Chiều dọc, phải (8 cảnh)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"Chiều ngang, trên cùng (18 phân cảnh)\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"Chiều ngang, trên cùng (24 phân cảnh)\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"Chỉ phân cảnh (4 phân cảnh)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"Chỉ phân cảnh (9 phân cảnh)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"Chỉ phân cảnh (16 phân cảnh)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"Chỉ phân cảnh (25 phân cảnh)\"\nBasic.Settings.General.ChannelName.stable=\"Ổn định\"\nBasic.Settings.General.ChannelDescription.stable=\"Phiên bản ổn định mới nhất\"\nBasic.Settings.General.ChannelName.beta=\"Bản beta / Bản phát hành ứng cử\"\nBasic.Settings.General.ChannelDescription.beta=\"Phiên bản tiền phát hành có thể không ổn định\"\nBasic.Settings.Appearance=\"Diện mạo\"\nBasic.Settings.Appearance.General=\"Tổng quan\"\nBasic.Settings.Appearance.General.Theme=\"Chủ đề\"\nBasic.Settings.Appearance.General.Variant=\"Kiểu cách\"\nBasic.Settings.Appearance.General.NoVariant=\"Không có sẵn kiểu nào\"\nBasic.Settings.Appearance.FontScale=\"Cỡ chữ\"\nBasic.Settings.Appearance.Density=\"Mật độ\"\nBasic.Settings.Appearance.Classic=\"Cổ điển\"\nBasic.Settings.Appearance.Compact=\"Gọn\"\nBasic.Settings.Appearance.Normal=\"Bình thường\"\nBasic.Settings.Appearance.Comfortable=\"Thoải mái\"\nBasic.Settings.Appearance.OptionsWarning=\"Một số tuỳ chọn về diện mạo sẽ không dùng được khi đang dùng kiểu dáng này.\"\nBasic.Settings.Stream=\"Luồng\"\nBasic.Settings.Stream.Destination=\"Điểm đến\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"Sử dụng xác thực\"\nBasic.Settings.Stream.Custom.Username=\"Tên đăng nhập\"\nBasic.Settings.Stream.Custom.Password=\"Mật khẩu\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: nhập srp_username.\\nRTMP: nhập tên người dùng.\\nSRT: không sử dụng.\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: nhập srp_password.\\nRTMP: nhập mật khẩu.\\nSRT: nhập cụm mật khẩu mã hóa.\"\nBasic.Settings.Stream.BandwidthTestMode=\"Bật chế độ kiểm tra băng thông\"\nBasic.Settings.Stream.TTVAddon=\"Phần mở rộng tán gẫu Twitch\"\nBasic.Settings.Stream.TTVAddon.None=\"Không có\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV và FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"Cài đặt luồng bị mất\"\nBasic.Settings.Stream.StreamSettingsWarning=\"Mở thiết đặt\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"URL và khoá luồng đang bị thiếu.\\n\\nMở thiết đặt để nhập URL và khoá luồng trong thẻ 'luồng'.\"\nBasic.Settings.Stream.MissingUrl=\"URL luồng đang bị thiếu.\\n\\nMở thiết đặt để nhập URL trong thẻ 'Luồng'.\"\nBasic.Settings.Stream.MissingStreamKey=\"Khoá luồng đang bị thiếu.\\n\\nMở thiết đặt để nhập khoả luồng trong thẻ 'Luồng'.\"\nBasic.Settings.Stream.IgnoreRecommended=\"Bỏ qua các đề xuất về thiết đặt dịch vụ phát luồng\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"Ghi đè cài đặt được đề xuất\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"Cảnh báo: Việc bỏ qua các giới hạn của dịch vụ có thể dẫn đến chất lượng luồng bị giảm sút hoặc ngăn bạn phát luồng.\\n\\nCó chắc là bạn muốn tiếp tục không?\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"Tốc độ bit tối đa của video: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"Tốc độ bit âm thanh tối đa: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"Độ phân giải tối đa: %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"FPS tối đa: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"Chỉ định Máy chủ tùy chỉnh...\"\nBasic.Settings.Stream.ServiceCustomServer=\"Máy chủ tùy chỉnh\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"Bật %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"Băng thông phát luồng tối đa\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"Tự động\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"Số lượng kênh video tối đa\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"Tự động\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"Cho phép phát trực tiếp sang FLV (sử dụng cài đặt tệp ghi đơn giản)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"Ghi đè cấu hình (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"Bật ghi đè cấu hình\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"Video đa kênh\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"Phát sóng đồng thời\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"Phát sóng đồng thời cho phép bạn biên mã và gửi nhiều mức chất lượng video khác nhau. <a href='https://obsproject.com/kb/whip-streaming-guide'>Tìm hiểu thêm</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"Tổng số lớp\"\nBasic.Settings.Stream.AdvancedOptions=\"Tùy chọn Nâng cao\"\nBasic.Settings.Output=\"Đầu ra\"\nBasic.Settings.Output.Format=\"Định dạng ghi hình\"\nBasic.Settings.Output.Format.hMP4=\"MP4 lai (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"MOV lai (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"MP4 bị phân mảnh (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"MOV bị phân mảnh (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"MOV phân mảnh ghi các bản ghi theo từng đoạn và không yêu cầu quá trình hoàn thiện giống như các tệp MOV truyền thống.\\nĐiều này đảm bảo tệp vẫn có thể phát được ngay cả khi quá trình ghi vào đĩa bị gián đoạn, chẳng hạn như do lỗi màn hình xanh hoặc mất điện.\\n\\nĐiều này có thể không tương thích với tất cả trình phát và trình chỉnh sửa. Sử dụng Tập tin → Ghép lại bản ghi để chuyển đổi tệp thành định dạng tương thích hơn nếu cần.\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"MP4 phân mảnh ghi các bản ghi theo từng đoạn và không yêu cầu quá trình hoàn thiện giống như các tệp MP4 truyền thống.\\nĐiều này đảm bảo tệp vẫn có thể phát được ngay cả khi quá trình ghi vào đĩa bị gián đoạn, chẳng hạn như do lỗi màn hình xanh hoặc mất điện.\\n\\nĐiều này có thể không tương thích với tất cả trình phát và trình chỉnh sửa. Sử dụng Tập tin → Ghép lại bản ghi để chuyển đổi tệp thành định dạng tương thích hơn nếu cần.\"\nBasic.Settings.Output.Encoder.Video=\"Bộ biên mã video\"\nBasic.Settings.Output.Encoder.Audio=\"Bộ biên mã âm thanh\"\nBasic.Settings.Output.SelectDirectory=\"Chọn thư mục ghi hình\"\nBasic.Settings.Output.DynamicBitrate=\"Điều chỉnh tốc độ bit linh hoạt để quản lý tắc nghẽn mạng\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"Điều chỉnh tốc độ bit linh hoạt để quản lý tắc nghẽn mạng (Bản Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"Thay vì hạ khung hình để giảm tắc nghẽn mạng, có thể ngay lập tức điều chỉnh tốc độ bit linh hoạt.\\n\\nChú ý điều này có thể dẫn đến tăng độ trễ với người xem nếu xuất hiện một lượng tắc nghẽn lớn.\\nNếu sụt tốc độ bit có thể mất một vài phút để khôi phục.\\n\\nHiện tại chỉ hỗ trợ RTMP.\"\nBasic.Settings.Output.Mode=\"Chế độ đầu ra\"\nBasic.Settings.Output.Mode.Simple=\"Dễ sử dụng\"\nBasic.Settings.Output.Mode.Adv=\"Nâng cao\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg đầu ra\"\nBasic.Settings.Output.UseReplayBuffer=\"Bật Replay Buffer\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"Thời gian phát lại tối đa\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"Bộ nhớ tối đa\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"Ước tính bộ nhớ sử dụng: %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"Cảnh báo: Mức sử dụng bộ nhớ ước tính là %1 MiB lớn hơn mức tối đa được khuyến nghị là %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"Không thể ước tính sử dụng bộ nhớ. Xin vui lòng thiết lập giới hạn bộ nhớ tối đa.\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"Tiền tố tên tệp lưu Replay\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"Hậu tố\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"Bộ đệm phát lại không thể sử dụng khi kiểu ghi được thiết lập thành Đầu ra Tùy chỉnh (FFmpeg).\"\nBasic.Settings.Output.Simple.SavePath=\"Đường dẫn ghi hình\"\nBasic.Settings.Output.Simple.RecordingQuality=\"Chất lượng ghi hình\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"Giống như luồng\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"Chất lượng cao, kích thước tệp trung bình\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"Chất lượng gốc, kích thước tệp lớn\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"Chất lượng bất tổn, kích thước tệp không lồ\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"Cảnh báo: Tốc độ bit luồng phát hình sẽ được đặt thành %1 vì đây là giới hạn tối đa của dịch vụ phát luồng hiện tại.\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"Cảnh báo: Tốc độ bit luồng phát âm thanh sẽ được đặt thành %1 vì đây là giới hạn tối đa của dịch vụ phát luồng hiện tại.\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"Cảnh báo: Việc ghi hình không thể được dừng lại nếu chất lượng ghi hình được đặt là \\\"Giống như luồng\\\".\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"Cảnh báo: Định dạng ghi hiện được chọn không tương thích với (các) bộ biên mã luồng đã chọn.\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"Chú ý: Ghi hình với một bộ biên mã phần mềm ở một chất lượng khác so với luồng sẽ yêu cầu sử dụng thêm CPU nếu bạn phát luồng và ghi hình cùng một lúc.\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"Cảnh báo: Chất lượng bất tổn đã tạo ra kích thước tập tin lớn hơn dự định. Chất lượng bất tổn có thể sử dụng trên 7 GB không gian đĩa trong một phút ở độ phân giải/ tốc độ khung hình cao. Bất tổn không nên được dùng cho bản ghi hình dài ngoại trừ trường hợp bạn có nhiều không gian ổ sẵn dùng. Bộ đệm phát lại không khả dụng khi sử dụng chất lượng lossless.\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"Có chắc là bạn muốn sử dụng chất lượng cao không?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"Cảnh báo chất lượng bất tổn\"\nBasic.Settings.Output.Simple.Encoder.Software=\"Phần mềm (x 264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"Phần cứng (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"Phần cứng (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"Phần cứng (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"Phần cứng (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"Phần cứng (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"Phần cứng (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"Phần cứng (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"Phần cứng (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"Phần cứng (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"Phần cứng (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"Phần mềm (x264 với lượng sử dụng CPU thấp, làm tăng kích thước tệp)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (mặc định)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Rãnh âm Twitch VOD (Sử dụng rãnh âm 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"Rãnh âm thanh\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"Tốc độ khung hình / độ phân giải không tương thích\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"Dịch vụ phát luồng này không hỗ trợ độ phân giải đầu ra và/hoặc tốc độ khung hình hiện tại của bạn. Chúng sẽ được thay đổi thành giá trị tương thích gần nhất:\\n\\n%1\\n\\nBạn có muốn tiếp tục không?\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"Độ phân giải: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"Bộ biên mã không tương thích\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"Dịch vụ phát luồng \\\"%1\\\" không hỗ trợ bộ biên mã \\\"%2\\\". Bộ biên mã này sẽ được đổi thành \\\"%3\\\".\\n\\nCó chắc là bạn muốn tiếp tục không?\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"Dịch vụ phát luồng \\\"%1\\\" không hỗ trợ bộ biên mã \\\"%2\\\" và \\\"%3\\\". Những bộ biên mã này sẽ được thay đổi thành \\\"%4\\\" và \\\"%5\\\".\\n\\nBạn có muốn tiếp tục không?\"\nBasic.Settings.Output.VideoBitrate=\"Tộc độ bit video\"\nBasic.Settings.Output.AudioBitrate=\"Tốc độ bit âm thanh\"\nBasic.Settings.Output.Reconnect=\"Tự động kết nối\"\nBasic.Settings.Output.RetryDelay=\"Độ trễ thử lại\"\nBasic.Settings.Output.MaxRetries=\"Tối đa Retries\"\nBasic.Settings.Output.Advanced=\"Bật cài đặt bộ biên mã (nâng cao)\"\nBasic.Settings.Output.EncoderPreset=\"Cài đặt cho trước bộ biên mã\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1 (mức sử dụng CPU thấp, được chất lượng kém)\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1 (mức sử dụng CPU trung bình, được chất lượng đạt chuẩn)\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1 (mức sử dụng CPU cao, chất lượng cao)\"\nBasic.Settings.Output.CustomEncoderSettings=\"Tùy chỉnh bộ biên mã\"\nBasic.Settings.Output.CustomMuxerSettings=\"Thiết đặt tùy chỉnh Muxer\"\nBasic.Settings.Output.NoSpaceFileName=\"Tạo ra tên tập tin mà không cần Space\"\nBasic.Settings.Output.Adv.Rescale=\"Chỉnh lại kích thước đầu ra\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"Đã tắt\"\nBasic.Settings.Output.Adv.AudioTrack=\"Rãnh âm thanh\"\nBasic.Settings.Output.Adv.Streaming=\"Phát luồng\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"Thiết đặt phát luồng\"\nBasic.Settings.Output.Adv.Audio.Track1=\"Rãnh âm 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"Rãnh âm 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"Rãnh âm 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"Rãnh âm 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"Rãnh âm 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"Rãnh âm 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Rãnh âm Twitch VOD\"\nBasic.Settings.Output.Adv.Encoder=\"Thiết đặt bộ biên mã\"\nBasic.Settings.Output.Adv.Recording=\"Ghi hình\"\nBasic.Settings.Output.Adv.Recording.Settings=\"Cài Đặt Quay Phim\"\nBasic.Settings.Output.Adv.Recording.RecType=\"Loại ghi hình\"\nBasic.Settings.Output.Adv.Recording.Type=\"Loại\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"Chuản\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"Tùy chỉnh đầu ra (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(Sử dụng bộ biên mã luồng)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"Định dạng tệp tin\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"Ghi đè nếu tập tin tồn tại\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"Đầu ra tùy chỉnh (FFmpeg) không có biện pháp bảo vệ nào.\\nHãy cân nhắc sử dụng loại ghi \\\"Tiêu chuẩn\\\".\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg ra loại\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"Sản lượng để URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"Đầu ra vào tập tin\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"Định dạng ghi hình phổ biến\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"Tất cả\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"Đường dẫn của tệp hoặc URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"Định dạng container\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"Âm thanh\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"Định dạng mặc định\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"Thùng chứa định dạng mô tả\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"Bộ biên giải mã âm thanh / video được đoán từ đường dẫn của tệp hoặc URL\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"Bộ biên mã mặc định\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"Vô hiệu hoá bộ biên mã\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"Bộ biên mã video\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"Thiết đặt bộ biên mã video (nếu có)\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"Bộ biên mã âm thanh\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"Thiết đặt bộ biên mã âm thanh (nếu có)\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Thiết đặt bộ ghép (nếu có)\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"Thời gian đặt Keyframe (giây)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"Hiển thị tất cả các bộ biên giải mã (ngay cả khi không tương thích)\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"Thiết đặt FFmpeg\"\nBasic.Settings.Output.EnableSplitFile=\"Tự động phân tách tệp đầu ra\"\nBasic.Settings.Output.SplitFile.TypeTime=\"Phân tách theo giờ\"\nBasic.Settings.Output.SplitFile.TypeSize=\"Phân chia theo kích cỡ\"\nBasic.Settings.Output.SplitFile.TypeManual=\"Chỉ tách thủ công\"\nBasic.Settings.Output.SplitFile.Time=\"Phân chia giờ\"\nBasic.Settings.Output.SplitFile.Size=\"Phân tách từ kích cỡ\"\nScreenshot=\"Đầu ra chụp màn hình\"\nScreenshot.SourceHotkey=\"Chụp màn hình Nguồn được chọn\"\nScreenshot.StudioProgram=\"Chụp màn hình (Chương trình)\"\nScreenshot.Preview=\"Chụp màn hình (Xem trước)\"\nScreenshot.Scene=\"Chụp màn hình (phân cảnh)\"\nScreenshot.Source=\"Chụp màn hình (Nguồn)\"\nFilenameFormatting.TT.CCYY=\"Năm, bốn chữ số\"\nFilenameFormatting.TT.YY=\"Năm, hai số cuối (00-99)\"\nFilenameFormatting.TT.MM=\"Tháng dưới dạng số thập phân (01-12)\"\nFilenameFormatting.TT.DD=\"Ngày trong tháng, không đệm (01-31)\"\nFilenameFormatting.TT.hh=\"Giờ ở định dạng 24h (00-23)\"\nFilenameFormatting.TT.mm=\"Phút (00-59)\"\nFilenameFormatting.TT.ss=\"Giây (00-59)\"\nFilenameFormatting.TT.Percent=\"Một dấu hiệu %\"\nFilenameFormatting.TT.a=\"Tên ngày trong tuần viết tắt\"\nFilenameFormatting.TT.A=\"Tên ngày trong tuần đầy đủ\"\nFilenameFormatting.TT.b=\"Tên tháng viết tắt\"\nFilenameFormatting.TT.B=\"Tên tháng đầy đủ\"\nFilenameFormatting.TT.d=\"Ngày trong tháng, không đệm (01-31)\"\nFilenameFormatting.TT.H=\"Giờ ở định dạng 24h (00-23)\"\nFilenameFormatting.TT.I=\"Giờ ở định dạng 12h (01-12)\"\nFilenameFormatting.TT.m=\"Tháng dưới dạng số thập phân (01-12)\"\nFilenameFormatting.TT.M=\"Phút (00-59)\"\nFilenameFormatting.TT.p=\"Chỉ định AM hoặc PM\"\nFilenameFormatting.TT.s=\"Thời gian tính bằng giây kể từ UNIX epoch\"\nFilenameFormatting.TT.S=\"Giây (00-59)\"\nFilenameFormatting.TT.y=\"Năm, hai số cuối (00-99)\"\nFilenameFormatting.TT.Y=\"Năm\"\nFilenameFormatting.TT.z=\"ISO 8601 bù từ UTC trong múi giờ\"\nFilenameFormatting.TT.Z=\"Tên múi giờ hoặc tên viết tắt\"\nFilenameFormatting.TT.FPS=\"Khung hình trên giây\"\nFilenameFormatting.TT.CRES=\"Độ phân giải cơ sở (bức vẽ)\"\nFilenameFormatting.TT.ORES=\"Độ phân giải (được thu phóng) đầu ra\"\nFilenameFormatting.TT.VF=\"Định dạng video\"\nBasic.Settings.Video.BaseResolution=\"Độ phân giải cơ sở (bức vẽ)\"\nBasic.Settings.Video.ScaledResolution=\"Độ phân giải (Được thu phóng) đầu ra\"\nBasic.Settings.Video.DownscaleFilter=\"Bộ lọc giảm độ phân giải\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[Độ phân giải đã khớp, không cần thiết phải giảm độ phân giải]\"\nBasic.Settings.Video.FPSCommon=\"Giá trị phổ biến khung hình/giây\"\nBasic.Settings.Video.FPSInteger=\"Giá trị số nguyên FPS\"\nBasic.Settings.Video.FPSFraction=\"Phân đoạn FPS giá trị\"\nBasic.Settings.Video.Numerator=\"Tử số\"\nBasic.Settings.Video.Denominator=\"Mẫu số\"\nBasic.Settings.Video.Renderer=\"Trình kết xuất\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (Thử nghiệm)\"\nBasic.Settings.Video.InvalidResolution=\"Giá trị độ phân giải không hợp lệ. Phải là [width]x[height] (tức là năm 1920 x 1080)\"\nBasic.Settings.Video.CurrentlyActive=\"Đầu ra video là hiện đang hoạt động. Hãy tắt vào bất kỳ kết quả đầu ra để thay đổi thiết đặt video.\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear (nhanh nhất, nhưng mờ nếu thu phóng)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic (thu phóng không nhoè, 16 mẫu)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos (thu phóng không nhoè, 36 mẫu)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Vùng (Tổng trọng số, 4/6/9 mẫu)\"\nBasic.Settings.Audio=\"Âm thanh\"\nBasic.Settings.Audio.SampleRate=\"Tốc độ lấy mẫu\"\nBasic.Settings.Audio.Channels=\"Kênh Tivi\"\nBasic.Settings.Audio.Meters=\"Công tơ\"\nBasic.Settings.Audio.MeterDecayRate=\"Tốc độ phân rã\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"Nhanh\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"Trung bình (Loại I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"Chậm (Loại II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"Loại công cụ đo cực đại\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"Âm thu cực đại\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"Âm thực cực đại (Sử dụng nhiều CPU hơn)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"LƯU Ý: Âm thanh vòm đã được kích hoạt.\"\nBasic.Settings.Audio.MultichannelWarning=\"Nếu phát luồng, hãy kiểm tra xem dịch vụ phát luồng của bạn có hỗ trợ cả nhập âm thanh vòm và phát lại âm thanh vòm hay không. Chẳng hạn, Facebook 360 Live hỗ trợ đầy đủ âm thanh vòm; YouTube Live hỗ trợ nhập âm thanh 5.1 (và phát lại trên TV).\\n\\nBộ lọc âm thanh của OBS tương thích với âm thanh vòm, mặc dù chưa chắc đã hỗ trợ các trình cắm VST.\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"Kích hoạt âm thanh vòm?\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"Có chắc là muốn kích hoạt âm thanh vòm không?\"\nBasic.Settings.Audio.Devices=\"Thiết bị âm thanh chung\"\nBasic.Settings.Audio.DesktopDevice=\"Âm thanh máy tính để bàn\"\nBasic.Settings.Audio.DesktopDevice2=\"Âm thanh máy tính để bàn 2\"\nBasic.Settings.Audio.AuxDevice=\"Âm thanh mic/phụ\"\nBasic.Settings.Audio.AuxDevice2=\"Âm thanh mic/phụ 2\"\nBasic.Settings.Audio.AuxDevice3=\"Âm thanh mic/phụ 3\"\nBasic.Settings.Audio.AuxDevice4=\"Âm thanh mic/phụ 4\"\nBasic.Settings.Audio.EnablePushToMute=\"Bật bấm-để-tắt-tiếng\"\nBasic.Settings.Audio.PushToMuteDelay=\"Độ trễ của bấm-để-tắt-tiếng\"\nBasic.Settings.Audio.EnablePushToTalk=\"Bật bấm-để-nói\"\nBasic.Settings.Audio.PushToTalkDelay=\"Độ trễ của bấm-để-nói\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[Thiết bị không được kết nối hoặc không sẵn dùng]\"\nBasic.Settings.Audio.Disabled=\"Đã vô hiệu\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"Chế độ đệm âm thanh có độ trễ thấp (Đối với đầu ra Decklink/NDI)\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"CẢNH BÁO: OBS đã bật bộ đệm âm thanh với độ trễ thấp.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"Chế độ đệm âm thanh có độ trễ thấp có thể khiến âm thanh bị trục trặc hoặc ngừng phát một số nguồn.\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"Bật chế độ đệm âm thanh có độ trễ thấp\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"Có chắc là bạn muốn bật chế độ đệm âm thanh có độ trễ thấp?\"\nBasic.Settings.Accessibility=\"Hỗ trợ tiếp cận\"\nBasic.Settings.Accessibility.ColorOverrides=\"Sử dụng màu khác\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"Đường viền nguồn (Chọn)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"Đường viền nguồn (Cắt)\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"Đường viền nguồn (Di chuột)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"Bộ trộn âm thanh với tần số (-60 to -20dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"Bộ trộn âm thanh với tần số (-20 to -9dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"Bộ trộn âm thanh với tần số (-9 đến 0dB)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"Bộ trộn âm thanh với tần số (-60 to -20dB) (chạy)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"Bộ trộn âm thanh với tần số (-20 to -9dB) (chạy)\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"Bộ trộn âm thanh với tần số (-9 đến 0dB) (chạy)\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"Tông Màu\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"Mặc định\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"Tùy chỉnh\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"Mù màu\"\nBasic.Settings.Advanced=\"Nâng cao\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"Hiện thông báo đầu ra đang hoạt động khi thoát\"\nBasic.Settings.Advanced.General.ProcessPriority=\"Chương trình ưu tiên\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"Cao\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"Trên bình thường\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"Thường\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"Dưới trung bình\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"Rảnh\"\nBasic.Settings.Advanced.FormatWarning=\"Cảnh báo: Các định dạng màu ngoài NV12/P010 chủ yếu dành để ghi hình và không được khuyến nghị sử dụng trong quá trình phát trực tuyến, vì điều này có thể làm tăng mức sử dụng CPU do chuyển đổi định dạng màu.\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"Cảnh báo: Định dạng cao chính xác thường được sử dụng với không gian màu HDR.\"\nBasic.Settings.Advanced.FormatWarning2100=\"Cảnh báo: BT.2020 nên sử dụng định dạng có độ chính xác cao hơn.\"\nBasic.Settings.Advanced.Video.ColorFormat=\"Định dạng màu sắc\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8-bit, 4:2:0, 2 mặt)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8-bit, 4:2:0, 3 mặt)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8-bit, 4:4:4, 3 mặt)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10-bit, 4:2:0, 2 mặt)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10-bit, 4:2:0, 3 mặt)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16-bit, 4:2:2, 2 mặt phẳng)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16-bit, 4:4:4, 2 mặt phẳng)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"Không gian màu\"\nBasic.Settings.Advanced.Video.ColorRange=\"Phạm vi màu\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"Một phần\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"Đầy đủ\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"Mức độ SDR trắng\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR danh nghĩa mức đỉnh\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"Thiết bị theo dõi\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"Mặc định\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"Tắt chỉnh âm của Windows\"\nBasic.Settings.Advanced.StreamDelay=\"Độ trễ luồng phát\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"Độ dài\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"Giữ điểm cắt (làm tăng độ trễ) khi tái kết nối\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"Ước tính bộ nhớ sử dụng: %1 MB\"\nBasic.Settings.Advanced.Network=\"Mạng\"\nBasic.Settings.Advanced.Network.Disabled=\"Giao thức phát trực tuyến hiện tại không hỗ trợ các thay đổi về cài đặt mạng.\"\nBasic.Settings.Advanced.Network.BindToIP=\"Liên kết với IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"Dòng IP\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"Bật tối ưu hóa mạng\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"Bật TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"Cố gắng để đầu ra của RTMP thân thiện hơn với các ứng dụng có độ trễ nhạy khác bằng cách điều tuyến tốc độ băng thông.\\nĐiều này có thể gia tăng nguy cơ sụt khung hình trên đường truyền không ổn định.\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"Tùy chọn sử dụng phím tắt\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"Không bao giờ tắt phím nóng\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"Không dùng phím tắt khi cửa sổ chính không tập trung\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"Không dùng phím tắt khi cửa sổ chính được tập trung\"\nBasic.Settings.Advanced.AutoRemux=\"Tự động ghép thành %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(ghi với định dạng mkv)\"\nBasic.AdvAudio=\"Thuộc tính âm thanh nâng cao\"\nBasic.AdvAudio.ActiveOnly=\"Chỉ những nguồn hoạt động\"\nBasic.AdvAudio.Name=\"Tên\"\nBasic.AdvAudio.Volume=\"Âm lượng\"\nBasic.AdvAudio.VolumeSource=\"Âm Thanh cho %1\"\nBasic.AdvAudio.Mono=\"Đơn\"\nBasic.AdvAudio.MonoSource=\"Nghe Mono ở cả hai bên '%1'\"\nBasic.AdvAudio.Balance=\"Cân bằng\"\nBasic.AdvAudio.BalanceSource=\"Cân bằng với '%1'\"\nBasic.AdvAudio.SyncOffset=\"Phần bù đồng bộ\"\nBasic.AdvAudio.SyncOffsetSource=\"Độ lệch đồng bộ cho '%1'\"\nBasic.AdvAudio.Monitoring=\"Giám sát âm thanh\"\nBasic.AdvAudio.Monitoring.None=\"Tắt giám sát\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"Chỉ giám sát (không âm thanh ra)\"\nBasic.AdvAudio.Monitoring.Both=\"Giám sát và ra âm thanh\"\nBasic.AdvAudio.MonitoringSource=\"Giám sát âm thanh cho '%1'\"\nBasic.AdvAudio.AudioTracks=\"Các rãnh âm\"\nBasic.Settings.Hotkeys=\"Phím nóng\"\nBasic.Settings.Hotkeys.Pair=\"Tổ hợp phím được chia sẻ với '%1' hành động như chuyển\"\nBasic.Settings.Hotkeys.Filter=\"Bộ lọc\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"Lọc theo phím nóng\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"Phím này được dùng chung với một hoặc nhiều tác vụ khác, nháy chuột để hiện các tác vụ trùng hợp\"\nBasic.Settings.Hotkeys.PleaseWait=\"Đang tải phím nóng, vui lòng đợi...\"\nBasic.Hotkeys.SelectScene=\"Chuyển sang phân cảnh\"\nBasic.SystemTray.Show=\"Hiện\"\nBasic.SystemTray.Hide=\"Ẩn\"\nBasic.SystemTray.Message.Reconnecting=\"Đã ngắt kết nối. Đang kết nối lại...\"\nHotkeys.Insert=\"Chèn\"\nHotkeys.Delete=\"Xoá\"\nHotkeys.End=\"Kết thúc\"\nHotkeys.PageUp=\"Lên 1 trang\"\nHotkeys.PageDown=\"Xuống 1 trang\"\nHotkeys.CapsLock=\"Caps lock\"\nHotkeys.Backspace=\"Phím xóa lùi\"\nHotkeys.Tab=\"Thẻ\"\nHotkeys.Print=\"In ra\"\nHotkeys.Pause=\"Tạm dừng\"\nHotkeys.Left=\"Trái\"\nHotkeys.Right=\"Phải\"\nHotkeys.Up=\"Lên\"\nHotkeys.Down=\"Xuống\"\nHotkeys.Menu=\"Bảng chọn\"\nHotkeys.Space=\"Khoảng trắng\"\nHotkeys.NumpadMultiply=\"Numpad nhân\"\nHotkeys.NumpadDivide=\"Numpad chia\"\nHotkeys.NumpadAdd=\"Numpad thêm\"\nHotkeys.NumpadSubtract=\"Numpad trừ\"\nHotkeys.NumpadDecimal=\"Numpad thập phân\"\nHotkeys.AppleKeypadNum=\"%1 (bàn phím)\"\nHotkeys.AppleKeypadMultiply=\"* (Bàn phím)\"\nHotkeys.AppleKeypadDivide=\"/ (Bàn phím)\"\nHotkeys.AppleKeypadAdd=\"+ (Bàn phím)\"\nHotkeys.AppleKeypadSubtract=\"-(Bàn phím)\"\nHotkeys.AppleKeypadDecimal=\". (Bàn phím)\"\nHotkeys.AppleKeypadEqual=\"= (Bàn phím)\"\nHotkeys.MouseButton=\"Chuột %1\"\nHotkeys.Escape=\"Esc (Thoát)\"\nMute=\"Tắt Tiếng\"\nUnmute=\"Bật tiếng\"\nPush-to-mute=\"Bấm để tắt tiếng\"\nPush-to-talk=\"Bấm để nói\"\nSceneItemShow=\"Hiển thị '%1'\"\nSceneItemHide=\"Ẩn '%1'\"\nOutputWarnings.NoTracksSelected=\"Bạn phải chọn ít nhất một rãnh âm\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"Lỗi cài đặt đầu ra\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"Tất cả đầu ra phải có ít nhất một track âm thanh được chọn.\"\nOutputWarnings.MP4Recording=\"Chú ý: các bản ghi hình được lưu ở dạng MP4/MOV sẽ không thể phục hồi nếu các tập tin không thể hoàn thiện (ví dụ: như bị màn hình xanh, mất điện, v.v.). Nếu bạn muốn ghi nhiều rãnh âm thanh thì nên xem xét sử dụng MKV và ghép lại bản ghi sang MP4/MOV sau khi hoàn thành (Tập tin → Ghép lại bản ghi)\"\nOutputWarnings.CannotPause=\"Chú ý: Không thể dừng ghi hình nếu bộ biên mã ghi hình được đặt là \\\"(Sử dụng bộ biên mã luồng)\\\"\"\nOutputWarnings.CodecIncompatible=\"Bộ biên mã video hoặc biên mã âm thanh được chọn đã bị đặt lại do không tương thích. Vui lòng chọn một bộ biên mã tương thích khác trong danh sách.\"\nCodecCompat.Incompatible=\"(Không tương thích với %1)\"\nCodecCompat.CodecPlaceholder=\"Chọn bộ biên mã...\"\nCodecCompat.ContainerPlaceholder=\"Chọn Định dạng...\"\nCodecCompat.CodecMissingOnExit.Title=\"Không có bộ biên mã nào được chọn\"\nCodecCompat.CodecMissingOnExit.Text=\"Ít nhất một bộ biên mã video hoặc âm thanh chưa được đặt. Vui lòng đảm bảo chọn bộ biên mã cho cả ghi hình và phát luồng.\"\nCodecCompat.ContainerMissingOnExit.Title=\"Không có định dạng được chọn\"\nCodecCompat.ContainerMissingOnExit.Text=\"Không có định dạng ghi nào được chọn. Vui lòng chọn định dạng ghi tương thích với bộ biên mã luồng đã chọn.\"\nFinalScene.Title=\"Xóa phân cảnh\"\nFinalScene.Text=\"Cần có ít nhất một phân cảnh.\"\nNoSources.Title=\"Không có nguồn\"\nNoSources.Text=\"Có vẻ như bạn chưa thêm nguồn ghi hình nào rồi, đầu ra giờ chỉ là màn hình đen. Có chắc là bạn muốn tiếp tục không?\"\nNoSources.Text.AddSource=\"Bạn có thể thêm nguồn bằng cách nhấn vào biểu tượng + dưới ô Nguồn trong của sổ chính bất cứ lúc nào.\"\nNoSources.Label=\"Bạn không có nguồn nào cả.\\nNháy vào nút + bên dưới,\\nHoặc nháy chuột phải vào đây để thêm một cái.\"\nChangeBG=\"Thiết lập màu sắc\"\nCustomColor=\"Tùy chỉnh màu sắc\"\nBrowserSource.EnableHardwareAcceleration=\"Kích hoạt tăng tốc phần cứng cho nguồn trình duyệt\"\nAbout=\"Giới thiệu\"\nAbout.Info=\"OBS Studio là một phần mềm phát trực tiếp và ghi hình mã nguồn mở và miễn phí.\"\nAbout.Donate=\"Quyên góp\"\nAbout.GetInvolved=\"Tham gia\"\nAbout.Authors=\"Các tác giả\"\nAbout.License=\"Giấy phép\"\nAbout.Error=\"Lỗi! Không thể đọc tệp.\\n\\nĐi tới: %1\"\nAbout.Contribute=\"Hỗ trợ dự án OBS\"\nAddUrl.Title=\"Thêm nguồn bằng URL\"\nAddUrl.Text=\"Bạn đã thả một URL vào OBS. Điều này sẽ tự động thêm liên kết như là một nguồn. Tiếp tục chứ?\"\nResizeOutputSizeOfSource=\"Thay đổi kích thước đầu ra (kích thước nguồn)\"\nResizeOutputSizeOfSource.Text=\"Độ phân giải cơ sở và độ phân giải đầu ra sẽ được thay dổi kích thước tùy theo kích thước của nguồn hiện tại.\"\nResizeOutputSizeOfSource.Continue=\"Có chắc là bạn muốn tiếp tục không?\"\nPreviewTransition=\"Xem trước cảnh chuyển\"\nImporter=\"Bộ nhập bộ sưu tập cảnh\"\nImporter.SelectCollection=\"Chọn một bộ sưu tập cảnh\"\nImporter.Collection=\"Bộ sưu tập cảnh\"\nImporter.HelpText=\"Thêm tệp vào cửa sổ này để nhập bộ sưu tập từ OBS hoặc các chương trình được hỗ trợ khác.\"\nImporter.Path=\"Đường dẫn cho bộ sưu tập\"\nImporter.Program=\"Ứng dụng đã phát hiện\"\nImporter.AutomaticCollectionPrompt=\"Tự động tìm kiếm bộ sưu tập cảnh\"\nImporter.AutomaticCollectionText=\"OBS có thể tự động tìm bộ sưu tập cảnh từ các chương trình bên thứ ba đã được hỗ trợ. Bạn có muốn OBS tự động tìm các bộ sưu tập cho bạn không?\\n\\nBạn có thể chỉnh lại sau trong mục Thiết đặt > Chung > Bộ nhập.\"\nImporter.SelectFile=\"Duyệt...\"\nRestart=\"Khởi động lại\"\nNeedsRestart=\"OBS Studio cần phải được khởi động lại. Bạn có muốn khởi động lại luôn không?\"\nLoadProfileNeedsRestart=\"Cấu hình có các cài đặt yêu cầu khởi động lại OBS:\\n%1\\n\\nBạn có muốn khởi động lại OBS để các cài đặt này có hiệu lực không?\"\nContextBar.NoSelectedSource=\"Chưa chọn nguồn nào\"\nContextBar.MediaControls.PlayMedia=\"Chơi phương tiện\"\nContextBar.MediaControls.PauseMedia=\"Tạm dừng phương tiện\"\nContextBar.MediaControls.StopMedia=\"Dừng phương tiện\"\nContextBar.MediaControls.RestartMedia=\"Khởi động lại phương tiện\"\nContextBar.MediaControls.PlaylistNext=\"Tiếp trong danh sách\"\nContextBar.MediaControls.PlaylistPrevious=\"Trước trong danh sách\"\nContextBar.MediaControls.BlindSeek=\"Công cụ tìm phương tiện\"\nYouTube.Auth.Ok=\"Đã uỷ quyền thành công.\\nBạn có thể đóng trang này lại rồi.\"\nYouTube.Auth.NoCode=\"Quá trình uỷ quyền đã không được hoàn tất.\"\nYouTube.Auth.NoChannels=\"Không có kênh nào có sẵn trên tài khoản được chọn\"\nYouTube.Auth.WaitingAuth.Title=\"Uỷ quyền người dùng YouTube\"\nYouTube.Auth.WaitingAuth.Text=\"Xin hãy hoàn thành sự uỷ quyền trong trình duyệt bên ngoài của bạn.<br>Nếu trình duyệt bên ngoài không mở, hãy mở liên kết này và hoàn thành việc uỷ quyền:<br>%1\"\nYouTube.AuthError.Text=\"Không thể lấy thông tin kênh: %1.\"\nYouTube.Actions.WindowTitle=\"Thiết lập phát sóng YouTube - Kênh: %1\"\nYouTube.Actions.CreateNewEvent=\"Tạo phát sóng mới\"\nYouTube.Actions.ChooseEvent=\"Chọn phát sóng hiện có\"\nYouTube.Actions.Title=\"Tiêu đề*\"\nYouTube.Actions.MyBroadcast=\"Phát sóng của tôi\"\nYouTube.Actions.Description=\"Mô tả\"\nYouTube.Actions.Privacy=\"Sự riêng tư*\"\nYouTube.Actions.Privacy.Private=\"Riêng tư\"\nYouTube.Actions.Privacy.Public=\"Công khai\"\nYouTube.Actions.Privacy.Unlisted=\"Không công khai\"\nYouTube.Actions.Category=\"Phân loại\"\nYouTube.Actions.Thumbnail=\"Hình thu nhỏ\"\nYouTube.Actions.Thumbnail.SelectFile=\"Chọn tệp...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"Không có tệp nào được chọn\"\nYouTube.Actions.Thumbnail.ClearFile=\"Xóa\"\nYouTube.Actions.MadeForKids=\"Video này có dành cho trẻ em không?*\"\nYouTube.Actions.MadeForKids.Yes=\"Có, video này dành cho trẻ em\"\nYouTube.Actions.MadeForKids.No=\"Không, video này không dành cho trẻ em\"\nYouTube.Actions.AdditionalSettings=\"Thiết đặt bổ sung\"\nYouTube.Actions.Latency=\"Độ trễ\"\nYouTube.Actions.Latency.Normal=\"Trung bình\"\nYouTube.Actions.Latency.Low=\"Thấp\"\nYouTube.Actions.Latency.UltraLow=\"Siêu thấp\"\nYouTube.Actions.EnableAutoStart=\"Bật tự bắt đầu\"\nYouTube.Actions.EnableAutoStop=\"Bật tự dừng lại\"\nYouTube.Actions.AutoStartStop.TT=\"Chỉ rõ nếu muốn lịch phát sóng tự động bắt đầu\"\nYouTube.Actions.EnableDVR=\"Bật DVR\"\nYouTube.Actions.360Video=\"video 360\"\nYouTube.Actions.ScheduleForLater=\"Thiết đặt \\\"ghi hình sau\\\"\"\nYouTube.Actions.RememberSettings=\"Ghi nhớ các thiết đặt này\"\nYouTube.Actions.Create_Ready=\"Tạo phát sóng\"\nYouTube.Actions.Create_GoLive=\"Tạo phát sóng và bắt đầu phát luồng\"\nYouTube.Actions.Choose_Ready=\"Chọn kênh phát sóng\"\nYouTube.Actions.Choose_GoLive=\"Chọn kênh phát sóng và bắt đầu phát trực tiếp\"\nYouTube.Actions.Create_Schedule=\"Lên lịch phát sóng\"\nYouTube.Actions.Create_Schedule_Ready=\"Lên lịch và chọn phát sóng\"\nYouTube.Actions.Dashboard=\"Mở YouTube Studio\"\nYouTube.Actions.Error.Title=\"Lỗi tạo kênh phát trực tiếp\"\nYouTube.Actions.Error.Text=\"Lỗi truy cập YouTube '%1'.<br/>Một lời mô tả lỗi chi tiết hơn có thể được tìm thấy tại <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"Lỗi truy cập YouTube. Hãy kiểm tra kết nối mạng của bạn hoặc truy cập máy chủ YouTube của bạn.\"\nYouTube.Actions.Error.NoBroadcastCreated=\"Lỗi tạo lập phát sóng '%1'.<br/>Một lời mô tả lỗi chi tiết hơn có thể được tìm thấy tại <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"Không có luồng nào được tạo ra. Xin vui lòng liên kết lại tài khoản của bạn.\"\nYouTube.Actions.Error.YouTubeApi=\"Lỗi API YouTube. Xin vui lòng xem tệp nhật kí để biết thêm thông tin.\"\nYouTube.Actions.Error.BroadcastNotFound=\"Không tìm thấy phát sóng được chọn.\"\nYouTube.Actions.Error.FileMissing=\"Tệp được chọn không tồn tại.\"\nYouTube.Actions.Error.FileOpeningFailed=\"Không thể mở tệp được chọn.\"\nYouTube.Actions.Error.FileTooLarge=\"Tệp được chọn quá lớn (Giới hạn: 2 MiB).\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"Chuyển tiếp phát sóng thất bại: %1<br/><br/>Nếu lỗi này tiếp tục <a href='https://studio.youtube.com/video/%2/livestreaming'>hãy mở mục phát trực tiếp trong YouTube Studio</a> và thử lại\"\nYouTube.Actions.Error.BroadcastTestStarting=\"Ghi hình đang chuyển sang giai đoạn kiểm tra. Xin thử lại trong 10-30 giây\"\nYouTube.Actions.EventsLoading=\"Đang tải danh sách sự kiện...\"\nYouTube.Actions.EventCreated.Title=\"Sự kiện đã được tạo\"\nYouTube.Actions.EventCreated.Text=\"Sự kiện đã được tạo thành công.\"\nYouTube.Actions.Stream=\"Luồng phát\"\nYouTube.Actions.Stream.ScheduledFor=\"Đã được lên lịch vào %1\"\nYouTube.Actions.Stream.Resume=\"Tiếp tục luồng phát đang bị gián đoạn\"\nYouTube.Actions.Stream.YTStudio=\"Do YouTube Studio tự động tạo\"\nYouTube.Actions.Notify.CreatingBroadcast=\"Đang tạo kênh phát sóng trực tiếp, vui lòng chờ...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"Bắt buộc khởi động thủ công\"\nYouTube.Actions.AutoStartStreamingWarning=\"Tự bắt đầu đã bị vô hiệu hoá cho sự kiện này, vui lòng nhấn vào \\\"Bắt đầu\\\" để bắt đầu phát sóng.\"\nYouTube.Actions.AutoStopStreamingWarning=\"Bạn sẽ không thể kết nối lại.<br>Luồng của bạn sẽ dừng lại và bạn sẽ không còn trực tiếp nữa.\"\nYouTube.Errors.liveStreamingNotEnabled=\"Phát trực tiếp chưa được kích hoạt trên kênh YouTube được chọn.<br/><br/>Xem <a href='https://www.youtube.com/features'>youtube.com/features</a> để biết thêm thông tin.\"\nYouTube.Errors.livePermissionBlocked=\"Phát  trực tiếp hiện không có sẵn trên kênh YouTube được chọn.<br/>Xin lưu ý rằng có thể cần đến 24 tiếng đồng hồ để có thể phát trực tiếp sau khi kích hoạt nó trong phần thiết đặt kênh của bạn.<br/><br/>Xem <a href='https://www.youtube.com/features'>youtube.com/features</a> để biết thêm chi tiết.\"\nYouTube.Errors.errorExecutingTransition=\"Chuyển cảnh thất bại do lỗi máy chủ. Xin thử lại trong vài giây\"\nYouTube.Errors.errorStreamInactive=\"YouTube hiện không nhận dữ liệu từ luồng của bạn. Xin vui lòng kiểm tra cấu hình và thử lại.\"\nYouTube.Errors.invalidTransition=\"Chuyển cảnh vừa rồi không hợp lệ. Việc này có thể do luồng chưa hoàn thành chuyển cảnh trước đó. Xin chờ vài giây và thử lại.\"\nYouTube.DocksRemoval.Title=\"Xóa định cố trình duyệt YouTube cũ\"\nYouTube.DocksRemoval.Text=\"Định cố trình duyệt này sẽ bị xóa vì hiện đã bất cập:\\n\\n%1\\nThay vào đó, hãy sử dụng \\\"định cố/ điều khiển luồng phát YouTube\\\".\"\nConfigDownload.WarningMessageTitle=\"Cảnh báo\"\nFailedToStartStream.MissingConfigURL=\"Không có URL cấu hình nào khả dụng cho dịch vụ hiện tại\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"URL RTMP tùy chỉnh không được chỉ định\"\nFailedToStartStream.InvalidCustomConfig=\"Cấu hình tùy chỉnh không hợp lệ\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"Không tạo được dịch vụ video đa luồng\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"Không tạo được đầu ra video RTMP đa luồng\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC không khả dụng.\\n\\nKhông tìm thấy bộ biên mã '%1'\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"Không tạo được bộ biên mã video '%1' (loại: '%2')\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"Không lấy được thông tin video OBS khi tạo bộ biên mã '%1' (loại: '%2')\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"Không tạo được bộ biên mã âm thanh\"\nFailedToStartStream.NoRTMPURLInConfig=\"Cấu hình không chứa URL RTMP(S) của luồng mục tiêu\"\nFailedToStartStream.FallbackToDefault=\"Không thể bắt đầu phát trực tiếp bằng %1; bạn có muốn thử lại bằng cách sử dụng cài đặt mã hóa đơn không?\"\nFailedToStartStream.ConfigRequestFailed=\"Không thể lấy cấu hình từ %1<br><br>Lỗi HTTP: %2\"\nFailedToStartStream.WarningUnknownStatus=\"Đã nhận được giá trị trạng thái không xác định '%1'\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\nBạn có muốn tiếp tục phát trực tuyến mà không có %1 không?\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\nBạn có muốn tiếp tục phát trực tuyến không?\"\nFailedToStartStream.MissingEncoderConfigs=\"Cấu hình phát trực tiếp không bao gồm cấu hình bộ biên mã\"\nFailedToStartStream.InvalidEncoderConfig=\"Cấu hình phát trực tiếp có chứa cấu hình bộ biên mã không hợp lệ\"\nFailedToStartStream.StatusMissingHTML=\"Yêu cầu phát trực tiếp trả về lỗi không xác định\"\nFailedToStartStream.NoConfigSupplied=\"Thiếu cấu hình\"\nMultitrackVideo.Info=\"%1 tự động tối ưu hóa cài đặt của bạn để mã hóa và gửi nhiều chất lượng video tới. Việc chọn tùy chọn này sẽ gửi %2 thông tin về máy tính và cài đặt phần mềm của bạn.\"\nMultitrackVideo.IncompatibleSettings.Title=\"Cài đặt không tương thích\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 hiện không tương thích với:\\n\\n%2\\nĐể tiếp tục phát trực tuyến với %1, hãy tắt các cài đặt không tương thích:\\n\\n%3\\nvà Bắt đầu phát trực tuyến lại.\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"Tắt cho lượt phát trực tiếp này và Bắt đầu phát trực tuyến\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"Cập nhật Cài đặt và Bắt đầu Phát trực tuyến\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 hiện không tương thích với [Âm thanh → Chung → Kênh] được đặt thành '%2', %3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[Âm thanh → Chung → Kênh] cần được đặt thành '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 yêu cầu nhiều cài đặt khác nhau cho [Âm thanh → Chung → Kênh]\"\nBasic.OpenPluginManager=\"Trình quản lí trình cắm\"\nPluginManager=\"Trình quản lí trình cắm\"\nPluginManager.Restart=\"OBS Studio cần phải được khởi động lại để áp dụng các thay đổi cho trình cắm\"\nPluginManager.MissingPlugin=\"[KHÔNG THẤY TRÌNH CẮM]\"\nPluginManager.Section.Discover=\"Duyệt\"\nPluginManager.Section.Manage=\"Đã cài\"\nPluginManager.Section.Updates=\"Cập nhật\"\nPluginManager.Section.Manage.Title=\"Quản lí các trình cắm đã bật\"\n"
  },
  {
    "path": "frontend/data/locale/zh-CN.ini",
    "content": "Language=\"简体中文\"\nOK=\"确定\"\nApply=\"应用\"\nCancel=\"取消\"\nClose=\"关闭\"\nSave=\"保存\"\nDiscard=\"不保存\"\nDisable=\"禁用\"\nYes=\"是\"\nNo=\"否\"\nAdd=\"添加\"\nRemove=\"删除\"\nRename=\"重命名\"\nInteract=\"交互\"\nFilters=\"滤镜\"\nProperties=\"设置\"\nMoveUp=\"上移\"\nMoveDown=\"下移\"\nSettings=\"设置\"\nDisplay=\"显示器\"\nName=\"名称\"\nExit=\"退出\"\nMixer=\"混音器\"\nBrowse=\"浏览\"\nMono=\"单声道\"\nStereo=\"立体声\"\nDroppedFrames=\"丢帧 %1 (%2%)\"\nProjector.Open.Program=\"打开输出投影\"\nProjector.Open.Preview=\"打开预览投影\"\nProjector.Open.Scene=\"打开场景投影\"\nProjector.Open.Source=\"打开源投影\"\nProjector.Open.Multiview=\"打开多视图\"\nProjector.Display=\"显示器：%1\"\nProjector.Window=\"新窗口\"\nProjector.Title=\"投影\"\nProjector.Title.Scene=\"场景：%1\"\nProjector.Title.Source=\"源：%1\"\nProjector.Title.Multiview=\"多视图\"\nProjector.ResizeWindowToContent=\"调整窗口以适配内容\"\nClear=\"清除\"\nRevert=\"还原\"\nShow=\"显示\"\nHide=\"隐藏\"\nUnhideAll=\"全部取消隐藏\"\nUntitled=\"未命名\"\nNew=\"新建…\"\nDuplicate=\"复制\"\nEnable=\"启用\"\nDisableOSXVSync=\"禁用 macOS 垂直同步\"\nResetOSXVSyncOnExit=\"退出时重置 macOS 垂直同步\"\nHighResourceUsage=\"编码过载！请考虑降低视频质量设置或使用更快的编码预设。\"\nTransition=\"转场动画\"\nQuickTransitions=\"快捷转场\"\nFadeToBlack=\"淡出到黑色\"\nLeft=\"左\"\nRight=\"右\"\nTop=\"顶部\"\nBottom=\"底部\"\nReset=\"重置\"\nHours=\"小时\"\nMinutes=\"分钟\"\nSeconds=\"秒\"\nDeprecated=\"已弃用\"\nReplayBuffer=\"回放缓存\"\nImport=\"导入\"\nExport=\"导出\"\nCopy=\"复制\"\nPaste=\"粘贴\"\nPasteReference=\"粘贴 (引用)\"\nPasteDuplicate=\"粘贴 (副本)\"\nRemuxRecordings=\"录像封装\"\nNext=\"下一步\"\nBack=\"上一步\"\nDefaults=\"默认值\"\nRestoreDefaults=\"恢复预设值\"\nHideMixer=\"在混音器中隐藏\"\nTransitionOverride=\"覆盖转场动画\"\nShowTransition=\"显示转场动画\"\nHideTransition=\"隐藏转场动画\"\nNone=\"无\"\nStudioMode.Preview=\"预览\"\nStudioMode.Program=\"输出\"\nStudioMode.PreviewSceneName=\"预览：%1\"\nStudioMode.ProgramSceneName=\"输出：%1\"\nShowInMultiview=\"在多视图中显示\"\nVerticalLayout=\"垂直布局\"\nGroup=\"群组\"\nDoNotShowAgain=\"不再显示\"\nDefault=\"(默认)\"\nCalculating=\"计算中……\"\nFullscreen=\"全屏\"\nWindowed=\"窗口化\"\nRefreshBrowser=\"刷新\"\nAspectRatio=\"长宽比 <b>%1:%2</b>\"\nLockVolume=\"锁定音量\"\nLogViewer=\"日志查看器\"\nShowOnStartup=\"启动时显示\"\nOpenFile=\"打开文件\"\nAddScene=\"添加场景\"\nAddSource=\"添加源\"\nRemoveScene=\"删除所选场景\"\nRemoveSource=\"删除所选源\"\nMoveSceneUp=\"向上移动场景\"\nMoveSceneDown=\"向下移动场景\"\nMoveSourceUp=\"向上移动源\"\nMoveSourceDown=\"向下移动源\"\nSourceProperties=\"打开来源设置\"\nSourceFilters=\"打开源滤镜\"\nMixerToolbarMenu=\"混音器菜单\"\nSceneFilters=\"打开场景滤镜\"\nList=\"列表\"\nGrid=\"网格\"\nAutomatic=\"自动\"\nComingSoon=\"即将推出\"\nPluginsFailedToLoad.Title=\"插件加载出错\"\nPluginsFailedToLoad.Text=\"以下 OBS 插件加载失败：\\n\\n%1\\n请更新或移除这些插件。\"\nAlreadyRunning.Title=\"OBS 已在运行\"\nAlreadyRunning.Text=\"OBS 已经在运行中！若你真的想要这样做，请在启动新的 OBS 前关闭其他正在在运行中的 OBS。如果你把 OBS 设置成最小化到系统托盘，请检查它是否仍在运行。\"\nAlreadyRunning.LaunchAnyway=\"仍然启动\"\nCrashHandling.Dialog.Title=\"检测到 OBS Studio 崩溃\"\nCrashHandling.Labels.Text=\"OBS Studio 未正确关闭。\\n\\n是否以安全模式运行（禁用第三方插件、脚本和 WebSocket）？\"\nCrashHandling.Labels.PrivacyNotice=\"您还可以选择自动将最新的崩溃报告上传到 OBSProject。<br /><br />在上传任何文件之前，请阅读<a href='https://obsproject.com/privacy-policy'>隐私政策</a>，并特别注意有关文件上传的部分。\"\nCrashHandling.Checkbox.SendReport=\"我已阅读隐私政策并同意上传。\"\nCrashHandling.Buttons.LaunchSafe=\"以安全模式运行\"\nCrashHandling.Buttons.LaunchNormal=\"以正常模式运行\"\nCrashHandling.Errors.UploadJSONError=\"尝试上传最新的崩溃日志时出错。请稍后重试。\"\nCrashHandling.Errors.Title=\"崩溃日志上传错误\"\nSafeMode.Restart=\"您要以安全模式重启 OBS（禁用第三方插件、脚本和 WebSocket）吗？\"\nSafeMode.RestartNormal=\"您要以正常模式重启 OBS 吗？\"\nChromeOS.Title=\"不支持的平台\"\nChromeOS.Text=\"OBS 似乎在 ChromeOS 容器内运行。此平台不受支持.\"\nWine.Title=\"检测到 Wine 虚拟机\"\nWine.Text=\"不支持在 Wine 上运行OBS，许多功能如采集或设备源将无法使用或受到限制。<br><br>建议运行原生版本的 OBS 例如 <a href='https://flathub.org/apps/details/com.obsproject.Studio'>我们的 Flatpak 版本</a> 或者您的操作系统自带的软件源里的包。\"\nDockCloseWarning.Title=\"关闭停靠窗口\"\nDockCloseWarning.Text=\"您刚刚关闭了一个停靠窗口。如果您想再次显示它，请使用菜单栏上的停靠窗口菜单。\"\nExtraBrowsers=\"自定义浏览器停靠窗口\"\nExtraBrowsers.Info=\"通过指定名称和 URL 来添加停靠窗口，然后选择“应用”或“关闭”来打开这些停靠窗口。你可以在任何时刻添加或删除这些停靠窗口。\"\nExtraBrowsers.DockName=\"停靠窗口名称\"\nAuth.Authing.Title=\"正在认证中…\"\nAuth.Authing.Text=\"正在与 %1 进行身份认证，请稍候…\"\nAuth.AuthFailure.Title=\"身份认证失败\"\nAuth.AuthFailure.Text=\"无法与 %1 进行身份认证：\\n\\n%2: %3\"\nAuth.InvalidScope.Title=\"需要身份认证\"\nAuth.InvalidScope.Text=\"%1 的身份认证要求已更改。某些功能可能不可用。\"\nAuth.LoadingChannel.Title=\"正在加载频道信息…\"\nAuth.LoadingChannel.Text=\"正在加载 %1 的频道信息，请稍候…\"\nAuth.LoadingChannel.Error=\"无法获取频道信息。\"\nAuth.ChannelFailure.Title=\"无法加载频道\"\nAuth.ChannelFailure.Text=\"为 %1 加载频道信息失败\\n\\n%2: %3\"\nAuth.Chat=\"聊天\"\nAuth.StreamInfo=\"直播信息\"\nTwitchAuth.Stats=\"Twitch 统计\"\nTwitchAuth.Feed=\"Twitch 活动源\"\nTwitchAuth.TwoFactorFail.Title=\"无法查询推流码\"\nTwitchAuth.TwoFactorFail.Text=\"OBS 无法连接到您的 Twitch 账户。请确保在 <a href='https://www.twitch.tv/settings/security'>Twitch 安全性设置</a>中开启了双重身份认证，只有开启后才能开始推流。\"\nRestreamAuth.Channels=\"Restream 频道\"\nCopy.Filters=\"复制滤镜\"\nPaste.Filters=\"粘贴滤镜\"\nBrowserPanelInit.Title=\"正在初始化浏览器…\"\nBrowserPanelInit.Text=\"正在初始化浏览器，请稍候…\"\nBandwidthTest.Region=\"地区\"\nBandwidthTest.Region.US=\"美国\"\nBandwidthTest.Region.EU=\"欧洲\"\nBandwidthTest.Region.Asia=\"亚洲\"\nBandwidthTest.Region.Other=\"其他\"\nBasic.AutoConfig=\"自动配置向导\"\nBasic.AutoConfig.ApplySettings=\"应用设置\"\nBasic.AutoConfig.StartPage=\"使用场景\"\nBasic.AutoConfig.StartPage.SubTitle=\"选择你的使用目的\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"优先优化直播，其次为录制\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"仅优化录制，将不会优化直播\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"只使用虚拟摄像头\"\nBasic.AutoConfig.VideoPage=\"视频设置\"\nBasic.AutoConfig.VideoPage.SubTitle=\"指定您想要使用的视频设置\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"使用当前值（%1x%2）\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"显示器 %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"使用当前值 (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 或 30 均可，优先选择 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 或 30 均可，优先高分辨率\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"注意：画布（基础）分辨率并不一定要与推流或录制的分辨率相同。实际推流或录制的分辨率可从画布分辨率缩小，以降低对资源的使用或对码率的需求。\"\nBasic.AutoConfig.StreamPage=\"直播信息\"\nBasic.AutoConfig.StreamPage.SubTitle=\"请输入你的推流信息\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"连接账户 (推荐)\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"解除账户连接\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"解除账户连接？\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"此更改将立即应用。您确定要解除账户连接吗？\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"获取推流码\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"更多信息\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"使用推流码\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"使用推流码 (高级)\"\nBasic.AutoConfig.StreamPage.Service=\"服务\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"显示全部...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"自定义...\"\nBasic.AutoConfig.StreamPage.Server=\"服务器\"\nBasic.AutoConfig.StreamPage.StreamKey=\"推流码\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST: 输入加密密钥\\nRTMP: 输入平台提供的密钥\\nSRT: 输入 streamid，若平台未使用请忽略\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"推流码\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Bearer 令牌\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"已连接的账户\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"通过带宽测试估计码率(可能需要几分钟)\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"优先选择硬件编码\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"硬件编码可大量降低 CPU 的使用率，但要获得与软件编码相同的质量，可能需要更多的码率。\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"直播警告\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"带宽测试将开始串流无音频的随机视频数据。如果可以，建议你暂时关闭视频保存功能，并把串流设置成私人直到测试结束。要继续吗？\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"测试 %1\"\nBasic.AutoConfig.TestPage=\"最终结果\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"该程序目前正在执行一系列的测试来估计最理想的设置\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"测试完成\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"执行带宽测试, 这可能需要几分钟...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"未找到该服务的协议输出\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"正在连接到 %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"无法连接任何服务器, 请检查你的网络连接并重试.\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"测试带宽: %1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"测试串流编码器中，这可能需要一分钟的时间...\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"测试录制编码器中，这可能需要一分钟的时间...\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"未能启动编码器\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"正在测试 %1x%2 %3 FPS...\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"直播编码器\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"录制编码器\"\nBasic.AutoConfig.TestPage.Result.Header=\"程序已确定这些估计设置是最适合你:\"\nBasic.AutoConfig.TestPage.Result.Footer=\"要使用这些设置，单击“应用设置”。要重新配置向导并再试一次，单击“后退”。要自己手动配置设置，单击“取消”，然后打开设置。\"\nBasic.AutoConfig.Info=\"自动配置向导将根据您的计算机配置和互联网速度来决定最佳设置。\"\nBasic.AutoConfig.RunAnytime=\"也可以随时通过工具菜单去运行。\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"串流 (缩放) 分辨率\"\nBasic.Stats=\"统计\"\nBasic.Stats.CPUUsage=\"CPU 使用率\"\nBasic.Stats.HDDSpaceAvailable=\"可用磁盘空间\"\nBasic.Stats.MemoryUsage=\"内存使用\"\nBasic.Stats.AverageTimeToRender=\"渲染帧平均时间\"\nBasic.Stats.SkippedFrames=\"由于编码延迟跳过的帧\"\nBasic.Stats.MissedFrames=\"由于渲染延迟错过的帧\"\nBasic.Stats.Output.Stream=\"直播\"\nBasic.Stats.Output.Recording=\"录制\"\nBasic.Stats.Status=\"状态\"\nBasic.Stats.Status.Recording=\"录制中\"\nBasic.Stats.Status.Live=\"直播中\"\nBasic.Stats.Status.Reconnecting=\"重新连接中\"\nBasic.Stats.Status.Inactive=\"未激活\"\nBasic.Stats.Status.Active=\"已激活\"\nBasic.Stats.DroppedFrames=\"丢弃的帧(网络)\"\nBasic.Stats.MegabytesSent=\"总数据输出\"\nBasic.Stats.Bitrate=\"码率\"\nBasic.Stats.DiskFullIn=\"硬盘装满前剩余录制时间（约）\"\nBasic.Stats.DiskFullIn.Text=\"%1 小时 %2 分钟\"\nBasic.Stats.ResetStats=\"重置统计数据\"\nResetUIWarning.Title=\"您确定要重置界面吗？\"\nResetUIWarning.Text=\"重置界面将隐藏额外的停靠窗口。如果希望重新显示这些停靠窗口，则需要从停靠窗口菜单中取消隐藏。\\n\\n您确定要重置界面吗？\"\nUpdater.Title=\"有可用的更新\"\nUpdater.Text=\"有可用的新版本:\"\nUpdater.UpdateNow=\"现在更新\"\nUpdater.RemindMeLater=\"稍后再提醒我\"\nUpdater.Skip=\"跳过版本\"\nUpdater.NoUpdatesAvailable.Title=\"无可用更新\"\nUpdater.NoUpdatesAvailable.Text=\"当前没有可用更新\"\nUpdater.BranchNotFound.Title=\"更新通道已删除\"\nUpdater.BranchNotFound.Text=\"你选择的更新通道已无法使用，OBS 已恢复为默认通道。\"\nUpdater.RepairButUpdatesAvailable.Title=\"完整性检查不可用\"\nUpdater.RepairButUpdatesAvailable.Text=\"检查文件完整性仅在最新版本可用。请至帮助→检查更新确认并更新您的 OBS 版本。\"\nUpdater.RepairConfirm.Title=\"确认完整性检查\"\nUpdater.RepairConfirm.Text=\"完整性检查将扫描您的 OBS，找出损坏、更改的文件并重新下载。此操作可能需要一定时间。\\n\\n您是否希望继续？\"\nUpdater.FailedToLaunch=\"启动更新程序失败\"\nQuickTransitions.SwapScenes=\"在转场动画之后切换预览/输出场景\"\nQuickTransitions.SwapScenesTT=\"在转场动画后，将预览的场景换至输出，将输出的场景（如果未被删除）换回至预览。\\n该功能无法用于撤销对原输出场景的更改。\"\nQuickTransitions.DuplicateScene=\"复制场景\"\nQuickTransitions.DuplicateSceneTT=\"编辑与当前输出同一场景时，可以修改源的变换和可见性而不影响输出。\\n若要修改源的属性而不影响程序输出，应当使用“复制源”功能。\\n切换此开关会重置当前的输出场景（如果场景未被删除）。\"\nQuickTransitions.EditProperties=\"复制源\"\nQuickTransitions.EditPropertiesTT=\"编辑与当前输出同一场景时，可以修改源的属性而不影响输出。\\n只有开启了“复制场景”之后才能开启此功能。\\n某些源（如采集或媒体源）并不支持此功能，且不能单独修改。\\n切换此开关会重置当前的输出场景（如果场景未被删除）。\\n\\n警告：因为需要复制源，可能会占用额外的系统或视频资源。\"\nQuickTransitions.HotkeyName=\"快速转场动画：%1\"\nBasic.AddTransition=\"添加可配置的转场动画\"\nBasic.RemoveTransition=\"删除可配置的转场动画\"\nBasic.TransitionProperties=\"转场动画属性\"\nBasic.SceneTransitions=\"转场动画\"\nBasic.TransitionDuration=\"时长\"\nBasic.TogglePreviewProgramMode=\"工作室模式\"\nBasic.EnablePreviewProgramMode=\"开启工作室模式\"\nBasic.DisablePreviewProgramMode=\"关闭工作室模式\"\nUndo.Undo=\"撤销\"\nUndo.Redo=\"重做\"\nUndo.Add=\"添加 '%1'\"\nUndo.Delete=\"删除 '%1'\"\nUndo.Rename=\"重命名 '%1'\"\nUndo.SceneCollection.Switch=\"切换到 '%1'\"\nUndo.Item.Undo=\"撤销 %1\"\nUndo.Item.Redo=\"重做 %1\"\nUndo.Sources.Multi=\"删除 %1 个来源\"\nUndo.Filters=\"修改 \\\"%1\\\" 滤镜\"\nUndo.Filters.Paste.Single=\"将滤镜 '%1' 粘贴到 '%2'\"\nUndo.Filters.Paste.Multiple=\"将滤镜从 '%1' 复制到 '%2'\"\nUndo.Transform=\"'%1'中的源变换\"\nUndo.Transform.Paste=\"在'%1'中粘贴变换\"\nUndo.Transform.Rotate=\"在 '%1' 中旋转\"\nUndo.Transform.Reset=\"'%1'中重置变换\"\nUndo.Transform.HFlip=\"'%1' 的水平翻转\"\nUndo.Transform.VFlip=\"'%1' 的垂直翻转\"\nUndo.Transform.FitToScreen=\"在“%1”中的按比例适配屏幕操作\"\nUndo.Transform.StretchToScreen=\"'%1' 中拉伸至屏幕的操作\"\nUndo.Transform.Center=\"'%1' 中屏幕居中的操作\"\nUndo.Transform.VCenter=\"'%1' 中屏幕垂直居中的操作\"\nUndo.Transform.HCenter=\"'%1' 中屏幕水平居中的操作\"\nUndo.Volume.Change=\"修改 \\\"%1\\\" 音量\"\nUndo.Volume.Mute=\"对 '%1' 设置静音\"\nUndo.Volume.Unmute=\"对 '%1' 取消静音\"\nUndo.Balance.Change=\"'%1' 上的声音平衡变更\"\nUndo.SyncOffset.Change=\"'%1' 上的同步偏移变更\"\nUndo.MonitoringType.Change=\"'%1' 上的音频监听变更\"\nUndo.Mixers.Change=\"'%1' 上的混音器变更\"\nUndo.ForceMono.On=\"'%1' 上开启强制单声道\"\nUndo.ForceMono.Off=\"'%1' 上禁用强制单声道\"\nUndo.Properties=\"修改 \\\"%1\\\" 设置\"\nUndo.Scene.Duplicate=\"复制场景 '%1'\"\nUndo.ShowTransition=\"显示'%1'上的转场动画\"\nUndo.HideTransition=\"隐藏'%1'上的转场动画\"\nUndo.ShowSceneItem=\"在 '%1' 中显示 '%2'\"\nUndo.HideSceneItem=\"在 '%1' 中隐藏 '%2'\"\nUndo.ReorderSources=\"在 '%1' 中重新排序来源\"\nUndo.MoveUp=\"在 '%2' 中上移 '%1'\"\nUndo.MoveDown=\"在 '%2' 中下移 '%1'\"\nUndo.MoveToTop=\"在 '%2' 中将 '%1' 移至顶部\"\nUndo.MoveToBottom=\"在 '%2' 中将 '%1' 移至底部\"\nUndo.PasteSource=\"在 '%1' 中粘贴源\"\nUndo.PasteSourceRef=\"在 '%1' 中粘贴源引用\"\nUndo.GroupItems=\"分组项目至 '%1'\"\nTransitionNameDlg.Text=\"请输入转场动画的名称\"\nTransitionNameDlg.Title=\"转场动画名称\"\nTitleBar.SafeMode=\"安全模式\"\nTitleBar.PortableMode=\"便携模式\"\nTitleBar.Profile=\"配置文件\"\nTitleBar.Scenes=\"场景\"\nNameExists.Title=\"名称已存在\"\nNameExists.Text=\"该名称已被使用。\"\nNoNameEntered.Title=\"请输入一个有效的名称\"\nNoNameEntered.Text=\"您不能使用空白名称。\"\nConfirmStart.Title=\"开始直播？\"\nConfirmStart.Text=\"你确定你想要开启直播？\"\nConfirmStop.Title=\"停止直播？\"\nConfirmStop.Text=\"你确定你想要停止直播？\"\nConfirmStopRecord.Title=\"停止录制？\"\nConfirmStopRecord.Text=\"你确定要停止录制吗？\"\nConfirmBWTest.Title=\"开始带宽测试？\"\nConfirmBWTest.Text=\"您已经在带宽测试模式下配置了 OBS。此模式允许在不开启频道的情况下进行网络测试。完成测试后，您需要禁用它，以便观众能够看到您的直播。\\n\\n是否继续？\"\nConfirmExit.Title=\"输出正在活动中\"\nConfirmExit.Text=\"OBS 目前正在进行推流 / 录制任务，确认停止当前正在进行的所有任务并关闭程序吗？\"\nConfirmRemove.Title=\"确认删除\"\nConfirmRemove.Text=\"您确定要移除“%1”吗？\"\nConfirmRemove.TextMultiple=\"您确定要删除 %1 项目吗？\"\nConfirmReset.Title=\"重置设置\"\nConfirmReset.Text=\"确定要恢复默认设置吗？\"\nOutput.StartStreamFailed=\"开启直播失败\"\nOutput.StartRecordingFailed=\"启动录像失败\"\nOutput.StartReplayFailed=\"启动回放缓存失败\"\nOutput.StartVirtualCamFailed=\"启动虚拟摄像头失败\"\nOutput.StartFailedGeneric=\"启动输出失败. 请检查日志来了解细节.\\n\\n注意: 如果你使用的是 NVENC 或 AMD 编码器, 请确保您的视频驱动程序是最新的.\"\nOutput.ReplayBuffer.PauseWarning.Title=\"暂停时无法保存回放\"\nOutput.ReplayBuffer.PauseWarning.Text=\"警告：在录制暂停时无法保存回放。\"\nOutput.ConnectFail.Title=\"连接失败\"\nOutput.ConnectFail.BadPath=\"无效的路径或URL。请检查您的设置以确认它们是有效的。\"\nOutput.ConnectFail.ConnectFailed=\"无法连接到服务器\"\nOutput.ConnectFail.InvalidStream=\"无法访问指定的频道或推流码，请仔细检查你的推流码。如果推流码是正确的，那么有可能是与服务器的连接出现问题。\"\nOutput.ConnectFail.HdrDisabled=\"当前输出已禁用HDR。\"\nOutput.ConnectFail.Error=\"试图连接到服务器时出现意外的错误。详细信息记录在日志文件中。\"\nOutput.ConnectFail.Disconnected=\"已与服务器断开连接。\"\nOutput.StreamEncodeError.Title=\"编码错误\"\nOutput.StreamEncodeError.Msg=\"直播时发生了编码器错误。\"\nOutput.StreamEncodeError.Msg.LastError=\"直播时发生编码器错误：<br><br>%1\"\nOutput.RecordFail.Title=\"未能开始录制\"\nOutput.RecordFail.Unsupported=\"输出格式不支持或者不支持超过一个音轨. 请检查你的设置, 然后重试\"\nOutput.RecordNoSpace.Title=\"磁盘空间不足\"\nOutput.RecordNoSpace.Msg=\"没有足够磁盘空间来继续录像.\"\nOutput.RecordError.Title=\"录像错误\"\nOutput.RecordError.Msg=\"录像时发生了预料外的错误。\"\nOutput.RecordError.EncodeErrorMsg=\"录制时发生了编码器错误。\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"录制时发生编码器错误：<br><br>%1\"\nOutput.BadPath.Title=\"文件路径错误\"\nOutput.BadPath.Text=\"无法打开已配置的录制路径。请在“设置 -> 输出-> 录制”下检查您的录制路径。\"\nOutput.NoBroadcast.Title=\"直播未设置\"\nOutput.NoBroadcast.Text=\"您需要先设置广播才能开始直播。\"\nOutput.BroadcastStartFailed=\"无法开始直播\"\nOutput.BroadcastStopFailed=\"无法停止直播\"\nLogUploadDialog.Title=\"OBS Studio 日志文件上传\"\nLogUploadDialog.Labels.PrivacyNotice=\"上传任何文件之前，请阅读<a href='https://obsproject.com/privacy-policy'>隐私政策</a>及其有关文件上传的部分。\"\nLogUploadDialog.Labels.Progress=\"日志上传中。请稍候…\"\nLogUploadDialog.Labels.Description.AppLog=\"您的日志文件已上传。您现在可以分享 URL 用于调试或支持。\"\nLogUploadDialog.Labels.Description.CrashLog=\"您的崩溃报告已上传。您现在可以分享 URL 用于调试。\"\nLogUploadDialog.Buttons.ConfirmUpload=\"上传\"\nLogUploadDialog.Buttons.CopyURL=\"复制日志 URL\"\nLogUploadDialog.Buttons.AnalyzeURL=\"分析日志文件\"\nLogUploadDialog.Buttons.RetryButton=\"重试\"\nLogUploadDialog.Errors.Template=\"尝试上传文件时发生错误：\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"未找到要上传的文件或文件为空。\"\nRemux.SourceFile=\"OBS 录像\"\nRemux.TargetFile=\"目标文件\"\nRemux.Remux=\"转换封装\"\nRemux.Stop=\"停止封装转换\"\nRemux.ClearFinished=\"清除已完成项目\"\nRemux.ClearAll=\"清除所有项目\"\nRemux.OBSRecording=\"OBS 录像\"\nRemux.FinishedTitle=\"转封装完成\"\nRemux.Finished=\"录像已经转封装\"\nRemux.FinishedError=\"录像封装已转换，但文件可能不完整\"\nRemux.SelectRecording=\"选择 OBS 录像文件…\"\nRemux.SelectTarget=\"选择目标文件…\"\nRemux.FileExistsTitle=\"目标文件已存在\"\nRemux.FileExists=\"目标文件已存在。你想替换它们吗？\"\nRemux.ExitUnfinishedTitle=\"转封装在进行中\"\nRemux.ExitUnfinished=\"转封装尚未完成，现在停止可能会使目标文件无法使用。\\n你确定要停止封装转换吗？\"\nRemux.HelpText=\"拖动文件到此窗口以转换封装格式，或选择空“OBS 录像”单元以浏览文件。\"\nRemux.NoFilesAddedTitle=\"没有添加转换封装的文件。\"\nRemux.NoFilesAdded=\"没有文件被添加到转换封装中。请拖放一个包含一个或多个视频文件的文件夹。\"\nMissingFiles=\"缺少文件\"\nMissingFiles.MissingFile=\"缺少文件\"\nMissingFiles.NewFile=\"新文件\"\nMissingFiles.HelpText=\"跟上次您使用 OBS 相比，有部分文件丢失。\"\nMissingFiles.Clear=\"<已清除>\"\nMissingFiles.NumFound=\"找到 %1 个，共 %2 个\"\nMissingFiles.Search=\"查找文件夹…\"\nMissingFiles.SelectFile=\"选择文件…\"\nMissingFiles.SelectDir=\"选择要搜索的文件夹\"\nMissingFiles.State=\"状态\"\nMissingFiles.Missing=\"缺失\"\nMissingFiles.Replaced=\"已替换\"\nMissingFiles.Cleared=\"已清除\"\nMissingFiles.Found=\"已找到\"\nMissingFiles.AutoSearch=\"找到其他匹配的文件\"\nMissingFiles.AutoSearchText=\"OBS 在文件夹中找到了其他匹配的缺失文件。您想要添加它们吗？\"\nMissingFiles.NoMissing.Title=\"缺失文件检查\"\nMissingFiles.NoMissing.Text=\"看起来没有缺少文件。\"\nMacPermissions.MenuAction=\"检查应用权限...\"\nMacPermissions.Title=\"检查应用权限\"\nMacPermissions.Description=\"OBS Studio 需要您的授权，才能提供某些功能。建议您开启这些权限，但不开启不会影响正常使用。您可以随时再开启这些权限。\"\nMacPermissions.Description.OpenDialog=\"您可以通过 OBS Studio 菜单重新打开此对话框。\"\nMacPermissions.AccessGranted=\"已允许访问\"\nMacPermissions.RequestAccess=\"请求访问\"\nMacPermissions.OpenPreferences=\"打开 %1 首选项\"\nMacPermissions.Item.ScreenRecording=\"屏幕录制\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS 需要此权限才能采集您的屏幕。\"\nMacPermissions.Item.Camera=\"摄像头\"\nMacPermissions.Item.Camera.Details=\"需要此权限才能从摄像头或采集卡中采集内容。\"\nMacPermissions.Item.Microphone=\"麦克风\"\nMacPermissions.Item.Microphone.Details=\"OBS 需要此权限才能使用您的麦克风或外部音频设备录制。\"\nMacPermissions.Item.InputMonitoring=\"输入监控\"\nMacPermissions.Item.InputMonitoring.Details=\"当 OBS 在后台运行时，热键需要该权限才能正常工作。\"\nMacPermissions.Item.Accessibility=\"无障碍环境\"\nMacPermissions.Item.Accessibility.Details=\"在旧版安装中，OBS 可能会列在“无障碍”而不是“输入监控”中。\"\nMacPermissions.Continue=\"继续\"\nSourceLeak.Title=\"源清理错误\"\nSourceLeak.Text=\"在更改场景集合时出现问题，部分源无法卸载。这个问题通常是由于插件没有正确释放资源导致的。请确保您所使用的所有插件都已更新到最新版本。\\n\\nOBS Studio将会退出以防任何潜在的数据损坏。\"\nBasic.DesktopDevice1=\"桌面音频\"\nBasic.DesktopDevice2=\"桌面音频 2\"\nBasic.AuxDevice1=\"麦克风/Aux\"\nBasic.AuxDevice2=\"麦克风/Aux 2\"\nBasic.AuxDevice3=\"麦克风/Aux 3\"\nBasic.AuxDevice4=\"麦克风/Aux 4\"\nBasic.Scene=\"场景\"\nBasic.DisplayCapture=\"屏幕采集\"\nBasic.Main.PreviewConextMenu.Enable=\"开启预览\"\nBasic.Main.Preview.Disable=\"关闭预览\"\nScaleFiltering=\"缩放算法\"\nScaleFiltering.Point=\"最近邻插值\"\nScaleFiltering.Bilinear=\"双线性插值\"\nScaleFiltering.Bicubic=\"双三次插值\"\nScaleFiltering.Lanczos=\"Lanczos插值\"\nScaleFiltering.Area=\"区域\"\nBlendingMethod=\"混合方式\"\nBlendingMethod.Default=\"默认\"\nBlendingMethod.SrgbOff=\"关闭 SRGB\"\nBlendingMode=\"混合模式\"\nBlendingMode.Normal=\"常规\"\nBlendingMode.Additive=\"添加\"\nBlendingMode.Subtract=\"减少\"\nBlendingMode.Screen=\"屏幕\"\nBlendingMode.Multiply=\"多层\"\nBlendingMode.Lighten=\"变亮\"\nBlendingMode.Darken=\"变暗\"\nDeinterlacing=\"反交错\"\nDeinterlacing.TopFieldFirst=\"顶场优先\"\nDeinterlacing.BottomFieldFirst=\"底场优先\"\nVolControl.SliderUnmuted=\"'%1'音量滑块:\"\nVolControl.SliderMuted=\"'%1'音量滑块: (当前静音)\"\nVolControl.Mute=\"静音 '%1'\"\nVolControl.Properties=\"设置 \\\"%1\\\"\"\nVolControl.UnassignedWarning.Title=\"未分配的音频源\"\nVolControl.UnassignedWarning.Text=\"\\\"%1\\\" 未分配给任何音轨，在推流或录音中无法听见。\\n\\n要将音频源分配给音轨，请通过右键菜单或混音台工具栏中的齿轮按钮打开“高级音频属性”。\"\nBasic.Main.AddSceneDlg.Title=\"添加场景\"\nBasic.Main.AddSceneDlg.Text=\"请输入场景名称\"\nBasic.Main.DefaultSceneName.Text=\"场景 %1\"\nBasic.Main.AddSceneCollection.Title=\"添加场景集合\"\nBasic.Main.AddSceneCollection.Text=\"请输入场景集合的名称\"\nBasic.Main.RenameSceneCollection.Title=\"重命名场景集合\"\nBasic.Main.RemigrateSceneCollection.Title=\"更新场景集合分辨率\"\nBasic.Main.RemigrateSceneCollection.Text=\"您是否要更新“%1”的场景集合分辨率以匹配当前配置文件的画布分辨率 %2x%3？\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"当输出处于活动状态时无法更新场景集合分辨率。\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"无法更新场景集合分辨率。原始分辨率未知。\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"无法重置：更改 OBS 分辨率失败。\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"无法重置：当前分辨率已经是场景集合的基本分辨率。\"\nAddProfile.Title=\"添加配置文件\"\nAddProfile.Text=\"请输入配置文件的名称\"\nAddProfile.WizardCheckbox=\"显示自动配置向导\"\nRenameProfile.Title=\"重命名配置文件\"\nBasic.Main.MixerRename.Title=\"重命名音频源\"\nBasic.Main.MixerRename.Text=\"请输入音频源名称\"\nBasic.Main.PreviewDisabled=\"预览当前已禁用\"\nBasic.SourceSelect=\"创建或选择源\"\nBasic.SourceSelect.CreateNew=\"新建\"\nBasic.SourceSelect.AddExisting=\"添加现有\"\nBasic.SourceSelect.AddVisible=\"使源可见\"\nBasic.Main.Sources.Visibility=\"可见性\"\nBasic.Main.Sources.VisibilityDescription=\"控制画布中 '%1' 的可见性\"\nBasic.Main.Sources.Lock=\"锁定\"\nBasic.Main.Sources.LockDescription=\"锁定画布中 '%1' 的位置和比例\"\nBasic.PropertiesWindow=\"设置 \\\"%1\\\"\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (自动选择: %2)\"\nBasic.PropertiesWindow.SelectColor=\"选择颜色\"\nBasic.PropertiesWindow.SelectFont=\"选择字体\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"选择字体\"\nBasic.PropertiesWindow.ConfirmTitle=\"设置已修改\"\nBasic.PropertiesWindow.Confirm=\"修改尚未保存。要保存吗？\"\nBasic.PropertiesWindow.NoProperties=\"没有设置选项\"\nBasic.PropertiesWindow.AddFiles=\"添加文件\"\nBasic.PropertiesWindow.AddDir=\"添加目录\"\nBasic.PropertiesWindow.AddURL=\"添加路径 /URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"将目录添加到 '%1'\"\nBasic.PropertiesWindow.AddEditableListFiles=\"将文件添加到 '%1'\"\nBasic.PropertiesWindow.AddEditableListEntry=\"将条目添加到 '%1'\"\nBasic.PropertiesWindow.EditEditableListEntry=\"编辑条目从 '%1'\"\nBasic.PropertiesView.FPS.Simple=\"常用帧率\"\nBasic.PropertiesView.FPS.Rational=\"有理数帧率\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"有效帧率范围:\"\nBasic.PropertiesView.UrlButton.Text=\"要使用默认浏览器打开此链接吗？\"\nBasic.PropertiesView.UrlButton.Text.Url=\"网址: %1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"打开网址\"\nBasic.InteractionWindow=\"与'%1'交互\"\nBasic.StatusBar.Reconnecting=\"断开, 重新连接在 %2 秒内 (尝试 %1)\"\nBasic.StatusBar.AttemptingReconnect=\"尝试重新连接... (尝试 %1)\"\nBasic.StatusBar.ReconnectSuccessful=\"重连成功\"\nBasic.StatusBar.Delay=\"延迟 (%1 秒)\"\nBasic.StatusBar.DelayStartingIn=\"延迟 (在 %1 秒后开始)\"\nBasic.StatusBar.DelayStoppingIn=\"延迟 (在 %1 秒后停止)\"\nBasic.StatusBar.DelayStartingStoppingIn=\"延迟 (在%1 秒后停止，在 %2 秒后开始)\"\nBasic.StatusBar.RecordingSavedTo=\"录像已保存到 \\\"%1\\\"\"\nBasic.StatusBar.ReplayBufferSavedTo=\"回放已保存到 \\\"%1\\\"\"\nBasic.StatusBar.ScreenshotSavedTo=\"截图已保存到 \\\"%1\\\"\"\nBasic.StatusBar.AutoRemuxedTo=\"录像自动封装到 '%1'\"\nBasic.Filters=\"滤镜\"\nBasic.Filters.AsyncFilters=\"音视频滤镜\"\nBasic.Filters.AudioFilters=\"滤音器\"\nBasic.Filters.EffectFilters=\"效果滤镜\"\nBasic.Filters.Title=\"'%1' 的滤镜\"\nBasic.Filters.AddFilter.Title=\"滤镜名称\"\nBasic.Filters.AddFilter.Text=\"请指定滤镜名称\"\nBasic.TransformWindow=\"场景项目变换\"\nBasic.TransformWindow.Position=\"位置\"\nBasic.TransformWindow.PositionX=\"X 坐标\"\nBasic.TransformWindow.PositionY=\"Y 坐标\"\nBasic.TransformWindow.Rotation=\"旋转\"\nBasic.TransformWindow.Size=\"大小\"\nBasic.TransformWindow.Width=\"宽度\"\nBasic.TransformWindow.Height=\"高度\"\nBasic.TransformWindow.Alignment=\"对齐方式\"\nBasic.TransformWindow.BoundsType=\"边框类型\"\nBasic.TransformWindow.BoundsAlignment=\"边框内对齐方式\"\nBasic.TransformWindow.Bounds=\"边框大小\"\nBasic.TransformWindow.BoundsWidth=\"边框宽度\"\nBasic.TransformWindow.BoundsHeight=\"边框高度\"\nBasic.TransformWindow.CropToBounds=\"裁剪到边框\"\nBasic.TransformWindow.Crop=\"裁剪\"\nBasic.TransformWindow.CropLeft=\"裁剪左侧\"\nBasic.TransformWindow.CropRight=\"裁剪右侧\"\nBasic.TransformWindow.CropTop=\"裁剪顶部\"\nBasic.TransformWindow.CropBottom=\"裁剪底部\"\nBasic.TransformWindow.Alignment.TopLeft=\"顶部左对齐\"\nBasic.TransformWindow.Alignment.TopCenter=\"顶部居中对齐\"\nBasic.TransformWindow.Alignment.TopRight=\"顶部右对齐\"\nBasic.TransformWindow.Alignment.CenterLeft=\"中部左对齐\"\nBasic.TransformWindow.Alignment.Center=\"居中对齐\"\nBasic.TransformWindow.Alignment.CenterRight=\"中部右对齐\"\nBasic.TransformWindow.Alignment.BottomLeft=\"下部左对齐\"\nBasic.TransformWindow.Alignment.BottomCenter=\"下部居中对齐\"\nBasic.TransformWindow.Alignment.BottomRight=\"下部右对齐\"\nBasic.TransformWindow.BoundsType.None=\"无边框\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"等比缩放到框内，不超过原始大小\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"等比缩放到框内\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"等比缩放到框外\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"等比缩放到边框宽度\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"等比缩放到边框高度\"\nBasic.TransformWindow.BoundsType.Stretch=\"拉伸到边框\"\nBasic.TransformWindow.Title=\"编辑 '%1 ' 的变换\"\nBasic.TransformWindow.NoSelectedSource=\"未选择源\"\nBasic.Main.AddSourceHelp.Title=\"无法添加源\"\nBasic.Main.AddSourceHelp.Text=\"你需要至少一个场景才能添加源。\"\nBasic.Main.Scenes=\"场景\"\nBasic.Main.Sources=\"源\"\nBasic.Main.Source=\"源\"\nBasic.Main.Controls=\"控制按钮\"\nBasic.Main.PreparingStream=\"准备中…\"\nBasic.Main.Connecting=\"连接中……\"\nBasic.Main.StartRecording=\"开始录制\"\nBasic.Main.StartReplayBuffer=\"启动回放缓存\"\nBasic.Main.SaveReplay=\"保存回放\"\nBasic.Main.StartStreaming=\"开始直播\"\nBasic.Main.StartBroadcast=\"开始直播\"\nBasic.Main.StartVirtualCam=\"启动虚拟摄像机\"\nBasic.Main.StopRecording=\"停止录制\"\nBasic.Main.PauseRecording=\"暂停录制\"\nBasic.Main.UnpauseRecording=\"恢复录制\"\nBasic.Main.SplitFile=\"分割录制文件\"\nBasic.Main.AddChapterMarker=\"添加章节标记 (仅限混合 MP4/MOV)\"\nBasic.Main.StoppingRecording=\"正在停止录制...\"\nBasic.Main.StopReplayBuffer=\"关闭回放缓存\"\nBasic.Main.StoppingReplayBuffer=\"正在关闭回放缓存...\"\nBasic.Main.SetupBroadcast=\"管理直播\"\nBasic.Main.StopStreaming=\"停止直播\"\nBasic.Main.StopBroadcast=\"结束直播\"\nBasic.Main.AutoStopEnabled=\"(自动停止)\"\nBasic.Main.StoppingStreaming=\"正在停止直播...\"\nBasic.Main.ForceStopStreaming=\"切断直播 (丢弃延迟)\"\nBasic.Main.ShowContextBar=\"显示源工具栏\"\nBasic.Main.HideContextBar=\"隐藏源工具栏\"\nBasic.Main.StopVirtualCam=\"停止虚拟摄像机\"\nBasic.Main.Group=\"分组 %1\"\nBasic.Main.GroupItems=\"对所选项目进行分组\"\nBasic.Main.Ungroup=\"取消分组\"\nBasic.Main.GridMode=\"网格模式\"\nBasic.Main.ListMode=\"列表模式\"\nBasic.Main.VirtualCamConfig=\"配置虚拟摄像头\"\nBasic.VCam.VirtualCamera=\"虚拟摄像头\"\nBasic.VCam.OutputType=\"输出类型\"\nBasic.VCam.OutputSelection=\"输出选择\"\nBasic.VCam.OutputType.Program=\"程序（默认）\"\nBasic.VCam.OutputSelection.NoSelection=\"尚未选择此输出类型\"\nBasic.VCam.RestartWarning=\"虚拟摄像头将重启以应用此变更\"\nBasic.MainMenu.File=\"文件(&F)\"\nBasic.MainMenu.File.Export=\"导出(&E)\"\nBasic.MainMenu.File.Import=\"导入(&I)\"\nBasic.MainMenu.File.ShowRecordings=\"显示录像(&R)\"\nBasic.MainMenu.File.Remux=\"录像转封装(&M)\"\nBasic.MainMenu.File.Settings=\"设置(&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"打开设置文件夹\"\nBasic.MainMenu.File.ShowProfileFolder=\"打开配置文件文件夹\"\nBasic.MainMenu.File.ShowMissingFiles=\"检查缺少的文件\"\nBasic.MainMenu.File.Exit=\"退出(&X)\"\nBasic.MainMenu.Edit=\"编辑(&E)\"\nBasic.MainMenu.Edit.Undo=\"撤消(&U)\"\nBasic.MainMenu.Edit.Redo=\"重做(&R)\"\nBasic.MainMenu.Edit.LockPreview=\"锁定预览(&L)\"\nBasic.MainMenu.Edit.Scale=\"预览缩放(&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"缩放至窗口\"\nBasic.MainMenu.Edit.Scale.Canvas=\"画布（%1x%2）\"\nBasic.MainMenu.Edit.Scale.Output=\"输出（%1x%2）\"\nBasic.MainMenu.Edit.Scale.Manual=\"缩放（%1x%2）\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"放大\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"缩小\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"重置缩放\"\nBasic.MainMenu.Edit.Transform=\"变换(&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"编辑变换(&E)...\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"复制变换\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"粘贴变换\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"重置变换(&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"顺时针旋转 90 度\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"逆时针旋转 90 度\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"旋转 180 度\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"水平翻转(&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"垂直翻转(&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"比例适配屏幕(&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"拉伸到全屏(&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"屏幕居中(&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"垂直居中\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"水平居中\"\nBasic.MainMenu.Edit.Order=\"排序(&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"上移(&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"下移(&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"移至顶层(&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"移至底层(&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"高级音频设置(&A)\"\nBasic.MainMenu.View=\"视图(&V)\"\nBasic.MainMenu.View.Toolbars=\"工具栏(&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"Dock 工具栏\"\nBasic.MainMenu.View.ContextBar=\"源工具栏\"\nBasic.MainMenu.View.SourceIcons=\"源图标(&I)\"\nBasic.MainMenu.View.StatusBar=\"状态栏(&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"全屏界面\"\nBasic.MainMenu.View.ResetUI=\"重置界面(&R)\"\nBasic.MainMenu.View.AlwaysOnTop=\"窗口置顶 (&A)\"\nBasic.MainMenu.View.SceneListMode=\"场景列表模式\"\nBasic.MainMenu.Docks=\"停靠窗口(&D)\"\nBasic.MainMenu.Docks.ResetDocks=\"重置停靠窗口(&R)\"\nBasic.MainMenu.Docks.LockDocks=\"锁定停靠窗口(&L)\"\nBasic.MainMenu.Docks.SideDocks=\"全高 Dock 栏(&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"自定义浏览器停靠窗口...(&C)\"\nBasic.MainMenu.Import=\"导入…\"\nBasic.MainMenu.Export=\"导出…\"\nBasic.MainMenu.SceneCollection=\"场景集合(&S)\"\nBasic.MainMenu.Profile=\"配置文件(&P)\"\nBasic.MainMenu.Profile.Import=\"导入配置文件\"\nBasic.MainMenu.Profile.Export=\"导出配置文件\"\nBasic.MainMenu.SceneCollection.Import=\"导入场景集合\"\nBasic.MainMenu.SceneCollection.Export=\"导出场景集合\"\nBasic.MainMenu.SceneCollection.Remigrate=\"重置基础分辨率\"\nBasic.MainMenu.SceneCollection.Migrate=\"设置基础分辨率\"\nBasic.MainMenu.Profile.Exists=\"配置文件已存在。\"\nBasic.MainMenu.Tools=\"工具(&T)\"\nBasic.MainMenu.Help=\"帮助(&H)\"\nBasic.MainMenu.Help.HelpPortal=\"帮助门户(&P)\"\nBasic.MainMenu.Help.Website=\"访问 OBS 主页(&W)\"\nBasic.MainMenu.Help.Discord=\"加入 &Discord 服务器\"\nBasic.MainMenu.Help.WhatsNew=\"更新内容\"\nBasic.MainMenu.Help.Logs=\"日志文件(&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"显示日志文件(&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"上传当前日志文件(&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"上传之前的日志文件(&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"查看当前日志(&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"发行说明\"\nBasic.MainMenu.Help.CheckForUpdates=\"检查更新(&C)\"\nBasic.MainMenu.Help.Repair=\"检查文件完整性\"\nBasic.MainMenu.Help.RestartSafeMode=\"以安全模式重启\"\nBasic.MainMenu.Help.RestartNormal=\"以正常模式重启\"\nBasic.MainMenu.Help.CrashLogs=\"错误报告(&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"查看错误报告(&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"上传之前的崩溃报告(&P)\"\nBasic.MainMenu.Help.About=\"关于(&A)\"\nBasic.Settings.ProgramRestart=\"要使这些设置生效，必须重新启动该程序。\"\nBasic.Settings.ConfirmTitle=\"确认更改\"\nBasic.Settings.Confirm=\"你有未保存的更改。保存更改吗？\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 正在控制某些串流设置\"\nBasic.Settings.General=\"常规\"\nBasic.Settings.General.Language=\"语言\"\nBasic.Settings.General.Updater=\"更新\"\nBasic.Settings.General.UpdateChannel=\"更新通道\"\nBasic.Settings.General.UpdateChannelDisabled=\"(关闭)\"\nBasic.Settings.General.UpdateChannelDefault=\"(默认)\"\nBasic.Settings.General.EnableAutoUpdates=\"启动时自动检查更新\"\nBasic.Settings.General.OpenStatsOnStartup=\"启动时打开统计对话框\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"在屏幕采集中隐藏OBS窗口\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"此设置将隐藏所有非投影的 OBS Studio 窗口，使其不被OBS 采集，避免影响\\n其他应用程序，如会议、屏幕共享、远程支持、屏幕截图和其他截屏软件。\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"开启此设置会隐藏所有非投影的 OBS Studio 窗口，使其不被OBS 采集，避免影响其他应用程序，如会议、屏幕共享、远程支持、屏幕截图和其他截屏软件。\"\nBasic.Settings.General.WarnBeforeStartingStream=\"开始直播时弹窗确认\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"停止直播时弹窗确认\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"停止录制时弹窗确认\"\nBasic.Settings.General.Projectors=\"投影窗口\"\nBasic.Settings.General.HideProjectorCursor=\"投影窗口中隐藏光标\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"使投影窗口置顶\"\nBasic.Settings.General.Snapping=\"源对齐吸附\"\nBasic.Settings.General.ScreenSnapping=\"吸附源到屏幕边缘\"\nBasic.Settings.General.CenterSnapping=\"吸附源到水平和垂直中心\"\nBasic.Settings.General.SourceSnapping=\"吸附源到其他的源\"\nBasic.Settings.General.SnapDistance=\"吸附敏感度\"\nBasic.Settings.General.SpacingHelpers=\"显示像素对齐指引\"\nBasic.Settings.General.RecordWhenStreaming=\"直播时自动录制\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"停止直播后继续录制\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"直播时自动启动回放缓存\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"停止直播后保持回放缓存开启\"\nBasic.Settings.General.SysTray=\"系统托盘\"\nBasic.Settings.General.SysTrayWhenStarted=\"开始时最小化到系统托盘\"\nBasic.Settings.General.SystemTrayHideMinimize=\"总是最小化到系统托盘，而不是任务栏\"\nBasic.Settings.General.SaveProjectors=\"退出时保存投影窗口设置\"\nBasic.Settings.General.CloseExistingProjectors=\"限制每个屏幕一个全屏投影\"\nBasic.Settings.General.Preview=\"预览\"\nBasic.Settings.General.OverflowHidden=\"隐藏溢出\"\nBasic.Settings.General.OverflowAlwaysVisible=\"溢出始终可见\"\nBasic.Settings.General.OverflowSelectionHidden=\"即使源不可见，也会显示溢出\"\nBasic.Settings.General.Importers=\"导入器\"\nBasic.Settings.General.AutomaticCollectionSearch=\"导入时在已知的场景集合位置搜索\"\nBasic.Settings.General.SwitchOnDoubleClick=\"双击时转场到场景\"\nBasic.Settings.General.StudioPortraitLayout=\"开启纵向布局\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"显示 预览/输出 标签\"\nBasic.Settings.General.Multiview=\"多视图\"\nBasic.Settings.General.Multiview.MouseSwitch=\"单击以切换场景\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"显示场景名称\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"显示安全区域（EBU R 95）\"\nBasic.Settings.General.MultiviewLayout=\"多视图布局\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"横向平铺，顶部（8 场景）\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"横向平铺，底部（8 场景）\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"纵向平铺，左侧（8 场景）\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"纵向平铺，右侧（8 场景）\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"横向平铺，顶部（18 场景）\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"横向平铺，顶部（24 场景）\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"只显示场景(4个场景)\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"只显示场景(9个场景)\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"只显示场景(16个场景)\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"只显示场景(25个场景)\"\nBasic.Settings.General.ChannelName.stable=\"稳定版\"\nBasic.Settings.General.ChannelDescription.stable=\"最新的稳定版本\"\nBasic.Settings.General.ChannelName.beta=\"测试版 / RC 版\"\nBasic.Settings.General.ChannelDescription.beta=\"可能不稳定的先行版本\"\nBasic.Settings.Appearance=\"外观\"\nBasic.Settings.Appearance.General=\"常规\"\nBasic.Settings.Appearance.General.Theme=\"主题\"\nBasic.Settings.Appearance.General.Variant=\"样式\"\nBasic.Settings.Appearance.General.NoVariant=\"没有可用的样式\"\nBasic.Settings.Appearance.FontScale=\"字体大小\"\nBasic.Settings.Appearance.Density=\"密度\"\nBasic.Settings.Appearance.Classic=\"经典\"\nBasic.Settings.Appearance.Compact=\"紧凑\"\nBasic.Settings.Appearance.Normal=\"正常\"\nBasic.Settings.Appearance.Comfortable=\"舒适\"\nBasic.Settings.Appearance.OptionsWarning=\"此样式不提供某些外观选项。\"\nBasic.Settings.Stream=\"直播\"\nBasic.Settings.Stream.Destination=\"终点\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"使用身份认证\"\nBasic.Settings.Stream.Custom.Username=\"用户名\"\nBasic.Settings.Stream.Custom.Password=\"密码\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST: 输入 srp_username 值\\nRTMP: 输入用户名\\nSRT: 请忽略\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST: 输入 srp_password 值\\nRTMP: 输入密码\\nSRT: 输入加密密钥\"\nBasic.Settings.Stream.BandwidthTestMode=\"开启带宽测试模式\"\nBasic.Settings.Stream.TTVAddon=\"Twitch 聊天扩展\"\nBasic.Settings.Stream.TTVAddon.None=\"无\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV 和 FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"缺少直播设置\"\nBasic.Settings.Stream.StreamSettingsWarning=\"打开设置\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"缺少推流地址和推流码。\\n\\n打开设置，在“直播”选项卡中即可输入推流地址和推流码。\"\nBasic.Settings.Stream.MissingUrl=\"缺少流的 URL。\\n\\n打开设置以在“流”选项卡中输入URL。\"\nBasic.Settings.Stream.MissingStreamKey=\"缺少推流码。\\n\\n打开设置，在“直播”选项卡中即可输入推流码。\"\nBasic.Settings.Stream.IgnoreRecommended=\"忽略流媒体服务的推荐设置\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"覆盖推荐设置\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"警告：忽略服务的限制可能会导致推流质量降低或阻止您进行推流。\\n\\n继续吗？\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"最大视频码率: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"最大音频码率: %1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"最大分辨率： %1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"最高帧率: %1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"指定自定义服务器…\"\nBasic.Settings.Stream.ServiceCustomServer=\"自定义服务器\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"启用 %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"最大串流带宽\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"自动\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"最大视频轨道数\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"自动\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"启用串流转储到 FLV (使用简单录制文件设置)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"配置覆盖 (JSON)\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"启用配置覆盖\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"多轨视频\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"额外画布\"\nBasic.Settings.Stream.WHIPSimulcastLabel=\"同播\"\nBasic.Settings.Stream.WHIPSimulcastInfo=\"同播功能允许您编码和发送多种视频质量。<a href='https://obsproject.com/kb/whip-streaming-guide'>了解更多</a>\"\nBasic.Settings.Stream.WHIPSimulcastTotalLayers=\"总层数\"\nBasic.Settings.Stream.AdvancedOptions=\"高级选项\"\nBasic.Settings.Output=\"输出\"\nBasic.Settings.Output.Format=\"录像格式\"\nBasic.Settings.Output.Format.MKV=\"Matroska 视频 (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"混合 MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"混合 MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"分片 MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"分片 MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"分片 MOV 以块的形式录制，不需要像传统 MOV 文件一样做最后处理。\\n这确保即使磁盘写入中断 (例如，由于蓝屏或断电)，文件仍可播放。\\n\\n可能不是所有播放器和编辑软件都能兼容。必要的话，请使用 “文件 → 录像转封装” 将文件转换为更兼容的格式。\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"分片 MP4 以块的形式录制，不需要像传统 MP4 文件一样做最后处理。\\n这确保即使磁盘写入中断 (例如，由于蓝屏或断电)，文件仍可播放。\\n\\n可能不是所有播放器和编辑软件都能兼容。必要的话，请使用 “文件 → 录像转封装” 将文件转换为更兼容的格式。\"\nBasic.Settings.Output.Encoder.Video=\"视频编码器\"\nBasic.Settings.Output.Encoder.Audio=\"音频编码器\"\nBasic.Settings.Output.SelectDirectory=\"选择录像目录\"\nBasic.Settings.Output.DynamicBitrate=\"动态调整码率以应对网络拥堵\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"动态调整码率以应对网络拥堵（Beta）\"\nBasic.Settings.Output.DynamicBitrate.TT=\"在网络拥堵时快速动态调整码率，而不是丢弃帧来减缓拥堵。\\n\\n注意：开启后，若有严重的突发网络拥堵，到观众的延迟可能会增加。\\n码率降低之后，可能需要几分钟时间才能恢复。\\n\\n当前只支持 RTMP 流。\"\nBasic.Settings.Output.Mode=\"输出模式\"\nBasic.Settings.Output.Mode.Simple=\"简单\"\nBasic.Settings.Output.Mode.Adv=\"高级\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg 输出\"\nBasic.Settings.Output.UseReplayBuffer=\"开启回放缓存\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"回放时长上限\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"最大内存\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"预计内存使用量：%1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"警告：预计使用 %1 MiB 内存，超过推荐的上限 %2 MiB\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"无法预估内存使用量，请设置内存使用量上限。\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"回放文件名前缀\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"后缀\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"回放缓存无法使用，因为录制类型被设置为“自定义输出 (FFmpeg)”。\"\nBasic.Settings.Output.Simple.SavePath=\"录像路径\"\nBasic.Settings.Output.Simple.RecordingQuality=\"录像质量\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"与串流画质相同\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"高质量, 中等文件大小\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"近似无损的质量, 大文件大小\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"无损的质量, 非常大的文件大小\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"警告：串流视频码率将设置为 %1，这是当前串流服务的上限。\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"警告：串流音频码率将设置为 %1，这是当前串流服务的上限。\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"警告：当录像质量设为“与串流画质相同”时，无法暂停录制。\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"警告：当前选择的录制格式与选择的流编码器不兼容。\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"警告：在使用软件编码器编码与流不同的质量时，同时进行推流和录制将会需要额外的CPU占用。\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"警告：无损质量会生成非常大的文件！在高分辨率和帧速率下每分钟可使用多达7GB的磁盘空间。除非您有大量可用磁盘空间，否则不建议长时间使用无损录制。使用无损质量时回放缓存不可用。\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"你确定你想要使用无损质量?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"无损质量警告!\"\nBasic.Settings.Output.Simple.Encoder.Software=\"软件 (x264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"硬件 (QSV, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"硬件 (QSV, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"硬件 (AMD, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"硬件 (AMD, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"硬件 (AMD, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"硬件 (NVENC, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"硬件 (NVENC, AV1)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"硬件 (NVENC, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"硬件 (Apple, H.264)\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"硬件 (Apple, HEVC)\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"软件 (x264, 降低 CPU 占用, 文件会更大)\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC（默认）\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch VOD 轨道 (使用轨道 2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"音轨\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"分辨率/帧率不兼容\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"此串流服务不支持您当前的输出分辨率和/或帧率。 它们将被更改为最接近的兼容值：\\n\\n%1\\n\\n你想要继续吗？\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"分辨率: %1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"帧率: %1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"编码器不兼容\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"串流服务“%1”不支持编码器“%2”。编码器将更改为“%3”。\\n\\n您想要继续吗？\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"流媒体服务“%1”不支持编码器“%2”和“%3”。这些编码器将更改为“%4”和“%5”。\\n\\n你想继续吗？\"\nBasic.Settings.Output.VideoBitrate=\"视频码率\"\nBasic.Settings.Output.AudioBitrate=\"音频码率\"\nBasic.Settings.Output.Reconnect=\"自动重连\"\nBasic.Settings.Output.RetryDelay=\"重连尝试间隔\"\nBasic.Settings.Output.MaxRetries=\"最大重试次数\"\nBasic.Settings.Output.Advanced=\"开启自定义编码器设置（高级）\"\nBasic.Settings.Output.EncoderPreset=\"编码器预设\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1（低CPU使用率，最低质量）\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1（中等 CPU 使用率，标准质量）（默认）\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1（高 CPU 使用率，高质量）\"\nBasic.Settings.Output.CustomEncoderSettings=\"自定义编码器设置\"\nBasic.Settings.Output.CustomMuxerSettings=\"自定义混流器设置\"\nBasic.Settings.Output.NoSpaceFileName=\"生成没有空格的文件名\"\nBasic.Settings.Output.Adv.Rescale=\"重新缩放输出\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"已禁用\"\nBasic.Settings.Output.Adv.AudioTrack=\"音轨\"\nBasic.Settings.Output.Adv.Streaming=\"直播\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"直播设置\"\nBasic.Settings.Output.Adv.Audio.Track1=\"音轨 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"音轨 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"音轨 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"音轨 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"音轨 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"音轨 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch VOD 音轨\"\nBasic.Settings.Output.Adv.Encoder=\"编码器设置\"\nBasic.Settings.Output.Adv.Recording=\"录制\"\nBasic.Settings.Output.Adv.Recording.Settings=\"录制设置\"\nBasic.Settings.Output.Adv.Recording.RecType=\"录制类型\"\nBasic.Settings.Output.Adv.Recording.Type=\"类型\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"标准\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"自定义输出 (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(使用直播编码器)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"文件名格式\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"允许覆盖已存在的文件\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"自定义输出 (FFmpeg) 没有提供任何安全措施。\\n请考虑使用“标准”录制类型。\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg 输出类型\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"输出到 URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"输出到文件\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"常见的录像格式\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"所有文件\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"文件路径或 URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"容器格式\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"音频\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"视频\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"默认格式\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"容器格式说明\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"通过文件路径或 URL 推断音频/视频格式\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"默认编码器\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"禁用编码器\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"视频编码器\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"视频编码器设置（如果有）\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"音频编码器\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"音频编码器设置（如果有）\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"混流器设置（如果有）\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"关键帧间隔（帧）\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"显示所有编码器（即使可能不兼容）\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg 设置\"\nBasic.Settings.Output.EnableSplitFile=\"自动分割文件\"\nBasic.Settings.Output.SplitFile.TypeTime=\"按时间分割\"\nBasic.Settings.Output.SplitFile.TypeSize=\"按大小分割\"\nBasic.Settings.Output.SplitFile.TypeManual=\"仅手动分割\"\nBasic.Settings.Output.SplitFile.Time=\"分割时间\"\nBasic.Settings.Output.SplitFile.Size=\"分割大小\"\nScreenshot=\"截屏输出\"\nScreenshot.SourceHotkey=\"当前所选源截屏\"\nScreenshot.StudioProgram=\"截屏 (输出)\"\nScreenshot.Preview=\"截屏 (预览)\"\nScreenshot.Scene=\"截屏 (场景)\"\nScreenshot.Source=\"截屏 (源)\"\nFilenameFormatting.TT.CCYY=\"年份，四位数\"\nFilenameFormatting.TT.YY=\"年份，后两位数 (00-99)\"\nFilenameFormatting.TT.MM=\"月份，十进制式 (01-12)\"\nFilenameFormatting.TT.DD=\"日份，用零填充 (01-31)\"\nFilenameFormatting.TT.hh=\"小时，24 小时制 (00-23)\"\nFilenameFormatting.TT.mm=\"分 (00-59)\"\nFilenameFormatting.TT.ss=\"秒 (00-59)\"\nFilenameFormatting.TT.Percent=\"% 符号\"\nFilenameFormatting.TT.a=\"缩写的星期名称\"\nFilenameFormatting.TT.A=\"完整的星期名称\"\nFilenameFormatting.TT.b=\"缩写的月份名称\"\nFilenameFormatting.TT.B=\"完整的月份名称\"\nFilenameFormatting.TT.d=\"日份，用零填充 (01-31)\"\nFilenameFormatting.TT.H=\"小时，24 小时制 (00-23)\"\nFilenameFormatting.TT.I=\"小时，12 小时制 (01-12)\"\nFilenameFormatting.TT.m=\"月份，十进制式 (01-12)\"\nFilenameFormatting.TT.M=\"分 (00-59)\"\nFilenameFormatting.TT.p=\"当前上午或下午\"\nFilenameFormatting.TT.s=\"自 UNIX 纪元以来的时间（秒）\"\nFilenameFormatting.TT.S=\"秒 (00-59)\"\nFilenameFormatting.TT.y=\"年份，后两位数 (00-99)\"\nFilenameFormatting.TT.Y=\"年份\"\nFilenameFormatting.TT.z=\"与 UTC（协调世界时）的时差 - ISO 8601\"\nFilenameFormatting.TT.Z=\"时区名称或缩写\"\nFilenameFormatting.TT.FPS=\"每秒帧数\"\nFilenameFormatting.TT.CRES=\"基础（画布）分辨率\"\nFilenameFormatting.TT.ORES=\"输出（缩放）分辨率\"\nFilenameFormatting.TT.VF=\"视频格式\"\nBasic.Settings.Video=\"视频\"\nBasic.Settings.Video.Adapter=\"视频适配器\"\nBasic.Settings.Video.BaseResolution=\"基础（画布）分辨率\"\nBasic.Settings.Video.ScaledResolution=\"输出 (缩放) 分辨率\"\nBasic.Settings.Video.DownscaleFilter=\"缩小算法\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[分辨率相符，不需要缩小]\"\nBasic.Settings.Video.FPS=\"帧率\"\nBasic.Settings.Video.FPSCommon=\"常用帧率\"\nBasic.Settings.Video.FPSInteger=\"整数帧率\"\nBasic.Settings.Video.FPSFraction=\"分数帧率\"\nBasic.Settings.Video.Numerator=\"分子：\"\nBasic.Settings.Video.Denominator=\"分母：\"\nBasic.Settings.Video.Renderer=\"渲染器\"\nBasic.Settings.Video.Renderer.Experimental=\"%1 (实验性)\"\nBasic.Settings.Video.InvalidResolution=\"无效的分辨率值。必须是 [宽]x[高]（如 1920x1080）\"\nBasic.Settings.Video.CurrentlyActive=\"视频输出处于活动状态。请关闭任何输出以更改视频设置。\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"双线性插值(最快, 但会变模糊)\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"双三次插值(锐化缩放, 16 个样本)\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos插值(锐化缩放, 36 个样本)\"\nBasic.Settings.Video.DownscaleFilter.Area=\"区域(加权和, 4/6/9个样本)\"\nBasic.Settings.Audio=\"音频\"\nBasic.Settings.Audio.SampleRate=\"采样率\"\nBasic.Settings.Audio.Channels=\"声道\"\nBasic.Settings.Audio.Meters=\"电平表\"\nBasic.Settings.Audio.MeterDecayRate=\"衰减速率\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"快速\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"中速(峰值电平表I型)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"慢速(峰值电平表II型)\"\nBasic.Settings.Audio.PeakMeterType=\"峰值计类型\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"采样峰值\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"真峰值 (更高的 CPU 使用率)\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"警告: 已开启环绕声音频。\"\nBasic.Settings.Audio.MultichannelWarning=\"如果要串流，请检查您的流媒体服务是否支持环绕立体声的接收和回放。例如，Facebook 360 Live 完整支持环绕立体声；YouTube Live 支持 5.1 环绕立体声的接收（并在电视上回放）。\\n\\nOBS 的音频滤镜兼容环绕立体声，但无法保证 VST 插件是否支持。\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"是否开启环绕立体声？\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"确实要开启环绕立体声吗？\"\nBasic.Settings.Audio.Devices=\"全局音频设备\"\nBasic.Settings.Audio.DesktopDevice=\"桌面音频\"\nBasic.Settings.Audio.DesktopDevice2=\"桌面音频 2\"\nBasic.Settings.Audio.AuxDevice=\"麦克风/辅助音频\"\nBasic.Settings.Audio.AuxDevice2=\"麦克风/辅助音频 2\"\nBasic.Settings.Audio.AuxDevice3=\"麦克风/辅助音频 3\"\nBasic.Settings.Audio.AuxDevice4=\"麦克风/辅助音频 4\"\nBasic.Settings.Audio.EnablePushToMute=\"开启按住静音\"\nBasic.Settings.Audio.PushToMuteDelay=\"按住静音延迟\"\nBasic.Settings.Audio.EnablePushToTalk=\"开启按住讲话\"\nBasic.Settings.Audio.PushToTalkDelay=\"按住讲话延时\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[设备未连接或不可用]\"\nBasic.Settings.Audio.Disabled=\"已禁用\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"低延迟音频缓冲模式（用于 Decklink/NDI 输出）\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"警告：低延迟音频缓冲已开启。\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"低延迟音频缓冲模式可能会导致音频卡顿，或使某些源无法播放。\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"是否开启低延迟音频缓冲模式？\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"您确定要开启低延迟音频缓冲模式吗？\"\nBasic.Settings.Accessibility=\"无障碍环境\"\nBasic.Settings.Accessibility.ColorOverrides=\"使用不同的颜色\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"源边框（选中）\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"源边框（裁剪）\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"源边框（悬停）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"混音台音量分段（-60 至 -20dB）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"混音台音量分段（-20 至 -9dB）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"混音台音量分段（-9 至 0dB）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"混音台音量分段（-60 至 -20dB，激活）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"混音台音量分段（-20 至 -9dB，激活）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"混音台音量分段（-9 至 0dB，激活）\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"预设颜色\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"默认\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"自定义\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"色盲替代方案\"\nBasic.Settings.Advanced=\"高级\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"如果有处于活动状态的输出，则在退出时显示警告\"\nBasic.Settings.Advanced.General.ProcessPriority=\"进程优先级\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"高\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"高于正常\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"正常\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"低于正常\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"低\"\nBasic.Settings.Advanced.FormatWarning=\"警告：NV12/P010 之外的颜色格式主要用于录像，并不推荐在串流时使用。因为串流时需要进行颜色格式转换，可能会增加 CPU 使用率。\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"警告：高精度格式一般用于配合 HDR 色彩空间。\"\nBasic.Settings.Advanced.FormatWarning2100=\"警告：Rec. 2100 要求精度更高的格式。\"\nBasic.Settings.Advanced.Video.ColorFormat=\"色彩格式\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12 (8 位, 4:2:0, 2 个平面)\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420 (8 位, 4:2:0, 3 个平面)\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444 (8 位, 4:4:4, 3 个平面)\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010 (10 位, 4:2:0, 2 个平面)\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010 (10 位, 4:2:0, 3 个平面)\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216 (16 位, 4:2:2, 2 个平面)\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416 (16 位, 4:4:4, 2 个平面)\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA (8 位)\"\nBasic.Settings.Advanced.Video.ColorSpace=\"色彩空间\"\nBasic.Settings.Advanced.Video.ColorRange=\"色彩范围\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"常规 (Limited)\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"扩展 (Full)\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR 白电平\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR 标称峰值电平\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"监听设备\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"默认\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"禁用 Windows 音频闪避（检测到通信活动时降低其他声音的音量）\"\nBasic.Settings.Advanced.StreamDelay=\"直播延迟\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"延迟时间\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"重新连接时保持截止点 (增加延迟)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"估计内存使用量：%1 MB\"\nBasic.Settings.Advanced.Network=\"网络\"\nBasic.Settings.Advanced.Network.Disabled=\"当前选择的流媒体协议不支持更改网络设置。\"\nBasic.Settings.Advanced.Network.BindToIP=\"绑定到 IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP 族\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"开启网络优化\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"开启 TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"尝试通过调节传输速率，使 RTMP 流的输出对其他的网络延迟敏感应用更友好。\\n在不稳定的网络连接下，可能会增加掉帧的风险。\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"快捷键与窗口焦点\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"任何时候都开启快捷键\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"当主窗口获得焦点时禁用快捷键\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"主窗口失去焦点时禁用快捷键\"\nBasic.Settings.Advanced.AutoRemux=\"自动封装至 %1 格式\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"(录制为 mkv 格式)\"\nBasic.AdvAudio=\"高级音频设置\"\nBasic.AdvAudio.ActiveOnly=\"仅活动源\"\nBasic.AdvAudio.Name=\"名称\"\nBasic.AdvAudio.Volume=\"音量\"\nBasic.AdvAudio.VolumeSource=\"'%1' 的音量\"\nBasic.AdvAudio.Mono=\"单声道​\"\nBasic.AdvAudio.MonoSource=\"'%1' 单声道缩混\"\nBasic.AdvAudio.Balance=\"平衡\"\nBasic.AdvAudio.BalanceSource=\"'%1' 平衡\"\nBasic.AdvAudio.SyncOffset=\"同步偏移\"\nBasic.AdvAudio.SyncOffsetSource=\"'%1' 同步偏移量\"\nBasic.AdvAudio.Monitoring=\"音频监听\"\nBasic.AdvAudio.Monitoring.None=\"关闭监听\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"仅监听(输出静音)\"\nBasic.AdvAudio.Monitoring.Both=\"监听并输出\"\nBasic.AdvAudio.MonitoringSource=\"'%1' 的音频监测\"\nBasic.AdvAudio.AudioTracks=\"音轨\"\nBasic.Settings.Hotkeys=\"快捷键\"\nBasic.Settings.Hotkeys.Pair=\"当按键组合与“%1”相同时，作为切换开关使用\"\nBasic.Settings.Hotkeys.Filter=\"筛选\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"按快捷键筛选\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"此快捷键已被一个或多个其他操作所使用，点击显示冲突\"\nBasic.Settings.Hotkeys.PleaseWait=\"正在加载快捷键，请稍候...\"\nBasic.Hotkeys.SelectScene=\"切换到场景\"\nBasic.SystemTray.Show=\"显示\"\nBasic.SystemTray.Hide=\"隐藏\"\nBasic.SystemTray.Message.Reconnecting=\"已断开连接。 正在重新连接…\"\nHotkeys.Left=\"左\"\nHotkeys.Right=\"右\"\nHotkeys.Up=\"上\"\nHotkeys.Down=\"下\"\nHotkeys.Space=\"空格\"\nHotkeys.NumpadNum=\"小键盘 %1\"\nHotkeys.NumpadMultiply=\"小键盘 乘\"\nHotkeys.NumpadDivide=\"小键盘 除\"\nHotkeys.NumpadAdd=\"小键盘 加\"\nHotkeys.NumpadSubtract=\"小键盘 减\"\nHotkeys.NumpadDecimal=\"小键盘 点\"\nHotkeys.AppleKeypadNum=\"%1 (键盘)\"\nHotkeys.AppleKeypadMultiply=\"* (键盘)\"\nHotkeys.AppleKeypadDivide=\"/ (键盘)\"\nHotkeys.AppleKeypadAdd=\"+ (键盘)\"\nHotkeys.AppleKeypadSubtract=\"- (键盘)\"\nHotkeys.AppleKeypadDecimal=\".(键盘)\"\nHotkeys.AppleKeypadEqual=\"= (键盘)\"\nHotkeys.MouseButton=\"鼠标 %1\"\nMute=\"静音\"\nUnmute=\"取消静音\"\nPush-to-mute=\"按住静音\"\nPush-to-talk=\"按住讲话\"\nSceneItemShow=\"显示“%1”\"\nSceneItemHide=\"隐藏“%1”\"\nOutputWarnings.NoTracksSelected=\"您必须选择至少一个轨道\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"输出设置错误\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"所有输出都必须至少选择一条音轨\"\nOutputWarnings.MP4Recording=\"警告：如果文件无法完成（例如，由于蓝屏BSOD，掉电等），保存到 MP4/MOV 的记录将无法恢复。如果要录制多个音轨，请考虑使用 MKV 录制，并在完成后将录像重新封装为 MP4/MOV（文件→录像转封装）\"\nOutputWarnings.CannotPause=\"警告：当录制编码器设为“（使用直播编码器）”时，无法暂停录制。\"\nOutputWarnings.CodecIncompatible=\"所选择的音频或视频编码器不兼容，设置项已还原。请从列表中选择兼容的编码器\"\nCodecCompat.Incompatible=\"(和%1不兼容)\"\nCodecCompat.CodecPlaceholder=\"选择编码器...\"\nCodecCompat.ContainerPlaceholder=\"选择格式...\"\nCodecCompat.CodecMissingOnExit.Title=\"未选择编码器\"\nCodecCompat.CodecMissingOnExit.Text=\"至少有一个视频或音频编码器未设置。请确认已经选择了录制和串流的编码器。\"\nCodecCompat.ContainerMissingOnExit.Title=\"未选择格式\"\nCodecCompat.ContainerMissingOnExit.Text=\"未选择录制格式。请选择与当前所选串流编码器兼容的录制格式。\"\nFinalScene.Title=\"删除场景\"\nFinalScene.Text=\"至少要有一个场景。\"\nNoSources.Title=\"无来源\"\nNoSources.Text=\"看起来您未添加任何视频源，所以我们将只会输出黑色画面。确实要这样做吗？\"\nNoSources.Text.AddSource=\"您可以通过单击主窗口中“来源”框下的“+”图标以添加来源。\"\nNoSources.Label=\"您还没有添加任何源。\\n点击下面的 + 按钮，\\n或者右击此处添加一个。\"\nChangeBG=\"设置颜色\"\nCustomColor=\"自定义颜色\"\nBrowserSource.EnableHardwareAcceleration=\"开启浏览器源硬件加速\"\nAbout=\"关于\"\nAbout.Info=\"OBS Studio 是一款自由及开源的视频录制与直播软件。\"\nAbout.Donate=\"捐款支持\"\nAbout.GetInvolved=\"参与贡献\"\nAbout.Authors=\"作者\"\nAbout.License=\"许可\"\nAbout.Error=\"发生错误！无法读取文件。\\n\\n转到：%1\"\nAbout.Contribute=\"支持 OBS 项目\"\nAddUrl.Title=\"通过 URL 添加源\"\nAddUrl.Text=\"您已将 URL 拖拽到OBS。这会自动将链接添加为一个源。是否继续？\"\nResizeOutputSizeOfSource=\"使用此源的尺寸作为输出分辨率\"\nResizeOutputSizeOfSource.Text=\"基准与输出分辨率将调整为当前源大小。\"\nResizeOutputSizeOfSource.Continue=\"您是否想要继续？\"\nPreviewTransition=\"预览转场动画\"\nImporter=\"场景集合导入器\"\nImporter.SelectCollection=\"选择一个场景集合\"\nImporter.Collection=\"场景集合\"\nImporter.HelpText=\"通过将文件添加到此窗口来从OBS或其他支持的程序导入集合。\"\nImporter.Path=\"集合路径\"\nImporter.Program=\"检测到的应用程序\"\nImporter.AutomaticCollectionPrompt=\"场景集合自动搜索\"\nImporter.AutomaticCollectionText=\"OBS可以自动从支持的第三方程序中找到可导入场景集合。是否需要OBS自动为您查找？\\n\\n稍后您可以在“设置 > 通用> 导入器”中更改此设置。\"\nImporter.SelectFile=\"浏览…\"\nRestart=\"重启\"\nNeedsRestart=\"OBS Studio需要重启。您想现在重新启动吗？\"\nLoadProfileNeedsRestart=\"配置文件包含需要重启OBS的设置:\\n%1\\n\\n您想要重启OBS使这些设置生效吗？\"\nContextBar.NoSelectedSource=\"未选择源\"\nContextBar.MediaControls.PlayMedia=\"播放媒体\"\nContextBar.MediaControls.PauseMedia=\"暂停媒体\"\nContextBar.MediaControls.StopMedia=\"停止媒体\"\nContextBar.MediaControls.RestartMedia=\"重新播放媒体\"\nContextBar.MediaControls.PlaylistNext=\"播放列表中的下一项\"\nContextBar.MediaControls.PlaylistPrevious=\"播放列表中的上一项\"\nContextBar.MediaControls.BlindSeek=\"媒体跳转小工具\"\nYouTube.Auth.Ok=\"身份认证成功。\\n您现在可以关闭此页面。\"\nYouTube.Auth.NoCode=\"身份认证过程尚未完成。\"\nYouTube.Auth.NoChannels=\"所选账户上没有可用的频道\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube用户身份认证\"\nYouTube.Auth.WaitingAuth.Text=\"请在您的外部浏览器中完成身份认证。<br>如果外部浏览器打不开，请前往此链接完成身份认证：<br>%1\"\nYouTube.AuthError.Text=\"获取频道信息失败： %1\"\nYouTube.Actions.WindowTitle=\"YouTube 直播设置 - 频道： %1\"\nYouTube.Actions.CreateNewEvent=\"建立新直播\"\nYouTube.Actions.ChooseEvent=\"选择现有直播\"\nYouTube.Actions.Title=\"标题*\"\nYouTube.Actions.MyBroadcast=\"我的直播\"\nYouTube.Actions.Description=\"描述\"\nYouTube.Actions.Privacy=\"隐私*\"\nYouTube.Actions.Privacy.Private=\"私享\"\nYouTube.Actions.Privacy.Public=\"公开\"\nYouTube.Actions.Privacy.Unlisted=\"不公开列出\"\nYouTube.Actions.Category=\"类别\"\nYouTube.Actions.Thumbnail=\"缩略图\"\nYouTube.Actions.Thumbnail.SelectFile=\"选择文件...\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"未选择文件\"\nYouTube.Actions.Thumbnail.ClearFile=\"清除\"\nYouTube.Actions.MadeForKids=\"此视频是面向儿童的内容吗？*\"\nYouTube.Actions.MadeForKids.Yes=\"是，内容是面向儿童的\"\nYouTube.Actions.MadeForKids.No=\"否，内容不是面向儿童的\"\nYouTube.Actions.AdditionalSettings=\"其他设置\"\nYouTube.Actions.Latency=\"延迟\"\nYouTube.Actions.Latency.Normal=\"正常\"\nYouTube.Actions.Latency.Low=\"低\"\nYouTube.Actions.Latency.UltraLow=\"极低\"\nYouTube.Actions.EnableAutoStart=\"开启自动开始\"\nYouTube.Actions.EnableAutoStop=\"开启自动停止\"\nYouTube.Actions.AutoStartStop.TT=\"表示该预约的直播是否自动开始\"\nYouTube.Actions.EnableDVR=\"开启 DVR\"\nYouTube.Actions.360Video=\"360° 视频\"\nYouTube.Actions.ScheduleForLater=\"计划稍后进行\"\nYouTube.Actions.RememberSettings=\"记住这些设置\"\nYouTube.Actions.Create_Ready=\"建立直播\"\nYouTube.Actions.Create_GoLive=\"建立直播并开始推流\"\nYouTube.Actions.Choose_Ready=\"选择直播\"\nYouTube.Actions.Choose_GoLive=\"选择直播并开始串流\"\nYouTube.Actions.Create_Schedule=\"计划直播\"\nYouTube.Actions.Create_Schedule_Ready=\"计划并选择直播\"\nYouTube.Actions.Dashboard=\"打开 YouTube Studio\"\nYouTube.Actions.Error.Title=\"创建实时直播时出错\"\nYouTube.Actions.Error.Text=\"访问 YouTube 时出现错误 '%1'.<br/>详细错误描述请在<a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>上查询\"\nYouTube.Actions.Error.General=\"YouTube 访问错误。请检查您的网络连接或 YouTube 服务器能否访问。\"\nYouTube.Actions.Error.NoBroadcastCreated=\"创建直播时发生错误 '%1'.<br/>详细错误描述请在<a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>上查询\"\nYouTube.Actions.Error.NoStreamCreated=\"流未创建。请重新链接您的账户。\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API 错误。请查看日志文件以获取更多信息。\"\nYouTube.Actions.Error.BroadcastNotFound=\"未找到所选择的广播。\"\nYouTube.Actions.Error.FileMissing=\"选定的文件不存在。\"\nYouTube.Actions.Error.FileOpeningFailed=\"打开选定文件失败。\"\nYouTube.Actions.Error.FileTooLarge=\"选定的文件太大 (限制: 2 MiB)。\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"直播转换失败: %1<br/><br/>如果这个错误仍然存在，请手动尝试 <a href='https://studio.youtube.com/video/%2/livestreaming'>在 YouTube Studio 中打开直播</a>。\"\nYouTube.Actions.Error.BroadcastTestStarting=\"直播正在转到测试阶段，这可能需要一些时间。请等待10-30秒再试一次。\"\nYouTube.Actions.EventsLoading=\"正在加载事件列表...\"\nYouTube.Actions.EventCreated.Title=\"事件已创建\"\nYouTube.Actions.EventCreated.Text=\"事件已成功创建。\"\nYouTube.Actions.Stream=\"直播\"\nYouTube.Actions.Stream.ScheduledFor=\"计划于 %1\"\nYouTube.Actions.Stream.Resume=\"恢复中断的串流\"\nYouTube.Actions.Stream.YTStudio=\"由 YouTube Studio 自动创建\"\nYouTube.Actions.Notify.CreatingBroadcast=\"创建实时直播，请稍候...\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"需要手动启动\"\nYouTube.Actions.AutoStartStreamingWarning=\"此事件已禁用自动启动，请单击“开始直播”来开始。\"\nYouTube.Actions.AutoStopStreamingWarning=\"您将无法重新连接。<br>您的串流将会停止并下线。\"\nYouTube.Errors.liveStreamingNotEnabled=\"所选 YouTube 频道上未开启直播。<br/><br/>请访问 <a href='https://www.youtube.com/features'>youtube.com/features</a> 以获取更多信息。\"\nYouTube.Errors.livePermissionBlocked=\"在选定的 YouTube 频道上直播不可用。<br/>请注意，在您的频道设置开启后可能需要等待长达24小时才能使用直播功能。<br/><br/>详情请查看 <a href='https://www.youtube.com/features'>youtube.com/features</a>。\"\nYouTube.Errors.errorExecutingTransition=\"由于后端错误，转场失败。请稍等几秒后重试。\"\nYouTube.Errors.errorStreamInactive=\"YouTube没有收到你的串流数据。请检查您的设置并重试。\"\nYouTube.Errors.invalidTransition=\"尝试的转场无效。这可能是由于之前的转场尚未结束。请稍等几秒后重试。\"\nYouTube.DocksRemoval.Title=\"清除旧版 YouTube 浏览器停靠栏\"\nYouTube.DocksRemoval.Text=\"这些浏览器停靠栏因弃用而将被移除：\\n\\n%1\\n请改用“停靠栏/YouTube 直播控制室”。\"\nConfigDownload.WarningMessageTitle=\"警告\"\nFailedToStartStream.MissingConfigURL=\"没有可用于当前服务的配置 URL\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"自定义 RTMP URL 未指定\"\nFailedToStartStream.InvalidCustomConfig=\"自定义配置无效\"\nFailedToStartStream.MissingCanvas=\"缺失已配置的额外画布\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"无法创建多轨视频服务\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"无法创建多轨视频 RTMP 输出\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC 不可用。\\n\\n找不到编码器类型“%1”\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"无法创建视频编码器“%1” (类型：“%2”)\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"创建编码器“%1” (类型：“%2”) 时无法获取 OBS 视频信息\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"无法创建音频编码器\"\nFailedToStartStream.NoRTMPURLInConfig=\"配置不包含串流目标 RTMP(S) URL\"\nFailedToStartStream.FallbackToDefault=\"使用 %1 启动串流失败；是否要使用单一编码设置重试？\"\nFailedToStartStream.ConfigRequestFailed=\"无法从 %1 获取配置<br><br>HTTP 错误：%2 \"\nFailedToStartStream.WarningUnknownStatus=\"接收到未知状态值“%1”\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\n是否要在没有 %1 的情况下继续串流？\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\n是否继续串流？\"\nFailedToStartStream.MissingEncoderConfigs=\"直播配置不包含编码器配置\"\nFailedToStartStream.InvalidEncoderConfig=\"直播配置包含无效的编码器配置\"\nFailedToStartStream.StatusMissingHTML=\"直播请求返回了未指定的错误\"\nFailedToStartStream.NoConfigSupplied=\"缺少配置\"\nMultitrackVideo.Info=\"%1 会自动优化您的设置来编码并发送多种视频质量。选择此选项将发送有关您的计算机和软件设置的 %2 信息。\"\nMultitrackVideo.IncompatibleSettings.Title=\"设置不兼容\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 当前与以下设置不兼容：\\n\\n%2\\n若要继续使用 %1 进行串流，请禁用不兼容的设置：\\n\\n%3\\n然后重新开始串流。\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"为本次串流禁用并开始串流\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"更新设置并开始串流\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 当前与设置为“%2”、%3 的 [音频 → 常规 → 声道] 不兼容\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[音频 → 常规 → 声道] 需要设置为“%1”\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 需要对 [音频 → 常规 → 声道] 进行多个不同的设置\"\nBasic.OpenPluginManager=\"插件管理器\"\nPluginManager=\"插件管理器\"\nPluginManager.Restart=\"必须重启 OBS Studio 才能应用插件更改\"\nPluginManager.MissingPlugin=\"[未找到插件]\"\nPluginManager.Section.Discover=\"浏览\"\nPluginManager.Section.Manage=\"已安装\"\nPluginManager.Section.Updates=\"更新\"\nPluginManager.Section.Manage.Title=\"管理已启用的插件\"\n"
  },
  {
    "path": "frontend/data/locale/zh-TW.ini",
    "content": "Language=\"繁體中文\"\nOK=\"確定\"\nApply=\"套用\"\nCancel=\"取消\"\nClose=\"關閉\"\nSave=\"儲存\"\nDiscard=\"捨棄\"\nDisable=\"停用\"\nYes=\"是\"\nNo=\"否\"\nAdd=\"新增\"\nRemove=\"移除\"\nRename=\"重新命名…\"\nInteract=\"互動\"\nFilters=\"濾鏡\"\nProperties=\"屬性\"\nMoveUp=\"向上移動\"\nMoveDown=\"向下移動\"\nSettings=\"設定\"\nDisplay=\"顯示器\"\nName=\"名稱\"\nExit=\"離開\"\nMixer=\"音效混音器\"\nBrowse=\"瀏覽\"\nMono=\"單聲道\"\nStereo=\"立體聲\"\nDroppedFrames=\"影格遺失： %1 (%2%)\"\nProjector.Open.Program=\"開啟程式投影\"\nProjector.Open.Preview=\"開啟預覽投影\"\nProjector.Open.Scene=\"開啟場景投影\"\nProjector.Open.Source=\"開啟來源投影\"\nProjector.Open.Multiview=\"開啟多重檢視\"\nProjector.Display=\"顯示器：%1\"\nProjector.Window=\"新視窗\"\nProjector.Title=\"投影\"\nProjector.Title.Scene=\"場景：%1\"\nProjector.Title.Source=\"來源：%1\"\nProjector.Title.Multiview=\"多檢視\"\nProjector.ResizeWindowToContent=\"縮放視窗至內容大小\"\nClear=\"清除\"\nRevert=\"復原\"\nShow=\"顯示\"\nHide=\"隱藏\"\nUnhideAll=\"全數取消隱藏\"\nUntitled=\"無標題\"\nNew=\"新增…\"\nDuplicate=\"複製…\"\nEnable=\"啟用\"\nDisableOSXVSync=\"關閉 macOS 垂直同步\"\nResetOSXVSyncOnExit=\"離開時重設 macOS 垂直同步\"\nHighResourceUsage=\"編碼過載！請考慮調低影像設定，或使用更快的編碼設定檔。\"\nTransition=\"轉場特效\"\nQuickTransitions=\"快速轉場\"\nFadeToBlack=\"淡出至黑色畫面\"\nLeft=\"左\"\nRight=\"右\"\nTop=\"上\"\nBottom=\"下\"\nReset=\"重設\"\nHours=\"時\"\nMinutes=\"分\"\nSeconds=\"秒\"\nDeprecated=\"已經廢棄\"\nReplayBuffer=\"重播緩衝區\"\nImport=\"匯入\"\nExport=\"匯出\"\nCopy=\"複製\"\nPaste=\"貼上\"\nPasteReference=\"貼上（參考）\"\nPasteDuplicate=\"貼上（複製）\"\nRemuxRecordings=\"重新封裝錄影\"\nNext=\"下一步\"\nBack=\"上一步\"\nDefaults=\"預設設定檔\"\nRestoreDefaults=\"預設設定\"\nHideMixer=\"在混合器中隱藏\"\nTransitionOverride=\"轉換覆蓋\"\nShowTransition=\"顯示轉場特效\"\nHideTransition=\"隱藏轉場特效\"\nNone=\"無\"\nStudioMode.Preview=\"預覽\"\nStudioMode.Program=\"展示\"\nStudioMode.PreviewSceneName=\"預覽：%1\"\nStudioMode.ProgramSceneName=\"節目：%1\"\nShowInMultiview=\"在多檢視中顯示\"\nVerticalLayout=\"垂直排版\"\nGroup=\"群組\"\nDoNotShowAgain=\"不再顯示\"\nDefault=\"（預設）\"\nCalculating=\"正在計算…\"\nFullscreen=\"全螢幕\"\nWindowed=\"視窗化\"\nRefreshBrowser=\"重新整理\"\nAspectRatio=\"長寬比 <b>%1:%2</b>\"\nLockVolume=\"鎖定音量\"\nLogViewer=\"記錄檢視器\"\nShowOnStartup=\"啟動時顯示\"\nOpenFile=\"開啟檔案\"\nAddScene=\"新增場景\"\nAddSource=\"加入來源\"\nRemoveScene=\"移除選取場景\"\nRemoveSource=\"移除選取來源\"\nMoveSceneUp=\"上移場景\"\nMoveSceneDown=\"下移場景\"\nMoveSourceUp=\"上移來源\"\nMoveSourceDown=\"下移來源\"\nSourceProperties=\"開啟來源屬性\"\nSourceFilters=\"開啟來源濾鏡\"\nMixerToolbarMenu=\"音效混音器選單\"\nSceneFilters=\"開啟場景濾鏡\"\nList=\"清單\"\nGrid=\"網格\"\nAutomatic=\"自動\"\nComingSoon=\"即將推出\"\nPluginsFailedToLoad.Title=\"載入外掛程式時發生錯誤\"\nPluginsFailedToLoad.Text=\"無法載入下列 OBS 外掛程式：\\n\\n%1\\n請更新或移除這些外掛程式。\"\nAlreadyRunning.Title=\"OBS 已經執行\"\nAlreadyRunning.Text=\"OBS 已經啟動！除非您就想這麼做，否則請在執行新實體前，先關閉現存的 OBS 實體。如果你已將 OBS 設定成最小化至系統工具列，那請檢查 OBS 是否還在工具列執行著。\"\nAlreadyRunning.LaunchAnyway=\"仍然啟動\"\nCrashHandling.Dialog.Title=\"偵測到 OBS Studio 當機\"\nCrashHandling.Labels.Text=\"OBS Studio 未正常關閉。\\n\\n要在安全模式下執行嗎？（已停用第三方外掛程式、指令碼和 WebSockets）\"\nCrashHandling.Labels.PrivacyNotice=\"您也可以選擇自動上傳最新的當機報告至 OBSProject。<br /><br />在任何檔案上傳之前，請務必閱讀《<a href='https://obsproject.com/privacy-policy'>隱私權政策</a>》，並特別留意有關檔案上傳的部分。\"\nCrashHandling.Checkbox.SendReport=\"我已閱讀《隱私權政策》並同意上傳。\"\nCrashHandling.Buttons.LaunchSafe=\"以安全模式執行\"\nCrashHandling.Buttons.LaunchNormal=\"以一般模式執行\"\nCrashHandling.Errors.UploadJSONError=\"嘗試上傳最新的當機記錄時發生錯誤。請稍後再試。\"\nCrashHandling.Errors.Title=\"當機記錄上傳錯誤\"\nSafeMode.Restart=\"您是否想將 OBS 重新啟動至安全模式（也就是停用第三方外掛程式、指令稿和 WebSocket）？\"\nSafeMode.RestartNormal=\"你要以一般模式重新啟動 OBS 嗎？\"\nChromeOS.Title=\"不支援的平台\"\nChromeOS.Text=\"OBS 似乎是在 ChromeOS 容器內執行。此平台不受支援。\"\nWine.Title=\"偵測到 Wine\"\nWine.Text=\"在 Wine 中執行 OBS 不受支援，且許多如擷取或裝置來源的功能，會無法運作或運作受限。<br><br>建議改執行 OBS 的原生版本，例如 <a href='https://flathub.org/apps/details/com.obsproject.Studio'>我們的 Flatpak 版本</a> 或適合您作業系統的套件。\"\nDockCloseWarning.Title=\"關閉停駐視窗\"\nDockCloseWarning.Text=\"您剛關閉了一個停駐視窗。若想再次顯示，請重新在選單欄的「停駐視窗」選單開啟此視窗。\"\nExtraBrowsers=\"自訂瀏覽器停駐視窗\"\nExtraBrowsers.Info=\"可透過提供名稱及網址來新增停駐視窗，再按下「套用」或「關閉」開啟停駐視窗。您可隨時新增或移除停駐視窗。\"\nExtraBrowsers.DockName=\"停駐視窗名稱\"\nAuth.Authing.Title=\"正在認證…\"\nAuth.Authing.Text=\"正在透過 %1 認證，請稍候…\"\nAuth.AuthFailure.Title=\"認證失敗\"\nAuth.AuthFailure.Text=\"透過 %1 認證失敗：\\n\\n%2：%3\"\nAuth.InvalidScope.Title=\"需要認證\"\nAuth.InvalidScope.Text=\"已變更 %1 的認證要求。可能無法使用部份功能。\"\nAuth.LoadingChannel.Title=\"正在載入頻道資訊…\"\nAuth.LoadingChannel.Text=\"正在載入 %1 的頻道資訊，請稍候…\"\nAuth.LoadingChannel.Error=\"無法取得頻道資訊。\"\nAuth.ChannelFailure.Title=\"讀取頻道資訊失敗\"\nAuth.ChannelFailure.Text=\"無法讀取 %1 頻道資訊\\n\\n%2: %3\"\nAuth.Chat=\"聊天室\"\nAuth.StreamInfo=\"實況資訊\"\nTwitchAuth.Stats=\"Twitch 狀態\"\nTwitchAuth.Feed=\"Twitch 活動摘要\"\nTwitchAuth.TwoFactorFail.Title=\"無法查詢串流金鑰\"\nTwitchAuth.TwoFactorFail.Text=\"OBS 無法連結您的 Twitch 帳號。請確保您已在 <a href='https://www.twitch.tv/settings/security'>Twitch 安全性設定</a> 中設定串流所需的兩步驟驗證。\"\nRestreamAuth.Channels=\"Restream 頻道\"\nCopy.Filters=\"複製過濾器\"\nPaste.Filters=\"貼上過濾器\"\nBrowserPanelInit.Title=\"正在初始化瀏覽器…\"\nBrowserPanelInit.Text=\"正在初始化瀏覽器，請稍候…\"\nBandwidthTest.Region=\"區域\"\nBandwidthTest.Region.US=\"美國\"\nBandwidthTest.Region.EU=\"歐洲\"\nBandwidthTest.Region.Asia=\"亞洲\"\nBandwidthTest.Region.Other=\"其他\"\nBasic.AutoConfig=\"自動設定精靈\"\nBasic.AutoConfig.ApplySettings=\"套用設定\"\nBasic.AutoConfig.StartPage=\"用量資訊\"\nBasic.AutoConfig.StartPage.SubTitle=\"指定想運用的場合\"\nBasic.AutoConfig.StartPage.PrioritizeStreaming=\"為串流最佳化，錄影為其次\"\nBasic.AutoConfig.StartPage.PrioritizeRecording=\"僅為錄影最佳化，不會用於串流\"\nBasic.AutoConfig.StartPage.PrioritizeVirtualCam=\"我只會使用虛擬相機\"\nBasic.AutoConfig.VideoPage=\"影像設定\"\nBasic.AutoConfig.VideoPage.SubTitle=\"指定想用的影片設定\"\nBasic.AutoConfig.VideoPage.BaseResolution.UseCurrent=\"使用現在的 (%1x%2)\"\nBasic.AutoConfig.VideoPage.BaseResolution.Display=\"顯示器 %1 (%2x%3)\"\nBasic.AutoConfig.VideoPage.FPS.UseCurrent=\"使用現在的 (%1)\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighFPS=\"60 或 30，但可能時優先選擇 60\"\nBasic.AutoConfig.VideoPage.FPS.PreferHighRes=\"60 或 30，但優先高解析度\"\nBasic.AutoConfig.VideoPage.CanvasExplanation=\"註：畫布（來源）解析度並不一定與串流或錄影相同。實際的串流 / 錄影解析度，可以從畫布解析度縮小，以減少資源使用或位元率需求。\"\nBasic.AutoConfig.StreamPage=\"串流資訊\"\nBasic.AutoConfig.StreamPage.SubTitle=\"請輸入您的串流資訊\"\nBasic.AutoConfig.StreamPage.ConnectAccount=\"連結帳號（建議）\"\nBasic.AutoConfig.StreamPage.DisconnectAccount=\"解除連結帳號\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title=\"是否要解除連結帳號？\"\nBasic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text=\"此變更將立即套用，確定斷開帳號連結？\"\nBasic.AutoConfig.StreamPage.GetStreamKey=\"取得串流金鑰\"\nBasic.AutoConfig.StreamPage.MoreInfo=\"更多資訊\"\nBasic.AutoConfig.StreamPage.UseStreamKey=\"使用串流金鑰\"\nBasic.AutoConfig.StreamPage.UseStreamKeyAdvanced=\"使用串流金鑰（進階設定）\"\nBasic.AutoConfig.StreamPage.Service=\"服務\"\nBasic.AutoConfig.StreamPage.Service.ShowAll=\"全部顯示...\"\nBasic.AutoConfig.StreamPage.Service.Custom=\"自訂…\"\nBasic.AutoConfig.StreamPage.Server=\"伺服器\"\nBasic.AutoConfig.StreamPage.StreamKey=\"串流金鑰\"\nBasic.AutoConfig.StreamPage.StreamKey.ToolTip=\"RIST：輸入加密密語。\\nRTMP：輸入服務提供的金鑰。\\nSRT：如果服務有設定 streamid 則輸入之。\"\nBasic.AutoConfig.StreamPage.EncoderKey=\"編碼器金鑰\"\nBasic.AutoConfig.StreamPage.BearerToken=\"Bearer 憑證\"\nBasic.AutoConfig.StreamPage.ConnectedAccount=\"連結的帳號\"\nBasic.AutoConfig.StreamPage.PerformBandwidthTest=\"藉由頻寬測試估計位元速率（可能需要幾分鐘）\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding=\"優先使用硬體編碼\"\nBasic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip=\"硬體編碼去除了大多數的 CPU 使用率，但可能需要更多的位元率以獲得同等的品質。\"\nBasic.AutoConfig.StreamPage.StreamWarning.Title=\"串流警告\"\nBasic.AutoConfig.StreamPage.StreamWarning.Text=\"頻寬測試即將開始串流隨機的視訊資料，不含音訊。如果可以，建議您暫時關閉儲存直播影片的功能，並將直播設為私人，直到測試完成為止。要繼續嗎？\"\nBasic.AutoConfig.StreamPage.UseMultitrackVideo=\"測試 %1\"\nBasic.AutoConfig.TestPage=\"最終結果\"\nBasic.AutoConfig.TestPage.SubTitle.Testing=\"程式目前正在執行一系列的測試以估計最理想的設定\"\nBasic.AutoConfig.TestPage.SubTitle.Complete=\"測試完成\"\nBasic.AutoConfig.TestPage.TestingBandwidth=\"執行頻寬測試，這可能需要幾分鐘...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.NoOutput=\"未找到本服務協定的輸出\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Connecting=\"連線到 %1...\"\nBasic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed=\"無法連線到任何伺服器，請檢查網路連線並重試。\"\nBasic.AutoConfig.TestPage.TestingBandwidth.Server=\"測試頻寬︰%1\"\nBasic.AutoConfig.TestPage.TestingStreamEncoder=\"測試串流編碼器，這可能需要一點時間…\"\nBasic.AutoConfig.TestPage.TestingRecordingEncoder=\"測試錄影編碼器，這可能需要一點時間…\"\nBasic.AutoConfig.TestPage.TestingRes.Fail=\"無法啟動編碼器\"\nBasic.AutoConfig.TestPage.TestingRes.Resolution=\"測試 %1x%2 %3 FPS…\"\nBasic.AutoConfig.TestPage.Result.StreamingEncoder=\"串流編碼器\"\nBasic.AutoConfig.TestPage.Result.RecordingEncoder=\"錄影編碼器\"\nBasic.AutoConfig.TestPage.Result.Header=\"程式判斷這些估計設定是最適合你︰\"\nBasic.AutoConfig.TestPage.Result.Footer=\"若要使用這些設定，請點選「套用設定」。要重新設定精靈並重試，請點選「返回」。 要手動設定，請點選「取消」，然後打開「設定」。\"\nBasic.AutoConfig.Info=\"[自動設定精靈] 會根據您電腦的配置及網路速度來判斷最適合的設定。\"\nBasic.AutoConfig.RunAnytime=\"這隨時都可以在 [工具] 選單啟動。\"\nBasic.AutoConfig.TestPage.Result.StreamingResolution=\"串流（縮放）解析度\"\nBasic.Stats=\"狀態\"\nBasic.Stats.CPUUsage=\"CPU 使用率\"\nBasic.Stats.HDDSpaceAvailable=\"可用磁碟空間\"\nBasic.Stats.MemoryUsage=\"記憶體使用量\"\nBasic.Stats.AverageTimeToRender=\"平均影格繪製時間\"\nBasic.Stats.SkippedFrames=\"因編碼卡頓所略過的影格\"\nBasic.Stats.MissedFrames=\"因繪製卡頓所錯失的影格\"\nBasic.Stats.Output.Stream=\"串流\"\nBasic.Stats.Output.Recording=\"錄影\"\nBasic.Stats.Status=\"狀態\"\nBasic.Stats.Status.Recording=\"正在錄製\"\nBasic.Stats.Status.Live=\"直播中\"\nBasic.Stats.Status.Reconnecting=\"重新連線中\"\nBasic.Stats.Status.Inactive=\"未啟動\"\nBasic.Stats.Status.Active=\"已啟動\"\nBasic.Stats.DroppedFrames=\"丟棄的影格（網路）\"\nBasic.Stats.MegabytesSent=\"總輸出資料\"\nBasic.Stats.Bitrate=\"位元率\"\nBasic.Stats.DiskFullIn=\"磁碟剩餘錄製時間（估計）\"\nBasic.Stats.DiskFullIn.Text=\"%1 小時 %2 分鐘\"\nBasic.Stats.ResetStats=\"重設統計資料\"\nResetUIWarning.Title=\"您是否要重設介面？\"\nResetUIWarning.Text=\"重設介面會隱藏額外的停駐視窗。若想重新顯示這些停駐視窗，則需要進入「停駐視窗」選單解除隱藏。\\n\\n是否重設介面？\"\nUpdater.Title=\"有可用的更新\"\nUpdater.Text=\"有新的更新可用：\"\nUpdater.UpdateNow=\"立即更新\"\nUpdater.RemindMeLater=\"稍後提醒我\"\nUpdater.Skip=\"略過此版本\"\nUpdater.NoUpdatesAvailable.Title=\"沒有更新\"\nUpdater.NoUpdatesAvailable.Text=\"目前沒有更新\"\nUpdater.BranchNotFound.Title=\"更新頻道已被裁撤\"\nUpdater.BranchNotFound.Text=\"您選取的更新頻道已經無法取用。OBS 已重設回預設值。\"\nUpdater.RepairButUpdatesAvailable.Title=\"無法進行完整性檢查\"\nUpdater.RepairButUpdatesAvailable.Text=\"檔案完整性檢查只能在最新版本進行。請使用「說明 → 檢查更新」，驗證並更新您的 OBS 安裝。\"\nUpdater.RepairConfirm.Title=\"確認完整性檢查\"\nUpdater.RepairConfirm.Text=\"執行完整性檢查，會掃描您 OBS 安裝的損壞部分，並重新下載損壞或被篡改的檔案。這可能會花上一段時間。\\n\\n是否繼續？\"\nUpdater.FailedToLaunch=\"無法啟動更新程式\"\nQuickTransitions.SwapScenes=\"轉場後交換預覽/節目場景\"\nQuickTransitions.SwapScenesTT=\"轉場後交換預覽和節目場景（如果節目的原始場景仍然存在）。\\n這並不會復原任何對節目原始場景所作的改動。\"\nQuickTransitions.DuplicateScene=\"複製場景\"\nQuickTransitions.DuplicateSceneTT=\"在修改同樣的場景時，讓修改影像來源的可見度/轉換時，不需要修改節目。\\n啟用「複製場景」以修改影像來源的屬性，而不變動節目的輸出。\\n變更這個值將會重置目前的節目場景（如果仍然存在）。\"\nQuickTransitions.EditProperties=\"複製來源\"\nQuickTransitions.EditPropertiesTT=\"在修改同樣的場景時，讓修改影像來源的可見度/轉換時，不需要修改節目。\\n這只能在「複製場景」啟用時使用。\\n特定來源（如擷取或媒體來源）不支援本功能，無法獨立編輯。\\n變更此值會重設目前的節目場景（如果仍然存在）。\\n\\n警告：由於會複製來源，這可能需要額外的系統或視訊資源。\"\nQuickTransitions.HotkeyName=\"快速轉場：%1\"\nBasic.AddTransition=\"新增可選的轉場特效\"\nBasic.RemoveTransition=\"移除可選的轉場特效\"\nBasic.TransitionProperties=\"轉場特效屬性\"\nBasic.SceneTransitions=\"轉場特效\"\nBasic.TransitionDuration=\"時長\"\nBasic.TogglePreviewProgramMode=\"工作室模式\"\nBasic.EnablePreviewProgramMode=\"啟用工作室模式\"\nBasic.DisablePreviewProgramMode=\"停用工作室模式\"\nUndo.Undo=\"復原\"\nUndo.Redo=\"取消復原\"\nUndo.Add=\"新增「%1」\"\nUndo.Delete=\"刪除「%1」\"\nUndo.Rename=\"重新命名「%1」\"\nUndo.SceneCollection.Switch=\"切到「%1」\"\nUndo.Item.Undo=\"還原%1\"\nUndo.Item.Redo=\"重做%1\"\nUndo.Sources.Multi=\"刪除 %1 個來源\"\nUndo.Filters=\"在「%1」變更濾鏡的動作\"\nUndo.Filters.Paste.Single=\"在「%2」貼上「%1」濾鏡\"\nUndo.Filters.Paste.Multiple=\"複製「%1」濾鏡到「%2」\"\nUndo.Transform=\"在「%1」進行來源變換的動作\"\nUndo.Transform.Paste=\"在「%1」貼上變換設定的動作\"\nUndo.Transform.Rotate=\"在「%1」旋轉的動作\"\nUndo.Transform.Reset=\"在「%1」重設變換設定的動作\"\nUndo.Transform.HFlip=\"在「%1」水平翻轉的動作\"\nUndo.Transform.VFlip=\"在「%1」垂直翻轉的動作\"\nUndo.Transform.FitToScreen=\"在「%1」等比例縮放到螢幕大小的動作\"\nUndo.Transform.StretchToScreen=\"在「%1」拉伸螢幕的動作\"\nUndo.Transform.Center=\"在「%1」置中畫面的動作\"\nUndo.Transform.VCenter=\"在「%1」垂直置中畫面的動作\"\nUndo.Transform.HCenter=\"在「%1」水平置中畫面的動作\"\nUndo.Volume.Change=\"在「%1」變更音量的動作\"\nUndo.Volume.Mute=\"靜音「%1」\"\nUndo.Volume.Unmute=\"取消靜音「%1」\"\nUndo.Balance.Change=\"「%1」上的音訊平衡變更\"\nUndo.SyncOffset.Change=\"「%1」上的音訊同步偏移變更\"\nUndo.MonitoringType.Change=\"變更「%1」上的音訊監控\"\nUndo.Mixers.Change=\"變更「%1」上的音訊混音器\"\nUndo.ForceMono.On=\"啟用「%1」上的強制單聲道\"\nUndo.ForceMono.Off=\"停用「%1」上的強制單聲道\"\nUndo.Properties=\"在「%1」變更屬性的動作\"\nUndo.Scene.Duplicate=\"在「%1」複製場景的動作\"\nUndo.ShowTransition=\"顯示「%1」上的轉場\"\nUndo.HideTransition=\"隱藏「%1」上的轉場\"\nUndo.ShowSceneItem=\"在「%2」顯示「%1」\"\nUndo.HideSceneItem=\"在「%2」隱藏「%1」\"\nUndo.ReorderSources=\"在 「%1」中重新排序來源\"\nUndo.MoveUp=\"在「%2」上移「%1」\"\nUndo.MoveDown=\"在「%2」下移「%1」\"\nUndo.MoveToTop=\"在「%2」移動「%1」至最上層\"\nUndo.MoveToBottom=\"在「%2」移動「%1」至最下層\"\nUndo.PasteSource=\"貼上「%1」中的來源\"\nUndo.PasteSourceRef=\"貼上「%1」中的來源參考\"\nUndo.GroupItems=\"將項目組成「%1」群組\"\nTransitionNameDlg.Text=\"請輸入轉場特效名稱\"\nTransitionNameDlg.Title=\"轉場特效名稱\"\nTitleBar.SafeMode=\"安全模式\"\nTitleBar.PortableMode=\"免安裝模式\"\nTitleBar.Profile=\"設定檔\"\nTitleBar.Scenes=\"場景\"\nNameExists.Title=\"名稱已經存在\"\nNameExists.Text=\"這個名稱已被使用。\"\nNoNameEntered.Title=\"請輸入有效的名稱\"\nNoNameEntered.Text=\"您不能使用空白的名稱。\"\nConfirmStart.Title=\"啟動串流？\"\nConfirmStart.Text=\"你確定你想要啟動串流？\"\nConfirmStop.Title=\"是否停止串流？\"\nConfirmStop.Text=\"你是否要停止串流？\"\nConfirmStopRecord.Title=\"停止錄影？\"\nConfirmStopRecord.Text=\"您是否要停止錄製？\"\nConfirmBWTest.Title=\"開始頻寬測試？\"\nConfirmBWTest.Text=\"你已在頻寬測試模式下設定了 OBS。此模式允許在沒有開始串流的情況下進行網路測試。完成測試後，你要停用它才可以進行串流。\\n\\n要繼續嗎？\"\nConfirmRemove.Title=\"確認刪除\"\nConfirmRemove.Text=\"您確定要移除「%1」嗎？\"\nConfirmRemove.TextMultiple=\"您確定要移除這 %1 個項目嘛？\"\nConfirmReset.Title=\"重設屬性\"\nConfirmReset.Text=\"您確定要將目前的屬性重設回預設值嗎？\"\nOutput.StartStreamFailed=\"無法開始串流\"\nOutput.StartRecordingFailed=\"無法開始錄影\"\nOutput.StartReplayFailed=\"無法啟動重播緩衝區\"\nOutput.StartVirtualCamFailed=\"啟動虛擬相機失敗\"\nOutput.StartFailedGeneric=\"啟動輸出失敗。 詳情請看記錄檔。\\n\\n注意︰ 如果你使用 NVENC 或 AMD 編碼器，請確保您的視訊驅動程式是最新。\"\nOutput.ReplayBuffer.PauseWarning.Title=\"暫停時，無法儲存檔案\"\nOutput.ReplayBuffer.PauseWarning.Text=\"警告：錄影暫停時，將無法儲存重播。\"\nOutput.ConnectFail.Title=\"連線失敗\"\nOutput.ConnectFail.BadPath=\"無效的路徑或 URL。 請確認您的設定是正確的。\"\nOutput.ConnectFail.ConnectFailed=\"與伺服器連線失敗\"\nOutput.ConnectFail.InvalidStream=\"無法訪問指定的頻道或串流金鑰，請仔細檢查您的串流金鑰。 如果它是正確的有可能是連接到伺服器時出現問題。\"\nOutput.ConnectFail.HdrDisabled=\"目前這個輸出已停用 HDR 輸出。\"\nOutput.ConnectFail.Error=\"嘗試連線伺服器時發生非預期的錯誤。詳細資訊請見記錄檔。\"\nOutput.ConnectFail.Disconnected=\"與伺服器連線中斷。\"\nOutput.StreamEncodeError.Title=\"編碼錯誤\"\nOutput.StreamEncodeError.Msg=\"串流時編碼器發生錯誤。\"\nOutput.StreamEncodeError.Msg.LastError=\"串流時編碼器發生錯誤：<br><br>%1\"\nOutput.RecordFail.Title=\"無法開始錄影\"\nOutput.RecordFail.Unsupported=\"輸出格式不被支援或不支援超過一個音軌。請確認你的設定後再試一次。\"\nOutput.RecordNoSpace.Title=\"硬碟空間不足\"\nOutput.RecordNoSpace.Msg=\"沒有足夠的空間存放錄製影片。\"\nOutput.RecordError.Title=\"錄製錯誤\"\nOutput.RecordError.Msg=\"在錄製時發生了預料外的錯誤。\"\nOutput.RecordError.EncodeErrorMsg=\"錄影時編碼器發生錯誤。\"\nOutput.RecordError.EncodeErrorMsg.LastError=\"錄影時編碼器發生錯誤：<br><br>%1\"\nOutput.BadPath.Title=\"無效的路徑\"\nOutput.BadPath.Text=\"無法開啟設定的錄影路徑。請檢查 [設定] > [輸出] > [錄影] 底下的錄影路徑。\"\nOutput.NoBroadcast.Title=\"未設定直播\"\nOutput.NoBroadcast.Text=\"在開始串流前，您需要先設定直播。\"\nOutput.BroadcastStartFailed=\"無法開始直播\"\nOutput.BroadcastStopFailed=\"無法停止直播\"\nLogUploadDialog.Title=\"OBS Studio 記錄檔上傳\"\nLogUploadDialog.Labels.PrivacyNotice=\"請在上傳任何檔案之前，閱讀《<a href='https://obsproject.com/privacy-policy'>隱私權政策</a>》及其關於檔案上傳的章節。\"\nLogUploadDialog.Labels.Progress=\"記錄檔上傳中。請稍候…\"\nLogUploadDialog.Labels.Description.AppLog=\"您的記錄檔已上傳。您現在可以透過分享這個網址以便進行除錯或支援。\"\nLogUploadDialog.Labels.Description.CrashLog=\"您的當機報告已上傳。您現在可以分享該網址以供偵錯或支援用途。\"\nLogUploadDialog.Buttons.ConfirmUpload=\"上傳\"\nLogUploadDialog.Buttons.CopyURL=\"複製記錄檔網址\"\nLogUploadDialog.Buttons.AnalyzeURL=\"分析記錄檔\"\nLogUploadDialog.Buttons.RetryButton=\"重試\"\nLogUploadDialog.Errors.Template=\"上傳檔案時發生錯誤：\\n\\n%1\"\nLogUploadDialog.Errors.NoLogFile=\"找不到要上傳的檔案，或檔案為空。\"\nRemux.SourceFile=\"OBS 錄影\"\nRemux.TargetFile=\"目標檔案\"\nRemux.Remux=\"重新封裝\"\nRemux.Stop=\"停止重新封裝\"\nRemux.ClearFinished=\"清除已完成項目\"\nRemux.ClearAll=\"清除所有項目\"\nRemux.OBSRecording=\"OBS 錄影\"\nRemux.FinishedTitle=\"重新封裝完成\"\nRemux.Finished=\"錄影已被重新封裝\"\nRemux.FinishedError=\"錄影已被重新封裝，但檔案可能不完整\"\nRemux.SelectRecording=\"請選擇 OBS 錄影檔…\"\nRemux.SelectTarget=\"請選擇目標檔案…\"\nRemux.FileExistsTitle=\"目標檔案已存在\"\nRemux.FileExists=\"以下的目標檔案已經存在，是否要取代？\"\nRemux.ExitUnfinishedTitle=\"正在重新封裝\"\nRemux.ExitUnfinished=\"尚未完成重新封裝，現在停止可能導致目標檔案損毀。\\n你確定要停止重新封裝嗎？\"\nRemux.HelpText=\"將檔案拖曳到此視窗中進行 Remux，或是選擇一個空白的「OBS 錄製」元件來瀏覽檔案。\"\nRemux.NoFilesAddedTitle=\"未加入要重新封裝的檔案\"\nRemux.NoFilesAdded=\"未加入要重新封裝的檔案。請拖曳內含一或多個影片檔案的資料夾至此。\"\nMissingFiles=\"檔案遺失\"\nMissingFiles.MissingFile=\"檔案遺失\"\nMissingFiles.NewFile=\"新檔案\"\nMissingFiles.HelpText=\"跟上次您使用 OBS 相比，有遺失部分檔案。\"\nMissingFiles.Clear=\"<已清除>\"\nMissingFiles.NumFound=\"找到 %1 / %2 個\"\nMissingFiles.Search=\"搜尋目錄…\"\nMissingFiles.SelectFile=\"選擇檔案…\"\nMissingFiles.SelectDir=\"選取在哪個資料夾中搜尋\"\nMissingFiles.State=\"狀態\"\nMissingFiles.Missing=\"遺失\"\nMissingFiles.Replaced=\"取代\"\nMissingFiles.Cleared=\"清除\"\nMissingFiles.Found=\"找到\"\nMissingFiles.AutoSearch=\"找到其他符合的檔案\"\nMissingFiles.AutoSearchText=\"OBS 在資料夾中發現到其他符合的遺失檔案。是否加入？\"\nMissingFiles.NoMissing.Title=\"遺失檔案檢查\"\nMissingFiles.NoMissing.Text=\"似乎沒遺失檔案。\"\nMacPermissions.MenuAction=\"檢視應用程式權限…\"\nMacPermissions.Title=\"檢視應用程式權限\"\nMacPermissions.Description=\"OBS Studio 需要您的權限，才能提供某些功能。建議啟用這些權限，但論使用 App 是非必須的。您可以稍後再啟用。\"\nMacPermissions.Description.OpenDialog=\"您可以透過 OBS Studio 的選單，重新開啟這個對話方塊。\"\nMacPermissions.AccessGranted=\"已授予權限\"\nMacPermissions.RequestAccess=\"請求存取權\"\nMacPermissions.OpenPreferences=\"開啟 %1 偏好設定\"\nMacPermissions.Item.ScreenRecording=\"螢幕錄製\"\nMacPermissions.Item.ScreenRecording.Details=\"OBS 需要本權限，方能擷取您的螢幕畫面。\"\nMacPermissions.Item.Camera=\"攝影機\"\nMacPermissions.Item.Camera.Details=\"若要從網路攝影機或擷取卡擷取內容，則需要這個權限。\"\nMacPermissions.Item.Microphone=\"麥克風\"\nMacPermissions.Item.Microphone.Details=\"如果您想用麥克風或其他錄音器，請允許 OBS 的錄音權利。\"\nMacPermissions.Item.InputMonitoring=\"輸入監視\"\nMacPermissions.Item.InputMonitoring.Details=\"當OBS在背景執行時需要取得權限來使用快速鍵。\"\nMacPermissions.Item.Accessibility=\"協助工具\"\nMacPermissions.Item.Accessibility.Details=\"在舊版本的安裝上，OBS 可能列為“可訪問性”，而不是“輸入監視”。\"\nMacPermissions.Continue=\"繼續\"\nSourceLeak.Title=\"清理來源時發生錯誤\"\nSourceLeak.Text=\"更改場景集時發生錯誤，且無法卸載部分來源。這通常是因為外掛程式沒有正確釋放資源。請檢查您使用的所有外掛程式是否已經是最新版本。\\n\\nOBS Studio 將立即結束，以免造成資料損毀。\"\nBasic.DesktopDevice1=\"輸出音效 1\"\nBasic.DesktopDevice2=\"輸出音效 2\"\nBasic.AuxDevice1=\"麥克風/輸入音效 1\"\nBasic.AuxDevice2=\"麥克風/輸入音效 2\"\nBasic.AuxDevice3=\"麥克風/輸入音效 3\"\nBasic.AuxDevice4=\"麥克風/輸入音效 4\"\nBasic.Scene=\"場景\"\nBasic.DisplayCapture=\"截取螢幕\"\nBasic.Main.PreviewConextMenu.Enable=\"啟用預覽\"\nBasic.Main.Preview.Disable=\"停用預覽\"\nScaleFiltering=\"縮放濾鏡\"\nScaleFiltering.Point=\"點\"\nScaleFiltering.Bilinear=\"雙線性插值\"\nScaleFiltering.Bicubic=\"雙三次插值\"\nScaleFiltering.Area=\"範圍\"\nBlendingMethod=\"混合方式\"\nBlendingMethod.Default=\"預設\"\nBlendingMethod.SrgbOff=\"SRGB 關閉\"\nBlendingMode=\"混合模式\"\nBlendingMode.Normal=\"正常\"\nBlendingMode.Additive=\"加入\"\nBlendingMode.Subtract=\"減去\"\nBlendingMode.Screen=\"濾色\"\nBlendingMode.Multiply=\"色彩增值\"\nBlendingMode.Lighten=\"變亮\"\nBlendingMode.Darken=\"變暗\"\nDeinterlacing=\"去交錯\"\nDeinterlacing.Discard=\"捨棄\"\nDeinterlacing.TopFieldFirst=\"由偶數場開始\"\nDeinterlacing.BottomFieldFirst=\"由奇數場開始\"\nVolControl.SliderUnmuted=\"「%1」的音量滑桿︰\"\nVolControl.SliderMuted=\"'%1' 的音量滑桿︰(目前靜音中)\"\nVolControl.Mute=\"靜音 '%1'\"\nVolControl.Properties=\"'%1' 的屬性\"\nVolControl.UnassignedWarning.Title=\"未指派的音訊來源\"\nVolControl.UnassignedWarning.Text=\"「%1」尚未指派到任何音軌，在串流或錄影中將不會聽到這部份的音訊。\\n\\n若要將音訊來源指派到音軌上，請在右鍵選單中開啟「進階音訊屬性」，或者是「音效混音器」停駐視窗中工具列的齒輪按鈕。\"\nBasic.Main.AddSceneDlg.Title=\"新增場景\"\nBasic.Main.AddSceneDlg.Text=\"請輸入場景名稱\"\nBasic.Main.DefaultSceneName.Text=\"場景 %1\"\nBasic.Main.AddSceneCollection.Title=\"新增場景群組\"\nBasic.Main.AddSceneCollection.Text=\"請輸入場景群組的名稱\"\nBasic.Main.RenameSceneCollection.Title=\"重新命名場景群組\"\nBasic.Main.RemigrateSceneCollection.Title=\"更新場景集合解析度\"\nBasic.Main.RemigrateSceneCollection.Text=\"您想將場景集合「%1」的解析度更新為與當前設定檔的畫布解析度 %2x%3 相符嗎？\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.Active=\"無法在輸出活動時更新場景集合解析度。\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution=\"更新場景集合解析度失敗。原始解析度未知。\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset=\"無法重設：更改 OBS 解析度失敗。\"\nBasic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches=\"無法重設：當前解析度已經是場景集合的基礎解析度。\"\nAddProfile.Title=\"新增設定檔\"\nAddProfile.Text=\"請輸入設定檔的名稱\"\nAddProfile.WizardCheckbox=\"顯示自動設定精靈\"\nRenameProfile.Title=\"重新命名設定檔\"\nBasic.Main.MixerRename.Title=\"重新命名音訊源\"\nBasic.Main.MixerRename.Text=\"請輸入此音訊源的名稱\"\nBasic.Main.PreviewDisabled=\"目前預覽已停用\"\nBasic.SourceSelect=\"建立/選取來源\"\nBasic.SourceSelect.CreateNew=\"建立新來源\"\nBasic.SourceSelect.AddExisting=\"加入已建立來源\"\nBasic.SourceSelect.AddVisible=\"顯示來源\"\nBasic.Main.Sources.Visibility=\"可見性\"\nBasic.Main.Sources.VisibilityDescription=\"控制畫布中「%1」的可見性\"\nBasic.Main.Sources.Lock=\"鎖定\"\nBasic.Main.Sources.LockDescription=\"鎖定畫布中「%1」的位置及比例\"\nBasic.PropertiesWindow=\"屬性「%1」\"\nBasic.PropertiesWindow.AutoSelectFormat=\"%1 (自動選擇: %2)\"\nBasic.PropertiesWindow.SelectColor=\"選取顏色\"\nBasic.PropertiesWindow.SelectFont=\"選擇字型\"\nBasic.PropertiesWindow.SelectFont.WindowTitle=\"選擇字型\"\nBasic.PropertiesWindow.ConfirmTitle=\"設定值已變更\"\nBasic.PropertiesWindow.Confirm=\"有尚未儲存的變更。要保存他們嗎？\"\nBasic.PropertiesWindow.NoProperties=\"無可用的屬性\"\nBasic.PropertiesWindow.AddFiles=\"新增檔案\"\nBasic.PropertiesWindow.AddDir=\"新增目錄\"\nBasic.PropertiesWindow.AddURL=\"新增路徑／URL\"\nBasic.PropertiesWindow.AddEditableListDir=\"將目錄新增至「%1」\"\nBasic.PropertiesWindow.AddEditableListFiles=\"將檔案新增至「%1」\"\nBasic.PropertiesWindow.AddEditableListEntry=\"將項目新增至「%1」\"\nBasic.PropertiesWindow.EditEditableListEntry=\"編輯「%1」的項目\"\nBasic.PropertiesView.FPS.Simple=\"常用 FPS 值\"\nBasic.PropertiesView.FPS.Rational=\"有理數 FPS 值\"\nBasic.PropertiesView.FPS.ValidFPSRanges=\"FPS 的有效範圍:\"\nBasic.PropertiesView.UrlButton.Text=\"您是否要在您的預設網頁瀏覽器開啟此連結？\"\nBasic.PropertiesView.UrlButton.Text.Url=\"網址：%1\"\nBasic.PropertiesView.UrlButton.OpenUrl=\"開啟網址\"\nBasic.InteractionWindow=\"與「%1」互動\"\nBasic.StatusBar.Reconnecting=\"已斷線，將在%2秒後重新連線 (已嘗試%1次)\"\nBasic.StatusBar.AttemptingReconnect=\"嘗試重新連線…（已嘗試 %1 次）\"\nBasic.StatusBar.ReconnectSuccessful=\"已恢復連線\"\nBasic.StatusBar.Delay=\"延遲（%1 秒）\"\nBasic.StatusBar.DelayStartingIn=\"延遲（在 %1 秒內開始）\"\nBasic.StatusBar.DelayStoppingIn=\"延遲（在 %1 秒內停止）\"\nBasic.StatusBar.DelayStartingStoppingIn=\"延遲（在 %1 秒內停止，%2 秒後開始）\"\nBasic.StatusBar.RecordingSavedTo=\"錄影檔已儲存至「%1」\"\nBasic.StatusBar.ReplayBufferSavedTo=\"重播緩衝已儲存至「%1」\"\nBasic.StatusBar.ScreenshotSavedTo=\"螢幕擷取已儲存至「%1」\"\nBasic.StatusBar.AutoRemuxedTo=\"錄影已自動重新封裝並儲存至「%1」\"\nBasic.Filters=\"濾鏡\"\nBasic.Filters.AsyncFilters=\"音訊/影像 濾鏡\"\nBasic.Filters.AudioFilters=\"音訊濾鏡\"\nBasic.Filters.EffectFilters=\"特效濾鏡\"\nBasic.Filters.Title=\"'%1' 的濾鏡\"\nBasic.Filters.AddFilter.Title=\"濾鏡名稱\"\nBasic.Filters.AddFilter.Text=\"請選定濾鏡名稱\"\nBasic.TransformWindow=\"場景物件變換\"\nBasic.TransformWindow.Position=\"位置\"\nBasic.TransformWindow.PositionX=\"X 位置\"\nBasic.TransformWindow.PositionY=\"Y 位置\"\nBasic.TransformWindow.Rotation=\"旋轉\"\nBasic.TransformWindow.Size=\"大小\"\nBasic.TransformWindow.Width=\"寬度\"\nBasic.TransformWindow.Height=\"高度\"\nBasic.TransformWindow.Alignment=\"對齊基準\"\nBasic.TransformWindow.BoundsType=\"邊框類型\"\nBasic.TransformWindow.BoundsAlignment=\"邊框內對齊\"\nBasic.TransformWindow.Bounds=\"邊框大小\"\nBasic.TransformWindow.BoundsWidth=\"邊框寬度\"\nBasic.TransformWindow.BoundsHeight=\"邊框高度\"\nBasic.TransformWindow.CropToBounds=\"將影像裁剪到邊緣範圍內\"\nBasic.TransformWindow.Crop=\"剪裁\"\nBasic.TransformWindow.CropLeft=\"左側剪裁\"\nBasic.TransformWindow.CropRight=\"右側剪裁\"\nBasic.TransformWindow.CropTop=\"頂部剪裁\"\nBasic.TransformWindow.CropBottom=\"底部剪裁\"\nBasic.TransformWindow.Alignment.TopLeft=\"置頂靠左\"\nBasic.TransformWindow.Alignment.TopCenter=\"置頂居中\"\nBasic.TransformWindow.Alignment.TopRight=\"置頂靠右\"\nBasic.TransformWindow.Alignment.CenterLeft=\"靠左居中\"\nBasic.TransformWindow.Alignment.Center=\"置中\"\nBasic.TransformWindow.Alignment.CenterRight=\"靠右居中\"\nBasic.TransformWindow.Alignment.BottomLeft=\"置底靠左\"\nBasic.TransformWindow.Alignment.BottomCenter=\"置底居中\"\nBasic.TransformWindow.Alignment.BottomRight=\"置底靠右\"\nBasic.TransformWindow.BoundsType.None=\"無邊框\"\nBasic.TransformWindow.BoundsType.MaxOnly=\"等比例縮放至框內，不超過原始大小\"\nBasic.TransformWindow.BoundsType.ScaleInner=\"等比例縮放至框內\"\nBasic.TransformWindow.BoundsType.ScaleOuter=\"等比例縮放至框外\"\nBasic.TransformWindow.BoundsType.ScaleToWidth=\"等比例縮放至符合寬度\"\nBasic.TransformWindow.BoundsType.ScaleToHeight=\"等比例縮放至符合高度\"\nBasic.TransformWindow.BoundsType.Stretch=\"依外框變換\"\nBasic.TransformWindow.Title=\"編輯「%1」的變換設定\"\nBasic.TransformWindow.NoSelectedSource=\"未選擇來源\"\nBasic.Main.AddSourceHelp.Title=\"無法新增來源\"\nBasic.Main.AddSourceHelp.Text=\"您必須擁有至少一個場景才能新增來源。\"\nBasic.Main.Scenes=\"場景\"\nBasic.Main.Sources=\"來源\"\nBasic.Main.Source=\"來源\"\nBasic.Main.Controls=\"控制項\"\nBasic.Main.PreparingStream=\"正在準備…\"\nBasic.Main.Connecting=\"連線中…\"\nBasic.Main.StartRecording=\"開始錄製\"\nBasic.Main.StartReplayBuffer=\"開始重播緩衝\"\nBasic.Main.SaveReplay=\"儲存重播\"\nBasic.Main.StartStreaming=\"開始串流\"\nBasic.Main.StartBroadcast=\"開始直播\"\nBasic.Main.StartVirtualCam=\"啟動虛擬相機\"\nBasic.Main.StopRecording=\"停止錄製\"\nBasic.Main.PauseRecording=\"暫停錄影\"\nBasic.Main.UnpauseRecording=\"繼續錄製\"\nBasic.Main.SplitFile=\"分割錄影檔案\"\nBasic.Main.AddChapterMarker=\"新增章節標記（僅限混合 MP4/MOV）\"\nBasic.Main.StoppingRecording=\"停止錄製…\"\nBasic.Main.StopReplayBuffer=\"停止重播緩衝\"\nBasic.Main.StoppingReplayBuffer=\"正在停止重播緩衝…\"\nBasic.Main.SetupBroadcast=\"管理直播\"\nBasic.Main.StopStreaming=\"停止串流\"\nBasic.Main.StopBroadcast=\"結束直播\"\nBasic.Main.AutoStopEnabled=\"（自動停止）\"\nBasic.Main.StoppingStreaming=\"停止串流…\"\nBasic.Main.ForceStopStreaming=\"停止實況（丟棄延遲）\"\nBasic.Main.ShowContextBar=\"顯示來源工具列\"\nBasic.Main.HideContextBar=\"隱藏來源工具列\"\nBasic.Main.StopVirtualCam=\"停止虛擬相機\"\nBasic.Main.Group=\"群組 %1\"\nBasic.Main.GroupItems=\"群組選取的項目\"\nBasic.Main.Ungroup=\"取消群組\"\nBasic.Main.GridMode=\"網格模式\"\nBasic.Main.ListMode=\"清單模式\"\nBasic.Main.VirtualCamConfig=\"設定虛擬相機\"\nBasic.VCam.VirtualCamera=\"虛擬相機\"\nBasic.VCam.OutputType=\"輸出類型\"\nBasic.VCam.OutputSelection=\"輸出選擇區域\"\nBasic.VCam.OutputType.Program=\"節目（預設值）\"\nBasic.VCam.OutputSelection.NoSelection=\"尚未選擇此輸出類型的項目\"\nBasic.VCam.RestartWarning=\"為套用此更動，虛擬相機將會重啟\"\nBasic.MainMenu.File=\"檔案 (&F)\"\nBasic.MainMenu.File.Export=\"匯出 (&E)\"\nBasic.MainMenu.File.Import=\"匯入 (&I)\"\nBasic.MainMenu.File.ShowRecordings=\"顯示錄影 (&R)\"\nBasic.MainMenu.File.Remux=\"重新封裝錄影 (&M)\"\nBasic.MainMenu.File.Settings=\"設定 (&S)\"\nBasic.MainMenu.File.ShowSettingsFolder=\"顯示設定資料夾\"\nBasic.MainMenu.File.ShowProfileFolder=\"顯示設定檔資料夾\"\nBasic.MainMenu.File.ShowMissingFiles=\"檢查遺失檔案\"\nBasic.MainMenu.File.Exit=\"離開 (&X)\"\nBasic.MainMenu.Edit=\"編輯 (&E)\"\nBasic.MainMenu.Edit.Undo=\"復原 (&U)\"\nBasic.MainMenu.Edit.Redo=\"重做 (&R)\"\nBasic.MainMenu.Edit.LockPreview=\"鎖定預覽 (&L)\"\nBasic.MainMenu.Edit.Scale=\"預覽與縮放 (&S)\"\nBasic.MainMenu.Edit.Scale.Window=\"縮放至視窗\"\nBasic.MainMenu.Edit.Scale.Canvas=\"畫布 (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Output=\"輸出 (%1x%2)\"\nBasic.MainMenu.Edit.Scale.Manual=\"縮放 (%1x%2)\"\nBasic.MainMenu.Edit.Scale.ZoomIn=\"放大\"\nBasic.MainMenu.Edit.Scale.ZoomOut=\"縮小\"\nBasic.MainMenu.Edit.Scale.ResetZoom=\"重設縮放\"\nBasic.MainMenu.Edit.Transform=\"變換 (&T)\"\nBasic.MainMenu.Edit.Transform.EditTransform=\"編輯變換設定(&E)\"\nBasic.MainMenu.Edit.Transform.CopyTransform=\"複製變換\"\nBasic.MainMenu.Edit.Transform.PasteTransform=\"貼上變換\"\nBasic.MainMenu.Edit.Transform.ResetTransform=\"重設變換設定 (&R)\"\nBasic.MainMenu.Edit.Transform.Rotate90CW=\"順時針旋轉 90°\"\nBasic.MainMenu.Edit.Transform.Rotate90CCW=\"逆時針旋轉 90°\"\nBasic.MainMenu.Edit.Transform.Rotate180=\"旋轉 180°\"\nBasic.MainMenu.Edit.Transform.FlipHorizontal=\"水平翻轉 (&H)\"\nBasic.MainMenu.Edit.Transform.FlipVertical=\"垂直翻轉 (&V)\"\nBasic.MainMenu.Edit.Transform.FitToScreen=\"等比例縮放到螢幕大小 (&F)\"\nBasic.MainMenu.Edit.Transform.StretchToScreen=\"拉伸到螢幕大小 (&S)\"\nBasic.MainMenu.Edit.Transform.CenterToScreen=\"置中畫面 (&C)\"\nBasic.MainMenu.Edit.Transform.VerticalCenter=\"垂直置中\"\nBasic.MainMenu.Edit.Transform.HorizontalCenter=\"水平置中\"\nBasic.MainMenu.Edit.Order=\"排序 (&O)\"\nBasic.MainMenu.Edit.Order.MoveUp=\"向上移動 (&U)\"\nBasic.MainMenu.Edit.Order.MoveDown=\"向下移動 (&D)\"\nBasic.MainMenu.Edit.Order.MoveToTop=\"移至最上層 (&T)\"\nBasic.MainMenu.Edit.Order.MoveToBottom=\"移至最下層 (&B)\"\nBasic.MainMenu.Edit.AdvAudio=\"進階音訊屬性 (&A)\"\nBasic.MainMenu.View=\"檢視 (&V)\"\nBasic.MainMenu.View.Toolbars=\"工具列 (&T)\"\nBasic.MainMenu.View.ListboxToolbars=\"停駐視窗工具列\"\nBasic.MainMenu.View.ContextBar=\"來源工具列\"\nBasic.MainMenu.View.SourceIcons=\"來源圖示 (&I)\"\nBasic.MainMenu.View.StatusBar=\"狀態列 (&S)\"\nBasic.MainMenu.View.Fullscreen.Interface=\"全螢幕介面\"\nBasic.MainMenu.View.ResetUI=\"重設使用者介面 (&R)\"\nBasic.MainMenu.View.AlwaysOnTop=\"總是在最上層 (&A)\"\nBasic.MainMenu.View.SceneListMode=\"場景清單模式\"\nBasic.MainMenu.Docks=\"停駐視窗 (&D)\"\nBasic.MainMenu.Docks.ResetDocks=\"重設停駐視窗 (&R)\"\nBasic.MainMenu.Docks.LockDocks=\"鎖定停駐視窗 (&L)\"\nBasic.MainMenu.Docks.SideDocks=\"全高度停駐視窗 (&F)\"\nBasic.MainMenu.Docks.CustomBrowserDocks=\"自訂瀏覽器停駐視窗(&C)\"\nBasic.MainMenu.Import=\"匯入...\"\nBasic.MainMenu.Export=\"匯出...\"\nBasic.MainMenu.SceneCollection=\"場景群組 (&S)\"\nBasic.MainMenu.Profile=\"設定檔 (&P)\"\nBasic.MainMenu.Profile.Import=\"匯入設定檔\"\nBasic.MainMenu.Profile.Export=\"匯出設定檔\"\nBasic.MainMenu.SceneCollection.Import=\"匯入場景群組\"\nBasic.MainMenu.SceneCollection.Export=\"匯出場景群組\"\nBasic.MainMenu.SceneCollection.Remigrate=\"重設基礎解析度\"\nBasic.MainMenu.SceneCollection.Migrate=\"設定基礎解析度\"\nBasic.MainMenu.Profile.Exists=\"已有該設定檔\"\nBasic.MainMenu.Tools=\"工具 (&T)\"\nBasic.MainMenu.Help=\"說明 (&H)\"\nBasic.MainMenu.Help.HelpPortal=\"說明中心 (&P)\"\nBasic.MainMenu.Help.Website=\"前往 OBS 網站 (&W)\"\nBasic.MainMenu.Help.Discord=\"加入 Discord 伺服器 (&D)\"\nBasic.MainMenu.Help.WhatsNew=\"最新消息\"\nBasic.MainMenu.Help.Logs=\"記錄檔 (&L)\"\nBasic.MainMenu.Help.Logs.ShowLogs=\"顯示記錄檔 (&S)\"\nBasic.MainMenu.Help.Logs.UploadCurrentLog=\"上傳目前記錄檔 (&C)\"\nBasic.MainMenu.Help.Logs.UploadLastLog=\"上傳先前的記錄檔 (&P)\"\nBasic.MainMenu.Help.Logs.ViewCurrentLog=\"顯示目前記錄檔 (&V)\"\nBasic.MainMenu.Help.ReleaseNotes=\"版本資訊\"\nBasic.MainMenu.Help.CheckForUpdates=\"檢查更新\"\nBasic.MainMenu.Help.Repair=\"檢查檔案完整性\"\nBasic.MainMenu.Help.RestartSafeMode=\"以安全模式重新啟動\"\nBasic.MainMenu.Help.RestartNormal=\"以一般模式重新啟動\"\nBasic.MainMenu.Help.CrashLogs=\"當機報告 (&R)\"\nBasic.MainMenu.Help.CrashLogs.ShowLogs=\"顯示當機報告 (&S)\"\nBasic.MainMenu.Help.CrashLogs.UploadLastLog=\"上傳先前的當機報告 (&P)\"\nBasic.MainMenu.Help.About=\"關於 (&A)\"\nBasic.Settings.ProgramRestart=\"為了套用特效設定必須關閉重啟。\"\nBasic.Settings.ConfirmTitle=\"確認修改\"\nBasic.Settings.Confirm=\"您有未儲存的修改。 是否儲存？\"\nBasic.Settings.MultitrackVideoDisabledSettings=\"%1 %2 正在控制您的某些串流設定\"\nBasic.Settings.General=\"一般\"\nBasic.Settings.General.Language=\"語言\"\nBasic.Settings.General.Updater=\"更新\"\nBasic.Settings.General.UpdateChannel=\"更新頻道\"\nBasic.Settings.General.UpdateChannelDisabled=\"（已停用）\"\nBasic.Settings.General.UpdateChannelDefault=\"（預設值）\"\nBasic.Settings.General.EnableAutoUpdates=\"啟動時自動檢查更新\"\nBasic.Settings.General.OpenStatsOnStartup=\"在啟動時開啟狀態對話方塊\"\nBasic.Settings.General.HideOBSWindowsFromCapture=\"從螢幕擷取中隱藏 OBS 視窗\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Tooltip=\"本設定會隱藏所有非投影的 OBS Studio 視窗，防止被 OBS 擷取而影響\\n其他應用程式，如會議、螢幕共用、遠端支援、螢幕截圖及其他擷取軟體。\"\nBasic.Settings.General.HideOBSWindowsFromCapture.Message=\"啟用本選項會隱藏所有非投影的 OBS Studio 視窗，防止被 OBS 擷取而影響\\n其他應用程式，如會議、螢幕共用、遠端支援、螢幕截圖及其他擷取軟體。\"\nBasic.Settings.General.WarnBeforeStartingStream=\"啟動串流時顯示確認對話方塊\"\nBasic.Settings.General.WarnBeforeStoppingStream=\"停止串流時顯示確認對話方塊\"\nBasic.Settings.General.WarnBeforeStoppingRecord=\"停止錄影時顯示確認對話方塊\"\nBasic.Settings.General.Projectors=\"投影\"\nBasic.Settings.General.HideProjectorCursor=\"當游標在投影上時隱藏游標\"\nBasic.Settings.General.ProjectorAlwaysOnTop=\"讓投影總是在最上層\"\nBasic.Settings.General.Snapping=\"貼齊對準來源\"\nBasic.Settings.General.ScreenSnapping=\"來源與螢幕邊緣貼齊\"\nBasic.Settings.General.CenterSnapping=\"來源與水平中央以及垂直中央貼齊\"\nBasic.Settings.General.SourceSnapping=\"來源與其他來源貼齊\"\nBasic.Settings.General.SnapDistance=\"貼齊敏感度\"\nBasic.Settings.General.SpacingHelpers=\"顯示像素對齊引導線\"\nBasic.Settings.General.RecordWhenStreaming=\"串流時自動錄製\"\nBasic.Settings.General.KeepRecordingWhenStreamStops=\"串流停止時繼續錄製\"\nBasic.Settings.General.ReplayBufferWhileStreaming=\"串流時自動啟動重播緩衝\"\nBasic.Settings.General.KeepReplayBufferStreamStops=\"串流停止時依然保持啟用重播緩衝\"\nBasic.Settings.General.SysTray=\"系統工具列\"\nBasic.Settings.General.SysTrayWhenStarted=\"開始時最小化至系統列\"\nBasic.Settings.General.SystemTrayHideMinimize=\"總是最小化到系統列，而非工作列\"\nBasic.Settings.General.SaveProjectors=\"退出時保存投影設定\"\nBasic.Settings.General.CloseExistingProjectors=\"限制每個螢幕只有一個全螢幕投影區\"\nBasic.Settings.General.Preview=\"預覽\"\nBasic.Settings.General.OverflowHidden=\"隱藏畫面溢出\"\nBasic.Settings.General.OverflowAlwaysVisible=\"總是顯示溢出畫面\"\nBasic.Settings.General.OverflowSelectionHidden=\"就算來源不可見，仍顯示畫面溢出\"\nBasic.Settings.General.Importers=\"匯入工具\"\nBasic.Settings.General.AutomaticCollectionSearch=\"匯入時搜尋已知的場景群組位置\"\nBasic.Settings.General.SwitchOnDoubleClick=\"按兩下時切換到場景\"\nBasic.Settings.General.StudioPortraitLayout=\"啟用縱向/垂直佈局\"\nBasic.Settings.General.TogglePreviewProgramLabels=\"顯示預覽/展示標籤\"\nBasic.Settings.General.Multiview=\"多檢視\"\nBasic.Settings.General.Multiview.MouseSwitch=\"點擊以在場景之間切換\"\nBasic.Settings.General.Multiview.DrawSourceNames=\"顯示場景名稱\"\nBasic.Settings.General.Multiview.DrawSafeAreas=\"繪製安全區域 (EBU R 95)\"\nBasic.Settings.General.MultiviewLayout=\"多檢視佈局\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Top=\"橫排、頂部（八個場景）\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Bottom=\"橫排、底部（八個場景）\"\nBasic.Settings.General.MultiviewLayout.Vertical.Left=\"直行、靠左（八個場景）\"\nBasic.Settings.General.MultiviewLayout.Vertical.Right=\"直行、靠右（八個場景）\"\nBasic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top=\"橫排、頂部（18 個場景）\"\nBasic.Settings.General.MultiviewLayout.Horizontal.Extended.Top=\"橫排、頂部（24 個場景）\"\nBasic.Settings.General.MultiviewLayout.4Scene=\"只有場景（4 個場景）\"\nBasic.Settings.General.MultiviewLayout.9Scene=\"只有場景（9 個場景）\"\nBasic.Settings.General.MultiviewLayout.16Scene=\"只有場景（16 個場景）\"\nBasic.Settings.General.MultiviewLayout.25Scene=\"只有場景（25 個場景）\"\nBasic.Settings.General.ChannelName.stable=\"穩定版\"\nBasic.Settings.General.ChannelDescription.stable=\"最新穩定發行版\"\nBasic.Settings.General.ChannelName.beta=\"測試版／候選發行版\"\nBasic.Settings.General.ChannelDescription.beta=\"可能不穩定的預先發佈版本\"\nBasic.Settings.Appearance=\"外觀\"\nBasic.Settings.Appearance.General=\"一般\"\nBasic.Settings.Appearance.General.Theme=\"佈景主題\"\nBasic.Settings.Appearance.General.Variant=\"樣式\"\nBasic.Settings.Appearance.General.NoVariant=\"沒有樣式\"\nBasic.Settings.Appearance.FontScale=\"字型大小\"\nBasic.Settings.Appearance.Density=\"密度\"\nBasic.Settings.Appearance.Classic=\"經典\"\nBasic.Settings.Appearance.Compact=\"緊湊\"\nBasic.Settings.Appearance.Normal=\"正常\"\nBasic.Settings.Appearance.Comfortable=\"舒適\"\nBasic.Settings.Appearance.OptionsWarning=\"這個樣式不支援部分外觀選項。\"\nBasic.Settings.Stream=\"串流\"\nBasic.Settings.Stream.Destination=\"目的地\"\nBasic.Settings.Stream.Custom.UseAuthentication=\"使用身份驗證\"\nBasic.Settings.Stream.Custom.Username=\"使用者名稱\"\nBasic.Settings.Stream.Custom.Password=\"密碼\"\nBasic.Settings.Stream.Custom.Username.ToolTip=\"RIST：輸入 srp_username。\\nRTMP：輸入使用者名稱。\\nSRT：用不到。\"\nBasic.Settings.Stream.Custom.Password.ToolTip=\"RIST：輸入 srp_password。\\nRTMP：輸入密碼。\\nSRT：輸入加密密語。\"\nBasic.Settings.Stream.BandwidthTestMode=\"啟用頻寬測試模式\"\nBasic.Settings.Stream.TTVAddon=\"Twitch 聊天室附加元件\"\nBasic.Settings.Stream.TTVAddon.None=\"無\"\nBasic.Settings.Stream.TTVAddon.Both=\"BetterTTV 及 FrankerFaceZ\"\nBasic.Settings.Stream.MissingSettingAlert=\"缺少串流設定\"\nBasic.Settings.Stream.StreamSettingsWarning=\"開啟設定\"\nBasic.Settings.Stream.MissingUrlAndApiKey=\"缺少網址及串流金鑰。\\n\\n開啟設定以在「串流」分頁輸入網址及串流金鑰。\"\nBasic.Settings.Stream.MissingUrl=\"缺少串流網址。\\n\\n開啟設定以在「串流」分頁輸入網址。\"\nBasic.Settings.Stream.MissingStreamKey=\"缺少串流金鑰。\\n\\n開啟設定以在「串流」分頁輸入串流金鑰。\"\nBasic.Settings.Stream.IgnoreRecommended=\"忽略串流服務的建議設定\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Title=\"覆蓋建議設定\"\nBasic.Settings.Stream.IgnoreRecommended.Warn.Text=\"警告：忽略服務限制可能會導致串流品質變低或無法串流。\\n\\n是否繼續？\"\nBasic.Settings.Stream.Recommended.MaxVideoBitrate=\"最高視訊位元速率：%1 kbps\"\nBasic.Settings.Stream.Recommended.MaxAudioBitrate=\"最高音訊位元速率：%1 kbps\"\nBasic.Settings.Stream.Recommended.MaxResolution=\"最高解析度：%1\"\nBasic.Settings.Stream.Recommended.MaxFPS=\"最高 FPS：%1\"\nBasic.Settings.Stream.SpecifyCustomServer=\"指定自訂伺服器…\"\nBasic.Settings.Stream.ServiceCustomServer=\"自訂伺服器\"\nBasic.Settings.Stream.EnableMultitrackVideo=\"啟用 %1\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate=\"最大串流頻寬\"\nBasic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto=\"自動\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracks=\"最大影片軌道數\"\nBasic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto=\"自動\"\nBasic.Settings.Stream.MultitrackVideoStreamDumpEnable=\"啟用串流轉儲至 FLV（使用簡單錄製檔案設定）\"\nBasic.Settings.Stream.MultitrackVideoConfigOverride=\"配置覆寫（JSON）\"\nBasic.Settings.Stream.MultitrackVideoConfigOverrideEnable=\"啟用配置覆寫\"\nBasic.Settings.Stream.MultitrackVideoLabel=\"多軌視頻\"\nBasic.Settings.Stream.MultitrackVideoExtraCanvas=\"額外的畫布\"\nBasic.Settings.Stream.AdvancedOptions=\"進階選項\"\nBasic.Settings.Output=\"輸出\"\nBasic.Settings.Output.Format=\"錄影格式\"\nBasic.Settings.Output.Format.MKV=\"Matroska 視訊檔案 (.mkv)\"\nBasic.Settings.Output.Format.hMP4=\"混合 MP4 (.mp4)\"\nBasic.Settings.Output.Format.hMOV=\"混合 MOV (.mov)\"\nBasic.Settings.Output.Format.fMP4=\"分段 MP4 (.mp4)\"\nBasic.Settings.Output.Format.fMOV=\"分段 MOV (.mov)\"\nBasic.Settings.Output.Format.TT.fragmented_mov=\"分段的 MOV 會分塊寫入錄影內容，並且和傳統 MOV 檔案不同，不需要寫入檔案結尾。\\n這樣「寫入磁碟」程序即便被打斷（舉例來說，因為 BSOD 或斷電），檔案依然是可以播放的。\\n\\n這可能不相容某些播放器和編輯器，如果需要相容的話，可以使用 [檔案] → [重新封裝錄影] 將檔案轉換為更能廣泛相容的格式。\"\nBasic.Settings.Output.Format.TT.fragmented_mp4=\"分段的 MP4 會分塊寫入錄影內容，並且和傳統 MP4 檔案不同，不需要寫入檔案結尾。\\n這樣「寫入磁碟」程序即便被打斷（舉例來說，因為 BSOD 或斷電），檔案依然是可以播放的。\\n\\n這可能不相容某些播放器和編輯器，如果需要相容的話，可以使用 [檔案] → [重新封裝錄影] 將檔案轉換為更能廣泛相容的格式。\"\nBasic.Settings.Output.Encoder.Video=\"影像編碼器\"\nBasic.Settings.Output.Encoder.Audio=\"音效編碼器\"\nBasic.Settings.Output.SelectDirectory=\"選擇錄影資料夾\"\nBasic.Settings.Output.DynamicBitrate=\"動態調整位元速率以管理堵塞問題\"\nBasic.Settings.Output.DynamicBitrate.Beta=\"動態調整位元速率以管理堵塞問題 (Beta)\"\nBasic.Settings.Output.DynamicBitrate.TT=\"快速動態變更位元速率而非丟棄影格來緩解堵塞問題。\\n\\n註：這可能會在突發嚴重堵塞時增加觀看者的延遲。\\n位元速率降低後，可能會需要幾分鐘才會恢復。\\n\\n目前僅支援 RTMP。\"\nBasic.Settings.Output.Mode=\"輸出模式\"\nBasic.Settings.Output.Mode.Simple=\"簡易\"\nBasic.Settings.Output.Mode.Adv=\"進階\"\nBasic.Settings.Output.Mode.FFmpeg=\"FFmpeg 輸出\"\nBasic.Settings.Output.UseReplayBuffer=\"啟用重播緩衝\"\nBasic.Settings.Output.ReplayBuffer.SecondsMax=\"最大重播時間\"\nBasic.Settings.Output.ReplayBuffer.MegabytesMax=\"設定記憶體使用上限\"\nBasic.Settings.Output.ReplayBuffer.Estimate=\"估計記憶體使用量︰ %1 MB\"\nBasic.Settings.Output.ReplayBuffer.EstimateTooLarge=\"警告：預估記憶體使用量（%1 MiB）大於建議最大值（%2 MiB）\"\nBasic.Settings.Output.ReplayBuffer.EstimateUnknown=\"無法預估記憶體使用量。請設定最大記憶體使用量。\"\nBasic.Settings.Output.ReplayBuffer.Prefix=\"重播緩衝檔案名稱前綴\"\nBasic.Settings.Output.ReplayBuffer.Suffix=\"後綴\"\nBasic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg=\"錄影類型設定為自訂輸出 (FFmpeg) 時，不能使用重放緩衝區 (Replay Buffer)。\"\nBasic.Settings.Output.Simple.SavePath=\"錄影路徑\"\nBasic.Settings.Output.Simple.RecordingQuality=\"錄影畫質\"\nBasic.Settings.Output.Simple.RecordingQuality.Stream=\"與串流同等畫質\"\nBasic.Settings.Output.Simple.RecordingQuality.Small=\"高畫質，檔案大小中等\"\nBasic.Settings.Output.Simple.RecordingQuality.HQ=\"近乎無損畫質，檔案大小巨大\"\nBasic.Settings.Output.Simple.RecordingQuality.Lossless=\"無損畫質，非常大的檔案\"\nBasic.Settings.Output.Simple.Warn.VideoBitrate=\"警告：串流視訊位元速率將設定到目前串流服務的最高值—%1。\"\nBasic.Settings.Output.Simple.Warn.AudioBitrate=\"警告：串流音訊位元速率將設定到目前串流服務的最高值—%1。\"\nBasic.Settings.Output.Simple.Warn.CannotPause=\"警告：如果錄影編碼器畫質設為「（和串流編碼器相同）」，錄影時將無法暫停。\"\nBasic.Settings.Output.Simple.Warn.IncompatibleContainer=\"警告：目前選擇的錄影格式與選擇的串流編碼器不相容。\"\nBasic.Settings.Output.Simple.Warn.Encoder=\"警告：如果錄影與串流同時運作，並使用與串流不同的編碼品質設定，將會增加額外的 CPU 使用量。\"\nBasic.Settings.Output.Simple.Warn.Lossless=\"警告：無損品質的錄影會產生極大的檔案！在高解析度和高影格率下，無損品質每分鐘可能消耗超過 7GB 的磁碟空間。除非你有大量的磁碟空間，否則不建議在長時間錄影時使用無損品質。使用無損品質時，無法使用重播緩衝功能。\"\nBasic.Settings.Output.Simple.Warn.Lossless.Msg=\"你確定你想要使用無損畫質?\"\nBasic.Settings.Output.Simple.Warn.Lossless.Title=\"無損畫質警告！\"\nBasic.Settings.Output.Simple.Encoder.Software=\"軟體編碼（ x264 ）\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.H264=\"硬體（QSV，H.264）\"\nBasic.Settings.Output.Simple.Encoder.Hardware.QSV.AV1=\"硬體（QSV，AV1）\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.H264=\"硬體（AMD，H.264）\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.HEVC=\"硬體（AMD，HEVC）\"\nBasic.Settings.Output.Simple.Encoder.Hardware.AMD.AV1=\"硬體（AMD，AV1）\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.H264=\"硬體（NVENC，H.264）\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.AV1=\"硬體（NVENC，AV1）\"\nBasic.Settings.Output.Simple.Encoder.Hardware.NVENC.HEVC=\"硬體（NVENC，HEVC）\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.H264=\"硬體（Apple，H.264）\"\nBasic.Settings.Output.Simple.Encoder.Hardware.Apple.HEVC=\"硬體（Apple，HEVC）\"\nBasic.Settings.Output.Simple.Encoder.SoftwareLowCPU=\"軟體編碼（ x264 預設低 CPU 使用率，將增加檔案容量 ）\"\nBasic.Settings.Output.Simple.Codec.AAC.Default=\"AAC (預設值)\"\nBasic.Settings.Output.Simple.TwitchVodTrack=\"Twitch 視訊點播軌道 (使用軌道2)\"\nBasic.Settings.Output.Simple.RecAudioTrack=\"音軌\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Title=\"不相容的解析度或影格速率\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Msg=\"這個串流服務不支援您目前輸出之解析度和影格速率的其中一個或全部。將會變更為最接近的相容值：\\n\\n%1\\n\\n是否繼續？\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.Resolution=\"解析度：%1\"\nBasic.Settings.Output.Warn.EnforceResolutionFPS.FPS=\"FPS：%1\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Title=\"編碼器不相容\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg=\"“%1” 串流服務不支援 “%2” 編碼器。編碼器將更換為 “%3”。\\n\\n是否繼續？\"\nBasic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2=\"「%1」串流服務不支援「%2」和「%3」編碼器。這些編碼器會更換為「%4」和「%5」。\\n\\n是否繼續？\"\nBasic.Settings.Output.VideoBitrate=\"影像位元率（kbit/s）\"\nBasic.Settings.Output.AudioBitrate=\"音效位元率（kbit/s）\"\nBasic.Settings.Output.Reconnect=\"自動重新連線\"\nBasic.Settings.Output.RetryDelay=\"重試延遲\"\nBasic.Settings.Output.MaxRetries=\"最大嘗試次數\"\nBasic.Settings.Output.Advanced=\"允許客製化編碼器的設定（進階版）\"\nBasic.Settings.Output.EncoderPreset=\"編碼器預設\"\nBasic.Settings.Output.EncoderPreset.ultrafast=\"%1（低 CPU 使用率，品質最差）\"\nBasic.Settings.Output.EncoderPreset.veryfast=\"%1（預設）（中等 CPU 使用量，標準品質）\"\nBasic.Settings.Output.EncoderPreset.fast=\"%1（高 CPU 使用率，高品質）\"\nBasic.Settings.Output.CustomEncoderSettings=\"自訂編碼器設定\"\nBasic.Settings.Output.CustomMuxerSettings=\"自訂 Muxer 設定\"\nBasic.Settings.Output.NoSpaceFileName=\"產生沒有空格的檔案名稱\"\nBasic.Settings.Output.Adv.Rescale=\"重新縮放輸出\"\nBasic.Settings.Output.Adv.Rescale.Disabled=\"停用\"\nBasic.Settings.Output.Adv.AudioTrack=\"音軌\"\nBasic.Settings.Output.Adv.Streaming=\"串流\"\nBasic.Settings.Output.Adv.Streaming.Settings=\"串流設定\"\nBasic.Settings.Output.Adv.Audio.Track1=\"音軌 1\"\nBasic.Settings.Output.Adv.Audio.Track2=\"音軌 2\"\nBasic.Settings.Output.Adv.Audio.Track3=\"音軌 3\"\nBasic.Settings.Output.Adv.Audio.Track4=\"音軌 4\"\nBasic.Settings.Output.Adv.Audio.Track5=\"音軌 5\"\nBasic.Settings.Output.Adv.Audio.Track6=\"音軌 6\"\nBasic.Settings.Output.Adv.TwitchVodTrack=\"Twitch 視訊點播軌道\"\nBasic.Settings.Output.Adv.Encoder=\"編碼器設定\"\nBasic.Settings.Output.Adv.Recording=\"錄影\"\nBasic.Settings.Output.Adv.Recording.Settings=\"錄影設定\"\nBasic.Settings.Output.Adv.Recording.RecType=\"錄影類型\"\nBasic.Settings.Output.Adv.Recording.Type=\"類型\"\nBasic.Settings.Output.Adv.Recording.Type.Standard=\"標準\"\nBasic.Settings.Output.Adv.Recording.Type.FFmpegOutput=\"自訂輸出 (FFmpeg)\"\nBasic.Settings.Output.Adv.Recording.UseStreamEncoder=\"(使用串流編碼器)\"\nBasic.Settings.Output.Adv.Recording.Filename=\"檔案名稱格式\"\nBasic.Settings.Output.Adv.Recording.OverwriteIfExists=\"如果檔案存在就覆寫\"\nBasic.Settings.Output.Adv.FFmpeg.CustomModeWarning=\"自訂輸出（FFmpeg）未提供任何安全防護措施。\\n建議使用「標準」錄製類型。\"\nBasic.Settings.Output.Adv.FFmpeg.Type=\"FFmpeg 輸出類型\"\nBasic.Settings.Output.Adv.FFmpeg.Type.URL=\"輸出到 URL\"\nBasic.Settings.Output.Adv.FFmpeg.Type.RecordToFile=\"輸出到檔案\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.Common=\"常見錄影格式\"\nBasic.Settings.Output.Adv.FFmpeg.SaveFilter.All=\"所有檔案\"\nBasic.Settings.Output.Adv.FFmpeg.SavePathURL=\"檔案路徑或 URL\"\nBasic.Settings.Output.Adv.FFmpeg.Format=\"封裝格式\"\nBasic.Settings.Output.Adv.FFmpeg.FormatAudio=\"音訊\"\nBasic.Settings.Output.Adv.FFmpeg.FormatVideo=\"影像\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDefault=\"預設格式\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDesc=\"封裝格式說明\"\nBasic.Settings.Output.Adv.FFmpeg.FormatDescDef=\"自檔案路徑或 URL 推測音訊/影像格式\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDefault=\"預設編碼器\"\nBasic.Settings.Output.Adv.FFmpeg.AVEncoderDisable=\"停用編碼器\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoder=\"影像編碼器\"\nBasic.Settings.Output.Adv.FFmpeg.VEncoderSettings=\"影像編碼設定（如果有才啟用）\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoder=\"音效編碼器\"\nBasic.Settings.Output.Adv.FFmpeg.AEncoderSettings=\"音效編碼設定（如果有才啟用）\"\nBasic.Settings.Output.Adv.FFmpeg.MuxerSettings=\"Muxer 設定（如果有才啟用）\"\nBasic.Settings.Output.Adv.FFmpeg.GOPSize=\"關鍵訊框間隔 (訊框)\"\nBasic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat=\"顯示所有編碼解碼器 （即使可能不相容）\"\nBasic.Settings.Output.Adv.FFmpeg.Settings=\"FFmpeg 設定\"\nBasic.Settings.Output.EnableSplitFile=\"自動分割檔案\"\nBasic.Settings.Output.SplitFile.TypeTime=\"依時間分割\"\nBasic.Settings.Output.SplitFile.TypeSize=\"依大小分割\"\nBasic.Settings.Output.SplitFile.TypeManual=\"只手動分割\"\nBasic.Settings.Output.SplitFile.Time=\"分割時間\"\nBasic.Settings.Output.SplitFile.Size=\"分割大小\"\nScreenshot=\"螢幕擷取輸出\"\nScreenshot.SourceHotkey=\"螢幕擷取選取來源\"\nScreenshot.StudioProgram=\"螢幕擷取（展示）\"\nScreenshot.Preview=\"螢幕擷取 (預覽)\"\nScreenshot.Scene=\"螢幕擷取 (場景)\"\nScreenshot.Source=\"螢幕擷取 (來源)\"\nFilenameFormatting.TT.CCYY=\"年份，四位數\"\nFilenameFormatting.TT.YY=\"年份，結尾兩位數 (00-99)\"\nFilenameFormatting.TT.MM=\"月份，以十進位數字表示 (01-12)\"\nFilenameFormatting.TT.DD=\"月份日期，以 0 填充 (01-31)\"\nFilenameFormatting.TT.hh=\"24 小時制格式 (00-23)\"\nFilenameFormatting.TT.mm=\"分鐘 (00-59)\"\nFilenameFormatting.TT.ss=\"秒鐘 (00-59)\"\nFilenameFormatting.TT.Percent=\"% 符號\"\nFilenameFormatting.TT.a=\"簡寫週間日名稱\"\nFilenameFormatting.TT.A=\"全寫週間日名稱\"\nFilenameFormatting.TT.b=\"簡寫月份名稱\"\nFilenameFormatting.TT.B=\"全寫月份名稱\"\nFilenameFormatting.TT.d=\"月份日期，以 0 填充 (01-31)\"\nFilenameFormatting.TT.H=\"24 小時制格式 (00-23)\"\nFilenameFormatting.TT.I=\"12 小時制格式 (01-12)\"\nFilenameFormatting.TT.m=\"月份，以十進位數字表示 (01-12)\"\nFilenameFormatting.TT.M=\"分鐘 (00-59)\"\nFilenameFormatting.TT.p=\"AM 或 PM 標記\"\nFilenameFormatting.TT.s=\"自 UNIX epoch 開始經過的時間秒數\"\nFilenameFormatting.TT.S=\"秒鐘 (00-59)\"\nFilenameFormatting.TT.y=\"年份，結尾兩位數 (00-99)\"\nFilenameFormatting.TT.Y=\"年份\"\nFilenameFormatting.TT.z=\"時區中 ISO 8601 對 UTC 的偏移\"\nFilenameFormatting.TT.Z=\"時區名稱或簡寫\"\nFilenameFormatting.TT.FPS=\"每秒幀數\"\nFilenameFormatting.TT.CRES=\"來源（畫布）解析度\"\nFilenameFormatting.TT.ORES=\"輸出（縮放）解析度\"\nFilenameFormatting.TT.VF=\"影像格式\"\nBasic.Settings.Video=\"影像\"\nBasic.Settings.Video.Adapter=\"視訊轉接器\"\nBasic.Settings.Video.BaseResolution=\"來源（畫布）解析度\"\nBasic.Settings.Video.ScaledResolution=\"輸出（縮放）解析度\"\nBasic.Settings.Video.DownscaleFilter=\"降階濾波器\"\nBasic.Settings.Video.DownscaleFilter.Unavailable=\"[解析度相配，無需縮減]\"\nBasic.Settings.Video.FPS=\"FPS（影格率）\"\nBasic.Settings.Video.FPSCommon=\"常用 FPS 數值\"\nBasic.Settings.Video.FPSInteger=\"整數 FPS 數值\"\nBasic.Settings.Video.FPSFraction=\"浮點數 FPS 數值\"\nBasic.Settings.Video.Numerator=\"分子\"\nBasic.Settings.Video.Denominator=\"分母\"\nBasic.Settings.Video.Renderer=\"渲染器\"\nBasic.Settings.Video.Renderer.Experimental=\"%1（實驗性）\"\nBasic.Settings.Video.InvalidResolution=\"無效的解析度。 必須表達為 [寬]x[高] (例如：1920x1080)\"\nBasic.Settings.Video.CurrentlyActive=\"目前影像輸出進行中。 請先停止輸出以便修改影像設定。\"\nBasic.Settings.Video.DownscaleFilter.Bilinear=\"Bilinear（快速，但縮放時易模糊）\"\nBasic.Settings.Video.DownscaleFilter.Bicubic=\"Bicubic（縮放後較銳利，取 16 個樣本數）\"\nBasic.Settings.Video.DownscaleFilter.Lanczos=\"Lanczos（縮放後最為銳利，取 36 個樣本數）\"\nBasic.Settings.Video.DownscaleFilter.Area=\"Area (加權總和，4/6/9 個樣本)\"\nBasic.Settings.Audio=\"音效\"\nBasic.Settings.Audio.SampleRate=\"取樣頻率\"\nBasic.Settings.Audio.Channels=\"通道數\"\nBasic.Settings.Audio.Meters=\"計量器\"\nBasic.Settings.Audio.MeterDecayRate=\"衰變率\"\nBasic.Settings.Audio.MeterDecayRate.Fast=\"快速\"\nBasic.Settings.Audio.MeterDecayRate.Medium=\"中 (Type I PPM)\"\nBasic.Settings.Audio.MeterDecayRate.Slow=\"慢 (Type II PPM)\"\nBasic.Settings.Audio.PeakMeterType=\"峰值表類型\"\nBasic.Settings.Audio.PeakMeterType.SamplePeak=\"範例峰值\"\nBasic.Settings.Audio.PeakMeterType.TruePeak=\"真實峰值（較高的 CPU 用量）\"\nBasic.Settings.Audio.MultichannelWarning.Enabled=\"警告: 已啟用環繞聲音訊。\"\nBasic.Settings.Audio.MultichannelWarning=\"直播時請確認您使用的直播服務支援立體聲的接收和播放。例如 Facebook 360 Live 完全支援立體聲；YouTube Live 支援 5.1 音源輸入（並於電視播放）。\\n\\nOBS 音訊過濾器支援立體聲，但不保證 VST 外掛程式有支援。\"\nBasic.Settings.Audio.MultichannelWarning.Title=\"是否啟用環繞聲音訊？\"\nBasic.Settings.Audio.MultichannelWarning.Confirm=\"確實要啟用環繞聲音訊嗎？\"\nBasic.Settings.Audio.Devices=\"全域音訊裝置\"\nBasic.Settings.Audio.DesktopDevice=\"桌面音效\"\nBasic.Settings.Audio.DesktopDevice2=\"桌面音效 2\"\nBasic.Settings.Audio.AuxDevice=\"麥克風 / 輔助音效\"\nBasic.Settings.Audio.AuxDevice2=\"麥克風 / 輔助音效 2\"\nBasic.Settings.Audio.AuxDevice3=\"麥克風 / 輔助音效 3\"\nBasic.Settings.Audio.AuxDevice4=\"麥克風 / 輔助音效 4\"\nBasic.Settings.Audio.EnablePushToMute=\"啟用 按壓時靜音\"\nBasic.Settings.Audio.PushToMuteDelay=\"按壓時靜音 延遲\"\nBasic.Settings.Audio.EnablePushToTalk=\"啟用 按壓時說話\"\nBasic.Settings.Audio.PushToTalkDelay=\"按壓時說話 延遲\"\nBasic.Settings.Audio.UnknownAudioDevice=\"[設備未連接或不可用]\"\nBasic.Settings.Audio.Disabled=\"已停用\"\nBasic.Settings.Audio.LowLatencyBufferingMode=\"低延遲音訊緩衝模式（適合 Decklink/NDI 輸出）\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Enabled=\"警告：已啟用低延遲音訊緩衝模式。\"\nBasic.Settings.Audio.LowLatencyBufferingWarning=\"「低延遲音訊緩衝模式」可能會導致音訊出現故障，或無法播放某些來源的音訊。\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Title=\"是否啟用低延遲音訊緩衝模式？\"\nBasic.Settings.Audio.LowLatencyBufferingWarning.Confirm=\"您確定要啟用低延遲音訊緩衝模式嗎？\"\nBasic.Settings.Accessibility=\"協助工具\"\nBasic.Settings.Accessibility.ColorOverrides=\"使用不同顏色\"\nBasic.Settings.Accessibility.ColorOverrides.SelectRed=\"來源邊框（選擇區域）\"\nBasic.Settings.Accessibility.ColorOverrides.SelectGreen=\"來源邊框（裁切）\"\nBasic.Settings.Accessibility.ColorOverrides.SelectBlue=\"來源邊框（暫留區域）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreen=\"混音器音量頻段（-60 至 -20 分貝）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellow=\"混音器音量頻段（-20 至 -9 分貝）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRed=\"混音器音量頻段（-9 至 0 分貝）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerGreenActive=\"混音器音量頻段（-60 至 -20 分貝，使用中）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerYellowActive=\"混音器音量頻段（-20 至 -9 分貝，使用中）\"\nBasic.Settings.Accessibility.ColorOverrides.MixerRedActive=\"混音器音量頻段（-9 至 0 分貝，使用中）\"\nBasic.Settings.Accessibility.ColorOverrides.Preset=\"色彩配置\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Default=\"預設\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.Custom=\"自訂\"\nBasic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1=\"色盲替代配置\"\nBasic.Settings.Advanced=\"進階\"\nBasic.Settings.Advanced.General.ConfirmOnExit=\"結束時顯示「輸出進行中」警告\"\nBasic.Settings.Advanced.General.ProcessPriority=\"程序優先順序\"\nBasic.Settings.Advanced.General.ProcessPriority.High=\"高\"\nBasic.Settings.Advanced.General.ProcessPriority.AboveNormal=\"在標準以上\"\nBasic.Settings.Advanced.General.ProcessPriority.Normal=\"標準\"\nBasic.Settings.Advanced.General.ProcessPriority.BelowNormal=\"低於一般\"\nBasic.Settings.Advanced.General.ProcessPriority.Idle=\"閒置\"\nBasic.Settings.Advanced.FormatWarning=\"警告：NV12/P010 之外的色彩格式主要用於錄影，不建議在串流時使用。在串流使用，可能會因為色彩格式轉換，而導致 CPU 用量增加。\"\nBasic.Settings.Advanced.FormatWarningPreciseSdr=\"警告：高精度格式更常和 HDR 色彩空間一起使用。\"\nBasic.Settings.Advanced.FormatWarning2100=\"警告：Rec. 2100 應使用精度更高的格式。\"\nBasic.Settings.Advanced.Video.ColorFormat=\"色彩格式\"\nBasic.Settings.Advanced.Video.ColorFormat.NV12=\"NV12（8 位，4:2:0，2 色盤）\"\nBasic.Settings.Advanced.Video.ColorFormat.I420=\"I420（8 位，4:2:0，3 色盤）\"\nBasic.Settings.Advanced.Video.ColorFormat.I444=\"I444（8 位，4:4:4，3 色盤）\"\nBasic.Settings.Advanced.Video.ColorFormat.P010=\"P010（10 位，4:2:0，2 色盤）\"\nBasic.Settings.Advanced.Video.ColorFormat.I010=\"I010（10 位，4:2:0，3 色盤）\"\nBasic.Settings.Advanced.Video.ColorFormat.P216=\"P216（16 位，4:2:2，2 色盤）\"\nBasic.Settings.Advanced.Video.ColorFormat.P416=\"P416（16 位，4:4:4，2 色盤）\"\nBasic.Settings.Advanced.Video.ColorFormat.BGRA=\"BGRA（8 位）\"\nBasic.Settings.Advanced.Video.ColorSpace=\"色彩空間\"\nBasic.Settings.Advanced.Video.ColorRange=\"色彩範圍\"\nBasic.Settings.Advanced.Video.ColorRange.Partial=\"部分\"\nBasic.Settings.Advanced.Video.ColorRange.Full=\"完整\"\nBasic.Settings.Advanced.Video.SdrWhiteLevel=\"SDR 白階\"\nBasic.Settings.Advanced.Video.HdrNominalPeakLevel=\"HDR 標稱峰值量級\"\nBasic.Settings.Advanced.Audio.MonitoringDevice=\"監測裝置\"\nBasic.Settings.Advanced.Audio.MonitoringDevice.Default=\"預設裝置\"\nBasic.Settings.Advanced.Audio.DisableAudioDucking=\"停用 Windows 雙層次音量自動調整\"\nBasic.Settings.Advanced.StreamDelay=\"實況延遲\"\nBasic.Settings.Advanced.StreamDelay.Duration=\"持續時間\"\nBasic.Settings.Advanced.StreamDelay.Preserve=\"重新連線時維持截止點 (增加延遲)\"\nBasic.Settings.Advanced.StreamDelay.MemoryUsage=\"預計記憶體使用率: %1 MB\"\nBasic.Settings.Advanced.Network=\"網路\"\nBasic.Settings.Advanced.Network.Disabled=\"目前選取的串流協定，不支援更改網路設定。\"\nBasic.Settings.Advanced.Network.BindToIP=\"綁定到 IP\"\nBasic.Settings.Advanced.Network.IPFamily=\"IP 家族\"\nBasic.Settings.Advanced.Network.EnableNewSocketLoop=\"啟用網路最佳化\"\nBasic.Settings.Advanced.Network.EnableLowLatencyMode=\"啟用 TCP pacing\"\nBasic.Settings.Advanced.Network.TCPPacing.Tooltip=\"嘗試藉由限制傳輸速度，來減少 RTMP 輸出對網路上其他要求延遲之應用程式的影響。\\n這可能會增加在不穩定連線中被捨棄影格的風險。\"\nBasic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior=\"快速鍵焦點行為\"\nBasic.Settings.Advanced.Hotkeys.NeverDisableHotkeys=\"永不停用快速鍵\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysInFocus=\"當主視窗處於焦點，則停用熱鍵\"\nBasic.Settings.Advanced.Hotkeys.DisableHotkeysOutOfFocus=\"僅在焦點不在主視窗時停用快速鍵\"\nBasic.Settings.Advanced.AutoRemux=\"自動重新封裝為 %1\"\nBasic.Settings.Advanced.AutoRemux.MP4=\"（以 mkv 格式錄製）\"\nBasic.AdvAudio=\"進階音訊屬性\"\nBasic.AdvAudio.ActiveOnly=\"僅使用中來源\"\nBasic.AdvAudio.Name=\"名稱\"\nBasic.AdvAudio.Volume=\"音量\"\nBasic.AdvAudio.VolumeSource=\"'%1' 的音量\"\nBasic.AdvAudio.Mono=\"單聲道\"\nBasic.AdvAudio.MonoSource=\"'%1' 的單聲道降混\"\nBasic.AdvAudio.Balance=\"聲道平衡\"\nBasic.AdvAudio.BalanceSource=\"%1 的平衡\"\nBasic.AdvAudio.SyncOffset=\"同步位移\"\nBasic.AdvAudio.SyncOffsetSource=\"'%1' 的同步偏移\"\nBasic.AdvAudio.Monitoring=\"音訊監測\"\nBasic.AdvAudio.Monitoring.None=\"關閉監測\"\nBasic.AdvAudio.Monitoring.MonitorOnly=\"僅監測（輸出為靜音）\"\nBasic.AdvAudio.Monitoring.Both=\"監測和輸出\"\nBasic.AdvAudio.MonitoringSource=\"'%1' 的音訊監控\"\nBasic.AdvAudio.AudioTracks=\"音軌\"\nBasic.Settings.Hotkeys=\"快捷鍵\"\nBasic.Settings.Hotkeys.Pair=\"按鍵組合與'%1'共用作為切換鍵\"\nBasic.Settings.Hotkeys.Filter=\"過濾\"\nBasic.Settings.Hotkeys.FilterByHotkey=\"依快捷鍵過濾\"\nBasic.Settings.Hotkeys.DuplicateWarning=\"有一或多個其他動作正在使用這個快捷鍵。點一下顯示衝突部分\"\nBasic.Settings.Hotkeys.PleaseWait=\"正在載入快捷鍵，請稍候…\"\nBasic.Hotkeys.SelectScene=\"切換到場景\"\nBasic.SystemTray.Show=\"顯示\"\nBasic.SystemTray.Hide=\"隱藏\"\nBasic.SystemTray.Message.Reconnecting=\"連線已中斷。正在重新連線…\"\nHotkeys.Insert=\"插入鍵\"\nHotkeys.Delete=\"刪除鍵\"\nHotkeys.Home=\"Home 鍵\"\nHotkeys.End=\"End 鍵\"\nHotkeys.PageUp=\"Page Up 鍵\"\nHotkeys.PageDown=\"Page Down 鍵\"\nHotkeys.NumLock=\"數字鎖定\"\nHotkeys.ScrollLock=\"捲動鎖定\"\nHotkeys.CapsLock=\"大寫鎖定\"\nHotkeys.Backspace=\"倒退鍵\"\nHotkeys.Tab=\"分頁\"\nHotkeys.Print=\"列印\"\nHotkeys.Pause=\"暫停\"\nHotkeys.Left=\"左\"\nHotkeys.Right=\"右\"\nHotkeys.Up=\"上\"\nHotkeys.Down=\"下\"\nHotkeys.Super=\"超級\"\nHotkeys.Menu=\"選單\"\nHotkeys.Space=\"空格\"\nHotkeys.NumpadNum=\"九宮鍵 %1\"\nHotkeys.NumpadMultiply=\"九宮鍵 乘號\"\nHotkeys.NumpadDivide=\"九宮鍵 除號\"\nHotkeys.NumpadAdd=\"九宮鍵 加號\"\nHotkeys.NumpadSubtract=\"九宮鍵 減號\"\nHotkeys.NumpadDecimal=\"九宮鍵 十進位\"\nHotkeys.AppleKeypadNum=\"%1 (九宮鍵)\"\nHotkeys.AppleKeypadMultiply=\"* (九宮鍵)\"\nHotkeys.AppleKeypadDivide=\"/ (九宮鍵)\"\nHotkeys.AppleKeypadAdd=\"+ (九宮鍵)\"\nHotkeys.AppleKeypadSubtract=\"+ (九宮鍵)\"\nHotkeys.AppleKeypadDecimal=\". (九宮鍵)\"\nHotkeys.AppleKeypadEqual=\"+ (九宮鍵)\"\nHotkeys.MouseButton=\"滑鼠 %1\"\nHotkeys.Escape=\"Esc 鍵\"\nMute=\"靜音\"\nUnmute=\"取消靜音\"\nPush-to-mute=\"按壓時靜音\"\nPush-to-talk=\"按壓時說話\"\nSceneItemShow=\"顯示 「%1」\"\nSceneItemHide=\"隱藏 「%1」\"\nOutputWarnings.NoTracksSelected=\"您必須至少選擇一個軌道\"\nOutputWarnings.NoTracksSelectedOnExit.Title=\"輸出設定有誤\"\nOutputWarnings.NoTracksSelectedOnExit.Text=\"所有輸出應當選取至少一個音軌。\"\nOutputWarnings.MP4Recording=\"警告︰ 如果檔案沒有完整儲存(例如由於 BSOD，斷電等中斷)，儲存成 MP4/MOV 的檔案將無法復原 。如果想要記錄多個音軌，請考慮儲存成 MKV ，並在完成後重新封裝成 MP4/MOV (檔案 -> 重新封裝)\"\nOutputWarnings.CannotPause=\"警告：如果錄影編碼器設為「（使用串流編碼器）」，錄影時將無法暫停。\"\nOutputWarnings.CodecIncompatible=\"您選擇的音訊或影片編碼器因不相容而重設。請從清單中選擇相容的編碼器。\"\nCodecCompat.Incompatible=\"（與 %1 不相容）\"\nCodecCompat.CodecPlaceholder=\"選擇編碼器…\"\nCodecCompat.ContainerPlaceholder=\"選擇格式…\"\nCodecCompat.CodecMissingOnExit.Title=\"未選擇編碼器\"\nCodecCompat.CodecMissingOnExit.Text=\"未設定至少一個影片或音訊編碼器。請確定已經為錄影和串流選擇編碼器。\"\nCodecCompat.ContainerMissingOnExit.Title=\"未選擇格式\"\nCodecCompat.ContainerMissingOnExit.Text=\"尚未選擇錄影格式。請選擇一個與選定串流編碼器相容的錄影格式。\"\nFinalScene.Title=\"刪除場景\"\nFinalScene.Text=\"至少要有一個場景。\"\nNoSources.Title=\"沒有來源\"\nNoSources.Text=\"看起來您尚未增加任何視訊來源，所以我們只會輸出黑畫面。確定？\"\nNoSources.Text.AddSource=\"您可以透過點一下主視窗「來源」框底下的 + 按鈕，隨時增加來源。\"\nNoSources.Label=\"您未設定任何來源。\\n按下方 + 鍵，\\n或在此按下右鍵新增一個來源。\"\nChangeBG=\"設定顏色\"\nCustomColor=\"自訂顏色\"\nBrowserSource.EnableHardwareAcceleration=\"啟用瀏覽器來源硬體加速\"\nAbout=\"關於\"\nAbout.Info=\"OBS Studio 是款自由且開放原始碼的影片錄製與直播串流軟體。\"\nAbout.Donate=\"做出貢獻\"\nAbout.GetInvolved=\"參與貢獻\"\nAbout.Authors=\"作者群\"\nAbout.License=\"授權\"\nAbout.Error=\"錯誤！無法讀取檔案。\\n\\n前往：%1\"\nAbout.Contribute=\"支持OBS專案\"\nAddUrl.Title=\"使用網址加入來源\"\nAddUrl.Text=\"您拖曳了網址到 OBS。這會自動將連結加為來源。繼續？\"\nAddUrl.Text.Url=\"網址：%1\"\nResizeOutputSizeOfSource=\"縮放輸出大小（來源大小）\"\nResizeOutputSizeOfSource.Text=\"來源和輸出解析度，將會縮放到目前來源的大小。\"\nResizeOutputSizeOfSource.Continue=\"是否繼續？\"\nPreviewTransition=\"預覽轉場\"\nImporter=\"場景群組匯入工具\"\nImporter.SelectCollection=\"選擇場景群組\"\nImporter.Collection=\"場景群組\"\nImporter.HelpText=\"請加入檔案至此視窗，以從 OBS 或其他支援的程式匯入群組。\"\nImporter.Path=\"群組路徑\"\nImporter.Program=\"偵測到的應用程式\"\nImporter.AutomaticCollectionPrompt=\"自動搜尋場景群組\"\nImporter.AutomaticCollectionText=\"OBS 能夠自動從支援的第三方程式尋找能匯入的場景群組。您是否要 OBS 幫你自動尋找群組？\\n\\n您稍後可在 [設定] > [一般] > [匯入工具] 變更。\"\nImporter.SelectFile=\"瀏覽...\"\nRestart=\"重新啟動\"\nNeedsRestart=\"OBS Studio 需要重新啟動。您是否要現在重新啟動？\"\nLoadProfileNeedsRestart=\"設定檔內有需要重新啟動 OBS 的設定：\\n%1\\n\\n是否要重新啟動 OBS，讓這些設定生效？\"\nContextBar.NoSelectedSource=\"未選擇來源\"\nContextBar.MediaControls.PlayMedia=\"播放媒體\"\nContextBar.MediaControls.PauseMedia=\"暫停媒體\"\nContextBar.MediaControls.StopMedia=\"停止媒體\"\nContextBar.MediaControls.RestartMedia=\"重新播放媒體\"\nContextBar.MediaControls.PlaylistNext=\"播放清單中下一個\"\nContextBar.MediaControls.PlaylistPrevious=\"播放清單中上一個\"\nContextBar.MediaControls.BlindSeek=\"媒體跳轉元件\"\nYouTube.Auth.Ok=\"授權成功。\\n您現可關閉本頁面。\"\nYouTube.Auth.NoCode=\"授權程序未完成。\"\nYouTube.Auth.NoChannels=\"選取帳戶沒有頻道\"\nYouTube.Auth.WaitingAuth.Title=\"YouTube 使用者授權\"\nYouTube.Auth.WaitingAuth.Text=\"請在 OBS 外的瀏覽器完成授權程序。<br>倘若未彈出瀏覽器視窗，請前往本連結完成授權程序：<br>%1\"\nYouTube.AuthError.Text=\"無法取得頻道資訊：%1。\"\nYouTube.Actions.WindowTitle=\"YouTube 直播設定－頻道：%1\"\nYouTube.Actions.CreateNewEvent=\"建立新直播\"\nYouTube.Actions.ChooseEvent=\"選擇現有直播\"\nYouTube.Actions.Title=\"標題*\"\nYouTube.Actions.MyBroadcast=\"我的直播\"\nYouTube.Actions.Description=\"描述\"\nYouTube.Actions.Privacy=\"瀏覽權限*\"\nYouTube.Actions.Privacy.Private=\"私人\"\nYouTube.Actions.Privacy.Public=\"公開\"\nYouTube.Actions.Privacy.Unlisted=\"不公開\"\nYouTube.Actions.Category=\"分類\"\nYouTube.Actions.Thumbnail=\"縮圖\"\nYouTube.Actions.Thumbnail.SelectFile=\"選擇檔案…\"\nYouTube.Actions.Thumbnail.NoFileSelected=\"未選擇檔案\"\nYouTube.Actions.Thumbnail.ClearFile=\"清除\"\nYouTube.Actions.MadeForKids=\"這部影片是為兒童打造的嗎？*\"\nYouTube.Actions.MadeForKids.Yes=\"是，這是為兒童打造的影片\"\nYouTube.Actions.MadeForKids.No=\"否，這不是為兒童打造的影片\"\nYouTube.Actions.AdditionalSettings=\"其他設定\"\nYouTube.Actions.Latency=\"延遲\"\nYouTube.Actions.Latency.Normal=\"正常延遲\"\nYouTube.Actions.Latency.Low=\"低延遲\"\nYouTube.Actions.Latency.UltraLow=\"超低延遲\"\nYouTube.Actions.EnableAutoStart=\"啟用自動開始功能\"\nYouTube.Actions.EnableAutoStop=\"啟用自動停止功能\"\nYouTube.Actions.AutoStartStop.TT=\"表示這個排定直播是否該自動開始\"\nYouTube.Actions.EnableDVR=\"啟用 DVR 功能\"\nYouTube.Actions.360Video=\"360 影片\"\nYouTube.Actions.ScheduleForLater=\"排定稍後串流\"\nYouTube.Actions.RememberSettings=\"記住這些設定\"\nYouTube.Actions.Create_Ready=\"建立直播\"\nYouTube.Actions.Create_GoLive=\"建立直播，開始串流\"\nYouTube.Actions.Choose_Ready=\"選擇直播\"\nYouTube.Actions.Choose_GoLive=\"選擇直播，開始串流\"\nYouTube.Actions.Create_Schedule=\"排定直播\"\nYouTube.Actions.Create_Schedule_Ready=\"排定及選擇直播\"\nYouTube.Actions.Dashboard=\"開啟 YouTube Studio\"\nYouTube.Actions.Error.Title=\"建立 Live 直播時發生錯誤\"\nYouTube.Actions.Error.Text=\"存取 YouTube 時發生「%1」錯誤。<br/>詳細錯誤資訊請參閱 <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.General=\"存取 YouTube 時發生錯誤。請檢查網路連線，並確認是否可以連線到 YouTube 伺服器。\"\nYouTube.Actions.Error.NoBroadcastCreated=\"建立直播時發生「%1」錯誤。<br/>詳細錯誤資訊請參閱 <a href='https://developers.google.com/youtube/v3/live/docs/errors'>https://developers.google.com/youtube/v3/live/docs/errors</a>\"\nYouTube.Actions.Error.NoStreamCreated=\"未建立串流。請重新連接您的帳號。\"\nYouTube.Actions.Error.YouTubeApi=\"YouTube API 發生錯誤。請見記錄檔以取得更多資訊。\"\nYouTube.Actions.Error.BroadcastNotFound=\"找不到選取廣播。\"\nYouTube.Actions.Error.FileMissing=\"找不到選取檔案。\"\nYouTube.Actions.Error.FileOpeningFailed=\"開啟選取檔案時發生錯誤。\"\nYouTube.Actions.Error.FileTooLarge=\"選取檔案大小過大（上限：2 MiB）。\"\nYouTube.Actions.Error.BroadcastTransitionFailed=\"直播轉場失敗：%1<br/><br/>如果問題仍然存在，<a href='https://studio.youtube.com/video/%2/livestreaming'>請開啟 YouTube Studio 中的直播</a>，自行嘗試轉場。\"\nYouTube.Actions.Error.BroadcastTestStarting=\"直播正在轉往測試階段，需要一些時間。請在 10-30 秒後重試。\"\nYouTube.Actions.EventsLoading=\"正在載入事件清單…\"\nYouTube.Actions.EventCreated.Title=\"活動已建立\"\nYouTube.Actions.EventCreated.Text=\"活動建立成功。\"\nYouTube.Actions.Stream=\"串流\"\nYouTube.Actions.Stream.ScheduledFor=\"已排程 %1\"\nYouTube.Actions.Stream.Resume=\"繼續被中斷的直播\"\nYouTube.Actions.Stream.YTStudio=\"YouTube Studio 自動建立\"\nYouTube.Actions.Notify.CreatingBroadcast=\"正在建立 Live 直播，請稍候…\"\nYouTube.Actions.AutoStartStreamingWarning.Title=\"需要自行啟動\"\nYouTube.Actions.AutoStartStreamingWarning=\"已停用本活動的「自動開始」功能。請點選「開始直播」進行直播。\"\nYouTube.Actions.AutoStopStreamingWarning=\"您之後會無法重新連線。<br>您的串流會停止，而您本人則會進入離線狀態。\"\nYouTube.Errors.liveStreamingNotEnabled=\"未在選取的 YouTube 頻道啟用即時串流功能。<br/><br/>請查閱 <a href='https://www.youtube.com/features'>youtube.com/features</a> 深入了解。\"\nYouTube.Errors.livePermissionBlocked=\"選取的 YouTube 頻道不支援「即時串流」功能。<br/>請注意，在頻道設定啟用即時串流功能後，可能需要等待至多 24 小時才能使用串流功能。<br/><br/>請查閱 <a href='https://www.youtube.com/features'>youtube.com/features</a> 深入了解。\"\nYouTube.Errors.errorExecutingTransition=\"後端發生錯誤導致轉場失敗。請稍等幾秒後重試。\"\nYouTube.Errors.errorStreamInactive=\"YouTube 未收到您串流的資料。請檢查設定後重試。\"\nYouTube.Errors.invalidTransition=\"嘗試進行的轉場無效。可能是因為串流尚未完成上一個轉場程序。請等待幾秒鐘後重試。\"\nYouTube.DocksRemoval.Title=\"清除舊版 YouTube 瀏覽器停駐視窗\"\nYouTube.DocksRemoval.Text=\"這些瀏覽器停駐視窗將被刪除，因為它們已被標記為過時：\\n\\n%1\\n請改用「停駐視窗/YouTube 直播控制室」。\"\nConfigDownload.WarningMessageTitle=\"警告\"\nFailedToStartStream.MissingConfigURL=\"目前服務沒有可用設定 URL\"\nFailedToStartStream.NoCustomRTMPURLInSettings=\"未指定自訂 RTMP URL\"\nFailedToStartStream.InvalidCustomConfig=\"無效的自訂配置\"\nFailedToStartStream.MissingCanvas=\"已設定的額外畫布遺失\"\nFailedToStartStream.FailedToCreateMultitrackVideoService=\"建立多軌視訊服務失敗\"\nFailedToStartStream.FailedToCreateMultitrackVideoOutput=\"無法建立多軌視訊 RTMP 輸出\"\nFailedToStartStream.EncoderNotAvailable=\"NVENC 不可用。\\n\\n找不到編碼器類型「%1」\"\nFailedToStartStream.FailedToCreateVideoEncoder=\"無法建立視訊編碼器「%1」(類型：「%2」)\"\nFailedToStartStream.FailedToGetOBSVideoInfo=\"建立編碼器「%1」時無法取得 OBS 視訊資訊（類型：「%2」）\"\nFailedToStartStream.FailedToCreateAudioEncoder=\"建立音訊編碼器失敗\"\nFailedToStartStream.NoRTMPURLInConfig=\"設定不包含串流目標 RTMP(S) URL\"\nFailedToStartStream.FallbackToDefault=\"使用 %1 啟動直播失敗；您是否想要嘗試使用單一編碼設置重新嘗試？\"\nFailedToStartStream.ConfigRequestFailed=\"無法從 %1 獲取配置<br><br>HTTP 錯誤：%2\"\nFailedToStartStream.WarningUnknownStatus=\"已收到未知的狀態值「%1」\"\nFailedToStartStream.WarningRetryNonMultitrackVideo=\"\\n<br><br>\\n您是否想繼續不使用 %1 進行直播？\"\nFailedToStartStream.WarningRetry=\"\\n<br><br>\\n您是否想繼續直播？\"\nFailedToStartStream.MissingEncoderConfigs=\"啟動直播配置中未包含編碼器配置\"\nFailedToStartStream.InvalidEncoderConfig=\"啟動直播配置包括無效的編碼設定\"\nFailedToStartStream.StatusMissingHTML=\"啟動直播請求返回了一個未指定的錯誤\"\nFailedToStartStream.NoConfigSupplied=\"缺少配置\"\nMultitrackVideo.Info=\"%1 會對多種視訊品質進行編碼，以自動最佳化設定，並傳送到 。選擇本選項將會將電腦和軟體設定的資訊傳送給 %2。\"\nMultitrackVideo.IncompatibleSettings.Title=\"不相容的設定\"\nMultitrackVideo.IncompatibleSettings.Text=\"%1 目前不兼容：\\n\\n%2\\n若要繼續使用 %1 進行直播，請禁用不兼容的設置：\\n\\n%3\\n然後再次開始直播。\"\nMultitrackVideo.IncompatibleSettings.DisableAndStartStreaming=\"停用此直播並開始直播\"\nMultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming=\"更新設定並開始直播\"\nMultitrackVideo.IncompatibleSettings.AudioChannels=\"%1 目前不相容 [音效 → 一般 → 頻道] 設置為 '%2'，%3\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsSingle=\"[音效 → 一般 → 頻道] 需要設置為 '%1'\"\nMultitrackVideo.IncompatibleSettings.AudioChannelsMultiple=\"%1 需要為 [音效 → 一般 → 頻道] 設置多種不同的設定\"\nBasic.OpenPluginManager=\"外掛管理器\"\nPluginManager=\"外掛管理器\"\nPluginManager.Restart=\"OBS Studio 需要重新啟動才能套用外掛變更\"\nPluginManager.MissingPlugin=\"[找不到外掛]\"\nPluginManager.Section.Discover=\"瀏覽\"\nPluginManager.Section.Manage=\"已安裝\"\nPluginManager.Section.Updates=\"更新\"\nPluginManager.Section.Manage.Title=\"管理已啟用的外掛\"\n"
  },
  {
    "path": "frontend/data/locale.ini",
    "content": "[an-ES]\nName=Aragonés\n\n[ar-SA]\nName=العربية\n\n[az-AZ]\nName=Azərbaycanca\n\n[be-BY]\nName=Беларуская\n\n[bg-BG]\nName=български\n\n[bn-BD]\nName=বাংলা\n\n[ca-ES]\nName=Català\n\n[cs-CZ]\nName=Čeština\n\n[da-DK]\nName=Dansk\n\n[de-DE]\nName=Deutsch\n\n[el-GR]\nName=Ελληνικά\n\n[en-GB]\nName=English (UK)\n\n[en-US]\nName=English\n\n[es-ES]\nName=Español\n\n[et-EE]\nName=Eesti\n\n[eu-ES]\nName=Euskara\n\n[fa-IR]\nName=فارسی\n\n[fi-FI]\nName=suomi\n\n[fil-PH]\nName=Filipino\n\n[fr-FR]\nName=Français\n\n[gd-GB]\nName=Gàidhlig\n\n[gl-ES]\nName=Galego\n\n[he-IL]\nName=עברית\n\n[hi-IN]\nName=हिन्दी\n\n[hr-HR]\nName=hrvatski\n\n[hu-HU]\nName=Magyar\n\n[hy-AM]\nName=Հայերեն\n\n[id-ID]\nName=Bahasa Indonesia\n\n[it-IT]\nName=Italiano\n\n[ja-JP]\nName=日本語\n\n[ka-GE]\nName=ქართული\n\n[kab-KAB]\nName=Taqbaylit\n\n[kmr-TR]\nName=Kurdî\n\n[ko-KR]\nName=한국어\n\n[lo-LA]\nName=ພາສາລາວ\n\n[ms-MY]\nName=Bahasa Melayu\n\n[nb-NO]\nName=Norsk bokmål\n\n[nl-NL]\nName=Nederlands\n\n[pl-PL]\nName=polski\n\n[pt-BR]\nName=Português brasileiro\n\n[pt-PT]\nName=Português (Portugal)\n\n[ro-RO]\nName=Română\n\n[ru-RU]\nName=Русский\n\n[si-LK]\nName=සිංහල\n\n[sk-SK]\nName=Slovenčina\n\n[sl-SI]\nName=Slovenščina\n\n[sr-CS]\nName=srpski\n\n[sr-SP]\nName=српски\n\n[sv-SE]\nName=Svenska\n\n[th-TH]\nName=ไทย\n\n[tl-PH]\nName=Tagalog\n\n[tr-TR]\nName=Türkçe\n\n[ug-CN]\nName=ئۇيغۇرچە\n\n[uk-UA]\nName=Українська\n\n[vi-VN]\nName=Tiếng Việt\n\n[zh-CN]\nName=简体中文\n\n[zh-TW]\nName=繁體中文\n"
  },
  {
    "path": "frontend/data/striped_line.effect",
    "content": "uniform float4x4 ViewProj;\nuniform float4 color = {1.0, 1.0, 1.0, 1.0};\nuniform float2 size;\nuniform float2 count_inv;\n\nstruct VertInOut {\n\tfloat4 pos : POSITION;\n\tfloat2 uv : TEXCOORD0;\n};\n\nVertInOut VSStripedLine(VertInOut vert_in)\n{\n\tVertInOut vert_out;\n\tvert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);\n\tvert_out.uv = vert_in.uv;\n\treturn vert_out;\n}\n\nfloat4 PSStripedLine(VertInOut vert_in) : TARGET\n{\n\tfloat2 multiplier = floor(fmod(vert_in.uv * size * count_inv, float2(2.0, 2.0)));\n\tif (size.x == 0.0) {\n\t\tmultiplier.x = 1.0;\n\t} else {\n\t\tmultiplier.y = 1.0;\n\t}\n\n\treturn color * multiplier.xxxx * multiplier.yyyy;\n}\n\ntechnique StripedLine\n{\n\tpass\n\t{\n\t\tvertex_shader = VSStripedLine(vert_in);\n\t\tpixel_shader = PSStripedLine(vert_in);\n\t}\n}\n"
  },
  {
    "path": "frontend/data/themes/System.obt",
    "content": "/* Intentionally left blank */\n/* Themes are created using Qt CSS, you can visit */\n/* http://doc.qt.io/qt-5/stylesheet-reference.html and */\n/* http://doc.qt.io/qt-5/stylesheet-examples.html for examples. */\n\n/* OBS will use the theme filename for the settings. */\n/* You can ship images using relative paths in qss. */\n/* Dark Theme is a good place to start if you need */\n/* a template. */\n\n@OBSThemeMeta {\n    name: 'System';\n    id: 'com.obsproject.System';\n    author: 'Warchamp7';\n}\n\n.bg-base {\n    background-color: #DCD9D7;\n}\n\n.text-heading {\n    font-size: 36px;\n    font-weight: bold;\n}\n\n.text-large {\n    font-size: 16px;\n}\n\n.text-bright {\n    color: var(--primary_light);\n}\n\n/* We need to set back the icons, or the preview wont stick. */\n.icon-plus {\n    qproperty-icon: url(:/res/images/plus.svg);\n}\n\n.icon-minus {\n    qproperty-icon: url(:/res/images/minus.svg);\n}\n\n.icon-trash {\n    qproperty-icon: url(:/res/images/trash.svg);\n}\n\n.icon-clear {\n    qproperty-icon: url(:/res/images/entry-clear.svg);\n}\n\n.icon-gear {\n    qproperty-icon: url(:/res/images/settings/general.svg);\n}\n\n.icon-dots-vert {\n    qproperty-icon: url(:/res/images/dots-vert.svg);\n}\n\n.icon-refresh {\n    qproperty-icon: url(:/res/images/refresh.svg);\n}\n\n.icon-cogs {\n    qproperty-icon: url(:/res/images/cogs.svg);\n}\n\n.icon-touch {\n    qproperty-icon: url(:/res/images/interact.svg);\n}\n\n.icon-up {\n    qproperty-icon: url(:/res/images/up.svg);\n}\n\n.icon-down {\n    qproperty-icon: url(:/res/images/down.svg);\n}\n\n.icon-filter {\n    qproperty-icon: url(:/res/images/filter.svg);\n}\n\n.icon-revert {\n    qproperty-icon: url(:res/images/revert.svg);\n}\n\n.indicator-mute {\n    outline: none;\n}\n\n.indicator-mute::indicator:checked {\n    image: url(:/res/images/mute.svg);\n}\n\n.indicator-mute::indicator:indeterminate {\n    image: url(:/res/images/unassigned.svg);\n}\n\n.indicator-mute::indicator:unchecked {\n    image: url(:/settings/images/settings/audio.svg);\n}\n\n.indicator-expand {\n    background: transparent;\n    outline: none;\n}\n\n.indicator-expand::indicator {\n    width: 10px;\n    height: 10px;\n}\n\n.indicator-expand::indicator:checked {\n    image: url(:/res/images/expand.svg);\n}\n\n.indicator-expand::indicator:unchecked {\n    image: url(:/res/images/collapse.svg);\n}\n\n/* Volume Control */\n\nVolumeMeter {\n    qproperty-backgroundNominalColor: rgb(15, 100, 15);\n    qproperty-backgroundWarningColor: rgb(100, 100, 15);\n    qproperty-backgroundErrorColor: rgb(100, 15, 15);\n    qproperty-foregroundNominalColor: rgb(50, 200, 50);\n    qproperty-foregroundWarningColor: rgb(255, 200, 50);\n    qproperty-foregroundErrorColor: rgb(200, 50, 50);\n    qproperty-magnitudeColor: rgb(0, 0, 0);\n    qproperty-majorTickColor: rgb(0, 0, 0);\n    qproperty-minorTickColor: rgb(50, 50, 50);\n    qproperty-meterThickness: 3;\n\n    /* The meter scale numbers normally use your QWidget font, with size    */\n    /* multiplied by meterFontScaling to get a proportionally smaller font. */\n    /* To use a unique font for the numbers, specify font-family and/or     */\n    /* font-size here, and set meterFontScaling to 1.0.                     */\n    qproperty-meterFontScaling: 0.7;\n}\n\n\n/* Label warning/error */\n\n.text-warning {\n    color: rgb(192, 128, 0);\n}\n\n.text-danger {\n    color: rgb(192, 0, 0);\n}\n\n.text-success {\n    color: rgb(0, 128, 0);\n}\n\n/* macOS Separator Fix */\nQMainWindow::separator {\n    background: transparent;\n    width: 4px;\n    height: 4px;\n}\n\n/* Preview background color */\n\nOBSQTDisplay {\n    qproperty-displayBackgroundColor: rgb(76, 76, 76);\n}\n\n/* Preview/Program labels */\n\n.label-preview-title {\n    font-size: 18px;\n    font-weight: bold;\n    color: rgb(122,121,122);\n}\n\n/* Settings Icons */\n\nOBSBasicSettings {\n    qproperty-generalIcon: url(:settings/images/settings/general.svg);\n    qproperty-appearanceIcon: url(:settings/images/settings/appearance.svg);\n    qproperty-streamIcon: url(:settings/images/settings/stream.svg);\n    qproperty-outputIcon: url(:settings/images/settings/output.svg);\n    qproperty-audioIcon: url(:settings/images/settings/audio.svg);\n    qproperty-videoIcon: url(:settings/images/settings/video.svg);\n    qproperty-hotkeysIcon: url(:settings/images/settings/hotkeys.svg);\n    qproperty-accessibilityIcon: url(:settings/images/settings/accessibility.svg);\n    qproperty-advancedIcon: url(:settings/images/settings/advanced.svg);\n}\n\nOBSBasicSettings QListWidget::item {\n    padding-top: 5px;\n    padding-bottom: 5px;\n}\n\n.checkbox-icon {\n    outline: none;\n    background: transparent;\n}\n\n/* Locked CheckBox */\n\n.indicator-lock::indicator:checked {\n    image: url(:res/images/locked.svg);\n}\n\n.indicator-lock::indicator:unchecked {\n    image: url(:res/images/unlocked.svg);\n}\n\n/* Visibility CheckBox */\n\n.indicator-visibility::indicator:checked {\n    image: url(:res/images/visible.svg);\n}\n\n.indicator-visibility::indicator:unchecked {\n    image: url(:res/images/invisible.svg);\n}\n\nOBSMissingFiles {\n    qproperty-warningIcon: url(:res/images/alert.svg);\n}\n\n/* Source Icons */\n\nOBSBasic {\n    qproperty-imageIcon: url(:res/images/sources/image.svg);\n    qproperty-colorIcon: url(:res/images/sources/brush.svg);\n    qproperty-slideshowIcon: url(:res/images/sources/slideshow.svg);\n    qproperty-audioInputIcon: url(:res/images/sources/microphone.svg);\n    qproperty-audioOutputIcon: url(:settings/images/settings/audio.svg);\n    qproperty-desktopCapIcon: url(:settings/images/settings/video.svg);\n    qproperty-windowCapIcon: url(:res/images/sources/window.svg);\n    qproperty-gameCapIcon: url(:res/images/sources/gamepad.svg);\n    qproperty-cameraIcon: url(:res/images/sources/camera.svg);\n    qproperty-textIcon: url(:res/images/sources/text.svg);\n    qproperty-mediaIcon: url(:res/images/sources/media.svg);\n    qproperty-browserIcon: url(:res/images/sources/globe.svg);\n    qproperty-groupIcon: url(:res/images/sources/group.svg);\n    qproperty-sceneIcon: url(:res/images/sources/scene.svg);\n    qproperty-defaultIcon: url(:res/images/sources/default.svg);\n    qproperty-audioProcessOutputIcon: url(:res/images/sources/windowaudio.svg);\n}\n\n/* Scene Tree */\n\nSceneTree {\n    qproperty-gridItemWidth: 150;\n    qproperty-gridItemHeight: 24;\n}\n\n/* Save icon */\n\n.icon-save {\n    qproperty-icon: url(:res/images/save.svg);\n}\n\n/* Studio Mode T-Bar */\n\n.slider-tbar {\n    height: 24px;\n}\n\n.slider-tbar::groove:horizontal {\n    border: 1px solid #4c4c4c;\n    height: 5px;\n    background: #DCD9D7;\n}\n\n.slider-tbar::sub-page:horizontal {\n    background: #DCD9D7;\n    border: 1px solid #4c4c4c;\n}\n\n.slider-tbar::handle:horizontal {\n    background-color: #4c4c4c;\n    width: 12px;\n    height: 24px;\n    margin: -24px 0px;\n}\n\n/* Source Context */\n#contextContainer QPushButton {\n    padding: 3px 10px;\n}\n\n#contextContainer QPushButton#sourcePropertiesButton {\n    qproperty-icon: url(:/settings/images/settings/general.svg);\n}\n\n#contextContainer QPushButton#sourceFiltersButton {\n    qproperty-icon: url(:/res/images/filter.svg);\n}\n\n#contextContainer QPushButton#sourceInteractButton {\n    qproperty-icon: url(:/res/images/interact.svg);\n}\n\n/* Media icons */\n\n.icon-media-play {\n    qproperty-icon: url(:res/images/media/media_play.svg);\n}\n\n.icon-media-pause {\n    qproperty-icon: url(:res/images/media/media_pause.svg);\n}\n\n.icon-media-restart {\n    qproperty-icon: url(:res/images/media/media_restart.svg);\n}\n\n.icon-media-stop {\n    qproperty-icon: url(:res/images/media/media_stop.svg);\n}\n\n.icon-media-next {\n    qproperty-icon: url(:res/images/media/media_next.svg);\n}\n\n.icon-media-prev {\n    qproperty-icon: url(:res/images/media/media_previous.svg);\n}\n\n/* YouTube Integration */\nOBSYoutubeActions {\n    qproperty-thumbPlaceholder: url(:res/images/sources/image.svg);\n}\n\n#ytEventList QLabel {\n    background-color: #e1e1e1;\n    border: 1px solid #ddd;\n}\n\n#ytEventList QLabel:hover {\n    background-color: #eee;\n    border: 1px solid #777;\n}\n\n#ytEventList .row-selected {\n    background-color: #ccc;\n    border: 1px solid #444;\n}\n\n/* Calendar Widget */\nQCalendarWidget QToolButton {\n    padding: 0px 12px;\n}\n\n#qt_calendar_monthbutton::menu-indicator {\n    image: url(:/res/images/down.svg);\n    subcontrol-position: right;\n    padding-top: 2px;\n    padding-right: 2px;\n    height: 8px;\n    width: 8px;\n}\n\n#qt_calendar_monthbutton::menu-indicator:hover {\n    image: url(:/res/images/down.svg);\n    color: red;\n}\n\nQCalendarWidget #qt_calendar_prevmonth {\n    padding: 2px;\n    qproperty-icon: url(:/res/images/left.svg);\n    icon-size: 16px;\n}\n\nQCalendarWidget #qt_calendar_nextmonth {\n    padding: 2px;\n    qproperty-icon: url(:/res/images/right.svg);\n    icon-size: 16px;\n}\n\n/* Status Bar */\nStatusBarWidget > QFrame {\n    padding: 0px 12px 8px;\n}\n\nidian--ToggleSwitch {\n    qproperty-handle: rgb(255, 255, 255);\n    qproperty-backgroundActive: #284cb8;\n    qproperty-backgroundInactive: #3c404b;\n}\n\nidian--Group {\n    border-radius: 4px;\n    font-weight: bold;\n}\n\nidian--Group > QLabel.title {\n    font-weight: bold;\n}\n\nidian--Group > QLabel.subtitle {\n    color: palette(button-text);\n}\n\nidian--PropertiesList {\n    background: palette(base);\n    border-radius: 4px;\n    border-width: 0px;\n    padding: 0px;\n    margin: 0px;\n}\n\nidian--GenericRow {\n    margin: 0;\n    padding: 0;\n    border-left: 3px solid transparent;\n    min-height: 48px;\n    background: transparent;\n}\n\nidian--GenericRow:hover {\n    background: palette(highlight);\n    border-left: 0;\n}\n\nidian--GenericRow[last=\"true\"]:hover {\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n}\n\nidian--GenericRow[first=\"true\"]:hover {\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\nidian--GenericRow > QLabel {\n    font-weight: 500;\n}\n\nidian--GenericRow > QLabel.subtitle {\n    font-size: 9pt;\n    color: palette(button-text);\n}\n\nidian--GenericRow QComboBox,\nidian--GenericRow QPushButton {\n    margin: 0;\n}\n\nidian--GenericRow QComboBox,\nidian--GenericRow QComboBox:hover,\nidian--GenericRow QComboBox:selected,\nidian--GenericRow QComboBox::on {\n    background: transparent;\n}\n\nidian--GenericRow QComboBox::drop-down {\n    border: none;\n}\n\nidian--GenericRow QComboBox::down-arrow {\n    image: url(./Light/collapse.svg);\n}\n\nidian--PropertiesListSpacer {\n    max-height: 1px;\n    min-height: 1px;\n    background-color: palette(midlight);\n}\n\nidian--CollapsibleRow {\n    margin: 0;\n    padding: 0;\n    border: none;\n}\n\nidian--CollapsibleRow idian--PropertiesList {\n    border-radius: 0;\n    background: palette(mid);\n}\n\nIdianPlayground QVBoxLayout {\n    background: palette(midlight);\n    border-radius: 4px;\n    padding-top: 32px;\n    padding-bottom: 8px;\n    font-weight: bold;\n    margin-bottom: 6px;\n}\n"
  },
  {
    "path": "frontend/data/themes/Yami.obt",
    "content": "@OBSThemeMeta {\n    name: 'Yami';\n    id: 'com.obsproject.Yami';\n    author: 'Warchamp7';\n    dark: 'true';\n}\n\n@OBSThemeVars {\n    /* OBS Color Palette */\n    --blue1: #718CDC;\n    --blue2: #476BD7;\n    --blue3: #284CB8;\n    --blue4: #213E97;\n    --blue5: #1A3278;\n    --blue6: #162A64;\n\n    --red1: #E85E75;\n    --red2: #E33B57;\n    --red3: #C01C37;\n    --red4: #A1172E;\n    --red5: #7D1224;\n    --red6: #590D1A;\n\n    --pink1: #E5619A;\n    --pink2: #E03E84;\n    --pink3: #C11F65;\n    --pink4: #9E1A53;\n    --pink5: #7B1441;\n    --pink6: #580E2E;\n\n    --teal1: #3DBEF5;\n    --teal2: #16B1F3;\n    --teal3: #0981B4;\n    --teal4: #086F9B;\n    --teal5: #065374;\n    --teal6: #04374E;\n\n    --purple1: #997FDC;\n    --purple2: #805FD3;\n    --purple3: #5B34BF;\n    --purple4: #4D2CA0;\n    --purple5: #3D2380;\n    --purple6: #2E1A60;\n\n    --green1: #59D966;\n    --green2: #37D247;\n    --green3: #25A231;\n    --green4: #1E8528;\n    --green5: #17641E;\n    --green6: #0F4313;\n\n    --yellow1: #EABC48;\n    --yellow2: #E5AF24;\n    --yellow3: #B88A16;\n    --yellow4: #926E11;\n    --yellow5: #6E520D;\n    --yellow6: #493709;\n\n    --grey1: #5B6273;\n    --grey2: #4E5566;\n    --grey3: #464B59;\n    --grey4: #3C404D;\n    --grey5: #323540;\n    --grey6: #272A33;\n    --grey7: #1D1F26;\n    --grey8: #13141A;\n\n    --white1: #FFFFFF;\n    --white2: #EBEBEB;\n    --white3: #D6D6D6;\n    --white4: #C2C2C2;\n    --white5: #ADADAD;\n\n    --black1: #0A0A0A;\n    --black2: #1F1F1F;\n    --black3: #333333;\n    --black4: #474747;\n    --black5: #5C5C5C;\n\n    /* Base Theme Colors */\n    --bg_window: var(--grey7);\n    --bg_base: var(--grey6);\n    --bg_preview: var(--grey8);\n\n    --primary: var(--blue3);\n    --primary_light: var(--blue2);\n    --primary_lighter: var(--blue1);\n    --primary_dark: var(--blue4);\n    --primary_darker: var(--blue5);\n\n    --warning: var(--yellow3);\n    --danger: var(--red3);\n\n    --text: var(--white1);\n    --text_light: rgb(214, 214, 214);\n    --text_muted: rgb(153, 153, 153);\n\n    --text_disabled: var(--text_muted);\n    --text_inactive: rgb(255, 254, 255);\n\n    /* Layout */\n    /* Configurable Values */\n    --font_base_value: var(--obsFontScale);\n    --padding_base_value: var(--obsPadding);\n    --spacing_base_value: calc(2 + calc(var(--obsPadding) / 2));\n\n    --highlight_width: 1px;\n    --highlight_color: var(--primary_lighter);\n\n    /* TODO: Better Accessibility focus state */\n    /* TODO: Move Accessibility Colors to Theme config system */\n    --border_highlight: \"transparent\";\n\n    /* OS Fixes */\n    --os_mac_font_base_value: calc(1.2 * var(--obsFontScale));\n\n    --font_base: calc(1pt * var(--font_base_value));\n    --font_small: max(7pt, calc(0.8pt * var(--font_base_value)));\n    --font_xsmall: max(6.75pt, calc(0.65pt * var(--font_base_value)));\n    --font_large: calc(1.1pt * var(--font_base_value));\n    --font_xlarge: calc(1.5pt * var(--font_base_value));\n\n    --font_heading: calc(2.5pt * var(--font_base_value));\n\n    --icon_base: calc(calc(max(2, var(--obsPadding)) * 1px) + 12px);\n    --icon_small: calc(4px + var(--font_base_value));\n\n    --spacing_base: min(max(1px, calc(0.4 * var(--spacing_base_value))), 2px);\n    --spacing_large: min(max(2px, calc(1px * var(--spacing_base_value))), 4px);\n    --spacing_small: max(1px, calc(0.25px * var(--spacing_base_value)));\n    --spacing_title: 4px;\n\n    --padding_base: calc(0.5px * var(--padding_base_value));\n    --padding_large: min(max(1px, calc(1px * var(--padding_base_value))), 5px);\n    --padding_xlarge: min(max(2px, calc(1.75px * var(--padding_base_value))), 10px);\n    --padding_small: max(0px, calc(0.25px * var(--padding_base_value)));\n\n    --padding_container: max(4px, var(--padding_base));\n    --padding_wide: min(calc(12px + max(var(--padding_base_value), 4)), 24px);\n    --padding_menu: calc(4px + calc(2 * var(--padding_base_value)));\n    --padding_menubar: max(6px, calc(2px + calc(1.25 * var(--padding_base_value))));\n\n    --padding_base_border: calc(var(--padding_base) + 1px);\n\n    --spinbox_button_height: calc(var(--input_height_half) - 1px);\n\n    --volume_slider: calc(calc(4px + var(--font_base_value)) / 4);\n    --volume_slider_box: calc(var(--volume_slider) * 4);\n    --volume_slider_label: calc(var(--volume_slider_box) * 2);\n\n    --scrollbar_size: 12px;\n    --settings_scrollbar_size: calc(var(--scrollbar_size) + 9px);\n    --scrollbar_handle: var(--grey4);\n\n    --scrollbar_bg: var(--grey6);\n    --scrollbar_hover: var(--grey3);\n    --scrollbar_down: var(--grey2);\n\n    --scrollbar_border: var(--grey2);\n\n    /* Inputs / Controls */\n    --border_color: var(--grey4);\n\n    --border_radius: 4px;\n    --border_radius_small: 2px;\n    --border_radius_large: 6px;\n\n    --input_font_scale: calc(var(--font_base_value) * 2.2);\n    --input_font_padding: calc(var(--padding_base_value) * 2);\n\n    --input_height_base: max(calc(var(--input_font_scale) + var(--input_font_padding)), 24);\n    --input_padding: calc(2px + var(--padding_base));\n    --input_text_padding: max(calc(6px + var(--padding_base)), 8px);\n    --input_height: calc(var(--input_height_base) - calc(var(--input_padding) * 2px));\n    --input_height_half: calc(var(--input_height_base) / 2);\n\n    --input_bg: var(--grey4);\n    --input_bg_hover: var(--grey7);\n    --input_bg_focus: var(--grey7);\n\n    --list_item_bg_selected: var(--primary);\n    --list_item_bg_hover: var(--primary_light);\n\n    --input_border: var(--grey1);\n    --input_border_width: 1px;\n    --input_border_hover: var(--grey1);\n    --input_border_focus: var(--primary);\n\n    --spacing_input: var(--spacing_base);\n\n    --button_bg: var(--input_bg);\n    --button_bg_hover: var(--grey3);\n    --button_bg_down: var(--grey7);\n    --button_bg_disabled: var(--grey6);\n\n    --button_border_width: var(--input_border_width);\n    --button_border: var(--button_bg);\n    --button_border_hover: var(--grey1);\n    --button_border_focus: var(--grey1);\n\n    --tab_bg: var(--button_bg_disabled);\n    --tab_bg_hover: var(--button_bg_hover);\n    --tab_bg_down: var(--primary);\n    --tab_bg_disabled: var(--button_bg_disabled);\n\n    --tab_border: var(--border_color);\n    --tab_border_hover: var(--button_border_hover);\n    --tab_border_focus: var(--primary_lighter);\n    --tab_border_selected: var(--primary);\n\n    --tab_padding_base: calc(5px + var(--padding_base));\n    --tab_padding_large: calc(9px + var(--padding_base));\n\n    --preview_scale_width: calc(calc(var(--input_text_padding) * 3.5) * calc(var(--font_base_value) / 10));\n\n    --separator_hover: var(--white1);\n\n    --action_row_base: calc(var(--input_height_base) * 0.75);\n    --action_row_height: calc(var(--action_row_base) + calc(var(--action_row_padding) * 2px));\n    --action_row_border: 3px;\n    --action_row_input_width: calc(var(--action_row_base) * 4);\n    --action_row_collapse: calc(var(--action_row_base) + var(--padding_large));\n    --action_row_collapse_radius: calc(var(--action_row_collapse) / 2);\n    --action_row_padding: calc(var(--padding_large) * 1.5);\n    --action_row_padding_x: calc(var(--action_row_padding) * 2);\n    --action_row_padding_nested: calc(var(--action_row_padding_x) * 1.5);\n\n    --toggle_border: 1;\n    --toggle_margin: 3;\n    --toggle_width: calc(var(--action_row_base) * 1.8);\n    --toggle_height: calc(var(--action_row_base) * 0.9);\n    --toggle_handle: calc(calc(calc(var(--toggle_height) * 0.9) - calc(var(--toggle_border) * 2)) - var(--toggle_margin));\n    --toggle_radius: calc(var(--toggle_height) / 2);\n\n    /* Qt Palette variables can be set with the \"palette_\" prefix */\n    --palette_window: var(--bg_window);\n    --palette_windowText: var(--text);\n    --palette_base: var(--bg_base);\n\n    --palette_light: var(--grey2);\n    --palette_mid: var(--grey7);\n    --palette_dark: var(--grey6);\n\n    --palette_highlight: var(--primary);\n    --palette_highlightedText: var(--text);\n\n    --palette_text: var(--text);\n    --palette_link: var(--blue2);\n    --palette_linkVisited: var(--blue2);\n\n    --palette_button: var(--button_bg);\n    --palette_buttonText: var(--text);\n\n    /* They can be selectively set for palette groups by appending those as well */\n    --palette_text_active: var(--text);\n    --palette_text_disabled: var(--text_disabled);\n    --palette_text_inactive: var(--text_inactive);\n\n    /*\n    * Variables calculated at runtime (after all themes have been composed).\n    *\n    * Support standard add, sub, mul, div operations.\n    * Also supports nested calls (but keep it reasonable).\n    *\n    * Note: When using two operands that have a type (e.g. \"px\") the type must match!\n    *       If only one operand has a type it'll be used for the result.\n    * Note 2: Cannot be !editable\n    * Note 3: Operands and operator MUST be separated by whitespace\n    */\n}\n\n/* --------------------- */\n/* General Styling Hints */\n\n/* Backgrounds */\n\n.bg_window {\n    background-color: var(--bg_window);\n}\n\n.bg-base {\n    background-color: var(--bg_base);\n}\n\n.text-title {\n    font-size: var(--font_large);\n    font-weight: bold;\n}\n\n.text-small {\n    font-size: var(--font_small);\n}\n\n.text-tiny {\n    font-size: var(--font_xsmall);\n}\n\n.text-heading {\n    font-size: var(--font_heading);\n    font-weight: bold;\n}\n\n.text-large {\n    font-size: var(--font_large);\n}\n\n.text-bold {\n    font-weight: bold;\n}\n\n.text-italic {\n    font-style: italic;\n}\n\n.text-bright {\n    color: var(--primary_light);\n}\n\n.text-muted {\n    color: var(--text_muted);\n}\n\n.text-warning {\n    color: var(--warning);\n}\n\n.text-danger {\n    color: var(--danger);\n}\n\n.text-success {\n    color: var(--green3);\n}\n\n.frame-notice {\n    background: var(--grey8);\n    border: 1px solid var(--grey6);\n    border-radius: var(--border_radius);\n    padding: var(--padding_xlarge) var(--padding_large);\n}\n\n.frame-notice QLabel {\n    padding: var(--padding_large) 0px;\n}\n\n.dialog-container {\n    padding: var(--padding_large) var(--padding_xlarge);\n}\n\n.dialog-frame {\n    background-color: var(--grey6);\n    border-radius: var(--border_radius);\n    border: 1px solid var(--border_color);\n    margin: var(--spacing_base);\n}\n\n.dialog-frame > QWidget {\n    margin: var(--spacing_base) 0;\n}\n\n.subtitle {\n    font-size: var(--font_small);\n    font-weight: bold;\n    color: var(--text_muted);\n}\n\n.button-primary {\n    background-color: var(--primary_dark);\n    border-color: var(--primary);\n    outline: none;\n}\n\n.button-primary:hover,\n.button-primary:focus {\n    background-color: var(--primary);\n    border-color: var(--white3);\n}\n\n.button-primary:hover {\n    border-color: var(--primary_light);\n}\n\n.toolbar-button {\n    background: transparent;\n    font-weight: bold;\n    padding: 0 var(--padding_xlarge);\n    margin: var(--spacing_small) var(--spacing_small) 0px;\n    border: var(--highlight_width) solid transparent;\n    border-radius: var(--border_radius);\n}\n\n.margin {\n    margin: var(--spacing_base);\n}\n\n.margin-x {\n    margin-left: var(--spacing_base);\n    margin-right: var(--spacing_base);\n}\n\n.margin-left {\n    margin-left: var(--spacing_base);\n}\n\n.margin-right {\n    margin-right: var(--spacing_base);\n}\n\n.margin-y {\n    margin-top: var(--spacing_base);\n    margin-bottom: var(--spacing_base);\n}\n\n.margin-top {\n    margin-top: var(--spacing_base);\n}\n\n.margin-bottom {\n    margin-bottom: var(--spacing_base);\n}\n\n/* Icon Overrides */\n\n.icon-plus {\n    qproperty-icon: url(theme:Dark/plus.svg);\n}\n\n.icon-minus {\n    qproperty-icon: url(theme:Dark/minus.svg);\n}\n\n.icon-trash {\n    qproperty-icon: url(theme:Dark/trash.svg);\n}\n\n.icon-clear {\n    qproperty-icon: url(theme:Dark/entry-clear.svg);\n}\n\n.icon-gear {\n    qproperty-icon: url(theme:Dark/settings/general.svg);\n}\n\n.icon-dots-vert {\n    qproperty-icon: url(theme:Dark/dots-vert.svg);\n}\n\n.icon-refresh {\n    qproperty-icon: url(theme:Dark/refresh.svg);\n}\n\n.icon-cogs {\n    qproperty-icon: url(theme:Dark/cogs.svg);\n}\n\n.icon-touch {\n    qproperty-icon: url(theme:Dark/interact.svg);\n}\n\n.icon-up {\n    qproperty-icon: url(theme:Dark/up.svg);\n}\n\n.icon-down {\n    qproperty-icon: url(theme:Dark/down.svg);\n}\n\n.icon-pause {\n    qproperty-icon: url(theme:Dark/media-pause.svg);\n}\n\n.icon-filter {\n    qproperty-icon: url(theme:Dark/filter.svg);\n}\n\n.icon-revert {\n    qproperty-icon: url(theme:Dark/revert.svg);\n}\n\n.icon-save {\n    qproperty-icon: url(theme:Dark/save.svg);\n}\n\n.icon-close {\n    qproperty-icon: url(theme:Dark/close.svg);\n}\n\n.icon-pin {\n    qproperty-icon: url(theme:Dark/pin.svg);\n}\n\n.icon-layout-vertical {\n    qproperty-icon: url(theme:Dark/layout-vertical.svg);\n}\n\n.icon-layout-horizontal {\n    qproperty-icon: url(theme:Dark/layout-horizontal.svg);\n}\n\n/* Media icons */\n\n.icon-media-play {\n    qproperty-icon: url(theme:Dark/media/media_play.svg);\n}\n\n.icon-media-pause {\n    qproperty-icon: url(theme:Dark/media/media_pause.svg);\n}\n\n.icon-media-restart {\n    qproperty-icon: url(theme:Dark/media/media_restart.svg);\n}\n\n.icon-media-stop {\n    qproperty-icon: url(theme:Dark/media/media_stop.svg);\n}\n\n.icon-media-next {\n    qproperty-icon: url(theme:Dark/media/media_next.svg);\n}\n\n.icon-media-prev {\n    qproperty-icon: url(theme:Dark/media/media_previous.svg);\n}\n\n/* Default widget style, we override only what is needed. */\n\nQWidget {\n    alternate-background-color: var(--bg_base);\n    color: var(--text);\n    selection-background-color: var(--primary);\n    selection-color: var(--text);\n    font-size: var(--font_base);\n    font-family: 'Open Sans', '.AppleSystemUIFont', Helvetica, Arial, 'MS Shell Dlg', sans-serif;\n}\n\nQWidget:disabled {\n    color: var(--text_disabled);\n}\n\n/* Container windows */\n\nQDialog,\nQMainWindow,\nQStatusBar,\nQMenuBar,\nQMenu {\n    background-color: var(--bg_window);\n}\n\n/* macOS Separator Fix */\n\nQMainWindow::separator {\n    background: transparent;\n    width: var(--spacing_large);\n    height: var(--spacing_large);\n    margin: 0px;\n}\n\nQMainWindow::separator:hover {\n    border: 1px solid var(--separator_hover);\n    margin: 1px;\n}\n\n/* General Widgets */\n\nQLabel,\nQGroupBox,\nQCheckBox {\n    background: transparent;\n}\n\nQComboBox,\nQCheckBox,\nQPushButton,\nQSpinBox,\nQDoubleSpinBox {\n    margin-top: var(--spacing_input);\n    margin-bottom: var(--spacing_input);\n}\n\nQListView QWidget,\nQListWidget QWidget {\n    margin-top: 0;\n    margin-bottom: 0;\n}\n\n* [frameShape=\"1\"],\n* [frameShape=\"2\"],\n* [frameShape=\"3\"],\n* [frameShape=\"4\"],\n* [frameShape=\"5\"],\n* [frameShape=\"6\"] {\n    border: 1px solid var(--bg_base);\n}\n\nQCheckBox,\nQGroupBox,\nQMenuBar,\nQRadioButton {\n    spacing: var(--spacing_small);\n}\n\n/* Misc */\n\nQAbstractItemView {\n    background-color: var(--bg_base);\n}\n\nQToolTip {\n    background-color: var(--bg_base);\n    color: var(--text);\n    border: none;\n}\n\nQProgressBar {\n    text-align: center;\n    border-radius: 4px;\n    border-top: 2px solid transparent;\n}\n\nQProgressBar::chunk {\n    background: var(--blue3);\n    border-top: 1px solid var(--blue2);\n    border-bottom: 2px solid var(--blue4);\n    border-radius: var(--border_radius_small);\n}\n\n/* Context Menu */\n\nQMenu::indicator {\n    left: var(--padding_large);\n}\n\nQMenu::icon {\n    left: var(--padding_xlarge);\n}\n\nQMenu::separator {\n    background: var(--border_color);\n    height: 1px;\n    margin: var(--spacing_base) var(--spacing_large);\n}\n\nQMenu::item:disabled {\n    color: var(--text_disabled);\n    background: transparent;\n}\n\nQMenu::right-arrow {\n    image: url(theme:Dark/expand.svg);\n}\n\n/* Top Menu Bar Items */\nQMenuBar::item {\n    background-color: transparent;\n    padding: var(--padding_large) var(--padding_menubar);\n}\n\nQMenuBar::item:selected {\n    background: var(--primary);\n}\n\n/* Toolbars */\n\nQToolBar::separator {\n    background: var(--border_color);\n    width: 1px;\n    margin: 0px var(--spacing_base) 0px;\n}\n\n/* Item Lists */\n\nQListWidget {\n    border-radius: var(--border_radius);\n}\n\nQListWidget::item {\n    color: var(--text);\n}\n\nQListView,\nQListWidget,\nQMenu {\n    padding: var(--spacing_base);\n    outline: none;\n}\n\nQMenu {\n    border: 1px solid var(--border_color);\n}\n\nQListWidget::item,\nSourceTreeItem {\n    padding: var(--padding_large) var(--padding_large);\n}\n\nQMenu::item,\nQMenu > QWidget {\n    padding: var(--padding_large) var(--padding_menu);\n    padding-right: 20px;\n}\n\nQMenu > QWidget {\n    outline: none;\n}\n\nQMenu::item,\nQMenu > QWidget,\nQListView::item,\nQListWidget::item {\n    border-radius: var(--border_radius);\n    color: var(--text);\n    border: 1px solid transparent;\n}\n\nSourceTreeItem {\n    border-radius: var(--border_radius);\n    color: var(--text);\n}\n\n/* Temporary fix for plugins affected by fix in #11555 */\nQListView::item,\nQListWidget::item,\nSourceTreeItem {\n    min-height: var(--padding_menu);\n}\n\nSourceTreeItem .checkbox-icon {\n    margin-right: 0;\n    margin-left: var(--spacing_large);\n}\n\nSourceTree .indicator-expand {\n    margin-right: var(--spacing_large);\n    margin-left: 0;\n}\n\nQMenu::item:selected,\nQMenu > QWidget:selected,\nQListView::item:selected,\nQListWidget::item:selected {\n    background-color: var(--primary);\n    border-color: var(--primary_light);\n}\n\nQMenu::item:hover,\nQListView::item:hover,\nQListWidget::item:hover {\n    background: var(--grey4);\n    border: 1px solid var(--grey3);\n}\n\nQMenu::item:focus,\nQListView::item:focus,\nQListWidget::item:focus,\nQMenu::item:selected:focus,\nQListView::item:selected:focus,\nQListWidget::item:selected:focus {\n    border: 1px solid var(--white3);\n}\n\nQMenu::item:selected:hover,\nQListView::item:selected:hover,\nQListWidget::item:selected:hover {\n    background: var(--primary_light);\n    border: 1px solid var(--primary_lighter);\n    color: var(--text);\n}\n\nQListView::item:disabled,\nQListView::item:disabled:hover,\nQListWidget::item:disabled,\nQListWidget::item:disabled:hover {\n    background: transparent;\n    color: var(--text_disabled);\n}\n\nQListView QLineEdit,\nQListWidget QLineEdit {\n    padding: 0;\n    padding-bottom: 1px;\n    margin: 0;\n    border: var(--input_border_width) solid var(--white1);\n    border-radius: var(--border_radius);\n}\n\nQListView QLineEdit:focus,\nQListWidget QLineEdit:focus {\n    border: 1px solid var(--grey1);\n}\n\n/* Settings QList */\n\nOBSBasicSettings QScrollBar:vertical {\n    width: var(--settings_scrollbar_size);\n    margin-left: 9px;\n}\n\nOBSBasicSettings QScrollBar:horizontal {\n    height: var(--settings_scrollbar_size);\n}\n\n/* Settings properties view */\nOBSBasicSettings #PropertiesContainer {\n    background-color: var(--bg_base);\n}\n\n/* Dock Widget */\nOBSDock > QWidget {\n    background: var(--bg_base);\n    border-bottom-left-radius: var(--border_radius);\n    border-bottom-right-radius: var(--border_radius);\n    border: 1px solid var(--border_color);\n    border-top: none;\n}\n\nOBSDock QToolBar {\n    padding: 1px;\n    margin: 0px;\n    height: 32px;\n    max-height: 32px;\n    border-top: 1px solid var(--border_color);\n    spacing: 0;\n}\n\nOBSDock QToolBar QToolButton {\n    background: transparent;\n    border: 1px solid transparent;\n    margin: 1px;\n    margin-top: 2px;\n    padding: var(--padding_large);\n    max-width: var(--icon_small);\n    max-height: var(--icon_small);\n}\n\nOBSDock QToolBar QToolButton:hover {\n    border-color: var(--grey1);\n}\n\nOBSDock QToolBar QToolButton:disabled {\n    background: var(--grey7);\n}\n\n#transitionsFrame {\n    padding: var(--padding_container);\n}\n\nOBSDock QLabel {\n    background: transparent;\n}\n\nQDockWidget {\n    font-size: var(--font_base);\n    font-weight: bold;\n\n    titlebar-close-icon: url(theme:Dark/close.svg);\n    titlebar-normal-icon: url(theme:Dark/popout.svg);\n}\n\nQDockWidget::title {\n    text-align: left;\n    background-color: var(--button_bg);\n    padding: var(--padding_large);\n    border-top-left-radius: var(--border_radius);\n    border-top-right-radius: var(--border_radius);\n}\n\nQDockWidget::close-button,\nQDockWidget::float-button {\n    border: none;\n    border-radius: var(--border_radius);\n    background: transparent;\n    margin-right: 1px;\n    min-width: var(--icon_base);\n    min-height: var(--icon_base);\n    icon-size: var(--icon_base);\n    padding: var(--padding_base);\n}\n\nQDockWidget::close-button:hover,\nQDockWidget::float-button:hover {\n    background: var(--button_bg_hover);\n}\n\nQDockWidget::close-button:pressed,\nQDockWidget::float-button:pressed {\n    background: var(--button_bg_down);\n    padding: 1px -1px -1px 1px;\n}\n\nQAbstractScrollArea {\n    border-radius: var(--border_radius);\n}\n\n/* ScrollAreaContents */\nQScrollArea > QWidget > QWidget {\n    background: transparent;\n}\n\n/* Qt enforces a padding inside its status bar, so we\n * oversize it and use margin to crunch it back down\n */\nOBSBasicStatusBar {\n    margin-top: var(--spacing_large);\n    border-top: 1px solid var(--border_color);\n    background: var(--bg_base);\n}\n\nStatusBarWidget > QFrame {\n    border: 0px solid var(--border_color);\n    border-left-width: 1px;\n    padding: 0px var(--padding_xlarge) var(--padding_small);\n}\n\n/* Group Box */\n\nQGroupBox {\n    background: var(--bg_base);\n    border-radius: var(--border_radius);\n    padding-top: var(--input_height_base);\n    padding-bottom: var(--padding_large);\n    font-weight: bold;\n    margin-bottom: var(--spacing_large);\n}\n\nQGroupBox::title {\n    subcontrol-origin: margin;\n    left: var(--spacing_title);\n    top: var(--spacing_title);\n}\n\n\n/* ScrollBars */\n\nQScrollBar {\n    background-color: var(--scrollbar_bg);\n    margin: 0px;\n    border-radius: var(--border_radius);\n    border: 1px solid var(--grey6);\n}\n\n::corner {\n    background-color: var(--bg_window);\n    border: none;\n}\n\nQScrollBar:vertical {\n    width: var(--scrollbar_size);\n}\n\nQScrollBar::add-line:vertical,\nQScrollBar::sub-line:vertical {\n    border: none;\n    background: none;\n    height: 0px;\n}\n\nQScrollBar::up-arrow:vertical,\nQScrollBar::down-arrow:vertical,\nQScrollBar::add-page:vertical,\nQScrollBar::sub-page:vertical {\n    border: none;\n    background: none;\n    color: none;\n}\n\nQScrollBar:horizontal {\n    height: var(--scrollbar_size);\n}\n\nQScrollBar::add-line:horizontal,\nQScrollBar::sub-line:horizontal {\n    border: none;\n    background: none;\n    width: 0px;\n}\n\nQScrollBar::left-arrow:horizontal,\nQScrollBar::right-arrow:horizontal,\nQScrollBar::add-page:horizontal,\nQScrollBar::sub-page:horizontal {\n    border: none;\n    background: none;\n    color: none;\n}\n\nQScrollBar::handle {\n    background-color: var(--scrollbar_handle);\n    margin: 2px;\n    border-radius: var(--border_radius_small);\n    border: 1px solid var(--scrollbar_handle);\n}\n\nQScrollBar::handle:hover {\n    background-color: var(--scrollbar_hover);\n    border-color: var(--scrollbar_border);\n}\n\nQScrollBar::handle:pressed {\n    background-color: var(--scrollbar_down);\n    border-color: var(--scrollbar_down);\n}\n\nQScrollBar::handle:vertical {\n    min-height: 32px;\n}\n\nQScrollBar::handle:horizontal {\n    min-width: 32px;\n}\n\nQScrollBar::handle:disabled {\n    background: transparent;\n    border-color: transparent;\n}\n\nOBSDock QScrollBar {\n    border-radius: 0;\n}\n\nOBSDock QListWidget::corner {\n    background: transparent;\n    border-top: 1px solid var(--border_color);\n    border-left: 1px solid var(--border_color);\n}\n\nOBSDock QScrollBar:horizontal {\n    border-top: 1px solid var(--border_color);\n}\n\nOBSDock QScrollBar:vertical {\n    border-left: 1px solid var(--border_color);\n}\n\n/* Source Context Bar */\n\n#contextContainer {\n    background-color: var(--bg_base);\n    margin-top: 4px;\n    border-radius: var(--border_radius);\n}\n\n#contextContainer QPushButton {\n    padding-left: 12px;\n    padding-right: 12px;\n}\n\nQPushButton#sourcePropertiesButton {\n    qproperty-icon: url(theme:Dark/settings/general.svg);\n    icon-size: var(--icon_base);\n}\n\nQPushButton#sourceFiltersButton {\n    qproperty-icon: url(theme:Dark/filter.svg);\n    icon-size: var(--icon_base);\n}\n\n/* Scenes and Sources toolbar */\n\nQToolBar {\n    background-color: transparent;\n    border: none;\n    margin: var(--spacing_base) 0px;\n    spacing: var(--spacing_base);\n}\n\nQToolBarExtension {\n    background: var(--button_bg);\n    min-width: 12px;\n    max-width: 12px;\n    padding: 4px 0px;\n    margin-left: 0px;\n\n    qproperty-icon: url(theme:Dark/dots-vert.svg);\n}\n\n\n/* Tab Widget */\n\n/* The tab widget frame */\nQTabWidget::pane {\n    border-top: 4px solid var(--tab_bg);\n}\n\nQTabBar {\n    outline: none;\n}\n\nQTabWidget::tab-bar {\n    alignment: left;\n}\n\nQTabBar QToolButton {\n    background: var(--button_bg);\n    border: none;\n}\n\nQTabBar::tab:top {\n    border-top-left-radius: 4px;\n    border-top-right-radius: 4px;\n}\n\nQTabBar::tab:bottom {\n    border-bottom-left-radius: 4px;\n    border-bottom-right-radius: 4px;\n}\n\nQTabBar::tab {\n    background: var(--tab_bg);\n    color: var(--text);\n    border: none;\n    padding: var(--tab_padding_base) var(--tab_padding_large);\n    min-width: 50px;\n    margin: 1px 0px;\n    margin-right: 2px;\n    border: 1px solid var(--tab_border);\n}\n\nQTabBar::tab:pressed {\n    background: var(--tab_bg_down);\n}\n\nQTabBar::tab:hover {\n    background: var(--tab_bg_hover);\n    border-color: var(--tab_border_hover);\n    color: var(--text);\n}\n\nQTabBar::tab:focus {\n    border-color: var(--tab_border_focus);\n}\n\nQTabBar::tab:selected {\n    background: var(--tab_bg_down);\n    color: var(--text);\n}\n\nQTabBar::tab:top {\n    border-bottom: 0px solid transparent;\n    margin-bottom: 0px;\n}\n\nQTabBar::tab:bottom {\n    border-top: 0px solid transparent;\n    margin-top: 0px;\n}\n\nQTabBar QToolButton {\n    background: var(--button_bg);\n    min-width: 16px;\n    padding: 0px;\n}\n\n/* ComboBox */\n\nQComboBox,\nQDateTimeEdit {\n    background-color: var(--input_bg);\n    border: 1px solid var(--input_bg);\n    border-radius: var(--border_radius);\n    padding: var(--input_padding) var(--input_text_padding);\n    height: var(--input_height);\n}\n\nQComboBox QAbstractItemView {\n    padding: var(--spacing_base) var(--spacing_large);\n    border: 1px solid var(--border_color);\n    outline: none;\n}\n\nQComboBox QAbstractItemView::item {\n    padding: var(--padding_large) var(--padding_xlarge);\n}\n\nQComboBox QAbstractItemView::item:selected,\nQComboBox QAbstractItemView::item:hover {\n    background-color: var(--list_item_bg_selected);\n}\n\nQComboBox:hover,\nQComboBox:focus,\nQDateTimeEdit:hover,\nQDateTimeEdit:selected {\n    border-color: var(--input_border_hover);\n}\n\nQComboBox:on {\n    border-bottom-left-radius: 0px;\n    border-bottom-right-radius: 0px;\n}\n\nQComboBox::drop-down,\nQDateTimeEdit::drop-down {\n    border: none;\n    border-left: 1px solid var(--grey6);\n    width: var(--input_height);\n}\n\nQComboBox::down-arrow,\nQDateTimeEdit::down-arrow {\n    qproperty-alignment: AlignTop;\n    image: url(theme:Dark/collapse.svg);\n    width: 100%;\n}\n\nQComboBox:editable:hover {\n    background-color: var(--input_bg_hover);\n    border-color: var(--input_border_hover);\n}\n\nQComboBox:on,\nQDateTimeEdit:on,\nQComboBox:editable:focus {\n    background-color: var(--input_bg_focus);\n    border-color: var(--input_border_focus);\n}\n\nQComboBox::drop-down:editable,\nQDateTimeEdit::drop-down:editable {\n    border-top-right-radius: 4px;\n    border-bottom-right-radius: 4px;\n}\n\nQComboBox::down-arrow:editable,\nQDateTimeEdit::down-arrow:editable {\n    qproperty-alignment: AlignTop;\n    image: url(theme:Dark/collapse.svg);\n    width: 100%;\n}\n\n/* Textedits etc */\n\nQLineEdit,\nQTextEdit,\nQPlainTextEdit {\n    background-color: var(--input_bg);\n    border: none;\n    border-radius: var(--border_radius);\n    padding: var(--input_padding) var(--input_text_padding);\n    border: 1px solid var(--input_bg);\n    height: var(--input_height);\n}\n\nQLineEdit:hover,\nQTextEdit:hover,\nQPlainTextEdit:hover {\n    background-color: var(--input_bg_hover);\n    border-color: var(--input_border_hover);\n}\n\nQLineEdit:focus,\nQTextEdit:focus,\nQPlainTextEdit:focus {\n    background-color: var(--input_bg_focus);\n    border-color: var(--input_border_focus);\n}\n\nQLineEdit:read-only,\nQLineEdit:read-only:hover,\nQLineEdit:read-only:focus {\n    background-color: transparent;\n    border-color: var(--input_bg);\n}\n\nQTextEdit:!editable,\nQTextEdit:!editable:hover,\nQTextEdit:!editable:focus {\n    background-color: var(--input_bg);\n}\n\n/* Spinbox and doubleSpinbox */\n\nQSpinBox,\nQDoubleSpinBox {\n    background-color: var(--input_bg);\n    border: var(--input_border_width) solid var(--input_bg);\n    border-radius: var(--border_radius);\n    padding: var(--input_padding) var(--input_text_padding);\n    height: var(--input_height);\n    max-height: var(--input_height);\n}\n\nQSpinBox:hover,\nQDoubleSpinBox:hover {\n    background-color: var(--input_bg_hover);\n    border-color: var(--input_border_hover);\n}\n\nQSpinBox:focus,\nQDoubleSpinBox:focus {\n    background-color: var(--input_bg_focus);\n    border-color: var(--input_border_focus);\n}\n\nQSpinBox::up-button,\nQDoubleSpinBox::up-button {\n    subcontrol-origin: padding;\n    /* position at the top right corner */\n    subcontrol-position: top right;\n\n    width: var(--input_height);\n    height: var(--spinbox_button_height);\n    border-left: 1px solid var(--grey6);\n    border-bottom: 1px solid transparent;\n    border-radius: 0px;\n    border-top-right-radius: var(--border_radius_small);\n}\n\nQSpinBox::down-button,\nQDoubleSpinBox::down-button {\n    subcontrol-origin: padding;\n    /* position at the top right corner */\n    subcontrol-position: bottom right;\n\n    width: var(--input_height);\n    height: var(--spinbox_button_height);\n    border-left: 1px solid var(--grey6);\n    border-top: 1px solid var(--grey6);\n    border-radius: 0px;\n    border-bottom-right-radius: var(--border_radius_small);\n}\n\nQSpinBox::up-button:hover,\nQSpinBox::down-button:hover,\nQDoubleSpinBox::up-button:hover,\nQDoubleSpinBox::down-button:hover {\n    background-color: var(--button_bg_hover);\n}\n\nQSpinBox::up-button:pressed,\nQSpinBox::down-button:pressed,\nQDoubleSpinBox::up-button:pressed,\nQDoubleSpinBox::down-button:pressed {\n    background-color: var(--button_bg_down);\n}\n\nQSpinBox::up-button:disabled,\nQSpinBox::up-button:off,\nQSpinBox::down-button:disabled,\nQSpinBox::down-button:off {\n    background-color: var(--button_bg_disabled);\n}\n\nQDoubleSpinBox::up-button:disabled,\nQDoubleSpinBox::up-button:off,\nQDoubleSpinBox::down-button:disabled,\nQDoubleSpinBox::down-button:off {\n    background-color: var(--button_bg_disabled);\n}\n\nQSpinBox::up-arrow,\nQDoubleSpinBox::up-arrow {\n    image: url(theme:Dark/up.svg);\n    width: 100%;\n    margin: 2px;\n}\n\nQSpinBox::down-arrow,\nQDoubleSpinBox::down-arrow {\n    image: url(theme:Dark/down.svg);\n    width: 100%;\n    padding: 2px;\n}\n\n/* Controls Dock */\n#controlsFrame {\n    padding: var(--padding_container);\n}\n\n#controlsFrame QPushButton {\n    margin: var(--spacing_base) var(--spacing_small);\n}\n\n#streamButton,\n#recordButton,\n#replayBufferButton,\n#broadcastButton {\n    padding: var(--padding_large);\n}\n\n#pauseRecordButton,\n#saveReplayButton,\n#virtualCamConfigButton {\n    padding: var(--padding_large) var(--padding_large);\n    width: var(--input_height);\n    max-width: var(--input_height);\n}\n\n/* Primary Control Button Checked Coloring */\n#streamButton:!hover:!pressed.state-active,\n#recordButton:!hover:!pressed.state-active,\n#pauseRecordButton:!hover:!pressed.state-active,\n#replayBufferButton:!hover:!pressed.state-active,\n#virtualCamButton:!hover:!pressed.state-active,\n#modeSwitch:!hover:!pressed.state-active,\n#broadcastButton:!hover:!pressed.state-active {\n    background: var(--primary);\n}\n\n/* Primary Control Button Hover Coloring */\n#streamButton:hover:!pressed.state-active,\n#recordButton:hover:!pressed.state-active,\n#pauseRecordButton:hover:!pressed.state-active,\n#replayBufferButton:!pressed.state-active,\n#virtualCamButton:!pressed.state-active,\n#modeSwitch:hover:!pressed.state-active,\n#broadcastButton:hover:!pressed.state-active {\n    background: var(--primary_light);\n    color: var(--text);\n}\n\n\n/* Buttons */\n\nQPushButton {\n    background-color: var(--button_bg);\n    color: var(--text);\n    border: 1px solid var(--button_border);\n    border-radius: var(--border_radius);\n    height: var(--input_height);\n    max-height: var(--input_height);\n    margin-top: var(--spacing_input);\n    margin-bottom: var(--spacing_input);\n    padding: var(--input_padding) var(--padding_wide);\n    icon-size: var(--icon_base);\n    outline: none;\n}\n\nQPushButton:hover {\n    background-color: var(--button_bg_hover);\n}\n\nQPushButton:hover,\nQPushButton:focus {\n    border-color: var(--button_border_hover);\n}\n\nQPushButton::flat {\n    background-color: var(--button_bg);\n}\n\nQPushButton:checked {\n    background-color: var(--primary);\n    border-color: var(--primary_light);\n}\n\nQPushButton:checked:hover,\nQPushButton:checked:focus {\n    border-color: var(--primary_lighter);\n}\n\nQPushButton:pressed,\nQPushButton:pressed:hover {\n    background-color: var(--button_bg_down);\n    border-color: var(--button_border);\n}\n\nQPushButton:disabled {\n    background-color: var(--button_bg_disabled);\n    border-color: var(--button_border);\n}\n\nQPushButton::menu-indicator {\n    image: url(theme:Dark/collapse.svg);\n    subcontrol-position: right;\n    subcontrol-origin: content;\n    margin-left: var(--spacing_large);\n    right: -2px;\n}\n\nQToolButton {\n    border: 1px solid var(--button_border);\n}\n\nQToolButton,\n.btn-tool {\n    background-color: var(--button_bg);\n    padding: var(--padding_base) var(--padding_base);\n    margin: 0px 0px;\n    border: 1px solid var(--button_border);\n    border-radius: var(--border_radius);\n    icon-size: var(--icon_base);\n}\n\nQToolButton:last-child,\n.btn-tool:last-child {\n    margin-right: 0px;\n}\n\nQToolButton:hover,\nQToolButton:focus,\n.btn-tool:hover,\n.btn-tool:focus {\n    border-color: var(--button_border);\n    background-color: var(--button_bg_hover);\n}\n\nQToolButton:pressed,\nQToolButton:pressed:hover,\n.btn-tool:pressed,\n.btn-tool:pressed:hover {\n    background-color: var(--button_bg_down);\n    border-color: var(--button_border);\n}\n\nQToolButton:disabled,\n.btn-tool:disabled {\n    background-color: var(--button_bg_disabled);\n    border-color: transparent;\n}\n\n/* Sliders */\n\nQSlider::groove {\n    background-color: var(--grey4);\n    border: none;\n    border-radius: 2px;\n}\n\nQSlider::groove:horizontal {\n    height: 4px;\n}\n\nQSlider::groove:vertical {\n    width: 4px;\n}\n\nQSlider::sub-page:horizontal {\n    background-color: var(--blue2);\n    border-radius: 2px;\n}\n\nQSlider::sub-page:horizontal:disabled {\n    background-color: var(--grey4);\n    border-radius: 2px;\n}\n\nQSlider::add-page:horizontal:disabled {\n    background-color: var(--grey7);\n    border-radius: 2px;\n}\n\nQSlider::add-page:vertical {\n    background-color: var(--blue2);\n    border-radius: 2px;\n}\n\nQSlider::add-page:vertical:disabled {\n    background-color: var(--grey4);\n    border-radius: 2px;\n}\n\nQSlider::sub-page:vertical:disabled {\n    background-color: var(--grey7);\n    border-radius: 2px;\n}\n\nQSlider::handle {\n    background-color: var(--white1);\n    border-radius: var(--border_radius);\n}\n\nQSlider::handle:horizontal {\n    height: 10px;\n    width: 20px;\n    /* Handle is placed by default on the contents rect of the groove. Expand outside the groove */\n    margin: -3px 0;\n}\n\nQSlider::handle:vertical {\n    width: 10px;\n    height: 20px;\n    /* Handle is placed by default on the contents rect of the groove. Expand outside the groove */\n    margin: 0 -3px;\n}\n\nQSlider::handle:hover {\n    background-color: var(--white4);\n}\n\nQSlider::handle:pressed {\n    background-color: var(--white3);\n}\n\nQSlider::handle:disabled {\n    background-color: var(--white5);\n}\n\n#stackedMixerArea {\n    border: none;\n    padding: 0px;\n}\n\n.mixer-category {\n    color: var(--text_muted);\n    margin: 0;\n    padding: 0;\n    text-align: center;\n}\n\n/* Volume Control Buttons */\n\n.btn-mute,\n.btn-monitor {\n    width: var(--icon_small);\n    height: var(--icon_small);\n    background-color: transparent;\n    padding: var(--padding_base_border) var(--padding_base_border);\n    margin: 0px var(--spacing_small);\n    border: var(--highlight_width) solid transparent;\n    border-radius: var(--border_radius);\n    icon-size: var(--icon_small);\n}\n\n.btn-mute:checked,\n.btn-monitor:checked {\n    background-color: transparent;\n    border-color: transparent;\n}\n\n.btn-mute:hover,\n.btn-monitor:hover {\n    background-color: var(--button_bg_hover);\n    border-color: transparent;\n}\n\n.btn-mute:pressed,\n.btn-monitor:pressed {\n    background-color: var(--button_bg_down);\n}\n\n/* Mute Button */\n\n.btn-mute {\n    color: var(--text);\n    qproperty-icon: url(theme:Dark/settings/audio.svg);\n}\n\n.btn-mute.checked {\n    color: var(--red3);\n    background: transparent;\n    qproperty-icon: url(theme:Dark/mute.svg);\n}\n\n.btn-mute.checked.hover {\n    color: var(--red2);\n    background: var(--grey8);\n    border-color: var(--red1);\n}\n\n.btn-mute.checked:focus {\n    border-color: var(--red1);\n}\n\n.btn-mute.mute-unassigned {\n    color: var(--yellow1);\n    background: var(--yellow6);\n    qproperty-icon: url(theme:Dark/unassigned.svg);\n}\n\n.btn-mute.mute-unassigned.hover,\n.btn-mute.mute-unassigned:focus {\n    border-color: var(--yellow1);\n}\n\n/* Monitor Button */\n\n.btn-monitor {\n    background: transparent;\n    qproperty-icon: url(theme:Dark/headphones-off.svg);\n}\n\n.btn-monitor:focus {\n    border-color: var(--red1);\n}\n\n.btn-monitor.checked {\n    background: var(--green4);\n    qproperty-icon: url(theme:Dark/headphones.svg);\n}\n\n.btn-monitor.checked:focus,\n.btn-monitor:checked:focus {\n    border-color: var(--green1);\n}\n\n.btn-monitor.checked:hover {\n    background: var(--green3);\n    border-color: var(--green1);\n}\n\n.volume-inactive .btn-monitor.checked {\n    background: var(--grey7);\n}\n\n.volume-inactive .btn-monitor.checked:hover {\n    background-color: var(--button_bg_hover);\n}\n\n/* Toggle hidden button */\n\n.toggle-hidden:checked {\n    background: var(--purple5);\n    border-color: var(--purple3);\n}\n\n.toggle-hidden:disabled {\n    background: transparent;\n    border-color: transparent;\n}\n\nVolumeControl,\nVolumeMeter {\n    background: var(--bg_base);\n}\n\nVolumeMeter {\n    color: var(--grey3);\n    font-size: 7.5pt;\n    font-weight: bold;\n    \n    qproperty-backgroundNominalColor: var(--green5);\n    qproperty-backgroundWarningColor: var(--yellow5);\n    qproperty-backgroundErrorColor: var(--red5);\n    qproperty-foregroundNominalColor: var(--green2);\n    qproperty-foregroundWarningColor: var(--yellow2);\n    qproperty-foregroundErrorColor: var(--red2);\n    qproperty-magnitudeColor: rgb(0, 0, 0);\n    qproperty-majorTickColor: var(--white5);\n    qproperty-minorTickColor: var(--grey1);\n}\n\nVolumeName {\n    background: transparent;\n    padding: var(--padding_base);\n}\n\nVolumeName QLabel {\n    font-size: var(--font_small);\n}\n\nVolumeName:focus {\n    border: 1px solid var(--button_border_hover);\n}\n\nVolumeName:hover {\n    background: var(--button_bg_hover);\n    border: 1px solid var(--button_border_hover);\n}\n\nVolumeControl #volLabel {\n    padding: var(--padding_base) 0px var(--padding_base);\n    text-align: center;\n    font-size: var(--font_base);\n    color: var(--text_muted);\n}\n\n/* Horizontal Mixer */\n#hVolumeWidgets VolumeControl {\n    padding: 0px 0px;\n    border-bottom: 1px solid var(--border_color);\n    margin-bottom: 0px;\n}\n\n#hVolumeWidgets .mixer-category {\n    min-width: 72px;    \n    margin: var(--spacing_base);\n    border-radius: var(--border_radius);\n}\n\n#hVolumeWidgets VolumeName QLabel {\n    margin: 0px var(--spacing_base) var(--spacing_base);\n}\n\n#hVolumeWidgets #volLabel {\n    margin: var(--spacing_base);\n    border-radius: var(--border_radius);\n}\n\n#hVolumeWidgets #volMeterFrame {\n    margin-bottom: var(--padding_xlarge);\n}\n\n#hVolumeWidgets VolumeControl QSlider {\n    margin: var(--padding_large) 0px var(--padding_xlarge);\n}\n\n#hVolumeWidgets VolumeControl QSlider::groove:horizontal {\n    background: var(--bg_window);\n    height: var(--volume_slider);\n}\n\n#hVolumeWidgets VolumeControl QPushButton {\n    margin-left: var(--padding_xlarge);\n}\n\n#hVolumeWidgets VolumeControl #volMeterFrame {\n    padding: 0 var(--padding_xlarge);\n}\n\n/* Vertical Mixer */\n#vVolumeWidgets VolumeControl {\n    padding: 0px 0px var(--padding_container);\n    border-right: 1px solid var(--border_color);\n}\n\n#vVolumeWidgets .mixer-category {\n    border-bottom: 1px solid var(--border_color);\n}\n\n#vMixerScrollArea VolumeControl QSlider {\n    width: var(--volume_slider_box);\n    margin: 0px var(--padding_xlarge);\n}\n\n#vVolumeWidgets VolumeControl #volLabel {\n    padding: var(--padding_base) 0px var(--padding_base);\n    min-width: var(--volume_slider_label);\n    margin-left: var(--padding_xlarge);\n    text-align: center;\n}\n\n#vVolumeWidgets VolumeControl QSlider::groove:vertical {\n    background: var(--bg_window);\n    width: var(--volume_slider);\n}\n\n#vVolumeWidgets VolumeControl #volMeterFrame {\n    padding: var(--padding_large) var(--padding_xlarge) var(--padding_large) 0px;\n}\n\n#vVolumeWidgets VolumeControl QLabel {\n    padding: 0px var(--padding_large);\n}\n\n/* Hidden Volume Controls */\nVolumeControl.volume-hidden,\nVolumeControl.volume-hidden VolumeMeter,\nVolumeControl.volume-inactive VolumeMeter,\nVolumeControl.volume-inactive {\n    background: var(--grey7);\n}\n\nVolumeControl.volume-hidden .mixer-name,\nVolumeControl.volume-inactive .mixer-name {\n    color: var(--text_muted);\n}\n\nVolumeControl .mixer-category {\n    font-weight: bold;\n    background: var(--blue6);\n    color: var(--blue1);\n}\n\nVolumeControl.volume-pinned .mixer-category {\n    background: var(--blue5);\n    color: var(--text);\n}\n\nVolumeControl.volume-hidden .mixer-category {\n    background: var(--purple6);\n    color: var(--purple1);\n}\n\nVolumeControl.volume-unassigned .mixer-category {\n    background: var(--yellow6);\n    color: var(--yellow1);\n}\n\nVolumeControl.volume-inactive .mixer-category {\n    background: var(--grey8);\n    color: var(--text_muted);\n}\n\nVolumeControl.volume-preview .mixer-category {\n    background: var(--green6);\n    color: var(--green1);\n}\n\n#stackedMixerArea VolumeControl.volume-hidden QSlider::groove,\n#stackedMixerArea VolumeControl.volume-inactive QSlider::groove {\n    background: var(--grey8);\n}\n\n/* Status Bar */\n\nQStatusBar::item {\n    border: none;\n}\n\n/* Table View */\n\nQTableView {\n    background: var(--bg_base);\n    gridline-color: var(--grey1);\n}\n\nQTableView::item {\n    margin: 0px;\n    padding: 0px;\n}\n\nQTableView QLineEdit {\n    background: var(--input_bg_focus);\n    padding: 0;\n    margin: 0;\n}\n\nQTableView QPushButton,\nQTableView QToolButton {\n    padding: 0px;\n    margin: -1px;\n    border-radius: 0px;\n}\n\nQHeaderView::section {\n    background-color: var(--button_bg);\n    color: var(--text);\n    border: none;\n    border-left: 1px solid var(--bg_window);\n    border-right: 1px solid var(--bg_window);\n    padding: 3px 0px;\n    margin-bottom: 2px;\n}\n\n/* Canvas / Preview background color */\n\nOBSQTDisplay {\n    qproperty-displayBackgroundColor: var(--bg_preview);\n}\n\n/* Filters Window */\n\nOBSBasicFilters #widget,\nOBSBasicFilters #widget_2 {\n    margin: 0px;\n    padding: 0px;\n    padding-bottom: var(--padding_base);\n}\n\nOBSBasicFilters #widget QPushButton,\nOBSBasicFilters #widget_2 QPushButton {\n    min-width: 16px;\n    padding: var(--padding_base) var(--padding_large);\n    margin-top: 0px;\n}\n\n/* Preview/Program labels */\n\n.label-preview-title {\n    font-size: var(--font_xlarge);\n    font-weight: bold;\n    color: var(--text_light);\n    margin-bottom: 4px;\n}\n\n/* Settings Icons */\n\nOBSBasicSettings {\n    qproperty-generalIcon: url(theme:Dark/settings/general.svg);\n    qproperty-appearanceIcon: url(theme:Dark/settings/appearance.svg);\n    qproperty-streamIcon: url(theme:Dark/settings/stream.svg);\n    qproperty-outputIcon: url(theme:Dark/settings/output.svg);\n    qproperty-audioIcon: url(theme:Dark/settings/audio.svg);\n    qproperty-videoIcon: url(theme:Dark/settings/video.svg);\n    qproperty-hotkeysIcon: url(theme:Dark/settings/hotkeys.svg);\n    qproperty-accessibilityIcon: url(theme:Dark/settings/accessibility.svg);\n    qproperty-advancedIcon: url(theme:Dark/settings/advanced.svg);\n}\n\n/* Checkboxes */\n\nQCheckBox::indicator,\nQGroupBox::indicator,\nQTableView::indicator {\n    width: var(--icon_base);\n    height: var(--icon_base);\n    margin-right: var(--spacing_large);\n}\n\nQGroupBox::indicator {\n    margin-left: 2px;\n}\n\nQCheckBox::indicator:unchecked,\nQGroupBox::indicator:unchecked,\nQTableView::indicator:unchecked {\n    image: url(theme:Yami/checkbox_unchecked.svg);\n}\n\nQCheckBox::indicator:unchecked:hover,\nQGroupBox::indicator:unchecked:hover,\nQTableView::indicator:unchecked:hover {\n    image: url(theme:Yami/checkbox_unchecked_focus.svg);\n}\n\nQCheckBox::indicator:checked,\nQGroupBox::indicator:checked,\nQTableView::indicator:checked {\n    image: url(theme:Yami/checkbox_checked.svg);\n}\n\nQCheckBox::indicator:checked:hover,\nQGroupBox::indicator:checked:hover,\nQTableView::indicator:checked:hover {\n    image: url(theme:Yami/checkbox_checked_focus.svg);\n}\n\nQCheckBox::indicator:checked:disabled,\nQGroupBox::indicator:checked:disabled,\nQTableView::indicator:checked:disabled {\n    image: url(theme:Yami/checkbox_checked_disabled.svg);\n}\n\nQCheckBox::indicator:unchecked:disabled,\nQGroupBox::indicator:unchecked:disabled,\nQTableView::indicator:unchecked:disabled {\n    image: url(theme:Yami/checkbox_unchecked_disabled.svg);\n}\n\n/* Icon Checkboxes */\n.checkbox-icon {\n    outline: none;\n    background: transparent;\n    max-width: var(--icon_base);\n    max-height: var(--icon_base);\n    padding: var(--padding_base);\n    margin-right: var(--spacing_large);\n    border: var(--highlight_width) solid transparent;\n    border-radius: var(--border_radius);\n}\n\n.checkbox-icon::indicator {\n    width: var(--icon_base);\n    height: var(--icon_base);\n}\n\n.checkbox-icon:hover,\n.checkbox-icon:focus {\n    border-color: var(--primary_lighter);\n}\n\n/* Locked CheckBox */\n\n.indicator-lock::indicator:checked,\n.indicator-lock::indicator:checked:hover {\n    image: url(theme:Dark/locked.svg);\n}\n\n.indicator-lock::indicator:unchecked,\n.indicator-lock::indicator:unchecked:hover {\n    image: url(:res/images/unlocked.svg);\n}\n\n/* Visibility CheckBox */\n\n.indicator-visibility::indicator:checked,\n.indicator-visibility::indicator:checked:hover {\n    image: url(theme:Dark/visible.svg);\n}\n\n.indicator-visibility::indicator:unchecked,\n.indicator-visibility::indicator:unchecked:hover {\n    image: url(:res/images/invisible.svg);\n}\n\n#hotkeyFilterReset {\n    margin-top: 0px;\n}\n\nOBSHotkeyWidget {\n    padding: 8px 0px;\n    margin: 2px 0px;\n}\n\nOBSHotkeyLabel {\n    padding: 4px 0px;\n}\n\nOBSHotkeyWidget QPushButton {\n    min-width: 16px;\n    padding: var(--padding_base);\n    margin-top: 0px;\n    margin-left: var(--spacing_base);\n}\n\n\n/* Sources List Group Collapse Checkbox */\n\n.indicator-expand::indicator:checked,\n.indicator-expand::indicator:checked:hover {\n    image: url(theme:Dark/expand.svg);\n}\n\n.indicator-expand::indicator:unchecked,\n.indicator-expand::indicator:unchecked:hover {\n    image: url(theme:Dark/collapse.svg);\n}\n\n/* Source Icons */\n\n.source-icon {\n    margin-right: var(--spacing_large);\n}\n\nOBSBasic {\n    qproperty-imageIcon: url(theme:Dark/sources/image.svg);\n    qproperty-colorIcon: url(theme:Dark/sources/brush.svg);\n    qproperty-slideshowIcon: url(theme:Dark/sources/slideshow.svg);\n    qproperty-audioInputIcon: url(theme:Dark/sources/microphone.svg);\n    qproperty-audioOutputIcon: url(theme:Dark/settings/audio.svg);\n    qproperty-desktopCapIcon: url(theme:Dark/settings/video.svg);\n    qproperty-windowCapIcon: url(theme:Dark/sources/window.svg);\n    qproperty-gameCapIcon: url(theme:Dark/sources/gamepad.svg);\n    qproperty-cameraIcon: url(theme:Dark/sources/camera.svg);\n    qproperty-textIcon: url(theme:Dark/sources/text.svg);\n    qproperty-mediaIcon: url(theme:Dark/sources/media.svg);\n    qproperty-browserIcon: url(theme:Dark/sources/globe.svg);\n    qproperty-groupIcon: url(theme:Dark/sources/group.svg);\n    qproperty-sceneIcon: url(theme:Dark/sources/scene.svg);\n    qproperty-defaultIcon: url(theme:Dark/sources/default.svg);\n    qproperty-audioProcessOutputIcon: url(theme:Dark/sources/windowaudio.svg);\n}\n\n/* Scene Tree Grid Mode */\n\nSceneTree {\n    qproperty-gridItemWidth: 154;\n    qproperty-gridItemHeight: var(--input_height_base);\n}\n\n.list-grid SceneTree::item {\n    color: var(--text);\n    background-color: var(--button_bg);\n    border-radius: var(--border_radius);\n    margin: var(--spacing_base);\n}\n\n.list-grid SceneTree::item:selected {\n    background-color: var(--list_item_bg_selected);\n}\n\n.list-grid SceneTree::item:checked {\n    background-color: var(--primary);\n}\n\n.list-grid SceneTree::item:hover {\n    background-color: var(--list_item_bg_hover);\n}\n\n.list-grid SceneTree::item:selected:hover {\n    background-color: var(--list_item_bg_hover);\n}\n\n/* Studio Mode T-Bar */\n\n.slider-tbar {\n    height: 24px;\n}\n\n.slider-tbar::groove:horizontal {\n    height: 8px;\n}\n\n.slider-tbar::sub-page:horizontal {\n    background: var(--blue2);\n}\n\n.slider-tbar::handle:horizontal {\n    width: 12px;\n    height: 24px;\n    margin: -24px 0px;\n}\n\n/* YouTube Integration */\nOBSYoutubeActions {\n    qproperty-thumbPlaceholder: url(theme:Dark/sources/image.svg);\n}\n\n#ytEventList QLabel {\n    color: var(--text);\n    background-color: var(--button_bg);\n    border: none;\n    border-radius: var(--border_radius);\n    padding: 4px 20px;\n}\n\n#ytEventList QLabel:hover {\n    background-color: var(--button_bg_hover);\n}\n\n#ytEventList .row-selected {\n    background-color: var(--primary);\n    border: none;\n}\n\n#ytEventList .row-selected:hover {\n    background-color: var(--primary_light);\n    color: var(--text);\n}\n\n/* Calendar Widget */\nQDateTimeEdit::down-arrow {\n    qproperty-alignment: AlignTop;\n    image: url(theme:Dark/down.svg);\n    width: 100%;\n}\n\nQDateTimeEdit:on {\n    background-color: var(--grey7);\n}\n\n/* Calendar Top Bar */\nQCalendarWidget QWidget#qt_calendar_navigationbar {\n    background-color: var(--bg_base);\n    padding: var(--padding_base) var(--padding_large);\n}\n\n/* Calendar Top Bar Buttons */\nQCalendarWidget QToolButton {\n    background-color: var(--button_bg);\n    padding: 2px 16px;\n    border-radius: var(--border_radius);\n    margin: var(--spacing_base);\n}\n\n#qt_calendar_monthbutton::menu-indicator {\n    image: url(theme:Dark/down.svg);\n    subcontrol-position: right;\n    padding-top: var(--padding_small);\n    padding-right: var(--padding_base);\n    height: 10px;\n    width: 10px;\n}\n\nQCalendarWidget #qt_calendar_prevmonth {\n    padding: var(--padding_small);\n    qproperty-icon: url(theme:Dark/left.svg);\n    icon-size: var(--icon_base);\n}\n\nQCalendarWidget #qt_calendar_nextmonth {\n    padding: var(--padding_small);\n    qproperty-icon: url(theme:Dark/right.svg);\n    icon-size: var(--icon_base);\n}\n\nQCalendarWidget QToolButton:hover {\n    background-color: var(--button_bg_hover);\n    border-radius: var(--border_radius);\n}\n\nQCalendarWidget QToolButton:pressed {\n    background-color: var(--button_bg_down);\n}\n\n/* Month Dropdown Menu */\nQCalendarWidget QMenu {}\n\n/* Year spinbox */\nQCalendarWidget QSpinBox {\n    background-color: var(--input_bg);\n    border: none;\n    border-radius: var(--border_radius);\n    margin: 0px var(--spacing_base) 0px 0px;\n    padding: var(--padding_base) 16px;\n}\n\nQCalendarWidget QSpinBox::up-button {\n    subcontrol-origin: border;\n    subcontrol-position: top right;\n    width: 16px;\n}\n\nQCalendarWidget QSpinBox::down-button {\n    subcontrol-origin: border;\n    subcontrol-position: bottom right;\n    width: 16px;\n}\n\nQCalendarWidget QSpinBox::up-arrow {\n    width: 10px;\n    height: 10px;\n}\n\nQCalendarWidget QSpinBox::down-arrow {\n    width: 10px;\n    height: 10px;\n}\n\n/* Days of the Week Bar */\nQCalendarWidget QWidget {\n    alternate-background-color: var(--grey7);\n}\n\nQCalendarWidget QAbstractItemView:enabled {\n    background-color: var(--bg_base);\n    color: var(--text);\n}\n\nQCalendarWidget QAbstractItemView:disabled {\n    color: var(--text_disabled);\n}\n\n/* VirtualCam Plugin Fixes */\n\n#VirtualProperties QWidget {\n    margin-top: 0;\n    margin-bottom: 0;\n}\n\n/* Disable icons on QDialogButtonBox */\nQDialogButtonBox {\n    dialogbuttonbox-buttons-have-icons: 0;\n}\n\n/* Stats dialog */\nOBSBasicStats {\n    background: var(--bg_base);\n}\n\n/* Advanced audio dialog */\nOBSBasicAdvAudio #scrollAreaWidgetContents {\n    background: var(--bg_base);\n}\n\n#previewScalePercent,\n#previewScalingMode {\n    background: transparent;\n    color: var(--text_muted);\n    font-size: var(--font_xsmall);\n    height: 14px;\n    max-height: 14px;\n    padding: 0px;\n    margin: 0;\n    border: none;\n    border-radius: 0;\n}\n\n#previewXContainer {\n    border: 1px solid var(--grey6);\n}\n\n#previewScalePercent {\n    padding: 0px var(--input_text_padding);\n    min-width: var(--preview_scale_width);\n}\n\n#previewScalingMode {\n    padding: 0px var(--input_text_padding);\n    border: 1px solid var(--grey6);\n}\n\n#previewScalingMode:hover,\n#previewScalingMode:focus {\n    border-color: var(--input_border_hover);\n}\n\n#previewScalingMode:on {\n    background-color: var(--input_bg_focus);\n    border-color: var(--input_border_focus);\n}\n\n#previewXScrollBar,\n#previewYScrollBar {\n    background: transparent;\n    border: 1px solid var(--grey6);\n    border-radius: 0;\n}\n\n#previewXScrollBar {\n    border-left: none;\n    height: 16px;\n}\n\n#previewXScrollBar::handle,\n#previewYScrollBar::handle {\n    margin: 3px;\n}\n\n#previewYScrollBar {\n    width: 16px;\n}\n\n#previewZoomInButton {\n    border: none;\n    border-radius: 0px;\n    outline: none;\n}\n\n#previewZoomOutButton {\n    border: none;\n    border-radius: 0px;\n    outline: none;\n}\n\n#previewZoomInButton:!hover,\n#previewZoomOutButton:!hover {\n    background-color: transparent;\n}\n\n#previewZoomInButton:pressed,\n#previewZoomOutButton:pressed {\n    background-color: var(--button_bg);\n}\n\n#previewZoomInButton:focus,\n#previewZoomOutButton:focus {\n    border: 1px solid var(--input_border_hover);\n}\n\n/* Idian Widgets */\nidian--Group {\n    border-radius: var(--border_radius);\n    font-weight: bold;\n    margin: 0 0 var(--spacing_base);\n    min-width: 300px;\n    max-width: 600px;\n}\n\nidian--Group .header .title {\n    font-weight: bold;\n    padding: var(--padding_large) 0;\n}\n\nidian--Group .header .description {\n    color: var(--text_muted);\n    padding: var(--spacing_small) 0;\n}\n\nidian--PropertiesList {\n    border-width: 0;\n    padding: 0;\n    margin: var(--spacing_base) 0;\n}\n\nidian--Row {\n    background: var(--grey5);\n    margin: 0;\n    padding: var(--action_row_padding) var(--action_row_padding_x);\n}\n\nidian--Row.keyFocus {\n    background: var(--grey4);\n    border: var(--highlight_width) solid var(--grey4);\n}\n\nidian--Row.cursor-pointer.hover {\n    background: var(--grey4);\n    border: var(--highlight_width) solid var(--grey1);\n}\n\nidian--Row.first {\n    border-top-left-radius: var(--border_radius);\n    border-top-right-radius: var(--border_radius);\n}\n\nidian--Row.last {\n    border-bottom-left-radius: var(--border_radius);\n    border-bottom-right-radius: var(--border_radius);\n}\n\nidian--Row > QLabel.description {\n    font-size: var(--font_small);\n    color: var(--text_muted);\n}\n\nidian--ToggleSwitch {\n    qproperty-background: var(--grey6);\n    qproperty-background_hover: var(--grey7);\n    qproperty-background_checked: var(--primary);\n    qproperty-background_checked_hover: var(--primary_light);\n\n    min-width: var(--toggle_width);\n    min-height: var(--toggle_height);\n\n    border-radius: var(--toggle_radius);\n\n    qproperty-handleColor: var(--white1);\n    qproperty-handleSize: var(--toggle_handle);\n\n    border: var(--highlight_width) solid transparent;\n}\n\nidian--ToggleSwitch.hover {\n    border-color: var(--grey4);\n}\n\nidian--ToggleSwitch.checked.hover {\n    border-color: var(--white1);\n}\n\nidian--ToggleSwitch.keyFocus {\n    border-color: var(--highlight_color);\n}\n\nidian--Row idian--ToggleSwitch.hover,\nidian--Row.hover > idian--ToggleSwitch.row-buddy {\n    border-color: var(--grey1);\n}\n\nidian--Row idian--ToggleSwitch.hover.checked,\nidian--Row.hover > idian--ToggleSwitch.checked.row-buddy {\n    border-color: var(--white1);\n}\n\nidian--Row QComboBox {\n    background-color: transparent;\n    min-height: var(--action_row_base);\n    max-height: var(--action_row_base);\n    min-width: var(--action_row_input_width);\n    border: var(--highlight_width) solid transparent;\n    padding: 0;\n    padding-left: var(--padding_xlarge);\n    margin: 0;\n}\n\nidian--Row QComboBox:focus {\n    border-color: transparent;\n}\n\nidian--Row QComboBox.hover {\n    border-color: var(--grey1);\n}\n\nidian--Row QComboBox.keyFocus {\n    border-color: var(--highlight_color);\n}\n\nidian--Row QComboBox::drop-down {\n    border: none;\n}\n\nidian--Row QComboBox::down-arrow {\n    image: url(theme:Dark/collapse.svg);\n}\n\nidian--Row QComboBox QAbstractItemView {\n    outline: none;\n}\n\nidian--Row QComboBox QAbstractItemView::item {\n    background-color: var(--bg_base);\n    padding: var(--padding_base) var(--padding_large);\n}\n\nidian--Row QComboBox QAbstractItemView.hover::item,\nidian--Row QComboBox QAbstractItemView::item:selected {\n    background-color: var(--list_item_bg_selected);\n    padding: var(--padding_base) var(--padding_large);\n}\n\nidian--Row QPushButton,\nidian--Row QSpinBox,\nidian--Row QDoubleSpinBox {\n    margin: 0;\n    padding: var(--padding_base) var(--action_row_padding_x);\n}\n\nidian--PropertiesListSpacer {\n    max-height: var(--spacing_small);\n    min-height: var(--spacing_small);\n    background-color: var(--bg_window);\n}\n\nidian--Row idian--CheckBox {\n    outline: none;\n}\n\nidian--CheckBox::indicator {\n    border: var(--highlight_width) solid transparent;\n    border-radius: var(--border_radius);\n}\n\nidian--CheckBox.keyFocus::indicator,\nidian--Row.hover > idian--CheckBox.row-buddy::indicator {\n    image: url(theme:Yami/checkbox_unchecked_focus.svg);\n}\n\nidian--CheckBox.keyFocus.checked::indicator,\nidian--Row.hover > idian--CheckBox.row-buddy.checked::indicator {\n    image: url(theme:Yami/checkbox_checked_focus.svg);\n}\n\nidian--CheckBox.hover::indicator,\nidian--CheckBox.checked.hover::indicator,\nidian--Row.hover > idian--CheckBox.row-buddy::indicator\nidian--Row.hover > idian--CheckBox.row-buddy.checked::indicator {\n    border-color: var(--grey1);\n}\n\nidian--CheckBox.keyFocus::indicator,\nidian--CheckBox.keyFocus.checked::indicator {\n    border-color: var(--highlight_color);\n}\n\nidian--CollapsibleRow {\n    margin: 0;\n    padding: 0;\n    border: none;\n}\n\nidian--CollapsibleRow.keyFocus {\n    border: var(--highlight_width) solid var(--highlight_color);\n}\n\nidian--CollapsibleRow idian--PropertiesList {\n    border-radius: 0;\n    border-left: 1px solid var(--grey5);\n    border-right: 1px solid var(--grey5);\n    border-bottom: 1px solid var(--grey5);\n    margin: var(--spacing_small) 0px 0px;\n}\n\nidian--CollapsibleRow idian--PropertiesList idian--Row {\n    background-color: var(--grey6);\n    padding-left: var(--action_row_padding_nested);\n}\n\nidian--CollapsibleRow idian--Row.first,\nidian--CollapsibleRow idian--Row.last {\n    border-radius: 0;\n}\n\nidian--CollapsibleRow idian--PropertiesList idian--ToggleSwitch {\n    qproperty-background: var(--grey7);\n    qproperty-background_hover: var(--grey6);\n}\n\nidian--ExpandButton {\n    background: transparent;\n    min-width: var(--action_row_collapse);\n    max-width: var(--action_row_collapse);\n    min-height: var(--action_row_collapse);\n    max-height: var(--action_row_collapse);\n    border: none;\n}\n\nidian--ExpandButton::indicator {\n    background: var(--grey5);\n    border-radius: var(--action_row_collapse_radius);\n    padding: var(--padding_large);\n    image: url(theme:Dark/down.svg);\n    border: var(--highlight_width) solid var(--grey5);\n}\n\nidian--ExpandButton::indicator:checked {\n    image: url(theme:Dark/up.svg);\n}\n\nidian--ExpandButton.keyFocus,\nidian--ExpandButton.keyFocus::indicator {\n    border-color: var(--highlight_color);\n}\n\nidian--RowFrame .btn-frame {\n    background: var(--grey5);\n    padding: var(--action_row_padding) var(--action_row_padding_x);\n}\n\nidian--RowFrame.hover .btn-frame {\n    background: var(--grey4);\n}\n\nidian--RowFrame.hover idian--Row {\n    background: var(--grey4);\n    border: var(--highlight_width) solid var(--grey1);\n    border-right: none;\n}\n\nidian--RowFrame.hover .row-buddy {\n    background: var(--grey4);\n    border: var(--highlight_width) solid var(--grey1);\n    border-left: none;\n}\n\nidian--RowFrame.hover idian--ExpandButton::indicator {\n    border-color: var(--grey1);\n}\n\nAlignmentSelector {\n    border: 1px solid var(--input_border);\n    margin: 0px;\n    border-radius: 2px;\n}\n\nAlignmentSelector:focus,\nAlignmentSelector:hover {\n    border: 1px solid var(--white3);\n}\n\nAlignmentSelector:checked:hover {\n    border: 1px solid var(--primary_lighter);\n}\n\nAlignmentSelector::indicator {\n    margin: 0px;\n    background: transparent;    \n}\n\nAlignmentSelector::indicator:checked {\n    background: var(--primary);\n}\n\nAlignmentSelector::indicator:checked:focus {\n    background: var(--primary_light);\n}\n\nAlignmentSelector:disabled {\n    border: 1px solid var(--grey3);\n}\n\nAlignmentSelector::indicator:disabled {\n    background: var(--grey5);\n}\n\nAlignmentSelector::indicator:checked:disabled {\n    background: var(--grey3);\n}\n"
  },
  {
    "path": "frontend/data/themes/Yami_Acri.ovt",
    "content": "@OBSThemeMeta {\n    name: 'Acri';\n    id: 'com.obsproject.Yami.Acri';\n    extends: 'com.obsproject.Yami';\n    author: 'Warchamp7';\n    dark: 'true';\n}\n\n@OBSThemeVars {\n    --grey1: rgb(97,97,97);\n    --grey2: rgb(87,87,87);\n    --grey3: rgb(61,61,63);\n    --grey4: rgb(22,36,88);\n    --grey5: rgb(40,40,42);\n    --grey6: rgb(24,24,25);\n    --grey7: rgb(16,16,16);\n    --grey8: rgb(9,9,9);\n\n    --primary: rgb(19,26,48);\n    --primary_light: rgb(42,58,117);\n    --primary_dark: rgb(22,31,65);\n\n    --border_color: var(--grey5);\n\n    --input_bg: var(--grey5);\n    --input_bg_hover: var(--grey7);\n    --input_bg_focus: var(--grey7);\n\n    --list_item_bg_selected: var(--primary);\n    --list_item_bg_hover: var(--primary_light);\n\n    --input_border: var(--grey1);\n    --input_border_hover: var(--grey1);\n    --input_border_focus: var(--primary);\n\n    --spacing_input: var(--spacing_base);\n\n    --button_bg: #162458;\n    --button_bg_hover: var(--primary_light);\n    --button_bg_down: var(--grey7);\n    --button_bg_disabled: var(--grey6);\n\n    --button_bg_red: rgb(88,22,36);\n    --button_bg_red_hover: rgb(116,32,49);\n    --button_bg_red_down: rgb(63,21,30);\n\n    --button_border: #162458;\n    --button_border_hover: var(--button_bg_hover);\n    --button_border_focus: var(--button_bg_hover);\n\n    --tab_bg_down: #162458;\n\n    --tab_border_hover: var(--primary_light);\n    --tab_border_focus: var(--input_border_focus);\n    --tab_border_selected: var(--primary);\n\n    --scrollbar_handle: var(--grey5);\n    --scrollbar_hover: var(--primary_light);\n    --scrollbar_down: var(--grey5);\n    --scrollbar_border: var(--grey5);\n\n    --toolbutton_bg: var(--grey5);\n    --toolbutton_bg_hover: var(--primary_light);\n    --toolbutton_bg_down: var(--primary_dark);\n}\n\nQMenu::separator {\n    background-color: var(--grey5);\n}\n\nQDockWidget::title {\n    background-color: var(--grey6);\n}\n\nQDockWidget::close-button:hover,\nQDockWidget::float-button:hover {\n    background: var(--grey3);\n}\n\nQScrollBar::handle:hover {\n    border-color: var(--scrollbar_hover);\n}\n\nQScrollBar::handle:pressed {\n    border-color: var(--scrollbar_down);\n}\n\nQToolButton,\nQPushButton[toolButton=\"true\"] {\n    background-color: var(--toolbutton_bg);\n    border-color: var(--toolbutton_bg);\n}\n\nQToolButton:hover,\nQToolButton:focus {\n    border-color: var(--primary_light);\n    background-color: var(--primary_light);\n}\n\nQToolButton:pressed,\nQToolButton:pressed:hover {\n    background-color: var(--toolbutton_bg_down);\n    border-color: var(--toolbutton_bg_down);\n}\n\n.indicator-mute::indicator,\n.indicator-mute::indicator:unchecked,\n.indicator-mute::indicator:focus {\n    background-color: var(--toolbutton_bg);\n    border: 1px solid var(--toolbutton_bg);\n}\n\n.indicator-mute::indicator:hover,\n.indicator-mute::indicator:unchecked:hover {\n    background-color: var(--toolbutton_bg_hover);\n    border: 1px solid var(--toolbutton_bg_hover);\n}\n\n.indicator-mute::indicator:pressed,\n.indicator-mute::indicator:pressed:hover {\n    background-color: var(--toolbutton_bg_down);\n    border-color: var(--toolbutton_bg_down);\n}\n\nQTabBar::tab {\n    background-color: var(--grey5);\n}\n\nQTabBar QToolButton {\n    background-color: var(--grey5);\n}\n\n/* Primary Control Button Checked Coloring */\n#streamButton:!hover:!pressed.state-active,\n#recordButton:!hover:!pressed.state-active,\n#pauseRecordButton:!hover:!pressed.state-active,\n#replayBufferButton:!hover:!pressed.state-active,\n#virtualCamButton:!hover:!pressed.state-active,\n#modeSwitch:!hover:!pressed.state-active,\n#broadcastButton:!hover:!pressed.state-active {\n    background: var(--button_bg_red);\n    border-color: var(--button_bg_red);\n}\n\n/* Primary Control Button Hover Coloring */\n#streamButton:hover:!pressed.state-active,\n#recordButton:hover:!pressed.state-active,\n#pauseRecordButton:hover:!pressed.state-active,\n#replayBufferButton:hover:!pressed.state-active,\n#virtualCamButton:hover:!pressed.state-active,\n#modeSwitch:hover:!pressed.state-active,\n#broadcastButton:hover:!pressed.state-active {\n    background: var(--button_bg_red_hover);\n}\n\n/* Primary Control Button Checked + Pressed Coloring */\n#streamButton:pressed.state-active,\n#recordButton:pressed.state-active,\n#pauseRecordButton:pressed.state-active,\n#replayBufferButton:pressed.state-active,\n#virtualCamButton:pressed.state-active,\n#modeSwitch:pressed.state-active,\n#broadcastButton:pressed.state-active {\n    background: var(--button_bg_red_down);\n}\n\nQPushButton:disabled {\n    border-color: var(--grey5);\n}\n\nQHeaderView::section {\n    background-color: var(--grey5);\n}\n\nOBSQTDisplay {\n    qproperty-displayBackgroundColor: var(--grey5);\n}\n\n.list-grid SceneTree::item:selected {\n    background-color: var(--button_bg_red);\n}\n\n.list-grid SceneTree::item:checked {\n    background-color: var(--button_bg_red);\n}\n\n.list-grid SceneTree::item:hover {\n    background-color: var(--button_bg_red_down);\n}\n\n.list-grid SceneTree::item:selected:hover {\n    background-color: var(--button_bg_red_hover);\n}\n\n#ytEventList QLabel {\n    background-color: var(--grey5);\n}\n\n#ytEventList QLabel:hover {\n    background-color: var(--grey3);\n}\n\nQCalendarWidget QToolButton {\n    background-color: var(--grey6);\n}\n\nQCalendarWidget QToolButton:hover {\n    background-color: var(--primary_light);\n}\n\nQCalendarWidget QToolButton:pressed {\n    background-color: var(--primary_dark);\n}\n\nQCalendarWidget QSpinBox {\n    background-color: var(--primary_dark);\n}\n\nidian--ToggleSwitch {\n    qproperty-background_checked: var(--button_bg);\n    qproperty-background_checked_hover: var(--primary_light);\n}\n"
  },
  {
    "path": "frontend/data/themes/Yami_Classic.ovt",
    "content": "@OBSThemeMeta {\n    name: 'Classic';\n    id: 'com.obsproject.Yami.Classic';\n    extends: 'com.obsproject.Yami';\n    author: 'Warchamp7';\n    dark: 'true';\n}\n\n@OBSThemeVars {\n    --grey1: rgb(97,97,97);\n    --grey2: rgb(134,135,134);\n    --grey3: rgb(122,121,122);\n    --grey4: rgb(76,76,76);\n    --grey5: rgb(70,69,70);\n    --grey6: rgb(31,30,31);\n    --grey7: rgb(58,57,58);\n    --grey8: rgb(46,45,46);\n\n    --bg_window: var(--grey7);\n    --bg_base: var(--grey6);\n    --bg_preview: rgb(76,76,76);\n\n    --primary: rgb(25,52,76);\n    --primary_light: rgb(33,71,109);\n\n    /* Layout */\n    --padding_large: min(max(0px, calc(1px * var(--padding_base_value))), 5px);\n\n    --padding_container: max(2px, var(--padding_base));\n\n    /* Inputs / Controls */\n\n    --border_color: var(--grey6);\n\n    --border_radius: 2px;\n    --border_radius_small: 1px;\n    --border_radius_large: 2px;\n\n    --input_height_base: max(calc(var(--input_font_scale) + var(--input_font_padding)), 20);\n    --input_padding: calc(0px + var(--padding_base));\n    --input_text_padding: max(calc(6px + var(--padding_base)), 8px);\n\n    --input_bg: var(--grey4);\n    --input_bg_hover: var(--grey1);\n    --input_bg_focus: var(--grey6);\n\n    --list_item_bg_selected: var(--primary);\n    --list_item_bg_hover: var(--primary_light);\n\n    --input_border: var(--grey4);\n    --input_border_hover: var(--grey1);\n    --input_border_focus: var(--grey6);\n\n    --spacing_input: var(--spacing_base);\n\n    --button_bg: var(--input_bg);\n    --button_bg_hover: var(--grey3);\n    --button_bg_down: var(--grey7);\n    --button_bg_disabled: var(--grey8);\n\n    --button_border: var(--button_bg);\n    --button_border_hover: var(--grey1);\n    --button_border_focus: var(--grey1);\n\n    --tab_bg: var(--input_bg);\n    --tab_bg_hover: var(--grey3);\n    --tab_bg_down: var(--grey7);\n    --tab_bg_disabled: var(--grey8);\n\n    --tab_border: var(--grey1);\n    --tab_border_hover: var(--grey1);\n    --tab_border_focus: var(--grey1);\n    --tab_border_selected: var(--primary);\n\n    --scrollbar_handle: var(--grey4);\n    --scrollbar_hover: var(--grey3);\n    --scrollbar_down: var(--grey8);\n    --scrollbar_border: var(--grey2);\n\n    /* Variant Extras */\n    --dock_title_padding: calc(2px + var(--padding_base));\n    --icon_base_mixer: calc(2px + var(--icon_base));\n    --padding_menu_y: calc(3px + calc(1 * var(--padding_base_value)));\n}\n\nQStatusBar {\n    background-color: var(--bg_window);\n}\n\nOBSDock > QWidget {\n    border-top: 1px solid var(--border_color);\n    padding-top: var(--spacing_large);\n}\n\nQDockWidget {\n    font-weight: normal;\n}\n\nQDockWidget::title {\n    background-color: var(--grey5);\n    padding: var(--dock_title_padding);\n    text-align: center;\n}\n\nQDockWidget > QWidget {\n    background: var(--bg_window);\n}\n\n#transitionsFrame {\n    padding: var(--padding_xlarge);\n}\n\nQMenu::item {\n    padding: var(--padding_menu_y) var(--padding_menu);\n}\n\nQMenu::item {\n    padding-right: 20px;\n}\n\nQGroupBox {\n    background: var(--bg_window);\n    border: 1px solid var(--border_color);\n}\n\nQTabWidget::pane {\n    border-width: 1px;\n}\n\nQTabBar::tab {\n    border-color: var(--tab_bg);\n    padding: 3px 6px;\n}\n\nQComboBox:hover,\nQComboBox:focus,\nQDateTimeEdit:hover,\nQDateTimeEdit:selected {\n    background-color: var(--input_bg_hover);\n}\n\nQComboBox:on,\nQDateTimeEdit:on,\nQComboBox:editable:focus {\n    background-color: var(--input_bg_focus);\n}\n\nQLineEdit,\nQTextEdit,\nQPlainTextEdit {\n    background-color: var(--input_bg_focus);\n    padding-left: 8px;\n}\n\nQLineEdit:hover,\nQTextEdit:hover,\nQPlainTextEdit:hover {\n    background-color: var(--input_bg_focus);\n}\n\nQTextEdit:!editable,\nQTextEdit:!editable:hover,\nQTextEdit:!editable:focus {\n    background-color: var(--input_bg_focus);\n}\n\nQSpinBox,\nQDoubleSpinBox {\n    background-color: var(--input_bg_focus);\n    border-width: 1px;\n    padding-left: 8px;\n    height: var(--input_height);\n    max-height: var(--input_height);\n}\n\nQSpinBox:hover,\nQDoubleSpinBox:hover {\n    background-color: var(--input_bg_focus);\n}\n\nQSpinBox::up-button:disabled,\nQSpinBox::up-button:off,\nQDoubleSpinBox::up-button:disabled,\nQDoubleSpinBox::up-button:off {\n    margin-top: 0px;\n}\n\nQSpinBox::down-button:disabled,\nQSpinBox::down-button:off,\nQDoubleSpinBox::down-button:disabled,\nQDoubleSpinBox::down-button:off {\n    margin-bottom: 0px;\n}\n\nOBSBasicSettings #PropertiesContainer {\n    background-color: var(--bg_window);\n}\n\nOBSBasicSettings QListWidget::item {\n    padding: 4px;\n}\n\nQPushButton:checked {\n    border-color: var(--primary);\n}\n\nQToolButton,\nQPushButton[toolButton=\"true\"] {\n    background-color: var(--bg_window);\n    border-color: var(--bg_window);\n}\n\n#stackedMixerArea QScrollArea {\n  background: var(--bg_base);\n}\n\n#hMixerScrollArea VolControl {\n    padding: 0px var(--padding_container);\n    margin-bottom: 1px;\n}\n\n#hMixerScrollArea #volMeterFrame {\n    margin-top: var(--spacing_large);\n}\n\n#vMixerScrollArea VolControl {\n    padding: 0px var(--padding_xlarge) var(--spacing_large);\n    border-right: 1px solid var(--bg_window);\n}\n\n#vMixerScrollArea QLabel {\n    font-size: var(--font_small);\n}\n\n#vMixerScrollArea #volLabel {\n    font-size: var(--font_base);\n}\n\n.indicator-mute::indicator,\n.indicator-mute::indicator:unchecked {\n    background-color: var(--bg_base);\n    border: none;\n    width: var(--icon_base_mixer);\n    height: var(--icon_base_mixer);\n    icon-size: var(--icon_base_mixer);\n}\n\n.indicator-mute::indicator:checked {\n    background-color: var(--bg_base);\n}\n\n.indicator-mute::indicator:checked:hover,\n.indicator-mute::indicator:unchecked:hover {\n    background-color: var(--bg_base);\n}\n\n.indicator-mute::indicator:hover,\n.indicator-mute::indicator:unchecked:hover {\n    icon-size: var(--icon_base_mixer);\n    border: none;\n}\n\n#contextContainer {\n  background-color: var(--bg_window);\n}\n\nVolumeMeter {\n    qproperty-backgroundNominalColor: rgb(38,127,38);\n    qproperty-backgroundWarningColor: rgb(127,127,38);\n    qproperty-backgroundErrorColor: rgb(127,38,38);\n    qproperty-foregroundNominalColor: rgb(76,255,76);\n    qproperty-foregroundWarningColor: rgb(255,255,76);\n    qproperty-foregroundErrorColor: rgb(255,76,76);\n    qproperty-magnitudeColor: rgb(0,0,0);\n    qproperty-majorTickColor: var(--text);\n    qproperty-minorTickColor: rgb(122,121,122); /* light */\n    qproperty-meterThickness: 3;\n}\n\nOBSBasicStats {\n    background: var(--bg_window);\n}\n"
  },
  {
    "path": "frontend/data/themes/Yami_Default.ovt",
    "content": "@OBSThemeMeta {\n    name: 'Default';\n    id: 'com.obsproject.Yami.Original';\n    extends: 'com.obsproject.Yami';\n    author: 'Warchamp7';\n    dark: 'true';\n}\n"
  },
  {
    "path": "frontend/data/themes/Yami_Grey.ovt",
    "content": "@OBSThemeMeta {\n    name: 'Grey';\n    id: 'com.obsproject.Yami.Grey';\n    extends: 'com.obsproject.Yami';\n    author: 'Warchamp7';\n    dark: 'true';\n}\n\n@OBSThemeVars {\n    --grey1: rgb(97,97,97);\n    --grey2: rgb(87,87,87);\n    --grey3: rgb(77,77,77);\n    --grey4: rgb(67,67,67);\n    --grey5: rgb(57,57,57);\n    --grey6: rgb(47,47,47);\n    --grey7: rgb(33,33,33);\n    --grey8: rgb(21,21,21);\n}\n"
  },
  {
    "path": "frontend/data/themes/Yami_Light.ovt",
    "content": "@OBSThemeMeta {\n    name: 'Light';\n    id: 'com.obsproject.Yami.Light';\n    extends: 'com.obsproject.Yami';\n    author: 'Warchamp7';\n    dark: 'false';\n}\n\n@OBSThemeVars {\n    --grey1: rgb(140,140,140);\n    --grey2: rgb(254,254,254);\n    --grey3: rgb(254,254,254);\n    --grey4: rgb(243,243,243);\n    --grey5: rgb(236,236,236);\n    --grey6: rgb(229,229,229);\n    --grey7: rgb(211,211,211);\n    --grey8: rgb(193,193,193);\n\n    --primary: rgb(140,181,255);\n    --primary_light: rgb(178,207,255);\n    --primary_dark: rgb(22,31,65);\n\n    --bg_window: var(--grey7);\n    --bg_base: var(--grey6);\n    --bg_preview: var(--grey8);\n\n    --text: var(--black1);\n    --text_light: var(--black3);\n    --text_muted: var(--black4);\n\n    --text_disabled: var(--text_muted);\n    --text_inactive: var(--text_light);\n\n    --input_bg_hover: var(--grey3);\n    --input_bg_focus: var(--grey3);\n\n    --button_bg_disabled: var(--grey7);\n\n    --separator_hover: var(--black1);\n\n    --scrollbar_handle: var(--grey3);\n    --scrollbar_hover: var(--grey6);\n    --scrollbar_down: var(--grey8);\n    --scrollbar_border: var(--grey7);\n}\n\n/* Mute Button */\n\n.btn-mute {\n    qproperty-icon: url(theme:Light/settings/audio.svg);\n}\n\n.btn-mute.checked {\n    qproperty-icon: url(theme:Light/mute.svg);\n}\n\n/* Monitor Button */\n\n.btn-monitor {\n    qproperty-icon: url(theme:Light/headphones-off.svg);\n}\n\n.btn-monitor.checked {\n    qproperty-icon: url(theme:Light/headphones.svg);\n}\n\nVolumeMeter {\n    qproperty-backgroundNominalColor: var(--green4);\n    qproperty-backgroundWarningColor: var(--yellow4);\n    qproperty-backgroundErrorColor: var(--red4);\n    qproperty-foregroundNominalColor: var(--green1);\n    qproperty-foregroundWarningColor: var(--yellow1);\n    qproperty-foregroundErrorColor: var(--red1);\n    qproperty-magnitudeColor: rgb(0,0,0);\n    qproperty-majorTickColor: var(--black1);\n    qproperty-minorTickColor: var(--black5);\n}\n\nQMenu::right-arrow {\n    image: url(theme:Light/expand.svg);\n}\n\nQDockWidget {\n    titlebar-close-icon: url(theme:Light/close.svg);\n    titlebar-normal-icon: url(theme:Light/popout.svg);\n}\n\nQPushButton#sourcePropertiesButton {\n    qproperty-icon: url(theme:Light/settings/general.svg);\n}\n\nQPushButton#sourceFiltersButton {\n    qproperty-icon: url(theme:Light/filter.svg);\n}\n\n.icon-plus {\n    qproperty-icon: url(theme:Light/plus.svg);\n}\n\n.icon-minus {\n    qproperty-icon: url(theme:Light/minus.svg);\n}\n\n.icon-trash {\n    qproperty-icon: url(theme:Light/trash.svg);\n}\n\n.icon-clear {\n    qproperty-icon: url(theme:Light/entry-clear.svg);\n}\n\n.icon-gear {\n    qproperty-icon: url(theme:Light/settings/general.svg);\n}\n\n.icon-dots-vert {\n    qproperty-icon: url(theme:Light/dots-vert.svg);\n}\n\n.icon-refresh {\n    qproperty-icon: url(theme:Light/refresh.svg);\n}\n\n.icon-cogs {\n    qproperty-icon: url(theme:Light/cogs.svg);\n}\n\n.icon-touch {\n    qproperty-icon: url(theme:Light/interact.svg);\n}\n\n.icon-up {\n    qproperty-icon: url(theme:Light/up.svg);\n}\n\n.icon-down {\n    qproperty-icon: url(theme:Light/down.svg);\n}\n\n.icon-filter {\n    qproperty-icon: url(theme:Light/filter.svg);\n}\n\n.icon-revert {\n    qproperty-icon: url(theme:Light/revert.svg);\n}\n\n.icon-close {\n    qproperty-icon: url(theme:Light/close.svg);\n}\n\n.icon-layout-vertical {\n    qproperty-icon: url(theme:Light/layout-vertical.svg);\n}\n\n.icon-layout-horizontal {\n    qproperty-icon: url(theme:Light/layout-horizontal.svg);\n}\n\nQToolBarExtension {\n    qproperty-icon: url(theme:Light/dots-vert.svg);\n}\n\nQComboBox::down-arrow,\nQDateTimeEdit::down-arrow {\n    image: url(theme:Light/collapse.svg);\n}\n\nQComboBox::down-arrow:editable,\nQDateTimeEdit::down-arrow:editable {\n    image: url(theme:Light/collapse.svg);\n}\n\nQSpinBox::up-arrow, QDoubleSpinBox::up-arrow {\n    image: url(theme:Light/up.svg);\n}\n\nQSpinBox::down-arrow, QDoubleSpinBox::down-arrow {\n    image: url(theme:Light/down.svg);\n}\n\nQPushButton::menu-indicator {\n    image: url(theme:Light/down.svg);\n}\n\nOBSBasicSettings {\n    qproperty-generalIcon: url(theme:Light/settings/general.svg);\n    qproperty-appearanceIcon: url(theme:Light/settings/appearance.svg);\n    qproperty-streamIcon: url(theme:Light/settings/stream.svg);\n    qproperty-outputIcon: url(theme:Light/settings/output.svg);\n    qproperty-audioIcon: url(theme:Light/settings/audio.svg);\n    qproperty-videoIcon: url(theme:Light/settings/video.svg);\n    qproperty-hotkeysIcon: url(theme:Light/settings/hotkeys.svg);\n    qproperty-accessibilityIcon: url(theme:Light/settings/accessibility.svg);\n    qproperty-advancedIcon: url(theme:Light/settings/advanced.svg);\n}\n\nQCheckBox::indicator:unchecked,\nQGroupBox::indicator:unchecked,\nQTableView::indicator:unchecked {\n    image: url(theme:Light/checkbox_unchecked.svg);\n}\n\nQCheckBox::indicator:unchecked:hover,\nQGroupBox::indicator:unchecked:hover,\nQTableView::indicator:unchecked:hover {\n    image: url(theme:Light/checkbox_unchecked_focus.svg);\n}\n\nQCheckBox::indicator:checked,\nQGroupBox::indicator:checked,\nQTableView::indicator:checked {\n    image: url(theme:Light/checkbox_checked.svg);\n}\n\nQCheckBox::indicator:checked:hover,\nQGroupBox::indicator:checked:hover,\nQTableView::indicator:checked:hover {\n    image: url(theme:Light/checkbox_checked_focus.svg);\n}\n\nQCheckBox::indicator:checked:disabled,\nQGroupBox::indicator:checked:disabled,\nQTableView::indicator:checked:disabled {\n    image: url(theme:Light/checkbox_checked_disabled.svg);\n}\n\nQCheckBox::indicator:unchecked:disabled,\nQGroupBox::indicator:unchecked:disabled,\nQTableView::indicator:unchecked:disabled {\n    image: url(theme:Light/checkbox_unchecked_disabled.svg);\n}\n\n.indicator-lock::indicator:checked,\n.indicator-lock::indicator:checked:hover {\n    image: url(theme:Light/locked.svg);\n}\n\n.indicator-visibility::indicator:checked,\n.indicator-visibility::indicator:checked:hover {\n    image: url(theme:Light/visible.svg);\n}\n\n.indicator-mute::indicator:checked {\n    image: url(theme:Light/mute.svg);\n}\n\n.indicator-mute::indicator:unchecked {\n    image: url(theme:Light/settings/audio.svg);\n}\n\n.indicator-mute::indicator:unchecked:hover {\n    image: url(theme:Light/settings/audio.svg);\n}\n\n.indicator-mute::indicator:unchecked:focus {\n    image: url(theme:Light/settings/audio.svg);\n}\n\n.indicator-mute::indicator:checked:hover {\n    image: url(theme:Light/mute.svg);\n}\n\n.indicator-mute::indicator:checked:focus {\n    image: url(theme:Light/mute.svg);\n}\n\n.indicator-mute::indicator:checked:disabled {\n    image: url(theme:Light/mute.svg);\n}\n\n.indicator-mute::indicator:unchecked:disabled {\n    image: url(theme:Light/settings/audio.svg);\n}\n\n.indicator-expand::indicator:checked,\n.indicator-expand::indicator:checked:hover {\n    image: url(theme:Light/expand.svg);\n}\n\n.indicator-expand::indicator:unchecked,\n.indicator-expand::indicator:unchecked:hover {\n    image: url(theme:Light/collapse.svg);\n}\n\n/* Source Icons */\nOBSBasic {\n    qproperty-imageIcon: url(theme:Light/sources/image.svg);\n    qproperty-colorIcon: url(theme:Light/sources/brush.svg);\n    qproperty-slideshowIcon: url(theme:Light/sources/slideshow.svg);\n    qproperty-audioInputIcon: url(theme:Light/sources/microphone.svg);\n    qproperty-audioOutputIcon: url(theme:Light/settings/audio.svg);\n    qproperty-desktopCapIcon: url(theme:Light/settings/video.svg);\n    qproperty-windowCapIcon: url(theme:Light/sources/window.svg);\n    qproperty-gameCapIcon: url(theme:Light/sources/gamepad.svg);\n    qproperty-cameraIcon: url(theme:Light/sources/camera.svg);\n    qproperty-textIcon: url(theme:Light/sources/text.svg);\n    qproperty-mediaIcon: url(theme:Light/sources/media.svg);\n    qproperty-browserIcon: url(theme:Light/sources/globe.svg);\n    qproperty-groupIcon: url(theme:Light/sources/group.svg);\n    qproperty-sceneIcon: url(theme:Light/sources/scene.svg);\n    qproperty-defaultIcon: url(theme:Light/sources/default.svg);\n    qproperty-audioProcessOutputIcon: url(theme:Light/sources/windowaudio.svg);\n}\n\n.icon-save {\n    qproperty-icon: url(theme:Light/save.svg);\n}\n\n/* Media icons */\n.icon-media-play {\n    qproperty-icon: url(theme:Light/media/media_play.svg);\n}\n\n.icon-media-pause {\n    qproperty-icon: url(theme:Light/media/media_pause.svg);\n}\n\n.icon-media-restart {\n    qproperty-icon: url(theme:Light/media/media_restart.svg);\n}\n\n.icon-media-stop {\n    qproperty-icon: url(theme:Light/media/media_stop.svg);\n}\n\n.icon-media-next {\n    qproperty-icon: url(theme:Light/media/media_next.svg);\n}\n\n.icon-media-prev {\n    qproperty-icon: url(theme:Light/media/media_previous.svg);\n}\n\n/* YouTube Integration */\nOBSYoutubeActions {\n    qproperty-thumbPlaceholder: url(theme:Light/sources/image.svg);\n}\n\nQDateTimeEdit::down-arrow {\n    image: url(theme:Light/down.svg);\n}\n\n#qt_calendar_monthbutton::menu-indicator {\n    image: url(theme:Light/down.svg);\n}\n\nQCalendarWidget #qt_calendar_prevmonth {\n    qproperty-icon: url(theme:Light/left.svg);\n}\n\nQCalendarWidget #qt_calendar_nextmonth {\n    qproperty-icon: url(theme:Light/right.svg);\n}\n\n/* Idian Widgets */\nidian--ToggleSwitch {\n    qproperty-background: var(--grey8);\n    qproperty-background_hover: var(--grey7);\n    qproperty-background_checked: var(--primary);\n    qproperty-background_checked_hover: var(--primary_light);\n}\n\nidian--Row QComboBox::down-arrow {\n    image: url(theme:Light/collapse.svg);\n}\n\nidian--CheckBox.keyFocus::indicator,\nidian--Row.hover > idian--CheckBox.row-buddy::indicator {\n    image: url(theme:Light/checkbox_unchecked_focus.svg);\n}\n\nidian--CheckBox.keyFocus.checked::indicator,\nidian--Row.hover > idian--CheckBox.row-buddy.checked::indicator {\n    image: url(theme:Light/checkbox_checked_focus.svg);\n}\n\nidian--ExpandButton::indicator {\n    image: url(theme:Light/down.svg);\n}\n\nidian--ExpandButton::indicator:checked {\n    image: url(theme:Light/up.svg);\n}\n"
  },
  {
    "path": "frontend/data/themes/Yami_Rachni.ovt",
    "content": "@OBSThemeMeta {\n    name: 'Rachni';\n    id: 'com.obsproject.Yami.Rachni';\n    extends: 'com.obsproject.Yami';\n    author: 'Warchamp7';\n    dark: 'true';\n}\n\n@OBSThemeVars {\n    --grey1: rgb(118,121,124);\n    --grey2: rgb(110,113,114);\n    --grey3: rgb(59,65,71);\n    --grey4: rgb(49,54,59);\n    --grey5: rgb(42,46,50);\n    --grey6: rgb(35,38,41);\n    --grey7: rgb(25,27,38);\n    --grey8: rgb(21,22,23);\n\n    --primary: rgb(145,76,103);\n    --primary_light: rgb(0,188,212);\n    --primary_lighter: rgb(240,96,146);\n    --primary_dark: rgb(89,66,79);\n    --primary_darker: rgb(25,27,38);\n\n    --bg_base: var(--grey6);\n    --bg_window: var(--grey4);\n    --bg_preview: rgb(34,37,40);\n\n    --border_color: var(--grey5);\n\n    --input_bg: var(--grey6);\n    --input_bg_hover: var(--grey7);\n    --input_bg_focus: var(--grey7);\n\n    --list_item_bg_selected: var(--primary_light);\n    --list_item_bg_hover: var(--primary);\n\n    --input_border: var(--grey1);\n    --input_border_hover: var(--primary_light);\n    --input_border_focus: var(--primary_light);\n\n    --spacing_input: var(--spacing_base);\n\n    --button_bg: var(--primary_light);\n    --button_bg_hover: var(--primary);\n    --button_bg_down: var(--primary_dark);\n    --button_bg_disabled: var(--grey6);\n\n    --button_bg_red: rgb(88,22,36);\n    --button_bg_red_hover: rgb(116,32,49);\n    --button_bg_red_down: rgb(63,21,30);\n\n    --button_border: var(--primary_light);\n    --button_border_hover: var(--button_bg_hover);\n    --button_border_focus: var(--button_bg_hover);\n\n    --tab_bg: var(--input_bg);\n    --tab_bg_hover: var(--grey3);\n    --tab_bg_down: var(--grey7);\n    --tab_bg_disabled: var(--grey6);\n\n    --tab_border: var(--grey3);\n    --tab_border_hover: var(--grey1);\n    --tab_border_focus: var(--grey1);\n    --tab_border_selected: var(--primary);\n\n    --scrollbar_handle: var(--grey2);\n    --scrollbar_hover: var(--primary);\n    --scrollbar_down: var(--grey3);\n    --scrollbar_border: var(--primary);\n\n    --toolbutton_bg: var(--grey3);\n    --toolbutton_bg_hover: var(--primary);\n    --toolbutton_bg_down: var(--primary_dark);\n    --toolbutton_bg_disabled: var(--grey4);\n}\n\nQDockWidget::title {\n    background-color: var(--bg_base);\n}\n\nQDockWidget::close-button:hover,\nQDockWidget::float-button:hover {\n    background: var(--grey3);\n}\n\nQComboBox,\nQDateTimeEdit,\nQLineEdit,\nQPlainTextEdit,\nQTextEdit,\nQSpinBox,\nQDoubleSpinBox {\n    border-width: 2px;\n    border-color: var(--input_border);\n}\n\nQPushButton {\n    border-width: 2px;\n}\n\nQMenu::item:selected,\nQListWidget::item:selected,\nSceneTree::item:selected,\nSourceTree::item:selected {\n    background-color: var(--primary_dark);\n}\n\nQMenu::item:hover,\nQListWidget::item:hover,\nSceneTree::item:hover,\nSourceTree::item:hover,\nQMenu::item:selected:hover,\nQListWidget::item:selected:hover,\nSceneTree::item:selected:hover,\nSourceTree::item:selected:hover {\n    background-color: var(--primary_light);\n}\n\nQToolBar {\n    background-color: var(--grey4);\n    padding: var(--padding_large);\n    margin: 0;\n}\n\nQPushButton:disabled {\n    border-color: var(--grey3);\n}\n\nQPushButton:checked,\nQPushButton.state-active {\n    border-color: var(--primary_lighter);\n}\n\nQPushButton:checked:hover,\nQPushButton:checked:focus {\n    border-color: var(--primary_light);\n}\n\nQToolButton,\nQPushButton[toolButton=\"true\"] {\n    background-color: var(--toolbutton_bg);\n    border-color: var(--toolbutton_bg);\n}\n\nQToolButton:hover,\nQToolButton:focus {\n    border-color: var(--primary);\n    background-color: var(--primary);\n}\n\nQToolButton:pressed,\nQToolButton:pressed:hover {\n    background-color: var(--toolbutton_bg_down);\n    border-color: var(--toolbutton_bg_down);\n}\n\n.indicator-mute::indicator,\n.indicator-mute::indicator:unchecked,\n.indicator-mute::indicator:focus {\n    background-color: var(--toolbutton_bg);\n    border: 1px solid var(--toolbutton_bg);\n}\n\n.indicator-mute::indicator:hover,\n.indicator-mute::indicator:unchecked:hover {\n    background-color: var(--toolbutton_bg_hover);\n    border: 1px solid var(--toolbutton_bg_hover);\n}\n\n.indicator-mute::indicator:pressed,\n.indicator-mute::indicator:pressed:hover {\n    background-color: var(--toolbutton_bg_down);\n    border-color: var(--toolbutton_bg_down);\n}\n\nQToolButton:disabled,\nQPushButton[toolButton=\"true\"]:disabled {\n    background-color: var(--toolbutton_bg_disabled);\n    border-color: transparent;\n}\n\nQTabBar::tab {\n    background-color: var(--grey5);\n}\n\nQTabBar::tab:top:selected {\n    border-bottom: 2px solid var(--primary);\n}\n\nQTabBar QToolButton {\n    background-color: var(--grey5);\n}\n\nQGroupBox {\n    background-color: var(--grey3);\n}\n\nOBSBasicSettings #PropertiesContainer {\n    background-color: var(--grey3);\n}\n\nQGroupBox::title {\n    color: var(--primary_lighter);\n}\n\nQSlider::groove {\n    background-color: var(--grey2);\n}\n\nVolumeMeter {\n    qproperty-backgroundNominalColor: rgb(0,128,79);\n    qproperty-backgroundWarningColor: rgb(128,57,0);\n    qproperty-backgroundErrorColor: rgb(128,9,0);\n    qproperty-foregroundNominalColor: rgb(119,255,143);\n    qproperty-foregroundWarningColor: rgb(255,157,76);\n    qproperty-foregroundErrorColor: rgb(255,89,76);\n    qproperty-magnitudeColor: palette(window);\n    qproperty-majorTickColor: palette(window-text);\n    qproperty-minorTickColor: palette(mid);\n}\n"
  },
  {
    "path": "frontend/dialogs/LogUploadDialog.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"LogUploadDialog.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <QClipboard>\n#include <QDesktopServices>\n#include <QTimer>\n#include <QUrlQuery>\n\n#include \"moc_LogUploadDialog.cpp\"\n\nstruct DialogPage {\n\tstatic constexpr int Start = 0;\n\tstatic constexpr int Success = 1;\n\tstatic constexpr int Error = 2;\n};\n\nnamespace OBS {\nLogUploadDialog::LogUploadDialog(QWidget *parent, LogFileType uploadType)\n\t: QDialog(parent),\n\t  ui(new Ui::LogUploadDialog),\n\t  uploadType_(uploadType)\n{\n\tuploadStatusTimer_ = std::make_unique<QTimer>(this);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\tui->setupUi(this);\n\n\tui->stackedWidget->setCurrentIndex(DialogPage::Start);\n\n\tui->privacyNotice->setText(QTStr(\"LogUploadDialog.Labels.PrivacyNotice\"));\n\n\tif (uploadType_ == LogFileType::CrashLog) {\n\t\tui->analyzeURL->hide();\n\t\tui->description->setText(Str(\"LogUploadDialog.Labels.Description.CrashLog\"));\n\t}\n\n\tconnect(ui->confirmUploadButton, &QPushButton::clicked, this, &LogUploadDialog::startLogUpload);\n\tconnect(ui->retryButton, &QPushButton::clicked, this, &LogUploadDialog::startLogUpload);\n\tconnect(ui->copyURL, &QPushButton::clicked, this, &LogUploadDialog::copyToClipBoard);\n\tconnect(ui->analyzeURL, &QPushButton::clicked, this, &LogUploadDialog::openAnalyzeURL);\n\tconnect(ui->closeButton, &QPushButton::clicked, this, &QDialog::reject);\n\tconnect(ui->retryCloseButton, &QPushButton::clicked, this, &QDialog::reject);\n\n\tOBSApp *app = App();\n\tconnect(app, &OBSApp::logUploadFinished, this, &LogUploadDialog::handleUploadSuccess);\n\tconnect(app, &OBSApp::logUploadFailed, this, &LogUploadDialog::handleUploadFailure);\n\n\tinstallEventFilter(CreateShortcutFilter());\n\n\tLogFileState uploadState = app->getLogFileState(uploadType);\n\tswitch (uploadState) {\n\tcase LogFileState::Uploaded:\n\t\tstartLogUpload();\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid LogUploadDialog::startLogUpload()\n{\n\tif (uploadType_ == LogFileType::NoType) {\n\t\treturn;\n\t}\n\n\tui->confirmUploadButton->setEnabled(false);\n\n\tconnect(uploadStatusTimer_.get(), &QTimer::timeout, this, [this]() {\n\t\tui->uploadProgress->setText(Str(\"LogUploadDialog.Labels.Progress\"));\n\t\tsetCursor(Qt::WaitCursor);\n\t});\n\n\tuploadStatusTimer_->setSingleShot(true);\n\tuploadStatusTimer_->start(1000);\n\n\tOBSApp *app = App();\n\n\tswitch (uploadType_) {\n\tcase LogFileType::CrashLog:\n\t\tapp->uploadLastCrashLog();\n\t\tbreak;\n\tcase LogFileType::CurrentAppLog:\n\t\tapp->uploadCurrentAppLog();\n\t\tbreak;\n\tcase LogFileType::LastAppLog:\n\t\tapp->uploadLastAppLog();\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid LogUploadDialog::handleUploadSuccess(LogFileType, const QString &fileURL)\n{\n\tuploadStatusTimer_->stop();\n\tunsetCursor();\n\tui->confirmUploadButton->setEnabled(true);\n\tui->uploadProgress->setText(\"\");\n\n\tui->urlEdit->setText(fileURL);\n\tui->stackedWidget->setCurrentIndex(DialogPage::Success);\n}\n\nvoid LogUploadDialog::handleUploadFailure(LogFileType, const QString &errorMessage)\n{\n\tuploadStatusTimer_->stop();\n\tunsetCursor();\n\tui->confirmUploadButton->setEnabled(true);\n\tui->uploadProgress->setText(\"\");\n\n\tQString errorDescription = QTStr(\"LogUploadDialog.Errors.Template\").arg(errorMessage);\n\tui->uploadErrorMessage->setText(errorDescription);\n\tui->stackedWidget->setCurrentIndex(DialogPage::Error);\n}\n\nvoid LogUploadDialog::copyToClipBoard() const\n{\n\tQClipboard *clipboard = QApplication::clipboard();\n\tclipboard->setText(ui->urlEdit->text());\n}\n\nvoid LogUploadDialog::openAnalyzeURL() const\n{\n\tQUrlQuery queryParameters;\n\tqueryParameters.addQueryItem(\"log_url\", QUrl::toPercentEncoding(ui->urlEdit->text()));\n\tQUrl analyzerUrl = QUrl(\"https://obsproject.com/tools/analyzer\", QUrl::TolerantMode);\n\n\tanalyzerUrl.setQuery(queryParameters);\n\n\tQDesktopServices::openUrl(analyzerUrl);\n}\n} // namespace OBS\n"
  },
  {
    "path": "frontend/dialogs/LogUploadDialog.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_LogUploadDialog.h\"\n\n#include <QDialog>\n\n#include <filesystem>\n\nclass QTimer;\n\nnamespace OBS {\n\nenum class LogFileType;\n\nclass LogUploadDialog : public QDialog {\n\tQ_OBJECT\n\nprivate:\n\tstd::unique_ptr<Ui::LogUploadDialog> ui;\n\tstd::unique_ptr<QTimer> uploadStatusTimer_;\n\n\tLogFileType uploadType_;\n\npublic:\n\tLogUploadDialog(QWidget *parent, LogFileType uploadType);\n\nprivate slots:\n\tvoid startLogUpload();\n\tvoid handleUploadSuccess(LogFileType uploadType, const QString &fileURL);\n\tvoid handleUploadFailure(LogFileType uploadType, const QString &errorMessage);\n\n\tvoid copyToClipBoard() const;\n\tvoid openAnalyzeURL() const;\n};\n} // namespace OBS\n"
  },
  {
    "path": "frontend/dialogs/NameDialog.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"NameDialog.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <QCheckBox>\n#include <QDialogButtonBox>\n#include <QLabel>\n#include <QLineEdit>\n#include <QVBoxLayout>\n\n#include \"moc_NameDialog.cpp\"\n\nNameDialog::NameDialog(QWidget *parent) : QDialog(parent)\n{\n\tinstallEventFilter(CreateShortcutFilter());\n\tsetModal(true);\n\tsetWindowModality(Qt::WindowModality::WindowModal);\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\tsetFixedWidth(555);\n\tsetMinimumHeight(100);\n\tQVBoxLayout *layout = new QVBoxLayout;\n\tsetLayout(layout);\n\n\tlabel = new QLabel(this);\n\tlayout->addWidget(label);\n\tlabel->setText(\"Set Text\");\n\n\tuserText = new QLineEdit(this);\n\tlayout->addWidget(userText);\n\n\tcheckbox = new QCheckBox(this);\n\tlayout->addWidget(checkbox);\n\n\tQDialogButtonBox *buttonbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);\n\tlayout->addWidget(buttonbox);\n\tbuttonbox->setCenterButtons(true);\n\n\tuserText->setFocus();\n\n\tconnect(buttonbox, &QDialogButtonBox::accepted, this, &QDialog::accept);\n\tconnect(buttonbox, &QDialogButtonBox::rejected, this, &QDialog::reject);\n\n\tlabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);\n\tadjustSize();\n}\n\nstatic bool IsWhitespace(char ch)\n{\n\treturn ch == ' ' || ch == '\\t';\n}\n\nstatic void CleanWhitespace(std::string &str)\n{\n\twhile (str.size() && IsWhitespace(str.back()))\n\t\tstr.erase(str.end() - 1);\n\twhile (str.size() && IsWhitespace(str.front()))\n\t\tstr.erase(str.begin());\n}\n\nbool NameDialog::AskForName(QWidget *parent, const QString &title, const QString &text, std::string &userTextInput,\n\t\t\t    const QString &placeHolder, int maxSize)\n{\n\tif (maxSize <= 0 || maxSize > 32767)\n\t\tmaxSize = 170;\n\n\tNameDialog dialog(parent);\n\tdialog.setWindowTitle(title);\n\n\tdialog.checkbox->setHidden(true);\n\tdialog.label->setText(text);\n\tdialog.userText->setMaxLength(maxSize);\n\tdialog.userText->setText(placeHolder);\n\tdialog.userText->selectAll();\n\n\tif (dialog.exec() != DialogCode::Accepted) {\n\t\treturn false;\n\t}\n\tuserTextInput = dialog.userText->text().toUtf8().constData();\n\tCleanWhitespace(userTextInput);\n\treturn true;\n}\n\nbool NameDialog::AskForNameWithOption(QWidget *parent, const QString &title, const QString &text,\n\t\t\t\t      std::string &userTextInput, const QString &optionLabel, bool &optionChecked,\n\t\t\t\t      const QString &placeHolder)\n{\n\tNameDialog dialog(parent);\n\tdialog.setWindowTitle(title);\n\n\tdialog.label->setText(text);\n\tdialog.userText->setMaxLength(170);\n\tdialog.userText->setText(placeHolder);\n\tdialog.checkbox->setText(optionLabel);\n\tdialog.checkbox->setChecked(optionChecked);\n\n\tif (dialog.exec() != DialogCode::Accepted) {\n\t\treturn false;\n\t}\n\n\tuserTextInput = dialog.userText->text().toUtf8().constData();\n\tCleanWhitespace(userTextInput);\n\toptionChecked = dialog.checkbox->isChecked();\n\treturn true;\n}\n"
  },
  {
    "path": "frontend/dialogs/NameDialog.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QDialog>\n\nclass QCheckBox;\nclass QLabel;\nclass QLineEdit;\nclass QString;\n\nclass NameDialog : public QDialog {\n\tQ_OBJECT\n\npublic:\n\tNameDialog(QWidget *parent);\n\n\t// Returns true if user clicks OK, false otherwise\n\t// userTextInput returns string that user typed into dialog\n\tstatic bool AskForName(QWidget *parent, const QString &title, const QString &text, std::string &userTextInput,\n\t\t\t       const QString &placeHolder = QString(\"\"), int maxSize = 170);\n\n\t// Returns true if user clicks OK, false otherwise\n\t// userTextInput returns string that user typed into dialog\n\t// userOptionReturn the checkbox was ticked user accepted\n\tstatic bool AskForNameWithOption(QWidget *parent, const QString &title, const QString &text,\n\t\t\t\t\t std::string &userTextInput, const QString &optionLabel, bool &optionChecked,\n\t\t\t\t\t const QString &placeHolder = QString(\"\"));\n\nprivate:\n\tQLabel *label;\n\tQLineEdit *userText;\n\tQCheckBox *checkbox;\n};\n"
  },
  {
    "path": "frontend/dialogs/OAuthLogin.cpp",
    "content": "#include \"OAuthLogin.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#ifdef BROWSER_AVAILABLE\n#include <browser-panel.hpp>\n#endif\n#include <ui-config.h>\n\n#include \"moc_OAuthLogin.cpp\"\n\n#ifdef BROWSER_AVAILABLE\nextern QCef *cef;\nextern QCefCookieManager *panel_cookies;\n#endif\n\nOAuthLogin::OAuthLogin(QWidget *parent, const std::string &url, bool token) : QDialog(parent), get_token(token)\n{\n#ifdef BROWSER_AVAILABLE\n\tif (!cef) {\n\t\treturn;\n\t}\n\n\tsetWindowTitle(\"Auth\");\n\tsetMinimumSize(400, 400);\n\tresize(700, 700);\n\n\tQt::WindowFlags flags = windowFlags();\n\tQt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint;\n\tsetWindowFlags(flags & (~helpFlag));\n\n\tOBSBasic::InitBrowserPanelSafeBlock();\n\n\tcefWidget = cef->create_widget(this, url, panel_cookies);\n\tif (!cefWidget) {\n\t\tfail = true;\n\t\treturn;\n\t}\n\n\tconnect(cefWidget, &QCefWidget::titleChanged, this, &OAuthLogin::setWindowTitle);\n\tconnect(cefWidget, &QCefWidget::urlChanged, this, &OAuthLogin::urlChanged);\n\n\tQPushButton *close = new QPushButton(QTStr(\"Cancel\"));\n\tconnect(close, &QAbstractButton::clicked, this, &QDialog::reject);\n\n\tQHBoxLayout *bottomLayout = new QHBoxLayout();\n\tbottomLayout->addStretch();\n\tbottomLayout->addWidget(close);\n\tbottomLayout->addStretch();\n\n\tQVBoxLayout *topLayout = new QVBoxLayout(this);\n\ttopLayout->addWidget(cefWidget);\n\ttopLayout->addLayout(bottomLayout);\n#else\n\tUNUSED_PARAMETER(url);\n#endif\n}\n\nOAuthLogin::~OAuthLogin() {}\n\nint OAuthLogin::exec()\n{\n#ifdef BROWSER_AVAILABLE\n\tif (cefWidget) {\n\t\treturn QDialog::exec();\n\t}\n#endif\n\treturn QDialog::Rejected;\n}\n\nvoid OAuthLogin::reject()\n{\n#ifdef BROWSER_AVAILABLE\n\tif (cefWidget) {\n\t\tcefWidget->closeBrowser();\n\t}\n#endif\n\tQDialog::reject();\n}\n\nvoid OAuthLogin::accept()\n{\n#ifdef BROWSER_AVAILABLE\n\tif (cefWidget) {\n\t\tcefWidget->closeBrowser();\n\t}\n#endif\n\tQDialog::accept();\n}\n\nvoid OAuthLogin::urlChanged(const QString &url)\n{\n\tstd::string uri = get_token ? \"access_token=\" : \"code=\";\n\tint code_idx = url.indexOf(uri.c_str());\n\tif (code_idx == -1)\n\t\treturn;\n\n\tif (!url.startsWith(OAUTH_BASE_URL))\n\t\treturn;\n\n\tcode_idx += (int)uri.size();\n\n\tint next_idx = url.indexOf(\"&\", code_idx);\n\tif (next_idx != -1)\n\t\tcode = url.mid(code_idx, next_idx - code_idx);\n\telse\n\t\tcode = url.right(url.size() - code_idx);\n\n\taccept();\n}\n"
  },
  {
    "path": "frontend/dialogs/OAuthLogin.hpp",
    "content": "#pragma once\n\n#include <QDialog>\n\nclass QCefWidget;\n\nclass OAuthLogin : public QDialog {\n\tQ_OBJECT\n\n\tQCefWidget *cefWidget = nullptr;\n\tQString code;\n\tbool get_token = false;\n\tbool fail = false;\n\npublic:\n\tOAuthLogin(QWidget *parent, const std::string &url, bool token);\n\t~OAuthLogin();\n\n\tinline QString GetCode() const { return code; }\n\tinline bool LoadFail() const { return fail; }\n\n\tvirtual int exec() override;\n\tvirtual void reject() override;\n\tvirtual void accept() override;\n\npublic slots:\n\tvoid urlChanged(const QString &url);\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSAbout.cpp",
    "content": "#include \"OBSAbout.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n#include <utility/RemoteTextThread.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <json11.hpp>\n\n#include \"moc_OBSAbout.cpp\"\n\nusing namespace json11;\n\nextern bool steam;\n\nOBSAbout::OBSAbout(QWidget *parent) : QDialog(parent), ui(new Ui::OBSAbout)\n{\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\n\tQString bitness;\n\n\tif (sizeof(void *) == 4)\n\t\tbitness = \" (32 bit)\";\n\telse if (sizeof(void *) == 8)\n\t\tbitness = \" (64 bit)\";\n\n\tQString ver = obs_get_version_string();\n\n\tui->version->setText(ver + bitness);\n\n\tui->contribute->setText(QTStr(\"About.Contribute\"));\n\n\tif (steam) {\n\t\tdelete ui->donate;\n\t} else {\n\t\tui->donate->setText(\"&nbsp;&nbsp;<a href='https://obsproject.com/contribute'>\" + QTStr(\"About.Donate\") +\n\t\t\t\t    \"</a>\");\n\t\tui->donate->setTextInteractionFlags(Qt::TextBrowserInteraction);\n\t\tui->donate->setOpenExternalLinks(true);\n\t}\n\n\tui->getInvolved->setText(\n\t\t\"&nbsp;&nbsp;<a href='https://github.com/obsproject/obs-studio/blob/master/CONTRIBUTING.rst'>\" +\n\t\tQTStr(\"About.GetInvolved\") + \"</a>\");\n\tui->getInvolved->setTextInteractionFlags(Qt::TextBrowserInteraction);\n\tui->getInvolved->setOpenExternalLinks(true);\n\n\tui->about->setText(\"<a href='#'>\" + QTStr(\"About\") + \"</a>\");\n\tui->authors->setText(\"<a href='#'>\" + QTStr(\"About.Authors\") + \"</a>\");\n\tui->license->setText(\"<a href='#'>\" + QTStr(\"About.License\") + \"</a>\");\n\n\tui->name->setProperty(\"class\", \"text-heading\");\n\tui->version->setProperty(\"class\", \"text-large\");\n\tui->about->setProperty(\"class\", \"bg-base\");\n\tui->authors->setProperty(\"class\", \"bg-base\");\n\tui->license->setProperty(\"class\", \"bg-base\");\n\tui->info->setProperty(\"class\", \"\");\n\n\tconnect(ui->about, &ClickableLabel::clicked, this, &OBSAbout::ShowAbout);\n\tconnect(ui->authors, &ClickableLabel::clicked, this, &OBSAbout::ShowAuthors);\n\tconnect(ui->license, &ClickableLabel::clicked, this, &OBSAbout::ShowLicense);\n\n\tQPointer<OBSAbout> about(this);\n\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main->patronJson.empty() && !main->patronJsonThread) {\n\t\tRemoteTextThread *thread =\n\t\t\tnew RemoteTextThread(\"https://obsproject.com/patreon/about-box.json\", \"application/json\");\n\t\tQObject::connect(thread, &RemoteTextThread::Result, main, &OBSBasic::UpdatePatronJson);\n\t\tQObject::connect(thread, &RemoteTextThread::Result, this, &OBSAbout::ShowAbout);\n\t\tmain->patronJsonThread.reset(thread);\n\t\tthread->start();\n\t} else {\n\t\tShowAbout();\n\t}\n}\n\nvoid OBSAbout::ShowAbout()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tif (main->patronJson.empty())\n\t\treturn;\n\n\tstd::string error;\n\tJson json = Json::parse(main->patronJson, error);\n\tconst Json::array &patrons = json.array_items();\n\tQString text;\n\n\ttext += \"<h1>Top Patreon contributors:</h1>\";\n\ttext += \"<p style=\\\"font-size:16px;\\\">\";\n\tbool first = true;\n\tbool top = true;\n\n\tfor (const Json &patron : patrons) {\n\t\tstd::string name = patron[\"name\"].string_value();\n\t\tstd::string link = patron[\"link\"].string_value();\n\t\tint amount = patron[\"amount\"].int_value();\n\n\t\tif (top && amount < 5000) {\n\t\t\ttext += \"</p>\";\n\t\t\ttop = false;\n\t\t} else if (!first) {\n\t\t\ttext += \"<br/>\";\n\t\t}\n\n\t\tif (!link.empty()) {\n\t\t\ttext += \"<a href=\\\"\";\n\t\t\ttext += QT_UTF8(link.c_str()).toHtmlEscaped();\n\t\t\ttext += \"\\\">\";\n\t\t}\n\t\ttext += QT_UTF8(name.c_str()).toHtmlEscaped();\n\t\tif (!link.empty())\n\t\t\ttext += \"</a>\";\n\n\t\tif (first)\n\t\t\tfirst = false;\n\t}\n\n\tui->textBrowser->setHtml(text);\n}\n\nvoid OBSAbout::ShowAuthors()\n{\n\tstd::string path;\n\tQString error = QTStr(\"About.Error\").arg(\"https://github.com/obsproject/obs-studio/blob/master/AUTHORS\");\n\n#ifdef __APPLE__\n\tif (!GetDataFilePath(\"AUTHORS\", path)) {\n#else\n\tif (!GetDataFilePath(\"authors/AUTHORS\", path)) {\n#endif\n\t\tui->textBrowser->setPlainText(error);\n\t\treturn;\n\t}\n\n\tui->textBrowser->setPlainText(QString::fromStdString(path));\n\n\tBPtr<char> text = os_quick_read_utf8_file(path.c_str());\n\n\tif (!text || !*text) {\n\t\tui->textBrowser->setPlainText(error);\n\t\treturn;\n\t}\n\n\tui->textBrowser->setPlainText(QT_UTF8(text));\n}\n\nvoid OBSAbout::ShowLicense()\n{\n\tstd::string path;\n\tQString error = QTStr(\"About.Error\").arg(\"https://github.com/obsproject/obs-studio/blob/master/COPYING\");\n\n\tif (!GetDataFilePath(\"license/gplv2.txt\", path)) {\n\t\tui->textBrowser->setPlainText(error);\n\t\treturn;\n\t}\n\n\tBPtr<char> text = os_quick_read_utf8_file(path.c_str());\n\n\tif (!text || !*text) {\n\t\tui->textBrowser->setPlainText(error);\n\t\treturn;\n\t}\n\n\tui->textBrowser->setPlainText(QT_UTF8(text));\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSAbout.hpp",
    "content": "#pragma once\n\n#include \"ui_OBSAbout.h\"\n\n#include <QDialog>\n\nclass OBSAbout : public QDialog {\n\tQ_OBJECT\n\npublic:\n\texplicit OBSAbout(QWidget *parent = 0);\n\n\tstd::unique_ptr<Ui::OBSAbout> ui;\n\nprivate slots:\n\tvoid ShowAbout();\n\tvoid ShowAuthors();\n\tvoid ShowLicense();\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicAdvAudio.cpp",
    "content": "#include \"OBSBasicAdvAudio.hpp\"\n#include \"ui_OBSAdvAudio.h\"\n\n#include <components/OBSAdvAudioCtrl.hpp>\n#include <utility/item-widget-helpers.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include \"moc_OBSBasicAdvAudio.cpp\"\n\nOBSBasicAdvAudio::OBSBasicAdvAudio(QWidget *parent) : QDialog(parent), ui(new Ui::OBSAdvAudio), showInactive(false)\n{\n\tui->setupUi(this);\n\n\tsignal_handler_t *sh = obs_get_signal_handler();\n\tsigs.emplace_back(sh, \"source_audio_activate\", OBSSourceAdded, this);\n\tsigs.emplace_back(sh, \"source_audio_deactivate\", OBSSourceRemoved, this);\n\tsigs.emplace_back(sh, \"source_activate\", OBSSourceActivated, this);\n\tsigs.emplace_back(sh, \"source_deactivate\", OBSSourceRemoved, this);\n\n\tVolumeType volType = (VolumeType)config_get_int(App()->GetUserConfig(), \"BasicWindow\", \"AdvAudioVolumeType\");\n\n\tif (volType == VolumeType::Percent)\n\t\tui->usePercent->setChecked(true);\n\n\tinstallEventFilter(CreateShortcutFilter());\n\n\t/* enum user scene/sources */\n\tobs_enum_sources(EnumSources, this);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\tsetAttribute(Qt::WA_DeleteOnClose, true);\n}\n\nOBSBasicAdvAudio::~OBSBasicAdvAudio()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tfor (size_t i = 0; i < controls.size(); ++i)\n\t\tdelete controls[i];\n\n\tmain->SaveProject();\n}\n\nbool OBSBasicAdvAudio::EnumSources(void *param, obs_source_t *source)\n{\n\tOBSBasicAdvAudio *dialog = static_cast<OBSBasicAdvAudio *>(param);\n\tuint32_t flags = obs_source_get_output_flags(source);\n\n\tif ((flags & OBS_SOURCE_AUDIO) != 0 &&\n\t    (dialog->showInactive || (obs_source_active(source) && obs_source_audio_active(source))))\n\t\tdialog->AddAudioSource(source);\n\n\treturn true;\n}\n\nvoid OBSBasicAdvAudio::OBSSourceAdded(void *param, calldata_t *calldata)\n{\n\tOBSSource source((obs_source_t *)calldata_ptr(calldata, \"source\"));\n\n\tQMetaObject::invokeMethod(static_cast<OBSBasicAdvAudio *>(param), \"SourceAdded\", Q_ARG(OBSSource, source));\n}\n\nvoid OBSBasicAdvAudio::OBSSourceRemoved(void *param, calldata_t *calldata)\n{\n\tOBSSource source((obs_source_t *)calldata_ptr(calldata, \"source\"));\n\n\tQMetaObject::invokeMethod(static_cast<OBSBasicAdvAudio *>(param), \"SourceRemoved\", Q_ARG(OBSSource, source));\n}\n\nvoid OBSBasicAdvAudio::OBSSourceActivated(void *param, calldata_t *calldata)\n{\n\tOBSSource source((obs_source_t *)calldata_ptr(calldata, \"source\"));\n\n\tif (obs_source_audio_active(source))\n\t\tQMetaObject::invokeMethod(static_cast<OBSBasicAdvAudio *>(param), \"SourceAdded\",\n\t\t\t\t\t  Q_ARG(OBSSource, source));\n}\n\ninline void OBSBasicAdvAudio::AddAudioSource(obs_source_t *source)\n{\n\tfor (size_t i = 0; i < controls.size(); i++) {\n\t\tif (controls[i]->GetSource() == source)\n\t\t\treturn;\n\t}\n\tOBSAdvAudioCtrl *control = new OBSAdvAudioCtrl(ui->mainLayout, source);\n\n\tInsertQObjectByName(controls, control);\n\n\tfor (auto control : controls) {\n\t\tcontrol->ShowAudioControl(ui->mainLayout);\n\t}\n}\n\nvoid OBSBasicAdvAudio::SourceAdded(OBSSource source)\n{\n\tuint32_t flags = obs_source_get_output_flags(source);\n\n\tif ((flags & OBS_SOURCE_AUDIO) == 0)\n\t\treturn;\n\n\tAddAudioSource(source);\n}\n\nvoid OBSBasicAdvAudio::SourceRemoved(OBSSource source)\n{\n\tuint32_t flags = obs_source_get_output_flags(source);\n\n\tif ((flags & OBS_SOURCE_AUDIO) == 0)\n\t\treturn;\n\n\tfor (size_t i = 0; i < controls.size(); i++) {\n\t\tif (controls[i]->GetSource() == source) {\n\t\t\tdelete controls[i];\n\t\t\tcontrols.erase(controls.begin() + i);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid OBSBasicAdvAudio::on_usePercent_toggled(bool checked)\n{\n\tVolumeType type;\n\n\tif (checked)\n\t\ttype = VolumeType::Percent;\n\telse\n\t\ttype = VolumeType::dB;\n\n\tfor (size_t i = 0; i < controls.size(); i++)\n\t\tcontrols[i]->SetVolumeWidget(type);\n\n\tconfig_set_int(App()->GetUserConfig(), \"BasicWindow\", \"AdvAudioVolumeType\", (int)type);\n}\n\nvoid OBSBasicAdvAudio::on_activeOnly_toggled(bool checked)\n{\n\tSetShowInactive(!checked);\n}\n\nvoid OBSBasicAdvAudio::SetShowInactive(bool show)\n{\n\tif (showInactive == show)\n\t\treturn;\n\n\tshowInactive = show;\n\n\tsigs.clear();\n\tsignal_handler_t *sh = obs_get_signal_handler();\n\n\tif (showInactive) {\n\t\tsigs.emplace_back(sh, \"source_create\", OBSSourceAdded, this);\n\t\tsigs.emplace_back(sh, \"source_remove\", OBSSourceRemoved, this);\n\n\t\tobs_enum_sources(EnumSources, this);\n\n\t\tSetIconsVisible(showVisible);\n\t} else {\n\t\tsigs.emplace_back(sh, \"source_audio_activate\", OBSSourceAdded, this);\n\t\tsigs.emplace_back(sh, \"source_audio_deactivate\", OBSSourceRemoved, this);\n\t\tsigs.emplace_back(sh, \"source_activate\", OBSSourceActivated, this);\n\t\tsigs.emplace_back(sh, \"source_deactivate\", OBSSourceRemoved, this);\n\n\t\tfor (size_t i = 0; i < controls.size(); i++) {\n\t\t\tconst auto source = controls[i]->GetSource();\n\t\t\tif (!(obs_source_active(source) && obs_source_audio_active(source))) {\n\t\t\t\tdelete controls[i];\n\t\t\t\tcontrols.erase(controls.begin() + i);\n\t\t\t\ti--;\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid OBSBasicAdvAudio::SetIconsVisible(bool visible)\n{\n\tshowVisible = visible;\n\n\tQLayoutItem *item = ui->mainLayout->itemAtPosition(0, 0);\n\tQLabel *headerLabel = qobject_cast<QLabel *>(item->widget());\n\tvisible ? headerLabel->show() : headerLabel->hide();\n\n\tfor (size_t i = 0; i < controls.size(); i++) {\n\t\tcontrols[i]->SetIconVisible(visible);\n\t}\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicAdvAudio.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QDialog>\n\nclass OBSAdvAudioCtrl;\nclass Ui_OBSAdvAudio;\n\n// \"Basic advanced audio\"?  ...\n\nclass OBSBasicAdvAudio : public QDialog {\n\tQ_OBJECT\n\nprivate:\n\tstd::vector<OBSSignal> sigs;\n\tbool showInactive;\n\tbool showVisible;\n\n\tstd::vector<OBSAdvAudioCtrl *> controls;\n\n\tinline void AddAudioSource(obs_source_t *source);\n\n\tstatic bool EnumSources(void *param, obs_source_t *source);\n\n\tstatic void OBSSourceAdded(void *param, calldata_t *calldata);\n\tstatic void OBSSourceRemoved(void *param, calldata_t *calldata);\n\tstatic void OBSSourceActivated(void *param, calldata_t *calldata);\n\n\tstd::unique_ptr<Ui_OBSAdvAudio> ui;\n\npublic slots:\n\tvoid SourceAdded(OBSSource source);\n\tvoid SourceRemoved(OBSSource source);\n\n\tvoid on_usePercent_toggled(bool checked);\n\tvoid on_activeOnly_toggled(bool checked);\n\npublic:\n\tOBSBasicAdvAudio(QWidget *parent);\n\t~OBSBasicAdvAudio();\n\tvoid SetShowInactive(bool showInactive);\n\tvoid SetIconsVisible(bool visible);\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicFilters.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasicFilters.hpp\"\n\n#include <components/VisibilityItemDelegate.hpp>\n#include <components/VisibilityItemWidget.hpp>\n#include <dialogs/NameDialog.hpp>\n#include <utility/display-helpers.hpp>\n#include <utility/item-widget-helpers.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <properties-view.hpp>\n#include <qt-wrappers.hpp>\n\n#include <QLineEdit>\n\n#ifdef _WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN 1\n#endif\n#include <Windows.h>\n#endif\n\n#include \"moc_OBSBasicFilters.cpp\"\n\nusing namespace std;\n\nOBSBasicFilters::OBSBasicFilters(QWidget *parent, OBSSource source_)\n\t: QDialog(parent),\n\t  ui(new Ui::OBSBasicFilters),\n\t  source(source_),\n\t  addSignal(obs_source_get_signal_handler(source), \"filter_add\", OBSBasicFilters::OBSSourceFilterAdded, this),\n\t  removeSignal(obs_source_get_signal_handler(source), \"filter_remove\", OBSBasicFilters::OBSSourceFilterRemoved,\n\t\t       this),\n\t  reorderSignal(obs_source_get_signal_handler(source), \"reorder_filters\", OBSBasicFilters::OBSSourceReordered,\n\t\t\tthis),\n\t  removeSourceSignal(obs_source_get_signal_handler(source), \"remove\", OBSBasicFilters::SourceRemoved, this),\n\t  renameSourceSignal(obs_source_get_signal_handler(source), \"rename\", OBSBasicFilters::SourceRenamed, this),\n\t  noPreviewMargin(13)\n{\n\tmain = OBSBasic::Get();\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\n\tui->asyncFilters->setItemDelegate(new VisibilityItemDelegate(ui->asyncFilters));\n\tui->effectFilters->setItemDelegate(new VisibilityItemDelegate(ui->effectFilters));\n\n\tconst char *name = obs_source_get_name(source);\n\tsetWindowTitle(QTStr(\"Basic.Filters.Title\").arg(QT_UTF8(name)));\n\n#ifndef QT_NO_SHORTCUT\n\tui->actionRemoveFilter->setShortcut(QApplication::translate(\"OBSBasicFilters\", \"Del\", nullptr));\n#endif // QT_NO_SHORTCUT\n\n\taddAction(ui->actionRenameFilter);\n\taddAction(ui->actionRemoveFilter);\n\taddAction(ui->actionMoveUp);\n\taddAction(ui->actionMoveDown);\n\n\tinstallEventFilter(CreateShortcutFilter());\n\n\tconnect(ui->asyncFilters->itemDelegate(), &QAbstractItemDelegate::closeEditor, this,\n\t\t[this](QWidget *editor) { FilterNameEdited(editor, ui->asyncFilters); });\n\n\tconnect(ui->effectFilters->itemDelegate(), &QAbstractItemDelegate::closeEditor, this,\n\t\t[this](QWidget *editor) { FilterNameEdited(editor, ui->effectFilters); });\n\n\tQPushButton *close = ui->buttonBox->button(QDialogButtonBox::Close);\n\tconnect(close, &QPushButton::clicked, this, &OBSBasicFilters::close);\n\tclose->setDefault(true);\n\n\tconnect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this,\n\t\t&OBSBasicFilters::ResetFilters);\n\n\tconnect(ui->asyncFilters->model(), &QAbstractItemModel::rowsMoved, this, &OBSBasicFilters::FiltersMoved);\n\tconnect(ui->effectFilters->model(), &QAbstractItemModel::rowsMoved, this, &OBSBasicFilters::FiltersMoved);\n\n\tuint32_t caps = obs_source_get_output_flags(source);\n\tbool audio = (caps & OBS_SOURCE_AUDIO) != 0;\n\tbool audioOnly = (caps & OBS_SOURCE_VIDEO) == 0;\n\tbool async = (caps & OBS_SOURCE_ASYNC) != 0;\n\n\tif (!async && !audio) {\n\t\tui->asyncWidget->setVisible(false);\n\t\tui->separatorLine->setVisible(false);\n\t}\n\tif (audioOnly) {\n\t\tui->effectWidget->setVisible(false);\n\t\tui->separatorLine->setVisible(false);\n\t\tUpdateSplitter(false);\n\t}\n\n\tif (async && !audioOnly && ui->asyncFilters->count() == 0 && ui->effectFilters->count() != 0) {\n\t\tui->effectFilters->setFocus();\n\t}\n\n\tif (audioOnly || (audio && !async))\n\t\tui->asyncLabel->setText(QTStr(\"Basic.Filters.AudioFilters\"));\n\n\tif (async && audio && ui->asyncFilters->count() == 0) {\n\t\tUpdateSplitter(false);\n\t} else if (!audioOnly) {\n\t\tUpdateSplitter();\n\t}\n\n\tobs_source_inc_showing(source);\n\n\tauto addDrawCallback = [this]() {\n\t\tobs_display_add_draw_callback(ui->preview->GetDisplay(), OBSBasicFilters::DrawPreview, this);\n\t};\n\n\tenum obs_source_type type = obs_source_get_type(source);\n\tbool drawable_type = type == OBS_SOURCE_TYPE_INPUT || type == OBS_SOURCE_TYPE_SCENE;\n\n\tif ((caps & OBS_SOURCE_VIDEO) != 0) {\n\t\tui->rightLayout->setContentsMargins(0, 0, 0, 0);\n\t\tui->preview->show();\n\t\tif (drawable_type)\n\t\t\tconnect(ui->preview, &OBSQTDisplay::DisplayCreated, this, addDrawCallback);\n\t} else {\n\t\tui->rightLayout->setContentsMargins(0, noPreviewMargin, 0, 0);\n\t\tui->preview->hide();\n\t}\n\n#ifdef __APPLE__\n\tui->actionRenameFilter->setShortcut({Qt::Key_Return});\n#else\n\tui->actionRenameFilter->setShortcut({Qt::Key_F2});\n#endif\n\n\tUpdateFilters();\n}\n\nOBSBasicFilters::~OBSBasicFilters()\n{\n\tobs_source_dec_showing(source);\n\tClearListItems(ui->asyncFilters);\n\tClearListItems(ui->effectFilters);\n}\n\nvoid OBSBasicFilters::Init()\n{\n\tshow();\n}\n\ninline OBSSource OBSBasicFilters::GetFilter(int row, bool async)\n{\n\tif (row == -1)\n\t\treturn OBSSource();\n\n\tQListWidget *list = async ? ui->asyncFilters : ui->effectFilters;\n\tQListWidgetItem *item = list->item(row);\n\tif (!item)\n\t\treturn OBSSource();\n\n\tQVariant v = item->data(Qt::UserRole);\n\treturn v.value<OBSSource>();\n}\n\nvoid FilterChangeUndoRedo(void *vp, obs_data_t *nd_old_settings, obs_data_t *new_settings)\n{\n\tobs_source_t *source = static_cast<obs_source_t *>(vp);\n\tconst char *source_uuid = obs_source_get_uuid(source);\n\tconst char *name = obs_source_get_name(source);\n\tOBSBasic *main = OBSBasic::Get();\n\n\tOBSDataAutoRelease redo_wrapper = obs_data_create();\n\tobs_data_set_string(redo_wrapper, \"uuid\", source_uuid);\n\tobs_data_set_string(redo_wrapper, \"settings\", obs_data_get_json(new_settings));\n\n\tOBSDataAutoRelease undo_wrapper = obs_data_create();\n\tobs_data_set_string(undo_wrapper, \"uuid\", source_uuid);\n\tobs_data_set_string(undo_wrapper, \"settings\", obs_data_get_json(nd_old_settings));\n\n\tauto undo_redo = [](const std::string &data) {\n\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\tconst char *filter_uuid = obs_data_get_string(dat, \"uuid\");\n\t\tOBSSourceAutoRelease filter = obs_get_source_by_uuid(filter_uuid);\n\t\tOBSDataAutoRelease new_settings = obs_data_create_from_json(obs_data_get_string(dat, \"settings\"));\n\n\t\tOBSDataAutoRelease current_settings = obs_source_get_settings(filter);\n\t\tobs_data_clear(current_settings);\n\n\t\tobs_source_update(filter, new_settings);\n\t\tobs_source_update_properties(filter);\n\t};\n\n\tmain->undo_s.enable();\n\n\tstd::string undo_data = obs_data_get_json(undo_wrapper);\n\tstd::string redo_data = obs_data_get_json(redo_wrapper);\n\tmain->undo_s.add_action(QTStr(\"Undo.Filters\").arg(name), undo_redo, undo_redo, undo_data, redo_data);\n\n\tobs_source_update(source, new_settings);\n}\n\nvoid OBSBasicFilters::UpdatePropertiesView(int row, bool async)\n{\n\tOBSSource filter = GetFilter(row, async);\n\tif (filter && view && view->IsObject(filter)) {\n\t\t/* do not recreate properties view if already using a view\n\t\t * with the same object */\n\t\treturn;\n\t}\n\n\tif (view) {\n\t\tupdatePropertiesSignal.Disconnect();\n\t\tui->propertiesFrame->setVisible(false);\n\t\t/* Deleting a filter will trigger a visibility change, which will also\n\t\t * trigger a focus change if the focus has not been on the list itself\n\t\t * (e.g. after interacting with the property view).\n\t\t *\n\t\t * When an async filter list is available in the view, it will be the first\n\t\t * candidate to receive focus. If this list is empty, we hide the property\n\t\t * view by default and set the view to a `nullptr`.\n\t\t *\n\t\t * When the call for the visibility change returns, we need to check for\n\t\t * this possibility, as another event might have hidden (and deleted) the\n\t\t * view already.\n\t\t *\n\t\t * macOS might be especially affected as it doesn't switch keyboard focus\n\t\t * to buttons like Windows does. */\n\t\tif (view) {\n\t\t\tview->hide();\n\t\t\tview->deleteLater();\n\t\t\tview = nullptr;\n\t\t}\n\t}\n\n\tif (!filter)\n\t\treturn;\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(filter);\n\n\tauto disabled_undo = [](void *vp, obs_data_t *settings) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tmain->undo_s.disable();\n\t\tobs_source_t *source = static_cast<obs_source_t *>(vp);\n\t\tobs_source_update(source, settings);\n\t};\n\n\tview = new OBSPropertiesView(settings.Get(), filter, (PropertiesReloadCallback)obs_source_properties,\n\t\t\t\t     (PropertiesUpdateCallback)FilterChangeUndoRedo,\n\t\t\t\t     (PropertiesVisualUpdateCb)disabled_undo);\n\n\tupdatePropertiesSignal.Connect(obs_source_get_signal_handler(filter), \"update_properties\",\n\t\t\t\t       OBSBasicFilters::UpdateProperties, this);\n\n\tview->setMinimumHeight(150);\n\tUpdateSplitter();\n\tui->propertiesLayout->addWidget(view);\n\tview->show();\n}\n\nvoid OBSBasicFilters::UpdateProperties(void *data, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<OBSBasicFilters *>(data)->view, \"ReloadProperties\");\n}\n\nvoid OBSBasicFilters::AddFilter(OBSSource filter, bool focus)\n{\n\tuint32_t flags = obs_source_get_output_flags(filter);\n\tbool async = (flags & OBS_SOURCE_ASYNC) != 0;\n\tQListWidget *list = async ? ui->asyncFilters : ui->effectFilters;\n\n\tQListWidgetItem *item = new QListWidgetItem();\n\tQt::ItemFlags itemFlags = item->flags();\n\n\titem->setFlags(itemFlags | Qt::ItemIsEditable);\n\titem->setData(Qt::UserRole, QVariant::fromValue(filter));\n\n\tlist->addItem(item);\n\tif (focus)\n\t\tlist->setCurrentItem(item);\n\n\tSetupVisibilityItem(list, item, filter);\n}\n\nvoid OBSBasicFilters::RemoveFilter(OBSSource filter)\n{\n\tuint32_t flags = obs_source_get_output_flags(filter);\n\tbool async = (flags & OBS_SOURCE_ASYNC) != 0;\n\tQListWidget *list = async ? ui->asyncFilters : ui->effectFilters;\n\n\tfor (int i = 0; i < list->count(); i++) {\n\t\tQListWidgetItem *item = list->item(i);\n\t\tQVariant v = item->data(Qt::UserRole);\n\t\tOBSSource curFilter = v.value<OBSSource>();\n\n\t\tif (filter == curFilter) {\n\t\t\tDeleteListItem(list, item);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst char *filterName = obs_source_get_name(filter);\n\tconst char *sourceName = obs_source_get_name(source);\n\tif (!sourceName || !filterName)\n\t\treturn;\n\n\tconst char *filterId = obs_source_get_id(filter);\n\n\tblog(LOG_INFO, \"User removed filter '%s' (%s) from source '%s'\", filterName, filterId, sourceName);\n\n\tmain->SaveProject();\n}\n\nstruct FilterOrderInfo {\n\tint asyncIdx = 0;\n\tint effectIdx = 0;\n\tOBSBasicFilters *window;\n\n\tinline FilterOrderInfo(OBSBasicFilters *window_) : window(window_) {}\n};\n\nvoid OBSBasicFilters::ReorderFilter(QListWidget *list, obs_source_t *filter, size_t idx)\n{\n\tint count = list->count();\n\n\tfor (int i = 0; i < count; i++) {\n\t\tQListWidgetItem *listItem = list->item(i);\n\t\tQVariant v = listItem->data(Qt::UserRole);\n\t\tOBSSource filterItem = v.value<OBSSource>();\n\n\t\tif (filterItem == filter) {\n\t\t\tif ((int)idx != i) {\n\t\t\t\tbool sel = (list->currentRow() == i);\n\n\t\t\t\tlistItem = TakeListItem(list, i);\n\t\t\t\tif (listItem) {\n\t\t\t\t\tlist->insertItem((int)idx, listItem);\n\t\t\t\t\tSetupVisibilityItem(list, listItem, filterItem);\n\n\t\t\t\t\tif (sel)\n\t\t\t\t\t\tlist->setCurrentRow((int)idx);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid OBSBasicFilters::ReorderFilters()\n{\n\tFilterOrderInfo info(this);\n\n\tobs_source_enum_filters(\n\t\tsource,\n\t\t[](obs_source_t *, obs_source_t *filter, void *p) {\n\t\t\tFilterOrderInfo *info = static_cast<FilterOrderInfo *>(p);\n\t\t\tuint32_t flags;\n\t\t\tbool async;\n\n\t\t\tflags = obs_source_get_output_flags(filter);\n\t\t\tasync = (flags & OBS_SOURCE_ASYNC) != 0;\n\n\t\t\tif (async) {\n\t\t\t\tinfo->window->ReorderFilter(info->window->ui->asyncFilters, filter, info->asyncIdx++);\n\t\t\t} else {\n\t\t\t\tinfo->window->ReorderFilter(info->window->ui->effectFilters, filter, info->effectIdx++);\n\t\t\t}\n\t\t},\n\t\t&info);\n}\n\nvoid OBSBasicFilters::UpdateFilters()\n{\n\tif (!source)\n\t\treturn;\n\n\tClearListItems(ui->effectFilters);\n\tClearListItems(ui->asyncFilters);\n\n\tobs_source_enum_filters(\n\t\tsource,\n\t\t[](obs_source_t *, obs_source_t *filter, void *p) {\n\t\t\tOBSBasicFilters *window = static_cast<OBSBasicFilters *>(p);\n\n\t\t\twindow->AddFilter(filter, false);\n\t\t},\n\t\tthis);\n\n\tif (ui->asyncFilters->count() > 0) {\n\t\tui->asyncFilters->setCurrentItem(ui->asyncFilters->item(0));\n\t} else if (ui->effectFilters->count() > 0) {\n\t\tui->effectFilters->setCurrentItem(ui->effectFilters->item(0));\n\t}\n\n\tmain->SaveProject();\n}\n\nvoid OBSBasicFilters::UpdateSplitter()\n{\n\tbool show_splitter_frame = ui->asyncFilters->count() + ui->effectFilters->count() > 0;\n\tUpdateSplitter(show_splitter_frame);\n}\n\nvoid OBSBasicFilters::UpdateSplitter(bool show_splitter_frame)\n{\n\tbool show_splitter_handle = show_splitter_frame;\n\tuint32_t caps = obs_source_get_output_flags(source);\n\tif ((caps & OBS_SOURCE_VIDEO) == 0)\n\t\tshow_splitter_handle = false;\n\n\tfor (int i = 0; i < ui->rightLayout->count(); i++) {\n\t\tQSplitterHandle *hndl = ui->rightLayout->handle(i);\n\t\thndl->setEnabled(show_splitter_handle);\n\t}\n\n\tui->propertiesFrame->setVisible(show_splitter_frame);\n}\n\nstatic bool filter_compatible(bool async, uint32_t sourceFlags, uint32_t filterFlags)\n{\n\tbool filterVideo = (filterFlags & OBS_SOURCE_VIDEO) != 0;\n\tbool filterAsync = (filterFlags & OBS_SOURCE_ASYNC) != 0;\n\tbool filterAudio = (filterFlags & OBS_SOURCE_AUDIO) != 0;\n\tbool audio = (sourceFlags & OBS_SOURCE_AUDIO) != 0;\n\tbool audioOnly = (sourceFlags & OBS_SOURCE_VIDEO) == 0;\n\tbool asyncSource = (sourceFlags & OBS_SOURCE_ASYNC) != 0;\n\n\tif (async && ((audioOnly && filterVideo) || (!audio && !asyncSource) || (filterAudio && !audio) ||\n\t\t      (!asyncSource && !filterAudio)))\n\t\treturn false;\n\n\treturn (async && (filterAudio || filterAsync)) || (!async && !filterAudio && !filterAsync);\n}\n\nQMenu *OBSBasicFilters::CreateAddFilterPopupMenu(bool async)\n{\n\tuint32_t sourceFlags = obs_source_get_output_flags(source);\n\tconst char *type_str;\n\tbool foundValues = false;\n\tsize_t idx = 0;\n\n\tstruct FilterInfo {\n\t\tstring type;\n\t\tstring name;\n\n\t\tinline FilterInfo(const char *type_, const char *name_) : type(type_), name(name_) {}\n\n\t\tbool operator<(const FilterInfo &r) const { return name < r.name; }\n\t};\n\n\tvector<FilterInfo> types;\n\twhile (obs_enum_filter_types(idx++, &type_str)) {\n\t\tconst char *name = obs_source_get_display_name(type_str);\n\t\tuint32_t caps = obs_get_source_output_flags(type_str);\n\n\t\tif ((caps & OBS_SOURCE_DEPRECATED) != 0)\n\t\t\tcontinue;\n\t\tif ((caps & OBS_SOURCE_CAP_DISABLED) != 0)\n\t\t\tcontinue;\n\t\tif ((caps & OBS_SOURCE_CAP_OBSOLETE) != 0)\n\t\t\tcontinue;\n\n\t\ttypes.emplace_back(type_str, name);\n\t}\n\n\tsort(types.begin(), types.end());\n\n\tQMenu *popup = new QMenu(QTStr(\"Add\"), this);\n\tfor (FilterInfo &type : types) {\n\t\tuint32_t filterFlags = obs_get_source_output_flags(type.type.c_str());\n\n\t\tif (!filter_compatible(async, sourceFlags, filterFlags))\n\t\t\tcontinue;\n\n\t\tQAction *popupItem = new QAction(QT_UTF8(type.name.c_str()), this);\n\t\tpopupItem->setData(QT_UTF8(type.type.c_str()));\n\t\tconnect(popupItem, &QAction::triggered, this, [this, type]() { AddNewFilter(type.type.c_str()); });\n\t\tpopup->addAction(popupItem);\n\n\t\tfoundValues = true;\n\t}\n\n\tif (!foundValues) {\n\t\tdelete popup;\n\t\tpopup = nullptr;\n\t}\n\n\treturn popup;\n}\n\nvoid OBSBasicFilters::AddNewFilter(const char *id)\n{\n\tif (id && *id) {\n\t\tOBSSourceAutoRelease existing_filter;\n\t\tstring name = obs_source_get_display_name(id);\n\n\t\tQString placeholder = QString::fromStdString(name);\n\t\tQString text{placeholder};\n\t\tint i = 2;\n\t\twhile ((existing_filter = obs_source_get_filter_by_name(source, QT_TO_UTF8(text)))) {\n\t\t\ttext = QString(\"%1 %2\").arg(placeholder).arg(i++);\n\t\t}\n\n\t\tbool success = NameDialog::AskForName(this, QTStr(\"Basic.Filters.AddFilter.Title\"),\n\t\t\t\t\t\t      QTStr(\"Basic.Filters.AddFilter.Text\"), name, text);\n\t\tif (!success)\n\t\t\treturn;\n\n\t\tif (name.empty()) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\t\tAddNewFilter(id);\n\t\t\treturn;\n\t\t}\n\n\t\texisting_filter = obs_source_get_filter_by_name(source, name.c_str());\n\t\tif (existing_filter) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\t\t\tAddNewFilter(id);\n\t\t\treturn;\n\t\t}\n\n\t\tOBSSourceAutoRelease filter = obs_source_create(id, name.c_str(), nullptr, nullptr);\n\t\tif (filter) {\n\t\t\tconst char *sourceName = obs_source_get_name(source);\n\n\t\t\tblog(LOG_INFO, \"User added filter '%s' (%s) to source '%s'\", name.c_str(), id, sourceName);\n\n\t\t\tobs_source_filter_add(source, filter);\n\t\t} else {\n\t\t\tblog(LOG_WARNING, \"Creating filter '%s' failed!\", id);\n\t\t\treturn;\n\t\t}\n\n\t\tstd::string parent_uuid(obs_source_get_uuid(source));\n\t\tstd::string scene_uuid = obs_source_get_uuid(OBSBasic::Get()->GetCurrentSceneSource());\n\t\t/* In order to ensure that the UUID persists through undo/redo,\n\t\t * we save the source data rather than just recreating the\n\t\t * source from scratch. */\n\t\tOBSDataAutoRelease rwrapper = obs_save_source(filter);\n\t\tobs_data_set_string(rwrapper, \"undo_uuid\", parent_uuid.c_str());\n\n\t\tOBSDataAutoRelease uwrapper = obs_data_create();\n\t\tobs_data_set_string(uwrapper, \"fname\", obs_source_get_name(filter));\n\t\tobs_data_set_string(uwrapper, \"suuid\", parent_uuid.c_str());\n\n\t\tauto undo = [scene_uuid](const std::string &data) {\n\t\t\tOBSSourceAutoRelease ssource = obs_get_source_by_uuid(scene_uuid.c_str());\n\t\t\tOBSBasic::Get()->SetCurrentScene(ssource.Get(), true);\n\n\t\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(dat, \"suuid\"));\n\t\t\tOBSSourceAutoRelease filter =\n\t\t\t\tobs_source_get_filter_by_name(source, obs_data_get_string(dat, \"fname\"));\n\t\t\tobs_source_filter_remove(source, filter);\n\t\t};\n\n\t\tauto redo = [scene_uuid](const std::string &data) {\n\t\t\tOBSSourceAutoRelease ssource = obs_get_source_by_uuid(scene_uuid.c_str());\n\t\t\tOBSBasic::Get()->SetCurrentScene(ssource.Get(), true);\n\n\t\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(dat, \"undo_uuid\"));\n\t\t\tOBSSourceAutoRelease filter = obs_load_source(dat);\n\t\t\tobs_source_filter_add(source, filter);\n\t\t};\n\n\t\tstd::string undo_data(obs_data_get_json(uwrapper));\n\t\tstd::string redo_data(obs_data_get_json(rwrapper));\n\t\tmain->undo_s.add_action(QTStr(\"Undo.Add\").arg(obs_source_get_name(filter)), undo, redo, undo_data,\n\t\t\t\t\tredo_data, false);\n\t}\n}\n\nvoid OBSBasicFilters::closeEvent(QCloseEvent *event)\n{\n\tQDialog::closeEvent(event);\n\tif (!event->isAccepted())\n\t\treturn;\n\n\tobs_display_remove_draw_callback(ui->preview->GetDisplay(), OBSBasicFilters::DrawPreview, this);\n\n\tmain->SaveProject();\n}\n\nbool OBSBasicFilters::nativeEvent(const QByteArray &, void *message, qintptr *)\n{\n#ifdef _WIN32\n\tconst MSG &msg = *static_cast<MSG *>(message);\n\tswitch (msg.message) {\n\tcase WM_MOVE:\n\t\tfor (OBSQTDisplay *const display : findChildren<OBSQTDisplay *>()) {\n\t\t\tdisplay->OnMove();\n\t\t}\n\t\tbreak;\n\tcase WM_DISPLAYCHANGE:\n\t\tfor (OBSQTDisplay *const display : findChildren<OBSQTDisplay *>()) {\n\t\t\tdisplay->OnDisplayChange();\n\t\t}\n\t}\n#else\n\tUNUSED_PARAMETER(message);\n#endif\n\n\treturn false;\n}\n\n/* OBS Signals */\n\nvoid OBSBasicFilters::OBSSourceFilterAdded(void *param, calldata_t *data)\n{\n\tOBSBasicFilters *window = static_cast<OBSBasicFilters *>(param);\n\tobs_source_t *filter = (obs_source_t *)calldata_ptr(data, \"filter\");\n\n\tQMetaObject::invokeMethod(window, \"AddFilter\", Q_ARG(OBSSource, OBSSource(filter)));\n}\n\nvoid OBSBasicFilters::OBSSourceFilterRemoved(void *param, calldata_t *data)\n{\n\tOBSBasicFilters *window = static_cast<OBSBasicFilters *>(param);\n\tobs_source_t *filter = (obs_source_t *)calldata_ptr(data, \"filter\");\n\n\tQMetaObject::invokeMethod(window, \"RemoveFilter\", Q_ARG(OBSSource, OBSSource(filter)));\n}\n\nvoid OBSBasicFilters::OBSSourceReordered(void *param, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<OBSBasicFilters *>(param), \"ReorderFilters\");\n}\n\nvoid OBSBasicFilters::SourceRemoved(void *param, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<OBSBasicFilters *>(param), \"close\");\n}\n\nvoid OBSBasicFilters::SourceRenamed(void *param, calldata_t *data)\n{\n\tconst char *name = calldata_string(data, \"new_name\");\n\tQString title = QTStr(\"Basic.Filters.Title\").arg(QT_UTF8(name));\n\n\tQMetaObject::invokeMethod(static_cast<OBSBasicFilters *>(param), \"setWindowTitle\", Q_ARG(QString, title));\n}\n\nvoid OBSBasicFilters::DrawPreview(void *data, uint32_t cx, uint32_t cy)\n{\n\tOBSBasicFilters *window = static_cast<OBSBasicFilters *>(data);\n\n\tif (!window->source)\n\t\treturn;\n\n\tuint32_t sourceCX = max(obs_source_get_width(window->source), 1u);\n\tuint32_t sourceCY = max(obs_source_get_height(window->source), 1u);\n\n\tint x, y;\n\tint newCX, newCY;\n\tfloat scale;\n\n\tGetScaleAndCenterPos(sourceCX, sourceCY, cx, cy, x, y, scale);\n\n\tnewCX = int(scale * float(sourceCX));\n\tnewCY = int(scale * float(sourceCY));\n\n\tgs_viewport_push();\n\tgs_projection_push();\n\tconst bool previous = gs_set_linear_srgb(true);\n\n\tgs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), -100.0f, 100.0f);\n\tgs_set_viewport(x, y, newCX, newCY);\n\tobs_source_video_render(window->source);\n\n\tgs_set_linear_srgb(previous);\n\tgs_projection_pop();\n\tgs_viewport_pop();\n}\n\n/* Qt Slots */\n\nstatic bool QueryRemove(QWidget *parent, obs_source_t *source)\n{\n\tconst char *name = obs_source_get_name(source);\n\n\tQString text = QTStr(\"ConfirmRemove.Text\").arg(QT_UTF8(name));\n\n\tQMessageBox remove_source(parent);\n\tremove_source.setText(text);\n\tQAbstractButton *Yes = remove_source.addButton(QTStr(\"Yes\"), QMessageBox::YesRole);\n\tremove_source.addButton(QTStr(\"No\"), QMessageBox::NoRole);\n\tremove_source.setIcon(QMessageBox::Question);\n\tremove_source.setWindowTitle(QTStr(\"ConfirmRemove.Title\"));\n\tremove_source.exec();\n\n\treturn Yes == remove_source.clickedButton();\n}\n\nvoid OBSBasicFilters::on_addAsyncFilter_clicked()\n{\n\tui->asyncFilters->setFocus();\n\tQScopedPointer<QMenu> popup(CreateAddFilterPopupMenu(true));\n\tif (popup)\n\t\tpopup->exec(QCursor::pos());\n}\n\nvoid OBSBasicFilters::on_removeAsyncFilter_clicked()\n{\n\tOBSSource filter = GetFilter(ui->asyncFilters->currentRow(), true);\n\tif (filter) {\n\t\tif (QueryRemove(this, filter))\n\t\t\tdelete_filter(filter);\n\t}\n}\n\nvoid OBSBasicFilters::on_moveAsyncFilterUp_clicked()\n{\n\tOBSSource filter = GetFilter(ui->asyncFilters->currentRow(), true);\n\tif (filter)\n\t\tobs_source_filter_set_order(source, filter, OBS_ORDER_MOVE_UP);\n}\n\nvoid OBSBasicFilters::on_moveAsyncFilterDown_clicked()\n{\n\tOBSSource filter = GetFilter(ui->asyncFilters->currentRow(), true);\n\tif (filter)\n\t\tobs_source_filter_set_order(source, filter, OBS_ORDER_MOVE_DOWN);\n}\n\nvoid OBSBasicFilters::on_asyncFilters_GotFocus()\n{\n\tUpdatePropertiesView(ui->asyncFilters->currentRow(), true);\n\tisAsync = true;\n}\n\nvoid OBSBasicFilters::on_asyncFilters_currentRowChanged(int row)\n{\n\tUpdatePropertiesView(row, true);\n}\n\nvoid OBSBasicFilters::on_addEffectFilter_clicked()\n{\n\tui->effectFilters->setFocus();\n\tQScopedPointer<QMenu> popup(CreateAddFilterPopupMenu(false));\n\tif (popup)\n\t\tpopup->exec(QCursor::pos());\n}\n\nvoid OBSBasicFilters::on_removeEffectFilter_clicked()\n{\n\tOBSSource filter = GetFilter(ui->effectFilters->currentRow(), false);\n\tif (filter) {\n\t\tif (QueryRemove(this, filter)) {\n\t\t\tdelete_filter(filter);\n\t\t}\n\t}\n}\n\nvoid OBSBasicFilters::on_moveEffectFilterUp_clicked()\n{\n\tOBSSource filter = GetFilter(ui->effectFilters->currentRow(), false);\n\tif (filter)\n\t\tobs_source_filter_set_order(source, filter, OBS_ORDER_MOVE_UP);\n}\n\nvoid OBSBasicFilters::on_moveEffectFilterDown_clicked()\n{\n\tOBSSource filter = GetFilter(ui->effectFilters->currentRow(), false);\n\tif (filter)\n\t\tobs_source_filter_set_order(source, filter, OBS_ORDER_MOVE_DOWN);\n}\n\nvoid OBSBasicFilters::on_effectFilters_GotFocus()\n{\n\tUpdatePropertiesView(ui->effectFilters->currentRow(), false);\n\tisAsync = false;\n}\n\nvoid OBSBasicFilters::on_effectFilters_currentRowChanged(int row)\n{\n\tUpdatePropertiesView(row, false);\n}\n\nvoid OBSBasicFilters::on_actionRemoveFilter_triggered()\n{\n\tif (ui->asyncFilters->hasFocus())\n\t\ton_removeAsyncFilter_clicked();\n\telse if (ui->effectFilters->hasFocus())\n\t\ton_removeEffectFilter_clicked();\n}\n\nvoid OBSBasicFilters::on_actionMoveUp_triggered()\n{\n\tif (ui->asyncFilters->hasFocus())\n\t\ton_moveAsyncFilterUp_clicked();\n\telse if (ui->effectFilters->hasFocus())\n\t\ton_moveEffectFilterUp_clicked();\n}\n\nvoid OBSBasicFilters::on_actionMoveDown_triggered()\n{\n\tif (ui->asyncFilters->hasFocus())\n\t\ton_moveAsyncFilterDown_clicked();\n\telse if (ui->effectFilters->hasFocus())\n\t\ton_moveEffectFilterDown_clicked();\n}\n\nvoid OBSBasicFilters::on_actionRenameFilter_triggered()\n{\n\tif (ui->asyncFilters->hasFocus())\n\t\tRenameAsyncFilter();\n\telse if (ui->effectFilters->hasFocus())\n\t\tRenameEffectFilter();\n}\n\nvoid OBSBasicFilters::CustomContextMenu(const QPoint &pos, bool async)\n{\n\tQListWidget *list = async ? ui->asyncFilters : ui->effectFilters;\n\tQListWidgetItem *item = list->itemAt(pos);\n\n\tQMenu popup(window());\n\n\tQPointer<QMenu> addMenu = CreateAddFilterPopupMenu(async);\n\tif (addMenu)\n\t\tpopup.addMenu(addMenu);\n\n\tif (item) {\n\t\tpopup.addSeparator();\n\t\tpopup.addAction(QTStr(\"Duplicate\"), this, [&]() {\n\t\t\tDuplicateItem(async ? ui->asyncFilters->currentItem() : ui->effectFilters->currentItem());\n\t\t});\n\t\tpopup.addSeparator();\n\t\tpopup.addAction(ui->actionRenameFilter);\n\t\tpopup.addAction(ui->actionRemoveFilter);\n\t\tpopup.addSeparator();\n\n\t\tQAction *copyAction = new QAction(QTStr(\"Copy\"));\n\t\tconnect(copyAction, &QAction::triggered, this, &OBSBasicFilters::CopyFilter);\n\t\tcopyAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_C));\n\t\tui->effectWidget->addAction(copyAction);\n\t\tui->asyncWidget->addAction(copyAction);\n\t\tpopup.addAction(copyAction);\n\t}\n\n\tQAction *pasteAction = new QAction(QTStr(\"Paste\"));\n\tpasteAction->setEnabled(main->copyFilter);\n\tconnect(pasteAction, &QAction::triggered, this, &OBSBasicFilters::PasteFilter);\n\tpasteAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_V));\n\tui->effectWidget->addAction(pasteAction);\n\tui->asyncWidget->addAction(pasteAction);\n\tpopup.addAction(pasteAction);\n\n\tpopup.exec(QCursor::pos());\n}\n\nvoid OBSBasicFilters::EditItem(QListWidgetItem *item, bool async)\n{\n\tif (editActive)\n\t\treturn;\n\n\tQt::ItemFlags flags = item->flags();\n\tOBSSource filter = item->data(Qt::UserRole).value<OBSSource>();\n\tconst char *name = obs_source_get_name(filter);\n\tQListWidget *list = async ? ui->asyncFilters : ui->effectFilters;\n\n\titem->setText(QT_UTF8(name));\n\titem->setFlags(flags | Qt::ItemIsEditable);\n\tlist->removeItemWidget(item);\n\tlist->editItem(item);\n\titem->setFlags(flags);\n\teditActive = true;\n}\n\nvoid OBSBasicFilters::DuplicateItem(QListWidgetItem *item)\n{\n\tOBSSource filter = item->data(Qt::UserRole).value<OBSSource>();\n\tstring name = obs_source_get_name(filter);\n\tOBSSourceAutoRelease existing_filter;\n\n\tQString placeholder = QString::fromStdString(name);\n\tQString text{placeholder};\n\tint i = 2;\n\twhile ((existing_filter = obs_source_get_filter_by_name(source, QT_TO_UTF8(text)))) {\n\t\ttext = QString(\"%1 %2\").arg(placeholder).arg(i++);\n\t}\n\n\tbool success = NameDialog::AskForName(this, QTStr(\"Basic.Filters.AddFilter.Title\"),\n\t\t\t\t\t      QTStr(\"Basic.Filters.AddFilter.Text\"), name, text);\n\tif (!success)\n\t\treturn;\n\n\tif (name.empty()) {\n\t\tOBSMessageBox::warning(this, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\tDuplicateItem(item);\n\t\treturn;\n\t}\n\n\texisting_filter = obs_source_get_filter_by_name(source, name.c_str());\n\tif (existing_filter) {\n\t\tOBSMessageBox::warning(this, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\t\tDuplicateItem(item);\n\t\treturn;\n\t}\n\tbool enabled = obs_source_enabled(filter);\n\tOBSSourceAutoRelease new_filter = obs_source_duplicate(filter, name.c_str(), false);\n\tif (new_filter) {\n\t\tconst char *sourceName = obs_source_get_name(source);\n\t\tconst char *id = obs_source_get_id(new_filter);\n\t\tblog(LOG_INFO,\n\t\t     \"User duplicated filter '%s' (%s) from '%s' \"\n\t\t     \"to source '%s'\",\n\t\t     name.c_str(), id, name.c_str(), sourceName);\n\t\tobs_source_set_enabled(new_filter, enabled);\n\t\tobs_source_filter_add(source, new_filter);\n\t}\n}\n\nvoid OBSBasicFilters::on_asyncFilters_customContextMenuRequested(const QPoint &pos)\n{\n\tCustomContextMenu(pos, true);\n}\n\nvoid OBSBasicFilters::on_effectFilters_customContextMenuRequested(const QPoint &pos)\n{\n\tCustomContextMenu(pos, false);\n}\n\nvoid OBSBasicFilters::RenameAsyncFilter()\n{\n\tEditItem(ui->asyncFilters->currentItem(), true);\n}\n\nvoid OBSBasicFilters::RenameEffectFilter()\n{\n\tEditItem(ui->effectFilters->currentItem(), false);\n}\n\nvoid OBSBasicFilters::FilterNameEdited(QWidget *editor, QListWidget *list)\n{\n\tQListWidgetItem *listItem = list->currentItem();\n\tOBSSource filter = listItem->data(Qt::UserRole).value<OBSSource>();\n\tQLineEdit *edit = qobject_cast<QLineEdit *>(editor);\n\tstring name = QT_TO_UTF8(edit->text().trimmed());\n\n\tconst char *prevName = obs_source_get_name(filter);\n\tbool sameName = (name == prevName);\n\tOBSSourceAutoRelease foundFilter = nullptr;\n\n\tif (!sameName)\n\t\tfoundFilter = obs_source_get_filter_by_name(source, name.c_str());\n\n\tif (foundFilter || name.empty() || sameName) {\n\t\tlistItem->setText(QT_UTF8(prevName));\n\n\t\tif (foundFilter) {\n\t\t\tOBSMessageBox::information(window(), QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\t\t} else if (name.empty()) {\n\t\t\tOBSMessageBox::information(window(), QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\t}\n\t} else {\n\t\tconst char *sourceName = obs_source_get_name(source);\n\n\t\tblog(LOG_INFO, \"User renamed filter '%s' on source '%s' to '%s'\", prevName, sourceName, name.c_str());\n\n\t\tlistItem->setText(QT_UTF8(name.c_str()));\n\t\tobs_source_set_name(filter, name.c_str());\n\n\t\tstd::string scene_uuid = obs_source_get_uuid(OBSBasic::Get()->GetCurrentSceneSource());\n\t\tauto undo = [scene_uuid, prev = std::string(prevName), name](const std::string &uuid) {\n\t\t\tOBSSourceAutoRelease ssource = obs_get_source_by_uuid(scene_uuid.c_str());\n\t\t\tOBSBasic::Get()->SetCurrentScene(ssource.Get(), true);\n\n\t\t\tOBSSourceAutoRelease filter = obs_get_source_by_uuid(uuid.c_str());\n\t\t\tobs_source_set_name(filter, prev.c_str());\n\t\t};\n\n\t\tauto redo = [scene_uuid, prev = std::string(prevName), name](const std::string &uuid) {\n\t\t\tOBSSourceAutoRelease ssource = obs_get_source_by_uuid(scene_uuid.c_str());\n\t\t\tOBSBasic::Get()->SetCurrentScene(ssource.Get(), true);\n\n\t\t\tOBSSourceAutoRelease filter = obs_get_source_by_uuid(uuid.c_str());\n\t\t\tobs_source_set_name(filter, name.c_str());\n\t\t};\n\n\t\tstd::string filter_uuid(obs_source_get_uuid(filter));\n\t\tmain->undo_s.add_action(QTStr(\"Undo.Rename\").arg(name.c_str()), undo, redo, filter_uuid, filter_uuid);\n\t}\n\n\tlistItem->setText(QString());\n\tSetupVisibilityItem(list, listItem, filter);\n\teditActive = false;\n}\n\nstatic bool ConfirmReset(QWidget *parent)\n{\n\tQMessageBox::StandardButton button;\n\n\tbutton = OBSMessageBox::question(parent, QTStr(\"ConfirmReset.Title\"), QTStr(\"ConfirmReset.Text\"),\n\t\t\t\t\t QMessageBox::Yes | QMessageBox::No);\n\n\treturn button == QMessageBox::Yes;\n}\n\nvoid OBSBasicFilters::ResetFilters()\n{\n\tQListWidget *list = isAsync ? ui->asyncFilters : ui->effectFilters;\n\tint row = list->currentRow();\n\n\tOBSSource filter = GetFilter(row, isAsync);\n\n\tif (!filter)\n\t\treturn;\n\n\tif (!ConfirmReset(this))\n\t\treturn;\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(filter);\n\n\tOBSDataAutoRelease empty_settings = obs_data_create();\n\tFilterChangeUndoRedo((void *)filter, settings, empty_settings);\n\n\tobs_data_clear(settings);\n\n\tif (!view->DeferUpdate())\n\t\tobs_source_update(filter, nullptr);\n\n\tview->ReloadProperties();\n}\n\nvoid OBSBasicFilters::CopyFilter()\n{\n\tOBSSource filter = nullptr;\n\n\tif (isAsync)\n\t\tfilter = GetFilter(ui->asyncFilters->currentRow(), true);\n\telse\n\t\tfilter = GetFilter(ui->effectFilters->currentRow(), false);\n\n\tmain->copyFilter = OBSGetWeakRef(filter);\n}\n\nvoid OBSBasicFilters::PasteFilter()\n{\n\tOBSSource filter = OBSGetStrongRef(main->copyFilter);\n\tif (!filter)\n\t\treturn;\n\n\tOBSDataArrayAutoRelease undo_array = obs_source_backup_filters(source);\n\tobs_source_copy_single_filter(source, filter);\n\tOBSDataArrayAutoRelease redo_array = obs_source_backup_filters(source);\n\n\tconst char *filterName = obs_source_get_name(filter);\n\tconst char *sourceName = obs_source_get_name(source);\n\tQString text = QTStr(\"Undo.Filters.Paste.Single\").arg(filterName, sourceName);\n\n\tmain->CreateFilterPasteUndoRedoAction(text, source, undo_array, redo_array);\n}\n\nvoid OBSBasicFilters::delete_filter(OBSSource filter)\n{\n\tOBSDataAutoRelease wrapper = obs_save_source(filter);\n\tstd::string parent_uuid(obs_source_get_uuid(source));\n\tobs_data_set_string(wrapper, \"undo_uuid\", parent_uuid.c_str());\n\n\tstd::string scene_uuid = obs_source_get_uuid(OBSBasic::Get()->GetCurrentSceneSource());\n\tauto undo = [scene_uuid](const std::string &data) {\n\t\tOBSSourceAutoRelease ssource = obs_get_source_by_uuid(scene_uuid.c_str());\n\t\tOBSBasic::Get()->SetCurrentScene(ssource.Get(), true);\n\n\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(dat, \"undo_uuid\"));\n\t\tOBSSourceAutoRelease filter = obs_load_source(dat);\n\t\tobs_source_filter_add(source, filter);\n\t};\n\n\tOBSDataAutoRelease rwrapper = obs_data_create();\n\tobs_data_set_string(rwrapper, \"fname\", obs_source_get_name(filter));\n\tobs_data_set_string(rwrapper, \"suuid\", parent_uuid.c_str());\n\tauto redo = [scene_uuid](const std::string &data) {\n\t\tOBSSourceAutoRelease ssource = obs_get_source_by_uuid(scene_uuid.c_str());\n\t\tOBSBasic::Get()->SetCurrentScene(ssource.Get(), true);\n\n\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(dat, \"suuid\"));\n\t\tOBSSourceAutoRelease filter = obs_source_get_filter_by_name(source, obs_data_get_string(dat, \"fname\"));\n\t\tobs_source_filter_remove(source, filter);\n\t};\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tmain->undo_s.add_action(QTStr(\"Undo.Delete\").arg(obs_source_get_name(filter)), undo, redo, undo_data, redo_data,\n\t\t\t\tfalse);\n\tobs_source_filter_remove(source, filter);\n}\n\nvoid OBSBasicFilters::FiltersMoved(const QModelIndex &, int srcIdxStart, int, const QModelIndex &, int)\n{\n\tQListWidget *list = isAsync ? ui->asyncFilters : ui->effectFilters;\n\tint neighborIdx = 0;\n\n\tif (srcIdxStart < list->currentRow())\n\t\tneighborIdx = list->currentRow() - 1;\n\telse if (srcIdxStart > list->currentRow())\n\t\tneighborIdx = list->currentRow() + 1;\n\telse\n\t\treturn;\n\n\tif (neighborIdx > list->count() - 1)\n\t\tneighborIdx = list->count() - 1;\n\telse if (neighborIdx < 0)\n\t\tneighborIdx = 0;\n\n\tOBSSource neighbor = GetFilter(neighborIdx, isAsync);\n\tint idx = obs_source_filter_get_index(source, neighbor);\n\n\tOBSSource filter = GetFilter(list->currentRow(), isAsync);\n\tobs_source_filter_set_index(source, filter, idx);\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicFilters.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_OBSBasicFilters.h\"\n\n#include <QDialog>\n\nclass OBSBasic;\nclass OBSPropertiesView;\n\nclass OBSBasicFilters : public QDialog {\n\tQ_OBJECT\n\nprivate:\n\tOBSBasic *main;\n\n\tstd::unique_ptr<Ui::OBSBasicFilters> ui;\n\tOBSSource source;\n\tOBSPropertiesView *view = nullptr;\n\n\tOBSSignal addSignal;\n\tOBSSignal removeSignal;\n\tOBSSignal reorderSignal;\n\n\tOBSSignal removeSourceSignal;\n\tOBSSignal renameSourceSignal;\n\tOBSSignal updatePropertiesSignal;\n\n\tinline OBSSource GetFilter(int row, bool async);\n\n\tvoid UpdateFilters();\n\tvoid UpdateSplitter();\n\tvoid UpdateSplitter(bool show_splitter_frame);\n\tvoid UpdatePropertiesView(int row, bool async);\n\n\tstatic void OBSSourceFilterAdded(void *param, calldata_t *data);\n\tstatic void OBSSourceFilterRemoved(void *param, calldata_t *data);\n\tstatic void OBSSourceReordered(void *param, calldata_t *data);\n\tstatic void SourceRemoved(void *param, calldata_t *data);\n\tstatic void SourceRenamed(void *param, calldata_t *data);\n\tstatic void UpdateProperties(void *data, calldata_t *params);\n\tstatic void DrawPreview(void *data, uint32_t cx, uint32_t cy);\n\n\tQMenu *CreateAddFilterPopupMenu(bool async);\n\n\tvoid AddNewFilter(const char *id);\n\tvoid ReorderFilter(QListWidget *list, obs_source_t *filter, size_t idx);\n\n\tvoid CustomContextMenu(const QPoint &pos, bool async);\n\tvoid EditItem(QListWidgetItem *item, bool async);\n\tvoid DuplicateItem(QListWidgetItem *item);\n\n\tvoid FilterNameEdited(QWidget *editor, QListWidget *list);\n\n\tvoid delete_filter(OBSSource filter);\n\n\tbool isAsync;\n\n\tint noPreviewMargin;\n\n\tbool editActive = false;\n\nprivate slots:\n\tvoid AddFilter(OBSSource filter, bool focus = true);\n\tvoid RemoveFilter(OBSSource filter);\n\tvoid ReorderFilters();\n\tvoid RenameAsyncFilter();\n\tvoid RenameEffectFilter();\n\tvoid ResetFilters();\n\n\tvoid on_addAsyncFilter_clicked();\n\tvoid on_removeAsyncFilter_clicked();\n\tvoid on_moveAsyncFilterUp_clicked();\n\tvoid on_moveAsyncFilterDown_clicked();\n\tvoid on_asyncFilters_currentRowChanged(int row);\n\tvoid on_asyncFilters_customContextMenuRequested(const QPoint &pos);\n\tvoid on_asyncFilters_GotFocus();\n\n\tvoid on_addEffectFilter_clicked();\n\tvoid on_removeEffectFilter_clicked();\n\tvoid on_moveEffectFilterUp_clicked();\n\tvoid on_moveEffectFilterDown_clicked();\n\tvoid on_effectFilters_currentRowChanged(int row);\n\tvoid on_effectFilters_customContextMenuRequested(const QPoint &pos);\n\tvoid on_effectFilters_GotFocus();\n\n\tvoid on_actionRemoveFilter_triggered();\n\tvoid on_actionMoveUp_triggered();\n\tvoid on_actionMoveDown_triggered();\n\n\tvoid on_actionRenameFilter_triggered();\n\n\tvoid CopyFilter();\n\tvoid PasteFilter();\n\n\tvoid FiltersMoved(const QModelIndex &srcParent, int srcIdxStart, int srcIdxEnd, const QModelIndex &dstParent,\n\t\t\t  int dstIdx);\n\npublic:\n\tOBSBasicFilters(QWidget *parent, OBSSource source_);\n\t~OBSBasicFilters();\n\n\tvoid Init();\n\n\tinline void UpdateSource(obs_source_t *target)\n\t{\n\t\tif (source == target)\n\t\t\tUpdateFilters();\n\t}\n\nprotected:\n\tvirtual void closeEvent(QCloseEvent *event) override;\n\tvirtual bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicInteraction.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasicInteraction.hpp\"\n\n#include <dialogs/OBSBasicProperties.hpp>\n#include <utility/OBSEventFilter.hpp>\n#include <utility/display-helpers.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QInputEvent>\n\n#ifdef _WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN 1\n#endif\n#include <Windows.h>\n#endif\n\n#include \"moc_OBSBasicInteraction.cpp\"\n\nusing namespace std;\n\nOBSBasicInteraction::OBSBasicInteraction(QWidget *parent, OBSSource source_)\n\t: QDialog(parent),\n\t  main(qobject_cast<OBSBasic *>(parent)),\n\t  ui(new Ui::OBSBasicInteraction),\n\t  source(source_),\n\t  removedSignal(obs_source_get_signal_handler(source), \"remove\", OBSBasicInteraction::SourceRemoved, this),\n\t  renamedSignal(obs_source_get_signal_handler(source), \"rename\", OBSBasicInteraction::SourceRenamed, this),\n\t  eventFilter(BuildEventFilter())\n{\n\tint cx = (int)config_get_int(App()->GetAppConfig(), \"InteractionWindow\", \"cx\");\n\tint cy = (int)config_get_int(App()->GetAppConfig(), \"InteractionWindow\", \"cy\");\n\n\tQt::WindowFlags flags = windowFlags();\n\tQt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint;\n\tsetWindowFlags(flags & (~helpFlag));\n\n\tui->setupUi(this);\n\n\tui->preview->setMouseTracking(true);\n\tui->preview->setFocusPolicy(Qt::StrongFocus);\n\tui->preview->installEventFilter(eventFilter.get());\n\n\tif (cx > 400 && cy > 400)\n\t\tresize(cx, cy);\n\n\tconst char *name = obs_source_get_name(source);\n\tsetWindowTitle(QTStr(\"Basic.InteractionWindow\").arg(QT_UTF8(name)));\n\n\tauto addDrawCallback = [this]() {\n\t\tobs_display_add_draw_callback(ui->preview->GetDisplay(), OBSBasicInteraction::DrawPreview, this);\n\t};\n\n\tconnect(ui->preview, &OBSQTDisplay::DisplayCreated, this, addDrawCallback);\n}\n\nOBSBasicInteraction::~OBSBasicInteraction()\n{\n\t// since QT fakes a mouse movement while destructing a widget\n\t// remove our event filter\n\tui->preview->removeEventFilter(eventFilter.get());\n}\n\nOBSEventFilter *OBSBasicInteraction::BuildEventFilter()\n{\n\treturn new OBSEventFilter([this](QObject *, QEvent *event) {\n\t\tswitch (event->type()) {\n\t\tcase QEvent::MouseButtonPress:\n\t\tcase QEvent::MouseButtonRelease:\n\t\tcase QEvent::MouseButtonDblClick:\n\t\t\treturn this->HandleMouseClickEvent(static_cast<QMouseEvent *>(event));\n\t\tcase QEvent::MouseMove:\n\t\tcase QEvent::Enter:\n\t\tcase QEvent::Leave:\n\t\t\treturn this->HandleMouseMoveEvent(static_cast<QMouseEvent *>(event));\n\n\t\tcase QEvent::Wheel:\n\t\t\treturn this->HandleMouseWheelEvent(static_cast<QWheelEvent *>(event));\n\t\tcase QEvent::FocusIn:\n\t\tcase QEvent::FocusOut:\n\t\t\treturn this->HandleFocusEvent(static_cast<QFocusEvent *>(event));\n\t\tcase QEvent::KeyPress:\n\t\tcase QEvent::KeyRelease:\n\t\t\treturn this->HandleKeyEvent(static_cast<QKeyEvent *>(event));\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t});\n}\n\nvoid OBSBasicInteraction::SourceRemoved(void *data, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<OBSBasicInteraction *>(data), \"close\");\n}\n\nvoid OBSBasicInteraction::SourceRenamed(void *data, calldata_t *params)\n{\n\tconst char *name = calldata_string(params, \"new_name\");\n\tQString title = QTStr(\"Basic.InteractionWindow\").arg(QT_UTF8(name));\n\n\tQMetaObject::invokeMethod(static_cast<OBSBasicProperties *>(data), \"setWindowTitle\", Q_ARG(QString, title));\n}\n\nvoid OBSBasicInteraction::DrawPreview(void *data, uint32_t cx, uint32_t cy)\n{\n\tOBSBasicInteraction *window = static_cast<OBSBasicInteraction *>(data);\n\n\tif (!window->source)\n\t\treturn;\n\n\tuint32_t sourceCX = max(obs_source_get_width(window->source), 1u);\n\tuint32_t sourceCY = max(obs_source_get_height(window->source), 1u);\n\n\tint x, y;\n\tint newCX, newCY;\n\tfloat scale;\n\n\tGetScaleAndCenterPos(sourceCX, sourceCY, cx, cy, x, y, scale);\n\n\tnewCX = int(scale * float(sourceCX));\n\tnewCY = int(scale * float(sourceCY));\n\n\tgs_viewport_push();\n\tgs_projection_push();\n\tconst bool previous = gs_set_linear_srgb(true);\n\n\tgs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), -100.0f, 100.0f);\n\tgs_set_viewport(x, y, newCX, newCY);\n\tobs_source_video_render(window->source);\n\n\tgs_set_linear_srgb(previous);\n\tgs_projection_pop();\n\tgs_viewport_pop();\n}\n\nvoid OBSBasicInteraction::closeEvent(QCloseEvent *event)\n{\n\tQDialog::closeEvent(event);\n\tif (!event->isAccepted())\n\t\treturn;\n\n\tconfig_set_int(App()->GetAppConfig(), \"InteractionWindow\", \"cx\", width());\n\tconfig_set_int(App()->GetAppConfig(), \"InteractionWindow\", \"cy\", height());\n\n\tobs_display_remove_draw_callback(ui->preview->GetDisplay(), OBSBasicInteraction::DrawPreview, this);\n}\n\nbool OBSBasicInteraction::nativeEvent(const QByteArray &, void *message, qintptr *)\n{\n#ifdef _WIN32\n\tconst MSG &msg = *static_cast<MSG *>(message);\n\tswitch (msg.message) {\n\tcase WM_MOVE:\n\t\tfor (OBSQTDisplay *const display : findChildren<OBSQTDisplay *>()) {\n\t\t\tdisplay->OnMove();\n\t\t}\n\t\tbreak;\n\tcase WM_DISPLAYCHANGE:\n\t\tfor (OBSQTDisplay *const display : findChildren<OBSQTDisplay *>()) {\n\t\t\tdisplay->OnDisplayChange();\n\t\t}\n\t}\n#else\n\tUNUSED_PARAMETER(message);\n#endif\n\n\treturn false;\n}\n\nstatic int TranslateQtKeyboardEventModifiers(QInputEvent *event, bool mouseEvent)\n{\n\tint obsModifiers = INTERACT_NONE;\n\n\tif (event->modifiers().testFlag(Qt::ShiftModifier))\n\t\tobsModifiers |= INTERACT_SHIFT_KEY;\n\tif (event->modifiers().testFlag(Qt::AltModifier))\n\t\tobsModifiers |= INTERACT_ALT_KEY;\n#ifdef __APPLE__\n\t// Mac: Meta = Control, Control = Command\n\tif (event->modifiers().testFlag(Qt::ControlModifier))\n\t\tobsModifiers |= INTERACT_COMMAND_KEY;\n\tif (event->modifiers().testFlag(Qt::MetaModifier))\n\t\tobsModifiers |= INTERACT_CONTROL_KEY;\n#else\n\t// Handle windows key? Can a browser even trap that key?\n\tif (event->modifiers().testFlag(Qt::ControlModifier))\n\t\tobsModifiers |= INTERACT_CONTROL_KEY;\n#endif\n\n\tif (!mouseEvent) {\n\t\tif (event->modifiers().testFlag(Qt::KeypadModifier))\n\t\t\tobsModifiers |= INTERACT_IS_KEY_PAD;\n\t}\n\n\treturn obsModifiers;\n}\n\nstatic int TranslateQtMouseEventModifiers(QMouseEvent *event)\n{\n\tint modifiers = TranslateQtKeyboardEventModifiers(event, true);\n\n\tif (event->buttons().testFlag(Qt::LeftButton))\n\t\tmodifiers |= INTERACT_MOUSE_LEFT;\n\tif (event->buttons().testFlag(Qt::MiddleButton))\n\t\tmodifiers |= INTERACT_MOUSE_MIDDLE;\n\tif (event->buttons().testFlag(Qt::RightButton))\n\t\tmodifiers |= INTERACT_MOUSE_RIGHT;\n\n\treturn modifiers;\n}\n\nbool OBSBasicInteraction::GetSourceRelativeXY(int mouseX, int mouseY, int &relX, int &relY)\n{\n\tfloat pixelRatio = devicePixelRatioF();\n\tint mouseXscaled = (int)roundf(mouseX * pixelRatio);\n\tint mouseYscaled = (int)roundf(mouseY * pixelRatio);\n\n\tQSize size = GetPixelSize(ui->preview);\n\n\tuint32_t sourceCX = max(obs_source_get_width(source), 1u);\n\tuint32_t sourceCY = max(obs_source_get_height(source), 1u);\n\n\tint x, y;\n\tfloat scale;\n\n\tGetScaleAndCenterPos(sourceCX, sourceCY, size.width(), size.height(), x, y, scale);\n\n\tif (x > 0) {\n\t\trelX = int(float(mouseXscaled - x) / scale);\n\t\trelY = int(float(mouseYscaled / scale));\n\t} else {\n\t\trelX = int(float(mouseXscaled / scale));\n\t\trelY = int(float(mouseYscaled - y) / scale);\n\t}\n\n\t// Confirm mouse is inside the source\n\tif (relX < 0 || relX > int(sourceCX))\n\t\treturn false;\n\tif (relY < 0 || relY > int(sourceCY))\n\t\treturn false;\n\n\treturn true;\n}\n\nbool OBSBasicInteraction::HandleMouseClickEvent(QMouseEvent *event)\n{\n\tbool mouseUp = event->type() == QEvent::MouseButtonRelease;\n\tint clickCount = 1;\n\tif (event->type() == QEvent::MouseButtonDblClick)\n\t\tclickCount = 2;\n\n\tstruct obs_mouse_event mouseEvent = {};\n\n\tmouseEvent.modifiers = TranslateQtMouseEventModifiers(event);\n\n\tint32_t button = 0;\n\n\tswitch (event->button()) {\n\tcase Qt::LeftButton:\n\t\tbutton = MOUSE_LEFT;\n\t\tbreak;\n\tcase Qt::MiddleButton:\n\t\tbutton = MOUSE_MIDDLE;\n\t\tbreak;\n\tcase Qt::RightButton:\n\t\tbutton = MOUSE_RIGHT;\n\t\tbreak;\n\tdefault:\n\t\tblog(LOG_WARNING, \"unknown button type %d\", event->button());\n\t\treturn false;\n\t}\n\n\t// Why doesn't this work?\n\t//if (event->flags().testFlag(Qt::MouseEventCreatedDoubleClick))\n\t//\tclickCount = 2;\n\n\tQPoint pos = event->pos();\n\tbool insideSource = GetSourceRelativeXY(pos.x(), pos.y(), mouseEvent.x, mouseEvent.y);\n\n\tif (mouseUp || insideSource)\n\t\tobs_source_send_mouse_click(source, &mouseEvent, button, mouseUp, clickCount);\n\n\treturn true;\n}\n\nbool OBSBasicInteraction::HandleMouseMoveEvent(QMouseEvent *event)\n{\n\tstruct obs_mouse_event mouseEvent = {};\n\n\tbool mouseLeave = event->type() == QEvent::Leave;\n\n\tif (!mouseLeave) {\n\t\tmouseEvent.modifiers = TranslateQtMouseEventModifiers(event);\n\t\tQPoint pos = event->pos();\n\t\tmouseLeave = !GetSourceRelativeXY(pos.x(), pos.y(), mouseEvent.x, mouseEvent.y);\n\t}\n\n\tobs_source_send_mouse_move(source, &mouseEvent, mouseLeave);\n\n\treturn true;\n}\n\nbool OBSBasicInteraction::HandleMouseWheelEvent(QWheelEvent *event)\n{\n\tstruct obs_mouse_event mouseEvent = {};\n\n\tmouseEvent.modifiers = TranslateQtKeyboardEventModifiers(event, true);\n\n\tint xDelta = 0;\n\tint yDelta = 0;\n\n\tconst QPoint angleDelta = event->angleDelta();\n\tif (!event->pixelDelta().isNull()) {\n\t\tif (angleDelta.x())\n\t\t\txDelta = event->pixelDelta().x();\n\t\telse\n\t\t\tyDelta = event->pixelDelta().y();\n\t} else {\n\t\tif (angleDelta.x())\n\t\t\txDelta = angleDelta.x();\n\t\telse\n\t\t\tyDelta = angleDelta.y();\n\t}\n\n\tconst QPointF position = event->position();\n\tconst int x = position.x();\n\tconst int y = position.y();\n\n\tif (GetSourceRelativeXY(x, y, mouseEvent.x, mouseEvent.y)) {\n\t\tobs_source_send_mouse_wheel(source, &mouseEvent, xDelta, yDelta);\n\t}\n\n\treturn true;\n}\n\nbool OBSBasicInteraction::HandleFocusEvent(QFocusEvent *event)\n{\n\tbool focus = event->type() == QEvent::FocusIn;\n\n\tobs_source_send_focus(source, focus);\n\n\treturn true;\n}\n\nbool OBSBasicInteraction::HandleKeyEvent(QKeyEvent *event)\n{\n\tstruct obs_key_event keyEvent;\n\n\tQByteArray text = event->text().toUtf8();\n\tkeyEvent.modifiers = TranslateQtKeyboardEventModifiers(event, false);\n\tkeyEvent.text = text.data();\n\tkeyEvent.native_modifiers = event->nativeModifiers();\n\tkeyEvent.native_scancode = event->nativeScanCode();\n\tkeyEvent.native_vkey = event->nativeVirtualKey();\n\n\tbool keyUp = event->type() == QEvent::KeyRelease;\n\n\tobs_source_send_key_click(source, &keyEvent, keyUp);\n\n\treturn true;\n}\n\nvoid OBSBasicInteraction::Init()\n{\n\tshow();\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicInteraction.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_OBSBasicInteraction.h\"\n\n#include <obs.hpp>\n\n#include <QDialog>\n\nclass OBSBasic;\nclass OBSEventFilter;\n\nclass OBSBasicInteraction : public QDialog {\n\tQ_OBJECT\n\nprivate:\n\tOBSBasic *main;\n\n\tstd::unique_ptr<Ui::OBSBasicInteraction> ui;\n\tOBSSource source;\n\tOBSSignal removedSignal;\n\tOBSSignal renamedSignal;\n\tstd::unique_ptr<OBSEventFilter> eventFilter;\n\n\tstatic void SourceRemoved(void *data, calldata_t *params);\n\tstatic void SourceRenamed(void *data, calldata_t *params);\n\tstatic void DrawPreview(void *data, uint32_t cx, uint32_t cy);\n\n\tbool GetSourceRelativeXY(int mouseX, int mouseY, int &x, int &y);\n\n\tbool HandleMouseClickEvent(QMouseEvent *event);\n\tbool HandleMouseMoveEvent(QMouseEvent *event);\n\tbool HandleMouseWheelEvent(QWheelEvent *event);\n\tbool HandleFocusEvent(QFocusEvent *event);\n\tbool HandleKeyEvent(QKeyEvent *event);\n\n\tOBSEventFilter *BuildEventFilter();\n\npublic:\n\tOBSBasicInteraction(QWidget *parent, OBSSource source_);\n\t~OBSBasicInteraction();\n\n\tvoid Init();\n\nprotected:\n\tvirtual void closeEvent(QCloseEvent *event) override;\n\tvirtual bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicProperties.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasicProperties.hpp\"\n\n#include <utility/display-helpers.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <properties-view.hpp>\n#include <qt-wrappers.hpp>\n#include <vertical-scroll-area.hpp>\n\n#include <QPushButton>\n\n#ifdef _WIN32\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN 1\n#endif\n#include <Windows.h>\n#endif\n\n#include \"moc_OBSBasicProperties.cpp\"\n\nusing namespace std;\n\nstatic void CreateTransitionScene(OBSSource scene, const char *text, uint32_t color);\n\nOBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_)\n\t: QDialog(parent),\n\t  ui(new Ui::OBSBasicProperties),\n\t  main(qobject_cast<OBSBasic *>(parent)),\n\t  acceptClicked(false),\n\t  source(source_),\n\t  removedSignal(obs_source_get_signal_handler(source), \"remove\", OBSBasicProperties::SourceRemoved, this),\n\t  renamedSignal(obs_source_get_signal_handler(source), \"rename\", OBSBasicProperties::SourceRenamed, this),\n\t  oldSettings(obs_data_create())\n{\n\tint cx = (int)config_get_int(App()->GetAppConfig(), \"PropertiesWindow\", \"cx\");\n\tint cy = (int)config_get_int(App()->GetAppConfig(), \"PropertiesWindow\", \"cy\");\n\n\tenum obs_source_type type = obs_source_get_type(source);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\tui->buttonBox->button(QDialogButtonBox::Ok)->setFocus();\n\n\tif (cx > 400 && cy > 400)\n\t\tresize(cx, cy);\n\n\t/* The OBSData constructor increments the reference once */\n\tobs_data_release(oldSettings);\n\n\tOBSDataAutoRelease nd_settings = obs_source_get_settings(source);\n\tobs_data_apply(oldSettings, nd_settings);\n\n\tview = new OBSPropertiesView(nd_settings.Get(), source, (PropertiesReloadCallback)obs_source_properties,\n\t\t\t\t     (PropertiesUpdateCallback) nullptr, // No special handling required for undo/redo\n\t\t\t\t     (PropertiesVisualUpdateCb)obs_source_update);\n\tview->setMinimumHeight(150);\n\n\tui->propertiesLayout->addWidget(view);\n\n\tif (type == OBS_SOURCE_TYPE_TRANSITION) {\n\t\tui->transitionButton->setVisible(true);\n\t\tconnect(ui->transitionButton, &QPushButton::clicked, this,\n\t\t\t&OBSBasicProperties::previewTransitionClicked);\n\t} else {\n\t\tui->transitionButton->setVisible(false);\n\t}\n\n\tview->show();\n\tinstallEventFilter(CreateShortcutFilter());\n\n\tconst char *name = obs_source_get_name(source);\n\tsetWindowTitle(QTStr(\"Basic.PropertiesWindow\").arg(QT_UTF8(name)));\n\n\tobs_source_inc_showing(source);\n\n\tupdatePropertiesSignal.Connect(obs_source_get_signal_handler(source), \"update_properties\",\n\t\t\t\t       OBSBasicProperties::UpdateProperties, this);\n\n\tauto addDrawCallback = [this]() {\n\t\tobs_display_add_draw_callback(ui->preview->GetDisplay(), OBSBasicProperties::DrawPreview, this);\n\t};\n\tauto addTransitionDrawCallback = [this]() {\n\t\tobs_display_add_draw_callback(ui->preview->GetDisplay(), OBSBasicProperties::DrawTransitionPreview,\n\t\t\t\t\t      this);\n\t};\n\tuint32_t caps = obs_source_get_output_flags(source);\n\tbool drawable_type = type == OBS_SOURCE_TYPE_INPUT || type == OBS_SOURCE_TYPE_SCENE;\n\tbool drawable_preview = (caps & OBS_SOURCE_VIDEO) != 0;\n\n\tif (drawable_preview && drawable_type) {\n\t\tui->preview->show();\n\t\tconnect(ui->preview, &OBSQTDisplay::DisplayCreated, this, addDrawCallback);\n\n\t} else if (type == OBS_SOURCE_TYPE_TRANSITION) {\n\t\tsourceA = obs_source_create_private(\"scene\", \"sourceA\", nullptr);\n\t\tsourceB = obs_source_create_private(\"scene\", \"sourceB\", nullptr);\n\n\t\tuint32_t colorA = 0xFFB26F52;\n\t\tuint32_t colorB = 0xFF6FB252;\n\n\t\tCreateTransitionScene(sourceA.Get(), \"A\", colorA);\n\t\tCreateTransitionScene(sourceB.Get(), \"B\", colorB);\n\n\t\t/**\n\t\t * The cloned source is made from scratch, rather than using\n\t\t * obs_source_duplicate, as the stinger transition would not\n\t\t * play correctly otherwise.\n\t\t */\n\n\t\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\n\t\tsourceClone = obs_source_create_private(obs_source_get_id(source), \"clone\", settings);\n\n\t\tobs_source_inc_active(sourceClone);\n\t\tobs_transition_set(sourceClone, sourceA);\n\n\t\tauto updateCallback = [this]() {\n\t\t\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\t\t\tobs_source_update(sourceClone, settings);\n\n\t\t\tobs_transition_clear(sourceClone);\n\t\t\tobs_transition_set(sourceClone, sourceA);\n\t\t\tobs_transition_force_stop(sourceClone);\n\n\t\t\tdirection = true;\n\t\t};\n\n\t\tconnect(view, &OBSPropertiesView::Changed, this, updateCallback);\n\n\t\tui->preview->show();\n\t\tconnect(ui->preview, &OBSQTDisplay::DisplayCreated, this, addTransitionDrawCallback);\n\n\t} else {\n\t\tui->preview->hide();\n\t}\n\n\tconnect(ui->defaultsButton, &QPushButton::clicked, this, &OBSBasicProperties::restoreDefaultsClicked);\n}\n\nOBSBasicProperties::~OBSBasicProperties()\n{\n\tif (sourceClone) {\n\t\tobs_source_dec_active(sourceClone);\n\t}\n\tobs_source_dec_showing(source);\n\tmain->SaveProject();\n\tmain->UpdateContextBarDeferred(true);\n}\n\nstatic obs_source_t *CreateLabel(const char *name, size_t h)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tOBSDataAutoRelease font = obs_data_create();\n\n\tstd::string text;\n\ttext += \" \";\n\ttext += name;\n\ttext += \" \";\n\n#if defined(_WIN32)\n\tobs_data_set_string(font, \"face\", \"Arial\");\n#elif defined(__APPLE__)\n\tobs_data_set_string(font, \"face\", \"Helvetica\");\n#else\n\tobs_data_set_string(font, \"face\", \"Monospace\");\n#endif\n\tobs_data_set_int(font, \"flags\", 1); // Bold text\n\tobs_data_set_int(font, \"size\", min(int(h), 300));\n\n\tobs_data_set_obj(settings, \"font\", font);\n\tobs_data_set_string(settings, \"text\", text.c_str());\n\tobs_data_set_bool(settings, \"outline\", false);\n\n#ifdef _WIN32\n\tconst char *text_source_id = \"text_gdiplus\";\n#else\n\tconst char *text_source_id = \"text_ft2_source\";\n#endif\n\n\tobs_source_t *txtSource = obs_source_create_private(text_source_id, name, settings);\n\n\treturn txtSource;\n}\n\nstatic void CreateTransitionScene(OBSSource scene, const char *text, uint32_t color)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_int(settings, \"width\", obs_source_get_width(scene));\n\tobs_data_set_int(settings, \"height\", obs_source_get_height(scene));\n\tobs_data_set_int(settings, \"color\", color);\n\n\tOBSSourceAutoRelease colorBG = obs_source_create_private(\"color_source\", \"background\", settings);\n\n\tobs_scene_add(obs_scene_from_source(scene), colorBG);\n\n\tOBSSourceAutoRelease label = CreateLabel(text, obs_source_get_height(scene));\n\tobs_sceneitem_t *item = obs_scene_add(obs_scene_from_source(scene), label);\n\n\tvec2 size;\n\tvec2_set(&size, obs_source_get_width(scene),\n#ifdef _WIN32\n\t\t obs_source_get_height(scene));\n#else\n\t\t obs_source_get_height(scene) * 0.8);\n#endif\n\n\tobs_sceneitem_set_bounds(item, &size);\n\tobs_sceneitem_set_bounds_type(item, OBS_BOUNDS_SCALE_INNER);\n}\n\nstatic bool ConfirmReset(QWidget *parent)\n{\n\tQMessageBox::StandardButton button;\n\n\tbutton = OBSMessageBox::question(parent, QTStr(\"ConfirmReset.Title\"), QTStr(\"ConfirmReset.Text\"),\n\t\t\t\t\t QMessageBox::Yes | QMessageBox::No);\n\n\treturn button == QMessageBox::Yes;\n}\n\nvoid OBSBasicProperties::restoreDefaultsClicked()\n{\n\tif (!ConfirmReset(this)) {\n\t\treturn;\n\t}\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\tobs_data_clear(settings);\n\n\tif (!view->DeferUpdate()) {\n\t\tobs_source_update(source, nullptr);\n\t}\n\n\tview->ReloadProperties();\n}\n\nvoid OBSBasicProperties::previewTransitionClicked()\n{\n\tOBSSource start;\n\tOBSSource end;\n\n\tif (direction) {\n\t\tstart = sourceA;\n\t\tend = sourceB;\n\t} else {\n\t\tstart = sourceB;\n\t\tend = sourceA;\n\t}\n\n\tobs_transition_set(sourceClone, start);\n\tobs_transition_start(sourceClone, OBS_TRANSITION_MODE_AUTO, main->GetTransitionDuration(), end);\n\tdirection = !direction;\n\n\tstart = nullptr;\n\tend = nullptr;\n}\n\nvoid OBSBasicProperties::SourceRemoved(void *data, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<OBSBasicProperties *>(data), \"close\");\n}\n\nvoid OBSBasicProperties::SourceRenamed(void *data, calldata_t *params)\n{\n\tconst char *name = calldata_string(params, \"new_name\");\n\tQString title = QTStr(\"Basic.PropertiesWindow\").arg(QT_UTF8(name));\n\n\tQMetaObject::invokeMethod(static_cast<OBSBasicProperties *>(data), \"setWindowTitle\", Q_ARG(QString, title));\n}\n\nvoid OBSBasicProperties::UpdateProperties(void *data, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<OBSBasicProperties *>(data)->view, \"ReloadProperties\");\n}\n\nvoid OBSBasicProperties::on_buttonBox_clicked(QAbstractButton *button)\n{\n\tQDialogButtonBox::ButtonRole val = ui->buttonBox->buttonRole(button);\n\n\tif (val == QDialogButtonBox::AcceptRole) {\n\n\t\tstd::string scene_uuid = obs_source_get_uuid(main->GetCurrentSceneSource());\n\n\t\tauto undo_redo = [scene_uuid](const std::string &data) {\n\t\t\tOBSDataAutoRelease settings = obs_data_create_from_json(data.c_str());\n\t\t\tOBSSourceAutoRelease source =\n\t\t\t\tobs_get_source_by_uuid(obs_data_get_string(settings, \"undo_uuid\"));\n\t\t\tobs_source_reset_settings(source, settings);\n\n\t\t\tobs_source_update_properties(source);\n\n\t\t\tOBSSourceAutoRelease scene_source = obs_get_source_by_uuid(scene_uuid.c_str());\n\n\t\t\tOBSBasic::Get()->SetCurrentScene(scene_source.Get(), true);\n\t\t};\n\n\t\tOBSDataAutoRelease new_settings = obs_data_create();\n\t\tOBSDataAutoRelease curr_settings = obs_source_get_settings(source);\n\t\tobs_data_apply(new_settings, curr_settings);\n\t\tobs_data_set_string(new_settings, \"undo_uuid\", obs_source_get_uuid(source));\n\t\tobs_data_set_string(oldSettings, \"undo_uuid\", obs_source_get_uuid(source));\n\n\t\tstd::string undo_data(obs_data_get_json(oldSettings));\n\t\tstd::string redo_data(obs_data_get_json(new_settings));\n\n\t\tif (undo_data.compare(redo_data) != 0)\n\t\t\tmain->undo_s.add_action(QTStr(\"Undo.Properties\").arg(obs_source_get_name(source)), undo_redo,\n\t\t\t\t\t\tundo_redo, undo_data, redo_data);\n\n\t\tacceptClicked = true;\n\t\tclose();\n\n\t\tif (view->DeferUpdate())\n\t\t\tview->UpdateSettings();\n\n\t} else if (val == QDialogButtonBox::RejectRole) {\n\t\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\t\tobs_data_clear(settings);\n\n\t\tif (view->DeferUpdate())\n\t\t\tobs_data_apply(settings, oldSettings);\n\t\telse\n\t\t\tobs_source_update(source, oldSettings);\n\n\t\tclose();\n\t}\n}\n\nvoid OBSBasicProperties::DrawPreview(void *data, uint32_t cx, uint32_t cy)\n{\n\tOBSBasicProperties *window = static_cast<OBSBasicProperties *>(data);\n\n\tif (!window->source)\n\t\treturn;\n\n\tuint32_t sourceCX = max(obs_source_get_width(window->source), 1u);\n\tuint32_t sourceCY = max(obs_source_get_height(window->source), 1u);\n\n\tint x, y;\n\tint newCX, newCY;\n\tfloat scale;\n\n\tGetScaleAndCenterPos(sourceCX, sourceCY, cx, cy, x, y, scale);\n\n\tnewCX = int(scale * float(sourceCX));\n\tnewCY = int(scale * float(sourceCY));\n\n\tgs_viewport_push();\n\tgs_projection_push();\n\tconst bool previous = gs_set_linear_srgb(true);\n\n\tgs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), -100.0f, 100.0f);\n\tgs_set_viewport(x, y, newCX, newCY);\n\tobs_source_video_render(window->source);\n\n\tgs_set_linear_srgb(previous);\n\tgs_projection_pop();\n\tgs_viewport_pop();\n}\n\nvoid OBSBasicProperties::DrawTransitionPreview(void *data, uint32_t cx, uint32_t cy)\n{\n\tOBSBasicProperties *window = static_cast<OBSBasicProperties *>(data);\n\n\tif (!window->sourceClone)\n\t\treturn;\n\n\tuint32_t sourceCX = max(obs_source_get_width(window->sourceClone), 1u);\n\tuint32_t sourceCY = max(obs_source_get_height(window->sourceClone), 1u);\n\n\tint x, y;\n\tint newCX, newCY;\n\tfloat scale;\n\n\tGetScaleAndCenterPos(sourceCX, sourceCY, cx, cy, x, y, scale);\n\n\tnewCX = int(scale * float(sourceCX));\n\tnewCY = int(scale * float(sourceCY));\n\n\tgs_viewport_push();\n\tgs_projection_push();\n\tgs_ortho(0.0f, float(sourceCX), 0.0f, float(sourceCY), -100.0f, 100.0f);\n\tgs_set_viewport(x, y, newCX, newCY);\n\n\tobs_source_video_render(window->sourceClone);\n\n\tgs_projection_pop();\n\tgs_viewport_pop();\n}\n\nvoid OBSBasicProperties::Cleanup()\n{\n\tconfig_set_int(App()->GetAppConfig(), \"PropertiesWindow\", \"cx\", width());\n\tconfig_set_int(App()->GetAppConfig(), \"PropertiesWindow\", \"cy\", height());\n\n\tobs_display_remove_draw_callback(ui->preview->GetDisplay(), OBSBasicProperties::DrawPreview, this);\n\tobs_display_remove_draw_callback(ui->preview->GetDisplay(), OBSBasicProperties::DrawTransitionPreview, this);\n}\n\nvoid OBSBasicProperties::reject()\n{\n\tif (!acceptClicked && (CheckSettings() != 0)) {\n\t\tif (!ConfirmQuit()) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tCleanup();\n\tdone(0);\n}\n\nvoid OBSBasicProperties::closeEvent(QCloseEvent *event)\n{\n\tQDialog::closeEvent(event);\n\tif (event->isAccepted())\n\t\tCleanup();\n}\n\nbool OBSBasicProperties::nativeEvent(const QByteArray &, void *message, qintptr *)\n{\n#ifdef _WIN32\n\tconst MSG &msg = *static_cast<MSG *>(message);\n\tswitch (msg.message) {\n\tcase WM_MOVE:\n\t\tfor (OBSQTDisplay *const display : findChildren<OBSQTDisplay *>()) {\n\t\t\tdisplay->OnMove();\n\t\t}\n\t\tbreak;\n\tcase WM_DISPLAYCHANGE:\n\t\tfor (OBSQTDisplay *const display : findChildren<OBSQTDisplay *>()) {\n\t\t\tdisplay->OnDisplayChange();\n\t\t}\n\t}\n#else\n\tUNUSED_PARAMETER(message);\n#endif\n\n\treturn false;\n}\n\nvoid OBSBasicProperties::Init()\n{\n\tshow();\n}\n\nint OBSBasicProperties::CheckSettings()\n{\n\tOBSDataAutoRelease currentSettings = obs_source_get_settings(source);\n\tconst char *oldSettingsJson = obs_data_get_json(oldSettings);\n\tconst char *currentSettingsJson = obs_data_get_json(currentSettings);\n\n\treturn strcmp(currentSettingsJson, oldSettingsJson);\n}\n\nbool OBSBasicProperties::ConfirmQuit()\n{\n\tQMessageBox::StandardButton button;\n\n\tbutton = OBSMessageBox::question(this, QTStr(\"Basic.PropertiesWindow.ConfirmTitle\"),\n\t\t\t\t\t QTStr(\"Basic.PropertiesWindow.Confirm\"),\n\t\t\t\t\t QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);\n\n\tswitch (button) {\n\tcase QMessageBox::Save:\n\t\tacceptClicked = true;\n\t\tif (view->DeferUpdate())\n\t\t\tview->UpdateSettings();\n\t\t// Do nothing because the settings are already updated\n\t\tbreak;\n\tcase QMessageBox::Discard:\n\t\tobs_source_update(source, oldSettings);\n\t\tbreak;\n\tcase QMessageBox::Cancel:\n\t\treturn false;\n\t\tbreak;\n\tdefault:\n\t\t/* If somehow the dialog fails to show, just default to\n\t\t * saving the settings. */\n\t\tbreak;\n\t}\n\treturn true;\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicProperties.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_OBSBasicProperties.h\"\n\n#include <QDialog>\n\nclass OBSBasic;\nclass OBSPropertiesView;\n\nclass OBSBasicProperties : public QDialog {\n\tQ_OBJECT\n\nprivate:\n\tOBSBasic *main;\n\n\tstd::unique_ptr<Ui::OBSBasicProperties> ui;\n\tbool acceptClicked;\n\n\tOBSSource source;\n\tOBSSignal removedSignal;\n\tOBSSignal renamedSignal;\n\tOBSSignal updatePropertiesSignal;\n\tOBSData oldSettings;\n\tOBSPropertiesView *view;\n\tQSplitter *windowSplitter;\n\n\tOBSSourceAutoRelease sourceA;\n\tOBSSourceAutoRelease sourceB;\n\tOBSSourceAutoRelease sourceClone;\n\tbool direction = true;\n\n\tstatic void SourceRemoved(void *data, calldata_t *params);\n\tstatic void SourceRenamed(void *data, calldata_t *params);\n\tstatic void UpdateProperties(void *data, calldata_t *params);\n\tstatic void DrawPreview(void *data, uint32_t cx, uint32_t cy);\n\tstatic void DrawTransitionPreview(void *data, uint32_t cx, uint32_t cy);\n\tvoid UpdateCallback(void *obj, obs_data_t *settings);\n\tbool ConfirmQuit();\n\tint CheckSettings();\n\tvoid Cleanup();\n\nprivate slots:\n\tvoid on_buttonBox_clicked(QAbstractButton *button);\n\tvoid restoreDefaultsClicked();\n\tvoid previewTransitionClicked();\n\npublic:\n\tOBSBasicProperties(QWidget *parent, OBSSource source_);\n\t~OBSBasicProperties();\n\n\tvoid Init();\n\nprotected:\n\tvirtual void closeEvent(QCloseEvent *event) override;\n\tvirtual bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;\n\tvirtual void reject() override;\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicSourceSelect.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasicSourceSelect.hpp\"\n\n#include <qt-wrappers.hpp>\n\n#include \"moc_OBSBasicSourceSelect.cpp\"\n\nstruct AddSourceData {\n\t/* Input data */\n\tobs_source_t *source;\n\tbool visible;\n\tobs_transform_info *transform = nullptr;\n\tobs_sceneitem_crop *crop = nullptr;\n\tobs_blending_method *blend_method = nullptr;\n\tobs_blending_type *blend_mode = nullptr;\n\tobs_scale_type *scale_type = nullptr;\n\tconst char *show_transition_id = nullptr;\n\tconst char *hide_transition_id = nullptr;\n\tOBSData show_transition_settings;\n\tOBSData hide_transition_settings;\n\tuint32_t show_transition_duration = 300;\n\tuint32_t hide_transition_duration = 300;\n\tOBSData private_settings;\n\n\t/* Return data */\n\tobs_sceneitem_t *scene_item = nullptr;\n};\n\nbool OBSBasicSourceSelect::EnumSources(void *data, obs_source_t *source)\n{\n\tif (obs_source_is_hidden(source))\n\t\treturn true;\n\n\tOBSBasicSourceSelect *window = static_cast<OBSBasicSourceSelect *>(data);\n\tconst char *name = obs_source_get_name(source);\n\tconst char *id = obs_source_get_unversioned_id(source);\n\n\tif (strcmp(id, window->id) == 0)\n\t\twindow->ui->sourceList->addItem(QT_UTF8(name));\n\n\treturn true;\n}\n\nbool OBSBasicSourceSelect::EnumGroups(void *data, obs_source_t *source)\n{\n\tOBSBasicSourceSelect *window = static_cast<OBSBasicSourceSelect *>(data);\n\tconst char *name = obs_source_get_name(source);\n\tconst char *id = obs_source_get_unversioned_id(source);\n\n\tif (strcmp(id, window->id) == 0) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tOBSScene scene = main->GetCurrentScene();\n\n\t\tobs_sceneitem_t *existing = obs_scene_get_group(scene, name);\n\t\tif (!existing)\n\t\t\twindow->ui->sourceList->addItem(QT_UTF8(name));\n\t}\n\n\treturn true;\n}\n\nvoid OBSBasicSourceSelect::OBSSourceAdded(void *data, calldata_t *calldata)\n{\n\tOBSBasicSourceSelect *window = static_cast<OBSBasicSourceSelect *>(data);\n\tobs_source_t *source = (obs_source_t *)calldata_ptr(calldata, \"source\");\n\n\tQMetaObject::invokeMethod(window, \"SourceAdded\", Q_ARG(OBSSource, source));\n}\n\nvoid OBSBasicSourceSelect::OBSSourceRemoved(void *data, calldata_t *calldata)\n{\n\tOBSBasicSourceSelect *window = static_cast<OBSBasicSourceSelect *>(data);\n\tobs_source_t *source = (obs_source_t *)calldata_ptr(calldata, \"source\");\n\n\tQMetaObject::invokeMethod(window, \"SourceRemoved\", Q_ARG(OBSSource, source));\n}\n\nvoid OBSBasicSourceSelect::SourceAdded(OBSSource source)\n{\n\tconst char *name = obs_source_get_name(source);\n\tconst char *sourceId = obs_source_get_unversioned_id(source);\n\n\tif (strcmp(sourceId, id) != 0)\n\t\treturn;\n\n\tui->sourceList->addItem(name);\n}\n\nvoid OBSBasicSourceSelect::SourceRemoved(OBSSource source)\n{\n\tconst char *name = obs_source_get_name(source);\n\tconst char *sourceId = obs_source_get_unversioned_id(source);\n\n\tif (strcmp(sourceId, id) != 0)\n\t\treturn;\n\n\tQList<QListWidgetItem *> items = ui->sourceList->findItems(name, Qt::MatchFixedString);\n\n\tif (!items.count())\n\t\treturn;\n\n\tdelete items[0];\n}\n\nstatic void AddSource(void *_data, obs_scene_t *scene)\n{\n\tAddSourceData *data = (AddSourceData *)_data;\n\tobs_sceneitem_t *sceneitem;\n\n\tsceneitem = obs_scene_add(scene, data->source);\n\n\tif (data->transform != nullptr)\n\t\tobs_sceneitem_set_info2(sceneitem, data->transform);\n\tif (data->crop != nullptr)\n\t\tobs_sceneitem_set_crop(sceneitem, data->crop);\n\tif (data->blend_method != nullptr)\n\t\tobs_sceneitem_set_blending_method(sceneitem, *data->blend_method);\n\tif (data->blend_mode != nullptr)\n\t\tobs_sceneitem_set_blending_mode(sceneitem, *data->blend_mode);\n\tif (data->scale_type != nullptr)\n\t\tobs_sceneitem_set_scale_filter(sceneitem, *data->scale_type);\n\n\tif (data->show_transition_id && *data->show_transition_id) {\n\t\tOBSSourceAutoRelease source = obs_source_create(data->show_transition_id, data->show_transition_id,\n\t\t\t\t\t\t\t\tdata->show_transition_settings, nullptr);\n\n\t\tif (source)\n\t\t\tobs_sceneitem_set_transition(sceneitem, true, source);\n\t}\n\n\tif (data->hide_transition_id && *data->hide_transition_id) {\n\t\tOBSSourceAutoRelease source = obs_source_create(data->hide_transition_id, data->hide_transition_id,\n\t\t\t\t\t\t\t\tdata->hide_transition_settings, nullptr);\n\n\t\tif (source)\n\t\t\tobs_sceneitem_set_transition(sceneitem, false, source);\n\t}\n\n\tobs_sceneitem_set_transition_duration(sceneitem, true, data->show_transition_duration);\n\tobs_sceneitem_set_transition_duration(sceneitem, false, data->hide_transition_duration);\n\n\tobs_sceneitem_set_visible(sceneitem, data->visible);\n\n\tif (data->private_settings) {\n\t\tOBSDataAutoRelease newPrivateSettings = obs_sceneitem_get_private_settings(sceneitem);\n\t\tobs_data_apply(newPrivateSettings, data->private_settings);\n\t}\n\n\tdata->scene_item = sceneitem;\n}\n\nchar *get_new_source_name(const char *name, const char *format)\n{\n\tstruct dstr new_name = {0};\n\tint inc = 0;\n\n\tdstr_copy(&new_name, name);\n\n\tfor (;;) {\n\t\tOBSSourceAutoRelease existing_source = obs_get_source_by_name(new_name.array);\n\t\tif (!existing_source)\n\t\t\tbreak;\n\n\t\tdstr_printf(&new_name, format, name, ++inc + 1);\n\t}\n\n\treturn new_name.array;\n}\n\nstatic void AddExisting(OBSSource source, bool visible, bool duplicate, SourceCopyInfo *info = nullptr)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tOBSScene scene = main->GetCurrentScene();\n\tif (!scene)\n\t\treturn;\n\n\tif (duplicate) {\n\t\tOBSSource from = source;\n\t\tchar *new_name = get_new_source_name(obs_source_get_name(source), \"%s %d\");\n\t\tsource = obs_source_duplicate(from, new_name, false);\n\t\tobs_source_release(source);\n\t\tbfree(new_name);\n\n\t\tif (!source)\n\t\t\treturn;\n\t}\n\n\tAddSourceData data;\n\tdata.source = source;\n\tdata.visible = visible;\n\n\tif (info) {\n\t\tdata.transform = &info->transform;\n\t\tdata.crop = &info->crop;\n\t\tdata.blend_method = &info->blend_method;\n\t\tdata.blend_mode = &info->blend_mode;\n\t\tdata.scale_type = &info->scale_type;\n\t\tdata.show_transition_id = info->show_transition_id;\n\t\tdata.hide_transition_id = info->hide_transition_id;\n\t\tdata.show_transition_settings = std::move(info->show_transition_settings);\n\t\tdata.hide_transition_settings = std::move(info->hide_transition_settings);\n\t\tdata.show_transition_duration = info->show_transition_duration;\n\t\tdata.hide_transition_duration = info->hide_transition_duration;\n\t\tdata.private_settings = std::move(info->private_settings);\n\t}\n\n\tobs_enter_graphics();\n\tobs_scene_atomic_update(scene, AddSource, &data);\n\tobs_leave_graphics();\n}\n\nstatic void AddExisting(const char *name, bool visible, bool duplicate)\n{\n\tOBSSourceAutoRelease source = obs_get_source_by_name(name);\n\tif (source) {\n\t\tAddExisting(source.Get(), visible, duplicate);\n\t}\n}\n\nbool AddNew(QWidget *parent, const char *id, const char *name, const bool visible, OBSSource &newSource,\n\t    OBSSceneItem &newSceneItem)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tOBSScene scene = main->GetCurrentScene();\n\tbool success = false;\n\tif (!scene)\n\t\treturn false;\n\n\tOBSSourceAutoRelease source = obs_get_source_by_name(name);\n\tif (source && parent) {\n\t\tOBSMessageBox::information(parent, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\n\t} else {\n\t\tconst char *v_id = obs_get_latest_input_type_id(id);\n\t\tsource = obs_source_create(v_id, name, NULL, nullptr);\n\n\t\tif (source) {\n\t\t\tAddSourceData data;\n\t\t\tdata.source = source;\n\t\t\tdata.visible = visible;\n\n\t\t\tobs_enter_graphics();\n\t\t\tobs_scene_atomic_update(scene, AddSource, &data);\n\t\t\tobs_leave_graphics();\n\n\t\t\tnewSource = source;\n\t\t\tnewSceneItem = data.scene_item;\n\n\t\t\t/* set monitoring if source monitors by default */\n\t\t\tuint32_t flags = obs_source_get_output_flags(source);\n\t\t\tif ((flags & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0) {\n\t\t\t\tobs_source_set_monitoring_type(source, OBS_MONITORING_TYPE_MONITOR_ONLY);\n\t\t\t}\n\n\t\t\tsuccess = true;\n\t\t}\n\t}\n\n\treturn success;\n}\n\nvoid OBSBasicSourceSelect::on_buttonBox_accepted()\n{\n\tbool useExisting = ui->selectExisting->isChecked();\n\tbool visible = ui->sourceVisible->isChecked();\n\n\tif (useExisting) {\n\t\tQListWidgetItem *item = ui->sourceList->currentItem();\n\t\tif (!item)\n\t\t\treturn;\n\n\t\tQString source_name = item->text();\n\t\tAddExisting(QT_TO_UTF8(source_name), visible, false);\n\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tconst char *scene_name = obs_source_get_name(main->GetCurrentSceneSource());\n\n\t\tauto undo = [scene_name, main](const std::string &) {\n\t\t\tobs_source_t *scene_source = obs_get_source_by_name(scene_name);\n\t\t\tmain->SetCurrentScene(scene_source, true);\n\t\t\tobs_source_release(scene_source);\n\n\t\t\tobs_scene_t *scene = obs_get_scene_by_name(scene_name);\n\t\t\tOBSSceneItem item;\n\t\t\tauto cb = [](obs_scene_t *, obs_sceneitem_t *sceneitem, void *data) {\n\t\t\t\tOBSSceneItem &last = *static_cast<OBSSceneItem *>(data);\n\t\t\t\tlast = sceneitem;\n\t\t\t\treturn true;\n\t\t\t};\n\t\t\tobs_scene_enum_items(scene, cb, &item);\n\n\t\t\tobs_sceneitem_remove(item);\n\t\t\tobs_scene_release(scene);\n\t\t};\n\n\t\tauto redo = [scene_name, main, source_name, visible](const std::string &) {\n\t\t\tobs_source_t *scene_source = obs_get_source_by_name(scene_name);\n\t\t\tmain->SetCurrentScene(scene_source, true);\n\t\t\tobs_source_release(scene_source);\n\t\t\tAddExisting(QT_TO_UTF8(source_name), visible, false);\n\t\t};\n\n\t\tundo_s.add_action(QTStr(\"Undo.Add\").arg(source_name), undo, redo, \"\", \"\");\n\t} else {\n\t\tif (ui->sourceName->text().isEmpty()) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\t\treturn;\n\t\t}\n\n\t\tOBSSceneItem item;\n\t\tif (!AddNew(this, id, QT_TO_UTF8(ui->sourceName->text()), visible, newSource, item))\n\t\t\treturn;\n\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tstd::string scene_name = obs_source_get_name(main->GetCurrentSceneSource());\n\t\tauto undo = [scene_name, main](const std::string &data) {\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_name(data.c_str());\n\t\t\tobs_source_remove(source);\n\n\t\t\tOBSSourceAutoRelease scene_source = obs_get_source_by_name(scene_name.c_str());\n\t\t\tmain->SetCurrentScene(scene_source.Get(), true);\n\t\t};\n\t\tOBSDataAutoRelease wrapper = obs_data_create();\n\t\tobs_data_set_string(wrapper, \"id\", id);\n\t\tobs_data_set_int(wrapper, \"item_id\", obs_sceneitem_get_id(item));\n\t\tobs_data_set_string(wrapper, \"name\", ui->sourceName->text().toUtf8().constData());\n\t\tobs_data_set_bool(wrapper, \"visible\", visible);\n\n\t\tauto redo = [scene_name, main](const std::string &data) {\n\t\t\tOBSSourceAutoRelease scene_source = obs_get_source_by_name(scene_name.c_str());\n\t\t\tmain->SetCurrentScene(scene_source.Get(), true);\n\n\t\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\t\tOBSSource source;\n\t\t\tOBSSceneItem item;\n\t\t\tAddNew(NULL, obs_data_get_string(dat, \"id\"), obs_data_get_string(dat, \"name\"),\n\t\t\t       obs_data_get_bool(dat, \"visible\"), source, item);\n\t\t\tobs_sceneitem_set_id(item, (int64_t)obs_data_get_int(dat, \"item_id\"));\n\t\t};\n\t\tundo_s.add_action(QTStr(\"Undo.Add\").arg(ui->sourceName->text()), undo, redo,\n\t\t\t\t  std::string(obs_source_get_name(newSource)), std::string(obs_data_get_json(wrapper)));\n\t}\n\n\tdone(DialogCode::Accepted);\n}\n\nvoid OBSBasicSourceSelect::on_buttonBox_rejected()\n{\n\tdone(DialogCode::Rejected);\n}\n\nstatic inline const char *GetSourceDisplayName(const char *id)\n{\n\tif (strcmp(id, \"scene\") == 0)\n\t\treturn Str(\"Basic.Scene\");\n\telse if (strcmp(id, \"group\") == 0)\n\t\treturn Str(\"Group\");\n\tconst char *v_id = obs_get_latest_input_type_id(id);\n\treturn obs_source_get_display_name(v_id);\n}\n\nOBSBasicSourceSelect::OBSBasicSourceSelect(OBSBasic *parent, const char *id_, undo_stack &undo_s)\n\t: QDialog(parent),\n\t  ui(new Ui::OBSBasicSourceSelect),\n\t  id(id_),\n\t  undo_s(undo_s)\n{\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\n\tui->sourceList->setAttribute(Qt::WA_MacShowFocusRect, false);\n\n\tQString placeHolderText{QT_UTF8(GetSourceDisplayName(id))};\n\n\tQString text{placeHolderText};\n\tint i = 2;\n\tOBSSourceAutoRelease source = nullptr;\n\twhile ((source = obs_get_source_by_name(QT_TO_UTF8(text)))) {\n\t\ttext = QString(\"%1 %2\").arg(placeHolderText).arg(i++);\n\t}\n\n\tui->sourceName->setText(text);\n\tui->sourceName->setFocus(); //Fixes deselect of text.\n\tui->sourceName->selectAll();\n\n\tinstallEventFilter(CreateShortcutFilter());\n\n\tconnect(ui->createNew, &QRadioButton::pressed, this, [&]() {\n\t\tQPushButton *button = ui->buttonBox->button(QDialogButtonBox::Ok);\n\t\tif (!button->isEnabled())\n\t\t\tbutton->setEnabled(true);\n\t});\n\tconnect(ui->selectExisting, &QRadioButton::pressed, this, [&]() {\n\t\tQPushButton *button = ui->buttonBox->button(QDialogButtonBox::Ok);\n\t\tbool enabled = ui->sourceList->selectedItems().size() != 0;\n\t\tif (button->isEnabled() != enabled)\n\t\t\tbutton->setEnabled(enabled);\n\t});\n\tconnect(ui->sourceList, &QListWidget::itemSelectionChanged, this, [&]() {\n\t\tQPushButton *button = ui->buttonBox->button(QDialogButtonBox::Ok);\n\t\tif (!button->isEnabled())\n\t\t\tbutton->setEnabled(true);\n\t});\n\n\tif (strcmp(id_, \"scene\") == 0) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tOBSSource curSceneSource = main->GetCurrentSceneSource();\n\n\t\tui->selectExisting->setChecked(true);\n\t\tui->createNew->setChecked(false);\n\t\tui->createNew->setEnabled(false);\n\t\tui->sourceName->setEnabled(false);\n\t\tui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);\n\n\t\tint count = main->ui->scenes->count();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tQListWidgetItem *item = main->ui->scenes->item(i);\n\t\t\tOBSScene scene = GetOBSRef<OBSScene>(item);\n\t\t\tOBSSource sceneSource = obs_scene_get_source(scene);\n\n\t\t\tif (curSceneSource == sceneSource)\n\t\t\t\tcontinue;\n\n\t\t\tconst char *name = obs_source_get_name(sceneSource);\n\t\t\tui->sourceList->addItem(QT_UTF8(name));\n\t\t}\n\t} else if (strcmp(id_, \"group\") == 0) {\n\t\tobs_enum_sources(EnumGroups, this);\n\t} else {\n\t\tobs_enum_sources(EnumSources, this);\n\t}\n}\n\nvoid OBSBasicSourceSelect::SourcePaste(SourceCopyInfo &info, bool dup)\n{\n\tOBSSource source = OBSGetStrongRef(info.weak_source);\n\tif (!source)\n\t\treturn;\n\n\tAddExisting(source, info.visible, dup, &info);\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicSourceSelect.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_OBSBasicSourceSelect.h\"\n\n#include <utility/undo_stack.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <obs.hpp>\n\n#include <QDialog>\n\nclass OBSBasicSourceSelect : public QDialog {\n\tQ_OBJECT\n\nprivate:\n\tstd::unique_ptr<Ui::OBSBasicSourceSelect> ui;\n\tconst char *id;\n\tundo_stack &undo_s;\n\n\tstatic bool EnumSources(void *data, obs_source_t *source);\n\tstatic bool EnumGroups(void *data, obs_source_t *source);\n\n\tstatic void OBSSourceRemoved(void *data, calldata_t *calldata);\n\tstatic void OBSSourceAdded(void *data, calldata_t *calldata);\n\nprivate slots:\n\tvoid on_buttonBox_accepted();\n\tvoid on_buttonBox_rejected();\n\n\tvoid SourceAdded(OBSSource source);\n\tvoid SourceRemoved(OBSSource source);\n\npublic:\n\tOBSBasicSourceSelect(OBSBasic *parent, const char *id, undo_stack &undo_s);\n\n\tOBSSource newSource;\n\n\tstatic void SourcePaste(SourceCopyInfo &info, bool duplicate);\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicTransform.cpp",
    "content": "#include \"OBSBasicTransform.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include \"moc_OBSBasicTransform.cpp\"\n\nnamespace {\nstatic bool find_sel(obs_scene_t *, obs_sceneitem_t *item, void *param)\n{\n\tOBSSceneItem &dst = *static_cast<OBSSceneItem *>(param);\n\n\tif (obs_sceneitem_selected(item)) {\n\t\tdst = item;\n\t\treturn false;\n\t}\n\tif (obs_sceneitem_is_group(item)) {\n\t\tobs_sceneitem_group_enum_items(item, find_sel, param);\n\t\tif (!!dst) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n};\n\nstatic OBSSceneItem FindASelectedItem(obs_scene_t *scene)\n{\n\tOBSSceneItem item;\n\tobs_scene_enum_items(scene, find_sel, &item);\n\treturn item;\n}\nstatic vec2 getAlignmentConversion(uint32_t alignment)\n{\n\tvec2 ratio = {0.5f, 0.5f};\n\tif (alignment & OBS_ALIGN_RIGHT) {\n\t\tratio.x = 1.0f;\n\t}\n\tif (alignment & OBS_ALIGN_LEFT) {\n\t\tratio.x = 0.0f;\n\t}\n\tif (alignment & OBS_ALIGN_BOTTOM) {\n\t\tratio.y = 1.0f;\n\t}\n\tif (alignment & OBS_ALIGN_TOP) {\n\t\tratio.y = 0.0f;\n\t}\n\n\treturn ratio;\n}\n} // namespace\n\n#define COMBO_CHANGED &QComboBox::currentIndexChanged\n#define ALIGN_CHANGED &AlignmentSelector::currentIndexChanged\n#define ISCROLL_CHANGED &QSpinBox::valueChanged\n#define DSCROLL_CHANGED &QDoubleSpinBox::valueChanged\n\nOBSBasicTransform::OBSBasicTransform(OBSSceneItem item, OBSBasic *parent)\n\t: QDialog(parent),\n\t  ui(new Ui::OBSBasicTransform),\n\t  main(parent)\n{\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\n\tpositionAlignment = new AlignmentSelector(this);\n\tpositionAlignment->setAccessibleName(QTStr(\"Basic.TransformWindow.Alignment\"));\n\tui->alignmentLayout->addWidget(positionAlignment);\n\tpositionAlignment->setAlignment(Qt::AlignTop | Qt::AlignLeft);\n\n\tboundsAlignment = new AlignmentSelector(this);\n\tboundsAlignment->setAccessibleName(QTStr(\"Basic.TransformWindow.BoundsAlignment\"));\n\tboundsAlignment->setEnabled(false);\n\tui->boundsAlignmentLayout->addWidget(boundsAlignment);\n\tboundsAlignment->setAlignment(Qt::AlignTop | Qt::AlignLeft);\n\n\tsetTabOrder(ui->rotation, positionAlignment);\n\tsetTabOrder(ui->boundsType, boundsAlignment);\n\n\thookWidget(ui->positionX, DSCROLL_CHANGED, &OBSBasicTransform::onControlChanged);\n\thookWidget(ui->positionY, DSCROLL_CHANGED, &OBSBasicTransform::onControlChanged);\n\thookWidget(ui->rotation, DSCROLL_CHANGED, &OBSBasicTransform::onControlChanged);\n\thookWidget(ui->sizeX, DSCROLL_CHANGED, &OBSBasicTransform::onControlChanged);\n\thookWidget(ui->sizeY, DSCROLL_CHANGED, &OBSBasicTransform::onControlChanged);\n\thookWidget(positionAlignment.get(), ALIGN_CHANGED, &OBSBasicTransform::onAlignChanged);\n\thookWidget(ui->boundsType, COMBO_CHANGED, &OBSBasicTransform::onBoundsType);\n\thookWidget(boundsAlignment.get(), ALIGN_CHANGED, &OBSBasicTransform::onControlChanged);\n\thookWidget(ui->boundsWidth, DSCROLL_CHANGED, &OBSBasicTransform::onControlChanged);\n\thookWidget(ui->boundsHeight, DSCROLL_CHANGED, &OBSBasicTransform::onControlChanged);\n\thookWidget(ui->cropLeft, ISCROLL_CHANGED, &OBSBasicTransform::onCropChanged);\n\thookWidget(ui->cropRight, ISCROLL_CHANGED, &OBSBasicTransform::onCropChanged);\n\thookWidget(ui->cropTop, ISCROLL_CHANGED, &OBSBasicTransform::onCropChanged);\n\thookWidget(ui->cropBottom, ISCROLL_CHANGED, &OBSBasicTransform::onCropChanged);\n#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)\n\thookWidget(ui->cropToBounds, &QCheckBox::checkStateChanged, &OBSBasicTransform::onControlChanged);\n#else\n\thookWidget(ui->cropToBounds, &QCheckBox::stateChanged, &OBSBasicTransform::onControlChanged);\n#endif\n\tui->buttonBox->button(QDialogButtonBox::Close)->setDefault(true);\n\n\tconnect(ui->buttonBox->button(QDialogButtonBox::Reset), &QPushButton::clicked, main,\n\t\t&OBSBasic::on_actionResetTransform_triggered);\n\n\tinstallEventFilter(CreateShortcutFilter());\n\n\tOBSScene scene = obs_sceneitem_get_scene(item);\n\tsetScene(scene);\n\tsetItem(item);\n\n\tstd::string name = obs_source_get_name(obs_sceneitem_get_source(item));\n\tsetWindowTitle(QTStr(\"Basic.TransformWindow.Title\").arg(name.c_str()));\n\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(main->GetCurrentScene(), false);\n\tundo_data = std::string(obs_data_get_json(wrapper));\n\n\tadjustSize();\n\tsetMinimumSize(size());\n\tsetMaximumSize(size());\n}\n\nOBSBasicTransform::~OBSBasicTransform()\n{\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(main->GetCurrentScene(), false);\n\n\tauto undo_redo = [](const std::string &data) {\n\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(dat, \"scene_uuid\"));\n\t\tOBSBasic::Get()->SetCurrentScene(source.Get(), true);\n\t\tobs_scene_load_transform_states(data.c_str());\n\t};\n\n\tstd::string redo_data(obs_data_get_json(wrapper));\n\tif (undo_data.compare(redo_data) != 0)\n\t\tmain->undo_s.add_action(\n\t\t\tQTStr(\"Undo.Transform\").arg(obs_source_get_name(obs_scene_get_source(main->GetCurrentScene()))),\n\t\t\tundo_redo, undo_redo, undo_data, redo_data);\n}\n\nvoid OBSBasicTransform::setScene(OBSScene scene)\n{\n\tsigs.clear();\n\n\tif (scene) {\n\t\tOBSSource source = obs_scene_get_source(scene);\n\t\tsignal_handler_t *signal = obs_source_get_signal_handler(source);\n\n\t\tsigs.emplace_back(signal, \"item_transform\", OBSSceneItemTransform, this);\n\t\tsigs.emplace_back(signal, \"item_remove\", OBSSceneItemRemoved, this);\n\t\tsigs.emplace_back(signal, \"item_select\", OBSSceneItemSelect, this);\n\t\tsigs.emplace_back(signal, \"item_deselect\", OBSSceneItemDeselect, this);\n\t\tsigs.emplace_back(signal, \"item_locked\", OBSSceneItemLocked, this);\n\t}\n}\n\nvoid OBSBasicTransform::setItem(OBSSceneItem newItem)\n{\n\tQMetaObject::invokeMethod(this, \"setItemQt\", Q_ARG(OBSSceneItem, OBSSceneItem(newItem)));\n}\n\nvoid OBSBasicTransform::setEnabled(bool enable)\n{\n\tui->transformSettings->setEnabled(enable);\n\tui->boundsSettings->setEnabled(enable);\n\tui->cropSettings->setEnabled(enable);\n\tui->buttonBox->button(QDialogButtonBox::Reset)->setEnabled(enable);\n}\n\nvoid OBSBasicTransform::setItemQt(OBSSceneItem newItem)\n{\n\titem = newItem;\n\tif (item)\n\t\trefreshControls();\n\n\tbool enable = !!item && !obs_sceneitem_locked(item);\n\tsetEnabled(enable);\n}\n\nvoid OBSBasicTransform::OBSSceneItemTransform(void *param, calldata_t *data)\n{\n\tOBSBasicTransform *window = static_cast<OBSBasicTransform *>(param);\n\tOBSSceneItem item = (obs_sceneitem_t *)calldata_ptr(data, \"item\");\n\n\tif (item == window->item && !window->ignoreTransformSignal)\n\t\tQMetaObject::invokeMethod(window, \"refreshControls\");\n}\n\nvoid OBSBasicTransform::OBSSceneItemRemoved(void *param, calldata_t *data)\n{\n\tOBSBasicTransform *window = static_cast<OBSBasicTransform *>(param);\n\tobs_scene_t *scene = (obs_scene_t *)calldata_ptr(data, \"scene\");\n\tobs_sceneitem_t *item = (obs_sceneitem_t *)calldata_ptr(data, \"item\");\n\n\tif (item == window->item)\n\t\twindow->setItem(FindASelectedItem(scene));\n}\n\nvoid OBSBasicTransform::OBSSceneItemSelect(void *param, calldata_t *data)\n{\n\tOBSBasicTransform *window = static_cast<OBSBasicTransform *>(param);\n\tOBSSceneItem item = (obs_sceneitem_t *)calldata_ptr(data, \"item\");\n\n\tif (item != window->item)\n\t\twindow->setItem(item);\n}\n\nvoid OBSBasicTransform::OBSSceneItemDeselect(void *param, calldata_t *data)\n{\n\tOBSBasicTransform *window = static_cast<OBSBasicTransform *>(param);\n\tobs_scene_t *scene = (obs_scene_t *)calldata_ptr(data, \"scene\");\n\tobs_sceneitem_t *item = (obs_sceneitem_t *)calldata_ptr(data, \"item\");\n\n\tif (item == window->item) {\n\t\twindow->setWindowTitle(QTStr(\"Basic.TransformWindow.NoSelectedSource\"));\n\t\twindow->setItem(FindASelectedItem(scene));\n\t}\n}\n\nvoid OBSBasicTransform::OBSSceneItemLocked(void *param, calldata_t *data)\n{\n\tOBSBasicTransform *window = static_cast<OBSBasicTransform *>(param);\n\tbool locked = calldata_bool(data, \"locked\");\n\n\tQMetaObject::invokeMethod(window, \"setEnabled\", Q_ARG(bool, !locked));\n}\n\nstatic const uint32_t indexToAlign[] = {OBS_ALIGN_TOP | OBS_ALIGN_LEFT,\n\t\t\t\t\tOBS_ALIGN_TOP,\n\t\t\t\t\tOBS_ALIGN_TOP | OBS_ALIGN_RIGHT,\n\t\t\t\t\tOBS_ALIGN_LEFT,\n\t\t\t\t\tOBS_ALIGN_CENTER,\n\t\t\t\t\tOBS_ALIGN_RIGHT,\n\t\t\t\t\tOBS_ALIGN_BOTTOM | OBS_ALIGN_LEFT,\n\t\t\t\t\tOBS_ALIGN_BOTTOM,\n\t\t\t\t\tOBS_ALIGN_BOTTOM | OBS_ALIGN_RIGHT};\n\nstatic int alignToIndex(uint32_t align)\n{\n\tint index = 0;\n\tfor (uint32_t curAlign : indexToAlign) {\n\t\tif (curAlign == align)\n\t\t\treturn index;\n\n\t\tindex++;\n\t}\n\n\treturn 0;\n}\n\nvoid OBSBasicTransform::refreshControls()\n{\n\tif (!item)\n\t\treturn;\n\n\tobs_transform_info oti;\n\tobs_sceneitem_crop crop;\n\tobs_sceneitem_get_info2(item, &oti);\n\tobs_sceneitem_get_crop(item, &crop);\n\n\tobs_source_t *source = obs_sceneitem_get_source(item);\n\tuint32_t source_cx = obs_source_get_width(source);\n\tuint32_t source_cy = obs_source_get_height(source);\n\tfloat width = float(source_cx);\n\tfloat height = float(source_cy);\n\n\tint alignIndex = alignToIndex(oti.alignment);\n\tint boundsAlignIndex = alignToIndex(oti.bounds_alignment);\n\n\tignoreItemChange = true;\n\tui->positionX->setValue(oti.pos.x);\n\tui->positionY->setValue(oti.pos.y);\n\tui->rotation->setValue(oti.rot);\n\tui->sizeX->setValue(oti.scale.x * width);\n\tui->sizeY->setValue(oti.scale.y * height);\n\tpositionAlignment->setCurrentIndex(alignIndex);\n\n\tbool valid_size = source_cx != 0 && source_cy != 0;\n\tui->sizeX->setEnabled(valid_size);\n\tui->sizeY->setEnabled(valid_size);\n\n\tui->boundsType->setCurrentIndex(int(oti.bounds_type));\n\tboundsAlignment->setCurrentIndex(boundsAlignIndex);\n\tui->boundsWidth->setValue(oti.bounds.x);\n\tui->boundsHeight->setValue(oti.bounds.y);\n\tui->cropToBounds->setChecked(oti.crop_to_bounds);\n\n\tui->cropLeft->setValue(int(crop.left));\n\tui->cropRight->setValue(int(crop.right));\n\tui->cropTop->setValue(int(crop.top));\n\tui->cropBottom->setValue(int(crop.bottom));\n\tignoreItemChange = false;\n\n\tstd::string name = obs_source_get_name(source);\n\tsetWindowTitle(QTStr(\"Basic.TransformWindow.Title\").arg(name.c_str()));\n}\n\nvoid OBSBasicTransform::onAlignChanged(int index)\n{\n\tuint32_t alignment = indexToAlign[index];\n\n\tvec2 flipRatio = getAlignmentConversion(alignment);\n\n\tobs_transform_info oti;\n\tobs_sceneitem_crop crop;\n\tobs_sceneitem_get_info2(item, &oti);\n\tobs_sceneitem_get_crop(item, &crop);\n\n\tobs_source_t *source = obs_sceneitem_get_source(item);\n\tuint32_t sourceWidth = obs_source_get_width(source);\n\tuint32_t sourceHeight = obs_source_get_height(source);\n\n\tuint32_t widthForFlip = sourceWidth - crop.left - crop.right;\n\tuint32_t heightForFlip = sourceHeight - crop.top - crop.bottom;\n\n\tif (oti.bounds_type != OBS_BOUNDS_NONE) {\n\t\twidthForFlip = oti.bounds.x;\n\t\theightForFlip = oti.bounds.y;\n\t}\n\n\tvec2 currentRatio = getAlignmentConversion(oti.alignment);\n\n\tfloat shiftX = (currentRatio.x - flipRatio.x) * widthForFlip * oti.scale.x;\n\tfloat shiftY = (currentRatio.y - flipRatio.y) * heightForFlip * oti.scale.y;\n\n\tbool previousIgnoreState = ignoreItemChange;\n\n\tignoreItemChange = true;\n\tui->positionX->setValue(oti.pos.x - shiftX);\n\tui->positionY->setValue(oti.pos.y - shiftY);\n\tignoreItemChange = previousIgnoreState;\n\n\tonControlChanged();\n}\n\nvoid OBSBasicTransform::onBoundsType(int index)\n{\n\tif (index == -1)\n\t\treturn;\n\n\tobs_bounds_type type = (obs_bounds_type)index;\n\tbool enable = (type != OBS_BOUNDS_NONE);\n\n\tboundsAlignment->setEnabled(enable && type != OBS_BOUNDS_STRETCH);\n\tui->boundsWidth->setEnabled(enable);\n\tui->boundsHeight->setEnabled(enable);\n\n\tbool isCoverBounds = type == OBS_BOUNDS_SCALE_OUTER || type == OBS_BOUNDS_SCALE_TO_WIDTH ||\n\t\t\t     type == OBS_BOUNDS_SCALE_TO_HEIGHT;\n\tui->cropToBounds->setEnabled(isCoverBounds);\n\n\tif (!ignoreItemChange) {\n\t\tobs_bounds_type lastType = obs_sceneitem_get_bounds_type(item);\n\t\tif (lastType == OBS_BOUNDS_NONE) {\n\t\t\tOBSSource source = obs_sceneitem_get_source(item);\n\t\t\tint width = (int)obs_source_get_width(source);\n\t\t\tint height = (int)obs_source_get_height(source);\n\n\t\t\tvec2 scale;\n\t\t\tobs_sceneitem_get_scale(item, &scale);\n\n\t\t\tobs_sceneitem_crop crop;\n\t\t\tobs_sceneitem_get_crop(item, &crop);\n\n\t\t\tui->sizeX->setValue(width);\n\t\t\tui->sizeY->setValue(height);\n\n\t\t\tui->boundsWidth->setValue((width - crop.left - crop.right) * scale.x);\n\t\t\tui->boundsHeight->setValue((height - crop.top - crop.bottom) * scale.y);\n\t\t} else if (type == OBS_BOUNDS_NONE) {\n\t\t\tOBSSource source = obs_sceneitem_get_source(item);\n\t\t\tint width = (int)obs_source_get_width(source);\n\t\t\tint height = (int)obs_source_get_height(source);\n\n\t\t\tmatrix4 draw;\n\t\t\tobs_sceneitem_get_draw_transform(item, &draw);\n\n\t\t\tui->sizeX->setValue(width * draw.x.x);\n\t\t\tui->sizeY->setValue(height * draw.y.y);\n\n\t\t\tobs_transform_info oti;\n\t\t\tobs_sceneitem_get_info2(item, &oti);\n\n\t\t\t// We use the draw transform values here which is always a top left coordinate origin.\n\t\t\tvec2 currentRatio = getAlignmentConversion(OBS_ALIGN_TOP | OBS_ALIGN_LEFT);\n\t\t\tvec2 flipRatio = getAlignmentConversion(oti.alignment);\n\n\t\t\tfloat drawX = draw.t.x;\n\t\t\tfloat drawY = draw.t.y;\n\n\t\t\tobs_sceneitem_crop crop;\n\t\t\tobs_sceneitem_get_crop(item, &crop);\n\n\t\t\tuint32_t widthForFlip = width - crop.left - crop.right;\n\t\t\tuint32_t heightForFlip = height - crop.top - crop.bottom;\n\n\t\t\tfloat shiftX = (currentRatio.x - flipRatio.x) * (widthForFlip * draw.x.x);\n\t\t\tfloat shiftY = (currentRatio.y - flipRatio.y) * (heightForFlip * draw.y.y);\n\n\t\t\tui->positionX->setValue(oti.pos.x - (oti.pos.x - drawX) - shiftX);\n\t\t\tui->positionY->setValue(oti.pos.y - (oti.pos.y - drawY) - shiftY);\n\t\t}\n\t}\n\n\tonControlChanged();\n}\n\nvoid OBSBasicTransform::onControlChanged()\n{\n\tif (ignoreItemChange)\n\t\treturn;\n\n\tobs_source_t *source = obs_sceneitem_get_source(item);\n\tuint32_t source_cx = obs_source_get_width(source);\n\tuint32_t source_cy = obs_source_get_height(source);\n\tdouble width = double(source_cx);\n\tdouble height = double(source_cy);\n\n\tobs_transform_info oti;\n\tobs_sceneitem_get_info2(item, &oti);\n\n\t/* do not scale a source if it has 0 width/height */\n\tif (source_cx != 0 && source_cy != 0) {\n\t\toti.scale.x = float(ui->sizeX->value() / width);\n\t\toti.scale.y = float(ui->sizeY->value() / height);\n\t}\n\n\toti.pos.x = float(ui->positionX->value());\n\toti.pos.y = float(ui->positionY->value());\n\toti.rot = float(ui->rotation->value());\n\toti.alignment = indexToAlign[positionAlignment->currentIndex()];\n\n\toti.bounds_type = (obs_bounds_type)ui->boundsType->currentIndex();\n\toti.bounds_alignment = indexToAlign[boundsAlignment->currentIndex()];\n\toti.bounds.x = float(ui->boundsWidth->value());\n\toti.bounds.y = float(ui->boundsHeight->value());\n\toti.crop_to_bounds = ui->cropToBounds->isChecked();\n\n\tignoreTransformSignal = true;\n\tobs_sceneitem_set_info2(item, &oti);\n\tignoreTransformSignal = false;\n}\n\nvoid OBSBasicTransform::onCropChanged()\n{\n\tif (ignoreItemChange)\n\t\treturn;\n\n\tobs_sceneitem_crop crop;\n\tcrop.left = uint32_t(ui->cropLeft->value());\n\tcrop.right = uint32_t(ui->cropRight->value());\n\tcrop.top = uint32_t(ui->cropTop->value());\n\tcrop.bottom = uint32_t(ui->cropBottom->value());\n\n\tignoreTransformSignal = true;\n\tobs_sceneitem_set_crop(item, &crop);\n\tignoreTransformSignal = false;\n}\n\nvoid OBSBasicTransform::onSceneChanged(QListWidgetItem *current, QListWidgetItem *)\n{\n\tif (!current)\n\t\treturn;\n\n\tOBSScene scene = GetOBSRef<OBSScene>(current);\n\tthis->setScene(scene);\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicTransform.hpp",
    "content": "#pragma once\n\n#include \"ui_OBSBasicTransform.h\"\n\n#include <obs.hpp>\n#include <components/AlignmentSelector.hpp>\n\n#include <QDialog>\n#include <QPointer>\n\nclass OBSBasic;\nclass QListWidgetItem;\n\nclass OBSBasicTransform : public QDialog {\n\tQ_OBJECT\n\nprivate:\n\tstd::unique_ptr<Ui::OBSBasicTransform> ui;\n\n\tQPointer<AlignmentSelector> positionAlignment;\n\tQPointer<AlignmentSelector> boundsAlignment;\n\n\tOBSBasic *main;\n\tOBSSceneItem item;\n\tstd::vector<OBSSignal> sigs;\n\n\tstd::string undo_data;\n\n\tbool ignoreTransformSignal = false;\n\tbool ignoreItemChange = false;\n\n\ttemplate<typename Widget, typename WidgetParent, typename... SignalArgs, typename... SlotArgs>\n\tvoid hookWidget(Widget *widget, void (WidgetParent::*signal)(SignalArgs...),\n\t\t\tvoid (OBSBasicTransform::*slot)(SlotArgs...))\n\t{\n\t\tQObject::connect(widget, signal, this, slot);\n\t}\n\n\tvoid setScene(OBSScene scene);\n\tvoid setItem(OBSSceneItem newItem);\n\n\tstatic void OBSSceneItemTransform(void *param, calldata_t *data);\n\tstatic void OBSSceneItemRemoved(void *param, calldata_t *data);\n\tstatic void OBSSceneItemSelect(void *param, calldata_t *data);\n\tstatic void OBSSceneItemDeselect(void *param, calldata_t *data);\n\tstatic void OBSSceneItemLocked(void *param, calldata_t *data);\n\nprivate slots:\n\tvoid refreshControls();\n\tvoid setItemQt(OBSSceneItem newItem);\n\tvoid onAlignChanged(int index);\n\tvoid onBoundsType(int index);\n\tvoid onControlChanged();\n\tvoid onCropChanged();\n\tvoid setEnabled(bool enable);\n\npublic:\n\tOBSBasicTransform(OBSSceneItem item, OBSBasic *parent);\n\t~OBSBasicTransform();\n\npublic slots:\n\tvoid onSceneChanged(QListWidgetItem *current, QListWidgetItem *prev);\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicVCamConfig.cpp",
    "content": "#include \"OBSBasicVCamConfig.hpp\"\n\n#include <OBSApp.hpp>\n\n#include \"moc_OBSBasicVCamConfig.cpp\"\n\nOBSBasicVCamConfig::OBSBasicVCamConfig(const VCamConfig &_config, bool _vcamActive, QWidget *parent)\n\t: config(_config),\n\t  vcamActive(_vcamActive),\n\t  activeType(_config.type),\n\t  QDialog(parent),\n\t  ui(new Ui::OBSBasicVCamConfig)\n{\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\n\tui->outputType->addItem(QTStr(\"Basic.VCam.OutputType.Program\"), (int)VCamOutputType::ProgramView);\n\tui->outputType->addItem(QTStr(\"StudioMode.Preview\"), (int)VCamOutputType::PreviewOutput);\n\tui->outputType->addItem(QTStr(\"Basic.Scene\"), (int)VCamOutputType::SceneOutput);\n\tui->outputType->addItem(QTStr(\"Basic.Main.Source\"), (int)VCamOutputType::SourceOutput);\n\n\tui->outputType->setCurrentIndex(ui->outputType->findData((int)config.type));\n\tOutputTypeChanged();\n\tconnect(ui->outputType, &QComboBox::currentIndexChanged, this, &OBSBasicVCamConfig::OutputTypeChanged);\n\n\tconnect(ui->buttonBox, &QDialogButtonBox::accepted, this, &OBSBasicVCamConfig::UpdateConfig);\n}\n\nvoid OBSBasicVCamConfig::OutputTypeChanged()\n{\n\tVCamOutputType type = (VCamOutputType)ui->outputType->currentData().toInt();\n\tui->outputSelection->setDisabled(false);\n\n\tauto list = ui->outputSelection;\n\tlist->clear();\n\n\tswitch (type) {\n\tcase VCamOutputType::Invalid:\n\tcase VCamOutputType::ProgramView:\n\tcase VCamOutputType::PreviewOutput:\n\t\tui->outputSelection->setDisabled(true);\n\t\tlist->addItem(QTStr(\"Basic.VCam.OutputSelection.NoSelection\"));\n\t\tbreak;\n\tcase VCamOutputType::SceneOutput: {\n\t\t// Scenes in default order\n\t\tBPtr<char *> scenes = obs_frontend_get_scene_names();\n\t\tfor (char **temp = scenes; *temp; temp++) {\n\t\t\tlist->addItem(*temp);\n\n\t\t\tif (config.scene.compare(*temp) == 0)\n\t\t\t\tlist->setCurrentIndex(list->count() - 1);\n\t\t}\n\t\tbreak;\n\t}\n\tcase VCamOutputType::SourceOutput: {\n\t\t// Sources in alphabetical order\n\t\tstd::vector<std::string> sources;\n\t\tauto AddSource = [&](obs_source_t *source) {\n\t\t\tauto name = obs_source_get_name(source);\n\n\t\t\tif (!(obs_source_get_output_flags(source) & OBS_SOURCE_VIDEO))\n\t\t\t\treturn;\n\n\t\t\tsources.push_back(name);\n\t\t};\n\t\tusing AddSource_t = decltype(AddSource);\n\n\t\tobs_enum_sources(\n\t\t\t[](void *data, obs_source_t *source) {\n\t\t\t\tauto &AddSource = *static_cast<AddSource_t *>(data);\n\t\t\t\tif (!obs_source_removed(source))\n\t\t\t\t\tAddSource(source);\n\t\t\t\treturn true;\n\t\t\t},\n\t\t\tstatic_cast<void *>(&AddSource));\n\n\t\t// Sort and select current item\n\t\tsort(sources.begin(), sources.end());\n\t\tfor (auto &&source : sources) {\n\t\t\tlist->addItem(source.c_str());\n\n\t\t\tif (config.source == source)\n\t\t\t\tlist->setCurrentIndex(list->count() - 1);\n\t\t}\n\t\tbreak;\n\t}\n\t}\n\n\tif (!vcamActive)\n\t\treturn;\n\n\trequireRestart = (activeType == VCamOutputType::ProgramView && type != VCamOutputType::ProgramView) ||\n\t\t\t (activeType != VCamOutputType::ProgramView && type == VCamOutputType::ProgramView);\n\n\tui->warningLabel->setVisible(requireRestart);\n}\n\nvoid OBSBasicVCamConfig::UpdateConfig()\n{\n\tVCamOutputType type = (VCamOutputType)ui->outputType->currentData().toInt();\n\tswitch (type) {\n\tcase VCamOutputType::ProgramView:\n\tcase VCamOutputType::PreviewOutput:\n\t\tbreak;\n\tcase VCamOutputType::SceneOutput:\n\t\tconfig.scene = ui->outputSelection->currentText().toStdString();\n\t\tbreak;\n\tcase VCamOutputType::SourceOutput:\n\t\tconfig.source = ui->outputSelection->currentText().toStdString();\n\t\tbreak;\n\tdefault:\n\t\t// unknown value, don't save type\n\t\treturn;\n\t}\n\n\tconfig.type = type;\n\n\tif (requireRestart) {\n\t\temit AcceptedAndRestart(config);\n\t} else {\n\t\temit Accepted(config);\n\t}\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSBasicVCamConfig.hpp",
    "content": "#pragma once\n\n#include \"ui_OBSBasicVCamConfig.h\"\n\n#include <utility/VCamConfig.hpp>\n\n#include <QDialog>\n\nclass OBSBasicVCamConfig : public QDialog {\n\tQ_OBJECT\n\n\tVCamConfig config;\n\n\tbool vcamActive;\n\tVCamOutputType activeType;\n\tbool requireRestart;\n\npublic:\n\texplicit OBSBasicVCamConfig(const VCamConfig &config, bool VCamActive, QWidget *parent = 0);\n\nprivate slots:\n\tvoid OutputTypeChanged();\n\tvoid UpdateConfig();\n\nprivate:\n\tstd::unique_ptr<Ui::OBSBasicVCamConfig> ui;\n\nsignals:\n\tvoid Accepted(const VCamConfig &config);\n\tvoid AcceptedAndRestart(const VCamConfig &config);\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSExtraBrowsers.cpp",
    "content": "#include \"OBSExtraBrowsers.hpp\"\n#include \"ui_OBSExtraBrowsers.h\"\n\n#include <utility/ExtraBrowsersDelegate.hpp>\n\n#include \"moc_OBSExtraBrowsers.cpp\"\n\nOBSExtraBrowsers::OBSExtraBrowsers(QWidget *parent) : QDialog(parent), ui(new Ui::OBSExtraBrowsers)\n{\n\tui->setupUi(this);\n\n\tsetAttribute(Qt::WA_DeleteOnClose, true);\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tmodel = new ExtraBrowsersModel(ui->table);\n\n\tui->table->setModel(model);\n\tui->table->setItemDelegateForColumn((int)Column::Title, new ExtraBrowsersDelegate(model));\n\tui->table->setItemDelegateForColumn((int)Column::Url, new ExtraBrowsersDelegate(model));\n\tui->table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Stretch);\n\tui->table->horizontalHeader()->setSectionResizeMode((int)Column::Delete, QHeaderView::ResizeMode::Fixed);\n\tui->table->setEditTriggers(QAbstractItemView::EditTrigger::CurrentChanged);\n}\n\nOBSExtraBrowsers::~OBSExtraBrowsers() {}\n\nvoid OBSExtraBrowsers::closeEvent(QCloseEvent *event)\n{\n\tQDialog::closeEvent(event);\n\tmodel->Apply();\n}\n\nvoid OBSExtraBrowsers::on_apply_clicked()\n{\n\tmodel->Apply();\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSExtraBrowsers.hpp",
    "content": "#pragma once\n\n#include <utility/ExtraBrowsersModel.hpp>\n\n#include <QDialog>\n\nclass Ui_OBSExtraBrowsers;\n\nclass OBSExtraBrowsers : public QDialog {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui_OBSExtraBrowsers> ui;\n\tExtraBrowsersModel *model;\n\npublic:\n\tOBSExtraBrowsers(QWidget *parent);\n\t~OBSExtraBrowsers();\n\n\tvoid closeEvent(QCloseEvent *event) override;\n\npublic slots:\n\tvoid on_apply_clicked();\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSIdianPlayground.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSIdianPlayground.hpp\"\n\n#include <Idian/Idian.hpp>\n\n#include <QTimer>\n\n#include \"moc_OBSIdianPlayground.cpp\"\n\nusing namespace idian;\n\nOBSIdianPlayground::OBSIdianPlayground(QWidget *parent) : QDialog(parent), ui(new Ui_OBSIdianPlayground)\n{\n\tui->setupUi(this);\n\n\tsetSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);\n\n\tGroup *test;\n\tRow *tmp;\n\n\tComboBox *cbox = new ComboBox;\n\tcbox->addItem(\"Test 1\");\n\tcbox->addItem(\"Test 2\");\n\n\t// Group box 1\n\ttest = new Group(this);\n\n\ttmp = new Row();\n\ttmp->setTitle(\"Row with a dropdown\");\n\ttmp->setSuffix(cbox);\n\ttest->properties()->addRow(tmp);\n\n\tcbox = new ComboBox;\n\tcbox->addItem(\"Test 3\");\n\tcbox->addItem(\"Test 4\");\n\ttmp = new Row();\n\ttmp->setTitle(\"Row with a dropdown\");\n\ttmp->setDescription(\"And a subtitle!\");\n\ttmp->setSuffix(cbox);\n\ttest->properties()->addRow(tmp);\n\n\ttmp = new Row();\n\ttmp->setTitle(\"Toggle Switch\");\n\ttmp->setSuffix(new ToggleSwitch());\n\ttest->properties()->addRow(tmp);\n\tui->scrollAreaWidgetContents->layout()->addWidget(test);\n\n\ttmp = new Row();\n\ttmp->setTitle(\"Delayed toggle switch\");\n\ttmp->setDescription(\"The state can be set separately\");\n\tauto tswitch = new ToggleSwitch;\n\ttswitch->setDelayed(true);\n\tconnect(tswitch, &ToggleSwitch::pendingChecked, this, [=]() {\n\t\t// Do async enable stuff, then set toggle status when complete\n\t\tQTimer::singleShot(1000, [=]() { tswitch->setStatus(true); });\n\t});\n\tconnect(tswitch, &ToggleSwitch::pendingUnchecked, this, [=]() {\n\t\t// Do async disable stuff, then set toggle status when complete\n\t\tQTimer::singleShot(1000, [=]() { tswitch->setStatus(false); });\n\t});\n\ttmp->setSuffix(tswitch);\n\ttest->properties()->addRow(tmp);\n\n\t// Group box 2\n\ttest = new Group();\n\ttest->setTitle(\"Just a few checkboxes\");\n\n\ttmp = new Row();\n\ttmp->setTitle(\"Box 1\");\n\ttmp->setPrefix(new CheckBox);\n\ttest->properties()->addRow(tmp);\n\n\ttmp = new Row();\n\ttmp->setTitle(\"Box 2\");\n\ttmp->setPrefix(new CheckBox);\n\ttest->properties()->addRow(tmp);\n\n\tui->scrollAreaWidgetContents->layout()->addWidget(test);\n\n\t// Group box 2\n\ttest = new Group();\n\ttest->setTitle(\"Another Group\");\n\ttest->setDescription(\"With a subtitle\");\n\n\ttmp = new Row();\n\ttmp->setTitle(\"Placeholder\");\n\ttmp->setSuffix(new ToggleSwitch);\n\ttest->properties()->addRow(tmp);\n\n\tCollapsibleRow *tmp2 = new CollapsibleRow(this);\n\ttmp2->setTitle(\"A Collapsible row!\");\n\ttmp2->setCheckable(true);\n\ttest->addRow(tmp2);\n\n\ttmp = new Row();\n\ttmp->setTitle(\"Spin box demo\");\n\ttmp->setSuffix(new DoubleSpinBox());\n\ttmp2->addRow(tmp);\n\n\ttmp = new Row();\n\ttmp->setTitle(\"Just another placeholder\");\n\ttmp->setSuffix(new ToggleSwitch(true));\n\ttmp2->addRow(tmp);\n\n\ttmp = new Row();\n\ttmp->setTitle(\"Placeholder 2\");\n\ttmp->setSuffix(new ToggleSwitch);\n\ttest->properties()->addRow(tmp);\n\n\tui->scrollAreaWidgetContents->setContentsMargins(0, 0, 0, 0);\n\tui->scrollAreaWidgetContents->layout()->setContentsMargins(0, 0, 0, 0);\n\tui->scrollAreaWidgetContents->layout()->addWidget(test);\n\tui->scrollAreaWidgetContents->layout()->setAlignment(Qt::AlignTop | Qt::AlignHCenter);\n\n\t// Test Checkable Group\n\tGroup *test2 = new Group();\n\ttest2->setTitle(\"Checkable Group\");\n\ttest2->setDescription(\"Description goes here\");\n\ttest2->setCheckable(true);\n\tui->scrollAreaWidgetContents->layout()->addWidget(test2);\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSIdianPlayground.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QDialog>\n\n#include <memory>\n\n#include <ui_OBSIdianPlayground.h>\n\n// QDialog including a bunch of custom widgets for demoing\nclass OBSIdianPlayground : public QDialog {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui_OBSIdianPlayground> ui;\n\npublic:\n\tOBSIdianPlayground(QWidget *parent);\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSLogViewer.cpp",
    "content": "#include \"OBSLogViewer.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QDesktopServices>\n#include <QFile>\n#include <QScrollBar>\n\n#include \"moc_OBSLogViewer.cpp\"\n\nOBSLogViewer::OBSLogViewer(QWidget *parent) : QDialog(parent), ui(new Ui::OBSLogViewer)\n{\n\tsetWindowFlags(windowFlags() & Qt::WindowMaximizeButtonHint & ~Qt::WindowContextHelpButtonHint);\n\tsetAttribute(Qt::WA_DeleteOnClose);\n\n\tui->setupUi(this);\n\n\tbool showLogViewerOnStartup = config_get_bool(App()->GetUserConfig(), \"LogViewer\", \"ShowLogStartup\");\n\n\tui->showStartup->setChecked(showLogViewerOnStartup);\n\n\tconst char *geom = config_get_string(App()->GetUserConfig(), \"LogViewer\", \"geometry\");\n\n\tif (geom != nullptr) {\n\t\tQByteArray ba = QByteArray::fromBase64(QByteArray(geom));\n\t\trestoreGeometry(ba);\n\t}\n\n\tInitLog();\n\n\tconnect(App(), &OBSApp::logLineAdded, this, &OBSLogViewer::AddLine);\n}\n\nOBSLogViewer::~OBSLogViewer()\n{\n\tconfig_set_string(App()->GetUserConfig(), \"LogViewer\", \"geometry\", saveGeometry().toBase64().constData());\n}\n\nvoid OBSLogViewer::on_showStartup_clicked(bool checked)\n{\n\tconfig_set_bool(App()->GetUserConfig(), \"LogViewer\", \"ShowLogStartup\", checked);\n}\n\nvoid OBSLogViewer::InitLog()\n{\n\tchar logDir[512];\n\tstd::string path;\n\n\tif (GetAppConfigPath(logDir, sizeof(logDir), \"obs-studio/logs\")) {\n\t\tpath += logDir;\n\t\tpath += \"/\";\n\t\tpath += App()->GetCurrentLog();\n\t}\n\n\tQFile file(QT_UTF8(path.c_str()));\n\n\tif (file.open(QIODevice::ReadOnly)) {\n\t\tQTextStream in(&file);\n\n\t\tQTextDocument *doc = ui->textArea->document();\n\t\tQTextCursor cursor(doc);\n\t\tcursor.movePosition(QTextCursor::End);\n\t\tcursor.beginEditBlock();\n\t\twhile (!in.atEnd()) {\n\t\t\tQString line = in.readLine();\n\t\t\tcursor.insertText(line);\n\t\t\tcursor.insertBlock();\n\t\t}\n\t\tcursor.endEditBlock();\n\n\t\tfile.close();\n\t}\n\tQScrollBar *scroll = ui->textArea->verticalScrollBar();\n\tscroll->setValue(scroll->maximum());\n}\n\nvoid OBSLogViewer::AddLine(int type, const QString &str)\n{\n\tQString msg = str.toHtmlEscaped();\n\n\tswitch (type) {\n\tcase LOG_WARNING:\n\t\tmsg = QString(\"<font color=\\\"#c08000\\\">%1</font>\").arg(msg);\n\t\tbreak;\n\tcase LOG_ERROR:\n\t\tmsg = QString(\"<font color=\\\"#c00000\\\">%1</font>\").arg(msg);\n\t\tbreak;\n\tdefault:\n\t\tmsg = QString(\"<font>%1</font>\").arg(msg);\n\t\tbreak;\n\t}\n\n\tQScrollBar *scroll = ui->textArea->verticalScrollBar();\n\tbool bottomScrolled = scroll->value() >= scroll->maximum() - 10;\n\n\tif (bottomScrolled)\n\t\tscroll->setValue(scroll->maximum());\n\n\tQTextDocument *doc = ui->textArea->document();\n\tQTextCursor cursor(doc);\n\tcursor.movePosition(QTextCursor::End);\n\tcursor.beginEditBlock();\n\tcursor.insertHtml(msg);\n\tcursor.insertBlock();\n\tcursor.endEditBlock();\n\n\tif (bottomScrolled)\n\t\tscroll->setValue(scroll->maximum());\n}\n\nvoid OBSLogViewer::on_openButton_clicked()\n{\n\tchar logDir[512];\n\tif (GetAppConfigPath(logDir, sizeof(logDir), \"obs-studio/logs\") <= 0)\n\t\treturn;\n\n\tconst char *log = App()->GetCurrentLog();\n\n\tstd::string path = logDir;\n\tpath += \"/\";\n\tpath += log;\n\n\tQUrl url = QUrl::fromLocalFile(QT_UTF8(path.c_str()));\n\tQDesktopServices::openUrl(url);\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSLogViewer.hpp",
    "content": "#pragma once\n\n#include \"ui_OBSLogViewer.h\"\n\n#include <QDialog>\n\nclass OBSLogViewer : public QDialog {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui::OBSLogViewer> ui;\n\n\tvoid InitLog();\n\nprivate slots:\n\tvoid AddLine(int type, const QString &text);\n\tvoid on_openButton_clicked();\n\tvoid on_showStartup_clicked(bool checked);\n\npublic:\n\tOBSLogViewer(QWidget *parent = 0);\n\t~OBSLogViewer();\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSMissingFiles.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSMissingFiles.hpp\"\n\n#include <OBSApp.hpp>\n#include <utility/MissingFilesModel.hpp>\n#include <utility/MissingFilesPathItemDelegate.hpp>\n\n#include <QFileDialog>\n\n#include \"moc_OBSMissingFiles.cpp\"\n\nOBSMissingFiles::OBSMissingFiles(obs_missing_files_t *files, QWidget *parent)\n\t: QDialog(parent),\n\t  filesModel(new MissingFilesModel),\n\t  ui(new Ui::OBSMissingFiles)\n{\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\n\tui->tableView->setModel(filesModel);\n\tui->tableView->setItemDelegateForColumn(MissingFilesColumn::NewPath, new MissingFilesPathItemDelegate());\n\tui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);\n\tui->tableView->horizontalHeader()->setMaximumSectionSize(width() / 4);\n\tui->tableView->horizontalHeader()->setSectionResizeMode(MissingFilesColumn::NewPath,\n\t\t\t\t\t\t\t\tQHeaderView::ResizeMode::Stretch);\n\tui->tableView->setEditTriggers(QAbstractItemView::EditTrigger::CurrentChanged);\n\tui->tableView->setWordWrap(false);\n\n\tui->warningIcon->setPixmap(filesModel->warningIcon.pixmap(QSize(32, 32)));\n\n\tfor (size_t i = 0; i < obs_missing_files_count(files); i++) {\n\t\tobs_missing_file_t *f = obs_missing_files_get_file(files, (int)i);\n\n\t\tconst char *oldPath = obs_missing_file_get_path(f);\n\t\tconst char *name = obs_missing_file_get_source_name(f);\n\n\t\taddMissingFile(oldPath, name);\n\t}\n\n\tQString found = QTStr(\"MissingFiles.NumFound\").arg(\"0\", QString::number(obs_missing_files_count(files)));\n\n\tui->found->setText(found);\n\n\tfileStore = files;\n\n\tconnect(ui->doneButton, &QPushButton::clicked, this, &OBSMissingFiles::saveFiles);\n\tconnect(ui->browseButton, &QPushButton::clicked, this, &OBSMissingFiles::browseFolders);\n\tconnect(ui->cancelButton, &QPushButton::clicked, this, &OBSMissingFiles::close);\n\tconnect(filesModel, &MissingFilesModel::dataChanged, this, &OBSMissingFiles::dataChanged);\n\n\tQModelIndex index = filesModel->createIndex(0, 1);\n\tQMetaObject::invokeMethod(ui->tableView, \"setCurrentIndex\", Qt::QueuedConnection,\n\t\t\t\t  Q_ARG(const QModelIndex &, index));\n}\n\nOBSMissingFiles::~OBSMissingFiles()\n{\n\tobs_missing_files_destroy(fileStore);\n}\n\nvoid OBSMissingFiles::addMissingFile(const char *originalPath, const char *sourceName)\n{\n\tQStringList list;\n\n\tlist.append(originalPath);\n\tlist.append(sourceName);\n\n\tQModelIndex insertIndex = filesModel->index(filesModel->rowCount() - 1, MissingFilesColumn::Source);\n\n\tfilesModel->setData(insertIndex, list, MissingFilesRole::NewPathsToProcessRole);\n}\n\nvoid OBSMissingFiles::saveFiles()\n{\n\tfor (int i = 0; i < filesModel->files.length(); i++) {\n\t\tMissingFilesState state = filesModel->files[i].state;\n\t\tif (state != MissingFilesState::Missing) {\n\t\t\tobs_missing_file_t *f = obs_missing_files_get_file(fileStore, i);\n\n\t\t\tQString path = filesModel->files[i].newPath;\n\n\t\t\tif (state == MissingFilesState::Cleared) {\n\t\t\t\tobs_missing_file_issue_callback(f, \"\");\n\t\t\t} else {\n\t\t\t\tchar *p = bstrdup(path.toStdString().c_str());\n\t\t\t\tobs_missing_file_issue_callback(f, p);\n\t\t\t\tbfree(p);\n\t\t\t}\n\t\t}\n\t}\n\n\tQDialog::accept();\n}\n\nvoid OBSMissingFiles::browseFolders()\n{\n\tQString dir = QFileDialog::getExistingDirectory(this, QTStr(\"MissingFiles.SelectDir\"), \"\",\n\t\t\t\t\t\t\tQFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);\n\n\tif (dir != \"\") {\n\t\tdir += \"/\";\n\t\tfilesModel->findAllFilesInPath(dir, true);\n\t}\n}\n\nvoid OBSMissingFiles::dataChanged()\n{\n\tQString found =\n\t\tQTStr(\"MissingFiles.NumFound\")\n\t\t\t.arg(QString::number(filesModel->found()), QString::number(obs_missing_files_count(fileStore)));\n\n\tui->found->setText(found);\n\n\tui->tableView->resizeColumnToContents(MissingFilesColumn::State);\n\tui->tableView->resizeColumnToContents(MissingFilesColumn::Source);\n}\n\nQIcon OBSMissingFiles::GetWarningIcon()\n{\n\treturn filesModel->warningIcon;\n}\n\nvoid OBSMissingFiles::SetWarningIcon(const QIcon &icon)\n{\n\tui->warningIcon->setPixmap(icon.pixmap(QSize(32, 32)));\n\tfilesModel->warningIcon = icon;\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSMissingFiles.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_OBSMissingFiles.h\"\n\n#include <obs.hpp>\n\n#include <QDialog>\n#include <QPointer>\n\nclass MissingFilesModel;\n\nclass OBSMissingFiles : public QDialog {\n\tQ_OBJECT\n\tQ_PROPERTY(QIcon warningIcon READ GetWarningIcon WRITE SetWarningIcon DESIGNABLE true)\n\n\tQPointer<MissingFilesModel> filesModel;\n\tstd::unique_ptr<Ui::OBSMissingFiles> ui;\n\npublic:\n\texplicit OBSMissingFiles(obs_missing_files_t *files, QWidget *parent = nullptr);\n\tvirtual ~OBSMissingFiles() override;\n\n\tvoid addMissingFile(const char *originalPath, const char *sourceName);\n\n\tQIcon GetWarningIcon();\n\tvoid SetWarningIcon(const QIcon &icon);\n\nprivate:\n\tvoid saveFiles();\n\tvoid browseFolders();\n\n\tobs_missing_files_t *fileStore;\n\npublic slots:\n\tvoid dataChanged();\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSPermissions.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSPermissions.hpp\"\n\n#include <OBSApp.hpp>\n\n#include \"moc_OBSPermissions.cpp\"\n\nOBSPermissions::OBSPermissions(QWidget *parent, MacPermissionStatus capture, MacPermissionStatus video,\n\t\t\t       MacPermissionStatus audio, MacPermissionStatus inputMonitoring)\n\t: QDialog(parent),\n\t  ui(new Ui::OBSPermissions)\n{\n\tui->setupUi(this);\n\tSetStatus(ui->capturePermissionButton, capture, QTStr(\"MacPermissions.Item.ScreenRecording\"));\n\tSetStatus(ui->videoPermissionButton, video, QTStr(\"MacPermissions.Item.Camera\"));\n\tSetStatus(ui->audioPermissionButton, audio, QTStr(\"MacPermissions.Item.Microphone\"));\n\tSetStatus(ui->inputMonitoringPermissionButton, inputMonitoring, QTStr(\"MacPermissions.Item.InputMonitoring\"));\n\n\tui->accessibilityPermissionButton->setText(\n\t\tQTStr(\"MacPermissions.OpenPreferences\").arg(QTStr(\"MacPermissions.Item.Accessibility\")));\n\tui->accessibilityPermissionButton->setVisible(inputMonitoring != kPermissionAuthorized);\n\tui->accessibilityPermissionLabel->setVisible(inputMonitoring != kPermissionAuthorized);\n}\n\nvoid OBSPermissions::SetStatus(QPushButton *btn, MacPermissionStatus status, const QString &preference)\n{\n\tif (status == kPermissionAuthorized) {\n\t\tbtn->setText(QTStr(\"MacPermissions.AccessGranted\"));\n\t} else if (status == kPermissionNotDetermined) {\n\t\tbtn->setText(QTStr(\"MacPermissions.RequestAccess\"));\n\t} else {\n\t\tbtn->setText(QTStr(\"MacPermissions.OpenPreferences\").arg(preference));\n\t}\n\tbtn->setEnabled(status != kPermissionAuthorized);\n\tbtn->setProperty(\"status\", status);\n}\n\nvoid OBSPermissions::on_capturePermissionButton_clicked()\n{\n\tOpenMacOSPrivacyPreferences(\"ScreenCapture\");\n\tRequestPermission(kScreenCapture);\n}\n\nvoid OBSPermissions::on_videoPermissionButton_clicked()\n{\n\tMacPermissionStatus status = (MacPermissionStatus)ui->videoPermissionButton->property(\"status\").toInt();\n\tif (status == kPermissionNotDetermined) {\n\t\tstatus = RequestPermission(kVideoDeviceAccess);\n\t\tSetStatus(ui->videoPermissionButton, status, QTStr(\"MacPermissions.Item.Camera\"));\n\t} else {\n\t\tOpenMacOSPrivacyPreferences(\"Camera\");\n\t}\n}\n\nvoid OBSPermissions::on_audioPermissionButton_clicked()\n{\n\tMacPermissionStatus status = (MacPermissionStatus)ui->audioPermissionButton->property(\"status\").toInt();\n\tif (status == kPermissionNotDetermined) {\n\t\tstatus = RequestPermission(kAudioDeviceAccess);\n\t\tSetStatus(ui->audioPermissionButton, status, QTStr(\"MacPermissions.Item.Microphone\"));\n\t} else {\n\t\tOpenMacOSPrivacyPreferences(\"Microphone\");\n\t}\n}\n\nvoid OBSPermissions::on_inputMonitoringPermissionButton_clicked()\n{\n\tOpenMacOSPrivacyPreferences(\"ListenEvent\");\n\tRequestPermission(kInputMonitoring);\n}\n\nvoid OBSPermissions::on_accessibilityPermissionButton_clicked()\n{\n\tOpenMacOSPrivacyPreferences(\"Accessibility\");\n}\n\nvoid OBSPermissions::on_continueButton_clicked()\n{\n\tconfig_set_int(App()->GetAppConfig(), \"General\", \"MacOSPermissionsDialogLastShown\",\n\t\t       MACOS_PERMISSIONS_DIALOG_VERSION);\n\tclose();\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSPermissions.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_OBSPermissions.h\"\n\n#include <utility/platform.hpp>\n\n#include <QDialog>\n\n#define MACOS_PERMISSIONS_DIALOG_VERSION 1\n\nclass OBSPermissions : public QDialog {\n\tQ_OBJECT\n\nprivate:\n\tstd::unique_ptr<Ui::OBSPermissions> ui;\n\tvoid SetStatus(QPushButton *btn, MacPermissionStatus status, const QString &preference);\n\npublic:\n\tOBSPermissions(QWidget *parent, MacPermissionStatus capture, MacPermissionStatus video,\n\t\t       MacPermissionStatus audio, MacPermissionStatus accessibility);\n\nprivate slots:\n\tvoid on_capturePermissionButton_clicked();\n\tvoid on_videoPermissionButton_clicked();\n\tvoid on_audioPermissionButton_clicked();\n\tvoid on_inputMonitoringPermissionButton_clicked();\n\tvoid on_accessibilityPermissionButton_clicked();\n\tvoid on_continueButton_clicked();\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSRemux.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSRemux.hpp\"\n\n#include <utility/RemuxEntryPathItemDelegate.hpp>\n#include <utility/RemuxQueueModel.hpp>\n#include <utility/RemuxWorker.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QDirIterator>\n#include <QDropEvent>\n#include <QMimeData>\n#include <QPushButton>\n\n#include \"moc_OBSRemux.cpp\"\n\nOBSRemux::OBSRemux(const char *path, QWidget *parent, bool autoRemux_)\n\t: QDialog(parent),\n\t  queueModel(new RemuxQueueModel),\n\t  worker(new RemuxWorker()),\n\t  ui(new Ui::OBSRemux),\n\t  recPath(path),\n\t  autoRemux(autoRemux_)\n{\n\tsetAcceptDrops(true);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\n\tui->progressBar->setVisible(false);\n\tui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);\n\tui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false);\n\n\tif (autoRemux) {\n\t\tresize(280, 40);\n\t\tui->tableView->hide();\n\t\tui->buttonBox->hide();\n\t\tui->label->hide();\n\t}\n\n\tui->progressBar->setMinimum(0);\n\tui->progressBar->setMaximum(1000);\n\tui->progressBar->setValue(0);\n\n\tui->tableView->setModel(queueModel);\n\tui->tableView->setItemDelegateForColumn(RemuxEntryColumn::InputPath,\n\t\t\t\t\t\tnew RemuxEntryPathItemDelegate(false, recPath));\n\tui->tableView->setItemDelegateForColumn(RemuxEntryColumn::OutputPath,\n\t\t\t\t\t\tnew RemuxEntryPathItemDelegate(true, recPath));\n\tui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Stretch);\n\tui->tableView->horizontalHeader()->setSectionResizeMode(RemuxEntryColumn::State,\n\t\t\t\t\t\t\t\tQHeaderView::ResizeMode::Fixed);\n\tui->tableView->setEditTriggers(QAbstractItemView::EditTrigger::CurrentChanged);\n\tui->tableView->setTextElideMode(Qt::ElideMiddle);\n\tui->tableView->setWordWrap(false);\n\n\tinstallEventFilter(CreateShortcutFilter());\n\n\tui->buttonBox->button(QDialogButtonBox::Ok)->setText(QTStr(\"Remux.Remux\"));\n\tui->buttonBox->button(QDialogButtonBox::Reset)->setText(QTStr(\"Remux.ClearFinished\"));\n\tui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(QTStr(\"Remux.ClearAll\"));\n\tui->buttonBox->button(QDialogButtonBox::Reset)->setDisabled(true);\n\n\tconnect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &OBSRemux::beginRemux);\n\tconnect(ui->buttonBox->button(QDialogButtonBox::Reset), &QPushButton::clicked, this, &OBSRemux::clearFinished);\n\tconnect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this,\n\t\t&OBSRemux::clearAll);\n\tconnect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &OBSRemux::close);\n\n\tworker->moveToThread(&remuxer);\n\tremuxer.start();\n\n\tconnect(worker.data(), &RemuxWorker::updateProgress, this, &OBSRemux::updateProgress);\n\tconnect(&remuxer, &QThread::finished, worker.data(), &QObject::deleteLater);\n\tconnect(worker.data(), &RemuxWorker::remuxFinished, this, &OBSRemux::remuxFinished);\n\tconnect(this, &OBSRemux::remux, worker.data(), &RemuxWorker::remux);\n\n\tconnect(queueModel.data(), &RemuxQueueModel::rowsInserted, this, &OBSRemux::rowCountChanged);\n\tconnect(queueModel.data(), &RemuxQueueModel::rowsRemoved, this, &OBSRemux::rowCountChanged);\n\n\tQModelIndex index = queueModel->createIndex(0, 1);\n\tQMetaObject::invokeMethod(ui->tableView, \"setCurrentIndex\", Qt::QueuedConnection,\n\t\t\t\t  Q_ARG(const QModelIndex &, index));\n}\n\nbool OBSRemux::stopRemux()\n{\n\tif (!worker->isWorking)\n\t\treturn true;\n\n\t// By locking the worker thread's mutex, we ensure that its\n\t// update poll will be blocked as long as we're in here with\n\t// the popup open.\n\tQMutexLocker lock(&worker->updateMutex);\n\n\tbool exit = false;\n\n\tif (QMessageBox::critical(nullptr, QTStr(\"Remux.ExitUnfinishedTitle\"), QTStr(\"Remux.ExitUnfinished\"),\n\t\t\t\t  QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {\n\t\texit = true;\n\t}\n\n\tif (exit) {\n\t\t// Inform the worker it should no longer be\n\t\t// working. It will interrupt accordingly in\n\t\t// its next update callback.\n\t\tworker->isWorking = false;\n\t}\n\n\treturn exit;\n}\n\nOBSRemux::~OBSRemux()\n{\n\tstopRemux();\n\tremuxer.quit();\n\tremuxer.wait();\n}\n\nvoid OBSRemux::rowCountChanged(const QModelIndex &, int, int)\n{\n\t// See if there are still any rows ready to remux. Change\n\t// the state of the \"go\" button accordingly.\n\t// There must be more than one row, since there will always be\n\t// at least one row for the empty insertion point.\n\tif (queueModel->rowCount() > 1) {\n\t\tui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);\n\t\tui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true);\n\t\tui->buttonBox->button(QDialogButtonBox::Reset)->setEnabled(queueModel->canClearFinished());\n\t} else {\n\t\tui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);\n\t\tui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(false);\n\t\tui->buttonBox->button(QDialogButtonBox::Reset)->setEnabled(false);\n\t}\n}\n\nvoid OBSRemux::dropEvent(QDropEvent *ev)\n{\n\tQStringList urlList;\n\n\tfor (QUrl url : ev->mimeData()->urls()) {\n\t\tQFileInfo fileInfo(url.toLocalFile());\n\n\t\tif (fileInfo.isDir()) {\n\t\t\tQStringList directoryFilter;\n\t\t\tdirectoryFilter << \"*.flv\"\n\t\t\t\t\t<< \"*.mp4\"\n\t\t\t\t\t<< \"*.mov\"\n\t\t\t\t\t<< \"*.mkv\"\n\t\t\t\t\t<< \"*.ts\"\n\t\t\t\t\t<< \"*.m3u8\";\n\n\t\t\tQDirIterator dirIter(fileInfo.absoluteFilePath(), directoryFilter, QDir::Files,\n\t\t\t\t\t     QDirIterator::Subdirectories);\n\n\t\t\twhile (dirIter.hasNext()) {\n\t\t\t\turlList.append(dirIter.next());\n\t\t\t}\n\t\t} else {\n\t\t\turlList.append(fileInfo.canonicalFilePath());\n\t\t}\n\t}\n\n\tif (urlList.empty()) {\n\t\tQMessageBox::information(nullptr, QTStr(\"Remux.NoFilesAddedTitle\"), QTStr(\"Remux.NoFilesAdded\"),\n\t\t\t\t\t QMessageBox::Ok);\n\t} else if (!autoRemux) {\n\t\tQModelIndex insertIndex = queueModel->index(queueModel->rowCount() - 1, RemuxEntryColumn::InputPath);\n\t\tqueueModel->setData(insertIndex, urlList, RemuxEntryRole::NewPathsToProcessRole);\n\t}\n}\n\nvoid OBSRemux::dragEnterEvent(QDragEnterEvent *ev)\n{\n\tif (ev->mimeData()->hasUrls() && !worker->isWorking)\n\t\tev->accept();\n}\n\nvoid OBSRemux::beginRemux()\n{\n\tif (worker->isWorking) {\n\t\tstopRemux();\n\t\treturn;\n\t}\n\n\tbool proceedWithRemux = true;\n\tQFileInfoList overwriteFiles = queueModel->checkForOverwrites();\n\n\tif (!overwriteFiles.empty()) {\n\t\tQString message = QTStr(\"Remux.FileExists\");\n\t\tmessage += \"\\n\\n\";\n\n\t\tfor (QFileInfo fileInfo : overwriteFiles)\n\t\t\tmessage += fileInfo.canonicalFilePath() + \"\\n\";\n\n\t\tif (OBSMessageBox::question(this, QTStr(\"Remux.FileExistsTitle\"), message) != QMessageBox::Yes)\n\t\t\tproceedWithRemux = false;\n\t}\n\n\tif (!proceedWithRemux)\n\t\treturn;\n\n\t// Set all jobs to \"pending\" first.\n\tqueueModel->beginProcessing();\n\n\tui->progressBar->setVisible(true);\n\tui->buttonBox->button(QDialogButtonBox::Ok)->setText(QTStr(\"Remux.Stop\"));\n\tsetAcceptDrops(false);\n\n\tremuxNextEntry();\n}\n\nvoid OBSRemux::AutoRemux(QString inFile, QString outFile)\n{\n\tif (inFile != \"\" && outFile != \"\" && autoRemux) {\n\t\tui->progressBar->setVisible(true);\n\t\temit remux(inFile, outFile);\n\t\tautoRemuxFile = outFile;\n\t}\n}\n\nvoid OBSRemux::remuxNextEntry()\n{\n\tworker->lastProgress = 0.f;\n\n\tQString inputPath, outputPath;\n\tif (queueModel->beginNextEntry(inputPath, outputPath)) {\n\t\temit remux(inputPath, outputPath);\n\t} else {\n\t\tqueueModel->autoRemux = autoRemux;\n\t\tqueueModel->endProcessing();\n\n\t\tif (!autoRemux) {\n\t\t\tOBSMessageBox::information(this, QTStr(\"Remux.FinishedTitle\"),\n\t\t\t\t\t\t   queueModel->checkForErrors() ? QTStr(\"Remux.FinishedError\")\n\t\t\t\t\t\t\t\t\t\t: QTStr(\"Remux.Finished\"));\n\t\t}\n\n\t\tui->progressBar->setVisible(autoRemux);\n\t\tui->buttonBox->button(QDialogButtonBox::Ok)->setText(QTStr(\"Remux.Remux\"));\n\t\tui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setEnabled(true);\n\t\tui->buttonBox->button(QDialogButtonBox::Reset)->setEnabled(queueModel->canClearFinished());\n\t\tsetAcceptDrops(true);\n\t}\n}\n\nvoid OBSRemux::closeEvent(QCloseEvent *event)\n{\n\tif (!stopRemux())\n\t\tevent->ignore();\n\telse\n\t\tQDialog::closeEvent(event);\n}\n\nvoid OBSRemux::reject()\n{\n\tif (!stopRemux())\n\t\treturn;\n\n\tQDialog::reject();\n}\n\nvoid OBSRemux::updateProgress(float percent)\n{\n\tui->progressBar->setValue(percent * 10);\n}\n\nvoid OBSRemux::remuxFinished(bool success)\n{\n\tui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);\n\n\tqueueModel->finishEntry(success);\n\n\tif (autoRemux && autoRemuxFile != \"\") {\n\t\tQTimer::singleShot(3000, this, &OBSRemux::close);\n\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tmain->ShowStatusBarMessage(QTStr(\"Basic.StatusBar.AutoRemuxedTo\").arg(autoRemuxFile));\n\t}\n\n\tremuxNextEntry();\n}\n\nvoid OBSRemux::clearFinished()\n{\n\tqueueModel->clearFinished();\n}\n\nvoid OBSRemux::clearAll()\n{\n\tqueueModel->clearAll();\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSRemux.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_OBSRemux.h\"\n\n#include <QPointer>\n#include <QThread>\n\nclass RemuxQueueModel;\nclass RemuxWorker;\n\nclass OBSRemux : public QDialog {\n\tQ_OBJECT\n\n\tQPointer<RemuxQueueModel> queueModel;\n\tQThread remuxer;\n\tQPointer<RemuxWorker> worker;\n\n\tstd::unique_ptr<Ui::OBSRemux> ui;\n\n\tconst char *recPath;\n\n\tvirtual void closeEvent(QCloseEvent *event) override;\n\tvirtual void reject() override;\n\n\tbool autoRemux;\n\tQString autoRemuxFile;\n\npublic:\n\texplicit OBSRemux(const char *recPath, QWidget *parent = nullptr, bool autoRemux = false);\n\tvirtual ~OBSRemux() override;\n\n\tusing job_t = std::shared_ptr<struct media_remux_job>;\n\n\tvoid AutoRemux(QString inFile, QString outFile);\n\nprotected:\n\tvirtual void dropEvent(QDropEvent *ev) override;\n\tvirtual void dragEnterEvent(QDragEnterEvent *ev) override;\n\n\tvoid remuxNextEntry();\n\nprivate slots:\n\tvoid rowCountChanged(const QModelIndex &parent, int first, int last);\n\npublic slots:\n\tvoid updateProgress(float percent);\n\tvoid remuxFinished(bool success);\n\tvoid beginRemux();\n\tbool stopRemux();\n\tvoid clearFinished();\n\tvoid clearAll();\n\nsignals:\n\tvoid remux(const QString &source, const QString &target);\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSUpdate.cpp",
    "content": "#include \"OBSUpdate.hpp\"\n\n#include <OBSApp.hpp>\n\n#include \"ui_OBSUpdate.h\"\n\nOBSUpdate::OBSUpdate(QWidget *parent, bool manualUpdate, const QString &text)\n\t: QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint),\n\t  ui(new Ui_OBSUpdate)\n{\n\tui->setupUi(this);\n\tui->text->setHtml(text);\n\n\tif (manualUpdate) {\n\t\tdelete ui->skip;\n\t\tui->skip = nullptr;\n\n\t\tui->no->setText(QTStr(\"Cancel\"));\n\t}\n}\n\nvoid OBSUpdate::on_yes_clicked()\n{\n\tdone(OBSUpdate::Yes);\n}\n\nvoid OBSUpdate::on_no_clicked()\n{\n\tdone(OBSUpdate::No);\n}\n\nvoid OBSUpdate::on_skip_clicked()\n{\n\tdone(OBSUpdate::Skip);\n}\n\nvoid OBSUpdate::accept()\n{\n\tdone(OBSUpdate::Yes);\n}\n\nvoid OBSUpdate::reject()\n{\n\tdone(OBSUpdate::No);\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSUpdate.hpp",
    "content": "#pragma once\n\n#include <QDialog>\n\nclass Ui_OBSUpdate;\n\nclass OBSUpdate : public QDialog {\n\tQ_OBJECT\n\npublic:\n\tenum ReturnVal { No, Yes, Skip };\n\n\tOBSUpdate(QWidget *parent, bool manualUpdate, const QString &text);\n\npublic slots:\n\tvoid on_yes_clicked();\n\tvoid on_no_clicked();\n\tvoid on_skip_clicked();\n\tvirtual void accept() override;\n\tvirtual void reject() override;\n\nprivate:\n\tstd::unique_ptr<Ui_OBSUpdate> ui;\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSWhatsNew.cpp",
    "content": "#include \"OBSWhatsNew.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#ifdef BROWSER_AVAILABLE\n#include <browser-panel.hpp>\nextern QCef *cef;\n#endif\n\n#include <QHBoxLayout>\n#include <QPushButton>\n#include <QVBoxLayout>\n\n#include \"moc_OBSWhatsNew.cpp\"\n\nOBSWhatsNew::OBSWhatsNew(QWidget *parent, const std::string &url) : QDialog(parent)\n{\n#ifdef BROWSER_AVAILABLE\n\tif (!cef) {\n\t\treturn;\n\t}\n\n\tsetWindowTitle(\"What's New\");\n\tsetAttribute(Qt::WA_DeleteOnClose, true);\n\tresize(700, 600);\n\n\tQt::WindowFlags flags = windowFlags();\n\tQt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint;\n\tsetWindowFlags(flags & (~helpFlag));\n\n\tOBSBasic::InitBrowserPanelSafeBlock();\n\n\tcefWidget = cef->create_widget(nullptr, url);\n\tif (!cefWidget) {\n\t\treturn;\n\t}\n\n\tconnect(cefWidget, &QCefWidget::titleChanged, this, &OBSWhatsNew::setWindowTitle);\n\n\tQPushButton *close = new QPushButton(QTStr(\"Close\"));\n\tconnect(close, &QAbstractButton::clicked, this, &QDialog::accept);\n\n\tQHBoxLayout *bottomLayout = new QHBoxLayout();\n\tbottomLayout->addStretch();\n\tbottomLayout->addWidget(close);\n\tbottomLayout->addStretch();\n\n\tQVBoxLayout *topLayout = new QVBoxLayout(this);\n\ttopLayout->addWidget(cefWidget);\n\ttopLayout->addLayout(bottomLayout);\n\n\tshow();\n#else\n\tUNUSED_PARAMETER(url);\n#endif\n}\n\nOBSWhatsNew::~OBSWhatsNew() {}\n\nvoid OBSWhatsNew::reject()\n{\n#ifdef BROWSER_AVAILABLE\n\tdelete cefWidget;\n#endif\n\tQDialog::reject();\n}\n\nvoid OBSWhatsNew::accept()\n{\n#ifdef BROWSER_AVAILABLE\n\tdelete cefWidget;\n#endif\n\tQDialog::accept();\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSWhatsNew.hpp",
    "content": "#pragma once\n\n#include <QPointer>\n#include <QDialog>\n\n#include <string>\n\nclass QCefWidget;\n\nclass OBSWhatsNew : public QDialog {\n\tQ_OBJECT\n\n\tQCefWidget *cefWidget = nullptr;\n\npublic:\n\tOBSWhatsNew(QWidget *parent, const std::string &url);\n\t~OBSWhatsNew();\n\n\tvirtual void reject() override;\n\tvirtual void accept() override;\n};\n"
  },
  {
    "path": "frontend/dialogs/OBSYoutubeActions.cpp",
    "content": "#include \"OBSYoutubeActions.hpp\"\n\n#include <docks/YouTubeAppDock.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QDesktopServices>\n#include <QFileInfo>\n#include <QImageReader>\n#include <QStandardPaths>\n#include <QToolTip>\n\n#include \"moc_OBSYoutubeActions.cpp\"\n\nconst QString SchedulDateAndTimeFormat = \"yyyy-MM-dd'T'hh:mm:ss'Z'\";\nconst QString RepresentSchedulDateAndTimeFormat = \"dddd, MMMM d, yyyy h:m\";\nconst QString IndexOfGamingCategory = \"20\";\n\nOBSYoutubeActions::OBSYoutubeActions(QWidget *parent, Auth *auth, bool broadcastReady)\n\t: QDialog(parent),\n\t  ui(new Ui::OBSYoutubeActions),\n\t  apiYouTube(dynamic_cast<YoutubeApiWrappers *>(auth)),\n\t  workerThread(new WorkerThread(apiYouTube)),\n\t  broadcastReady(broadcastReady)\n{\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\tui->setupUi(this);\n\n\tui->privacyBox->addItem(QTStr(\"YouTube.Actions.Privacy.Public\"), \"public\");\n\tui->privacyBox->addItem(QTStr(\"YouTube.Actions.Privacy.Unlisted\"), \"unlisted\");\n\tui->privacyBox->addItem(QTStr(\"YouTube.Actions.Privacy.Private\"), \"private\");\n\n\tui->latencyBox->addItem(QTStr(\"YouTube.Actions.Latency.Normal\"), \"normal\");\n\tui->latencyBox->addItem(QTStr(\"YouTube.Actions.Latency.Low\"), \"low\");\n\tui->latencyBox->addItem(QTStr(\"YouTube.Actions.Latency.UltraLow\"), \"ultraLow\");\n\n\tUpdateOkButtonStatus();\n\n\tconnect(ui->title, &QLineEdit::textChanged, this, [&](const QString &) { this->UpdateOkButtonStatus(); });\n\tconnect(ui->privacyBox, &QComboBox::currentTextChanged, this,\n\t\t[&](const QString &) { this->UpdateOkButtonStatus(); });\n\tconnect(ui->yesMakeForKids, &QRadioButton::toggled, this, [&](bool) { this->UpdateOkButtonStatus(); });\n\tconnect(ui->notMakeForKids, &QRadioButton::toggled, this, [&](bool) { this->UpdateOkButtonStatus(); });\n\tconnect(ui->tabWidget, &QTabWidget::currentChanged, this, [&](int) { this->UpdateOkButtonStatus(); });\n\tconnect(ui->pushButton, &QPushButton::clicked, this, &OBSYoutubeActions::OpenYouTubeDashboard);\n\n\tconnect(ui->helpAutoStartStop, &QLabel::linkActivated, this,\n\t\t[](const QString &) { QToolTip::showText(QCursor::pos(), QTStr(\"YouTube.Actions.AutoStartStop.TT\")); });\n\tconnect(ui->help360Video, &QLabel::linkActivated, this,\n\t\t[](const QString &link) { QDesktopServices::openUrl(QUrl(link)); });\n\tconnect(ui->helpMadeForKids, &QLabel::linkActivated, this,\n\t\t[](const QString &link) { QDesktopServices::openUrl(QUrl(link)); });\n\n\tui->scheduledTime->setVisible(false);\n#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)\n\tconnect(ui->checkScheduledLater, &QCheckBox::checkStateChanged, this,\n\t\t[&](Qt::CheckState state)\n#else\n\tconnect(ui->checkScheduledLater, &QCheckBox::stateChanged, this,\n\t\t[&](int state)\n#endif\n\t\t{\n\t\t\tconst bool checked = (state == Qt::Checked);\n\t\t\tui->scheduledTime->setVisible(checked);\n\t\t\tif (checked) {\n\t\t\t\tui->checkAutoStart->setVisible(true);\n\t\t\t\tui->checkAutoStop->setVisible(true);\n\t\t\t\tui->helpAutoStartStop->setVisible(true);\n\n\t\t\t\tui->checkAutoStart->setChecked(false);\n\t\t\t\tui->checkAutoStop->setChecked(false);\n\t\t\t} else {\n\t\t\t\tui->checkAutoStart->setVisible(false);\n\t\t\t\tui->checkAutoStop->setVisible(false);\n\t\t\t\tui->helpAutoStartStop->setVisible(false);\n\n\t\t\t\tui->checkAutoStart->setChecked(true);\n\t\t\t\tui->checkAutoStop->setChecked(true);\n\t\t\t}\n\t\t\tUpdateOkButtonStatus();\n\t\t});\n\n\tui->checkAutoStart->setVisible(false);\n\tui->checkAutoStop->setVisible(false);\n\tui->helpAutoStartStop->setVisible(false);\n\n\tui->scheduledTime->setDateTime(QDateTime::currentDateTime());\n\n\tauto thumbSelectionHandler = [&]() {\n\t\tif (thumbnailFile.isEmpty()) {\n\t\t\tQString filePath = OpenFile(this, QTStr(\"YouTube.Actions.Thumbnail.SelectFile\"),\n\t\t\t\t\t\t    QStandardPaths::writableLocation(QStandardPaths::PicturesLocation),\n\t\t\t\t\t\t    QString(\"Images (*.png *.jpg *.jpeg *.gif)\"));\n\n\t\t\tif (!filePath.isEmpty()) {\n\t\t\t\tQFileInfo tFile(filePath);\n\t\t\t\tif (!tFile.exists()) {\n\t\t\t\t\treturn ShowErrorDialog(this, QTStr(\"YouTube.Actions.Error.FileMissing\"));\n\t\t\t\t} else if (tFile.size() > 2 * 1024 * 1024) {\n\t\t\t\t\treturn ShowErrorDialog(this, QTStr(\"YouTube.Actions.Error.FileTooLarge\"));\n\t\t\t\t}\n\n\t\t\t\tthumbnailFile = filePath;\n\t\t\t\tui->selectedFileName->setText(thumbnailFile);\n\t\t\t\tui->selectFileButton->setText(QTStr(\"YouTube.Actions.Thumbnail.ClearFile\"));\n\n\t\t\t\tQImageReader imgReader(filePath);\n\t\t\t\timgReader.setAutoTransform(true);\n\t\t\t\tconst QImage newImage = imgReader.read();\n\t\t\t\tui->thumbnailPreview->setPixmap(QPixmap::fromImage(newImage).scaled(\n\t\t\t\t\t160, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation));\n\t\t\t}\n\t\t} else {\n\t\t\tthumbnailFile.clear();\n\t\t\tui->selectedFileName->setText(QTStr(\"YouTube.Actions.Thumbnail.NoFileSelected\"));\n\t\t\tui->selectFileButton->setText(QTStr(\"YouTube.Actions.Thumbnail.SelectFile\"));\n\t\t\tui->thumbnailPreview->setPixmap(GetPlaceholder().pixmap(QSize(16, 16)));\n\t\t}\n\t};\n\n\tconnect(ui->selectFileButton, &QPushButton::clicked, this, thumbSelectionHandler);\n\tconnect(ui->thumbnailPreview, &ClickableLabel::clicked, this, thumbSelectionHandler);\n\n\tif (!apiYouTube) {\n\t\tblog(LOG_DEBUG, \"YouTube API auth NOT found.\");\n\t\tCancel();\n\t\treturn;\n\t}\n\n\tconst char *name = config_get_string(OBSBasic::Get()->Config(), \"YouTube\", \"ChannelName\");\n\tthis->setWindowTitle(QTStr(\"YouTube.Actions.WindowTitle\").arg(name));\n\n\tQVector<CategoryDescription> category_list;\n\tif (!apiYouTube->GetVideoCategoriesList(category_list)) {\n\t\tShowErrorDialog(parent, apiYouTube->GetLastError().isEmpty()\n\t\t\t\t\t\t? QTStr(\"YouTube.Actions.Error.General\")\n\t\t\t\t\t\t: QTStr(\"YouTube.Actions.Error.Text\").arg(apiYouTube->GetLastError()));\n\t\tCancel();\n\t\treturn;\n\t}\n\tfor (auto &category : category_list) {\n\t\tui->categoryBox->addItem(category.title, category.id);\n\t\tif (category.id == IndexOfGamingCategory) {\n\t\t\tui->categoryBox->setCurrentText(category.title);\n\t\t}\n\t}\n\n\tconnect(ui->okButton, &QPushButton::clicked, this, &OBSYoutubeActions::InitBroadcast);\n\tconnect(ui->saveButton, &QPushButton::clicked, this, &OBSYoutubeActions::ReadyBroadcast);\n\tconnect(ui->cancelButton, &QPushButton::clicked, this, [&]() {\n\t\tblog(LOG_DEBUG, \"YouTube live broadcast creation cancelled.\");\n\t\t// Close the dialog.\n\t\tCancel();\n\t});\n\n\tqDeleteAll(ui->scrollAreaWidgetContents->findChildren<QWidget *>(QString(), Qt::FindDirectChildrenOnly));\n\n\t// Add label indicating loading state\n\tQLabel *loadingLabel = new QLabel();\n\tloadingLabel->setTextFormat(Qt::RichText);\n\tloadingLabel->setAlignment(Qt::AlignHCenter);\n\tloadingLabel->setText(QString(\"<big>%1</big>\").arg(QTStr(\"YouTube.Actions.EventsLoading\")));\n\tui->scrollAreaWidgetContents->layout()->addWidget(loadingLabel);\n\n\t// Delete \"loading...\" label on completion\n\tconnect(workerThread, &WorkerThread::finished, this, [&] {\n\t\tQLayoutItem *item = ui->scrollAreaWidgetContents->layout()->takeAt(0);\n\t\titem->widget()->deleteLater();\n\t});\n\n\tconnect(workerThread, &WorkerThread::failed, this, [&]() {\n\t\tauto last_error = apiYouTube->GetLastError();\n\t\tif (last_error.isEmpty())\n\t\t\tlast_error = QTStr(\"YouTube.Actions.Error.YouTubeApi\");\n\n\t\tif (!apiYouTube->GetTranslatedError(last_error))\n\t\t\tlast_error = QTStr(\"YouTube.Actions.Error.Text\").arg(last_error);\n\n\t\tShowErrorDialog(this, last_error);\n\t\tQDialog::reject();\n\t});\n\n\tconnect(workerThread, &WorkerThread::new_item, this,\n\t\t[&](const QString &title, const QString &dateTimeString, const QString &broadcast,\n\t\t    const QString &status, bool astart, bool astop) {\n\t\t\tClickableLabel *label = new ClickableLabel();\n\t\t\tlabel->setTextFormat(Qt::RichText);\n\n\t\t\tif (status == \"live\" || status == \"testing\") {\n\t\t\t\t// Resumable stream\n\t\t\t\tlabel->setText(QString(\"<big>%1</big><br/>%2\")\n\t\t\t\t\t\t       .arg(title, QTStr(\"YouTube.Actions.Stream.Resume\")));\n\n\t\t\t} else if (dateTimeString.isEmpty()) {\n\t\t\t\t// The broadcast created by YouTube Studio has no start time.\n\t\t\t\t// Yes this does violate the restrictions set in YouTube's API\n\t\t\t\t// But why would YouTube care about consistency?\n\t\t\t\tlabel->setText(QString(\"<big>%1</big><br/>%2\")\n\t\t\t\t\t\t       .arg(title, QTStr(\"YouTube.Actions.Stream.YTStudio\")));\n\t\t\t} else {\n\t\t\t\tlabel->setText(\n\t\t\t\t\tQString(\"<big>%1</big><br/>%2\")\n\t\t\t\t\t\t.arg(title,\n\t\t\t\t\t\t     QTStr(\"YouTube.Actions.Stream.ScheduledFor\").arg(dateTimeString)));\n\t\t\t}\n\n\t\t\tlabel->setAlignment(Qt::AlignHCenter);\n\t\t\tlabel->setMargin(4);\n\n\t\t\tconnect(label, &ClickableLabel::clicked, this, [&, label, broadcast, astart, astop]() {\n\t\t\t\tfor (QWidget *i : ui->scrollAreaWidgetContents->findChildren<QWidget *>(\n\t\t\t\t\t     QString(), Qt::FindDirectChildrenOnly)) {\n\n\t\t\t\t\ti->setProperty(\"class\", \"\");\n\t\t\t\t\ti->style()->unpolish(i);\n\t\t\t\t\ti->style()->polish(i);\n\t\t\t\t}\n\t\t\t\tlabel->setProperty(\"class\", \"row-selected\");\n\t\t\t\tlabel->style()->unpolish(label);\n\t\t\t\tlabel->style()->polish(label);\n\n\t\t\t\tthis->selectedBroadcast = broadcast;\n\t\t\t\tthis->autostart = astart;\n\t\t\t\tthis->autostop = astop;\n\t\t\t\tUpdateOkButtonStatus();\n\t\t\t});\n\t\t\tui->scrollAreaWidgetContents->layout()->addWidget(label);\n\n\t\t\tif (selectedBroadcast == broadcast)\n\t\t\t\tlabel->clicked();\n\t\t});\n\tworkerThread->start();\n\n\tOBSBasic *main = OBSBasic::Get();\n\tbool rememberSettings = config_get_bool(main->activeConfiguration, \"YouTube\", \"RememberSettings\");\n\tif (rememberSettings)\n\t\tLoadSettings();\n\n\t// Switch to events page and select readied broadcast once loaded\n\tif (broadcastReady) {\n\t\tui->tabWidget->setCurrentIndex(1);\n\t\tselectedBroadcast = apiYouTube->GetBroadcastId();\n\t}\n\n#ifdef __APPLE__\n\t// MacOS theming issues\n\tthis->resize(this->width() + 200, this->height() + 120);\n#endif\n\tvalid = true;\n}\n\nvoid OBSYoutubeActions::showEvent(QShowEvent *event)\n{\n\tQDialog::showEvent(event);\n\tif (thumbnailFile.isEmpty())\n\t\tui->thumbnailPreview->setPixmap(GetPlaceholder().pixmap(QSize(16, 16)));\n}\n\nOBSYoutubeActions::~OBSYoutubeActions()\n{\n\tworkerThread->stop();\n\tworkerThread->wait();\n\n\tdelete workerThread;\n}\n\nvoid WorkerThread::run()\n{\n\tif (!pending)\n\t\treturn;\n\tjson11::Json broadcasts;\n\n\tfor (QString broadcastStatus : {\"active\", \"upcoming\"}) {\n\t\tif (!apiYouTube->GetBroadcastsList(broadcasts, \"\", broadcastStatus)) {\n\t\t\temit failed();\n\t\t\treturn;\n\t\t}\n\n\t\twhile (pending) {\n\t\t\tauto items = broadcasts[\"items\"].array_items();\n\t\t\tfor (auto item : items) {\n\t\t\t\tQString status =\n\t\t\t\t\tQString::fromStdString(item[\"status\"][\"lifeCycleStatus\"].string_value());\n\n\t\t\t\tif (status == \"live\" || status == \"testing\") {\n\t\t\t\t\t// Check that the attached liveStream is offline (reconnectable)\n\t\t\t\t\tQString stream_id = QString::fromStdString(\n\t\t\t\t\t\titem[\"contentDetails\"][\"boundStreamId\"].string_value());\n\t\t\t\t\tjson11::Json stream;\n\t\t\t\t\tif (!apiYouTube->FindStream(stream_id, stream))\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\tif (stream[\"status\"][\"streamStatus\"] == \"active\")\n\t\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tQString title = QString::fromStdString(item[\"snippet\"][\"title\"].string_value());\n\t\t\t\tQString scheduledStartTime =\n\t\t\t\t\tQString::fromStdString(item[\"snippet\"][\"scheduledStartTime\"].string_value());\n\t\t\t\tQString broadcast = QString::fromStdString(item[\"id\"].string_value());\n\n\t\t\t\t// Treat already started streams as autostart for UI purposes\n\t\t\t\tbool astart = status == \"live\" ||\n\t\t\t\t\t      item[\"contentDetails\"][\"enableAutoStart\"].bool_value();\n\t\t\t\tbool astop = item[\"contentDetails\"][\"enableAutoStop\"].bool_value();\n\n\t\t\t\tQDateTime utcDTime =\n\t\t\t\t\tQDateTime::fromString(scheduledStartTime, SchedulDateAndTimeFormat);\n\t\t\t\t// DateTime parser means that input datetime is a local, so we need to move it\n\t\t\t\tQDateTime dateTime = utcDTime.addSecs(utcDTime.offsetFromUtc());\n\n\t\t\t\tQString dateTimeString = QLocale().toString(\n\t\t\t\t\tdateTime, QString(\"%1  %2\").arg(QLocale().dateFormat(QLocale::LongFormat),\n\t\t\t\t\t\t\t\t\tQLocale().timeFormat(QLocale::ShortFormat)));\n\n\t\t\t\temit new_item(title, dateTimeString, broadcast, status, astart, astop);\n\t\t\t}\n\n\t\t\tauto nextPageToken = broadcasts[\"nextPageToken\"].string_value();\n\t\t\tif (nextPageToken.empty() || items.empty())\n\t\t\t\tbreak;\n\t\t\telse {\n\t\t\t\tif (!pending)\n\t\t\t\t\treturn;\n\t\t\t\tif (!apiYouTube->GetBroadcastsList(broadcasts, QString::fromStdString(nextPageToken),\n\t\t\t\t\t\t\t\t   broadcastStatus)) {\n\t\t\t\t\temit failed();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\temit ready();\n}\n\nvoid OBSYoutubeActions::UpdateOkButtonStatus()\n{\n\tbool enable = false;\n\n\tif (ui->tabWidget->currentIndex() == 0) {\n\t\tenable = !ui->title->text().isEmpty() && !ui->privacyBox->currentText().isEmpty() &&\n\t\t\t (ui->yesMakeForKids->isChecked() || ui->notMakeForKids->isChecked());\n\t\tui->okButton->setEnabled(enable);\n\t\tui->saveButton->setEnabled(enable);\n\n\t\tif (ui->checkScheduledLater->checkState() == Qt::Checked) {\n\t\t\tui->okButton->setText(QTStr(\"YouTube.Actions.Create_Schedule\"));\n\t\t\tui->saveButton->setText(QTStr(\"YouTube.Actions.Create_Schedule_Ready\"));\n\t\t} else {\n\t\t\tui->okButton->setText(QTStr(\"YouTube.Actions.Create_GoLive\"));\n\t\t\tui->saveButton->setText(QTStr(\"YouTube.Actions.Create_Ready\"));\n\t\t}\n\t\tui->pushButton->setVisible(false);\n\t} else {\n\t\tenable = !selectedBroadcast.isEmpty();\n\t\tui->okButton->setEnabled(enable);\n\t\tui->saveButton->setEnabled(enable);\n\t\tui->okButton->setText(QTStr(\"YouTube.Actions.Choose_GoLive\"));\n\t\tui->saveButton->setText(QTStr(\"YouTube.Actions.Choose_Ready\"));\n\n\t\tui->pushButton->setVisible(true);\n\t}\n}\nbool OBSYoutubeActions::CreateEventAction(YoutubeApiWrappers *api, BroadcastDescription &broadcast,\n\t\t\t\t\t  StreamDescription &stream, bool stream_later, bool ready_broadcast)\n{\n\tYoutubeApiWrappers *apiYouTube = api;\n\tUiToBroadcast(broadcast);\n\n\tif (stream_later) {\n\t\t// DateTime parser means that input datetime is a local, so we need to move it\n\t\tauto dateTime = ui->scheduledTime->dateTime();\n\t\tauto utcDTime = dateTime.addSecs(-dateTime.offsetFromUtc());\n\t\tbroadcast.schedul_date_time = utcDTime.toString(SchedulDateAndTimeFormat);\n\t} else {\n\t\t// stream now is always autostart/autostop\n\t\tbroadcast.auto_start = true;\n\t\tbroadcast.auto_stop = true;\n\t\tbroadcast.schedul_date_time = QDateTime::currentDateTimeUtc().toString(SchedulDateAndTimeFormat);\n\t}\n\n\tautostart = broadcast.auto_start;\n\tautostop = broadcast.auto_stop;\n\n\tblog(LOG_DEBUG, \"Scheduled date and time: %s\", broadcast.schedul_date_time.toStdString().c_str());\n\tif (!apiYouTube->InsertBroadcast(broadcast)) {\n\t\tblog(LOG_DEBUG, \"No broadcast created.\");\n\t\treturn false;\n\t}\n\tif (!apiYouTube->SetVideoCategory(broadcast.id, broadcast.title, broadcast.description,\n\t\t\t\t\t  broadcast.category.id)) {\n\t\tblog(LOG_DEBUG, \"No category set.\");\n\t\treturn false;\n\t}\n\tif (!thumbnailFile.isEmpty()) {\n\t\tblog(LOG_INFO, \"Uploading thumbnail file \\\"%s\\\"...\", thumbnailFile.toStdString().c_str());\n\t\tif (!apiYouTube->SetVideoThumbnail(broadcast.id, thumbnailFile)) {\n\t\t\tblog(LOG_DEBUG, \"No thumbnail set.\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!stream_later || ready_broadcast) {\n\t\tstream = {\"\", \"\", \"OBS Studio Video Stream\"};\n\t\tif (!apiYouTube->InsertStream(stream)) {\n\t\t\tblog(LOG_DEBUG, \"No stream created.\");\n\t\t\treturn false;\n\t\t}\n\t\tif (!apiYouTube->BindStream(broadcast.id, stream.id)) {\n\t\t\tblog(LOG_DEBUG, \"No stream binded.\");\n\t\t\treturn false;\n\t\t}\n\n\t\tif (broadcast.privacy != \"private\") {\n\t\t\tapiYouTube->SetChatId(broadcast.id);\n\t\t} else {\n\t\t\tapiYouTube->ResetChat();\n\t\t}\n\t}\n\n#ifdef YOUTUBE_ENABLED\n\tif (OBSBasic::Get()->GetYouTubeAppDock())\n\t\tOBSBasic::Get()->GetYouTubeAppDock()->BroadcastCreated(broadcast.id.toStdString().c_str());\n#endif\n\n\treturn true;\n}\n\nbool OBSYoutubeActions::ChooseAnEventAction(YoutubeApiWrappers *api, StreamDescription &stream)\n{\n\tYoutubeApiWrappers *apiYouTube = api;\n\n\tjson11::Json json;\n\tif (!apiYouTube->FindBroadcast(selectedBroadcast, json)) {\n\t\tblog(LOG_DEBUG, \"No broadcast found.\");\n\t\treturn false;\n\t}\n\n\tstd::string boundStreamId = json[\"items\"].array_items()[0][\"contentDetails\"][\"boundStreamId\"].string_value();\n\tstd::string broadcastPrivacy = json[\"items\"].array_items()[0][\"status\"][\"privacyStatus\"].string_value();\n\n\tstream.id = boundStreamId.c_str();\n\tif (!stream.id.isEmpty() && apiYouTube->FindStream(stream.id, json)) {\n\t\tauto item = json[\"items\"].array_items()[0];\n\t\tauto streamName = item[\"cdn\"][\"ingestionInfo\"][\"streamName\"].string_value();\n\t\tauto title = item[\"snippet\"][\"title\"].string_value();\n\n\t\tstream.name = streamName.c_str();\n\t\tstream.title = title.c_str();\n\t\tapi->SetBroadcastId(selectedBroadcast);\n\t} else {\n\t\tstream = {\"\", \"\", \"OBS Studio Video Stream\"};\n\t\tif (!apiYouTube->InsertStream(stream)) {\n\t\t\tblog(LOG_DEBUG, \"No stream created.\");\n\t\t\treturn false;\n\t\t}\n\t\tif (!apiYouTube->BindStream(selectedBroadcast, stream.id)) {\n\t\t\tblog(LOG_DEBUG, \"No stream binded.\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (broadcastPrivacy != \"private\")\n\t\tapiYouTube->SetChatId(selectedBroadcast);\n\telse\n\t\tapiYouTube->ResetChat();\n\n#ifdef YOUTUBE_ENABLED\n\tif (OBSBasic::Get()->GetYouTubeAppDock())\n\t\tOBSBasic::Get()->GetYouTubeAppDock()->BroadcastSelected(selectedBroadcast.toStdString().c_str());\n#endif\n\n\treturn true;\n}\n\nvoid OBSYoutubeActions::ShowErrorDialog(QWidget *parent, QString text)\n{\n\tQMessageBox dlg(parent);\n\tdlg.setWindowFlags(dlg.windowFlags() & ~Qt::WindowCloseButtonHint);\n\tdlg.setWindowTitle(QTStr(\"YouTube.Actions.Error.Title\"));\n\tdlg.setText(text);\n\tdlg.setTextFormat(Qt::RichText);\n\tdlg.setIcon(QMessageBox::Warning);\n\tdlg.setStandardButtons(QMessageBox::StandardButton::Ok);\n\tdlg.exec();\n}\n\nvoid OBSYoutubeActions::InitBroadcast()\n{\n\tBroadcastDescription broadcast;\n\tStreamDescription stream;\n\tQMessageBox msgBox(this);\n\tmsgBox.setWindowFlags(msgBox.windowFlags() & ~Qt::WindowCloseButtonHint);\n\tmsgBox.setWindowTitle(QTStr(\"YouTube.Actions.Notify.Title\"));\n\tmsgBox.setText(QTStr(\"YouTube.Actions.Notify.CreatingBroadcast\"));\n\tmsgBox.setStandardButtons(QMessageBox::StandardButtons());\n\n\tbool success = false;\n\tauto action = [&]() {\n\t\tif (ui->tabWidget->currentIndex() == 0) {\n\t\t\tsuccess = this->CreateEventAction(apiYouTube, broadcast, stream,\n\t\t\t\t\t\t\t  ui->checkScheduledLater->isChecked());\n\t\t} else {\n\t\t\tsuccess = this->ChooseAnEventAction(apiYouTube, stream);\n\t\t\tif (success)\n\t\t\t\tbroadcast.id = this->selectedBroadcast;\n\t\t};\n\t\tQMetaObject::invokeMethod(&msgBox, \"accept\", Qt::QueuedConnection);\n\t};\n\tQScopedPointer<QThread> thread(CreateQThread(action));\n\tthread->start();\n\tmsgBox.exec();\n\tthread->wait();\n\n\tif (success) {\n\t\tif (ui->tabWidget->currentIndex() == 0) {\n\t\t\t// Stream later usecase.\n\t\t\tif (ui->checkScheduledLater->isChecked()) {\n\t\t\t\tQMessageBox msg(this);\n\t\t\t\tmsg.setWindowTitle(QTStr(\"YouTube.Actions.EventCreated.Title\"));\n\t\t\t\tmsg.setText(QTStr(\"YouTube.Actions.EventCreated.Text\"));\n\t\t\t\tmsg.setStandardButtons(QMessageBox::Ok);\n\t\t\t\tmsg.exec();\n\t\t\t\t// Close dialog without start streaming.\n\t\t\t\tCancel();\n\t\t\t} else {\n\t\t\t\t// Stream now usecase.\n\t\t\t\tblog(LOG_DEBUG, \"New valid stream: %s\", QT_TO_UTF8(stream.name));\n\t\t\t\temit ok(QT_TO_UTF8(broadcast.id), QT_TO_UTF8(stream.id), QT_TO_UTF8(stream.name), true,\n\t\t\t\t\ttrue, true);\n\t\t\t\tAccept();\n\t\t\t}\n\t\t} else {\n\t\t\t// Stream to precreated broadcast usecase.\n\t\t\temit ok(QT_TO_UTF8(broadcast.id), QT_TO_UTF8(stream.id), QT_TO_UTF8(stream.name), autostart,\n\t\t\t\tautostop, true);\n\t\t\tAccept();\n\t\t}\n\t} else {\n\t\t// Fail.\n\t\tauto last_error = apiYouTube->GetLastError();\n\t\tif (last_error.isEmpty())\n\t\t\tlast_error = QTStr(\"YouTube.Actions.Error.YouTubeApi\");\n\t\tif (!apiYouTube->GetTranslatedError(last_error))\n\t\t\tlast_error = QTStr(\"YouTube.Actions.Error.NoBroadcastCreated\").arg(last_error);\n\n\t\tShowErrorDialog(this, last_error);\n\t}\n}\n\nvoid OBSYoutubeActions::ReadyBroadcast()\n{\n\tBroadcastDescription broadcast;\n\tStreamDescription stream;\n\tQMessageBox msgBox(this);\n\tmsgBox.setWindowFlags(msgBox.windowFlags() & ~Qt::WindowCloseButtonHint);\n\tmsgBox.setWindowTitle(QTStr(\"YouTube.Actions.Notify.Title\"));\n\tmsgBox.setText(QTStr(\"YouTube.Actions.Notify.CreatingBroadcast\"));\n\tmsgBox.setStandardButtons(QMessageBox::StandardButtons());\n\n\tbool success = false;\n\tauto action = [&]() {\n\t\tif (ui->tabWidget->currentIndex() == 0) {\n\t\t\tsuccess = this->CreateEventAction(apiYouTube, broadcast, stream,\n\t\t\t\t\t\t\t  ui->checkScheduledLater->isChecked(), true);\n\t\t} else {\n\t\t\tsuccess = this->ChooseAnEventAction(apiYouTube, stream);\n\t\t\tif (success)\n\t\t\t\tbroadcast.id = this->selectedBroadcast;\n\t\t};\n\t\tQMetaObject::invokeMethod(&msgBox, \"accept\", Qt::QueuedConnection);\n\t};\n\tQScopedPointer<QThread> thread(CreateQThread(action));\n\tthread->start();\n\tmsgBox.exec();\n\tthread->wait();\n\n\tif (success) {\n\t\temit ok(QT_TO_UTF8(broadcast.id), QT_TO_UTF8(stream.id), QT_TO_UTF8(stream.name), autostart, autostop,\n\t\t\tfalse);\n\t\tAccept();\n\t} else {\n\t\t// Fail.\n\t\tauto last_error = apiYouTube->GetLastError();\n\t\tif (last_error.isEmpty())\n\t\t\tlast_error = QTStr(\"YouTube.Actions.Error.YouTubeApi\");\n\t\tif (!apiYouTube->GetTranslatedError(last_error))\n\t\t\tlast_error = QTStr(\"YouTube.Actions.Error.NoBroadcastCreated\").arg(last_error);\n\n\t\tShowErrorDialog(this, last_error);\n\t}\n}\n\nvoid OBSYoutubeActions::UiToBroadcast(BroadcastDescription &broadcast)\n{\n\tbroadcast.title = ui->title->text();\n\t// ToDo: UI warning rather than silent truncation\n\tbroadcast.description = ui->description->toPlainText().left(5000);\n\tbroadcast.privacy = ui->privacyBox->currentData().toString();\n\tbroadcast.category.title = ui->categoryBox->currentText();\n\tbroadcast.category.id = ui->categoryBox->currentData().toString();\n\tbroadcast.made_for_kids = ui->yesMakeForKids->isChecked();\n\tbroadcast.latency = ui->latencyBox->currentData().toString();\n\tbroadcast.auto_start = ui->checkAutoStart->isChecked();\n\tbroadcast.auto_stop = ui->checkAutoStop->isChecked();\n\tbroadcast.dvr = ui->checkDVR->isChecked();\n\tbroadcast.schedul_for_later = ui->checkScheduledLater->isChecked();\n\tbroadcast.projection = ui->check360Video->isChecked() ? \"360\" : \"rectangular\";\n\n\tif (ui->checkRememberSettings->isChecked())\n\t\tSaveSettings(broadcast);\n}\n\nvoid OBSYoutubeActions::SaveSettings(BroadcastDescription &broadcast)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tconfig_set_string(main->activeConfiguration, \"YouTube\", \"Title\", QT_TO_UTF8(broadcast.title));\n\tconfig_set_string(main->activeConfiguration, \"YouTube\", \"Description\", QT_TO_UTF8(broadcast.description));\n\tconfig_set_string(main->activeConfiguration, \"YouTube\", \"Privacy\", QT_TO_UTF8(broadcast.privacy));\n\tconfig_set_string(main->activeConfiguration, \"YouTube\", \"CategoryID\", QT_TO_UTF8(broadcast.category.id));\n\tconfig_set_string(main->activeConfiguration, \"YouTube\", \"Latency\", QT_TO_UTF8(broadcast.latency));\n\tconfig_set_bool(main->activeConfiguration, \"YouTube\", \"MadeForKids\", broadcast.made_for_kids);\n\tconfig_set_bool(main->activeConfiguration, \"YouTube\", \"AutoStart\", broadcast.auto_start);\n\tconfig_set_bool(main->activeConfiguration, \"YouTube\", \"AutoStop\", broadcast.auto_start);\n\tconfig_set_bool(main->activeConfiguration, \"YouTube\", \"DVR\", broadcast.dvr);\n\tconfig_set_bool(main->activeConfiguration, \"YouTube\", \"ScheduleForLater\", broadcast.schedul_for_later);\n\tconfig_set_string(main->activeConfiguration, \"YouTube\", \"Projection\", QT_TO_UTF8(broadcast.projection));\n\tconfig_set_string(main->activeConfiguration, \"YouTube\", \"ThumbnailFile\", QT_TO_UTF8(thumbnailFile));\n\tconfig_set_bool(main->activeConfiguration, \"YouTube\", \"RememberSettings\", true);\n}\n\nvoid OBSYoutubeActions::LoadSettings()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tconst char *title = config_get_string(main->activeConfiguration, \"YouTube\", \"Title\");\n\tui->title->setText(QT_UTF8(title));\n\n\tconst char *desc = config_get_string(main->activeConfiguration, \"YouTube\", \"Description\");\n\tui->description->setPlainText(QT_UTF8(desc));\n\n\tconst char *priv = config_get_string(main->activeConfiguration, \"YouTube\", \"Privacy\");\n\tint index = ui->privacyBox->findData(priv);\n\tui->privacyBox->setCurrentIndex(index);\n\n\tconst char *catID = config_get_string(main->activeConfiguration, \"YouTube\", \"CategoryID\");\n\tindex = ui->categoryBox->findData(catID);\n\tui->categoryBox->setCurrentIndex(index);\n\n\tconst char *latency = config_get_string(main->activeConfiguration, \"YouTube\", \"Latency\");\n\tindex = ui->latencyBox->findData(latency);\n\tui->latencyBox->setCurrentIndex(index);\n\n\tbool dvr = config_get_bool(main->activeConfiguration, \"YouTube\", \"DVR\");\n\tui->checkDVR->setChecked(dvr);\n\n\tbool forKids = config_get_bool(main->activeConfiguration, \"YouTube\", \"MadeForKids\");\n\tif (forKids)\n\t\tui->yesMakeForKids->setChecked(true);\n\telse\n\t\tui->notMakeForKids->setChecked(true);\n\n\tbool schedLater = config_get_bool(main->activeConfiguration, \"YouTube\", \"ScheduleForLater\");\n\tui->checkScheduledLater->setChecked(schedLater);\n\n\tbool autoStart = config_get_bool(main->activeConfiguration, \"YouTube\", \"AutoStart\");\n\tui->checkAutoStart->setChecked(autoStart);\n\n\tbool autoStop = config_get_bool(main->activeConfiguration, \"YouTube\", \"AutoStop\");\n\tui->checkAutoStop->setChecked(autoStop);\n\n\tconst char *projection = config_get_string(main->activeConfiguration, \"YouTube\", \"Projection\");\n\tif (projection && *projection) {\n\t\tif (strcmp(projection, \"360\") == 0)\n\t\t\tui->check360Video->setChecked(true);\n\t\telse\n\t\t\tui->check360Video->setChecked(false);\n\t}\n\n\tconst char *thumbFile = config_get_string(main->activeConfiguration, \"YouTube\", \"ThumbnailFile\");\n\tif (thumbFile && *thumbFile) {\n\t\tQFileInfo tFile(thumbFile);\n\t\t// Re-check validity before setting path again\n\t\tif (tFile.exists() && tFile.size() <= 2 * 1024 * 1024) {\n\t\t\tthumbnailFile = tFile.absoluteFilePath();\n\t\t\tui->selectedFileName->setText(thumbnailFile);\n\t\t\tui->selectFileButton->setText(QTStr(\"YouTube.Actions.Thumbnail.ClearFile\"));\n\n\t\t\tQImageReader imgReader(thumbnailFile);\n\t\t\timgReader.setAutoTransform(true);\n\t\t\tconst QImage newImage = imgReader.read();\n\t\t\tui->thumbnailPreview->setPixmap(QPixmap::fromImage(newImage).scaled(\n\t\t\t\t160, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation));\n\t\t}\n\t}\n}\n\nvoid OBSYoutubeActions::OpenYouTubeDashboard()\n{\n\tChannelDescription channel;\n\tif (!apiYouTube->GetChannelDescription(channel)) {\n\t\tblog(LOG_DEBUG, \"Could not get channel description.\");\n\t\tShowErrorDialog(this, apiYouTube->GetLastError().isEmpty()\n\t\t\t\t\t      ? QTStr(\"YouTube.Actions.Error.General\")\n\t\t\t\t\t      : QTStr(\"YouTube.Actions.Error.Text\").arg(apiYouTube->GetLastError()));\n\t\treturn;\n\t}\n\n\t//https://studio.youtube.com/channel/UCA9bSfH3KL186kyiUsvi3IA/videos/live?filter=%5B%5D&sort=%7B%22columnType%22%3A%22date%22%2C%22sortOrder%22%3A%22DESCENDING%22%7D\n\tQString uri =\n\t\tQString(\"https://studio.youtube.com/channel/%1/videos/live?filter=[]&sort={\\\"columnType\\\"%3A\\\"date\\\"%2C\\\"sortOrder\\\"%3A\\\"DESCENDING\\\"}\")\n\t\t\t.arg(channel.id);\n\tQDesktopServices::openUrl(QUrl(uri));\n}\n\nvoid OBSYoutubeActions::Cancel()\n{\n\tworkerThread->stop();\n\treject();\n}\nvoid OBSYoutubeActions::Accept()\n{\n\tworkerThread->stop();\n\taccept();\n}\n"
  },
  {
    "path": "frontend/dialogs/OBSYoutubeActions.hpp",
    "content": "#pragma once\n\n#include \"ui_OBSYoutubeActions.h\"\n\n#include <utility/YoutubeApiWrappers.hpp>\n\n#include <QThread>\n\nclass WorkerThread : public QThread {\n\tQ_OBJECT\npublic:\n\tWorkerThread(YoutubeApiWrappers *api) : QThread(), apiYouTube(api) {}\n\n\tvoid stop() { pending = false; }\n\nprotected:\n\tYoutubeApiWrappers *apiYouTube;\n\tbool pending = true;\n\npublic slots:\n\tvoid run() override;\nsignals:\n\tvoid ready();\n\tvoid new_item(const QString &title, const QString &dateTimeString, const QString &broadcast,\n\t\t      const QString &status, bool astart, bool astop);\n\tvoid failed();\n};\n\nclass OBSYoutubeActions : public QDialog {\n\tQ_OBJECT\n\tQ_PROPERTY(QIcon thumbPlaceholder READ GetPlaceholder WRITE SetPlaceholder DESIGNABLE true)\n\n\tstd::unique_ptr<Ui::OBSYoutubeActions> ui;\n\nsignals:\n\tvoid ok(const QString &broadcast_id, const QString &stream_id, const QString &key, bool autostart,\n\t\tbool autostop, bool start_now);\n\nprotected:\n\tvoid showEvent(QShowEvent *event) override;\n\tvoid UpdateOkButtonStatus();\n\n\tbool CreateEventAction(YoutubeApiWrappers *api, BroadcastDescription &broadcast, StreamDescription &stream,\n\t\t\t       bool stream_later, bool ready_broadcast = false);\n\tbool ChooseAnEventAction(YoutubeApiWrappers *api, StreamDescription &stream);\n\n\tvoid ShowErrorDialog(QWidget *parent, QString text);\n\npublic:\n\texplicit OBSYoutubeActions(QWidget *parent, Auth *auth, bool broadcastReady);\n\tvirtual ~OBSYoutubeActions() override;\n\n\tbool Valid() { return valid; };\n\nprivate:\n\tvoid InitBroadcast();\n\tvoid ReadyBroadcast();\n\tvoid UiToBroadcast(BroadcastDescription &broadcast);\n\tvoid OpenYouTubeDashboard();\n\tvoid Cancel();\n\tvoid Accept();\n\tvoid SaveSettings(BroadcastDescription &broadcast);\n\tvoid LoadSettings();\n\n\tQIcon GetPlaceholder() { return thumbPlaceholder; }\n\tvoid SetPlaceholder(const QIcon &icon) { thumbPlaceholder = icon; }\n\n\tQString selectedBroadcast;\n\tbool autostart, autostop;\n\tbool valid = false;\n\tYoutubeApiWrappers *apiYouTube;\n\tWorkerThread *workerThread = nullptr;\n\tbool broadcastReady = false;\n\tQString thumbnailFile;\n\tQIcon thumbPlaceholder;\n};\n"
  },
  {
    "path": "frontend/docks/BrowserDock.cpp",
    "content": "#include \"BrowserDock.hpp\"\n\n#include <QCloseEvent>\n#include \"moc_BrowserDock.cpp\"\n\nvoid BrowserDock::closeEvent(QCloseEvent *event)\n{\n\tOBSDock::closeEvent(event);\n\n\tif (!event->isAccepted()) {\n\t\treturn;\n\t}\n\n\tstatic int panel_version = -1;\n\tif (panel_version == -1) {\n\t\tpanel_version = obs_browser_qcef_version();\n\t}\n\n\tif (panel_version >= 2 && !!cefWidget) {\n\t\tcefWidget->closeBrowser();\n\t}\n}\n\nvoid BrowserDock::showEvent(QShowEvent *event)\n{\n\tOBSDock::showEvent(event);\n\tsetWindowTitle(title);\n}\n"
  },
  {
    "path": "frontend/docks/BrowserDock.hpp",
    "content": "#pragma once\n\n#include \"OBSDock.hpp\"\n\n#include <browser-panel.hpp>\n\nextern QCef *cef;\nextern QCefCookieManager *panel_cookies;\n\nclass BrowserDock : public OBSDock {\n\tQ_OBJECT\n\nprivate:\n\tQString title;\n\npublic:\n\tinline BrowserDock() : OBSDock() { setAttribute(Qt::WA_NativeWindow); }\n\tinline BrowserDock(const QString &title_) : OBSDock(title_)\n\t{\n\t\ttitle = title_;\n\t\tsetAttribute(Qt::WA_NativeWindow);\n\t}\n\n\tQScopedPointer<QCefWidget> cefWidget;\n\n\tinline void SetWidget(QCefWidget *widget_)\n\t{\n\t\tsetWidget(widget_);\n\t\tcefWidget.reset(widget_);\n\t}\n\n\tinline void setTitle(const QString &title_) { title = title_; }\n\n\tvoid closeEvent(QCloseEvent *event) override;\n\tvoid showEvent(QShowEvent *event) override;\n};\n"
  },
  {
    "path": "frontend/docks/OBSDock.cpp",
    "content": "#include \"OBSDock.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include <QCheckBox>\n#include <QMessageBox>\n\n#include \"moc_OBSDock.cpp\"\n\nvoid OBSDock::closeEvent(QCloseEvent *event)\n{\n\tauto msgBox = []() {\n\t\tQMessageBox msgbox(App()->GetMainWindow());\n\t\tmsgbox.setWindowTitle(QTStr(\"DockCloseWarning.Title\"));\n\t\tmsgbox.setText(QTStr(\"DockCloseWarning.Text\"));\n\t\tmsgbox.setIcon(QMessageBox::Icon::Information);\n\t\tmsgbox.addButton(QMessageBox::Ok);\n\n\t\tQCheckBox *cb = new QCheckBox(QTStr(\"DoNotShowAgain\"));\n\t\tmsgbox.setCheckBox(cb);\n\n\t\tmsgbox.exec();\n\n\t\tif (cb->isChecked()) {\n\t\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"WarnedAboutClosingDocks\", true);\n\t\t\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n\t\t}\n\t};\n\n\tbool warned = config_get_bool(App()->GetUserConfig(), \"General\", \"WarnedAboutClosingDocks\");\n\tif (!OBSBasic::Get()->isClosing() && !warned) {\n\t\tQMetaObject::invokeMethod(App(), \"Exec\", Qt::QueuedConnection, Q_ARG(VoidFunc, msgBox));\n\t}\n\n\tQDockWidget::closeEvent(event);\n\n\tif (widget() && event->isAccepted()) {\n\t\tQEvent widgetEvent(QEvent::Type(QEvent::User + QEvent::Close));\n\t\tqApp->sendEvent(widget(), &widgetEvent);\n\t}\n}\n\nvoid OBSDock::showEvent(QShowEvent *event)\n{\n\tQDockWidget::showEvent(event);\n}\n"
  },
  {
    "path": "frontend/docks/OBSDock.hpp",
    "content": "#pragma once\n\n#include <QDockWidget>\n\nclass QCloseEvent;\nclass QShowEvent;\nclass QString;\n\nclass OBSDock : public QDockWidget {\n\tQ_OBJECT\n\npublic:\n\tinline OBSDock(QWidget *parent = nullptr) : QDockWidget(parent) {}\n\tinline OBSDock(const QString &title, QWidget *parent = nullptr) : QDockWidget(title, parent) {}\n\n\tvirtual void closeEvent(QCloseEvent *event);\n\tvirtual void showEvent(QShowEvent *event);\n};\n"
  },
  {
    "path": "frontend/docks/YouTubeAppDock.cpp",
    "content": "#include \"YouTubeAppDock.hpp\"\n\n#include <utility/YoutubeApiWrappers.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QUuid>\n#include <nlohmann/json.hpp>\n\n#include \"moc_YouTubeAppDock.cpp\"\n\nusing json = nlohmann::json;\n\nextern bool cef_js_avail;\n\n#ifdef YOUTUBE_WEBAPP_PLACEHOLDER\nstatic constexpr const char *YOUTUBE_WEBAPP_PLACEHOLDER_URL = YOUTUBE_WEBAPP_PLACEHOLDER;\n#else\nstatic constexpr const char *YOUTUBE_WEBAPP_PLACEHOLDER_URL =\n\t\"https://studio.youtube.com/live/channel/UC/console?kc=OBS\";\n#endif\n\n#ifdef YOUTUBE_WEBAPP_ADDRESS\nstatic constexpr const char *YOUTUBE_WEBAPP_ADDRESS_URL = YOUTUBE_WEBAPP_ADDRESS;\n#else\nstatic constexpr const char *YOUTUBE_WEBAPP_ADDRESS_URL = \"https://studio.youtube.com/live/channel/%1/console?kc=OBS\";\n#endif\n\nstatic constexpr const char *BROADCAST_CREATED = \"BROADCAST_CREATED\";\nstatic constexpr const char *BROADCAST_SELECTED = \"BROADCAST_SELECTED\";\nstatic constexpr const char *INGESTION_STARTED = \"INGESTION_STARTED\";\nstatic constexpr const char *INGESTION_STOPPED = \"INGESTION_STOPPED\";\n\nYouTubeAppDock::YouTubeAppDock(const QString &title) : BrowserDock(title), dockBrowser(nullptr)\n{\n\tcef->init_browser();\n\tOBSBasic::InitBrowserPanelSafeBlock();\n\tAddYouTubeAppDock();\n}\n\nbool YouTubeAppDock::IsYTServiceSelected()\n{\n\tif (!cef_js_avail)\n\t\treturn false;\n\n\tobs_service_t *service_obj = OBSBasic::Get()->GetService();\n\tOBSDataAutoRelease settings = obs_service_get_settings(service_obj);\n\tconst char *service = obs_data_get_string(settings, \"service\");\n\treturn IsYouTubeService(service);\n}\n\nvoid YouTubeAppDock::AccountConnected()\n{\n\tchannelId.clear(); // renew channel id\n\tUpdateChannelId();\n}\n\nvoid YouTubeAppDock::AccountDisconnected()\n{\n\tSettingsUpdated(true);\n}\n\nvoid YouTubeAppDock::SettingsUpdated(bool cleanup)\n{\n\tbool ytservice = IsYTServiceSelected();\n\tSetVisibleYTAppDockInMenu(ytservice);\n\n\t// definitely cleanup if YT switched off\n\tif (!ytservice || cleanup) {\n\t\tif (panel_cookies) {\n\t\t\tpanel_cookies->DeleteCookies(\"youtube.com\", \"\");\n\t\t\tpanel_cookies->DeleteCookies(\"google.com\", \"\");\n\t\t}\n\t}\n\n\tif (ytservice)\n\t\tUpdate();\n}\n\nstd::string YouTubeAppDock::InitYTUserUrl()\n{\n\tstd::string user_url(YOUTUBE_WEBAPP_PLACEHOLDER_URL);\n\n\tif (IsUserSignedIntoYT()) {\n\t\tYoutubeApiWrappers *apiYouTube = GetYTApi();\n\t\tif (apiYouTube) {\n\t\t\tChannelDescription channel_description;\n\t\t\tif (apiYouTube->GetChannelDescription(channel_description)) {\n\t\t\t\tQString url = QString(YOUTUBE_WEBAPP_ADDRESS_URL).arg(channel_description.id);\n\t\t\t\tuser_url = url.toStdString();\n\t\t\t} else {\n\t\t\t\tblog(LOG_ERROR, \"YT: InitYTUserUrl() Failed to get channel id\");\n\t\t\t}\n\t\t}\n\t} else {\n\t\tblog(LOG_ERROR, \"YT: InitYTUserUrl() User is not signed\");\n\t}\n\n\tblog(LOG_DEBUG, \"YT: InitYTUserUrl() User url: %s\", user_url.c_str());\n\treturn user_url;\n}\n\nvoid YouTubeAppDock::AddYouTubeAppDock()\n{\n\tQString bId(QUuid::createUuid().toString());\n\tbId.replace(QRegularExpression(\"[{}-]\"), \"\");\n\tthis->setProperty(\"uuid\", bId);\n\tthis->setObjectName(\"youtubeLiveControlPanel\");\n\tthis->resize(580, 500);\n\tthis->setMinimumSize(400, 300);\n\tthis->setAllowedAreas(Qt::AllDockWidgetAreas);\n\n\tOBSBasic::Get()->AddDockWidget(this, Qt::RightDockWidgetArea);\n\n\tif (IsYTServiceSelected()) {\n\t\tconst std::string url = InitYTUserUrl();\n\t\tCreateBrowserWidget(url);\n\t} else {\n\t\tthis->setVisible(false);\n\t\tthis->toggleViewAction()->setVisible(false);\n\t}\n}\n\nvoid YouTubeAppDock::CreateBrowserWidget(const std::string &url)\n{\n\tif (dockBrowser)\n\t\tdelete dockBrowser;\n\tdockBrowser = cef->create_widget(this, url, panel_cookies);\n\tif (!dockBrowser)\n\t\treturn;\n\n\tif (obs_browser_qcef_version() >= 1)\n\t\tdockBrowser->allowAllPopups(true);\n\n\tthis->SetWidget(dockBrowser);\n\n\tQWidget::connect(dockBrowser.get(), &QCefWidget::urlChanged, this, &YouTubeAppDock::ReloadChatDock);\n\n\tUpdate();\n}\n\nvoid YouTubeAppDock::SetVisibleYTAppDockInMenu(bool visible)\n{\n\tif (visible && toggleViewAction()->isVisible())\n\t\treturn;\n\n\ttoggleViewAction()->setVisible(visible);\n\tthis->setVisible(visible);\n}\n\n// only 'ACCOUNT' mode supported\nvoid YouTubeAppDock::BroadcastCreated(const char *stream_id)\n{\n\tDispatchYTEvent(BROADCAST_CREATED, stream_id, YTSM_ACCOUNT);\n}\n\n// only 'ACCOUNT' mode supported\nvoid YouTubeAppDock::BroadcastSelected(const char *stream_id)\n{\n\tDispatchYTEvent(BROADCAST_SELECTED, stream_id, YTSM_ACCOUNT);\n}\n\n// both 'ACCOUNT' and 'STREAM_KEY' modes supported\nvoid YouTubeAppDock::IngestionStarted()\n{\n\tobs_service_t *service_obj = OBSBasic::Get()->GetService();\n\tOBSDataAutoRelease settings = obs_service_get_settings(service_obj);\n\tconst char *service = obs_data_get_string(settings, \"service\");\n\tif (IsYouTubeService(service)) {\n\t\tif (IsUserSignedIntoYT()) {\n\t\t\tconst char *broadcast_id = obs_data_get_string(settings, \"broadcast_id\");\n\t\t\tthis->IngestionStarted(broadcast_id, YouTubeAppDock::YTSM_ACCOUNT);\n\t\t} else {\n\t\t\tconst char *stream_key = obs_data_get_string(settings, \"key\");\n\t\t\tthis->IngestionStarted(stream_key, YouTubeAppDock::YTSM_STREAM_KEY);\n\t\t}\n\t}\n}\n\nvoid YouTubeAppDock::IngestionStarted(const char *stream_id, streaming_mode_t mode)\n{\n\tDispatchYTEvent(INGESTION_STARTED, stream_id, mode);\n}\n\n// both 'ACCOUNT' and 'STREAM_KEY' modes supported\nvoid YouTubeAppDock::IngestionStopped()\n{\n\tobs_service_t *service_obj = OBSBasic::Get()->GetService();\n\tOBSDataAutoRelease settings = obs_service_get_settings(service_obj);\n\tconst char *service = obs_data_get_string(settings, \"service\");\n\n\tif (IsYouTubeService(service)) {\n\t\tif (IsUserSignedIntoYT()) {\n\t\t\tconst char *broadcast_id = obs_data_get_string(settings, \"broadcast_id\");\n\t\t\tthis->IngestionStopped(broadcast_id, YouTubeAppDock::YTSM_ACCOUNT);\n\t\t} else {\n\t\t\tconst char *stream_key = obs_data_get_string(settings, \"key\");\n\t\t\tthis->IngestionStopped(stream_key, YouTubeAppDock::YTSM_STREAM_KEY);\n\t\t}\n\t}\n}\n\nvoid YouTubeAppDock::IngestionStopped(const char *stream_id, streaming_mode_t mode)\n{\n\tDispatchYTEvent(INGESTION_STOPPED, stream_id, mode);\n}\n\nvoid YouTubeAppDock::showEvent(QShowEvent *)\n{\n\tif (!dockBrowser)\n\t\tUpdate();\n}\n\nvoid YouTubeAppDock::closeEvent(QCloseEvent *event)\n{\n\tBrowserDock::closeEvent(event);\n\tthis->SetWidget(nullptr);\n}\n\nvoid YouTubeAppDock::DispatchYTEvent(const char *event, const char *video_id, streaming_mode_t mode)\n{\n\tif (!dockBrowser)\n\t\treturn;\n\n\t// update channelId if empty:\n\tUpdateChannelId();\n\n\t// notify YouTube Live Streaming API:\n\tstd::string script;\n\tif (mode == YTSM_ACCOUNT) {\n\t\tscript = QString(R\"\"\"(\n\t\t\tif (window.location.hostname == 'studio.youtube.com') {\n\t\t\t\tlet event = {\n\t\t\t\t\ttype: '%1',\n\t\t\t\t\tchannelId: '%2',\n\t\t\t\t\tvideoId: '%3',\n\t\t\t\t};\n\t\t\t\tconsole.log(event);\n\t\t\t\tif (window.ytlsapi && window.ytlsapi.dispatchEvent)\n\t\t\t\t\twindow.ytlsapi.dispatchEvent(event);\n\t\t\t}\n\t\t)\"\"\")\n\t\t\t\t .arg(event)\n\t\t\t\t .arg(channelId)\n\t\t\t\t .arg(video_id)\n\t\t\t\t .toStdString();\n\t} else {\n\t\tconst char *stream_key = video_id;\n\t\tscript = QString(R\"\"\"(\n\t\t\tif (window.location.hostname == 'studio.youtube.com') {\n\t\t\t\tlet event = {\n\t\t\t\t\ttype: '%1',\n\t\t\t\t\tstreamKey: '%2',\n\t\t\t\t};\n\t\t\t\tconsole.log(event);\n\t\t\t\tif (window.ytlsapi && window.ytlsapi.dispatchEvent)\n\t\t\t\t\twindow.ytlsapi.dispatchEvent(event);\n\t\t\t}\n\t\t)\"\"\")\n\t\t\t\t .arg(event)\n\t\t\t\t .arg(stream_key)\n\t\t\t\t .toStdString();\n\t}\n\tdockBrowser->executeJavaScript(script);\n\n\t// in case of user still not logged in in dock panel, remember last event\n\tSetInitEvent(mode, event, video_id, channelId.toStdString().c_str());\n}\n\nvoid YouTubeAppDock::Update()\n{\n\tstd::string url = InitYTUserUrl();\n\n\tif (!dockBrowser) {\n\t\tCreateBrowserWidget(url);\n\t} else {\n\t\tdockBrowser->setURL(url);\n\t}\n\n\t// if streaming already run, let's notify YT about past event\n\tif (OBSBasic::Get()->StreamingActive()) {\n\t\tobs_service_t *service_obj = OBSBasic::Get()->GetService();\n\t\tOBSDataAutoRelease settings = obs_service_get_settings(service_obj);\n\t\tif (IsUserSignedIntoYT()) {\n\t\t\tchannelId.clear(); // renew channelId\n\t\t\tUpdateChannelId();\n\t\t\tconst char *broadcast_id = obs_data_get_string(settings, \"broadcast_id\");\n\t\t\tSetInitEvent(YTSM_ACCOUNT, INGESTION_STARTED, broadcast_id, channelId.toStdString().c_str());\n\t\t} else {\n\t\t\tconst char *stream_key = obs_data_get_string(settings, \"key\");\n\t\t\tSetInitEvent(YTSM_STREAM_KEY, INGESTION_STARTED, stream_key);\n\t\t}\n\t} else {\n\t\tSetInitEvent(IsUserSignedIntoYT() ? YTSM_ACCOUNT : YTSM_STREAM_KEY);\n\t}\n\n\tdockBrowser->reloadPage();\n}\n\nvoid YouTubeAppDock::UpdateChannelId()\n{\n\tif (channelId.isEmpty()) {\n\t\tYoutubeApiWrappers *apiYouTube = GetYTApi();\n\t\tif (apiYouTube) {\n\t\t\tChannelDescription channel_description;\n\t\t\tif (apiYouTube->GetChannelDescription(channel_description)) {\n\t\t\t\tchannelId = channel_description.id;\n\t\t\t} else {\n\t\t\t\tblog(LOG_ERROR, \"YT: AccountConnected() Failed \"\n\t\t\t\t\t\t\"to get channel id\");\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid YouTubeAppDock::ReloadChatDock()\n{\n\tif (IsUserSignedIntoYT()) {\n\t\tYoutubeApiWrappers *apiYouTube = GetYTApi();\n\t\tif (apiYouTube) {\n\t\t\tapiYouTube->ReloadChat();\n\t\t}\n\t}\n}\n\nvoid YouTubeAppDock::SetInitEvent(streaming_mode_t mode, const char *event, const char *video_id, const char *channelId)\n{\n\tconst std::string version = App()->GetVersionString();\n\n\tQString api_event;\n\tif (event) {\n\t\tif (mode == YTSM_ACCOUNT) {\n\t\t\tapi_event = QString(R\"\"\"(,\n\t\t\t\t\tinitEvent: {\n\t\t\t\t\t\ttype: '%1',\n\t\t\t\t\t\tchannelId: '%2',\n\t\t\t\t\t\tvideoId: '%3',\n\t\t\t\t\t}\n\t\t\t)\"\"\")\n\t\t\t\t\t    .arg(event)\n\t\t\t\t\t    .arg(channelId)\n\t\t\t\t\t    .arg(video_id);\n\t\t} else {\n\t\t\tapi_event = QString(R\"\"\"(,\n\t\t\t\t\tinitEvent: {\n\t\t\t\t\t\ttype: '%1',\n\t\t\t\t\t\tstreamKey: '%2',\n\t\t\t\t\t}\n\t\t\t)\"\"\")\n\t\t\t\t\t    .arg(event)\n\t\t\t\t\t    .arg(video_id);\n\t\t}\n\t}\n\n\tstd::string script = QString(R\"\"\"(\n\t\tlet obs_name = '%1';\n\t\tlet obs_version = '%2';\n\t\tlet client_mode = %3;\n\t\tif (window.location.hostname == 'studio.youtube.com') {\n\t\t\tconsole.log(\"name:\", obs_name);\n\t\t\tconsole.log(\"version:\", obs_version);\n\t\t\tconsole.log(\"initEvent:\", {\n\t\t\t\t\tinitClientMode: client_mode\n\t\t\t\t\t%4 });\n\t\t\tif (window.ytlsapi && window.ytlsapi.init)\n\t\t\t\twindow.ytlsapi.init(obs_name, obs_version, undefined, {\n\t\t\t\t\tinitClientMode: client_mode\n\t\t\t\t\t%4 });\n\t\t}\n\t)\"\"\")\n\t\t\t\t     .arg(\"OBS\")\n\t\t\t\t     .arg(version.c_str())\n\t\t\t\t     .arg(mode == YTSM_ACCOUNT ? \"'ACCOUNT'\" : \"'STREAM_KEY'\")\n\t\t\t\t     .arg(api_event)\n\t\t\t\t     .toStdString();\n\tdockBrowser->setStartupScript(script);\n}\n\nYoutubeApiWrappers *YouTubeAppDock::GetYTApi()\n{\n\tAuth *auth = OBSBasic::Get()->GetAuth();\n\tif (auth) {\n\t\tYoutubeApiWrappers *apiYouTube(dynamic_cast<YoutubeApiWrappers *>(auth));\n\t\tif (apiYouTube) {\n\t\t\treturn apiYouTube;\n\t\t} else {\n\t\t\tblog(LOG_ERROR, \"YT: GetYTApi() Failed to get YoutubeApiWrappers\");\n\t\t}\n\t} else {\n\t\tblog(LOG_ERROR, \"YT: GetYTApi() Failed to get Auth\");\n\t}\n\treturn nullptr;\n}\n\nvoid YouTubeAppDock::CleanupYouTubeUrls()\n{\n\tif (!cef_js_avail)\n\t\treturn;\n\n\tstatic constexpr const char *YOUTUBE_VIDEO_URL = \"://studio.youtube.com/video/\";\n\t// remove legacy YouTube Browser Docks (once)\n\n\tbool youtube_cleanup_done = config_get_bool(App()->GetUserConfig(), \"General\", \"YtDockCleanupDone\");\n\n\tif (youtube_cleanup_done)\n\t\treturn;\n\n\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"YtDockCleanupDone\", true);\n\n\tconst char *jsonStr = config_get_string(App()->GetUserConfig(), \"BasicWindow\", \"ExtraBrowserDocks\");\n\tif (!jsonStr)\n\t\treturn;\n\n\tjson array = json::parse(jsonStr);\n\tif (!array.is_array())\n\t\treturn;\n\n\tjson save_array;\n\tstd::string removedYTUrl;\n\n\tfor (json &item : array) {\n\t\tauto url = item[\"url\"].get<std::string>();\n\n\t\tif (url.find(YOUTUBE_VIDEO_URL) != std::string::npos) {\n\t\t\tblog(LOG_DEBUG, \"YT: found legacy url: %s\", url.c_str());\n\t\t\tremovedYTUrl += url;\n\t\t\tremovedYTUrl += \";\\n\";\n\t\t} else {\n\t\t\tsave_array.push_back(item);\n\t\t}\n\t}\n\n\tif (!removedYTUrl.empty()) {\n\t\tconst QString msg_title = QTStr(\"YouTube.DocksRemoval.Title\");\n\t\tconst QString msg_text = QTStr(\"YouTube.DocksRemoval.Text\").arg(QT_UTF8(removedYTUrl.c_str()));\n\t\tOBSMessageBox::warning(OBSBasic::Get(), msg_title, msg_text);\n\n\t\tstd::string output = save_array.dump();\n\t\tconfig_set_string(App()->GetUserConfig(), \"BasicWindow\", \"ExtraBrowserDocks\", output.c_str());\n\t}\n}\n"
  },
  {
    "path": "frontend/docks/YouTubeAppDock.hpp",
    "content": "#pragma once\n\n#include \"BrowserDock.hpp\"\n\n#include <QPointer>\n\nclass QAction;\nclass QCefWidget;\nclass YoutubeApiWrappers;\n\nclass YouTubeAppDock : public BrowserDock {\n\tQ_OBJECT\n\npublic:\n\tYouTubeAppDock(const QString &title);\n\n\tenum streaming_mode_t { YTSM_ACCOUNT, YTSM_STREAM_KEY };\n\n\tvoid AccountConnected();\n\tvoid AccountDisconnected();\n\tvoid Update();\n\n\tvoid BroadcastCreated(const char *stream_id);\n\tvoid BroadcastSelected(const char *stream_id);\n\tvoid IngestionStarted();\n\tvoid IngestionStopped();\n\n\tstatic bool IsYTServiceSelected();\n\tstatic YoutubeApiWrappers *GetYTApi();\n\tstatic void CleanupYouTubeUrls();\n\npublic slots:\n\tvoid SettingsUpdated(bool cleanup = false);\n\nprotected:\n\tvoid IngestionStarted(const char *stream_id, streaming_mode_t mode);\n\tvoid IngestionStopped(const char *stream_id, streaming_mode_t mode);\n\nprivate:\n\tstd::string InitYTUserUrl();\n\tvoid SetVisibleYTAppDockInMenu(bool visible);\n\tvoid AddYouTubeAppDock();\n\tvoid CreateBrowserWidget(const std::string &url);\n\tvirtual void showEvent(QShowEvent *event) override;\n\tvirtual void closeEvent(QCloseEvent *event) override;\n\tvoid DispatchYTEvent(const char *event, const char *video_id, streaming_mode_t mode);\n\tvoid UpdateChannelId();\n\tvoid ReloadChatDock();\n\tvoid SetInitEvent(streaming_mode_t mode, const char *event = nullptr, const char *video_id = nullptr,\n\t\t\t  const char *channelId = nullptr);\n\n\tQString channelId;\n\tQPointer<QCefWidget> dockBrowser;\n};\n"
  },
  {
    "path": "frontend/docks/YouTubeChatDock.cpp",
    "content": "#include \"YouTubeChatDock.hpp\"\n\n#include <docks/YouTubeAppDock.hpp>\n#include <utility/YoutubeApiWrappers.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QHBoxLayout>\n#include <QPushButton>\n\n#include \"moc_YouTubeChatDock.cpp\"\n\n#ifdef BROWSER_AVAILABLE\nvoid YoutubeChatDock::YoutubeCookieCheck()\n{\n\tQPointer<YoutubeChatDock> this_ = this;\n\tauto cb = [this_](bool currentlyLoggedIn) {\n\t\tbool previouslyLoggedIn = this_->isLoggedIn;\n\t\tthis_->isLoggedIn = currentlyLoggedIn;\n\t\tbool loginStateChanged = (currentlyLoggedIn && !previouslyLoggedIn) ||\n\t\t\t\t\t (!currentlyLoggedIn && previouslyLoggedIn);\n\t\tif (loginStateChanged) {\n\t\t\tOBSBasic *main = OBSBasic::Get();\n\t\t\tif (main->GetYouTubeAppDock() != nullptr) {\n\t\t\t\tQMetaObject::invokeMethod(main->GetYouTubeAppDock(), \"SettingsUpdated\",\n\t\t\t\t\t\t\t  Qt::QueuedConnection, Q_ARG(bool, !currentlyLoggedIn));\n\t\t\t}\n\t\t}\n\t};\n\tif (panel_cookies) {\n\t\tpanel_cookies->CheckForCookie(\"https://www.youtube.com\", \"SID\", cb);\n\t}\n}\n#endif\n"
  },
  {
    "path": "frontend/docks/YouTubeChatDock.hpp",
    "content": "#pragma once\n\n#ifdef BROWSER_AVAILABLE\n#include \"BrowserDock.hpp\"\n\nclass YoutubeChatDock : public BrowserDock {\n\tQ_OBJECT\n\nprivate:\n\tbool isLoggedIn;\n\npublic:\n\tYoutubeChatDock(const QString &title) : BrowserDock(title) {}\n\n\tinline void SetWidget(QCefWidget *widget_)\n\t{\n\t\tBrowserDock::SetWidget(widget_);\n\t\tQWidget::connect(cefWidget.get(), &QCefWidget::urlChanged, this, &YoutubeChatDock::YoutubeCookieCheck);\n\t}\nprivate slots:\n\tvoid YoutubeCookieCheck();\n};\n#endif\n"
  },
  {
    "path": "frontend/forms/AutoConfigFinishPage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AutoConfigFinishPage</class>\n <widget class=\"QWidget\" name=\"AutoConfigFinishPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\"/>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string notr=\"true\"/>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/AutoConfigStartPage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AutoConfigStartPage</class>\n <widget class=\"QWidget\" name=\"AutoConfigStartPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\"/>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QRadioButton\" name=\"prioritizeStreaming\">\n     <property name=\"text\">\n      <string>Basic.AutoConfig.StartPage.PrioritizeStreaming</string>\n     </property>\n     <property name=\"checked\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QRadioButton\" name=\"prioritizeRecording\">\n     <property name=\"text\">\n      <string>Basic.AutoConfig.StartPage.PrioritizeRecording</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer_2\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Expanding</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>40</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"label_2\">\n     <property name=\"font\">\n      <font>\n       <bold>true</bold>\n      </font>\n     </property>\n     <property name=\"text\">\n      <string>Basic.AutoConfig.Info</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer_3\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Fixed</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>5</width>\n       <height>5</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"font\">\n      <font>\n       <bold>true</bold>\n      </font>\n     </property>\n     <property name=\"text\">\n      <string>Basic.AutoConfig.RunAnytime</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Fixed</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/AutoConfigStreamPage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AutoConfigStreamPage</class>\n <widget class=\"QWidget\" name=\"AutoConfigStreamPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>726</width>\n    <height>407</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\"/>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <property name=\"spacing\">\n    <number>0</number>\n   </property>\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QFrame\" name=\"widget\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <layout class=\"QFormLayout\" name=\"topLayout\">\n      <property name=\"fieldGrowthPolicy\">\n       <enum>QFormLayout::ExpandingFieldsGrow</enum>\n      </property>\n      <property name=\"labelAlignment\">\n       <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>12</number>\n      </property>\n      <item row=\"0\" column=\"0\">\n       <spacer name=\"horizontalSpacer_6\">\n        <property name=\"orientation\">\n         <enum>Qt::Horizontal</enum>\n        </property>\n        <property name=\"sizeType\">\n         <enum>QSizePolicy::Fixed</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>87</width>\n          <height>0</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n      <item row=\"1\" column=\"0\">\n       <widget class=\"QLabel\" name=\"serviceLabel\">\n        <property name=\"text\">\n         <string>Basic.AutoConfig.StreamPage.Service</string>\n        </property>\n        <property name=\"buddy\">\n         <cstring>service</cstring>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"1\">\n       <widget class=\"QFrame\" name=\"serviceWidget\">\n        <layout class=\"QHBoxLayout\" name=\"serviceWidgetLayout\">\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QComboBox\" name=\"service\"/>\n         </item>\n         <item>\n          <widget class=\"UrlPushButton\" name=\"moreInfoButton\">\n           <property name=\"text\">\n            <string>Basic.AutoConfig.StreamPage.MoreInfo</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n      <item row=\"2\" column=\"1\">\n       <spacer name=\"verticalSpacer\">\n        <property name=\"orientation\">\n         <enum>Qt::Vertical</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>12</width>\n          <height>12</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QStackedWidget\" name=\"stackedWidget\">\n     <property name=\"currentIndex\">\n      <number>1</number>\n     </property>\n     <widget class=\"QWidget\" name=\"loginPage\">\n      <layout class=\"QFormLayout\" name=\"loginPageLayout\">\n       <property name=\"fieldGrowthPolicy\">\n        <enum>QFormLayout::ExpandingFieldsGrow</enum>\n       </property>\n       <property name=\"labelAlignment\">\n        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n       </property>\n       <item row=\"0\" column=\"0\">\n        <spacer name=\"horizontalSpacer_3\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Fixed</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>87</width>\n           <height>17</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item row=\"0\" column=\"1\">\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n         <item>\n          <widget class=\"QPushButton\" name=\"connectAccount\">\n           <property name=\"text\">\n            <string>Basic.AutoConfig.StreamPage.ConnectAccount</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer name=\"horizontalSpacer_4\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <spacer name=\"horizontalSpacer_7\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Fixed</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>87</width>\n           <height>17</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item row=\"1\" column=\"1\">\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n         <item>\n          <widget class=\"QPushButton\" name=\"useStreamKey\">\n           <property name=\"text\">\n            <string>Basic.AutoConfig.StreamPage.UseStreamKey</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <spacer name=\"horizontalSpacer_8\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"streamkeyPage\">\n      <layout class=\"QFormLayout\" name=\"streamkeyPageLayout\">\n       <property name=\"fieldGrowthPolicy\">\n        <enum>QFormLayout::ExpandingFieldsGrow</enum>\n       </property>\n       <property name=\"labelAlignment\">\n        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n       </property>\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"serverLabel\">\n         <property name=\"text\">\n          <string>Basic.AutoConfig.StreamPage.Server</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"0\" column=\"1\">\n        <widget class=\"QStackedWidget\" name=\"serverStackedWidget\">\n         <property name=\"currentIndex\">\n          <number>0</number>\n         </property>\n         <widget class=\"QWidget\" name=\"servicePage\">\n          <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n           <property name=\"leftMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"topMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"rightMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"bottomMargin\">\n            <number>0</number>\n           </property>\n           <item>\n            <widget class=\"QComboBox\" name=\"server\"/>\n           </item>\n          </layout>\n         </widget>\n         <widget class=\"QWidget\" name=\"customPage\">\n          <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n           <property name=\"leftMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"topMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"rightMargin\">\n            <number>0</number>\n           </property>\n           <property name=\"bottomMargin\">\n            <number>0</number>\n           </property>\n           <item>\n            <widget class=\"QLineEdit\" name=\"customServer\"/>\n           </item>\n          </layout>\n         </widget>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <widget class=\"QLabel\" name=\"streamKeyLabel\">\n         <property name=\"text\">\n          <string>Basic.AutoConfig.StreamPage.StreamKey</string>\n         </property>\n         <property name=\"openExternalLinks\">\n          <bool>true</bool>\n         </property>\n         <property name=\"buddy\">\n          <cstring>key</cstring>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"1\">\n        <widget class=\"QFrame\" name=\"streamKeyWidget\">\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n          <property name=\"leftMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"topMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"rightMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"bottomMargin\">\n           <number>0</number>\n          </property>\n          <item>\n           <widget class=\"QLineEdit\" name=\"key\">\n            <property name=\"inputMask\">\n             <string notr=\"true\"/>\n            </property>\n            <property name=\"text\">\n             <string notr=\"true\"/>\n            </property>\n            <property name=\"echoMode\">\n             <enum>QLineEdit::Password</enum>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"show\">\n            <property name=\"text\">\n             <string>Show</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"UrlPushButton\" name=\"streamKeyButton\">\n            <property name=\"text\">\n             <string>Basic.AutoConfig.StreamPage.GetStreamKey</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"1\">\n        <widget class=\"QPushButton\" name=\"connectAccount2\">\n         <property name=\"cursor\">\n          <cursorShape>PointingHandCursor</cursorShape>\n         </property>\n         <property name=\"text\">\n          <string>Basic.AutoConfig.StreamPage.ConnectAccount</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"0\">\n        <widget class=\"QLabel\" name=\"connectedAccountLabel\">\n         <property name=\"text\">\n          <string>Basic.AutoConfig.StreamPage.ConnectedAccount</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"1\">\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\">\n         <property name=\"leftMargin\">\n          <number>7</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>7</number>\n         </property>\n         <item>\n          <widget class=\"QLabel\" name=\"connectedAccountText\">\n           <property name=\"font\">\n            <font>\n             <bold>true</bold>\n            </font>\n           </property>\n           <property name=\"text\">\n            <string>Auth.LoadingChannel.Title</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"disconnectAccount\">\n           <property name=\"text\">\n            <string>Basic.AutoConfig.StreamPage.DisconnectAccount</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item row=\"4\" column=\"0\">\n        <spacer name=\"horizontalSpacer\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>0</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item row=\"4\" column=\"1\">\n        <widget class=\"QPushButton\" name=\"useStreamKeyAdv\">\n         <property name=\"text\">\n          <string>Basic.AutoConfig.StreamPage.UseStreamKeyAdvanced</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"5\" column=\"0\">\n        <widget class=\"QLabel\" name=\"bitrateLabel\">\n         <property name=\"text\">\n          <string>Basic.Settings.Output.VideoBitrate</string>\n         </property>\n         <property name=\"buddy\">\n          <cstring>bitrate</cstring>\n         </property>\n        </widget>\n       </item>\n       <item row=\"5\" column=\"1\">\n        <widget class=\"QSpinBox\" name=\"bitrate\">\n         <property name=\"suffix\">\n          <string notr=\"true\"/>\n         </property>\n         <property name=\"minimum\">\n          <number>500</number>\n         </property>\n         <property name=\"maximum\">\n          <number>51000</number>\n         </property>\n         <property name=\"value\">\n          <number>2500</number>\n         </property>\n        </widget>\n       </item>\n       <item row=\"6\" column=\"1\">\n        <widget class=\"QCheckBox\" name=\"preferHardware\">\n         <property name=\"toolTip\">\n          <string>Basic.AutoConfig.StreamPage.PreferHardwareEncoding.ToolTip</string>\n         </property>\n         <property name=\"text\">\n          <string>Basic.AutoConfig.StreamPage.PreferHardwareEncoding</string>\n         </property>\n         <property name=\"checked\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item row=\"7\" column=\"1\">\n        <widget class=\"QCheckBox\" name=\"doBandwidthTest\">\n         <property name=\"text\">\n          <string>Basic.AutoConfig.StreamPage.PerformBandwidthTest</string>\n         </property>\n         <property name=\"checked\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item row=\"8\" column=\"1\">\n        <widget class=\"QCheckBox\" name=\"useMultitrackVideo\">\n         <property name=\"text\">\n          <string>Basic.AutoConfig.StreamPage.UseMultitrackVideo</string>\n         </property>\n         <property name=\"checked\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item row=\"9\" column=\"1\">\n        <widget class=\"QLabel\" name=\"multitrackVideoInfo\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Minimum\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"text\">\n          <string>MultitrackVideo.Info</string>\n         </property>\n         <property name=\"textFormat\">\n          <enum>Qt::RichText</enum>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>true</bool>\n         </property>\n         <property name=\"openExternalLinks\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item row=\"10\" column=\"1\">\n        <spacer name=\"verticalSpacer_2\">\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>6</width>\n           <height>6</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item row=\"11\" column=\"1\">\n        <widget class=\"QGroupBox\" name=\"region\">\n         <property name=\"title\">\n          <string>BandwidthTest.Region</string>\n         </property>\n         <layout class=\"QGridLayout\" name=\"gridLayout\">\n          <item row=\"2\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"regionAsia\">\n            <property name=\"text\">\n             <string>BandwidthTest.Region.Asia</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QCheckBox\" name=\"regionUS\">\n            <property name=\"text\">\n             <string>BandwidthTest.Region.US</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"regionEU\">\n            <property name=\"text\">\n             <string>BandwidthTest.Region.EU</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"regionOther\">\n            <property name=\"text\">\n             <string>BandwidthTest.Region.Other</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>UrlPushButton</class>\n   <extends>QPushButton</extends>\n   <header>components/UrlPushButton.hpp</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>connectAccount2</sender>\n   <signal>clicked()</signal>\n   <receiver>connectAccount</receiver>\n   <slot>click()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>382</x>\n     <y>279</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>114</x>\n     <y>82</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/forms/AutoConfigTestPage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AutoConfigTestPage</class>\n <widget class=\"QWidget\" name=\"AutoConfigTestPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\"/>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QStackedWidget\" name=\"stackedWidget\">\n     <property name=\"currentIndex\">\n      <number>2</number>\n     </property>\n     <widget class=\"QWidget\" name=\"testPage\">\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <widget class=\"QLabel\" name=\"progressLabel\">\n         <property name=\"text\">\n          <string notr=\"true\"/>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"subProgressLabel\">\n         <property name=\"text\">\n          <string notr=\"true\"/>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QProgressBar\" name=\"progressBar\">\n         <property name=\"value\">\n          <number>24</number>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"verticalSpacer\">\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>40</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"finishPage\">\n      <layout class=\"QVBoxLayout\" name=\"finishPageLayout\">\n       <item>\n        <widget class=\"QLabel\" name=\"finalResultLabel\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"text\">\n          <string>Basic.AutoConfig.TestPage.Result.Header</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"finalResultLabel_2\">\n         <property name=\"text\">\n          <string>Basic.AutoConfig.TestPage.Result.Footer</string>\n         </property>\n         <property name=\"alignment\">\n          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"verticalSpacer_2\">\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>40</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"errorPage\">\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n       <item>\n        <widget class=\"QLabel\" name=\"errorLabel\">\n         <property name=\"text\">\n          <string notr=\"true\"/>\n         </property>\n         <property name=\"alignment\">\n          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/AutoConfigVideoPage.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AutoConfigVideoPage</class>\n <widget class=\"QWidget\" name=\"AutoConfigVideoPage\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>470</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\"/>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout_2\">\n     <property name=\"fieldGrowthPolicy\">\n      <enum>QFormLayout::ExpandingFieldsGrow</enum>\n     </property>\n     <property name=\"labelAlignment\">\n      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n     </property>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Basic.Settings.Video.BaseResolution</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>canvasRes</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"canvasRes\"/>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_3\">\n       <property name=\"text\">\n        <string>Basic.Settings.Video.FPS</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>fps</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"fps\"/>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Fixed</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>87</width>\n         <height>17</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"warningLabel\">\n     <property name=\"text\">\n      <string>Basic.AutoConfig.VideoPage.CanvasExplanation</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n     <property name=\"class\" stdset=\"0\">\n      <string notr=\"true\">text-warning</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/ColorSelect.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ColorSelect</class>\n <widget class=\"QWidget\" name=\"ColorSelect\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>126</width>\n    <height>70</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Form</string>\n  </property>\n  <property name=\"styleSheet\">\n   <string notr=\"true\">QPushButton { border: 1px solid black; margin: 0; padding: 0; }</string>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n   <item row=\"0\" column=\"0\">\n    <layout class=\"QGridLayout\" name=\"gridLayout\">\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QPushButton\" name=\"preset1\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"styleSheet\">\n        <string notr=\"true\"/>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"preset\" stdset=\"0\">\n        <string>1</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QPushButton\" name=\"preset2\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"preset\" stdset=\"0\">\n        <string>2</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"2\">\n      <widget class=\"QPushButton\" name=\"preset3\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"preset\" stdset=\"0\">\n        <string>3</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"3\">\n      <widget class=\"QPushButton\" name=\"preset4\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"preset\" stdset=\"0\">\n        <string>4</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QPushButton\" name=\"preset5\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"preset\" stdset=\"0\">\n        <string>5</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QPushButton\" name=\"preset6\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"preset\" stdset=\"0\">\n        <string>6</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"2\">\n      <widget class=\"QPushButton\" name=\"preset7\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"preset\" stdset=\"0\">\n        <string>7</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"3\">\n      <widget class=\"QPushButton\" name=\"preset8\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>22</width>\n         <height>22</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"preset8\" stdset=\"0\">\n        <string/>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources>\n  <include location=\"obs.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/LogUploadDialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>LogUploadDialog</class>\n <widget class=\"QDialog\" name=\"LogUploadDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>600</width>\n    <height>140</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>LogUploadDialog.Title</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_LogUploadDialog\">\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetFixedSize</enum>\n   </property>\n   <item>\n    <widget class=\"QStackedWidget\" name=\"stackedWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"confirmUploadPage\">\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_confirmUploadPage\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_privacyNotice\">\n         <item>\n          <widget class=\"QLabel\" name=\"privacyNotice\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"text\">\n            <string>LogUploadDialog.Labels.PrivacyNotice</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n           <property name=\"openExternalLinks\">\n            <bool>true</bool>\n           </property>\n           <property name=\"textInteractionFlags\">\n            <set>Qt::TextBrowserInteraction</set>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_uploadProgress\">\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QLabel\" name=\"uploadProgress\">\n           <property name=\"enabled\">\n            <bool>true</bool>\n           </property>\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"text\">\n            <string/>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <spacer name=\"horizontalSpacer\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"confirmUploadButton\">\n           <property name=\"text\">\n            <string>LogUploadDialog.Buttons.ConfirmUpload</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"closeButton\">\n           <property name=\"text\">\n            <string>Cancel</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"uploadSucceededPage\">\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_uploadSucceededPage\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QLabel\" name=\"description\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"text\">\n            <string>LogUploadDialog.Labels.Description.AppLog</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>false</bool>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n         <item>\n          <widget class=\"QLineEdit\" name=\"urlEdit\">\n           <property name=\"text\">\n            <string notr=\"true\"/>\n           </property>\n           <property name=\"readOnly\">\n            <bool>true</bool>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"copyURL\">\n           <property name=\"text\">\n            <string>LogUploadDialog.Buttons.CopyURL</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"analyzeURL\">\n           <property name=\"text\">\n            <string>LogUploadDialog.Buttons.AnalyzeURL</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n         <property name=\"standardButtons\">\n          <set>QDialogButtonBox::Close</set>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"uploadFailedPage\">\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_uploadFailedPage\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n       <item>\n        <widget class=\"QLabel\" name=\"uploadErrorMessage\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"text\">\n          <string>LogUploadDialog.Labels.UploadFailMessage</string>\n         </property>\n         <property name=\"alignment\">\n          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n         <item>\n          <spacer name=\"horizontalSpacer_2\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>10</width>\n             <height>2</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"retryButton\">\n           <property name=\"text\">\n            <string>LogUploadDialog.Buttons.RetryButton</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"retryCloseButton\">\n           <property name=\"text\">\n            <string>Cancel</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>LogUploadDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>LogUploadDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSAbout.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSAbout</class>\n <widget class=\"QWidget\" name=\"OBSAbout\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>840</width>\n    <height>519</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>About</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <property name=\"spacing\">\n      <number>30</number>\n     </property>\n     <property name=\"leftMargin\">\n      <number>30</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>10</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>6</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>6</number>\n     </property>\n     <item>\n      <widget class=\"QLabel\" name=\"icon\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>256</width>\n         <height>256</height>\n        </size>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>256</width>\n         <height>256</height>\n        </size>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"pixmap\">\n        <pixmap>:res/images/obs.png</pixmap>\n       </property>\n       <property name=\"scaledContents\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n       <property name=\"spacing\">\n        <number>0</number>\n       </property>\n       <item>\n        <widget class=\"QLabel\" name=\"name\">\n         <property name=\"text\">\n          <string notr=\"true\">OBS Studio</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"version\">\n         <property name=\"text\">\n          <string notr=\"true\">Version</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"verticalSpacer_2\">\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Fixed</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"info\">\n         <property name=\"text\">\n          <string>About.Info</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"verticalSpacer_3\">\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Fixed</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"contribute\">\n         <property name=\"text\">\n          <string notr=\"true\">Contribute</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"donate\">\n         <property name=\"text\">\n          <string notr=\"true\">Donate</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"getInvolved\">\n         <property name=\"text\">\n          <string notr=\"true\">Get Involved</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"verticalSpacer\">\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeType\">\n          <enum>QSizePolicy::Fixed</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>20</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <widget class=\"QTextBrowser\" name=\"textBrowser\">\n         <property name=\"openExternalLinks\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"widget\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>60</height>\n      </size>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n      <property name=\"spacing\">\n       <number>0</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"ClickableLabel\" name=\"about\">\n        <property name=\"text\">\n         <string>About</string>\n        </property>\n        <property name=\"alignment\">\n         <set>Qt::AlignCenter</set>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"ClickableLabel\" name=\"authors\">\n        <property name=\"text\">\n         <string>Authors</string>\n        </property>\n        <property name=\"alignment\">\n         <set>Qt::AlignCenter</set>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"ClickableLabel\" name=\"license\">\n        <property name=\"text\">\n         <string>License</string>\n        </property>\n        <property name=\"alignment\">\n         <set>Qt::AlignCenter</set>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ClickableLabel</class>\n   <extends>QLabel</extends>\n   <header>components/ClickableLabel.hpp</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSAdvAudio.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSAdvAudio</class>\n <widget class=\"QDialog\" name=\"OBSAdvAudio\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1100</width>\n    <height>340</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"contextMenuPolicy\">\n   <enum>Qt::CustomContextMenu</enum>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Basic.AdvAudio</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout\">\n   <property name=\"leftMargin\">\n    <number>11</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>11</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>11</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>11</number>\n   </property>\n   <property name=\"verticalSpacing\">\n    <number>11</number>\n   </property>\n   <item row=\"1\" column=\"0\">\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <property name=\"spacing\">\n      <number>7</number>\n     </property>\n     <item>\n      <widget class=\"QCheckBox\" name=\"activeOnly\">\n       <property name=\"text\">\n        <string>Basic.AdvAudio.ActiveOnly</string>\n       </property>\n       <property name=\"checked\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"closeButton\">\n       <property name=\"text\">\n        <string>Close</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"0\" column=\"0\">\n    <widget class=\"QScrollArea\" name=\"scrollArea\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"sizeAdjustPolicy\">\n      <enum>QAbstractScrollArea::AdjustToContents</enum>\n     </property>\n     <property name=\"widgetResizable\">\n      <bool>true</bool>\n     </property>\n     <widget class=\"QWidget\" name=\"scrollAreaWidgetContents\">\n      <property name=\"geometry\">\n       <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>1157</width>\n        <height>262</height>\n       </rect>\n      </property>\n      <layout class=\"QGridLayout\" name=\"gridLayout_3\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"spacing\">\n        <number>0</number>\n       </property>\n       <item row=\"0\" column=\"0\">\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n         <property name=\"spacing\">\n          <number>0</number>\n         </property>\n         <property name=\"leftMargin\">\n          <number>4</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>4</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>4</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>4</number>\n         </property>\n         <item>\n          <layout class=\"QGridLayout\" name=\"mainLayout\">\n           <property name=\"verticalSpacing\">\n            <number>4</number>\n           </property>\n           <item row=\"0\" column=\"5\">\n            <widget class=\"QLabel\" name=\"label_2\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>Basic.AdvAudio.Balance</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"2\">\n            <widget class=\"QLabel\" name=\"label_7\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>Basic.Stats.Status</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"4\">\n            <widget class=\"QLabel\" name=\"label_3\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>Basic.AdvAudio.Mono</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"8\">\n            <widget class=\"QLabel\" name=\"label_5\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>Basic.AdvAudio.AudioTracks</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"1\">\n            <widget class=\"QLabel\" name=\"label\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>Basic.AdvAudio.Name</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"6\">\n            <widget class=\"QLabel\" name=\"label_4\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>Basic.AdvAudio.SyncOffset</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"7\">\n            <widget class=\"QLabel\" name=\"label_6\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>Basic.AdvAudio.Monitoring</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"3\">\n            <widget class=\"QWidget\" name=\"widget\" native=\"true\">\n             <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n              <property name=\"leftMargin\">\n               <number>0</number>\n              </property>\n              <property name=\"topMargin\">\n               <number>0</number>\n              </property>\n              <property name=\"rightMargin\">\n               <number>0</number>\n              </property>\n              <property name=\"bottomMargin\">\n               <number>0</number>\n              </property>\n              <property name=\"spacing\">\n               <number>0</number>\n              </property>\n              <item row=\"0\" column=\"0\">\n               <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n                <property name=\"spacing\">\n                 <number>2</number>\n                </property>\n                <item>\n                 <widget class=\"QLabel\" name=\"label_9\">\n                  <property name=\"font\">\n                   <font>\n                    <bold>true</bold>\n                   </font>\n                  </property>\n                  <property name=\"text\">\n                   <string>Basic.AdvAudio.Volume</string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QCheckBox\" name=\"usePercent\">\n                  <property name=\"font\">\n                   <font>\n                    <bold>true</bold>\n                   </font>\n                  </property>\n                  <property name=\"text\">\n                   <string>%</string>\n                  </property>\n                 </widget>\n                </item>\n                <item>\n                 <spacer name=\"horizontalSpacer_3\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Horizontal</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Minimum</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>0</width>\n                    <height>0</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n               </layout>\n              </item>\n             </layout>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_8\">\n             <property name=\"text\">\n              <string/>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <spacer name=\"verticalSpacer\">\n           <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>20</width>\n             <height>40</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>closeButton</sender>\n   <signal>clicked()</signal>\n   <receiver>OBSAdvAudio</receiver>\n   <slot>close()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>1286</x>\n     <y>474</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>672</x>\n     <y>250</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSBasic.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <author>Lain</author>\n <class>OBSBasic</class>\n <widget class=\"QMainWindow\" name=\"OBSBasic\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1280</width>\n    <height>960</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>0</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>.MainWindow</string>\n  </property>\n  <property name=\"windowIcon\">\n   <iconset resource=\"obs.qrc\">\n    <normaloff>:/res/images/obs.png</normaloff>:/res/images/obs.png</iconset>\n  </property>\n  <property name=\"styleSheet\">\n   <string notr=\"true\"/>\n  </property>\n  <property name=\"dockOptions\">\n   <set>QMainWindow::AllowNestedDocks|QMainWindow::AllowTabbedDocks</set>\n  </property>\n  <widget class=\"QWidget\" name=\"centralwidget\">\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"spacing\">\n     <number>0</number>\n    </property>\n    <property name=\"leftMargin\">\n     <number>4</number>\n    </property>\n    <property name=\"topMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>4</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"QWidget\" name=\"canvasEditor\" native=\"true\">\n      <layout class=\"QHBoxLayout\" name=\"previewLayout\">\n       <property name=\"spacing\">\n        <number>2</number>\n       </property>\n       <property name=\"leftMargin\">\n        <number>1</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>1</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>1</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>1</number>\n       </property>\n       <item>\n        <widget class=\"QFrame\" name=\"previewDisabledWidget\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <layout class=\"QVBoxLayout\" name=\"verticalLayout_7\">\n          <property name=\"spacing\">\n           <number>0</number>\n          </property>\n          <property name=\"leftMargin\">\n           <number>1</number>\n          </property>\n          <property name=\"topMargin\">\n           <number>1</number>\n          </property>\n          <property name=\"rightMargin\">\n           <number>1</number>\n          </property>\n          <property name=\"bottomMargin\">\n           <number>1</number>\n          </property>\n          <item>\n           <spacer name=\"verticalSpacer_2\">\n            <property name=\"orientation\">\n             <enum>Qt::Vertical</enum>\n            </property>\n            <property name=\"sizeHint\" stdset=\"0\">\n             <size>\n              <width>20</width>\n              <height>40</height>\n             </size>\n            </property>\n           </spacer>\n          </item>\n          <item>\n           <widget class=\"QLabel\" name=\"label\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"text\">\n             <string>Basic.Main.PreviewDisabled</string>\n            </property>\n            <property name=\"alignment\">\n             <set>Qt::AlignCenter</set>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n            <item>\n             <spacer name=\"horizontalSpacer_2\">\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>40</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n            <item>\n             <widget class=\"QPushButton\" name=\"enablePreviewButton\">\n              <property name=\"text\">\n               <string>Basic.Main.PreviewConextMenu.Enable</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <spacer name=\"horizontalSpacer_3\">\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>40</width>\n                <height>20</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n           </layout>\n          </item>\n          <item>\n           <spacer name=\"verticalSpacer_3\">\n            <property name=\"orientation\">\n             <enum>Qt::Vertical</enum>\n            </property>\n            <property name=\"sizeHint\" stdset=\"0\">\n             <size>\n              <width>20</width>\n              <height>40</height>\n             </size>\n            </property>\n           </spacer>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QWidget\" name=\"previewContainer\" native=\"true\">\n         <layout class=\"QVBoxLayout\" name=\"previewTextLayout\">\n          <property name=\"spacing\">\n           <number>0</number>\n          </property>\n          <property name=\"leftMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"topMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"rightMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"bottomMargin\">\n           <number>0</number>\n          </property>\n          <item>\n           <widget class=\"QLabel\" name=\"previewLabel\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Ignored\" vsizetype=\"Preferred\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"text\">\n             <string>StudioMode.PreviewSceneName</string>\n            </property>\n            <property name=\"alignment\">\n             <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>\n            </property>\n            <property name=\"class\" stdset=\"0\">\n             <string>label-preview-title</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <layout class=\"QGridLayout\" name=\"gridLayout\">\n            <property name=\"spacing\">\n             <number>0</number>\n            </property>\n            <item row=\"1\" column=\"0\" colspan=\"2\">\n             <widget class=\"OBSBasicPreview\" name=\"preview\" native=\"true\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"minimumSize\">\n               <size>\n                <width>32</width>\n                <height>32</height>\n               </size>\n              </property>\n              <property name=\"focusPolicy\">\n               <enum>Qt::ClickFocus</enum>\n              </property>\n              <property name=\"contextMenuPolicy\">\n               <enum>Qt::CustomContextMenu</enum>\n              </property>\n              <addaction name=\"actionRemoveSource\"/>\n             </widget>\n            </item>\n            <item row=\"2\" column=\"1\">\n             <widget class=\"QWidget\" name=\"previewXContainer\" native=\"true\">\n              <layout class=\"QHBoxLayout\" name=\"horizontalLayout_7\">\n               <property name=\"spacing\">\n                <number>0</number>\n               </property>\n               <property name=\"leftMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"topMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"rightMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"bottomMargin\">\n                <number>0</number>\n               </property>\n               <item>\n                <widget class=\"QPushButton\" name=\"previewZoomOutButton\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"minimumSize\">\n                  <size>\n                   <width>20</width>\n                   <height>16</height>\n                  </size>\n                 </property>\n                 <property name=\"maximumSize\">\n                  <size>\n                   <width>20</width>\n                   <height>16</height>\n                  </size>\n                 </property>\n                 <property name=\"toolTip\">\n                  <string>Basic.MainMenu.Edit.Scale.ZoomOut</string>\n                 </property>\n                 <property name=\"accessibleName\">\n                  <string>Basic.MainMenu.Edit.Scale.ZoomOut</string>\n                 </property>\n                 <property name=\"text\">\n                  <string/>\n                 </property>\n                 <property name=\"icon\">\n                  <iconset resource=\"obs.qrc\">\n                   <normaloff>:/res/images/minus.svg</normaloff>:/res/images/minus.svg</iconset>\n                 </property>\n                 <property name=\"iconSize\">\n                  <size>\n                   <width>8</width>\n                   <height>8</height>\n                  </size>\n                 </property>\n                 <property name=\"toolButton\" stdset=\"0\">\n                  <bool>true</bool>\n                 </property>\n                 <property name=\"class\" stdset=\"0\">\n                  <string>icon-minus</string>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"OBSPreviewScalingLabel\" name=\"previewScalePercent\">\n                 <property name=\"text\">\n                  <string>100%</string>\n                 </property>\n                 <property name=\"alignment\">\n                  <set>Qt::AlignCenter</set>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"QPushButton\" name=\"previewZoomInButton\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"minimumSize\">\n                  <size>\n                   <width>20</width>\n                   <height>16</height>\n                  </size>\n                 </property>\n                 <property name=\"maximumSize\">\n                  <size>\n                   <width>20</width>\n                   <height>16</height>\n                  </size>\n                 </property>\n                 <property name=\"toolTip\">\n                  <string>Basic.MainMenu.Edit.Scale.ZoomIn</string>\n                 </property>\n                 <property name=\"accessibleName\">\n                  <string>Basic.MainMenu.Edit.Scale.ZoomIn</string>\n                 </property>\n                 <property name=\"text\">\n                  <string/>\n                 </property>\n                 <property name=\"icon\">\n                  <iconset resource=\"obs.qrc\">\n                   <normaloff>:/res/images/plus.svg</normaloff>:/res/images/plus.svg</iconset>\n                 </property>\n                 <property name=\"iconSize\">\n                  <size>\n                   <width>8</width>\n                   <height>8</height>\n                  </size>\n                 </property>\n                 <property name=\"toolButton\" stdset=\"0\">\n                  <bool>true</bool>\n                 </property>\n                 <property name=\"class\" stdset=\"0\">\n                  <string>icon-plus</string>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"OBSPreviewScalingComboBox\" name=\"previewScalingMode\">\n                 <item>\n                  <property name=\"text\">\n                   <string>Basic.MainMenu.Edit.Scale.Window</string>\n                  </property>\n                 </item>\n                 <item>\n                  <property name=\"text\">\n                   <string>Basic.MainMenu.Edit.Scale.Canvas</string>\n                  </property>\n                 </item>\n                </widget>\n               </item>\n               <item>\n                <spacer name=\"horizontalSpacer_5\">\n                 <property name=\"orientation\">\n                  <enum>Qt::Horizontal</enum>\n                 </property>\n                 <property name=\"sizeType\">\n                  <enum>QSizePolicy::Fixed</enum>\n                 </property>\n                 <property name=\"sizeHint\" stdset=\"0\">\n                  <size>\n                   <width>4</width>\n                   <height>0</height>\n                  </size>\n                 </property>\n                </spacer>\n               </item>\n               <item>\n                <widget class=\"QScrollBar\" name=\"previewXScrollBar\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"minimum\">\n                  <number>-200</number>\n                 </property>\n                 <property name=\"maximum\">\n                  <number>200</number>\n                 </property>\n                 <property name=\"singleStep\">\n                  <number>10</number>\n                 </property>\n                 <property name=\"pageStep\">\n                  <number>100</number>\n                 </property>\n                 <property name=\"orientation\">\n                  <enum>Qt::Horizontal</enum>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </widget>\n            </item>\n            <item row=\"1\" column=\"2\">\n             <widget class=\"QScrollBar\" name=\"previewYScrollBar\">\n              <property name=\"orientation\">\n               <enum>Qt::Vertical</enum>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </item>\n         </layout>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </item>\n    <item>\n     <widget class=\"QFrame\" name=\"contextContainer\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n        <horstretch>0</horstretch>\n        <verstretch>0</verstretch>\n       </sizepolicy>\n      </property>\n      <property name=\"minimumSize\">\n       <size>\n        <width>0</width>\n        <height>0</height>\n       </size>\n      </property>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout9\">\n       <property name=\"spacing\">\n        <number>6</number>\n       </property>\n       <property name=\"sizeConstraint\">\n        <enum>QLayout::SetDefaultConstraint</enum>\n       </property>\n       <property name=\"leftMargin\">\n        <number>6</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>4</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>6</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>4</number>\n       </property>\n       <item>\n        <widget class=\"QFrame\" name=\"contextSubContainer\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"minimumSize\">\n          <size>\n           <width>0</width>\n           <height>0</height>\n          </size>\n         </property>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n          <property name=\"spacing\">\n           <number>6</number>\n          </property>\n          <property name=\"leftMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"topMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"rightMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"bottomMargin\">\n           <number>0</number>\n          </property>\n          <item>\n           <widget class=\"QLabel\" name=\"contextSourceIcon\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"minimumSize\">\n             <size>\n              <width>22</width>\n              <height>22</height>\n             </size>\n            </property>\n            <property name=\"maximumSize\">\n             <size>\n              <width>22</width>\n              <height>22</height>\n             </size>\n            </property>\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QLabel\" name=\"contextSourceLabel\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"minimumSize\">\n             <size>\n              <width>160</width>\n              <height>22</height>\n             </size>\n            </property>\n            <property name=\"maximumSize\">\n             <size>\n              <width>160</width>\n              <height>22</height>\n             </size>\n            </property>\n            <property name=\"text\">\n             <string>TextLabel</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QLabel\" name=\"contextSourceIconSpacer\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"minimumSize\">\n             <size>\n              <width>22</width>\n              <height>22</height>\n             </size>\n            </property>\n            <property name=\"maximumSize\">\n             <size>\n              <width>22</width>\n              <height>22</height>\n             </size>\n            </property>\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"Line\" name=\"line_3\">\n            <property name=\"orientation\">\n             <enum>Qt::Vertical</enum>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"sourcePropertiesButton\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"minimumSize\">\n             <size>\n              <width>0</width>\n              <height>22</height>\n             </size>\n            </property>\n            <property name=\"toolTip\">\n             <string>SourceProperties</string>\n            </property>\n            <property name=\"text\">\n             <string>Properties</string>\n            </property>\n            <property name=\"icon\">\n             <iconset resource=\"obs.qrc\">\n              <normaloff>:/settings/images/settings/general.svg</normaloff>:/settings/images/settings/general.svg</iconset>\n            </property>\n            <property name=\"flat\">\n             <bool>false</bool>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"sourceFiltersButton\">\n            <property name=\"minimumSize\">\n             <size>\n              <width>0</width>\n              <height>22</height>\n             </size>\n            </property>\n            <property name=\"toolTip\">\n             <string>SourceFilters</string>\n            </property>\n            <property name=\"text\">\n             <string>Filters</string>\n            </property>\n            <property name=\"icon\">\n             <iconset resource=\"obs.qrc\">\n              <normaloff>:/res/images/filter.svg</normaloff>:/res/images/filter.svg</iconset>\n            </property>\n            <property name=\"flat\">\n             <bool>false</bool>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"sourceInteractButton\">\n            <property name=\"minimumSize\">\n             <size>\n              <width>0</width>\n              <height>22</height>\n             </size>\n            </property>\n            <property name=\"toolTip\">\n             <string>Interact</string>\n            </property>\n            <property name=\"text\">\n             <string>Interact</string>\n            </property>\n            <property name=\"icon\">\n             <iconset resource=\"obs.qrc\">\n              <normaloff>:/res/images/interact.svg</normaloff>:/res/images/interact.svg</iconset>\n            </property>\n            <property name=\"flat\">\n             <bool>false</bool>\n            </property>\n            <property name=\"class\" stdset=\"0\">\n             <string notr=\"true\">icon-touch</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"Line\" name=\"line\">\n            <property name=\"orientation\">\n             <enum>Qt::Vertical</enum>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QFrame\" name=\"emptySpace\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Minimum\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"minimumSize\">\n          <size>\n           <width>0</width>\n           <height>22</height>\n          </size>\n         </property>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\">\n          <property name=\"spacing\">\n           <number>0</number>\n          </property>\n          <property name=\"leftMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"topMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"rightMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"bottomMargin\">\n           <number>0</number>\n          </property>\n         </layout>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n  <widget class=\"QMenuBar\" name=\"menubar\">\n   <property name=\"geometry\">\n    <rect>\n     <x>0</x>\n     <y>0</y>\n     <width>1280</width>\n     <height>22</height>\n    </rect>\n   </property>\n   <widget class=\"QMenu\" name=\"menu_File\">\n    <property name=\"title\">\n     <string>Basic.MainMenu.File</string>\n    </property>\n    <addaction name=\"actionShow_Recordings\"/>\n    <addaction name=\"actionRemux\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"action_Settings\"/>\n    <addaction name=\"actionShowSettingsFolder\"/>\n    <addaction name=\"actionShowProfileFolder\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionE_xit\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuBasic_MainMenu_Help\">\n    <property name=\"title\">\n     <string>Basic.MainMenu.Help</string>\n    </property>\n    <widget class=\"QMenu\" name=\"menuLogFiles\">\n     <property name=\"title\">\n      <string>Basic.MainMenu.Help.Logs</string>\n     </property>\n     <addaction name=\"actionShowLogs\"/>\n     <addaction name=\"actionUploadCurrentLog\"/>\n     <addaction name=\"actionUploadLastLog\"/>\n     <addaction name=\"actionViewCurrentLog\"/>\n    </widget>\n    <widget class=\"QMenu\" name=\"menuCrashLogs\">\n     <property name=\"title\">\n      <string>Basic.MainMenu.Help.CrashLogs</string>\n     </property>\n     <addaction name=\"actionShowCrashLogs\"/>\n     <addaction name=\"actionUploadLastCrashLog\"/>\n    </widget>\n    <addaction name=\"actionHelpPortal\"/>\n    <addaction name=\"actionWebsite\"/>\n    <addaction name=\"actionDiscord\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"menuLogFiles\"/>\n    <addaction name=\"menuCrashLogs\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionRepair\"/>\n    <addaction name=\"actionCheckForUpdates\"/>\n    <addaction name=\"actionRestartSafe\"/>\n    <addaction name=\"actionShowMacPermissions\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionShowWhatsNew\"/>\n    <addaction name=\"actionReleaseNotes\"/>\n    <addaction name=\"actionShowAbout\"/>\n    <addaction name=\"separator\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuBasic_MainMenu_Edit\">\n    <property name=\"title\">\n     <string>Basic.MainMenu.Edit</string>\n    </property>\n    <widget class=\"QMenu\" name=\"transformMenu\">\n     <property name=\"title\">\n      <string>Basic.MainMenu.Edit.Transform</string>\n     </property>\n     <addaction name=\"actionEditTransform\"/>\n     <addaction name=\"actionCopyTransform\"/>\n     <addaction name=\"actionPasteTransform\"/>\n     <addaction name=\"actionResetTransform\"/>\n     <addaction name=\"separator\"/>\n     <addaction name=\"actionRotate90CW\"/>\n     <addaction name=\"actionRotate90CCW\"/>\n     <addaction name=\"actionRotate180\"/>\n     <addaction name=\"separator\"/>\n     <addaction name=\"actionFlipHorizontal\"/>\n     <addaction name=\"actionFlipVertical\"/>\n     <addaction name=\"separator\"/>\n     <addaction name=\"actionFitToScreen\"/>\n     <addaction name=\"actionStretchToScreen\"/>\n     <addaction name=\"actionCenterToScreen\"/>\n     <addaction name=\"actionVerticalCenter\"/>\n     <addaction name=\"actionHorizontalCenter\"/>\n    </widget>\n    <widget class=\"QMenu\" name=\"orderMenu\">\n     <property name=\"title\">\n      <string>Basic.MainMenu.Edit.Order</string>\n     </property>\n     <addaction name=\"actionMoveUp\"/>\n     <addaction name=\"actionMoveDown\"/>\n     <addaction name=\"separator\"/>\n     <addaction name=\"actionMoveToTop\"/>\n     <addaction name=\"actionMoveToBottom\"/>\n    </widget>\n    <widget class=\"QMenu\" name=\"scalingMenu\">\n     <property name=\"title\">\n      <string>Basic.MainMenu.Edit.Scale</string>\n     </property>\n     <addaction name=\"actionScaleWindow\"/>\n     <addaction name=\"actionScaleCanvas\"/>\n     <addaction name=\"actionScaleOutput\"/>\n     <addaction name=\"separator\"/>\n     <addaction name=\"actionPreviewZoomIn\"/>\n     <addaction name=\"actionPreviewZoomOut\"/>\n     <addaction name=\"actionPreviewResetZoom\"/>\n    </widget>\n    <action name=\"actionCopySource\">\n     <property name=\"text\">\n      <string>Copy</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Ctrl+C</string>\n     </property>\n    </action>\n    <action name=\"actionPasteRef\">\n     <property name=\"enabled\">\n      <bool>false</bool>\n     </property>\n     <property name=\"text\">\n      <string>PasteReference</string>\n     </property>\n     <property name=\"iconText\">\n      <string>PasteReference</string>\n     </property>\n     <property name=\"toolTip\">\n      <string>PasteReference</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>Ctrl+V</string>\n     </property>\n    </action>\n    <action name=\"actionCopyFilters\">\n     <property name=\"text\">\n      <string>Copy.Filters</string>\n     </property>\n    </action>\n    <action name=\"actionPasteFilters\">\n     <property name=\"enabled\">\n      <bool>false</bool>\n     </property>\n     <property name=\"text\">\n      <string>Paste.Filters</string>\n     </property>\n    </action>\n    <addaction name=\"actionMainUndo\"/>\n    <addaction name=\"actionMainRedo\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionCopySource\"/>\n    <addaction name=\"actionPasteRef\"/>\n    <addaction name=\"actionPasteDup\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionCopyFilters\"/>\n    <addaction name=\"actionPasteFilters\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"transformMenu\"/>\n    <addaction name=\"orderMenu\"/>\n    <addaction name=\"scalingMenu\"/>\n    <addaction name=\"actionLockPreview\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionAdvAudioProperties\"/>\n    <addaction name=\"separator\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"profileMenu\">\n    <property name=\"title\">\n     <string>Basic.MainMenu.Profile</string>\n    </property>\n    <addaction name=\"actionNewProfile\"/>\n    <addaction name=\"actionDupProfile\"/>\n    <addaction name=\"actionRenameProfile\"/>\n    <addaction name=\"actionRemoveProfile\"/>\n    <addaction name=\"actionImportProfile\"/>\n    <addaction name=\"actionExportProfile\"/>\n    <addaction name=\"separator\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"sceneCollectionMenu\">\n    <property name=\"title\">\n     <string>Basic.MainMenu.SceneCollection</string>\n    </property>\n    <addaction name=\"actionNewSceneCollection\"/>\n    <addaction name=\"actionDupSceneCollection\"/>\n    <addaction name=\"actionRenameSceneCollection\"/>\n    <addaction name=\"actionRemoveSceneCollection\"/>\n    <addaction name=\"actionImportSceneCollection\"/>\n    <addaction name=\"actionExportSceneCollection\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionShowMissingFiles\"/>\n    <addaction name=\"actionRemigrateSceneCollection\"/>\n    <addaction name=\"separator\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"viewMenu\">\n    <property name=\"title\">\n     <string>Basic.MainMenu.View</string>\n    </property>\n    <widget class=\"QMenu\" name=\"sceneListModeMenu\">\n     <property name=\"title\">\n      <string>Basic.MainMenu.View.SceneListMode</string>\n     </property>\n     <addaction name=\"actionSceneListMode\"/>\n     <addaction name=\"actionSceneGridMode\"/>\n    </widget>\n    <widget class=\"QMenu\" name=\"multiviewProjectorMenu\">\n     <property name=\"title\">\n      <string>Projector.Open.Multiview</string>\n     </property>\n    </widget>\n    <action name=\"resetUI\">\n     <property name=\"text\">\n      <string>Basic.MainMenu.View.ResetUI</string>\n     </property>\n    </action>\n    <action name=\"actionFullscreenInterface\">\n     <property name=\"text\">\n      <string>Basic.MainMenu.View.Fullscreen.Interface</string>\n     </property>\n     <property name=\"shortcut\">\n      <string>F11</string>\n     </property>\n    </action>\n    <addaction name=\"resetUI\"/>\n    <addaction name=\"actionFullscreenInterface\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"sceneListModeMenu\"/>\n    <addaction name=\"toggleListboxToolbars\"/>\n    <addaction name=\"toggleContextBar\"/>\n    <addaction name=\"toggleSourceIcons\"/>\n    <addaction name=\"toggleStatusBar\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"stats\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"multiviewProjectorMenu\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionAlwaysOnTop\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuTools\">\n    <property name=\"title\">\n     <string>Basic.MainMenu.Tools</string>\n    </property>\n    <addaction name=\"autoConfigure\"/>\n    <addaction name=\"idianPlayground\"/>\n    <addaction name=\"actionOpenPluginManager\"/>\n    <addaction name=\"separator\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuDocks\">\n    <property name=\"title\">\n     <string>Basic.MainMenu.Docks</string>\n    </property>\n    <addaction name=\"lockDocks\"/>\n    <addaction name=\"sideDocks\"/>\n    <addaction name=\"resetDocks\"/>\n    <addaction name=\"separator\"/>\n   </widget>\n   <addaction name=\"menu_File\"/>\n   <addaction name=\"menuBasic_MainMenu_Edit\"/>\n   <addaction name=\"viewMenu\"/>\n   <addaction name=\"menuDocks\"/>\n   <addaction name=\"profileMenu\"/>\n   <addaction name=\"sceneCollectionMenu\"/>\n   <addaction name=\"menuTools\"/>\n   <addaction name=\"menuBasic_MainMenu_Help\"/>\n  </widget>\n  <widget class=\"OBSBasicStatusBar\" name=\"statusbar\"/>\n  <widget class=\"OBSDock\" name=\"scenesDock\">\n   <property name=\"features\">\n    <set>QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>\n   </property>\n   <property name=\"windowTitle\">\n    <string>Basic.Main.Scenes</string>\n   </property>\n   <attribute name=\"dockWidgetArea\">\n    <number>8</number>\n   </attribute>\n   <widget class=\"QWidget\" name=\"dockWidgetContents_2\">\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_6\">\n     <property name=\"spacing\">\n      <number>0</number>\n     </property>\n     <property name=\"leftMargin\">\n      <number>1</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>1</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>1</number>\n     </property>\n     <item>\n      <widget class=\"QFrame\" name=\"scenesFrame\">\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_12\">\n        <property name=\"spacing\">\n         <number>0</number>\n        </property>\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"SceneTree\" name=\"scenes\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"contextMenuPolicy\">\n           <enum>Qt::CustomContextMenu</enum>\n          </property>\n          <property name=\"frameShape\">\n           <enum>QFrame::NoFrame</enum>\n          </property>\n          <property name=\"frameShadow\">\n           <enum>QFrame::Plain</enum>\n          </property>\n          <property name=\"showDropIndicator\" stdset=\"0\">\n           <bool>true</bool>\n          </property>\n          <property name=\"dragEnabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"dragDropMode\">\n           <enum>QAbstractItemView::InternalMove</enum>\n          </property>\n          <property name=\"defaultDropAction\">\n           <enum>Qt::TargetMoveAction</enum>\n          </property>\n          <!-- Spacing must be 0 due to QTBUG-106395 -->\n          <property name=\"spacing\">\n           <number>0</number>\n          </property>\n          <addaction name=\"actionRemoveScene\"/>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QToolBar\" name=\"scenesToolbar\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"iconSize\">\n           <size>\n            <width>16</width>\n            <height>16</height>\n           </size>\n          </property>\n          <property name=\"floatable\">\n           <bool>false</bool>\n          </property>\n          <addaction name=\"actionAddScene\"/>\n          <addaction name=\"actionRemoveScene\"/>\n          <addaction name=\"separator\"/>\n          <addaction name=\"actionSceneFilters\"/>\n          <addaction name=\"separator\"/>\n          <addaction name=\"actionSceneUp\"/>\n          <addaction name=\"actionSceneDown\"/>\n         </widget>\n        </item>\n        <item>\n         <spacer name=\"scenesFixedSizeHSpacer\">\n          <property name=\"orientation\">\n           <enum>Qt::Horizontal</enum>\n          </property>\n          <property name=\"sizeType\">\n           <enum>QSizePolicy::Fixed</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>150</width>\n            <height>0</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </widget>\n  </widget>\n  <widget class=\"OBSDock\" name=\"sourcesDock\">\n   <property name=\"features\">\n    <set>QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>\n   </property>\n   <property name=\"windowTitle\">\n    <string>Basic.Main.Sources</string>\n   </property>\n   <attribute name=\"dockWidgetArea\">\n    <number>8</number>\n   </attribute>\n   <widget class=\"QWidget\" name=\"dockWidgetContents_6\">\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n     <property name=\"spacing\">\n      <number>0</number>\n     </property>\n     <property name=\"leftMargin\">\n      <number>1</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>1</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>1</number>\n     </property>\n     <item>\n      <widget class=\"QFrame\" name=\"sourcesFrame\">\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_17\">\n        <property name=\"spacing\">\n         <number>0</number>\n        </property>\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"SourceTree\" name=\"sources\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"contextMenuPolicy\">\n           <enum>Qt::CustomContextMenu</enum>\n          </property>\n          <property name=\"frameShape\">\n           <enum>QFrame::NoFrame</enum>\n          </property>\n          <property name=\"showDropIndicator\" stdset=\"0\">\n           <bool>true</bool>\n          </property>\n          <property name=\"dragEnabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"dragDropMode\">\n           <enum>QAbstractItemView::InternalMove</enum>\n          </property>\n          <property name=\"defaultDropAction\">\n           <enum>Qt::TargetMoveAction</enum>\n          </property>\n          <property name=\"selectionMode\">\n           <enum>QAbstractItemView::ExtendedSelection</enum>\n          </property>\n          <!-- Spacing must be 0 due to QTBUG-106395 -->\n          <property name=\"spacing\">\n           <number>0</number>\n          </property>\n          <addaction name=\"actionRemoveSource\"/>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QToolBar\" name=\"sourcesToolbar\">\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"iconSize\">\n           <size>\n            <width>16</width>\n            <height>16</height>\n           </size>\n          </property>\n          <property name=\"floatable\">\n           <bool>false</bool>\n          </property>\n          <addaction name=\"actionAddSource\"/>\n          <addaction name=\"actionRemoveSource\"/>\n          <addaction name=\"separator\"/>\n          <addaction name=\"actionSourceProperties\"/>\n          <addaction name=\"separator\"/>\n          <addaction name=\"actionSourceUp\"/>\n          <addaction name=\"actionSourceDown\"/>\n         </widget>\n        </item>\n        <item>\n         <spacer name=\"sourcesFixedSizeHSpacer\">\n          <property name=\"orientation\">\n           <enum>Qt::Horizontal</enum>\n          </property>\n          <property name=\"sizeType\">\n           <enum>QSizePolicy::Fixed</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>150</width>\n            <height>0</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </widget>\n  </widget>\n  <widget class=\"OBSDock\" name=\"mixerDock\">\n   <property name=\"contextMenuPolicy\">\n    <enum>Qt::CustomContextMenu</enum>\n   </property>\n   <property name=\"features\">\n    <set>QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>\n   </property>\n   <property name=\"windowTitle\">\n    <string>Mixer</string>\n   </property>\n   <attribute name=\"dockWidgetArea\">\n    <number>8</number>\n   </attribute>\n   <widget class=\"QWidget\" name=\"dockWidgetContents_7\">\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n     <property name=\"spacing\">\n      <number>0</number>\n     </property>\n     <property name=\"leftMargin\">\n      <number>1</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>1</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>1</number>\n     </property>\n    </layout>\n   </widget>\n  </widget>\n  <widget class=\"OBSDock\" name=\"transitionsDock\">\n   <property name=\"features\">\n    <set>QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable</set>\n   </property>\n   <property name=\"windowTitle\">\n    <string>Basic.SceneTransitions</string>\n   </property>\n   <attribute name=\"dockWidgetArea\">\n    <number>8</number>\n   </attribute>\n   <widget class=\"QWidget\" name=\"dockWidgetContents_5\">\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n     <property name=\"spacing\">\n      <number>0</number>\n     </property>\n     <property name=\"leftMargin\">\n      <number>1</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>1</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>1</number>\n     </property>\n     <item>\n      <widget class=\"QFrame\" name=\"transitionsFrame\">\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_8\">\n        <property name=\"spacing\">\n         <number>0</number>\n        </property>\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"QComboBox\" name=\"transitions\">\n          <property name=\"minimumSize\">\n           <size>\n            <width>120</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"accessibleName\">\n           <string>Transition</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n          <property name=\"spacing\">\n           <number>4</number>\n          </property>\n          <item>\n           <widget class=\"QLabel\" name=\"transitionDurationLabel\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"text\">\n             <string>Basic.TransitionDuration</string>\n            </property>\n            <property name=\"buddy\">\n             <cstring>transitionDuration</cstring>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QSpinBox\" name=\"transitionDuration\">\n            <property name=\"accessibleName\">\n             <string>Basic.TransitionDuration</string>\n            </property>\n            <property name=\"suffix\">\n             <string> ms</string>\n            </property>\n            <property name=\"minimum\">\n             <number>50</number>\n            </property>\n            <property name=\"maximum\">\n             <number>20000</number>\n            </property>\n            <property name=\"singleStep\">\n             <number>50</number>\n            </property>\n            <property name=\"value\">\n             <number>300</number>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n        <item>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n          <property name=\"spacing\">\n           <number>4</number>\n          </property>\n          <item>\n           <spacer name=\"horizontalSpacer\">\n            <property name=\"orientation\">\n             <enum>Qt::Horizontal</enum>\n            </property>\n            <property name=\"sizeType\">\n             <enum>QSizePolicy::Expanding</enum>\n            </property>\n            <property name=\"sizeHint\" stdset=\"0\">\n             <size>\n              <width>40</width>\n              <height>20</height>\n             </size>\n            </property>\n           </spacer>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"transitionAdd\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"toolTip\">\n             <string>Basic.AddTransition</string>\n            </property>\n            <property name=\"accessibleName\">\n             <string>Basic.AddTransition</string>\n            </property>\n            <property name=\"text\">\n             <string notr=\"true\"/>\n            </property>\n            <property name=\"icon\">\n             <iconset resource=\"obs.qrc\">\n              <normaloff>:/res/images/plus.svg</normaloff>:/res/images/plus.svg</iconset>\n            </property>\n            <property name=\"flat\">\n             <bool>false</bool>\n            </property>\n            <property name=\"class\" stdset=\"0\">\n             <string notr=\"true\">btn-tool icon-plus</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"transitionRemove\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"toolTip\">\n             <string>Basic.RemoveTransition</string>\n            </property>\n            <property name=\"accessibleName\">\n             <string>Basic.RemoveTransition</string>\n            </property>\n            <property name=\"text\">\n             <string notr=\"true\"/>\n            </property>\n            <property name=\"icon\">\n             <iconset resource=\"obs.qrc\">\n              <normaloff>:/res/images/minus.svg</normaloff>:/res/images/minus.svg</iconset>\n            </property>\n            <property name=\"flat\">\n             <bool>false</bool>\n            </property>\n            <property name=\"class\" stdset=\"0\">\n             <string notr=\"true\">btn-tool icon-trash</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QPushButton\" name=\"transitionProps\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"toolTip\">\n             <string>Basic.TransitionProperties</string>\n            </property>\n            <property name=\"accessibleName\">\n             <string>Basic.TransitionProperties</string>\n            </property>\n            <property name=\"text\">\n             <string notr=\"true\"/>\n            </property>\n            <property name=\"icon\">\n             <iconset resource=\"obs.qrc\">\n              <normaloff>:/settings/images/settings/general.svg</normaloff>:/settings/images/settings/general.svg</iconset>\n            </property>\n            <property name=\"flat\">\n             <bool>false</bool>\n            </property>\n            <property name=\"class\" stdset=\"0\">\n             <string notr=\"true\">btn-tool icon-dots-vert</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n        <item>\n         <spacer name=\"verticalSpacer\">\n          <property name=\"orientation\">\n           <enum>Qt::Vertical</enum>\n          </property>\n          <property name=\"sizeHint\" stdset=\"0\">\n           <size>\n            <width>20</width>\n            <height>40</height>\n           </size>\n          </property>\n         </spacer>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </widget>\n  </widget>\n  <action name=\"actionAddScene\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/plus.svg</normaloff>:/res/images/plus.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Add</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>AddScene</string>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string notr=\"true\">icon-plus</string>\n   </property>\n  </action>\n  <action name=\"actionAddSource\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/plus.svg</normaloff>:/res/images/plus.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Add</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>AddSource</string>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string notr=\"true\">icon-plus</string>\n   </property>\n  </action>\n  <action name=\"actionRemoveScene\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/minus.svg</normaloff>:/res/images/minus.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Remove</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>RemoveScene</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Del</string>\n   </property>\n   <property name=\"shortcutContext\">\n    <enum>Qt::WidgetWithChildrenShortcut</enum>\n   </property>\n   <property name=\"iconVisibleInMenu\">\n    <bool>false</bool>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string notr=\"true\">icon-trash</string>\n   </property>\n  </action>\n  <action name=\"actionRemoveSource\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/minus.svg</normaloff>:/res/images/minus.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Remove</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>RemoveSource</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Del</string>\n   </property>\n   <property name=\"shortcutContext\">\n    <enum>Qt::WidgetWithChildrenShortcut</enum>\n   </property>\n   <property name=\"iconVisibleInMenu\">\n    <bool>false</bool>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string notr=\"true\">icon-trash</string>\n   </property>\n  </action>\n  <action name=\"actionSourceProperties\">\n   <property name=\"enabled\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/settings/images/settings/general.svg</normaloff>:/settings/images/settings/general.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Properties</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>SourceProperties</string>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string notr=\"true\">icon-gear</string>\n   </property>\n  </action>\n  <action name=\"actionSceneUp\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/up.svg</normaloff>:/res/images/up.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>MoveUp</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>MoveSceneUp</string>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string notr=\"true\">icon-up</string>\n   </property>\n  </action>\n  <action name=\"actionSourceUp\">\n   <property name=\"enabled\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/up.svg</normaloff>:/res/images/up.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>MoveUp</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>MoveSourceUp</string>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string notr=\"true\">icon-up</string>\n   </property>\n  </action>\n  <action name=\"actionSceneDown\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/down.svg</normaloff>:/res/images/down.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>MoveDown</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>MoveSceneDown</string>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string notr=\"true\">icon-down</string>\n   </property>\n  </action>\n  <action name=\"actionSourceDown\">\n   <property name=\"enabled\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/down.svg</normaloff>:/res/images/down.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>MoveDown</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>MoveSourceDown</string>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string notr=\"true\">icon-down</string>\n   </property>\n  </action>\n  <action name=\"actionShow_Recordings\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.File.ShowRecordings</string>\n   </property>\n  </action>\n  <action name=\"actionRemux\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.File.Remux</string>\n   </property>\n  </action>\n  <action name=\"action_Settings\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.File.Settings</string>\n   </property>\n  </action>\n  <action name=\"actionE_xit\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.File.Exit</string>\n   </property>\n  </action>\n  <action name=\"actionShowLogs\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.Logs.ShowLogs</string>\n   </property>\n  </action>\n  <action name=\"actionUploadLastLog\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.Logs.UploadLastLog</string>\n   </property>\n  </action>\n  <action name=\"actionUploadCurrentLog\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.Logs.UploadCurrentLog</string>\n   </property>\n  </action>\n  <action name=\"actionViewCurrentLog\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.Logs.ViewCurrentLog</string>\n   </property>\n  </action>\n  <action name=\"actionUndo\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Undo</string>\n   </property>\n  </action>\n  <action name=\"actionRedo\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Redo</string>\n   </property>\n  </action>\n  <action name=\"actionEditTransform\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.EditTransform</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+E</string>\n   </property>\n  </action>\n  <action name=\"actionCopyTransform\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.CopyTransform</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Shift+C</string>\n   </property>\n  </action>\n  <action name=\"actionPasteTransform\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.PasteTransform</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Shift+V</string>\n   </property>\n  </action>\n  <action name=\"actionRotate90CW\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.Rotate90CW</string>\n   </property>\n  </action>\n  <action name=\"actionRotate90CCW\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.Rotate90CCW</string>\n   </property>\n  </action>\n  <action name=\"actionRotate180\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.Rotate180</string>\n   </property>\n  </action>\n  <action name=\"actionFitToScreen\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.FitToScreen</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+F</string>\n   </property>\n  </action>\n  <action name=\"actionStretchToScreen\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.StretchToScreen</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+S</string>\n   </property>\n  </action>\n  <action name=\"actionResetTransform\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.ResetTransform</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+R</string>\n   </property>\n  </action>\n  <action name=\"actionCenterToScreen\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.CenterToScreen</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+D</string>\n   </property>\n  </action>\n  <action name=\"actionVerticalCenter\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.VerticalCenter</string>\n   </property>\n  </action>\n  <action name=\"actionHorizontalCenter\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.HorizontalCenter</string>\n   </property>\n  </action>\n  <action name=\"actionFlipHorizontal\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.FlipHorizontal</string>\n   </property>\n  </action>\n  <action name=\"actionFlipVertical\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Transform.FlipVertical</string>\n   </property>\n  </action>\n  <action name=\"actionMoveUp\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Order.MoveUp</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Up</string>\n   </property>\n  </action>\n  <action name=\"actionMoveDown\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Order.MoveDown</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Down</string>\n   </property>\n  </action>\n  <action name=\"actionMoveToTop\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Order.MoveToTop</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Home</string>\n   </property>\n  </action>\n  <action name=\"actionMoveToBottom\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Order.MoveToBottom</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+End</string>\n   </property>\n  </action>\n  <action name=\"actionCheckForUpdates\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.CheckForUpdates</string>\n   </property>\n  </action>\n  <action name=\"actionShowMacPermissions\">\n   <property name=\"text\">\n    <string>MacPermissions.MenuAction</string>\n   </property>\n  </action>\n  <action name=\"actionRepair\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.Repair</string>\n   </property>\n  </action>\n  <action name=\"actionRestartSafe\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.RestartSafeMode</string>\n   </property>\n  </action>\n  <action name=\"actionReleaseNotes\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.ReleaseNotes</string>\n   </property>\n   <property name=\"visible\">\n    <bool>false</bool>\n   </property>\n  </action>\n  <action name=\"actionInteract\">\n   <property name=\"text\">\n    <string>Interact</string>\n   </property>\n  </action>\n  <action name=\"actionAdvAudioProperties\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.AdvAudio</string>\n   </property>\n  </action>\n  <action name=\"actionWebsite\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.Website</string>\n   </property>\n  </action>\n  <action name=\"actionNewSceneCollection\">\n   <property name=\"text\">\n    <string>New</string>\n   </property>\n  </action>\n  <action name=\"actionDupSceneCollection\">\n   <property name=\"text\">\n    <string>Duplicate</string>\n   </property>\n  </action>\n  <action name=\"actionRenameSceneCollection\">\n   <property name=\"text\">\n    <string>Rename</string>\n   </property>\n  </action>\n  <action name=\"actionRemoveSceneCollection\">\n   <property name=\"text\">\n    <string>Remove</string>\n   </property>\n  </action>\n  <action name=\"actionImportSceneCollection\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Import</string>\n   </property>\n  </action>\n  <action name=\"actionExportSceneCollection\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Export</string>\n   </property>\n  </action>\n  <action name=\"actionShowMissingFiles\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.File.ShowMissingFiles</string>\n   </property>\n  </action>\n  <action name=\"actionRemigrateSceneCollection\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.SceneCollection.Remigrate</string>\n   </property>\n  </action>\n  <action name=\"actionNewProfile\">\n   <property name=\"text\">\n    <string>New</string>\n   </property>\n  </action>\n  <action name=\"actionDupProfile\">\n   <property name=\"text\">\n    <string>Duplicate</string>\n   </property>\n  </action>\n  <action name=\"actionRenameProfile\">\n   <property name=\"text\">\n    <string>Rename</string>\n   </property>\n  </action>\n  <action name=\"actionRemoveProfile\">\n   <property name=\"text\">\n    <string>Remove</string>\n   </property>\n  </action>\n  <action name=\"actionImportProfile\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Import</string>\n   </property>\n  </action>\n  <action name=\"actionExportProfile\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Export</string>\n   </property>\n  </action>\n  <action name=\"actionShowSettingsFolder\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.File.ShowSettingsFolder</string>\n   </property>\n  </action>\n  <action name=\"actionShowProfileFolder\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.File.ShowProfileFolder</string>\n   </property>\n  </action>\n  <action name=\"actionAlwaysOnTop\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.View.AlwaysOnTop</string>\n   </property>\n  </action>\n  <action name=\"toggleListboxToolbars\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.View.ListboxToolbars</string>\n   </property>\n  </action>\n  <action name=\"toggleStatusBar\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.View.StatusBar</string>\n   </property>\n  </action>\n  <action name=\"actionLockPreview\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.LockPreview</string>\n   </property>\n  </action>\n  <action name=\"actionScaleWindow\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Scale.Window</string>\n   </property>\n  </action>\n  <action name=\"actionScaleCanvas\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Scale.Canvas</string>\n   </property>\n  </action>\n  <action name=\"actionScaleOutput\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Scale.Output</string>\n   </property>\n  </action>\n  <action name=\"actionPasteDup\">\n   <property name=\"text\">\n    <string>PasteDuplicate</string>\n   </property>\n  </action>\n  <action name=\"idianPlayground\">\n   <property name=\"text\">\n    <string>Idian Playground</string>\n   </property>\n  </action>\n  <action name=\"autoConfigure2\">\n   <property name=\"text\">\n    <string>Basic.AutoConfig</string>\n   </property>\n  </action>\n  <action name=\"autoConfigure\">\n   <property name=\"text\">\n    <string>Basic.AutoConfig</string>\n   </property>\n  </action>\n  <action name=\"actionOpenPluginManager\">\n   <property name=\"text\">\n    <string>Basic.OpenPluginManager</string>\n   </property>\n  </action>\n  <action name=\"stats\">\n   <property name=\"text\">\n    <string>Basic.Stats</string>\n   </property>\n  </action>\n  <action name=\"resetDocks\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Docks.ResetDocks</string>\n   </property>\n  </action>\n  <action name=\"lockDocks\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Docks.LockDocks</string>\n   </property>\n  </action>\n  <action name=\"sideDocks\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Docks.SideDocks</string>\n   </property>\n  </action>\n  <action name=\"actionHelpPortal\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.HelpPortal</string>\n   </property>\n  </action>\n  <action name=\"actionShowCrashLogs\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.CrashLogs.ShowLogs</string>\n   </property>\n  </action>\n  <action name=\"actionUploadLastCrashLog\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.CrashLogs.UploadLastLog</string>\n   </property>\n  </action>\n  <action name=\"actionDiscord\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.Discord</string>\n   </property>\n  </action>\n  <action name=\"actionShowWhatsNew\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.WhatsNew</string>\n   </property>\n  </action>\n  <action name=\"actionShowAbout\">\n   <property name=\"text\">\n    <string>Basic.MainMenu.Help.About</string>\n   </property>\n  </action>\n  <action name=\"toggleSourceIcons\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.View.SourceIcons</string>\n   </property>\n  </action>\n  <action name=\"toggleContextToolbars\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.View.Toolbars.Context</string>\n   </property>\n  </action>\n  <action name=\"toggleContextBar\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"checked\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.View.ContextBar</string>\n   </property>\n  </action>\n  <action name=\"actionMainUndo\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Undo.Undo</string>\n   </property>\n  </action>\n  <action name=\"actionMainRedo\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Undo.Redo</string>\n   </property>\n  </action>\n  <action name=\"actionMixerToolbarAdvAudio\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/settings/images/settings/advanced.svg</normaloff>:/settings/images/settings/advanced.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>AdvAudioProps</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Basic.AdvAudio</string>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string>icon-cogs</string>\n   </property>\n  </action>\n  <action name=\"actionSceneFilters\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/filter.svg</normaloff>:/res/images/filter.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>SceneFilters</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>SceneFilters</string>\n   </property>\n   <property name=\"class\" stdset=\"0\">\n    <string>icon-filter</string>\n   </property>\n  </action>\n  <action name=\"actionSceneGridMode\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>Grid</string>\n   </property>\n  </action>\n  <action name=\"actionSceneListMode\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"text\">\n    <string>List</string>\n   </property>\n  </action>\n  <action name=\"actionPreview\">\n   <property name=\"text\">\n    <string>Preview</string>\n   </property>\n  </action>\n  <action name=\"actionPreviewZoomIn\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Scale.ZoomIn</string>\n   </property>\n  </action>\n  <action name=\"actionPreviewZoomOut\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Scale.ZoomOut</string>\n   </property>\n  </action>\n  <action name=\"actionPreviewResetZoom\">\n   <property name=\"enabled\">\n    <bool>false</bool>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Scale.ResetZoom</string>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>OBSBasicPreview</class>\n   <extends>QWidget</extends>\n   <header>widgets/OBSBasicPreview.hpp</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>OBSBasicStatusBar</class>\n   <extends>QStatusBar</extends>\n   <header>widgets/OBSBasicStatusBar.hpp</header>\n  </customwidget>\n  <customwidget>\n   <class>SourceTree</class>\n   <extends>QListView</extends>\n   <header>components/SourceTree.hpp</header>\n  </customwidget>\n  <customwidget>\n   <class>SceneTree</class>\n   <extends>QListWidget</extends>\n   <header>components/SceneTree.hpp</header>\n  </customwidget>\n  <customwidget>\n   <class>OBSDock</class>\n   <extends>QDockWidget</extends>\n   <header>docks/OBSDock.hpp</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>OBSPreviewScalingLabel</class>\n   <extends>QLabel</extends>\n   <header>components/OBSPreviewScalingLabel.hpp</header>\n  </customwidget>\n  <customwidget>\n   <class>OBSPreviewScalingComboBox</class>\n   <extends>QComboBox</extends>\n   <header>components/OBSPreviewScalingComboBox.hpp</header>\n  </customwidget>\n </customwidgets>\n <resources>\n  <include location=\"obs.qrc\"/>\n </resources>\n <connections>\n  <connection>\n   <sender>actionE_xit</sender>\n   <signal>triggered()</signal>\n   <receiver>OBSBasic</receiver>\n   <slot>close()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>-1</x>\n     <y>-1</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>463</x>\n     <y>351</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSBasicControls.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSBasicControls</class>\n <widget class=\"QWidget\" name=\"OBSBasicControls\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>318</width>\n    <height>213</height>\n   </rect>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_9\">\n   <property name=\"spacing\">\n    <number>0</number>\n   </property>\n   <property name=\"leftMargin\">\n    <number>1</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>1</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>1</number>\n   </property>\n   <item>\n    <widget class=\"QFrame\" name=\"controlsFrame\">\n     <layout class=\"QVBoxLayout\" name=\"buttonsVLayout\">\n      <property name=\"spacing\">\n       <number>0</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <layout class=\"QHBoxLayout\" name=\"horizontalLayout_7\">\n        <item>\n         <widget class=\"QPushButton\" name=\"streamButton\">\n          <property name=\"enabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Ignored\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>150</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"text\">\n           <string>Basic.Main.StartStreaming</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"broadcastButton\">\n          <property name=\"enabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Ignored\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>150</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"text\">\n           <string>Basic.Main.StartBroadcast</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </item>\n      <item>\n       <layout class=\"QHBoxLayout\" name=\"recordingLayout\">\n        <property name=\"spacing\">\n         <number>2</number>\n        </property>\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"QPushButton\" name=\"recordButton\">\n          <property name=\"enabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>0</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"text\">\n           <string>Basic.Main.StartRecording</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"pauseRecordButton\">\n          <property name=\"enabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>0</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"toolTip\">\n           <string>Basic.Main.PauseRecording</string>\n          </property>\n          <property name=\"accessibleName\">\n           <string>Basic.Main.PauseRecording</string>\n          </property>\n          <property name=\"icon\">\n           <iconset resource=\"obs.qrc\">\n            <normaloff>:/res/images/media-pause.svg</normaloff>:/res/images/media-pause.svg</iconset>\n          </property>\n          <property name=\"class\" stdset=\"0\">\n           <string>icon-media-pause</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </item>\n      <item>\n       <layout class=\"QHBoxLayout\" name=\"replayBufferLayout\">\n        <property name=\"spacing\">\n         <number>2</number>\n        </property>\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"QPushButton\" name=\"replayBufferButton\">\n          <property name=\"enabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>0</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"text\">\n           <string>Basic.Main.StartReplayBuffer</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"saveReplayButton\">\n          <property name=\"enabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>0</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"toolTip\">\n           <string>Basic.Main.SaveReplay</string>\n          </property>\n          <property name=\"accessibleName\">\n           <string>Basic.Main.SaveReplay</string>\n          </property>\n          <property name=\"icon\">\n           <iconset resource=\"obs.qrc\">\n            <normaloff>:/res/images/save.svg</normaloff>:/res/images/save.svg</iconset>\n          </property>\n          <property name=\"class\" stdset=\"0\">\n           <string>icon-save</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </item>\n      <item>\n       <layout class=\"QHBoxLayout\" name=\"virtualCamLayout\">\n        <property name=\"spacing\">\n         <number>2</number>\n        </property>\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"QPushButton\" name=\"virtualCamButton\">\n          <property name=\"enabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>0</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"text\">\n           <string>Basic.Main.StartVirtualCam</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"virtualCamConfigButton\">\n          <property name=\"enabled\">\n           <bool>true</bool>\n          </property>\n          <property name=\"minimumSize\">\n           <size>\n            <width>0</width>\n            <height>0</height>\n           </size>\n          </property>\n          <property name=\"toolTip\">\n           <string>Basic.Main.VirtualCamConfig</string>\n          </property>\n          <property name=\"accessibleName\">\n           <string>Basic.Main.VirtualCamConfig</string>\n          </property>\n          <property name=\"icon\">\n           <iconset resource=\"obs.qrc\">\n            <normaloff>:/settings/images/settings/general.svg</normaloff>:/settings/images/settings/general.svg</iconset>\n          </property>\n          <property name=\"class\" stdset=\"0\">\n           <string>icon-gear</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </item>\n      <item>\n       <widget class=\"QPushButton\" name=\"modeSwitch\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Ignored\" vsizetype=\"Fixed\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"minimumSize\">\n         <size>\n          <width>150</width>\n          <height>0</height>\n         </size>\n        </property>\n        <property name=\"text\">\n         <string>Basic.TogglePreviewProgramMode</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QPushButton\" name=\"settingsButton\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Ignored\" vsizetype=\"Fixed\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"minimumSize\">\n         <size>\n          <width>150</width>\n          <height>0</height>\n         </size>\n        </property>\n        <property name=\"text\">\n         <string>Settings</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <spacer name=\"expVSpacer\">\n        <property name=\"orientation\">\n         <enum>Qt::Vertical</enum>\n        </property>\n        <property name=\"sizeHint\" stdset=\"0\">\n         <size>\n          <width>0</width>\n          <height>0</height>\n         </size>\n        </property>\n       </spacer>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources>\n  <include location=\"obs.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSBasicFilters.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSBasicFilters</class>\n <widget class=\"QDialog\" name=\"OBSBasicFilters\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>861</width>\n    <height>726</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Basic.Filters</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\" stretch=\"1,10\">\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetMinimumSize</enum>\n     </property>\n     <item>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <widget class=\"QFrame\" name=\"asyncWidget\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"minimumSize\">\n          <size>\n           <width>255</width>\n           <height>0</height>\n          </size>\n         </property>\n         <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n          <property name=\"leftMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"topMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"rightMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"bottomMargin\">\n           <number>0</number>\n          </property>\n          <item>\n           <widget class=\"QLabel\" name=\"asyncLabel\">\n            <property name=\"text\">\n             <string>Basic.Filters.AsyncFilters</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"FocusList\" name=\"asyncFilters\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"contextMenuPolicy\">\n             <enum>Qt::CustomContextMenu</enum>\n            </property>\n            <property name=\"dragEnabled\">\n             <bool>true</bool>\n            </property>\n            <property name=\"dragDropMode\">\n             <enum>QAbstractItemView::InternalMove</enum>\n            </property>\n            <property name=\"defaultDropAction\">\n             <enum>Qt::TargetMoveAction</enum>\n            </property>\n            <property name=\"spacing\">\n             <number>1</number>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QFrame\" name=\"widget\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n             <property name=\"spacing\">\n              <number>4</number>\n             </property>\n             <property name=\"leftMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"rightMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QPushButton\" name=\"addAsyncFilter\">\n               <property name=\"maximumSize\">\n                <size>\n                 <width>22</width>\n                 <height>22</height>\n                </size>\n               </property>\n               <property name=\"accessibleName\">\n                <string>Add</string>\n               </property>\n               <property name=\"text\">\n                <string notr=\"true\"/>\n               </property>\n               <property name=\"icon\">\n                <iconset resource=\"obs.qrc\">\n                 <normaloff>:/res/images/plus.svg</normaloff>:/res/images/plus.svg</iconset>\n               </property>\n               <property name=\"autoDefault\">\n                <bool>false</bool>\n               </property>\n               <property name=\"flat\">\n                <bool>true</bool>\n               </property>\n               <property name=\"class\" stdset=\"0\">\n                <string notr=\"true\">btn-tool icon-plus</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"removeAsyncFilter\">\n               <property name=\"maximumSize\">\n                <size>\n                 <width>22</width>\n                 <height>22</height>\n                </size>\n               </property>\n               <property name=\"accessibleName\">\n                <string>Remove</string>\n               </property>\n               <property name=\"text\">\n                <string notr=\"true\"/>\n               </property>\n               <property name=\"icon\">\n                <iconset resource=\"obs.qrc\">\n                 <normaloff>:/res/images/minus.svg</normaloff>:/res/images/minus.svg</iconset>\n               </property>\n               <property name=\"autoDefault\">\n                <bool>false</bool>\n               </property>\n               <property name=\"flat\">\n                <bool>true</bool>\n               </property>\n               <property name=\"class\" stdset=\"0\">\n                <string notr=\"true\">btn-tool icon-trash</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"moveAsyncFilterUp\">\n               <property name=\"maximumSize\">\n                <size>\n                 <width>22</width>\n                 <height>22</height>\n                </size>\n               </property>\n               <property name=\"accessibleName\">\n                <string>MoveUp</string>\n               </property>\n               <property name=\"text\">\n                <string notr=\"true\"/>\n               </property>\n               <property name=\"icon\">\n                <iconset resource=\"obs.qrc\">\n                 <normaloff>:/res/images/up.svg</normaloff>:/res/images/up.svg</iconset>\n               </property>\n               <property name=\"autoDefault\">\n                <bool>false</bool>\n               </property>\n               <property name=\"flat\">\n                <bool>true</bool>\n               </property>\n               <property name=\"class\" stdset=\"0\">\n                <string notr=\"true\">btn-tool icon-up</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"moveAsyncFilterDown\">\n               <property name=\"maximumSize\">\n                <size>\n                 <width>22</width>\n                 <height>22</height>\n                </size>\n               </property>\n               <property name=\"accessibleName\">\n                <string>MoveDown</string>\n               </property>\n               <property name=\"text\">\n                <string notr=\"true\"/>\n               </property>\n               <property name=\"icon\">\n                <iconset resource=\"obs.qrc\">\n                 <normaloff>:/res/images/down.svg</normaloff>:/res/images/down.svg</iconset>\n               </property>\n               <property name=\"autoDefault\">\n                <bool>false</bool>\n               </property>\n               <property name=\"flat\">\n                <bool>true</bool>\n               </property>\n               <property name=\"class\" stdset=\"0\">\n                <string notr=\"true\">btn-tool icon-down</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <spacer name=\"asyncToolbarSpacer\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>20</width>\n                 <height>0</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n            </layout>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"Line\" name=\"separatorLine\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QFrame\" name=\"effectWidget\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"minimumSize\">\n          <size>\n           <width>255</width>\n           <height>0</height>\n          </size>\n         </property>\n         <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n          <property name=\"leftMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"topMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"rightMargin\">\n           <number>0</number>\n          </property>\n          <property name=\"bottomMargin\">\n           <number>0</number>\n          </property>\n          <item>\n           <widget class=\"QLabel\" name=\"label_2\">\n            <property name=\"text\">\n             <string>Basic.Filters.EffectFilters</string>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"FocusList\" name=\"effectFilters\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"contextMenuPolicy\">\n             <enum>Qt::CustomContextMenu</enum>\n            </property>\n            <property name=\"dragEnabled\">\n             <bool>true</bool>\n            </property>\n            <property name=\"dragDropMode\">\n             <enum>QAbstractItemView::InternalMove</enum>\n            </property>\n            <property name=\"defaultDropAction\">\n             <enum>Qt::TargetMoveAction</enum>\n            </property>\n            <property name=\"spacing\">\n             <number>1</number>\n            </property>\n           </widget>\n          </item>\n          <item>\n           <widget class=\"QFrame\" name=\"widget_2\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\">\n             <property name=\"spacing\">\n              <number>4</number>\n             </property>\n             <property name=\"leftMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"rightMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QPushButton\" name=\"addEffectFilter\">\n               <property name=\"maximumSize\">\n                <size>\n                 <width>22</width>\n                 <height>22</height>\n                </size>\n               </property>\n               <property name=\"accessibleName\">\n                <string>Add</string>\n               </property>\n               <property name=\"text\">\n                <string notr=\"true\"/>\n               </property>\n               <property name=\"icon\">\n                <iconset resource=\"obs.qrc\">\n                 <normaloff>:/res/images/plus.svg</normaloff>:/res/images/plus.svg</iconset>\n               </property>\n               <property name=\"autoDefault\">\n                <bool>false</bool>\n               </property>\n               <property name=\"flat\">\n                <bool>true</bool>\n               </property>\n               <property name=\"class\" stdset=\"0\">\n                <string notr=\"true\">btn-tool icon-plus</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"removeEffectFilter\">\n               <property name=\"maximumSize\">\n                <size>\n                 <width>22</width>\n                 <height>22</height>\n                </size>\n               </property>\n               <property name=\"accessibleName\">\n                <string>Remove</string>\n               </property>\n               <property name=\"text\">\n                <string notr=\"true\"/>\n               </property>\n               <property name=\"icon\">\n                <iconset resource=\"obs.qrc\">\n                 <normaloff>:/res/images/minus.svg</normaloff>:/res/images/minus.svg</iconset>\n               </property>\n               <property name=\"autoDefault\">\n                <bool>false</bool>\n               </property>\n               <property name=\"flat\">\n                <bool>true</bool>\n               </property>\n               <property name=\"class\" stdset=\"0\">\n                <string notr=\"true\">btn-tool icon-trash</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"moveEffectFilterUp\">\n               <property name=\"maximumSize\">\n                <size>\n                 <width>22</width>\n                 <height>22</height>\n                </size>\n               </property>\n               <property name=\"accessibleName\">\n                <string>MoveUp</string>\n               </property>\n               <property name=\"text\">\n                <string notr=\"true\"/>\n               </property>\n               <property name=\"icon\">\n                <iconset resource=\"obs.qrc\">\n                 <normaloff>:/res/images/up.svg</normaloff>:/res/images/up.svg</iconset>\n               </property>\n               <property name=\"autoDefault\">\n                <bool>false</bool>\n               </property>\n               <property name=\"flat\">\n                <bool>true</bool>\n               </property>\n               <property name=\"class\" stdset=\"0\">\n                <string notr=\"true\">btn-tool icon-up</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"moveEffectFilterDown\">\n               <property name=\"maximumSize\">\n                <size>\n                 <width>22</width>\n                 <height>22</height>\n                </size>\n               </property>\n               <property name=\"accessibleName\">\n                <string>MoveDown</string>\n               </property>\n               <property name=\"text\">\n                <string notr=\"true\"/>\n               </property>\n               <property name=\"icon\">\n                <iconset resource=\"obs.qrc\">\n                 <normaloff>:/res/images/down.svg</normaloff>:/res/images/down.svg</iconset>\n               </property>\n               <property name=\"autoDefault\">\n                <bool>false</bool>\n               </property>\n               <property name=\"flat\">\n                <bool>true</bool>\n               </property>\n               <property name=\"class\" stdset=\"0\">\n                <string notr=\"true\">btn-tool icon-down</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <spacer name=\"effectToolbarSpacer\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeType\">\n                <enum>QSizePolicy::Expanding</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>20</width>\n                 <height>0</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n            </layout>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n      </layout>\n     </item>\n     <item>\n      <widget class=\"QFrame\" name=\"rightContainerLayout\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>200</width>\n         <height>0</height>\n        </size>\n       </property>\n       <property name=\"frameShape\">\n        <enum>QFrame::NoFrame</enum>\n       </property>\n       <property name=\"frameShadow\">\n        <enum>QFrame::Plain</enum>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout_6\">\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"QSplitter\" name=\"rightLayout\">\n          <property name=\"minimumSize\">\n           <size>\n            <width>0</width>\n            <height>400</height>\n           </size>\n          </property>\n          <property name=\"orientation\">\n           <enum>Qt::Vertical</enum>\n          </property>\n          <property name=\"childrenCollapsible\">\n           <bool>false</bool>\n          </property>\n          <widget class=\"QFrame\" name=\"previewFrame\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <layout class=\"QVBoxLayout\" name=\"verticalLayout_7\">\n            <property name=\"spacing\">\n             <number>0</number>\n            </property>\n            <property name=\"leftMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"topMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"rightMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"bottomMargin\">\n             <number>0</number>\n            </property>\n            <item>\n             <widget class=\"OBSQTDisplay\" name=\"preview\" native=\"true\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n              <property name=\"minimumSize\">\n               <size>\n                <width>0</width>\n                <height>150</height>\n               </size>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </widget>\n          <widget class=\"QFrame\" name=\"propertiesFrame\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <layout class=\"QVBoxLayout\" name=\"propertiesLayout\">\n            <property name=\"leftMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"topMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"rightMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"bottomMargin\">\n             <number>0</number>\n            </property>\n           </layout>\n          </widget>\n         </widget>\n        </item>\n        <item>\n         <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n          <property name=\"spacing\">\n           <number>4</number>\n          </property>\n          <property name=\"sizeConstraint\">\n           <enum>QLayout::SetMaximumSize</enum>\n          </property>\n          <item>\n           <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n            <property name=\"standardButtons\">\n             <set>QDialogButtonBox::Close|QDialogButtonBox::RestoreDefaults</set>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n  <action name=\"actionRemoveFilter\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/minus.svg</normaloff>:/res/images/minus.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Remove</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Del</string>\n   </property>\n   <property name=\"iconVisibleInMenu\">\n    <bool>false</bool>\n   </property>\n  </action>\n  <action name=\"actionMoveUp\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/up.svg</normaloff>:/res/images/up.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Order.MoveUp</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Up</string>\n   </property>\n  </action>\n  <action name=\"actionMoveDown\">\n   <property name=\"icon\">\n    <iconset resource=\"obs.qrc\">\n     <normaloff>:/res/images/down.svg</normaloff>:/res/images/down.svg</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Basic.MainMenu.Edit.Order.MoveDown</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Down</string>\n   </property>\n  </action>\n  <action name=\"actionRenameFilter\">\n   <property name=\"text\">\n    <string>Rename</string>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>OBSQTDisplay</class>\n   <extends>QWidget</extends>\n   <header>widgets/OBSQTDisplay.hpp</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>FocusList</class>\n   <extends>QListWidget</extends>\n   <header>components/FocusList.hpp</header>\n  </customwidget>\n </customwidgets>\n <resources>\n  <include location=\"obs.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSBasicInteraction.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSBasicInteraction</class>\n <widget class=\"QDialog\" name=\"OBSBasicInteraction\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>664</width>\n    <height>562</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Dialog</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"OBSQTDisplay\" name=\"preview\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>20</width>\n       <height>20</height>\n      </size>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>OBSQTDisplay</class>\n   <extends>QWidget</extends>\n   <header>widgets/OBSQTDisplay.hpp</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSBasicProperties.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSBasicProperties</class>\n <widget class=\"QDialog\" name=\"OBSBasicProperties\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>720</width>\n    <height>580</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Properties</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QSplitter\" name=\"windowSplitter\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>400</height>\n      </size>\n     </property>\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"childrenCollapsible\">\n      <bool>false</bool>\n     </property>\n     <widget class=\"QFrame\" name=\"previewFrame\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n        <horstretch>0</horstretch>\n        <verstretch>1</verstretch>\n       </sizepolicy>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"frameShadow\">\n       <enum>QFrame::Plain</enum>\n      </property>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_7\">\n       <property name=\"spacing\">\n        <number>0</number>\n       </property>\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n       <item>\n        <widget class=\"OBSQTDisplay\" name=\"preview\" native=\"true\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n           <horstretch>0</horstretch>\n           <verstretch>1</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"minimumSize\">\n          <size>\n           <width>20</width>\n           <height>150</height>\n          </size>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QFrame\" name=\"propertiesFrame\">\n      <property name=\"sizePolicy\">\n       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n        <horstretch>0</horstretch>\n        <verstretch>1</verstretch>\n       </sizepolicy>\n      </property>\n      <property name=\"frameShape\">\n       <enum>QFrame::NoFrame</enum>\n      </property>\n      <property name=\"frameShadow\">\n       <enum>QFrame::Plain</enum>\n      </property>\n      <layout class=\"QVBoxLayout\" name=\"propertiesLayout\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"frame\">\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>0</number>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n      <property name=\"spacing\">\n       <number>6</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QPushButton\" name=\"defaultsButton\">\n        <property name=\"text\">\n         <string>RestoreDefaults</string>\n        </property>\n        <property name=\"autoDefault\">\n         <bool>false</bool>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QPushButton\" name=\"transitionButton\">\n        <property name=\"text\">\n         <string>PreviewTransition</string>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n        <property name=\"standardButtons\">\n         <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>OBSQTDisplay</class>\n   <extends>QWidget</extends>\n   <header>widgets/OBSQTDisplay.hpp</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources>\n  <include location=\"obs.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSBasicSettings.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSBasicSettings</class>\n <widget class=\"QDialog\" name=\"OBSBasicSettings\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>981</width>\n    <height>730</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>700</width>\n    <height>512</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Settings</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_32\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <property name=\"spacing\">\n      <number>0</number>\n     </property>\n     <item>\n      <widget class=\"QListWidget\" name=\"listWidget\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Expanding\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"maximumSize\">\n        <size>\n         <width>180</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"iconSize\">\n        <size>\n         <width>16</width>\n         <height>16</height>\n        </size>\n       </property>\n       <property name=\"spacing\">\n        <number>1</number>\n       </property>\n       <property name=\"currentRow\">\n        <number>0</number>\n       </property>\n       <item>\n        <property name=\"text\">\n         <string>Basic.Settings.General</string>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"obs.qrc\">\n          <normaloff>:/settings/images/settings/general.svg</normaloff>:/settings/images/settings/general.svg</iconset>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Basic.Settings.Appearance</string>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"obs.qrc\">\n          <normaloff>:/settings/images/settings/appearance.svg</normaloff>:/settings/images/settings/appearance.svg</iconset>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Basic.Settings.Stream</string>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"obs.qrc\">\n          <normaloff>:/settings/images/settings/stream.svg</normaloff>:/settings/images/settings/stream.svg</iconset>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Basic.Settings.Output</string>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"obs.qrc\">\n          <normaloff>:/settings/images/settings/output.svg</normaloff>:/settings/images/settings/output.svg</iconset>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Basic.Settings.Audio</string>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"obs.qrc\">\n          <normaloff>:/settings/images/settings/audio.svg</normaloff>:/settings/images/settings/audio.svg</iconset>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Basic.Settings.Video</string>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"obs.qrc\">\n          <normaloff>:/settings/images/settings/video.svg</normaloff>:/settings/images/settings/video.svg</iconset>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Basic.Settings.Hotkeys</string>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"obs.qrc\">\n          <normaloff>:/settings/images/settings/hotkeys.svg</normaloff>:/settings/images/settings/hotkeys.svg</iconset>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Basic.Settings.Accessibility</string>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"obs.qrc\">\n          <normaloff>:/settings/images/settings/accessibility.svg</normaloff>:/settings/images/settings/accessibility.svg</iconset>\n        </property>\n       </item>\n       <item>\n        <property name=\"text\">\n         <string>Basic.Settings.Advanced</string>\n        </property>\n        <property name=\"icon\">\n         <iconset resource=\"obs.qrc\">\n          <normaloff>:/settings/images/settings/advanced.svg</normaloff>:/settings/images/settings/advanced.svg</iconset>\n        </property>\n       </item>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QStackedWidget\" name=\"settingsPages\">\n       <property name=\"currentIndex\">\n        <number>0</number>\n       </property>\n       <widget class=\"QWidget\" name=\"generalPage\">\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_18\">\n         <property name=\"leftMargin\">\n          <number>9</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QScrollArea\" name=\"scrollArea_2\">\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <property name=\"lineWidth\">\n            <number>0</number>\n           </property>\n           <property name=\"widgetResizable\">\n            <bool>true</bool>\n           </property>\n           <widget class=\"QWidget\" name=\"scrollAreaWidgetContents_2\">\n            <property name=\"geometry\">\n             <rect>\n              <x>0</x>\n              <y>0</y>\n              <width>755</width>\n              <height>1260</height>\n             </rect>\n            </property>\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout_19\">\n             <property name=\"leftMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"rightMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QFrame\" name=\"widget_2\">\n               <layout class=\"QVBoxLayout\" name=\"verticalLayout_20\">\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_15\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.General</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_32\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.Language</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>language</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"language\"/>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_3\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>5</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"openStatsOnStartup\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.OpenStatsOnStartup</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"hideOBSFromCapture\">\n                     <property name=\"toolTip\">\n                      <string>Basic.Settings.General.HideOBSWindowsFromCapture.Tooltip</string>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.HideOBSWindowsFromCapture</string>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"updateSettingsGroupBox\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.General.Updater</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_20\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"updateChannelLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.UpdateChannel</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"updateChannelBox\">\n                     <property name=\"editable\">\n                      <bool>false</bool>\n                     </property>\n                     <property name=\"currentText\">\n                      <string/>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"enableAutoUpdates\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.EnableAutoUpdates</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_29\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_16\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Output</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_2\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_5\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>5</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"warnBeforeStreamStart\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.WarnBeforeStartingStream</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"warnBeforeStreamStop\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.WarnBeforeStoppingStream</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"warnBeforeRecordStop\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.WarnBeforeStoppingRecord</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"recordWhenStreaming\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.RecordWhenStreaming</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"4\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"keepRecordStreamStops\">\n                     <property name=\"enabled\">\n                      <bool>false</bool>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.KeepRecordingWhenStreamStops</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"5\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"replayWhileStreaming\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.ReplayBufferWhileStreaming</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"6\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"keepReplayStreamStops\">\n                     <property name=\"enabled\">\n                      <bool>false</bool>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.KeepReplayBufferStreamStops</string>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_10\">\n                  <property name=\"enabled\">\n                   <bool>true</bool>\n                  </property>\n                  <property name=\"title\">\n                   <string>Basic.Settings.General.Snapping</string>\n                  </property>\n                  <property name=\"flat\">\n                   <bool>false</bool>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_21\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"snappingEnabled\">\n                     <property name=\"text\">\n                      <string>Enable</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"screenSnapping\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.ScreenSnapping</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"4\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"centerSnapping\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.CenterSnapping</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"sourceSnapping\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.SourceSnapping</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QDoubleSpinBox\" name=\"snapDistance\">\n                     <property name=\"decimals\">\n                      <number>1</number>\n                     </property>\n                     <property name=\"singleStep\">\n                      <double>0.500000000000000</double>\n                     </property>\n                     <property name=\"value\">\n                      <double>10.000000000000000</double>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_9\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.SnapDistance</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>snapDistance</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_4\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>5</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_14\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.General.Projectors</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_28\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"hideProjectorCursor\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.HideProjectorCursor</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"projectorAlwaysOnTop\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.ProjectorAlwaysOnTop</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"saveProjectors\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.SaveProjectors</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"closeProjectors\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.CloseExistingProjectors</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <spacer name=\"horizontalSpacer\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>5</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_13\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.General.SysTray</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_29\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"systemTrayEnabled\">\n                     <property name=\"text\">\n                      <string>Enable</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"systemTrayWhenStarted\">\n                     <property name=\"enabled\">\n                      <bool>false</bool>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.SysTrayWhenStarted</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"systemTrayAlways\">\n                     <property name=\"enabled\">\n                      <bool>false</bool>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.SystemTrayHideMinimize</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_2\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>5</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_18\">\n                  <property name=\"title\">\n                   <string>StudioMode.Preview</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_35\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"overflowAlwaysVisible\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.OverflowAlwaysVisible</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_25\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>5</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"overflowSelectionHide\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.OverflowSelectionHidden</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"previewSafeAreas\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.Multiview.DrawSafeAreas</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"4\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"previewSpacingHelpers\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.SpacingHelpers</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"overflowHide\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.OverflowHidden</string>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_19\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.General.Importers</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_36\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_26\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>5</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"automaticSearch\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.AutomaticCollectionSearch</string>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_11\">\n                  <property name=\"title\">\n                   <string>Basic.TogglePreviewProgramMode</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_31\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"doubleClickSwitch\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.SwitchOnDoubleClick</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_6\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>5</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"studioPortraitLayout\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.StudioPortraitLayout</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"prevProgLabelToggle\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.TogglePreviewProgramLabels</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBoxMultiview\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.General.Multiview</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayoutMultiview\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <spacer name=\"horizontalSpacerMultiview\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>5</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"multiviewMouseSwitch\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.Multiview.MouseSwitch</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"multiviewDrawNames\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.Multiview.DrawSourceNames</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"multiviewDrawAreas\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.Multiview.DrawSafeAreas</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"multiviewLayout\"/>\n                   </item>\n                   <item row=\"3\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_64\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.General.MultiviewLayout</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>multiviewLayout</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <spacer name=\"verticalSpacer_11\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>20</width>\n                    <height>40</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n               </layout>\n              </widget>\n             </item>\n            </layout>\n           </widget>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"appearancePage\">\n        <layout class=\"QVBoxLayout\" name=\"formLayout_69\">\n         <property name=\"leftMargin\">\n          <number>9</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QScrollArea\" name=\"scrollArea_420\">\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <property name=\"lineWidth\">\n            <number>0</number>\n           </property>\n           <property name=\"widgetResizable\">\n            <bool>true</bool>\n           </property>\n           <widget class=\"QWidget\" name=\"appearancePageContents\">\n            <property name=\"geometry\">\n             <rect>\n              <x>0</x>\n              <y>0</y>\n              <width>772</width>\n              <height>680</height>\n             </rect>\n            </property>\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout_1337\">\n             <property name=\"leftMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"rightMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QFrame\" name=\"appearanceFrame\">\n               <layout class=\"QVBoxLayout\" name=\"verticalLayout_30\">\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"appearanceGeneral\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Appearance</string>\n                  </property>\n                  <property name=\"checkable\">\n                   <bool>false</bool>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_37\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_45\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Appearance.General.Theme</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>theme</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"theme\"/>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_10\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Appearance.General.Variant</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>themeVariant</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"themeVariant\"/>\n                   </item>\n                   <item row=\"2\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"appearanceSettingLabelFontScale\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Appearance.FontScale</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>appearanceFontScale</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QFrame\" name=\"frame_2\">\n                     <property name=\"frameShape\">\n                      <enum>QFrame::NoFrame</enum>\n                     </property>\n                     <property name=\"frameShadow\">\n                      <enum>QFrame::Plain</enum>\n                     </property>\n                     <property name=\"lineWidth\">\n                      <number>0</number>\n                     </property>\n                     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_23\" stretch=\"0,5\">\n                      <property name=\"leftMargin\">\n                       <number>0</number>\n                      </property>\n                      <property name=\"topMargin\">\n                       <number>0</number>\n                      </property>\n                      <property name=\"rightMargin\">\n                       <number>0</number>\n                      </property>\n                      <property name=\"bottomMargin\">\n                       <number>0</number>\n                      </property>\n                      <item>\n                       <widget class=\"QLineEdit\" name=\"appearanceFontScaleText\">\n                        <property name=\"sizePolicy\">\n                         <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                          <horstretch>0</horstretch>\n                          <verstretch>0</verstretch>\n                         </sizepolicy>\n                        </property>\n                        <property name=\"focusPolicy\">\n                         <enum>Qt::NoFocus</enum>\n                        </property>\n                        <property name=\"text\">\n                         <string>10</string>\n                        </property>\n                        <property name=\"alignment\">\n                         <set>Qt::AlignCenter</set>\n                        </property>\n                        <property name=\"readOnly\">\n                         <bool>true</bool>\n                        </property>\n                        <property name=\"clearButtonEnabled\">\n                         <bool>false</bool>\n                        </property>\n                       </widget>\n                      </item>\n                      <item>\n                       <widget class=\"AbsoluteSlider\" name=\"appearanceFontScale\">\n                        <property name=\"sizePolicy\">\n                         <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n                          <horstretch>0</horstretch>\n                          <verstretch>0</verstretch>\n                         </sizepolicy>\n                        </property>\n                        <property name=\"minimum\">\n                         <number>8</number>\n                        </property>\n                        <property name=\"maximum\">\n                         <number>12</number>\n                        </property>\n                        <property name=\"pageStep\">\n                         <number>2</number>\n                        </property>\n                        <property name=\"value\">\n                         <number>10</number>\n                        </property>\n                        <property name=\"orientation\">\n                         <enum>Qt::Horizontal</enum>\n                        </property>\n                        <property name=\"tickPosition\">\n                         <enum>QSlider::TicksBothSides</enum>\n                        </property>\n                        <property name=\"tickInterval\">\n                         <number>1</number>\n                        </property>\n                       </widget>\n                      </item>\n                     </layout>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_20\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Appearance.Density</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <widget class=\"QFrame\" name=\"frame_5\">\n                     <property name=\"frameShape\">\n                      <enum>QFrame::NoFrame</enum>\n                     </property>\n                     <property name=\"frameShadow\">\n                      <enum>QFrame::Plain</enum>\n                     </property>\n                     <property name=\"lineWidth\">\n                      <number>0</number>\n                     </property>\n                     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_34\">\n                      <property name=\"leftMargin\">\n                       <number>0</number>\n                      </property>\n                      <property name=\"topMargin\">\n                       <number>0</number>\n                      </property>\n                      <property name=\"rightMargin\">\n                       <number>0</number>\n                      </property>\n                      <property name=\"bottomMargin\">\n                       <number>0</number>\n                      </property>\n                      <item>\n                       <widget class=\"QPushButton\" name=\"appearanceDensity1\">\n                        <property name=\"text\">\n                         <string>Basic.Settings.Appearance.Classic</string>\n                        </property>\n                        <property name=\"checkable\">\n                         <bool>true</bool>\n                        </property>\n                        <property name=\"autoExclusive\">\n                         <bool>true</bool>\n                        </property>\n                        <attribute name=\"buttonGroup\">\n                         <string notr=\"true\">appearanceDensityButtonGroup</string>\n                        </attribute>\n                       </widget>\n                      </item>\n                      <item>\n                       <widget class=\"QPushButton\" name=\"appearanceDensity2\">\n                        <property name=\"text\">\n                         <string>Basic.Settings.Appearance.Compact</string>\n                        </property>\n                        <property name=\"checkable\">\n                         <bool>true</bool>\n                        </property>\n                        <property name=\"autoExclusive\">\n                         <bool>true</bool>\n                        </property>\n                        <attribute name=\"buttonGroup\">\n                         <string notr=\"true\">appearanceDensityButtonGroup</string>\n                        </attribute>\n                       </widget>\n                      </item>\n                      <item>\n                       <widget class=\"QPushButton\" name=\"appearanceDensity3\">\n                        <property name=\"text\">\n                         <string>Basic.Settings.Appearance.Normal</string>\n                        </property>\n                        <property name=\"checkable\">\n                         <bool>true</bool>\n                        </property>\n                        <property name=\"checked\">\n                         <bool>true</bool>\n                        </property>\n                        <property name=\"autoExclusive\">\n                         <bool>true</bool>\n                        </property>\n                        <attribute name=\"buttonGroup\">\n                         <string notr=\"true\">appearanceDensityButtonGroup</string>\n                        </attribute>\n                       </widget>\n                      </item>\n                      <item>\n                       <widget class=\"QPushButton\" name=\"appearanceDensity4\">\n                        <property name=\"text\">\n                         <string>Basic.Settings.Appearance.Comfortable</string>\n                        </property>\n                        <property name=\"checkable\">\n                         <bool>true</bool>\n                        </property>\n                        <property name=\"autoExclusive\">\n                         <bool>true</bool>\n                        </property>\n                        <attribute name=\"buttonGroup\">\n                         <string notr=\"true\">appearanceDensityButtonGroup</string>\n                        </attribute>\n                       </widget>\n                      </item>\n                     </layout>\n                    </widget>\n                   </item>\n                   <item row=\"4\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_17\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>10</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <spacer name=\"verticalSpacer_8\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>20</width>\n                    <height>40</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n                <item>\n                 <widget class=\"QFrame\" name=\"appearanceOptionsWarning\">\n                  <property name=\"frameShape\">\n                   <enum>QFrame::NoFrame</enum>\n                  </property>\n                  <property name=\"frameShadow\">\n                   <enum>QFrame::Plain</enum>\n                  </property>\n                  <property name=\"lineWidth\">\n                   <number>0</number>\n                  </property>\n                  <layout class=\"QVBoxLayout\" name=\"verticalLayout_35\">\n                   <item>\n                    <widget class=\"QLabel\" name=\"appearanceOptionsWarningLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Appearance.OptionsWarning</string>\n                     </property>\n                     <property name=\"class\" stdset=\"0\">\n                      <string>text-warning</string>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n               </layout>\n              </widget>\n             </item>\n            </layout>\n           </widget>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"streamPage\">\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n         <property name=\"leftMargin\">\n          <number>9</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QFrame\" name=\"widget_5\">\n           <layout class=\"QFormLayout\" name=\"topStreamLayout\">\n            <property name=\"fieldGrowthPolicy\">\n             <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n            </property>\n            <property name=\"labelAlignment\">\n             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n            </property>\n            <property name=\"leftMargin\">\n             <number>9</number>\n            </property>\n            <property name=\"topMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"rightMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"bottomMargin\">\n             <number>0</number>\n            </property>\n            <item row=\"0\" column=\"0\">\n             <widget class=\"QLabel\" name=\"serviceLabel\">\n              <property name=\"minimumSize\">\n               <size>\n                <width>170</width>\n                <height>0</height>\n               </size>\n              </property>\n              <property name=\"text\">\n               <string>Basic.AutoConfig.StreamPage.Service</string>\n              </property>\n              <property name=\"alignment\">\n               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n              </property>\n              <property name=\"buddy\">\n               <cstring>service</cstring>\n              </property>\n             </widget>\n            </item>\n            <item row=\"0\" column=\"1\">\n             <widget class=\"QFrame\" name=\"serviceWidget\">\n              <layout class=\"QHBoxLayout\" name=\"serviceWidgetLayout\" stretch=\"0,0\">\n               <property name=\"leftMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"topMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"rightMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"bottomMargin\">\n                <number>0</number>\n               </property>\n               <item>\n                <widget class=\"QComboBox\" name=\"service\">\n                 <property name=\"maxVisibleItems\">\n                  <number>20</number>\n                 </property>\n                </widget>\n               </item>\n               <item>\n                <widget class=\"UrlPushButton\" name=\"moreInfoButton\">\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"text\">\n                  <string>Basic.AutoConfig.StreamPage.MoreInfo</string>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </widget>\n            </item>\n           </layout>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QStackedWidget\" name=\"streamStackWidget\">\n           <property name=\"currentIndex\">\n            <number>0</number>\n           </property>\n           <widget class=\"QWidget\" name=\"loginPage\">\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout_31\">\n             <property name=\"leftMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"rightMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QGroupBox\" name=\"groupBox_8\">\n               <property name=\"title\">\n                <string>Basic.Settings.Stream.Destination</string>\n               </property>\n               <layout class=\"QFormLayout\" name=\"loginPageLayout\">\n                <property name=\"fieldGrowthPolicy\">\n                 <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                </property>\n                <property name=\"topMargin\">\n                 <number>2</number>\n                </property>\n                <item row=\"0\" column=\"0\">\n                 <spacer name=\"horizontalSpacer_20\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Horizontal</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Fixed</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>170</width>\n                    <height>19</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n                <item row=\"0\" column=\"1\">\n                 <layout class=\"QHBoxLayout\" name=\"horizontalLayout_16\">\n                  <item>\n                   <widget class=\"QPushButton\" name=\"connectAccount\">\n                    <property name=\"text\">\n                     <string>Basic.AutoConfig.StreamPage.ConnectAccount</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item>\n                   <spacer name=\"horizontalSpacer_21\">\n                    <property name=\"orientation\">\n                     <enum>Qt::Horizontal</enum>\n                    </property>\n                    <property name=\"sizeHint\" stdset=\"0\">\n                     <size>\n                      <width>40</width>\n                      <height>10</height>\n                     </size>\n                    </property>\n                   </spacer>\n                  </item>\n                 </layout>\n                </item>\n                <item row=\"1\" column=\"0\">\n                 <spacer name=\"horizontalSpacer_22\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Horizontal</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Fixed</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>170</width>\n                    <height>10</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n                <item row=\"1\" column=\"1\">\n                 <layout class=\"QHBoxLayout\" name=\"horizontalLayout_17\">\n                  <item>\n                   <widget class=\"QPushButton\" name=\"useStreamKey\">\n                    <property name=\"text\">\n                     <string>Basic.AutoConfig.StreamPage.UseStreamKey</string>\n                    </property>\n                   </widget>\n                  </item>\n                  <item>\n                   <spacer name=\"horizontalSpacer_23\">\n                    <property name=\"orientation\">\n                     <enum>Qt::Horizontal</enum>\n                    </property>\n                    <property name=\"sizeHint\" stdset=\"0\">\n                     <size>\n                      <width>40</width>\n                      <height>10</height>\n                     </size>\n                    </property>\n                   </spacer>\n                  </item>\n                 </layout>\n                </item>\n               </layout>\n              </widget>\n             </item>\n             <item>\n              <spacer name=\"verticalSpacer_10\">\n               <property name=\"orientation\">\n                <enum>Qt::Vertical</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>20</width>\n                 <height>0</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n            </layout>\n           </widget>\n           <widget class=\"QScrollArea\" name=\"streamKeyScrollArea\">\n            <property name=\"frameShape\">\n             <enum>QFrame::NoFrame</enum>\n            </property>\n            <property name=\"frameShadow\">\n             <enum>QFrame::Plain</enum>\n            </property>\n            <property name=\"lineWidth\">\n             <number>0</number>\n            </property>\n            <property name=\"widgetResizable\">\n             <bool>true</bool>\n            </property>\n            <widget class=\"QWidget\" name=\"streamKeyPage\">\n             <property name=\"geometry\">\n              <rect>\n               <x>0</x>\n               <y>0</y>\n               <width>987</width>\n               <height>791</height>\n              </rect>\n             </property>\n             <layout class=\"QVBoxLayout\" name=\"streamkeyPageLayout\">\n              <property name=\"leftMargin\">\n               <number>0</number>\n              </property>\n              <property name=\"topMargin\">\n               <number>0</number>\n              </property>\n              <property name=\"rightMargin\">\n               <number>0</number>\n              </property>\n              <property name=\"bottomMargin\">\n               <number>0</number>\n              </property>\n              <item>\n               <widget class=\"QGroupBox\" name=\"destinationGroupBox\">\n                <property name=\"title\">\n                 <string>Basic.Settings.Stream.Destination</string>\n                </property>\n                <layout class=\"QFormLayout\" name=\"destinationLayout\">\n                 <property name=\"fieldGrowthPolicy\">\n                  <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                 </property>\n                 <property name=\"labelAlignment\">\n                  <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                 </property>\n                 <property name=\"leftMargin\">\n                  <number>9</number>\n                 </property>\n                 <property name=\"topMargin\">\n                  <number>2</number>\n                 </property>\n                 <property name=\"rightMargin\">\n                  <number>9</number>\n                 </property>\n                 <property name=\"bottomMargin\">\n                  <number>9</number>\n                 </property>\n                 <item row=\"0\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"serverLabel\">\n                   <property name=\"text\">\n                    <string>Basic.AutoConfig.StreamPage.Server</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"0\" column=\"1\">\n                  <widget class=\"QStackedWidget\" name=\"serverStackedWidget\">\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <property name=\"currentIndex\">\n                    <number>1</number>\n                   </property>\n                   <widget class=\"QWidget\" name=\"servicePage\">\n                    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_21\">\n                     <property name=\"leftMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"topMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"rightMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"bottomMargin\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <widget class=\"QComboBox\" name=\"server\"/>\n                     </item>\n                    </layout>\n                   </widget>\n                   <widget class=\"QWidget\" name=\"customPage\">\n                    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_22\">\n                     <property name=\"leftMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"topMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"rightMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"bottomMargin\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <widget class=\"QLineEdit\" name=\"customServer\"/>\n                     </item>\n                    </layout>\n                   </widget>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"serviceCustomServerLabel\">\n                   <property name=\"text\">\n                    <string>Basic.Settings.Stream.ServiceCustomServer</string>\n                   </property>\n                   <property name=\"buddy\">\n                    <cstring>serviceCustomServer</cstring>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"1\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"serviceCustomServer\"/>\n                 </item>\n                 <item row=\"2\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"streamKeyLabel\">\n                   <property name=\"text\">\n                    <string>Basic.AutoConfig.StreamPage.StreamKey</string>\n                   </property>\n                   <property name=\"openExternalLinks\">\n                    <bool>true</bool>\n                   </property>\n                   <property name=\"buddy\">\n                    <cstring>key</cstring>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"2\" column=\"1\">\n                  <widget class=\"QFrame\" name=\"streamKeyWidget\">\n                   <layout class=\"QHBoxLayout\" name=\"horizontalLayout_11\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLineEdit\" name=\"key\">\n                      <property name=\"inputMask\">\n                       <string notr=\"true\"/>\n                      </property>\n                      <property name=\"text\">\n                       <string notr=\"true\"/>\n                      </property>\n                      <property name=\"echoMode\">\n                       <enum>QLineEdit::Password</enum>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"show\">\n                      <property name=\"text\">\n                       <string>Show</string>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"UrlPushButton\" name=\"getStreamKeyButton\">\n                      <property name=\"toolTip\">\n                       <string/>\n                      </property>\n                      <property name=\"toolTipDuration\">\n                       <number>-4</number>\n                      </property>\n                      <property name=\"text\">\n                       <string>Basic.AutoConfig.StreamPage.GetStreamKey</string>\n                      </property>\n                     </widget>\n                    </item>\n                   </layout>\n                  </widget>\n                 </item>\n                 <item row=\"3\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"connectedAccountLabel\">\n                   <property name=\"text\">\n                    <string>Basic.AutoConfig.StreamPage.ConnectedAccount</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"3\" column=\"1\">\n                  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_15\">\n                   <item>\n                    <widget class=\"QPushButton\" name=\"connectAccount2\">\n                     <property name=\"cursor\">\n                      <cursorShape>PointingHandCursor</cursorShape>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.AutoConfig.StreamPage.ConnectAccount</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <widget class=\"QLabel\" name=\"connectedAccountText\">\n                     <property name=\"styleSheet\">\n                      <string notr=\"true\">font-weight: bold</string>\n                     </property>\n                     <property name=\"text\">\n                      <string>Auth.LoadingChannel.Title</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <widget class=\"QPushButton\" name=\"disconnectAccount\">\n                     <property name=\"text\">\n                      <string>Basic.AutoConfig.StreamPage.DisconnectAccount</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <spacer name=\"horizontalSpacer_19\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>40</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </item>\n                 <item row=\"4\" column=\"0\">\n                  <spacer name=\"horizontalSpacer_18\">\n                   <property name=\"orientation\">\n                    <enum>Qt::Horizontal</enum>\n                   </property>\n                   <property name=\"sizeHint\" stdset=\"0\">\n                    <size>\n                     <width>170</width>\n                     <height>0</height>\n                    </size>\n                   </property>\n                  </spacer>\n                 </item>\n                 <item row=\"5\" column=\"1\">\n                  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_28\">\n                   <item>\n                    <widget class=\"QPushButton\" name=\"useStreamKeyAdv\">\n                     <property name=\"text\">\n                      <string>Basic.AutoConfig.StreamPage.UseStreamKeyAdvanced</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item>\n                    <spacer name=\"horizontalSpacer_28\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>40</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </item>\n                 <item row=\"6\" column=\"1\">\n                  <widget class=\"QCheckBox\" name=\"useAuth\">\n                   <property name=\"text\">\n                    <string>Basic.Settings.Stream.Custom.UseAuthentication</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"7\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"authUsernameLabel\">\n                   <property name=\"text\">\n                    <string>Basic.Settings.Stream.Custom.Username</string>\n                   </property>\n                   <property name=\"buddy\">\n                    <cstring>authUsername</cstring>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"7\" column=\"1\">\n                  <widget class=\"QLineEdit\" name=\"authUsername\"/>\n                 </item>\n                 <item row=\"8\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"authPwLabel\">\n                   <property name=\"text\">\n                    <string>Basic.Settings.Stream.Custom.Password</string>\n                   </property>\n                   <property name=\"buddy\">\n                    <cstring>authPw</cstring>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"8\" column=\"1\">\n                  <widget class=\"QFrame\" name=\"authPwWidget\">\n                   <layout class=\"QHBoxLayout\" name=\"horizontalLayout_25\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLineEdit\" name=\"authPw\">\n                      <property name=\"echoMode\">\n                       <enum>QLineEdit::Password</enum>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"authPwShow\">\n                      <property name=\"text\">\n                       <string>Show</string>\n                      </property>\n                     </widget>\n                    </item>\n                   </layout>\n                  </widget>\n                 </item>\n                </layout>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QGroupBox\" name=\"multitrackVideoGroupBox\">\n                <property name=\"title\">\n                 <string>Basic.Settings.Stream.MultitrackVideoLabel</string>\n                </property>\n                <layout class=\"QVBoxLayout\" name=\"verticalLayout_34\">\n                 <property name=\"leftMargin\">\n                  <number>9</number>\n                 </property>\n                 <property name=\"topMargin\">\n                  <number>2</number>\n                 </property>\n                 <property name=\"rightMargin\">\n                  <number>9</number>\n                 </property>\n                 <property name=\"bottomMargin\">\n                  <number>9</number>\n                 </property>\n                 <item>\n                  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_12\">\n                   <item>\n                    <spacer name=\"horizontalSpacer_31\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>10</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item>\n                    <widget class=\"QLabel\" name=\"multitrackVideoInfo\">\n                     <property name=\"text\">\n                      <string>MultitrackVideo.Info</string>\n                     </property>\n                     <property name=\"textFormat\">\n                      <enum>Qt::RichText</enum>\n                     </property>\n                     <property name=\"wordWrap\">\n                      <bool>true</bool>\n                     </property>\n                     <property name=\"openExternalLinks\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </item>\n                 <item>\n                  <layout class=\"QFormLayout\" name=\"formLayout_27\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_30\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>10</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"enableMultitrackVideo\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Stream.EnableMultitrackVideo</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"multitrackVideoMaximumAggregateBitrateLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrate</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_31\" stretch=\"0,0\">\n                     <item>\n                      <widget class=\"QCheckBox\" name=\"multitrackVideoMaximumAggregateBitrateAuto\">\n                       <property name=\"text\">\n                        <string>Basic.Settings.Stream.MultitrackVideoMaximumAggregateBitrateAuto</string>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QSpinBox\" name=\"multitrackVideoMaximumAggregateBitrate\">\n                       <property name=\"sizePolicy\">\n                        <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n                         <horstretch>0</horstretch>\n                         <verstretch>0</verstretch>\n                        </sizepolicy>\n                       </property>\n                       <property name=\"suffix\">\n                        <string notr=\"true\"> Kbps</string>\n                       </property>\n                       <property name=\"minimum\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"maximum\">\n                        <number>1000000</number>\n                       </property>\n                       <property name=\"singleStep\">\n                        <number>50</number>\n                       </property>\n                       <property name=\"value\">\n                        <number>0</number>\n                       </property>\n                      </widget>\n                     </item>\n                    </layout>\n                   </item>\n                   <item row=\"2\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"multitrackVideoMaximumVideoTracksLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Stream.MultitrackVideoMaximumVideoTracks</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_32\" stretch=\"0,0\">\n                     <item>\n                      <widget class=\"QCheckBox\" name=\"multitrackVideoMaximumVideoTracksAuto\">\n                       <property name=\"sizePolicy\">\n                        <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                         <horstretch>0</horstretch>\n                         <verstretch>0</verstretch>\n                        </sizepolicy>\n                       </property>\n                       <property name=\"text\">\n                        <string>Basic.Settings.Stream.MultitrackVideoMaximumVideoTracksAuto</string>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QSpinBox\" name=\"multitrackVideoMaximumVideoTracks\">\n                       <property name=\"sizePolicy\">\n                        <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n                         <horstretch>0</horstretch>\n                         <verstretch>0</verstretch>\n                        </sizepolicy>\n                       </property>\n                       <property name=\"minimum\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"maximum\">\n                        <number>100</number>\n                       </property>\n                       <property name=\"value\">\n                        <number>0</number>\n                       </property>\n                      </widget>\n                     </item>\n                    </layout>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"multitrackVideoAdditionalCanvas\">\n                     <property name=\"placeholderText\">\n                      <string>None</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"4\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_32\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>10</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"4\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"multitrackVideoStreamDumpEnable\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Stream.MultitrackVideoStreamDumpEnable</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"5\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"multitrackVideoConfigOverrideEnable\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Stream.MultitrackVideoConfigOverrideEnable</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"6\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"multitrackVideoConfigOverrideLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Stream.MultitrackVideoConfigOverride</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"6\" column=\"1\">\n                    <widget class=\"QPlainTextEdit\" name=\"multitrackVideoConfigOverride\">\n                     <property name=\"sizePolicy\">\n                      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n                       <horstretch>0</horstretch>\n                       <verstretch>0</verstretch>\n                      </sizepolicy>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"multitrackVideoAdditionalCanvasLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Stream.MultitrackVideoExtraCanvas</string>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </item>\n                </layout>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QGroupBox\" name=\"whipSimulcastGroupBox\">\n                <property name=\"title\">\n                 <string>Basic.Settings.Stream.WHIPSimulcastLabel</string>\n                </property>\n                <layout class=\"QVBoxLayout\" name=\"verticalLayout_35\">\n                 <property name=\"leftMargin\">\n                  <number>9</number>\n                 </property>\n                 <property name=\"topMargin\">\n                  <number>2</number>\n                 </property>\n                 <property name=\"rightMargin\">\n                  <number>9</number>\n                 </property>\n                 <property name=\"bottomMargin\">\n                  <number>9</number>\n                 </property>\n                 <item>\n                  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_33\">\n                   <item>\n                    <spacer name=\"horizontalSpacer_33\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Fixed</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>10</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item>\n                    <widget class=\"QLabel\" name=\"whipSimulcastInfo\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Stream.WHIPSimulcastInfo</string>\n                     </property>\n                     <property name=\"textFormat\">\n                      <enum>Qt::RichText</enum>\n                     </property>\n                     <property name=\"wordWrap\">\n                      <bool>true</bool>\n                     </property>\n                     <property name=\"openExternalLinks\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </item>\n                 <item>\n                  <layout class=\"QFormLayout\" name=\"formLayout_39\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"whipSimulcastTotalLayersLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Stream.WHIPSimulcastTotalLayers</string>\n                     </property>\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>170</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_35\" stretch=\"0,0\">\n                     <item>\n                      <widget class=\"QSpinBox\" name=\"whipSimulcastTotalLayers\">\n                       <property name=\"minimum\">\n                        <number>1</number>\n                       </property>\n                       <property name=\"maximum\">\n                        <number>4</number>\n                       </property>\n                       <property name=\"value\">\n                        <number>1</number>\n                       </property>\n                      </widget>\n                     </item>\n                    </layout>\n                   </item>\n                  </layout>\n                 </item>\n                </layout>\n               </widget>\n               </item>\n              <item>\n               <widget class=\"QGroupBox\" name=\"serviceAdvancedOptionsGroupBox\">\n                <property name=\"title\">\n                 <string>Basic.Settings.Stream.AdvancedOptions</string>\n                </property>\n                <layout class=\"QFormLayout\" name=\"serviceAdvancedOptionsLayout\">\n                 <property name=\"fieldGrowthPolicy\">\n                  <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                 </property>\n                 <property name=\"labelAlignment\">\n                  <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                 </property>\n                 <property name=\"leftMargin\">\n                  <number>9</number>\n                 </property>\n                 <property name=\"topMargin\">\n                  <number>2</number>\n                 </property>\n                 <property name=\"rightMargin\">\n                  <number>9</number>\n                 </property>\n                 <property name=\"bottomMargin\">\n                  <number>9</number>\n                 </property>\n                 <item row=\"0\" column=\"0\">\n                  <widget class=\"QLabel\" name=\"twitchAddonLabel\">\n                   <property name=\"text\">\n                    <string>Basic.Settings.Stream.TTVAddon</string>\n                   </property>\n                   <property name=\"buddy\">\n                    <cstring>twitchAddonDropdown</cstring>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"0\" column=\"1\">\n                  <widget class=\"QComboBox\" name=\"twitchAddonDropdown\"/>\n                 </item>\n                 <item row=\"1\" column=\"0\">\n                  <spacer name=\"horizontalSpacer_18_1\">\n                   <property name=\"orientation\">\n                    <enum>Qt::Horizontal</enum>\n                   </property>\n                   <property name=\"sizeHint\" stdset=\"0\">\n                    <size>\n                     <width>170</width>\n                     <height>0</height>\n                    </size>\n                   </property>\n                  </spacer>\n                 </item>\n                 <item row=\"1\" column=\"1\">\n                  <widget class=\"QCheckBox\" name=\"bandwidthTestEnable\">\n                   <property name=\"text\">\n                    <string>Basic.Settings.Stream.BandwidthTestMode</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"2\" column=\"1\">\n                  <widget class=\"QCheckBox\" name=\"ignoreRecommended\">\n                   <property name=\"text\">\n                    <string>Basic.Settings.Stream.IgnoreRecommended</string>\n                   </property>\n                  </widget>\n                 </item>\n                 <item row=\"3\" column=\"1\">\n                  <widget class=\"QLabel\" name=\"enforceSettingsLabel\">\n                   <property name=\"text\">\n                    <string notr=\"true\"/>\n                   </property>\n                   <property name=\"textFormat\">\n                    <enum>Qt::RichText</enum>\n                   </property>\n                   <property name=\"openExternalLinks\">\n                    <bool>true</bool>\n                   </property>\n                  </widget>\n                 </item>\n                </layout>\n               </widget>\n              </item>\n              <item>\n               <spacer name=\"verticalSpacer_12\">\n                <property name=\"orientation\">\n                 <enum>Qt::Vertical</enum>\n                </property>\n                <property name=\"sizeHint\" stdset=\"0\">\n                 <size>\n                  <width>20</width>\n                  <height>0</height>\n                 </size>\n                </property>\n               </spacer>\n              </item>\n             </layout>\n            </widget>\n           </widget>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"outputPage\">\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n         <property name=\"leftMargin\">\n          <number>9</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QFrame\" name=\"frame\">\n           <layout class=\"QVBoxLayout\" name=\"verticalLayout_6\">\n            <property name=\"leftMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"topMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"rightMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"bottomMargin\">\n             <number>0</number>\n            </property>\n            <item>\n             <widget class=\"QFrame\" name=\"multitrackVideoNoticeBox\">\n              <property name=\"class\" stdset=\"0\">\n               <string notr=\"true\">frame-notice</string>\n              </property>\n              <layout class=\"QVBoxLayout\" name=\"multitrackVideoNoticeBoxLayout\">\n               <property name=\"leftMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"topMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"rightMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"bottomMargin\">\n                <number>0</number>\n               </property>\n               <item>\n                <widget class=\"QLabel\" name=\"multitrackVideoNotice\">\n                 <property name=\"text\">\n                  <string>Basic.Settings.MultitrackVideoDisabledSettings</string>\n                 </property>\n                 <property name=\"alignment\">\n                  <set>Qt::AlignCenter</set>\n                 </property>\n                </widget>\n               </item>\n              </layout>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QFrame\" name=\"widget\">\n              <layout class=\"QFormLayout\" name=\"formLayout_5\">\n               <property name=\"fieldGrowthPolicy\">\n                <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n               </property>\n               <property name=\"leftMargin\">\n                <number>9</number>\n               </property>\n               <property name=\"topMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"rightMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"bottomMargin\">\n                <number>0</number>\n               </property>\n               <item row=\"0\" column=\"0\">\n                <widget class=\"QLabel\" name=\"outputModeLabel\">\n                 <property name=\"minimumSize\">\n                  <size>\n                   <width>170</width>\n                   <height>0</height>\n                  </size>\n                 </property>\n                 <property name=\"text\">\n                  <string>Basic.Settings.Output.Mode</string>\n                 </property>\n                 <property name=\"alignment\">\n                  <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                 </property>\n                 <property name=\"buddy\">\n                  <cstring>outputMode</cstring>\n                 </property>\n                </widget>\n               </item>\n               <item row=\"0\" column=\"1\">\n                <widget class=\"QComboBox\" name=\"outputMode\">\n                 <property name=\"enabled\">\n                  <bool>true</bool>\n                 </property>\n                 <property name=\"sizePolicy\">\n                  <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n                   <horstretch>0</horstretch>\n                   <verstretch>0</verstretch>\n                  </sizepolicy>\n                 </property>\n                 <property name=\"currentIndex\">\n                  <number>0</number>\n                 </property>\n                 <item>\n                  <property name=\"text\">\n                   <string>Basic.Settings.Output.Mode.Simple</string>\n                  </property>\n                 </item>\n                 <item>\n                  <property name=\"text\">\n                   <string>Basic.Settings.Output.Mode.Adv</string>\n                  </property>\n                 </item>\n                </widget>\n               </item>\n              </layout>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QStackedWidget\" name=\"outputModePages\">\n              <property name=\"currentIndex\">\n               <number>0</number>\n              </property>\n              <widget class=\"QWidget\" name=\"easyOutputsPage\">\n               <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item>\n                 <widget class=\"QScrollArea\" name=\"simpleOutScroll\">\n                  <property name=\"frameShape\">\n                   <enum>QFrame::NoFrame</enum>\n                  </property>\n                  <property name=\"frameShadow\">\n                   <enum>QFrame::Plain</enum>\n                  </property>\n                  <property name=\"lineWidth\">\n                   <number>0</number>\n                  </property>\n                  <property name=\"widgetResizable\">\n                   <bool>true</bool>\n                  </property>\n                  <widget class=\"QWidget\" name=\"scrollAreaWidgetContents_3\">\n                   <property name=\"geometry\">\n                    <rect>\n                     <x>0</x>\n                     <y>0</y>\n                     <width>755</width>\n                     <height>609</height>\n                    </rect>\n                   </property>\n                   <property name=\"sizePolicy\">\n                    <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                     <horstretch>0</horstretch>\n                     <verstretch>0</verstretch>\n                    </sizepolicy>\n                   </property>\n                   <layout class=\"QVBoxLayout\" name=\"verticalLayout_52\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QGroupBox\" name=\"simpleStreamingGroupBox\">\n                      <property name=\"title\">\n                       <string>Basic.Settings.Output.Adv.Streaming</string>\n                      </property>\n                      <layout class=\"QFormLayout\" name=\"simpleStreamingLayout\">\n                       <property name=\"fieldGrowthPolicy\">\n                        <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                       </property>\n                       <property name=\"labelAlignment\">\n                        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                       </property>\n                       <property name=\"leftMargin\">\n                        <number>9</number>\n                       </property>\n                       <property name=\"topMargin\">\n                        <number>2</number>\n                       </property>\n                       <property name=\"rightMargin\">\n                        <number>9</number>\n                       </property>\n                       <property name=\"bottomMargin\">\n                        <number>9</number>\n                       </property>\n                       <item row=\"0\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simpleOutputVBitrateLabel\">\n                         <property name=\"minimumSize\">\n                          <size>\n                           <width>170</width>\n                           <height>0</height>\n                          </size>\n                         </property>\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.VideoBitrate</string>\n                         </property>\n                         <property name=\"alignment\">\n                          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutputVBitrate</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"0\" column=\"1\">\n                        <widget class=\"QSpinBox\" name=\"simpleOutputVBitrate\">\n                         <property name=\"minimum\">\n                          <number>200</number>\n                         </property>\n                         <property name=\"maximum\">\n                          <number>1000000</number>\n                         </property>\n                         <property name=\"value\">\n                          <number>2000</number>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"1\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simpleOutputABitrateLabel\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.AudioBitrate</string>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutputABitrate</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"1\" column=\"1\">\n                        <widget class=\"QComboBox\" name=\"simpleOutputABitrate\">\n                         <property name=\"currentIndex\">\n                          <number>8</number>\n                         </property>\n                         <item>\n                          <property name=\"text\">\n                           <string>32</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>48</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>64</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>80</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>96</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>112</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>128</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>160</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>192</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>256</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>320</string>\n                          </property>\n                         </item>\n                        </widget>\n                       </item>\n                       <item row=\"2\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simpleOutStrEncoderLabel\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.Encoder.Video</string>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutRecEncoder</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"2\" column=\"1\">\n                        <widget class=\"QComboBox\" name=\"simpleOutStrEncoder\"/>\n                       </item>\n                       <item row=\"3\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simpleOutPresetLabel\">\n                         <property name=\"enabled\">\n                          <bool>true</bool>\n                         </property>\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.EncoderPreset</string>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutPreset</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"3\" column=\"1\">\n                        <widget class=\"QComboBox\" name=\"simpleOutPreset\"/>\n                       </item>\n                       <item row=\"4\" column=\"1\">\n                        <widget class=\"QCheckBox\" name=\"simpleOutAdvanced\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.Advanced</string>\n                         </property>\n                         <property name=\"checked\">\n                          <bool>true</bool>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"5\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simpleOutCustomLabel\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.CustomEncoderSettings</string>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutCustom</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"5\" column=\"1\">\n                        <widget class=\"QLineEdit\" name=\"simpleOutCustom\"/>\n                       </item>\n                       <item row=\"6\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simpleOutStrAEncoderLabel\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.Encoder.Audio</string>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutStrAEncoder</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"6\" column=\"1\">\n                        <widget class=\"QComboBox\" name=\"simpleOutStrAEncoder\"/>\n                       </item>\n                      </layout>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QGroupBox\" name=\"simpleRecordingGroupBox\">\n                      <property name=\"title\">\n                       <string>Basic.Settings.Output.Adv.Recording</string>\n                      </property>\n                      <layout class=\"QFormLayout\" name=\"formLayout_6\">\n                       <property name=\"fieldGrowthPolicy\">\n                        <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                       </property>\n                       <property name=\"labelAlignment\">\n                        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                       </property>\n                       <property name=\"leftMargin\">\n                        <number>9</number>\n                       </property>\n                       <property name=\"topMargin\">\n                        <number>2</number>\n                       </property>\n                       <property name=\"rightMargin\">\n                        <number>9</number>\n                       </property>\n                       <property name=\"bottomMargin\">\n                        <number>9</number>\n                       </property>\n                       <item row=\"0\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label_18\">\n                         <property name=\"minimumSize\">\n                          <size>\n                           <width>170</width>\n                           <height>0</height>\n                          </size>\n                         </property>\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.Simple.SavePath</string>\n                         </property>\n                         <property name=\"alignment\">\n                          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutputPath</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"0\" column=\"1\">\n                        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n                         <item>\n                          <widget class=\"QLineEdit\" name=\"simpleOutputPath\">\n                           <property name=\"enabled\">\n                            <bool>true</bool>\n                           </property>\n                          </widget>\n                         </item>\n                         <item>\n                          <widget class=\"QPushButton\" name=\"simpleOutputBrowse\">\n                           <property name=\"enabled\">\n                            <bool>true</bool>\n                           </property>\n                           <property name=\"text\">\n                            <string>Browse</string>\n                           </property>\n                          </widget>\n                         </item>\n                        </layout>\n                       </item>\n                       <item row=\"1\" column=\"1\">\n                        <widget class=\"QCheckBox\" name=\"simpleNoSpace\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.NoSpaceFileName</string>\n                         </property>\n                         <property name=\"checked\">\n                          <bool>true</bool>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"2\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label_26\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.Simple.RecordingQuality</string>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutRecQuality</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"2\" column=\"1\">\n                        <widget class=\"QComboBox\" name=\"simpleOutRecQuality\"/>\n                       </item>\n                       <item row=\"3\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simpleOutRecFormatLabel\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.Format</string>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutRecFormat</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"3\" column=\"1\">\n                        <widget class=\"QComboBox\" name=\"simpleOutRecFormat\">\n                         <property name=\"editable\">\n                          <bool>false</bool>\n                         </property>\n                         <property name=\"currentText\">\n                          <string/>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"4\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simpleOutRecEncoderLabel\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.Encoder.Video</string>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutRecEncoder</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"4\" column=\"1\">\n                        <widget class=\"QComboBox\" name=\"simpleOutRecEncoder\"/>\n                       </item>\n                       <item row=\"5\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simpleOutRecAEncoderLabel\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.Encoder.Audio</string>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutRecAEncoder</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"5\" column=\"1\">\n                        <widget class=\"QComboBox\" name=\"simpleOutRecAEncoder\"/>\n                       </item>\n                       <item row=\"6\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simplerectrack_label\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.Simple.RecAudioTrack</string>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"6\" column=\"1\">\n                        <widget class=\"QStackedWidget\" name=\"simpleRecTrackWidget\">\n                         <property name=\"sizePolicy\">\n                          <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                           <horstretch>0</horstretch>\n                           <verstretch>0</verstretch>\n                          </sizepolicy>\n                         </property>\n                         <property name=\"currentIndex\">\n                          <number>0</number>\n                         </property>\n                         <widget class=\"QWidget\" name=\"simpleRecTracks\">\n                          <layout class=\"QHBoxLayout\" name=\"horizontalLayoutSimpleTracks\">\n                           <property name=\"leftMargin\">\n                            <number>0</number>\n                           </property>\n                           <property name=\"topMargin\">\n                            <number>0</number>\n                           </property>\n                           <property name=\"rightMargin\">\n                            <number>0</number>\n                           </property>\n                           <property name=\"bottomMargin\">\n                            <number>0</number>\n                           </property>\n                           <item>\n                            <widget class=\"QCheckBox\" name=\"simpleOutRecTrack1\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string notr=\"true\">1</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QCheckBox\" name=\"simpleOutRecTrack2\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string notr=\"true\">2</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QCheckBox\" name=\"simpleOutRecTrack3\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string notr=\"true\">3</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QCheckBox\" name=\"simpleOutRecTrack4\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string notr=\"true\">4</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QCheckBox\" name=\"simpleOutRecTrack5\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string notr=\"true\">5</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QCheckBox\" name=\"simpleOutRecTrack6\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string notr=\"true\">6</string>\n                             </property>\n                            </widget>\n                           </item>\n                          </layout>\n                         </widget>\n                         <widget class=\"QWidget\" name=\"simpleFlvTracks\">\n                          <property name=\"enabled\">\n                           <bool>false</bool>\n                          </property>\n                          <layout class=\"QHBoxLayout\" name=\"horizontalLayoutSimpleFLVTracks\">\n                           <property name=\"leftMargin\">\n                            <number>0</number>\n                           </property>\n                           <property name=\"topMargin\">\n                            <number>0</number>\n                           </property>\n                           <property name=\"rightMargin\">\n                            <number>0</number>\n                           </property>\n                           <property name=\"bottomMargin\">\n                            <number>0</number>\n                           </property>\n                           <item>\n                            <widget class=\"QRadioButton\" name=\"simpleFlvTrack1\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string>1</string>\n                             </property>\n                             <property name=\"checked\">\n                              <bool>true</bool>\n                             </property>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QRadioButton\" name=\"simpleFlvTrack2\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string>2</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QRadioButton\" name=\"simpleFlvTrack3\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string>3</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QRadioButton\" name=\"simpleFlvTrack4\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string>4</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QRadioButton\" name=\"simpleFlvTrack5\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string>5</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QRadioButton\" name=\"simpleFlvTrack6\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"text\">\n                              <string>6</string>\n                             </property>\n                            </widget>\n                           </item>\n                          </layout>\n                         </widget>\n                        </widget>\n                       </item>\n                       <item row=\"7\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label_420\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.CustomMuxerSettings</string>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>simpleOutMuxCustom</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"7\" column=\"1\">\n                        <widget class=\"QLineEdit\" name=\"simpleOutMuxCustom\"/>\n                       </item>\n                      </layout>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QGroupBox\" name=\"simpleReplayBuf\">\n                      <property name=\"title\">\n                       <string>ReplayBuffer</string>\n                      </property>\n                      <property name=\"checkable\">\n                       <bool>true</bool>\n                      </property>\n                      <property name=\"checked\">\n                       <bool>true</bool>\n                      </property>\n                      <layout class=\"QFormLayout\" name=\"formLayout_24\">\n                       <property name=\"fieldGrowthPolicy\">\n                        <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                       </property>\n                       <property name=\"labelAlignment\">\n                        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                       </property>\n                       <property name=\"leftMargin\">\n                        <number>9</number>\n                       </property>\n                       <property name=\"topMargin\">\n                        <number>2</number>\n                       </property>\n                       <property name=\"rightMargin\">\n                        <number>9</number>\n                       </property>\n                       <property name=\"bottomMargin\">\n                        <number>9</number>\n                       </property>\n                       <item row=\"0\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label_35\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.ReplayBuffer.SecondsMax</string>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"0\" column=\"1\">\n                        <widget class=\"QSpinBox\" name=\"simpleRBSecMax\">\n                         <property name=\"suffix\">\n                          <string notr=\"true\"> sec</string>\n                         </property>\n                         <property name=\"minimum\">\n                          <number>5</number>\n                         </property>\n                         <property name=\"maximum\">\n                          <number>21600</number>\n                         </property>\n                         <property name=\"value\">\n                          <number>15</number>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"1\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"simpleRBMegsMaxLabel\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.ReplayBuffer.MegabytesMax</string>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"1\" column=\"1\">\n                        <widget class=\"QSpinBox\" name=\"simpleRBMegsMax\">\n                         <property name=\"suffix\">\n                          <string notr=\"true\"> MB</string>\n                         </property>\n                         <property name=\"minimum\">\n                          <number>20</number>\n                         </property>\n                         <property name=\"maximum\">\n                          <number>8192</number>\n                         </property>\n                         <property name=\"value\">\n                          <number>512</number>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"2\" column=\"1\">\n                        <widget class=\"QLabel\" name=\"simpleRBEstimate\">\n                         <property name=\"text\">\n                          <string notr=\"true\"/>\n                         </property>\n                        </widget>\n                       </item>\n                      </layout>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QFrame\" name=\"simpleOutputContainer\">\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>0</width>\n                        <height>20</height>\n                       </size>\n                      </property>\n                      <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n                       <property name=\"leftMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"topMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"rightMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"bottomMargin\">\n                        <number>0</number>\n                       </property>\n                      </layout>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"verticalSpacer_2\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Vertical</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>20</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </widget>\n                 </widget>\n                </item>\n                <item>\n                 <layout class=\"QVBoxLayout\" name=\"simpleOutInfoLayout\">\n                  <property name=\"leftMargin\">\n                   <number>9</number>\n                  </property>\n                  <property name=\"topMargin\">\n                   <number>9</number>\n                  </property>\n                  <property name=\"rightMargin\">\n                   <number>9</number>\n                  </property>\n                  <property name=\"bottomMargin\">\n                   <number>9</number>\n                  </property>\n                 </layout>\n                </item>\n               </layout>\n              </widget>\n              <widget class=\"QWidget\" name=\"advOutputsPage\">\n               <layout class=\"QVBoxLayout\" name=\"verticalLayout_8\">\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item>\n                 <widget class=\"QTabWidget\" name=\"advOutTabs\">\n                  <property name=\"currentIndex\">\n                   <number>0</number>\n                  </property>\n                  <property name=\"usesScrollButtons\">\n                   <bool>true</bool>\n                  </property>\n                  <widget class=\"QWidget\" name=\"advOutputStreamTab\">\n                   <attribute name=\"title\">\n                    <string>Basic.Settings.Output.Adv.Streaming</string>\n                   </attribute>\n                   <layout class=\"QVBoxLayout\" name=\"verticalLayout_12\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>9</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QScrollArea\" name=\"scrollArea_3\">\n                      <property name=\"frameShape\">\n                       <enum>QFrame::NoFrame</enum>\n                      </property>\n                      <property name=\"frameShadow\">\n                       <enum>QFrame::Plain</enum>\n                      </property>\n                      <property name=\"lineWidth\">\n                       <number>0</number>\n                      </property>\n                      <property name=\"widgetResizable\">\n                       <bool>true</bool>\n                      </property>\n                      <widget class=\"QWidget\" name=\"scrollAreaWidgetContents_5\">\n                       <property name=\"geometry\">\n                        <rect>\n                         <x>0</x>\n                         <y>0</y>\n                         <width>424</width>\n                         <height>175</height>\n                        </rect>\n                       </property>\n                       <layout class=\"QVBoxLayout\" name=\"verticalLayout_14\">\n                        <property name=\"leftMargin\">\n                         <number>0</number>\n                        </property>\n                        <property name=\"topMargin\">\n                         <number>0</number>\n                        </property>\n                        <property name=\"rightMargin\">\n                         <number>0</number>\n                        </property>\n                        <property name=\"bottomMargin\">\n                         <number>0</number>\n                        </property>\n                        <item>\n                         <widget class=\"QGroupBox\" name=\"advOutTopContainer\">\n                          <property name=\"title\">\n                           <string>Basic.Settings.Output.Adv.Streaming.Settings</string>\n                          </property>\n                          <layout class=\"QFormLayout\" name=\"advOutTopLayout\">\n                           <property name=\"fieldGrowthPolicy\">\n                            <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                           </property>\n                           <property name=\"labelAlignment\">\n                            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                           </property>\n                           <property name=\"leftMargin\">\n                            <number>9</number>\n                           </property>\n                           <property name=\"topMargin\">\n                            <number>2</number>\n                           </property>\n                           <property name=\"rightMargin\">\n                            <number>9</number>\n                           </property>\n                           <property name=\"bottomMargin\">\n                            <number>9</number>\n                           </property>\n                           <item row=\"0\" column=\"0\">\n                            <widget class=\"QLabel\" name=\"advStreamTrackWidgetLabel\">\n                             <property name=\"minimumSize\">\n                              <size>\n                               <width>170</width>\n                               <height>0</height>\n                              </size>\n                             </property>\n                             <property name=\"text\">\n                              <string>Basic.Settings.Output.Adv.AudioTrack</string>\n                             </property>\n                             <property name=\"alignment\">\n                              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                             </property>\n                            </widget>\n                           </item>\n                           <item row=\"0\" column=\"1\">\n                            <widget class=\"QStackedWidget\" name=\"advStreamTrackWidget\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"currentIndex\">\n                              <number>0</number>\n                             </property>\n                             <widget class=\"QFrame\" name=\"streamSingleTracks\">\n                              <layout class=\"QHBoxLayout\" name=\"horizontalLayout_7\">\n                               <property name=\"leftMargin\">\n                                <number>0</number>\n                               </property>\n                               <property name=\"topMargin\">\n                                <number>0</number>\n                               </property>\n                               <property name=\"rightMargin\">\n                                <number>0</number>\n                               </property>\n                               <property name=\"bottomMargin\">\n                                <number>0</number>\n                               </property>\n                               <item>\n                                <widget class=\"QRadioButton\" name=\"advOutTrack1\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">1</string>\n                                 </property>\n                                 <property name=\"checked\">\n                                  <bool>true</bool>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item>\n                                <widget class=\"QRadioButton\" name=\"advOutTrack2\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">2</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item>\n                                <widget class=\"QRadioButton\" name=\"advOutTrack3\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">3</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item>\n                                <widget class=\"QRadioButton\" name=\"advOutTrack4\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">4</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item>\n                                <widget class=\"QRadioButton\" name=\"advOutTrack5\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">5</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item>\n                                <widget class=\"QRadioButton\" name=\"advOutTrack6\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">6</string>\n                                 </property>\n                                </widget>\n                               </item>\n                              </layout>\n                             </widget>\n                             <widget class=\"QWidget\" name=\"streamMultiTracks\">\n                              <layout class=\"QHBoxLayout\" name=\"horizontalLayout_multitrack\">\n                               <property name=\"leftMargin\">\n                                <number>0</number>\n                               </property>\n                               <property name=\"topMargin\">\n                                <number>0</number>\n                               </property>\n                               <property name=\"rightMargin\">\n                                <number>0</number>\n                               </property>\n                               <property name=\"bottomMargin\">\n                                <number>0</number>\n                               </property>\n                               <item>\n                                <widget class=\"QCheckBox\" name=\"advOutMultiTrack1\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">1</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item>\n                                <widget class=\"QCheckBox\" name=\"advOutMultiTrack2\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">2</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item>\n                                <widget class=\"QCheckBox\" name=\"advOutMultiTrack3\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">3</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item>\n                                <widget class=\"QCheckBox\" name=\"advOutMultiTrack4\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">4</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item>\n                                <widget class=\"QCheckBox\" name=\"advOutMultiTrack5\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">5</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item>\n                                <widget class=\"QCheckBox\" name=\"advOutMultiTrack6\">\n                                 <property name=\"text\">\n                                  <string notr=\"true\">6</string>\n                                 </property>\n                                </widget>\n                               </item>\n                              </layout>\n                             </widget>\n                            </widget>\n                           </item>\n                           <item row=\"1\" column=\"0\">\n                            <widget class=\"QLabel\" name=\"advOutAEncLabel\">\n                             <property name=\"text\">\n                              <string>Basic.Settings.Output.Encoder.Audio</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item row=\"1\" column=\"1\">\n                            <widget class=\"QComboBox\" name=\"advOutAEncoder\"/>\n                           </item>\n                           <item row=\"2\" column=\"0\">\n                            <widget class=\"QLabel\" name=\"advOutEncLabel\">\n                             <property name=\"text\">\n                              <string>Basic.Settings.Output.Encoder.Video</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item row=\"2\" column=\"1\">\n                            <widget class=\"QComboBox\" name=\"advOutEncoder\"/>\n                           </item>\n                           <item row=\"3\" column=\"0\">\n                            <widget class=\"QLabel\" name=\"advOutUseRescale\">\n                             <property name=\"layoutDirection\">\n                              <enum>Qt::RightToLeft</enum>\n                             </property>\n                             <property name=\"text\">\n                              <string>Basic.Settings.Output.Adv.Rescale</string>\n                             </property>\n                            </widget>\n                           </item>\n                           <item row=\"3\" column=\"1\">\n                            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_10\">\n                             <item>\n                              <widget class=\"QComboBox\" name=\"advOutRescaleFilter\"/>\n                             </item>\n                             <item>\n                              <widget class=\"QComboBox\" name=\"advOutRescale\">\n                               <property name=\"enabled\">\n                                <bool>false</bool>\n                               </property>\n                               <property name=\"editable\">\n                                <bool>true</bool>\n                               </property>\n                              </widget>\n                             </item>\n                            </layout>\n                           </item>\n                          </layout>\n                         </widget>\n                        </item>\n                        <item>\n                         <widget class=\"QGroupBox\" name=\"advOutEncoderProps\">\n                          <property name=\"title\">\n                           <string>Basic.Settings.Output.Adv.Encoder</string>\n                          </property>\n                          <layout class=\"QVBoxLayout\" name=\"advOutEncoderLayout\">\n                           <property name=\"leftMargin\">\n                            <number>8</number>\n                           </property>\n                           <property name=\"topMargin\">\n                            <number>2</number>\n                           </property>\n                           <property name=\"rightMargin\">\n                            <number>8</number>\n                           </property>\n                           <property name=\"bottomMargin\">\n                            <number>8</number>\n                           </property>\n                          </layout>\n                         </widget>\n                        </item>\n                        <item>\n                         <spacer name=\"verticalSpacer_0\">\n                          <property name=\"orientation\">\n                           <enum>Qt::Vertical</enum>\n                          </property>\n                          <property name=\"sizeHint\" stdset=\"0\">\n                           <size>\n                            <width>20</width>\n                            <height>0</height>\n                           </size>\n                          </property>\n                         </spacer>\n                        </item>\n                       </layout>\n                      </widget>\n                     </widget>\n                    </item>\n                   </layout>\n                  </widget>\n                  <widget class=\"QWidget\" name=\"advOutputRecordTab\">\n                   <attribute name=\"title\">\n                    <string>Basic.Settings.Output.Adv.Recording</string>\n                   </attribute>\n                   <layout class=\"QVBoxLayout\" name=\"verticalLayout_11\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>9</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QFrame\" name=\"advOutRecTypeContainer\">\n                      <layout class=\"QFormLayout\" name=\"formLayout_9\">\n                       <property name=\"fieldGrowthPolicy\">\n                        <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                       </property>\n                       <property name=\"labelAlignment\">\n                        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                       </property>\n                       <property name=\"leftMargin\">\n                        <number>9</number>\n                       </property>\n                       <property name=\"topMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"rightMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"bottomMargin\">\n                        <number>0</number>\n                       </property>\n                       <item row=\"0\" column=\"0\">\n                        <widget class=\"QLabel\" name=\"label_31\">\n                         <property name=\"minimumSize\">\n                          <size>\n                           <width>170</width>\n                           <height>0</height>\n                          </size>\n                         </property>\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.Adv.Recording.Type</string>\n                         </property>\n                         <property name=\"alignment\">\n                          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                         </property>\n                         <property name=\"buddy\">\n                          <cstring>advOutRecType</cstring>\n                         </property>\n                        </widget>\n                       </item>\n                       <item row=\"0\" column=\"1\">\n                        <widget class=\"QComboBox\" name=\"advOutRecType\">\n                         <item>\n                          <property name=\"text\">\n                           <string>Basic.Settings.Output.Adv.Recording.Type.Standard</string>\n                          </property>\n                         </item>\n                         <item>\n                          <property name=\"text\">\n                           <string>Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput</string>\n                          </property>\n                         </item>\n                        </widget>\n                       </item>\n                      </layout>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QStackedWidget\" name=\"stackedWidget\">\n                      <property name=\"currentIndex\">\n                       <number>0</number>\n                      </property>\n                      <widget class=\"QWidget\" name=\"advOutRecStandard\">\n                       <property name=\"sizePolicy\">\n                        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n                         <horstretch>0</horstretch>\n                         <verstretch>0</verstretch>\n                        </sizepolicy>\n                       </property>\n                       <layout class=\"QVBoxLayout\" name=\"verticalLayout_13\" stretch=\"0\">\n                        <property name=\"leftMargin\">\n                         <number>0</number>\n                        </property>\n                        <property name=\"topMargin\">\n                         <number>0</number>\n                        </property>\n                        <property name=\"rightMargin\">\n                         <number>0</number>\n                        </property>\n                        <property name=\"bottomMargin\">\n                         <number>0</number>\n                        </property>\n                        <item>\n                         <widget class=\"QScrollArea\" name=\"scrollArea_4\">\n                          <property name=\"sizePolicy\">\n                           <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n                            <horstretch>0</horstretch>\n                            <verstretch>0</verstretch>\n                           </sizepolicy>\n                          </property>\n                          <property name=\"frameShape\">\n                           <enum>QFrame::NoFrame</enum>\n                          </property>\n                          <property name=\"frameShadow\">\n                           <enum>QFrame::Plain</enum>\n                          </property>\n                          <property name=\"lineWidth\">\n                           <number>0</number>\n                          </property>\n                          <property name=\"widgetResizable\">\n                           <bool>true</bool>\n                          </property>\n                          <property name=\"alignment\">\n                           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n                          </property>\n                          <widget class=\"QWidget\" name=\"scrollAreaWidgetContents_4\">\n                           <property name=\"geometry\">\n                            <rect>\n                             <x>0</x>\n                             <y>0</y>\n                             <width>509</width>\n                             <height>371</height>\n                            </rect>\n                           </property>\n                           <property name=\"sizePolicy\">\n                            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                             <horstretch>0</horstretch>\n                             <verstretch>0</verstretch>\n                            </sizepolicy>\n                           </property>\n                           <layout class=\"QVBoxLayout\" name=\"verticalLayout_21\">\n                            <property name=\"leftMargin\">\n                             <number>0</number>\n                            </property>\n                            <property name=\"topMargin\">\n                             <number>0</number>\n                            </property>\n                            <property name=\"rightMargin\">\n                             <number>0</number>\n                            </property>\n                            <property name=\"bottomMargin\">\n                             <number>0</number>\n                            </property>\n                            <item>\n                             <widget class=\"QGroupBox\" name=\"advOutRecTopContainer\">\n                              <property name=\"title\">\n                               <string>Basic.Settings.Output.Adv.Recording.Settings</string>\n                              </property>\n                              <layout class=\"QFormLayout\" name=\"formLayout_16\">\n                               <property name=\"fieldGrowthPolicy\">\n                                <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                               </property>\n                               <property name=\"labelAlignment\">\n                                <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                               </property>\n                               <property name=\"formAlignment\">\n                                <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n                               </property>\n                               <property name=\"leftMargin\">\n                                <number>9</number>\n                               </property>\n                               <property name=\"topMargin\">\n                                <number>2</number>\n                               </property>\n                               <property name=\"rightMargin\">\n                                <number>9</number>\n                               </property>\n                               <property name=\"bottomMargin\">\n                                <number>9</number>\n                               </property>\n                               <item row=\"0\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_32\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <property name=\"minimumSize\">\n                                  <size>\n                                   <width>170</width>\n                                   <height>0</height>\n                                  </size>\n                                 </property>\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Simple.SavePath</string>\n                                 </property>\n                                 <property name=\"alignment\">\n                                  <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"0\" column=\"1\">\n                                <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\">\n                                 <item>\n                                  <widget class=\"QLineEdit\" name=\"advOutRecPath\">\n                                   <property name=\"enabled\">\n                                    <bool>true</bool>\n                                   </property>\n                                   <property name=\"sizePolicy\">\n                                    <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n                                     <horstretch>0</horstretch>\n                                     <verstretch>0</verstretch>\n                                    </sizepolicy>\n                                   </property>\n                                  </widget>\n                                 </item>\n                                 <item>\n                                  <widget class=\"QPushButton\" name=\"advOutRecPathBrowse\">\n                                   <property name=\"enabled\">\n                                    <bool>true</bool>\n                                   </property>\n                                   <property name=\"sizePolicy\">\n                                    <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                     <horstretch>0</horstretch>\n                                     <verstretch>0</verstretch>\n                                    </sizepolicy>\n                                   </property>\n                                   <property name=\"text\">\n                                    <string>Browse</string>\n                                   </property>\n                                  </widget>\n                                 </item>\n                                </layout>\n                               </item>\n                               <item row=\"1\" column=\"0\">\n                                <spacer name=\"horizontalSpacer_27\">\n                                 <property name=\"orientation\">\n                                  <enum>Qt::Horizontal</enum>\n                                 </property>\n                                 <property name=\"sizeHint\" stdset=\"0\">\n                                  <size>\n                                   <width>40</width>\n                                   <height>20</height>\n                                  </size>\n                                 </property>\n                                </spacer>\n                               </item>\n                               <item row=\"1\" column=\"1\">\n                                <widget class=\"QCheckBox\" name=\"advOutNoSpace\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.NoSpaceFileName</string>\n                                 </property>\n                                 <property name=\"checked\">\n                                  <bool>true</bool>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"2\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_43\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Format</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"2\" column=\"1\">\n                                <widget class=\"QComboBox\" name=\"advOutRecFormat\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <property name=\"editable\">\n                                  <bool>false</bool>\n                                 </property>\n                                 <property name=\"currentText\">\n                                  <string/>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"5\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_29\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.AudioTrack</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"5\" column=\"1\">\n                                <widget class=\"QStackedWidget\" name=\"advRecTrackWidget\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <property name=\"currentIndex\">\n                                  <number>1</number>\n                                 </property>\n                                 <widget class=\"QWidget\" name=\"recTracks\">\n                                  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_9\">\n                                   <property name=\"leftMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"topMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"rightMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"bottomMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <item>\n                                    <widget class=\"QCheckBox\" name=\"advOutRecTrack1\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string notr=\"true\">1</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QCheckBox\" name=\"advOutRecTrack2\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string notr=\"true\">2</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QCheckBox\" name=\"advOutRecTrack3\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string notr=\"true\">3</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QCheckBox\" name=\"advOutRecTrack4\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string notr=\"true\">4</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QCheckBox\" name=\"advOutRecTrack5\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string notr=\"true\">5</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QCheckBox\" name=\"advOutRecTrack6\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string notr=\"true\">6</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                  </layout>\n                                 </widget>\n                                 <widget class=\"QWidget\" name=\"flvTracks\">\n                                  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_8\">\n                                   <property name=\"leftMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"topMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"rightMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"bottomMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <item>\n                                    <widget class=\"QRadioButton\" name=\"flvTrack1\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string>1</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QRadioButton\" name=\"flvTrack2\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string>2</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QRadioButton\" name=\"flvTrack3\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string>3</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QRadioButton\" name=\"flvTrack4\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string>4</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QRadioButton\" name=\"flvTrack5\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string>5</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QRadioButton\" name=\"flvTrack6\">\n                                     <property name=\"sizePolicy\">\n                                      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                       <horstretch>0</horstretch>\n                                       <verstretch>0</verstretch>\n                                      </sizepolicy>\n                                     </property>\n                                     <property name=\"text\">\n                                      <string>6</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                  </layout>\n                                 </widget>\n                                </widget>\n                               </item>\n                               <item row=\"4\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"advOutRecAEncLabel\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Encoder.Audio</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"4\" column=\"1\">\n                                <widget class=\"QComboBox\" name=\"advOutRecAEncoder\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"3\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"advOutRecEncLabel\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Encoder.Video</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"3\" column=\"1\">\n                                <widget class=\"QComboBox\" name=\"advOutRecEncoder\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"6\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"advOutRecUseRescale\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Preferred\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <property name=\"layoutDirection\">\n                                  <enum>Qt::RightToLeft</enum>\n                                 </property>\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.Rescale</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"6\" column=\"1\">\n                                <widget class=\"QFrame\" name=\"advOutRecRescaleContainer\">\n                                 <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n                                  <property name=\"leftMargin\">\n                                   <number>0</number>\n                                  </property>\n                                  <property name=\"topMargin\">\n                                   <number>0</number>\n                                  </property>\n                                  <property name=\"rightMargin\">\n                                   <number>0</number>\n                                  </property>\n                                  <property name=\"bottomMargin\">\n                                   <number>0</number>\n                                  </property>\n                                  <item>\n                                   <widget class=\"QComboBox\" name=\"advOutRecRescaleFilter\"/>\n                                  </item>\n                                  <item>\n                                   <widget class=\"QComboBox\" name=\"advOutRecRescale\">\n                                    <property name=\"enabled\">\n                                     <bool>false</bool>\n                                    </property>\n                                    <property name=\"sizePolicy\">\n                                     <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                      <horstretch>0</horstretch>\n                                      <verstretch>0</verstretch>\n                                     </sizepolicy>\n                                    </property>\n                                    <property name=\"editable\">\n                                     <bool>true</bool>\n                                    </property>\n                                   </widget>\n                                  </item>\n                                 </layout>\n                                </widget>\n                               </item>\n                               <item row=\"7\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_9001\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.CustomMuxerSettings</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"7\" column=\"1\">\n                                <widget class=\"QLineEdit\" name=\"advOutMuxCustom\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"8\" column=\"0\">\n                                <widget class=\"QCheckBox\" name=\"advOutSplitFile\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Expanding\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <property name=\"layoutDirection\">\n                                  <enum>Qt::RightToLeft</enum>\n                                 </property>\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.EnableSplitFile</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"8\" column=\"1\">\n                                <widget class=\"QComboBox\" name=\"advOutSplitFileType\">\n                                 <property name=\"enabled\">\n                                  <bool>false</bool>\n                                 </property>\n                                 <item>\n                                  <property name=\"text\">\n                                   <string>Basic.Settings.Output.SplitFile.TypeTime</string>\n                                  </property>\n                                 </item>\n                                 <item>\n                                  <property name=\"text\">\n                                   <string>Basic.Settings.Output.SplitFile.TypeSize</string>\n                                  </property>\n                                 </item>\n                                 <item>\n                                  <property name=\"text\">\n                                   <string>Basic.Settings.Output.SplitFile.TypeManual</string>\n                                  </property>\n                                 </item>\n                                </widget>\n                               </item>\n                               <item row=\"9\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"advOutSplitFileTimeLabel\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.SplitFile.Time</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"9\" column=\"1\">\n                                <widget class=\"QSpinBox\" name=\"advOutSplitFileTime\">\n                                 <property name=\"suffix\">\n                                  <string> min</string>\n                                 </property>\n                                 <property name=\"minimum\">\n                                  <number>1</number>\n                                 </property>\n                                 <property name=\"maximum\">\n                                  <number>525600</number>\n                                 </property>\n                                 <property name=\"value\">\n                                  <number>15</number>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"10\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"advOutSplitFileSizeLabel\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.SplitFile.Size</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"10\" column=\"1\">\n                                <widget class=\"QSpinBox\" name=\"advOutSplitFileSize\">\n                                 <property name=\"suffix\">\n                                  <string> MB</string>\n                                 </property>\n                                 <property name=\"minimum\">\n                                  <number>20</number>\n                                 </property>\n                                 <property name=\"maximum\">\n                                  <number>1073741824</number>\n                                 </property>\n                                 <property name=\"value\">\n                                  <number>2048</number>\n                                 </property>\n                                </widget>\n                               </item>\n                              </layout>\n                             </widget>\n                            </item>\n                            <item>\n                             <widget class=\"QGroupBox\" name=\"advOutRecEncoderProps\">\n                              <property name=\"sizePolicy\">\n                               <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n                                <horstretch>0</horstretch>\n                                <verstretch>0</verstretch>\n                               </sizepolicy>\n                              </property>\n                              <property name=\"title\">\n                               <string>Basic.Settings.Output.Adv.Encoder</string>\n                              </property>\n                              <layout class=\"QVBoxLayout\" name=\"verticalLayout_15\">\n                               <property name=\"leftMargin\">\n                                <number>9</number>\n                               </property>\n                               <property name=\"topMargin\">\n                                <number>2</number>\n                               </property>\n                               <property name=\"rightMargin\">\n                                <number>9</number>\n                               </property>\n                               <property name=\"bottomMargin\">\n                                <number>8</number>\n                               </property>\n                              </layout>\n                             </widget>\n                            </item>\n                            <item>\n                             <spacer name=\"verticalSpacer_3\">\n                              <property name=\"orientation\">\n                               <enum>Qt::Vertical</enum>\n                              </property>\n                              <property name=\"sizeHint\" stdset=\"0\">\n                               <size>\n                                <width>20</width>\n                                <height>0</height>\n                               </size>\n                              </property>\n                             </spacer>\n                            </item>\n                           </layout>\n                          </widget>\n                         </widget>\n                        </item>\n                       </layout>\n                      </widget>\n                      <widget class=\"QWidget\" name=\"advOutRecFFmpegPage\">\n                       <property name=\"sizePolicy\">\n                        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n                         <horstretch>0</horstretch>\n                         <verstretch>0</verstretch>\n                        </sizepolicy>\n                       </property>\n                       <layout class=\"QVBoxLayout\" name=\"verticalLayout_26\">\n                        <property name=\"leftMargin\">\n                         <number>0</number>\n                        </property>\n                        <property name=\"topMargin\">\n                         <number>0</number>\n                        </property>\n                        <property name=\"rightMargin\">\n                         <number>0</number>\n                        </property>\n                        <property name=\"bottomMargin\">\n                         <number>0</number>\n                        </property>\n                        <item>\n                         <widget class=\"QScrollArea\" name=\"scrollArea_5\">\n                          <property name=\"frameShape\">\n                           <enum>QFrame::NoFrame</enum>\n                          </property>\n                          <property name=\"frameShadow\">\n                           <enum>QFrame::Plain</enum>\n                          </property>\n                          <property name=\"lineWidth\">\n                           <number>0</number>\n                          </property>\n                          <property name=\"widgetResizable\">\n                           <bool>true</bool>\n                          </property>\n                          <widget class=\"QWidget\" name=\"scrollAreaWidgetContents_8\">\n                           <property name=\"geometry\">\n                            <rect>\n                             <x>0</x>\n                             <y>0</y>\n                             <width>625</width>\n                             <height>489</height>\n                            </rect>\n                           </property>\n                           <layout class=\"QVBoxLayout\" name=\"verticalLayout_27\">\n                            <property name=\"leftMargin\">\n                             <number>0</number>\n                            </property>\n                            <property name=\"topMargin\">\n                             <number>0</number>\n                            </property>\n                            <property name=\"rightMargin\">\n                             <number>0</number>\n                            </property>\n                            <property name=\"bottomMargin\">\n                             <number>0</number>\n                            </property>\n                            <item>\n                             <widget class=\"QGroupBox\" name=\"advOutRecFFmpeg\">\n                              <property name=\"title\">\n                               <string>Basic.Settings.Output.Adv.FFmpeg.Settings</string>\n                              </property>\n                              <layout class=\"QFormLayout\" name=\"formLayout_7\">\n                               <property name=\"fieldGrowthPolicy\">\n                                <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                               </property>\n                               <property name=\"labelAlignment\">\n                                <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                               </property>\n                               <property name=\"leftMargin\">\n                                <number>9</number>\n                               </property>\n                               <property name=\"topMargin\">\n                                <number>2</number>\n                               </property>\n                               <property name=\"rightMargin\">\n                                <number>9</number>\n                               </property>\n                               <property name=\"bottomMargin\">\n                                <number>9</number>\n                               </property>\n                               <item row=\"0\" column=\"0\" colspan=\"2\">\n                                <widget class=\"QLabel\" name=\"label_19\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.CustomModeWarning</string>\n                                 </property>\n                                 <property name=\"wordWrap\">\n                                  <bool>true</bool>\n                                 </property>\n                                 <property name=\"class\" stdset=\"0\">\n                                  <string notr=\"true\">text-warning</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"1\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_48\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <property name=\"minimumSize\">\n                                  <size>\n                                   <width>170</width>\n                                   <height>0</height>\n                                  </size>\n                                 </property>\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.Type</string>\n                                 </property>\n                                 <property name=\"alignment\">\n                                  <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"1\" column=\"1\">\n                                <widget class=\"QComboBox\" name=\"advOutFFType\">\n                                 <item>\n                                  <property name=\"text\">\n                                   <string>Basic.Settings.Output.Adv.FFmpeg.Type.RecordToFile</string>\n                                  </property>\n                                 </item>\n                                 <item>\n                                  <property name=\"text\">\n                                   <string>Basic.Settings.Output.Adv.FFmpeg.Type.URL</string>\n                                  </property>\n                                 </item>\n                                </widget>\n                               </item>\n                               <item row=\"2\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_36\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <property name=\"minimumSize\">\n                                  <size>\n                                   <width>170</width>\n                                   <height>0</height>\n                                  </size>\n                                 </property>\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.SavePathURL</string>\n                                 </property>\n                                 <property name=\"alignment\">\n                                  <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"2\" column=\"1\">\n                                <widget class=\"QStackedWidget\" name=\"stackedWidget_2\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <property name=\"currentIndex\">\n                                  <number>0</number>\n                                 </property>\n                                 <widget class=\"QWidget\" name=\"page_7\">\n                                  <property name=\"sizePolicy\">\n                                   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n                                    <horstretch>0</horstretch>\n                                    <verstretch>0</verstretch>\n                                   </sizepolicy>\n                                  </property>\n                                  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_27\">\n                                   <property name=\"spacing\">\n                                    <number>3</number>\n                                   </property>\n                                   <property name=\"leftMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"topMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"rightMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"bottomMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <item>\n                                    <widget class=\"QLineEdit\" name=\"advOutFFRecPath\">\n                                     <property name=\"readOnly\">\n                                      <bool>true</bool>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                   <item>\n                                    <widget class=\"QPushButton\" name=\"advOutFFPathBrowse\">\n                                     <property name=\"text\">\n                                      <string>Browse</string>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                  </layout>\n                                 </widget>\n                                 <widget class=\"QWidget\" name=\"page_8\">\n                                  <property name=\"sizePolicy\">\n                                   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n                                    <horstretch>0</horstretch>\n                                    <verstretch>0</verstretch>\n                                   </sizepolicy>\n                                  </property>\n                                  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_30\">\n                                   <property name=\"spacing\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"leftMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"topMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"rightMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <property name=\"bottomMargin\">\n                                    <number>0</number>\n                                   </property>\n                                   <item>\n                                    <widget class=\"QLineEdit\" name=\"advOutFFURL\">\n                                     <property name=\"enabled\">\n                                      <bool>true</bool>\n                                     </property>\n                                    </widget>\n                                   </item>\n                                  </layout>\n                                 </widget>\n                                </widget>\n                               </item>\n                               <item row=\"3\" column=\"1\">\n                                <widget class=\"QCheckBox\" name=\"advOutFFNoSpace\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.NoSpaceFileName</string>\n                                 </property>\n                                 <property name=\"checked\">\n                                  <bool>true</bool>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"4\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_16\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.Format</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"4\" column=\"1\">\n                                <widget class=\"QComboBox\" name=\"advOutFFFormat\"/>\n                               </item>\n                               <item row=\"5\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_44\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.FormatDesc</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"5\" column=\"1\">\n                                <widget class=\"QLabel\" name=\"advOutFFFormatDesc\">\n                                 <property name=\"text\">\n                                  <string/>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"6\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_1337\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.MuxerSettings</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"6\" column=\"1\">\n                                <widget class=\"QLineEdit\" name=\"advOutFFMCfg\"/>\n                               </item>\n                               <item row=\"7\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_40\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.VideoBitrate</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"7\" column=\"1\">\n                                <widget class=\"QSpinBox\" name=\"advOutFFVBitrate\">\n                                 <property name=\"minimum\">\n                                  <number>0</number>\n                                 </property>\n                                 <property name=\"maximum\">\n                                  <number>1000000000</number>\n                                 </property>\n                                 <property name=\"value\">\n                                  <number>2500</number>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"8\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_63\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.GOPSize</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"8\" column=\"1\">\n                                <widget class=\"QSpinBox\" name=\"advOutFFVGOPSize\">\n                                 <property name=\"maximum\">\n                                  <number>1000000000</number>\n                                 </property>\n                                 <property name=\"value\">\n                                  <number>250</number>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"9\" column=\"0\">\n                                <widget class=\"QCheckBox\" name=\"advOutFFUseRescale\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Expanding\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <property name=\"layoutDirection\">\n                                  <enum>Qt::RightToLeft</enum>\n                                 </property>\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.Rescale</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"9\" column=\"1\">\n                                <widget class=\"QComboBox\" name=\"advOutFFRescale\">\n                                 <property name=\"enabled\">\n                                  <bool>false</bool>\n                                 </property>\n                                 <property name=\"editable\">\n                                  <bool>true</bool>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"10\" column=\"1\">\n                                <widget class=\"QCheckBox\" name=\"advOutFFIgnoreCompat\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"11\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_37\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.VEncoder</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"11\" column=\"1\">\n                                <widget class=\"QComboBox\" name=\"advOutFFVEncoder\"/>\n                               </item>\n                               <item row=\"12\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_38\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"12\" column=\"1\">\n                                <widget class=\"QLineEdit\" name=\"advOutFFVCfg\"/>\n                               </item>\n                               <item row=\"13\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_41\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.AudioBitrate</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"13\" column=\"1\">\n                                <widget class=\"QSpinBox\" name=\"advOutFFABitrate\">\n                                 <property name=\"minimum\">\n                                  <number>32</number>\n                                 </property>\n                                 <property name=\"maximum\">\n                                  <number>4096</number>\n                                 </property>\n                                 <property name=\"singleStep\">\n                                  <number>16</number>\n                                 </property>\n                                 <property name=\"value\">\n                                  <number>128</number>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"14\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_47\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.AudioTrack</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"14\" column=\"1\">\n                                <widget class=\"QFrame\" name=\"widget_10\">\n                                 <property name=\"sizePolicy\">\n                                  <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                                   <horstretch>0</horstretch>\n                                   <verstretch>0</verstretch>\n                                  </sizepolicy>\n                                 </property>\n                                 <layout class=\"QHBoxLayout\" name=\"horizontalLayout_26\">\n                                  <property name=\"leftMargin\">\n                                   <number>0</number>\n                                  </property>\n                                  <property name=\"topMargin\">\n                                   <number>0</number>\n                                  </property>\n                                  <property name=\"rightMargin\">\n                                   <number>0</number>\n                                  </property>\n                                  <property name=\"bottomMargin\">\n                                   <number>0</number>\n                                  </property>\n                                  <item>\n                                   <widget class=\"QCheckBox\" name=\"advOutFFTrack1\">\n                                    <property name=\"text\">\n                                     <string notr=\"true\">1</string>\n                                    </property>\n                                    <property name=\"checked\">\n                                     <bool>true</bool>\n                                    </property>\n                                   </widget>\n                                  </item>\n                                  <item>\n                                   <widget class=\"QCheckBox\" name=\"advOutFFTrack2\">\n                                    <property name=\"text\">\n                                     <string notr=\"true\">2</string>\n                                    </property>\n                                   </widget>\n                                  </item>\n                                  <item>\n                                   <widget class=\"QCheckBox\" name=\"advOutFFTrack3\">\n                                    <property name=\"text\">\n                                     <string notr=\"true\">3</string>\n                                    </property>\n                                   </widget>\n                                  </item>\n                                  <item>\n                                   <widget class=\"QCheckBox\" name=\"advOutFFTrack4\">\n                                    <property name=\"text\">\n                                     <string notr=\"true\">4</string>\n                                    </property>\n                                   </widget>\n                                  </item>\n                                  <item>\n                                   <widget class=\"QCheckBox\" name=\"advOutFFTrack5\">\n                                    <property name=\"text\">\n                                     <string notr=\"true\">5</string>\n                                    </property>\n                                   </widget>\n                                  </item>\n                                  <item>\n                                   <widget class=\"QCheckBox\" name=\"advOutFFTrack6\">\n                                    <property name=\"text\">\n                                     <string notr=\"true\">6</string>\n                                    </property>\n                                   </widget>\n                                  </item>\n                                 </layout>\n                                </widget>\n                               </item>\n                               <item row=\"15\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_39\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.AEncoder</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"15\" column=\"1\">\n                                <widget class=\"QComboBox\" name=\"advOutFFAEncoder\"/>\n                               </item>\n                               <item row=\"16\" column=\"0\">\n                                <widget class=\"QLabel\" name=\"label_46\">\n                                 <property name=\"text\">\n                                  <string>Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings</string>\n                                 </property>\n                                </widget>\n                               </item>\n                               <item row=\"16\" column=\"1\">\n                                <widget class=\"QLineEdit\" name=\"advOutFFACfg\"/>\n                               </item>\n                              </layout>\n                             </widget>\n                            </item>\n                            <item>\n                             <spacer name=\"verticalSpacer_6\">\n                              <property name=\"orientation\">\n                               <enum>Qt::Vertical</enum>\n                              </property>\n                              <property name=\"sizeHint\" stdset=\"0\">\n                               <size>\n                                <width>20</width>\n                                <height>0</height>\n                               </size>\n                              </property>\n                             </spacer>\n                            </item>\n                           </layout>\n                          </widget>\n                         </widget>\n                        </item>\n                       </layout>\n                      </widget>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QFrame\" name=\"advOutRecInfo\">\n                      <layout class=\"QVBoxLayout\" name=\"advOutRecInfoLayout\">\n                       <property name=\"leftMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"topMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"rightMargin\">\n                        <number>9</number>\n                       </property>\n                       <property name=\"bottomMargin\">\n                        <number>0</number>\n                       </property>\n                      </layout>\n                     </widget>\n                    </item>\n                   </layout>\n                  </widget>\n                  <widget class=\"QWidget\" name=\"advOutputAudioTracksTab\">\n                   <attribute name=\"title\">\n                    <string>Basic.Settings.Audio</string>\n                   </attribute>\n                   <layout class=\"QVBoxLayout\" name=\"verticalLayout_9\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>9</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QFrame\" name=\"widget_3\">\n                      <property name=\"sizePolicy\">\n                       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                       </sizepolicy>\n                      </property>\n                      <layout class=\"QVBoxLayout\" name=\"verticalLayout_10\">\n                       <property name=\"leftMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"topMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"rightMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"bottomMargin\">\n                        <number>0</number>\n                       </property>\n                       <item>\n                        <widget class=\"QScrollArea\" name=\"scrollArea_6\">\n                         <property name=\"frameShape\">\n                          <enum>QFrame::NoFrame</enum>\n                         </property>\n                         <property name=\"frameShadow\">\n                          <enum>QFrame::Plain</enum>\n                         </property>\n                         <property name=\"lineWidth\">\n                          <number>0</number>\n                         </property>\n                         <property name=\"widgetResizable\">\n                          <bool>true</bool>\n                         </property>\n                         <widget class=\"QWidget\" name=\"scrollAreaWidgetContents_7\">\n                          <property name=\"geometry\">\n                           <rect>\n                            <x>0</x>\n                            <y>0</y>\n                            <width>258</width>\n                            <height>510</height>\n                           </rect>\n                          </property>\n                          <layout class=\"QVBoxLayout\" name=\"verticalLayout_28\">\n                           <property name=\"spacing\">\n                            <number>6</number>\n                           </property>\n                           <property name=\"leftMargin\">\n                            <number>0</number>\n                           </property>\n                           <property name=\"topMargin\">\n                            <number>0</number>\n                           </property>\n                           <property name=\"rightMargin\">\n                            <number>0</number>\n                           </property>\n                           <property name=\"bottomMargin\">\n                            <number>0</number>\n                           </property>\n                           <item>\n                            <widget class=\"QGroupBox\" name=\"groupBox\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"title\">\n                              <string>Basic.Settings.Output.Adv.Audio.Track1</string>\n                             </property>\n                             <layout class=\"QFormLayout\" name=\"formLayout_10\">\n                              <property name=\"fieldGrowthPolicy\">\n                               <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                              </property>\n                              <property name=\"labelAlignment\">\n                               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                              </property>\n                              <property name=\"leftMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"topMargin\">\n                               <number>2</number>\n                              </property>\n                              <property name=\"rightMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"bottomMargin\">\n                               <number>9</number>\n                              </property>\n                              <item row=\"0\" column=\"1\">\n                               <widget class=\"QComboBox\" name=\"advOutTrack1Bitrate\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                                <property name=\"currentIndex\">\n                                 <number>8</number>\n                                </property>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>32</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>48</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>64</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>80</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>96</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>112</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>128</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>160</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>192</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>256</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>320</string>\n                                 </property>\n                                </item>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"label_55\">\n                                <property name=\"text\">\n                                 <string>Name</string>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack1Name</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"1\">\n                               <widget class=\"QLineEdit\" name=\"advOutTrack1Name\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"0\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"advOutTrack1BitrateLabel\">\n                                <property name=\"minimumSize\">\n                                 <size>\n                                  <width>170</width>\n                                  <height>0</height>\n                                 </size>\n                                </property>\n                                <property name=\"text\">\n                                 <string>Basic.Settings.Output.AudioBitrate</string>\n                                </property>\n                                <property name=\"alignment\">\n                                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack1Bitrate</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                             </layout>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QGroupBox\" name=\"groupBox_2\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"title\">\n                              <string>Basic.Settings.Output.Adv.Audio.Track2</string>\n                             </property>\n                             <layout class=\"QFormLayout\" name=\"formLayout_11\">\n                              <property name=\"fieldGrowthPolicy\">\n                               <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                              </property>\n                              <property name=\"labelAlignment\">\n                               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                              </property>\n                              <property name=\"leftMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"topMargin\">\n                               <number>2</number>\n                              </property>\n                              <property name=\"rightMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"bottomMargin\">\n                               <number>9</number>\n                              </property>\n                              <item row=\"0\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"advOutTrack2BitrateLabel\">\n                                <property name=\"minimumSize\">\n                                 <size>\n                                  <width>170</width>\n                                  <height>0</height>\n                                 </size>\n                                </property>\n                                <property name=\"text\">\n                                 <string>Basic.Settings.Output.AudioBitrate</string>\n                                </property>\n                                <property name=\"alignment\">\n                                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack2Bitrate</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"0\" column=\"1\">\n                               <widget class=\"QComboBox\" name=\"advOutTrack2Bitrate\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                                <property name=\"currentIndex\">\n                                 <number>8</number>\n                                </property>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>32</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>48</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>64</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>80</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>96</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>112</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>128</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>160</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>192</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>256</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>320</string>\n                                 </property>\n                                </item>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"label_50\">\n                                <property name=\"text\">\n                                 <string>Name</string>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack2Name</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"1\">\n                               <widget class=\"QLineEdit\" name=\"advOutTrack2Name\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                               </widget>\n                              </item>\n                             </layout>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QGroupBox\" name=\"groupBox_3\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"title\">\n                              <string>Basic.Settings.Output.Adv.Audio.Track3</string>\n                             </property>\n                             <layout class=\"QFormLayout\" name=\"formLayout_12\">\n                              <property name=\"fieldGrowthPolicy\">\n                               <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                              </property>\n                              <property name=\"labelAlignment\">\n                               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                              </property>\n                              <property name=\"leftMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"topMargin\">\n                               <number>2</number>\n                              </property>\n                              <property name=\"rightMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"bottomMargin\">\n                               <number>9</number>\n                              </property>\n                              <item row=\"0\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"advOutTrack3BitrateLabel\">\n                                <property name=\"minimumSize\">\n                                 <size>\n                                  <width>170</width>\n                                  <height>0</height>\n                                 </size>\n                                </property>\n                                <property name=\"text\">\n                                 <string>Basic.Settings.Output.AudioBitrate</string>\n                                </property>\n                                <property name=\"alignment\">\n                                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack3Bitrate</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"0\" column=\"1\">\n                               <widget class=\"QComboBox\" name=\"advOutTrack3Bitrate\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                                <property name=\"currentIndex\">\n                                 <number>8</number>\n                                </property>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>32</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>48</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>64</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>80</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>96</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>112</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>128</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>160</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>192</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>256</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>320</string>\n                                 </property>\n                                </item>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"label_52\">\n                                <property name=\"text\">\n                                 <string>Name</string>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack3Name</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"1\">\n                               <widget class=\"QLineEdit\" name=\"advOutTrack3Name\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                               </widget>\n                              </item>\n                             </layout>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QGroupBox\" name=\"groupBox_4\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"title\">\n                              <string>Basic.Settings.Output.Adv.Audio.Track4</string>\n                             </property>\n                             <layout class=\"QFormLayout\" name=\"formLayout_13\">\n                              <property name=\"fieldGrowthPolicy\">\n                               <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                              </property>\n                              <property name=\"labelAlignment\">\n                               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                              </property>\n                              <property name=\"leftMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"topMargin\">\n                               <number>2</number>\n                              </property>\n                              <property name=\"rightMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"bottomMargin\">\n                               <number>9</number>\n                              </property>\n                              <item row=\"0\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"advOutTrack4BitrateLabel\">\n                                <property name=\"minimumSize\">\n                                 <size>\n                                  <width>170</width>\n                                  <height>0</height>\n                                 </size>\n                                </property>\n                                <property name=\"text\">\n                                 <string>Basic.Settings.Output.AudioBitrate</string>\n                                </property>\n                                <property name=\"alignment\">\n                                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack4Bitrate</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"0\" column=\"1\">\n                               <widget class=\"QComboBox\" name=\"advOutTrack4Bitrate\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                                <property name=\"currentIndex\">\n                                 <number>8</number>\n                                </property>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>32</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>48</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>64</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>80</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>96</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>112</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>128</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>160</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>192</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>256</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>320</string>\n                                 </property>\n                                </item>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"label_54\">\n                                <property name=\"text\">\n                                 <string>Name</string>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack4Name</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"1\">\n                               <widget class=\"QLineEdit\" name=\"advOutTrack4Name\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                               </widget>\n                              </item>\n                             </layout>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QGroupBox\" name=\"groupBox_9\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"title\">\n                              <string>Basic.Settings.Output.Adv.Audio.Track5</string>\n                             </property>\n                             <layout class=\"QFormLayout\" name=\"formLayout_25\">\n                              <property name=\"fieldGrowthPolicy\">\n                               <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                              </property>\n                              <property name=\"labelAlignment\">\n                               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                              </property>\n                              <property name=\"leftMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"topMargin\">\n                               <number>2</number>\n                              </property>\n                              <property name=\"rightMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"bottomMargin\">\n                               <number>9</number>\n                              </property>\n                              <item row=\"0\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"advOutTrack5BitrateLabel\">\n                                <property name=\"minimumSize\">\n                                 <size>\n                                  <width>170</width>\n                                  <height>0</height>\n                                 </size>\n                                </property>\n                                <property name=\"text\">\n                                 <string>Basic.Settings.Output.AudioBitrate</string>\n                                </property>\n                                <property name=\"alignment\">\n                                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack5Bitrate</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"0\" column=\"1\">\n                               <widget class=\"QComboBox\" name=\"advOutTrack5Bitrate\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                                <property name=\"currentIndex\">\n                                 <number>8</number>\n                                </property>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>32</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>48</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>64</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>80</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>96</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>112</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>128</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>160</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>192</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>256</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>320</string>\n                                 </property>\n                                </item>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"label_60\">\n                                <property name=\"text\">\n                                 <string>Name</string>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack5Name</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"1\">\n                               <widget class=\"QLineEdit\" name=\"advOutTrack5Name\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                               </widget>\n                              </item>\n                             </layout>\n                            </widget>\n                           </item>\n                           <item>\n                            <widget class=\"QGroupBox\" name=\"groupBox_12\">\n                             <property name=\"sizePolicy\">\n                              <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                               <horstretch>0</horstretch>\n                               <verstretch>0</verstretch>\n                              </sizepolicy>\n                             </property>\n                             <property name=\"title\">\n                              <string>Basic.Settings.Output.Adv.Audio.Track6</string>\n                             </property>\n                             <layout class=\"QFormLayout\" name=\"formLayout_26\">\n                              <property name=\"fieldGrowthPolicy\">\n                               <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                              </property>\n                              <property name=\"labelAlignment\">\n                               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                              </property>\n                              <property name=\"leftMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"topMargin\">\n                               <number>2</number>\n                              </property>\n                              <property name=\"rightMargin\">\n                               <number>9</number>\n                              </property>\n                              <property name=\"bottomMargin\">\n                               <number>9</number>\n                              </property>\n                              <item row=\"0\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"advOutTrack6BitrateLabel\">\n                                <property name=\"minimumSize\">\n                                 <size>\n                                  <width>170</width>\n                                  <height>0</height>\n                                 </size>\n                                </property>\n                                <property name=\"text\">\n                                 <string>Basic.Settings.Output.AudioBitrate</string>\n                                </property>\n                                <property name=\"alignment\">\n                                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack6Bitrate</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"0\" column=\"1\">\n                               <widget class=\"QComboBox\" name=\"advOutTrack6Bitrate\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                                <property name=\"currentIndex\">\n                                 <number>8</number>\n                                </property>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>32</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>48</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>64</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>80</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>96</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>112</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>128</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>160</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>192</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>256</string>\n                                 </property>\n                                </item>\n                                <item>\n                                 <property name=\"text\">\n                                  <string>320</string>\n                                 </property>\n                                </item>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"0\">\n                               <widget class=\"QLabel\" name=\"label_62\">\n                                <property name=\"text\">\n                                 <string>Name</string>\n                                </property>\n                                <property name=\"buddy\">\n                                 <cstring>advOutTrack6Name</cstring>\n                                </property>\n                               </widget>\n                              </item>\n                              <item row=\"1\" column=\"1\">\n                               <widget class=\"QLineEdit\" name=\"advOutTrack6Name\">\n                                <property name=\"sizePolicy\">\n                                 <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n                                  <horstretch>0</horstretch>\n                                  <verstretch>0</verstretch>\n                                 </sizepolicy>\n                                </property>\n                               </widget>\n                              </item>\n                             </layout>\n                            </widget>\n                           </item>\n                           <item>\n                            <spacer name=\"verticalSpacer_5\">\n                             <property name=\"orientation\">\n                              <enum>Qt::Vertical</enum>\n                             </property>\n                             <property name=\"sizeType\">\n                              <enum>QSizePolicy::Expanding</enum>\n                             </property>\n                             <property name=\"sizeHint\" stdset=\"0\">\n                              <size>\n                               <width>20</width>\n                               <height>0</height>\n                              </size>\n                             </property>\n                            </spacer>\n                           </item>\n                          </layout>\n                         </widget>\n                        </widget>\n                       </item>\n                      </layout>\n                     </widget>\n                    </item>\n                   </layout>\n                  </widget>\n                  <widget class=\"QWidget\" name=\"advOutputReplayTab\">\n                   <attribute name=\"title\">\n                    <string>ReplayBuffer</string>\n                   </attribute>\n                   <layout class=\"QVBoxLayout\" name=\"verticalLayout_22\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>9</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QFrame\" name=\"advOutputReplayFrame\">\n                      <property name=\"lineWidth\">\n                       <number>0</number>\n                      </property>\n                      <layout class=\"QVBoxLayout\" name=\"verticalLayout_29\">\n                       <property name=\"leftMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"topMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"rightMargin\">\n                        <number>0</number>\n                       </property>\n                       <property name=\"bottomMargin\">\n                        <number>0</number>\n                       </property>\n                       <item>\n                        <widget class=\"QLabel\" name=\"advReplayBufCustomFFmpeg\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.ReplayBuffer.UnavailableCustomFFmpeg</string>\n                         </property>\n                         <property name=\"class\" stdset=\"0\">\n                          <string>text-warning</string>\n                         </property>\n                        </widget>\n                       </item>\n                       <item>\n                        <widget class=\"QCheckBox\" name=\"advReplayBuf\">\n                         <property name=\"text\">\n                          <string>Basic.Settings.Output.UseReplayBuffer</string>\n                         </property>\n                        </widget>\n                       </item>\n                       <item>\n                        <widget class=\"QFrame\" name=\"advReplayBufferFrame\">\n                         <property name=\"sizePolicy\">\n                          <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                           <horstretch>0</horstretch>\n                           <verstretch>0</verstretch>\n                          </sizepolicy>\n                         </property>\n                         <layout class=\"QFormLayout\" name=\"formLayout_30\">\n                          <property name=\"fieldGrowthPolicy\">\n                           <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                          </property>\n                          <property name=\"labelAlignment\">\n                           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                          </property>\n                          <property name=\"leftMargin\">\n                           <number>0</number>\n                          </property>\n                          <property name=\"topMargin\">\n                           <number>0</number>\n                          </property>\n                          <property name=\"rightMargin\">\n                           <number>0</number>\n                          </property>\n                          <property name=\"bottomMargin\">\n                           <number>0</number>\n                          </property>\n                          <item row=\"0\" column=\"0\">\n                           <widget class=\"QLabel\" name=\"advRBSecMaxLabel\">\n                            <property name=\"text\">\n                             <string>Basic.Settings.Output.ReplayBuffer.SecondsMax</string>\n                            </property>\n                           </widget>\n                          </item>\n                          <item row=\"0\" column=\"1\">\n                           <widget class=\"QSpinBox\" name=\"advRBSecMax\">\n                            <property name=\"suffix\">\n                             <string notr=\"true\"> s</string>\n                            </property>\n                            <property name=\"minimum\">\n                             <number>5</number>\n                            </property>\n                            <property name=\"maximum\">\n                             <number>21600</number>\n                            </property>\n                            <property name=\"value\">\n                             <number>15</number>\n                            </property>\n                           </widget>\n                          </item>\n                          <item row=\"1\" column=\"1\">\n                           <widget class=\"QSpinBox\" name=\"advRBMegsMax\">\n                            <property name=\"suffix\">\n                             <string> MB</string>\n                            </property>\n                            <property name=\"minimum\">\n                             <number>20</number>\n                            </property>\n                            <property name=\"maximum\">\n                             <number>8192</number>\n                            </property>\n                            <property name=\"value\">\n                             <number>512</number>\n                            </property>\n                           </widget>\n                          </item>\n                          <item row=\"1\" column=\"0\">\n                           <widget class=\"QLabel\" name=\"advRBMegsMaxLabel\">\n                            <property name=\"text\">\n                             <string>Basic.Settings.Output.ReplayBuffer.MegabytesMax</string>\n                            </property>\n                           </widget>\n                          </item>\n                          <item row=\"2\" column=\"1\">\n                           <widget class=\"QLabel\" name=\"advRBEstimate\">\n                            <property name=\"text\">\n                             <string notr=\"true\"/>\n                            </property>\n                           </widget>\n                          </item>\n                         </layout>\n                        </widget>\n                       </item>\n                      </layout>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"verticalSpacer\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Vertical</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>20</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </widget>\n                 </widget>\n                </item>\n               </layout>\n              </widget>\n             </widget>\n            </item>\n           </layout>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"audioPage\">\n        <layout class=\"QVBoxLayout\" name=\"formLayout\">\n         <property name=\"leftMargin\">\n          <number>9</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QScrollArea\" name=\"scrollArea_50\">\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <property name=\"lineWidth\">\n            <number>0</number>\n           </property>\n           <property name=\"widgetResizable\">\n            <bool>true</bool>\n           </property>\n           <widget class=\"QWidget\" name=\"scrollAreaWidgetContents_50\">\n            <property name=\"geometry\">\n             <rect>\n              <x>0</x>\n              <y>0</y>\n              <width>772</width>\n              <height>636</height>\n             </rect>\n            </property>\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout_50\">\n             <property name=\"leftMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"rightMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QFrame\" name=\"widget_50\">\n               <layout class=\"QVBoxLayout\" name=\"verticalLayout_51\">\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"audioGeneralGroupBox\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.General</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_52\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_14\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>170</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.SampleRate</string>\n                     </property>\n                     <property name=\"alignment\">\n                      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>sampleRate</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"sampleRate\">\n                     <property name=\"currentText\">\n                      <string notr=\"true\">44.1 kHz</string>\n                     </property>\n                     <property name=\"currentIndex\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <property name=\"text\">\n                       <string>44.1 kHz</string>\n                      </property>\n                     </item>\n                     <item>\n                      <property name=\"text\">\n                       <string>48 kHz</string>\n                      </property>\n                     </item>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_15\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.Channels</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>channelSetup</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"channelSetup\">\n                     <property name=\"currentText\">\n                      <string>Mono</string>\n                     </property>\n                     <property name=\"currentIndex\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <property name=\"text\">\n                       <string>Mono</string>\n                      </property>\n                     </item>\n                     <item>\n                      <property name=\"text\">\n                       <string>Stereo</string>\n                      </property>\n                     </item>\n                     <item>\n                      <property name=\"text\">\n                       <string>2.1</string>\n                      </property>\n                     </item>\n                     <item>\n                      <property name=\"text\">\n                       <string>4.0</string>\n                      </property>\n                     </item>\n                     <item>\n                      <property name=\"text\">\n                       <string>4.1</string>\n                      </property>\n                     </item>\n                     <item>\n                      <property name=\"text\">\n                       <string>5.1</string>\n                      </property>\n                     </item>\n                     <item>\n                      <property name=\"text\">\n                       <string>7.1</string>\n                      </property>\n                     </item>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"audioDevicesGroupBox\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Audio.Devices</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_53\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_2\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>170</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.DesktopDevice</string>\n                     </property>\n                     <property name=\"alignment\">\n                      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>desktopAudioDevice1</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"desktopAudioDevice1\">\n                     <property name=\"enabled\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_3\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.DesktopDevice2</string>\n                     </property>\n                     <property name=\"alignment\">\n                      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>desktopAudioDevice2</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"desktopAudioDevice2\">\n                     <property name=\"enabled\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_4\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.AuxDevice</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>auxAudioDevice1</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"auxAudioDevice1\">\n                     <property name=\"enabled\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_5\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.AuxDevice2</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>auxAudioDevice2</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"auxAudioDevice2\">\n                     <property name=\"enabled\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"4\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_6\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.AuxDevice3</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>auxAudioDevice3</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"4\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"auxAudioDevice3\">\n                     <property name=\"enabled\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"5\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"auxAudioDevice4\">\n                     <property name=\"enabled\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"5\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_67\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.AuxDevice4</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>auxAudioDevice4</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"audioMetersGroupBox\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Audio.Meters</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_54\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_65\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>170</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.MeterDecayRate</string>\n                     </property>\n                     <property name=\"alignment\">\n                      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>meterDecayRate</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"meterDecayRate\">\n                     <property name=\"currentText\">\n                      <string>Basic.Settings.Audio.MeterDecayRate.Fast</string>\n                     </property>\n                     <property name=\"currentIndex\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <property name=\"text\">\n                       <string>Basic.Settings.Audio.MeterDecayRate.Fast</string>\n                      </property>\n                     </item>\n                     <item>\n                      <property name=\"text\">\n                       <string>Basic.Settings.Audio.MeterDecayRate.Medium</string>\n                      </property>\n                     </item>\n                     <item>\n                      <property name=\"text\">\n                       <string>Basic.Settings.Audio.MeterDecayRate.Slow</string>\n                      </property>\n                     </item>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_66\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.PeakMeterType</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>peakMeterType</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"peakMeterType\">\n                     <property name=\"currentIndex\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <property name=\"text\">\n                       <string>Basic.Settings.Audio.PeakMeterType.SamplePeak</string>\n                      </property>\n                     </item>\n                     <item>\n                      <property name=\"text\">\n                       <string>Basic.Settings.Audio.PeakMeterType.TruePeak</string>\n                      </property>\n                     </item>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"audioAdvGroupBox\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Advanced</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_56\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"monitoringDeviceLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Audio.MonitoringDevice</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>monitoringDevice</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"monitoringDevice\"/>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"disableAudioDucking\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Audio.DisableAudioDucking</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_11\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeType\">\n                      <enum>QSizePolicy::Expanding</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"lowLatencyBuffering\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Audio.LowLatencyBufferingMode</string>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"audioHotkeysGroupBox\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Hotkeys</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"audioSourceLayout\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <spacer name=\"verticalSpacer_4\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>20</width>\n                    <height>0</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n               </layout>\n              </widget>\n             </item>\n            </layout>\n           </widget>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"audioMsg\">\n           <property name=\"text\">\n            <string notr=\"true\"/>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>true</bool>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>text-danger</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"audioMsg_2\">\n           <property name=\"text\">\n            <string notr=\"true\"/>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>true</bool>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string notr=\"true\">text-warning</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"videoPage\">\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n         <property name=\"leftMargin\">\n          <number>9</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QGroupBox\" name=\"videoGeneral\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"title\">\n            <string>Basic.Settings.General</string>\n           </property>\n           <layout class=\"QFormLayout\" name=\"formLayout_15\">\n            <property name=\"fieldGrowthPolicy\">\n             <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n            </property>\n            <property name=\"labelAlignment\">\n             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n            </property>\n            <property name=\"topMargin\">\n             <number>2</number>\n            </property>\n            <item row=\"0\" column=\"0\">\n             <widget class=\"QLabel\" name=\"label_8\">\n              <property name=\"minimumSize\">\n               <size>\n                <width>170</width>\n                <height>0</height>\n               </size>\n              </property>\n              <property name=\"text\">\n               <string>Basic.Settings.Video.BaseResolution</string>\n              </property>\n              <property name=\"alignment\">\n               <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n              </property>\n              <property name=\"buddy\">\n               <cstring>baseResolution</cstring>\n              </property>\n             </widget>\n            </item>\n            <item row=\"0\" column=\"1\">\n             <layout class=\"QHBoxLayout\" name=\"horizontalLayout_29\">\n              <property name=\"spacing\">\n               <number>6</number>\n              </property>\n              <item>\n               <widget class=\"QComboBox\" name=\"baseResolution\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"editable\">\n                 <bool>true</bool>\n                </property>\n                <property name=\"currentText\">\n                 <string notr=\"true\"/>\n                </property>\n                <property name=\"duplicatesEnabled\">\n                 <bool>false</bool>\n                </property>\n                <property name=\"frame\">\n                 <bool>true</bool>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QLabel\" name=\"baseAspect\">\n                <property name=\"text\">\n                 <string>AspectRatio</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n            <item row=\"1\" column=\"0\">\n             <widget class=\"QLabel\" name=\"outputResLabel\">\n              <property name=\"text\">\n               <string>Basic.Settings.Video.ScaledResolution</string>\n              </property>\n              <property name=\"buddy\">\n               <cstring>outputResolution</cstring>\n              </property>\n             </widget>\n            </item>\n            <item row=\"1\" column=\"1\">\n             <layout class=\"QHBoxLayout\" name=\"outputResLayout\">\n              <property name=\"spacing\">\n               <number>6</number>\n              </property>\n              <item>\n               <widget class=\"QComboBox\" name=\"outputResolution\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <property name=\"editable\">\n                 <bool>true</bool>\n                </property>\n                <property name=\"currentText\">\n                 <string notr=\"true\"/>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QLabel\" name=\"scaledAspect\">\n                <property name=\"text\">\n                 <string>AspectRatio</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n            <item row=\"2\" column=\"0\">\n             <widget class=\"QLabel\" name=\"label_11\">\n              <property name=\"text\">\n               <string>Basic.Settings.Video.DownscaleFilter</string>\n              </property>\n              <property name=\"buddy\">\n               <cstring>downscaleFilter</cstring>\n              </property>\n             </widget>\n            </item>\n            <item row=\"2\" column=\"1\">\n             <widget class=\"QComboBox\" name=\"downscaleFilter\"/>\n            </item>\n            <item row=\"3\" column=\"0\">\n             <widget class=\"QComboBox\" name=\"fpsType\">\n              <property name=\"currentText\">\n               <string>Basic.Settings.Video.FPSCommon</string>\n              </property>\n              <item>\n               <property name=\"text\">\n                <string>Basic.Settings.Video.FPSCommon</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>Basic.Settings.Video.FPSInteger</string>\n               </property>\n              </item>\n              <item>\n               <property name=\"text\">\n                <string>Basic.Settings.Video.FPSFraction</string>\n               </property>\n              </item>\n             </widget>\n            </item>\n            <item row=\"3\" column=\"1\">\n             <widget class=\"QStackedWidget\" name=\"fpsTypes\">\n              <property name=\"lineWidth\">\n               <number>0</number>\n              </property>\n              <property name=\"currentIndex\">\n               <number>0</number>\n              </property>\n              <widget class=\"QWidget\" name=\"page\">\n               <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item alignment=\"Qt::AlignTop\">\n                 <widget class=\"QComboBox\" name=\"fpsCommon\">\n                  <property name=\"currentText\">\n                   <string notr=\"true\">10</string>\n                  </property>\n                  <property name=\"currentIndex\">\n                   <number>0</number>\n                  </property>\n                  <item>\n                   <property name=\"text\">\n                    <string notr=\"true\">10</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string notr=\"true\">20</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>24 NTSC</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>25 PAL</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string notr=\"true\">29.97</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string notr=\"true\">30</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string notr=\"true\">48</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string>50 PAL</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string notr=\"true\">59.94</string>\n                   </property>\n                  </item>\n                  <item>\n                   <property name=\"text\">\n                    <string notr=\"true\">60</string>\n                   </property>\n                  </item>\n                 </widget>\n                </item>\n               </layout>\n              </widget>\n              <widget class=\"QWidget\" name=\"page_3\">\n               <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item alignment=\"Qt::AlignTop\">\n                 <widget class=\"QSpinBox\" name=\"fpsInteger\">\n                  <property name=\"minimum\">\n                   <number>1</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>120</number>\n                  </property>\n                  <property name=\"value\">\n                   <number>30</number>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </widget>\n              <widget class=\"QWidget\" name=\"page_2\">\n               <layout class=\"QFormLayout\" name=\"formLayout_4\">\n                <property name=\"fieldGrowthPolicy\">\n                 <enum>QFormLayout::ExpandingFieldsGrow</enum>\n                </property>\n                <property name=\"labelAlignment\">\n                 <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                </property>\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item row=\"0\" column=\"0\">\n                 <widget class=\"QLabel\" name=\"label_12\">\n                  <property name=\"text\">\n                   <string>Basic.Settings.Video.Numerator</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"0\" column=\"1\">\n                 <widget class=\"QSpinBox\" name=\"fpsNumerator\">\n                  <property name=\"minimum\">\n                   <number>1</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>1000000</number>\n                  </property>\n                  <property name=\"value\">\n                   <number>30</number>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"0\">\n                 <widget class=\"QLabel\" name=\"label_13\">\n                  <property name=\"text\">\n                   <string>Basic.Settings.Video.Denominator</string>\n                  </property>\n                 </widget>\n                </item>\n                <item row=\"1\" column=\"1\">\n                 <widget class=\"QSpinBox\" name=\"fpsDenominator\">\n                  <property name=\"minimum\">\n                   <number>1</number>\n                  </property>\n                  <property name=\"maximum\">\n                   <number>1000000</number>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </widget>\n             </widget>\n            </item>\n            <item row=\"4\" column=\"0\">\n             <spacer name=\"horizontalSpacer_24\">\n              <property name=\"orientation\">\n               <enum>Qt::Horizontal</enum>\n              </property>\n              <property name=\"sizeHint\" stdset=\"0\">\n               <size>\n                <width>170</width>\n                <height>10</height>\n               </size>\n              </property>\n             </spacer>\n            </item>\n           </layout>\n          </widget>\n         </item>\n         <item>\n          <spacer name=\"verticalSpacer_9\">\n           <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n           </property>\n           <property name=\"sizeType\">\n            <enum>QSizePolicy::Expanding</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>20</width>\n             <height>0</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"videoMsg\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"text\">\n            <string notr=\"true\"/>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>true</bool>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>text-danger</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"hotkeyPage\">\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_25\">\n         <property name=\"leftMargin\">\n          <number>9</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <layout class=\"QGridLayout\" name=\"hotkeySearchLayout\">\n           <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"hotkeySearchLabel\">\n             <property name=\"text\">\n              <string>Basic.Settings.Hotkeys.Filter</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"hotkeyFilterSearch\"/>\n           </item>\n           <item row=\"0\" column=\"2\">\n            <widget class=\"QLabel\" name=\"hotkeyFilterLabel\">\n             <property name=\"text\">\n              <string>Basic.Settings.Hotkeys.FilterByHotkey</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"3\">\n            <widget class=\"OBSHotkeyEdit\" name=\"hotkeyFilterInput\"/>\n           </item>\n           <item row=\"0\" column=\"4\">\n            <widget class=\"QPushButton\" name=\"hotkeyFilterReset\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"minimumSize\">\n              <size>\n               <width>0</width>\n               <height>0</height>\n              </size>\n             </property>\n             <property name=\"maximumSize\">\n              <size>\n               <width>16777215</width>\n               <height>16777215</height>\n              </size>\n             </property>\n             <property name=\"baseSize\">\n              <size>\n               <width>0</width>\n               <height>0</height>\n              </size>\n             </property>\n             <property name=\"toolTip\">\n              <string>Clear</string>\n             </property>\n             <property name=\"icon\">\n              <iconset resource=\"obs.qrc\">\n               <normaloff>:/res/images/revert.svg</normaloff>:/res/images/revert.svg</iconset>\n             </property>\n             <property name=\"iconSize\">\n              <size>\n               <width>20</width>\n               <height>20</height>\n              </size>\n             </property>\n             <property name=\"flat\">\n              <bool>false</bool>\n             </property>\n             <property name=\"class\" stdset=\"0\">\n              <string>btn-tool icon-revert</string>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <widget class=\"QScrollArea\" name=\"hotkeyScrollArea\">\n           <property name=\"widgetResizable\">\n            <bool>true</bool>\n           </property>\n           <widget class=\"QWidget\" name=\"hotkeyScrollContents\">\n            <property name=\"geometry\">\n             <rect>\n              <x>0</x>\n              <y>0</y>\n              <width>770</width>\n              <height>642</height>\n             </rect>\n            </property>\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout_33\">\n             <property name=\"leftMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"rightMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QFrame\" name=\"hotkeyScrollFrame\">\n               <layout class=\"QFormLayout\" name=\"hotkeyFormLayout\">\n                <property name=\"fieldGrowthPolicy\">\n                 <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                </property>\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item row=\"0\" column=\"0\" colspan=\"2\">\n                 <widget class=\"QLabel\" name=\"pleaseWaitLabel\">\n                  <property name=\"sizePolicy\">\n                   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                    <horstretch>0</horstretch>\n                    <verstretch>0</verstretch>\n                   </sizepolicy>\n                  </property>\n                  <property name=\"text\">\n                   <string>Basic.Settings.Hotkeys.PleaseWait</string>\n                  </property>\n                  <property name=\"alignment\">\n                   <set>Qt::AlignCenter</set>\n                  </property>\n                 </widget>\n                </item>\n               </layout>\n              </widget>\n             </item>\n            </layout>\n           </widget>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"accessPage\">\n        <layout class=\"QVBoxLayout\" name=\"formLayout_41\">\n         <property name=\"leftMargin\">\n          <number>9</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QScrollArea\" name=\"scrollArea_7\">\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <property name=\"lineWidth\">\n            <number>0</number>\n           </property>\n           <property name=\"widgetResizable\">\n            <bool>true</bool>\n           </property>\n           <widget class=\"QWidget\" name=\"accessPageContents\">\n            <property name=\"geometry\">\n             <rect>\n              <x>0</x>\n              <y>0</y>\n              <width>772</width>\n              <height>680</height>\n             </rect>\n            </property>\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout_42\">\n             <property name=\"leftMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"rightMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <layout class=\"QVBoxLayout\" name=\"verticalLayout_7\" stretch=\"0,0\">\n               <property name=\"leftMargin\">\n                <number>0</number>\n               </property>\n               <property name=\"rightMargin\">\n                <number>0</number>\n               </property>\n               <item>\n                <widget class=\"QGroupBox\" name=\"colorsGroupBox\">\n                 <property name=\"title\">\n                  <string>Basic.Settings.Accessibility.ColorOverrides</string>\n                 </property>\n                 <property name=\"checkable\">\n                  <bool>true</bool>\n                 </property>\n                 <property name=\"checked\">\n                  <bool>false</bool>\n                 </property>\n                 <layout class=\"QFormLayout\" name=\"formLayout_8\">\n                  <property name=\"fieldGrowthPolicy\">\n                   <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                  </property>\n                  <property name=\"labelAlignment\">\n                   <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                  </property>\n                  <property name=\"topMargin\">\n                   <number>2</number>\n                  </property>\n                  <item row=\"0\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"colorPresetLabel\">\n                    <property name=\"text\">\n                     <string>Basic.Settings.Accessibility.ColorOverrides.Preset</string>\n                    </property>\n                    <property name=\"buddy\">\n                     <cstring>colorPreset</cstring>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"0\" column=\"1\">\n                   <widget class=\"QComboBox\" name=\"colorPreset\">\n                    <item>\n                     <property name=\"text\">\n                      <string>Basic.Settings.Accessibility.ColorOverrides.Preset.Default</string>\n                     </property>\n                    </item>\n                    <item>\n                     <property name=\"text\">\n                      <string>Basic.Settings.Accessibility.ColorOverrides.Preset.ColorBlind1</string>\n                     </property>\n                    </item>\n                    <item>\n                     <property name=\"text\">\n                      <string>Basic.Settings.Accessibility.ColorOverrides.Preset.Custom</string>\n                     </property>\n                    </item>\n                   </widget>\n                  </item>\n                  <item row=\"1\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"colorSelectLabel_1\">\n                    <property name=\"text\">\n                     <string>Basic.Settings.Accessibility.ColorOverrides.SelectRed</string>\n                    </property>\n                    <property name=\"buddy\">\n                     <cstring>choose1</cstring>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"1\" column=\"1\">\n                   <layout class=\"QHBoxLayout\" name=\"colorSelectLayout_1\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLabel\" name=\"color1\">\n                      <property name=\"sizePolicy\">\n                       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                       </sizepolicy>\n                      </property>\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>80</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string notr=\"true\">color here</string>\n                      </property>\n                      <property name=\"alignment\">\n                       <set>Qt::AlignCenter</set>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"choose1\">\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>0</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string>Basic.PropertiesWindow.SelectColor</string>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"colorSpacer_1\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Horizontal</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>40</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </item>\n                  <item row=\"2\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"colorSelectLabel_2\">\n                    <property name=\"text\">\n                     <string>Basic.Settings.Accessibility.ColorOverrides.SelectGreen</string>\n                    </property>\n                    <property name=\"buddy\">\n                     <cstring>choose2</cstring>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"2\" column=\"1\">\n                   <layout class=\"QHBoxLayout\" name=\"colorSelectLayout_2\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLabel\" name=\"color2\">\n                      <property name=\"sizePolicy\">\n                       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                       </sizepolicy>\n                      </property>\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>80</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string notr=\"true\">color here</string>\n                      </property>\n                      <property name=\"alignment\">\n                       <set>Qt::AlignCenter</set>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"choose2\">\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>0</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string>Basic.PropertiesWindow.SelectColor</string>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"colorSpacer_2\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Horizontal</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>40</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </item>\n                  <item row=\"3\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"colorSelectLabel_3\">\n                    <property name=\"text\">\n                     <string>Basic.Settings.Accessibility.ColorOverrides.SelectBlue</string>\n                    </property>\n                    <property name=\"buddy\">\n                     <cstring>choose3</cstring>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"3\" column=\"1\">\n                   <layout class=\"QHBoxLayout\" name=\"colorSelectLayout_3\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLabel\" name=\"color3\">\n                      <property name=\"sizePolicy\">\n                       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                       </sizepolicy>\n                      </property>\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>80</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string notr=\"true\">color here</string>\n                      </property>\n                      <property name=\"alignment\">\n                       <set>Qt::AlignCenter</set>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"choose3\">\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>0</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string>Basic.PropertiesWindow.SelectColor</string>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"colorSpacer_3\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Horizontal</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>40</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </item>\n                  <item row=\"4\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"colorSelectLabel_4\">\n                    <property name=\"text\">\n                     <string>Basic.Settings.Accessibility.ColorOverrides.MixerGreen</string>\n                    </property>\n                    <property name=\"buddy\">\n                     <cstring>choose4</cstring>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"4\" column=\"1\">\n                   <layout class=\"QHBoxLayout\" name=\"colorSelectLayout_4\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLabel\" name=\"color4\">\n                      <property name=\"sizePolicy\">\n                       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                       </sizepolicy>\n                      </property>\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>80</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string notr=\"true\">color here</string>\n                      </property>\n                      <property name=\"alignment\">\n                       <set>Qt::AlignCenter</set>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"choose4\">\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>0</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string>Basic.PropertiesWindow.SelectColor</string>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"colorSpacer_4\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Horizontal</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>40</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </item>\n                  <item row=\"5\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"colorSelectLabel_5\">\n                    <property name=\"text\">\n                     <string>Basic.Settings.Accessibility.ColorOverrides.MixerYellow</string>\n                    </property>\n                    <property name=\"buddy\">\n                     <cstring>choose5</cstring>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"5\" column=\"1\">\n                   <layout class=\"QHBoxLayout\" name=\"colorSelectLayout_5\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLabel\" name=\"color5\">\n                      <property name=\"sizePolicy\">\n                       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                       </sizepolicy>\n                      </property>\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>80</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string notr=\"true\">color here</string>\n                      </property>\n                      <property name=\"alignment\">\n                       <set>Qt::AlignCenter</set>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"choose5\">\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>0</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string>Basic.PropertiesWindow.SelectColor</string>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"colorSpacer_5\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Horizontal</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>40</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </item>\n                  <item row=\"6\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"colorSelectLabel_6\">\n                    <property name=\"text\">\n                     <string>Basic.Settings.Accessibility.ColorOverrides.MixerRed</string>\n                    </property>\n                    <property name=\"buddy\">\n                     <cstring>choose6</cstring>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"6\" column=\"1\">\n                   <layout class=\"QHBoxLayout\" name=\"colorSelectLayout_6\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLabel\" name=\"color6\">\n                      <property name=\"sizePolicy\">\n                       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                       </sizepolicy>\n                      </property>\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>80</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string notr=\"true\">color here</string>\n                      </property>\n                      <property name=\"alignment\">\n                       <set>Qt::AlignCenter</set>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"choose6\">\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>0</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string>Basic.PropertiesWindow.SelectColor</string>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"colorSpacer_6\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Horizontal</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>40</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </item>\n                  <item row=\"7\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"colorSelectLabel_7\">\n                    <property name=\"text\">\n                     <string>Basic.Settings.Accessibility.ColorOverrides.MixerGreenActive</string>\n                    </property>\n                    <property name=\"buddy\">\n                     <cstring>choose7</cstring>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"7\" column=\"1\">\n                   <layout class=\"QHBoxLayout\" name=\"colorSelectLayout_7\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLabel\" name=\"color7\">\n                      <property name=\"sizePolicy\">\n                       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                       </sizepolicy>\n                      </property>\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>80</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string notr=\"true\">color here</string>\n                      </property>\n                      <property name=\"alignment\">\n                       <set>Qt::AlignCenter</set>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"choose7\">\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>0</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string>Basic.PropertiesWindow.SelectColor</string>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"colorSpacer_7\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Horizontal</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>40</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </item>\n                  <item row=\"8\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"colorSelectLabel_8\">\n                    <property name=\"text\">\n                     <string>Basic.Settings.Accessibility.ColorOverrides.MixerYellowActive</string>\n                    </property>\n                    <property name=\"buddy\">\n                     <cstring>choose8</cstring>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"8\" column=\"1\">\n                   <layout class=\"QHBoxLayout\" name=\"colorSelectLayout_8\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLabel\" name=\"color8\">\n                      <property name=\"sizePolicy\">\n                       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                       </sizepolicy>\n                      </property>\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>80</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string notr=\"true\">color here</string>\n                      </property>\n                      <property name=\"alignment\">\n                       <set>Qt::AlignCenter</set>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"choose8\">\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>0</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string>Basic.PropertiesWindow.SelectColor</string>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"colorSpacer_8\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Horizontal</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>40</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </item>\n                  <item row=\"9\" column=\"0\">\n                   <widget class=\"QLabel\" name=\"colorSelectLabel_9\">\n                    <property name=\"text\">\n                     <string>Basic.Settings.Accessibility.ColorOverrides.MixerRedActive</string>\n                    </property>\n                    <property name=\"buddy\">\n                     <cstring>choose9</cstring>\n                    </property>\n                   </widget>\n                  </item>\n                  <item row=\"9\" column=\"1\">\n                   <layout class=\"QHBoxLayout\" name=\"colorSelectLayout_9\">\n                    <property name=\"leftMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"topMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"rightMargin\">\n                     <number>0</number>\n                    </property>\n                    <property name=\"bottomMargin\">\n                     <number>0</number>\n                    </property>\n                    <item>\n                     <widget class=\"QLabel\" name=\"color9\">\n                      <property name=\"sizePolicy\">\n                       <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n                        <horstretch>0</horstretch>\n                        <verstretch>0</verstretch>\n                       </sizepolicy>\n                      </property>\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>80</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string notr=\"true\">color here</string>\n                      </property>\n                      <property name=\"alignment\">\n                       <set>Qt::AlignCenter</set>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <widget class=\"QPushButton\" name=\"choose9\">\n                      <property name=\"minimumSize\">\n                       <size>\n                        <width>0</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                      <property name=\"maximumSize\">\n                       <size>\n                        <width>16777215</width>\n                        <height>16777215</height>\n                       </size>\n                      </property>\n                      <property name=\"text\">\n                       <string>Basic.PropertiesWindow.SelectColor</string>\n                      </property>\n                     </widget>\n                    </item>\n                    <item>\n                     <spacer name=\"colorSpacer_9\">\n                      <property name=\"orientation\">\n                       <enum>Qt::Horizontal</enum>\n                      </property>\n                      <property name=\"sizeHint\" stdset=\"0\">\n                       <size>\n                        <width>40</width>\n                        <height>0</height>\n                       </size>\n                      </property>\n                     </spacer>\n                    </item>\n                   </layout>\n                  </item>\n                 </layout>\n                </widget>\n               </item>\n               <item>\n                <spacer name=\"verticalSpacer_7\">\n                 <property name=\"orientation\">\n                  <enum>Qt::Vertical</enum>\n                 </property>\n                 <property name=\"sizeType\">\n                  <enum>QSizePolicy::Expanding</enum>\n                 </property>\n                 <property name=\"sizeHint\" stdset=\"0\">\n                  <size>\n                   <width>20</width>\n                   <height>0</height>\n                  </size>\n                 </property>\n                </spacer>\n               </item>\n              </layout>\n             </item>\n            </layout>\n           </widget>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"advancedPage\">\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_16\">\n         <property name=\"leftMargin\">\n          <number>9</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QScrollArea\" name=\"scrollArea\">\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <property name=\"lineWidth\">\n            <number>0</number>\n           </property>\n           <property name=\"widgetResizable\">\n            <bool>true</bool>\n           </property>\n           <widget class=\"QWidget\" name=\"scrollAreaWidgetContents\">\n            <property name=\"geometry\">\n             <rect>\n              <x>0</x>\n              <y>0</y>\n              <width>755</width>\n              <height>952</height>\n             </rect>\n            </property>\n            <layout class=\"QVBoxLayout\" name=\"verticalLayout_23\">\n             <property name=\"leftMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"topMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"rightMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QFrame\" name=\"widget_11\">\n               <layout class=\"QVBoxLayout\" name=\"verticalLayout_24\">\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"advancedGeneralGroupBox\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.General</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_22\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"processPriorityLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.General.ProcessPriority</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>processPriority</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"processPriority\"/>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_13\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"confirmOnExit\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.General.ConfirmOnExit</string>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"advancedVideoContainer\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Video</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_14\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"rendererLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Video.Renderer</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>renderer</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"renderer\">\n                     <property name=\"currentText\">\n                      <string notr=\"true\"/>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"adapterLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Video.Adapter</string>\n                     </property>\n                     <property name=\"alignment\">\n                      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>adapter</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"adapter\">\n                     <property name=\"enabled\">\n                      <bool>false</bool>\n                     </property>\n                     <property name=\"currentText\">\n                      <string notr=\"true\"/>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_30\">\n                     <property name=\"minimumSize\">\n                      <size>\n                       <width>0</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Video.ColorFormat</string>\n                     </property>\n                     <property name=\"alignment\">\n                      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>colorFormat</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"colorFormat\"/>\n                   </item>\n                   <item row=\"3\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_33\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Video.ColorSpace</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>colorSpace</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_20\">\n                     <property name=\"leftMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"topMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"rightMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"bottomMargin\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <widget class=\"QComboBox\" name=\"colorSpace\"/>\n                     </item>\n                     <item>\n                      <widget class=\"QLabel\" name=\"label_34\">\n                       <property name=\"sizePolicy\">\n                        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                         <horstretch>0</horstretch>\n                         <verstretch>0</verstretch>\n                        </sizepolicy>\n                       </property>\n                       <property name=\"text\">\n                        <string>Basic.Settings.Advanced.Video.ColorRange</string>\n                       </property>\n                       <property name=\"buddy\">\n                        <cstring>colorRange</cstring>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QComboBox\" name=\"colorRange\"/>\n                     </item>\n                    </layout>\n                   </item>\n                   <item row=\"4\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_sdrWhiteLevel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Video.SdrWhiteLevel</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>sdrWhiteLevel</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"4\" column=\"1\">\n                    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_sdrPaperWhite\">\n                     <property name=\"leftMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"topMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"rightMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"bottomMargin\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <widget class=\"QSpinBox\" name=\"sdrWhiteLevel\">\n                       <property name=\"suffix\">\n                        <string notr=\"true\"> nits</string>\n                       </property>\n                       <property name=\"minimum\">\n                        <number>80</number>\n                       </property>\n                       <property name=\"maximum\">\n                        <number>480</number>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QLabel\" name=\"label_hdrNominalPeakLevel\">\n                       <property name=\"sizePolicy\">\n                        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                         <horstretch>0</horstretch>\n                         <verstretch>0</verstretch>\n                        </sizepolicy>\n                       </property>\n                       <property name=\"text\">\n                        <string>Basic.Settings.Advanced.Video.HdrNominalPeakLevel</string>\n                       </property>\n                       <property name=\"buddy\">\n                        <cstring>hdrNominalPeakLevel</cstring>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QSpinBox\" name=\"hdrNominalPeakLevel\">\n                       <property name=\"suffix\">\n                        <string notr=\"true\"> nits</string>\n                       </property>\n                       <property name=\"minimum\">\n                        <number>400</number>\n                       </property>\n                       <property name=\"maximum\">\n                        <number>10000</number>\n                       </property>\n                      </widget>\n                     </item>\n                    </layout>\n                   </item>\n                   <item row=\"5\" column=\"1\">\n                    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_18\">\n                     <property name=\"leftMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"topMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"rightMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"bottomMargin\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <widget class=\"QCheckBox\" name=\"disableOSXVSync\">\n                       <property name=\"text\">\n                        <string>DisableOSXVSync</string>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QCheckBox\" name=\"resetOSXVSync\">\n                       <property name=\"text\">\n                        <string>ResetOSXVSyncOnExit</string>\n                       </property>\n                      </widget>\n                     </item>\n                    </layout>\n                   </item>\n                   <item row=\"6\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_12\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_6\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Output.Adv.Recording</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_17\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_7\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Output.Adv.Recording.Filename</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>filenameFormatting</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QLineEdit\" name=\"filenameFormatting\"/>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"overwriteIfExists\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Output.Adv.Recording.OverwriteIfExists</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_14\">\n                     <property name=\"leftMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"topMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"rightMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"bottomMargin\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <widget class=\"QLineEdit\" name=\"simpleRBPrefix\"/>\n                     </item>\n                     <item>\n                      <widget class=\"QLabel\" name=\"label_58\">\n                       <property name=\"text\">\n                        <string>Basic.Settings.Output.ReplayBuffer.Suffix</string>\n                       </property>\n                       <property name=\"buddy\">\n                        <cstring>simpleRBSuffix</cstring>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QLineEdit\" name=\"simpleRBSuffix\"/>\n                     </item>\n                    </layout>\n                   </item>\n                   <item row=\"3\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_57\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Output.ReplayBuffer.Prefix</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>simpleRBPrefix</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_10\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"autoRemux\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.AutoRemux</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_16\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>40</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_5\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Advanced.StreamDelay</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_18\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_56\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.StreamDelay.Duration</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>streamDelaySec</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QFrame\" name=\"widget_12\">\n                     <property name=\"enabled\">\n                      <bool>true</bool>\n                     </property>\n                     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_13\">\n                      <property name=\"spacing\">\n                       <number>5</number>\n                      </property>\n                      <property name=\"leftMargin\">\n                       <number>0</number>\n                      </property>\n                      <property name=\"topMargin\">\n                       <number>0</number>\n                      </property>\n                      <property name=\"rightMargin\">\n                       <number>0</number>\n                      </property>\n                      <property name=\"bottomMargin\">\n                       <number>0</number>\n                      </property>\n                      <item>\n                       <widget class=\"QSpinBox\" name=\"streamDelaySec\">\n                        <property name=\"enabled\">\n                         <bool>true</bool>\n                        </property>\n                        <property name=\"sizePolicy\">\n                         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n                          <horstretch>0</horstretch>\n                          <verstretch>0</verstretch>\n                         </sizepolicy>\n                        </property>\n                        <property name=\"minimumSize\">\n                         <size>\n                          <width>80</width>\n                          <height>0</height>\n                         </size>\n                        </property>\n                        <property name=\"suffix\">\n                         <string notr=\"true\"> s</string>\n                        </property>\n                        <property name=\"minimum\">\n                         <number>1</number>\n                        </property>\n                        <property name=\"maximum\">\n                         <number>1800</number>\n                        </property>\n                       </widget>\n                      </item>\n                      <item>\n                       <widget class=\"QLabel\" name=\"streamDelayInfo\">\n                        <property name=\"text\">\n                         <string notr=\"true\">Estimated RAM goes here</string>\n                        </property>\n                       </widget>\n                      </item>\n                     </layout>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"streamDelayPreserve\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.StreamDelay.Preserve</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"streamDelayEnable\">\n                     <property name=\"text\">\n                      <string>Enable</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_9\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_7\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Output.Reconnect</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_19\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"reconnectEnable\">\n                     <property name=\"text\">\n                      <string>Enable</string>\n                     </property>\n                     <property name=\"checked\">\n                      <bool>true</bool>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_19\">\n                     <property name=\"leftMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"topMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"rightMargin\">\n                      <number>0</number>\n                     </property>\n                     <property name=\"bottomMargin\">\n                      <number>0</number>\n                     </property>\n                     <item>\n                      <widget class=\"QSpinBox\" name=\"reconnectRetryDelay\">\n                       <property name=\"suffix\">\n                        <string> s</string>\n                       </property>\n                       <property name=\"minimum\">\n                        <number>1</number>\n                       </property>\n                       <property name=\"maximum\">\n                        <number>30</number>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QLabel\" name=\"label_22\">\n                       <property name=\"sizePolicy\">\n                        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Preferred\">\n                         <horstretch>0</horstretch>\n                         <verstretch>0</verstretch>\n                        </sizepolicy>\n                       </property>\n                       <property name=\"text\">\n                        <string>Basic.Settings.Output.MaxRetries</string>\n                       </property>\n                       <property name=\"buddy\">\n                        <cstring>reconnectMaxRetries</cstring>\n                       </property>\n                      </widget>\n                     </item>\n                     <item>\n                      <widget class=\"QSpinBox\" name=\"reconnectMaxRetries\">\n                       <property name=\"minimum\">\n                        <number>1</number>\n                       </property>\n                       <property name=\"maximum\">\n                        <number>10000</number>\n                       </property>\n                      </widget>\n                     </item>\n                    </layout>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_17\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Output.RetryDelay</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>reconnectRetryDelay</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_8\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"advNetworkGroupBox\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Advanced.Network</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_23\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QLabel\" name=\"advNetworkDisabled\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Network.Disabled</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"ipFamilyLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Network.IPFamily</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>ipFamily</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"1\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"ipFamily\"/>\n                   </item>\n                   <item row=\"2\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"bindToIPLabel\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Network.BindToIP</string>\n                     </property>\n                     <property name=\"buddy\">\n                      <cstring>bindToIP</cstring>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"2\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"bindToIP\"/>\n                   </item>\n                   <item row=\"4\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"enableNewSocketLoop\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Network.EnableNewSocketLoop</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"5\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"enableLowLatencyMode\">\n                     <property name=\"enabled\">\n                      <bool>false</bool>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Network.EnableLowLatencyMode</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"4\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_7\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>1</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"3\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"dynBitrate\">\n                     <property name=\"toolTip\">\n                      <string>Basic.Settings.Output.DynamicBitrate.TT</string>\n                     </property>\n                     <property name=\"text\">\n                      <string>Basic.Settings.Output.DynamicBitrate.Beta</string>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"sourcesGroup\">\n                  <property name=\"title\">\n                   <string>Basic.Main.Sources</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_34\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_15\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>20</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QCheckBox\" name=\"browserHWAccel\">\n                     <property name=\"text\">\n                      <string>BrowserSource.EnableHardwareAcceleration</string>\n                     </property>\n                    </widget>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <widget class=\"QGroupBox\" name=\"groupBox_17\">\n                  <property name=\"title\">\n                   <string>Basic.Settings.Hotkeys</string>\n                  </property>\n                  <layout class=\"QFormLayout\" name=\"formLayout_33\">\n                   <property name=\"fieldGrowthPolicy\">\n                    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n                   </property>\n                   <property name=\"labelAlignment\">\n                    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>2</number>\n                   </property>\n                   <item row=\"0\" column=\"0\">\n                    <widget class=\"QLabel\" name=\"label_21\">\n                     <property name=\"text\">\n                      <string>Basic.Settings.Advanced.Hotkeys.HotkeyFocusBehavior</string>\n                     </property>\n                    </widget>\n                   </item>\n                   <item row=\"0\" column=\"1\">\n                    <widget class=\"QComboBox\" name=\"hotkeyFocusType\"/>\n                   </item>\n                   <item row=\"1\" column=\"0\">\n                    <spacer name=\"horizontalSpacer_14\">\n                     <property name=\"orientation\">\n                      <enum>Qt::Horizontal</enum>\n                     </property>\n                     <property name=\"sizeHint\" stdset=\"0\">\n                      <size>\n                       <width>170</width>\n                       <height>0</height>\n                      </size>\n                     </property>\n                    </spacer>\n                   </item>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <spacer name=\"verticalSpacer_13\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeType\">\n                   <enum>QSizePolicy::Expanding</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>20</width>\n                    <height>0</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n               </layout>\n              </widget>\n             </item>\n            </layout>\n           </widget>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QFrame\" name=\"advancedInfoFrame\">\n           <layout class=\"QVBoxLayout\" name=\"verticalLayout_17\">\n            <property name=\"leftMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"topMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"rightMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"bottomMargin\">\n             <number>0</number>\n            </property>\n            <item>\n             <widget class=\"QLabel\" name=\"advancedMsg\">\n              <property name=\"text\">\n               <string notr=\"true\"/>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>true</bool>\n              </property>\n              <property name=\"class\" stdset=\"0\">\n               <string>text-danger</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QLabel\" name=\"advancedMsg2\">\n              <property name=\"text\">\n               <string notr=\"true\"/>\n              </property>\n              <property name=\"wordWrap\">\n               <bool>true</bool>\n              </property>\n              <property name=\"class\" stdset=\"0\">\n               <string>text-danger</string>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>UrlPushButton</class>\n   <extends>QPushButton</extends>\n   <header>components/UrlPushButton.hpp</header>\n  </customwidget>\n  <customwidget>\n   <class>OBSHotkeyEdit</class>\n   <extends>QLineEdit</extends>\n   <header>settings/OBSHotkeyEdit.hpp</header>\n  </customwidget>\n  <customwidget>\n   <class>AbsoluteSlider</class>\n   <extends>QSlider</extends>\n   <header>components/AbsoluteSlider.hpp</header>\n  </customwidget>\n </customwidgets>\n <tabstops>\n  <tabstop>listWidget</tabstop>\n  <tabstop>scrollArea_2</tabstop>\n  <tabstop>language</tabstop>\n  <tabstop>openStatsOnStartup</tabstop>\n  <tabstop>hideOBSFromCapture</tabstop>\n  <tabstop>updateChannelBox</tabstop>\n  <tabstop>enableAutoUpdates</tabstop>\n  <tabstop>warnBeforeStreamStart</tabstop>\n  <tabstop>warnBeforeStreamStop</tabstop>\n  <tabstop>warnBeforeRecordStop</tabstop>\n  <tabstop>recordWhenStreaming</tabstop>\n  <tabstop>keepRecordStreamStops</tabstop>\n  <tabstop>replayWhileStreaming</tabstop>\n  <tabstop>keepReplayStreamStops</tabstop>\n  <tabstop>snappingEnabled</tabstop>\n  <tabstop>snapDistance</tabstop>\n  <tabstop>screenSnapping</tabstop>\n  <tabstop>sourceSnapping</tabstop>\n  <tabstop>centerSnapping</tabstop>\n  <tabstop>hideProjectorCursor</tabstop>\n  <tabstop>projectorAlwaysOnTop</tabstop>\n  <tabstop>saveProjectors</tabstop>\n  <tabstop>closeProjectors</tabstop>\n  <tabstop>systemTrayEnabled</tabstop>\n  <tabstop>systemTrayWhenStarted</tabstop>\n  <tabstop>systemTrayAlways</tabstop>\n  <tabstop>overflowHide</tabstop>\n  <tabstop>overflowAlwaysVisible</tabstop>\n  <tabstop>overflowSelectionHide</tabstop>\n  <tabstop>previewSafeAreas</tabstop>\n  <tabstop>previewSpacingHelpers</tabstop>\n  <tabstop>automaticSearch</tabstop>\n  <tabstop>doubleClickSwitch</tabstop>\n  <tabstop>studioPortraitLayout</tabstop>\n  <tabstop>prevProgLabelToggle</tabstop>\n  <tabstop>multiviewMouseSwitch</tabstop>\n  <tabstop>multiviewDrawNames</tabstop>\n  <tabstop>multiviewDrawAreas</tabstop>\n  <tabstop>multiviewLayout</tabstop>\n  <tabstop>theme</tabstop>\n  <tabstop>themeVariant</tabstop>\n  <tabstop>service</tabstop>\n  <tabstop>moreInfoButton</tabstop>\n  <tabstop>connectAccount</tabstop>\n  <tabstop>useStreamKey</tabstop>\n  <tabstop>server</tabstop>\n  <tabstop>customServer</tabstop>\n  <tabstop>key</tabstop>\n  <tabstop>show</tabstop>\n  <tabstop>getStreamKeyButton</tabstop>\n  <tabstop>connectAccount2</tabstop>\n  <tabstop>disconnectAccount</tabstop>\n  <tabstop>bandwidthTestEnable</tabstop>\n  <tabstop>useAuth</tabstop>\n  <tabstop>twitchAddonDropdown</tabstop>\n  <tabstop>authUsername</tabstop>\n  <tabstop>authPw</tabstop>\n  <tabstop>authPwShow</tabstop>\n  <tabstop>outputMode</tabstop>\n  <tabstop>simpleOutputVBitrate</tabstop>\n  <tabstop>simpleOutputABitrate</tabstop>\n  <tabstop>simpleOutStrEncoder</tabstop>\n  <tabstop>simpleOutPreset</tabstop>\n  <tabstop>simpleOutAdvanced</tabstop>\n  <tabstop>simpleOutCustom</tabstop>\n  <tabstop>simpleOutRecTrack1</tabstop>\n  <tabstop>simpleOutRecTrack2</tabstop>\n  <tabstop>simpleOutRecTrack3</tabstop>\n  <tabstop>simpleOutRecTrack4</tabstop>\n  <tabstop>simpleOutRecTrack5</tabstop>\n  <tabstop>simpleOutRecTrack6</tabstop>\n  <tabstop>simpleOutputPath</tabstop>\n  <tabstop>simpleOutputBrowse</tabstop>\n  <tabstop>simpleNoSpace</tabstop>\n  <tabstop>simpleOutRecQuality</tabstop>\n  <tabstop>simpleOutRecFormat</tabstop>\n  <tabstop>simpleOutRecEncoder</tabstop>\n  <tabstop>simpleOutMuxCustom</tabstop>\n  <tabstop>simpleReplayBuf</tabstop>\n  <tabstop>simpleRBSecMax</tabstop>\n  <tabstop>simpleRBMegsMax</tabstop>\n  <tabstop>advOutTabs</tabstop>\n  <tabstop>advOutTrack1</tabstop>\n  <tabstop>advOutTrack2</tabstop>\n  <tabstop>advOutTrack3</tabstop>\n  <tabstop>advOutTrack4</tabstop>\n  <tabstop>advOutTrack5</tabstop>\n  <tabstop>advOutTrack6</tabstop>\n  <tabstop>advOutMultiTrack1</tabstop>\n  <tabstop>advOutMultiTrack2</tabstop>\n  <tabstop>advOutMultiTrack3</tabstop>\n  <tabstop>advOutMultiTrack4</tabstop>\n  <tabstop>advOutMultiTrack5</tabstop>\n  <tabstop>advOutMultiTrack6</tabstop>\n  <tabstop>advOutEncoder</tabstop>\n  <tabstop>advOutRescaleFilter</tabstop>\n  <tabstop>advOutRescale</tabstop>\n  <tabstop>advOutRecType</tabstop>\n  <tabstop>advOutRecPath</tabstop>\n  <tabstop>advOutRecPathBrowse</tabstop>\n  <tabstop>advOutNoSpace</tabstop>\n  <tabstop>advOutRecFormat</tabstop>\n  <tabstop>advOutRecTrack1</tabstop>\n  <tabstop>advOutRecTrack2</tabstop>\n  <tabstop>advOutRecTrack3</tabstop>\n  <tabstop>advOutRecTrack4</tabstop>\n  <tabstop>advOutRecTrack5</tabstop>\n  <tabstop>advOutRecTrack6</tabstop>\n  <tabstop>advOutRecEncoder</tabstop>\n  <tabstop>advOutRecRescaleFilter</tabstop>\n  <tabstop>advOutRecRescale</tabstop>\n  <tabstop>advOutMuxCustom</tabstop>\n  <tabstop>advOutSplitFile</tabstop>\n  <tabstop>advOutSplitFileType</tabstop>\n  <tabstop>advOutSplitFileTime</tabstop>\n  <tabstop>advOutSplitFileSize</tabstop>\n  <tabstop>advOutTrack1Bitrate</tabstop>\n  <tabstop>advOutTrack1Name</tabstop>\n  <tabstop>advOutTrack2Bitrate</tabstop>\n  <tabstop>advOutTrack2Name</tabstop>\n  <tabstop>advOutTrack3Bitrate</tabstop>\n  <tabstop>advOutTrack3Name</tabstop>\n  <tabstop>advOutTrack4Bitrate</tabstop>\n  <tabstop>advOutTrack4Name</tabstop>\n  <tabstop>advOutTrack5Bitrate</tabstop>\n  <tabstop>advOutTrack5Name</tabstop>\n  <tabstop>advOutTrack6Bitrate</tabstop>\n  <tabstop>advOutTrack6Name</tabstop>\n  <tabstop>advRBSecMax</tabstop>\n  <tabstop>advRBMegsMax</tabstop>\n  <tabstop>scrollArea_50</tabstop>\n  <tabstop>sampleRate</tabstop>\n  <tabstop>channelSetup</tabstop>\n  <tabstop>desktopAudioDevice1</tabstop>\n  <tabstop>desktopAudioDevice2</tabstop>\n  <tabstop>auxAudioDevice1</tabstop>\n  <tabstop>auxAudioDevice2</tabstop>\n  <tabstop>auxAudioDevice3</tabstop>\n  <tabstop>auxAudioDevice4</tabstop>\n  <tabstop>meterDecayRate</tabstop>\n  <tabstop>peakMeterType</tabstop>\n  <tabstop>monitoringDevice</tabstop>\n  <tabstop>disableAudioDucking</tabstop>\n  <tabstop>lowLatencyBuffering</tabstop>\n  <tabstop>baseResolution</tabstop>\n  <tabstop>outputResolution</tabstop>\n  <tabstop>downscaleFilter</tabstop>\n  <tabstop>fpsType</tabstop>\n  <tabstop>fpsCommon</tabstop>\n  <tabstop>fpsInteger</tabstop>\n  <tabstop>fpsNumerator</tabstop>\n  <tabstop>fpsDenominator</tabstop>\n  <tabstop>scrollArea</tabstop>\n  <tabstop>processPriority</tabstop>\n  <tabstop>confirmOnExit</tabstop>\n  <tabstop>renderer</tabstop>\n  <tabstop>adapter</tabstop>\n  <tabstop>colorFormat</tabstop>\n  <tabstop>colorSpace</tabstop>\n  <tabstop>colorRange</tabstop>\n  <tabstop>sdrWhiteLevel</tabstop>\n  <tabstop>hdrNominalPeakLevel</tabstop>\n  <tabstop>disableOSXVSync</tabstop>\n  <tabstop>resetOSXVSync</tabstop>\n  <tabstop>filenameFormatting</tabstop>\n  <tabstop>overwriteIfExists</tabstop>\n  <tabstop>autoRemux</tabstop>\n  <tabstop>simpleRBPrefix</tabstop>\n  <tabstop>simpleRBSuffix</tabstop>\n  <tabstop>streamDelayEnable</tabstop>\n  <tabstop>streamDelaySec</tabstop>\n  <tabstop>streamDelayPreserve</tabstop>\n  <tabstop>reconnectEnable</tabstop>\n  <tabstop>reconnectRetryDelay</tabstop>\n  <tabstop>reconnectMaxRetries</tabstop>\n  <tabstop>bindToIP</tabstop>\n  <tabstop>dynBitrate</tabstop>\n  <tabstop>enableNewSocketLoop</tabstop>\n  <tabstop>enableLowLatencyMode</tabstop>\n  <tabstop>browserHWAccel</tabstop>\n  <tabstop>hotkeyFocusType</tabstop>\n  <tabstop>ignoreRecommended</tabstop>\n  <tabstop>useStreamKeyAdv</tabstop>\n  <tabstop>hotkeyFilterSearch</tabstop>\n  <tabstop>hotkeyFilterInput</tabstop>\n  <tabstop>hotkeyFilterReset</tabstop>\n  <tabstop>hotkeyScrollArea</tabstop>\n </tabstops>\n <resources>\n  <include location=\"obs.qrc\"/>\n </resources>\n <connections>\n  <connection>\n   <sender>listWidget</sender>\n   <signal>currentRowChanged(int)</signal>\n   <receiver>settingsPages</receiver>\n   <slot>setCurrentIndex(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>75</x>\n     <y>35</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>199</x>\n     <y>50</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>fpsType</sender>\n   <signal>currentIndexChanged(int)</signal>\n   <receiver>fpsTypes</receiver>\n   <slot>setCurrentIndex(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>310</x>\n     <y>29</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>241</x>\n     <y>30</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>outputMode</sender>\n   <signal>currentIndexChanged(int)</signal>\n   <receiver>outputModePages</receiver>\n   <slot>setCurrentIndex(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>241</x>\n     <y>16</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>401</x>\n     <y>64</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>simpleOutAdvanced</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>simpleOutCustom</receiver>\n   <slot>setVisible(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>251</x>\n     <y>64</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>251</x>\n     <y>64</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>simpleOutAdvanced</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>simpleOutCustomLabel</receiver>\n   <slot>setVisible(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>251</x>\n     <y>64</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>251</x>\n     <y>64</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>systemTrayEnabled</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>systemTrayWhenStarted</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>933</x>\n     <y>579</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>933</x>\n     <y>602</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>systemTrayEnabled</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>systemTrayAlways</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>933</x>\n     <y>579</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>933</x>\n     <y>625</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>enableNewSocketLoop</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>enableLowLatencyMode</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>250</x>\n     <y>39</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>250</x>\n     <y>39</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>snappingEnabled</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>label_9</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>933</x>\n     <y>340</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>347</x>\n     <y>366</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>snappingEnabled</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>snapDistance</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>933</x>\n     <y>340</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>933</x>\n     <y>366</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>snappingEnabled</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>screenSnapping</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>933</x>\n     <y>340</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>933</x>\n     <y>389</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>snappingEnabled</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>sourceSnapping</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>933</x>\n     <y>340</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>933</x>\n     <y>412</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>snappingEnabled</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>centerSnapping</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>933</x>\n     <y>340</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>933</x>\n     <y>435</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>recordWhenStreaming</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>keepRecordStreamStops</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>933</x>\n     <y>222</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>933</x>\n     <y>245</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>replayWhileStreaming</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>keepReplayStreamStops</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>933</x>\n     <y>268</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>933</x>\n     <y>291</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>streamDelayEnable</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>label_56</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>250</x>\n     <y>39</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>250</x>\n     <y>39</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>streamDelayEnable</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>streamDelaySec</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>250</x>\n     <y>39</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>250</x>\n     <y>39</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>streamDelayEnable</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>streamDelayInfo</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>250</x>\n     <y>39</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>250</x>\n     <y>39</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>streamDelayEnable</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>streamDelayPreserve</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>250</x>\n     <y>39</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>250</x>\n     <y>39</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>connectAccount2</sender>\n   <signal>clicked()</signal>\n   <receiver>connectAccount</receiver>\n   <slot>click()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>484</x>\n     <y>142</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>454</x>\n     <y>87</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>advOutSplitFile</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>advOutSplitFileType</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>327</x>\n     <y>355</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>701</x>\n     <y>355</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>advOutRecType</sender>\n   <signal>currentIndexChanged(int)</signal>\n   <receiver>stackedWidget</receiver>\n   <slot>setCurrentIndex(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>663</x>\n     <y>106</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>560</x>\n     <y>391</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>advOutFFUseRescale</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>advOutFFRescale</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>334</x>\n     <y>377</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>685</x>\n     <y>377</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>advOutFFType</sender>\n   <signal>currentIndexChanged(int)</signal>\n   <receiver>stackedWidget_2</receiver>\n   <slot>setCurrentIndex(int)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>685</x>\n     <y>163</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>685</x>\n     <y>191</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n <buttongroups>\n  <buttongroup name=\"appearanceDensityButtonGroup\"/>\n </buttongroups>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSBasicSourceSelect.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSBasicSourceSelect</class>\n <widget class=\"QDialog\" name=\"OBSBasicSourceSelect\">\n  <property name=\"windowModality\">\n   <enum>Qt::WindowModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>352</width>\n    <height>314</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Basic.SourceSelect</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n     <item>\n      <widget class=\"QRadioButton\" name=\"createNew\">\n       <property name=\"text\">\n        <string>Basic.SourceSelect.CreateNew</string>\n       </property>\n       <property name=\"checked\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLineEdit\" name=\"sourceName\"/>\n     </item>\n     <item>\n      <widget class=\"QRadioButton\" name=\"selectExisting\">\n       <property name=\"text\">\n        <string>Basic.SourceSelect.AddExisting</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QListWidget\" name=\"sourceList\">\n       <property name=\"enabled\">\n        <bool>false</bool>\n       </property>\n       <property name=\"sortingEnabled\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QCheckBox\" name=\"sourceVisible\">\n       <property name=\"text\">\n        <string>Basic.SourceSelect.AddVisible</string>\n       </property>\n       <property name=\"checked\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>createNew</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>sourceName</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>79</x>\n     <y>29</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>108</x>\n     <y>53</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>selectExisting</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>sourceList</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>51</x>\n     <y>80</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>65</x>\n     <y>128</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSBasicTransform.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSBasicTransform</class>\n <widget class=\"QDialog\" name=\"OBSBasicTransform\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>900</width>\n    <height>280</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Basic.TransformWindow</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\" stretch=\"0\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QFrame\" name=\"outerFrame\">\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>0</number>\n     </property>\n     <property name=\"class\" stdset=\"0\">\n      <string>dialog-container</string>\n     </property>\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n      <property name=\"spacing\">\n       <number>0</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QFrame\" name=\"transformSettings\">\n        <property name=\"frameShape\">\n         <enum>QFrame::NoFrame</enum>\n        </property>\n        <property name=\"frameShadow\">\n         <enum>QFrame::Plain</enum>\n        </property>\n        <property name=\"lineWidth\">\n         <number>0</number>\n        </property>\n        <property name=\"class\" stdset=\"0\">\n         <string>dialog-container dialog-frame</string>\n        </property>\n        <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"horizontalSpacing\">\n          <number>8</number>\n         </property>\n         <property name=\"verticalSpacing\">\n          <number>2</number>\n         </property>\n         <item row=\"1\" column=\"2\">\n          <widget class=\"QLabel\" name=\"sizeWidthLabel\">\n           <property name=\"accessibleName\">\n            <string>Width</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Width</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"0\">\n          <widget class=\"QLabel\" name=\"positionXLabel\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Accessible.PositionX</string>\n           </property>\n           <property name=\"text\">\n            <string>X</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"3\">\n          <widget class=\"QDoubleSpinBox\" name=\"sizeX\">\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Width</string>\n           </property>\n           <property name=\"suffix\">\n            <string> px</string>\n           </property>\n           <property name=\"decimals\">\n            <number>2</number>\n           </property>\n           <property name=\"minimum\">\n            <double>-90001.000000000000000</double>\n           </property>\n           <property name=\"maximum\">\n            <double>90001.000000000000000</double>\n           </property>\n           <property name=\"singleStep\">\n            <double>1.000000000000000</double>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"4\">\n          <widget class=\"QLabel\" name=\"label_2\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Rotation</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Rotation</string>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>subtitle</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"1\">\n          <widget class=\"QDoubleSpinBox\" name=\"positionX\">\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.PositionX</string>\n           </property>\n           <property name=\"suffix\">\n            <string> px</string>\n           </property>\n           <property name=\"decimals\">\n            <number>2</number>\n           </property>\n           <property name=\"minimum\">\n            <double>-90001.000000000000000</double>\n           </property>\n           <property name=\"maximum\">\n            <double>90001.000000000000000</double>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"4\">\n          <widget class=\"QDoubleSpinBox\" name=\"rotation\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Rotation</string>\n           </property>\n           <property name=\"suffix\">\n            <string>°</string>\n           </property>\n           <property name=\"minimum\">\n            <double>-360.000000000000000</double>\n           </property>\n           <property name=\"maximum\">\n            <double>360.000000000000000</double>\n           </property>\n           <property name=\"singleStep\">\n            <double>0.100000000000000</double>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"2\" colspan=\"2\">\n          <widget class=\"QLabel\" name=\"label_3\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Size</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Size</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>subtitle</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"0\" colspan=\"2\">\n          <widget class=\"QLabel\" name=\"label\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Position</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Position</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>subtitle</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"6\">\n          <spacer name=\"horizontalSpacer_3\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>0</width>\n             <height>10</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item row=\"2\" column=\"0\">\n          <widget class=\"QLabel\" name=\"positionYLabel\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Accessible.PositionY</string>\n           </property>\n           <property name=\"text\">\n            <string>Y</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"1\">\n          <widget class=\"QDoubleSpinBox\" name=\"positionY\">\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.PositionY</string>\n           </property>\n           <property name=\"suffix\">\n            <string> px</string>\n           </property>\n           <property name=\"decimals\">\n            <number>2</number>\n           </property>\n           <property name=\"minimum\">\n            <double>-90001.000000000000000</double>\n           </property>\n           <property name=\"maximum\">\n            <double>90001.000000000000000</double>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"3\">\n          <widget class=\"QDoubleSpinBox\" name=\"sizeY\">\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Height</string>\n           </property>\n           <property name=\"suffix\">\n            <string> px</string>\n           </property>\n           <property name=\"decimals\">\n            <number>2</number>\n           </property>\n           <property name=\"minimum\">\n            <double>-90001.000000000000000</double>\n           </property>\n           <property name=\"maximum\">\n            <double>90001.000000000000000</double>\n           </property>\n           <property name=\"singleStep\">\n            <double>1.000000000000000</double>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"2\">\n          <widget class=\"QLabel\" name=\"sizeHeightLabel\">\n           <property name=\"accessibleName\">\n            <string>Height</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Height</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"5\">\n          <widget class=\"QFrame\" name=\"alignmentWidget\">\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <property name=\"lineWidth\">\n            <number>0</number>\n           </property>\n           <layout class=\"QVBoxLayout\" name=\"alignmentLayout\">\n            <property name=\"spacing\">\n             <number>0</number>\n            </property>\n            <property name=\"leftMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"topMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"rightMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"bottomMargin\">\n             <number>0</number>\n            </property>\n           </layout>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"5\">\n          <widget class=\"QLabel\" name=\"label_4\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Alignment</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Alignment</string>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>subtitle</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QFrame\" name=\"boundsSettings\">\n        <property name=\"frameShape\">\n         <enum>QFrame::NoFrame</enum>\n        </property>\n        <property name=\"frameShadow\">\n         <enum>QFrame::Plain</enum>\n        </property>\n        <property name=\"lineWidth\">\n         <number>0</number>\n        </property>\n        <property name=\"class\" stdset=\"0\">\n         <string>dialog-container dialog-frame</string>\n        </property>\n        <layout class=\"QGridLayout\" name=\"gridLayout_3\">\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"horizontalSpacing\">\n          <number>8</number>\n         </property>\n         <property name=\"verticalSpacing\">\n          <number>2</number>\n         </property>\n         <item row=\"1\" column=\"0\" colspan=\"3\">\n          <widget class=\"QComboBox\" name=\"boundsType\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.BoundsType</string>\n           </property>\n           <item>\n            <property name=\"text\">\n             <string>Basic.TransformWindow.BoundsType.None</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Basic.TransformWindow.BoundsType.Stretch</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Basic.TransformWindow.BoundsType.ScaleInner</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Basic.TransformWindow.BoundsType.ScaleOuter</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Basic.TransformWindow.BoundsType.ScaleToWidth</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Basic.TransformWindow.BoundsType.ScaleToHeight</string>\n            </property>\n           </item>\n           <item>\n            <property name=\"text\">\n             <string>Basic.TransformWindow.BoundsType.MaxOnly</string>\n            </property>\n           </item>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"7\" rowspan=\"4\">\n          <spacer name=\"horizontalSpacer_4\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>0</width>\n             <height>10</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item row=\"0\" column=\"0\" colspan=\"3\">\n          <widget class=\"QLabel\" name=\"label_7\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Bounds</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Bounds</string>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>subtitle</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"3\">\n          <widget class=\"QFrame\" name=\"alignmentWidget2\">\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <property name=\"lineWidth\">\n            <number>0</number>\n           </property>\n           <layout class=\"QVBoxLayout\" name=\"boundsAlignmentLayout\">\n            <property name=\"leftMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"topMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"rightMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"bottomMargin\">\n             <number>0</number>\n            </property>\n           </layout>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"3\">\n          <widget class=\"QLabel\" name=\"label_6\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Alignment</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Alignment</string>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>subtitle</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"0\">\n          <widget class=\"QLabel\" name=\"boundsWidthLabel\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.BoundsWidth</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Width</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"0\">\n          <widget class=\"QLabel\" name=\"boundsHeightLabel\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.BoundsHeight</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Height</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"1\">\n          <widget class=\"QDoubleSpinBox\" name=\"boundsWidth\">\n           <property name=\"enabled\">\n            <bool>false</bool>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.BoundsWidth</string>\n           </property>\n           <property name=\"suffix\">\n            <string> px</string>\n           </property>\n           <property name=\"decimals\">\n            <number>2</number>\n           </property>\n           <property name=\"minimum\">\n            <double>1.000000000000000</double>\n           </property>\n           <property name=\"maximum\">\n            <double>90001.000000000000000</double>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"1\">\n          <widget class=\"QDoubleSpinBox\" name=\"boundsHeight\">\n           <property name=\"enabled\">\n            <bool>false</bool>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.BoundsHeight</string>\n           </property>\n           <property name=\"suffix\">\n            <string> px</string>\n           </property>\n           <property name=\"decimals\">\n            <number>2</number>\n           </property>\n           <property name=\"minimum\">\n            <double>1.000000000000000</double>\n           </property>\n           <property name=\"maximum\">\n            <double>90001.000000000000000</double>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"2\">\n          <widget class=\"QCheckBox\" name=\"cropToBounds\">\n           <property name=\"enabled\">\n            <bool>false</bool>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>100</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.BoundsAlignment</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.CropToBounds</string>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string/>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QFrame\" name=\"cropSettings\">\n        <property name=\"lineWidth\">\n         <number>0</number>\n        </property>\n        <property name=\"class\" stdset=\"0\">\n         <string>dialog-container dialog-frame</string>\n        </property>\n        <layout class=\"QGridLayout\" name=\"gridLayout\">\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"horizontalSpacing\">\n          <number>8</number>\n         </property>\n         <property name=\"verticalSpacing\">\n          <number>2</number>\n         </property>\n         <item row=\"1\" column=\"0\">\n          <widget class=\"QLabel\" name=\"cropLeftLabel\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.CropLeft</string>\n           </property>\n           <property name=\"text\">\n            <string>Left</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"2\">\n          <widget class=\"QLabel\" name=\"cropRightLabel\">\n           <property name=\"text\">\n            <string>Right</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"3\">\n          <widget class=\"QSpinBox\" name=\"cropBottom\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.CropBottom</string>\n           </property>\n           <property name=\"suffix\">\n            <string> px</string>\n           </property>\n           <property name=\"maximum\">\n            <number>100000</number>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"4\">\n          <spacer name=\"horizontalSpacer_2\">\n           <property name=\"orientation\">\n            <enum>Qt::Horizontal</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>40</width>\n             <height>20</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item row=\"1\" column=\"3\">\n          <widget class=\"QSpinBox\" name=\"cropRight\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.CropRight</string>\n           </property>\n           <property name=\"suffix\">\n            <string> px</string>\n           </property>\n           <property name=\"maximum\">\n            <number>100000</number>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"1\">\n          <widget class=\"QSpinBox\" name=\"cropLeft\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.CropLeft</string>\n           </property>\n           <property name=\"suffix\">\n            <string> px</string>\n           </property>\n           <property name=\"maximum\">\n            <number>100000</number>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"0\">\n          <widget class=\"QLabel\" name=\"cropTopLabel\">\n           <property name=\"text\">\n            <string>Top</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"1\">\n          <widget class=\"QSpinBox\" name=\"cropTop\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>120</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"maximumSize\">\n            <size>\n             <width>120</width>\n             <height>16777215</height>\n            </size>\n           </property>\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.CropTop</string>\n           </property>\n           <property name=\"suffix\">\n            <string> px</string>\n           </property>\n           <property name=\"maximum\">\n            <number>100000</number>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"0\">\n          <widget class=\"QLabel\" name=\"label_8\">\n           <property name=\"accessibleName\">\n            <string>Basic.TransformWindow.Crop</string>\n           </property>\n           <property name=\"text\">\n            <string>Basic.TransformWindow.Crop</string>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>subtitle</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"2\">\n          <widget class=\"QLabel\" name=\"cropBottomLabel\">\n           <property name=\"text\">\n            <string>Bottom</string>\n           </property>\n           <property name=\"alignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n        <property name=\"standardButtons\">\n         <set>QDialogButtonBox::Close|QDialogButtonBox::Reset</set>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <tabstops>\n  <tabstop>positionX</tabstop>\n  <tabstop>positionY</tabstop>\n  <tabstop>sizeX</tabstop>\n  <tabstop>sizeY</tabstop>\n  <tabstop>rotation</tabstop>\n  <tabstop>boundsType</tabstop>\n  <tabstop>boundsWidth</tabstop>\n  <tabstop>boundsHeight</tabstop>\n  <tabstop>cropToBounds</tabstop>\n  <tabstop>cropLeft</tabstop>\n  <tabstop>cropRight</tabstop>\n  <tabstop>cropTop</tabstop>\n  <tabstop>cropBottom</tabstop>\n </tabstops>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>OBSBasicTransform</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>358</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSBasicVCamConfig.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSBasicVCamConfig</class>\n <widget class=\"QDialog\" name=\"OBSBasicVCamConfig\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>170</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Basic.VCam.VirtualCamera</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"outputTypeLabel\">\n     <property name=\"text\">\n      <string>Basic.VCam.OutputType</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QComboBox\" name=\"outputType\"/>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"outputSelectionLabel\">\n     <property name=\"text\">\n      <string>Basic.VCam.OutputSelection</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QComboBox\" name=\"outputSelection\"/>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"warningLabel\">\n     <property name=\"visible\">\n      <bool>false</bool>\n     </property>\n     <property name=\"text\">\n      <string>Basic.VCam.RestartWarning</string>\n     </property>\n     <property name=\"class\" stdset=\"0\">\n      <string notr=\"true\">text-warning</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>20</width>\n       <height>40</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>OBSBasicVCamConfig</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>OBSBasicVCamConfig</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSExtraBrowsers.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSExtraBrowsers</class>\n <widget class=\"QDialog\" name=\"OBSExtraBrowsers\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>785</width>\n    <height>353</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>ExtraBrowsers</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>ExtraBrowsers.Info</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QTableView\" name=\"table\">\n     <property name=\"selectionMode\">\n      <enum>QAbstractItemView::NoSelection</enum>\n     </property>\n     <attribute name=\"horizontalHeaderMinimumSectionSize\">\n      <number>23</number>\n     </attribute>\n     <attribute name=\"horizontalHeaderDefaultSectionSize\">\n      <number>23</number>\n     </attribute>\n     <attribute name=\"verticalHeaderVisible\">\n      <bool>false</bool>\n     </attribute>\n     <attribute name=\"verticalHeaderDefaultSectionSize\">\n      <number>24</number>\n     </attribute>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"apply\">\n       <property name=\"text\">\n        <string>Apply</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"close\">\n       <property name=\"text\">\n        <string>Close</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>close</sender>\n   <signal>clicked()</signal>\n   <receiver>OBSExtraBrowsers</receiver>\n   <slot>close()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>520</x>\n     <y>286</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>435</x>\n     <y>-19</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSIdianPlayground.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSIdianPlayground</class>\n <widget class=\"QDialog\" name=\"OBSIdianPlayground\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>700</width>\n    <height>700</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Idian Playground</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QScrollArea\" name=\"scrollArea\">\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"widgetResizable\">\n      <bool>true</bool>\n     </property>\n     <widget class=\"QWidget\" name=\"scrollAreaWidgetContents\">\n      <property name=\"geometry\">\n       <rect>\n        <x>0</x>\n        <y>0</y>\n        <width>786</width>\n        <height>919</height>\n       </rect>\n      </property>\n      <layout class=\"QVBoxLayout\" name=\"scrollContentsLayout\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>9</number>\n       </property>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSImporter.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSImporter</class>\n <widget class=\"QDialog\" name=\"OBSImporter\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>850</width>\n    <height>400</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Importer</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout\">\n   <item row=\"2\" column=\"0\">\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n     <property name=\"spacing\">\n      <number>6</number>\n     </property>\n     <item>\n      <widget class=\"QPushButton\" name=\"importerSelectFiles\">\n       <property name=\"text\">\n        <string>Importer.SelectFile</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"0\" column=\"0\">\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>Importer.HelpText</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"0\">\n    <widget class=\"QTableView\" name=\"tableView\">\n     <property name=\"selectionMode\">\n      <enum>QAbstractItemView::NoSelection</enum>\n     </property>\n     <attribute name=\"horizontalHeaderMinimumSectionSize\">\n      <number>23</number>\n     </attribute>\n     <attribute name=\"horizontalHeaderDefaultSectionSize\">\n      <number>23</number>\n     </attribute>\n     <attribute name=\"verticalHeaderVisible\">\n      <bool>false</bool>\n     </attribute>\n     <attribute name=\"verticalHeaderDefaultSectionSize\">\n      <number>24</number>\n     </attribute>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSLogViewer.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSLogViewer</class>\n <widget class=\"QDialog\" name=\"OBSLogViewer\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>805</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>LogViewer</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>4</number>\n   </property>\n   <item>\n    <widget class=\"OBSPlainTextEdit\" name=\"textArea\">\n     <property name=\"readOnly\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"buttonLayout\">\n     <property name=\"leftMargin\">\n      <number>10</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>10</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>0</number>\n     </property>\n     <item>\n      <widget class=\"QCheckBox\" name=\"showStartup\">\n       <property name=\"text\">\n        <string>ShowOnStartup</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"openButton\">\n       <property name=\"text\">\n        <string>OpenFile</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"clearButton\">\n       <property name=\"text\">\n        <string>Clear</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"closeButton\">\n       <property name=\"text\">\n        <string>Close</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>OBSPlainTextEdit</class>\n   <extends>QPlainTextEdit</extends>\n   <header>plain-text-edit.hpp</header>\n  </customwidget>\n </customwidgets>\n <resources>\n  <include location=\"obs.qrc\"/>\n </resources>\n <connections>\n  <connection>\n   <sender>closeButton</sender>\n   <signal>clicked()</signal>\n   <receiver>OBSLogViewer</receiver>\n   <slot>close()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>clearButton</sender>\n   <signal>clicked()</signal>\n   <receiver>textArea</receiver>\n   <slot>clear()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>20</x>\n     <y>20</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSMissingFiles.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSMissingFiles</class>\n <widget class=\"QDialog\" name=\"OBSMissingFiles\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>700</width>\n    <height>320</height>\n   </rect>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>700</width>\n    <height>320</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>MissingFiles</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout\">\n   <item row=\"0\" column=\"0\">\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n     <item>\n      <widget class=\"QLabel\" name=\"warningIcon\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>0</width>\n         <height>0</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>MissingFiles.HelpText</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"2\" column=\"0\">\n    <widget class=\"QTableView\" name=\"tableView\">\n     <property name=\"selectionMode\">\n      <enum>QAbstractItemView::NoSelection</enum>\n     </property>\n     <attribute name=\"horizontalHeaderMinimumSectionSize\">\n      <number>23</number>\n     </attribute>\n     <attribute name=\"horizontalHeaderDefaultSectionSize\">\n      <number>23</number>\n     </attribute>\n     <attribute name=\"verticalHeaderVisible\">\n      <bool>false</bool>\n     </attribute>\n     <attribute name=\"verticalHeaderDefaultSectionSize\">\n      <number>24</number>\n     </attribute>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"0\">\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <item>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n       <item>\n        <widget class=\"QLabel\" name=\"found\">\n         <property name=\"text\">\n          <string>MissingFiles.NumFound</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"horizontalSpacer\">\n         <property name=\"orientation\">\n          <enum>Qt::Horizontal</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>40</width>\n           <height>20</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n         <item>\n          <widget class=\"QPushButton\" name=\"browseButton\">\n           <property name=\"text\">\n            <string>MissingFiles.Search</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"doneButton\">\n           <property name=\"text\">\n            <string>Apply</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"cancelButton\">\n           <property name=\"text\">\n            <string>Cancel</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSPermissions.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSPermissions</class>\n <widget class=\"QDialog\" name=\"OBSPermissions\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>692</width>\n    <height>613</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Minimum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>0</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>MacPermissions.Title</string>\n  </property>\n  <property name=\"windowIcon\">\n   <iconset resource=\"obs.qrc\">\n    <normaloff>:/res/images/obs.png</normaloff>:/res/images/obs.png</iconset>\n  </property>\n  <property name=\"styleSheet\">\n   <string notr=\"true\"/>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n   <item row=\"0\" column=\"0\">\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <property name=\"spacing\">\n      <number>0</number>\n     </property>\n     <property name=\"sizeConstraint\">\n      <enum>QLayout::SetNoConstraint</enum>\n     </property>\n     <property name=\"leftMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"topMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"rightMargin\">\n      <number>0</number>\n     </property>\n     <property name=\"bottomMargin\">\n      <number>0</number>\n     </property>\n     <item>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_7\">\n       <item>\n        <widget class=\"QLabel\" name=\"label\">\n         <property name=\"text\">\n          <string>MacPermissions.Description</string>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QGridLayout\" name=\"gridLayout\">\n         <item row=\"1\" column=\"0\">\n          <spacer name=\"verticalSpacer\">\n           <property name=\"orientation\">\n            <enum>Qt::Vertical</enum>\n           </property>\n           <property name=\"sizeHint\" stdset=\"0\">\n            <size>\n             <width>20</width>\n             <height>40</height>\n            </size>\n           </property>\n          </spacer>\n         </item>\n         <item row=\"3\" column=\"0\">\n          <widget class=\"QPushButton\" name=\"continueButton\">\n           <property name=\"text\">\n            <string>MacPermissions.Continue</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"0\">\n          <widget class=\"QLabel\" name=\"label_2\">\n           <property name=\"text\">\n            <string>MacPermissions.Description.OpenDialog</string>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>true</bool>\n           </property>\n           <property name=\"bottomMargin\" stdset=\"0\">\n            <number>8</number>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"0\">\n          <layout class=\"QFormLayout\" name=\"formLayout\">\n           <property name=\"fieldGrowthPolicy\">\n            <enum>QFormLayout::AllNonFixedFieldsGrow</enum>\n           </property>\n           <property name=\"rowWrapPolicy\">\n            <enum>QFormLayout::WrapAllRows</enum>\n           </property>\n           <property name=\"topMargin\">\n            <number>22</number>\n           </property>\n           <property name=\"bottomMargin\">\n            <number>0</number>\n           </property>\n           <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_4\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>MacPermissions.Item.ScreenRecording</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"1\">\n            <widget class=\"QLabel\" name=\"label_5\">\n             <property name=\"text\">\n              <string>MacPermissions.Item.ScreenRecording.Details</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"1\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n             <property name=\"topMargin\">\n              <number>8</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>25</number>\n             </property>\n             <item>\n              <spacer name=\"horizontalSpacer\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"capturePermissionButton\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"minimumSize\">\n                <size>\n                 <width>300</width>\n                 <height>0</height>\n                </size>\n               </property>\n               <property name=\"maximumSize\">\n                <size>\n                 <width>300</width>\n                 <height>16777215</height>\n                </size>\n               </property>\n               <property name=\"baseSize\">\n                <size>\n                 <width>300</width>\n                 <height>0</height>\n                </size>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item row=\"2\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_7\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>MacPermissions.Item.Camera</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n             </property>\n            </widget>\n           </item>\n           <item row=\"2\" column=\"1\">\n            <widget class=\"QLabel\" name=\"label_8\">\n             <property name=\"text\">\n              <string>MacPermissions.Item.Camera.Details</string>\n             </property>\n             <property name=\"wordWrap\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"1\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n             <property name=\"topMargin\">\n              <number>8</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>25</number>\n             </property>\n             <item>\n              <spacer name=\"horizontalSpacer_2\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"videoPermissionButton\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"minimumSize\">\n                <size>\n                 <width>300</width>\n                 <height>0</height>\n                </size>\n               </property>\n               <property name=\"maximumSize\">\n                <size>\n                 <width>300</width>\n                 <height>16777215</height>\n                </size>\n               </property>\n               <property name=\"baseSize\">\n                <size>\n                 <width>300</width>\n                 <height>0</height>\n                </size>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item row=\"4\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_9\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>MacPermissions.Item.Microphone</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n             </property>\n            </widget>\n           </item>\n           <item row=\"4\" column=\"1\">\n            <widget class=\"QLabel\" name=\"label_10\">\n             <property name=\"text\">\n              <string>MacPermissions.Item.Microphone.Details</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"5\" column=\"1\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n             <property name=\"topMargin\">\n              <number>8</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>25</number>\n             </property>\n             <item>\n              <spacer name=\"horizontalSpacer_3\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"audioPermissionButton\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"minimumSize\">\n                <size>\n                 <width>300</width>\n                 <height>0</height>\n                </size>\n               </property>\n               <property name=\"maximumSize\">\n                <size>\n                 <width>300</width>\n                 <height>16777215</height>\n                </size>\n               </property>\n               <property name=\"baseSize\">\n                <size>\n                 <width>300</width>\n                 <height>0</height>\n                </size>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item row=\"6\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_3\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>MacPermissions.Item.InputMonitoring</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n             </property>\n            </widget>\n           </item>\n           <item row=\"6\" column=\"1\">\n            <widget class=\"QLabel\" name=\"label_13\">\n             <property name=\"text\">\n              <string>MacPermissions.Item.InputMonitoring.Details</string>\n             </property>\n             <property name=\"wordWrap\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"7\" column=\"1\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n             <property name=\"sizeConstraint\">\n              <enum>QLayout::SetDefaultConstraint</enum>\n             </property>\n             <item>\n              <widget class=\"QWidget\"/>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"inputMonitoringPermissionButton\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"minimumSize\">\n                <size>\n                 <width>300</width>\n                 <height>0</height>\n                </size>\n               </property>\n               <property name=\"maximumSize\">\n                <size>\n                 <width>300</width>\n                 <height>16777215</height>\n                </size>\n               </property>\n               <property name=\"baseSize\">\n                <size>\n                 <width>300</width>\n                 <height>0</height>\n                </size>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item row=\"8\" column=\"1\">\n            <widget class=\"QLabel\" name=\"accessibilityPermissionLabel\">\n             <property name=\"text\">\n              <string>MacPermissions.Item.Accessibility.Details</string>\n             </property>\n             <property name=\"wordWrap\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"9\" column=\"1\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\">\n             <property name=\"sizeConstraint\">\n              <enum>QLayout::SetDefaultConstraint</enum>\n             </property>\n             <property name=\"topMargin\">\n              <number>8</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>25</number>\n             </property>\n             <item>\n              <spacer name=\"horizontalSpacer_4\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>20</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n             <item>\n              <widget class=\"QPushButton\" name=\"accessibilityPermissionButton\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"minimumSize\">\n                <size>\n                 <width>300</width>\n                 <height>0</height>\n                </size>\n               </property>\n               <property name=\"maximumSize\">\n                <size>\n                 <width>300</width>\n                 <height>16777215</height>\n                </size>\n               </property>\n               <property name=\"baseSize\">\n                <size>\n                 <width>300</width>\n                 <height>0</height>\n                </size>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n          </layout>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources>\n  <include location=\"obs.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSRemux.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSRemux</class>\n <widget class=\"QDialog\" name=\"OBSRemux\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>850</width>\n    <height>400</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>RemuxRecordings</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QGridLayout\" name=\"gridLayout\">\n   <item row=\"0\" column=\"0\">\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>Remux.HelpText</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"0\">\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n     <property name=\"spacing\">\n      <number>6</number>\n     </property>\n     <item>\n      <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n       <property name=\"standardButtons\">\n        <set>QDialogButtonBox::Close|QDialogButtonBox::Ok|QDialogButtonBox::Reset|QDialogButtonBox::RestoreDefaults</set>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item row=\"1\" column=\"0\">\n    <widget class=\"QTableView\" name=\"tableView\">\n     <property name=\"selectionMode\">\n      <enum>QAbstractItemView::NoSelection</enum>\n     </property>\n     <attribute name=\"horizontalHeaderMinimumSectionSize\">\n      <number>23</number>\n     </attribute>\n     <attribute name=\"horizontalHeaderDefaultSectionSize\">\n      <number>23</number>\n     </attribute>\n     <attribute name=\"verticalHeaderVisible\">\n      <bool>false</bool>\n     </attribute>\n     <attribute name=\"verticalHeaderDefaultSectionSize\">\n      <number>24</number>\n     </attribute>\n    </widget>\n   </item>\n   <item row=\"2\" column=\"0\">\n    <widget class=\"QProgressBar\" name=\"progressBar\">\n     <property name=\"value\">\n      <number>24</number>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSUpdate.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSUpdate</class>\n <widget class=\"QDialog\" name=\"OBSUpdate\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>611</width>\n    <height>526</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Updater.Title</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>Updater.Text</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QTextBrowser\" name=\"text\">\n     <property name=\"readOnly\">\n      <bool>true</bool>\n     </property>\n     <property name=\"html\">\n      <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;\n&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;\np, li { white-space: pre-wrap; }\nhr { height: 1px; border-width: 0; }\nli.unchecked::marker { content: &quot;\\2610&quot;; }\nli.checked::marker { content: &quot;\\2612&quot;; }\n&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;\n&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"openExternalLinks\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"Line\" name=\"line\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"yes\">\n       <property name=\"text\">\n        <string>Updater.UpdateNow</string>\n       </property>\n       <property name=\"default\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"no\">\n       <property name=\"text\">\n        <string>Updater.RemindMeLater</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"skip\">\n       <property name=\"text\">\n        <string>Updater.Skip</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/OBSYoutubeActions.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OBSYoutubeActions</class>\n <widget class=\"QDialog\" name=\"OBSYoutubeActions\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>821</width>\n    <height>738</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>YouTube.Actions.WindowTitle</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <property name=\"spacing\">\n    <number>6</number>\n   </property>\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetMinimumSize</enum>\n   </property>\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"ytEventCreate\">\n      <attribute name=\"title\">\n       <string>YouTube.Actions.CreateNewEvent</string>\n      </attribute>\n      <layout class=\"QGridLayout\" name=\"gridLayout\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"spacing\">\n        <number>0</number>\n       </property>\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QScrollArea\" name=\"scrollArea_2\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"minimumSize\">\n          <size>\n           <width>0</width>\n           <height>0</height>\n          </size>\n         </property>\n         <property name=\"widgetResizable\">\n          <bool>true</bool>\n         </property>\n         <widget class=\"QWidget\" name=\"scrollAreaWidgetContents_3\">\n          <property name=\"geometry\">\n           <rect>\n            <x>0</x>\n            <y>0</y>\n            <width>795</width>\n            <height>657</height>\n           </rect>\n          </property>\n          <layout class=\"QFormLayout\" name=\"formLayout_2\">\n           <property name=\"fieldGrowthPolicy\">\n            <enum>QFormLayout::ExpandingFieldsGrow</enum>\n           </property>\n           <property name=\"labelAlignment\">\n            <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n           </property>\n           <property name=\"topMargin\">\n            <number>9</number>\n           </property>\n           <item row=\"2\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label\">\n             <property name=\"text\">\n              <string>YouTube.Actions.Title</string>\n             </property>\n             <property name=\"buddy\">\n              <cstring>title</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"2\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"title\">\n             <property name=\"text\">\n              <string>YouTube.Actions.MyBroadcast</string>\n             </property>\n             <property name=\"maxLength\">\n              <number>100</number>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_2\">\n             <property name=\"text\">\n              <string>YouTube.Actions.Description</string>\n             </property>\n             <property name=\"buddy\">\n              <cstring>description</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"1\">\n            <widget class=\"QPlainTextEdit\" name=\"description\">\n             <property name=\"tabChangesFocus\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"4\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_4\">\n             <property name=\"text\">\n              <string>YouTube.Actions.Privacy</string>\n             </property>\n             <property name=\"buddy\">\n              <cstring>privacyBox</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"4\" column=\"1\">\n            <widget class=\"QComboBox\" name=\"privacyBox\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"sizeAdjustPolicy\">\n              <enum>QComboBox::AdjustToContentsOnFirstShow</enum>\n             </property>\n            </widget>\n           </item>\n           <item row=\"5\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_5\">\n             <property name=\"text\">\n              <string>YouTube.Actions.Category</string>\n             </property>\n             <property name=\"buddy\">\n              <cstring>categoryBox</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"5\" column=\"1\">\n            <widget class=\"QComboBox\" name=\"categoryBox\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n            </widget>\n           </item>\n           <item row=\"6\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_8\">\n             <property name=\"text\">\n              <string>YouTube.Actions.MadeForKids</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"6\" column=\"1\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n             <item>\n              <widget class=\"QRadioButton\" name=\"notMakeForKids\">\n               <property name=\"text\">\n                <string>YouTube.Actions.MadeForKids.No</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"helpMadeForKids\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"text\">\n                <string>YouTube.Actions.MadeForKids.Help</string>\n               </property>\n               <property name=\"textFormat\">\n                <enum>Qt::RichText</enum>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item row=\"7\" column=\"1\">\n            <widget class=\"QRadioButton\" name=\"yesMakeForKids\">\n             <property name=\"text\">\n              <string>YouTube.Actions.MadeForKids.Yes</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"8\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_6\">\n             <property name=\"text\">\n              <string>YouTube.Actions.Thumbnail</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"8\" column=\"1\">\n            <widget class=\"ClickableLabel\" name=\"thumbnailPreview\">\n             <property name=\"enabled\">\n              <bool>true</bool>\n             </property>\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"minimumSize\">\n              <size>\n               <width>162</width>\n               <height>92</height>\n              </size>\n             </property>\n             <property name=\"maximumSize\">\n              <size>\n               <width>162</width>\n               <height>92</height>\n              </size>\n             </property>\n             <property name=\"frameShape\">\n              <enum>QFrame::Box</enum>\n             </property>\n             <property name=\"text\">\n              <string/>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignCenter</set>\n             </property>\n            </widget>\n           </item>\n           <item row=\"9\" column=\"1\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_13\">\n             <property name=\"rightMargin\">\n              <number>0</number>\n             </property>\n             <property name=\"bottomMargin\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QPushButton\" name=\"selectFileButton\">\n               <property name=\"text\">\n                <string>YouTube.Actions.Thumbnail.SelectFile</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"selectedFileName\">\n               <property name=\"text\">\n                <string>YouTube.Actions.Thumbnail.NoFileSelected</string>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item row=\"26\" column=\"1\">\n            <spacer name=\"verticalSpacer_2\">\n             <property name=\"orientation\">\n              <enum>Qt::Vertical</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>20</width>\n               <height>40</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n           <item row=\"10\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_7\">\n             <property name=\"font\">\n              <font>\n               <bold>true</bold>\n              </font>\n             </property>\n             <property name=\"text\">\n              <string>YouTube.Actions.AdditionalSettings</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"14\" column=\"1\">\n            <widget class=\"QCheckBox\" name=\"checkScheduledLater\">\n             <property name=\"enabled\">\n              <bool>true</bool>\n             </property>\n             <property name=\"text\">\n              <string>YouTube.Actions.ScheduleForLater</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"13\" column=\"1\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_7\">\n             <item>\n              <widget class=\"QCheckBox\" name=\"check360Video\">\n               <property name=\"text\">\n                <string>YouTube.Actions.360Video</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"help360Video\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"text\">\n                <string>YouTube.Actions.360Video.Help</string>\n               </property>\n               <property name=\"textFormat\">\n                <enum>Qt::RichText</enum>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <spacer name=\"horizontalSpacer_10\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>0</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n            </layout>\n           </item>\n           <item row=\"15\" column=\"1\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\">\n             <item>\n              <widget class=\"QCheckBox\" name=\"checkAutoStart\">\n               <property name=\"enabled\">\n                <bool>true</bool>\n               </property>\n               <property name=\"text\">\n                <string>YouTube.Actions.EnableAutoStart</string>\n               </property>\n               <property name=\"checkable\">\n                <bool>true</bool>\n               </property>\n               <property name=\"checked\">\n                <bool>true</bool>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QLabel\" name=\"helpAutoStartStop\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"text\">\n                <string notr=\"true\">&lt;a href=&quot;.&quot;&gt;(?)&lt;/a&gt;</string>\n               </property>\n               <property name=\"textFormat\">\n                <enum>Qt::RichText</enum>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <spacer name=\"horizontalSpacer_9\">\n               <property name=\"orientation\">\n                <enum>Qt::Horizontal</enum>\n               </property>\n               <property name=\"sizeHint\" stdset=\"0\">\n                <size>\n                 <width>40</width>\n                 <height>0</height>\n                </size>\n               </property>\n              </spacer>\n             </item>\n            </layout>\n           </item>\n           <item row=\"16\" column=\"1\">\n            <widget class=\"QCheckBox\" name=\"checkAutoStop\">\n             <property name=\"enabled\">\n              <bool>true</bool>\n             </property>\n             <property name=\"text\">\n              <string>YouTube.Actions.EnableAutoStop</string>\n             </property>\n             <property name=\"checked\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"17\" column=\"1\">\n            <widget class=\"QDateTimeEdit\" name=\"scheduledTime\">\n             <property name=\"enabled\">\n              <bool>true</bool>\n             </property>\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"calendarPopup\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"10\" column=\"1\">\n            <spacer name=\"horizontalSpacer\">\n             <property name=\"orientation\">\n              <enum>Qt::Horizontal</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>40</width>\n               <height>0</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n           <item row=\"12\" column=\"1\">\n            <widget class=\"QCheckBox\" name=\"checkDVR\">\n             <property name=\"text\">\n              <string>YouTube.Actions.EnableDVR</string>\n             </property>\n             <property name=\"checked\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n           <item row=\"11\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_3\">\n             <property name=\"text\">\n              <string>YouTube.Actions.Latency</string>\n             </property>\n             <property name=\"buddy\">\n              <cstring>latencyBox</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"11\" column=\"1\">\n            <widget class=\"QComboBox\" name=\"latencyBox\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"1\">\n            <widget class=\"QCheckBox\" name=\"checkRememberSettings\">\n             <property name=\"text\">\n              <string>YouTube.Actions.RememberSettings</string>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </widget>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"ytEventList\">\n      <attribute name=\"title\">\n       <string>YouTube.Actions.ChooseEvent</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n       <item>\n        <widget class=\"QScrollArea\" name=\"scrollArea\">\n         <property name=\"verticalScrollBarPolicy\">\n          <enum>Qt::ScrollBarAsNeeded</enum>\n         </property>\n         <property name=\"widgetResizable\">\n          <bool>true</bool>\n         </property>\n         <widget class=\"QWidget\" name=\"scrollAreaWidgetContents\">\n          <property name=\"geometry\">\n           <rect>\n            <x>0</x>\n            <y>0</y>\n            <width>191</width>\n            <height>216</height>\n           </rect>\n          </property>\n          <property name=\"sizePolicy\">\n           <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n            <horstretch>0</horstretch>\n            <verstretch>0</verstretch>\n           </sizepolicy>\n          </property>\n          <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n           <item>\n            <widget class=\"QLabel\" name=\"label_11\">\n             <property name=\"styleSheet\">\n              <string notr=\"true\">border: 1px solid black;</string>\n             </property>\n             <property name=\"text\">\n              <string>&lt;big&gt;Friday Fortnite Stream&lt;/big&gt;&lt;br/&gt;scheduled for 11/11/20 2:00pm</string>\n             </property>\n             <property name=\"textFormat\">\n              <enum>Qt::RichText</enum>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignCenter</set>\n             </property>\n             <property name=\"margin\">\n              <number>4</number>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QLabel\" name=\"label_12\">\n             <property name=\"styleSheet\">\n              <string notr=\"true\">border: 1px solid black;</string>\n             </property>\n             <property name=\"text\">\n              <string>&lt;big&gt;Friday Fortnite Stream&lt;/big&gt;&lt;br/&gt;scheduled for 11/11/20 2:00pm</string>\n             </property>\n             <property name=\"textFormat\">\n              <enum>Qt::RichText</enum>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignCenter</set>\n             </property>\n             <property name=\"margin\">\n              <number>4</number>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QLabel\" name=\"label_13\">\n             <property name=\"styleSheet\">\n              <string notr=\"true\">border: 1px solid black;</string>\n             </property>\n             <property name=\"text\">\n              <string>&lt;big&gt;Friday Fortnite Stream&lt;/big&gt;&lt;br/&gt;scheduled for 11/11/20 2:00pm</string>\n             </property>\n             <property name=\"textFormat\">\n              <enum>Qt::RichText</enum>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignCenter</set>\n             </property>\n             <property name=\"margin\">\n              <number>4</number>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QLabel\" name=\"label_14\">\n             <property name=\"styleSheet\">\n              <string notr=\"true\">border: 1px solid black;</string>\n             </property>\n             <property name=\"text\">\n              <string>&lt;big&gt;Friday Fortnite Stream&lt;/big&gt;&lt;br/&gt;scheduled for 11/11/20 2:00pm</string>\n             </property>\n             <property name=\"textFormat\">\n              <enum>Qt::RichText</enum>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignCenter</set>\n             </property>\n             <property name=\"margin\">\n              <number>4</number>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </widget>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QWidget\" name=\"widget\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"spacing\">\n       <number>0</number>\n      </property>\n      <item row=\"0\" column=\"0\">\n       <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\" stretch=\"1,2,0,6\">\n        <property name=\"spacing\">\n         <number>10</number>\n        </property>\n        <property name=\"sizeConstraint\">\n         <enum>QLayout::SetNoConstraint</enum>\n        </property>\n        <item>\n         <widget class=\"QPushButton\" name=\"cancelButton\">\n          <property name=\"text\">\n           <string>Cancel</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"pushButton\">\n          <property name=\"text\">\n           <string>YouTube.Actions.Dashboard</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"saveButton\">\n          <property name=\"text\">\n           <string>YouTube.Actions.Create_Ready</string>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"okButton\">\n          <property name=\"text\">\n           <string>YouTube.Actions.Create_GoLive</string>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ClickableLabel</class>\n   <extends>QLabel</extends>\n   <header>components/ClickableLabel.hpp</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/PluginManagerWindow.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>PluginManagerWindow</class>\n <widget class=\"QDialog\" name=\"PluginManagerWindow\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>861</width>\n    <height>400</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>PluginManager</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QFrame\" name=\"dialogInner\">\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>0</number>\n     </property>\n     <property name=\"class\" stdset=\"0\">\n      <string>dialog-container</string>\n     </property>\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n      <property name=\"spacing\">\n       <number>0</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QFrame\" name=\"frame\">\n        <property name=\"frameShape\">\n         <enum>QFrame::NoFrame</enum>\n        </property>\n        <property name=\"frameShadow\">\n         <enum>QFrame::Plain</enum>\n        </property>\n        <property name=\"lineWidth\">\n         <number>0</number>\n        </property>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout\" stretch=\"1,4\">\n         <property name=\"spacing\">\n          <number>0</number>\n         </property>\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QFrame\" name=\"sectionFrame\">\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <property name=\"lineWidth\">\n            <number>0</number>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>dialog-frame</string>\n           </property>\n           <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n            <property name=\"spacing\">\n             <number>0</number>\n            </property>\n            <property name=\"leftMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"topMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"rightMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"bottomMargin\">\n             <number>0</number>\n            </property>\n            <item>\n             <widget class=\"QListWidget\" name=\"sectionList\">\n              <property name=\"frameShape\">\n               <enum>QFrame::NoFrame</enum>\n              </property>\n              <property name=\"frameShadow\">\n               <enum>QFrame::Plain</enum>\n              </property>\n              <property name=\"lineWidth\">\n               <number>0</number>\n              </property>\n              <property name=\"editTriggers\">\n               <set>QAbstractItemView::NoEditTriggers</set>\n              </property>\n              <property name=\"showDropIndicator\" stdset=\"0\">\n               <bool>false</bool>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QFrame\" name=\"modulesFrame\">\n           <property name=\"frameShape\">\n            <enum>QFrame::NoFrame</enum>\n           </property>\n           <property name=\"frameShadow\">\n            <enum>QFrame::Plain</enum>\n           </property>\n           <property name=\"lineWidth\">\n            <number>0</number>\n           </property>\n           <property name=\"class\" stdset=\"0\">\n            <string>dialog-container dialog-frame</string>\n           </property>\n           <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n            <property name=\"spacing\">\n             <number>0</number>\n            </property>\n            <property name=\"leftMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"topMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"rightMargin\">\n             <number>0</number>\n            </property>\n            <property name=\"bottomMargin\">\n             <number>0</number>\n            </property>\n            <item>\n             <widget class=\"QLabel\" name=\"label\">\n              <property name=\"frameShape\">\n               <enum>QFrame::NoFrame</enum>\n              </property>\n              <property name=\"lineWidth\">\n               <number>0</number>\n              </property>\n              <property name=\"text\">\n               <string>PluginManager.Section.Manage.Title</string>\n              </property>\n              <property name=\"indent\">\n               <number>0</number>\n              </property>\n              <property name=\"class\" stdset=\"0\">\n               <string>text-title</string>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QScrollArea\" name=\"modulesListContainer\">\n              <property name=\"frameShape\">\n               <enum>QFrame::NoFrame</enum>\n              </property>\n              <property name=\"frameShadow\">\n               <enum>QFrame::Plain</enum>\n              </property>\n              <property name=\"lineWidth\">\n               <number>0</number>\n              </property>\n              <property name=\"horizontalScrollBarPolicy\">\n               <enum>Qt::ScrollBarAlwaysOff</enum>\n              </property>\n              <property name=\"sizeAdjustPolicy\">\n               <enum>QAbstractScrollArea::AdjustToContents</enum>\n              </property>\n              <property name=\"widgetResizable\">\n               <bool>false</bool>\n              </property>\n              <property name=\"alignment\">\n               <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n              </property>\n              <property name=\"class\" stdset=\"0\">\n               <string/>\n              </property>\n              <widget class=\"QWidget\" name=\"modulesListContents\">\n               <property name=\"geometry\">\n                <rect>\n                 <x>0</x>\n                 <y>0</y>\n                 <width>689</width>\n                 <height>50</height>\n                </rect>\n               </property>\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <layout class=\"QVBoxLayout\" name=\"verticalLayout_5\">\n                <property name=\"spacing\">\n                 <number>0</number>\n                </property>\n                <property name=\"leftMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"topMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"rightMargin\">\n                 <number>0</number>\n                </property>\n                <property name=\"bottomMargin\">\n                 <number>0</number>\n                </property>\n                <item>\n                 <widget class=\"QFrame\" name=\"modulesList\">\n                  <property name=\"minimumSize\">\n                   <size>\n                    <width>0</width>\n                    <height>20</height>\n                   </size>\n                  </property>\n                  <property name=\"frameShape\">\n                   <enum>QFrame::NoFrame</enum>\n                  </property>\n                  <property name=\"frameShadow\">\n                   <enum>QFrame::Plain</enum>\n                  </property>\n                  <property name=\"lineWidth\">\n                   <number>0</number>\n                  </property>\n                  <layout class=\"QVBoxLayout\" name=\"verticalLayout_6\">\n                   <property name=\"spacing\">\n                    <number>0</number>\n                   </property>\n                   <property name=\"leftMargin\">\n                    <number>0</number>\n                   </property>\n                   <property name=\"topMargin\">\n                    <number>0</number>\n                   </property>\n                   <property name=\"rightMargin\">\n                    <number>0</number>\n                   </property>\n                   <property name=\"bottomMargin\">\n                    <number>0</number>\n                   </property>\n                  </layout>\n                 </widget>\n                </item>\n                <item>\n                 <spacer name=\"verticalSpacer\">\n                  <property name=\"orientation\">\n                   <enum>Qt::Vertical</enum>\n                  </property>\n                  <property name=\"sizeHint\" stdset=\"0\">\n                   <size>\n                    <width>10</width>\n                    <height>0</height>\n                   </size>\n                  </property>\n                 </spacer>\n                </item>\n               </layout>\n              </widget>\n             </widget>\n            </item>\n            <item>\n             <widget class=\"QLabel\" name=\"manageRestartLabel\">\n              <property name=\"text\">\n               <string>PluginManager.Restart</string>\n              </property>\n              <property name=\"alignment\">\n               <set>Qt::AlignCenter</set>\n              </property>\n              <property name=\"class\" stdset=\"0\">\n               <string>frame-notice</string>\n              </property>\n             </widget>\n            </item>\n           </layout>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QFrame\" name=\"footer\">\n        <property name=\"frameShape\">\n         <enum>QFrame::NoFrame</enum>\n        </property>\n        <property name=\"frameShadow\">\n         <enum>QFrame::Plain</enum>\n        </property>\n        <property name=\"lineWidth\">\n         <number>0</number>\n        </property>\n        <property name=\"class\" stdset=\"0\">\n         <string>dialog-container</string>\n        </property>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n         <property name=\"spacing\">\n          <number>0</number>\n         </property>\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n           <property name=\"standardButtons\">\n            <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/StatusBarWidget.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>StatusBarWidget</class>\n <widget class=\"QWidget\" name=\"StatusBarWidget\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>714</width>\n    <height>34</height>\n   </rect>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>34</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <property name=\"spacing\">\n    <number>0</number>\n   </property>\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>6</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QFrame\" name=\"messageFrame\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"styleSheet\">\n      <string notr=\"true\">border: none;</string>\n     </property>\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_9\">\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QLabel\" name=\"message\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Ignored\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>Message</string>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"delayFrame\">\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>0</number>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_7\">\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QLabel\" name=\"delayInfo\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>DelayInfo</string>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"issuesFrame\">\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>0</number>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\">\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QLabel\" name=\"droppedFrames\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>DroppedFrames</string>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"networkFrame\">\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>0</number>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n      <property name=\"spacing\">\n       <number>6</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QLabel\" name=\"statusIcon\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string/>\n        </property>\n        <property name=\"pixmap\">\n         <pixmap resource=\"obs.qrc\">:/res/images/network-inactive.svg</pixmap>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLabel\" name=\"kbps\">\n        <property name=\"enabled\">\n         <bool>true</bool>\n        </property>\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>0 kbps</string>\n        </property>\n        <property name=\"alignment\">\n         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"streamFrame\">\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>0</number>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QLabel\" name=\"streamIcon\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string/>\n        </property>\n        <property name=\"pixmap\">\n         <pixmap resource=\"obs.qrc\">:/res/images/streaming-inactive.svg</pixmap>\n        </property>\n        <property name=\"scaledContents\">\n         <bool>false</bool>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLabel\" name=\"streamTime\">\n        <property name=\"enabled\">\n         <bool>true</bool>\n        </property>\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>00:00:00</string>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"recordFrame\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>0</number>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QLabel\" name=\"recordIcon\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string/>\n        </property>\n        <property name=\"pixmap\">\n         <pixmap resource=\"obs.qrc\">:/res/images/recording-inactive.svg</pixmap>\n        </property>\n        <property name=\"scaledContents\">\n         <bool>false</bool>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLabel\" name=\"recordTime\">\n        <property name=\"enabled\">\n         <bool>true</bool>\n        </property>\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>00:00:00</string>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"cpuFrame\">\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>0</number>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QLabel\" name=\"cpuUsage\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>CPU: 0.0%</string>\n        </property>\n        <property name=\"alignment\">\n         <set>Qt::AlignCenter</set>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"fpsFrame\">\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>0</number>\n     </property>\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_8\">\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QLabel\" name=\"fpsCurrent\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Maximum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>0.00 / 0.00 FPS</string>\n        </property>\n        <property name=\"alignment\">\n         <set>Qt::AlignCenter</set>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources>\n  <include location=\"obs.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/XML-Schema-Qt5.15.xsd",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Includes modified casing due to Qt Creator 5.0.0: cursorshape -> cursorShape -->\n<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n\n    <xs:element name=\"ui\" type=\"UI\" />\n\n    <xs:complexType name=\"UI\">\n        <xs:all>\n            <xs:element name=\"author\" type=\"xs:string\" minOccurs=\"0\" />\n            <xs:element name=\"comment\" type=\"xs:string\" minOccurs=\"0\" />\n            <xs:element name=\"exportmacro\" type=\"xs:string\" minOccurs=\"0\" />\n            <xs:element name=\"class\" type=\"xs:string\" />\n            <xs:element name=\"widget\" type=\"Widget\" />\n            <xs:element name=\"layoutdefault\" type=\"LayoutDefault\" minOccurs=\"0\" />\n            <xs:element name=\"layoutfunction\" type=\"LayoutFunction\" minOccurs=\"0\" />\n            <xs:element name=\"pixmapfunction\" type=\"xs:string\" minOccurs=\"0\" />\n            <xs:element name=\"customwidgets\" type=\"CustomWidgets\" minOccurs=\"0\" />\n            <xs:element name=\"tabstops\" type=\"TabStops\" minOccurs=\"0\" />\n            <!-- <xs:element name=\"images\" use=\"deprecated\" /> -->\n            <xs:element name=\"includes\" type=\"Includes\" minOccurs=\"0\" />\n            <xs:element name=\"resources\" type=\"Resources\" minOccurs=\"0\" />\n            <xs:element name=\"connections\" type=\"Connections\" minOccurs=\"0\" />\n            <xs:element name=\"designerdata\" type=\"DesignerData\" minOccurs=\"0\" />\n            <xs:element name=\"slots\" type=\"Slots\" minOccurs=\"0\" />\n            <xs:element name=\"buttongroups\" type=\"ButtonGroups\" minOccurs=\"0\" />\n        </xs:all>\n        <xs:attribute name=\"version\" type=\"xs:string\" />\n        <xs:attribute name=\"language\" type=\"xs:string\" />\n        <xs:attribute name=\"displayname\" type=\"xs:string\" />\n        <xs:attribute name=\"idbasedtr\" type=\"xs:boolean\" />\n        <xs:attribute name=\"connectslotsbyname\" type=\"xs:boolean\" />\n        <!-- Legacy attribute generated by uic3 -->\n        <xs:attribute name=\"stdsetdef\" type=\"xs:integer\" />\n        <!-- Legacy attribute generated by the VS integration -->\n        <xs:attribute name=\"stdSetDef\" type=\"xs:integer\" />\n    </xs:complexType>\n\n    <!-- include begin -->\n    <xs:complexType name=\"Includes\">\n        <xs:sequence>\n            <xs:element name=\"include\" type=\"Include\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"Include\" mixed=\"true\">\n        <xs:attribute name=\"location\" type=\"xs:string\" />\n        <xs:attribute name=\"impldecl\" type=\"xs:string\" />\n    </xs:complexType>\n    <!-- include end -->\n\n    <!-- resource begin -->\n    <xs:complexType name=\"Resources\">\n        <xs:sequence>\n            <xs:element name=\"include\" type=\"Resource\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"Resource\">\n        <xs:attribute name=\"location\" type=\"xs:string\" />\n    </xs:complexType>\n    <!-- resource end -->\n\n    <!-- actions begin -->\n    <xs:complexType name=\"ActionGroup\">\n        <xs:sequence maxOccurs=\"unbounded\">\n            <xs:element name=\"action\" type=\"Action\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"actiongroup\" type=\"ActionGroup\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"property\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"attribute\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"Action\">\n        <xs:sequence maxOccurs=\"unbounded\">\n            <xs:element name=\"property\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"attribute\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n        <xs:attribute name=\"menu\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"ActionRef\">\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n    </xs:complexType>\n    <!-- actions end -->\n\n    <!-- button groups begin -->\n    <xs:complexType name=\"ButtonGroup\">\n        <xs:sequence maxOccurs=\"unbounded\">\n            <xs:element name=\"property\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"attribute\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"ButtonGroups\">\n        <xs:sequence>\n            <xs:element name=\"buttongroup\" type=\"ButtonGroup\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n    <!--  button groups end -->\n\n    <!-- custom widget support begin -->\n    <xs:complexType name=\"CustomWidgets\">\n        <xs:sequence>\n            <xs:element name=\"customwidget\" type=\"CustomWidget\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"Header\" mixed=\"true\">\n        <xs:attribute name=\"location\" type=\"xs:string\" /> <!-- local or global -->\n    </xs:complexType>\n\n    <xs:complexType name=\"CustomWidget\">\n        <xs:all>\n            <xs:element name=\"class\" type=\"xs:string\" />\n            <xs:element name=\"extends\" type=\"xs:string\" />\n            <xs:element name=\"header\" type=\"Header\" />\n            <xs:element name=\"sizehint\" type=\"Size\" minOccurs=\"0\" />\n            <xs:element name=\"addpagemethod\" type=\"xs:string\" minOccurs=\"0\" />\n            <xs:element name=\"container\" type=\"xs:integer\" minOccurs=\"0\" />\n            <!-- <xs:element name=\"sizepolicy\" use=\"deprecated\" /> -->\n            <xs:element name=\"pixmap\" type=\"xs:string\" minOccurs=\"0\" />\n            <!-- <xs:element name=\"script\" use=\"deprecated\" /> -->\n            <!-- <xs:element name=\"properties\" use=\"deprecated\" /> -->\n            <xs:element name=\"slots\" type=\"Slots\" minOccurs=\"0\" />\n            <xs:element name=\"propertyspecifications\" type=\"PropertySpecifications\" minOccurs=\"0\" />\n        </xs:all>\n    </xs:complexType>\n\n    <!-- custom widget support end -->\n\n    <xs:complexType name=\"LayoutDefault\">\n        <xs:attribute name=\"spacing\" type=\"xs:integer\" />\n        <xs:attribute name=\"margin\" type=\"xs:integer\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"LayoutFunction\">\n        <xs:attribute name=\"spacing\" type=\"xs:string\" />\n        <xs:attribute name=\"margin\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"TabStops\">\n        <xs:sequence>\n            <xs:element name=\"tabstop\" type=\"xs:string\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"Layout\">\n        <xs:sequence maxOccurs=\"unbounded\">\n            <xs:element name=\"property\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"attribute\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"item\" type=\"LayoutItem\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n        <xs:attribute name=\"class\" type=\"xs:string\" />\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n        <xs:attribute name=\"stretch\" type=\"xs:string\" />\n        <xs:attribute name=\"rowstretch\" type=\"xs:string\" />\n        <xs:attribute name=\"columnstretch\" type=\"xs:string\" />\n        <xs:attribute name=\"rowminimumheight\" type=\"xs:string\" />\n        <xs:attribute name=\"columnminimumwidth\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"LayoutItem\">\n        <xs:choice>\n            <xs:element name=\"widget\" type=\"Widget\" />\n            <xs:element name=\"layout\" type=\"Layout\" />\n            <xs:element name=\"spacer\" type=\"Spacer\" />\n        </xs:choice>\n        <xs:attribute name=\"row\" type=\"xs:integer\" />\n        <xs:attribute name=\"column\" type=\"xs:integer\" />\n        <xs:attribute name=\"rowspan\" type=\"xs:integer\" />\n        <xs:attribute name=\"colspan\" type=\"xs:integer\" />\n        <xs:attribute name=\"alignment\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <!-- item view begin -->\n    <xs:complexType name=\"Row\">\n        <xs:sequence>\n            <xs:element name=\"property\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"Column\">\n        <xs:sequence>\n            <xs:element name=\"property\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"Item\">\n        <xs:sequence maxOccurs=\"unbounded\">\n            <xs:element name=\"property\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"item\" type=\"Item\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n        <xs:attribute name=\"row\" type=\"xs:integer\" />\n        <xs:attribute name=\"column\" type=\"xs:integer\" />\n    </xs:complexType>\n    <!-- item view end -->\n\n    <xs:complexType name=\"Widget\">\n        <!-- Order does not matter (cannot use xs:all here\n                     because of unbounded row, column, item -->\n        <xs:sequence>\n            <xs:element name=\"class\" type=\"xs:string\" minOccurs=\"0\" maxOccurs=\"unbounded\" /> <!-- unbounded kept for compatibility reasons -->\n            <xs:element name=\"property\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <!-- <xs:element name=\"script\" use=\"deprecated\" /> -->\n            <!-- <xs:element name=\"widgetdata\" use=\"deprecated\" /> -->\n            <xs:element name=\"attribute\" type=\"Property\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <!-- item view begin -->\n            <xs:element name=\"row\" type=\"Row\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"column\" type=\"Column\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"item\" type=\"Item\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <!-- item view end -->\n            <xs:element name=\"layout\" type=\"Layout\" minOccurs=\"0\" maxOccurs=\"unbounded\" /> <!-- unbounded kept for compatibility reasons -->\n            <xs:element name=\"widget\" type=\"Widget\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"action\" type=\"Action\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"actiongroup\" type=\"ActionGroup\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"addaction\" type=\"ActionRef\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"zorder\" type=\"xs:string\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n        <xs:attribute name=\"class\" type=\"xs:string\" />\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n        <xs:attribute name=\"native\" type=\"xs:boolean\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"Spacer\">\n        <xs:sequence>\n            <xs:element name=\"property\" type=\"Property\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"Color\">\n        <xs:all>\n            <xs:element name=\"red\" type=\"xs:integer\" />\n            <xs:element name=\"green\" type=\"xs:integer\" />\n            <xs:element name=\"blue\" type=\"xs:integer\" />\n        </xs:all>\n        <xs:attribute name=\"alpha\" type=\"xs:integer\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"GradientStop\">\n        <xs:sequence>\n            <xs:element name=\"color\" type=\"Color\" />\n        </xs:sequence>\n        <xs:attribute name=\"position\" type=\"xs:double\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"Gradient\">\n        <xs:sequence>\n            <xs:element name=\"gradientStop\" type=\"GradientStop\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n        <xs:attribute name=\"startx\" type=\"xs:double\" />\n        <xs:attribute name=\"starty\" type=\"xs:double\" />\n        <xs:attribute name=\"endx\" type=\"xs:double\" />\n        <xs:attribute name=\"endy\" type=\"xs:double\" />\n        <xs:attribute name=\"centralx\" type=\"xs:double\" />\n        <xs:attribute name=\"centraly\" type=\"xs:double\" />\n        <xs:attribute name=\"focalx\" type=\"xs:double\" />\n        <xs:attribute name=\"focaly\" type=\"xs:double\" />\n        <xs:attribute name=\"radius\" type=\"xs:double\" />\n        <xs:attribute name=\"angle\" type=\"xs:double\" />\n        <xs:attribute name=\"type\" type=\"xs:string\" />\n        <xs:attribute name=\"spread\" type=\"xs:string\" />\n        <xs:attribute name=\"coordinatemode\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"Brush\">\n        <xs:choice>\n            <xs:element name=\"color\" type=\"Color\" />\n            <xs:element name=\"texture\" type=\"Property\" />\n            <xs:element name=\"gradient\" type=\"Gradient\" />\n        </xs:choice>\n        <xs:attribute name=\"brushstyle\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"ColorRole\">\n        <xs:sequence>\n            <xs:element name=\"brush\" type=\"Brush\" />\n        </xs:sequence>\n        <xs:attribute name=\"role\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"ColorGroup\">\n        <xs:sequence maxOccurs=\"unbounded\">\n            <xs:element name=\"colorrole\" type=\"ColorRole\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"color\" type=\"Color\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"Palette\">\n        <xs:all>\n            <xs:element name=\"active\" type=\"ColorGroup\" />\n            <xs:element name=\"inactive\" type=\"ColorGroup\" />\n            <xs:element name=\"disabled\" type=\"ColorGroup\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"Font\">\n        <xs:all>\n            <xs:element name=\"family\" type=\"xs:string\" minOccurs=\"0\" />\n            <xs:element name=\"pointsize\" type=\"xs:integer\" minOccurs=\"0\" />\n            <xs:element name=\"weight\" type=\"xs:integer\" minOccurs=\"0\" />\n            <xs:element name=\"italic\" type=\"xs:boolean\" minOccurs=\"0\" />\n            <xs:element name=\"bold\" type=\"xs:boolean\" minOccurs=\"0\" />\n            <xs:element name=\"underline\" type=\"xs:boolean\" minOccurs=\"0\" />\n            <xs:element name=\"strikeout\" type=\"xs:boolean\" minOccurs=\"0\" />\n            <xs:element name=\"antialiasing\" type=\"xs:boolean\" minOccurs=\"0\" />\n            <xs:element name=\"stylestrategy\" type=\"xs:string\" minOccurs=\"0\" />\n            <xs:element name=\"kerning\" type=\"xs:boolean\" minOccurs=\"0\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"Point\">\n        <xs:all>\n            <xs:element name=\"x\" type=\"xs:integer\" />\n            <xs:element name=\"y\" type=\"xs:integer\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"Rect\">\n        <xs:all>\n            <xs:element name=\"x\" type=\"xs:integer\" />\n            <xs:element name=\"y\" type=\"xs:integer\" />\n            <xs:element name=\"width\" type=\"xs:integer\" />\n            <xs:element name=\"height\" type=\"xs:integer\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"Locale\">\n        <xs:attribute name=\"language\" type=\"xs:string\" />\n        <xs:attribute name=\"country\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"SizePolicy\">\n        <xs:all>\n            <xs:element name=\"hsizetype\" type=\"xs:integer\" minOccurs=\"0\" />\n            <xs:element name=\"vsizetype\" type=\"xs:integer\" minOccurs=\"0\" />\n            <xs:element name=\"horstretch\" type=\"xs:integer\" />\n            <xs:element name=\"verstretch\" type=\"xs:integer\" />\n        </xs:all>\n        <xs:attribute name=\"hsizetype\" type=\"xs:string\" />\n        <xs:attribute name=\"vsizetype\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"Size\">\n        <xs:all>\n            <xs:element name=\"width\" type=\"xs:integer\" />\n            <xs:element name=\"height\" type=\"xs:integer\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"Date\">\n        <xs:all>\n            <xs:element name=\"year\" type=\"xs:integer\" />\n            <xs:element name=\"month\" type=\"xs:integer\" />\n            <xs:element name=\"day\" type=\"xs:integer\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"Time\">\n        <xs:all>\n            <xs:element name=\"hour\" type=\"xs:integer\" />\n            <xs:element name=\"minute\" type=\"xs:integer\" />\n            <xs:element name=\"second\" type=\"xs:integer\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"DateTime\">\n        <xs:all>\n            <xs:element name=\"hour\" type=\"xs:integer\" />\n            <xs:element name=\"minute\" type=\"xs:integer\" />\n            <xs:element name=\"second\" type=\"xs:integer\" />\n            <xs:element name=\"year\" type=\"xs:integer\" />\n            <xs:element name=\"month\" type=\"xs:integer\" />\n            <xs:element name=\"day\" type=\"xs:integer\" />\n        </xs:all>\n    </xs:complexType>\n\n    <!-- <xs:complexType name=\"StringList\">\n        <xs:attribute name=\"notr\" type=\"xs:string\" />\n        <xs:attribute name=\"comment\" type=\"xs:string\" />\n        <xs:attribute name=\"extracomment\" type=\"xs:string\" />\n        <xs:attribute name=\"id\" type=\"xs:string\" />\n        <xs:sequence>\n            <xs:element name=\"string\" type=\"xs:string\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType> -->\n\n    <xs:complexType name=\"ResourcePixmap\" mixed=\"true\">\n        <xs:attribute name=\"resource\" type=\"xs:string\" />\n        <xs:attribute name=\"alias\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"ResourceIcon\" mixed=\"true\">\n        <xs:all>\n            <xs:element name=\"normaloff\" type=\"ResourcePixmap\" minOccurs=\"0\" />\n            <xs:element name=\"normalon\" type=\"ResourcePixmap\" minOccurs=\"0\" />\n            <xs:element name=\"disabledoff\" type=\"ResourcePixmap\" minOccurs=\"0\" />\n            <xs:element name=\"disabledon\" type=\"ResourcePixmap\" minOccurs=\"0\" />\n            <xs:element name=\"activeoff\" type=\"ResourcePixmap\" minOccurs=\"0\" />\n            <xs:element name=\"activeon\" type=\"ResourcePixmap\" minOccurs=\"0\" />\n            <xs:element name=\"selectedoff\" type=\"ResourcePixmap\" minOccurs=\"0\" />\n            <xs:element name=\"selectedon\" type=\"ResourcePixmap\" minOccurs=\"0\" />\n        </xs:all>\n        <xs:attribute name=\"theme\" type=\"xs:string\" />\n        <xs:attribute name=\"resource\" type=\"xs:string\" /> <!-- pre 4.4 legacy support -->\n    </xs:complexType>\n\n    <xs:complexType name=\"String\" mixed=\"true\">\n        <xs:attribute name=\"notr\" type=\"xs:string\" />\n        <xs:attribute name=\"comment\" type=\"xs:string\" />\n        <xs:attribute name=\"extracomment\" type=\"xs:string\" />\n        <xs:attribute name=\"id\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"PointF\">\n        <xs:all>\n            <xs:element name=\"x\" type=\"xs:double\" />\n            <xs:element name=\"y\" type=\"xs:double\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"RectF\">\n        <xs:all>\n            <xs:element name=\"x\" type=\"xs:double\" />\n            <xs:element name=\"y\" type=\"xs:double\" />\n            <xs:element name=\"width\" type=\"xs:double\" />\n            <xs:element name=\"height\" type=\"xs:double\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"SizeF\">\n        <xs:all>\n            <xs:element name=\"width\" type=\"xs:double\" />\n            <xs:element name=\"height\" type=\"xs:double\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"Char\">\n        <xs:sequence>\n            <xs:element name=\"unicode\" type=\"xs:integer\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"Url\">\n        <xs:sequence>\n            <xs:element name=\"string\" type=\"String\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"Property\">\n        <xs:choice>\n            <xs:element name=\"bool\" type=\"xs:string\" />\n            <xs:element name=\"color\" type=\"Color\" />\n            <xs:element name=\"cstring\" type=\"xs:string\" />\n            <xs:element name=\"cursor\" type=\"xs:integer\" />\n            <xs:element name=\"cursorShape\" type=\"xs:string\" />\n            <xs:element name=\"enum\" type=\"xs:string\" />\n            <xs:element name=\"font\" type=\"Font\" />\n            <xs:element name=\"iconset\" type=\"ResourceIcon\" />\n            <xs:element name=\"pixmap\" type=\"ResourcePixmap\" />\n            <xs:element name=\"palette\" type=\"Palette\" />\n            <xs:element name=\"point\" type=\"Point\" />\n            <xs:element name=\"rect\" type=\"Rect\" />\n            <xs:element name=\"set\" type=\"xs:string\" />\n            <xs:element name=\"locale\" type=\"Locale\" />\n            <xs:element name=\"sizepolicy\" type=\"SizePolicy\" />\n            <xs:element name=\"size\" type=\"Size\" />\n            <xs:element name=\"string\" type=\"String\" />\n            <!-- <xs:element name=\"stringlist\" type=\"StringList\" /> -->\n            <xs:element name=\"number\" type=\"xs:integer\" />\n            <xs:element name=\"float\" type=\"xs:float\" />\n            <xs:element name=\"double\" type=\"xs:double\" />\n            <xs:element name=\"date\" type=\"Date\" />\n            <xs:element name=\"time\" type=\"Time\" />\n            <xs:element name=\"datetime\" type=\"DateTime\" />\n            <xs:element name=\"pointf\" type=\"PointF\" />\n            <xs:element name=\"rectf\" type=\"RectF\" />\n            <xs:element name=\"sizef\" type=\"SizeF\" />\n            <xs:element name=\"longlong\" type=\"xs:long\" />\n            <xs:element name=\"char\" type=\"Char\" />\n            <xs:element name=\"url\" type=\"Url\" />\n            <xs:element name=\"uint\" type=\"xs:unsignedInt\" />\n            <xs:element name=\"ulonglong\" type=\"xs:unsignedLong\" />\n            <xs:element name=\"brush\" type=\"Brush\" />\n        </xs:choice>\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n        <xs:attribute name=\"stdset\" type=\"xs:integer\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"Connections\">\n        <xs:sequence>\n            <xs:element name=\"connection\" type=\"Connection\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"Connection\">\n        <xs:all>\n            <xs:element name=\"sender\" type=\"xs:string\" />\n            <xs:element name=\"signal\" type=\"xs:string\" />\n            <xs:element name=\"receiver\" type=\"xs:string\" />\n            <xs:element name=\"slot\" type=\"xs:string\" />\n            <xs:element name=\"hints\" type=\"ConnectionHints\" minOccurs=\"0\" />\n        </xs:all>\n    </xs:complexType>\n\n    <xs:complexType name=\"ConnectionHints\">\n        <xs:sequence>\n            <xs:element name=\"hint\" type=\"ConnectionHint\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"ConnectionHint\">\n        <xs:all>\n            <xs:element name=\"x\" type=\"xs:integer\" />\n            <xs:element name=\"y\" type=\"xs:integer\" />\n        </xs:all>\n        <xs:attribute name=\"type\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"DesignerData\">\n        <xs:sequence>\n            <xs:element name=\"property\" type=\"Property\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"Slots\">\n        <xs:sequence maxOccurs=\"unbounded\">\n            <xs:element name=\"signal\" type=\"xs:string\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"slot\" type=\"xs:string\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"PropertySpecifications\">\n        <xs:sequence maxOccurs=\"unbounded\">\n            <xs:element name=\"tooltip\" type=\"PropertyToolTip\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n            <xs:element name=\"stringpropertyspecification\" type=\"StringPropertySpecification\" minOccurs=\"0\" maxOccurs=\"unbounded\" />\n        </xs:sequence>\n    </xs:complexType>\n\n    <xs:complexType name=\"PropertyToolTip\">\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n    </xs:complexType>\n\n    <xs:complexType name=\"StringPropertySpecification\">\n        <xs:attribute name=\"name\" type=\"xs:string\" />\n        <xs:attribute name=\"type\" type=\"xs:string\" />\n        <xs:attribute name=\"notr\" type=\"xs:string\" />\n    </xs:complexType>\n\n</xs:schema>"
  },
  {
    "path": "frontend/forms/obs.qrc",
    "content": "<RCC>\n  <qresource prefix=\"/res\">\n    <file>images/layout-horizontal.svg</file>\n    <file>images/layout-vertical.svg</file>\n    <file>images/close.svg</file>\n    <file>images/headphones.svg</file>\n    <file>images/save.svg</file>\n    <file>images/media-pause.svg</file>\n    <file>images/headphones-off.svg</file>\n    <file>images/trash.svg</file>\n    <file>images/filter.svg</file>\n    <file>images/mute.svg</file>\n    <file>images/unassigned.svg</file>\n    <file>images/refresh.svg</file>\n    <file>images/no_sources.svg</file>\n    <file>images/minus.svg</file>\n    <file>images/plus.svg</file>\n    <file>images/down.svg</file>\n    <file>images/up.svg</file>\n    <file>images/obs.png</file>\n    <file>images/obs_macos.svg</file>\n    <file>images/obs_paused.png</file>\n    <file>images/obs_paused_macos.svg</file>\n    <file>images/tray_active.png</file>\n    <file>images/tray_active_macos.svg</file>\n    <file>images/paused.png</file>\n    <file>images/paused_mac.png</file>\n    <file>images/active.png</file>\n    <file>images/active_mac.png</file>\n    <file>images/right.svg</file>\n    <file>images/unlocked.svg</file>\n    <file>images/locked.svg</file>\n    <file>images/invisible.svg</file>\n    <file>images/visible.svg</file>\n    <file>images/help.svg</file>\n    <file>images/help_light.svg</file>\n    <file>images/hide.svg</file>\n    <file>images/revert.svg</file>\n    <file>images/alert.svg</file>\n    <file>images/warning.svg</file>\n    <file>images/settings/general.svg</file>\n    <file>images/sources/brush.svg</file>\n    <file>images/sources/camera.svg</file>\n    <file>images/sources/gamepad.svg</file>\n    <file>images/sources/globe.svg</file>\n    <file>images/sources/group.svg</file>\n    <file>images/sources/image.svg</file>\n    <file>images/sources/media.svg</file>\n    <file>images/sources/microphone.svg</file>\n    <file>images/sources/scene.svg</file>\n    <file>images/sources/slideshow.svg</file>\n    <file>images/sources/text.svg</file>\n    <file>images/sources/window.svg</file>\n    <file>images/sources/default.svg</file>\n    <file>images/sources/windowaudio.svg</file>\n    <file>images/recording-active.svg</file>\n    <file>images/recording-inactive.svg</file>\n    <file>images/recording-pause.svg</file>\n    <file>images/recording-pause-inactive.svg</file>\n    <file>images/streaming-active.svg</file>\n    <file>images/streaming-inactive.svg</file>\n    <file>images/media/media_play.svg</file>\n    <file>images/media/media_pause.svg</file>\n    <file>images/media/media_next.svg</file>\n    <file>images/media/media_previous.svg</file>\n    <file>images/media/media_restart.svg</file>\n    <file>images/media/media_stop.svg</file>\n    <file>images/interact.svg</file>\n    <file>images/dots.svg</file>\n    <file>images/dots-vert.svg</file>\n    <file>images/cogs.svg</file>\n    <file>images/expand.svg</file>\n    <file>images/collapse.svg</file>\n    <file>images/entry-clear.svg</file>\n    <file>images/network-excellent.svg</file>\n    <file>images/network-good.svg</file>\n    <file>images/network-mediocre.svg</file>\n    <file>images/network-bad.svg</file>\n    <file>images/network-disconnected.svg</file>\n    <file>images/network-inactive.svg</file>\n  </qresource>\n  <qresource prefix=\"/settings\">\n    <file>images/settings/output.svg</file>\n    <file>images/settings/stream.svg</file>\n    <file>images/settings/advanced.svg</file>\n    <file>images/settings/video.svg</file>\n    <file>images/settings/audio.svg</file>\n    <file>images/settings/general.svg</file>\n    <file>images/settings/hotkeys.svg</file>\n    <file>images/settings/accessibility.svg</file>\n    <file>images/settings/appearance.svg</file>\n  </qresource>\n  <qresource prefix=\"\">\n    <file>fonts/OpenSans-Regular.ttf</file>\n    <file>fonts/OpenSans-Bold.ttf</file>\n    <file>fonts/OpenSans-Italic.ttf</file>\n  </qresource>\n</RCC>\n"
  },
  {
    "path": "frontend/forms/source-toolbar/browser-source-toolbar.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>BrowserSourceToolbar</class>\n <widget class=\"QWidget\" name=\"BrowserSourceToolbar\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>628</width>\n    <height>22</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>22</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QPushButton\" name=\"refresh\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string>RefreshBrowser</string>\n     </property>\n     <property name=\"icon\">\n      <iconset resource=\"../obs.qrc\">\n       <normaloff>:/res/images/refresh.svg</normaloff>:/res/images/refresh.svg</iconset>\n     </property>\n     <property name=\"flat\">\n      <bool>true</bool>\n     </property>\n     <property name=\"class\" stdset=\"0\">\n      <string notr=\"true\">icon-refresh</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"Line\" name=\"line\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"horizontalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>40</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources>\n  <include location=\"../obs.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/source-toolbar/color-source-toolbar.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ColorSourceToolbar</class>\n <widget class=\"QWidget\" name=\"ColorSourceToolbar\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>565</width>\n    <height>22</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>22</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QLabel\" name=\"color\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>80</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string notr=\"true\">color here</string>\n     </property>\n     <property name=\"alignment\">\n      <set>Qt::AlignCenter</set>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPushButton\" name=\"choose\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string>Basic.PropertiesWindow.SelectColor</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"horizontalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>40</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/source-toolbar/device-select-toolbar.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DeviceSelectToolbar</class>\n <widget class=\"QWidget\" name=\"DeviceSelectToolbar\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>665</width>\n    <height>22</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>22</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\" stretch=\"0,1,0,0\">\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetNoConstraint</enum>\n   </property>\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QLabel\" name=\"deviceLabel\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string notr=\"true\">Device</string>\n     </property>\n     <property name=\"buddy\">\n      <cstring>device</cstring>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QComboBox\" name=\"device\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"maximumSize\">\n      <size>\n       <width>600</width>\n       <height>16777215</height>\n      </size>\n     </property>\n     <property name=\"currentText\">\n      <string notr=\"true\"/>\n     </property>\n     <property name=\"sizeAdjustPolicy\">\n      <enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPushButton\" name=\"activateButton\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string notr=\"true\">Activate</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"horizontalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"sizeType\">\n      <enum>QSizePolicy::Expanding</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>0</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/source-toolbar/game-capture-toolbar.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GameCaptureToolbar</class>\n <widget class=\"QWidget\" name=\"GameCaptureToolbar\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>650</width>\n    <height>24</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>22</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\" stretch=\"0,4,0,5,0\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QLabel\" name=\"modeLabel\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string notr=\"true\">Mode</string>\n     </property>\n     <property name=\"buddy\">\n      <cstring>mode</cstring>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QComboBox\" name=\"mode\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>120</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"maximumSize\">\n      <size>\n       <width>400</width>\n       <height>16777215</height>\n      </size>\n     </property>\n     <property name=\"currentText\">\n      <string notr=\"true\"/>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"windowLabel\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string notr=\"true\">Window</string>\n     </property>\n     <property name=\"buddy\">\n      <cstring>window</cstring>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QComboBox\" name=\"window\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>120</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"maximumSize\">\n      <size>\n       <width>600</width>\n       <height>16777215</height>\n      </size>\n     </property>\n     <property name=\"currentText\">\n      <string notr=\"true\"/>\n     </property>\n     <property name=\"sizeAdjustPolicy\">\n      <enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"empty\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>0</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/source-toolbar/image-source-toolbar.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ImageSourceToolbar</class>\n <widget class=\"QWidget\" name=\"ImageSourceToolbar\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>580</width>\n    <height>22</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>22</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\" stretch=\"0,1,0,0\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QLabel\" name=\"pathLabel\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string notr=\"true\">Image File</string>\n     </property>\n     <property name=\"buddy\">\n      <cstring>path</cstring>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLineEdit\" name=\"path\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>100</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"maximumSize\">\n      <size>\n       <width>400</width>\n       <height>16777215</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string notr=\"true\"/>\n     </property>\n     <property name=\"readOnly\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPushButton\" name=\"browse\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string>Browse</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <spacer name=\"horizontalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>0</width>\n       <height>20</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/source-toolbar/media-controls.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MediaControls</class>\n <widget class=\"QWidget\" name=\"MediaControls\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>888</width>\n    <height>22</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>22</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <property name=\"spacing\">\n    <number>6</number>\n   </property>\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QPushButton\" name=\"playPauseButton\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>22</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"toolTip\">\n      <string>ContextBar.MediaControls.RestartMedia</string>\n     </property>\n     <property name=\"text\">\n      <string/>\n     </property>\n     <property name=\"icon\">\n      <iconset resource=\"../obs.qrc\">\n       <normaloff>:/res/images/media/media_restart.svg</normaloff>:/res/images/media/media_restart.svg</iconset>\n     </property>\n     <property name=\"iconSize\">\n      <size>\n       <width>20</width>\n       <height>20</height>\n      </size>\n     </property>\n     <property name=\"flat\">\n      <bool>true</bool>\n     </property>\n     <property name=\"class\" stdset=\"0\">\n      <string>icon-media-play</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPushButton\" name=\"previousButton\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>22</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"toolTip\">\n      <string>ContextBar.MediaControls.PlaylistPrevious</string>\n     </property>\n     <property name=\"text\">\n      <string/>\n     </property>\n     <property name=\"icon\">\n      <iconset resource=\"../obs.qrc\">\n       <normaloff>:/res/images/media/media_previous.svg</normaloff>:/res/images/media/media_previous.svg</iconset>\n     </property>\n     <property name=\"iconSize\">\n      <size>\n       <width>20</width>\n       <height>20</height>\n      </size>\n     </property>\n     <property name=\"shortcut\">\n      <string>P</string>\n     </property>\n     <property name=\"flat\">\n      <bool>true</bool>\n     </property>\n     <property name=\"class\" stdset=\"0\">\n      <string>icon-media-prev</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPushButton\" name=\"stopButton\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>22</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"toolTip\">\n      <string>ContextBar.MediaControls.StopMedia</string>\n     </property>\n     <property name=\"text\">\n      <string/>\n     </property>\n     <property name=\"icon\">\n      <iconset resource=\"../obs.qrc\">\n       <normaloff>:/res/images/media/media_stop.svg</normaloff>:/res/images/media/media_stop.svg</iconset>\n     </property>\n     <property name=\"iconSize\">\n      <size>\n       <width>20</width>\n       <height>20</height>\n      </size>\n     </property>\n     <property name=\"shortcut\">\n      <string>S</string>\n     </property>\n     <property name=\"flat\">\n      <bool>true</bool>\n     </property>\n     <property name=\"class\" stdset=\"0\">\n      <string>icon-media-stop</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPushButton\" name=\"nextButton\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>22</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"toolTip\">\n      <string>ContextBar.MediaControls.PlaylistNext</string>\n     </property>\n     <property name=\"text\">\n      <string/>\n     </property>\n     <property name=\"icon\">\n      <iconset resource=\"../obs.qrc\">\n       <normaloff>:/res/images/media/media_next.svg</normaloff>:/res/images/media/media_next.svg</iconset>\n     </property>\n     <property name=\"iconSize\">\n      <size>\n       <width>20</width>\n       <height>20</height>\n      </size>\n     </property>\n     <property name=\"shortcut\">\n      <string>N</string>\n     </property>\n     <property name=\"flat\">\n      <bool>true</bool>\n     </property>\n     <property name=\"class\" stdset=\"0\">\n      <string>icon-media-next</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"AbsoluteSlider\" name=\"slider\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"accessibleName\">\n      <string>ContextBar.MediaControls.BlindSeek</string>\n     </property>\n     <property name=\"maximum\">\n      <number>1024</number>\n     </property>\n     <property name=\"tracking\">\n      <bool>false</bool>\n     </property>\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"timerLabel\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string>--:--:--</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string>/</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"ClickableLabel\" name=\"durationLabel\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string>--:--:--</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"emptySpaceAgain\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ClickableLabel</class>\n   <extends>QLabel</extends>\n   <header>components/ClickableLabel.hpp</header>\n  </customwidget>\n  <customwidget>\n   <class>AbsoluteSlider</class>\n   <extends>QSlider</extends>\n   <header>components/AbsoluteSlider.hpp</header>\n  </customwidget>\n </customwidgets>\n <resources>\n  <include location=\"../obs.qrc\"/>\n </resources>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/forms/source-toolbar/text-source-toolbar.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>TextSourceToolbar</class>\n <widget class=\"QWidget\" name=\"TextSourceToolbar\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>726</width>\n    <height>22</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>0</width>\n    <height>22</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <property name=\"leftMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"topMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"rightMargin\">\n    <number>0</number>\n   </property>\n   <property name=\"bottomMargin\">\n    <number>0</number>\n   </property>\n   <item>\n    <widget class=\"QPushButton\" name=\"selectFont\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string>Basic.PropertiesWindow.SelectFont</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QPushButton\" name=\"selectColor\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n     <property name=\"text\">\n      <string>Basic.PropertiesWindow.SelectColor</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QFrame\" name=\"emptySpace\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Preferred\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLineEdit\" name=\"text\">\n     <property name=\"minimumSize\">\n      <size>\n       <width>0</width>\n       <height>22</height>\n      </size>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/importer/ImporterEntryPathItemDelegate.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"ImporterEntryPathItemDelegate.hpp\"\n#include \"ImporterModel.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QHBoxLayout>\n#include <QLineEdit>\n#include <QToolButton>\n\n#include \"moc_ImporterEntryPathItemDelegate.cpp\"\n\nImporterEntryPathItemDelegate::ImporterEntryPathItemDelegate() : QStyledItemDelegate() {}\n\nQWidget *ImporterEntryPathItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */,\n\t\t\t\t\t\t     const QModelIndex &index) const\n{\n\tbool empty = index.model()\n\t\t\t     ->index(index.row(), ImporterColumn::Path)\n\t\t\t     .data(ImporterEntryRole::CheckEmpty)\n\t\t\t     .value<bool>();\n\n\tQSizePolicy buttonSizePolicy(QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Expanding,\n\t\t\t\t     QSizePolicy::ControlType::PushButton);\n\n\tQWidget *container = new QWidget(parent);\n\n\tauto browseCallback = [this, container]() {\n\t\tconst_cast<ImporterEntryPathItemDelegate *>(this)->handleBrowse(container);\n\t};\n\n\tauto clearCallback = [this, container]() {\n\t\tconst_cast<ImporterEntryPathItemDelegate *>(this)->handleClear(container);\n\t};\n\n\tQHBoxLayout *layout = new QHBoxLayout();\n\tlayout->setContentsMargins(0, 0, 0, 0);\n\tlayout->setSpacing(0);\n\n\tQLineEdit *text = new QLineEdit();\n\ttext->setObjectName(QStringLiteral(\"text\"));\n\ttext->setSizePolicy(QSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding,\n\t\t\t\t\tQSizePolicy::ControlType::LineEdit));\n\tlayout->addWidget(text);\n\n\tQObject::connect(text, &QLineEdit::editingFinished, this, &ImporterEntryPathItemDelegate::updateText);\n\n\tQToolButton *browseButton = new QToolButton();\n\tbrowseButton->setText(\"...\");\n\tbrowseButton->setSizePolicy(buttonSizePolicy);\n\tlayout->addWidget(browseButton);\n\n\tconnect(browseButton, &QToolButton::clicked, this, browseCallback);\n\n\t// The \"clear\" button is not shown in output cells\n\t// or the insertion point's input cell.\n\tif (!empty) {\n\t\tQToolButton *clearButton = new QToolButton();\n\t\tclearButton->setText(\"X\");\n\t\tclearButton->setSizePolicy(buttonSizePolicy);\n\t\tlayout->addWidget(clearButton);\n\n\t\tconnect(clearButton, &QToolButton::clicked, this, clearCallback);\n\t}\n\n\tcontainer->setLayout(layout);\n\tcontainer->setFocusProxy(text);\n\treturn container;\n}\n\nvoid ImporterEntryPathItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const\n{\n\tQLineEdit *text = editor->findChild<QLineEdit *>();\n\ttext->setText(index.data().toString());\n\teditor->setProperty(PATH_LIST_PROP, QVariant());\n}\n\nvoid ImporterEntryPathItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,\n\t\t\t\t\t\t const QModelIndex &index) const\n{\n\t// We use the PATH_LIST_PROP property to pass a list of\n\t// path strings from the editor widget into the model's\n\t// NewPathsToProcessRole. This is only used when paths\n\t// are selected through the \"browse\" or \"delete\" buttons\n\t// in the editor. If the user enters new text in the\n\t// text box, we simply pass that text on to the model\n\t// as normal text data in the default role.\n\tQVariant pathListProp = editor->property(PATH_LIST_PROP);\n\tif (pathListProp.isValid()) {\n\t\tQStringList list = editor->property(PATH_LIST_PROP).toStringList();\n\t\tmodel->setData(index, list, ImporterEntryRole::NewPath);\n\t} else {\n\t\tQLineEdit *lineEdit = editor->findChild<QLineEdit *>();\n\t\tmodel->setData(index, lineEdit->text());\n\t}\n}\n\nvoid ImporterEntryPathItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,\n\t\t\t\t\t  const QModelIndex &index) const\n{\n\tQStyleOptionViewItem localOption = option;\n\tinitStyleOption(&localOption, index);\n\n\tQApplication::style()->drawControl(QStyle::CE_ItemViewItem, &localOption, painter);\n}\n\nvoid ImporterEntryPathItemDelegate::handleBrowse(QWidget *container)\n{\n\tQString Pattern = \"(*.json *.bpres *.xml *.xconfig)\";\n\n\tQLineEdit *text = container->findChild<QLineEdit *>();\n\n\tQString currentPath = text->text();\n\n\tbool isSet = false;\n\tQStringList paths = OpenFiles(container, QTStr(\"Importer.SelectCollection\"), currentPath,\n\t\t\t\t      QTStr(\"Importer.Collection\") + QString(\" \") + Pattern);\n\n\tif (!paths.empty()) {\n\t\tcontainer->setProperty(PATH_LIST_PROP, paths);\n\t\tisSet = true;\n\t}\n\n\tif (isSet)\n\t\temit commitData(container);\n}\n\nvoid ImporterEntryPathItemDelegate::handleClear(QWidget *container)\n{\n\t// An empty string list will indicate that the entry is being\n\t// blanked and should be deleted.\n\tcontainer->setProperty(PATH_LIST_PROP, QStringList());\n\n\temit commitData(container);\n}\n\nvoid ImporterEntryPathItemDelegate::updateText()\n{\n\tQLineEdit *lineEdit = dynamic_cast<QLineEdit *>(sender());\n\tQWidget *editor = lineEdit->parentWidget();\n\temit commitData(editor);\n}\n"
  },
  {
    "path": "frontend/importer/ImporterEntryPathItemDelegate.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QStyledItemDelegate>\n\nclass ImporterEntryPathItemDelegate : public QStyledItemDelegate {\n\tQ_OBJECT\n\npublic:\n\tImporterEntryPathItemDelegate();\n\n\tvirtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */,\n\t\t\t\t      const QModelIndex &index) const override;\n\n\tvirtual void setEditorData(QWidget *editor, const QModelIndex &index) const override;\n\tvirtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;\n\tvirtual void paint(QPainter *painter, const QStyleOptionViewItem &option,\n\t\t\t   const QModelIndex &index) const override;\n\nprivate:\n\tconst char *PATH_LIST_PROP = \"pathList\";\n\n\tvoid handleBrowse(QWidget *container);\n\tvoid handleClear(QWidget *container);\n\nprivate slots:\n\tvoid updateText();\n};\n"
  },
  {
    "path": "frontend/importer/ImporterModel.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"ImporterModel.hpp\"\n\n#include <OBSApp.hpp>\n#include <importers/importers.hpp>\n\n#include \"moc_ImporterModel.cpp\"\n\nint ImporterModel::rowCount(const QModelIndex &) const\n{\n\treturn options.length() + 1;\n}\n\nint ImporterModel::columnCount(const QModelIndex &) const\n{\n\treturn ImporterColumn::Count;\n}\n\nQVariant ImporterModel::data(const QModelIndex &index, int role) const\n{\n\tQVariant result = QVariant();\n\n\tif (index.row() >= options.length()) {\n\t\tif (role == ImporterEntryRole::CheckEmpty)\n\t\t\tresult = true;\n\t\telse\n\t\t\treturn QVariant();\n\t} else if (role == Qt::DisplayRole) {\n\t\tswitch (index.column()) {\n\t\tcase ImporterColumn::Path:\n\t\t\tresult = options[index.row()].path;\n\t\t\tbreak;\n\t\tcase ImporterColumn::Program:\n\t\t\tresult = options[index.row()].program;\n\t\t\tbreak;\n\t\tcase ImporterColumn::Name:\n\t\t\tresult = options[index.row()].name;\n\t\t}\n\t} else if (role == Qt::EditRole) {\n\t\tif (index.column() == ImporterColumn::Name) {\n\t\t\tresult = options[index.row()].name;\n\t\t}\n\t} else if (role == Qt::CheckStateRole) {\n\t\tswitch (index.column()) {\n\t\tcase ImporterColumn::Selected:\n\t\t\tif (options[index.row()].program != \"\")\n\t\t\t\tresult = options[index.row()].selected ? Qt::Checked : Qt::Unchecked;\n\t\t\telse\n\t\t\t\tresult = Qt::Unchecked;\n\t\t}\n\t} else if (role == ImporterEntryRole::CheckEmpty) {\n\t\tresult = options[index.row()].empty;\n\t}\n\n\treturn result;\n}\n\nQt::ItemFlags ImporterModel::flags(const QModelIndex &index) const\n{\n\tQt::ItemFlags flags = QAbstractTableModel::flags(index);\n\n\tif (index.column() == ImporterColumn::Selected && index.row() != options.length()) {\n\t\tflags |= Qt::ItemIsUserCheckable;\n\t} else if (index.column() == ImporterColumn::Path ||\n\t\t   (index.column() == ImporterColumn::Name && index.row() != options.length())) {\n\t\tflags |= Qt::ItemIsEditable;\n\t}\n\n\treturn flags;\n}\n\nvoid ImporterModel::checkInputPath(int row)\n{\n\tImporterEntry &entry = options[row];\n\n\tif (entry.path.isEmpty()) {\n\t\tentry.program = \"\";\n\t\tentry.empty = true;\n\t\tentry.selected = false;\n\t\tentry.name = \"\";\n\t} else {\n\t\tentry.empty = false;\n\n\t\tstd::string program = DetectProgram(entry.path.toStdString());\n\t\tentry.program = QTStr(program.c_str());\n\n\t\tif (program.empty()) {\n\t\t\tentry.selected = false;\n\t\t} else {\n\t\t\tstd::string name = GetSCName(entry.path.toStdString(), program);\n\t\t\tentry.name = name.c_str();\n\t\t}\n\t}\n\n\temit dataChanged(index(row, 0), index(row, ImporterColumn::Count));\n}\n\nbool ImporterModel::setData(const QModelIndex &index, const QVariant &value, int role)\n{\n\tif (role == ImporterEntryRole::NewPath) {\n\t\tQStringList list = value.toStringList();\n\n\t\tif (list.size() == 0) {\n\t\t\tif (index.row() < options.size()) {\n\t\t\t\tbeginRemoveRows(QModelIndex(), index.row(), index.row());\n\t\t\t\toptions.removeAt(index.row());\n\t\t\t\tendRemoveRows();\n\t\t\t}\n\t\t} else {\n\t\t\tif (list.size() > 0 && index.row() < options.length()) {\n\t\t\t\toptions[index.row()].path = list[0];\n\t\t\t\tcheckInputPath(index.row());\n\n\t\t\t\tlist.removeAt(0);\n\t\t\t}\n\n\t\t\tif (list.size() > 0) {\n\t\t\t\tint row = index.row();\n\t\t\t\tint lastRow = row + list.size() - 1;\n\t\t\t\tbeginInsertRows(QModelIndex(), row, lastRow);\n\n\t\t\t\tfor (QString path : list) {\n\t\t\t\t\tImporterEntry entry;\n\t\t\t\t\tentry.path = path;\n\t\t\t\t\tentry.selected = true;\n\t\t\t\t\tentry.empty = true;\n\n\t\t\t\t\toptions.insert(row, entry);\n\n\t\t\t\t\trow++;\n\t\t\t\t}\n\n\t\t\t\tendInsertRows();\n\n\t\t\t\tfor (row = index.row(); row <= lastRow; row++) {\n\t\t\t\t\tcheckInputPath(row);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else if (index.row() == options.length()) {\n\t\tQString path = value.toString();\n\n\t\tif (!path.isEmpty()) {\n\t\t\tImporterEntry entry;\n\t\t\tentry.path = path;\n\t\t\tentry.selected = role != ImporterEntryRole::AutoPath;\n\t\t\tentry.empty = false;\n\n\t\t\tbeginInsertRows(QModelIndex(), options.length() + 1, options.length() + 1);\n\t\t\toptions.append(entry);\n\t\t\tendInsertRows();\n\n\t\t\tcheckInputPath(index.row());\n\t\t}\n\t} else if (index.column() == ImporterColumn::Selected) {\n\t\tbool select = value.toBool();\n\n\t\toptions[index.row()].selected = select;\n\t} else if (index.column() == ImporterColumn::Path) {\n\t\tQString path = value.toString();\n\t\toptions[index.row()].path = path;\n\n\t\tcheckInputPath(index.row());\n\t} else if (index.column() == ImporterColumn::Name) {\n\t\tQString name = value.toString();\n\t\toptions[index.row()].name = name;\n\t}\n\n\temit dataChanged(index, index);\n\n\treturn true;\n}\n\nQVariant ImporterModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n\tQVariant result = QVariant();\n\n\tif (role == Qt::DisplayRole && orientation == Qt::Orientation::Horizontal) {\n\t\tswitch (section) {\n\t\tcase ImporterColumn::Path:\n\t\t\tresult = QTStr(\"Importer.Path\");\n\t\t\tbreak;\n\t\tcase ImporterColumn::Program:\n\t\t\tresult = QTStr(\"Importer.Program\");\n\t\t\tbreak;\n\t\tcase ImporterColumn::Name:\n\t\t\tresult = QTStr(\"Name\");\n\t\t}\n\t}\n\n\treturn result;\n}\n"
  },
  {
    "path": "frontend/importer/ImporterModel.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QAbstractTableModel>\n\nenum ImporterColumn {\n\tSelected,\n\tName,\n\tPath,\n\tProgram,\n\n\tCount\n};\n\nenum ImporterEntryRole { EntryStateRole = Qt::UserRole, NewPath, AutoPath, CheckEmpty };\n\nclass ImporterModel : public QAbstractTableModel {\n\tQ_OBJECT\n\n\tfriend class OBSImporter;\n\npublic:\n\tImporterModel(QObject *parent = 0) : QAbstractTableModel(parent) {}\n\n\tint rowCount(const QModelIndex &parent = QModelIndex()) const;\n\tint columnCount(const QModelIndex &parent = QModelIndex()) const;\n\tQVariant data(const QModelIndex &index, int role) const;\n\tQVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n\tQt::ItemFlags flags(const QModelIndex &index) const;\n\tbool setData(const QModelIndex &index, const QVariant &value, int role);\n\nprivate:\n\tstruct ImporterEntry {\n\t\tQString path;\n\t\tQString program;\n\t\tQString name;\n\n\t\tbool selected;\n\t\tbool empty;\n\t};\n\n\tQList<ImporterEntry> options;\n\n\tvoid checkInputPath(int row);\n};\n"
  },
  {
    "path": "frontend/importer/OBSImporter.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSImporter.hpp\"\n#include \"ImporterEntryPathItemDelegate.hpp\"\n#include \"ImporterModel.hpp\"\n\n#include <importers/importers.hpp>\n#include <models/SceneCollection.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QDirIterator>\n#include <QDropEvent>\n#include <QMessageBox>\n#include <QMimeData>\n#include <QPushButton>\n\n#include \"moc_OBSImporter.cpp\"\nOBSImporter::OBSImporter(QWidget *parent) : QDialog(parent), optionsModel(new ImporterModel), ui(new Ui::OBSImporter)\n{\n\tsetAcceptDrops(true);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\n\tui->tableView->setModel(optionsModel);\n\tui->tableView->setItemDelegateForColumn(ImporterColumn::Path, new ImporterEntryPathItemDelegate());\n\tui->tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);\n\tui->tableView->horizontalHeader()->setSectionResizeMode(ImporterColumn::Name, QHeaderView::ResizeMode::Stretch);\n\tui->tableView->horizontalHeader()->setSectionResizeMode(ImporterColumn::Path, QHeaderView::ResizeMode::Stretch);\n\n\tconnect(optionsModel, &ImporterModel::dataChanged, this, &OBSImporter::dataChanged);\n\n\tui->tableView->setEditTriggers(QAbstractItemView::EditTrigger::CurrentChanged);\n\n\tui->buttonBox->button(QDialogButtonBox::Ok)->setText(QTStr(\"Import\"));\n\n\tconnect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this,\n\t\t&OBSImporter::importCollections);\n\tconnect(ui->importerSelectFiles, &QPushButton::clicked, this, &OBSImporter::browseImport);\n\tconnect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &OBSImporter::close);\n\n\tImportersInit();\n\n\tbool autoSearchPrompt = config_get_bool(App()->GetUserConfig(), \"General\", \"AutoSearchPrompt\");\n\n\tif (!autoSearchPrompt) {\n\t\tQMessageBox::StandardButton button = OBSMessageBox::question(\n\t\t\tparent, QTStr(\"Importer.AutomaticCollectionPrompt\"), QTStr(\"Importer.AutomaticCollectionText\"));\n\n\t\tif (button == QMessageBox::Yes) {\n\t\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"AutomaticCollectionSearch\", true);\n\t\t} else {\n\t\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"AutomaticCollectionSearch\", false);\n\t\t}\n\n\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"AutoSearchPrompt\", true);\n\t}\n\n\tbool autoSearch = config_get_bool(App()->GetUserConfig(), \"General\", \"AutomaticCollectionSearch\");\n\n\tOBSImporterFiles f;\n\tif (autoSearch)\n\t\tf = ImportersFindFiles();\n\n\tfor (size_t i = 0; i < f.size(); i++) {\n\t\tQString path = f[i].c_str();\n\t\tpath.replace(\"\\\\\", \"/\");\n\t\taddImportOption(path, true);\n\t}\n\n\tf.clear();\n\n\tui->tableView->resizeColumnsToContents();\n\n\tQModelIndex index = optionsModel->createIndex(optionsModel->rowCount() - 1, 2);\n\tQMetaObject::invokeMethod(ui->tableView, \"setCurrentIndex\", Qt::QueuedConnection,\n\t\t\t\t  Q_ARG(const QModelIndex &, index));\n}\n\nvoid OBSImporter::addImportOption(QString path, bool automatic)\n{\n\tQStringList list;\n\n\tlist.append(path);\n\n\tQModelIndex insertIndex = optionsModel->index(optionsModel->rowCount() - 1, ImporterColumn::Path);\n\n\toptionsModel->setData(insertIndex, list, automatic ? ImporterEntryRole::AutoPath : ImporterEntryRole::NewPath);\n}\n\nvoid OBSImporter::dropEvent(QDropEvent *ev)\n{\n\tfor (QUrl url : ev->mimeData()->urls()) {\n\t\tQFileInfo fileInfo(url.toLocalFile());\n\t\tif (fileInfo.isDir()) {\n\n\t\t\tQDirIterator dirIter(fileInfo.absoluteFilePath(), QDir::Files);\n\n\t\t\twhile (dirIter.hasNext()) {\n\t\t\t\taddImportOption(dirIter.next(), false);\n\t\t\t}\n\t\t} else {\n\t\t\taddImportOption(fileInfo.canonicalFilePath(), false);\n\t\t}\n\t}\n}\n\nvoid OBSImporter::dragEnterEvent(QDragEnterEvent *ev)\n{\n\tif (ev->mimeData()->hasUrls())\n\t\tev->accept();\n}\n\nvoid OBSImporter::browseImport()\n{\n\tQString Pattern = \"(*.json *.bpres *.xml *.xconfig)\";\n\n\tQStringList paths = OpenFiles(this, QTStr(\"Importer.SelectCollection\"), \"\",\n\t\t\t\t      QTStr(\"Importer.Collection\") + QString(\" \") + Pattern);\n\n\tif (!paths.empty()) {\n\t\tfor (int i = 0; i < paths.count(); i++) {\n\t\t\taddImportOption(paths[i], false);\n\t\t}\n\t}\n}\n\nbool GetUnusedName(std::string &name)\n{\n\tOBSBasic *basic = OBSBasic::Get();\n\n\tif (!basic->GetSceneCollectionByName(name)) {\n\t\treturn false;\n\t}\n\n\tstd::string newName;\n\tint inc = 2;\n\tdo {\n\t\tnewName = name;\n\t\tnewName += \" \";\n\t\tnewName += std::to_string(inc++);\n\t} while (basic->GetSceneCollectionByName(newName));\n\n\tname = newName;\n\treturn true;\n}\n\nconstexpr std::string_view OBSSceneCollectionPath = \"obs-studio/basic/scenes/\";\n\nvoid OBSImporter::importCollections()\n{\n\tsetEnabled(false);\n\n\tconst std::filesystem::path sceneCollectionLocation =\n\t\tApp()->userScenesLocation / std::filesystem::u8path(OBSSceneCollectionPath);\n\n\tfor (int i = 0; i < optionsModel->rowCount() - 1; i++) {\n\t\tint selected = optionsModel->index(i, ImporterColumn::Selected).data(Qt::CheckStateRole).value<int>();\n\n\t\tif (selected == Qt::Unchecked)\n\t\t\tcontinue;\n\n\t\tstd::string pathStr = optionsModel->index(i, ImporterColumn::Path)\n\t\t\t\t\t      .data(Qt::DisplayRole)\n\t\t\t\t\t      .value<QString>()\n\t\t\t\t\t      .toStdString();\n\t\tstd::string nameStr = optionsModel->index(i, ImporterColumn::Name)\n\t\t\t\t\t      .data(Qt::DisplayRole)\n\t\t\t\t\t      .value<QString>()\n\t\t\t\t\t      .toStdString();\n\n\t\tjson11::Json res;\n\t\tImportSC(pathStr, nameStr, res);\n\n\t\tif (res != json11::Json()) {\n\t\t\tjson11::Json::object out = res.object_items();\n\t\t\tstd::string name = res[\"name\"].string_value();\n\t\t\tstd::string file;\n\n\t\t\tif (GetUnusedName(name)) {\n\t\t\t\tjson11::Json::object newOut = out;\n\t\t\t\tnewOut[\"name\"] = name;\n\t\t\t\tout = newOut;\n\t\t\t}\n\n\t\t\tstd::string fileName;\n\t\t\tif (!GetFileSafeName(name.c_str(), fileName)) {\n\t\t\t\tblog(LOG_WARNING, \"Failed to create safe file name for '%s'\", fileName.c_str());\n\t\t\t}\n\n\t\t\tstd::string collectionFile;\n\t\t\tcollectionFile.reserve(sceneCollectionLocation.u8string().size() + fileName.size());\n\t\t\tcollectionFile.append(sceneCollectionLocation.u8string()).append(fileName);\n\n\t\t\tif (!GetClosestUnusedFileName(collectionFile, \"json\")) {\n\t\t\t\tblog(LOG_WARNING, \"Failed to get closest file name for %s\", fileName.c_str());\n\t\t\t}\n\n\t\t\tstd::string out_str = json11::Json(out).dump();\n\n\t\t\tbool success = os_quick_write_utf8_file(collectionFile.c_str(), out_str.c_str(), out_str.size(),\n\t\t\t\t\t\t\t\tfalse);\n\n\t\t\tblog(LOG_INFO, \"Import Scene Collection: %s (%s) - %s\", name.c_str(), fileName.c_str(),\n\t\t\t     success ? \"SUCCESS\" : \"FAILURE\");\n\t\t}\n\t}\n\n\tclose();\n}\n\nvoid OBSImporter::dataChanged()\n{\n\tui->tableView->resizeColumnToContents(ImporterColumn::Name);\n\n\tbool enableImportButton = false;\n\n\tfor (int i = 0; i < optionsModel->rowCount() - 1; i++) {\n\t\tint selected = optionsModel->index(i, ImporterColumn::Selected).data(Qt::CheckStateRole).value<int>();\n\t\tif (selected == Qt::Checked) {\n\t\t\tenableImportButton = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enableImportButton);\n}\n"
  },
  {
    "path": "frontend/importer/OBSImporter.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_OBSImporter.h\"\n\n#include <QDialog>\n#include <QPointer>\n\nclass ImporterModel;\n\nclass OBSImporter : public QDialog {\n\tQ_OBJECT\n\n\tQPointer<ImporterModel> optionsModel;\n\tstd::unique_ptr<Ui::OBSImporter> ui;\n\npublic:\n\texplicit OBSImporter(QWidget *parent = nullptr);\n\n\tvoid addImportOption(QString path, bool automatic);\n\nprotected:\n\tvirtual void dropEvent(QDropEvent *ev) override;\n\tvirtual void dragEnterEvent(QDragEnterEvent *ev) override;\n\npublic slots:\n\tvoid browseImport();\n\tvoid importCollections();\n\tvoid dataChanged();\n};\n"
  },
  {
    "path": "frontend/importers/classic.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"importers.hpp\"\n\n#include <QByteArray>\n\nusing namespace std;\nusing namespace json11;\n\nstatic bool source_name_exists(const Json::array &sources, const string &name)\n{\n\tfor (size_t i = 0; i < sources.size(); i++) {\n\t\tJson source = sources[i];\n\t\tif (name == source[\"name\"].string_value())\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\n#define translate_int(in_key, in, out_key, out, off) out[out_key] = in[in_key].int_value() + off;\n#define translate_string(in_key, in, out_key, out) out[out_key] = in[in_key];\n#define translate_bool(in_key, in, out_key, out) out[out_key] = in[in_key].int_value() == 1;\n\nstatic Json::object translate_scene_item(const Json &in, const Json &source)\n{\n\tJson::object item = Json::object{};\n\n\ttranslate_string(\"name\", source, \"name\", item);\n\n\ttranslate_int(\"crop.top\", in, \"crop_top\", item, 0);\n\ttranslate_int(\"crop.bottom\", in, \"crop_bottom\", item, 0);\n\ttranslate_int(\"crop.left\", in, \"crop_left\", item, 0);\n\ttranslate_int(\"crop.right\", in, \"crop_right\", item, 0);\n\n\tJson::object pos = Json::object{};\n\ttranslate_int(\"x\", in, \"x\", pos, 0);\n\ttranslate_int(\"y\", in, \"y\", pos, 0);\n\n\tJson::object bounds = Json::object{};\n\ttranslate_int(\"cx\", in, \"x\", bounds, 0);\n\ttranslate_int(\"cy\", in, \"y\", bounds, 0);\n\n\titem[\"pos\"] = pos;\n\titem[\"bounds\"] = bounds;\n\titem[\"bounds_type\"] = 2;\n\titem[\"visible\"] = true;\n\n\treturn item;\n}\n\nstatic int red_blue_swap(int color)\n{\n\tint r = color / 256 / 256;\n\tint b = color % 256;\n\n\treturn color - (r * 65536) - b + (b * 65536) + r;\n}\n\nstatic void create_string_obj(const string &data, Json::array &arr);\n\nstatic Json::object translate_source(const Json &in, const Json &sources)\n{\n\tstring id = in[\"class\"].string_value();\n\tstring name = in[\"name\"].string_value();\n\n\tJson::array source_arr = sources.array_items();\n\n\tif (id == \"GlobalSource\") {\n\t\tfor (size_t i = 0; i < source_arr.size(); i++) {\n\t\t\tJson source = source_arr[i];\n\t\t\tif (name == source[\"name\"].string_value()) {\n\t\t\t\tJson::object obj = source.object_items();\n\t\t\t\tobj[\"preexist\"] = true;\n\t\t\t\treturn obj;\n\t\t\t}\n\t\t}\n\t}\n\n\tJson in_settings = in[\"data\"];\n\n\tJson::object settings = Json::object{};\n\tJson::object out = Json::object{};\n\n\tint i = 0;\n\tstring new_name = name;\n\twhile (source_name_exists(source_arr, new_name)) {\n\t\tnew_name = name + to_string(++i);\n\t}\n\tout[\"name\"] = new_name;\n\n\tif (id == \"TextSource\") {\n\t\tout[\"id\"] = \"text_gdiplus\";\n\n\t\tint color = in_settings[\"color\"].int_value() + 16777216;\n\t\tcolor = red_blue_swap(color) + 4278190080;\n\t\tsettings[\"color\"] = color;\n\n\t\tcolor = in_settings[\"backgroundColor\"].int_value();\n\t\tcolor = red_blue_swap(color + 16777216) + 4278190080;\n\t\tsettings[\"bk_color\"] = color;\n\n\t\tcolor = in_settings[\"outlineColor\"].int_value();\n\t\tcolor = red_blue_swap(color + 16777216) + 4278190080;\n\t\tsettings[\"outline_color\"] = color;\n\n\t\ttranslate_string(\"text\", in_settings, \"text\", settings);\n\t\ttranslate_int(\"backgroundOpacity\", in_settings, \"bk_opacity\", settings, 0);\n\t\ttranslate_bool(\"vertical\", in_settings, \"vertical\", settings);\n\t\ttranslate_int(\"textOpacity\", in_settings, \"opacity\", settings, 0);\n\t\ttranslate_bool(\"useOutline\", in_settings, \"outline\", settings);\n\t\ttranslate_int(\"outlineOpacity\", in_settings, \"outline_opacity\", settings, 0);\n\t\ttranslate_int(\"outlineSize\", in_settings, \"outline_size\", settings, 0);\n\t\ttranslate_bool(\"useTextExtents\", in_settings, \"extents\", settings);\n\t\ttranslate_int(\"extentWidth\", in_settings, \"extents_cx\", settings, 0);\n\t\ttranslate_int(\"extentHeight\", in_settings, \"extents_cy\", settings, 0);\n\t\ttranslate_bool(\"mode\", in_settings, \"read_from_file\", settings);\n\t\ttranslate_bool(\"wrap\", in_settings, \"extents_wrap\", settings);\n\n\t\tstring str = in_settings[\"file\"].string_value();\n\t\tsettings[\"file\"] = StringReplace(str, \"\\\\\\\\\", \"/\");\n\n\t\tint in_align = in_settings[\"align\"].int_value();\n\t\tstring align = in_align == 0 ? \"left\" : (in_align == 1 ? \"center\" : \"right\");\n\n\t\tsettings[\"align\"] = align;\n\n\t\tbool bold = in_settings[\"bold\"].int_value() == 1;\n\t\tbool italic = in_settings[\"italic\"].int_value() == 1;\n\t\tbool underline = in_settings[\"underline\"].int_value() == 1;\n\n\t\tint flags = bold ? OBS_FONT_BOLD : 0;\n\t\tflags |= italic ? OBS_FONT_ITALIC : 0;\n\t\tflags |= underline ? OBS_FONT_UNDERLINE : 0;\n\n\t\tJson::object font = Json::object{};\n\n\t\tfont[\"flags\"] = flags;\n\n\t\ttranslate_int(\"fontSize\", in_settings, \"size\", font, 0);\n\t\ttranslate_string(\"font\", in_settings, \"face\", font);\n\n\t\tif (bold && italic) {\n\t\t\tfont[\"style\"] = \"Bold Italic\";\n\t\t} else if (bold) {\n\t\t\tfont[\"style\"] = \"Bold\";\n\t\t} else if (italic) {\n\t\t\tfont[\"style\"] = \"Italic\";\n\t\t} else {\n\t\t\tfont[\"style\"] = \"Regular\";\n\t\t}\n\n\t\tsettings[\"font\"] = font;\n\t} else if (id == \"MonitorCaptureSource\") {\n\t\tout[\"id\"] = \"monitor_capture\";\n\n\t\ttranslate_int(\"monitor\", in_settings, \"monitor\", settings, 0);\n\t\ttranslate_bool(\"captureMouse\", in_settings, \"capture_cursor\", settings);\n\t} else if (id == \"BitmapImageSource\") {\n\t\tout[\"id\"] = \"image_source\";\n\n\t\tstring str = in_settings[\"path\"].string_value();\n\t\tsettings[\"file\"] = StringReplace(str, \"\\\\\\\\\", \"/\");\n\t} else if (id == \"BitmapTransitionSource\") {\n\t\tout[\"id\"] = \"slideshow\";\n\n\t\tJson files = in_settings[\"bitmap\"];\n\n\t\tif (!files.is_array()) {\n\t\t\tfiles = Json::array{in_settings[\"bitmap\"]};\n\t\t}\n\n\t\tsettings[\"files\"] = files;\n\t} else if (id == \"WindowCaptureSource\") {\n\t\tout[\"id\"] = \"window_capture\";\n\n\t\tstring win = in_settings[\"window\"].string_value();\n\t\tstring winClass = in_settings[\"windowClass\"].string_value();\n\n\t\twin = StringReplace(win, \"/\", \"\\\\\\\\\");\n\t\twin = StringReplace(win, \":\", \"#3A\");\n\t\twinClass = StringReplace(winClass, \":\", \"#3A\");\n\n\t\tsettings[\"window\"] = win + \":\" + winClass + \":\";\n\t\tsettings[\"priority\"] = 0;\n\t} else if (id == \"CLRBrowserSource\") {\n\t\tout[\"id\"] = \"browser_source\";\n\n\t\tstring browser_dec =\n\t\t\tQByteArray::fromBase64(in_settings[\"sourceSettings\"].string_value().c_str()).toStdString();\n\n\t\tstring err;\n\n\t\tJson browser = Json::parse(browser_dec, err);\n\n\t\tif (err != \"\")\n\t\t\treturn Json::object{};\n\n\t\tJson::object obj = browser.object_items();\n\n\t\ttranslate_string(\"CSS\", obj, \"css\", settings);\n\t\ttranslate_int(\"Height\", obj, \"height\", settings, 0);\n\t\ttranslate_int(\"Width\", obj, \"width\", settings, 0);\n\t\ttranslate_string(\"Url\", obj, \"url\", settings);\n\t} else if (id == \"DeviceCapture\") {\n\t\tout[\"id\"] = \"dshow_input\";\n\n\t\tstring device_id = in_settings[\"deviceID\"].string_value();\n\t\tstring device_name = in_settings[\"deviceName\"].string_value();\n\n\t\tsettings[\"video_device_id\"] = device_name + \":\" + device_id;\n\n\t\tint w = in_settings[\"resolutionWidth\"].int_value();\n\t\tint h = in_settings[\"resolutionHeight\"].int_value();\n\n\t\tsettings[\"resolution\"] = to_string(w) + \"x\" + to_string(h);\n\t} else if (id == \"GraphicsCapture\") {\n\t\tbool hotkey = in_settings[\"useHotkey\"].int_value() == 1;\n\n\t\tif (hotkey) {\n\t\t\tsettings[\"capture_mode\"] = \"hotkey\";\n\t\t} else {\n\t\t\tsettings[\"capture_mode\"] = \"window\";\n\t\t}\n\n\t\tstring winClass = in_settings[\"windowClass\"].string_value();\n\t\tstring exec = in_settings[\"executable\"].string_value();\n\n\t\tsettings[\"window\"] = \":\" + winClass + \":\" + exec;\n\n\t\ttranslate_bool(\"captureMouse\", in_settings, \"capture_cursor\", settings);\n\t}\n\n\tout[\"settings\"] = settings;\n\n\treturn out;\n}\n\n#undef translate_int\n#undef translate_string\n#undef translate_bool\n\nstatic void translate_sc(const Json &in, Json &out)\n{\n\tJson::object res = Json::object{};\n\n\tJson::array out_sources = Json::array{};\n\tJson::array global = in[\"globals\"].array_items();\n\n\tif (!in[\"globals\"].is_null()) {\n\t\tfor (size_t i = 0; i < global.size(); i++) {\n\t\t\tJson source = global[i];\n\n\t\t\tJson out_source = translate_source(source, out_sources);\n\t\t\tout_sources.push_back(out_source);\n\t\t}\n\t}\n\n\tJson::array scenes = in[\"scenes\"].array_items();\n\tstring first_name = \"\";\n\n\tfor (size_t i = 0; i < scenes.size(); i++) {\n\t\tJson in_scene = scenes[i];\n\n\t\tif (first_name.empty())\n\t\t\tfirst_name = in_scene[\"name\"].string_value();\n\n\t\tJson::array items = Json::array{};\n\n\t\tJson::array sources = in_scene[\"sources\"].array_items();\n\n\t\tfor (size_t x = sources.size(); x > 0; x--) {\n\t\t\tJson source = sources[x - 1];\n\n\t\t\tJson::object out_source = translate_source(source, out_sources);\n\t\t\tJson::object out_item = translate_scene_item(source, out_source);\n\n\t\t\tout_item[\"id\"] = (int)x - 1;\n\n\t\t\titems.push_back(out_item);\n\n\t\t\tif (out_source.find(\"preexist\") == out_source.end())\n\t\t\t\tout_sources.push_back(out_source);\n\t\t}\n\n\t\tout_sources.push_back(\n\t\t\tJson::object{{\"id\", \"scene\"},\n\t\t\t\t     {\"name\", in_scene[\"name\"]},\n\t\t\t\t     {\"settings\", Json::object{{\"items\", items}, {\"id_counter\", (int)items.size()}}}});\n\t}\n\n\tres[\"current_scene\"] = first_name;\n\tres[\"current_program_scene\"] = first_name;\n\tres[\"sources\"] = out_sources;\n\tres[\"name\"] = in[\"name\"];\n\n\tout = res;\n}\n\nstatic void create_string(const string &name, Json::object &out, const string &data)\n{\n\tstring str = StringReplace(data, \"\\\\\\\\\", \"/\");\n\tout[name] = str;\n}\n\nstatic void create_string_obj(const string &data, Json::array &arr)\n{\n\tJson::object obj = Json::object{};\n\tcreate_string(\"value\", obj, data);\n\tarr.push_back(obj);\n}\n\nstatic void create_double(const string &name, Json::object &out, const string &data)\n{\n\tdouble d = atof(data.c_str());\n\tout[name] = d;\n}\n\nstatic void create_int(const string &name, Json::object &out, const string &data)\n{\n\tint i = atoi(data.c_str());\n\tout[name] = i;\n}\n\nstatic void create_data_item(Json::object &out, const string &line)\n{\n\tsize_t end_pos = line.find(':') - 1;\n\n\tif (end_pos == string::npos)\n\t\treturn;\n\n\tsize_t start_pos = 0;\n\twhile (line[start_pos] == ' ')\n\t\tstart_pos++;\n\n\tstring name = line.substr(start_pos, end_pos - start_pos);\n\tconst char *c_name = name.c_str();\n\n\tstring first = line.substr(end_pos + 3);\n\n\tif ((first[0] >= 'A' && first[0] <= 'Z') || (first[0] >= 'a' && first[0] <= 'z') || first[0] == '\\\\' ||\n\t    first[0] == '/') {\n\t\tif (out.find(c_name) != out.end()) {\n\t\t\tJson::array arr = out[c_name].array_items();\n\t\t\tif (out[c_name].is_string()) {\n\t\t\t\tJson::array new_arr = Json::array{};\n\t\t\t\tstring str = out[c_name].string_value();\n\t\t\t\tcreate_string_obj(str, new_arr);\n\t\t\t\tarr = std::move(new_arr);\n\t\t\t}\n\n\t\t\tcreate_string_obj(first, arr);\n\t\t\tout[c_name] = arr;\n\t\t} else {\n\t\t\tcreate_string(c_name, out, first);\n\t\t}\n\t} else if (first[0] == '\"') {\n\t\tstring str = first.substr(1, first.size() - 2);\n\n\t\tif (out.find(c_name) != out.end()) {\n\t\t\tJson::array arr = out[c_name].array_items();\n\t\t\tif (out[c_name].is_string()) {\n\t\t\t\tJson::array new_arr = Json::array{};\n\t\t\t\tstring str1 = out[c_name].string_value();\n\t\t\t\tcreate_string_obj(str1, new_arr);\n\t\t\t\tarr = std::move(new_arr);\n\t\t\t}\n\n\t\t\tcreate_string_obj(str, arr);\n\t\t\tout[c_name] = arr;\n\t\t} else {\n\t\t\tcreate_string(c_name, out, str);\n\t\t}\n\t} else if (first.find('.') != string::npos) {\n\t\tcreate_double(c_name, out, first);\n\t} else {\n\t\tcreate_int(c_name, out, first);\n\t}\n}\n\nstatic Json::object create_object(Json::object &out, string &line, string &src);\n\nstatic Json::array create_sources(Json::object &out, string &line, string &src)\n{\n\tJson::array res = Json::array{};\n\n\tline = ReadLine(src);\n\tsize_t l_len = line.size();\n\twhile (!line.empty() && line[l_len - 1] != '}') {\n\t\tsize_t end_pos = line.find(':');\n\n\t\tif (end_pos == string::npos)\n\t\t\treturn Json::array{};\n\n\t\tsize_t start_pos = 0;\n\t\twhile (line[start_pos] == ' ')\n\t\t\tstart_pos++;\n\n\t\tstring name = line.substr(start_pos, end_pos - start_pos - 1);\n\n\t\tJson::object nul = Json::object();\n\n\t\tJson::object source = create_object(nul, line, src);\n\t\tsource[\"name\"] = name;\n\t\tres.push_back(source);\n\n\t\tline = ReadLine(src);\n\t\tl_len = line.size();\n\t}\n\n\tif (!out.empty())\n\t\tout[\"sources\"] = res;\n\n\treturn res;\n}\n\nstatic Json::object create_object(Json::object &out, string &line, string &src)\n{\n\tsize_t end_pos = line.find(':');\n\n\tif (end_pos == string::npos)\n\t\treturn Json::object{};\n\n\tsize_t start_pos = 0;\n\twhile (line[start_pos] == ' ')\n\t\tstart_pos++;\n\n\tstring name = line.substr(start_pos, end_pos - start_pos - 1);\n\n\tJson::object res = Json::object{};\n\n\tline = ReadLine(src);\n\n\tsize_t l_len = line.size() - 1;\n\n\twhile (!line.empty() && line[l_len] != '}') {\n\t\tstart_pos = 0;\n\t\twhile (line[start_pos] == ' ')\n\t\t\tstart_pos++;\n\n\t\tif (line.substr(start_pos, 7) == \"sources\")\n\t\t\tcreate_sources(res, line, src);\n\t\telse if (line[l_len] == '{')\n\t\t\tcreate_object(res, line, src);\n\t\telse\n\t\t\tcreate_data_item(res, line);\n\n\t\tline = ReadLine(src);\n\t\tl_len = line.size() - 1;\n\t}\n\n\tif (!out.empty())\n\t\tout[name] = res;\n\n\treturn res;\n}\n\nstring ClassicImporter::Name(const string &path)\n{\n\treturn GetFilenameFromPath(path);\n}\n\nint ClassicImporter::ImportScenes(const string &path, string &name, Json &res)\n{\n\tBPtr<char> file_data = os_quick_read_utf8_file(path.c_str());\n\tif (!file_data)\n\t\treturn IMPORTER_FILE_WONT_OPEN;\n\n\tif (name.empty())\n\t\tname = GetFilenameFromPath(path);\n\n\tJson::object data = Json::object{};\n\tdata[\"name\"] = name;\n\n\tstring file = file_data.Get();\n\tstring line = ReadLine(file);\n\n\twhile (!line.empty() && line[0] != '\\0') {\n\t\tstring key = line != \"global sources : {\" ? \"scenes\" : \"globals\";\n\n\t\tJson::array arr = create_sources(data, line, file);\n\t\tdata[key] = arr;\n\n\t\tline = ReadLine(file);\n\t}\n\n\tJson sc = data;\n\ttranslate_sc(sc, res);\n\n\tQDir dir(path.c_str());\n\n\tTranslateOSStudio(res);\n\tTranslatePaths(res, QDir::cleanPath(dir.filePath(\"..\")).toStdString());\n\n\treturn IMPORTER_SUCCESS;\n}\n\nbool ClassicImporter::Check(const string &path)\n{\n\tBPtr<char> file_data = os_quick_read_utf8_file(path.c_str());\n\n\tif (!file_data)\n\t\treturn false;\n\n\tbool check = false;\n\n\tif (strncmp(file_data, \"scenes : {\\r\\n\", 12) == 0)\n\t\tcheck = true;\n\n\treturn check;\n}\n\nOBSImporterFiles ClassicImporter::FindFiles()\n{\n\tOBSImporterFiles res;\n\n#ifdef _WIN32\n\tchar dst[512];\n\tint found = os_get_config_path(dst, 512, \"OBS\\\\sceneCollection\\\\\");\n\tif (found == -1)\n\t\treturn res;\n\n\tos_dir_t *dir = os_opendir(dst);\n\tstruct os_dirent *ent;\n\twhile ((ent = os_readdir(dir)) != NULL) {\n\t\tif (ent->directory || *ent->d_name == '.')\n\t\t\tcontinue;\n\n\t\tstring name = ent->d_name;\n\t\tsize_t pos = name.find(\".xconfig\");\n\t\tif (pos != -1 && pos == name.length() - 8) {\n\t\t\tstring path = dst + name;\n\t\t\tres.push_back(path);\n\t\t}\n\t}\n\n\tos_closedir(dir);\n#endif\n\n\treturn res;\n}\n"
  },
  {
    "path": "frontend/importers/importers.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"importers.hpp\"\n#include <memory>\n\nusing namespace std;\nusing namespace json11;\n\nvector<unique_ptr<Importer>> importers;\n\nvoid ImportersInit()\n{\n\timporters.clear();\n\timporters.push_back(make_unique<StudioImporter>());\n\timporters.push_back(make_unique<ClassicImporter>());\n\timporters.push_back(make_unique<SLImporter>());\n\timporters.push_back(make_unique<XSplitImporter>());\n}\n\nint ImportSCFromProg(const string &path, string &name, const string &program, Json &res)\n{\n\tif (!os_file_exists(path.c_str())) {\n\t\treturn IMPORTER_FILE_NOT_FOUND;\n\t}\n\n\tfor (size_t i = 0; i < importers.size(); i++) {\n\t\tif (program == importers[i]->Prog()) {\n\t\t\treturn importers[i]->ImportScenes(path, name, res);\n\t\t}\n\t}\n\n\treturn IMPORTER_UNKNOWN_ERROR;\n}\n\nint ImportSC(const string &path, std::string &name, Json &res)\n{\n\tif (!os_file_exists(path.c_str())) {\n\t\treturn IMPORTER_FILE_NOT_FOUND;\n\t}\n\n\tstring prog = DetectProgram(path);\n\n\tif (prog == \"Null\") {\n\t\treturn IMPORTER_FILE_NOT_RECOGNISED;\n\t}\n\n\treturn ImportSCFromProg(path, name, prog, res);\n}\n\nstring DetectProgram(const string &path)\n{\n\tif (!os_file_exists(path.c_str())) {\n\t\treturn \"Null\";\n\t}\n\n\tfor (size_t i = 0; i < importers.size(); i++) {\n\t\tif (importers[i]->Check(path)) {\n\t\t\treturn importers[i]->Prog();\n\t\t}\n\t}\n\n\treturn \"Null\";\n}\n\nstring GetSCName(const string &path, const string &prog)\n{\n\tfor (size_t i = 0; i < importers.size(); i++) {\n\t\tif (importers[i]->Prog() == prog) {\n\t\t\treturn importers[i]->Name(path);\n\t\t}\n\t}\n\n\treturn \"Null\";\n}\n\nOBSImporterFiles ImportersFindFiles()\n{\n\tOBSImporterFiles f;\n\n\tfor (size_t i = 0; i < importers.size(); i++) {\n\t\tOBSImporterFiles f2 = importers[i]->FindFiles();\n\t\tif (f2.size() != 0) {\n\t\t\tf.insert(f.end(), f2.begin(), f2.end());\n\t\t}\n\t}\n\n\treturn f;\n}\n"
  },
  {
    "path": "frontend/importers/importers.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"obs.hpp\"\n#include \"json11.hpp\"\n#include <util/platform.h>\n#include <util/util.hpp>\n#include <string>\n#include <vector>\n#include <QDir>\n\nenum obs_importer_responses {\n\tIMPORTER_SUCCESS,\n\tIMPORTER_FILE_NOT_FOUND,\n\tIMPORTER_FILE_NOT_RECOGNISED,\n\tIMPORTER_FILE_WONT_OPEN,\n\tIMPORTER_ERROR_DURING_CONVERSION,\n\tIMPORTER_UNKNOWN_ERROR,\n\tIMPORTER_NOT_FOUND\n};\n\ntypedef std::vector<std::string> OBSImporterFiles;\n\nclass Importer {\npublic:\n\tvirtual ~Importer() {}\n\tvirtual std::string Prog() { return \"Null\"; };\n\tvirtual int ImportScenes(const std::string &path, std::string &name, json11::Json &res) = 0;\n\tvirtual bool Check(const std::string &path) = 0;\n\tvirtual std::string Name(const std::string &path) = 0;\n\tvirtual OBSImporterFiles FindFiles()\n\t{\n\t\tOBSImporterFiles f;\n\t\treturn f;\n\t};\n};\n\nclass ClassicImporter : public Importer {\npublic:\n\tstd::string Prog() { return \"OBSClassic\"; };\n\tint ImportScenes(const std::string &path, std::string &name, json11::Json &res);\n\tbool Check(const std::string &path);\n\tstd::string Name(const std::string &path);\n\tOBSImporterFiles FindFiles();\n};\n\nclass StudioImporter : public Importer {\npublic:\n\tstd::string Prog() { return \"OBSStudio\"; };\n\tint ImportScenes(const std::string &path, std::string &name, json11::Json &res);\n\tbool Check(const std::string &path);\n\tstd::string Name(const std::string &path);\n};\n\nclass SLImporter : public Importer {\npublic:\n\tstd::string Prog() { return \"Streamlabs\"; };\n\tint ImportScenes(const std::string &path, std::string &name, json11::Json &res);\n\tbool Check(const std::string &path);\n\tstd::string Name(const std::string &path);\n\tOBSImporterFiles FindFiles();\n};\n\nclass XSplitImporter : public Importer {\npublic:\n\tstd::string Prog() { return \"XSplitBroadcaster\"; };\n\tint ImportScenes(const std::string &path, std::string &name, json11::Json &res);\n\tbool Check(const std::string &path);\n\tstd::string Name(const std::string &) { return \"XSplit Import\"; };\n\tOBSImporterFiles FindFiles();\n};\n\nvoid ImportersInit();\n\nstd::string DetectProgram(const std::string &path);\nstd::string GetSCName(const std::string &path, const std::string &prog);\n\nint ImportSCFromProg(const std::string &path, std::string &name, const std::string &program, json11::Json &res);\nint ImportSC(const std::string &path, std::string &name, json11::Json &res);\n\nOBSImporterFiles ImportersFindFiles();\n\nvoid TranslateOSStudio(json11::Json &data);\nvoid TranslatePaths(json11::Json &data, const std::string &rootDir);\n\nstatic inline std::string GetFilenameFromPath(const std::string &path)\n{\n#ifdef _WIN32\n\tsize_t pos = path.find_last_of('\\\\');\n\tif (pos == -1 || pos < path.find_last_of('/'))\n\t\tpos = path.find_last_of('/');\n#else\n\tsize_t pos = path.find_last_of('/');\n#endif\n\tsize_t ext = path.find_last_of('.');\n\n\tif (ext < pos) {\n\t\treturn path.substr(pos + 1);\n\t} else {\n\t\treturn path.substr(pos + 1, ext - pos - 1);\n\t}\n}\n\nstatic inline std::string GetFolderFromPath(const std::string &path)\n{\n#ifdef _WIN32\n\tsize_t pos = path.find_last_of('\\\\');\n\tif (pos == -1 || pos < path.find_last_of('/'))\n\t\tpos = path.find_last_of('/');\n#else\n\tsize_t pos = path.find_last_of('/');\n#endif\n\treturn path.substr(0, pos + 1);\n}\n\nstatic inline std::string StringReplace(const std::string &in, const std::string &search, const std::string &rep)\n{\n\tstd::string res = in;\n\tsize_t pos;\n\n\twhile ((pos = res.find(search)) != std::string::npos) {\n\t\tres.replace(pos, search.length(), rep);\n\t}\n\n\treturn res;\n}\n\nstatic inline std::string ReadLine(std::string &str)\n{\n\tstr = StringReplace(str, \"\\r\\n\", \"\\n\");\n\n\tsize_t pos = str.find('\\n');\n\n\tif (pos == std::string::npos)\n\t\tpos = str.find(EOF);\n\n\tif (pos == std::string::npos)\n\t\tpos = str.find('\\0');\n\n\tif (pos == std::string::npos)\n\t\treturn \"\";\n\n\tstd::string res = str.substr(0, pos);\n\tstr = str.substr(pos + 1);\n\n\treturn res;\n}\n"
  },
  {
    "path": "frontend/importers/sl.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"importers.hpp\"\n\nusing namespace std;\nusing namespace json11;\n\nstatic string translate_key(const string &sl_key)\n{\n\tif (sl_key.empty()) {\n\t\treturn \"IGNORE\";\n\t}\n\n\tif (sl_key.substr(0, 6) == \"Numpad\" && sl_key.size() == 7) {\n\t\treturn \"OBS_KEY_NUM\" + sl_key.substr(6);\n\t} else if (sl_key.substr(0, 3) == \"Key\") {\n\t\treturn \"OBS_KEY_\" + sl_key.substr(3);\n\t} else if (sl_key.substr(0, 5) == \"Digit\") {\n\t\treturn \"OBS_KEY_\" + sl_key.substr(5);\n\t} else if (sl_key[0] == 'F' && sl_key.size() < 4) {\n\t\treturn \"OBS_KEY_\" + sl_key;\n\t}\n\n#define add_translation(str, out) \\\n\tif (sl_key == str) {      \\\n\t\treturn out;       \\\n\t}\n\n\tadd_translation(\"Backquote\", \"OBS_KEY_ASCIITILDE\");\n\tadd_translation(\"Backspace\", \"OBS_KEY_BACKSPACE\");\n\tadd_translation(\"Tab\", \"OBS_KEY_TAB\");\n\tadd_translation(\"Space\", \"OBS_KEY_SPACE\");\n\tadd_translation(\"Period\", \"OBS_KEY_PERIOD\");\n\tadd_translation(\"Slash\", \"OBS_KEY_SLASH\");\n\tadd_translation(\"Backslash\", \"OBS_KEY_BACKSLASH\");\n\tadd_translation(\"Minus\", \"OBS_KEY_MINUS\");\n\tadd_translation(\"Comma\", \"OBS_KEY_COMMA\");\n\tadd_translation(\"Plus\", \"OBS_KEY_PLUS\");\n\tadd_translation(\"Quote\", \"OBS_KEY_APOSTROPHE\");\n\tadd_translation(\"Semicolon\", \"OBS_KEY_SEMICOLON\");\n\tadd_translation(\"NumpadSubtract\", \"OBS_KEY_NUMMINUS\");\n\tadd_translation(\"NumpadAdd\", \"OBS_KEY_NUMPLUS\");\n\tadd_translation(\"NumpadDecimal\", \"OBS_KEY_NUMPERIOD\");\n\tadd_translation(\"NumpadDivide\", \"OBS_KEY_NUMSLASH\");\n\tadd_translation(\"NumpadMultiply\", \"OBS_KEY_NUMASTERISK\");\n\tadd_translation(\"Enter\", \"OBS_KEY_RETURN\");\n\tadd_translation(\"CapsLock\", \"OBS_KEY_CAPSLOCK\");\n\tadd_translation(\"NumLock\", \"OBS_KEY_NUMLOCK\");\n\tadd_translation(\"ScrollLock\", \"OBS_KEY_SCROLLLOCK\");\n\tadd_translation(\"Pause\", \"OBS_KEY_PAUSE\");\n\tadd_translation(\"Insert\", \"OBS_KEY_INSERT\");\n\tadd_translation(\"Home\", \"OBS_KEY_HOME\");\n\tadd_translation(\"End\", \"OBS_KEY_END\");\n\tadd_translation(\"Escape\", \"OBS_KEY_ESCAPE\");\n\tadd_translation(\"Delete\", \"OBS_KEY_DELETE\");\n\tadd_translation(\"ArrowUp\", \"OBS_KEY_UP\");\n\tadd_translation(\"ArrowDown\", \"OBS_KEY_DOWN\");\n\tadd_translation(\"ArrowLeft\", \"OBS_KEY_LEFT\");\n\tadd_translation(\"ArrowRight\", \"OBS_KEY_RIGHT\");\n\tadd_translation(\"PageUp\", \"OBS_KEY_PAGEUP\");\n\tadd_translation(\"PageDown\", \"OBS_KEY_PAGEDOWN\");\n\tadd_translation(\"BracketLeft\", \"OBS_KEY_BRACKETLEFT\");\n\tadd_translation(\"BracketRight\", \"OBS_KEY_BRACKETRIGHT\");\n#undef add_translation\n\n\treturn \"\";\n}\n\nstatic string translate_hotkey(const Json &hotkey, const string &source)\n{\n\tstring name = hotkey[\"actionName\"].string_value();\n\n#define add_translation(in, str, out, source) \\\n\tif (in == str) {                      \\\n\t\treturn out + source;          \\\n\t}\n\n\tadd_translation(name, \"TOGGLE_SOURCE_VISIBILITY_SHOW\", \"libobs.show_scene_item.\", source);\n\tadd_translation(name, \"TOGGLE_SOURCE_VISIBILITY_HIDE\", \"libobs.hide_scene_item.\", source);\n\n\tstring empty = \"\";\n\n\tadd_translation(name, \"SWITCH_TO_SCENE\", \"OBSBasic.SelectScene\", empty);\n\n\tadd_translation(name, \"TOGGLE_MUTE\", \"libobs.mute\", empty);\n\tadd_translation(name, \"TOGGLE_UNMUTE\", \"libobs.unmute\", empty);\n\tadd_translation(name, \"PUSH_TO_MUTE\", \"libobs.push-to-mute\", empty);\n\tadd_translation(name, \"PUSH_TO_TALK\", \"libobs.push-to-talk\", empty);\n\tadd_translation(name, \"GAME_CAPTURE_HOTKEY_START\", \"hotkey_start\", empty);\n\tadd_translation(name, \"GAME_CAPTURE_HOTKEY_STOP\", \"hotkey_stop\", empty);\n\n\treturn \"\";\n#undef add_translation\n}\n\nstatic bool source_name_exists(const Json::array &sources, const string &name)\n{\n\tfor (size_t i = 0; i < sources.size(); i++) {\n\t\tJson item = sources[i];\n\t\tstring source_name = item[\"name\"].string_value();\n\n\t\tif (source_name == name)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic string get_source_name_from_id(const Json &root, const Json::array &sources, const string &id)\n{\n\tfor (size_t i = 0; i < sources.size(); i++) {\n\t\tJson item = sources[i];\n\t\tstring source_id = item[\"sl_id\"].string_value();\n\n\t\tif (source_id == id)\n\t\t\treturn item[\"name\"].string_value();\n\t}\n\n\tJson::array scene_arr = root[\"scenes\"][\"items\"].array_items();\n\n\tfor (size_t i = 0; i < scene_arr.size(); i++) {\n\t\tJson item = scene_arr[i];\n\t\tstring source_id = item[\"id\"].string_value();\n\n\t\tif (source_id == id) {\n\t\t\tstring name = item[\"name\"].string_value();\n\n\t\t\tint copy = 1;\n\t\t\tstring out_name = name;\n\n\t\t\twhile (source_name_exists(sources, out_name))\n\t\t\t\tout_name = name + \"(\" + to_string(copy++) + \")\";\n\n\t\t\treturn out_name;\n\t\t}\n\t}\n\n\treturn \"\";\n}\n\nstatic void get_hotkey_bindings(Json::object &out_hotkeys, const Json &in_hotkeys, const string &name)\n{\n\tJson::array hot_arr = in_hotkeys.array_items();\n\tfor (size_t i = 0; i < hot_arr.size(); i++) {\n\t\tJson hotkey = hot_arr[i];\n\t\tJson::array bindings = hotkey[\"bindings\"].array_items();\n\t\tJson::array out_hotkey = Json::array{};\n\n\t\tstring hotkey_name = translate_hotkey(hotkey, name);\n\n\t\tfor (size_t x = 0; x < bindings.size(); x++) {\n\t\t\tJson binding = bindings[x];\n\t\t\tJson modifiers = binding[\"modifiers\"];\n\n\t\t\tstring key = translate_key(binding[\"key\"].string_value());\n\n\t\t\tif (key == \"IGNORE\")\n\t\t\t\tcontinue;\n\n\t\t\tout_hotkey.push_back(Json::object{{\"control\", modifiers[\"ctrl\"]},\n\t\t\t\t\t\t\t  {\"shift\", modifiers[\"shift\"]},\n\t\t\t\t\t\t\t  {\"command\", modifiers[\"meta\"]},\n\t\t\t\t\t\t\t  {\"alt\", modifiers[\"alt\"]},\n\t\t\t\t\t\t\t  {\"key\", key}});\n\t\t}\n\n\t\tout_hotkeys[hotkey_name] = out_hotkey;\n\t}\n}\n\nstatic void get_scene_items(const Json &root, const Json::array &out_sources, Json::object &scene,\n\t\t\t    const Json::array &in)\n{\n\tint length = 0;\n\n\tJson::object hotkeys = scene[\"hotkeys\"].object_items();\n\n\tJson::array out_items = Json::array{};\n\tfor (size_t i = 0; i < in.size(); i++) {\n\t\tJson item = in[i];\n\n\t\tJson in_crop = item[\"crop\"];\n\t\tstring id = item[\"sourceId\"].string_value();\n\t\tstring name = get_source_name_from_id(root, out_sources, id);\n\n\t\tJson::array hotkey_items = item[\"hotkeys\"][\"items\"].array_items();\n\n\t\tget_hotkey_bindings(hotkeys, hotkey_items, name);\n\n\t\tout_items.push_back(Json::object{{\"name\", name},\n\t\t\t\t\t\t {\"id\", length++},\n\t\t\t\t\t\t {\"pos\", Json::object{{\"x\", item[\"x\"]}, {\"y\", item[\"y\"]}}},\n\t\t\t\t\t\t {\"scale\", Json::object{{\"x\", item[\"scaleX\"]}, {\"y\", item[\"scaleY\"]}}},\n\t\t\t\t\t\t {\"rot\", item[\"rotation\"]},\n\t\t\t\t\t\t {\"visible\", item[\"visible\"]},\n\t\t\t\t\t\t {\"crop_top\", in_crop[\"top\"]},\n\t\t\t\t\t\t {\"crop_bottom\", in_crop[\"bottom\"]},\n\t\t\t\t\t\t {\"crop_left\", in_crop[\"left\"]},\n\t\t\t\t\t\t {\"crop_right\", in_crop[\"right\"]}});\n\t}\n\n\tscene[\"hotkeys\"] = hotkeys;\n\tscene[\"settings\"] = Json::object{{\"items\", out_items}, {\"id_counter\", length}};\n}\n\nstatic void translate_screen_capture(Json::object &out_settings, string &type)\n{\n\tstring subtype_info = out_settings[\"capture_source_list\"].string_value();\n\tsize_t pos = subtype_info.find(':');\n\tstring subtype = subtype_info.substr(0, pos);\n\n\tif (subtype == \"game\") {\n\t\ttype = \"game_capture\";\n\t} else if (subtype == \"monitor\") {\n\t\ttype = \"monitor_capture\";\n\t\tout_settings[\"monitor\"] = subtype_info.substr(pos);\n\t} else if (subtype == \"window\") {\n\t\ttype = \"window_capture\";\n\t\tout_settings[\"cursor\"] = out_settings[\"capture_cursor\"];\n\t\tout_settings[\"window\"] = out_settings[\"capture_window_line\"].string_value();\n\t\tout_settings[\"capture_cursor\"] = nullptr;\n\t}\n\tout_settings[\"auto_capture_rules_path\"] = nullptr;\n\tout_settings[\"auto_placeholder_image\"] = nullptr;\n\tout_settings[\"auto_placeholder_message\"] = nullptr;\n\tout_settings[\"capture_source_list\"] = nullptr;\n\tout_settings[\"capture_window_line\"] = nullptr;\n}\n\nstatic int attempt_import(const Json &root, const string &name, Json &res)\n{\n\tJson::array source_arr = root[\"sources\"][\"items\"].array_items();\n\tJson::array scenes_arr = root[\"scenes\"][\"items\"].array_items();\n\tJson::array t_arr = root[\"transitions\"][\"transitions\"].array_items();\n\n\tstring t_id = root[\"transitions\"][\"defaultTransitionId\"].string_value();\n\n\tJson::array out_sources = Json::array{};\n\tJson::array out_transitions = Json::array{};\n\n\tfor (size_t i = 0; i < source_arr.size(); i++) {\n\t\tJson source = source_arr[i];\n\n\t\tJson in_hotkeys = source[\"hotkeys\"];\n\t\tJson::array hotkey_items = source[\"hotkeys\"][\"items\"].array_items();\n\t\tJson in_filters = source[\"filters\"];\n\t\tJson::array filter_items = in_filters[\"items\"].array_items();\n\n\t\tJson in_settings = source[\"settings\"];\n\t\tJson in_sync = source[\"syncOffset\"];\n\n\t\tint sync = (int)(in_sync[\"sec\"].number_value() * 1000000000 + in_sync[\"nsec\"].number_value());\n\n\t\tdouble vol = source[\"volume\"].number_value();\n\t\tbool muted = source[\"muted\"].bool_value();\n\t\tstring name = source[\"name\"].string_value();\n\t\tint monitoring = (int)source[\"monitoringType\"].int_value();\n\n\t\tJson::object out_hotkeys = Json::object{};\n\t\tget_hotkey_bindings(out_hotkeys, hotkey_items, \"\");\n\n\t\tJson::array out_filters = Json::array{};\n\t\tfor (size_t x = 0; x < filter_items.size(); x++) {\n\t\t\tJson::object filter = filter_items[x].object_items();\n\t\t\tstring type = filter[\"type\"].string_value();\n\t\t\tfilter[\"id\"] = type;\n\n\t\t\tout_filters.push_back(filter);\n\t\t}\n\n\t\tint copy = 1;\n\t\tstring out_name = name;\n\t\twhile (source_name_exists(out_sources, out_name))\n\t\t\tout_name = name + \"(\" + to_string(copy++) + \")\";\n\n\t\tstring sl_id = source[\"id\"].string_value();\n\n\t\tstring type = source[\"type\"].string_value();\n\t\tJson::object out_settings = in_settings.object_items();\n\t\tif (type == \"screen_capture\") {\n\t\t\ttranslate_screen_capture(out_settings, type);\n\t\t}\n\n\t\tout_sources.push_back(Json::object{{\"filters\", out_filters},\n\t\t\t\t\t\t   {\"hotkeys\", out_hotkeys},\n\t\t\t\t\t\t   {\"id\", type},\n\t\t\t\t\t\t   {\"sl_id\", sl_id},\n\t\t\t\t\t\t   {\"settings\", out_settings},\n\t\t\t\t\t\t   {\"sync\", sync},\n\t\t\t\t\t\t   {\"volume\", vol},\n\t\t\t\t\t\t   {\"muted\", muted},\n\t\t\t\t\t\t   {\"name\", out_name},\n\t\t\t\t\t\t   {\"monitoring_type\", monitoring}});\n\t}\n\n\tstring scene_name = \"\";\n\n\tfor (size_t i = 0; i < scenes_arr.size(); i++) {\n\t\tJson scene = scenes_arr[i];\n\n\t\tJson in_hotkeys = scene[\"hotkeys\"];\n\t\tJson::array hotkey_items = in_hotkeys[\"items\"].array_items();\n\t\tJson in_filters = scene[\"filters\"];\n\t\tJson::array filter_items = in_filters[\"items\"].array_items();\n\n\t\tJson in_settings = scene[\"settings\"];\n\n\t\tstring name = scene[\"name\"].string_value();\n\n\t\tJson::object out_hotkeys = Json::object{};\n\t\tget_hotkey_bindings(out_hotkeys, hotkey_items, \"\");\n\n\t\tJson::array out_filters = Json::array{};\n\t\tfor (size_t x = 0; x < filter_items.size(); x++) {\n\t\t\tJson::object filter = filter_items[x].object_items();\n\t\t\tstring type = filter[\"type\"].string_value();\n\t\t\tfilter[\"id\"] = type;\n\n\t\t\tout_filters.push_back(filter);\n\t\t}\n\n\t\tint copy = 1;\n\t\tstring out_name = name;\n\t\twhile (source_name_exists(out_sources, out_name))\n\t\t\tout_name = name + \"(\" + to_string(copy++) + \")\";\n\n\t\tif (scene_name.empty())\n\t\t\tscene_name = out_name;\n\n\t\tstring sl_id = scene[\"id\"].string_value();\n\n\t\tJson::object out = Json::object{{\"filters\", out_filters},  {\"hotkeys\", out_hotkeys},\n\t\t\t\t\t\t{\"id\", \"scene\"},           {\"sl_id\", sl_id},\n\t\t\t\t\t\t{\"settings\", in_settings}, {\"volume\", 1.0},\n\t\t\t\t\t\t{\"name\", out_name},        {\"private_settings\", Json::object{}}};\n\n\t\tJson in_items = scene[\"sceneItems\"];\n\t\tJson::array items_arr = in_items[\"items\"].array_items();\n\n\t\tget_scene_items(root, out_sources, out, items_arr);\n\n\t\tout_sources.push_back(out);\n\t}\n\n\tstring transition_name = \"\";\n\n\tfor (size_t i = 0; i < t_arr.size(); i++) {\n\t\tJson transition = t_arr[i];\n\n\t\tJson in_settings = transition[\"settings\"];\n\n\t\tint duration = transition[\"duration\"].int_value();\n\t\tstring name = transition[\"name\"].string_value();\n\t\tstring id = transition[\"id\"].string_value();\n\n\t\tif (id == t_id)\n\t\t\ttransition_name = name;\n\n\t\tout_transitions.push_back(Json::object{{\"id\", transition[\"type\"]},\n\t\t\t\t\t\t       {\"settings\", in_settings},\n\t\t\t\t\t\t       {\"name\", name},\n\t\t\t\t\t\t       {\"duration\", duration}});\n\t}\n\n\tres = Json::object{{\"sources\", out_sources},\n\t\t\t   {\"transitions\", out_transitions},\n\t\t\t   {\"current_scene\", scene_name},\n\t\t\t   {\"current_program_scene\", scene_name},\n\t\t\t   {\"current_transition\", transition_name},\n\t\t\t   {\"name\", name == \"\" ? \"Streamlabs Import\" : name}};\n\n\treturn IMPORTER_SUCCESS;\n}\n\nstring SLImporter::Name(const string &path)\n{\n\tstring name;\n\n\tstring folder = GetFolderFromPath(path);\n\tstring manifest_file = GetFilenameFromPath(path);\n\tstring manifest_path = folder + \"manifest.json\";\n\n\tif (os_file_exists(manifest_path.c_str())) {\n\t\tBPtr<char> file_data = os_quick_read_utf8_file(manifest_path.c_str());\n\n\t\tstring err;\n\t\tJson data = Json::parse(file_data, err);\n\n\t\tif (err == \"\") {\n\t\t\tJson::array collections = data[\"collections\"].array_items();\n\n\t\t\tbool name_set = false;\n\n\t\t\tfor (size_t i = 0, l = collections.size(); i < l; i++) {\n\t\t\t\tJson c = collections[i];\n\t\t\t\tstring c_id = c[\"id\"].string_value();\n\t\t\t\tstring c_name = c[\"name\"].string_value();\n\n\t\t\t\tif (c_id == manifest_file) {\n\t\t\t\t\tname = std::move(c_name);\n\t\t\t\t\tname_set = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!name_set) {\n\t\t\t\tname = \"Unknown Streamlabs Import\";\n\t\t\t}\n\t\t}\n\t} else {\n\t\tname = \"Unknown Streamlabs Import\";\n\t}\n\n\treturn name;\n}\n\nint SLImporter::ImportScenes(const string &path, string &name, Json &res)\n{\n\tBPtr<char> file_data = os_quick_read_utf8_file(path.c_str());\n\n\tstd::string err;\n\tJson data = Json::parse(file_data, err);\n\n\tif (err != \"\")\n\t\treturn IMPORTER_ERROR_DURING_CONVERSION;\n\n\tstring node_type = data[\"nodeType\"].string_value();\n\n\tint result = IMPORTER_ERROR_DURING_CONVERSION;\n\n\tif (node_type == \"RootNode\") {\n\t\tif (name == \"\") {\n\t\t\tstring auto_name = Name(path);\n\t\t\tresult = attempt_import(data, auto_name, res);\n\t\t} else {\n\t\t\tresult = attempt_import(data, name, res);\n\t\t}\n\t}\n\n\tQDir dir(path.c_str());\n\n\tTranslateOSStudio(res);\n\tTranslatePaths(res, QDir::cleanPath(dir.filePath(\"..\")).toStdString());\n\n\treturn result;\n}\n\nbool SLImporter::Check(const string &path)\n{\n\tbool check = false;\n\n\tBPtr<char> file_data = os_quick_read_utf8_file(path.c_str());\n\n\tif (file_data) {\n\t\tstring err;\n\t\tJson root = Json::parse(file_data, err);\n\n\t\tif (!root.is_null()) {\n\t\t\tstring node_type = root[\"nodeType\"].string_value();\n\n\t\t\tif (node_type == \"RootNode\")\n\t\t\t\tcheck = true;\n\t\t}\n\t}\n\n\treturn check;\n}\n\nOBSImporterFiles SLImporter::FindFiles()\n{\n\tOBSImporterFiles res;\n#if defined(_WIN32) || defined(__APPLE__)\n\tchar dst[512];\n\n\tint found = os_get_config_path(dst, 512, \"slobs-client/SceneCollections/\");\n\n\tif (found == -1)\n\t\treturn res;\n\n\tos_dir_t *dir = os_opendir(dst);\n\tstruct os_dirent *ent;\n\twhile ((ent = os_readdir(dir)) != NULL) {\n\t\tstring name = ent->d_name;\n\n\t\tif (ent->directory || name[0] == '.' || name == \"manifest.json\")\n\t\t\tcontinue;\n\n\t\tsize_t pos = name.find_last_of(\".json\");\n\t\tsize_t end_pos = name.size() - 1;\n\t\tif (pos != string::npos && pos == end_pos) {\n\t\t\tstring str = dst + name;\n\t\t\tres.push_back(str);\n\t\t}\n\t}\n\tos_closedir(dir);\n#endif\n\treturn res;\n}\n"
  },
  {
    "path": "frontend/importers/studio.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"importers.hpp\"\n#if !defined(_WIN32) && !defined(__APPLE__)\n#include <obs-nix-platform.h>\n#endif\n\nusing namespace std;\nusing namespace json11;\n\nvoid TranslateOSStudio(Json &res)\n{\n\tJson::object out = res.object_items();\n\tJson::array sources = out[\"sources\"].array_items();\n\n\tfor (size_t i = 0; i < sources.size(); i++) {\n\t\tJson::object source = sources[i].object_items();\n\t\tJson::object settings = source[\"settings\"].object_items();\n\n\t\tstring id = source[\"id\"].string_value();\n\n#define DirectTranslation(before, after)                                      \\\n\tif (id == before) {                                                   \\\n\t\tsource[\"id\"] = after;                                         \\\n\t\tsource[\"versioned_id\"] = obs_get_latest_input_type_id(after); \\\n\t}\n\n#define ClearTranslation(before, after)                                       \\\n\tif (id == before) {                                                   \\\n\t\tsource[\"id\"] = after;                                         \\\n\t\tsource[\"settings\"] = Json::object{};                          \\\n\t\tsource[\"versioned_id\"] = obs_get_latest_input_type_id(after); \\\n\t}\n\n#ifdef __APPLE__\n\t\tDirectTranslation(\"text_gdiplus\", \"text_ft2_source\");\n\n\t\tClearTranslation(\"game_capture\", \"syphon-input\");\n\n\t\tClearTranslation(\"wasapi_input_capture\", \"coreaudio_input_capture\");\n\t\tClearTranslation(\"wasapi_output_capture\", \"coreaudio_output_capture\");\n\t\tClearTranslation(\"pulse_input_capture\", \"coreaudio_input_capture\");\n\t\tClearTranslation(\"pulse_output_capture\", \"coreaudio_output_capture\");\n\n\t\tClearTranslation(\"jack_output_capture\", \"coreaudio_output_capture\");\n\t\tClearTranslation(\"alsa_input_capture\", \"coreaudio_input_capture\");\n\n\t\tClearTranslation(\"dshow_input\", \"av_capture_input\");\n\t\tClearTranslation(\"v4l2_input\", \"av_capture_input\");\n\n\t\tClearTranslation(\"xcomposite_input\", \"window_capture\");\n\n\t\tif (id == \"monitor_capture\") {\n\t\t\tif (settings[\"show_cursor\"].is_null() && !settings[\"capture_cursor\"].is_null()) {\n\t\t\t\tbool cursor = settings[\"capture_cursor\"].bool_value();\n\n\t\t\t\tsettings[\"show_cursor\"] = cursor;\n\t\t\t}\n\t\t}\n\n\t\tDirectTranslation(\"xshm_input\", \"monitor_capture\");\n#elif defined(_WIN32)\n\t\tDirectTranslation(\"text_ft2_source\", \"text_gdiplus\");\n\n\t\tClearTranslation(\"syphon-input\", \"game_capture\");\n\n\t\tClearTranslation(\"coreaudio_input_capture\", \"wasapi_input_capture\");\n\t\tClearTranslation(\"coreaudio_output_capture\", \"wasapi_output_capture\");\n\t\tClearTranslation(\"pulse_input_capture\", \"wasapi_input_capture\");\n\t\tClearTranslation(\"pulse_output_capture\", \"wasapi_output_capture\");\n\n\t\tClearTranslation(\"jack_output_capture\", \"wasapi_output_capture\");\n\t\tClearTranslation(\"alsa_input_capture\", \"wasapi_input_capture\");\n\n\t\tClearTranslation(\"av_capture_input\", \"dshow_input\");\n\t\tClearTranslation(\"v4l2_input\", \"dshow_input\");\n\n\t\tClearTranslation(\"xcomposite_input\", \"window_capture\");\n\n\t\tif (id == \"monitor_capture\" || id == \"xshm_input\") {\n\t\t\tif (!settings[\"show_cursor\"].is_null()) {\n\t\t\t\tbool cursor = settings[\"show_cursor\"].bool_value();\n\n\t\t\t\tsettings[\"capture_cursor\"] = cursor;\n\t\t\t}\n\n\t\t\tsource[\"id\"] = \"monitor_capture\";\n\t\t}\n#else\n\t\tDirectTranslation(\"text_gdiplus\", \"text_ft2_source\");\n\n\t\tClearTranslation(\"coreaudio_input_capture\", \"pulse_input_capture\");\n\t\tClearTranslation(\"coreaudio_output_capture\", \"pulse_output_capture\");\n\t\tClearTranslation(\"wasapi_input_capture\", \"pulse_input_capture\");\n\t\tClearTranslation(\"wasapi_output_capture\", \"pulse_output_capture\");\n\n\t\tClearTranslation(\"av_capture_input\", \"v4l2_input\");\n\t\tClearTranslation(\"dshow_input\", \"v4l2_input\");\n\n\t\tif (obs_get_nix_platform() == OBS_NIX_PLATFORM_X11_EGL) {\n\t\t\tClearTranslation(\"game_capture\", \"xcomposite_input\");\n\t\t\tClearTranslation(\"window_capture\", \"xcomposite_input\");\n\t\t} else {\n\t\t\tClearTranslation(\"game_capture\", \"pipewire-screen-capture-source\");\n\t\t\tClearTranslation(\"window_capture\", \"pipewire-screen-capture-source\");\n\t\t}\n\n\t\tif (id == \"monitor_capture\") {\n\t\t\tsource[\"id\"] = \"xshm_input\";\n\n\t\t\tif (settings[\"show_cursor\"].is_null() && !settings[\"capture_cursor\"].is_null()) {\n\t\t\t\tbool cursor = settings[\"capture_cursor\"].bool_value();\n\n\t\t\t\tsettings[\"show_cursor\"] = cursor;\n\t\t\t}\n\t\t}\n#endif\n\t\tsource[\"settings\"] = settings;\n\t\tsources[i] = source;\n#undef DirectTranslation\n#undef ClearTranslation\n\t}\n\n\tout[\"sources\"] = sources;\n\n\tres = out;\n}\n\nstatic string CheckPath(const string &path, const string &rootDir)\n{\n\tchar root[512];\n\t*root = 0;\n\tsize_t rootLen = os_get_abs_path(rootDir.c_str(), root, sizeof(root));\n\n\tchar absPath[512];\n\t*absPath = 0;\n\tsize_t len = os_get_abs_path((rootDir + path).c_str(), absPath, sizeof(absPath));\n\n\tif (len == 0)\n\t\treturn path;\n\n\tif (strstr(absPath, root) != absPath)\n\t\treturn path;\n\n\tif (*(absPath + rootLen) != QDir::separator().toLatin1())\n\t\treturn path;\n\n\treturn absPath;\n}\n\nvoid TranslatePaths(Json &res, const string &rootDir)\n{\n\tif (res.is_object()) {\n\t\tJson::object out = res.object_items();\n\n\t\tfor (auto it = out.begin(); it != out.end(); it++) {\n\t\t\tJson val = it->second;\n\n\t\t\tif (val.is_string()) {\n\t\t\t\tif (val.string_value().rfind(\"./\", 0) != 0)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tout[it->first] = CheckPath(val.string_value(), rootDir);\n\t\t\t} else if (val.is_array() || val.is_object()) {\n\t\t\t\tTranslatePaths(val, rootDir);\n\t\t\t\tout[it->first] = val;\n\t\t\t}\n\t\t}\n\n\t\tres = out;\n\t} else if (res.is_array()) {\n\t\tJson::array out = res.array_items();\n\n\t\tfor (size_t i = 0; i != out.size(); i++) {\n\t\t\tJson val = out[i];\n\n\t\t\tif (val.is_string()) {\n\t\t\t\tif (val.string_value().rfind(\"./\", 0) != 0)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tout[i] = CheckPath(val.string_value(), rootDir);\n\t\t\t} else if (val.is_array() || val.is_object()) {\n\t\t\t\tTranslatePaths(val, rootDir);\n\t\t\t\tout[i] = val;\n\t\t\t}\n\t\t}\n\n\t\tres = out;\n\t}\n}\n\nbool StudioImporter::Check(const string &path)\n{\n\tBPtr<char> file_data = os_quick_read_utf8_file(path.c_str());\n\tstring err;\n\tJson collection = Json::parse(file_data, err);\n\n\tif (err != \"\")\n\t\treturn false;\n\n\tif (collection.is_null())\n\t\treturn false;\n\n\tif (collection[\"sources\"].is_null())\n\t\treturn false;\n\n\tif (collection[\"name\"].is_null())\n\t\treturn false;\n\n\tif (collection[\"current_scene\"].is_null())\n\t\treturn false;\n\n\treturn true;\n}\n\nstring StudioImporter::Name(const string &path)\n{\n\tBPtr<char> file_data = os_quick_read_utf8_file(path.c_str());\n\tstring err;\n\n\tJson d = Json::parse(file_data, err);\n\n\tstring name = d[\"name\"].string_value();\n\n\treturn name;\n}\n\nint StudioImporter::ImportScenes(const string &path, string &name, Json &res)\n{\n\tif (!os_file_exists(path.c_str()))\n\t\treturn IMPORTER_FILE_NOT_FOUND;\n\n\tif (!Check(path.c_str()))\n\t\treturn IMPORTER_FILE_NOT_RECOGNISED;\n\n\tBPtr<char> file_data = os_quick_read_utf8_file(path.c_str());\n\tstring err;\n\tJson d = Json::parse(file_data, err);\n\n\tif (err != \"\")\n\t\treturn IMPORTER_ERROR_DURING_CONVERSION;\n\n\tQDir dir(path.c_str());\n\n\tTranslateOSStudio(d);\n\tTranslatePaths(d, QDir::cleanPath(dir.filePath(\"..\")).toStdString());\n\n\tJson::object obj = d.object_items();\n\n\tif (name != \"\")\n\t\tobj[\"name\"] = name;\n\telse\n\t\tobj[\"name\"] = \"OBS Studio Import\";\n\n\tres = obj;\n\n\treturn IMPORTER_SUCCESS;\n}\n"
  },
  {
    "path": "frontend/importers/xsplit.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019-2020 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"importers.hpp\"\n#include <ctype.h>\n\n#include <QDomDocument>\n\nusing namespace std;\nusing namespace json11;\n\nstatic int hex_string_to_int(string str)\n{\n\tint res = 0;\n\n\tif (str[0] == '#')\n\t\tstr = str.substr(1);\n\n\tfor (size_t i = 0, l = str.size(); i < l; i++) {\n\t\tres *= 16;\n\n\t\tif (str[0] >= '0' && str[0] <= '9')\n\t\t\tres += str[0] - '0';\n\t\telse\n\t\t\tres += str[0] - 'A' + 10;\n\n\t\tstr = str.substr(1);\n\t}\n\n\treturn res;\n}\n\nstatic Json::object parse_text(QString &config)\n{\n\tint start = config.indexOf(\"*{\");\n\tconfig = config.mid(start + 1);\n\tconfig.replace(\"\\\\\", \"/\");\n\n\tstring err;\n\tJson data = Json::parse(config.toStdString(), err);\n\n\tif (err != \"\")\n\t\treturn Json::object{};\n\n\tstring outline = data[\"outline\"].string_value();\n\tint out = 0;\n\n\tif (outline == \"thick\")\n\t\tout = 20;\n\telse if (outline == \"thicker\")\n\t\tout = 40;\n\telse if (outline == \"thinner\")\n\t\tout = 5;\n\telse if (outline == \"thin\")\n\t\tout = 10;\n\n\tstring valign = data[\"vertAlign\"].string_value();\n\tif (valign == \"middle\")\n\t\tvalign = \"center\";\n\n\tJson font = Json::object{{\"face\", data[\"fontStyle\"]}, {\"size\", 200}};\n\n\treturn Json::object{{\"text\", data[\"text\"]},\n\t\t\t    {\"font\", font},\n\t\t\t    {\"outline\", out > 0},\n\t\t\t    {\"outline_size\", out},\n\t\t\t    {\"outline_color\", hex_string_to_int(data[\"outlineColor\"].string_value())},\n\t\t\t    {\"color\", hex_string_to_int(data[\"color\"].string_value())},\n\t\t\t    {\"align\", data[\"textAlign\"]},\n\t\t\t    {\"valign\", valign},\n\t\t\t    {\"alpha\", data[\"opacity\"]}};\n}\n\nstatic Json::array parse_playlist(QString &playlist)\n{\n\tJson::array out = Json::array{};\n\n\twhile (true) {\n\t\tint end = playlist.indexOf('*');\n\t\tQString path = playlist.left(end);\n\n\t\tout.push_back(Json::object{{\"value\", path.toStdString()}});\n\n\t\tint next = playlist.indexOf('|');\n\t\tif (next == -1)\n\t\t\tbreak;\n\n\t\tplaylist = playlist.mid(next + 1);\n\t}\n\n\treturn out;\n}\n\nstatic void parse_media_types(QDomNamedNodeMap &attr, Json::object &source, Json::object &settings)\n{\n\tQString playlist = attr.namedItem(\"FilePlaylist\").nodeValue();\n\n\tif (playlist != \"\") {\n\t\tsource[\"id\"] = \"vlc_source\";\n\t\tsettings[\"playlist\"] = parse_playlist(playlist);\n\n\t\tQString end_op = attr.namedItem(\"OpWhenFinished\").nodeValue();\n\t\tif (end_op == \"2\")\n\t\t\tsettings[\"loop\"] = true;\n\t} else {\n\t\tQString url = attr.namedItem(\"item\").nodeValue();\n\t\tint sep = url.indexOf(\"://\");\n\n\t\tif (sep != -1) {\n\t\t\tQString prot = url.left(sep);\n\t\t\tif (prot == \"smlndi\") {\n\t\t\t\tsource[\"id\"] = \"ndi_source\";\n\t\t\t} else {\n\t\t\t\tsource[\"id\"] = \"ffmpeg_source\";\n\t\t\t\tint info = url.indexOf(\"\\\\\");\n\t\t\t\tQString input;\n\n\t\t\t\tif (info != -1) {\n\t\t\t\t\tinput = url.left(info);\n\t\t\t\t} else {\n\t\t\t\t\tinput = url;\n\t\t\t\t}\n\n\t\t\t\tsettings[\"input\"] = input.toStdString();\n\t\t\t\tsettings[\"is_local_file\"] = false;\n\t\t\t}\n\t\t} else {\n\t\t\tsource[\"id\"] = \"ffmpeg_source\";\n\t\t\tsettings[\"local_file\"] = url.replace(\"\\\\\", \"/\").toStdString();\n\t\t\tsettings[\"is_local_file\"] = true;\n\t\t}\n\t}\n}\n\nstatic Json::object parse_slideshow(QString &config)\n{\n\tint start = config.indexOf(\"images\\\":[\");\n\tif (start == -1)\n\t\treturn Json::object{};\n\n\tconfig = config.mid(start + 8);\n\tconfig.replace(\"\\\\\\\\\", \"/\");\n\n\tint end = config.indexOf(']');\n\tif (end == -1)\n\t\treturn Json::object{};\n\n\tstring arr = config.left(end + 1).toStdString();\n\tstring err;\n\tJson::array files = Json::parse(arr, err).array_items();\n\n\tif (err != \"\")\n\t\treturn Json::object{};\n\n\tJson::array files_out = Json::array{};\n\n\tfor (size_t i = 0; i < files.size(); i++) {\n\t\tstring file = files[i].string_value();\n\t\tfiles_out.push_back(Json::object{{\"value\", file}});\n\t}\n\n\tQString options = config.mid(end + 1);\n\toptions[0] = '{';\n\n\tJson opt = Json::parse(options.toStdString(), err);\n\n\tif (err != \"\")\n\t\treturn Json::object{};\n\n\treturn Json::object{{\"randomize\", opt[\"random\"]},\n\t\t\t    {\"slide_time\", opt[\"delay\"].number_value() * 1000 + 700},\n\t\t\t    {\"files\", files_out}};\n}\n\nstatic bool source_name_exists(const string &name, const Json::array &sources)\n{\n\tfor (size_t i = 0; i < sources.size(); i++) {\n\t\tif (sources.at(i)[\"name\"].string_value() == name)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic Json get_source_with_id(const string &src_id, const Json::array &sources)\n{\n\tfor (size_t i = 0; i < sources.size(); i++) {\n\t\tif (sources.at(i)[\"src_id\"].string_value() == src_id)\n\t\t\treturn sources.at(i);\n\t}\n\n\treturn nullptr;\n}\n\nstatic void parse_items(QDomNode &item, Json::array &items, Json::array &sources)\n{\n\twhile (!item.isNull()) {\n\t\tQDomNamedNodeMap attr = item.attributes();\n\t\tQString srcid = attr.namedItem(\"srcid\").nodeValue();\n\t\tdouble vol = attr.namedItem(\"volume\").nodeValue().toDouble();\n\t\tint type = attr.namedItem(\"type\").nodeValue().toInt();\n\n\t\tstring name;\n\t\tJson::object settings;\n\t\tJson::object source;\n\t\tstring temp_name;\n\t\tint x = 0;\n\n\t\tJson exists = get_source_with_id(srcid.toStdString(), sources);\n\t\tif (!exists.is_null()) {\n\t\t\tname = exists[\"name\"].string_value();\n\t\t\tgoto skip;\n\t\t}\n\n\t\tname = attr.namedItem(\"cname\").nodeValue().toStdString();\n\t\tif (name.empty() || name[0] == '\\0')\n\t\t\tname = attr.namedItem(\"name\").nodeValue().toStdString();\n\n\t\ttemp_name = name;\n\t\twhile (source_name_exists(temp_name, sources)) {\n\t\t\tstring new_name = name + \" \" + to_string(x++);\n\t\t\ttemp_name = new_name;\n\t\t}\n\n\t\tname = temp_name;\n\n\t\tsettings = Json::object{};\n\t\tsource = Json::object{{\"name\", name}, {\"src_id\", srcid.toStdString()}, {\"volume\", vol}};\n\n\t\t/** type=1     means Media of some kind (Video Playlist, RTSP,\n\t\t               RTMP, NDI or Media File).\n\t\t    type=2     means either a DShow or WASAPI source.\n\t\t    type=4     means an Image source.\n\t\t    type=5     means either a Display or Window Capture.\n\t\t    type=7     means a Game Capture.\n\t\t    type=8     means rendered with a browser, which includes:\n\t\t                   Web Page, Image Slideshow, Text.\n\t\t    type=11    means another Scene. **/\n\n\t\tif (type == 1) {\n\t\t\tparse_media_types(attr, source, settings);\n\t\t} else if (type == 2) {\n\t\t\tQString audio = attr.namedItem(\"itemaudio\").nodeValue();\n\n\t\t\tif (audio.isEmpty()) {\n\t\t\t\tsource[\"id\"] = \"dshow_input\";\n\t\t\t} else {\n\t\t\t\tsource[\"id\"] = \"wasapi_input_capture\";\n\t\t\t\tint dev = audio.indexOf(\"\\\\wave:\") + 6;\n\n\t\t\t\tQString res = \"{0.0.1.00000000}.\" + audio.mid(dev);\n\t\t\t\tres = res.toLower();\n\n\t\t\t\tsettings[\"device_id\"] = res.toStdString();\n\t\t\t}\n\t\t} else if (type == 4) {\n\t\t\tsource[\"id\"] = \"image_source\";\n\n\t\t\tQString path = attr.namedItem(\"item\").nodeValue();\n\t\t\tpath.replace(\"\\\\\", \"/\");\n\t\t\tsettings[\"file\"] = path.toStdString();\n\t\t} else if (type == 5) {\n\t\t\tQString opt = attr.namedItem(\"item\").nodeValue();\n\n\t\t\tQDomDocument options;\n\t\t\toptions.setContent(opt);\n\n\t\t\tQDomNode el = options.documentElement();\n\n\t\t\tQDomNamedNodeMap o_attr = el.attributes();\n\t\t\tQString display = o_attr.namedItem(\"desktop\").nodeValue();\n\n\t\t\tif (!display.isEmpty()) {\n\t\t\t\tsource[\"id\"] = \"monitor_capture\";\n\t\t\t\tint cursor = attr.namedItem(\"ScrCapShowMouse\").nodeValue().toInt();\n\t\t\t\tsettings[\"capture_cursor\"] = cursor == 1;\n\t\t\t} else {\n\t\t\t\tsource[\"id\"] = \"window_capture\";\n\n\t\t\t\tQString exec = o_attr.namedItem(\"module\").nodeValue();\n\t\t\t\tQString window = o_attr.namedItem(\"window\").nodeValue();\n\t\t\t\tQString _class = o_attr.namedItem(\"class\").nodeValue();\n\n\t\t\t\tint pos = exec.lastIndexOf('\\\\');\n\n\t\t\t\tif (_class.isEmpty()) {\n\t\t\t\t\t_class = \"class\";\n\t\t\t\t}\n\n\t\t\t\tQString res = window + \":\" + _class + \":\" + exec.mid(pos + 1);\n\n\t\t\t\tsettings[\"window\"] = res.toStdString();\n\t\t\t\tsettings[\"priority\"] = 2;\n\t\t\t}\n\t\t} else if (type == 7) {\n\t\t\tQString opt = attr.namedItem(\"item\").nodeValue();\n\t\t\topt.replace(\"&lt;\", \"<\");\n\t\t\topt.replace(\"&gt;\", \">\");\n\t\t\topt.replace(\"&quot;\", \"\\\"\");\n\n\t\t\tQDomDocument doc;\n\t\t\tdoc.setContent(opt);\n\n\t\t\tQDomNode el = doc.documentElement();\n\t\t\tQDomNamedNodeMap o_attr = el.attributes();\n\n\t\t\tQString name = o_attr.namedItem(\"wndname\").nodeValue();\n\t\t\tQString exec = o_attr.namedItem(\"imagename\").nodeValue();\n\n\t\t\tQString res = name = \"::\" + exec;\n\n\t\t\tsource[\"id\"] = \"game_capture\";\n\t\t\tsettings[\"window\"] = res.toStdString();\n\t\t\tsettings[\"capture_mode\"] = \"window\";\n\t\t} else if (type == 8) {\n\t\t\tQString plugin = attr.namedItem(\"item\").nodeValue();\n\n\t\t\tif (plugin.startsWith(\"html:plugin:imageslideshowplg*\")) {\n\t\t\t\tsource[\"id\"] = \"slideshow\";\n\t\t\t\tsettings = parse_slideshow(plugin);\n\t\t\t} else if (plugin.startsWith(\"html:plugin:titleplg\")) {\n\t\t\t\tsource[\"id\"] = \"text_gdiplus\";\n\t\t\t\tsettings = parse_text(plugin);\n\t\t\t} else if (plugin.startsWith(\"http\")) {\n\t\t\t\tsource[\"id\"] = \"browser_source\";\n\t\t\t\tint end = plugin.indexOf('*');\n\t\t\t\tsettings[\"url\"] = plugin.left(end).toStdString();\n\t\t\t}\n\t\t} else if (type == 11) {\n\t\t\tQString id = attr.namedItem(\"item\").nodeValue();\n\t\t\tJson source = get_source_with_id(id.toStdString(), sources);\n\t\t\tname = source[\"name\"].string_value();\n\n\t\t\tgoto skip;\n\t\t}\n\n\t\tsource[\"settings\"] = settings;\n\t\tsources.push_back(source);\n\n\tskip:\n\t\tstruct obs_video_info ovi;\n\t\tobs_get_video_info(&ovi);\n\n\t\tint width = ovi.base_width;\n\t\tint height = ovi.base_height;\n\n\t\tdouble pos_left = attr.namedItem(\"pos_left\").nodeValue().toDouble();\n\t\tdouble pos_right = attr.namedItem(\"pos_right\").nodeValue().toDouble();\n\t\tdouble pos_top = attr.namedItem(\"pos_top\").nodeValue().toDouble();\n\t\tdouble pos_bottom = attr.namedItem(\"pos_bottom\").nodeValue().toDouble();\n\n\t\tbool visible = attr.namedItem(\"visible\").nodeValue() == \"1\";\n\n\t\tJson out_item = Json::object{{\"bounds_type\", 2},\n\t\t\t\t\t     {\"pos\", Json::object{{\"x\", pos_left * width}, {\"y\", pos_top * height}}},\n\t\t\t\t\t     {\"bounds\", Json::object{{\"x\", (pos_right - pos_left) * width},\n\t\t\t\t\t\t\t\t     {\"y\", (pos_bottom - pos_top) * height}}},\n\t\t\t\t\t     {\"name\", name},\n\t\t\t\t\t     {\"visible\", visible}};\n\n\t\titems.push_back(out_item);\n\n\t\titem = item.nextSibling();\n\t}\n}\n\nstatic Json::object parse_scenes(QDomElement &scenes)\n{\n\tJson::array sources = Json::array{};\n\n\tQString first = \"\";\n\n\tQDomNode in_scene = scenes.firstChild();\n\twhile (!in_scene.isNull()) {\n\t\tQString type = in_scene.nodeName();\n\n\t\tif (type == \"placement\") {\n\t\t\tQDomNamedNodeMap attr = in_scene.attributes();\n\n\t\t\tQString name = attr.namedItem(\"name\").nodeValue();\n\t\t\tQString id = attr.namedItem(\"id\").nodeValue();\n\n\t\t\tif (first.isEmpty())\n\t\t\t\tfirst = name;\n\n\t\t\tJson out = Json::object{{\"id\", \"scene\"},\n\t\t\t\t\t\t{\"name\", name.toStdString().c_str()},\n\t\t\t\t\t\t{\"src_id\", id.toStdString().c_str()}};\n\n\t\t\tsources.push_back(out);\n\t\t}\n\t\tin_scene = in_scene.nextSibling();\n\t}\n\n\tin_scene = scenes.firstChild();\n\tfor (size_t i = 0, l = sources.size(); i < l; i++) {\n\t\tJson::object source = sources[i].object_items();\n\t\tJson::array items = Json::array{};\n\t\tQDomNode firstChild = in_scene.firstChild();\n\n\t\tparse_items(firstChild, items, sources);\n\n\t\tJson settings = Json::object{{\"items\", items}, {\"id_counter\", (int)items.size()}};\n\n\t\tsource[\"settings\"] = settings;\n\t\tsources[i] = source;\n\n\t\tin_scene = in_scene.nextSibling();\n\t}\n\n\treturn Json::object{{\"sources\", sources},\n\t\t\t    {\"current_scene\", first.toStdString()},\n\t\t\t    {\"current_program_scene\", first.toStdString()}};\n}\n\nint XSplitImporter::ImportScenes(const string &path, string &name, json11::Json &res)\n{\n\tif (name == \"\")\n\t\tname = \"XSplit Import\";\n\n\tBPtr<char> file_data = os_quick_read_utf8_file(path.c_str());\n\n\tif (!file_data)\n\t\treturn IMPORTER_FILE_WONT_OPEN;\n\n\tQDomDocument doc;\n\tdoc.setContent(QString(file_data));\n\n\tQDomElement docElem = doc.documentElement();\n\n\tJson::object r = parse_scenes(docElem);\n\tr[\"name\"] = name;\n\n\tres = r;\n\n\tQDir dir(path.c_str());\n\n\tTranslateOSStudio(res);\n\tTranslatePaths(res, QDir::cleanPath(dir.filePath(\"..\")).toStdString());\n\n\treturn IMPORTER_SUCCESS;\n}\n\nbool XSplitImporter::Check(const string &path)\n{\n\tbool check = false;\n\n\tBPtr<char> file_data = os_quick_read_utf8_file(path.c_str());\n\n\tif (!file_data)\n\t\treturn false;\n\n\tstring pos = file_data.Get();\n\n\tstring line = ReadLine(pos);\n\twhile (!line.empty()) {\n\t\tif (line.substr(0, 5) == \"<?xml\") {\n\t\t\tline = ReadLine(pos);\n\t\t} else {\n\t\t\tif (line.substr(0, 14) == \"<configuration\") {\n\t\t\t\tcheck = true;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn check;\n}\n\nOBSImporterFiles XSplitImporter::FindFiles()\n{\n\tOBSImporterFiles res;\n#ifdef _WIN32\n\tchar dst[512];\n\tint found = os_get_program_data_path(dst, 512, \"SplitMediaLabs\\\\XSplit\\\\Presentation2.0\\\\\");\n\n\tif (found == -1)\n\t\treturn res;\n\n\tos_dir_t *dir = os_opendir(dst);\n\tstruct os_dirent *ent;\n\n\twhile ((ent = os_readdir(dir)) != NULL) {\n\t\tstring name = ent->d_name;\n\n\t\tif (ent->directory || name[0] == '.')\n\t\t\tcontinue;\n\n\t\tif (name == \"Placements.bpres\") {\n\t\t\tstring str = dst + name;\n\t\t\tres.push_back(str);\n\n\t\t\tbreak;\n\t\t}\n\t}\n\tos_closedir(dir);\n#endif\n\treturn res;\n}\n"
  },
  {
    "path": "frontend/models/Rect.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Patrick Heyer <PatTheMav@users.noreply.github.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"Rect.hpp\"\n\nnamespace OBS {\nbool Rect::isZero() const\n{\n\treturn width_ == 0.0 || height_ == 0.0;\n}\n\nbool operator==(const Rect &lhs, const Rect &rhs)\n{\n\treturn lhs.width_ == rhs.width_ && lhs.height_ == rhs.height_;\n}\n\nbool operator!=(const Rect &lhs, const Rect &rhs)\n{\n\treturn lhs.width_ != rhs.width_ || lhs.height_ != rhs.height_;\n}\n} // namespace OBS\n"
  },
  {
    "path": "frontend/models/Rect.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Patrick Heyer <PatTheMav@users.noreply.github.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <algorithm>\n#include <limits>\n\nnamespace {\ntemplate<typename numberType> inline double safeConvertToDouble(numberType number)\n{\n\tconstexpr double minDoubleValue = 0.0;\n\tconstexpr double maxDoubleValue = std::numeric_limits<double>::max();\n\n\tif (number > maxDoubleValue) {\n\t\treturn maxDoubleValue;\n\t} else if (number < minDoubleValue) {\n\t\treturn minDoubleValue;\n\t} else {\n\t\treturn static_cast<double>(number);\n\t}\n}\n} // namespace\n\nnamespace OBS {\n\nstruct Rect {\n\tdouble width_ = 0.0;\n\tdouble height_ = 0.0;\n\n\tRect() = default;\n\n\ttemplate<typename widthType, typename heightType> Rect(widthType width, heightType height)\n\t{\n\t\tsetWidth(width);\n\t\tsetHeight(height);\n\t}\n\n\ttemplate<typename returnType> returnType getWidth() const\n\t{\n\t\tstatic_assert(std::is_convertible_v<double, returnType>,\n\t\t\t      \"Cannot convert <double> to requested return type\");\n\n\t\treturn static_cast<returnType>(width_);\n\t};\n\n\ttemplate<typename numberType> void setWidth(numberType width)\n\t{\n\t\tstatic_assert(std::is_same_v<numberType, double> || std::is_convertible_v<numberType, double>,\n\t\t\t      \"Cannot convert provided argument to <double>\");\n\n\t\tif (std::is_same<numberType, double>::value) {\n\t\t\twidth_ = std::max(static_cast<double>(width), 0.0);\n\t\t} else {\n\t\t\twidth_ = safeConvertToDouble(width);\n\t\t}\n\t}\n\n\ttemplate<typename returnType> returnType getHeight() const\n\t{\n\t\tstatic_assert(std::is_convertible_v<double, returnType>,\n\t\t\t      \"Cannot convert <double> to requested return type\");\n\n\t\treturn static_cast<returnType>(height_);\n\t};\n\n\ttemplate<typename numberType> void setHeight(numberType height)\n\t{\n\t\tstatic_assert(std::is_same_v<numberType, double> || std::is_convertible_v<numberType, double>,\n\t\t\t      \"Cannot convert provided argument to <double>\");\n\n\t\tif (std::is_same<numberType, double>::value) {\n\t\t\theight_ = std::max(static_cast<double>(height), 0.0);\n\t\t} else {\n\t\t\theight_ = safeConvertToDouble(height);\n\t\t}\n\t}\n\n\tbool isZero() const;\n\n\tfriend bool operator==(const Rect &lhs, const Rect &rhs);\n\tfriend bool operator!=(const Rect &lhs, const Rect &rhs);\n};\n} // namespace OBS\n"
  },
  {
    "path": "frontend/models/SceneCollection.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Patrick Heyer <PatTheMav@users.noreply.github.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"SceneCollection.hpp\"\n\nstatic constexpr std::string_view invalidCoordinateMode = \"Invalid coordinate mode provided\";\n\nnamespace OBS {\nstd::string SceneCollection::getName() const\n{\n\treturn name_;\n}\n\nstd::string SceneCollection::getFileName() const\n{\n\treturn filePath_.filename().u8string();\n}\n\nstd::string SceneCollection::getFileStem() const\n{\n\treturn filePath_.stem().u8string();\n}\n\nstd::filesystem::path SceneCollection::getFilePath() const\n{\n\treturn filePath_;\n}\n\nstd::string SceneCollection::getFilePathString() const\n{\n\treturn filePath_.u8string();\n}\n\nSceneCoordinateMode SceneCollection::getCoordinateMode() const\n{\n\treturn coordinateMode_;\n}\n\nvoid SceneCollection::setCoordinateMode(SceneCoordinateMode newMode)\n{\n\tif (newMode != SceneCoordinateMode::Invalid) {\n\t\tcoordinateMode_ = newMode;\n\t} else {\n\t\tthrow std::invalid_argument(invalidCoordinateMode.data());\n\t}\n}\n\nRect SceneCollection::getMigrationResolution() const\n{\n\treturn migrationResolution_;\n}\n\nvoid SceneCollection::setMigrationResolution(Rect newResolution)\n{\n\tmigrationResolution_ = newResolution;\n}\n} // namespace OBS\n"
  },
  {
    "path": "frontend/models/SceneCollection.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Patrick Heyer <PatTheMav@users.noreply.github.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"Rect.hpp\"\n\n#include <string>\n#include <filesystem>\n\nnamespace OBS {\n\nenum class SceneCoordinateMode { Invalid, Absolute, Relative };\n\nclass SceneCollection {\nprivate:\n\tstd::string name_;\n\tstd::filesystem::path filePath_;\n\n\tSceneCoordinateMode coordinateMode_ = SceneCoordinateMode::Relative;\n\tRect migrationResolution_;\n\npublic:\n\tSceneCollection() = default;\n\tSceneCollection(const std::string &name, const std::filesystem::path &filePath)\n\t\t: name_(name),\n\t\t  filePath_(filePath) {};\n\n\tstd::string getName() const;\n\tstd::string getFileName() const;\n\tstd::string getFileStem() const;\n\tstd::filesystem::path getFilePath() const;\n\tstd::string getFilePathString() const;\n\n\tSceneCoordinateMode getCoordinateMode() const;\n\tvoid setCoordinateMode(SceneCoordinateMode newMode);\n\n\tRect getMigrationResolution() const;\n\tvoid setMigrationResolution(Rect newResolution);\n\ttemplate<typename widthType, typename heightType>\n\tvoid setMigrationResolution(widthType width, heightType height)\n\t{\n\t\tthis->migrationResolution_ = Rect(width, height);\n\t}\n\n\tinline int getVersion() const { return (coordinateMode_ == SceneCoordinateMode::Relative) ? 2 : 1; };\n\n\tbool empty() const { return name_.empty() || filePath_.empty(); }\n};\n} // namespace OBS\n"
  },
  {
    "path": "frontend/oauth/Auth.cpp",
    "content": "#include \"Auth.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include \"moc_Auth.cpp\"\n\nstruct AuthInfo {\n\tAuth::Def def;\n\tAuth::create_cb create;\n};\n\nstatic std::vector<AuthInfo> authDefs;\n\nvoid Auth::RegisterAuth(const Def &d, create_cb create)\n{\n\tAuthInfo info = {d, create};\n\tauthDefs.push_back(info);\n}\n\nstd::shared_ptr<Auth> Auth::Create(const std::string &service)\n{\n\tfor (auto &a : authDefs) {\n\t\tif (service.find(a.def.service) != std::string::npos) {\n\t\t\treturn a.create();\n\t\t}\n\t}\n\n\treturn nullptr;\n}\n\nAuth::Type Auth::AuthType(const std::string &service)\n{\n\tfor (auto &a : authDefs) {\n\t\tif (service.find(a.def.service) != std::string::npos) {\n\t\t\treturn a.def.type;\n\t\t}\n\t}\n\n\treturn Type::None;\n}\n\nbool Auth::External(const std::string &service)\n{\n\tfor (auto &a : authDefs) {\n\t\tif (service.find(a.def.service) != std::string::npos) {\n\t\t\treturn a.def.externalOAuth;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid Auth::Load()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tconst char *typeStr = config_get_string(main->Config(), \"Auth\", \"Type\");\n\tif (!typeStr)\n\t\ttypeStr = \"\";\n\n\tmain->auth = Create(typeStr);\n\tif (main->auth) {\n\t\tif (main->auth->LoadInternal()) {\n\t\t\tmain->auth->LoadUI();\n\t\t\tmain->SetBroadcastFlowEnabled(main->auth->broadcastFlow());\n\t\t}\n\t} else {\n\t\tmain->SetBroadcastFlowEnabled(false);\n\t}\n}\n\nvoid Auth::Save()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tAuth *auth = main->auth.get();\n\tif (!auth) {\n\t\tif (config_has_user_value(main->Config(), \"Auth\", \"Type\")) {\n\t\t\tconfig_remove_value(main->Config(), \"Auth\", \"Type\");\n\t\t\tconfig_save_safe(main->Config(), \"tmp\", nullptr);\n\t\t}\n\t\treturn;\n\t}\n\n\tconfig_set_string(main->Config(), \"Auth\", \"Type\", auth->service());\n\tauth->SaveInternal();\n\tconfig_save_safe(main->Config(), \"tmp\", nullptr);\n}\n"
  },
  {
    "path": "frontend/oauth/Auth.hpp",
    "content": "#pragma once\n\n#include <QObject>\n\nclass Auth : public QObject {\n\tQ_OBJECT\n\nprotected:\n\tvirtual void SaveInternal() = 0;\n\tvirtual bool LoadInternal() = 0;\n\n\tbool firstLoad = true;\n\n\tstruct ErrorInfo {\n\t\tstd::string message;\n\t\tstd::string error;\n\n\t\tErrorInfo(std::string message_, std::string error_) : message(message_), error(error_) {}\n\t};\n\npublic:\n\tenum class Type {\n\t\tNone,\n\t\tOAuth_StreamKey,\n\t\tOAuth_LinkedAccount,\n\t};\n\n\tstruct Def {\n\t\tstd::string service;\n\t\tType type;\n\t\tbool externalOAuth;\n\t\tbool usesBroadcastFlow;\n\t};\n\n\ttypedef std::function<std::shared_ptr<Auth>()> create_cb;\n\n\tinline Auth(const Def &d) : def(d) {}\n\tvirtual ~Auth() {}\n\n\tinline Type type() const { return def.type; }\n\tinline const char *service() const { return def.service.c_str(); }\n\tinline bool external() const { return def.externalOAuth; }\n\tinline bool broadcastFlow() const { return def.usesBroadcastFlow; }\n\n\tvirtual void LoadUI() {}\n\n\tvirtual void OnStreamConfig() {}\n\n\tstatic std::shared_ptr<Auth> Create(const std::string &service);\n\tstatic Type AuthType(const std::string &service);\n\tstatic bool External(const std::string &service);\n\tstatic void Load();\n\tstatic void Save();\n\nprotected:\n\tstatic void RegisterAuth(const Def &d, create_cb create);\n\nprivate:\n\tDef def;\n};\n"
  },
  {
    "path": "frontend/oauth/AuthListener.cpp",
    "content": "#include \"AuthListener.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QRegularExpression>\n#include <QTcpServer>\n#include <QTcpSocket>\n\n#include \"moc_AuthListener.cpp\"\n\n#define LOGO_URL \"https://obsproject.com/assets/images/new_icon_small-r.png\"\n\nstatic const QString serverResponseHeader = QStringLiteral(\"HTTP/1.0 200 OK\\n\"\n\t\t\t\t\t\t\t   \"Connection: close\\n\"\n\t\t\t\t\t\t\t   \"Content-Type: text/html; charset=UTF-8\\n\"\n\t\t\t\t\t\t\t   \"Server: OBS Studio\\n\"\n\t\t\t\t\t\t\t   \"\\n\"\n\t\t\t\t\t\t\t   \"<html><head><title>OBS Studio\"\n\t\t\t\t\t\t\t   \"</title></head>\");\n\nstatic const QString responseTemplate = \"<center>\"\n\t\t\t\t\t\"<img src=\\\"\" LOGO_URL\n\t\t\t\t\t\"\\\" alt=\\\"OBS\\\" class=\\\"center\\\"  height=\\\"60\\\" width=\\\"60\\\">\"\n\t\t\t\t\t\"</center>\"\n\t\t\t\t\t\"<center><p style=\\\"font-family:verdana; font-size:13pt\\\">%1</p></center>\";\n\nAuthListener::AuthListener(QObject *parent) : QObject(parent)\n{\n\tserver = new QTcpServer(this);\n\tconnect(server, &QTcpServer::newConnection, this, &AuthListener::NewConnection);\n\tif (!server->listen(QHostAddress::LocalHost, 0)) {\n\t\tblog(LOG_DEBUG, \"Server could not start\");\n\t\temit fail();\n\t} else {\n\t\tblog(LOG_DEBUG, \"Server started at port %d\", server->serverPort());\n\t}\n}\n\nquint16 AuthListener::GetPort()\n{\n\treturn server ? server->serverPort() : 0;\n}\n\nvoid AuthListener::SetState(QString state)\n{\n\tthis->state = state;\n}\n\nvoid AuthListener::NewConnection()\n{\n\tQTcpSocket *socket = server->nextPendingConnection();\n\tif (socket) {\n\t\tconnect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);\n\t\tconnect(socket, &QTcpSocket::readyRead, socket, [&, socket]() {\n\t\t\tQByteArray buffer;\n\t\t\twhile (socket->bytesAvailable() > 0) {\n\t\t\t\tbuffer.append(socket->readAll());\n\t\t\t}\n\t\t\tsocket->write(QT_TO_UTF8(serverResponseHeader));\n\t\t\tQString redirect = QString::fromLatin1(buffer);\n\t\t\tblog(LOG_DEBUG, \"redirect: %s\", QT_TO_UTF8(redirect));\n\n\t\t\tQRegularExpression re_state(\"(&|\\\\?)state=(?<state>[^&]+)\");\n\t\t\tQRegularExpression re_code(\"(&|\\\\?)code=(?<code>[^&]+)\");\n\n\t\t\tQRegularExpressionMatch match = re_state.match(redirect);\n\n\t\t\tQString code;\n\n\t\t\tif (match.hasMatch()) {\n\t\t\t\tif (state == match.captured(\"state\")) {\n\t\t\t\t\tmatch = re_code.match(redirect);\n\t\t\t\t\tif (!match.hasMatch())\n\t\t\t\t\t\tblog(LOG_DEBUG, \"no 'code' \"\n\t\t\t\t\t\t\t\t\"in server \"\n\t\t\t\t\t\t\t\t\"redirect\");\n\n\t\t\t\t\tcode = match.captured(\"code\");\n\t\t\t\t} else {\n\t\t\t\t\tblog(LOG_WARNING, \"state mismatch \"\n\t\t\t\t\t\t\t  \"while handling \"\n\t\t\t\t\t\t\t  \"redirect\");\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tblog(LOG_DEBUG, \"no 'state' in \"\n\t\t\t\t\t\t\"server redirect\");\n\t\t\t}\n\n\t\t\tif (code.isEmpty()) {\n\t\t\t\tauto data = responseTemplate.arg(QTStr(\"YouTube.Auth.NoCode\"));\n\t\t\t\tsocket->write(QT_TO_UTF8(data));\n\t\t\t\temit fail();\n\t\t\t} else {\n\t\t\t\tauto data = responseTemplate.arg(QTStr(\"YouTube.Auth.Ok\"));\n\t\t\t\tsocket->write(QT_TO_UTF8(data));\n\t\t\t\temit ok(code);\n\t\t\t}\n\t\t\tsocket->flush();\n\t\t\tsocket->close();\n\t\t});\n\t} else {\n\t\temit fail();\n\t}\n}\n"
  },
  {
    "path": "frontend/oauth/AuthListener.hpp",
    "content": "#pragma once\n\n#include <QObject>\n\nclass QTcpServer;\n\nclass AuthListener : public QObject {\n\tQ_OBJECT\n\n\tQTcpServer *server;\n\tQString state;\n\nsignals:\n\tvoid ok(const QString &code);\n\tvoid fail();\n\nprotected:\n\tvoid NewConnection();\n\npublic:\n\texplicit AuthListener(QObject *parent = 0);\n\tquint16 GetPort();\n\tvoid SetState(QString state);\n};\n"
  },
  {
    "path": "frontend/oauth/OAuth.cpp",
    "content": "#include \"OAuth.hpp\"\n\n#include <utility/RemoteTextThread.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QMessageBox>\n#include <json11.hpp>\n\n#include \"moc_OAuth.cpp\"\n\nusing namespace json11;\n\nstruct OAuthInfo {\n\tAuth::Def def;\n\tOAuth::login_cb login;\n\tOAuth::delete_cookies_cb delete_cookies;\n};\n\nstatic std::vector<OAuthInfo> loginCBs;\nvoid OAuth::RegisterOAuth(const Def &d, create_cb create, login_cb login, delete_cookies_cb delete_cookies)\n{\n\tOAuthInfo info = {d, login, delete_cookies};\n\tloginCBs.push_back(info);\n\tRegisterAuth(d, create);\n}\n\nstd::shared_ptr<Auth> OAuth::Login(QWidget *parent, const std::string &service)\n{\n\tfor (auto &a : loginCBs) {\n\t\tif (service.find(a.def.service) != std::string::npos) {\n\t\t\treturn a.login(parent, service);\n\t\t}\n\t}\n\n\treturn nullptr;\n}\n\nvoid OAuth::DeleteCookies(const std::string &service)\n{\n\tfor (auto &a : loginCBs) {\n\t\tif (service.find(a.def.service) != std::string::npos) {\n\t\t\ta.delete_cookies();\n\t\t}\n\t}\n}\n\nvoid OAuth::SaveInternal()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tconfig_set_string(main->Config(), service(), \"RefreshToken\", refresh_token.c_str());\n\tconfig_set_string(main->Config(), service(), \"Token\", token.c_str());\n\tconfig_set_uint(main->Config(), service(), \"ExpireTime\", expire_time);\n\tconfig_set_int(main->Config(), service(), \"ScopeVer\", currentScopeVer);\n}\n\nstatic inline std::string get_config_str(OBSBasic *main, const char *section, const char *name)\n{\n\tconst char *val = config_get_string(main->Config(), section, name);\n\treturn val ? val : \"\";\n}\n\nbool OAuth::LoadInternal()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\trefresh_token = get_config_str(main, service(), \"RefreshToken\");\n\ttoken = get_config_str(main, service(), \"Token\");\n\texpire_time = config_get_uint(main->Config(), service(), \"ExpireTime\");\n\tcurrentScopeVer = (int)config_get_int(main->Config(), service(), \"ScopeVer\");\n\treturn implicit ? !token.empty() : !refresh_token.empty();\n}\n\nbool OAuth::TokenExpired()\n{\n\tif (token.empty())\n\t\treturn true;\n\tif ((uint64_t)time(nullptr) > expire_time - 5)\n\t\treturn true;\n\treturn false;\n}\n\nbool OAuth::GetToken(const char *url, const std::string &client_id, const std::string &secret,\n\t\t     const std::string &redirect_uri, int scope_ver, const std::string &auth_code, bool retry)\n{\n\treturn GetTokenInternal(url, client_id, secret, redirect_uri, scope_ver, auth_code, retry);\n}\n\nbool OAuth::GetToken(const char *url, const std::string &client_id, int scope_ver, const std::string &auth_code,\n\t\t     bool retry)\n{\n\treturn GetTokenInternal(url, client_id, {}, {}, scope_ver, auth_code, retry);\n}\n\nbool OAuth::GetTokenInternal(const char *url, const std::string &client_id, const std::string &secret,\n\t\t\t     const std::string &redirect_uri, int scope_ver, const std::string &auth_code, bool retry)\ntry {\n\tstd::string output;\n\tstd::string error;\n\tstd::string desc;\n\n\tif (currentScopeVer > 0 && currentScopeVer < scope_ver) {\n\t\tif (RetryLogin()) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\tQString title = QTStr(\"Auth.InvalidScope.Title\");\n\t\t\tQString text = QTStr(\"Auth.InvalidScope.Text\").arg(service());\n\n\t\t\tQMessageBox::warning(OBSBasic::Get(), title, text);\n\t\t}\n\t}\n\n\tif (auth_code.empty() && !TokenExpired()) {\n\t\treturn true;\n\t}\n\n\tstd::string post_data;\n\tpost_data += \"action=redirect&client_id=\";\n\tpost_data += client_id;\n\tif (!secret.empty()) {\n\t\tpost_data += \"&client_secret=\";\n\t\tpost_data += secret;\n\t}\n\tif (!redirect_uri.empty()) {\n\t\tpost_data += \"&redirect_uri=\";\n\t\tpost_data += redirect_uri;\n\t}\n\n\tif (!auth_code.empty()) {\n\t\tpost_data += \"&grant_type=authorization_code&code=\";\n\t\tpost_data += auth_code;\n\t} else {\n\t\tpost_data += \"&grant_type=refresh_token&refresh_token=\";\n\t\tpost_data += refresh_token;\n\t}\n\n\tbool success = false;\n\n\tauto func = [&]() {\n\t\tsuccess = GetRemoteFile(url, output, error, nullptr, \"application/x-www-form-urlencoded\", \"\",\n\t\t\t\t\tpost_data.c_str(), std::vector<std::string>(), nullptr, 5);\n\t};\n\n\tExecThreadedWithoutBlocking(func, QTStr(\"Auth.Authing.Title\"), QTStr(\"Auth.Authing.Text\").arg(service()));\n\tif (!success || output.empty())\n\t\tthrow ErrorInfo(\"Failed to get token from remote\", error);\n\n\tJson json = Json::parse(output, error);\n\tif (!error.empty())\n\t\tthrow ErrorInfo(\"Failed to parse json\", error);\n\n\t/* -------------------------- */\n\t/* error handling             */\n\n\terror = json[\"error\"].string_value();\n\tif (!retry && error == \"invalid_grant\") {\n\t\tif (RetryLogin()) {\n\t\t\treturn true;\n\t\t}\n\t}\n\tif (!error.empty())\n\t\tthrow ErrorInfo(error, json[\"error_description\"].string_value());\n\n\t/* -------------------------- */\n\t/* success!                   */\n\n\texpire_time = (uint64_t)time(nullptr) + json[\"expires_in\"].int_value();\n\ttoken = json[\"access_token\"].string_value();\n\tif (token.empty())\n\t\tthrow ErrorInfo(\"Failed to get token from remote\", error);\n\n\tif (!auth_code.empty()) {\n\t\trefresh_token = json[\"refresh_token\"].string_value();\n\t\tif (refresh_token.empty())\n\t\t\tthrow ErrorInfo(\"Failed to get refresh token from \"\n\t\t\t\t\t\"remote\",\n\t\t\t\t\terror);\n\n\t\tcurrentScopeVer = scope_ver;\n\t}\n\n\treturn true;\n\n} catch (ErrorInfo &info) {\n\tif (!retry) {\n\t\tQString title = QTStr(\"Auth.AuthFailure.Title\");\n\t\tQString text = QTStr(\"Auth.AuthFailure.Text\").arg(service(), info.message.c_str(), info.error.c_str());\n\n\t\tQMessageBox::warning(OBSBasic::Get(), title, text);\n\t}\n\n\tblog(LOG_WARNING, \"%s: %s: %s\", __FUNCTION__, info.message.c_str(), info.error.c_str());\n\treturn false;\n}\n\nvoid OAuthStreamKey::OnStreamConfig()\n{\n\tif (key_.empty())\n\t\treturn;\n\n\tOBSBasic *main = OBSBasic::Get();\n\tobs_service_t *service = main->GetService();\n\n\tOBSDataAutoRelease settings = obs_service_get_settings(service);\n\n\tbool bwtest = obs_data_get_bool(settings, \"bwtest\");\n\n\tif (bwtest && strcmp(this->service(), \"Twitch\") == 0)\n\t\tobs_data_set_string(settings, \"key\", (key_ + \"?bandwidthtest=true\").c_str());\n\telse\n\t\tobs_data_set_string(settings, \"key\", key_.c_str());\n\n\tobs_service_update(service, settings);\n}\n"
  },
  {
    "path": "frontend/oauth/OAuth.hpp",
    "content": "#pragma once\n\n#include \"Auth.hpp\"\n\nclass OAuth : public Auth {\n\tQ_OBJECT\n\npublic:\n\tinline OAuth(const Def &d) : Auth(d) {}\n\n\ttypedef std::function<std::shared_ptr<Auth>(QWidget *, const std::string &service_name)> login_cb;\n\ttypedef std::function<void()> delete_cookies_cb;\n\n\tstatic std::shared_ptr<Auth> Login(QWidget *parent, const std::string &service);\n\tstatic void DeleteCookies(const std::string &service);\n\n\tstatic void RegisterOAuth(const Def &d, create_cb create, login_cb login, delete_cookies_cb delete_cookies);\n\nprotected:\n\tstd::string refresh_token;\n\tstd::string token;\n\tbool implicit = false;\n\tuint64_t expire_time = 0;\n\tint currentScopeVer = 0;\n\n\tvirtual void SaveInternal() override;\n\tvirtual bool LoadInternal() override;\n\n\tvirtual bool RetryLogin() = 0;\n\tbool TokenExpired();\n\tbool GetToken(const char *url, const std::string &client_id, int scope_ver,\n\t\t      const std::string &auth_code = std::string(), bool retry = false);\n\tbool GetToken(const char *url, const std::string &client_id, const std::string &secret,\n\t\t      const std::string &redirect_uri, int scope_ver, const std::string &auth_code, bool retry);\n\nprivate:\n\tbool GetTokenInternal(const char *url, const std::string &client_id, const std::string &secret,\n\t\t\t      const std::string &redirect_uri, int scope_ver, const std::string &auth_code, bool retry);\n};\n\nclass OAuthStreamKey : public OAuth {\n\tQ_OBJECT\n\nprotected:\n\tstd::string key_;\n\npublic:\n\tinline OAuthStreamKey(const Def &d) : OAuth(d) {}\n\n\tinline const std::string &key() const { return key_; }\n\n\tvirtual void OnStreamConfig() override;\n};\n"
  },
  {
    "path": "frontend/oauth/RestreamAuth.cpp",
    "content": "#include \"RestreamAuth.hpp\"\n\n#include <dialogs/OAuthLogin.hpp>\n#include <docks/BrowserDock.hpp>\n#include <utility/RemoteTextThread.hpp>\n#include <utility/obf.h>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n#include <ui-config.h>\n\n#include <json11.hpp>\n\n#include \"moc_RestreamAuth.cpp\"\n\nusing namespace json11;\n\n/* ------------------------------------------------------------------------- */\n\n#define RESTREAM_AUTH_URL OAUTH_BASE_URL \"v1/restream/redirect\"\n#define RESTREAM_TOKEN_URL OAUTH_BASE_URL \"v1/restream/token\"\n#define RESTREAM_STREAMKEY_URL \"https://api.restream.io/v2/user/streamKey\"\n#define RESTREAM_SCOPE_VERSION 1\n\n#define RESTREAM_CHAT_DOCK_NAME \"restreamChat\"\n#define RESTREAM_INFO_DOCK_NAME \"restreamInfo\"\n#define RESTREAM_CHANNELS_DOCK_NAME \"restreamChannel\"\n\nstatic Auth::Def restreamDef = {\"Restream\", Auth::Type::OAuth_StreamKey};\n\n/* ------------------------------------------------------------------------- */\n\nRestreamAuth::RestreamAuth(const Def &d) : OAuthStreamKey(d) {}\n\nRestreamAuth::~RestreamAuth()\n{\n\tif (!uiLoaded)\n\t\treturn;\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tmain->RemoveDockWidget(RESTREAM_CHAT_DOCK_NAME);\n\tmain->RemoveDockWidget(RESTREAM_INFO_DOCK_NAME);\n\tmain->RemoveDockWidget(RESTREAM_CHANNELS_DOCK_NAME);\n}\n\nbool RestreamAuth::GetChannelInfo()\ntry {\n\tstd::string client_id = RESTREAM_CLIENTID;\n\tdeobfuscate_str(&client_id[0], RESTREAM_HASH);\n\n\tif (!GetToken(RESTREAM_TOKEN_URL, client_id, RESTREAM_SCOPE_VERSION))\n\t\treturn false;\n\tif (token.empty())\n\t\treturn false;\n\tif (!key_.empty())\n\t\treturn true;\n\n\tstd::string auth;\n\tauth += \"Authorization: Bearer \";\n\tauth += token;\n\n\tstd::vector<std::string> headers;\n\theaders.push_back(std::string(\"Client-ID: \") + client_id);\n\theaders.push_back(std::move(auth));\n\n\tstd::string output;\n\tstd::string error;\n\tJson json;\n\tbool success;\n\n\tauto func = [&]() {\n\t\tsuccess = GetRemoteFile(RESTREAM_STREAMKEY_URL, output, error, nullptr, \"application/json\", \"\", nullptr,\n\t\t\t\t\theaders, nullptr, 5);\n\t};\n\n\tExecThreadedWithoutBlocking(func, QTStr(\"Auth.LoadingChannel.Title\"),\n\t\t\t\t    QTStr(\"Auth.LoadingChannel.Text\").arg(service()));\n\tif (!success || output.empty())\n\t\tthrow ErrorInfo(\"Failed to get stream key from remote\", error);\n\n\tjson = Json::parse(output, error);\n\tif (!error.empty())\n\t\tthrow ErrorInfo(\"Failed to parse json\", error);\n\n\terror = json[\"error\"].string_value();\n\tif (!error.empty())\n\t\tthrow ErrorInfo(error, json[\"error_description\"].string_value());\n\n\tkey_ = json[\"streamKey\"].string_value();\n\n\treturn true;\n} catch (ErrorInfo info) {\n\tQString title = QTStr(\"Auth.ChannelFailure.Title\");\n\tQString text = QTStr(\"Auth.ChannelFailure.Text\").arg(service(), info.message.c_str(), info.error.c_str());\n\n\tQMessageBox::warning(OBSBasic::Get(), title, text);\n\n\tblog(LOG_WARNING, \"%s: %s: %s\", __FUNCTION__, info.message.c_str(), info.error.c_str());\n\treturn false;\n}\n\nvoid RestreamAuth::SaveInternal()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tconfig_set_string(main->Config(), service(), \"DockState\", main->saveState().toBase64().constData());\n\tOAuthStreamKey::SaveInternal();\n}\n\nstatic inline std::string get_config_str(OBSBasic *main, const char *section, const char *name)\n{\n\tconst char *val = config_get_string(main->Config(), section, name);\n\treturn val ? val : \"\";\n}\n\nbool RestreamAuth::LoadInternal()\n{\n\tfirstLoad = false;\n\treturn OAuthStreamKey::LoadInternal();\n}\n\nvoid RestreamAuth::LoadUI()\n{\n\tif (!cef)\n\t\treturn;\n\tif (uiLoaded)\n\t\treturn;\n\tif (!GetChannelInfo())\n\t\treturn;\n\n\tOBSBasic::InitBrowserPanelSafeBlock();\n\tOBSBasic *main = OBSBasic::Get();\n\n\tQCefWidget *browser;\n\tstd::string url;\n\tstd::string script;\n\n\t/* ----------------------------------- */\n\n\turl = \"https://restream.io/chat-application\";\n\n\tQSize size = main->frameSize();\n\tQPoint pos = main->pos();\n\n\tBrowserDock *chat = new BrowserDock(QTStr(\"Auth.Chat\"));\n\tchat->setObjectName(RESTREAM_CHAT_DOCK_NAME);\n\tchat->resize(420, 600);\n\tchat->setMinimumSize(200, 300);\n\tchat->setWindowTitle(QTStr(\"Auth.Chat\"));\n\tchat->setAllowedAreas(Qt::AllDockWidgetAreas);\n\n\tbrowser = cef->create_widget(chat, url, panel_cookies);\n\tchat->SetWidget(browser);\n\n\tmain->AddDockWidget(chat, Qt::RightDockWidgetArea);\n\n\t/* ----------------------------------- */\n\n\turl = \"https://restream.io/titles/embed\";\n\n\tBrowserDock *info = new BrowserDock(QTStr(\"Auth.StreamInfo\"));\n\tinfo->setObjectName(RESTREAM_INFO_DOCK_NAME);\n\tinfo->resize(410, 600);\n\tinfo->setMinimumSize(200, 150);\n\tinfo->setWindowTitle(QTStr(\"Auth.StreamInfo\"));\n\tinfo->setAllowedAreas(Qt::AllDockWidgetAreas);\n\n\tbrowser = cef->create_widget(info, url, panel_cookies);\n\tinfo->SetWidget(browser);\n\n\tmain->AddDockWidget(info, Qt::LeftDockWidgetArea);\n\n\t/* ----------------------------------- */\n\n\turl = \"https://restream.io/channel/embed\";\n\n\tBrowserDock *channels = new BrowserDock(QTStr(\"RestreamAuth.Channels\"));\n\tchannels->setObjectName(RESTREAM_CHANNELS_DOCK_NAME);\n\tchannels->resize(410, 600);\n\tchannels->setMinimumSize(410, 300);\n\tchannels->setWindowTitle(QTStr(\"RestreamAuth.Channels\"));\n\tchannels->setAllowedAreas(Qt::AllDockWidgetAreas);\n\n\tbrowser = cef->create_widget(channels, url, panel_cookies);\n\tchannels->SetWidget(browser);\n\n\tmain->AddDockWidget(channels, Qt::LeftDockWidgetArea);\n\n\t/* ----------------------------------- */\n\n\tchat->setFloating(true);\n\tinfo->setFloating(true);\n\tchannels->setFloating(true);\n\n\tchat->move(pos.x() + size.width() - chat->width() - 30, pos.y() + 60);\n\tinfo->move(pos.x() + 20, pos.y() + 60);\n\tchannels->move(pos.x() + 20 + info->width() + 10, pos.y() + 60);\n\n\tif (firstLoad) {\n\t\tchat->setVisible(true);\n\t\tinfo->setVisible(true);\n\t\tchannels->setVisible(true);\n\t} else {\n\t\tconst char *dockStateStr = config_get_string(main->Config(), service(), \"DockState\");\n\t\tQByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr));\n\t\tmain->restoreState(dockState);\n\t}\n\n\tuiLoaded = true;\n}\n\nbool RestreamAuth::RetryLogin()\n{\n\tOAuthLogin login(OBSBasic::Get(), RESTREAM_AUTH_URL, false);\n\tcef->add_popup_whitelist_url(\"about:blank\", &login);\n\tif (login.exec() == QDialog::Rejected) {\n\t\treturn false;\n\t}\n\n\tstd::shared_ptr<RestreamAuth> auth = std::make_shared<RestreamAuth>(restreamDef);\n\n\tstd::string client_id = RESTREAM_CLIENTID;\n\tdeobfuscate_str(&client_id[0], RESTREAM_HASH);\n\n\treturn GetToken(RESTREAM_TOKEN_URL, client_id, RESTREAM_SCOPE_VERSION, QT_TO_UTF8(login.GetCode()), true);\n}\n\nstd::shared_ptr<Auth> RestreamAuth::Login(QWidget *parent, const std::string &)\n{\n\tOAuthLogin login(parent, RESTREAM_AUTH_URL, false);\n\tcef->add_popup_whitelist_url(\"about:blank\", &login);\n\n\tif (login.exec() == QDialog::Rejected) {\n\t\treturn nullptr;\n\t}\n\n\tstd::shared_ptr<RestreamAuth> auth = std::make_shared<RestreamAuth>(restreamDef);\n\n\tstd::string client_id = RESTREAM_CLIENTID;\n\tdeobfuscate_str(&client_id[0], RESTREAM_HASH);\n\n\tif (!auth->GetToken(RESTREAM_TOKEN_URL, client_id, RESTREAM_SCOPE_VERSION, QT_TO_UTF8(login.GetCode()))) {\n\t\treturn nullptr;\n\t}\n\n\tstd::string error;\n\tif (auth->GetChannelInfo()) {\n\t\treturn auth;\n\t}\n\n\treturn nullptr;\n}\n\nstatic std::shared_ptr<Auth> CreateRestreamAuth()\n{\n\treturn std::make_shared<RestreamAuth>(restreamDef);\n}\n\nstatic void DeleteCookies()\n{\n\tif (panel_cookies) {\n\t\tpanel_cookies->DeleteCookies(\"restream.io\", std::string());\n\t}\n}\n\nvoid RegisterRestreamAuth()\n{\n#if !defined(__APPLE__) && !defined(_WIN32)\n\tif (QApplication::platformName().contains(\"wayland\"))\n\t\treturn;\n#endif\n\n\tOAuth::RegisterOAuth(restreamDef, CreateRestreamAuth, RestreamAuth::Login, DeleteCookies);\n}\n"
  },
  {
    "path": "frontend/oauth/RestreamAuth.hpp",
    "content": "#pragma once\n\n#include \"OAuth.hpp\"\n\nclass RestreamAuth : public OAuthStreamKey {\n\tQ_OBJECT\n\n\tbool uiLoaded = false;\n\n\tvirtual bool RetryLogin() override;\n\n\tvirtual void SaveInternal() override;\n\tvirtual bool LoadInternal() override;\n\n\tbool GetChannelInfo();\n\n\tvirtual void LoadUI() override;\n\npublic:\n\tRestreamAuth(const Def &d);\n\t~RestreamAuth();\n\n\tstatic std::shared_ptr<Auth> Login(QWidget *parent, const std::string &service_name);\n};\n"
  },
  {
    "path": "frontend/oauth/TwitchAuth.cpp",
    "content": "#include \"TwitchAuth.hpp\"\n\n#include <dialogs/OAuthLogin.hpp>\n#include <docks/BrowserDock.hpp>\n#include <utility/RemoteTextThread.hpp>\n#include <utility/obf.h>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n#include <ui-config.h>\n\n#include <QUuid>\n\n#include \"moc_TwitchAuth.cpp\"\n\nusing namespace json11;\n\n/* ------------------------------------------------------------------------- */\n\n#define TWITCH_AUTH_URL OAUTH_BASE_URL \"v1/twitch/redirect\"\n#define TWITCH_TOKEN_URL OAUTH_BASE_URL \"v1/twitch/token\"\n\n#define TWITCH_SCOPE_VERSION 1\n\n#define TWITCH_CHAT_DOCK_NAME \"twitchChat\"\n#define TWITCH_INFO_DOCK_NAME \"twitchInfo\"\n#define TWITCH_STATS_DOCK_NAME \"twitchStats\"\n#define TWITCH_FEED_DOCK_NAME \"twitchFeed\"\n\nstatic Auth::Def twitchDef = {\"Twitch\", Auth::Type::OAuth_StreamKey};\n\n/* ------------------------------------------------------------------------- */\n\nTwitchAuth::TwitchAuth(const Def &d) : OAuthStreamKey(d)\n{\n\tif (!cef)\n\t\treturn;\n\n\tcef->add_popup_whitelist_url(\"https://twitch.tv/popout/frankerfacez/chat?ffz-settings\", this);\n\n\t/* enables javascript-based popups.  basically bttv popups */\n\tcef->add_popup_whitelist_url(\"about:blank#blocked\", this);\n\n\tuiLoadTimer.setSingleShot(true);\n\tuiLoadTimer.setInterval(500);\n\tconnect(&uiLoadTimer, &QTimer::timeout, this, &TwitchAuth::TryLoadSecondaryUIPanes);\n}\n\nTwitchAuth::~TwitchAuth()\n{\n\tif (!uiLoaded)\n\t\treturn;\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tmain->RemoveDockWidget(TWITCH_CHAT_DOCK_NAME);\n\tmain->RemoveDockWidget(TWITCH_INFO_DOCK_NAME);\n\tmain->RemoveDockWidget(TWITCH_STATS_DOCK_NAME);\n\tmain->RemoveDockWidget(TWITCH_FEED_DOCK_NAME);\n}\n\nbool TwitchAuth::MakeApiRequest(const char *path, Json &json_out)\n{\n\tstd::string client_id = TWITCH_CLIENTID;\n\tdeobfuscate_str(&client_id[0], TWITCH_HASH);\n\n\tstd::string url = \"https://api.twitch.tv/helix/\";\n\turl += std::string(path);\n\n\tstd::vector<std::string> headers;\n\theaders.push_back(std::string(\"Client-ID: \") + client_id);\n\theaders.push_back(std::string(\"Authorization: Bearer \") + token);\n\n\tstd::string output;\n\tstd::string error;\n\tlong error_code = 0;\n\n\tbool success = false;\n\n\tauto func = [&]() {\n\t\tsuccess = GetRemoteFile(url.c_str(), output, error, &error_code, \"application/json\", \"\", nullptr,\n\t\t\t\t\theaders, nullptr, 5);\n\t};\n\n\tExecThreadedWithoutBlocking(func, QTStr(\"Auth.LoadingChannel.Title\"),\n\t\t\t\t    QTStr(\"Auth.LoadingChannel.Text\").arg(service()));\n\tif (error_code == 403) {\n\t\tOBSMessageBox::warning(OBSBasic::Get(), Str(\"TwitchAuth.TwoFactorFail.Title\"),\n\t\t\t\t       Str(\"TwitchAuth.TwoFactorFail.Text\"), true);\n\t\tblog(LOG_WARNING, \"%s: %s. API response: %s\", __FUNCTION__,\n\t\t     \"Got 403 from Twitch, user probably does not \"\n\t\t     \"have two-factor authentication enabled on \"\n\t\t     \"their account\",\n\t\t     output.empty() ? \"<none>\" : output.c_str());\n\t\treturn false;\n\t}\n\n\tif (!success || output.empty())\n\t\tthrow ErrorInfo(\"Failed to get text from remote\", error);\n\n\tjson_out = Json::parse(output, error);\n\tif (!error.empty())\n\t\tthrow ErrorInfo(\"Failed to parse json\", error);\n\n\terror = json_out[\"error\"].string_value();\n\tif (!error.empty())\n\t\tthrow ErrorInfo(error, json_out[\"message\"].string_value());\n\n\treturn true;\n}\n\nbool TwitchAuth::GetChannelInfo()\ntry {\n\tstd::string client_id = TWITCH_CLIENTID;\n\tdeobfuscate_str(&client_id[0], TWITCH_HASH);\n\n\tif (!GetToken(TWITCH_TOKEN_URL, client_id, TWITCH_SCOPE_VERSION))\n\t\treturn false;\n\tif (token.empty())\n\t\treturn false;\n\tif (!key_.empty())\n\t\treturn true;\n\n\tJson json;\n\tbool success = MakeApiRequest(\"users\", json);\n\n\tif (!success)\n\t\treturn false;\n\n\tname = json[\"data\"][0][\"login\"].string_value();\n\n\tstd::string path = \"streams/key?broadcaster_id=\" + json[\"data\"][0][\"id\"].string_value();\n\tsuccess = MakeApiRequest(path.c_str(), json);\n\tif (!success)\n\t\treturn false;\n\n\tkey_ = json[\"data\"][0][\"stream_key\"].string_value();\n\n\treturn true;\n} catch (ErrorInfo info) {\n\tQString title = QTStr(\"Auth.ChannelFailure.Title\");\n\tQString text = QTStr(\"Auth.ChannelFailure.Text\").arg(service(), info.message.c_str(), info.error.c_str());\n\n\tQMessageBox::warning(OBSBasic::Get(), title, text);\n\n\tblog(LOG_WARNING, \"%s: %s: %s\", __FUNCTION__, info.message.c_str(), info.error.c_str());\n\treturn false;\n}\n\nvoid TwitchAuth::SaveInternal()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tconfig_set_string(main->Config(), service(), \"Name\", name.c_str());\n\tconfig_set_string(main->Config(), service(), \"UUID\", uuid.c_str());\n\n\tif (uiLoaded) {\n\t\tconfig_set_string(main->Config(), service(), \"DockState\", main->saveState().toBase64().constData());\n\t}\n\tOAuthStreamKey::SaveInternal();\n}\n\nstatic inline std::string get_config_str(OBSBasic *main, const char *section, const char *name)\n{\n\tconst char *val = config_get_string(main->Config(), section, name);\n\treturn val ? val : \"\";\n}\n\nbool TwitchAuth::LoadInternal()\n{\n\tif (!cef)\n\t\treturn false;\n\n\tOBSBasic *main = OBSBasic::Get();\n\tname = get_config_str(main, service(), \"Name\");\n\tuuid = get_config_str(main, service(), \"UUID\");\n\n\tfirstLoad = false;\n\treturn OAuthStreamKey::LoadInternal();\n}\n\nstatic const char *ffz_script = \"\\\nvar ffz = document.createElement('script');\\\nffz.setAttribute('src','https://cdn.frankerfacez.com/script/script.min.js');\\\ndocument.head.appendChild(ffz);\";\n\nstatic const char *bttv_script = \"\\\nlocalStorage.setItem('bttv_clickTwitchEmotes', true);\\\nlocalStorage.setItem('bttv_darkenedMode', true);\\\nlocalStorage.setItem('bttv_bttvGIFEmotes', true);\\\nvar bttv = document.createElement('script');\\\nbttv.setAttribute('src','https://cdn.betterttv.net/betterttv.js');\\\ndocument.head.appendChild(bttv);\";\n\nstatic const char *referrer_script1 = \"\\\nObject.defineProperty(document, 'referrer', {get : function() { return '\";\nstatic const char *referrer_script2 = \"'; }});\";\n\nvoid TwitchAuth::LoadUI()\n{\n\tif (!cef)\n\t\treturn;\n\tif (uiLoaded)\n\t\treturn;\n\tif (!GetChannelInfo())\n\t\treturn;\n\n\tOBSBasic::InitBrowserPanelSafeBlock();\n\tOBSBasic *main = OBSBasic::Get();\n\n\tQCefWidget *browser;\n\tstd::string url;\n\tstd::string script;\n\n\t/* Twitch panels require a UUID, it does not actually need to be unique,\n\t * and is generated client-side.\n\t * It is only for preferences stored in the browser's local store. */\n\tif (uuid.empty()) {\n\t\tQString qtUuid = QUuid::createUuid().toString();\n\t\tqtUuid.replace(QRegularExpression(\"[{}-]\"), \"\");\n\t\tuuid = qtUuid.toStdString();\n\t}\n\n\tstd::string moderation_tools_url;\n\tmoderation_tools_url = \"https://www.twitch.tv/\";\n\tmoderation_tools_url += name;\n\tmoderation_tools_url += \"/dashboard/settings/moderation?no-reload=true\";\n\n\t/* ----------------------------------- */\n\n\turl = \"https://www.twitch.tv/popout/\";\n\turl += name;\n\turl += \"/chat\";\n\n\tQSize size = main->frameSize();\n\tQPoint pos = main->pos();\n\n\tBrowserDock *chat = new BrowserDock(QTStr(\"Auth.Chat\"));\n\tchat->setObjectName(TWITCH_CHAT_DOCK_NAME);\n\tchat->resize(300, 600);\n\tchat->setMinimumSize(200, 300);\n\tchat->setWindowTitle(QTStr(\"Auth.Chat\"));\n\tchat->setAllowedAreas(Qt::AllDockWidgetAreas);\n\n\tbrowser = cef->create_widget(chat, url, panel_cookies);\n\tchat->SetWidget(browser);\n\tcef->add_force_popup_url(moderation_tools_url, chat);\n\n\tif (App()->IsThemeDark()) {\n\t\tscript = \"localStorage.setItem('twilight.theme', 1);\";\n\t} else {\n\t\tscript = \"localStorage.setItem('twilight.theme', 0);\";\n\t}\n\n\tconst int twAddonChoice = config_get_int(main->Config(), service(), \"AddonChoice\");\n\tif (twAddonChoice) {\n\t\tif (twAddonChoice & 0x1)\n\t\t\tscript += bttv_script;\n\t\tif (twAddonChoice & 0x2)\n\t\t\tscript += ffz_script;\n\t}\n\n\tbrowser->setStartupScript(script);\n\n\tmain->AddDockWidget(chat, Qt::RightDockWidgetArea);\n\n\t/* ----------------------------------- */\n\n\tchat->setFloating(true);\n\tchat->move(pos.x() + size.width() - chat->width() - 50, pos.y() + 50);\n\n\tif (firstLoad) {\n\t\tchat->setVisible(true);\n\t} else {\n\t\tconst char *dockStateStr = config_get_string(main->Config(), service(), \"DockState\");\n\t\tQByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr));\n\t\tmain->restoreState(dockState);\n\t}\n\n\tTryLoadSecondaryUIPanes();\n\n\tuiLoaded = true;\n}\n\nvoid TwitchAuth::LoadSecondaryUIPanes()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tQCefWidget *browser;\n\tstd::string url;\n\tstd::string script;\n\n\tQSize size = main->frameSize();\n\tQPoint pos = main->pos();\n\n\tif (App()->IsThemeDark()) {\n\t\tscript = \"localStorage.setItem('twilight.theme', 1);\";\n\t} else {\n\t\tscript = \"localStorage.setItem('twilight.theme', 0);\";\n\t}\n\tscript += referrer_script1;\n\tscript += \"https://www.twitch.tv/\";\n\tscript += name;\n\tscript += \"/dashboard/live\";\n\tscript += referrer_script2;\n\n\tconst int twAddonChoice = config_get_int(main->Config(), service(), \"AddonChoice\");\n\tif (twAddonChoice) {\n\t\tif (twAddonChoice & 0x1)\n\t\t\tscript += bttv_script;\n\t\tif (twAddonChoice & 0x2)\n\t\t\tscript += ffz_script;\n\t}\n\n\t/* ----------------------------------- */\n\n\turl = \"https://dashboard.twitch.tv/popout/u/\";\n\turl += name;\n\turl += \"/stream-manager/edit-stream-info\";\n\n\tBrowserDock *info = new BrowserDock(QTStr(\"Auth.StreamInfo\"));\n\tinfo->setObjectName(TWITCH_INFO_DOCK_NAME);\n\tinfo->resize(300, 650);\n\tinfo->setMinimumSize(200, 300);\n\tinfo->setWindowTitle(QTStr(\"Auth.StreamInfo\"));\n\tinfo->setAllowedAreas(Qt::AllDockWidgetAreas);\n\n\tbrowser = cef->create_widget(info, url, panel_cookies);\n\tinfo->SetWidget(browser);\n\tbrowser->setStartupScript(script);\n\n\tmain->AddDockWidget(info, Qt::RightDockWidgetArea);\n\n\t/* ----------------------------------- */\n\n\turl = \"https://www.twitch.tv/popout/\";\n\turl += name;\n\turl += \"/dashboard/live/stats\";\n\n\tBrowserDock *stats = new BrowserDock(QTStr(\"TwitchAuth.Stats\"));\n\tstats->setObjectName(TWITCH_STATS_DOCK_NAME);\n\tstats->resize(200, 250);\n\tstats->setMinimumSize(200, 150);\n\tstats->setWindowTitle(QTStr(\"TwitchAuth.Stats\"));\n\tstats->setAllowedAreas(Qt::AllDockWidgetAreas);\n\n\tbrowser = cef->create_widget(stats, url, panel_cookies);\n\tstats->SetWidget(browser);\n\tbrowser->setStartupScript(script);\n\n\tmain->AddDockWidget(stats, Qt::RightDockWidgetArea);\n\n\t/* ----------------------------------- */\n\n\turl = \"https://dashboard.twitch.tv/popout/u/\";\n\turl += name;\n\turl += \"/stream-manager/activity-feed\";\n\turl += \"?uuid=\" + uuid;\n\n\tBrowserDock *feed = new BrowserDock(QTStr(\"TwitchAuth.Feed\"));\n\tfeed->setObjectName(TWITCH_FEED_DOCK_NAME);\n\tfeed->resize(300, 650);\n\tfeed->setMinimumSize(200, 300);\n\tfeed->setWindowTitle(QTStr(\"TwitchAuth.Feed\"));\n\tfeed->setAllowedAreas(Qt::AllDockWidgetAreas);\n\n\tbrowser = cef->create_widget(feed, url, panel_cookies);\n\tfeed->SetWidget(browser);\n\tbrowser->setStartupScript(script);\n\n\tmain->AddDockWidget(feed, Qt::RightDockWidgetArea);\n\n\t/* ----------------------------------- */\n\n\tinfo->setFloating(true);\n\tstats->setFloating(true);\n\tfeed->setFloating(true);\n\n\tQSize statSize = stats->frameSize();\n\n\tinfo->move(pos.x() + 50, pos.y() + 50);\n\tstats->move(pos.x() + size.width() / 2 - statSize.width() / 2,\n\t\t    pos.y() + size.height() / 2 - statSize.height() / 2);\n\tfeed->move(pos.x() + 100, pos.y() + 100);\n\n\tif (firstLoad) {\n\t\tinfo->setVisible(true);\n\t\tstats->setVisible(false);\n\t\tfeed->setVisible(false);\n\t} else {\n\t\tuint32_t lastVersion = config_get_int(App()->GetAppConfig(), \"General\", \"LastVersion\");\n\n\t\tif (lastVersion <= MAKE_SEMANTIC_VERSION(23, 0, 2)) {\n\t\t\tfeed->setVisible(false);\n\t\t}\n\n\t\tconst char *dockStateStr = config_get_string(main->Config(), service(), \"DockState\");\n\t\tQByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr));\n\n\t\tif (main->isVisible() || !main->isMaximized())\n\t\t\tmain->restoreState(dockState);\n\t}\n}\n\n/* Twitch.tv has an OAuth for itself.  If we try to load multiple panel pages\n * at once before it's OAuth'ed itself, they will all try to perform the auth\n * process at the same time, get their own request codes, and only the last\n * code will be valid -- so one or more panels are guaranteed to fail.\n *\n * To solve this, we want to load just one panel first (the chat), and then all\n * subsequent panels should only be loaded once we know that Twitch has auth'ed\n * itself (if the cookie \"auth-token\" exists for twitch.tv).\n *\n * This is annoying to deal with. */\nvoid TwitchAuth::TryLoadSecondaryUIPanes()\n{\n\tQPointer<TwitchAuth> this_ = this;\n\n\tauto cb = [this_](bool found) {\n\t\tif (!this_) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!found) {\n\t\t\tQMetaObject::invokeMethod(&this_->uiLoadTimer, \"start\");\n\t\t} else {\n\t\t\tQMetaObject::invokeMethod(this_, \"LoadSecondaryUIPanes\");\n\t\t}\n\t};\n\n\tpanel_cookies->CheckForCookie(\"https://www.twitch.tv\", \"auth-token\", cb);\n}\n\nbool TwitchAuth::RetryLogin()\n{\n\tOAuthLogin login(OBSBasic::Get(), TWITCH_AUTH_URL, false);\n\tif (login.exec() == QDialog::Rejected) {\n\t\treturn false;\n\t}\n\n\tstd::shared_ptr<TwitchAuth> auth = std::make_shared<TwitchAuth>(twitchDef);\n\tstd::string client_id = TWITCH_CLIENTID;\n\tdeobfuscate_str(&client_id[0], TWITCH_HASH);\n\n\treturn GetToken(TWITCH_TOKEN_URL, client_id, TWITCH_SCOPE_VERSION, QT_TO_UTF8(login.GetCode()), true);\n}\n\nstd::shared_ptr<Auth> TwitchAuth::Login(QWidget *parent, const std::string &)\n{\n\tOAuthLogin login(parent, TWITCH_AUTH_URL, false);\n\tif (login.exec() == QDialog::Rejected) {\n\t\treturn nullptr;\n\t}\n\n\tstd::shared_ptr<TwitchAuth> auth = std::make_shared<TwitchAuth>(twitchDef);\n\n\tstd::string client_id = TWITCH_CLIENTID;\n\tdeobfuscate_str(&client_id[0], TWITCH_HASH);\n\n\tif (!auth->GetToken(TWITCH_TOKEN_URL, client_id, TWITCH_SCOPE_VERSION, QT_TO_UTF8(login.GetCode()))) {\n\t\treturn nullptr;\n\t}\n\n\tif (auth->GetChannelInfo()) {\n\t\treturn auth;\n\t}\n\n\treturn nullptr;\n}\n\nstatic std::shared_ptr<Auth> CreateTwitchAuth()\n{\n\treturn std::make_shared<TwitchAuth>(twitchDef);\n}\n\nstatic void DeleteCookies()\n{\n\tif (panel_cookies)\n\t\tpanel_cookies->DeleteCookies(\"twitch.tv\", std::string());\n}\n\nvoid RegisterTwitchAuth()\n{\n#if !defined(__APPLE__) && !defined(_WIN32)\n\tif (QApplication::platformName().contains(\"wayland\"))\n\t\treturn;\n#endif\n\n\tOAuth::RegisterOAuth(twitchDef, CreateTwitchAuth, TwitchAuth::Login, DeleteCookies);\n}\n"
  },
  {
    "path": "frontend/oauth/TwitchAuth.hpp",
    "content": "#pragma once\n\n#include \"OAuth.hpp\"\n\n#include <json11.hpp>\n\n#include <QTimer>\n\nclass TwitchAuth : public OAuthStreamKey {\n\tQ_OBJECT\n\n\tbool uiLoaded = false;\n\n\tstd::string name;\n\tstd::string uuid;\n\n\tvirtual bool RetryLogin() override;\n\n\tvirtual void SaveInternal() override;\n\tvirtual bool LoadInternal() override;\n\n\tbool MakeApiRequest(const char *path, json11::Json &json_out);\n\tbool GetChannelInfo();\n\n\tvirtual void LoadUI() override;\n\npublic:\n\tTwitchAuth(const Def &d);\n\t~TwitchAuth();\n\n\tstatic std::shared_ptr<Auth> Login(QWidget *parent, const std::string &service_name);\n\n\tQTimer uiLoadTimer;\n\npublic slots:\n\tvoid TryLoadSecondaryUIPanes();\n\tvoid LoadSecondaryUIPanes();\n};\n"
  },
  {
    "path": "frontend/oauth/YoutubeAuth.cpp",
    "content": "#include \"YoutubeAuth.hpp\"\n\n#ifdef BROWSER_AVAILABLE\n#include <docks/YouTubeChatDock.hpp>\n#endif\n#include <oauth/AuthListener.hpp>\n#include <utility/YoutubeApiWrappers.hpp>\n#include <utility/obf.h>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n#include <ui-config.h>\n\n#include <QDesktopServices>\n#include <QRandomGenerator>\n\n#include \"moc_YoutubeAuth.cpp\"\n\n#define YOUTUBE_AUTH_URL \"https://accounts.google.com/o/oauth2/v2/auth\"\n#define YOUTUBE_TOKEN_URL \"https://www.googleapis.com/oauth2/v4/token\"\n#define YOUTUBE_SCOPE_VERSION 1\n#define YOUTUBE_API_STATE_LENGTH 32\n#define SECTION_NAME \"YouTube\"\n\n#define YOUTUBE_CHAT_PLACEHOLDER_URL \"https://obsproject.com/placeholders/youtube-chat\"\n#define YOUTUBE_CHAT_POPOUT_URL \"https://www.youtube.com/live_chat?is_popout=1&dark_theme=1&v=%1\"\n\n#define YOUTUBE_CHAT_DOCK_NAME \"ytChat\"\n\nstatic const char allowedChars[] = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\nstatic const int allowedCount = static_cast<int>(sizeof(allowedChars) - 1);\n/* ------------------------------------------------------------------------- */\n\nstatic inline void OpenBrowser(const QString auth_uri)\n{\n\tQUrl url(auth_uri, QUrl::StrictMode);\n\tQDesktopServices::openUrl(url);\n}\n\nstatic void DeleteCookies()\n{\n\tif (panel_cookies) {\n\t\tpanel_cookies->DeleteCookies(\"youtube.com\", \"\");\n\t\tpanel_cookies->DeleteCookies(\"google.com\", \"\");\n\t}\n}\n\nvoid RegisterYoutubeAuth()\n{\n\tfor (auto &service : youtubeServices) {\n\t\tOAuth::RegisterOAuth(\n\t\t\tservice, [service]() { return std::make_shared<YoutubeApiWrappers>(service); },\n\t\t\tYoutubeAuth::Login, DeleteCookies);\n\t}\n}\n\nYoutubeAuth::YoutubeAuth(const Def &d) : OAuthStreamKey(d), section(SECTION_NAME) {}\n\nYoutubeAuth::~YoutubeAuth()\n{\n\tif (!uiLoaded)\n\t\treturn;\n\n#ifdef BROWSER_AVAILABLE\n\tOBSBasic *main = OBSBasic::Get();\n\n\tmain->RemoveDockWidget(YOUTUBE_CHAT_DOCK_NAME);\n\tchat = nullptr;\n#endif\n}\n\nbool YoutubeAuth::RetryLogin()\n{\n\treturn true;\n}\n\nvoid YoutubeAuth::SaveInternal()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tconfig_set_string(main->Config(), service(), \"DockState\", main->saveState().toBase64().constData());\n\n\tconst char *section_name = section.c_str();\n\tconfig_set_string(main->Config(), section_name, \"RefreshToken\", refresh_token.c_str());\n\tconfig_set_string(main->Config(), section_name, \"Token\", token.c_str());\n\tconfig_set_uint(main->Config(), section_name, \"ExpireTime\", expire_time);\n\tconfig_set_int(main->Config(), section_name, \"ScopeVer\", currentScopeVer);\n}\n\nstatic inline std::string get_config_str(OBSBasic *main, const char *section, const char *name)\n{\n\tconst char *val = config_get_string(main->Config(), section, name);\n\treturn val ? val : \"\";\n}\n\nbool YoutubeAuth::LoadInternal()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tconst char *section_name = section.c_str();\n\trefresh_token = get_config_str(main, section_name, \"RefreshToken\");\n\ttoken = get_config_str(main, section_name, \"Token\");\n\texpire_time = config_get_uint(main->Config(), section_name, \"ExpireTime\");\n\tcurrentScopeVer = (int)config_get_int(main->Config(), section_name, \"ScopeVer\");\n\tfirstLoad = false;\n\treturn implicit ? !token.empty() : !refresh_token.empty();\n}\n\nvoid YoutubeAuth::LoadUI()\n{\n\tif (uiLoaded)\n\t\treturn;\n\n#ifdef BROWSER_AVAILABLE\n\tif (!cef)\n\t\treturn;\n\n\tOBSBasic::InitBrowserPanelSafeBlock();\n\tOBSBasic *main = OBSBasic::Get();\n\n\tQCefWidget *browser;\n\n\tQSize size = main->frameSize();\n\tQPoint pos = main->pos();\n\n\tchat = new YoutubeChatDock(QTStr(\"Auth.Chat\"));\n\tchat->setObjectName(YOUTUBE_CHAT_DOCK_NAME);\n\tchat->resize(300, 600);\n\tchat->setMinimumSize(200, 300);\n\tchat->setAllowedAreas(Qt::AllDockWidgetAreas);\n\n\tbrowser = cef->create_widget(chat, YOUTUBE_CHAT_PLACEHOLDER_URL, panel_cookies);\n\n\tchat->SetWidget(browser);\n\tmain->AddDockWidget(chat, Qt::RightDockWidgetArea);\n\n\tchat->setFloating(true);\n\tchat->move(pos.x() + size.width() - chat->width() - 50, pos.y() + 50);\n\n\tif (firstLoad) {\n\t\tchat->setVisible(true);\n\t}\n#endif\n\n\tif (!main->GetYouTubeAppDock()) {\n\t\tmain->NewYouTubeAppDock();\n\t}\n\n\tif (!firstLoad) {\n\t\tconst char *dockStateStr = config_get_string(main->Config(), service(), \"DockState\");\n\t\tQByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr));\n\t\tmain->restoreState(dockState);\n\t}\n\n\tuiLoaded = true;\n}\n\nvoid YoutubeAuth::SetChatId(const QString &chat_id)\n{\n#ifdef BROWSER_AVAILABLE\n\tQString chat_url = QString(YOUTUBE_CHAT_POPOUT_URL).arg(chat_id);\n\n\tif (chat && chat->cefWidget) {\n\t\tchat->cefWidget->setURL(chat_url.toStdString());\n\t}\n#else\n\tUNUSED_PARAMETER(chat_id);\n#endif\n}\n\nvoid YoutubeAuth::ResetChat()\n{\n#ifdef BROWSER_AVAILABLE\n\tif (chat && chat->cefWidget) {\n\t\tchat->cefWidget->setURL(YOUTUBE_CHAT_PLACEHOLDER_URL);\n\t}\n#endif\n}\n\nvoid YoutubeAuth::ReloadChat()\n{\n#ifdef BROWSER_AVAILABLE\n\tif (chat && chat->cefWidget) {\n\t\tchat->cefWidget->reloadPage();\n\t}\n#endif\n}\n\nQString YoutubeAuth::GenerateState()\n{\n\tchar state[YOUTUBE_API_STATE_LENGTH + 1];\n\tQRandomGenerator *rng = QRandomGenerator::system();\n\tint i;\n\n\tfor (i = 0; i < YOUTUBE_API_STATE_LENGTH; i++)\n\t\tstate[i] = allowedChars[rng->bounded(0, allowedCount)];\n\tstate[i] = 0;\n\n\treturn state;\n}\n\n// Static.\nstd::shared_ptr<Auth> YoutubeAuth::Login(QWidget *owner, const std::string &service)\n{\n\tQString auth_code;\n\tAuthListener server;\n\n\tauto it = std::find_if(youtubeServices.begin(), youtubeServices.end(),\n\t\t\t       [service](auto &item) { return service == item.service; });\n\tif (it == youtubeServices.end()) {\n\t\treturn nullptr;\n\t}\n\tconst auto auth = std::make_shared<YoutubeApiWrappers>(*it);\n\n\tQString redirect_uri = QString(\"http://127.0.0.1:%1\").arg(server.GetPort());\n\n\tQMessageBox dlg(owner);\n\tdlg.setWindowFlags(dlg.windowFlags() & ~Qt::WindowCloseButtonHint);\n\tdlg.setWindowTitle(QTStr(\"YouTube.Auth.WaitingAuth.Title\"));\n\n\tstd::string clientid = YOUTUBE_CLIENTID;\n\tstd::string secret = YOUTUBE_SECRET;\n\tdeobfuscate_str(&clientid[0], YOUTUBE_CLIENTID_HASH);\n\tdeobfuscate_str(&secret[0], YOUTUBE_SECRET_HASH);\n\n\tQString state;\n\tstate = auth->GenerateState();\n\tserver.SetState(state);\n\n\tQString url_template;\n\turl_template += \"%1\";\n\turl_template += \"?response_type=code\";\n\turl_template += \"&client_id=%2\";\n\turl_template += \"&redirect_uri=%3\";\n\turl_template += \"&state=%4\";\n\turl_template += \"&scope=https://www.googleapis.com/auth/youtube\";\n\tQString url = url_template.arg(YOUTUBE_AUTH_URL, clientid.c_str(), redirect_uri, state);\n\n\tQString text = QTStr(\"YouTube.Auth.WaitingAuth.Text\");\n\ttext = text.arg(QString(\"<a href='%1'>Google OAuth Service</a>\").arg(url));\n\n\tdlg.setText(text);\n\tdlg.setTextFormat(Qt::RichText);\n\tdlg.setStandardButtons(QMessageBox::StandardButton::Cancel);\n#if defined(__APPLE__) && QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)\n\t/* We can't show clickable links with the native NSAlert, so let's\n\t * force the old non-native dialog instead. */\n\tdlg.setOption(QMessageBox::Option::DontUseNativeDialog);\n#endif\n\n\tconnect(&dlg, &QMessageBox::buttonClicked, &dlg, [&](QAbstractButton *) {\n#ifdef _DEBUG\n\t\tblog(LOG_DEBUG, \"Action Cancelled.\");\n#endif\n\t\t// TODO: Stop server.\n\t\tdlg.reject();\n\t});\n\n\t// Async Login.\n\tconnect(&server, &AuthListener::ok, &dlg, [&dlg, &auth_code](QString code) {\n#ifdef _DEBUG\n\t\tblog(LOG_DEBUG, \"Got youtube redirected answer: %s\", QT_TO_UTF8(code));\n#endif\n\t\tauth_code = code;\n\t\tdlg.accept();\n\t});\n\tconnect(&server, &AuthListener::fail, &dlg, [&dlg]() {\n#ifdef _DEBUG\n\t\tblog(LOG_DEBUG, \"No access granted\");\n#endif\n\t\tdlg.reject();\n\t});\n\n\tauto open_external_browser = [url]() {\n\t\tOpenBrowser(url);\n\t};\n\tQScopedPointer<QThread> thread(CreateQThread(open_external_browser));\n\tthread->start();\n\n#if defined(__APPLE__) && QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) && QT_VERSION < QT_VERSION_CHECK(6, 6, 0)\n\tconst bool nativeDialogs = qApp->testAttribute(Qt::AA_DontUseNativeDialogs);\n\tApp()->setAttribute(Qt::AA_DontUseNativeDialogs, true);\n\tdlg.exec();\n\tApp()->setAttribute(Qt::AA_DontUseNativeDialogs, nativeDialogs);\n#else\n\tdlg.exec();\n#endif\n\n\tif (dlg.result() == QMessageBox::Cancel || dlg.result() == QDialog::Rejected)\n\t\treturn nullptr;\n\n\tif (!auth->GetToken(YOUTUBE_TOKEN_URL, clientid, secret, QT_TO_UTF8(redirect_uri), YOUTUBE_SCOPE_VERSION,\n\t\t\t    QT_TO_UTF8(auth_code), true)) {\n\t\treturn nullptr;\n\t}\n\n\tconfig_t *config = OBSBasic::Get()->Config();\n\tconfig_remove_value(config, \"YouTube\", \"ChannelName\");\n\n\tChannelDescription cd;\n\tif (auth->GetChannelDescription(cd))\n\t\tconfig_set_string(config, \"YouTube\", \"ChannelName\", QT_TO_UTF8(cd.title));\n\n\tconfig_save_safe(config, \"tmp\", nullptr);\n\treturn auth;\n}\n"
  },
  {
    "path": "frontend/oauth/YoutubeAuth.hpp",
    "content": "#pragma once\n\n#include \"OAuth.hpp\"\n\ninline const std::vector<Auth::Def> youtubeServices = {{\"YouTube - RTMP\", Auth::Type::OAuth_LinkedAccount, true, true},\n\t\t\t\t\t\t       {\"YouTube - RTMPS\", Auth::Type::OAuth_LinkedAccount, true, true},\n\t\t\t\t\t\t       {\"YouTube - HLS\", Auth::Type::OAuth_LinkedAccount, true, true}};\n\n#ifdef BROWSER_AVAILABLE\nclass YoutubeChatDock;\n#endif\n\nclass YoutubeAuth : public OAuthStreamKey {\n\tQ_OBJECT\n\n\tbool uiLoaded = false;\n\tstd::string section;\n\n#ifdef BROWSER_AVAILABLE\n\tYoutubeChatDock *chat = nullptr;\n#endif\n\n\tvirtual bool RetryLogin() override;\n\tvirtual void SaveInternal() override;\n\tvirtual bool LoadInternal() override;\n\tvirtual void LoadUI() override;\n\n\tQString GenerateState();\n\npublic:\n\tYoutubeAuth(const Def &d);\n\t~YoutubeAuth();\n\n\tvoid SetChatId(const QString &chat_id);\n\tvoid ResetChat();\n\tvoid ReloadChat();\n\n\tstatic std::shared_ptr<Auth> Login(QWidget *parent, const std::string &service);\n};\n"
  },
  {
    "path": "frontend/obs-main.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <OBSApp.hpp>\n\n#include <components/VolumeAccessibleInterface.hpp>\n#ifdef __APPLE__\n#include <dialogs/OBSPermissions.hpp>\n#endif\n#include <utility/BaseLexer.hpp>\n#include <utility/OBSTranslator.hpp>\n#include <utility/platform.hpp>\n\n#include <qt-wrappers.hpp>\n#include <util/platform.h>\n#include <util/profiler.h>\n#include <util/util.hpp>\n#ifdef _WIN32\n#include <util/windows/win-version.h>\n#endif\n\n#include <QFontDatabase>\n#include <QProcess>\n#include <QPushButton>\n#include <curl/curl.h>\n\n#include <fstream>\n#include <iostream>\n#include <sstream>\n#ifdef _WIN32\n#include <shellapi.h>\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#else\n#include <signal.h>\n#endif\n\nusing namespace std;\n\nstatic log_handler_t def_log_handler;\n\nextern string currentLogFile;\nextern string lastLogFile;\n\nbool portable_mode = false;\nbool steam = false;\nbool safe_mode = false;\nbool disable_3p_plugins = false;\nstatic bool unclean_shutdown = false;\nbool multi = false;\nstatic bool log_verbose = false;\nstatic bool unfiltered_log = false;\nbool opt_start_streaming = false;\nbool opt_start_recording = false;\nbool opt_studio_mode = false;\nbool opt_start_replaybuffer = false;\nbool opt_start_virtualcam = false;\nbool opt_minimize_tray = false;\nbool opt_allow_opengl = false;\nbool opt_always_on_top = false;\nbool opt_disable_updater = false;\nbool opt_disable_missing_files_check = false;\nstring opt_starting_collection;\nstring opt_starting_profile;\nstring opt_starting_scene;\n\nbool restart = false;\nbool restart_safe = false;\nstatic QStringList arguments;\n\nstring CurrentTimeString()\n{\n\tusing namespace std::chrono;\n\n\tstruct tm tstruct;\n\tchar buf[80];\n\n\tauto tp = system_clock::now();\n\tauto now = system_clock::to_time_t(tp);\n\ttstruct = *localtime(&now);\n\n\tsize_t written = strftime(buf, sizeof(buf), \"%T\", &tstruct);\n\tif (ratio_less<system_clock::period, seconds::period>::value && written && (sizeof(buf) - written) > 5) {\n\t\tauto tp_secs = time_point_cast<seconds>(tp);\n\t\tauto millis = duration_cast<milliseconds>(tp - tp_secs).count();\n\n\t\tsnprintf(buf + written, sizeof(buf) - written, \".%03u\", static_cast<unsigned>(millis));\n\t}\n\n\treturn buf;\n}\n\nstatic void LogString(fstream &logFile, const char *timeString, char *str, int log_level)\n{\n\tstatic mutex logfile_mutex;\n\tstring msg;\n\tmsg += timeString;\n\tmsg += str;\n\n\tlogfile_mutex.lock();\n\tlogFile << msg << endl;\n\tlogfile_mutex.unlock();\n\n\tQMetaObject::invokeMethod(App(), \"addLogLine\", Qt::QueuedConnection, Q_ARG(int, log_level),\n\t\t\t\t  Q_ARG(QString, QString(msg.c_str())));\n}\n\nstatic inline void LogStringChunk(fstream &logFile, char *str, int log_level)\n{\n\tchar *nextLine = str;\n\tstring timeString = CurrentTimeString();\n\ttimeString += \": \";\n\n\twhile (*nextLine) {\n\t\tchar *nextLine = strchr(str, '\\n');\n\t\tif (!nextLine)\n\t\t\tbreak;\n\n\t\tif (nextLine != str && nextLine[-1] == '\\r') {\n\t\t\tnextLine[-1] = 0;\n\t\t} else {\n\t\t\tnextLine[0] = 0;\n\t\t}\n\n\t\tLogString(logFile, timeString.c_str(), str, log_level);\n\t\tnextLine++;\n\t\tstr = nextLine;\n\t}\n\n\tLogString(logFile, timeString.c_str(), str, log_level);\n}\n\n#define MAX_REPEATED_LINES 30\n#define MAX_CHAR_VARIATION (255 * 3)\n\nstatic inline int sum_chars(const char *str)\n{\n\tint val = 0;\n\tfor (; *str != 0; str++)\n\t\tval += *str;\n\n\treturn val;\n}\n\nstatic inline bool too_many_repeated_entries(fstream &logFile, const char *msg, const char *output_str)\n{\n\tstatic mutex log_mutex;\n\tstatic const char *last_msg_ptr = nullptr;\n\tstatic int last_char_sum = 0;\n\tstatic int rep_count = 0;\n\n\tint new_sum = sum_chars(output_str);\n\n\tlock_guard<mutex> guard(log_mutex);\n\n\tif (unfiltered_log) {\n\t\treturn false;\n\t}\n\n\tif (last_msg_ptr == msg) {\n\t\tint diff = std::abs(new_sum - last_char_sum);\n\t\tif (diff < MAX_CHAR_VARIATION) {\n\t\t\treturn (rep_count++ >= MAX_REPEATED_LINES);\n\t\t}\n\t}\n\n\tif (rep_count > MAX_REPEATED_LINES) {\n\t\tlogFile << CurrentTimeString() << \": Last log entry repeated for \"\n\t\t\t<< to_string(rep_count - MAX_REPEATED_LINES) << \" more lines\" << endl;\n\t}\n\n\tlast_msg_ptr = msg;\n\tlast_char_sum = new_sum;\n\trep_count = 0;\n\n\treturn false;\n}\n\nstatic void do_log(int log_level, const char *msg, va_list args, void *param)\n{\n\tfstream &logFile = *static_cast<fstream *>(param);\n\tchar str[8192];\n\n#ifndef _WIN32\n\tva_list args2;\n\tva_copy(args2, args);\n#endif\n\n\tvsnprintf(str, sizeof(str), msg, args);\n\n#ifdef _WIN32\n\tif (IsDebuggerPresent()) {\n\t\tint wNum = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);\n\t\tif (wNum > 1) {\n\t\t\tstatic wstring wide_buf;\n\t\t\tstatic mutex wide_mutex;\n\n\t\t\tlock_guard<mutex> lock(wide_mutex);\n\t\t\twide_buf.reserve(wNum + 1);\n\t\t\twide_buf.resize(wNum - 1);\n\t\t\tMultiByteToWideChar(CP_UTF8, 0, str, -1, &wide_buf[0], wNum);\n\t\t\twide_buf.push_back('\\n');\n\n\t\t\tOutputDebugStringW(wide_buf.c_str());\n\t\t}\n\t}\n#endif\n\n#if !defined(_WIN32) && defined(_DEBUG)\n\tdef_log_handler(log_level, msg, args2, nullptr);\n#endif\n\n\tif (log_level <= LOG_INFO || log_verbose) {\n#if !defined(_WIN32) && !defined(_DEBUG)\n\t\tdef_log_handler(log_level, msg, args2, nullptr);\n#endif\n\t\tif (!too_many_repeated_entries(logFile, msg, str))\n\t\t\tLogStringChunk(logFile, str, log_level);\n\t}\n\n#if defined(_WIN32) && defined(OBS_DEBUGBREAK_ON_ERROR)\n\tif (log_level <= LOG_ERROR && IsDebuggerPresent())\n\t\t__debugbreak();\n#endif\n\n#ifndef _WIN32\n\tva_end(args2);\n#endif\n}\n\nstatic bool get_token(lexer *lex, string &str, base_token_type type)\n{\n\tbase_token token;\n\tif (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))\n\t\treturn false;\n\tif (token.type != type)\n\t\treturn false;\n\n\tstr.assign(token.text.array, token.text.len);\n\treturn true;\n}\n\nstatic bool expect_token(lexer *lex, const char *str, base_token_type type)\n{\n\tbase_token token;\n\tif (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))\n\t\treturn false;\n\tif (token.type != type)\n\t\treturn false;\n\n\treturn strref_cmp(&token.text, str) == 0;\n}\n\nstatic uint64_t convert_log_name(bool has_prefix, const char *name)\n{\n\tBaseLexer lex;\n\tstring year, month, day, hour, minute, second;\n\n\tlexer_start(lex, name);\n\n\tif (has_prefix) {\n\t\tstring temp;\n\t\tif (!get_token(lex, temp, BASETOKEN_ALPHA))\n\t\t\treturn 0;\n\t}\n\n\tif (!get_token(lex, year, BASETOKEN_DIGIT))\n\t\treturn 0;\n\tif (!expect_token(lex, \"-\", BASETOKEN_OTHER))\n\t\treturn 0;\n\tif (!get_token(lex, month, BASETOKEN_DIGIT))\n\t\treturn 0;\n\tif (!expect_token(lex, \"-\", BASETOKEN_OTHER))\n\t\treturn 0;\n\tif (!get_token(lex, day, BASETOKEN_DIGIT))\n\t\treturn 0;\n\tif (!get_token(lex, hour, BASETOKEN_DIGIT))\n\t\treturn 0;\n\tif (!expect_token(lex, \"-\", BASETOKEN_OTHER))\n\t\treturn 0;\n\tif (!get_token(lex, minute, BASETOKEN_DIGIT))\n\t\treturn 0;\n\tif (!expect_token(lex, \"-\", BASETOKEN_OTHER))\n\t\treturn 0;\n\tif (!get_token(lex, second, BASETOKEN_DIGIT))\n\t\treturn 0;\n\n\tstringstream timestring;\n\ttimestring << year << month << day << hour << minute << second;\n\treturn std::stoull(timestring.str());\n}\n\n/* If upgrading from an older (non-XDG) build of OBS, move config files to XDG directory. */\n/* TODO: Remove after version 32.0. */\n#if defined(__FreeBSD__)\nstatic void move_to_xdg(void)\n{\n\tchar old_path[512];\n\tchar new_path[512];\n\tchar *home = getenv(\"HOME\");\n\tif (!home)\n\t\treturn;\n\n\tif (snprintf(old_path, sizeof(old_path), \"%s/.obs-studio\", home) <= 0)\n\t\treturn;\n\n\t/* make base xdg path if it doesn't already exist */\n\tif (GetAppConfigPath(new_path, sizeof(new_path), \"\") <= 0)\n\t\treturn;\n\tif (os_mkdirs(new_path) == MKDIR_ERROR)\n\t\treturn;\n\n\tif (GetAppConfigPath(new_path, sizeof(new_path), \"obs-studio\") <= 0)\n\t\treturn;\n\n\tif (os_file_exists(old_path) && !os_file_exists(new_path)) {\n\t\trename(old_path, new_path);\n\t}\n}\n#endif\n\nstatic void delete_oldest_file(bool has_prefix, const char *location)\n{\n\tBPtr<char> logDir(GetAppConfigPathPtr(location));\n\tstring oldestLog;\n\tuint64_t oldest_ts = (uint64_t)-1;\n\tstruct os_dirent *entry;\n\n\tunsigned int maxLogs = (unsigned int)config_get_uint(App()->GetAppConfig(), \"General\", \"MaxLogs\");\n\n\tos_dir_t *dir = os_opendir(logDir);\n\tif (dir) {\n\t\tunsigned int count = 0;\n\n\t\twhile ((entry = os_readdir(dir)) != NULL) {\n\t\t\tif (entry->directory || *entry->d_name == '.')\n\t\t\t\tcontinue;\n\n\t\t\tuint64_t ts = convert_log_name(has_prefix, entry->d_name);\n\n\t\t\tif (ts) {\n\t\t\t\tif (ts < oldest_ts) {\n\t\t\t\t\toldestLog = entry->d_name;\n\t\t\t\t\toldest_ts = ts;\n\t\t\t\t}\n\n\t\t\t\tcount++;\n\t\t\t}\n\t\t}\n\n\t\tos_closedir(dir);\n\n\t\tif (count > maxLogs) {\n\t\t\tstringstream delPath;\n\n\t\t\tdelPath << logDir << \"/\" << oldestLog;\n\t\t\tos_unlink(delPath.str().c_str());\n\t\t}\n\t}\n}\n\nstatic void get_last_log(bool has_prefix, const char *subdir_to_use, std::string &last)\n{\n\tBPtr<char> logDir(GetAppConfigPathPtr(subdir_to_use));\n\tstruct os_dirent *entry;\n\tos_dir_t *dir = os_opendir(logDir);\n\tuint64_t highest_ts = 0;\n\n\tif (dir) {\n\t\twhile ((entry = os_readdir(dir)) != NULL) {\n\t\t\tif (entry->directory || *entry->d_name == '.')\n\t\t\t\tcontinue;\n\n\t\t\tuint64_t ts = convert_log_name(has_prefix, entry->d_name);\n\n\t\t\tif (ts > highest_ts) {\n\t\t\t\tlast = entry->d_name;\n\t\t\t\thighest_ts = ts;\n\t\t\t}\n\t\t}\n\n\t\tos_closedir(dir);\n\t}\n}\n\nstatic void create_log_file(fstream &logFile)\n{\n\tstringstream dst;\n\n\tget_last_log(false, \"obs-studio/logs\", lastLogFile);\n\n\tcurrentLogFile = GenerateTimeDateFilename(\"txt\");\n\tdst << \"obs-studio/logs/\" << currentLogFile.c_str();\n\n\tBPtr<char> path(GetAppConfigPathPtr(dst.str().c_str()));\n\n#ifdef _WIN32\n\tBPtr<wchar_t> wpath;\n\tos_utf8_to_wcs_ptr(path, 0, &wpath);\n\tlogFile.open(wpath, ios_base::in | ios_base::out | ios_base::trunc);\n#else\n\tlogFile.open(path, ios_base::in | ios_base::out | ios_base::trunc);\n#endif\n\n\tif (logFile.is_open()) {\n\t\tdelete_oldest_file(false, \"obs-studio/logs\");\n\t\tbase_set_log_handler(do_log, &logFile);\n\t} else {\n\t\tblog(LOG_ERROR, \"Failed to open log file\");\n\t}\n}\n\nstatic auto ProfilerNameStoreRelease = [](profiler_name_store_t *store) {\n\tprofiler_name_store_free(store);\n};\n\nusing ProfilerNameStore = std::unique_ptr<profiler_name_store_t, decltype(ProfilerNameStoreRelease)>;\n\nProfilerNameStore CreateNameStore()\n{\n\treturn ProfilerNameStore{profiler_name_store_create(), ProfilerNameStoreRelease};\n}\n\nstatic auto SnapshotRelease = [](profiler_snapshot_t *snap) {\n\tprofile_snapshot_free(snap);\n};\n\nusing ProfilerSnapshot = std::unique_ptr<profiler_snapshot_t, decltype(SnapshotRelease)>;\n\nProfilerSnapshot GetSnapshot()\n{\n\treturn ProfilerSnapshot{profile_snapshot_create(), SnapshotRelease};\n}\n\nstatic void SaveProfilerData(const ProfilerSnapshot &snap)\n{\n\tif (currentLogFile.empty())\n\t\treturn;\n\n\tauto pos = currentLogFile.rfind('.');\n\tif (pos == currentLogFile.npos)\n\t\treturn;\n\n#define LITERAL_SIZE(x) x, (sizeof(x) - 1)\n\tostringstream dst;\n\tdst.write(LITERAL_SIZE(\"obs-studio/profiler_data/\"));\n\tdst.write(currentLogFile.c_str(), pos);\n\tdst.write(LITERAL_SIZE(\".csv.gz\"));\n#undef LITERAL_SIZE\n\n\tBPtr<char> path = GetAppConfigPathPtr(dst.str().c_str());\n\tif (!profiler_snapshot_dump_csv_gz(snap.get(), path))\n\t\tblog(LOG_WARNING, \"Could not save profiler data to '%s'\", static_cast<const char *>(path));\n}\n\nstatic auto ProfilerFree = [](void *) {\n\tprofiler_stop();\n\n\tauto snap = GetSnapshot();\n\n\tprofiler_print(snap.get());\n\tprofiler_print_time_between_calls(snap.get());\n\n\tSaveProfilerData(snap);\n\n\tprofiler_free();\n};\n\nQAccessibleInterface *accessibleFactory(const QString &classname, QObject *object)\n{\n\tif (classname == QLatin1String(\"VolumeSlider\") && object && object->isWidgetType())\n\t\treturn new VolumeAccessibleInterface(static_cast<QWidget *>(object));\n\n\treturn nullptr;\n}\n\nstatic const char *run_program_init = \"run_program_init\";\nstatic int run_program(fstream &logFile, int argc, char *argv[])\n{\n\tint ret = -1;\n\n\tauto profilerNameStore = CreateNameStore();\n\n\tstd::unique_ptr<void, decltype(ProfilerFree)> prof_release(static_cast<void *>(&ProfilerFree), ProfilerFree);\n\n\tprofiler_start();\n\tprofile_register_root(run_program_init, 0);\n\n\tScopeProfiler prof{run_program_init};\n\n#ifdef _WIN32\n\tQGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);\n#endif\n\n\tQCoreApplication::addLibraryPath(\".\");\n\n#if __APPLE__\n\tInstallNSApplicationSubclass();\n\tInstallNSThreadLocks();\n\n\tif (!isInBundle()) {\n\t\tblog(LOG_ERROR,\n\t\t     \"OBS cannot be run as a standalone binary on macOS. Run the Application bundle instead.\");\n\t\treturn ret;\n\t}\n#endif\n\n#if !defined(_WIN32) && !defined(__APPLE__)\n\t/* NOTE: Users blindly set this, but this theme is incompatible with Qt6 and\n\t * crashes loading saved geometry. Just turn off this theme and let users complain OBS\n\t * looks ugly instead of crashing. */\n\tconst char *platform_theme = getenv(\"QT_QPA_PLATFORMTHEME\");\n\tif (platform_theme && strcmp(platform_theme, \"qt5ct\") == 0)\n\t\tunsetenv(\"QT_QPA_PLATFORMTHEME\");\n#endif\n\n\t/* NOTE: This disables an optimisation in Qt that attempts to determine if\n\t * any \"siblings\" intersect with a widget when determining the approximate\n\t * visible/unobscured area. However, by Qt's own admission this is slow\n\t * and in the case of OBS it significantly slows down lists with many\n\t * elements (e.g. Hotkeys) and it is actually faster to disable it. */\n\tqputenv(\"QT_NO_SUBTRACTOPAQUESIBLINGS\", \"1\");\n\n\tOBSApp program(argc, argv, profilerNameStore.get());\n\ttry {\n\t\tQAccessible::installFactory(accessibleFactory);\n\t\tQFontDatabase::addApplicationFont(\":/fonts/OpenSans-Regular.ttf\");\n\t\tQFontDatabase::addApplicationFont(\":/fonts/OpenSans-Bold.ttf\");\n\t\tQFontDatabase::addApplicationFont(\":/fonts/OpenSans-Italic.ttf\");\n\n\t\tbool created_log = false;\n\n\t\tprogram.AppInit();\n\t\tdelete_oldest_file(false, \"obs-studio/profiler_data\");\n\n\t\tOBSTranslator translator;\n\t\tprogram.installTranslator(&translator);\n\n\t\t/* --------------------------------------- */\n\t\t/* check and warn if already running       */\n\n\t\tbool cancel_launch = false;\n\t\tbool already_running = false;\n\n#ifdef _WIN32\n\t\tRunOnceMutex rom =\n#endif\n\t\t\tCheckIfAlreadyRunning(already_running);\n\n\t\tif (!already_running) {\n\t\t\tgoto run;\n\t\t}\n\n\t\tif (!multi) {\n\t\t\tQMessageBox mb(QMessageBox::Question, QTStr(\"AlreadyRunning.Title\"),\n\t\t\t\t       QTStr(\"AlreadyRunning.Text\"));\n\t\t\tmb.addButton(QTStr(\"AlreadyRunning.LaunchAnyway\"), QMessageBox::YesRole);\n\t\t\tQPushButton *cancelButton = mb.addButton(QTStr(\"Cancel\"), QMessageBox::NoRole);\n\t\t\tmb.setDefaultButton(cancelButton);\n\n\t\t\tmb.exec();\n\t\t\tcancel_launch = mb.clickedButton() == cancelButton;\n\t\t}\n\n\t\tif (cancel_launch)\n\t\t\treturn 0;\n\n\t\tif (!created_log) {\n\t\t\tcreate_log_file(logFile);\n\t\t\tcreated_log = true;\n\t\t}\n\n\t\tif (multi) {\n\t\t\tblog(LOG_INFO, \"User enabled --multi flag and is now \"\n\t\t\t\t       \"running multiple instances of OBS.\");\n\t\t} else {\n\t\t\tblog(LOG_WARNING, \"================================\");\n\t\t\tblog(LOG_WARNING, \"Warning: OBS is already running!\");\n\t\t\tblog(LOG_WARNING, \"================================\");\n\t\t\tblog(LOG_WARNING, \"User is now running multiple \"\n\t\t\t\t\t  \"instances of OBS!\");\n\t\t\t/* Clear unclean_shutdown flag as multiple instances\n\t\t\t * running from the same config will lead to a\n\t\t\t * false-positive detection.*/\n\t\t\tunclean_shutdown = false;\n\t\t}\n\n\t\t/* --------------------------------------- */\n\trun:\n\n#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__FreeBSD__)\n\t\t// Mounted by termina during chromeOS linux container startup\n\t\t// https://chromium.googlesource.com/chromiumos/overlays/board-overlays/+/master/project-termina/chromeos-base/termina-lxd-scripts/files/lxd_setup.sh\n\t\tos_dir_t *crosDir = os_opendir(\"/opt/google/cros-containers\");\n\t\tif (crosDir) {\n\t\t\tQMessageBox::StandardButtons buttons(QMessageBox::Ok);\n\t\t\tQMessageBox mb(QMessageBox::Critical, QTStr(\"ChromeOS.Title\"), QTStr(\"ChromeOS.Text\"), buttons,\n\t\t\t\t       nullptr);\n\n\t\t\tmb.exec();\n\t\t\treturn 0;\n\t\t}\n#endif\n\n\t\tif (!created_log)\n\t\t\tcreate_log_file(logFile);\n\n\t\tprogram.checkForUncleanShutdown();\n\n\t\tqInstallMessageHandler([](QtMsgType type, const QMessageLogContext &, const QString &message) {\n\t\t\tswitch (type) {\n#ifdef _DEBUG\n\t\t\tcase QtDebugMsg:\n\t\t\t\tblog(LOG_DEBUG, \"%s\", QT_TO_UTF8(message));\n\t\t\t\tbreak;\n\t\t\tcase QtInfoMsg:\n\t\t\t\tblog(LOG_INFO, \"%s\", QT_TO_UTF8(message));\n\t\t\t\tbreak;\n#else\n\t\t\tcase QtDebugMsg:\n\t\t\tcase QtInfoMsg:\n\t\t\t\tbreak;\n#endif\n\t\t\tcase QtWarningMsg:\n\t\t\t\tblog(LOG_WARNING, \"%s\", QT_TO_UTF8(message));\n\t\t\t\tbreak;\n\t\t\tcase QtCriticalMsg:\n\t\t\tcase QtFatalMsg:\n\t\t\t\tblog(LOG_ERROR, \"%s\", QT_TO_UTF8(message));\n\t\t\t\tbreak;\n\t\t\t}\n\t\t});\n\n#ifdef __APPLE__\n\t\tMacPermissionStatus audio_permission = CheckPermission(kAudioDeviceAccess);\n\t\tMacPermissionStatus video_permission = CheckPermission(kVideoDeviceAccess);\n\t\tMacPermissionStatus input_monitoring_permission = CheckPermission(kInputMonitoring);\n\t\tMacPermissionStatus screen_permission = CheckPermission(kScreenCapture);\n\n\t\tint permissionsDialogLastShown =\n\t\t\tconfig_get_int(App()->GetAppConfig(), \"General\", \"MacOSPermissionsDialogLastShown\");\n\t\tif (permissionsDialogLastShown < MACOS_PERMISSIONS_DIALOG_VERSION) {\n\t\t\tOBSPermissions check(nullptr, screen_permission, video_permission, audio_permission,\n\t\t\t\t\t     input_monitoring_permission);\n\t\t\tcheck.exec();\n\t\t}\n#endif\n\n#ifdef _WIN32\n\t\tif (IsRunningOnWine()) {\n\t\t\tQMessageBox mb(QMessageBox::Question, QTStr(\"Wine.Title\"), QTStr(\"Wine.Text\"));\n\t\t\tmb.setTextFormat(Qt::RichText);\n\t\t\tmb.addButton(QTStr(\"AlreadyRunning.LaunchAnyway\"), QMessageBox::AcceptRole);\n\t\t\tQPushButton *closeButton = mb.addButton(QMessageBox::Close);\n\t\t\tmb.setDefaultButton(closeButton);\n\n\t\t\tmb.exec();\n\t\t\tif (mb.clickedButton() == closeButton)\n\t\t\t\treturn 0;\n\t\t}\n#endif\n\n\t\tif (argc > 1) {\n\t\t\tstringstream stor;\n\t\t\tstor << argv[1];\n\t\t\tfor (int i = 2; i < argc; ++i) {\n\t\t\t\tstor << \" \" << argv[i];\n\t\t\t}\n\t\t\tblog(LOG_INFO, \"Command Line Arguments: %s\", stor.str().c_str());\n\t\t}\n\n\t\tif (!program.OBSInit())\n\t\t\treturn 0;\n\n\t\tprof.Stop();\n\n\t\tret = program.exec();\n\n\t} catch (const char *error) {\n\t\tblog(LOG_ERROR, \"%s\", error);\n\t\tOBSErrorBox(nullptr, \"%s\", error);\n\t}\n\n\tif (restart || restart_safe) {\n\t\targuments = qApp->arguments();\n\n\t\tif (restart_safe) {\n\t\t\targuments.append(\"--safe-mode\");\n\t\t} else {\n\t\t\targuments.removeAll(\"--safe-mode\");\n\t\t}\n\t}\n\n\treturn ret;\n}\n\n#define MAX_CRASH_REPORT_SIZE (200 * 1024)\n\n#ifdef _WIN32\n\n#define CRASH_MESSAGE                                                      \\\n\t\"Woops, OBS has crashed!\\n\\nWould you like to copy the crash log \" \\\n\t\"to the clipboard? The crash log will still be saved to:\\n\\n%s\"\n\nstatic void main_crash_handler(const char *format, va_list args, void * /* param */)\n{\n\tchar *text = new char[MAX_CRASH_REPORT_SIZE];\n\n\tvsnprintf(text, MAX_CRASH_REPORT_SIZE, format, args);\n\ttext[MAX_CRASH_REPORT_SIZE - 1] = 0;\n\n\tstring crashFilePath = \"obs-studio/crashes\";\n\n\tdelete_oldest_file(true, crashFilePath.c_str());\n\n\tstring name = crashFilePath + \"/\";\n\tname += \"Crash \" + GenerateTimeDateFilename(\"txt\");\n\n\tBPtr<char> path(GetAppConfigPathPtr(name.c_str()));\n\n\tfstream file;\n\n#ifdef _WIN32\n\tBPtr<wchar_t> wpath;\n\tos_utf8_to_wcs_ptr(path, 0, &wpath);\n\tfile.open(wpath, ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary);\n#else\n\tfile.open(path, ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary);\n#endif\n\tfile << text;\n\tfile.close();\n\n\tstring pathString(path.Get());\n\n#ifdef _WIN32\n\tstd::replace(pathString.begin(), pathString.end(), '/', '\\\\');\n#endif\n\n\tstring absolutePath = canonical(filesystem::path(pathString)).u8string();\n\n\tsize_t size = snprintf(nullptr, 0, CRASH_MESSAGE, absolutePath.c_str());\n\n\tunique_ptr<char[]> message_buffer(new char[size + 1]);\n\n\tsnprintf(message_buffer.get(), size + 1, CRASH_MESSAGE, absolutePath.c_str());\n\n\tstring finalMessage = string(message_buffer.get(), message_buffer.get() + size);\n\n\tint ret = MessageBoxA(NULL, finalMessage.c_str(), \"OBS has crashed!\", MB_YESNO | MB_ICONERROR | MB_TASKMODAL);\n\n\tif (ret == IDYES) {\n\t\tsize_t len = strlen(text);\n\n\t\tHGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, len);\n\t\tmemcpy(GlobalLock(mem), text, len);\n\t\tGlobalUnlock(mem);\n\n\t\tOpenClipboard(0);\n\t\tEmptyClipboard();\n\t\tSetClipboardData(CF_TEXT, mem);\n\t\tCloseClipboard();\n\t}\n\n\texit(-1);\n}\n\nstatic void load_debug_privilege(void)\n{\n\tconst DWORD flags = TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY;\n\tTOKEN_PRIVILEGES tp;\n\tHANDLE token;\n\tLUID val;\n\n\tif (!OpenProcessToken(GetCurrentProcess(), flags, &token)) {\n\t\treturn;\n\t}\n\n\tif (!!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &val)) {\n\t\ttp.PrivilegeCount = 1;\n\t\ttp.Privileges[0].Luid = val;\n\t\ttp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\n\n\t\tAdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL);\n\t}\n\n\tif (!!LookupPrivilegeValue(NULL, SE_INC_BASE_PRIORITY_NAME, &val)) {\n\t\ttp.PrivilegeCount = 1;\n\t\ttp.Privileges[0].Luid = val;\n\t\ttp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;\n\n\t\tif (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) {\n\t\t\tblog(LOG_INFO, \"Could not set privilege to \"\n\t\t\t\t       \"increase GPU priority\");\n\t\t}\n\t}\n\n\tCloseHandle(token);\n}\n#endif\n\nstatic inline bool arg_is(const char *arg, const char *long_form, const char *short_form)\n{\n\treturn (long_form && strcmp(arg, long_form) == 0) || (short_form && strcmp(arg, short_form) == 0);\n}\n\n#ifdef _WIN32\nstatic constexpr char vcRunErrorTitle[] = \"Outdated Visual C++ Runtime\";\nstatic constexpr char vcRunErrorMsg[] = \"OBS Studio requires a newer version of the Microsoft Visual C++ \"\n\t\t\t\t\t\"Redistributables.\\n\\nYou will now be directed to the download page.\";\nstatic constexpr char vcRunInstallerUrl[] = \"https://obsproject.com/visual-studio-2022-runtimes\";\n\nstatic bool vc_runtime_outdated()\n{\n\twin_version_info ver;\n\tif (!get_dll_ver(L\"msvcp140.dll\", &ver))\n\t\treturn true;\n\t/* Major is always 14 (hence 140.dll), so we only care about minor. */\n\tif (ver.minor >= 40)\n\t\treturn false;\n\n\tint choice = MessageBoxA(NULL, vcRunErrorMsg, vcRunErrorTitle, MB_OKCANCEL | MB_ICONERROR | MB_TASKMODAL);\n\tif (choice == IDOK) {\n\t\t/* Open the URL in the default browser. */\n\t\tShellExecuteA(NULL, \"open\", vcRunInstallerUrl, NULL, NULL, SW_SHOWNORMAL);\n\t}\n\n\treturn true;\n}\n#endif\n\n#if defined(__APPLE__) || defined(__linux__)\n#define BASE_PATH \"..\"\n#else\n#define BASE_PATH \"../..\"\n#endif\n\n#if defined(ENABLE_PORTABLE_CONFIG) || defined(_WIN32)\n#define ALLOW_PORTABLE_MODE 1\n#else\n#define ALLOW_PORTABLE_MODE 0\n#endif\n\nint main(int argc, char *argv[])\n{\n#ifndef _WIN32\n\tusing SignalHandlerCallback = decltype(OBSApp::sigIntSignalHandler);\n\n\tauto setupSignalHandler = [](SignalHandlerCallback &callback, int signal) {\n\t\tstruct sigaction signalHandler{};\n\t\tsignalHandler.sa_handler = callback;\n\t\tsigemptyset(&signalHandler.sa_mask);\n\t\tsignalHandler.sa_flags = 0;\n\n\t\tsigaction(signal, &signalHandler, nullptr);\n\t};\n\n\tsetupSignalHandler(OBSApp::sigIntSignalHandler, SIGINT);\n\tsetupSignalHandler(OBSApp::sigTermSignalHandler, SIGTERM);\n\tsetupSignalHandler(OBSApp::sigTermSignalHandler, SIGHUP);\n\tsetupSignalHandler(OBSApp::sigAbrtSignalHandler, SIGABRT);\n\tsetupSignalHandler(OBSApp::sigQuitSignalHandler, SIGQUIT);\n\n\t// Block SIGPIPE in all threads, this can happen if a thread calls write on a closed pipe.\n\t// Supposedly this can happen when OBS tries to write to a closed RTMP socket.\n\tsigset_t sigpipeMask{};\n\tsigemptyset(&sigpipeMask);\n\tsigaddset(&sigpipeMask, SIGPIPE);\n\tsigset_t savedMask{};\n\n\t// pthread_sigmask returns 0 on success\n\tbool signalMaskChanged = pthread_sigmask(SIG_BLOCK, &sigpipeMask, &savedMask) == 0;\n\n\tif (!signalMaskChanged) {\n\t\tperror(\"Unexpected failure to add 'SIG_BLOCK' to 'SIGPIPE' signal mask.\");\n\t\texit(1);\n\t}\n#endif\n\n#ifdef _WIN32\n\t// Abort as early as possible if MSVC runtime is outdated\n\tif (vc_runtime_outdated())\n\t\treturn 1;\n\t// Try to keep this as early as possible\n\tinstall_dll_blocklist_hook();\n\n\tobs_init_win32_crash_handler();\n\tSetErrorMode(SEM_FAILCRITICALERRORS);\n\tload_debug_privilege();\n\tbase_set_crash_handler(main_crash_handler, nullptr);\n\n\t/* Shutdown priority value is a range from 0 - 4FF with higher values getting first priority.\n\t * 000 - 0FF and 400 - 4FF are reserved system ranges.\n\t * Processes start at shutdown level 0x280 by default.\n\t * We set the main OBS application to a higher priority to ensure it tries to close before\n\t * any subprocesses such as CEF.\n\t */\n\tSetProcessShutdownParameters(0x300, SHUTDOWN_NORETRY);\n\n\tconst HMODULE hRtwq = LoadLibrary(L\"RTWorkQ.dll\");\n\tif (hRtwq) {\n\t\ttypedef HRESULT(STDAPICALLTYPE * PFN_RtwqStartup)();\n\t\tPFN_RtwqStartup func = (PFN_RtwqStartup)GetProcAddress(hRtwq, \"RtwqStartup\");\n\t\tfunc();\n\t}\n#endif\n\n\tbase_get_log_handler(&def_log_handler, nullptr);\n\n#if defined(__FreeBSD__)\n\tmove_to_xdg();\n#endif\n\n\tobs_set_cmdline_args(argc, argv);\n\n\tfor (int i = 1; i < argc; i++) {\n\t\tif (arg_is(argv[i], \"--multi\", \"-m\")) {\n\t\t\tmulti = true;\n\n#if ALLOW_PORTABLE_MODE\n\t\t} else if (arg_is(argv[i], \"--portable\", \"-p\")) {\n\t\t\tportable_mode = true;\n\n#endif\n\t\t} else if (arg_is(argv[i], \"--verbose\", nullptr)) {\n\t\t\tlog_verbose = true;\n\n\t\t} else if (arg_is(argv[i], \"--safe-mode\", nullptr)) {\n\t\t\tsafe_mode = true;\n\n\t\t} else if (arg_is(argv[i], \"--only-bundled-plugins\", nullptr)) {\n\t\t\tdisable_3p_plugins = true;\n\n\t\t} else if (arg_is(argv[i], \"--always-on-top\", nullptr)) {\n\t\t\topt_always_on_top = true;\n\n\t\t} else if (arg_is(argv[i], \"--unfiltered_log\", nullptr)) {\n\t\t\tunfiltered_log = true;\n\n\t\t} else if (arg_is(argv[i], \"--startstreaming\", nullptr)) {\n\t\t\topt_start_streaming = true;\n\n\t\t} else if (arg_is(argv[i], \"--startrecording\", nullptr)) {\n\t\t\topt_start_recording = true;\n\n\t\t} else if (arg_is(argv[i], \"--startreplaybuffer\", nullptr)) {\n\t\t\topt_start_replaybuffer = true;\n\n\t\t} else if (arg_is(argv[i], \"--startvirtualcam\", nullptr)) {\n\t\t\topt_start_virtualcam = true;\n\n\t\t} else if (arg_is(argv[i], \"--collection\", nullptr)) {\n\t\t\tif (++i < argc)\n\t\t\t\topt_starting_collection = argv[i];\n\n\t\t} else if (arg_is(argv[i], \"--profile\", nullptr)) {\n\t\t\tif (++i < argc)\n\t\t\t\topt_starting_profile = argv[i];\n\n\t\t} else if (arg_is(argv[i], \"--scene\", nullptr)) {\n\t\t\tif (++i < argc)\n\t\t\t\topt_starting_scene = argv[i];\n\n\t\t} else if (arg_is(argv[i], \"--minimize-to-tray\", nullptr)) {\n\t\t\topt_minimize_tray = true;\n\n\t\t} else if (arg_is(argv[i], \"--studio-mode\", nullptr)) {\n\t\t\topt_studio_mode = true;\n\n\t\t} else if (arg_is(argv[i], \"--allow-opengl\", nullptr)) {\n\t\t\topt_allow_opengl = true;\n\n\t\t} else if (arg_is(argv[i], \"--disable-updater\", nullptr)) {\n\t\t\topt_disable_updater = true;\n\n\t\t} else if (arg_is(argv[i], \"--disable-missing-files-check\", nullptr)) {\n\t\t\topt_disable_missing_files_check = true;\n\n\t\t} else if (arg_is(argv[i], \"--steam\", nullptr)) {\n\t\t\tsteam = true;\n\n\t\t} else if (arg_is(argv[i], \"--help\", \"-h\")) {\n\t\t\tstd::string help =\n\t\t\t\t\"--help, -h: Get list of available commands.\\n\\n\"\n\t\t\t\t\"--startstreaming: Automatically start streaming.\\n\"\n\t\t\t\t\"--startrecording: Automatically start recording.\\n\"\n\t\t\t\t\"--startreplaybuffer: Start replay buffer.\\n\"\n\t\t\t\t\"--startvirtualcam: Start virtual camera (if available).\\n\\n\"\n\t\t\t\t\"--collection <string>: Use specific scene collection.\"\n\t\t\t\t\"\\n\"\n\t\t\t\t\"--profile <string>: Use specific profile.\\n\"\n\t\t\t\t\"--scene <string>: Start with specific scene.\\n\\n\"\n\t\t\t\t\"--studio-mode: Enable studio mode.\\n\"\n\t\t\t\t\"--minimize-to-tray: Minimize to system tray.\\n\"\n#if ALLOW_PORTABLE_MODE\n\t\t\t\t\"--portable, -p: Use portable mode.\\n\"\n#endif\n\t\t\t\t\"--multi, -m: Don't warn when launching multiple instances.\\n\\n\"\n\t\t\t\t\"--safe-mode: Run in Safe Mode (disables third-party plugins, scripting, and WebSockets).\\n\"\n\t\t\t\t\"--only-bundled-plugins: Only load included (first-party) plugins\\n\"\n\t\t\t\t\"--verbose: Make log more verbose.\\n\"\n\t\t\t\t\"--always-on-top: Start in 'always on top' mode.\\n\\n\"\n\t\t\t\t\"--unfiltered_log: Make log unfiltered.\\n\\n\"\n\t\t\t\t\"--disable-updater: Disable built-in updater (Windows/Mac only)\\n\\n\"\n\t\t\t\t\"--disable-missing-files-check: Disable the missing files dialog which can appear on startup.\\n\\n\";\n\n#ifdef _WIN32\n\t\t\tMessageBoxA(NULL, help.c_str(), \"Help\", MB_OK | MB_ICONASTERISK);\n#else\n\t\t\tstd::cout << help << \"--version, -V: Get current version.\\n\";\n#endif\n\t\t\texit(0);\n\n\t\t} else if (arg_is(argv[i], \"--version\", \"-V\")) {\n\t\t\tstd::cout << \"OBS Studio - \" << App()->GetVersionString(false) << \"\\n\";\n\t\t\texit(0);\n\t\t}\n\t}\n\n#if ALLOW_PORTABLE_MODE\n\tif (!portable_mode) {\n\t\tportable_mode = os_file_exists(BASE_PATH \"/portable_mode\") ||\n\t\t\t\tos_file_exists(BASE_PATH \"/obs_portable_mode\") ||\n\t\t\t\tos_file_exists(BASE_PATH \"/portable_mode.txt\") ||\n\t\t\t\tos_file_exists(BASE_PATH \"/obs_portable_mode.txt\");\n\t}\n\n\tif (!opt_disable_updater) {\n\t\topt_disable_updater = os_file_exists(BASE_PATH \"/disable_updater\") ||\n\t\t\t\t      os_file_exists(BASE_PATH \"/disable_updater.txt\");\n\t}\n\n\tif (!opt_disable_missing_files_check) {\n\t\topt_disable_missing_files_check = os_file_exists(BASE_PATH \"/disable_missing_files_check\") ||\n\t\t\t\t\t\t  os_file_exists(BASE_PATH \"/disable_missing_files_check.txt\");\n\t}\n#endif\n\n\tfstream logFile;\n\n\tcurl_global_init(CURL_GLOBAL_ALL);\n\tint ret = run_program(logFile, argc, argv);\n\n#ifdef _WIN32\n\tif (hRtwq) {\n\t\ttypedef HRESULT(STDAPICALLTYPE * PFN_RtwqShutdown)();\n\t\tPFN_RtwqShutdown func = (PFN_RtwqShutdown)GetProcAddress(hRtwq, \"RtwqShutdown\");\n\t\tfunc();\n\t\tFreeLibrary(hRtwq);\n\t}\n\n\tlog_blocked_dlls();\n#endif\n\n\tblog(LOG_INFO, \"Number of memory leaks: %ld\", bnum_allocs());\n\tbase_set_log_handler(nullptr, nullptr);\n\n\tif (restart || restart_safe) {\n\t\tauto executable = arguments.takeFirst();\n\t\tQProcess::startDetached(executable, arguments);\n\t}\n\n\treturn ret;\n}\n"
  },
  {
    "path": "frontend/plugin-manager/PluginManager.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by FiniteSingularity <finitesingularityttv@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"PluginManager.hpp\"\n#include \"PluginManagerWindow.hpp\"\n\n#include <OBSApp.hpp>\n#include <qt-wrappers.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <QMessageBox>\n\n#include <nlohmann/json.hpp>\n\n#include <algorithm>\n#include <fstream>\n\nextern bool restart;\n\nnamespace OBS {\n\nvoid addModuleToPluginManagerImpl(void *param, obs_module_t *newModule)\n{\n\tauto &instance = *static_cast<OBS::PluginManager *>(param);\n\tstd::string moduleName = obs_get_module_file_name(newModule);\n\tmoduleName = moduleName.substr(0, moduleName.rfind(\".\"));\n\n\tif (!obs_get_module_allow_disable(moduleName.c_str()))\n\t\treturn;\n\n\tconst char *display_name = obs_get_module_name(newModule);\n\tstd::string module_name = moduleName;\n\tconst char *id = obs_get_module_id(newModule);\n\tconst char *version = obs_get_module_version(newModule);\n\n\tauto it = std::find_if(instance.modules_.begin(), instance.modules_.end(),\n\t\t\t       [&](OBS::ModuleInfo module) { return module.module_name == moduleName; });\n\n\tif (it == instance.modules_.end()) {\n\t\tinstance.modules_.push_back({display_name ? display_name : \"\", module_name, id ? id : \"\",\n\t\t\t\t\t     version ? version : \"\", true, true});\n\t} else {\n\t\tit->display_name = display_name ? display_name : \"\";\n\t\tit->module_name = module_name;\n\t\tit->id = id ? id : \"\";\n\t\tit->version = version ? version : \"\";\n\t}\n}\n\nconstexpr std::string_view OBSPluginManagerPath = \"obs-studio/plugin_manager\";\nconstexpr std::string_view OBSPluginManagerModulesFile = \"modules.json\";\n\nvoid PluginManager::preLoad()\n{\n\tloadModules_();\n\tdisableModules_();\n}\n\nvoid PluginManager::postLoad()\n{\n\t// Find any new modules and add to Plugin Manager.\n\tobs_enum_modules(addModuleToPluginManager, this);\n\t// Get list of valid module types.\n\taddModuleTypes_();\n\tsaveModules_();\n\t// Add provided features from any unloaded modules\n\tlinkUnloadedModules_();\n}\n\nstd::filesystem::path PluginManager::getConfigFilePath_()\n{\n\tstd::filesystem::path path = App()->userPluginManagerSettingsLocation /\n\t\t\t\t     std::filesystem::u8path(OBSPluginManagerPath) /\n\t\t\t\t     std::filesystem::u8path(OBSPluginManagerModulesFile);\n\treturn path;\n}\n\nvoid PluginManager::loadModules_()\n{\n\tauto modulesFile = getConfigFilePath_();\n\tif (std::filesystem::exists(modulesFile)) {\n\t\tstd::ifstream jsonFile(modulesFile);\n\t\tnlohmann::json data;\n\t\ttry {\n\t\t\tdata = nlohmann::json::parse(jsonFile);\n\t\t} catch (const nlohmann::json::parse_error &error) {\n\t\t\tmodules_.clear();\n\t\t\tblog(LOG_ERROR, \"Error loading modules config file: %s\", error.what());\n\t\t\tblog(LOG_ERROR, \"Generating new config file.\");\n\t\t\treturn;\n\t\t}\n\t\tmodules_.clear();\n\t\tfor (auto it : data) {\n\t\t\tModuleInfo obsModule;\n\t\t\ttry {\n\t\t\t\tobsModule = {it.at(\"display_name\"),\n\t\t\t\t\t     it.at(\"module_name\"),\n\t\t\t\t\t     it.at(\"id\"),\n\t\t\t\t\t     it.at(\"version\"),\n\t\t\t\t\t     it.at(\"enabled\"),\n\t\t\t\t\t     it.at(\"enabled\"),\n\t\t\t\t\t     it.at(\"sources\"),\n\t\t\t\t\t     it.at(\"outputs\"),\n\t\t\t\t\t     it.at(\"encoders\"),\n\t\t\t\t\t     it.at(\"services\"),\n\t\t\t\t\t     {},\n\t\t\t\t\t     {},\n\t\t\t\t\t     {},\n\t\t\t\t\t     {}};\n\t\t\t} catch (const nlohmann::json::out_of_range &error) {\n\t\t\t\tblog(LOG_WARNING, \"Error loading module info: %s\", error.what());\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tmodules_.push_back(obsModule);\n\t\t}\n\t}\n}\n\nvoid PluginManager::linkUnloadedModules_()\n{\n\tfor (const auto &moduleInfo : modules_) {\n\t\tif (!moduleInfo.enabled) {\n\t\t\tauto obsModule = obs_get_disabled_module(moduleInfo.module_name.c_str());\n\t\t\tif (!obsModule) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (const auto &source : moduleInfo.sources) {\n\t\t\t\tobs_module_add_source(obsModule, source.c_str());\n\t\t\t}\n\t\t\tfor (const auto &output : moduleInfo.outputs) {\n\t\t\t\tobs_module_add_output(obsModule, output.c_str());\n\t\t\t}\n\t\t\tfor (const auto &encoder : moduleInfo.encoders) {\n\t\t\t\tobs_module_add_encoder(obsModule, encoder.c_str());\n\t\t\t}\n\t\t\tfor (const auto &service : moduleInfo.services) {\n\t\t\t\tobs_module_add_service(obsModule, service.c_str());\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid PluginManager::saveModules_()\n{\n\tauto modulesFile = getConfigFilePath_();\n\tstd::ofstream outFile(modulesFile);\n\tnlohmann::json data = nlohmann::json::array();\n\n\tfor (auto const &moduleInfo : modules_) {\n\t\tnlohmann::json modData;\n\t\tmodData[\"display_name\"] = moduleInfo.display_name;\n\t\tmodData[\"module_name\"] = moduleInfo.module_name;\n\t\tmodData[\"id\"] = moduleInfo.id;\n\t\tmodData[\"version\"] = moduleInfo.version;\n\t\tmodData[\"enabled\"] = moduleInfo.enabled;\n\t\tmodData[\"sources\"] = moduleInfo.sources;\n\t\tmodData[\"outputs\"] = moduleInfo.outputs;\n\t\tmodData[\"encoders\"] = moduleInfo.encoders;\n\t\tmodData[\"services\"] = moduleInfo.services;\n\t\tdata.push_back(modData);\n\t}\n\toutFile << std::setw(4) << data << std::endl;\n}\n\nvoid PluginManager::addModuleTypes_()\n{\n\tconst char *source_id;\n\tint i = 0;\n\twhile (obs_enum_source_types(i, &source_id)) {\n\t\ti += 1;\n\t\tobs_module_t *obsModule = obs_source_get_module(source_id);\n\t\tif (!obsModule) {\n\t\t\tcontinue;\n\t\t}\n\t\tstd::string moduleName = obs_get_module_file_name(obsModule);\n\t\tmoduleName = moduleName.substr(0, moduleName.rfind(\".\"));\n\t\tauto it = std::find_if(modules_.begin(), modules_.end(),\n\t\t\t\t       [moduleName](ModuleInfo const &m) { return m.module_name == moduleName; });\n\t\tif (it != modules_.end()) {\n\t\t\tit->sourcesLoaded.push_back(source_id);\n\t\t}\n\t}\n\n\tconst char *output_id;\n\ti = 0;\n\twhile (obs_enum_output_types(i, &output_id)) {\n\t\ti += 1;\n\t\tobs_module_t *obsModule = obs_output_get_module(output_id);\n\t\tif (!obsModule) {\n\t\t\tcontinue;\n\t\t}\n\t\tstd::string moduleName = obs_get_module_file_name(obsModule);\n\t\tmoduleName = moduleName.substr(0, moduleName.rfind(\".\"));\n\t\tauto it = std::find_if(modules_.begin(), modules_.end(),\n\t\t\t\t       [moduleName](ModuleInfo const &m) { return m.module_name == moduleName; });\n\t\tif (it != modules_.end()) {\n\t\t\tit->outputsLoaded.push_back(output_id);\n\t\t}\n\t}\n\n\tconst char *encoder_id;\n\ti = 0;\n\twhile (obs_enum_encoder_types(i, &encoder_id)) {\n\t\ti += 1;\n\t\tobs_module_t *obsModule = obs_encoder_get_module(encoder_id);\n\t\tif (!obsModule) {\n\t\t\tcontinue;\n\t\t}\n\t\tstd::string moduleName = obs_get_module_file_name(obsModule);\n\t\tmoduleName = moduleName.substr(0, moduleName.rfind(\".\"));\n\t\tauto it = std::find_if(modules_.begin(), modules_.end(),\n\t\t\t\t       [moduleName](ModuleInfo const &m) { return m.module_name == moduleName; });\n\t\tif (it != modules_.end()) {\n\t\t\tit->encodersLoaded.push_back(encoder_id);\n\t\t}\n\t}\n\n\tconst char *service_id;\n\ti = 0;\n\twhile (obs_enum_service_types(i, &service_id)) {\n\t\ti += 1;\n\t\tobs_module_t *obsModule = obs_service_get_module(service_id);\n\t\tif (!obsModule) {\n\t\t\tcontinue;\n\t\t}\n\t\tstd::string moduleName = obs_get_module_file_name(obsModule);\n\t\tmoduleName = moduleName.substr(0, moduleName.rfind(\".\"));\n\t\tauto it = std::find_if(modules_.begin(), modules_.end(),\n\t\t\t\t       [moduleName](ModuleInfo const &m) { return m.module_name == moduleName; });\n\t\tif (it != modules_.end()) {\n\t\t\tit->servicesLoaded.push_back(service_id);\n\t\t}\n\t}\n\n\tfor (auto &moduleInfo : modules_) {\n\t\tif (moduleInfo.enabledAtLaunch) {\n\t\t\tmoduleInfo.sources = moduleInfo.sourcesLoaded;\n\t\t\tmoduleInfo.encoders = moduleInfo.encodersLoaded;\n\t\t\tmoduleInfo.outputs = moduleInfo.outputsLoaded;\n\t\t\tmoduleInfo.services = moduleInfo.servicesLoaded;\n\t\t} else {\n\t\t\tfor (auto const &source : moduleInfo.sources) {\n\t\t\t\tdisabledSources_.push_back(source);\n\t\t\t}\n\t\t\tfor (auto const &output : moduleInfo.outputs) {\n\t\t\t\tdisabledOutputs_.push_back(output);\n\t\t\t}\n\t\t\tfor (auto const &encoder : moduleInfo.encoders) {\n\t\t\t\tdisabledEncoders_.push_back(encoder);\n\t\t\t}\n\t\t\tfor (auto const &service : moduleInfo.services) {\n\t\t\t\tdisabledServices_.push_back(service);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid PluginManager::disableModules_()\n{\n\tfor (const auto &moduleInfo : modules_) {\n\t\tif (!moduleInfo.enabled) {\n\t\t\tobs_add_disabled_module(moduleInfo.module_name.c_str());\n\t\t}\n\t}\n}\n\nvoid PluginManager::open()\n{\n\tauto main = OBSBasic::Get();\n\tPluginManagerWindow pluginManagerWindow(modules_, main);\n\tauto result = pluginManagerWindow.exec();\n\tif (result == QDialog::Accepted) {\n\t\tmodules_ = pluginManagerWindow.result();\n\t\tsaveModules_();\n\n\t\tbool changed = false;\n\n\t\tfor (auto const &moduleInfo : modules_) {\n\t\t\tif (moduleInfo.enabled != moduleInfo.enabledAtLaunch) {\n\t\t\t\tchanged = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (changed) {\n\t\t\tQMessageBox::StandardButton button =\n\t\t\t\tOBSMessageBox::question(main, QTStr(\"Restart\"), QTStr(\"NeedsRestart\"));\n\n\t\t\tif (button == QMessageBox::Yes) {\n\t\t\t\trestart = true;\n\t\t\t\tmain->close();\n\t\t\t}\n\t\t}\n\t}\n}\n\n}; // namespace OBS\n"
  },
  {
    "path": "frontend/plugin-manager/PluginManager.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by FiniteSingularity <finitesingularityttv@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <obs-module.h>\n\n#include <filesystem>\n#include <string>\n#include <vector>\n\nnamespace OBS {\n\nstruct ModuleInfo {\n\tstd::string display_name;\n\tstd::string module_name;\n\tstd::string id;\n\tstd::string version;\n\tbool enabled;\n\tbool enabledAtLaunch;\n\tstd::vector<std::string> sources;\n\tstd::vector<std::string> outputs;\n\tstd::vector<std::string> encoders;\n\tstd::vector<std::string> services;\n\tstd::vector<std::string> sourcesLoaded;\n\tstd::vector<std::string> outputsLoaded;\n\tstd::vector<std::string> encodersLoaded;\n\tstd::vector<std::string> servicesLoaded;\n};\n\nclass PluginManager {\nprivate:\n\tstd::vector<ModuleInfo> modules_ = {};\n\tstd::vector<std::string> disabledSources_ = {};\n\tstd::vector<std::string> disabledOutputs_ = {};\n\tstd::vector<std::string> disabledServices_ = {};\n\tstd::vector<std::string> disabledEncoders_ = {};\n\tstd::filesystem::path getConfigFilePath_();\n\tvoid loadModules_();\n\tvoid saveModules_();\n\tvoid disableModules_();\n\tvoid addModuleTypes_();\n\tvoid linkUnloadedModules_();\n\npublic:\n\tvoid preLoad();\n\tvoid postLoad();\n\tvoid open();\n\n\tfriend void addModuleToPluginManagerImpl(void *param, obs_module_t *newModule);\n};\n\nvoid addModuleToPluginManagerImpl(void *param, obs_module_t *newModule);\n\n}; // namespace OBS\n\n// Anonymous namespace function to add module to plugin manager\n// via libobs's module enumeration.\nnamespace {\ninline void addModuleToPluginManager(void *param, obs_module_t *newModule)\n{\n\tOBS::addModuleToPluginManagerImpl(param, newModule);\n}\n} // namespace\n"
  },
  {
    "path": "frontend/plugin-manager/PluginManagerWindow.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by FiniteSingularity <finitesingularityttv@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"PluginManagerWindow.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <QCheckBox>\n#include <QDialogButtonBox>\n#include <QHBoxLayout>\n#include <QLabel>\n#include <QScrollArea>\n#include <QString>\n#include <QVBoxLayout>\n\n#include \"moc_PluginManagerWindow.cpp\"\n\nextern bool safe_mode;\n\nnamespace OBS {\n\nPluginManagerWindow::PluginManagerWindow(std::vector<ModuleInfo> const &modules, QWidget *parent)\n\t: QDialog(parent),\n\t  modules_(modules),\n\t  ui(new Ui::PluginManagerWindow)\n{\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\n\tui->modulesListContainer->viewport()->setAutoFillBackground(false);\n\tui->modulesListContents->setAutoFillBackground(false);\n\n\t// Set up sidebar entries\n\tui->sectionList->clear();\n\tui->sectionList->setSelectionMode(QAbstractItemView::SingleSelection);\n\n\tconnect(ui->sectionList, &QListWidget::itemSelectionChanged, this,\n\t\t&PluginManagerWindow::sectionSelectionChanged);\n\n\tQListWidgetItem *browse = new QListWidgetItem(QTStr(\"PluginManager.Section.Discover\"));\n\tbrowse->setFlags(browse->flags() & ~Qt::ItemIsEnabled);\n\tbrowse->setFlags(browse->flags() & ~Qt::ItemIsSelectable);\n\tbrowse->setToolTip(QTStr(\"ComingSoon\"));\n\tui->sectionList->addItem(browse);\n\n\tQListWidgetItem *installed = new QListWidgetItem(QTStr(\"PluginManager.Section.Manage\"));\n\tui->sectionList->addItem(installed);\n\n\tQListWidgetItem *updates = new QListWidgetItem(QTStr(\"PluginManager.Section.Updates\"));\n\tupdates->setFlags(updates->flags() & ~Qt::ItemIsEnabled);\n\tupdates->setFlags(updates->flags() & ~Qt::ItemIsSelectable);\n\tupdates->setToolTip(QTStr(\"ComingSoon\"));\n\tui->sectionList->addItem(updates);\n\n\tsetSection(ui->sectionList->indexFromItem(installed));\n\n\tstd::sort(modules_.begin(), modules_.end(), [](const ModuleInfo &a, const ModuleInfo &b) {\n\t\tstd::string aName = !a.display_name.empty() ? a.display_name : a.module_name;\n\t\tstd::string bName = !b.display_name.empty() ? b.display_name : b.module_name;\n\t\treturn aName < bName;\n\t});\n\n\tstd::sort(modules_.begin(), modules_.end(), [](const ModuleInfo &a, const ModuleInfo &b) {\n\t\tbool missingA = !obs_get_module(a.module_name.c_str()) &&\n\t\t\t\t!obs_get_disabled_module(a.module_name.c_str());\n\t\tbool missingB = !obs_get_module(b.module_name.c_str()) &&\n\t\t\t\t!obs_get_disabled_module(b.module_name.c_str());\n\n\t\treturn !missingA && missingB;\n\t});\n\n\tint row = 0;\n\tint missingIndex = -1;\n\tfor (auto &metadata : modules_) {\n\t\tstd::string id = metadata.module_name;\n\t\t// Check if the module is missing:\n\t\tbool missing = !obs_get_module(id.c_str()) && !obs_get_disabled_module(id.c_str());\n\n\t\tQString name = !metadata.display_name.empty() ? metadata.display_name.c_str()\n\t\t\t\t\t\t\t      : metadata.module_name.c_str();\n\n\t\tif (missing && missingIndex == -1) {\n\t\t\tmissingIndex = row;\n\t\t}\n\n\t\tauto item = new QCheckBox(name);\n\t\titem->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);\n\t\titem->setChecked(metadata.enabled);\n\n\t\tif (!metadata.enabledAtLaunch || missing) {\n\t\t\titem->setProperty(\"class\", \"text-muted\");\n\t\t}\n\n\t\tui->modulesList->layout()->addWidget(item);\n\n\t\tconnect(item, &QCheckBox::toggled, this, [this, row](bool checked) {\n\t\t\tmodules_[row].enabled = checked;\n\t\t\tui->manageRestartLabel->setVisible(isEnabledPluginsChanged());\n\t\t});\n\n\t\trow++;\n\t}\n\n\tQVBoxLayout *layout = qobject_cast<QVBoxLayout *>(ui->modulesList->layout());\n\tif (safe_mode) {\n\t\tQLabel *safeModeLabel = new QLabel(ui->modulesList);\n\t\tsafeModeLabel->setText(QTStr(\"PluginManager.SafeMode\"));\n\t\tsafeModeLabel->setProperty(\"class\", \"text-muted text-italic\");\n\t\tsafeModeLabel->setIndent(0);\n\n\t\tlayout->insertWidget(0, safeModeLabel);\n\t} else if (missingIndex != -1) {\n\t\tQLabel *missingLabel = new QLabel(ui->modulesList);\n\t\tmissingLabel->setText(QTStr(\"PluginManager.MissingPlugin\"));\n\t\tmissingLabel->setProperty(\"class\", \"text-warning text-bold\");\n\t\tmissingLabel->setIndent(0);\n\n\t\tlayout->insertWidget(missingIndex, new QLabel(\"\", ui->modulesList));\n\t\tlayout->insertWidget(missingIndex + 1, missingLabel);\n\t}\n\n\tui->modulesList->adjustSize();\n\tui->modulesListContents->adjustSize();\n\n\tui->manageRestartLabel->setVisible(isEnabledPluginsChanged());\n\n\tconnect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);\n\tconnect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);\n}\n\nvoid PluginManagerWindow::sectionSelectionChanged()\n{\n\tauto selected = ui->sectionList->selectedItems();\n\tif (selected.count() != 1) {\n\t\tsetSection(activeSectionIndex);\n\t} else {\n\t\tauto selectionIndex = ui->sectionList->indexFromItem(selected.first());\n\t\tsetSection(selectionIndex);\n\t}\n}\n\nvoid PluginManagerWindow::setSection(QPersistentModelIndex index)\n{\n\tif (ui->sectionList->itemFromIndex(index)) {\n\t\tactiveSectionIndex = index;\n\t\tui->sectionList->setCurrentIndex(index);\n\t}\n}\n\nbool PluginManagerWindow::isEnabledPluginsChanged()\n{\n\tbool result = false;\n\tfor (auto &metadata : modules_) {\n\t\tif (metadata.enabledAtLaunch != metadata.enabled) {\n\t\t\tresult = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn result;\n}\n\n}; // namespace OBS\n"
  },
  {
    "path": "frontend/plugin-manager/PluginManagerWindow.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by FiniteSingularity <finitesingularityttv@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_PluginManagerWindow.h\"\n#include \"PluginManager.hpp\"\n\n#include <QDialog>\n#include <QWidget>\n\nnamespace OBS {\n\nclass PluginManagerWindow : public QDialog {\n\tQ_OBJECT\n\tstd::unique_ptr<Ui::PluginManagerWindow> ui;\n\npublic:\n\texplicit PluginManagerWindow(std::vector<ModuleInfo> const &modules, QWidget *parent = nullptr);\n\tinline std::vector<ModuleInfo> const result() { return modules_; }\n\nprivate:\n\tstd::vector<ModuleInfo> modules_;\n\n\tvoid sectionSelectionChanged();\n\tQPersistentModelIndex activeSectionIndex;\n\tvoid setSection(QPersistentModelIndex index);\n\n\tbool isEnabledPluginsChanged();\n};\n\n}; // namespace OBS\n"
  },
  {
    "path": "frontend/plugins/CMakeLists.txt",
    "content": "add_subdirectory(aja-output-ui)\nadd_subdirectory(decklink-captions)\nadd_subdirectory(decklink-output-ui)\nadd_subdirectory(frontend-tools)\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/AJAOutputUI.cpp",
    "content": "#include \"AJAOutputUI.h\"\n#include \"aja-ui-main.h\"\n\n#include \"../../../plugins/aja/aja-common.hpp\"\n#include \"../../../plugins/aja/aja-ui-props.hpp\"\n#include \"../../../plugins/aja/aja-enums.hpp\"\n#include \"../../../plugins/aja/aja-card-manager.hpp\"\n\n#include <ajantv2/includes/ntv2card.h>\n#include <ajantv2/includes/ntv2devicefeatures.h>\n#include <ajantv2/includes/ntv2enums.h>\n#include <ajantv2/includes/ntv2utils.h>\n\n#include <obs-module.h>\n#include <util/platform.h>\n#include <util/util.hpp>\n\nAJAOutputUI::AJAOutputUI(QWidget *parent) : QDialog(parent), ui(new Ui_Output)\n{\n\tui->setupUi(this);\n\tsetSizeGripEnabled(true);\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tpropertiesView = nullptr;\n\tpreviewPropertiesView = nullptr;\n\tmiscPropertiesView = nullptr;\n}\n\nvoid AJAOutputUI::ShowHideDialog()\n{\n\tSetupPropertiesView();\n\tSetupPreviewPropertiesView();\n\tSetupMiscPropertiesView();\n\n\tsetVisible(!isVisible());\n}\n\nvoid AJAOutputUI::SetupPropertiesView()\n{\n\tif (propertiesView)\n\t\tdelete propertiesView;\n\n\tobs_data_t *settings = obs_data_create();\n\tOBSData data = load_settings(kProgramPropsFilename);\n\tif (data) {\n\t\tobs_data_apply(settings, data);\n\t} else {\n\t\t// apply default settings\n\t\tobs_data_set_default_int(settings, kUIPropOutput.id, static_cast<long long>(IOSelection::Invalid));\n\t\tobs_data_set_default_int(settings, kUIPropVideoFormatSelect.id,\n\t\t\t\t\t static_cast<long long>(kDefaultAJAVideoFormat));\n\t\tobs_data_set_default_int(settings, kUIPropPixelFormatSelect.id,\n\t\t\t\t\t static_cast<long long>(kDefaultAJAPixelFormat));\n\t\tobs_data_set_default_int(settings, kUIPropSDITransport.id,\n\t\t\t\t\t static_cast<long long>(kDefaultAJASDITransport));\n\t\tobs_data_set_default_int(settings, kUIPropSDITransport4K.id,\n\t\t\t\t\t static_cast<long long>(kDefaultAJASDITransport4K));\n\t}\n\n\t// Assign an ID to the program output plugin instance for channel usage tracking\n\tobs_data_set_string(settings, kUIPropAJAOutputID.id, kProgramOutputID);\n\n\tpropertiesView =\n\t\tnew OBSPropertiesView(settings, \"aja_output\", (PropertiesReloadCallback)obs_get_output_properties, 170);\n\n\tui->propertiesLayout->addWidget(propertiesView);\n\tobs_data_release(settings);\n\n\tconnect(propertiesView, &OBSPropertiesView::Changed, this, &AJAOutputUI::PropertiesChanged);\n}\n\nvoid AJAOutputUI::SaveSettings(const char *filename, obs_data_t *settings)\n{\n\tBPtr<char> modulePath = obs_module_get_config_path(obs_current_module(), \"\");\n\n\tos_mkdirs(modulePath);\n\n\tBPtr<char> path = obs_module_get_config_path(obs_current_module(), filename);\n\n\tif (settings)\n\t\tobs_data_save_json_safe(settings, path, \"tmp\", \"bak\");\n}\n\nvoid AJAOutputUI::SetupPreviewPropertiesView()\n{\n\tif (previewPropertiesView)\n\t\tdelete previewPropertiesView;\n\n\tobs_data_t *settings = obs_data_create();\n\n\tOBSData data = load_settings(kPreviewPropsFilename);\n\tif (data) {\n\t\tobs_data_apply(settings, data);\n\t} else {\n\t\t// apply default settings\n\t\tobs_data_set_default_int(settings, kUIPropOutput.id, static_cast<long long>(IOSelection::Invalid));\n\t\tobs_data_set_default_int(settings, kUIPropVideoFormatSelect.id,\n\t\t\t\t\t static_cast<long long>(kDefaultAJAVideoFormat));\n\t\tobs_data_set_default_int(settings, kUIPropPixelFormatSelect.id,\n\t\t\t\t\t static_cast<long long>(kDefaultAJAPixelFormat));\n\t\tobs_data_set_default_int(settings, kUIPropSDITransport.id,\n\t\t\t\t\t static_cast<long long>(kDefaultAJASDITransport));\n\t\tobs_data_set_default_int(settings, kUIPropSDITransport4K.id,\n\t\t\t\t\t static_cast<long long>(kDefaultAJASDITransport4K));\n\t}\n\n\t// Assign an ID to the program output plugin instance for channel usage tracking\n\tobs_data_set_string(settings, kUIPropAJAOutputID.id, kPreviewOutputID);\n\n\tpreviewPropertiesView =\n\t\tnew OBSPropertiesView(settings, \"aja_output\", (PropertiesReloadCallback)obs_get_output_properties, 170);\n\n\tui->previewPropertiesLayout->addWidget(previewPropertiesView);\n\tobs_data_release(settings);\n\n\tconnect(previewPropertiesView, &OBSPropertiesView::Changed, this, &AJAOutputUI::PreviewPropertiesChanged);\n}\n\nvoid AJAOutputUI::on_outputButton_clicked()\n{\n\tSaveSettings(kProgramPropsFilename, propertiesView->GetSettings());\n\toutput_toggle();\n}\n\nvoid AJAOutputUI::PropertiesChanged()\n{\n\tSaveSettings(kProgramPropsFilename, propertiesView->GetSettings());\n}\n\nvoid AJAOutputUI::OutputStateChanged(bool active)\n{\n\tQString text;\n\tif (active) {\n\t\ttext = QString(obs_module_text(\"Stop\"));\n\t} else {\n\t\ttext = QString(obs_module_text(\"Start\"));\n\t}\n\n\tui->outputButton->setChecked(active);\n\tui->outputButton->setText(text);\n}\n\nvoid AJAOutputUI::on_previewOutputButton_clicked()\n{\n\tSaveSettings(kPreviewPropsFilename, previewPropertiesView->GetSettings());\n\tpreview_output_toggle();\n}\n\nvoid AJAOutputUI::PreviewPropertiesChanged()\n{\n\tSaveSettings(kPreviewPropsFilename, previewPropertiesView->GetSettings());\n}\n\nvoid AJAOutputUI::PreviewOutputStateChanged(bool active)\n{\n\tQString text;\n\tif (active) {\n\t\ttext = QString(obs_module_text(\"Stop\"));\n\t} else {\n\t\ttext = QString(obs_module_text(\"Start\"));\n\t}\n\n\tui->previewOutputButton->setChecked(active);\n\tui->previewOutputButton->setText(text);\n}\n\nstatic obs_properties_t *create_misc_props_ui(void *vp)\n{\n\tAJAOutputUI *outputUI = (AJAOutputUI *)vp;\n\tif (!outputUI)\n\t\treturn nullptr;\n\taja::CardManager *cardManager = outputUI->GetCardManager();\n\tif (!cardManager)\n\t\treturn nullptr;\n\n\tbool haveMultiView = false;\n\tfor (auto &c : *cardManager) {\n\t\tauto deviceID = c.second->GetDeviceID();\n\t\tfor (const auto &id : aja::MultiViewCards()) {\n\t\t\tif (deviceID == id) {\n\t\t\t\thaveMultiView = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tobs_properties_t *props = obs_properties_create();\n\tobs_property_t *deviceList = obs_properties_add_list(props, kUIPropDevice.id,\n\t\t\t\t\t\t\t     obs_module_text(kUIPropDevice.text), OBS_COMBO_TYPE_LIST,\n\t\t\t\t\t\t\t     OBS_COMBO_FORMAT_STRING);\n\tobs_property_t *multiViewEnable =\n\t\tobs_properties_add_bool(props, kUIPropMultiViewEnable.id, obs_module_text(kUIPropMultiViewEnable.text));\n\tobs_property_t *multiViewAudioSources = obs_properties_add_list(\n\t\tprops, kUIPropMultiViewAudioSource.id, obs_module_text(kUIPropMultiViewAudioSource.text),\n\t\tOBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);\n\n\tobs_property_list_clear(deviceList);\n\tobs_property_list_clear(multiViewAudioSources);\n\n\tNTV2DeviceID firstDeviceID = DEVICE_ID_NOTFOUND;\n\tpopulate_misc_device_list(deviceList, cardManager, firstDeviceID);\n\tpopulate_multi_view_audio_sources(multiViewAudioSources, firstDeviceID);\n\tobs_property_set_modified_callback2(deviceList, on_misc_device_selected, cardManager);\n\tobs_property_set_modified_callback2(multiViewEnable, on_multi_view_toggle, cardManager);\n\tobs_property_set_modified_callback2(multiViewAudioSources, on_multi_view_toggle, cardManager);\n\n\toutputUI->ui->label_3->setVisible(haveMultiView);\n\tobs_property_set_visible(deviceList, haveMultiView);\n\tobs_property_set_visible(multiViewEnable, haveMultiView);\n\tobs_property_set_visible(multiViewAudioSources, haveMultiView);\n\n\treturn props;\n}\n\nvoid AJAOutputUI::MiscPropertiesChanged()\n{\n\tSaveSettings(kMiscPropsFilename, miscPropertiesView->GetSettings());\n}\n\nvoid AJAOutputUI::SetCardManager(aja::CardManager *cm)\n{\n\tcardManager = cm;\n}\n\naja::CardManager *AJAOutputUI::GetCardManager()\n{\n\treturn cardManager;\n}\n\nvoid AJAOutputUI::SetupMiscPropertiesView()\n{\n\tif (miscPropertiesView)\n\t\tdelete miscPropertiesView;\n\n\tobs_data_t *settings = obs_data_create();\n\tOBSData data = load_settings(kMiscPropsFilename);\n\tif (data) {\n\t\tobs_data_apply(settings, data);\n\t}\n\n\tmiscPropertiesView = new OBSPropertiesView(settings, this, (PropertiesReloadCallback)create_misc_props_ui,\n\t\t\t\t\t\t   nullptr, nullptr, 170);\n\n\tui->miscPropertiesLayout->addWidget(miscPropertiesView);\n\tobs_data_release(settings);\n\tconnect(miscPropertiesView, &OBSPropertiesView::Changed, this, &AJAOutputUI::MiscPropertiesChanged);\n}\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/AJAOutputUI.h",
    "content": "#pragma once\n\n#include <QDialog>\n#include <properties-view.hpp>\n\n#include \"ui_output.h\"\n\nnamespace aja {\nclass CardManager;\n}\n\nclass AJAOutputUI : public QDialog {\n\tQ_OBJECT\nprivate:\n\tOBSPropertiesView *propertiesView;\n\tOBSPropertiesView *previewPropertiesView;\n\tOBSPropertiesView *miscPropertiesView;\n\taja::CardManager *cardManager;\npublic slots:\n\tvoid on_outputButton_clicked();\n\tvoid PropertiesChanged();\n\tvoid OutputStateChanged(bool);\n\n\tvoid on_previewOutputButton_clicked();\n\tvoid PreviewPropertiesChanged();\n\tvoid PreviewOutputStateChanged(bool);\n\n\tvoid MiscPropertiesChanged();\n\npublic:\n\tstd::unique_ptr<Ui_Output> ui;\n\tAJAOutputUI(QWidget *parent);\n\n\tvoid SetCardManager(aja::CardManager *cm);\n\taja::CardManager *GetCardManager();\n\n\tvoid ShowHideDialog();\n\tvoid SaveSettings(const char *filename, obs_data_t *settings);\n\tvoid SetupPropertiesView();\n\tvoid SetupPreviewPropertiesView();\n\tvoid SetupMiscPropertiesView();\n};\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nif(NOT ENABLE_AJA)\n  target_disable(aja-output-ui)\n  return()\nendif()\n\nfind_package(LibAJANTV2 REQUIRED)\n\nfind_package(Qt6 REQUIRED Widgets)\n\nif(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD)\n  find_package(X11 REQUIRED)\nendif()\n\nif(NOT TARGET OBS::aja-support)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/plugins/aja\" \"${CMAKE_BINARY_DIR}/plugins/aja\")\nendif()\n\nif(NOT TARGET OBS::properties-view)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/shared/properties-view\" \"${CMAKE_BINARY_DIR}/shared/properties-view\")\nendif()\n\nadd_library(aja-output-ui MODULE)\nadd_library(OBS::aja-output-ui ALIAS aja-output-ui)\n\ntarget_sources(aja-output-ui PRIVATE aja-ui-main.cpp aja-ui-main.h AJAOutputUI.cpp AJAOutputUI.h)\n\ntarget_sources(aja-output-ui PRIVATE forms/output.ui)\n\ntarget_compile_options(\n  aja-output-ui\n  PRIVATE\n    $<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:-Wno-deprecated-declarations>\n    $<$<COMPILE_LANG_AND_ID:CXX,AppleClang,Clang>:-Wno-quoted-include-in-framework-header>\n)\n\ntarget_link_libraries(\n  aja-output-ui\n  PRIVATE\n    OBS::libobs\n    OBS::aja-support\n    OBS::frontend-api\n    OBS::properties-view\n    Qt::Widgets\n    AJA::LibAJANTV2\n    $<$<PLATFORM_ID:Linux,FreeBSD,OpenBSD>:X11::X11>\n)\n\ntarget_link_options(aja-output-ui PRIVATE $<$<PLATFORM_ID:Windows>:/IGNORE:4099>)\n\nif(OS_WINDOWS)\n  configure_file(cmake/windows/obs-module.rc.in aja-output-ui.rc)\n  target_sources(aja-output-ui PRIVATE aja-output-ui.rc)\n\n  set_property(TARGET aja-output-ui APPEND PROPERTY AUTORCC_OPTIONS --format-version 1)\nendif()\n\nset_target_properties_obs(\n  aja-output-ui\n  PROPERTIES FOLDER frontend\n             PREFIX \"\"\n             AUTOMOC ON\n             AUTOUIC ON\n             AUTORCC ON\n             AUTOUIC_SEARCH_PATHS forms\n)\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/aja-ui-main.cpp",
    "content": "#include \"aja-ui-main.h\"\n#include \"AJAOutputUI.h\"\n\n#include \"../../../plugins/aja/aja-ui-props.hpp\"\n#include \"../../../plugins/aja/aja-card-manager.hpp\"\n#include \"../../../plugins/aja/aja-routing.hpp\"\n\n#include <obs-module.h>\n#include <obs-frontend-api.h>\n#include <QMainWindow>\n#include <QAction>\n#include <util/util.hpp>\n#include <util/platform.h>\n#include <media-io/video-io.h>\n#include <media-io/video-frame.h>\n#include <ajantv2/includes/ntv2devicescanner.h>\n\nOBS_DECLARE_MODULE()\nOBS_MODULE_USE_DEFAULT_LOCALE(\"aja-output-ui\", \"en-US\")\n\nstatic aja::CardManager *cardManager = nullptr;\nstatic AJAOutputUI *ajaOutputUI = nullptr;\nstatic obs_output_t *output = nullptr;\n\nbool main_output_running = false;\nbool preview_output_running = false;\n\nstruct preview_output {\n\tbool enabled;\n\tobs_source_t *current_source;\n\tobs_output_t *output;\n\n\tvideo_t *video_queue;\n\tgs_texrender_t *texrender;\n\tgs_stagesurf_t *stagesurface;\n\tuint8_t *video_data;\n\tuint32_t video_linesize;\n\n\tobs_video_info ovi;\n};\n\nstatic struct preview_output context = {0};\n\nOBSData load_settings(const char *filename)\n{\n\tBPtr<char> path = obs_module_get_config_path(obs_current_module(), filename);\n\tBPtr<char> jsonData = os_quick_read_utf8_file(path);\n\tif (!!jsonData) {\n\t\tobs_data_t *data = obs_data_create_from_json(jsonData);\n\t\tOBSData dataRet(data);\n\t\tobs_data_release(data);\n\n\t\treturn dataRet;\n\t}\n\n\treturn nullptr;\n}\n\nvoid output_stop()\n{\n\tobs_output_stop(output);\n\tobs_output_release(output);\n\tmain_output_running = false;\n\tajaOutputUI->OutputStateChanged(false);\n}\n\nvoid output_start()\n{\n\tOBSData settings = load_settings(kProgramPropsFilename);\n\n\tif (settings != nullptr) {\n\t\toutput = obs_output_create(\"aja_output\", kProgramOutputID, settings, NULL);\n\n\t\tbool started = obs_output_start(output);\n\t\tobs_data_release(settings);\n\n\t\tmain_output_running = started;\n\n\t\tajaOutputUI->OutputStateChanged(started);\n\n\t\tif (!started)\n\t\t\toutput_stop();\n\t}\n}\n\nvoid output_toggle()\n{\n\tif (main_output_running)\n\t\toutput_stop();\n\telse\n\t\toutput_start();\n}\n\nvoid on_preview_scene_changed(enum obs_frontend_event event, void *param);\nvoid render_preview_source(void *param, uint32_t cx, uint32_t cy);\n\nvoid preview_output_stop()\n{\n\tobs_output_stop(context.output);\n\tobs_output_release(context.output);\n\n\tobs_remove_main_render_callback(render_preview_source, &context);\n\tobs_frontend_remove_event_callback(on_preview_scene_changed, &context);\n\n\tobs_source_release(context.current_source);\n\n\tobs_enter_graphics();\n\tgs_stagesurface_destroy(context.stagesurface);\n\tgs_texrender_destroy(context.texrender);\n\tobs_leave_graphics();\n\n\tvideo_output_close(context.video_queue);\n\n\tpreview_output_running = false;\n\tajaOutputUI->PreviewOutputStateChanged(false);\n}\n\nvoid preview_output_start()\n{\n\tOBSData settings = load_settings(kPreviewPropsFilename);\n\n\tif (settings != nullptr) {\n\t\tcontext.output = obs_output_create(\"aja_output\", kPreviewOutputID, settings, NULL);\n\n\t\tobs_get_video_info(&context.ovi);\n\n\t\tuint32_t width = context.ovi.base_width;\n\t\tuint32_t height = context.ovi.base_height;\n\n\t\tobs_enter_graphics();\n\t\tcontext.texrender = gs_texrender_create(GS_BGRA, GS_ZS_NONE);\n\t\tcontext.stagesurface = gs_stagesurface_create(width, height, GS_BGRA);\n\t\tobs_leave_graphics();\n\n\t\tconst video_output_info *mainVOI = video_output_get_info(obs_get_video());\n\n\t\tvideo_output_info vi = {0};\n\t\tvi.format = VIDEO_FORMAT_BGRA;\n\t\tvi.width = width;\n\t\tvi.height = height;\n\t\tvi.fps_den = context.ovi.fps_den;\n\t\tvi.fps_num = context.ovi.fps_num;\n\t\tvi.cache_size = 16;\n\t\tvi.colorspace = mainVOI->colorspace;\n\t\tvi.range = mainVOI->range;\n\t\tvi.name = kPreviewOutputID;\n\n\t\tvideo_output_open(&context.video_queue, &vi);\n\n\t\tobs_frontend_add_event_callback(on_preview_scene_changed, &context);\n\t\tif (obs_frontend_preview_program_mode_active()) {\n\t\t\tcontext.current_source = obs_frontend_get_current_preview_scene();\n\t\t} else {\n\t\t\tcontext.current_source = obs_frontend_get_current_scene();\n\t\t}\n\t\tobs_add_main_render_callback(render_preview_source, &context);\n\n\t\tobs_output_set_media(context.output, context.video_queue, obs_get_audio());\n\t\tbool started = obs_output_start(context.output);\n\n\t\tobs_data_release(settings);\n\n\t\tpreview_output_running = started;\n\t\tajaOutputUI->PreviewOutputStateChanged(started);\n\n\t\tif (!started)\n\t\t\tpreview_output_stop();\n\t}\n}\n\nvoid preview_output_toggle()\n{\n\tif (preview_output_running)\n\t\tpreview_output_stop();\n\telse\n\t\tpreview_output_start();\n}\n\nvoid populate_misc_device_list(obs_property_t *list, aja::CardManager *cardManager, NTV2DeviceID &firstDeviceID)\n{\n\tfor (const auto &iter : *cardManager) {\n\t\tif (!iter.second)\n\t\t\tcontinue;\n\t\tif (firstDeviceID == DEVICE_ID_NOTFOUND)\n\t\t\tfirstDeviceID = iter.second->GetDeviceID();\n\t\tobs_property_list_add_string(list, iter.second->GetDisplayName().c_str(),\n\t\t\t\t\t     iter.second->GetCardID().c_str());\n\t}\n}\n\nvoid populate_multi_view_audio_sources(obs_property_t *list, NTV2DeviceID id)\n{\n\tobs_property_list_clear(list);\n\tconst QList<NTV2InputSource> kMultiViewAudioInputs = {\n\t\tNTV2_INPUTSOURCE_SDI1,\n\t\tNTV2_INPUTSOURCE_SDI2,\n\t\tNTV2_INPUTSOURCE_SDI3,\n\t\tNTV2_INPUTSOURCE_SDI4,\n\t};\n\tfor (const auto &inp : kMultiViewAudioInputs) {\n\t\tif (NTV2DeviceCanDoInputSource(id, inp)) {\n\t\t\tstd::string inputSourceStr = NTV2InputSourceToString(inp, true);\n\t\t\tobs_property_list_add_int(list, inputSourceStr.c_str(), (long long)inp);\n\t\t}\n\t}\n}\n\nbool on_misc_device_selected(void *data, obs_properties_t *props, obs_property_t *, obs_data_t *settings)\n{\n\tconst char *cardID = obs_data_get_string(settings, kUIPropDevice.id);\n\tif (!cardID || !cardID[0])\n\t\treturn false;\n\taja::CardManager *cardManager = (aja::CardManager *)data;\n\tif (!cardManager)\n\t\treturn false;\n\tauto cardEntry = cardManager->GetCardEntry(cardID);\n\tif (!cardEntry)\n\t\treturn false;\n\n\tNTV2DeviceID deviceID = cardEntry->GetDeviceID();\n\tbool enableMultiViewUI = NTV2DeviceCanDoHDMIMultiView(deviceID);\n\tobs_property_t *multiViewCheckbox = obs_properties_get(props, kUIPropMultiViewEnable.id);\n\tobs_property_t *multiViewAudioSource = obs_properties_get(props, kUIPropMultiViewAudioSource.id);\n\tpopulate_multi_view_audio_sources(multiViewAudioSource, deviceID);\n\tobs_property_set_enabled(multiViewCheckbox, enableMultiViewUI);\n\tobs_property_set_enabled(multiViewAudioSource, enableMultiViewUI);\n\treturn true;\n}\n\nstatic void toggle_multi_view(CNTV2Card *card, NTV2InputSource src, bool enable)\n{\n\tstd::ostringstream oss;\n\tfor (int i = 0; i < 4; i++) {\n\t\tstd::string datastream = std::to_string(i);\n\t\toss << \"sdi[\" << datastream << \"][0]->hdmi[0][\" << datastream << \"];\";\n\t}\n\n\tNTV2DeviceID deviceId = card->GetDeviceID();\n\tNTV2AudioSystem audioSys = NTV2InputSourceToAudioSystem(src);\n\tif (NTV2DeviceCanDoHDMIMultiView(deviceId)) {\n\t\tNTV2XptConnections cnx;\n\t\tif (aja::Routing::ParseRouteString(oss.str(), cnx)) {\n\t\t\tcard->SetMultiRasterBypassEnable(!enable);\n\t\t\tif (enable) {\n\t\t\t\tcard->ApplySignalRoute(cnx, false);\n\t\t\t\tif (NTV2DeviceCanDoAudioMixer(deviceId)) {\n\t\t\t\t\tcard->SetAudioMixerInputAudioSystem(NTV2_AudioMixerInputMain, audioSys);\n\t\t\t\t\tcard->SetAudioMixerInputChannelSelect(NTV2_AudioMixerInputMain,\n\t\t\t\t\t\t\t\t\t      NTV2_AudioChannel1_2);\n\t\t\t\t\tcard->SetAudioMixerInputChannelsMute(NTV2_AudioMixerInputAux1,\n\t\t\t\t\t\t\t\t\t     NTV2AudioChannelsMuteAll);\n\t\t\t\t\tcard->SetAudioMixerInputChannelsMute(NTV2_AudioMixerInputAux2,\n\t\t\t\t\t\t\t\t\t     NTV2AudioChannelsMuteAll);\n\t\t\t\t}\n\t\t\t\tcard->SetAudioLoopBack(NTV2_AUDIO_LOOPBACK_ON, audioSys);\n\t\t\t\tcard->SetAudioOutputMonitorSource(NTV2_AudioChannel1_2, audioSys);\n\t\t\t\tcard->SetHDMIOutAudioChannels(NTV2_HDMIAudio8Channels);\n\t\t\t\tcard->SetHDMIOutAudioSource2Channel(NTV2_AudioChannel1_2, audioSys);\n\t\t\t\tcard->SetHDMIOutAudioSource8Channel(NTV2_AudioChannel1_8, audioSys);\n\t\t\t} else {\n\t\t\t\tcard->RemoveConnections(cnx);\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool on_multi_view_toggle(void *data, obs_properties_t *, obs_property_t *, obs_data_t *settings)\n{\n\tbool multiViewEnabled = obs_data_get_bool(settings, kUIPropMultiViewEnable.id) && !main_output_running &&\n\t\t\t\t!preview_output_running;\n\tconst int audioInputSource = obs_data_get_int(settings, kUIPropMultiViewAudioSource.id);\n\tconst char *cardID = obs_data_get_string(settings, kUIPropDevice.id);\n\tif (!cardID || !cardID[0])\n\t\treturn false;\n\taja::CardManager *cardManager = (aja::CardManager *)data;\n\tif (!cardManager)\n\t\treturn false;\n\tCNTV2Card *card = cardManager->GetCard(cardID);\n\tif (!card)\n\t\treturn false;\n\n\tNTV2InputSource inputSource = (NTV2InputSource)audioInputSource;\n\ttoggle_multi_view(card, inputSource, multiViewEnabled);\n\n\treturn true;\n}\n\n// MISC PROPS\n\nvoid on_preview_scene_changed(enum obs_frontend_event event, void *param)\n{\n\tauto ctx = (struct preview_output *)param;\n\tswitch (event) {\n\tcase OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED:\n\tcase OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:\n\t\tobs_source_release(ctx->current_source);\n\t\tctx->current_source = obs_frontend_get_current_preview_scene();\n\t\tbreak;\n\tcase OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED:\n\t\tobs_source_release(ctx->current_source);\n\t\tctx->current_source = obs_frontend_get_current_scene();\n\t\tbreak;\n\tcase OBS_FRONTEND_EVENT_SCENE_CHANGED:\n\t\tif (!obs_frontend_preview_program_mode_active()) {\n\t\t\tobs_source_release(ctx->current_source);\n\t\t\tctx->current_source = obs_frontend_get_current_scene();\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid render_preview_source(void *param, uint32_t, uint32_t)\n{\n\tauto ctx = (struct preview_output *)param;\n\n\tif (!ctx->current_source)\n\t\treturn;\n\n\tuint32_t width = obs_source_get_base_width(ctx->current_source);\n\tuint32_t height = obs_source_get_base_height(ctx->current_source);\n\n\tgs_texrender_reset(ctx->texrender);\n\n\tif (gs_texrender_begin(ctx->texrender, width, height)) {\n\t\tstruct vec4 background;\n\t\tvec4_zero(&background);\n\n\t\tgs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0);\n\t\tgs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f);\n\n\t\tgs_blend_state_push();\n\t\tgs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);\n\n\t\tobs_source_video_render(ctx->current_source);\n\n\t\tgs_blend_state_pop();\n\t\tgs_texrender_end(ctx->texrender);\n\n\t\tstruct video_frame output_frame;\n\t\tif (video_output_lock_frame(ctx->video_queue, &output_frame, 1, os_gettime_ns())) {\n\t\t\tgs_stage_texture(ctx->stagesurface, gs_texrender_get_texture(ctx->texrender));\n\n\t\t\tif (gs_stagesurface_map(ctx->stagesurface, &ctx->video_data, &ctx->video_linesize)) {\n\t\t\t\tuint32_t linesize = output_frame.linesize[0];\n\t\t\t\tfor (uint32_t i = 0; i < ctx->ovi.base_height; i++) {\n\t\t\t\t\tuint32_t dst_offset = linesize * i;\n\t\t\t\t\tuint32_t src_offset = ctx->video_linesize * i;\n\t\t\t\t\tmemcpy(output_frame.data[0] + dst_offset, ctx->video_data + src_offset,\n\t\t\t\t\t       linesize);\n\t\t\t\t}\n\n\t\t\t\tgs_stagesurface_unmap(ctx->stagesurface);\n\t\t\t\tctx->video_data = nullptr;\n\t\t\t}\n\n\t\t\tvideo_output_unlock_frame(ctx->video_queue);\n\t\t}\n\t}\n}\n\nvoid addOutputUI(void)\n{\n\tQAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(\"AJA I/O Device Output\");\n\n\tQMainWindow *window = (QMainWindow *)obs_frontend_get_main_window();\n\n\tobs_frontend_push_ui_translation(obs_module_get_string);\n\tajaOutputUI = new AJAOutputUI(window);\n\tobs_frontend_pop_ui_translation();\n\n\tauto cb = []() {\n\t\tajaOutputUI->ShowHideDialog();\n\t};\n\n\tQObject::connect(action, &QAction::triggered, action, cb);\n}\n\nstatic void OBSEvent(enum obs_frontend_event event, void *)\n{\n\tif (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {\n\t\tOBSData settings = load_settings(kProgramPropsFilename);\n\t\tif (settings && obs_data_get_bool(settings, kUIPropAutoStartOutput.id))\n\t\t\toutput_start();\n\n\t\tOBSData previewSettings = load_settings(kPreviewPropsFilename);\n\t\tif (previewSettings && obs_data_get_bool(previewSettings, kUIPropAutoStartOutput.id))\n\t\t\tpreview_output_start();\n\n\t\tOBSData miscSettings = load_settings(kMiscPropsFilename);\n\t\tif (miscSettings && ajaOutputUI) {\n\t\t\ton_multi_view_toggle(ajaOutputUI->GetCardManager(), nullptr, nullptr, miscSettings);\n\t\t}\n\t} else if (event == OBS_FRONTEND_EVENT_EXIT) {\n\t\tif (main_output_running)\n\t\t\toutput_stop();\n\t\tif (preview_output_running)\n\t\t\tpreview_output_stop();\n\t}\n}\n\nstatic void aja_loaded(void * /* data */, calldata_t *calldata)\n{\n\t// Receive CardManager pointer from the main AJA plugin\n\tcalldata_get_ptr(calldata, \"card_manager\", &cardManager);\n\tif (ajaOutputUI)\n\t\tajaOutputUI->SetCardManager(cardManager);\n}\n\nbool obs_module_load(void)\n{\n\tCNTV2DeviceScanner scanner;\n\tauto numDevices = scanner.GetNumDevices();\n\tif (numDevices == 0) {\n\t\tblog(LOG_WARNING, \"No AJA devices found, skipping loading AJA UI plugin\");\n\t\treturn false;\n\t}\n\n\t// Signal to wait for AJA plugin to finish loading so we can access the CardManager instance\n\tauto signal_handler = obs_get_signal_handler();\n\tsignal_handler_add(signal_handler, \"void aja_loaded(ptr card_manager)\");\n\tsignal_handler_connect(signal_handler, \"aja_loaded\", aja_loaded, nullptr);\n\n\taddOutputUI();\n\n\tobs_frontend_add_event_callback(OBSEvent, nullptr);\n\n\treturn true;\n}\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/aja-ui-main.h",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <ajantv2/includes/ntv2enums.h>\nnamespace aja {\nclass CardManager;\n}\n\nstatic const char *kProgramPropsFilename = \"ajaOutputProps.json\";\nstatic const char *kPreviewPropsFilename = \"ajaPreviewOutputProps.json\";\nstatic const char *kMiscPropsFilename = \"ajaMiscProps.json\";\n\nOBSData load_settings(const char *filename);\nvoid output_toggle();\nvoid preview_output_toggle();\nvoid populate_misc_device_list(obs_property_t *list, aja::CardManager *cardManager, NTV2DeviceID &firstDeviceID);\nvoid populate_multi_view_audio_sources(obs_property_t *list, NTV2DeviceID id);\nbool on_misc_device_selected(void *data, obs_properties_t *props, obs_property_t *list, obs_data_t *settings);\nbool on_multi_view_toggle(void *data, obs_properties_t *props, obs_property_t *list, obs_data_t *settings);\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/cmake/windows/obs-module.rc.in",
    "content": "1 VERSIONINFO\nFILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"040904B0\"\n    BEGIN\n      VALUE \"CompanyName\", \"${OBS_COMPANY_NAME}\"\n      VALUE \"FileDescription\", \"OBS AJA Output UI\"\n      VALUE \"FileVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"ProductName\", \"${OBS_PRODUCT_NAME}\"\n      VALUE \"ProductVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"Comments\", \"${OBS_COMMENTS}\"\n      VALUE \"LegalCopyright\", \"${OBS_LEGAL_COPYRIGHT}\"\n      VALUE \"InternalName\", \"aja-output-ui\"\n      VALUE \"OriginalFilename\", \"aja-output-ui\"\n    END\n  END\n\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0409, 0x04B0\n  END\nEND\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/af-ZA.ini",
    "content": "AJAOutput.Device=\"AJA I/O-toestelafvoer\"\nAJAOutput.ProgramOutput=\"Programafvoer\"\nAJAOutput.PreviewOutput=\"Voorskou-afvoer\"\nAJAOutput.MiscOutput=\"Bykomende instellings\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/ar-SA.ini",
    "content": "AJAOutput.Device=\"مَخرج جهاز AJA I/O\"\nAJAOutput.ProgramOutput=\"مَخرج البرنامج\"\nAJAOutput.PreviewOutput=\"مَخرج المعاينة\"\nAJAOutput.MiscOutput=\"إعدادات إضافية\"\nAJAOutput.MultiViewEnable=\"تمكين عرض متعدد\"\nAJAOutput.MultiViewAudioSource=\"مصدر الصوت للعرض المتعدد\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/az-AZ.ini",
    "content": "AJAOutput.Device=\"AJA I/O Qurğu Çıxışı\"\nAJAOutput.ProgramOutput=\"Proqram Çıxışı\"\nAJAOutput.PreviewOutput=\"Önizləmə Çıxışı\"\nAJAOutput.MiscOutput=\"Əlavə Parametrlər\"\nAJAOutput.MultiViewEnable=\"Çoxlu Görünüşü Aktivləşdir\"\nAJAOutput.MultiViewAudioSource=\"Çox Görünüşlü Səs Mənbəyi\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/be-BY.ini",
    "content": "AJAOutput.Device=\"Захоп прылады AJA I/O\"\nAJAOutput.ProgramOutput=\"Праграмны вывад\"\nAJAOutput.PreviewOutput=\"Прадпрагляд вываду\"\nAJAOutput.MiscOutput=\"Дадатковыя налады\"\nAJAOutput.MultiViewEnable=\"Уключыць мультыпрагляд\"\nAJAOutput.MultiViewAudioSource=\"Мультыпрагляд крыніц аўдыя\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/bg-BG.ini",
    "content": "AJAOutput.Device=\"Изход на устройството AJA I/O\"\nAJAOutput.ProgramOutput=\"Програмен изход\"\nAJAOutput.PreviewOutput=\"Нагледно изхода\"\nAJAOutput.MiscOutput=\"Допълнителни настройки\"\nAJAOutput.MultiViewEnable=\"Включване на множко изгледи\"\nAJAOutput.MultiViewAudioSource=\"Източник на звука за множко изгледи\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/ca-ES.ini",
    "content": "AJAOutput.Device=\"Dispositiu de sortida d'E/S AJA\"\nAJAOutput.ProgramOutput=\"Sortida del programa\"\nAJAOutput.PreviewOutput=\"Sortida de la vista prèvia\"\nAJAOutput.MiscOutput=\"Configuració addicional\"\nAJAOutput.MultiViewEnable=\"Habilita la visualització múltiple\"\nAJAOutput.MultiViewAudioSource=\"Font d'àudio de visualització múltiple\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/cs-CZ.ini",
    "content": "AJAOutput.Device=\"Výstup AJA I/O zařízení\"\nAJAOutput.ProgramOutput=\"Výstup programu\"\nAJAOutput.PreviewOutput=\"Náhled výstupu\"\nAJAOutput.MiscOutput=\"Další nastavení\"\nAJAOutput.MultiViewEnable=\"Povolit Multiview\"\nAJAOutput.MultiViewAudioSource=\"Zdroj zvuku pro Multiview\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/da-DK.ini",
    "content": "AJAOutput.Device=\"AJA I/O-enhedsoutput\"\nAJAOutput.ProgramOutput=\"Programoutput\"\nAJAOutput.PreviewOutput=\"Forhåndsvisningsoutput\"\nAJAOutput.MiscOutput=\"Yderligere indstillinger\"\nAJAOutput.MultiViewEnable=\"Aktivér Multivisning\"\nAJAOutput.MultiViewAudioSource=\"Multivisning Lydkilde\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/de-DE.ini",
    "content": "AJAOutput.Device=\"AJA-I/O-Geräteausgabe\"\nAJAOutput.ProgramOutput=\"Programmausgabe\"\nAJAOutput.PreviewOutput=\"Vorschauausgabe\"\nAJAOutput.MiscOutput=\"Weitere Einstellungen\"\nAJAOutput.MultiViewEnable=\"Multiview aktivieren\"\nAJAOutput.MultiViewAudioSource=\"Multiview-Audioquelle\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/el-GR.ini",
    "content": "AJAOutput.Device=\"Συσκευή εξόδου I/O AJA\"\nAJAOutput.ProgramOutput=\"Έξοδος Προγράμματος\"\nAJAOutput.PreviewOutput=\"Προεπισκόπηση Εξόδου\"\nAJAOutput.MiscOutput=\"Πρόσθετες Ρυθμίσεις\"\nAJAOutput.MultiViewEnable=\"Ενεργοποίηση Πολλαπλής Προβολής\"\nAJAOutput.MultiViewAudioSource=\"Πηγή Ήχου Πολλαπλής Προβολής\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/en-GB.ini",
    "content": "#"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/en-US.ini",
    "content": "AJAOutput.Device=\"AJA I/O Device Output\"\nAJAOutput.ProgramOutput=\"Program Output\"\nAJAOutput.PreviewOutput=\"Preview Output\"\nAJAOutput.MiscOutput=\"Additional Settings\"\nAJAOutput.MultiViewEnable=\"Enable Multi View\"\nAJAOutput.MultiViewAudioSource=\"Multi View Audio Source\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/es-ES.ini",
    "content": "AJAOutput.Device=\"Salida de dispositivo de E/S AJA\"\nAJAOutput.ProgramOutput=\"Salida del programa\"\nAJAOutput.PreviewOutput=\"Salida de la vista previa\"\nAJAOutput.MiscOutput=\"Ajustes adicionales\"\nAJAOutput.MultiViewEnable=\"Habilitar vista múltiple\"\nAJAOutput.MultiViewAudioSource=\"Fuente de audio de la vista múltiple\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/et-EE.ini",
    "content": "AJAOutput.Device=\"AJA I/O seadme väljund\"\nAJAOutput.ProgramOutput=\"Programmi väljund\"\nAJAOutput.PreviewOutput=\"Eelvaate väljund\"\nAJAOutput.MiscOutput=\"Lisaseaded\"\nAJAOutput.MultiViewEnable=\"Luba mitu vaadet\"\nAJAOutput.MultiViewAudioSource=\"Multivaate heliallikas\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/eu-ES.ini",
    "content": "AJAOutput.Device=\"AJA I/O Irteera pantaila-argazki gailua\"\nAJAOutput.ProgramOutput=\"Irteera programa\"\nAJAOutput.PreviewOutput=\"Irteera aurrerakina\"\nAJAOutput.MiscOutput=\"Ezarpen gehigarriak\"\nAJAOutput.MultiViewEnable=\"Ikuspuntu anizkoitza baimendu\"\nAJAOutput.MultiViewAudioSource=\"Entzunezko iturburuaren ikuspuntu anizkoitza\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/fa-IR.ini",
    "content": "AJAOutput.Device=\"خروجی دستگاه و/خ AJA\"\nAJAOutput.ProgramOutput=\"خروجی برنامه\"\nAJAOutput.PreviewOutput=\"پیش نمایش خروجی\"\nAJAOutput.MiscOutput=\"تنظیمات اضافی\"\nAJAOutput.MultiViewEnable=\"چند نمای را فعال کنید\"\nAJAOutput.MultiViewAudioSource=\"منبع صوتی چند نما\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/fi-FI.ini",
    "content": "AJAOutput.Device=\"AJA I/O -laitteen ulostulo\"\nAJAOutput.ProgramOutput=\"Ohjelman ulostulo\"\nAJAOutput.PreviewOutput=\"Esikatselun ulostulo\"\nAJAOutput.MiscOutput=\"Lisäasetukset\"\nAJAOutput.MultiViewEnable=\"Ota moninäkymä käyttöön\"\nAJAOutput.MultiViewAudioSource=\"Moninäkymän äänilähde\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/fil-PH.ini",
    "content": "AJAOutput.PreviewOutput=\"Silipin Ang Output\"\nAJAOutput.MiscOutput=\"Karagdagang kaayusan\"\nAJAOutput.MultiViewEnable=\"Paganahin ang Multi View\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/fr-FR.ini",
    "content": "AJAOutput.Device=\"Périphérique de sortie I/O AJA\"\nAJAOutput.ProgramOutput=\"Sortie du programme\"\nAJAOutput.PreviewOutput=\"Sortie de l'aperçu\"\nAJAOutput.MiscOutput=\"Paramètres supplémentaires\"\nAJAOutput.MultiViewEnable=\"Activer Multi View\"\nAJAOutput.MultiViewAudioSource=\"Source audio de la sortie Multi View\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/gl-ES.ini",
    "content": "AJAOutput.Device=\"Dispositivo de Saida AJA I/O\"\nAJAOutput.ProgramOutput=\"Programa saida\"\nAJAOutput.PreviewOutput=\"Mostrar a Saida\"\nAJAOutput.MiscOutput=\"Configuracions adiccionais\"\nAJAOutput.MultiViewEnable=\"Activar Multiples Vistas\"\nAJAOutput.MultiViewAudioSource=\"Fonte de son Multi vistas\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/he-IL.ini",
    "content": "AJAOutput.Device=\"פלט התקן AJA I/O\"\nAJAOutput.ProgramOutput=\"פלט תכנית\"\nAJAOutput.PreviewOutput=\"פלט תצוגה מקדימה\"\nAJAOutput.MiscOutput=\"הגדרות נוספות\"\nAJAOutput.MultiViewEnable=\"הפעלת ריבוי תצוגות\"\nAJAOutput.MultiViewAudioSource=\"מקור שמע של ריבוי תצוגות\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/hi-IN.ini",
    "content": "AJAOutput.Device=\"AJA I/O डिवाइस आउटपुट\"\nAJAOutput.ProgramOutput=\"प्रोग्राम आउटपुट\"\nAJAOutput.PreviewOutput=\"पूर्वावलोकन आउटपुट\"\nAJAOutput.MiscOutput=\"अतिरिक्त सेटिंग\"\nAJAOutput.MultiViewEnable=\"अनेक दृश्य सक्षम करें\"\nAJAOutput.MultiViewAudioSource=\"अनेक दृश्य ऑडियो स्रोत\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/hr-HR.ini",
    "content": "AJAOutput.Device=\"AJA I/O Izlaz uređaja\"\nAJAOutput.ProgramOutput=\"Pretpregled programa\"\nAJAOutput.PreviewOutput=\"Pretpregled izlaznog rezultata\"\nAJAOutput.MiscOutput=\"Dodatne postavke\"\nAJAOutput.MultiViewEnable=\"Omogući višestruki prikaz\"\nAJAOutput.MultiViewAudioSource=\"Izvor zvuka s višestrukim prikazom\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/hu-HU.ini",
    "content": "AJAOutput.Device=\"AJA I/O eszközkimenet\"\nAJAOutput.ProgramOutput=\"Programkimenet\"\nAJAOutput.PreviewOutput=\"Előnézeti kimenet\"\nAJAOutput.MiscOutput=\"További beállítások\"\nAJAOutput.MultiViewEnable=\"Többszörös nézet engedélyezése\"\nAJAOutput.MultiViewAudioSource=\"Többszörös nézet hangforrása\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/hy-AM.ini",
    "content": "AJAOutput.Device=\"AJA I/O սարքի ելք\"\nAJAOutput.ProgramOutput=\"Ծրագրային Ելքագրում\"\nAJAOutput.PreviewOutput=\"Նախադիտման արդյունքը\"\nAJAOutput.MiscOutput=\"Լրացուցիչ կարգավորումներ\"\nAJAOutput.MultiViewEnable=\"Միացնել Բազմակի դիտումը\"\nAJAOutput.MultiViewAudioSource=\"Բազմակի դիտումի աուդիոի աղբյուր\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/id-ID.ini",
    "content": "AJAOutput.Device=\"Perangkat Keluaran AJA I/O\"\nAJAOutput.ProgramOutput=\"Keluaran Program\"\nAJAOutput.PreviewOutput=\"Keluaran Pratinjau\"\nAJAOutput.MiscOutput=\"Pengaturan Tambahan\"\nAJAOutput.MultiViewEnable=\"Aktifkan Tampilan Ganda\"\nAJAOutput.MultiViewAudioSource=\"Tampilan Sumber Suara Ganda\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/it-IT.ini",
    "content": "AJAOutput.Device=\"Dispositivo AJA I/O di uscita\"\nAJAOutput.ProgramOutput=\"Programma di uscita\"\nAJAOutput.PreviewOutput=\"Anteprima di uscita\"\nAJAOutput.MiscOutput=\"Ulteriori impostazioni\"\nAJAOutput.MultiViewEnable=\"Abilita Visualizzazione Multipla\"\nAJAOutput.MultiViewAudioSource=\"Visualizzazione multipla della sorgente audio\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/ja-JP.ini",
    "content": "AJAOutput.Device=\"AJA I/O デバイス出力\"\nAJAOutput.ProgramOutput=\"プログラム出力\"\nAJAOutput.PreviewOutput=\"プレビュー出力\"\nAJAOutput.MiscOutput=\"詳細設定\"\nAJAOutput.MultiViewEnable=\"マルチビューを有効にする\"\nAJAOutput.MultiViewAudioSource=\"マルチビュー 音声ソース\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/ka-GE.ini",
    "content": "AJAOutput.Device=\"AJA-I/O-მოწყობილობით გამოტანა\"\nAJAOutput.ProgramOutput=\"პროგრამის გამოტანა\"\nAJAOutput.PreviewOutput=\"შეთვალიერების გამოტანა\"\nAJAOutput.MiscOutput=\"დამატებითი პარამეტრები\"\nAJAOutput.MultiViewEnable=\"მრავალხედიანის ჩართვა\"\nAJAOutput.MultiViewAudioSource=\"მრავალხედიანი ხმოვანი წყარო\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/kaa.ini",
    "content": "AJAOutput.MiscOutput=\"Qosımsha sazlawlar\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/kmr-TR.ini",
    "content": "AJAOutput.Device=\"Derana amûrê AJA I/O\"\nAJAOutput.ProgramOutput=\"Derana bernameyê\"\nAJAOutput.PreviewOutput=\"Derana pêşdîtinê\"\nAJAOutput.MiscOutput=\"Sazkariyên vebijêrkî\"\nAJAOutput.MultiViewEnable=\"Pir dîtin çalak bike\"\nAJAOutput.MultiViewAudioSource=\"Çavkaniya dengê ya pir dîtin\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/ko-KR.ini",
    "content": "AJAOutput.Device=\"AJA I/O 디바이스 출력\"\nAJAOutput.ProgramOutput=\"프로그램 출력\"\nAJAOutput.PreviewOutput=\"미리보기 출력\"\nAJAOutput.MiscOutput=\"추가 설정\"\nAJAOutput.MultiViewEnable=\"다중 화면 활성화\"\nAJAOutput.MultiViewAudioSource=\"다중 화면 오디오 소스\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/lo-LA.ini",
    "content": "AJAOutput.Device=\"ຜົນອອກອຸປະກອນ AJA I/O\"\nAJAOutput.ProgramOutput=\"ຜົນອອກໂປຣແກຣມ\"\nAJAOutput.PreviewOutput=\"ຜົນອອກການສະແດງຕົວຢ່າງ\"\nAJAOutput.MiscOutput=\"ການຕັ້ງຄ່າເພີ່ມເຕີມ\"\nAJAOutput.MultiViewEnable=\"ເປີດໃຊ້ງານມຸມມອງຫຼາຍຈໍ\"\nAJAOutput.MultiViewAudioSource=\"ແຫຼ່ງສຽງມຸມມອງຫຼາຍຈໍ\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/lv-LV.ini",
    "content": "AJAOutput.ProgramOutput=\"Programmas Izvade\"\nAJAOutput.MiscOutput=\"Papildu Iestatījumi\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/ms-MY.ini",
    "content": "AJAOutput.Device=\"Output Peranti I/O AJA\"\nAJAOutput.ProgramOutput=\"Output Program\"\nAJAOutput.PreviewOutput=\"Output Pratonton\"\nAJAOutput.MiscOutput=\"Tetapan Tambahan\"\nAJAOutput.MultiViewEnable=\"Benarkan Pandangan Berbilang\"\nAJAOutput.MultiViewAudioSource=\"Sumber Audio Pandangan Berbilang\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/nb-NO.ini",
    "content": "AJAOutput.Device=\"AJA I/O Enhet Utgang\"\nAJAOutput.ProgramOutput=\"Program Utgang\"\nAJAOutput.PreviewOutput=\"Forhåndsvis Utgang\"\nAJAOutput.MiscOutput=\"Tilleggsinnstillinger\"\nAJAOutput.MultiViewEnable=\"Aktiver flervisning\"\nAJAOutput.MultiViewAudioSource=\"Flere viser lydkilde\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/nl-NL.ini",
    "content": "AJAOutput.Device=\"AJA I/O Apparaat Uitvoer\"\nAJAOutput.ProgramOutput=\"Programma Uitvoer\"\nAJAOutput.PreviewOutput=\"Preview uitvoer\"\nAJAOutput.MiscOutput=\"Aanvullende instellingen\"\nAJAOutput.MultiViewEnable=\"Multi weergave inschakelen\"\nAJAOutput.MultiViewAudioSource=\"Multi weergave audiobron\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/pl-PL.ini",
    "content": "AJAOutput.Device=\"Wyjście AJA I/O\"\nAJAOutput.ProgramOutput=\"Wyjście w aplikacji\"\nAJAOutput.PreviewOutput=\"Podgląd wyjścia\"\nAJAOutput.MiscOutput=\"Dodatkowe ustawienia\"\nAJAOutput.MultiViewEnable=\"Włącz Multiview\"\nAJAOutput.MultiViewAudioSource=\"Źródło dźwięku Multiview\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/pt-BR.ini",
    "content": "AJAOutput.Device=\"Saída do dispositivo I/O AJA\"\nAJAOutput.ProgramOutput=\"Saída do programa\"\nAJAOutput.PreviewOutput=\"Saída da pré-visualização\"\nAJAOutput.MiscOutput=\"Configurações adicionais\"\nAJAOutput.MultiViewEnable=\"Habilitar visualização múltipla\"\nAJAOutput.MultiViewAudioSource=\"Fonte de áudio da visualização múltipla\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/pt-PT.ini",
    "content": "AJAOutput.Device=\"Dispositivo de saída AJA I/O\"\nAJAOutput.ProgramOutput=\"Saída do Programa\"\nAJAOutput.PreviewOutput=\"Saída de antevisão\"\nAJAOutput.MiscOutput=\"Definições adicionais\"\nAJAOutput.MultiViewEnable=\"Ativar multivisualização\"\nAJAOutput.MultiViewAudioSource=\"Fonte de áudio de multivisualização\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/ro-RO.ini",
    "content": "AJAOutput.Device=\"Outputul dispozitivului AJA I/O\"\nAJAOutput.ProgramOutput=\"Outputul programului\"\nAJAOutput.PreviewOutput=\"Previzualizarea outputului\"\nAJAOutput.MiscOutput=\"Setări adiționale\"\nAJAOutput.MultiViewEnable=\"Activează vizualizarea multiplă\"\nAJAOutput.MultiViewAudioSource=\"Sursă audio pentru vizualizare multiplă\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/ru-RU.ini",
    "content": "AJAOutput.Device=\"Захват устройства AJA I/O\"\nAJAOutput.ProgramOutput=\"Вывод программы\"\nAJAOutput.PreviewOutput=\"Предпросмотр вывода\"\nAJAOutput.MiscOutput=\"Дополнительные настройки\"\nAJAOutput.MultiViewEnable=\"Включить мультипросмотр\"\nAJAOutput.MultiViewAudioSource=\"Мультипросмотр источника аудио\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/si-LK.ini",
    "content": "AJAOutput.Device=\"ඒජේඒ ආ/ප්‍ර උපාංගයේ ප්‍රතිදානය\"\nAJAOutput.ProgramOutput=\"වැඩසටහනේ ප්‍රතිදානය\"\nAJAOutput.PreviewOutput=\"පෙරදසුනේ ප්‍රතිදානය\"\nAJAOutput.MiscOutput=\"අතිරේක සැකසුම්\"\nAJAOutput.MultiViewEnable=\"බහුදැක්ම සබල කරන්න\"\nAJAOutput.MultiViewAudioSource=\"බහුදැක්ම ශ්‍රව්‍ය මූලාශ්‍රය\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/sk-SK.ini",
    "content": "AJAOutput.Device=\"Výstup AJA I/O zariadenia\"\nAJAOutput.ProgramOutput=\"Výstup programu\"\nAJAOutput.PreviewOutput=\"Výstup náhľadu\"\nAJAOutput.MiscOutput=\"Ďalšie nastavenia\"\nAJAOutput.MultiViewEnable=\"Zapnúť Multi View\"\nAJAOutput.MultiViewAudioSource=\"Multi View zdroj zvuku\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/sl-SI.ini",
    "content": "AJAOutput.Device=\"Izhod AJA vhodno-izhodne naprave\"\nAJAOutput.ProgramOutput=\"Programski izhod\"\nAJAOutput.PreviewOutput=\"Izhod za predogled\"\nAJAOutput.MiscOutput=\"Dodatne nastavitve\"\nAJAOutput.MultiViewEnable=\"Omogoči večzaslonski pogled\"\nAJAOutput.MultiViewAudioSource=\"Vir zvoka za večzaslonski pogled\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/sq-AL.ini",
    "content": "AJAOutput.MiscOutput=\"Parametra shtesë\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/sr-SP.ini",
    "content": "AJAOutput.Device=\"Излаз AJA I/O уређаја\"\nAJAOutput.ProgramOutput=\"Излаз програма\"\nAJAOutput.PreviewOutput=\"Преглед излаза\"\nAJAOutput.MiscOutput=\"Додатна подешавања\"\nAJAOutput.MultiViewEnable=\"Омогући вишеструки приказ\"\nAJAOutput.MultiViewAudioSource=\"Извор звука са вишеструким приказом\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/sv-SE.ini",
    "content": "AJAOutput.Device=\"I/O-enhetsutmatning för AJA\"\nAJAOutput.ProgramOutput=\"Programutmatning\"\nAJAOutput.PreviewOutput=\"Förhandsvisningsutmatning\"\nAJAOutput.MiscOutput=\"Ytterligare inställningar\"\nAJAOutput.MultiViewEnable=\"Aktivera multivy\"\nAJAOutput.MultiViewAudioSource=\"Ljudkälla i multivy\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/th-TH.ini",
    "content": "AJAOutput.Device=\"เอาต์พุตอุปกรณ์ I/O ของ AJA\"\nAJAOutput.ProgramOutput=\"เอาต์พุตของโปรแกรม\"\nAJAOutput.PreviewOutput=\"เอาต์พุตแสดงตัวอย่าง\"\nAJAOutput.MiscOutput=\"การตั้งค่าเพิ่มเติม\"\nAJAOutput.MultiViewEnable=\"เปิดใช้การแสดงผลหลายจอ\"\nAJAOutput.MultiViewAudioSource=\"มุมมองหลายจอ แหล่งเสียง\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/tl-PH.ini",
    "content": "AJAOutput.Device=\"Output ng Device na AJA I/O\"\nAJAOutput.ProgramOutput=\"Output sa Program\"\nAJAOutput.PreviewOutput=\"Pagpakita ng Output\"\nAJAOutput.MiscOutput=\"Karagdagang mga Setting\"\nAJAOutput.MultiViewEnable=\"I-enable ang Multi View\"\nAJAOutput.MultiViewAudioSource=\"Pagkukuhanan ng Audio sa Multi View\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/tr-TR.ini",
    "content": "AJAOutput.Device=\"AJA I/O Aygıt Çıktısı\"\nAJAOutput.ProgramOutput=\"Program Çıktısı\"\nAJAOutput.PreviewOutput=\"Önizleme Çıktısı\"\nAJAOutput.MiscOutput=\"Ek Ayarlar\"\nAJAOutput.MultiViewEnable=\"Çoklu Görünümü Etkinleştir\"\nAJAOutput.MultiViewAudioSource=\"Çoklu Görünümlü Ses Kaynağı\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/tt-RU.ini",
    "content": "AJAOutput.MiscOutput=\"Өстәмә көйләүләр\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/ug-CN.ini",
    "content": "AJAOutput.Device=\"AJA I/O ئۈسكۈنە چىقىرىش\"\nAJAOutput.ProgramOutput=\"پىروگرامما چىقىرىش\"\nAJAOutput.PreviewOutput=\"ئالدىن كۆزىتىش چىقىرىش\"\nAJAOutput.MiscOutput=\"قوشۇمچە تەڭشەكلەر\"\nAJAOutput.MultiViewEnable=\"كۆپ كۆرۈنۈشنى قوزغات\"\nAJAOutput.MultiViewAudioSource=\"كۆپ كۆرۈنۈش ئۈن مەنبەسى\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/uk-UA.ini",
    "content": "AJAOutput.Device=\"AJA I/O Пристрій виводу\"\nAJAOutput.ProgramOutput=\"Вивід програми\"\nAJAOutput.PreviewOutput=\"Попередній перегляд виводу\"\nAJAOutput.MiscOutput=\"Додаткові налаштування\"\nAJAOutput.MultiViewEnable=\"Увімкнути мульти-перегляд\"\nAJAOutput.MultiViewAudioSource=\"Мульти-перегляд джерела звуку\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/ur-PK.ini",
    "content": "AJAOutput.Device=\"AJA I/O ڈیوائس آؤٹ پٹ\"\nAJAOutput.ProgramOutput=\"پروگرام آؤٹ پٹ\"\nAJAOutput.PreviewOutput=\"پیش نظارہ آؤٹ پٹ\"\nAJAOutput.MiscOutput=\"اضافی ترتیبات\"\nAJAOutput.MultiViewEnable=\"ملٹی ویو کو فعال کریں\"\nAJAOutput.MultiViewAudioSource=\"ملٹی ویو آڈیو ماخذ\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/vi-VN.ini",
    "content": "AJAOutput.Device=\"Đầu ra thiết bị I/O AJA\"\nAJAOutput.ProgramOutput=\"Đầu ra thiết bị\"\nAJAOutput.PreviewOutput=\"Đầu ra xem trước\"\nAJAOutput.MiscOutput=\"Thiết đặt bổ sung\"\nAJAOutput.MultiViewEnable=\"Bật Nhiều góc nhìn\"\nAJAOutput.MultiViewAudioSource=\"Nguồn âm thanh Nhiều góc nhìn\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/zh-CN.ini",
    "content": "AJAOutput.Device=\"AJA I/O 设备输出\"\nAJAOutput.ProgramOutput=\"程序输出\"\nAJAOutput.PreviewOutput=\"预览输出\"\nAJAOutput.MiscOutput=\"附加设置\"\nAJAOutput.MultiViewEnable=\"开启多视图\"\nAJAOutput.MultiViewAudioSource=\"音频源多视图\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/data/locale/zh-TW.ini",
    "content": "AJAOutput.Device=\"AJA I/O 裝置輸出\"\nAJAOutput.ProgramOutput=\"節目輸出\"\nAJAOutput.PreviewOutput=\"預覽輸出\"\nAJAOutput.MiscOutput=\"其他設定\"\nAJAOutput.MultiViewEnable=\"啟用多檢視模式\"\nAJAOutput.MultiViewAudioSource=\"多檢視音訊來源\"\n"
  },
  {
    "path": "frontend/plugins/aja-output-ui/forms/output.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>Output</class>\n <widget class=\"QDialog\" name=\"Output\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>800</width>\n    <height>540</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>AJAOutput.Device</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <property name=\"modal\">\n   <bool>false</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetDefaultConstraint</enum>\n   </property>\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>AJAOutput.ProgramOutput</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"propertiesLayout\"/>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <property name=\"spacing\">\n      <number>6</number>\n     </property>\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"outputButton\">\n       <property name=\"text\">\n        <string>Start</string>\n       </property>\n       <property name=\"checkable\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"label_2\">\n     <property name=\"text\">\n      <string>AJAOutput.PreviewOutput</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"previewPropertiesLayout\"/>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"previewOutputButton\">\n       <property name=\"text\">\n        <string>Start</string>\n       </property>\n       <property name=\"checkable\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"label_3\">\n     <property name=\"text\">\n      <string>AJAOutput.MiscOutput</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"miscPropertiesLayout\"/>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/plugins/decklink-captions/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nif(NOT ENABLE_DECKLINK)\n  target_disable(decklink-captions)\n  return()\nendif()\n\nif(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD)\n  find_package(X11 REQUIRED)\n  mark_as_advanced(X11)\nendif()\n\nfind_package(Qt6 REQUIRED Widgets)\n\nadd_library(decklink-captions MODULE)\nadd_library(OBS::decklink-captions ALIAS decklink-captions)\n\ntarget_sources(decklink-captions PRIVATE decklink-captions.cpp decklink-captions.h forms/captions.ui)\n\ntarget_compile_options(\n  decklink-captions\n  PRIVATE $<$<PLATFORM_ID:Darwin>:-Wno-quoted-include-in-framework-header> $<$<PLATFORM_ID:Darwin>:-Wno-comma>\n)\n\ntarget_link_libraries(\n  decklink-captions\n  PRIVATE\n    OBS::frontend-api\n    OBS::libobs\n    Qt::Widgets\n    \"$<$<PLATFORM_ID:Darwin>:$<LINK_LIBRARY:FRAMEWORK,Cocoa.framework>>\"\n    $<$<PLATFORM_ID:Linux,FreeBSD>:X11::X11>\n)\n\nif(OS_WINDOWS)\n  configure_file(cmake/windows/obs-module.rc.in decklink-captions.rc)\n  target_sources(decklink-captions PRIVATE decklink-captions.rc)\n\n  set_property(TARGET decklink-captions APPEND PROPERTY AUTORCC_OPTIONS --format-version 1)\nendif()\n\nset_target_properties_obs(\n  decklink-captions\n  PROPERTIES FOLDER plugins/decklink\n             PREFIX \"\"\n             AUTOMOC ON\n             AUTOUIC ON\n             AUTORCC ON\n             AUTOUIC_SEARCH_PATHS forms\n)\n"
  },
  {
    "path": "frontend/plugins/decklink-captions/cmake/windows/obs-module.rc.in",
    "content": "1 VERSIONINFO\nFILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"040904B0\"\n    BEGIN\n      VALUE \"CompanyName\", \"${OBS_COMPANY_NAME}\"\n      VALUE \"FileDescription\", \"OBS DeckLink Captions module\"\n      VALUE \"FileVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"ProductName\", \"${OBS_PRODUCT_NAME}\"\n      VALUE \"ProductVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"Comments\", \"${OBS_COMMENTS}\"\n      VALUE \"LegalCopyright\", \"${OBS_LEGAL_COPYRIGHT}\"\n      VALUE \"InternalName\", \"decklink-captions\"\n      VALUE \"OriginalFilename\", \"decklink-captions\"\n    END\n  END\n\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0409, 0x04B0\n  END\nEND\n"
  },
  {
    "path": "frontend/plugins/decklink-captions/data/.keepme",
    "content": ""
  },
  {
    "path": "frontend/plugins/decklink-captions/decklink-captions.cpp",
    "content": "#include <obs-frontend-api.h>\n#include <QMainWindow>\n#include <QAction>\n#include <obs.hpp>\n#include \"decklink-captions.h\"\n\nOBS_DECLARE_MODULE()\nOBS_MODULE_USE_DEFAULT_LOCALE(\"decklink-captons\", \"en-US\")\n\nstruct obs_captions {\n\tstd::string source_name;\n\tOBSWeakSource source;\n\n\tvoid start();\n\tvoid stop();\n\n\tobs_captions();\n\tinline ~obs_captions() { stop(); }\n};\n\nobs_captions::obs_captions() {}\n\nstatic obs_captions *captions = nullptr;\n\nDecklinkCaptionsUI::DecklinkCaptionsUI(QWidget *parent) : QDialog(parent), ui(new Ui_CaptionsDialog)\n{\n\tui->setupUi(this);\n\n\tsetSizeGripEnabled(true);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tauto cb = [this](obs_source_t *source) {\n\t\tuint32_t caps = obs_source_get_output_flags(source);\n\t\tQString name = obs_source_get_name(source);\n\n\t\tif (caps & OBS_SOURCE_CEA_708)\n\t\t\tui->source->addItem(name);\n\n\t\tOBSWeakSource weak = OBSGetWeakRef(source);\n\t\tif (weak == captions->source)\n\t\t\tui->source->setCurrentText(name);\n\t\treturn true;\n\t};\n\n\tusing cb_t = decltype(cb);\n\n\tui->source->blockSignals(true);\n\tui->source->addItem(QStringLiteral(\"\"));\n\tui->source->setCurrentIndex(0);\n\tobs_enum_sources([](void *data, obs_source_t *source) { return (*static_cast<cb_t *>(data))(source); }, &cb);\n\tui->source->blockSignals(false);\n}\n\nvoid DecklinkCaptionsUI::on_source_currentIndexChanged(int)\n{\n\tcaptions->stop();\n\n\tcaptions->source_name = ui->source->currentText().toUtf8().constData();\n\tcaptions->source = GetWeakSourceByName(captions->source_name.c_str());\n\n\tcaptions->start();\n}\n\nstatic void caption_callback(void * /* param */, obs_source_t * /* source */, const struct obs_source_cea_708 *captions)\n{\n\tobs_output *output = obs_frontend_get_streaming_output();\n\tif (output) {\n\t\tif (obs_frontend_streaming_active() && obs_output_active(output)) {\n\t\t\tobs_output_caption(output, captions);\n\t\t}\n\t\tobs_output_release(output);\n\t}\n}\n\nvoid obs_captions::start()\n{\n\tOBSSource s = OBSGetStrongRef(source);\n\tif (!s) {\n\t\t//warn(\"Source invalid\");\n\t\treturn;\n\t}\n\tobs_source_add_caption_callback(s, caption_callback, nullptr);\n}\n\nvoid obs_captions::stop()\n{\n\tOBSSource s = OBSGetStrongRef(source);\n\tif (s)\n\t\tobs_source_remove_caption_callback(s, caption_callback, nullptr);\n}\n\nstatic void save_decklink_caption_data(obs_data_t *save_data, bool saving, void *)\n{\n\tif (saving) {\n\t\tobs_data_t *obj = obs_data_create();\n\n\t\tobs_data_set_string(obj, \"source\", captions->source_name.c_str());\n\n\t\tobs_data_set_obj(save_data, \"decklink_captions\", obj);\n\t\tobs_data_release(obj);\n\t} else {\n\t\tcaptions->stop();\n\n\t\tobs_data_t *obj = obs_data_get_obj(save_data, \"decklink_captions\");\n\t\tif (!obj)\n\t\t\tobj = obs_data_create();\n\n\t\tcaptions->source_name = obs_data_get_string(obj, \"source\");\n\t\tcaptions->source = GetWeakSourceByName(captions->source_name.c_str());\n\t\tobs_data_release(obj);\n\n\t\tcaptions->start();\n\t}\n}\n\nvoid addOutputUI(void)\n{\n\tQAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text(\"Decklink Captions\"));\n\n\tcaptions = new obs_captions;\n\n\tauto cb = []() {\n\t\tobs_frontend_push_ui_translation(obs_module_get_string);\n\n\t\tQWidget *window = (QWidget *)obs_frontend_get_main_window();\n\n\t\tDecklinkCaptionsUI dialog(window);\n\t\tdialog.exec();\n\n\t\tobs_frontend_pop_ui_translation();\n\t};\n\n\tobs_frontend_add_save_callback(save_decklink_caption_data, nullptr);\n\n\tQObject::connect(action, &QAction::triggered, action, cb);\n}\n\nbool obs_module_load(void)\n{\n\treturn true;\n}\n\nvoid obs_module_post_load(void)\n{\n\tif (!obs_get_module(\"decklink\"))\n\t\treturn;\n\n\taddOutputUI();\n}\n"
  },
  {
    "path": "frontend/plugins/decklink-captions/decklink-captions.h",
    "content": "#include <QDialog>\n#include <obs-module.h>\n#include <util/platform.h>\n#include <obs.hpp>\n#include <memory>\n#include \"ui_captions.h\"\n\nclass DecklinkCaptionsUI : public QDialog {\n\tQ_OBJECT\nprivate:\npublic:\n\tstd::unique_ptr<Ui_CaptionsDialog> ui;\n\tDecklinkCaptionsUI(QWidget *parent);\n\npublic slots:\n\tvoid on_source_currentIndexChanged(int idx);\n};\n\nstatic inline OBSWeakSource GetWeakSourceByName(const char *name)\n{\n\tOBSWeakSource weak;\n\tobs_source_t *source = obs_get_source_by_name(name);\n\tif (source) {\n\t\tweak = obs_source_get_weak_source(source);\n\t\tobs_weak_source_release(weak);\n\t\tobs_source_release(source);\n\t}\n\n\treturn weak;\n}\n"
  },
  {
    "path": "frontend/plugins/decklink-captions/forms/captions.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>CaptionsDialog</class>\n <widget class=\"QDialog\" name=\"CaptionsDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>519</width>\n    <height>104</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Captions</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <property name=\"labelAlignment\">\n      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n     </property>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Captions.Source</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>source</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"source\">\n       <property name=\"insertPolicy\">\n        <enum>QComboBox::InsertAlphabetically</enum>\n       </property>\n       <property name=\"sizeAdjustPolicy\">\n        <enum>QComboBox::AdjustToContents</enum>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>0</width>\n       <height>0</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"accept\">\n       <property name=\"text\">\n        <string>OK</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>accept</sender>\n   <signal>clicked()</signal>\n   <receiver>CaptionsDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>268</x>\n     <y>331</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>229</x>\n     <y>-11</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/plugins/decklink-output-ui/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nif(NOT ENABLE_DECKLINK)\n  target_disable(decklink-output-ui)\n  return()\nendif()\n\nfind_package(Qt6 REQUIRED Widgets)\n\nif(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD)\n  find_package(X11 REQUIRED)\nendif()\n\nadd_library(decklink-output-ui MODULE)\nadd_library(OBS::decklink-output-ui ALIAS decklink-output-ui)\n\nif(NOT TARGET OBS::properties-view)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/shared/properties-view\" \"${CMAKE_BINARY_DIR}/shared/properties-view\")\nendif()\n\ntarget_sources(decklink-output-ui PRIVATE forms/output.ui)\n\ntarget_sources(\n  decklink-output-ui\n  PRIVATE DecklinkOutputUI.cpp DecklinkOutputUI.h decklink-ui-main.cpp decklink-ui-main.h\n)\n\ntarget_compile_options(decklink-output-ui PRIVATE $<$<PLATFORM_ID:Darwin>:-Wno-quoted-include-in-framework-header>)\n\ntarget_link_libraries(\n  decklink-output-ui\n  PRIVATE\n    OBS::libobs\n    OBS::frontend-api\n    OBS::properties-view\n    Qt::Widgets\n    \"$<$<PLATFORM_ID:Darwin>:$<LINK_LIBRARY:FRAMEWORK,Cocoa.framework>>\"\n    $<$<PLATFORM_ID:Linux,FreeBSD,OpenBSD>:X11::X11>\n)\n\nif(OS_WINDOWS)\n  configure_file(cmake/windows/obs-module.rc.in decklink-output-ui.rc)\n  target_sources(decklink-output-ui PRIVATE decklink-output-ui.rc)\n\n  set_property(TARGET decklink-output-ui APPEND PROPERTY AUTORCC_OPTIONS --format-version 1)\nendif()\n\nset_target_properties_obs(\n  decklink-output-ui\n  PROPERTIES FOLDER frontend\n             PREFIX \"\"\n             AUTOMOC ON\n             AUTOUIC ON\n             AUTORCC ON\n             AUTOUIC_SEARCH_PATHS forms\n)\n"
  },
  {
    "path": "frontend/plugins/decklink-output-ui/DecklinkOutputUI.cpp",
    "content": "#include \"DecklinkOutputUI.h\"\n#include <obs-module.h>\n#include <util/platform.h>\n#include <util/util.hpp>\n#include \"decklink-ui-main.h\"\n\nDecklinkOutputUI::DecklinkOutputUI(QWidget *parent) : QDialog(parent), ui(new Ui_Output)\n{\n\tui->setupUi(this);\n\n\tsetSizeGripEnabled(true);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tpropertiesView = nullptr;\n\tpreviewPropertiesView = nullptr;\n}\n\nvoid DecklinkOutputUI::ShowHideDialog()\n{\n\tSetupPropertiesView();\n\tSetupPreviewPropertiesView();\n\n\tsetVisible(!isVisible());\n}\n\nvoid DecklinkOutputUI::SetupPropertiesView()\n{\n\tif (propertiesView)\n\t\tdelete propertiesView;\n\n\tobs_data_t *settings = obs_data_create();\n\n\tOBSData data = load_settings();\n\tif (data)\n\t\tobs_data_apply(settings, data);\n\n\tpropertiesView = new OBSPropertiesView(settings, \"decklink_output\",\n\t\t\t\t\t       (PropertiesReloadCallback)obs_get_output_properties, 170);\n\n\tui->propertiesLayout->addWidget(propertiesView);\n\tobs_data_release(settings);\n\n\tconnect(propertiesView, &OBSPropertiesView::Changed, this, &DecklinkOutputUI::PropertiesChanged);\n}\n\nvoid DecklinkOutputUI::SaveSettings()\n{\n\tBPtr<char> modulePath = obs_module_get_config_path(obs_current_module(), \"\");\n\n\tos_mkdirs(modulePath);\n\n\tBPtr<char> path = obs_module_get_config_path(obs_current_module(), \"decklinkOutputProps.json\");\n\n\tobs_data_t *settings = propertiesView->GetSettings();\n\tif (settings)\n\t\tobs_data_save_json_safe(settings, path, \"tmp\", \"bak\");\n}\n\nvoid DecklinkOutputUI::SetupPreviewPropertiesView()\n{\n\tif (previewPropertiesView)\n\t\tdelete previewPropertiesView;\n\n\tobs_data_t *settings = obs_data_create();\n\n\tOBSData data = load_preview_settings();\n\tif (data)\n\t\tobs_data_apply(settings, data);\n\n\tpreviewPropertiesView = new OBSPropertiesView(settings, \"decklink_output\",\n\t\t\t\t\t\t      (PropertiesReloadCallback)obs_get_output_properties, 170);\n\n\tui->previewPropertiesLayout->addWidget(previewPropertiesView);\n\tobs_data_release(settings);\n\n\tconnect(previewPropertiesView, &OBSPropertiesView::Changed, this, &DecklinkOutputUI::PreviewPropertiesChanged);\n}\n\nvoid DecklinkOutputUI::SavePreviewSettings()\n{\n\tBPtr<char> modulePath = obs_module_get_config_path(obs_current_module(), \"\");\n\n\tos_mkdirs(modulePath);\n\n\tBPtr<char> path = obs_module_get_config_path(obs_current_module(), \"decklinkPreviewOutputProps.json\");\n\n\tobs_data_t *settings = previewPropertiesView->GetSettings();\n\tif (settings)\n\t\tobs_data_save_json_safe(settings, path, \"tmp\", \"bak\");\n}\n\nvoid DecklinkOutputUI::on_outputButton_clicked()\n{\n\tSaveSettings();\n\toutput_toggle();\n}\n\nvoid DecklinkOutputUI::PropertiesChanged()\n{\n\tSaveSettings();\n}\n\nvoid DecklinkOutputUI::OutputStateChanged(bool active)\n{\n\tQString text;\n\tif (active) {\n\t\ttext = QString(obs_module_text(\"Stop\"));\n\t} else {\n\t\ttext = QString(obs_module_text(\"Start\"));\n\t}\n\n\tui->outputButton->setChecked(active);\n\tui->outputButton->setText(text);\n}\n\nvoid DecklinkOutputUI::on_previewOutputButton_clicked()\n{\n\tSavePreviewSettings();\n\tpreview_output_toggle();\n}\n\nvoid DecklinkOutputUI::PreviewPropertiesChanged()\n{\n\tSavePreviewSettings();\n}\n\nvoid DecklinkOutputUI::PreviewOutputStateChanged(bool active)\n{\n\tQString text;\n\tif (active) {\n\t\ttext = QString(obs_module_text(\"Stop\"));\n\t} else {\n\t\ttext = QString(obs_module_text(\"Start\"));\n\t}\n\n\tui->previewOutputButton->setChecked(active);\n\tui->previewOutputButton->setText(text);\n}\n"
  },
  {
    "path": "frontend/plugins/decklink-output-ui/DecklinkOutputUI.h",
    "content": "#pragma once\n\n#include <QDialog>\n#include <properties-view.hpp>\n\n#include \"ui_output.h\"\n\nclass DecklinkOutputUI : public QDialog {\n\tQ_OBJECT\nprivate:\n\tOBSPropertiesView *propertiesView;\n\tOBSPropertiesView *previewPropertiesView;\n\npublic slots:\n\tvoid on_outputButton_clicked();\n\tvoid PropertiesChanged();\n\tvoid OutputStateChanged(bool);\n\n\tvoid on_previewOutputButton_clicked();\n\tvoid PreviewPropertiesChanged();\n\tvoid PreviewOutputStateChanged(bool);\n\npublic:\n\tstd::unique_ptr<Ui_Output> ui;\n\tDecklinkOutputUI(QWidget *parent);\n\n\tvoid ShowHideDialog();\n\n\tvoid SetupPropertiesView();\n\tvoid SaveSettings();\n\n\tvoid SetupPreviewPropertiesView();\n\tvoid SavePreviewSettings();\n};\n"
  },
  {
    "path": "frontend/plugins/decklink-output-ui/cmake/windows/obs-module.rc.in",
    "content": "1 VERSIONINFO\nFILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"040904B0\"\n    BEGIN\n      VALUE \"CompanyName\", \"${OBS_COMPANY_NAME}\"\n      VALUE \"FileDescription\", \"OBS Decklink Output UI\"\n      VALUE \"FileVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"ProductName\", \"${OBS_PRODUCT_NAME}\"\n      VALUE \"ProductVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"Comments\", \"${OBS_COMMENTS}\"\n      VALUE \"LegalCopyright\", \"${OBS_LEGAL_COPYRIGHT}\"\n      VALUE \"InternalName\", \"decklink-output-ui\"\n      VALUE \"OriginalFilename\", \"decklink-output-ui\"\n    END\n  END\n\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0409, 0x04B0\n  END\nEND\n"
  },
  {
    "path": "frontend/plugins/decklink-output-ui/data/.keepme",
    "content": ""
  },
  {
    "path": "frontend/plugins/decklink-output-ui/decklink-ui-main.cpp",
    "content": "#include <obs-module.h>\n#include <obs-frontend-api.h>\n#include <QMainWindow>\n#include <QAction>\n#include <util/util.hpp>\n#include <util/platform.h>\n#include <media-io/video-io.h>\n#include <media-io/video-frame.h>\n#include \"DecklinkOutputUI.h\"\n#include \"../../../plugins/decklink/const.h\"\n\nOBS_DECLARE_MODULE()\nOBS_MODULE_USE_DEFAULT_LOCALE(\"decklink-output-ui\", \"en-US\")\n\nDecklinkOutputUI *doUI;\n\nbool shutting_down = false;\n\nbool main_output_running = false;\nbool preview_output_running = false;\n\nconstexpr size_t STAGE_BUFFER_COUNT = 3;\n\nstruct decklink_ui_output {\n\tbool enabled;\n\tobs_source_t *current_source;\n\tobs_output_t *output;\n\n\tvideo_t *video_queue;\n\tgs_texrender_t *texrender_premultiplied;\n\tgs_texrender_t *texrender;\n\tgs_stagesurf_t *stagesurfaces[STAGE_BUFFER_COUNT];\n\tbool surf_written[STAGE_BUFFER_COUNT];\n\tsize_t stage_index;\n\tuint8_t *video_data;\n\tuint32_t video_linesize;\n\n\tobs_video_info ovi;\n};\n\nstatic struct decklink_ui_output context = {0};\nstatic struct decklink_ui_output context_preview = {0};\n\nOBSData load_settings()\n{\n\tBPtr<char> path = obs_module_get_config_path(obs_current_module(), \"decklinkOutputProps.json\");\n\tBPtr<char> jsonData = os_quick_read_utf8_file(path);\n\tif (!!jsonData) {\n\t\tobs_data_t *data = obs_data_create_from_json(jsonData);\n\t\tOBSData dataRet(data);\n\t\tobs_data_release(data);\n\n\t\treturn dataRet;\n\t}\n\n\treturn nullptr;\n}\n\nstatic void decklink_ui_tick(void *param, float sec);\nstatic void decklink_ui_render(void *param);\n\nvoid output_stop()\n{\n\tobs_remove_main_rendered_callback(decklink_ui_render, &context);\n\n\tobs_output_stop(context.output);\n\tobs_output_release(context.output);\n\n\tobs_enter_graphics();\n\tfor (gs_stagesurf_t *&surf : context.stagesurfaces) {\n\t\tgs_stagesurface_destroy(surf);\n\t\tsurf = nullptr;\n\t}\n\tgs_texrender_destroy(context.texrender);\n\tcontext.texrender = nullptr;\n\tobs_leave_graphics();\n\n\tvideo_output_close(context.video_queue);\n\tobs_remove_tick_callback(decklink_ui_tick, &context);\n\n\tmain_output_running = false;\n\n\tif (!shutting_down)\n\t\tdoUI->OutputStateChanged(false);\n}\n\nvoid output_start()\n{\n\tOBSData settings = load_settings();\n\n\tif (settings != nullptr) {\n\t\tobs_output_t *const output = obs_output_create(\"decklink_output\", \"decklink_output\", settings, NULL);\n\t\tconst struct video_scale_info *const conversion = obs_output_get_video_conversion(output);\n\t\tif (conversion != nullptr) {\n\t\t\tcontext.output = output;\n\t\t\tobs_add_tick_callback(decklink_ui_tick, &context);\n\n\t\t\tobs_get_video_info(&context.ovi);\n\n\t\t\tconst uint32_t width = conversion->width;\n\t\t\tconst uint32_t height = conversion->height;\n\n\t\t\tobs_enter_graphics();\n\t\t\tcontext.texrender_premultiplied = nullptr;\n\t\t\tcontext.texrender = gs_texrender_create(GS_BGRA, GS_ZS_NONE);\n\t\t\tfor (gs_stagesurf_t *&surf : context.stagesurfaces)\n\t\t\t\tsurf = gs_stagesurface_create(width, height, GS_BGRA);\n\t\t\tobs_leave_graphics();\n\n\t\t\tfor (bool &written : context.surf_written)\n\t\t\t\twritten = false;\n\n\t\t\tcontext.stage_index = 0;\n\n\t\t\tvideo_output_info vi = {0};\n\t\t\tvi.format = VIDEO_FORMAT_BGRA;\n\t\t\tvi.width = width;\n\t\t\tvi.height = height;\n\t\t\tvi.fps_den = context.ovi.fps_den;\n\t\t\tvi.fps_num = context.ovi.fps_num;\n\t\t\tvi.cache_size = 16;\n\t\t\tvi.colorspace = VIDEO_CS_DEFAULT;\n\t\t\tvi.range = VIDEO_RANGE_FULL;\n\t\t\tvi.name = \"decklink_output\";\n\n\t\t\tvideo_output_open(&context.video_queue, &vi);\n\n\t\t\tcontext.current_source = nullptr;\n\t\t\tobs_add_main_rendered_callback(decklink_ui_render, &context);\n\n\t\t\tobs_output_set_media(context.output, context.video_queue, obs_get_audio());\n\t\t\tbool started = obs_output_start(context.output);\n\n\t\t\tmain_output_running = started;\n\n\t\t\tif (!shutting_down)\n\t\t\t\tdoUI->OutputStateChanged(started);\n\n\t\t\tif (!started)\n\t\t\t\toutput_stop();\n\t\t} else {\n\t\t\tobs_output_release(output);\n\t\t}\n\t}\n}\n\nvoid output_toggle()\n{\n\tif (main_output_running)\n\t\toutput_stop();\n\telse\n\t\toutput_start();\n}\n\nOBSData load_preview_settings()\n{\n\tBPtr<char> path = obs_module_get_config_path(obs_current_module(), \"decklinkPreviewOutputProps.json\");\n\tBPtr<char> jsonData = os_quick_read_utf8_file(path);\n\tif (!!jsonData) {\n\t\tobs_data_t *data = obs_data_create_from_json(jsonData);\n\t\tOBSData dataRet(data);\n\t\tobs_data_release(data);\n\n\t\treturn dataRet;\n\t}\n\n\treturn nullptr;\n}\n\nvoid on_preview_scene_changed(enum obs_frontend_event event, void *param);\n\nstatic void decklink_ui_tick(void *param, float /* sec */)\n{\n\tauto ctx = (struct decklink_ui_output *)param;\n\n\tif (ctx->texrender_premultiplied)\n\t\tgs_texrender_reset(ctx->texrender_premultiplied);\n\tif (ctx->texrender)\n\t\tgs_texrender_reset(ctx->texrender);\n}\n\nvoid preview_output_stop()\n{\n\tobs_remove_main_rendered_callback(decklink_ui_render, &context_preview);\n\tobs_frontend_remove_event_callback(on_preview_scene_changed, &context_preview);\n\n\tobs_output_stop(context_preview.output);\n\tobs_output_release(context_preview.output);\n\n\tobs_source_release(context_preview.current_source);\n\n\tobs_enter_graphics();\n\tfor (gs_stagesurf_t *&surf : context_preview.stagesurfaces) {\n\t\tgs_stagesurface_destroy(surf);\n\t\tsurf = nullptr;\n\t}\n\tgs_texrender_destroy(context_preview.texrender);\n\tcontext_preview.texrender = nullptr;\n\tgs_texrender_destroy(context_preview.texrender_premultiplied);\n\tcontext_preview.texrender_premultiplied = nullptr;\n\tobs_leave_graphics();\n\n\tvideo_output_close(context_preview.video_queue);\n\tobs_remove_tick_callback(decklink_ui_tick, &context_preview);\n\n\tpreview_output_running = false;\n\n\tif (!shutting_down)\n\t\tdoUI->PreviewOutputStateChanged(false);\n}\n\nvoid preview_output_start()\n{\n\tOBSData settings = load_preview_settings();\n\n\tif (settings != nullptr) {\n\t\tobs_output_t *const output = obs_output_create(\"decklink_output\", \"decklink_output\", settings, NULL);\n\t\tconst struct video_scale_info *const conversion = obs_output_get_video_conversion(output);\n\t\tif (conversion != nullptr) {\n\t\t\tcontext_preview.output = output;\n\t\t\tobs_add_tick_callback(decklink_ui_tick, &context_preview);\n\n\t\t\tobs_get_video_info(&context_preview.ovi);\n\n\t\t\tconst uint32_t width = conversion->width;\n\t\t\tconst uint32_t height = conversion->height;\n\n\t\t\tobs_enter_graphics();\n\t\t\tcontext_preview.texrender_premultiplied = gs_texrender_create(GS_BGRA, GS_ZS_NONE);\n\t\t\tcontext_preview.texrender = gs_texrender_create(GS_BGRA, GS_ZS_NONE);\n\t\t\tfor (gs_stagesurf_t *&surf : context_preview.stagesurfaces)\n\t\t\t\tsurf = gs_stagesurface_create(width, height, GS_BGRA);\n\t\t\tobs_leave_graphics();\n\n\t\t\tfor (bool &written : context_preview.surf_written)\n\t\t\t\twritten = false;\n\n\t\t\tcontext_preview.stage_index = 0;\n\n\t\t\tvideo_output_info vi = {0};\n\t\t\tvi.format = VIDEO_FORMAT_BGRA;\n\t\t\tvi.width = width;\n\t\t\tvi.height = height;\n\t\t\tvi.fps_den = context_preview.ovi.fps_den;\n\t\t\tvi.fps_num = context_preview.ovi.fps_num;\n\t\t\tvi.cache_size = 16;\n\t\t\tvi.colorspace = VIDEO_CS_DEFAULT;\n\t\t\tvi.range = VIDEO_RANGE_FULL;\n\t\t\tvi.name = \"decklink_preview_output\";\n\n\t\t\tvideo_output_open(&context_preview.video_queue, &vi);\n\n\t\t\tobs_frontend_add_event_callback(on_preview_scene_changed, &context_preview);\n\t\t\tif (obs_frontend_preview_program_mode_active()) {\n\t\t\t\tcontext_preview.current_source = obs_frontend_get_current_preview_scene();\n\t\t\t} else {\n\t\t\t\tcontext_preview.current_source = obs_frontend_get_current_scene();\n\t\t\t}\n\t\t\tobs_add_main_rendered_callback(decklink_ui_render, &context_preview);\n\n\t\t\tobs_output_set_media(context_preview.output, context_preview.video_queue, obs_get_audio());\n\t\t\tbool started = obs_output_start(context_preview.output);\n\n\t\t\tpreview_output_running = started;\n\t\t\tif (!shutting_down)\n\t\t\t\tdoUI->PreviewOutputStateChanged(started);\n\n\t\t\tif (!started)\n\t\t\t\tpreview_output_stop();\n\t\t} else {\n\t\t\tobs_output_release(output);\n\t\t}\n\t}\n}\n\nvoid preview_output_toggle()\n{\n\tif (preview_output_running)\n\t\tpreview_output_stop();\n\telse\n\t\tpreview_output_start();\n}\n\nvoid on_preview_scene_changed(enum obs_frontend_event event, void *param)\n{\n\tauto ctx = (struct decklink_ui_output *)param;\n\tswitch (event) {\n\tcase OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED:\n\tcase OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:\n\t\tobs_source_release(ctx->current_source);\n\t\tctx->current_source = obs_frontend_get_current_preview_scene();\n\t\tbreak;\n\tcase OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED:\n\t\tobs_source_release(ctx->current_source);\n\t\tctx->current_source = obs_frontend_get_current_scene();\n\t\tbreak;\n\tcase OBS_FRONTEND_EVENT_SCENE_CHANGED:\n\t\tif (!obs_frontend_preview_program_mode_active()) {\n\t\t\tobs_source_release(ctx->current_source);\n\t\t\tctx->current_source = obs_frontend_get_current_scene();\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nstatic void decklink_ui_render(void *param)\n{\n\tauto *const ctx = (struct decklink_ui_output *)param;\n\n\tuint32_t width = 0;\n\tuint32_t height = 0;\n\tgs_texture_t *tex = nullptr;\n\n\tif (ctx == &context) {\n\t\tif (!main_output_running)\n\t\t\treturn;\n\n\t\ttex = obs_get_main_texture();\n\t\tif (!tex)\n\t\t\treturn;\n\n\t\twidth = gs_texture_get_width(tex);\n\t\theight = gs_texture_get_height(tex);\n\t} else if (ctx == &context_preview) {\n\t\tif (!preview_output_running)\n\t\t\treturn;\n\n\t\tif (!ctx->current_source)\n\t\t\treturn;\n\n\t\twidth = obs_source_get_base_width(ctx->current_source);\n\t\theight = obs_source_get_base_height(ctx->current_source);\n\n\t\tgs_texrender_t *const texrender_premultiplied = ctx->texrender_premultiplied;\n\t\tif (!gs_texrender_begin(texrender_premultiplied, width, height))\n\t\t\treturn;\n\n\t\tstruct vec4 background;\n\t\tvec4_zero(&background);\n\n\t\tgs_clear(GS_CLEAR_COLOR, &background, 0.0f, 0);\n\t\tgs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f);\n\n\t\tgs_blend_state_push();\n\t\tgs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);\n\n\t\tobs_source_video_render(ctx->current_source);\n\n\t\tgs_blend_state_pop();\n\t\tgs_texrender_end(texrender_premultiplied);\n\n\t\ttex = gs_texrender_get_texture(texrender_premultiplied);\n\t} else {\n\t\treturn;\n\t}\n\n\tconst struct video_scale_info *const conversion = obs_output_get_video_conversion(ctx->output);\n\tconst uint32_t scaled_width = conversion->width;\n\tconst uint32_t scaled_height = conversion->height;\n\n\tif (!gs_texrender_begin(ctx->texrender, scaled_width, scaled_height))\n\t\treturn;\n\n\tconst bool previous = gs_framebuffer_srgb_enabled();\n\tconst bool source_hdr = (ctx->ovi.colorspace == VIDEO_CS_2100_PQ) || (ctx->ovi.colorspace == VIDEO_CS_2100_HLG);\n\tconst bool target_hdr = source_hdr && (conversion->colorspace == VIDEO_CS_2100_PQ);\n\tgs_enable_framebuffer_srgb(!target_hdr);\n\tgs_enable_blending(false);\n\n\tgs_effect_t *const effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);\n\tgs_effect_set_texture_srgb(gs_effect_get_param_by_name(effect, \"image\"), tex);\n\tconst char *const tech_name = target_hdr ? \"DrawAlphaDivideR10L\"\n\t\t\t\t\t\t : (source_hdr ? \"DrawAlphaDivideTonemap\" : \"DrawAlphaDivide\");\n\twhile (gs_effect_loop(effect, tech_name)) {\n\t\tgs_effect_set_float(gs_effect_get_param_by_name(effect, \"multiplier\"),\n\t\t\t\t    obs_get_video_sdr_white_level() / 10000.f);\n\t\tgs_draw_sprite(tex, 0, 0, 0);\n\t}\n\n\tgs_enable_blending(true);\n\tgs_enable_framebuffer_srgb(previous);\n\n\tgs_texrender_end(ctx->texrender);\n\n\tconst size_t write_stage_index = ctx->stage_index;\n\tgs_stage_texture(ctx->stagesurfaces[write_stage_index], gs_texrender_get_texture(ctx->texrender));\n\tctx->surf_written[write_stage_index] = true;\n\n\tconst size_t read_stage_index = (write_stage_index + 1) % STAGE_BUFFER_COUNT;\n\tif (ctx->surf_written[read_stage_index]) {\n\t\tstruct video_frame output_frame;\n\t\tif (video_output_lock_frame(ctx->video_queue, &output_frame, 1, os_gettime_ns())) {\n\t\t\tgs_stagesurf_t *const read_surf = ctx->stagesurfaces[read_stage_index];\n\t\t\tif (gs_stagesurface_map(read_surf, &ctx->video_data, &ctx->video_linesize)) {\n\t\t\t\tuint32_t linesize = output_frame.linesize[0];\n\t\t\t\tfor (uint32_t i = 0; i < scaled_height; i++) {\n\t\t\t\t\tuint32_t dst_offset = linesize * i;\n\t\t\t\t\tuint32_t src_offset = ctx->video_linesize * i;\n\t\t\t\t\tmemcpy(output_frame.data[0] + dst_offset, ctx->video_data + src_offset,\n\t\t\t\t\t       linesize);\n\t\t\t\t}\n\n\t\t\t\tgs_stagesurface_unmap(read_surf);\n\t\t\t\tctx->video_data = nullptr;\n\t\t\t}\n\n\t\t\tvideo_output_unlock_frame(ctx->video_queue);\n\t\t}\n\t}\n\n\tctx->stage_index = read_stage_index;\n}\n\nvoid addOutputUI(void)\n{\n\tQAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text(\"Decklink Output\"));\n\n\tQMainWindow *window = (QMainWindow *)obs_frontend_get_main_window();\n\n\tobs_frontend_push_ui_translation(obs_module_get_string);\n\tdoUI = new DecklinkOutputUI(window);\n\tobs_frontend_pop_ui_translation();\n\n\tauto cb = []() {\n\t\tdoUI->ShowHideDialog();\n\t};\n\n\tQObject::connect(action, &QAction::triggered, action, cb);\n}\n\nstatic void OBSEvent(enum obs_frontend_event event, void *)\n{\n\tif (event == OBS_FRONTEND_EVENT_FINISHED_LOADING) {\n\t\tOBSData settings = load_settings();\n\n\t\tif (settings && obs_data_get_bool(settings, \"auto_start\"))\n\t\t\toutput_start();\n\n\t\tOBSData previewSettings = load_preview_settings();\n\n\t\tif (previewSettings && obs_data_get_bool(previewSettings, \"auto_start\"))\n\t\t\tpreview_output_start();\n\t} else if (event == OBS_FRONTEND_EVENT_EXIT) {\n\t\tshutting_down = true;\n\n\t\tif (preview_output_running)\n\t\t\tpreview_output_stop();\n\n\t\tif (main_output_running)\n\t\t\toutput_stop();\n\t}\n}\n\nbool obs_module_load(void)\n{\n\treturn true;\n}\n\nvoid obs_module_unload(void)\n{\n\tshutting_down = true;\n\n\tif (preview_output_running)\n\t\tpreview_output_stop();\n\n\tif (main_output_running)\n\t\toutput_stop();\n}\n\nvoid obs_module_post_load(void)\n{\n\tif (!obs_get_module(\"decklink\"))\n\t\treturn;\n\n\taddOutputUI();\n\n\tobs_frontend_add_event_callback(OBSEvent, nullptr);\n}\n"
  },
  {
    "path": "frontend/plugins/decklink-output-ui/decklink-ui-main.h",
    "content": "#pragma once\n\nvoid output_toggle();\nOBSData load_settings();\nvoid preview_output_toggle();\nOBSData load_preview_settings();\n"
  },
  {
    "path": "frontend/plugins/decklink-output-ui/forms/output.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>Output</class>\n <widget class=\"QDialog\" name=\"Output\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>785</width>\n    <height>484</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Decklink Output</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <property name=\"modal\">\n   <bool>false</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <property name=\"sizeConstraint\">\n    <enum>QLayout::SetDefaultConstraint</enum>\n   </property>\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>Output</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"propertiesLayout\"/>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <property name=\"spacing\">\n      <number>6</number>\n     </property>\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"outputButton\">\n       <property name=\"text\">\n        <string>Start</string>\n       </property>\n       <property name=\"checkable\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"label_2\">\n     <property name=\"text\">\n      <string>Preview Output</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"previewPropertiesLayout\"/>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"previewOutputButton\">\n       <property name=\"text\">\n        <string>Start</string>\n       </property>\n       <property name=\"checkable\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nfind_package(Qt6 REQUIRED Widgets)\n\nif(OS_LINUX OR OS_FREEBSD OR OS_OPENBSD)\n  find_package(X11 REQUIRED)\nendif()\n\nadd_library(frontend-tools MODULE)\nadd_library(OBS::frontend-tools ALIAS frontend-tools)\n\nif(NOT TARGET OBS::properties-view)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/shared/properties-view\" \"${CMAKE_BINARY_DIR}/shared/properties-view\")\nendif()\n\nif(NOT TARGET OBS::qt-plain-text-edit)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/shared/qt/plain-text-edit\" \"${CMAKE_BINARY_DIR}/shared/qt/plain-text-edit\")\nendif()\n\nif(NOT TARGET OBS::qt-wrappers)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/shared/qt/wrappers\" \"${CMAKE_BINARY_DIR}/shared/qt/wrappers\")\nendif()\n\ntarget_sources(\n  frontend-tools\n  PRIVATE\n    $<$<PLATFORM_ID:Darwin>:auto-scene-switcher-osx.mm>\n    $<$<PLATFORM_ID:Linux,FreeBSD,OpenBSD>:auto-scene-switcher-nix.cpp>\n    $<$<PLATFORM_ID:Windows>:auto-scene-switcher-win.cpp>\n    $<$<PLATFORM_ID:Windows>:captions-handler.cpp>\n    $<$<PLATFORM_ID:Windows>:captions-handler.hpp>\n    $<$<PLATFORM_ID:Windows>:captions-mssapi-stream.cpp>\n    $<$<PLATFORM_ID:Windows>:captions-mssapi-stream.hpp>\n    $<$<PLATFORM_ID:Windows>:captions-mssapi.cpp>\n    $<$<PLATFORM_ID:Windows>:captions-mssapi.hpp>\n    $<$<PLATFORM_ID:Windows>:captions.cpp>\n    $<$<PLATFORM_ID:Windows>:captions.hpp>\n    auto-scene-switcher.cpp\n    auto-scene-switcher.hpp\n    frontend-tools.c\n    output-timer.cpp\n    output-timer.hpp\n    tool-helpers.hpp\n)\n\ntarget_sources(\n  frontend-tools\n  PRIVATE forms/auto-scene-switcher.ui forms/captions.ui forms/output-timer.ui forms/scripts.ui\n)\n\ntarget_compile_options(\n  frontend-tools\n  PRIVATE $<$<PLATFORM_ID:Darwin>:-Wno-quoted-include-in-framework-header> $<$<PLATFORM_ID:Darwin>:-Wno-comma>\n)\n\ntarget_link_libraries(\n  frontend-tools\n  PRIVATE\n    OBS::frontend-api\n    OBS::libobs\n    OBS::properties-view\n    OBS::qt-plain-text-edit\n    OBS::qt-wrappers\n    Qt::Widgets\n    \"$<$<PLATFORM_ID:Darwin>:$<LINK_LIBRARY:FRAMEWORK,Cocoa>>\"\n    $<$<PLATFORM_ID:Linux,FreeBSD,OpenBSD>:X11::X11>\n)\n\nadd_subdirectory(\"${CMAKE_SOURCE_DIR}/shared/obs-scripting\" \"${CMAKE_BINARY_DIR}/shared/obs-scripting\")\n\nif(ENABLE_SCRIPTING AND TARGET OBS::scripting)\n  target_sources(frontend-tools PRIVATE scripts.cpp scripts.hpp)\n  target_link_libraries(frontend-tools PRIVATE OBS::scripting)\n  target_enable_feature(frontend-tools \"Scripting Support (Frontend)\" ENABLE_SCRIPTING)\nendif()\n\nif(OS_WINDOWS)\n  configure_file(cmake/windows/obs-module.rc.in frontend-tools.rc)\n  target_sources(frontend-tools PRIVATE frontend-tools.rc)\n\n  set_property(TARGET frontend-tools APPEND PROPERTY AUTORCC_OPTIONS --format-version 1)\nendif()\n\nset_target_properties_obs(\n  frontend-tools\n  PROPERTIES FOLDER frontend\n             PREFIX \"\"\n             AUTOMOC ON\n             AUTOUIC ON\n             AUTORCC ON\n             AUTOUIC_SEARCH_PATHS forms\n             XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES\n)\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/auto-scene-switcher-nix.cpp",
    "content": "#include <X11/Xlib.h>\n#include <X11/Xatom.h>\n#include <X11/Xutil.h>\n#undef Bool\n#undef CursorShape\n#undef Expose\n#undef KeyPress\n#undef KeyRelease\n#undef FocusIn\n#undef FocusOut\n#undef FontChange\n#undef None\n#undef Status\n#undef Unsorted\n#include <util/platform.h>\n#include \"auto-scene-switcher.hpp\"\n\nusing namespace std;\n\nstatic Display *xdisplay = 0;\n\nDisplay *disp()\n{\n\tif (!xdisplay)\n\t\txdisplay = XOpenDisplay(NULL);\n\n\treturn xdisplay;\n}\n\nvoid CleanupSceneSwitcher()\n{\n\tif (!xdisplay)\n\t\treturn;\n\n\tXCloseDisplay(xdisplay);\n\txdisplay = 0;\n}\n\nstatic bool ewmhIsSupported()\n{\n\tDisplay *display = disp();\n\tAtom netSupportingWmCheck = XInternAtom(display, \"_NET_SUPPORTING_WM_CHECK\", true);\n\tAtom actualType;\n\tint format = 0;\n\tunsigned long num = 0, bytes = 0;\n\tunsigned char *data = NULL;\n\tWindow ewmh_window = 0;\n\tWindow root_window = 0;\n\n\troot_window = DefaultRootWindow(display);\n\tif (!root_window) {\n\t\treturn false;\n\t}\n\n\tint status = XGetWindowProperty(display, root_window, netSupportingWmCheck, 0L, 1L, false, XA_WINDOW,\n\t\t\t\t\t&actualType, &format, &num, &bytes, &data);\n\n\tif (status == Success) {\n\t\tif (num > 0) {\n\t\t\tewmh_window = ((Window *)data)[0];\n\t\t}\n\t\tif (data) {\n\t\t\tXFree(data);\n\t\t\tdata = NULL;\n\t\t}\n\t}\n\n\tif (ewmh_window) {\n\t\tstatus = XGetWindowProperty(display, ewmh_window, netSupportingWmCheck, 0L, 1L, false, XA_WINDOW,\n\t\t\t\t\t    &actualType, &format, &num, &bytes, &data);\n\t\tif (status != Success || num == 0 || ewmh_window != ((Window *)data)[0]) {\n\t\t\tewmh_window = 0;\n\t\t}\n\t\tif (status == Success && data) {\n\t\t\tXFree(data);\n\t\t}\n\t}\n\n\treturn ewmh_window != 0;\n}\n\nstatic std::vector<Window> getTopLevelWindows()\n{\n\tstd::vector<Window> res;\n\n\tres.resize(0);\n\n\tif (!ewmhIsSupported()) {\n\t\treturn res;\n\t}\n\n\tAtom netClList = XInternAtom(disp(), \"_NET_CLIENT_LIST\", true);\n\tAtom actualType;\n\tint format;\n\tunsigned long num, bytes;\n\tWindow *data = 0;\n\n\tfor (int i = 0; i < ScreenCount(disp()); ++i) {\n\t\tWindow rootWin = RootWindow(disp(), i);\n\t\tif (!rootWin) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tint status = XGetWindowProperty(disp(), rootWin, netClList, 0L, ~0L, false, AnyPropertyType,\n\t\t\t\t\t\t&actualType, &format, &num, &bytes, (uint8_t **)&data);\n\n\t\tif (status != Success) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (unsigned long i = 0; i < num; ++i)\n\t\t\tres.emplace_back(data[i]);\n\n\t\tXFree(data);\n\t}\n\n\treturn res;\n}\n\nstatic std::string GetWindowTitle(size_t i)\n{\n\tWindow w = getTopLevelWindows().at(i);\n\tif (!w) {\n\t\treturn \"\";\n\t}\n\tstd::string windowTitle;\n\tchar *name;\n\n\tint status = XFetchName(disp(), w, &name);\n\tif (status >= Success && name != nullptr) {\n\t\tstd::string str(name);\n\t\twindowTitle = str;\n\t\tXFree(name);\n\t} else {\n\t\tXTextProperty xtp_new_name;\n\t\tif (XGetWMName(disp(), w, &xtp_new_name) != 0 && xtp_new_name.value != nullptr) {\n\t\t\tstd::string str((const char *)xtp_new_name.value);\n\t\t\twindowTitle = str;\n\t\t\tXFree(xtp_new_name.value);\n\t\t}\n\t}\n\n\treturn windowTitle;\n}\n\nvoid GetWindowList(vector<string> &windows)\n{\n\twindows.resize(0);\n\n\tfor (size_t i = 0; i < getTopLevelWindows().size(); ++i) {\n\t\tif (GetWindowTitle(i) != \"\")\n\t\t\twindows.emplace_back(GetWindowTitle(i));\n\t}\n}\n\nvoid GetCurrentWindowTitle(string &title)\n{\n\tif (!ewmhIsSupported()) {\n\t\treturn;\n\t}\n\n\tAtom active = XInternAtom(disp(), \"_NET_ACTIVE_WINDOW\", true);\n\tAtom actualType;\n\tint format;\n\tunsigned long num, bytes;\n\tWindow *data = 0;\n\tchar *name;\n\n\tWindow rootWin = RootWindow(disp(), 0);\n\tif (!rootWin) {\n\t\treturn;\n\t}\n\n\tXGetWindowProperty(disp(), rootWin, active, 0L, ~0L, false, AnyPropertyType, &actualType, &format, &num, &bytes,\n\t\t\t   (uint8_t **)&data);\n\n\tif (!data[0]) {\n\t\treturn;\n\t}\n\tint status = XFetchName(disp(), data[0], &name);\n\n\tif (status >= Success && name != nullptr) {\n\t\tstd::string str(name);\n\t\ttitle = str;\n\t} else {\n\t\tXTextProperty xtp_new_name;\n\t\tif (XGetWMName(disp(), data[0], &xtp_new_name) != 0 && xtp_new_name.value != nullptr) {\n\t\t\tstd::string str((const char *)xtp_new_name.value);\n\t\t\ttitle = str;\n\t\t\tXFree(xtp_new_name.value);\n\t\t}\n\t}\n\n\tXFree(name);\n}\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/auto-scene-switcher-osx.mm",
    "content": "#import <AppKit/AppKit.h>\n#include <util/platform.h>\n#include \"auto-scene-switcher.hpp\"\n\nusing namespace std;\n\nvoid GetWindowList(vector<string> &windows)\n{\n    windows.resize(0);\n\n    @autoreleasepool {\n        NSWorkspace *ws = [NSWorkspace sharedWorkspace];\n        NSArray *array = [ws runningApplications];\n        for (NSRunningApplication *app in array) {\n            NSString *name = app.localizedName;\n            if (!name)\n                continue;\n\n            const char *str = name.UTF8String;\n            if (str && *str)\n                windows.emplace_back(str);\n        }\n    }\n}\n\nvoid GetCurrentWindowTitle(string &title)\n{\n    title.resize(0);\n\n    @autoreleasepool {\n        NSWorkspace *ws = [NSWorkspace sharedWorkspace];\n        NSRunningApplication *app = [ws frontmostApplication];\n        if (app) {\n            NSString *name = app.localizedName;\n            if (!name)\n                return;\n\n            const char *str = name.UTF8String;\n            if (str && *str)\n                title = str;\n        }\n    }\n}\n\nvoid CleanupSceneSwitcher() {}\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/auto-scene-switcher-win.cpp",
    "content": "#include <windows.h>\n#include <util/platform.h>\n#include \"auto-scene-switcher.hpp\"\n\nusing namespace std;\n\nstatic bool GetWindowTitle(HWND window, string &title)\n{\n\tsize_t len = (size_t)GetWindowTextLengthW(window);\n\twstring wtitle;\n\n\twtitle.resize(len);\n\tif (!GetWindowTextW(window, &wtitle[0], (int)len + 1))\n\t\treturn false;\n\n\tlen = os_wcs_to_utf8(wtitle.c_str(), 0, nullptr, 0);\n\ttitle.resize(len);\n\tos_wcs_to_utf8(wtitle.c_str(), 0, &title[0], len + 1);\n\treturn true;\n}\n\nstatic bool WindowValid(HWND window)\n{\n\tLONG_PTR styles, ex_styles;\n\tRECT rect;\n\tDWORD id;\n\n\tif (!IsWindowVisible(window))\n\t\treturn false;\n\tGetWindowThreadProcessId(window, &id);\n\tif (id == GetCurrentProcessId())\n\t\treturn false;\n\n\tGetClientRect(window, &rect);\n\tstyles = GetWindowLongPtr(window, GWL_STYLE);\n\tex_styles = GetWindowLongPtr(window, GWL_EXSTYLE);\n\n\tif (ex_styles & WS_EX_TOOLWINDOW)\n\t\treturn false;\n\tif (styles & WS_CHILD)\n\t\treturn false;\n\n\treturn true;\n}\n\nvoid GetWindowList(vector<string> &windows)\n{\n\tHWND window = GetWindow(GetDesktopWindow(), GW_CHILD);\n\n\twhile (window) {\n\t\tstring title;\n\t\tif (WindowValid(window) && GetWindowTitle(window, title))\n\t\t\twindows.emplace_back(title);\n\t\twindow = GetNextWindow(window, GW_HWNDNEXT);\n\t}\n}\n\nvoid GetCurrentWindowTitle(string &title)\n{\n\tHWND window = GetForegroundWindow();\n\tDWORD id;\n\n\tGetWindowThreadProcessId(window, &id);\n\tif (id == GetCurrentProcessId()) {\n\t\ttitle = \"\";\n\t\treturn;\n\t}\n\tGetWindowTitle(window, title);\n}\n\nvoid CleanupSceneSwitcher() {}\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/auto-scene-switcher.cpp",
    "content": "#include <obs-frontend-api.h>\n#include <obs-module.h>\n#include <obs.hpp>\n#include <util/util.hpp>\n#include <QMainWindow>\n#include <QMessageBox>\n#include <QAction>\n#include \"auto-scene-switcher.hpp\"\n#include \"tool-helpers.hpp\"\n\n#include <condition_variable>\n#include <chrono>\n#include <string>\n#include <vector>\n#include <thread>\n#include <regex>\n#include <mutex>\n\nusing namespace std;\n\n#define DEFAULT_INTERVAL 300\n\nstruct SceneSwitch {\n\tOBSWeakSource scene;\n\tstring window;\n\tregex re;\n\n\tinline SceneSwitch(OBSWeakSource scene_, const char *window_) : scene(scene_), window(window_), re(window_) {}\n};\n\nstatic inline bool WeakSourceValid(obs_weak_source_t *ws)\n{\n\tOBSSourceAutoRelease source = obs_weak_source_get_source(ws);\n\treturn !!source;\n}\n\nstruct SwitcherData {\n\tthread th;\n\tcondition_variable cv;\n\tmutex m;\n\tbool stop = false;\n\n\tvector<SceneSwitch> switches;\n\tOBSWeakSource nonMatchingScene;\n\tint interval = DEFAULT_INTERVAL;\n\tbool switchIfNotMatching = false;\n\n\tvoid Thread();\n\tvoid Start();\n\tvoid Stop();\n\n\tvoid Prune()\n\t{\n\t\tfor (size_t i = 0; i < switches.size(); i++) {\n\t\t\tSceneSwitch &s = switches[i];\n\t\t\tif (!WeakSourceValid(s.scene))\n\t\t\t\tswitches.erase(switches.begin() + i--);\n\t\t}\n\n\t\tif (nonMatchingScene && !WeakSourceValid(nonMatchingScene)) {\n\t\t\tswitchIfNotMatching = false;\n\t\t\tnonMatchingScene = nullptr;\n\t\t}\n\t}\n\n\tinline ~SwitcherData() { Stop(); }\n};\n\nstatic SwitcherData *switcher = nullptr;\n\nstatic inline QString MakeSwitchName(const QString &scene, const QString &window)\n{\n\treturn QStringLiteral(\"[\") + scene + QStringLiteral(\"]: \") + window;\n}\n\nSceneSwitcher::SceneSwitcher(QWidget *parent) : QDialog(parent), ui(new Ui_SceneSwitcher)\n{\n\tui->setupUi(this);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tlock_guard<mutex> lock(switcher->m);\n\n\tswitcher->Prune();\n\n\tBPtr<char *> scenes = obs_frontend_get_scene_names();\n\tchar **temp = scenes;\n\twhile (*temp) {\n\t\tconst char *name = *temp;\n\t\tui->scenes->addItem(name);\n\t\tui->noMatchSwitchScene->addItem(name);\n\t\ttemp++;\n\t}\n\n\tif (switcher->switchIfNotMatching)\n\t\tui->noMatchSwitch->setChecked(true);\n\telse\n\t\tui->noMatchDontSwitch->setChecked(true);\n\n\tui->noMatchSwitchScene->setCurrentText(GetWeakSourceName(switcher->nonMatchingScene).c_str());\n\tui->checkInterval->setValue(switcher->interval);\n\n\tvector<string> windows;\n\tGetWindowList(windows);\n\n\tfor (string &window : windows)\n\t\tui->windows->addItem(window.c_str());\n\n\tfor (auto &s : switcher->switches) {\n\t\tstring sceneName = GetWeakSourceName(s.scene);\n\t\tQString text = MakeSwitchName(sceneName.c_str(), s.window.c_str());\n\n\t\tQListWidgetItem *item = new QListWidgetItem(text, ui->switches);\n\t\titem->setData(Qt::UserRole, s.window.c_str());\n\t}\n\n\tif (switcher->th.joinable())\n\t\tSetStarted();\n\telse\n\t\tSetStopped();\n\n\tloading = false;\n\tconnect(this, &QDialog::finished, this, &SceneSwitcher::finished);\n}\n\nvoid SceneSwitcher::finished()\n{\n\tobs_frontend_save();\n}\n\nint SceneSwitcher::FindByData(const QString &window)\n{\n\tint count = ui->switches->count();\n\tint idx = -1;\n\n\tfor (int i = 0; i < count; i++) {\n\t\tQListWidgetItem *item = ui->switches->item(i);\n\t\tQString itemWindow = item->data(Qt::UserRole).toString();\n\n\t\tif (itemWindow == window) {\n\t\t\tidx = i;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn idx;\n}\n\nvoid SceneSwitcher::on_switches_currentRowChanged(int idx)\n{\n\tif (loading)\n\t\treturn;\n\tif (idx == -1)\n\t\treturn;\n\n\tQListWidgetItem *item = ui->switches->item(idx);\n\n\tQString window = item->data(Qt::UserRole).toString();\n\n\tlock_guard<mutex> lock(switcher->m);\n\tfor (auto &s : switcher->switches) {\n\t\tif (window.compare(s.window.c_str()) == 0) {\n\t\t\tstring name = GetWeakSourceName(s.scene);\n\t\t\tui->scenes->setCurrentText(name.c_str());\n\t\t\tui->windows->setCurrentText(window);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid SceneSwitcher::on_close_clicked()\n{\n\tdone(0);\n}\n\nvoid SceneSwitcher::on_add_clicked()\n{\n\tQString sceneName = ui->scenes->currentText();\n\tQString windowName = ui->windows->currentText();\n\n\tif (windowName.isEmpty())\n\t\treturn;\n\n\tOBSWeakSource source = GetWeakSourceByQString(sceneName);\n\tQVariant v = QVariant::fromValue(windowName);\n\n\tQString text = MakeSwitchName(sceneName, windowName);\n\n\tint idx = FindByData(windowName);\n\n\tif (idx == -1) {\n\t\ttry {\n\t\t\tlock_guard<mutex> lock(switcher->m);\n\t\t\tswitcher->switches.emplace_back(source, windowName.toUtf8().constData());\n\n\t\t\tQListWidgetItem *item = new QListWidgetItem(text, ui->switches);\n\t\t\titem->setData(Qt::UserRole, v);\n\t\t} catch (const regex_error &) {\n\t\t\tQMessageBox::warning(this, obs_module_text(\"InvalidRegex.Title\"),\n\t\t\t\t\t     obs_module_text(\"InvalidRegex.Text\"));\n\t\t}\n\t} else {\n\t\tQListWidgetItem *item = ui->switches->item(idx);\n\t\titem->setText(text);\n\n\t\tstring window = windowName.toUtf8().constData();\n\n\t\t{\n\t\t\tlock_guard<mutex> lock(switcher->m);\n\t\t\tfor (auto &s : switcher->switches) {\n\t\t\t\tif (s.window == window) {\n\t\t\t\t\ts.scene = source;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tui->switches->sortItems();\n\t}\n}\n\nvoid SceneSwitcher::on_remove_clicked()\n{\n\tQListWidgetItem *item = ui->switches->currentItem();\n\tif (!item)\n\t\treturn;\n\n\tstring window = item->data(Qt::UserRole).toString().toUtf8().constData();\n\n\t{\n\t\tlock_guard<mutex> lock(switcher->m);\n\t\tauto &switches = switcher->switches;\n\n\t\tfor (auto it = switches.begin(); it != switches.end(); ++it) {\n\t\t\tauto &s = *it;\n\n\t\t\tif (s.window == window) {\n\t\t\t\tswitches.erase(it);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tdelete item;\n}\n\nvoid SceneSwitcher::UpdateNonMatchingScene(const QString &name)\n{\n\tOBSSourceAutoRelease scene = obs_get_source_by_name(name.toUtf8().constData());\n\tOBSWeakSourceAutoRelease ws = obs_source_get_weak_source(scene);\n\n\tswitcher->nonMatchingScene = ws.Get();\n}\n\nvoid SceneSwitcher::on_noMatchDontSwitch_clicked()\n{\n\tif (loading)\n\t\treturn;\n\n\tlock_guard<mutex> lock(switcher->m);\n\tswitcher->switchIfNotMatching = false;\n}\n\nvoid SceneSwitcher::on_noMatchSwitch_clicked()\n{\n\tif (loading)\n\t\treturn;\n\n\tlock_guard<mutex> lock(switcher->m);\n\tswitcher->switchIfNotMatching = true;\n\tUpdateNonMatchingScene(ui->noMatchSwitchScene->currentText());\n}\n\nvoid SceneSwitcher::on_noMatchSwitchScene_currentTextChanged(const QString &text)\n{\n\tif (loading)\n\t\treturn;\n\n\tlock_guard<mutex> lock(switcher->m);\n\tUpdateNonMatchingScene(text);\n}\n\nvoid SceneSwitcher::on_checkInterval_valueChanged(int value)\n{\n\tif (loading)\n\t\treturn;\n\n\tlock_guard<mutex> lock(switcher->m);\n\tswitcher->interval = value;\n}\n\nvoid SceneSwitcher::SetStarted()\n{\n\tui->toggleStartButton->setText(obs_module_text(\"Stop\"));\n\tui->pluginRunningText->setText(obs_module_text(\"Active\"));\n}\n\nvoid SceneSwitcher::SetStopped()\n{\n\tui->toggleStartButton->setText(obs_module_text(\"Start\"));\n\tui->pluginRunningText->setText(obs_module_text(\"Inactive\"));\n}\n\nvoid SceneSwitcher::on_toggleStartButton_clicked()\n{\n\tif (switcher->th.joinable()) {\n\t\tswitcher->Stop();\n\t\tSetStopped();\n\t} else {\n\t\tswitcher->Start();\n\t\tSetStarted();\n\t}\n}\n\nstatic void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *)\n{\n\tif (saving) {\n\t\tlock_guard<mutex> lock(switcher->m);\n\t\tOBSDataAutoRelease obj = obs_data_create();\n\t\tOBSDataArrayAutoRelease array = obs_data_array_create();\n\n\t\tswitcher->Prune();\n\n\t\tfor (SceneSwitch &s : switcher->switches) {\n\t\t\tOBSDataAutoRelease array_obj = obs_data_create();\n\n\t\t\tOBSSourceAutoRelease source = obs_weak_source_get_source(s.scene);\n\t\t\tif (source) {\n\t\t\t\tconst char *n = obs_source_get_name(source);\n\t\t\t\tobs_data_set_string(array_obj, \"scene\", n);\n\t\t\t\tobs_data_set_string(array_obj, \"window_title\", s.window.c_str());\n\t\t\t\tobs_data_array_push_back(array, array_obj);\n\t\t\t}\n\t\t}\n\n\t\tstring nonMatchingSceneName = GetWeakSourceName(switcher->nonMatchingScene);\n\n\t\tobs_data_set_int(obj, \"interval\", switcher->interval);\n\t\tobs_data_set_string(obj, \"non_matching_scene\", nonMatchingSceneName.c_str());\n\t\tobs_data_set_bool(obj, \"switch_if_not_matching\", switcher->switchIfNotMatching);\n\t\tobs_data_set_bool(obj, \"active\", switcher->th.joinable());\n\t\tobs_data_set_array(obj, \"switches\", array);\n\n\t\tobs_data_set_obj(save_data, \"auto-scene-switcher\", obj);\n\t} else {\n\t\tswitcher->m.lock();\n\n\t\tOBSDataAutoRelease obj = obs_data_get_obj(save_data, \"auto-scene-switcher\");\n\t\tOBSDataArrayAutoRelease array = obs_data_get_array(obj, \"switches\");\n\t\tsize_t count = obs_data_array_count(array);\n\n\t\tif (!obj)\n\t\t\tobj = obs_data_create();\n\n\t\tobs_data_set_default_int(obj, \"interval\", DEFAULT_INTERVAL);\n\n\t\tswitcher->interval = obs_data_get_int(obj, \"interval\");\n\t\tswitcher->switchIfNotMatching = obs_data_get_bool(obj, \"switch_if_not_matching\");\n\t\tstring nonMatchingScene = obs_data_get_string(obj, \"non_matching_scene\");\n\t\tbool active = obs_data_get_bool(obj, \"active\");\n\n\t\tswitcher->nonMatchingScene = GetWeakSourceByName(nonMatchingScene.c_str());\n\n\t\tswitcher->switches.clear();\n\n\t\tfor (size_t i = 0; i < count; i++) {\n\t\t\tOBSDataAutoRelease array_obj = obs_data_array_item(array, i);\n\n\t\t\tconst char *scene = obs_data_get_string(array_obj, \"scene\");\n\t\t\tconst char *window = obs_data_get_string(array_obj, \"window_title\");\n\n\t\t\tswitcher->switches.emplace_back(GetWeakSourceByName(scene), window);\n\t\t}\n\n\t\tswitcher->m.unlock();\n\n\t\tif (active)\n\t\t\tswitcher->Start();\n\t\telse\n\t\t\tswitcher->Stop();\n\t}\n}\n\nvoid SwitcherData::Thread()\n{\n\tchrono::duration<long long, milli> duration = chrono::milliseconds(interval);\n\tstring lastTitle;\n\tstring title;\n\n\tfor (;;) {\n\t\tunique_lock<mutex> lock(m);\n\t\tOBSWeakSource scene;\n\t\tbool match = false;\n\n\t\tcv.wait_for(lock, duration);\n\t\tif (switcher->stop) {\n\t\t\tswitcher->stop = false;\n\t\t\tbreak;\n\t\t}\n\n\t\tduration = chrono::milliseconds(interval);\n\n\t\tGetCurrentWindowTitle(title);\n\n\t\tif (lastTitle != title) {\n\t\t\tswitcher->Prune();\n\n\t\t\tfor (SceneSwitch &s : switches) {\n\t\t\t\tif (s.window == title) {\n\t\t\t\t\tmatch = true;\n\t\t\t\t\tscene = s.scene;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* try regex */\n\t\t\tif (!match) {\n\t\t\t\tfor (SceneSwitch &s : switches) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tbool matches = regex_match(title, s.re);\n\t\t\t\t\t\tif (matches) {\n\t\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\t\tscene = s.scene;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (const regex_error &) {\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!match && switchIfNotMatching && nonMatchingScene) {\n\t\t\t\tmatch = true;\n\t\t\t\tscene = nonMatchingScene;\n\t\t\t}\n\n\t\t\tif (match) {\n\t\t\t\tOBSSourceAutoRelease source = obs_weak_source_get_source(scene);\n\t\t\t\tOBSSourceAutoRelease currentSource = obs_frontend_get_current_scene();\n\n\t\t\t\tif (source && source != currentSource)\n\t\t\t\t\tobs_frontend_set_current_scene(source);\n\t\t\t}\n\t\t}\n\n\t\tlastTitle = title;\n\t}\n}\n\nvoid SwitcherData::Start()\n{\n\tif (!switcher->th.joinable())\n\t\tswitcher->th = thread([]() { switcher->Thread(); });\n}\n\nvoid SwitcherData::Stop()\n{\n\tif (th.joinable()) {\n\t\t{\n\t\t\tlock_guard<mutex> lock(m);\n\t\t\tstop = true;\n\t\t}\n\t\tcv.notify_one();\n\t\tth.join();\n\t}\n}\n\nextern \"C\" void FreeSceneSwitcher()\n{\n\tCleanupSceneSwitcher();\n\n\tdelete switcher;\n\tswitcher = nullptr;\n}\n\nstatic void OBSEvent(enum obs_frontend_event event, void *)\n{\n\tif (event == OBS_FRONTEND_EVENT_EXIT)\n\t\tFreeSceneSwitcher();\n}\n\nextern \"C\" void InitSceneSwitcher()\n{\n#if !defined(__APPLE__) && !defined(_WIN32)\n\tif (QApplication::platformName().contains(\"wayland\"))\n\t\treturn;\n#endif\n\n\tQAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text(\"SceneSwitcher\"));\n\n\tswitcher = new SwitcherData;\n\n\tauto cb = []() {\n\t\tobs_frontend_push_ui_translation(obs_module_get_string);\n\n\t\tQMainWindow *window = (QMainWindow *)obs_frontend_get_main_window();\n\n\t\tSceneSwitcher ss(window);\n\t\tss.exec();\n\n\t\tobs_frontend_pop_ui_translation();\n\t};\n\n\tobs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);\n\tobs_frontend_add_event_callback(OBSEvent, nullptr);\n\n\tQObject::connect(action, &QAction::triggered, action, cb);\n}\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/auto-scene-switcher.hpp",
    "content": "#pragma once\n\n#include <QDialog>\n#include <memory>\n#include <vector>\n#include <string>\n\n#include \"ui_auto-scene-switcher.h\"\n\nstruct obs_weak_source;\ntypedef struct obs_weak_source obs_weak_source_t;\n\nclass QCloseEvent;\n\nclass SceneSwitcher : public QDialog {\n\tQ_OBJECT\n\npublic:\n\tstd::unique_ptr<Ui_SceneSwitcher> ui;\n\tbool loading = true;\n\n\tSceneSwitcher(QWidget *parent);\n\n\tvoid SetStarted();\n\tvoid SetStopped();\n\n\tint FindByData(const QString &window);\n\n\tvoid UpdateNonMatchingScene(const QString &name);\n\npublic slots:\n\tvoid on_switches_currentRowChanged(int idx);\n\tvoid on_close_clicked();\n\tvoid on_add_clicked();\n\tvoid on_remove_clicked();\n\tvoid on_noMatchDontSwitch_clicked();\n\tvoid on_noMatchSwitch_clicked();\n\tvoid on_noMatchSwitchScene_currentTextChanged(const QString &text);\n\tvoid on_checkInterval_valueChanged(int value);\n\tvoid on_toggleStartButton_clicked();\n\tvoid finished();\n};\n\nvoid GetWindowList(std::vector<std::string> &windows);\nvoid GetCurrentWindowTitle(std::string &title);\nvoid CleanupSceneSwitcher();\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/captions-handler.cpp",
    "content": "#include \"captions-handler.hpp\"\n\ncaptions_handler::captions_handler(captions_cb callback, enum audio_format format, uint32_t sample_rate) : cb(callback)\n{\n\tif (!reset_resampler(format, sample_rate))\n\t\tthrow CAPTIONS_ERROR_GENERIC_FAIL;\n}\n\nbool captions_handler::reset_resampler(enum audio_format format, uint32_t sample_rate)\ntry {\n\tobs_audio_info ai;\n\tif (!obs_get_audio_info(&ai))\n\t\tthrow std::string(\"Failed to get OBS audio info\");\n\n\tresample_info src = {ai.samples_per_sec, AUDIO_FORMAT_FLOAT_PLANAR, ai.speakers};\n\tresample_info dst = {sample_rate, format, SPEAKERS_MONO};\n\n\tif (!resampler.reset(dst, src))\n\t\tthrow std::string(\"Failed to create audio resampler\");\n\n\treturn true;\n\n} catch (std::string text) {\n\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, text.c_str());\n\treturn false;\n}\n\nvoid captions_handler::push_audio(const audio_data *audio)\n{\n\tuint8_t *out[MAX_AV_PLANES];\n\tuint32_t frames;\n\tuint64_t ts_offset;\n\tbool success;\n\n\tsuccess = audio_resampler_resample(resampler, out, &frames, &ts_offset, (const uint8_t *const *)audio->data,\n\t\t\t\t\t   audio->frames);\n\tif (success)\n\t\tpcm_data(out[0], frames);\n}\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/captions-handler.hpp",
    "content": "#pragma once\n\n#include <media-io/audio-resampler.h>\n#include <obs-module.h>\n#include <functional>\n#include <string>\n\nclass resampler_obj {\n\taudio_resampler_t *resampler = nullptr;\n\npublic:\n\tinline ~resampler_obj() { audio_resampler_destroy(resampler); }\n\n\tinline bool reset(const resample_info &dst, const resample_info &src)\n\t{\n\t\taudio_resampler_destroy(resampler);\n\t\tresampler = audio_resampler_create(&dst, &src);\n\t\treturn !!resampler;\n\t}\n\n\tinline operator audio_resampler_t *() { return resampler; }\n};\n\n/* ------------------------------------------------------------------------- */\n\ntypedef std::function<void(const std::string &)> captions_cb;\n\n#define CAPTIONS_ERROR_GENERIC_FAIL std::string(obs_module_text(\"Captions.Error.GenericFail\"))\n\n/* ------------------------------------------------------------------------- */\n\nclass captions_handler {\n\tcaptions_cb cb;\n\tresampler_obj resampler;\n\nprotected:\n\tinline void callback(const std::string &text) { cb(text); }\n\n\tvirtual void pcm_data(const void *data, size_t frames) = 0;\n\n\t/* always resamples to 1 channel */\n\tbool reset_resampler(enum audio_format format, uint32_t sample_rate);\n\npublic:\n\t/* throw std::string for errors shown to users */\n\tcaptions_handler(captions_cb callback, enum audio_format format, uint32_t sample_rate);\n\tvirtual ~captions_handler() {}\n\n\tvoid push_audio(const audio_data *audio);\n};\n\n/* ------------------------------------------------------------------------- */\n\nstruct captions_handler_info {\n\tstd::string (*name)(void);\n\tcaptions_handler *(*create)(captions_cb cb, const std::string &lang);\n};\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/captions-mssapi-stream.cpp",
    "content": "#include \"captions-mssapi-stream.hpp\"\n#include \"captions-mssapi.hpp\"\n#include <mmreg.h>\n#include <util/windows/CoTaskMemPtr.hpp>\n#include <util/threading.h>\n#include <util/base.h>\n\nusing namespace std;\n\n#if 0\n#define debugfunc(format, ...) blog(LOG_DEBUG, \"[Captions] %s(\" format \")\", __FUNCTION__, ##__VA_ARGS__)\n#else\n#define debugfunc(format, ...)\n#endif\n\nCaptionStream::CaptionStream(DWORD samplerate_, mssapi_captions *handler_)\n\t: handler(handler_),\n\t  samplerate(samplerate_),\n\t  event(CreateEvent(nullptr, false, false, nullptr))\n{\n\tbuf_info.ulMsMinNotification = 50;\n\tbuf_info.ulMsBufferSize = 500;\n\tbuf_info.ulMsEventBias = 0;\n\n\tformat.wFormatTag = WAVE_FORMAT_PCM;\n\tformat.nChannels = 1;\n\tformat.nSamplesPerSec = 16000;\n\tformat.nAvgBytesPerSec = format.nSamplesPerSec * sizeof(uint16_t);\n\tformat.nBlockAlign = 2;\n\tformat.wBitsPerSample = 16;\n\tformat.cbSize = sizeof(format);\n}\n\nvoid CaptionStream::Stop()\n{\n\t{\n\t\tlock_guard<mutex> lock(m);\n\t\tdeque_free(buf);\n\t}\n\n\tcv.notify_one();\n}\n\nvoid CaptionStream::PushAudio(const void *data, size_t frames)\n{\n\tbool ready = false;\n\n\tlock_guard<mutex> lock(m);\n\tdeque_push_back(buf, data, frames * sizeof(int16_t));\n\twrite_pos += frames * sizeof(int16_t);\n\n\tif (wait_size && buf->size >= wait_size)\n\t\tready = true;\n\tif (ready)\n\t\tcv.notify_one();\n}\n\n// IUnknown methods\n\nSTDMETHODIMP CaptionStream::QueryInterface(REFIID riid, void **ppv)\n{\n\tif (riid == IID_IUnknown) {\n\t\tAddRef();\n\t\t*ppv = this;\n\n\t} else if (riid == IID_IStream) {\n\t\tAddRef();\n\t\t*ppv = (IStream *)this;\n\n\t} else if (riid == IID_ISpStreamFormat) {\n\t\tAddRef();\n\t\t*ppv = (ISpStreamFormat *)this;\n\n\t} else if (riid == IID_ISpAudio) {\n\t\tAddRef();\n\t\t*ppv = (ISpAudio *)this;\n\n\t} else {\n\t\t*ppv = nullptr;\n\t\treturn E_NOINTERFACE;\n\t}\n\n\treturn NOERROR;\n}\n\nSTDMETHODIMP_(ULONG) CaptionStream::AddRef()\n{\n\treturn (ULONG)os_atomic_inc_long(&refs);\n}\n\nSTDMETHODIMP_(ULONG) CaptionStream::Release()\n{\n\tULONG new_refs = (ULONG)os_atomic_dec_long(&refs);\n\tif (!new_refs)\n\t\tdelete this;\n\n\treturn new_refs;\n}\n\n// ISequentialStream methods\n\nSTDMETHODIMP CaptionStream::Read(void *data, ULONG bytes, ULONG *read_bytes)\n{\n\tHRESULT hr = S_OK;\n\tsize_t cur_size;\n\n\tdebugfunc(\"data, %lu, read_bytes\", bytes);\n\tif (!data)\n\t\treturn STG_E_INVALIDPOINTER;\n\n\t{\n\t\tlock_guard<mutex> lock1(m);\n\t\twait_size = bytes;\n\t\tcur_size = buf->size;\n\t}\n\n\tunique_lock<mutex> lock(m);\n\n\tif (bytes > cur_size)\n\t\tcv.wait(lock);\n\n\tif (bytes > (ULONG)buf->size) {\n\t\tbytes = (ULONG)buf->size;\n\t\thr = S_FALSE;\n\t}\n\tif (bytes)\n\t\tdeque_pop_front(buf, data, bytes);\n\tif (read_bytes)\n\t\t*read_bytes = bytes;\n\n\twait_size = 0;\n\tpos.QuadPart += bytes;\n\treturn hr;\n}\n\nSTDMETHODIMP CaptionStream::Write(const void *, ULONG bytes, ULONG *)\n{\n\tdebugfunc(\"data, %lu, written_bytes\", bytes);\n\n\treturn STG_E_INVALIDFUNCTION;\n}\n\n// IStream methods\n\nSTDMETHODIMP CaptionStream::Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *new_pos)\n{\n\tdebugfunc(\"%lld, %lx, new_pos\", move, origin);\n\n\tif (!new_pos)\n\t\treturn E_POINTER;\n\n\tif (origin != SEEK_CUR || move.QuadPart != 0)\n\t\treturn E_NOTIMPL;\n\n\t*new_pos = pos;\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::SetSize(ULARGE_INTEGER new_size)\n{\n\tdebugfunc(\"%llu\", new_size);\n\treturn STG_E_INVALIDFUNCTION;\n}\n\nSTDMETHODIMP CaptionStream::CopyTo(IStream *stream, ULARGE_INTEGER bytes, ULARGE_INTEGER *read_bytes,\n\t\t\t\t   ULARGE_INTEGER *written_bytes)\n{\n\tHRESULT hr;\n\n\tdebugfunc(\"stream, %llu, read_bytes, written_bytes\", bytes);\n\n\tif (!stream)\n\t\treturn STG_E_INVALIDPOINTER;\n\n\tULONG written = 0;\n\tif (bytes.QuadPart > (ULONGLONG)buf->size)\n\t\tbytes.QuadPart = (ULONGLONG)buf->size;\n\n\tlock_guard<mutex> lock(m);\n\ttemp_buf.resize((size_t)bytes.QuadPart);\n\tdeque_peek_front(buf, &temp_buf[0], (size_t)bytes.QuadPart);\n\n\thr = stream->Write(temp_buf.data(), (ULONG)bytes.QuadPart, &written);\n\n\tif (read_bytes)\n\t\t*read_bytes = bytes;\n\tif (written_bytes)\n\t\twritten_bytes->QuadPart = written;\n\n\treturn hr;\n}\n\nSTDMETHODIMP CaptionStream::Commit(DWORD commit_flags)\n{\n\tdebugfunc(\"%lx\", commit_flags);\n\t/* TODO? */\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::Revert(void)\n{\n\tdebugfunc(\"\");\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::LockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type)\n{\n\tdebugfunc(\"%llu, %llu, %ld\", offset, size, type);\n\t/* TODO? */\n\treturn STG_E_INVALIDFUNCTION;\n}\n\nSTDMETHODIMP CaptionStream::UnlockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type)\n{\n\tdebugfunc(\"%llu, %llu, %ld\", offset, size, type);\n\t/* TODO? */\n\treturn STG_E_INVALIDFUNCTION;\n}\n\nstatic const wchar_t *stat_name = L\"Caption stream\";\n\nSTDMETHODIMP CaptionStream::Stat(STATSTG *stg, DWORD flag)\n{\n\tdebugfunc(\"stg, %lu\", flag);\n\n\tif (!stg)\n\t\treturn E_POINTER;\n\n\tlock_guard<mutex> lock(m);\n\t*stg = {};\n\tstg->type = STGTY_STREAM;\n\tstg->cbSize.QuadPart = (ULONGLONG)buf->size;\n\n\tif (flag == STATFLAG_DEFAULT) {\n\t\tsize_t byte_size = (wcslen(stat_name) + 1) * sizeof(wchar_t);\n\t\tstg->pwcsName = (wchar_t *)CoTaskMemAlloc(byte_size);\n\t\tmemcpy(stg->pwcsName, stat_name, byte_size);\n\t}\n\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::Clone(IStream **stream)\n{\n\tdebugfunc(\"stream\");\n\t*stream = nullptr;\n\treturn E_NOTIMPL;\n}\n\n// ISpStreamFormat methods\n\nSTDMETHODIMP CaptionStream::GetFormat(GUID *guid, WAVEFORMATEX **co_mem_wfex_out)\n{\n\tdebugfunc(\"guid, co_mem_wfex_out\");\n\n\tif (!guid || !co_mem_wfex_out)\n\t\treturn E_POINTER;\n\n\tif (format.wFormatTag == 0) {\n\t\t*co_mem_wfex_out = nullptr;\n\t\treturn S_OK;\n\t}\n\n\tvoid *wfex = CoTaskMemAlloc(sizeof(format));\n\tmemcpy(wfex, &format, sizeof(format));\n\n\t*co_mem_wfex_out = (WAVEFORMATEX *)wfex;\n\treturn S_OK;\n}\n\n// ISpAudio methods\n\nSTDMETHODIMP CaptionStream::SetState(SPAUDIOSTATE state_, ULONGLONG)\n{\n\tdebugfunc(\"%lu, reserved\", state_);\n\tstate = state_;\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::SetFormat(REFGUID guid_ref, const WAVEFORMATEX *wfex)\n{\n\tdebugfunc(\"guid, wfex\");\n\tif (!wfex)\n\t\treturn E_INVALIDARG;\n\n\tif (guid_ref == SPDFID_WaveFormatEx) {\n\t\tlock_guard<mutex> lock(m);\n\t\tmemcpy(&format, wfex, sizeof(format));\n\t\tif (!handler->reset_resampler(AUDIO_FORMAT_16BIT, wfex->nSamplesPerSec))\n\t\t\treturn E_FAIL;\n\n\t\t/* 50 msec */\n\t\tDWORD size = format.nSamplesPerSec / 20;\n\t\tDWORD byte_size = size * format.nBlockAlign;\n\t\tdeque_reserve(buf, (size_t)byte_size);\n\t}\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::GetStatus(SPAUDIOSTATUS *status)\n{\n\tdebugfunc(\"status\");\n\n\tif (!status)\n\t\treturn E_POINTER;\n\n\t/* TODO? */\n\tlock_guard<mutex> lock(m);\n\t*status = {};\n\tstatus->cbNonBlockingIO = (ULONG)buf->size;\n\tstatus->State = state;\n\tstatus->CurSeekPos = pos.QuadPart;\n\tstatus->CurDevicePos = write_pos;\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::SetBufferInfo(const SPAUDIOBUFFERINFO *buf_info_)\n{\n\tdebugfunc(\"buf_info\");\n\n\t/* TODO */\n\tbuf_info = *buf_info_;\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::GetBufferInfo(SPAUDIOBUFFERINFO *buf_info_)\n{\n\tdebugfunc(\"buf_info\");\n\tif (!buf_info_)\n\t\treturn E_POINTER;\n\n\t*buf_info_ = buf_info;\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::GetDefaultFormat(GUID *format, WAVEFORMATEX **co_mem_wfex_out)\n{\n\tdebugfunc(\"format, co_mem_wfex_out\");\n\n\tif (!format || !co_mem_wfex_out)\n\t\treturn E_POINTER;\n\n\tvoid *wfex = CoTaskMemAlloc(sizeof(format));\n\tmemcpy(wfex, &format, sizeof(format));\n\n\t*format = SPDFID_WaveFormatEx;\n\t*co_mem_wfex_out = (WAVEFORMATEX *)wfex;\n\treturn S_OK;\n}\n\nSTDMETHODIMP_(HANDLE) CaptionStream::EventHandle(void)\n{\n\tdebugfunc(\"\");\n\treturn event;\n}\n\nSTDMETHODIMP CaptionStream::GetVolumeLevel(ULONG *level)\n{\n\tdebugfunc(\"level\");\n\tif (!level)\n\t\treturn E_POINTER;\n\n\t*level = vol;\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::SetVolumeLevel(ULONG level)\n{\n\tdebugfunc(\"%lu\", level);\n\tvol = level;\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::GetBufferNotifySize(ULONG *size)\n{\n\tdebugfunc(\"size\");\n\tif (!size)\n\t\treturn E_POINTER;\n\t*size = notify_size;\n\treturn S_OK;\n}\n\nSTDMETHODIMP CaptionStream::SetBufferNotifySize(ULONG size)\n{\n\tdebugfunc(\"%lu\", size);\n\tnotify_size = size;\n\treturn S_OK;\n}\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/captions-mssapi-stream.hpp",
    "content": "#pragma once\n\n#include <windows.h>\n#include <sapi.h>\n#include <condition_variable>\n#include <mutex>\n#include <vector>\n#include <obs.h>\n#include <util/deque.h>\n#include <util/windows/WinHandle.hpp>\n\n#include <fstream>\n\nclass Deque {\n\tdeque buf = {};\n\npublic:\n\tinline ~Deque() { deque_free(&buf); }\n\tinline operator deque *() { return &buf; }\n\tinline deque *operator->() { return &buf; }\n};\n\nclass mssapi_captions;\n\nclass CaptionStream : public ISpAudio {\n\tvolatile long refs = 1;\n\tSPAUDIOBUFFERINFO buf_info = {};\n\tmssapi_captions *handler;\n\tULONG notify_size = 0;\n\tSPAUDIOSTATE state;\n\tWinHandle event;\n\tULONG vol = 0;\n\n\tstd::condition_variable cv;\n\tstd::mutex m;\n\tstd::vector<int16_t> temp_buf;\n\tWAVEFORMATEX format = {};\n\n\tDeque buf;\n\tULONG wait_size = 0;\n\tDWORD samplerate = 0;\n\tULARGE_INTEGER pos = {};\n\tULONGLONG write_pos = 0;\n\npublic:\n\tCaptionStream(DWORD samplerate, mssapi_captions *handler_);\n\n\tvoid Stop();\n\tvoid PushAudio(const void *data, size_t frames);\n\n\t// IUnknown methods\n\tSTDMETHODIMP QueryInterface(REFIID riid, void **ppv) override;\n\tSTDMETHODIMP_(ULONG) AddRef() override;\n\tSTDMETHODIMP_(ULONG) Release() override;\n\n\t// ISequentialStream methods\n\tSTDMETHODIMP Read(void *data, ULONG bytes, ULONG *read_bytes) override;\n\tSTDMETHODIMP Write(const void *data, ULONG bytes, ULONG *written_bytes) override;\n\n\t// IStream methods\n\tSTDMETHODIMP Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *new_pos) override;\n\tSTDMETHODIMP SetSize(ULARGE_INTEGER new_size) override;\n\tSTDMETHODIMP CopyTo(IStream *stream, ULARGE_INTEGER bytes, ULARGE_INTEGER *read_bytes,\n\t\t\t    ULARGE_INTEGER *written_bytes) override;\n\tSTDMETHODIMP Commit(DWORD commit_flags) override;\n\tSTDMETHODIMP Revert(void) override;\n\tSTDMETHODIMP LockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) override;\n\tSTDMETHODIMP UnlockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD type) override;\n\tSTDMETHODIMP Stat(STATSTG *stg, DWORD flags) override;\n\tSTDMETHODIMP Clone(IStream **stream) override;\n\n\t// ISpStreamFormat methods\n\tSTDMETHODIMP GetFormat(GUID *guid, WAVEFORMATEX **co_mem_wfex_out) override;\n\n\t// ISpAudio methods\n\tSTDMETHODIMP SetState(SPAUDIOSTATE state, ULONGLONG reserved) override;\n\tSTDMETHODIMP SetFormat(REFGUID guid_ref, const WAVEFORMATEX *wfex) override;\n\tSTDMETHODIMP GetStatus(SPAUDIOSTATUS *status) override;\n\tSTDMETHODIMP SetBufferInfo(const SPAUDIOBUFFERINFO *buf_info) override;\n\tSTDMETHODIMP GetBufferInfo(SPAUDIOBUFFERINFO *buf_info) override;\n\tSTDMETHODIMP GetDefaultFormat(GUID *format, WAVEFORMATEX **co_mem_wfex_out) override;\n\tSTDMETHODIMP_(HANDLE) EventHandle(void) override;\n\tSTDMETHODIMP GetVolumeLevel(ULONG *level) override;\n\tSTDMETHODIMP SetVolumeLevel(ULONG level) override;\n\tSTDMETHODIMP GetBufferNotifySize(ULONG *size) override;\n\tSTDMETHODIMP SetBufferNotifySize(ULONG size) override;\n};\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/captions-mssapi.cpp",
    "content": "#include \"captions-mssapi.hpp\"\n\n#define do_log(type, format, ...) blog(type, \"[Captions] \" format, ##__VA_ARGS__)\n\n#define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__)\n#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)\n\nmssapi_captions::mssapi_captions(captions_cb callback, const std::string &lang)\ntry : captions_handler(callback, AUDIO_FORMAT_16BIT, 16000) {\n\tHRESULT hr;\n\n\tstd::wstring wlang;\n\twlang.resize(lang.size());\n\n\tfor (size_t i = 0; i < lang.size(); i++)\n\t\twlang[i] = (wchar_t)lang[i];\n\n\tLCID lang_id = LocaleNameToLCID(wlang.c_str(), 0);\n\n\twchar_t lang_str[32];\n\t_snwprintf(lang_str, 31, L\"language=%x\", (int)lang_id);\n\n\tstop = CreateEvent(nullptr, false, false, nullptr);\n\tif (!stop.Valid())\n\t\tthrow \"Failed to create event\";\n\n\thr = SpFindBestToken(SPCAT_RECOGNIZERS, lang_str, nullptr, &token);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"SpFindBestToken failed\", hr);\n\n\thr = CoCreateInstance(CLSID_SpInprocRecognizer, nullptr, CLSCTX_ALL, __uuidof(ISpRecognizer),\n\t\t\t      (void **)&recognizer);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"CoCreateInstance for recognizer failed\", hr);\n\n\thr = recognizer->SetRecognizer(token);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"SetRecognizer failed\", hr);\n\n\thr = recognizer->SetRecoState(SPRST_INACTIVE);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"SetRecoState(SPRST_INACTIVE) failed\", hr);\n\n\thr = recognizer->CreateRecoContext(&context);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"CreateRecoContext failed\", hr);\n\n\tULONGLONG interest = SPFEI(SPEI_RECOGNITION) | SPFEI(SPEI_END_SR_STREAM);\n\thr = context->SetInterest(interest, interest);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"SetInterest failed\", hr);\n\n\thr = context->SetNotifyWin32Event();\n\tif (FAILED(hr))\n\t\tthrow HRError(\"SetNotifyWin32Event\", hr);\n\n\tnotify = context->GetNotifyEventHandle();\n\tif (notify == INVALID_HANDLE_VALUE)\n\t\tthrow HRError(\"GetNotifyEventHandle failed\", E_NOINTERFACE);\n\n\tsize_t sample_rate = audio_output_get_sample_rate(obs_get_audio());\n\taudio = new CaptionStream((DWORD)sample_rate, this);\n\taudio->Release();\n\n\thr = recognizer->SetInput(audio, false);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"SetInput failed\", hr);\n\n\thr = context->CreateGrammar(1, &grammar);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"CreateGrammar failed\", hr);\n\n\thr = grammar->LoadDictation(nullptr, SPLO_STATIC);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"LoadDictation failed\", hr);\n\n\ttry {\n\t\tt = std::thread([this]() { main_thread(); });\n\t} catch (...) {\n\t\tthrow \"Failed to create thread\";\n\t}\n\n} catch (const char *err) {\n\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, err);\n\tthrow CAPTIONS_ERROR_GENERIC_FAIL;\n\n} catch (HRError err) {\n\tblog(LOG_WARNING, \"%s: %s (%lX)\", __FUNCTION__, err.str, err.hr);\n\tthrow CAPTIONS_ERROR_GENERIC_FAIL;\n}\n\nmssapi_captions::~mssapi_captions()\n{\n\tif (t.joinable()) {\n\t\tSetEvent(stop);\n\t\tt.join();\n\t}\n}\n\nvoid mssapi_captions::main_thread()\ntry {\n\tHRESULT hr;\n\n\tos_set_thread_name(__FUNCTION__);\n\n\thr = grammar->SetDictationState(SPRS_ACTIVE);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"SetDictationState failed\", hr);\n\n\thr = recognizer->SetRecoState(SPRST_ACTIVE);\n\tif (FAILED(hr))\n\t\tthrow HRError(\"SetRecoState(SPRST_ACTIVE) failed\", hr);\n\n\tHANDLE events[] = {notify, stop};\n\n\tstarted = true;\n\n\tfor (;;) {\n\t\tDWORD ret = WaitForMultipleObjects(2, events, false, INFINITE);\n\t\tif (ret != WAIT_OBJECT_0)\n\t\t\tbreak;\n\n\t\tCSpEvent event;\n\t\tbool exit = false;\n\n\t\twhile (event.GetFrom(context) == S_OK) {\n\t\t\tif (event.eEventId == SPEI_RECOGNITION) {\n\t\t\t\tISpRecoResult *result = event.RecoResult();\n\n\t\t\t\tCoTaskMemPtr<wchar_t> text;\n\t\t\t\thr = result->GetText((ULONG)-1, (ULONG)-1, true, &text, nullptr);\n\t\t\t\tif (FAILED(hr))\n\t\t\t\t\tcontinue;\n\n\t\t\t\tchar text_utf8[512];\n\t\t\t\tos_wcs_to_utf8(text, 0, text_utf8, 512);\n\n\t\t\t\tcallback(text_utf8);\n\n\t\t\t\tblog(LOG_DEBUG, \"\\\"%s\\\"\", text_utf8);\n\n\t\t\t} else if (event.eEventId == SPEI_END_SR_STREAM) {\n\t\t\t\texit = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (exit)\n\t\t\tbreak;\n\t}\n\n\taudio->Stop();\n\n} catch (HRError err) {\n\tblog(LOG_WARNING, \"%s failed: %s (%lX)\", __FUNCTION__, err.str, err.hr);\n}\n\nvoid mssapi_captions::pcm_data(const void *data, size_t frames)\n{\n\tif (started)\n\t\taudio->PushAudio(data, frames);\n}\n\ncaptions_handler_info mssapi_info = {[]() -> std::string { return \"Microsoft Speech-to-Text\"; },\n\t\t\t\t     [](captions_cb cb, const std::string &lang) -> captions_handler * {\n\t\t\t\t\t     return new mssapi_captions(cb, lang);\n\t\t\t\t     }};\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/captions-mssapi.hpp",
    "content": "#pragma once\n\n#include \"captions-handler.hpp\"\n#include \"captions-mssapi-stream.hpp\"\n#include <util/windows/HRError.hpp>\n#include <util/windows/ComPtr.hpp>\n#include <util/windows/WinHandle.hpp>\n#include <util/windows/CoTaskMemPtr.hpp>\n#include <util/threading.h>\n#include <util/platform.h>\n\n#ifdef _MSC_VER\n#pragma warning(push)\n#pragma warning(disable : 4996)\n#endif\n\n#include <sphelper.h>\n\n#ifdef _MSC_VER\n#pragma warning(pop)\n#endif\n\n#include <obs.hpp>\n\n#include <thread>\n\nclass mssapi_captions : public captions_handler {\n\tfriend class CaptionStream;\n\n\tComPtr<CaptionStream> audio;\n\tComPtr<ISpObjectToken> token;\n\tComPtr<ISpRecoGrammar> grammar;\n\tComPtr<ISpRecognizer> recognizer;\n\tComPtr<ISpRecoContext> context;\n\n\tHANDLE notify;\n\tWinHandle stop;\n\tstd::thread t;\n\tbool started = false;\n\n\tvoid main_thread();\n\npublic:\n\tmssapi_captions(captions_cb callback, const std::string &lang);\n\tvirtual ~mssapi_captions();\n\tvirtual void pcm_data(const void *data, size_t frames) override;\n};\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/captions.cpp",
    "content": "#include <QMessageBox>\n#include <QAction>\n\n#include <windows.h>\n#include <obs-frontend-api.h>\n#include \"captions.hpp\"\n#include \"captions-handler.hpp\"\n#include \"tool-helpers.hpp\"\n#include <util/dstr.hpp>\n#include <util/platform.h>\n#include <util/windows/WinHandle.hpp>\n#include <util/windows/ComPtr.hpp>\n#include <obs-module.h>\n\n#ifdef _MSC_VER\n#pragma warning(push)\n#pragma warning(disable : 4996)\n#endif\n\n#include <sphelper.h>\n\n#ifdef _MSC_VER\n#pragma warning(pop)\n#endif\n\n#include <unordered_map>\n#include <vector>\n#include <string>\n#include <thread>\n#include <mutex>\n\n#include \"captions-mssapi.hpp\"\n\n#define do_log(type, format, ...) blog(type, \"[Captions] \" format, ##__VA_ARGS__)\n\n#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)\n#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)\n\nusing namespace std;\n\n#define DEFAULT_HANDLER \"mssapi\"\n\nstruct obs_captions {\n\tstring handler_id = DEFAULT_HANDLER;\n\tstring source_name;\n\tOBSWeakSource source;\n\tunique_ptr<captions_handler> handler;\n\tLANGID lang_id = GetUserDefaultUILanguage();\n\n\tstd::unordered_map<std::string, captions_handler_info &> handler_types;\n\n\tinline void register_handler(const char *id, captions_handler_info &info) { handler_types.emplace(id, info); }\n\n\tvoid start();\n\tvoid stop();\n\n\tobs_captions();\n\tinline ~obs_captions() { stop(); }\n};\n\nstatic obs_captions *captions = nullptr;\n\n/* ------------------------------------------------------------------------- */\n\nstruct locale_info {\n\tDStr name;\n\tLANGID id;\n\n\tinline locale_info() {}\n\tinline locale_info(const locale_info &) = delete;\n\tinline locale_info(locale_info &&li) : name(std::move(li.name)), id(li.id) {}\n};\n\nstatic void get_valid_locale_names(vector<locale_info> &names);\nstatic bool valid_lang(LANGID id);\n\n/* ------------------------------------------------------------------------- */\n\nCaptionsDialog::CaptionsDialog(QWidget *parent) : QDialog(parent), ui(new Ui_CaptionsDialog)\n{\n\tui->setupUi(this);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tauto cb = [this](obs_source_t *source) {\n\t\tuint32_t caps = obs_source_get_output_flags(source);\n\t\tQString name = obs_source_get_name(source);\n\n\t\tif (caps & OBS_SOURCE_AUDIO)\n\t\t\tui->source->addItem(name);\n\n\t\tOBSWeakSource weak = OBSGetWeakRef(source);\n\t\tif (weak == captions->source)\n\t\t\tui->source->setCurrentText(name);\n\t\treturn true;\n\t};\n\n\tusing cb_t = decltype(cb);\n\n\tui->source->blockSignals(true);\n\tui->source->addItem(QStringLiteral(\"\"));\n\tui->source->setCurrentIndex(0);\n\tobs_enum_sources([](void *data, obs_source_t *source) { return (*static_cast<cb_t *>(data))(source); }, &cb);\n\tui->source->blockSignals(false);\n\n\tfor (auto &ht : captions->handler_types) {\n\t\tQString name = ht.second.name().c_str();\n\t\tQString id = ht.first.c_str();\n\t\tui->provider->addItem(name, id);\n\t}\n\n\tQString qhandler_id = captions->handler_id.c_str();\n\tint idx = ui->provider->findData(qhandler_id);\n\tif (idx != -1)\n\t\tui->provider->setCurrentIndex(idx);\n\n\tui->enable->blockSignals(true);\n\tui->enable->setChecked(!!captions->handler);\n\tui->enable->blockSignals(false);\n\n\tvector<locale_info> locales;\n\tget_valid_locale_names(locales);\n\n\tbool set_language = false;\n\n\tui->language->blockSignals(true);\n\tfor (int idx = 0; idx < (int)locales.size(); idx++) {\n\t\tlocale_info &locale = locales[idx];\n\n\t\tui->language->addItem(locale.name->array, (int)locale.id);\n\n\t\tif (locale.id == captions->lang_id) {\n\t\t\tui->language->setCurrentIndex(idx);\n\t\t\tset_language = true;\n\t\t}\n\t}\n\n\tif (!set_language && locales.size())\n\t\tui->language->setCurrentIndex(0);\n\n\tui->language->blockSignals(false);\n\n\tif (!locales.size()) {\n\t\tui->source->setEnabled(false);\n\t\tui->enable->setEnabled(false);\n\t\tui->language->setEnabled(false);\n\n\t} else if (!set_language) {\n\t\tbool started = !!captions->handler;\n\t\tif (started)\n\t\t\tcaptions->stop();\n\n\t\tcaptions->lang_id = locales[0].id;\n\n\t\tif (started)\n\t\t\tcaptions->start();\n\t}\n}\n\nvoid CaptionsDialog::on_source_currentIndexChanged(int)\n{\n\tbool started = !!captions->handler;\n\tif (started)\n\t\tcaptions->stop();\n\n\tcaptions->source_name = ui->source->currentText().toUtf8().constData();\n\tcaptions->source = GetWeakSourceByName(captions->source_name.c_str());\n\n\tif (started)\n\t\tcaptions->start();\n}\n\nvoid CaptionsDialog::on_enable_clicked(bool checked)\n{\n\tif (checked) {\n\t\tcaptions->start();\n\t\tif (!captions->handler) {\n\t\t\tui->enable->blockSignals(true);\n\t\t\tui->enable->setChecked(false);\n\t\t\tui->enable->blockSignals(false);\n\t\t}\n\t} else {\n\t\tcaptions->stop();\n\t}\n}\n\nvoid CaptionsDialog::on_language_currentIndexChanged(int)\n{\n\tbool started = !!captions->handler;\n\tif (started)\n\t\tcaptions->stop();\n\n\tcaptions->lang_id = (LANGID)ui->language->currentData().toInt();\n\n\tif (started)\n\t\tcaptions->start();\n}\n\nvoid CaptionsDialog::on_provider_currentIndexChanged(int idx)\n{\n\tbool started = !!captions->handler;\n\tif (started)\n\t\tcaptions->stop();\n\n\tcaptions->handler_id = ui->provider->itemData(idx).toString().toUtf8().constData();\n\n\tif (started)\n\t\tcaptions->start();\n}\n\n/* ------------------------------------------------------------------------- */\n\nstatic void caption_text(const std::string &text)\n{\n\tOBSOutputAutoRelease output = obs_frontend_get_streaming_output();\n\tif (output) {\n\t\tobs_output_output_caption_text1(output, text.c_str());\n\t}\n}\n\nstatic void audio_capture(void *, obs_source_t *, const struct audio_data *audio, bool)\n{\n\tcaptions->handler->push_audio(audio);\n}\n\nvoid obs_captions::start()\n{\n\tif (!captions->handler && valid_lang(lang_id)) {\n\t\twchar_t wname[256];\n\n\t\tauto pair = handler_types.find(handler_id);\n\t\tif (pair == handler_types.end()) {\n\t\t\twarn(\"Failed to find handler '%s'\", handler_id.c_str());\n\t\t\treturn;\n\t\t}\n\n\t\tif (!LCIDToLocaleName(lang_id, wname, 256, 0)) {\n\t\t\twarn(\"Failed to get locale name: %d\", (int)GetLastError());\n\t\t\treturn;\n\t\t}\n\n\t\tsize_t len = (size_t)wcslen(wname);\n\n\t\tstring lang_name;\n\t\tlang_name.resize(len);\n\n\t\tfor (size_t i = 0; i < len; i++)\n\t\t\tlang_name[i] = (char)wname[i];\n\n\t\tOBSSource s = OBSGetStrongRef(source);\n\t\tif (!s) {\n\t\t\twarn(\"Source invalid\");\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tcaptions_handler *h = pair->second.create(caption_text, lang_name);\n\t\t\thandler.reset(h);\n\n\t\t\tOBSSource s = OBSGetStrongRef(source);\n\t\t\tobs_source_add_audio_capture_callback(s, audio_capture, nullptr);\n\n\t\t} catch (std::string text) {\n\t\t\tQWidget *window = (QWidget *)obs_frontend_get_main_window();\n\n\t\t\twarn(\"Failed to create handler: %s\", text.c_str());\n\n\t\t\tQMessageBox::warning(window, obs_module_text(\"Captions.Error.GenericFail\"), text.c_str());\n\t\t}\n\t}\n}\n\nvoid obs_captions::stop()\n{\n\tOBSSource s = OBSGetStrongRef(source);\n\tif (s)\n\t\tobs_source_remove_audio_capture_callback(s, audio_capture, nullptr);\n\thandler.reset();\n}\n\nstatic bool get_locale_name(LANGID id, char *out)\n{\n\twchar_t name[256];\n\n\tint size = GetLocaleInfoW(id, LOCALE_SENGLISHLANGUAGENAME, name, 256);\n\tif (size <= 0)\n\t\treturn false;\n\n\tos_wcs_to_utf8(name, 0, out, 256);\n\treturn true;\n}\n\nstatic bool valid_lang(LANGID id)\n{\n\tComPtr<ISpObjectToken> token;\n\twchar_t lang_str[32];\n\tHRESULT hr;\n\n\t_snwprintf(lang_str, 31, L\"language=%x\", (int)id);\n\n\thr = SpFindBestToken(SPCAT_RECOGNIZERS, lang_str, nullptr, &token);\n\treturn SUCCEEDED(hr);\n}\n\nstatic void get_valid_locale_names(vector<locale_info> &locales)\n{\n\tlocale_info cur;\n\tchar locale_name[256];\n\n\tstatic const LANGID default_locales[] = {0x0409, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408,\n\t\t\t\t\t\t 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, 0x0410, 0x0411, 0x0412,\n\t\t\t\t\t\t 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0};\n\n\t/* ---------------------------------- */\n\n\tLANGID def_id = GetUserDefaultUILanguage();\n\tLANGID id = def_id;\n\tif (valid_lang(id) && get_locale_name(id, locale_name)) {\n\t\tdstr_copy(cur.name, obs_module_text(\"Captions.CurrentSystemLanguage\"));\n\t\tdstr_replace(cur.name, \"%1\", locale_name);\n\t\tcur.id = id;\n\n\t\tlocales.push_back(std::move(cur));\n\t}\n\n\t/* ---------------------------------- */\n\n\tconst LANGID *locale = default_locales;\n\n\twhile (*locale) {\n\t\tid = *locale;\n\n\t\tif (id != def_id && valid_lang(id) && get_locale_name(id, locale_name)) {\n\n\t\t\tdstr_copy(cur.name, locale_name);\n\t\t\tcur.id = id;\n\n\t\t\tlocales.push_back(std::move(cur));\n\t\t}\n\n\t\tlocale++;\n\t}\n}\n\n/* ------------------------------------------------------------------------- */\n\nextern captions_handler_info mssapi_info;\n\nobs_captions::obs_captions()\n{\n\tregister_handler(\"mssapi\", mssapi_info);\n}\n\n/* ------------------------------------------------------------------------- */\n\nextern \"C\" void FreeCaptions()\n{\n\tdelete captions;\n\tcaptions = nullptr;\n}\n\nstatic void obs_event(enum obs_frontend_event event, void *)\n{\n\tif (event == OBS_FRONTEND_EVENT_EXIT)\n\t\tFreeCaptions();\n}\n\nstatic void save_caption_data(obs_data_t *save_data, bool saving, void *)\n{\n\tif (saving) {\n\t\tOBSDataAutoRelease obj = obs_data_create();\n\n\t\tobs_data_set_string(obj, \"source\", captions->source_name.c_str());\n\t\tobs_data_set_bool(obj, \"enabled\", !!captions->handler);\n\t\tobs_data_set_int(obj, \"lang_id\", captions->lang_id);\n\t\tobs_data_set_string(obj, \"provider\", captions->handler_id.c_str());\n\n\t\tobs_data_set_obj(save_data, \"captions\", obj);\n\t} else {\n\t\tcaptions->stop();\n\n\t\tOBSDataAutoRelease obj = obs_data_get_obj(save_data, \"captions\");\n\t\tif (!obj)\n\t\t\tobj = obs_data_create();\n\n\t\tobs_data_set_default_int(obj, \"lang_id\", GetUserDefaultUILanguage());\n\t\tobs_data_set_default_string(obj, \"provider\", DEFAULT_HANDLER);\n\n\t\tbool enabled = obs_data_get_bool(obj, \"enabled\");\n\t\tcaptions->source_name = obs_data_get_string(obj, \"source\");\n\t\tcaptions->lang_id = (int)obs_data_get_int(obj, \"lang_id\");\n\t\tcaptions->handler_id = obs_data_get_string(obj, \"provider\");\n\t\tcaptions->source = GetWeakSourceByName(captions->source_name.c_str());\n\n\t\tif (enabled)\n\t\t\tcaptions->start();\n\t}\n}\n\nextern \"C\" void InitCaptions()\n{\n\tQAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text(\"Captions\"));\n\n\tcaptions = new obs_captions;\n\n\tauto cb = []() {\n\t\tobs_frontend_push_ui_translation(obs_module_get_string);\n\n\t\tQWidget *window = (QWidget *)obs_frontend_get_main_window();\n\n\t\tCaptionsDialog dialog(window);\n\t\tdialog.exec();\n\n\t\tobs_frontend_pop_ui_translation();\n\t};\n\n\tobs_frontend_add_save_callback(save_caption_data, nullptr);\n\tobs_frontend_add_event_callback(obs_event, nullptr);\n\n\taction->connect(action, &QAction::triggered, cb);\n}\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/captions.hpp",
    "content": "#pragma once\n\n#include <QDialog>\n#include <memory>\n\n#include \"ui_captions.h\"\n\nclass CaptionsDialog : public QDialog {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui_CaptionsDialog> ui;\n\npublic:\n\tCaptionsDialog(QWidget *parent);\n\npublic slots:\n\tvoid on_source_currentIndexChanged(int idx);\n\tvoid on_enable_clicked(bool checked);\n\tvoid on_language_currentIndexChanged(int idx);\n\tvoid on_provider_currentIndexChanged(int idx);\n};\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/cmake/windows/obs-module.rc.in",
    "content": "1 VERSIONINFO\nFILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"040904B0\"\n    BEGIN\n      VALUE \"CompanyName\", \"${OBS_COMPANY_NAME}\"\n      VALUE \"FileDescription\", \"OBS Frontend Tools\"\n      VALUE \"FileVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"ProductName\", \"${OBS_PRODUCT_NAME}\"\n      VALUE \"ProductVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"Comments\", \"${OBS_COMMENTS}\"\n      VALUE \"LegalCopyright\", \"${OBS_LEGAL_COPYRIGHT}\"\n      VALUE \"InternalName\", \"frontend-tools\"\n      VALUE \"OriginalFilename\", \"frontend-tools\"\n    END\n  END\n\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0409, 0x04B0\n  END\nEND\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/af-ZA.ini",
    "content": "SceneSwitcher=\"Outomatiese toneelwisselaar\"\nSceneSwitcher.ActiveOrNotActive=\"Toneelwisselaar is:\"\nInvalidRegex.Title=\"Ongeldige reguliere uitdrukking\"\nInvalidRegex.Text=\"Die ingevoerde reguliere uitdrukking is ongeldig.\"\nActive=\"Aktief\"\nInactive=\"Onaktief\"\nStart=\"Begin\"\nCaptions=\"Ondertiteling (eksperimenteel)\"\nCaptions.AudioSource=\"Oudiobron\"\nCaptions.CurrentSystemLanguage=\"Huidige stelseltaal (%1)\"\nCaptions.Provider=\"Voorsiener\"\nCaptions.Error.GenericFail=\"Kon nie ondertiteling begin nie\"\nOutputTimer.Stream=\"Stop stroom na:\"\nOutputTimer.Record=\"Stop opname na:\"\nOutputTimer.Stream.StoppingIn=\"Stroom stop oor:\"\nOutputTimer.Record.StoppingIn=\"Opname stop oor:\"\nScripts=\"Skripte\"\nLoadedScripts=\"Gelaaide skripte\"\nAddScripts=\"Voeg skripte toe\"\nRemoveScripts=\"Verwyder skripte\"\nReloadScripts=\"Herlaai skripte\"\nEditScript=\"Wysig skrip\"\nReload=\"Gerlaai\"\nOpenFileLocation=\"Open lêerligging\"\nPythonSettings=\"Python-instellings\"\nPythonSettings.PythonInstallPath32bit=\"Python Installasiepad (32 bis)\"\nPythonSettings.PythonInstallPath64bit=\"Python Installasiepad (64 bis)\"\nPythonSettings.BrowsePythonPath=\"Blaai na Python pad\"\nPythonSettings.PythonVersion=\"Gelaaide Python-weergawe: %1\"\nPythonSettings.PythonNotLoaded=\"Python nie tans gelaai nie\"\nPythonSettings.AlreadyLoaded.Title=\"Python is reeds gelaai\"\nPythonSettings.AlreadyLoaded.Message=\"’n Kopie van Python %1 is reeds gelaai. Om die nuutgekose Python-weergawe te laai moet u OBS herbegin.\"\nScriptLogWindow=\"Skriplogboek\"\nDescription=\"Beskrywing\"\nScriptDescriptionLink.Text=\"Open hierdie skakel in u verstekwebblaaier?\"\nFileFilter.ScriptFiles=\"Skriplêers\"\nFileFilter.AllFiles=\"Alle lêers\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/an-ES.ini",
    "content": "SceneSwitcher=\"Cambiador automatico de scenas\"\nSceneSwitcher.OnNoMatch=\"Cuan no coincide garra finestra:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"No cambiar\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Cambiar ta:\"\nSceneSwitcher.CheckInterval=\"Comprebar lo titol d'a finestra activa cada:\"\nSceneSwitcher.ActiveOrNotActive=\"Lo cambiador de scena ye:\"\nInvalidRegex.Title=\"Expresión regular no valida\"\nInvalidRegex.Text=\"La expresión regular que has escrito no ye valida.\"\nActive=\"Activo\"\nInactive=\"Inactivo\"\nStart=\"Iniciar\"\nStop=\"Aturar\"\nCaptions=\"Subtitols (experimental)\"\nCaptions.AudioSource=\"Fuent d'audio\"\nCaptions.CurrentSystemLanguage=\"Idioma d'o sistema actual (%1)\"\nCaptions.Provider=\"Proveyedor\"\nCaptions.Error.GenericFail=\"No s'han puesto iniciar los subtitols\"\nOutputTimer=\"Temporizador de salida\"\nOutputTimer.Stream=\"Aturar la transmisión dimpués de:\"\nOutputTimer.Record=\"Aturar la gravación dimpués de:\"\nOutputTimer.Stream.StoppingIn=\"La transmisión s'aturará d'aquí a:\"\nOutputTimer.Record.StoppingIn=\"La gravación s'aturará d'aquí a:\"\nOutputTimer.Stream.EnableEverytime=\"Activar lo temporizador de transmisión cada vegada\"\nOutputTimer.Record.EnableEverytime=\"Activar lo temporizador de gravación cada vegada\"\nOutputTimer.Record.PauseTimer=\"Pausar lo temporizador en pausar la gravación\"\nLoadedScripts=\"Scripts cargaus\"\nAddScripts=\"Anyadir scripts\"\nRemoveScripts=\"Elminar scripts\"\nReloadScripts=\"Recargar scripts\"\nEditScript=\"Editar lo script\"\nReload=\"Recargar\"\nOpenFileLocation=\"Ubrir la localización d'o fichero\"\nPythonSettings=\"Achustes de Python\"\nPythonSettings.PythonInstallPath32bit=\"Camín d'instalación de Python (32 bits)\"\nPythonSettings.PythonInstallPath64bit=\"Camín d'instalación de Python (64 bits)\"\nPythonSettings.BrowsePythonPath=\"Mirar lo camín de Python\"\nScriptLogWindow=\"Rechistro de scripts\"\nDescription=\"Descripción\"\nScriptDescriptionLink.Text=\"Ubrir este vinclo en o tuyo navegador per defecto?\"\nScriptDescriptionLink.OpenURL=\"Ubrir la URL\"\nFileFilter.ScriptFiles=\"Fichers script\"\nFileFilter.AllFiles=\"Toz los Fichers\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ar-SA.ini",
    "content": "SceneSwitcher=\"مبدل المَشاهِد التلقائي\"\nSceneSwitcher.OnNoMatch=\"عندما لا تتطابق أي نافذة:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"لا تقم بالتبديل\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"التبديل إلى:\"\nSceneSwitcher.CheckInterval=\"التأكد من عنوان النافذة النشطة كل:\"\nSceneSwitcher.ActiveOrNotActive=\"حالة مبدل المشاهد:\"\nInvalidRegex.Title=\"تعبير نمطي غير صحيح\"\nInvalidRegex.Text=\"التعبير النمطي الذي أدخلته غير صحيح.\"\nActive=\"مفعّل\"\nInactive=\"غير مُفعّل\"\nStart=\"ابدأ\"\nStop=\"ايقاف\"\nCaptions=\"ترجمة فورية (تجريبي)\"\nCaptions.AudioSource=\"مصدر الصوت\"\nCaptions.CurrentSystemLanguage=\"لغة النظام الحالية (%1)\"\nCaptions.Provider=\"موفر الخدمة\"\nCaptions.Error.GenericFail=\"فشل في تشغيل الترجمة الفورية\"\nOutputTimer=\"مؤقِّت الإخراج\"\nOutputTimer.Stream=\"إيقاف البث بعد:\"\nOutputTimer.Record=\"إيقاف التسجيل بعد:\"\nOutputTimer.Stream.StoppingIn=\"سيتم إيقاف البث بعد:\"\nOutputTimer.Record.StoppingIn=\"سيتم إيقاف التسجيل بعد:\"\nOutputTimer.Stream.EnableEverytime=\"تفعيل مؤقِّت البث في كل مرة\"\nOutputTimer.Record.EnableEverytime=\"تفعيل مؤقِّت التسجيل في كل مرة\"\nOutputTimer.Record.PauseTimer=\"إيقاف المُؤَقِت عندما يكون التسجيل متوقف\"\nScripts=\"سكريبتات\"\nLoadedScripts=\"السكريبتات المحملة\"\nAddScripts=\"إضافة سكريبت\"\nRemoveScripts=\"إزالة السكريبت\"\nReloadScripts=\"إعادة تحميل السكربتات\"\nEditScript=\"تحرير سكريبت\"\nReload=\"إعادة تحميل\"\nOpenFileLocation=\"فتح موقع الملف\"\nPythonSettings=\"إعدادات بايثون\"\nPythonSettings.PythonInstallPath32bit=\"مسار تثبيت بايثون (32 بت)\"\nPythonSettings.PythonInstallPath64bit=\"مسار تثبيت بايثون (64 بت)\"\nPythonSettings.BrowsePythonPath=\"استعراض مسار البايثون\"\nPythonSettings.PythonVersion=\"إصدار البايثون المُحمل: %1\"\nPythonSettings.PythonNotLoaded=\"لم يتم تحميل بايثون حاليًا\"\nPythonSettings.AlreadyLoaded.Title=\"تم تحميل بايثون بالفعل\"\nPythonSettings.AlreadyLoaded.Message=\"نسخة بايثون %1 قد تم تحميلها. لتحميل إصدار بايثون الذي تم اختياره للتو، الرجاء إعادة تشغيل OBS.\"\nScriptLogWindow=\"سجل السكريبت\"\nDescription=\"الوصف\"\nScriptDescriptionLink.Text=\"فتح هذا الرابط في متصفحك الافتراضي ؟\"\nScriptDescriptionLink.Text.Url=\"الرابط: %1\"\nScriptDescriptionLink.OpenURL=\"فتح الرابط\"\nFileFilter.ScriptFiles=\"ملفات سكريبت\"\nFileFilter.AllFiles=\"جميع الملفات\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/az-AZ.ini",
    "content": "SceneSwitcher=\"Avtomatik Səhnə Dəyişdirici\"\nSceneSwitcher.OnNoMatch=\"Heç bir pəncərə uyğun gəlmədikdə:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Dəyişmə\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Buna dəyiş:\"\nSceneSwitcher.CheckInterval=\"Aktiv pəncərə başlığını yoxla (hər):\"\nSceneSwitcher.ActiveOrNotActive=\"Səhnə Dəyişdirici:\"\nInvalidRegex.Title=\"Yanlış Müntəzəm İfadə\"\nInvalidRegex.Text=\"Daxil etdiyiniz müntəzəm ifadə düzgün deyil.\"\nActive=\"Aktiv\"\nInactive=\"Qeyri aktiv\"\nStart=\"Başla\"\nStop=\"Dayandır\"\nCaptions=\"Altyazılar (Eksperimental)\"\nCaptions.AudioSource=\"Səs mənbəsi\"\nCaptions.CurrentSystemLanguage=\"Cari Sistem Dili (%1)\"\nCaptions.Provider=\"Təchizatçı\"\nCaptions.Error.GenericFail=\"Altyazıları başlatmaq mümkün olmadı.\"\nOutputTimer=\"Çıxış Vaxtölçəni\"\nOutputTimer.Stream=\"Yayımı bu müddətdən sonra dayandır:\"\nOutputTimer.Record=\"Yazını bu müddətdən sonra dayandır:\"\nOutputTimer.Stream.StoppingIn=\"Yayımın dayanmasına qalıb:\"\nOutputTimer.Record.StoppingIn=\"Yazının dayanmasına qalıb:\"\nOutputTimer.Stream.EnableEverytime=\"Yayım vaxtölçənini hər dəfə aktiv et\"\nOutputTimer.Record.EnableEverytime=\"Yazı vaxtölçənini hər dəfə aktiv et\"\nOutputTimer.Record.PauseTimer=\"Yazı dayandırıldıqda vaxtölçəni dayandır\"\nScripts=\"Skriptlər\"\nLoadedScripts=\"Yüklənmiş Skriptlər\"\nAddScripts=\"Skript Əlavə et\"\nRemoveScripts=\"Skripti Sil\"\nReloadScripts=\"Skripti Yenidən yüklə\"\nEditScript=\"Skripti Redaktə et\"\nReload=\"Yenidən Yüklə\"\nOpenFileLocation=\"Fayl yerləşməsini aç\"\nPythonSettings=\"Python Parametrləri\"\nPythonSettings.PythonInstallPath32bit=\"Python Quraşdırma Yolu (32 bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python Quraşdırma Yolu (64 bit)\"\nPythonSettings.BrowsePythonPath=\"Python Yoluna Göz at\"\nPythonSettings.PythonVersion=\"Yüklənmiş Python Versiyası:%1\"\nPythonSettings.PythonNotLoaded=\"Python hələ yüklənməyib\"\nPythonSettings.AlreadyLoaded.Title=\"Python artıq yüklənib\"\nPythonSettings.AlreadyLoaded.Message=\"Python %1 artıq yüklənib. Yeni seçilmiş Python versiyasını yükləmək üçün, zəhmət olmasa, OBS-i yenidən başladın.\"\nScriptLogWindow=\"Sktript Jurnalı\"\nDescription=\"Açıqlama\"\nScriptDescriptionLink.Text=\"Bu link standart veb brauzerinizdə açılsın?\"\nScriptDescriptionLink.OpenURL=\"URL-ni aç\"\nFileFilter.ScriptFiles=\"Skript Faylları\"\nFileFilter.AllFiles=\"Bütün Fayllar\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ba-RU.ini",
    "content": "Active=\"Әүҙемле\"\nInactive=\"Әүҙемһеҙ\"\nStart=\"Башларға\"\nStop=\"Туҡтатырға\"\nCaptions.AudioSource=\"Тауыш сығанағы\"\nCaptions.CurrentSystemLanguage=\"Системаның ағымдағы теле (%1)\"\nScripts=\"Скрипттар\"\nFileFilter.AllFiles=\"Барлыҡ файлдар\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/be-BY.ini",
    "content": "SceneSwitcher=\"Аўтаматычны пераключальнік сцэн\"\nSceneSwitcher.OnNoMatch=\"Калі няма адпаведных вокнаў:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Не пераключацца\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Пераключыцца на\"\nSceneSwitcher.CheckInterval=\"Правяраць загаловак актыўнага акна раз на\"\nSceneSwitcher.ActiveOrNotActive=\"Стан пераключальніка:\"\nInvalidRegex.Title=\"Няправільны рэгулярны выраз\"\nInvalidRegex.Text=\"Уведзены рэгулярны выраз ёсць памылковым.\"\nActive=\"Актыўны\"\nInactive=\"Неактыўны\"\nStart=\"Пачаць\"\nStop=\"Спыніць\"\nCaptions=\"Субцітры (эксперыментальны)\"\nCaptions.AudioSource=\"Крыніца гуку\"\nCaptions.CurrentSystemLanguage=\"Бягучая мова сістэмы (%1)\"\nCaptions.Provider=\"Пастаўшчык\"\nCaptions.Error.GenericFail=\"Не атрымалася запусціць субцітры\"\nOutputTimer=\"Таймер вываду\"\nOutputTimer.Stream=\"Спыніць стрым праз\"\nOutputTimer.Record=\"Спыніць запіс праз\"\nOutputTimer.Stream.StoppingIn=\"Стрым спыніцца праз\"\nOutputTimer.Record.StoppingIn=\"Запіс спыніцца праз\"\nOutputTimer.Stream.EnableEverytime=\"Заўжды ўключаць таймер стрыма\"\nOutputTimer.Record.EnableEverytime=\"Заўжды ўключаць таймер запісу\"\nOutputTimer.Record.PauseTimer=\"Прыпыняць таймер пры прыпыненні запісу\"\nScripts=\"Скрыпты\"\nLoadedScripts=\"Загружаныя скрыпты\"\nAddScripts=\"Дадаць скрыпты\"\nRemoveScripts=\"Выдаліць скрыпты\"\nReloadScripts=\"Перазагрузіць скрыпты\"\nEditScript=\"Рэдагаваць\"\nReload=\"Перазагрузіць\"\nOpenFileLocation=\"Адкрыць папку з файлам\"\nPythonSettings=\"Налады Python\"\nPythonSettings.PythonInstallPath32bit=\"Шлях да Python (32 біт)\"\nPythonSettings.PythonInstallPath64bit=\"Шлях да Python (64 біт)\"\nPythonSettings.BrowsePythonPath=\"Знайсці шлях да Python\"\nPythonSettings.PythonVersion=\"Загружаная версія Python: %1\"\nPythonSettings.PythonNotLoaded=\"Python не загружаны\"\nPythonSettings.AlreadyLoaded.Title=\"Python ужо загружаны\"\nPythonSettings.AlreadyLoaded.Message=\"Python %1 ужо загружаны. Каб загрузіць версію, выбраную цяпер, перазапусціце OBS.\"\nScriptLogWindow=\"Журнал\"\nDescription=\"Апісанне\"\nScriptDescriptionLink.Text=\"Адкрыць спасылку ў прадвызначаным вэб-браўзеры?\"\nScriptDescriptionLink.OpenURL=\"Адкрыць URL\"\nFileFilter.ScriptFiles=\"Файлы скрыптаў\"\nFileFilter.AllFiles=\"Усе файлы\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/bg-BG.ini",
    "content": "SceneSwitcher=\"Автоматичен превключвател на сцени\"\nSceneSwitcher.OnNoMatch=\"Когато прозорците не съвпадат:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Без превключване\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Превключи на:\"\nSceneSwitcher.CheckInterval=\"Проверка название на активния прозорец всеки:\"\nSceneSwitcher.ActiveOrNotActive=\"Превключвателят на сцени е:\"\nInvalidRegex.Title=\"Невалиден регулярен израз\"\nInvalidRegex.Text=\"Въведохте невалиден регулярен израз.\"\nActive=\"Активен\"\nInactive=\"Изключен\"\nStart=\"Стартирай\"\nStop=\"Спри\"\nCaptions=\"Надписи (експериментално)\"\nCaptions.AudioSource=\"Звуков източник\"\nCaptions.CurrentSystemLanguage=\"Текущ език на системата (%1)\"\nCaptions.Provider=\"Доставчик\"\nCaptions.Error.GenericFail=\"Неуспешно зареждане на надписи\"\nOutputTimer=\"Изходен таймер\"\nOutputTimer.Stream=\"Прекъсни излъчването след:\"\nOutputTimer.Record=\"Прекъсни записването след:\"\nOutputTimer.Stream.StoppingIn=\"Излъчването ще спре след:\"\nOutputTimer.Record.StoppingIn=\"Записването ще спре след:\"\nOutputTimer.Stream.EnableEverytime=\"Показвай брояча на излъчването всеки път\"\nOutputTimer.Record.EnableEverytime=\"Включване на таймера при запис всеки път\"\nOutputTimer.Record.PauseTimer=\"Пауза на таймера при пауза на записа\"\nScripts=\"Скриптове\"\nLoadedScripts=\"Заредени скриптове\"\nAddScripts=\"Добавяне на скриптове\"\nRemoveScripts=\"Премахване на скриптове\"\nReloadScripts=\"Презареждане на скриптове\"\nEditScript=\"Редактиране на кода\"\nReload=\"Презареди\"\nOpenFileLocation=\"Отворете местоположението на файла\"\nPythonSettings=\"Настройки на Python\"\nPythonSettings.PythonInstallPath32bit=\"Път за инсталация на Python (32 бита)\"\nPythonSettings.PythonInstallPath64bit=\"Път за инсталация на Python (64 бита)\"\nPythonSettings.BrowsePythonPath=\"Разглеждане на пътя до Python\"\nPythonSettings.PythonVersion=\"Заредена Python Версия: %1\"\nPythonSettings.PythonNotLoaded=\"Python не можа да бъде зареден\"\nPythonSettings.AlreadyLoaded.Title=\"Python вече е зареден\"\nPythonSettings.AlreadyLoaded.Message=\"Копие на Python %1 вече е заредено. За да заредите друга версия на Python, моля рестартирайте OBS.\"\nScriptLogWindow=\"Записи на скриптове\"\nDescription=\"Описание\"\nScriptDescriptionLink.Text=\"Да се отвори ли тази връзка в уеб браузъра ви по подразбиране?\"\nFileFilter.ScriptFiles=\"Файлове със скрипт\"\nFileFilter.AllFiles=\"Всички файлове\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/bn-BD.ini",
    "content": "SceneSwitcher=\"স্বয়ংক্রিয় দৃশ্য পরিবর্তক\"\nSceneSwitcher.OnNoMatch=\"যখন কোনও উইন্ডো মেলে নাঃ\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"স্যুইচ করবেন না\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"পরিবর্তন করুনঃ\"\nSceneSwitcher.CheckInterval=\"সক্রিয় উইন্ডো শিরোনাম চেক করুন প্রতিঃ\"\nSceneSwitcher.ActiveOrNotActive=\"দৃশ্য পরিবর্তনকারী হলঃ\"\nInvalidRegex.Title=\"অবৈধ নিয়মিত এক্সপ্রেশন\"\nInvalidRegex.Text=\"যে রেগুলার এক্সপ্রেশন প্রবেশ করেছ তা ভুল।\"\nActive=\"সক্রিয়\"\nInactive=\"নিষ্ক্রিয়\"\nStart=\"শুরু\"\nStop=\"থামুন\"\nCaptions=\"ক্যাপশন (পরীক্ষামূলক)\"\nCaptions.AudioSource=\"অডিও উৎস\"\nCaptions.CurrentSystemLanguage=\"বর্তমান সিস্টেমের ভাষা (%1)\"\nCaptions.Provider=\"প্রোভাইডার\"\nCaptions.Error.GenericFail=\"ক্যাপশন শুরু করতে ব্যর্থ\"\nOutputTimer=\"আউটপুট টাইমার\"\nOutputTimer.Stream=\"এর পরে বন্ধ।:\"\nOutputTimer.Record=\"এর পরে রেকর্ডিং বন্ধ করুন:\"\nOutputTimer.Stream.StoppingIn=\"স্ট্রিমিং থামবে:\"\nOutputTimer.Record.StoppingIn=\"রেকর্ডিং থামবেঃ\"\nOutputTimer.Stream.EnableEverytime=\"প্রতিবার স্ট্রিমিং টাইমার চালু করুন\"\nOutputTimer.Record.EnableEverytime=\"প্রতিবার রেকর্ডিং টাইমার চালু করুন\"\nOutputTimer.Record.PauseTimer=\"রেকর্ডিং থামান যখন টাইমার থামে\"\nScripts=\"স্ক্রিপ্টসমূহ\"\nLoadedScripts=\"স্ক্রিপ্টসমূহ লোড করা হয়েছে\"\nAddScripts=\"স্ক্রিপ্ট যুক্ত করুন\"\nRemoveScripts=\"স্ক্রিপ্ট সরান\"\nReloadScripts=\"স্ক্রিপ্টসমূহ পুনঃলোড করুন\"\nEditScript=\"স্ক্রিপ্ট সম্পাদনা\"\nReload=\"পুনঃলোড\"\nOpenFileLocation=\"ফাইল অবস্থান খুলুন\"\nPythonSettings=\"পাইথন সেটিংসসমূহ\"\nPythonSettings.PythonInstallPath32bit=\"পাইথন ইনস্টল পাথ (৩২ বিট)\"\nPythonSettings.PythonInstallPath64bit=\"পাইথন ইনস্টল পাথ (৬৪ বিট)\"\nPythonSettings.BrowsePythonPath=\"পাইথন পাথ ব্রাউজ করুন\"\nScriptLogWindow=\"স্ক্রিপ্ট লগ\"\nDescription=\"বর্ণনা\"\nScriptDescriptionLink.Text=\"আপনার ডিফল্ট ওয়েব ব্রাউজারে এই লিঙ্কটি খুলবেন?\"\nScriptDescriptionLink.Text.Url=\"ইউআরএল: %1\"\nScriptDescriptionLink.OpenURL=\"ইউআরএল খুলুন\"\nFileFilter.ScriptFiles=\"স্ক্রিপ্ট ফাইল\"\nFileFilter.AllFiles=\"সকল ফাইল\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ca-ES.ini",
    "content": "SceneSwitcher=\"Canviador d'escena automàtica\"\nSceneSwitcher.OnNoMatch=\"Quan no coincideixi amb cap finestra:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"No canviar\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Canvia a:\"\nSceneSwitcher.CheckInterval=\"Comprova el títol de la finestra activa cada:\"\nSceneSwitcher.ActiveOrNotActive=\"El canviador d'escena està:\"\nInvalidRegex.Title=\"Expressió regular no vàlida\"\nInvalidRegex.Text=\"L'expressió regular que heu introduït no és vàlida.\"\nActive=\"Actiu\"\nInactive=\"Inactiu\"\nStart=\"Inicia\"\nStop=\"Atura\"\nCaptions=\"Subtítols (Experimental)\"\nCaptions.AudioSource=\"Font d'àudio\"\nCaptions.CurrentSystemLanguage=\"Idioma actual del sistema (%1)\"\nCaptions.Provider=\"Proveïdor\"\nCaptions.Error.GenericFail=\"No s'ha pogut iniciar els subtítols\"\nOutputTimer=\"Temporitzador de sortida\"\nOutputTimer.Stream=\"Atura la transmissió després de:\"\nOutputTimer.Record=\"Atura la gravació després de:\"\nOutputTimer.Stream.StoppingIn=\"La transmissió s'aturarà en:\"\nOutputTimer.Record.StoppingIn=\"La gravació s'aturarà en:\"\nOutputTimer.Stream.EnableEverytime=\"Activa el temporitzador en cada transmissió\"\nOutputTimer.Record.EnableEverytime=\"Activa el temporitzador en cada enregistrament\"\nOutputTimer.Record.PauseTimer=\"Pausa el temporitzador en pausar l'enregistrament\"\nLoadedScripts=\"Scripts carregats\"\nAddScripts=\"Afegeix scripts\"\nRemoveScripts=\"Suprimeix scripts\"\nReloadScripts=\"Torna a carregar els scripts\"\nEditScript=\"Edita l'script\"\nReload=\"Recarrega\"\nOpenFileLocation=\"Obre la ubicació del fitxer\"\nPythonSettings=\"Configuració del Python\"\nPythonSettings.PythonInstallPath32bit=\"Camí d'instal·lació del Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Camí d'instal·lació del Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"Camí del Python\"\nPythonSettings.PythonVersion=\"Versió de Python carregada: %1\"\nPythonSettings.PythonNotLoaded=\"El Python no està carregat actualment\"\nPythonSettings.AlreadyLoaded.Title=\"El Python ja està carregat\"\nPythonSettings.AlreadyLoaded.Message=\"Ja s'ha carregat una còpia del Python %1. Per carregar la versió del Python seleccionada recentment, reinicieu l 'OBS.\"\nScriptLogWindow=\"Informe script\"\nDescription=\"Descripció\"\nScriptDescriptionLink.Text=\"Voleu obrir aquest enllaç al navegador per defecte?\"\nScriptDescriptionLink.OpenURL=\"Obre l'URL\"\nFileFilter.ScriptFiles=\"Fitxers script\"\nFileFilter.AllFiles=\"Tots els fitxers\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/cs-CZ.ini",
    "content": "SceneSwitcher=\"Automatický přepínač scén\"\nSceneSwitcher.OnNoMatch=\"Výchozí chování u nesouhlasící:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Nepřepínat\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Přepnout na:\"\nSceneSwitcher.CheckInterval=\"Kontrolovat titulek aktivního okna každých:\"\nSceneSwitcher.ActiveOrNotActive=\"Přepínač scén je:\"\nInvalidRegex.Title=\"Chybný regulární výraz\"\nInvalidRegex.Text=\"Zadaný regulární výraz je chybný.\"\nActive=\"Aktivní\"\nInactive=\"Neaktivní\"\nStart=\"Spustit\"\nStop=\"Zastavit\"\nCaptions=\"Titulky (experiment.)\"\nCaptions.AudioSource=\"Zdroj zvuku\"\nCaptions.CurrentSystemLanguage=\"Aktuální systémový jazyk (%1)\"\nCaptions.Provider=\"Zprostředkovatel\"\nCaptions.Error.GenericFail=\"Nezdařilo se spuštění titulků\"\nOutputTimer=\"Časovač\"\nOutputTimer.Stream=\"Přestat vysílat po:\"\nOutputTimer.Record=\"Přestat nahrávat po:\"\nOutputTimer.Stream.StoppingIn=\"Vysílání se zastaví za:\"\nOutputTimer.Record.StoppingIn=\"Nahrávání se zastaví za:\"\nOutputTimer.Stream.EnableEverytime=\"Pokaždé povolit časovač vysílání\"\nOutputTimer.Record.EnableEverytime=\"Pokaždé povolit časovač nahrávání\"\nOutputTimer.Record.PauseTimer=\"Pozastavit časovač při pozastavení nahrávání\"\nScripts=\"Skripty\"\nLoadedScripts=\"Načtené skripty\"\nAddScripts=\"Přidat skripty\"\nRemoveScripts=\"Odebrat skripty\"\nReloadScripts=\"Restartovat skripty\"\nEditScript=\"Upravit skript\"\nReload=\"Obnovit\"\nOpenFileLocation=\"Otevřít umístění souboru\"\nPythonSettings=\"Python nastavení\"\nPythonSettings.PythonInstallPath32bit=\"Cesta k instalaci Pythonu (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Cesta k instalaci Pythonu (64bit)\"\nPythonSettings.BrowsePythonPath=\"Najít cestu k Pythonu\"\nPythonSettings.PythonVersion=\"Načtená verze Python: %1\"\nPythonSettings.PythonNotLoaded=\"Python není aktuálně načten\"\nPythonSettings.AlreadyLoaded.Title=\"Python je již načten\"\nPythonSettings.AlreadyLoaded.Message=\"Kopie Python %1 je jiý načtena. Pro načtení nově vybrané verze Python, prosím, restartujte OBS.\"\nScriptLogWindow=\"Log skriptu\"\nDescription=\"Popis\"\nScriptDescriptionLink.Text=\"Chcete otevřít tento odkaz ve vašem výchozím internetovém prohlížeči?\"\nScriptDescriptionLink.Text.Url=\"Adresa: %1\"\nScriptDescriptionLink.OpenURL=\"Otevřít adresu\"\nFileFilter.ScriptFiles=\"Soubory skriptů\"\nFileFilter.AllFiles=\"Všechny soubory\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/da-DK.ini",
    "content": "SceneSwitcher=\"Automatisk sceneskifter\"\nSceneSwitcher.OnNoMatch=\"Når intet vindue matcher:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Skift ikke\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Skift til:\"\nSceneSwitcher.CheckInterval=\"Tjek aktiv vinduestitel hver:\"\nSceneSwitcher.ActiveOrNotActive=\"Sceneskifter er:\"\nInvalidRegex.Title=\"Ugyldigt regulært udtryk\"\nInvalidRegex.Text=\"Det af dig angivne regulære udtryk er ugyldigt.\"\nActive=\"Aktiv\"\nInactive=\"Inaktiv\"\nCaptions=\"Billedtekster (eksperimentel)\"\nCaptions.AudioSource=\"Lydkilde\"\nCaptions.CurrentSystemLanguage=\"Aktuelt systemsprog (%1)\"\nCaptions.Provider=\"Leverandør\"\nCaptions.Error.GenericFail=\"Kunne ikke starte billedtekster\"\nOutputTimer=\"Outputtimer\"\nOutputTimer.Stream=\"Stands streaming efter:\"\nOutputTimer.Record=\"Stands optagelse efter:\"\nOutputTimer.Stream.StoppingIn=\"Streaming standser om:\"\nOutputTimer.Record.StoppingIn=\"Optagelse standser om:\"\nOutputTimer.Stream.EnableEverytime=\"Aktivér streamingtimer hver gang\"\nOutputTimer.Record.EnableEverytime=\"Aktivér optagetimer hver gang\"\nOutputTimer.Record.PauseTimer=\"Sæt timer på pause, når optagelsen er sat på pause\"\nLoadedScripts=\"Indlæste scripts\"\nAddScripts=\"Tilføj scripts\"\nRemoveScripts=\"Fjern scripts\"\nReloadScripts=\"Genindlæs scripts\"\nEditScript=\"Redigér script\"\nReload=\"Genindlæs\"\nOpenFileLocation=\"Åbn filplacering\"\nPythonSettings=\"Python-indstillinger\"\nPythonSettings.PythonInstallPath32bit=\"Python-installationssti (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python-installationssti (64bit)\"\nPythonSettings.BrowsePythonPath=\"Find Python-sti\"\nPythonSettings.PythonVersion=\"Indlæst Python version: %1\"\nPythonSettings.PythonNotLoaded=\"Python ikke indlæst i øjeblikket\"\nPythonSettings.AlreadyLoaded.Title=\"Python Allerede Indlæst\"\nPythonSettings.AlreadyLoaded.Message=\"En kopi af Python %1 er allerede indlæst. Genstart venligst OBS for at indlæse den nyligt valgte Python-version.\"\nScriptLogWindow=\"Scriptlog\"\nDescription=\"Beskrivelse\"\nScriptDescriptionLink.Text=\"Åbn dette link i din webbrowser?\"\nScriptDescriptionLink.OpenURL=\"Åbe URL\"\nFileFilter.ScriptFiles=\"Scriptfiler\"\nFileFilter.AllFiles=\"Alle filer\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/de-DE.ini",
    "content": "SceneSwitcher=\"Automatischer Szenenwechsler\"\nSceneSwitcher.OnNoMatch=\"Wenn kein Fenster passt:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Nicht wechseln\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Wechseln zu:\"\nSceneSwitcher.CheckInterval=\"Titel des aktiven Fensters überprüfen alle:\"\nSceneSwitcher.ActiveOrNotActive=\"Szenenwechsler ist:\"\nInvalidRegex.Title=\"Ungültiger regulärer Ausdruck\"\nInvalidRegex.Text=\"Der reguläre Ausdruck, den Sie eingegeben haben, ist ungültig.\"\nActive=\"Aktiv\"\nInactive=\"Inaktiv\"\nStart=\"Starten\"\nStop=\"Beenden\"\nCaptions=\"Untertitel (experimentell)\"\nCaptions.AudioSource=\"Audioquelle\"\nCaptions.CurrentSystemLanguage=\"Aktuelle Systemsprache (%1)\"\nCaptions.Provider=\"Service\"\nCaptions.Error.GenericFail=\"Starten der Untertitel fehlgeschlagen\"\nOutputTimer=\"Ausgabetimer\"\nOutputTimer.Stream=\"Stream endet nach:\"\nOutputTimer.Record=\"Aufnahme endet nach:\"\nOutputTimer.Stream.StoppingIn=\"Stream endet in:\"\nOutputTimer.Record.StoppingIn=\"Aufnahme endet in:\"\nOutputTimer.Stream.EnableEverytime=\"Streamingtimer jedes Mal aktivieren\"\nOutputTimer.Record.EnableEverytime=\"Aufnahmetimer jedes Mal aktivieren\"\nOutputTimer.Record.PauseTimer=\"Timer beim Pausieren der Aufnahme anhalten\"\nScripts=\"Skripte\"\nLoadedScripts=\"Geladene Skripte\"\nAddScripts=\"Skripte hinzufügen\"\nRemoveScripts=\"Skripte entfernen\"\nReloadScripts=\"Skripte neu laden\"\nEditScript=\"Skript bearbeiten\"\nReload=\"Neu laden\"\nOpenFileLocation=\"Dateipfad öffnen\"\nPythonSettings=\"Python-Einstellungen\"\nPythonSettings.PythonInstallPath32bit=\"Python-Installationspfad (32 Bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python-Installationspfad (64 Bit)\"\nPythonSettings.BrowsePythonPath=\"Python-Pfad öffnen\"\nPythonSettings.PythonVersion=\"Geladene Python-Version: %1\"\nPythonSettings.PythonNotLoaded=\"Python derzeit nicht geladen\"\nPythonSettings.AlreadyLoaded.Title=\"Python bereits geladen\"\nPythonSettings.AlreadyLoaded.Message=\"Eine Kopie von Python %1 ist bereits geladen. Um die neu gewählte Python-Version zu laden, starten Sie OBS neu.\"\nScriptLogWindow=\"Skriptlog\"\nDescription=\"Beschreibung\"\nScriptDescriptionLink.Text=\"Diesen Link im Browser öffnen?\"\nScriptDescriptionLink.OpenURL=\"URL öffnen\"\nFileFilter.ScriptFiles=\"Skriptdateien\"\nFileFilter.AllFiles=\"Alle Dateien\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/el-GR.ini",
    "content": "SceneSwitcher=\"Αυτόματος Εναλλαγέας Σκηνών\"\nSceneSwitcher.OnNoMatch=\"Όταν δεν ταιριάζει με κανένα παράθυρο:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Καμία Μετάβαση\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Μετάβαση σε:\"\nSceneSwitcher.CheckInterval=\"Έλεγχος τίτλου ενεργού παραθύρου κάθε:\"\nSceneSwitcher.ActiveOrNotActive=\"Ο Εναλλαγέας Σκηνών είναι:\"\nInvalidRegex.Title=\"Μη έγκυρη τυπική έκφραση\"\nInvalidRegex.Text=\"Η τυπική έκφραση που εισαγάγατε δεν είναι έγκυρη.\"\nActive=\"Ενεργός\"\nInactive=\"Ανενεργός\"\nStart=\"Eκκίνηση\"\nStop=\"Διακοπή\"\nCaptions=\"Υπότιτλοι (Πειραματικό)\"\nCaptions.AudioSource=\"Πηγή ήχου\"\nCaptions.CurrentSystemLanguage=\"Τρέχουσα γλώσσα του συστήματος (%1)\"\nCaptions.Provider=\"Πάροχος\"\nCaptions.Error.GenericFail=\"Αποτυχία εκκίνησης των υποτίτλων\"\nOutputTimer=\"Χρονόμετρο εξόδου\"\nOutputTimer.Stream=\"Διακοπή streaming μετά από:\"\nOutputTimer.Record=\"Διακοπή εγγραφής βίντεο μετά από:\"\nOutputTimer.Stream.StoppingIn=\"Διακοπή streaming σε:\"\nOutputTimer.Record.StoppingIn=\"Διακοπή εγγραφής σε:\"\nOutputTimer.Stream.EnableEverytime=\"Ενεργοποίηση χρονόμετρου streaming κάθε φορά\"\nOutputTimer.Record.EnableEverytime=\"Ενεργοποίηση χρονόμετρου εγγραφής κάθε φορά\"\nOutputTimer.Record.PauseTimer=\"Σταματήστε τον χρονοδιακόπτη όταν η καταγραφή είναι σε παύση\"\nScripts=\"Δέσμες ενεργειών\"\nLoadedScripts=\"Φορτωμένες δέσμες ενεργειών\"\nAddScripts=\"Προσθήκη δεσμών ενεργειών\"\nRemoveScripts=\"Αφαίρεση δεσμών ενεργειών\"\nReloadScripts=\"Επαναφόρτωση δεσμών ενεργειών\"\nEditScript=\"Επεξεργασία Αρχείου\"\nReload=\"Επαναφόρτωση\"\nOpenFileLocation=\"Άνοιγμα τοποθεσίας αρχείου\"\nPythonSettings=\"Ρυθμίσεις Python\"\nPythonSettings.PythonInstallPath32bit=\"Χώρος εγκατάστασης Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Χώρος εγκατάστασης Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"Περιήγηση τού χώρου εγκατάστασης Python\"\nPythonSettings.PythonVersion=\"Φορτωμένη Έκδοση Python: %1\"\nPythonSettings.PythonNotLoaded=\"Δεν φορτώθηκε η Python\"\nPythonSettings.AlreadyLoaded.Title=\"Η Python Φορτώθηκε Ήδη\"\nPythonSettings.AlreadyLoaded.Message=\"Ένα αντίγραφο της Python %1 είναι ήδη φορτωμένο. Για να φορτώσετε τη νέα επιλεγμένη έκδοση της Python, παρακαλούμε επανεκκινήστε το OBS.\"\nDescription=\"Περιγραφή\"\nScriptDescriptionLink.Text=\"Ανοίξτε αυτόν τον σύνδεσμο με το προκαθορισμένο πρόγραμα περιήγησης;\"\nScriptDescriptionLink.Text.Url=\"Σύνδεσμος: %1\"\nScriptDescriptionLink.OpenURL=\"Άνοιγμα Συνδέσμων\"\nFileFilter.ScriptFiles=\"Κρυπτογραφημένο αρχείο δέσμης ενεργειών\"\nFileFilter.AllFiles=\"Όλα τα αρχεία\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/en-GB.ini",
    "content": "#"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/en-US.ini",
    "content": "SceneSwitcher=\"Automatic Scene Switcher\"\nSceneSwitcher.OnNoMatch=\"When no window matches:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Don't switch\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Switch to:\"\nSceneSwitcher.CheckInterval=\"Check active window title every:\"\nSceneSwitcher.ActiveOrNotActive=\"Scene Switcher is:\"\nInvalidRegex.Title=\"Invalid Regular Expression\"\nInvalidRegex.Text=\"The regular expression that you entered is invalid.\"\nActive=\"Active\"\nInactive=\"Inactive\"\nStart=\"Start\"\nStop=\"Stop\"\n\nCaptions=\"Captions (Experimental)\"\nCaptions.AudioSource=\"Audio source\"\nCaptions.CurrentSystemLanguage=\"Current System Language (%1)\"\nCaptions.Provider=\"Provider\"\nCaptions.Error.GenericFail=\"Failed to start captions\"\n\nOutputTimer=\"Output Timer\"\nOutputTimer.Stream=\"Stop streaming after:\"\nOutputTimer.Record=\"Stop recording after:\"\nOutputTimer.Stream.StoppingIn=\"Streaming stopping in:\"\nOutputTimer.Record.StoppingIn=\"Recording stopping in:\"\nOutputTimer.Stream.EnableEverytime=\"Enable streaming timer every time\"\nOutputTimer.Record.EnableEverytime=\"Enable recording timer every time\"\nOutputTimer.Record.PauseTimer=\"Pause timer when recording is paused\"\n\nScripts=\"Scripts\"\nLoadedScripts=\"Loaded Scripts\"\nAddScripts=\"Add Scripts\"\nRemoveScripts=\"Remove Scripts\"\nReloadScripts=\"Reload Scripts\"\nLoadedScripts=\"Loaded Scripts\"\nEditScript=\"Edit Script\"\nReload=\"Reload\"\nOpenFileLocation=\"Open file location\"\nPythonSettings=\"Python Settings\"\nPythonSettings.PythonInstallPath32bit=\"Python Install Path (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python Install Path (64bit)\"\nPythonSettings.BrowsePythonPath=\"Browse Python Path\"\nPythonSettings.PythonVersion=\"Loaded Python Version: %1\"\nPythonSettings.PythonNotLoaded=\"Python not currently loaded\"\nPythonSettings.AlreadyLoaded.Title=\"Python Already Loaded\"\nPythonSettings.AlreadyLoaded.Message=\"A copy of Python %1 is already loaded. To load the newly selected Python version, please restart OBS.\"\nScriptLogWindow=\"Script Log\"\nDescription=\"Description\"\nScriptDescriptionLink.Text=\"Open this link in your default web browser?\"\nScriptDescriptionLink.Text.Url=\"URL: %1\"\nScriptDescriptionLink.OpenURL=\"Open URL\"\n\nFileFilter.ScriptFiles=\"Script Files\"\nFileFilter.AllFiles=\"All Files\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/eo-UY.ini",
    "content": "SceneSwitcher=\"Aŭtomata scen-ŝanĝilo\"\nSceneSwitcher.OnNoMatch=\"Kiam neniu fenestro kongruas:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Ne ŝanĝu\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Ŝanĝu al:\"\nSceneSwitcher.CheckInterval=\"Kontrolu aktivan fenestro-titolon ĉiu(j)n:\"\nSceneSwitcher.ActiveOrNotActive=\"Scen-ŝanĝilo estas:\"\nInvalidRegex.Title=\"Nevalida regula esprimo\"\nInvalidRegex.Text=\"La regula esprimo, kiun vi enigis, estas nevalida.\"\nActive=\"Aktiva\"\nInactive=\"Neaktiva\"\nStart=\"Startigi\"\nStop=\"Ĉesigi\"\nCaptions=\"Subtekstoj (eksperimenta)\"\nCaptions.AudioSource=\"Sonfonto\"\nCaptions.CurrentSystemLanguage=\"Aktuala sistem-lingvo (%1)\"\nCaptions.Provider=\"Provizanto\"\nCaptions.Error.GenericFail=\"Malsukcesis startigi subtekstojn\"\nOutputTimer=\"Elig-prokrastilo\"\nOutputTimer.Stream=\"Ĉesi elsendi post:\"\nOutputTimer.Record=\"Ĉesi registri post:\"\nOutputTimer.Stream.StoppingIn=\"Elsendado ĉesos en:\"\nOutputTimer.Record.StoppingIn=\"Registrado ĉesos en:\"\nOutputTimer.Stream.EnableEverytime=\"Ebligi elsend-prokrastilon ĉiun fojon\"\nOutputTimer.Record.EnableEverytime=\"Ebligi registro-prokrastilon ĉiun fojon\"\nOutputTimer.Record.PauseTimer=\"Paŭzigi prokrastilon kiam registrado estas paŭzigita\"\nScripts=\"Skriptoj\"\nLoadedScripts=\"Ŝargitaj skriptoj\"\nAddScripts=\"Aldoni skriptojn\"\nRemoveScripts=\"Forigi skriptojn\"\nReloadScripts=\"Reŝargi skriptojn\"\nEditScript=\"Redakti Skripton\"\nReload=\"Reŝargi\"\nOpenFileLocation=\"Malfermi dosierlokon\"\nPythonSettings=\"Python-agordoj\"\nPythonSettings.PythonInstallPath32bit=\"Instal-loko de Python (32-bita)\"\nPythonSettings.PythonInstallPath64bit=\"Instal-loko de Python (64-bita)\"\nPythonSettings.BrowsePythonPath=\"Malfermi Python-indikon\"\nScriptLogWindow=\"Skript-protokolo\"\nDescription=\"Priskribo\"\nScriptDescriptionLink.Text=\"Ĉu malfermi tiun ĉi ligilon en via defaŭlta retfoliumilon?\"\nScriptDescriptionLink.Text.Url=\"Retadreso: %1\"\nScriptDescriptionLink.OpenURL=\"Malfermi retadreson\"\nFileFilter.ScriptFiles=\"Skript-dosieroj\"\nFileFilter.AllFiles=\"Ĉiuj dosieroj\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/es-ES.ini",
    "content": "SceneSwitcher=\"Selector de escena automático\"\nSceneSwitcher.OnNoMatch=\"Cuando no coincida con ninguna ventana:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"No cambiar\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Cambiar a:\"\nSceneSwitcher.CheckInterval=\"Comprobar el título de la ventana activa cada:\"\nSceneSwitcher.ActiveOrNotActive=\"El cambiador de escena está:\"\nInvalidRegex.Title=\"Expresión regular no válida\"\nInvalidRegex.Text=\"La expresión regular que has introducido no es válida.\"\nActive=\"Activo\"\nInactive=\"Inactivo\"\nStart=\"Iniciar\"\nStop=\"Detener\"\nCaptions=\"Subtítulos (Experimental)\"\nCaptions.AudioSource=\"Fuente de audio\"\nCaptions.CurrentSystemLanguage=\"Idioma actual del sistema (%1)\"\nCaptions.Provider=\"Proveedor\"\nCaptions.Error.GenericFail=\"Fallo al iniciar los subtítulos\"\nOutputTimer=\"Temporizador de salida\"\nOutputTimer.Stream=\"Detener la transmisión después de:\"\nOutputTimer.Record=\"Detener la grabación después de:\"\nOutputTimer.Stream.StoppingIn=\"Finalizando la transmisión en:\"\nOutputTimer.Record.StoppingIn=\"Finalizando la grabación en:\"\nOutputTimer.Stream.EnableEverytime=\"Activar temporizador en cada transmisión\"\nOutputTimer.Record.EnableEverytime=\"Activar temporizador en cada grabación\"\nOutputTimer.Record.PauseTimer=\"Pausar temporizador cuando la grabación se pause\"\nLoadedScripts=\"Scripts Cargados\"\nAddScripts=\"Añadir Scripts\"\nRemoveScripts=\"Eliminar Scripts\"\nReloadScripts=\"Recargar Scripts\"\nEditScript=\"Editar script\"\nReload=\"Recargar\"\nOpenFileLocation=\"Abrir ubicación del archivo\"\nPythonSettings=\"Configuración de Python\"\nPythonSettings.PythonInstallPath32bit=\"Ruta de la instalación de Python (32 bits)\"\nPythonSettings.PythonInstallPath64bit=\"Ruta de la instalación de Python (64 bits)\"\nPythonSettings.BrowsePythonPath=\"Explorar ruta de Python\"\nPythonSettings.PythonVersion=\"Versión de Python cargada: %1\"\nPythonSettings.PythonNotLoaded=\"Python no está cargado actualmente\"\nPythonSettings.AlreadyLoaded.Title=\"Python ya está cargado\"\nPythonSettings.AlreadyLoaded.Message=\"Una copia de Python %1 ya está cargada. Para cargar la nueva versión de Python seleccionada, reinicie OBS.\"\nScriptLogWindow=\"Registros de script\"\nDescription=\"Descripción\"\nScriptDescriptionLink.Text=\"¿Abrir este enlace en su navegador predeterminado?\"\nScriptDescriptionLink.OpenURL=\"Abrir URL\"\nFileFilter.ScriptFiles=\"Archivos de Script\"\nFileFilter.AllFiles=\"Todos los archivos\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/et-EE.ini",
    "content": "SceneSwitcher=\"Automaatne stseeni vahetaja\"\nSceneSwitcher.OnNoMatch=\"Kui üksi aken ei sobi:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Ära vaheta\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Lülitu ümber:\"\nSceneSwitcher.CheckInterval=\"Kontrollige aktiivse akna pealkiri iga:\"\nSceneSwitcher.ActiveOrNotActive=\"Stseen vahetaja on:\"\nInvalidRegex.Title=\"Kehtetu regulaaravaldis\"\nInvalidRegex.Text=\"Sisestatud regulaaravaldis on kehtetu.\"\nActive=\"Aktiivne\"\nInactive=\"Inaktiivne\"\nStart=\"Alusta\"\nStop=\"Lõpeta\"\nCaptions=\"Subtiitrid (eksperimentaalne)\"\nCaptions.AudioSource=\"Heli allikas\"\nCaptions.CurrentSystemLanguage=\"Praegune süsteemi keel (%1)\"\nCaptions.Provider=\"Pakkuja\"\nCaptions.Error.GenericFail=\"Subtiitrite käivitamine ebaõnnestus\"\nOutputTimer=\"Väljundi taimer\"\nOutputTimer.Stream=\"Lõpeta voogedastus pärast:\"\nOutputTimer.Record=\"Lõpeta voogedastus pärast:\"\nOutputTimer.Stream.StoppingIn=\"Voogedastus lõppeb:\"\nOutputTimer.Record.StoppingIn=\"Salvestamine lõppeb:\"\nOutputTimer.Stream.EnableEverytime=\"Lülita voogedastuse taimer alati sisse\"\nOutputTimer.Record.EnableEverytime=\"Lülita salvestus taimer alati sisse\"\nOutputTimer.Record.PauseTimer=\"Peatage taimer, kui salvestamine on peatatud\"\nScripts=\"Skriptid\"\nLoadedScripts=\"Laetud skriptid\"\nAddScripts=\"Skriptide lisamine\"\nRemoveScripts=\"Skriptide eemaldamine\"\nReloadScripts=\"Lae skriptid uuesti\"\nEditScript=\"Skripti muutmine\"\nReload=\"Lae uuesti\"\nOpenFileLocation=\"Ava faili asukoht\"\nPythonSettings=\"Pythoni seaded\"\nPythonSettings.PythonInstallPath32bit=\"Pythoni installitee (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Pythoni installitee (64bit)\"\nPythonSettings.BrowsePythonPath=\"Sirvige Pythoni teed\"\nPythonSettings.PythonVersion=\"Laaditud Pythoni versioon: %1\"\nPythonSettings.PythonNotLoaded=\"Python ei ole laaditud\"\nPythonSettings.AlreadyLoaded.Title=\"Python on juba laaditud\"\nPythonSettings.AlreadyLoaded.Message=\"Pythoni koopia %1 on juba laaditud. Uue valitud Pythoni versiooni laadimiseks käivitage OBS uuesti.\"\nScriptLogWindow=\"Skripti logi\"\nDescription=\"Kirjeldus\"\nScriptDescriptionLink.Text=\"Ava see link oma vaikimisi veebibrauseris?\"\nScriptDescriptionLink.OpenURL=\"Ava URL\"\nFileFilter.ScriptFiles=\"Skriptifailid\"\nFileFilter.AllFiles=\"Kõik failid\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/eu-ES.ini",
    "content": "SceneSwitcher=\"Eszena-aldatzaile automatikoa\"\nSceneSwitcher.OnNoMatch=\"Leihoa bat etortzen ez denean:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Ez aldatu\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Aldatu hona:\"\nSceneSwitcher.CheckInterval=\"Leiho aktiboaren titulua egiaztatzeko maiztasuna:\"\nSceneSwitcher.ActiveOrNotActive=\"Eszena aldatzailea dago:\"\nInvalidRegex.Title=\"Adierazpen erregular baliogabea\"\nInvalidRegex.Text=\"Sartu duzun adierazpen erregularra baliogabea da.\"\nActive=\"Aktiboa\"\nInactive=\"Inaktiboa\"\nStart=\"Hasi\"\nStop=\"Gelditu\"\nCaptions=\"Epigrafeak (esperimentala)\"\nCaptions.AudioSource=\"Audio-iturburua\"\nCaptions.CurrentSystemLanguage=\"Sistemaren hizkuntza (%1)\"\nCaptions.Provider=\"Hornitzailea\"\nCaptions.Error.GenericFail=\"Huts egin du grabazioak\"\nOutputTimer=\"Irteera tenporizadorea\"\nOutputTimer.Stream=\"Gelditu transmisioa hau pasata:\"\nOutputTimer.Record=\"Gelditu grabazioa hau pasata:\"\nOutputTimer.Stream.StoppingIn=\"Transmisioa geldituko da: hau barru:\"\nOutputTimer.Record.StoppingIn=\"Grabazioa geldituko da hau barru:\"\nOutputTimer.Stream.EnableEverytime=\"Gaitu transmisio tenporizadorea aldiro\"\nOutputTimer.Record.EnableEverytime=\"Gaitu grabazio tenporizadorea aldiro\"\nOutputTimer.Record.PauseTimer=\"Pausatu tenporizadorea grabazioa pausatuta dagoenean\"\nScripts=\"Script-ak\"\nLoadedScripts=\"Kargatutako script-ak\"\nAddScripts=\"Gehitu script-ak\"\nRemoveScripts=\"Kendu script-ak\"\nReloadScripts=\"Birkargatu script-ak\"\nEditScript=\"Editatu script-a\"\nReload=\"Berriro kargatu\"\nOpenFileLocation=\"Artxiboaren kokapena ireki\"\nPythonSettings=\"Python ezarpenak\"\nPythonSettings.PythonInstallPath32bit=\"Python instalazioaren bide-izena (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python instalazioaren bide-izena (64bit)\"\nPythonSettings.BrowsePythonPath=\"Arakatu Python-en bide-izena\"\nPythonSettings.PythonVersion=\"Python Bertsioa Exekutatuta: %1\"\nPythonSettings.PythonNotLoaded=\"Python ez dago exekutatuta oraindik\"\nPythonSettings.AlreadyLoaded.Title=\"Python Exekutatuta dago\"\nPythonSettings.AlreadyLoaded.Message=\"Python-eko %1 kopia bat dago exekutatuta. Inportatzeko Python hautatutako bertsio berria, mesedez OBS berrabiarazi\"\nScriptLogWindow=\"Script-aren erregistroa\"\nDescription=\"Deskribapena\"\nScriptDescriptionLink.Text=\"Ireki esteka hau zure nabigatzaile lehenetsian?\"\nScriptDescriptionLink.Text.Url=\"URLa: %1\"\nScriptDescriptionLink.OpenURL=\"Ireki web-helbidea\"\nFileFilter.ScriptFiles=\"Script-aren fitxategiak\"\nFileFilter.AllFiles=\"Fitxategi guztiak\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/fa-IR.ini",
    "content": "SceneSwitcher=\"تغییر صحنه خودکار\"\nSceneSwitcher.OnNoMatch=\"وقتی هیچ پنجره‌ای همخوانی نداشت:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"سوییچ نکن\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"تغییر کن به:\"\nSceneSwitcher.CheckInterval=\"عنوان پنجره فعال را هر بار بررسی کنید:\"\nSceneSwitcher.ActiveOrNotActive=\"تغییر صحنه:\"\nInvalidRegex.Title=\"عبارت منظم نامعتبر است\"\nInvalidRegex.Text=\"عبارت منظمی که وارد کرده اید نامعتبر است.\"\nActive=\"فعال\"\nInactive=\"غیر فعال\"\nStart=\"شروع\"\nStop=\"توقف\"\nCaptions=\"زیرنویس ها (تجربی)\"\nCaptions.AudioSource=\"منبع صدا\"\nCaptions.CurrentSystemLanguage=\"زبان سیستم فعلی (%1)\"\nCaptions.Provider=\"ارائه دهنده\"\nCaptions.Error.GenericFail=\"زیرنویس شروع نشد\"\nOutputTimer=\"تایمر خروجی\"\nOutputTimer.Stream=\"توقف پخش جریانی بعد از:\"\nOutputTimer.Record=\"توقف ضبط بعد از:\"\nOutputTimer.Stream.StoppingIn=\"توقف پخش جریان در:\"\nOutputTimer.Record.StoppingIn=\"متوقف کردن ضبط در:\"\nOutputTimer.Stream.EnableEverytime=\"هربار زمان سنج پخش جریانی را فعال کنید\"\nOutputTimer.Record.EnableEverytime=\"هربار زمان سنج ضبط را فعال کنید\"\nOutputTimer.Record.PauseTimer=\"هنگام مکث ضبط، تایمر مکث کند\"\nScripts=\"اسکریپت ها\"\nLoadedScripts=\"اسکریپت های بارگیری شده\"\nAddScripts=\"اضافه کردن اسکریپت ها\"\nRemoveScripts=\"پاک کردن اسکریپت ها\"\nReloadScripts=\"بارگیری مجدد اسکریپت ها\"\nEditScript=\"ویرایش اسکریپت ها\"\nReload=\"تازه‌سازی\"\nOpenFileLocation=\"گشودن مکان پرونده\"\nPythonSettings=\"تنظیمات پایتون\"\nPythonSettings.PythonInstallPath32bit=\"مسیر نصب پایتون (32 بیت)\"\nPythonSettings.PythonInstallPath64bit=\"مسیر نصب پایتون (64 بیت)\"\nPythonSettings.BrowsePythonPath=\"مرور مسیر پایتون\"\nPythonSettings.PythonVersion=\"نسخه پایتون بارگذاری شده: %1\"\nPythonSettings.PythonNotLoaded=\"پایتون بارگذاری نشده است\"\nPythonSettings.AlreadyLoaded.Title=\"پایتون قبلا بارگذاری شده است\"\nPythonSettings.AlreadyLoaded.Message=\"یک نسخه از پایتون %1 قبلاً بارگذاری شده است. برای بارگذاری نسخه تازه انتخاب شده پایتون، لطفاً OBS را دوباره راه‌اندازی کنید.\"\nScriptLogWindow=\"اسکریپت نویسی\"\nDescription=\"توضیحات\"\nScriptDescriptionLink.Text=\"این پیوند را در مرورگر وب پیش فرض خود باز می کنید?\"\nScriptDescriptionLink.Text.Url=\"نشانی: %1\"\nScriptDescriptionLink.OpenURL=\"باز کردن نشانی\"\nFileFilter.ScriptFiles=\"پرونده‌های کدنوشته\"\nFileFilter.AllFiles=\"تمامی پرونده‌ها\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/fi-FI.ini",
    "content": "SceneSwitcher=\"Automaattinen näkymävaihtaja\"\nSceneSwitcher.OnNoMatch=\"Kun ikkunaa ei löydy:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Älä vaihda\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Vaihda:\"\nSceneSwitcher.CheckInterval=\"Tarkista aktiivisen ikkunan otsikko:\"\nSceneSwitcher.ActiveOrNotActive=\"Näkymävaihtaja:\"\nActive=\"Aktiivinen\"\nInactive=\"Ei käytössä\"\nStart=\"Käynnistä\"\nStop=\"Pysäytä\"\nCaptions=\"Kuvatekstit (Experimental)\"\nCaptions.AudioSource=\"Äänilähde\"\nCaptions.CurrentSystemLanguage=\"Järjestelmän kieli (%1)\"\nCaptions.Provider=\"Tarjoaja\"\nCaptions.Error.GenericFail=\"Kuvatekstityksen aloittaminen epäonnistui\"\nOutputTimer=\"Ulostulo-ajastin\"\nOutputTimer.Stream=\"Pysäyttää lähetyksen:\"\nOutputTimer.Record=\"Pysäyttää tallennuksen:\"\nOutputTimer.Stream.StoppingIn=\"Lähetys pysäytetään:\"\nOutputTimer.Record.StoppingIn=\"Tallennus pysäytetään:\"\nOutputTimer.Stream.EnableEverytime=\"Ota lähetysajastin käyttöön aina\"\nOutputTimer.Record.EnableEverytime=\"Ota tallennusajastin käyttöön aina\"\nOutputTimer.Record.PauseTimer=\"Pysäytä ajastin kun tallennus on pysäytettynä\"\nScripts=\"Skriptit\"\nLoadedScripts=\"Ladatut skriptit\"\nAddScripts=\"Lisää skriptejä\"\nRemoveScripts=\"Poista skriptejä\"\nReloadScripts=\"Lataa skriptit uudelleen\"\nEditScript=\"Muokkaa skriptiä\"\nReload=\"Päivitä\"\nOpenFileLocation=\"Avaa tiedoston sijainti\"\nPythonSettings=\"Python-asetukset\"\nPythonSettings.PythonInstallPath32bit=\"Python-asennuspolku (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python-asennuspolku (64bit)\"\nPythonSettings.BrowsePythonPath=\"Selaa Python-polku\"\nPythonSettings.PythonVersion=\"Ladattu Python-versio: %1\"\nPythonSettings.PythonNotLoaded=\"Pythonia ei ole ladattu\"\nPythonSettings.AlreadyLoaded.Title=\"Python on jo ladattu\"\nPythonSettings.AlreadyLoaded.Message=\"Kopio Pythonin versiosta %1 on jo ladattu. Ladataksesi uuden valitun Python-version, käynnistä OBS uudelleen.\"\nScriptLogWindow=\"Skripti-lokit\"\nDescription=\"Kuvaus\"\nScriptDescriptionLink.Text=\"Avaa tämä linkki oletusselaimessasi?\"\nScriptDescriptionLink.OpenURL=\"Avaa URL\"\nFileFilter.ScriptFiles=\"Skripti-tiedostot\"\nFileFilter.AllFiles=\"Kaikki tiedostot\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/fil-PH.ini",
    "content": "SceneSwitcher=\"Awyomatikong Tagalipat ng Eksena\"\nSceneSwitcher.OnNoMatch=\"Kapag walang tumutugma na window:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Huwag lumipat\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Lumipat sa:\"\nSceneSwitcher.CheckInterval=\"Suriin ang aktibong pamagat ng bintana bawat:\"\nSceneSwitcher.ActiveOrNotActive=\"Ang Tagalipat ng Eksena ay:\"\nInvalidRegex.Title=\"Di-wastong Regular Expression\"\nInvalidRegex.Text=\"Ang regular na expression na ipinasok mo ay hindi wasto.\"\nActive=\"Aktibo\"\nInactive=\"Di-aktibo\"\nStart=\"Simulan\"\nStop=\"Ihinto\"\nCaptions=\"Mga Caption (Experimental)\"\nCaptions.AudioSource=\"Bukal ng audio\"\nCaptions.CurrentSystemLanguage=\"Kasalukuyang Wika ng Sistema (%1)\"\nCaptions.Provider=\"Tagapagbigay\"\nCaptions.Error.GenericFail=\"Nabigong simulan ang mga caption\"\nOutputTimer=\"Timer ng Output\"\nOutputTimer.Stream=\"Itigil ang pag-iistream pagkatapos ng:\"\nOutputTimer.Record=\"Itigil ang pag-record pagkatapos ng:\"\nOutputTimer.Stream.StoppingIn=\"Matitigil ang pag-iistream sa:\"\nOutputTimer.Record.StoppingIn=\"Matitigil ang pagre-record sa:\"\nOutputTimer.Stream.EnableEverytime=\"Paganahin ang streaming timer sa bawat oras\"\nOutputTimer.Record.EnableEverytime=\"Paganahin ang timer ng pagrerecord palagi\"\nOutputTimer.Record.PauseTimer=\"I-pause ang timer kapag naka-pause ang pagrerecord\"\nScripts=\"Mga iskrip\"\nLoadedScripts=\"Mga nakaload na mga Iskrip\"\nAddScripts=\"Magdagdag ng mga Iskrip\"\nRemoveScripts=\"Alisin ang mga Iskrip\"\nReloadScripts=\"I-reload ang mga Iskrip\"\nEditScript=\"Baguhin ang Script\"\nReload=\"I-reload\"\nOpenFileLocation=\"Buksan ang lokasyon ng file\"\nPythonSettings=\"Mga setting ng Python\"\nPythonSettings.PythonInstallPath32bit=\"Landas ng Instalasyon ng Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Landas ng Instalasyon ng Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"Pumunta sa Landas ng Python\"\nPythonSettings.PythonVersion=\"Na-load na ang Bersyon ng Python : %1\"\nPythonSettings.PythonNotLoaded=\"Kasalukuyang hindi na-loload ang Python\"\nPythonSettings.AlreadyLoaded.Title=\"Nag-load na ang Python\"\nPythonSettings.AlreadyLoaded.Message=\"Na-load na ang kopya ng Python %1. Upang i-load ang panibagong napiling bersyon ng Python, mangyaring i-restart ang OBS.\"\nScriptLogWindow=\"Window ng Log ng Iskrip\"\nDescription=\"Paglalarawan\"\nScriptDescriptionLink.Text=\"Buksan ang link na ito sa iyong default na pang-browse ng web?\"\nScriptDescriptionLink.OpenURL=\"Buksan ang URL\"\nFileFilter.ScriptFiles=\"Mga File ng Iskrip\"\nFileFilter.AllFiles=\"Lahat ng mga File\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/fr-FR.ini",
    "content": "SceneSwitcher=\"Sélecteur automatique de scène\"\nSceneSwitcher.OnNoMatch=\"Si aucune fenêtre ne correspond :\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Ne rien faire\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Basculer vers :\"\nSceneSwitcher.CheckInterval=\"Vérifier le titre de la fenêtre active toutes les :\"\nSceneSwitcher.ActiveOrNotActive=\"Le sélecteur automatique de scène est :\"\nInvalidRegex.Title=\"Expression régulière invalide\"\nInvalidRegex.Text=\"L'expression régulière saisie est invalide.\"\nActive=\"Actif\"\nInactive=\"Inactif\"\nStart=\"Démarrer\"\nStop=\"Arrêter\"\nCaptions=\"Sous-titres (expérimental)\"\nCaptions.AudioSource=\"Source audio\"\nCaptions.CurrentSystemLanguage=\"Langue du système (%1)\"\nCaptions.Provider=\"Fournisseur\"\nCaptions.Error.GenericFail=\"Impossible de démarrer les sous-titres\"\nOutputTimer=\"Minuterie des sorties\"\nOutputTimer.Stream=\"Arrêter le streaming après :\"\nOutputTimer.Record=\"Arrêter l'enregistrement après :\"\nOutputTimer.Stream.StoppingIn=\"Arrêt du streaming dans :\"\nOutputTimer.Record.StoppingIn=\"Arrêt de l'enregistrement dans :\"\nOutputTimer.Stream.EnableEverytime=\"Démarrer le minuteur automatiquement à chaque stream\"\nOutputTimer.Record.EnableEverytime=\"Démarrer le minuteur automatiquement à chaque enregistrement\"\nOutputTimer.Record.PauseTimer=\"Mettre en pause le minuteur quand l'enregistrement est mis en pause\"\nLoadedScripts=\"Scripts chargés\"\nAddScripts=\"Ajouter des Scripts\"\nRemoveScripts=\"Retirer des Scripts\"\nReloadScripts=\"Recharger les Scripts\"\nEditScript=\"Modifier le script\"\nReload=\"Recharger\"\nOpenFileLocation=\"Ouvrir l'emplacement du fichier\"\nPythonSettings=\"Paramètres Python\"\nPythonSettings.PythonInstallPath32bit=\"Chemin d’installation Python (32 bits)\"\nPythonSettings.PythonInstallPath64bit=\"Chemin d’installation Python (64 bits)\"\nPythonSettings.BrowsePythonPath=\"Localiser l'installation de Python\"\nPythonSettings.PythonVersion=\"Version de Python chargée : %1\"\nPythonSettings.PythonNotLoaded=\"Python non chargé\"\nPythonSettings.AlreadyLoaded.Title=\"Python est déjà chargé\"\nPythonSettings.AlreadyLoaded.Message=\"Une copie de Python %1 est déjà chargée. Pour charger la version Python nouvellement sélectionnée, veuillez redémarrer OBS.\"\nScriptLogWindow=\"Journal de script\"\nScriptDescriptionLink.Text=\"Souhaitez-vous ouvrir ce lien avec votre navigateur internet usuel ?\"\nScriptDescriptionLink.Text.Url=\"URL : %1\"\nScriptDescriptionLink.OpenURL=\"Ouvrir l'URL\"\nFileFilter.ScriptFiles=\"Fichiers de script\"\nFileFilter.AllFiles=\"Tous les fichiers\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/gd-GB.ini",
    "content": "SceneSwitcher=\"Suidsear fèin-obrachail nan sealladh\"\nSceneSwitcher.OnNoMatch=\"Mura freagair uinneag\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Na dèan suids\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Dèan suids gu:\"\nSceneSwitcher.CheckInterval=\"Thoir sùil air tiotal na h-uinneige gnìomhaich gach:\"\nSceneSwitcher.ActiveOrNotActive=\"Tha suidsear nan sealladh:\"\nInvalidRegex.Title=\"Chan eil an t-eas-preisean riaghailteach dligheach\"\nInvalidRegex.Text=\"Chan eil an t-eas-preisean riaghailteach a chuir thu a-steach dligheach.\"\nActive=\"Gnìomhach\"\nInactive=\"Neo-ghnìomhach\"\nStart=\"Tòisich\"\nStop=\"Cuir stad air\"\nCaptions=\"Fo-thiotalan (deuchainneil)\"\nCaptions.AudioSource=\"Tùs fuaime\"\nCaptions.CurrentSystemLanguage=\"Cànan làithreach an t-siostaim (%1)\"\nCaptions.Provider=\"Solaraiche\"\nCaptions.Error.GenericFail=\"Cha deach leinn na fo-thiotalan a thòiseachadh\"\nOutputTimer=\"Tìmear an às-chuir\"\nOutputTimer.Stream=\"Cuir stad air an t-sruthadh às dèidh:\"\nOutputTimer.Record=\"Cuir stad air a’ chlàradh às dèidh:\"\nOutputTimer.Stream.StoppingIn=\"Thèid an sruthadh a chur ’na stad às dèidh:\"\nOutputTimer.Record.StoppingIn=\"Thèid an clàradh a chur ’na stad às dèidh:\"\nOutputTimer.Stream.EnableEverytime=\"Cuir tìmear an t-sruthaidh an comas gach turas\"\nOutputTimer.Record.EnableEverytime=\"Cuir tìmear a’ chlàraidh an comas gach turas\"\nOutputTimer.Record.PauseTimer=\"Cuir an tìmear ’na stad nuair a bhios an clàradh ’na stad\"\nScripts=\"Sgriobtaichean\"\nLoadedScripts=\"Sgriobtaichean luchdaichte\"\nAddScripts=\"Cuir sgriobtaichean ris\"\nRemoveScripts=\"Thoir sgriobtaichean air falbh\"\nReloadScripts=\"Ath-luchdaich na sgriobtaichean\"\nEditScript=\"Deasaich sgriobt\"\nReload=\"Ath-luchdaich\"\nOpenFileLocation=\"Fosgail ionad an fhaidhle\"\nPythonSettings=\"Roghainnean Python\"\nPythonSettings.PythonInstallPath32bit=\"Slighe stàlaidh Python (32 biod)\"\nPythonSettings.PythonInstallPath64bit=\"Slighe stàlaidh Python (64 biod)\"\nPythonSettings.BrowsePythonPath=\"Rùraich airson slighe stàlaidh Python\"\nScriptLogWindow=\"Loga nan sgriobt\"\nDescription=\"Tuairisgeul\"\nScriptDescriptionLink.Text=\"A bheil thu airson an ceangal seo fhosgladh sa bhrabhsair-lìn bhunaiteach agad?\"\nScriptDescriptionLink.OpenURL=\"Fosgail URL\"\nFileFilter.ScriptFiles=\"Faidhlichean sgriobt\"\nFileFilter.AllFiles=\"Na h-uile faidhle\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/gl-ES.ini",
    "content": "SceneSwitcher=\"Conmutador automático de escena\"\nSceneSwitcher.OnNoMatch=\"Cando non coincida ningunha xanela:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Non conmutar\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Cambiar a:\"\nSceneSwitcher.CheckInterval=\"Comprobar o título da xanela activa cada:\"\nSceneSwitcher.ActiveOrNotActive=\"O conmutador de escena está:\"\nInvalidRegex.Title=\"Expresión regular non válida\"\nInvalidRegex.Text=\"A expresión regular que introduciu non é válida.\"\nActive=\"Activo\"\nInactive=\"Inactivo\"\nStart=\"Iniciar\"\nStop=\"Parar\"\nCaptions=\"Subtítulos (experimental)\"\nCaptions.AudioSource=\"Fonte de son\"\nCaptions.CurrentSystemLanguage=\"Idioma actual do sistema (%1)\"\nCaptions.Provider=\"Fornecedor\"\nCaptions.Error.GenericFail=\"Produciuse un erro ao iniciar os suntítulos\"\nOutputTimer=\"Temporizador de saída\"\nOutputTimer.Stream=\"Deter a emisión após:\"\nOutputTimer.Record=\"Deter a gravación após:\"\nOutputTimer.Stream.StoppingIn=\"Detendo a emisión en:\"\nOutputTimer.Record.StoppingIn=\"Detendo a gravación en:\"\nOutputTimer.Stream.EnableEverytime=\"Activar o temporizador en cada emisión\"\nOutputTimer.Record.EnableEverytime=\"Activar o temporizador en cada gravación\"\nOutputTimer.Record.PauseTimer=\"Pór en pausa o temporizador ao pór en pausa a gravación\"\nLoadedScripts=\"Scripts cargados\"\nAddScripts=\"Engadir scripts\"\nRemoveScripts=\"Retirar scripts\"\nReloadScripts=\"Volver cargar scripts\"\nEditScript=\"Editar script\"\nReload=\"Volver cargar\"\nOpenFileLocation=\"Abrir a localización do ficheiro\"\nPythonSettings=\"Axustes de Python\"\nPythonSettings.PythonInstallPath32bit=\"Ruta de instalación de Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Ruta de instalación de Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"Examinar a ruta de Python\"\nPythonSettings.PythonVersion=\"Versión de Python cargada: %1\"\nPythonSettings.PythonNotLoaded=\"Python non está cargado\"\nPythonSettings.AlreadyLoaded.Title=\"Python xa está cargado\"\nPythonSettings.AlreadyLoaded.Message=\"Unha copia de Python %1 xa está cargada. Para cargar a nova versión de Python seleccionada, por favor reinicie OBS.\"\nScriptLogWindow=\"Rexistro de script\"\nDescription=\"Descrición\"\nScriptDescriptionLink.Text=\"Abrir esta ligazón no seu navegador predeterminado?\"\nScriptDescriptionLink.OpenURL=\"Abrir URL\"\nFileFilter.ScriptFiles=\"Ficheiros de scripts\"\nFileFilter.AllFiles=\"Todos os ficheiros\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/he-IL.ini",
    "content": "SceneSwitcher=\"החלפת סצנה אוטומטית\"\nSceneSwitcher.OnNoMatch=\"כאשר אין חלון מתאים:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"אל תעבור\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"עבור ל:\"\nSceneSwitcher.CheckInterval=\"בדוק כותרת חלון פעיל בכל:\"\nSceneSwitcher.ActiveOrNotActive=\"החלפת סצנה זה:\"\nInvalidRegex.Title=\"ביטוי רגולרי לא חוקי\"\nInvalidRegex.Text=\"הביטוי הרגולרי שהזנת אינו חוקי.\"\nActive=\"פעיל\"\nInactive=\"לא פעיל\"\nStart=\"התחל\"\nStop=\"עצור\"\nCaptions=\"כיתובים (ניסיוני)\"\nCaptions.AudioSource=\"מקור שמע\"\nCaptions.CurrentSystemLanguage=\"שפת המערכת הנוכחי (%1)\"\nCaptions.Provider=\"ספק\"\nCaptions.Error.GenericFail=\"נכשלה הפעלת כיתובים\"\nOutputTimer=\"פלט טיימר\"\nOutputTimer.Stream=\"סיום השידור החי תוך:\"\nOutputTimer.Record=\"סיום ההקלטה תוך:\"\nOutputTimer.Stream.StoppingIn=\"השידור החי יסתיים בעוד:\"\nOutputTimer.Record.StoppingIn=\"ההקלטה תסתיים בעוד:\"\nOutputTimer.Stream.EnableEverytime=\"הפעלת טיימר לשידורים החיים בכל פעם\"\nOutputTimer.Record.EnableEverytime=\"הפעלת טיימר להקלטות בכל פעם\"\nOutputTimer.Record.PauseTimer=\"השהה את הטיימר בזמן שההקלטה מושהית\"\nScripts=\"סקריפטים\"\nLoadedScripts=\"סקריפטים טעונים\"\nAddScripts=\"הוספת סקריפטים\"\nRemoveScripts=\"הסרת סקריפטים\"\nReloadScripts=\"טעינת סקריפטים מחדש\"\nEditScript=\"ערוך תסריט\"\nReload=\"טען מחדש\"\nOpenFileLocation=\"פתח מיקום קובץ\"\nPythonSettings=\"הגדרות פייתון\"\nPythonSettings.PythonInstallPath32bit=\"נתיב התקנת פייתון (32 סיביות)\"\nPythonSettings.PythonInstallPath64bit=\"התקנת נתיב פייתון (64 סיביות)\"\nPythonSettings.BrowsePythonPath=\"עיון בנתיב פייתון\"\nPythonSettings.PythonVersion=\"גרסת Python שנטענה: %1\"\nPythonSettings.PythonNotLoaded=\"Python לא טעון כרגע\"\nPythonSettings.AlreadyLoaded.Title=\"Python כבר נטען\"\nPythonSettings.AlreadyLoaded.Message=\"עותק Python %1 כבר טעון. כדי לטעון את גרסת ה־Python החדשה שנבחרה, נא להפעיל את OBS מחדש.\"\nScriptLogWindow=\"סקריפט לוג\"\nDescription=\"תיאור\"\nScriptDescriptionLink.Text=\"לפתוח קישור זה בתוך דפדפן ברירת המחדל שלך?\"\nScriptDescriptionLink.Text.Url=\"כתובת: %1\"\nScriptDescriptionLink.OpenURL=\"פתח כתובת\"\nFileFilter.ScriptFiles=\"קובץ סקריפט\"\nFileFilter.AllFiles=\"כל הקבצים\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/hi-IN.ini",
    "content": "SceneSwitcher=\"स्वचालित दृश्य स्विचर\"\nSceneSwitcher.OnNoMatch=\"जब कोई विंडो मेल नहीं खाती:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"स्विच न करें\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"में स्विच करे\"\nSceneSwitcher.CheckInterval=\"में सक्रिय विंडो शीर्षक की जाँच करें :\"\nSceneSwitcher.ActiveOrNotActive=\"दृश्य स्वीचर :\"\nInvalidRegex.Title=\"अमान्य रेगुलर एक्सप्रेशन\"\nInvalidRegex.Text=\"आपके द्वारा दर्ज किया गया रेगुलर एक्सप्रेशन अमान्य है.\"\nActive=\"सक्रिय\"\nInactive=\"निष्क्रिय\"\nStart=\"प्रारंभ\"\nStop=\"रोकें\"\nCaptions=\"कैप्शन्स (प्रयोगात्मक)\"\nCaptions.AudioSource=\"ऑडियो स्रोत\"\nCaptions.CurrentSystemLanguage=\"वर्तमान सिस्टम भाषा (%1)\"\nCaptions.Provider=\"प्रदाता\"\nCaptions.Error.GenericFail=\"कैप्शन्स आरंभ करने में विफल\"\nOutputTimer=\"आउटपुट टाइमर\"\nOutputTimer.Stream=\"इसके बाद प्रसारण रोकें :\"\nOutputTimer.Record=\"इसके बाद रिकॉर्डिंग बंद करें :\"\nOutputTimer.Stream.StoppingIn=\"स्ट्रीमिंग इतने में बंद होगा :\"\nOutputTimer.Record.StoppingIn=\"रिकॉर्डिंग इतने में बंद होगा :\"\nOutputTimer.Stream.EnableEverytime=\"प्रत्येक बार स्ट्रीमिंग टाइमर सक्षम करें\"\nOutputTimer.Record.EnableEverytime=\"प्रत्येक बार रिकॉर्डिंग टाइमर सक्षम करें\"\nOutputTimer.Record.PauseTimer=\"रिकॉर्डिंग रोके जाने पर टाइमर रोकें\"\nScripts=\"स्क्रिप्ट\"\nLoadedScripts=\"लोड की गई स्क्रिप्टें\"\nAddScripts=\"स्क्रिप्ट जोड़ें\"\nRemoveScripts=\"स्क्रिप्ट हटाएं\"\nReloadScripts=\"स्क्रिप्ट रिलोड करें\"\nEditScript=\"स्क्रिप्ट संपादित करें\"\nReload=\"फिर से लोड करें\"\nOpenFileLocation=\"फाइल स्थान खोलें\"\nPythonSettings=\"Python सेटिंग्स\"\nPythonSettings.PythonInstallPath32bit=\"Python इंस्टाल पथ (32 बिट)\"\nPythonSettings.PythonInstallPath64bit=\"Python इंस्टाल पथ (64 बिट)\"\nPythonSettings.BrowsePythonPath=\"Python पथ ब्राउज़ करें\"\nPythonSettings.PythonVersion=\"लोडेड Python संस्करण : %1\"\nPythonSettings.PythonNotLoaded=\"Python वर्तमान में लोड नहीं है\"\nPythonSettings.AlreadyLoaded.Title=\"Python पहले से लोड है\"\nPythonSettings.AlreadyLoaded.Message=\"Python %1 की एक प्रति पहले ही लोड हो चुकी है. नए चयनित Python संस्करण को लोड करने के लिए, कृपया OBS को पुनः आरंभ करें.\"\nScriptLogWindow=\"स्क्रिप्ट लॉग\"\nDescription=\"विवरण\"\nScriptDescriptionLink.Text=\"इस लिंक को अपने डिफ़ॉल्ट वेब ब्राउज़र में खोलें?\"\nScriptDescriptionLink.OpenURL=\"URL खोलें\"\nFileFilter.ScriptFiles=\"स्क्रिप्ट फ़ाइलें\"\nFileFilter.AllFiles=\"सभी फ़ाइलें\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/hr-HR.ini",
    "content": "SceneSwitcher=\"Automatski mjenjač scena\"\nSceneSwitcher.OnNoMatch=\"Kada nema odgovarajućeg prozora:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Ne prebacuj scenu\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Prebaci se na:\"\nSceneSwitcher.CheckInterval=\"Provjeri naziv aktivnog prozora svakih:\"\nSceneSwitcher.ActiveOrNotActive=\"Mjenjač scena je:\"\nInvalidRegex.Title=\"Nevažeć regularni izraz\"\nInvalidRegex.Text=\"Regularni izraz koji ste unijeli je nevažeć.\"\nActive=\"Aktivan\"\nInactive=\"Neaktivan\"\nStart=\"Pokreni\"\nStop=\"Zaustavi\"\nCaptions=\"Titlovi (eksperimentalno)\"\nCaptions.AudioSource=\"Izvor zvuka\"\nCaptions.CurrentSystemLanguage=\"Trenutni sistemski jezik (%1)\"\nCaptions.Provider=\"Opskrbljivač\"\nCaptions.Error.GenericFail=\"Greška u inicijalizaciji titlova\"\nOutputTimer=\"Štoperica\"\nOutputTimer.Stream=\"Prekini emitiranje za:\"\nOutputTimer.Record=\"Prekini snimanje za:\"\nOutputTimer.Stream.StoppingIn=\"Emitiranje se prekida za:\"\nOutputTimer.Record.StoppingIn=\"Snimanje se prekida za:\"\nOutputTimer.Stream.EnableEverytime=\"Uvijek omogući štopericu za emitiranje\"\nOutputTimer.Record.EnableEverytime=\"Uvijek omogući štopericu za snimanje\"\nOutputTimer.Record.PauseTimer=\"Zaustavi štopericu dok je snimanje pauzirano\"\nScripts=\"Skripte\"\nLoadedScripts=\"Učitane skripte\"\nAddScripts=\"Dodaj skripte\"\nRemoveScripts=\"Ukloni skripte\"\nReloadScripts=\"Osvježi skripte\"\nEditScript=\"Uredi skripta\"\nReload=\"Učitaj iznova\"\nOpenFileLocation=\"Otvori lokaciju datoteke\"\nPythonSettings=\"Postavke Pythona\"\nPythonSettings.PythonInstallPath32bit=\"Put do Python instalacije (32-bitne)\"\nPythonSettings.PythonInstallPath64bit=\"Put do Python instalacije (64-bitne)\"\nPythonSettings.BrowsePythonPath=\"Lociraj Python ručno\"\nPythonSettings.PythonVersion=\"Učitana Python verzija: %1\"\nPythonSettings.PythonNotLoaded=\"Python trenutačno nije učitan\"\nPythonSettings.AlreadyLoaded.Title=\"Python je već učitan\"\nPythonSettings.AlreadyLoaded.Message=\"Kopija Pythona %1 je već učitana. Za učitavanje novoodabrane verzije Pythona ponovo pokreni OBS.\"\nScriptLogWindow=\"Zapisnik\"\nDescription=\"Opis\"\nScriptDescriptionLink.Text=\"Želite li otvoriti poveznicu u zadanom pregledniku?\"\nScriptDescriptionLink.Text.Url=\"Adresa: %1\"\nScriptDescriptionLink.OpenURL=\"Otvori adresu\"\nFileFilter.ScriptFiles=\"Skripte\"\nFileFilter.AllFiles=\"Sve datoteke\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/hu-HU.ini",
    "content": "SceneSwitcher=\"Automatikus jelenetváltó\"\nSceneSwitcher.OnNoMatch=\"Ha nem található megfelelő ablak:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Ne váltson\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Váltson:\"\nSceneSwitcher.CheckInterval=\"Aktív ablak ellenőrzése ennyi időközönként:\"\nSceneSwitcher.ActiveOrNotActive=\"Jelenet váltó:\"\nInvalidRegex.Title=\"Érvénytelen kifejezés\"\nInvalidRegex.Text=\"A megadott kifejezés érvénytelen.\"\nActive=\"Aktív\"\nInactive=\"Inaktiv\"\nCaptions=\"Feliratok (Kísérleti)\"\nCaptions.AudioSource=\"Audio forrás\"\nCaptions.CurrentSystemLanguage=\"Rendszer aktuális nyelve (%1)\"\nCaptions.Provider=\"Szolgáltató\"\nCaptions.Error.GenericFail=\"Felirat indítása sikertelen\"\nOutputTimer=\"Kimeneti időzítő\"\nOutputTimer.Stream=\"Közvetítés leállítása:\"\nOutputTimer.Record=\"Felvétel leállítása:\"\nOutputTimer.Stream.StoppingIn=\"A közvetítés leáll:\"\nOutputTimer.Record.StoppingIn=\"Felvétel leáll:\"\nOutputTimer.Stream.EnableEverytime=\"A közvetítés időzítőjének elindítása minden alkalommal\"\nOutputTimer.Record.EnableEverytime=\"Felvétel időzítő indítása minden alkalommal\"\nOutputTimer.Record.PauseTimer=\"Idő megállítása, ha a felvétel is megáll\"\nScripts=\"Szkriptek\"\nLoadedScripts=\"Betöltött szkriptek\"\nAddScripts=\"Szkriptek hozzáadása\"\nRemoveScripts=\"Szkriptek eltávolítása\"\nReloadScripts=\"Szkriptek újraindítása\"\nEditScript=\"Parancsfájl szerkesztése\"\nReload=\"Újratöltés\"\nOpenFileLocation=\"Fájl helyének megnyitása\"\nPythonSettings=\"Python beállítások\"\nPythonSettings.PythonInstallPath32bit=\"Python telepítési útvonal (32 bites)\"\nPythonSettings.PythonInstallPath64bit=\"Python telepítési útvonal (64 bites)\"\nPythonSettings.BrowsePythonPath=\"Python elérési útjának tallózása\"\nPythonSettings.PythonVersion=\"Betöltött Python verzió: %1\"\nPythonSettings.PythonNotLoaded=\"A Python jelenleg nincs betöltve\"\nPythonSettings.AlreadyLoaded.Title=\"A Python már be lett töltve\"\nPythonSettings.AlreadyLoaded.Message=\"A Python %1 egy másolata már be lett töltve. Hogy betöltse az újonnan kiválasztott Python verziót, indítsa újra az OBS-t.\"\nScriptLogWindow=\"Parancsfájlnapló\"\nDescription=\"Leírás\"\nScriptDescriptionLink.Text=\"Meg akarod nyitni ezt a linket az alapérelmezett böngésződben?\"\nScriptDescriptionLink.OpenURL=\"URL Megnyitása\"\nFileFilter.ScriptFiles=\"Szkriptfájl\"\nFileFilter.AllFiles=\"Minden fájl\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/hy-AM.ini",
    "content": "SceneSwitcher=\"Տեսարանի ավտոմատ անջատիչ\"\nSceneSwitcher.OnNoMatch=\"Երբ ոչ մի պատուհան չի տեղավորվում:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Մի անցել\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Անցնել:\"\nSceneSwitcher.CheckInterval=\"Ստուգեք ակտիվ պատուհանի անունը ամեն անգամ՝\"\nSceneSwitcher.ActiveOrNotActive=\"Տեսարանի փոխարկիչ:\"\nInvalidRegex.Title=\"Անվավեր կանոնավոր արտահայտություն\"\nInvalidRegex.Text=\"Ձեր մուտքագրած կանոնավոր արտահայտությունը պարունակում է սխալներ:\"\nActive=\"Ակտիվ է\"\nInactive=\"Ոչ ակտիվ\"\nStart=\"Սկսել\"\nStop=\"Կանգնեցնել\"\nCaptions=\"Ենթագրեր (փորձնական)\"\nCaptions.AudioSource=\"Աուդիո աղբյուր\"\nCaptions.CurrentSystemLanguage=\"Ընթացիկ համակարգի լեզուն (%1)\"\nCaptions.Provider=\"Մատակարար\"\nCaptions.Error.GenericFail=\"Չհաջողվեց գործարկել ենթագրերը\"\nOutputTimer=\"Տեսագրման և հոսքի ժամանակաչափ\"\nOutputTimer.Stream=\"Ավարտել հեռարձակումը հետևյալում:\"\nOutputTimer.Record=\"Ավարտել տեսագրումը հետևյալում:\"\nOutputTimer.Stream.StoppingIn=\"Հոսքը կավարտվի հետևյալով.\"\nOutputTimer.Record.StoppingIn=\"Տեսագրումը կավարտվի հետևյալով.\"\nOutputTimer.Stream.EnableEverytime=\"Ամեն անգամ միացրեք հեռարձակման ժամանակաչափը\"\nOutputTimer.Record.EnableEverytime=\"Ամեն անգամ միացրեք տեսագրման ժամանակաչափը\"\nOutputTimer.Record.PauseTimer=\"Դադարեցնել ժամաչափը, երբ տեսագրությունը դադարեցված է\"\nScripts=\"Սցենարներ\"\nLoadedScripts=\"Բեռնված սցենարներ\"\nAddScripts=\"Ավելացնել սցենարներ\"\nRemoveScripts=\"Հեռացնել սցենարներ\"\nReloadScripts=\"Վերբեռնել սցենարները\"\nEditScript=\"Խմգագրել սցենարը\"\nReload=\"Վերբեռնել\"\nOpenFileLocation=\"Բացել ֆայլի վայրը\"\nPythonSettings=\"Python-ի կարգավորումներ\"\nPythonSettings.PythonInstallPath32bit=\"Python Տեղադրման ուղին (32 բիթ)\"\nPythonSettings.PythonInstallPath64bit=\"Python Տեղադրման ուղին (64 բիթ)\"\nPythonSettings.BrowsePythonPath=\"Թերթել Python ուղին\"\nPythonSettings.PythonVersion=\"Բեռնված Python-ի տարբերակը․ %1\"\nPythonSettings.PythonNotLoaded=\"Python-ը դեռ բեռնված չէ\"\nPythonSettings.AlreadyLoaded.Title=\"Python-ը արդեն բեռնվել է\"\nPythonSettings.AlreadyLoaded.Message=\"Python-ի %1 տարբերակը արդեն բեռնվել է։ Նորը բեռնելու համար՝ ընտրեք Python-ի տարբերակը, վերամեկնարկեք OBS-ը։\"\nScriptLogWindow=\"Սցենարի մատյան\"\nDescription=\"Նկարագրություն\"\nScriptDescriptionLink.Text=\"Բացե՞լ այս հղումը կանխադրված դիտարկիչում:\"\nScriptDescriptionLink.Text.Url=\"URL-հասցեն: %1\"\nScriptDescriptionLink.OpenURL=\"Բացել URL-ը\"\nFileFilter.ScriptFiles=\"Սցենարի ֆայլերը\"\nFileFilter.AllFiles=\"Բոլոր ֆայլերը\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/id-ID.ini",
    "content": "SceneSwitcher=\"Pengalih Adegan Otomatis\"\nSceneSwitcher.OnNoMatch=\"Ketika tidak ada jendela yang cocok:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Jangan alihkan\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Alihkan ke:\"\nSceneSwitcher.CheckInterval=\"Periksa judul jendela aktif setiap:\"\nSceneSwitcher.ActiveOrNotActive=\"Pengalih Adegan sedang:\"\nInvalidRegex.Title=\"Ekspresi Reguler Tidak Sah\"\nInvalidRegex.Text=\"Ekspresi reguler yang Anda masukkan tidak sah.\"\nActive=\"Aktif\"\nInactive=\"Tidak Aktif\"\nStart=\"Mulai\"\nStop=\"Hentikan\"\nCaptions=\"Closed Caption (CC) (Eksperimental)\"\nCaptions.AudioSource=\"Sumber audio\"\nCaptions.CurrentSystemLanguage=\"Bahasa Sistem Sekarang (%1)\"\nCaptions.Provider=\"Penyedia\"\nCaptions.Error.GenericFail=\"Gagal memulai caption\"\nOutputTimer=\"Timer Output\"\nOutputTimer.Stream=\"Berhenti streaming setelah:\"\nOutputTimer.Record=\"Berhenti merekam setelah:\"\nOutputTimer.Stream.StoppingIn=\"Streaming berhenti dalam:\"\nOutputTimer.Record.StoppingIn=\"Perekaman berhenti dalam:\"\nOutputTimer.Stream.EnableEverytime=\"Aktifkan timer streaming setiap saat\"\nOutputTimer.Record.EnableEverytime=\"Aktifkan timer rekaman setiap saat\"\nOutputTimer.Record.PauseTimer=\"Jeda timer saat rekaman dijeda\"\nScripts=\"Skrip\"\nLoadedScripts=\"Skrip yang Dimuat\"\nAddScripts=\"Tambahkan Script\"\nRemoveScripts=\"Hapus Skrip\"\nReloadScripts=\"Muat Ulang Skrip\"\nEditScript=\"Ubah Skrip\"\nReload=\"Muat ulang\"\nOpenFileLocation=\"Buka lokasi berkas\"\nPythonSettings=\"Setelan Python\"\nPythonSettings.PythonInstallPath32bit=\"Jalur Pemasangan Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Jalur Pemasangan Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"Telusuri Jalur Folder Python\"\nPythonSettings.PythonVersion=\"Termuat Versi Python: %1\"\nPythonSettings.PythonNotLoaded=\"Saat ini Python tidak termuat\"\nPythonSettings.AlreadyLoaded.Title=\"Python Sudah Di Muat\"\nPythonSettings.AlreadyLoaded.Message=\"Salinan Python %1 sudah di muat. Untuk memuat versi Python baru yang dipilih, mohon aktifkan ulang OBS.\"\nScriptLogWindow=\"Catatan Skrip\"\nDescription=\"Deskripsi\"\nScriptDescriptionLink.Text=\"Buka tautan ini di peramban web bawaan Anda?\"\nScriptDescriptionLink.OpenURL=\"Buka URL\"\nFileFilter.ScriptFiles=\"Berkas Skrip\"\nFileFilter.AllFiles=\"Semua Berkas\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/is-IS.ini",
    "content": "SceneSwitcher=\"Skjáskiptir\"\nSceneSwitcher.OnNoMatch=\"SkjáskiptirPassaEkki\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/it-IT.ini",
    "content": "SceneSwitcher=\"Cambio scena automatico\"\nSceneSwitcher.OnNoMatch=\"Quando nessuna finestra coincide:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Non cambiare\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Passa a:\"\nSceneSwitcher.CheckInterval=\"Controlla il titolo della finestra attiva ogni:\"\nSceneSwitcher.ActiveOrNotActive=\"Il cambio di scena è:\"\nInvalidRegex.Title=\"Espressione regolare non valida\"\nInvalidRegex.Text=\"L'espressione regolare che hai inserito non è valida.\"\nActive=\"Attivo\"\nInactive=\"Inattivo\"\nStart=\"Avvia\"\nCaptions=\"Sottotitoli (sperimentale)\"\nCaptions.AudioSource=\"Fonte dell'audio\"\nCaptions.CurrentSystemLanguage=\"Lingua del sistema corrente (%1)\"\nCaptions.Provider=\"Fornitore\"\nCaptions.Error.GenericFail=\"Avvio dei sottotitoli non riuscito\"\nOutputTimer=\"Conto alla rovescia\"\nOutputTimer.Stream=\"Termina la diretta dopo:\"\nOutputTimer.Record=\"Termina la registrazione dopo:\"\nOutputTimer.Stream.StoppingIn=\"La diretta terminerà tra:\"\nOutputTimer.Record.StoppingIn=\"La registrazione terminerà tra:\"\nOutputTimer.Stream.EnableEverytime=\"Attiva il conto alla rovescia per le dirette ogni volta\"\nOutputTimer.Record.EnableEverytime=\"Attiva il conto alla rovescia per le registrazioni ogni volta\"\nOutputTimer.Record.PauseTimer=\"Ferma il timer quando la registrazione è in pausa\"\nScripts=\"Script\"\nLoadedScripts=\"Script caricati\"\nAddScripts=\"Aggiungi script\"\nRemoveScripts=\"Rimuovi script\"\nReloadScripts=\"Ricarica gli script\"\nEditScript=\"Modifica Script\"\nReload=\"Ricarica\"\nOpenFileLocation=\"Apri Posizione File\"\nPythonSettings=\"Impostazioni di Python\"\nPythonSettings.PythonInstallPath32bit=\"Percorso d'installazione di Python (32 bit)\"\nPythonSettings.PythonInstallPath64bit=\"Percorso d'installazione di Python (64 bit)\"\nPythonSettings.BrowsePythonPath=\"Sfoglia Percorso Python\"\nPythonSettings.PythonVersion=\"Versione Python caricata: %1\"\nPythonSettings.PythonNotLoaded=\"Python non attualmente caricato\"\nPythonSettings.AlreadyLoaded.Title=\"Python già caricato\"\nPythonSettings.AlreadyLoaded.Message=\"Una copia di Python %1 è già stata caricata. Per caricare la nuova versione Python selezionata, riavvia OBS.\"\nScriptLogWindow=\"Log degli script\"\nDescription=\"Descrizione\"\nScriptDescriptionLink.Text=\"Vuoi aprire questo link con il tuo browser predefinito?\"\nScriptDescriptionLink.OpenURL=\"Apri URL\"\nFileFilter.ScriptFiles=\"Lista degli script\"\nFileFilter.AllFiles=\"Tutti i file\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ja-JP.ini",
    "content": "SceneSwitcher=\"自動シーンスイッチャー\"\nSceneSwitcher.OnNoMatch=\"どのウィンドウにも一致しない場合:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"切り替えない\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"切り替える:\"\nSceneSwitcher.CheckInterval=\"アクティブウィンドウタイトルを確認する間隔:\"\nSceneSwitcher.ActiveOrNotActive=\"シーンスイッチャーは:\"\nInvalidRegex.Title=\"無効な正規表現\"\nInvalidRegex.Text=\"入力した正規表現は有効ではありません。\"\nActive=\"アクティブ\"\nInactive=\"非アクティブ\"\nStart=\"開始\"\nStop=\"停止\"\nCaptions=\"字幕 (実験的)\"\nCaptions.AudioSource=\"音声ソース\"\nCaptions.CurrentSystemLanguage=\"現在のシステム言語 (%1)\"\nCaptions.Provider=\"プロバイダ\"\nCaptions.Error.GenericFail=\"字幕の開始に失敗しました\"\nOutputTimer=\"出力タイマー\"\nOutputTimer.Stream=\"配信停止の時間設定:\"\nOutputTimer.Record=\"録画停止の時間設定:\"\nOutputTimer.Stream.StoppingIn=\"配信停止まで:\"\nOutputTimer.Record.StoppingIn=\"録画停止まで:\"\nOutputTimer.Stream.EnableEverytime=\"毎回配信タイマーを有効にする\"\nOutputTimer.Record.EnableEverytime=\"毎回録画タイマーを有効にする\"\nOutputTimer.Record.PauseTimer=\"録画が一時停止したときにタイマーを一時停止する\"\nScripts=\"スクリプト\"\nLoadedScripts=\"ロードしたスクリプト\"\nAddScripts=\"スクリプトを追加\"\nRemoveScripts=\"スクリプトを削除\"\nReloadScripts=\"スクリプトの再読み込み\"\nEditScript=\"スクリプトを編集\"\nReload=\"再読み込み\"\nOpenFileLocation=\"ファイルの場所を開く\"\nPythonSettings=\"Python の設定\"\nPythonSettings.PythonInstallPath32bit=\"Python インストールパス (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python インストールパス (64bit)\"\nPythonSettings.BrowsePythonPath=\"Python パスを参照\"\nPythonSettings.PythonVersion=\"ロードされた Python バージョン: %1\"\nPythonSettings.PythonNotLoaded=\"Python は現在ロードされていません\"\nPythonSettings.AlreadyLoaded.Title=\"Pythonは既にロードされています\"\nPythonSettings.AlreadyLoaded.Message=\"Python %1 のコピーは既にロードされています。新しく選択された Python のバージョンをロードするには、OBSを再起動してください。\"\nScriptLogWindow=\"スクリプトログ\"\nDescription=\"説明\"\nScriptDescriptionLink.Text=\"このリンクを既定のウェブブラウザで開きますか？\"\nScriptDescriptionLink.OpenURL=\"URLを開く\"\nFileFilter.ScriptFiles=\"スクリプトファイル\"\nFileFilter.AllFiles=\"すべてのファイル\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ka-GE.ini",
    "content": "SceneSwitcher=\"სცენის თვითგადამრთველი\"\nSceneSwitcher.OnNoMatch=\"როცა ყველა ფანჯარა შეუსაბამოა:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"არ გადაირთოს\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"გადაირთოს:\"\nSceneSwitcher.CheckInterval=\"მოქმედი ფანჯრის სათაურის შემოწმების დრო:\"\nSceneSwitcher.ActiveOrNotActive=\"სცენის თვითგადამრთველი:\"\nInvalidRegex.Title=\"არამართებული რეგულარული გამოსახულება\"\nInvalidRegex.Text=\"შეყვანილი რეგულარული გამოსახულება არასწორია.\"\nActive=\"ჩართული\"\nInactive=\"გამორთული\"\nStart=\"დაწყება\"\nStop=\"შეწყვეტა\"\nCaptions=\"წარწერები (საცდელი)\"\nCaptions.AudioSource=\"აუდიოს წყარო\"\nCaptions.CurrentSystemLanguage=\"სისტემის მიმდინარე ენა (%1)\"\nCaptions.Provider=\"მომწოდებელი\"\nCaptions.Error.GenericFail=\"წარწერების დადება ვერ მოხერხდა\"\nOutputTimer=\"ჩაწერის და ნაკადის წამმზომი\"\nOutputTimer.Stream=\"ნაკადი გაეშვას არაუმეტეს:\"\nOutputTimer.Record=\"ჩაწერა გაგრძელდეს არაუმეტეს:\"\nOutputTimer.Stream.StoppingIn=\"ნაკადის შეწყვეტის დრო:\"\nOutputTimer.Record.StoppingIn=\"ჩაწერის შეწყვეტის დრო:\"\nOutputTimer.Stream.EnableEverytime=\"ნაკადის წამმზომის ჩართვა ყოველ ჯერზე\"\nOutputTimer.Record.EnableEverytime=\"ჩაწერის წამმზომის ჩართვა ყოველ ჯერზე\"\nOutputTimer.Record.PauseTimer=\"წამმზომის შეჩერება ჩაწერის შეჩერებისას\"\nScripts=\"სკრიპტები\"\nLoadedScripts=\"ჩატვირთული სკრიპტები\"\nAddScripts=\"სკრიპტების დამატება\"\nRemoveScripts=\"სკრიპტების მოცილება\"\nReloadScripts=\"სკრიპტების გადატვირთვა\"\nEditScript=\"სკრიპტის ჩასწორება\"\nReload=\"განახლება\"\nOpenFileLocation=\"ადგილსამყოფელი\"\nPythonSettings=\"Python-ის პარამეტრები\"\nPythonSettings.PythonInstallPath32bit=\"Python-ის დასაყენებელი მდებარეობა (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python-ის დასაყენებელი მდებარეობა (64bit)\"\nPythonSettings.BrowsePythonPath=\"Python-ის მდებარეობის მოძიება\"\nPythonSettings.PythonVersion=\"ჩატვირთული ვერსიის Python: %1\"\nPythonSettings.PythonNotLoaded=\"Python ამჟამად არაა ჩატვირთული\"\nPythonSettings.AlreadyLoaded.Title=\"Python უკვე ჩატვირთულია\"\nPythonSettings.AlreadyLoaded.Message=\"ერთი Python %1 უკვე ჩატვირთულია. ახლად შერჩეული ვერსიის Python რომ ჩაიტვირთოს, გთხოვთ ხელახლა გაუშვათ OBS.\"\nScriptLogWindow=\"სკრიპტის აღრიცხვა\"\nDescription=\"აღწერილობა\"\nScriptDescriptionLink.Text=\"გაიხსნას ბმული ნაგულისხმევ ბრაუზერში?\"\nScriptDescriptionLink.Text.Url=\"ბმული: %1\"\nScriptDescriptionLink.OpenURL=\"ბმულის გახსნა\"\nFileFilter.ScriptFiles=\"სკრიპტის ფაილები\"\nFileFilter.AllFiles=\"ყველა ფაილი\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/kaa.ini",
    "content": "SceneSwitcher=\"Avtomat túrde kórinisti ózgertiw\"\nSceneSwitcher.OnNoMatch=\"Hesh bir ayna sáykes kelmese:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Almastırmaw\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Almastırıw:\"\nSceneSwitcher.CheckInterval=\"Aktiv ayna temasın tekseriń: hár ... ta\"\nSceneSwitcher.ActiveOrNotActive=\"Kórinis Almastırģısh:\"\nActive=\"Aktiv\"\nInactive=\"Aktiv emes\"\nStart=\"Iske túsiriw\"\nStop=\"Toqtatıw\"\nCaptions.AudioSource=\"Audio deregi\"\nCaptions.CurrentSystemLanguage=\"Ámeldegi sistema tili (%1)\"\nCaptions.Error.GenericFail=\"Subtitrlardı iske túsiriw ámelge aspadı\"\nScripts=\"Skriptler\"\nLoadedScripts=\"Júklengen skriptler\"\nAddScripts=\"Skriptlerdi qosıw\"\nRemoveScripts=\"Skriptlerdi óshiriw\"\nReloadScripts=\"Skriptlerdi qayta júklew\"\nEditScript=\"Skriptti ózgertiw\"\nReload=\"Qayta júklew\"\nOpenFileLocation=\"Fayl jaylasıw ornı\"\nPythonSettings=\"Python sazlawları\"\nPythonSettings.PythonInstallPath32bit=\"Python ornatıw jolı (32 bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python ornatıw jolı (64 bit)\"\nPythonSettings.BrowsePythonPath=\"Python jolın kórsetiw\"\nPythonSettings.PythonVersion=\"Júklengen Python versiyası: %1\"\nPythonSettings.PythonNotLoaded=\"Python házirgi waqıtta júklenbegen\"\nPythonSettings.AlreadyLoaded.Title=\"Python álleqashan júklengen\"\nScriptLogWindow=\"Skript jurnalı\"\nDescription=\"Táriypleniwi\"\nScriptDescriptionLink.Text.Url=\"URL mánzil: %1\"\nScriptDescriptionLink.OpenURL=\"URL mánzildi ashıw\"\nFileFilter.ScriptFiles=\"Skript faylları\"\nFileFilter.AllFiles=\"Barlıq fayllar\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/kab-KAB.ini",
    "content": "SceneSwitcher=\"Asenfal awurman n usayes\"\nSceneSwitcher.OnNoMatch=\"Ma ulac asfaylu inmeɣran:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Ur senfal ara\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Senfel ɣer:\"\nSceneSwitcher.CheckInterval=\"Selken azwel n usfaylu urmid yal:\"\nSceneSwitcher.ActiveOrNotActive=\"Asenfal n usayes:\"\nInvalidRegex.Title=\"Tanfalit talugant d tarmeɣtut\"\nInvalidRegex.Text=\"Tanfalit talugant i tsekcmeḍ d tarmeɣtut.\"\nActive=\"Yermed\"\nInactive=\"Yensa\"\nStart=\"Sekker\"\nStop=\"Seḥbes\"\nCaptions=\"Iduzwilen (d tarmit)\"\nCaptions.AudioSource=\"Aɣbalu n umeslaw\"\nCaptions.CurrentSystemLanguage=\"Tutlayt tamirant n unagraw (%1)\"\nCaptions.Provider=\"Aseǧǧaw\"\nCaptions.Error.GenericFail=\"Asekker n twaṭṭfiwin ur yeddi ara\"\nOutputTimer=\"Amakud n tuffɣa\"\nOutputTimer.Stream=\"Ḥbes asuddem mbaɛd:\"\nOutputTimer.Record=\"Ḥbes asekles mbaɛd:\"\nOutputTimer.Stream.StoppingIn=\"Asuddem ad iḥbes di:\"\nOutputTimer.Record.StoppingIn=\"Asekles ad iḥbes di:\"\nOutputTimer.Stream.EnableEverytime=\"Sermed amakud n usuddem yal tikkelt\"\nOutputTimer.Record.EnableEverytime=\"Sermed amakud n usekles yal tikkelt\"\nOutputTimer.Record.PauseTimer=\"Ḥbes amakud ticki yeḥbes usekles\"\nScripts=\"Iskripten\"\nLoadedScripts=\"Iskripten yulin\"\nAddScripts=\"Rnu iskripten\"\nRemoveScripts=\"Kkes iskripten\"\nReloadScripts=\"Ales asali n yiskripten\"\nReload=\"Ales asali\"\nOpenFileLocation=\"Ldi adig n ufaylu\"\nPythonSettings=\"Iɣewwaren n Python\"\nPythonSettings.PythonInstallPath32bit=\"Abrid n usbeddi n Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Abrid n usbeddi n Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"Snirem abrid n Python\"\nScriptLogWindow=\"Aɣmis n uskript\"\nDescription=\"Aglam\"\nScriptDescriptionLink.Text=\"Ad teldiḍ aseɣwen-agi deg yiminig n web inek m n lexṣas?\"\nScriptDescriptionLink.OpenURL=\"Ldi URL\"\nFileFilter.ScriptFiles=\"Ifuyla n uskript\"\nFileFilter.AllFiles=\"Akk ifuyla\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/kmr-TR.ini",
    "content": "SceneSwitcher=\"Guherkerê dîmenê bi xweberî\"\nSceneSwitcher.OnNoMatch=\"Gava ku çarçove li hev nayên:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Neguhêre\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Biguhêre bo\"\nSceneSwitcher.CheckInterval=\"Sernavê çarçoveya çalak kontrol bike bo her:\"\nSceneSwitcher.ActiveOrNotActive=\"Guherkera dîmen ev e:\"\nInvalidRegex.Title=\"Şirovekirina bi rêkûpêk a nederbasdar\"\nInvalidRegex.Text=\"Ragihandina bi rêkûpêk a ku te nivsandiye nederbasdar e.\"\nActive=\"Çalak e\"\nInactive=\"Neçalak e\"\nStart=\"Dest pê bike\"\nStop=\"Bi dawî bike\"\nCaptions=\"Jêrnivîs (Ceribandî)\"\nCaptions.AudioSource=\"Çavkaniya dengê\"\nCaptions.CurrentSystemLanguage=\"Zimanê pergala heyî (%1)\"\nCaptions.Provider=\"Dabînker\"\nCaptions.Error.GenericFail=\"Destpêkirina jêrnivîsê biserneket\"\nOutputTimer=\"Demkera deranê\"\nOutputTimer.Stream=\"Weşanê bi dawî bike piştî:\"\nOutputTimer.Record=\"Tomarkirinê bi dawî bike piştî:\"\nOutputTimer.Stream.StoppingIn=\"Weşan bi dawî dibe di:\"\nOutputTimer.Record.StoppingIn=\"Tomarkirin bi dawî dibe di:\"\nOutputTimer.Stream.EnableEverytime=\"Her dem demkera weşanê çalak bike\"\nOutputTimer.Record.EnableEverytime=\"Her dem demkera tomarkirinê çalak bike\"\nOutputTimer.Record.PauseTimer=\"Demker rawestîne gava tomarkirin hate rawestan\"\nScripts=\"Skrîpt\"\nLoadedScripts=\"Skrîptên tomarkirî\"\nAddScripts=\"Skrîptan tevlî bike\"\nRemoveScripts=\"Skrîptan rake\"\nReloadScripts=\"Skrîptan ji nû ve bar bike\"\nEditScript=\"Skrîpt serrast bike\"\nReload=\"Ji nû ve bar bike\"\nOpenFileLocation=\"Cihê pelê veke\"\nPythonSettings=\"Sazkariyên Python\"\nPythonSettings.PythonInstallPath32bit=\"Riya sazkarina Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Riya sazkarina Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"Riya sazkarina Python bigere\"\nPythonSettings.PythonVersion=\"Guhertoya Python a barkirî: %1\"\nPythonSettings.PythonNotLoaded=\"Python di dema heyî de nehatiye barkirin\"\nPythonSettings.AlreadyLoaded.Title=\"Python jixwe barkirî ye\"\nPythonSettings.AlreadyLoaded.Message=\"Jêgirtinek ji Python %1 jixwe hatiye barkirin. Ji bo barkirina ya guhertoya Python nû ya hilbijartî, jkx OBS ji nû ve bide destpêkirin\"\nScriptLogWindow=\"Têketinên Skrîpt\"\nDescription=\"Danasîn\"\nScriptDescriptionLink.Text=\"Vê girêdanê di geroka tevnê xwe ya berdest de veke?\"\nScriptDescriptionLink.OpenURL=\"URL veke\"\nFileFilter.ScriptFiles=\"Pelên Skrîpt\"\nFileFilter.AllFiles=\"Hemû pel\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ko-KR.ini",
    "content": "SceneSwitcher=\"자동 장면 전환기\"\nSceneSwitcher.OnNoMatch=\"일치하는 윈도우가 없는 경우:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"전환하지 않음\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"여기로 전환:\"\nSceneSwitcher.CheckInterval=\"활성화된 윈도우 제목을 확인:\"\nSceneSwitcher.ActiveOrNotActive=\"장면 전환기:\"\nInvalidRegex.Title=\"유효하지 않은 정규식\"\nInvalidRegex.Text=\"입력한 정규식이 유효하지 않습니다.\"\nActive=\"활성화\"\nInactive=\"비활성화\"\nStart=\"시작\"\nStop=\"중단\"\nCaptions=\"자막 (실험적)\"\nCaptions.AudioSource=\"오디오 소스\"\nCaptions.CurrentSystemLanguage=\"현재 시스템 언어 (%1)\"\nCaptions.Provider=\"공급자\"\nCaptions.Error.GenericFail=\"자막을 시작하지 못했습니다\"\nOutputTimer=\"출력 시간 설정\"\nOutputTimer.Stream=\"이 시간 이후 방송 중단:\"\nOutputTimer.Record=\"이 시간 이후 녹화 중단:\"\nOutputTimer.Stream.StoppingIn=\"방송 중지까지 남은 시간:\"\nOutputTimer.Record.StoppingIn=\"녹화 중지까지 남은 시간:\"\nOutputTimer.Stream.EnableEverytime=\"항상 스트리밍 타이머 활성화\"\nOutputTimer.Record.EnableEverytime=\"항상 녹화 타이머 활성화\"\nOutputTimer.Record.PauseTimer=\"녹화가 중지되었을 때 타이머 일시 정지\"\nScripts=\"스크립트\"\nLoadedScripts=\"적용된 스크립트\"\nAddScripts=\"스크립트 추가\"\nRemoveScripts=\"스크립트 제거\"\nReloadScripts=\"스크립트 다시 적용\"\nEditScript=\"스크립트 수정\"\nReload=\"재실행\"\nOpenFileLocation=\"파일 위치 열기\"\nPythonSettings=\"Python 설정\"\nPythonSettings.PythonInstallPath32bit=\"Python 설치 경로 (32비트)\"\nPythonSettings.PythonInstallPath64bit=\"Python 설치 경로 (64비트)\"\nPythonSettings.BrowsePythonPath=\"Python 경로 찾기\"\nPythonSettings.PythonVersion=\"로드된 파이썬 버전: %1\"\nPythonSettings.PythonNotLoaded=\"파이썬이 로드되지 않았습니다\"\nPythonSettings.AlreadyLoaded.Title=\"파이썬이 이미 로드되었습니다\"\nPythonSettings.AlreadyLoaded.Message=\"이미 %1개의 파이썬이 로드되었습니다. 새로 선택된 파이썬 버전을 로드하시려면 OBS를 다시 시작해주세요.\"\nScriptLogWindow=\"스크립트 로그\"\nDescription=\"설명\"\nScriptDescriptionLink.Text=\"이 링크를 기본 웹 브라우저에서 열까요?\"\nScriptDescriptionLink.OpenURL=\"URL 열기\"\nFileFilter.ScriptFiles=\"스크립트 파일\"\nFileFilter.AllFiles=\"모든 파일\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/lo-LA.ini",
    "content": "SceneSwitcher=\"ປຸ່ມປ່ຽນສາກອັດຕະໂນມັດ\"\nSceneSwitcher.OnNoMatch=\"ເມື່ອບໍ່ມີປ່ອງຢ້ຽມທີ່ກົງກັນ:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"ບໍ່ຕ້ອງປ່ຽນ\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"ປ່ຽນໄປຍັງ:\"\nSceneSwitcher.CheckInterval=\"ກວດສອບຊື່ປ່ອງຢ້ຽມທີ່ໃຊ້ງານຢູ່ທຸກໆ:\"\nSceneSwitcher.ActiveOrNotActive=\"ສະຖານະຕົວປ່ຽນສາກ:\"\nInvalidRegex.Title=\"Regular Expression ບໍ່ຖືກຕ້ອງ\"\nInvalidRegex.Text=\"Regular Expression ທີ່ທ່ານປ້ອນບໍ່ຖືກຕ້ອງ.\"\nActive=\"ເປີດໃຊ້ງານ\"\nInactive=\"ປິດໃຊ້ງານ\"\nStart=\"ເລີ່ມ\"\nStop=\"ຢຸດ\"\nCaptions=\"ຄຳບັນຍາຍ (ທົດລອງ)\"\nCaptions.AudioSource=\"ສຽງຂາເຂົ້າ\"\nCaptions.CurrentSystemLanguage=\"ພາສາຂອງລະບົບປະຈຸບັນ (%1)\"\nCaptions.Provider=\"ຜູ້ໃຫ້ບໍລິການ\"\nCaptions.Error.GenericFail=\"ລົ້ມເຫຼວໃນການເລີ່ມຄຳບັນຍາຍ\"\nOutputTimer=\"ຕົວຕັ້ງເວລາການສົ່ງອອກ\"\nOutputTimer.Stream=\"ຢຸດການຖ່າຍທອດສົດຫຼັງຈາກ:\"\nOutputTimer.Record=\"ຢຸດການບັນທຶກຫຼັງຈາກ:\"\nOutputTimer.Stream.StoppingIn=\"ການຖ່າຍທອດສົດຈະຢຸດໃນ:\"\nOutputTimer.Record.StoppingIn=\"ການບັນທຶກຈະຢຸດໃນ:\"\nOutputTimer.Stream.EnableEverytime=\"ເປີດໃຊ້ຕົວຕັ້ງເວລາຖ່າຍທອດສົດທຸກຄັ້ງ\"\nOutputTimer.Record.EnableEverytime=\"ເປີດໃຊ້ຕົວຕັ້ງເວລາບັນທຶກທຸກຄັ້ງ\"\nOutputTimer.Record.PauseTimer=\"ຢຸດເວລາຊົ່ວຄາວ ເມື່ອການບັນທຶກຖືກຢຸດຊົ່ວຄາວ\"\nScripts=\"ສະຄຣິບ\"\nLoadedScripts=\"ສະຄຣິບທີ່ໂຫຼດແລ້ວ\"\nAddScripts=\"ເພີ່ມສະຄຣິບ\"\nRemoveScripts=\"ລຶບສະຄຣິບ\"\nReloadScripts=\"ໂຫຼດສະຄຣິບຄືນໃໝ່\"\nEditScript=\"ແກ້ໄຂສະຄຣິບ\"\nReload=\"ໂຫຼດຄືນໃໝ່\"\nOpenFileLocation=\"ເປີດບ່ອນເກັບໄຟລ໌\"\nPythonSettings=\"ການຕັ້ງຄ່າ Python\"\nPythonSettings.PythonInstallPath32bit=\"ເສັ້ນທາງຕິດຕັ້ງ Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"ເສັ້ນທາງຕິດຕັ້ງ Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"ເລືອກເສັ້ນທາງ Python\"\nPythonSettings.PythonVersion=\"ເວີຊັນ Python ທີ່ໂຫຼດ: %1\"\nPythonSettings.PythonNotLoaded=\"ຍັງບໍ່ທັນໂຫຼດ Python\"\nPythonSettings.AlreadyLoaded.Title=\"Python ຖືກໂຫຼດແລ້ວ\"\nPythonSettings.AlreadyLoaded.Message=\"Python %1 ຖືກໂຫຼດຢູ່ແລ້ວ. ເພື່ອໂຫຼດ Python ເວີຊັນໃໝ່ທີ່ເລືອກ, ກະລຸນາປິດແລ້ວເປີດ OBS ໃໝ່.\"\nScriptLogWindow=\"ບັນທຶກການທຳງານສະຄຣິບ\"\nDescription=\"ຄຳອະທິບາຍ\"\nScriptDescriptionLink.Text=\"ເປີດລິ້ງນີ້ໃນເວັບບຣາວເຊີເລີ່ມຕົ້ນຂອງທ່ານບໍ່?\"\nScriptDescriptionLink.OpenURL=\"ເປີດ URL\"\nFileFilter.ScriptFiles=\"ໄຟລ໌ສະຄຣິບ\"\nFileFilter.AllFiles=\"ທຸກໄຟລ໌\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/lt-LT.ini",
    "content": "SceneSwitcher=\"Automatinis scenos perjungiklis\"\nSceneSwitcher.OnNoMatch=\"Kai langai nesutampa:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Neperjungti\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Perjungti į:\"\nSceneSwitcher.CheckInterval=\"Tikrinti aktyvaus lango antraštę kas:\"\nSceneSwitcher.ActiveOrNotActive=\"Scenų keitimas yra:\"\nInvalidRegex.Title=\"Neteisinga Reguliarioji Išraiška\"\nInvalidRegex.Text=\"Reguliarioji išraiška, kurią įvedėte - neteisinga.\"\nActive=\"Aktyvus\"\nInactive=\"Neaktyvus\"\nStart=\"Įjungti\"\nStop=\"Išjungti\"\nCaptions=\"Titrai (Eksperimentinis)\"\nCaptions.AudioSource=\"Garso šaltinis\"\nCaptions.CurrentSystemLanguage=\"Dabartinė sistemos kalba (%1)\"\nCaptions.Provider=\"Iš\"\nCaptions.Error.GenericFail=\"Nepavyko įjungti titrų\"\nOutputTimer=\"Išvesties automatinis laikmatis\"\nOutputTimer.Stream=\"Sustabdyti transliaciją po:\"\nOutputTimer.Record=\"Stabdyti įrašymą po:\"\nOutputTimer.Stream.StoppingIn=\"Transliacija sustos po:\"\nOutputTimer.Record.StoppingIn=\"Įrašymas sustos po:\"\nOutputTimer.Stream.EnableEverytime=\"Įgalinti transliacijos laikmatį kiekvieną kartą\"\nOutputTimer.Record.EnableEverytime=\"Įgalinti įrašymo laikmatį kiekvieną kartą\"\nOutputTimer.Record.PauseTimer=\"Pristabdykite laikmatį, kai įrašymas pristabdytas\"\nScripts=\"Skriptai\"\nLoadedScripts=\"Pakrauti Skriptai\"\nAddScripts=\"Pridėti skriptai\"\nRemoveScripts=\"Pašalinkite skriptai\"\nReloadScripts=\"Iš naujo paleisti skriptus\"\nEditScript=\"Taisyti scenarijų\"\nReload=\"Perkrauti\"\nOpenFileLocation=\"Atidaryti failo vietą\"\nPythonSettings=\"„Python“ nustatymai\"\nPythonSettings.PythonInstallPath32bit=\"„Python“ diegimo kelias (32 bitų)\"\nPythonSettings.PythonInstallPath64bit=\"„Python“ diegimo kelias (32 bitų)\"\nPythonSettings.BrowsePythonPath=\"Naršykite „Python“ kelią\"\nScriptLogWindow=\"Scenarijaus žurnalas\"\nDescription=\"apibūdinimas\"\nScriptDescriptionLink.Text=\"Atidaryti šią nuorodą numatytojoje žiniatinklio naršyklėje?\"\nScriptDescriptionLink.OpenURL=\"Atidaryti URL\"\nFileFilter.ScriptFiles=\"skripto failus\"\nFileFilter.AllFiles=\"Pridėti failus\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/lv-LV.ini",
    "content": "SceneSwitcher=\"Automātisks Ainu Pārslēdzējs\"\nSceneSwitcher.OnNoMatch=\"Kad neviens logs neatbilst:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Nepārslēgties\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Pārslēgties uz:\"\nSceneSwitcher.CheckInterval=\"Pārbaudīt aktīvā loga nosaukumu ik pēc:\"\nSceneSwitcher.ActiveOrNotActive=\"Ainu Pārslēdzējs ir:\"\nInvalidRegex.Title=\"Nederīga Regulārā Izteiksme\"\nInvalidRegex.Text=\"Ievadītā regulārā izteiksme nav derīga.\"\nActive=\"Aktīvs\"\nInactive=\"Neaktīvs\"\nStart=\"Sākt\"\nStop=\"Apturēt\"\nCaptions=\"Subtitri (Eksperimentāli)\"\nCaptions.AudioSource=\"Audio avots\"\nCaptions.CurrentSystemLanguage=\"Pašreizējā Sistēmas Valoda (%1)\"\nCaptions.Provider=\"Provaiders\"\nCaptions.Error.GenericFail=\"Neizdevās sākt subtitrus\"\nOutputTimer=\"Izvades Taimeris\"\nOutputTimer.Stream=\"Pārtraukt straumēšanu pēc:\"\nOutputTimer.Record=\"Pārtraukt ierakstīšanu pēc:\"\nOutputTimer.Stream.StoppingIn=\"Straumēšana apstājas pēc:\"\nOutputTimer.Record.StoppingIn=\"Ierakstīšana apstājas pēc:\"\nOutputTimer.Stream.EnableEverytime=\"Iespējot straumēšanas taimeri katru reizi\"\nOutputTimer.Record.EnableEverytime=\"Iespējot ierakstīšanas taimeri katru reizi\"\nOutputTimer.Record.PauseTimer=\"Apstādināt taimeri, kad ierakstīšana ir apturēta\"\nScripts=\"Skripti\"\nLoadedScripts=\"Ielādētie Skripti\"\nAddScripts=\"Pievienot Skriptus\"\nRemoveScripts=\"Noņemt Skriptus\"\nReloadScripts=\"Atkārtoti ielādēt Skriptus\"\nEditScript=\"Rediģēt Skriptu\"\nReload=\"Pārlādēt\"\nOpenFileLocation=\"Atvērt faila atrašanās vietu\"\nPythonSettings=\"Python Iestatījumi\"\nPythonSettings.PythonInstallPath32bit=\"Python Instalēšanas Ceļš (32 bitu)\"\nPythonSettings.PythonInstallPath64bit=\"Python Instalēšanas Ceļš (64 bitu)\"\nPythonSettings.BrowsePythonPath=\"Pārlūkot Python Ceļu\"\nPythonSettings.PythonVersion=\"Ielādēta Python Versija: %1\"\nPythonSettings.PythonNotLoaded=\"Python pašlaik nav ielādēts\"\nPythonSettings.AlreadyLoaded.Title=\"Python jau ir ielādēts\"\nPythonSettings.AlreadyLoaded.Message=\"Python %1 kopija jau ir ielādēta. Lai ielādētu jaunizveidoto Python versiju, lūdzu, restartējiet OBS.\"\nScriptLogWindow=\"Skripta Žurnāls\"\nDescription=\"Apraksts\"\nScriptDescriptionLink.Text=\"Atvērt šo saiti noklusējuma tīmekļa pārlūkā?\"\nScriptDescriptionLink.OpenURL=\"Atvērt URL\"\nFileFilter.ScriptFiles=\"Skriptu Faili\"\nFileFilter.AllFiles=\"Visi Faili\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/mn-MN.ini",
    "content": "SceneSwitcher.OnNoMatch.DontSwitch=\"Бүү соль\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Үүнрүү шилжүүл:\"\nActive=\"Идэвхтэй\"\nInactive=\"Идэвхгүй\"\nStart=\"Эхлэх\"\nStop=\"Зогсоох\"\nCaptions.AudioSource=\"Аудио эх сурвалж\"\nCaptions.CurrentSystemLanguage=\"Одоогийн системийн хэл (%1)\"\nCaptions.Provider=\"Үйлчилгээ үзүүлэгч\"\nOutputTimer=\"Гаралтын цагийн тохируулга\"\nOutputTimer.Stream=\"Цацалтыг үүний дараа зогсоох:\"\nOutputTimer.Record=\"Бичлэгийг үүний дараа зогсоох:\"\nOutputTimer.Stream.StoppingIn=\"Урсгалыг зогсооход:\"\nOutputTimer.Record.StoppingIn=\"Бичлэгийг зогсооход:\"\nScripts=\"Скриптүүд\"\nLoadedScripts=\"Уншигдсан скриптүүд\"\nAddScripts=\"Скрипт нэмэх\"\nRemoveScripts=\"Скрипт хасах\"\nReloadScripts=\"Скрипт дахин унших\"\nEditScript=\"Скрипт өөрчлөх\"\nOpenFileLocation=\"Файлын байршлыг нээх\"\nPythonSettings=\"Python тохиргоо\"\nPythonSettings.PythonInstallPath32bit=\"Python суусан байрлал (32 бит)\"\nPythonSettings.PythonInstallPath64bit=\"Python суусан байрлал (64 бит)\"\nPythonSettings.BrowsePythonPath=\"Python-ы замыг заах\"\nPythonSettings.PythonNotLoaded=\"Python-ийг ачааллаагүй байна\"\nPythonSettings.AlreadyLoaded.Title=\"Python-ийг аль хэдийн ачаалласан байна\"\nPythonSettings.AlreadyLoaded.Message=\"Python %1-ийг аль хэдийн ачаалласан байна. Шинэ Python хувилбарыг ачаалахын тулд OBS-г дахин эхлүүлнэ үү.\"\nScriptLogWindow=\"Скриптийн тэмдэглэл\"\nDescription=\"Тайлбар\"\nScriptDescriptionLink.Text=\"Энэ линкийг үндсэн веб хөтөчөөр нээх үү?\"\nScriptDescriptionLink.OpenURL=\"URL нээх\"\nFileFilter.ScriptFiles=\"Скрипт файлууд\"\nFileFilter.AllFiles=\"Бүх файлууд\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ms-MY.ini",
    "content": "SceneSwitcher=\"Penukar Adegan Automatik\"\nSceneSwitcher.OnNoMatch=\"Bila tiada padanan tetingkap:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Jangan ubah\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Ubah kepada:\"\nSceneSwitcher.CheckInterval=\"Periksa tajuk tetingkap aktif setiap:\"\nSceneSwitcher.ActiveOrNotActive=\"Penukar Adegan ialah:\"\nInvalidRegex.Title=\"Ungkapan Nalar Tidak Sah\"\nInvalidRegex.Text=\"Ungkapan nalar yang anda masukkan tidak sah.\"\nActive=\"Aktif\"\nInactive=\"Tidak Aktif\"\nStart=\"Mula\"\nStop=\"Henti\"\nCaptions=\"Kapsyen (Eksperimental)\"\nCaptions.AudioSource=\"Sumber audio\"\nCaptions.CurrentSystemLanguage=\"Bahasa Sistem Semasa (%1)\"\nCaptions.Provider=\"Penyedia\"\nCaptions.Error.GenericFail=\"Gagal memulakan kapsyen\"\nOutputTimer=\"Pemasa Output\"\nOutputTimer.Stream=\"Berhenti menstrim selepas\"\nOutputTimer.Record=\"Berhenti merakam selepas:\"\nOutputTimer.Stream.StoppingIn=\"Penstriman berhenti dalam tempoh:\"\nOutputTimer.Record.StoppingIn=\"Rakaman berhenti dalam tempoh:\"\nOutputTimer.Stream.EnableEverytime=\"Benarkan pemasa penstriman setiap masa\"\nOutputTimer.Record.EnableEverytime=\"Benarkan pemasa rakaman setiap masa\"\nOutputTimer.Record.PauseTimer=\"Jeda pemasa ketika rakaman dijeda\"\nScripts=\"Skrip\"\nLoadedScripts=\"Muat Skrip\"\nAddScripts=\"Tambah Skrip\"\nRemoveScripts=\"Buang Skrip\"\nReloadScripts=\"Muat Semula Skrip\"\nEditScript=\"Sunting Skrip\"\nReload=\"Mula Semula\"\nOpenFileLocation=\"Buka lokasi fail\"\nPythonSettings=\"Tetapan Python\"\nPythonSettings.PythonInstallPath32bit=\"Laluan Pemasangan Python (32 bit)\"\nPythonSettings.PythonInstallPath64bit=\"Laluan Pemasangan Python (64 bit)\"\nPythonSettings.BrowsePythonPath=\"Layar Laluan Python\"\nPythonSettings.PythonVersion=\"Versi Python Yang Dimuatkan: %1\"\nPythonSettings.PythonNotLoaded=\"Python masih belum dimuatkan\"\nPythonSettings.AlreadyLoaded.Title=\"Python Telah Sedia Dimuatkan\"\nPythonSettings.AlreadyLoaded.Message=\"Salinan Python %1 telah sedia ada dimuatkan. Untuk memuatkan pilihan versi baharu Python, sila hidupkan kembali OBS.\"\nScriptLogWindow=\"Log Skrip\"\nDescription=\"Keterangan\"\nScriptDescriptionLink.Text=\"Buka pautan ini dalam pelayar sesawang lalai anda?\"\nScriptDescriptionLink.OpenURL=\"Buka URL\"\nFileFilter.ScriptFiles=\"Fail Skrip\"\nFileFilter.AllFiles=\"Semua Fail\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/nb-NO.ini",
    "content": "SceneSwitcher=\"Automatisk sceneskifter\"\nSceneSwitcher.OnNoMatch=\"Når ingen vindu passer overens:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Ikke bytt\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Bytt til:\"\nSceneSwitcher.CheckInterval=\"Sjekk aktivt vindu hver:\"\nSceneSwitcher.ActiveOrNotActive=\"Sceneskifter er:\"\nInvalidRegex.Title=\"Ugyldig regulært utrykk\"\nInvalidRegex.Text=\"Det regulære utrykket du har angitt er ugyldig.\"\nActive=\"Aktiv\"\nInactive=\"Inaktiv\"\nStop=\"Stopp\"\nCaptions=\"Bildetekster (eksperimentell)\"\nCaptions.AudioSource=\"Lyd kilde\"\nCaptions.CurrentSystemLanguage=\"Någjeldende System Språk (%1)\"\nCaptions.Provider=\"Leverandør\"\nCaptions.Error.GenericFail=\"Feilet å starte undertekst\"\nOutputTimer=\"Stoppeklokke\"\nOutputTimer.Stream=\"Stopp streaming etter:\"\nOutputTimer.Record=\"Stopp opptak etter:\"\nOutputTimer.Stream.StoppingIn=\"Streaming stopper om:\"\nOutputTimer.Record.StoppingIn=\"Opptak stopper om:\"\nOutputTimer.Stream.EnableEverytime=\"Aktiver strømmeklokken hver gang\"\nOutputTimer.Record.EnableEverytime=\"Aktiver opptaksklokken hver gang\"\nOutputTimer.Record.PauseTimer=\"Sett stoppeklokken på pause når innspilling er pauset\"\nScripts=\"Skripter\"\nLoadedScripts=\"Innlastede skripter\"\nAddScripts=\"Legg til skripter\"\nRemoveScripts=\"Fjern skripter\"\nReloadScripts=\"Last inn skripter på nytt\"\nEditScript=\"Rediger skript\"\nReload=\"Oppdater\"\nOpenFileLocation=\"Åpne filplassering\"\nPythonSettings=\"Python-innstillinger\"\nPythonSettings.PythonInstallPath32bit=\"Python-installasjonsfilbane (32-bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python-installasjonsfilbane (64-bit)\"\nPythonSettings.BrowsePythonPath=\"Finn Python-filbanen\"\nPythonSettings.PythonNotLoaded=\"Python er ikke lastet inn for øyeblikket\"\nPythonSettings.AlreadyLoaded.Title=\"Python er allerede lastet inn\"\nScriptLogWindow=\"Skripthistorikk\"\nDescription=\"Beskrivelse\"\nScriptDescriptionLink.Text=\"Åpne denne lenken i standard nettleser?\"\nScriptDescriptionLink.OpenURL=\"Åpne URL\"\nFileFilter.ScriptFiles=\"Skriptfiler\"\nFileFilter.AllFiles=\"Alle filer\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/nl-NL.ini",
    "content": "SceneSwitcher=\"Automatische Scènewisselaar\"\nSceneSwitcher.OnNoMatch=\"Als geen venster overeenkomt:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Niet verwisselen\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Wissel naar:\"\nSceneSwitcher.CheckInterval=\"Controleer actieve venstertitel elke:\"\nSceneSwitcher.ActiveOrNotActive=\"Scènewisselaar is:\"\nInvalidRegex.Title=\"Ongeldige Reguliere Expressie\"\nInvalidRegex.Text=\"De ingevoerde reguliere expressie is ongeldig.\"\nActive=\"Actief\"\nInactive=\"Inactief\"\nCaptions=\"Ondertiteling (Experimenteel)\"\nCaptions.AudioSource=\"Audiobron\"\nCaptions.CurrentSystemLanguage=\"Huidige Systeemtaal (%1)\"\nCaptions.Error.GenericFail=\"Kon de ondertitelingen niet starten\"\nOutputTimer=\"Uitvoertimer\"\nOutputTimer.Stream=\"Stop met streamen na:\"\nOutputTimer.Record=\"Stop met opnemen na:\"\nOutputTimer.Stream.StoppingIn=\"Stream stopt over:\"\nOutputTimer.Record.StoppingIn=\"Opname stopt over:\"\nOutputTimer.Stream.EnableEverytime=\"Schakel streaming timer elke keer in\"\nOutputTimer.Record.EnableEverytime=\"Schakel opnametimer elke keer in\"\nOutputTimer.Record.PauseTimer=\"Pauzeer timer tijdens het pauzeren van de opname\"\nLoadedScripts=\"Geladen Scripts\"\nAddScripts=\"Voeg Scripts Toe\"\nRemoveScripts=\"Verwijder Scripts\"\nReloadScripts=\"Herlaad Scripts\"\nEditScript=\"Script bewerken\"\nReload=\"Herladen\"\nOpenFileLocation=\"Open bestandslocatie\"\nPythonSettings=\"Python-instellingen\"\nPythonSettings.PythonInstallPath32bit=\"Python Installatiepad (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python Installatiepad (64bit)\"\nPythonSettings.BrowsePythonPath=\"Blader Naar Python Pad\"\nPythonSettings.PythonVersion=\"Geladen Python versie: %1\"\nPythonSettings.PythonNotLoaded=\"Python momenteel niet geladen\"\nPythonSettings.AlreadyLoaded.Title=\"Python al geladen\"\nPythonSettings.AlreadyLoaded.Message=\"Er is al een kopie van Python %1 geladen. Herstart OBS om de nieuw geselecteerde Python versie te laden.\"\nDescription=\"Beschrijving\"\nScriptDescriptionLink.Text=\"Deze link in uw standaard webbrowser openen?\"\nFileFilter.ScriptFiles=\"Script Bestanden\"\nFileFilter.AllFiles=\"Alle Bestanden\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/nn-NO.ini",
    "content": "SceneSwitcher.OnNoMatch.DontSwitch=\"Ikkje byt\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Byt til:\"\nActive=\"Aktiv\"\nInactive=\"Inaktiv\"\nStop=\"Stopp\"\nCaptions.AudioSource=\"Lydkjelde\"\nCaptions.Provider=\"Tilbydar\"\nEditScript=\"Rediger skript\"\nFileFilter.AllFiles=\"Alle filer\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/oc-FR.ini",
    "content": "Captions.AudioSource=\"Font àudio\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/pl-PL.ini",
    "content": "SceneSwitcher=\"Automatyczne przełączanie scen\"\nSceneSwitcher.OnNoMatch=\"Gdy nie pasuje żadne okno:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Nie przełączaj\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Przełącz na:\"\nSceneSwitcher.CheckInterval=\"Sprawdź tytuł aktywnego okna co:\"\nSceneSwitcher.ActiveOrNotActive=\"Przełączanie scen jest:\"\nInvalidRegex.Title=\"Nieprawidłowe wyrażenie regularne\"\nInvalidRegex.Text=\"Podane wyrażenie regularne jest nieprawidłowe.\"\nActive=\"Aktywne\"\nInactive=\"Nieaktywne\"\nCaptions=\"Napisy (eksperymentalne)\"\nCaptions.AudioSource=\"Źródła dźwięku\"\nCaptions.CurrentSystemLanguage=\"Obecny język systemu (%1)\"\nCaptions.Provider=\"Silnik\"\nCaptions.Error.GenericFail=\"Uruchomienie napisów nie powiodło się\"\nOutputTimer=\"Wyłącznik czasowy\"\nOutputTimer.Stream=\"Zatrzymaj stream po:\"\nOutputTimer.Record=\"Zatrzymaj nagrywanie po:\"\nOutputTimer.Stream.StoppingIn=\"Zatrzymanie streamu za:\"\nOutputTimer.Record.StoppingIn=\"Zatrzymanie nagrywania za:\"\nOutputTimer.Stream.EnableEverytime=\"Włącz timer streamu za każdym razem\"\nOutputTimer.Record.EnableEverytime=\"Włącz timer nagrywania za każdym razem\"\nOutputTimer.Record.PauseTimer=\"Zatrzymaj czas, gdy nagrywanie jest zapauzowane\"\nScripts=\"Skrypty\"\nLoadedScripts=\"Wczytane skrypty\"\nAddScripts=\"Dodaj skrypty\"\nRemoveScripts=\"Usuń skrypty\"\nReloadScripts=\"Przeładuj skrypty\"\nEditScript=\"Edytuj skrypt\"\nReload=\"Przeładuj\"\nOpenFileLocation=\"Otwórz lokalizację pliku\"\nPythonSettings=\"Ustawienia Pythona\"\nPythonSettings.PythonInstallPath32bit=\"Ścieżka instalacji Pythona (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Ścieżka instalacji Pythona (64bit)\"\nPythonSettings.BrowsePythonPath=\"Wybierz ścieżkę instalacji Pythona\"\nPythonSettings.PythonVersion=\"Wczytana wersja Pythona: %1\"\nPythonSettings.PythonNotLoaded=\"Python nie jest obecnie wczytany\"\nPythonSettings.AlreadyLoaded.Title=\"Python jest już wczytany\"\nPythonSettings.AlreadyLoaded.Message=\"Kopia Pythona %1 jest już wczytana. Aby wczytać nowo wybraną wersję Pythona, uruchom ponownie OBS.\"\nScriptLogWindow=\"Dziennik skryptów\"\nDescription=\"Opis\"\nScriptDescriptionLink.Text=\"Otworzyć ten link w domyślnej przeglądarce internetowej?\"\nScriptDescriptionLink.OpenURL=\"Otwórz URL\"\nFileFilter.ScriptFiles=\"Pliki skryptów\"\nFileFilter.AllFiles=\"Wszystkie pliki\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/pt-BR.ini",
    "content": "SceneSwitcher=\"Alternador automático de cenas\"\nSceneSwitcher.OnNoMatch=\"Quando nenhuma janela corresponder:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Não alternar\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Alternar para:\"\nSceneSwitcher.CheckInterval=\"Checar o título da janela ativa a cada:\"\nSceneSwitcher.ActiveOrNotActive=\"O alternador de cenas está:\"\nInvalidRegex.Title=\"Expressão regular inválida\"\nInvalidRegex.Text=\"A expressão regular (regex) que você inseriu é inválida.\"\nActive=\"ligado\"\nInactive=\"desligado\"\nStart=\"Iniciar\"\nStop=\"Parar\"\nCaptions=\"Legendas (experimental)\"\nCaptions.AudioSource=\"Fonte de áudio\"\nCaptions.CurrentSystemLanguage=\"Idioma atual do sistema (%1)\"\nCaptions.Provider=\"Provedor\"\nCaptions.Error.GenericFail=\"Falha ao iniciar legendas\"\nOutputTimer=\"Temporizador de saída\"\nOutputTimer.Stream=\"Parar a transmissão após:\"\nOutputTimer.Record=\"Parar a gravação após:\"\nOutputTimer.Stream.StoppingIn=\"A transmissão irá parar em:\"\nOutputTimer.Record.StoppingIn=\"A gravação irá parar em:\"\nOutputTimer.Stream.EnableEverytime=\"Sempre ativar o temporizador de transmissão\"\nOutputTimer.Record.EnableEverytime=\"Sempre ativar o temporizador de transmissão\"\nOutputTimer.Record.PauseTimer=\"Pausar o temporizador quando a gravação é pausada\"\nLoadedScripts=\"Scripts carregados\"\nAddScripts=\"Adicionar scripts\"\nRemoveScripts=\"Remover scripts\"\nReloadScripts=\"Recarregar scripts\"\nEditScript=\"Editar script\"\nReload=\"Recarregar\"\nOpenFileLocation=\"Abrir local do arquivo\"\nPythonSettings=\"Configurações do Python\"\nPythonSettings.PythonInstallPath32bit=\"Caminho de instalação do Python (32 bits)\"\nPythonSettings.PythonInstallPath64bit=\"Caminho de instalação do Python (64 bits)\"\nPythonSettings.BrowsePythonPath=\"Procurar caminho do Python\"\nPythonSettings.PythonVersion=\"Versão carregada do Python: %1\"\nPythonSettings.PythonNotLoaded=\"O Python não está carregado\"\nPythonSettings.AlreadyLoaded.Title=\"O Python já foi carregado\"\nPythonSettings.AlreadyLoaded.Message=\"Uma cópia do Python %1 já foi carregada. Para carregar a versão recém-selecionada do Python, reinicie o OBS.\"\nScriptLogWindow=\"Log dos scripts\"\nDescription=\"Descrição\"\nScriptDescriptionLink.Text=\"Abrir este link no seu navegador padrão?\"\nScriptDescriptionLink.OpenURL=\"Abrir URL\"\nFileFilter.ScriptFiles=\"Arquivos de scripts\"\nFileFilter.AllFiles=\"Todos os arquivos\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/pt-PT.ini",
    "content": "SceneSwitcher=\"Alternador automático de cena\"\nSceneSwitcher.OnNoMatch=\"Quando nenhuma janela corresponder:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Não trocar\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Trocar para:\"\nSceneSwitcher.CheckInterval=\"Verificar título da janela ativa a cada:\"\nSceneSwitcher.ActiveOrNotActive=\"A troca de cenas está:\"\nInvalidRegex.Title=\"Expressão regular inválida\"\nInvalidRegex.Text=\"A expressão regular que inseriu é inválida.\"\nActive=\"Ativa\"\nInactive=\"Inativa\"\nStart=\"Iniciar\"\nStop=\"Parar\"\nCaptions=\"Legendas (experimental)\"\nCaptions.AudioSource=\"Fonte de áudio\"\nCaptions.CurrentSystemLanguage=\"Idioma atual do sistema (%1)\"\nCaptions.Provider=\"Fornecedor\"\nCaptions.Error.GenericFail=\"Falha ao iniciar legendas\"\nOutputTimer=\"Temporizador de saída\"\nOutputTimer.Stream=\"Parar transmissão após:\"\nOutputTimer.Record=\"Parar gravação após:\"\nOutputTimer.Stream.StoppingIn=\"Transmissão a parar em:\"\nOutputTimer.Record.StoppingIn=\"Gravação a parar em:\"\nOutputTimer.Stream.EnableEverytime=\"Ativar temporizador a cada transmissão\"\nOutputTimer.Record.EnableEverytime=\"Ativar sempre temporizador de gravação\"\nOutputTimer.Record.PauseTimer=\"Pausar relógio nas pausas de gravação\"\nLoadedScripts=\"Scripts carregados\"\nAddScripts=\"Adicionar scripts\"\nRemoveScripts=\"Remover scripts\"\nReloadScripts=\"Recarregar scripts\"\nEditScript=\"Editar script\"\nReload=\"Recarregar\"\nOpenFileLocation=\"Abrir localização do ficheiro\"\nPythonSettings=\"Definições do Python\"\nPythonSettings.PythonInstallPath32bit=\"Caminho da instalação do Python (32bits)\"\nPythonSettings.PythonInstallPath64bit=\"Caminho da instalação do Python (64bits)\"\nPythonSettings.BrowsePythonPath=\"Procurar caminho do Python\"\nPythonSettings.PythonVersion=\"Versão Python carregada: %1\"\nPythonSettings.PythonNotLoaded=\"O Python não está carregado\"\nPythonSettings.AlreadyLoaded.Title=\"Python já carregado\"\nPythonSettings.AlreadyLoaded.Message=\"Uma cópia do Python %1 já está carregada. Para carregar a versão selecionada do Python, por favor, reinicie o OBS.\"\nScriptLogWindow=\"Diário do script\"\nDescription=\"Descrição\"\nScriptDescriptionLink.Text=\"Abrir a ligação no navegador predefinido?\"\nScriptDescriptionLink.OpenURL=\"Abrir URL\"\nFileFilter.ScriptFiles=\"Ficheiros de script\"\nFileFilter.AllFiles=\"Todos os ficheiros\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ro-RO.ini",
    "content": "SceneSwitcher=\"Comutator de scene automat\"\nSceneSwitcher.OnNoMatch=\"Când nicio fereastră nu se potrivește:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Nu comuta\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Comută la:\"\nSceneSwitcher.CheckInterval=\"Verifică titlul ferestrei active la fiecare:\"\nSceneSwitcher.ActiveOrNotActive=\"Comutatorul de scene este:\"\nInvalidRegex.Title=\"Expresie regulată invalidă\"\nInvalidRegex.Text=\"Expresia regulată pe care ai introdus-o este nevalidă.\"\nActive=\"Activ\"\nInactive=\"Inactiv\"\nStart=\"Pornește\"\nStop=\"Oprește\"\nCaptions=\"Subtitrări (experimentale)\"\nCaptions.AudioSource=\"Sursă audio\"\nCaptions.CurrentSystemLanguage=\"Limba actuală a sistemului (%1)\"\nCaptions.Provider=\"Furnizor\"\nCaptions.Error.GenericFail=\"Eșec la pornirea subtitrărilor\"\nOutputTimer=\"Temporizator pentru output\"\nOutputTimer.Stream=\"Oprește streamingul după:\"\nOutputTimer.Record=\"Oprește înregistrarea după:\"\nOutputTimer.Stream.StoppingIn=\"Streamingul se oprește în:\"\nOutputTimer.Record.StoppingIn=\"Înregistrarea se oprește în:\"\nOutputTimer.Stream.EnableEverytime=\"Activează temporizatorul pentru streaming de fiecare dată\"\nOutputTimer.Record.EnableEverytime=\"Activează temporizatorul pentru înregistrare de fiecare dată\"\nOutputTimer.Record.PauseTimer=\"Pune pe pauză temporizatorul când înregistrarea este pusă pe pauză\"\nScripts=\"Scripturi\"\nLoadedScripts=\"Scripturi încărcate\"\nAddScripts=\"Adaugă scripturi\"\nRemoveScripts=\"Elimină scripturi\"\nReloadScripts=\"Reîncarcă scripturile\"\nEditScript=\"Editează scriptul\"\nReload=\"Reîncarcă\"\nOpenFileLocation=\"Deschide locația fișierului\"\nPythonSettings=\"Setări Python\"\nPythonSettings.PythonInstallPath32bit=\"Calea instalării Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Calea instalării Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"Răsfoiește calea Python\"\nPythonSettings.PythonVersion=\"Versiunea Python încărcată: %1\"\nPythonSettings.PythonNotLoaded=\"Python nu este încărcat\"\nPythonSettings.AlreadyLoaded.Title=\"Python deja încărcat\"\nPythonSettings.AlreadyLoaded.Message=\"O copie a lui Python %1 este deja încărcată. Pentru a încărca versiunea Python nou selectată, vă rugăm să reporniți OBS.\"\nScriptLogWindow=\"Jurnalul scripturilor\"\nDescription=\"Descriere\"\nScriptDescriptionLink.Text=\"Deschizi acest link în browserul web implicit?\"\nScriptDescriptionLink.OpenURL=\"Deschide URL-ul\"\nFileFilter.ScriptFiles=\"Fișiere de script\"\nFileFilter.AllFiles=\"Toate fișierele\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ru-RU.ini",
    "content": "SceneSwitcher=\"Автоматический переключатель сцен\"\nSceneSwitcher.OnNoMatch=\"Когда ни одно окно не подходит:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Не переключать\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Переключать на:\"\nSceneSwitcher.CheckInterval=\"Проверять имя активного окна каждые:\"\nSceneSwitcher.ActiveOrNotActive=\"Переключатель сцен:\"\nInvalidRegex.Title=\"Недопустимое регулярное выражение\"\nInvalidRegex.Text=\"Регулярное выражение, которое вы ввели, содержит ошибки.\"\nActive=\"Активен\"\nInactive=\"Неактивен\"\nStart=\"Запустить\"\nStop=\"Остановить\"\nCaptions=\"Субтитры (экспериментально)\"\nCaptions.AudioSource=\"Источник звука\"\nCaptions.CurrentSystemLanguage=\"Текущий язык системы (%1)\"\nCaptions.Provider=\"Поставщик\"\nCaptions.Error.GenericFail=\"Не удалось запустить субтитры\"\nOutputTimer=\"Таймер записи и трансляции\"\nOutputTimer.Stream=\"Остановить трансляцию через:\"\nOutputTimer.Record=\"Завершить запись через:\"\nOutputTimer.Stream.StoppingIn=\"Трансляция будет остановлена через:\"\nOutputTimer.Record.StoppingIn=\"Запись будет завершена через:\"\nOutputTimer.Stream.EnableEverytime=\"Включать таймер трансляции каждый раз\"\nOutputTimer.Record.EnableEverytime=\"Включать таймер записи каждый раз\"\nOutputTimer.Record.PauseTimer=\"Приостановить таймер при паузе записи\"\nScripts=\"Скрипты\"\nLoadedScripts=\"Загруженные скрипты\"\nAddScripts=\"Добавить скрипты\"\nRemoveScripts=\"Удалить скрипты\"\nReloadScripts=\"Перезагрузить скрипты\"\nEditScript=\"Править скрипт\"\nReload=\"Обновить\"\nOpenFileLocation=\"Открыть место расположения файла\"\nPythonSettings=\"Настройки Python\"\nPythonSettings.PythonInstallPath32bit=\"Путь установки Python (32 бита)\"\nPythonSettings.PythonInstallPath64bit=\"Путь установки Python (64 бита)\"\nPythonSettings.BrowsePythonPath=\"Обзор пути Python\"\nPythonSettings.PythonVersion=\"Загружена версия Python: %1\"\nPythonSettings.PythonNotLoaded=\"Python не загружен\"\nPythonSettings.AlreadyLoaded.Title=\"Python уже загружен\"\nPythonSettings.AlreadyLoaded.Message=\"Копия Python %1 уже загружена. Чтобы загрузить новую выбранную версию Python, перезапустите OBS.\"\nScriptLogWindow=\"Лог скрипта\"\nDescription=\"Описание\"\nScriptDescriptionLink.Text=\"Открыть эту ссылку в браузере по умолчанию?\"\nScriptDescriptionLink.Text.Url=\"Ссылка: %1\"\nScriptDescriptionLink.OpenURL=\"Открыть ссылку\"\nFileFilter.ScriptFiles=\"Файлы скриптов\"\nFileFilter.AllFiles=\"Все файлы\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/si-LK.ini",
    "content": "SceneSwitcher=\"ස්වයංක්‍රිය සිද්ධි මාරුව\"\nSceneSwitcher.OnNoMatch=\"කිසිදු කවුළුවක් නොගැළපෙන විට:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"මාරුවන්න එපා\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"වෙත මාරුවන්න:\"\nSceneSwitcher.CheckInterval=\"සක්‍රිය කවුළුවේ සිරැසිය යළි පරීක්‍ෂාව:\"\nSceneSwitcher.ActiveOrNotActive=\"සිද්ධි මාරුව:\"\nActive=\"ක්‍රියාත්මකයි\"\nInactive=\"ක්‍රියාත්මක නැත\"\nStart=\"අරඹන්න\"\nStop=\"නවත්වන්න\"\nCaptions.AudioSource=\"ශ්‍රව්‍ය මූලාශ්‍රය\"\nCaptions.CurrentSystemLanguage=\"වත්මන් පද්ධතියේ භාෂාව (%1)\"\nCaptions.Provider=\"සැපයුම්කරු\"\nOutputTimer=\"ප්‍රතිදාන මුහුර්තකය\"\nOutputTimer.Stream.StoppingIn=\"ප්‍රචාරය නැවතෙන්නේ:\"\nOutputTimer.Record.StoppingIn=\"පටිගතය නැවතෙන්නේ:\"\nScripts=\"අත්පත්\"\nLoadedScripts=\"පූරණය වූ අත්පත්\"\nAddScripts=\"අත්පත් යොදන්න\"\nRemoveScripts=\"අත්පත් ඉවතලන්න\"\nReloadScripts=\"අත්පත් යළි පූරණය\"\nEditScript=\"අත්පත සංස්කරණය\"\nReload=\"නැවත පූරණය\"\nOpenFileLocation=\"ගොනුවේ ස්ථානය විවෘත කරන්න\"\nPythonSettings=\"පයිතන් සැකසුම්\"\nPythonSettings.PythonInstallPath32bit=\"පයිතන් ස්ථාපන මාර්ගය (බිටු 32)\"\nPythonSettings.PythonInstallPath64bit=\"පයිතන් ස්ථාපන මාර්ගය (බිටු 64)\"\nPythonSettings.BrowsePythonPath=\"පයිතන් මාර්ගය පිරික්සන්න\"\nPythonSettings.PythonVersion=\"පූරණය වූ පයිතන් අනුවාදය: %1\"\nPythonSettings.PythonNotLoaded=\"දැනට පයිතන් පූරණය වී නැත\"\nPythonSettings.AlreadyLoaded.Title=\"පයිතන් පූරණය වී ඇත\"\nScriptLogWindow=\"අත්පත සටහන\"\nDescription=\"සවිස්තරය\"\nScriptDescriptionLink.Text.Url=\"ඒ.ස.නි.: %1\"\nScriptDescriptionLink.OpenURL=\"ඒ.ස.නි. විවෘත කරන්න\"\nFileFilter.ScriptFiles=\"අත්පත් ගොනු\"\nFileFilter.AllFiles=\"සියළුම ගොනු\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/sk-SK.ini",
    "content": "SceneSwitcher=\"Automatický prepínač scén\"\nSceneSwitcher.OnNoMatch=\"Ak sa žiadne okno nezhoduje:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Neprepínať\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Prepnúť na:\"\nSceneSwitcher.CheckInterval=\"Kontrolovať názov aktívneho okna každých:\"\nSceneSwitcher.ActiveOrNotActive=\"Prepínač scén je:\"\nInvalidRegex.Title=\"Neplatný regulárny výraz\"\nInvalidRegex.Text=\"Zadaný regulárny výraz je chybný.\"\nActive=\"Aktívny\"\nInactive=\"Neaktívny\"\nStart=\"Spustiť\"\nStop=\"Zastaviť\"\nCaptions=\"Titulky (experiment.)\"\nCaptions.AudioSource=\"Zdroj zvuku\"\nCaptions.CurrentSystemLanguage=\"Aktuálny systémový jazyk (%1)\"\nCaptions.Provider=\"Poskytovateľ\"\nCaptions.Error.GenericFail=\"Nepodarilo sa spustiť titulky\"\nOutputTimer=\"Časovač\"\nOutputTimer.Stream=\"Zastaviť stream po:\"\nOutputTimer.Record=\"Zastaviť nahrávanie po:\"\nOutputTimer.Stream.StoppingIn=\"Streamovanie za zastaví za:\"\nOutputTimer.Record.StoppingIn=\"Nahrávanie za zastaví za:\"\nOutputTimer.Stream.EnableEverytime=\"Povoliť časovač streamovania zakaždým\"\nOutputTimer.Record.EnableEverytime=\"Zapnúť časovač nahrávania zakaždým\"\nOutputTimer.Record.PauseTimer=\"Pozastaviť časovač keď je pozastavené nahrávanie\"\nScripts=\"Skripty\"\nLoadedScripts=\"Načítané skripty\"\nAddScripts=\"Pridať skripty\"\nRemoveScripts=\"Odstrániť skripty\"\nReloadScripts=\"Znovu-načítať skripty\"\nEditScript=\"Upraviť skript\"\nReload=\"Znovu načítať\"\nOpenFileLocation=\"Otvoriť umiestnenie súboru\"\nPythonSettings=\"Python nastavenia\"\nPythonSettings.PythonInstallPath32bit=\"Python inštalačná cesta (32bitová)\"\nPythonSettings.PythonInstallPath64bit=\"Python inštalačná cesta (64bitová)\"\nPythonSettings.BrowsePythonPath=\"Prehliadať cestu k Pythonu\"\nPythonSettings.PythonVersion=\"Načítaná Python verzia: %1\"\nPythonSettings.PythonNotLoaded=\"Python nie je momentálne načítaný\"\nPythonSettings.AlreadyLoaded.Title=\"Python je už načítaný\"\nPythonSettings.AlreadyLoaded.Message=\"Nejaká kópia Python-a %1 je už momentálne načítaná. Pre načítanie novo-vybratej verzie Python-a, prosím reštartujte OBS.\"\nScriptLogWindow=\"Log skriptu\"\nDescription=\"Popis\"\nScriptDescriptionLink.Text=\"Otvoriť tento odkaz v predvolenom webovom prehliadači?\"\nScriptDescriptionLink.OpenURL=\"Otvoriť URL\"\nFileFilter.ScriptFiles=\"Súbory skriptu\"\nFileFilter.AllFiles=\"Všetky súbory\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/sl-SI.ini",
    "content": "SceneSwitcher=\"Samodejno preklapljanje prizorov\"\nSceneSwitcher.OnNoMatch=\"Ko se nobeno okno ne ujema:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Ne preklopi\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Preklopi na:\"\nSceneSwitcher.CheckInterval=\"Preveri naziv aktivnega okna vsakih:\"\nSceneSwitcher.ActiveOrNotActive=\"Preklopnik prizorov je:\"\nInvalidRegex.Title=\"Neveljaven regularni izraz\"\nInvalidRegex.Text=\"Vneseni regularni izraz ni veljaven.\"\nActive=\"Dejaven\"\nInactive=\"Nedejaven\"\nStart=\"Začni\"\nStop=\"Ustavi\"\nCaptions=\"Napisi (preizkusno)\"\nCaptions.AudioSource=\"Zvočni vir\"\nCaptions.CurrentSystemLanguage=\"Trenutni jezik sistema (%1)\"\nCaptions.Provider=\"Ponudnik\"\nCaptions.Error.GenericFail=\"Zagon napisov je spodletel\"\nOutputTimer=\"Odštevalnik izhoda\"\nOutputTimer.Stream=\"Prenehaj pretakati po:\"\nOutputTimer.Record=\"Prenehaj snemati po:\"\nOutputTimer.Stream.StoppingIn=\"Pretok se bo ustavil čez:\"\nOutputTimer.Record.StoppingIn=\"Snemanje se bo ustavilo čez:\"\nOutputTimer.Stream.EnableEverytime=\"Vsakič omogoči odštevalnik pretakanja\"\nOutputTimer.Record.EnableEverytime=\"Vsakič omogoči odštevalnik snemanja\"\nOutputTimer.Record.PauseTimer=\"Premor odštevalnika ob premoru snemanja\"\nScripts=\"Skripti\"\nLoadedScripts=\"Naloženi skripti\"\nAddScripts=\"Dodaj skripte\"\nRemoveScripts=\"Odstrani skripte\"\nReloadScripts=\"Ponovno naloži skripte\"\nEditScript=\"Urejanje skripta\"\nReload=\"Ponovno naloži\"\nOpenFileLocation=\"Odpri lokacijo datoteke\"\nPythonSettings=\"Nastavitve Python\"\nPythonSettings.PythonInstallPath32bit=\"Namestitvena pot Pythona (32-bitni)\"\nPythonSettings.PythonInstallPath64bit=\"Namestitvena pot Pythona (64-bitni)\"\nPythonSettings.BrowsePythonPath=\"Prebrskaj pot do Pythona\"\nPythonSettings.PythonVersion=\"Naložena različica Pythona: %1\"\nPythonSettings.PythonNotLoaded=\"Python trenutno ni naložen\"\nPythonSettings.AlreadyLoaded.Title=\"Python je že naložen\"\nPythonSettings.AlreadyLoaded.Message=\"Kopija Pythona %1 je že naložena. Za nalaganje novo izbrane različice Pythona je potrebno ponovno zagnati OBS.\"\nScriptLogWindow=\"Dnevnik skriptov\"\nDescription=\"Opis\"\nScriptDescriptionLink.Text=\"Odprem to povezavi v tvojem privzetem brskalniku?\"\nScriptDescriptionLink.OpenURL=\"Odpri URL\"\nFileFilter.ScriptFiles=\"Datoteke skriptov\"\nFileFilter.AllFiles=\"Vse datoteke\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/sq-AL.ini",
    "content": "Active=\"Aktiv\"\nStart=\"Fillo\"\nStop=\"Ndalo\"\nOutputTimer.Record=\"Ndalo rregjistrimin pas:\"\nOutputTimer.Record.StoppingIn=\"Rregjistrimi ndalon në:\"\nAddScripts=\"Shto Scripts\"\nOpenFileLocation=\"Hap vendodhjen e skedarit\"\nDescription=\"Përshkrimi\"\nScriptDescriptionLink.OpenURL=\"Hap URL\"\nFileFilter.ScriptFiles=\"Skedarët Script\"\nFileFilter.AllFiles=\"Të gjithë skedarët\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/sr-CS.ini",
    "content": "SceneSwitcher=\"Automatski menjač scena\"\nSceneSwitcher.OnNoMatch=\"Kada se nijedan naslov prozora ne poklapa:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Ne menjaj\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Promeni na:\"\nSceneSwitcher.CheckInterval=\"Proveravaj naziv aktivnog prozora svakih:\"\nSceneSwitcher.ActiveOrNotActive=\"Menjač scena je:\"\nInvalidRegex.Title=\"Neispravan regularni izraz\"\nInvalidRegex.Text=\"Regularni izraz koji ste uneli nije ispravan.\"\nActive=\"Aktivan\"\nInactive=\"Neaktivan\"\nStart=\"Pokreni\"\nStop=\"Zaustavi\"\nCaptions=\"Natpisi (Eksperimentalno)\"\nCaptions.AudioSource=\"Izvor zvuka\"\nCaptions.CurrentSystemLanguage=\"Trenutni jezik sistema (%1)\"\nCaptions.Provider=\"Provajder\"\nCaptions.Error.GenericFail=\"Nemogućnost prikazivanja natpisa\"\nOutputTimer=\"Tempomat snimanja i emitovanja\"\nOutputTimer.Stream=\"Zaustavi emitovanje nakon:\"\nOutputTimer.Record=\"Zaustavi snimanje nakon:\"\nOutputTimer.Stream.StoppingIn=\"Prekidanje emitovanja za:\"\nOutputTimer.Record.StoppingIn=\"Prekidanje snimanja za:\"\nOutputTimer.Stream.EnableEverytime=\"Uključi tajmer strimovanja svaki put\"\nOutputTimer.Record.EnableEverytime=\"Uključi tajmer snimanja svaki put\"\nOutputTimer.Record.PauseTimer=\"Pauzirajte tajmer kada se pauzira snimanje\"\nScripts=\"Skripte\"\nLoadedScripts=\"Učitane skripte\"\nAddScripts=\"Dodaj skripte\"\nRemoveScripts=\"Izbaci skripte\"\nReloadScripts=\"Ponovo učitaj skripte\"\nEditScript=\"Uredi skriptu\"\nReload=\"Osveži\"\nOpenFileLocation=\"Otvori lokaciju fajla\"\nPythonSettings=\"Python podešavanja\"\nPythonSettings.PythonInstallPath32bit=\"Putanja do foldera sa Python instalacijom (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Putanja do foldera sa Python instalacijom (64bit)\"\nPythonSettings.BrowsePythonPath=\"Pretraži putanju do Python foldera\"\nScriptLogWindow=\"Prijava skripte\"\nDescription=\"Opis\"\nScriptDescriptionLink.Text=\"Otvorite ovaj link u vašem podrazumevanom internter pretraživaču?\"\nScriptDescriptionLink.OpenURL=\"Otvori URL\"\nFileFilter.ScriptFiles=\"Fajlovi skripte\"\nFileFilter.AllFiles=\"Svi fajlovi\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/sr-SP.ini",
    "content": "SceneSwitcher=\"Аутоматски мењач сцена\"\nSceneSwitcher.OnNoMatch=\"Када се ниједан наслов прозора не поклапа:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Не мењај\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Промени на:\"\nSceneSwitcher.CheckInterval=\"Проверавај назив активног прозора сваких:\"\nSceneSwitcher.ActiveOrNotActive=\"Мењач сцена је:\"\nInvalidRegex.Title=\"Неисправан регуларни израз\"\nInvalidRegex.Text=\"Регуларни израз који сте унели није исправан.\"\nActive=\"Активан\"\nInactive=\"Неактиван\"\nStart=\"Покрени\"\nStop=\"Заустави\"\nCaptions=\"Натписи (експериментално)\"\nCaptions.AudioSource=\"Извор звука\"\nCaptions.CurrentSystemLanguage=\"Тренутни језик система (%1)\"\nCaptions.Provider=\"Добављач\"\nCaptions.Error.GenericFail=\"Немогућност приказивања натписа\"\nOutputTimer=\"Темпомат снимања и емитовања\"\nOutputTimer.Stream=\"Заустави емитовање након:\"\nOutputTimer.Record=\"Заустави снимање након:\"\nOutputTimer.Stream.StoppingIn=\"Прекидање емитовања за:\"\nOutputTimer.Record.StoppingIn=\"Прекидање снимања за:\"\nOutputTimer.Stream.EnableEverytime=\"Укључи тајмер стримовања сваки пут\"\nOutputTimer.Record.EnableEverytime=\"Укључи тајмер снимања сваки пут\"\nOutputTimer.Record.PauseTimer=\"Паузирај тајмер када је снимање паузирано\"\nScripts=\"Скрипте\"\nLoadedScripts=\"Учитане скрипте\"\nAddScripts=\"Додај скрипте\"\nRemoveScripts=\"Избаци скрипте\"\nReloadScripts=\"Поново учитај скрипте\"\nEditScript=\"Измени скрипту\"\nReload=\"Поново учитај\"\nOpenFileLocation=\"Отвори локацију датотеке\"\nPythonSettings=\"Python подешавања\"\nPythonSettings.PythonInstallPath32bit=\"Путања до фолдера са Python инсталацијом (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Путања до фолдера са Python инсталацијом (64bit)\"\nPythonSettings.BrowsePythonPath=\"Претражи путању до Python фолдера\"\nPythonSettings.PythonVersion=\"Учитана верзија Пајтона: %1\"\nPythonSettings.PythonNotLoaded=\"Пајтон тренутно није учитан\"\nPythonSettings.AlreadyLoaded.Title=\"Пајтон је већ учитан\"\nPythonSettings.AlreadyLoaded.Message=\"Копија Пајтона %1 је већ учитана. Да бисте учитали новоизабрану верзију Пајтона, поново покрените OBS.\"\nScriptLogWindow=\"Дневник скрипти\"\nDescription=\"Опис\"\nScriptDescriptionLink.Text=\"Отворити ову везу у твом подразумеваном веб прегледачу?\"\nScriptDescriptionLink.OpenURL=\"Отвори URL\"\nFileFilter.ScriptFiles=\"Фајлови скрипте\"\nFileFilter.AllFiles=\"Све датотеке\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/sv-SE.ini",
    "content": "SceneSwitcher=\"Automatisk scenbytare\"\nSceneSwitcher.OnNoMatch=\"När inga fönster matchar:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Byt inte\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Byt till:\"\nSceneSwitcher.CheckInterval=\"Kontrollera titel på aktivt fönster varje:\"\nSceneSwitcher.ActiveOrNotActive=\"Scenbytaren är:\"\nInvalidRegex.Title=\"Ogiltigt reguljärt uttryck\"\nInvalidRegex.Text=\"Det reguljära uttrycket du angav är ogiltigt.\"\nActive=\"Aktiv\"\nInactive=\"Inaktiv\"\nStart=\"Starta\"\nStop=\"Stoppa\"\nCaptions=\"Undertexter (experimentell)\"\nCaptions.AudioSource=\"Ljudkälla\"\nCaptions.CurrentSystemLanguage=\"Aktuellt systemspråk (%1)\"\nCaptions.Provider=\"Tillhandahållare\"\nCaptions.Error.GenericFail=\"Det gick inte att starta undertexter\"\nOutputTimer=\"Utdatatimer\"\nOutputTimer.Stream=\"Stoppa strömmen efter:\"\nOutputTimer.Record=\"Stoppa inspelningen efter:\"\nOutputTimer.Stream.StoppingIn=\"Strömmen stoppas om:\"\nOutputTimer.Record.StoppingIn=\"Inspelningen stoppas om:\"\nOutputTimer.Stream.EnableEverytime=\"Aktivera strömtimer varje gång\"\nOutputTimer.Record.EnableEverytime=\"Aktivera inspelningstimer varje gång\"\nOutputTimer.Record.PauseTimer=\"Pausa timer när inspelning pausas\"\nScripts=\"Skript\"\nLoadedScripts=\"Inlästa skript\"\nAddScripts=\"Lägg till skript\"\nRemoveScripts=\"Ta bort skript\"\nReloadScripts=\"Ladda om skript\"\nEditScript=\"Redigera skript\"\nReload=\"Uppdatera\"\nOpenFileLocation=\"Öppna målmapp\"\nPythonSettings=\"Python-inställningar\"\nPythonSettings.PythonInstallPath32bit=\"Installationssökväg för Python (32 bitar)\"\nPythonSettings.PythonInstallPath64bit=\"Installationssökväg för Python (64 bitar)\"\nPythonSettings.BrowsePythonPath=\"Bläddra Python-sökväg\"\nPythonSettings.PythonVersion=\"Inläst Python-version: %1\"\nPythonSettings.PythonNotLoaded=\"Python har för tillfället inte lästs in\"\nPythonSettings.AlreadyLoaded.Title=\"Python har redan lästs in\"\nPythonSettings.AlreadyLoaded.Message=\"En utgåva av Python %1 har redan lästs in. För att läsa in den nyligen valda Python-versionen, starta om OBS.\"\nScriptLogWindow=\"Skriptlogg\"\nDescription=\"Beskrivning\"\nScriptDescriptionLink.Text=\"Öppna denna länk i din standardwebbläsare?\"\nScriptDescriptionLink.Text.Url=\"Webbadress: %1\"\nScriptDescriptionLink.OpenURL=\"Öppna webbadress\"\nFileFilter.ScriptFiles=\"Skriptfiler\"\nFileFilter.AllFiles=\"Alla filer\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/szl-PL.ini",
    "content": "SceneSwitcher=\"Autōmatyczne szaltrowanie bin\"\nSceneSwitcher.OnNoMatch=\"Kej niy sztimuje żodne ôkno:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Niy szaltruj\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Przeszaltruj na:\"\nSceneSwitcher.CheckInterval=\"Zobocz tytuł aktywnego ôkna co:\"\nSceneSwitcher.ActiveOrNotActive=\"Szaltrowanie bin je:\"\nInvalidRegex.Title=\"Niynoleżne wyrażynie prawidelne\"\nInvalidRegex.Text=\"Podane wyrażynie prawidelne je niynoleżne.\"\nActive=\"Aktywne\"\nInactive=\"Niyaktywne\"\nStart=\"Sztart\"\nStop=\"Sztop\"\nCaptions=\"Podpisy (eksperymyntalne)\"\nCaptions.AudioSource=\"Zdrzōdła klangu\"\nCaptions.CurrentSystemLanguage=\"Teroźno godka systymu (%1)\"\nCaptions.Provider=\"Liferant\"\nCaptions.Error.GenericFail=\"Niy idzie sztartnōńć szkryftōw\"\nOutputTimer=\"Wyłōncznik czasowy\"\nOutputTimer.Stream=\"Zasztopuj stream po:\"\nOutputTimer.Record=\"Zasztopuj nagrowanie po:\"\nOutputTimer.Stream.StoppingIn=\"Sztopowanie streamu za:\"\nOutputTimer.Record.StoppingIn=\"Sztopowanie nagrowanio za:\"\nOutputTimer.Stream.EnableEverytime=\"Włōncz timer streamu kożdy roz\"\nOutputTimer.Record.EnableEverytime=\"Włōncz timer nagrowanio kożdy roz\"\nOutputTimer.Record.PauseTimer=\"Zasztopuj czas, kej nagrowanie je zapauzowane\"\nScripts=\"Skrypty\"\nLoadedScripts=\"Wgrane skrypty\"\nAddScripts=\"Przidej skrypty\"\nRemoveScripts=\"Wyciep skrypty\"\nReloadScripts=\"Przeladuj skrypty\"\nEditScript=\"Edytuj skrypt\"\nReload=\"Przeladuj\"\nOpenFileLocation=\"Ôtwōrz lokalizacyjo zbioru\"\nPythonSettings=\"Ôpcyje Pythona\"\nPythonSettings.PythonInstallPath32bit=\"Ściyżka instalacyje Pythona (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Ściyżka instalacyje Pythona (64bit)\"\nPythonSettings.BrowsePythonPath=\"Ôbier ściyżka instalacyje Pythona\"\nScriptLogWindow=\"Dziynnik skryptōw\"\nDescription=\"Ôpis\"\nScriptDescriptionLink.Text=\"Ôtworzić tyn link we internetowyj przeziyroczce?\"\nScriptDescriptionLink.OpenURL=\"Ôtwōrz URL\"\nFileFilter.ScriptFiles=\"Zbiory skryptōw\"\nFileFilter.AllFiles=\"Wszyjske Zbiory\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ta-IN.ini",
    "content": "SceneSwitcher=\"தானியங்கு காட்சி நிலைமாற்றி\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"மாற வேண்டாம்\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"மாற்று:\"\nSceneSwitcher.CheckInterval=\"செயலில் உள்ள சாளர தலைப்பை சரிபார்க்கவும்:\"\nSceneSwitcher.ActiveOrNotActive=\"காட்சி இடமாற்றி உள்ளது:\"\nInvalidRegex.Title=\"செல்லாத வழக்கமான வெளிப்பாடு\"\nInvalidRegex.Text=\"நீங்கள் உள்ளிட்ட வழக்கமான தொடர் தவறானது.\"\nActive=\"செயலில்\"\nInactive=\"செயலற்ற\"\nStart=\"தொடங்கு\"\nStop=\"நிறுத்து\"\nCaptions=\"தலைப்புகளை (சோதனை)\"\nCaptions.AudioSource=\"ஒலி ஆதாரம்\"\nCaptions.CurrentSystemLanguage=\"தற்போதைய அமைப்பு மொழி (%1)\"\nCaptions.Provider=\"வழங்குநர்\"\nCaptions.Error.GenericFail=\"தலைப்புகள் துவக்குதல் தோல்வி\"\nOutputTimer=\"வெளியீடு நேரம்\"\nOutputTimer.Stream=\"நேரலை நிறுத்த நேரம்:\"\nOutputTimer.Record=\"பதிவு நிறுத்த நேரம்:\"\nOutputTimer.Stream.StoppingIn=\"நேரலை முடிவடையும் நேரம்:\"\nOutputTimer.Record.StoppingIn=\"பதிவு முடிவடையும் நேரம்:\"\nScripts=\"எழுத்து\"\nReload=\"புதுப்பி\"\nOpenFileLocation=\"கோப்பு இருப்பிடத்தைத் திற\"\nPythonSettings=\"Python அமைப்புகள்\"\nPythonSettings.PythonInstallPath32bit=\"Python நிறுவல் பாதை (32 பிட்)\"\nPythonSettings.PythonInstallPath64bit=\"Python நிறுவல் பாதை (64 பிட்)\"\nPythonSettings.BrowsePythonPath=\"Python பாதைக்காக உலாவு\"\nDescription=\"விளக்கம்\"\nScriptDescriptionLink.OpenURL=\"இணைப்பைத் திற\"\nFileFilter.AllFiles=\"அனைத்து கோப்புகள்\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/th-TH.ini",
    "content": "SceneSwitcher=\"ตัวสลับฉากอัตโนมัติ\"\nSceneSwitcher.OnNoMatch=\"เมื่อหน้าต่างไม่สัมพันธ์กัน:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"ไม่สลับ\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"สลับไปยัง:\"\nSceneSwitcher.CheckInterval=\"ตรวจสอบหน้าต่างที่ถูกเลือกทุกๆ:\"\nSceneSwitcher.ActiveOrNotActive=\"ตัวสลับฉาก:\"\nInvalidRegex.Title=\"นิพจน์ปกติไม่ถูกต้อง\"\nInvalidRegex.Text=\"นิพจน์ปกติที่คุณป้อนมาไม่ถูกต้อง\"\nActive=\"ใช้งานอยู่\"\nInactive=\"ไม่ใช้งาน\"\nStart=\"เริ่ม\"\nStop=\"หยุด\"\nCaptions=\"คำบรรยาย (ทดลอง)\"\nCaptions.AudioSource=\"แหล่งเสียง\"\nCaptions.CurrentSystemLanguage=\"ภาษาระบบปัจจุบัน (%1)\"\nCaptions.Provider=\"ระบบบริการ\"\nCaptions.Error.GenericFail=\"การเริ่มคำบรรยายล้มเหลว\"\nOutputTimer=\"ตัวตั้งเวลาเอาต์พุต\"\nOutputTimer.Stream=\"หยุดสตรีมมิ่งหลังจาก:\"\nOutputTimer.Record=\"หยุดบันทึกหลังจาก:\"\nOutputTimer.Stream.StoppingIn=\"สตรีมมิ่งหยุดใน:\"\nOutputTimer.Record.StoppingIn=\"การบันทึกหยุดใน:\"\nOutputTimer.Stream.EnableEverytime=\"เปิดใช้งานตัวจับเวลาการสตรีมทุกครั้ง\"\nOutputTimer.Record.EnableEverytime=\"เปิดใช้งานตัวจับเวลาการบันทึกทุกครั้ง\"\nOutputTimer.Record.PauseTimer=\"หยุดตัวจับเวลาเมื่อการบันทึกถูกหยุดชั่วคราว\"\nScripts=\"สคริปต์\"\nLoadedScripts=\"สคริปต์ที่โหลด\"\nAddScripts=\"เพิ่มสคริปต์\"\nRemoveScripts=\"ลบสคริปต์\"\nReloadScripts=\"โหลดสคริปต์ใหม่\"\nEditScript=\"แก้ไขสคริปต์\"\nReload=\"โหลดใหม่\"\nOpenFileLocation=\"เปิดตำแหน่งไฟล์\"\nPythonSettings=\"การตั้งค่าไพธอน\"\nPythonSettings.PythonInstallPath32bit=\"ตำแหน่งติดตั้งไพธอน (32 บิต)\"\nPythonSettings.PythonInstallPath64bit=\"ตำแหน่งติดตั้งไพธอน (64 บิต)\"\nPythonSettings.BrowsePythonPath=\"ค้นหาตำแหน่งไพธอน\"\nPythonSettings.PythonVersion=\"เวอร์ชั่นของไพธอนที่โหลด: %1\"\nPythonSettings.PythonNotLoaded=\"ไพธอนยังไม่ได้โหลดในขณะนี้\"\nPythonSettings.AlreadyLoaded.Title=\"ไพธอนโหลดมาแล้ว\"\nPythonSettings.AlreadyLoaded.Message=\"โปรแกรมไพธอน %1 ที่คัดลอกมาได้โหลดเรียบร้อยแล้ว หากต้องการโหลดไพธอนเวอร์ชั่นที่เลือกมาใหม่ กรุณารีสตาร์ท OBS\"\nScriptLogWindow=\"ข้อมูลสคริปต์\"\nDescription=\"คำอธิบาย\"\nScriptDescriptionLink.Text=\"เปิดลิงก์นี้ในเว็บเบราว์เซอร์เริ่มต้นของคุณหรือไม่?\"\nScriptDescriptionLink.OpenURL=\"เปิด URL\"\nFileFilter.ScriptFiles=\"ไฟล์สคริปต์\"\nFileFilter.AllFiles=\"ไฟล์ทั้งหมด\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/tl-PH.ini",
    "content": "SceneSwitcher=\"Awtomatikong Tagapalit ng Eksena\"\nSceneSwitcher.OnNoMatch=\"Kapag walang tumugmang window:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Huwag lumipat\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Lumipat sa:\"\nSceneSwitcher.CheckInterval=\"Suriin ang pamagat ng aktibong window kada:\"\nSceneSwitcher.ActiveOrNotActive=\"Ang Tag-palit ng Eksena ay:\"\nInvalidRegex.Title=\"Di-wastong Regular Expression\"\nInvalidRegex.Text=\"Ang regular na ekspresyon na iyong ipinasok ay imbalido.\"\nActive=\"Aktibo\"\nInactive=\"Hindi aktibo\"\nStart=\"Simula\"\nStop=\"Ihinto\"\nCaptions=\"Mga Pamagat (Eksparimento)\"\nCaptions.AudioSource=\"Pinanggalingan ng Audio\"\nCaptions.CurrentSystemLanguage=\"Kasalukuyang Wika ng Sistema (%1)\"\nCaptions.Provider=\"Tagapagtustos\"\nCaptions.Error.GenericFail=\"Nabigo sa pasisimula ng mga pamagat\"\nOutputTimer=\"Pamlabas na Orasan\"\nOutputTimer.Stream=\"Itigil ang pag-stream pagkatapos:\"\nOutputTimer.Record=\"Itigil ang pagtatala pagkatapos:\"\nOutputTimer.Stream.StoppingIn=\"Ang pagtigil ng stream sa:\"\nOutputTimer.Record.StoppingIn=\"Ang pagtigil ng pagtatala sa:\"\nOutputTimer.Stream.EnableEverytime=\"Paganahin ang streaming timer sa bawat oras\"\nOutputTimer.Record.EnableEverytime=\"Paganahin ang timer ng pagtatala bawat oras\"\nScripts=\"Mga iskrip\"\nLoadedScripts=\"Mga iskrip na naikarga\"\nAddScripts=\"Magdagdag ng iskrip\"\nRemoveScripts=\"Tanggalin ang mga iskrip\"\nReloadScripts=\"Ikargang muli ang mga Iskrip\"\nPythonSettings=\"Mga Setting Python\"\nPythonSettings.PythonInstallPath32bit=\"Landas ng Pagkakabit ng Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Landas ng Pagkakabit ng Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"Pumunta sa Landas ng Python\"\nScriptLogWindow=\"Mag-log sa Iskrip\"\nDescription=\"Paglalarawan\"\nFileFilter.ScriptFiles=\"Mga File ng Iskrip\"\nFileFilter.AllFiles=\"Lahat ng mga File\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/tr-TR.ini",
    "content": "SceneSwitcher=\"Otomatik Sahne Değiştirici\"\nSceneSwitcher.OnNoMatch=\"Hiçbir pencere ile eşleşmez ise:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Geçiş yapma\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Şuna geç:\"\nSceneSwitcher.CheckInterval=\"Etkin pencere başlığını kontrol et:\"\nSceneSwitcher.ActiveOrNotActive=\"Sahne Değiştirici:\"\nInvalidRegex.Title=\"Geçersiz Kurallı İfade\"\nInvalidRegex.Text=\"Girdiğiniz kurallı ifade geçersiz.\"\nActive=\"Etkin\"\nInactive=\"Devre Dışı\"\nStart=\"Başlat\"\nStop=\"Durdur\"\nCaptions=\"Altyazı (Deneysel)\"\nCaptions.AudioSource=\"Ses kaynağı\"\nCaptions.CurrentSystemLanguage=\"Geçerli Sistem Dili (%1)\"\nCaptions.Provider=\"Sağlayıcı\"\nCaptions.Error.GenericFail=\"Altyazı başlatılamadı\"\nOutputTimer=\"Çıkış Zamanlayıcısı\"\nOutputTimer.Stream=\"Şuradan sonra yayını durdur:\"\nOutputTimer.Record=\"Şuradan sonra kaydı durdur:\"\nOutputTimer.Stream.StoppingIn=\"Yayın durduruluyor:\"\nOutputTimer.Record.StoppingIn=\"Kayıt durduruluyor:\"\nOutputTimer.Stream.EnableEverytime=\"Her zaman yayın zamanlayıcıyı etkinleştir\"\nOutputTimer.Record.EnableEverytime=\"Her zaman kayıt zamanlayıcıyı etkinleştir\"\nOutputTimer.Record.PauseTimer=\"Kayıt durdurulduğunda sayacı durdur\"\nScripts=\"Betikler\"\nLoadedScripts=\"Yüklü Betikler\"\nAddScripts=\"Betik Ekle\"\nRemoveScripts=\"Betikleri Sil\"\nReloadScripts=\"Betikleri Yeniden Yükle\"\nEditScript=\"Komut Dosyasını Düzenle\"\nReload=\"Yeniden Yükle\"\nOpenFileLocation=\"Dosya konumunu aç\"\nPythonSettings=\"Python Ayarları\"\nPythonSettings.PythonInstallPath32bit=\"Python Kurulum Yolu (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python Kurulum Yolu (64bit)\"\nPythonSettings.BrowsePythonPath=\"Python Yoluna Göz At\"\nPythonSettings.PythonVersion=\"Yüklenen Python Sürümü: %1\"\nPythonSettings.PythonNotLoaded=\"Python şu anda yüklü değil\"\nPythonSettings.AlreadyLoaded.Title=\"Python Zaten Yüklü\"\nPythonSettings.AlreadyLoaded.Message=\"Python %1 için bir kopya zaten yüklü. Seçilen yeni Python versiyonunu yüklemek için lütfen OBS'i yeniden başlatın.\"\nScriptLogWindow=\"Betik Günlüğü\"\nDescription=\"Açıklama\"\nScriptDescriptionLink.Text=\"Bu bağlantı varsayılan web tarayıcınızda açılsın mı?\"\nScriptDescriptionLink.Text.Url=\"Bağlantı Adresi: %1\"\nScriptDescriptionLink.OpenURL=\"Bağlantı Adresini Aç\"\nFileFilter.ScriptFiles=\"Betik Dosyaları\"\nFileFilter.AllFiles=\"Tüm Dosyalar\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/tt-RU.ini",
    "content": "Active=\"Актив\"\nInactive=\"Активсыз\"\nStart=\"Башлау\"\nStop=\"туктату\"\nCaptions.AudioSource=\"Аудио чыганагы\"\nCaptions.CurrentSystemLanguage=\"Систем хәзерге тел (%1)\"\nScripts=\"Скриптлар\"\nReload=\"Яңадан йөкләү\"\nOpenFileLocation=\"Файл урынын ачу\"\nPythonSettings=\"Python көйләүләре\"\nPythonSettings.PythonInstallPath32bit=\"Python'ны утырту юлы (32 бит)\"\nPythonSettings.PythonInstallPath64bit=\"Python'ны утырту юлы (64 бит)\"\nDescription=\"Тасвирлама\"\nScriptDescriptionLink.Text.Url=\"Сылтама: %1\"\nScriptDescriptionLink.OpenURL=\"URL адресын ачу\"\nFileFilter.AllFiles=\"Барлык файллар\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/ug-CN.ini",
    "content": "SceneSwitcher=\"ئۆزلۈكىدىن كۆرۈنۈش ئالماشتۇرغۇچ\"\nSceneSwitcher.OnNoMatch=\"ھېچقانداق كۆزنەك ماس كەلمىگەندە:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"ئالماشتۇرما\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"ئالماشتۇرىدىغىنى:\"\nSceneSwitcher.CheckInterval=\"ئاكتىپ كۆزنەك ماۋزۇسىنى تەكشۈرۈش ۋاقتى:\"\nSceneSwitcher.ActiveOrNotActive=\"ئېكران ئالماشتۇرغۇچ:\"\nInvalidRegex.Title=\"ئىناۋەتسىز مۇنتىزىم ئىپادە\"\nInvalidRegex.Text=\"كىرگۈزگەن مۇنتىزىم ئىپادە ئىناۋەتسىز.\"\nActive=\"ئاكتىپ\"\nInactive=\"ئاكتىپسىز\"\nStart=\"باشلاش\"\nStop=\"توختا\"\nCaptions=\"كىنو خېتى (سىناق)\"\nCaptions.AudioSource=\"ئۈن مەنبەسى\"\nCaptions.CurrentSystemLanguage=\"نۆۋەتتىكى سىستېما تىلى (%1)\"\nCaptions.Provider=\"تەمىنلىگۈچى\"\nCaptions.Error.GenericFail=\"كىنو خېتىنى قوزغىتالمىدى\"\nOutputTimer=\"چىقىرىش ۋاقىت خاتىرىلىگۈچ\"\nOutputTimer.Stream=\"ئېقىم توختىتىش ۋاقتى:\"\nOutputTimer.Record=\"خاتىرىلەشنى توختىتىش ۋاقتى:\"\nOutputTimer.Stream.StoppingIn=\"ئېقىم تارقىتىش توختايدۇ:\"\nOutputTimer.Record.StoppingIn=\"خاتىرىلەش توختايدۇ:\"\nOutputTimer.Stream.EnableEverytime=\"ھەر قېتىم ئېقىم ۋاقىت خاتىرىلىگۈچنى قوزغىتىدۇ\"\nOutputTimer.Record.EnableEverytime=\"ھەر قېتىم ئېقىم خاتىرىلىگۈچنى قوزغىتىدۇ\"\nOutputTimer.Record.PauseTimer=\"خاتىرىلەش ۋاقىتلىق توختىغاندا ۋاقىت خاتىرىلىگۈچمۇ ۋاقىتلىق توختايدۇ\"\nScripts=\"قوليازما\"\nLoadedScripts=\"يۈكلەنگەن قوليازما\"\nAddScripts=\"قوليازما قوش\"\nRemoveScripts=\"قوليازمىنى چىقىرىۋەت\"\nReloadScripts=\"قوليازمىنى قايتا يۈكلە\"\nEditScript=\"قوليازما تەھرىر\"\nReload=\"قايتا يۈكلە\"\nOpenFileLocation=\"ھۆججەت ئورنىنى ئاچ\"\nPythonSettings=\"Python تەڭشەكلىرى\"\nPythonSettings.PythonInstallPath32bit=\"Python ئورنىتىش يولى (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python ئورنىتىش يولى (64bit)\"\nPythonSettings.BrowsePythonPath=\"Python يولىغا كۆز يۈگۈرت\"\nPythonSettings.PythonVersion=\"يۈكلەنگەن Python نەشرى: %1\"\nPythonSettings.PythonNotLoaded=\"نۆۋەتتە Python يۈكلەنمىدى\"\nPythonSettings.AlreadyLoaded.Title=\"Python يۈكلەندى\"\nPythonSettings.AlreadyLoaded.Message=\"Python %1 نەشرى يۈكلەندى. يېڭىراق Python نەشرىنى يۈكلەش ئۈچۈن OBS نى قايتا قوزغىتىڭ.\"\nScriptLogWindow=\"قوليازما خاتىرىسى\"\nDescription=\"چۈشەندۈرۈش\"\nScriptDescriptionLink.Text=\"كۆڭۈلدىكى توركۆرگۈڭىزدە بۇ ئۇلانمىنى ئاچامدۇ؟\"\nScriptDescriptionLink.Text.Url=\"تور ئادرېسى: %1\"\nScriptDescriptionLink.OpenURL=\"URL ئاچ\"\nFileFilter.ScriptFiles=\"قوليازما ھۆججەت\"\nFileFilter.AllFiles=\"بارلىق ھۆججەتلەر\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/uk-UA.ini",
    "content": "SceneSwitcher=\"Автоматичний перемикач сцен\"\nSceneSwitcher.OnNoMatch=\"Якщо відповідні вікна відсутні:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Не перемикати\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Перейти до:\"\nSceneSwitcher.CheckInterval=\"Перевіряти заголовок активного вікна кожні:\"\nSceneSwitcher.ActiveOrNotActive=\"Перемикач сцен:\"\nInvalidRegex.Title=\"Недійсний регулярний вираз\"\nInvalidRegex.Text=\"Введено недійсний регулярний вираз.\"\nActive=\"Активний\"\nInactive=\"Неактивний\"\nStart=\"Запустити\"\nStop=\"Зупинити\"\nCaptions=\"Субтитри (експериментально)\"\nCaptions.AudioSource=\"Джерело аудіо\"\nCaptions.CurrentSystemLanguage=\"Поточна мова системи (%1)\"\nCaptions.Provider=\"Постачальник\"\nCaptions.Error.GenericFail=\"Не вдалося запустити субтитри\"\nOutputTimer=\"Таймер виводу\"\nOutputTimer.Stream=\"Завершити трансляцію за:\"\nOutputTimer.Record=\"Зупинити запис за:\"\nOutputTimer.Stream.StoppingIn=\"Трансляція припиниться за:\"\nOutputTimer.Record.StoppingIn=\"Запис припиниться за:\"\nOutputTimer.Stream.EnableEverytime=\"Щоразу вмикати таймер трансляції\"\nOutputTimer.Record.EnableEverytime=\"Щоразу вмикати таймер запису\"\nOutputTimer.Record.PauseTimer=\"Призупиняти таймер, коли записування на паузі\"\nScripts=\"Скрипти\"\nLoadedScripts=\"Завантажені скрипти\"\nAddScripts=\"Додати скрипти\"\nRemoveScripts=\"Видалити скрипти\"\nReloadScripts=\"Перезавантажити скрипти\"\nEditScript=\"Змінити скрипт\"\nReload=\"Перезавантажити\"\nOpenFileLocation=\"Відкрити папку файлу\"\nPythonSettings=\"Налаштування Python\"\nPythonSettings.PythonInstallPath32bit=\"Шлях інсталяції Python (32 біти)\"\nPythonSettings.PythonInstallPath64bit=\"Шлях інсталяції Python (64 біти)\"\nPythonSettings.BrowsePythonPath=\"Огляд шляху до програми Python\"\nPythonSettings.PythonVersion=\"Завантажена версія Python: %1\"\nPythonSettings.PythonNotLoaded=\"Python не завантажено\"\nPythonSettings.AlreadyLoaded.Title=\"Python вже завантажений\"\nPythonSettings.AlreadyLoaded.Message=\"Копія Python %1 вже завантажена. Щоб завантажити нову версію Python, перезапустіть OBS.\"\nScriptLogWindow=\"Журнал скрипта\"\nDescription=\"Опис\"\nScriptDescriptionLink.Text=\"Відкрити це посилання у стандартному веббраузері?\"\nScriptDescriptionLink.OpenURL=\"Відкрити URL\"\nFileFilter.ScriptFiles=\"Файли скриптів\"\nFileFilter.AllFiles=\"Усі файли\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/vi-VN.ini",
    "content": "SceneSwitcher=\"Tự động chuyển cảnh\"\nSceneSwitcher.OnNoMatch=\"Khi cửa sổ không khớp:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"Không chuyển\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"Chuyển sang:\"\nSceneSwitcher.CheckInterval=\"Kiểm tra tiêu đề cửa sổ mỗi:\"\nSceneSwitcher.ActiveOrNotActive=\"Chuyển cảnh đang:\"\nInvalidRegex.Title=\"Cụm từ thông dụng không hợp lệ\"\nInvalidRegex.Text=\"Cụm từ thông dụng mà bạn đã nhập không hợp lệ.\"\nActive=\"Đang hoạt động\"\nInactive=\"Không hoạt động\"\nStart=\"Bắt đầu\"\nStop=\"Dừng\"\nCaptions=\"Phụ đề (Thử nghiệm)\"\nCaptions.AudioSource=\"Nguồn âm thanh\"\nCaptions.CurrentSystemLanguage=\"Ngôn ngữ hiện tại của máy tính (%1)\"\nCaptions.Provider=\"Nhà cung cấp\"\nCaptions.Error.GenericFail=\"Thất bại trong việc bắt đầu phụ đề\"\nOutputTimer=\"Hẹn giờ đầu ra\"\nOutputTimer.Stream=\"Dừng stream sau:\"\nOutputTimer.Record=\"Dừng ghi video sau:\"\nOutputTimer.Stream.StoppingIn=\"Luồng sẽ dừng trong:\"\nOutputTimer.Record.StoppingIn=\"Quay video sẽ dừng trong:\"\nOutputTimer.Stream.EnableEverytime=\"Bật hẹn giờ phát luồng mỗi lần\"\nOutputTimer.Record.EnableEverytime=\"Bật hẹn giờ phát mỗi lần\"\nOutputTimer.Record.PauseTimer=\"Dừng bấm giờ khi ghi hình tạm dừng\"\nScripts=\"Kịch bản\"\nLoadedScripts=\"Kịch bản đã nạp\"\nAddScripts=\"Thêm script\"\nRemoveScripts=\"Gỡ bỏ kịch bản\"\nReloadScripts=\"Nạp lại kịch bản\"\nEditScript=\"Sửa tập lệnh\"\nReload=\"Tải lại\"\nOpenFileLocation=\"Mở vị trí tệp\"\nPythonSettings=\"Thiết đặt Python\"\nPythonSettings.PythonInstallPath32bit=\"Đường dẫn cài đặt Python (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Đường dẫn cài đặt Python (64bit)\"\nPythonSettings.BrowsePythonPath=\"Duyệt đường dẫn Python\"\nPythonSettings.PythonVersion=\"Phiên bản Python hiện tại: %1\"\nPythonSettings.PythonNotLoaded=\"Python hiện chưa được nạp\"\nPythonSettings.AlreadyLoaded.Title=\"Python đã được load\"\nPythonSettings.AlreadyLoaded.Message=\"Phiên bản Python %1 đang trong sử dụng. Để load lại phiên bản Python mới, vui lòng restart lại OBS.\"\nScriptLogWindow=\"Bản ghi kịch bản\"\nDescription=\"Mô tả\"\nScriptDescriptionLink.Text=\"Mở liên kết trên trình duyệt web mặc định?\"\nScriptDescriptionLink.OpenURL=\"Mở URL\"\nFileFilter.ScriptFiles=\"Tập tin script\"\nFileFilter.AllFiles=\"Tất cả tập tin\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/zh-CN.ini",
    "content": "SceneSwitcher=\"场景自动切换器\"\nSceneSwitcher.OnNoMatch=\"当没有匹配的窗口时:\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"不切换\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"切换到：\"\nSceneSwitcher.CheckInterval=\"每隔一段时间检查活动窗口标题：\"\nSceneSwitcher.ActiveOrNotActive=\"场景切换器：\"\nInvalidRegex.Title=\"无效的正则表达式\"\nInvalidRegex.Text=\"您输入的正则表达式是无效的。\"\nActive=\"已激活\"\nInactive=\"未激活\"\nStart=\"开始\"\nStop=\"停止\"\nCaptions=\"字幕 (实验)\"\nCaptions.AudioSource=\"音频源\"\nCaptions.CurrentSystemLanguage=\"当前系统语言 (%1)\"\nCaptions.Provider=\"字幕提供程序\"\nCaptions.Error.GenericFail=\"字幕启动失败\"\nOutputTimer=\"输出计时器\"\nOutputTimer.Stream=\"此时间后停止推流：\"\nOutputTimer.Record=\"此时间后停止录制：\"\nOutputTimer.Stream.StoppingIn=\"推流停止倒计时：\"\nOutputTimer.Record.StoppingIn=\"录制停止倒计时：\"\nOutputTimer.Stream.EnableEverytime=\"每次都开启推流计时器\"\nOutputTimer.Record.EnableEverytime=\"每次都开启录制计时器\"\nOutputTimer.Record.PauseTimer=\"在录制暂停时暂停计时器\"\nScripts=\"脚本\"\nLoadedScripts=\"已载入脚本\"\nAddScripts=\"添加脚本\"\nRemoveScripts=\"移除脚本\"\nReloadScripts=\"重新载入脚本\"\nEditScript=\"编辑脚本\"\nReload=\"重新载入\"\nOpenFileLocation=\"打开文件位置\"\nPythonSettings=\"Python 设置\"\nPythonSettings.PythonInstallPath32bit=\"Python 安装路径 (32位)\"\nPythonSettings.PythonInstallPath64bit=\"Python 安装路径 (64位)\"\nPythonSettings.BrowsePythonPath=\"浏览 Python 路径\"\nPythonSettings.PythonVersion=\"已加载 Python 版本：%1\"\nPythonSettings.PythonNotLoaded=\"当前未加载 Python\"\nPythonSettings.AlreadyLoaded.Title=\"已加载 Python\"\nPythonSettings.AlreadyLoaded.Message=\"已经加载 Python %1。要加载新选中的 Python 版本，请重启 OBS。\"\nScriptLogWindow=\"脚本日志\"\nDescription=\"说明\"\nScriptDescriptionLink.Text=\"使用默认网络浏览器打开此链接？\"\nScriptDescriptionLink.Text.Url=\"网址: %1\"\nScriptDescriptionLink.OpenURL=\"打开网址\"\nFileFilter.ScriptFiles=\"脚本文件\"\nFileFilter.AllFiles=\"所有文件\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/locale/zh-TW.ini",
    "content": "SceneSwitcher=\"自動場景切換器\"\nSceneSwitcher.OnNoMatch=\"當沒有視窗相符時︰\"\nSceneSwitcher.OnNoMatch.DontSwitch=\"不切換\"\nSceneSwitcher.OnNoMatch.SwitchTo=\"切換到︰\"\nSceneSwitcher.CheckInterval=\"檢查使用中視窗標題的頻率︰\"\nSceneSwitcher.ActiveOrNotActive=\"場景切換器︰\"\nInvalidRegex.Title=\"無效的正規表示式\"\nInvalidRegex.Text=\"您輸入的正規表示式不正確。\"\nActive=\"啟動中\"\nInactive=\"未啟動\"\nStart=\"開始\"\nStop=\"停止\"\nCaptions=\"標題 （實驗）\"\nCaptions.AudioSource=\"音訊源\"\nCaptions.CurrentSystemLanguage=\"目前系統語言 (%1)\"\nCaptions.Provider=\"提供程式\"\nCaptions.Error.GenericFail=\"啟動標題失敗\"\nOutputTimer=\"輸出計時器\"\nOutputTimer.Stream=\"在下面時間後停止串流：\"\nOutputTimer.Record=\"在下面時間後停止錄影：\"\nOutputTimer.Stream.StoppingIn=\"串流將在下面時間內停止\"\nOutputTimer.Record.StoppingIn=\"錄影將在下面時間內停止\"\nOutputTimer.Stream.EnableEverytime=\"每次都啟動串流計時器\"\nOutputTimer.Record.EnableEverytime=\"每次都啟動錄影計時器\"\nOutputTimer.Record.PauseTimer=\"錄影暫停時，計時器也暫停。\"\nScripts=\"腳本\"\nLoadedScripts=\"載入的腳本\"\nAddScripts=\"加入腳本\"\nRemoveScripts=\"移除腳本\"\nReloadScripts=\"重新載入腳本\"\nEditScript=\"編輯腳本\"\nReload=\"重新載入\"\nOpenFileLocation=\"開啟檔案所在位置\"\nPythonSettings=\"Python 設定\"\nPythonSettings.PythonInstallPath32bit=\"Python 安裝路徑 (32bit)\"\nPythonSettings.PythonInstallPath64bit=\"Python 安裝路徑 (64bit)\"\nPythonSettings.BrowsePythonPath=\"瀏覽 Python 路徑\"\nPythonSettings.PythonVersion=\"已載入的 Python 版本： %1\"\nPythonSettings.PythonNotLoaded=\"當前未載入 Python\"\nPythonSettings.AlreadyLoaded.Title=\"Python 已載入\"\nPythonSettings.AlreadyLoaded.Message=\"有一個已加載的 Python %1 副本。 請重啟 OBS 以加載新選擇的 Python 版本。\"\nScriptLogWindow=\"指令稿記錄\"\nDescription=\"描述\"\nScriptDescriptionLink.Text=\"您是否要在您的預設網頁瀏覽器開啟此連結？\"\nScriptDescriptionLink.Text.Url=\"網址：%1\"\nScriptDescriptionLink.OpenURL=\"開啟網址\"\nFileFilter.ScriptFiles=\"腳本檔案\"\nFileFilter.AllFiles=\"所有檔案\"\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/scripts/clock-source.lua",
    "content": "obs = obslua\nbit = require(\"bit\")\n\nsource_def = {}\nsource_def.id = \"lua_clock_source\"\nsource_def.output_flags = bit.bor(obs.OBS_SOURCE_VIDEO, obs.OBS_SOURCE_CUSTOM_DRAW)\n\nfunction image_source_load(image, file)\n\tobs.obs_enter_graphics();\n\tobs.gs_image_file_free(image);\n\tobs.obs_leave_graphics();\n\n\tobs.gs_image_file_init(image, file);\n\n\tobs.obs_enter_graphics();\n\tobs.gs_image_file_init_texture(image);\n\tobs.obs_leave_graphics();\n\n\tif not image.loaded then\n\t\tprint(\"failed to load texture \" .. file);\n\tend\nend\n\nsource_def.get_name = function()\n\treturn \"Lua Clock\"\nend\n\nsource_def.create = function(source, settings)\n\tlocal data = {}\n\tdata.image = obs.gs_image_file()\n\tdata.hour_image = obs.gs_image_file()\n\tdata.minute_image = obs.gs_image_file()\n\tdata.second_image = obs.gs_image_file()\n\n\timage_source_load(data.image, script_path() .. \"clock-source/dial.png\")\n\timage_source_load(data.hour_image, script_path() .. \"clock-source/hour.png\")\n\timage_source_load(data.minute_image, script_path() .. \"clock-source/minute.png\")\n\timage_source_load(data.second_image, script_path() .. \"clock-source/second.png\")\n\n\treturn data\nend\n\nsource_def.destroy = function(data)\n\tobs.obs_enter_graphics();\n\tobs.gs_image_file_free(data.image);\n\tobs.gs_image_file_free(data.hour_image);\n\tobs.gs_image_file_free(data.minute_image);\n\tobs.gs_image_file_free(data.second_image);\n\tobs.obs_leave_graphics();\nend\n\nsource_def.video_render = function(data, effect)\n\tif not data.image.texture then\n\t\treturn;\n\tend\n\n\tlocal time = os.date(\"*t\")\n\tlocal seconds = time.sec\n\tlocal mins = time.min + seconds / 60.0;\n\tlocal hours = time.hour + (mins * 60.0) / 3600.0;\n\n\teffect = obs.obs_get_base_effect(obs.OBS_EFFECT_DEFAULT)\n\n\tobs.gs_blend_state_push()\n\tobs.gs_reset_blend_state()\n\n\twhile obs.gs_effect_loop(effect, \"Draw\") do\n\t\tobs.obs_source_draw(data.image.texture, 0, 0, data.image.cx, data.image.cy, false);\n\tend\n\n\tobs.gs_matrix_push()\n\tobs.gs_matrix_translate3f(250, 250, 0)\n\tobs.gs_matrix_rotaa4f(0.0, 0.0, 1.0, 2 * math.pi / 60 * mins);\n\tobs.gs_matrix_translate3f(-250, -250, 0)\n\n\twhile obs.gs_effect_loop(effect, \"Draw\") do\n\t\tobs.obs_source_draw(data.minute_image.texture, 0, 0, data.image.cx, data.image.cy, false);\n\tend\n\n\tobs.gs_matrix_pop()\n\n\tobs.gs_matrix_push()\n\tobs.gs_matrix_translate3f(250, 250, 0)\n\tobs.gs_matrix_rotaa4f(0.0, 0.0, 1.0, 2.0 * math.pi / 12 * hours);\n\tobs.gs_matrix_translate3f(-250, -250, 0)\n\n\twhile obs.gs_effect_loop(effect, \"Draw\") do\n\t\tobs.obs_source_draw(data.hour_image.texture, 0, 0, data.image.cx, data.image.cy, false);\n\tend\n\n\tobs.gs_matrix_pop()\n\n\tobs.gs_matrix_push()\n\tobs.gs_matrix_translate3f(250, 250, 0)\n\tobs.gs_matrix_rotaa4f(0.0, 0.0, 1.0, 2 * math.pi / 60 * seconds);\n\tobs.gs_matrix_translate3f(-250, -250, 0)\n\n\twhile obs.gs_effect_loop(effect, \"Draw\") do\n\t\tobs.obs_source_draw(data.second_image.texture, 0, 0, data.image.cx, data.image.cy, false);\n\tend\n\n\tobs.gs_matrix_pop()\n\n\tobs.gs_blend_state_pop()\nend\n\nsource_def.get_width = function(data)\n\treturn 500\nend\n\nsource_def.get_height = function(data)\n\treturn 500\nend\n\nfunction script_description()\n\treturn \"Adds a \\\"Lua Clock\\\" source which draws an animated analog clock.\"\nend\n\nobs.obs_register_source(source_def)\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/scripts/countdown.lua",
    "content": "obs           = obslua\nsource_name   = \"\"\ntotal_seconds = 0\n\ncur_seconds   = 0\nlast_text     = \"\"\nstop_text     = \"\"\nactivated     = false\n\nhotkey_id     = obs.OBS_INVALID_HOTKEY_ID\n\n-- Function to set the time text\nfunction set_time_text()\n\tlocal seconds       = math.floor(cur_seconds % 60)\n\tlocal total_minutes = math.floor(cur_seconds / 60)\n\tlocal minutes       = math.floor(total_minutes % 60)\n\tlocal hours         = math.floor(total_minutes / 60)\n\tlocal text          = string.format(\"%02d:%02d:%02d\", hours, minutes, seconds)\n\n\tif cur_seconds < 1 then\n\t\ttext = stop_text\n\tend\n\n\tif text ~= last_text then\n\t\tlocal source = obs.obs_get_source_by_name(source_name)\n\t\tif source ~= nil then\n\t\t\tlocal settings = obs.obs_data_create()\n\t\t\tobs.obs_data_set_string(settings, \"text\", text)\n\t\t\tobs.obs_source_update(source, settings)\n\t\t\tobs.obs_data_release(settings)\n\t\t\tobs.obs_source_release(source)\n\t\tend\n\tend\n\n\tlast_text = text\nend\n\nfunction timer_callback()\n\tcur_seconds = cur_seconds - 1\n\tif cur_seconds < 0 then\n\t\tobs.remove_current_callback()\n\t\tcur_seconds = 0\n\tend\n\n\tset_time_text()\nend\n\nfunction activate(activating)\n\tif activated == activating then\n\t\treturn\n\tend\n\n\tactivated = activating\n\n\tif activating then\n\t\tcur_seconds = total_seconds\n\t\tset_time_text()\n\t\tobs.timer_add(timer_callback, 1000)\n\telse\n\t\tobs.timer_remove(timer_callback)\n\tend\nend\n\n-- Called when a source is activated/deactivated\nfunction activate_signal(cd, activating)\n\tlocal source = obs.calldata_source(cd, \"source\")\n\tif source ~= nil then\n\t\tlocal name = obs.obs_source_get_name(source)\n\t\tif (name == source_name) then\n\t\t\tactivate(activating)\n\t\tend\n\tend\nend\n\nfunction source_activated(cd)\n\tactivate_signal(cd, true)\nend\n\nfunction source_deactivated(cd)\n\tactivate_signal(cd, false)\nend\n\nfunction reset(pressed)\n\tif not pressed then\n\t\treturn\n\tend\n\n\tactivate(false)\n\tlocal source = obs.obs_get_source_by_name(source_name)\n\tif source ~= nil then\n\t\tlocal active = obs.obs_source_active(source)\n\t\tobs.obs_source_release(source)\n\t\tactivate(active)\n\tend\nend\n\nfunction reset_button_clicked(props, p)\n\treset(true)\n\treturn false\nend\n\n----------------------------------------------------------\n\n-- A function named script_properties defines the properties that the user\n-- can change for the entire script module itself\nfunction script_properties()\n\tlocal props = obs.obs_properties_create()\n\tobs.obs_properties_add_int(props, \"duration\", \"Duration (minutes)\", 1, 100000, 1)\n\n\tlocal p = obs.obs_properties_add_list(props, \"source\", \"Text Source\", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING)\n\tlocal sources = obs.obs_enum_sources()\n\tif sources ~= nil then\n\t\tfor _, source in ipairs(sources) do\n\t\t\tsource_id = obs.obs_source_get_unversioned_id(source)\n\t\t\tif source_id == \"text_gdiplus\" or source_id == \"text_ft2_source\" then\n\t\t\t\tlocal name = obs.obs_source_get_name(source)\n\t\t\t\tobs.obs_property_list_add_string(p, name, name)\n\t\t\tend\n\t\tend\n\tend\n\tobs.source_list_release(sources)\n\n\tobs.obs_properties_add_text(props, \"stop_text\", \"Final Text\", obs.OBS_TEXT_DEFAULT)\n\tobs.obs_properties_add_button(props, \"reset_button\", \"Reset Timer\", reset_button_clicked)\n\n\treturn props\nend\n\n-- A function named script_description returns the description shown to\n-- the user\nfunction script_description()\n\treturn \"Sets a text source to act as a countdown timer when the source is active.\\n\\nMade by Lain\"\nend\n\n-- A function named script_update will be called when settings are changed\nfunction script_update(settings)\n\tactivate(false)\n\n\ttotal_seconds = obs.obs_data_get_int(settings, \"duration\") * 60\n\tsource_name = obs.obs_data_get_string(settings, \"source\")\n\tstop_text = obs.obs_data_get_string(settings, \"stop_text\")\n\n\treset(true)\nend\n\n-- A function named script_defaults will be called to set the default settings\nfunction script_defaults(settings)\n\tobs.obs_data_set_default_int(settings, \"duration\", 5)\n\tobs.obs_data_set_default_string(settings, \"stop_text\", \"Starting soon (tm)\")\nend\n\n-- A function named script_save will be called when the script is saved\n--\n-- NOTE: This function is usually used for saving extra data (such as in this\n-- case, a hotkey's save data).  Settings set via the properties are saved\n-- automatically.\nfunction script_save(settings)\n\tlocal hotkey_save_array = obs.obs_hotkey_save(hotkey_id)\n\tobs.obs_data_set_array(settings, \"reset_hotkey\", hotkey_save_array)\n\tobs.obs_data_array_release(hotkey_save_array)\nend\n\n-- a function named script_load will be called on startup\nfunction script_load(settings)\n\t-- Connect hotkey and activation/deactivation signal callbacks\n\t--\n\t-- NOTE: These particular script callbacks do not necessarily have to\n\t-- be disconnected, as callbacks will automatically destroy themselves\n\t-- if the script is unloaded.  So there's no real need to manually\n\t-- disconnect callbacks that are intended to last until the script is\n\t-- unloaded.\n\tlocal sh = obs.obs_get_signal_handler()\n\tobs.signal_handler_connect(sh, \"source_activate\", source_activated)\n\tobs.signal_handler_connect(sh, \"source_deactivate\", source_deactivated)\n\n\thotkey_id = obs.obs_hotkey_register_frontend(\"reset_timer_thingy\", \"Reset Timer\", reset)\n\tlocal hotkey_save_array = obs.obs_data_get_array(settings, \"reset_hotkey\")\n\tobs.obs_hotkey_load(hotkey_id, hotkey_save_array)\n\tobs.obs_data_array_release(hotkey_save_array)\nend\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/scripts/instant-replay.lua",
    "content": "obs         = obslua\nsource_name = \"\"\nhotkey_id   = obs.OBS_INVALID_HOTKEY_ID\nattempts    = 0\nlast_replay = \"\"\n\n----------------------------------------------------------\n\nfunction try_play()\n\tlocal replay_buffer = obs.obs_frontend_get_replay_buffer_output()\n\tif replay_buffer == nil then\n\t\tobs.remove_current_callback()\n\t\treturn\n\tend\n\n\t-- Call the procedure of the replay buffer named \"get_last_replay\" to\n\t-- get the last replay created by the replay buffer\n\tlocal cd = obs.calldata_create()\n\tlocal ph = obs.obs_output_get_proc_handler(replay_buffer)\n\tobs.proc_handler_call(ph, \"get_last_replay\", cd)\n\tlocal path = obs.calldata_string(cd, \"path\")\n\tobs.calldata_destroy(cd)\n\n\tobs.obs_output_release(replay_buffer)\n\n\tif path == last_replay then\n\t\tpath = nil\n\tend\n\n\t-- If the path is valid and the source exists, update it with the\n\t-- replay file to play back the replay.  Otherwise, stop attempting to\n\t-- replay after 10 retries\n\tif path == nil then\n\t\tattempts = attempts + 1\n\t\tif attempts >= 10 then\n\t\t\tobs.remove_current_callback()\n\t\tend\n\telse\n\t\tlast_replay = path\n\t\tlocal source = obs.obs_get_source_by_name(source_name)\n\t\tif source ~= nil then\n\t\t\tlocal settings = obs.obs_data_create()\n\t\t\tsource_id = obs.obs_source_get_id(source)\n\t\t\tif source_id == \"ffmpeg_source\" then\n\t\t\t\tobs.obs_data_set_string(settings, \"local_file\", path)\n\t\t\t\tobs.obs_data_set_bool(settings, \"is_local_file\", true)\n\n\t\t\t\t-- updating will automatically cause the source to\n\t\t\t\t-- refresh if the source is currently active\n\t\t\t\tobs.obs_source_update(source, settings)\n\t\t\telseif source_id == \"vlc_source\" then\n\t\t\t\t-- \"playlist\"\n\t\t\t\tarray = obs.obs_data_array_create()\n\t\t\t\titem = obs.obs_data_create()\n\t\t\t\tobs.obs_data_set_string(item, \"value\", path)\n\t\t\t\tobs.obs_data_array_push_back(array, item)\n\t\t\t\tobs.obs_data_set_array(settings, \"playlist\", array)\n\n\t\t\t\t-- updating will automatically cause the source to\n\t\t\t\t-- refresh if the source is currently active\n\t\t\t\tobs.obs_source_update(source, settings)\n\t\t\t\tobs.obs_data_release(item)\n\t\t\t\tobs.obs_data_array_release(array)\n\t\t\tend\n\n\t\t\tobs.obs_data_release(settings)\n\t\t\tobs.obs_source_release(source)\n\t\tend\n\n\t\tobs.remove_current_callback()\n\tend\nend\n\n-- The \"Instant Replay\" hotkey callback\nfunction instant_replay(pressed)\n\tif not pressed then\n\t\treturn\n\tend\n\n\tlocal replay_buffer = obs.obs_frontend_get_replay_buffer_output()\n\tif replay_buffer ~= nil then\n\t\t-- Call the procedure of the replay buffer named \"get_last_replay\" to\n\t\t-- get the last replay created by the replay buffer\n\t\tlocal ph = obs.obs_output_get_proc_handler(replay_buffer)\n\t\tobs.proc_handler_call(ph, \"save\", nil)\n\n\t\t-- Set a 2-second timer to attempt playback every 1 second\n\t\t-- until the replay is available\n\t\tif obs.obs_output_active(replay_buffer) then\n\t\t\tattempts = 0\n\t\t\tobs.timer_add(try_play, 2000)\n\t\telse\n\t\t\tobs.script_log(obs.LOG_WARNING, \"Tried to save an instant replay, but the replay buffer is not active!\")\n\t\tend\n\n\t\tobs.obs_output_release(replay_buffer)\n\telse\n\t\tobs.script_log(obs.LOG_WARNING, \"Tried to save an instant replay, but found no active replay buffer!\")\n\tend\nend\n\n----------------------------------------------------------\n\n-- A function named script_update will be called when settings are changed\nfunction script_update(settings)\n\tsource_name = obs.obs_data_get_string(settings, \"source\")\nend\n\n-- A function named script_description returns the description shown to\n-- the user\nfunction script_description()\n\treturn \"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 Lain and Exeldro\"\nend\n\n-- A function named script_properties defines the properties that the user\n-- can change for the entire script module itself\nfunction script_properties()\n\tprops = obs.obs_properties_create()\n\n\tlocal p = obs.obs_properties_add_list(props, \"source\", \"Media Source\", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING)\n\tlocal sources = obs.obs_enum_sources()\n\tif sources ~= nil then\n\t\tfor _, source in ipairs(sources) do\n\t\t\tsource_id = obs.obs_source_get_id(source)\n\t\t\tif source_id == \"ffmpeg_source\" then\n\t\t\t\tlocal name = obs.obs_source_get_name(source)\n\t\t\t\tobs.obs_property_list_add_string(p, name, name)\n\t\t\telseif source_id == \"vlc_source\" then\n\t\t\t\tlocal name = obs.obs_source_get_name(source)\n\t\t\t\tobs.obs_property_list_add_string(p, name, name)\n\t\t\telse\n\t\t\t\t-- obs.script_log(obs.LOG_INFO, source_id)\n\t\t\tend\n\t\tend\n\tend\n\tobs.source_list_release(sources)\n\n\treturn props\nend\n\n-- A function named script_load will be called on startup\nfunction script_load(settings)\n\thotkey_id = obs.obs_hotkey_register_frontend(\"instant_replay.trigger\", \"Instant Replay\", instant_replay)\n\tlocal hotkey_save_array = obs.obs_data_get_array(settings, \"instant_replay.trigger\")\n\tobs.obs_hotkey_load(hotkey_id, hotkey_save_array)\n\tobs.obs_data_array_release(hotkey_save_array)\nend\n\n-- A function named script_save will be called when the script is saved\n--\n-- NOTE: This function is usually used for saving extra data (such as in this\n-- case, a hotkey's save data).  Settings set via the properties are saved\n-- automatically.\nfunction script_save(settings)\n\tlocal hotkey_save_array = obs.obs_hotkey_save(hotkey_id)\n\tobs.obs_data_set_array(settings, \"instant_replay.trigger\", hotkey_save_array)\n\tobs.obs_data_array_release(hotkey_save_array)\nend\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/scripts/pause-scene.lua",
    "content": "obs = obslua\npause_scene = \"\"\n\nfunction on_event(event)\n\tif event == obs.OBS_FRONTEND_EVENT_SCENE_CHANGED then\n\t\tlocal scene = obs.obs_frontend_get_current_scene()\n\t\tlocal scene_name = obs.obs_source_get_name(scene)\n\t\tif pause_scene == scene_name then\n\t\t\tobs.obs_frontend_recording_pause(true)\n\t\telse\n\t\t\tobs.obs_frontend_recording_pause(false)\n\t\tend\n\n\t\tobs.obs_source_release(scene);\n\tend\nend\n\nfunction script_properties()\n\tlocal props = obs.obs_properties_create()\n\n\tlocal p = obs.obs_properties_add_list(props, \"pause_scene\", \"Pause Scene\", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING)\n\tlocal scenes = obs.obs_frontend_get_scenes()\n\tif scenes ~= nil then\n\t\tfor _, scene in ipairs(scenes) do\n\t\t\tlocal name = obs.obs_source_get_name(scene);\n\t\t\tobs.obs_property_list_add_string(p, name, name)\n\t\tend\n\tend\n\tobs.source_list_release(scenes)\n\n\treturn props\nend\n\nfunction script_description()\n\treturn \"Adds the ability to pause recording when switching to a specific scene\"\nend\n\nfunction script_update(settings)\n\tpause_scene = obs.obs_data_get_string(settings, \"pause_scene\")\nend\n\nfunction script_load(settings)\n\tobs.obs_frontend_add_event_callback(on_event)\nend\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/data/scripts/url-text.py",
    "content": "import obspython as obs\nimport urllib.request\nimport urllib.error\n\nurl         = \"\"\ninterval    = 30\nsource_name = \"\"\n\n# ------------------------------------------------------------\n\ndef update_text():\n\tglobal url\n\tglobal interval\n\tglobal source_name\n\n\tsource = obs.obs_get_source_by_name(source_name)\n\tif source is not None:\n\t\ttry:\n\t\t\twith urllib.request.urlopen(url) as response:\n\t\t\t\tdata = response.read()\n\t\t\t\ttext = data.decode('utf-8')\n\n\t\t\t\tsettings = obs.obs_data_create()\n\t\t\t\tobs.obs_data_set_string(settings, \"text\", text)\n\t\t\t\tobs.obs_source_update(source, settings)\n\t\t\t\tobs.obs_data_release(settings)\n\n\t\texcept urllib.error.URLError as err:\n\t\t\tobs.script_log(obs.LOG_WARNING, \"Error opening URL '\" + url + \"': \" + err.reason)\n\t\t\tobs.remove_current_callback()\n\n\t\tobs.obs_source_release(source)\n\ndef refresh_pressed(props, prop):\n\tupdate_text()\n\n# ------------------------------------------------------------\n\ndef script_description():\n\treturn \"Updates a text source to the text retrieved from a URL at every specified interval.\\n\\nBy Lain\"\n\ndef script_update(settings):\n\tglobal url\n\tglobal interval\n\tglobal source_name\n\n\turl         = obs.obs_data_get_string(settings, \"url\")\n\tinterval    = obs.obs_data_get_int(settings, \"interval\")\n\tsource_name = obs.obs_data_get_string(settings, \"source\")\n\n\tobs.timer_remove(update_text)\n\n\tif url != \"\" and source_name != \"\":\n\t\tobs.timer_add(update_text, interval * 1000)\n\ndef script_defaults(settings):\n\tobs.obs_data_set_default_int(settings, \"interval\", 30)\n\ndef script_properties():\n\tprops = obs.obs_properties_create()\n\n\tobs.obs_properties_add_text(props, \"url\", \"URL\", obs.OBS_TEXT_DEFAULT)\n\tobs.obs_properties_add_int(props, \"interval\", \"Update Interval (seconds)\", 5, 3600, 1)\n\n\tp = obs.obs_properties_add_list(props, \"source\", \"Text Source\", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING)\n\tsources = obs.obs_enum_sources()\n\tif sources is not None:\n\t\tfor source in sources:\n\t\t\tsource_id = obs.obs_source_get_unversioned_id(source)\n\t\t\tif source_id == \"text_gdiplus\" or source_id == \"text_ft2_source\":\n\t\t\t\tname = obs.obs_source_get_name(source)\n\t\t\t\tobs.obs_property_list_add_string(p, name, name)\n\n\t\tobs.source_list_release(sources)\n\n\tobs.obs_properties_add_button(props, \"button\", \"Refresh\", refresh_pressed)\n\treturn props\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/forms/auto-scene-switcher.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SceneSwitcher</class>\n <widget class=\"QDialog\" name=\"SceneSwitcher\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>743</width>\n    <height>563</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>SceneSwitcher</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QComboBox\" name=\"windows\">\n       <property name=\"editable\">\n        <bool>true</bool>\n       </property>\n       <property name=\"maxVisibleItems\">\n        <number>20</number>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QComboBox\" name=\"scenes\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Maximum\" vsizetype=\"Fixed\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"minimumSize\">\n        <size>\n         <width>100</width>\n         <height>0</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QListWidget\" name=\"switches\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"spacing\">\n      <number>1</number>\n     </property>\n     <property name=\"sortingEnabled\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <property name=\"spacing\">\n      <number>4</number>\n     </property>\n     <item>\n      <widget class=\"QPushButton\" name=\"add\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>16777215</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"flat\">\n        <bool>true</bool>\n       </property>\n       <property name=\"class\" stdset=\"0\">\n        <string notr=\"true\">icon-plus</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"remove\">\n       <property name=\"maximumSize\">\n        <size>\n         <width>16777215</width>\n         <height>16777215</height>\n        </size>\n       </property>\n       <property name=\"flat\">\n        <bool>true</bool>\n       </property>\n       <property name=\"class\" stdset=\"0\">\n        <string notr=\"true\">icon-trash</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <property name=\"fieldGrowthPolicy\">\n      <enum>QFormLayout::ExpandingFieldsGrow</enum>\n     </property>\n     <property name=\"labelAlignment\">\n      <set>Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing</set>\n     </property>\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <property name=\"text\">\n        <string>SceneSwitcher.OnNoMatch</string>\n       </property>\n       <property name=\"alignment\">\n        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n       <item>\n        <widget class=\"QRadioButton\" name=\"noMatchDontSwitch\">\n         <property name=\"text\">\n          <string>SceneSwitcher.OnNoMatch.DontSwitch</string>\n         </property>\n         <property name=\"checked\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n         <property name=\"leftMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"topMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"rightMargin\">\n          <number>0</number>\n         </property>\n         <property name=\"bottomMargin\">\n          <number>0</number>\n         </property>\n         <item>\n          <widget class=\"QRadioButton\" name=\"noMatchSwitch\">\n           <property name=\"text\">\n            <string>SceneSwitcher.OnNoMatch.SwitchTo</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QComboBox\" name=\"noMatchSwitchScene\">\n           <property name=\"enabled\">\n            <bool>false</bool>\n           </property>\n           <property name=\"minimumSize\">\n            <size>\n             <width>100</width>\n             <height>0</height>\n            </size>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>SceneSwitcher.CheckInterval</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>checkInterval</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QSpinBox\" name=\"checkInterval\">\n       <property name=\"minimumSize\">\n        <size>\n         <width>100</width>\n         <height>0</height>\n        </size>\n       </property>\n       <property name=\"suffix\">\n        <string notr=\"true\">ms</string>\n       </property>\n       <property name=\"minimum\">\n        <number>50</number>\n       </property>\n       <property name=\"maximum\">\n        <number>20000</number>\n       </property>\n       <property name=\"value\">\n        <number>300</number>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_4\">\n       <property name=\"text\">\n        <string>SceneSwitcher.ActiveOrNotActive</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>200</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QPushButton\" name=\"toggleStartButton\">\n       <property name=\"text\">\n        <string>Start</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"1\">\n      <spacer name=\"verticalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Vertical</enum>\n       </property>\n       <property name=\"sizeType\">\n        <enum>QSizePolicy::Preferred</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>20</width>\n         <height>40</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLabel\" name=\"pluginRunningText\">\n       <property name=\"text\">\n        <string notr=\"true\">Not Active</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item alignment=\"Qt::AlignRight\">\n    <widget class=\"QPushButton\" name=\"close\">\n     <property name=\"text\">\n      <string>Close</string>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>noMatchSwitch</sender>\n   <signal>toggled(bool)</signal>\n   <receiver>noMatchSwitchScene</receiver>\n   <slot>setEnabled(bool)</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>286</x>\n     <y>347</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>483</x>\n     <y>352</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/forms/captions.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>CaptionsDialog</class>\n <widget class=\"QDialog\" name=\"CaptionsDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>519</width>\n    <height>162</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Captions</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <property name=\"labelAlignment\">\n      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>\n     </property>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Captions.AudioSource</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>source</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"source\">\n       <property name=\"insertPolicy\">\n        <enum>QComboBox::InsertAlphabetically</enum>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QCheckBox\" name=\"enable\">\n       <property name=\"text\">\n        <string>Enable</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>Basic.Settings.General.Language</string>\n       </property>\n       <property name=\"buddy\">\n        <cstring>language</cstring>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"language\"/>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"provider\">\n       <property name=\"insertPolicy\">\n        <enum>QComboBox::InsertAlphabetically</enum>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_3\">\n       <property name=\"text\">\n        <string>Captions.Provider</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <spacer name=\"verticalSpacer\">\n     <property name=\"orientation\">\n      <enum>Qt::Vertical</enum>\n     </property>\n     <property name=\"sizeHint\" stdset=\"0\">\n      <size>\n       <width>0</width>\n       <height>0</height>\n      </size>\n     </property>\n    </spacer>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"accept\">\n       <property name=\"text\">\n        <string>OK</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <spacer name=\"horizontalSpacer_2\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>accept</sender>\n   <signal>clicked()</signal>\n   <receiver>CaptionsDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>268</x>\n     <y>331</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>229</x>\n     <y>-11</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/forms/output-timer.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>OutputTimer</class>\n <widget class=\"QDialog\" name=\"OutputTimer\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>697</width>\n    <height>240</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>OutputTimer</string>\n  </property>\n  <layout class=\"QGridLayout\" name=\"timerLayout\">\n   <item row=\"0\" column=\"3\">\n    <widget class=\"QLabel\" name=\"hoursLabel\">\n     <property name=\"text\">\n      <string>Hours</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"2\">\n    <widget class=\"QSpinBox\" name=\"recordingTimerHours\">\n     <property name=\"minimum\">\n      <number>0</number>\n     </property>\n     <property name=\"maximum\">\n      <number>24</number>\n     </property>\n     <property name=\"value\">\n      <number>0</number>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"6\">\n    <widget class=\"QSpinBox\" name=\"streamingTimerSeconds\">\n     <property name=\"minimum\">\n      <number>0</number>\n     </property>\n     <property name=\"maximum\">\n      <number>59</number>\n     </property>\n     <property name=\"value\">\n      <number>30</number>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"8\">\n    <widget class=\"QPushButton\" name=\"outputTimerStream\">\n     <property name=\"text\">\n      <string>Start</string>\n     </property>\n     <property name=\"checkable\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"4\">\n    <widget class=\"QSpinBox\" name=\"recordingTimerMinutes\">\n     <property name=\"minimum\">\n      <number>0</number>\n     </property>\n     <property name=\"maximum\">\n      <number>59</number>\n     </property>\n     <property name=\"value\">\n      <number>0</number>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"1\">\n    <widget class=\"QLabel\" name=\"streamingLabel\">\n     <property name=\"text\">\n      <string>OutputTimer.Stream</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"4\">\n    <widget class=\"QSpinBox\" name=\"streamingTimerMinutes\">\n     <property name=\"maximum\">\n      <number>59</number>\n     </property>\n     <property name=\"value\">\n      <number>0</number>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"8\">\n    <widget class=\"QPushButton\" name=\"outputTimerRecord\">\n     <property name=\"text\">\n      <string>Start</string>\n     </property>\n     <property name=\"checkable\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item row=\"6\" column=\"1\">\n    <widget class=\"QLabel\" name=\"recordStoppingIn\">\n     <property name=\"text\">\n      <string>OutputTimer.Record.StoppingIn</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"7\">\n    <widget class=\"QLabel\" name=\"secondsLabel_2\">\n     <property name=\"text\">\n      <string>Seconds</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"2\" column=\"2\">\n    <widget class=\"QLabel\" name=\"streamTime\">\n     <property name=\"text\">\n      <string>00:00:00</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"5\">\n    <widget class=\"QLabel\" name=\"minutesLabel_2\">\n     <property name=\"text\">\n      <string>Minutes</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"2\">\n    <widget class=\"QSpinBox\" name=\"streamingTimerHours\">\n     <property name=\"minimum\">\n      <number>0</number>\n     </property>\n     <property name=\"maximum\">\n      <number>24</number>\n     </property>\n     <property name=\"value\">\n      <number>0</number>\n     </property>\n    </widget>\n   </item>\n   <item row=\"6\" column=\"2\">\n    <widget class=\"QLabel\" name=\"recordTime\">\n     <property name=\"text\">\n      <string>00:00:00</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"5\">\n    <widget class=\"QLabel\" name=\"minutesLabel\">\n     <property name=\"text\">\n      <string>Minutes</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"1\">\n    <widget class=\"QLabel\" name=\"recordingLabel\">\n     <property name=\"text\">\n      <string>OutputTimer.Record</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"6\">\n    <widget class=\"QSpinBox\" name=\"recordingTimerSeconds\">\n     <property name=\"minimum\">\n      <number>0</number>\n     </property>\n     <property name=\"maximum\">\n      <number>59</number>\n     </property>\n     <property name=\"value\">\n      <number>30</number>\n     </property>\n    </widget>\n   </item>\n   <item row=\"1\" column=\"1\" colspan=\"3\">\n    <widget class=\"QCheckBox\" name=\"autoStartStreamTimer\">\n     <property name=\"text\">\n      <string>OutputTimer.Stream.EnableEverytime</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"2\" column=\"1\">\n    <widget class=\"QLabel\" name=\"streamStoppingIn\">\n     <property name=\"text\">\n      <string>OutputTimer.Stream.StoppingIn</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"4\" column=\"1\" colspan=\"3\">\n    <widget class=\"QCheckBox\" name=\"autoStartRecordTimer\">\n     <property name=\"text\">\n      <string>OutputTimer.Record.EnableEverytime</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"7\" column=\"8\">\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Close</set>\n     </property>\n    </widget>\n   </item>\n   <item row=\"3\" column=\"3\">\n    <widget class=\"QLabel\" name=\"hoursLabel_2\">\n     <property name=\"text\">\n      <string>Hours</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"0\" column=\"7\">\n    <widget class=\"QLabel\" name=\"secondsLabel\">\n     <property name=\"text\">\n      <string>Seconds</string>\n     </property>\n    </widget>\n   </item>\n   <item row=\"5\" column=\"1\" colspan=\"3\">\n    <widget class=\"QCheckBox\" name=\"pauseRecordTimer\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"text\">\n      <string>OutputTimer.Record.PauseTimer</string>\n     </property>\n     <property name=\"checked\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <tabstops>\n  <tabstop>streamingTimerHours</tabstop>\n  <tabstop>streamingTimerMinutes</tabstop>\n  <tabstop>streamingTimerSeconds</tabstop>\n  <tabstop>outputTimerStream</tabstop>\n  <tabstop>autoStartStreamTimer</tabstop>\n  <tabstop>recordingTimerHours</tabstop>\n  <tabstop>recordingTimerMinutes</tabstop>\n  <tabstop>recordingTimerSeconds</tabstop>\n  <tabstop>outputTimerRecord</tabstop>\n  <tabstop>autoStartRecordTimer</tabstop>\n  <tabstop>pauseRecordTimer</tabstop>\n </tabstops>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/forms/scripts.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ScriptsTool</class>\n <widget class=\"QDialog\" name=\"ScriptsTool\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>775</width>\n    <height>492</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Scripts</string>\n  </property>\n  <property name=\"sizeGripEnabled\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <property name=\"tabBarAutoHide\">\n      <bool>true</bool>\n     </property>\n     <widget class=\"QWidget\" name=\"scriptsTab\">\n      <attribute name=\"title\">\n       <string>Scripts</string>\n      </attribute>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n         <item>\n          <widget class=\"QLabel\" name=\"label_2\">\n           <property name=\"text\">\n            <string>LoadedScripts</string>\n           </property>\n           <property name=\"buddy\">\n            <cstring>scripts</cstring>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QListWidget\" name=\"scripts\">\n           <property name=\"contextMenuPolicy\">\n            <enum>Qt::CustomContextMenu</enum>\n           </property>\n           <property name=\"spacing\">\n            <number>1</number>\n           </property>\n           <property name=\"sortingEnabled\">\n            <bool>true</bool>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n           <item>\n            <widget class=\"QPushButton\" name=\"addScripts\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"maximumSize\">\n              <size>\n               <width>16777215</width>\n               <height>16777215</height>\n              </size>\n             </property>\n             <property name=\"toolTip\">\n              <string>AddScripts</string>\n             </property>\n             <property name=\"accessibleName\">\n              <string>AddScripts</string>\n             </property>\n             <property name=\"text\">\n              <string notr=\"true\"/>\n             </property>\n             <property name=\"flat\">\n              <bool>true</bool>\n             </property>\n             <property name=\"class\" stdset=\"0\">\n              <string notr=\"true\">icon-plus</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QPushButton\" name=\"removeScripts\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"maximumSize\">\n              <size>\n               <width>16777215</width>\n               <height>16777215</height>\n              </size>\n             </property>\n             <property name=\"toolTip\">\n              <string>RemoveScripts</string>\n             </property>\n             <property name=\"accessibleName\">\n              <string>RemoveScripts</string>\n             </property>\n             <property name=\"text\">\n              <string notr=\"true\"/>\n             </property>\n             <property name=\"flat\">\n              <bool>true</bool>\n             </property>\n             <property name=\"class\" stdset=\"0\">\n              <string notr=\"true\">icon-trash</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QPushButton\" name=\"reloadScripts\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Fixed\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"maximumSize\">\n              <size>\n               <width>16777215</width>\n               <height>16777215</height>\n              </size>\n             </property>\n             <property name=\"toolTip\">\n              <string>ReloadScripts</string>\n             </property>\n             <property name=\"accessibleName\">\n              <string>ReloadScripts</string>\n             </property>\n             <property name=\"text\">\n              <string notr=\"true\"/>\n             </property>\n             <property name=\"flat\">\n              <bool>true</bool>\n             </property>\n             <property name=\"class\" stdset=\"0\">\n              <string notr=\"true\">icon-refresh</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <spacer name=\"horizontalSpacer_2\">\n             <property name=\"orientation\">\n              <enum>Qt::Horizontal</enum>\n             </property>\n             <property name=\"sizeHint\" stdset=\"0\">\n              <size>\n               <width>40</width>\n               <height>20</height>\n              </size>\n             </property>\n            </spacer>\n           </item>\n           <item>\n            <widget class=\"QPushButton\" name=\"defaults\">\n             <property name=\"text\">\n              <string>Defaults</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QPushButton\" name=\"editScript\">\n             <property name=\"text\">\n              <string>EditScript</string>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QPushButton\" name=\"scriptLog\">\n             <property name=\"text\">\n              <string>ScriptLogWindow</string>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QVBoxLayout\" name=\"propertiesLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"label_3\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Maximum\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"text\">\n            <string>Description</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"description\">\n           <property name=\"text\">\n            <string notr=\"true\"/>\n           </property>\n           <property name=\"wordWrap\">\n            <bool>true</bool>\n           </property>\n           <property name=\"margin\">\n            <number>12</number>\n           </property>\n           <property name=\"openExternalLinks\">\n            <bool>false</bool>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"pythonSettingsTab\">\n      <attribute name=\"title\">\n       <string>PythonSettings</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <widget class=\"QLabel\" name=\"pythonPathLabel\">\n         <property name=\"text\">\n          <string notr=\"true\"/>\n         </property>\n         <property name=\"buddy\">\n          <cstring>pythonPath</cstring>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n         <item>\n          <widget class=\"QLineEdit\" name=\"pythonPath\">\n           <property name=\"readOnly\">\n            <bool>true</bool>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"pythonPathBrowse\">\n           <property name=\"accessibleName\">\n            <string>PythonSettings.BrowsePythonPath</string>\n           </property>\n           <property name=\"text\">\n            <string>Browse</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"QLabel\" name=\"pythonVersionLabel\">\n         <property name=\"text\">\n          <string/>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <spacer name=\"verticalSpacer\">\n         <property name=\"orientation\">\n          <enum>Qt::Vertical</enum>\n         </property>\n         <property name=\"sizeHint\" stdset=\"0\">\n          <size>\n           <width>510</width>\n           <height>306</height>\n          </size>\n         </property>\n        </spacer>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <spacer name=\"horizontalSpacer\">\n       <property name=\"orientation\">\n        <enum>Qt::Horizontal</enum>\n       </property>\n       <property name=\"sizeHint\" stdset=\"0\">\n        <size>\n         <width>40</width>\n         <height>20</height>\n        </size>\n       </property>\n      </spacer>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"close\">\n       <property name=\"text\">\n        <string>Close</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <tabstops>\n  <tabstop>tabWidget</tabstop>\n  <tabstop>close</tabstop>\n  <tabstop>pythonPath</tabstop>\n  <tabstop>pythonPathBrowse</tabstop>\n </tabstops>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/frontend-tools-config.h.in",
    "content": "#pragma once\n\n#ifndef TRUE\n#define TRUE 1\n#endif\n\n#ifndef ON\n#define ON 1\n#endif\n\n#ifndef FALSE\n#define FALSE 0\n#endif\n\n#ifndef OFF\n#define OFF 0\n#endif\n\n#cmakedefine ENABLE_SCRIPTING\n#cmakedefine LUAJIT_FOUND\n#cmakedefine Python_FOUND\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/frontend-tools.c",
    "content": "#include <obs-module.h>\n\nOBS_DECLARE_MODULE()\nOBS_MODULE_USE_DEFAULT_LOCALE(\"frontend-tools\", \"en-US\")\n\nvoid InitSceneSwitcher();\nvoid FreeSceneSwitcher();\n\n#if defined(_WIN32)\nvoid InitCaptions();\nvoid FreeCaptions();\n#endif\n\nvoid InitOutputTimer();\nvoid FreeOutputTimer();\n\n#if defined(ENABLE_SCRIPTING)\nvoid InitScripts();\nvoid FreeScripts();\n#endif\n\nbool obs_module_load(void)\n{\n#if defined(_WIN32)\n\tInitCaptions();\n#endif\n\tInitSceneSwitcher();\n\tInitOutputTimer();\n#if defined(ENABLE_SCRIPTING)\n\tInitScripts();\n#endif\n\treturn true;\n}\n\nvoid obs_module_unload(void)\n{\n#if defined(_WIN32)\n\tFreeCaptions();\n#endif\n\tFreeSceneSwitcher();\n\tFreeOutputTimer();\n#if defined(ENABLE_SCRIPTING)\n\tFreeScripts();\n#endif\n}\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/output-timer.cpp",
    "content": "#include <obs-frontend-api.h>\n#include <obs-module.h>\n#include <obs.hpp>\n#include <util/util.hpp>\n#include <QAction>\n#include <QMainWindow>\n#include <QTimer>\n#include <QObject>\n#include \"output-timer.hpp\"\n\nusing namespace std;\n\nOutputTimer *ot;\n\nOutputTimer::OutputTimer(QWidget *parent) : QDialog(parent), ui(new Ui_OutputTimer)\n{\n\tui->setupUi(this);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tQObject::connect(ui->outputTimerStream, &QPushButton::clicked, this, &OutputTimer::StreamingTimerButton);\n\tQObject::connect(ui->outputTimerRecord, &QPushButton::clicked, this, &OutputTimer::RecordingTimerButton);\n\tQObject::connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this,\n\t\t\t &OutputTimer::hide);\n\n\tstreamingTimer = new QTimer(this);\n\tstreamingTimerDisplay = new QTimer(this);\n\n\trecordingTimer = new QTimer(this);\n\trecordingTimerDisplay = new QTimer(this);\n\n\tQObject::connect(streamingTimer, &QTimer::timeout, this, &OutputTimer::EventStopStreaming);\n\tQObject::connect(streamingTimerDisplay, &QTimer::timeout, this, &OutputTimer::UpdateStreamTimerDisplay);\n\tQObject::connect(recordingTimer, &QTimer::timeout, this, &OutputTimer::EventStopRecording);\n\tQObject::connect(recordingTimerDisplay, &QTimer::timeout, this, &OutputTimer::UpdateRecordTimerDisplay);\n}\n\nvoid OutputTimer::closeEvent(QCloseEvent *)\n{\n\tobs_frontend_save();\n}\n\nvoid OutputTimer::StreamingTimerButton()\n{\n\tif (!obs_frontend_streaming_active()) {\n\t\tblog(LOG_INFO, \"Starting stream due to OutputTimer\");\n\t\tobs_frontend_streaming_start();\n\t} else if (streamingAlreadyActive) {\n\t\tStreamTimerStart();\n\t\tstreamingAlreadyActive = false;\n\t} else if (obs_frontend_streaming_active()) {\n\t\tblog(LOG_INFO, \"Stopping stream due to OutputTimer\");\n\t\tobs_frontend_streaming_stop();\n\t}\n}\n\nvoid OutputTimer::RecordingTimerButton()\n{\n\tif (!obs_frontend_recording_active()) {\n\t\tblog(LOG_INFO, \"Starting recording due to OutputTimer\");\n\t\tobs_frontend_recording_start();\n\t} else if (recordingAlreadyActive) {\n\t\tRecordTimerStart();\n\t\trecordingAlreadyActive = false;\n\t} else if (obs_frontend_recording_active()) {\n\t\tblog(LOG_INFO, \"Stopping recording due to OutputTimer\");\n\t\tobs_frontend_recording_stop();\n\t}\n}\n\nvoid OutputTimer::StreamTimerStart()\n{\n\tif (!isVisible() && ui->autoStartStreamTimer->isChecked() == false) {\n\t\tstreamingAlreadyActive = true;\n\t\treturn;\n\t}\n\n\tint hours = ui->streamingTimerHours->value();\n\tint minutes = ui->streamingTimerMinutes->value();\n\tint seconds = ui->streamingTimerSeconds->value();\n\n\tint total = (((hours * 3600) + (minutes * 60)) + seconds) * 1000;\n\n\tif (total == 0)\n\t\ttotal = 1000;\n\n\tstreamingTimer->setInterval(total);\n\tstreamingTimer->setSingleShot(true);\n\n\tstreamingTimer->start();\n\tstreamingTimerDisplay->start(1000);\n\tui->outputTimerStream->setText(obs_module_text(\"Stop\"));\n\n\tUpdateStreamTimerDisplay();\n\n\tui->outputTimerStream->setChecked(true);\n}\n\nvoid OutputTimer::RecordTimerStart()\n{\n\tif (!isVisible() && ui->autoStartRecordTimer->isChecked() == false) {\n\t\trecordingAlreadyActive = true;\n\t\treturn;\n\t}\n\n\tint hours = ui->recordingTimerHours->value();\n\tint minutes = ui->recordingTimerMinutes->value();\n\tint seconds = ui->recordingTimerSeconds->value();\n\n\tint total = (((hours * 3600) + (minutes * 60)) + seconds) * 1000;\n\n\tif (total == 0)\n\t\ttotal = 1000;\n\n\trecordingTimer->setInterval(total);\n\trecordingTimer->setSingleShot(true);\n\n\trecordingTimer->start();\n\trecordingTimerDisplay->start(1000);\n\tui->outputTimerRecord->setText(obs_module_text(\"Stop\"));\n\n\tUpdateRecordTimerDisplay();\n\n\tui->outputTimerRecord->setChecked(true);\n}\n\nvoid OutputTimer::StreamTimerStop()\n{\n\tstreamingAlreadyActive = false;\n\n\tif (!isVisible() && streamingTimer->isActive() == false)\n\t\treturn;\n\n\tif (streamingTimer->isActive())\n\t\tstreamingTimer->stop();\n\n\tui->outputTimerStream->setText(obs_module_text(\"Start\"));\n\n\tif (streamingTimerDisplay->isActive())\n\t\tstreamingTimerDisplay->stop();\n\n\tui->streamTime->setText(\"00:00:00\");\n\tui->outputTimerStream->setChecked(false);\n}\n\nvoid OutputTimer::RecordTimerStop()\n{\n\trecordingAlreadyActive = false;\n\n\tif (!isVisible() && recordingTimer->isActive() == false)\n\t\treturn;\n\n\tif (recordingTimer->isActive())\n\t\trecordingTimer->stop();\n\n\tui->outputTimerRecord->setText(obs_module_text(\"Start\"));\n\n\tif (recordingTimerDisplay->isActive())\n\t\trecordingTimerDisplay->stop();\n\n\tui->recordTime->setText(\"00:00:00\");\n\tui->outputTimerRecord->setChecked(false);\n}\n\nvoid OutputTimer::UpdateStreamTimerDisplay()\n{\n\tint remainingTime = streamingTimer->remainingTime() / 1000;\n\n\tint seconds = remainingTime % 60;\n\tint minutes = (remainingTime % 3600) / 60;\n\tint hours = remainingTime / 3600;\n\n\tQString text = QString::asprintf(\"%02d:%02d:%02d\", hours, minutes, seconds);\n\tui->streamTime->setText(text);\n}\n\nvoid OutputTimer::UpdateRecordTimerDisplay()\n{\n\tint remainingTime = 0;\n\n\tif (obs_frontend_recording_paused() && ui->pauseRecordTimer->isChecked())\n\t\tremainingTime = recordingTimeLeft / 1000;\n\telse\n\t\tremainingTime = recordingTimer->remainingTime() / 1000;\n\n\tint seconds = remainingTime % 60;\n\tint minutes = (remainingTime % 3600) / 60;\n\tint hours = remainingTime / 3600;\n\n\tQString text = QString::asprintf(\"%02d:%02d:%02d\", hours, minutes, seconds);\n\tui->recordTime->setText(text);\n}\n\nvoid OutputTimer::PauseRecordingTimer()\n{\n\tif (!ui->pauseRecordTimer->isChecked())\n\t\treturn;\n\n\tif (recordingTimer->isActive()) {\n\t\trecordingTimeLeft = recordingTimer->remainingTime();\n\t\trecordingTimer->stop();\n\t}\n}\n\nvoid OutputTimer::UnpauseRecordingTimer()\n{\n\tif (!ui->pauseRecordTimer->isChecked())\n\t\treturn;\n\n\tif (recordingTimeLeft > 0 && !recordingTimer->isActive())\n\t\trecordingTimer->start(recordingTimeLeft);\n}\n\nvoid OutputTimer::ShowHideDialog()\n{\n\tif (!isVisible()) {\n\t\tsetVisible(true);\n\t\tQTimer::singleShot(250, this, &OutputTimer::show);\n\t} else {\n\t\tsetVisible(false);\n\t\tQTimer::singleShot(250, this, &OutputTimer::hide);\n\t}\n}\n\nvoid OutputTimer::EventStopStreaming()\n{\n\tblog(LOG_INFO, \"Stopping stream due to OutputTimer timeout\");\n\tobs_frontend_streaming_stop();\n}\n\nvoid OutputTimer::EventStopRecording()\n{\n\tblog(LOG_INFO, \"Stopping recording due to OutputTimer timeout\");\n\tobs_frontend_recording_stop();\n}\n\nstatic void SaveOutputTimer(obs_data_t *save_data, bool saving, void *)\n{\n\tif (saving) {\n\t\tOBSDataAutoRelease obj = obs_data_create();\n\n\t\tobs_data_set_int(obj, \"streamTimerHours\", ot->ui->streamingTimerHours->value());\n\t\tobs_data_set_int(obj, \"streamTimerMinutes\", ot->ui->streamingTimerMinutes->value());\n\t\tobs_data_set_int(obj, \"streamTimerSeconds\", ot->ui->streamingTimerSeconds->value());\n\n\t\tobs_data_set_int(obj, \"recordTimerHours\", ot->ui->recordingTimerHours->value());\n\t\tobs_data_set_int(obj, \"recordTimerMinutes\", ot->ui->recordingTimerMinutes->value());\n\t\tobs_data_set_int(obj, \"recordTimerSeconds\", ot->ui->recordingTimerSeconds->value());\n\n\t\tobs_data_set_bool(obj, \"autoStartStreamTimer\", ot->ui->autoStartStreamTimer->isChecked());\n\t\tobs_data_set_bool(obj, \"autoStartRecordTimer\", ot->ui->autoStartRecordTimer->isChecked());\n\n\t\tobs_data_set_bool(obj, \"pauseRecordTimer\", ot->ui->pauseRecordTimer->isChecked());\n\n\t\tobs_data_set_obj(save_data, \"output-timer\", obj);\n\t} else {\n\t\tOBSDataAutoRelease obj = obs_data_get_obj(save_data, \"output-timer\");\n\n\t\tif (!obj)\n\t\t\tobj = obs_data_create();\n\n\t\tot->ui->streamingTimerHours->setValue(obs_data_get_int(obj, \"streamTimerHours\"));\n\t\tot->ui->streamingTimerMinutes->setValue(obs_data_get_int(obj, \"streamTimerMinutes\"));\n\t\tot->ui->streamingTimerSeconds->setValue(obs_data_get_int(obj, \"streamTimerSeconds\"));\n\n\t\tot->ui->recordingTimerHours->setValue(obs_data_get_int(obj, \"recordTimerHours\"));\n\t\tot->ui->recordingTimerMinutes->setValue(obs_data_get_int(obj, \"recordTimerMinutes\"));\n\t\tot->ui->recordingTimerSeconds->setValue(obs_data_get_int(obj, \"recordTimerSeconds\"));\n\n\t\tot->ui->autoStartStreamTimer->setChecked(obs_data_get_bool(obj, \"autoStartStreamTimer\"));\n\t\tot->ui->autoStartRecordTimer->setChecked(obs_data_get_bool(obj, \"autoStartRecordTimer\"));\n\n\t\tot->ui->pauseRecordTimer->setChecked(obs_data_get_bool(obj, \"pauseRecordTimer\"));\n\t}\n}\n\nextern \"C\" void FreeOutputTimer() {}\n\nstatic void OBSEvent(enum obs_frontend_event event, void *)\n{\n\tif (event == OBS_FRONTEND_EVENT_EXIT) {\n\t\tobs_frontend_save();\n\t\tFreeOutputTimer();\n\t} else if (event == OBS_FRONTEND_EVENT_STREAMING_STARTED) {\n\t\tot->StreamTimerStart();\n\t} else if (event == OBS_FRONTEND_EVENT_STREAMING_STOPPING) {\n\t\tot->StreamTimerStop();\n\t} else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTED) {\n\t\tot->RecordTimerStart();\n\t} else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPING) {\n\t\tot->RecordTimerStop();\n\t} else if (event == OBS_FRONTEND_EVENT_RECORDING_PAUSED) {\n\t\tot->PauseRecordingTimer();\n\t} else if (event == OBS_FRONTEND_EVENT_RECORDING_UNPAUSED) {\n\t\tot->UnpauseRecordingTimer();\n\t}\n}\n\nextern \"C\" void InitOutputTimer()\n{\n\tQAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text(\"OutputTimer\"));\n\n\tobs_frontend_push_ui_translation(obs_module_get_string);\n\n\tQMainWindow *window = (QMainWindow *)obs_frontend_get_main_window();\n\n\tot = new OutputTimer(window);\n\n\tauto cb = []() {\n\t\tot->ShowHideDialog();\n\t};\n\n\tobs_frontend_pop_ui_translation();\n\n\tobs_frontend_add_save_callback(SaveOutputTimer, nullptr);\n\tobs_frontend_add_event_callback(OBSEvent, nullptr);\n\n\tQObject::connect(action, &QAction::triggered, action, cb);\n}\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/output-timer.hpp",
    "content": "#pragma once\n\n#include <QDialog>\n#include <memory>\n\n#include \"ui_output-timer.h\"\n\nclass QCloseEvent;\n\nclass OutputTimer : public QDialog {\n\tQ_OBJECT\n\npublic:\n\tstd::unique_ptr<Ui_OutputTimer> ui;\n\tOutputTimer(QWidget *parent);\n\n\tvoid closeEvent(QCloseEvent *event) override;\n\tvoid PauseRecordingTimer();\n\tvoid UnpauseRecordingTimer();\n\npublic slots:\n\tvoid StreamingTimerButton();\n\tvoid RecordingTimerButton();\n\tvoid StreamTimerStart();\n\tvoid RecordTimerStart();\n\tvoid StreamTimerStop();\n\tvoid RecordTimerStop();\n\tvoid UpdateStreamTimerDisplay();\n\tvoid UpdateRecordTimerDisplay();\n\tvoid ShowHideDialog();\n\tvoid EventStopStreaming();\n\tvoid EventStopRecording();\n\nprivate:\n\tbool streamingAlreadyActive = false;\n\tbool recordingAlreadyActive = false;\n\n\tQTimer *streamingTimer;\n\tQTimer *recordingTimer;\n\tQTimer *streamingTimerDisplay;\n\tQTimer *recordingTimerDisplay;\n\n\tint recordingTimeLeft = -1;\n};\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/scripts.cpp",
    "content": "#include \"obs-module.h\"\n#include \"scripts.hpp\"\n\n#include <QFileDialog>\n#include <QHBoxLayout>\n#include <QVBoxLayout>\n#include <QScrollBar>\n#include <QPushButton>\n#include <QFontDatabase>\n#include <QFont>\n#include <QDialogButtonBox>\n#include <QResizeEvent>\n#include <QAction>\n#include <QMessageBox>\n#include <QMenu>\n#include <QUrl>\n#include <QDesktopServices>\n#include <qt-wrappers.hpp>\n#include <plain-text-edit.hpp>\n#include <properties-view.hpp>\n\n#include <obs.hpp>\n#include <obs-module.h>\n#include <obs-frontend-api.h>\n#include <obs-scripting.h>\n\n#include <util/config-file.h>\n#include <util/platform.h>\n#include <util/util.hpp>\n\n#include <string>\n\n#include \"ui_scripts.h\"\n\n#if defined(Python_FOUND) && (defined(_WIN32) || defined(__APPLE__))\n#define PYTHON_UI 1\n#else\n#define PYTHON_UI 0\n#endif\n\n#if ARCH_BITS == 64\n#define ARCH_NAME \"64bit\"\n#else\n#define ARCH_NAME \"32bit\"\n#endif\n\n#define PYTHONPATH_LABEL_TEXT \"PythonSettings.PythonInstallPath\" ARCH_NAME\n\n/* ----------------------------------------------------------------- */\n\nusing OBSScript = OBSPtr<obs_script_t *, obs_script_destroy>;\n\nstruct ScriptData {\n\tstd::vector<OBSScript> scripts;\n\n\tinline obs_script_t *FindScript(const char *path)\n\t{\n\t\tfor (OBSScript &script : scripts) {\n\t\t\tconst char *script_path = obs_script_get_path(script);\n\t\t\tif (strcmp(script_path, path) == 0) {\n\t\t\t\treturn script;\n\t\t\t}\n\t\t}\n\n\t\treturn nullptr;\n\t}\n\n\tbool ScriptOpened(const char *path)\n\t{\n\t\tfor (OBSScript &script : scripts) {\n\t\t\tconst char *script_path = obs_script_get_path(script);\n\t\t\tif (strcmp(script_path, path) == 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n};\n\nstatic ScriptData *scriptData = nullptr;\nstatic ScriptsTool *scriptsWindow = nullptr;\nstatic ScriptLogWindow *scriptLogWindow = nullptr;\nstatic OBSPlainTextEdit *scriptLogWidget = nullptr;\n\n/* ----------------------------------------------------------------- */\n\nScriptLogWindow::ScriptLogWindow() : QDialog(nullptr)\n{\n\tOBSPlainTextEdit *edit = new OBSPlainTextEdit();\n\tedit->setReadOnly(true);\n\tedit->setWordWrapMode(QTextOption::NoWrap);\n\n\tQHBoxLayout *buttonLayout = new QHBoxLayout();\n\tQPushButton *clearButton = new QPushButton(tr(\"Clear\"));\n\tconnect(clearButton, &QPushButton::clicked, this, &ScriptLogWindow::ClearWindow);\n\tQPushButton *closeButton = new QPushButton(tr(\"Close\"));\n\tconnect(closeButton, &QPushButton::clicked, this, &QDialog::hide);\n\n\tbuttonLayout->addStretch();\n\tbuttonLayout->addWidget(clearButton);\n\tbuttonLayout->addWidget(closeButton);\n\n\tQVBoxLayout *layout = new QVBoxLayout();\n\tlayout->addWidget(edit);\n\tlayout->addLayout(buttonLayout);\n\n\tsetLayout(layout);\n\tscriptLogWidget = edit;\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tresize(600, 400);\n\n\tconfig_t *global_config = obs_frontend_get_user_config();\n\tconst char *geom = config_get_string(global_config, \"ScriptLogWindow\", \"geometry\");\n\tif (geom != nullptr) {\n\t\tQByteArray ba = QByteArray::fromBase64(QByteArray(geom));\n\t\trestoreGeometry(ba);\n\t}\n\n\tsetWindowTitle(obs_module_text(\"ScriptLogWindow\"));\n\n\tconnect(edit->verticalScrollBar(), &QAbstractSlider::sliderMoved, this, &ScriptLogWindow::ScrollChanged);\n}\n\nScriptLogWindow::~ScriptLogWindow()\n{\n\tconfig_t *global_config = obs_frontend_get_user_config();\n\tconfig_set_string(global_config, \"ScriptLogWindow\", \"geometry\", saveGeometry().toBase64().constData());\n}\n\nvoid ScriptLogWindow::ScrollChanged(int val)\n{\n\tQScrollBar *scroll = scriptLogWidget->verticalScrollBar();\n\tbottomScrolled = (val == scroll->maximum());\n}\n\nvoid ScriptLogWindow::resizeEvent(QResizeEvent *event)\n{\n\tQWidget::resizeEvent(event);\n\n\tif (bottomScrolled) {\n\t\tQScrollBar *scroll = scriptLogWidget->verticalScrollBar();\n\t\tscroll->setValue(scroll->maximum());\n\t}\n}\n\nvoid ScriptLogWindow::AddLogMsg(int log_level, QString msg)\n{\n\tQScrollBar *scroll = scriptLogWidget->verticalScrollBar();\n\tbottomScrolled = scroll->value() == scroll->maximum();\n\n\tlines += QStringLiteral(\"\\n\");\n\tlines += msg;\n\tscriptLogWidget->setPlainText(lines);\n\n\tif (bottomScrolled)\n\t\tscroll->setValue(scroll->maximum());\n\n\tif (log_level <= LOG_WARNING) {\n\t\tshow();\n\t\traise();\n\t}\n}\n\nvoid ScriptLogWindow::ClearWindow()\n{\n\tClear();\n\tscriptLogWidget->setPlainText(QString());\n}\n\nvoid ScriptLogWindow::Clear()\n{\n\tlines.clear();\n}\n\n/* ----------------------------------------------------------------- */\n\nScriptsTool::ScriptsTool() : QDialog(nullptr), ui(new Ui_ScriptsTool)\n{\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\tRefreshLists();\n\n#if PYTHON_UI\n\tconfig_t *config = obs_frontend_get_user_config();\n\tconst char *path = config_get_string(config, \"Python\", \"Path\" ARCH_NAME);\n\tui->pythonPath->setText(path);\n\tui->pythonPathLabel->setText(obs_module_text(PYTHONPATH_LABEL_TEXT));\n\tupdatePythonVersionLabel();\n#else\n\tdelete ui->pythonSettingsTab;\n\tui->pythonSettingsTab = nullptr;\n\tui->tabWidget->setStyleSheet(\"QTabWidget::pane {border: 0;}\");\n#endif\n\n\tdelete propertiesView;\n\tpropertiesView = new QWidget();\n\tpropertiesView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n\tui->propertiesLayout->addWidget(propertiesView);\n\n\tconfig_t *user_config = obs_frontend_get_user_config();\n\tint row = config_get_int(user_config, \"scripts-tool\", \"prevScriptRow\");\n\tui->scripts->setCurrentRow(row);\n}\n\nScriptsTool::~ScriptsTool()\n{\n\tconfig_t *user_config = obs_frontend_get_user_config();\n\tconfig_set_int(user_config, \"scripts-tool\", \"prevScriptRow\", ui->scripts->currentRow());\n}\n\nvoid ScriptsTool::updatePythonVersionLabel()\n{\n\tQString label;\n\tif (obs_scripting_python_loaded()) {\n\t\tchar version[8];\n\t\tobs_scripting_python_version(version, sizeof(version));\n\t\tlabel = QString(obs_module_text(\"PythonSettings.PythonVersion\")).arg(version);\n\t} else {\n\t\tlabel = obs_module_text(\"PythonSettings.PythonNotLoaded\");\n\t}\n\tui->pythonVersionLabel->setText(label);\n}\n\nvoid ScriptsTool::RemoveScript(const char *path)\n{\n\tfor (size_t i = 0; i < scriptData->scripts.size(); i++) {\n\t\tOBSScript &script = scriptData->scripts[i];\n\n\t\tconst char *script_path = obs_script_get_path(script);\n\t\tif (strcmp(script_path, path) == 0) {\n\t\t\tscriptData->scripts.erase(scriptData->scripts.begin() + i);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid ScriptsTool::ReloadScript(const char *path)\n{\n\tfor (OBSScript &script : scriptData->scripts) {\n\t\tconst char *script_path = obs_script_get_path(script);\n\t\tif (strcmp(script_path, path) == 0) {\n\t\t\tobs_script_reload(script);\n\n\t\t\tOBSDataAutoRelease settings = obs_script_get_settings(script);\n\n\t\t\tobs_properties_t *prop = obs_script_get_properties(script);\n\t\t\tobs_properties_apply_settings(prop, settings);\n\t\t\tobs_properties_destroy(prop);\n\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid ScriptsTool::RefreshLists()\n{\n\tui->scripts->clear();\n\n\tfor (OBSScript &script : scriptData->scripts) {\n\t\tconst char *script_file = obs_script_get_file(script);\n\t\tconst char *script_path = obs_script_get_path(script);\n\n\t\tQListWidgetItem *item = new QListWidgetItem(script_file);\n\t\titem->setData(Qt::UserRole, QString(script_path));\n\t\tui->scripts->addItem(item);\n\t}\n}\n\nvoid ScriptsTool::SetScriptDefaults(const char *path)\n{\n\tfor (OBSScript &script : scriptData->scripts) {\n\t\tconst char *script_path = obs_script_get_path(script);\n\t\tif (strcmp(script_path, path) == 0) {\n\t\t\tOBSDataAutoRelease settings = obs_script_get_settings(script);\n\t\t\tobs_data_clear(settings);\n\n\t\t\tobs_script_update(script, nullptr);\n\t\t\ton_reloadScripts_clicked();\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid ScriptsTool::on_close_clicked()\n{\n\tclose();\n}\n\nvoid ScriptsTool::on_addScripts_clicked()\n{\n\tconst char **formats = obs_scripting_supported_formats();\n\tconst char **cur_format = formats;\n\tQString extensions;\n\tQString filter;\n\n\twhile (*cur_format) {\n\t\tif (!extensions.isEmpty())\n\t\t\textensions += QStringLiteral(\" \");\n\n\t\textensions += QStringLiteral(\"*.\");\n\t\textensions += *cur_format;\n\n\t\tcur_format++;\n\t}\n\n\tif (!extensions.isEmpty()) {\n\t\tfilter += obs_module_text(\"FileFilter.ScriptFiles\");\n\t\tfilter += QStringLiteral(\" (\");\n\t\tfilter += extensions;\n\t\tfilter += QStringLiteral(\")\");\n\t}\n\n\tif (filter.isEmpty())\n\t\treturn;\n\n\tstatic std::string lastBrowsedDir;\n\n\tif (lastBrowsedDir.empty()) {\n\t\tBPtr<char> baseScriptPath = obs_module_file(\"scripts\");\n\t\tlastBrowsedDir = baseScriptPath;\n\t}\n\n\tQStringList files =\n\t\tOpenFiles(this, QT_UTF8(obs_module_text(\"AddScripts\")), QT_UTF8(lastBrowsedDir.c_str()), filter);\n\tif (!files.count())\n\t\treturn;\n\n\tfor (const QString &file : files) {\n\t\tlastBrowsedDir = QFileInfo(file).absolutePath().toUtf8().constData();\n\n\t\tQByteArray pathBytes = file.toUtf8();\n\t\tconst char *path = pathBytes.constData();\n\n\t\tif (scriptData->ScriptOpened(path)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tobs_script_t *script = obs_script_create(path, NULL);\n\t\tif (script) {\n\t\t\tconst char *script_file = obs_script_get_file(script);\n\n\t\t\tscriptData->scripts.emplace_back(script);\n\n\t\t\tQListWidgetItem *item = new QListWidgetItem(script_file);\n\t\t\titem->setData(Qt::UserRole, QString(file));\n\t\t\tui->scripts->addItem(item);\n\n\t\t\tOBSDataAutoRelease settings = obs_script_get_settings(script);\n\n\t\t\tobs_properties_t *prop = obs_script_get_properties(script);\n\t\t\tobs_properties_apply_settings(prop, settings);\n\t\t\tobs_properties_destroy(prop);\n\n\t\t\tui->scripts->setCurrentItem(item);\n\t\t}\n\t}\n}\n\nvoid ScriptsTool::on_removeScripts_clicked()\n{\n\tQList<QListWidgetItem *> items = ui->scripts->selectedItems();\n\n\tfor (QListWidgetItem *item : items)\n\t\tRemoveScript(item->data(Qt::UserRole).toString().toUtf8().constData());\n\tRefreshLists();\n}\n\nvoid ScriptsTool::on_reloadScripts_clicked()\n{\n\tQList<QListWidgetItem *> items = ui->scripts->selectedItems();\n\tfor (QListWidgetItem *item : items)\n\t\tReloadScript(item->data(Qt::UserRole).toString().toUtf8().constData());\n\n\ton_scripts_currentRowChanged(ui->scripts->currentRow());\n}\n\nvoid ScriptsTool::OpenScriptParentDirectory()\n{\n\tQList<QListWidgetItem *> items = ui->scripts->selectedItems();\n\tfor (QListWidgetItem *item : items) {\n\t\tQDir dir(item->data(Qt::UserRole).toString());\n\t\tdir.cdUp();\n\t\tQDesktopServices::openUrl(QUrl::fromLocalFile(dir.absolutePath()));\n\t}\n}\n\nvoid ScriptsTool::on_scripts_customContextMenuRequested(const QPoint &pos)\n{\n\n\tQListWidgetItem *item = ui->scripts->itemAt(pos);\n\n\tQMenu popup(this);\n\n\tobs_frontend_push_ui_translation(obs_module_get_string);\n\n\tpopup.addAction(tr(\"Add\"), this, &ScriptsTool::on_addScripts_clicked);\n\n\tif (item) {\n\t\tpopup.addSeparator();\n\t\tpopup.addAction(obs_module_text(\"Reload\"), this, &ScriptsTool::on_reloadScripts_clicked);\n\t\tpopup.addAction(obs_module_text(\"OpenFileLocation\"), this, &ScriptsTool::OpenScriptParentDirectory);\n\t\tpopup.addSeparator();\n\t\tpopup.addAction(tr(\"Remove\"), this, &ScriptsTool::on_removeScripts_clicked);\n\t}\n\tobs_frontend_pop_ui_translation();\n\n\tpopup.exec(QCursor::pos());\n}\n\nvoid ScriptsTool::on_editScript_clicked()\n{\n\tint row = ui->scripts->currentRow();\n\tif (row == -1)\n\t\treturn;\n\tQUrl url = QUrl::fromLocalFile(ui->scripts->item(row)->data(Qt::UserRole).toString());\n\tQDesktopServices::openUrl(url);\n}\n\nvoid ScriptsTool::on_scriptLog_clicked()\n{\n\tscriptLogWindow->show();\n\tscriptLogWindow->raise();\n}\n\nvoid ScriptsTool::on_pythonPathBrowse_clicked()\n{\n\tQString curPath = ui->pythonPath->text();\n\tQString newPath = SelectDirectory(this, ui->pythonPathLabel->text(), curPath);\n\n\tif (newPath.isEmpty())\n\t\treturn;\n\n\tQByteArray array = newPath.toUtf8();\n\tconst char *path = array.constData();\n\n\tconfig_t *config = obs_frontend_get_user_config();\n\tconfig_set_string(config, \"Python\", \"Path\" ARCH_NAME, path);\n\n\tui->pythonPath->setText(newPath);\n\n\tbool loaded = obs_scripting_python_loaded();\n\n\tif (loaded && !newPath.isEmpty() && curPath.compare(newPath) != 0) {\n\t\tchar version[8];\n\t\tobs_scripting_python_version(version, sizeof(version));\n\t\tQString message = QString(obs_module_text(\"PythonSettings.AlreadyLoaded.Message\")).arg(version);\n\t\tOBSMessageBox::information(this, obs_module_text(\"PythonSettings.AlreadyLoaded.Title\"), message);\n\t\treturn;\n\t} else if (loaded) {\n\t\treturn;\n\t}\n\n\tif (!obs_scripting_load_python(path))\n\t\treturn;\n\n\tupdatePythonVersionLabel();\n\n\tfor (OBSScript &script : scriptData->scripts) {\n\t\tenum obs_script_lang lang = obs_script_get_lang(script);\n\t\tif (lang == OBS_SCRIPT_LANG_PYTHON) {\n\t\t\tobs_script_reload(script);\n\t\t}\n\t}\n\n\ton_scripts_currentRowChanged(ui->scripts->currentRow());\n}\n\nvoid ScriptsTool::on_scripts_currentRowChanged(int row)\n{\n\tui->propertiesLayout->removeWidget(propertiesView);\n\tdelete propertiesView;\n\n\tif (row == -1) {\n\t\tpropertiesView = new QWidget();\n\t\tpropertiesView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n\t\tui->propertiesLayout->addWidget(propertiesView);\n\t\tui->description->setText(QString());\n\t\treturn;\n\t}\n\n\tQByteArray array = ui->scripts->item(row)->data(Qt::UserRole).toString().toUtf8();\n\tconst char *path = array.constData();\n\n\tobs_script_t *script = scriptData->FindScript(path);\n\tif (!script) {\n\t\tpropertiesView = nullptr;\n\t\treturn;\n\t}\n\n\tOBSDataAutoRelease settings = obs_script_get_settings(script);\n\n\tOBSPropertiesView *view = new OBSPropertiesView(settings.Get(), script,\n\t\t\t\t\t\t\t(PropertiesReloadCallback)obs_script_get_properties, nullptr,\n\t\t\t\t\t\t\t(PropertiesVisualUpdateCb)obs_script_update);\n\tview->SetDeferrable(false);\n\n\tpropertiesView = view;\n\n\tui->propertiesLayout->addWidget(propertiesView);\n\tui->description->setText(obs_script_get_description(script));\n}\n\nvoid ScriptsTool::on_defaults_clicked()\n{\n\tQListWidgetItem *item = ui->scripts->currentItem();\n\tif (!item)\n\t\treturn;\n\n\tSetScriptDefaults(item->data(Qt::UserRole).toString().toUtf8().constData());\n}\n\nvoid ScriptsTool::on_description_linkActivated(const QString &link)\n{\n\tQUrl url(link, QUrl::StrictMode);\n\tif (url.isValid() && (url.scheme().compare(\"http\") == 0 || url.scheme().compare(\"https\") == 0)) {\n\t\tQString msg(obs_module_text(\"ScriptDescriptionLink.Text\"));\n\t\tmsg += \"\\n\\n\";\n\t\tmsg += QString(obs_module_text(\"ScriptDescriptionLink.Text.Url\")).arg(link);\n\n\t\tconst char *open = obs_module_text(\"ScriptDescriptionLink.OpenURL\");\n\n\t\tQMessageBox messageBox(this);\n\t\tmessageBox.setWindowTitle(open);\n\t\tmessageBox.setText(msg);\n\n\t\tobs_frontend_push_ui_translation(obs_module_get_string);\n\t\tQPushButton *yesButton = messageBox.addButton(open, QMessageBox::YesRole);\n\t\tQPushButton *noButton = messageBox.addButton(tr(\"Cancel\"), QMessageBox::NoRole);\n\t\tobs_frontend_pop_ui_translation();\n\t\tmessageBox.setDefaultButton(yesButton);\n\t\tmessageBox.setEscapeButton(noButton);\n\t\tmessageBox.setIcon(QMessageBox::Question);\n\t\tmessageBox.exec();\n\n\t\tif (messageBox.clickedButton() == yesButton)\n\t\t\tQDesktopServices::openUrl(url);\n\t}\n}\n\n/* ----------------------------------------------------------------- */\n\nextern \"C\" void FreeScripts()\n{\n\tobs_scripting_unload();\n}\n\nstatic void obs_event(enum obs_frontend_event event, void *)\n{\n\tif (event == OBS_FRONTEND_EVENT_EXIT) {\n\t\tdelete scriptData;\n\t\tdelete scriptsWindow;\n\t\tdelete scriptLogWindow;\n\n\t\tscriptData = nullptr;\n\t\tscriptsWindow = nullptr;\n\t\tscriptLogWindow = nullptr;\n\n\t} else if (event == OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP) {\n\t\tif (scriptLogWindow) {\n\t\t\tscriptLogWindow->hide();\n\t\t\tscriptLogWindow->Clear();\n\t\t}\n\n\t\tdelete scriptData;\n\t\tscriptData = new ScriptData;\n\t}\n}\n\nstatic void load_script_data(obs_data_t *load_data, bool, void *)\n{\n\tOBSDataArrayAutoRelease array = obs_data_get_array(load_data, \"scripts-tool\");\n\n\tdelete scriptData;\n\tscriptData = new ScriptData;\n\n\tsize_t size = obs_data_array_count(array);\n\tfor (size_t i = 0; i < size; i++) {\n\t\tOBSDataAutoRelease obj = obs_data_array_item(array, i);\n\t\tconst char *path = obs_data_get_string(obj, \"path\");\n\t\tOBSDataAutoRelease settings = obs_data_get_obj(obj, \"settings\");\n\n\t\tobs_script_t *script = obs_script_create(path, settings);\n\t\tif (script) {\n\t\t\tscriptData->scripts.emplace_back(script);\n\t\t}\n\t}\n\n\tif (scriptsWindow)\n\t\tscriptsWindow->RefreshLists();\n}\n\nstatic void save_script_data(obs_data_t *save_data, bool saving, void *)\n{\n\tif (!saving)\n\t\treturn;\n\n\tOBSDataArrayAutoRelease array = obs_data_array_create();\n\n\tfor (OBSScript &script : scriptData->scripts) {\n\t\tconst char *script_path = obs_script_get_path(script);\n\t\tOBSDataAutoRelease settings = obs_script_save(script);\n\n\t\tOBSDataAutoRelease obj = obs_data_create();\n\t\tobs_data_set_string(obj, \"path\", script_path);\n\t\tobs_data_set_obj(obj, \"settings\", settings);\n\t\tobs_data_array_push_back(array, obj);\n\t}\n\n\tobs_data_set_array(save_data, \"scripts-tool\", array);\n}\n\nstatic void script_log(void *, obs_script_t *script, int log_level, const char *message)\n{\n\tQString qmsg;\n\n\tif (script) {\n\t\tqmsg = QStringLiteral(\"[%1] %2\").arg(obs_script_get_file(script), message);\n\t} else {\n\t\tqmsg = QStringLiteral(\"[Unknown Script] %1\").arg(message);\n\t}\n\n\tQMetaObject::invokeMethod(scriptLogWindow, \"AddLogMsg\", Q_ARG(int, log_level), Q_ARG(QString, qmsg));\n}\n\nextern \"C\" void InitScripts()\n{\n\tscriptLogWindow = new ScriptLogWindow();\n\n\tobs_scripting_set_module(obs_current_module());\n\tobs_scripting_load();\n\tobs_scripting_set_log_callback(script_log, nullptr);\n\n\tQAction *action = (QAction *)obs_frontend_add_tools_menu_qaction(obs_module_text(\"Scripts\"));\n\n#if PYTHON_UI\n\tconfig_t *config = obs_frontend_get_user_config();\n\tconst char *python_path = config_get_string(config, \"Python\", \"Path\" ARCH_NAME);\n\n#ifdef __APPLE__\n\tif (python_path && *python_path) {\n\t\tstd::string _python_path(python_path);\n\t\tstd::size_t pos = _python_path.find(\"/Python.framework/Versions\");\n\n\t\tif (pos != std::string::npos) {\n\t\t\tstd::string _temp = _python_path.substr(0, pos);\n\t\t\tconfig_set_string(config, \"Python\", \"Path\" ARCH_NAME, _temp.c_str());\n\t\t\tconfig_save(config);\n\t\t\tpython_path = _temp.c_str();\n\t\t}\n\t}\n#endif\n\n\tif (!obs_scripting_python_loaded() && python_path && *python_path)\n\t\tobs_scripting_load_python(python_path);\n#endif\n\n\tscriptData = new ScriptData;\n\n\tauto cb = []() {\n\t\tobs_frontend_push_ui_translation(obs_module_get_string);\n\n\t\tif (!scriptsWindow) {\n\t\t\tscriptsWindow = new ScriptsTool();\n\t\t\tscriptsWindow->show();\n\t\t} else {\n\t\t\tscriptsWindow->show();\n\t\t\tscriptsWindow->raise();\n\t\t}\n\n\t\tobs_frontend_pop_ui_translation();\n\t};\n\n\tobs_frontend_add_save_callback(save_script_data, nullptr);\n\tobs_frontend_add_preload_callback(load_script_data, nullptr);\n\tobs_frontend_add_event_callback(obs_event, nullptr);\n\n\tQObject::connect(action, &QAction::triggered, action, cb);\n}\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/scripts.hpp",
    "content": "#pragma once\n\n#include <QDialog>\n#include <QString>\n#include <memory>\n\nclass Ui_ScriptsTool;\n\nclass ScriptLogWindow : public QDialog {\n\tQ_OBJECT\n\n\tQString lines;\n\tbool bottomScrolled = true;\n\n\tvoid resizeEvent(QResizeEvent *event) override;\n\npublic:\n\tScriptLogWindow();\n\t~ScriptLogWindow();\n\npublic slots:\n\tvoid AddLogMsg(int log_level, QString msg);\n\tvoid ClearWindow();\n\tvoid Clear();\n\tvoid ScrollChanged(int val);\n};\n\nclass ScriptsTool : public QDialog {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui_ScriptsTool> ui;\n\tQWidget *propertiesView = nullptr;\n\n\tvoid updatePythonVersionLabel();\n\npublic:\n\tScriptsTool();\n\t~ScriptsTool();\n\n\tvoid RemoveScript(const char *path);\n\tvoid ReloadScript(const char *path);\n\tvoid RefreshLists();\n\tvoid SetScriptDefaults(const char *path);\n\npublic slots:\n\tvoid on_close_clicked();\n\n\tvoid on_addScripts_clicked();\n\tvoid on_removeScripts_clicked();\n\tvoid on_reloadScripts_clicked();\n\tvoid on_editScript_clicked();\n\tvoid on_scriptLog_clicked();\n\tvoid on_defaults_clicked();\n\n\tvoid OpenScriptParentDirectory();\n\n\tvoid on_scripts_currentRowChanged(int row);\n\n\tvoid on_pythonPathBrowse_clicked();\n\nprivate slots:\n\tvoid on_description_linkActivated(const QString &link);\n\tvoid on_scripts_customContextMenuRequested(const QPoint &pos);\n};\n"
  },
  {
    "path": "frontend/plugins/frontend-tools/tool-helpers.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n#include <string>\n#include <QString>\n\nstatic inline OBSWeakSource GetWeakSourceByName(const char *name)\n{\n\tOBSWeakSource weak;\n\tobs_source_t *source = obs_get_source_by_name(name);\n\tif (source) {\n\t\tweak = obs_source_get_weak_source(source);\n\t\tobs_weak_source_release(weak);\n\t\tobs_source_release(source);\n\t}\n\n\treturn weak;\n}\n\nstatic inline OBSWeakSource GetWeakSourceByQString(const QString &name)\n{\n\treturn GetWeakSourceByName(name.toUtf8().constData());\n}\n\nstatic inline std::string GetWeakSourceName(obs_weak_source_t *weak_source)\n{\n\tstd::string name;\n\n\tobs_source_t *source = obs_weak_source_get_source(weak_source);\n\tif (source) {\n\t\tname = obs_source_get_name(source);\n\t\tobs_source_release(source);\n\t}\n\n\treturn name;\n}\n"
  },
  {
    "path": "frontend/settings/OBSBasicSettings.cpp",
    "content": "/******************************************************************************\n Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n Philippe Groarke <philippe.groarke@gmail.com>\n \n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2 of the License, or\n (at your option) any later version.\n \n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n \n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>.\n ******************************************************************************/\n\n#include \"OBSBasicSettings.hpp\"\n#include \"OBSHotkeyLabel.hpp\"\n#include \"OBSHotkeyWidget.hpp\"\n\n#include <components/Multiview.hpp>\n#include <components/OBSSourceLabel.hpp>\n#include <components/SilentUpdateCheckBox.hpp>\n#include <components/SilentUpdateSpinBox.hpp>\n#ifdef YOUTUBE_ENABLED\n#include <docks/YouTubeAppDock.hpp>\n#endif\n#include <utility/audio-encoders.hpp>\n#include <utility/BaseLexer.hpp>\n#include <utility/FFmpegCodec.hpp>\n#include <utility/FFmpegFormat.hpp>\n#include <utility/SettingsEventFilter.hpp>\n#ifdef YOUTUBE_ENABLED\n#include <utility/YoutubeApiWrappers.hpp>\n#endif\n#include <widgets/OBSBasic.hpp>\n#include <widgets/OBSProjector.hpp>\n\n#include <properties-view.hpp>\n#include <qt-wrappers.hpp>\n\n#include <QCompleter>\n#include <QStandardItemModel>\n\n#include <sstream>\n\n#include \"moc_OBSBasicSettings.cpp\"\n\nusing namespace std;\n\nextern const char *get_simple_output_encoder(const char *encoder);\n\nextern bool restart;\nextern bool opt_allow_opengl;\nextern bool cef_js_avail;\n\nstatic inline bool ResTooHigh(uint32_t cx, uint32_t cy)\n{\n\treturn cx > 16384 || cy > 16384;\n}\n\nstatic inline bool ResTooLow(uint32_t cx, uint32_t cy)\n{\n\treturn cx < 32 || cy < 32;\n}\n\n/* parses \"[width]x[height]\", string, i.e. 1024x768 */\nstatic bool ConvertResText(const char *res, uint32_t &cx, uint32_t &cy)\n{\n\tBaseLexer lex;\n\tbase_token token;\n\n\tlexer_start(lex, res);\n\n\t/* parse width */\n\tif (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))\n\t\treturn false;\n\tif (token.type != BASETOKEN_DIGIT)\n\t\treturn false;\n\n\tcx = std::stoul(token.text.array);\n\n\t/* parse 'x' */\n\tif (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))\n\t\treturn false;\n\tif (strref_cmpi(&token.text, \"x\") != 0)\n\t\treturn false;\n\n\t/* parse height */\n\tif (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))\n\t\treturn false;\n\tif (token.type != BASETOKEN_DIGIT)\n\t\treturn false;\n\n\tcy = std::stoul(token.text.array);\n\n\t/* shouldn't be any more tokens after this */\n\tif (lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))\n\t\treturn false;\n\n\tif (ResTooHigh(cx, cy) || ResTooLow(cx, cy)) {\n\t\tcx = cy = 0;\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstatic inline bool WidgetChanged(QWidget *widget)\n{\n\treturn widget->property(\"changed\").toBool();\n}\n\nstatic inline void SetComboByName(QComboBox *combo, const QString &name)\n{\n\tint idx = combo->findText(name);\n\tif (idx != -1)\n\t\tcombo->setCurrentIndex(idx);\n}\n\nstatic inline bool SetComboByValue(QComboBox *combo, const QString &name)\n{\n\tint idx = combo->findData(name);\n\tif (idx != -1) {\n\t\tcombo->setCurrentIndex(idx);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic inline bool SetInvalidValue(QComboBox *combo, const QString &name, const QVariant &data)\n{\n\tcombo->insertItem(0, name, data);\n\n\tQStandardItemModel *model = dynamic_cast<QStandardItemModel *>(combo->model());\n\tif (!model)\n\t\treturn false;\n\n\tQStandardItem *item = model->item(0);\n\titem->setFlags(Qt::NoItemFlags);\n\n\tcombo->setCurrentIndex(0);\n\treturn true;\n}\n\nstatic inline QString GetComboData(QComboBox *combo)\n{\n\tint idx = combo->currentIndex();\n\tif (idx == -1)\n\t\treturn QString();\n\n\treturn combo->itemData(idx).toString();\n}\n\nstatic int FindEncoder(QComboBox *combo, const char *name, int id)\n{\n\tFFmpegCodec codec{name, id};\n\n\tfor (int i = 0; i < combo->count(); i++) {\n\t\tQVariant v = combo->itemData(i);\n\t\tif (!v.isNull()) {\n\t\t\tif (codec == v.value<FFmpegCodec>()) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\t}\n\treturn -1;\n}\n\n#define INVALID_BITRATE 10000\nstatic int FindClosestAvailableAudioBitrate(QComboBox *box, int bitrate)\n{\n\tQList<int> bitrates;\n\tint prev = 0;\n\tint next = INVALID_BITRATE;\n\n\tfor (int i = 0; i < box->count(); i++)\n\t\tbitrates << box->itemText(i).toInt();\n\n\tfor (int val : bitrates) {\n\t\tif (next > val) {\n\t\t\tif (val == bitrate)\n\t\t\t\treturn bitrate;\n\n\t\t\tif (val < next && val > bitrate)\n\t\t\t\tnext = val;\n\t\t\tif (val > prev && val < bitrate)\n\t\t\t\tprev = val;\n\t\t}\n\t}\n\n\tif (next != INVALID_BITRATE)\n\t\treturn next;\n\tif (prev != 0)\n\t\treturn prev;\n\treturn 192;\n}\n#undef INVALID_BITRATE\n\nstatic void PopulateSimpleBitrates(QComboBox *box, bool opus)\n{\n\tauto &bitrateMap = opus ? GetSimpleOpusEncoderBitrateMap() : GetSimpleAACEncoderBitrateMap();\n\tif (bitrateMap.empty())\n\t\treturn;\n\n\tvector<pair<QString, QString>> pairs;\n\tfor (auto &entry : bitrateMap)\n\t\tpairs.emplace_back(QString::number(entry.first), obs_encoder_get_display_name(entry.second.c_str()));\n\n\tQString currentBitrate = box->currentText();\n\tbox->clear();\n\n\tfor (auto &pair : pairs) {\n\t\tbox->addItem(pair.first);\n\t\tbox->setItemData(box->count() - 1, pair.second, Qt::ToolTipRole);\n\t}\n\n\tif (box->findData(currentBitrate) == -1) {\n\t\tint bitrate = FindClosestAvailableAudioBitrate(box, currentBitrate.toInt());\n\t\tbox->setCurrentText(QString::number(bitrate));\n\t} else\n\t\tbox->setCurrentText(currentBitrate);\n}\n\nstatic void PopulateAdvancedBitrates(initializer_list<QComboBox *> boxes, const char *stream_id, const char *rec_id)\n{\n\tauto &streamBitrates = GetAudioEncoderBitrates(stream_id);\n\tauto &recBitrates = GetAudioEncoderBitrates(rec_id);\n\tif (streamBitrates.empty() || recBitrates.empty())\n\t\treturn;\n\n\tQList<int> streamBitratesList;\n\tfor (auto &bitrate : streamBitrates)\n\t\tstreamBitratesList << bitrate;\n\n\tfor (auto box : boxes) {\n\t\tQString currentBitrate = box->currentText();\n\t\tbox->clear();\n\n\t\tfor (auto &bitrate : recBitrates) {\n\t\t\tif (streamBitratesList.indexOf(bitrate) == -1)\n\t\t\t\tcontinue;\n\n\t\t\tbox->addItem(QString::number(bitrate));\n\t\t}\n\n\t\tif (box->findData(currentBitrate) == -1) {\n\t\t\tint bitrate = FindClosestAvailableAudioBitrate(box, currentBitrate.toInt());\n\t\t\tbox->setCurrentText(QString::number(bitrate));\n\t\t} else\n\t\t\tbox->setCurrentText(currentBitrate);\n\t}\n}\n\nstatic std::tuple<int, int> aspect_ratio(int cx, int cy)\n{\n\tint common = std::gcd(cx, cy);\n\tint newCX = cx / common;\n\tint newCY = cy / common;\n\n\tif (newCX == 8 && newCY == 5) {\n\t\tnewCX = 16;\n\t\tnewCY = 10;\n\t}\n\n\treturn std::make_tuple(newCX, newCY);\n}\n\nstatic inline void HighlightGroupBoxLabel(QGroupBox *gb, QWidget *widget, QString objectName)\n{\n\tQFormLayout *layout = qobject_cast<QFormLayout *>(gb->layout());\n\n\tif (!layout)\n\t\treturn;\n\n\tQLabel *label = qobject_cast<QLabel *>(layout->labelForField(widget));\n\n\tif (label) {\n\t\tlabel->setObjectName(objectName);\n\n\t\tlabel->style()->unpolish(label);\n\t\tlabel->style()->polish(label);\n\t}\n}\n\nvoid RestrictResetBitrates(initializer_list<QComboBox *> boxes, int maxbitrate);\n\n/* clang-format off */\n#define COMBO_CHANGED   &QComboBox::currentIndexChanged\n#define EDIT_CHANGED    &QLineEdit::textChanged\n#define CBEDIT_CHANGED  &QComboBox::editTextChanged\n#define CHECK_CHANGED   &QCheckBox::toggled\n#define GROUP_CHANGED   &QGroupBox::toggled\n#define SCROLL_CHANGED  &QSpinBox::valueChanged\n#define DSCROLL_CHANGED &QDoubleSpinBox::valueChanged\n#define TEXT_CHANGED    &QPlainTextEdit::textChanged\n#define SLIDER_CHANGED  &QSlider::valueChanged\n\n#define GENERAL_CHANGED &OBSBasicSettings::GeneralChanged\n#define STREAM1_CHANGED &OBSBasicSettings::Stream1Changed\n#define OUTPUTS_CHANGED &OBSBasicSettings::OutputsChanged\n#define AUDIO_RESTART   &OBSBasicSettings::AudioChangedRestart\n#define AUDIO_CHANGED   &OBSBasicSettings::AudioChanged\n#define VIDEO_RES       &OBSBasicSettings::VideoChangedResolution\n#define VIDEO_CHANGED   &OBSBasicSettings::VideoChanged\n#define A11Y_CHANGED    &OBSBasicSettings::A11yChanged\n#define APPEAR_CHANGED  &OBSBasicSettings::AppearanceChanged\n#define ADV_CHANGED     &OBSBasicSettings::AdvancedChanged\n#define ADV_RESTART     &OBSBasicSettings::AdvancedChangedRestart\n/* clang-format on */\n\nOBSBasicSettings::OBSBasicSettings(QWidget *parent)\n\t: QDialog(parent),\n\t  main(qobject_cast<OBSBasic *>(parent)),\n\t  ui(new Ui::OBSBasicSettings)\n{\n\tstring path;\n\n\tEnableThreadedMessageBoxes(true);\n\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tui->setupUi(this);\n\n\tmain->EnableOutputs(false);\n\n\tui->listWidget->setAttribute(Qt::WA_MacShowFocusRect, false);\n\n\t/* clang-format off */\n\tHookWidget(ui->language,             COMBO_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->updateChannelBox,     COMBO_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->enableAutoUpdates,    CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->openStatsOnStartup,   CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->hideOBSFromCapture,   CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->warnBeforeStreamStart,CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->warnBeforeStreamStop, CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->warnBeforeRecordStop, CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->hideProjectorCursor,  CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->projectorAlwaysOnTop, CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->recordWhenStreaming,  CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->keepRecordStreamStops,CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->replayWhileStreaming, CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->keepReplayStreamStops,CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->systemTrayEnabled,    CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->systemTrayWhenStarted,CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->systemTrayAlways,     CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->saveProjectors,       CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->closeProjectors,      CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->snappingEnabled,      CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->screenSnapping,       CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->centerSnapping,       CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->sourceSnapping,       CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->snapDistance,         DSCROLL_CHANGED,GENERAL_CHANGED);\n\tHookWidget(ui->overflowHide,         CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->overflowAlwaysVisible,CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->overflowSelectionHide,CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->previewSafeAreas,     CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->automaticSearch,      CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->previewSpacingHelpers,CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->doubleClickSwitch,    CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->studioPortraitLayout, CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->prevProgLabelToggle,  CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->multiviewMouseSwitch, CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->multiviewDrawNames,   CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->multiviewDrawAreas,   CHECK_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->multiviewLayout,      COMBO_CHANGED,  GENERAL_CHANGED);\n\tHookWidget(ui->theme, \t\t     COMBO_CHANGED,  APPEAR_CHANGED);\n\tHookWidget(ui->themeVariant,\t     COMBO_CHANGED,  APPEAR_CHANGED);\n\tHookWidget(ui->appearanceFontScale,  SLIDER_CHANGED, APPEAR_CHANGED);\n\tHookWidget(ui->appearanceDensity1,   CHECK_CHANGED,  APPEAR_CHANGED);\n\tHookWidget(ui->appearanceDensity2,   CHECK_CHANGED,  APPEAR_CHANGED);\n\tHookWidget(ui->appearanceDensity3,   CHECK_CHANGED,  APPEAR_CHANGED);\n\tHookWidget(ui->appearanceDensity4,   CHECK_CHANGED,  APPEAR_CHANGED);\n\tHookWidget(ui->service,              COMBO_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->server,               COMBO_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->customServer,         EDIT_CHANGED,   STREAM1_CHANGED);\n\tHookWidget(ui->serviceCustomServer,  EDIT_CHANGED,   STREAM1_CHANGED);\n\tHookWidget(ui->key,                  EDIT_CHANGED,   STREAM1_CHANGED);\n\tHookWidget(ui->bandwidthTestEnable,  CHECK_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->twitchAddonDropdown,  COMBO_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->useAuth,              CHECK_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->authUsername,         EDIT_CHANGED,   STREAM1_CHANGED);\n\tHookWidget(ui->authPw,               EDIT_CHANGED,   STREAM1_CHANGED);\n\tHookWidget(ui->ignoreRecommended,    CHECK_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->whipSimulcastTotalLayers, SCROLL_CHANGED, STREAM1_CHANGED);\n\tHookWidget(ui->enableMultitrackVideo,      CHECK_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->multitrackVideoMaximumAggregateBitrateAuto, CHECK_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->multitrackVideoMaximumAggregateBitrate,     SCROLL_CHANGED, STREAM1_CHANGED);\n\tHookWidget(ui->multitrackVideoMaximumVideoTracksAuto, CHECK_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->multitrackVideoMaximumVideoTracks,     SCROLL_CHANGED, STREAM1_CHANGED);\n\tHookWidget(ui->multitrackVideoStreamDumpEnable,            CHECK_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->multitrackVideoConfigOverrideEnable,        CHECK_CHANGED,  STREAM1_CHANGED);\n\tHookWidget(ui->multitrackVideoConfigOverride,              TEXT_CHANGED,   STREAM1_CHANGED);\n\tHookWidget(ui->multitrackVideoAdditionalCanvas,            COMBO_CHANGED,   STREAM1_CHANGED);\n\tHookWidget(ui->outputMode,           COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutputPath,     EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleNoSpace,        CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutRecFormat,   COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutputVBitrate, SCROLL_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutStrEncoder,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutStrAEncoder, COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutputABitrate, COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutAdvanced,    CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutPreset,      COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutCustom,      EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutRecQuality,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutRecEncoder,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutRecAEncoder, COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutRecTrack1,   CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutRecTrack2,   CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutRecTrack3,   CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutRecTrack4,   CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutRecTrack5,   CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutRecTrack6,   CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleOutMuxCustom,   EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleReplayBuf,      GROUP_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleRBSecMax,       SCROLL_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->simpleRBMegsMax,      SCROLL_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutEncoder,        COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutAEncoder,       COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRescale,        CBEDIT_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRescaleFilter,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack1,         CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack2,         CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack3,         CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack4,         CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack5,         CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack6,         CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutMultiTrack1,    CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutMultiTrack2,    CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutMultiTrack3,    CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutMultiTrack4,    CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutMultiTrack5,    CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutMultiTrack6,    CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecType,        COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecPath,        EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutNoSpace,        CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecFormat,      COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecEncoder,     COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecAEncoder,    COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecRescale,     CBEDIT_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecRescaleFilter, COMBO_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutMuxCustom,      EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutSplitFile,      CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutSplitFileType,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutSplitFileTime,  SCROLL_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutSplitFileSize,  SCROLL_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecTrack1,      CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecTrack2,      CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecTrack3,      CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecTrack4,      CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecTrack5,      CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutRecTrack6,      CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->flvTrack1,            CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->flvTrack2,            CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->flvTrack3,            CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->flvTrack4,            CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->flvTrack5,            CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->flvTrack6,            CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFType,         COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFRecPath,      EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFNoSpace,      CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFURL,          EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFFormat,       COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFMCfg,         EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFVBitrate,     SCROLL_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFVGOPSize,     SCROLL_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFUseRescale,   CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFIgnoreCompat, CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFRescale,      CBEDIT_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFVEncoder,     COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFVCfg,         EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFABitrate,     SCROLL_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFTrack1,       CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFTrack2,       CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFTrack3,       CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFTrack4,       CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFTrack5,       CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFTrack6,       CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFAEncoder,     COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutFFACfg,         EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack1Bitrate,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack1Name,     EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack2Bitrate,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack2Name,     EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack3Bitrate,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack3Name,     EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack4Bitrate,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack4Name,     EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack5Bitrate,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack5Name,     EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack6Bitrate,  COMBO_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advOutTrack6Name,     EDIT_CHANGED,   OUTPUTS_CHANGED);\n\tHookWidget(ui->advReplayBuf,         CHECK_CHANGED,  OUTPUTS_CHANGED);\n\tHookWidget(ui->advRBSecMax,          SCROLL_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->advRBMegsMax,         SCROLL_CHANGED, OUTPUTS_CHANGED);\n\tHookWidget(ui->channelSetup,         COMBO_CHANGED,  AUDIO_RESTART);\n\tHookWidget(ui->sampleRate,           COMBO_CHANGED,  AUDIO_RESTART);\n\tHookWidget(ui->meterDecayRate,       COMBO_CHANGED,  AUDIO_CHANGED);\n\tHookWidget(ui->peakMeterType,        COMBO_CHANGED,  AUDIO_CHANGED);\n\tHookWidget(ui->desktopAudioDevice1,  COMBO_CHANGED,  AUDIO_CHANGED);\n\tHookWidget(ui->desktopAudioDevice2,  COMBO_CHANGED,  AUDIO_CHANGED);\n\tHookWidget(ui->auxAudioDevice1,      COMBO_CHANGED,  AUDIO_CHANGED);\n\tHookWidget(ui->auxAudioDevice2,      COMBO_CHANGED,  AUDIO_CHANGED);\n\tHookWidget(ui->auxAudioDevice3,      COMBO_CHANGED,  AUDIO_CHANGED);\n\tHookWidget(ui->auxAudioDevice4,      COMBO_CHANGED,  AUDIO_CHANGED);\n\tHookWidget(ui->baseResolution,       CBEDIT_CHANGED, VIDEO_RES);\n\tHookWidget(ui->outputResolution,     CBEDIT_CHANGED, VIDEO_RES);\n\tHookWidget(ui->downscaleFilter,      COMBO_CHANGED,  VIDEO_CHANGED);\n\tHookWidget(ui->fpsType,              COMBO_CHANGED,  VIDEO_CHANGED);\n\tHookWidget(ui->fpsCommon,            COMBO_CHANGED,  VIDEO_CHANGED);\n\tHookWidget(ui->fpsInteger,           SCROLL_CHANGED, VIDEO_CHANGED);\n\tHookWidget(ui->fpsNumerator,         SCROLL_CHANGED, VIDEO_CHANGED);\n\tHookWidget(ui->fpsDenominator,       SCROLL_CHANGED, VIDEO_CHANGED);\n\tHookWidget(ui->colorsGroupBox,       GROUP_CHANGED,  A11Y_CHANGED);\n\tHookWidget(ui->colorPreset,          COMBO_CHANGED,  A11Y_CHANGED);\n\tHookWidget(ui->renderer,             COMBO_CHANGED,  ADV_RESTART);\n\tHookWidget(ui->adapter,              COMBO_CHANGED,  ADV_RESTART);\n\tHookWidget(ui->colorFormat,          COMBO_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->colorSpace,           COMBO_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->colorRange,           COMBO_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->sdrWhiteLevel,        SCROLL_CHANGED, ADV_CHANGED);\n\tHookWidget(ui->hdrNominalPeakLevel,  SCROLL_CHANGED, ADV_CHANGED);\n\tHookWidget(ui->disableOSXVSync,      CHECK_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->resetOSXVSync,        CHECK_CHANGED,  ADV_CHANGED);\n\tif (obs_audio_monitoring_available())\n\t\tHookWidget(ui->monitoringDevice,     COMBO_CHANGED,  ADV_CHANGED);\n#ifdef _WIN32\n\tHookWidget(ui->disableAudioDucking,  CHECK_CHANGED,  ADV_CHANGED);\n#endif\n#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)\n\tHookWidget(ui->browserHWAccel,       CHECK_CHANGED,  ADV_RESTART);\n#endif\n\tHookWidget(ui->filenameFormatting,   EDIT_CHANGED,   ADV_CHANGED);\n\tHookWidget(ui->overwriteIfExists,    CHECK_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->simpleRBPrefix,       EDIT_CHANGED,   ADV_CHANGED);\n\tHookWidget(ui->simpleRBSuffix,       EDIT_CHANGED,   ADV_CHANGED);\n\tHookWidget(ui->streamDelayEnable,    CHECK_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->streamDelaySec,       SCROLL_CHANGED, ADV_CHANGED);\n\tHookWidget(ui->streamDelayPreserve,  CHECK_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->reconnectEnable,      CHECK_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->reconnectRetryDelay,  SCROLL_CHANGED, ADV_CHANGED);\n\tHookWidget(ui->reconnectMaxRetries,  SCROLL_CHANGED, ADV_CHANGED);\n\tHookWidget(ui->processPriority,      COMBO_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->confirmOnExit,        CHECK_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->bindToIP,             COMBO_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->ipFamily,             COMBO_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->enableNewSocketLoop,  CHECK_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->enableLowLatencyMode, CHECK_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->hotkeyFocusType,      COMBO_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->autoRemux,            CHECK_CHANGED,  ADV_CHANGED);\n\tHookWidget(ui->dynBitrate,           CHECK_CHANGED,  ADV_CHANGED);\n\t/* clang-format on */\n\n#define ADD_HOTKEY_FOCUS_TYPE(s) ui->hotkeyFocusType->addItem(QTStr(\"Basic.Settings.Advanced.Hotkeys.\" s), s)\n\n\tADD_HOTKEY_FOCUS_TYPE(\"NeverDisableHotkeys\");\n\tADD_HOTKEY_FOCUS_TYPE(\"DisableHotkeysInFocus\");\n\tADD_HOTKEY_FOCUS_TYPE(\"DisableHotkeysOutOfFocus\");\n\n#undef ADD_HOTKEY_FOCUS_TYPE\n\n\tui->simpleOutputVBitrate->setSingleStep(50);\n\tui->simpleOutputVBitrate->setSuffix(\" Kbps\");\n\tui->advOutFFVBitrate->setSingleStep(50);\n\tui->advOutFFVBitrate->setSuffix(\" Kbps\");\n\tui->advOutFFABitrate->setSuffix(\" Kbps\");\n\n#if !defined(_WIN32) && !defined(ENABLE_SPARKLE_UPDATER)\n\tdelete ui->updateSettingsGroupBox;\n\tui->updateSettingsGroupBox = nullptr;\n\tui->updateChannelLabel = nullptr;\n\tui->updateChannelBox = nullptr;\n\tui->enableAutoUpdates = nullptr;\n#else\n\t// Hide update section if disabled\n\tif (App()->IsUpdaterDisabled())\n\t\tui->updateSettingsGroupBox->hide();\n#endif\n\n\t// Remove the Advanced Audio section if monitoring is not supported, as the monitoring device selection is the only item in the group box.\n\tif (!obs_audio_monitoring_available()) {\n\t\tdelete ui->monitoringDeviceLabel;\n\t\tui->monitoringDeviceLabel = nullptr;\n\t\tdelete ui->monitoringDevice;\n\t\tui->monitoringDevice = nullptr;\n\t}\n\n#ifdef _WIN32\n\tif (!SetDisplayAffinitySupported()) {\n\t\tdelete ui->hideOBSFromCapture;\n\t\tui->hideOBSFromCapture = nullptr;\n\t}\n\n\tstatic struct ProcessPriority {\n\t\tconst char *name;\n\t\tconst char *val;\n\t} processPriorities[] = {\n\t\t{\"Basic.Settings.Advanced.General.ProcessPriority.High\", \"High\"},\n\t\t{\"Basic.Settings.Advanced.General.ProcessPriority.AboveNormal\", \"AboveNormal\"},\n\t\t{\"Basic.Settings.Advanced.General.ProcessPriority.Normal\", \"Normal\"},\n\t\t{\"Basic.Settings.Advanced.General.ProcessPriority.BelowNormal\", \"BelowNormal\"},\n\t\t{\"Basic.Settings.Advanced.General.ProcessPriority.Idle\", \"Idle\"},\n\t};\n\n\tfor (ProcessPriority pri : processPriorities)\n\t\tui->processPriority->addItem(QTStr(pri.name), pri.val);\n\n#else\n#if defined(__APPLE__) && defined(__aarch64__)\n\tdelete ui->adapterLabel;\n\tdelete ui->adapter;\n\n\tui->adapterLabel = nullptr;\n\tui->adapter = nullptr;\n#else\n\tdelete ui->rendererLabel;\n\tdelete ui->renderer;\n\tdelete ui->adapterLabel;\n\tdelete ui->adapter;\n\n\tui->rendererLabel = nullptr;\n\tui->renderer = nullptr;\n\tui->adapterLabel = nullptr;\n\tui->adapter = nullptr;\n#endif\n\tdelete ui->processPriorityLabel;\n\tdelete ui->processPriority;\n\tdelete ui->enableNewSocketLoop;\n\tdelete ui->enableLowLatencyMode;\n\tdelete ui->hideOBSFromCapture;\n#if !defined(__APPLE__) && !defined(__linux__)\n\tdelete ui->browserHWAccel;\n\tdelete ui->sourcesGroup;\n#endif\n\tdelete ui->disableAudioDucking;\n\n\tui->processPriorityLabel = nullptr;\n\tui->processPriority = nullptr;\n\tui->enableNewSocketLoop = nullptr;\n\tui->enableLowLatencyMode = nullptr;\n\tui->hideOBSFromCapture = nullptr;\n#if !defined(__APPLE__) && !defined(__linux__)\n\tui->browserHWAccel = nullptr;\n\tui->sourcesGroup = nullptr;\n#endif\n\tui->disableAudioDucking = nullptr;\n#endif\n\n#ifndef __APPLE__\n\tdelete ui->disableOSXVSync;\n\tdelete ui->resetOSXVSync;\n\tui->disableOSXVSync = nullptr;\n\tui->resetOSXVSync = nullptr;\n#endif\n\n\tconnect(ui->streamDelaySec, &QSpinBox::valueChanged, this, &OBSBasicSettings::UpdateStreamDelayEstimate);\n\tconnect(ui->outputMode, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::UpdateStreamDelayEstimate);\n\tconnect(ui->simpleOutputVBitrate, &QSpinBox::valueChanged, this, &OBSBasicSettings::UpdateStreamDelayEstimate);\n\tconnect(ui->simpleOutputABitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::UpdateStreamDelayEstimate);\n\tconnect(ui->advOutTrack1Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::UpdateStreamDelayEstimate);\n\tconnect(ui->advOutTrack2Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::UpdateStreamDelayEstimate);\n\tconnect(ui->advOutTrack3Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::UpdateStreamDelayEstimate);\n\tconnect(ui->advOutTrack4Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::UpdateStreamDelayEstimate);\n\tconnect(ui->advOutTrack5Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::UpdateStreamDelayEstimate);\n\tconnect(ui->advOutTrack6Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::UpdateStreamDelayEstimate);\n\n\t//Apply button disabled until change.\n\tEnableApplyButton(false);\n\n\tinstallEventFilter(new SettingsEventFilter());\n\n\tLoadColorRanges();\n\tLoadColorSpaces();\n\tLoadColorFormats();\n\tLoadFormats();\n\n\tauto ReloadAudioSources = [](void *data, calldata_t *param) {\n\t\tauto settings = static_cast<OBSBasicSettings *>(data);\n\t\tauto source = static_cast<obs_source_t *>(calldata_ptr(param, \"source\"));\n\n\t\tif (!source)\n\t\t\treturn;\n\n\t\tif (!(obs_source_get_output_flags(source) & OBS_SOURCE_AUDIO))\n\t\t\treturn;\n\n\t\tQMetaObject::invokeMethod(settings, \"ReloadAudioSources\", Qt::QueuedConnection);\n\t};\n\tsourceCreated.Connect(obs_get_signal_handler(), \"source_create\", ReloadAudioSources, this);\n\tchannelChanged.Connect(obs_get_signal_handler(), \"channel_change\", ReloadAudioSources, this);\n\n\thotkeyConflictIcon = QIcon::fromTheme(\"obs\", QIcon(\":/res/images/warning.svg\"));\n\n\tauto ReloadHotkeys = [](void *data, calldata_t *) {\n\t\tauto settings = static_cast<OBSBasicSettings *>(data);\n\t\tQMetaObject::invokeMethod(settings, \"ReloadHotkeys\");\n\t};\n\thotkeyRegistered.Connect(obs_get_signal_handler(), \"hotkey_register\", ReloadHotkeys, this);\n\n\tauto ReloadHotkeysIgnore = [](void *data, calldata_t *param) {\n\t\tauto settings = static_cast<OBSBasicSettings *>(data);\n\t\tauto key = static_cast<obs_hotkey_t *>(calldata_ptr(param, \"key\"));\n\t\tQMetaObject::invokeMethod(settings, \"ReloadHotkeys\", Q_ARG(obs_hotkey_id, obs_hotkey_get_id(key)));\n\t};\n\thotkeyUnregistered.Connect(obs_get_signal_handler(), \"hotkey_unregister\", ReloadHotkeysIgnore, this);\n\n\tFillSimpleRecordingValues();\n\tif (obs_audio_monitoring_available())\n\t\tFillAudioMonitoringDevices();\n\n\tconnect(ui->channelSetup, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::SurroundWarning);\n\tconnect(ui->channelSetup, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::SpeakerLayoutChanged);\n\tconnect(ui->lowLatencyBuffering, &QCheckBox::clicked, this, &OBSBasicSettings::LowLatencyBufferingChanged);\n\tconnect(ui->simpleOutRecQuality, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::SimpleRecordingQualityChanged);\n\tconnect(ui->simpleOutRecQuality, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::SimpleRecordingQualityLosslessWarning);\n\tconnect(ui->simpleOutRecFormat, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::SimpleRecordingEncoderChanged);\n\tconnect(ui->simpleOutStrEncoder, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::SimpleStreamingEncoderChanged);\n\tconnect(ui->simpleOutStrEncoder, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::SimpleRecordingEncoderChanged);\n\tconnect(ui->simpleOutRecEncoder, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::SimpleRecordingEncoderChanged);\n\tconnect(ui->simpleOutRecAEncoder, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::SimpleRecordingEncoderChanged);\n\tconnect(ui->simpleOutputVBitrate, &QSpinBox::valueChanged, this,\n\t\t&OBSBasicSettings::SimpleRecordingEncoderChanged);\n\tconnect(ui->simpleOutputABitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::SimpleRecordingEncoderChanged);\n\tconnect(ui->simpleOutAdvanced, &QCheckBox::toggled, this, &OBSBasicSettings::SimpleRecordingEncoderChanged);\n\tconnect(ui->ignoreRecommended, &QCheckBox::toggled, this, &OBSBasicSettings::SimpleRecordingEncoderChanged);\n\tconnect(ui->simpleReplayBuf, &QGroupBox::toggled, this, &OBSBasicSettings::SimpleReplayBufferChanged);\n\tconnect(ui->simpleOutputVBitrate, &QSpinBox::valueChanged, this, &OBSBasicSettings::SimpleReplayBufferChanged);\n\tconnect(ui->simpleOutputABitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::SimpleReplayBufferChanged);\n\tconnect(ui->simpleRBSecMax, &QSpinBox::valueChanged, this, &OBSBasicSettings::SimpleReplayBufferChanged);\n#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)\n\tconnect(ui->advOutSplitFile, &QCheckBox::checkStateChanged, this, &OBSBasicSettings::AdvOutSplitFileChanged);\n#else\n\tconnect(ui->advOutSplitFile, &QCheckBox::stateChanged, this, &OBSBasicSettings::AdvOutSplitFileChanged);\n#endif\n\tconnect(ui->advOutSplitFileType, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::AdvOutSplitFileChanged);\n\tconnect(ui->advReplayBuf, &QCheckBox::toggled, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutRecTrack1, &QCheckBox::toggled, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutRecTrack2, &QCheckBox::toggled, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutRecTrack3, &QCheckBox::toggled, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutRecTrack4, &QCheckBox::toggled, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutRecTrack5, &QCheckBox::toggled, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutRecTrack6, &QCheckBox::toggled, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutTrack1Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutTrack2Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutTrack3Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutTrack4Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutTrack5Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutTrack6Bitrate, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutRecType, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advOutRecEncoder, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\tconnect(ui->advRBSecMax, &QSpinBox::valueChanged, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\n\t// GPU scaling filters\n\tauto addScaleFilter = [&](const char *string, int value) -> void {\n\t\tui->advOutRescaleFilter->addItem(QTStr(string), value);\n\t\tui->advOutRecRescaleFilter->addItem(QTStr(string), value);\n\t};\n\n\taddScaleFilter(\"Basic.Settings.Output.Adv.Rescale.Disabled\", OBS_SCALE_DISABLE);\n\taddScaleFilter(\"Basic.Settings.Video.DownscaleFilter.Bilinear\", OBS_SCALE_BILINEAR);\n\taddScaleFilter(\"Basic.Settings.Video.DownscaleFilter.Area\", OBS_SCALE_AREA);\n\taddScaleFilter(\"Basic.Settings.Video.DownscaleFilter.Bicubic\", OBS_SCALE_BICUBIC);\n\taddScaleFilter(\"Basic.Settings.Video.DownscaleFilter.Lanczos\", OBS_SCALE_LANCZOS);\n\n\tauto connectScaleFilter = [&](QComboBox *filter, QComboBox *res) -> void {\n\t\tconnect(filter, &QComboBox::currentIndexChanged, this,\n\t\t\t[this, res, filter](int) { res->setEnabled(filter->currentData() != OBS_SCALE_DISABLE); });\n\t};\n\n\tconnectScaleFilter(ui->advOutRescaleFilter, ui->advOutRescale);\n\tconnectScaleFilter(ui->advOutRecRescaleFilter, ui->advOutRecRescale);\n\n\t// Get Bind to IP Addresses\n\tobs_properties_t *ppts = obs_get_output_properties(\"rtmp_output\");\n\tobs_property_t *p = obs_properties_get(ppts, \"bind_ip\");\n\n\tsize_t count = obs_property_list_item_count(p);\n\tfor (size_t i = 0; i < count; i++) {\n\t\tconst char *name = obs_property_list_item_name(p, i);\n\t\tconst char *val = obs_property_list_item_string(p, i);\n\n\t\tui->bindToIP->addItem(QT_UTF8(name), val);\n\t}\n\n\t// Add IP Family options\n\tp = obs_properties_get(ppts, \"ip_family\");\n\n\tcount = obs_property_list_item_count(p);\n\tfor (size_t i = 0; i < count; i++) {\n\t\tconst char *name = obs_property_list_item_name(p, i);\n\t\tconst char *val = obs_property_list_item_string(p, i);\n\n\t\tui->ipFamily->addItem(QT_UTF8(name), val);\n\t}\n\n\tobs_properties_destroy(ppts);\n\n\tui->multitrackVideoNoticeBox->setVisible(false);\n\n\tInitStreamPage();\n\tInitAppearancePage();\n\tLoadSettings(false);\n\n\tui->advOutTrack1->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track1\"));\n\tui->advOutTrack2->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track2\"));\n\tui->advOutTrack3->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track3\"));\n\tui->advOutTrack4->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track4\"));\n\tui->advOutTrack5->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track5\"));\n\tui->advOutTrack6->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track6\"));\n\n\tui->advOutRecTrack1->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track1\"));\n\tui->advOutRecTrack2->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track2\"));\n\tui->advOutRecTrack3->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track3\"));\n\tui->advOutRecTrack4->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track4\"));\n\tui->advOutRecTrack5->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track5\"));\n\tui->advOutRecTrack6->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track6\"));\n\n\tui->advOutFFTrack1->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track1\"));\n\tui->advOutFFTrack2->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track2\"));\n\tui->advOutFFTrack3->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track3\"));\n\tui->advOutFFTrack4->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track4\"));\n\tui->advOutFFTrack5->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track5\"));\n\tui->advOutFFTrack6->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Audio.Track6\"));\n\n\tui->snappingEnabled->setAccessibleName(QTStr(\"Basic.Settings.General.Snapping\"));\n\tui->systemTrayEnabled->setAccessibleName(QTStr(\"Basic.Settings.General.SysTray\"));\n\tui->label_31->setAccessibleName(QTStr(\"Basic.Settings.Output.Adv.Recording.RecType\"));\n\tui->streamDelayEnable->setAccessibleName(QTStr(\"Basic.Settings.Advanced.StreamDelay\"));\n\tui->reconnectEnable->setAccessibleName(QTStr(\"Basic.Settings.Output.Reconnect\"));\n\n\t// Add warning checks to advanced output recording section controls\n\tconnect(ui->advOutRecTrack1, &QCheckBox::clicked, this, &OBSBasicSettings::AdvOutRecCheckWarnings);\n\tconnect(ui->advOutRecTrack2, &QCheckBox::clicked, this, &OBSBasicSettings::AdvOutRecCheckWarnings);\n\tconnect(ui->advOutRecTrack3, &QCheckBox::clicked, this, &OBSBasicSettings::AdvOutRecCheckWarnings);\n\tconnect(ui->advOutRecTrack4, &QCheckBox::clicked, this, &OBSBasicSettings::AdvOutRecCheckWarnings);\n\tconnect(ui->advOutRecTrack5, &QCheckBox::clicked, this, &OBSBasicSettings::AdvOutRecCheckWarnings);\n\tconnect(ui->advOutRecTrack6, &QCheckBox::clicked, this, &OBSBasicSettings::AdvOutRecCheckWarnings);\n\tconnect(ui->advOutRecFormat, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::AdvOutRecCheckWarnings);\n\tconnect(ui->advOutRecEncoder, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::AdvOutRecCheckWarnings);\n\n\t// Check codec compatibility when format (container) changes\n\tconnect(ui->advOutRecFormat, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::AdvOutRecCheckCodecs);\n\n\t// Set placeholder used when selection was reset due to incompatibilities\n\tui->advOutAEncoder->setPlaceholderText(QTStr(\"CodecCompat.CodecPlaceholder\"));\n\tui->advOutRecEncoder->setPlaceholderText(QTStr(\"CodecCompat.CodecPlaceholder\"));\n\tui->advOutRecAEncoder->setPlaceholderText(QTStr(\"CodecCompat.CodecPlaceholder\"));\n\tui->simpleOutRecEncoder->setPlaceholderText(QTStr(\"CodecCompat.CodecPlaceholder\"));\n\tui->simpleOutRecAEncoder->setPlaceholderText(QTStr(\"CodecCompat.CodecPlaceholder\"));\n\tui->simpleOutRecFormat->setPlaceholderText(QTStr(\"CodecCompat.ContainerPlaceholder\"));\n\n\tSimpleRecordingQualityChanged();\n\tAdvOutSplitFileChanged();\n\tAdvOutRecCheckCodecs();\n\tAdvOutRecCheckWarnings();\n\n\tUpdateAutomaticReplayBufferCheckboxes();\n\n\tApp()->DisableHotkeys();\n\n\tchannelIndex = ui->channelSetup->currentIndex();\n\tsampleRateIndex = ui->sampleRate->currentIndex();\n\tllBufferingEnabled = ui->lowLatencyBuffering->isChecked();\n\n\tQRegularExpression rx(\"\\\\d{1,5}x\\\\d{1,5}\");\n\tQValidator *validator = new QRegularExpressionValidator(rx, this);\n\tui->baseResolution->lineEdit()->setValidator(validator);\n\tui->outputResolution->lineEdit()->setValidator(validator);\n\tui->advOutRescale->lineEdit()->setValidator(validator);\n\tui->advOutRecRescale->lineEdit()->setValidator(validator);\n\tui->advOutFFRescale->lineEdit()->setValidator(validator);\n\n\tconnect(ui->useStreamKeyAdv, &QCheckBox::clicked, this, &OBSBasicSettings::UseStreamKeyAdvClicked);\n\n\tconnect(ui->simpleOutStrAEncoder, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::SimpleStreamAudioEncoderChanged);\n\tconnect(ui->advOutAEncoder, &QComboBox::currentIndexChanged, this, &OBSBasicSettings::AdvAudioEncodersChanged);\n\tconnect(ui->advOutRecAEncoder, &QComboBox::currentIndexChanged, this,\n\t\t&OBSBasicSettings::AdvAudioEncodersChanged);\n\n\tUpdateAudioWarnings();\n\tUpdateAdvNetworkGroup();\n\n\tui->audioMsg->setVisible(false);\n\tui->advancedMsg->setVisible(false);\n\tui->advancedMsg2->setVisible(false);\n}\n\nOBSBasicSettings::~OBSBasicSettings()\n{\n\tdelete ui->filenameFormatting->completer();\n\tmain->EnableOutputs(true);\n\n\tApp()->UpdateHotkeyFocusSetting();\n\n\tEnableThreadedMessageBoxes(false);\n}\n\nvoid OBSBasicSettings::SaveCombo(QComboBox *widget, const char *section, const char *value)\n{\n\tif (WidgetChanged(widget))\n\t\tconfig_set_string(main->Config(), section, value, QT_TO_UTF8(widget->currentText()));\n}\n\nvoid OBSBasicSettings::SaveComboData(QComboBox *widget, const char *section, const char *value)\n{\n\tif (WidgetChanged(widget)) {\n\t\tQString str = GetComboData(widget);\n\t\tconfig_set_string(main->Config(), section, value, QT_TO_UTF8(str));\n\t}\n}\n\nvoid OBSBasicSettings::SaveCheckBox(QAbstractButton *widget, const char *section, const char *value, bool invert)\n{\n\tif (WidgetChanged(widget)) {\n\t\tbool checked = widget->isChecked();\n\t\tif (invert)\n\t\t\tchecked = !checked;\n\n\t\tconfig_set_bool(main->Config(), section, value, checked);\n\t}\n}\n\nvoid OBSBasicSettings::SaveEdit(QLineEdit *widget, const char *section, const char *value)\n{\n\tif (WidgetChanged(widget))\n\t\tconfig_set_string(main->Config(), section, value, QT_TO_UTF8(widget->text()));\n}\n\nvoid OBSBasicSettings::SaveSpinBox(QSpinBox *widget, const char *section, const char *value)\n{\n\tif (WidgetChanged(widget))\n\t\tconfig_set_int(main->Config(), section, value, widget->value());\n}\n\nvoid OBSBasicSettings::SaveText(QPlainTextEdit *widget, const char *section, const char *value)\n{\n\tif (!WidgetChanged(widget))\n\t\treturn;\n\n\tauto utf8 = widget->toPlainText().toUtf8();\n\n\tOBSDataAutoRelease safe_text = obs_data_create();\n\tobs_data_set_string(safe_text, \"text\", utf8.constData());\n\n\tconfig_set_string(main->Config(), section, value, obs_data_get_json(safe_text));\n}\n\nstd::string DeserializeConfigText(const char *value)\n{\n\tOBSDataAutoRelease data = obs_data_create_from_json(value);\n\treturn obs_data_get_string(data, \"text\");\n}\n\nvoid OBSBasicSettings::SaveGroupBox(QGroupBox *widget, const char *section, const char *value)\n{\n\tif (WidgetChanged(widget))\n\t\tconfig_set_bool(main->Config(), section, value, widget->isChecked());\n}\n\n#define CS_PARTIAL_STR QTStr(\"Basic.Settings.Advanced.Video.ColorRange.Partial\")\n#define CS_FULL_STR QTStr(\"Basic.Settings.Advanced.Video.ColorRange.Full\")\n\nvoid OBSBasicSettings::LoadColorRanges()\n{\n\tui->colorRange->addItem(CS_PARTIAL_STR, \"Partial\");\n\tui->colorRange->addItem(CS_FULL_STR, \"Full\");\n}\n\n#define CS_SRGB_STR QTStr(\"Basic.Settings.Advanced.Video.ColorSpace.sRGB\")\n#define CS_709_STR QTStr(\"Basic.Settings.Advanced.Video.ColorSpace.709\")\n#define CS_601_STR QTStr(\"Basic.Settings.Advanced.Video.ColorSpace.601\")\n#define CS_2100PQ_STR QTStr(\"Basic.Settings.Advanced.Video.ColorSpace.2100PQ\")\n#define CS_2100HLG_STR QTStr(\"Basic.Settings.Advanced.Video.ColorSpace.2100HLG\")\n\nvoid OBSBasicSettings::LoadColorSpaces()\n{\n\tui->colorSpace->addItem(CS_SRGB_STR, \"sRGB\");\n\tui->colorSpace->addItem(CS_709_STR, \"709\");\n\tui->colorSpace->addItem(CS_601_STR, \"601\");\n\tui->colorSpace->addItem(CS_2100PQ_STR, \"2100PQ\");\n\tui->colorSpace->addItem(CS_2100HLG_STR, \"2100HLG\");\n}\n\n#define CF_NV12_STR QTStr(\"Basic.Settings.Advanced.Video.ColorFormat.NV12\")\n#define CF_I420_STR QTStr(\"Basic.Settings.Advanced.Video.ColorFormat.I420\")\n#define CF_I444_STR QTStr(\"Basic.Settings.Advanced.Video.ColorFormat.I444\")\n#define CF_P010_STR QTStr(\"Basic.Settings.Advanced.Video.ColorFormat.P010\")\n#define CF_I010_STR QTStr(\"Basic.Settings.Advanced.Video.ColorFormat.I010\")\n#define CF_P216_STR QTStr(\"Basic.Settings.Advanced.Video.ColorFormat.P216\")\n#define CF_P416_STR QTStr(\"Basic.Settings.Advanced.Video.ColorFormat.P416\")\n#define CF_BGRA_STR QTStr(\"Basic.Settings.Advanced.Video.ColorFormat.BGRA\")\n\nvoid OBSBasicSettings::LoadColorFormats()\n{\n\tui->colorFormat->addItem(CF_NV12_STR, \"NV12\");\n\tui->colorFormat->addItem(CF_I420_STR, \"I420\");\n\tui->colorFormat->addItem(CF_I444_STR, \"I444\");\n\tui->colorFormat->addItem(CF_P010_STR, \"P010\");\n\tui->colorFormat->addItem(CF_I010_STR, \"I010\");\n\tui->colorFormat->addItem(CF_P216_STR, \"P216\");\n\tui->colorFormat->addItem(CF_P416_STR, \"P416\");\n\tui->colorFormat->addItem(CF_BGRA_STR, \"RGB\"); // Avoid config break\n}\n\n#define AV_FORMAT_DEFAULT_STR QTStr(\"Basic.Settings.Output.Adv.FFmpeg.FormatDefault\")\n#define AUDIO_STR QTStr(\"Basic.Settings.Output.Adv.FFmpeg.FormatAudio\")\n#define VIDEO_STR QTStr(\"Basic.Settings.Output.Adv.FFmpeg.FormatVideo\")\n\nvoid OBSBasicSettings::LoadFormats()\n{\n#define FORMAT_STR(str) QTStr(\"Basic.Settings.Output.Format.\" str)\n\tui->advOutFFFormat->blockSignals(true);\n\n\tformats = GetSupportedFormats();\n\n\tfor (auto &format : formats) {\n\t\tbool audio = format.HasAudio();\n\t\tbool video = format.HasVideo();\n\n\t\tif (audio || video) {\n\t\t\tQString itemText(format.name);\n\t\t\tif (audio ^ video)\n\t\t\t\titemText += QString(\" (%1)\").arg(audio ? AUDIO_STR : VIDEO_STR);\n\n\t\t\tui->advOutFFFormat->addItem(itemText, QVariant::fromValue(format));\n\t\t}\n\t}\n\n\tui->advOutFFFormat->model()->sort(0);\n\n\tui->advOutFFFormat->insertItem(0, AV_FORMAT_DEFAULT_STR);\n\n\tui->advOutFFFormat->blockSignals(false);\n\n\tui->simpleOutRecFormat->addItem(FORMAT_STR(\"FLV\"), \"flv\");\n\tui->simpleOutRecFormat->addItem(FORMAT_STR(\"MKV\"), \"mkv\");\n\tui->simpleOutRecFormat->addItem(FORMAT_STR(\"MP4\"), \"mp4\");\n\tui->simpleOutRecFormat->addItem(FORMAT_STR(\"MOV\"), \"mov\");\n\tui->simpleOutRecFormat->addItem(FORMAT_STR(\"hMP4\"), \"hybrid_mp4\");\n\tui->simpleOutRecFormat->addItem(FORMAT_STR(\"hMOV\"), \"hybrid_mov\");\n\tui->simpleOutRecFormat->addItem(FORMAT_STR(\"fMP4\"), \"fragmented_mp4\");\n\tui->simpleOutRecFormat->addItem(FORMAT_STR(\"fMOV\"), \"fragmented_mov\");\n\tui->simpleOutRecFormat->addItem(FORMAT_STR(\"TS\"), \"mpegts\");\n\n\tui->advOutRecFormat->addItem(FORMAT_STR(\"FLV\"), \"flv\");\n\tui->advOutRecFormat->addItem(FORMAT_STR(\"MKV\"), \"mkv\");\n\tui->advOutRecFormat->addItem(FORMAT_STR(\"MP4\"), \"mp4\");\n\tui->advOutRecFormat->addItem(FORMAT_STR(\"MOV\"), \"mov\");\n\tui->advOutRecFormat->addItem(FORMAT_STR(\"hMP4\"), \"hybrid_mp4\");\n\tui->advOutRecFormat->addItem(FORMAT_STR(\"hMOV\"), \"hybrid_mov\");\n\tui->advOutRecFormat->addItem(FORMAT_STR(\"fMP4\"), \"fragmented_mp4\");\n\tui->advOutRecFormat->addItem(FORMAT_STR(\"fMOV\"), \"fragmented_mov\");\n\tui->advOutRecFormat->addItem(FORMAT_STR(\"TS\"), \"mpegts\");\n\tui->advOutRecFormat->addItem(FORMAT_STR(\"HLS\"), \"hls\");\n\n#undef FORMAT_STR\n}\n\nstatic void AddCodec(QComboBox *combo, const FFmpegCodec &codec)\n{\n\tQString itemText;\n\tif (codec.long_name)\n\t\titemText = QString(\"%1 - %2\").arg(codec.name, codec.long_name);\n\telse\n\t\titemText = codec.name;\n\n\tcombo->addItem(itemText, QVariant::fromValue(codec));\n}\n\n#define AV_ENCODER_DEFAULT_STR QTStr(\"Basic.Settings.Output.Adv.FFmpeg.AVEncoderDefault\")\n\nstatic void AddDefaultCodec(QComboBox *combo, const FFmpegFormat &format, FFmpegCodecType codecType)\n{\n\tFFmpegCodec codec = format.GetDefaultEncoder(codecType);\n\n\tint existingIdx = FindEncoder(combo, codec.name, codec.id);\n\tif (existingIdx >= 0)\n\t\tcombo->removeItem(existingIdx);\n\n\tQString itemText;\n\tif (codec.long_name) {\n\t\titemText = QString(\"%1 - %2 (%3)\").arg(codec.name, codec.long_name, AV_ENCODER_DEFAULT_STR);\n\t} else {\n\t\titemText = QString(\"%1 (%2)\").arg(codec.name, AV_ENCODER_DEFAULT_STR);\n\t}\n\n\tcombo->addItem(itemText, QVariant::fromValue(codec));\n}\n\n#define AV_ENCODER_DISABLE_STR QTStr(\"Basic.Settings.Output.Adv.FFmpeg.AVEncoderDisable\")\n\nvoid OBSBasicSettings::ReloadCodecs(const FFmpegFormat &format)\n{\n\tui->advOutFFAEncoder->blockSignals(true);\n\tui->advOutFFVEncoder->blockSignals(true);\n\tui->advOutFFAEncoder->clear();\n\tui->advOutFFVEncoder->clear();\n\n\tbool ignore_compatibility = ui->advOutFFIgnoreCompat->isChecked();\n\tvector<FFmpegCodec> supportedCodecs = GetFormatCodecs(format, ignore_compatibility);\n\n\tfor (auto &codec : supportedCodecs) {\n\t\tswitch (codec.type) {\n\t\tcase FFmpegCodecType::AUDIO:\n\t\t\tAddCodec(ui->advOutFFAEncoder, codec);\n\t\t\tbreak;\n\t\tcase FFmpegCodecType::VIDEO:\n\t\t\tAddCodec(ui->advOutFFVEncoder, codec);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (format.HasAudio())\n\t\tAddDefaultCodec(ui->advOutFFAEncoder, format, FFmpegCodecType::AUDIO);\n\tif (format.HasVideo())\n\t\tAddDefaultCodec(ui->advOutFFVEncoder, format, FFmpegCodecType::VIDEO);\n\n\tui->advOutFFAEncoder->model()->sort(0);\n\tui->advOutFFVEncoder->model()->sort(0);\n\n\tQVariant disable = QVariant::fromValue(FFmpegCodec());\n\n\tui->advOutFFAEncoder->insertItem(0, AV_ENCODER_DISABLE_STR, disable);\n\tui->advOutFFVEncoder->insertItem(0, AV_ENCODER_DISABLE_STR, disable);\n\n\tui->advOutFFAEncoder->blockSignals(false);\n\tui->advOutFFVEncoder->blockSignals(false);\n}\n\nvoid OBSBasicSettings::LoadLanguageList()\n{\n\tconst char *currentLang = App()->GetLocale();\n\n\tui->language->clear();\n\n\tfor (const auto &locale : GetLocaleNames()) {\n\t\tint idx = ui->language->count();\n\n\t\tui->language->addItem(QT_UTF8(locale.second.c_str()), QT_UTF8(locale.first.c_str()));\n\n\t\tif (locale.first == currentLang)\n\t\t\tui->language->setCurrentIndex(idx);\n\t}\n\n\tui->language->model()->sort(0);\n}\n\n#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)\nvoid TranslateBranchInfo(const QString &name, QString &displayName, QString &description)\n{\n\tQString translatedName = QTStr(QT_TO_UTF8((\"Basic.Settings.General.ChannelName.\" + name)));\n\tQString translatedDesc = QTStr(QT_TO_UTF8((\"Basic.Settings.General.ChannelDescription.\" + name)));\n\n\tif (!translatedName.startsWith(\"Basic.Settings.\"))\n\t\tdisplayName = translatedName;\n\tif (!translatedDesc.startsWith(\"Basic.Settings.\"))\n\t\tdescription = translatedDesc;\n}\n#endif\n\nvoid OBSBasicSettings::LoadBranchesList()\n{\n#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)\n\tbool configBranchRemoved = true;\n\tQString configBranch = config_get_string(App()->GetAppConfig(), \"General\", \"UpdateBranch\");\n\n\tfor (const UpdateBranch &branch : App()->GetBranches()) {\n\t\tif (branch.name == configBranch)\n\t\t\tconfigBranchRemoved = false;\n\t\tif (!branch.is_visible && branch.name != configBranch)\n\t\t\tcontinue;\n\n\t\tQString displayName = branch.display_name;\n\t\tQString description = branch.description;\n\n\t\tTranslateBranchInfo(branch.name, displayName, description);\n\t\tQString itemDesc = displayName + \" - \" + description;\n\n\t\tif (!branch.is_enabled) {\n\t\t\titemDesc.prepend(\" \");\n\t\t\titemDesc.prepend(QTStr(\"Basic.Settings.General.UpdateChannelDisabled\"));\n\t\t} else if (branch.name == \"stable\") {\n\t\t\titemDesc.append(\" \");\n\t\t\titemDesc.append(QTStr(\"Basic.Settings.General.UpdateChannelDefault\"));\n\t\t}\n\n\t\tui->updateChannelBox->addItem(itemDesc, branch.name);\n\n\t\t// Disable item if branch is disabled\n\t\tif (!branch.is_enabled) {\n\t\t\tQStandardItemModel *model = dynamic_cast<QStandardItemModel *>(ui->updateChannelBox->model());\n\t\t\tQStandardItem *item = model->item(ui->updateChannelBox->count() - 1);\n\t\t\titem->setFlags(Qt::NoItemFlags);\n\t\t}\n\t}\n\n\t// Fall back to default if not yet set or user-selected branch has been removed\n\tif (configBranch.isEmpty() || configBranchRemoved)\n\t\tconfigBranch = \"stable\";\n\n\tint idx = ui->updateChannelBox->findData(configBranch);\n\tui->updateChannelBox->setCurrentIndex(idx);\n#endif\n}\n\nvoid OBSBasicSettings::LoadGeneralSettings()\n{\n\tloading = true;\n\n\tLoadLanguageList();\n\n#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)\n\tbool enableAutoUpdates = config_get_bool(App()->GetAppConfig(), \"General\", \"EnableAutoUpdates\");\n\tui->enableAutoUpdates->setChecked(enableAutoUpdates);\n\n\tLoadBranchesList();\n#endif\n\tbool openStatsOnStartup = config_get_bool(main->Config(), \"General\", \"OpenStatsOnStartup\");\n\tui->openStatsOnStartup->setChecked(openStatsOnStartup);\n\n#if defined(_WIN32)\n\tif (ui->hideOBSFromCapture) {\n\t\tbool hideWindowFromCapture =\n\t\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"HideOBSWindowsFromCapture\");\n\t\tui->hideOBSFromCapture->setChecked(hideWindowFromCapture);\n\n#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)\n\t\tconnect(ui->hideOBSFromCapture, &QCheckBox::checkStateChanged, this,\n\t\t\t&OBSBasicSettings::HideOBSWindowWarning);\n#else\n\t\tconnect(ui->hideOBSFromCapture, &QCheckBox::stateChanged, this,\n\t\t\t&OBSBasicSettings::HideOBSWindowWarning);\n#endif\n\t}\n#endif\n\n\tbool recordWhenStreaming = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"RecordWhenStreaming\");\n\tui->recordWhenStreaming->setChecked(recordWhenStreaming);\n\n\tbool keepRecordStreamStops =\n\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"KeepRecordingWhenStreamStops\");\n\tui->keepRecordStreamStops->setChecked(keepRecordStreamStops);\n\n\tbool replayWhileStreaming =\n\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ReplayBufferWhileStreaming\");\n\tui->replayWhileStreaming->setChecked(replayWhileStreaming);\n\n\tbool keepReplayStreamStops =\n\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"KeepReplayBufferStreamStops\");\n\tui->keepReplayStreamStops->setChecked(keepReplayStreamStops);\n\n\tbool systemTrayEnabled = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayEnabled\");\n\tui->systemTrayEnabled->setChecked(systemTrayEnabled);\n\n\tbool systemTrayWhenStarted = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayWhenStarted\");\n\tui->systemTrayWhenStarted->setChecked(systemTrayWhenStarted);\n\n\tbool systemTrayAlways = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayMinimizeToTray\");\n\tui->systemTrayAlways->setChecked(systemTrayAlways);\n\n\tbool saveProjectors = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SaveProjectors\");\n\tui->saveProjectors->setChecked(saveProjectors);\n\n\tbool closeProjectors = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"CloseExistingProjectors\");\n\tui->closeProjectors->setChecked(closeProjectors);\n\n\tbool snappingEnabled = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SnappingEnabled\");\n\tui->snappingEnabled->setChecked(snappingEnabled);\n\n\tbool screenSnapping = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ScreenSnapping\");\n\tui->screenSnapping->setChecked(screenSnapping);\n\n\tbool centerSnapping = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"CenterSnapping\");\n\tui->centerSnapping->setChecked(centerSnapping);\n\n\tbool sourceSnapping = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SourceSnapping\");\n\tui->sourceSnapping->setChecked(sourceSnapping);\n\n\tdouble snapDistance = config_get_double(App()->GetUserConfig(), \"BasicWindow\", \"SnapDistance\");\n\tui->snapDistance->setValue(snapDistance);\n\n\tbool warnBeforeStreamStart = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"WarnBeforeStartingStream\");\n\tui->warnBeforeStreamStart->setChecked(warnBeforeStreamStart);\n\n\tbool spacingHelpersEnabled = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SpacingHelpersEnabled\");\n\tui->previewSpacingHelpers->setChecked(spacingHelpersEnabled);\n\n\tbool warnBeforeStreamStop = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"WarnBeforeStoppingStream\");\n\tui->warnBeforeStreamStop->setChecked(warnBeforeStreamStop);\n\n\tbool warnBeforeRecordStop = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"WarnBeforeStoppingRecord\");\n\tui->warnBeforeRecordStop->setChecked(warnBeforeRecordStop);\n\n\tbool hideProjectorCursor = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"HideProjectorCursor\");\n\tui->hideProjectorCursor->setChecked(hideProjectorCursor);\n\n\tbool projectorAlwaysOnTop = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ProjectorAlwaysOnTop\");\n\tui->projectorAlwaysOnTop->setChecked(projectorAlwaysOnTop);\n\n\tbool overflowHide = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"OverflowHidden\");\n\tui->overflowHide->setChecked(overflowHide);\n\n\tbool overflowAlwaysVisible = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"OverflowAlwaysVisible\");\n\tui->overflowAlwaysVisible->setChecked(overflowAlwaysVisible);\n\n\tbool overflowSelectionHide = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"OverflowSelectionHidden\");\n\tui->overflowSelectionHide->setChecked(overflowSelectionHide);\n\n\tbool safeAreas = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowSafeAreas\");\n\tui->previewSafeAreas->setChecked(safeAreas);\n\n\tbool automaticSearch = config_get_bool(App()->GetUserConfig(), \"General\", \"AutomaticCollectionSearch\");\n\tui->automaticSearch->setChecked(automaticSearch);\n\n\tbool doubleClickSwitch = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"TransitionOnDoubleClick\");\n\tui->doubleClickSwitch->setChecked(doubleClickSwitch);\n\n\tbool studioPortraitLayout = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"StudioPortraitLayout\");\n\tui->studioPortraitLayout->setChecked(studioPortraitLayout);\n\n\tbool prevProgLabels = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"StudioModeLabels\");\n\tui->prevProgLabelToggle->setChecked(prevProgLabels);\n\n\tbool multiviewMouseSwitch = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewMouseSwitch\");\n\tui->multiviewMouseSwitch->setChecked(multiviewMouseSwitch);\n\n\tbool multiviewDrawNames = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewDrawNames\");\n\tui->multiviewDrawNames->setChecked(multiviewDrawNames);\n\n\tbool multiviewDrawAreas = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewDrawAreas\");\n\tui->multiviewDrawAreas->setChecked(multiviewDrawAreas);\n\n\tui->multiviewLayout->addItem(QTStr(\"Basic.Settings.General.MultiviewLayout.Horizontal.Top\"),\n\t\t\t\t     static_cast<int>(MultiviewLayout::HORIZONTAL_TOP_8_SCENES));\n\tui->multiviewLayout->addItem(QTStr(\"Basic.Settings.General.MultiviewLayout.Horizontal.Bottom\"),\n\t\t\t\t     static_cast<int>(MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES));\n\tui->multiviewLayout->addItem(QTStr(\"Basic.Settings.General.MultiviewLayout.Vertical.Left\"),\n\t\t\t\t     static_cast<int>(MultiviewLayout::VERTICAL_LEFT_8_SCENES));\n\tui->multiviewLayout->addItem(QTStr(\"Basic.Settings.General.MultiviewLayout.Vertical.Right\"),\n\t\t\t\t     static_cast<int>(MultiviewLayout::VERTICAL_RIGHT_8_SCENES));\n\tui->multiviewLayout->addItem(QTStr(\"Basic.Settings.General.MultiviewLayout.Horizontal.18Scene.Top\"),\n\t\t\t\t     static_cast<int>(MultiviewLayout::HORIZONTAL_TOP_18_SCENES));\n\tui->multiviewLayout->addItem(QTStr(\"Basic.Settings.General.MultiviewLayout.Horizontal.Extended.Top\"),\n\t\t\t\t     static_cast<int>(MultiviewLayout::HORIZONTAL_TOP_24_SCENES));\n\tui->multiviewLayout->addItem(QTStr(\"Basic.Settings.General.MultiviewLayout.4Scene\"),\n\t\t\t\t     static_cast<int>(MultiviewLayout::SCENES_ONLY_4_SCENES));\n\tui->multiviewLayout->addItem(QTStr(\"Basic.Settings.General.MultiviewLayout.9Scene\"),\n\t\t\t\t     static_cast<int>(MultiviewLayout::SCENES_ONLY_9_SCENES));\n\tui->multiviewLayout->addItem(QTStr(\"Basic.Settings.General.MultiviewLayout.16Scene\"),\n\t\t\t\t     static_cast<int>(MultiviewLayout::SCENES_ONLY_16_SCENES));\n\tui->multiviewLayout->addItem(QTStr(\"Basic.Settings.General.MultiviewLayout.25Scene\"),\n\t\t\t\t     static_cast<int>(MultiviewLayout::SCENES_ONLY_25_SCENES));\n\n\tui->multiviewLayout->setCurrentIndex(ui->multiviewLayout->findData(\n\t\tQVariant::fromValue(config_get_int(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewLayout\"))));\n\n\tprevLangIndex = ui->language->currentIndex();\n\n\tif (obs_video_active())\n\t\tui->language->setEnabled(false);\n\n\tloading = false;\n}\n\nvoid OBSBasicSettings::LoadRendererList()\n{\n#if defined(_WIN32) || (defined(__APPLE__) && defined(__aarch64__))\n\tconst char *renderer = config_get_string(App()->GetAppConfig(), \"Video\", \"Renderer\");\n#ifdef _WIN32\n\tui->renderer->addItem(QString(\"Direct3D 11\"), QString(\"Direct3D 11\"));\n\tif (opt_allow_opengl || strcmp(renderer, \"OpenGL\") == 0) {\n\t\tui->renderer->addItem(QString(\"OpenGL\"), QString(\"OpenGL\"));\n\t}\n#else\n\tui->renderer->addItem(QString(\"OpenGL\"), QString(\"OpenGL\"));\n\tui->renderer->addItem(QTStr(\"Basic.Settings.Video.Renderer.Experimental\").arg(\"Metal\"), QString(\"Metal\"));\n#endif\n\tint index = ui->renderer->findData(QString(renderer));\n\tif (index == -1) {\n\t\tindex = 0;\n\t}\n\n\t// the video adapter selection is not currently implemented, hide for now\n\t// to avoid user confusion. was previously protected by\n\t// if (strcmp(renderer, \"OpenGL\") == 0)\n\tdelete ui->adapter;\n\tdelete ui->adapterLabel;\n\tui->adapter = nullptr;\n\tui->adapterLabel = nullptr;\n\n\tui->renderer->setCurrentIndex(index);\n#endif\n}\n\nstatic string ResString(uint32_t cx, uint32_t cy)\n{\n\tstringstream res;\n\tres << cx << \"x\" << cy;\n\treturn res.str();\n}\n\n/* some nice default output resolution vals */\nstatic const double vals[] = {1.0, 1.25, (1.0 / 0.75), 1.5, (1.0 / 0.6), 1.75, 2.0, 2.25, 2.5, 2.75, 3.0};\n\nstatic const size_t numVals = sizeof(vals) / sizeof(double);\n\nvoid OBSBasicSettings::ResetDownscales(uint32_t cx, uint32_t cy, bool ignoreAllSignals)\n{\n\tQString advRescale;\n\tQString advRecRescale;\n\tQString advFFRescale;\n\tQString oldOutputRes;\n\tstring bestScale;\n\tint bestPixelDiff = 0x7FFFFFFF;\n\tuint32_t out_cx = outputCX;\n\tuint32_t out_cy = outputCY;\n\n\tadvRescale = ui->advOutRescale->lineEdit()->text();\n\tadvRecRescale = ui->advOutRecRescale->lineEdit()->text();\n\tadvFFRescale = ui->advOutFFRescale->lineEdit()->text();\n\n\tbool lockedOutputRes = !ui->outputResolution->isEditable();\n\n\tif (!lockedOutputRes) {\n\t\tui->outputResolution->blockSignals(true);\n\t\tui->outputResolution->clear();\n\t}\n\tif (ignoreAllSignals) {\n\t\tui->advOutRescale->blockSignals(true);\n\t\tui->advOutRecRescale->blockSignals(true);\n\t\tui->advOutFFRescale->blockSignals(true);\n\t}\n\tui->advOutRescale->clear();\n\tui->advOutRecRescale->clear();\n\tui->advOutFFRescale->clear();\n\n\tif (!out_cx || !out_cy) {\n\t\tout_cx = cx;\n\t\tout_cy = cy;\n\t\toldOutputRes = ui->baseResolution->lineEdit()->text();\n\t} else {\n\t\toldOutputRes = QString::number(out_cx) + \"x\" + QString::number(out_cy);\n\t}\n\n\tfor (size_t idx = 0; idx < numVals; idx++) {\n\t\tuint32_t downscaleCX = uint32_t(double(cx) / vals[idx]);\n\t\tuint32_t downscaleCY = uint32_t(double(cy) / vals[idx]);\n\t\tuint32_t outDownscaleCX = uint32_t(double(out_cx) / vals[idx]);\n\t\tuint32_t outDownscaleCY = uint32_t(double(out_cy) / vals[idx]);\n\n\t\tdownscaleCX &= 0xFFFFFFFC;\n\t\tdownscaleCY &= 0xFFFFFFFE;\n\t\toutDownscaleCX &= 0xFFFFFFFE;\n\t\toutDownscaleCY &= 0xFFFFFFFE;\n\n\t\tstring res = ResString(downscaleCX, downscaleCY);\n\t\tstring outRes = ResString(outDownscaleCX, outDownscaleCY);\n\t\tif (!lockedOutputRes)\n\t\t\tui->outputResolution->addItem(res.c_str());\n\t\tui->advOutRescale->addItem(outRes.c_str());\n\t\tui->advOutRecRescale->addItem(outRes.c_str());\n\t\tui->advOutFFRescale->addItem(outRes.c_str());\n\n\t\t/* always try to find the closest output resolution to the\n\t\t * previously set output resolution */\n\t\tint newPixelCount = int(downscaleCX * downscaleCY);\n\t\tint oldPixelCount = int(out_cx * out_cy);\n\t\tint diff = abs(newPixelCount - oldPixelCount);\n\n\t\tif (diff < bestPixelDiff) {\n\t\t\tbestScale = res;\n\t\t\tbestPixelDiff = diff;\n\t\t}\n\t}\n\n\tstring res = ResString(cx, cy);\n\n\tif (!lockedOutputRes) {\n\t\tfloat baseAspect = float(cx) / float(cy);\n\t\tfloat outputAspect = float(out_cx) / float(out_cy);\n\t\tbool closeAspect = close_float(baseAspect, outputAspect, 0.01f);\n\n\t\tif (closeAspect) {\n\t\t\tui->outputResolution->lineEdit()->setText(oldOutputRes);\n\t\t\ton_outputResolution_editTextChanged(oldOutputRes);\n\t\t} else {\n\t\t\tui->outputResolution->lineEdit()->setText(bestScale.c_str());\n\t\t\ton_outputResolution_editTextChanged(bestScale.c_str());\n\t\t}\n\n\t\tui->outputResolution->blockSignals(false);\n\n\t\tif (!closeAspect) {\n\t\t\tui->outputResolution->setProperty(\"changed\", QVariant(true));\n\t\t\tvideoChanged = true;\n\t\t}\n\t}\n\n\tif (advRescale.isEmpty())\n\t\tadvRescale = res.c_str();\n\tif (advRecRescale.isEmpty())\n\t\tadvRecRescale = res.c_str();\n\tif (advFFRescale.isEmpty())\n\t\tadvFFRescale = res.c_str();\n\n\tui->advOutRescale->lineEdit()->setText(advRescale);\n\tui->advOutRecRescale->lineEdit()->setText(advRecRescale);\n\tui->advOutFFRescale->lineEdit()->setText(advFFRescale);\n\n\tif (ignoreAllSignals) {\n\t\tui->advOutRescale->blockSignals(false);\n\t\tui->advOutRecRescale->blockSignals(false);\n\t\tui->advOutFFRescale->blockSignals(false);\n\t}\n}\n\nvoid OBSBasicSettings::LoadDownscaleFilters()\n{\n\tQString downscaleFilter = ui->downscaleFilter->currentData().toString();\n\tif (downscaleFilter.isEmpty())\n\t\tdownscaleFilter = config_get_string(main->Config(), \"Video\", \"ScaleType\");\n\n\tui->downscaleFilter->clear();\n\tif (ui->baseResolution->currentText() == ui->outputResolution->currentText()) {\n\t\tui->downscaleFilter->setEnabled(false);\n\t\tui->downscaleFilter->addItem(QTStr(\"Basic.Settings.Video.DownscaleFilter.Unavailable\"),\n\t\t\t\t\t     downscaleFilter);\n\t} else {\n\t\tui->downscaleFilter->setEnabled(true);\n\t\tui->downscaleFilter->addItem(QTStr(\"Basic.Settings.Video.DownscaleFilter.Bilinear\"),\n\t\t\t\t\t     QT_UTF8(\"bilinear\"));\n\t\tui->downscaleFilter->addItem(QTStr(\"Basic.Settings.Video.DownscaleFilter.Area\"), QT_UTF8(\"area\"));\n\t\tui->downscaleFilter->addItem(QTStr(\"Basic.Settings.Video.DownscaleFilter.Bicubic\"), QT_UTF8(\"bicubic\"));\n\t\tui->downscaleFilter->addItem(QTStr(\"Basic.Settings.Video.DownscaleFilter.Lanczos\"), QT_UTF8(\"lanczos\"));\n\n\t\tif (downscaleFilter == \"bilinear\")\n\t\t\tui->downscaleFilter->setCurrentIndex(0);\n\t\telse if (downscaleFilter == \"lanczos\")\n\t\t\tui->downscaleFilter->setCurrentIndex(3);\n\t\telse if (downscaleFilter == \"area\")\n\t\t\tui->downscaleFilter->setCurrentIndex(1);\n\t\telse\n\t\t\tui->downscaleFilter->setCurrentIndex(2);\n\t}\n}\n\nvoid OBSBasicSettings::LoadResolutionLists()\n{\n\tuint32_t cx = config_get_uint(main->Config(), \"Video\", \"BaseCX\");\n\tuint32_t cy = config_get_uint(main->Config(), \"Video\", \"BaseCY\");\n\tuint32_t out_cx = config_get_uint(main->Config(), \"Video\", \"OutputCX\");\n\tuint32_t out_cy = config_get_uint(main->Config(), \"Video\", \"OutputCY\");\n\n\tui->baseResolution->clear();\n\n\tauto addRes = [this](int cx, int cy) {\n\t\tQString res = ResString(cx, cy).c_str();\n\t\tif (ui->baseResolution->findText(res) == -1)\n\t\t\tui->baseResolution->addItem(res);\n\t};\n\n\tfor (QScreen *screen : QGuiApplication::screens()) {\n\t\tQSize as = screen->size();\n\t\tuint32_t as_width = as.width();\n\t\tuint32_t as_height = as.height();\n\n\t\t// Calculate physical screen resolution based on the virtual screen resolution\n\t\t// They might differ if scaling is enabled, e.g. for HiDPI screens\n\t\tas_width = round(as_width * screen->devicePixelRatio());\n\t\tas_height = round(as_height * screen->devicePixelRatio());\n\n\t\taddRes(as_width, as_height);\n\t}\n\n\taddRes(1920, 1080);\n\taddRes(1280, 720);\n\n\tstring outputResString = ResString(out_cx, out_cy);\n\n\tui->baseResolution->lineEdit()->setText(ResString(cx, cy).c_str());\n\n\tRecalcOutputResPixels(outputResString.c_str());\n\tResetDownscales(cx, cy);\n\n\tui->outputResolution->lineEdit()->setText(outputResString.c_str());\n\n\tstd::tuple<int, int> aspect = aspect_ratio(cx, cy);\n\n\tui->baseAspect->setText(\n\t\tQTStr(\"AspectRatio\").arg(QString::number(std::get<0>(aspect)), QString::number(std::get<1>(aspect))));\n}\n\nstatic inline void LoadFPSCommon(OBSBasic *main, Ui::OBSBasicSettings *ui)\n{\n\tconst char *val = config_get_string(main->Config(), \"Video\", \"FPSCommon\");\n\n\tint idx = ui->fpsCommon->findText(val);\n\tif (idx == -1)\n\t\tidx = 4;\n\tui->fpsCommon->setCurrentIndex(idx);\n}\n\nstatic inline void LoadFPSInteger(OBSBasic *main, Ui::OBSBasicSettings *ui)\n{\n\tuint32_t val = config_get_uint(main->Config(), \"Video\", \"FPSInt\");\n\tui->fpsInteger->setValue(val);\n}\n\nstatic inline void LoadFPSFraction(OBSBasic *main, Ui::OBSBasicSettings *ui)\n{\n\tuint32_t num = config_get_uint(main->Config(), \"Video\", \"FPSNum\");\n\tuint32_t den = config_get_uint(main->Config(), \"Video\", \"FPSDen\");\n\n\tui->fpsNumerator->setValue(num);\n\tui->fpsDenominator->setValue(den);\n}\n\nvoid OBSBasicSettings::LoadFPSData()\n{\n\tLoadFPSCommon(main, ui.get());\n\tLoadFPSInteger(main, ui.get());\n\tLoadFPSFraction(main, ui.get());\n\n\tuint32_t fpsType = config_get_uint(main->Config(), \"Video\", \"FPSType\");\n\tif (fpsType > 2)\n\t\tfpsType = 0;\n\n\tui->fpsType->setCurrentIndex(fpsType);\n\tui->fpsTypes->setCurrentIndex(fpsType);\n}\n\nvoid OBSBasicSettings::LoadVideoSettings()\n{\n\tloading = true;\n\n\tif (obs_video_active()) {\n\t\tui->videoPage->setEnabled(false);\n\t\tui->videoMsg->setText(QTStr(\"Basic.Settings.Video.CurrentlyActive\"));\n\t}\n\n\tLoadResolutionLists();\n\tLoadFPSData();\n\tLoadDownscaleFilters();\n\n\tloading = false;\n}\n\nstatic inline bool IsSurround(const char *speakers)\n{\n\tstatic const char *surroundLayouts[] = {\"2.1\", \"4.0\", \"4.1\", \"5.1\", \"7.1\", nullptr};\n\n\tif (!speakers || !*speakers)\n\t\treturn false;\n\n\tconst char **curLayout = surroundLayouts;\n\tfor (; *curLayout; ++curLayout) {\n\t\tif (strcmp(*curLayout, speakers) == 0) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid OBSBasicSettings::LoadSimpleOutputSettings()\n{\n\tconst char *path = config_get_string(main->Config(), \"SimpleOutput\", \"FilePath\");\n\tbool noSpace = config_get_bool(main->Config(), \"SimpleOutput\", \"FileNameWithoutSpace\");\n\tconst char *format = config_get_string(main->Config(), \"SimpleOutput\", \"RecFormat2\");\n\tint videoBitrate = config_get_uint(main->Config(), \"SimpleOutput\", \"VBitrate\");\n\tconst char *streamEnc = config_get_string(main->Config(), \"SimpleOutput\", \"StreamEncoder\");\n\tconst char *streamAudioEnc = config_get_string(main->Config(), \"SimpleOutput\", \"StreamAudioEncoder\");\n\tint audioBitrate = config_get_uint(main->Config(), \"SimpleOutput\", \"ABitrate\");\n\tbool advanced = config_get_bool(main->Config(), \"SimpleOutput\", \"UseAdvanced\");\n\tconst char *preset = config_get_string(main->Config(), \"SimpleOutput\", \"Preset\");\n\tconst char *qsvPreset = config_get_string(main->Config(), \"SimpleOutput\", \"QSVPreset\");\n\tconst char *nvPreset = config_get_string(main->Config(), \"SimpleOutput\", \"NVENCPreset2\");\n\tconst char *amdPreset = config_get_string(main->Config(), \"SimpleOutput\", \"AMDPreset\");\n\tconst char *amdAV1Preset = config_get_string(main->Config(), \"SimpleOutput\", \"AMDAV1Preset\");\n\tconst char *custom = config_get_string(main->Config(), \"SimpleOutput\", \"x264Settings\");\n\tconst char *recQual = config_get_string(main->Config(), \"SimpleOutput\", \"RecQuality\");\n\tconst char *recEnc = config_get_string(main->Config(), \"SimpleOutput\", \"RecEncoder\");\n\tconst char *recAudioEnc = config_get_string(main->Config(), \"SimpleOutput\", \"RecAudioEncoder\");\n\tconst char *muxCustom = config_get_string(main->Config(), \"SimpleOutput\", \"MuxerCustom\");\n\tbool replayBuf = config_get_bool(main->Config(), \"SimpleOutput\", \"RecRB\");\n\tint rbTime = config_get_int(main->Config(), \"SimpleOutput\", \"RecRBTime\");\n\tint rbSize = config_get_int(main->Config(), \"SimpleOutput\", \"RecRBSize\");\n\tint tracks = config_get_int(main->Config(), \"SimpleOutput\", \"RecTracks\");\n\n\tui->simpleOutRecTrack1->setChecked(tracks & (1 << 0));\n\tui->simpleOutRecTrack2->setChecked(tracks & (1 << 1));\n\tui->simpleOutRecTrack3->setChecked(tracks & (1 << 2));\n\tui->simpleOutRecTrack4->setChecked(tracks & (1 << 3));\n\tui->simpleOutRecTrack5->setChecked(tracks & (1 << 4));\n\tui->simpleOutRecTrack6->setChecked(tracks & (1 << 5));\n\n\tcurPreset = preset;\n\tcurQSVPreset = qsvPreset;\n\tcurNVENCPreset = nvPreset;\n\tcurAMDPreset = amdPreset;\n\tcurAMDAV1Preset = amdAV1Preset;\n\n\tbool isOpus = strcmp(streamAudioEnc, \"opus\") == 0;\n\taudioBitrate = isOpus ? FindClosestAvailableSimpleOpusBitrate(audioBitrate)\n\t\t\t      : FindClosestAvailableSimpleAACBitrate(audioBitrate);\n\n\tui->simpleOutputPath->setText(path);\n\tui->simpleNoSpace->setChecked(noSpace);\n\tui->simpleOutputVBitrate->setValue(videoBitrate);\n\n\tint idx = ui->simpleOutRecFormat->findData(format);\n\tui->simpleOutRecFormat->setCurrentIndex(idx);\n\n\tPopulateSimpleBitrates(ui->simpleOutputABitrate, isOpus);\n\n\tconst char *speakers = config_get_string(main->Config(), \"Audio\", \"ChannelSetup\");\n\n\t// restrict list of bitrates when multichannel is OFF\n\tif (!IsSurround(speakers))\n\t\tRestrictResetBitrates({ui->simpleOutputABitrate}, 320);\n\n\tSetComboByName(ui->simpleOutputABitrate, QString::number(audioBitrate));\n\n\tui->simpleOutAdvanced->setChecked(advanced);\n\tui->simpleOutCustom->setText(custom);\n\n\tidx = ui->simpleOutRecQuality->findData(QString(recQual));\n\tif (idx == -1)\n\t\tidx = 0;\n\tui->simpleOutRecQuality->setCurrentIndex(idx);\n\n\tidx = ui->simpleOutStrEncoder->findData(QString(streamEnc));\n\tif (idx == -1)\n\t\tidx = 0;\n\tui->simpleOutStrEncoder->setCurrentIndex(idx);\n\n\tidx = ui->simpleOutStrAEncoder->findData(QString(streamAudioEnc));\n\tif (idx == -1)\n\t\tidx = 0;\n\tui->simpleOutStrAEncoder->setCurrentIndex(idx);\n\n\tidx = ui->simpleOutRecEncoder->findData(QString(recEnc));\n\tui->simpleOutRecEncoder->setCurrentIndex(idx);\n\n\tidx = ui->simpleOutRecAEncoder->findData(QString(recAudioEnc));\n\tui->simpleOutRecAEncoder->setCurrentIndex(idx);\n\n\tui->simpleOutMuxCustom->setText(muxCustom);\n\n\tui->simpleReplayBuf->setChecked(replayBuf);\n\tui->simpleRBSecMax->setValue(rbTime);\n\tui->simpleRBMegsMax->setValue(rbSize);\n\n\tSimpleStreamingEncoderChanged();\n}\n\nstatic inline QString makeFormatToolTip()\n{\n\tstatic const char *format_list[][2] = {\n\t\t{\"CCYY\", \"FilenameFormatting.TT.CCYY\"}, {\"YY\", \"FilenameFormatting.TT.YY\"},\n\t\t{\"MM\", \"FilenameFormatting.TT.MM\"},     {\"DD\", \"FilenameFormatting.TT.DD\"},\n\t\t{\"hh\", \"FilenameFormatting.TT.hh\"},     {\"mm\", \"FilenameFormatting.TT.mm\"},\n\t\t{\"ss\", \"FilenameFormatting.TT.ss\"},     {\"%\", \"FilenameFormatting.TT.Percent\"},\n\t\t{\"a\", \"FilenameFormatting.TT.a\"},       {\"A\", \"FilenameFormatting.TT.A\"},\n\t\t{\"b\", \"FilenameFormatting.TT.b\"},       {\"B\", \"FilenameFormatting.TT.B\"},\n\t\t{\"d\", \"FilenameFormatting.TT.d\"},       {\"H\", \"FilenameFormatting.TT.H\"},\n\t\t{\"I\", \"FilenameFormatting.TT.I\"},       {\"m\", \"FilenameFormatting.TT.m\"},\n\t\t{\"M\", \"FilenameFormatting.TT.M\"},       {\"p\", \"FilenameFormatting.TT.p\"},\n\t\t{\"s\", \"FilenameFormatting.TT.s\"},       {\"S\", \"FilenameFormatting.TT.S\"},\n\t\t{\"y\", \"FilenameFormatting.TT.y\"},       {\"Y\", \"FilenameFormatting.TT.Y\"},\n\t\t{\"z\", \"FilenameFormatting.TT.z\"},       {\"Z\", \"FilenameFormatting.TT.Z\"},\n\t\t{\"FPS\", \"FilenameFormatting.TT.FPS\"},   {\"CRES\", \"FilenameFormatting.TT.CRES\"},\n\t\t{\"ORES\", \"FilenameFormatting.TT.ORES\"}, {\"VF\", \"FilenameFormatting.TT.VF\"},\n\t};\n\n\tQString html = \"<table>\";\n\n\tfor (auto f : format_list) {\n\t\thtml += \"<tr><th align='left'>%\";\n\t\thtml += f[0];\n\t\thtml += \"</th><td>\";\n\t\thtml += QTStr(f[1]);\n\t\thtml += \"</td></tr>\";\n\t}\n\n\thtml += \"</table>\";\n\treturn html;\n}\n\nvoid OBSBasicSettings::LoadAdvOutputStreamingSettings()\n{\n\tconst char *rescaleRes = config_get_string(main->Config(), \"AdvOut\", \"RescaleRes\");\n\tint rescaleFilter = config_get_int(main->Config(), \"AdvOut\", \"RescaleFilter\");\n\tint trackIndex = config_get_int(main->Config(), \"AdvOut\", \"TrackIndex\");\n\tint audioMixes = config_get_int(main->Config(), \"AdvOut\", \"StreamMultiTrackAudioMixes\");\n\tui->advOutRescale->setEnabled(rescaleFilter != OBS_SCALE_DISABLE);\n\tui->advOutRescale->setCurrentText(rescaleRes);\n\n\tint idx = ui->advOutRescaleFilter->findData(rescaleFilter);\n\tif (idx != -1)\n\t\tui->advOutRescaleFilter->setCurrentIndex(idx);\n\n\tQStringList specList = QTStr(\"FilenameFormatting.completer\").split(QRegularExpression(\"\\n\"));\n\tQCompleter *specCompleter = new QCompleter(specList);\n\tspecCompleter->setCaseSensitivity(Qt::CaseSensitive);\n\tspecCompleter->setFilterMode(Qt::MatchContains);\n\tui->filenameFormatting->setCompleter(specCompleter);\n\tui->filenameFormatting->setToolTip(makeFormatToolTip());\n\n\tswitch (trackIndex) {\n\tcase 1:\n\t\tui->advOutTrack1->setChecked(true);\n\t\tbreak;\n\tcase 2:\n\t\tui->advOutTrack2->setChecked(true);\n\t\tbreak;\n\tcase 3:\n\t\tui->advOutTrack3->setChecked(true);\n\t\tbreak;\n\tcase 4:\n\t\tui->advOutTrack4->setChecked(true);\n\t\tbreak;\n\tcase 5:\n\t\tui->advOutTrack5->setChecked(true);\n\t\tbreak;\n\tcase 6:\n\t\tui->advOutTrack6->setChecked(true);\n\t\tbreak;\n\t}\n\tui->advOutMultiTrack1->setChecked(audioMixes & (1 << 0));\n\tui->advOutMultiTrack2->setChecked(audioMixes & (1 << 1));\n\tui->advOutMultiTrack3->setChecked(audioMixes & (1 << 2));\n\tui->advOutMultiTrack4->setChecked(audioMixes & (1 << 3));\n\tui->advOutMultiTrack5->setChecked(audioMixes & (1 << 4));\n\tui->advOutMultiTrack6->setChecked(audioMixes & (1 << 5));\n\n\tobs_service_t *service_obj = main->GetService();\n\tconst char *protocol = nullptr;\n\tprotocol = obs_service_get_protocol(service_obj);\n\tSwapMultiTrack(protocol);\n}\n\nOBSPropertiesView *OBSBasicSettings::CreateEncoderPropertyView(const char *encoder, const char *path, bool changed)\n{\n\tOBSDataAutoRelease settings = obs_encoder_defaults(encoder);\n\tOBSPropertiesView *view;\n\n\tif (path) {\n\t\tconst OBSBasic *basic = OBSBasic::Get();\n\t\tconst OBSProfile &currentProfile = basic->GetCurrentProfile();\n\n\t\tconst std::filesystem::path jsonFilePath = currentProfile.path / std::filesystem::u8path(path);\n\n\t\tif (!jsonFilePath.empty()) {\n\t\t\tobs_data_t *data = obs_data_create_from_json_file_safe(jsonFilePath.u8string().c_str(), \"bak\");\n\t\t\tobs_data_apply(settings, data);\n\t\t\tobs_data_release(data);\n\t\t}\n\t}\n\n\tview = new OBSPropertiesView(settings.Get(), encoder, (PropertiesReloadCallback)obs_get_encoder_properties,\n\t\t\t\t     170);\n\tview->setFrameShape(QFrame::NoFrame);\n\tview->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);\n\tview->setProperty(\"changed\", QVariant(changed));\n\tview->setScrolling(false);\n\tQObject::connect(view, &OBSPropertiesView::Changed, this, &OBSBasicSettings::OutputsChanged);\n\n\treturn view;\n}\n\nvoid OBSBasicSettings::LoadAdvOutputStreamingEncoderProperties()\n{\n\tconst char *type = config_get_string(main->Config(), \"AdvOut\", \"Encoder\");\n\n\tdelete streamEncoderProps;\n\tstreamEncoderProps = CreateEncoderPropertyView(type, \"streamEncoder.json\");\n\tstreamEncoderProps->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);\n\tui->advOutEncoderLayout->addWidget(streamEncoderProps);\n\n\tconnect(streamEncoderProps, &OBSPropertiesView::Changed, this, &OBSBasicSettings::UpdateStreamDelayEstimate);\n\tconnect(streamEncoderProps, &OBSPropertiesView::Changed, this, &OBSBasicSettings::AdvReplayBufferChanged);\n\n\tcurAdvStreamEncoder = type;\n\n\tif (!SetComboByValue(ui->advOutEncoder, type)) {\n\t\tuint32_t caps = obs_get_encoder_caps(type);\n\t\tif ((caps & ENCODER_HIDE_FLAGS) != 0) {\n\t\t\tQString encName = QT_UTF8(obs_encoder_get_display_name(type));\n\t\t\tif (caps & OBS_ENCODER_CAP_DEPRECATED)\n\t\t\t\tencName += \" (\" + QTStr(\"Deprecated\") + \")\";\n\n\t\t\tui->advOutEncoder->insertItem(0, encName, QT_UTF8(type));\n\t\t\tSetComboByValue(ui->advOutEncoder, type);\n\t\t}\n\t}\n\n\tUpdateStreamDelayEstimate();\n}\n\nvoid OBSBasicSettings::LoadAdvOutputRecordingSettings()\n{\n\tconst char *type = config_get_string(main->Config(), \"AdvOut\", \"RecType\");\n\tconst char *format = config_get_string(main->Config(), \"AdvOut\", \"RecFormat2\");\n\tconst char *path = config_get_string(main->Config(), \"AdvOut\", \"RecFilePath\");\n\tbool noSpace = config_get_bool(main->Config(), \"AdvOut\", \"RecFileNameWithoutSpace\");\n\tconst char *rescaleRes = config_get_string(main->Config(), \"AdvOut\", \"RecRescaleRes\");\n\tint rescaleFilter = config_get_int(main->Config(), \"AdvOut\", \"RecRescaleFilter\");\n\tconst char *muxCustom = config_get_string(main->Config(), \"AdvOut\", \"RecMuxerCustom\");\n\tint tracks = config_get_int(main->Config(), \"AdvOut\", \"RecTracks\");\n\tint flvTrack = config_get_int(main->Config(), \"AdvOut\", \"FLVTrack\");\n\tbool splitFile = config_get_bool(main->Config(), \"AdvOut\", \"RecSplitFile\");\n\tconst char *splitFileType = config_get_string(main->Config(), \"AdvOut\", \"RecSplitFileType\");\n\tint splitFileTime = config_get_int(main->Config(), \"AdvOut\", \"RecSplitFileTime\");\n\tint splitFileSize = config_get_int(main->Config(), \"AdvOut\", \"RecSplitFileSize\");\n\n\tint typeIndex = (astrcmpi(type, \"FFmpeg\") == 0) ? 1 : 0;\n\tui->advOutRecType->setCurrentIndex(typeIndex);\n\tui->advOutRecPath->setText(path);\n\tui->advOutNoSpace->setChecked(noSpace);\n\tui->advOutRecRescale->setCurrentText(rescaleRes);\n\tint idx = ui->advOutRecRescaleFilter->findData(rescaleFilter);\n\tif (idx != -1)\n\t\tui->advOutRecRescaleFilter->setCurrentIndex(idx);\n\tui->advOutMuxCustom->setText(muxCustom);\n\n\tidx = ui->advOutRecFormat->findData(format);\n\tui->advOutRecFormat->setCurrentIndex(idx);\n\n\tui->advOutRecTrack1->setChecked(tracks & (1 << 0));\n\tui->advOutRecTrack2->setChecked(tracks & (1 << 1));\n\tui->advOutRecTrack3->setChecked(tracks & (1 << 2));\n\tui->advOutRecTrack4->setChecked(tracks & (1 << 3));\n\tui->advOutRecTrack5->setChecked(tracks & (1 << 4));\n\tui->advOutRecTrack6->setChecked(tracks & (1 << 5));\n\n\tif (astrcmpi(splitFileType, \"Size\") == 0)\n\t\tidx = 1;\n\telse if (astrcmpi(splitFileType, \"Manual\") == 0)\n\t\tidx = 2;\n\telse\n\t\tidx = 0;\n\tui->advOutSplitFile->setChecked(splitFile);\n\tui->advOutSplitFileType->setCurrentIndex(idx);\n\tui->advOutSplitFileTime->setValue(splitFileTime);\n\tui->advOutSplitFileSize->setValue(splitFileSize);\n\n\tswitch (flvTrack) {\n\tcase 1:\n\t\tui->flvTrack1->setChecked(true);\n\t\tbreak;\n\tcase 2:\n\t\tui->flvTrack2->setChecked(true);\n\t\tbreak;\n\tcase 3:\n\t\tui->flvTrack3->setChecked(true);\n\t\tbreak;\n\tcase 4:\n\t\tui->flvTrack4->setChecked(true);\n\t\tbreak;\n\tcase 5:\n\t\tui->flvTrack5->setChecked(true);\n\t\tbreak;\n\tcase 6:\n\t\tui->flvTrack6->setChecked(true);\n\t\tbreak;\n\tdefault:\n\t\tui->flvTrack1->setChecked(true);\n\t\tbreak;\n\t}\n}\n\nvoid OBSBasicSettings::LoadAdvOutputRecordingEncoderProperties()\n{\n\tconst char *type = config_get_string(main->Config(), \"AdvOut\", \"RecEncoder\");\n\n\tdelete recordEncoderProps;\n\trecordEncoderProps = nullptr;\n\n\tif (astrcmpi(type, \"none\") != 0) {\n\t\trecordEncoderProps = CreateEncoderPropertyView(type, \"recordEncoder.json\");\n\t\trecordEncoderProps->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);\n\t\tui->advOutRecEncoderProps->layout()->addWidget(recordEncoderProps);\n\t\tconnect(recordEncoderProps, &OBSPropertiesView::Changed, this,\n\t\t\t&OBSBasicSettings::AdvReplayBufferChanged);\n\t}\n\n\tcurAdvRecordEncoder = type;\n\n\tif (!SetComboByValue(ui->advOutRecEncoder, type)) {\n\t\tuint32_t caps = obs_get_encoder_caps(type);\n\t\tif ((caps & ENCODER_HIDE_FLAGS) != 0) {\n\t\t\tQString encName = QT_UTF8(obs_encoder_get_display_name(type));\n\t\t\tif (caps & OBS_ENCODER_CAP_DEPRECATED)\n\t\t\t\tencName += \" (\" + QTStr(\"Deprecated\") + \")\";\n\n\t\t\tui->advOutRecEncoder->insertItem(1, encName, QT_UTF8(type));\n\t\t\tSetComboByValue(ui->advOutRecEncoder, type);\n\t\t} else {\n\t\t\tui->advOutRecEncoder->setCurrentIndex(-1);\n\t\t}\n\t}\n}\n\nstatic void SelectFormat(QComboBox *combo, const char *name, const char *mimeType)\n{\n\tFFmpegFormat format{name, mimeType};\n\n\tfor (int i = 0; i < combo->count(); i++) {\n\t\tQVariant v = combo->itemData(i);\n\t\tif (!v.isNull()) {\n\t\t\tif (format == v.value<FFmpegFormat>()) {\n\t\t\t\tcombo->setCurrentIndex(i);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tcombo->setCurrentIndex(0);\n}\n\nstatic void SelectEncoder(QComboBox *combo, const char *name, int id)\n{\n\tint idx = FindEncoder(combo, name, id);\n\tif (idx >= 0)\n\t\tcombo->setCurrentIndex(idx);\n}\n\nvoid OBSBasicSettings::LoadAdvOutputFFmpegSettings()\n{\n\tbool saveFile = config_get_bool(main->Config(), \"AdvOut\", \"FFOutputToFile\");\n\tconst char *path = config_get_string(main->Config(), \"AdvOut\", \"FFFilePath\");\n\tbool noSpace = config_get_bool(main->Config(), \"AdvOut\", \"FFFileNameWithoutSpace\");\n\tconst char *url = config_get_string(main->Config(), \"AdvOut\", \"FFURL\");\n\tconst char *format = config_get_string(main->Config(), \"AdvOut\", \"FFFormat\");\n\tconst char *mimeType = config_get_string(main->Config(), \"AdvOut\", \"FFFormatMimeType\");\n\tconst char *muxCustom = config_get_string(main->Config(), \"AdvOut\", \"FFMCustom\");\n\tint videoBitrate = config_get_int(main->Config(), \"AdvOut\", \"FFVBitrate\");\n\tint gopSize = config_get_int(main->Config(), \"AdvOut\", \"FFVGOPSize\");\n\tbool rescale = config_get_bool(main->Config(), \"AdvOut\", \"FFRescale\");\n\tbool codecCompat = config_get_bool(main->Config(), \"AdvOut\", \"FFIgnoreCompat\");\n\tconst char *rescaleRes = config_get_string(main->Config(), \"AdvOut\", \"FFRescaleRes\");\n\tconst char *vEncoder = config_get_string(main->Config(), \"AdvOut\", \"FFVEncoder\");\n\tint vEncoderId = config_get_int(main->Config(), \"AdvOut\", \"FFVEncoderId\");\n\tconst char *vEncCustom = config_get_string(main->Config(), \"AdvOut\", \"FFVCustom\");\n\tint audioBitrate = config_get_int(main->Config(), \"AdvOut\", \"FFABitrate\");\n\tint audioMixes = config_get_int(main->Config(), \"AdvOut\", \"FFAudioMixes\");\n\tconst char *aEncoder = config_get_string(main->Config(), \"AdvOut\", \"FFAEncoder\");\n\tint aEncoderId = config_get_int(main->Config(), \"AdvOut\", \"FFAEncoderId\");\n\tconst char *aEncCustom = config_get_string(main->Config(), \"AdvOut\", \"FFACustom\");\n\n\tui->advOutFFType->setCurrentIndex(saveFile ? 0 : 1);\n\tui->advOutFFRecPath->setText(QT_UTF8(path));\n\tui->advOutFFNoSpace->setChecked(noSpace);\n\tui->advOutFFURL->setText(QT_UTF8(url));\n\tSelectFormat(ui->advOutFFFormat, format, mimeType);\n\tui->advOutFFMCfg->setText(muxCustom);\n\tui->advOutFFVBitrate->setValue(videoBitrate);\n\tui->advOutFFVGOPSize->setValue(gopSize);\n\tui->advOutFFUseRescale->setChecked(rescale);\n\tui->advOutFFIgnoreCompat->setChecked(codecCompat);\n\tui->advOutFFRescale->setEnabled(rescale);\n\tui->advOutFFRescale->setCurrentText(rescaleRes);\n\tSelectEncoder(ui->advOutFFVEncoder, vEncoder, vEncoderId);\n\tui->advOutFFVCfg->setText(vEncCustom);\n\tui->advOutFFABitrate->setValue(audioBitrate);\n\tSelectEncoder(ui->advOutFFAEncoder, aEncoder, aEncoderId);\n\tui->advOutFFACfg->setText(aEncCustom);\n\n\tui->advOutFFTrack1->setChecked(audioMixes & (1 << 0));\n\tui->advOutFFTrack2->setChecked(audioMixes & (1 << 1));\n\tui->advOutFFTrack3->setChecked(audioMixes & (1 << 2));\n\tui->advOutFFTrack4->setChecked(audioMixes & (1 << 3));\n\tui->advOutFFTrack5->setChecked(audioMixes & (1 << 4));\n\tui->advOutFFTrack6->setChecked(audioMixes & (1 << 5));\n}\n\nvoid OBSBasicSettings::LoadAdvOutputAudioSettings()\n{\n\tint track1Bitrate = config_get_uint(main->Config(), \"AdvOut\", \"Track1Bitrate\");\n\tint track2Bitrate = config_get_uint(main->Config(), \"AdvOut\", \"Track2Bitrate\");\n\tint track3Bitrate = config_get_uint(main->Config(), \"AdvOut\", \"Track3Bitrate\");\n\tint track4Bitrate = config_get_uint(main->Config(), \"AdvOut\", \"Track4Bitrate\");\n\tint track5Bitrate = config_get_uint(main->Config(), \"AdvOut\", \"Track5Bitrate\");\n\tint track6Bitrate = config_get_uint(main->Config(), \"AdvOut\", \"Track6Bitrate\");\n\tconst char *name1 = config_get_string(main->Config(), \"AdvOut\", \"Track1Name\");\n\tconst char *name2 = config_get_string(main->Config(), \"AdvOut\", \"Track2Name\");\n\tconst char *name3 = config_get_string(main->Config(), \"AdvOut\", \"Track3Name\");\n\tconst char *name4 = config_get_string(main->Config(), \"AdvOut\", \"Track4Name\");\n\tconst char *name5 = config_get_string(main->Config(), \"AdvOut\", \"Track5Name\");\n\tconst char *name6 = config_get_string(main->Config(), \"AdvOut\", \"Track6Name\");\n\n\tconst char *encoder_id = config_get_string(main->Config(), \"AdvOut\", \"AudioEncoder\");\n\tconst char *rec_encoder_id = config_get_string(main->Config(), \"AdvOut\", \"RecAudioEncoder\");\n\n\tPopulateAdvancedBitrates({ui->advOutTrack1Bitrate, ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,\n\t\t\t\t  ui->advOutTrack4Bitrate, ui->advOutTrack5Bitrate, ui->advOutTrack6Bitrate},\n\t\t\t\t encoder_id, strcmp(rec_encoder_id, \"none\") != 0 ? rec_encoder_id : encoder_id);\n\n\ttrack1Bitrate = FindClosestAvailableAudioBitrate(ui->advOutTrack1Bitrate, track1Bitrate);\n\ttrack2Bitrate = FindClosestAvailableAudioBitrate(ui->advOutTrack2Bitrate, track2Bitrate);\n\ttrack3Bitrate = FindClosestAvailableAudioBitrate(ui->advOutTrack3Bitrate, track3Bitrate);\n\ttrack4Bitrate = FindClosestAvailableAudioBitrate(ui->advOutTrack4Bitrate, track4Bitrate);\n\ttrack5Bitrate = FindClosestAvailableAudioBitrate(ui->advOutTrack5Bitrate, track5Bitrate);\n\ttrack6Bitrate = FindClosestAvailableAudioBitrate(ui->advOutTrack6Bitrate, track6Bitrate);\n\n\t// restrict list of bitrates when multichannel is OFF\n\tconst char *speakers = config_get_string(main->Config(), \"Audio\", \"ChannelSetup\");\n\n\t// restrict list of bitrates when multichannel is OFF\n\tif (!IsSurround(speakers)) {\n\t\tRestrictResetBitrates({ui->advOutTrack1Bitrate, ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,\n\t\t\t\t       ui->advOutTrack4Bitrate, ui->advOutTrack5Bitrate, ui->advOutTrack6Bitrate},\n\t\t\t\t      320);\n\t}\n\n\tSetComboByName(ui->advOutTrack1Bitrate, QString::number(track1Bitrate));\n\tSetComboByName(ui->advOutTrack2Bitrate, QString::number(track2Bitrate));\n\tSetComboByName(ui->advOutTrack3Bitrate, QString::number(track3Bitrate));\n\tSetComboByName(ui->advOutTrack4Bitrate, QString::number(track4Bitrate));\n\tSetComboByName(ui->advOutTrack5Bitrate, QString::number(track5Bitrate));\n\tSetComboByName(ui->advOutTrack6Bitrate, QString::number(track6Bitrate));\n\n\tui->advOutTrack1Name->setText(name1);\n\tui->advOutTrack2Name->setText(name2);\n\tui->advOutTrack3Name->setText(name3);\n\tui->advOutTrack4Name->setText(name4);\n\tui->advOutTrack5Name->setText(name5);\n\tui->advOutTrack6Name->setText(name6);\n}\n\nvoid OBSBasicSettings::LoadOutputSettings()\n{\n\tloading = true;\n\n\tResetEncoders();\n\n\tconst char *mode = config_get_string(main->Config(), \"Output\", \"Mode\");\n\n\tint modeIdx = astrcmpi(mode, \"Advanced\") == 0 ? 1 : 0;\n\tui->outputMode->setCurrentIndex(modeIdx);\n\n\tLoadSimpleOutputSettings();\n\tLoadAdvOutputStreamingSettings();\n\tLoadAdvOutputStreamingEncoderProperties();\n\n\tconst char *type = config_get_string(main->Config(), \"AdvOut\", \"AudioEncoder\");\n\tif (!SetComboByValue(ui->advOutAEncoder, type))\n\t\tui->advOutAEncoder->setCurrentIndex(-1);\n\n\tLoadAdvOutputRecordingSettings();\n\tLoadAdvOutputRecordingEncoderProperties();\n\ttype = config_get_string(main->Config(), \"AdvOut\", \"RecAudioEncoder\");\n\tif (!SetComboByValue(ui->advOutRecAEncoder, type))\n\t\tui->advOutRecAEncoder->setCurrentIndex(-1);\n\tLoadAdvOutputFFmpegSettings();\n\tLoadAdvOutputAudioSettings();\n\n\tif (obs_video_active()) {\n\t\tui->outputMode->setEnabled(false);\n\t\tui->outputModeLabel->setEnabled(false);\n\t\tui->simpleOutStrEncoderLabel->setEnabled(false);\n\t\tui->simpleOutStrEncoder->setEnabled(false);\n\t\tui->simpleOutStrAEncoderLabel->setEnabled(false);\n\t\tui->simpleOutStrAEncoder->setEnabled(false);\n\t\tui->simpleRecordingGroupBox->setEnabled(false);\n\t\tui->simpleReplayBuf->setEnabled(false);\n\t\tui->advOutTopContainer->setEnabled(false);\n\t\tui->advOutRecTopContainer->setEnabled(false);\n\t\tui->advOutRecTypeContainer->setEnabled(false);\n\t\tui->advOutputAudioTracksTab->setEnabled(false);\n\t\tui->advNetworkGroupBox->setEnabled(false);\n\t}\n\n\tloading = false;\n}\n\nvoid OBSBasicSettings::SetAdvOutputFFmpegEnablement(FFmpegCodecType encoderType, bool enabled, bool enableEncoder)\n{\n\tbool rescale = config_get_bool(main->Config(), \"AdvOut\", \"FFRescale\");\n\n\tswitch (encoderType) {\n\tcase FFmpegCodecType::VIDEO:\n\t\tui->advOutFFVBitrate->setEnabled(enabled);\n\t\tui->advOutFFVGOPSize->setEnabled(enabled);\n\t\tui->advOutFFUseRescale->setEnabled(enabled);\n\t\tui->advOutFFRescale->setEnabled(enabled && rescale);\n\t\tui->advOutFFVEncoder->setEnabled(enabled || enableEncoder);\n\t\tui->advOutFFVCfg->setEnabled(enabled);\n\t\tbreak;\n\tcase FFmpegCodecType::AUDIO:\n\t\tui->advOutFFABitrate->setEnabled(enabled);\n\t\tui->advOutFFAEncoder->setEnabled(enabled || enableEncoder);\n\t\tui->advOutFFACfg->setEnabled(enabled);\n\t\tui->advOutFFTrack1->setEnabled(enabled);\n\t\tui->advOutFFTrack2->setEnabled(enabled);\n\t\tui->advOutFFTrack3->setEnabled(enabled);\n\t\tui->advOutFFTrack4->setEnabled(enabled);\n\t\tui->advOutFFTrack5->setEnabled(enabled);\n\t\tui->advOutFFTrack6->setEnabled(enabled);\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nstatic inline void LoadListValue(QComboBox *widget, const char *text, const char *val)\n{\n\twidget->addItem(QT_UTF8(text), QT_UTF8(val));\n}\n\nvoid OBSBasicSettings::LoadListValues(QComboBox *widget, obs_property_t *prop, int index)\n{\n\tsize_t count = obs_property_list_item_count(prop);\n\n\tOBSSourceAutoRelease source = obs_get_output_source(index);\n\tconst char *deviceId = nullptr;\n\tOBSDataAutoRelease settings = nullptr;\n\n\tif (source) {\n\t\tsettings = obs_source_get_settings(source);\n\t\tif (settings)\n\t\t\tdeviceId = obs_data_get_string(settings, \"device_id\");\n\t}\n\n\twidget->addItem(QTStr(\"Basic.Settings.Audio.Disabled\"), \"disabled\");\n\n\tfor (size_t i = 0; i < count; i++) {\n\t\tconst char *name = obs_property_list_item_name(prop, i);\n\t\tconst char *val = obs_property_list_item_string(prop, i);\n\t\tLoadListValue(widget, name, val);\n\t}\n\n\tif (deviceId) {\n\t\tQVariant var(QT_UTF8(deviceId));\n\t\tint idx = widget->findData(var);\n\t\tif (idx != -1) {\n\t\t\twidget->setCurrentIndex(idx);\n\t\t} else {\n\t\t\twidget->insertItem(0,\n\t\t\t\t\t   QTStr(\"Basic.Settings.Audio.\"\n\t\t\t\t\t\t \"UnknownAudioDevice\"),\n\t\t\t\t\t   var);\n\t\t\twidget->setCurrentIndex(0);\n\t\t\tHighlightGroupBoxLabel(ui->audioDevicesGroupBox, widget, \"errorLabel\");\n\t\t}\n\t}\n}\n\nvoid OBSBasicSettings::LoadAudioDevices()\n{\n\tconst char *input_id = App()->InputAudioSource();\n\tconst char *output_id = App()->OutputAudioSource();\n\n\tobs_properties_t *input_props = obs_get_source_properties(input_id);\n\tobs_properties_t *output_props = obs_get_source_properties(output_id);\n\n\tif (input_props) {\n\t\tobs_property_t *inputs = obs_properties_get(input_props, \"device_id\");\n\t\tLoadListValues(ui->auxAudioDevice1, inputs, 3);\n\t\tLoadListValues(ui->auxAudioDevice2, inputs, 4);\n\t\tLoadListValues(ui->auxAudioDevice3, inputs, 5);\n\t\tLoadListValues(ui->auxAudioDevice4, inputs, 6);\n\t\tobs_properties_destroy(input_props);\n\t}\n\n\tif (output_props) {\n\t\tobs_property_t *outputs = obs_properties_get(output_props, \"device_id\");\n\t\tLoadListValues(ui->desktopAudioDevice1, outputs, 1);\n\t\tLoadListValues(ui->desktopAudioDevice2, outputs, 2);\n\t\tobs_properties_destroy(output_props);\n\t}\n\n\tif (obs_video_active()) {\n\t\tui->sampleRate->setEnabled(false);\n\t\tui->channelSetup->setEnabled(false);\n\t}\n}\n\n#define NBSP \"\\xC2\\xA0\"\n\nvoid OBSBasicSettings::LoadAudioSources()\n{\n\tif (ui->audioSourceLayout->rowCount() > 0) {\n\t\tQLayoutItem *forDeletion = ui->audioSourceLayout->takeAt(0);\n\t\tforDeletion->widget()->deleteLater();\n\t\tdelete forDeletion;\n\t}\n\tauto layout = new QFormLayout();\n\tlayout->setVerticalSpacing(15);\n\tlayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);\n\n\taudioSourceSignals.clear();\n\taudioSources.clear();\n\n\tauto widget = new QWidget();\n\twidget->setLayout(layout);\n\tui->audioSourceLayout->addRow(widget);\n\n\tconst char *enablePtm = Str(\"Basic.Settings.Audio.EnablePushToMute\");\n\tconst char *ptmDelay = Str(\"Basic.Settings.Audio.PushToMuteDelay\");\n\tconst char *enablePtt = Str(\"Basic.Settings.Audio.EnablePushToTalk\");\n\tconst char *pttDelay = Str(\"Basic.Settings.Audio.PushToTalkDelay\");\n\tauto AddSource = [&](obs_source_t *source) {\n\t\tif (!(obs_source_get_output_flags(source) & OBS_SOURCE_AUDIO))\n\t\t\treturn true;\n\n\t\tauto form = new QFormLayout();\n\t\tform->setVerticalSpacing(0);\n\t\tform->setHorizontalSpacing(5);\n\t\tform->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);\n\n\t\tauto ptmCB = new SilentUpdateCheckBox();\n\t\tptmCB->setText(enablePtm);\n\t\tptmCB->setChecked(obs_source_push_to_mute_enabled(source));\n\t\tform->addRow(ptmCB);\n\n\t\tauto ptmSB = new SilentUpdateSpinBox();\n\t\tptmSB->setSuffix(NBSP \"ms\");\n\t\tptmSB->setRange(0, INT_MAX);\n\t\tptmSB->setValue(obs_source_get_push_to_mute_delay(source));\n\t\tform->addRow(ptmDelay, ptmSB);\n\n\t\tauto pttCB = new SilentUpdateCheckBox();\n\t\tpttCB->setText(enablePtt);\n\t\tpttCB->setChecked(obs_source_push_to_talk_enabled(source));\n\t\tform->addRow(pttCB);\n\n\t\tauto pttSB = new SilentUpdateSpinBox();\n\t\tpttSB->setSuffix(NBSP \"ms\");\n\t\tpttSB->setRange(0, INT_MAX);\n\t\tpttSB->setValue(obs_source_get_push_to_talk_delay(source));\n\t\tform->addRow(pttDelay, pttSB);\n\n\t\tHookWidget(ptmCB, CHECK_CHANGED, AUDIO_CHANGED);\n\t\tHookWidget(ptmSB, SCROLL_CHANGED, AUDIO_CHANGED);\n\t\tHookWidget(pttCB, CHECK_CHANGED, AUDIO_CHANGED);\n\t\tHookWidget(pttSB, SCROLL_CHANGED, AUDIO_CHANGED);\n\n\t\taudioSourceSignals.reserve(audioSourceSignals.size() + 4);\n\n\t\tauto handler = obs_source_get_signal_handler(source);\n\t\taudioSourceSignals.emplace_back(\n\t\t\thandler, \"push_to_mute_changed\",\n\t\t\t[](void *data, calldata_t *param) {\n\t\t\t\tQMetaObject::invokeMethod(static_cast<QObject *>(data), \"setCheckedSilently\",\n\t\t\t\t\t\t\t  Q_ARG(bool, calldata_bool(param, \"enabled\")));\n\t\t\t},\n\t\t\tptmCB);\n\t\taudioSourceSignals.emplace_back(\n\t\t\thandler, \"push_to_mute_delay\",\n\t\t\t[](void *data, calldata_t *param) {\n\t\t\t\tQMetaObject::invokeMethod(static_cast<QObject *>(data), \"setValueSilently\",\n\t\t\t\t\t\t\t  Q_ARG(int, calldata_int(param, \"delay\")));\n\t\t\t},\n\t\t\tptmSB);\n\t\taudioSourceSignals.emplace_back(\n\t\t\thandler, \"push_to_talk_changed\",\n\t\t\t[](void *data, calldata_t *param) {\n\t\t\t\tQMetaObject::invokeMethod(static_cast<QObject *>(data), \"setCheckedSilently\",\n\t\t\t\t\t\t\t  Q_ARG(bool, calldata_bool(param, \"enabled\")));\n\t\t\t},\n\t\t\tpttCB);\n\t\taudioSourceSignals.emplace_back(\n\t\t\thandler, \"push_to_talk_delay\",\n\t\t\t[](void *data, calldata_t *param) {\n\t\t\t\tQMetaObject::invokeMethod(static_cast<QObject *>(data), \"setValueSilently\",\n\t\t\t\t\t\t\t  Q_ARG(int, calldata_int(param, \"delay\")));\n\t\t\t},\n\t\t\tpttSB);\n\n\t\taudioSources.emplace_back(OBSGetWeakRef(source), ptmCB, ptmSB, pttCB, pttSB);\n\n\t\tauto label = new OBSSourceLabel(source);\n\t\tTruncateLabel(label, label->text());\n\t\tlabel->setMinimumSize(QSize(170, 0));\n\t\tlabel->setAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);\n\t\tconnect(label, &OBSSourceLabel::removed, this,\n\t\t\t[=]() { QMetaObject::invokeMethod(this, \"ReloadAudioSources\"); });\n\t\tconnect(label, &OBSSourceLabel::destroyed, this,\n\t\t\t[=]() { QMetaObject::invokeMethod(this, \"ReloadAudioSources\"); });\n\n\t\tlayout->addRow(label, form);\n\t\treturn true;\n\t};\n\n\tusing AddSource_t = decltype(AddSource);\n\tobs_enum_sources(\n\t\t[](void *data, obs_source_t *source) {\n\t\t\tauto &AddSource = *static_cast<AddSource_t *>(data);\n\t\t\tif (!obs_source_removed(source))\n\t\t\t\tAddSource(source);\n\t\t\treturn true;\n\t\t},\n\t\tstatic_cast<void *>(&AddSource));\n\n\tif (layout->rowCount() == 0)\n\t\tui->audioHotkeysGroupBox->hide();\n\telse\n\t\tui->audioHotkeysGroupBox->show();\n}\n\nvoid OBSBasicSettings::LoadAudioSettings()\n{\n\tuint32_t sampleRate = config_get_uint(main->Config(), \"Audio\", \"SampleRate\");\n\tconst char *speakers = config_get_string(main->Config(), \"Audio\", \"ChannelSetup\");\n\tdouble meterDecayRate = config_get_double(main->Config(), \"Audio\", \"MeterDecayRate\");\n\tuint32_t peakMeterTypeIdx = config_get_uint(main->Config(), \"Audio\", \"PeakMeterType\");\n\tbool enableLLAudioBuffering = config_get_bool(App()->GetUserConfig(), \"Audio\", \"LowLatencyAudioBuffering\");\n\n\tloading = true;\n\n\tconst char *str;\n\tif (sampleRate == 48000)\n\t\tstr = \"48 kHz\";\n\telse\n\t\tstr = \"44.1 kHz\";\n\n\tint sampleRateIdx = ui->sampleRate->findText(str);\n\tif (sampleRateIdx != -1)\n\t\tui->sampleRate->setCurrentIndex(sampleRateIdx);\n\n\tif (strcmp(speakers, \"Mono\") == 0)\n\t\tui->channelSetup->setCurrentIndex(0);\n\telse if (strcmp(speakers, \"2.1\") == 0)\n\t\tui->channelSetup->setCurrentIndex(2);\n\telse if (strcmp(speakers, \"4.0\") == 0)\n\t\tui->channelSetup->setCurrentIndex(3);\n\telse if (strcmp(speakers, \"4.1\") == 0)\n\t\tui->channelSetup->setCurrentIndex(4);\n\telse if (strcmp(speakers, \"5.1\") == 0)\n\t\tui->channelSetup->setCurrentIndex(5);\n\telse if (strcmp(speakers, \"7.1\") == 0)\n\t\tui->channelSetup->setCurrentIndex(6);\n\telse\n\t\tui->channelSetup->setCurrentIndex(1);\n\n\tif (meterDecayRate == VOLUME_METER_DECAY_MEDIUM)\n\t\tui->meterDecayRate->setCurrentIndex(1);\n\telse if (meterDecayRate == VOLUME_METER_DECAY_SLOW)\n\t\tui->meterDecayRate->setCurrentIndex(2);\n\telse\n\t\tui->meterDecayRate->setCurrentIndex(0);\n\n\tui->peakMeterType->setCurrentIndex(peakMeterTypeIdx);\n\tui->lowLatencyBuffering->setChecked(enableLLAudioBuffering);\n\n\tLoadAudioDevices();\n\tLoadAudioSources();\n\n\tloading = false;\n}\n\nvoid OBSBasicSettings::UpdateColorFormatSpaceWarning()\n{\n\tconst QString format = ui->colorFormat->currentData().toString();\n\tswitch (ui->colorSpace->currentIndex()) {\n\tcase 3: /* Rec.2100 (PQ) */\n\tcase 4: /* Rec.2100 (HLG) */\n\t\tif ((format == \"P010\") || (format == \"P216\") || (format == \"P416\")) {\n\t\t\tui->advancedMsg2->clear();\n\t\t\tui->advancedMsg2->setVisible(false);\n\t\t} else if (format == \"I010\") {\n\t\t\tui->advancedMsg2->setText(QTStr(\"Basic.Settings.Advanced.FormatWarning\"));\n\t\t\tui->advancedMsg2->setVisible(true);\n\t\t} else {\n\t\t\tui->advancedMsg2->setText(QTStr(\"Basic.Settings.Advanced.FormatWarning2100\"));\n\t\t\tui->advancedMsg2->setVisible(true);\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tif (format == \"NV12\") {\n\t\t\tui->advancedMsg2->clear();\n\t\t\tui->advancedMsg2->setVisible(false);\n\t\t} else if ((format == \"I010\") || (format == \"P010\") || (format == \"P216\") || (format == \"P416\")) {\n\t\t\tui->advancedMsg2->setText(QTStr(\"Basic.Settings.Advanced.FormatWarningPreciseSdr\"));\n\t\t\tui->advancedMsg2->setVisible(true);\n\t\t} else {\n\t\t\tui->advancedMsg2->setText(QTStr(\"Basic.Settings.Advanced.FormatWarning\"));\n\t\t\tui->advancedMsg2->setVisible(true);\n\t\t}\n\t}\n}\n\nvoid OBSBasicSettings::LoadAdvancedSettings()\n{\n\tconst char *videoColorFormat = config_get_string(main->Config(), \"Video\", \"ColorFormat\");\n\tconst char *videoColorSpace = config_get_string(main->Config(), \"Video\", \"ColorSpace\");\n\tconst char *videoColorRange = config_get_string(main->Config(), \"Video\", \"ColorRange\");\n\tuint32_t sdrWhiteLevel = (uint32_t)config_get_uint(main->Config(), \"Video\", \"SdrWhiteLevel\");\n\tuint32_t hdrNominalPeakLevel = (uint32_t)config_get_uint(main->Config(), \"Video\", \"HdrNominalPeakLevel\");\n\n\tQString monDevName;\n\tQString monDevId;\n\tif (obs_audio_monitoring_available()) {\n\t\tmonDevName = config_get_string(main->Config(), \"Audio\", \"MonitoringDeviceName\");\n\t\tmonDevId = config_get_string(main->Config(), \"Audio\", \"MonitoringDeviceId\");\n\t}\n\tbool enableDelay = config_get_bool(main->Config(), \"Output\", \"DelayEnable\");\n\tint delaySec = config_get_int(main->Config(), \"Output\", \"DelaySec\");\n\tbool preserveDelay = config_get_bool(main->Config(), \"Output\", \"DelayPreserve\");\n\tbool reconnect = config_get_bool(main->Config(), \"Output\", \"Reconnect\");\n\tint retryDelay = config_get_int(main->Config(), \"Output\", \"RetryDelay\");\n\tint maxRetries = config_get_int(main->Config(), \"Output\", \"MaxRetries\");\n\tconst char *filename = config_get_string(main->Config(), \"Output\", \"FilenameFormatting\");\n\tbool overwriteIfExists = config_get_bool(main->Config(), \"Output\", \"OverwriteIfExists\");\n\tconst char *bindIP = config_get_string(main->Config(), \"Output\", \"BindIP\");\n\tconst char *rbPrefix = config_get_string(main->Config(), \"SimpleOutput\", \"RecRBPrefix\");\n\tconst char *rbSuffix = config_get_string(main->Config(), \"SimpleOutput\", \"RecRBSuffix\");\n\tbool replayBuf = config_get_bool(main->Config(), \"AdvOut\", \"RecRB\");\n\tint rbTime = config_get_int(main->Config(), \"AdvOut\", \"RecRBTime\");\n\tint rbSize = config_get_int(main->Config(), \"AdvOut\", \"RecRBSize\");\n\tbool autoRemux = config_get_bool(main->Config(), \"Video\", \"AutoRemux\");\n\tconst char *hotkeyFocusType = config_get_string(App()->GetUserConfig(), \"General\", \"HotkeyFocusType\");\n\tbool dynBitrate = config_get_bool(main->Config(), \"Output\", \"DynamicBitrate\");\n\tconst char *ipFamily = config_get_string(main->Config(), \"Output\", \"IPFamily\");\n\tbool confirmOnExit = config_get_bool(App()->GetUserConfig(), \"General\", \"ConfirmOnExit\");\n\n\tloading = true;\n\n\tLoadRendererList();\n\n\tif (obs_audio_monitoring_available() && !SetComboByValue(ui->monitoringDevice, monDevId))\n\t\tSetInvalidValue(ui->monitoringDevice, monDevName, monDevId);\n\n\tui->confirmOnExit->setChecked(confirmOnExit);\n\n\tui->filenameFormatting->setText(filename);\n\tui->overwriteIfExists->setChecked(overwriteIfExists);\n\tui->simpleRBPrefix->setText(rbPrefix);\n\tui->simpleRBSuffix->setText(rbSuffix);\n\n\tui->advReplayBuf->setChecked(replayBuf);\n\tui->advRBSecMax->setValue(rbTime);\n\tui->advRBMegsMax->setValue(rbSize);\n\n\tui->reconnectEnable->setChecked(reconnect);\n\tui->reconnectRetryDelay->setValue(retryDelay);\n\tui->reconnectMaxRetries->setValue(maxRetries);\n\n\tui->streamDelaySec->setValue(delaySec);\n\tui->streamDelayPreserve->setChecked(preserveDelay);\n\tui->streamDelayEnable->setChecked(enableDelay);\n\tui->autoRemux->setChecked(autoRemux);\n\tui->dynBitrate->setChecked(dynBitrate);\n\n\tSetComboByValue(ui->colorFormat, videoColorFormat);\n\tSetComboByValue(ui->colorSpace, videoColorSpace);\n\tSetComboByValue(ui->colorRange, videoColorRange);\n\tui->sdrWhiteLevel->setValue(sdrWhiteLevel);\n\tui->hdrNominalPeakLevel->setValue(hdrNominalPeakLevel);\n\n\tSetComboByValue(ui->ipFamily, ipFamily);\n\tif (!SetComboByValue(ui->bindToIP, bindIP))\n\t\tSetInvalidValue(ui->bindToIP, bindIP, bindIP);\n\n\tif (obs_video_active()) {\n\t\tui->advancedVideoContainer->setEnabled(false);\n\t}\n\n#ifdef __APPLE__\n\tbool disableOSXVSync = config_get_bool(App()->GetAppConfig(), \"Video\", \"DisableOSXVSync\");\n\tbool resetOSXVSync = config_get_bool(App()->GetAppConfig(), \"Video\", \"ResetOSXVSyncOnExit\");\n\tui->disableOSXVSync->setChecked(disableOSXVSync);\n\tui->resetOSXVSync->setChecked(resetOSXVSync);\n\tui->resetOSXVSync->setEnabled(disableOSXVSync);\n#elif _WIN32\n\tbool disableAudioDucking = config_get_bool(App()->GetAppConfig(), \"Audio\", \"DisableAudioDucking\");\n\tui->disableAudioDucking->setChecked(disableAudioDucking);\n\n\tconst char *processPriority = config_get_string(App()->GetAppConfig(), \"General\", \"ProcessPriority\");\n\tbool enableNewSocketLoop = config_get_bool(main->Config(), \"Output\", \"NewSocketLoopEnable\");\n\tbool enableLowLatencyMode = config_get_bool(main->Config(), \"Output\", \"LowLatencyEnable\");\n\n\tint idx = ui->processPriority->findData(processPriority);\n\tif (idx == -1)\n\t\tidx = ui->processPriority->findData(\"Normal\");\n\tui->processPriority->setCurrentIndex(idx);\n\n\tui->enableNewSocketLoop->setChecked(enableNewSocketLoop);\n\tui->enableLowLatencyMode->setChecked(enableLowLatencyMode);\n\tui->enableLowLatencyMode->setToolTip(QTStr(\"Basic.Settings.Advanced.Network.TCPPacing.Tooltip\"));\n#endif\n#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)\n\tbool browserHWAccel = config_get_bool(App()->GetAppConfig(), \"General\", \"BrowserHWAccel\");\n\tui->browserHWAccel->setChecked(browserHWAccel);\n\tprevBrowserAccel = ui->browserHWAccel->isChecked();\n#endif\n\n\tSetComboByValue(ui->hotkeyFocusType, hotkeyFocusType);\n\n\tloading = false;\n}\n\ntemplate<typename Func>\nstatic inline void LayoutHotkey(OBSBasicSettings *settings, obs_hotkey_id id, obs_hotkey_t *key, Func &&fun,\n\t\t\t\tconst map<obs_hotkey_id, vector<obs_key_combination_t>> &keys)\n{\n\tauto *label = new OBSHotkeyLabel;\n\tQString text = QT_UTF8(obs_hotkey_get_description(key));\n\n\tlabel->setProperty(\"fullName\", text);\n\tTruncateLabel(label, text);\n\n\tOBSHotkeyWidget *hw = nullptr;\n\n\tauto combos = keys.find(id);\n\tif (combos == std::end(keys))\n\t\thw = new OBSHotkeyWidget(settings, id, obs_hotkey_get_name(key), settings);\n\telse\n\t\thw = new OBSHotkeyWidget(settings, id, obs_hotkey_get_name(key), settings, combos->second);\n\n\thw->label = label;\n\thw->setAccessibleName(text);\n\tlabel->widget = hw;\n\n\tfun(key, label, hw);\n}\n\ntemplate<typename Func, typename T> static QLabel *makeLabel(T &t, Func &&getName)\n{\n\tQLabel *label = new QLabel(getName(t));\n\tlabel->setStyleSheet(\"font-weight: bold;\");\n\treturn label;\n}\n\ntemplate<typename Func> static QLabel *makeLabel(const OBSSource &source, Func &&)\n{\n\tOBSSourceLabel *label = new OBSSourceLabel(source);\n\tlabel->setStyleSheet(\"font-weight: bold;\");\n\tQString name = QT_UTF8(obs_source_get_name(source));\n\tTruncateLabel(label, name);\n\n\treturn label;\n}\n\ntemplate<typename Func, typename T>\nstatic inline void AddHotkeys(QFormLayout &layout, Func &&getName,\n\t\t\t      std::vector<std::tuple<T, QPointer<QLabel>, QPointer<QWidget>>> &hotkeys)\n{\n\tif (hotkeys.empty())\n\t\treturn;\n\n\tlayout.setItem(layout.rowCount(), QFormLayout::SpanningRole, new QSpacerItem(0, 10));\n\n\tusing tuple_type = std::tuple<T, QPointer<QLabel>, QPointer<QWidget>>;\n\n\tstable_sort(begin(hotkeys), end(hotkeys), [&](const tuple_type &a, const tuple_type &b) {\n\t\tconst auto &o_a = get<0>(a);\n\t\tconst auto &o_b = get<0>(b);\n\t\treturn o_a != o_b && string(getName(o_a)) < getName(o_b);\n\t});\n\n\tstring prevName;\n\tfor (const auto &hotkey : hotkeys) {\n\t\tconst auto &o = get<0>(hotkey);\n\t\tconst char *name = getName(o);\n\t\tif (prevName != name) {\n\t\t\tprevName = name;\n\t\t\tlayout.setItem(layout.rowCount(), QFormLayout::SpanningRole, new QSpacerItem(0, 10));\n\t\t\tlayout.addRow(makeLabel(o, getName));\n\t\t}\n\n\t\tauto hlabel = get<1>(hotkey);\n\t\tauto widget = get<2>(hotkey);\n\t\tlayout.addRow(hlabel, widget);\n\t}\n}\n\nvoid OBSBasicSettings::LoadHotkeySettings(obs_hotkey_id ignoreKey)\n{\n\thotkeys.clear();\n\tif (ui->hotkeyFormLayout->rowCount() > 0) {\n\t\tQLayoutItem *forDeletion = ui->hotkeyFormLayout->takeAt(0);\n\t\tforDeletion->widget()->deleteLater();\n\t\tdelete forDeletion;\n\t}\n\tui->hotkeyFilterSearch->blockSignals(true);\n\tui->hotkeyFilterInput->blockSignals(true);\n\tui->hotkeyFilterSearch->setText(\"\");\n\tui->hotkeyFilterInput->ResetKey();\n\tui->hotkeyFilterSearch->blockSignals(false);\n\tui->hotkeyFilterInput->blockSignals(false);\n\n\tusing keys_t = map<obs_hotkey_id, vector<obs_key_combination_t>>;\n\tkeys_t keys;\n\tobs_enum_hotkey_bindings(\n\t\t[](void *data, size_t, obs_hotkey_binding_t *binding) {\n\t\t\tauto &keys = *static_cast<keys_t *>(data);\n\n\t\t\tkeys[obs_hotkey_binding_get_hotkey_id(binding)].emplace_back(\n\t\t\t\tobs_hotkey_binding_get_key_combination(binding));\n\n\t\t\treturn true;\n\t\t},\n\t\t&keys);\n\n\tQFormLayout *hotkeysLayout = new QFormLayout();\n\thotkeysLayout->setVerticalSpacing(0);\n\thotkeysLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);\n\thotkeysLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignTrailing | Qt::AlignVCenter);\n\tauto hotkeyChildWidget = new QWidget();\n\thotkeyChildWidget->setLayout(hotkeysLayout);\n\tui->hotkeyFormLayout->addRow(hotkeyChildWidget);\n\n\tusing namespace std;\n\tusing encoders_elem_t = tuple<OBSEncoder, QPointer<QLabel>, QPointer<QWidget>>;\n\tusing outputs_elem_t = tuple<OBSOutput, QPointer<QLabel>, QPointer<QWidget>>;\n\tusing services_elem_t = tuple<OBSService, QPointer<QLabel>, QPointer<QWidget>>;\n\tusing sources_elem_t = tuple<OBSSource, QPointer<QLabel>, QPointer<QWidget>>;\n\tvector<encoders_elem_t> encoders;\n\tvector<outputs_elem_t> outputs;\n\tvector<services_elem_t> services;\n\tvector<sources_elem_t> scenes;\n\tvector<sources_elem_t> sources;\n\n\tvector<obs_hotkey_id> pairIds;\n\tmap<obs_hotkey_id, pair<obs_hotkey_id, OBSHotkeyLabel *>> pairLabels;\n\n\tusing std::move;\n\n\tauto HandleEncoder = [&](void *registerer, OBSHotkeyLabel *label, OBSHotkeyWidget *hw) {\n\t\tauto weak_encoder = static_cast<obs_weak_encoder_t *>(registerer);\n\t\tauto encoder = OBSGetStrongRef(weak_encoder);\n\n\t\tif (!encoder)\n\t\t\treturn true;\n\n\t\tencoders.emplace_back(std::move(encoder), label, hw);\n\t\treturn false;\n\t};\n\n\tauto HandleOutput = [&](void *registerer, OBSHotkeyLabel *label, OBSHotkeyWidget *hw) {\n\t\tauto weak_output = static_cast<obs_weak_output_t *>(registerer);\n\t\tauto output = OBSGetStrongRef(weak_output);\n\n\t\tif (!output)\n\t\t\treturn true;\n\n\t\toutputs.emplace_back(std::move(output), label, hw);\n\t\treturn false;\n\t};\n\n\tauto HandleService = [&](void *registerer, OBSHotkeyLabel *label, OBSHotkeyWidget *hw) {\n\t\tauto weak_service = static_cast<obs_weak_service_t *>(registerer);\n\t\tauto service = OBSGetStrongRef(weak_service);\n\n\t\tif (!service)\n\t\t\treturn true;\n\n\t\tservices.emplace_back(std::move(service), label, hw);\n\t\treturn false;\n\t};\n\n\tauto HandleSource = [&](void *registerer, OBSHotkeyLabel *label, OBSHotkeyWidget *hw) {\n\t\tauto weak_source = static_cast<obs_weak_source_t *>(registerer);\n\t\tauto source = OBSGetStrongRef(weak_source);\n\n\t\tif (!source)\n\t\t\treturn true;\n\n\t\tif (obs_scene_from_source(source))\n\t\t\tscenes.emplace_back(source, label, hw);\n\t\telse if (obs_source_get_name(source) != NULL)\n\t\t\tsources.emplace_back(source, label, hw);\n\n\t\treturn false;\n\t};\n\n\tauto RegisterHotkey = [&](obs_hotkey_t *key, OBSHotkeyLabel *label, OBSHotkeyWidget *hw) {\n\t\tauto registerer_type = obs_hotkey_get_registerer_type(key);\n\t\tvoid *registerer = obs_hotkey_get_registerer(key);\n\n\t\tobs_hotkey_id partner = obs_hotkey_get_pair_partner_id(key);\n\t\tif (partner != OBS_INVALID_HOTKEY_ID) {\n\t\t\tpairLabels.emplace(obs_hotkey_get_id(key), make_pair(partner, label));\n\t\t\tpairIds.push_back(obs_hotkey_get_id(key));\n\t\t}\n\n\t\tusing std::move;\n\n\t\tswitch (registerer_type) {\n\t\tcase OBS_HOTKEY_REGISTERER_FRONTEND:\n\t\t\thotkeysLayout->addRow(label, hw);\n\t\t\tbreak;\n\n\t\tcase OBS_HOTKEY_REGISTERER_ENCODER:\n\t\t\tif (HandleEncoder(registerer, label, hw))\n\t\t\t\treturn;\n\t\t\tbreak;\n\n\t\tcase OBS_HOTKEY_REGISTERER_OUTPUT:\n\t\t\tif (HandleOutput(registerer, label, hw))\n\t\t\t\treturn;\n\t\t\tbreak;\n\n\t\tcase OBS_HOTKEY_REGISTERER_SERVICE:\n\t\t\tif (HandleService(registerer, label, hw))\n\t\t\t\treturn;\n\t\t\tbreak;\n\n\t\tcase OBS_HOTKEY_REGISTERER_SOURCE:\n\t\t\tif (HandleSource(registerer, label, hw))\n\t\t\t\treturn;\n\t\t\tbreak;\n\t\t}\n\n\t\thotkeys.emplace_back(registerer_type == OBS_HOTKEY_REGISTERER_FRONTEND, hw);\n\t\tconnect(hw, &OBSHotkeyWidget::KeyChanged, this, [this, hotkeysLayout]() {\n\t\t\tHotkeysChanged();\n\t\t\tScanDuplicateHotkeys(hotkeysLayout);\n\t\t});\n\t\tconnect(hw, &OBSHotkeyWidget::SearchKey, this, [this](obs_key_combination_t combo) {\n\t\t\tui->hotkeyFilterSearch->setText(\"\");\n\t\t\tui->hotkeyFilterInput->HandleNewKey(combo);\n\t\t\tui->hotkeyFilterInput->KeyChanged(combo);\n\t\t});\n\t};\n\n\tauto data = make_tuple(RegisterHotkey, std::move(keys), ignoreKey, this);\n\tusing data_t = decltype(data);\n\tobs_enum_hotkeys(\n\t\t[](void *data, obs_hotkey_id id, obs_hotkey_t *key) {\n\t\t\tdata_t &d = *static_cast<data_t *>(data);\n\t\t\tif (id != get<2>(d))\n\t\t\t\tLayoutHotkey(get<3>(d), id, key, get<0>(d), get<1>(d));\n\t\t\treturn true;\n\t\t},\n\t\t&data);\n\n\tfor (auto keyId : pairIds) {\n\t\tauto data1 = pairLabels.find(keyId);\n\t\tif (data1 == end(pairLabels))\n\t\t\tcontinue;\n\n\t\tauto &label1 = data1->second.second;\n\t\tif (label1->pairPartner)\n\t\t\tcontinue;\n\n\t\tauto data2 = pairLabels.find(data1->second.first);\n\t\tif (data2 == end(pairLabels))\n\t\t\tcontinue;\n\n\t\tauto &label2 = data2->second.second;\n\t\tif (label2->pairPartner)\n\t\t\tcontinue;\n\n\t\tQString tt = QTStr(\"Basic.Settings.Hotkeys.Pair\");\n\t\tauto name1 = label1->text();\n\t\tauto name2 = label2->text();\n\n\t\tauto Update = [&](OBSHotkeyLabel *label, const QString &name, OBSHotkeyLabel *other,\n\t\t\t\t  const QString &otherName) {\n\t\t\tQString string = other->property(\"fullName\").value<QString>();\n\n\t\t\tif (string.isEmpty() || string.isNull())\n\t\t\t\tstring = otherName;\n\n\t\t\tlabel->setToolTip(tt.arg(string));\n\t\t\tlabel->setText(name + \" *\");\n\t\t\tlabel->pairPartner = other;\n\t\t};\n\t\tUpdate(label1, name1, label2, name2);\n\t\tUpdate(label2, name2, label1, name1);\n\t}\n\n\tAddHotkeys(*hotkeysLayout, obs_output_get_name, outputs);\n\tAddHotkeys(*hotkeysLayout, obs_source_get_name, scenes);\n\tAddHotkeys(*hotkeysLayout, obs_source_get_name, sources);\n\tAddHotkeys(*hotkeysLayout, obs_encoder_get_name, encoders);\n\tAddHotkeys(*hotkeysLayout, obs_service_get_name, services);\n\n\tScanDuplicateHotkeys(hotkeysLayout);\n\n\t/* After this function returns the UI can still be unresponsive for a bit.\n\t * So by deferring the call to unsetCursor() to the Qt event loop it will\n\t * take until it has actually finished processing the created widgets\n\t * before the cursor is reset. */\n\tQTimer::singleShot(1, this, &OBSBasicSettings::unsetCursor);\n\thotkeysLoaded = true;\n}\n\nvoid OBSBasicSettings::LoadSettings(bool changedOnly)\n{\n\tif (!changedOnly || generalChanged)\n\t\tLoadGeneralSettings();\n\tif (!changedOnly || stream1Changed)\n\t\tLoadStream1Settings();\n\tif (!changedOnly || outputsChanged)\n\t\tLoadOutputSettings();\n\tif (!changedOnly || audioChanged)\n\t\tLoadAudioSettings();\n\tif (!changedOnly || videoChanged)\n\t\tLoadVideoSettings();\n\tif (!changedOnly || a11yChanged)\n\t\tLoadA11ySettings();\n\tif (!changedOnly || appearanceChanged)\n\t\tLoadAppearanceSettings();\n\tif (!changedOnly || advancedChanged)\n\t\tLoadAdvancedSettings();\n}\n\nvoid OBSBasicSettings::SaveGeneralSettings()\n{\n\tint languageIndex = ui->language->currentIndex();\n\tQVariant langData = ui->language->itemData(languageIndex);\n\tstring language = langData.toString().toStdString();\n\n\tif (WidgetChanged(ui->language))\n\t\tconfig_set_string(App()->GetUserConfig(), \"General\", \"Language\", language.c_str());\n\n#if defined(_WIN32) || defined(ENABLE_SPARKLE_UPDATER)\n\tif (WidgetChanged(ui->enableAutoUpdates))\n\t\tconfig_set_bool(App()->GetAppConfig(), \"General\", \"EnableAutoUpdates\",\n\t\t\t\tui->enableAutoUpdates->isChecked());\n\tint branchIdx = ui->updateChannelBox->currentIndex();\n\tQString branchName = ui->updateChannelBox->itemData(branchIdx).toString();\n\n\tif (WidgetChanged(ui->updateChannelBox)) {\n\t\tconfig_set_string(App()->GetAppConfig(), \"General\", \"UpdateBranch\", QT_TO_UTF8(branchName));\n\t\tforceUpdateCheck = true;\n\t}\n#endif\n#ifdef _WIN32\n\tif (ui->hideOBSFromCapture && WidgetChanged(ui->hideOBSFromCapture)) {\n\t\tbool hide_window = ui->hideOBSFromCapture->isChecked();\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"HideOBSWindowsFromCapture\", hide_window);\n\n\t\tQWindowList windows = QGuiApplication::allWindows();\n\t\tfor (auto window : windows) {\n\t\t\tif (window->isVisible()) {\n\t\t\t\tmain->SetDisplayAffinity(window);\n\t\t\t}\n\t\t}\n\n\t\tblog(LOG_INFO, \"Hide OBS windows from screen capture: %s\", hide_window ? \"true\" : \"false\");\n\t}\n#endif\n\tif (WidgetChanged(ui->openStatsOnStartup))\n\t\tconfig_set_bool(main->Config(), \"General\", \"OpenStatsOnStartup\", ui->openStatsOnStartup->isChecked());\n\tif (WidgetChanged(ui->snappingEnabled))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SnappingEnabled\",\n\t\t\t\tui->snappingEnabled->isChecked());\n\tif (WidgetChanged(ui->screenSnapping))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"ScreenSnapping\",\n\t\t\t\tui->screenSnapping->isChecked());\n\tif (WidgetChanged(ui->centerSnapping))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"CenterSnapping\",\n\t\t\t\tui->centerSnapping->isChecked());\n\tif (WidgetChanged(ui->sourceSnapping))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SourceSnapping\",\n\t\t\t\tui->sourceSnapping->isChecked());\n\tif (WidgetChanged(ui->snapDistance))\n\t\tconfig_set_double(App()->GetUserConfig(), \"BasicWindow\", \"SnapDistance\", ui->snapDistance->value());\n\tif (WidgetChanged(ui->overflowAlwaysVisible) || WidgetChanged(ui->overflowHide) ||\n\t    WidgetChanged(ui->overflowSelectionHide)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"OverflowAlwaysVisible\",\n\t\t\t\tui->overflowAlwaysVisible->isChecked());\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"OverflowHidden\", ui->overflowHide->isChecked());\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"OverflowSelectionHidden\",\n\t\t\t\tui->overflowSelectionHide->isChecked());\n\t\tmain->UpdatePreviewOverflowSettings();\n\t}\n\tif (WidgetChanged(ui->previewSafeAreas)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowSafeAreas\",\n\t\t\t\tui->previewSafeAreas->isChecked());\n\t\tmain->UpdatePreviewSafeAreas();\n\t}\n\n\tif (WidgetChanged(ui->previewSpacingHelpers)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SpacingHelpersEnabled\",\n\t\t\t\tui->previewSpacingHelpers->isChecked());\n\t\tmain->UpdatePreviewSpacingHelpers();\n\t}\n\n\tif (WidgetChanged(ui->doubleClickSwitch))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"TransitionOnDoubleClick\",\n\t\t\t\tui->doubleClickSwitch->isChecked());\n\tif (WidgetChanged(ui->automaticSearch))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"AutomaticCollectionSearch\",\n\t\t\t\tui->automaticSearch->isChecked());\n\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"WarnBeforeStartingStream\",\n\t\t\tui->warnBeforeStreamStart->isChecked());\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"WarnBeforeStoppingStream\",\n\t\t\tui->warnBeforeStreamStop->isChecked());\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"WarnBeforeStoppingRecord\",\n\t\t\tui->warnBeforeRecordStop->isChecked());\n\n\tif (WidgetChanged(ui->hideProjectorCursor)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"HideProjectorCursor\",\n\t\t\t\tui->hideProjectorCursor->isChecked());\n\t\tmain->UpdateProjectorHideCursor();\n\t}\n\n\tif (WidgetChanged(ui->projectorAlwaysOnTop)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"ProjectorAlwaysOnTop\",\n\t\t\t\tui->projectorAlwaysOnTop->isChecked());\n#if defined(_WIN32) || defined(__APPLE__)\n\t\tmain->UpdateProjectorAlwaysOnTop(ui->projectorAlwaysOnTop->isChecked());\n#else\n\t\tmain->ResetProjectors();\n#endif\n\t}\n\n\tif (WidgetChanged(ui->recordWhenStreaming))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"RecordWhenStreaming\",\n\t\t\t\tui->recordWhenStreaming->isChecked());\n\tif (WidgetChanged(ui->keepRecordStreamStops))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"KeepRecordingWhenStreamStops\",\n\t\t\t\tui->keepRecordStreamStops->isChecked());\n\n\tif (WidgetChanged(ui->replayWhileStreaming))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"ReplayBufferWhileStreaming\",\n\t\t\t\tui->replayWhileStreaming->isChecked());\n\tif (WidgetChanged(ui->keepReplayStreamStops))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"KeepReplayBufferStreamStops\",\n\t\t\t\tui->keepReplayStreamStops->isChecked());\n\n\tif (WidgetChanged(ui->systemTrayEnabled)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayEnabled\",\n\t\t\t\tui->systemTrayEnabled->isChecked());\n\n\t\tmain->SystemTray(false);\n\t}\n\n\tif (WidgetChanged(ui->systemTrayWhenStarted))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayWhenStarted\",\n\t\t\t\tui->systemTrayWhenStarted->isChecked());\n\n\tif (WidgetChanged(ui->systemTrayAlways))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayMinimizeToTray\",\n\t\t\t\tui->systemTrayAlways->isChecked());\n\n\tif (WidgetChanged(ui->saveProjectors))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SaveProjectors\",\n\t\t\t\tui->saveProjectors->isChecked());\n\n\tif (WidgetChanged(ui->closeProjectors))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"CloseExistingProjectors\",\n\t\t\t\tui->closeProjectors->isChecked());\n\n\tif (WidgetChanged(ui->studioPortraitLayout)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"StudioPortraitLayout\",\n\t\t\t\tui->studioPortraitLayout->isChecked());\n\n\t\tmain->ResetUI();\n\t}\n\n\tif (WidgetChanged(ui->prevProgLabelToggle)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"StudioModeLabels\",\n\t\t\t\tui->prevProgLabelToggle->isChecked());\n\n\t\tmain->ResetUI();\n\t}\n\n\tbool multiviewChanged = false;\n\tif (WidgetChanged(ui->multiviewMouseSwitch)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewMouseSwitch\",\n\t\t\t\tui->multiviewMouseSwitch->isChecked());\n\t\tmultiviewChanged = true;\n\t}\n\n\tif (WidgetChanged(ui->multiviewDrawNames)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewDrawNames\",\n\t\t\t\tui->multiviewDrawNames->isChecked());\n\t\tmultiviewChanged = true;\n\t}\n\n\tif (WidgetChanged(ui->multiviewDrawAreas)) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewDrawAreas\",\n\t\t\t\tui->multiviewDrawAreas->isChecked());\n\t\tmultiviewChanged = true;\n\t}\n\n\tif (WidgetChanged(ui->multiviewLayout)) {\n\t\tconfig_set_int(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewLayout\",\n\t\t\t       ui->multiviewLayout->currentData().toInt());\n\t\tmultiviewChanged = true;\n\t}\n\n\tif (multiviewChanged)\n\t\tOBSProjector::UpdateMultiviewProjectors();\n}\n\nvoid OBSBasicSettings::SaveVideoSettings()\n{\n\tQString baseResolution = ui->baseResolution->currentText();\n\tQString outputResolution = ui->outputResolution->currentText();\n\tint fpsType = ui->fpsType->currentIndex();\n\tuint32_t cx = 0, cy = 0;\n\n\t/* ------------------- */\n\n\tif (WidgetChanged(ui->baseResolution) && ConvertResText(QT_TO_UTF8(baseResolution), cx, cy)) {\n\t\tconfig_set_uint(main->Config(), \"Video\", \"BaseCX\", cx);\n\t\tconfig_set_uint(main->Config(), \"Video\", \"BaseCY\", cy);\n\t}\n\n\tif (WidgetChanged(ui->outputResolution) && ConvertResText(QT_TO_UTF8(outputResolution), cx, cy)) {\n\t\tconfig_set_uint(main->Config(), \"Video\", \"OutputCX\", cx);\n\t\tconfig_set_uint(main->Config(), \"Video\", \"OutputCY\", cy);\n\t}\n\n\tif (WidgetChanged(ui->fpsType))\n\t\tconfig_set_uint(main->Config(), \"Video\", \"FPSType\", fpsType);\n\n\tSaveCombo(ui->fpsCommon, \"Video\", \"FPSCommon\");\n\tSaveSpinBox(ui->fpsInteger, \"Video\", \"FPSInt\");\n\tSaveSpinBox(ui->fpsNumerator, \"Video\", \"FPSNum\");\n\tSaveSpinBox(ui->fpsDenominator, \"Video\", \"FPSDen\");\n\tSaveComboData(ui->downscaleFilter, \"Video\", \"ScaleType\");\n}\n\nvoid OBSBasicSettings::SaveAdvancedSettings()\n{\n\tQString lastMonitoringDevice = config_get_string(main->Config(), \"Audio\", \"MonitoringDeviceId\");\n\n#if defined(_WIN32) || (defined(__APPLE__) && defined(__aarch64__))\n\tif (WidgetChanged(ui->renderer)) {\n\t\tconfig_set_string(App()->GetAppConfig(), \"Video\", \"Renderer\",\n\t\t\t\t  QT_TO_UTF8(ui->renderer->currentData().toString()));\n\t}\n#endif\n\n#ifdef _WIN32\n\tstd::string priority = QT_TO_UTF8(ui->processPriority->currentData().toString());\n\tconfig_set_string(App()->GetAppConfig(), \"General\", \"ProcessPriority\", priority.c_str());\n\tif (main->Active())\n\t\tSetProcessPriority(priority.c_str());\n\n\tSaveCheckBox(ui->enableNewSocketLoop, \"Output\", \"NewSocketLoopEnable\");\n\tSaveCheckBox(ui->enableLowLatencyMode, \"Output\", \"LowLatencyEnable\");\n#endif\n#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)\n\tbool browserHWAccel = ui->browserHWAccel->isChecked();\n\tconfig_set_bool(App()->GetAppConfig(), \"General\", \"BrowserHWAccel\", browserHWAccel);\n#endif\n\n\tif (WidgetChanged(ui->hotkeyFocusType)) {\n\t\tQString str = GetComboData(ui->hotkeyFocusType);\n\t\tconfig_set_string(App()->GetUserConfig(), \"General\", \"HotkeyFocusType\", QT_TO_UTF8(str));\n\t}\n\n#ifdef __APPLE__\n\tif (WidgetChanged(ui->disableOSXVSync)) {\n\t\tbool disable = ui->disableOSXVSync->isChecked();\n\t\tconfig_set_bool(App()->GetAppConfig(), \"Video\", \"DisableOSXVSync\", disable);\n\t\tEnableOSXVSync(!disable);\n\t}\n\tif (WidgetChanged(ui->resetOSXVSync))\n\t\tconfig_set_bool(App()->GetAppConfig(), \"Video\", \"ResetOSXVSyncOnExit\", ui->resetOSXVSync->isChecked());\n#endif\n\n\tSaveComboData(ui->colorFormat, \"Video\", \"ColorFormat\");\n\tSaveComboData(ui->colorSpace, \"Video\", \"ColorSpace\");\n\tSaveComboData(ui->colorRange, \"Video\", \"ColorRange\");\n\tSaveSpinBox(ui->sdrWhiteLevel, \"Video\", \"SdrWhiteLevel\");\n\tSaveSpinBox(ui->hdrNominalPeakLevel, \"Video\", \"HdrNominalPeakLevel\");\n\tif (obs_audio_monitoring_available()) {\n\t\tSaveCombo(ui->monitoringDevice, \"Audio\", \"MonitoringDeviceName\");\n\t\tSaveComboData(ui->monitoringDevice, \"Audio\", \"MonitoringDeviceId\");\n\t}\n\n#ifdef _WIN32\n\tif (WidgetChanged(ui->disableAudioDucking)) {\n\t\tbool disable = ui->disableAudioDucking->isChecked();\n\t\tconfig_set_bool(App()->GetAppConfig(), \"Audio\", \"DisableAudioDucking\", disable);\n\t\tDisableAudioDucking(disable);\n\t}\n#endif\n\n\tif (WidgetChanged(ui->confirmOnExit))\n\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"ConfirmOnExit\", ui->confirmOnExit->isChecked());\n\n\tSaveEdit(ui->filenameFormatting, \"Output\", \"FilenameFormatting\");\n\tSaveEdit(ui->simpleRBPrefix, \"SimpleOutput\", \"RecRBPrefix\");\n\tSaveEdit(ui->simpleRBSuffix, \"SimpleOutput\", \"RecRBSuffix\");\n\tSaveCheckBox(ui->overwriteIfExists, \"Output\", \"OverwriteIfExists\");\n\tSaveCheckBox(ui->streamDelayEnable, \"Output\", \"DelayEnable\");\n\tSaveSpinBox(ui->streamDelaySec, \"Output\", \"DelaySec\");\n\tSaveCheckBox(ui->streamDelayPreserve, \"Output\", \"DelayPreserve\");\n\tSaveCheckBox(ui->reconnectEnable, \"Output\", \"Reconnect\");\n\tSaveSpinBox(ui->reconnectRetryDelay, \"Output\", \"RetryDelay\");\n\tSaveSpinBox(ui->reconnectMaxRetries, \"Output\", \"MaxRetries\");\n\tSaveComboData(ui->bindToIP, \"Output\", \"BindIP\");\n\tSaveComboData(ui->ipFamily, \"Output\", \"IPFamily\");\n\tSaveCheckBox(ui->autoRemux, \"Video\", \"AutoRemux\");\n\tSaveCheckBox(ui->dynBitrate, \"Output\", \"DynamicBitrate\");\n\n\tif (obs_audio_monitoring_available()) {\n\t\tQString newDevice = ui->monitoringDevice->currentData().toString();\n\n\t\tif (lastMonitoringDevice != newDevice) {\n\t\t\tobs_set_audio_monitoring_device(QT_TO_UTF8(ui->monitoringDevice->currentText()),\n\t\t\t\t\t\t\tQT_TO_UTF8(newDevice));\n\n\t\t\tblog(LOG_INFO, \"Audio monitoring device:\\n\\tname: %s\\n\\tid: %s\",\n\t\t\t     QT_TO_UTF8(ui->monitoringDevice->currentText()), QT_TO_UTF8(newDevice));\n\t\t}\n\t}\n}\n\nstatic inline const char *OutputModeFromIdx(int idx)\n{\n\tif (idx == 1)\n\t\treturn \"Advanced\";\n\telse\n\t\treturn \"Simple\";\n}\n\nstatic inline const char *RecTypeFromIdx(int idx)\n{\n\tif (idx == 1)\n\t\treturn \"FFmpeg\";\n\telse\n\t\treturn \"Standard\";\n}\n\nstatic inline const char *SplitFileTypeFromIdx(int idx)\n{\n\tif (idx == 1)\n\t\treturn \"Size\";\n\telse if (idx == 2)\n\t\treturn \"Manual\";\n\telse\n\t\treturn \"Time\";\n}\n\nstatic void WriteJsonData(OBSPropertiesView *view, const char *path)\n{\n\tif (!view || !WidgetChanged(view))\n\t\treturn;\n\n\tconst OBSBasic *basic = OBSBasic::Get();\n\tconst OBSProfile &currentProfile = basic->GetCurrentProfile();\n\n\tconst std::filesystem::path jsonFilePath = currentProfile.path / std::filesystem::u8path(path);\n\n\tif (!jsonFilePath.empty()) {\n\t\tobs_data_t *settings = view->GetSettings();\n\t\tif (settings) {\n\t\t\tobs_data_save_json_safe(settings, jsonFilePath.u8string().c_str(), \"tmp\", \"bak\");\n\t\t}\n\t}\n}\n\nstatic void SaveTrackIndex(config_t *config, const char *section, const char *name, QAbstractButton *check1,\n\t\t\t   QAbstractButton *check2, QAbstractButton *check3, QAbstractButton *check4,\n\t\t\t   QAbstractButton *check5, QAbstractButton *check6)\n{\n\tif (check1->isChecked())\n\t\tconfig_set_int(config, section, name, 1);\n\telse if (check2->isChecked())\n\t\tconfig_set_int(config, section, name, 2);\n\telse if (check3->isChecked())\n\t\tconfig_set_int(config, section, name, 3);\n\telse if (check4->isChecked())\n\t\tconfig_set_int(config, section, name, 4);\n\telse if (check5->isChecked())\n\t\tconfig_set_int(config, section, name, 5);\n\telse if (check6->isChecked())\n\t\tconfig_set_int(config, section, name, 6);\n}\n\nvoid OBSBasicSettings::SaveFormat(QComboBox *combo)\n{\n\tQVariant v = combo->currentData();\n\tif (!v.isNull()) {\n\t\tauto format = v.value<FFmpegFormat>();\n\t\tconfig_set_string(main->Config(), \"AdvOut\", \"FFFormat\", format.name);\n\t\tconfig_set_string(main->Config(), \"AdvOut\", \"FFFormatMimeType\", format.mime_type);\n\n\t\tconst char *ext = format.extensions;\n\t\tstring extStr = ext ? ext : \"\";\n\n\t\tchar *comma = strchr(&extStr[0], ',');\n\t\tif (comma)\n\t\t\t*comma = 0;\n\n\t\tconfig_set_string(main->Config(), \"AdvOut\", \"FFExtension\", extStr.c_str());\n\t} else {\n\t\tconfig_set_string(main->Config(), \"AdvOut\", \"FFFormat\", nullptr);\n\t\tconfig_set_string(main->Config(), \"AdvOut\", \"FFFormatMimeType\", nullptr);\n\n\t\tconfig_remove_value(main->Config(), \"AdvOut\", \"FFExtension\");\n\t}\n}\n\nvoid OBSBasicSettings::SaveEncoder(QComboBox *combo, const char *section, const char *value)\n{\n\tQVariant v = combo->currentData();\n\tFFmpegCodec cd{};\n\tif (!v.isNull())\n\t\tcd = v.value<FFmpegCodec>();\n\n\tconfig_set_int(main->Config(), section, QT_TO_UTF8(QString(\"%1Id\").arg(value)), cd.id);\n\tif (cd.id != 0)\n\t\tconfig_set_string(main->Config(), section, value, cd.name);\n\telse\n\t\tconfig_set_string(main->Config(), section, value, nullptr);\n}\n\nvoid OBSBasicSettings::SaveOutputSettings()\n{\n\tconfig_set_string(main->Config(), \"Output\", \"Mode\", OutputModeFromIdx(ui->outputMode->currentIndex()));\n\n\tQString encoder = ui->simpleOutStrEncoder->currentData().toString();\n\tconst char *presetType;\n\n\tif (encoder == SIMPLE_ENCODER_QSV)\n\t\tpresetType = \"QSVPreset\";\n\telse if (encoder == SIMPLE_ENCODER_QSV_AV1)\n\t\tpresetType = \"QSVPreset\";\n\telse if (encoder == SIMPLE_ENCODER_NVENC)\n\t\tpresetType = \"NVENCPreset2\";\n\telse if (encoder == SIMPLE_ENCODER_NVENC_AV1)\n\t\tpresetType = \"NVENCPreset2\";\n#ifdef ENABLE_HEVC\n\telse if (encoder == SIMPLE_ENCODER_AMD_HEVC)\n\t\tpresetType = \"AMDPreset\";\n\telse if (encoder == SIMPLE_ENCODER_NVENC_HEVC)\n\t\tpresetType = \"NVENCPreset2\";\n#endif\n\telse if (encoder == SIMPLE_ENCODER_AMD)\n\t\tpresetType = \"AMDPreset\";\n\telse if (encoder == SIMPLE_ENCODER_AMD_AV1)\n\t\tpresetType = \"AMDAV1Preset\";\n\telse if (encoder == SIMPLE_ENCODER_APPLE_H264\n#ifdef ENABLE_HEVC\n\t\t || encoder == SIMPLE_ENCODER_APPLE_HEVC\n#endif\n\t)\n\t\t/* The Apple encoders don't have presets like the other encoders\n         do. This only exists to make sure that the x264 preset doesn't\n         get overwritten with empty data. */\n\t\tpresetType = \"ApplePreset\";\n\telse\n\t\tpresetType = \"Preset\";\n\n\tSaveSpinBox(ui->simpleOutputVBitrate, \"SimpleOutput\", \"VBitrate\");\n\tSaveComboData(ui->simpleOutStrEncoder, \"SimpleOutput\", \"StreamEncoder\");\n\tSaveComboData(ui->simpleOutStrAEncoder, \"SimpleOutput\", \"StreamAudioEncoder\");\n\tSaveCombo(ui->simpleOutputABitrate, \"SimpleOutput\", \"ABitrate\");\n\tSaveEdit(ui->simpleOutputPath, \"SimpleOutput\", \"FilePath\");\n\tSaveCheckBox(ui->simpleNoSpace, \"SimpleOutput\", \"FileNameWithoutSpace\");\n\tSaveComboData(ui->simpleOutRecFormat, \"SimpleOutput\", \"RecFormat2\");\n\tSaveCheckBox(ui->simpleOutAdvanced, \"SimpleOutput\", \"UseAdvanced\");\n\tSaveComboData(ui->simpleOutPreset, \"SimpleOutput\", presetType);\n\tSaveEdit(ui->simpleOutCustom, \"SimpleOutput\", \"x264Settings\");\n\tSaveComboData(ui->simpleOutRecQuality, \"SimpleOutput\", \"RecQuality\");\n\tSaveComboData(ui->simpleOutRecEncoder, \"SimpleOutput\", \"RecEncoder\");\n\tSaveComboData(ui->simpleOutRecAEncoder, \"SimpleOutput\", \"RecAudioEncoder\");\n\tSaveEdit(ui->simpleOutMuxCustom, \"SimpleOutput\", \"MuxerCustom\");\n\tSaveGroupBox(ui->simpleReplayBuf, \"SimpleOutput\", \"RecRB\");\n\tSaveSpinBox(ui->simpleRBSecMax, \"SimpleOutput\", \"RecRBTime\");\n\tSaveSpinBox(ui->simpleRBMegsMax, \"SimpleOutput\", \"RecRBSize\");\n\tconfig_set_int(main->Config(), \"SimpleOutput\", \"RecTracks\", SimpleOutGetSelectedAudioTracks());\n\n\tcurAdvStreamEncoder = GetComboData(ui->advOutEncoder);\n\n\tSaveComboData(ui->advOutEncoder, \"AdvOut\", \"Encoder\");\n\tSaveComboData(ui->advOutAEncoder, \"AdvOut\", \"AudioEncoder\");\n\tSaveCombo(ui->advOutRescale, \"AdvOut\", \"RescaleRes\");\n\tSaveComboData(ui->advOutRescaleFilter, \"AdvOut\", \"RescaleFilter\");\n\tSaveTrackIndex(main->Config(), \"AdvOut\", \"TrackIndex\", ui->advOutTrack1, ui->advOutTrack2, ui->advOutTrack3,\n\t\t       ui->advOutTrack4, ui->advOutTrack5, ui->advOutTrack6);\n\tconfig_set_int(main->Config(), \"AdvOut\", \"StreamMultiTrackAudioMixes\", AdvOutGetStreamingSelectedAudioTracks());\n\tconfig_set_string(main->Config(), \"AdvOut\", \"RecType\", RecTypeFromIdx(ui->advOutRecType->currentIndex()));\n\n\tcurAdvRecordEncoder = GetComboData(ui->advOutRecEncoder);\n\n\tSaveEdit(ui->advOutRecPath, \"AdvOut\", \"RecFilePath\");\n\tSaveCheckBox(ui->advOutNoSpace, \"AdvOut\", \"RecFileNameWithoutSpace\");\n\tSaveComboData(ui->advOutRecFormat, \"AdvOut\", \"RecFormat2\");\n\tSaveComboData(ui->advOutRecEncoder, \"AdvOut\", \"RecEncoder\");\n\tSaveComboData(ui->advOutRecAEncoder, \"AdvOut\", \"RecAudioEncoder\");\n\tSaveCombo(ui->advOutRecRescale, \"AdvOut\", \"RecRescaleRes\");\n\tSaveComboData(ui->advOutRecRescaleFilter, \"AdvOut\", \"RecRescaleFilter\");\n\tSaveEdit(ui->advOutMuxCustom, \"AdvOut\", \"RecMuxerCustom\");\n\tSaveCheckBox(ui->advOutSplitFile, \"AdvOut\", \"RecSplitFile\");\n\tconfig_set_string(main->Config(), \"AdvOut\", \"RecSplitFileType\",\n\t\t\t  SplitFileTypeFromIdx(ui->advOutSplitFileType->currentIndex()));\n\tSaveSpinBox(ui->advOutSplitFileTime, \"AdvOut\", \"RecSplitFileTime\");\n\tSaveSpinBox(ui->advOutSplitFileSize, \"AdvOut\", \"RecSplitFileSize\");\n\n\tconfig_set_int(main->Config(), \"AdvOut\", \"RecTracks\", AdvOutGetSelectedAudioTracks());\n\n\tconfig_set_int(main->Config(), \"AdvOut\", \"FLVTrack\", CurrentFLVTrack());\n\n\tconfig_set_bool(main->Config(), \"AdvOut\", \"FFOutputToFile\",\n\t\t\tui->advOutFFType->currentIndex() == 0 ? true : false);\n\tSaveEdit(ui->advOutFFRecPath, \"AdvOut\", \"FFFilePath\");\n\tSaveCheckBox(ui->advOutFFNoSpace, \"AdvOut\", \"FFFileNameWithoutSpace\");\n\tSaveEdit(ui->advOutFFURL, \"AdvOut\", \"FFURL\");\n\tSaveFormat(ui->advOutFFFormat);\n\tSaveEdit(ui->advOutFFMCfg, \"AdvOut\", \"FFMCustom\");\n\tSaveSpinBox(ui->advOutFFVBitrate, \"AdvOut\", \"FFVBitrate\");\n\tSaveSpinBox(ui->advOutFFVGOPSize, \"AdvOut\", \"FFVGOPSize\");\n\tSaveCheckBox(ui->advOutFFUseRescale, \"AdvOut\", \"FFRescale\");\n\tSaveCheckBox(ui->advOutFFIgnoreCompat, \"AdvOut\", \"FFIgnoreCompat\");\n\tSaveCombo(ui->advOutFFRescale, \"AdvOut\", \"FFRescaleRes\");\n\tSaveEncoder(ui->advOutFFVEncoder, \"AdvOut\", \"FFVEncoder\");\n\tSaveEdit(ui->advOutFFVCfg, \"AdvOut\", \"FFVCustom\");\n\tSaveSpinBox(ui->advOutFFABitrate, \"AdvOut\", \"FFABitrate\");\n\tSaveEncoder(ui->advOutFFAEncoder, \"AdvOut\", \"FFAEncoder\");\n\tSaveEdit(ui->advOutFFACfg, \"AdvOut\", \"FFACustom\");\n\tconfig_set_int(main->Config(), \"AdvOut\", \"FFAudioMixes\",\n\t\t       (ui->advOutFFTrack1->isChecked() ? (1 << 0) : 0) |\n\t\t\t       (ui->advOutFFTrack2->isChecked() ? (1 << 1) : 0) |\n\t\t\t       (ui->advOutFFTrack3->isChecked() ? (1 << 2) : 0) |\n\t\t\t       (ui->advOutFFTrack4->isChecked() ? (1 << 3) : 0) |\n\t\t\t       (ui->advOutFFTrack5->isChecked() ? (1 << 4) : 0) |\n\t\t\t       (ui->advOutFFTrack6->isChecked() ? (1 << 5) : 0));\n\tSaveCombo(ui->advOutTrack1Bitrate, \"AdvOut\", \"Track1Bitrate\");\n\tSaveCombo(ui->advOutTrack2Bitrate, \"AdvOut\", \"Track2Bitrate\");\n\tSaveCombo(ui->advOutTrack3Bitrate, \"AdvOut\", \"Track3Bitrate\");\n\tSaveCombo(ui->advOutTrack4Bitrate, \"AdvOut\", \"Track4Bitrate\");\n\tSaveCombo(ui->advOutTrack5Bitrate, \"AdvOut\", \"Track5Bitrate\");\n\tSaveCombo(ui->advOutTrack6Bitrate, \"AdvOut\", \"Track6Bitrate\");\n\tSaveEdit(ui->advOutTrack1Name, \"AdvOut\", \"Track1Name\");\n\tSaveEdit(ui->advOutTrack2Name, \"AdvOut\", \"Track2Name\");\n\tSaveEdit(ui->advOutTrack3Name, \"AdvOut\", \"Track3Name\");\n\tSaveEdit(ui->advOutTrack4Name, \"AdvOut\", \"Track4Name\");\n\tSaveEdit(ui->advOutTrack5Name, \"AdvOut\", \"Track5Name\");\n\tSaveEdit(ui->advOutTrack6Name, \"AdvOut\", \"Track6Name\");\n\n\tif (vodTrackCheckbox) {\n\t\tSaveCheckBox(simpleVodTrack, \"SimpleOutput\", \"VodTrackEnabled\");\n\t\tSaveCheckBox(vodTrackCheckbox, \"AdvOut\", \"VodTrackEnabled\");\n\t\tSaveTrackIndex(main->Config(), \"AdvOut\", \"VodTrackIndex\", vodTrack[0], vodTrack[1], vodTrack[2],\n\t\t\t       vodTrack[3], vodTrack[4], vodTrack[5]);\n\t}\n\n\tSaveCheckBox(ui->advReplayBuf, \"AdvOut\", \"RecRB\");\n\tSaveSpinBox(ui->advRBSecMax, \"AdvOut\", \"RecRBTime\");\n\tSaveSpinBox(ui->advRBMegsMax, \"AdvOut\", \"RecRBSize\");\n\n\tWriteJsonData(streamEncoderProps, \"streamEncoder.json\");\n\tWriteJsonData(recordEncoderProps, \"recordEncoder.json\");\n\tmain->ResetOutputs();\n}\n\nvoid OBSBasicSettings::SaveAudioSettings()\n{\n\tQString sampleRateStr = ui->sampleRate->currentText();\n\tint channelSetupIdx = ui->channelSetup->currentIndex();\n\n\tconst char *channelSetup;\n\tswitch (channelSetupIdx) {\n\tcase 0:\n\t\tchannelSetup = \"Mono\";\n\t\tbreak;\n\tcase 1:\n\t\tchannelSetup = \"Stereo\";\n\t\tbreak;\n\tcase 2:\n\t\tchannelSetup = \"2.1\";\n\t\tbreak;\n\tcase 3:\n\t\tchannelSetup = \"4.0\";\n\t\tbreak;\n\tcase 4:\n\t\tchannelSetup = \"4.1\";\n\t\tbreak;\n\tcase 5:\n\t\tchannelSetup = \"5.1\";\n\t\tbreak;\n\tcase 6:\n\t\tchannelSetup = \"7.1\";\n\t\tbreak;\n\n\tdefault:\n\t\tchannelSetup = \"Stereo\";\n\t\tbreak;\n\t}\n\n\tint sampleRate = 44100;\n\tif (sampleRateStr == \"48 kHz\")\n\t\tsampleRate = 48000;\n\n\tif (WidgetChanged(ui->sampleRate))\n\t\tconfig_set_uint(main->Config(), \"Audio\", \"SampleRate\", sampleRate);\n\n\tif (WidgetChanged(ui->channelSetup))\n\t\tconfig_set_string(main->Config(), \"Audio\", \"ChannelSetup\", channelSetup);\n\n\tif (WidgetChanged(ui->meterDecayRate)) {\n\t\tdouble meterDecayRate;\n\t\tswitch (ui->meterDecayRate->currentIndex()) {\n\t\tcase 0:\n\t\t\tmeterDecayRate = VOLUME_METER_DECAY_FAST;\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tmeterDecayRate = VOLUME_METER_DECAY_MEDIUM;\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tmeterDecayRate = VOLUME_METER_DECAY_SLOW;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmeterDecayRate = VOLUME_METER_DECAY_FAST;\n\t\t\tbreak;\n\t\t}\n\t\tconfig_set_double(main->Config(), \"Audio\", \"MeterDecayRate\", meterDecayRate);\n\n\t\temit main->profileSettingChanged(\"Audio\", \"MeterDecayRate\");\n\t}\n\n\tif (WidgetChanged(ui->peakMeterType)) {\n\t\tuint32_t peakMeterTypeIdx = ui->peakMeterType->currentIndex();\n\t\tconfig_set_uint(main->Config(), \"Audio\", \"PeakMeterType\", peakMeterTypeIdx);\n\n\t\temit main->profileSettingChanged(\"Audio\", \"PeakMeterType\");\n\t}\n\n\tif (WidgetChanged(ui->lowLatencyBuffering)) {\n\t\tbool enableLLAudioBuffering = ui->lowLatencyBuffering->isChecked();\n\t\tconfig_set_bool(App()->GetUserConfig(), \"Audio\", \"LowLatencyAudioBuffering\", enableLLAudioBuffering);\n\t}\n\n\tfor (auto &audioSource : audioSources) {\n\t\tauto source = OBSGetStrongRef(get<0>(audioSource));\n\t\tif (!source)\n\t\t\tcontinue;\n\n\t\tauto &ptmCB = get<1>(audioSource);\n\t\tauto &ptmSB = get<2>(audioSource);\n\t\tauto &pttCB = get<3>(audioSource);\n\t\tauto &pttSB = get<4>(audioSource);\n\n\t\tobs_source_enable_push_to_mute(source, ptmCB->isChecked());\n\t\tobs_source_set_push_to_mute_delay(source, ptmSB->value());\n\n\t\tobs_source_enable_push_to_talk(source, pttCB->isChecked());\n\t\tobs_source_set_push_to_talk_delay(source, pttSB->value());\n\t}\n\n\tauto UpdateAudioDevice = [this](bool input, QComboBox *combo, const char *name, int index) {\n\t\tmain->ResetAudioDevice(input ? App()->InputAudioSource() : App()->OutputAudioSource(),\n\t\t\t\t       QT_TO_UTF8(GetComboData(combo)), Str(name), index);\n\t};\n\n\tUpdateAudioDevice(false, ui->desktopAudioDevice1, \"Basic.DesktopDevice1\", 1);\n\tUpdateAudioDevice(false, ui->desktopAudioDevice2, \"Basic.DesktopDevice2\", 2);\n\tUpdateAudioDevice(true, ui->auxAudioDevice1, \"Basic.AuxDevice1\", 3);\n\tUpdateAudioDevice(true, ui->auxAudioDevice2, \"Basic.AuxDevice2\", 4);\n\tUpdateAudioDevice(true, ui->auxAudioDevice3, \"Basic.AuxDevice3\", 5);\n\tUpdateAudioDevice(true, ui->auxAudioDevice4, \"Basic.AuxDevice4\", 6);\n\tmain->SaveProject();\n}\n\nvoid OBSBasicSettings::SaveHotkeySettings()\n{\n\tconst auto &config = main->Config();\n\n\tusing namespace std;\n\n\tstd::vector<obs_key_combination> combinations;\n\tfor (auto &hotkey : hotkeys) {\n\t\tauto &hw = *hotkey.second;\n\t\tif (!hw.Changed())\n\t\t\tcontinue;\n\n\t\thw.Save(combinations);\n\n\t\tif (!hotkey.first)\n\t\t\tcontinue;\n\n\t\tOBSDataArrayAutoRelease array = obs_hotkey_save(hw.id);\n\t\tOBSDataAutoRelease data = obs_data_create();\n\t\tobs_data_set_array(data, \"bindings\", array);\n\t\tconst char *json = obs_data_get_json(data);\n\t\tconfig_set_string(config, \"Hotkeys\", hw.name.c_str(), json);\n\t}\n\n\tif (!main->outputHandler || !main->outputHandler->replayBuffer)\n\t\treturn;\n\n\tconst char *id = obs_obj_get_id(main->outputHandler->replayBuffer);\n\tif (strcmp(id, \"replay_buffer\") == 0) {\n\t\tOBSDataAutoRelease hotkeys = obs_hotkeys_save_output(main->outputHandler->replayBuffer);\n\t\tconfig_set_string(config, \"Hotkeys\", \"ReplayBuffer\", obs_data_get_json(hotkeys));\n\t}\n}\n\n#define MINOR_SEPARATOR \"------------------------------------------------\"\n\nstatic void AddChangedVal(std::string &changed, const char *str)\n{\n\tif (changed.size())\n\t\tchanged += \", \";\n\tchanged += str;\n}\n\nvoid OBSBasicSettings::SaveSettings()\n{\n\tif (generalChanged)\n\t\tSaveGeneralSettings();\n\tif (stream1Changed)\n\t\tSaveStream1Settings();\n\tif (outputsChanged)\n\t\tSaveOutputSettings();\n\tif (audioChanged)\n\t\tSaveAudioSettings();\n\tif (videoChanged)\n\t\tSaveVideoSettings();\n\tif (hotkeysChanged)\n\t\tSaveHotkeySettings();\n\tif (a11yChanged)\n\t\tSaveA11ySettings();\n\tif (advancedChanged)\n\t\tSaveAdvancedSettings();\n\tif (appearanceChanged)\n\t\tSaveAppearanceSettings();\n\tif (videoChanged || advancedChanged)\n\t\tmain->ResetVideo();\n\n\tconfig_save_safe(main->Config(), \"tmp\", nullptr);\n\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n\tmain->SaveProject();\n\n\tif (Changed()) {\n\t\tstd::string changed;\n\t\tif (generalChanged)\n\t\t\tAddChangedVal(changed, \"general\");\n\t\tif (stream1Changed)\n\t\t\tAddChangedVal(changed, \"stream 1\");\n\t\tif (outputsChanged)\n\t\t\tAddChangedVal(changed, \"outputs\");\n\t\tif (audioChanged)\n\t\t\tAddChangedVal(changed, \"audio\");\n\t\tif (videoChanged)\n\t\t\tAddChangedVal(changed, \"video\");\n\t\tif (hotkeysChanged)\n\t\t\tAddChangedVal(changed, \"hotkeys\");\n\t\tif (a11yChanged)\n\t\t\tAddChangedVal(changed, \"a11y\");\n\t\tif (appearanceChanged)\n\t\t\tAddChangedVal(changed, \"appearance\");\n\t\tif (advancedChanged)\n\t\t\tAddChangedVal(changed, \"advanced\");\n\n\t\tblog(LOG_INFO, \"Settings changed (%s)\", changed.c_str());\n\t\tblog(LOG_INFO, MINOR_SEPARATOR);\n\t}\n\n\tbool langChanged = (ui->language->currentIndex() != prevLangIndex);\n\tbool audioRestart =\n\t\t(ui->channelSetup->currentIndex() != channelIndex || ui->sampleRate->currentIndex() != sampleRateIndex);\n\tbool browserHWAccelChanged = (ui->browserHWAccel && ui->browserHWAccel->isChecked() != prevBrowserAccel);\n\n\tif (langChanged || audioRestart || browserHWAccelChanged)\n\t\trestart = true;\n\telse\n\t\trestart = false;\n}\n\nbool OBSBasicSettings::QueryChanges()\n{\n\tQMessageBox::StandardButton button;\n\n\tbutton = OBSMessageBox::question(this, QTStr(\"Basic.Settings.ConfirmTitle\"), QTStr(\"Basic.Settings.Confirm\"),\n\t\t\t\t\t QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);\n\n\tif (button == QMessageBox::Cancel) {\n\t\treturn false;\n\t} else if (button == QMessageBox::Yes) {\n\t\tif (!QueryAllowedToClose())\n\t\t\treturn false;\n\n\t\tSaveSettings();\n\t} else {\n\t\tif (savedTheme != App()->GetTheme())\n\t\t\tApp()->SetTheme(savedTheme->id);\n\n\t\tLoadSettings(true);\n\t\trestart = false;\n\t}\n\n\tClearChanged();\n\treturn true;\n}\n\nbool OBSBasicSettings::QueryAllowedToClose()\n{\n\tbool simple = (ui->outputMode->currentIndex() == 0);\n\n\tbool invalidEncoder = false;\n\tbool invalidFormat = false;\n\tbool invalidTracks = false;\n\tif (simple) {\n\t\tif (ui->simpleOutRecEncoder->currentIndex() == -1 || ui->simpleOutStrEncoder->currentIndex() == -1 ||\n\t\t    ui->simpleOutRecAEncoder->currentIndex() == -1 || ui->simpleOutStrAEncoder->currentIndex() == -1)\n\t\t\tinvalidEncoder = true;\n\n\t\tif (ui->simpleOutRecFormat->currentIndex() == -1)\n\t\t\tinvalidFormat = true;\n\n\t\tQString qual = ui->simpleOutRecQuality->currentData().toString();\n\t\tQString format = ui->simpleOutRecFormat->currentData().toString();\n\t\tif (SimpleOutGetSelectedAudioTracks() == 0 && qual != \"Stream\" && format != \"flv\")\n\t\t\tinvalidTracks = true;\n\t} else {\n\t\tif (ui->advOutRecEncoder->currentIndex() == -1 || ui->advOutEncoder->currentIndex() == -1 ||\n\t\t    ui->advOutRecAEncoder->currentIndex() == -1 || ui->advOutAEncoder->currentIndex() == -1)\n\t\t\tinvalidEncoder = true;\n\n\t\tQString format = ui->advOutRecFormat->currentData().toString();\n\t\tif (AdvOutGetSelectedAudioTracks() == 0 && format != \"flv\")\n\t\t\tinvalidTracks = true;\n\t\tif (AdvOutGetStreamingSelectedAudioTracks() == 0)\n\t\t\tinvalidTracks = true;\n\t}\n\n\tif (invalidEncoder) {\n\t\tOBSMessageBox::warning(this, QTStr(\"CodecCompat.CodecMissingOnExit.Title\"),\n\t\t\t\t       QTStr(\"CodecCompat.CodecMissingOnExit.Text\"));\n\t\treturn false;\n\t} else if (invalidFormat) {\n\t\tOBSMessageBox::warning(this, QTStr(\"CodecCompat.ContainerMissingOnExit.Title\"),\n\t\t\t\t       QTStr(\"CodecCompat.ContainerMissingOnExit.Text\"));\n\t\treturn false;\n\t} else if (invalidTracks) {\n\t\tOBSMessageBox::warning(this, QTStr(\"OutputWarnings.NoTracksSelectedOnExit.Title\"),\n\t\t\t\t       QTStr(\"OutputWarnings.NoTracksSelectedOnExit.Text\"));\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid OBSBasicSettings::closeEvent(QCloseEvent *event)\n{\n\tif (!AskIfCanCloseSettings())\n\t\tevent->ignore();\n}\n\nvoid OBSBasicSettings::showEvent(QShowEvent *event)\n{\n\tQDialog::showEvent(event);\n\n\t/* Reduce the height of the widget area if too tall compared to the screen\n\t * size (e.g., 720p) with potential window decoration (e.g., titlebar). */\n\tconst int titleBarHeight = QApplication::style()->pixelMetric(QStyle::PM_TitleBarHeight);\n\tconst int maxHeight = round(screen()->availableGeometry().height() - titleBarHeight);\n\tif (size().height() >= maxHeight)\n\t\tresize(size().width(), maxHeight);\n}\n\nvoid OBSBasicSettings::reject()\n{\n\tif (AskIfCanCloseSettings())\n\t\tclose();\n}\n\nvoid OBSBasicSettings::on_listWidget_itemSelectionChanged()\n{\n\tint row = ui->listWidget->currentRow();\n\n\tif (loading || row == pageIndex)\n\t\treturn;\n\n\tif (!hotkeysLoaded && row == Pages::HOTKEYS) {\n\t\tsetCursor(Qt::BusyCursor);\n\t\t/* Look, I know this /feels/ wrong, but the specific issue we're dealing with\n\t\t * here means that the UI locks up immediately even when using \"invokeMethod\".\n\t\t * So the only way for the user to see the loading message on the page is to\n\t\t * give the Qt event loop a tiny bit of time to switch to the hotkey page,\n\t\t * and only then start loading. This could maybe be done by subclassing QWidget\n\t\t * for the hotkey page and then using showEvent() but I *really* don't want\n\t\t * to deal with that right now. I've got better things to do with my life\n\t\t * than to work around this god damn stupid issue for something we'll remove\n\t\t * soon enough anyway. So this solution it is. */\n\t\tQTimer::singleShot(1, this, [&]() { LoadHotkeySettings(); });\n\t}\n\n\tpageIndex = row;\n}\n\nvoid OBSBasicSettings::UpdateYouTubeAppDockSettings()\n{\n#if defined(BROWSER_AVAILABLE) && defined(YOUTUBE_ENABLED)\n\tif (cef_js_avail) {\n\t\tstd::string service = ui->service->currentText().toStdString();\n\t\tif (IsYouTubeService(service)) {\n\t\t\tif (!main->GetYouTubeAppDock()) {\n\t\t\t\tmain->NewYouTubeAppDock();\n\t\t\t}\n\t\t\tmain->GetYouTubeAppDock()->SettingsUpdated(!IsYouTubeService(service) || stream1Changed);\n\t\t} else {\n\t\t\tif (main->GetYouTubeAppDock()) {\n\t\t\t\tmain->GetYouTubeAppDock()->AccountDisconnected();\n\t\t\t}\n\t\t\tmain->DeleteYouTubeAppDock();\n\t\t}\n\t}\n#endif\n}\n\nvoid OBSBasicSettings::on_buttonBox_clicked(QAbstractButton *button)\n{\n\tQDialogButtonBox::ButtonRole val = ui->buttonBox->buttonRole(button);\n\n\tif (val == QDialogButtonBox::ApplyRole || val == QDialogButtonBox::AcceptRole) {\n\t\tif (!QueryAllowedToClose())\n\t\t\treturn;\n\n\t\tSaveSettings();\n\n\t\tUpdateYouTubeAppDockSettings();\n\t\tClearChanged();\n\t}\n\n\tif (val == QDialogButtonBox::AcceptRole || val == QDialogButtonBox::RejectRole) {\n\t\tif (val == QDialogButtonBox::RejectRole) {\n\t\t\tif (savedTheme != App()->GetTheme())\n\t\t\t\tApp()->SetTheme(savedTheme->id);\n\t\t}\n\t\tClearChanged();\n\t\tclose();\n\t}\n}\n\nvoid OBSBasicSettings::on_simpleOutputBrowse_clicked()\n{\n\tQString dir =\n\t\tSelectDirectory(this, QTStr(\"Basic.Settings.Output.SelectDirectory\"), ui->simpleOutputPath->text());\n\tif (dir.isEmpty())\n\t\treturn;\n\n\tui->simpleOutputPath->setText(dir);\n}\n\nvoid OBSBasicSettings::on_advOutRecPathBrowse_clicked()\n{\n\tQString dir = SelectDirectory(this, QTStr(\"Basic.Settings.Output.SelectDirectory\"), ui->advOutRecPath->text());\n\tif (dir.isEmpty())\n\t\treturn;\n\n\tui->advOutRecPath->setText(dir);\n}\n\nvoid OBSBasicSettings::on_advOutFFPathBrowse_clicked()\n{\n\tQString dir = SelectDirectory(this, QTStr(\"Basic.Settings.Output.SelectDirectory\"), ui->advOutRecPath->text());\n\tif (dir.isEmpty())\n\t\treturn;\n\n\tui->advOutFFRecPath->setText(dir);\n}\n\nvoid OBSBasicSettings::on_advOutEncoder_currentIndexChanged()\n{\n\tQString encoder = GetComboData(ui->advOutEncoder);\n\tif (!loading) {\n\t\tbool loadSettings = encoder == curAdvStreamEncoder;\n\n\t\tdelete streamEncoderProps;\n\t\tstreamEncoderProps = CreateEncoderPropertyView(QT_TO_UTF8(encoder),\n\t\t\t\t\t\t\t       loadSettings ? \"streamEncoder.json\" : nullptr, true);\n\t\tstreamEncoderProps->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);\n\t\tui->advOutEncoderLayout->addWidget(streamEncoderProps);\n\t}\n\n\tui->advOutUseRescale->setVisible(true);\n\tui->advOutRescale->setVisible(true);\n}\n\nvoid OBSBasicSettings::on_advOutRecEncoder_currentIndexChanged(int idx)\n{\n\tif (!loading) {\n\t\tdelete recordEncoderProps;\n\t\trecordEncoderProps = nullptr;\n\t}\n\n\tif (idx <= 0) {\n\t\tui->advOutRecUseRescale->setVisible(false);\n\t\tui->advOutRecRescaleContainer->setVisible(false);\n\t\tui->advOutRecEncoderProps->setVisible(false);\n\t\treturn;\n\t}\n\n\tQString encoder = GetComboData(ui->advOutRecEncoder);\n\tbool loadSettings = encoder == curAdvRecordEncoder;\n\n\tif (!loading) {\n\t\trecordEncoderProps = CreateEncoderPropertyView(QT_TO_UTF8(encoder),\n\t\t\t\t\t\t\t       loadSettings ? \"recordEncoder.json\" : nullptr, true);\n\t\trecordEncoderProps->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);\n\t\tui->advOutRecEncoderProps->layout()->addWidget(recordEncoderProps);\n\t\tconnect(recordEncoderProps, &OBSPropertiesView::Changed, this,\n\t\t\t&OBSBasicSettings::AdvReplayBufferChanged);\n\t}\n\n\tui->advOutRecUseRescale->setVisible(true);\n\tui->advOutRecRescaleContainer->setVisible(true);\n\tui->advOutRecEncoderProps->setVisible(true);\n}\n\nvoid OBSBasicSettings::on_advOutFFIgnoreCompat_stateChanged(int)\n{\n\t/* Little hack to reload codecs when checked */\n\ton_advOutFFFormat_currentIndexChanged(ui->advOutFFFormat->currentIndex());\n}\n\n#define DEFAULT_CONTAINER_STR QTStr(\"Basic.Settings.Output.Adv.FFmpeg.FormatDescDef\")\n\nvoid OBSBasicSettings::on_advOutFFFormat_currentIndexChanged(int idx)\n{\n\tconst QVariant itemDataVariant = ui->advOutFFFormat->itemData(idx);\n\n\tif (!itemDataVariant.isNull()) {\n\t\tauto format = itemDataVariant.value<FFmpegFormat>();\n\t\tSetAdvOutputFFmpegEnablement(FFmpegCodecType::AUDIO, format.HasAudio(), false);\n\t\tSetAdvOutputFFmpegEnablement(FFmpegCodecType::VIDEO, format.HasVideo(), false);\n\t\tReloadCodecs(format);\n\n\t\tui->advOutFFFormatDesc->setText(format.long_name);\n\n\t\tFFmpegCodec defaultAudioCodecDesc = format.GetDefaultEncoder(FFmpegCodecType::AUDIO);\n\t\tFFmpegCodec defaultVideoCodecDesc = format.GetDefaultEncoder(FFmpegCodecType::VIDEO);\n\t\tSelectEncoder(ui->advOutFFAEncoder, defaultAudioCodecDesc.name, defaultAudioCodecDesc.id);\n\t\tSelectEncoder(ui->advOutFFVEncoder, defaultVideoCodecDesc.name, defaultVideoCodecDesc.id);\n\t} else {\n\t\tui->advOutFFAEncoder->blockSignals(true);\n\t\tui->advOutFFVEncoder->blockSignals(true);\n\t\tui->advOutFFAEncoder->clear();\n\t\tui->advOutFFVEncoder->clear();\n\n\t\tui->advOutFFFormatDesc->setText(DEFAULT_CONTAINER_STR);\n\t}\n}\n\nvoid OBSBasicSettings::on_advOutFFAEncoder_currentIndexChanged(int idx)\n{\n\tconst QVariant itemDataVariant = ui->advOutFFAEncoder->itemData(idx);\n\tif (!itemDataVariant.isNull()) {\n\t\tauto desc = itemDataVariant.value<FFmpegCodec>();\n\t\tSetAdvOutputFFmpegEnablement(FFmpegCodecType::AUDIO, desc.id != 0 || desc.name != nullptr, true);\n\t}\n}\n\nvoid OBSBasicSettings::on_advOutFFVEncoder_currentIndexChanged(int idx)\n{\n\tconst QVariant itemDataVariant = ui->advOutFFVEncoder->itemData(idx);\n\tif (!itemDataVariant.isNull()) {\n\t\tauto desc = itemDataVariant.value<FFmpegCodec>();\n\t\tSetAdvOutputFFmpegEnablement(FFmpegCodecType::VIDEO, desc.id != 0 || desc.name != nullptr, true);\n\t}\n}\n\nvoid OBSBasicSettings::on_advOutFFType_currentIndexChanged(int idx)\n{\n\tui->advOutFFNoSpace->setHidden(idx != 0);\n}\n\nvoid OBSBasicSettings::on_colorFormat_currentIndexChanged(int)\n{\n\tUpdateColorFormatSpaceWarning();\n}\n\nvoid OBSBasicSettings::on_colorSpace_currentIndexChanged(int)\n{\n\tUpdateColorFormatSpaceWarning();\n}\n\n#define INVALID_RES_STR \"Basic.Settings.Video.InvalidResolution\"\n\nstatic bool ValidResolutions(Ui::OBSBasicSettings *ui)\n{\n\tQString baseRes = ui->baseResolution->lineEdit()->text();\n\tuint32_t cx, cy;\n\n\tif (!ConvertResText(QT_TO_UTF8(baseRes), cx, cy)) {\n\t\tui->videoMsg->setText(QTStr(INVALID_RES_STR));\n\t\treturn false;\n\t}\n\n\tbool lockedOutRes = !ui->outputResolution->isEditable();\n\tif (!lockedOutRes) {\n\t\tQString outRes = ui->outputResolution->lineEdit()->text();\n\t\tif (!ConvertResText(QT_TO_UTF8(outRes), cx, cy)) {\n\t\t\tui->videoMsg->setText(QTStr(INVALID_RES_STR));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tui->videoMsg->setText(\"\");\n\treturn true;\n}\n\nvoid OBSBasicSettings::RecalcOutputResPixels(const char *resText)\n{\n\tuint32_t newCX;\n\tuint32_t newCY;\n\n\tif (ConvertResText(resText, newCX, newCY) && newCX && newCY) {\n\t\toutputCX = newCX;\n\t\toutputCY = newCY;\n\n\t\tstd::tuple<int, int> aspect = aspect_ratio(outputCX, outputCY);\n\n\t\tui->scaledAspect->setText(\n\t\t\tQTStr(\"AspectRatio\")\n\t\t\t\t.arg(QString::number(std::get<0>(aspect)), QString::number(std::get<1>(aspect))));\n\t}\n}\n\nbool OBSBasicSettings::AskIfCanCloseSettings()\n{\n\tbool canCloseSettings = false;\n\n\tif (!Changed() || QueryChanges())\n\t\tcanCloseSettings = true;\n\n\tif (forceAuthReload) {\n\t\tmain->auth->Save();\n\t\tmain->auth->Load();\n\t\tforceAuthReload = false;\n\t}\n\n\tif (forceUpdateCheck) {\n\t\tmain->CheckForUpdates(false);\n\t\tforceUpdateCheck = false;\n\t}\n\n\treturn canCloseSettings;\n}\n\nvoid OBSBasicSettings::on_filenameFormatting_textEdited(const QString &text)\n{\n\tQString safeStr = text;\n\n#ifdef __APPLE__\n\tsafeStr.replace(QRegularExpression(\"[:]\"), \"\");\n#elif defined(_WIN32)\n\tsafeStr.replace(QRegularExpression(\"[<>:\\\"\\\\|\\\\?\\\\*]\"), \"\");\n#else\n\t// TODO: Add filtering for other platforms\n#endif\n\n\tif (text != safeStr)\n\t\tui->filenameFormatting->setText(safeStr);\n}\n\nvoid OBSBasicSettings::on_outputResolution_editTextChanged(const QString &text)\n{\n\tif (!loading) {\n\t\tRecalcOutputResPixels(QT_TO_UTF8(text));\n\t\tLoadDownscaleFilters();\n\t}\n}\n\nvoid OBSBasicSettings::on_baseResolution_editTextChanged(const QString &text)\n{\n\tif (!loading && ValidResolutions(ui.get())) {\n\t\tQString baseResolution = text;\n\t\tuint32_t cx, cy;\n\n\t\tConvertResText(QT_TO_UTF8(baseResolution), cx, cy);\n\n\t\tstd::tuple<int, int> aspect = aspect_ratio(cx, cy);\n\n\t\tui->baseAspect->setText(\n\t\t\tQTStr(\"AspectRatio\")\n\t\t\t\t.arg(QString::number(std::get<0>(aspect)), QString::number(std::get<1>(aspect))));\n\n\t\tResetDownscales(cx, cy);\n\t}\n}\n\nvoid OBSBasicSettings::GeneralChanged()\n{\n\tif (!loading) {\n\t\tgeneralChanged = true;\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::Stream1Changed()\n{\n\tif (!loading) {\n\t\tstream1Changed = true;\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::OutputsChanged()\n{\n\tif (!loading) {\n\t\toutputsChanged = true;\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\n\t\tUpdateMultitrackVideo();\n\t}\n}\n\nvoid OBSBasicSettings::AudioChanged()\n{\n\tif (!loading) {\n\t\taudioChanged = true;\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::AudioChangedRestart()\n{\n\tui->audioMsg->setVisible(false);\n\n\tif (!loading) {\n\t\tint currentChannelIndex = ui->channelSetup->currentIndex();\n\t\tint currentSampleRateIndex = ui->sampleRate->currentIndex();\n\t\tbool currentLLAudioBufVal = ui->lowLatencyBuffering->isChecked();\n\n\t\tif (currentChannelIndex != channelIndex || currentSampleRateIndex != sampleRateIndex ||\n\t\t    currentLLAudioBufVal != llBufferingEnabled) {\n\t\t\tui->audioMsg->setText(QTStr(\"Basic.Settings.ProgramRestart\"));\n\t\t\tui->audioMsg->setVisible(true);\n\t\t} else {\n\t\t\tui->audioMsg->setText(\"\");\n\t\t}\n\n\t\taudioChanged = true;\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::ReloadAudioSources()\n{\n\tLoadAudioSources();\n}\n\n#define MULTI_CHANNEL_WARNING \"Basic.Settings.Audio.MultichannelWarning\"\n\nvoid OBSBasicSettings::SpeakerLayoutChanged(int idx)\n{\n\tQString speakerLayoutQstr = ui->channelSetup->itemText(idx);\n\tstd::string speakerLayout = QT_TO_UTF8(speakerLayoutQstr);\n\tbool surround = IsSurround(speakerLayout.c_str());\n\tbool isOpus = ui->simpleOutStrAEncoder->currentData().toString() == \"opus\";\n\n\tif (surround) {\n\t\t/*\n\t\t * Display all bitrates\n\t\t */\n\t\tPopulateSimpleBitrates(ui->simpleOutputABitrate, isOpus);\n\n\t\tstring stream_encoder_id = ui->advOutAEncoder->currentData().toString().toStdString();\n\t\tstring record_encoder_id = ui->advOutRecAEncoder->currentData().toString().toStdString();\n\t\tPopulateAdvancedBitrates({ui->advOutTrack1Bitrate, ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,\n\t\t\t\t\t  ui->advOutTrack4Bitrate, ui->advOutTrack5Bitrate, ui->advOutTrack6Bitrate},\n\t\t\t\t\t stream_encoder_id.c_str(),\n\t\t\t\t\t record_encoder_id == \"none\" ? stream_encoder_id.c_str()\n\t\t\t\t\t\t\t\t     : record_encoder_id.c_str());\n\t} else {\n\t\t/*\n\t\t * Reset audio bitrate for simple and adv mode, update list of\n\t\t * bitrates and save setting.\n\t\t */\n\t\tRestrictResetBitrates({ui->simpleOutputABitrate, ui->advOutTrack1Bitrate, ui->advOutTrack2Bitrate,\n\t\t\t\t       ui->advOutTrack3Bitrate, ui->advOutTrack4Bitrate, ui->advOutTrack5Bitrate,\n\t\t\t\t       ui->advOutTrack6Bitrate},\n\t\t\t\t      320);\n\n\t\tSaveCombo(ui->simpleOutputABitrate, \"SimpleOutput\", \"ABitrate\");\n\t\tSaveCombo(ui->advOutTrack1Bitrate, \"AdvOut\", \"Track1Bitrate\");\n\t\tSaveCombo(ui->advOutTrack2Bitrate, \"AdvOut\", \"Track2Bitrate\");\n\t\tSaveCombo(ui->advOutTrack3Bitrate, \"AdvOut\", \"Track3Bitrate\");\n\t\tSaveCombo(ui->advOutTrack4Bitrate, \"AdvOut\", \"Track4Bitrate\");\n\t\tSaveCombo(ui->advOutTrack5Bitrate, \"AdvOut\", \"Track5Bitrate\");\n\t\tSaveCombo(ui->advOutTrack6Bitrate, \"AdvOut\", \"Track6Bitrate\");\n\t}\n\n\tUpdateAudioWarnings();\n}\n\n#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)\nvoid OBSBasicSettings::HideOBSWindowWarning(Qt::CheckState state)\n#else\nvoid OBSBasicSettings::HideOBSWindowWarning(int state)\n#endif\n{\n\tif (loading || state == Qt::Unchecked)\n\t\treturn;\n\n\tif (config_get_bool(App()->GetUserConfig(), \"General\", \"WarnedAboutHideOBSFromCapture\"))\n\t\treturn;\n\n\tOBSMessageBox::information(this, QTStr(\"Basic.Settings.General.HideOBSWindowsFromCapture\"),\n\t\t\t\t   QTStr(\"Basic.Settings.General.HideOBSWindowsFromCapture.Message\"));\n\n\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"WarnedAboutHideOBSFromCapture\", true);\n\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n}\n\n/*\n * resets current bitrate if too large and restricts the number of bitrates\n * displayed when multichannel OFF\n */\n\nvoid RestrictResetBitrates(initializer_list<QComboBox *> boxes, int maxbitrate)\n{\n\tfor (auto box : boxes) {\n\t\tint idx = box->currentIndex();\n\t\tint max_bitrate = FindClosestAvailableAudioBitrate(box, maxbitrate);\n\t\tint count = box->count();\n\t\tint max_idx = box->findText(QT_UTF8(std::to_string(max_bitrate).c_str()));\n\n\t\tfor (int i = (count - 1); i > max_idx; i--)\n\t\t\tbox->removeItem(i);\n\n\t\tif (idx > max_idx) {\n\t\t\tint default_bitrate = FindClosestAvailableAudioBitrate(box, maxbitrate / 2);\n\t\t\tint default_idx = box->findText(QT_UTF8(std::to_string(default_bitrate).c_str()));\n\n\t\t\tbox->setCurrentIndex(default_idx);\n\t\t\tbox->setProperty(\"changed\", QVariant(true));\n\t\t} else {\n\t\t\tbox->setCurrentIndex(idx);\n\t\t}\n\t}\n}\n\nvoid OBSBasicSettings::AdvancedChangedRestart()\n{\n\tui->advancedMsg->setVisible(false);\n\n\tif (!loading) {\n\t\tadvancedChanged = true;\n\t\tui->advancedMsg->setText(QTStr(\"Basic.Settings.ProgramRestart\"));\n\t\tui->advancedMsg->setVisible(true);\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::VideoChangedResolution()\n{\n\tif (!loading && ValidResolutions(ui.get())) {\n\t\tvideoChanged = true;\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::VideoChanged()\n{\n\tif (!loading) {\n\t\tvideoChanged = true;\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::HotkeysChanged()\n{\n\tusing namespace std;\n\tif (loading)\n\t\treturn;\n\n\thotkeysChanged = any_of(begin(hotkeys), end(hotkeys), [](const pair<bool, QPointer<OBSHotkeyWidget>> &hotkey) {\n\t\tconst auto &hw = *hotkey.second;\n\t\treturn hw.Changed();\n\t});\n\n\tif (hotkeysChanged)\n\t\tEnableApplyButton(true);\n}\n\nvoid OBSBasicSettings::SearchHotkeys(const QString &text, obs_key_combination_t filterCombo)\n{\n\n\tif (ui->hotkeyFormLayout->rowCount() == 0)\n\t\treturn;\n\n\tstd::vector<obs_key_combination_t> combos;\n\tbool showHotkey;\n\tui->hotkeyScrollArea->ensureVisible(0, 0);\n\n\tQLayoutItem *hotkeysItem = ui->hotkeyFormLayout->itemAt(0);\n\tQWidget *hotkeys = hotkeysItem->widget();\n\tif (!hotkeys)\n\t\treturn;\n\n\tQFormLayout *hotkeysLayout = qobject_cast<QFormLayout *>(hotkeys->layout());\n\thotkeysLayout->setEnabled(false);\n\n\tQString needle = text.toLower();\n\n\tfor (int i = 0; i < hotkeysLayout->rowCount(); i++) {\n\t\tauto label = hotkeysLayout->itemAt(i, QFormLayout::LabelRole);\n\t\tif (!label)\n\t\t\tcontinue;\n\n\t\tOBSHotkeyLabel *item = qobject_cast<OBSHotkeyLabel *>(label->widget());\n\t\tif (!item)\n\t\t\tcontinue;\n\n\t\tQString fullname = item->property(\"fullName\").value<QString>();\n\n\t\tshowHotkey = needle.isEmpty() || fullname.toLower().contains(needle);\n\n\t\tif (showHotkey && !obs_key_combination_is_empty(filterCombo)) {\n\t\t\tshowHotkey = false;\n\n\t\t\titem->widget->GetCombinations(combos);\n\t\t\tfor (auto combo : combos) {\n\t\t\t\tif (combo == filterCombo) {\n\t\t\t\t\tshowHotkey = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlabel->widget()->setVisible(showHotkey);\n\n\t\tauto field = hotkeysLayout->itemAt(i, QFormLayout::FieldRole);\n\t\tif (field)\n\t\t\tfield->widget()->setVisible(showHotkey);\n\t}\n\thotkeysLayout->setEnabled(true);\n}\n\nvoid OBSBasicSettings::on_hotkeyFilterReset_clicked()\n{\n\tui->hotkeyFilterSearch->setText(\"\");\n\tui->hotkeyFilterInput->ResetKey();\n}\n\nvoid OBSBasicSettings::on_hotkeyFilterSearch_textChanged(const QString text)\n{\n\tSearchHotkeys(text, ui->hotkeyFilterInput->key);\n}\n\nvoid OBSBasicSettings::on_hotkeyFilterInput_KeyChanged(obs_key_combination_t combo)\n{\n\tSearchHotkeys(ui->hotkeyFilterSearch->text(), combo);\n}\n\nnamespace std {\ntemplate<> struct hash<obs_key_combination_t> {\n\tsize_t operator()(obs_key_combination_t value) const\n\t{\n\t\tsize_t h1 = hash<uint32_t>{}(value.modifiers);\n\t\tsize_t h2 = hash<int>{}(value.key);\n\t\t// Same as boost::hash_combine()\n\t\th2 ^= h1 + 0x9e3779b9 + (h2 << 6) + (h2 >> 2);\n\t\treturn h2;\n\t}\n};\n} // namespace std\n\nbool OBSBasicSettings::ScanDuplicateHotkeys(QFormLayout *layout)\n{\n\ttypedef struct assignment {\n\t\tOBSHotkeyLabel *label;\n\t\tOBSHotkeyEdit *edit;\n\t} assignment;\n\n\tunordered_map<obs_key_combination_t, vector<assignment>> assignments;\n\tvector<OBSHotkeyLabel *> items;\n\tbool hasDupes = false;\n\n\tfor (int i = 0; i < layout->rowCount(); i++) {\n\t\tauto label = layout->itemAt(i, QFormLayout::LabelRole);\n\t\tif (!label)\n\t\t\tcontinue;\n\t\tOBSHotkeyLabel *item = qobject_cast<OBSHotkeyLabel *>(label->widget());\n\t\tif (!item)\n\t\t\tcontinue;\n\n\t\titems.push_back(item);\n\n\t\tfor (auto &edit : item->widget->edits) {\n\t\t\tedit->hasDuplicate = false;\n\n\t\t\tif (obs_key_combination_is_empty(edit->key))\n\t\t\t\tcontinue;\n\n\t\t\tfor (assignment &assign : assignments[edit->key]) {\n\t\t\t\tif (item->pairPartner == assign.label)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tassign.edit->hasDuplicate = true;\n\t\t\t\tedit->hasDuplicate = true;\n\t\t\t\thasDupes = true;\n\t\t\t}\n\n\t\t\tassignments[edit->key].push_back({item, edit});\n\t\t}\n\t}\n\n\tfor (auto *item : items)\n\t\tfor (auto &edit : item->widget->edits)\n\t\t\tedit->UpdateDuplicationState();\n\n\treturn hasDupes;\n}\n\nvoid OBSBasicSettings::ReloadHotkeys(obs_hotkey_id ignoreKey)\n{\n\tif (!hotkeysLoaded)\n\t\treturn;\n\tLoadHotkeySettings(ignoreKey);\n}\n\nvoid OBSBasicSettings::A11yChanged()\n{\n\tif (!loading) {\n\t\ta11yChanged = true;\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::AppearanceChanged()\n{\n\tif (!loading) {\n\t\tappearanceChanged = true;\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::AdvancedChanged()\n{\n\tif (!loading) {\n\t\tadvancedChanged = true;\n\t\tsender()->setProperty(\"changed\", QVariant(true));\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::AdvOutSplitFileChanged()\n{\n\tbool splitFile = ui->advOutSplitFile->isChecked();\n\tint splitFileType = splitFile ? ui->advOutSplitFileType->currentIndex() : -1;\n\n\tui->advOutSplitFileType->setEnabled(splitFile);\n\tui->advOutSplitFileTimeLabel->setVisible(splitFileType == 0);\n\tui->advOutSplitFileTime->setVisible(splitFileType == 0);\n\tui->advOutSplitFileSizeLabel->setVisible(splitFileType == 1);\n\tui->advOutSplitFileSize->setVisible(splitFileType == 1);\n}\n\nstatic void DisableIncompatibleCodecs(QComboBox *cbox, const QString &format, const QString &formatName,\n\t\t\t\t      const QString &streamEncoder)\n{\n\tQString strEncLabel = QTStr(\"Basic.Settings.Output.Adv.Recording.UseStreamEncoder\");\n\tQString recEncoder = cbox->currentData().toString();\n\n\t/* Check if selected encoders and output format are compatible, disable incompatible items. */\n\tbool currentCompatible = true;\n\tfor (int idx = 0; idx < cbox->count(); idx++) {\n\t\tQString encName = cbox->itemData(idx).toString();\n\t\tstring encoderId = (encName == \"none\") ? streamEncoder.toStdString() : encName.toStdString();\n\t\tQString encDisplayName = (encName == \"none\") ? strEncLabel\n\t\t\t\t\t\t\t     : obs_encoder_get_display_name(encoderId.c_str());\n\n\t\t/* Something has gone horribly wrong and there's no encoder */\n\t\tif (encoderId.empty())\n\t\t\tcontinue;\n\n\t\tif (obs_get_encoder_caps(encoderId.c_str()) & OBS_ENCODER_CAP_DEPRECATED) {\n\t\t\tencDisplayName += \" (\" + QTStr(\"Deprecated\") + \")\";\n\t\t}\n\n\t\tconst char *codec = obs_get_encoder_codec(encoderId.c_str());\n\n\t\tbool is_compatible = ContainerSupportsCodec(format.toStdString(), codec);\n\t\t/* Fall back to FFmpeg check if codec not one of the built-in ones. */\n\t\tif (!is_compatible && !IsBuiltinCodec(codec)) {\n\t\t\tstring ext = GetFormatExt(QT_TO_UTF8(format));\n\t\t\tis_compatible = FFCodecAndFormatCompatible(codec, ext.c_str());\n\t\t}\n\n\t\tQStandardItemModel *model = dynamic_cast<QStandardItemModel *>(cbox->model());\n\t\tQStandardItem *item = model->item(idx);\n\n\t\tif (is_compatible) {\n\t\t\titem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);\n\t\t} else {\n\t\t\tif (recEncoder == encName)\n\t\t\t\tcurrentCompatible = false;\n\n\t\t\titem->setFlags(Qt::NoItemFlags);\n\t\t\tencDisplayName += \" \";\n\t\t\tencDisplayName += QTStr(\"CodecCompat.Incompatible\").arg(formatName);\n\t\t}\n\n\t\titem->setText(encDisplayName);\n\t}\n\n\t// Set to invalid entry if encoder was incompatible\n\tif (!currentCompatible)\n\t\tcbox->setCurrentIndex(-1);\n}\n\nvoid OBSBasicSettings::AdvOutRecCheckCodecs()\n{\n\tQString recFormat = ui->advOutRecFormat->currentData().toString();\n\tQString recFormatName = ui->advOutRecFormat->currentText();\n\n\t/* Set tooltip if available */\n\tQString tooltip = QTStr(QT_TO_UTF8((\"Basic.Settings.Output.Format.TT.\" + recFormat)));\n\n\tif (!tooltip.startsWith(\"Basic.Settings.Output\"))\n\t\tui->advOutRecFormat->setToolTip(tooltip);\n\telse\n\t\tui->advOutRecFormat->setToolTip(nullptr);\n\n\tQString streamEncoder = ui->advOutEncoder->currentData().toString();\n\tQString streamAudioEncoder = ui->advOutAEncoder->currentData().toString();\n\n\tint oldVEncoderIdx = ui->advOutRecEncoder->currentIndex();\n\tint oldAEncoderIdx = ui->advOutRecAEncoder->currentIndex();\n\tDisableIncompatibleCodecs(ui->advOutRecEncoder, recFormat, recFormatName, streamEncoder);\n\tDisableIncompatibleCodecs(ui->advOutRecAEncoder, recFormat, recFormatName, streamAudioEncoder);\n\n\t/* Only invoke AdvOutRecCheckWarnings() if it wouldn't already have\n\t * been triggered by one of the encoder selections being reset. */\n\tif (ui->advOutRecEncoder->currentIndex() == oldVEncoderIdx &&\n\t    ui->advOutRecAEncoder->currentIndex() == oldAEncoderIdx)\n\t\tAdvOutRecCheckWarnings();\n}\n\n#if defined(__APPLE__) && QT_VERSION < QT_VERSION_CHECK(6, 5, 1)\n// Workaround for QTBUG-56064 on macOS\nstatic void ResetInvalidSelection(QComboBox *cbox)\n{\n\tint idx = cbox->currentIndex();\n\tif (idx < 0)\n\t\treturn;\n\n\tQStandardItemModel *model = dynamic_cast<QStandardItemModel *>(cbox->model());\n\tQStandardItem *item = model->item(idx);\n\n\tif (item->isEnabled())\n\t\treturn;\n\n\t// Reset to \"invalid\" state if item was disabled\n\tcbox->blockSignals(true);\n\tcbox->setCurrentIndex(-1);\n\tcbox->blockSignals(false);\n}\n#endif\n\nvoid OBSBasicSettings::AdvOutRecCheckWarnings()\n{\n\tauto Checked = [](QCheckBox *box) {\n\t\treturn box->isChecked() ? 1 : 0;\n\t};\n\n\tQString errorMsg;\n\tQString warningMsg;\n\tuint32_t tracks = Checked(ui->advOutRecTrack1) + Checked(ui->advOutRecTrack2) + Checked(ui->advOutRecTrack3) +\n\t\t\t  Checked(ui->advOutRecTrack4) + Checked(ui->advOutRecTrack5) + Checked(ui->advOutRecTrack6);\n\n\tbool useStreamEncoder = ui->advOutRecEncoder->currentIndex() == 0;\n\tif (useStreamEncoder) {\n\t\tif (!warningMsg.isEmpty())\n\t\t\twarningMsg += \"\\n\\n\";\n\t\twarningMsg += QTStr(\"OutputWarnings.CannotPause\");\n\t}\n\n\tQString recFormat = ui->advOutRecFormat->currentData().toString();\n\n\tif (recFormat == \"flv\") {\n\t\tui->advRecTrackWidget->setCurrentWidget(ui->flvTracks);\n\t} else {\n\t\tui->advRecTrackWidget->setCurrentWidget(ui->recTracks);\n\n\t\tif (tracks == 0)\n\t\t\terrorMsg = QTStr(\"OutputWarnings.NoTracksSelected\");\n\t}\n\n\tif (recFormat == \"mp4\" || recFormat == \"mov\") {\n\t\tif (!warningMsg.isEmpty())\n\t\t\twarningMsg += \"\\n\\n\";\n\n\t\twarningMsg += QTStr(\"OutputWarnings.MP4Recording\");\n\t\tui->autoRemux->setText(QTStr(\"Basic.Settings.Advanced.AutoRemux\").arg(\"mp4\") + \" \" +\n\t\t\t\t       QTStr(\"Basic.Settings.Advanced.AutoRemux.MP4\"));\n\t} else {\n\t\tui->autoRemux->setText(QTStr(\"Basic.Settings.Advanced.AutoRemux\").arg(\"mp4\"));\n\t}\n\n#if defined(__APPLE__) && QT_VERSION < QT_VERSION_CHECK(6, 5, 1)\n\t// Workaround for QTBUG-56064 on macOS\n\tResetInvalidSelection(ui->advOutRecEncoder);\n\tResetInvalidSelection(ui->advOutRecAEncoder);\n#endif\n\n\t// Show warning if codec selection was reset to an invalid state\n\tif (ui->advOutRecEncoder->currentIndex() == -1 || ui->advOutRecAEncoder->currentIndex() == -1) {\n\t\tif (!warningMsg.isEmpty())\n\t\t\twarningMsg += \"\\n\\n\";\n\n\t\twarningMsg += QTStr(\"OutputWarnings.CodecIncompatible\");\n\t}\n\n\tdelete advOutRecWarning;\n\n\tif (!errorMsg.isEmpty() || !warningMsg.isEmpty()) {\n\t\tadvOutRecWarning = new QLabel(errorMsg.isEmpty() ? warningMsg : errorMsg, this);\n\t\tadvOutRecWarning->setProperty(\"class\", errorMsg.isEmpty() ? \"text-warning\" : \"text-danger\");\n\t\tadvOutRecWarning->setWordWrap(true);\n\n\t\tui->advOutRecInfoLayout->addWidget(advOutRecWarning);\n\t}\n}\n\nstatic inline QString MakeMemorySizeString(int bitrate, int seconds)\n{\n\tQString str = QTStr(\"Basic.Settings.Advanced.StreamDelay.MemoryUsage\");\n\tint megabytes = bitrate * seconds / 1000 / 8;\n\n\treturn str.arg(QString::number(megabytes));\n}\n\nvoid OBSBasicSettings::UpdateSimpleOutStreamDelayEstimate()\n{\n\tint seconds = ui->streamDelaySec->value();\n\tint vBitrate = ui->simpleOutputVBitrate->value();\n\tint aBitrate = ui->simpleOutputABitrate->currentText().toInt();\n\n\tQString msg = MakeMemorySizeString(vBitrate + aBitrate, seconds);\n\n\tui->streamDelayInfo->setText(msg);\n}\n\nvoid OBSBasicSettings::UpdateAdvOutStreamDelayEstimate()\n{\n\tif (!streamEncoderProps)\n\t\treturn;\n\n\tOBSData settings = streamEncoderProps->GetSettings();\n\tint trackIndex = config_get_int(main->Config(), \"AdvOut\", \"TrackIndex\");\n\tQString aBitrateText;\n\n\tswitch (trackIndex) {\n\tcase 1:\n\t\taBitrateText = ui->advOutTrack1Bitrate->currentText();\n\t\tbreak;\n\tcase 2:\n\t\taBitrateText = ui->advOutTrack2Bitrate->currentText();\n\t\tbreak;\n\tcase 3:\n\t\taBitrateText = ui->advOutTrack3Bitrate->currentText();\n\t\tbreak;\n\tcase 4:\n\t\taBitrateText = ui->advOutTrack4Bitrate->currentText();\n\t\tbreak;\n\tcase 5:\n\t\taBitrateText = ui->advOutTrack5Bitrate->currentText();\n\t\tbreak;\n\tcase 6:\n\t\taBitrateText = ui->advOutTrack6Bitrate->currentText();\n\t\tbreak;\n\t}\n\n\tint seconds = ui->streamDelaySec->value();\n\tint vBitrate = (int)obs_data_get_int(settings, \"bitrate\");\n\tint aBitrate = aBitrateText.toInt();\n\n\tQString msg = MakeMemorySizeString(vBitrate + aBitrate, seconds);\n\n\tui->streamDelayInfo->setText(msg);\n}\n\nvoid OBSBasicSettings::UpdateStreamDelayEstimate()\n{\n\tif (ui->outputMode->currentIndex() == 0)\n\t\tUpdateSimpleOutStreamDelayEstimate();\n\telse\n\t\tUpdateAdvOutStreamDelayEstimate();\n\n\tUpdateAutomaticReplayBufferCheckboxes();\n}\n\nbool EncoderAvailable(const char *encoder)\n{\n\tconst char *val;\n\tint i = 0;\n\n\twhile (obs_enum_encoder_types(i++, &val))\n\t\tif (strcmp(val, encoder) == 0)\n\t\t\treturn true;\n\n\treturn false;\n}\n\nvoid OBSBasicSettings::FillSimpleRecordingValues()\n{\n#define ADD_QUALITY(str) \\\n\tui->simpleOutRecQuality->addItem(QTStr(\"Basic.Settings.Output.Simple.RecordingQuality.\" str), QString(str));\n#define ENCODER_STR(str) QTStr(\"Basic.Settings.Output.Simple.Encoder.\" str)\n\n\tADD_QUALITY(\"Stream\");\n\tADD_QUALITY(\"Small\");\n\tADD_QUALITY(\"HQ\");\n\tADD_QUALITY(\"Lossless\");\n\n\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Software\"), QString(SIMPLE_ENCODER_X264));\n\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"SoftwareLowCPU\"), QString(SIMPLE_ENCODER_X264_LOWCPU));\n\tif (EncoderAvailable(\"obs_qsv11\"))\n\t\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Hardware.QSV.H264\"), QString(SIMPLE_ENCODER_QSV));\n\tif (EncoderAvailable(\"obs_qsv11_av1\"))\n\t\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Hardware.QSV.AV1\"), QString(SIMPLE_ENCODER_QSV_AV1));\n\tif (EncoderAvailable(\"ffmpeg_nvenc\"))\n\t\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Hardware.NVENC.H264\"), QString(SIMPLE_ENCODER_NVENC));\n\tif (EncoderAvailable(\"obs_nvenc_av1_tex\"))\n\t\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Hardware.NVENC.AV1\"), QString(SIMPLE_ENCODER_NVENC_AV1));\n#ifdef ENABLE_HEVC\n\tif (EncoderAvailable(\"h265_texture_amf\"))\n\t\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Hardware.AMD.HEVC\"), QString(SIMPLE_ENCODER_AMD_HEVC));\n\tif (EncoderAvailable(\"ffmpeg_hevc_nvenc\"))\n\t\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Hardware.NVENC.HEVC\"),\n\t\t\t\t\t\t QString(SIMPLE_ENCODER_NVENC_HEVC));\n#endif\n\tif (EncoderAvailable(\"h264_texture_amf\"))\n\t\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Hardware.AMD.H264\"), QString(SIMPLE_ENCODER_AMD));\n\tif (EncoderAvailable(\"av1_texture_amf\"))\n\t\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Hardware.AMD.AV1\"), QString(SIMPLE_ENCODER_AMD_AV1));\n\tif (EncoderAvailable(\"com.apple.videotoolbox.videoencoder.ave.avc\")\n#ifndef __aarch64__\n\t    && os_get_emulation_status() == true\n#endif\n\t)\n\t\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Hardware.Apple.H264\"),\n\t\t\t\t\t\t QString(SIMPLE_ENCODER_APPLE_H264));\n#ifdef ENABLE_HEVC\n\tif (EncoderAvailable(\"com.apple.videotoolbox.videoencoder.ave.hevc\")\n#ifndef __aarch64__\n\t    && os_get_emulation_status() == true\n#endif\n\t)\n\t\tui->simpleOutRecEncoder->addItem(ENCODER_STR(\"Hardware.Apple.HEVC\"),\n\t\t\t\t\t\t QString(SIMPLE_ENCODER_APPLE_HEVC));\n#endif\n\n\tif (EncoderAvailable(\"CoreAudio_AAC\") || EncoderAvailable(\"libfdk_aac\") || EncoderAvailable(\"ffmpeg_aac\"))\n\t\tui->simpleOutRecAEncoder->addItem(QTStr(\"Basic.Settings.Output.Simple.Codec.AAC.Default\"), \"aac\");\n\tif (EncoderAvailable(\"ffmpeg_opus\"))\n\t\tui->simpleOutRecAEncoder->addItem(QTStr(\"Basic.Settings.Output.Simple.Codec.Opus\"), \"opus\");\n\n#undef ADD_QUALITY\n#undef ENCODER_STR\n}\n\nvoid OBSBasicSettings::FillAudioMonitoringDevices()\n{\n\tQComboBox *cb = ui->monitoringDevice;\n\n\tauto enum_devices = [](void *param, const char *name, const char *id) {\n\t\tQComboBox *cb = (QComboBox *)param;\n\t\tcb->addItem(name, id);\n\t\treturn true;\n\t};\n\n\tcb->addItem(QTStr(\"Basic.Settings.Advanced.Audio.MonitoringDevice\"\n\t\t\t  \".Default\"),\n\t\t    \"default\");\n\n\tobs_enum_audio_monitoring_devices(enum_devices, cb);\n}\n\nvoid OBSBasicSettings::SimpleRecordingQualityChanged()\n{\n\tQString qual = ui->simpleOutRecQuality->currentData().toString();\n\tbool streamQuality = qual == \"Stream\";\n\tbool losslessQuality = !streamQuality && qual == \"Lossless\";\n\n\tbool showEncoder = !streamQuality && !losslessQuality;\n\tui->simpleOutRecEncoder->setVisible(showEncoder);\n\tui->simpleOutRecEncoderLabel->setVisible(showEncoder);\n\tui->simpleOutRecAEncoder->setVisible(showEncoder);\n\tui->simpleOutRecAEncoderLabel->setVisible(showEncoder);\n\tui->simpleOutRecFormat->setVisible(!losslessQuality);\n\tui->simpleOutRecFormatLabel->setVisible(!losslessQuality);\n\n\tUpdateMultitrackVideo();\n\tSimpleRecordingEncoderChanged();\n\tSimpleReplayBufferChanged();\n}\n\nextern const char *get_simple_output_encoder(const char *encoder);\n\nvoid OBSBasicSettings::SimpleStreamingEncoderChanged()\n{\n\tQString encoder = ui->simpleOutStrEncoder->currentData().toString();\n\tQString preset;\n\tconst char *defaultPreset = nullptr;\n\n\tui->simpleOutAdvanced->setVisible(true);\n\tui->simpleOutPresetLabel->setVisible(true);\n\tui->simpleOutPreset->setVisible(true);\n\tui->simpleOutPreset->clear();\n\n\tif (encoder == SIMPLE_ENCODER_QSV || encoder == SIMPLE_ENCODER_QSV_AV1) {\n\t\tui->simpleOutPreset->addItem(\"speed\", \"speed\");\n\t\tui->simpleOutPreset->addItem(\"balanced\", \"balanced\");\n\t\tui->simpleOutPreset->addItem(\"quality\", \"quality\");\n\n\t\tdefaultPreset = \"balanced\";\n\t\tpreset = curQSVPreset;\n\n\t} else if (encoder == SIMPLE_ENCODER_NVENC || encoder == SIMPLE_ENCODER_NVENC_HEVC ||\n\t\t   encoder == SIMPLE_ENCODER_NVENC_AV1) {\n\n\t\tconst char *name = get_simple_output_encoder(QT_TO_UTF8(encoder));\n\t\tconst bool isFFmpegEncoder = strncmp(name, \"ffmpeg_\", 7) == 0;\n\t\tobs_properties_t *props = obs_get_encoder_properties(name);\n\n\t\tobs_property_t *p = obs_properties_get(props, isFFmpegEncoder ? \"preset2\" : \"preset\");\n\t\tsize_t num = obs_property_list_item_count(p);\n\t\tfor (size_t i = 0; i < num; i++) {\n\t\t\tconst char *name = obs_property_list_item_name(p, i);\n\t\t\tconst char *val = obs_property_list_item_string(p, i);\n\n\t\t\tui->simpleOutPreset->addItem(QT_UTF8(name), val);\n\t\t}\n\n\t\tobs_properties_destroy(props);\n\n\t\tdefaultPreset = \"default\";\n\t\tpreset = curNVENCPreset;\n\n\t} else if (encoder == SIMPLE_ENCODER_AMD || encoder == SIMPLE_ENCODER_AMD_HEVC) {\n\t\tui->simpleOutPreset->addItem(\"Speed\", \"speed\");\n\t\tui->simpleOutPreset->addItem(\"Balanced\", \"balanced\");\n\t\tui->simpleOutPreset->addItem(\"Quality\", \"quality\");\n\n\t\tdefaultPreset = \"balanced\";\n\t\tpreset = curAMDPreset;\n\t} else if (encoder == SIMPLE_ENCODER_APPLE_H264\n#ifdef ENABLE_HEVC\n\t\t   || encoder == SIMPLE_ENCODER_APPLE_HEVC\n#endif\n\t) {\n\t\tui->simpleOutAdvanced->setChecked(false);\n\t\tui->simpleOutAdvanced->setVisible(false);\n\t\tui->simpleOutPreset->setVisible(false);\n\t\tui->simpleOutPresetLabel->setVisible(false);\n\n\t} else if (encoder == SIMPLE_ENCODER_AMD_AV1) {\n\t\tui->simpleOutPreset->addItem(\"Speed\", \"speed\");\n\t\tui->simpleOutPreset->addItem(\"Balanced\", \"balanced\");\n\t\tui->simpleOutPreset->addItem(\"Quality\", \"quality\");\n\t\tui->simpleOutPreset->addItem(\"High Quality\", \"highQuality\");\n\n\t\tdefaultPreset = \"balanced\";\n\t\tpreset = curAMDAV1Preset;\n\t} else {\n\n#define PRESET_STR(val) QString(Str(\"Basic.Settings.Output.EncoderPreset.\" val)).arg(val)\n\t\tui->simpleOutPreset->addItem(PRESET_STR(\"ultrafast\"), \"ultrafast\");\n\t\tui->simpleOutPreset->addItem(\"superfast\", \"superfast\");\n\t\tui->simpleOutPreset->addItem(PRESET_STR(\"veryfast\"), \"veryfast\");\n\t\tui->simpleOutPreset->addItem(\"faster\", \"faster\");\n\t\tui->simpleOutPreset->addItem(PRESET_STR(\"fast\"), \"fast\");\n#undef PRESET_STR\n\n\t\t/* Users might have previously selected a preset which is no\n\t\t * longer available in simple mode. Make sure we don't mess\n\t\t * with their setups without them knowing. */\n\t\tif (ui->simpleOutPreset->findData(curPreset) == -1) {\n\t\t\tui->simpleOutPreset->addItem(curPreset, curPreset);\n\t\t\tQStandardItemModel *model = qobject_cast<QStandardItemModel *>(ui->simpleOutPreset->model());\n\t\t\tQStandardItem *item = model->item(model->rowCount() - 1);\n\t\t\titem->setEnabled(false);\n\t\t}\n\n\t\tdefaultPreset = \"veryfast\";\n\t\tpreset = curPreset;\n\t}\n\n\tint idx = ui->simpleOutPreset->findData(QVariant(preset));\n\tif (idx == -1)\n\t\tidx = ui->simpleOutPreset->findData(QVariant(defaultPreset));\n\n\tui->simpleOutPreset->setCurrentIndex(idx);\n}\n\n#define ESTIMATE_STR \"Basic.Settings.Output.ReplayBuffer.Estimate\"\n#define ESTIMATE_TOO_LARGE_STR \"Basic.Settings.Output.ReplayBuffer.EstimateTooLarge\"\n#define ESTIMATE_UNKNOWN_STR \"Basic.Settings.Output.ReplayBuffer.EstimateUnknown\"\n\nvoid OBSBasicSettings::UpdateAutomaticReplayBufferCheckboxes()\n{\n\tbool state = false;\n\tswitch (ui->outputMode->currentIndex()) {\n\tcase 0: {\n\t\tconst bool lossless = ui->simpleOutRecQuality->currentData().toString() == \"Lossless\";\n\t\tstate = ui->simpleReplayBuf->isChecked();\n\t\tui->simpleReplayBuf->setEnabled(!obs_frontend_replay_buffer_active() && !lossless);\n\t\tbreak;\n\t}\n\tcase 1: {\n\t\tstate = ui->advReplayBuf->isChecked();\n\t\tbool customFFmpeg = ui->advOutRecType->currentIndex() == 1;\n\t\tui->advReplayBuf->setEnabled(!obs_frontend_replay_buffer_active() && !customFFmpeg);\n\t\tui->advReplayBufCustomFFmpeg->setVisible(customFFmpeg);\n\t\tbreak;\n\t}\n\t}\n\tui->replayWhileStreaming->setEnabled(state);\n\tui->keepReplayStreamStops->setEnabled(state && ui->replayWhileStreaming->isChecked());\n}\n\nvoid OBSBasicSettings::SimpleReplayBufferChanged()\n{\n\tQString qual = ui->simpleOutRecQuality->currentData().toString();\n\tbool streamQuality = qual == \"Stream\";\n\tint abitrate = 0;\n\n\tui->simpleRBMegsMax->setVisible(!streamQuality);\n\tui->simpleRBMegsMaxLabel->setVisible(!streamQuality);\n\n\tif (ui->simpleOutRecFormat->currentText().compare(\"flv\") == 0 || streamQuality) {\n\t\tabitrate = ui->simpleOutputABitrate->currentText().toInt();\n\t} else {\n\t\tint delta = ui->simpleOutputABitrate->currentText().toInt();\n\t\tif (ui->simpleOutRecTrack1->isChecked())\n\t\t\tabitrate += delta;\n\t\tif (ui->simpleOutRecTrack2->isChecked())\n\t\t\tabitrate += delta;\n\t\tif (ui->simpleOutRecTrack3->isChecked())\n\t\t\tabitrate += delta;\n\t\tif (ui->simpleOutRecTrack4->isChecked())\n\t\t\tabitrate += delta;\n\t\tif (ui->simpleOutRecTrack5->isChecked())\n\t\t\tabitrate += delta;\n\t\tif (ui->simpleOutRecTrack6->isChecked())\n\t\t\tabitrate += delta;\n\t}\n\n\tint vbitrate = ui->simpleOutputVBitrate->value();\n\tint seconds = ui->simpleRBSecMax->value();\n\n\t// Set maximum to 75% of installed memory\n\tuint64_t memTotal = os_get_sys_total_size();\n\tint64_t memMaxMB = memTotal ? memTotal * 3 / 4 / 1024 / 1024 : 8192;\n\n\tint64_t memMB = int64_t(seconds) * int64_t(vbitrate + abitrate) * 1000 / 8 / 1024 / 1024;\n\tif (memMB < 1)\n\t\tmemMB = 1;\n\n\tui->simpleRBEstimate->setObjectName(\"\");\n\tif (streamQuality) {\n\t\tif (memMB <= memMaxMB) {\n\t\t\tui->simpleRBEstimate->setText(QTStr(ESTIMATE_STR).arg(QString::number(int(memMB))));\n\t\t} else {\n\t\t\tui->simpleRBEstimate->setText(\n\t\t\t\tQTStr(ESTIMATE_TOO_LARGE_STR)\n\t\t\t\t\t.arg(QString::number(int(memMB)), QString::number(int(memMaxMB))));\n\t\t\tui->simpleRBEstimate->setProperty(\"class\", \"text-warning\");\n\t\t}\n\t} else {\n\t\tui->simpleRBEstimate->setText(QTStr(ESTIMATE_UNKNOWN_STR));\n\t\tui->simpleRBMegsMax->setMaximum(memMaxMB);\n\t}\n\n\tui->simpleRBEstimate->style()->polish(ui->simpleRBEstimate);\n\n\tUpdateAutomaticReplayBufferCheckboxes();\n}\n\n#define TEXT_USE_STREAM_ENC QTStr(\"Basic.Settings.Output.Adv.Recording.UseStreamEncoder\")\n\nvoid OBSBasicSettings::AdvReplayBufferChanged()\n{\n\tobs_data_t *settings;\n\tQString encoder = ui->advOutRecEncoder->currentText();\n\tbool useStream = QString::compare(encoder, TEXT_USE_STREAM_ENC) == 0;\n\n\tif (useStream && streamEncoderProps) {\n\t\tsettings = streamEncoderProps->GetSettings();\n\t} else if (!useStream && recordEncoderProps) {\n\t\tsettings = recordEncoderProps->GetSettings();\n\t} else {\n\t\tif (useStream)\n\t\t\tencoder = GetComboData(ui->advOutEncoder);\n\t\tsettings = obs_encoder_defaults(encoder.toUtf8().constData());\n\n\t\tif (!settings)\n\t\t\treturn;\n\n\t\tconst OBSProfile &currentProfile = main->GetCurrentProfile();\n\n\t\tconst std::filesystem::path jsonFilePath =\n\t\t\tcurrentProfile.path / std::filesystem::u8path(\"recordEncoder.json\");\n\n\t\tif (!jsonFilePath.empty()) {\n\t\t\tOBSDataAutoRelease data =\n\t\t\t\tobs_data_create_from_json_file_safe(jsonFilePath.u8string().c_str(), \"bak\");\n\t\t\tobs_data_apply(settings, data);\n\t\t}\n\t}\n\n\tint vbitrate = (int)obs_data_get_int(settings, \"bitrate\");\n\tconst char *rateControl = obs_data_get_string(settings, \"rate_control\");\n\n\tif (!rateControl)\n\t\trateControl = \"\";\n\n\tbool lossless = strcmp(rateControl, \"lossless\") == 0 || ui->advOutRecType->currentIndex() == 1;\n\tbool replayBufferEnabled = ui->advReplayBuf->isChecked();\n\n\tint abitrate = 0;\n\tif (ui->advOutRecTrack1->isChecked())\n\t\tabitrate += ui->advOutTrack1Bitrate->currentText().toInt();\n\tif (ui->advOutRecTrack2->isChecked())\n\t\tabitrate += ui->advOutTrack2Bitrate->currentText().toInt();\n\tif (ui->advOutRecTrack3->isChecked())\n\t\tabitrate += ui->advOutTrack3Bitrate->currentText().toInt();\n\tif (ui->advOutRecTrack4->isChecked())\n\t\tabitrate += ui->advOutTrack4Bitrate->currentText().toInt();\n\tif (ui->advOutRecTrack5->isChecked())\n\t\tabitrate += ui->advOutTrack5Bitrate->currentText().toInt();\n\tif (ui->advOutRecTrack6->isChecked())\n\t\tabitrate += ui->advOutTrack6Bitrate->currentText().toInt();\n\n\tint seconds = ui->advRBSecMax->value();\n\n\t// Set maximum to 75% of installed memory\n\tuint64_t memTotal = os_get_sys_total_size();\n\tint64_t memMaxMB = memTotal ? memTotal * 3 / 4 / 1024 / 1024 : 8192;\n\n\tint64_t memMB = int64_t(seconds) * int64_t(vbitrate + abitrate) * 1000 / 8 / 1024 / 1024;\n\tif (memMB < 1)\n\t\tmemMB = 1;\n\n\tbool varRateControl = (astrcmpi(rateControl, \"CBR\") == 0 || astrcmpi(rateControl, \"VBR\") == 0 ||\n\t\t\t       astrcmpi(rateControl, \"ABR\") == 0);\n\tif (vbitrate == 0)\n\t\tvarRateControl = false;\n\n\tui->advRBEstimate->setObjectName(\"\");\n\tif (varRateControl) {\n\t\tui->advRBMegsMax->setVisible(false);\n\t\tui->advRBMegsMaxLabel->setVisible(false);\n\n\t\tif (memMB <= memMaxMB) {\n\t\t\tui->advRBEstimate->setText(QTStr(ESTIMATE_STR).arg(QString::number(int(memMB))));\n\t\t} else {\n\t\t\tui->advRBEstimate->setText(\n\t\t\t\tQTStr(ESTIMATE_TOO_LARGE_STR)\n\t\t\t\t\t.arg(QString::number(int(memMB)), QString::number(int(memMaxMB))));\n\t\t\tui->advRBEstimate->setProperty(\"class\", \"text-warning\");\n\t\t}\n\t} else {\n\t\tui->advRBMegsMax->setVisible(true);\n\t\tui->advRBMegsMaxLabel->setVisible(true);\n\t\tui->advRBMegsMax->setMaximum(memMaxMB);\n\t\tui->advRBEstimate->setText(QTStr(ESTIMATE_UNKNOWN_STR));\n\t}\n\n\tui->advReplayBufferFrame->setEnabled(!lossless && replayBufferEnabled);\n\tui->advRBEstimate->style()->polish(ui->advRBEstimate);\n\tui->advReplayBuf->setEnabled(!lossless);\n\n\tUpdateAutomaticReplayBufferCheckboxes();\n}\n\n#define SIMPLE_OUTPUT_WARNING(str) QTStr(\"Basic.Settings.Output.Simple.Warn.\" str)\n\nstatic void DisableIncompatibleSimpleCodecs(QComboBox *cbox, const QString &format)\n{\n\t/* Unlike in advanced mode the available simple mode encoders are\n\t * hardcoded, so this check is also a simpler, hardcoded one. */\n\tQString encoder = cbox->currentData().toString();\n\n\tbool currentCompatible = true;\n\tfor (int idx = 0; idx < cbox->count(); idx++) {\n\t\tQString encName = cbox->itemData(idx).toString();\n\t\tQString codec;\n\n\t\t/* Simple mode does not expose audio encoder variants directly,\n\t\t * so we have to simply set the codec to the internal name. */\n\t\tif (encName == \"opus\" || encName == \"aac\") {\n\t\t\tcodec = encName;\n\t\t} else {\n\t\t\tconst char *encoder_id = get_simple_output_encoder(QT_TO_UTF8(encName));\n\t\t\tcodec = obs_get_encoder_codec(encoder_id);\n\t\t}\n\n\t\tQStandardItemModel *model = dynamic_cast<QStandardItemModel *>(cbox->model());\n\t\tQStandardItem *item = model->item(idx);\n\n\t\tif (ContainerSupportsCodec(format.toStdString(), codec.toStdString())) {\n\t\t\titem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);\n\t\t} else {\n\t\t\tif (encoder == encName)\n\t\t\t\tcurrentCompatible = false;\n\n\t\t\titem->setFlags(Qt::NoItemFlags);\n\t\t}\n\t}\n\n\tif (!currentCompatible)\n\t\tcbox->setCurrentIndex(-1);\n}\n\nstatic void DisableIncompatibleSimpleContainer(QComboBox *cbox, const QString &currentFormat, const QString &vEncoder,\n\t\t\t\t\t       const QString &aEncoder)\n{\n\t/* Similar to above, but works in reverse to disable incompatible formats\n\t * based on the encoder selection. */\n\tstring vCodec = obs_get_encoder_codec(get_simple_output_encoder(QT_TO_UTF8(vEncoder)));\n\tstring aCodec = aEncoder.toStdString();\n\n\tbool currentCompatible = true;\n\tfor (int idx = 0; idx < cbox->count(); idx++) {\n\t\tQString format = cbox->itemData(idx).toString();\n\t\tstring formatStr = format.toStdString();\n\n\t\tQStandardItemModel *model = dynamic_cast<QStandardItemModel *>(cbox->model());\n\t\tQStandardItem *item = model->item(idx);\n\n\t\tif (ContainerSupportsCodec(formatStr, vCodec) && ContainerSupportsCodec(formatStr, aCodec)) {\n\t\t\titem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);\n\t\t} else {\n\t\t\tif (format == currentFormat)\n\t\t\t\tcurrentCompatible = false;\n\n\t\t\titem->setFlags(Qt::NoItemFlags);\n\t\t}\n\t}\n\n\tif (!currentCompatible)\n\t\tcbox->setCurrentIndex(-1);\n}\n\nvoid OBSBasicSettings::SimpleRecordingEncoderChanged()\n{\n\tQString qual = ui->simpleOutRecQuality->currentData().toString();\n\tQString warning;\n\tbool enforceBitrate = !ui->ignoreRecommended->isChecked();\n\tOBSService service = GetStream1Service();\n\n\tdelete simpleOutRecWarning;\n\n\tif (enforceBitrate && service) {\n\t\tOBSDataAutoRelease videoSettings = obs_data_create();\n\t\tOBSDataAutoRelease audioSettings = obs_data_create();\n\t\tint oldVBitrate = ui->simpleOutputVBitrate->value();\n\t\tint oldABitrate = ui->simpleOutputABitrate->currentText().toInt();\n\t\tobs_data_set_int(videoSettings, \"bitrate\", oldVBitrate);\n\t\tobs_data_set_int(audioSettings, \"bitrate\", oldABitrate);\n\n\t\tobs_service_apply_encoder_settings(service, videoSettings, audioSettings);\n\n\t\tint newVBitrate = obs_data_get_int(videoSettings, \"bitrate\");\n\t\tint newABitrate = obs_data_get_int(audioSettings, \"bitrate\");\n\n\t\tif (newVBitrate < oldVBitrate)\n\t\t\twarning = SIMPLE_OUTPUT_WARNING(\"VideoBitrate\").arg(newVBitrate);\n\t\tif (newABitrate < oldABitrate) {\n\t\t\tif (!warning.isEmpty())\n\t\t\t\twarning += \"\\n\\n\";\n\t\t\twarning += SIMPLE_OUTPUT_WARNING(\"AudioBitrate\").arg(newABitrate);\n\t\t}\n\t}\n\n\tQString format = ui->simpleOutRecFormat->currentData().toString();\n\t/* Set tooltip if available */\n\tQString tooltip = QTStr(QT_TO_UTF8((\"Basic.Settings.Output.Format.TT.\" + format)));\n\n\tif (!tooltip.startsWith(\"Basic.Settings.Output\"))\n\t\tui->simpleOutRecFormat->setToolTip(tooltip);\n\telse\n\t\tui->simpleOutRecFormat->setToolTip(nullptr);\n\n\tif (qual == \"Lossless\") {\n\t\tif (!warning.isEmpty())\n\t\t\twarning += \"\\n\\n\";\n\t\twarning += SIMPLE_OUTPUT_WARNING(\"Lossless\");\n\t\twarning += \"\\n\\n\";\n\t\twarning += SIMPLE_OUTPUT_WARNING(\"Encoder\");\n\n\t} else if (qual != \"Stream\") {\n\t\tQString enc = ui->simpleOutRecEncoder->currentData().toString();\n\t\tQString streamEnc = ui->simpleOutStrEncoder->currentData().toString();\n\t\tbool x264RecEnc = (enc == SIMPLE_ENCODER_X264 || enc == SIMPLE_ENCODER_X264_LOWCPU);\n\n\t\tif (streamEnc == SIMPLE_ENCODER_X264 && x264RecEnc) {\n\t\t\tif (!warning.isEmpty())\n\t\t\t\twarning += \"\\n\\n\";\n\t\t\twarning += SIMPLE_OUTPUT_WARNING(\"Encoder\");\n\t\t}\n\n\t\t/* Prevent function being called recursively if changes happen. */\n\t\tui->simpleOutRecEncoder->blockSignals(true);\n\t\tui->simpleOutRecAEncoder->blockSignals(true);\n\t\tDisableIncompatibleSimpleCodecs(ui->simpleOutRecEncoder, format);\n\t\tDisableIncompatibleSimpleCodecs(ui->simpleOutRecAEncoder, format);\n\t\tui->simpleOutRecAEncoder->blockSignals(false);\n\t\tui->simpleOutRecEncoder->blockSignals(false);\n\n\t\tif (ui->simpleOutRecEncoder->currentIndex() == -1 || ui->simpleOutRecAEncoder->currentIndex() == -1) {\n\t\t\tif (!warning.isEmpty())\n\t\t\t\twarning += \"\\n\\n\";\n\t\t\twarning += QTStr(\"OutputWarnings.CodecIncompatible\");\n\t\t}\n\t} else {\n\t\t/* When using stream encoders do the reverse; Disable containers that are incompatible. */\n\t\tQString streamEnc = ui->simpleOutStrEncoder->currentData().toString();\n\t\tQString streamAEnc = ui->simpleOutStrAEncoder->currentData().toString();\n\n\t\tui->simpleOutRecFormat->blockSignals(true);\n\t\tDisableIncompatibleSimpleContainer(ui->simpleOutRecFormat, format, streamEnc, streamAEnc);\n\t\tui->simpleOutRecFormat->blockSignals(false);\n\n\t\tif (ui->simpleOutRecFormat->currentIndex() == -1) {\n\t\t\tif (!warning.isEmpty())\n\t\t\t\twarning += \"\\n\\n\";\n\t\t\twarning += SIMPLE_OUTPUT_WARNING(\"IncompatibleContainer\");\n\t\t}\n\n\t\tif (!warning.isEmpty())\n\t\t\twarning += \"\\n\\n\";\n\t\twarning += SIMPLE_OUTPUT_WARNING(\"CannotPause\");\n\t}\n\n\tif (qual != \"Lossless\" && (format == \"mp4\" || format == \"mov\")) {\n\t\tif (!warning.isEmpty())\n\t\t\twarning += \"\\n\\n\";\n\t\twarning += QTStr(\"OutputWarnings.MP4Recording\");\n\t\tui->autoRemux->setText(QTStr(\"Basic.Settings.Advanced.AutoRemux\").arg(\"mp4\") + \" \" +\n\t\t\t\t       QTStr(\"Basic.Settings.Advanced.AutoRemux.MP4\"));\n\t} else {\n\t\tui->autoRemux->setText(QTStr(\"Basic.Settings.Advanced.AutoRemux\").arg(\"mp4\"));\n\t}\n\n\tif (qual == \"Stream\") {\n\t\tui->simpleRecTrackWidget->setCurrentWidget(ui->simpleFlvTracks);\n\t} else if (qual == \"Lossless\") {\n\t\tui->simpleRecTrackWidget->setCurrentWidget(ui->simpleRecTracks);\n\t} else {\n\t\tif (format == \"flv\") {\n\t\t\tui->simpleRecTrackWidget->setCurrentWidget(ui->simpleFlvTracks);\n\t\t} else {\n\t\t\tui->simpleRecTrackWidget->setCurrentWidget(ui->simpleRecTracks);\n\t\t}\n\t}\n\n\tif (warning.isEmpty())\n\t\treturn;\n\n\tsimpleOutRecWarning = new QLabel(warning, this);\n\tsimpleOutRecWarning->setProperty(\"class\", \"text-warning\");\n\tsimpleOutRecWarning->setWordWrap(true);\n\tui->simpleOutInfoLayout->addWidget(simpleOutRecWarning);\n}\n\nvoid OBSBasicSettings::SurroundWarning(int idx)\n{\n\tif (idx == lastChannelSetupIdx || idx == -1)\n\t\treturn;\n\n\tif (loading) {\n\t\tlastChannelSetupIdx = idx;\n\t\treturn;\n\t}\n\n\tQString speakerLayoutQstr = ui->channelSetup->itemText(idx);\n\tbool surround = IsSurround(QT_TO_UTF8(speakerLayoutQstr));\n\n\tQString lastQstr = ui->channelSetup->itemText(lastChannelSetupIdx);\n\tbool wasSurround = IsSurround(QT_TO_UTF8(lastQstr));\n\n\tif (surround && !wasSurround) {\n\t\tQMessageBox::StandardButton button;\n\n\t\tQString warningString = QTStr(\"Basic.Settings.ProgramRestart\") + QStringLiteral(\"\\n\\n\") +\n\t\t\t\t\tQTStr(MULTI_CHANNEL_WARNING) + QStringLiteral(\"\\n\\n\") +\n\t\t\t\t\tQTStr(MULTI_CHANNEL_WARNING \".Confirm\");\n\n\t\tbutton = OBSMessageBox::question(this, QTStr(MULTI_CHANNEL_WARNING \".Title\"), warningString);\n\n\t\tif (button == QMessageBox::No) {\n\t\t\tQMetaObject::invokeMethod(ui->channelSetup, \"setCurrentIndex\", Qt::QueuedConnection,\n\t\t\t\t\t\t  Q_ARG(int, lastChannelSetupIdx));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tlastChannelSetupIdx = idx;\n}\n\n#define LL_BUFFERING_WARNING \"Basic.Settings.Audio.LowLatencyBufferingWarning\"\n\nvoid OBSBasicSettings::UpdateAudioWarnings()\n{\n\tQString speakerLayoutQstr = ui->channelSetup->currentText();\n\tbool surround = IsSurround(QT_TO_UTF8(speakerLayoutQstr));\n\tbool lowBufferingActive = ui->lowLatencyBuffering->isChecked();\n\n\tQString text;\n\n\tif (surround) {\n\t\ttext = QTStr(MULTI_CHANNEL_WARNING \".Enabled\") + QStringLiteral(\"\\n\\n\") + QTStr(MULTI_CHANNEL_WARNING);\n\t}\n\n\tif (lowBufferingActive) {\n\t\tif (!text.isEmpty())\n\t\t\ttext += QStringLiteral(\"\\n\\n\");\n\n\t\ttext += QTStr(LL_BUFFERING_WARNING \".Enabled\") + QStringLiteral(\"\\n\\n\") + QTStr(LL_BUFFERING_WARNING);\n\t}\n\n\tui->audioMsg_2->setText(text);\n\tui->audioMsg_2->setVisible(!text.isEmpty());\n}\n\nvoid OBSBasicSettings::LowLatencyBufferingChanged(bool checked)\n{\n\tif (checked) {\n\t\tQString warningStr =\n\t\t\tQTStr(LL_BUFFERING_WARNING) + QStringLiteral(\"\\n\\n\") + QTStr(LL_BUFFERING_WARNING \".Confirm\");\n\n\t\tauto button = OBSMessageBox::question(this, QTStr(LL_BUFFERING_WARNING \".Title\"), warningStr);\n\n\t\tif (button == QMessageBox::No) {\n\t\t\tQMetaObject::invokeMethod(ui->lowLatencyBuffering, \"setChecked\", Qt::QueuedConnection,\n\t\t\t\t\t\t  Q_ARG(bool, false));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tQMetaObject::invokeMethod(this, \"UpdateAudioWarnings\", Qt::QueuedConnection);\n\tQMetaObject::invokeMethod(this, \"AudioChangedRestart\");\n}\n\nvoid OBSBasicSettings::SimpleRecordingQualityLosslessWarning(int idx)\n{\n\tif (idx == lastSimpleRecQualityIdx || idx == -1)\n\t\treturn;\n\n\tQString qual = ui->simpleOutRecQuality->itemData(idx).toString();\n\n\tif (loading) {\n\t\tlastSimpleRecQualityIdx = idx;\n\t\treturn;\n\t}\n\n\tif (qual == \"Lossless\") {\n\t\tQMessageBox::StandardButton button;\n\n\t\tQString warningString =\n\t\t\tSIMPLE_OUTPUT_WARNING(\"Lossless\") + QString(\"\\n\\n\") + SIMPLE_OUTPUT_WARNING(\"Lossless.Msg\");\n\n\t\tbutton = OBSMessageBox::question(this, SIMPLE_OUTPUT_WARNING(\"Lossless.Title\"), warningString);\n\n\t\tif (button == QMessageBox::No) {\n\t\t\tQMetaObject::invokeMethod(ui->simpleOutRecQuality, \"setCurrentIndex\", Qt::QueuedConnection,\n\t\t\t\t\t\t  Q_ARG(int, lastSimpleRecQualityIdx));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tlastSimpleRecQualityIdx = idx;\n}\n\nvoid OBSBasicSettings::on_disableOSXVSync_clicked()\n{\n#ifdef __APPLE__\n\tif (!loading) {\n\t\tbool disable = ui->disableOSXVSync->isChecked();\n\t\tui->resetOSXVSync->setEnabled(disable);\n\t}\n#endif\n}\n\nQIcon OBSBasicSettings::GetGeneralIcon() const\n{\n\treturn generalIcon;\n}\n\nQIcon OBSBasicSettings::GetAppearanceIcon() const\n{\n\treturn appearanceIcon;\n}\n\nQIcon OBSBasicSettings::GetStreamIcon() const\n{\n\treturn streamIcon;\n}\n\nQIcon OBSBasicSettings::GetOutputIcon() const\n{\n\treturn outputIcon;\n}\n\nQIcon OBSBasicSettings::GetAudioIcon() const\n{\n\treturn audioIcon;\n}\n\nQIcon OBSBasicSettings::GetVideoIcon() const\n{\n\treturn videoIcon;\n}\n\nQIcon OBSBasicSettings::GetHotkeysIcon() const\n{\n\treturn hotkeysIcon;\n}\n\nQIcon OBSBasicSettings::GetAccessibilityIcon() const\n{\n\treturn accessibilityIcon;\n}\n\nQIcon OBSBasicSettings::GetAdvancedIcon() const\n{\n\treturn advancedIcon;\n}\n\nvoid OBSBasicSettings::SetGeneralIcon(const QIcon &icon)\n{\n\tui->listWidget->item(Pages::GENERAL)->setIcon(icon);\n}\n\nvoid OBSBasicSettings::SetAppearanceIcon(const QIcon &icon)\n{\n\tui->listWidget->item(Pages::APPEARANCE)->setIcon(icon);\n}\n\nvoid OBSBasicSettings::SetStreamIcon(const QIcon &icon)\n{\n\tui->listWidget->item(Pages::STREAM)->setIcon(icon);\n}\n\nvoid OBSBasicSettings::SetOutputIcon(const QIcon &icon)\n{\n\tui->listWidget->item(Pages::OUTPUT)->setIcon(icon);\n}\n\nvoid OBSBasicSettings::SetAudioIcon(const QIcon &icon)\n{\n\tui->listWidget->item(Pages::AUDIO)->setIcon(icon);\n}\n\nvoid OBSBasicSettings::SetVideoIcon(const QIcon &icon)\n{\n\tui->listWidget->item(Pages::VIDEO)->setIcon(icon);\n}\n\nvoid OBSBasicSettings::SetHotkeysIcon(const QIcon &icon)\n{\n\tui->listWidget->item(Pages::HOTKEYS)->setIcon(icon);\n}\n\nvoid OBSBasicSettings::SetAccessibilityIcon(const QIcon &icon)\n{\n\tui->listWidget->item(Pages::ACCESSIBILITY)->setIcon(icon);\n}\n\nvoid OBSBasicSettings::SetAdvancedIcon(const QIcon &icon)\n{\n\tui->listWidget->item(Pages::ADVANCED)->setIcon(icon);\n}\n\nint OBSBasicSettings::CurrentFLVTrack()\n{\n\tif (ui->flvTrack1->isChecked())\n\t\treturn 1;\n\telse if (ui->flvTrack2->isChecked())\n\t\treturn 2;\n\telse if (ui->flvTrack3->isChecked())\n\t\treturn 3;\n\telse if (ui->flvTrack4->isChecked())\n\t\treturn 4;\n\telse if (ui->flvTrack5->isChecked())\n\t\treturn 5;\n\telse if (ui->flvTrack6->isChecked())\n\t\treturn 6;\n\n\treturn 0;\n}\n\nint OBSBasicSettings::SimpleOutGetSelectedAudioTracks()\n{\n\tint tracks = (ui->simpleOutRecTrack1->isChecked() ? (1 << 0) : 0) |\n\t\t     (ui->simpleOutRecTrack2->isChecked() ? (1 << 1) : 0) |\n\t\t     (ui->simpleOutRecTrack3->isChecked() ? (1 << 2) : 0) |\n\t\t     (ui->simpleOutRecTrack4->isChecked() ? (1 << 3) : 0) |\n\t\t     (ui->simpleOutRecTrack5->isChecked() ? (1 << 4) : 0) |\n\t\t     (ui->simpleOutRecTrack6->isChecked() ? (1 << 5) : 0);\n\treturn tracks;\n}\n\nint OBSBasicSettings::AdvOutGetSelectedAudioTracks()\n{\n\tint tracks =\n\t\t(ui->advOutRecTrack1->isChecked() ? (1 << 0) : 0) | (ui->advOutRecTrack2->isChecked() ? (1 << 1) : 0) |\n\t\t(ui->advOutRecTrack3->isChecked() ? (1 << 2) : 0) | (ui->advOutRecTrack4->isChecked() ? (1 << 3) : 0) |\n\t\t(ui->advOutRecTrack5->isChecked() ? (1 << 4) : 0) | (ui->advOutRecTrack6->isChecked() ? (1 << 5) : 0);\n\treturn tracks;\n}\n\nint OBSBasicSettings::AdvOutGetStreamingSelectedAudioTracks()\n{\n\tint tracks = (ui->advOutMultiTrack1->isChecked() ? (1 << 0) : 0) |\n\t\t     (ui->advOutMultiTrack2->isChecked() ? (1 << 1) : 0) |\n\t\t     (ui->advOutMultiTrack3->isChecked() ? (1 << 2) : 0) |\n\t\t     (ui->advOutMultiTrack4->isChecked() ? (1 << 3) : 0) |\n\t\t     (ui->advOutMultiTrack5->isChecked() ? (1 << 4) : 0) |\n\t\t     (ui->advOutMultiTrack6->isChecked() ? (1 << 5) : 0);\n\treturn tracks;\n}\n\n/* Using setEditable(true) on a QComboBox when there's a custom style in use\n * does not work properly, so instead completely recreate the widget, which\n * seems to work fine. */\nvoid OBSBasicSettings::RecreateOutputResolutionWidget()\n{\n\tQSizePolicy sizePolicy = ui->outputResolution->sizePolicy();\n\tbool changed = WidgetChanged(ui->outputResolution);\n\n\tdelete ui->outputResolution;\n\tui->outputResolution = new QComboBox(ui->videoPage);\n\tui->outputResolution->setObjectName(QString::fromUtf8(\"outputResolution\"));\n\tui->outputResolution->setSizePolicy(sizePolicy);\n\tui->outputResolution->setEditable(true);\n\tui->outputResolution->setProperty(\"changed\", changed);\n\tui->outputResLabel->setBuddy(ui->outputResolution);\n\n\tui->outputResLayout->insertWidget(0, ui->outputResolution);\n\n\tQWidget::setTabOrder(ui->baseResolution, ui->outputResolution);\n\tQWidget::setTabOrder(ui->outputResolution, ui->downscaleFilter);\n\n\tHookWidget(ui->outputResolution, CBEDIT_CHANGED, VIDEO_RES);\n\n\tconnect(ui->outputResolution, &QComboBox::editTextChanged, this,\n\t\t&OBSBasicSettings::on_outputResolution_editTextChanged);\n\n\tui->outputResolution->lineEdit()->setValidator(ui->baseResolution->lineEdit()->validator());\n}\n\nvoid OBSBasicSettings::UpdateAdvNetworkGroup()\n{\n\tbool enabled = protocol.contains(\"RTMP\");\n\n\tui->advNetworkDisabled->setVisible(!enabled);\n\n\tui->bindToIPLabel->setVisible(enabled);\n\tui->bindToIP->setVisible(enabled);\n\tui->dynBitrate->setVisible(enabled);\n\tui->ipFamilyLabel->setVisible(enabled);\n\tui->ipFamily->setVisible(enabled);\n#ifdef _WIN32\n\tui->enableNewSocketLoop->setVisible(enabled);\n\tui->enableLowLatencyMode->setVisible(enabled);\n#endif\n}\n\nvoid OBSBasicSettings::UpdateMultitrackVideo()\n{\n\t// Technically, it should currently be safe to toggle multitrackVideo\n\t// while not streaming (recording should be irrelevant), but practically\n\t// output settings aren't currently being tracked with that degree of\n\t// flexibility, so just disable everything while outputs are active.\n\tauto toggle_available = !main->Active();\n\n\t// FIXME: protocol is not updated properly for WHIP; what do?\n\tauto available = protocol.startsWith(\"RTMP\");\n\n\tif (available && !IsCustomService()) {\n\t\tOBSDataAutoRelease settings = obs_data_create();\n\t\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(ui->service->currentText()));\n\t\tOBSServiceAutoRelease temp_service =\n\t\t\tobs_service_create_private(\"rtmp_common\", \"auto config query service\", settings);\n\t\tsettings = obs_service_get_settings(temp_service);\n\t\tavailable = obs_data_has_user_value(settings, \"multitrack_video_configuration_url\");\n\t\tif (!available && ui->enableMultitrackVideo->isChecked())\n\t\t\tui->enableMultitrackVideo->setChecked(false);\n\t}\n\n\tui->multitrackVideoGroupBox->setVisible(available);\n\n\tui->enableMultitrackVideo->setEnabled(toggle_available);\n\n\tui->multitrackVideoMaximumAggregateBitrateLabel->setEnabled(toggle_available &&\n\t\t\t\t\t\t\t\t    ui->enableMultitrackVideo->isChecked());\n\tui->multitrackVideoMaximumAggregateBitrateAuto->setEnabled(toggle_available &&\n\t\t\t\t\t\t\t\t   ui->enableMultitrackVideo->isChecked());\n\tui->multitrackVideoMaximumAggregateBitrate->setEnabled(\n\t\ttoggle_available && ui->enableMultitrackVideo->isChecked() &&\n\t\t!ui->multitrackVideoMaximumAggregateBitrateAuto->isChecked());\n\n\tui->multitrackVideoMaximumVideoTracksLabel->setEnabled(toggle_available &&\n\t\t\t\t\t\t\t       ui->enableMultitrackVideo->isChecked());\n\tui->multitrackVideoMaximumVideoTracksAuto->setEnabled(toggle_available &&\n\t\t\t\t\t\t\t      ui->enableMultitrackVideo->isChecked());\n\tui->multitrackVideoMaximumVideoTracks->setEnabled(toggle_available && ui->enableMultitrackVideo->isChecked() &&\n\t\t\t\t\t\t\t  !ui->multitrackVideoMaximumVideoTracksAuto->isChecked());\n\tui->multitrackVideoAdditionalCanvas->setEnabled(toggle_available && ui->enableMultitrackVideo->isChecked());\n\n\tui->multitrackVideoStreamDumpEnable->setVisible(available && IsCustomService());\n\tui->multitrackVideoConfigOverrideEnable->setVisible(available && IsCustomService());\n\tui->multitrackVideoConfigOverrideLabel->setVisible(available && IsCustomService());\n\tui->multitrackVideoConfigOverride->setVisible(available && IsCustomService());\n\n\tui->multitrackVideoStreamDumpEnable->setEnabled(toggle_available && ui->enableMultitrackVideo->isChecked());\n\tui->multitrackVideoConfigOverrideEnable->setEnabled(toggle_available && ui->enableMultitrackVideo->isChecked());\n\tui->multitrackVideoConfigOverrideLabel->setEnabled(toggle_available && ui->enableMultitrackVideo->isChecked() &&\n\t\t\t\t\t\t\t   ui->multitrackVideoConfigOverrideEnable->isChecked());\n\tui->multitrackVideoConfigOverride->setEnabled(toggle_available && ui->enableMultitrackVideo->isChecked() &&\n\t\t\t\t\t\t      ui->multitrackVideoConfigOverrideEnable->isChecked());\n\n\tauto update_simple_output_settings = [&](bool mtv_enabled) {\n\t\tauto recording_uses_stream_encoder = ui->simpleOutRecQuality->currentData().toString() == \"Stream\";\n\t\tmtv_enabled = mtv_enabled && !recording_uses_stream_encoder;\n\n\t\tui->simpleOutputVBitrateLabel->setDisabled(mtv_enabled);\n\t\tui->simpleOutputVBitrate->setDisabled(mtv_enabled);\n\n\t\tui->simpleOutputABitrateLabel->setDisabled(mtv_enabled);\n\t\tui->simpleOutputABitrate->setDisabled(mtv_enabled);\n\n\t\tui->simpleOutStrEncoderLabel->setDisabled(mtv_enabled);\n\t\tui->simpleOutStrEncoder->setDisabled(mtv_enabled);\n\n\t\tui->simpleOutPresetLabel->setDisabled(mtv_enabled);\n\t\tui->simpleOutPreset->setDisabled(mtv_enabled);\n\n\t\tui->simpleOutCustomLabel->setDisabled(mtv_enabled);\n\t\tui->simpleOutCustom->setDisabled(mtv_enabled);\n\n\t\tui->simpleOutStrAEncoderLabel->setDisabled(mtv_enabled);\n\t\tui->simpleOutStrAEncoder->setDisabled(mtv_enabled);\n\t};\n\n\tauto update_advanced_output_settings = [&](bool mtv_enabled) {\n\t\tauto recording_uses_stream_video_encoder = ui->advOutRecEncoder->currentText() == TEXT_USE_STREAM_ENC;\n\t\tauto recording_uses_stream_audio_encoder = ui->advOutRecAEncoder->currentData() == \"none\";\n\t\tauto disable_video = mtv_enabled && !recording_uses_stream_video_encoder;\n\t\tauto disable_audio = mtv_enabled && !recording_uses_stream_audio_encoder;\n\n\t\tui->advOutAEncLabel->setDisabled(disable_audio);\n\t\tui->advOutAEncoder->setDisabled(disable_audio);\n\n\t\tui->advOutEncLabel->setDisabled(disable_video);\n\t\tui->advOutEncoder->setDisabled(disable_video);\n\n\t\tui->advOutUseRescale->setDisabled(disable_video);\n\t\tui->advOutRescale->setDisabled(disable_video);\n\t\tui->advOutRescaleFilter->setDisabled(disable_video);\n\n\t\tif (streamEncoderProps)\n\t\t\tstreamEncoderProps->SetDisabled(disable_video);\n\t};\n\n\tauto update_advanced_output_audio_tracks = [&](bool mtv_enabled) {\n\t\tauto vod_track_enabled = vodTrackCheckbox && vodTrackCheckbox->isChecked();\n\n\t\tauto vod_track_idx_enabled = [&](size_t idx) {\n\t\t\treturn vod_track_enabled && vodTrack[idx - 1] && vodTrack[idx - 1]->isChecked();\n\t\t};\n\n\t\tauto track1_warning_visible = mtv_enabled &&\n\t\t\t\t\t      (ui->advOutTrack1->isChecked() || vod_track_idx_enabled(1));\n\t\tauto track1_disabled = track1_warning_visible && !ui->advOutRecTrack1->isChecked();\n\t\tui->advOutTrack1BitrateLabel->setDisabled(track1_disabled);\n\t\tui->advOutTrack1Bitrate->setDisabled(track1_disabled);\n\n\t\tauto track2_warning_visible = mtv_enabled &&\n\t\t\t\t\t      (ui->advOutTrack2->isChecked() || vod_track_idx_enabled(2));\n\t\tauto track2_disabled = track2_warning_visible && !ui->advOutRecTrack2->isChecked();\n\t\tui->advOutTrack2BitrateLabel->setDisabled(track2_disabled);\n\t\tui->advOutTrack2Bitrate->setDisabled(track2_disabled);\n\n\t\tauto track3_warning_visible = mtv_enabled &&\n\t\t\t\t\t      (ui->advOutTrack3->isChecked() || vod_track_idx_enabled(3));\n\t\tauto track3_disabled = track3_warning_visible && !ui->advOutRecTrack3->isChecked();\n\t\tui->advOutTrack3BitrateLabel->setDisabled(track3_disabled);\n\t\tui->advOutTrack3Bitrate->setDisabled(track3_disabled);\n\n\t\tauto track4_warning_visible = mtv_enabled &&\n\t\t\t\t\t      (ui->advOutTrack4->isChecked() || vod_track_idx_enabled(4));\n\t\tauto track4_disabled = track4_warning_visible && !ui->advOutRecTrack4->isChecked();\n\t\tui->advOutTrack4BitrateLabel->setDisabled(track4_disabled);\n\t\tui->advOutTrack4Bitrate->setDisabled(track4_disabled);\n\n\t\tauto track5_warning_visible = mtv_enabled &&\n\t\t\t\t\t      (ui->advOutTrack5->isChecked() || vod_track_idx_enabled(5));\n\t\tauto track5_disabled = track5_warning_visible && !ui->advOutRecTrack5->isChecked();\n\t\tui->advOutTrack5BitrateLabel->setDisabled(track5_disabled);\n\t\tui->advOutTrack5Bitrate->setDisabled(track5_disabled);\n\n\t\tauto track6_warning_visible = mtv_enabled &&\n\t\t\t\t\t      (ui->advOutTrack6->isChecked() || vod_track_idx_enabled(6));\n\t\tauto track6_disabled = track6_warning_visible && !ui->advOutRecTrack6->isChecked();\n\t\tui->advOutTrack6BitrateLabel->setDisabled(track6_disabled);\n\t\tui->advOutTrack6Bitrate->setDisabled(track6_disabled);\n\t};\n\n\tif (available) {\n\t\tOBSDataAutoRelease settings;\n\t\t{\n\t\t\tauto service_name = ui->service->currentText();\n\t\t\tauto custom_server = ui->customServer->text().trimmed();\n\n\t\t\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\t\t\tobs_property_t *service = obs_properties_get(props, \"service\");\n\n\t\t\tsettings = obs_data_create();\n\n\t\t\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(service_name));\n\t\t\tobs_property_modified(service, settings);\n\n\t\t\tobs_properties_destroy(props);\n\t\t}\n\n\t\tauto multitrack_video_name = QTStr(\"Basic.Settings.Stream.MultitrackVideoLabel\");\n\t\tif (obs_data_has_user_value(settings, \"multitrack_video_name\"))\n\t\t\tmultitrack_video_name = obs_data_get_string(settings, \"multitrack_video_name\");\n\n\t\tui->enableMultitrackVideo->setText(\n\t\t\tQTStr(\"Basic.Settings.Stream.EnableMultitrackVideo\").arg(multitrack_video_name));\n\n\t\tif (obs_data_has_user_value(settings, \"multitrack_video_disclaimer\")) {\n\t\t\tui->multitrackVideoInfo->setVisible(true);\n\t\t\tui->multitrackVideoInfo->setText(obs_data_get_string(settings, \"multitrack_video_disclaimer\"));\n\t\t} else {\n\t\t\tui->multitrackVideoInfo->setText(\n\t\t\t\tQTStr(\"MultitrackVideo.Info\").arg(multitrack_video_name, ui->service->currentText()));\n\t\t}\n\n\t\tauto disabled_text = QTStr(\"Basic.Settings.MultitrackVideoDisabledSettings\")\n\t\t\t\t\t     .arg(ui->service->currentText())\n\t\t\t\t\t     .arg(multitrack_video_name);\n\n\t\tui->multitrackVideoNotice->setText(disabled_text);\n\n\t\tauto mtv_enabled = ui->enableMultitrackVideo->isChecked();\n\t\tui->multitrackVideoNoticeBox->setVisible(mtv_enabled);\n\n\t\tupdate_simple_output_settings(mtv_enabled);\n\t\tupdate_advanced_output_settings(mtv_enabled);\n\t\tupdate_advanced_output_audio_tracks(mtv_enabled);\n\t} else {\n\t\tui->multitrackVideoNoticeBox->setVisible(false);\n\n\t\tupdate_simple_output_settings(false);\n\t\tupdate_advanced_output_settings(false);\n\t\tupdate_advanced_output_audio_tracks(false);\n\t}\n}\n\nvoid OBSBasicSettings::SimpleStreamAudioEncoderChanged()\n{\n\tPopulateSimpleBitrates(ui->simpleOutputABitrate, ui->simpleOutStrAEncoder->currentData().toString() == \"opus\");\n\n\tif (IsSurround(QT_TO_UTF8(ui->channelSetup->currentText())))\n\t\treturn;\n\n\tRestrictResetBitrates({ui->simpleOutputABitrate}, 320);\n}\n\nvoid OBSBasicSettings::AdvAudioEncodersChanged()\n{\n\tQString streamEncoder = ui->advOutAEncoder->currentData().toString();\n\tQString recEncoder = ui->advOutRecAEncoder->currentData().toString();\n\n\tif (recEncoder == \"none\")\n\t\trecEncoder = streamEncoder;\n\n\tPopulateAdvancedBitrates({ui->advOutTrack1Bitrate, ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,\n\t\t\t\t  ui->advOutTrack4Bitrate, ui->advOutTrack5Bitrate, ui->advOutTrack6Bitrate},\n\t\t\t\t QT_TO_UTF8(streamEncoder), QT_TO_UTF8(recEncoder));\n\n\tif (IsSurround(QT_TO_UTF8(ui->channelSetup->currentText())))\n\t\treturn;\n\n\tRestrictResetBitrates({ui->advOutTrack1Bitrate, ui->advOutTrack2Bitrate, ui->advOutTrack3Bitrate,\n\t\t\t       ui->advOutTrack4Bitrate, ui->advOutTrack5Bitrate, ui->advOutTrack6Bitrate},\n\t\t\t      320);\n}\n"
  },
  {
    "path": "frontend/settings/OBSBasicSettings.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_OBSBasicSettings.h\"\n\n#include <utility/FFmpegShared.hpp>\n\n#include <QPointer>\n\n#define VOLUME_METER_DECAY_FAST 23.53\n#define VOLUME_METER_DECAY_MEDIUM 11.76\n#define VOLUME_METER_DECAY_SLOW 8.57\n\nclass Auth;\nclass OBSBasic;\nclass OBSHotkeyWidget;\nclass OBSPropertiesView;\nstruct FFmpegFormat;\nstruct OBSTheme;\n\nstd::string DeserializeConfigText(const char *value);\n\nclass OBSBasicSettings : public QDialog {\n\tQ_OBJECT\n\tQ_PROPERTY(QIcon generalIcon READ GetGeneralIcon WRITE SetGeneralIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon appearanceIcon READ GetAppearanceIcon WRITE SetAppearanceIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon streamIcon READ GetStreamIcon WRITE SetStreamIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon outputIcon READ GetOutputIcon WRITE SetOutputIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon audioIcon READ GetAudioIcon WRITE SetAudioIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon videoIcon READ GetVideoIcon WRITE SetVideoIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon hotkeysIcon READ GetHotkeysIcon WRITE SetHotkeysIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon accessibilityIcon READ GetAccessibilityIcon WRITE SetAccessibilityIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon advancedIcon READ GetAdvancedIcon WRITE SetAdvancedIcon DESIGNABLE true)\n\n\tenum Pages { GENERAL, APPEARANCE, STREAM, OUTPUT, AUDIO, VIDEO, HOTKEYS, ACCESSIBILITY, ADVANCED, NUM_PAGES };\n\nprivate:\n\tOBSBasic *main;\n\n\tstd::unique_ptr<Ui::OBSBasicSettings> ui;\n\n\tstd::shared_ptr<Auth> auth;\n\n\tbool generalChanged = false;\n\tbool stream1Changed = false;\n\tbool outputsChanged = false;\n\tbool audioChanged = false;\n\tbool videoChanged = false;\n\tbool hotkeysChanged = false;\n\tbool a11yChanged = false;\n\tbool appearanceChanged = false;\n\tbool advancedChanged = false;\n\tint pageIndex = 0;\n\tbool loading = true;\n\tbool forceAuthReload = false;\n\tbool forceUpdateCheck = false;\n\tint sampleRateIndex = 0;\n\tint channelIndex = 0;\n\tbool llBufferingEnabled = false;\n\tbool hotkeysLoaded = false;\n\n\tint lastSimpleRecQualityIdx = 0;\n\tint lastServiceIdx = -1;\n\tint lastIgnoreRecommended = -1;\n\tint lastChannelSetupIdx = 0;\n\n\tstatic constexpr uint32_t ENCODER_HIDE_FLAGS = (OBS_ENCODER_CAP_DEPRECATED | OBS_ENCODER_CAP_INTERNAL);\n\n\tOBSTheme *savedTheme = nullptr;\n\n\tstd::vector<FFmpegFormat> formats;\n\n\tOBSPropertiesView *streamProperties = nullptr;\n\tOBSPropertiesView *streamEncoderProps = nullptr;\n\tOBSPropertiesView *recordEncoderProps = nullptr;\n\n\tQPointer<QLabel> advOutRecWarning;\n\tQPointer<QLabel> simpleOutRecWarning;\n\n\tQString curPreset;\n\tQString curQSVPreset;\n\tQString curNVENCPreset;\n\tQString curAMDPreset;\n\tQString curAMDAV1Preset;\n\n\tQString curAdvStreamEncoder;\n\tQString curAdvRecordEncoder;\n\n\tusing AudioSource_t = std::tuple<OBSWeakSource, QPointer<QCheckBox>, QPointer<QSpinBox>, QPointer<QCheckBox>,\n\t\t\t\t\t QPointer<QSpinBox>>;\n\tstd::vector<AudioSource_t> audioSources;\n\tstd::vector<OBSSignal> audioSourceSignals;\n\tOBSSignal sourceCreated;\n\tOBSSignal channelChanged;\n\n\tstd::vector<std::pair<bool, QPointer<OBSHotkeyWidget>>> hotkeys;\n\tOBSSignal hotkeyRegistered;\n\tOBSSignal hotkeyUnregistered;\n\n\tuint32_t outputCX = 0;\n\tuint32_t outputCY = 0;\n\n\tQPointer<QCheckBox> simpleVodTrack;\n\n\tQPointer<QCheckBox> vodTrackCheckbox;\n\tQPointer<QWidget> vodTrackContainer;\n\tQPointer<QRadioButton> vodTrack[MAX_AUDIO_MIXES];\n\n\tQIcon hotkeyConflictIcon;\n\n\tvoid SaveCombo(QComboBox *widget, const char *section, const char *value);\n\tvoid SaveComboData(QComboBox *widget, const char *section, const char *value);\n\tvoid SaveCheckBox(QAbstractButton *widget, const char *section, const char *value, bool invert = false);\n\tvoid SaveGroupBox(QGroupBox *widget, const char *section, const char *value);\n\tvoid SaveEdit(QLineEdit *widget, const char *section, const char *value);\n\tvoid SaveSpinBox(QSpinBox *widget, const char *section, const char *value);\n\tvoid SaveText(QPlainTextEdit *widget, const char *section, const char *value);\n\tvoid SaveFormat(QComboBox *combo);\n\tvoid SaveEncoder(QComboBox *combo, const char *section, const char *value);\n\n\tbool ResFPSValid(obs_service_resolution *res_list, size_t res_count, int max_fps);\n\n\t// TODO: Remove, orphaned method\n\tvoid ClosestResFPS(obs_service_resolution *res_list, size_t res_count, int max_fps, int &new_cx, int &new_cy,\n\t\t\t   int &new_fps);\n\n\tinline bool Changed() const\n\t{\n\t\treturn generalChanged || appearanceChanged || outputsChanged || stream1Changed || audioChanged ||\n\t\t       videoChanged || advancedChanged || hotkeysChanged || a11yChanged;\n\t}\n\n\tinline void EnableApplyButton(bool en) { ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(en); }\n\n\tinline void ClearChanged()\n\t{\n\t\tgeneralChanged = false;\n\t\tstream1Changed = false;\n\t\toutputsChanged = false;\n\t\taudioChanged = false;\n\t\tvideoChanged = false;\n\t\thotkeysChanged = false;\n\t\ta11yChanged = false;\n\t\tadvancedChanged = false;\n\t\tappearanceChanged = false;\n\t\tEnableApplyButton(false);\n\t}\n\n\ttemplate<typename Widget, typename WidgetParent, typename... SignalArgs, typename... SlotArgs>\n\tvoid HookWidget(Widget *widget, void (WidgetParent::*signal)(SignalArgs...),\n\t\t\tvoid (OBSBasicSettings::*slot)(SlotArgs...))\n\t{\n\t\tQObject::connect(widget, signal, this, slot);\n\t\twidget->setProperty(\"changed\", QVariant(false));\n\t}\n\n\tbool QueryChanges();\n\tbool QueryAllowedToClose();\n\n\tvoid ResetEncoders(bool streamOnly = false);\n\tvoid LoadColorRanges();\n\tvoid LoadColorSpaces();\n\tvoid LoadColorFormats();\n\tvoid LoadFormats();\n\tvoid ReloadCodecs(const FFmpegFormat &format);\n\n\tvoid UpdateColorFormatSpaceWarning();\n\n\tvoid LoadGeneralSettings();\n\tvoid LoadStream1Settings();\n\tvoid LoadOutputSettings();\n\tvoid LoadAudioSettings();\n\tvoid LoadVideoSettings();\n\tvoid LoadHotkeySettings(obs_hotkey_id ignoreKey = OBS_INVALID_HOTKEY_ID);\n\tvoid LoadA11ySettings(bool presetChange = false);\n\tvoid LoadAppearanceSettings(bool reload = false);\n\tvoid LoadAdvancedSettings();\n\tvoid LoadSettings(bool changedOnly);\n\n\tOBSPropertiesView *CreateEncoderPropertyView(const char *encoder, const char *path, bool changed = false);\n\n\t/* general */\n\tvoid LoadLanguageList();\n\tvoid LoadThemeList(bool firstLoad);\n\tvoid LoadBranchesList();\n\n\t/* stream */\n\tvoid InitStreamPage();\n\tbool IsCustomService() const;\n\tinline bool IsWHIP() const;\n\tvoid LoadServices(bool showAll);\n\tvoid OnOAuthStreamKeyConnected();\n\tvoid OnAuthConnected();\n\tQString lastService;\n\tQString protocol;\n\tQString lastCustomServer;\n\tint prevLangIndex;\n\tbool prevBrowserAccel;\n\n\tvoid ServiceChanged(bool resetFields = false);\n\tQString FindProtocol();\n\tvoid UpdateServerList();\n\tvoid UpdateKeyLink();\n\tvoid UpdateVodTrackSetting();\n\tvoid UpdateServiceRecommendations();\n\tvoid UpdateMoreInfoLink();\n\tvoid UpdateAdvNetworkGroup();\n\n\t/* Appearance */\n\tvoid InitAppearancePage();\n\tvoid enableAppearanceFontControls(bool enable);\n\tvoid enableAppearanceDensityControls(bool enable);\n\n\tbool IsCustomServer();\n\nprivate slots:\n\tvoid UpdateMultitrackVideo();\n\tvoid RecreateOutputResolutionWidget();\n\tbool UpdateResFPSLimits();\n\tvoid DisplayEnforceWarning(bool checked);\n\tvoid on_show_clicked();\n\tvoid on_authPwShow_clicked();\n\tvoid on_connectAccount_clicked();\n\tvoid on_disconnectAccount_clicked();\n\tvoid on_useStreamKey_clicked();\n\tvoid on_useAuth_toggled();\n\tvoid on_server_currentIndexChanged(int index);\n\n\tvoid on_hotkeyFilterReset_clicked();\n\tvoid on_hotkeyFilterSearch_textChanged(const QString text);\n\tvoid on_hotkeyFilterInput_KeyChanged(obs_key_combination_t combo);\n\nprivate:\n\t/* output */\n\tvoid LoadSimpleOutputSettings();\n\tvoid LoadAdvOutputStreamingSettings();\n\tvoid LoadAdvOutputStreamingEncoderProperties();\n\tvoid LoadAdvOutputRecordingSettings();\n\tvoid LoadAdvOutputRecordingEncoderProperties();\n\tvoid LoadAdvOutputFFmpegSettings();\n\tvoid LoadAdvOutputAudioSettings();\n\tvoid SetAdvOutputFFmpegEnablement(FFmpegCodecType encoderType, bool enabled, bool enableEncode = false);\n\n\t/* audio */\n\tvoid LoadListValues(QComboBox *widget, obs_property_t *prop, int index);\n\tvoid LoadAudioDevices();\n\tvoid LoadAudioSources();\n\n\t/* video */\n\tvoid LoadRendererList();\n\tvoid ResetDownscales(uint32_t cx, uint32_t cy, bool ignoreAllSignals = false);\n\tvoid LoadDownscaleFilters();\n\tvoid LoadResolutionLists();\n\tvoid LoadFPSData();\n\n\t/* a11y */\n\tvoid UpdateA11yColors();\n\tvoid SetDefaultColors();\n\tvoid ResetDefaultColors();\n\tQColor GetColor(uint32_t colorVal, QString label);\n\tuint32_t preset = 0;\n\tuint32_t selectRed = 0x0000FF;\n\tuint32_t selectGreen = 0x00FF00;\n\tuint32_t selectBlue = 0xFF7F00;\n\tuint32_t mixerGreen = 0x267f26;\n\tuint32_t mixerYellow = 0x267f7f;\n\tuint32_t mixerRed = 0x26267f;\n\tuint32_t mixerGreenActive = 0x4cff4c;\n\tuint32_t mixerYellowActive = 0x4cffff;\n\tuint32_t mixerRedActive = 0x4c4cff;\n\n\tvoid SaveGeneralSettings();\n\tvoid SaveStream1Settings();\n\tvoid SaveOutputSettings();\n\tvoid SaveAudioSettings();\n\tvoid SaveVideoSettings();\n\tvoid SaveHotkeySettings();\n\tvoid SaveA11ySettings();\n\tvoid SaveAppearanceSettings();\n\tvoid SaveAdvancedSettings();\n\tvoid SaveSettings();\n\n\tvoid SearchHotkeys(const QString &text, obs_key_combination_t filterCombo);\n\n\tvoid UpdateSimpleOutStreamDelayEstimate();\n\tvoid UpdateAdvOutStreamDelayEstimate();\n\n\tvoid FillSimpleRecordingValues();\n\tvoid FillAudioMonitoringDevices();\n\n\tvoid RecalcOutputResPixels(const char *resText);\n\n\tbool AskIfCanCloseSettings();\n\n\tvoid UpdateYouTubeAppDockSettings();\n\n\tQIcon generalIcon;\n\tQIcon appearanceIcon;\n\tQIcon streamIcon;\n\tQIcon outputIcon;\n\tQIcon audioIcon;\n\tQIcon videoIcon;\n\tQIcon hotkeysIcon;\n\tQIcon accessibilityIcon;\n\tQIcon advancedIcon;\n\n\tQIcon GetGeneralIcon() const;\n\tQIcon GetAppearanceIcon() const;\n\tQIcon GetStreamIcon() const;\n\tQIcon GetOutputIcon() const;\n\tQIcon GetAudioIcon() const;\n\tQIcon GetVideoIcon() const;\n\tQIcon GetHotkeysIcon() const;\n\tQIcon GetAccessibilityIcon() const;\n\tQIcon GetAdvancedIcon() const;\n\n\tint CurrentFLVTrack();\n\tint SimpleOutGetSelectedAudioTracks();\n\tint AdvOutGetSelectedAudioTracks();\n\tint AdvOutGetStreamingSelectedAudioTracks();\n\n\tOBSService GetStream1Service();\n\n\tbool ServiceAndVCodecCompatible();\n\tbool ServiceAndACodecCompatible();\n\tbool ServiceSupportsCodecCheck();\n\n\tinline bool AllowsMultiTrack(const char *protocol);\n\tvoid SwapMultiTrack(const char *protocol);\n\nprivate slots:\n\tvoid on_theme_activated(int idx);\n\tvoid on_themeVariant_activated(int idx);\n\tvoid updateAppearanceControls();\n\n\tvoid on_listWidget_itemSelectionChanged();\n\tvoid on_buttonBox_clicked(QAbstractButton *button);\n\n\tvoid on_service_currentIndexChanged(int idx);\n\tvoid on_customServer_textChanged(const QString &text);\n\tvoid on_simpleOutputBrowse_clicked();\n\tvoid on_advOutRecPathBrowse_clicked();\n\tvoid on_advOutFFPathBrowse_clicked();\n\tvoid on_advOutEncoder_currentIndexChanged();\n\tvoid on_advOutRecEncoder_currentIndexChanged(int idx);\n\tvoid on_advOutFFIgnoreCompat_stateChanged(int state);\n\tvoid on_advOutFFFormat_currentIndexChanged(int idx);\n\tvoid on_advOutFFAEncoder_currentIndexChanged(int idx);\n\tvoid on_advOutFFVEncoder_currentIndexChanged(int idx);\n\tvoid on_advOutFFType_currentIndexChanged(int idx);\n\n\tvoid on_colorFormat_currentIndexChanged(int idx);\n\tvoid on_colorSpace_currentIndexChanged(int idx);\n\n\tvoid on_filenameFormatting_textEdited(const QString &text);\n\tvoid on_outputResolution_editTextChanged(const QString &text);\n\tvoid on_baseResolution_editTextChanged(const QString &text);\n\n\tvoid on_disableOSXVSync_clicked();\n\n\tvoid on_choose1_clicked();\n\tvoid on_choose2_clicked();\n\tvoid on_choose3_clicked();\n\tvoid on_choose4_clicked();\n\tvoid on_choose5_clicked();\n\tvoid on_choose6_clicked();\n\tvoid on_choose7_clicked();\n\tvoid on_choose8_clicked();\n\tvoid on_choose9_clicked();\n\tvoid on_colorPreset_currentIndexChanged(int idx);\n\n\tvoid GeneralChanged();\n\n#if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)\n\tvoid HideOBSWindowWarning(Qt::CheckState state);\n#else\n\tvoid HideOBSWindowWarning(int state);\n#endif\n\tvoid AudioChanged();\n\tvoid AudioChangedRestart();\n\tvoid ReloadAudioSources();\n\tvoid SurroundWarning(int idx);\n\tvoid SpeakerLayoutChanged(int idx);\n\tvoid LowLatencyBufferingChanged(bool checked);\n\tvoid UpdateAudioWarnings();\n\tvoid OutputsChanged();\n\tvoid Stream1Changed();\n\tvoid VideoChanged();\n\tvoid VideoChangedResolution();\n\tvoid HotkeysChanged();\n\tbool ScanDuplicateHotkeys(QFormLayout *layout);\n\tvoid ReloadHotkeys(obs_hotkey_id ignoreKey = OBS_INVALID_HOTKEY_ID);\n\tvoid A11yChanged();\n\tvoid AppearanceChanged();\n\tvoid AdvancedChanged();\n\tvoid AdvancedChangedRestart();\n\n\tvoid UpdateStreamDelayEstimate();\n\n\tvoid UpdateAutomaticReplayBufferCheckboxes();\n\n\tvoid AdvOutSplitFileChanged();\n\tvoid AdvOutRecCheckWarnings();\n\tvoid AdvOutRecCheckCodecs();\n\n\tvoid SimpleRecordingQualityChanged();\n\tvoid SimpleRecordingEncoderChanged();\n\tvoid SimpleRecordingQualityLosslessWarning(int idx);\n\n\tvoid SimpleReplayBufferChanged();\n\tvoid AdvReplayBufferChanged();\n\n\tvoid SimpleStreamingEncoderChanged();\n\n\tOBSService SpawnTempService();\n\n\tvoid SetGeneralIcon(const QIcon &icon);\n\tvoid SetAppearanceIcon(const QIcon &icon);\n\tvoid SetStreamIcon(const QIcon &icon);\n\tvoid SetOutputIcon(const QIcon &icon);\n\tvoid SetAudioIcon(const QIcon &icon);\n\tvoid SetVideoIcon(const QIcon &icon);\n\tvoid SetHotkeysIcon(const QIcon &icon);\n\tvoid SetAccessibilityIcon(const QIcon &icon);\n\tvoid SetAdvancedIcon(const QIcon &icon);\n\n\tvoid UseStreamKeyAdvClicked();\n\n\tvoid SimpleStreamAudioEncoderChanged();\n\tvoid AdvAudioEncodersChanged();\n\nprotected:\n\tvirtual void closeEvent(QCloseEvent *event) override;\n\tvirtual void showEvent(QShowEvent *event) override;\n\tvoid reject() override;\n\npublic:\n\tOBSBasicSettings(QWidget *parent);\n\t~OBSBasicSettings();\n\n\tinline const QIcon &GetHotkeyConflictIcon() const { return hotkeyConflictIcon; }\n};\n"
  },
  {
    "path": "frontend/settings/OBSBasicSettings_A11y.cpp",
    "content": "#include \"OBSBasicSettings.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include <QColorDialog>\n\nenum ColorPreset {\n\tCOLOR_PRESET_DEFAULT,\n\tCOLOR_PRESET_COLOR_BLIND_1,\n\tCOLOR_PRESET_CUSTOM = 99,\n};\n\nstatic inline QColor color_from_int(long long val)\n{\n\treturn QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);\n}\n\nstatic inline long long color_to_int(QColor color)\n{\n\tauto shift = [&](unsigned val, int shift) {\n\t\treturn ((val & 0xff) << shift);\n\t};\n\n\treturn shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);\n}\n\nQColor OBSBasicSettings::GetColor(uint32_t colorVal, QString label)\n{\n\tQColorDialog::ColorDialogOptions options;\n\n#ifdef __linux__\n\t// TODO: Revisit hang on Ubuntu with native dialog\n\toptions |= QColorDialog::DontUseNativeDialog;\n#endif\n\n\tQColor color = color_from_int(colorVal);\n\n\treturn QColorDialog::getColor(color, this, label, options);\n}\n\nvoid OBSBasicSettings::LoadA11ySettings(bool presetChange)\n{\n\tconfig_t *config = App()->GetUserConfig();\n\n\tloading = true;\n\tif (!presetChange) {\n\t\tpreset = config_get_int(config, \"Accessibility\", \"ColorPreset\");\n\n\t\tbool block = ui->colorPreset->blockSignals(true);\n\t\tui->colorPreset->setCurrentIndex(std::min(preset, (uint32_t)ui->colorPreset->count() - 1));\n\t\tui->colorPreset->blockSignals(block);\n\n\t\tbool checked = config_get_bool(config, \"Accessibility\", \"OverrideColors\");\n\n\t\tui->colorsGroupBox->setChecked(checked);\n\t}\n\n\tif (preset == COLOR_PRESET_DEFAULT) {\n\t\tResetDefaultColors();\n\t\tSetDefaultColors();\n\t} else if (preset == COLOR_PRESET_COLOR_BLIND_1) {\n\t\tResetDefaultColors();\n\n\t\tmixerGreenActive = 0x742E94;\n\t\tmixerGreen = 0x4A1A60;\n\t\tmixerYellowActive = 0x3349F9;\n\t\tmixerYellow = 0x1F2C97;\n\t\tmixerRedActive = 0xBEAC63;\n\t\tmixerRed = 0x675B28;\n\n\t\tselectRed = 0x3349F9;\n\t\tselectGreen = 0xFF56C9;\n\t\tselectBlue = 0xB09B44;\n\n\t\tSetDefaultColors();\n\t} else if (preset == COLOR_PRESET_CUSTOM) {\n\t\tSetDefaultColors();\n\n\t\tselectRed = config_get_int(config, \"Accessibility\", \"SelectRed\");\n\t\tselectGreen = config_get_int(config, \"Accessibility\", \"SelectGreen\");\n\t\tselectBlue = config_get_int(config, \"Accessibility\", \"SelectBlue\");\n\n\t\tmixerGreen = config_get_int(config, \"Accessibility\", \"MixerGreen\");\n\t\tmixerYellow = config_get_int(config, \"Accessibility\", \"MixerYellow\");\n\t\tmixerRed = config_get_int(config, \"Accessibility\", \"MixerRed\");\n\n\t\tmixerGreenActive = config_get_int(config, \"Accessibility\", \"MixerGreenActive\");\n\t\tmixerYellowActive = config_get_int(config, \"Accessibility\", \"MixerYellowActive\");\n\t\tmixerRedActive = config_get_int(config, \"Accessibility\", \"MixerRedActive\");\n\t}\n\n\tUpdateA11yColors();\n\n\tloading = false;\n}\n\nvoid OBSBasicSettings::SaveA11ySettings()\n{\n\tconfig_t *config = App()->GetUserConfig();\n\n\tconfig_set_bool(config, \"Accessibility\", \"OverrideColors\", ui->colorsGroupBox->isChecked());\n\tconfig_set_int(config, \"Accessibility\", \"ColorPreset\", preset);\n\n\tconfig_set_int(config, \"Accessibility\", \"SelectRed\", selectRed);\n\tconfig_set_int(config, \"Accessibility\", \"SelectGreen\", selectGreen);\n\tconfig_set_int(config, \"Accessibility\", \"SelectBlue\", selectBlue);\n\tconfig_set_int(config, \"Accessibility\", \"MixerGreen\", mixerGreen);\n\tconfig_set_int(config, \"Accessibility\", \"MixerYellow\", mixerYellow);\n\tconfig_set_int(config, \"Accessibility\", \"MixerRed\", mixerRed);\n\tconfig_set_int(config, \"Accessibility\", \"MixerGreenActive\", mixerGreenActive);\n\tconfig_set_int(config, \"Accessibility\", \"MixerYellowActive\", mixerYellowActive);\n\tconfig_set_int(config, \"Accessibility\", \"MixerRedActive\", mixerRedActive);\n\n\temit main->userSettingChanged(\"Accessibility\", \"SettingsChanged\");\n}\n\nstatic void SetStyle(QLabel *label, uint32_t colorVal)\n{\n\tQColor color = color_from_int(colorVal);\n\tcolor.setAlpha(255);\n\tQPalette palette = QPalette(color);\n\tlabel->setFrameStyle(QFrame::Sunken | QFrame::Panel);\n\tlabel->setText(color.name(QColor::HexRgb));\n\tlabel->setPalette(palette);\n\tlabel->setStyleSheet(QString(\"background-color: %1; color: %2;\")\n\t\t\t\t     .arg(palette.color(QPalette::Window).name(QColor::HexRgb))\n\t\t\t\t     .arg(palette.color(QPalette::WindowText).name(QColor::HexRgb)));\n\tlabel->setAutoFillBackground(true);\n\tlabel->setAlignment(Qt::AlignCenter);\n}\n\nvoid OBSBasicSettings::UpdateA11yColors()\n{\n\tSetStyle(ui->color1, selectRed);\n\tSetStyle(ui->color2, selectGreen);\n\tSetStyle(ui->color3, selectBlue);\n\tSetStyle(ui->color4, mixerGreen);\n\tSetStyle(ui->color5, mixerYellow);\n\tSetStyle(ui->color6, mixerRed);\n\tSetStyle(ui->color7, mixerGreenActive);\n\tSetStyle(ui->color8, mixerYellowActive);\n\tSetStyle(ui->color9, mixerRedActive);\n}\n\nvoid OBSBasicSettings::SetDefaultColors()\n{\n\tconfig_t *config = App()->GetUserConfig();\n\tconfig_set_default_int(config, \"Accessibility\", \"SelectRed\", selectRed);\n\tconfig_set_default_int(config, \"Accessibility\", \"SelectGreen\", selectGreen);\n\tconfig_set_default_int(config, \"Accessibility\", \"SelectBlue\", selectBlue);\n\n\tconfig_set_default_int(config, \"Accessibility\", \"MixerGreen\", mixerGreen);\n\tconfig_set_default_int(config, \"Accessibility\", \"MixerYellow\", mixerYellow);\n\tconfig_set_default_int(config, \"Accessibility\", \"MixerRed\", mixerRed);\n\n\tconfig_set_default_int(config, \"Accessibility\", \"MixerGreenActive\", mixerGreenActive);\n\tconfig_set_default_int(config, \"Accessibility\", \"MixerYellowActive\", mixerYellowActive);\n\tconfig_set_default_int(config, \"Accessibility\", \"MixerRedActive\", mixerRedActive);\n}\n\nvoid OBSBasicSettings::ResetDefaultColors()\n{\n\tselectRed = 0x0000FF;\n\tselectGreen = 0x00FF00;\n\tselectBlue = 0xFF7F00;\n\tmixerGreen = 0x267f26;\n\tmixerYellow = 0x267f7f;\n\tmixerRed = 0x26267f;\n\tmixerGreenActive = 0x4cff4c;\n\tmixerYellowActive = 0x4cffff;\n\tmixerRedActive = 0x4c4cff;\n}\n\nvoid OBSBasicSettings::on_colorPreset_currentIndexChanged(int idx)\n{\n\tpreset = idx == ui->colorPreset->count() - 1 ? COLOR_PRESET_CUSTOM : idx;\n\tLoadA11ySettings(true);\n}\n\nvoid OBSBasicSettings::on_choose1_clicked()\n{\n\tQColor color = GetColor(selectRed, QTStr(\"Basic.Settings.Accessibility.ColorOverrides.SelectRed\"));\n\n\tif (!color.isValid())\n\t\treturn;\n\n\tselectRed = color_to_int(color);\n\n\tpreset = COLOR_PRESET_CUSTOM;\n\tbool block = ui->colorPreset->blockSignals(true);\n\tui->colorPreset->setCurrentIndex(ui->colorPreset->count() - 1);\n\tui->colorPreset->blockSignals(block);\n\n\tA11yChanged();\n\n\tUpdateA11yColors();\n}\n\nvoid OBSBasicSettings::on_choose2_clicked()\n{\n\tQColor color = GetColor(selectGreen, QTStr(\"Basic.Settings.Accessibility.ColorOverrides.SelectGreen\"));\n\n\tif (!color.isValid())\n\t\treturn;\n\n\tselectGreen = color_to_int(color);\n\n\tpreset = COLOR_PRESET_CUSTOM;\n\tbool block = ui->colorPreset->blockSignals(true);\n\tui->colorPreset->setCurrentIndex(ui->colorPreset->count() - 1);\n\tui->colorPreset->blockSignals(block);\n\n\tA11yChanged();\n\n\tUpdateA11yColors();\n}\n\nvoid OBSBasicSettings::on_choose3_clicked()\n{\n\tQColor color = GetColor(selectBlue, QTStr(\"Basic.Settings.Accessibility.ColorOverrides.SelectBlue\"));\n\n\tif (!color.isValid())\n\t\treturn;\n\n\tselectBlue = color_to_int(color);\n\n\tpreset = COLOR_PRESET_CUSTOM;\n\tbool block = ui->colorPreset->blockSignals(true);\n\tui->colorPreset->setCurrentIndex(ui->colorPreset->count() - 1);\n\tui->colorPreset->blockSignals(block);\n\n\tA11yChanged();\n\n\tUpdateA11yColors();\n}\n\nvoid OBSBasicSettings::on_choose4_clicked()\n{\n\tQColor color = GetColor(mixerGreen, QTStr(\"Basic.Settings.Accessibility.ColorOverrides.MixerGreen\"));\n\n\tif (!color.isValid())\n\t\treturn;\n\n\tmixerGreen = color_to_int(color);\n\n\tpreset = COLOR_PRESET_CUSTOM;\n\tbool block = ui->colorPreset->blockSignals(true);\n\tui->colorPreset->setCurrentIndex(ui->colorPreset->count() - 1);\n\tui->colorPreset->blockSignals(block);\n\n\tA11yChanged();\n\n\tUpdateA11yColors();\n}\n\nvoid OBSBasicSettings::on_choose5_clicked()\n{\n\tQColor color = GetColor(mixerYellow, QTStr(\"Basic.Settings.Accessibility.ColorOverrides.MixerYellow\"));\n\n\tif (!color.isValid())\n\t\treturn;\n\n\tmixerYellow = color_to_int(color);\n\n\tpreset = COLOR_PRESET_CUSTOM;\n\tbool block = ui->colorPreset->blockSignals(true);\n\tui->colorPreset->setCurrentIndex(ui->colorPreset->count() - 1);\n\tui->colorPreset->blockSignals(block);\n\n\tA11yChanged();\n\n\tUpdateA11yColors();\n}\n\nvoid OBSBasicSettings::on_choose6_clicked()\n{\n\tQColor color = GetColor(mixerRed, QTStr(\"Basic.Settings.Accessibility.ColorOverrides.MixerRed\"));\n\n\tif (!color.isValid())\n\t\treturn;\n\n\tmixerRed = color_to_int(color);\n\n\tpreset = COLOR_PRESET_CUSTOM;\n\tbool block = ui->colorPreset->blockSignals(true);\n\tui->colorPreset->setCurrentIndex(ui->colorPreset->count() - 1);\n\tui->colorPreset->blockSignals(block);\n\n\tA11yChanged();\n\n\tUpdateA11yColors();\n}\n\nvoid OBSBasicSettings::on_choose7_clicked()\n{\n\tQColor color =\n\t\tGetColor(mixerGreenActive, QTStr(\"Basic.Settings.Accessibility.ColorOverrides.MixerGreenActive\"));\n\n\tif (!color.isValid())\n\t\treturn;\n\n\tmixerGreenActive = color_to_int(color);\n\n\tpreset = COLOR_PRESET_CUSTOM;\n\tbool block = ui->colorPreset->blockSignals(true);\n\tui->colorPreset->setCurrentIndex(ui->colorPreset->count() - 1);\n\tui->colorPreset->blockSignals(block);\n\n\tA11yChanged();\n\n\tUpdateA11yColors();\n}\n\nvoid OBSBasicSettings::on_choose8_clicked()\n{\n\tQColor color =\n\t\tGetColor(mixerYellowActive, QTStr(\"Basic.Settings.Accessibility.ColorOverrides.MixerYellowActive\"));\n\n\tif (!color.isValid())\n\t\treturn;\n\n\tmixerYellowActive = color_to_int(color);\n\n\tpreset = COLOR_PRESET_CUSTOM;\n\tbool block = ui->colorPreset->blockSignals(true);\n\tui->colorPreset->setCurrentIndex(ui->colorPreset->count() - 1);\n\tui->colorPreset->blockSignals(block);\n\n\tA11yChanged();\n\n\tUpdateA11yColors();\n}\n\nvoid OBSBasicSettings::on_choose9_clicked()\n{\n\tQColor color = GetColor(mixerRedActive, QTStr(\"Basic.Settings.Accessibility.ColorOverrides.MixerRedActive\"));\n\n\tif (!color.isValid())\n\t\treturn;\n\n\tmixerRedActive = color_to_int(color);\n\n\tpreset = COLOR_PRESET_CUSTOM;\n\tbool block = ui->colorPreset->blockSignals(true);\n\tui->colorPreset->setCurrentIndex(ui->colorPreset->count() - 1);\n\tui->colorPreset->blockSignals(block);\n\n\tA11yChanged();\n\n\tUpdateA11yColors();\n}\n"
  },
  {
    "path": "frontend/settings/OBSBasicSettings_Appearance.cpp",
    "content": "#include \"OBSBasicSettings.hpp\"\n\n#include <OBSApp.hpp>\n#include <utility/platform.hpp>\n\n#include <qt-wrappers.hpp>\n\nvoid OBSBasicSettings::InitAppearancePage()\n{\n\tsavedTheme = App()->GetTheme();\n\tconst QString currentBaseTheme = savedTheme->isBaseTheme ? savedTheme->id : savedTheme->parent;\n\n\tfor (const OBSTheme &theme : App()->GetThemes()) {\n\t\tif (theme.isBaseTheme && (HighContrastEnabled() || theme.isVisible || theme.id == currentBaseTheme)) {\n\t\t\tui->theme->addItem(theme.name, theme.id);\n\t\t}\n\t}\n\n\tint idx = ui->theme->findData(currentBaseTheme);\n\tif (idx != -1)\n\t\tui->theme->setCurrentIndex(idx);\n\n\tui->themeVariant->setPlaceholderText(QTStr(\"Basic.Settings.Appearance.General.NoVariant\"));\n\n\tui->appearanceFontScale->setDisplayTicks(true);\n\n\tconnect(ui->appearanceFontScale, &QSlider::valueChanged, ui->appearanceFontScaleText,\n\t\t[this](int value) { ui->appearanceFontScaleText->setText(QString::number(value)); });\n\tui->appearanceFontScaleText->setText(QString::number(ui->appearanceFontScale->value()));\n\n\tconnect(App(), &OBSApp::StyleChanged, this, &OBSBasicSettings::updateAppearanceControls);\n\tupdateAppearanceControls();\n}\n\nvoid OBSBasicSettings::LoadThemeList(bool reload)\n{\n\tProfileScope(\"OBSBasicSettings::LoadThemeList\");\n\n\tconst OBSTheme *currentTheme = App()->GetTheme();\n\tconst QString currentBaseTheme = currentTheme->isBaseTheme ? currentTheme->id : currentTheme->parent;\n\n\t/* Nothing to do if current and last base theme were the same */\n\tconst QString baseThemeId = ui->theme->currentData().toString();\n\tif (reload && baseThemeId == currentBaseTheme)\n\t\treturn;\n\n\tui->themeVariant->blockSignals(true);\n\tui->themeVariant->clear();\n\n\tauto themes = App()->GetThemes();\n\tstd::sort(themes.begin(), themes.end(), [](const OBSTheme &a, const OBSTheme &b) -> bool {\n\t\treturn QString::compare(a.name, b.name, Qt::CaseInsensitive) < 0;\n\t});\n\n\tQString defaultVariant;\n\tconst OBSTheme *baseTheme = App()->GetTheme(baseThemeId);\n\n\tfor (const OBSTheme &theme : themes) {\n\t\t/* Skip non-visible themes */\n\t\tif (!theme.isVisible || theme.isHighContrast)\n\t\t\tcontinue;\n\t\t/* Skip non-child themes */\n\t\tif (theme.isBaseTheme || theme.parent != baseThemeId)\n\t\t\tcontinue;\n\n\t\tui->themeVariant->addItem(theme.name, theme.id);\n\t\tif (baseTheme && theme.filename == baseTheme->filename)\n\t\t\tdefaultVariant = theme.id;\n\t}\n\n\tint idx = ui->themeVariant->findData(currentTheme->id);\n\tif (idx != -1)\n\t\tui->themeVariant->setCurrentIndex(idx);\n\n\tui->themeVariant->setEnabled(ui->themeVariant->count() > 0);\n\tui->themeVariant->blockSignals(false);\n\t/* If no variant is selected but variants are available set the first one. */\n\tif (idx == -1 && ui->themeVariant->count() > 0) {\n\t\tidx = ui->themeVariant->findData(defaultVariant);\n\t\tui->themeVariant->setCurrentIndex(idx != -1 ? idx : 0);\n\t}\n}\n\nvoid OBSBasicSettings::LoadAppearanceSettings(bool reload)\n{\n\tloading = true;\n\n\tLoadThemeList(reload);\n\n\tif (reload) {\n\t\tQString themeId = ui->theme->currentData().toString();\n\t\tif (ui->themeVariant->currentIndex() != -1)\n\t\t\tthemeId = ui->themeVariant->currentData().toString();\n\n\t\tApp()->SetTheme(themeId);\n\t}\n\n\tint fontScale = config_get_int(App()->GetUserConfig(), \"Appearance\", \"FontScale\");\n\tui->appearanceFontScale->setValue(fontScale);\n\n\tint densityId = config_get_int(App()->GetUserConfig(), \"Appearance\", \"Density\");\n\tQAbstractButton *densityButton = ui->appearanceDensityButtonGroup->button(densityId);\n\tif (densityButton) {\n\t\tdensityButton->setChecked(true);\n\t}\n\tupdateAppearanceControls();\n\n\tloading = false;\n}\n\nvoid OBSBasicSettings::SaveAppearanceSettings()\n{\n\tconfig_t *config = App()->GetUserConfig();\n\n\tOBSTheme *currentTheme = App()->GetTheme();\n\tif (savedTheme != currentTheme) {\n\t\tconfig_set_string(config, \"Appearance\", \"Theme\", QT_TO_UTF8(currentTheme->id));\n\t}\n\n\tconfig_set_int(config, \"Appearance\", \"FontScale\", ui->appearanceFontScale->value());\n\n\tint densityId = ui->appearanceDensityButtonGroup->checkedId();\n\tconfig_set_int(config, \"Appearance\", \"Density\", densityId);\n\n\tApp()->SetTheme(currentTheme->id);\n}\n\nvoid OBSBasicSettings::on_theme_activated(int)\n{\n\tLoadAppearanceSettings(true);\n}\n\nvoid OBSBasicSettings::on_themeVariant_activated(int)\n{\n\tLoadAppearanceSettings(true);\n}\n\nvoid OBSBasicSettings::updateAppearanceControls()\n{\n\tOBSTheme *theme = App()->GetTheme();\n\tenableAppearanceFontControls(theme->usesFontScale);\n\tenableAppearanceDensityControls(theme->usesDensity);\n\tif (!theme->usesFontScale || !theme->usesDensity) {\n\t\tui->appearanceOptionsWarning->setVisible(true);\n\t} else {\n\t\tui->appearanceOptionsWarning->setVisible(false);\n\t}\n\tstyle()->polish(ui->appearanceOptionsWarningLabel);\n}\n\nvoid OBSBasicSettings::enableAppearanceFontControls(bool enable)\n{\n\tui->appearanceFontScale->setEnabled(enable);\n\tui->appearanceFontScaleText->setEnabled(enable);\n}\n\nvoid OBSBasicSettings::enableAppearanceDensityControls(bool enable)\n{\n\tconst QList<QAbstractButton *> buttons = ui->appearanceDensityButtonGroup->buttons();\n\tfor (QAbstractButton *button : buttons) {\n\t\tbutton->setEnabled(enable);\n\t}\n}\n"
  },
  {
    "path": "frontend/settings/OBSBasicSettings_Stream.cpp",
    "content": "#include \"OBSBasicSettings.hpp\"\n\n#ifdef YOUTUBE_ENABLED\n#include <docks/YouTubeAppDock.hpp>\n#endif\n#include <oauth/OAuth.hpp>\n#ifdef YOUTUBE_ENABLED\n#include <utility/YoutubeApiWrappers.hpp>\n#endif\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QUuid>\n\nstatic const QUuid &CustomServerUUID()\n{\n\tstatic const QUuid uuid = QUuid::fromString(QT_UTF8(\"{241da255-70f2-4bbb-bef7-509695bf8e65}\"));\n\treturn uuid;\n}\n\nstruct QCef;\nstruct QCefCookieManager;\n\nextern QCef *cef;\nextern QCefCookieManager *panel_cookies;\nextern bool cef_js_avail;\n\nenum class ListOpt : int {\n\tShowAll = 1,\n\tCustom,\n\tWHIP,\n};\n\nenum class Section : int {\n\tConnect,\n\tStreamKey,\n};\n\nbool OBSBasicSettings::IsCustomService() const\n{\n\treturn ui->service->currentData().toInt() == (int)ListOpt::Custom;\n}\n\ninline bool OBSBasicSettings::IsWHIP() const\n{\n\treturn ui->service->currentData().toInt() == (int)ListOpt::WHIP;\n}\n\nvoid OBSBasicSettings::InitStreamPage()\n{\n\tui->connectAccount2->setVisible(false);\n\tui->disconnectAccount->setVisible(false);\n\tui->bandwidthTestEnable->setVisible(false);\n\n\tui->twitchAddonDropdown->setVisible(false);\n\tui->twitchAddonLabel->setVisible(false);\n\n\tui->connectedAccountLabel->setVisible(false);\n\tui->connectedAccountText->setVisible(false);\n\n\tint vertSpacing = ui->topStreamLayout->verticalSpacing();\n\n\tQMargins m = ui->topStreamLayout->contentsMargins();\n\tm.setBottom(vertSpacing / 2);\n\tui->topStreamLayout->setContentsMargins(m);\n\n\tm = ui->loginPageLayout->contentsMargins();\n\tm.setTop(vertSpacing / 2);\n\tui->loginPageLayout->setContentsMargins(m);\n\n\tm = ui->streamkeyPageLayout->contentsMargins();\n\tm.setTop(vertSpacing / 2);\n\tui->streamkeyPageLayout->setContentsMargins(m);\n\n\tLoadServices(false);\n\n\tui->twitchAddonDropdown->addItem(QTStr(\"Basic.Settings.Stream.TTVAddon.None\"));\n\tui->twitchAddonDropdown->addItem(QTStr(\"Basic.Settings.Stream.TTVAddon.BTTV\"));\n\tui->twitchAddonDropdown->addItem(QTStr(\"Basic.Settings.Stream.TTVAddon.FFZ\"));\n\tui->twitchAddonDropdown->addItem(QTStr(\"Basic.Settings.Stream.TTVAddon.Both\"));\n\n\tconnect(ui->ignoreRecommended, &QCheckBox::clicked, this, &OBSBasicSettings::DisplayEnforceWarning);\n\tconnect(ui->ignoreRecommended, &QCheckBox::toggled, this, &OBSBasicSettings::UpdateResFPSLimits);\n\n\tconnect(ui->enableMultitrackVideo, &QCheckBox::toggled, this, &OBSBasicSettings::UpdateMultitrackVideo);\n\tconnect(ui->multitrackVideoMaximumAggregateBitrateAuto, &QCheckBox::toggled, this,\n\t\t&OBSBasicSettings::UpdateMultitrackVideo);\n\tconnect(ui->multitrackVideoMaximumVideoTracksAuto, &QCheckBox::toggled, this,\n\t\t&OBSBasicSettings::UpdateMultitrackVideo);\n\tconnect(ui->multitrackVideoConfigOverrideEnable, &QCheckBox::toggled, this,\n\t\t&OBSBasicSettings::UpdateMultitrackVideo);\n}\n\nvoid OBSBasicSettings::LoadStream1Settings()\n{\n\tbool ignoreRecommended = config_get_bool(main->Config(), \"Stream1\", \"IgnoreRecommended\");\n\tint whipSimulcastTotalLayers = config_get_int(main->Config(), \"Stream1\", \"WHIPSimulcastTotalLayers\");\n\n\tobs_service_t *service_obj = main->GetService();\n\tconst char *type = obs_service_get_type(service_obj);\n\tbool is_rtmp_custom = (strcmp(type, \"rtmp_custom\") == 0);\n\tbool is_rtmp_common = (strcmp(type, \"rtmp_common\") == 0);\n\tbool is_whip = (strcmp(type, \"whip_custom\") == 0);\n\n\tloading = true;\n\n\tOBSDataAutoRelease settings = obs_service_get_settings(service_obj);\n\n\tconst char *service = obs_data_get_string(settings, \"service\");\n\tconst char *server = obs_data_get_string(settings, \"server\");\n\tconst char *key = obs_data_get_string(settings, \"key\");\n\tbool use_custom_server = obs_data_get_bool(settings, \"using_custom_server\");\n\tprotocol = QT_UTF8(obs_service_get_protocol(service_obj));\n\tconst char *bearer_token = obs_data_get_string(settings, \"bearer_token\");\n\n\tif (is_rtmp_custom || is_whip)\n\t\tui->customServer->setText(server);\n\n\tif (is_rtmp_custom) {\n\t\tui->service->setCurrentIndex(0);\n\t\tlastServiceIdx = 0;\n\t\tlastCustomServer = ui->customServer->text();\n\n\t\tbool use_auth = obs_data_get_bool(settings, \"use_auth\");\n\t\tconst char *username = obs_data_get_string(settings, \"username\");\n\t\tconst char *password = obs_data_get_string(settings, \"password\");\n\t\tui->authUsername->setText(QT_UTF8(username));\n\t\tui->authPw->setText(QT_UTF8(password));\n\t\tui->useAuth->setChecked(use_auth);\n\t} else {\n\t\tint idx = ui->service->findText(service);\n\t\tif (idx == -1) {\n\t\t\tif (service && *service)\n\t\t\t\tui->service->insertItem(1, service);\n\t\t\tidx = 1;\n\t\t}\n\t\tui->service->setCurrentIndex(idx);\n\t\tlastServiceIdx = idx;\n\n\t\tbool bw_test = obs_data_get_bool(settings, \"bwtest\");\n\t\tui->bandwidthTestEnable->setChecked(bw_test);\n\n\t\tidx = config_get_int(main->Config(), \"Twitch\", \"AddonChoice\");\n\t\tui->twitchAddonDropdown->setCurrentIndex(idx);\n\t}\n\n\tui->enableMultitrackVideo->setChecked(config_get_bool(main->Config(), \"Stream1\", \"EnableMultitrackVideo\"));\n\n\tui->multitrackVideoMaximumAggregateBitrateAuto->setChecked(\n\t\tconfig_get_bool(main->Config(), \"Stream1\", \"MultitrackVideoMaximumAggregateBitrateAuto\"));\n\tif (config_has_user_value(main->Config(), \"Stream1\", \"MultitrackVideoMaximumAggregateBitrate\")) {\n\t\tui->multitrackVideoMaximumAggregateBitrate->setValue(\n\t\t\tconfig_get_int(main->Config(), \"Stream1\", \"MultitrackVideoMaximumAggregateBitrate\"));\n\t}\n\n\tui->multitrackVideoMaximumVideoTracksAuto->setChecked(\n\t\tconfig_get_bool(main->Config(), \"Stream1\", \"MultitrackVideoMaximumVideoTracksAuto\"));\n\tif (config_has_user_value(main->Config(), \"Stream1\", \"MultitrackVideoMaximumVideoTracks\"))\n\t\tui->multitrackVideoMaximumVideoTracks->setValue(\n\t\t\tconfig_get_int(main->Config(), \"Stream1\", \"MultitrackVideoMaximumVideoTracks\"));\n\n\tui->multitrackVideoStreamDumpEnable->setChecked(\n\t\tconfig_get_bool(main->Config(), \"Stream1\", \"MultitrackVideoStreamDumpEnabled\"));\n\n\tui->multitrackVideoConfigOverrideEnable->setChecked(\n\t\tconfig_get_bool(main->Config(), \"Stream1\", \"MultitrackVideoConfigOverrideEnabled\"));\n\tif (config_has_user_value(main->Config(), \"Stream1\", \"MultitrackVideoConfigOverride\"))\n\t\tui->multitrackVideoConfigOverride->setPlainText(\n\t\t\tDeserializeConfigText(\n\t\t\t\tconfig_get_string(main->Config(), \"Stream1\", \"MultitrackVideoConfigOverride\"))\n\t\t\t\t.c_str());\n\n\tui->multitrackVideoAdditionalCanvas->clear();\n\tui->multitrackVideoAdditionalCanvas->addItem(QTStr(\"None\"));\n\tfor (const auto &canvas : main->GetCanvases()) {\n\t\tif (obs_canvas_get_flags(canvas) & EPHEMERAL)\n\t\t\tcontinue;\n\n\t\tui->multitrackVideoAdditionalCanvas->addItem(obs_canvas_get_name(canvas), obs_canvas_get_uuid(canvas));\n\t}\n\n\tif (config_has_user_value(main->Config(), \"Stream1\", \"MultitrackExtraCanvas\")) {\n\t\t/* Currently we only support one canvas, so the value will just be one UUID. */\n\t\tconst std::string_view uuid = config_get_string(main->Config(), \"Stream1\", \"MultitrackExtraCanvas\");\n\t\tif (!uuid.empty()) {\n\t\t\tint idx = ui->multitrackVideoAdditionalCanvas->findData(uuid.data());\n\t\t\tui->multitrackVideoAdditionalCanvas->setCurrentIndex(idx);\n\t\t}\n\t}\n\n\tUpdateServerList();\n\n\tif (is_rtmp_common) {\n\t\tint idx = -1;\n\t\tif (use_custom_server) {\n\t\t\tidx = ui->server->findData(CustomServerUUID());\n\t\t} else {\n\t\t\tidx = ui->server->findData(QString::fromUtf8(server));\n\t\t}\n\n\t\tif (idx == -1) {\n\t\t\tif (server && *server)\n\t\t\t\tui->server->insertItem(0, server, server);\n\t\t\tidx = 0;\n\t\t}\n\t\tui->server->setCurrentIndex(idx);\n\t}\n\n\tif (use_custom_server)\n\t\tui->serviceCustomServer->setText(server);\n\n\tif (is_whip) {\n\t\tui->key->setText(bearer_token);\n\t\tui->whipSimulcastGroupBox->show();\n\t} else {\n\t\tui->key->setText(key);\n\t\tui->whipSimulcastGroupBox->hide();\n\t}\n\n\tServiceChanged(true);\n\n\tUpdateKeyLink();\n\tUpdateMoreInfoLink();\n\tUpdateVodTrackSetting();\n\tUpdateServiceRecommendations();\n\tUpdateMultitrackVideo();\n\n\tbool streamActive = obs_frontend_streaming_active();\n\tui->streamPage->setEnabled(!streamActive);\n\n\tui->ignoreRecommended->setChecked(ignoreRecommended);\n\tui->whipSimulcastTotalLayers->setValue(whipSimulcastTotalLayers);\n\n\tloading = false;\n\n\tQMetaObject::invokeMethod(this, \"UpdateResFPSLimits\", Qt::QueuedConnection);\n}\n\n#define SRT_PROTOCOL \"srt\"\n#define RIST_PROTOCOL \"rist\"\n\nbool OBSBasicSettings::AllowsMultiTrack(const char *protocol)\n{\n\treturn astrcmpi_n(protocol, SRT_PROTOCOL, strlen(SRT_PROTOCOL)) == 0 ||\n\t       astrcmpi_n(protocol, RIST_PROTOCOL, strlen(RIST_PROTOCOL)) == 0;\n}\n\nvoid OBSBasicSettings::SwapMultiTrack(const char *protocol)\n{\n\tif (protocol) {\n\t\tif (AllowsMultiTrack(protocol)) {\n\t\t\tui->advStreamTrackWidget->setCurrentWidget(ui->streamMultiTracks);\n\t\t} else {\n\t\t\tui->advStreamTrackWidget->setCurrentWidget(ui->streamSingleTracks);\n\t\t}\n\t}\n}\n\nvoid OBSBasicSettings::SaveStream1Settings()\n{\n\tbool customServer = IsCustomService();\n\tbool whip = IsWHIP();\n\tconst char *service_id = \"rtmp_common\";\n\n\tif (customServer) {\n\t\tservice_id = \"rtmp_custom\";\n\t} else if (whip) {\n\t\tservice_id = \"whip_custom\";\n\t}\n\n\tobs_service_t *oldService = main->GetService();\n\tOBSDataAutoRelease hotkeyData = obs_hotkeys_save_service(oldService);\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tif (!customServer && !whip) {\n\t\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(ui->service->currentText()));\n\t\tobs_data_set_string(settings, \"protocol\", QT_TO_UTF8(protocol));\n\t\tif (ui->server->currentData() == CustomServerUUID()) {\n\t\t\tobs_data_set_bool(settings, \"using_custom_server\", true);\n\n\t\t\tobs_data_set_string(settings, \"server\", QT_TO_UTF8(ui->serviceCustomServer->text()));\n\t\t} else {\n\t\t\tobs_data_set_string(settings, \"server\", QT_TO_UTF8(ui->server->currentData().toString()));\n\t\t}\n\t} else {\n\t\tobs_data_set_string(settings, \"server\", QT_TO_UTF8(ui->customServer->text().trimmed()));\n\t\tobs_data_set_bool(settings, \"use_auth\", ui->useAuth->isChecked());\n\t\tif (ui->useAuth->isChecked()) {\n\t\t\tobs_data_set_string(settings, \"username\", QT_TO_UTF8(ui->authUsername->text()));\n\t\t\tobs_data_set_string(settings, \"password\", QT_TO_UTF8(ui->authPw->text()));\n\t\t}\n\t}\n\n\tif (!!auth && strcmp(auth->service(), \"Twitch\") == 0) {\n\t\tbool choiceExists = config_has_user_value(main->Config(), \"Twitch\", \"AddonChoice\");\n\t\tint currentChoice = config_get_int(main->Config(), \"Twitch\", \"AddonChoice\");\n\t\tint newChoice = ui->twitchAddonDropdown->currentIndex();\n\n\t\tconfig_set_int(main->Config(), \"Twitch\", \"AddonChoice\", newChoice);\n\n\t\tif (choiceExists && currentChoice != newChoice)\n\t\t\tforceAuthReload = true;\n\n\t\tobs_data_set_bool(settings, \"bwtest\", ui->bandwidthTestEnable->isChecked());\n\t} else {\n\t\tobs_data_set_bool(settings, \"bwtest\", false);\n\t}\n\n\tif (whip) {\n\t\tobs_data_set_string(settings, \"service\", \"WHIP\");\n\t\tobs_data_set_string(settings, \"bearer_token\", QT_TO_UTF8(ui->key->text()));\n\t} else {\n\t\tobs_data_set_string(settings, \"key\", QT_TO_UTF8(ui->key->text()));\n\t}\n\n\tOBSServiceAutoRelease newService = obs_service_create(service_id, \"default_service\", settings, hotkeyData);\n\n\tif (!newService)\n\t\treturn;\n\n\tmain->SetService(newService);\n\tmain->SaveService();\n\tmain->auth = auth;\n\tif (!!main->auth) {\n\t\tmain->auth->LoadUI();\n\t\tmain->SetBroadcastFlowEnabled(main->auth->broadcastFlow());\n\t} else {\n\t\tmain->SetBroadcastFlowEnabled(false);\n\t}\n\n\tSaveCheckBox(ui->ignoreRecommended, \"Stream1\", \"IgnoreRecommended\");\n\n\tauto oldWHIPSimulcastTotalLayers = config_get_int(main->Config(), \"Stream1\", \"WHIPSimulcastTotalLayers\");\n\tSaveSpinBox(ui->whipSimulcastTotalLayers, \"Stream1\", \"WHIPSimulcastTotalLayers\");\n\n\tauto oldMultitrackVideoSetting = config_get_bool(main->Config(), \"Stream1\", \"EnableMultitrackVideo\");\n\n\tif (!IsCustomService()) {\n\t\tOBSDataAutoRelease settings = obs_data_create();\n\t\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(ui->service->currentText()));\n\t\tOBSServiceAutoRelease temp_service =\n\t\t\tobs_service_create_private(\"rtmp_common\", \"auto config query service\", settings);\n\t\tsettings = obs_service_get_settings(temp_service);\n\t\tauto available = obs_data_has_user_value(settings, \"multitrack_video_configuration_url\");\n\n\t\tif (available) {\n\t\t\tSaveCheckBox(ui->enableMultitrackVideo, \"Stream1\", \"EnableMultitrackVideo\");\n\t\t} else {\n\t\t\tconfig_remove_value(main->Config(), \"Stream1\", \"EnableMultitrackVideo\");\n\t\t}\n\t} else {\n\t\tSaveCheckBox(ui->enableMultitrackVideo, \"Stream1\", \"EnableMultitrackVideo\");\n\t}\n\tSaveCheckBox(ui->multitrackVideoMaximumAggregateBitrateAuto, \"Stream1\",\n\t\t     \"MultitrackVideoMaximumAggregateBitrateAuto\");\n\tSaveSpinBox(ui->multitrackVideoMaximumAggregateBitrate, \"Stream1\", \"MultitrackVideoMaximumAggregateBitrate\");\n\tSaveCheckBox(ui->multitrackVideoMaximumVideoTracksAuto, \"Stream1\", \"MultitrackVideoMaximumVideoTracksAuto\");\n\tSaveSpinBox(ui->multitrackVideoMaximumVideoTracks, \"Stream1\", \"MultitrackVideoMaximumVideoTracks\");\n\tSaveCheckBox(ui->multitrackVideoStreamDumpEnable, \"Stream1\", \"MultitrackVideoStreamDumpEnabled\");\n\tSaveCheckBox(ui->multitrackVideoConfigOverrideEnable, \"Stream1\", \"MultitrackVideoConfigOverrideEnabled\");\n\tSaveText(ui->multitrackVideoConfigOverride, \"Stream1\", \"MultitrackVideoConfigOverride\");\n\tSaveComboData(ui->multitrackVideoAdditionalCanvas, \"Stream1\", \"MultitrackExtraCanvas\");\n\n\tif (oldMultitrackVideoSetting != ui->enableMultitrackVideo->isChecked() ||\n\t    oldWHIPSimulcastTotalLayers != ui->whipSimulcastTotalLayers->value())\n\t\tmain->ResetOutputs();\n\n\tSwapMultiTrack(QT_TO_UTF8(protocol));\n}\n\nvoid OBSBasicSettings::UpdateMoreInfoLink()\n{\n\tif (IsCustomService() || IsWHIP()) {\n\t\tui->moreInfoButton->hide();\n\t\treturn;\n\t}\n\n\tQString serviceName = ui->service->currentText();\n\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\tobs_property_t *services = obs_properties_get(props, \"service\");\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(serviceName));\n\tobs_property_modified(services, settings);\n\n\tconst char *more_info_link = obs_data_get_string(settings, \"more_info_link\");\n\n\tif (!more_info_link || (*more_info_link == '\\0')) {\n\t\tui->moreInfoButton->hide();\n\t} else {\n\t\tui->moreInfoButton->setTargetUrl(QUrl(more_info_link));\n\t\tui->moreInfoButton->show();\n\t}\n\tobs_properties_destroy(props);\n}\n\nvoid OBSBasicSettings::UpdateKeyLink()\n{\n\tQString serviceName = ui->service->currentText();\n\tQString customServer = ui->customServer->text().trimmed();\n\tQString streamKeyLink;\n\n\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\tobs_property_t *services = obs_properties_get(props, \"service\");\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(serviceName));\n\tobs_property_modified(services, settings);\n\n\tstreamKeyLink = obs_data_get_string(settings, \"stream_key_link\");\n\n\tif (customServer.contains(\"fbcdn.net\") && IsCustomService()) {\n\t\tstreamKeyLink = \"https://www.facebook.com/live/producer?ref=OBS\";\n\t}\n\n\tif (serviceName == \"Dacast\") {\n\t\tui->streamKeyLabel->setText(QTStr(\"Basic.AutoConfig.StreamPage.EncoderKey\"));\n\t\tui->streamKeyLabel->setToolTip(\"\");\n\t} else if (IsWHIP()) {\n\t\tui->streamKeyLabel->setText(QTStr(\"Basic.AutoConfig.StreamPage.BearerToken\"));\n\t\tui->streamKeyLabel->setToolTip(\"\");\n\t} else if (!IsCustomService()) {\n\t\tui->streamKeyLabel->setText(QTStr(\"Basic.AutoConfig.StreamPage.StreamKey\"));\n\t\tui->streamKeyLabel->setToolTip(\"\");\n\t} else {\n\t\t/* add tooltips for stream key, user, password fields */\n\t\tQString file = !App()->IsThemeDark() ? \":/res/images/help.svg\" : \":/res/images/help_light.svg\";\n\t\tQString lStr = \"<html>%1 <img src='%2' style=' \\\n\t\t\t\tvertical-align: bottom;  \\\n\t\t\t\t' /></html>\";\n\n\t\tui->streamKeyLabel->setText(lStr.arg(QTStr(\"Basic.AutoConfig.StreamPage.StreamKey\"), file));\n\t\tui->streamKeyLabel->setToolTip(QTStr(\"Basic.AutoConfig.StreamPage.StreamKey.ToolTip\"));\n\n\t\tui->authUsernameLabel->setText(lStr.arg(QTStr(\"Basic.Settings.Stream.Custom.Username\"), file));\n\t\tui->authUsernameLabel->setToolTip(QTStr(\"Basic.Settings.Stream.Custom.Username.ToolTip\"));\n\n\t\tui->authPwLabel->setText(lStr.arg(QTStr(\"Basic.Settings.Stream.Custom.Password\"), file));\n\t\tui->authPwLabel->setToolTip(QTStr(\"Basic.Settings.Stream.Custom.Password.ToolTip\"));\n\t}\n\n\tif (QString(streamKeyLink).isNull() || QString(streamKeyLink).isEmpty()) {\n\t\tui->getStreamKeyButton->hide();\n\t} else {\n\t\tui->getStreamKeyButton->setTargetUrl(QUrl(streamKeyLink));\n\t\tui->getStreamKeyButton->show();\n\t}\n\tobs_properties_destroy(props);\n}\n\nvoid OBSBasicSettings::LoadServices(bool showAll)\n{\n\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tobs_data_set_bool(settings, \"show_all\", showAll);\n\n\tobs_property_t *prop = obs_properties_get(props, \"show_all\");\n\tobs_property_modified(prop, settings);\n\n\tui->service->blockSignals(true);\n\tui->service->clear();\n\n\tQStringList names;\n\n\tobs_property_t *services = obs_properties_get(props, \"service\");\n\tsize_t services_count = obs_property_list_item_count(services);\n\tfor (size_t i = 0; i < services_count; i++) {\n\t\tconst char *name = obs_property_list_item_string(services, i);\n\t\tnames.push_back(name);\n\t}\n\n\tif (showAll)\n\t\tnames.sort(Qt::CaseInsensitive);\n\n\tfor (QString &name : names)\n\t\tui->service->addItem(name);\n\n\tif (obs_is_output_protocol_registered(\"WHIP\")) {\n\t\tui->service->addItem(QTStr(\"WHIP\"), QVariant((int)ListOpt::WHIP));\n\t}\n\n\tif (!showAll) {\n\t\tui->service->addItem(QTStr(\"Basic.AutoConfig.StreamPage.Service.ShowAll\"),\n\t\t\t\t     QVariant((int)ListOpt::ShowAll));\n\t}\n\n\tui->service->insertItem(0, QTStr(\"Basic.AutoConfig.StreamPage.Service.Custom\"), QVariant((int)ListOpt::Custom));\n\n\tif (!lastService.isEmpty()) {\n\t\tint idx = ui->service->findText(lastService);\n\t\tif (idx != -1)\n\t\t\tui->service->setCurrentIndex(idx);\n\t}\n\n\tobs_properties_destroy(props);\n\n\tui->service->blockSignals(false);\n}\n\nstatic inline bool is_auth_service(const std::string &service)\n{\n\treturn Auth::AuthType(service) != Auth::Type::None;\n}\n\nstatic inline bool is_external_oauth(const std::string &service)\n{\n\treturn Auth::External(service);\n}\n\nstatic void reset_service_ui_fields(Ui::OBSBasicSettings *ui, std::string &service, bool loading)\n{\n\tbool external_oauth = is_external_oauth(service);\n\tif (external_oauth) {\n\t\tui->streamKeyWidget->setVisible(false);\n\t\tui->streamKeyLabel->setVisible(false);\n\t\tui->connectAccount2->setVisible(true);\n\t\tui->useStreamKeyAdv->setVisible(true);\n\t\tui->streamStackWidget->setCurrentIndex((int)Section::StreamKey);\n\t} else if (cef) {\n\t\tQString key = ui->key->text();\n\t\tbool can_auth = is_auth_service(service);\n\t\tint page = can_auth && (!loading || key.isEmpty()) ? (int)Section::Connect : (int)Section::StreamKey;\n\n\t\tui->streamStackWidget->setCurrentIndex(page);\n\t\tui->streamKeyWidget->setVisible(true);\n\t\tui->streamKeyLabel->setVisible(true);\n\t\tui->connectAccount2->setVisible(can_auth);\n\t\tui->useStreamKeyAdv->setVisible(false);\n\t} else {\n\t\tui->connectAccount2->setVisible(false);\n\t\tui->useStreamKeyAdv->setVisible(false);\n\t\tui->streamStackWidget->setCurrentIndex((int)Section::StreamKey);\n\t}\n\n\tui->connectedAccountLabel->setVisible(false);\n\tui->connectedAccountText->setVisible(false);\n\tui->disconnectAccount->setVisible(false);\n}\n\n#ifdef YOUTUBE_ENABLED\nstatic void get_yt_ch_title(Ui::OBSBasicSettings *ui)\n{\n\tconst char *name = config_get_string(OBSBasic::Get()->Config(), \"YouTube\", \"ChannelName\");\n\tif (name) {\n\t\tui->connectedAccountText->setText(name);\n\t} else {\n\t\t// if we still not changed the service page\n\t\tif (IsYouTubeService(QT_TO_UTF8(ui->service->currentText()))) {\n\t\t\tui->connectedAccountText->setText(QTStr(\"Auth.LoadingChannel.Error\"));\n\t\t}\n\t}\n}\n#endif\n\nvoid OBSBasicSettings::UseStreamKeyAdvClicked()\n{\n\tui->streamKeyWidget->setVisible(true);\n\tui->streamKeyLabel->setVisible(true);\n\tui->useStreamKeyAdv->setVisible(false);\n}\n\nvoid OBSBasicSettings::on_service_currentIndexChanged(int idx)\n{\n\tif (ui->service->currentData().toInt() == (int)ListOpt::ShowAll) {\n\t\tLoadServices(true);\n\t\tui->service->showPopup();\n\t\treturn;\n\t}\n\n\tServiceChanged();\n\n\tUpdateMoreInfoLink();\n\tUpdateServerList();\n\tUpdateKeyLink();\n\tUpdateServiceRecommendations();\n\n\tUpdateVodTrackSetting();\n\n\tprotocol = FindProtocol();\n\tUpdateAdvNetworkGroup();\n\tUpdateMultitrackVideo();\n\n\tif (ServiceSupportsCodecCheck() && UpdateResFPSLimits()) {\n\t\tlastServiceIdx = idx;\n\t\tif (idx == 0)\n\t\t\tlastCustomServer = ui->customServer->text();\n\t}\n\n\tif (!IsCustomService()) {\n\t\tui->advStreamTrackWidget->setCurrentWidget(ui->streamSingleTracks);\n\t} else {\n\t\tSwapMultiTrack(QT_TO_UTF8(protocol));\n\t}\n\n\tif (IsWHIP()) {\n\t\tui->whipSimulcastGroupBox->show();\n\t} else {\n\t\tui->whipSimulcastGroupBox->hide();\n\t}\n}\n\nvoid OBSBasicSettings::on_customServer_textChanged(const QString &)\n{\n\tUpdateKeyLink();\n\n\tprotocol = FindProtocol();\n\tUpdateAdvNetworkGroup();\n\tUpdateMultitrackVideo();\n\n\tif (ServiceSupportsCodecCheck())\n\t\tlastCustomServer = ui->customServer->text();\n\n\tSwapMultiTrack(QT_TO_UTF8(protocol));\n}\n\nvoid OBSBasicSettings::ServiceChanged(bool resetFields)\n{\n\tstd::string service = QT_TO_UTF8(ui->service->currentText());\n\tbool custom = IsCustomService();\n\tbool whip = IsWHIP();\n\n\tui->disconnectAccount->setVisible(false);\n\tui->bandwidthTestEnable->setVisible(false);\n\tui->twitchAddonDropdown->setVisible(false);\n\tui->twitchAddonLabel->setVisible(false);\n\n\tif (resetFields || lastService != service.c_str()) {\n\t\treset_service_ui_fields(ui.get(), service, loading);\n\n\t\tui->enableMultitrackVideo->setChecked(\n\t\t\tconfig_get_bool(main->Config(), \"Stream1\", \"EnableMultitrackVideo\"));\n\t\tUpdateMultitrackVideo();\n\t}\n\n\tui->useAuth->setVisible(custom);\n\tui->authUsernameLabel->setVisible(custom);\n\tui->authUsername->setVisible(custom);\n\tui->authPwLabel->setVisible(custom);\n\tui->authPwWidget->setVisible(custom);\n\n\tif (custom || whip) {\n\t\tui->destinationLayout->insertRow(1, ui->serverLabel, ui->serverStackedWidget);\n\n\t\tui->serverStackedWidget->setCurrentIndex(1);\n\t\tui->serverStackedWidget->setVisible(true);\n\t\tui->serverLabel->setVisible(true);\n\t\ton_useAuth_toggled();\n\t} else {\n\t\tui->serverStackedWidget->setCurrentIndex(0);\n\t}\n\n\tauth.reset();\n\n\tif (!main->auth) {\n\t\treturn;\n\t}\n\n\tauto system_auth_service = main->auth->service();\n\tbool service_check = service.find(system_auth_service) != std::string::npos;\n#ifdef YOUTUBE_ENABLED\n\tservice_check = service_check ? service_check\n\t\t\t\t      : IsYouTubeService(system_auth_service) && IsYouTubeService(service);\n#endif\n\tif (service_check) {\n\t\tauth = main->auth;\n\t\tOnAuthConnected();\n\t}\n}\n\nQString OBSBasicSettings::FindProtocol()\n{\n\tif (IsCustomService()) {\n\t\tif (ui->customServer->text().isEmpty())\n\t\t\treturn QString(\"RTMP\");\n\n\t\tQString server = ui->customServer->text();\n\n\t\tif (obs_is_output_protocol_registered(\"RTMPS\") && server.startsWith(\"rtmps://\"))\n\t\t\treturn QString(\"RTMPS\");\n\n\t\tif (server.startsWith(\"srt://\"))\n\t\t\treturn QString(\"SRT\");\n\n\t\tif (server.startsWith(\"rist://\"))\n\t\t\treturn QString(\"RIST\");\n\n\t} else {\n\t\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\t\tobs_property_t *services = obs_properties_get(props, \"service\");\n\n\t\tOBSDataAutoRelease settings = obs_data_create();\n\n\t\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(ui->service->currentText()));\n\t\tobs_property_modified(services, settings);\n\n\t\tobs_properties_destroy(props);\n\n\t\tconst char *protocol = obs_data_get_string(settings, \"protocol\");\n\t\tif (protocol && *protocol)\n\t\t\treturn QT_UTF8(protocol);\n\t}\n\n\treturn QString(\"RTMP\");\n}\n\nvoid OBSBasicSettings::UpdateServerList()\n{\n\tQString serviceName = ui->service->currentText();\n\n\tlastService = serviceName;\n\n\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\tobs_property_t *services = obs_properties_get(props, \"service\");\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(serviceName));\n\tobs_property_modified(services, settings);\n\n\tobs_property_t *servers = obs_properties_get(props, \"server\");\n\n\tui->server->clear();\n\n\tsize_t servers_count = obs_property_list_item_count(servers);\n\tfor (size_t i = 0; i < servers_count; i++) {\n\t\tconst char *name = obs_property_list_item_name(servers, i);\n\t\tconst char *server = obs_property_list_item_string(servers, i);\n\t\tui->server->addItem(name, server);\n\t}\n\n\tif (serviceName == \"Twitch\" || serviceName == \"Amazon IVS\") {\n\t\tui->server->addItem(QTStr(\"Basic.Settings.Stream.SpecifyCustomServer\"), CustomServerUUID());\n\t}\n\n\tobs_properties_destroy(props);\n}\n\nvoid OBSBasicSettings::on_show_clicked()\n{\n\tif (ui->key->echoMode() == QLineEdit::Password) {\n\t\tui->key->setEchoMode(QLineEdit::Normal);\n\t\tui->show->setText(QTStr(\"Hide\"));\n\t} else {\n\t\tui->key->setEchoMode(QLineEdit::Password);\n\t\tui->show->setText(QTStr(\"Show\"));\n\t}\n}\n\nvoid OBSBasicSettings::on_authPwShow_clicked()\n{\n\tif (ui->authPw->echoMode() == QLineEdit::Password) {\n\t\tui->authPw->setEchoMode(QLineEdit::Normal);\n\t\tui->authPwShow->setText(QTStr(\"Hide\"));\n\t} else {\n\t\tui->authPw->setEchoMode(QLineEdit::Password);\n\t\tui->authPwShow->setText(QTStr(\"Show\"));\n\t}\n}\n\nOBSService OBSBasicSettings::SpawnTempService()\n{\n\tbool custom = IsCustomService();\n\tbool whip = IsWHIP();\n\tconst char *service_id = \"rtmp_common\";\n\n\tif (custom) {\n\t\tservice_id = \"rtmp_custom\";\n\t} else if (whip) {\n\t\tservice_id = \"whip_custom\";\n\t}\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tif (!custom && !whip) {\n\t\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(ui->service->currentText()));\n\t\tobs_data_set_string(settings, \"server\", QT_TO_UTF8(ui->server->currentData().toString()));\n\t} else {\n\t\tobs_data_set_string(settings, \"server\", QT_TO_UTF8(ui->customServer->text().trimmed()));\n\t}\n\n\tif (whip)\n\t\tobs_data_set_string(settings, \"bearer_token\", QT_TO_UTF8(ui->key->text()));\n\telse\n\t\tobs_data_set_string(settings, \"key\", QT_TO_UTF8(ui->key->text()));\n\n\tOBSServiceAutoRelease newService = obs_service_create(service_id, \"temp_service\", settings, nullptr);\n\treturn newService.Get();\n}\n\nvoid OBSBasicSettings::OnOAuthStreamKeyConnected()\n{\n\tOAuthStreamKey *a = reinterpret_cast<OAuthStreamKey *>(auth.get());\n\n\tif (a) {\n\t\tbool validKey = !a->key().empty();\n\n\t\tif (validKey)\n\t\t\tui->key->setText(QT_UTF8(a->key().c_str()));\n\n\t\tui->streamKeyWidget->setVisible(false);\n\t\tui->streamKeyLabel->setVisible(false);\n\t\tui->connectAccount2->setVisible(false);\n\t\tui->disconnectAccount->setVisible(true);\n\t\tui->useStreamKeyAdv->setVisible(false);\n\n\t\tui->connectedAccountLabel->setVisible(false);\n\t\tui->connectedAccountText->setVisible(false);\n\n\t\tif (strcmp(a->service(), \"Twitch\") == 0) {\n\t\t\tui->bandwidthTestEnable->setVisible(true);\n\t\t\tui->twitchAddonLabel->setVisible(true);\n\t\t\tui->twitchAddonDropdown->setVisible(true);\n\t\t} else {\n\t\t\tui->bandwidthTestEnable->setChecked(false);\n\t\t}\n#ifdef YOUTUBE_ENABLED\n\t\tif (IsYouTubeService(a->service())) {\n\t\t\tui->key->clear();\n\n\t\t\tui->connectedAccountLabel->setVisible(true);\n\t\t\tui->connectedAccountText->setVisible(true);\n\n\t\t\tui->connectedAccountText->setText(QTStr(\"Auth.LoadingChannel.Title\"));\n\n\t\t\tget_yt_ch_title(ui.get());\n\t\t}\n#endif\n\t}\n\n\tui->streamStackWidget->setCurrentIndex((int)Section::StreamKey);\n}\n\nvoid OBSBasicSettings::OnAuthConnected()\n{\n\tstd::string service = QT_TO_UTF8(ui->service->currentText());\n\tAuth::Type type = Auth::AuthType(service);\n\n\tif (type == Auth::Type::OAuth_StreamKey || type == Auth::Type::OAuth_LinkedAccount) {\n\t\tOnOAuthStreamKeyConnected();\n\t}\n\n\tif (!loading) {\n\t\tstream1Changed = true;\n\t\tEnableApplyButton(true);\n\t}\n}\n\nvoid OBSBasicSettings::on_connectAccount_clicked()\n{\n\tstd::string service = QT_TO_UTF8(ui->service->currentText());\n\n\tOAuth::DeleteCookies(service);\n\n\tauth = OAuthStreamKey::Login(this, service);\n\tif (!!auth) {\n\t\tOnAuthConnected();\n#ifdef YOUTUBE_ENABLED\n\t\tif (cef_js_avail && IsYouTubeService(service)) {\n\t\t\tif (!main->GetYouTubeAppDock()) {\n\t\t\t\tmain->NewYouTubeAppDock();\n\t\t\t}\n\t\t\tmain->GetYouTubeAppDock()->AccountConnected();\n\t\t}\n#endif\n\n\t\tui->useStreamKeyAdv->setVisible(false);\n\t}\n}\n\n#define DISCONNECT_COMFIRM_TITLE \"Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title\"\n#define DISCONNECT_COMFIRM_TEXT \"Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text\"\n\nvoid OBSBasicSettings::on_disconnectAccount_clicked()\n{\n\tQMessageBox::StandardButton button;\n\n\tbutton = OBSMessageBox::question(this, QTStr(DISCONNECT_COMFIRM_TITLE), QTStr(DISCONNECT_COMFIRM_TEXT));\n\n\tif (button == QMessageBox::No) {\n\t\treturn;\n\t}\n\n\tmain->auth.reset();\n\tauth.reset();\n\tmain->SetBroadcastFlowEnabled(false);\n\n\tstd::string service = QT_TO_UTF8(ui->service->currentText());\n\n#ifdef BROWSER_AVAILABLE\n\tOAuth::DeleteCookies(service);\n#endif\n\n\tui->bandwidthTestEnable->setChecked(false);\n\n\treset_service_ui_fields(ui.get(), service, loading);\n\n\tui->bandwidthTestEnable->setVisible(false);\n\tui->twitchAddonDropdown->setVisible(false);\n\tui->twitchAddonLabel->setVisible(false);\n\tui->key->setText(\"\");\n\n\tui->connectedAccountLabel->setVisible(false);\n\tui->connectedAccountText->setVisible(false);\n\n#ifdef YOUTUBE_ENABLED\n\tif (cef_js_avail && IsYouTubeService(service)) {\n\t\tif (!main->GetYouTubeAppDock()) {\n\t\t\tmain->NewYouTubeAppDock();\n\t\t}\n\t\tmain->GetYouTubeAppDock()->AccountDisconnected();\n\t\tmain->GetYouTubeAppDock()->Update();\n\t}\n#endif\n}\n\nvoid OBSBasicSettings::on_useStreamKey_clicked()\n{\n\tui->streamStackWidget->setCurrentIndex((int)Section::StreamKey);\n}\n\nvoid OBSBasicSettings::on_useAuth_toggled()\n{\n\tif (!IsCustomService())\n\t\treturn;\n\n\tbool use_auth = ui->useAuth->isChecked();\n\n\tui->authUsernameLabel->setVisible(use_auth);\n\tui->authUsername->setVisible(use_auth);\n\tui->authPwLabel->setVisible(use_auth);\n\tui->authPwWidget->setVisible(use_auth);\n}\n\nbool OBSBasicSettings::IsCustomServer()\n{\n\treturn ui->server->currentData() == QVariant{CustomServerUUID()};\n}\n\nvoid OBSBasicSettings::on_server_currentIndexChanged(int /*index*/)\n{\n\tauto server_is_custom = IsCustomServer();\n\n\tui->serviceCustomServerLabel->setVisible(server_is_custom);\n\tui->serviceCustomServer->setVisible(server_is_custom);\n}\n\nvoid OBSBasicSettings::UpdateVodTrackSetting()\n{\n\tbool enableForCustomServer = config_get_bool(App()->GetUserConfig(), \"General\", \"EnableCustomServerVodTrack\");\n\tbool enableVodTrack = ui->service->currentText() == \"Twitch\";\n\tbool wasEnabled = !!vodTrackCheckbox;\n\n\tif (enableForCustomServer && IsCustomService())\n\t\tenableVodTrack = true;\n\n\tif (enableVodTrack == wasEnabled)\n\t\treturn;\n\n\tif (!enableVodTrack) {\n\t\tdelete vodTrackCheckbox;\n\t\tdelete vodTrackContainer;\n\t\tdelete simpleVodTrack;\n\t\treturn;\n\t}\n\n\t/* -------------------------------------- */\n\t/* simple output mode vod track widgets   */\n\n\tbool simpleAdv = ui->simpleOutAdvanced->isChecked();\n\tbool vodTrackEnabled = config_get_bool(main->Config(), \"SimpleOutput\", \"VodTrackEnabled\");\n\n\tsimpleVodTrack = new QCheckBox(this);\n\tsimpleVodTrack->setText(QTStr(\"Basic.Settings.Output.Simple.TwitchVodTrack\"));\n\tsimpleVodTrack->setVisible(simpleAdv);\n\tsimpleVodTrack->setChecked(vodTrackEnabled);\n\n\tint pos;\n\tui->simpleStreamingLayout->getWidgetPosition(ui->simpleOutAdvanced, &pos, nullptr);\n\tui->simpleStreamingLayout->insertRow(pos + 1, nullptr, simpleVodTrack);\n\n\tHookWidget(simpleVodTrack.data(), &QCheckBox::clicked, &OBSBasicSettings::OutputsChanged);\n\tconnect(ui->simpleOutAdvanced, &QCheckBox::toggled, simpleVodTrack.data(), &QCheckBox::setVisible);\n\n\t/* -------------------------------------- */\n\t/* advanced output mode vod track widgets */\n\n\tvodTrackCheckbox = new QCheckBox(this);\n\tvodTrackCheckbox->setText(QTStr(\"Basic.Settings.Output.Adv.TwitchVodTrack\"));\n\tvodTrackCheckbox->setLayoutDirection(Qt::RightToLeft);\n\n\tvodTrackContainer = new QWidget(this);\n\tQHBoxLayout *vodTrackLayout = new QHBoxLayout();\n\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\tvodTrack[i] = new QRadioButton(QString::number(i + 1));\n\t\tvodTrackLayout->addWidget(vodTrack[i]);\n\n\t\tHookWidget(vodTrack[i].data(), &QRadioButton::clicked, &OBSBasicSettings::OutputsChanged);\n\t}\n\n\tHookWidget(vodTrackCheckbox.data(), &QCheckBox::clicked, &OBSBasicSettings::OutputsChanged);\n\n\tvodTrackLayout->addStretch();\n\tvodTrackLayout->setContentsMargins(0, 0, 0, 0);\n\n\tvodTrackContainer->setLayout(vodTrackLayout);\n\n\tui->advOutTopLayout->insertRow(2, vodTrackCheckbox, vodTrackContainer);\n\n\tvodTrackEnabled = config_get_bool(main->Config(), \"AdvOut\", \"VodTrackEnabled\");\n\tvodTrackCheckbox->setChecked(vodTrackEnabled);\n\tvodTrackContainer->setEnabled(vodTrackEnabled);\n\n\tconnect(vodTrackCheckbox, &QCheckBox::clicked, vodTrackContainer, &QWidget::setEnabled);\n\n\tint trackIndex = config_get_int(main->Config(), \"AdvOut\", \"VodTrackIndex\");\n\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\tvodTrack[i]->setChecked((i + 1) == trackIndex);\n\t}\n}\n\nOBSService OBSBasicSettings::GetStream1Service()\n{\n\treturn stream1Changed ? SpawnTempService() : OBSService(main->GetService());\n}\n\nvoid OBSBasicSettings::UpdateServiceRecommendations()\n{\n\tbool customServer = IsCustomService();\n\tui->ignoreRecommended->setVisible(!customServer);\n\tui->enforceSettingsLabel->setVisible(!customServer);\n\n\tOBSService service = GetStream1Service();\n\n\tint vbitrate, abitrate;\n\tBPtr<obs_service_resolution> res_list;\n\tsize_t res_count;\n\tint fps;\n\n\tobs_service_get_max_bitrate(service, &vbitrate, &abitrate);\n\tobs_service_get_supported_resolutions(service, &res_list, &res_count);\n\tobs_service_get_max_fps(service, &fps);\n\n\tQString text;\n\n#define ENFORCE_TEXT(x) QTStr(\"Basic.Settings.Stream.Recommended.\" x)\n\tif (vbitrate)\n\t\ttext += ENFORCE_TEXT(\"MaxVideoBitrate\").arg(QString::number(vbitrate));\n\tif (abitrate) {\n\t\tif (!text.isEmpty())\n\t\t\ttext += \"<br>\";\n\t\ttext += ENFORCE_TEXT(\"MaxAudioBitrate\").arg(QString::number(abitrate));\n\t}\n\tif (res_count) {\n\t\tif (!text.isEmpty())\n\t\t\ttext += \"<br>\";\n\n\t\tobs_service_resolution best_res = {};\n\t\tint best_res_pixels = 0;\n\n\t\tfor (size_t i = 0; i < res_count; i++) {\n\t\t\tobs_service_resolution res = res_list[i];\n\t\t\tint res_pixels = res.cx + res.cy;\n\t\t\tif (res_pixels > best_res_pixels) {\n\t\t\t\tbest_res = res;\n\t\t\t\tbest_res_pixels = res_pixels;\n\t\t\t}\n\t\t}\n\n\t\tQString res_str = QString(\"%1x%2\").arg(QString::number(best_res.cx), QString::number(best_res.cy));\n\t\ttext += ENFORCE_TEXT(\"MaxResolution\").arg(res_str);\n\t}\n\tif (fps) {\n\t\tif (!text.isEmpty())\n\t\t\ttext += \"<br>\";\n\n\t\ttext += ENFORCE_TEXT(\"MaxFPS\").arg(QString::number(fps));\n\t}\n#undef ENFORCE_TEXT\n\n#ifdef YOUTUBE_ENABLED\n\tif (IsYouTubeService(QT_TO_UTF8(ui->service->currentText()))) {\n\t\tif (!text.isEmpty())\n\t\t\ttext += \"<br><br>\";\n\n\t\ttext += \"<a href=\\\"https://www.youtube.com/t/terms\\\">\"\n\t\t\t\"YouTube Terms of Service</a><br>\"\n\t\t\t\"<a href=\\\"http://www.google.com/policies/privacy\\\">\"\n\t\t\t\"Google Privacy Policy</a><br>\"\n\t\t\t\"<a href=\\\"https://security.google.com/settings/security/permissions\\\">\"\n\t\t\t\"Google Third-Party Permissions</a>\";\n\t}\n#endif\n\tui->enforceSettingsLabel->setText(text);\n}\n\nvoid OBSBasicSettings::DisplayEnforceWarning(bool checked)\n{\n\tif (IsCustomService())\n\t\treturn;\n\n\tif (!checked) {\n\t\tSimpleRecordingEncoderChanged();\n\t\treturn;\n\t}\n\n\tQMessageBox::StandardButton button;\n\n#define ENFORCE_WARNING(x) QTStr(\"Basic.Settings.Stream.IgnoreRecommended.Warn.\" x)\n\n\tbutton = OBSMessageBox::question(this, ENFORCE_WARNING(\"Title\"), ENFORCE_WARNING(\"Text\"));\n#undef ENFORCE_WARNING\n\n\tif (button == QMessageBox::No) {\n\t\tQMetaObject::invokeMethod(ui->ignoreRecommended, \"setChecked\", Qt::QueuedConnection,\n\t\t\t\t\t  Q_ARG(bool, false));\n\t\treturn;\n\t}\n\n\tSimpleRecordingEncoderChanged();\n}\n\nbool OBSBasicSettings::ResFPSValid(obs_service_resolution *res_list, size_t res_count, int max_fps)\n{\n\tif (!res_count && !max_fps)\n\t\treturn true;\n\n\tif (res_count) {\n\t\tQString res = ui->outputResolution->currentText();\n\t\tbool found_res = false;\n\n\t\tint cx, cy;\n\t\tif (sscanf(QT_TO_UTF8(res), \"%dx%d\", &cx, &cy) != 2)\n\t\t\treturn false;\n\n\t\tfor (size_t i = 0; i < res_count; i++) {\n\t\t\tif (res_list[i].cx == cx && res_list[i].cy == cy) {\n\t\t\t\tfound_res = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!found_res)\n\t\t\treturn false;\n\t}\n\n\tif (max_fps) {\n\t\tint fpsType = ui->fpsType->currentIndex();\n\t\tif (fpsType != 0)\n\t\t\treturn false;\n\n\t\tstd::string fps_str = QT_TO_UTF8(ui->fpsCommon->currentText());\n\t\tfloat fps;\n\t\tsscanf(fps_str.c_str(), \"%f\", &fps);\n\t\tif (fps > (float)max_fps)\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nextern void set_closest_res(int &cx, int &cy, struct obs_service_resolution *res_list, size_t count);\n\n/* Checks for and updates the resolution and FPS limits of a service, if any.\n *\n * If the service has a resolution and/or FPS limit, this will enforce those\n * limitations in the UI itself, preventing the user from selecting a\n * resolution or FPS that's not supported.\n *\n * This is an unpleasant thing to have to do to users, but there is no other\n * way to ensure that a service's restricted resolution/framerate values are\n * properly enforced, otherwise users will just be confused when things aren't\n * working correctly. The user can turn it off if they're partner (or if they\n * want to risk getting in trouble with their service) by selecting the \"Ignore\n * recommended settings\" option in the stream section of settings.\n *\n * This only affects services that have a resolution and/or framerate limit, of\n * which as of this writing, and hopefully for the foreseeable future, there is\n * only one.\n */\nbool OBSBasicSettings::UpdateResFPSLimits()\n{\n\tif (loading)\n\t\treturn false;\n\n\tint idx = ui->service->currentIndex();\n\tif (idx == -1)\n\t\treturn false;\n\n\tbool ignoreRecommended = ui->ignoreRecommended->isChecked();\n\tBPtr<obs_service_resolution> res_list;\n\tsize_t res_count = 0;\n\tint max_fps = 0;\n\n\tif (!IsCustomService() && !ignoreRecommended) {\n\t\tOBSService service = GetStream1Service();\n\t\tobs_service_get_supported_resolutions(service, &res_list, &res_count);\n\t\tobs_service_get_max_fps(service, &max_fps);\n\t}\n\n\t/* ------------------------------------ */\n\t/* Check for enforced res/FPS           */\n\n\tQString res = ui->outputResolution->currentText();\n\tQString fps_str;\n\tint cx = 0, cy = 0;\n\tdouble max_fpsd = (double)max_fps;\n\tint closest_fps_index = -1;\n\tdouble fpsd;\n\n\tsscanf(QT_TO_UTF8(res), \"%dx%d\", &cx, &cy);\n\n\tif (res_count)\n\t\tset_closest_res(cx, cy, res_list, res_count);\n\n\tif (max_fps) {\n\t\tint fpsType = ui->fpsType->currentIndex();\n\n\t\tif (fpsType == 1) { //Integer\n\t\t\tfpsd = (double)ui->fpsInteger->value();\n\t\t} else if (fpsType == 2) { //Fractional\n\t\t\tfpsd = (double)ui->fpsNumerator->value() / (double)ui->fpsDenominator->value();\n\t\t} else { //Common\n\t\t\tsscanf(QT_TO_UTF8(ui->fpsCommon->currentText()), \"%lf\", &fpsd);\n\t\t}\n\n\t\tdouble closest_diff = 1000000000000.0;\n\n\t\tfor (int i = 0; i < ui->fpsCommon->count(); i++) {\n\t\t\tdouble com_fpsd;\n\t\t\tsscanf(QT_TO_UTF8(ui->fpsCommon->itemText(i)), \"%lf\", &com_fpsd);\n\n\t\t\tif (com_fpsd > max_fpsd) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tdouble diff = fabs(com_fpsd - fpsd);\n\t\t\tif (diff < closest_diff) {\n\t\t\t\tclosest_diff = diff;\n\t\t\t\tclosest_fps_index = i;\n\t\t\t\tfps_str = ui->fpsCommon->itemText(i);\n\t\t\t}\n\t\t}\n\t}\n\n\tQString res_str = QString(\"%1x%2\").arg(QString::number(cx), QString::number(cy));\n\n\t/* ------------------------------------ */\n\t/* Display message box if res/FPS bad   */\n\n\tbool valid = ResFPSValid(res_list, res_count, max_fps);\n\n\tif (!valid) {\n\t\t/* if the user was already on facebook with an incompatible\n\t\t * resolution, assume it's an upgrade */\n\t\tif (lastServiceIdx == -1 && lastIgnoreRecommended == -1) {\n\t\t\tui->ignoreRecommended->setChecked(true);\n\t\t\tui->ignoreRecommended->setProperty(\"changed\", true);\n\t\t\tstream1Changed = true;\n\t\t\tEnableApplyButton(true);\n\t\t\treturn UpdateResFPSLimits();\n\t\t}\n\n\t\tQMessageBox::StandardButton button;\n\n#define WARNING_VAL(x) QTStr(\"Basic.Settings.Output.Warn.EnforceResolutionFPS.\" x)\n\n\t\tQString str;\n\t\tif (res_count)\n\t\t\tstr += WARNING_VAL(\"Resolution\").arg(res_str);\n\t\tif (max_fps) {\n\t\t\tif (!str.isEmpty())\n\t\t\t\tstr += \"\\n\";\n\t\t\tstr += WARNING_VAL(\"FPS\").arg(fps_str);\n\t\t}\n\n\t\tbutton = OBSMessageBox::question(this, WARNING_VAL(\"Title\"), WARNING_VAL(\"Msg\").arg(str));\n#undef WARNING_VAL\n\n\t\tif (button == QMessageBox::No) {\n\t\t\tif (idx != lastServiceIdx)\n\t\t\t\tQMetaObject::invokeMethod(ui->service, \"setCurrentIndex\", Qt::QueuedConnection,\n\t\t\t\t\t\t\t  Q_ARG(int, lastServiceIdx));\n\t\t\telse\n\t\t\t\tQMetaObject::invokeMethod(ui->ignoreRecommended, \"setChecked\", Qt::QueuedConnection,\n\t\t\t\t\t\t\t  Q_ARG(bool, true));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* ------------------------------------ */\n\t/* Update widgets/values if switching   */\n\t/* to/from enforced resolution/FPS      */\n\n\tui->outputResolution->blockSignals(true);\n\tif (res_count) {\n\t\tui->outputResolution->clear();\n\t\tui->outputResolution->setEditable(false);\n\t\tHookWidget(ui->outputResolution, &QComboBox::currentIndexChanged,\n\t\t\t   &OBSBasicSettings::VideoChangedResolution);\n\n\t\tint new_res_index = -1;\n\n\t\tfor (size_t i = 0; i < res_count; i++) {\n\t\t\tobs_service_resolution val = res_list[i];\n\t\t\tQString str = QString(\"%1x%2\").arg(QString::number(val.cx), QString::number(val.cy));\n\t\t\tui->outputResolution->addItem(str);\n\n\t\t\tif (val.cx == cx && val.cy == cy)\n\t\t\t\tnew_res_index = (int)i;\n\t\t}\n\n\t\tui->outputResolution->setCurrentIndex(new_res_index);\n\t\tif (!valid) {\n\t\t\tui->outputResolution->setProperty(\"changed\", true);\n\t\t\tvideoChanged = true;\n\t\t\tEnableApplyButton(true);\n\t\t}\n\t} else {\n\t\tQString baseRes = ui->baseResolution->currentText();\n\t\tint baseCX, baseCY;\n\t\tsscanf(QT_TO_UTF8(baseRes), \"%dx%d\", &baseCX, &baseCY);\n\n\t\tif (!ui->outputResolution->isEditable()) {\n\t\t\tRecreateOutputResolutionWidget();\n\t\t\tui->outputResolution->blockSignals(true);\n\t\t\tResetDownscales((uint32_t)baseCX, (uint32_t)baseCY, true);\n\t\t\tui->outputResolution->setCurrentText(res);\n\t\t}\n\t}\n\tui->outputResolution->blockSignals(false);\n\n\tif (max_fps) {\n\t\tfor (int i = 0; i < ui->fpsCommon->count(); i++) {\n\t\t\tdouble com_fpsd;\n\t\t\tsscanf(QT_TO_UTF8(ui->fpsCommon->itemText(i)), \"%lf\", &com_fpsd);\n\n\t\t\tif (com_fpsd > max_fpsd) {\n\t\t\t\tSetComboItemEnabled(ui->fpsCommon, i, false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tui->fpsType->setCurrentIndex(0);\n\t\tui->fpsCommon->setCurrentIndex(closest_fps_index);\n\t\tif (!valid) {\n\t\t\tui->fpsType->setProperty(\"changed\", true);\n\t\t\tui->fpsCommon->setProperty(\"changed\", true);\n\t\t\tvideoChanged = true;\n\t\t\tEnableApplyButton(true);\n\t\t}\n\t} else {\n\t\tfor (int i = 0; i < ui->fpsCommon->count(); i++)\n\t\t\tSetComboItemEnabled(ui->fpsCommon, i, true);\n\t}\n\n\tSetComboItemEnabled(ui->fpsType, 1, !max_fps);\n\tSetComboItemEnabled(ui->fpsType, 2, !max_fps);\n\n\t/* ------------------------------------ */\n\n\tlastIgnoreRecommended = (int)ignoreRecommended;\n\n\treturn true;\n}\n\nstatic bool service_supports_codec(const char **codecs, const char *codec)\n{\n\tif (!codecs)\n\t\treturn true;\n\n\twhile (*codecs) {\n\t\tif (strcmp(*codecs, codec) == 0)\n\t\t\treturn true;\n\t\tcodecs++;\n\t}\n\n\treturn false;\n}\n\nextern bool EncoderAvailable(const char *encoder);\nextern const char *get_simple_output_encoder(const char *name);\n\nstatic inline bool service_supports_encoder(const char **codecs, const char *encoder)\n{\n\tif (!EncoderAvailable(encoder))\n\t\treturn false;\n\n\tconst char *codec = obs_get_encoder_codec(encoder);\n\treturn service_supports_codec(codecs, codec);\n}\n\nstatic bool return_first_id(void *data, const char *id)\n{\n\tconst char **output = (const char **)data;\n\n\t*output = id;\n\treturn false;\n}\n\nbool OBSBasicSettings::ServiceAndVCodecCompatible()\n{\n\tbool simple = (ui->outputMode->currentIndex() == 0);\n\tbool ret;\n\n\tconst char *codec;\n\n\tif (simple) {\n\t\tQString encoder = ui->simpleOutStrEncoder->currentData().toString();\n\t\tconst char *id = get_simple_output_encoder(QT_TO_UTF8(encoder));\n\t\tcodec = obs_get_encoder_codec(id);\n\t} else {\n\t\tQString encoder = ui->advOutEncoder->currentData().toString();\n\t\tcodec = obs_get_encoder_codec(QT_TO_UTF8(encoder));\n\t}\n\n\tOBSService service = SpawnTempService();\n\tconst char **codecs = obs_service_get_supported_video_codecs(service);\n\n\tif (!codecs || IsCustomService()) {\n\t\tconst char *output;\n\t\tchar **output_codecs;\n\n\t\tobs_enum_output_types_with_protocol(QT_TO_UTF8(protocol), &output, return_first_id);\n\n\t\toutput_codecs = strlist_split(obs_get_output_supported_video_codecs(output), ';', false);\n\n\t\tret = service_supports_codec((const char **)output_codecs, codec);\n\n\t\tstrlist_free(output_codecs);\n\t} else {\n\t\tret = service_supports_codec(codecs, codec);\n\t}\n\n\treturn ret;\n}\n\nbool OBSBasicSettings::ServiceAndACodecCompatible()\n{\n\tbool simple = (ui->outputMode->currentIndex() == 0);\n\tbool ret;\n\n\tQString codec;\n\n\tif (simple) {\n\t\tcodec = ui->simpleOutStrAEncoder->currentData().toString();\n\t} else {\n\t\tQString encoder = ui->advOutAEncoder->currentData().toString();\n\t\tcodec = obs_get_encoder_codec(QT_TO_UTF8(encoder));\n\t}\n\n\tOBSService service = SpawnTempService();\n\tconst char **codecs = obs_service_get_supported_audio_codecs(service);\n\n\tif (!codecs || IsCustomService()) {\n\t\tconst char *output;\n\t\tchar **output_codecs;\n\n\t\tobs_enum_output_types_with_protocol(QT_TO_UTF8(protocol), &output, return_first_id);\n\t\toutput_codecs = strlist_split(obs_get_output_supported_audio_codecs(output), ';', false);\n\n\t\tret = service_supports_codec((const char **)output_codecs, QT_TO_UTF8(codec));\n\n\t\tstrlist_free(output_codecs);\n\t} else {\n\t\tret = service_supports_codec(codecs, QT_TO_UTF8(codec));\n\t}\n\n\treturn ret;\n}\n\n/* we really need a way to find fallbacks in a less hardcoded way. maybe. */\nstatic QString get_adv_fallback(const QString &enc)\n{\n\tif (enc == \"obs_nvenc_hevc_tex\" || enc == \"obs_nvenc_av1_tex\" || enc == \"jim_hevc_nvenc\" ||\n\t    enc == \"jim_av1_nvenc\")\n\t\treturn \"obs_nvenc_h264_tex\";\n\tif (enc == \"h265_texture_amf\" || enc == \"av1_texture_amf\")\n\t\treturn \"h264_texture_amf\";\n\tif (enc == \"com.apple.videotoolbox.videoencoder.ave.hevc\")\n\t\treturn \"com.apple.videotoolbox.videoencoder.ave.avc\";\n\tif (enc == \"obs_qsv11_av1\")\n\t\treturn \"obs_qsv11\";\n\treturn \"obs_x264\";\n}\n\nstatic QString get_adv_audio_fallback(const QString &enc)\n{\n\tconst char *codec = obs_get_encoder_codec(QT_TO_UTF8(enc));\n\n\tif (codec && strcmp(codec, \"aac\") == 0)\n\t\treturn \"ffmpeg_opus\";\n\n\tQString aac_default = \"ffmpeg_aac\";\n\tif (EncoderAvailable(\"CoreAudio_AAC\"))\n\t\taac_default = \"CoreAudio_AAC\";\n\telse if (EncoderAvailable(\"libfdk_aac\"))\n\t\taac_default = \"libfdk_aac\";\n\n\treturn aac_default;\n}\n\nstatic QString get_simple_fallback(const QString &enc)\n{\n\tif (enc == SIMPLE_ENCODER_NVENC_HEVC || enc == SIMPLE_ENCODER_NVENC_AV1)\n\t\treturn SIMPLE_ENCODER_NVENC;\n\tif (enc == SIMPLE_ENCODER_AMD_HEVC || enc == SIMPLE_ENCODER_AMD_AV1)\n\t\treturn SIMPLE_ENCODER_AMD;\n\tif (enc == SIMPLE_ENCODER_APPLE_HEVC)\n\t\treturn SIMPLE_ENCODER_APPLE_H264;\n\tif (enc == SIMPLE_ENCODER_QSV_AV1)\n\t\treturn SIMPLE_ENCODER_QSV;\n\treturn SIMPLE_ENCODER_X264;\n}\n\nbool OBSBasicSettings::ServiceSupportsCodecCheck()\n{\n\tif (loading)\n\t\treturn false;\n\n\tbool vcodec_compat = ServiceAndVCodecCompatible();\n\tbool acodec_compat = ServiceAndACodecCompatible();\n\n\tif (vcodec_compat && acodec_compat) {\n\t\tif (lastServiceIdx != ui->service->currentIndex() || IsCustomService())\n\t\t\tResetEncoders(true);\n\t\treturn true;\n\t}\n\n\tQString service = ui->service->currentText();\n\tQString cur_video_name;\n\tQString fb_video_name;\n\tQString cur_audio_name;\n\tQString fb_audio_name;\n\tbool simple = (ui->outputMode->currentIndex() == 0);\n\n\t/* ------------------------------------------------- */\n\t/* get current codec                                 */\n\n\tif (simple) {\n\t\tQString cur_enc = ui->simpleOutStrEncoder->currentData().toString();\n\t\tQString fb_enc = get_simple_fallback(cur_enc);\n\n\t\tint cur_idx = ui->simpleOutStrEncoder->findData(cur_enc);\n\t\tint fb_idx = ui->simpleOutStrEncoder->findData(fb_enc);\n\n\t\tcur_video_name = ui->simpleOutStrEncoder->itemText(cur_idx);\n\t\tfb_video_name = ui->simpleOutStrEncoder->itemText(fb_idx);\n\n\t\tcur_enc = ui->simpleOutStrAEncoder->currentData().toString();\n\t\tfb_enc = (cur_enc == \"opus\") ? \"aac\" : \"opus\";\n\n\t\tcur_audio_name = ui->simpleOutStrAEncoder->itemText(ui->simpleOutStrAEncoder->findData(cur_enc));\n\t\tfb_audio_name = (cur_enc == \"opus\") ? QTStr(\"Basic.Settings.Output.Simple.Codec.AAC\")\n\t\t\t\t\t\t    : QTStr(\"Basic.Settings.Output.Simple.Codec.Opus\");\n\t} else {\n\t\tQString cur_enc = ui->advOutEncoder->currentData().toString();\n\t\tQString fb_enc = get_adv_fallback(cur_enc);\n\n\t\tcur_video_name = obs_encoder_get_display_name(QT_TO_UTF8(cur_enc));\n\t\tfb_video_name = obs_encoder_get_display_name(QT_TO_UTF8(fb_enc));\n\n\t\tcur_enc = ui->advOutAEncoder->currentData().toString();\n\t\tfb_enc = get_adv_audio_fallback(cur_enc);\n\n\t\tcur_audio_name = obs_encoder_get_display_name(QT_TO_UTF8(cur_enc));\n\t\tfb_audio_name = obs_encoder_get_display_name(QT_TO_UTF8(fb_enc));\n\t}\n\n#define WARNING_VAL(x) QTStr(\"Basic.Settings.Output.Warn.ServiceCodecCompatibility.\" x)\n\n\tQString msg = WARNING_VAL(\"Msg\").arg(service, vcodec_compat ? cur_audio_name : cur_video_name,\n\t\t\t\t\t     vcodec_compat ? fb_audio_name : fb_video_name);\n\tif (!vcodec_compat && !acodec_compat)\n\t\tmsg = WARNING_VAL(\"Msg2\").arg(service, cur_video_name, cur_audio_name, fb_video_name, fb_audio_name);\n\n\tauto button = OBSMessageBox::question(this, WARNING_VAL(\"Title\"), msg);\n#undef WARNING_VAL\n\n\tif (button == QMessageBox::No) {\n\t\tif (lastServiceIdx == 0 && lastServiceIdx == ui->service->currentIndex())\n\t\t\tQMetaObject::invokeMethod(ui->customServer, \"setText\", Qt::QueuedConnection,\n\t\t\t\t\t\t  Q_ARG(QString, lastCustomServer));\n\t\telse\n\t\t\tQMetaObject::invokeMethod(ui->service, \"setCurrentIndex\", Qt::QueuedConnection,\n\t\t\t\t\t\t  Q_ARG(int, lastServiceIdx));\n\t\treturn false;\n\t}\n\n\tResetEncoders(true);\n\treturn true;\n}\n\n#define TEXT_USE_STREAM_ENC QTStr(\"Basic.Settings.Output.Adv.Recording.UseStreamEncoder\")\n\nvoid OBSBasicSettings::ResetEncoders(bool streamOnly)\n{\n\tQString lastAdvVideoEnc = ui->advOutEncoder->currentData().toString();\n\tQString lastVideoEnc = ui->simpleOutStrEncoder->currentData().toString();\n\tQString lastAdvAudioEnc = ui->advOutAEncoder->currentData().toString();\n\tQString lastAudioEnc = ui->simpleOutStrAEncoder->currentData().toString();\n\tOBSService service = SpawnTempService();\n\tconst char **vcodecs = obs_service_get_supported_video_codecs(service);\n\tconst char **acodecs = obs_service_get_supported_audio_codecs(service);\n\tconst char *type;\n\tBPtr<char *> output_vcodecs;\n\tBPtr<char *> output_acodecs;\n\tsize_t idx = 0;\n\n\tif (!vcodecs || IsCustomService()) {\n\t\tconst char *output;\n\n\t\tobs_enum_output_types_with_protocol(QT_TO_UTF8(protocol), &output, return_first_id);\n\t\toutput_vcodecs = strlist_split(obs_get_output_supported_video_codecs(output), ';', false);\n\t\tvcodecs = (const char **)output_vcodecs.Get();\n\t}\n\n\tif (!acodecs || IsCustomService()) {\n\t\tconst char *output;\n\n\t\tobs_enum_output_types_with_protocol(QT_TO_UTF8(protocol), &output, return_first_id);\n\t\toutput_acodecs = strlist_split(obs_get_output_supported_audio_codecs(output), ';', false);\n\t\tacodecs = (const char **)output_acodecs.Get();\n\t}\n\n\tQSignalBlocker s1(ui->simpleOutStrEncoder);\n\tQSignalBlocker s2(ui->advOutEncoder);\n\tQSignalBlocker s3(ui->simpleOutStrAEncoder);\n\tQSignalBlocker s4(ui->advOutAEncoder);\n\n\t/* ------------------------------------------------- */\n\t/* clear encoder lists                               */\n\n\tui->simpleOutStrEncoder->clear();\n\tui->advOutEncoder->clear();\n\tui->simpleOutStrAEncoder->clear();\n\tui->advOutAEncoder->clear();\n\n\tif (!streamOnly) {\n\t\tui->advOutRecEncoder->clear();\n\t\tui->advOutRecAEncoder->clear();\n\t}\n\n\t/* ------------------------------------------------- */\n\t/* load advanced stream/recording encoders           */\n\n\twhile (obs_enum_encoder_types(idx++, &type)) {\n\t\tconst char *name = obs_encoder_get_display_name(type);\n\t\tconst char *codec = obs_get_encoder_codec(type);\n\t\tuint32_t caps = obs_get_encoder_caps(type);\n\n\t\tQString qName = QT_UTF8(name);\n\t\tQString qType = QT_UTF8(type);\n\n\t\tif (obs_get_encoder_type(type) == OBS_ENCODER_VIDEO) {\n\t\t\tif ((caps & ENCODER_HIDE_FLAGS) != 0)\n\t\t\t\tcontinue;\n\n\t\t\tif (service_supports_codec(vcodecs, codec))\n\t\t\t\tui->advOutEncoder->addItem(qName, qType);\n\t\t\tif (!streamOnly)\n\t\t\t\tui->advOutRecEncoder->addItem(qName, qType);\n\t\t}\n\n\t\tif (obs_get_encoder_type(type) == OBS_ENCODER_AUDIO) {\n\t\t\tif (service_supports_codec(acodecs, codec))\n\t\t\t\tui->advOutAEncoder->addItem(qName, qType);\n\t\t\tif (!streamOnly)\n\t\t\t\tui->advOutRecAEncoder->addItem(qName, qType);\n\t\t}\n\t}\n\n\tui->advOutEncoder->model()->sort(0);\n\tui->advOutAEncoder->model()->sort(0);\n\n\tif (!streamOnly) {\n\t\tui->advOutRecEncoder->model()->sort(0);\n\t\tui->advOutRecEncoder->insertItem(0, TEXT_USE_STREAM_ENC, \"none\");\n\t\tui->advOutRecAEncoder->model()->sort(0);\n\t\tui->advOutRecAEncoder->insertItem(0, TEXT_USE_STREAM_ENC, \"none\");\n\t}\n\n\t/* ------------------------------------------------- */\n\t/* load simple stream encoders                       */\n\n#define ENCODER_STR(str) QTStr(\"Basic.Settings.Output.Simple.Encoder.\" str)\n\n\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Software\"), QString(SIMPLE_ENCODER_X264));\n#ifdef _WIN32\n\tif (service_supports_encoder(vcodecs, \"obs_qsv11\"))\n\t\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Hardware.QSV.H264\"), QString(SIMPLE_ENCODER_QSV));\n\tif (service_supports_encoder(vcodecs, \"obs_qsv11_av1\"))\n\t\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Hardware.QSV.AV1\"), QString(SIMPLE_ENCODER_QSV_AV1));\n#endif\n\tif (service_supports_encoder(vcodecs, \"ffmpeg_nvenc\"))\n\t\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Hardware.NVENC.H264\"), QString(SIMPLE_ENCODER_NVENC));\n\tif (service_supports_encoder(vcodecs, \"obs_nvenc_av1_tex\"))\n\t\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Hardware.NVENC.AV1\"), QString(SIMPLE_ENCODER_NVENC_AV1));\n#ifdef ENABLE_HEVC\n\tif (service_supports_encoder(vcodecs, \"h265_texture_amf\"))\n\t\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Hardware.AMD.HEVC\"), QString(SIMPLE_ENCODER_AMD_HEVC));\n\tif (service_supports_encoder(vcodecs, \"ffmpeg_hevc_nvenc\"))\n\t\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Hardware.NVENC.HEVC\"),\n\t\t\t\t\t\t QString(SIMPLE_ENCODER_NVENC_HEVC));\n#endif\n\tif (service_supports_encoder(vcodecs, \"h264_texture_amf\"))\n\t\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Hardware.AMD.H264\"), QString(SIMPLE_ENCODER_AMD));\n\tif (service_supports_encoder(vcodecs, \"av1_texture_amf\"))\n\t\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Hardware.AMD.AV1\"), QString(SIMPLE_ENCODER_AMD_AV1));\n/* Preprocessor guard required for the macOS version check */\n#ifdef __APPLE__\n\tif (service_supports_encoder(vcodecs, \"com.apple.videotoolbox.videoencoder.ave.avc\")\n#ifndef __aarch64__\n\t    && os_get_emulation_status() == true\n#endif\n\t) {\n\t\tif (__builtin_available(macOS 13.0, *)) {\n\t\t\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Hardware.Apple.H264\"),\n\t\t\t\t\t\t\t QString(SIMPLE_ENCODER_APPLE_H264));\n\t\t}\n\t}\n#ifdef ENABLE_HEVC\n\tif (service_supports_encoder(vcodecs, \"com.apple.videotoolbox.videoencoder.ave.hevc\")\n#ifndef __aarch64__\n\t    && os_get_emulation_status() == true\n#endif\n\t) {\n\t\tif (__builtin_available(macOS 13.0, *)) {\n\t\t\tui->simpleOutStrEncoder->addItem(ENCODER_STR(\"Hardware.Apple.HEVC\"),\n\t\t\t\t\t\t\t QString(SIMPLE_ENCODER_APPLE_HEVC));\n\t\t}\n\t}\n#endif\n#endif\n\tif (service_supports_encoder(acodecs, \"CoreAudio_AAC\") || service_supports_encoder(acodecs, \"libfdk_aac\") ||\n\t    service_supports_encoder(acodecs, \"ffmpeg_aac\"))\n\t\tui->simpleOutStrAEncoder->addItem(QTStr(\"Basic.Settings.Output.Simple.Codec.AAC.Default\"), \"aac\");\n\tif (service_supports_encoder(acodecs, \"ffmpeg_opus\"))\n\t\tui->simpleOutStrAEncoder->addItem(QTStr(\"Basic.Settings.Output.Simple.Codec.Opus\"), \"opus\");\n#undef ENCODER_STR\n\n\t/* ------------------------------------------------- */\n\t/* Find fallback encoders                            */\n\n\tif (!lastAdvVideoEnc.isEmpty()) {\n\t\tint idx = ui->advOutEncoder->findData(lastAdvVideoEnc);\n\t\tif (idx == -1) {\n\t\t\tlastAdvVideoEnc = get_adv_fallback(lastAdvVideoEnc);\n\t\t\tui->advOutEncoder->setProperty(\"changed\", QVariant(true));\n\t\t\tOutputsChanged();\n\t\t}\n\n\t\tidx = ui->advOutEncoder->findData(lastAdvVideoEnc);\n\t\ts2.unblock();\n\t\tui->advOutEncoder->setCurrentIndex(idx);\n\t}\n\n\tif (!lastAdvAudioEnc.isEmpty()) {\n\t\tint idx = ui->advOutAEncoder->findData(lastAdvAudioEnc);\n\t\tif (idx == -1) {\n\t\t\tlastAdvAudioEnc = get_adv_audio_fallback(lastAdvAudioEnc);\n\t\t\tui->advOutAEncoder->setProperty(\"changed\", QVariant(true));\n\t\t\tOutputsChanged();\n\t\t}\n\n\t\tidx = ui->advOutAEncoder->findData(lastAdvAudioEnc);\n\t\ts4.unblock();\n\t\tui->advOutAEncoder->setCurrentIndex(idx);\n\t}\n\n\tif (!lastVideoEnc.isEmpty()) {\n\t\tint idx = ui->simpleOutStrEncoder->findData(lastVideoEnc);\n\t\tif (idx == -1) {\n\t\t\tlastVideoEnc = get_simple_fallback(lastVideoEnc);\n\t\t\tui->simpleOutStrEncoder->setProperty(\"changed\", QVariant(true));\n\t\t\tOutputsChanged();\n\t\t}\n\n\t\tidx = ui->simpleOutStrEncoder->findData(lastVideoEnc);\n\t\ts1.unblock();\n\t\tui->simpleOutStrEncoder->setCurrentIndex(idx);\n\t}\n\n\tif (!lastAudioEnc.isEmpty()) {\n\t\tint idx = ui->simpleOutStrAEncoder->findData(lastAudioEnc);\n\t\tif (idx == -1) {\n\t\t\tlastAudioEnc = (lastAudioEnc == \"opus\") ? \"aac\" : \"opus\";\n\t\t\tui->simpleOutStrAEncoder->setProperty(\"changed\", QVariant(true));\n\t\t\tOutputsChanged();\n\t\t}\n\n\t\tidx = ui->simpleOutStrAEncoder->findData(lastAudioEnc);\n\t\ts3.unblock();\n\t\tui->simpleOutStrAEncoder->setCurrentIndex(idx);\n\t}\n}\n"
  },
  {
    "path": "frontend/settings/OBSHotkeyEdit.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014-2015 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSHotkeyEdit.hpp\"\n#include \"OBSBasicSettings.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <qt-wrappers.hpp>\n#include <util/dstr.hpp>\n\n#include <QKeyEvent>\n\n#include \"moc_OBSHotkeyEdit.cpp\"\n\nvoid OBSHotkeyEdit::keyPressEvent(QKeyEvent *event)\n{\n\tif (event->isAutoRepeat())\n\t\treturn;\n\n\tobs_key_combination_t new_key;\n\n\tswitch (event->key()) {\n\tcase Qt::Key_Shift:\n\tcase Qt::Key_Control:\n\tcase Qt::Key_Alt:\n\tcase Qt::Key_Meta:\n\t\tnew_key.key = OBS_KEY_NONE;\n\t\tbreak;\n\n#ifdef __APPLE__\n\tcase Qt::Key_CapsLock:\n\t\t// kVK_CapsLock == 57\n\t\tnew_key.key = obs_key_from_virtual_key(57);\n\t\tbreak;\n#endif\n\n\tdefault:\n\t\tnew_key.key = obs_key_from_virtual_key(event->nativeVirtualKey());\n\t}\n\n\tnew_key.modifiers = TranslateQtKeyboardEventModifiers(event->modifiers());\n\n\tHandleNewKey(new_key);\n}\n\nQVariant OBSHotkeyEdit::inputMethodQuery(Qt::InputMethodQuery query) const\n{\n\tif (query == Qt::ImEnabled) {\n\t\treturn false;\n\t} else {\n\t\treturn QLineEdit::inputMethodQuery(query);\n\t}\n}\n\n#ifdef __APPLE__\nvoid OBSHotkeyEdit::keyReleaseEvent(QKeyEvent *event)\n{\n\tif (event->isAutoRepeat())\n\t\treturn;\n\n\tif (event->key() != Qt::Key_CapsLock)\n\t\treturn;\n\n\tobs_key_combination_t new_key;\n\n\t// kVK_CapsLock == 57\n\tnew_key.key = obs_key_from_virtual_key(57);\n\tnew_key.modifiers = TranslateQtKeyboardEventModifiers(event->modifiers());\n\n\tHandleNewKey(new_key);\n}\n#endif\n\nvoid OBSHotkeyEdit::mousePressEvent(QMouseEvent *event)\n{\n\tobs_key_combination_t new_key;\n\n\tswitch (event->button()) {\n\tcase Qt::NoButton:\n\tcase Qt::LeftButton:\n\tcase Qt::RightButton:\n\tcase Qt::AllButtons:\n\tcase Qt::MouseButtonMask:\n\t\treturn;\n\n\tcase Qt::MiddleButton:\n\t\tnew_key.key = OBS_KEY_MOUSE3;\n\t\tbreak;\n\n#define MAP_BUTTON(i, j)                        \\\n\tcase Qt::ExtraButton##i:                \\\n\t\tnew_key.key = OBS_KEY_MOUSE##j; \\\n\t\tbreak;\n\t\tMAP_BUTTON(1, 4)\n\t\tMAP_BUTTON(2, 5)\n\t\tMAP_BUTTON(3, 6)\n\t\tMAP_BUTTON(4, 7)\n\t\tMAP_BUTTON(5, 8)\n\t\tMAP_BUTTON(6, 9)\n\t\tMAP_BUTTON(7, 10)\n\t\tMAP_BUTTON(8, 11)\n\t\tMAP_BUTTON(9, 12)\n\t\tMAP_BUTTON(10, 13)\n\t\tMAP_BUTTON(11, 14)\n\t\tMAP_BUTTON(12, 15)\n\t\tMAP_BUTTON(13, 16)\n\t\tMAP_BUTTON(14, 17)\n\t\tMAP_BUTTON(15, 18)\n\t\tMAP_BUTTON(16, 19)\n\t\tMAP_BUTTON(17, 20)\n\t\tMAP_BUTTON(18, 21)\n\t\tMAP_BUTTON(19, 22)\n\t\tMAP_BUTTON(20, 23)\n\t\tMAP_BUTTON(21, 24)\n\t\tMAP_BUTTON(22, 25)\n\t\tMAP_BUTTON(23, 26)\n\t\tMAP_BUTTON(24, 27)\n#undef MAP_BUTTON\n\t}\n\n\tnew_key.modifiers = TranslateQtKeyboardEventModifiers(event->modifiers());\n\n\tHandleNewKey(new_key);\n}\n\nvoid OBSHotkeyEdit::HandleNewKey(obs_key_combination_t new_key)\n{\n\tif (new_key == key || obs_key_combination_is_empty(new_key))\n\t\treturn;\n\n\tkey = new_key;\n\n\tchanged = true;\n\temit KeyChanged(key);\n\n\tRenderKey();\n}\n\nvoid OBSHotkeyEdit::RenderKey()\n{\n\tDStr str;\n\tobs_key_combination_to_str(key, str);\n\n\tsetText(QT_UTF8(str));\n}\n\nvoid OBSHotkeyEdit::ResetKey()\n{\n\tkey = original;\n\n\tchanged = false;\n\temit KeyChanged(key);\n\n\tRenderKey();\n}\n\nvoid OBSHotkeyEdit::ClearKey()\n{\n\tkey = {0, OBS_KEY_NONE};\n\n\tchanged = true;\n\temit KeyChanged(key);\n\n\tRenderKey();\n}\n\nvoid OBSHotkeyEdit::UpdateDuplicationState()\n{\n\tif (!dupeIcon && !hasDuplicate)\n\t\treturn;\n\n\tif (!dupeIcon)\n\t\tCreateDupeIcon();\n\n\tif (dupeIcon->isVisible() != hasDuplicate) {\n\t\tdupeIcon->setVisible(hasDuplicate);\n\t\tupdate();\n\t}\n}\n\nvoid OBSHotkeyEdit::InitSignalHandler()\n{\n\tlayoutChanged = {obs_get_signal_handler(), \"hotkey_layout_change\",\n\t\t\t [](void *this_, calldata_t *) {\n\t\t\t\t auto edit = static_cast<OBSHotkeyEdit *>(this_);\n\t\t\t\t QMetaObject::invokeMethod(edit, \"ReloadKeyLayout\");\n\t\t\t },\n\t\t\t this};\n}\n\nvoid OBSHotkeyEdit::CreateDupeIcon()\n{\n\tdupeIcon = addAction(settings->GetHotkeyConflictIcon(), ActionPosition::TrailingPosition);\n\tdupeIcon->setToolTip(QTStr(\"Basic.Settings.Hotkeys.DuplicateWarning\"));\n\tconnect(dupeIcon, &QAction::triggered, this, [this] { emit SearchKey(key); });\n\tdupeIcon->setVisible(false);\n}\n\nvoid OBSHotkeyEdit::ReloadKeyLayout()\n{\n\tRenderKey();\n}\n"
  },
  {
    "path": "frontend/settings/OBSHotkeyEdit.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014-2015 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <obs.hpp>\n\n#include <OBSApp.hpp>\n\n#include <QLineEdit>\n\nclass OBSBasicSettings;\nclass QWidget;\n\nstatic inline bool operator!=(const obs_key_combination_t &c1, const obs_key_combination_t &c2)\n{\n\treturn c1.modifiers != c2.modifiers || c1.key != c2.key;\n}\n\nstatic inline bool operator==(const obs_key_combination_t &c1, const obs_key_combination_t &c2)\n{\n\treturn !(c1 != c2);\n}\n\nclass OBSHotkeyEdit : public QLineEdit {\n\tQ_OBJECT;\n\npublic:\n\tOBSHotkeyEdit(QWidget *parent, obs_key_combination_t original, OBSBasicSettings *settings)\n\t\t: QLineEdit(parent),\n\t\t  original(original),\n\t\t  settings(settings)\n\t{\n\t\tsetAttribute(Qt::WA_InputMethodEnabled, false);\n\t\tsetAttribute(Qt::WA_MacShowFocusRect, true);\n\t\tsetStyle(App()->GetInvisibleCursorStyle());\n\t\tInitSignalHandler();\n\t\tResetKey();\n\t}\n\tOBSHotkeyEdit(QWidget *parent = nullptr) : QLineEdit(parent), original({}), settings(nullptr)\n\t{\n\t\tsetAttribute(Qt::WA_InputMethodEnabled, false);\n\t\tsetAttribute(Qt::WA_MacShowFocusRect, true);\n\t\tsetStyle(App()->GetInvisibleCursorStyle());\n\t\tInitSignalHandler();\n\t\tResetKey();\n\t}\n\n\tobs_key_combination_t original;\n\tobs_key_combination_t key;\n\tOBSBasicSettings *settings;\n\tbool changed = false;\n\n\tvoid UpdateDuplicationState();\n\tbool hasDuplicate = false;\n\tQVariant inputMethodQuery(Qt::InputMethodQuery) const override;\n\nprotected:\n\tOBSSignal layoutChanged;\n\tQAction *dupeIcon = nullptr;\n\n\tvoid InitSignalHandler();\n\tvoid CreateDupeIcon();\n\n\tvoid keyPressEvent(QKeyEvent *event) override;\n#ifdef __APPLE__\n\tvoid keyReleaseEvent(QKeyEvent *event) override;\n#endif\n\tvoid mousePressEvent(QMouseEvent *event) override;\n\n\tvoid RenderKey();\n\npublic slots:\n\tvoid HandleNewKey(obs_key_combination_t new_key);\n\tvoid ReloadKeyLayout();\n\tvoid ResetKey();\n\tvoid ClearKey();\n\nsignals:\n\tvoid KeyChanged(obs_key_combination_t);\n\tvoid SearchKey(obs_key_combination_t);\n};\n"
  },
  {
    "path": "frontend/settings/OBSHotkeyLabel.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014-2015 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSHotkeyLabel.hpp\"\n#include \"OBSHotkeyWidget.hpp\"\n\n#include <QEnterEvent>\n#include <QStyle>\n\n#include \"moc_OBSHotkeyLabel.cpp\"\n\nstatic inline void updateStyle(QWidget *widget)\n{\n\tauto style = widget->style();\n\tstyle->unpolish(widget);\n\tstyle->polish(widget);\n\twidget->update();\n}\n\nvoid OBSHotkeyLabel::highlightPair(bool highlight)\n{\n\tif (!pairPartner)\n\t\treturn;\n\n\tpairPartner->setProperty(\"class\", highlight ? \"text-bright\" : \"\");\n\tupdateStyle(pairPartner);\n\tsetProperty(\"class\", highlight ? \"text-bright\" : \"\");\n\tupdateStyle(this);\n}\n\nvoid OBSHotkeyLabel::enterEvent(QEnterEvent *event)\n{\n\n\tif (!pairPartner)\n\t\treturn;\n\n\tevent->accept();\n\thighlightPair(true);\n}\n\nvoid OBSHotkeyLabel::leaveEvent(QEvent *event)\n{\n\tif (!pairPartner)\n\t\treturn;\n\n\tevent->accept();\n\thighlightPair(false);\n}\n\nvoid OBSHotkeyLabel::setToolTip(const QString &toolTip)\n{\n\tQLabel::setToolTip(toolTip);\n\tif (widget)\n\t\twidget->setToolTip(toolTip);\n}\n"
  },
  {
    "path": "frontend/settings/OBSHotkeyLabel.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014-2015 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QLabel>\n#include <QPointer>\n\nclass OBSHotkeyWidget;\n\nclass OBSHotkeyLabel : public QLabel {\n\tQ_OBJECT\n\npublic:\n\tQPointer<OBSHotkeyLabel> pairPartner;\n\tQPointer<OBSHotkeyWidget> widget;\n\tvoid highlightPair(bool highlight);\n\tvoid enterEvent(QEnterEvent *event) override;\n\tvoid leaveEvent(QEvent *event) override;\n\tvoid setToolTip(const QString &toolTip);\n};\n"
  },
  {
    "path": "frontend/settings/OBSHotkeyWidget.cpp",
    "content": "/******************************************************************************\n Copyright (C) 2014-2015 by Ruwen Hahn <palana@stunned.de>\n \n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2 of the License, or\n (at your option) any later version.\n \n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n \n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>.\n ******************************************************************************/\n\n#include \"OBSHotkeyWidget.hpp\"\n#include \"OBSHotkeyLabel.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <QEnterEvent>\n\n#include \"moc_OBSHotkeyWidget.cpp\"\n\nvoid OBSHotkeyWidget::SetKeyCombinations(const std::vector<obs_key_combination_t> &combos)\n{\n\tif (combos.empty())\n\t\tAddEdit({0, OBS_KEY_NONE});\n\n\tfor (auto combo : combos)\n\t\tAddEdit(combo);\n}\n\nbool OBSHotkeyWidget::Changed() const\n{\n\treturn changed || std::any_of(begin(edits), end(edits), [](OBSHotkeyEdit *edit) { return edit->changed; });\n}\n\nvoid OBSHotkeyWidget::Apply()\n{\n\tfor (auto &edit : edits) {\n\t\tedit->original = edit->key;\n\t\tedit->changed = false;\n\t}\n\n\tchanged = false;\n\n\tfor (auto &revertButton : revertButtons)\n\t\trevertButton->setEnabled(false);\n}\n\nvoid OBSHotkeyWidget::GetCombinations(std::vector<obs_key_combination_t> &combinations) const\n{\n\tcombinations.clear();\n\tfor (auto &edit : edits)\n\t\tif (!obs_key_combination_is_empty(edit->key))\n\t\t\tcombinations.emplace_back(edit->key);\n}\n\nvoid OBSHotkeyWidget::Save()\n{\n\tstd::vector<obs_key_combination_t> combinations;\n\tSave(combinations);\n}\n\nvoid OBSHotkeyWidget::Save(std::vector<obs_key_combination_t> &combinations)\n{\n\tGetCombinations(combinations);\n\tApply();\n\n\tauto AtomicUpdate = [&]() {\n\t\tignoreChangedBindings = true;\n\n\t\tobs_hotkey_load_bindings(id, combinations.data(), combinations.size());\n\n\t\tignoreChangedBindings = false;\n\t};\n\tusing AtomicUpdate_t = decltype(&AtomicUpdate);\n\n\tobs_hotkey_update_atomic([](void *d) { (*static_cast<AtomicUpdate_t>(d))(); },\n\t\t\t\t static_cast<void *>(&AtomicUpdate));\n}\n\nvoid OBSHotkeyWidget::AddEdit(obs_key_combination combo, int idx)\n{\n\tauto edit = new OBSHotkeyEdit(parentWidget(), combo, settings);\n\tedit->setToolTip(toolTip);\n\n\tauto revert = new QPushButton;\n\trevert->setProperty(\"class\", \"icon-revert\");\n\trevert->setToolTip(QTStr(\"Revert\"));\n\trevert->setEnabled(false);\n\n\tauto clear = new QPushButton;\n\tclear->setProperty(\"class\", \"icon-clear\");\n\tclear->setToolTip(QTStr(\"Clear\"));\n\tclear->setEnabled(!obs_key_combination_is_empty(combo));\n\n\tQObject::connect(edit, &OBSHotkeyEdit::KeyChanged, this, [=](obs_key_combination_t new_combo) {\n\t\tclear->setEnabled(!obs_key_combination_is_empty(new_combo));\n\t\trevert->setEnabled(edit->original != new_combo);\n\t});\n\n\tauto add = new QPushButton;\n\tadd->setProperty(\"class\", \"icon-plus\");\n\tadd->setToolTip(QTStr(\"Add\"));\n\n\tauto remove = new QPushButton;\n\tremove->setProperty(\"class\", \"icon-trash\");\n\tremove->setToolTip(QTStr(\"Remove\"));\n\tremove->setEnabled(removeButtons.size() > 0);\n\n\tauto CurrentIndex = [&, remove] {\n\t\tauto res = std::find(begin(removeButtons), end(removeButtons), remove);\n\t\treturn std::distance(begin(removeButtons), res);\n\t};\n\n\tQObject::connect(add, &QPushButton::clicked, this,\n\t\t\t [&, CurrentIndex] { AddEdit({0, OBS_KEY_NONE}, CurrentIndex() + 1); });\n\n\tQObject::connect(remove, &QPushButton::clicked, this, [&, CurrentIndex] { RemoveEdit(CurrentIndex()); });\n\n\tQHBoxLayout *subLayout = new QHBoxLayout;\n\tsubLayout->setContentsMargins(0, 2, 0, 2);\n\tsubLayout->addWidget(edit);\n\tsubLayout->addWidget(revert);\n\tsubLayout->addWidget(clear);\n\tsubLayout->addWidget(add);\n\tsubLayout->addWidget(remove);\n\n\tif (removeButtons.size() == 1)\n\t\tremoveButtons.front()->setEnabled(true);\n\n\tif (idx != -1) {\n\t\trevertButtons.insert(begin(revertButtons) + idx, revert);\n\t\tremoveButtons.insert(begin(removeButtons) + idx, remove);\n\t\tedits.insert(begin(edits) + idx, edit);\n\t} else {\n\t\trevertButtons.emplace_back(revert);\n\t\tremoveButtons.emplace_back(remove);\n\t\tedits.emplace_back(edit);\n\t}\n\n\tlayout()->insertLayout(idx, subLayout);\n\n\tQObject::connect(revert, &QPushButton::clicked, edit, &OBSHotkeyEdit::ResetKey);\n\tQObject::connect(clear, &QPushButton::clicked, edit, &OBSHotkeyEdit::ClearKey);\n\n\tQObject::connect(edit, &OBSHotkeyEdit::KeyChanged, this, [&](obs_key_combination) { emit KeyChanged(); });\n\tQObject::connect(edit, &OBSHotkeyEdit::SearchKey, this,\n\t\t\t [this](obs_key_combination combo) { emit SearchKey(combo); });\n}\n\nvoid OBSHotkeyWidget::RemoveEdit(size_t idx, bool signal)\n{\n\tauto &edit = *(begin(edits) + idx);\n\tif (!obs_key_combination_is_empty(edit->original) && signal) {\n\t\tchanged = true;\n\t}\n\n\trevertButtons.erase(begin(revertButtons) + idx);\n\tremoveButtons.erase(begin(removeButtons) + idx);\n\tedits.erase(begin(edits) + idx);\n\n\tauto item = layout()->takeAt(static_cast<int>(idx));\n\tQLayoutItem *child = nullptr;\n\twhile ((child = item->layout()->takeAt(0))) {\n\t\tdelete child->widget();\n\t\tdelete child;\n\t}\n\tdelete item;\n\n\tif (removeButtons.size() == 1)\n\t\tremoveButtons.front()->setEnabled(false);\n\n\temit KeyChanged();\n}\n\nvoid OBSHotkeyWidget::BindingsChanged(void *data, calldata_t *param)\n{\n\tauto widget = static_cast<OBSHotkeyWidget *>(data);\n\tauto key = static_cast<obs_hotkey_t *>(calldata_ptr(param, \"key\"));\n\n\tQMetaObject::invokeMethod(widget, \"HandleChangedBindings\", Q_ARG(obs_hotkey_id, obs_hotkey_get_id(key)));\n}\n\nvoid OBSHotkeyWidget::HandleChangedBindings(obs_hotkey_id id_)\n{\n\tif (ignoreChangedBindings || id != id_)\n\t\treturn;\n\n\tstd::vector<obs_key_combination_t> bindings;\n\tauto LoadBindings = [&](obs_hotkey_binding_t *binding) {\n\t\tif (obs_hotkey_binding_get_hotkey_id(binding) != id)\n\t\t\treturn;\n\n\t\tauto get_combo = obs_hotkey_binding_get_key_combination;\n\t\tbindings.push_back(get_combo(binding));\n\t};\n\tusing LoadBindings_t = decltype(&LoadBindings);\n\n\tobs_enum_hotkey_bindings(\n\t\t[](void *data, size_t, obs_hotkey_binding_t *binding) {\n\t\t\tauto LoadBindings = *static_cast<LoadBindings_t>(data);\n\t\t\tLoadBindings(binding);\n\t\t\treturn true;\n\t\t},\n\t\tstatic_cast<void *>(&LoadBindings));\n\n\twhile (edits.size() > 0)\n\t\tRemoveEdit(edits.size() - 1, false);\n\n\tSetKeyCombinations(bindings);\n}\n\nvoid OBSHotkeyWidget::enterEvent(QEnterEvent *event)\n{\n\tif (!label)\n\t\treturn;\n\n\tevent->accept();\n\tlabel->highlightPair(true);\n}\n\nvoid OBSHotkeyWidget::leaveEvent(QEvent *event)\n{\n\tif (!label)\n\t\treturn;\n\n\tevent->accept();\n\tlabel->highlightPair(false);\n}\n"
  },
  {
    "path": "frontend/settings/OBSHotkeyWidget.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014-2015 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"OBSHotkeyEdit.hpp\"\n\n#include <QPointer>\n#include <QPushButton>\n#include <QVBoxLayout>\n#include <QWidget>\n\nclass OBSBasicSettings;\nclass OBSHotkeyLabel;\n\nclass OBSHotkeyWidget : public QWidget {\n\tQ_OBJECT;\n\npublic:\n\tOBSHotkeyWidget(QWidget *parent, obs_hotkey_id id, std::string name, OBSBasicSettings *settings,\n\t\t\tconst std::vector<obs_key_combination_t> &combos = {})\n\t\t: QWidget(parent),\n\t\t  id(id),\n\t\t  name(name),\n\t\t  bindingsChanged(obs_get_signal_handler(), \"hotkey_bindings_changed\",\n\t\t\t\t  &OBSHotkeyWidget::BindingsChanged, this),\n\t\t  settings(settings)\n\t{\n\t\tauto layout = new QVBoxLayout;\n\t\tlayout->setSpacing(0);\n\t\tlayout->setContentsMargins(0, 0, 0, 0);\n\t\tsetLayout(layout);\n\n\t\tSetKeyCombinations(combos);\n\t}\n\n\tvoid SetKeyCombinations(const std::vector<obs_key_combination_t> &);\n\n\tobs_hotkey_id id;\n\tstd::string name;\n\n\tbool changed = false;\n\tbool Changed() const;\n\n\tQPointer<OBSHotkeyLabel> label;\n\tstd::vector<QPointer<OBSHotkeyEdit>> edits;\n\n\tQString toolTip;\n\tvoid setToolTip(const QString &toolTip_)\n\t{\n\t\ttoolTip = toolTip_;\n\t\tfor (auto &edit : edits)\n\t\t\tedit->setToolTip(toolTip_);\n\t}\n\n\tvoid Apply();\n\tvoid GetCombinations(std::vector<obs_key_combination_t> &) const;\n\tvoid Save();\n\tvoid Save(std::vector<obs_key_combination_t> &combinations);\n\n\tvoid enterEvent(QEnterEvent *event) override;\n\tvoid leaveEvent(QEvent *event) override;\n\nprivate:\n\tvoid AddEdit(obs_key_combination combo, int idx = -1);\n\tvoid RemoveEdit(size_t idx, bool signal = true);\n\n\tstatic void BindingsChanged(void *data, calldata_t *param);\n\n\tstd::vector<QPointer<QPushButton>> removeButtons;\n\tstd::vector<QPointer<QPushButton>> revertButtons;\n\tOBSSignal bindingsChanged;\n\tbool ignoreChangedBindings = false;\n\tOBSBasicSettings *settings;\n\n\tQVBoxLayout *layout() const { return dynamic_cast<QVBoxLayout *>(QWidget::layout()); }\n\nprivate slots:\n\tvoid HandleChangedBindings(obs_hotkey_id id_);\n\nsignals:\n\tvoid KeyChanged();\n\tvoid SearchKey(obs_key_combination_t);\n};\n"
  },
  {
    "path": "frontend/updater/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\nfind_package(zstd)\nfind_package(nlohmann_json 3.11 REQUIRED)\n\nadd_executable(updater WIN32)\n\ntarget_sources(\n  updater\n  PRIVATE\n    hash.cpp\n    helpers.cpp\n    helpers.hpp\n    http.cpp\n    init-hook-files.c\n    manifest.hpp\n    patch.cpp\n    resource.h\n    updater.cpp\n    updater.hpp\n    updater.manifest\n    updater.rc\n)\n\ntarget_compile_definitions(updater PRIVATE \"PSAPI_VERSION=2\")\n\ntarget_include_directories(updater PRIVATE \"${CMAKE_SOURCE_DIR}/libobs\" \"${CMAKE_SOURCE_DIR}/frontend/utility\")\n\ntarget_link_libraries(\n  updater\n  PRIVATE OBS::blake2_static nlohmann_json::nlohmann_json zstd::libzstd_static comctl32 shell32 version winhttp wintrust\n)\n\n# zstd is hardcoded with /DEFAULTLIB:LIBCMT\ntarget_link_options(\n  updater\n  PRIVATE\n    $<$<CONFIG:DEBUG>:/NODEFAULTLIB:LIBCMT>\n    \"$<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>:/PDBALTPATH:$<TARGET_PDB_FILE_NAME:updater>>\"\n    \"$<$<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>:/PDBSTRIPPED:$<TARGET_PDB_FILE_NAME:updater>>\"\n)\n\nset_target_properties(\n  updater\n  PROPERTIES FOLDER frontend OUTPUT_NAME updater MSVC_RUNTIME_LIBRARY \"MultiThreaded$<$<CONFIG:Debug>:Debug>\"\n)\n"
  },
  {
    "path": "frontend/updater/hash.cpp",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"updater.hpp\"\n\n#include <util/windows/WinHandle.hpp>\n#include <vector>\n\nusing namespace std;\n\nvoid HashToString(const B2Hash &in, string &out)\n{\n\tconstexpr char alphabet[] = \"0123456789abcdef\";\n\tout.resize(kBlake2StrLength);\n\n\tfor (size_t i = 0; i != kBlake2HashLength; ++i) {\n\t\tout[2 * i] = alphabet[(uint8_t)in[i] / 16];\n\t\tout[2 * i + 1] = alphabet[(uint8_t)in[i] % 16];\n\t}\n}\n\nvoid StringToHash(const string &in, B2Hash &out)\n{\n\tunsigned int temp;\n\tconst char *str = in.c_str();\n\n\tfor (size_t i = 0; i < kBlake2HashLength; i++) {\n\t\tsscanf_s(str + i * 2, \"%02x\", &temp);\n\t\tout[i] = static_cast<std::byte>(temp);\n\t}\n}\n\nbool CalculateFileHash(const wchar_t *path, B2Hash &hash)\n{\n\tstatic __declspec(thread) vector<BYTE> hashBuffer;\n\tblake2b_state blake2;\n\tif (blake2b_init(&blake2, kBlake2HashLength) != 0)\n\t\treturn false;\n\n\thashBuffer.resize(1048576);\n\n\tWinHandle handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);\n\tif (handle == INVALID_HANDLE_VALUE)\n\t\treturn false;\n\n\tfor (;;) {\n\t\tDWORD read = 0;\n\t\tif (!ReadFile(handle, hashBuffer.data(), (DWORD)hashBuffer.size(), &read, nullptr))\n\t\t\treturn false;\n\n\t\tif (!read)\n\t\t\tbreak;\n\n\t\tif (blake2b_update(&blake2, hashBuffer.data(), read) != 0)\n\t\t\treturn false;\n\t}\n\n\tif (blake2b_final(&blake2, hash.data(), hash.size()) != 0)\n\t\treturn false;\n\n\treturn true;\n}\n"
  },
  {
    "path": "frontend/updater/helpers.cpp",
    "content": "#include \"helpers.hpp\"\n\nvoid FreeProvider(HCRYPTPROV prov)\n{\n\tCryptReleaseContext(prov, 0);\n}\n"
  },
  {
    "path": "frontend/updater/helpers.hpp",
    "content": "#pragma once\n\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#include <Wincrypt.h>\n\n/* ------------------------------------------------------------------------ */\n\ntemplate<typename T, void freefunc(T)> class CustomHandle {\n\tT handle;\n\npublic:\n\tinline CustomHandle() : handle(0) {}\n\tinline CustomHandle(T in) : handle(in) {}\n\tinline ~CustomHandle()\n\t{\n\t\tif (handle)\n\t\t\tfreefunc(handle);\n\t}\n\n\tinline T *operator&() { return &handle; }\n\tinline operator T() const { return handle; }\n\tinline T get() const { return handle; }\n\n\tinline CustomHandle<T, freefunc> &operator=(T in)\n\t{\n\t\tif (handle)\n\t\t\tfreefunc(handle);\n\t\thandle = in;\n\t\treturn *this;\n\t}\n\n\tinline bool operator!() const { return !handle; }\n};\n\nvoid FreeProvider(HCRYPTPROV prov);\n\nusing CryptProvider = CustomHandle<HCRYPTPROV, FreeProvider>;\n"
  },
  {
    "path": "frontend/updater/http.cpp",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"Updater.hpp\"\n\n#include <algorithm>\n\nusing namespace std;\n\n#define MAX_BUF_SIZE 262144\n#define READ_BUF_SIZE 32768\n\n/* ------------------------------------------------------------------------ */\n\nstatic bool ReadHTTPData(string &responseBuf, const uint8_t *buffer, DWORD outSize)\n{\n\ttry {\n\t\tresponseBuf.append((const char *)buffer, outSize);\n\t} catch (...) {\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nbool HTTPPostData(const wchar_t *url, const BYTE *data, int dataLen, const wchar_t *extraHeaders, int *responseCode,\n\t\t  string &responseBuf)\n{\n\tHttpHandle hSession;\n\tHttpHandle hConnect;\n\tHttpHandle hRequest;\n\tURL_COMPONENTS urlComponents = {};\n\tbool secure = false;\n\n\twchar_t hostName[256];\n\twchar_t path[1024];\n\n\tconst wchar_t *acceptTypes[] = {L\"*/*\", nullptr};\n\n\tconst DWORD tlsProtocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;\n\tconst DWORD compressionFlags = WINHTTP_DECOMPRESSION_FLAG_ALL;\n\n\tresponseBuf.clear();\n\n\t/* -------------------------------------- *\n\t * get URL components                     */\n\n\turlComponents.dwStructSize = sizeof(urlComponents);\n\n\turlComponents.lpszHostName = hostName;\n\turlComponents.dwHostNameLength = _countof(hostName);\n\n\turlComponents.lpszUrlPath = path;\n\turlComponents.dwUrlPathLength = _countof(path);\n\n\tWinHttpCrackUrl(url, 0, 0, &urlComponents);\n\n\tif (urlComponents.nPort == 443)\n\t\tsecure = true;\n\n\t/* -------------------------------------- *\n\t * connect to server                      */\n\n\thSession = WinHttpOpen(L\"OBS Studio Updater/3.0\", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME,\n\t\t\t       WINHTTP_NO_PROXY_BYPASS, 0);\n\tif (!hSession) {\n\t\t*responseCode = -1;\n\t\treturn false;\n\t}\n\n\tWinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, (LPVOID)&tlsProtocols, sizeof(tlsProtocols));\n\tWinHttpSetOption(hSession, WINHTTP_OPTION_DECOMPRESSION, (LPVOID)&compressionFlags, sizeof(compressionFlags));\n\n\thConnect = WinHttpConnect(hSession, hostName, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT,\n\t\t\t\t  0);\n\tif (!hConnect) {\n\t\t*responseCode = -2;\n\t\treturn false;\n\t}\n\n\t/* -------------------------------------- *\n\t * request data                           */\n\n\thRequest = WinHttpOpenRequest(hConnect, L\"POST\", path, nullptr, WINHTTP_NO_REFERER, acceptTypes,\n\t\t\t\t      secure ? WINHTTP_FLAG_SECURE | WINHTTP_FLAG_REFRESH : WINHTTP_FLAG_REFRESH);\n\tif (!hRequest) {\n\t\t*responseCode = -3;\n\t\treturn false;\n\t}\n\n\tbool bResults =\n\t\t!!WinHttpSendRequest(hRequest, extraHeaders, extraHeaders ? -1 : 0, (void *)data, dataLen, dataLen, 0);\n\n\t/* -------------------------------------- *\n\t * end request                            */\n\n\tif (bResults) {\n\t\tbResults = !!WinHttpReceiveResponse(hRequest, nullptr);\n\t} else {\n\t\t*responseCode = GetLastError();\n\t\treturn false;\n\t}\n\n\t/* -------------------------------------- *\n\t * get headers                            */\n\n\twchar_t statusCode[8];\n\tDWORD statusCodeLen;\n\n\tstatusCodeLen = sizeof(statusCode);\n\tif (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE, WINHTTP_HEADER_NAME_BY_INDEX, &statusCode,\n\t\t\t\t &statusCodeLen, WINHTTP_NO_HEADER_INDEX)) {\n\t\t*responseCode = -4;\n\t\treturn false;\n\t} else {\n\t\tstatusCode[_countof(statusCode) - 1] = 0;\n\t}\n\n\t/* -------------------------------------- *\n\t * allocate response data                 */\n\n\tDWORD responseBufSize = MAX_BUF_SIZE;\n\n\ttry {\n\t\tresponseBuf.reserve(responseBufSize);\n\t} catch (...) {\n\t\t*responseCode = -6;\n\t\treturn false;\n\t}\n\n\t/* -------------------------------------- *\n\t * read data                              */\n\n\t*responseCode = wcstoul(statusCode, nullptr, 10);\n\n\t/* are we supposed to return true here? */\n\tif (!bResults || *responseCode != 200)\n\t\treturn true;\n\n\tBYTE buffer[READ_BUF_SIZE];\n\tDWORD dwSize, outSize;\n\n\tdo {\n\t\t/* Check for available data. */\n\t\tdwSize = 0;\n\t\tif (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {\n\t\t\t*responseCode = -8;\n\t\t\treturn false;\n\t\t}\n\n\t\tdwSize = std::min(dwSize, (DWORD)sizeof(buffer));\n\n\t\tif (!WinHttpReadData(hRequest, (void *)buffer, dwSize, &outSize)) {\n\t\t\t*responseCode = -9;\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!outSize)\n\t\t\tbreak;\n\n\t\tif (!ReadHTTPData(responseBuf, buffer, outSize)) {\n\t\t\t*responseCode = -6;\n\t\t\treturn false;\n\t\t}\n\n\t\tif (WaitForSingleObject(cancelRequested, 0) == WAIT_OBJECT_0) {\n\t\t\t*responseCode = -14;\n\t\t\treturn false;\n\t\t}\n\t} while (dwSize > 0);\n\n\treturn true;\n}\n\n/* ------------------------------------------------------------------------ */\n\nstatic bool ReadHTTPFile(HANDLE updateFile, const uint8_t *buffer, DWORD outSize, int *responseCode)\n{\n\tDWORD written;\n\tif (!WriteFile(updateFile, buffer, outSize, &written, nullptr)) {\n\t\t*responseCode = -12;\n\t\treturn false;\n\t}\n\n\tif (written != outSize) {\n\t\t*responseCode = -13;\n\t\treturn false;\n\t}\n\n\tcompletedFileSize += outSize;\n\treturn true;\n}\n\nstatic void UpdateProgressBar()\n{\n\tint position = (int)(((float)completedFileSize / (float)totalFileSize) * 100.0f);\n\tint oldPosition = lastPosition;\n\n\tif (position > oldPosition &&\n\t    lastPosition.compare_exchange_strong(oldPosition, position, memory_order_relaxed)) {\n\t\tSendDlgItemMessage(hwndMain, IDC_PROGRESS, PBM_SETPOS, position, 0);\n\t}\n}\n\nbool HTTPGetFile(HINTERNET hConnect, const wchar_t *url, const wchar_t *outputPath, const wchar_t *extraHeaders,\n\t\t int *responseCode)\n{\n\tHttpHandle hRequest;\n\n\tconst wchar_t *acceptTypes[] = {L\"*/*\", nullptr};\n\n\tURL_COMPONENTS urlComponents = {};\n\tbool secure = false;\n\n\twchar_t hostName[256];\n\twchar_t path[1024];\n\n\t/* -------------------------------------- *\n\t * get URL components                     */\n\n\turlComponents.dwStructSize = sizeof(urlComponents);\n\n\turlComponents.lpszHostName = hostName;\n\turlComponents.dwHostNameLength = _countof(hostName);\n\n\turlComponents.lpszUrlPath = path;\n\turlComponents.dwUrlPathLength = _countof(path);\n\n\tWinHttpCrackUrl(url, 0, 0, &urlComponents);\n\n\tif (urlComponents.nPort == 443)\n\t\tsecure = true;\n\n\t/* -------------------------------------- *\n\t * request data                           */\n\n\thRequest = WinHttpOpenRequest(hConnect, L\"GET\", path, nullptr, WINHTTP_NO_REFERER, acceptTypes,\n\t\t\t\t      secure ? WINHTTP_FLAG_SECURE | WINHTTP_FLAG_REFRESH : WINHTTP_FLAG_REFRESH);\n\tif (!hRequest) {\n\t\t*responseCode = -3;\n\t\treturn false;\n\t}\n\n\tbool bResults =\n\t\t!!WinHttpSendRequest(hRequest, extraHeaders, extraHeaders ? -1 : 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);\n\n\t/* -------------------------------------- *\n\t * end request                            */\n\n\tif (bResults) {\n\t\tbResults = !!WinHttpReceiveResponse(hRequest, nullptr);\n\t} else {\n\t\t*responseCode = GetLastError();\n\t\treturn false;\n\t}\n\n\t/* -------------------------------------- *\n\t * get headers                            */\n\n\twchar_t statusCode[8];\n\tDWORD statusCodeLen;\n\n\tstatusCodeLen = sizeof(statusCode);\n\tif (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE, WINHTTP_HEADER_NAME_BY_INDEX, &statusCode,\n\t\t\t\t &statusCodeLen, WINHTTP_NO_HEADER_INDEX)) {\n\t\t*responseCode = -4;\n\t\treturn false;\n\t} else {\n\t\tstatusCode[_countof(statusCode) - 1] = 0;\n\t}\n\n\t/* -------------------------------------- *\n\t * read data                              */\n\n\t*responseCode = wcstoul(statusCode, nullptr, 10);\n\n\t/* are we supposed to return true here? */\n\tif (!bResults || *responseCode != 200)\n\t\treturn true;\n\n\tBYTE buffer[READ_BUF_SIZE];\n\tDWORD dwSize, outSize;\n\n\tWinHandle updateFile = CreateFile(outputPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);\n\tif (!updateFile.Valid()) {\n\t\t*responseCode = -7;\n\t\treturn false;\n\t}\n\n\tdo {\n\t\t/* Check for available data. */\n\t\tdwSize = 0;\n\t\tif (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {\n\t\t\t*responseCode = -8;\n\t\t\treturn false;\n\t\t}\n\n\t\tdwSize = std::min(dwSize, (DWORD)sizeof(buffer));\n\n\t\tif (!WinHttpReadData(hRequest, (void *)buffer, dwSize, &outSize)) {\n\t\t\t*responseCode = -9;\n\t\t\treturn false;\n\t\t} else {\n\t\t\tif (!outSize)\n\t\t\t\tbreak;\n\n\t\t\tif (!ReadHTTPFile(updateFile, buffer, outSize, responseCode))\n\t\t\t\treturn false;\n\n\t\t\tUpdateProgressBar();\n\t\t}\n\n\t\tif (WaitForSingleObject(cancelRequested, 0) == WAIT_OBJECT_0) {\n\t\t\t*responseCode = -14;\n\t\t\treturn false;\n\t\t}\n\n\t} while (dwSize > 0);\n\n\treturn true;\n}\n\nbool HTTPGetBuffer(HINTERNET hConnect, const wchar_t *url, const wchar_t *extraHeaders, vector<std::byte> &out,\n\t\t   int *responseCode)\n{\n\tHttpHandle hRequest;\n\n\tconst wchar_t *acceptTypes[] = {L\"*/*\", nullptr};\n\n\tURL_COMPONENTS urlComponents = {};\n\tbool secure = false;\n\n\twchar_t hostName[256];\n\twchar_t path[1024];\n\n\t/* -------------------------------------- *\n\t * get URL components                     */\n\n\turlComponents.dwStructSize = sizeof(urlComponents);\n\n\turlComponents.lpszHostName = hostName;\n\turlComponents.dwHostNameLength = _countof(hostName);\n\n\turlComponents.lpszUrlPath = path;\n\turlComponents.dwUrlPathLength = _countof(path);\n\n\tWinHttpCrackUrl(url, 0, 0, &urlComponents);\n\n\tif (urlComponents.nPort == 443)\n\t\tsecure = true;\n\n\t/* -------------------------------------- *\n\t * request data                           */\n\n\thRequest = WinHttpOpenRequest(hConnect, L\"GET\", path, nullptr, WINHTTP_NO_REFERER, acceptTypes,\n\t\t\t\t      secure ? WINHTTP_FLAG_SECURE | WINHTTP_FLAG_REFRESH : WINHTTP_FLAG_REFRESH);\n\tif (!hRequest) {\n\t\t*responseCode = -3;\n\t\treturn false;\n\t}\n\n\tbool bResults =\n\t\t!!WinHttpSendRequest(hRequest, extraHeaders, extraHeaders ? -1 : 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);\n\n\t/* -------------------------------------- *\n\t * end request                            */\n\n\tif (bResults) {\n\t\tbResults = !!WinHttpReceiveResponse(hRequest, nullptr);\n\t} else {\n\t\t*responseCode = GetLastError();\n\t\treturn false;\n\t}\n\n\t/* -------------------------------------- *\n\t * get headers                            */\n\n\twchar_t statusCode[8];\n\tDWORD statusCodeLen;\n\n\tstatusCodeLen = sizeof(statusCode);\n\tif (!WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE, WINHTTP_HEADER_NAME_BY_INDEX, &statusCode,\n\t\t\t\t &statusCodeLen, WINHTTP_NO_HEADER_INDEX)) {\n\t\t*responseCode = -4;\n\t\treturn false;\n\t} else {\n\t\tstatusCode[_countof(statusCode) - 1] = 0;\n\t}\n\n\t/* -------------------------------------- *\n\t * read data                              */\n\n\t*responseCode = wcstoul(statusCode, nullptr, 10);\n\n\t/* are we supposed to return true here? */\n\tif (!bResults || *responseCode != 200)\n\t\treturn true;\n\n\tBYTE buffer[READ_BUF_SIZE];\n\tDWORD dwSize, outSize;\n\n\tdo {\n\t\t/* Check for available data. */\n\t\tdwSize = 0;\n\t\tif (!WinHttpQueryDataAvailable(hRequest, &dwSize)) {\n\t\t\t*responseCode = -8;\n\t\t\treturn false;\n\t\t}\n\n\t\tdwSize = std::min(dwSize, (DWORD)sizeof(buffer));\n\n\t\tif (!WinHttpReadData(hRequest, (void *)buffer, dwSize, &outSize)) {\n\t\t\t*responseCode = -9;\n\t\t\treturn false;\n\t\t} else {\n\t\t\tif (!outSize)\n\t\t\t\tbreak;\n\n\t\t\tout.insert(out.end(), (std::byte *)buffer, (std::byte *)buffer + outSize);\n\n\t\t\tcompletedFileSize += outSize;\n\t\t\tUpdateProgressBar();\n\t\t}\n\n\t\tif (WaitForSingleObject(cancelRequested, 0) == WAIT_OBJECT_0) {\n\t\t\t*responseCode = -14;\n\t\t\treturn false;\n\t\t}\n\n\t} while (dwSize > 0);\n\n\treturn true;\n}\n"
  },
  {
    "path": "frontend/updater/init-hook-files.c",
    "content": "#include <windows.h>\n#include <strsafe.h>\n#include <shlobj.h>\n#include <stdbool.h>\n#include <aclapi.h>\n#include <sddl.h>\n\nstatic bool add_aap_perms(const wchar_t *dir)\n{\n\tPSECURITY_DESCRIPTOR sd = NULL;\n\tSID *aap_sid = NULL;\n\tSID *bu_sid = NULL;\n\tPACL new_dacl1 = NULL;\n\tPACL new_dacl2 = NULL;\n\tbool success = false;\n\n\tPACL dacl;\n\tif (GetNamedSecurityInfoW(dir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &dacl, NULL, &sd) !=\n\t    ERROR_SUCCESS) {\n\t\tgoto fail;\n\t}\n\n\tEXPLICIT_ACCESSW ea = {0};\n\tea.grfAccessPermissions = GENERIC_READ | GENERIC_EXECUTE;\n\tea.grfAccessMode = GRANT_ACCESS;\n\tea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;\n\tea.Trustee.TrusteeForm = TRUSTEE_IS_SID;\n\n\t/* ALL_APP_PACKAGES */\n\tConvertStringSidToSidW(L\"S-1-15-2-1\", &aap_sid);\n\tea.Trustee.ptstrName = (wchar_t *)aap_sid;\n\n\tif (SetEntriesInAclW(1, &ea, dacl, &new_dacl1) != ERROR_SUCCESS) {\n\t\tgoto fail;\n\t}\n\n\tea.grfAccessPermissions = GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE;\n\n\t/* BUILTIN_USERS */\n\tConvertStringSidToSidW(L\"S-1-5-32-545\", &bu_sid);\n\tea.Trustee.ptstrName = (wchar_t *)bu_sid;\n\n\tDWORD s = SetEntriesInAclW(1, &ea, new_dacl1, &new_dacl2);\n\tif (s != ERROR_SUCCESS) {\n\t\tgoto fail;\n\t}\n\n\tif (SetNamedSecurityInfoW((wchar_t *)dir, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl2,\n\t\t\t\t  NULL) != ERROR_SUCCESS) {\n\t\tgoto fail;\n\t}\n\n\tsuccess = true;\nfail:\n\tif (sd)\n\t\tLocalFree(sd);\n\tif (new_dacl1)\n\t\tLocalFree(new_dacl1);\n\tif (new_dacl2)\n\t\tLocalFree(new_dacl2);\n\tif (aap_sid)\n\t\tLocalFree(aap_sid);\n\tif (bu_sid)\n\t\tLocalFree(bu_sid);\n\treturn success;\n}\n\nstatic inline bool file_exists(const wchar_t *path)\n{\n\tWIN32_FIND_DATAW wfd;\n\tHANDLE h = FindFirstFileW(path, &wfd);\n\tif (h == INVALID_HANDLE_VALUE)\n\t\treturn false;\n\tFindClose(h);\n\treturn true;\n}\n\nstatic LSTATUS get_reg(HKEY hkey, LPCWSTR sub_key, LPCWSTR value_name, bool b64)\n{\n\tHKEY key;\n\tLSTATUS status;\n\tDWORD flags = b64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;\n\tDWORD size = sizeof(DWORD);\n\tDWORD val;\n\n\tstatus = RegOpenKeyEx(hkey, sub_key, 0, KEY_READ | flags, &key);\n\tif (status == ERROR_SUCCESS) {\n\t\tstatus = RegQueryValueExW(key, value_name, NULL, NULL, (LPBYTE)&val, &size);\n\t\tRegCloseKey(key);\n\t}\n\treturn status;\n}\n\n#define get_programdata_path(path, subpath)                                                   \\\n\tdo {                                                                                  \\\n\t\tSHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, path); \\\n\t\tStringCbCatW(path, sizeof(path), L\"\\\\\");                                      \\\n\t\tStringCbCatW(path, sizeof(path), subpath);                                    \\\n\t} while (false)\n\n#define make_filename(str, name, ext)                                \\\n\tdo {                                                         \\\n\t\tStringCbCatW(str, sizeof(str), name);                \\\n\t\tStringCbCatW(str, sizeof(str), b64 ? L\"64\" : L\"32\"); \\\n\t\tStringCbCatW(str, sizeof(str), ext);                 \\\n\t} while (false)\n\n#define IMPLICIT_LAYERS L\"SOFTWARE\\\\Khronos\\\\Vulkan\\\\ImplicitLayers\"\n#define HOOK_LOCATION L\"\\\\data\\\\obs-plugins\\\\win-capture\\\\\"\n\nstatic bool update_hook_file(bool b64)\n{\n\twchar_t temp[MAX_PATH];\n\twchar_t src[MAX_PATH];\n\twchar_t dst[MAX_PATH];\n\twchar_t src_json[MAX_PATH];\n\twchar_t dst_json[MAX_PATH];\n\n\tGetCurrentDirectoryW(_countof(src_json), src_json);\n\tStringCbCat(src_json, sizeof(src_json), HOOK_LOCATION);\n\tmake_filename(src_json, L\"obs-vulkan\", L\".json\");\n\n\tGetCurrentDirectoryW(_countof(src), src);\n\tStringCbCat(src, sizeof(src), HOOK_LOCATION);\n\tmake_filename(src, L\"graphics-hook\", L\".dll\");\n\n\tget_programdata_path(temp, L\"obs-studio-hook\\\\\");\n\tStringCbCopyW(dst_json, sizeof(dst_json), temp);\n\tStringCbCopyW(dst, sizeof(dst), temp);\n\tmake_filename(dst_json, L\"obs-vulkan\", L\".json\");\n\tmake_filename(dst, L\"graphics-hook\", L\".dll\");\n\n\tif (!file_exists(src)) {\n\t\treturn false;\n\t}\n\n\tCreateDirectoryW(temp, NULL);\n\tadd_aap_perms(temp);\n\tCopyFileW(src, dst, false);\n\tCopyFileW(src_json, dst_json, false);\n\treturn true;\n}\n\nstatic void update_vulkan_registry(bool b64)\n{\n\tDWORD flags = b64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;\n\twchar_t path[MAX_PATH];\n\tDWORD temp;\n\tLSTATUS s;\n\tHKEY key;\n\n\tget_programdata_path(path, L\"obs-studio-hook\\\\\");\n\tmake_filename(path, L\"obs-vulkan\", L\".json\");\n\n\ts = get_reg(HKEY_CURRENT_USER, IMPLICIT_LAYERS, path, b64);\n\tif (s == ERROR_SUCCESS) {\n\t\ts = RegOpenKeyEx(HKEY_CURRENT_USER, IMPLICIT_LAYERS, 0, KEY_WRITE | flags, &key);\n\t\tif (s == ERROR_SUCCESS) {\n\t\t\tRegDeleteValueW(key, path);\n\t\t\tRegCloseKey(key);\n\t\t}\n\t}\n\n\ts = get_reg(HKEY_LOCAL_MACHINE, IMPLICIT_LAYERS, path, b64);\n\tif (s == ERROR_SUCCESS) {\n\t\treturn;\n\t}\n\n\t/* ------------------- */\n\n\ts = RegCreateKeyExW(HKEY_LOCAL_MACHINE, IMPLICIT_LAYERS, 0, NULL, 0, KEY_WRITE | flags, NULL, &key, &temp);\n\tif (s != ERROR_SUCCESS) {\n\t\tgoto finish;\n\t}\n\n\tDWORD zero = 0;\n\ts = RegSetValueExW(key, path, 0, REG_DWORD, (const BYTE *)&zero, sizeof(zero));\n\tif (s != ERROR_SUCCESS) {\n\t\tgoto finish;\n\t}\n\nfinish:\n\tif (key)\n\t\tRegCloseKey(key);\n}\n\nvoid UpdateHookFiles(void)\n{\n\tif (update_hook_file(true))\n\t\tupdate_vulkan_registry(true);\n\tif (update_hook_file(false))\n\t\tupdate_vulkan_registry(false);\n}\n"
  },
  {
    "path": "frontend/updater/manifest.hpp",
    "content": "/*\n * Copyright (c) 2023 Dennis Sädtler <dennis@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#pragma once\n\n#include <string>\n#include <nlohmann/json.hpp>\n\nusing json = nlohmann::json;\n\nnamespace updater {\nstruct File {\n\t/* 160-bit blake2b hashes of zstd compressed and raw file (hex string) */\n\tstd::string compressed_hash;\n\tstd::string hash;\n\t/* Relative path */\n\tstd::string name;\n\t/* Uncompressed size in bytes */\n\tsize_t size = 0;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(File, compressed_hash, hash, name, size)\n};\n\nstruct Package {\n\tstd::string name;\n\tstd::vector<File> files = {};\n\t/* Relative paths of files to remove */\n\tstd::vector<std::string> removed_files = {};\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(Package, name, files, removed_files)\n};\n\nstruct Manifest {\n\t/* Update packages */\n\tstd::vector<Package> packages = {};\n\n\t/* Version information */\n\tuint8_t version_major = 0;\n\tuint8_t version_minor = 0;\n\tuint8_t version_patch = 0;\n\tuint8_t beta = 0;\n\tuint8_t rc = 0;\n\tstd::string commit;\n\n\t/* Hash of VC redist file */\n\tstd::string vc2019_redist_x64;\n\n\t/* Release notes in HTML format */\n\tstd::string notes;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(Manifest, packages, version_major, version_minor, version_patch, beta, rc,\n\t\t\t\t       commit, vc2019_redist_x64, notes)\n};\n\nstruct PatchRequest {\n\t/* Relative path of file */\n\tstd::string name;\n\t/* 160-bit blake2b hash of existing file */\n\tstd::string hash;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(PatchRequest, name, hash)\n};\n\nusing PatchesRequest = std::vector<PatchRequest>;\n\nstruct PatchResponse {\n\t/* Relative path of file */\n\tstd::string name;\n\t/* 160-bit blake2b hash of patch file */\n\tstd::string hash;\n\t/* Download URL for patch file */\n\tstd::string source;\n\t/* Size of patch file */\n\tsize_t size = 0;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(PatchResponse, name, hash, size, source)\n};\n\nusing PatchesResponse = std::vector<PatchResponse>;\n} // namespace updater\n"
  },
  {
    "path": "frontend/updater/patch.cpp",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"updater.hpp\"\n\n#include <vector>\n\nusing namespace std;\n\n/* ------------------------------------------------------------------------ */\n\nstatic int64_t offtin(const uint8_t *buf)\n{\n\tint64_t y;\n\n\ty = buf[7] & 0x7F;\n\ty = y * 256;\n\ty += buf[6];\n\ty = y * 256;\n\ty += buf[5];\n\ty = y * 256;\n\ty += buf[4];\n\ty = y * 256;\n\ty += buf[3];\n\ty = y * 256;\n\ty += buf[2];\n\ty = y * 256;\n\ty += buf[1];\n\ty = y * 256;\n\ty += buf[0];\n\n\tif (buf[7] & 0x80)\n\t\ty = -y;\n\n\treturn y;\n}\n\n/* ------------------------------------------------------------------------ */\n\nconstexpr const char *kDeltaMagic = \"BOUF//ZSTD//DICT\";\nconstexpr int kMagicSize = 16;\nconstexpr int kHeaderSize = kMagicSize + 8; // magic + int64_t delta size\n\nint ApplyPatch(ZSTD_DCtx *zstdCtx, const std::byte *patch_data, const size_t patch_size, const wchar_t *targetFile)\ntry {\n\tint64_t newsize;\n\tbool success;\n\n\tWinHandle hTarget;\n\n\t/* --------------------------------- *\n\t * open patch and file to patch      */\n\n\thTarget = CreateFile(targetFile, GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);\n\tif (!hTarget.Valid())\n\t\tthrow int(GetLastError());\n\n\t/* --------------------------------- *\n\t * read patch header                 */\n\n\tif (memcmp(patch_data, kDeltaMagic, kMagicSize) != 0)\n\t\tthrow int(-4);\n\n\t/* --------------------------------- *\n\t * allocate new file size data       */\n\n\tnewsize = offtin((const uint8_t *)patch_data + kMagicSize);\n\tif (newsize < 0 || newsize >= 0x7ffffffff)\n\t\tthrow int(-5);\n\n\tvector<std::byte> newData;\n\ttry {\n\t\tnewData.resize((size_t)newsize);\n\t} catch (...) {\n\t\tthrow int(-1);\n\t}\n\n\t/* --------------------------------- *\n\t * read old file                     */\n\n\tDWORD read;\n\tDWORD oldFileSize;\n\n\toldFileSize = GetFileSize(hTarget, nullptr);\n\tif (oldFileSize == INVALID_FILE_SIZE)\n\t\tthrow int(GetLastError());\n\n\tvector<std::byte> oldData;\n\ttry {\n\t\toldData.resize(oldFileSize);\n\t} catch (...) {\n\t\tthrow int(-1);\n\t}\n\n\tif (!ReadFile(hTarget, oldData.data(), oldFileSize, &read, nullptr))\n\t\tthrow int(GetLastError());\n\tif (read != oldFileSize)\n\t\tthrow int(-1);\n\n\t/* --------------------------------- *\n\t * patch to new file data            */\n\n\tsize_t result = ZSTD_decompress_usingDict(zstdCtx, newData.data(), newData.size(), patch_data + kHeaderSize,\n\t\t\t\t\t\t  patch_size - kHeaderSize, oldData.data(), oldData.size());\n\n\tif (result != newsize || ZSTD_isError(result))\n\t\tthrow int(-9);\n\n\t/* --------------------------------- *\n\t * write new file                    */\n\n\thTarget = nullptr;\n\thTarget = CreateFile(targetFile, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);\n\tif (!hTarget.Valid())\n\t\tthrow int(GetLastError());\n\n\tDWORD written;\n\n\tsuccess = !!WriteFile(hTarget, newData.data(), (DWORD)newsize, &written, nullptr);\n\tif (!success || written != newsize)\n\t\tthrow int(GetLastError());\n\n\treturn 0;\n\n} catch (int code) {\n\treturn code;\n}\n"
  },
  {
    "path": "frontend/updater/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by updater.rc\n//\n#define IDD_UPDATEDIALOG 101\n#define IDI_ICON1 103\n#define IDC_PROGRESS 1001\n#define IDC_STATUS 1002\n#define IDCBUTTON 1004\n#define IDC_BUTTON 1004\n#define IDC_LOG 1005\n#define IDC_LOGBUTTON 1006\n\n// Next default values for new objects\n//\n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE 104\n#define _APS_NEXT_COMMAND_VALUE 40001\n#define _APS_NEXT_CONTROL_VALUE 1007\n#define _APS_NEXT_SYMED_VALUE 101\n#endif\n#endif\n"
  },
  {
    "path": "frontend/updater/updater.cpp",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"manifest.hpp\"\n#include \"updater.hpp\"\n\n#include <psapi.h>\n#include <SoftPub.h>\n#include <WinTrust.h>\n\n#include <util/windows/CoTaskMemPtr.hpp>\n\n#include <exception>\n#include <future>\n#include <mutex>\n#include <queue>\n#include <string>\n#include <string_view>\n#include <unordered_set>\n\nusing namespace std;\nusing namespace updater;\n\n/* ----------------------------------------------------------------------- */\n\nconstexpr const string_view kCDNUrl = \"https://cdn-fastly.obsproject.com/\";\nconstexpr const wchar_t *kCDNHostname = L\"cdn-fastly.obsproject.com\";\nconstexpr const wchar_t *kCDNUpdateBaseUrl = L\"https://cdn-fastly.obsproject.com/update_studio\";\nconstexpr const wchar_t *kPatchManifestURL = L\"https://obsproject.com/update_studio/getpatchmanifest\";\nconstexpr const wchar_t *kVSRedistURL = L\"https://aka.ms/vs/17/release/vc_redist.x64.exe\";\nconstexpr const wchar_t *kMSHostname = L\"aka.ms\";\n\n/* ----------------------------------------------------------------------- */\n\nHANDLE cancelRequested = nullptr;\nHANDLE updateThread = nullptr;\nHINSTANCE hinstMain = nullptr;\nHWND hwndMain = nullptr;\nHCRYPTPROV hProvider = 0;\n\nstatic bool bExiting = false;\nstatic bool updateFailed = false;\n\nstatic bool logVisible = false;\nstatic int baseWindowHeight = 0;\n\nstatic constexpr int WM_LOG_MESSAGE = (WM_APP + 1);\nstatic constexpr int LOG_HEIGHT_DLU = 120;\n\nstatic atomic<bool> downloadThreadFailure = false;\n\nsize_t totalFileSize = 0;\natomic<size_t> completedFileSize = 0;\natomic<int> lastPosition;\nstatic atomic<int> completedUpdates = 0;\n\nstatic wchar_t tempPath[MAX_PATH];\nstatic wchar_t obs_base_directory[MAX_PATH];\n\nstruct LastError {\n\tDWORD code;\n\tinline LastError() { code = GetLastError(); }\n};\n\nvoid FreeWinHttpHandle(HINTERNET handle)\n{\n\tWinHttpCloseHandle(handle);\n}\n\n/* ----------------------------------------------------------------------- */\n\nstatic bool IsVSRedistOutdated()\n{\n\tVS_FIXEDFILEINFO *info = nullptr;\n\tUINT len = 0;\n\tvector<std::byte> buf;\n\n\tconst wchar_t vc_dll[] = L\"msvcp140\";\n\n\tauto size = GetFileVersionInfoSize(vc_dll, nullptr);\n\tif (!size)\n\t\treturn true;\n\n\tbuf.resize(size);\n\tif (!GetFileVersionInfo(vc_dll, 0, size, buf.data()))\n\t\treturn true;\n\n\tbool success = VerQueryValue(buf.data(), L\"\\\\\", reinterpret_cast<LPVOID *>(&info), &len);\n\tif (!success || !info || !len)\n\t\treturn true;\n\n\treturn LOWORD(info->dwFileVersionMS) < 40;\n}\n\nstatic void Log(const wchar_t *fmt, ...)\n{\n\tva_list argptr;\n\n\t/* Find out how big our buffer needs to be */\n\tva_start(argptr, fmt);\n\tint len = _vscwprintf(fmt, argptr);\n\tva_end(argptr);\n\n\tif (len <= 0)\n\t\treturn;\n\n\t/* Using len + 1 for null terminator, which gets chopped off below */\n\twstring str(len + 1, L'\\0');\n\n\tva_start(argptr, fmt);\n\tlen = _vsnwprintf_s(str.data(), len + 1, _TRUNCATE, fmt, argptr);\n\tva_end(argptr);\n\n\tif (len <= 0)\n\t\treturn;\n\n\t/* Append newline and send to main window as a PostMessage to\n\t*  avoid blocking worker threads with UI messages */\n\tstr.resize(len);\n\tstr += L\"\\r\\n\";\n\n\tauto msg = make_unique<wstring>(move(str));\n\n\tif (PostMessage(hwndMain, WM_LOG_MESSAGE, 0, reinterpret_cast<LPARAM>(msg.get()))) {\n\t\tmsg.release();\n\t}\n}\n\nstatic void Status(const wchar_t *fmt, ...)\n{\n\twchar_t str[512];\n\n\tva_list argptr;\n\tva_start(argptr, fmt);\n\tStringCbVPrintf(str, sizeof(str), fmt, argptr);\n\tva_end(argptr);\n\n\tSetDlgItemText(hwndMain, IDC_STATUS, str);\n\tLog(L\"%s\", str);\n}\n\nstatic bool MyCopyFile(const wchar_t *src, const wchar_t *dest)\ntry {\n\tWinHandle hSrc;\n\tWinHandle hDest;\n\n\thSrc = CreateFile(src, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN,\n\t\t\t  nullptr);\n\tif (!hSrc.Valid())\n\t\tthrow LastError();\n\n\thDest = CreateFile(dest, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);\n\tif (!hDest.Valid())\n\t\tthrow LastError();\n\n\tBYTE buf[65536];\n\tDWORD read, wrote;\n\n\tfor (;;) {\n\t\tif (!ReadFile(hSrc, buf, sizeof(buf), &read, nullptr))\n\t\t\tthrow LastError();\n\n\t\tif (read == 0)\n\t\t\tbreak;\n\n\t\tif (!WriteFile(hDest, buf, read, &wrote, nullptr))\n\t\t\tthrow LastError();\n\n\t\tif (wrote != read)\n\t\t\treturn false;\n\t}\n\n\treturn true;\n\n} catch (LastError &error) {\n\tSetLastError(error.code);\n\treturn false;\n}\n\nstatic void MyDeleteFile(const wstring &filename)\n{\n\t/* Try straightforward delete first */\n\tif (DeleteFile(filename.c_str()))\n\t\treturn;\n\n\tDWORD err = GetLastError();\n\tif (err == ERROR_FILE_NOT_FOUND)\n\t\treturn;\n\n\t/* If all else fails, schedule the file to be deleted on reboot */\n\tMoveFileEx(filename.c_str(), nullptr, MOVEFILE_DELAY_UNTIL_REBOOT);\n}\n\nstatic bool IsSafeFilename(const wchar_t *path)\n{\n\tconst wchar_t *p = path;\n\n\tif (!*p)\n\t\treturn false;\n\n\tif (wcsstr(path, L\"..\"))\n\t\treturn false;\n\n\tif (*p == '/')\n\t\treturn false;\n\n\twhile (*p) {\n\t\tif (!isalnum(*p) && *p != '.' && *p != '/' && *p != '_' && *p != '-')\n\t\t\treturn false;\n\t\tp++;\n\t}\n\n\treturn true;\n}\n\nstatic string QuickReadFile(const wchar_t *path)\n{\n\tstring data;\n\n\tWinHandle handle = CreateFileW(path, GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);\n\tif (!handle.Valid()) {\n\t\treturn {};\n\t}\n\n\tLARGE_INTEGER size;\n\n\tif (!GetFileSizeEx(handle, &size)) {\n\t\treturn {};\n\t}\n\n\tdata.resize((size_t)size.QuadPart);\n\n\tDWORD read;\n\tif (!ReadFile(handle, data.data(), (DWORD)data.size(), &read, nullptr)) {\n\t\treturn {};\n\t}\n\tif (read != size.QuadPart) {\n\t\treturn {};\n\t}\n\n\treturn data;\n}\n\nstatic bool QuickWriteFile(const wchar_t *file, const void *data, size_t size)\ntry {\n\tWinHandle handle = CreateFile(file, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, 0, nullptr);\n\n\tif (handle == INVALID_HANDLE_VALUE)\n\t\tthrow LastError();\n\n\tDWORD written;\n\tif (!WriteFile(handle, data, (DWORD)size, &written, nullptr))\n\t\tthrow LastError();\n\n\treturn true;\n\n} catch (LastError &error) {\n\tSetLastError(error.code);\n\treturn false;\n}\n\n/* ----------------------------------------------------------------------- */\n\n/* Extend std::hash for B2Hash */\ntemplate<> struct std::hash<B2Hash> {\n\tsize_t operator()(const B2Hash &value) const noexcept\n\t{\n\t\treturn hash<string_view>{}(string_view(reinterpret_cast<const char *>(value.data()), value.size()));\n\t}\n};\n\nenum state_t {\n\tSTATE_INVALID,\n\tSTATE_PENDING_DOWNLOAD,\n\tSTATE_DOWNLOADING,\n\tSTATE_DOWNLOADED,\n\tSTATE_ALREADY_DOWNLOADED,\n\tSTATE_INSTALL_FAILED,\n\tSTATE_INSTALLED,\n};\n\nstruct update_t {\n\twstring sourceURL;\n\twstring outputPath;\n\twstring previousFile;\n\tstring packageName;\n\n\tB2Hash hash;\n\tB2Hash my_hash;\n\tB2Hash downloadHash;\n\n\tsize_t fileSize = 0;\n\tstate_t state = STATE_INVALID;\n\tbool has_hash = false;\n\tbool patchable = false;\n\tbool compressed = false;\n\n\tupdate_t() = default;\n\n\tupdate_t(const update_t &from) = default;\n\n\tupdate_t(update_t &&from) noexcept\n\t\t: sourceURL(std::move(from.sourceURL)),\n\t\t  outputPath(std::move(from.outputPath)),\n\t\t  previousFile(std::move(from.previousFile)),\n\t\t  packageName(std::move(from.packageName)),\n\t\t  hash(from.hash),\n\t\t  my_hash(from.my_hash),\n\t\t  downloadHash(from.downloadHash),\n\t\t  fileSize(from.fileSize),\n\t\t  state(from.state),\n\t\t  has_hash(from.has_hash),\n\t\t  patchable(from.patchable),\n\t\t  compressed(from.compressed)\n\t{\n\t\tfrom.state = STATE_INVALID;\n\t}\n\n\tvoid CleanPartialUpdate() const\n\t{\n\t\tif (state == STATE_INSTALL_FAILED || state == STATE_INSTALLED) {\n\t\t\tif (!previousFile.empty()) {\n\t\t\t\tDeleteFile(outputPath.c_str());\n\t\t\t\tMyCopyFile(previousFile.c_str(), outputPath.c_str());\n\t\t\t\tDeleteFile(previousFile.c_str());\n\t\t\t} else {\n\t\t\t\tDeleteFile(outputPath.c_str());\n\t\t\t}\n\t\t}\n\t}\n\n\tupdate_t &operator=(const update_t &from) = default;\n};\n\nstruct deletion_t {\n\twstring originalFilename;\n\twstring deleteMeFilename;\n\n\tvoid UndoRename() const\n\t{\n\t\tif (!deleteMeFilename.empty())\n\t\t\tMoveFile(deleteMeFilename.c_str(), originalFilename.c_str());\n\t}\n};\n\nstatic unordered_map<B2Hash, vector<std::byte>> download_data;\nstatic unordered_map<string, B2Hash> hashes;\nstatic vector<update_t> updates;\nstatic vector<deletion_t> deletions;\nstatic mutex updateMutex;\n\nstatic inline void CleanupPartialUpdates()\n{\n\tfor (update_t &update : updates)\n\t\tupdate.CleanPartialUpdate();\n\n\tfor (deletion_t &deletion : deletions)\n\t\tdeletion.UndoRename();\n}\n\n/* ----------------------------------------------------------------------- */\n\nstatic int Decompress(ZSTD_DCtx *ctx, std::vector<std::byte> &buf, size_t size)\n{\n\t// Copy compressed data\n\tvector<std::byte> comp(buf.begin(), buf.end());\n\n\ttry {\n\t\tbuf.resize(size);\n\t} catch (...) {\n\t\treturn -1;\n\t}\n\n\t// Overwrite buffer with decompressed data\n\tsize_t result = ZSTD_decompressDCtx(ctx, buf.data(), buf.size(), comp.data(), comp.size());\n\n\tif (result != size)\n\t\treturn -9;\n\tif (ZSTD_isError(result))\n\t\treturn -10;\n\n\treturn 0;\n}\n\nbool DownloadWorkerThread()\n{\n\tconst DWORD tlsProtocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3;\n\n\tconst DWORD enableHTTP2Flag = WINHTTP_PROTOCOL_FLAG_HTTP2;\n\n\tconst DWORD compressionFlags = WINHTTP_DECOMPRESSION_FLAG_ALL;\n\n\tHttpHandle hSession = WinHttpOpen(L\"OBS Studio Updater/3.0\", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,\n\t\t\t\t\t  WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);\n\tif (!hSession) {\n\t\tdownloadThreadFailure = true;\n\t\tStatus(L\"Update failed: Couldn't open obsproject.com\");\n\t\treturn false;\n\t}\n\n\tWinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, (LPVOID)&tlsProtocols, sizeof(tlsProtocols));\n\n\tWinHttpSetOption(hSession, WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL, (LPVOID)&enableHTTP2Flag,\n\t\t\t sizeof(enableHTTP2Flag));\n\n\tWinHttpSetOption(hSession, WINHTTP_OPTION_DECOMPRESSION, (LPVOID)&compressionFlags, sizeof(compressionFlags));\n\n\tHttpHandle hConnect = WinHttpConnect(hSession, kCDNHostname, INTERNET_DEFAULT_HTTPS_PORT, 0);\n\tif (!hConnect) {\n\t\tdownloadThreadFailure = true;\n\t\tStatus(L\"Update failed: Couldn't connect to %s\", kCDNHostname);\n\t\treturn false;\n\t}\n\n\tZSTDDCtx zCtx;\n\n\tfor (;;) {\n\t\tbool foundWork = false;\n\n\t\tunique_lock<mutex> ulock(updateMutex);\n\n\t\tfor (update_t &update : updates) {\n\t\t\tint responseCode;\n\n\t\t\tDWORD waitResult = WaitForSingleObject(cancelRequested, 0);\n\t\t\tif (waitResult == WAIT_OBJECT_0) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (update.state != STATE_PENDING_DOWNLOAD)\n\t\t\t\tcontinue;\n\n\t\t\tupdate.state = STATE_DOWNLOADING;\n\n\t\t\tulock.unlock();\n\n\t\t\tfoundWork = true;\n\n\t\t\tif (downloadThreadFailure) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tauto &buf = download_data.at(update.downloadHash);\n\t\t\t/* Reserve required memory */\n\t\t\tbuf.reserve(update.fileSize);\n\n\t\t\tLog(L\"Downloading %s...\", update.sourceURL.c_str());\n\n\t\t\tif (!HTTPGetBuffer(hConnect, update.sourceURL.c_str(), L\"Accept-Encoding: gzip\", buf,\n\t\t\t\t\t   &responseCode)) {\n\t\t\t\tdownloadThreadFailure = true;\n\t\t\t\tStatus(L\"Update failed: Could not download \"\n\t\t\t\t       L\"%s (error code %d)\",\n\t\t\t\t       update.outputPath.c_str(), responseCode);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif (responseCode != 200) {\n\t\t\t\tdownloadThreadFailure = true;\n\t\t\t\tStatus(L\"Update failed: Could not download \"\n\t\t\t\t       L\"%s (error code %d)\",\n\t\t\t\t       update.outputPath.c_str(), responseCode);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t/* Validate hash of downloaded data. */\n\t\t\tB2Hash dataHash;\n\t\t\tblake2b(dataHash.data(), dataHash.size(), buf.data(), buf.size(), nullptr, 0);\n\n\t\t\tif (dataHash != update.downloadHash) {\n\t\t\t\tdownloadThreadFailure = true;\n\t\t\t\tStatus(L\"Update failed: Integrity check \"\n\t\t\t\t       L\"failed on %s\",\n\t\t\t\t       update.outputPath.c_str());\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t/* Decompress data in compressed buffer. */\n\t\t\tif (update.compressed && !update.patchable) {\n\t\t\t\tint res = Decompress(zCtx, buf, update.fileSize);\n\t\t\t\tif (res) {\n\t\t\t\t\tdownloadThreadFailure = true;\n\t\t\t\t\tStatus(L\"Update failed: Decompression \"\n\t\t\t\t\t       L\"failed on %s (error code %d)\",\n\t\t\t\t\t       update.outputPath.c_str(), res);\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tulock.lock();\n\n\t\t\tupdate.state = STATE_DOWNLOADED;\n\t\t\tcompletedUpdates++;\n\t\t}\n\n\t\tif (!foundWork) {\n\t\t\tbreak;\n\t\t}\n\t\tif (downloadThreadFailure) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic bool RunDownloadWorkers(int num)\ntry {\n\tvector<future<bool>> thread_success_results;\n\tthread_success_results.resize(num);\n\n\tfor (future<bool> &result : thread_success_results) {\n\t\tresult = async(DownloadWorkerThread);\n\t}\n\tfor (future<bool> &result : thread_success_results) {\n\t\tif (!result.get()) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n\n} catch (const exception &e) {\n\tStatus(L\"Exception: %S\", e.what());\n\treturn false;\n} catch (...) {\n\tStatus(L\"Unknown exception occurred in RunDownloadWorkers\");\n\treturn false;\n}\n\n/* ----------------------------------------------------------------------- */\n\nenum { WAITIFOBS_SUCCESS, WAITIFOBS_WRONG_PROCESS, WAITIFOBS_CANCELLED };\n\nstatic inline DWORD WaitIfOBS(DWORD id, const wchar_t *expected)\n{\n\twchar_t path[MAX_PATH];\n\twchar_t *name;\n\tDWORD path_len = _countof(path);\n\n\t*path = 0;\n\n\tWinHandle proc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | SYNCHRONIZE, false, id);\n\tif (!proc.Valid())\n\t\treturn WAITIFOBS_WRONG_PROCESS;\n\n\tif (!QueryFullProcessImageNameW(proc, 0, path, &path_len))\n\t\treturn WAITIFOBS_WRONG_PROCESS;\n\n\t// check it's actually our exe that's running\n\tsize_t len = wcslen(obs_base_directory);\n\tif (wcsncmp(path, obs_base_directory, len) != 0)\n\t\treturn WAITIFOBS_WRONG_PROCESS;\n\n\tname = wcsrchr(path, L'\\\\');\n\tif (name)\n\t\tname += 1;\n\telse\n\t\tname = path;\n\n\tif (_wcsnicmp(name, expected, 5) == 0) {\n\t\tHANDLE hWait[2];\n\t\thWait[0] = proc;\n\t\thWait[1] = cancelRequested;\n\n\t\tLog(L\"Waiting for OBS PID %d at %s...\", id, path);\n\n\t\tint i = WaitForMultipleObjects(2, hWait, false, INFINITE);\n\t\tif (i == WAIT_OBJECT_0 + 1)\n\t\t\treturn WAITIFOBS_CANCELLED;\n\n\t\treturn WAITIFOBS_SUCCESS;\n\t}\n\n\treturn WAITIFOBS_WRONG_PROCESS;\n}\n\nstatic bool WaitForOBS()\n{\n\tDWORD proc_ids[1024], needed, count;\n\n\tif (!EnumProcesses(proc_ids, sizeof(proc_ids), &needed)) {\n\t\treturn true;\n\t}\n\n\tcount = needed / sizeof(DWORD);\n\n\tfor (DWORD i = 0; i < count; i++) {\n\t\tDWORD id = proc_ids[i];\n\t\tif (id != 0) {\n\t\t\tswitch (WaitIfOBS(id, L\"obs64\")) {\n\t\t\tcase WAITIFOBS_SUCCESS:\n\t\t\t\treturn true;\n\t\t\tcase WAITIFOBS_WRONG_PROCESS:\n\t\t\t\tbreak;\n\t\t\tcase WAITIFOBS_CANCELLED:\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/* ----------------------------------------------------------------------- */\n\nstatic inline bool UTF8ToWide(wchar_t *wide, int wideSize, const char *utf8)\n{\n\treturn !!MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wide, wideSize);\n}\n\nstatic inline bool WideToUTF8(char *utf8, int utf8Size, const wchar_t *wide)\n{\n\treturn !!WideCharToMultiByte(CP_UTF8, 0, wide, -1, utf8, utf8Size, nullptr, nullptr);\n}\n\n#define UTF8ToWideBuf(wide, utf8) UTF8ToWide(wide, _countof(wide), utf8)\n#define WideToUTF8Buf(utf8, wide) WideToUTF8(utf8, _countof(utf8), wide)\n\n/* ----------------------------------------------------------------------- */\n\nqueue<string> hashQueue;\n\nvoid HasherThread()\n{\n\tunique_lock ulock(updateMutex, defer_lock);\n\n\twhile (true) {\n\t\tulock.lock();\n\t\tif (hashQueue.empty())\n\t\t\treturn;\n\n\t\tauto fileName = hashQueue.front();\n\t\thashQueue.pop();\n\n\t\tulock.unlock();\n\n\t\twchar_t updateFileName[MAX_PATH];\n\n\t\tif (!UTF8ToWideBuf(updateFileName, fileName.c_str()))\n\t\t\tcontinue;\n\n\t\tif (!IsSafeFilename(updateFileName))\n\t\t\tcontinue;\n\n\t\tB2Hash existingHash;\n\t\tif (CalculateFileHash(updateFileName, existingHash)) {\n\t\t\tulock.lock();\n\t\t\thashes.emplace(fileName, existingHash);\n\t\t\tulock.unlock();\n\t\t}\n\t}\n}\n\nstatic void RunHasherWorkers(int num, const vector<Package> &packages)\ntry {\n\n\tfor (const Package &package : packages) {\n\t\tfor (const File &file : package.files) {\n\t\t\thashQueue.push(file.name);\n\t\t}\n\t}\n\n\tvector<future<void>> futures;\n\tfutures.resize(num);\n\n\tfor (auto &result : futures) {\n\t\tresult = async(launch::async, HasherThread);\n\t}\n\tfor (auto &result : futures) {\n\t\tresult.wait();\n\t}\n\n} catch (const exception &e) {\n\tStatus(L\"Exception: %S\", e.what());\n} catch (...) {\n\tStatus(L\"Unknown exception occurred in RunHasherWorkers\");\n}\n\n/* ----------------------------------------------------------------------- */\n\nstatic inline bool FileExists(const wchar_t *path)\n{\n\tWIN32_FIND_DATAW wfd;\n\tHANDLE hFind;\n\n\thFind = FindFirstFileW(path, &wfd);\n\tif (hFind != INVALID_HANDLE_VALUE)\n\t\tFindClose(hFind);\n\n\treturn hFind != INVALID_HANDLE_VALUE;\n}\n\nstatic bool NonCorePackageInstalled(const char *name)\n{\n\tif (strcmp(name, \"obs-browser\") == 0)\n\t\treturn FileExists(L\"obs-plugins\\\\64bit\\\\obs-browser.dll\");\n\n\treturn false;\n}\n\nstatic bool AddPackageUpdateFiles(const Package &package, const wchar_t *branch)\n{\n\twchar_t wPackageName[512];\n\tif (!UTF8ToWideBuf(wPackageName, package.name.c_str()))\n\t\treturn false;\n\n\tif (package.name != \"core\" && !NonCorePackageInstalled(package.name.c_str()))\n\t\treturn true;\n\n\tfor (const File &file : package.files) {\n\t\tif (file.hash.size() != kBlake2StrLength)\n\t\t\tcontinue;\n\n\t\t/* The download hash may not exist if a file is uncompressed */\n\n\t\tbool compressed = false;\n\t\tif (file.compressed_hash.size() == kBlake2StrLength)\n\t\t\tcompressed = true;\n\n\t\t/* convert strings to wide */\n\n\t\twchar_t sourceURL[1024];\n\t\twchar_t updateFileName[MAX_PATH];\n\n\t\tif (!UTF8ToWideBuf(updateFileName, file.name.c_str()))\n\t\t\tcontinue;\n\n\t\t/* make sure paths are safe */\n\n\t\tif (!IsSafeFilename(updateFileName)) {\n\t\t\tStatus(L\"Update failed: Unsafe path '%s' found in \"\n\t\t\t       L\"manifest\",\n\t\t\t       updateFileName);\n\t\t\treturn false;\n\t\t}\n\n\t\tStringCbPrintf(sourceURL, sizeof(sourceURL), L\"%s/%s/%s/%s\", kCDNUpdateBaseUrl, branch, wPackageName,\n\t\t\t       updateFileName);\n\n\t\t/* Convert hashes */\n\t\tB2Hash updateHash;\n\t\tStringToHash(file.hash, updateHash);\n\n\t\t/* We don't really care if this fails, it's just to avoid\n\t\t * wasting bandwidth by downloading unmodified files */\n\t\tB2Hash localFileHash;\n\t\tbool has_hash = false;\n\n\t\tif (hashes.count(file.name)) {\n\t\t\tlocalFileHash = hashes[file.name];\n\t\t\tif (localFileHash == updateHash)\n\t\t\t\tcontinue;\n\n\t\t\thas_hash = true;\n\t\t}\n\n\t\t/* Add update file */\n\t\tupdate_t update;\n\t\tupdate.fileSize = file.size;\n\t\tupdate.outputPath = updateFileName;\n\t\tupdate.sourceURL = sourceURL;\n\t\tupdate.packageName = package.name;\n\t\tupdate.state = STATE_PENDING_DOWNLOAD;\n\t\tupdate.patchable = false;\n\t\tupdate.compressed = compressed;\n\t\tupdate.hash = updateHash;\n\n\t\tif (compressed) {\n\t\t\tupdate.sourceURL += L\".zst\";\n\t\t\tStringToHash(file.compressed_hash, update.downloadHash);\n\t\t} else {\n\t\t\tupdate.downloadHash = updateHash;\n\t\t}\n\n\t\tupdate.has_hash = has_hash;\n\t\tif (has_hash)\n\t\t\tupdate.my_hash = localFileHash;\n\n\t\tupdates.push_back(std::move(update));\n\n\t\ttotalFileSize += file.size;\n\t}\n\n\treturn true;\n}\n\nstatic void AddPackageRemovedFiles(const Package &package)\n{\n\tfor (const string &filename : package.removed_files) {\n\t\twchar_t removedFileName[MAX_PATH];\n\t\tif (!UTF8ToWideBuf(removedFileName, filename.c_str()))\n\t\t\tcontinue;\n\n\t\t/* Ensure paths are safe, also check if file exists */\n\t\tif (!IsSafeFilename(removedFileName))\n\t\t\tcontinue;\n\t\t/* Technically GetFileAttributes can fail for other reasons,\n\t\t * so double-check by also checking the last error */\n\t\tif (GetFileAttributesW(removedFileName) == INVALID_FILE_ATTRIBUTES) {\n\t\t\tint err = GetLastError();\n\t\t\tif (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tdeletion_t deletion;\n\t\tdeletion.originalFilename = removedFileName;\n\n\t\tdeletions.push_back(deletion);\n\t}\n}\n\nstatic bool RenameRemovedFile(deletion_t &deletion)\n{\n\t_TCHAR deleteMeName[MAX_PATH];\n\t_TCHAR randomStr[MAX_PATH];\n\n\tBYTE junk[40];\n\tB2Hash hash;\n\tstring temp;\n\n\tCryptGenRandom(hProvider, sizeof(junk), junk);\n\tblake2b(hash.data(), hash.size(), junk, sizeof(junk), nullptr, 0);\n\tHashToString(hash, temp);\n\n\tif (!UTF8ToWideBuf(randomStr, temp.c_str()))\n\t\treturn false;\n\n\trandomStr[8] = 0;\n\n\tStringCbCopy(deleteMeName, sizeof(deleteMeName), deletion.originalFilename.c_str());\n\n\tStringCbCat(deleteMeName, sizeof(deleteMeName), L\".\");\n\tStringCbCat(deleteMeName, sizeof(deleteMeName), randomStr);\n\tStringCbCat(deleteMeName, sizeof(deleteMeName), L\".deleteme\");\n\n\tif (MoveFile(deletion.originalFilename.c_str(), deleteMeName)) {\n\t\t/* Only set this if the file was successfully renamed */\n\t\tdeletion.deleteMeFilename = deleteMeName;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic bool UpdateWithPatchIfAvailable(const PatchResponse &patch)\n{\n\twchar_t widePatchableFilename[MAX_PATH];\n\twchar_t sourceURL[1024];\n\n\tif (patch.source.compare(0, kCDNUrl.size(), kCDNUrl) != 0)\n\t\treturn false;\n\n\tif (patch.name.find('/') == string::npos)\n\t\treturn false;\n\n\tstring patchPackageName(patch.name, 0, patch.name.find('/'));\n\tstring fileName(patch.name, patch.name.find('/') + 1);\n\n\tif (!UTF8ToWideBuf(widePatchableFilename, fileName.c_str()))\n\t\treturn false;\n\tif (!UTF8ToWideBuf(sourceURL, patch.source.c_str()))\n\t\treturn false;\n\n\tfor (update_t &update : updates) {\n\t\tif (update.packageName != patchPackageName)\n\t\t\tcontinue;\n\t\tif (update.outputPath != widePatchableFilename)\n\t\t\tcontinue;\n\n\t\tupdate.patchable = true;\n\n\t\t/* Replace the source URL with the patch file, update\n\t         * the download hash, and re-calculate download size */\n\t\tStringToHash(patch.hash, update.downloadHash);\n\t\tupdate.sourceURL = sourceURL;\n\t\ttotalFileSize -= (update.fileSize - patch.size);\n\t\tupdate.fileSize = patch.size;\n\n\t\tLog(L\"Found patch for %s, %d bytes\", widePatchableFilename, (int)patch.size);\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic bool MoveInUseFileAway(const update_t &file)\n{\n\t_TCHAR deleteMeName[MAX_PATH];\n\t_TCHAR randomStr[MAX_PATH];\n\n\tBYTE junk[40];\n\tB2Hash hash;\n\tstring temp;\n\n\tCryptGenRandom(hProvider, sizeof(junk), junk);\n\tblake2b(hash.data(), hash.size(), junk, sizeof(junk), nullptr, 0);\n\tHashToString(hash, temp);\n\n\tif (!UTF8ToWideBuf(randomStr, temp.c_str()))\n\t\treturn false;\n\n\trandomStr[8] = 0;\n\n\tStringCbCopy(deleteMeName, sizeof(deleteMeName), file.outputPath.c_str());\n\n\tStringCbCat(deleteMeName, sizeof(deleteMeName), L\".\");\n\tStringCbCat(deleteMeName, sizeof(deleteMeName), randomStr);\n\tStringCbCat(deleteMeName, sizeof(deleteMeName), L\".deleteme\");\n\n\tif (MoveFile(file.outputPath.c_str(), deleteMeName)) {\n\n\t\tif (MyCopyFile(deleteMeName, file.outputPath.c_str())) {\n\t\t\tMoveFileEx(deleteMeName, NULL, MOVEFILE_DELAY_UNTIL_REBOOT);\n\n\t\t\treturn true;\n\t\t} else {\n\t\t\tMoveFile(deleteMeName, file.outputPath.c_str());\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic bool UpdateFile(ZSTD_DCtx *ctx, update_t &file)\n{\n\twchar_t oldFileRenamedPath[MAX_PATH];\n\n\t/* Grab the patch/file data from the global cache. */\n\tvector<std::byte> &patch_data = download_data[file.downloadHash];\n\n\t/* Check if we're replacing an existing file or just installing a new\n\t * one */\n\tDWORD attribs = GetFileAttributes(file.outputPath.c_str());\n\n\tif (attribs != INVALID_FILE_ATTRIBUTES) {\n\t\twchar_t baseName[MAX_PATH];\n\n\t\tStringCbCopy(baseName, sizeof(baseName), file.outputPath.c_str());\n\n\t\twchar_t *curFileName = wcsrchr(baseName, '/');\n\t\tif (curFileName) {\n\t\t\tcurFileName[0] = '\\0';\n\t\t\tcurFileName++;\n\t\t} else\n\t\t\tcurFileName = baseName;\n\n\t\t/* Backup the existing file in case a rollback is needed */\n\t\tStringCbCopy(oldFileRenamedPath, sizeof(oldFileRenamedPath), file.outputPath.c_str());\n\t\tStringCbCat(oldFileRenamedPath, sizeof(oldFileRenamedPath), L\".old\");\n\n\t\tif (!MyCopyFile(file.outputPath.c_str(), oldFileRenamedPath)) {\n\t\t\tDWORD err = GetLastError();\n\t\t\tint is_sharing_violation = (err == ERROR_SHARING_VIOLATION || err == ERROR_USER_MAPPED_FILE);\n\n\t\t\tif (is_sharing_violation)\n\t\t\t\tStatus(L\"Update failed: %s is still in use.  \"\n\t\t\t\t       L\"Close all programs and try again.\",\n\t\t\t\t       curFileName);\n\t\t\telse\n\t\t\t\tStatus(L\"Update failed: Couldn't backup %s \"\n\t\t\t\t       L\"(error %d)\",\n\t\t\t\t       curFileName, GetLastError());\n\t\t\treturn false;\n\t\t}\n\n\t\tfile.previousFile = oldFileRenamedPath;\n\n\t\tint error_code;\n\t\tbool installed_ok;\n\t\tbool already_tried_to_move = false;\n\n\tretryAfterMovingFile:\n\n\t\tif (file.patchable) {\n\t\t\tLog(L\"Patching %s...\", file.outputPath.c_str());\n\n\t\t\terror_code = ApplyPatch(ctx, patch_data.data(), file.fileSize, file.outputPath.c_str());\n\n\t\t\tinstalled_ok = (error_code == 0);\n\n\t\t\tif (installed_ok) {\n\t\t\t\tB2Hash patchedFileHash;\n\t\t\t\tif (!CalculateFileHash(file.outputPath.c_str(), patchedFileHash)) {\n\t\t\t\t\tStatus(L\"Update failed: Couldn't \"\n\t\t\t\t\t       L\"verify integrity of patched %s\",\n\t\t\t\t\t       curFileName);\n\n\t\t\t\t\tfile.state = STATE_INSTALL_FAILED;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\tif (file.hash != patchedFileHash) {\n\t\t\t\t\tStatus(L\"Update failed: Integrity \"\n\t\t\t\t\t       L\"check of patched \"\n\t\t\t\t\t       L\"%s failed\",\n\t\t\t\t\t       curFileName);\n\n\t\t\t\t\tfile.state = STATE_INSTALL_FAILED;\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tLog(L\"Installing %s...\", file.outputPath.c_str());\n\t\t\tinstalled_ok = QuickWriteFile(file.outputPath.c_str(), patch_data.data(), patch_data.size());\n\t\t\terror_code = GetLastError();\n\t\t}\n\n\t\tif (!installed_ok) {\n\t\t\tint is_sharing_violation =\n\t\t\t\t(error_code == ERROR_SHARING_VIOLATION || error_code == ERROR_USER_MAPPED_FILE);\n\n\t\t\tif (is_sharing_violation) {\n\t\t\t\tif (!already_tried_to_move) {\n\t\t\t\t\talready_tried_to_move = true;\n\n\t\t\t\t\tif (MoveInUseFileAway(file))\n\t\t\t\t\t\tgoto retryAfterMovingFile;\n\t\t\t\t}\n\n\t\t\t\tStatus(L\"Update failed: %s is still in use.  \"\n\t\t\t\t       L\"Close all \"\n\t\t\t\t       L\"programs and try again.\",\n\t\t\t\t       curFileName);\n\t\t\t} else {\n\t\t\t\tDWORD err = GetLastError();\n\t\t\t\tStatus(L\"Update failed: Couldn't update %s \"\n\t\t\t\t       L\"(error %d)\",\n\t\t\t\t       curFileName, err ? err : error_code);\n\t\t\t}\n\n\t\t\tfile.state = STATE_INSTALL_FAILED;\n\t\t\treturn false;\n\t\t}\n\n\t\tfile.state = STATE_INSTALLED;\n\t} else {\n\t\tif (file.patchable) {\n\t\t\t/* Uh oh, we thought we could patch something but it's\n\t\t\t * no longer there! */\n\t\t\tStatus(L\"Update failed: Source file %s not found\", file.outputPath.c_str());\n\t\t\treturn false;\n\t\t}\n\n\t\tLog(L\"Installing %s...\", file.outputPath.c_str());\n\n\t\t/* We may be installing into new folders,\n\t\t * make sure they exist */\n\t\tfilesystem::path filePath(file.outputPath.c_str());\n\t\tcreate_directories(filePath.parent_path());\n\n\t\tfile.previousFile = L\"\";\n\n\t\tbool success = !!QuickWriteFile(file.outputPath.c_str(), patch_data.data(), patch_data.size());\n\t\tif (!success) {\n\t\t\tStatus(L\"Update failed: Couldn't install %s (error %d)\", file.outputPath.c_str(),\n\t\t\t       GetLastError());\n\t\t\tfile.state = STATE_INSTALL_FAILED;\n\t\t\treturn false;\n\t\t}\n\n\t\tfile.state = STATE_INSTALLED;\n\t}\n\n\treturn true;\n}\n\nqueue<reference_wrapper<update_t>> updateQueue;\n\nstatic atomic<int> installed = 0;\nstatic atomic<bool> updateThreadFailed = false;\n\nstatic bool UpdateWorker()\n{\n\tunique_lock<mutex> ulock(updateMutex, defer_lock);\n\tZSTDDCtx zCtx;\n\n\twhile (true) {\n\t\tulock.lock();\n\n\t\tif (updateThreadFailed)\n\t\t\treturn false;\n\t\tif (updateQueue.empty())\n\t\t\tbreak;\n\n\t\tauto update = updateQueue.front();\n\t\tupdateQueue.pop();\n\t\tulock.unlock();\n\n\t\tif (!UpdateFile(zCtx, update)) {\n\t\t\tupdateThreadFailed = true;\n\t\t\treturn false;\n\t\t} else {\n\t\t\tint position = (int)(((float)++installed / (float)completedUpdates) * 100.0f);\n\t\t\tint oldPosition = lastPosition;\n\n\t\t\tif (position > oldPosition &&\n\t\t\t    lastPosition.compare_exchange_strong(oldPosition, position, memory_order_relaxed)) {\n\t\t\t\tSendDlgItemMessage(hwndMain, IDC_PROGRESS, PBM_SETPOS, position, 0);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic bool RunUpdateWorkers(int num)\ntry {\n\tfor (update_t &update : updates)\n\t\tupdateQueue.emplace(update);\n\n\tvector<future<bool>> thread_success_results;\n\tthread_success_results.resize(num);\n\n\tfor (future<bool> &result : thread_success_results) {\n\t\tresult = async(launch::async, UpdateWorker);\n\t}\n\tfor (future<bool> &result : thread_success_results) {\n\t\tif (!result.get()) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n\n} catch (const exception &e) {\n\tStatus(L\"Exception: %S\", e.what());\n\treturn false;\n} catch (...) {\n\tStatus(L\"Unknown exception occurred in RunUpdateWorkers\");\n\treturn false;\n}\n\nstatic bool UpdateVSRedists()\n{\n\t/* ------------------------------------------ *\n\t * Initialize session                         */\n\n\tconst DWORD tlsProtocols = WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;\n\n\tconst DWORD compressionFlags = WINHTTP_DECOMPRESSION_FLAG_ALL;\n\n\tHttpHandle hSession = WinHttpOpen(L\"OBS Studio Updater/3.0\", WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY,\n\t\t\t\t\t  WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);\n\tif (!hSession) {\n\t\tStatus(L\"VC Redist Update failed: Couldn't create session\");\n\t\treturn false;\n\t}\n\n\tWinHttpSetOption(hSession, WINHTTP_OPTION_SECURE_PROTOCOLS, (LPVOID)&tlsProtocols, sizeof(tlsProtocols));\n\n\tWinHttpSetOption(hSession, WINHTTP_OPTION_DECOMPRESSION, (LPVOID)&compressionFlags, sizeof(compressionFlags));\n\n\tHttpHandle hConnect = WinHttpConnect(hSession, kMSHostname, INTERNET_DEFAULT_HTTPS_PORT, 0);\n\tif (!hConnect) {\n\t\tStatus(L\"Update failed: Couldn't connect to %S\", kMSHostname);\n\t\treturn false;\n\t}\n\n\tint responseCode;\n\n\tDWORD waitResult = WaitForSingleObject(cancelRequested, 0);\n\tif (waitResult == WAIT_OBJECT_0) {\n\t\treturn false;\n\t}\n\n\t/* ------------------------------------------ *\n\t * Download redist                            */\n\n\tStatus(L\"Downloading Visual C++ Redistributable\");\n\n\twstring destPath;\n\tdestPath += tempPath;\n\tdestPath += L\"\\\\VC_redist.x64.exe\";\n\n\tif (!HTTPGetFile(hConnect, kVSRedistURL, destPath.c_str(), L\"Accept-Encoding: gzip\", &responseCode)) {\n\n\t\tDeleteFile(destPath.c_str());\n\t\tStatus(L\"Update failed: Could not download \"\n\t\t       L\"%s (error code %d)\",\n\t\t       L\"Visual C++ Redistributable\", responseCode);\n\t\treturn false;\n\t}\n\n\t/* ------------------------------------------ *\n\t * Verify file signature                      */\n\n\tGUID action = WINTRUST_ACTION_GENERIC_VERIFY_V2;\n\n\tWINTRUST_FILE_INFO fileInfo = {};\n\tfileInfo.cbStruct = sizeof(fileInfo);\n\tfileInfo.pcwszFilePath = destPath.c_str();\n\n\tWINTRUST_DATA data = {};\n\tdata.cbStruct = sizeof(data);\n\tdata.dwUIChoice = WTD_UI_NONE;\n\tdata.dwUnionChoice = WTD_CHOICE_FILE;\n\tdata.dwStateAction = WTD_STATEACTION_VERIFY;\n\tdata.pFile = &fileInfo;\n\n\tLONG result = WinVerifyTrust(nullptr, &action, &data);\n\n\tif (result != ERROR_SUCCESS) {\n\t\tStatus(L\"Update failed: Signature verification failed for \"\n\t\t       L\"%s (error code %d / %d)\",\n\t\t       L\"Visual C++ Redistributable\", result, GetLastError());\n\t\tDeleteFile(destPath.c_str());\n\t\treturn false;\n\t}\n\n\t/* ------------------------------------------ *\n\t * If verification succeeded, install redist  */\n\n\twchar_t commandline[MAX_PATH + MAX_PATH];\n\tStringCbPrintf(commandline, sizeof(commandline), L\"%s /install /quiet /norestart\", destPath.c_str());\n\n\tPROCESS_INFORMATION pi = {};\n\tSTARTUPINFO si = {};\n\tsi.cb = sizeof(si);\n\n\tbool success = !!CreateProcessW(destPath.c_str(), commandline, nullptr, nullptr, false, CREATE_NO_WINDOW,\n\t\t\t\t\tnullptr, nullptr, &si, &pi);\n\tif (success) {\n\t\tStatus(L\"Installing %s...\", L\"Visual C++ Redistributable\");\n\n\t\tCloseHandle(pi.hThread);\n\t\tWaitForSingleObject(pi.hProcess, INFINITE);\n\t\tCloseHandle(pi.hProcess);\n\t} else {\n\t\tStatus(L\"Update failed: Could not execute \"\n\t\t       L\"%s (error code %d)\",\n\t\t       L\"Visual C++ Redistributable\", (int)GetLastError());\n\t}\n\n\tDeleteFile(destPath.c_str());\n\n\twaitResult = WaitForSingleObject(cancelRequested, 0);\n\tif (waitResult == WAIT_OBJECT_0) {\n\t\treturn false;\n\t}\n\n\treturn success;\n}\n\nstatic void UpdateRegistryVersion(const Manifest &manifest)\n{\n\tconst char *regKey = R\"(Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OBS Studio)\";\n\tLSTATUS res;\n\tHKEY key;\n\tchar version[32];\n\tint formattedLen;\n\n\t/* The manifest does not store a version string, so we gotta make one ourselves. */\n\tif (manifest.beta || manifest.rc) {\n\t\tformattedLen = sprintf_s(version, sizeof(version), \"%d.%d.%d-%s%d\", manifest.version_major,\n\t\t\t\t\t manifest.version_minor, manifest.version_patch, manifest.beta ? \"beta\" : \"rc\",\n\t\t\t\t\t manifest.beta ? manifest.beta : manifest.rc);\n\t} else {\n\t\tformattedLen = sprintf_s(version, sizeof(version), \"%d.%d.%d\", manifest.version_major,\n\t\t\t\t\t manifest.version_minor, manifest.version_patch);\n\t}\n\n\tif (formattedLen <= 0)\n\t\treturn;\n\n\tres = RegOpenKeyExA(HKEY_LOCAL_MACHINE, regKey, 0, KEY_WRITE | KEY_WOW64_32KEY, &key);\n\tif (res != ERROR_SUCCESS)\n\t\treturn;\n\n\tRegSetValueExA(key, \"DisplayVersion\", 0, REG_SZ, (const BYTE *)version, formattedLen + 1);\n\tRegCloseKey(key);\n}\n\nstatic void ClearShaderCache()\n{\n\twchar_t shader_path[MAX_PATH];\n\tif (SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, shader_path) == S_OK) {\n\t\tif (SUCCEEDED(StringCbCatW(shader_path, sizeof(shader_path), L\"\\\\obs-studio\\\\shader-cache\"))) {\n\t\t\tfilesystem::remove_all(shader_path);\n\t\t}\n\t}\n}\n\nextern \"C\" void UpdateHookFiles(void);\n\nstatic bool Update(wchar_t *cmdLine)\n{\n\t/* ------------------------------------- *\n\t * Check to make sure OBS isn't running  */\n\n\tHANDLE hObsUpdateMutex = OpenMutexW(SYNCHRONIZE, false, L\"OBSStudioUpdateMutex\");\n\tif (hObsUpdateMutex) {\n\t\tHANDLE hWait[2];\n\t\thWait[0] = hObsUpdateMutex;\n\t\thWait[1] = cancelRequested;\n\n\t\tint i = WaitForMultipleObjects(2, hWait, false, INFINITE);\n\n\t\tif (i == WAIT_OBJECT_0)\n\t\t\tReleaseMutex(hObsUpdateMutex);\n\n\t\tCloseHandle(hObsUpdateMutex);\n\n\t\tif (i == WAIT_OBJECT_0 + 1)\n\t\t\treturn false;\n\t}\n\n\tif (!WaitForOBS())\n\t\treturn false;\n\n\t/* ------------------------------------- *\n\t * Init crypt stuff                      */\n\n\tCryptProvider hProvider;\n\tif (!CryptAcquireContext(&hProvider, nullptr, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {\n\t\tSetDlgItemTextW(hwndMain, IDC_STATUS, L\"Update failed: CryptAcquireContext failure\");\n\t\treturn false;\n\t}\n\n\t::hProvider = hProvider;\n\n\t/* ------------------------------------- */\n\n\tSetDlgItemTextW(hwndMain, IDC_STATUS, L\"Searching for available updates...\");\n\n\tHWND hProgress = GetDlgItem(hwndMain, IDC_PROGRESS);\n\tLONG_PTR style = GetWindowLongPtr(hProgress, GWL_STYLE);\n\tSetWindowLongPtr(hProgress, GWL_STYLE, style | PBS_MARQUEE);\n\n\tSendDlgItemMessage(hwndMain, IDC_PROGRESS, PBM_SETMARQUEE, 1, 0);\n\n\t/* ------------------------------------- *\n\t * Check if updating portable build      */\n\n\tbool bIsPortable = false;\n\twstring branch = L\"stable\";\n\twstring appdata;\n\n\tif (cmdLine[0]) {\n\t\tint argc;\n\t\tLPWSTR *argv = CommandLineToArgvW(cmdLine, &argc);\n\n\t\tif (argv) {\n\t\t\tfor (int i = 0; i < argc; i++) {\n\t\t\t\tif (wcscmp(argv[i], L\"Portable\") == 0) {\n\t\t\t\t\t// Legacy OBS\n\t\t\t\t\tbIsPortable = true;\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (wcsncmp(argv[i], L\"--branch=\", 9) == 0) {\n\t\t\t\t\tbranch = argv[i] + 9;\n\t\t\t\t} else if (wcsncmp(argv[i], L\"--appdata=\", 10) == 0) {\n\t\t\t\t\tappdata = argv[i] + 10;\n\t\t\t\t} else if (wcscmp(argv[i], L\"--portable\") == 0) {\n\t\t\t\t\tbIsPortable = true;\n\t\t\t\t} else if (wcsncmp(argv[i], L\"--portable--branch=\", 19) == 0) {\n\t\t\t\t\t/* Versions pre-29.1 beta 2 produce broken parameters :( */\n\t\t\t\t\tbIsPortable = true;\n\t\t\t\t\tbranch = argv[i] + 19;\n\t\t\t\t}\n\t\t\t}\n\t\t\tLocalFree((HLOCAL)argv);\n\t\t}\n\t}\n\n\tLog(L\"Branch: %s, AppData: %s, Portable: %d\", branch.c_str(), appdata.c_str(), bIsPortable ? 1 : 0);\n\n\t/* ------------------------------------- *\n\t * Get config path                       */\n\n\twchar_t lpAppDataPath[MAX_PATH];\n\tlpAppDataPath[0] = 0;\n\n\tif (bIsPortable) {\n\t\tStringCbCopy(lpAppDataPath, sizeof(lpAppDataPath), obs_base_directory);\n\t\tStringCbCat(lpAppDataPath, sizeof(lpAppDataPath), L\"\\\\config\");\n\t} else {\n\t\tif (!appdata.empty()) {\n\t\t\tHRESULT hr = StringCbCopy(lpAppDataPath, sizeof(lpAppDataPath), appdata.c_str());\n\t\t\tif (hr != S_OK) {\n\t\t\t\tStatus(L\"Update failed: Could not determine AppData \"\n\t\t\t\t       L\"location\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\tCoTaskMemPtr<wchar_t> pOut;\n\t\t\tHRESULT hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, nullptr, &pOut);\n\t\t\tif (hr != S_OK) {\n\t\t\t\tStatus(L\"Update failed: Could not determine AppData \"\n\t\t\t\t       L\"location\");\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tStringCbCopy(lpAppDataPath, sizeof(lpAppDataPath), pOut);\n\t\t}\n\t}\n\n\tStringCbCat(lpAppDataPath, sizeof(lpAppDataPath), L\"\\\\obs-studio\");\n\n\t/* ------------------------------------- *\n\t * Get download path                     */\n\n\twchar_t manifestPath[MAX_PATH];\n\twchar_t tempDirName[MAX_PATH];\n\n\tmanifestPath[0] = 0;\n\ttempDirName[0] = 0;\n\n\tStringCbPrintf(manifestPath, sizeof(manifestPath), L\"%s\\\\updates\\\\manifest.json\", lpAppDataPath);\n\tif (!GetTempPathW(_countof(tempDirName), tempDirName)) {\n\t\tStatus(L\"Update failed: Failed to get temp path: %ld\", GetLastError());\n\t\treturn false;\n\t}\n\tif (!GetTempFileNameW(tempDirName, L\"obs-studio\", 0, tempPath)) {\n\t\tStatus(L\"Update failed: Failed to create temp dir name: %ld\", GetLastError());\n\t\treturn false;\n\t}\n\n\tDeleteFile(tempPath);\n\tCreateDirectory(tempPath, nullptr);\n\n\t/* ------------------------------------- *\n\t * Load manifest file                    */\n\n\tManifest manifest;\n\t{\n\t\tLog(L\"Using manifest %s\", manifestPath);\n\n\t\tstring manifestFile = QuickReadFile(manifestPath);\n\t\tif (manifestFile.empty()) {\n\t\t\tStatus(L\"Update failed: Couldn't load manifest file\");\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tjson manifestContents = json::parse(manifestFile);\n\t\t\tmanifest = manifestContents.get<Manifest>();\n\t\t} catch (json::exception &e) {\n\t\t\tStatus(L\"Update failed: Couldn't parse update \"\n\t\t\t       L\"manifest: %S\",\n\t\t\t       e.what());\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* ------------------------------------- *\n\t * Hash local files listed in manifest   */\n\n\tRunHasherWorkers(4, manifest.packages);\n\n\t/* ------------------------------------- *\n\t * Parse current manifest update files   */\n\n\tfor (const Package &package : manifest.packages) {\n\t\tif (!AddPackageUpdateFiles(package, branch.c_str())) {\n\t\t\tStatus(L\"Update failed: Failed to process update packages\");\n\t\t\treturn false;\n\t\t}\n\n\t\t/* Add removed files to deletion queue (if any) */\n\t\tAddPackageRemovedFiles(package);\n\t}\n\n\tSendDlgItemMessage(hwndMain, IDC_PROGRESS, PBM_SETMARQUEE, 0, 0);\n\tSetWindowLongPtr(hProgress, GWL_STYLE, style);\n\n\t/* ------------------------------------- *\n\t * Exit if updates already installed     */\n\n\tif (updates.empty()) {\n\t\tStatus(L\"All available updates are already installed.\");\n\t\tSetDlgItemText(hwndMain, IDC_BUTTON, L\"Launch OBS\");\n\t\treturn true;\n\t}\n\n\t/* ------------------------------------- *\n\t * Check VS redistributables version     */\n\n\tif (IsVSRedistOutdated()) {\n\t\tif (!UpdateVSRedists()) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* ------------------------------------- *\n\t * Generate file hash json               */\n\n\tPatchesRequest files;\n\tfor (update_t &update : updates) {\n\t\tif (!update.has_hash)\n\t\t\tcontinue;\n\n\t\tchar outputPath[MAX_PATH];\n\t\tif (!WideToUTF8Buf(outputPath, update.outputPath.c_str()))\n\t\t\tcontinue;\n\n\t\tstring hash_string;\n\t\tHashToString(update.my_hash, hash_string);\n\n\t\tstring package_path;\n\t\tpackage_path = update.packageName;\n\t\tpackage_path += \"/\";\n\t\tpackage_path += outputPath;\n\n\t\tfiles.push_back({package_path, hash_string});\n\t}\n\n\t/* ------------------------------------- *\n\t * Send file hashes                      */\n\n\tstring newManifest;\n\tif (!files.empty()) {\n\t\tjson request = files;\n\t\tstring post_body = request.dump();\n\n\t\tint len = (int)post_body.size();\n\t\tsize_t compressSize = ZSTD_compressBound(len);\n\t\tstring compressedJson;\n\n\t\tcompressedJson.resize(compressSize);\n\n\t\tsize_t result = ZSTD_compress(compressedJson.data(), compressedJson.size(), post_body.data(),\n\t\t\t\t\t      post_body.size(), ZSTD_CLEVEL_DEFAULT);\n\n\t\tif (ZSTD_isError(result))\n\t\t\treturn false;\n\n\t\tcompressedJson.resize(result);\n\n\t\twstring manifestUrl(kPatchManifestURL);\n\t\tif (branch != L\"stable\")\n\t\t\tmanifestUrl += L\"?branch=\" + branch;\n\n\t\tint responseCode;\n\t\tbool success = !!HTTPPostData(manifestUrl.c_str(), (BYTE *)compressedJson.data(),\n\t\t\t\t\t      (int)compressedJson.size(), L\"Accept-Encoding: gzip\", &responseCode,\n\t\t\t\t\t      newManifest);\n\n\t\tif (!success)\n\t\t\treturn false;\n\n\t\tif (responseCode != 200) {\n\t\t\tStatus(L\"Update failed: HTTP/%d while trying to \"\n\t\t\t       L\"download patch manifest\",\n\t\t\t       responseCode);\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\tnewManifest = \"[]\";\n\t}\n\n\t/* ------------------------------------- *\n\t * Parse new manifest                    */\n\n\tPatchesResponse patches;\n\ttry {\n\t\tjson patchManifest = json::parse(newManifest);\n\t\tpatches = patchManifest.get<PatchesResponse>();\n\t} catch (json::exception &e) {\n\t\tStatus(L\"Update failed: Couldn't parse patch manifest: %S\", e.what());\n\t\treturn false;\n\t}\n\n\t/* Update updates with patch information. */\n\tint patchableFiles = 0;\n\tfor (const PatchResponse &patch : patches) {\n\t\tif (UpdateWithPatchIfAvailable(patch)) {\n\t\t\tpatchableFiles++;\n\t\t}\n\t}\n\n\tLog(L\"Total files: %d, patchable: %d, patches received: %d\", (int)updates.size(), (int)files.size(),\n\t    patchableFiles);\n\n\t/* ------------------------------------- *\n\t * Deduplicate Downloads                 */\n\n\tunordered_set<B2Hash> downloadHashes;\n\tfor (update_t &update : updates) {\n\t\tif (downloadHashes.count(update.downloadHash)) {\n\t\t\tupdate.state = STATE_ALREADY_DOWNLOADED;\n\t\t\ttotalFileSize -= update.fileSize;\n\t\t\tcompletedUpdates++;\n\t\t} else {\n\t\t\tdownloadHashes.insert(update.downloadHash);\n\t\t}\n\t}\n\n\tLog(L\"Total download size: %lld bytes\", totalFileSize);\n\n\t/* ------------------------------------- *\n\t * Download Updates                      */\n\n\t/* Pre-allocate download_data map so worker threads only\n\t * look up existing entries rather than inserting new ones,\n\t * avoiding concurrent map mutation from multiple threads. */\n\tdownload_data.reserve(downloadHashes.size());\n\tfor (update_t &update : updates) {\n\t\tif (update.state == STATE_PENDING_DOWNLOAD)\n\t\t\tdownload_data.try_emplace(update.downloadHash);\n\t}\n\n\tStatus(L\"Downloading updates...\");\n\tif (!RunDownloadWorkers(4))\n\t\treturn false;\n\n\tif ((size_t)completedUpdates != updates.size()) {\n\t\tStatus(L\"Update failed to download all files.\");\n\t\treturn false;\n\t}\n\n\t/* ------------------------------------- *\n\t * Install updates                       */\n\n\tSendDlgItemMessage(hwndMain, IDC_PROGRESS, PBM_SETPOS, 0, 0);\n\tlastPosition = 0;\n\n\tStatus(L\"Installing updates...\");\n\tif (!RunUpdateWorkers(4))\n\t\treturn false;\n\n\tfor (deletion_t &deletion : deletions) {\n\t\tif (!RenameRemovedFile(deletion)) {\n\t\t\tStatus(L\"Update failed: Couldn't remove \"\n\t\t\t       L\"obsolete files\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/* ------------------------------------- *\n\t * Install virtual camera                */\n\n\tauto runcommand = [](wchar_t *cmd) {\n\t\tSTARTUPINFO si = {};\n\t\tsi.cb = sizeof(si);\n\t\tsi.dwFlags = STARTF_USESHOWWINDOW;\n\t\tsi.wShowWindow = SW_HIDE;\n\n\t\tLog(L\"Executing: %s\", cmd);\n\n\t\tPROCESS_INFORMATION pi;\n\t\tbool success = !!CreateProcessW(nullptr, cmd, nullptr, nullptr, false, CREATE_NEW_CONSOLE, nullptr,\n\t\t\t\t\t\tnullptr, &si, &pi);\n\t\tif (success) {\n\t\t\tWaitForSingleObject(pi.hProcess, INFINITE);\n\t\t\tCloseHandle(pi.hThread);\n\t\t\tCloseHandle(pi.hProcess);\n\t\t}\n\t};\n\n\tif (!bIsPortable) {\n\t\tStatus(L\"Installing Virtual Camera...\");\n\t\twchar_t regsvr[MAX_PATH];\n\t\twchar_t src[MAX_PATH];\n\t\twchar_t tmp[MAX_PATH];\n\t\twchar_t tmp2[MAX_PATH];\n\n\t\tSHGetFolderPathW(nullptr, CSIDL_SYSTEM, nullptr, SHGFP_TYPE_CURRENT, regsvr);\n\t\tStringCbCat(regsvr, sizeof(regsvr), L\"\\\\regsvr32.exe\");\n\n\t\tStringCbCopy(src, sizeof(src), obs_base_directory);\n\t\tStringCbCat(src, sizeof(src), L\"\\\\data\\\\obs-plugins\\\\win-dshow\\\\\");\n\n\t\tStringCbCopy(tmp, sizeof(tmp), L\"\\\"\");\n\t\tStringCbCat(tmp, sizeof(tmp), regsvr);\n\t\tStringCbCat(tmp, sizeof(tmp), L\"\\\" /s \\\"\");\n\t\tStringCbCat(tmp, sizeof(tmp), src);\n\t\tStringCbCat(tmp, sizeof(tmp), L\"obs-virtualcam-module\");\n\n\t\tStringCbCopy(tmp2, sizeof(tmp2), tmp);\n\t\tStringCbCat(tmp2, sizeof(tmp2), L\"32.dll\\\"\");\n\t\truncommand(tmp2);\n\n\t\tStringCbCopy(tmp2, sizeof(tmp2), tmp);\n\t\tStringCbCat(tmp2, sizeof(tmp2), L\"64.dll\\\"\");\n\t\truncommand(tmp2);\n\t}\n\n\t/* ------------------------------------- *\n\t * Update hook files and vulkan registry */\n\n\tStatus(L\"Updating Game Capture hooks...\");\n\tUpdateHookFiles();\n\n\t/* ------------------------------------- *\n\t * Clear shader cache                    */\n\n\tStatus(L\"Clearing shader cache...\");\n\tClearShaderCache();\n\n\t/* ------------------------------------- *\n\t * Update installed version in registry  */\n\n\tif (!bIsPortable) {\n\t\tStatus(L\"Updating version information...\");\n\t\tUpdateRegistryVersion(manifest);\n\t}\n\n\t/* ------------------------------------- *\n\t * Finish                                */\n\n\tStatus(L\"Cleaning up...\");\n\t/* If we get here, all updates installed successfully so we can purge\n\t * the old versions */\n\tfor (update_t &update : updates) {\n\t\tif (!update.previousFile.empty())\n\t\t\tDeleteFile(update.previousFile.c_str());\n\t}\n\n\t/* Delete all removed files mentioned in the manifest */\n\tfor (deletion_t &deletion : deletions)\n\t\tMyDeleteFile(deletion.deleteMeFilename);\n\n\tSendDlgItemMessage(hwndMain, IDC_PROGRESS, PBM_SETPOS, 100, 0);\n\n\tStatus(L\"Update complete.\");\n\tSetDlgItemText(hwndMain, IDC_BUTTON, L\"Launch OBS\");\n\treturn true;\n}\n\nstatic DWORD WINAPI UpdateThread(void *arg)\n{\n\twchar_t *cmdLine = (wchar_t *)arg;\n\n\tbool success = Update(cmdLine);\n\n\tif (!success) {\n\t\t/* This handles deleting temp files and rolling back and\n\t\t * partially installed updates */\n\t\tCleanupPartialUpdates();\n\n\t\tif (tempPath[0])\n\t\t\tRemoveDirectory(tempPath);\n\n\t\tif (WaitForSingleObject(cancelRequested, 0) == WAIT_OBJECT_0)\n\t\t\tStatus(L\"Update aborted.\");\n\n\t\tHWND hProgress = GetDlgItem(hwndMain, IDC_PROGRESS);\n\n\t\t/* Even a no-op style change apparently resets the progress bar */\n\t\tLONG_PTR style = GetWindowLongPtr(hProgress, GWL_STYLE);\n\t\tif (style & PBS_MARQUEE)\n\t\t\tSetWindowLongPtr(hProgress, GWL_STYLE, style & ~PBS_MARQUEE);\n\n\t\tSendMessage(hProgress, PBM_SETSTATE, PBST_ERROR, 0);\n\n\t\tSetDlgItemText(hwndMain, IDC_BUTTON, L\"Exit\");\n\t\tEnableWindow(GetDlgItem(hwndMain, IDC_BUTTON), true);\n\n\t\tupdateFailed = true;\n\t} else {\n\t\tif (tempPath[0])\n\t\t\tRemoveDirectory(tempPath);\n\t}\n\n\tif (bExiting)\n\t\tExitProcess(success);\n\treturn 0;\n}\n\nstatic void CancelUpdate(bool quit)\n{\n\tif (WaitForSingleObject(updateThread, 0) != WAIT_OBJECT_0) {\n\t\tbExiting = quit;\n\t\tSetEvent(cancelRequested);\n\t} else {\n\t\tPostQuitMessage(0);\n\t}\n}\n\nstatic void LaunchOBS(LPWSTR lpCmdLine)\n{\n\twchar_t newCwd[MAX_PATH];\n\twchar_t obsPath[MAX_PATH];\n\n\tStringCbCopy(obsPath, sizeof(obsPath), obs_base_directory);\n\tStringCbCat(obsPath, sizeof(obsPath), L\"\\\\bin\\\\64bit\");\n\tSetCurrentDirectory(obsPath);\n\tStringCbCopy(newCwd, sizeof(newCwd), obsPath);\n\n\tStringCbCat(obsPath, sizeof(obsPath), L\"\\\\obs64.exe\");\n\n\tif (!FileExists(obsPath)) {\n\t\t/* TODO: give user a message maybe? */\n\t\treturn;\n\t}\n\n\tSHELLEXECUTEINFO execInfo;\n\n\tZeroMemory(&execInfo, sizeof(execInfo));\n\n\texecInfo.cbSize = sizeof(execInfo);\n\texecInfo.lpFile = obsPath;\n\texecInfo.lpDirectory = newCwd;\n\texecInfo.nShow = SW_SHOWNORMAL;\n\n\tif (lpCmdLine[0])\n\t\texecInfo.lpParameters = lpCmdLine;\n\n\tShellExecuteEx(&execInfo);\n}\n\nstatic void ToggleLogVisibility()\n{\n\tHWND hwndLog = GetDlgItem(hwndMain, IDC_LOG);\n\tif (!hwndLog)\n\t\treturn;\n\n\tlogVisible = !logVisible;\n\n\tRECT rc;\n\tGetWindowRect(hwndMain, &rc);\n\n\tSetDlgItemText(hwndMain, IDC_LOGBUTTON, logVisible ? L\"Hide Log\" : L\"Show Log\");\n\n\tif (logVisible) {\n\t\tRECT dluRect = {0, 0, 0, LOG_HEIGHT_DLU};\n\t\tMapDialogRect(hwndMain, &dluRect);\n\t\tint logPixelHeight = dluRect.bottom;\n\n\t\tSetWindowPos(hwndMain, nullptr, 0, 0, rc.right - rc.left, baseWindowHeight + logPixelHeight,\n\t\t\t     SWP_NOMOVE | SWP_NOZORDER);\n\n\t\tSendMessage(hwndLog, EM_SCROLL, SB_BOTTOM, 0);\n\n\t\tShowWindow(hwndLog, SW_SHOW);\n\t} else {\n\t\tShowWindow(hwndLog, SW_HIDE);\n\t\tSetWindowPos(hwndMain, nullptr, 0, 0, rc.right - rc.left, baseWindowHeight, SWP_NOMOVE | SWP_NOZORDER);\n\t}\n}\n\nstatic INT_PTR CALLBACK UpdateDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\n{\n\tswitch (message) {\n\tcase WM_INITDIALOG: {\n\t\tstatic HICON hMainIcon = LoadIcon(hinstMain, MAKEINTRESOURCE(IDI_ICON1));\n\t\tSendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hMainIcon);\n\t\tSendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hMainIcon);\n\n\t\t/* Save original window height for ToggleLogVisibility */\n\t\tRECT rc;\n\t\tGetWindowRect(hwnd, &rc);\n\t\tbaseWindowHeight = rc.bottom - rc.top;\n\n\t\t/* Position the log window on the bottom of the main window */\n\t\tRECT clientRc;\n\t\tGetClientRect(hwnd, &clientRc);\n\n\t\tRECT dluRect = {0, 0, 0, LOG_HEIGHT_DLU};\n\t\tMapDialogRect(hwnd, &dluRect);\n\n\t\tHWND hwndLog =\n\t\t\tCreateWindowEx(WS_EX_CLIENTEDGE, L\"EDIT\", L\"\",\n\t\t\t\t       WS_CHILD | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL,\n\t\t\t\t       0, clientRc.bottom, clientRc.right, dluRect.bottom, hwnd,\n\t\t\t\t       (HMENU)(INT_PTR)IDC_LOG, hinstMain, nullptr);\n\n\t\t/* 32k by default without this */\n\t\tSendMessage(hwndLog, EM_SETLIMITTEXT, 0, 0);\n\n\t\t/* Propagate the main window font or it looks ugly */\n\t\tHFONT hFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);\n\t\tif (hFont)\n\t\t\tSendMessage(hwndLog, WM_SETFONT, (WPARAM)hFont, FALSE);\n\n\t\treturn true;\n\t}\n\n\tcase WM_COMMAND:\n\t\tif (LOWORD(wParam) == IDC_BUTTON) {\n\t\t\tif (HIWORD(wParam) == BN_CLICKED) {\n\t\t\t\tDWORD result = WaitForSingleObject(updateThread, 0);\n\t\t\t\tif (result == WAIT_OBJECT_0) {\n\t\t\t\t\tif (updateFailed)\n\t\t\t\t\t\tPostQuitMessage(0);\n\t\t\t\t\telse\n\t\t\t\t\t\tPostQuitMessage(1);\n\t\t\t\t} else {\n\t\t\t\t\tEnableWindow((HWND)lParam, false);\n\t\t\t\t\tCancelUpdate(false);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (LOWORD(wParam) == IDC_LOGBUTTON) {\n\t\t\tif (HIWORD(wParam) == BN_CLICKED)\n\t\t\t\tToggleLogVisibility();\n\t\t}\n\t\treturn true;\n\n\tcase WM_CLOSE:\n\t\tCancelUpdate(true);\n\t\treturn true;\n\n\tcase WM_LOG_MESSAGE: {\n\t\t/* Log message sent to us by Log() as PostMessage */\n\t\tunique_ptr<wstring> msg(reinterpret_cast<wstring *>(lParam));\n\n\t\tHWND hwndLog = GetDlgItem(hwnd, IDC_LOG);\n\t\tif (hwndLog && msg) {\n\t\t\tint textLen = GetWindowTextLength(hwndLog);\n\t\t\tSendMessage(hwndLog, EM_SETSEL, textLen, textLen);\n\t\t\tSendMessage(hwndLog, EM_REPLACESEL, FALSE, reinterpret_cast<LPARAM>(msg->c_str()));\n\t\t}\n\t\treturn true;\n\t}\n\t}\n\n\treturn false;\n}\n\nstatic int RestartAsAdmin(LPCWSTR lpCmdLine, LPCWSTR cwd)\n{\n\twchar_t myPath[MAX_PATH];\n\tif (!GetModuleFileNameW(nullptr, myPath, _countof(myPath) - 1)) {\n\t\treturn 0;\n\t}\n\n\t/* If the admin is a different user, add the path to the user's\n\t * AppData to the command line so we can load the correct manifest. */\n\twstring elevatedCmdLine(lpCmdLine);\n\tCoTaskMemPtr<wchar_t> pOut;\n\tHRESULT hr = SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_DEFAULT, nullptr, &pOut);\n\tif (hr == S_OK) {\n\t\televatedCmdLine += L\" \\\"--appdata=\";\n\t\televatedCmdLine += pOut;\n\t\televatedCmdLine += L\"\\\"\";\n\t}\n\n\tSHELLEXECUTEINFO shExInfo = {0};\n\tshExInfo.cbSize = sizeof(shExInfo);\n\tshExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;\n\tshExInfo.hwnd = nullptr;\n\tshExInfo.lpVerb = L\"runas\";                      /* Operation to perform */\n\tshExInfo.lpFile = myPath;                        /* Application to start */\n\tshExInfo.lpParameters = elevatedCmdLine.c_str(); /* Additional parameters */\n\tshExInfo.lpDirectory = cwd;\n\tshExInfo.nShow = SW_NORMAL;\n\tshExInfo.hInstApp = nullptr;\n\n\t/* annoyingly the actual elevated updater will disappear behind other\n\t * windows :( */\n\tAllowSetForegroundWindow(ASFW_ANY);\n\n\tif (ShellExecuteEx(&shExInfo)) {\n\t\tDWORD exitCode;\n\n\t\tWaitForSingleObject(shExInfo.hProcess, INFINITE);\n\n\t\tif (GetExitCodeProcess(shExInfo.hProcess, &exitCode)) {\n\t\t\tif (exitCode == 1) {\n\t\t\t\treturn exitCode;\n\t\t\t}\n\t\t}\n\t\tCloseHandle(shExInfo.hProcess);\n\t}\n\treturn 0;\n}\n\nstatic bool HasElevation()\n{\n\tSID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;\n\tPSID sid = nullptr;\n\tBOOL elevated = false;\n\tBOOL success;\n\n\tsuccess = AllocateAndInitializeSid(&sia, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,\n\t\t\t\t\t   0, &sid);\n\tif (success && sid) {\n\t\tCheckTokenMembership(nullptr, sid, &elevated);\n\t\tFreeSid(sid);\n\t}\n\n\treturn elevated;\n}\n\nint WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR lpCmdLine, int)\n{\n\tINITCOMMONCONTROLSEX icce;\n\n\twchar_t cwd[MAX_PATH];\n\tGetCurrentDirectoryW(_countof(cwd) - 1, cwd);\n\n\tif (!IsWindows10OrGreater()) {\n\t\tMessageBox(nullptr,\n\t\t\t   L\"OBS Studio 28 and newer no longer support Windows 7,\"\n\t\t\t   L\" Windows 8, or Windows 8.1. You can disable the\"\n\t\t\t   L\" following setting to opt out of future updates:\"\n\t\t\t   L\" Settings → General → General → Automatically check\"\n\t\t\t   L\" for updates on startup\",\n\t\t\t   L\"Unsupported Operating System\", MB_ICONWARNING);\n\t\treturn 0;\n\t}\n\n\tif (!HasElevation()) {\n\n\t\tWinHandle hMutex = OpenMutex(SYNCHRONIZE, false, L\"OBSUpdaterRunningAsNonAdminUser\");\n\t\tif (hMutex) {\n\t\t\tMessageBox(nullptr, L\"OBS Studio Updater must be run as an administrator.\", L\"Updater Error\",\n\t\t\t\t   MB_ICONWARNING);\n\t\t\treturn 2;\n\t\t}\n\n\t\tHANDLE hLowMutex = CreateMutexW(nullptr, true, L\"OBSUpdaterRunningAsNonAdminUser\");\n\n\t\t/* return code 1 =  user wanted to launch OBS */\n\t\tif (RestartAsAdmin(lpCmdLine, cwd) == 1) {\n\t\t\tStringCbCat(cwd, sizeof(cwd), L\"\\\\..\\\\..\");\n\t\t\tGetFullPathName(cwd, _countof(obs_base_directory), obs_base_directory, nullptr);\n\t\t\tSetCurrentDirectory(obs_base_directory);\n\n\t\t\tLaunchOBS(lpCmdLine);\n\t\t}\n\n\t\tif (hLowMutex) {\n\t\t\tReleaseMutex(hLowMutex);\n\t\t\tCloseHandle(hLowMutex);\n\t\t}\n\n\t\treturn 0;\n\t} else {\n\t\tStringCbCat(cwd, sizeof(cwd), L\"\\\\..\\\\..\");\n\t\tGetFullPathName(cwd, _countof(obs_base_directory), obs_base_directory, nullptr);\n\t\tSetCurrentDirectory(obs_base_directory);\n\n\t\thinstMain = hInstance;\n\n\t\ticce.dwSize = sizeof(icce);\n\t\ticce.dwICC = ICC_PROGRESS_CLASS;\n\n\t\tInitCommonControlsEx(&icce);\n\n\t\thwndMain = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_UPDATEDIALOG), nullptr, UpdateDialogProc);\n\t\tif (!hwndMain) {\n\t\t\treturn -1;\n\t\t}\n\n\t\tLog(L\"Update target: %s\", obs_base_directory);\n\n\t\tShowWindow(hwndMain, SW_SHOWNORMAL);\n\t\tSetForegroundWindow(hwndMain);\n\n\t\tcancelRequested = CreateEvent(nullptr, true, false, nullptr);\n\t\tupdateThread = CreateThread(nullptr, 0, UpdateThread, lpCmdLine, 0, nullptr);\n\n\t\tMSG msg;\n\t\twhile (GetMessage(&msg, nullptr, 0, 0)) {\n\t\t\tif (!IsDialogMessage(hwndMain, &msg)) {\n\t\t\t\tTranslateMessage(&msg);\n\t\t\t\tDispatchMessage(&msg);\n\t\t\t}\n\t\t}\n\n\t\t/* there is no non-elevated process waiting for us if UAC is\n\t\t * disabled */\n\t\tWinHandle hMutex = OpenMutex(SYNCHRONIZE, false, L\"OBSUpdaterRunningAsNonAdminUser\");\n\t\tif (msg.wParam == 1 && !hMutex) {\n\t\t\tLaunchOBS(lpCmdLine);\n\t\t}\n\n\t\treturn (int)msg.wParam;\n\t}\n}\n"
  },
  {
    "path": "frontend/updater/updater.hpp",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#pragma once\n\n#define WINVER 0x0600\n#define _WIN32_WINDOWS 0x0600\n#define _WIN32_WINNT 0x0600\n#define WIN32_LEAN_AND_MEAN\n\n#include <windows.h>\n#include <versionhelpers.h>\n#include <winhttp.h>\n#include <commctrl.h>\n#include <Wincrypt.h>\n#include <shlobj.h>\n#include <shellapi.h>\n#include <malloc.h>\n#include <stdlib.h>\n#include <tchar.h>\n#include <strsafe.h>\n#include <ctype.h>\n#include <blake2.h>\n#include <zstd.h>\n\n#include <array>\n#include <atomic>\n#include <string>\n#include <vector>\n\n#include \"helpers.hpp\"\n\nconstexpr uint8_t kBlake2HashLength = 20;\nconstexpr uint8_t kBlake2StrLength = kBlake2HashLength * 2;\nusing B2Hash = std::array<std::byte, kBlake2HashLength>;\n\n#if defined _M_IX86\n#pragma comment(linker, \"/manifestdependency:\\\"type='win32' \"       \\\n\t\t\t\"name='Microsoft.Windows.Common-Controls' \" \\\n\t\t\t\"version='6.0.0.0' \"                        \\\n\t\t\t\"processorArchitecture='x86' \"              \\\n\t\t\t\"publicKeyToken='6595b64144ccf1df' \"        \\\n\t\t\t\"language='*'\\\"\")\n#elif defined _M_IA64\n#pragma comment(linker, \"/manifestdependency:\\\"type='win32' \"       \\\n\t\t\t\"name='Microsoft.Windows.Common-Controls' \" \\\n\t\t\t\"version='6.0.0.0' \"                        \\\n\t\t\t\"processorArchitecture='ia64' \"             \\\n\t\t\t\"publicKeyToken='6595b64144ccf1df' \"        \\\n\t\t\t\"language='*'\\\"\")\n#elif defined _M_X64\n#pragma comment(linker, \"/manifestdependency:\\\"type='win32' \"       \\\n\t\t\t\"name='Microsoft.Windows.Common-Controls' \" \\\n\t\t\t\"version='6.0.0.0' \"                        \\\n\t\t\t\"processorArchitecture='amd64' \"            \\\n\t\t\t\"publicKeyToken='6595b64144ccf1df' \"        \\\n\t\t\t\"language='*'\\\"\")\n#else\n#pragma comment(linker, \"/manifestdependency:\\\"type='win32' \"          \\\n\t\t\t\"name='Microsoft.Windows.Common-Controls' \"    \\\n\t\t\t\"version='6.0.0.0' processorArchitecture='*' \" \\\n\t\t\t\"publicKeyToken='6595b64144ccf1df' \"           \\\n\t\t\t\"language='*'\\\"\")\n#endif\n\n#include <util/windows/WinHandle.hpp>\n#include \"resource.h\"\n\nbool HTTPGetFile(HINTERNET hConnect, const wchar_t *url, const wchar_t *outputPath, const wchar_t *extraHeaders,\n\t\t int *responseCode);\nbool HTTPGetBuffer(HINTERNET hConnect, const wchar_t *url, const wchar_t *extraHeaders, std::vector<std::byte> &out,\n\t\t   int *responseCode);\nbool HTTPPostData(const wchar_t *url, const BYTE *data, int dataLen, const wchar_t *extraHeaders, int *responseCode,\n\t\t  std::string &response);\n\nvoid HashToString(const B2Hash &in, std::string &out);\nvoid StringToHash(const std::string &in, B2Hash &out);\n\nbool CalculateFileHash(const wchar_t *path, B2Hash &hash);\n\nint ApplyPatch(ZSTD_DCtx *zstdCtx, const std::byte *patch_data, size_t patch_size, const wchar_t *targetFile);\n\nextern HWND hwndMain;\nextern HCRYPTPROV hProvider;\nextern size_t totalFileSize;\nextern std::atomic<int> lastPosition;\nextern std::atomic<size_t> completedFileSize;\nextern HANDLE cancelRequested;\n\n#pragma pack(push, r1, 1)\n\ntypedef struct {\n\tBLOBHEADER blobheader;\n\tRSAPUBKEY rsapubkey;\n} PUBLICKEYHEADER;\n\n#pragma pack(pop, r1)\n\nvoid FreeWinHttpHandle(HINTERNET handle);\nusing HttpHandle = CustomHandle<HINTERNET, FreeWinHttpHandle>;\n\n/* ------------------------------------------------------------------------ */\n\nclass ZSTDDCtx {\n\tZSTD_DCtx *ctx = nullptr;\n\npublic:\n\tinline ZSTDDCtx() { ctx = ZSTD_createDCtx(); }\n\tinline ~ZSTDDCtx() { ZSTD_freeDCtx(ctx); }\n\tinline operator ZSTD_DCtx *() const { return ctx; }\n\tinline ZSTD_DCtx *get() const { return ctx; }\n};\n"
  },
  {
    "path": "frontend/updater/updater.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\" xmlns:asmv3=\"urn:schemas-microsoft-com:asm.v3\">\n\n<asmv3:application>\n\t<asmv3:windowsSettings xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">\n\t\t<dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">system</dpiAwareness>\n\t\t<dpiAware>true</dpiAware>\n\t</asmv3:windowsSettings>\n</asmv3:application>\n\n<compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n\t<application>\n\t\t<!-- Windows 10 -->\n\t\t<supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\n\t\t<!-- Windows 8.1 -->\n\t\t<supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\"/>\n\t\t<!-- Windows Vista -->\n\t\t<supportedOS Id=\"{e2011457-1546-43c5-a5fe-008deee3d3f0}\"/>\n\t\t<!-- Windows 7 -->\n\t\t<supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\"/>\n\t\t<!-- Windows 8 -->\n\t\t<supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\"/>\n\t</application>\n</compatibility>\n</assembly>"
  },
  {
    "path": "frontend/updater/updater.rc",
    "content": "// Microsoft Visual C++ generated resource script.\n//\n#include \"resource.h\"\n\n#define APSTUDIO_READONLY_SYMBOLS\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 2 resource.\n//\n#include \"winres.h\"\n\n/////////////////////////////////////////////////////////////////////////////\n#undef APSTUDIO_READONLY_SYMBOLS\n\n/////////////////////////////////////////////////////////////////////////////\n// English (United States) resources\n\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\n#pragma code_page(1252)\n\n#ifdef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// TEXTINCLUDE\n//\n\n1 TEXTINCLUDE \nBEGIN\n    \"resource.h\\0\"\nEND\n\n2 TEXTINCLUDE \nBEGIN\n    \"#include \"\"afxres.h\"\"\\r\\n\"\n    \"\\0\"\nEND\n\n3 TEXTINCLUDE \nBEGIN\n    \"\\r\\n\"\n    \"\\0\"\nEND\n\n#endif    // APSTUDIO_INVOKED\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Dialog\n//\n\nIDD_UPDATEDIALOG DIALOGEX 0, 0, 316, 56\nSTYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU\nEXSTYLE WS_EX_APPWINDOW\nCAPTION \"OBS Studio Update\"\nFONT 8, \"MS Shell Dlg\", 400, 0, 0x1\nBEGIN\n    PUSHBUTTON      \"Cancel\",IDC_BUTTON,259,34,50,14\n    PUSHBUTTON      \"Show Log\",IDC_LOGBUTTON,7,34,50,14\n    CONTROL         \"\",IDC_PROGRESS,\"msctls_progress32\",PBS_SMOOTH,7,17,302,14\n    LTEXT           \"Waiting for OBS to exit...\",IDC_STATUS,7,7,302,8,SS_WORDELLIPSIS\nEND\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// DESIGNINFO\n//\n\n#ifdef APSTUDIO_INVOKED\nGUIDELINES DESIGNINFO\nBEGIN\n    IDD_UPDATEDIALOG, DIALOG\n    BEGIN\n        LEFTMARGIN, 7\n        RIGHTMARGIN, 309\n        TOPMARGIN, 7\n        BOTTOMMARGIN, 48\n    END\nEND\n#endif    // APSTUDIO_INVOKED\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Version\n//\n\nVS_VERSION_INFO VERSIONINFO\n FILEVERSION 1,0,0,1\n PRODUCTVERSION 1,0,0,1\n FILEFLAGSMASK 0x3fL\n#ifdef _DEBUG\n FILEFLAGS 0x1L\n#else\n FILEFLAGS 0x0L\n#endif\n FILEOS 0x40004L\n FILETYPE 0x1L\n FILESUBTYPE 0x0L\nBEGIN\n    BLOCK \"StringFileInfo\"\n    BEGIN\n        BLOCK \"040904b0\"\n        BEGIN\n            VALUE \"CompanyName\", \"obsproject.com\"\n            VALUE \"FileDescription\", \"OBS Updater\"\n            VALUE \"FileVersion\", \"1.0.0.1\"\n            VALUE \"InternalName\", \"updater.exe\"\n            VALUE \"LegalCopyright\", \"Copyright (C) 2013 Richard Stanway\"\n            VALUE \"OriginalFilename\", \"updater.exe\"\n            VALUE \"ProductName\", \"OBS Updater\"\n            VALUE \"ProductVersion\", \"1.0.0.1\"\n        END\n    END\n    BLOCK \"VarFileInfo\"\n    BEGIN\n        VALUE \"Translation\", 0x409, 1200\n    END\nEND\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Icon\n//\n\n// Icon with lowest ID value placed first to ensure application icon\n// remains consistent on all systems.\nIDI_ICON1               ICON                    \"../../../cmake/bundle/windows/obs-studio.ico\"\n#endif    // English (United States) resources\n/////////////////////////////////////////////////////////////////////////////\n\n\n\n#ifndef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 3 resource.\n//\n\n\n/////////////////////////////////////////////////////////////////////////////\n#endif    // not APSTUDIO_INVOKED\n\n"
  },
  {
    "path": "frontend/utility/AdvancedOutput.cpp",
    "content": "#include \"AdvancedOutput.hpp\"\n\n#include <utility/audio-encoders.hpp>\n#include <utility/StartMultiTrackVideoStreamingGuard.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\nusing namespace std;\n\nstatic OBSData GetDataFromJsonFile(const char *jsonFile)\n{\n\tconst OBSBasic *basic = OBSBasic::Get();\n\n\tconst OBSProfile &currentProfile = basic->GetCurrentProfile();\n\n\tconst std::filesystem::path jsonFilePath = currentProfile.path / std::filesystem::u8path(jsonFile);\n\n\tOBSDataAutoRelease data = nullptr;\n\n\tif (!jsonFilePath.empty()) {\n\t\tBPtr<char> jsonData = os_quick_read_utf8_file(jsonFilePath.u8string().c_str());\n\n\t\tif (!!jsonData) {\n\t\t\tdata = obs_data_create_from_json(jsonData);\n\t\t}\n\t}\n\n\tif (!data) {\n\t\tdata = obs_data_create();\n\t}\n\n\treturn data.Get();\n}\n\nstatic void ApplyEncoderDefaults(OBSData &settings, const obs_encoder_t *encoder)\n{\n\tOBSData dataRet = obs_encoder_get_defaults(encoder);\n\tobs_data_release(dataRet);\n\n\tif (!!settings)\n\t\tobs_data_apply(dataRet, settings);\n\tsettings = std::move(dataRet);\n}\n\n#define ADV_ARCHIVE_NAME \"adv_archive_audio\"\n\n#ifdef __APPLE__\nstatic void translate_macvth264_encoder(const char *&encoder)\n{\n\tif (strcmp(encoder, \"vt_h264_hw\") == 0) {\n\t\tencoder = \"com.apple.videotoolbox.videoencoder.h264.gva\";\n\t} else if (strcmp(encoder, \"vt_h264_sw\") == 0) {\n\t\tencoder = \"com.apple.videotoolbox.videoencoder.h264\";\n\t}\n}\n#endif\n\nAdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)\n{\n\tconst char *recType = config_get_string(main->Config(), \"AdvOut\", \"RecType\");\n\tconst char *streamEncoder = config_get_string(main->Config(), \"AdvOut\", \"Encoder\");\n\tconst char *streamAudioEncoder = config_get_string(main->Config(), \"AdvOut\", \"AudioEncoder\");\n\tconst char *recordEncoder = config_get_string(main->Config(), \"AdvOut\", \"RecEncoder\");\n\tconst char *recAudioEncoder = config_get_string(main->Config(), \"AdvOut\", \"RecAudioEncoder\");\n\tconst char *recFormat = config_get_string(main->Config(), \"AdvOut\", \"RecFormat2\");\n#ifdef __APPLE__\n\ttranslate_macvth264_encoder(streamEncoder);\n\ttranslate_macvth264_encoder(recordEncoder);\n#endif\n\n\tffmpegOutput = astrcmpi(recType, \"FFmpeg\") == 0;\n\tffmpegRecording = ffmpegOutput && config_get_bool(main->Config(), \"AdvOut\", \"FFOutputToFile\");\n\tuseStreamEncoder = astrcmpi(recordEncoder, \"none\") == 0;\n\tuseStreamAudioEncoder = astrcmpi(recAudioEncoder, \"none\") == 0;\n\n\tOBSData streamEncSettings = GetDataFromJsonFile(\"streamEncoder.json\");\n\tOBSData recordEncSettings = GetDataFromJsonFile(\"recordEncoder.json\");\n\n\tif (ffmpegOutput) {\n\t\tfileOutput = obs_output_create(\"ffmpeg_output\", \"adv_ffmpeg_output\", nullptr, nullptr);\n\t\tif (!fileOutput)\n\t\t\tthrow \"Failed to create recording FFmpeg output \"\n\t\t\t      \"(advanced output)\";\n\t} else {\n\t\tbool useReplayBuffer = config_get_bool(main->Config(), \"AdvOut\", \"RecRB\");\n\t\tif (useReplayBuffer) {\n\t\t\tOBSDataAutoRelease hotkey;\n\t\t\tconst char *str = config_get_string(main->Config(), \"Hotkeys\", \"ReplayBuffer\");\n\t\t\tif (str)\n\t\t\t\thotkey = obs_data_create_from_json(str);\n\t\t\telse\n\t\t\t\thotkey = nullptr;\n\n\t\t\treplayBuffer = obs_output_create(\"replay_buffer\", Str(\"ReplayBuffer\"), nullptr, hotkey);\n\n\t\t\tif (!replayBuffer)\n\t\t\t\tthrow \"Failed to create replay buffer output \"\n\t\t\t\t      \"(simple output)\";\n\n\t\t\tsignal_handler_t *signal = obs_output_get_signal_handler(replayBuffer);\n\n\t\t\tstartReplayBuffer.Connect(signal, \"start\", OBSStartReplayBuffer, this);\n\t\t\tstopReplayBuffer.Connect(signal, \"stop\", OBSStopReplayBuffer, this);\n\t\t\treplayBufferStopping.Connect(signal, \"stopping\", OBSReplayBufferStopping, this);\n\t\t\treplayBufferSaved.Connect(signal, \"saved\", OBSReplayBufferSaved, this);\n\t\t}\n\n\t\tconst char *mux = \"ffmpeg_muxer\";\n\t\tif (strcmp(recFormat, \"hybrid_mp4\") == 0)\n\t\t\tmux = \"mp4_output\";\n\t\telse if (strcmp(recFormat, \"hybrid_mov\") == 0)\n\t\t\tmux = \"mov_output\";\n\n\t\tfileOutput = obs_output_create(mux, \"adv_file_output\", nullptr, nullptr);\n\t\tif (!fileOutput)\n\t\t\tthrow \"Failed to create recording output \"\n\t\t\t      \"(advanced output)\";\n\n\t\tif (!useStreamEncoder) {\n\t\t\tvideoRecording = obs_video_encoder_create(recordEncoder, \"advanced_video_recording\",\n\t\t\t\t\t\t\t\t  recordEncSettings, nullptr);\n\t\t\tif (!videoRecording)\n\t\t\t\tthrow \"Failed to create recording video \"\n\t\t\t\t      \"encoder (advanced output)\";\n\t\t\tobs_encoder_release(videoRecording);\n\t\t}\n\t}\n\n\tvideoStreaming = obs_video_encoder_create(streamEncoder, \"advanced_video_stream\", streamEncSettings, nullptr);\n\tif (!videoStreaming)\n\t\tthrow \"Failed to create streaming video encoder \"\n\t\t      \"(advanced output)\";\n\tobs_encoder_release(videoStreaming);\n\tif (whipSimulcastEncoders != nullptr) {\n\t\twhipSimulcastEncoders->Create(streamEncoder, config_get_int(main->Config(), \"AdvOut\", \"RescaleFilter\"),\n\t\t\t\t\t      config_get_int(main->Config(), \"Stream1\", \"WHIPSimulcastTotalLayers\"),\n\t\t\t\t\t      video_output_get_width(obs_get_video()),\n\t\t\t\t\t      video_output_get_height(obs_get_video()));\n\t}\n\n\tconst char *rate_control =\n\t\tobs_data_get_string(useStreamEncoder ? streamEncSettings : recordEncSettings, \"rate_control\");\n\tif (!rate_control)\n\t\trate_control = \"\";\n\tusesBitrate = astrcmpi(rate_control, \"CBR\") == 0 || astrcmpi(rate_control, \"VBR\") == 0 ||\n\t\t      astrcmpi(rate_control, \"ABR\") == 0;\n\n\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\tchar name[19];\n\t\tsnprintf(name, sizeof(name), \"adv_record_audio_%d\", i);\n\n\t\trecordTrack[i] = obs_audio_encoder_create(useStreamAudioEncoder ? streamAudioEncoder : recAudioEncoder,\n\t\t\t\t\t\t\t  name, nullptr, i, nullptr);\n\n\t\tif (!recordTrack[i]) {\n\t\t\tthrow \"Failed to create audio encoder \"\n\t\t\t      \"(advanced output)\";\n\t\t}\n\n\t\tobs_encoder_release(recordTrack[i]);\n\n\t\tsnprintf(name, sizeof(name), \"adv_stream_audio_%d\", i);\n\t\tstreamTrack[i] = obs_audio_encoder_create(streamAudioEncoder, name, nullptr, i, nullptr);\n\n\t\tif (!streamTrack[i]) {\n\t\t\tthrow \"Failed to create streaming audio encoders \"\n\t\t\t      \"(advanced output)\";\n\t\t}\n\n\t\tobs_encoder_release(streamTrack[i]);\n\t}\n\n\tstd::string id;\n\tint streamTrackIndex = config_get_int(main->Config(), \"AdvOut\", \"TrackIndex\") - 1;\n\tstreamAudioEnc =\n\t\tobs_audio_encoder_create(streamAudioEncoder, \"adv_stream_audio\", nullptr, streamTrackIndex, nullptr);\n\tif (!streamAudioEnc)\n\t\tthrow \"Failed to create streaming audio encoder \"\n\t\t      \"(advanced output)\";\n\tobs_encoder_release(streamAudioEnc);\n\n\tid = \"\";\n\tint vodTrack = config_get_int(main->Config(), \"AdvOut\", \"VodTrackIndex\") - 1;\n\tstreamArchiveEnc = obs_audio_encoder_create(streamAudioEncoder, ADV_ARCHIVE_NAME, nullptr, vodTrack, nullptr);\n\tif (!streamArchiveEnc)\n\t\tthrow \"Failed to create archive audio encoder \"\n\t\t      \"(advanced output)\";\n\tobs_encoder_release(streamArchiveEnc);\n\n\tstartRecording.Connect(obs_output_get_signal_handler(fileOutput), \"start\", OBSStartRecording, this);\n\tstopRecording.Connect(obs_output_get_signal_handler(fileOutput), \"stop\", OBSStopRecording, this);\n\trecordStopping.Connect(obs_output_get_signal_handler(fileOutput), \"stopping\", OBSRecordStopping, this);\n\trecordFileChanged.Connect(obs_output_get_signal_handler(fileOutput), \"file_changed\", OBSRecordFileChanged,\n\t\t\t\t  this);\n}\n\nvoid AdvancedOutput::UpdateStreamSettings()\n{\n\tbool applyServiceSettings = config_get_bool(main->Config(), \"AdvOut\", \"ApplyServiceSettings\");\n\tbool enforceBitrate = !config_get_bool(main->Config(), \"Stream1\", \"IgnoreRecommended\");\n\tbool dynBitrate = config_get_bool(main->Config(), \"Output\", \"DynamicBitrate\");\n\tconst char *streamEncoder = config_get_string(main->Config(), \"AdvOut\", \"Encoder\");\n\n\tOBSData settings = GetDataFromJsonFile(\"streamEncoder.json\");\n\tApplyEncoderDefaults(settings, videoStreaming);\n\n\tif (applyServiceSettings) {\n\t\tint bitrate = (int)obs_data_get_int(settings, \"bitrate\");\n\t\tint keyint_sec = (int)obs_data_get_int(settings, \"keyint_sec\");\n\t\tobs_service_apply_encoder_settings(main->GetService(), settings, nullptr);\n\t\tif (!enforceBitrate) {\n\t\t\tint maxVideoBitrate;\n\t\t\tint maxAudioBitrate;\n\t\t\tobs_service_get_max_bitrate(main->GetService(), &maxVideoBitrate, &maxAudioBitrate);\n\n\t\t\tstd::string videoBitRateLogString = maxVideoBitrate > 0 ? std::to_string(maxVideoBitrate)\n\t\t\t\t\t\t\t\t\t\t: \"None\";\n\t\t\tstd::string audioBitRateLogString = maxAudioBitrate > 0 ? std::to_string(maxAudioBitrate)\n\t\t\t\t\t\t\t\t\t\t: \"None\";\n\n\t\t\tblog(LOG_INFO,\n\t\t\t     \"User is ignoring service bitrate limits.\\n\"\n\t\t\t     \"Service Recommendations:\\n\"\n\t\t\t     \"\\tvideo bitrate: %s\\n\"\n\t\t\t     \"\\taudio bitrate: %s\",\n\t\t\t     videoBitRateLogString.c_str(), audioBitRateLogString.c_str());\n\n\t\t\tobs_data_set_int(settings, \"bitrate\", bitrate);\n\t\t}\n\n\t\tint enforced_keyint_sec = (int)obs_data_get_int(settings, \"keyint_sec\");\n\t\tif (keyint_sec != 0 && keyint_sec < enforced_keyint_sec)\n\t\t\tobs_data_set_int(settings, \"keyint_sec\", keyint_sec);\n\t} else {\n\t\tblog(LOG_WARNING, \"User is ignoring service settings.\");\n\t}\n\n\tif (dynBitrate && strstr(streamEncoder, \"nvenc\") != nullptr)\n\t\tobs_data_set_bool(settings, \"lookahead\", false);\n\n\tvideo_t *video = obs_get_video();\n\tenum video_format format = video_output_get_format(video);\n\n\tswitch (format) {\n\tcase VIDEO_FORMAT_I420:\n\tcase VIDEO_FORMAT_NV12:\n\tcase VIDEO_FORMAT_I010:\n\tcase VIDEO_FORMAT_P010:\n\t\tbreak;\n\tdefault:\n\t\tobs_encoder_set_preferred_video_format(videoStreaming, VIDEO_FORMAT_NV12);\n\t}\n\n\tobs_encoder_update(videoStreaming, settings);\n\tif (whipSimulcastEncoders != nullptr) {\n\t\twhipSimulcastEncoders->Update(settings, obs_data_get_int(settings, \"bitrate\"));\n\t}\n}\n\ninline void AdvancedOutput::UpdateRecordingSettings()\n{\n\tOBSData settings = GetDataFromJsonFile(\"recordEncoder.json\");\n\tobs_encoder_update(videoRecording, settings);\n}\n\nvoid AdvancedOutput::Update()\n{\n\tUpdateStreamSettings();\n\tif (!useStreamEncoder && !ffmpegOutput)\n\t\tUpdateRecordingSettings();\n\tUpdateAudioSettings();\n}\n\ninline bool AdvancedOutput::allowsMultiTrack()\n{\n\tconst char *protocol = nullptr;\n\tobs_service_t *service_obj = main->GetService();\n\tprotocol = obs_service_get_protocol(service_obj);\n\tif (!protocol)\n\t\treturn false;\n\treturn astrcmpi_n(protocol, SRT_PROTOCOL, strlen(SRT_PROTOCOL)) == 0 ||\n\t       astrcmpi_n(protocol, RIST_PROTOCOL, strlen(RIST_PROTOCOL)) == 0;\n}\n\ninline void AdvancedOutput::SetupStreaming()\n{\n\tconst char *rescaleRes = config_get_string(main->Config(), \"AdvOut\", \"RescaleRes\");\n\tint rescaleFilter = config_get_int(main->Config(), \"AdvOut\", \"RescaleFilter\");\n\tint multiTrackAudioMixes = config_get_int(main->Config(), \"AdvOut\", \"StreamMultiTrackAudioMixes\");\n\tunsigned int cx = 0;\n\tunsigned int cy = 0;\n\tint idx = 0;\n\tbool is_multitrack_output = allowsMultiTrack();\n\n\tif (rescaleFilter != OBS_SCALE_DISABLE && rescaleRes && *rescaleRes) {\n\t\tif (sscanf(rescaleRes, \"%ux%u\", &cx, &cy) != 2) {\n\t\t\tcx = 0;\n\t\t\tcy = 0;\n\t\t}\n\t}\n\n\tif (!is_multitrack_output) {\n\t\tobs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0);\n\t} else {\n\t\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\t\tif ((multiTrackAudioMixes & (1 << i)) != 0) {\n\t\t\t\tobs_output_set_audio_encoder(streamOutput, streamTrack[i], idx);\n\t\t\t\tidx++;\n\t\t\t}\n\t\t}\n\t}\n\n\tobs_encoder_set_scaled_size(videoStreaming, cx, cy);\n\tobs_encoder_set_gpu_scale_type(videoStreaming, (obs_scale_type)rescaleFilter);\n\n\tconst char *id = obs_service_get_id(main->GetService());\n\tif (strcmp(id, \"rtmp_custom\") == 0) {\n\t\tOBSDataAutoRelease settings = obs_data_create();\n\t\tobs_service_apply_encoder_settings(main->GetService(), settings, nullptr);\n\t\tobs_encoder_update(videoStreaming, settings);\n\t}\n}\n\ninline void AdvancedOutput::SetupRecording()\n{\n\tconst char *path = config_get_string(main->Config(), \"AdvOut\", \"RecFilePath\");\n\tconst char *mux = config_get_string(main->Config(), \"AdvOut\", \"RecMuxerCustom\");\n\tconst char *rescaleRes = config_get_string(main->Config(), \"AdvOut\", \"RecRescaleRes\");\n\tint rescaleFilter = config_get_int(main->Config(), \"AdvOut\", \"RecRescaleFilter\");\n\tint tracks;\n\n\tconst char *recFormat = config_get_string(main->Config(), \"AdvOut\", \"RecFormat2\");\n\n\tbool is_fragmented = strncmp(recFormat, \"fragmented\", 10) == 0;\n\tbool flv = strcmp(recFormat, \"flv\") == 0;\n\n\tif (flv)\n\t\ttracks = config_get_int(main->Config(), \"AdvOut\", \"FLVTrack\");\n\telse\n\t\ttracks = config_get_int(main->Config(), \"AdvOut\", \"RecTracks\");\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tunsigned int cx = 0;\n\tunsigned int cy = 0;\n\tint idx = 0;\n\n\t/* Hack to allow recordings without any audio tracks selected. It is no\n\t * longer possible to select such a configuration in settings, but legacy\n\t * configurations might still have this configured and we don't want to\n\t * just break them. */\n\tif (tracks == 0)\n\t\ttracks = config_get_int(main->Config(), \"AdvOut\", \"TrackIndex\");\n\n\tif (useStreamEncoder) {\n\t\tobs_output_set_video_encoder(fileOutput, videoStreaming);\n\t\tif (replayBuffer)\n\t\t\tobs_output_set_video_encoder(replayBuffer, videoStreaming);\n\t} else {\n\t\tif (rescaleFilter != OBS_SCALE_DISABLE && rescaleRes && *rescaleRes) {\n\t\t\tif (sscanf(rescaleRes, \"%ux%u\", &cx, &cy) != 2) {\n\t\t\t\tcx = 0;\n\t\t\t\tcy = 0;\n\t\t\t}\n\t\t}\n\n\t\tobs_encoder_set_scaled_size(videoRecording, cx, cy);\n\t\tobs_encoder_set_gpu_scale_type(videoRecording, (obs_scale_type)rescaleFilter);\n\t\tobs_output_set_video_encoder(fileOutput, videoRecording);\n\t\tif (replayBuffer)\n\t\t\tobs_output_set_video_encoder(replayBuffer, videoRecording);\n\t}\n\n\tif (!flv) {\n\t\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\t\tif ((tracks & (1 << i)) != 0) {\n\t\t\t\tobs_output_set_audio_encoder(fileOutput, recordTrack[i], idx);\n\t\t\t\tif (replayBuffer)\n\t\t\t\t\tobs_output_set_audio_encoder(replayBuffer, recordTrack[i], idx);\n\t\t\t\tidx++;\n\t\t\t}\n\t\t}\n\t} else if (flv && tracks != 0) {\n\t\tobs_output_set_audio_encoder(fileOutput, recordTrack[tracks - 1], idx);\n\n\t\tif (replayBuffer)\n\t\t\tobs_output_set_audio_encoder(replayBuffer, recordTrack[tracks - 1], idx);\n\t}\n\n\t// Use fragmented MOV/MP4 if user has not already specified custom movflags\n\tif (is_fragmented && (!mux || strstr(mux, \"movflags\") == NULL)) {\n\t\tstring mux_frag = \"movflags=frag_keyframe+empty_moov+delay_moov\";\n\t\tif (mux) {\n\t\t\tmux_frag += \" \";\n\t\t\tmux_frag += mux;\n\t\t}\n\t\tobs_data_set_string(settings, \"muxer_settings\", mux_frag.c_str());\n\t} else {\n\t\tif (is_fragmented)\n\t\t\tblog(LOG_WARNING, \"User enabled fragmented recording, \"\n\t\t\t\t\t  \"but custom muxer settings contained movflags.\");\n\t\tobs_data_set_string(settings, \"muxer_settings\", mux);\n\t}\n\n\tobs_data_set_string(settings, \"path\", path);\n\tobs_output_update(fileOutput, settings);\n\tif (replayBuffer)\n\t\tobs_output_update(replayBuffer, settings);\n}\n\ninline void AdvancedOutput::SetupFFmpeg()\n{\n\tconst char *url = config_get_string(main->Config(), \"AdvOut\", \"FFURL\");\n\tint vBitrate = config_get_int(main->Config(), \"AdvOut\", \"FFVBitrate\");\n\tint gopSize = config_get_int(main->Config(), \"AdvOut\", \"FFVGOPSize\");\n\tbool rescale = config_get_bool(main->Config(), \"AdvOut\", \"FFRescale\");\n\tconst char *rescaleRes = config_get_string(main->Config(), \"AdvOut\", \"FFRescaleRes\");\n\tconst char *formatName = config_get_string(main->Config(), \"AdvOut\", \"FFFormat\");\n\tconst char *mimeType = config_get_string(main->Config(), \"AdvOut\", \"FFFormatMimeType\");\n\tconst char *muxCustom = config_get_string(main->Config(), \"AdvOut\", \"FFMCustom\");\n\tconst char *vEncoder = config_get_string(main->Config(), \"AdvOut\", \"FFVEncoder\");\n\tint vEncoderId = config_get_int(main->Config(), \"AdvOut\", \"FFVEncoderId\");\n\tconst char *vEncCustom = config_get_string(main->Config(), \"AdvOut\", \"FFVCustom\");\n\tint aBitrate = config_get_int(main->Config(), \"AdvOut\", \"FFABitrate\");\n\tint aMixes = config_get_int(main->Config(), \"AdvOut\", \"FFAudioMixes\");\n\tconst char *aEncoder = config_get_string(main->Config(), \"AdvOut\", \"FFAEncoder\");\n\tint aEncoderId = config_get_int(main->Config(), \"AdvOut\", \"FFAEncoderId\");\n\tconst char *aEncCustom = config_get_string(main->Config(), \"AdvOut\", \"FFACustom\");\n\n\tOBSDataArrayAutoRelease audio_names = obs_data_array_create();\n\n\tfor (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\tstring cfg_name = \"Track\";\n\t\tcfg_name += to_string((int)i + 1);\n\t\tcfg_name += \"Name\";\n\n\t\tconst char *audioName = config_get_string(main->Config(), \"AdvOut\", cfg_name.c_str());\n\n\t\tOBSDataAutoRelease item = obs_data_create();\n\t\tobs_data_set_string(item, \"name\", audioName);\n\t\tobs_data_array_push_back(audio_names, item);\n\t}\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tobs_data_set_array(settings, \"audio_names\", audio_names);\n\tobs_data_set_string(settings, \"url\", url);\n\tobs_data_set_string(settings, \"format_name\", formatName);\n\tobs_data_set_string(settings, \"format_mime_type\", mimeType);\n\tobs_data_set_string(settings, \"muxer_settings\", muxCustom);\n\tobs_data_set_int(settings, \"gop_size\", gopSize);\n\tobs_data_set_int(settings, \"video_bitrate\", vBitrate);\n\tobs_data_set_string(settings, \"video_encoder\", vEncoder);\n\tobs_data_set_int(settings, \"video_encoder_id\", vEncoderId);\n\tobs_data_set_string(settings, \"video_settings\", vEncCustom);\n\tobs_data_set_int(settings, \"audio_bitrate\", aBitrate);\n\tobs_data_set_string(settings, \"audio_encoder\", aEncoder);\n\tobs_data_set_int(settings, \"audio_encoder_id\", aEncoderId);\n\tobs_data_set_string(settings, \"audio_settings\", aEncCustom);\n\n\tif (rescale && rescaleRes && *rescaleRes) {\n\t\tint width;\n\t\tint height;\n\t\tint val = sscanf(rescaleRes, \"%dx%d\", &width, &height);\n\n\t\tif (val == 2 && width && height) {\n\t\t\tobs_data_set_int(settings, \"scale_width\", width);\n\t\t\tobs_data_set_int(settings, \"scale_height\", height);\n\t\t}\n\t}\n\n\tobs_output_set_mixers(fileOutput, aMixes);\n\tobs_output_set_media(fileOutput, obs_get_video(), obs_get_audio());\n\tobs_output_update(fileOutput, settings);\n}\n\nstatic inline void SetEncoderName(obs_encoder_t *encoder, const char *name, const char *defaultName)\n{\n\tobs_encoder_set_name(encoder, (name && *name) ? name : defaultName);\n}\n\ninline void AdvancedOutput::UpdateAudioSettings()\n{\n\tbool applyServiceSettings = config_get_bool(main->Config(), \"AdvOut\", \"ApplyServiceSettings\");\n\tbool enforceBitrate = !config_get_bool(main->Config(), \"Stream1\", \"IgnoreRecommended\");\n\tint streamTrackIndex = config_get_int(main->Config(), \"AdvOut\", \"TrackIndex\");\n\tint vodTrackIndex = config_get_int(main->Config(), \"AdvOut\", \"VodTrackIndex\");\n\tconst char *audioEncoder = config_get_string(main->Config(), \"AdvOut\", \"AudioEncoder\");\n\tconst char *recAudioEncoder = config_get_string(main->Config(), \"AdvOut\", \"RecAudioEncoder\");\n\n\tbool is_multitrack_output = allowsMultiTrack();\n\n\tOBSDataAutoRelease settings[MAX_AUDIO_MIXES];\n\n\tfor (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\tstring cfg_name = \"Track\";\n\t\tcfg_name += to_string((int)i + 1);\n\t\tcfg_name += \"Name\";\n\t\tconst char *name = config_get_string(main->Config(), \"AdvOut\", cfg_name.c_str());\n\n\t\tstring def_name = \"Track\";\n\t\tdef_name += to_string((int)i + 1);\n\t\tSetEncoderName(recordTrack[i], name, def_name.c_str());\n\t\tSetEncoderName(streamTrack[i], name, def_name.c_str());\n\t}\n\n\tfor (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\tint track = (int)(i + 1);\n\t\tsettings[i] = obs_data_create();\n\t\tobs_data_set_int(settings[i], \"bitrate\", GetAudioBitrate(i, recAudioEncoder));\n\n\t\tobs_encoder_update(recordTrack[i], settings[i]);\n\n\t\tobs_data_set_int(settings[i], \"bitrate\", GetAudioBitrate(i, audioEncoder));\n\n\t\tif (!is_multitrack_output) {\n\t\t\tif (track == streamTrackIndex || track == vodTrackIndex) {\n\t\t\t\tif (applyServiceSettings) {\n\t\t\t\t\tint bitrate = (int)obs_data_get_int(settings[i], \"bitrate\");\n\t\t\t\t\tobs_service_apply_encoder_settings(main->GetService(), nullptr, settings[i]);\n\n\t\t\t\t\tif (!enforceBitrate)\n\t\t\t\t\t\tobs_data_set_int(settings[i], \"bitrate\", bitrate);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (track == streamTrackIndex)\n\t\t\t\tobs_encoder_update(streamAudioEnc, settings[i]);\n\t\t\tif (track == vodTrackIndex)\n\t\t\t\tobs_encoder_update(streamArchiveEnc, settings[i]);\n\t\t} else {\n\t\t\tobs_encoder_update(streamTrack[i], settings[i]);\n\t\t}\n\t}\n}\n\nvoid AdvancedOutput::SetupOutputs()\n{\n\tobs_encoder_set_video(videoStreaming, obs_get_video());\n\tif (videoRecording)\n\t\tobs_encoder_set_video(videoRecording, obs_get_video());\n\tfor (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\tobs_encoder_set_audio(streamTrack[i], obs_get_audio());\n\t\tobs_encoder_set_audio(recordTrack[i], obs_get_audio());\n\t}\n\tobs_encoder_set_audio(streamAudioEnc, obs_get_audio());\n\tobs_encoder_set_audio(streamArchiveEnc, obs_get_audio());\n\n\tSetupStreaming();\n\n\tif (ffmpegOutput)\n\t\tSetupFFmpeg();\n\telse\n\t\tSetupRecording();\n}\n\nint AdvancedOutput::GetAudioBitrate(size_t i, const char *id) const\n{\n\tstatic const char *names[] = {\n\t\t\"Track1Bitrate\", \"Track2Bitrate\", \"Track3Bitrate\", \"Track4Bitrate\", \"Track5Bitrate\", \"Track6Bitrate\",\n\t};\n\tint bitrate = (int)config_get_uint(main->Config(), \"AdvOut\", names[i]);\n\treturn FindClosestAvailableAudioBitrate(id, bitrate);\n}\n\ninline std::optional<size_t> AdvancedOutput::VodTrackMixerIdx(obs_service_t *service)\n{\n\tint streamTrackIndex = config_get_int(main->Config(), \"AdvOut\", \"TrackIndex\");\n\tbool vodTrackEnabled = config_get_bool(main->Config(), \"AdvOut\", \"VodTrackEnabled\");\n\tint vodTrackIndex = config_get_int(main->Config(), \"AdvOut\", \"VodTrackIndex\");\n\tbool enableForCustomServer = config_get_bool(App()->GetUserConfig(), \"General\", \"EnableCustomServerVodTrack\");\n\n\tconst char *id = obs_service_get_id(service);\n\tif (strcmp(id, \"rtmp_custom\") == 0) {\n\t\tvodTrackEnabled = enableForCustomServer ? vodTrackEnabled : false;\n\t} else {\n\t\tOBSDataAutoRelease settings = obs_service_get_settings(service);\n\t\tconst char *service = obs_data_get_string(settings, \"service\");\n\t\tif (!ServiceSupportsVodTrack(service))\n\t\t\tvodTrackEnabled = false;\n\t}\n\n\tif (vodTrackEnabled && streamTrackIndex != vodTrackIndex)\n\t\treturn {vodTrackIndex - 1};\n\treturn std::nullopt;\n}\n\ninline void AdvancedOutput::SetupVodTrack(obs_service_t *service)\n{\n\tif (VodTrackMixerIdx(service).has_value())\n\t\tobs_output_set_audio_encoder(streamOutput, streamArchiveEnc, 1);\n\telse\n\t\tclear_archive_encoder(streamOutput, ADV_ARCHIVE_NAME);\n}\n\nstd::shared_future<void> AdvancedOutput::SetupStreaming(obs_service_t *service,\n\t\t\t\t\t\t\tSetupStreamingContinuation_t continuation)\n{\n\tint multiTrackAudioMixes = config_get_int(main->Config(), \"AdvOut\", \"StreamMultiTrackAudioMixes\");\n\n\tbool is_multitrack_output = allowsMultiTrack();\n\n\tif (!useStreamEncoder || (!ffmpegOutput && !obs_output_active(fileOutput))) {\n\t\tUpdateStreamSettings();\n\t}\n\n\tUpdateAudioSettings();\n\n\tif (!Active())\n\t\tSetupOutputs();\n\n\tAuth *auth = main->GetAuth();\n\tif (auth)\n\t\tauth->OnStreamConfig();\n\n\t/* --------------------- */\n\n\tconst char *type = GetStreamOutputType(service);\n\tif (!type) {\n\t\tcontinuation(false);\n\t\treturn StartMultitrackVideoStreamingGuard::MakeReadyFuture();\n\t}\n\n\tconst char *audio_encoder_id = config_get_string(main->Config(), \"AdvOut\", \"AudioEncoder\");\n\tint streamTrackIndex = config_get_int(main->Config(), \"AdvOut\", \"TrackIndex\") - 1;\n\n\tauto handle_multitrack_video_result = [this, type = std::string{type}, is_multitrack_output,\n\t\t\t\t\t       multiTrackAudioMixes](std::optional<bool> multitrackVideoResult) {\n\t\tif (multitrackVideoResult.has_value())\n\t\t\treturn multitrackVideoResult.value();\n\n\t\t/* XXX: this is messy and disgusting and should be refactored */\n\t\tif (outputType != type) {\n\t\t\tstreamDelayStarting.Disconnect();\n\t\t\tstreamStopping.Disconnect();\n\t\t\tstartStreaming.Disconnect();\n\t\t\tstopStreaming.Disconnect();\n\n\t\t\tstreamOutput = obs_output_create(type.c_str(), \"adv_stream\", nullptr, nullptr);\n\t\t\tif (!streamOutput) {\n\t\t\t\tblog(LOG_WARNING, \"Creation of stream output type '%s' failed!\", type.c_str());\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tstreamDelayStarting.Connect(obs_output_get_signal_handler(streamOutput), \"starting\",\n\t\t\t\t\t\t    OBSStreamStarting, this);\n\t\t\tstreamStopping.Connect(obs_output_get_signal_handler(streamOutput), \"stopping\",\n\t\t\t\t\t       OBSStreamStopping, this);\n\n\t\t\tstartStreaming.Connect(obs_output_get_signal_handler(streamOutput), \"start\", OBSStartStreaming,\n\t\t\t\t\t       this);\n\t\t\tstopStreaming.Connect(obs_output_get_signal_handler(streamOutput), \"stop\", OBSStopStreaming,\n\t\t\t\t\t      this);\n\n\t\t\toutputType = type;\n\t\t}\n\n\t\tobs_output_set_video_encoder(streamOutput, videoStreaming);\n\t\tif (whipSimulcastEncoders != nullptr) {\n\t\t\twhipSimulcastEncoders->SetStreamOutput(streamOutput);\n\t\t}\n\t\tobs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0);\n\n\t\tif (!is_multitrack_output) {\n\t\t\tobs_output_set_audio_encoder(streamOutput, streamAudioEnc, 0);\n\t\t} else {\n\t\t\tint idx = 0;\n\t\t\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\t\t\tif ((multiTrackAudioMixes & (1 << i)) != 0) {\n\t\t\t\t\tobs_output_set_audio_encoder(streamOutput, streamTrack[i], idx);\n\t\t\t\t\tidx++;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t};\n\n\treturn SetupMultitrackVideo(service, audio_encoder_id, static_cast<size_t>(streamTrackIndex),\n\t\t\t\t    VodTrackMixerIdx(service), [=](std::optional<bool> res) {\n\t\t\t\t\t    continuation(handle_multitrack_video_result(res));\n\t\t\t\t    });\n}\n\nbool AdvancedOutput::StartStreaming(obs_service_t *service)\n{\n\tobs_output_set_service(streamOutput, service);\n\n\tbool reconnect = config_get_bool(main->Config(), \"Output\", \"Reconnect\");\n\tint retryDelay = config_get_int(main->Config(), \"Output\", \"RetryDelay\");\n\tint maxRetries = config_get_int(main->Config(), \"Output\", \"MaxRetries\");\n\tbool useDelay = config_get_bool(main->Config(), \"Output\", \"DelayEnable\");\n\tint delaySec = config_get_int(main->Config(), \"Output\", \"DelaySec\");\n\tbool preserveDelay = config_get_bool(main->Config(), \"Output\", \"DelayPreserve\");\n\tconst char *bindIP = config_get_string(main->Config(), \"Output\", \"BindIP\");\n\tconst char *ipFamily = config_get_string(main->Config(), \"Output\", \"IPFamily\");\n#ifdef _WIN32\n\tbool enableNewSocketLoop = config_get_bool(main->Config(), \"Output\", \"NewSocketLoopEnable\");\n\tbool enableLowLatencyMode = config_get_bool(main->Config(), \"Output\", \"LowLatencyEnable\");\n#endif\n\tbool enableDynBitrate = config_get_bool(main->Config(), \"Output\", \"DynamicBitrate\");\n\n\tif (multitrackVideo && multitrackVideoActive &&\n\t    !multitrackVideo->HandleIncompatibleSettings(main, main->Config(), service, enableDynBitrate)) {\n\t\tmultitrackVideoActive = false;\n\t\treturn false;\n\t}\n\n\tbool is_rtmp = false;\n\tobs_service_t *service_obj = main->GetService();\n\tconst char *protocol = obs_service_get_protocol(service_obj);\n\tif (protocol) {\n\t\tif (astrcmpi_n(protocol, RTMP_PROTOCOL, strlen(RTMP_PROTOCOL)) == 0)\n\t\t\tis_rtmp = true;\n\t}\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"bind_ip\", bindIP);\n\tobs_data_set_string(settings, \"ip_family\", ipFamily);\n#ifdef _WIN32\n\tobs_data_set_bool(settings, \"new_socket_loop_enabled\", enableNewSocketLoop);\n\tobs_data_set_bool(settings, \"low_latency_mode_enabled\", enableLowLatencyMode);\n#endif\n\tobs_data_set_bool(settings, \"dyn_bitrate\", enableDynBitrate);\n\n\tauto streamOutput = StreamingOutput(); // shadowing is sort of bad, but also convenient\n\n\tobs_output_update(streamOutput, settings);\n\n\tif (!reconnect)\n\t\tmaxRetries = 0;\n\n\tobs_output_set_delay(streamOutput, useDelay ? delaySec : 0, preserveDelay ? OBS_OUTPUT_DELAY_PRESERVE : 0);\n\n\tobs_output_set_reconnect_settings(streamOutput, maxRetries, retryDelay);\n\tif (is_rtmp) {\n\t\tSetupVodTrack(service);\n\t}\n\tif (obs_output_start(streamOutput)) {\n\t\tif (multitrackVideo && multitrackVideoActive)\n\t\t\tmultitrackVideo->StartedStreaming();\n\t\treturn true;\n\t}\n\n\tif (multitrackVideo && multitrackVideoActive)\n\t\tmultitrackVideoActive = false;\n\n\tconst char *error = obs_output_get_last_error(streamOutput);\n\tbool hasLastError = error && *error;\n\tif (hasLastError)\n\t\tlastError = error;\n\telse\n\t\tlastError = string();\n\n\tconst char *type = obs_output_get_id(streamOutput);\n\tblog(LOG_WARNING, \"Stream output type '%s' failed to start!%s%s\", type, hasLastError ? \"  Last Error: \" : \"\",\n\t     hasLastError ? error : \"\");\n\treturn false;\n}\n\nbool AdvancedOutput::StartRecording()\n{\n\tconst char *path;\n\tconst char *recFormat;\n\tconst char *filenameFormat;\n\tbool noSpace = false;\n\tbool overwriteIfExists = false;\n\tbool splitFile;\n\tconst char *splitFileType;\n\tint splitFileTime;\n\tint splitFileSize;\n\n\tif (!useStreamEncoder) {\n\t\tif (!ffmpegOutput) {\n\t\t\tUpdateRecordingSettings();\n\t\t}\n\t} else if (!obs_output_active(StreamingOutput())) {\n\t\tUpdateStreamSettings();\n\t}\n\n\tUpdateAudioSettings();\n\n\tif (!Active())\n\t\tSetupOutputs();\n\n\tif (!ffmpegOutput || ffmpegRecording) {\n\t\tpath = config_get_string(main->Config(), \"AdvOut\", ffmpegRecording ? \"FFFilePath\" : \"RecFilePath\");\n\t\trecFormat = config_get_string(main->Config(), \"AdvOut\", ffmpegRecording ? \"FFExtension\" : \"RecFormat2\");\n\t\tfilenameFormat = config_get_string(main->Config(), \"Output\", \"FilenameFormatting\");\n\t\toverwriteIfExists = config_get_bool(main->Config(), \"Output\", \"OverwriteIfExists\");\n\t\tnoSpace = config_get_bool(main->Config(), \"AdvOut\",\n\t\t\t\t\t  ffmpegRecording ? \"FFFileNameWithoutSpace\" : \"RecFileNameWithoutSpace\");\n\t\tsplitFile = config_get_bool(main->Config(), \"AdvOut\", \"RecSplitFile\");\n\n\t\tstring strPath = GetRecordingFilename(path, recFormat, noSpace, overwriteIfExists, filenameFormat,\n\t\t\t\t\t\t      ffmpegRecording);\n\n\t\tOBSDataAutoRelease settings = obs_data_create();\n\t\tobs_data_set_string(settings, ffmpegRecording ? \"url\" : \"path\", strPath.c_str());\n\n\t\tif (splitFile) {\n\t\t\tsplitFileType = config_get_string(main->Config(), \"AdvOut\", \"RecSplitFileType\");\n\t\t\tsplitFileTime = (astrcmpi(splitFileType, \"Time\") == 0)\n\t\t\t\t\t\t? config_get_int(main->Config(), \"AdvOut\", \"RecSplitFileTime\")\n\t\t\t\t\t\t: 0;\n\t\t\tsplitFileSize = (astrcmpi(splitFileType, \"Size\") == 0)\n\t\t\t\t\t\t? config_get_int(main->Config(), \"AdvOut\", \"RecSplitFileSize\")\n\t\t\t\t\t\t: 0;\n\t\t\tstring ext = GetFormatExt(recFormat);\n\t\t\tobs_data_set_string(settings, \"directory\", path);\n\t\t\tobs_data_set_string(settings, \"format\", filenameFormat);\n\t\t\tobs_data_set_string(settings, \"extension\", ext.c_str());\n\t\t\tobs_data_set_bool(settings, \"allow_spaces\", !noSpace);\n\t\t\tobs_data_set_bool(settings, \"allow_overwrite\", overwriteIfExists);\n\t\t\tobs_data_set_bool(settings, \"split_file\", true);\n\t\t\tobs_data_set_int(settings, \"max_time_sec\", splitFileTime * 60);\n\t\t\tobs_data_set_int(settings, \"max_size_mb\", splitFileSize);\n\t\t}\n\n\t\tobs_output_update(fileOutput, settings);\n\t}\n\n\tif (!obs_output_start(fileOutput)) {\n\t\tQString error_reason;\n\t\tconst char *error = obs_output_get_last_error(fileOutput);\n\t\tif (error)\n\t\t\terror_reason = QT_UTF8(error);\n\t\telse\n\t\t\terror_reason = QTStr(\"Output.StartFailedGeneric\");\n\t\tQMessageBox::critical(main, QTStr(\"Output.StartRecordingFailed\"), error_reason);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool AdvancedOutput::StartReplayBuffer()\n{\n\tconst char *path;\n\tconst char *recFormat;\n\tconst char *filenameFormat;\n\tbool noSpace = false;\n\tbool overwriteIfExists = false;\n\tconst char *rbPrefix;\n\tconst char *rbSuffix;\n\tint rbTime;\n\tint rbSize;\n\n\tif (!useStreamEncoder) {\n\t\tif (!ffmpegOutput)\n\t\t\tUpdateRecordingSettings();\n\t} else if (!obs_output_active(StreamingOutput())) {\n\t\tUpdateStreamSettings();\n\t}\n\n\tUpdateAudioSettings();\n\n\tif (!Active())\n\t\tSetupOutputs();\n\n\tif (!ffmpegOutput || ffmpegRecording) {\n\t\tpath = config_get_string(main->Config(), \"AdvOut\", ffmpegRecording ? \"FFFilePath\" : \"RecFilePath\");\n\t\trecFormat = config_get_string(main->Config(), \"AdvOut\", ffmpegRecording ? \"FFExtension\" : \"RecFormat2\");\n\t\tfilenameFormat = config_get_string(main->Config(), \"Output\", \"FilenameFormatting\");\n\t\toverwriteIfExists = config_get_bool(main->Config(), \"Output\", \"OverwriteIfExists\");\n\t\tnoSpace = config_get_bool(main->Config(), \"AdvOut\",\n\t\t\t\t\t  ffmpegRecording ? \"FFFileNameWithoutSpace\" : \"RecFileNameWithoutSpace\");\n\t\trbPrefix = config_get_string(main->Config(), \"SimpleOutput\", \"RecRBPrefix\");\n\t\trbSuffix = config_get_string(main->Config(), \"SimpleOutput\", \"RecRBSuffix\");\n\t\trbTime = config_get_int(main->Config(), \"AdvOut\", \"RecRBTime\");\n\t\trbSize = config_get_int(main->Config(), \"AdvOut\", \"RecRBSize\");\n\n\t\tstring f = GetFormatString(filenameFormat, rbPrefix, rbSuffix);\n\t\tstring ext = GetFormatExt(recFormat);\n\n\t\tOBSDataAutoRelease settings = obs_data_create();\n\n\t\tobs_data_set_string(settings, \"directory\", path);\n\t\tobs_data_set_string(settings, \"format\", f.c_str());\n\t\tobs_data_set_string(settings, \"extension\", ext.c_str());\n\t\tobs_data_set_bool(settings, \"allow_spaces\", !noSpace);\n\t\tobs_data_set_int(settings, \"max_time_sec\", rbTime);\n\t\tobs_data_set_int(settings, \"max_size_mb\", usesBitrate ? 0 : rbSize);\n\n\t\tobs_output_update(replayBuffer, settings);\n\t}\n\n\tif (!obs_output_start(replayBuffer)) {\n\t\tQString error_reason;\n\t\tconst char *error = obs_output_get_last_error(replayBuffer);\n\t\tif (error)\n\t\t\terror_reason = QT_UTF8(error);\n\t\telse\n\t\t\terror_reason = QTStr(\"Output.StartFailedGeneric\");\n\t\tQMessageBox::critical(main, QTStr(\"Output.StartReplayFailed\"), error_reason);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid AdvancedOutput::StopStreaming(bool force)\n{\n\tauto output = StreamingOutput();\n\tif (force && output)\n\t\tobs_output_force_stop(output);\n\telse if (multitrackVideo && multitrackVideoActive)\n\t\tmultitrackVideo->StopStreaming();\n\telse\n\t\tobs_output_stop(output);\n}\n\nvoid AdvancedOutput::StopRecording(bool force)\n{\n\tif (force)\n\t\tobs_output_force_stop(fileOutput);\n\telse\n\t\tobs_output_stop(fileOutput);\n}\n\nvoid AdvancedOutput::StopReplayBuffer(bool force)\n{\n\tif (force)\n\t\tobs_output_force_stop(replayBuffer);\n\telse\n\t\tobs_output_stop(replayBuffer);\n}\n\nbool AdvancedOutput::StreamingActive() const\n{\n\treturn obs_output_active(StreamingOutput());\n}\n\nbool AdvancedOutput::RecordingActive() const\n{\n\treturn obs_output_active(fileOutput);\n}\n\nbool AdvancedOutput::ReplayBufferActive() const\n{\n\treturn obs_output_active(replayBuffer);\n}\n"
  },
  {
    "path": "frontend/utility/AdvancedOutput.hpp",
    "content": "#pragma once\n\n#include \"BasicOutputHandler.hpp\"\n\nstruct AdvancedOutput : BasicOutputHandler {\n\tOBSEncoder streamAudioEnc;\n\tOBSEncoder streamArchiveEnc;\n\tOBSEncoder streamTrack[MAX_AUDIO_MIXES];\n\tOBSEncoder recordTrack[MAX_AUDIO_MIXES];\n\tOBSEncoder videoStreaming;\n\tOBSEncoder videoRecording;\n\n\tbool ffmpegOutput;\n\tbool ffmpegRecording;\n\tbool useStreamEncoder;\n\tbool useStreamAudioEncoder;\n\tbool usesBitrate = false;\n\n\tAdvancedOutput(OBSBasic *main_);\n\n\tinline void UpdateStreamSettings();\n\tinline void UpdateRecordingSettings();\n\tinline void UpdateAudioSettings();\n\tvirtual void Update() override;\n\n\tinline std::optional<size_t> VodTrackMixerIdx(obs_service_t *service);\n\tinline void SetupVodTrack(obs_service_t *service);\n\n\tinline void SetupStreaming();\n\tinline void SetupRecording();\n\tinline void SetupFFmpeg();\n\tvoid SetupOutputs() override;\n\tint GetAudioBitrate(size_t i, const char *id) const;\n\n\tvirtual std::shared_future<void> SetupStreaming(obs_service_t *service,\n\t\t\t\t\t\t\tSetupStreamingContinuation_t continuation) override;\n\tvirtual bool StartStreaming(obs_service_t *service) override;\n\tvirtual bool StartRecording() override;\n\tvirtual bool StartReplayBuffer() override;\n\tvirtual void StopStreaming(bool force) override;\n\tvirtual void StopRecording(bool force) override;\n\tvirtual void StopReplayBuffer(bool force) override;\n\tvirtual bool StreamingActive() const override;\n\tvirtual bool RecordingActive() const override;\n\tvirtual bool ReplayBufferActive() const override;\n\tbool allowsMultiTrack();\n};\n"
  },
  {
    "path": "frontend/utility/AutoUpdateThread.cpp",
    "content": "#include \"AutoUpdateThread.hpp\"\n#include \"ui_OBSUpdate.h\"\n\n#include <OBSApp.hpp>\n#include <dialogs/OBSUpdate.hpp>\n#include <updater/manifest.hpp>\n#include <utility/WhatsNewInfoThread.hpp>\n#include <utility/update-helpers.hpp>\n\n#include <qt-wrappers.hpp>\n\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#include <shellapi.h>\n\n#include \"moc_AutoUpdateThread.cpp\"\n\n/* ------------------------------------------------------------------------ */\n\n#ifndef WIN_MANIFEST_URL\n#define WIN_MANIFEST_URL \"https://obsproject.com/update_studio/manifest.json\"\n#endif\n\n#ifndef WIN_MANIFEST_BASE_URL\n#define WIN_MANIFEST_BASE_URL \"https://obsproject.com/update_studio/\"\n#endif\n\n#ifndef WIN_BRANCHES_URL\n#define WIN_BRANCHES_URL \"https://obsproject.com/update_studio/branches.json\"\n#endif\n\n#ifndef WIN_DEFAULT_BRANCH\n#define WIN_DEFAULT_BRANCH \"stable\"\n#endif\n\n#ifndef WIN_UPDATER_URL\n#define WIN_UPDATER_URL \"https://obsproject.com/update_studio/updater.exe\"\n#endif\n\n/* ------------------------------------------------------------------------ */\n\nusing namespace std;\nusing namespace updater;\n\nextern char *GetConfigPathPtr(const char *name);\n\nstatic bool ParseUpdateManifest(const char *manifest_data, bool *updatesAvailable, string &notes, string &updateVer,\n\t\t\t\tconst string &branch)\ntry {\n\tconstexpr uint64_t currentVersion = (uint64_t)LIBOBS_API_VER << 16ULL | OBS_RELEASE_CANDIDATE << 8ULL |\n\t\t\t\t\t    OBS_BETA;\n\tconstexpr bool isPreRelease = currentVersion & 0xffff || std::char_traits<char>::length(OBS_COMMIT);\n\n\tjson manifestContents = json::parse(manifest_data);\n\tManifest manifest = manifestContents.get<Manifest>();\n\n\tif (manifest.version_major == 0 && manifest.commit.empty())\n\t\tthrow strprintf(\"Invalid version number: %d.%d.%d\", manifest.version_major, manifest.version_minor,\n\t\t\t\tmanifest.version_patch);\n\n\tnotes = manifest.notes;\n\n\tif (manifest.commit.empty()) {\n\t\tuint64_t new_ver = MAKE_SEMANTIC_VERSION((uint64_t)manifest.version_major,\n\t\t\t\t\t\t\t (uint64_t)manifest.version_minor,\n\t\t\t\t\t\t\t (uint64_t)manifest.version_patch);\n\t\tnew_ver <<= 16;\n\t\t/* RC builds are shifted so that rc1 and beta1 versions do not result\n\t\t * in the same new_ver. */\n\t\tif (manifest.rc > 0)\n\t\t\tnew_ver |= (uint64_t)manifest.rc << 8;\n\t\telse if (manifest.beta > 0)\n\t\t\tnew_ver |= (uint64_t)manifest.beta;\n\n\t\tupdateVer = to_string(new_ver);\n\n\t\t/* When using a pre-release build or non-default branch we only check if\n\t\t * the manifest version is different, so that it can be rolled back. */\n\t\tif (branch != WIN_DEFAULT_BRANCH || isPreRelease)\n\t\t\t*updatesAvailable = new_ver != currentVersion;\n\t\telse\n\t\t\t*updatesAvailable = new_ver > currentVersion;\n\t} else {\n\t\t/* Test or nightly builds may not have a (valid) version number,\n\t\t * so compare commit hashes instead. */\n\t\tupdateVer = manifest.commit.substr(0, 8);\n\t\t*updatesAvailable = !currentVersion || manifest.commit.compare(0, strlen(OBS_COMMIT), OBS_COMMIT) != 0;\n\t}\n\n\treturn true;\n\n} catch (string &text) {\n\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, text.c_str());\n\treturn false;\n}\n\n/* ------------------------------------------------------------------------ */\n\nbool GetBranchAndUrl(string &selectedBranch, string &manifestUrl)\n{\n\tconst char *config_branch = config_get_string(App()->GetAppConfig(), \"General\", \"UpdateBranch\");\n\tif (!config_branch)\n\t\treturn true;\n\n\tbool found = false;\n\tfor (const UpdateBranch &branch : App()->GetBranches()) {\n\t\tif (branch.name != config_branch)\n\t\t\tcontinue;\n\t\t/* A branch that is found but disabled will just silently fall back to\n\t\t * the default. But if the branch was removed entirely, the user should\n\t\t * be warned, so leave this false *only* if the branch was removed. */\n\t\tfound = true;\n\n\t\tif (branch.is_enabled) {\n\t\t\tselectedBranch = branch.name.toStdString();\n\t\t\tif (branch.name != WIN_DEFAULT_BRANCH) {\n\t\t\t\tmanifestUrl = WIN_MANIFEST_BASE_URL;\n\t\t\t\tmanifestUrl += \"manifest_\" + branch.name.toStdString() + \".json\";\n\t\t\t}\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn found;\n}\n\n/* ------------------------------------------------------------------------ */\n\nvoid AutoUpdateThread::infoMsg(const QString &title, const QString &text)\n{\n\tOBSMessageBox::information(App()->GetMainWindow(), title, text);\n}\n\nvoid AutoUpdateThread::info(const QString &title, const QString &text)\n{\n\tQMetaObject::invokeMethod(this, \"infoMsg\", Qt::BlockingQueuedConnection, Q_ARG(QString, title),\n\t\t\t\t  Q_ARG(QString, text));\n}\n\nint AutoUpdateThread::queryUpdateSlot(bool localManualUpdate, const QString &text)\n{\n\tOBSUpdate updateDlg(App()->GetMainWindow(), localManualUpdate, text);\n\treturn updateDlg.exec();\n}\n\nint AutoUpdateThread::queryUpdate(bool localManualUpdate, const char *text_utf8)\n{\n\tint ret = OBSUpdate::No;\n\tQString text = text_utf8;\n\tQMetaObject::invokeMethod(this, \"queryUpdateSlot\", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, ret),\n\t\t\t\t  Q_ARG(bool, localManualUpdate), Q_ARG(QString, text));\n\treturn ret;\n}\n\nbool AutoUpdateThread::queryRepairSlot()\n{\n\tQMessageBox::StandardButton res =\n\t\tOBSMessageBox::question(App()->GetMainWindow(), QTStr(\"Updater.RepairConfirm.Title\"),\n\t\t\t\t\tQTStr(\"Updater.RepairConfirm.Text\"), QMessageBox::Yes | QMessageBox::Cancel);\n\n\treturn res == QMessageBox::Yes;\n}\n\nbool AutoUpdateThread::queryRepair()\n{\n\tbool ret = false;\n\tQMetaObject::invokeMethod(this, \"queryRepairSlot\", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ret));\n\treturn ret;\n}\n\nvoid AutoUpdateThread::run()\ntry {\n\tstring text;\n\tstring branch = WIN_DEFAULT_BRANCH;\n\tstring manifestUrl = WIN_MANIFEST_URL;\n\tvector<string> extraHeaders;\n\tbool updatesAvailable = false;\n\n\tstruct FinishedTrigger {\n\t\tinline ~FinishedTrigger() { QMetaObject::invokeMethod(App()->GetMainWindow(), \"updateCheckFinished\"); }\n\t} finishedTrigger;\n\n\t/* ----------------------------------- *\n\t * get branches from server            */\n\n\tif (FetchAndVerifyFile(\"branches\", \"obs-studio\\\\updates\\\\branches.json\", WIN_BRANCHES_URL, &text))\n\t\tApp()->SetBranchData(text);\n\n\t/* ----------------------------------- *\n\t * check branch and get manifest url   */\n\n\tif (!GetBranchAndUrl(branch, manifestUrl)) {\n\t\tconfig_set_string(App()->GetAppConfig(), \"General\", \"UpdateBranch\", WIN_DEFAULT_BRANCH);\n\t\tinfo(QTStr(\"Updater.BranchNotFound.Title\"), QTStr(\"Updater.BranchNotFound.Text\"));\n\t}\n\n\t/* allow server to know if this was a manual update check in case\n\t * we want to allow people to bypass a configured rollout rate */\n\tif (manualUpdate)\n\t\textraHeaders.emplace_back(\"X-OBS2-ManualUpdate: 1\");\n\n\t/* ----------------------------------- *\n\t * get manifest from server            */\n\n\ttext.clear();\n\tif (!FetchAndVerifyFile(\"manifest\", \"obs-studio\\\\updates\\\\manifest.json\", manifestUrl.c_str(), &text,\n\t\t\t\textraHeaders))\n\t\treturn;\n\n\t/* ----------------------------------- *\n\t * check manifest for update           */\n\n\tstring notes;\n\tstring updateVer;\n\n\tif (!ParseUpdateManifest(text.c_str(), &updatesAvailable, notes, updateVer, branch))\n\t\tthrow string(\"Failed to parse manifest\");\n\n\tif (!updatesAvailable && !repairMode) {\n\t\tif (manualUpdate)\n\t\t\tinfo(QTStr(\"Updater.NoUpdatesAvailable.Title\"), QTStr(\"Updater.NoUpdatesAvailable.Text\"));\n\t\treturn;\n\t} else if (updatesAvailable && repairMode) {\n\t\tinfo(QTStr(\"Updater.RepairButUpdatesAvailable.Title\"), QTStr(\"Updater.RepairButUpdatesAvailable.Text\"));\n\t\treturn;\n\t}\n\n\t/* ----------------------------------- *\n\t * skip this version if set to skip    */\n\n\tconst char *skipUpdateVer = config_get_string(App()->GetAppConfig(), \"General\", \"SkipUpdateVersion\");\n\tif (!manualUpdate && !repairMode && skipUpdateVer && updateVer == skipUpdateVer)\n\t\treturn;\n\n\t/* ----------------------------------- *\n\t * fetch updater module                */\n\n\tif (!FetchAndVerifyFile(\"updater\", \"obs-studio\\\\updates\\\\updater.exe\", WIN_UPDATER_URL, nullptr))\n\t\treturn;\n\n\t/* ----------------------------------- *\n\t * query user for update               */\n\n\tif (repairMode) {\n\t\tif (!queryRepair())\n\t\t\treturn;\n\t} else {\n\t\tint queryResult = queryUpdate(manualUpdate, notes.c_str());\n\n\t\tif (queryResult == OBSUpdate::No) {\n\t\t\tif (!manualUpdate) {\n\t\t\t\tlong long t = (long long)time(nullptr);\n\t\t\t\tconfig_set_int(App()->GetAppConfig(), \"General\", \"LastUpdateCheck\", t);\n\t\t\t}\n\t\t\treturn;\n\n\t\t} else if (queryResult == OBSUpdate::Skip) {\n\t\t\tconfig_set_string(App()->GetAppConfig(), \"General\", \"SkipUpdateVersion\", updateVer.c_str());\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/* ----------------------------------- *\n\t * get working dir                     */\n\n\twchar_t cwd[MAX_PATH];\n\tGetModuleFileNameW(nullptr, cwd, _countof(cwd) - 1);\n\twchar_t *p = wcsrchr(cwd, '\\\\');\n\tif (p)\n\t\t*p = 0;\n\n\t/* ----------------------------------- *\n\t * execute updater                     */\n\n\tBPtr<char> updateFilePath = GetAppConfigPathPtr(\"obs-studio\\\\updates\\\\updater.exe\");\n\tBPtr<wchar_t> wUpdateFilePath;\n\n\tsize_t size = os_utf8_to_wcs_ptr(updateFilePath, 0, &wUpdateFilePath);\n\tif (!size)\n\t\tthrow string(\"Could not convert updateFilePath to wide\");\n\n\t/* note, can't use CreateProcess to launch as admin. */\n\tSHELLEXECUTEINFO execInfo = {};\n\n\texecInfo.cbSize = sizeof(execInfo);\n\texecInfo.lpFile = wUpdateFilePath;\n\n\tstring parameters;\n\tif (branch != WIN_DEFAULT_BRANCH)\n\t\tparameters += \"--branch=\" + branch;\n\n\tobs_cmdline_args obs_args = obs_get_cmdline_args();\n\tfor (int idx = 1; idx < obs_args.argc; idx++) {\n\t\tif (!parameters.empty())\n\t\t\tparameters += \" \";\n\n\t\tparameters += obs_args.argv[idx];\n\t}\n\n\t/* Portable mode can be enabled via sentinel files, so copying the\n\t * command line doesn't guarantee the flag to be there. */\n\tif (App()->IsPortableMode() && parameters.find(\"--portable\") == string::npos) {\n\t\tif (!parameters.empty())\n\t\t\tparameters += \" \";\n\t\tparameters += \"--portable\";\n\t}\n\n\tBPtr<wchar_t> lpParameters;\n\tsize = os_utf8_to_wcs_ptr(parameters.c_str(), 0, &lpParameters);\n\tif (!size && !parameters.empty())\n\t\tthrow string(\"Could not convert parameters to wide\");\n\n\texecInfo.lpParameters = lpParameters;\n\texecInfo.lpDirectory = cwd;\n\texecInfo.nShow = SW_SHOWNORMAL;\n\n\tif (!ShellExecuteEx(&execInfo)) {\n\t\tQString msg = QTStr(\"Updater.FailedToLaunch\");\n\t\tinfo(msg, msg);\n\t\tthrow strprintf(\"Can't launch updater '%s': %d\", updateFilePath.Get(), GetLastError());\n\t}\n\n\t/* force OBS to perform another update check immediately after updating\n\t * in case of issues with the new version */\n\tconfig_set_int(App()->GetAppConfig(), \"General\", \"LastUpdateCheck\", 0);\n\tconfig_set_string(App()->GetAppConfig(), \"General\", \"SkipUpdateVersion\", \"0\");\n\n\tQMetaObject::invokeMethod(App()->GetMainWindow(), \"close\");\n\n} catch (string &text) {\n\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, text.c_str());\n}\n"
  },
  {
    "path": "frontend/utility/AutoUpdateThread.hpp",
    "content": "#pragma once\n\n#include <QThread>\n\nclass AutoUpdateThread : public QThread {\n\tQ_OBJECT\n\n\tbool manualUpdate;\n\tbool repairMode;\n\tbool user_confirmed = false;\n\n\tvirtual void run() override;\n\n\tvoid info(const QString &title, const QString &text);\n\tint queryUpdate(bool manualUpdate, const char *text_utf8);\n\tbool queryRepair();\n\nprivate slots:\n\tvoid infoMsg(const QString &title, const QString &text);\n\tint queryUpdateSlot(bool manualUpdate, const QString &text);\n\tbool queryRepairSlot();\n\npublic:\n\tAutoUpdateThread(bool manualUpdate_, bool repairMode_ = false)\n\t\t: manualUpdate(manualUpdate_),\n\t\t  repairMode(repairMode_)\n\t{\n\t}\n};\n"
  },
  {
    "path": "frontend/utility/BaseLexer.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <util/lexer.h>\n\nstruct BaseLexer {\n\tlexer lex;\n\npublic:\n\tinline BaseLexer() { lexer_init(&lex); }\n\tinline ~BaseLexer() { lexer_free(&lex); }\n\toperator lexer *() { return &lex; }\n};\n"
  },
  {
    "path": "frontend/utility/BasicOutputHandler.cpp",
    "content": "#include \"BasicOutputHandler.hpp\"\n#include \"AdvancedOutput.hpp\"\n#include \"SimpleOutput.hpp\"\n\n#include <utility/MultitrackVideoError.hpp>\n#include <utility/StartMultiTrackVideoStreamingGuard.hpp>\n#include <utility/VCamConfig.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QThreadPool>\n\nusing namespace std;\n\nextern bool EncoderAvailable(const char *encoder);\n\nvolatile bool streaming_active = false;\nvolatile bool recording_active = false;\nvolatile bool recording_paused = false;\nvolatile bool replaybuf_active = false;\nvolatile bool virtualcam_active = false;\n\nvoid OBSStreamStarting(void *data, calldata_t *params)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\tobs_output_t *obj = (obs_output_t *)calldata_ptr(params, \"output\");\n\n\tint sec = (int)obs_output_get_active_delay(obj);\n\tif (sec == 0)\n\t\treturn;\n\n\toutput->delayActive = true;\n\tQMetaObject::invokeMethod(output->main, \"StreamDelayStarting\", Q_ARG(int, sec));\n}\n\nvoid OBSStreamStopping(void *data, calldata_t *params)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\tobs_output_t *obj = (obs_output_t *)calldata_ptr(params, \"output\");\n\n\tint sec = (int)obs_output_get_active_delay(obj);\n\tif (sec == 0)\n\t\tQMetaObject::invokeMethod(output->main, \"StreamStopping\");\n\telse\n\t\tQMetaObject::invokeMethod(output->main, \"StreamDelayStopping\", Q_ARG(int, sec));\n}\n\nvoid OBSStartStreaming(void *data, calldata_t * /* params */)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\toutput->streamingActive = true;\n\tos_atomic_set_bool(&streaming_active, true);\n\tQMetaObject::invokeMethod(output->main, \"StreamingStart\");\n}\n\nvoid OBSStopStreaming(void *data, calldata_t *params)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\tint code = (int)calldata_int(params, \"code\");\n\tconst char *last_error = calldata_string(params, \"last_error\");\n\n\tQString arg_last_error = QString::fromUtf8(last_error);\n\n\toutput->streamingActive = false;\n\toutput->delayActive = false;\n\toutput->multitrackVideoActive = false;\n\tos_atomic_set_bool(&streaming_active, false);\n\tQMetaObject::invokeMethod(output->main, \"StreamingStop\", Q_ARG(int, code), Q_ARG(QString, arg_last_error));\n}\n\nvoid OBSStartRecording(void *data, calldata_t * /* params */)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\n\toutput->recordingActive = true;\n\tos_atomic_set_bool(&recording_active, true);\n\tQMetaObject::invokeMethod(output->main, \"RecordingStart\");\n}\n\nvoid OBSStopRecording(void *data, calldata_t *params)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\tint code = (int)calldata_int(params, \"code\");\n\tconst char *last_error = calldata_string(params, \"last_error\");\n\n\tQString arg_last_error = QString::fromUtf8(last_error);\n\n\toutput->recordingActive = false;\n\tos_atomic_set_bool(&recording_active, false);\n\tos_atomic_set_bool(&recording_paused, false);\n\tQMetaObject::invokeMethod(output->main, \"RecordingStop\", Q_ARG(int, code), Q_ARG(QString, arg_last_error));\n}\n\nvoid OBSRecordStopping(void *data, calldata_t * /* params */)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\tQMetaObject::invokeMethod(output->main, \"RecordStopping\");\n}\n\nvoid OBSRecordFileChanged(void *data, calldata_t *params)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\tconst char *next_file = calldata_string(params, \"next_file\");\n\n\tQString arg_last_file = QString::fromUtf8(output->lastRecordingPath.c_str());\n\n\tQMetaObject::invokeMethod(output->main, \"RecordingFileChanged\", Q_ARG(QString, arg_last_file));\n\n\toutput->lastRecordingPath = next_file;\n}\n\nvoid OBSStartReplayBuffer(void *data, calldata_t * /* params */)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\n\toutput->replayBufferActive = true;\n\tos_atomic_set_bool(&replaybuf_active, true);\n\tQMetaObject::invokeMethod(output->main, \"ReplayBufferStart\");\n}\n\nvoid OBSStopReplayBuffer(void *data, calldata_t *params)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\tint code = (int)calldata_int(params, \"code\");\n\n\toutput->replayBufferActive = false;\n\tos_atomic_set_bool(&replaybuf_active, false);\n\tQMetaObject::invokeMethod(output->main, \"ReplayBufferStop\", Q_ARG(int, code));\n}\n\nvoid OBSReplayBufferStopping(void *data, calldata_t * /* params */)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\tQMetaObject::invokeMethod(output->main, \"ReplayBufferStopping\");\n}\n\nvoid OBSReplayBufferSaved(void *data, calldata_t * /* params */)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\tQMetaObject::invokeMethod(output->main, \"ReplayBufferSaved\", Qt::QueuedConnection);\n}\n\nstatic void OBSStartVirtualCam(void *data, calldata_t * /* params */)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\n\toutput->virtualCamActive = true;\n\tos_atomic_set_bool(&virtualcam_active, true);\n\tQMetaObject::invokeMethod(output->main, \"OnVirtualCamStart\");\n}\n\nstatic void OBSStopVirtualCam(void *data, calldata_t *params)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\tint code = (int)calldata_int(params, \"code\");\n\n\toutput->virtualCamActive = false;\n\tos_atomic_set_bool(&virtualcam_active, false);\n\tQMetaObject::invokeMethod(output->main, \"OnVirtualCamStop\", Q_ARG(int, code));\n}\n\nstatic void OBSDeactivateVirtualCam(void *data, calldata_t * /* params */)\n{\n\tBasicOutputHandler *output = static_cast<BasicOutputHandler *>(data);\n\toutput->DestroyVirtualCamView();\n}\n\nbool return_first_id(void *data, const char *id)\n{\n\tconst char **output = (const char **)data;\n\n\t*output = id;\n\treturn false;\n}\n\nconst char *GetStreamOutputType(const obs_service_t *service)\n{\n\tconst char *protocol = obs_service_get_protocol(service);\n\tconst char *output = nullptr;\n\n\tif (!protocol) {\n\t\tblog(LOG_WARNING, \"The service '%s' has no protocol set\", obs_service_get_id(service));\n\t\treturn nullptr;\n\t}\n\n\tif (!obs_is_output_protocol_registered(protocol)) {\n\t\tblog(LOG_WARNING, \"The protocol '%s' is not registered\", protocol);\n\t\treturn nullptr;\n\t}\n\n\t/* Check if the service has a preferred output type */\n\toutput = obs_service_get_preferred_output_type(service);\n\tif (output) {\n\t\tif ((obs_get_output_flags(output) & OBS_OUTPUT_SERVICE) != 0)\n\t\t\treturn output;\n\n\t\tblog(LOG_WARNING, \"The output '%s' is not registered, fallback to another one\", output);\n\t}\n\n\t/* Otherwise, prefer first-party output types */\n\tif (can_use_output(protocol, \"rtmp_output\", \"RTMP\", \"RTMPS\")) {\n\t\treturn \"rtmp_output\";\n\t} else if (can_use_output(protocol, \"ffmpeg_hls_muxer\", \"HLS\")) {\n\t\treturn \"ffmpeg_hls_muxer\";\n\t} else if (can_use_output(protocol, \"ffmpeg_mpegts_muxer\", \"SRT\", \"RIST\")) {\n\t\treturn \"ffmpeg_mpegts_muxer\";\n\t}\n\n\t/* If third-party protocol, use the first enumerated type */\n\tobs_enum_output_types_with_protocol(protocol, &output, return_first_id);\n\tif (output)\n\t\treturn output;\n\n\tblog(LOG_WARNING, \"No output compatible with the service '%s' is registered\", obs_service_get_id(service));\n\n\treturn nullptr;\n}\n\nBasicOutputHandler::BasicOutputHandler(OBSBasic *main_) : main(main_)\n{\n\tif (main->vcamEnabled) {\n\t\tvirtualCam = obs_output_create(VIRTUAL_CAM_ID, \"virtualcam_output\", nullptr, nullptr);\n\n\t\tsignal_handler_t *signal = obs_output_get_signal_handler(virtualCam);\n\t\tstartVirtualCam.Connect(signal, \"start\", OBSStartVirtualCam, this);\n\t\tstopVirtualCam.Connect(signal, \"stop\", OBSStopVirtualCam, this);\n\t\tdeactivateVirtualCam.Connect(signal, \"deactivate\", OBSDeactivateVirtualCam, this);\n\t}\n\n\tauto service = main_->GetService();\n\tOBSDataAutoRelease settings = obs_service_get_settings(service);\n\tauto multitrack_enabled = config_get_bool(main->Config(), \"Stream1\", \"EnableMultitrackVideo\") &&\n\t\t\t\t  (obs_data_has_user_value(settings, \"multitrack_video_configuration_url\") ||\n\t\t\t\t   strcmp(obs_service_get_id(service), \"rtmp_custom\") == 0);\n\n\tif (multitrack_enabled)\n\t\tmultitrackVideo = make_unique<MultitrackVideoOutput>();\n\n\tif (config_get_int(main->Config(), \"Stream1\", \"WHIPSimulcastTotalLayers\") > 1)\n\t\twhipSimulcastEncoders = make_unique<WHIPSimulcastEncoders>();\n}\n\nextern void log_vcam_changed(const VCamConfig &config, bool starting);\n\nbool BasicOutputHandler::StartVirtualCam()\n{\n\tif (!main->vcamEnabled)\n\t\treturn false;\n\n\tbool typeIsProgram = main->vcamConfig.type == VCamOutputType::ProgramView;\n\n\tif (!virtualCamView && !typeIsProgram)\n\t\tvirtualCamView = obs_view_create();\n\n\tUpdateVirtualCamOutputSource();\n\n\tif (!virtualCamVideo) {\n\t\tvirtualCamVideo = typeIsProgram ? obs_get_video() : obs_view_add(virtualCamView);\n\n\t\tif (!virtualCamVideo)\n\t\t\treturn false;\n\t}\n\n\tobs_output_set_media(virtualCam, virtualCamVideo, obs_get_audio());\n\tif (!Active())\n\t\tSetupOutputs();\n\n\tbool success = obs_output_start(virtualCam);\n\tif (!success) {\n\t\tQString errorReason;\n\n\t\tconst char *error = obs_output_get_last_error(virtualCam);\n\t\tif (error) {\n\t\t\terrorReason = QT_UTF8(error);\n\t\t} else {\n\t\t\terrorReason = QTStr(\"Output.StartFailedGeneric\");\n\t\t}\n\n\t\tQMessageBox::critical(main, QTStr(\"Output.StartVirtualCamFailed\"), errorReason);\n\n\t\tDestroyVirtualCamView();\n\t}\n\n\tlog_vcam_changed(main->vcamConfig, true);\n\n\treturn success;\n}\n\nvoid BasicOutputHandler::StopVirtualCam()\n{\n\tif (main->vcamEnabled) {\n\t\tobs_output_stop(virtualCam);\n\t}\n}\n\nbool BasicOutputHandler::VirtualCamActive() const\n{\n\tif (main->vcamEnabled) {\n\t\treturn obs_output_active(virtualCam);\n\t}\n\treturn false;\n}\n\nvoid BasicOutputHandler::UpdateVirtualCamOutputSource()\n{\n\tif (!main->vcamEnabled || !virtualCamView)\n\t\treturn;\n\n\tOBSSourceAutoRelease source;\n\n\tswitch (main->vcamConfig.type) {\n\tcase VCamOutputType::Invalid:\n\tcase VCamOutputType::ProgramView:\n\t\tDestroyVirtualCameraScene();\n\t\treturn;\n\tcase VCamOutputType::PreviewOutput: {\n\t\tDestroyVirtualCameraScene();\n\t\tOBSSource s = main->GetCurrentSceneSource();\n\t\tobs_source_get_ref(s);\n\t\tsource = s.Get();\n\t\tbreak;\n\t}\n\tcase VCamOutputType::SceneOutput:\n\t\tDestroyVirtualCameraScene();\n\t\tsource = obs_get_source_by_name(main->vcamConfig.scene.c_str());\n\t\tbreak;\n\tcase VCamOutputType::SourceOutput:\n\t\tOBSSourceAutoRelease s = obs_get_source_by_name(main->vcamConfig.source.c_str());\n\n\t\tif (!vCamSourceScene)\n\t\t\tvCamSourceScene = obs_scene_create_private(\"vcam_source\");\n\t\tsource = obs_source_get_ref(obs_scene_get_source(vCamSourceScene));\n\n\t\tif (vCamSourceSceneItem && (obs_sceneitem_get_source(vCamSourceSceneItem) != s)) {\n\t\t\tobs_sceneitem_remove(vCamSourceSceneItem);\n\t\t\tvCamSourceSceneItem = nullptr;\n\t\t}\n\n\t\tif (!vCamSourceSceneItem) {\n\t\t\tvCamSourceSceneItem = obs_scene_add(vCamSourceScene, s);\n\n\t\t\tobs_sceneitem_set_bounds_type(vCamSourceSceneItem, OBS_BOUNDS_SCALE_INNER);\n\t\t\tobs_sceneitem_set_bounds_alignment(vCamSourceSceneItem, OBS_ALIGN_CENTER);\n\n\t\t\tconst struct vec2 size = {\n\t\t\t\t(float)obs_source_get_width(source),\n\t\t\t\t(float)obs_source_get_height(source),\n\t\t\t};\n\t\t\tobs_sceneitem_set_bounds(vCamSourceSceneItem, &size);\n\t\t}\n\t\tbreak;\n\t}\n\n\tOBSSourceAutoRelease current = obs_view_get_source(virtualCamView, 0);\n\tif (source != current)\n\t\tobs_view_set_source(virtualCamView, 0, source);\n}\n\nvoid BasicOutputHandler::DestroyVirtualCamView()\n{\n\tif (main->vcamConfig.type == VCamOutputType::ProgramView) {\n\t\tvirtualCamVideo = nullptr;\n\t\treturn;\n\t}\n\n\tobs_view_remove(virtualCamView);\n\tobs_view_set_source(virtualCamView, 0, nullptr);\n\tvirtualCamVideo = nullptr;\n\n\tobs_view_destroy(virtualCamView);\n\tvirtualCamView = nullptr;\n\n\tDestroyVirtualCameraScene();\n}\n\nvoid BasicOutputHandler::DestroyVirtualCameraScene()\n{\n\tif (!vCamSourceScene)\n\t\treturn;\n\n\tobs_scene_release(vCamSourceScene);\n\tvCamSourceScene = nullptr;\n\tvCamSourceSceneItem = nullptr;\n}\n\nconst char *FindAudioEncoderFromCodec(const char *type)\n{\n\tconst char *alt_enc_id = nullptr;\n\tsize_t i = 0;\n\n\twhile (obs_enum_encoder_types(i++, &alt_enc_id)) {\n\t\tconst char *codec = obs_get_encoder_codec(alt_enc_id);\n\t\tif (strcmp(type, codec) == 0) {\n\t\t\treturn alt_enc_id;\n\t\t}\n\t}\n\n\treturn nullptr;\n}\n\nvoid clear_archive_encoder(obs_output_t *output, const char *expected_name)\n{\n\tobs_encoder_t *last = obs_output_get_audio_encoder(output, 1);\n\tbool clear = false;\n\n\t/* ensures that we don't remove twitch's soundtrack encoder */\n\tif (last) {\n\t\tconst char *name = obs_encoder_get_name(last);\n\t\tclear = name && strcmp(name, expected_name) == 0;\n\t\tobs_encoder_release(last);\n\t}\n\n\tif (clear)\n\t\tobs_output_set_audio_encoder(output, nullptr, 1);\n}\n\nvoid BasicOutputHandler::SetupAutoRemux(const char *&container)\n{\n\tbool autoRemux = config_get_bool(main->Config(), \"Video\", \"AutoRemux\");\n\tif (autoRemux && strcmp(container, \"mp4\") == 0)\n\t\tcontainer = \"mkv\";\n}\n\nstd::string BasicOutputHandler::GetRecordingFilename(const char *path, const char *container, bool noSpace,\n\t\t\t\t\t\t     bool overwrite, const char *format, bool ffmpeg)\n{\n\tif (!ffmpeg)\n\t\tSetupAutoRemux(container);\n\n\tstring dst = GetOutputFilename(path, container, noSpace, overwrite, format);\n\tlastRecordingPath = dst;\n\treturn dst;\n}\n\nextern std::string DeserializeConfigText(const char *text);\n\nstd::shared_future<void> BasicOutputHandler::SetupMultitrackVideo(obs_service_t *service, std::string audio_encoder_id,\n\t\t\t\t\t\t\t\t  size_t main_audio_mixer,\n\t\t\t\t\t\t\t\t  std::optional<size_t> vod_track_mixer,\n\t\t\t\t\t\t\t\t  std::function<void(std::optional<bool>)> continuation)\n{\n\tauto start_streaming_guard = std::make_shared<StartMultitrackVideoStreamingGuard>();\n\tif (!multitrackVideo) {\n\t\tcontinuation(std::nullopt);\n\t\treturn start_streaming_guard->GetFuture();\n\t}\n\n\tmultitrackVideoActive = false;\n\n\tstreamDelayStarting.Disconnect();\n\tstreamStopping.Disconnect();\n\tstartStreaming.Disconnect();\n\tstopStreaming.Disconnect();\n\n\tbool is_custom = strncmp(\"rtmp_custom\", obs_service_get_type(service), 11) == 0;\n\n\tstd::optional<std::string> custom_config = std::nullopt;\n\tif (config_get_bool(main->Config(), \"Stream1\", \"MultitrackVideoConfigOverrideEnabled\"))\n\t\tcustom_config = DeserializeConfigText(\n\t\t\tconfig_get_string(main->Config(), \"Stream1\", \"MultitrackVideoConfigOverride\"));\n\n\tstd::optional<QString> extraCanvasUUID;\n\tconst char *uuid = config_get_string(main->Config(), \"Stream1\", \"MultitrackExtraCanvas\");\n\tif (uuid && *uuid) {\n\t\textraCanvasUUID = uuid;\n\t}\n\n\tOBSDataAutoRelease settings = obs_service_get_settings(service);\n\tQString key = obs_data_get_string(settings, \"key\");\n\n\tconst char *service_name = \"<unknown>\";\n\tif (is_custom && obs_data_has_user_value(settings, \"service_name\")) {\n\t\tservice_name = obs_data_get_string(settings, \"service_name\");\n\t} else if (!is_custom) {\n\t\tservice_name = obs_data_get_string(settings, \"service\");\n\t}\n\n\tstd::optional<std::string> custom_rtmp_url;\n\tstd::optional<bool> use_rtmps;\n\tauto server = obs_data_get_string(settings, \"server\");\n\tif (strncmp(server, \"auto\", 4) != 0) {\n\t\tcustom_rtmp_url = server;\n\t} else {\n\t\tQString server_ = server;\n\t\tuse_rtmps = server_.contains(\"rtmps\", Qt::CaseInsensitive);\n\t}\n\n\tauto service_custom_server = obs_data_get_bool(settings, \"using_custom_server\");\n\tif (custom_rtmp_url.has_value()) {\n\t\tblog(LOG_INFO, \"Using %sserver '%s'\", service_custom_server ? \"custom \" : \"\", custom_rtmp_url->c_str());\n\t}\n\n\tauto maximum_aggregate_bitrate =\n\t\tconfig_get_bool(main->Config(), \"Stream1\", \"MultitrackVideoMaximumAggregateBitrateAuto\")\n\t\t\t? std::nullopt\n\t\t\t: std::make_optional<uint32_t>(\n\t\t\t\t  config_get_int(main->Config(), \"Stream1\", \"MultitrackVideoMaximumAggregateBitrate\"));\n\n\tauto maximum_video_tracks = config_get_bool(main->Config(), \"Stream1\", \"MultitrackVideoMaximumVideoTracksAuto\")\n\t\t\t\t\t    ? std::nullopt\n\t\t\t\t\t    : std::make_optional<uint32_t>(config_get_int(\n\t\t\t\t\t\t      main->Config(), \"Stream1\", \"MultitrackVideoMaximumVideoTracks\"));\n\n\tauto stream_dump_config = GenerateMultitrackVideoStreamDumpConfig();\n\n\tauto continue_on_main_thread = [&, start_streaming_guard, service = OBSService{service},\n\t\t\t\t\tcontinuation =\n\t\t\t\t\t\tstd::move(continuation)](std::optional<MultitrackVideoError> error) {\n\t\tif (error) {\n\t\t\tOBSDataAutoRelease service_settings = obs_service_get_settings(service);\n\t\t\tauto multitrack_video_name = QTStr(\"Basic.Settings.Stream.MultitrackVideoLabel\");\n\t\t\tif (obs_data_has_user_value(service_settings, \"multitrack_video_name\")) {\n\t\t\t\tmultitrack_video_name = obs_data_get_string(service_settings, \"multitrack_video_name\");\n\t\t\t}\n\n\t\t\tmultitrackVideoActive = false;\n\t\t\tif (!error->ShowDialog(main, multitrack_video_name))\n\t\t\t\treturn continuation(false);\n\t\t\treturn continuation(std::nullopt);\n\t\t}\n\n\t\tmultitrackVideoActive = true;\n\n\t\tauto signal_handler = multitrackVideo->StreamingSignalHandler();\n\n\t\tstreamDelayStarting.Connect(signal_handler, \"starting\", OBSStreamStarting, this);\n\t\tstreamStopping.Connect(signal_handler, \"stopping\", OBSStreamStopping, this);\n\n\t\tstartStreaming.Connect(signal_handler, \"start\", OBSStartStreaming, this);\n\t\tstopStreaming.Connect(signal_handler, \"stop\", OBSStopStreaming, this);\n\t\treturn continuation(true);\n\t};\n\n\tQThreadPool::globalInstance()->start([=, main = main, multitrackVideo = multitrackVideo.get(),\n\t\t\t\t\t      service_name = std::string{service_name}, service = OBSService{service},\n\t\t\t\t\t      stream_dump_config = OBSData{stream_dump_config},\n\t\t\t\t\t      start_streaming_guard = start_streaming_guard]() mutable {\n\t\tstd::optional<MultitrackVideoError> error;\n\t\ttry {\n\t\t\tmultitrackVideo->PrepareStreaming(main, service_name.c_str(), service, custom_rtmp_url, key,\n\t\t\t\t\t\t\t  audio_encoder_id.c_str(), maximum_aggregate_bitrate,\n\t\t\t\t\t\t\t  maximum_video_tracks, custom_config, stream_dump_config,\n\t\t\t\t\t\t\t  main_audio_mixer, vod_track_mixer, use_rtmps,\n\t\t\t\t\t\t\t  extraCanvasUUID);\n\t\t} catch (const MultitrackVideoError &error_) {\n\t\t\terror.emplace(error_);\n\t\t}\n\n\t\tQMetaObject::invokeMethod(main, [=] { continue_on_main_thread(error); });\n\t});\n\n\treturn start_streaming_guard->GetFuture();\n}\n\nOBSDataAutoRelease BasicOutputHandler::GenerateMultitrackVideoStreamDumpConfig()\n{\n\tauto stream_dump_enabled = config_get_bool(main->Config(), \"Stream1\", \"MultitrackVideoStreamDumpEnabled\");\n\n\tif (!stream_dump_enabled)\n\t\treturn nullptr;\n\n\tconst char *path = config_get_string(main->Config(), \"SimpleOutput\", \"FilePath\");\n\tbool noSpace = config_get_bool(main->Config(), \"SimpleOutput\", \"FileNameWithoutSpace\");\n\tconst char *filenameFormat = config_get_string(main->Config(), \"Output\", \"FilenameFormatting\");\n\tbool overwriteIfExists = config_get_bool(main->Config(), \"Output\", \"OverwriteIfExists\");\n\tbool useMP4 = config_get_bool(main->Config(), \"Stream1\", \"MultitrackVideoStreamDumpAsMP4\");\n\n\tstring f;\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tf = GetFormatString(filenameFormat, nullptr, nullptr);\n\tstring strPath = GetRecordingFilename(path, useMP4 ? \"mp4\" : \"flv\", noSpace, overwriteIfExists, f.c_str(),\n\t\t\t\t\t      // never remux stream dump\n\t\t\t\t\t      false);\n\tobs_data_set_string(settings, \"path\", strPath.c_str());\n\n\tif (useMP4) {\n\t\tobs_data_set_bool(settings, \"use_mp4\", true);\n\t\tobs_data_set_string(settings, \"muxer_settings\", \"write_encoder_info=1\");\n\t}\n\n\treturn settings;\n}\n\nBasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main)\n{\n\treturn new SimpleOutput(main);\n}\n\nBasicOutputHandler *CreateAdvancedOutputHandler(OBSBasic *main)\n{\n\treturn new AdvancedOutput(main);\n}\n"
  },
  {
    "path": "frontend/utility/BasicOutputHandler.hpp",
    "content": "#pragma once\n\n#include <utility/MultitrackVideoOutput.hpp>\n#include <utility/WHIPSimulcastEncoders.hpp>\n\n#include <obs.hpp>\n#include <util/dstr.hpp>\n\n#include <future>\n\n#define RTMP_PROTOCOL \"rtmp\"\n#define SRT_PROTOCOL \"srt\"\n#define RIST_PROTOCOL \"rist\"\n\nclass OBSBasic;\n\nusing SetupStreamingContinuation_t = std::function<void(bool)>;\n\nstruct BasicOutputHandler {\n\tOBSOutputAutoRelease fileOutput;\n\tOBSOutputAutoRelease streamOutput;\n\tOBSOutputAutoRelease replayBuffer;\n\tOBSOutputAutoRelease virtualCam;\n\tbool streamingActive = false;\n\tbool recordingActive = false;\n\tbool delayActive = false;\n\tbool replayBufferActive = false;\n\tbool virtualCamActive = false;\n\tOBSBasic *main;\n\n\tstd::unique_ptr<MultitrackVideoOutput> multitrackVideo;\n\tbool multitrackVideoActive = false;\n\n\tOBSOutputAutoRelease StreamingOutput() const\n\t{\n\t\treturn (multitrackVideo && multitrackVideoActive)\n\t\t\t       ? multitrackVideo->StreamingOutput()\n\t\t\t       : OBSOutputAutoRelease{obs_output_get_ref(streamOutput)};\n\t}\n\n\tobs_view_t *virtualCamView = nullptr;\n\tvideo_t *virtualCamVideo = nullptr;\n\tobs_scene_t *vCamSourceScene = nullptr;\n\tobs_sceneitem_t *vCamSourceSceneItem = nullptr;\n\n\tstd::unique_ptr<WHIPSimulcastEncoders> whipSimulcastEncoders;\n\n\tstd::string outputType;\n\tstd::string lastError;\n\n\tstd::string lastRecordingPath;\n\n\tOBSSignal startRecording;\n\tOBSSignal stopRecording;\n\tOBSSignal startReplayBuffer;\n\tOBSSignal stopReplayBuffer;\n\tOBSSignal startStreaming;\n\tOBSSignal stopStreaming;\n\tOBSSignal startVirtualCam;\n\tOBSSignal stopVirtualCam;\n\tOBSSignal deactivateVirtualCam;\n\tOBSSignal streamDelayStarting;\n\tOBSSignal streamStopping;\n\tOBSSignal recordStopping;\n\tOBSSignal recordFileChanged;\n\tOBSSignal replayBufferStopping;\n\tOBSSignal replayBufferSaved;\n\n\tBasicOutputHandler(OBSBasic *main_);\n\n\tvirtual ~BasicOutputHandler() {};\n\n\tvirtual std::shared_future<void> SetupStreaming(obs_service_t *service,\n\t\t\t\t\t\t\tSetupStreamingContinuation_t continuation) = 0;\n\tvirtual bool StartStreaming(obs_service_t *service) = 0;\n\tvirtual bool StartRecording() = 0;\n\tvirtual bool StartReplayBuffer() { return false; }\n\tvirtual bool StartVirtualCam();\n\tvirtual void StopStreaming(bool force = false) = 0;\n\tvirtual void StopRecording(bool force = false) = 0;\n\tvirtual void StopReplayBuffer(bool force = false) { (void)force; }\n\tvirtual void StopVirtualCam();\n\tvirtual bool StreamingActive() const = 0;\n\tvirtual bool RecordingActive() const = 0;\n\tvirtual bool ReplayBufferActive() const { return false; }\n\tvirtual bool VirtualCamActive() const;\n\n\tvirtual void Update() = 0;\n\tvirtual void SetupOutputs() = 0;\n\n\tvirtual void UpdateVirtualCamOutputSource();\n\tvirtual void DestroyVirtualCamView();\n\tvirtual void DestroyVirtualCameraScene();\n\n\tinline bool Active() const\n\t{\n\t\treturn streamingActive || recordingActive || delayActive || replayBufferActive || virtualCamActive ||\n\t\t       multitrackVideoActive;\n\t}\n\nprotected:\n\tvoid SetupAutoRemux(const char *&container);\n\tstd::string GetRecordingFilename(const char *path, const char *container, bool noSpace, bool overwrite,\n\t\t\t\t\t const char *format, bool ffmpeg);\n\n\tstd::shared_future<void> SetupMultitrackVideo(obs_service_t *service, std::string audio_encoder_id,\n\t\t\t\t\t\t      size_t main_audio_mixer, std::optional<size_t> vod_track_mixer,\n\t\t\t\t\t\t      std::function<void(std::optional<bool>)> continuation);\n\tOBSDataAutoRelease GenerateMultitrackVideoStreamDumpConfig();\n};\n\nBasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main);\nBasicOutputHandler *CreateAdvancedOutputHandler(OBSBasic *main);\n\nvoid OBSStreamStarting(void *data, calldata_t *params);\nvoid OBSStreamStopping(void *data, calldata_t *params);\nvoid OBSStartStreaming(void *data, calldata_t *params);\nvoid OBSStopStreaming(void *data, calldata_t *params);\nvoid OBSStartRecording(void *data, calldata_t *params);\nvoid OBSStopRecording(void *data, calldata_t *params);\nvoid OBSRecordStopping(void *data, calldata_t *params);\nvoid OBSRecordFileChanged(void *data, calldata_t *params);\nvoid OBSStartReplayBuffer(void *data, calldata_t *params);\nvoid OBSStopReplayBuffer(void *data, calldata_t *params);\nvoid OBSReplayBufferStopping(void *data, calldata_t *params);\nvoid OBSReplayBufferSaved(void *data, calldata_t *params);\n\ninline bool can_use_output(const char *prot, const char *output, const char *prot_test1,\n\t\t\t   const char *prot_test2 = nullptr)\n{\n\treturn (strcmp(prot, prot_test1) == 0 || (prot_test2 && strcmp(prot, prot_test2) == 0)) &&\n\t       (obs_get_output_flags(output) & OBS_OUTPUT_SERVICE) != 0;\n}\n\nconst char *GetStreamOutputType(const obs_service_t *service);\n\ninline bool ServiceSupportsVodTrack(const char *service)\n{\n\tstatic const char *vodTrackServices[] = {\"Twitch\"};\n\n\tfor (const char *vodTrackService : vodTrackServices) {\n\t\tif (astrcmpi(vodTrackService, service) == 0)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid clear_archive_encoder(obs_output_t *output, const char *expected_name);\n"
  },
  {
    "path": "frontend/utility/CrashHandler.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Patrick Heyer <PatTheMav@users.noreply.github.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"CrashHandler.hpp\"\n#include <OBSApp.hpp>\n#include <qt-wrappers.hpp>\n\n#include <nlohmann/json.hpp>\n\n#include <fstream>\n#include <iostream>\n#include <sstream>\n#include <string_view>\n\n#include \"moc_CrashHandler.cpp\"\n\nusing json = nlohmann::json;\nusing CrashLogUpdateResult = OBS::CrashHandler::CrashLogUpdateResult;\n\nnamespace {\n\nconstexpr std::string_view crashSentinelPath = \"obs-studio/.sentinel\";\nconstexpr std::string_view crashSentinelPrefix = \"run_\";\nconstexpr std::string_view crashUploadURL = \"https://obsproject.com/logs/upload\";\n\n#ifndef NDEBUG\nconstexpr bool isSentinelEnabled = false;\n#else\nconstexpr bool isSentinelEnabled = true;\n#endif\n\nstd::string getCrashLogFileContent(std::filesystem::path crashLogFile)\n{\n\tstd::string crashLogFileContent;\n\n\tif (!std::filesystem::exists(crashLogFile)) {\n\t\treturn crashLogFileContent;\n\t}\n\n\tstd::fstream filestream;\n\tstd::streampos crashLogFileSize = 0;\n\tfilestream.open(crashLogFile, std::ios::in | std::ios::binary);\n\n\tif (filestream.is_open()) {\n\t\tcrashLogFileSize = filestream.tellg();\n\t\tfilestream.seekg(0, std::ios::end);\n\t\tcrashLogFileSize = filestream.tellg() - crashLogFileSize;\n\t\tfilestream.seekg(0);\n\n\t\tcrashLogFileContent.resize(crashLogFileSize);\n\t\tcrashLogFileContent.assign((std::istreambuf_iterator<char>(filestream)),\n\t\t\t\t\t   (std::istreambuf_iterator<char>()));\n\t}\n\n\treturn crashLogFileContent;\n}\n\nstd::pair<OBS::TimePoint, std::string> buildCrashLogUploadContent(OBS::PlatformType platformType,\n\t\t\t\t\t\t\t\t  std::string crashLogFileContent)\n{\n\tOBS::TimePoint uploadTimePoint = OBS::Clock::now();\n\tstd::time_t uploadTimePoint_c = OBS::Clock::to_time_t(uploadTimePoint);\n\tstd::tm uploadTimeLocal = *std::localtime(&uploadTimePoint_c);\n\n\tstd::stringstream uploadLogMessage;\n\n\tswitch (platformType) {\n\tcase OBS::PlatformType::Windows:\n\t\tuploadLogMessage << \"OBS \" << App()->GetVersionString(false) << \" crash file uploaded at \"\n\t\t\t\t << std::put_time(&uploadTimeLocal, \"%Y-%m-%d, %X\") << \"\\n\\n\"\n\t\t\t\t << crashLogFileContent;\n\t\tbreak;\n\tcase OBS::PlatformType::macOS:\n\t\tuploadLogMessage << crashLogFileContent;\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn std::pair<OBS::TimePoint, std::string>(uploadTimePoint, uploadLogMessage.str());\n}\n} // namespace\n\nnamespace OBS {\n\nstatic_assert(!crashSentinelPath.empty(), \"Crash sentinel path name cannot be empty\");\nstatic_assert(!crashSentinelPrefix.empty(), \"Crash sentinel filename prefix cannot be empty\");\nstatic_assert(!crashUploadURL.empty(), \"Crash sentinel upload URL cannot be empty\");\n\nCrashHandler::CrashHandler(QUuid appLaunchUUID) : appLaunchUUID_(appLaunchUUID)\n{\n\tif (!isSentinelEnabled) {\n\t\treturn;\n\t}\n\n\tsetupSentinel();\n\tcheckCrashState();\n}\n\nCrashHandler::~CrashHandler()\n{\n\tif (isActiveCrashHandler_) {\n\t\tapplicationShutdownHandler();\n\t}\n}\n\nCrashHandler::CrashHandler(CrashHandler &&other) noexcept\n{\n\tcrashLogUploadThread_ = std::exchange(other.crashLogUploadThread_, nullptr);\n\tisActiveCrashHandler_ = true;\n\tother.isActiveCrashHandler_ = false;\n}\n\nCrashHandler &CrashHandler::operator=(CrashHandler &&other) noexcept\n{\n\tstd::swap(crashLogUploadThread_, other.crashLogUploadThread_);\n\tisActiveCrashHandler_ = true;\n\tother.isActiveCrashHandler_ = false;\n\treturn *this;\n}\n\nbool CrashHandler::hasUncleanShutdown()\n{\n\treturn isSentinelEnabled && isSentinelPresent_;\n}\n\nbool CrashHandler::hasNewCrashLog()\n{\n\tCrashLogUpdateResult result = updateLocalCrashLogState();\n\n\tif (result == CrashLogUpdateResult::NotAvailable) {\n\t\treturn false;\n\t}\n\n\tbool hasNewCrashLog = (result == CrashLogUpdateResult::Updated);\n\tbool hasNoLogUrl = lastCrashLogURL_.empty();\n\n\treturn (hasNewCrashLog || hasNoLogUrl);\n}\n\nCrashLogUpdateResult CrashHandler::updateLocalCrashLogState()\n{\n\tupdateCrashLogFromConfig();\n\n\tstd::filesystem::path lastLocalCrashLogFile = findLastCrashLog();\n\n\tif (lastLocalCrashLogFile.empty() && lastCrashLogFile_.empty()) {\n\t\treturn CrashLogUpdateResult::NotAvailable;\n\t}\n\n\tif (lastLocalCrashLogFile != lastCrashLogFile_) {\n\t\tlastCrashLogFile_ = std::move(lastLocalCrashLogFile);\n\t\tlastCrashLogFileName_ = lastCrashLogFile_.filename().u8string();\n\t\tlastCrashLogURL_.clear();\n\n\t\treturn CrashLogUpdateResult::Updated;\n\t} else {\n\t\treturn CrashLogUpdateResult::NotUpdated;\n\t}\n}\n\nvoid CrashHandler::uploadLastCrashLog()\n{\n\tbool newCrashDetected = hasNewCrashLog();\n\n\tif (newCrashDetected) {\n\t\tuploadCrashLogToServer();\n\t} else {\n\t\thandleExistingCrashLogUpload();\n\t}\n}\n\nvoid CrashHandler::checkCrashState()\n{\n\tbool currentSentinelFound = false;\n\n\tstd::filesystem::path crashSentinelPath = crashSentinelFile_.parent_path();\n\n\tif (!std::filesystem::exists(crashSentinelPath)) {\n\t\ttry {\n\t\t\tstd::filesystem::create_directory(crashSentinelPath);\n\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\tblog(LOG_ERROR,\n\t\t\t     \"Crash sentinel location '%s' does not exist and unable to create directory:\\n%s.\",\n\t\t\t     crashSentinelPath.u8string().c_str(), error.what());\n\t\t\treturn;\n\t\t}\n\t}\n\n\tfor (const auto &entry : std::filesystem::directory_iterator(crashSentinelPath)) {\n\t\tif (entry.is_directory()) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tstd::string entryFileName = entry.path().filename().u8string();\n\n\t\tif (entryFileName.rfind(crashSentinelPrefix.data(), 0) != 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.path().filename() == crashSentinelFile_.filename()) {\n\t\t\tcurrentSentinelFound = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tisSentinelPresent_ = true;\n\t}\n\n\tif (!currentSentinelFound) {\n\t\tstd::fstream filestream;\n\t\tfilestream.open(crashSentinelFile_, std::ios::out);\n\t\tfilestream.close();\n\t}\n}\n\nvoid CrashHandler::setupSentinel()\n{\n\tBPtr crashSentinelPathString = GetAppConfigPathPtr(crashSentinelPath.data());\n\tstd::string appLaunchUUIDString = appLaunchUUID_.toString(QUuid::WithoutBraces).toStdString();\n\n\tstd::string crashSentinelFilePath = crashSentinelPathString.Get();\n\tcrashSentinelFilePath.reserve(crashSentinelFilePath.size() + crashSentinelPrefix.size() +\n\t\t\t\t      appLaunchUUIDString.size() + 1);\n\tcrashSentinelFilePath.append(\"/\").append(crashSentinelPrefix).append(appLaunchUUIDString);\n\n\tcrashSentinelFile_ = std::filesystem::u8path(crashSentinelFilePath);\n\n\tisActiveCrashHandler_ = true;\n}\n\nvoid CrashHandler::updateCrashLogFromConfig()\n{\n\tconfig_t *appConfig = App()->GetAppConfig();\n\n\tif (!appConfig) {\n\t\treturn;\n\t}\n\n\tconst char *last_crash_log_file = config_get_string(appConfig, \"CrashHandler\", \"last_crash_log_file\");\n\tconst char *last_crash_log_url = config_get_string(appConfig, \"CrashHandler\", \"last_crash_log_url\");\n\n\tstd::string lastCrashLogFilePath = last_crash_log_file ? last_crash_log_file : \"\";\n\tint64_t lastCrashLogUploadTimestamp = config_get_int(appConfig, \"CrashHandler\", \"last_crash_log_time\");\n\tstd::string lastCrashLogUploadURL = last_crash_log_url ? last_crash_log_url : \"\";\n\n\tOBS::Clock::duration durationSinceEpoch = std::chrono::seconds(lastCrashLogUploadTimestamp);\n\tOBS::TimePoint lastCrashLogUploadTime = OBS::TimePoint(durationSinceEpoch);\n\n\tlastCrashLogFile_ = std::filesystem::u8path(lastCrashLogFilePath);\n\tlastCrashLogFileName_ = lastCrashLogFile_.filename().u8string();\n\tlastCrashLogURL_ = lastCrashLogUploadURL;\n\tlastCrashUploadTime_ = lastCrashLogUploadTime;\n}\n\nvoid CrashHandler::saveCrashLogToConfig()\n{\n\tconfig_t *appConfig = App()->GetAppConfig();\n\n\tif (!appConfig) {\n\t\treturn;\n\t}\n\n\tstd::time_t uploadTimePoint_c = OBS::Clock::to_time_t(lastCrashUploadTime_);\n\n\tconfig_set_string(appConfig, \"CrashHandler\", \"last_crash_log_file\", lastCrashLogFile_.u8string().c_str());\n\tconfig_set_int(appConfig, \"CrashHandler\", \"last_crash_log_time\", uploadTimePoint_c);\n\tconfig_set_string(appConfig, \"CrashHandler\", \"last_crash_log_url\", lastCrashLogURL_.c_str());\n\tconfig_save_safe(appConfig, \"tmp\", nullptr);\n}\n\nvoid CrashHandler::uploadCrashLogToServer()\n{\n\tstd::string crashLogFileContent = getCrashLogFileContent(lastCrashLogFile_);\n\n\tif (crashLogFileContent.empty()) {\n\t\tblog(LOG_WARNING, \"Most recent crash log file was empty or unavailable for reading\");\n\t\treturn;\n\t}\n\n\temit crashLogUploadStarted();\n\n\tPlatformType platformType = getPlatformType();\n\n\tauto crashLogUploadContent = buildCrashLogUploadContent(platformType, crashLogFileContent);\n\n\tif (crashLogUploadThread_) {\n\t\tcrashLogUploadThread_->wait();\n\t}\n\n\tauto uploadThread =\n\t\tstd::make_unique<RemoteTextThread>(crashUploadURL.data(), \"text/plain\", crashLogUploadContent.second);\n\n\tstd::swap(crashLogUploadThread_, uploadThread);\n\n\tconnect(crashLogUploadThread_.get(), &RemoteTextThread::Result, this,\n\t\t&CrashHandler::crashLogUploadResultHandler);\n\n\tlastCrashUploadTime_ = crashLogUploadContent.first;\n\n\tcrashLogUploadThread_->start();\n}\n\nvoid CrashHandler::handleExistingCrashLogUpload()\n{\n\tif (!lastCrashLogURL_.empty()) {\n\t\tconst QString crashLogUrlString = QString::fromStdString(lastCrashLogURL_);\n\n\t\temit crashLogUploadFinished(crashLogUrlString);\n\t} else {\n\t\tuploadCrashLogToServer();\n\t}\n}\n\nvoid CrashHandler::crashLogUploadResultHandler(const QString &uploadResult, const QString &error)\n{\n\tif (uploadResult.isEmpty()) {\n\t\temit crashLogUploadFailed(error);\n\t\treturn;\n\t}\n\n\tjson uploadResultData = json::parse(uploadResult.toStdString());\n\n\ttry {\n\t\tstd::string crashLogUrl = uploadResultData[\"url\"];\n\n\t\tlastCrashLogURL_ = crashLogUrl;\n\n\t\tsaveCrashLogToConfig();\n\n\t\tconst QString crashLogUrlString = QString::fromStdString(crashLogUrl);\n\n\t\temit crashLogUploadFinished(crashLogUrlString);\n\t} catch (const json::exception &error) {\n\t\tblog(LOG_ERROR, \"JSON error while parsing crash upload response:\\n%s\", error.what());\n\n\t\tconst QString jsonErrorMessage = QTStr(\"CrashHandling.Errors.UploadJSONError\");\n\t\temit crashLogUploadFailed(jsonErrorMessage);\n\t}\n}\n\nvoid CrashHandler::applicationShutdownHandler() noexcept\n{\n\tif (!isSentinelEnabled) {\n\t\treturn;\n\t}\n\n\tif (crashSentinelFile_.empty()) {\n\t\tblog(LOG_ERROR, \"No crash sentinel location set for crash handler\");\n\t\treturn;\n\t}\n\n\tconst std::filesystem::path crashSentinelPath = crashSentinelFile_.parent_path();\n\n\tif (!std::filesystem::exists(crashSentinelPath)) {\n\t\tblog(LOG_ERROR, \"Crash sentinel location '%s' does not exist\", crashSentinelPath.u8string().c_str());\n\t\treturn;\n\t}\n\n\tfor (const auto &entry : std::filesystem::directory_iterator(crashSentinelPath)) {\n\t\tif (entry.is_directory()) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tstd::string entryFileName = entry.path().filename().u8string();\n\n\t\tif (entryFileName.rfind(crashSentinelPrefix.data(), 0) != 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tstd::filesystem::remove(entry.path());\n\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\tblog(LOG_ERROR, \"Failed to delete crash sentinel file:\\n%s\", error.what());\n\t\t}\n\t}\n\n\tisActiveCrashHandler_ = false;\n}\n} // namespace OBS\n"
  },
  {
    "path": "frontend/utility/CrashHandler.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Patrick Heyer <PatTheMav@users.noreply.github.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <utility/RemoteTextThread.hpp>\n\n#include <util/base.h>\n\n#include <QObject>\n#include <QThread>\n#include <QUuid>\n\n#include <chrono>\n#include <filesystem>\n\nnamespace OBS {\n\nenum class PlatformType { InvalidPlatform, Windows, macOS, Linux, FreeBSD };\n\nusing Clock = std::chrono::system_clock;\nusing TimePoint = std::chrono::time_point<Clock>;\n\nclass CrashHandler : public QObject {\n\tQ_OBJECT\n\nprivate:\n\tQUuid appLaunchUUID_;\n\tstd::filesystem::path crashSentinelFile_;\n\n\tTimePoint lastCrashUploadTime_;\n\tstd::filesystem::path lastCrashLogFile_;\n\tstd::string lastCrashLogFileName_;\n\tstd::string lastCrashLogURL_;\n\n\tbool isSentinelPresent_ = false;\n\tbool isActiveCrashHandler_ = false;\n\n\tstd::unique_ptr<RemoteTextThread> crashLogUploadThread_;\n\npublic:\n\tCrashHandler() = default;\n\tCrashHandler(QUuid appLaunchUUID);\n\tCrashHandler(const CrashHandler &other) = delete;\n\tCrashHandler &operator=(const CrashHandler &other) = delete;\n\tCrashHandler(CrashHandler &&other) noexcept;\n\tCrashHandler &operator=(CrashHandler &&other) noexcept;\n\n\t~CrashHandler();\n\n\tbool hasUncleanShutdown();\n\tbool hasNewCrashLog();\n\tstd::filesystem::path getCrashLogDirectory() const;\n\tvoid uploadLastCrashLog();\n\n\tenum class CrashLogUpdateResult { InvalidResult, NotAvailable, NotUpdated, Updated };\n\nprivate:\n\tvoid checkCrashState();\n\tvoid setupSentinel();\n\tstd::filesystem::path findLastCrashLog() const;\n\n\tCrashLogUpdateResult updateLocalCrashLogState();\n\tvoid updateCrashLogFromConfig();\n\tvoid saveCrashLogToConfig();\n\tvoid uploadCrashLogToServer();\n\tvoid handleExistingCrashLogUpload();\n\n\tPlatformType getPlatformType() const;\n\nprivate slots:\n\tvoid crashLogUploadResultHandler(const QString &uploadResult, const QString &error);\n\n\t// FIXME: Turn into private slot once OBSBasic does not handle application shutdown duties anymore.\npublic slots:\n\tvoid applicationShutdownHandler() noexcept;\n\nsignals:\n\tvoid crashLogUploadStarted() const;\n\tvoid crashLogUploadFailed(const QString &errorMessage) const;\n\tvoid crashLogUploadFinished(const QString &crashLogURL) const;\n};\n} // namespace OBS\n"
  },
  {
    "path": "frontend/utility/CrashHandler_FreeBSD.cpp",
    "content": "/******************************************************************************\n Copyright (C) 2025 by Patrick Heyer <PatTheMav@users.noreply.github.com>\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>.\n ******************************************************************************/\n\n#include \"CrashHandler.hpp\"\n\nnamespace OBS {\n\nPlatformType CrashHandler::getPlatformType() const\n{\n\treturn PlatformType::FreeBSD;\n}\n\nstd::filesystem::path CrashHandler::findLastCrashLog() const\n{\n\tstd::filesystem::path lastCrashLogFile;\n\n\treturn lastCrashLogFile;\n}\n\nstd::filesystem::path CrashHandler::getCrashLogDirectory() const\n{\n\tstd::filesystem::path crashLogDirectoryPath;\n\n\treturn crashLogDirectoryPath;\n}\n} // namespace OBS\n"
  },
  {
    "path": "frontend/utility/CrashHandler_Linux.cpp",
    "content": "/******************************************************************************\n Copyright (C) 2025 by Patrick Heyer <PatTheMav@users.noreply.github.com>\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>.\n ******************************************************************************/\n\n#include \"CrashHandler.hpp\"\n\nnamespace OBS {\n\nPlatformType CrashHandler::getPlatformType() const\n{\n\treturn PlatformType::Linux;\n}\n\nstd::filesystem::path CrashHandler::findLastCrashLog() const\n{\n\tstd::filesystem::path lastCrashLogFile;\n\n\treturn lastCrashLogFile;\n}\n\nstd::filesystem::path CrashHandler::getCrashLogDirectory() const\n{\n\tstd::filesystem::path crashLogDirectoryPath;\n\n\treturn crashLogDirectoryPath;\n}\n} // namespace OBS\n"
  },
  {
    "path": "frontend/utility/CrashHandler_MacOS.mm",
    "content": "/******************************************************************************\n Copyright (C) 2025 by Patrick Heyer <PatTheMav@users.noreply.github.com>\n \n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2 of the License, or\n (at your option) any later version.\n \n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n \n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>.\n ******************************************************************************/\n\n@import Foundation;\n\n#import \"CrashHandler.hpp\"\n\nnamespace {\n    NSURL *getDiagnosticReportsDirectory()\n    {\n        NSFileManager *fileManager = [NSFileManager defaultManager];\n\n        NSArray<NSURL *> *libraryURLs = [fileManager URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask];\n\n        if (libraryURLs.count == 0) {\n            blog(LOG_ERROR, \"Unable to get macOS user library directory URL\");\n            return nil;\n        }\n\n        NSURL *diagnosticsReportsURL = [libraryURLs[0] URLByAppendingPathComponent:@\"logs/DiagnosticReports\"];\n\n        return diagnosticsReportsURL;\n    }\n}  // namespace\n\nnamespace OBS {\n    PlatformType CrashHandler::getPlatformType() const\n    {\n        return PlatformType::macOS;\n    }\n\n    std::filesystem::path CrashHandler::findLastCrashLog() const\n    {\n        std::filesystem::path crashLogDirectoryPath;\n\n        NSURL *diagnosticsReportsURL = getDiagnosticReportsDirectory();\n\n        if (!diagnosticsReportsURL) {\n            return crashLogDirectoryPath;\n        }\n\n        BOOL (^enumerationErrorHandler)(NSURL *_Nonnull, NSError *_Nonnull) =\n            ^BOOL(NSURL *_Nonnull url __unused, NSError *_Nonnull error) {\n                blog(LOG_ERROR, \"Failed to enumerate diagnostics reports directory: %s\",\n                     error.localizedDescription.UTF8String);\n\n                return NO;\n            };\n\n        NSFileManager *fileManager = [NSFileManager defaultManager];\n\n        NSDirectoryEnumerator *dirEnumerator = [fileManager\n                       enumeratorAtURL:diagnosticsReportsURL\n            includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey]\n                               options:(NSDirectoryEnumerationSkipsHiddenFiles) errorHandler:enumerationErrorHandler];\n\n        NSMutableArray<NSURL *> *reportCandidates = [NSMutableArray array];\n\n        for (NSURL *entry in dirEnumerator) {\n            NSString *fileName = nil;\n            [entry getResourceValue:&fileName forKey:NSURLNameKey error:nil];\n\n            if ([fileName hasPrefix:@\"OBS\"] && [fileName hasSuffix:@\".ips\"]) {\n                [reportCandidates addObject:entry];\n            }\n        }\n\n        NSArray<NSURL *> *sortedCandidates = [reportCandidates\n            sortedArrayUsingComparator:^NSComparisonResult(NSURL *_Nonnull obj1, NSURL *_Nonnull obj2) {\n                NSDate *creationDateObj1 = nil;\n                NSDate *creationDateObj2 = nil;\n\n                [obj1 getResourceValue:&creationDateObj1 forKey:NSURLCreationDateKey error:nil];\n                [obj2 getResourceValue:&creationDateObj2 forKey:NSURLCreationDateKey error:nil];\n\n                NSComparisonResult result = [creationDateObj2 compare:creationDateObj1];\n\n                return result;\n            }];\n\n        if (sortedCandidates.count > 0) {\n            NSURL *lastDiagnosticsReport = sortedCandidates[0];\n            crashLogDirectoryPath = std::filesystem::u8path(lastDiagnosticsReport.path.UTF8String);\n        }\n\n        return crashLogDirectoryPath;\n    }\n\n    std::filesystem::path CrashHandler::getCrashLogDirectory() const\n    {\n        std::filesystem::path crashLogDirectoryPath;\n\n        NSURL *diagnosticsReportsURL = getDiagnosticReportsDirectory();\n\n        if (diagnosticsReportsURL) {\n            crashLogDirectoryPath = std::filesystem::u8path(diagnosticsReportsURL.path.UTF8String);\n        }\n\n        return crashLogDirectoryPath;\n    }\n}  // namespace OBS\n"
  },
  {
    "path": "frontend/utility/CrashHandler_Windows.cpp",
    "content": "/******************************************************************************\n Copyright (C) 2025 by Patrick Heyer <PatTheMav@users.noreply.github.com>\n \n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2 of the License, or\n (at your option) any later version.\n \n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n \n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>.\n ******************************************************************************/\n\n#include \"CrashHandler.hpp\"\n#include <OBSApp.hpp>\n\n#include <util/util.hpp>\n\n#include <vector>\n\nnamespace OBS {\n\nusing CrashFileEntry = std::pair<std::filesystem::path, std::filesystem::file_time_type>;\n\nPlatformType CrashHandler::getPlatformType() const\n{\n\treturn PlatformType::Windows;\n}\n\nstd::filesystem::path CrashHandler::findLastCrashLog() const\n{\n\tstd::filesystem::path lastCrashLogFile;\n\n\tstd::filesystem::path crashLogDirectory = getCrashLogDirectory();\n\n\tif (!std::filesystem::exists(crashLogDirectory)) {\n\t\tblog(LOG_ERROR, \"Crash log directory '%s' does not exist\", crashLogDirectory.u8string().c_str());\n\n\t\treturn lastCrashLogFile;\n\t}\n\n\tstd::vector<CrashFileEntry> crashLogFiles;\n\n\tfor (const auto &entry : std::filesystem::directory_iterator(crashLogDirectory)) {\n\t\tif (entry.is_directory()) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tstd::string entryFileName = entry.path().filename().u8string();\n\n\t\tif (entryFileName.rfind(\"Crash \", 0) != 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tCrashFileEntry crashLogFile =\n\t\t\tCrashFileEntry(entry.path(), std::filesystem::last_write_time(entry.path()));\n\n\t\tcrashLogFiles.push_back(crashLogFile);\n\t}\n\n\tstd::sort(crashLogFiles.begin(), crashLogFiles.end(),\n\t\t  [](CrashFileEntry &lhs, CrashFileEntry &rhs) { return lhs.second > rhs.second; });\n\n\tif (crashLogFiles.size() > 0) {\n\t\tlastCrashLogFile = crashLogFiles.front().first;\n\t}\n\n\treturn lastCrashLogFile;\n}\n\nstd::filesystem::path CrashHandler::getCrashLogDirectory() const\n{\n\tBPtr crashLogDirectory = GetAppConfigPathPtr(\"obs-studio/crashes\");\n\n\tstd::string crashLogDirectoryString = crashLogDirectory.Get();\n\n\tstd::filesystem::path crashLogDirectoryPath = std::filesystem::u8path(crashLogDirectoryString);\n\n\treturn crashLogDirectoryPath;\n}\n} // namespace OBS\n"
  },
  {
    "path": "frontend/utility/ExtraBrowsersDelegate.cpp",
    "content": "#include \"ExtraBrowsersDelegate.hpp\"\n#include \"ExtraBrowsersModel.hpp\"\n\n#include <components/EditWidget.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QKeyEvent>\n\n#include \"moc_ExtraBrowsersDelegate.cpp\"\n\nQWidget *ExtraBrowsersDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &,\n\t\t\t\t\t     const QModelIndex &index) const\n{\n\tQLineEdit *text = new EditWidget(parent, index);\n\ttext->installEventFilter(const_cast<ExtraBrowsersDelegate *>(this));\n\ttext->setSizePolicy(QSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding,\n\t\t\t\t\tQSizePolicy::ControlType::LineEdit));\n\treturn text;\n}\n\nvoid ExtraBrowsersDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const\n{\n\tQLineEdit *text = reinterpret_cast<QLineEdit *>(editor);\n\ttext->blockSignals(true);\n\ttext->setText(index.data().toString());\n\ttext->blockSignals(false);\n}\n\nbool ExtraBrowsersDelegate::eventFilter(QObject *object, QEvent *event)\n{\n\tQLineEdit *edit = qobject_cast<QLineEdit *>(object);\n\tif (!edit)\n\t\treturn false;\n\n\tif (LineEditCanceled(event)) {\n\t\tRevertText(edit);\n\t}\n\tif (LineEditChanged(event)) {\n\t\tUpdateText(edit);\n\n\t\tif (event->type() == QEvent::KeyPress) {\n\t\t\tQKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);\n\t\t\tif (keyEvent->key() == Qt::Key_Tab) {\n\t\t\t\tmodel->TabSelection(true);\n\t\t\t} else if (keyEvent->key() == Qt::Key_Backtab) {\n\t\t\t\tmodel->TabSelection(false);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool ExtraBrowsersDelegate::ValidName(const QString &name) const\n{\n\tfor (auto &item : model->items) {\n\t\tif (name.compare(item.title, Qt::CaseInsensitive) == 0) {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn true;\n}\n\nvoid ExtraBrowsersDelegate::RevertText(QLineEdit *edit_)\n{\n\tEditWidget *edit = reinterpret_cast<EditWidget *>(edit_);\n\tint row = edit->index.row();\n\tint col = edit->index.column();\n\tbool newItem = (row == model->items.size());\n\n\tQString oldText;\n\tif (col == (int)Column::Title) {\n\t\toldText = newItem ? model->newTitle : model->items[row].title;\n\t} else {\n\t\toldText = newItem ? model->newURL : model->items[row].url;\n\t}\n\n\tedit->setText(oldText);\n}\n\nbool ExtraBrowsersDelegate::UpdateText(QLineEdit *edit_)\n{\n\tEditWidget *edit = reinterpret_cast<EditWidget *>(edit_);\n\tint row = edit->index.row();\n\tint col = edit->index.column();\n\tbool newItem = (row == model->items.size());\n\n\tQString text = edit->text().trimmed();\n\n\tif (!newItem && text.isEmpty()) {\n\t\treturn false;\n\t}\n\n\tif (col == (int)Column::Title) {\n\t\tQString oldText = newItem ? model->newTitle : model->items[row].title;\n\t\tbool same = oldText.compare(text, Qt::CaseInsensitive) == 0;\n\n\t\tif (!same && !ValidName(text)) {\n\t\t\tedit->setText(oldText);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tif (!newItem) {\n\t\t/* if edited existing item, update it*/\n\t\tswitch (col) {\n\t\tcase (int)Column::Title:\n\t\t\tmodel->items[row].title = text;\n\t\t\tbreak;\n\t\tcase (int)Column::Url:\n\t\t\tmodel->items[row].url = text;\n\t\t\tbreak;\n\t\t}\n\t} else {\n\t\t/* if both new values filled out, create new one */\n\t\tswitch (col) {\n\t\tcase (int)Column::Title:\n\t\t\tmodel->newTitle = text;\n\t\t\tbreak;\n\t\tcase (int)Column::Url:\n\t\t\tmodel->newURL = text;\n\t\t\tbreak;\n\t\t}\n\n\t\tmodel->CheckToAdd();\n\t}\n\n\temit commitData(edit);\n\treturn true;\n}\n"
  },
  {
    "path": "frontend/utility/ExtraBrowsersDelegate.hpp",
    "content": "#pragma once\n\n#include <QStyledItemDelegate>\n\nclass ExtraBrowsersModel;\n\nclass ExtraBrowsersDelegate : public QStyledItemDelegate {\n\tQ_OBJECT\n\npublic:\n\tinline ExtraBrowsersDelegate(ExtraBrowsersModel *model_) : QStyledItemDelegate(nullptr), model(model_) {}\n\n\tQWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,\n\t\t\t      const QModelIndex &index) const override;\n\n\tvoid setEditorData(QWidget *editor, const QModelIndex &index) const override;\n\n\tbool eventFilter(QObject *object, QEvent *event) override;\n\tvoid RevertText(QLineEdit *edit);\n\tbool UpdateText(QLineEdit *edit);\n\tbool ValidName(const QString &text) const;\n\n\tExtraBrowsersModel *model;\n};\n"
  },
  {
    "path": "frontend/utility/ExtraBrowsersModel.cpp",
    "content": "#include \"ExtraBrowsersModel.hpp\"\n\n#include <components/DelButton.hpp>\n#include <docks/BrowserDock.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QTableView>\n\n#include \"moc_ExtraBrowsersModel.cpp\"\n\nvoid ExtraBrowsersModel::Reset()\n{\n\titems.clear();\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tfor (int i = 0; i < main->extraBrowserDocks.size(); i++) {\n\t\tItem item;\n\t\titem.prevIdx = i;\n\t\titem.title = main->extraBrowserDockNames[i];\n\t\titem.url = main->extraBrowserDockTargets[i];\n\t\titems.push_back(item);\n\t}\n}\n\nint ExtraBrowsersModel::rowCount(const QModelIndex &) const\n{\n\tint count = items.size() + 1;\n\treturn count;\n}\n\nint ExtraBrowsersModel::columnCount(const QModelIndex &) const\n{\n\treturn (int)Column::Count;\n}\n\nQVariant ExtraBrowsersModel::data(const QModelIndex &index, int role) const\n{\n\tint column = index.column();\n\tint idx = index.row();\n\tint count = items.size();\n\tbool validRole = role == Qt::DisplayRole || role == Qt::AccessibleTextRole;\n\n\tif (!validRole)\n\t\treturn QVariant();\n\n\tif (idx >= 0 && idx < count) {\n\t\tswitch (column) {\n\t\tcase (int)Column::Title:\n\t\t\treturn items[idx].title;\n\t\tcase (int)Column::Url:\n\t\t\treturn items[idx].url;\n\t\t}\n\t} else if (idx == count) {\n\t\tswitch (column) {\n\t\tcase (int)Column::Title:\n\t\t\treturn newTitle;\n\t\tcase (int)Column::Url:\n\t\t\treturn newURL;\n\t\t}\n\t}\n\n\treturn QVariant();\n}\n\nQVariant ExtraBrowsersModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n\tbool validRole = role == Qt::DisplayRole || role == Qt::AccessibleTextRole;\n\n\tif (validRole && orientation == Qt::Orientation::Horizontal) {\n\t\tswitch (section) {\n\t\tcase (int)Column::Title:\n\t\t\treturn QTStr(\"ExtraBrowsers.DockName\");\n\t\tcase (int)Column::Url:\n\t\t\treturn QStringLiteral(\"URL\");\n\t\t}\n\t}\n\n\treturn QVariant();\n}\n\nQt::ItemFlags ExtraBrowsersModel::flags(const QModelIndex &index) const\n{\n\tQt::ItemFlags flags = QAbstractTableModel::flags(index);\n\n\tif (index.column() != (int)Column::Delete)\n\t\tflags |= Qt::ItemIsEditable;\n\n\treturn flags;\n}\nvoid ExtraBrowsersModel::AddDeleteButton(int idx)\n{\n\tQTableView *widget = reinterpret_cast<QTableView *>(parent());\n\n\tQModelIndex index = createIndex(idx, (int)Column::Delete, nullptr);\n\n\tQPushButton *del = new DelButton(index);\n\tdel->setProperty(\"class\", \"icon-trash\");\n\tdel->setObjectName(\"extraPanelDelete\");\n\tdel->setMinimumSize(QSize(20, 20));\n\tconnect(del, &QPushButton::clicked, this, &ExtraBrowsersModel::DeleteItem);\n\n\twidget->setIndexWidget(index, del);\n\twidget->setRowHeight(idx, 20);\n\twidget->setColumnWidth(idx, 20);\n}\n\nvoid ExtraBrowsersModel::CheckToAdd()\n{\n\tif (newTitle.isEmpty() || newURL.isEmpty())\n\t\treturn;\n\n\tint idx = items.size() + 1;\n\tbeginInsertRows(QModelIndex(), idx, idx);\n\n\tItem item;\n\titem.prevIdx = -1;\n\titem.title = newTitle;\n\titem.url = newURL;\n\titems.push_back(item);\n\n\tnewTitle = \"\";\n\tnewURL = \"\";\n\n\tendInsertRows();\n\n\tAddDeleteButton(idx - 1);\n}\n\nvoid ExtraBrowsersModel::UpdateItem(Item &item)\n{\n\tint idx = item.prevIdx;\n\n\tOBSBasic *main = OBSBasic::Get();\n\tBrowserDock *dock = reinterpret_cast<BrowserDock *>(main->extraBrowserDocks[idx].get());\n\tdock->setWindowTitle(item.title);\n\tdock->setObjectName(item.title + OBJ_NAME_SUFFIX);\n\n\tif (main->extraBrowserDockNames[idx] != item.title) {\n\t\tmain->extraBrowserDockNames[idx] = item.title;\n\t\tdock->toggleViewAction()->setText(item.title);\n\t\tdock->setTitle(item.title);\n\t}\n\n\tif (main->extraBrowserDockTargets[idx] != item.url) {\n\t\tdock->cefWidget->setURL(QT_TO_UTF8(item.url));\n\t\tmain->extraBrowserDockTargets[idx] = item.url;\n\t}\n}\n\nvoid ExtraBrowsersModel::DeleteItem()\n{\n\tQTableView *widget = reinterpret_cast<QTableView *>(parent());\n\n\tDelButton *del = reinterpret_cast<DelButton *>(sender());\n\tint row = del->index.row();\n\n\t/* there's some sort of internal bug in Qt and deleting certain index\n\t * widgets or \"editors\" that can cause a crash inside Qt if the widget\n\t * is not manually removed, at least on 5.7 */\n\twidget->setIndexWidget(del->index, nullptr);\n\tdel->deleteLater();\n\n\t/* --------- */\n\n\tbeginRemoveRows(QModelIndex(), row, row);\n\n\tint prevIdx = items[row].prevIdx;\n\titems.removeAt(row);\n\n\tif (prevIdx != -1) {\n\t\tint i = 0;\n\t\tfor (; i < deleted.size() && deleted[i] < prevIdx; i++)\n\t\t\t;\n\t\tdeleted.insert(i, prevIdx);\n\t}\n\n\tendRemoveRows();\n}\n\nvoid ExtraBrowsersModel::Apply()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tfor (Item &item : items) {\n\t\tif (item.prevIdx != -1) {\n\t\t\tUpdateItem(item);\n\t\t} else {\n\t\t\tQString uuid = QUuid::createUuid().toString();\n\t\t\tuuid.replace(QRegularExpression(\"[{}-]\"), \"\");\n\t\t\tmain->AddExtraBrowserDock(item.title, item.url, uuid, true);\n\t\t}\n\t}\n\n\tfor (int i = deleted.size() - 1; i >= 0; i--) {\n\t\tint idx = deleted[i];\n\t\tmain->extraBrowserDockTargets.removeAt(idx);\n\t\tmain->extraBrowserDockNames.removeAt(idx);\n\t\tmain->extraBrowserDocks.removeAt(idx);\n\t}\n\n\tif (main->extraBrowserDocks.empty())\n\t\tmain->extraBrowserMenuDocksSeparator.clear();\n\n\tdeleted.clear();\n\n\tReset();\n}\n\nvoid ExtraBrowsersModel::TabSelection(bool forward)\n{\n\tQListView *widget = reinterpret_cast<QListView *>(parent());\n\tQItemSelectionModel *selModel = widget->selectionModel();\n\n\tQModelIndex sel = selModel->currentIndex();\n\tint row = sel.row();\n\tint col = sel.column();\n\n\tswitch (sel.column()) {\n\tcase (int)Column::Title:\n\t\tif (!forward) {\n\t\t\tif (row == 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\trow -= 1;\n\t\t}\n\n\t\tcol += 1;\n\t\tbreak;\n\n\tcase (int)Column::Url:\n\t\tif (forward) {\n\t\t\tif (row == items.size()) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\trow += 1;\n\t\t}\n\n\t\tcol -= 1;\n\t}\n\n\tsel = createIndex(row, col, nullptr);\n\tselModel->setCurrentIndex(sel, QItemSelectionModel::Clear);\n}\n\nvoid ExtraBrowsersModel::Init()\n{\n\tfor (int i = 0; i < items.count(); i++)\n\t\tAddDeleteButton(i);\n}\n"
  },
  {
    "path": "frontend/utility/ExtraBrowsersModel.hpp",
    "content": "#pragma once\n\n#include <QAbstractTableModel>\n#include <QUuid>\n\nenum class Column : int {\n\tTitle,\n\tUrl,\n\tDelete,\n\n\tCount,\n};\n\n#define OBJ_NAME_SUFFIX \"_extraBrowser\"\n\nclass ExtraBrowsersModel : public QAbstractTableModel {\n\tQ_OBJECT\n\npublic:\n\tinline ExtraBrowsersModel(QObject *parent = nullptr) : QAbstractTableModel(parent)\n\t{\n\t\tReset();\n\t\tQMetaObject::invokeMethod(this, \"Init\", Qt::QueuedConnection);\n\t}\n\n\tint rowCount(const QModelIndex &parent = QModelIndex()) const override;\n\tint columnCount(const QModelIndex &parent = QModelIndex()) const override;\n\tQVariant data(const QModelIndex &index, int role) const override;\n\tQVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;\n\tQt::ItemFlags flags(const QModelIndex &index) const override;\n\n\tstruct Item {\n\t\tint prevIdx;\n\t\tQString title;\n\t\tQString url;\n\t};\n\n\tvoid TabSelection(bool forward);\n\n\tvoid AddDeleteButton(int idx);\n\tvoid Reset();\n\tvoid CheckToAdd();\n\tvoid UpdateItem(Item &item);\n\tvoid DeleteItem();\n\tvoid Apply();\n\n\tQVector<Item> items;\n\tQVector<int> deleted;\n\n\tQString newTitle;\n\tQString newURL;\n\npublic slots:\n\tvoid Init();\n};\n"
  },
  {
    "path": "frontend/utility/FFmpegCodec.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"FFmpegCodec.hpp\"\n#include \"FFmpegFormat.hpp\"\n\n#include <unordered_map>\n#include <unordered_set>\n\nusing namespace std;\n\nvector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format, bool ignore_compatibility)\n{\n\tvector<FFmpegCodec> codecs;\n\tconst AVCodec *codec;\n\tvoid *i = 0;\n\n\twhile ((codec = av_codec_iterate(&i)) != nullptr) {\n\t\t// Not an encoding codec\n\t\tif (!av_codec_is_encoder(codec))\n\t\t\tcontinue;\n\t\t// Skip if not supported and compatibility check not disabled\n\t\tif (!ignore_compatibility && !av_codec_get_tag(format.codec_tags, codec->id)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tcodecs.emplace_back(codec);\n\t}\n\n\treturn codecs;\n}\n\nbool FFCodecAndFormatCompatible(const char *codec, const char *format)\n{\n\tif (!codec || !format)\n\t\treturn false;\n\n\tconst AVOutputFormat *output_format = av_guess_format(format, nullptr, nullptr);\n\tif (!output_format)\n\t\treturn false;\n\n\tconst AVCodecDescriptor *codec_desc = avcodec_descriptor_get_by_name(codec);\n\tif (!codec_desc)\n\t\treturn false;\n\n\treturn avformat_query_codec(output_format, codec_desc->id, FF_COMPLIANCE_NORMAL) == 1;\n}\n\nstatic const unordered_set<string> builtin_codecs = {\n\t\"h264\", \"hevc\", \"av1\", \"prores\", \"aac\", \"opus\", \"alac\", \"flac\", \"pcm_s16le\", \"pcm_s24le\", \"pcm_f32le\",\n};\n\nbool IsBuiltinCodec(const char *codec)\n{\n\treturn builtin_codecs.count(codec) > 0;\n}\n\nstatic const unordered_map<string, unordered_set<string>> codec_compat = {\n\t// Technically our muxer supports HEVC and AV1 as well, but nothing else does\n\t{\"flv\",\n\t {\n\t\t \"h264\",\n\t\t \"aac\",\n\t }},\n\t{\"mpegts\",\n\t {\n\t\t \"h264\",\n\t\t \"hevc\",\n\t\t \"aac\",\n\t\t \"opus\",\n\t }},\n\t{\"hls\",\n\t // Also using MPEG-TS in our case, but no Opus support\n\t {\n\t\t \"h264\",\n\t\t \"hevc\",\n\t\t \"aac\",\n\t }},\n\t{\"mp4\",\n\t {\n\t\t \"h264\",\n\t\t \"hevc\",\n\t\t \"av1\",\n\t\t \"aac\",\n\t\t \"opus\",\n\t\t \"alac\",\n\t\t \"flac\",\n\t\t \"pcm_s16le\",\n\t\t \"pcm_s24le\",\n\t\t \"pcm_f32le\",\n\t }},\n\t{\"fragmented_mp4\",\n\t {\n\t\t \"h264\",\n\t\t \"hevc\",\n\t\t \"av1\",\n\t\t \"aac\",\n\t\t \"opus\",\n\t\t \"alac\",\n\t\t \"flac\",\n\t\t \"pcm_s16le\",\n\t\t \"pcm_s24le\",\n\t\t \"pcm_f32le\",\n\t }},\n\t// Not part of FFmpeg, see obs-outputs module\n\t{\"hybrid_mp4\",\n\t {\n\t\t \"h264\",\n\t\t \"hevc\",\n\t\t \"av1\",\n\t\t \"aac\",\n\t\t \"opus\",\n\t\t \"alac\",\n\t\t \"flac\",\n\t\t \"pcm_s16le\",\n\t\t \"pcm_s24le\",\n\t\t \"pcm_f32le\",\n\t }},\n\t// Not part of FFmpeg, see obs-outputs module\n\t{\"hybrid_mov\",\n\t {\n\t\t \"h264\",\n\t\t \"hevc\",\n\t\t \"prores\",\n\t\t \"aac\",\n\t\t \"alac\",\n\t\t \"pcm_s16le\",\n\t\t \"pcm_s24le\",\n\t\t \"pcm_f32le\",\n\t }},\n\t{\"mov\",\n\t {\n\t\t \"h264\",\n\t\t \"hevc\",\n\t\t \"prores\",\n\t\t \"aac\",\n\t\t \"alac\",\n\t\t \"pcm_s16le\",\n\t\t \"pcm_s24le\",\n\t\t \"pcm_f32le\",\n\t }},\n\t{\"fragmented_mov\",\n\t {\n\t\t \"h264\",\n\t\t \"hevc\",\n\t\t \"prores\",\n\t\t \"aac\",\n\t\t \"alac\",\n\t\t \"pcm_s16le\",\n\t\t \"pcm_s24le\",\n\t\t \"pcm_f32le\",\n\t }},\n\t// MKV supports everything\n\t{\"mkv\", {}},\n};\n\nbool ContainerSupportsCodec(const string &container, const string &codec)\n{\n\tauto iter = codec_compat.find(container);\n\tif (iter == codec_compat.end())\n\t\treturn false;\n\n\tauto codecs = iter->second;\n\t// Assume everything is supported\n\tif (codecs.empty())\n\t\treturn true;\n\n\treturn codecs.count(codec) > 0;\n}\n"
  },
  {
    "path": "frontend/utility/FFmpegCodec.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"FFmpegShared.hpp\"\n\nextern \"C\" {\n#include <libavcodec/avcodec.h>\n#include <libavdevice/avdevice.h>\n}\n#include <qmetatype.h>\n\nstruct FFmpegFormat;\n\nstruct FFmpegCodec {\n\tconst char *name;\n\tconst char *long_name;\n\tint id;\n\n\tFFmpegCodecType type;\n\n\tFFmpegCodec() = default;\n\n\tFFmpegCodec(const char *name, int id, FFmpegCodecType type = UNKNOWN)\n\t\t: name(name),\n\t\t  long_name(nullptr),\n\t\t  id(id),\n\t\t  type(type)\n\t{\n\t}\n\n\tFFmpegCodec(const AVCodec *codec) : name(codec->name), long_name(codec->long_name), id(codec->id)\n\t{\n\t\tswitch (codec->type) {\n\t\tcase AVMEDIA_TYPE_AUDIO:\n\t\t\ttype = AUDIO;\n\t\t\tbreak;\n\t\tcase AVMEDIA_TYPE_VIDEO:\n\t\t\ttype = VIDEO;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ttype = UNKNOWN;\n\t\t}\n\t}\n\n\tbool operator==(const FFmpegCodec &codec) const\n\t{\n\t\tif (id != codec.id)\n\t\t\treturn false;\n\n\t\treturn strequal(name, codec.name);\n\t}\n};\nQ_DECLARE_METATYPE(FFmpegCodec)\n\nstd::vector<FFmpegCodec> GetFormatCodecs(const FFmpegFormat &format, bool ignore_compatibility);\n\nbool FFCodecAndFormatCompatible(const char *codec, const char *format);\nbool IsBuiltinCodec(const char *codec);\nbool ContainerSupportsCodec(const std::string &container, const std::string &codec);\n"
  },
  {
    "path": "frontend/utility/FFmpegFormat.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"FFmpegFormat.hpp\"\n#include \"FFmpegCodec.hpp\"\n\nusing namespace std;\n\nstatic bool is_output_device(const AVClass *avclass)\n{\n\tif (!avclass)\n\t\treturn false;\n\n\tswitch (avclass->category) {\n\tcase AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT:\n\tcase AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT:\n\tcase AV_CLASS_CATEGORY_DEVICE_OUTPUT:\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nvector<FFmpegFormat> GetSupportedFormats()\n{\n\tvector<FFmpegFormat> formats;\n\tconst AVOutputFormat *output_format;\n\n\tvoid *i = 0;\n\twhile ((output_format = av_muxer_iterate(&i)) != nullptr) {\n\t\tif (is_output_device(output_format->priv_class))\n\t\t\tcontinue;\n\n\t\tformats.emplace_back(output_format);\n\t}\n\n\treturn formats;\n}\n\nFFmpegCodec FFmpegFormat::GetDefaultEncoder(FFmpegCodecType codec_type) const\n{\n\tconst AVCodecID codec_id = codec_type == VIDEO ? video_codec : audio_codec;\n\tif (codec_type == UNKNOWN || codec_id == AV_CODEC_ID_NONE)\n\t\treturn {};\n\n\tif (auto codec = avcodec_find_encoder(codec_id))\n\t\treturn {codec};\n\n\t/* Fall back to using the format name as the encoder,\n\t * this works for some formats such as FLV. */\n\treturn FFmpegCodec{name, codec_id, codec_type};\n}\n"
  },
  {
    "path": "frontend/utility/FFmpegFormat.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"FFmpegShared.hpp\"\n\nextern \"C\" {\n#include <libavcodec/avcodec.h>\n#include <libavdevice/avdevice.h>\n}\n#include <qmetatype.h>\n\nstruct FFmpegCodec;\n\nstruct FFmpegFormat {\n\tconst char *name;\n\tconst char *long_name;\n\tconst char *mime_type;\n\tconst char *extensions;\n\tAVCodecID audio_codec;\n\tAVCodecID video_codec;\n\tconst AVCodecTag *const *codec_tags;\n\n\tFFmpegFormat() = default;\n\n\tFFmpegFormat(const char *name, const char *mime_type)\n\t\t: name(name),\n\t\t  long_name(nullptr),\n\t\t  mime_type(mime_type),\n\t\t  extensions(nullptr),\n\t\t  audio_codec(AV_CODEC_ID_NONE),\n\t\t  video_codec(AV_CODEC_ID_NONE),\n\t\t  codec_tags(nullptr)\n\t{\n\t}\n\n\tFFmpegFormat(const AVOutputFormat *av_format)\n\t\t: name(av_format->name),\n\t\t  long_name(av_format->long_name),\n\t\t  mime_type(av_format->mime_type),\n\t\t  extensions(av_format->extensions),\n\t\t  audio_codec(av_format->audio_codec),\n\t\t  video_codec(av_format->video_codec),\n\t\t  codec_tags(av_format->codec_tag)\n\t{\n\t}\n\n\tFFmpegCodec GetDefaultEncoder(FFmpegCodecType codec_type) const;\n\n\tbool HasAudio() const { return audio_codec != AV_CODEC_ID_NONE; }\n\tbool HasVideo() const { return video_codec != AV_CODEC_ID_NONE; }\n\n\tbool operator==(const FFmpegFormat &format) const\n\t{\n\t\tif (!strequal(name, format.name))\n\t\t\treturn false;\n\n\t\treturn strequal(mime_type, format.mime_type);\n\t}\n};\n\nQ_DECLARE_METATYPE(FFmpegFormat)\n\nstd::vector<FFmpegFormat> GetSupportedFormats();\n"
  },
  {
    "path": "frontend/utility/FFmpegShared.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <string.h>\n\nenum FFmpegCodecType { AUDIO, VIDEO, UNKNOWN };\n\n/* This needs to handle a few special cases due to how the format is used in the UI:\n * - strequal(nullptr, \"\") must be true\n * - strequal(\"\", nullptr) must be true\n * - strequal(nullptr, nullptr) must be true\n */\nstatic bool strequal(const char *a, const char *b)\n{\n\tif (!a && !b)\n\t\treturn true;\n\tif (!a && *b == 0)\n\t\treturn true;\n\tif (!b && *a == 0)\n\t\treturn true;\n\tif (!a || !b)\n\t\treturn false;\n\n\treturn strcmp(a, b) == 0;\n}\n"
  },
  {
    "path": "frontend/utility/GoLiveAPI_CensoredJson.cpp",
    "content": "#include \"GoLiveAPI_CensoredJson.hpp\"\n\n#include <nlohmann/json.hpp>\n\n#include <unordered_map>\n\nvoid censorRecurse(obs_data_t *);\nvoid censorRecurseArray(obs_data_array_t *);\n\nvoid censorRecurse(obs_data_t *data)\n{\n\t// if we found what we came to censor, censor it\n\tconst char *a = obs_data_get_string(data, \"authentication\");\n\tif (a && *a) {\n\t\tobs_data_set_string(data, \"authentication\", \"CENSORED\");\n\t}\n\n\t// recurse to child objects and arrays\n\tobs_data_item_t *item = obs_data_first(data);\n\tfor (; item != NULL; obs_data_item_next(&item)) {\n\t\tenum obs_data_type typ = obs_data_item_gettype(item);\n\n\t\tif (typ == OBS_DATA_OBJECT) {\n\t\t\tobs_data_t *child_data = obs_data_item_get_obj(item);\n\t\t\tcensorRecurse(child_data);\n\t\t\tobs_data_release(child_data);\n\t\t} else if (typ == OBS_DATA_ARRAY) {\n\t\t\tOBSDataArrayAutoRelease child_array = obs_data_item_get_array(item);\n\t\t\tcensorRecurseArray(child_array);\n\t\t}\n\t}\n}\n\nvoid censorRecurseArray(obs_data_array_t *array)\n{\n\tconst size_t sz = obs_data_array_count(array);\n\tfor (size_t i = 0; i < sz; i++) {\n\t\tobs_data_t *item = obs_data_array_item(array, i);\n\t\tcensorRecurse(item);\n\t\tobs_data_release(item);\n\t}\n}\n\nQString censoredJson(obs_data_t *data, bool pretty)\n{\n\tif (!data) {\n\t\treturn \"\";\n\t}\n\n\t// Ugly clone via JSON write/read\n\tconst char *j = obs_data_get_json(data);\n\tobs_data_t *clone = obs_data_create_from_json(j);\n\n\t// Censor our copy\n\tcensorRecurse(clone);\n\n\t// Turn our copy into JSON\n\tQString s = pretty ? obs_data_get_json_pretty(clone) : obs_data_get_json(clone);\n\n\t// Eliminate our copy\n\tobs_data_release(clone);\n\n\treturn s;\n}\n\nusing json = nlohmann::json;\n\nvoid censorRecurse(json &data)\n{\n\tif (!data.is_structured())\n\t\treturn;\n\n\tauto it = data.find(\"authentication\");\n\tif (it != data.end() && it->is_string()) {\n\t\t*it = \"CENSORED\";\n\t}\n\n\tfor (auto &child : data) {\n\t\tcensorRecurse(child);\n\t}\n}\n\nQString censoredJson(json data, bool pretty)\n{\n\tcensorRecurse(data);\n\n\treturn QString::fromStdString(data.dump(pretty ? 4 : -1));\n}\n"
  },
  {
    "path": "frontend/utility/GoLiveAPI_CensoredJson.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QString>\n#include <nlohmann/json_fwd.hpp>\n\n/**\n * Returns the input serialized to JSON, but any non-empty \"authorization\"\n * properties have their values replaced by \"CENSORED\".\n */\nQString censoredJson(obs_data_t *data, bool pretty = false);\nQString censoredJson(nlohmann::json data, bool pretty = false);\n"
  },
  {
    "path": "frontend/utility/GoLiveAPI_Network.cpp",
    "content": "#include \"GoLiveAPI_Network.hpp\"\n#include \"GoLiveAPI_CensoredJson.hpp\"\n\n#include <OBSApp.hpp>\n#include <utility/MultitrackVideoError.hpp>\n#include <utility/RemoteTextThread.hpp>\n\n#include <obs.hpp>\n\n#include <QMessageBox>\n#include <QThreadPool>\n#include <nlohmann/json.hpp>\n#include <qstring.h>\n\n#include <string>\n\nusing json = nlohmann::json;\n\nQt::ConnectionType BlockingConnectionTypeFor(QObject *object);\n\nvoid HandleGoLiveApiErrors(QWidget *parent, const json &raw_json, const GoLiveApi::Config &config)\n{\n\tusing GoLiveApi::StatusResult;\n\n\tif (!config.status)\n\t\treturn;\n\n\tauto &status = *config.status;\n\tif (status.result == StatusResult::Success)\n\t\treturn;\n\n\tauto warn_continue = [&](QString message) {\n\t\tbool ret = false;\n\t\tQMetaObject::invokeMethod(\n\t\t\tparent,\n\t\t\t[=] {\n\t\t\t\tQMessageBox mb(parent);\n\t\t\t\tmb.setIcon(QMessageBox::Warning);\n\t\t\t\tmb.setWindowTitle(QTStr(\"ConfigDownload.WarningMessageTitle\"));\n\t\t\t\tmb.setTextFormat(Qt::RichText);\n\t\t\t\tmb.setText(message + QTStr(\"FailedToStartStream.WarningRetry\"));\n\t\t\t\tmb.setStandardButtons(QMessageBox::StandardButton::Yes |\n\t\t\t\t\t\t      QMessageBox::StandardButton::No);\n\t\t\t\treturn mb.exec() == QMessageBox::StandardButton::No;\n\t\t\t},\n\t\t\tBlockingConnectionTypeFor(parent), &ret);\n\t\tif (ret)\n\t\t\tthrow MultitrackVideoError::cancel();\n\t};\n\n\tauto missing_html = [] {\n\t\treturn QTStr(\"FailedToStartStream.StatusMissingHTML\").toStdString();\n\t};\n\n\tif (status.result == StatusResult::Unknown) {\n\t\treturn warn_continue(QTStr(\"FailedToStartStream.WarningUnknownStatus\")\n\t\t\t\t\t     .arg(raw_json[\"status\"][\"result\"].dump().c_str()));\n\n\t} else if (status.result == StatusResult::Warning) {\n\t\tif (config.encoder_configurations.empty()) {\n\t\t\tthrow MultitrackVideoError::warning(status.html_en_us.value_or(missing_html()).c_str());\n\t\t}\n\n\t\treturn warn_continue(status.html_en_us.value_or(missing_html()).c_str());\n\t} else if (status.result == StatusResult::Error) {\n\t\tthrow MultitrackVideoError::critical(status.html_en_us.value_or(missing_html()).c_str());\n\t}\n}\n\nGoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url, const GoLiveApi::PostData &post_data,\n\t\t\t\t       const QString &multitrack_video_name)\n{\n\tjson post_data_json = post_data;\n\tblog(LOG_INFO, \"Go live POST data: %s\", censoredJson(post_data_json).toUtf8().constData());\n\n\tif (url.isEmpty())\n\t\tthrow MultitrackVideoError::critical(QTStr(\"FailedToStartStream.MissingConfigURL\"));\n\n\tstd::string encodeConfigText;\n\tstd::string libraryError;\n\n\tstd::vector<std::string> headers;\n\theaders.push_back(\"Content-Type: application/json\");\n\tbool encodeConfigDownloadedOk = GetRemoteFile(url.toUtf8().constData(), encodeConfigText,\n\t\t\t\t\t\t      libraryError, // out params\n\t\t\t\t\t\t      nullptr,\n\t\t\t\t\t\t      nullptr, // out params (response code and content type)\n\t\t\t\t\t\t      \"POST\", post_data_json.dump().c_str(), headers,\n\t\t\t\t\t\t      nullptr, // signature\n\t\t\t\t\t\t      5);      // timeout in seconds\n\n\tif (!encodeConfigDownloadedOk)\n\t\tthrow MultitrackVideoError::warning(\n\t\t\tQTStr(\"FailedToStartStream.ConfigRequestFailed\").arg(url, libraryError.c_str()));\n\ttry {\n\t\tauto data = json::parse(encodeConfigText);\n\t\tblog(LOG_INFO, \"Go live response data: %s\", censoredJson(data, true).toUtf8().constData());\n\t\tGoLiveApi::Config config = data;\n\t\tHandleGoLiveApiErrors(parent, data, config);\n\t\treturn config;\n\n\t} catch (const json::exception &e) {\n\t\tblog(LOG_INFO, \"Failed to parse go live config: %s\", e.what());\n\t\tthrow MultitrackVideoError::warning(\n\t\t\tQTStr(\"FailedToStartStream.FallbackToDefault\").arg(multitrack_video_name));\n\t}\n}\n\nQString MultitrackVideoAutoConfigURL(obs_service_t *service)\n{\n\tstatic const std::optional<QString> cli_url = []() -> std::optional<QString> {\n\t\tauto args = qApp->arguments();\n\t\tfor (int i = 0; i < args.length() - 1; i++) {\n\t\t\tif (args[i] == \"--config-url\" && args.length() > (i + 1)) {\n\t\t\t\treturn args[i + 1];\n\t\t\t}\n\t\t}\n\t\treturn std::nullopt;\n\t}();\n\n\tQString url;\n\tif (cli_url.has_value()) {\n\t\turl = *cli_url;\n\t} else {\n\t\tOBSDataAutoRelease settings = obs_service_get_settings(service);\n\t\turl = obs_data_get_string(settings, \"multitrack_video_configuration_url\");\n\t}\n\n\tblog(LOG_INFO, \"Go live URL: %s\", url.toUtf8().constData());\n\treturn url;\n}\n"
  },
  {
    "path": "frontend/utility/GoLiveAPI_Network.hpp",
    "content": "#pragma once\n\n#include \"models/multitrack-video.hpp\"\n\n#include <obs.hpp>\n\n#include <QString>\n\n/** Returns either GO_LIVE_API_PRODUCTION_URL or a command line override. */\nQString MultitrackVideoAutoConfigURL(obs_service_t *service);\n\nclass QWidget;\n\nGoLiveApi::Config DownloadGoLiveConfig(QWidget *parent, QString url, const GoLiveApi::PostData &post_data,\n\t\t\t\t       const QString &multitrack_video_name);\n"
  },
  {
    "path": "frontend/utility/GoLiveAPI_PostData.cpp",
    "content": "#include \"GoLiveAPI_PostData.hpp\"\n#include \"models/multitrack-video.hpp\"\n\n#include <utility/system-info.hpp>\n\n#include <nlohmann/json.hpp>\n\nGoLiveApi::PostData constructGoLivePost(QString streamKey, const std::optional<uint64_t> &maximum_aggregate_bitrate,\n\t\t\t\t\tconst std::optional<uint32_t> &maximum_video_tracks, bool vod_track_enabled,\n\t\t\t\t\tconst std::vector<OBSCanvasAutoRelease> &canvases)\n{\n\tGoLiveApi::PostData post_data{};\n\tpost_data.service = \"IVS\";\n\tpost_data.schema_version = \"2025-01-25\";\n\tpost_data.authentication = streamKey.toStdString();\n\n\tsystem_info(post_data.capabilities);\n\n\tauto &client = post_data.client;\n\n\tclient.name = \"obs-studio\";\n\tclient.version = obs_get_version_string();\n\n\tconst char *encoder_id = nullptr;\n\tfor (size_t i = 0; obs_enum_encoder_types(i, &encoder_id); i++) {\n\t\tauto codec = obs_get_encoder_codec(encoder_id);\n\t\tif (!codec)\n\t\t\tcontinue;\n\n\t\tif (qstricmp(codec, \"h264\") == 0) {\n\t\t\tclient.supported_codecs.emplace(\"h264\");\n#ifdef ENABLE_HEVC\n\t\t} else if (qstricmp(codec, \"hevc\") == 0) {\n\t\t\tclient.supported_codecs.emplace(\"h265\");\n#endif\n\t\t} else if (qstricmp(codec, \"av1\") == 0) {\n\t\t\tclient.supported_codecs.emplace(\"av1\");\n\t\t}\n\t}\n\n\tauto &preferences = post_data.preferences;\n\tpreferences.vod_track_audio = vod_track_enabled;\n\n\tobs_video_info ovi;\n\tif (obs_get_video_info(&ovi))\n\t\tpreferences.composition_gpu_index = ovi.adapter;\n\n\tfor (const auto &canvas : canvases) {\n\t\tif (obs_canvas_get_video_info(canvas, &ovi)) {\n\t\t\tpreferences.canvases.emplace_back(GoLiveApi::Canvas{ovi.output_width,\n\t\t\t\t\t\t\t\t\t    ovi.output_height,\n\t\t\t\t\t\t\t\t\t    ovi.base_width,\n\t\t\t\t\t\t\t\t\t    ovi.base_height,\n\t\t\t\t\t\t\t\t\t    {ovi.fps_num, ovi.fps_den}});\n\t\t}\n\t}\n\n\tobs_audio_info2 oai2;\n\tif (obs_get_audio_info2(&oai2)) {\n\t\tpreferences.audio_samples_per_sec = oai2.samples_per_sec;\n\t\tpreferences.audio_channels = get_audio_channels(oai2.speakers);\n\t\tpreferences.audio_fixed_buffering = oai2.fixed_buffering;\n\t\tpreferences.audio_max_buffering_ms = oai2.max_buffering_ms;\n\t}\n\n\tif (maximum_aggregate_bitrate.has_value())\n\t\tpreferences.maximum_aggregate_bitrate = maximum_aggregate_bitrate.value();\n\n\tif (maximum_video_tracks.has_value()) {\n\t\t/* Cap to maximum supported number of output encoders. */\n\t\tpreferences.maximum_video_tracks =\n\t\t\tstd::min(maximum_video_tracks.value(), static_cast<uint32_t>(MAX_OUTPUT_VIDEO_ENCODERS));\n\t}\n\n\treturn post_data;\n}\n"
  },
  {
    "path": "frontend/utility/GoLiveAPI_PostData.hpp",
    "content": "#pragma once\n\n#include \"models/multitrack-video.hpp\"\n\n#include <obs.hpp>\n\n#include <QString>\n\n#include <optional>\n\nGoLiveApi::PostData constructGoLivePost(QString streamKey, const std::optional<uint64_t> &maximum_aggregate_bitrate,\n\t\t\t\t\tconst std::optional<uint32_t> &maximum_video_tracks, bool vod_track_enabled,\n\t\t\t\t\tconst std::vector<OBSCanvasAutoRelease> &canvases);\n"
  },
  {
    "path": "frontend/utility/MacUpdateThread.cpp",
    "content": "#include \"MacUpdateThread.hpp\"\n\n#include <OBSApp.hpp>\n#include <utility/WhatsNewInfoThread.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include \"moc_MacUpdateThread.cpp\"\n\nstatic const char *MAC_BRANCHES_URL = \"https://obsproject.com/update_studio/branches.json\";\nstatic const char *MAC_DEFAULT_BRANCH = \"stable\";\n\nbool GetBranch(std::string &selectedBranch)\n{\n\tconst char *config_branch = config_get_string(App()->GetAppConfig(), \"General\", \"UpdateBranch\");\n\tif (!config_branch)\n\t\treturn true;\n\n\tbool found = false;\n\tfor (const UpdateBranch &branch : App()->GetBranches()) {\n\t\tif (branch.name != config_branch)\n\t\t\tcontinue;\n\t\t/* A branch that is found but disabled will just silently fall back to\n\t\t * the default. But if the branch was removed entirely, the user should\n\t\t * be warned, so leave this false *only* if the branch was removed. */\n\t\tfound = true;\n\n\t\tif (branch.is_enabled) {\n\t\t\tselectedBranch = branch.name.toStdString();\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn found;\n}\n\nvoid MacUpdateThread::infoMsg(const QString &title, const QString &text)\n{\n\tOBSMessageBox::information(App()->GetMainWindow(), title, text);\n}\n\nvoid MacUpdateThread::info(const QString &title, const QString &text)\n{\n\tQMetaObject::invokeMethod(this, \"infoMsg\", Qt::BlockingQueuedConnection, Q_ARG(QString, title),\n\t\t\t\t  Q_ARG(QString, text));\n}\n\nvoid MacUpdateThread::run()\ntry {\n\tstd::string text;\n\tstd::string branch = MAC_DEFAULT_BRANCH;\n\n\t/* ----------------------------------- *\n\t * get branches from server            */\n\n\tif (FetchAndVerifyFile(\"branches\", \"obs-studio/updates/branches.json\", MAC_BRANCHES_URL, &text))\n\t\tApp()->SetBranchData(text);\n\n\t/* ----------------------------------- *\n\t * Validate branch selection           */\n\n\tif (!GetBranch(branch)) {\n\t\tconfig_set_string(App()->GetAppConfig(), \"General\", \"UpdateBranch\", MAC_DEFAULT_BRANCH);\n\t\tinfo(QTStr(\"Updater.BranchNotFound.Title\"), QTStr(\"Updater.BranchNotFound.Text\"));\n\t}\n\n\temit Result(QString::fromStdString(branch), manualUpdate);\n\n} catch (std::string &text) {\n\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, text.c_str());\n}\n"
  },
  {
    "path": "frontend/utility/MacUpdateThread.hpp",
    "content": "#pragma once\n\n#include <QThread>\n\nclass MacUpdateThread : public QThread {\n\tQ_OBJECT\n\n\tbool manualUpdate;\n\n\tvirtual void run() override;\n\n\tvoid info(const QString &title, const QString &text);\n\nsignals:\n\tvoid Result(const QString &branch, bool manual);\n\nprivate slots:\n\tvoid infoMsg(const QString &title, const QString &text);\n\npublic:\n\tMacUpdateThread(bool manual) : manualUpdate(manual) {}\n};\n"
  },
  {
    "path": "frontend/utility/MissingFilesModel.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"MissingFilesModel.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include <QFileInfo>\n#include <QMessageBox>\n\n#include \"moc_MissingFilesModel.cpp\"\n\nMissingFilesModel::MissingFilesModel(QObject *parent) : QAbstractTableModel(parent)\n{\n\tQStyle *style = QApplication::style();\n\n\twarningIcon = style->standardIcon(QStyle::SP_MessageBoxWarning);\n}\n\nint MissingFilesModel::rowCount(const QModelIndex &) const\n{\n\treturn files.length();\n}\n\nint MissingFilesModel::columnCount(const QModelIndex &) const\n{\n\treturn MissingFilesColumn::Count;\n}\n\nint MissingFilesModel::found() const\n{\n\tint res = 0;\n\n\tfor (int i = 0; i < files.length(); i++) {\n\t\tif (files[i].state != Missing && files[i].state != Cleared)\n\t\t\tres++;\n\t}\n\n\treturn res;\n}\n\nQVariant MissingFilesModel::data(const QModelIndex &index, int role) const\n{\n\tQVariant result = QVariant();\n\n\tif (index.row() >= files.length()) {\n\t\treturn QVariant();\n\t} else if (role == Qt::DisplayRole) {\n\t\tQFileInfo fi(files[index.row()].originalPath);\n\n\t\tswitch (index.column()) {\n\t\tcase MissingFilesColumn::Source:\n\t\t\tresult = files[index.row()].source;\n\t\t\tbreak;\n\t\tcase MissingFilesColumn::OriginalPath:\n\t\t\tresult = fi.fileName();\n\t\t\tbreak;\n\t\tcase MissingFilesColumn::NewPath:\n\t\t\tresult = files[index.row()].newPath;\n\t\t\tbreak;\n\t\tcase MissingFilesColumn::State:\n\t\t\tswitch (files[index.row()].state) {\n\t\t\tcase MissingFilesState::Missing:\n\t\t\t\tresult = QTStr(\"MissingFiles.Missing\");\n\t\t\t\tbreak;\n\n\t\t\tcase MissingFilesState::Replaced:\n\t\t\t\tresult = QTStr(\"MissingFiles.Replaced\");\n\t\t\t\tbreak;\n\n\t\t\tcase MissingFilesState::Found:\n\t\t\t\tresult = QTStr(\"MissingFiles.Found\");\n\t\t\t\tbreak;\n\n\t\t\tcase MissingFilesState::Cleared:\n\t\t\t\tresult = QTStr(\"MissingFiles.Cleared\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t} else if (role == Qt::DecorationRole && index.column() == MissingFilesColumn::Source) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tOBSSourceAutoRelease source = obs_get_source_by_name(files[index.row()].source.toStdString().c_str());\n\n\t\tif (source) {\n\t\t\tresult = main->GetSourceIcon(obs_source_get_id(source));\n\t\t}\n\t} else if (role == Qt::FontRole && index.column() == MissingFilesColumn::State) {\n\t\tQFont font = QFont();\n\t\tfont.setBold(true);\n\n\t\tresult = font;\n\t} else if (role == Qt::ToolTipRole && index.column() == MissingFilesColumn::State) {\n\t\tswitch (files[index.row()].state) {\n\t\tcase MissingFilesState::Missing:\n\t\t\tresult = QTStr(\"MissingFiles.Missing\");\n\t\t\tbreak;\n\n\t\tcase MissingFilesState::Replaced:\n\t\t\tresult = QTStr(\"MissingFiles.Replaced\");\n\t\t\tbreak;\n\n\t\tcase MissingFilesState::Found:\n\t\t\tresult = QTStr(\"MissingFiles.Found\");\n\t\t\tbreak;\n\n\t\tcase MissingFilesState::Cleared:\n\t\t\tresult = QTStr(\"MissingFiles.Cleared\");\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t} else if (role == Qt::ToolTipRole) {\n\t\tswitch (index.column()) {\n\t\tcase MissingFilesColumn::OriginalPath:\n\t\t\tresult = files[index.row()].originalPath;\n\t\t\tbreak;\n\t\tcase MissingFilesColumn::NewPath:\n\t\t\tresult = files[index.row()].newPath;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nQt::ItemFlags MissingFilesModel::flags(const QModelIndex &index) const\n{\n\tQt::ItemFlags flags = QAbstractTableModel::flags(index);\n\n\tif (index.column() == MissingFilesColumn::OriginalPath) {\n\t\tflags &= ~Qt::ItemIsEditable;\n\t} else if (index.column() == MissingFilesColumn::NewPath && index.row() != files.length()) {\n\t\tflags |= Qt::ItemIsEditable;\n\t}\n\n\treturn flags;\n}\n\nvoid MissingFilesModel::findAllFilesInPath(const QString &path, bool skipPrompt)\n{\n\tallFilesFound = false;\n\tfileCheckLoop(path, skipPrompt, 0);\n}\n\nvoid MissingFilesModel::fileCheckLoop(const QString &path, bool skipPrompt, int depth)\n{\n\tif (allFilesFound) {\n\t\treturn;\n\t}\n\n\tif (files.length() == found()) {\n\t\tallFilesFound = true;\n\t\treturn;\n\t}\n\n\trecursionLevel += 1;\n\tQUrl url = QUrl::fromLocalFile(path);\n\tQString dir = url.toDisplayString(QUrl::RemoveScheme | QUrl::RemoveFilename | QUrl::PreferLocalFile);\n\n\tbool prompted = skipPrompt;\n\tint depthWithoutFileMatch = depth + 1;\n\n\tint loopIndex = -1;\n\tfor (const MissingFileEntry &file : files) {\n\t\tloopIndex++;\n\n\t\tif (file.state != MissingFilesState::Missing) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tQUrl origFile = QUrl::fromLocalFile(file.originalPath);\n\t\tQString filename = origFile.fileName();\n\t\tQString testFile = dir + filename;\n\n\t\tif (os_file_exists(testFile.toStdString().c_str())) {\n\t\t\tdepthWithoutFileMatch = 0;\n\n\t\t\tif (!prompted) {\n\t\t\t\tQMessageBox::StandardButton button =\n\t\t\t\t\tQMessageBox::question(nullptr, QTStr(\"MissingFiles.AutoSearch\"),\n\t\t\t\t\t\t\t      QTStr(\"MissingFiles.AutoSearchText\"));\n\n\t\t\t\tif (button == QMessageBox::No) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tprompted = true;\n\t\t\t}\n\t\t\tQModelIndex in = index(loopIndex, MissingFilesColumn::NewPath);\n\t\t\tsetData(in, testFile, Qt::DisplayRole);\n\t\t}\n\t}\n\n\tif (depthWithoutFileMatch <= 2) {\n\t\tos_dir_t *folder = os_opendir(dir.toStdString().c_str());\n\t\tstruct os_dirent *ent;\n\t\twhile ((ent = os_readdir(folder)) != NULL) {\n\t\t\tif (!ent->directory || *ent->d_name == '.')\n\t\t\t\tcontinue;\n\n\t\t\tQString directoryPath = dir + QString(ent->d_name) + \"/\";\n\t\t\tfileCheckLoop(directoryPath, true, depthWithoutFileMatch);\n\t\t}\n\n\t\tos_closedir(folder);\n\t}\n\trecursionLevel -= 1;\n}\n\nbool MissingFilesModel::setData(const QModelIndex &index, const QVariant &value, int role)\n{\n\tbool success = false;\n\n\tint row = index.row();\n\tQString valueString = value.toString();\n\n\tif (role == MissingFilesRole::NewPathsToProcessRole) {\n\t\tQStringList list = value.toStringList();\n\n\t\tint newRow = row + 1;\n\t\tbeginInsertRows(QModelIndex(), newRow, newRow);\n\n\t\tMissingFileEntry entry;\n\t\tentry.originalPath = list[0].replace(\"\\\\\", \"/\");\n\t\tentry.source = list[1];\n\n\t\tfiles.insert(newRow, entry);\n\n\t\tendInsertRows();\n\n\t\tsuccess = true;\n\t} else if (index.column() == MissingFilesColumn::NewPath && files[row].newPath != valueString) {\n\t\tQString path = valueString;\n\t\tfiles[row].newPath = valueString;\n\t\tQString fileName = QUrl(path).fileName();\n\t\tQString origFileName = QUrl(files[row].originalPath).fileName();\n\n\t\tif (path.isEmpty()) {\n\t\t\tfiles[row].state = MissingFilesState::Missing;\n\t\t} else if (path.compare(QTStr(\"MissingFiles.Clear\")) == 0) {\n\t\t\tfiles[row].state = MissingFilesState::Cleared;\n\t\t} else if (fileName.compare(origFileName) == 0) {\n\t\t\tfiles[row].state = MissingFilesState::Found;\n\n\t\t\tif (recursionLevel == 0) {\n\t\t\t\tfindAllFilesInPath(path, false);\n\t\t\t}\n\t\t} else {\n\t\t\tfiles[row].state = MissingFilesState::Replaced;\n\n\t\t\tif (recursionLevel == 0) {\n\t\t\t\tfindAllFilesInPath(path, false);\n\t\t\t}\n\t\t}\n\n\t\temit dataChanged(index, index);\n\n\t\tsuccess = true;\n\t}\n\n\treturn success;\n}\n\nQVariant MissingFilesModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n\tQVariant result = QVariant();\n\n\tif (role == Qt::DisplayRole && orientation == Qt::Orientation::Horizontal) {\n\t\tswitch (section) {\n\t\tcase MissingFilesColumn::State:\n\t\t\tresult = QTStr(\"MissingFiles.State\");\n\t\t\tbreak;\n\t\tcase MissingFilesColumn::Source:\n\t\t\tresult = QTStr(\"Basic.Main.Source\");\n\t\t\tbreak;\n\t\tcase MissingFilesColumn::OriginalPath:\n\t\t\tresult = QTStr(\"MissingFiles.MissingFile\");\n\t\t\tbreak;\n\t\tcase MissingFilesColumn::NewPath:\n\t\t\tresult = QTStr(\"MissingFiles.NewFile\");\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn result;\n}\n"
  },
  {
    "path": "frontend/utility/MissingFilesModel.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QAbstractTableModel>\n#include <QIcon>\n\n// TODO: Fix redefinition error of due to clash with enums defined in importer code.\nenum MissingFilesRole { EntryStateRole = Qt::UserRole, NewPathsToProcessRole };\n\n// TODO: Fix redefinition error of due to clash with enums defined in importer code.\nenum MissingFilesColumn { Source, OriginalPath, NewPath, State, Count };\n\nenum MissingFilesState { Missing, Found, Replaced, Cleared };\n\nQ_DECLARE_METATYPE(MissingFilesState);\n\nclass MissingFilesModel : public QAbstractTableModel {\n\tQ_OBJECT\n\n\tfriend class OBSMissingFiles;\n\npublic:\n\texplicit MissingFilesModel(QObject *parent = 0);\n\n\tint rowCount(const QModelIndex &parent = QModelIndex()) const;\n\tint columnCount(const QModelIndex &parent = QModelIndex()) const;\n\tint found() const;\n\tQVariant data(const QModelIndex &index, int role) const;\n\tQVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n\tQt::ItemFlags flags(const QModelIndex &index) const;\n\tbool setData(const QModelIndex &index, const QVariant &value, int role);\n\n\tbool allFilesFound = false;\n\tint recursionLevel = 0;\n\n\tQIcon warningIcon;\n\nprivate:\n\tstruct MissingFileEntry {\n\t\tMissingFilesState state = MissingFilesState::Missing;\n\n\t\tQString source;\n\n\t\tQString originalPath;\n\t\tQString newPath;\n\t};\n\n\tQList<MissingFileEntry> files;\n\n\tvoid findAllFilesInPath(const QString &path, bool skipPrompt);\n\tvoid fileCheckLoop(const QString &path, bool skipPrompt, int depth);\n};\n"
  },
  {
    "path": "frontend/utility/MissingFilesPathItemDelegate.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"MissingFilesPathItemDelegate.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <QFileDialog>\n#include <QHBoxLayout>\n#include <QLineEdit>\n#include <QToolButton>\n\n#include \"moc_MissingFilesPathItemDelegate.cpp\"\n\nenum MissingFilesRole { EntryStateRole = Qt::UserRole, NewPathsToProcessRole };\n\nMissingFilesPathItemDelegate::MissingFilesPathItemDelegate() : QStyledItemDelegate() {}\n\nQWidget *MissingFilesPathItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */,\n\t\t\t\t\t\t    const QModelIndex &) const\n{\n\tQSizePolicy buttonSizePolicy(QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Expanding,\n\t\t\t\t     QSizePolicy::ControlType::PushButton);\n\n\tQWidget *container = new QWidget(parent);\n\n\tauto browseCallback = [this, container]() {\n\t\tconst_cast<MissingFilesPathItemDelegate *>(this)->handleBrowse(container);\n\t};\n\n\tauto clearCallback = [this, container]() {\n\t\tconst_cast<MissingFilesPathItemDelegate *>(this)->handleClear(container);\n\t};\n\n\tQHBoxLayout *layout = new QHBoxLayout();\n\tlayout->setContentsMargins(0, 0, 0, 0);\n\tlayout->setSpacing(0);\n\n\tQLineEdit *text = new QLineEdit();\n\ttext->setObjectName(QStringLiteral(\"text\"));\n\ttext->setSizePolicy(QSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding,\n\t\t\t\t\tQSizePolicy::ControlType::LineEdit));\n\tlayout->addWidget(text);\n\n\tQToolButton *browseButton = new QToolButton();\n\tbrowseButton->setText(\"...\");\n\tbrowseButton->setSizePolicy(buttonSizePolicy);\n\tlayout->addWidget(browseButton);\n\n\tconnect(browseButton, &QToolButton::clicked, this, browseCallback);\n\n\tQToolButton *clearButton = new QToolButton();\n\tQIcon icon;\n\ticon.addFile(QString::fromUtf8(\":/res/images/close.svg\"), QSize(), QIcon::Mode::Normal, QIcon::State::Off);\n\tclearButton->setIcon(icon);\n\tclearButton->setProperty(\"class\", \"icon-close\");\n\tclearButton->setSizePolicy(buttonSizePolicy);\n\tlayout->addWidget(clearButton);\n\n\tconnect(clearButton, &QToolButton::clicked, this, clearCallback);\n\n\tcontainer->setLayout(layout);\n\tcontainer->setFocusProxy(text);\n\n\treturn container;\n}\n\nvoid MissingFilesPathItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const\n{\n\tQLineEdit *text = editor->findChild<QLineEdit *>();\n\ttext->setText(index.data().toString());\n\n\teditor->setProperty(PATH_LIST_PROP, QVariant());\n}\n\nvoid MissingFilesPathItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,\n\t\t\t\t\t\tconst QModelIndex &index) const\n{\n\t// We use the PATH_LIST_PROP property to pass a list of\n\t// path strings from the editor widget into the model's\n\t// NewPathsToProcessRole. This is only used when paths\n\t// are selected through the \"browse\" or \"delete\" buttons\n\t// in the editor. If the user enters new text in the\n\t// text box, we simply pass that text on to the model\n\t// as normal text data in the default role.\n\tQVariant pathListProp = editor->property(PATH_LIST_PROP);\n\tif (pathListProp.isValid()) {\n\t\tQStringList list = editor->property(PATH_LIST_PROP).toStringList();\n\t\tmodel->setData(index, list);\n\t} else {\n\t\tQLineEdit *lineEdit = editor->findChild<QLineEdit *>();\n\t\tmodel->setData(index, lineEdit->text(), 0);\n\t}\n}\n\nvoid MissingFilesPathItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,\n\t\t\t\t\t const QModelIndex &index) const\n{\n\tQStyleOptionViewItem localOption = option;\n\tinitStyleOption(&localOption, index);\n\n\tQApplication::style()->drawControl(QStyle::CE_ItemViewItem, &localOption, painter);\n}\n\nvoid MissingFilesPathItemDelegate::initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const\n{\n\tQStyledItemDelegate::initStyleOption(option, index);\n\toption->textElideMode = Qt::ElideMiddle;\n}\n\nvoid MissingFilesPathItemDelegate::handleBrowse(QWidget *container)\n{\n\n\tQLineEdit *text = container->findChild<QLineEdit *>();\n\n\tQString currentPath = text->text();\n\tif (currentPath.isEmpty() || currentPath.compare(QTStr(\"MissingFiles.Clear\")) == 0)\n\t\tcurrentPath = \"\";\n\n\tbool isSet = false;\n\n\tQString newPath =\n\t\tQFileDialog::getOpenFileName(container, QTStr(\"MissingFiles.SelectFile\"), currentPath, nullptr);\n\n#ifdef __APPLE__\n\t// TODO: Revisit when QTBUG-42661 is fixed\n\tcontainer->window()->raise();\n#endif\n\n\tif (!newPath.isEmpty()) {\n\t\tcontainer->setProperty(PATH_LIST_PROP, QStringList() << newPath);\n\t\tisSet = true;\n\t}\n\n\tif (isSet)\n\t\temit commitData(container);\n}\n\nvoid MissingFilesPathItemDelegate::handleClear(QWidget *container)\n{\n\t// An empty string list will indicate that the entry is being\n\t// blanked and should be deleted.\n\tcontainer->setProperty(PATH_LIST_PROP, QStringList() << QTStr(\"MissingFiles.Clear\"));\n\tcontainer->findChild<QLineEdit *>()->clearFocus();\n\t((QWidget *)container->parent())->setFocus();\n\temit commitData(container);\n}\n"
  },
  {
    "path": "frontend/utility/MissingFilesPathItemDelegate.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QStyledItemDelegate>\n\nclass MissingFilesPathItemDelegate : public QStyledItemDelegate {\n\tQ_OBJECT\n\npublic:\n\tMissingFilesPathItemDelegate();\n\n\tvirtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */,\n\t\t\t\t      const QModelIndex &index) const override;\n\n\tvirtual void setEditorData(QWidget *editor, const QModelIndex &index) const override;\n\tvirtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;\n\tvirtual void paint(QPainter *painter, const QStyleOptionViewItem &option,\n\t\t\t   const QModelIndex &index) const override;\n\nprotected:\n\tvoid initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override;\n\nprivate:\n\tconst char *PATH_LIST_PROP = \"pathList\";\n\n\tvoid handleBrowse(QWidget *container);\n\tvoid handleClear(QWidget *container);\n};\n"
  },
  {
    "path": "frontend/utility/MultitrackVideoError.cpp",
    "content": "#include \"MultitrackVideoError.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <QMessageBox>\n#include <QPushButton>\n\nMultitrackVideoError MultitrackVideoError::critical(QString error)\n{\n\treturn {Type::Critical, error};\n}\n\nMultitrackVideoError MultitrackVideoError::warning(QString error)\n{\n\treturn {Type::Warning, error};\n}\n\nMultitrackVideoError MultitrackVideoError::cancel()\n{\n\treturn {Type::Cancel, {}};\n}\n\nbool MultitrackVideoError::ShowDialog(QWidget *parent, const QString &multitrack_video_name) const\n{\n\tQMessageBox mb(parent);\n\tmb.setTextFormat(Qt::RichText);\n\tmb.setWindowTitle(QTStr(\"Output.StartStreamFailed\"));\n\n\tif (type == Type::Warning) {\n\t\tmb.setText(error +\n\t\t\t   QTStr(\"FailedToStartStream.WarningRetryNonMultitrackVideo\").arg(multitrack_video_name));\n\t\tmb.setIcon(QMessageBox::Warning);\n\t\tQAbstractButton *yesButton = mb.addButton(QTStr(\"Yes\"), QMessageBox::YesRole);\n\t\tmb.addButton(QTStr(\"No\"), QMessageBox::NoRole);\n\t\tmb.exec();\n\n\t\treturn mb.clickedButton() == yesButton;\n\t} else if (type == Type::Critical) {\n\t\tmb.setText(error);\n\t\tmb.setIcon(QMessageBox::Critical);\n\t\tmb.setStandardButtons(QMessageBox::StandardButton::Ok); // cannot continue\n\t\tmb.exec();\n\t}\n\n\treturn false;\n}\n"
  },
  {
    "path": "frontend/utility/MultitrackVideoError.hpp",
    "content": "#pragma once\n\n#include <QString>\n\nclass QWidget;\n\nstruct MultitrackVideoError {\n\tstatic MultitrackVideoError critical(QString error);\n\tstatic MultitrackVideoError warning(QString error);\n\tstatic MultitrackVideoError cancel();\n\n\tbool ShowDialog(QWidget *parent, const QString &multitrack_video_name) const;\n\n\tenum struct Type {\n\t\tCritical,\n\t\tWarning,\n\t\tCancel,\n\t};\n\n\tconst Type type;\n\tconst QString error;\n};\n"
  },
  {
    "path": "frontend/utility/MultitrackVideoOutput.cpp",
    "content": "#include \"MultitrackVideoError.hpp\"\n#include \"MultitrackVideoOutput.hpp\"\n#include \"models/multitrack-video.hpp\"\n#include \"GoLiveAPI_Network.hpp\"\n#include \"GoLiveAPI_PostData.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <bpm.h>\n#include <util/dstr.hpp>\n#include <libavformat/avformat.h>\n\n#include <QPushButton>\n#include <QMessageBox>\n#include <QThread>\n#include <QUrl>\n#include <QUrlQuery>\n\n#include <cinttypes>\n\n// Codec profile strings\nstatic const char *h264_main = \"Main\";\nstatic const char *h264_high = \"High\";\nstatic const char *h264_cb = \"Constrained Baseline\";\nstatic const char *hevc_main = \"Main\";\nstatic const char *hevc_main10 = \"Main 10\";\nstatic const char *av1_main = \"Main\";\n\n// Maximum reconnect attempts with an invalid key error before giving up (roughly 30 seconds with default start value)\nstatic constexpr uint8_t MAX_RECONNECT_ATTEMPTS = 5;\n\nQt::ConnectionType BlockingConnectionTypeFor(QObject *object)\n{\n\treturn object->thread() == QThread::currentThread() ? Qt::DirectConnection : Qt::BlockingQueuedConnection;\n}\n\nbool MultitrackVideoDeveloperModeEnabled()\n{\n\tstatic bool developer_mode = [] {\n\t\tauto args = qApp->arguments();\n\t\tfor (const auto &arg : args) {\n\t\t\tif (arg == \"--enable-multitrack-video-dev\") {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}();\n\treturn developer_mode;\n}\n\nstatic OBSServiceAutoRelease create_service(const GoLiveApi::Config &go_live_config,\n\t\t\t\t\t    const std::optional<std::string> &rtmp_url, const QString &in_stream_key,\n\t\t\t\t\t    std::optional<bool> use_rtmps)\n{\n\tconst char *url = nullptr;\n\tQString stream_key = in_stream_key;\n\n\tconst auto &ingest_endpoints = go_live_config.ingest_endpoints;\n\n\tfor (auto &endpoint : ingest_endpoints) {\n\t\tif (qstrnicmp(\"RTMP\", endpoint.protocol.c_str(), 4))\n\t\t\tcontinue;\n\n\t\tif (use_rtmps.has_value() && *use_rtmps != (qstricmp(\"RTMPS\", endpoint.protocol.c_str()) == 0))\n\t\t\tcontinue;\n\n\t\turl = endpoint.url_template.c_str();\n\t\tif (endpoint.authentication && !endpoint.authentication->empty()) {\n\t\t\tblog(LOG_INFO, \"Using stream key supplied by autoconfig\");\n\t\t\tstream_key = QString::fromStdString(*endpoint.authentication);\n\t\t}\n\t\tbreak;\n\t}\n\n\tif (rtmp_url.has_value()) {\n\t\t// Despite being set by user, it was set to a \"\"\n\t\tif (rtmp_url->empty()) {\n\t\t\tthrow MultitrackVideoError::warning(QTStr(\"FailedToStartStream.NoCustomRTMPURLInSettings\"));\n\t\t}\n\n\t\turl = rtmp_url->c_str();\n\t\tblog(LOG_INFO, \"Using custom RTMP URL: '%s'\", url);\n\t} else {\n\t\tif (!url) {\n\t\t\tblog(LOG_ERROR, \"No RTMP URL in go live config\");\n\t\t\tthrow MultitrackVideoError::warning(QTStr(\"FailedToStartStream.NoRTMPURLInConfig\"));\n\t\t}\n\n\t\tblog(LOG_INFO, \"Using URL template: '%s'\", url);\n\t}\n\n\tDStr str;\n\tdstr_cat(str, url);\n\n\t// dstr_find does not protect against null, and dstr_cat will\n\t// not initialize str if cat'ing with a null url\n\tif (!dstr_is_empty(str)) {\n\t\tauto found = dstr_find(str, \"/{stream_key}\");\n\t\tif (found)\n\t\t\tdstr_remove(str, found - str->array, str->len - (found - str->array));\n\t}\n\n\t/* The stream key itself may contain query parameters, such as\n\t * \"bandwidthtest\" that need to be carried over. */\n\tQUrl parsed_user_key{in_stream_key};\n\tQUrlQuery user_key_query{parsed_user_key};\n\n\tQUrl parsed_key{stream_key};\n\n\tQUrl parsed_url{url};\n\tQUrlQuery parsed_query{parsed_url};\n\n\tfor (const auto &[key, value] : user_key_query.queryItems())\n\t\tparsed_query.addQueryItem(key, value);\n\n\tif (!go_live_config.meta.config_id.empty()) {\n\t\tparsed_query.addQueryItem(\"clientConfigId\", QString::fromStdString(go_live_config.meta.config_id));\n\t}\n\n\tparsed_key.setQuery(parsed_query);\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"server\", str->array);\n\tobs_data_set_string(settings, \"key\", parsed_key.toString().toUtf8().constData());\n\n\tauto service = obs_service_create(\"rtmp_custom\", \"multitrack video service\", settings, nullptr);\n\n\tif (!service) {\n\t\tblog(LOG_WARNING, \"Failed to create multitrack video service\");\n\t\tthrow MultitrackVideoError::warning(QTStr(\"FailedToStartStream.FailedToCreateMultitrackVideoService\"));\n\t}\n\n\treturn service;\n}\n\nstatic OBSOutputAutoRelease create_output()\n{\n\tOBSOutputAutoRelease output = obs_output_create(\"rtmp_output\", \"rtmp multitrack video\", nullptr, nullptr);\n\n\tif (!output) {\n\t\tblog(LOG_ERROR, \"Failed to create multitrack video rtmp output\");\n\t\tthrow MultitrackVideoError::warning(QTStr(\"FailedToStartStream.FailedToCreateMultitrackVideoOutput\"));\n\t}\n\n\treturn output;\n}\n\nstatic OBSOutputAutoRelease create_recording_output(obs_data_t *settings)\n{\n\tOBSOutputAutoRelease output;\n\tbool useMP4 = obs_data_get_bool(settings, \"use_mp4\");\n\n\tif (useMP4) {\n\t\toutput = obs_output_create(\"mp4_output\", \"mp4 multitrack video\", settings, nullptr);\n\t} else {\n\t\toutput = obs_output_create(\"flv_output\", \"flv multitrack video\", settings, nullptr);\n\t}\n\n\tif (!output) {\n\t\tblog(LOG_ERROR, \"Failed to create multitrack video %s output\", useMP4 ? \"mp4\" : \"flv\");\n\t}\n\n\treturn output;\n}\n\nstatic void adjust_video_encoder_scaling(const obs_video_info &ovi, obs_encoder_t *video_encoder,\n\t\t\t\t\t const GoLiveApi::VideoEncoderConfiguration &encoder_config,\n\t\t\t\t\t size_t encoder_index)\n{\n\tauto requested_width = encoder_config.width;\n\tauto requested_height = encoder_config.height;\n\n\tif (ovi.base_width < requested_width || ovi.base_height < requested_height) {\n\t\tblog(LOG_WARNING,\n\t\t     \"Requested resolution exceeds canvas/available resolution for encoder %zu: %\" PRIu32 \"x%\" PRIu32\n\t\t     \" > %\" PRIu32 \"x%\" PRIu32,\n\t\t     encoder_index, requested_width, requested_height, ovi.base_width, ovi.base_height);\n\t}\n\n\tobs_encoder_set_scaled_size(video_encoder, requested_width, requested_height);\n\tobs_encoder_set_gpu_scale_type(video_encoder, encoder_config.gpu_scale_type.value_or(OBS_SCALE_BICUBIC));\n\tobs_encoder_set_preferred_video_format(video_encoder, encoder_config.format.value_or(VIDEO_FORMAT_NV12));\n\tobs_encoder_set_preferred_color_space(video_encoder, encoder_config.colorspace.value_or(VIDEO_CS_709));\n\tobs_encoder_set_preferred_range(video_encoder, encoder_config.range.value_or(VIDEO_RANGE_PARTIAL));\n}\n\nstatic uint32_t closest_divisor(const obs_video_info &ovi, const media_frames_per_second &target_fps)\n{\n\tauto target = (uint64_t)target_fps.numerator * ovi.fps_den;\n\tauto source = (uint64_t)ovi.fps_num * target_fps.denominator;\n\treturn std::max(1u, static_cast<uint32_t>(source / target));\n}\n\nstatic void adjust_encoder_frame_rate_divisor(const obs_video_info &ovi, obs_encoder_t *video_encoder,\n\t\t\t\t\t      const GoLiveApi::VideoEncoderConfiguration &encoder_config,\n\t\t\t\t\t      const size_t encoder_index)\n{\n\tif (!encoder_config.framerate) {\n\t\tblog(LOG_WARNING, \"`framerate` not specified for encoder %zu\", encoder_index);\n\t\treturn;\n\t}\n\tmedia_frames_per_second requested_fps = *encoder_config.framerate;\n\n\tif (ovi.fps_num == requested_fps.numerator && ovi.fps_den == requested_fps.denominator)\n\t\treturn;\n\n\tauto divisor = closest_divisor(ovi, requested_fps);\n\tif (divisor <= 1)\n\t\treturn;\n\n\tblog(LOG_INFO, \"Setting frame rate divisor to %u for encoder %zu\", divisor, encoder_index);\n\tobs_encoder_set_frame_rate_divisor(video_encoder, divisor);\n}\n\nstatic bool encoder_available(const char *type)\n{\n\tconst char *id = nullptr;\n\n\tfor (size_t idx = 0; obs_enum_encoder_types(idx, &id); idx++) {\n\t\tif (strcmp(id, type) == 0)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic OBSEncoderAutoRelease create_video_encoder(DStr &name_buffer, size_t encoder_index,\n\t\t\t\t\t\t  const GoLiveApi::VideoEncoderConfiguration &encoder_config,\n\t\t\t\t\t\t  const OBSCanvasAutoRelease &canvas)\n{\n\tauto encoder_type = encoder_config.type.c_str();\n\tif (!encoder_available(encoder_type)) {\n\t\tblog(LOG_ERROR, \"Encoder type '%s' not available\", encoder_type);\n\t\tthrow MultitrackVideoError::warning(QTStr(\"FailedToStartStream.EncoderNotAvailable\").arg(encoder_type));\n\t}\n\n\tdstr_printf(name_buffer, \"multitrack video video encoder %zu\", encoder_index);\n\n\tOBSDataAutoRelease encoder_settings = obs_data_create_from_json(encoder_config.settings.dump().c_str());\n\n\t/* VAAPI-based encoders unfortunately use an integer for \"profile\". Until a string-based \"profile\" can be used with\n\t * VAAPI, find the corresponding integer value and update the settings with an integer-based \"profile\".\n\t */\n\tif (strstr(encoder_type, \"vaapi\")) {\n\t\t// Move the \"profile\" string to \"profile_str\".\n\t\tconst char *profile_str = obs_data_get_string(encoder_settings, \"profile\");\n\t\tobs_data_set_string(encoder_settings, \"profile_str\", profile_str);\n\t\tobs_data_item_t *profile_item = obs_data_item_byname(encoder_settings, \"profile\");\n\t\tobs_data_item_remove(&profile_item);\n\t\tobs_data_item_release(&profile_item);\n\n\t\t// Find the vaapi_profile integer based on codec type and \"profile\" string.\n\t\tint vaapi_profile;\n\t\tconst char *codec = obs_get_encoder_codec(encoder_type);\n\t\tif (strcmp(codec, \"h264\") == 0) {\n\t\t\tif (astrcmpi(profile_str, h264_main) == 0) {\n\t\t\t\tvaapi_profile = AV_PROFILE_H264_MAIN;\n\t\t\t} else if (astrcmpi(profile_str, h264_high) == 0) {\n\t\t\t\tvaapi_profile = AV_PROFILE_H264_HIGH;\n\t\t\t} else if (astrcmpi(profile_str, h264_cb) == 0) {\n\t\t\t\tvaapi_profile = AV_PROFILE_H264_CONSTRAINED_BASELINE;\n\t\t\t} else {\n\t\t\t\tblog(LOG_WARNING, \"Unsupported H264 profile '%s', setting to Main profile\",\n\t\t\t\t     profile_str);\n\t\t\t\tvaapi_profile = AV_PROFILE_H264_MAIN;\n\t\t\t}\n\t\t} else if (strcmp(codec, \"hevc\") == 0) {\n\t\t\tif (astrcmpi(profile_str, hevc_main) == 0) {\n\t\t\t\tvaapi_profile = AV_PROFILE_HEVC_MAIN;\n\t\t\t} else if (astrcmpi(profile_str, hevc_main10) == 0) {\n\t\t\t\tvaapi_profile = AV_PROFILE_HEVC_MAIN_10;\n\t\t\t} else {\n\t\t\t\tblog(LOG_WARNING, \"Unsupported HEVC profile '%s', setting to Main profile\",\n\t\t\t\t     profile_str);\n\t\t\t\tvaapi_profile = AV_PROFILE_HEVC_MAIN;\n\t\t\t}\n\t\t} else if (strcmp(codec, \"av1\") == 0) {\n\t\t\tif (astrcmpi(profile_str, av1_main) == 0) {\n\t\t\t\tvaapi_profile = AV_PROFILE_AV1_MAIN;\n\t\t\t} else {\n\t\t\t\tblog(LOG_WARNING, \"Unsupported AV1 profile '%s', setting to Main profile\", profile_str);\n\t\t\t\tvaapi_profile = AV_PROFILE_AV1_MAIN;\n\t\t\t}\n\t\t} else {\n\t\t\tvaapi_profile = AV_PROFILE_UNKNOWN;\n\t\t\tblog(LOG_WARNING, \"Unsupported codec '%s', setting profile to unknown\", codec);\n\t\t}\n\t\tobs_data_set_int(encoder_settings, \"profile\", vaapi_profile);\n\t}\n\tobs_data_set_bool(encoder_settings, \"disable_scenecut\", true);\n\n\tOBSEncoderAutoRelease video_encoder =\n\t\tobs_video_encoder_create(encoder_type, name_buffer, encoder_settings, nullptr);\n\tif (!video_encoder) {\n\t\tblog(LOG_ERROR, \"Failed to create video encoder '%s'\", name_buffer->array);\n\t\tthrow MultitrackVideoError::warning(\n\t\t\tQTStr(\"FailedToStartStream.FailedToCreateVideoEncoder\").arg(name_buffer->array, encoder_type));\n\t}\n\tobs_encoder_set_video(video_encoder, obs_canvas_get_video(canvas));\n\n\tobs_video_info ovi;\n\tif (!obs_canvas_get_video_info(canvas, &ovi)) {\n\t\tblog(LOG_WARNING, \"Failed to get obs_video_info while creating encoder %zu\", encoder_index);\n\t\tthrow MultitrackVideoError::warning(\n\t\t\tQTStr(\"FailedToStartStream.FailedToGetOBSVideoInfo\").arg(name_buffer->array, encoder_type));\n\t}\n\n\tadjust_video_encoder_scaling(ovi, video_encoder, encoder_config, encoder_index);\n\tadjust_encoder_frame_rate_divisor(ovi, video_encoder, encoder_config, encoder_index);\n\n\treturn video_encoder;\n}\n\nstatic OBSEncoderAutoRelease create_audio_encoder(const char *name, const char *audio_encoder_id, obs_data_t *settings,\n\t\t\t\t\t\t  size_t mixer_idx)\n{\n\tOBSEncoderAutoRelease audio_encoder =\n\t\tobs_audio_encoder_create(audio_encoder_id, name, settings, mixer_idx, nullptr);\n\tif (!audio_encoder) {\n\t\tblog(LOG_ERROR, \"Failed to create audio encoder\");\n\t\tthrow MultitrackVideoError::warning(QTStr(\"FailedToStartStream.FailedToCreateAudioEncoder\"));\n\t}\n\tobs_encoder_set_audio(audio_encoder, obs_get_audio());\n\treturn audio_encoder;\n}\n\nstruct OBSOutputs {\n\tOBSOutputAutoRelease output, recording_output;\n};\n\nstatic OBSOutputs SetupOBSOutput(QWidget *parent, const QString &multitrack_video_name,\n\t\t\t\t obs_data_t *dump_stream_to_file_config, const GoLiveApi::Config &go_live_config,\n\t\t\t\t std::vector<OBSEncoderAutoRelease> &audio_encoders,\n\t\t\t\t std::shared_ptr<obs_encoder_group_t> &video_encoder_group,\n\t\t\t\t const char *audio_encoder_id, size_t main_audio_mixer,\n\t\t\t\t std::optional<size_t> vod_track_mixer,\n\t\t\t\t const std::vector<OBSCanvasAutoRelease> &canvases);\nstatic void SetupSignalHandlers(bool recording, MultitrackVideoOutput *self, obs_output_t *output, OBSSignal &start,\n\t\t\t\tOBSSignal &stop);\n\nvoid MultitrackVideoOutput::PrepareStreaming(\n\tQWidget *parent, const char *service_name, obs_service_t *service, const std::optional<std::string> &rtmp_url,\n\tconst QString &stream_key, const char *audio_encoder_id, std::optional<uint32_t> maximum_aggregate_bitrate,\n\tstd::optional<uint32_t> maximum_video_tracks, std::optional<std::string> custom_config,\n\tobs_data_t *dump_stream_to_file_config, size_t main_audio_mixer, std::optional<size_t> vod_track_mixer,\n\tstd::optional<bool> use_rtmps, std::optional<QString> extra_canvas)\n{\n\t{\n\t\tconst std::lock_guard<std::mutex> current_lock{current_mutex};\n\t\tconst std::lock_guard<std::mutex> current_stream_dump_lock{current_stream_dump_mutex};\n\t\tif (current || current_stream_dump) {\n\t\t\tblog(LOG_WARNING, \"Tried to prepare multitrack video output while it's already active\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\trestart_on_error = false;\n\n\tstd::optional<GoLiveApi::Config> go_live_config;\n\tstd::optional<GoLiveApi::Config> custom;\n\tbool is_custom_config = custom_config.has_value();\n\tauto auto_config_url = MultitrackVideoAutoConfigURL(service);\n\n\tOBSDataAutoRelease service_settings = obs_service_get_settings(service);\n\tauto multitrack_video_name = QTStr(\"Basic.Settings.Stream.MultitrackVideoLabel\");\n\tif (obs_data_has_user_value(service_settings, \"multitrack_video_name\")) {\n\t\tmultitrack_video_name = obs_data_get_string(service_settings, \"multitrack_video_name\");\n\t}\n\n\tauto auto_config_url_data = auto_config_url.toUtf8();\n\n\tstd::vector<OBSCanvasAutoRelease> canvases;\n\n\tcanvases.emplace_back(obs_get_main_canvas());\n\tif (extra_canvas) {\n\t\tobs_canvas_t *canvas = obs_get_canvas_by_uuid(extra_canvas->toUtf8().constData());\n\t\tif (!canvas) {\n\t\t\tthrow MultitrackVideoError::critical(QTStr(\"FailedToStartStream.MissingCanvas\"));\n\t\t}\n\t\tcanvases.emplace_back(canvas);\n\t}\n\n\tstd::string canvasNames;\n\tfor (const auto &canvas : canvases) {\n\t\tif (!canvasNames.empty())\n\t\t\tcanvasNames += \", \";\n\n\t\tcanvasNames += obs_canvas_get_name(canvas);\n\t}\n\n\tDStr vod_track_info_storage;\n\tif (vod_track_mixer.has_value())\n\t\tdstr_printf(vod_track_info_storage, \"Yes (mixer: %zu)\", vod_track_mixer.value());\n\n\tblog(LOG_INFO,\n\t     \"Preparing enhanced broadcasting stream for:\\n\"\n\t     \"    custom config:  %s\\n\"\n\t     \"    config url:     %s\\n\"\n\t     \"  settings:\\n\"\n\t     \"    service:               %s\\n\"\n\t     \"    max aggregate bitrate: %s (%\" PRIu32 \")\\n\"\n\t     \"    max video tracks:      %s (%\" PRIu32 \")\\n\"\n\t     \"    custom rtmp url:       %s ('%s')\\n\"\n\t     \"    vod track:             %s\\n\"\n\t     \"    canvases:              %s\",\n\t     is_custom_config ? \"Yes\" : \"No\", !auto_config_url.isEmpty() ? auto_config_url_data.constData() : \"(null)\",\n\t     service_name, maximum_aggregate_bitrate.has_value() ? \"Set\" : \"Auto\",\n\t     maximum_aggregate_bitrate.value_or(0), maximum_video_tracks.has_value() ? \"Set\" : \"Auto\",\n\t     maximum_video_tracks.value_or(0), rtmp_url.has_value() ? \"Yes\" : \"No\",\n\t     rtmp_url.has_value() ? rtmp_url->c_str() : \"\",\n\t     vod_track_info_storage->array ? vod_track_info_storage->array : \"No\", canvasNames.c_str());\n\n\tconst bool custom_config_only = auto_config_url.isEmpty() && custom_config.has_value() &&\n\t\t\t\t\tstrcmp(obs_service_get_id(service), \"rtmp_custom\") == 0;\n\n\tif (!custom_config_only) {\n\t\tauto go_live_post = constructGoLivePost(stream_key, maximum_aggregate_bitrate, maximum_video_tracks,\n\t\t\t\t\t\t\tvod_track_mixer.has_value(), canvases);\n\n\t\tgo_live_config = DownloadGoLiveConfig(parent, auto_config_url, go_live_post, multitrack_video_name);\n\t}\n\n\tif (custom_config.has_value()) {\n\t\tGoLiveApi::Config parsed_custom;\n\t\ttry {\n\t\t\tparsed_custom = nlohmann::json::parse(*custom_config);\n\t\t} catch (const nlohmann::json::exception &exception) {\n\t\t\tblog(LOG_WARNING, \"Failed to parse custom config: %s\", exception.what());\n\t\t\tthrow MultitrackVideoError::critical(QTStr(\"FailedToStartStream.InvalidCustomConfig\"));\n\t\t}\n\n\t\t// copy unique ID from go live request\n\t\tif (go_live_config.has_value()) {\n\t\t\tparsed_custom.meta.config_id = go_live_config->meta.config_id;\n\t\t\tblog(LOG_INFO, \"Using config_id from go live config with custom config: %s\",\n\t\t\t     parsed_custom.meta.config_id.c_str());\n\t\t}\n\n\t\tnlohmann::json custom_data = parsed_custom;\n\t\tblog(LOG_INFO, \"Using custom go live config: %s\", custom_data.dump(4).c_str());\n\n\t\tcustom.emplace(std::move(parsed_custom));\n\t}\n\n\tif (go_live_config.has_value()) {\n\t\tblog(LOG_INFO, \"Enhanced broadcasting config_id: '%s'\", go_live_config->meta.config_id.c_str());\n\t}\n\n\tif (!go_live_config && !custom) {\n\t\tblog(LOG_ERROR, \"MultitrackVideoOutput: no config set, this should never happen\");\n\t\tthrow MultitrackVideoError::warning(QTStr(\"FailedToStartStream.NoConfig\"));\n\t}\n\n\tconst auto &output_config = custom ? *custom : *go_live_config;\n\tconst auto &service_config = go_live_config ? *go_live_config : *custom;\n\n\tstd::vector<OBSEncoderAutoRelease> audio_encoders;\n\tstd::shared_ptr<obs_encoder_group_t> video_encoder_group;\n\tauto outputs = SetupOBSOutput(parent, multitrack_video_name, dump_stream_to_file_config, output_config,\n\t\t\t\t      audio_encoders, video_encoder_group, audio_encoder_id, main_audio_mixer,\n\t\t\t\t      vod_track_mixer, canvases);\n\tauto output = std::move(outputs.output);\n\tauto recording_output = std::move(outputs.recording_output);\n\tif (!output)\n\t\tthrow MultitrackVideoError::warning(\n\t\t\tQTStr(\"FailedToStartStream.FallbackToDefault\").arg(multitrack_video_name));\n\n\tauto multitrack_video_service = create_service(service_config, rtmp_url, stream_key, use_rtmps);\n\tif (!multitrack_video_service)\n\t\tthrow MultitrackVideoError::warning(\n\t\t\tQTStr(\"FailedToStartStream.FallbackToDefault\").arg(multitrack_video_name));\n\n\tobs_output_set_service(output, multitrack_video_service);\n\n\t// Register the BPM (Broadcast Performance Metrics) callback\n\tobs_output_add_packet_callback(output, bpm_inject, NULL);\n\n\t// Set callback to prevent reconnection attempts once the stream key has become invalid\n\tstatic auto reconnect_cb = [](void *param, obs_output_t *, int code) -> bool {\n\t\tauto _this = static_cast<MultitrackVideoOutput *>(param);\n\t\treturn code != OBS_OUTPUT_INVALID_STREAM || (_this->reconnect_attempts++ < MAX_RECONNECT_ATTEMPTS);\n\t};\n\tobs_output_set_reconnect_callback(output, reconnect_cb, this);\n\n\tOBSSignal start_streaming;\n\tOBSSignal stop_streaming;\n\tSetupSignalHandlers(false, this, output, start_streaming, stop_streaming);\n\n\tif (dump_stream_to_file_config && recording_output) {\n\t\tOBSSignal start_recording;\n\t\tOBSSignal stop_recording;\n\t\tSetupSignalHandlers(true, this, recording_output, start_recording, stop_recording);\n\n\t\tdecltype(audio_encoders) recording_audio_encoders;\n\t\trecording_audio_encoders.reserve(audio_encoders.size());\n\t\tfor (auto &encoder : audio_encoders) {\n\t\t\trecording_audio_encoders.emplace_back(obs_encoder_get_ref(encoder));\n\t\t}\n\n\t\tstd::vector<OBSCanvasAutoRelease> recording_canvases(canvases.size());\n\t\tfor (const auto &canvas : canvases) {\n\t\t\trecording_canvases.emplace_back(obs_canvas_get_ref(canvas));\n\t\t}\n\n\t\t{\n\t\t\tconst std::lock_guard current_stream_dump_lock{current_stream_dump_mutex};\n\t\t\tcurrent_stream_dump.emplace(OBSOutputObjects{\n\t\t\t\tstd::move(recording_output),\n\t\t\t\tvideo_encoder_group,\n\t\t\t\tstd::move(recording_audio_encoders),\n\t\t\t\tnullptr,\n\t\t\t\tstd::move(start_recording),\n\t\t\t\tstd::move(stop_recording),\n\t\t\t\tstd::move(recording_canvases),\n\t\t\t});\n\t\t}\n\t}\n\n\tconst std::lock_guard current_lock{current_mutex};\n\tcurrent.emplace(OBSOutputObjects{\n\t\tstd::move(output),\n\t\tvideo_encoder_group,\n\t\tstd::move(audio_encoders),\n\t\tstd::move(multitrack_video_service),\n\t\tstd::move(start_streaming),\n\t\tstd::move(stop_streaming),\n\t\tstd::move(canvases),\n\t});\n}\n\nsignal_handler_t *MultitrackVideoOutput::StreamingSignalHandler()\n{\n\tconst std::lock_guard current_lock{current_mutex};\n\treturn current.has_value() ? obs_output_get_signal_handler(current->output_) : nullptr;\n}\n\nvoid MultitrackVideoOutput::StartedStreaming()\n{\n\tOBSOutputAutoRelease dump_output;\n\t{\n\t\tconst std::lock_guard current_stream_dump_lock{current_stream_dump_mutex};\n\t\tif (current_stream_dump && current_stream_dump->output_) {\n\t\t\tdump_output = obs_output_get_ref(current_stream_dump->output_);\n\t\t}\n\t}\n\n\tif (!dump_output)\n\t\treturn;\n\n\tauto result = obs_output_start(dump_output);\n\tblog(LOG_INFO, \"MultitrackVideoOutput: starting recording%s\", result ? \"\" : \" failed\");\n}\n\nvoid MultitrackVideoOutput::StopStreaming()\n{\n\trestart_on_error = false;\n\n\tOBSOutputAutoRelease current_output;\n\t{\n\t\tconst std::lock_guard current_lock{current_mutex};\n\t\tif (current && current->output_)\n\t\t\tcurrent_output = obs_output_get_ref(current->output_);\n\t}\n\tif (current_output)\n\t\tobs_output_stop(current_output);\n\n\tOBSOutputAutoRelease dump_output;\n\t{\n\t\tconst std::lock_guard current_stream_dump_lock{current_stream_dump_mutex};\n\t\tif (current_stream_dump && current_stream_dump->output_)\n\t\t\tdump_output = obs_output_get_ref(current_stream_dump->output_);\n\t}\n\tif (dump_output)\n\t\tobs_output_stop(dump_output);\n}\n\nbool MultitrackVideoOutput::HandleIncompatibleSettings(QWidget *parent, config_t *config, obs_service_t *service,\n\t\t\t\t\t\t       bool &enableDynBitrate)\n{\n\tQString incompatible_settings;\n\tQString where_to_disable;\n\tQString incompatible_settings_list;\n\n\tsize_t num = 1;\n\n\tauto check_setting = [&](bool setting, const char *name, const char *section) {\n\t\tif (!setting)\n\t\t\treturn;\n\n\t\tincompatible_settings += QString(\" %1. %2\\n\").arg(num).arg(QTStr(name));\n\n\t\twhere_to_disable += QString(\" %1. [%2 → %3 → %4]\\n\")\n\t\t\t\t\t    .arg(num)\n\t\t\t\t\t    .arg(QTStr(\"Settings\"))\n\t\t\t\t\t    .arg(QTStr(\"Basic.Settings.Advanced\"))\n\t\t\t\t\t    .arg(QTStr(section));\n\n\t\tincompatible_settings_list += QString(\"%1, \").arg(name);\n\n\t\tnum += 1;\n\t};\n\n\tcheck_setting(enableDynBitrate, \"Basic.Settings.Output.DynamicBitrate.Beta\", \"Basic.Settings.Advanced.Network\");\n\n\tif (incompatible_settings.isEmpty())\n\t\treturn true;\n\n\tOBSDataAutoRelease service_settings = obs_service_get_settings(service);\n\n\tQMessageBox mb(parent);\n\tmb.setIcon(QMessageBox::Critical);\n\tmb.setWindowTitle(QTStr(\"MultitrackVideo.IncompatibleSettings.Title\"));\n\tmb.setText(QString(QTStr(\"MultitrackVideo.IncompatibleSettings.Text\"))\n\t\t\t   .arg(obs_data_get_string(service_settings, \"multitrack_video_name\"))\n\t\t\t   .arg(incompatible_settings)\n\t\t\t   .arg(where_to_disable));\n\tauto this_stream = mb.addButton(QTStr(\"MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming\"),\n\t\t\t\t\tQMessageBox::AcceptRole);\n\tauto all_streams = mb.addButton(QString(QTStr(\"MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming\")),\n\t\t\t\t\tQMessageBox::AcceptRole);\n\tmb.setStandardButtons(QMessageBox::StandardButton::Cancel);\n\n\tmb.exec();\n\n\tconst char *action = \"cancel\";\n\tif (mb.clickedButton() == this_stream) {\n\t\taction = \"DisableAndStartStreaming\";\n\t} else if (mb.clickedButton() == all_streams) {\n\t\taction = \"UpdateAndStartStreaming\";\n\t}\n\n\tblog(LOG_INFO,\n\t     \"MultitrackVideoOutput: attempted to start stream with incompatible\"\n\t     \"settings (%s); action taken: %s\",\n\t     incompatible_settings_list.toUtf8().constData(), action);\n\n\tif (mb.clickedButton() == this_stream || mb.clickedButton() == all_streams) {\n\t\tenableDynBitrate = false;\n\n\t\tif (mb.clickedButton() == all_streams) {\n\t\t\tconfig_set_bool(config, \"Output\", \"DynamicBitrate\", false);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tMultitrackVideoOutput::ReleaseOnMainThread(take_current());\n\tMultitrackVideoOutput::ReleaseOnMainThread(take_current_stream_dump());\n\n\treturn false;\n}\n\nstatic bool create_video_encoders(const GoLiveApi::Config &go_live_config,\n\t\t\t\t  std::shared_ptr<obs_encoder_group_t> &video_encoder_group, obs_output_t *output,\n\t\t\t\t  obs_output_t *recording_output, const std::vector<OBSCanvasAutoRelease> &canvases)\n{\n\tDStr video_encoder_name_buffer;\n\tif (go_live_config.encoder_configurations.empty()) {\n\t\tblog(LOG_WARNING, \"MultitrackVideoOutput: Missing video encoder configurations\");\n\t\tthrow MultitrackVideoError::warning(QTStr(\"FailedToStartStream.MissingEncoderConfigs\"));\n\t}\n\n\tstd::shared_ptr<obs_encoder_group_t> encoder_group(obs_encoder_group_create(), obs_encoder_group_destroy);\n\tif (!encoder_group)\n\t\treturn false;\n\n\tauto max_canvas_idx = canvases.size() - 1;\n\n\tfor (size_t i = 0; i < go_live_config.encoder_configurations.size(); i++) {\n\t\tauto &config = go_live_config.encoder_configurations[i];\n\t\tif (config.canvas_index > max_canvas_idx) {\n\t\t\tblog(LOG_ERROR, \"MultitrackVideoOutput: Invalid canvas index: %u\", config.canvas_index);\n\t\t\tthrow MultitrackVideoError::warning(QTStr(\"FailedToStartStream.InvalidEncoderConfig\"));\n\t\t}\n\n\t\tauto &canvas = canvases[config.canvas_index];\n\t\tauto encoder = create_video_encoder(video_encoder_name_buffer, i, config, canvas);\n\t\tif (!encoder)\n\t\t\treturn false;\n\n\t\tif (!obs_encoder_set_group(encoder, encoder_group.get()))\n\t\t\treturn false;\n\n\t\tobs_output_set_video_encoder2(output, encoder, i);\n\t\tif (recording_output)\n\t\t\tobs_output_set_video_encoder2(recording_output, encoder, i);\n\t}\n\n\tvideo_encoder_group = encoder_group;\n\treturn true;\n}\n\nstatic void create_audio_encoders(const GoLiveApi::Config &go_live_config,\n\t\t\t\t  std::vector<OBSEncoderAutoRelease> &audio_encoders, obs_output_t *output,\n\t\t\t\t  obs_output_t *recording_output, const char *audio_encoder_id, size_t main_audio_mixer,\n\t\t\t\t  std::optional<size_t> vod_track_mixer, std::vector<speaker_layout> &speaker_layouts,\n\t\t\t\t  speaker_layout &current_layout)\n{\n\tspeaker_layout speakers = SPEAKERS_UNKNOWN;\n\tobs_audio_info oai = {};\n\tif (obs_get_audio_info(&oai))\n\t\tspeakers = oai.speakers;\n\n\tcurrent_layout = speakers;\n\n\tauto sanitize_audio_channels = [&](obs_encoder_t *encoder, uint32_t channels) {\n\t\tspeaker_layout target_speakers = SPEAKERS_UNKNOWN;\n\t\tfor (size_t i = 0; i <= (size_t)SPEAKERS_7POINT1; i++) {\n\t\t\tif (get_audio_channels((speaker_layout)i) != channels)\n\t\t\t\tcontinue;\n\n\t\t\ttarget_speakers = (speaker_layout)i;\n\t\t\tbreak;\n\t\t}\n\t\tif (target_speakers == SPEAKERS_UNKNOWN) {\n\t\t\tblog(LOG_WARNING,\n\t\t\t     \"MultitrackVideoOutput: Could not find \"\n\t\t\t     \"speaker layout for %\" PRIu32 \"channels \"\n\t\t\t     \"while configuring encoder '%s'\",\n\t\t\t     channels, obs_encoder_get_name(encoder));\n\t\t\treturn;\n\t\t}\n\t\tif (speakers != SPEAKERS_UNKNOWN &&\n\t\t    (channels > get_audio_channels(speakers) || speakers == target_speakers))\n\t\t\treturn;\n\n\t\tauto it = std::find(std::begin(speaker_layouts), std::end(speaker_layouts), target_speakers);\n\t\tif (it == std::end(speaker_layouts))\n\t\t\tspeaker_layouts.push_back(target_speakers);\n\t};\n\n\tusing encoder_configs_type = decltype(go_live_config.audio_configurations.live);\n\tDStr encoder_name_buffer;\n\tsize_t output_encoder_index = 0;\n\n\tauto create_encoders = [&](const char *name_prefix, const encoder_configs_type &configs, size_t mixer_idx) {\n\t\tif (configs.empty()) {\n\t\t\tblog(LOG_WARNING, \"MultitrackVideoOutput: Missing audio encoder configurations (for '%s')\",\n\t\t\t     name_prefix);\n\t\t\tthrow MultitrackVideoError::warning(QTStr(\"FailedToStartStream.MissingEncoderConfigs\"));\n\t\t}\n\n\t\tfor (size_t i = 0; i < configs.size(); i++) {\n\t\t\tdstr_printf(encoder_name_buffer, \"%s %zu\", name_prefix, i);\n\t\t\tOBSDataAutoRelease settings = obs_data_create_from_json(configs[i].settings.dump().c_str());\n\t\t\tOBSEncoderAutoRelease audio_encoder =\n\t\t\t\tcreate_audio_encoder(encoder_name_buffer->array, audio_encoder_id, settings, mixer_idx);\n\n\t\t\tsanitize_audio_channels(audio_encoder, configs[i].channels);\n\n\t\t\tobs_output_set_audio_encoder(output, audio_encoder, output_encoder_index);\n\t\t\tif (recording_output)\n\t\t\t\tobs_output_set_audio_encoder(recording_output, audio_encoder, output_encoder_index);\n\t\t\toutput_encoder_index += 1;\n\t\t\taudio_encoders.emplace_back(std::move(audio_encoder));\n\t\t}\n\t};\n\n\tcreate_encoders(\"multitrack video live audio\", go_live_config.audio_configurations.live, main_audio_mixer);\n\n\tif (!vod_track_mixer.has_value())\n\t\treturn;\n\n\t// we already check for empty inside of `create_encoders`\n\tencoder_configs_type empty = {};\n\tcreate_encoders(\"multitrack video vod audio\", go_live_config.audio_configurations.vod.value_or(empty),\n\t\t\t*vod_track_mixer);\n\n\treturn;\n}\n\nstatic const char *speaker_layout_to_string(speaker_layout layout)\n{\n\tswitch (layout) {\n\tcase SPEAKERS_MONO:\n\t\treturn \"Mono\";\n\tcase SPEAKERS_2POINT1:\n\t\treturn \"2.1\";\n\tcase SPEAKERS_4POINT0:\n\t\treturn \"4.0\";\n\tcase SPEAKERS_4POINT1:\n\t\treturn \"4.1\";\n\tcase SPEAKERS_5POINT1:\n\t\treturn \"5.1\";\n\tcase SPEAKERS_7POINT1:\n\t\treturn \"7.1\";\n\tcase SPEAKERS_UNKNOWN:\n\tcase SPEAKERS_STEREO:\n\t\treturn \"Stereo\";\n\t}\n\n\treturn \"Stereo\";\n}\n\nstatic void handle_speaker_layout_issues(QWidget *parent, const QString &multitrack_video_name,\n\t\t\t\t\t const std::vector<speaker_layout> &requested_layouts, speaker_layout layout)\n{\n\tif (requested_layouts.empty())\n\t\treturn;\n\n\tQString message;\n\tif (requested_layouts.size() == 1) {\n\t\tmessage = QTStr(\"MultitrackVideo.IncompatibleSettings.AudioChannelsSingle\")\n\t\t\t\t  .arg(QTStr(speaker_layout_to_string(requested_layouts.front())));\n\t} else {\n\t\tmessage =\n\t\t\tQTStr(\"MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple\").arg(multitrack_video_name);\n\t}\n\n\tQMetaObject::invokeMethod(\n\t\tparent,\n\t\t[&] {\n\t\t\tQMessageBox mb(parent);\n\t\t\tmb.setIcon(QMessageBox::Critical);\n\t\t\tmb.setWindowTitle(QTStr(\"MultitrackVideo.IncompatibleSettings.Title\"));\n\t\t\tmb.setText(QTStr(\"MultitrackVideo.IncompatibleSettings.AudioChannels\")\n\t\t\t\t\t   .arg(multitrack_video_name)\n\t\t\t\t\t   .arg(QTStr(speaker_layout_to_string(layout)))\n\t\t\t\t\t   .arg(message));\n\n\t\t\tmb.setStandardButtons(QMessageBox::StandardButton::Cancel);\n\n\t\t\tmb.exec();\n\t\t},\n\t\tBlockingConnectionTypeFor(parent));\n\n\tblog(LOG_INFO, \"MultitrackVideoOutput: Attempted to start stream with incompatible \"\n\t\t       \"audio channel setting. Action taken: cancel\");\n\n\tthrow MultitrackVideoError::cancel();\n}\n\nstatic OBSOutputs SetupOBSOutput(QWidget *parent, const QString &multitrack_video_name,\n\t\t\t\t obs_data_t *dump_stream_to_file_config, const GoLiveApi::Config &go_live_config,\n\t\t\t\t std::vector<OBSEncoderAutoRelease> &audio_encoders,\n\t\t\t\t std::shared_ptr<obs_encoder_group_t> &video_encoder_group,\n\t\t\t\t const char *audio_encoder_id, size_t main_audio_mixer,\n\t\t\t\t std::optional<size_t> vod_track_mixer,\n\t\t\t\t const std::vector<OBSCanvasAutoRelease> &canvases)\n{\n\tauto output = create_output();\n\tOBSOutputAutoRelease recording_output;\n\tif (dump_stream_to_file_config)\n\t\trecording_output = create_recording_output(dump_stream_to_file_config);\n\n\tif (!create_video_encoders(go_live_config, video_encoder_group, output, recording_output, canvases))\n\t\treturn {nullptr, nullptr};\n\n\tstd::vector<speaker_layout> requested_speaker_layouts;\n\tspeaker_layout current_layout = SPEAKERS_UNKNOWN;\n\tcreate_audio_encoders(go_live_config, audio_encoders, output, recording_output, audio_encoder_id,\n\t\t\t      main_audio_mixer, vod_track_mixer, requested_speaker_layouts, current_layout);\n\n\thandle_speaker_layout_issues(parent, multitrack_video_name, requested_speaker_layouts, current_layout);\n\n\treturn {std::move(output), std::move(recording_output)};\n}\n\nvoid SetupSignalHandlers(bool recording, MultitrackVideoOutput *self, obs_output_t *output, OBSSignal &start,\n\t\t\t OBSSignal &stop)\n{\n\tauto handler = obs_output_get_signal_handler(output);\n\n\tstart.Connect(handler, \"start\", !recording ? StreamStartHandler : RecordingStartHandler, self);\n\n\tstop.Connect(handler, \"stop\", !recording ? StreamStopHandler : RecordingStopHandler, self);\n}\n\nstd::optional<MultitrackVideoOutput::OBSOutputObjects> MultitrackVideoOutput::take_current()\n{\n\tconst std::lock_guard<std::mutex> current_lock{current_mutex};\n\tauto val = std::move(current);\n\tcurrent.reset();\n\treturn val;\n}\n\nstd::optional<MultitrackVideoOutput::OBSOutputObjects> MultitrackVideoOutput::take_current_stream_dump()\n{\n\tconst std::lock_guard<std::mutex> current_stream_dump_lock{current_stream_dump_mutex};\n\tauto val = std::move(current_stream_dump);\n\tcurrent_stream_dump.reset();\n\treturn val;\n}\n\nvoid MultitrackVideoOutput::ReleaseOnMainThread(std::optional<OBSOutputObjects> objects)\n{\n\n\tif (!objects.has_value())\n\t\treturn;\n\n\tQMetaObject::invokeMethod(\n\t\tQApplication::instance()->thread(), [objects = std::move(objects)] {}, Qt::QueuedConnection);\n}\n\nvoid StreamStartHandler(void *arg, calldata_t *)\n{\n\tauto self = static_cast<MultitrackVideoOutput *>(arg);\n\tself->restart_on_error = true;\n\tself->reconnect_attempts = 0;\n}\n\nvoid StreamStopHandler(void *arg, calldata_t *data)\n{\n\tauto self = static_cast<MultitrackVideoOutput *>(arg);\n\n\tOBSOutputAutoRelease stream_dump_output;\n\t{\n\t\tconst std::lock_guard<std::mutex> current_stream_dump_lock{self->current_stream_dump_mutex};\n\t\tif (self->current_stream_dump && self->current_stream_dump->output_)\n\t\t\tstream_dump_output = obs_output_get_ref(self->current_stream_dump->output_);\n\t}\n\tif (stream_dump_output)\n\t\tobs_output_stop(stream_dump_output);\n\n\t/* Unregister the BPM (Broadcast Performance Metrics) callback and destroy the allocated metrics data. */\n\tobs_output_remove_packet_callback(static_cast<obs_output_t *>(calldata_ptr(data, \"output\")), bpm_inject, NULL);\n\tbpm_destroy(static_cast<obs_output_t *>(calldata_ptr(data, \"output\")));\n\n\tMultitrackVideoOutput::ReleaseOnMainThread(self->take_current());\n}\n\nvoid RecordingStartHandler(void * /* arg */, calldata_t * /* data */)\n{\n\tblog(LOG_INFO, \"MultitrackVideoOutput: recording started\");\n}\n\nvoid RecordingStopHandler(void *arg, calldata_t *)\n{\n\tauto self = static_cast<MultitrackVideoOutput *>(arg);\n\tblog(LOG_INFO, \"MultitrackVideoOutput: recording stopped\");\n\tMultitrackVideoOutput::ReleaseOnMainThread(self->take_current_stream_dump());\n}\n"
  },
  {
    "path": "frontend/utility/MultitrackVideoOutput.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n#include <util/config-file.h>\n\n#include <memory>\n#include <mutex>\n#include <optional>\n#include <string>\n#include <vector>\n\nclass QString;\nclass QWidget;\n\nvoid StreamStartHandler(void *arg, calldata_t *);\nvoid StreamStopHandler(void *arg, calldata_t *data);\n\nvoid RecordingStartHandler(void *arg, calldata_t *data);\nvoid RecordingStopHandler(void *arg, calldata_t *);\n\nbool MultitrackVideoDeveloperModeEnabled();\n\nstruct MultitrackVideoOutput {\npublic:\n\tvoid PrepareStreaming(QWidget *parent, const char *service_name, obs_service_t *service,\n\t\t\t      const std::optional<std::string> &rtmp_url, const QString &stream_key,\n\t\t\t      const char *audio_encoder_id, std::optional<uint32_t> maximum_aggregate_bitrate,\n\t\t\t      std::optional<uint32_t> maximum_video_tracks, std::optional<std::string> custom_config,\n\t\t\t      obs_data_t *dump_stream_to_file_config, size_t main_audio_mixer,\n\t\t\t      std::optional<size_t> vod_track_mixer, std::optional<bool> use_rtmps,\n\t\t\t      std::optional<QString> extra_canvas);\n\tsignal_handler_t *StreamingSignalHandler();\n\tvoid StartedStreaming();\n\tvoid StopStreaming();\n\tbool HandleIncompatibleSettings(QWidget *parent, config_t *config, obs_service_t *service,\n\t\t\t\t\tbool &enableDynBitrate);\n\n\tOBSOutputAutoRelease StreamingOutput()\n\t{\n\t\tconst std::lock_guard current_lock{current_mutex};\n\t\treturn current ? obs_output_get_ref(current->output_) : nullptr;\n\t}\n\n\tbool RestartOnError() { return restart_on_error; }\n\nprivate:\n\tstruct OBSOutputObjects {\n\t\tOBSOutputAutoRelease output_;\n\t\tstd::shared_ptr<obs_encoder_group_t> video_encoder_group_;\n\t\tstd::vector<OBSEncoderAutoRelease> audio_encoders_;\n\t\tOBSServiceAutoRelease multitrack_video_service_;\n\t\tOBSSignal start_signal, stop_signal;\n\t\tstd::vector<OBSCanvasAutoRelease> canvases;\n\t};\n\n\tstd::optional<OBSOutputObjects> take_current();\n\tstd::optional<OBSOutputObjects> take_current_stream_dump();\n\n\tstatic void ReleaseOnMainThread(std::optional<OBSOutputObjects> objects);\n\n\tstd::mutex current_mutex;\n\tstd::optional<OBSOutputObjects> current;\n\n\tstd::mutex current_stream_dump_mutex;\n\tstd::optional<OBSOutputObjects> current_stream_dump;\n\n\tbool restart_on_error = false;\n\tuint8_t reconnect_attempts = 0;\n\n\tfriend void StreamStartHandler(void *arg, calldata_t *data);\n\tfriend void StreamStopHandler(void *arg, calldata_t *data);\n\tfriend void RecordingStartHandler(void *arg, calldata_t *data);\n\tfriend void RecordingStopHandler(void *arg, calldata_t *);\n};\n"
  },
  {
    "path": "frontend/utility/NativeEventFilter.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"NativeEventFilter.hpp\"\n\nnamespace OBS {\n\nbool NativeEventFilter::nativeEventFilter(const QByteArray &, void *, qintptr *)\n{\n\t// Stub file for operating systems that do not need nativeEventFilter\n\treturn false;\n}\n} // namespace OBS\n"
  },
  {
    "path": "frontend/utility/NativeEventFilter.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QAbstractNativeEventFilter>\n\nnamespace OBS {\n\nclass NativeEventFilter : public QAbstractNativeEventFilter {\n\npublic:\n\tbool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result);\n};\n} // namespace OBS\n"
  },
  {
    "path": "frontend/utility/NativeEventFilter_Windows.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"NativeEventFilter.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include <sstream>\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n\nnamespace OBS {\n\nbool NativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)\n{\n\tif (eventType == \"windows_generic_MSG\") {\n\t\tMSG *msg = static_cast<MSG *>(message);\n\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tif (!main) {\n\t\t\treturn false;\n\t\t}\n\n\t\tswitch (msg->message) {\n\t\tcase WM_QUERYENDSESSION:\n\t\t\tmain->saveAll();\n\t\t\tif (msg->lParam == ENDSESSION_CRITICAL) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (main->shouldPromptForClose()) {\n\t\t\t\tif (result) {\n\t\t\t\t\t*result = FALSE;\n\t\t\t\t}\n\t\t\t\tQTimer::singleShot(1, main, &OBSBasic::close);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\tcase WM_ENDSESSION:\n\t\t\tif (msg->wParam == TRUE) {\n\t\t\t\t// Session is ending, start closing the main window now with no checks or prompts.\n\t\t\t\tmain->closeWindow();\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n} // namespace OBS\n"
  },
  {
    "path": "frontend/utility/OBSCanvas.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Dennis Sädtler <saedtler@twitch.tv>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSCanvas.hpp\"\n\n#include <utility>\n\nnamespace OBS {\n\nCanvas::Canvas(obs_canvas_t *canvas) : canvas(canvas) {}\n\nCanvas::Canvas(Canvas &&other) noexcept\n{\n\tcanvas = std::exchange(other.canvas, nullptr);\n}\n\nCanvas::~Canvas() noexcept\n{\n\tif (!canvas)\n\t\treturn;\n\n\tobs_canvas_remove(canvas);\n\tobs_canvas_release(canvas);\n\tcanvas = nullptr;\n}\n\nCanvas &Canvas::operator=(Canvas &&other) noexcept\n{\n\tcanvas = std::exchange(other.canvas, canvas);\n\n\treturn *this;\n}\n\nstd::optional<OBSDataAutoRelease> Canvas::Save() const\n{\n\tif (!canvas)\n\t\treturn std::nullopt;\n\tif (obs_data_t *saved = obs_save_canvas(canvas))\n\t\treturn saved;\n\n\treturn std::nullopt;\n}\n\nstd::unique_ptr<Canvas> Canvas::Load(obs_data_t *data)\n{\n\tif (OBSDataAutoRelease canvas_data = obs_data_get_obj(data, \"info\")) {\n\t\tif (obs_canvas_t *canvas = obs_load_canvas(canvas_data)) {\n\t\t\treturn std::make_unique<Canvas>(canvas);\n\t\t}\n\t}\n\n\treturn nullptr;\n}\n\nstd::vector<Canvas> Canvas::LoadCanvases(obs_data_array_t *canvases)\n{\n\tauto cb = [](obs_data_t *data, void *param) -> void {\n\t\tauto vec = static_cast<std::vector<Canvas> *>(param);\n\t\tif (auto canvas = Canvas::Load(data))\n\t\t\tvec->emplace_back(std::move(*canvas));\n\t};\n\n\tstd::vector<Canvas> ret;\n\tobs_data_array_enum(canvases, cb, &ret);\n\n\treturn ret;\n}\n\nOBSDataArrayAutoRelease Canvas::SaveCanvases(const std::vector<Canvas> &canvases)\n{\n\tOBSDataArrayAutoRelease savedCanvases = obs_data_array_create();\n\n\tfor (auto &canvas : canvases) {\n\t\tauto canvas_data = canvas.Save();\n\t\tif (!canvas_data)\n\t\t\tcontinue;\n\n\t\tOBSDataAutoRelease data = obs_data_create();\n\t\tobs_data_set_obj(data, \"info\", *canvas_data);\n\t\tobs_data_array_push_back(savedCanvases, data);\n\t}\n\n\treturn savedCanvases;\n}\n\n} // namespace OBS\n"
  },
  {
    "path": "frontend/utility/OBSCanvas.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Dennis Sädtler <saedtler@twitch.tv>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <vector>\n\n#include \"obs.h\"\n#include \"obs.hpp\"\n\nnamespace OBS {\nclass Canvas {\n\npublic:\n\tCanvas(obs_canvas_t *canvas);\n\tCanvas(Canvas &&other) noexcept;\n\n\t~Canvas() noexcept;\n\n\t// No default or copy/move constructors\n\tCanvas() = delete;\n\tCanvas(Canvas &other) = delete;\n\n\tCanvas &operator=(Canvas &&other) noexcept;\n\n\toperator obs_canvas_t *() const { return canvas; }\n\n\t[[nodiscard]] std::optional<OBSDataAutoRelease> Save() const;\n\tstatic std::unique_ptr<Canvas> Load(obs_data_t *data);\n\tstatic std::vector<Canvas> LoadCanvases(obs_data_array_t *canvases);\n\tstatic OBSDataArrayAutoRelease SaveCanvases(const std::vector<Canvas> &canvases);\n\nprivate:\n\tobs_canvas_t *canvas = nullptr;\n};\n} // namespace OBS\n"
  },
  {
    "path": "frontend/utility/OBSEventFilter.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QEvent>\n#include <QObject>\n\ntypedef std::function<bool(QObject *, QEvent *)> EventFilterFunc;\n\nclass OBSEventFilter : public QObject {\n\tQ_OBJECT\npublic:\n\tOBSEventFilter(EventFilterFunc filter_) : filter(filter_) {}\n\nprotected:\n\tbool eventFilter(QObject *obj, QEvent *event) { return filter(obj, event); }\n\npublic:\n\tEventFilterFunc filter;\n};\n"
  },
  {
    "path": "frontend/utility/OBSProxyStyle.cpp",
    "content": "#include \"OBSProxyStyle.hpp\"\n#include \"moc_OBSProxyStyle.cpp\"\n\nint OBSProxyStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,\n\t\t\t     QStyleHintReturn *returnData) const\n{\n\tif (hint == SH_ComboBox_AllowWheelScrolling)\n\t\treturn 0;\n#ifdef __APPLE__\n\tif (hint == SH_ComboBox_UseNativePopup)\n\t\treturn 1;\n#endif\n\n\treturn QProxyStyle::styleHint(hint, option, widget, returnData);\n}\n\nint OBSInvisibleCursorProxyStyle::pixelMetric(PixelMetric metric, const QStyleOption *option,\n\t\t\t\t\t      const QWidget *widget) const\n{\n\n\tif (metric == PM_TextCursorWidth)\n\t\treturn 0;\n\n\treturn QProxyStyle::pixelMetric(metric, option, widget);\n}\n"
  },
  {
    "path": "frontend/utility/OBSProxyStyle.hpp",
    "content": "#pragma once\n\n#include <QProxyStyle>\n\nclass OBSProxyStyle : public QProxyStyle {\n\tQ_OBJECT\n\npublic:\n\tOBSProxyStyle() : QProxyStyle() {}\n\n\tOBSProxyStyle(const QString &key) : QProxyStyle(key) {}\n\n\tint styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget,\n\t\t      QStyleHintReturn *returnData) const override;\n};\n\nclass OBSInvisibleCursorProxyStyle : public OBSProxyStyle {\n\tQ_OBJECT\n\npublic:\n\tOBSInvisibleCursorProxyStyle() : OBSProxyStyle() {}\n\n\tint pixelMetric(PixelMetric pm, const QStyleOption *option, const QWidget *widget) const override;\n};\n"
  },
  {
    "path": "frontend/utility/OBSSparkle.hpp",
    "content": "#pragma once\n\n#import <QObject>\n\nclass QAction;\n#ifdef __OBJC__\n@class OBSUpdateDelegate;\n#endif\n\nclass OBSSparkle : public QObject {\n\tQ_OBJECT\n\npublic:\n\tOBSSparkle(const char *branch, QAction *checkForUpdatesAction);\n\tvoid setBranch(const char *branch);\n\tvoid checkForUpdates(bool manualCheck);\n\nprivate:\n#ifdef __OBJC__\n\tOBSUpdateDelegate *updaterDelegate;\n#else\n\tvoid *updaterDelegate;\n#endif\n};\n"
  },
  {
    "path": "frontend/utility/OBSSparkle.mm",
    "content": "#import \"OBSSparkle.hpp\"\n#import \"OBSUpdateDelegate.h\"\n\nOBSSparkle::OBSSparkle(const char *branch, QAction *checkForUpdatesAction)\n{\n    @autoreleasepool {\n        updaterDelegate = [[OBSUpdateDelegate alloc] init];\n        updaterDelegate.branch = [NSString stringWithUTF8String:branch];\n        updaterDelegate.updaterController =\n            [[SPUStandardUpdaterController alloc] initWithStartingUpdater:YES updaterDelegate:updaterDelegate\n                                                       userDriverDelegate:nil];\n        [updaterDelegate observeCanCheckForUpdatesWithAction:checkForUpdatesAction];\n    }\n}\n\nvoid OBSSparkle::setBranch(const char *branch)\n{\n    updaterDelegate.branch = [NSString stringWithUTF8String:branch];\n}\n\nvoid OBSSparkle::checkForUpdates(bool manualCheck)\n{\n    @autoreleasepool {\n        if (manualCheck) {\n            [updaterDelegate.updaterController checkForUpdates:nil];\n        } else {\n            [updaterDelegate.updaterController.updater checkForUpdatesInBackground];\n        }\n    }\n}\n"
  },
  {
    "path": "frontend/utility/OBSTheme.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QString>\n#include <QStringList>\n\n#include <filesystem>\n\nstruct OBSTheme {\n\t/* internal name, must be unique */\n\tQString id;\n\tQString name;\n\tQString author;\n\tQString extends;\n\n\t/* First ancestor base theme */\n\tQString parent;\n\t/* Dependencies from root to direct ancestor */\n\tQStringList dependencies;\n\t/* File path */\n\tstd::filesystem::path location;\n\tstd::filesystem::path filename; /* Filename without extension */\n\n\tbool isDark;\n\tbool isVisible;      /* Whether it should be shown to the user */\n\tbool isBaseTheme;    /* Whether it is a \"style\" or variant */\n\tbool isHighContrast; /* Whether it is a high-contrast adjustment layer */\n\n\tbool usesFontScale = false; /* Whether the generated QSS uses the font scale option */\n\tbool usesDensity = false;   /* Whether the generated QSS uses the density option */\n};\n"
  },
  {
    "path": "frontend/utility/OBSThemeVariable.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Dennis Sädtler <dennis@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QString>\n#include <QVariant>\n\nstruct OBSThemeVariable {\n\tenum VariableType {\n\t\tColor,  /* RGB color value*/\n\t\tSize,   /* Number with suffix denoting size (e.g. px, pt, em) */\n\t\tNumber, /* Number without suffix */\n\t\tString, /* Raw string (e.g. color name, border style, etc.) */\n\t\tAlias,  /* Points at another variable, value will be the key */\n\t\tCalc,   /* Simple calculation with two operands */\n\t\tMin,    /* Get the smallest of two Size or Number */\n\t\tMax,    /* Get the largest of two Size or Number */\n\t};\n\n\t/* Whether the variable should be editable in the UI */\n\tbool editable = false;\n\t/* Used for VariableType::Size only */\n\tQString suffix;\n\n\tVariableType type;\n\tQString name;\n\tQVariant value;\n\tQVariant userValue; /* If overwritten by user, use this value instead */\n};\n"
  },
  {
    "path": "frontend/utility/OBSTranslator.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSTranslator.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include \"moc_OBSTranslator.cpp\"\n\nQString OBSTranslator::translate(const char *, const char *sourceText, const char *, int) const\n{\n\tconst char *out = nullptr;\n\tQString str(sourceText);\n\tstr.replace(\" \", \"\");\n\tif (!App()->TranslateString(QT_TO_UTF8(str), &out))\n\t\treturn QString(sourceText);\n\n\treturn QT_UTF8(out);\n}\n"
  },
  {
    "path": "frontend/utility/OBSTranslator.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QString>\n#include <QTranslator>\n\nclass OBSTranslator : public QTranslator {\n\tQ_OBJECT\n\npublic:\n\tvirtual bool isEmpty() const override { return false; }\n\n\tvirtual QString translate(const char *context, const char *sourceText, const char *disambiguation,\n\t\t\t\t  int n) const override;\n};\n"
  },
  {
    "path": "frontend/utility/OBSUpdateDelegate.h",
    "content": "/******************************************************************************\n Copyright (C) 2024 by Patrick Heyer <opensource@patrickheyer.com>\n \n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2 of the License, or\n (at your option) any later version.\n \n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n \n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>.\n ******************************************************************************/\n\n#pragma once\n\n#import <Foundation/Foundation.h>\n#import <Sparkle/Sparkle.h>\n\n#import <QAction>\n\n@interface OBSUpdateDelegate : NSObject <SPUUpdaterDelegate> {\n}\n@property (copy) NSString *_Nonnull branch;\n@property (nonatomic) SPUStandardUpdaterController *_Nonnull updaterController;\n\n- (nonnull NSSet<NSString *> *)allowedChannelsForUpdater:(nonnull SPUUpdater *)updater;\n- (void)observeCanCheckForUpdatesWithAction:(nonnull QAction *)action;\n- (void)observeValueForKeyPath:(NSString *_Nullable)keyPath\n                      ofObject:(id _Nullable)object\n                        change:(NSDictionary<NSKeyValueChangeKey, id> *_Nullable)change\n                       context:(void *_Nullable)context;\n\n@end\n"
  },
  {
    "path": "frontend/utility/OBSUpdateDelegate.mm",
    "content": "#import \"OBSUpdateDelegate.h\"\n\n@implementation OBSUpdateDelegate {\n}\n\n@synthesize branch;\n\n- (nonnull NSSet<NSString *> *)allowedChannelsForUpdater:(nonnull SPUUpdater *)updater\n{\n    return [NSSet setWithObject:branch];\n}\n\n- (void)observeCanCheckForUpdatesWithAction:(nonnull QAction *)action;\n{\n    [_updaterController.updater addObserver:self forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates))\n                                    options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew)\n                                    context:(void *) action];\n}\n\n- (void)observeValueForKeyPath:(NSString *)keyPath\n                      ofObject:(id)object\n                        change:(NSDictionary<NSKeyValueChangeKey, id> *)change\n                       context:(void *)context\n{\n    if ([keyPath isEqualToString:NSStringFromSelector(@selector(canCheckForUpdates))]) {\n        QAction *menuAction = (QAction *) context;\n        menuAction->setEnabled(_updaterController.updater.canCheckForUpdates);\n    } else {\n        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];\n    }\n}\n\n- (void)dealloc\n{\n    @autoreleasepool {\n        [_updaterController.updater removeObserver:self forKeyPath:NSStringFromSelector(@selector(canCheckForUpdates))];\n    }\n}\n\n@end\n"
  },
  {
    "path": "frontend/utility/PreviewProgramSizeObserver.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"PreviewProgramSizeObserver.hpp\"\n\n#include <QEvent>\n#include <QLayout>\n#include <QResizeEvent>\n#include <QTimer>\n\n#include <util/base.h>\n\nPreviewProgramSizeObserver::PreviewProgramSizeObserver(QWidget *widgetLeft, QWidget *widgetRight, QObject *parent)\n\t: QObject(parent),\n\t  left(widgetLeft),\n\t  right(widgetRight)\n{\n\tif (!left || !right) {\n\t\treturn;\n\t}\n\n\tstd::pair<QWidget *, QWidget *> siblingParents = findSiblingParents(left, right);\n\tleftContainer = siblingParents.first;\n\trightContainer = siblingParents.second;\n\n\tleftOriginalMaxSize = leftContainer->maximumSize();\n\trightOriginalMaxSize = rightContainer->maximumSize();\n\n\tQWidget *sharedParent = leftContainer->parentWidget();\n\n\tif (!sharedParent) {\n\t\treturn;\n\t}\n\tancestorContainer = sharedParent;\n\n\tQLayout *ancestorLayout = ancestorContainer->layout();\n\tif (QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(ancestorLayout)) {\n\t\tsetOrientation((boxLayout->direction() == QBoxLayout::LeftToRight) ? Qt::Horizontal : Qt::Vertical);\n\t}\n\n\tleftTargetSize = left->width();\n\trightTargetSize = right->width();\n\n\tancestorContainer->installEventFilter(this);\n\n\tconnect(ancestorContainer, &QWidget::destroyed, this, &QWidget::deleteLater);\n\tconnect(left, &QWidget::destroyed, this, &QWidget::deleteLater);\n\tconnect(right, &QWidget::destroyed, this, &QWidget::deleteLater);\n\tconnect(leftContainer, &QWidget::destroyed, this, &QWidget::deleteLater);\n\tconnect(rightContainer, &QWidget::destroyed, this, &QWidget::deleteLater);\n}\n\nPreviewProgramSizeObserver::~PreviewProgramSizeObserver()\n{\n\tif (ancestorContainer) {\n\t\tancestorContainer->removeEventFilter(this);\n\t}\n\tif (leftContainer) {\n\t\tleftContainer->setMaximumSize(leftOriginalMaxSize);\n\t}\n\n\tif (rightContainer) {\n\t\trightContainer->setMaximumSize(rightOriginalMaxSize);\n\t}\n}\n\nvoid PreviewProgramSizeObserver::setOrientation(Qt::Orientation orientation_)\n{\n\torientation = orientation_;\n}\n\nstd::pair<QWidget *, QWidget *> PreviewProgramSizeObserver::findSiblingParents(QWidget *a, QWidget *b)\n{\n\t// Search through ancestors of two widgets to find the topmost pair that are siblings\n\tQWidget *ancestor1 = a;\n\tQWidget *ancestor2 = b;\n\n\twhile (ancestor1 && ancestor2) {\n\t\tQWidget *parent1 = ancestor1->parentWidget();\n\t\tQWidget *parent2 = ancestor2->parentWidget();\n\n\t\tif (!parent1 || !parent2) {\n\t\t\tbreak;\n\t\t}\n\n\t\t// Found sibling containers\n\t\tif (parent1 == parent2) {\n\t\t\treturn {ancestor1, ancestor2};\n\t\t}\n\n\t\tif (parent1->isAncestorOf(parent2)) {\n\t\t\tancestor2 = parent2;\n\t\t} else if (parent2->isAncestorOf(parent1)) {\n\t\t\tancestor1 = parent1;\n\t\t} else {\n\t\t\tancestor1 = parent1;\n\t\t\tancestor2 = parent2;\n\t\t}\n\t}\n\n\treturn {a, b};\n}\n\nvoid PreviewProgramSizeObserver::syncContainerSizes(int containerSizeDelta)\n{\n\tauto setMax = (orientation == Qt::Horizontal) ? [](QWidget* widget, int value) {\n\t\twidget->setMaximumWidth(value);\n\t\t} : [](QWidget* widget, int value) {\n\t\twidget->setMaximumHeight(value);\n\t\t};\n\n\tQLayout *ancestorLayout = ancestorContainer->layout();\n\tif (QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(ancestorLayout)) {\n\t\tsetOrientation((boxLayout->direction() == QBoxLayout::LeftToRight) ? Qt::Horizontal : Qt::Vertical);\n\t}\n\n\tif (orientation == Qt::Horizontal) {\n\t\tleftContainer->setMaximumHeight(leftOriginalMaxSize.height());\n\t\trightContainer->setMaximumHeight(rightOriginalMaxSize.height());\n\t} else {\n\t\tleftContainer->setMaximumWidth(leftOriginalMaxSize.width());\n\t\trightContainer->setMaximumWidth(rightOriginalMaxSize.width());\n\t}\n\n\tint leftInner = (orientation == Qt::Horizontal) ? left->width() : left->height();\n\tint rightInner = (orientation == Qt::Horizontal) ? right->width() : right->height();\n\n\tint leftOuter = (orientation == Qt::Horizontal) ? leftContainer->width() : leftContainer->height();\n\tint rightOuter = (orientation == Qt::Horizontal) ? rightContainer->width() : rightContainer->height();\n\n\tint totalOuter = leftOuter + rightOuter;\n\tif (containerSizeDelta >= 0) {\n\t\ttotalOuter += containerSizeDelta;\n\t}\n\n\tint leftOffset = leftOuter - leftInner;\n\tint rightOffset = rightOuter - rightInner;\n\n\tint targetInner = (totalOuter - leftOffset - rightOffset) / 2;\n\n\tleftTargetSize = targetInner + leftOffset;\n\trightTargetSize = targetInner + rightOffset;\n\n\tif (containerSizeDelta >= 0) {\n\t\tsetMax(leftContainer, leftTargetSize);\n\t\tsetMax(rightContainer, rightTargetSize);\n\t} else {\n\t\t// Container shrunk, only set max size on larger widget\n\t\tif (leftInner > rightInner) {\n\t\t\tsetMax(leftContainer, leftTargetSize);\n\t\t\tsetMax(rightContainer, QWIDGETSIZE_MAX);\n\t\t} else {\n\t\t\tsetMax(leftContainer, QWIDGETSIZE_MAX);\n\t\t\tsetMax(rightContainer, rightTargetSize);\n\t\t}\n\n\t\t// Force a second recalculation\n\t\tQTimer::singleShot(1, this, [&, setMax]() {\n\t\t\tif (updating) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tupdating = true;\n\n\t\t\tsetMax(leftContainer, leftTargetSize);\n\t\t\tsetMax(rightContainer, rightTargetSize);\n\n\t\t\tupdating = false;\n\t\t});\n\t}\n}\n\nbool PreviewProgramSizeObserver::eventFilter(QObject *target, QEvent *event)\n{\n\tif (event->type() != QEvent::Resize && event->type() != QEvent::LayoutRequest) {\n\t\treturn QObject::eventFilter(target, event);\n\t}\n\n\tif (!left || !right || !leftContainer || !rightContainer) {\n\t\tdeleteLater();\n\t\treturn QObject::eventFilter(target, event);\n\t}\n\n\tif (updating == true) {\n\t\treturn QObject::eventFilter(target, event);\n\t}\n\n\tupdating = true;\n\n\tif (event->type() == QEvent::LayoutRequest) {\n\t\tsyncContainerSizes(0);\n\t} else if (event->type() == QEvent::Resize) {\n\t\tQResizeEvent *resizeEvent = static_cast<QResizeEvent *>(event);\n\t\tint newSize = (orientation == Qt::Horizontal) ? resizeEvent->size().width()\n\t\t\t\t\t\t\t      : resizeEvent->size().height();\n\t\tint oldSize = (orientation == Qt::Horizontal) ? resizeEvent->oldSize().width()\n\t\t\t\t\t\t\t      : resizeEvent->oldSize().height();\n\n\t\tif (newSize - oldSize != 0) {\n\t\t\tsyncContainerSizes(newSize - oldSize);\n\t\t}\n\t}\n\n\tupdating = false;\n\n\treturn QObject::eventFilter(target, event);\n}\n"
  },
  {
    "path": "frontend/utility/PreviewProgramSizeObserver.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QObject>\n#include <QPointer>\n#include <QWidget>\n\nclass PreviewProgramSizeObserver : public QObject {\n\tQ_OBJECT\n\n\tQPointer<QWidget> left;\n\tQPointer<QWidget> right;\n\n\tQPointer<QWidget> leftContainer;\n\tQPointer<QWidget> rightContainer;\n\n\tQSize leftOriginalMaxSize;\n\tQSize rightOriginalMaxSize;\n\n\tint leftTargetSize{};\n\tint rightTargetSize{};\n\n\tQPointer<QWidget> ancestorContainer;\n\n\tbool updating = false;\n\n\tQt::Orientation orientation = Qt::Horizontal;\n\n\tstd::pair<QWidget *, QWidget *> findSiblingParents(QWidget *a, QWidget *b);\n\n\tvoid syncContainerSizes(int containerSizeDelta);\n\npublic:\n\tPreviewProgramSizeObserver(QWidget *widgetLeft, QWidget *widgetRight, QObject *parent = nullptr);\n\t~PreviewProgramSizeObserver();\n\n\tvoid setOrientation(Qt::Orientation orientation_);\n\nprotected:\n\tbool eventFilter(QObject *target, QEvent *event) override;\n};\n"
  },
  {
    "path": "frontend/utility/QuickTransition.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"QuickTransition.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <qt-wrappers.hpp>\n\nstatic inline QString MakeQuickTransitionText(QuickTransition *qt)\n{\n\tQString name;\n\n\tif (!qt->fadeToBlack)\n\t\tname = QT_UTF8(obs_source_get_name(qt->source));\n\telse\n\t\tname = QTStr(\"FadeToBlack\");\n\n\tif (!obs_transition_fixed(qt->source))\n\t\tname += QString(\" (%1ms)\").arg(QString::number(qt->duration));\n\treturn name;\n}\n\nvoid QuickTransition::SourceRenamed(void *param, calldata_t *)\n{\n\tQuickTransition *qt = static_cast<QuickTransition *>(param);\n\n\tQString hotkeyName = QTStr(\"QuickTransitions.HotkeyName\").arg(MakeQuickTransitionText(qt));\n\n\tobs_hotkey_set_description(qt->hotkey, QT_TO_UTF8(hotkeyName));\n}\n"
  },
  {
    "path": "frontend/utility/QuickTransition.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <obs.hpp>\n\n#include <QMetaType>\n\nclass QPushButton;\n\nstruct QuickTransition {\n\tQPushButton *button = nullptr;\n\tOBSSource source;\n\tobs_hotkey_id hotkey = OBS_INVALID_HOTKEY_ID;\n\tint duration = 0;\n\tint id = 0;\n\tbool fadeToBlack = false;\n\n\tinline QuickTransition() {}\n\tinline QuickTransition(OBSSource source_, int duration_, int id_, bool fadeToBlack_ = false)\n\t\t: source(source_),\n\t\t  duration(duration_),\n\t\t  id(id_),\n\t\t  fadeToBlack(fadeToBlack_),\n\t\t  renamedSignal(std::make_shared<OBSSignal>(obs_source_get_signal_handler(source), \"rename\",\n\t\t\t\t\t\t\t    SourceRenamed, this))\n\t{\n\t}\n\nprivate:\n\tstatic void SourceRenamed(void *param, calldata_t *data);\n\tstd::shared_ptr<OBSSignal> renamedSignal;\n};\n"
  },
  {
    "path": "frontend/utility/RemoteTextThread.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"RemoteTextThread.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <qt-wrappers.hpp>\n#include <util/curl/curl-helper.h>\n\n#include \"moc_RemoteTextThread.cpp\"\n\nusing namespace std;\n\nstatic auto curl_deleter = [](CURL *curl) {\n\tcurl_easy_cleanup(curl);\n};\n\nusing Curl = unique_ptr<CURL, decltype(curl_deleter)>;\n\nstatic size_t string_write(char *ptr, size_t size, size_t nmemb, string &str)\n{\n\tsize_t total = size * nmemb;\n\tif (total)\n\t\tstr.append(ptr, total);\n\n\treturn total;\n}\n\nvoid RemoteTextThread::run()\n{\n\tchar error[CURL_ERROR_SIZE];\n\tCURLcode code;\n\n\tstring versionString(\"User-Agent: obs-basic \");\n\tversionString += App()->GetVersionString();\n\n\tstring contentTypeString;\n\tif (!contentType.empty()) {\n\t\tcontentTypeString += \"Content-Type: \";\n\t\tcontentTypeString += contentType;\n\t}\n\n\tCurl curl{curl_easy_init(), curl_deleter};\n\tif (curl) {\n\t\tstruct curl_slist *header = nullptr;\n\t\tstring str;\n\n\t\theader = curl_slist_append(header, versionString.c_str());\n\n\t\tif (!contentTypeString.empty()) {\n\t\t\theader = curl_slist_append(header, contentTypeString.c_str());\n\t\t}\n\n\t\tfor (std::string &h : extraHeaders)\n\t\t\theader = curl_slist_append(header, h.c_str());\n\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_ACCEPT_ENCODING, \"\");\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header);\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, error);\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L);\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, string_write);\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str);\n\t\tcurl_obs_set_revoke_setting(curl.get());\n\n\t\tif (timeoutSec)\n\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, timeoutSec);\n\n\t\tif (!postData.empty()) {\n\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, postData.c_str());\n\t\t}\n\n\t\tcode = curl_easy_perform(curl.get());\n\t\tif (code != CURLE_OK) {\n\t\t\tblog(LOG_WARNING, \"RemoteTextThread: HTTP request failed. %s\",\n\t\t\t     strlen(error) ? error : curl_easy_strerror(code));\n\t\t\temit Result(QString(), QT_UTF8(error));\n\t\t} else {\n\t\t\temit Result(QT_UTF8(str.c_str()), QString());\n\t\t}\n\n\t\tcurl_slist_free_all(header);\n\t}\n}\n\nstatic size_t header_write(char *ptr, size_t size, size_t nmemb, vector<string> &list)\n{\n\tstring str;\n\n\tsize_t total = size * nmemb;\n\tif (total)\n\t\tstr.append(ptr, total);\n\n\tif (str.back() == '\\n')\n\t\tstr.resize(str.size() - 1);\n\tif (str.back() == '\\r')\n\t\tstr.resize(str.size() - 1);\n\n\tlist.push_back(std::move(str));\n\treturn total;\n}\n\nbool GetRemoteFile(const char *url, std::string &str, std::string &error, long *responseCode, const char *contentType,\n\t\t   std::string request_type, const char *postData, std::vector<std::string> extraHeaders,\n\t\t   std::string *signature, int timeoutSec, bool fail_on_error, int postDataSize)\n{\n\tvector<string> header_in_list;\n\tchar error_in[CURL_ERROR_SIZE];\n\tCURLcode code = CURLE_FAILED_INIT;\n\n\terror_in[0] = 0;\n\n\tstring versionString(\"User-Agent: obs-basic \");\n\tversionString += App()->GetVersionString();\n\n\tstring contentTypeString;\n\tif (contentType) {\n\t\tcontentTypeString += \"Content-Type: \";\n\t\tcontentTypeString += contentType;\n\t}\n\n\tCurl curl{curl_easy_init(), curl_deleter};\n\tif (curl) {\n\t\tstruct curl_slist *header = nullptr;\n\n\t\theader = curl_slist_append(header, versionString.c_str());\n\n\t\tif (!contentTypeString.empty()) {\n\t\t\theader = curl_slist_append(header, contentTypeString.c_str());\n\t\t}\n\n\t\tfor (std::string &h : extraHeaders)\n\t\t\theader = curl_slist_append(header, h.c_str());\n\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_URL, url);\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_ACCEPT_ENCODING, \"\");\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header);\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_ERRORBUFFER, error_in);\n\t\tif (fail_on_error)\n\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_FAILONERROR, 1L);\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, string_write);\n\t\tcurl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &str);\n\t\tcurl_obs_set_revoke_setting(curl.get());\n\n\t\tif (signature) {\n\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_HEADERFUNCTION, header_write);\n\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_HEADERDATA, &header_in_list);\n\t\t}\n\n\t\tif (timeoutSec)\n\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, timeoutSec);\n\n\t\tif (!request_type.empty()) {\n\t\t\tif (request_type != \"GET\")\n\t\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST, request_type.c_str());\n\n\t\t\t// Special case of \"POST\"\n\t\t\tif (request_type == \"POST\") {\n\t\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_POST, 1);\n\t\t\t\tif (!postData)\n\t\t\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, \"{}\");\n\t\t\t}\n\t\t}\n\t\tif (postData) {\n\t\t\tif (postDataSize > 0) {\n\t\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, (long)postDataSize);\n\t\t\t}\n\t\t\tcurl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, postData);\n\t\t}\n\n\t\tcode = curl_easy_perform(curl.get());\n\t\tif (responseCode)\n\t\t\tcurl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, responseCode);\n\n\t\tif (code != CURLE_OK) {\n\t\t\terror = strlen(error_in) ? error_in : curl_easy_strerror(code);\n\t\t} else if (signature) {\n\t\t\tfor (string &h : header_in_list) {\n\t\t\t\tstring name = h.substr(0, 13);\n\t\t\t\t// HTTP headers are technically case-insensitive\n\t\t\t\tif (name == \"X-Signature: \" || name == \"x-signature: \") {\n\t\t\t\t\t*signature = h.substr(13);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tcurl_slist_free_all(header);\n\t}\n\n\treturn code == CURLE_OK;\n}\n"
  },
  {
    "path": "frontend/utility/RemoteTextThread.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QThread>\n\nclass RemoteTextThread : public QThread {\n\tQ_OBJECT\n\n\tstd::string url;\n\tstd::string contentType;\n\tstd::string postData;\n\n\tstd::vector<std::string> extraHeaders;\n\n\tint timeoutSec = 0;\n\n\tvoid run() override;\n\nsignals:\n\tvoid Result(const QString &text, const QString &error);\n\npublic:\n\tinline RemoteTextThread(std::string url_, std::string contentType_ = std::string(),\n\t\t\t\tstd::string postData_ = std::string(), int timeoutSec_ = 0)\n\t\t: url(url_),\n\t\t  contentType(contentType_),\n\t\t  postData(postData_),\n\t\t  timeoutSec(timeoutSec_)\n\t{\n\t}\n\n\tinline RemoteTextThread(std::string url_, std::vector<std::string> &&extraHeaders_,\n\t\t\t\tstd::string contentType_ = std::string(), std::string postData_ = std::string(),\n\t\t\t\tint timeoutSec_ = 0)\n\t\t: url(url_),\n\t\t  contentType(contentType_),\n\t\t  postData(postData_),\n\t\t  extraHeaders(std::move(extraHeaders_)),\n\t\t  timeoutSec(timeoutSec_)\n\t{\n\t}\n};\n\nbool GetRemoteFile(const char *url, std::string &str, std::string &error, long *responseCode = nullptr,\n\t\t   const char *contentType = nullptr, std::string request_type = \"\", const char *postData = nullptr,\n\t\t   std::vector<std::string> extraHeaders = std::vector<std::string>(), std::string *signature = nullptr,\n\t\t   int timeoutSec = 0, bool fail_on_error = true, int postDataSize = 0);\n"
  },
  {
    "path": "frontend/utility/RemuxEntryPathItemDelegate.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"RemuxEntryPathItemDelegate.hpp\"\n#include \"RemuxQueueModel.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QHBoxLayout>\n#include <QLineEdit>\n#include <QToolButton>\n\n#include \"moc_RemuxEntryPathItemDelegate.cpp\"\n\nRemuxEntryPathItemDelegate::RemuxEntryPathItemDelegate(bool isOutput, const QString &defaultPath)\n\t: QStyledItemDelegate(),\n\t  isOutput(isOutput),\n\t  defaultPath(defaultPath)\n{\n}\n\nQWidget *RemuxEntryPathItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */,\n\t\t\t\t\t\t  const QModelIndex &index) const\n{\n\tRemuxEntryState state = index.model()\n\t\t\t\t\t->index(index.row(), RemuxEntryColumn::State)\n\t\t\t\t\t.data(RemuxEntryRole::EntryStateRole)\n\t\t\t\t\t.value<RemuxEntryState>();\n\tif (state == RemuxEntryState::Pending || state == RemuxEntryState::InProgress) {\n\t\t// Never allow modification of rows that are\n\t\t// in progress.\n\t\treturn Q_NULLPTR;\n\t} else if (isOutput && state != RemuxEntryState::Ready) {\n\t\t// Do not allow modification of output rows\n\t\t// that aren't associated with a valid input.\n\t\treturn Q_NULLPTR;\n\t} else if (!isOutput && state == RemuxEntryState::Complete) {\n\t\t// Don't allow modification of rows that are\n\t\t// already complete.\n\t\treturn Q_NULLPTR;\n\t} else {\n\t\tQSizePolicy buttonSizePolicy(QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Expanding,\n\t\t\t\t\t     QSizePolicy::ControlType::PushButton);\n\n\t\tQWidget *container = new QWidget(parent);\n\n\t\tauto browseCallback = [this, container]() {\n\t\t\tconst_cast<RemuxEntryPathItemDelegate *>(this)->handleBrowse(container);\n\t\t};\n\n\t\tauto clearCallback = [this, container]() {\n\t\t\tconst_cast<RemuxEntryPathItemDelegate *>(this)->handleClear(container);\n\t\t};\n\n\t\tQHBoxLayout *layout = new QHBoxLayout();\n\t\tlayout->setContentsMargins(0, 0, 0, 0);\n\t\tlayout->setSpacing(0);\n\n\t\tQLineEdit *text = new QLineEdit();\n\t\ttext->setObjectName(QStringLiteral(\"text\"));\n\t\ttext->setSizePolicy(QSizePolicy(QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Expanding,\n\t\t\t\t\t\tQSizePolicy::ControlType::LineEdit));\n\t\tlayout->addWidget(text);\n\n\t\tQObject::connect(text, &QLineEdit::editingFinished, this, &RemuxEntryPathItemDelegate::updateText);\n\n\t\tQToolButton *browseButton = new QToolButton();\n\t\tbrowseButton->setText(\"...\");\n\t\tbrowseButton->setSizePolicy(buttonSizePolicy);\n\t\tlayout->addWidget(browseButton);\n\n\t\tconnect(browseButton, &QToolButton::clicked, this, browseCallback);\n\n\t\t// The \"clear\" button is not shown in output cells\n\t\t// or the insertion point's input cell.\n\t\tif (!isOutput && state != RemuxEntryState::Empty) {\n\t\t\tQToolButton *clearButton = new QToolButton();\n\t\t\tclearButton->setText(\"X\");\n\t\t\tclearButton->setSizePolicy(buttonSizePolicy);\n\t\t\tlayout->addWidget(clearButton);\n\n\t\t\tconnect(clearButton, &QToolButton::clicked, this, clearCallback);\n\t\t}\n\n\t\tcontainer->setLayout(layout);\n\t\tcontainer->setFocusProxy(text);\n\t\treturn container;\n\t}\n}\n\nvoid RemuxEntryPathItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const\n{\n\tQLineEdit *text = editor->findChild<QLineEdit *>();\n\ttext->setText(index.data().toString());\n\teditor->setProperty(PATH_LIST_PROP, QVariant());\n}\n\nvoid RemuxEntryPathItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,\n\t\t\t\t\t      const QModelIndex &index) const\n{\n\t// We use the PATH_LIST_PROP property to pass a list of\n\t// path strings from the editor widget into the model's\n\t// NewPathsToProcessRole. This is only used when paths\n\t// are selected through the \"browse\" or \"delete\" buttons\n\t// in the editor. If the user enters new text in the\n\t// text box, we simply pass that text on to the model\n\t// as normal text data in the default role.\n\tQVariant pathListProp = editor->property(PATH_LIST_PROP);\n\tif (pathListProp.isValid()) {\n\t\tQStringList list = editor->property(PATH_LIST_PROP).toStringList();\n\t\tif (isOutput) {\n\t\t\tif (list.size() > 0)\n\t\t\t\tmodel->setData(index, list);\n\t\t} else\n\t\t\tmodel->setData(index, list, RemuxEntryRole::NewPathsToProcessRole);\n\t} else {\n\t\tQLineEdit *lineEdit = editor->findChild<QLineEdit *>();\n\t\tmodel->setData(index, lineEdit->text());\n\t}\n}\n\nvoid RemuxEntryPathItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,\n\t\t\t\t       const QModelIndex &index) const\n{\n\tRemuxEntryState state = index.model()\n\t\t\t\t\t->index(index.row(), RemuxEntryColumn::State)\n\t\t\t\t\t.data(RemuxEntryRole::EntryStateRole)\n\t\t\t\t\t.value<RemuxEntryState>();\n\n\tQStyleOptionViewItem localOption = option;\n\tinitStyleOption(&localOption, index);\n\n\tif (isOutput) {\n\t\tif (state != Ready) {\n\t\t\tQColor background =\n\t\t\t\tlocalOption.palette.color(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Window);\n\n\t\t\tlocalOption.backgroundBrush = QBrush(background);\n\t\t}\n\t}\n\n\tQApplication::style()->drawControl(QStyle::CE_ItemViewItem, &localOption, painter);\n}\n\nvoid RemuxEntryPathItemDelegate::handleBrowse(QWidget *container)\n{\n\tQString ExtensionPattern = \"(*.mp4 *.flv *.mov *.mkv *.ts *.m3u8)\";\n\n\tQLineEdit *text = container->findChild<QLineEdit *>();\n\n\tQString currentPath = text->text();\n\tif (currentPath.isEmpty())\n\t\tcurrentPath = defaultPath;\n\n\tbool isSet = false;\n\tif (isOutput) {\n\t\tQString newPath = SaveFile(container, QTStr(\"Remux.SelectTarget\"), currentPath, ExtensionPattern);\n\n\t\tif (!newPath.isEmpty()) {\n\t\t\tcontainer->setProperty(PATH_LIST_PROP, QStringList() << newPath);\n\t\t\tisSet = true;\n\t\t}\n\t} else {\n\t\tQStringList paths = OpenFiles(container, QTStr(\"Remux.SelectRecording\"), currentPath,\n\t\t\t\t\t      QTStr(\"Remux.OBSRecording\") + QString(\" \") + ExtensionPattern);\n\n\t\tif (!paths.empty()) {\n\t\t\tcontainer->setProperty(PATH_LIST_PROP, paths);\n\t\t\tisSet = true;\n\t\t}\n#ifdef __APPLE__\n\t\t// TODO: Revisit when QTBUG-42661 is fixed\n\t\tcontainer->window()->raise();\n#endif\n\t}\n\n\tif (isSet)\n\t\temit commitData(container);\n}\n\nvoid RemuxEntryPathItemDelegate::handleClear(QWidget *container)\n{\n\t// An empty string list will indicate that the entry is being\n\t// blanked and should be deleted.\n\tcontainer->setProperty(PATH_LIST_PROP, QStringList());\n\n\temit commitData(container);\n}\n\nvoid RemuxEntryPathItemDelegate::updateText()\n{\n\tQLineEdit *lineEdit = dynamic_cast<QLineEdit *>(sender());\n\tQWidget *editor = lineEdit->parentWidget();\n\temit commitData(editor);\n}\n"
  },
  {
    "path": "frontend/utility/RemuxEntryPathItemDelegate.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QStyledItemDelegate>\n\nclass RemuxEntryPathItemDelegate : public QStyledItemDelegate {\n\tQ_OBJECT\n\npublic:\n\tRemuxEntryPathItemDelegate(bool isOutput, const QString &defaultPath);\n\n\tvirtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem & /* option */,\n\t\t\t\t      const QModelIndex &index) const override;\n\n\tvirtual void setEditorData(QWidget *editor, const QModelIndex &index) const override;\n\tvirtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;\n\tvirtual void paint(QPainter *painter, const QStyleOptionViewItem &option,\n\t\t\t   const QModelIndex &index) const override;\n\nprivate:\n\tbool isOutput;\n\tQString defaultPath;\n\tconst char *PATH_LIST_PROP = \"pathList\";\n\n\tvoid handleBrowse(QWidget *container);\n\tvoid handleClear(QWidget *container);\n\nprivate slots:\n\tvoid updateText();\n};\n"
  },
  {
    "path": "frontend/utility/RemuxQueueModel.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"RemuxQueueModel.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <QDir>\n#include <QStyle>\n\n#include \"moc_RemuxQueueModel.cpp\"\n\nint RemuxQueueModel::rowCount(const QModelIndex &) const\n{\n\treturn queue.length() + (isProcessing ? 0 : 1);\n}\n\nint RemuxQueueModel::columnCount(const QModelIndex &) const\n{\n\treturn RemuxEntryColumn::Count;\n}\n\nQVariant RemuxQueueModel::data(const QModelIndex &index, int role) const\n{\n\tQVariant result = QVariant();\n\n\tif (index.row() >= queue.length()) {\n\t\treturn QVariant();\n\t} else if (role == Qt::DisplayRole) {\n\t\tswitch (index.column()) {\n\t\tcase RemuxEntryColumn::InputPath:\n\t\t\tresult = queue[index.row()].sourcePath;\n\t\t\tbreak;\n\t\tcase RemuxEntryColumn::OutputPath:\n\t\t\tresult = queue[index.row()].targetPath;\n\t\t\tbreak;\n\t\t}\n\t} else if (role == Qt::DecorationRole && index.column() == RemuxEntryColumn::State) {\n\t\tresult = getIcon(queue[index.row()].state);\n\t} else if (role == RemuxEntryRole::EntryStateRole) {\n\t\tresult = queue[index.row()].state;\n\t}\n\n\treturn result;\n}\n\nQVariant RemuxQueueModel::headerData(int section, Qt::Orientation orientation, int role) const\n{\n\tQVariant result = QVariant();\n\n\tif (role == Qt::DisplayRole && orientation == Qt::Orientation::Horizontal) {\n\t\tswitch (section) {\n\t\tcase RemuxEntryColumn::State:\n\t\t\tresult = QString();\n\t\t\tbreak;\n\t\tcase RemuxEntryColumn::InputPath:\n\t\t\tresult = QTStr(\"Remux.SourceFile\");\n\t\t\tbreak;\n\t\tcase RemuxEntryColumn::OutputPath:\n\t\t\tresult = QTStr(\"Remux.TargetFile\");\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nQt::ItemFlags RemuxQueueModel::flags(const QModelIndex &index) const\n{\n\tQt::ItemFlags flags = QAbstractTableModel::flags(index);\n\n\tif (index.column() == RemuxEntryColumn::InputPath) {\n\t\tflags |= Qt::ItemIsEditable;\n\t} else if (index.column() == RemuxEntryColumn::OutputPath && index.row() != queue.length()) {\n\t\tflags |= Qt::ItemIsEditable;\n\t}\n\n\treturn flags;\n}\n\nbool RemuxQueueModel::setData(const QModelIndex &index, const QVariant &value, int role)\n{\n\tbool success = false;\n\n\tif (role == RemuxEntryRole::NewPathsToProcessRole) {\n\t\tQStringList pathList = value.toStringList();\n\n\t\tif (pathList.size() == 0) {\n\t\t\tif (index.row() < queue.size()) {\n\t\t\t\tbeginRemoveRows(QModelIndex(), index.row(), index.row());\n\t\t\t\tqueue.removeAt(index.row());\n\t\t\t\tendRemoveRows();\n\t\t\t}\n\t\t} else {\n\t\t\tif (pathList.size() >= 1 && index.row() < queue.length()) {\n\t\t\t\tqueue[index.row()].sourcePath = pathList[0];\n\t\t\t\tcheckInputPath(index.row());\n\n\t\t\t\tpathList.removeAt(0);\n\n\t\t\t\tsuccess = true;\n\t\t\t}\n\n\t\t\tif (pathList.size() > 0) {\n\t\t\t\tint row = index.row();\n\t\t\t\tint lastRow = row + pathList.size() - 1;\n\t\t\t\tbeginInsertRows(QModelIndex(), row, lastRow);\n\n\t\t\t\tfor (QString path : pathList) {\n\t\t\t\t\tRemuxQueueEntry entry;\n\t\t\t\t\tentry.sourcePath = path;\n\t\t\t\t\tentry.state = RemuxEntryState::Empty;\n\n\t\t\t\t\tqueue.insert(row, entry);\n\t\t\t\t\trow++;\n\t\t\t\t}\n\t\t\t\tendInsertRows();\n\n\t\t\t\tfor (row = index.row(); row <= lastRow; row++) {\n\t\t\t\t\tcheckInputPath(row);\n\t\t\t\t}\n\n\t\t\t\tsuccess = true;\n\t\t\t}\n\t\t}\n\t} else if (index.row() == queue.length()) {\n\t\tQString path = value.toString();\n\n\t\tif (!path.isEmpty()) {\n\t\t\tRemuxQueueEntry entry;\n\t\t\tentry.sourcePath = path;\n\t\t\tentry.state = RemuxEntryState::Empty;\n\n\t\t\tbeginInsertRows(QModelIndex(), queue.length() + 1, queue.length() + 1);\n\t\t\tqueue.append(entry);\n\t\t\tendInsertRows();\n\n\t\t\tcheckInputPath(index.row());\n\t\t\tsuccess = true;\n\t\t}\n\t} else {\n\t\tQString path = value.toString();\n\n\t\tif (path.isEmpty()) {\n\t\t\tif (index.column() == RemuxEntryColumn::InputPath) {\n\t\t\t\tbeginRemoveRows(QModelIndex(), index.row(), index.row());\n\t\t\t\tqueue.removeAt(index.row());\n\t\t\t\tendRemoveRows();\n\t\t\t}\n\t\t} else {\n\t\t\tswitch (index.column()) {\n\t\t\tcase RemuxEntryColumn::InputPath:\n\t\t\t\tqueue[index.row()].sourcePath = value.toString();\n\t\t\t\tcheckInputPath(index.row());\n\t\t\t\tsuccess = true;\n\t\t\t\tbreak;\n\t\t\tcase RemuxEntryColumn::OutputPath:\n\t\t\t\tqueue[index.row()].targetPath = value.toString();\n\t\t\t\temit dataChanged(index, index);\n\t\t\t\tsuccess = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn success;\n}\n\nQVariant RemuxQueueModel::getIcon(RemuxEntryState state)\n{\n\tQVariant icon;\n\tQStyle *style = QApplication::style();\n\n\tswitch (state) {\n\tcase RemuxEntryState::Complete:\n\t\ticon = style->standardIcon(QStyle::SP_DialogApplyButton);\n\t\tbreak;\n\n\tcase RemuxEntryState::InProgress:\n\t\ticon = style->standardIcon(QStyle::SP_ArrowRight);\n\t\tbreak;\n\n\tcase RemuxEntryState::Error:\n\t\ticon = style->standardIcon(QStyle::SP_DialogCancelButton);\n\t\tbreak;\n\n\tcase RemuxEntryState::InvalidPath:\n\t\ticon = style->standardIcon(QStyle::SP_MessageBoxWarning);\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn icon;\n}\n\nvoid RemuxQueueModel::checkInputPath(int row)\n{\n\tRemuxQueueEntry &entry = queue[row];\n\n\tif (entry.sourcePath.isEmpty()) {\n\t\tentry.state = RemuxEntryState::Empty;\n\t} else {\n\t\tentry.sourcePath = QDir::toNativeSeparators(entry.sourcePath);\n\t\tQFileInfo fileInfo(entry.sourcePath);\n\t\tif (fileInfo.exists())\n\t\t\tentry.state = RemuxEntryState::Ready;\n\t\telse\n\t\t\tentry.state = RemuxEntryState::InvalidPath;\n\n\t\tQString newExt = \".mp4\";\n\t\tQString suffix = fileInfo.suffix();\n\n\t\tif (suffix.contains(\"mov\", Qt::CaseInsensitive) || suffix.contains(\"mp4\", Qt::CaseInsensitive)) {\n\t\t\tnewExt = \".remuxed.\" + suffix;\n\t\t}\n\n\t\tif (entry.state == RemuxEntryState::Ready)\n\t\t\tentry.targetPath = QDir::toNativeSeparators(fileInfo.path() + QDir::separator() +\n\t\t\t\t\t\t\t\t    fileInfo.completeBaseName() + newExt);\n\t}\n\n\tif (entry.state == RemuxEntryState::Ready && isProcessing)\n\t\tentry.state = RemuxEntryState::Pending;\n\n\temit dataChanged(index(row, 0), index(row, RemuxEntryColumn::Count));\n}\n\nQFileInfoList RemuxQueueModel::checkForOverwrites() const\n{\n\tQFileInfoList list;\n\n\tfor (const RemuxQueueEntry &entry : queue) {\n\t\tif (entry.state == RemuxEntryState::Ready) {\n\t\t\tQFileInfo fileInfo(entry.targetPath);\n\t\t\tif (fileInfo.exists()) {\n\t\t\t\tlist.append(fileInfo);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn list;\n}\n\nbool RemuxQueueModel::checkForErrors() const\n{\n\tbool hasErrors = false;\n\n\tfor (const RemuxQueueEntry &entry : queue) {\n\t\tif (entry.state == RemuxEntryState::Error) {\n\t\t\thasErrors = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn hasErrors;\n}\n\nvoid RemuxQueueModel::clearAll()\n{\n\tbeginRemoveRows(QModelIndex(), 0, queue.size() - 1);\n\tqueue.clear();\n\tendRemoveRows();\n}\n\nvoid RemuxQueueModel::clearFinished()\n{\n\tint index = 0;\n\n\tfor (index = 0; index < queue.size(); index++) {\n\t\tconst RemuxQueueEntry &entry = queue[index];\n\t\tif (entry.state == RemuxEntryState::Complete) {\n\t\t\tbeginRemoveRows(QModelIndex(), index, index);\n\t\t\tqueue.removeAt(index);\n\t\t\tendRemoveRows();\n\t\t\tindex--;\n\t\t}\n\t}\n}\n\nbool RemuxQueueModel::canClearFinished() const\n{\n\tbool canClearFinished = false;\n\tfor (const RemuxQueueEntry &entry : queue)\n\t\tif (entry.state == RemuxEntryState::Complete) {\n\t\t\tcanClearFinished = true;\n\t\t\tbreak;\n\t\t}\n\n\treturn canClearFinished;\n}\n\nvoid RemuxQueueModel::beginProcessing()\n{\n\tfor (RemuxQueueEntry &entry : queue)\n\t\tif (entry.state == RemuxEntryState::Ready)\n\t\t\tentry.state = RemuxEntryState::Pending;\n\n\t// Signal that the insertion point no longer exists.\n\tbeginRemoveRows(QModelIndex(), queue.length(), queue.length());\n\tendRemoveRows();\n\n\tisProcessing = true;\n\n\temit dataChanged(index(0, RemuxEntryColumn::State), index(queue.length(), RemuxEntryColumn::State));\n}\n\nvoid RemuxQueueModel::endProcessing()\n{\n\tfor (RemuxQueueEntry &entry : queue) {\n\t\tif (entry.state == RemuxEntryState::Pending) {\n\t\t\tentry.state = RemuxEntryState::Ready;\n\t\t}\n\t}\n\n\t// Signal that the insertion point exists again.\n\tisProcessing = false;\n\tif (!autoRemux) {\n\t\tbeginInsertRows(QModelIndex(), queue.length(), queue.length());\n\t\tendInsertRows();\n\t}\n\n\temit dataChanged(index(0, RemuxEntryColumn::State), index(queue.length(), RemuxEntryColumn::State));\n}\n\nbool RemuxQueueModel::beginNextEntry(QString &inputPath, QString &outputPath)\n{\n\tbool anyStarted = false;\n\n\tfor (int row = 0; row < queue.length(); row++) {\n\t\tRemuxQueueEntry &entry = queue[row];\n\t\tif (entry.state == RemuxEntryState::Pending) {\n\t\t\tentry.state = RemuxEntryState::InProgress;\n\n\t\t\tinputPath = entry.sourcePath;\n\t\t\toutputPath = entry.targetPath;\n\n\t\t\tQModelIndex index = this->index(row, RemuxEntryColumn::State);\n\t\t\temit dataChanged(index, index);\n\n\t\t\tanyStarted = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn anyStarted;\n}\n\nvoid RemuxQueueModel::finishEntry(bool success)\n{\n\tfor (int row = 0; row < queue.length(); row++) {\n\t\tRemuxQueueEntry &entry = queue[row];\n\t\tif (entry.state == RemuxEntryState::InProgress) {\n\t\t\tif (success)\n\t\t\t\tentry.state = RemuxEntryState::Complete;\n\t\t\telse\n\t\t\t\tentry.state = RemuxEntryState::Error;\n\n\t\t\tQModelIndex index = this->index(row, RemuxEntryColumn::State);\n\t\t\temit dataChanged(index, index);\n\n\t\t\tbreak;\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "frontend/utility/RemuxQueueModel.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QAbstractTableModel>\n#include <QFileInfoList>\n\nenum RemuxEntryState { Empty, Ready, Pending, InProgress, Complete, InvalidPath, Error };\n\nQ_DECLARE_METATYPE(RemuxEntryState);\n\nenum RemuxEntryColumn {\n\tState,\n\tInputPath,\n\tOutputPath,\n\n\tCount\n};\n\nenum RemuxEntryRole { EntryStateRole = Qt::UserRole, NewPathsToProcessRole };\n\nclass RemuxQueueModel : public QAbstractTableModel {\n\tQ_OBJECT\n\n\tfriend class OBSRemux;\n\npublic:\n\tRemuxQueueModel(QObject *parent = 0) : QAbstractTableModel(parent), isProcessing(false) {}\n\n\tint rowCount(const QModelIndex &parent = QModelIndex()) const;\n\tint columnCount(const QModelIndex &parent = QModelIndex()) const;\n\tQVariant data(const QModelIndex &index, int role) const;\n\tQVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n\tQt::ItemFlags flags(const QModelIndex &index) const;\n\tbool setData(const QModelIndex &index, const QVariant &value, int role);\n\n\tQFileInfoList checkForOverwrites() const;\n\tbool checkForErrors() const;\n\tvoid beginProcessing();\n\tvoid endProcessing();\n\tbool beginNextEntry(QString &inputPath, QString &outputPath);\n\tvoid finishEntry(bool success);\n\tbool canClearFinished() const;\n\tvoid clearFinished();\n\tvoid clearAll();\n\n\tbool autoRemux = false;\n\nprivate:\n\tstruct RemuxQueueEntry {\n\t\tRemuxEntryState state;\n\n\t\tQString sourcePath;\n\t\tQString targetPath;\n\t};\n\n\tQList<RemuxQueueEntry> queue;\n\tbool isProcessing;\n\n\tstatic QVariant getIcon(RemuxEntryState state);\n\n\tvoid checkInputPath(int row);\n};\n"
  },
  {
    "path": "frontend/utility/RemuxWorker.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"RemuxWorker.hpp\"\n\n#include <media-io/media-remux.h>\n#include <qt-wrappers.hpp>\n\nvoid RemuxWorker::UpdateProgress(float percent)\n{\n\tif (abs(lastProgress - percent) < 0.1f)\n\t\treturn;\n\n\temit updateProgress(percent);\n\tlastProgress = percent;\n}\n\nvoid RemuxWorker::remux(const QString &source, const QString &target)\n{\n\tisWorking = true;\n\n\tauto callback = [](void *data, float percent) {\n\t\tRemuxWorker *rw = static_cast<RemuxWorker *>(data);\n\n\t\tQMutexLocker lock(&rw->updateMutex);\n\n\t\trw->UpdateProgress(percent);\n\n\t\treturn rw->isWorking;\n\t};\n\n\tbool stopped = false;\n\tbool success = false;\n\n\tmedia_remux_job_t mr_job = nullptr;\n\tif (media_remux_job_create(&mr_job, QT_TO_UTF8(source), QT_TO_UTF8(target))) {\n\n\t\tsuccess = media_remux_job_process(mr_job, callback, this);\n\n\t\tmedia_remux_job_destroy(mr_job);\n\n\t\tstopped = !isWorking;\n\t}\n\n\tisWorking = false;\n\n\temit remuxFinished(!stopped && success);\n}\n"
  },
  {
    "path": "frontend/utility/RemuxWorker.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QMutex>\n#include <QObject>\n\nclass RemuxWorker : public QObject {\n\tQ_OBJECT\n\n\tQMutex updateMutex;\n\n\tbool isWorking;\n\n\tfloat lastProgress;\n\tvoid UpdateProgress(float percent);\n\n\texplicit RemuxWorker() : isWorking(false) {}\n\tvirtual ~RemuxWorker() {};\n\nprivate slots:\n\tvoid remux(const QString &source, const QString &target);\n\nsignals:\n\tvoid updateProgress(float percent);\n\tvoid remuxFinished(bool success);\n\n\tfriend class OBSRemux;\n};\n"
  },
  {
    "path": "frontend/utility/SceneRenameDelegate.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"SceneRenameDelegate.hpp\"\n\n#include <QKeyEvent>\n#include <QLineEdit>\n\n#include \"moc_SceneRenameDelegate.cpp\"\n\nSceneRenameDelegate::SceneRenameDelegate(QObject *parent) : QStyledItemDelegate(parent) {}\n\nvoid SceneRenameDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const\n{\n\tQStyledItemDelegate::setEditorData(editor, index);\n\tQLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);\n\tif (lineEdit)\n\t\tlineEdit->selectAll();\n}\n\nbool SceneRenameDelegate::eventFilter(QObject *editor, QEvent *event)\n{\n\tif (event->type() == QEvent::KeyPress) {\n\t\tQKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);\n\t\tswitch (keyEvent->key()) {\n\t\tcase Qt::Key_Escape: {\n\t\t\tQLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);\n\t\t\tif (lineEdit)\n\t\t\t\tlineEdit->undo();\n\t\t\tbreak;\n\t\t}\n\t\tcase Qt::Key_Tab:\n\t\tcase Qt::Key_Backtab:\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn QStyledItemDelegate::eventFilter(editor, event);\n}\n"
  },
  {
    "path": "frontend/utility/SceneRenameDelegate.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <QStyledItemDelegate>\n\nclass SceneRenameDelegate : public QStyledItemDelegate {\n\tQ_OBJECT\n\npublic:\n\tSceneRenameDelegate(QObject *parent);\n\tvirtual void setEditorData(QWidget *editor, const QModelIndex &index) const override;\n\nprotected:\n\tvirtual bool eventFilter(QObject *editor, QEvent *event) override;\n};\n"
  },
  {
    "path": "frontend/utility/ScreenshotObj.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"ScreenshotObj.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#ifdef _WIN32\n#include <wincodec.h>\n#include <wincodecsdk.h>\n#include <wrl/client.h>\n#pragma comment(lib, \"windowscodecs.lib\")\n#endif\n\n#include \"moc_ScreenshotObj.cpp\"\n\nstatic void ScreenshotTick(void *param, float);\n\nScreenshotObj::ScreenshotObj(obs_source_t *source) : weakSource(OBSGetWeakRef(source))\n{\n\tobs_add_tick_callback(ScreenshotTick, this);\n}\n\nScreenshotObj::~ScreenshotObj()\n{\n\tobs_enter_graphics();\n\tgs_stagesurface_destroy(stagesurf);\n\tgs_texrender_destroy(texrender);\n\tobs_leave_graphics();\n\n\tobs_remove_tick_callback(ScreenshotTick, this);\n\n\tif (th.joinable()) {\n\t\tth.join();\n\n\t\tif (cx && cy) {\n\t\t\tOBSBasic *main = OBSBasic::Get();\n\t\t\tmain->ShowStatusBarMessage(\n\t\t\t\tQTStr(\"Basic.StatusBar.ScreenshotSavedTo\").arg(QT_UTF8(path.c_str())));\n\n\t\t\tmain->lastScreenshot = path;\n\n\t\t\tmain->OnEvent(OBS_FRONTEND_EVENT_SCREENSHOT_TAKEN);\n\t\t}\n\t}\n}\n\nvoid ScreenshotObj::Screenshot()\n{\n\tOBSSource source = OBSGetStrongRef(weakSource);\n\n\tif (source) {\n\t\tcx = obs_source_get_width(source);\n\t\tcy = obs_source_get_height(source);\n\t} else {\n\t\tobs_video_info ovi;\n\t\tobs_get_video_info(&ovi);\n\t\tcx = ovi.base_width;\n\t\tcy = ovi.base_height;\n\t}\n\n\tif (!cx || !cy) {\n\t\tblog(LOG_WARNING, \"Cannot screenshot, invalid target size\");\n\t\tobs_remove_tick_callback(ScreenshotTick, this);\n\t\tdeleteLater();\n\t\treturn;\n\t}\n\n#ifdef _WIN32\n\tenum gs_color_space space = obs_source_get_color_space(source, 0, nullptr);\n\tif (space == GS_CS_709_EXTENDED) {\n\t\t/* Convert for JXR */\n\t\tspace = GS_CS_709_SCRGB;\n\t}\n#else\n\t/* Tonemap to SDR if HDR */\n\tconst enum gs_color_space space = GS_CS_SRGB;\n#endif\n\tconst enum gs_color_format format = gs_get_format_from_space(space);\n\n\ttexrender = gs_texrender_create(format, GS_ZS_NONE);\n\tstagesurf = gs_stagesurface_create(cx, cy, format);\n\n\tif (gs_texrender_begin_with_color_space(texrender, cx, cy, space)) {\n\t\tvec4 zero;\n\t\tvec4_zero(&zero);\n\n\t\tgs_clear(GS_CLEAR_COLOR, &zero, 0.0f, 0);\n\t\tgs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);\n\n\t\tgs_blend_state_push();\n\t\tgs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);\n\n\t\tif (source) {\n\t\t\tobs_source_inc_showing(source);\n\t\t\tobs_source_video_render(source);\n\t\t\tobs_source_dec_showing(source);\n\t\t} else {\n\t\t\tobs_render_main_texture();\n\t\t}\n\n\t\tgs_blend_state_pop();\n\t\tgs_texrender_end(texrender);\n\t}\n}\n\nvoid ScreenshotObj::Download()\n{\n\tgs_stage_texture(stagesurf, gs_texrender_get_texture(texrender));\n}\n\nvoid ScreenshotObj::Copy()\n{\n\tuint8_t *videoData = nullptr;\n\tuint32_t videoLinesize = 0;\n\n\tif (gs_stagesurface_map(stagesurf, &videoData, &videoLinesize)) {\n\t\tif (gs_stagesurface_get_color_format(stagesurf) == GS_RGBA16F) {\n\t\t\tconst uint32_t linesize = cx * 8;\n\t\t\thalf_bytes.reserve(cx * cy * 8);\n\n\t\t\tfor (uint32_t y = 0; y < cy; y++) {\n\t\t\t\tconst uint8_t *const line = videoData + (y * videoLinesize);\n\t\t\t\thalf_bytes.insert(half_bytes.end(), line, line + linesize);\n\t\t\t}\n\t\t} else {\n\t\t\timage = QImage(cx, cy, QImage::Format::Format_RGBX8888);\n\n\t\t\tint linesize = image.bytesPerLine();\n\t\t\tfor (int y = 0; y < (int)cy; y++)\n\t\t\t\tmemcpy(image.scanLine(y), videoData + (y * videoLinesize), linesize);\n\t\t}\n\n\t\tgs_stagesurface_unmap(stagesurf);\n\t}\n}\n\nvoid ScreenshotObj::Save()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tconfig_t *config = main->Config();\n\n\tconst char *mode = config_get_string(config, \"Output\", \"Mode\");\n\tconst char *type = config_get_string(config, \"AdvOut\", \"RecType\");\n\tconst char *adv_path = strcmp(type, \"Standard\") ? config_get_string(config, \"AdvOut\", \"FFFilePath\")\n\t\t\t\t\t\t\t: config_get_string(config, \"AdvOut\", \"RecFilePath\");\n\tconst char *rec_path = strcmp(mode, \"Advanced\") ? config_get_string(config, \"SimpleOutput\", \"FilePath\")\n\t\t\t\t\t\t\t: adv_path;\n\n\tbool noSpace = config_get_bool(config, \"SimpleOutput\", \"FileNameWithoutSpace\");\n\tconst char *filenameFormat = config_get_string(config, \"Output\", \"FilenameFormatting\");\n\tbool overwriteIfExists = config_get_bool(config, \"Output\", \"OverwriteIfExists\");\n\n\tconst char *ext = half_bytes.empty() ? \"png\" : \"jxr\";\n\tpath = GetOutputFilename(rec_path, ext, noSpace, overwriteIfExists,\n\t\t\t\t GetFormatString(filenameFormat, \"Screenshot\", nullptr).c_str());\n\n\tth = std::thread([this] { MuxAndFinish(); });\n}\n\n#ifdef _WIN32\nstatic HRESULT SaveJxrImage(LPCWSTR path, uint8_t *pixels, uint32_t cx, uint32_t cy, IWICBitmapFrameEncode *frameEncode,\n\t\t\t    IPropertyBag2 *options)\n{\n\twchar_t lossless[] = L\"Lossless\";\n\tPROPBAG2 bag = {};\n\tbag.pstrName = lossless;\n\tVARIANT value = {};\n\tvalue.vt = VT_BOOL;\n\tvalue.bVal = TRUE;\n\tHRESULT hr = options->Write(1, &bag, &value);\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\thr = frameEncode->Initialize(options);\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\thr = frameEncode->SetSize(cx, cy);\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\thr = frameEncode->SetResolution(72, 72);\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\tWICPixelFormatGUID pixelFormat = GUID_WICPixelFormat64bppRGBAHalf;\n\thr = frameEncode->SetPixelFormat(&pixelFormat);\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\tif (memcmp(&pixelFormat, &GUID_WICPixelFormat64bppRGBAHalf, sizeof(WICPixelFormatGUID)) != 0)\n\t\treturn E_FAIL;\n\n\thr = frameEncode->WritePixels(cy, cx * 8, cx * cy * 8, pixels);\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\thr = frameEncode->Commit();\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\treturn S_OK;\n}\n\nstatic HRESULT SaveJxr(LPCWSTR path, uint8_t *pixels, uint32_t cx, uint32_t cy)\n{\n\tMicrosoft::WRL::ComPtr<IWICImagingFactory> factory;\n\tHRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,\n\t\t\t\t      IID_PPV_ARGS(factory.GetAddressOf()));\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\tMicrosoft::WRL::ComPtr<IWICStream> stream;\n\thr = factory->CreateStream(stream.GetAddressOf());\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\thr = stream->InitializeFromFilename(path, GENERIC_WRITE);\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\tMicrosoft::WRL::ComPtr<IWICBitmapEncoder> encoder = NULL;\n\thr = factory->CreateEncoder(GUID_ContainerFormatWmp, NULL, encoder.GetAddressOf());\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\thr = encoder->Initialize(stream.Get(), WICBitmapEncoderNoCache);\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\tMicrosoft::WRL::ComPtr<IWICBitmapFrameEncode> frameEncode;\n\tMicrosoft::WRL::ComPtr<IPropertyBag2> options;\n\thr = encoder->CreateNewFrame(frameEncode.GetAddressOf(), options.GetAddressOf());\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\thr = SaveJxrImage(path, pixels, cx, cy, frameEncode.Get(), options.Get());\n\tif (FAILED(hr))\n\t\treturn hr;\n\n\tencoder->Commit();\n\treturn S_OK;\n}\n#endif // #ifdef _WIN32\n\nvoid ScreenshotObj::MuxAndFinish()\n{\n\tif (half_bytes.empty()) {\n\t\timage.save(QT_UTF8(path.c_str()));\n\t\tblog(LOG_INFO, \"Saved screenshot to '%s'\", path.c_str());\n\t} else {\n#ifdef _WIN32\n\t\twchar_t *path_w = nullptr;\n\t\tos_utf8_to_wcs_ptr(path.c_str(), 0, &path_w);\n\t\tif (path_w) {\n\t\t\tSaveJxr(path_w, half_bytes.data(), cx, cy);\n\t\t\tbfree(path_w);\n\t\t}\n#endif // #ifdef _WIN32\n\t}\n\n\tdeleteLater();\n}\n\n#define STAGE_SCREENSHOT 0\n#define STAGE_DOWNLOAD 1\n#define STAGE_COPY_AND_SAVE 2\n#define STAGE_FINISH 3\n\nstatic void ScreenshotTick(void *param, float)\n{\n\tScreenshotObj *data = static_cast<ScreenshotObj *>(param);\n\n\tif (data->stage == STAGE_FINISH) {\n\t\treturn;\n\t}\n\n\tobs_enter_graphics();\n\n\tswitch (data->stage) {\n\tcase STAGE_SCREENSHOT:\n\t\tdata->Screenshot();\n\t\tbreak;\n\tcase STAGE_DOWNLOAD:\n\t\tdata->Download();\n\t\tbreak;\n\tcase STAGE_COPY_AND_SAVE:\n\t\tdata->Copy();\n\t\tQMetaObject::invokeMethod(data, \"Save\");\n\t\tobs_remove_tick_callback(ScreenshotTick, data);\n\t\tbreak;\n\t}\n\n\tobs_leave_graphics();\n\n\tdata->stage++;\n}\n"
  },
  {
    "path": "frontend/utility/ScreenshotObj.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <obs.hpp>\n\n#include <QImage>\n#include <QObject>\n\n#include <thread>\n\nclass ScreenshotObj : public QObject {\n\tQ_OBJECT\n\npublic:\n\tScreenshotObj(obs_source_t *source);\n\t~ScreenshotObj() override;\n\tvoid Screenshot();\n\tvoid Download();\n\tvoid Copy();\n\tvoid MuxAndFinish();\n\n\tgs_texrender_t *texrender = nullptr;\n\tgs_stagesurf_t *stagesurf = nullptr;\n\tOBSWeakSource weakSource;\n\tstd::string path;\n\tQImage image;\n\tstd::vector<uint8_t> half_bytes;\n\tuint32_t cx;\n\tuint32_t cy;\n\tstd::thread th;\n\n\tint stage = 0;\n\npublic slots:\n\tvoid Save();\n};\n"
  },
  {
    "path": "frontend/utility/SettingsEventFilter.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <OBSApp.hpp>\n#include <utility/OBSEventFilter.hpp>\n\n#include <QKeyEvent>\n#include <QObject>\n\nclass SettingsEventFilter : public QObject {\n\tQ_OBJECT\n\n\tQScopedPointer<OBSEventFilter> shortcutFilter;\n\npublic:\n\tinline SettingsEventFilter() : shortcutFilter((OBSEventFilter *)CreateShortcutFilter()) {}\n\nprotected:\n\tbool eventFilter(QObject *obj, QEvent *event) override\n\t{\n\t\tint key;\n\n\t\tswitch (event->type()) {\n\t\tcase QEvent::KeyPress:\n\t\tcase QEvent::KeyRelease:\n\t\t\tkey = static_cast<QKeyEvent *>(event)->key();\n\t\t\tif (key == Qt::Key_Escape) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\treturn shortcutFilter->filter(obj, event);\n\t}\n};\n"
  },
  {
    "path": "frontend/utility/SimpleOutput.cpp",
    "content": "#include \"SimpleOutput.hpp\"\n\n#include <utility/audio-encoders.hpp>\n#include <utility/StartMultiTrackVideoStreamingGuard.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\nusing namespace std;\n\nstatic bool CreateSimpleAACEncoder(OBSEncoder &res, int bitrate, const char *name, size_t idx)\n{\n\tconst char *id_ = GetSimpleAACEncoderForBitrate(bitrate);\n\tif (!id_) {\n\t\tres = nullptr;\n\t\treturn false;\n\t}\n\n\tres = obs_audio_encoder_create(id_, name, nullptr, idx, nullptr);\n\n\tif (res) {\n\t\tobs_encoder_release(res);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic bool CreateSimpleOpusEncoder(OBSEncoder &res, int bitrate, const char *name, size_t idx)\n{\n\tconst char *id_ = GetSimpleOpusEncoderForBitrate(bitrate);\n\tif (!id_) {\n\t\tres = nullptr;\n\t\treturn false;\n\t}\n\n\tres = obs_audio_encoder_create(id_, name, nullptr, idx, nullptr);\n\n\tif (res) {\n\t\tobs_encoder_release(res);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nextern bool EncoderAvailable(const char *encoder);\n\nvoid SimpleOutput::LoadRecordingPreset_Lossless()\n{\n\tfileOutput = obs_output_create(\"ffmpeg_output\", \"simple_ffmpeg_output\", nullptr, nullptr);\n\tif (!fileOutput)\n\t\tthrow \"Failed to create recording FFmpeg output \"\n\t\t      \"(simple output)\";\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"format_name\", \"avi\");\n\tobs_data_set_string(settings, \"video_encoder\", \"utvideo\");\n\tobs_data_set_string(settings, \"audio_encoder\", \"pcm_s16le\");\n\n\tobs_output_update(fileOutput, settings);\n}\n\nvoid SimpleOutput::LoadRecordingPreset_Lossy(const char *encoderId)\n{\n\tvideoRecording = obs_video_encoder_create(encoderId, \"simple_video_recording\", nullptr, nullptr);\n\tif (!videoRecording)\n\t\tthrow \"Failed to create video recording encoder (simple output)\";\n\tobs_encoder_release(videoRecording);\n}\n\nvoid SimpleOutput::LoadStreamingPreset_Lossy(const char *encoderId)\n{\n\tvideoStreaming = obs_video_encoder_create(encoderId, \"simple_video_stream\", nullptr, nullptr);\n\tif (!videoStreaming)\n\t\tthrow \"Failed to create video streaming encoder (simple output)\";\n\tobs_encoder_release(videoStreaming);\n\n\tif (whipSimulcastEncoders != nullptr) {\n\t\twhipSimulcastEncoders->Create(encoderId, config_get_int(main->Config(), \"AdvOut\", \"RescaleFilter\"),\n\t\t\t\t\t      config_get_int(main->Config(), \"Stream1\", \"WHIPSimulcastTotalLayers\"),\n\t\t\t\t\t      video_output_get_width(obs_get_video()),\n\t\t\t\t\t      video_output_get_height(obs_get_video()));\n\t}\n}\n\n/* mistakes have been made to lead us to this. */\nconst char *get_simple_output_encoder(const char *encoder)\n{\n\tif (strcmp(encoder, SIMPLE_ENCODER_X264) == 0) {\n\t\treturn \"obs_x264\";\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_X264_LOWCPU) == 0) {\n\t\treturn \"obs_x264\";\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0) {\n\t\treturn \"obs_qsv11_v2\";\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_QSV_AV1) == 0) {\n\t\treturn \"obs_qsv11_av1\";\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0) {\n\t\treturn \"h264_texture_amf\";\n#ifdef ENABLE_HEVC\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_AMD_HEVC) == 0) {\n\t\treturn \"h265_texture_amf\";\n#endif\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_AMD_AV1) == 0) {\n\t\treturn \"av1_texture_amf\";\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) {\n\t\treturn EncoderAvailable(\"obs_nvenc_h264_tex\") ? \"obs_nvenc_h264_tex\" : \"ffmpeg_nvenc\";\n#ifdef ENABLE_HEVC\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_NVENC_HEVC) == 0) {\n\t\treturn EncoderAvailable(\"obs_nvenc_hevc_tex\") ? \"obs_nvenc_hevc_tex\" : \"ffmpeg_hevc_nvenc\";\n#endif\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_NVENC_AV1) == 0) {\n\t\treturn \"obs_nvenc_av1_tex\";\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_APPLE_H264) == 0) {\n\t\treturn \"com.apple.videotoolbox.videoencoder.ave.avc\";\n#ifdef ENABLE_HEVC\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_APPLE_HEVC) == 0) {\n\t\treturn \"com.apple.videotoolbox.videoencoder.ave.hevc\";\n#endif\n\t}\n\n\treturn \"obs_x264\";\n}\n\nvoid SimpleOutput::LoadRecordingPreset()\n{\n\tconst char *quality = config_get_string(main->Config(), \"SimpleOutput\", \"RecQuality\");\n\tconst char *encoder = config_get_string(main->Config(), \"SimpleOutput\", \"RecEncoder\");\n\tconst char *audio_encoder = config_get_string(main->Config(), \"SimpleOutput\", \"RecAudioEncoder\");\n\n\tvideoEncoder = encoder;\n\tvideoQuality = quality;\n\tffmpegOutput = false;\n\n\tif (strcmp(quality, \"Stream\") == 0) {\n\t\tvideoRecording = videoStreaming;\n\t\taudioRecording = audioStreaming;\n\t\tusingRecordingPreset = false;\n\t\treturn;\n\n\t} else if (strcmp(quality, \"Lossless\") == 0) {\n\t\tLoadRecordingPreset_Lossless();\n\t\tusingRecordingPreset = true;\n\t\tffmpegOutput = true;\n\t\treturn;\n\n\t} else {\n\t\tlowCPUx264 = false;\n\n\t\tif (strcmp(encoder, SIMPLE_ENCODER_X264_LOWCPU) == 0)\n\t\t\tlowCPUx264 = true;\n\t\tLoadRecordingPreset_Lossy(get_simple_output_encoder(encoder));\n\t\tusingRecordingPreset = true;\n\n\t\tbool success = false;\n\n\t\tif (strcmp(audio_encoder, \"opus\") == 0)\n\t\t\tsuccess = CreateSimpleOpusEncoder(audioRecording, 192, \"simple_opus_recording\", 0);\n\t\telse\n\t\t\tsuccess = CreateSimpleAACEncoder(audioRecording, 192, \"simple_aac_recording\", 0);\n\n\t\tif (!success)\n\t\t\tthrow \"Failed to create audio recording encoder \"\n\t\t\t      \"(simple output)\";\n\t\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\t\tchar name[23];\n\t\t\tif (strcmp(audio_encoder, \"opus\") == 0) {\n\t\t\t\tsnprintf(name, sizeof name, \"simple_opus_recording%d\", i);\n\t\t\t\tsuccess = CreateSimpleOpusEncoder(audioTrack[i], GetAudioBitrate(), name, i);\n\t\t\t} else {\n\t\t\t\tsnprintf(name, sizeof name, \"simple_aac_recording%d\", i);\n\t\t\t\tsuccess = CreateSimpleAACEncoder(audioTrack[i], GetAudioBitrate(), name, i);\n\t\t\t}\n\t\t\tif (!success)\n\t\t\t\tthrow \"Failed to create multi-track audio recording encoder \"\n\t\t\t\t      \"(simple output)\";\n\t\t}\n\t}\n}\n\n#define SIMPLE_ARCHIVE_NAME \"simple_archive_audio\"\n\nSimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_)\n{\n\tconst char *encoder = config_get_string(main->Config(), \"SimpleOutput\", \"StreamEncoder\");\n\tconst char *audio_encoder = config_get_string(main->Config(), \"SimpleOutput\", \"StreamAudioEncoder\");\n\n\tLoadStreamingPreset_Lossy(get_simple_output_encoder(encoder));\n\n\tbool success = false;\n\n\tif (strcmp(audio_encoder, \"opus\") == 0)\n\t\tsuccess = CreateSimpleOpusEncoder(audioStreaming, GetAudioBitrate(), \"simple_opus\", 0);\n\telse\n\t\tsuccess = CreateSimpleAACEncoder(audioStreaming, GetAudioBitrate(), \"simple_aac\", 0);\n\n\tif (!success)\n\t\tthrow \"Failed to create audio streaming encoder (simple output)\";\n\n\tif (strcmp(audio_encoder, \"opus\") == 0)\n\t\tsuccess = CreateSimpleOpusEncoder(audioArchive, GetAudioBitrate(), SIMPLE_ARCHIVE_NAME, 1);\n\telse\n\t\tsuccess = CreateSimpleAACEncoder(audioArchive, GetAudioBitrate(), SIMPLE_ARCHIVE_NAME, 1);\n\n\tif (!success)\n\t\tthrow \"Failed to create audio archive encoder (simple output)\";\n\n\tLoadRecordingPreset();\n\n\tif (!ffmpegOutput) {\n\t\tbool useReplayBuffer = config_get_bool(main->Config(), \"SimpleOutput\", \"RecRB\");\n\t\tconst char *recFormat = config_get_string(main->Config(), \"SimpleOutput\", \"RecFormat2\");\n\n\t\tif (useReplayBuffer) {\n\t\t\tOBSDataAutoRelease hotkey;\n\t\t\tconst char *str = config_get_string(main->Config(), \"Hotkeys\", \"ReplayBuffer\");\n\t\t\tif (str)\n\t\t\t\thotkey = obs_data_create_from_json(str);\n\t\t\telse\n\t\t\t\thotkey = nullptr;\n\n\t\t\treplayBuffer = obs_output_create(\"replay_buffer\", Str(\"ReplayBuffer\"), nullptr, hotkey);\n\n\t\t\tif (!replayBuffer)\n\t\t\t\tthrow \"Failed to create replay buffer output \"\n\t\t\t\t      \"(simple output)\";\n\n\t\t\tsignal_handler_t *signal = obs_output_get_signal_handler(replayBuffer);\n\n\t\t\tstartReplayBuffer.Connect(signal, \"start\", OBSStartReplayBuffer, this);\n\t\t\tstopReplayBuffer.Connect(signal, \"stop\", OBSStopReplayBuffer, this);\n\t\t\treplayBufferStopping.Connect(signal, \"stopping\", OBSReplayBufferStopping, this);\n\t\t\treplayBufferSaved.Connect(signal, \"saved\", OBSReplayBufferSaved, this);\n\t\t}\n\n\t\tconst char *mux = \"ffmpeg_muxer\";\n\t\tif (strcmp(recFormat, \"hybrid_mp4\") == 0)\n\t\t\tmux = \"mp4_output\";\n\t\telse if (strcmp(recFormat, \"hybrid_mov\") == 0)\n\t\t\tmux = \"mov_output\";\n\n\t\tfileOutput = obs_output_create(mux, \"simple_file_output\", nullptr, nullptr);\n\t\tif (!fileOutput)\n\t\t\tthrow \"Failed to create recording output \"\n\t\t\t      \"(simple output)\";\n\t}\n\n\tstartRecording.Connect(obs_output_get_signal_handler(fileOutput), \"start\", OBSStartRecording, this);\n\tstopRecording.Connect(obs_output_get_signal_handler(fileOutput), \"stop\", OBSStopRecording, this);\n\trecordStopping.Connect(obs_output_get_signal_handler(fileOutput), \"stopping\", OBSRecordStopping, this);\n}\n\nint SimpleOutput::GetAudioBitrate() const\n{\n\tconst char *audio_encoder = config_get_string(main->Config(), \"SimpleOutput\", \"StreamAudioEncoder\");\n\tint bitrate = (int)config_get_uint(main->Config(), \"SimpleOutput\", \"ABitrate\");\n\n\tif (strcmp(audio_encoder, \"opus\") == 0)\n\t\treturn FindClosestAvailableSimpleOpusBitrate(bitrate);\n\n\treturn FindClosestAvailableSimpleAACBitrate(bitrate);\n}\n\nvoid SimpleOutput::Update()\n{\n\tOBSDataAutoRelease videoSettings = obs_data_create();\n\tOBSDataAutoRelease audioSettings = obs_data_create();\n\n\tint videoBitrate = config_get_uint(main->Config(), \"SimpleOutput\", \"VBitrate\");\n\tint audioBitrate = GetAudioBitrate();\n\tbool advanced = config_get_bool(main->Config(), \"SimpleOutput\", \"UseAdvanced\");\n\tbool enforceBitrate = !config_get_bool(main->Config(), \"Stream1\", \"IgnoreRecommended\");\n\tconst char *custom = config_get_string(main->Config(), \"SimpleOutput\", \"x264Settings\");\n\tconst char *encoder = config_get_string(main->Config(), \"SimpleOutput\", \"StreamEncoder\");\n\tconst char *encoder_id = obs_encoder_get_id(videoStreaming);\n\tconst char *presetType;\n\tconst char *preset;\n\n\tif (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0) {\n\t\tpresetType = \"QSVPreset\";\n\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_QSV_AV1) == 0) {\n\t\tpresetType = \"QSVPreset\";\n\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0) {\n\t\tpresetType = \"AMDPreset\";\n\n#ifdef ENABLE_HEVC\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_AMD_HEVC) == 0) {\n\t\tpresetType = \"AMDPreset\";\n#endif\n\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) {\n\t\tpresetType = \"NVENCPreset2\";\n\n#ifdef ENABLE_HEVC\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_NVENC_HEVC) == 0) {\n\t\tpresetType = \"NVENCPreset2\";\n#endif\n\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_AMD_AV1) == 0) {\n\t\tpresetType = \"AMDAV1Preset\";\n\n\t} else if (strcmp(encoder, SIMPLE_ENCODER_NVENC_AV1) == 0) {\n\t\tpresetType = \"NVENCPreset2\";\n\n\t} else {\n\t\tpresetType = \"Preset\";\n\t}\n\n\tpreset = config_get_string(main->Config(), \"SimpleOutput\", presetType);\n\n\t/* Only use preset2 for legacy/FFmpeg NVENC Encoder. */\n\tif (strncmp(encoder_id, \"ffmpeg_\", 7) == 0 && strcmp(presetType, \"NVENCPreset2\") == 0) {\n\t\tobs_data_set_string(videoSettings, \"preset2\", preset);\n\t} else {\n\t\tobs_data_set_string(videoSettings, \"preset\", preset);\n\t}\n\n\tobs_data_set_string(videoSettings, \"rate_control\", \"CBR\");\n\tobs_data_set_int(videoSettings, \"bitrate\", videoBitrate);\n\n\tif (advanced)\n\t\tobs_data_set_string(videoSettings, \"x264opts\", custom);\n\n\tobs_data_set_string(audioSettings, \"rate_control\", \"CBR\");\n\tobs_data_set_int(audioSettings, \"bitrate\", audioBitrate);\n\n\tobs_service_apply_encoder_settings(main->GetService(), videoSettings, audioSettings);\n\n\tif (!enforceBitrate) {\n\t\tint maxVideoBitrate;\n\t\tint maxAudioBitrate;\n\t\tobs_service_get_max_bitrate(main->GetService(), &maxVideoBitrate, &maxAudioBitrate);\n\n\t\tstd::string videoBitrateLogString = maxVideoBitrate > 0 ? std::to_string(maxVideoBitrate) : \"None\";\n\t\tstd::string audioBitrateLogString = maxAudioBitrate > 0 ? std::to_string(maxAudioBitrate) : \"None\";\n\n\t\tblog(LOG_INFO,\n\t\t     \"User is ignoring service bitrate limits.\\n\"\n\t\t     \"Service Recommendations:\\n\"\n\t\t     \"\\tvideo bitrate: %s\\n\"\n\t\t     \"\\taudio bitrate: %s\",\n\t\t     videoBitrateLogString.c_str(), audioBitrateLogString.c_str());\n\n\t\tobs_data_set_int(videoSettings, \"bitrate\", videoBitrate);\n\t\tobs_data_set_int(audioSettings, \"bitrate\", audioBitrate);\n\t}\n\n\tvideo_t *video = obs_get_video();\n\tenum video_format format = video_output_get_format(video);\n\n\tswitch (format) {\n\tcase VIDEO_FORMAT_I420:\n\tcase VIDEO_FORMAT_NV12:\n\tcase VIDEO_FORMAT_I010:\n\tcase VIDEO_FORMAT_P010:\n\t\tbreak;\n\tdefault:\n\t\tobs_encoder_set_preferred_video_format(videoStreaming, VIDEO_FORMAT_NV12);\n\t\tif (whipSimulcastEncoders != nullptr) {\n\t\t\twhipSimulcastEncoders->SetVideoFormat(VIDEO_FORMAT_NV12);\n\t\t}\n\t}\n\n\tobs_encoder_update(videoStreaming, videoSettings);\n\tobs_encoder_update(audioStreaming, audioSettings);\n\tobs_encoder_update(audioArchive, audioSettings);\n\n\tif (whipSimulcastEncoders != nullptr) {\n\t\twhipSimulcastEncoders->Update(videoSettings, videoBitrate);\n\t}\n}\n\nvoid SimpleOutput::UpdateRecordingAudioSettings()\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_int(settings, \"bitrate\", 192);\n\tobs_data_set_string(settings, \"rate_control\", \"CBR\");\n\n\tint tracks = config_get_int(main->Config(), \"SimpleOutput\", \"RecTracks\");\n\tconst char *recFormat = config_get_string(main->Config(), \"SimpleOutput\", \"RecFormat2\");\n\tconst char *quality = config_get_string(main->Config(), \"SimpleOutput\", \"RecQuality\");\n\tbool flv = strcmp(recFormat, \"flv\") == 0;\n\n\tif (flv || strcmp(quality, \"Stream\") == 0) {\n\t\tobs_encoder_update(audioRecording, settings);\n\t} else {\n\t\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\t\tif ((tracks & (1 << i)) != 0) {\n\t\t\t\tobs_encoder_update(audioTrack[i], settings);\n\t\t\t}\n\t\t}\n\t}\n}\n\n#define CROSS_DIST_CUTOFF 2000.0\n\nint SimpleOutput::CalcCRF(int crf)\n{\n\tint cx = config_get_uint(main->Config(), \"Video\", \"OutputCX\");\n\tint cy = config_get_uint(main->Config(), \"Video\", \"OutputCY\");\n\tdouble fCX = double(cx);\n\tdouble fCY = double(cy);\n\n\tif (lowCPUx264)\n\t\tcrf -= 2;\n\n\tdouble crossDist = sqrt(fCX * fCX + fCY * fCY);\n\tdouble crfResReduction = fmin(CROSS_DIST_CUTOFF, crossDist) / CROSS_DIST_CUTOFF;\n\tcrfResReduction = (1.0 - crfResReduction) * 10.0;\n\n\treturn crf - int(crfResReduction);\n}\n\nvoid SimpleOutput::UpdateRecordingSettings_x264_crf(int crf)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_int(settings, \"crf\", crf);\n\tobs_data_set_bool(settings, \"use_bufsize\", true);\n\tobs_data_set_string(settings, \"rate_control\", \"CRF\");\n\tobs_data_set_string(settings, \"profile\", \"high\");\n\tobs_data_set_string(settings, \"preset\", lowCPUx264 ? \"ultrafast\" : \"veryfast\");\n\n\tobs_encoder_update(videoRecording, settings);\n}\n\nstatic bool icq_available(obs_encoder_t *encoder)\n{\n\tobs_properties_t *props = obs_encoder_properties(encoder);\n\tobs_property_t *p = obs_properties_get(props, \"rate_control\");\n\tbool icq_found = false;\n\n\tsize_t num = obs_property_list_item_count(p);\n\tfor (size_t i = 0; i < num; i++) {\n\t\tconst char *val = obs_property_list_item_string(p, i);\n\t\tif (strcmp(val, \"ICQ\") == 0) {\n\t\t\ticq_found = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tobs_properties_destroy(props);\n\treturn icq_found;\n}\n\nvoid SimpleOutput::UpdateRecordingSettings_qsv11(int crf, bool av1)\n{\n\tbool icq = icq_available(videoRecording);\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"profile\", \"high\");\n\n\tif (icq && !av1) {\n\t\tobs_data_set_string(settings, \"rate_control\", \"ICQ\");\n\t\tobs_data_set_int(settings, \"icq_quality\", crf);\n\t} else {\n\t\tobs_data_set_string(settings, \"rate_control\", \"CQP\");\n\t\tobs_data_set_int(settings, \"cqp\", crf);\n\t}\n\n\tobs_encoder_update(videoRecording, settings);\n}\n\nvoid SimpleOutput::UpdateRecordingSettings_nvenc(int cqp)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"rate_control\", \"CQP\");\n\tobs_data_set_string(settings, \"profile\", \"high\");\n\tobs_data_set_int(settings, \"cqp\", cqp);\n\n\tobs_encoder_update(videoRecording, settings);\n}\n\nvoid SimpleOutput::UpdateRecordingSettings_nvenc_hevc_av1(int cqp)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"rate_control\", \"CQP\");\n\tobs_data_set_string(settings, \"profile\", \"main\");\n\tobs_data_set_int(settings, \"cqp\", cqp);\n\n\tobs_encoder_update(videoRecording, settings);\n}\n\nvoid SimpleOutput::UpdateRecordingSettings_apple(int quality)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"rate_control\", \"CRF\");\n\tobs_data_set_string(settings, \"profile\", \"high\");\n\tobs_data_set_int(settings, \"quality\", quality);\n\n\tobs_encoder_update(videoRecording, settings);\n}\n\n#ifdef ENABLE_HEVC\nvoid SimpleOutput::UpdateRecordingSettings_apple_hevc(int quality)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"rate_control\", \"CRF\");\n\tobs_data_set_string(settings, \"profile\", \"main\");\n\tobs_data_set_int(settings, \"quality\", quality);\n\n\tobs_encoder_update(videoRecording, settings);\n}\n#endif\n\nvoid SimpleOutput::UpdateRecordingSettings_amd_cqp(int cqp)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"rate_control\", \"CQP\");\n\tobs_data_set_string(settings, \"profile\", \"high\");\n\tobs_data_set_string(settings, \"preset\", \"quality\");\n\tobs_data_set_int(settings, \"cqp\", cqp);\n\tobs_encoder_update(videoRecording, settings);\n}\n\nvoid SimpleOutput::UpdateRecordingSettings()\n{\n\tbool ultra_hq = (videoQuality == \"HQ\");\n\tint crf = CalcCRF(ultra_hq ? 16 : 23);\n\n\tif (astrcmp_n(videoEncoder.c_str(), \"x264\", 4) == 0) {\n\t\tUpdateRecordingSettings_x264_crf(crf);\n\n\t} else if (videoEncoder == SIMPLE_ENCODER_QSV) {\n\t\tUpdateRecordingSettings_qsv11(crf, false);\n\n\t} else if (videoEncoder == SIMPLE_ENCODER_QSV_AV1) {\n\t\tUpdateRecordingSettings_qsv11(crf, true);\n\n\t} else if (videoEncoder == SIMPLE_ENCODER_AMD) {\n\t\tUpdateRecordingSettings_amd_cqp(crf);\n\n#ifdef ENABLE_HEVC\n\t} else if (videoEncoder == SIMPLE_ENCODER_AMD_HEVC) {\n\t\tUpdateRecordingSettings_amd_cqp(crf);\n#endif\n\n\t} else if (videoEncoder == SIMPLE_ENCODER_AMD_AV1) {\n\t\tUpdateRecordingSettings_amd_cqp(crf);\n\n\t} else if (videoEncoder == SIMPLE_ENCODER_NVENC) {\n\t\tUpdateRecordingSettings_nvenc(crf);\n\n#ifdef ENABLE_HEVC\n\t} else if (videoEncoder == SIMPLE_ENCODER_NVENC_HEVC) {\n\t\tUpdateRecordingSettings_nvenc_hevc_av1(crf);\n#endif\n\t} else if (videoEncoder == SIMPLE_ENCODER_NVENC_AV1) {\n\t\tUpdateRecordingSettings_nvenc_hevc_av1(crf);\n\n\t} else if (videoEncoder == SIMPLE_ENCODER_APPLE_H264) {\n\t\t/* These are magic numbers. 0 - 100, more is better. */\n\t\tUpdateRecordingSettings_apple(ultra_hq ? 70 : 50);\n#ifdef ENABLE_HEVC\n\t} else if (videoEncoder == SIMPLE_ENCODER_APPLE_HEVC) {\n\t\tUpdateRecordingSettings_apple_hevc(ultra_hq ? 70 : 50);\n#endif\n\t}\n\tUpdateRecordingAudioSettings();\n}\n\ninline void SimpleOutput::SetupOutputs()\n{\n\tSimpleOutput::Update();\n\tobs_encoder_set_video(videoStreaming, obs_get_video());\n\tobs_encoder_set_audio(audioStreaming, obs_get_audio());\n\tobs_encoder_set_audio(audioArchive, obs_get_audio());\n\tint tracks = config_get_int(main->Config(), \"SimpleOutput\", \"RecTracks\");\n\tconst char *recFormat = config_get_string(main->Config(), \"SimpleOutput\", \"RecFormat2\");\n\tbool flv = strcmp(recFormat, \"flv\") == 0;\n\n\tif (usingRecordingPreset) {\n\t\tif (ffmpegOutput) {\n\t\t\tobs_output_set_media(fileOutput, obs_get_video(), obs_get_audio());\n\t\t} else {\n\t\t\tobs_encoder_set_video(videoRecording, obs_get_video());\n\t\t\tif (flv) {\n\t\t\t\tobs_encoder_set_audio(audioRecording, obs_get_audio());\n\t\t\t} else {\n\t\t\t\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\t\t\t\tif ((tracks & (1 << i)) != 0) {\n\t\t\t\t\t\tobs_encoder_set_audio(audioTrack[i], obs_get_audio());\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\tobs_encoder_set_audio(audioRecording, obs_get_audio());\n\t}\n}\n\nstd::shared_future<void> SimpleOutput::SetupStreaming(obs_service_t *service, SetupStreamingContinuation_t continuation)\n{\n\tif (!Active())\n\t\tSetupOutputs();\n\n\tAuth *auth = main->GetAuth();\n\tif (auth)\n\t\tauth->OnStreamConfig();\n\n\t/* --------------------- */\n\n\tconst char *type = GetStreamOutputType(service);\n\tif (!type) {\n\t\tcontinuation(false);\n\t\treturn StartMultitrackVideoStreamingGuard::MakeReadyFuture();\n\t}\n\n\tauto audio_bitrate = GetAudioBitrate();\n\tauto vod_track_mixer = IsVodTrackEnabled(service) ? std::optional{1} : std::nullopt;\n\n\tauto handle_multitrack_video_result = [this, type = std::string{type},\n\t\t\t\t\t       service](std::optional<bool> multitrackVideoResult) {\n\t\tif (multitrackVideoResult.has_value())\n\t\t\treturn multitrackVideoResult.value();\n\n\t\t/* XXX: this is messy and disgusting and should be refactored */\n\t\tif (outputType != type) {\n\t\t\tstreamDelayStarting.Disconnect();\n\t\t\tstreamStopping.Disconnect();\n\t\t\tstartStreaming.Disconnect();\n\t\t\tstopStreaming.Disconnect();\n\n\t\t\tstreamOutput = obs_output_create(type.c_str(), \"simple_stream\", nullptr, nullptr);\n\t\t\tif (!streamOutput) {\n\t\t\t\tblog(LOG_WARNING, \"Creation of stream output type '%s' failed!\", type.c_str());\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tstreamDelayStarting.Connect(obs_output_get_signal_handler(streamOutput), \"starting\",\n\t\t\t\t\t\t    OBSStreamStarting, this);\n\t\t\tstreamStopping.Connect(obs_output_get_signal_handler(streamOutput), \"stopping\",\n\t\t\t\t\t       OBSStreamStopping, this);\n\n\t\t\tstartStreaming.Connect(obs_output_get_signal_handler(streamOutput), \"start\", OBSStartStreaming,\n\t\t\t\t\t       this);\n\t\t\tstopStreaming.Connect(obs_output_get_signal_handler(streamOutput), \"stop\", OBSStopStreaming,\n\t\t\t\t\t      this);\n\n\t\t\toutputType = type;\n\t\t}\n\n\t\tobs_output_set_video_encoder(streamOutput, videoStreaming);\n\t\tif (whipSimulcastEncoders != nullptr) {\n\t\t\twhipSimulcastEncoders->SetStreamOutput(streamOutput);\n\t\t}\n\t\tobs_output_set_audio_encoder(streamOutput, audioStreaming, 0);\n\t\tobs_output_set_service(streamOutput, service);\n\t\treturn true;\n\t};\n\n\treturn SetupMultitrackVideo(service, GetSimpleAACEncoderForBitrate(audio_bitrate), 0, vod_track_mixer,\n\t\t\t\t    [=](std::optional<bool> res) {\n\t\t\t\t\t    continuation(handle_multitrack_video_result(res));\n\t\t\t\t    });\n}\n\nbool SimpleOutput::IsVodTrackEnabled(obs_service_t *service)\n{\n\tbool advanced = config_get_bool(main->Config(), \"SimpleOutput\", \"UseAdvanced\");\n\tbool enable = config_get_bool(main->Config(), \"SimpleOutput\", \"VodTrackEnabled\");\n\tbool enableForCustomServer = config_get_bool(App()->GetUserConfig(), \"General\", \"EnableCustomServerVodTrack\");\n\n\tOBSDataAutoRelease settings = obs_service_get_settings(service);\n\tconst char *name = obs_data_get_string(settings, \"service\");\n\n\tconst char *id = obs_service_get_id(service);\n\tif (strcmp(id, \"rtmp_custom\") == 0)\n\t\treturn enableForCustomServer ? enable : false;\n\telse\n\t\treturn advanced && enable && ServiceSupportsVodTrack(name);\n}\n\nvoid SimpleOutput::SetupVodTrack(obs_service_t *service)\n{\n\tif (IsVodTrackEnabled(service))\n\t\tobs_output_set_audio_encoder(streamOutput, audioArchive, 1);\n\telse\n\t\tclear_archive_encoder(streamOutput, SIMPLE_ARCHIVE_NAME);\n}\n\nbool SimpleOutput::StartStreaming(obs_service_t *service)\n{\n\tbool reconnect = config_get_bool(main->Config(), \"Output\", \"Reconnect\");\n\tint retryDelay = config_get_uint(main->Config(), \"Output\", \"RetryDelay\");\n\tint maxRetries = config_get_uint(main->Config(), \"Output\", \"MaxRetries\");\n\tbool useDelay = config_get_bool(main->Config(), \"Output\", \"DelayEnable\");\n\tint delaySec = config_get_int(main->Config(), \"Output\", \"DelaySec\");\n\tbool preserveDelay = config_get_bool(main->Config(), \"Output\", \"DelayPreserve\");\n\tconst char *bindIP = config_get_string(main->Config(), \"Output\", \"BindIP\");\n\tconst char *ipFamily = config_get_string(main->Config(), \"Output\", \"IPFamily\");\n#ifdef _WIN32\n\tbool enableNewSocketLoop = config_get_bool(main->Config(), \"Output\", \"NewSocketLoopEnable\");\n\tbool enableLowLatencyMode = config_get_bool(main->Config(), \"Output\", \"LowLatencyEnable\");\n#endif\n\tbool enableDynBitrate = config_get_bool(main->Config(), \"Output\", \"DynamicBitrate\");\n\n\tif (multitrackVideo && multitrackVideoActive &&\n\t    !multitrackVideo->HandleIncompatibleSettings(main, main->Config(), service, enableDynBitrate)) {\n\t\tmultitrackVideoActive = false;\n\t\treturn false;\n\t}\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"bind_ip\", bindIP);\n\tobs_data_set_string(settings, \"ip_family\", ipFamily);\n#ifdef _WIN32\n\tobs_data_set_bool(settings, \"new_socket_loop_enabled\", enableNewSocketLoop);\n\tobs_data_set_bool(settings, \"low_latency_mode_enabled\", enableLowLatencyMode);\n#endif\n\tobs_data_set_bool(settings, \"dyn_bitrate\", enableDynBitrate);\n\n\tauto streamOutput = StreamingOutput(); // shadowing is sort of bad, but also convenient\n\n\tobs_output_update(streamOutput, settings);\n\n\tif (!reconnect)\n\t\tmaxRetries = 0;\n\n\tobs_output_set_delay(streamOutput, useDelay ? delaySec : 0, preserveDelay ? OBS_OUTPUT_DELAY_PRESERVE : 0);\n\n\tobs_output_set_reconnect_settings(streamOutput, maxRetries, retryDelay);\n\n\tif (!multitrackVideo || !multitrackVideoActive)\n\t\tSetupVodTrack(service);\n\n\tif (obs_output_start(streamOutput)) {\n\t\tif (multitrackVideo && multitrackVideoActive)\n\t\t\tmultitrackVideo->StartedStreaming();\n\t\treturn true;\n\t}\n\n\tif (multitrackVideo && multitrackVideoActive)\n\t\tmultitrackVideoActive = false;\n\n\tconst char *error = obs_output_get_last_error(streamOutput);\n\tbool hasLastError = error && *error;\n\tif (hasLastError)\n\t\tlastError = error;\n\telse\n\t\tlastError = string();\n\n\tconst char *type = obs_output_get_id(streamOutput);\n\tblog(LOG_WARNING, \"Stream output type '%s' failed to start!%s%s\", type, hasLastError ? \"  Last Error: \" : \"\",\n\t     hasLastError ? error : \"\");\n\treturn false;\n}\n\nvoid SimpleOutput::UpdateRecording()\n{\n\tconst char *recFormat = config_get_string(main->Config(), \"SimpleOutput\", \"RecFormat2\");\n\tbool flv = strcmp(recFormat, \"flv\") == 0;\n\tint tracks = config_get_int(main->Config(), \"SimpleOutput\", \"RecTracks\");\n\tint idx = 0;\n\tint idx2 = 0;\n\tconst char *quality = config_get_string(main->Config(), \"SimpleOutput\", \"RecQuality\");\n\n\tif (replayBufferActive || recordingActive)\n\t\treturn;\n\n\tif (usingRecordingPreset) {\n\t\tif (!ffmpegOutput)\n\t\t\tUpdateRecordingSettings();\n\t} else if (!obs_output_active(streamOutput)) {\n\t\tUpdate();\n\t}\n\n\tif (!Active())\n\t\tSetupOutputs();\n\n\tif (!ffmpegOutput) {\n\t\tobs_output_set_video_encoder(fileOutput, videoRecording);\n\t\tif (flv || strcmp(quality, \"Stream\") == 0) {\n\t\t\tobs_output_set_audio_encoder(fileOutput, audioRecording, 0);\n\t\t} else {\n\t\t\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\t\t\tif ((tracks & (1 << i)) != 0) {\n\t\t\t\t\tobs_output_set_audio_encoder(fileOutput, audioTrack[i], idx++);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif (replayBuffer) {\n\t\tobs_output_set_video_encoder(replayBuffer, videoRecording);\n\t\tif (flv || strcmp(quality, \"Stream\") == 0) {\n\t\t\tobs_output_set_audio_encoder(replayBuffer, audioRecording, 0);\n\t\t} else {\n\t\t\tfor (int i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\t\t\tif ((tracks & (1 << i)) != 0) {\n\t\t\t\t\tobs_output_set_audio_encoder(replayBuffer, audioTrack[i], idx2++);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\trecordingConfigured = true;\n}\n\nbool SimpleOutput::ConfigureRecording(bool updateReplayBuffer)\n{\n\tconst char *path = config_get_string(main->Config(), \"SimpleOutput\", \"FilePath\");\n\tconst char *format = config_get_string(main->Config(), \"SimpleOutput\", \"RecFormat2\");\n\tconst char *mux = config_get_string(main->Config(), \"SimpleOutput\", \"MuxerCustom\");\n\tbool noSpace = config_get_bool(main->Config(), \"SimpleOutput\", \"FileNameWithoutSpace\");\n\tconst char *filenameFormat = config_get_string(main->Config(), \"Output\", \"FilenameFormatting\");\n\tbool overwriteIfExists = config_get_bool(main->Config(), \"Output\", \"OverwriteIfExists\");\n\tconst char *rbPrefix = config_get_string(main->Config(), \"SimpleOutput\", \"RecRBPrefix\");\n\tconst char *rbSuffix = config_get_string(main->Config(), \"SimpleOutput\", \"RecRBSuffix\");\n\tint rbTime = config_get_int(main->Config(), \"SimpleOutput\", \"RecRBTime\");\n\tint rbSize = config_get_int(main->Config(), \"SimpleOutput\", \"RecRBSize\");\n\tint tracks = config_get_int(main->Config(), \"SimpleOutput\", \"RecTracks\");\n\n\tbool is_fragmented = strncmp(format, \"fragmented\", 10) == 0;\n\tbool is_lossless = videoQuality == \"Lossless\";\n\n\tstring f;\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tif (updateReplayBuffer) {\n\t\tf = GetFormatString(filenameFormat, rbPrefix, rbSuffix);\n\t\tstring ext = GetFormatExt(format);\n\t\tobs_data_set_string(settings, \"directory\", path);\n\t\tobs_data_set_string(settings, \"format\", f.c_str());\n\t\tobs_data_set_string(settings, \"extension\", ext.c_str());\n\t\tobs_data_set_bool(settings, \"allow_spaces\", !noSpace);\n\t\tobs_data_set_int(settings, \"max_time_sec\", rbTime);\n\t\tobs_data_set_int(settings, \"max_size_mb\", usingRecordingPreset ? rbSize : 0);\n\t} else {\n\t\tf = GetFormatString(filenameFormat, nullptr, nullptr);\n\t\tstring strPath = GetRecordingFilename(path, ffmpegOutput ? \"avi\" : format, noSpace, overwriteIfExists,\n\t\t\t\t\t\t      f.c_str(), ffmpegOutput);\n\t\tobs_data_set_string(settings, ffmpegOutput ? \"url\" : \"path\", strPath.c_str());\n\t\tif (ffmpegOutput)\n\t\t\tobs_output_set_mixers(fileOutput, tracks);\n\t}\n\n\t// Use fragmented MOV/MP4 if user has not already specified custom movflags\n\tif (is_fragmented && !is_lossless && (!mux || strstr(mux, \"movflags\") == NULL)) {\n\t\tstring mux_frag = \"movflags=frag_keyframe+empty_moov+delay_moov\";\n\t\tif (mux) {\n\t\t\tmux_frag += \" \";\n\t\t\tmux_frag += mux;\n\t\t}\n\t\tobs_data_set_string(settings, \"muxer_settings\", mux_frag.c_str());\n\t} else {\n\t\tif (is_fragmented && !is_lossless)\n\t\t\tblog(LOG_WARNING, \"User enabled fragmented recording, \"\n\t\t\t\t\t  \"but custom muxer settings contained movflags.\");\n\t\tobs_data_set_string(settings, \"muxer_settings\", mux);\n\t}\n\n\tif (updateReplayBuffer)\n\t\tobs_output_update(replayBuffer, settings);\n\telse\n\t\tobs_output_update(fileOutput, settings);\n\n\treturn true;\n}\n\nbool SimpleOutput::StartRecording()\n{\n\tUpdateRecording();\n\tif (!ConfigureRecording(false))\n\t\treturn false;\n\tif (!obs_output_start(fileOutput)) {\n\t\tQString error_reason;\n\t\tconst char *error = obs_output_get_last_error(fileOutput);\n\t\tif (error)\n\t\t\terror_reason = QT_UTF8(error);\n\t\telse\n\t\t\terror_reason = QTStr(\"Output.StartFailedGeneric\");\n\t\tQMessageBox::critical(main, QTStr(\"Output.StartRecordingFailed\"), error_reason);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool SimpleOutput::StartReplayBuffer()\n{\n\tUpdateRecording();\n\tif (!ConfigureRecording(true))\n\t\treturn false;\n\tif (!obs_output_start(replayBuffer)) {\n\t\tQMessageBox::critical(main, QTStr(\"Output.StartReplayFailed\"), QTStr(\"Output.StartFailedGeneric\"));\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid SimpleOutput::StopStreaming(bool force)\n{\n\tauto output = StreamingOutput();\n\tif (force && output)\n\t\tobs_output_force_stop(output);\n\telse if (multitrackVideo && multitrackVideoActive)\n\t\tmultitrackVideo->StopStreaming();\n\telse\n\t\tobs_output_stop(output);\n}\n\nvoid SimpleOutput::StopRecording(bool force)\n{\n\tif (force)\n\t\tobs_output_force_stop(fileOutput);\n\telse\n\t\tobs_output_stop(fileOutput);\n}\n\nvoid SimpleOutput::StopReplayBuffer(bool force)\n{\n\tif (force)\n\t\tobs_output_force_stop(replayBuffer);\n\telse\n\t\tobs_output_stop(replayBuffer);\n}\n\nbool SimpleOutput::StreamingActive() const\n{\n\treturn obs_output_active(StreamingOutput());\n}\n\nbool SimpleOutput::RecordingActive() const\n{\n\treturn obs_output_active(fileOutput);\n}\n\nbool SimpleOutput::ReplayBufferActive() const\n{\n\treturn obs_output_active(replayBuffer);\n}\n"
  },
  {
    "path": "frontend/utility/SimpleOutput.hpp",
    "content": "#pragma once\n\n#include \"BasicOutputHandler.hpp\"\n\nstruct SimpleOutput : BasicOutputHandler {\n\tOBSEncoder audioStreaming;\n\tOBSEncoder videoStreaming;\n\tOBSEncoder audioRecording;\n\tOBSEncoder audioArchive;\n\tOBSEncoder videoRecording;\n\tOBSEncoder audioTrack[MAX_AUDIO_MIXES];\n\n\tstd::string videoEncoder;\n\tstd::string videoQuality;\n\tbool usingRecordingPreset = false;\n\tbool recordingConfigured = false;\n\tbool ffmpegOutput = false;\n\tbool lowCPUx264 = false;\n\n\tSimpleOutput(OBSBasic *main_);\n\n\tint CalcCRF(int crf);\n\n\tvoid UpdateRecordingSettings_x264_crf(int crf);\n\tvoid UpdateRecordingSettings_qsv11(int crf, bool av1);\n\tvoid UpdateRecordingSettings_nvenc(int cqp);\n\tvoid UpdateRecordingSettings_nvenc_hevc_av1(int cqp);\n\tvoid UpdateRecordingSettings_amd_cqp(int cqp);\n\tvoid UpdateRecordingSettings_apple(int quality);\n#ifdef ENABLE_HEVC\n\tvoid UpdateRecordingSettings_apple_hevc(int quality);\n#endif\n\tvoid UpdateRecordingSettings();\n\tvoid UpdateRecordingAudioSettings();\n\tvirtual void Update() override;\n\n\tvoid SetupOutputs() override;\n\tint GetAudioBitrate() const;\n\n\tvoid LoadRecordingPreset_Lossy(const char *encoder);\n\tvoid LoadRecordingPreset_Lossless();\n\tvoid LoadRecordingPreset();\n\n\tvoid LoadStreamingPreset_Lossy(const char *encoder);\n\n\tvoid UpdateRecording();\n\tbool ConfigureRecording(bool useReplayBuffer);\n\n\tbool IsVodTrackEnabled(obs_service_t *service);\n\tvoid SetupVodTrack(obs_service_t *service);\n\n\tvirtual std::shared_future<void> SetupStreaming(obs_service_t *service,\n\t\t\t\t\t\t\tSetupStreamingContinuation_t continuation) override;\n\tvirtual bool StartStreaming(obs_service_t *service) override;\n\tvirtual bool StartRecording() override;\n\tvirtual bool StartReplayBuffer() override;\n\tvirtual void StopStreaming(bool force) override;\n\tvirtual void StopRecording(bool force) override;\n\tvirtual void StopReplayBuffer(bool force) override;\n\tvirtual bool StreamingActive() const override;\n\tvirtual bool RecordingActive() const override;\n\tvirtual bool ReplayBufferActive() const override;\n};\n"
  },
  {
    "path": "frontend/utility/StartMultiTrackVideoStreamingGuard.hpp",
    "content": "#pragma once\n\n#include <future>\n\nstruct StartMultitrackVideoStreamingGuard {\n\tStartMultitrackVideoStreamingGuard() { future = guard.get_future().share(); };\n\t~StartMultitrackVideoStreamingGuard() { guard.set_value(); }\n\n\tstd::shared_future<void> GetFuture() const { return future; }\n\n\tstatic std::shared_future<void> MakeReadyFuture()\n\t{\n\t\tStartMultitrackVideoStreamingGuard guard;\n\t\treturn guard.GetFuture();\n\t}\n\nprivate:\n\tstd::promise<void> guard;\n\tstd::shared_future<void> future;\n};\n"
  },
  {
    "path": "frontend/utility/SurfaceEventFilter.hpp",
    "content": "#pragma once\n\n#include <widgets/OBSQTDisplay.hpp>\n\n#include <QObject>\n#include <QPlatformSurfaceEvent>\n\nclass SurfaceEventFilter : public QObject {\n\tQ_OBJECT\n\n\tOBSQTDisplay *display;\n\npublic:\n\tSurfaceEventFilter(OBSQTDisplay *src) : QObject(src), display(src) {}\n\nprotected:\n\tbool eventFilter(QObject *obj, QEvent *event) override\n\t{\n\t\tbool result = QObject::eventFilter(obj, event);\n\t\tQPlatformSurfaceEvent *surfaceEvent;\n\n\t\tswitch (event->type()) {\n\t\tcase QEvent::PlatformSurface:\n\t\t\tsurfaceEvent = static_cast<QPlatformSurfaceEvent *>(event);\n\n\t\t\tswitch (surfaceEvent->surfaceEventType()) {\n\t\t\tcase QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed:\n\t\t\t\tdisplay->DestroyDisplay();\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\treturn result;\n\t}\n};\n"
  },
  {
    "path": "frontend/utility/VCamConfig.hpp",
    "content": "#pragma once\n\nconstexpr const char *VIRTUAL_CAM_ID = \"virtualcam_output\";\n\nenum VCamOutputType {\n\tInvalid,\n\tSceneOutput,\n\tSourceOutput,\n\tProgramView,\n\tPreviewOutput,\n};\n\n// Kept for config upgrade\nenum VCamInternalType {\n\tDefault,\n\tPreview,\n};\n\nstruct VCamConfig {\n\tVCamOutputType type = VCamOutputType::ProgramView;\n\tstd::string scene;\n\tstd::string source;\n};\n"
  },
  {
    "path": "frontend/utility/WHIPSimulcastEncoders.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Sean DuBois <sean@pion.ly>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n#pragma once\n\nstruct WHIPSimulcastEncoders {\npublic:\n\tvoid Create(const char *encoderId, int rescaleFilter, int whipSimulcastTotalLayers, uint32_t outputWidth,\n\t\t    uint32_t outputHeight)\n\t{\n\t\tif (rescaleFilter == OBS_SCALE_DISABLE) {\n\t\t\trescaleFilter = OBS_SCALE_BICUBIC;\n\t\t}\n\n\t\tif (whipSimulcastTotalLayers <= 1) {\n\t\t\treturn;\n\t\t}\n\n\t\tauto widthStep = outputWidth / whipSimulcastTotalLayers;\n\t\tauto heightStep = outputHeight / whipSimulcastTotalLayers;\n\t\tstd::string encoder_name = \"whip_simulcast_0\";\n\n\t\tfor (auto i = whipSimulcastTotalLayers - 1; i > 0; i--) {\n\t\t\tuint32_t width = widthStep * i;\n\t\t\twidth -= width % 2;\n\n\t\t\tuint32_t height = heightStep * i;\n\t\t\theight -= height % 2;\n\n\t\t\tencoder_name[encoder_name.size() - 1] = std::to_string(i).at(0);\n\t\t\tauto whip_simulcast_encoder =\n\t\t\t\tobs_video_encoder_create(encoderId, encoder_name.c_str(), nullptr, nullptr);\n\n\t\t\tif (whip_simulcast_encoder) {\n\t\t\t\tobs_encoder_set_video(whip_simulcast_encoder, obs_get_video());\n\t\t\t\tobs_encoder_set_scaled_size(whip_simulcast_encoder, width, height);\n\t\t\t\tobs_encoder_set_gpu_scale_type(whip_simulcast_encoder, (obs_scale_type)rescaleFilter);\n\t\t\t\twhipSimulcastEncoders.push_back(whip_simulcast_encoder);\n\t\t\t\tobs_encoder_release(whip_simulcast_encoder);\n\t\t\t} else {\n\t\t\t\tblog(LOG_WARNING,\n\t\t\t\t     \"Failed to create video streaming WHIP Simulcast encoders (BasicOutputHandler)\");\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid Update(obs_data_t *videoSettings, int videoBitrate)\n\t{\n\t\tauto bitrateStep = videoBitrate / static_cast<int>(whipSimulcastEncoders.size() + 1);\n\t\tfor (auto &whipSimulcastEncoder : whipSimulcastEncoders) {\n\t\t\tvideoBitrate -= bitrateStep;\n\t\t\tobs_data_set_int(videoSettings, \"bitrate\", videoBitrate);\n\t\t\tobs_encoder_update(whipSimulcastEncoder, videoSettings);\n\t\t}\n\t}\n\n\tvoid SetVideoFormat(enum video_format format)\n\t{\n\t\tfor (auto enc : whipSimulcastEncoders)\n\t\t\tobs_encoder_set_preferred_video_format(enc, format);\n\t}\n\n\tvoid SetStreamOutput(obs_output_t *streamOutput)\n\t{\n\t\tfor (size_t i = 0; i < whipSimulcastEncoders.size(); i++)\n\t\t\tobs_output_set_video_encoder2(streamOutput, whipSimulcastEncoders[i], i + 1);\n\t}\n\nprivate:\n\tstd::vector<OBSEncoder> whipSimulcastEncoders;\n};\n"
  },
  {
    "path": "frontend/utility/WhatsNewBrowserInitThread.cpp",
    "content": "#include \"WhatsNewBrowserInitThread.hpp\"\n\n#ifdef BROWSER_AVAILABLE\n#include <browser-panel.hpp>\n#endif\n\n#include \"moc_WhatsNewBrowserInitThread.cpp\"\n\n#ifdef BROWSER_AVAILABLE\nstruct QCef;\nextern QCef *cef;\n#endif\n\nvoid WhatsNewBrowserInitThread::run()\n{\n#ifdef BROWSER_AVAILABLE\n\tcef->wait_for_browser_init();\n#endif\n\temit Result(url);\n}\n"
  },
  {
    "path": "frontend/utility/WhatsNewBrowserInitThread.hpp",
    "content": "#pragma once\n\n#include <QThread>\n\nclass WhatsNewBrowserInitThread : public QThread {\n\tQ_OBJECT\n\n\tQString url;\n\n\tvirtual void run() override;\n\nsignals:\n\tvoid Result(const QString &url);\n\npublic:\n\tinline WhatsNewBrowserInitThread(const QString &url_) : url(url_) {}\n};\n"
  },
  {
    "path": "frontend/utility/WhatsNewInfoThread.cpp",
    "content": "#include \"WhatsNewInfoThread.hpp\"\n\n#include <OBSApp.hpp>\n#include <utility/RemoteTextThread.hpp>\n#include <utility/crypto-helpers.hpp>\n#include <utility/platform.hpp>\n#include <utility/update-helpers.hpp>\n\n#include <QRandomGenerator>\n#include <blake2.h>\n\n#include <fstream>\n\n#include \"moc_WhatsNewInfoThread.cpp\"\n\n#ifndef MAC_WHATSNEW_URL\n#define MAC_WHATSNEW_URL \"https://obsproject.com/update_studio/whatsnew.json\"\n#endif\n\n#ifndef WIN_WHATSNEW_URL\n#define WIN_WHATSNEW_URL \"https://obsproject.com/update_studio/whatsnew.json\"\n#endif\n\n#ifndef LINUX_WHATSNEW_URL\n#define LINUX_WHATSNEW_URL \"https://obsproject.com/update_studio/whatsnew.json\"\n#endif\n\n#ifdef __APPLE__\n#define WHATSNEW_URL MAC_WHATSNEW_URL\n#elif defined(_WIN32)\n#define WHATSNEW_URL WIN_WHATSNEW_URL\n#else\n#define WHATSNEW_URL LINUX_WHATSNEW_URL\n#endif\n\n#define HASH_READ_BUF_SIZE 65536\n#define BLAKE2_HASH_LENGTH 20\n\n/* ------------------------------------------------------------------------ */\n\nstatic bool QuickWriteFile(const char *file, const std::string &data)\ntry {\n\tstd::ofstream fileStream(std::filesystem::u8path(file), std::ios::binary);\n\tif (fileStream.fail())\n\t\tthrow strprintf(\"Failed to open file '%s': %s\", file, strerror(errno));\n\n\tfileStream.write(data.data(), data.size());\n\tif (fileStream.fail())\n\t\tthrow strprintf(\"Failed to write file '%s': %s\", file, strerror(errno));\n\n\treturn true;\n\n} catch (std::string &text) {\n\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, text.c_str());\n\treturn false;\n}\n\nstatic bool QuickReadFile(const char *file, std::string &data)\ntry {\n\tstd::ifstream fileStream(std::filesystem::u8path(file), std::ios::binary);\n\tif (!fileStream.is_open() || fileStream.fail())\n\t\tthrow strprintf(\"Failed to open file '%s': %s\", file, strerror(errno));\n\n\tfileStream.seekg(0, fileStream.end);\n\tsize_t size = fileStream.tellg();\n\tfileStream.seekg(0);\n\n\tdata.resize(size);\n\tfileStream.read(&data[0], size);\n\n\tif (fileStream.fail())\n\t\tthrow strprintf(\"Failed to write file '%s': %s\", file, strerror(errno));\n\n\treturn true;\n\n} catch (std::string &text) {\n\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, text.c_str());\n\treturn false;\n}\n\nstatic bool CalculateFileHash(const char *path, uint8_t *hash)\ntry {\n\tblake2b_state blake2;\n\tif (blake2b_init(&blake2, BLAKE2_HASH_LENGTH) != 0)\n\t\treturn false;\n\n\tstd::ifstream file(std::filesystem::u8path(path), std::ios::binary);\n\tif (!file.is_open() || file.fail())\n\t\treturn false;\n\n\tchar buf[HASH_READ_BUF_SIZE];\n\n\tfor (;;) {\n\t\tfile.read(buf, HASH_READ_BUF_SIZE);\n\t\tsize_t read = file.gcount();\n\t\tif (blake2b_update(&blake2, &buf, read) != 0)\n\t\t\treturn false;\n\t\tif (file.eof())\n\t\t\tbreak;\n\t}\n\n\tif (blake2b_final(&blake2, hash, BLAKE2_HASH_LENGTH) != 0)\n\t\treturn false;\n\n\treturn true;\n\n} catch (std::string &text) {\n\tblog(LOG_DEBUG, \"%s: %s\", __FUNCTION__, text.c_str());\n\treturn false;\n}\n\n/* ------------------------------------------------------------------------ */\n\nvoid GenerateGUID(std::string &guid)\n{\n\tconst char alphabet[] = \"0123456789abcdef\";\n\tQRandomGenerator *rng = QRandomGenerator::system();\n\n\tguid.resize(40);\n\n\tfor (size_t i = 0; i < 40; i++) {\n\t\tguid[i] = alphabet[rng->bounded(0, 16)];\n\t}\n}\n\nstd::string GetProgramGUID()\n{\n\tstatic std::mutex m;\n\tstd::lock_guard<std::mutex> lock(m);\n\n\t/* NOTE: this is an arbitrary random number that we use to count the\n\t * number of unique OBS installations and is not associated with any\n\t * kind of identifiable information */\n\tconst char *pguid = config_get_string(App()->GetAppConfig(), \"General\", \"InstallGUID\");\n\tstd::string guid;\n\tif (pguid)\n\t\tguid = pguid;\n\n\tif (guid.empty()) {\n\t\tGenerateGUID(guid);\n\n\t\tif (!guid.empty())\n\t\t\tconfig_set_string(App()->GetAppConfig(), \"General\", \"InstallGUID\", guid.c_str());\n\t}\n\n\treturn guid;\n}\n\n/* ------------------------------------------------------------------------ */\n\nstatic void LoadPublicKey(std::string &pubkey)\n{\n\tstd::string pemFilePath;\n\n\tif (!GetDataFilePath(\"OBSPublicRSAKey.pem\", pemFilePath))\n\t\tthrow std::string(\"Could not find OBS public key file!\");\n\tif (!QuickReadFile(pemFilePath.c_str(), pubkey))\n\t\tthrow std::string(\"Could not read OBS public key file!\");\n}\n\nstatic bool CheckDataSignature(const char *name, const std::string &data, const std::string &hexSig)\ntry {\n\tstatic std::mutex pubkey_mutex;\n\tstatic std::string obsPubKey;\n\n\tif (hexSig.empty() || hexSig.length() > 0xFFFF || (hexSig.length() & 1) != 0)\n\t\tthrow strprintf(\"Missing or invalid signature for %s: %s\", name, hexSig.c_str());\n\n\tstd::scoped_lock lock(pubkey_mutex);\n\tif (obsPubKey.empty())\n\t\tLoadPublicKey(obsPubKey);\n\n\t// Convert hex string to bytes\n\tauto signature = QByteArray::fromHex(hexSig.data());\n\n\tif (!VerifySignature((uint8_t *)obsPubKey.data(), obsPubKey.size(), (uint8_t *)data.data(), data.size(),\n\t\t\t     (uint8_t *)signature.data(), signature.size()))\n\t\tthrow strprintf(\"Signature check failed for %s\", name);\n\n\treturn true;\n\n} catch (std::string &text) {\n\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, text.c_str());\n\treturn false;\n}\n\n/* ------------------------------------------------------------------------ */\n\nbool FetchAndVerifyFile(const char *name, const char *file, const char *url, std::string *out,\n\t\t\tconst std::vector<std::string> &extraHeaders)\n{\n\tlong responseCode;\n\tstd::vector<std::string> headers;\n\tstd::string error;\n\tstd::string signature;\n\tstd::string data;\n\tuint8_t fileHash[BLAKE2_HASH_LENGTH];\n\tbool success;\n\n\tBPtr<char> filePath = GetAppConfigPathPtr(file);\n\n\tif (!extraHeaders.empty()) {\n\t\theaders.insert(headers.end(), extraHeaders.begin(), extraHeaders.end());\n\t}\n\n\t/* ----------------------------------- *\n\t * avoid downloading file again        */\n\n\tif (CalculateFileHash(filePath, fileHash)) {\n\t\tauto hash = QByteArray::fromRawData((const char *)fileHash, BLAKE2_HASH_LENGTH);\n\n\t\tQString header = \"If-None-Match: \" + hash.toHex();\n\t\theaders.push_back(header.toStdString());\n\t}\n\n\t/* ----------------------------------- *\n\t * get current install GUID            */\n\n\tstd::string guid = GetProgramGUID();\n\n\tif (!guid.empty()) {\n\t\tstd::string header = \"X-OBS2-GUID: \" + guid;\n\t\theaders.push_back(std::move(header));\n\t}\n\n\t/* ----------------------------------- *\n\t * get file from server                */\n\n\tsuccess = GetRemoteFile(url, data, error, &responseCode, nullptr, \"\", nullptr, headers, &signature);\n\n\tif (!success || (responseCode != 200 && responseCode != 304)) {\n\t\tif (responseCode == 404)\n\t\t\treturn false;\n\n\t\tthrow strprintf(\"Failed to fetch %s file: %s\", name, error.c_str());\n\t}\n\n\t/* ----------------------------------- *\n\t * verify file signature               */\n\n\tif (responseCode == 200) {\n\t\tsuccess = CheckDataSignature(name, data, signature);\n\t\tif (!success)\n\t\t\tthrow strprintf(\"Invalid %s signature\", name);\n\t}\n\n\t/* ----------------------------------- *\n\t * write or load file                  */\n\n\tif (responseCode == 200) {\n\t\tif (!QuickWriteFile(filePath, data))\n\t\t\tthrow strprintf(\"Could not write file '%s'\", filePath.Get());\n\t} else if (out) { /* Only read file if caller wants data */\n\t\tif (!QuickReadFile(filePath, data))\n\t\t\tthrow strprintf(\"Could not read file '%s'\", filePath.Get());\n\t}\n\n\tif (out)\n\t\t*out = data;\n\n\t/* ----------------------------------- *\n\t * success                             */\n\treturn true;\n}\n\nvoid WhatsNewInfoThread::run()\ntry {\n\tstd::string text;\n\n\tif (FetchAndVerifyFile(\"whatsnew\", \"obs-studio/updates/whatsnew.json\", WHATSNEW_URL, &text)) {\n\t\temit Result(QString::fromStdString(text));\n\t}\n} catch (std::string &text) {\n\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, text.c_str());\n}\n"
  },
  {
    "path": "frontend/utility/WhatsNewInfoThread.hpp",
    "content": "#pragma once\n\n#include <QThread>\n\nbool FetchAndVerifyFile(const char *name, const char *file, const char *url, std::string *out,\n\t\t\tconst std::vector<std::string> &extraHeaders = std::vector<std::string>());\n\nclass WhatsNewInfoThread : public QThread {\n\tQ_OBJECT\n\n\tvirtual void run() override;\n\nsignals:\n\tvoid Result(const QString &text);\n\npublic:\n\tinline WhatsNewInfoThread() {}\n};\n"
  },
  {
    "path": "frontend/utility/YoutubeApiWrappers.cpp",
    "content": "#include \"YoutubeApiWrappers.hpp\"\n\n#include <utility/RemoteTextThread.hpp>\n#include <utility/obf.h>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n#include <ui-config.h>\n\n#include <QFile>\n#include <QMimeDatabase>\n\n#include \"moc_YoutubeApiWrappers.cpp\"\n\nusing namespace json11;\n\nnamespace {\nusing std::string_view_literals::operator\"\"sv;\n\nconstexpr auto youtubeLiveStreamUrl = \"https://www.googleapis.com/youtube/v3/liveStreams\"sv;\nconstexpr auto youtubeLiveBroadcastUrl = \"https://www.googleapis.com/youtube/v3/liveBroadcasts\"sv;\nconstexpr auto youtubeLiveBroadcastTransitionUrl = \"https://www.googleapis.com/youtube/v3/liveBroadcasts/transition\"sv;\nconstexpr auto youtubeLiveBroadcastBindUrl = \"https://www.googleapis.com/youtube/v3/liveBroadcasts/bind\"sv;\n\nconstexpr auto youtubeLiveChannelUrl = \"https://www.googleapis.com/youtube/v3/channels\"sv;\nconstexpr auto youtubeLiveTokenUrl = \"https://oauth2.googleapis.com/token\"sv;\nconstexpr auto youtubeLiveVideoCategoriesUrl = \"https://www.googleapis.com/youtube/v3/videoCategories\"sv;\nconstexpr auto youtubeLiveVideosUrl = \"https://www.googleapis.com/youtube/v3/videos\"sv;\nconstexpr auto youtubeLiveThumbnailUrl = \"https://www.googleapis.com/upload/youtube/v3/thumbnails/set\"sv;\n\nconstexpr auto defaultBroadcastsPerQuery = 50; // acceptable values are 0 to 50, inclusive\n} // namespace\n\nbool IsYouTubeService(const std::string &service)\n{\n\tauto it = find_if(youtubeServices.begin(), youtubeServices.end(),\n\t\t\t  [&service](const Auth::Def &yt) { return service == yt.service; });\n\treturn it != youtubeServices.end();\n}\nbool IsUserSignedIntoYT()\n{\n\tAuth *auth = OBSBasic::Get()->GetAuth();\n\tif (auth) {\n\t\tYoutubeApiWrappers *apiYouTube(dynamic_cast<YoutubeApiWrappers *>(auth));\n\t\tif (apiYouTube) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nbool YoutubeApiWrappers::GetTranslatedError(QString &error_message)\n{\n\tconst QString errorKey = \"YouTube.Errors.\" + lastErrorReason.toUtf8();\n\tconst QString translated = QTStr(QT_TO_UTF8(errorKey));\n\t// No translation found\n\tif (translated.startsWith(\"YouTube.Errors.\"))\n\t\treturn false;\n\terror_message = translated;\n\treturn true;\n}\n\nYoutubeApiWrappers::YoutubeApiWrappers(const Def &d) : YoutubeAuth(d) {}\n\nbool YoutubeApiWrappers::TryInsertCommand(const char *url, const char *content_type, std::string request_type,\n\t\t\t\t\t  const char *data, Json &json_out, long *error_code, int data_size)\n{\n\tlong httpStatusCode = 0;\n\n#ifdef _DEBUG\n\tblog(LOG_DEBUG, \"YouTube API command URL: %s\", url);\n\tif (data && data[0] == '{') // only log JSON data\n\t\tblog(LOG_DEBUG, \"YouTube API command data: %s\", data);\n#endif\n\tif (token.empty())\n\t\treturn false;\n\tstd::string output;\n\tstd::string error;\n\t// Increase timeout by the time it takes to transfer `data_size` at 1 Mbps\n\tint timeout = 60 + data_size / 125000;\n\tbool success = GetRemoteFile(url, output, error, &httpStatusCode, content_type, request_type, data,\n\t\t\t\t     {\"Authorization: Bearer \" + token}, nullptr, timeout, false, data_size);\n\tif (error_code)\n\t\t*error_code = httpStatusCode;\n\n\tif (!success || output.empty()) {\n\t\tif (!error.empty())\n\t\t\tblog(LOG_WARNING, \"YouTube API request failed: %s\", error.c_str());\n\t\treturn false;\n\t}\n\n\tjson_out = Json::parse(output, error);\n#ifdef _DEBUG\n\tblog(LOG_DEBUG, \"YouTube API command answer: %s\", json_out.dump().c_str());\n#endif\n\tif (!error.empty()) {\n\t\treturn false;\n\t}\n\treturn httpStatusCode < 400;\n}\n\nbool YoutubeApiWrappers::UpdateAccessToken()\n{\n\tif (refresh_token.empty()) {\n\t\treturn false;\n\t}\n\n\tstd::string clientid = YOUTUBE_CLIENTID;\n\tstd::string secret = YOUTUBE_SECRET;\n\tdeobfuscate_str(&clientid[0], YOUTUBE_CLIENTID_HASH);\n\tdeobfuscate_str(&secret[0], YOUTUBE_SECRET_HASH);\n\n\tstd::string r_token = QUrl::toPercentEncoding(refresh_token.c_str()).toStdString();\n\tconst QString data_template = \"client_id=%1\"\n\t\t\t\t      \"&client_secret=%2\"\n\t\t\t\t      \"&refresh_token=%3\"\n\t\t\t\t      \"&grant_type=refresh_token\";\n\tconst QString data =\n\t\tdata_template.arg(QString(clientid.c_str()), QString(secret.c_str()), QString(r_token.c_str()));\n\tJson json_out;\n\tbool success = TryInsertCommand(youtubeLiveTokenUrl.data(), \"application/x-www-form-urlencoded\", \"\",\n\t\t\t\t\tQT_TO_UTF8(data), json_out);\n\n\tif (!success || json_out.object_items().find(\"error\") != json_out.object_items().end())\n\t\treturn false;\n\ttoken = json_out[\"access_token\"].string_value();\n\treturn token.empty() ? false : true;\n}\n\nbool YoutubeApiWrappers::InsertCommand(const char *url, const char *content_type, std::string request_type,\n\t\t\t\t       const char *data, Json &json_out, int data_size)\n{\n\tlong error_code;\n\tbool success = TryInsertCommand(url, content_type, request_type, data, json_out, &error_code, data_size);\n\n\tif (error_code == 401) {\n\t\t// Attempt to update access token and try again\n\t\tif (!UpdateAccessToken())\n\t\t\treturn false;\n\t\tsuccess = TryInsertCommand(url, content_type, request_type, data, json_out, &error_code, data_size);\n\t}\n\n\tif (json_out.object_items().find(\"error\") != json_out.object_items().end()) {\n\t\tblog(LOG_ERROR, \"YouTube API error:\\n\\tHTTP status: %ld\\n\\tURL: %s\\n\\tJSON: %s\", error_code, url,\n\t\t     json_out.dump().c_str());\n\n\t\tlastError = json_out[\"error\"][\"code\"].int_value();\n\t\tlastErrorReason = QString(json_out[\"error\"][\"errors\"][0][\"reason\"].string_value().c_str());\n\t\tlastErrorMessage = QString(json_out[\"error\"][\"message\"].string_value().c_str());\n\n\t\t// The existence of an error implies non-success even if the HTTP status code disagrees.\n\t\tsuccess = false;\n\t}\n\treturn success;\n}\n\nbool YoutubeApiWrappers::GetChannelDescription(ChannelDescription &channel_description)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\n\tconst std::string url =\n\t\tstd::string(youtubeLiveChannelUrl) + \"?part=snippet,contentDetails,statistics&mine=true\";\n\tJson json_out;\n\tif (!InsertCommand(url.c_str(), \"application/json\", \"\", nullptr, json_out)) {\n\t\treturn false;\n\t}\n\n\tif (json_out[\"pageInfo\"][\"totalResults\"].int_value() == 0) {\n\t\tlastErrorMessage = QTStr(\"YouTube.Auth.NoChannels\");\n\t\treturn false;\n\t}\n\n\tchannel_description.id = QString(json_out[\"items\"][0][\"id\"].string_value().c_str());\n\tchannel_description.title = QString(json_out[\"items\"][0][\"snippet\"][\"title\"].string_value().c_str());\n\treturn channel_description.id.isEmpty() ? false : true;\n}\n\nbool YoutubeApiWrappers::InsertBroadcast(BroadcastDescription &broadcast)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\tconst std::string url = std::string(youtubeLiveBroadcastUrl) + \"?part=snippet,status,contentDetails\";\n\tconst Json data = Json::object{\n\t\t{\"snippet\",\n\t\t Json::object{\n\t\t\t {\"title\", QT_TO_UTF8(broadcast.title)},\n\t\t\t {\"description\", QT_TO_UTF8(broadcast.description)},\n\t\t\t {\"scheduledStartTime\", QT_TO_UTF8(broadcast.schedul_date_time)},\n\t\t }},\n\t\t{\"status\",\n\t\t Json::object{\n\t\t\t {\"privacyStatus\", QT_TO_UTF8(broadcast.privacy)},\n\t\t\t {\"selfDeclaredMadeForKids\", broadcast.made_for_kids},\n\t\t }},\n\t\t{\"contentDetails\",\n\t\t Json::object{\n\t\t\t {\"latencyPreference\", QT_TO_UTF8(broadcast.latency)},\n\t\t\t {\"enableAutoStart\", broadcast.auto_start},\n\t\t\t {\"enableAutoStop\", broadcast.auto_stop},\n\t\t\t {\"enableDvr\", broadcast.dvr},\n\t\t\t {\"projection\", QT_TO_UTF8(broadcast.projection)},\n\t\t\t {\n\t\t\t\t \"monitorStream\",\n\t\t\t\t Json::object{\n\t\t\t\t\t {\"enableMonitorStream\", false},\n\t\t\t\t },\n\t\t\t },\n\t\t }},\n\t};\n\tJson json_out;\n\tif (!InsertCommand(url.c_str(), \"application/json\", \"\", data.dump().c_str(), json_out)) {\n\t\treturn false;\n\t}\n\tbroadcast.id = QString(json_out[\"id\"].string_value().c_str());\n\treturn broadcast.id.isEmpty() ? false : true;\n}\n\nbool YoutubeApiWrappers::InsertStream(StreamDescription &stream)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\tconst std::string url = std::string(youtubeLiveStreamUrl) + \"?part=snippet,cdn,status,contentDetails\";\n\tconst Json data = Json::object{\n\t\t{\"snippet\",\n\t\t Json::object{\n\t\t\t {\"title\", QT_TO_UTF8(stream.title)},\n\t\t }},\n\t\t{\"cdn\",\n\t\t Json::object{\n\t\t\t {\"frameRate\", \"variable\"},\n\t\t\t {\"ingestionType\", \"rtmp\"},\n\t\t\t {\"resolution\", \"variable\"},\n\t\t }},\n\t\t{\"contentDetails\", Json::object{{\"isReusable\", false}}},\n\t};\n\tJson json_out;\n\tif (!InsertCommand(url.c_str(), \"application/json\", \"\", data.dump().c_str(), json_out)) {\n\t\treturn false;\n\t}\n\tstream.id = QString(json_out[\"id\"].string_value().c_str());\n\tstream.name = QString(json_out[\"cdn\"][\"ingestionInfo\"][\"streamName\"].string_value().c_str());\n\treturn stream.id.isEmpty() ? false : true;\n}\n\nbool YoutubeApiWrappers::BindStream(const QString broadcast_id, const QString stream_id)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\t// TODO: Use std::format with std::string instead of QString::arg with C++20\n\tconst QString url_template = QString(youtubeLiveBroadcastBindUrl.data()) +\n\t\t\t\t     \"?id=%1&streamId=%2&part=id,snippet,contentDetails,status\";\n\tconst QString url = url_template.arg(broadcast_id, stream_id);\n\tconst Json data = Json::object{};\n\tthis->broadcast_id = broadcast_id;\n\tJson json_out;\n\treturn InsertCommand(QT_TO_UTF8(url), \"application/json\", \"\", data.dump().c_str(), json_out);\n}\n\nbool YoutubeApiWrappers::GetBroadcastsList(Json &json_out, const QString &page, const QString &status)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\tstd::string url = std::string(youtubeLiveBroadcastUrl) +\n\t\t\t  \"?part=snippet,contentDetails,status&broadcastType=all&maxResults=\" +\n\t\t\t  std::to_string(defaultBroadcastsPerQuery);\n\n\tif (status.isEmpty())\n\t\turl += \"&mine=true\";\n\telse\n\t\turl += \"&broadcastStatus=\" + status.toStdString();\n\n\tif (!page.isEmpty())\n\t\turl += \"&pageToken=\" + page.toStdString();\n\treturn InsertCommand(url.c_str(), \"application/json\", \"\", nullptr, json_out);\n}\n\nbool YoutubeApiWrappers::GetVideoCategoriesList(QVector<CategoryDescription> &category_list_out)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\t// TODO: Use std::format with C++20\n\tconst QString url_template =\n\t\tQString(youtubeLiveVideoCategoriesUrl.data()) + \"?part=snippet&regionCode=%1&hl=%2\";\n\n\t/*\n\t * All OBS locale regions aside from \"US\" are missing category id 29\n\t * (\"Nonprofits & Activism\"), but it is still available to channels\n\t * set to those regions via the YouTube Studio website.\n\t * To work around this inconsistency with the API all locales will\n\t * use the \"US\" region and only set the language part for localisation.\n\t * It is worth noting that none of the regions available on YouTube\n\t * feature any category not also available to the \"US\" region.\n\t */\n\tQString url = url_template.arg(\"US\", QLocale().name());\n\n\tJson json_out;\n\tif (!InsertCommand(QT_TO_UTF8(url), \"application/json\", \"\", nullptr, json_out)) {\n\t\tif (lastErrorReason != \"unsupportedLanguageCode\" && lastErrorReason != \"invalidLanguage\")\n\t\t\treturn false;\n\t\t// Try again with en-US if YouTube error indicates an unsupported locale\n\t\turl = url_template.arg(\"US\", \"en_US\");\n\t\tif (!InsertCommand(QT_TO_UTF8(url), \"application/json\", \"\", nullptr, json_out))\n\t\t\treturn false;\n\t}\n\tcategory_list_out = {};\n\tfor (auto &j : json_out[\"items\"].array_items()) {\n\t\t// Assignable only.\n\t\tif (j[\"snippet\"][\"assignable\"].bool_value()) {\n\t\t\tcategory_list_out.push_back(\n\t\t\t\t{j[\"id\"].string_value().c_str(), j[\"snippet\"][\"title\"].string_value().c_str()});\n\t\t}\n\t}\n\treturn category_list_out.isEmpty() ? false : true;\n}\n\nbool YoutubeApiWrappers::SetVideoCategory(const QString &video_id, const QString &video_title,\n\t\t\t\t\t  const QString &video_description, const QString &categorie_id)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\tconst std::string url = std::string(youtubeLiveVideosUrl) + \"?part=snippet\";\n\tconst Json data = Json::object{\n\t\t{\"id\", QT_TO_UTF8(video_id)},\n\t\t{\"snippet\",\n\t\t Json::object{\n\t\t\t {\"title\", QT_TO_UTF8(video_title)},\n\t\t\t {\"description\", QT_TO_UTF8(video_description)},\n\t\t\t {\"categoryId\", QT_TO_UTF8(categorie_id)},\n\t\t }},\n\t};\n\tJson json_out;\n\treturn InsertCommand(url.c_str(), \"application/json\", \"PUT\", data.dump().c_str(), json_out);\n}\n\nbool YoutubeApiWrappers::SetVideoThumbnail(const QString &video_id, const QString &thumbnail_file)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\n\t// Make sure the file hasn't been deleted since originally selecting it\n\tif (!QFile::exists(thumbnail_file)) {\n\t\tlastErrorMessage = QTStr(\"YouTube.Actions.Error.FileMissing\");\n\t\treturn false;\n\t}\n\n\tQFile thumbFile(thumbnail_file);\n\tif (!thumbFile.open(QFile::ReadOnly)) {\n\t\tlastErrorMessage = QTStr(\"YouTube.Actions.Error.FileOpeningFailed\");\n\t\treturn false;\n\t}\n\n\tconst QByteArray fileContents = thumbFile.readAll();\n\tconst QString mime = QMimeDatabase().mimeTypeForData(fileContents).name();\n\n\tconst std::string url = std::string(youtubeLiveThumbnailUrl) + \"?videoId=\" + video_id.toStdString();\n\tJson json_out;\n\treturn InsertCommand(url.c_str(), QT_TO_UTF8(mime), \"POST\", fileContents.constData(), json_out,\n\t\t\t     fileContents.size());\n}\n\nbool YoutubeApiWrappers::StartBroadcast(const QString &broadcast_id)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\n\tJson json_out;\n\tif (!FindBroadcast(broadcast_id, json_out))\n\t\treturn false;\n\n\tauto lifeCycleStatus = json_out[\"items\"][0][\"status\"][\"lifeCycleStatus\"].string_value();\n\n\tif (lifeCycleStatus == \"live\" || lifeCycleStatus == \"liveStarting\")\n\t\t// Broadcast is already (going to be) live\n\t\treturn true;\n\telse if (lifeCycleStatus == \"testStarting\") {\n\t\t// User will need to wait a few seconds before attempting to start broadcast\n\t\tlastErrorMessage = QTStr(\"YouTube.Actions.Error.BroadcastTestStarting\");\n\t\tlastErrorReason.clear();\n\t\treturn false;\n\t}\n\n\t// Only reset if broadcast has monitoring enabled and is not already in \"testing\" mode\n\tauto monitorStreamEnabled =\n\t\tjson_out[\"items\"][0][\"contentDetails\"][\"monitorStream\"][\"enableMonitorStream\"].bool_value();\n\tif (lifeCycleStatus != \"testing\" && monitorStreamEnabled && !ResetBroadcast(broadcast_id, json_out))\n\t\treturn false;\n\n\t// TODO: Use std::format with C++20\n\tconst QString url_template =\n\t\tQString(youtubeLiveBroadcastTransitionUrl.data()) + \"?id=%1&broadcastStatus=%2&part=status\";\n\tconst QString live = url_template.arg(broadcast_id, \"live\");\n\tbool success = InsertCommand(QT_TO_UTF8(live), \"application/json\", \"POST\", \"{}\", json_out);\n\t// Return a success if the command failed, but was redundant (broadcast already live)\n\treturn success || lastErrorReason == \"redundantTransition\";\n}\n\nbool YoutubeApiWrappers::StartLatestBroadcast()\n{\n\treturn StartBroadcast(this->broadcast_id);\n}\n\nbool YoutubeApiWrappers::StopBroadcast(const QString &broadcast_id)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\n\tconst QString url_template =\n\t\tQString(youtubeLiveBroadcastTransitionUrl.data()) + \"?id=%1&broadcastStatus=complete&part=status\";\n\tconst QString url = url_template.arg(broadcast_id);\n\tJson json_out;\n\tbool success = InsertCommand(QT_TO_UTF8(url), \"application/json\", \"POST\", \"{}\", json_out);\n\t// Return a success if the command failed, but was redundant (broadcast already stopped)\n\treturn success || lastErrorReason == \"redundantTransition\";\n}\n\nbool YoutubeApiWrappers::StopLatestBroadcast()\n{\n\treturn StopBroadcast(this->broadcast_id);\n}\n\nvoid YoutubeApiWrappers::SetBroadcastId(QString &broadcast_id)\n{\n\tthis->broadcast_id = broadcast_id;\n}\n\nQString YoutubeApiWrappers::GetBroadcastId()\n{\n\treturn this->broadcast_id;\n}\n\nbool YoutubeApiWrappers::ResetBroadcast(const QString &broadcast_id, json11::Json &json_out)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\n\tauto snippet = json_out[\"items\"][0][\"snippet\"];\n\tauto status = json_out[\"items\"][0][\"status\"];\n\tauto contentDetails = json_out[\"items\"][0][\"contentDetails\"];\n\tauto monitorStream = contentDetails[\"monitorStream\"];\n\n\tconst Json data = Json::object{\n\t\t{\"id\", QT_TO_UTF8(broadcast_id)},\n\t\t{\"snippet\",\n\t\t Json::object{\n\t\t\t {\"title\", snippet[\"title\"]},\n\t\t\t {\"description\", snippet[\"description\"]},\n\t\t\t {\"scheduledStartTime\", snippet[\"scheduledStartTime\"]},\n\t\t\t {\"scheduledEndTime\", snippet[\"scheduledEndTime\"]},\n\t\t }},\n\t\t{\"status\",\n\t\t Json::object{\n\t\t\t {\"privacyStatus\", status[\"privacyStatus\"]},\n\t\t\t {\"madeForKids\", status[\"madeForKids\"]},\n\t\t\t {\"selfDeclaredMadeForKids\", status[\"selfDeclaredMadeForKids\"]},\n\t\t }},\n\t\t{\"contentDetails\",\n\t\t Json::object{\n\t\t\t {\n\t\t\t\t \"monitorStream\",\n\t\t\t\t Json::object{\n\t\t\t\t\t {\"enableMonitorStream\", false},\n\t\t\t\t\t {\"broadcastStreamDelayMs\", monitorStream[\"broadcastStreamDelayMs\"]},\n\t\t\t\t },\n\t\t\t },\n\t\t\t {\"enableAutoStart\", contentDetails[\"enableAutoStart\"]},\n\t\t\t {\"enableAutoStop\", contentDetails[\"enableAutoStop\"]},\n\t\t\t {\"enableClosedCaptions\", contentDetails[\"enableClosedCaptions\"]},\n\t\t\t {\"enableDvr\", contentDetails[\"enableDvr\"]},\n\t\t\t {\"enableContentEncryption\", contentDetails[\"enableContentEncryption\"]},\n\t\t\t {\"enableEmbed\", contentDetails[\"enableEmbed\"]},\n\t\t\t {\"recordFromStart\", contentDetails[\"recordFromStart\"]},\n\t\t\t {\"startWithSlate\", contentDetails[\"startWithSlate\"]},\n\t\t }},\n\t};\n\n\tconst std::string put = std::string(youtubeLiveBroadcastUrl) + \"?part=id,snippet,contentDetails,status\";\n\treturn InsertCommand(put.c_str(), \"application/json\", \"PUT\", data.dump().c_str(), json_out);\n}\n\nbool YoutubeApiWrappers::FindBroadcast(const QString &id, json11::Json &json_out)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\tstd::string url = std::string(youtubeLiveBroadcastUrl) +\n\t\t\t  \"?part=id,snippet,contentDetails,status&broadcastType=all&maxResults=1\";\n\turl += \"&id=\" + id.toStdString();\n\n\tif (!InsertCommand(url.c_str(), \"application/json\", \"\", nullptr, json_out))\n\t\treturn false;\n\n\tauto items = json_out[\"items\"].array_items();\n\tif (items.size() != 1) {\n\t\tlastErrorMessage = QTStr(\"YouTube.Actions.Error.BroadcastNotFound\");\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool YoutubeApiWrappers::FindStream(const QString &id, json11::Json &json_out)\n{\n\tlastErrorMessage.clear();\n\tlastErrorReason.clear();\n\tstd::string url = std::string(youtubeLiveStreamUrl) + \"?part=id,snippet,cdn,status&maxResults=1\";\n\turl += \"&id=\" + id.toStdString();\n\n\tif (!InsertCommand(url.c_str(), \"application/json\", \"\", nullptr, json_out))\n\t\treturn false;\n\n\tauto items = json_out[\"items\"].array_items();\n\tif (items.size() != 1) {\n\t\tlastErrorMessage = \"No active broadcast found.\";\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "frontend/utility/YoutubeApiWrappers.hpp",
    "content": "#pragma once\n\n#include <oauth/YoutubeAuth.hpp>\n\n#include <json11.hpp>\n\n#include <QString>\n\nstruct ChannelDescription {\n\tQString id;\n\tQString title;\n};\n\nstruct StreamDescription {\n\tQString id;\n\tQString name;\n\tQString title;\n};\n\nstruct CategoryDescription {\n\tQString id;\n\tQString title;\n};\n\nstruct BroadcastDescription {\n\tQString id;\n\tQString title;\n\tQString description;\n\tQString privacy;\n\tCategoryDescription category;\n\tQString latency;\n\tbool made_for_kids;\n\tbool auto_start;\n\tbool auto_stop;\n\tbool dvr;\n\tbool schedul_for_later;\n\tQString schedul_date_time;\n\tQString projection;\n};\n\nbool IsYouTubeService(const std::string &service);\nbool IsUserSignedIntoYT();\n\nclass YoutubeApiWrappers : public YoutubeAuth {\n\tQ_OBJECT\n\n\tbool TryInsertCommand(const char *url, const char *content_type, std::string request_type, const char *data,\n\t\t\t      json11::Json &ret, long *error_code = nullptr, int data_size = 0);\n\tbool UpdateAccessToken();\n\tbool InsertCommand(const char *url, const char *content_type, std::string request_type, const char *data,\n\t\t\t   json11::Json &ret, int data_size = 0);\n\npublic:\n\tYoutubeApiWrappers(const Def &d);\n\n\tbool GetChannelDescription(ChannelDescription &channel_description);\n\tbool InsertBroadcast(BroadcastDescription &broadcast);\n\tbool InsertStream(StreamDescription &stream);\n\tbool BindStream(const QString broadcast_id, const QString stream_id);\n\tbool GetBroadcastsList(json11::Json &json_out, const QString &page, const QString &status);\n\tbool GetVideoCategoriesList(QVector<CategoryDescription> &category_list_out);\n\tbool SetVideoCategory(const QString &video_id, const QString &video_title, const QString &video_description,\n\t\t\t      const QString &categorie_id);\n\tbool SetVideoThumbnail(const QString &video_id, const QString &thumbnail_file);\n\tbool StartBroadcast(const QString &broadcast_id);\n\tbool StopBroadcast(const QString &broadcast_id);\n\tbool ResetBroadcast(const QString &broadcast_id, json11::Json &json_out);\n\tbool StartLatestBroadcast();\n\tbool StopLatestBroadcast();\n\n\tvoid SetBroadcastId(QString &broadcast_id);\n\tQString GetBroadcastId();\n\n\tbool FindBroadcast(const QString &id, json11::Json &json_out);\n\tbool FindStream(const QString &id, json11::Json &json_out);\n\n\tQString GetLastError() { return lastErrorMessage; };\n\tbool GetTranslatedError(QString &error_message);\n\nprivate:\n\tQString broadcast_id;\n\n\tint lastError;\n\tQString lastErrorMessage;\n\tQString lastErrorReason;\n};\n"
  },
  {
    "path": "frontend/utility/audio-encoders.cpp",
    "content": "#include \"audio-encoders.hpp\"\n\n#include <OBSApp.hpp>\n#include <widgets/OBSMainWindow.hpp>\n\n#include <mutex>\n#include <sstream>\n\nusing namespace std;\n\nstatic const char *NullToEmpty(const char *str)\n{\n\treturn str ? str : \"\";\n}\n\nstatic const char *EncoderName(const std::string &id)\n{\n\treturn NullToEmpty(obs_encoder_get_display_name(id.c_str()));\n}\n\nstatic void HandleIntProperty(obs_property_t *prop, std::vector<int> &bitrates)\n{\n\tconst int max_ = obs_property_int_max(prop);\n\tconst int step = obs_property_int_step(prop);\n\n\tfor (int i = obs_property_int_min(prop); i <= max_; i += step)\n\t\tbitrates.push_back(i);\n}\n\nstatic void HandleListProperty(obs_property_t *prop, const char *id, std::vector<int> &bitrates)\n{\n\tobs_combo_format format = obs_property_list_format(prop);\n\tif (format != OBS_COMBO_FORMAT_INT) {\n\t\tblog(LOG_ERROR,\n\t\t     \"Encoder '%s' (%s) returned bitrate \"\n\t\t     \"OBS_PROPERTY_LIST property of unhandled \"\n\t\t     \"format %d\",\n\t\t     EncoderName(id), id, static_cast<int>(format));\n\t\treturn;\n\t}\n\n\tconst size_t count = obs_property_list_item_count(prop);\n\tfor (size_t i = 0; i < count; i++) {\n\t\tif (obs_property_list_item_disabled(prop, i))\n\t\t\tcontinue;\n\n\t\tint bitrate = static_cast<int>(obs_property_list_item_int(prop, i));\n\t\tbitrates.push_back(bitrate);\n\t}\n}\n\nstatic void HandleSampleRate(obs_property_t *prop, const char *id)\n{\n\tauto ReleaseData = [](obs_data_t *data) {\n\t\tobs_data_release(data);\n\t};\n\tstd::unique_ptr<obs_data_t, decltype(ReleaseData)> data{obs_encoder_defaults(id), ReleaseData};\n\n\tif (!data) {\n\t\tblog(LOG_ERROR,\n\t\t     \"Failed to get defaults for encoder '%s' (%s) \"\n\t\t     \"while populating bitrate map\",\n\t\t     EncoderName(id), id);\n\t\treturn;\n\t}\n\n\tauto main = reinterpret_cast<OBSMainWindow *>(App()->GetMainWindow());\n\tif (!main) {\n\t\tblog(LOG_ERROR, \"Failed to get main window while populating \"\n\t\t\t\t\"bitrate map\");\n\t\treturn;\n\t}\n\n\tuint32_t sampleRate = config_get_uint(main->Config(), \"Audio\", \"SampleRate\");\n\n\tobs_data_set_int(data.get(), \"samplerate\", sampleRate);\n\n\tobs_property_modified(prop, data.get());\n}\n\nstatic void HandleEncoderProperties(const char *id, std::vector<int> &bitrates)\n{\n\tauto DestroyProperties = [](obs_properties_t *props) {\n\t\tobs_properties_destroy(props);\n\t};\n\tstd::unique_ptr<obs_properties_t, decltype(DestroyProperties)> props{obs_get_encoder_properties(id),\n\t\t\t\t\t\t\t\t\t     DestroyProperties};\n\n\tif (!props) {\n\t\tblog(LOG_ERROR,\n\t\t     \"Failed to get properties for encoder \"\n\t\t     \"'%s' (%s)\",\n\t\t     EncoderName(id), id);\n\t\treturn;\n\t}\n\n\tobs_property_t *samplerate = obs_properties_get(props.get(), \"samplerate\");\n\tif (samplerate)\n\t\tHandleSampleRate(samplerate, id);\n\n\tobs_property_t *bitrate = obs_properties_get(props.get(), \"bitrate\");\n\n\tobs_property_type type = obs_property_get_type(bitrate);\n\tswitch (type) {\n\tcase OBS_PROPERTY_INT:\n\t\treturn HandleIntProperty(bitrate, bitrates);\n\n\tcase OBS_PROPERTY_LIST:\n\t\treturn HandleListProperty(bitrate, id, bitrates);\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\tblog(LOG_ERROR,\n\t     \"Encoder '%s' (%s) returned bitrate property \"\n\t     \"of unhandled type %d\",\n\t     EncoderName(id), id, static_cast<int>(type));\n}\n\nstatic const char *GetCodec(const char *id)\n{\n\treturn NullToEmpty(obs_get_encoder_codec(id));\n}\n\nstatic std::vector<int> fallbackBitrates;\nstatic map<std::string, std::vector<int>> encoderBitrates;\n\nstatic void PopulateBitrateLists()\n{\n\tstatic once_flag once;\n\n\tcall_once(once, []() {\n\t\tstruct obs_audio_info aoi;\n\t\tobs_get_audio_info(&aoi);\n\n\t\t/* NOTE: ffmpeg_aac and ffmpeg_opus have the same properties\n\t\t * their bitrates will also be used as a fallback */\n\t\tHandleEncoderProperties(\"ffmpeg_aac\", fallbackBitrates);\n\n\t\tif (fallbackBitrates.empty())\n\t\t\tblog(LOG_ERROR, \"Could not enumerate fallback encoder \"\n\t\t\t\t\t\"bitrates\");\n\n\t\tostringstream ss;\n\t\tfor (auto &bitrate : fallbackBitrates)\n\t\t\tss << \"\\n\t\" << setw(3) << bitrate << \" kbit/s:\";\n\n\t\tblog(LOG_DEBUG, \"Fallback encoder bitrates:%s\", ss.str().c_str());\n\n\t\tconst char *id = nullptr;\n\t\tfor (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {\n\t\t\tif (obs_get_encoder_type(id) != OBS_ENCODER_AUDIO)\n\t\t\t\tcontinue;\n\n\t\t\tif (strcmp(id, \"ffmpeg_aac\") == 0 || strcmp(id, \"ffmpeg_opus\") == 0)\n\t\t\t\tcontinue;\n\n\t\t\tstd::string encoder = id;\n\n\t\t\tHandleEncoderProperties(id, encoderBitrates[encoder]);\n\n\t\t\tif (encoderBitrates[encoder].empty())\n\t\t\t\tblog(LOG_ERROR,\n\t\t\t\t     \"Could not enumerate %s encoder \"\n\t\t\t\t     \"bitrates\",\n\t\t\t\t     id);\n\n\t\t\tostringstream ss;\n\t\t\tfor (auto &bitrate : encoderBitrates[encoder])\n\t\t\t\tss << \"\\n\t\" << setw(3) << bitrate << \" kbit/s\";\n\n\t\t\tblog(LOG_DEBUG, \"%s (%s) encoder bitrates:%s\", EncoderName(id), id, ss.str().c_str());\n\t\t}\n\n\t\tif (encoderBitrates.empty() && fallbackBitrates.empty())\n\t\t\tblog(LOG_ERROR, \"Could not enumerate any audio encoder \"\n\t\t\t\t\t\"bitrates\");\n\t});\n}\n\nstatic map<int, std::string> simpleAACBitrateMap;\n\nstatic void PopulateSimpleAACBitrateMap()\n{\n\tPopulateBitrateLists();\n\n\tstatic once_flag once;\n\n\tcall_once(once, []() {\n\t\tconst string encoders[] = {\n\t\t\t\"ffmpeg_aac\",\n\t\t\t\"libfdk_aac\",\n\t\t\t\"CoreAudio_AAC\",\n\t\t};\n\n\t\tconst string fallbackEncoder = encoders[0];\n\n\t\tstruct obs_audio_info aoi;\n\t\tobs_get_audio_info(&aoi);\n\n\t\tfor (auto &bitrate : fallbackBitrates)\n\t\t\tsimpleAACBitrateMap[bitrate] = fallbackEncoder;\n\n\t\tconst char *id = nullptr;\n\t\tfor (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {\n\t\t\tauto Compare = [=](const string &val) {\n\t\t\t\treturn val == NullToEmpty(id);\n\t\t\t};\n\n\t\t\tif (find_if(begin(encoders), end(encoders), Compare) != end(encoders))\n\t\t\t\tcontinue;\n\n\t\t\tif (strcmp(GetCodec(id), \"aac\") != 0)\n\t\t\t\tcontinue;\n\n\t\t\tstd::string encoder = id;\n\t\t\tif (encoderBitrates[encoder].empty())\n\t\t\t\tcontinue;\n\n\t\t\tfor (auto &bitrate : encoderBitrates[encoder])\n\t\t\t\tsimpleAACBitrateMap[bitrate] = encoder;\n\t\t}\n\n\t\tfor (auto &encoder : encoders) {\n\t\t\tif (encoder == fallbackEncoder)\n\t\t\t\tcontinue;\n\n\t\t\tif (strcmp(GetCodec(encoder.c_str()), \"aac\") != 0)\n\t\t\t\tcontinue;\n\n\t\t\tfor (auto &bitrate : encoderBitrates[encoder])\n\t\t\t\tsimpleAACBitrateMap[bitrate] = encoder;\n\t\t}\n\n\t\tif (simpleAACBitrateMap.empty()) {\n\t\t\tblog(LOG_ERROR, \"Could not enumerate any AAC encoder \"\n\t\t\t\t\t\"bitrates\");\n\t\t\treturn;\n\t\t}\n\n\t\tostringstream ss;\n\t\tfor (auto &entry : simpleAACBitrateMap)\n\t\t\tss << \"\\n\t\" << setw(3) << entry.first << \" kbit/s: '\" << EncoderName(entry.second)\n\t\t\t   << \"' (\" << entry.second << ')';\n\n\t\tblog(LOG_DEBUG, \"AAC simple encoder bitrate mapping:%s\", ss.str().c_str());\n\t});\n}\n\nstatic map<int, std::string> simpleOpusBitrateMap;\n\nstatic void PopulateSimpleOpusBitrateMap()\n{\n\tPopulateBitrateLists();\n\n\tstatic once_flag once;\n\n\tcall_once(once, []() {\n\t\tstruct obs_audio_info aoi;\n\t\tobs_get_audio_info(&aoi);\n\n\t\tfor (auto &bitrate : fallbackBitrates)\n\t\t\tsimpleOpusBitrateMap[bitrate] = \"ffmpeg_opus\";\n\n\t\tconst char *id = nullptr;\n\t\tfor (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {\n\t\t\tif (strcmp(GetCodec(id), \"opus\") != 0)\n\t\t\t\tcontinue;\n\n\t\t\tstd::string encoder = id;\n\t\t\tif (encoderBitrates[encoder].empty())\n\t\t\t\tcontinue;\n\n\t\t\tfor (auto &bitrate : encoderBitrates[encoder])\n\t\t\t\tsimpleOpusBitrateMap[bitrate] = encoder;\n\t\t}\n\n\t\tif (simpleOpusBitrateMap.empty()) {\n\t\t\tblog(LOG_ERROR, \"Could not enumerate any Opus encoder \"\n\t\t\t\t\t\"bitrates\");\n\t\t\treturn;\n\t\t}\n\n\t\tostringstream ss;\n\t\tfor (auto &entry : simpleOpusBitrateMap)\n\t\t\tss << \"\\n\t\" << setw(3) << entry.first << \" kbit/s: '\" << EncoderName(entry.second)\n\t\t\t   << \"' (\" << entry.second << ')';\n\n\t\tblog(LOG_DEBUG, \"Opus simple encoder bitrate mapping:%s\", ss.str().c_str());\n\t});\n}\n\nconst map<int, std::string> &GetSimpleAACEncoderBitrateMap()\n{\n\tPopulateSimpleAACBitrateMap();\n\treturn simpleAACBitrateMap;\n}\n\nconst map<int, std::string> &GetSimpleOpusEncoderBitrateMap()\n{\n\tPopulateSimpleOpusBitrateMap();\n\treturn simpleOpusBitrateMap;\n}\n\nconst char *GetSimpleAACEncoderForBitrate(int bitrate)\n{\n\tauto &map_ = GetSimpleAACEncoderBitrateMap();\n\tauto res = map_.find(bitrate);\n\tif (res == end(map_))\n\t\treturn NULL;\n\treturn res->second.c_str();\n}\n\nconst char *GetSimpleOpusEncoderForBitrate(int bitrate)\n{\n\tauto &map_ = GetSimpleOpusEncoderBitrateMap();\n\tauto res = map_.find(bitrate);\n\tif (res == end(map_))\n\t\treturn NULL;\n\treturn res->second.c_str();\n}\n\n#define INVALID_BITRATE 10000\n\nstatic int FindClosestAvailableSimpleBitrate(int bitrate, const map<int, std::string> &map)\n{\n\tint prev = 0;\n\tint next = INVALID_BITRATE;\n\n\tfor (auto val : map) {\n\t\tif (next > val.first) {\n\t\t\tif (val.first == bitrate)\n\t\t\t\treturn bitrate;\n\n\t\t\tif (val.first < next && val.first > bitrate)\n\t\t\t\tnext = val.first;\n\t\t\tif (val.first > prev && val.first < bitrate)\n\t\t\t\tprev = val.first;\n\t\t}\n\t}\n\n\tif (next != INVALID_BITRATE)\n\t\treturn next;\n\tif (prev != 0)\n\t\treturn prev;\n\treturn 192;\n}\n\nint FindClosestAvailableSimpleAACBitrate(int bitrate)\n{\n\treturn FindClosestAvailableSimpleBitrate(bitrate, GetSimpleAACEncoderBitrateMap());\n}\n\nint FindClosestAvailableSimpleOpusBitrate(int bitrate)\n{\n\treturn FindClosestAvailableSimpleBitrate(bitrate, GetSimpleOpusEncoderBitrateMap());\n}\n\nconst std::vector<int> &GetAudioEncoderBitrates(const char *id)\n{\n\tstd::string encoder = id;\n\tPopulateBitrateLists();\n\tif (encoderBitrates[encoder].empty())\n\t\treturn fallbackBitrates;\n\treturn encoderBitrates[encoder];\n}\n\nint FindClosestAvailableAudioBitrate(const char *id, int bitrate)\n{\n\tPopulateBitrateLists();\n\n\tint prev = 0;\n\tint next = INVALID_BITRATE;\n\tstd::string encoder = id;\n\n\tfor (auto val : encoderBitrates[encoder].empty() ? fallbackBitrates : encoderBitrates[encoder]) {\n\t\tif (next > val) {\n\t\t\tif (val == bitrate)\n\t\t\t\treturn bitrate;\n\n\t\t\tif (val < next && val > bitrate)\n\t\t\t\tnext = val;\n\t\t\tif (val > prev && val < bitrate)\n\t\t\t\tprev = val;\n\t\t}\n\t}\n\n\tif (next != INVALID_BITRATE)\n\t\treturn next;\n\tif (prev != 0)\n\t\treturn prev;\n\treturn 192;\n}\n"
  },
  {
    "path": "frontend/utility/audio-encoders.hpp",
    "content": "#pragma once\n\n#include <map>\n#include <string>\n#include <vector>\n\nconst std::map<int, std::string> &GetSimpleAACEncoderBitrateMap();\nconst char *GetSimpleAACEncoderForBitrate(int bitrate);\nint FindClosestAvailableSimpleAACBitrate(int bitrate);\n\nconst std::map<int, std::string> &GetSimpleOpusEncoderBitrateMap();\nconst char *GetSimpleOpusEncoderForBitrate(int bitrate);\nint FindClosestAvailableSimpleOpusBitrate(int bitrate);\n\nconst std::vector<int> &GetAudioEncoderBitrates(const char *id);\nint FindClosestAvailableAudioBitrate(const char *id, int bitrate);\n"
  },
  {
    "path": "frontend/utility/crypto-helpers-mac.mm",
    "content": "#include \"crypto-helpers.hpp\"\n\n#import <Foundation/Foundation.h>\n#import <Security/Security.h>\n#import <Security/SecKey.h>\n\nbool VerifySignature(const uint8_t *pubKey, const size_t pubKeyLen, const uint8_t *buf, const size_t len,\n                     const uint8_t *sig, const size_t sigLen)\n{\n    NSData *pubKeyData = [NSData dataWithBytes:pubKey length:pubKeyLen];\n    CFArrayRef items = nullptr;\n\n    OSStatus res = SecItemImport((CFDataRef) pubKeyData, nullptr, nullptr, nullptr, (SecItemImportExportFlags) 0,\n                                 nullptr, nullptr, &items);\n    if (res != errSecSuccess)\n        return false;\n\n    SecKeyRef pubKeyRef = (SecKeyRef) CFArrayGetValueAtIndex(items, 0);\n    NSData *signedData = [NSData dataWithBytes:buf length:len];\n    NSData *signature = [NSData dataWithBytes:sig length:sigLen];\n\n    CFErrorRef errRef;\n    bool result = SecKeyVerifySignature(pubKeyRef, kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512,\n                                        (__bridge CFDataRef) signedData, (__bridge CFDataRef) signature, &errRef);\n\n    CFRelease(items);\n    return result;\n};\n"
  },
  {
    "path": "frontend/utility/crypto-helpers-mbedtls.cpp",
    "content": "#include \"crypto-helpers.hpp\"\n\n#include <mbedtls/md.h>\n#include <mbedtls/pk.h>\n\nbool VerifySignature(const uint8_t *pubKey, const size_t pubKeyLen, const uint8_t *buf, const size_t len,\n\t\t     const uint8_t *sig, const size_t sigLen)\n{\n\tbool result = false;\n\tint ret = 1;\n\tunsigned char hash[64];\n\tmbedtls_pk_context pk;\n\n\tmbedtls_pk_init(&pk);\n\n\t// Parse PEM key\n\tif ((ret = mbedtls_pk_parse_public_key(&pk, pubKey, pubKeyLen + 1)) != 0) {\n\t\tgoto exit;\n\t}\n\t// Hash input buffer\n\tif ((ret = mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA512), buf, len, hash)) != 0) {\n\t\tgoto exit;\n\t}\n\t// Verify signature\n\tif ((ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA512, hash, 64, sig, sigLen)) != 0) {\n\t\tgoto exit;\n\t}\n\n\tresult = true;\n\nexit:\n\tmbedtls_pk_free(&pk);\n\n\treturn result;\n}\n"
  },
  {
    "path": "frontend/utility/crypto-helpers.hpp",
    "content": "#pragma once\n\n#include <cstdint>\n#include <stdlib.h>\n\nbool VerifySignature(const uint8_t *pubKey, const size_t pubKeyLen, const uint8_t *buf, const size_t len,\n\t\t     const uint8_t *sig, const size_t sigLen);\n"
  },
  {
    "path": "frontend/utility/display-helpers.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <graphics/matrix4.h>\n#include <graphics/vec4.h>\n#include <obs.hpp>\n\n#include <QSize>\n#include <QWidget>\n\nstatic inline void GetScaleAndCenterPos(int baseCX, int baseCY, int windowCX, int windowCY, int &x, int &y,\n\t\t\t\t\tfloat &scale)\n{\n\tdouble windowAspect, baseAspect;\n\tint newCX, newCY;\n\n\twindowAspect = double(windowCX) / double(windowCY);\n\tbaseAspect = double(baseCX) / double(baseCY);\n\n\tif (windowAspect > baseAspect) {\n\t\tscale = float(windowCY) / float(baseCY);\n\t\tnewCX = int(double(windowCY) * baseAspect);\n\t\tnewCY = windowCY;\n\t} else {\n\t\tscale = float(windowCX) / float(baseCX);\n\t\tnewCX = windowCX;\n\t\tnewCY = int(float(windowCX) / baseAspect);\n\t}\n\n\tx = windowCX / 2 - newCX / 2;\n\ty = windowCY / 2 - newCY / 2;\n}\n\nstatic inline void GetCenterPosFromFixedScale(int baseCX, int baseCY, int windowCX, int windowCY, int &x, int &y,\n\t\t\t\t\t      float scale)\n{\n\tx = (float(windowCX) - float(baseCX) * scale) / 2.0f;\n\ty = (float(windowCY) - float(baseCY) * scale) / 2.0f;\n}\n\nstatic inline QSize GetPixelSize(QWidget *widget)\n{\n\treturn widget->size() * widget->devicePixelRatioF();\n}\n\n#define OUTLINE_COLOR 0xFFD0D0D0\n#define LINE_LENGTH 0.1f\n\n// Rec. ITU-R BT.1848-1 / EBU R 95\n#define ACTION_SAFE_PERCENT 0.035f       // 3.5%\n#define GRAPHICS_SAFE_PERCENT 0.05f      // 5.0%\n#define FOURBYTHREE_SAFE_PERCENT 0.1625f // 16.25%\n\nstatic inline void InitSafeAreas(gs_vertbuffer_t **actionSafeMargin, gs_vertbuffer_t **graphicsSafeMargin,\n\t\t\t\t gs_vertbuffer_t **fourByThreeSafeMargin, gs_vertbuffer_t **leftLine,\n\t\t\t\t gs_vertbuffer_t **topLine, gs_vertbuffer_t **rightLine)\n{\n\tobs_enter_graphics();\n\n\t// All essential action should be placed inside this area\n\tgs_render_start(true);\n\tgs_vertex2f(ACTION_SAFE_PERCENT, ACTION_SAFE_PERCENT);\n\tgs_vertex2f(ACTION_SAFE_PERCENT, 1 - ACTION_SAFE_PERCENT);\n\tgs_vertex2f(1 - ACTION_SAFE_PERCENT, 1 - ACTION_SAFE_PERCENT);\n\tgs_vertex2f(1 - ACTION_SAFE_PERCENT, ACTION_SAFE_PERCENT);\n\tgs_vertex2f(ACTION_SAFE_PERCENT, ACTION_SAFE_PERCENT);\n\t*actionSafeMargin = gs_render_save();\n\n\t// All graphics should be placed inside this area\n\tgs_render_start(true);\n\tgs_vertex2f(GRAPHICS_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);\n\tgs_vertex2f(GRAPHICS_SAFE_PERCENT, 1 - GRAPHICS_SAFE_PERCENT);\n\tgs_vertex2f(1 - GRAPHICS_SAFE_PERCENT, 1 - GRAPHICS_SAFE_PERCENT);\n\tgs_vertex2f(1 - GRAPHICS_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);\n\tgs_vertex2f(GRAPHICS_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);\n\t*graphicsSafeMargin = gs_render_save();\n\n\t// 4:3 safe area for widescreen\n\tgs_render_start(true);\n\tgs_vertex2f(FOURBYTHREE_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);\n\tgs_vertex2f(1 - FOURBYTHREE_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);\n\tgs_vertex2f(1 - FOURBYTHREE_SAFE_PERCENT, 1 - GRAPHICS_SAFE_PERCENT);\n\tgs_vertex2f(FOURBYTHREE_SAFE_PERCENT, 1 - GRAPHICS_SAFE_PERCENT);\n\tgs_vertex2f(FOURBYTHREE_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);\n\t*fourByThreeSafeMargin = gs_render_save();\n\n\tgs_render_start(true);\n\tgs_vertex2f(0.0f, 0.5f);\n\tgs_vertex2f(LINE_LENGTH, 0.5f);\n\t*leftLine = gs_render_save();\n\n\tgs_render_start(true);\n\tgs_vertex2f(0.5f, 0.0f);\n\tgs_vertex2f(0.5f, LINE_LENGTH);\n\t*topLine = gs_render_save();\n\n\tgs_render_start(true);\n\tgs_vertex2f(1.0f, 0.5f);\n\tgs_vertex2f(1 - LINE_LENGTH, 0.5f);\n\t*rightLine = gs_render_save();\n\n\tobs_leave_graphics();\n}\n\nstatic inline void RenderSafeAreas(gs_vertbuffer_t *vb, int cx, int cy)\n{\n\tif (!vb)\n\t\treturn;\n\n\tmatrix4 transform;\n\tmatrix4_identity(&transform);\n\ttransform.x.x = cx;\n\ttransform.y.y = cy;\n\n\tgs_load_vertexbuffer(vb);\n\n\tgs_matrix_push();\n\tgs_matrix_mul(&transform);\n\n\tgs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);\n\tgs_eparam_t *color = gs_effect_get_param_by_name(solid, \"color\");\n\n\tgs_effect_set_color(color, OUTLINE_COLOR);\n\twhile (gs_effect_loop(solid, \"Solid\"))\n\t\tgs_draw(GS_LINESTRIP, 0, 0);\n\n\tgs_matrix_pop();\n}\n"
  },
  {
    "path": "frontend/utility/item-widget-helpers.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"item-widget-helpers.hpp\"\n\n#include <QListWidget>\n\nQListWidgetItem *TakeListItem(QListWidget *widget, int row)\n{\n\tQListWidgetItem *item = widget->item(row);\n\n\tif (item)\n\t\tdelete widget->itemWidget(item);\n\n\treturn widget->takeItem(row);\n}\n\nvoid DeleteListItem(QListWidget *widget, QListWidgetItem *item)\n{\n\tif (item) {\n\t\tdelete widget->itemWidget(item);\n\t\tdelete item;\n\t}\n}\n\nvoid ClearListItems(QListWidget *widget)\n{\n#if QT_VERSION < QT_VERSION_CHECK(6, 4, 3)\n\t// Workaround for the SceneTree workaround for QTBUG-105870\n\twidget->setProperty(\"clearing\", true);\n#endif\n\n\twidget->setCurrentItem(nullptr, QItemSelectionModel::Clear);\n\n\tfor (int i = 0; i < widget->count(); i++)\n\t\tdelete widget->itemWidget(widget->item(i));\n\n\twidget->clear();\n#if QT_VERSION < QT_VERSION_CHECK(6, 4, 3)\n\t// Workaround for the SceneTree workaround for QTBUG-105870\n\twidget->setProperty(\"clearing\", false);\n#endif\n}\n"
  },
  {
    "path": "frontend/utility/item-widget-helpers.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n/* This file exists to prevent ->deleteLater from being called on custom-made\n * item widgets in widgets such as list widgets.  We do this to prevent things\n * such as references to sources/etc from getting stuck in the Qt event queue\n * with no way of controlling when they'll be released. */\n\n#include <QObject>\n\nclass QListWidget;\nclass QListWidgetItem;\n\nQListWidgetItem *TakeListItem(QListWidget *widget, int row);\nvoid DeleteListItem(QListWidget *widget, QListWidgetItem *item);\nvoid ClearListItems(QListWidget *widget);\n\ntemplate<typename QObjectPtr> void InsertQObjectByName(std::vector<QObjectPtr> &controls, QObjectPtr control)\n{\n\tQString name = control->objectName();\n\tauto finder = [name](QObjectPtr elem) {\n\t\treturn name.localeAwareCompare(elem->objectName()) < 0;\n\t};\n\tauto found_at = std::find_if(controls.begin(), controls.end(), finder);\n\n\tcontrols.insert(found_at, control);\n}\n"
  },
  {
    "path": "frontend/utility/models/branches.hpp",
    "content": "/*\n * Copyright (c) 2023 Dennis Sädtler <dennis@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#pragma once\n\n#include <string>\n#include <nlohmann/json.hpp>\n\nstruct JsonBranch {\n\t/* Internal name / ID of the branch (used in updater) */\n\tstd::string name;\n\t/* Human readable name */\n\tstd::string display_name;\n\t/* Description */\n\tstd::string description;\n\t/* Whether updating should use the branch if selected or fall back to stable */\n\tbool enabled = false;\n\t/* Whether the branch should be displayed in the UI */\n\tbool visible = false;\n\t/* OS compatibility */\n\tbool windows = false;\n\tbool macos = false;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(JsonBranch, name, display_name, description, enabled, visible,\n\t\t\t\t\t\t    windows, macos)\n};\n\nusing JsonBranches = std::vector<JsonBranch>;\n"
  },
  {
    "path": "frontend/utility/models/multitrack-video.hpp",
    "content": "/*\n * Copyright (c) 2024 Ruwen Hahn <haruwenz@twitch.tv>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#pragma once\n\n#include <string>\n#include <optional>\n#include <unordered_set>\n\n#include <obs.h>\n\n#include <nlohmann/json.hpp>\n\n/*\n * Support for (de-)serialising std::optional\n * From https://github.com/nlohmann/json/issues/1749#issuecomment-1731266676\n * whatsnew.hpp's version doesn't seem to work here\n */\ntemplate<typename T> struct nlohmann::adl_serializer<std::optional<T>> {\n\tstatic void from_json(const json &j, std::optional<T> &opt)\n\t{\n\t\tif (j.is_null()) {\n\t\t\topt = std::nullopt;\n\t\t} else {\n\t\t\topt = j.get<T>();\n\t\t}\n\t}\n\tstatic void to_json(json &json, std::optional<T> t)\n\t{\n\t\tif (t) {\n\t\t\tjson = *t;\n\t\t} else {\n\t\t\tjson = nullptr;\n\t\t}\n\t}\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(obs_scale_type, {\n\t\t\t\t\t\t     {OBS_SCALE_DISABLE, \"OBS_SCALE_DISABLE\"},\n\t\t\t\t\t\t     {OBS_SCALE_POINT, \"OBS_SCALE_POINT\"},\n\t\t\t\t\t\t     {OBS_SCALE_BICUBIC, \"OBS_SCALE_BICUBIC\"},\n\t\t\t\t\t\t     {OBS_SCALE_BILINEAR, \"OBS_SCALE_BILINEAR\"},\n\t\t\t\t\t\t     {OBS_SCALE_LANCZOS, \"OBS_SCALE_LANCZOS\"},\n\t\t\t\t\t\t     {OBS_SCALE_AREA, \"OBS_SCALE_AREA\"},\n\t\t\t\t\t     })\n\nNLOHMANN_JSON_SERIALIZE_ENUM(video_colorspace, {\n\t\t\t\t\t\t       {VIDEO_CS_DEFAULT, \"VIDEO_CS_DEFAULT\"},\n\t\t\t\t\t\t       {VIDEO_CS_601, \"VIDEO_CS_601\"},\n\t\t\t\t\t\t       {VIDEO_CS_709, \"VIDEO_CS_709\"},\n\t\t\t\t\t\t       {VIDEO_CS_SRGB, \"VIDEO_CS_SRGB\"},\n\t\t\t\t\t\t       {VIDEO_CS_2100_PQ, \"VIDEO_CS_2100_PQ\"},\n\t\t\t\t\t\t       {VIDEO_CS_2100_HLG, \"VIDEO_CS_2100_HLG\"},\n\t\t\t\t\t       })\n\n/* This only includes output formats selectable in advanced settings. */\nNLOHMANN_JSON_SERIALIZE_ENUM(video_format, {\n\t\t\t\t\t\t   {VIDEO_FORMAT_NONE, \"VIDEO_FORMAT_NONE\"},\n\t\t\t\t\t\t   {VIDEO_FORMAT_I420, \"VIDEO_FORMAT_I420\"},\n\t\t\t\t\t\t   {VIDEO_FORMAT_NV12, \"VIDEO_FORMAT_NV12\"},\n\t\t\t\t\t\t   {VIDEO_FORMAT_BGRA, \"VIDEO_FORMAT_BGRA\"},\n\t\t\t\t\t\t   {VIDEO_FORMAT_I444, \"VIDEO_FORMAT_I444\"},\n\t\t\t\t\t\t   {VIDEO_FORMAT_I010, \"VIDEO_FORMAT_I010\"},\n\t\t\t\t\t\t   {VIDEO_FORMAT_P010, \"VIDEO_FORMAT_P010\"},\n\t\t\t\t\t\t   {VIDEO_FORMAT_P216, \"VIDEO_FORMAT_P216\"},\n\t\t\t\t\t\t   {VIDEO_FORMAT_P416, \"VIDEO_FORMAT_P416\"},\n\t\t\t\t\t   })\n\nNLOHMANN_JSON_SERIALIZE_ENUM(video_range_type, {\n\t\t\t\t\t\t       {VIDEO_RANGE_DEFAULT, \"VIDEO_RANGE_DEFAULT\"},\n\t\t\t\t\t\t       {VIDEO_RANGE_PARTIAL, \"VIDEO_RANGE_PARTIAL\"},\n\t\t\t\t\t\t       {VIDEO_RANGE_FULL, \"VIDEO_RANGE_FULL\"},\n\t\t\t\t\t       })\n\nNLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(media_frames_per_second, numerator, denominator)\n\nnamespace GoLiveApi {\nusing std::string;\nusing std::optional;\nusing json = nlohmann::json;\n\nstruct Client {\n\tstring name = \"obs-studio\";\n\tstring version;\n\tstd::unordered_set<std::string> supported_codecs;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(Client, name, version, supported_codecs)\n};\n\nstruct Cpu {\n\tint32_t physical_cores;\n\tint32_t logical_cores;\n\toptional<uint32_t> speed;\n\toptional<string> name;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(Cpu, physical_cores, logical_cores, speed, name)\n};\n\nstruct Memory {\n\tuint64_t total;\n\tuint64_t free;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(Memory, total, free)\n};\n\nstruct Gpu {\n\tstring model;\n\tuint32_t vendor_id;\n\tuint32_t device_id;\n\tuint64_t dedicated_video_memory;\n\tuint64_t shared_system_memory;\n\toptional<string> driver_version;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(Gpu, model, vendor_id, device_id, dedicated_video_memory, shared_system_memory,\n\t\t\t\t       driver_version)\n};\n\nstruct GamingFeatures {\n\toptional<bool> game_bar_enabled;\n\toptional<bool> game_dvr_allowed;\n\toptional<bool> game_dvr_enabled;\n\toptional<bool> game_dvr_bg_recording;\n\toptional<bool> game_mode_enabled;\n\toptional<bool> hags_enabled;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(GamingFeatures, game_bar_enabled, game_dvr_allowed, game_dvr_enabled,\n\t\t\t\t       game_dvr_bg_recording, game_mode_enabled, hags_enabled)\n};\n\nstruct System {\n\tstring version;\n\tstring name;\n\tint build;\n\tstring release;\n\tstring revision;\n\tint bits;\n\tbool arm;\n\tbool armEmulation;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(System, version, name, build, release, revision, bits, arm, armEmulation)\n};\n\nstruct Capabilities {\n\tCpu cpu;\n\tMemory memory;\n\toptional<GamingFeatures> gaming_features;\n\tSystem system;\n\toptional<std::vector<Gpu>> gpu;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(Capabilities, cpu, memory, gaming_features, system, gpu)\n};\n\nstruct Canvas {\n\tuint32_t width;\n\tuint32_t height;\n\tuint32_t canvas_width;\n\tuint32_t canvas_height;\n\tmedia_frames_per_second framerate;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(Canvas, width, height, canvas_width, canvas_height, framerate)\n};\n\nstruct Preferences {\n\toptional<uint64_t> maximum_aggregate_bitrate;\n\toptional<uint32_t> maximum_video_tracks;\n\tbool vod_track_audio;\n\toptional<uint32_t> composition_gpu_index;\n\tuint32_t audio_samples_per_sec;\n\tuint32_t audio_channels;\n\tuint32_t audio_max_buffering_ms;\n\tbool audio_fixed_buffering;\n\tstd::vector<Canvas> canvases;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(Preferences, maximum_aggregate_bitrate, maximum_video_tracks, vod_track_audio,\n\t\t\t\t       composition_gpu_index, audio_samples_per_sec, audio_channels,\n\t\t\t\t       audio_max_buffering_ms, audio_fixed_buffering, canvases)\n};\n\nstruct PostData {\n\tstring service;\n\tstring schema_version;\n\tstring authentication;\n\n\tClient client;\n\tCapabilities capabilities;\n\tPreferences preferences;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(PostData, service, schema_version, authentication, client, capabilities,\n\t\t\t\t       preferences)\n};\n\n// Config Response\n\nstruct Meta {\n\tstring service;\n\tstring schema_version;\n\tstring config_id;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(Meta, service, schema_version, config_id)\n};\n\nenum struct StatusResult {\n\tUnknown,\n\tSuccess,\n\tWarning,\n\tError,\n};\n\nNLOHMANN_JSON_SERIALIZE_ENUM(StatusResult, {\n\t\t\t\t\t\t   {StatusResult::Unknown, nullptr},\n\t\t\t\t\t\t   {StatusResult::Success, \"success\"},\n\t\t\t\t\t\t   {StatusResult::Warning, \"warning\"},\n\t\t\t\t\t\t   {StatusResult::Error, \"error\"},\n\t\t\t\t\t   })\n\nstruct Status {\n\tStatusResult result = StatusResult::Unknown;\n\toptional<string> html_en_us;\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Status, result, html_en_us)\n};\n\nstruct IngestEndpoint {\n\tstring protocol;\n\tstring url_template;\n\toptional<string> authentication;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(IngestEndpoint, protocol, url_template, authentication)\n};\n\nstruct VideoEncoderConfiguration {\n\tstring type;\n\tuint32_t width;\n\tuint32_t height;\n\toptional<media_frames_per_second> framerate;\n\toptional<obs_scale_type> gpu_scale_type;\n\toptional<video_colorspace> colorspace;\n\toptional<video_range_type> range;\n\toptional<video_format> format;\n\tjson settings;\n\tuint32_t canvas_index;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(VideoEncoderConfiguration, type, width, height, framerate,\n\t\t\t\t\t\t    gpu_scale_type, colorspace, range, format, settings, canvas_index)\n};\n\nstruct AudioEncoderConfiguration {\n\tstring codec;\n\tuint32_t track_id;\n\tuint32_t channels;\n\tjson settings;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE(AudioEncoderConfiguration, codec, track_id, channels, settings)\n};\n\nstruct AudioConfigurations {\n\tstd::vector<AudioEncoderConfiguration> live;\n\toptional<std::vector<AudioEncoderConfiguration>> vod;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(AudioConfigurations, live, vod)\n};\n\nstruct Config {\n\tMeta meta;\n\toptional<Status> status;\n\tstd::vector<IngestEndpoint> ingest_endpoints;\n\tstd::vector<VideoEncoderConfiguration> encoder_configurations;\n\tAudioConfigurations audio_configurations;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Config, meta, status, ingest_endpoints, encoder_configurations,\n\t\t\t\t\t\t    audio_configurations)\n};\n} // namespace GoLiveApi\n"
  },
  {
    "path": "frontend/utility/models/whatsnew.hpp",
    "content": "/*\n * Copyright (c) 2023 Dennis Sädtler <dennis@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#pragma once\n\n#include <string>\n#include <optional>\n\n#include <nlohmann/json.hpp>\n\n/*\n * Support for (de-)serialising std::optional\n * Adapted from https://github.com/nlohmann/json/issues/1749#issuecomment-1555093802\n */\ntemplate<typename T> struct nlohmann::adl_serializer<std::optional<T>> {\n\tstatic std::optional<T> from_json(const json &json)\n\t{\n\t\treturn json.is_null() ? std::nullopt : std::optional{json.get<T>()};\n\t}\n\n\tstatic void to_json(json &json, std::optional<T> t)\n\t{\n\t\tif (t)\n\t\t\tjson = *t;\n\t\telse\n\t\t\tjson = nullptr;\n\t}\n};\n\nstruct WhatsNewPlatforms {\n\tbool windows = false;\n\tbool macos = false;\n\tbool linux = false;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(WhatsNewPlatforms, windows, macos, linux)\n};\n\nstruct WhatsNewItem {\n\t/* Target OBS version (patch is ignored) */\n\tstd::string version;\n\t/* Beta/RC release to target */\n\tint Beta = 0;\n\tint RC = 0;\n\t/* URL of webpage to be displayed */\n\tstd::string url;\n\t/* Increment for this version's item */\n\tint increment = 0;\n\t/* Optional OS filter */\n\tstd::optional<WhatsNewPlatforms> os = std::nullopt;\n\n\tNLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(WhatsNewItem, version, Beta, RC, url, increment, os)\n};\n\nusing WhatsNewList = std::vector<WhatsNewItem>;\n"
  },
  {
    "path": "frontend/utility/obf.c",
    "content": "#include \"obf.h\"\n\n#include <stdbool.h>\n\n#define LOWER_HALFBYTE(x) ((x) & 0xF)\n#define UPPER_HALFBYTE(x) (((x) >> 4) & 0xF)\n\nvoid deobfuscate_str(char *str, uint64_t val)\n{\n\tuint8_t *dec_val = (uint8_t *)&val;\n\tint i = 0;\n\n\twhile (*str != 0) {\n\t\tint pos = i / 2;\n\t\tbool bottom = (i % 2) == 0;\n\t\tuint8_t *ch = (uint8_t *)str;\n\t\tuint8_t xor = bottom ? LOWER_HALFBYTE(dec_val[pos]) : UPPER_HALFBYTE(dec_val[pos]);\n\n\t\t*ch ^= xor;\n\n\t\tif (++i == sizeof(uint64_t) * 2)\n\t\t\ti = 0;\n\n\t\tstr++;\n\t}\n}\n"
  },
  {
    "path": "frontend/utility/obf.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern void deobfuscate_str(char *str, uint64_t val);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "frontend/utility/platform-osx.mm",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#import \"platform.hpp\"\n\n#import <OBSApp.hpp>\n\n#import <util/threading.h>\n\n#import <AVFoundation/AVFoundation.h>\n#import <AppKit/AppKit.h>\n#import <dlfcn.h>\n\nusing namespace std;\n\nbool isInBundle()\n{\n    NSRunningApplication *app = [NSRunningApplication currentApplication];\n    return [app bundleIdentifier] != nil;\n}\n\nbool GetDataFilePath(const char *data, string &output)\n{\n    NSURL *bundleUrl = [[NSBundle mainBundle] bundleURL];\n    NSString *path = [[bundleUrl path] stringByAppendingFormat:@\"/%@/%s\", @\"Contents/Resources\", data];\n    output = path.UTF8String;\n\n    return !access(output.c_str(), R_OK);\n}\n\nvoid CheckIfAlreadyRunning(bool &already_running)\n{\n    NSString *bundleId = [[NSBundle mainBundle] bundleIdentifier];\n\n    NSUInteger appCount = [[NSRunningApplication runningApplicationsWithBundleIdentifier:bundleId] count];\n\n    already_running = appCount > 1;\n}\n\nstring GetDefaultVideoSavePath()\n{\n    NSFileManager *fm = [NSFileManager defaultManager];\n    NSURL *url = [fm URLForDirectory:NSMoviesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:true\n                               error:nil];\n\n    if (!url)\n        return getenv(\"HOME\");\n\n    return url.path.fileSystemRepresentation;\n}\n\nvector<string> GetPreferredLocales()\n{\n    NSArray *preferred = [NSLocale preferredLanguages];\n\n    auto locales = GetLocaleNames();\n    auto lang_to_locale = [&locales](string lang) -> string {\n        string lang_match = \"\";\n\n        for (const auto &locale : locales) {\n            if (locale.first == lang.substr(0, locale.first.size()))\n                return locale.first;\n\n            if (!lang_match.size() && locale.first.substr(0, 2) == lang.substr(0, 2))\n                lang_match = locale.first;\n        }\n\n        return lang_match;\n    };\n\n    vector<string> result;\n    result.reserve(preferred.count);\n\n    for (NSString *lang in preferred) {\n        string locale = lang_to_locale(lang.UTF8String);\n        if (!locale.size())\n            continue;\n\n        if (find(begin(result), end(result), locale) != end(result))\n            continue;\n\n        result.emplace_back(locale);\n    }\n\n    return result;\n}\n\nbool IsAlwaysOnTop(QWidget *window)\n{\n    return (window->windowFlags() & Qt::WindowStaysOnTopHint) != 0;\n}\n\nvoid disableColorSpaceConversion(QWidget *window)\n{\n    NSView *view = (__bridge NSView *) reinterpret_cast<void *>(window->winId());\n    view.window.colorSpace = NSColorSpace.sRGBColorSpace;\n}\n\nvoid SetAlwaysOnTop(QWidget *window, bool enable)\n{\n    Qt::WindowFlags flags = window->windowFlags();\n\n    if (enable) {\n        NSView *view = (__bridge NSView *) reinterpret_cast<void *>(window->winId());\n\n        [[view window] setLevel:NSScreenSaverWindowLevel];\n\n        flags |= Qt::WindowStaysOnTopHint;\n    } else {\n        flags &= ~Qt::WindowStaysOnTopHint;\n    }\n\n    window->setWindowFlags(flags);\n    window->show();\n}\n\nbool SetDisplayAffinitySupported(void)\n{\n    // Not implemented yet\n    return false;\n}\n\ntypedef void (*set_int_t)(int);\n\nvoid EnableOSXVSync(bool enable)\n{\n    static bool initialized = false;\n    static bool valid = false;\n    static set_int_t set_debug_options = nullptr;\n    static set_int_t deferred_updates = nullptr;\n\n    if (!initialized) {\n        void *quartzCore = dlopen(\"/System/Library/Frameworks/\"\n                                  \"QuartzCore.framework/QuartzCore\",\n                                  RTLD_LAZY);\n        if (quartzCore) {\n            set_debug_options = (set_int_t) dlsym(quartzCore, \"CGSSetDebugOptions\");\n            deferred_updates = (set_int_t) dlsym(quartzCore, \"CGSDeferredUpdates\");\n\n            valid = set_debug_options && deferred_updates;\n        }\n\n        initialized = true;\n    }\n\n    if (valid) {\n        set_debug_options(enable ? 0 : 0x08000000);\n        deferred_updates(enable ? 1 : 0);\n    }\n}\n\nvoid EnableOSXDockIcon(bool enable)\n{\n    if (enable)\n        [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];\n    else\n        [NSApp setActivationPolicy:NSApplicationActivationPolicyProhibited];\n}\n\n@interface DockView : NSView {\n      @private\n    QIcon _icon;\n}\n@end\n\n@implementation DockView\n\n- (id)initWithIcon:(QIcon)icon\n{\n    self = [super init];\n    _icon = icon;\n    return self;\n}\n\n- (void)drawRect:(NSRect)dirtyRect\n{\n    CGSize size = dirtyRect.size;\n\n    /* Draw regular app icon */\n    NSImage *appIcon = [[NSWorkspace sharedWorkspace] iconForFile:[[NSBundle mainBundle] bundlePath]];\n    [appIcon drawInRect:CGRectMake(0, 0, size.width, size.height)];\n\n    /* Draw small icon on top */\n    float iconSize = 0.45;\n    CGImageRef image = _icon.pixmap(size.width, size.height).toImage().toCGImage();\n    CGContextRef context = [[NSGraphicsContext currentContext] CGContext];\n    CGContextDrawImage(\n        context, CGRectMake(size.width * (1 - iconSize), 0, size.width * iconSize, size.height * iconSize), image);\n    CGImageRelease(image);\n}\n\n@end\n\nMacPermissionStatus CheckPermissionWithPrompt(MacPermissionType type, bool prompt_for_permission)\n{\n    __block MacPermissionStatus permissionResponse = kPermissionNotDetermined;\n\n    switch (type) {\n        case kAudioDeviceAccess: {\n            AVAuthorizationStatus audioStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];\n\n            if (audioStatus == AVAuthorizationStatusNotDetermined && prompt_for_permission) {\n                os_event_t *block_finished;\n                os_event_init(&block_finished, OS_EVENT_TYPE_MANUAL);\n                [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio\n                                         completionHandler:^(BOOL granted __attribute((unused))) {\n                                             os_event_signal(block_finished);\n                                         }];\n                os_event_wait(block_finished);\n                os_event_destroy(block_finished);\n                audioStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];\n            }\n\n            permissionResponse = (MacPermissionStatus) audioStatus;\n\n            blog(LOG_INFO, \"[macOS] Permission for audio device access %s.\",\n                 permissionResponse == kPermissionAuthorized ? \"granted\" : \"denied\");\n\n            break;\n        }\n        case kVideoDeviceAccess: {\n            AVAuthorizationStatus videoStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];\n\n            if (videoStatus == AVAuthorizationStatusNotDetermined && prompt_for_permission) {\n                os_event_t *block_finished;\n                os_event_init(&block_finished, OS_EVENT_TYPE_MANUAL);\n                [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo\n                                         completionHandler:^(BOOL granted __attribute((unused))) {\n                                             os_event_signal(block_finished);\n                                         }];\n\n                os_event_wait(block_finished);\n                os_event_destroy(block_finished);\n                videoStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];\n            }\n\n            permissionResponse = (MacPermissionStatus) videoStatus;\n\n            blog(LOG_INFO, \"[macOS] Permission for video device access %s.\",\n                 permissionResponse == kPermissionAuthorized ? \"granted\" : \"denied\");\n\n            break;\n        }\n        case kScreenCapture: {\n            permissionResponse = (CGPreflightScreenCaptureAccess() ? kPermissionAuthorized : kPermissionDenied);\n\n            if (permissionResponse != kPermissionAuthorized && prompt_for_permission) {\n                permissionResponse = (CGRequestScreenCaptureAccess() ? kPermissionAuthorized : kPermissionDenied);\n            }\n\n            blog(LOG_INFO, \"[macOS] Permission for screen capture %s.\",\n                 permissionResponse == kPermissionAuthorized ? \"granted\" : \"denied\");\n\n            break;\n        }\n        case kInputMonitoring: {\n            permissionResponse = (CGPreflightListenEventAccess() ? kPermissionAuthorized : kPermissionDenied);\n\n            if (permissionResponse != kPermissionAuthorized && prompt_for_permission) {\n                permissionResponse = (CGRequestListenEventAccess() ? kPermissionAuthorized : kPermissionDenied);\n            }\n\n            blog(LOG_INFO, \"[macOS] Permission for input monitoring %s.\",\n                 permissionResponse == kPermissionAuthorized ? \"granted\" : \"denied\");\n            break;\n        }\n    }\n\n    return permissionResponse;\n}\n\nvoid OpenMacOSPrivacyPreferences(const char *tab)\n{\n    NSURL *url = [NSURL\n        URLWithString:[NSString\n                          stringWithFormat:@\"x-apple.systempreferences:com.apple.preference.security?Privacy_%s\", tab]];\n    [[NSWorkspace sharedWorkspace] openURL:url];\n}\n\nvoid SetMacOSDarkMode(bool dark)\n{\n    if (dark) {\n        NSApp.appearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];\n    } else {\n        NSApp.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];\n    }\n}\n\nvoid TaskbarOverlayInit() {}\n\nvoid TaskbarOverlaySetStatus(TaskbarOverlayStatus status)\n{\n    QIcon icon;\n    if (status == TaskbarOverlayStatusActive)\n        icon = QIcon::fromTheme(\"obs-active\", QIcon(\":/res/images/active_mac.png\"));\n    else if (status == TaskbarOverlayStatusPaused)\n        icon = QIcon::fromTheme(\"obs-paused\", QIcon(\":/res/images/paused_mac.png\"));\n\n    NSDockTile *tile = [NSApp dockTile];\n    [tile setContentView:[[DockView alloc] initWithIcon:icon]];\n    [tile display];\n}\n\n/*\n * This custom NSApplication subclass makes the app compatible with CEF. Qt\n * also has an NSApplication subclass, but it doesn't conflict thanks to Qt\n * using arcane magic to hook into the NSApplication superclass itself if the\n * program has its own NSApplication subclass.\n */\n\n@protocol CrAppProtocol\n- (BOOL)isHandlingSendEvent;\n@end\n\n@interface OBSApplication : NSApplication <CrAppProtocol>\n@property (nonatomic, getter=isHandlingSendEvent) BOOL handlingSendEvent;\n@end\n\n@implementation OBSApplication\n\n- (void)sendEvent:(NSEvent *)event\n{\n    _handlingSendEvent = YES;\n    [super sendEvent:event];\n    _handlingSendEvent = NO;\n}\n\n@end\n\nvoid InstallNSThreadLocks()\n{\n    [[NSThread new] start];\n\n    if ([NSThread isMultiThreaded] != 1) {\n        abort();\n    }\n}\n\nvoid InstallNSApplicationSubclass()\n{\n    [OBSApplication sharedApplication];\n}\n\nbool HighContrastEnabled()\n{\n    return [[NSWorkspace sharedWorkspace] accessibilityDisplayShouldIncreaseContrast];\n}\n"
  },
  {
    "path": "frontend/utility/platform-windows.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"platform.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <util/windows/ComPtr.hpp>\n#include <util/windows/WinHandle.hpp>\n#include <util/windows/win-version.h>\n\n#include <Dwmapi.h>\n#include <audiopolicy.h>\n#include <mmdeviceapi.h>\n#include <shellapi.h>\n#include <shlobj.h>\n#include <sstream>\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n\nusing namespace std;\n\nextern bool portable_mode;\nextern int GetConfigPath(char *path, size_t size, const char *name);\n\nstatic inline bool check_path(const char *data, const char *path, string &output)\n{\n\tostringstream str;\n\tstr << path << data;\n\toutput = str.str();\n\n\tblog(LOG_DEBUG, \"Attempted path: %s\", output.c_str());\n\n\treturn os_file_exists(output.c_str());\n}\n\nbool GetDataFilePath(const char *data, string &output)\n{\n\tif (check_path(data, \"data/obs-studio/\", output))\n\t\treturn true;\n\n\treturn check_path(data, OBS_DATA_PATH \"/obs-studio/\", output);\n}\n\nstring GetDefaultVideoSavePath()\n{\n\twchar_t path_utf16[MAX_PATH];\n\tchar path_utf8[MAX_PATH] = {};\n\n\tSHGetFolderPathW(NULL, CSIDL_MYVIDEO, NULL, SHGFP_TYPE_CURRENT, path_utf16);\n\n\tos_wcs_to_utf8(path_utf16, wcslen(path_utf16), path_utf8, MAX_PATH);\n\treturn string(path_utf8);\n}\n\nstatic vector<string> GetUserPreferredLocales()\n{\n\tvector<string> result;\n\n\tULONG num, length = 0;\n\tif (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &num, nullptr, &length))\n\t\treturn result;\n\n\tvector<wchar_t> buffer(length);\n\tif (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &num, &buffer.front(), &length))\n\t\treturn result;\n\n\tresult.reserve(num);\n\tauto start = begin(buffer);\n\tauto end_ = end(buffer);\n\tdecltype(start) separator;\n\twhile ((separator = find(start, end_, 0)) != end_) {\n\t\tif (result.size() == num)\n\t\t\tbreak;\n\n\t\tchar conv[MAX_PATH] = {};\n\t\tos_wcs_to_utf8(&*start, separator - start, conv, MAX_PATH);\n\n\t\tresult.emplace_back(conv);\n\n\t\tstart = separator + 1;\n\t}\n\n\treturn result;\n}\n\nvector<string> GetPreferredLocales()\n{\n\tvector<string> windows_locales = GetUserPreferredLocales();\n\tauto obs_locales = GetLocaleNames();\n\tauto windows_to_obs = [&obs_locales](string windows) {\n\t\tstring lang_match;\n\n\t\tfor (auto &locale_pair : obs_locales) {\n\t\t\tauto &locale = locale_pair.first;\n\t\t\tif (locale == windows.substr(0, locale.size()))\n\t\t\t\treturn locale;\n\n\t\t\tif (lang_match.size())\n\t\t\t\tcontinue;\n\n\t\t\tif (locale.substr(0, 2) == windows.substr(0, 2))\n\t\t\t\tlang_match = locale;\n\t\t}\n\n\t\treturn lang_match;\n\t};\n\n\tvector<string> result;\n\tresult.reserve(obs_locales.size());\n\n\tfor (const string &locale : windows_locales) {\n\t\tstring match = windows_to_obs(locale);\n\t\tif (!match.size())\n\t\t\tcontinue;\n\n\t\tif (find(begin(result), end(result), match) != end(result))\n\t\t\tcontinue;\n\n\t\tresult.emplace_back(match);\n\t}\n\n\treturn result;\n}\n\nuint32_t GetWindowsVersion()\n{\n\tstatic uint32_t ver = 0;\n\n\tif (ver == 0) {\n\t\tstruct win_version_info ver_info;\n\n\t\tget_win_ver(&ver_info);\n\t\tver = (ver_info.major << 8) | ver_info.minor;\n\t}\n\n\treturn ver;\n}\n\nuint32_t GetWindowsBuild()\n{\n\tstatic uint32_t build = 0;\n\n\tif (build == 0) {\n\t\tstruct win_version_info ver_info;\n\n\t\tget_win_ver(&ver_info);\n\t\tbuild = ver_info.build;\n\t}\n\n\treturn build;\n}\n\nbool IsAlwaysOnTop(QWidget *window)\n{\n\tDWORD exStyle = GetWindowLong((HWND)window->winId(), GWL_EXSTYLE);\n\treturn (exStyle & WS_EX_TOPMOST) != 0;\n}\n\nvoid SetAlwaysOnTop(QWidget *window, bool enable)\n{\n\tHWND hwnd = (HWND)window->winId();\n\tSetWindowPos(hwnd, enable ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0,\n\t\t     SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);\n}\n\nvoid SetProcessPriority(const char *priority)\n{\n\tif (!priority)\n\t\treturn;\n\n\tif (strcmp(priority, \"High\") == 0)\n\t\tSetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);\n\telse if (strcmp(priority, \"AboveNormal\") == 0)\n\t\tSetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);\n\telse if (strcmp(priority, \"Normal\") == 0)\n\t\tSetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);\n\telse if (strcmp(priority, \"BelowNormal\") == 0)\n\t\tSetPriorityClass(GetCurrentProcess(), BELOW_NORMAL_PRIORITY_CLASS);\n\telse if (strcmp(priority, \"Idle\") == 0)\n\t\tSetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);\n}\n\nvoid SetWin32DropStyle(QWidget *window)\n{\n\tHWND hwnd = (HWND)window->winId();\n\tLONG_PTR ex_style = GetWindowLongPtr(hwnd, GWL_EXSTYLE);\n\tex_style |= WS_EX_ACCEPTFILES;\n\tSetWindowLongPtr(hwnd, GWL_EXSTYLE, ex_style);\n}\n\nbool SetDisplayAffinitySupported(void)\n{\n\tstatic bool checked = false;\n\tstatic bool supported;\n\n\t/* this has to be version gated as setting WDA_EXCLUDEFROMCAPTURE on\n\t   older Windows builds behaves like WDA_MONITOR (black box) */\n\n\tif (!checked) {\n\t\tif (GetWindowsVersion() > 0x0A00 || GetWindowsVersion() == 0x0A00 && GetWindowsBuild() >= 19041)\n\t\t\tsupported = true;\n\t\telse\n\t\t\tsupported = false;\n\n\t\tchecked = true;\n\t}\n\n\treturn supported;\n}\n\nbool DisableAudioDucking(bool disable)\n{\n\tComPtr<IMMDeviceEnumerator> devEmum;\n\tComPtr<IMMDevice> device;\n\tComPtr<IAudioSessionManager2> sessionManager2;\n\tComPtr<IAudioSessionControl> sessionControl;\n\tComPtr<IAudioSessionControl2> sessionControl2;\n\n\tHRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER,\n\t\t\t\t\t  __uuidof(IMMDeviceEnumerator), (void **)&devEmum);\n\tif (FAILED(result))\n\t\treturn false;\n\n\tresult = devEmum->GetDefaultAudioEndpoint(eRender, eConsole, &device);\n\tif (FAILED(result))\n\t\treturn false;\n\n\tresult = device->Activate(__uuidof(IAudioSessionManager2), CLSCTX_INPROC_SERVER, nullptr,\n\t\t\t\t  (void **)&sessionManager2);\n\tif (FAILED(result))\n\t\treturn false;\n\n\tresult = sessionManager2->GetAudioSessionControl(nullptr, 0, &sessionControl);\n\tif (FAILED(result))\n\t\treturn false;\n\n\tresult = sessionControl->QueryInterface(&sessionControl2);\n\tif (FAILED(result))\n\t\treturn false;\n\n\tresult = sessionControl2->SetDuckingPreference(disable);\n\treturn SUCCEEDED(result);\n}\n\nstruct RunOnceMutexData {\n\tWinHandle handle;\n\n\tinline RunOnceMutexData(HANDLE h) : handle(h) {}\n};\n\nRunOnceMutex::RunOnceMutex(RunOnceMutex &&rom)\n{\n\tdelete data;\n\tdata = rom.data;\n\trom.data = nullptr;\n}\n\nRunOnceMutex::~RunOnceMutex()\n{\n\tdelete data;\n}\n\nRunOnceMutex &RunOnceMutex::operator=(RunOnceMutex &&rom)\n{\n\tdelete data;\n\tdata = rom.data;\n\trom.data = nullptr;\n\treturn *this;\n}\n\nRunOnceMutex CheckIfAlreadyRunning(bool &already_running)\n{\n\tstring name;\n\n\tif (!portable_mode) {\n\t\tname = \"OBSStudioCore\";\n\t} else {\n\t\tchar path[500];\n\t\tchar absPath[512];\n\t\t*path = 0;\n\t\t*absPath = 0;\n\t\tGetAppConfigPath(path, sizeof(path), \"\");\n\t\tos_get_abs_path(path, absPath, sizeof(absPath));\n\t\tname = \"OBSStudioPortable\";\n\t\tname += absPath;\n\t}\n\n\tBPtr<wchar_t> wname;\n\tos_utf8_to_wcs_ptr(name.c_str(), name.size(), &wname);\n\n\tif (wname) {\n\t\twchar_t *temp = wname;\n\t\twhile (*temp) {\n\t\t\tif (!iswalnum(*temp))\n\t\t\t\t*temp = L'_';\n\t\t\ttemp++;\n\t\t}\n\t}\n\n\tHANDLE h = OpenMutexW(SYNCHRONIZE, false, wname.Get());\n\talready_running = !!h;\n\n\tif (!already_running)\n\t\th = CreateMutexW(nullptr, false, wname.Get());\n\n\tRunOnceMutex rom(h ? new RunOnceMutexData(h) : nullptr);\n\treturn rom;\n}\n\nstruct MonitorData {\n\tconst wchar_t *id;\n\tMONITORINFOEX info;\n\tbool found;\n};\n\nstatic BOOL CALLBACK GetMonitorCallback(HMONITOR monitor, HDC, LPRECT, LPARAM param)\n{\n\tMonitorData *data = (MonitorData *)param;\n\n\tif (GetMonitorInfoW(monitor, &data->info)) {\n\t\tif (wcscmp(data->info.szDevice, data->id) == 0) {\n\t\t\tdata->found = true;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/* Based on https://www.winehq.org/pipermail/wine-devel/2008-September/069387.html */\ntypedef const char *(CDECL *WINEGETVERSION)(void);\nbool IsRunningOnWine()\n{\n\tWINEGETVERSION func;\n\tHMODULE nt;\n\n\tnt = GetModuleHandleW(L\"ntdll\");\n\tif (!nt)\n\t\treturn false;\n\n\tfunc = (WINEGETVERSION)GetProcAddress(nt, \"wine_get_version\");\n\tif (func) {\n\t\tblog(LOG_WARNING, \"Running on Wine version \\\"%s\\\"\", func());\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nHWND hwnd;\nvoid TaskbarOverlayInit()\n{\n\thwnd = (HWND)App()->GetMainWindow()->winId();\n}\n\nvoid TaskbarOverlaySetStatus(TaskbarOverlayStatus status)\n{\n\tITaskbarList4 *taskbarIcon;\n\tauto hr = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbarIcon));\n\n\tif (FAILED(hr)) {\n\t\ttaskbarIcon->Release();\n\t\treturn;\n\t}\n\n\thr = taskbarIcon->HrInit();\n\n\tif (FAILED(hr)) {\n\t\ttaskbarIcon->Release();\n\t\treturn;\n\t}\n\n\tQIcon qicon;\n\tswitch (status) {\n\tcase TaskbarOverlayStatusActive:\n\t\tqicon = QIcon::fromTheme(\"obs-active\", QIcon(\":/res/images/active.png\"));\n\t\tbreak;\n\tcase TaskbarOverlayStatusPaused:\n\t\tqicon = QIcon::fromTheme(\"obs-paused\", QIcon(\":/res/images/paused.png\"));\n\t\tbreak;\n\tcase TaskbarOverlayStatusInactive:\n\t\ttaskbarIcon->SetOverlayIcon(hwnd, nullptr, nullptr);\n\t\ttaskbarIcon->Release();\n\t\treturn;\n\t}\n\n\tHICON hicon = nullptr;\n\tif (!qicon.isNull()) {\n\t\tQ_GUI_EXPORT HICON qt_pixmapToWinHICON(const QPixmap &p);\n\t\thicon = qt_pixmapToWinHICON(qicon.pixmap(GetSystemMetrics(SM_CXSMICON)));\n\t\tif (!hicon)\n\t\t\treturn;\n\t}\n\n\ttaskbarIcon->SetOverlayIcon(hwnd, hicon, nullptr);\n\tDestroyIcon(hicon);\n\ttaskbarIcon->Release();\n}\n\nbool HighContrastEnabled()\n{\n\tHIGHCONTRAST hc = {};\n\thc.cbSize = sizeof(HIGHCONTRAST);\n\n\tif (SystemParametersInfo(SPI_GETHIGHCONTRAST, hc.cbSize, &hc, 0))\n\t\treturn hc.dwFlags & HCF_HIGHCONTRASTON;\n\n\treturn false;\n}\n"
  },
  {
    "path": "frontend/utility/platform-x11.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n    Copyright (C) 2014 by Zachary Lund <admin@computerquip.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"platform.hpp\"\n\n#include <OBSApp.hpp>\n\n#include <obs-config.h>\n#include <util/bmem.h>\n#include <util/platform.h>\n\n#include <QDBusConnection>\n#include <QDBusReply>\n\n#if defined(__FreeBSD__) || defined(__DragonFly__)\n#include <condition_variable>\n#include <mutex>\n#endif\n#include <sstream>\n#include <string>\n#if defined(__FreeBSD__) || defined(__DragonFly__)\n#include <thread>\n#endif\n#include <vector>\n\n#if defined(__FreeBSD__) || defined(__DragonFly__)\n#include <fcntl.h>\n#include <sys/param.h>\n#include <sys/queue.h>\n#include <sys/socket.h>\n#include <libprocstat.h>\n#include <pthread_np.h>\n#endif\n#if defined(__FreeBSD__) || defined(__DragonFly__)\n#include <sys/param.h>\n#endif\n#ifdef __linux__\n#include <sys/socket.h>\n#endif\n#if defined(__FreeBSD__) || defined(__DragonFly__)\n#include <sys/sysctl.h>\n#endif\n#ifdef __linux__\n#include <sys/un.h>\n#endif\n#if defined(__FreeBSD__) || defined(__DragonFly__)\n#include <sys/user.h>\n#endif\n#include <unistd.h>\n\nusing std::ostringstream;\nusing std::string;\nusing std::vector;\n\n#ifdef __linux__\nvoid CheckIfAlreadyRunning(bool &already_running)\n{\n\tint uniq = socket(AF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);\n\n\tif (uniq == -1) {\n\t\tblog(LOG_ERROR, \"Failed to check for running instance, socket: %d\", errno);\n\t\talready_running = 0;\n\t\treturn;\n\t}\n\n\tstruct sockaddr_un bindInfo;\n\tmemset(&bindInfo, 0, sizeof(sockaddr_un));\n\tbindInfo.sun_family = AF_LOCAL;\n\tauto bindInfoStrlen = snprintf(bindInfo.sun_path + 1, sizeof(bindInfo.sun_path) - 1, \"%s %d %s\",\n\t\t\t\t       \"/com/obsproject\", getpid(), App()->GetVersionString().c_str());\n\n\tint bindErr = bind(uniq, (struct sockaddr *)&bindInfo, sizeof(sa_family_t) + 1 + bindInfoStrlen);\n\talready_running = bindErr == 0 ? 0 : 1;\n\n\tif (already_running) {\n\t\treturn;\n\t}\n\n\tFILE *fp = fopen(\"/proc/net/unix\", \"re\");\n\n\tif (fp == NULL) {\n\t\treturn;\n\t}\n\n\tchar *line = NULL;\n\tsize_t n = 0;\n\tint obsCnt = 0;\n\twhile (getdelim(&line, &n, ' ', fp) != EOF) {\n\t\tline[strcspn(line, \"\\n\")] = '\\0';\n\t\tif (*line == '@') {\n\t\t\tif (strstr(line, \"@/com/obsproject\") != NULL) {\n\t\t\t\t++obsCnt;\n\t\t\t}\n\t\t}\n\t}\n\talready_running = obsCnt == 1 ? 0 : 1;\n\tfree(line);\n\tfclose(fp);\n}\n#endif\n#if defined(__FreeBSD__) || defined(__DragonFly__)\nstruct RunOnce {\n\tstd::thread thr;\n\tstatic const char *thr_name;\n\tstd::condition_variable cv;\n\tstd::condition_variable wait_cv;\n\tstd::mutex mtx;\n\tbool exiting = false;\n\tbool name_changed = false;\n\n\tvoid thr_proc()\n\t{\n\t\tstd::unique_lock<std::mutex> lk(mtx);\n\t\tpthread_set_name_np(pthread_self(), thr_name);\n\t\tname_changed = true;\n\t\twait_cv.notify_all();\n\t\tcv.wait(lk, [this]() { return exiting; });\n\t}\n\n\t~RunOnce()\n\t{\n\t\tif (thr.joinable()) {\n\t\t\tstd::unique_lock<std::mutex> lk(mtx);\n\t\t\texiting = true;\n\t\t\tcv.notify_one();\n\t\t\tlk.unlock();\n\t\t\tthr.join();\n\t\t}\n\t}\n} RO;\nconst char *RunOnce::thr_name = \"OBS runonce\";\n\nvoid CheckIfAlreadyRunning(bool &already_running)\n{\n\tstd::string tmpfile_name = \"/tmp/obs-studio.lock.\" + std::to_string(geteuid());\n\tint fd = open(tmpfile_name.c_str(), O_RDWR | O_CREAT | O_EXLOCK, 0600);\n\tif (fd == -1) {\n\t\talready_running = true;\n\t\treturn;\n\t}\n\n\talready_running = false;\n\n\tprocstat *ps = procstat_open_sysctl();\n\tunsigned int count;\n\tauto procs = procstat_getprocs(ps, KERN_PROC_UID | KERN_PROC_INC_THREAD, geteuid(), &count);\n\tfor (unsigned int i = 0; i < count; i++) {\n\t\tif (!strncmp(procs[i].ki_tdname, RunOnce::thr_name, sizeof(procs[i].ki_tdname))) {\n\t\t\talready_running = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\tprocstat_freeprocs(ps, procs);\n\tprocstat_close(ps);\n\n\tRO.thr = std::thread(std::mem_fn(&RunOnce::thr_proc), &RO);\n\n\t{\n\t\tstd::unique_lock<std::mutex> lk(RO.mtx);\n\t\tRO.wait_cv.wait(lk, []() { return RO.name_changed; });\n\t}\n\n\tunlink(tmpfile_name.c_str());\n\tclose(fd);\n}\n#endif\n\nstatic inline bool check_path(const char *data, const char *path, string &output)\n{\n\tostringstream str;\n\tstr << path << data;\n\toutput = str.str();\n\n\tblog(LOG_DEBUG, \"Attempted path: %s\", output.c_str());\n\n\treturn (access(output.c_str(), R_OK) == 0);\n}\n\n#define INSTALL_DATA_PATH OBS_INSTALL_PREFIX \"/\" OBS_DATA_PATH \"/obs-studio/\"\n\nbool GetDataFilePath(const char *data, string &output)\n{\n\tchar *data_path = getenv(\"OBS_DATA_PATH\");\n\tif (data_path != NULL) {\n\t\tif (check_path(data, data_path, output))\n\t\t\treturn true;\n\t}\n\n\tchar *relative_data_path = os_get_executable_path_ptr(\"../\" OBS_DATA_PATH \"/obs-studio/\");\n\n\tif (relative_data_path) {\n\t\tbool result = check_path(data, relative_data_path, output);\n\t\tbfree(relative_data_path);\n\n\t\tif (result) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (check_path(data, OBS_DATA_PATH \"/obs-studio/\", output))\n\t\treturn true;\n\n\tif (check_path(data, INSTALL_DATA_PATH, output))\n\t\treturn true;\n\n\treturn false;\n}\n\nstring GetDefaultVideoSavePath()\n{\n\treturn string(getenv(\"HOME\"));\n}\n\nvector<string> GetPreferredLocales()\n{\n\tvector<string> matched;\n\tstring messages = setlocale(LC_MESSAGES, NULL);\n\tif (!messages.size() || messages == \"C\" || messages == \"POSIX\")\n\t\treturn {};\n\n\tif (messages.size() > 2)\n\t\tmessages[2] = '-';\n\n\tfor (auto &locale_pair : GetLocaleNames()) {\n\t\tauto &locale = locale_pair.first;\n\t\tif (locale == messages.substr(0, locale.size()))\n\t\t\treturn {locale};\n\n\t\tif (locale.substr(0, 2) == messages.substr(0, 2))\n\t\t\tmatched.push_back(locale);\n\t}\n\n\treturn matched;\n}\n\nbool IsAlwaysOnTop(QWidget *window)\n{\n\treturn (window->windowFlags() & Qt::WindowStaysOnTopHint) != 0;\n}\n\nvoid SetAlwaysOnTop(QWidget *window, bool enable)\n{\n\tQt::WindowFlags flags = window->windowFlags();\n\n\tif (enable)\n\t\tflags |= Qt::WindowStaysOnTopHint;\n\telse\n\t\tflags &= ~Qt::WindowStaysOnTopHint;\n\n\twindow->setWindowFlags(flags);\n\twindow->show();\n}\n\nbool SetDisplayAffinitySupported(void)\n{\n\t// Not implemented yet\n\treturn false;\n}\n\n// Not implemented yet\nvoid TaskbarOverlayInit() {}\nvoid TaskbarOverlaySetStatus(TaskbarOverlayStatus) {}\n\nbool HighContrastEnabled()\n{\n\tQDBusReply<QVariant> reply;\n\tQDBusMessage msgXdpSettingsVersion = QDBusMessage::createMethodCall(\"org.freedesktop.portal.Desktop\",\n\t\t\t\t\t\t\t\t\t    \"/org/freedesktop/portal/desktop\",\n\t\t\t\t\t\t\t\t\t    \"org.freedesktop.DBus.Properties\", \"Get\");\n\tmsgXdpSettingsVersion << \"org.freedesktop.portal.Settings\"\n\t\t\t      << \"version\";\n\n\treply = QDBusConnection::sessionBus().call(msgXdpSettingsVersion);\n\n\tif (!reply.isValid()) {\n\t\tblog(LOG_WARNING, \"Get on org.freedesktop.portal.Settings returned an invalid reply\");\n\t\treturn false;\n\t}\n\n\t/* NOTE: org.freedesktop.portal.Settings got its contrast settings after\n\t * the ReadOne method. So assumes that if ReadOne is not available, contrast\n\t * isn't available either. */\n\tif (uint32_t version = reply.value().toUInt() < 2) {\n\t\tblog(LOG_WARNING, \"org.freedesktop.portal.Settings version %u does not support ReadOne\", version);\n\t\treturn false;\n\t}\n\n\t/* NOTE: If contrast is not available if will return 0 (false). */\n\tQDBusMessage msgXdpSettingsContrast =\n\t\tQDBusMessage::createMethodCall(\"org.freedesktop.portal.Desktop\", \"/org/freedesktop/portal/desktop\",\n\t\t\t\t\t       \"org.freedesktop.portal.Settings\", \"ReadOne\");\n\n\tmsgXdpSettingsContrast << \"org.freedesktop.appearance\"\n\t\t\t       << \"contrast\";\n\n\treply = QDBusConnection::sessionBus().call(msgXdpSettingsContrast);\n\n\tif (!reply.isValid()) {\n\t\tblog(LOG_WARNING, \"ReadOne on org.freedesktop.portal.Settings returned an invalid reply\");\n\t\treturn false;\n\t}\n\n\treturn reply.value().toUInt() != 0;\n}\n"
  },
  {
    "path": "frontend/utility/platform.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <util/c99defs.h>\n\n#include <string>\n#include <vector>\n\nclass QWidget;\n\n/* Gets the path of obs-studio specific data files (such as locale) */\nbool GetDataFilePath(const char *data, std::string &path);\n\nstd::string GetDefaultVideoSavePath();\n\nstd::vector<std::string> GetPreferredLocales();\n\nbool IsAlwaysOnTop(QWidget *window);\nvoid SetAlwaysOnTop(QWidget *window, bool enable);\n\nbool SetDisplayAffinitySupported(void);\n\nbool HighContrastEnabled();\n\nenum TaskbarOverlayStatus {\n\tTaskbarOverlayStatusInactive,\n\tTaskbarOverlayStatusActive,\n\tTaskbarOverlayStatusPaused,\n};\nvoid TaskbarOverlayInit();\nvoid TaskbarOverlaySetStatus(TaskbarOverlayStatus status);\n\n#ifdef _WIN32\nclass RunOnceMutex;\nRunOnceMutex\n#else\nvoid\n#endif\nCheckIfAlreadyRunning(bool &already_running);\n\n#ifdef _WIN32\nuint32_t GetWindowsVersion();\nuint32_t GetWindowsBuild();\nvoid SetProcessPriority(const char *priority);\nvoid SetWin32DropStyle(QWidget *window);\nbool DisableAudioDucking(bool disable);\n\nstruct RunOnceMutexData;\n\nclass RunOnceMutex {\n\tRunOnceMutexData *data = nullptr;\n\npublic:\n\tRunOnceMutex(RunOnceMutexData *data_) : data(data_) {}\n\tRunOnceMutex(const RunOnceMutex &rom) = delete;\n\tRunOnceMutex(RunOnceMutex &&rom);\n\t~RunOnceMutex();\n\n\tRunOnceMutex &operator=(const RunOnceMutex &rom) = delete;\n\tRunOnceMutex &operator=(RunOnceMutex &&rom);\n};\n\nbool IsRunningOnWine();\n#endif\n\n#ifdef __APPLE__\ntypedef enum {\n\tkAudioDeviceAccess = 0,\n\tkVideoDeviceAccess = 1,\n\tkScreenCapture = 2,\n\tkInputMonitoring = 3\n} MacPermissionType;\n\ntypedef enum {\n\tkPermissionNotDetermined = 0,\n\tkPermissionRestricted = 1,\n\tkPermissionDenied = 2,\n\tkPermissionAuthorized = 3,\n} MacPermissionStatus;\n\nvoid EnableOSXVSync(bool enable);\nvoid EnableOSXDockIcon(bool enable);\nbool isInBundle();\nvoid InstallNSApplicationSubclass();\nvoid InstallNSThreadLocks();\nvoid disableColorSpaceConversion(QWidget *window);\nvoid SetMacOSDarkMode(bool dark);\n\nMacPermissionStatus CheckPermissionWithPrompt(MacPermissionType type, bool prompt_for_permission);\n#define CheckPermission(x) CheckPermissionWithPrompt(x, false)\n#define RequestPermission(x) CheckPermissionWithPrompt(x, true)\nvoid OpenMacOSPrivacyPreferences(const char *tab);\n#endif\n"
  },
  {
    "path": "frontend/utility/system-info-macos.mm",
    "content": "#include \"system-info.hpp\"\n\n#ifdef __aarch64__\n#import <util/platform.h>\n\n#import <Foundation/Foundation.h>\n#import <Foundation/NSProcessInfo.h>\n#import <sys/sysctl.h>\n#import <sys/types.h>\n\nnamespace {\n    std::optional<std::string> getCpuName()\n    {\n        std::string name;\n        size_t size;\n        int ret;\n\n        ret = sysctlbyname(\"machdep.cpu.brand_string\", nullptr, &size, nullptr, 0);\n        if (ret != 0)\n            return std::nullopt;\n\n        name.resize(size);\n\n        ret = sysctlbyname(\"machdep.cpu.brand_string\", name.data(), &size, nullptr, 0);\n        if (ret != 0)\n            return std::nullopt;\n\n        // Remove null terminator\n        name.resize(name.find('\\0'));\n        return name;\n    }\n\n    // Apple Silicon Macs have a single SoC that contains both GPU and CPU, the same information is valid for both.\n    void fillSoCInfo(GoLiveApi::Capabilities &capabilities)\n    {\n        capabilities.cpu.name = getCpuName();\n        // Getting the frequency is not supported on Apple Silicon.\n        capabilities.cpu.physical_cores = os_get_physical_cores();\n        capabilities.cpu.logical_cores = os_get_logical_cores();\n\n        capabilities.memory.total = os_get_sys_total_size();\n        capabilities.memory.free = os_get_sys_free_size();\n\n        // Apple Silicon does not support dGPUs, there's only going to be one (the SoC).\n        GoLiveApi::Gpu gpu;\n        gpu.model = capabilities.cpu.name.value_or(\"Unknown\");\n        gpu.vendor_id = 0x106b;  // Always Apple\n        gpu.device_id = 0;       // Always 0 for Apple Silicon\n\n        std::vector<GoLiveApi::Gpu> gpus;\n        gpus.push_back(std::move(gpu));\n        capabilities.gpu = gpus;\n    }\n\n    void fillSystemInfo(GoLiveApi::System &sysinfo)\n    {\n        NSProcessInfo *procInfo = [NSProcessInfo processInfo];\n        NSOperatingSystemVersion versionObj = [procInfo operatingSystemVersion];\n\n        sysinfo.name = \"macOS\";\n        sysinfo.bits = 64;  // 32-bit macOS is long deprecated.\n        sysinfo.version = [[procInfo operatingSystemVersionString] UTF8String];\n\n        switch (versionObj.majorVersion) {\n            case 11:\n                sysinfo.release = \"Big Sur\";\n                break;\n            case 12:\n                sysinfo.release = \"Monterey\";\n                break;\n            case 13:\n                sysinfo.release = \"Ventura\";\n                break;\n            case 14:\n                sysinfo.release = \"Sonoma\";\n                break;\n            case 15:\n                sysinfo.release = \"Sequoia\";\n                break;\n            default:\n                sysinfo.release = \"unknown\";\n        }\n\n        sysinfo.arm = true;\n        sysinfo.armEmulation = false;\n    }\n}  // namespace\n\nvoid system_info(GoLiveApi::Capabilities &capabilities)\n{\n    fillSoCInfo(capabilities);\n    fillSystemInfo(capabilities.system);\n}\n\n#else /* !__aarch64__ */\n\nvoid system_info(GoLiveApi::Capabilities &)\n{\n    /* Not implemented */\n}\n#endif\n"
  },
  {
    "path": "frontend/utility/system-info-posix.cpp",
    "content": "#include \"system-info.hpp\"\n#include \"util/dstr.hpp\"\n#include \"util/platform.h\"\n#include <sys/utsname.h>\n#include <unistd.h>\n#include <string>\n#include <fstream>\n#include <regex>\n\nusing namespace std;\n\nextern \"C\" {\n#include \"pci/pci.h\"\n}\n\n// Anonymous namespace ensures internal linkage\nnamespace {\nstruct drm_card_info {\n\tuint16_t vendor_id;\n\tuint16_t device_id;\n\tuint16_t subsystem_vendor;\n\tuint16_t subsystem_device;\n\n\t/* match_count is the number of matches between\n\t * the tokenized GPU identification string and\n\t * the device_name and vendor_name supplied by\n\t * libpci. It is used to associate the GPU that\n\t * OBS is using and the cards found in a bus scan\n\t * using libpci.\n\t */\n\tuint16_t match_count;\n\n\t/* The following 2 fields are easily\n\t * obtained for AMD GPUs. Reporting\n\t * for NVIDIA GPUs is not working at\n\t * the moment.\n\t */\n\tuint64_t dedicated_vram_total;\n\tuint64_t shared_system_memory_total;\n\n\t// PCI domain:bus:slot:function\n\tstd::string dbsf;\n\tstd::string device_name;\n\tstd::string vendor_name;\n};\n\nconstexpr std::string_view WHITE_SPACE = \" \\f\\n\\r\\t\\v\";\n\n// trim_ws() will remove leading and trailing\n// white space from the string.\nvoid trim_ws(std::string &s)\n{\n\t// Trim leading whitespace\n\tsize_t pos = s.find_first_not_of(WHITE_SPACE);\n\tif (pos != std::string::npos)\n\t\ts = s.substr(pos);\n\n\t// Trim trailing whitespace\n\tpos = s.find_last_not_of(WHITE_SPACE);\n\tif (pos != std::string::npos)\n\t\ts = s.substr(0, pos + 1);\n}\n\nbool compare_match_strength(const drm_card_info &a, const drm_card_info &b)\n{\n\treturn a.match_count > b.match_count;\n}\n\nvoid adjust_gpu_model(std::string &model)\n{\n\t/* Use the sub-string between the [] brackets. For example,\n\t * the NVIDIA Quadro P4000 model string from PCI ID database\n\t * is \"GP104GL [Quadro P4000]\", and we only want the \"Quadro\n\t * P4000\" sub-string.\n\t */\n\tsize_t first = model.find('[');\n\tsize_t last = model.find_last_of(']');\n\tif ((last - first) > 1) {\n\t\tstd::string adjusted_model = model.substr(first + 1, last - first - 1);\n\t\tmodel.assign(adjusted_model);\n\t}\n}\n\nbool get_distribution_info(std::string &distro, std::string &version)\n{\n\tifstream file;\n\tstd::string line;\n\tconst std::string flatpak_file = \"/.flatpak-info\";\n\tconst std::string systemd_file = \"/etc/os-release\";\n\n\tdistro = \"\";\n\tversion = \"\";\n\n\tif (std::filesystem::exists(flatpak_file)) {\n\t\t/* The .flatpak-info file has a line of the form:\n\t\t *\n\t\t * runtime=runtime/org.kde.Platform/x86_64/6.6\n\t\t *\n\t\t * Parse the line into tokens to identify the name and\n\t\t * version, which are \"org.kde.Platform\" and \"6.6\" respectively,\n\t\t * in the example above.\n\t\t */\n\t\tfile.open(flatpak_file);\n\t\tif (file.is_open()) {\n\t\t\twhile (getline(file, line)) {\n\t\t\t\tif (line.compare(0, 16, \"runtime=runtime/\") == 0) {\n\t\t\t\t\tsize_t pos = line.find('/');\n\t\t\t\t\tif (pos != string::npos && line.at(pos + 1) != '\\0') {\n\t\t\t\t\t\tline.erase(0, pos + 1);\n\n\t\t\t\t\t\t/* Split the string into tokens with a regex\n\t\t\t\t\t\t * of one or more '/' characters'.\n\t\t\t\t\t\t */\n\t\t\t\t\t\tstd::regex fp_reg(\"[/]+\");\n\t\t\t\t\t\tvector<std::string> fp_tokens(\n\t\t\t\t\t\t\tsregex_token_iterator(line.begin(), line.end(), fp_reg, -1),\n\t\t\t\t\t\t\tsregex_token_iterator());\n\t\t\t\t\t\tif (fp_tokens.size() >= 2) {\n\t\t\t\t\t\t\tauto token = begin(fp_tokens);\n\t\t\t\t\t\t\tdistro = \"Flatpak \" + *token;\n\t\t\t\t\t\t\ttoken = next(fp_tokens.end(), -1);\n\t\t\t\t\t\t\tversion = *token;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tdistro = \"Flatpak unknown\";\n\t\t\t\t\t\t\tversion = \"0\";\n\t\t\t\t\t\t\tblog(LOG_DEBUG,\n\t\t\t\t\t\t\t     \"%s: Format of 'runtime' entry unrecognized in file %s\",\n\t\t\t\t\t\t\t     __FUNCTION__, flatpak_file.c_str());\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfile.close();\n\t\t}\n\t} else if (std::filesystem::exists(systemd_file)) {\n\t\t/* systemd-based distributions use /etc/os-release to identify\n\t\t * the OS. For example, the Ubuntu 24.04.1 variant looks like:\n\t\t *\n\t\t * PRETTY_NAME=\"Ubuntu 24.04.1 LTS\"\n\t\t * NAME=\"Ubuntu\"\n\t\t * VERSION_ID=\"24.04\"\n\t\t * VERSION=\"24.04.1 LTS (Noble Numbat)\"\n\t\t * VERSION_CODENAME=noble\n\t\t * ID=ubuntu\n\t\t * ID_LIKE=debian\n\t\t * HOME_URL=\"https://www.ubuntu.com/\"\n\t\t * SUPPORT_URL=\"https://help.ubuntu.com/\"\n\t\t * BUG_REPORT_URL=\"https://bugs.launchpad.net/ubuntu/\"\n\t\t * PRIVACY_POLICY_URL=\"https://www.ubuntu.com/legal/terms-and-policies/privacy-policy\"\n\t\t * UBUNTU_CODENAME=noble\n\t\t * LOGO=ubuntu-logo\n\t\t *\n\t\t * Parse the file looking for the NAME and VERSION_ID fields.\n\t\t * Note that some distributions wrap the entry in '\"' characters\n\t\t * while others do not, so we need to remove those characters.\n\t\t */\n\t\tfile.open(systemd_file);\n\t\tif (file.is_open()) {\n\t\t\twhile (getline(file, line)) {\n\t\t\t\tif (line.compare(0, 4, \"NAME\") == 0) {\n\t\t\t\t\tsize_t pos = line.find('=');\n\t\t\t\t\tif (pos != std::string::npos && line.at(pos + 1) != '\\0') {\n\t\t\t\t\t\tdistro = line.substr(pos + 1);\n\n\t\t\t\t\t\t// Remove the '\"' characters from the string, if any.\n\t\t\t\t\t\tdistro.erase(std::remove(distro.begin(), distro.end(), '\"'),\n\t\t\t\t\t\t\t     distro.end());\n\n\t\t\t\t\t\ttrim_ws(distro);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (line.compare(0, 10, \"VERSION_ID\") == 0) {\n\t\t\t\t\tsize_t pos = line.find('=');\n\t\t\t\t\tif (pos != std::string::npos && line.at(pos + 1) != '\\0') {\n\t\t\t\t\t\tversion = line.substr(pos + 1);\n\n\t\t\t\t\t\t// Remove the '\"' characters from the string, if any.\n\t\t\t\t\t\tversion.erase(std::remove(version.begin(), version.end(), '\"'),\n\t\t\t\t\t\t\t      version.end());\n\n\t\t\t\t\t\ttrim_ws(version);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tfile.close();\n\t\t}\n\t} else {\n\t\tblog(LOG_DEBUG, \"%s: Failed to find host OS or flatpak info\", __FUNCTION__);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool get_cpu_name(optional<std::string> &proc_name)\n{\n\tifstream file(\"/proc/cpuinfo\");\n\tstd::string line;\n\tint physical_id = -1;\n\tbool found_name = false;\n\n\t/* Initialize processor name. Some ARM-based hosts do\n\t * not output the \"model name\" field in /proc/cpuinfo.\n\t */\n\tproc_name = \"Unavailable\";\n\n\tif (file.is_open()) {\n\t\twhile (getline(file, line)) {\n\t\t\tif (line.compare(0, 10, \"model name\") == 0) {\n\t\t\t\tsize_t pos = line.find(':');\n\t\t\t\tif (pos != std::string::npos && line.at(pos + 1) != '\\0') {\n\t\t\t\t\tproc_name = line.substr(pos + 1);\n\t\t\t\t\ttrim_ws((std::string &)proc_name);\n\t\t\t\t\tfound_name = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (line.compare(0, 11, \"physical id\") == 0) {\n\t\t\t\tsize_t pos = line.find(':');\n\t\t\t\tif (pos != std::string::npos && line.at(pos + 1) != '\\0') {\n\t\t\t\t\tphysical_id = atoi(&line[pos + 1]);\n\t\t\t\t\tif (physical_id == 0 && found_name)\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfile.close();\n\t}\n\treturn true;\n}\n\nbool get_cpu_freq(uint32_t &cpu_freq)\n{\n\tifstream freq_file;\n\tstd::string line;\n\n\t/* Look for the sysfs tree \"base_frequency\" first.\n\t * Intel exports \"base_frequency, AMD does not.\n\t * If not found, use \"cpuinfo_max_freq\".\n\t */\n\tfreq_file.open(\"/sys/devices/system/cpu/cpu0/cpufreq/base_frequency\");\n\tif (!freq_file.is_open()) {\n\t\tfreq_file.open(\"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq\");\n\t\tif (!freq_file.is_open())\n\t\t\treturn false;\n\t}\n\n\tif (getline(freq_file, line)) {\n\t\ttrim_ws(line);\n\t\t// Convert the CPU frequency string to an integer in MHz\n\t\tcpu_freq = atoi(line.c_str()) / 1000;\n\t} else {\n\t\tcpu_freq = 0;\n\t}\n\tfreq_file.close();\n\n\treturn true;\n}\n\n/* get_drm_cards() will find all render-capable cards\n * in /sys/class/drm and populate a vector of drm_card_info.\n */\nvoid get_drm_cards(std::vector<drm_card_info> &cards)\n{\n\tstruct drm_card_info dci;\n\tchar *file_str = NULL;\n\tchar buf[256];\n\tint len = 0;\n\tuint val = 0;\n\n\tstd::string base_path = \"/sys/class/drm/\";\n\tstd::string drm_path = base_path;\n\tsize_t base_len = base_path.length();\n\tsize_t node_len = 0;\n\n\tos_dir_t *dir = os_opendir(base_path.c_str());\n\tstruct os_dirent *ent;\n\n\twhile ((ent = os_readdir(dir)) != NULL) {\n\t\tstd::string entry_name = ent->d_name;\n\t\tif (ent->directory && (entry_name.find(\"renderD\") != std::string::npos)) {\n\t\t\tdci = {0};\n\t\t\tdrm_path.resize(base_len);\n\t\t\tdrm_path += ent->d_name;\n\t\t\tdrm_path += \"/device\";\n\n\t\t\t/* Get the PCI D:B:S:F (domain:bus:slot:function) in string form\n\t\t\t * by reading the device symlink.\n\t\t\t */\n\t\t\tif ((len = readlink(drm_path.c_str(), buf, sizeof(buf) - 1)) != -1) {\n\t\t\t\t// readlink() doesn't null terminate strings, so do it explicitly\n\t\t\t\tbuf[len] = '\\0';\n\t\t\t\tdci.dbsf = buf;\n\n\t\t\t\t/* The DBSF string is of the form: \"../../../0000:65:00.0/\".\n\t\t\t\t* Remove all the '/' characters, and\n\t\t\t\t* remove all the leading '.' characters.\n\t\t\t\t*/\n\t\t\t\tdci.dbsf.erase(std::remove(dci.dbsf.begin(), dci.dbsf.end(), '/'), dci.dbsf.end());\n\t\t\t\tdci.dbsf.erase(0, dci.dbsf.find_first_not_of(\".\"));\n\n\t\t\t\tnode_len = drm_path.length();\n\n\t\t\t\t// Get the device_id\n\t\t\t\tdrm_path += \"/device\";\n\t\t\t\tfile_str = os_quick_read_utf8_file(drm_path.c_str());\n\t\t\t\tif (!file_str) {\n\t\t\t\t\tblog(LOG_ERROR, \"Could not read from '%s'\", drm_path.c_str());\n\t\t\t\t\tdci.device_id = 0;\n\t\t\t\t} else {\n\t\t\t\t\t// Skip over the \"0x\" and convert\n\t\t\t\t\tsscanf(file_str + 2, \"%x\", &val);\n\t\t\t\t\tdci.device_id = val;\n\t\t\t\t\tbfree(file_str);\n\t\t\t\t}\n\n\t\t\t\t// Get the vendor_id\n\t\t\t\tdrm_path.resize(node_len);\n\t\t\t\tdrm_path += \"/vendor\";\n\t\t\t\tfile_str = os_quick_read_utf8_file(drm_path.c_str());\n\t\t\t\tif (!file_str) {\n\t\t\t\t\tblog(LOG_ERROR, \"Could not read from '%s'\", drm_path.c_str());\n\t\t\t\t\tdci.vendor_id = 0;\n\t\t\t\t} else {\n\t\t\t\t\t// Skip over the \"0x\" and convert\n\t\t\t\t\tsscanf(file_str + 2, \"%x\", &val);\n\t\t\t\t\tdci.vendor_id = val;\n\t\t\t\t\tbfree(file_str);\n\t\t\t\t}\n\n\t\t\t\t// Get the subsystem_vendor\n\t\t\t\tdrm_path.resize(node_len);\n\t\t\t\tdrm_path += \"/subsystem_vendor\";\n\t\t\t\tfile_str = os_quick_read_utf8_file(drm_path.c_str());\n\t\t\t\tif (!file_str) {\n\t\t\t\t\tdci.subsystem_vendor = 0;\n\t\t\t\t} else {\n\t\t\t\t\t// Skip over the \"0x\" and convert\n\t\t\t\t\tsscanf(file_str + 2, \"%x\", &val);\n\t\t\t\t\tdci.subsystem_vendor = val;\n\t\t\t\t\tbfree(file_str);\n\t\t\t\t}\n\n\t\t\t\t// Get the subsystem_device\n\t\t\t\tdrm_path.resize(node_len);\n\t\t\t\tdrm_path += \"/subsystem_device\";\n\t\t\t\tfile_str = os_quick_read_utf8_file(drm_path.c_str());\n\t\t\t\tif (!file_str) {\n\t\t\t\t\tdci.subsystem_device = 0;\n\t\t\t\t} else {\n\t\t\t\t\t// Skip over the \"0x\" and convert\n\t\t\t\t\tsscanf(file_str + 2, \"%x\", &val);\n\t\t\t\t\tdci.subsystem_device = val;\n\t\t\t\t\tbfree(file_str);\n\t\t\t\t}\n\n\t\t\t\t/* The amdgpu driver exports the GPU memory information\n\t\t\t\t * via sysfs nodes. Sadly NVIDIA doesn't have the same\n\t\t\t\t * information via sysfs. Read the amdgpu-based nodes\n\t\t\t\t * if present and get the required fields.\n\t\t\t\t *\n\t\t\t\t * Get the GPU total dedicated VRAM, if available\n\t\t\t\t */\n\t\t\t\tdrm_path.resize(node_len);\n\t\t\t\tdrm_path += \"/mem_info_vram_total\";\n\t\t\t\tfile_str = os_quick_read_utf8_file(drm_path.c_str());\n\t\t\t\tif (!file_str) {\n\t\t\t\t\tdci.dedicated_vram_total = 0;\n\t\t\t\t} else {\n\t\t\t\t\tsscanf(file_str, \"%lu\", &dci.dedicated_vram_total);\n\t\t\t\t\tbfree(file_str);\n\t\t\t\t}\n\n\t\t\t\t// Get the GPU total shared system memory, if available\n\t\t\t\tdrm_path.resize(node_len);\n\t\t\t\tdrm_path += \"/mem_info_gtt_total\";\n\t\t\t\tfile_str = os_quick_read_utf8_file(drm_path.c_str());\n\t\t\t\tif (!file_str) {\n\t\t\t\t\tdci.shared_system_memory_total = 0;\n\t\t\t\t} else {\n\t\t\t\t\tsscanf(file_str, \"%lu\", &dci.shared_system_memory_total);\n\t\t\t\t\tbfree(file_str);\n\t\t\t\t}\n\n\t\t\t\tcards.push_back(dci);\n\t\t\t\tblog(LOG_DEBUG,\n\t\t\t\t     \"%s: drm_adapter_info: PCI B:D:S:F: %s, device_id:0x%x,\"\n\t\t\t\t     \"vendor_id:0x%x, sub_device:0x%x, sub_vendor:0x%x,\"\n\t\t\t\t     \"vram_total: %lu, sys_memory: %lu\",\n\t\t\t\t     __FUNCTION__, dci.dbsf.c_str(), dci.device_id, dci.vendor_id, dci.subsystem_device,\n\t\t\t\t     dci.subsystem_vendor, dci.dedicated_vram_total, dci.shared_system_memory_total);\n\t\t\t} else {\n\t\t\t\tblog(LOG_DEBUG, \"%s: Failed to read symlink for %s\", __FUNCTION__, drm_path.c_str());\n\t\t\t}\n\t\t}\n\t}\n\tos_closedir(dir);\n}\n\n/* system_gpu_data() returns a sorted vector of GoLiveApi::Gpu\n * objects needed to build the multitrack video request.\n *\n * When a single GPU is installed the case is trivial. In systems\n * with multiple GPUs (including the CPU iGPU), the GPU that\n * OBS is currently using must be placed first in the list, so\n * that composition_gpu_index=0 is valid. composition_gpu_index\n * is set to to ovi.adapter which is always 0 apparently.\n *\n * system_gpu_data() does the following:\n *   1. Gather information about the GPU being used by OBS\n *   2. Scan the PCIe bus to identify all GPUs\n *   3. Find a best match of the GPU in-use in the list found in 2\n *   4. Generate the sorted list of GoLiveAPI::Gpu entries\n *\n * Note that the PCIe device_id and vendor_id are not available\n * via OpenGL calls, hence the need to match the in-use GPU with\n * the libpci scanned results and extract the PCIe information.\n */\nstd::optional<std::vector<GoLiveApi::Gpu>> system_gpu_data()\n{\n\tstd::vector<GoLiveApi::Gpu> adapter_info;\n\tGoLiveApi::Gpu gpu;\n\tstd::string gs_driver_version;\n\tstd::string gs_device_renderer;\n\tuint64_t dedicated_video_memory = 0;\n\tuint64_t shared_system_memory = 0;\n\n\tstd::vector<drm_card_info> drm_cards;\n\tstruct pci_access *pacc;\n\tpacc = pci_alloc();\n\tpci_init(pacc);\n\tchar slot[256];\n\n\t// Obtain GPU information by querying graphics API\n\tobs_enter_graphics();\n\tgs_driver_version = gs_get_driver_version();\n\tgs_device_renderer = gs_get_renderer();\n\tdedicated_video_memory = gs_get_gpu_dmem() * 1024;\n\tshared_system_memory = gs_get_gpu_smem() * 1024;\n\tobs_leave_graphics();\n\n\t/* Split the GPU renderer string into tokens with\n\t * a regex of one or more white-space characters.\n\t */\n\tstd::regex ws_reg(\"[\\\\s]+\");\n\tvector<std::string> gpu_tokens(sregex_token_iterator(gs_device_renderer.begin(), gs_device_renderer.end(),\n\t\t\t\t\t\t\t     ws_reg, -1),\n\t\t\t\t       sregex_token_iterator());\n\n\t// Remove extraneous characters from the tokens\n\tconstexpr std::string_view EXTRA_CHARS = \",()[]{}\";\n\tfor (auto token = begin(gpu_tokens); token != end(gpu_tokens); ++token) {\n\t\tfor (unsigned int i = 0; i < EXTRA_CHARS.size(); ++i) {\n\t\t\ttoken->erase(std::remove(token->begin(), token->end(), EXTRA_CHARS[i]), token->end());\n\t\t}\n\t\t// Convert tokens to lower-case\n\t\tstd::transform(token->begin(), token->end(), token->begin(), ::tolower);\n\t\tblog(LOG_DEBUG, \"%s: gpu_token: '%s'\", __FUNCTION__, token->c_str());\n\t}\n\n\t// Scan the PCI bus once\n\tpci_scan_bus(pacc);\n\n\t// Discover the set of DRM render-capable GPUs\n\tget_drm_cards(drm_cards);\n\tblog(LOG_DEBUG, \"Number of GPUs detected: %lu\", drm_cards.size());\n\n\t// Iterate through drm_cards to get the device and vendor names via libpci\n\tfor (auto card = begin(drm_cards); card != end(drm_cards); ++card) {\n\t\tstruct pci_dev *pdev;\n\t\tstruct pci_filter pfilter;\n\t\tchar namebuf[1024];\n\n\t\t/* Get around the 'const char*' vs 'char*'\n\t\t * type issue with pci_filter_parse_slot().\n\t\t */\n\t\tstrncpy(slot, card->dbsf.c_str(), sizeof(slot));\n\n\t\t// Validate the \"slot\" string according to libpci\n\t\tpci_filter_init(pacc, &pfilter);\n\t\tif (pci_filter_parse_slot(&pfilter, slot)) {\n\t\t\tblog(LOG_DEBUG, \"%s: pci_filter_parse_slot() failed\", __FUNCTION__);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Get the device name and vendor name from libpci\n\t\tfor (pdev = pacc->devices; pdev; pdev = pdev->next) {\n\t\t\tif (pci_filter_match(&pfilter, pdev)) {\n\t\t\t\tpci_fill_info(pdev, PCI_FILL_IDENT);\n\t\t\t\tcard->device_name =\n\t\t\t\t\tpci_lookup_name(pacc, namebuf, sizeof(namebuf),\n\t\t\t\t\t\t\tPCI_LOOKUP_DEVICE | PCI_LOOKUP_CACHE | PCI_LOOKUP_NETWORK,\n\t\t\t\t\t\t\tpdev->vendor_id, pdev->device_id);\n\t\t\t\tcard->vendor_name =\n\t\t\t\t\tpci_lookup_name(pacc, namebuf, sizeof(namebuf),\n\t\t\t\t\t\t\tPCI_LOOKUP_VENDOR | PCI_LOOKUP_CACHE | PCI_LOOKUP_NETWORK,\n\t\t\t\t\t\t\tpdev->vendor_id, pdev->device_id);\n\t\t\t\tblog(LOG_DEBUG, \"libpci lookup: device_name: %s, vendor_name: %s\",\n\t\t\t\t     card->device_name.c_str(), card->vendor_name.c_str());\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tpci_cleanup(pacc);\n\n\t/* Iterate through drm_cards to determine a string match count\n\t * against the GPU string tokens from the OpenGL identification.\n\t */\n\tfor (auto card = begin(drm_cards); card != end(drm_cards); ++card) {\n\t\tcard->match_count = 0;\n\t\tfor (auto token = begin(gpu_tokens); token != end(gpu_tokens); ++token) {\n\t\t\tstd::string card_device_name = card->device_name;\n\t\t\tstd::string card_gpu_vendor = card->vendor_name;\n\t\t\tstd::transform(card_device_name.begin(), card_device_name.end(), card_device_name.begin(),\n\t\t\t\t       ::tolower);\n\t\t\tstd::transform(card_gpu_vendor.begin(), card_gpu_vendor.end(), card_gpu_vendor.begin(),\n\t\t\t\t       ::tolower);\n\n\t\t\t// Compare GPU string tokens to PCI device name\n\t\t\tstd::size_t found = card_device_name.find(*token);\n\t\t\tif (found != std::string::npos) {\n\t\t\t\tcard->match_count++;\n\t\t\t\tblog(LOG_DEBUG, \"Found %s in PCI device name\", (*token).c_str());\n\t\t\t}\n\t\t\t// Compare GPU string tokens to PCI vendor name\n\t\t\tfound = card_gpu_vendor.find(*token);\n\t\t\tif (found != std::string::npos) {\n\t\t\t\tcard->match_count++;\n\t\t\t\tblog(LOG_DEBUG, \"Found %s in PCI vendor name\", (*token).c_str());\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Sort the cards based on the highest match strength.\n\t * In the case of multiple cards and the first one is not a higher\n\t * match, there is ambiguity and all we can do is log a warning.\n\t * The chance of this happening is low, but not impossible.\n\t */\n\tstd::sort(drm_cards.begin(), drm_cards.end(), compare_match_strength);\n\tif ((drm_cards.size() > 1) && (std::next(begin(drm_cards))->match_count) >= begin(drm_cards)->match_count) {\n\t\tblog(LOG_WARNING, \"%s: Ambiguous GPU association. Possible incorrect sort order.\", __FUNCTION__);\n\t\tfor (auto card = begin(drm_cards); card != end(drm_cards); ++card) {\n\t\t\tblog(LOG_DEBUG, \"Total matches for card %s: %u\", card->device_name.c_str(), card->match_count);\n\t\t}\n\t}\n\n\t/* Iterate through the sorted list of cards and generate\n\t * the GoLiveApi GPU list.\n\t */\n\tfor (auto card = begin(drm_cards); card != end(drm_cards); ++card) {\n\t\tgpu.device_id = card->device_id;\n\t\tgpu.vendor_id = card->vendor_id;\n\t\tgpu.model = card->device_name;\n\t\tadjust_gpu_model(gpu.model);\n\n\t\tif (card == begin(drm_cards)) {\n\t\t\t/* The first card in the list corresponds to the\n\t\t\t * driver version and GPU memory information obtained\n\t\t\t * previously by OpenGL calls into the GPU.\n\t\t\t */\n\t\t\tgpu.driver_version = gs_driver_version;\n\t\t\tgpu.dedicated_video_memory = dedicated_video_memory;\n\t\t\tgpu.shared_system_memory = shared_system_memory;\n\t\t} else {\n\t\t\t/* The driver version for the other device(s)\n\t\t\t * is not accessible easily in a common location.\n\t\t\t */\n\t\t\tgpu.driver_version = \"Unknown\";\n\n\t\t\t/* Use the GPU memory info discovered with get_drm_card_info()\n\t\t\t * stored in drm_cards. amdgpu driver exposes the GPU memory\n\t\t\t * info via sysfs, NVIDIA does not.\n\t\t\t */\n\t\t\tgpu.dedicated_video_memory = card->dedicated_vram_total;\n\t\t\tgpu.shared_system_memory = card->shared_system_memory_total;\n\t\t}\n\t\tadapter_info.push_back(gpu);\n\t}\n\n\treturn adapter_info;\n}\n} // namespace\n\nvoid system_info(GoLiveApi::Capabilities &capabilities)\n{\n\t// Determine the GPU capabilities\n\tcapabilities.gpu = system_gpu_data();\n\n\t// Determine the CPU capabilities\n\t{\n\t\tauto &cpu_data = capabilities.cpu;\n\t\tcpu_data.physical_cores = os_get_physical_cores();\n\t\tcpu_data.logical_cores = os_get_logical_cores();\n\n\t\tif (!get_cpu_name(cpu_data.name)) {\n\t\t\tcpu_data.name = \"Unknown\";\n\t\t}\n\n\t\tuint32_t cpu_freq;\n\t\tif (get_cpu_freq(cpu_freq)) {\n\t\t\tcpu_data.speed = cpu_freq;\n\t\t} else {\n\t\t\tcpu_data.speed = 0;\n\t\t}\n\t}\n\n\t// Determine the memory capabilities\n\t{\n\t\tauto &memory_data = capabilities.memory;\n\t\tmemory_data.total = os_get_sys_total_size();\n\t\tmemory_data.free = os_get_sys_free_size();\n\t}\n\n\t// Reporting of gaming features not supported on Linux\n\tUNUSED_PARAMETER(capabilities.gaming_features);\n\n\t// Determine the system capabilities\n\t{\n\t\tauto &system_data = capabilities.system;\n\n\t\tif (!get_distribution_info(system_data.name, system_data.release)) {\n\t\t\tsystem_data.name = \"Linux-based distribution\";\n\t\t\tsystem_data.release = \"unknown\";\n\t\t}\n\n\t\tstruct utsname utsinfo;\n\t\tstatic const uint16_t max_sys_data_version_sz = 128;\n\t\tif (uname(&utsinfo) == 0) {\n\t\t\t/* To determine if the host is 64-bit, check if the machine\n\t\t\t * name contains \"64\", as in \"x86_64\" or \"aarch64\".\n\t\t\t */\n\t\t\tsystem_data.bits = strstr(utsinfo.machine, \"64\") ? 64 : 32;\n\n\t\t\t/* To determine if the host CPU is ARM based, check if the\n\t\t\t * machine name contains \"aarch\".\n\t\t\t */\n\t\t\tsystem_data.arm = strstr(utsinfo.machine, \"aarch\") ? true : false;\n\n\t\t\t/* Send the sysname (usually \"Linux\"), kernel version and\n\t\t\t * release reported by utsname as the version string.\n\t\t\t * The code below will produce something like:\n\t\t\t *\n\t\t\t * \"Linux 6.5.0-41-generic #41~22.04.2-Ubuntu SMP PREEMPT_DYNAMIC\n\t\t\t *  Mon Jun  3 11:32:55 UTC 2\"\n\t\t\t */\n\t\t\tsystem_data.version = utsinfo.sysname;\n\t\t\tsystem_data.version.append(\" \");\n\t\t\tsystem_data.version.append(utsinfo.release);\n\t\t\tsystem_data.version.append(\" \");\n\t\t\tsystem_data.version.append(utsinfo.version);\n\t\t\t// Ensure system_data.version string is within the maximum size\n\t\t\tif (system_data.version.size() > max_sys_data_version_sz) {\n\t\t\t\tsystem_data.version.resize(max_sys_data_version_sz);\n\t\t\t}\n\t\t} else {\n\t\t\tUNUSED_PARAMETER(system_data.bits);\n\t\t\tUNUSED_PARAMETER(system_data.arm);\n\t\t\tsystem_data.version = \"unknown\";\n\t\t}\n\n\t\t// On Linux-based distros, there's no build or revision info\n\t\tUNUSED_PARAMETER(system_data.build);\n\t\tUNUSED_PARAMETER(system_data.revision);\n\n\t\tsystem_data.armEmulation = os_get_emulation_status();\n\t}\n}\n"
  },
  {
    "path": "frontend/utility/system-info-windows.cpp",
    "content": "#include \"system-info.hpp\"\n\n#include <util/dstr.hpp>\n#include <util/platform.h>\n#include <util/windows/ComPtr.hpp>\n#include <util/windows/win-registry.h>\n#include <util/windows/win-version.h>\n\n#include <cinttypes>\n\n#include <dxgi.h>\n#include <shlobj.h>\n\nstatic std::optional<std::vector<GoLiveApi::Gpu>> system_gpu_data()\n{\n\tComPtr<IDXGIFactory1> factory;\n\tComPtr<IDXGIAdapter1> adapter;\n\tHRESULT hr;\n\tUINT i;\n\n\thr = CreateDXGIFactory1(IID_PPV_ARGS(&factory));\n\tif (FAILED(hr))\n\t\treturn std::nullopt;\n\n\tstd::vector<GoLiveApi::Gpu> adapter_info;\n\n\tfor (i = 0; factory->EnumAdapters1(i, adapter.Assign()) == S_OK; ++i) {\n\t\tDXGI_ADAPTER_DESC desc;\n\t\tchar name[512] = \"\";\n\t\tchar driver_version[512] = \"\";\n\n\t\thr = adapter->GetDesc(&desc);\n\t\tif (FAILED(hr))\n\t\t\tcontinue;\n\n\t\t/* ignore Microsoft's 'basic' renderer' */\n\t\tif (desc.VendorId == 0x1414 && desc.DeviceId == 0x8c)\n\t\t\tcontinue;\n\n\t\tos_wcs_to_utf8(desc.Description, 0, name, sizeof(name));\n\n\t\tGoLiveApi::Gpu data;\n\t\tdata.model = name;\n\n\t\tdata.vendor_id = desc.VendorId;\n\t\tdata.device_id = desc.DeviceId;\n\n\t\tdata.dedicated_video_memory = desc.DedicatedVideoMemory;\n\t\tdata.shared_system_memory = desc.SharedSystemMemory;\n\n\t\t/* driver version */\n\t\tLARGE_INTEGER umd;\n\t\thr = adapter->CheckInterfaceSupport(__uuidof(IDXGIDevice), &umd);\n\t\tif (SUCCEEDED(hr)) {\n\t\t\tconst uint64_t version = umd.QuadPart;\n\t\t\tconst uint16_t aa = (version >> 48) & 0xffff;\n\t\t\tconst uint16_t bb = (version >> 32) & 0xffff;\n\t\t\tconst uint16_t ccccc = (version >> 16) & 0xffff;\n\t\t\tconst uint16_t ddddd = version & 0xffff;\n\t\t\tsnprintf(driver_version, sizeof(driver_version), \"%\" PRIu16 \".%\" PRIu16 \".%\" PRIu16 \".%\" PRIu16,\n\t\t\t\t aa, bb, ccccc, ddddd);\n\t\t\tdata.driver_version = driver_version;\n\t\t}\n\n\t\tadapter_info.push_back(data);\n\t}\n\n\treturn adapter_info;\n}\n\nstatic void get_processor_info(char **name, DWORD *speed)\n{\n\tHKEY key;\n\twchar_t data[1024];\n\tDWORD size;\n\tLSTATUS status;\n\n\tmemset(data, 0, sizeof(data));\n\n\tstatus = RegOpenKeyW(HKEY_LOCAL_MACHINE, L\"HARDWARE\\\\DESCRIPTION\\\\System\\\\CentralProcessor\\\\0\", &key);\n\tif (status != ERROR_SUCCESS)\n\t\treturn;\n\n\tsize = sizeof(data);\n\tstatus = RegQueryValueExW(key, L\"ProcessorNameString\", NULL, NULL, (LPBYTE)data, &size);\n\tif (status == ERROR_SUCCESS) {\n\t\tos_wcs_to_utf8_ptr(data, 0, name);\n\t} else {\n\t\t*name = 0;\n\t}\n\n\tsize = sizeof(*speed);\n\tstatus = RegQueryValueExW(key, L\"~MHz\", NULL, NULL, (LPBYTE)speed, &size);\n\tif (status != ERROR_SUCCESS)\n\t\t*speed = 0;\n\n\tRegCloseKey(key);\n}\n\n#define WIN10_GAME_BAR_REG_KEY L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\GameDVR\"\n#define WIN10_GAME_DVR_POLICY_REG_KEY L\"SOFTWARE\\\\Policies\\\\Microsoft\\\\Windows\\\\GameDVR\"\n#define WIN10_GAME_DVR_REG_KEY L\"System\\\\GameConfigStore\"\n#define WIN10_GAME_MODE_REG_KEY L\"Software\\\\Microsoft\\\\GameBar\"\n#define WIN10_HAGS_REG_KEY L\"SYSTEM\\\\CurrentControlSet\\\\Control\\\\GraphicsDrivers\"\n\nstatic std::optional<GoLiveApi::GamingFeatures> get_gaming_features_data(const win_version_info &ver)\n{\n\tuint32_t win_ver = (ver.major << 8) | ver.minor;\n\tif (win_ver < 0xA00)\n\t\treturn std::nullopt;\n\n\tGoLiveApi::GamingFeatures gaming_features;\n\n\tstruct feature_mapping_s {\n\t\tstd::optional<bool> *field;\n\t\tHKEY hkey;\n\t\tLPCWSTR sub_key;\n\t\tLPCWSTR value_name;\n\t\tLPCWSTR backup_value_name;\n\t\tbool non_existence_is_false;\n\t\tDWORD disabled_value;\n\t};\n\tstruct feature_mapping_s features[] = {\n\t\t{&gaming_features.game_bar_enabled, HKEY_CURRENT_USER, WIN10_GAME_BAR_REG_KEY, L\"AppCaptureEnabled\", 0,\n\t\t false, 0},\n\t\t{&gaming_features.game_dvr_allowed, HKEY_CURRENT_USER, WIN10_GAME_DVR_POLICY_REG_KEY, L\"AllowGameDVR\",\n\t\t 0, false, 0},\n\t\t{&gaming_features.game_dvr_enabled, HKEY_CURRENT_USER, WIN10_GAME_DVR_REG_KEY, L\"GameDVR_Enabled\", 0,\n\t\t false, 0},\n\t\t{&gaming_features.game_dvr_bg_recording, HKEY_CURRENT_USER, WIN10_GAME_BAR_REG_KEY,\n\t\t L\"HistoricalCaptureEnabled\", 0, false, 0},\n\t\t{&gaming_features.game_mode_enabled, HKEY_CURRENT_USER, WIN10_GAME_MODE_REG_KEY, L\"AutoGameModeEnabled\",\n\t\t L\"AllowAutoGameMode\", false, 0},\n\t\t{&gaming_features.hags_enabled, HKEY_LOCAL_MACHINE, WIN10_HAGS_REG_KEY, L\"HwSchMode\", 0, true, 1}};\n\n\tfor (int i = 0; i < sizeof(features) / sizeof(*features); ++i) {\n\t\tstruct reg_dword info;\n\n\t\tget_reg_dword(features[i].hkey, features[i].sub_key, features[i].value_name, &info);\n\n\t\tif (info.status != ERROR_SUCCESS && features[i].backup_value_name) {\n\t\t\tget_reg_dword(features[i].hkey, features[i].sub_key, features[i].backup_value_name, &info);\n\t\t}\n\n\t\tif (info.status == ERROR_SUCCESS) {\n\t\t\t*features[i].field = info.return_value != features[i].disabled_value;\n\t\t} else if (features[i].non_existence_is_false) {\n\t\t\t*features[i].field = false;\n\t\t}\n\t}\n\n\treturn gaming_features;\n}\n\nstatic inline bool get_reg_sz(HKEY key, const wchar_t *val, wchar_t *buf, DWORD size)\n{\n\tconst LSTATUS status = RegGetValueW(key, NULL, val, RRF_RT_REG_SZ, NULL, buf, &size);\n\treturn status == ERROR_SUCCESS;\n}\n\n#define MAX_SZ_LEN 256\n#define WINVER_REG_KEY L\"SOFTWARE\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\"\n\nstatic char win_release_id[MAX_SZ_LEN] = \"unavailable\";\n\nstatic inline void get_reg_ver(struct win_version_info *ver)\n{\n\tHKEY key;\n\tDWORD size, dw_val;\n\tLSTATUS status;\n\twchar_t str[MAX_SZ_LEN];\n\n\tstatus = RegOpenKeyW(HKEY_LOCAL_MACHINE, WINVER_REG_KEY, &key);\n\tif (status != ERROR_SUCCESS)\n\t\treturn;\n\n\tsize = sizeof(dw_val);\n\n\tstatus = RegQueryValueExW(key, L\"CurrentMajorVersionNumber\", NULL, NULL, (LPBYTE)&dw_val, &size);\n\tif (status == ERROR_SUCCESS)\n\t\tver->major = (int)dw_val;\n\n\tstatus = RegQueryValueExW(key, L\"CurrentMinorVersionNumber\", NULL, NULL, (LPBYTE)&dw_val, &size);\n\tif (status == ERROR_SUCCESS)\n\t\tver->minor = (int)dw_val;\n\n\tstatus = RegQueryValueExW(key, L\"UBR\", NULL, NULL, (LPBYTE)&dw_val, &size);\n\tif (status == ERROR_SUCCESS)\n\t\tver->revis = (int)dw_val;\n\n\tif (get_reg_sz(key, L\"CurrentBuildNumber\", str, sizeof(str))) {\n\t\tver->build = wcstol(str, NULL, 10);\n\t}\n\n\tconst wchar_t *release_key = ver->build > 19041 ? L\"DisplayVersion\" : L\"ReleaseId\";\n\tif (get_reg_sz(key, release_key, str, sizeof(str))) {\n\t\tos_wcs_to_utf8(str, 0, win_release_id, MAX_SZ_LEN);\n\t}\n\n\tRegCloseKey(key);\n}\n\nvoid system_info(GoLiveApi::Capabilities &capabilities)\n{\n\tchar tmpstr[1024];\n\n\tcapabilities.gpu = system_gpu_data();\n\n\t{\n\t\tauto &cpu_data = capabilities.cpu;\n\t\tcpu_data.physical_cores = os_get_physical_cores();\n\t\tcpu_data.logical_cores = os_get_logical_cores();\n\t\tDWORD processorSpeed;\n\t\tchar *processorName;\n\t\tget_processor_info(&processorName, &processorSpeed);\n\t\tif (processorSpeed)\n\t\t\tcpu_data.speed = processorSpeed;\n\t\tif (processorName)\n\t\t\tcpu_data.name = processorName;\n\t\tbfree(processorName);\n\t}\n\n\t{\n\t\tauto &memory_data = capabilities.memory;\n\t\tmemory_data.total = os_get_sys_total_size();\n\t\tmemory_data.free = os_get_sys_free_size();\n\t}\n\n\tstruct win_version_info ver;\n\tget_win_ver(&ver);\n\tget_reg_ver(&ver);\n\n\t// Gaming features\n\tcapabilities.gaming_features = get_gaming_features_data(ver);\n\n\t{\n\t\tauto &system_data = capabilities.system;\n\n\t\tsnprintf(tmpstr, sizeof(tmpstr), \"%d.%d\", ver.major, ver.minor);\n\n\t\tsystem_data.version = tmpstr;\n\t\tsystem_data.name = \"Windows\";\n\t\tsystem_data.build = ver.build;\n\t\tsystem_data.release = win_release_id;\n\t\tsystem_data.revision = std::to_string(ver.revis);\n\t\tsystem_data.bits = is_64_bit_windows() ? 64 : 32;\n\t\tsystem_data.arm = is_arm64_windows();\n\t\tsystem_data.armEmulation = os_get_emulation_status();\n\t}\n}\n"
  },
  {
    "path": "frontend/utility/system-info.hpp",
    "content": "#pragma once\n\n#include \"models/multitrack-video.hpp\"\n\nvoid system_info(GoLiveApi::Capabilities &capabilities);\n"
  },
  {
    "path": "frontend/utility/undo_stack.cpp",
    "content": "#include \"undo_stack.hpp\"\n\n#include <OBSApp.hpp>\n\n#include \"moc_undo_stack.cpp\"\n\n#define MAX_STACK_SIZE 5000\n\nundo_stack::undo_stack(ui_ptr ui) : ui(ui)\n{\n\tQObject::connect(&repeat_reset_timer, &QTimer::timeout, this, &undo_stack::reset_repeatable_state);\n\trepeat_reset_timer.setSingleShot(true);\n\trepeat_reset_timer.setInterval(3000);\n}\n\nvoid undo_stack::reset_repeatable_state()\n{\n\tlast_is_repeatable = false;\n}\n\nvoid undo_stack::clear()\n{\n\tundo_items.clear();\n\tredo_items.clear();\n\tlast_is_repeatable = false;\n\n\tui->actionMainUndo->setText(QTStr(\"Undo.Undo\"));\n\tui->actionMainRedo->setText(QTStr(\"Undo.Redo\"));\n\n\tui->actionMainUndo->setDisabled(true);\n\tui->actionMainRedo->setDisabled(true);\n}\n\nvoid undo_stack::add_action(const QString &name, const undo_redo_cb &undo, const undo_redo_cb &redo,\n\t\t\t    const std::string &undo_data, const std::string &redo_data, bool repeatable)\n{\n\tif (!is_enabled())\n\t\treturn;\n\n\twhile (undo_items.size() >= MAX_STACK_SIZE) {\n\t\tundo_redo_t item = undo_items.back();\n\t\tundo_items.pop_back();\n\t}\n\n\tif (repeatable) {\n\t\trepeat_reset_timer.start();\n\t}\n\n\tif (last_is_repeatable && repeatable && name == undo_items[0].name) {\n\t\tundo_items[0].redo = redo;\n\t\tundo_items[0].redo_data = redo_data;\n\t\treturn;\n\t}\n\n\tundo_redo_t n = {name, undo_data, redo_data, undo, redo};\n\n\tlast_is_repeatable = repeatable;\n\tundo_items.push_front(n);\n\tclear_redo();\n\n\tui->actionMainUndo->setText(QTStr(\"Undo.Item.Undo\").arg(name));\n\tui->actionMainUndo->setEnabled(true);\n\n\tui->actionMainRedo->setText(QTStr(\"Undo.Redo\"));\n\tui->actionMainRedo->setDisabled(true);\n}\n\nvoid undo_stack::undo()\n{\n\tif (undo_items.size() == 0 || !is_enabled())\n\t\treturn;\n\n\tlast_is_repeatable = false;\n\n\tundo_redo_t temp = undo_items.front();\n\ttemp.undo(temp.undo_data);\n\tredo_items.push_front(temp);\n\tundo_items.pop_front();\n\n\tui->actionMainRedo->setText(QTStr(\"Undo.Item.Redo\").arg(temp.name));\n\tui->actionMainRedo->setEnabled(true);\n\n\tif (undo_items.size() == 0) {\n\t\tui->actionMainUndo->setDisabled(true);\n\t\tui->actionMainUndo->setText(QTStr(\"Undo.Undo\"));\n\t} else {\n\t\tui->actionMainUndo->setText(QTStr(\"Undo.Item.Undo\").arg(undo_items.front().name));\n\t}\n}\n\nvoid undo_stack::redo()\n{\n\tif (redo_items.size() == 0 || !is_enabled())\n\t\treturn;\n\n\tlast_is_repeatable = false;\n\n\tundo_redo_t temp = redo_items.front();\n\ttemp.redo(temp.redo_data);\n\tundo_items.push_front(temp);\n\tredo_items.pop_front();\n\n\tui->actionMainUndo->setText(QTStr(\"Undo.Item.Undo\").arg(temp.name));\n\tui->actionMainUndo->setEnabled(true);\n\n\tif (redo_items.size() == 0) {\n\t\tui->actionMainRedo->setDisabled(true);\n\t\tui->actionMainRedo->setText(QTStr(\"Undo.Redo\"));\n\t} else {\n\t\tui->actionMainRedo->setText(QTStr(\"Undo.Item.Redo\").arg(redo_items.front().name));\n\t}\n}\n\nvoid undo_stack::enable_internal()\n{\n\tlast_is_repeatable = false;\n\n\tui->actionMainUndo->setDisabled(false);\n\tif (redo_items.size() > 0)\n\t\tui->actionMainRedo->setDisabled(false);\n}\n\nvoid undo_stack::disable_internal()\n{\n\tlast_is_repeatable = false;\n\n\tui->actionMainUndo->setDisabled(true);\n\tui->actionMainRedo->setDisabled(true);\n}\n\nvoid undo_stack::enable()\n{\n\tenabled = true;\n\tif (is_enabled())\n\t\tenable_internal();\n}\n\nvoid undo_stack::disable()\n{\n\tif (is_enabled())\n\t\tdisable_internal();\n\tenabled = false;\n}\n\nvoid undo_stack::push_disabled()\n{\n\tif (is_enabled())\n\t\tdisable_internal();\n\tdisable_refs++;\n}\n\nvoid undo_stack::pop_disabled()\n{\n\tdisable_refs--;\n\tif (is_enabled())\n\t\tenable_internal();\n}\n\nvoid undo_stack::clear_redo()\n{\n\tredo_items.clear();\n}\n"
  },
  {
    "path": "frontend/utility/undo_stack.hpp",
    "content": "#pragma once\n\n#include \"ui_OBSBasic.h\"\n\n#include <QObject>\n#include <QString>\n#include <QTimer>\n\n#include <deque>\n#include <string>\n\nclass undo_stack : public QObject {\n\tQ_OBJECT\n\n\ttypedef std::function<void(const std::string &data)> undo_redo_cb;\n\ttypedef std::function<void(bool is_undo)> func;\n\ttypedef std::unique_ptr<Ui::OBSBasic> &ui_ptr;\n\n\tstruct undo_redo_t {\n\t\tQString name;\n\t\tstd::string undo_data;\n\t\tstd::string redo_data;\n\t\tundo_redo_cb undo;\n\t\tundo_redo_cb redo;\n\t};\n\n\tui_ptr ui;\n\tstd::deque<undo_redo_t> undo_items;\n\tstd::deque<undo_redo_t> redo_items;\n\tint disable_refs = 0;\n\tbool enabled = true;\n\tbool last_is_repeatable = false;\n\n\tQTimer repeat_reset_timer;\n\n\tinline bool is_enabled() const { return !disable_refs && enabled; }\n\n\tvoid enable_internal();\n\tvoid disable_internal();\n\tvoid clear_redo();\n\nprivate slots:\n\tvoid reset_repeatable_state();\n\npublic:\n\tundo_stack(ui_ptr ui);\n\n\tvoid enable();\n\tvoid disable();\n\tvoid push_disabled();\n\tvoid pop_disabled();\n\n\tvoid clear();\n\tvoid add_action(const QString &name, const undo_redo_cb &undo, const undo_redo_cb &redo,\n\t\t\tconst std::string &undo_data, const std::string &redo_data, bool repeatable = false);\n\tvoid undo();\n\tvoid redo();\n};\n"
  },
  {
    "path": "frontend/utility/update-helpers.cpp",
    "content": "#include \"update-helpers.hpp\"\n\n#include <stdarg.h>\n\nstd::string vstrprintf(const char *format, va_list args)\n{\n\tva_list args2;\n\n\tif (!format)\n\t\treturn std::string();\n\n\tva_copy(args2, args);\n\n\tstd::string str;\n\tint size = (int)vsnprintf(nullptr, 0, format, args2) + 1;\n\tstr.resize(size);\n\tvsnprintf(&str[0], size, format, args);\n\n\tva_end(args2);\n\n\treturn str;\n}\n\nstd::string strprintf(const char *format, ...)\n{\n\tva_list args;\n\n\tva_start(args, format);\n\tstd::string str;\n\tstr = vstrprintf(format, args);\n\tva_end(args);\n\n\treturn str;\n}\n"
  },
  {
    "path": "frontend/utility/update-helpers.hpp",
    "content": "#pragma once\n\n#include <string>\n\nstd::string strprintf(const char *format, ...);\n"
  },
  {
    "path": "frontend/utility/win-dll-blocklist.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Richard Stanway\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <Windows.h>\n#include <psapi.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <inttypes.h>\n#include \"detours.h\"\n#include \"obs.h\"\n\n// Undocumented NT structs / function definitions !\ntypedef enum _SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 } SECTION_INHERIT;\n\ntypedef enum _SECTION_INFORMATION_CLASS {\n\tSectionBasicInformation = 0,\n\tSectionImageInformation\n} SECTION_INFORMATION_CLASS;\n\ntypedef struct _SECTION_BASIC_INFORMATION {\n\tPVOID BaseAddress;\n\tULONG Attributes;\n\tLARGE_INTEGER Size;\n} SECTION_BASIC_INFORMATION;\n\ntypedef NTSTATUS(STDMETHODCALLTYPE *fn_NtMapViewOfSection)(HANDLE, HANDLE, PVOID, ULONG_PTR, SIZE_T, PLARGE_INTEGER,\n\t\t\t\t\t\t\t   PSIZE_T, SECTION_INHERIT, ULONG, ULONG);\n\ntypedef NTSTATUS(STDMETHODCALLTYPE *fn_NtUnmapViewOfSection)(HANDLE, PVOID);\n\ntypedef NTSTATUS(STDMETHODCALLTYPE *fn_NtQuerySection)(HANDLE, SECTION_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);\n\nstatic fn_NtMapViewOfSection ntMap;\nstatic fn_NtUnmapViewOfSection ntUnmap;\nstatic fn_NtQuerySection ntQuery;\n\n#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)\n\n// Method of matching timestamp of DLL in PE header\ntypedef enum {\n\tTS_IGNORE = 0,      // Ignore timestamp; block all DLLs with this name\n\tTS_EQUAL,           // Block only DLL with this exact timestamp\n\tTS_LESS_THAN,       // Block all DLLs with an earlier timestamp\n\tTS_GREATER_THAN,    // Block all DLLs with a later timestamp\n\tTS_ALLOW_ONLY_THIS, // Invert behavior: only allow this specific timestamp\n} ts_compare_t;\n\ntypedef struct {\n\t// DLL name, lower case\n\tconst wchar_t *name;\n\n\t// Length of name, calculated at startup - leave as zero\n\tsize_t name_len;\n\n\t// PE timestamp\n\tconst uint32_t timestamp;\n\n\t// How to treat the timestamp field\n\tconst ts_compare_t method;\n\n\t// Number of times we've blocked this DLL, for logging purposes\n\tuint64_t blocked_count;\n} blocked_module_t;\n\n/*\n * Note: The name matches at the end of the string based on its length, this allows\n * for matching DLLs that may have generic names but a problematic version only\n * exists in a certain directory. A name should always include a path component\n * so that e.g. fraps.dll doesn't match notfraps.dll.\n */\n\nstatic blocked_module_t blocked_modules[] = {\n\t// Dell / Alienware Backup & Recovery, crashes during \"Browse\" dialogs\n\t{L\"\\\\dbroverlayiconbackuped.dll\", 0, 0, TS_IGNORE},\n\n\t// RTSS, no good reason for this to be in OBS\n\t{L\"\\\\rtsshooks.dll\", 0, 0, TS_IGNORE},\n\n\t// Dolby Axon overlay\n\t{L\"\\\\axonoverlay.dll\", 0, 0, TS_IGNORE},\n\n\t// Action! Recorder Software\n\t{L\"\\\\action_x64.dll\", 0, 0, TS_IGNORE},\n\n\t// ASUS GamerOSD, breaks DX11 things\n\t{L\"\\\\atkdx11disp.dll\", 0, 0, TS_IGNORE},\n\n\t// Malware\n\t{L\"\\\\sendori.dll\", 0, 0, TS_IGNORE},\n\n\t// Astril VPN Proxy, hooks stuff and crashes\n\t{L\"\\\\asproxy64.dll\", 0, 0, TS_IGNORE},\n\n\t// Nahimic Audio\n\t{L\"\\\\nahimicmsidevprops.dll\", 0, 0, TS_IGNORE},\n\t{L\"\\\\nahimicmsiosd.dll\", 0, 0, TS_IGNORE},\n\n\t// FRAPS hook\n\t{L\"\\\\fraps64.dll\", 0, 0, TS_IGNORE},\n\n\t// ASUS GPU TWEAK II OSD\n\t{L\"\\\\gtii-osd64.dll\", 0, 0, TS_IGNORE},\n\t{L\"\\\\gtii-osd64-vk.dll\", 0, 0, TS_IGNORE},\n\n\t// EVGA Precision, D3D crashes\n\t{L\"\\\\pxshw10_x64.dll\", 0, 0, TS_IGNORE},\n\n\t// Wacom / Other tablet driver, locks up UI\n\t{L\"\\\\wintab32.dll\", 0, 0, TS_IGNORE},\n\n\t// MainConcept Image Scaler, crashes in its own thread. Block versions\n\t// older than the one Elgato uses (2016-02-15).\n\t{L\"\\\\mc_trans_video_imagescaler.dll\", 0, 1455495131, TS_LESS_THAN},\n\n\t// Weird Polish banking \"security\" software, breaks UI\n\t{L\"\\\\wslbscr64.dll\", 0, 0, TS_IGNORE},\n\n\t// Various things hooking with EasyHook that probably shouldn't touch OBS\n\t{L\"\\\\easyhook64.dll\", 0, 0, TS_IGNORE},\n\n\t// Ultramon\n\t{L\"\\\\rtsultramonhook.dll\", 0, 0, TS_IGNORE},\n\n\t// HiAlgo Boost, locks up UI\n\t{L\"\\\\hookdll.dll\", 0, 0, TS_IGNORE},\n\n\t// Adobe Core Sync? Crashes NDI.\n\t{L\"\\\\coresync_x64.dll\", 0, 0, TS_IGNORE},\n\n\t// Fasso DRM, crashes D3D\n\t{L\"\\\\f_sps.dll\", 0, 0, TS_IGNORE},\n\n\t// Korean banking \"security\" software, crashes randomly\n\t{L\"\\\\t_prevent64.dll\", 0, 0, TS_IGNORE},\n\n\t// Bandicam, doesn't unhook cleanly and freezes preview\n\t// Reference: https://github.com/obsproject/obs-studio/issues/8552\n\t{L\"\\\\bdcam64.dll\", 0, 0, TS_IGNORE},\n\n\t// \"Citrix ICAService\" that crashes during DShow enumeration\n\t// Reference: https://obsproject.com/forum/threads/165863/\n\t{L\"\\\\ctxdsendpoints64.dll\", 0, 0, TS_IGNORE},\n\n\t// Generic named unity capture filter. Unfortunately only a forked version\n\t// has a critical fix to prevent deadlocks during enumeration. We block\n\t// all versions since if someone didn't change the DLL name they likely\n\t// didn't implement the deadlock fix.\n\t// Reference: https://github.com/schellingb/UnityCapture/commit/2eabf0f\n\t{L\"\\\\unitycapturefilter64bit.dll\", 0, 0, TS_IGNORE},\n\n\t// VSeeFace capture filter < v1.13.38b3 without above fix implemented\n\t{L\"\\\\vseefacecamera64bit.dll\", 0, 1666993098, TS_LESS_THAN},\n\n\t// VTuber Maker capture filter < 2023-03-13 without above fix implemented\n\t{L\"\\\\live3dvirtualcam\\\\lib64_new2.dll\", 0, 1678695956, TS_LESS_THAN},\n\n\t// Obsolete unfixed versions of VTuber Maker capture filter\n\t{L\"\\\\live3dvirtualcam\\\\lib64_new.dll\", 0, 0, TS_IGNORE},\n\t{L\"\\\\live3dvirtualcam\\\\lib64.dll\", 0, 0, TS_IGNORE},\n\n\t// VirtualMotionCapture capture filter < 2022-12-18 without above fix\n\t// Reference: https://github.com/obsproject/obs-studio/issues/8552\n\t{L\"\\\\vmc_camerafilter64bit.dll\", 0, 1671349891, TS_LESS_THAN},\n\n\t// HolisticMotionCapture capture filter, not yet patched. Blocking\n\t// all previous versions in case an update is released.\n\t// Reference: https://github.com/obsproject/obs-studio/issues/8552\n\t{L\"\\\\holisticmotioncapturefilter64bit.dll\", 0, 1680044549, TS_LESS_THAN},\n\n\t// Elgato Stream Deck plugin < 2024-02-01\n\t// Blocking all previous versions because they have undefined behavior\n\t// that results in crashes.\n\t// Reference: https://github.com/obsproject/obs-studio/issues/10245\n\t{L\"\\\\streamdeckplugin.dll\", 0, 1706745600, TS_LESS_THAN},\n\n\t// TikTok Live Studio Virtual Camera, causes freezing and other issues during enumeration\n\t// Different versions seem to be installed in different places, so we have to match on DLL only.\n\t// Reference: https://www.hanselman.com/blog/webcam-randomly-pausing-in-obs-discord-and-websites-lsvcam-and-tiktok-studio\n\t{L\"\\\\lsvcam.dll\", 0, 0, TS_IGNORE},\n};\n\nstatic bool is_module_blocked(wchar_t *dll, uint32_t timestamp)\n{\n\tblocked_module_t *first_allowed = NULL;\n\tsize_t len;\n\n\tlen = wcslen(dll);\n\n\twcslwr(dll);\n\n\t// Default behavior is to not block\n\tbool should_block = false;\n\n\tfor (int i = 0; i < _countof(blocked_modules); i++) {\n\t\tblocked_module_t *b = &blocked_modules[i];\n\t\twchar_t *dll_ptr;\n\n\t\tif (len >= b->name_len)\n\t\t\tdll_ptr = dll + len - b->name_len;\n\t\telse\n\t\t\tdll_ptr = dll;\n\n\t\tif (!wcscmp(dll_ptr, b->name)) {\n\t\t\tif (b->method == TS_IGNORE) {\n\t\t\t\tb->blocked_count++;\n\t\t\t\treturn true;\n\t\t\t} else if (b->method == TS_EQUAL && timestamp == b->timestamp) {\n\t\t\t\tb->blocked_count++;\n\t\t\t\treturn true;\n\t\t\t} else if (b->method == TS_LESS_THAN && timestamp < b->timestamp) {\n\t\t\t\tb->blocked_count++;\n\t\t\t\treturn true;\n\t\t\t} else if (b->method == TS_GREATER_THAN && timestamp > b->timestamp) {\n\t\t\t\tb->blocked_count++;\n\t\t\t\treturn true;\n\t\t\t} else if (b->method == TS_ALLOW_ONLY_THIS) {\n\t\t\t\t// Invert default behavior to block if\n\t\t\t\t// we don't find any matching timestamps\n\t\t\t\t// for this DLL.\n\t\t\t\tshould_block = true;\n\n\t\t\t\tif (timestamp == b->timestamp)\n\t\t\t\t\treturn false;\n\n\t\t\t\t// Bit of a hack to support counting of\n\t\t\t\t// TS_ALLOW_ONLY_THIS blocks as there may\n\t\t\t\t// be multiple entries for the same DLL.\n\t\t\t\tif (!first_allowed)\n\t\t\t\t\tfirst_allowed = b;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (first_allowed)\n\t\tfirst_allowed->blocked_count++;\n\n\treturn should_block;\n}\n\nstatic NTSTATUS NtMapViewOfSection_hook(HANDLE SectionHandle, HANDLE ProcessHandle, PVOID *BaseAddress,\n\t\t\t\t\tULONG_PTR ZeroBits, SIZE_T CommitSize, PLARGE_INTEGER SectionOffset,\n\t\t\t\t\tPSIZE_T ViewSize, SECTION_INHERIT InheritDisposition, ULONG AllocationType,\n\t\t\t\t\tULONG Win32Protect)\n{\n\tSECTION_BASIC_INFORMATION section_information;\n\twchar_t fileName[MAX_PATH];\n\tSIZE_T wrote = 0;\n\tNTSTATUS ret;\n\tuint32_t timestamp = 0;\n\n\tret = ntMap(SectionHandle, ProcessHandle, BaseAddress, ZeroBits, CommitSize, SectionOffset, ViewSize,\n\t\t    InheritDisposition, AllocationType, Win32Protect);\n\n\t// Verify map and process\n\tif (ret < 0 || ProcessHandle != GetCurrentProcess())\n\t\treturn ret;\n\n\t// Fetch section information\n\tif (ntQuery(SectionHandle, SectionBasicInformation, &section_information, sizeof(section_information), &wrote) <\n\t    0)\n\t\treturn ret;\n\n\t// Verify fetch was successful\n\tif (wrote != sizeof(section_information))\n\t\treturn ret;\n\n\t// We're not interested in non-image maps\n\tif (!(section_information.Attributes & SEC_IMAGE))\n\t\treturn ret;\n\n\t// Examine the PE header. Perhaps the map is small\n\t// so wrap it in an exception handler in case we\n\t// read past the end of the buffer.\n\t__try {\n\t\tBYTE *p = (BYTE *)*BaseAddress;\n\t\tIMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)p;\n\t\tif (dos->e_magic != IMAGE_DOS_SIGNATURE)\n\t\t\treturn ret;\n\n\t\tIMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(p + dos->e_lfanew);\n\t\tif (nt->Signature != IMAGE_NT_SIGNATURE)\n\t\t\treturn ret;\n\n\t\ttimestamp = nt->FileHeader.TimeDateStamp;\n\n\t} __except (EXCEPTION_EXECUTE_HANDLER) {\n\t\treturn ret;\n\t}\n\n\t// Get the actual filename if possible\n\tif (K32GetMappedFileNameW(ProcessHandle, *BaseAddress, fileName, _countof(fileName)) == 0)\n\t\treturn ret;\n\n\tif (is_module_blocked(fileName, timestamp)) {\n\t\tntUnmap(ProcessHandle, BaseAddress);\n\t\tret = STATUS_UNSUCCESSFUL;\n\t}\n\n\treturn ret;\n}\n\nvoid install_dll_blocklist_hook(void)\n{\n\tHMODULE nt = GetModuleHandle(L\"NTDLL\");\n\tif (!nt)\n\t\treturn;\n\n\tntMap = (fn_NtMapViewOfSection)GetProcAddress(nt, \"NtMapViewOfSection\");\n\tif (!ntMap)\n\t\treturn;\n\n\tntUnmap = (fn_NtUnmapViewOfSection)GetProcAddress(nt, \"NtUnmapViewOfSection\");\n\tif (!ntUnmap)\n\t\treturn;\n\n\tntQuery = (fn_NtQuerySection)GetProcAddress(nt, \"NtQuerySection\");\n\tif (!ntQuery)\n\t\treturn;\n\n\t// Pre-compute length of all DLL names for exact matching\n\tfor (int i = 0; i < _countof(blocked_modules); i++) {\n\t\tblocked_module_t *b = &blocked_modules[i];\n\t\tb->name_len = wcslen(b->name);\n\t}\n\n\tDetourTransactionBegin();\n\n\tif (DetourAttach((PVOID *)&ntMap, NtMapViewOfSection_hook) != NO_ERROR)\n\t\tDetourTransactionAbort();\n\telse\n\t\tDetourTransactionCommit();\n}\n\nvoid log_blocked_dlls(void)\n{\n\tfor (int i = 0; i < _countof(blocked_modules); i++) {\n\t\tblocked_module_t *b = &blocked_modules[i];\n\t\tif (b->blocked_count) {\n\t\t\tblog(LOG_WARNING, \"Blocked loading of '%S' %\" PRIu64 \" time%S.\", b->name, b->blocked_count,\n\t\t\t     b->blocked_count == 1 ? L\"\" : L\"s\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "frontend/widgets/AudioMixer.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"AudioMixer.hpp\"\n\n#include <components/MenuCheckBox.hpp>\n#include <dialogs/NameDialog.hpp>\n#include <utility/item-widget-helpers.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <Idian/Utils.hpp>\n\n#include <QAction>\n#include <QCheckBox>\n#include <QHBoxLayout>\n#include <QMenu>\n#include <QPointer>\n#include <QPushButton>\n#include <QScrollArea>\n#include <QStackedWidget>\n#include <QTimer>\n#include <QToolBar>\n#include <QVBoxLayout>\n#include <QWidgetAction>\n\n#include \"moc_AudioMixer.cpp\"\n\nconstexpr int GLOBAL_SOURCE_TOTAL = 6;\n\nnamespace {\nbool isHiddenInMixer(obs_source_t *source)\n{\n\tOBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);\n\tbool hidden = obs_data_get_bool(priv_settings, \"mixer_hidden\");\n\n\treturn hidden;\n}\n\nbool isPinnedInMixer(obs_source_t *source)\n{\n\tOBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);\n\tbool hidden = obs_data_get_bool(priv_settings, \"mixer_pinned\");\n\n\treturn hidden;\n}\n\nbool isSourceAudioActive(obs_source_t *source)\n{\n\tbool active = obs_source_active(source) && obs_source_audio_active(source);\n\n\treturn active;\n}\n\nbool isVolumeLocked(obs_source_t *source)\n{\n\tOBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);\n\tbool lock = obs_data_get_bool(priv_settings, \"volume_locked\");\n\n\treturn lock;\n}\n\n} // namespace\n\nAudioMixer::AudioMixer(QWidget *parent) : QFrame(parent)\n{\n\tmixerVertical = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"VerticalVolumeControl\");\n\tshowInactive = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerShowInactive\");\n\tkeepInactiveLast = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerKeepInactiveLast\");\n\tshowHidden = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerShowHidden\");\n\tkeepHiddenLast = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerKeepHiddenLast\");\n\n\tmainLayout = new QVBoxLayout(this);\n\tmainLayout->setContentsMargins(0, 0, 0, 0);\n\tmainLayout->setSpacing(0);\n\tsetLayout(mainLayout);\n\tsetFrameShape(QFrame::NoFrame);\n\tsetLineWidth(0);\n\n\tstackedMixerArea = new QStackedWidget(this);\n\tstackedMixerArea->setObjectName(\"stackedMixerArea\");\n\n\t// Horizontal Widgets\n\thMixerScrollArea = new QScrollArea(this);\n\thMixerScrollArea->setObjectName(\"hMixerScrollArea\");\n\thMixerScrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n\thMixerScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);\n\thMixerScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\n\thMixerScrollArea->setWidgetResizable(true);\n\thMixerScrollArea->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);\n\n\thVolumeWidgets = new QWidget(this);\n\thVolumeWidgets->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);\n\thVolumeWidgets->setObjectName(\"hVolumeWidgets\");\n\n\thVolumeControlLayout = new QVBoxLayout(hVolumeWidgets);\n\thVolumeWidgets->setLayout(hVolumeControlLayout);\n\thVolumeControlLayout->setContentsMargins(0, 0, 0, 0);\n\thVolumeControlLayout->setSpacing(0);\n\thVolumeControlLayout->setAlignment(Qt::AlignTop);\n\n\thMixerScrollArea->setWidget(hVolumeWidgets);\n\n\t// Vertical Widgets\n\tvMixerScrollArea = new QScrollArea(this);\n\tvMixerScrollArea->setObjectName(\"vMixerScrollArea\");\n\tvMixerScrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n\tvMixerScrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);\n\tvMixerScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);\n\tvMixerScrollArea->setWidgetResizable(true);\n\tvMixerScrollArea->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu);\n\n\tvVolumeWidgets = new QWidget(this);\n\n\tvVolumeWidgets->setObjectName(\"vVolumeWidgets\");\n\n\tvVolumeControlLayout = new QHBoxLayout(vVolumeWidgets);\n\tvVolumeWidgets->setLayout(vVolumeControlLayout);\n\tvVolumeControlLayout->setContentsMargins(0, 0, 0, 0);\n\tvVolumeControlLayout->setSpacing(0);\n\tvVolumeControlLayout->setAlignment(Qt::AlignLeft);\n\n\tvMixerScrollArea->setWidget(vVolumeWidgets);\n\n\tstackedMixerArea->addWidget(hMixerScrollArea);\n\tstackedMixerArea->addWidget(vMixerScrollArea);\n\n\tmixerToolbar = new QToolBar(this);\n\tmixerToolbar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);\n\tmixerToolbar->setIconSize(QSize(16, 16));\n\tmixerToolbar->setFloatable(false);\n\n\tmainLayout->addWidget(stackedMixerArea);\n\tmainLayout->addWidget(mixerToolbar);\n\n\tadvAudio = new QAction(this);\n\tadvAudio->setText(QTStr(\"Basic.AdvAudio\"));\n\tadvAudio->setToolTip(QTStr(\"Basic.AdvAudio\"));\n\tQIcon advIcon;\n\tadvIcon.addFile(QString::fromUtf8(\":/settings/images/settings/advanced.svg\"), QSize(16, 16),\n\t\t\tQIcon::Mode::Normal, QIcon::State::Off);\n\tadvAudio->setIcon(advIcon);\n\tadvAudio->setObjectName(\"actionMixerToolbarAdvAudio\");\n\n\tlayoutButton = new QAction(this);\n\tlayoutButton->setText(\"\");\n\tlayoutButton->setToolTip(QTStr(\"Basic.AudioMixer.Layout.Vertical\"));\n\tQIcon layoutIcon;\n\tlayoutIcon.addFile(QString::fromUtf8(\":/res/images/layout-vertical.svg\"), QSize(16, 16), QIcon::Mode::Normal,\n\t\t\t   QIcon::State::Off);\n\tlayoutButton->setIcon(layoutIcon);\n\tlayoutButton->setObjectName(\"actionMixerToolbarToggleLayout\");\n\n\tQWidget *spacer = new QWidget(mixerToolbar);\n\tspacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);\n\n\toptionsButton = new QPushButton(mixerToolbar);\n\toptionsButton->setText(QTStr(\"Basic.AudioMixer.Options\"));\n\tidian::Utils::addClass(optionsButton, \"toolbar-button\");\n\tidian::Utils::addClass(optionsButton, \"text-bold\");\n\n\tcreateMixerContextMenu();\n\toptionsButton->setMenu(mixerMenu);\n\n\ttoggleHiddenButton = new QPushButton(mixerToolbar);\n\ttoggleHiddenButton->setCheckable(true);\n\ttoggleHiddenButton->setChecked(showHidden);\n\ttoggleHiddenButton->setText(QTStr(\"Basic.AudioMixer.HiddenTotal\").arg(0));\n\ttoggleHiddenButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);\n\tidian::Utils::addClass(toggleHiddenButton, \"toolbar-button\");\n\tidian::Utils::addClass(toggleHiddenButton, \"toggle-hidden\");\n\n\tmixerToolbar->addWidget(toggleHiddenButton);\n\n\tmixerToolbar->addSeparator();\n\tmixerToolbar->addWidget(spacer);\n\tmixerToolbar->addSeparator();\n\n\tmixerToolbar->addAction(layoutButton);\n\tmixerToolbar->addSeparator();\n\tmixerToolbar->addAction(advAudio);\n\tmixerToolbar->addSeparator();\n\tmixerToolbar->addWidget(optionsButton);\n\n\t// Setting this property on the QAction itself does not seem to work despite\n\t// the UI files doing exactly that, so we set it on the action widget directly\n\tQWidget *advAudioWidget = mixerToolbar->widgetForAction(advAudio);\n\tidian::Utils::addClass(advAudioWidget, \"icon-cogs\");\n\n\t// Connect to OBS signals\n\tsignalHandlers.reserve(signalHandlers.size() + 8);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_create\", AudioMixer::obsSourceCreate, this);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_remove\", AudioMixer::obsSourceRemove, this);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_destroy\", AudioMixer::obsSourceRemove, this);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_rename\", AudioMixer::obsSourceRename, this);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_activate\", AudioMixer::obsSourceActivated, this);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_deactivate\", AudioMixer::obsSourceDeactivated,\n\t\t\t\t    this);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_audio_activate\",\n\t\t\t\t    AudioMixer::obsSourceAudioActivated, this);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_audio_deactivate\",\n\t\t\t\t    AudioMixer::obsSourceAudioDeactivated, this);\n\n\tobs_frontend_add_event_callback(AudioMixer::onFrontendEvent, this);\n\n\t// Connect to Qt signals\n\tconnect(hMixerScrollArea, &QScrollArea::customContextMenuRequested, this,\n\t\t&AudioMixer::mixerContextMenuRequested);\n\n\tconnect(vMixerScrollArea, &QScrollArea::customContextMenuRequested, this,\n\t\t&AudioMixer::mixerContextMenuRequested);\n\n\tconnect(&updateTimer, &QTimer::timeout, this, &AudioMixer::updateVolumeLayouts);\n\tupdateTimer.setSingleShot(true);\n\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\tconnect(main, &OBSBasic::userSettingChanged, this,\n\t\t\t[this](const std::string &category, const std::string &name) {\n\t\t\t\tif (category == \"BasicWindow\" && name == \"VerticalVolumeControl\") {\n\t\t\t\t\tupdateLayout();\n\t\t\t\t} else if (category == \"BasicWindow\" && name == \"MixerShowInactive\") {\n\t\t\t\t\tupdateShowInactive();\n\t\t\t\t} else if (category == \"BasicWindow\" && name == \"MixerKeepInactiveLast\") {\n\t\t\t\t\tupdateKeepInactiveLast();\n\t\t\t\t} else if (category == \"BasicWindow\" && name == \"MixerShowHidden\") {\n\t\t\t\t\tupdateShowHidden();\n\t\t\t\t} else if (category == \"BasicWindow\" && name == \"MixerKeepHiddenLast\") {\n\t\t\t\t\tupdateKeepHiddenLast();\n\t\t\t\t} else if (category == \"BasicWindow\" && name == \"ShowListboxToolbars\") {\n\t\t\t\t\tupdateShowToolbar();\n\t\t\t\t} else if (category == \"Accessibility\" && name == \"SettingsChanged\") {\n\t\t\t\t\trefreshVolumeColors();\n\t\t\t\t}\n\t\t\t});\n\n\t\tconnect(main, &OBSBasic::mixerStatusChanged, this, &AudioMixer::queueLayoutUpdate);\n\n\t\tconnect(advAudio, &QAction::triggered, main, &OBSBasic::on_actionAdvAudioProperties_triggered,\n\t\t\tQt::DirectConnection);\n\t\tconnect(toggleHiddenButton, &QPushButton::clicked, this, &AudioMixer::toggleShowHidden);\n\t\tconnect(layoutButton, &QAction::triggered, main, &OBSBasic::toggleMixerLayout);\n\t}\n\n\tupdateShowToolbar();\n\tupdatePreviewSources();\n\tupdatePreviewHandlers();\n\tupdateGlobalSources();\n\n\treloadVolumeControls();\n}\n\nAudioMixer::~AudioMixer()\n{\n\tsignalHandlers.clear();\n\n\tpreviewSources.clear();\n\tglobalSources.clear();\n\n\tclearVolumeControls();\n\n\tobs_frontend_remove_event_callback(AudioMixer::onFrontendEvent, this);\n}\n\nvoid AudioMixer::updateLayout()\n{\n\tbool vertical = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"VerticalVolumeControl\");\n\tsetMixerLayoutVertical(vertical);\n\n\tupdateVolumeLayouts();\n}\n\nvoid AudioMixer::toggleShowInactive(bool checked)\n{\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerShowInactive\", checked);\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\temit main->userSettingChanged(\"BasicWindow\", \"MixerShowInactive\");\n\t}\n}\n\nvoid AudioMixer::toggleKeepInactiveLast(bool checked)\n{\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerKeepInactiveLast\", checked);\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\temit main->userSettingChanged(\"BasicWindow\", \"MixerKeepInactiveLast\");\n\t}\n}\n\nvoid AudioMixer::toggleShowHidden(bool checked)\n{\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerShowHidden\", checked);\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\temit main->userSettingChanged(\"BasicWindow\", \"MixerShowHidden\");\n\t}\n}\n\nvoid AudioMixer::toggleKeepHiddenLast(bool checked)\n{\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerKeepHiddenLast\", checked);\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main) {\n\t\temit main->userSettingChanged(\"BasicWindow\", \"MixerKeepHiddenLast\");\n\t}\n}\n\nVolumeControl *AudioMixer::createVolumeControl(obs_source_t *source)\n{\n\tbool vertical = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"VerticalVolumeControl\");\n\tVolumeControl *control = new VolumeControl(source, this, vertical);\n\n\tcontrol->setGlobalInMixer(isSourceGlobal(source));\n\tcontrol->setHiddenInMixer(isHiddenInMixer(source));\n\tcontrol->setPinnedInMixer(isPinnedInMixer(source));\n\n\tcontrol->enableSlider(!isVolumeLocked(source));\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tdouble meterDecayRate = config_get_double(main->Config(), \"Audio\", \"MeterDecayRate\");\n\tcontrol->setMeterDecayRate(meterDecayRate);\n\n\tuint64_t peakMeterTypeIdx = config_get_uint(main->Config(), \"Audio\", \"PeakMeterType\");\n\n\tobs_peak_meter_type peakMeterType;\n\tswitch (peakMeterTypeIdx) {\n\tcase 0:\n\t\tpeakMeterType = SAMPLE_PEAK_METER;\n\t\tbreak;\n\tcase 1:\n\t\tpeakMeterType = TRUE_PEAK_METER;\n\t\tbreak;\n\tdefault:\n\t\tpeakMeterType = SAMPLE_PEAK_METER;\n\t\tbreak;\n\t}\n\n\tcontrol->setPeakMeterType(peakMeterType);\n\n\tconnect(control, &VolumeControl::unhideAll, this, &AudioMixer::unhideAllAudioControls);\n\n\treturn control;\n}\n\nvoid AudioMixer::updateControlVisibility(QString uuid)\n{\n\tauto item = volumeList.find(uuid);\n\tif (item == volumeList.end()) {\n\t\treturn;\n\t}\n\n\tVolumeControl *control = item->second;\n\tbool show = getMixerVisibilityForControl(control);\n\n\tif (show) {\n\t\tcontrol->show();\n\t} else {\n\t\tcontrol->hide();\n\t}\n\n\tqueueLayoutUpdate();\n}\n\nvoid AudioMixer::addSource(QString uuid)\n{\n\taddControlForUuid(uuid);\n\tupdatePreviewSources();\n\tupdateGlobalSources();\n}\n\nvoid AudioMixer::removeSource(QString uuid)\n{\n\tremoveControlForUuid(uuid);\n\tupdatePreviewSources();\n\tupdateGlobalSources();\n}\n\nvoid AudioMixer::updatePreviewSources()\n{\n\tbool isStudioMode = obs_frontend_preview_program_mode_active();\n\tclearPreviewSources();\n\n\tif (isStudioMode) {\n\t\tOBSSourceAutoRelease previewSource = obs_frontend_get_current_preview_scene();\n\t\tif (!previewSource) {\n\t\t\treturn;\n\t\t}\n\n\t\tobs_scene_t *previewScene = obs_scene_from_source(previewSource);\n\t\tif (!previewScene) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!previewScene) {\n\t\t\treturn;\n\t\t}\n\n\t\tauto getPreviewSources = [this](obs_scene_t *, obs_sceneitem_t *item) {\n\t\t\tif (!obs_sceneitem_visible(item)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tobs_source_t *source = obs_sceneitem_get_source(item);\n\t\t\tif (!source) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tuint32_t flags = obs_source_get_output_flags(source);\n\t\t\tif ((flags & OBS_SOURCE_AUDIO) == 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (!obs_source_audio_active(source)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tauto uuidPointer = obs_source_get_uuid(source);\n\t\t\tif (uuidPointer && *uuidPointer) {\n\t\t\t\tpreviewSources.insert(QString::fromUtf8(uuidPointer));\n\t\t\t}\n\n\t\t\treturn true;\n\t\t};\n\n\t\tusing getPreviewSources_t = decltype(getPreviewSources);\n\n\t\tauto previewEnum = [](obs_scene_t *scene, obs_sceneitem_t *item, void *data) -> bool {\n\t\t\treturn (*static_cast<getPreviewSources_t *>(data))(scene, item);\n\t\t};\n\n\t\tobs_scene_enum_items(previewScene, previewEnum, &getPreviewSources);\n\t}\n}\n\nvoid AudioMixer::updateGlobalSources()\n{\n\tglobalSources.clear();\n\n\tfor (int i = 1; i <= GLOBAL_SOURCE_TOTAL; i++) {\n\t\tOBSSourceAutoRelease source = obs_get_output_source(i);\n\t\tif (source) {\n\t\t\tauto uuidPointer = obs_source_get_uuid(source);\n\t\t\tif (uuidPointer && *uuidPointer) {\n\t\t\t\tglobalSources.insert(QString::fromUtf8(uuidPointer));\n\t\t\t}\n\t\t}\n\t}\n\n\tqueueLayoutUpdate();\n}\n\nQBoxLayout *AudioMixer::activeLayout() const\n{\n\tbool vertical = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"VerticalVolumeControl\");\n\n\tQBoxLayout *layout = vertical ? static_cast<QBoxLayout *>(vVolumeControlLayout)\n\t\t\t\t      : static_cast<QBoxLayout *>(hVolumeControlLayout);\n\n\treturn layout;\n}\n\nvoid AudioMixer::reloadVolumeControls()\n{\n\tclearVolumeControls();\n\n\tauto createMixerControls = [](void *param, obs_source_t *source) -> bool {\n\t\tAudioMixer *mixer = static_cast<AudioMixer *>(param);\n\n\t\tuint32_t flags = obs_source_get_output_flags(source);\n\n\t\tif ((flags & OBS_SOURCE_AUDIO) == 0) {\n\t\t\treturn true;\n\t\t}\n\n\t\tauto uuidPointer = obs_source_get_uuid(source);\n\t\tif (!uuidPointer || !*uuidPointer) {\n\t\t\treturn true;\n\t\t}\n\n\t\tmixer->addControlForUuid(QString::fromUtf8(uuidPointer));\n\t\treturn true;\n\t};\n\n\tobs_enum_sources(createMixerControls, this);\n\n\tqueueLayoutUpdate();\n}\n\nbool AudioMixer::getMixerVisibilityForControl(VolumeControl *control)\n{\n\tbool isPinned = control->mixerStatus().has(VolumeControl::MixerStatus::Pinned);\n\tbool isPreviewed = control->mixerStatus().has(VolumeControl::MixerStatus::Preview);\n\tbool isHidden = control->mixerStatus().has(VolumeControl::MixerStatus::Hidden);\n\tbool isAudioActive = control->mixerStatus().has(VolumeControl::MixerStatus::Active);\n\n\tif (isPinned) {\n\t\treturn true;\n\t}\n\n\tif (isHidden && showHidden) {\n\t\treturn true;\n\t}\n\n\tif (!isAudioActive && showInactive) {\n\t\treturn !isHidden;\n\t}\n\n\tif (isAudioActive) {\n\t\treturn !isHidden;\n\t}\n\n\tif (isPreviewed) {\n\t\treturn !isHidden;\n\t}\n\n\treturn false;\n}\n\nvoid AudioMixer::clearPreviewSources()\n{\n\tpreviewSources.clear();\n}\n\nbool AudioMixer::isSourcePreviewed(obs_source_t *source)\n{\n\tif (!source) {\n\t\treturn false;\n\t}\n\n\tauto uuidPointer = obs_source_get_uuid(source);\n\tif (!uuidPointer || !*uuidPointer) {\n\t\treturn false;\n\t}\n\n\tif (previewSources.find(QString::fromUtf8(uuidPointer)) != previewSources.end()) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool AudioMixer::isSourceGlobal(obs_source_t *source)\n{\n\tif (!source) {\n\t\treturn false;\n\t}\n\n\tauto uuidPointer = obs_source_get_uuid(source);\n\tif (!uuidPointer || !*uuidPointer) {\n\t\treturn false;\n\t}\n\n\tif (globalSources.find(QString::fromUtf8(uuidPointer)) != globalSources.end()) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid AudioMixer::clearVolumeControls()\n{\n\tfor (const auto &[uuid, control] : volumeList) {\n\t\tif (control) {\n\t\t\tcontrol->deleteLater();\n\t\t}\n\t}\n\tvolumeList.clear();\n}\n\nvoid AudioMixer::refreshVolumeColors()\n{\n\tfor (const auto &[uuid, control] : volumeList) {\n\t\tcontrol->refreshColors();\n\t}\n}\n\nvoid AudioMixer::unhideAllAudioControls()\n{\n\tfor (const auto &[uuid, control] : volumeList) {\n\t\tcontrol->setHiddenInMixer(false);\n\t}\n\n\tqueueLayoutUpdate();\n}\n\nvoid AudioMixer::queueLayoutUpdate()\n{\n\tif (!updateTimer.isActive()) {\n\t\tupdateTimer.start(0);\n\t}\n}\n\nvoid AudioMixer::updateVolumeLayouts()\n{\n\tsetUpdatesEnabled(false);\n\n\thiddenCount = 0;\n\tbool vertical = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"VerticalVolumeControl\");\n\n\tstd::vector<RankedVolume> rankedVolumes;\n\trankedVolumes.reserve(volumeList.size());\n\tfor (const auto &entry : volumeList) {\n\t\tVolumeControl *control = entry.second;\n\t\tif (control) {\n\t\t\tint sortingWeight = 0;\n\n\t\t\tOBSSource source = OBSGetStrongRef(control->weakSource());\n\t\t\tif (!source) {\n\t\t\t\tconst char *cachedName = control->getCachedName().toUtf8().constData();\n\t\t\t\tblog(LOG_INFO, \"Tried to sort VolumeControl for '%s' but source is null\", cachedName);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tbool isPreviewed = isSourcePreviewed(source);\n\t\t\tbool isGlobal = isSourceGlobal(source);\n\t\t\tbool isPinned = isPinnedInMixer(source);\n\t\t\tbool isHidden = isHiddenInMixer(source);\n\t\t\tbool isAudioActive = isSourceAudioActive(source);\n\t\t\tbool isLocked = isVolumeLocked(source);\n\n\t\t\tcontrol->mixerStatus().set(VolumeControl::MixerStatus::Preview, isPreviewed);\n\t\t\tcontrol->mixerStatus().set(VolumeControl::MixerStatus::Global, isGlobal);\n\t\t\tcontrol->mixerStatus().set(VolumeControl::MixerStatus::Pinned, isPinned);\n\t\t\tcontrol->mixerStatus().set(VolumeControl::MixerStatus::Hidden, isHidden);\n\t\t\tcontrol->mixerStatus().set(VolumeControl::MixerStatus::Active, isAudioActive);\n\t\t\tcontrol->mixerStatus().set(VolumeControl::MixerStatus::Locked, isLocked);\n\n\t\t\tif (isHidden) {\n\t\t\t\thiddenCount += 1;\n\t\t\t}\n\n\t\t\tif (!isGlobal) {\n\t\t\t\tsortingWeight += 20;\n\t\t\t}\n\n\t\t\tif (!isPinned) {\n\t\t\t\tsortingWeight += 20;\n\t\t\t}\n\n\t\t\tif (isHidden && keepHiddenLast) {\n\t\t\t\tsortingWeight += 20;\n\n\t\t\t\tif (isPreviewed) {\n\t\t\t\t\tsortingWeight -= 10;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!isAudioActive && keepInactiveLast) {\n\t\t\t\tsortingWeight += 50;\n\n\t\t\t\tif (isPreviewed) {\n\t\t\t\t\tsortingWeight -= 10;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trankedVolumes.push_back({control, sortingWeight});\n\t\t}\n\t}\n\n\tstd::sort(rankedVolumes.begin(), rankedVolumes.end(), [](const RankedVolume &a, const RankedVolume &b) {\n\t\tconst QString &nameA = a.control->getCachedName();\n\t\tconst QString &nameB = b.control->getCachedName();\n\n\t\tif (a.sortingWeight == b.sortingWeight) {\n\t\t\treturn nameA.toLower() < nameB.toLower();\n\t\t}\n\n\t\treturn a.sortingWeight < b.sortingWeight;\n\t});\n\n\tVolumeControl *prevControl = nullptr;\n\tint index = 0;\n\tQBoxLayout *layout = activeLayout();\n\n\tvMixerScrollArea->setWidgetResizable(false);\n\thMixerScrollArea->setWidgetResizable(false);\n\n\tQSize minimumSize{};\n\tfor (const auto &entry : rankedVolumes) {\n\t\tVolumeControl *volControl = entry.control;\n\t\tif (!volControl) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tlayout->insertWidget(index, volControl);\n\t\tvolControl->setVertical(vertical);\n\t\tvolControl->updateName();\n\t\tvolControl->updateMixerState();\n\n\t\tbool showControl = getMixerVisibilityForControl(volControl);\n\n\t\tif (showControl) {\n\t\t\tvolControl->show();\n\t\t} else {\n\t\t\tvolControl->hide();\n\t\t}\n\n\t\tif (prevControl == nullptr) {\n\t\t\tsetTabOrder(previousInFocusChain(), volControl->firstWidget());\n\t\t} else {\n\t\t\tsetTabOrder(prevControl->lastWidget(), volControl->firstWidget());\n\t\t}\n\t\tvolControl->updateTabOrder();\n\n\t\tprevControl = volControl;\n\n\t\tif (!minimumSize.isValid()) {\n\t\t\tminimumSize = volControl->minimumSizeHint();\n\t\t}\n\n\t\t++index;\n\t}\n\n\ttoggleHiddenButton->setText(QTStr(\"Basic.AudioMixer.HiddenTotal\").arg(hiddenCount));\n\tif (hiddenCount == 0) {\n\t\ttoggleHiddenButton->setDisabled(true);\n\t\tidian::Utils::toggleClass(toggleHiddenButton, \"text-muted\", true);\n\t} else {\n\t\ttoggleHiddenButton->setDisabled(false);\n\t\tidian::Utils::toggleClass(toggleHiddenButton, \"text-muted\", false);\n\t}\n\n\tvMixerScrollArea->setWidgetResizable(true);\n\thMixerScrollArea->setWidgetResizable(true);\n\n\tint scrollBarSize = QApplication::style()->pixelMetric(QStyle::PM_ScrollBarExtent);\n\tstackedMixerArea->setMinimumSize(minimumSize.width() + scrollBarSize, minimumSize.height() + scrollBarSize);\n\n\tsetUpdatesEnabled(true);\n}\n\nvoid AudioMixer::mixerContextMenuRequested()\n{\n\tshowMixerContextMenu();\n}\n\nvoid AudioMixer::setMixerLayoutVertical(bool vertical)\n{\n\tmixerVertical = vertical;\n\n\tif (vertical) {\n\t\tstackedMixerArea->setCurrentIndex(1);\n\n\t\tQIcon layoutIcon;\n\t\tlayoutIcon.addFile(QString::fromUtf8(\":/res/images/layout-horizontal.svg\"), QSize(16, 16),\n\t\t\t\t   QIcon::Mode::Normal, QIcon::State::Off);\n\t\tlayoutButton->setIcon(layoutIcon);\n\t\tlayoutButton->setToolTip(QTStr(\"Basic.AudioMixer.Layout.Horizontal\"));\n\t} else {\n\t\tstackedMixerArea->setCurrentIndex(0);\n\n\t\tQIcon layoutIcon;\n\t\tlayoutIcon.addFile(QString::fromUtf8(\":/res/images/layout-vertical.svg\"), QSize(16, 16),\n\t\t\t\t   QIcon::Mode::Normal, QIcon::State::Off);\n\t\tlayoutButton->setIcon(layoutIcon);\n\t\tlayoutButton->setToolTip(QTStr(\"Basic.AudioMixer.Layout.Vertical\"));\n\t}\n\n\t// Qt caches the size of QWidgetActions so this is the simplest way to update the text\n\t// of the checkboxes in the menu and make sure the menu size is recalculated.\n\tcreateMixerContextMenu();\n\n\tQWidget *buttonWidget = mixerToolbar->widgetForAction(layoutButton);\n\tif (buttonWidget) {\n\t\tidian::Utils::toggleClass(buttonWidget, \"icon-layout-horizontal\", vertical);\n\t\tidian::Utils::toggleClass(buttonWidget, \"icon-layout-vertical\", !vertical);\n\t}\n}\n\nvoid AudioMixer::createMixerContextMenu()\n{\n\tif (mixerMenu) {\n\t\tmixerMenu->deleteLater();\n\t}\n\n\tmixerMenu = new QMenu(this);\n\n\t// Create menu actions\n\tQAction *unhideAllAction = new QAction(QTStr(\"UnhideAll\"), mixerMenu);\n\n\tshowHiddenCheckBox = new MenuCheckBox(QTStr(\"Basic.AudioMixer.ShowHidden\"), mixerMenu);\n\tQWidgetAction *showHiddenAction = new QWidgetAction(mixerMenu);\n\tshowHiddenCheckBox->setAction(showHiddenAction);\n\tshowHiddenCheckBox->setChecked(showHidden);\n\tshowHiddenAction->setDefaultWidget(showHiddenCheckBox);\n\n\tQWidgetAction *showInactiveAction = new QWidgetAction(mixerMenu);\n\tMenuCheckBox *showInactiveCheckBox = new MenuCheckBox(QTStr(\"Basic.AudioMixer.ShowInactive\"), mixerMenu);\n\tshowInactiveCheckBox->setAction(showInactiveAction);\n\tshowInactiveCheckBox->setChecked(showInactive);\n\tshowInactiveAction->setDefaultWidget(showInactiveCheckBox);\n\n\tQWidgetAction *hiddenLastAction = new QWidgetAction(mixerMenu);\n\tconst char *hiddenLastString = mixerVertical ? \"Basic.AudioMixer.KeepHiddenRight\"\n\t\t\t\t\t\t     : \"Basic.AudioMixer.KeepHiddenBottom\";\n\tMenuCheckBox *hiddenLastCheckBox = new MenuCheckBox(QTStr(hiddenLastString), mixerMenu);\n\thiddenLastCheckBox->setAction(hiddenLastAction);\n\thiddenLastCheckBox->setChecked(keepHiddenLast);\n\thiddenLastAction->setDefaultWidget(hiddenLastCheckBox);\n\n\tQWidgetAction *inactiveLastAction = new QWidgetAction(mixerMenu);\n\tconst char *inactiveLastString = mixerVertical ? \"Basic.AudioMixer.KeepInactiveRight\"\n\t\t\t\t\t\t       : \"Basic.AudioMixer.KeepInactiveBottom\";\n\tMenuCheckBox *inactiveLastCheckBox = new MenuCheckBox(QTStr(inactiveLastString), mixerMenu);\n\tinactiveLastCheckBox->setAction(inactiveLastAction);\n\tinactiveLastCheckBox->setChecked(keepInactiveLast);\n\tinactiveLastAction->setDefaultWidget(inactiveLastCheckBox);\n\n\t// Connect menu actions\n\tconnect(unhideAllAction, &QAction::triggered, this, &AudioMixer::unhideAllAudioControls, Qt::DirectConnection);\n\n\tconnect(showHiddenCheckBox, &QCheckBox::toggled, this, &AudioMixer::toggleShowHidden, Qt::DirectConnection);\n\tconnect(hiddenLastCheckBox, &QCheckBox::toggled, this, &AudioMixer::toggleKeepHiddenLast, Qt::DirectConnection);\n\n\tconnect(showInactiveCheckBox, &QCheckBox::toggled, this, &AudioMixer::toggleShowInactive, Qt::DirectConnection);\n\tconnect(inactiveLastCheckBox, &QCheckBox::toggled, this, &AudioMixer::toggleKeepInactiveLast,\n\t\tQt::DirectConnection);\n\n\t// Build menu and show\n\tmixerMenu->addAction(unhideAllAction);\n\tmixerMenu->addSeparator();\n\tmixerMenu->addAction(showHiddenAction);\n\tmixerMenu->addAction(showInactiveAction);\n\tmixerMenu->addAction(hiddenLastAction);\n\tmixerMenu->addAction(inactiveLastAction);\n\n\toptionsButton->setMenu(mixerMenu);\n}\n\nvoid AudioMixer::showMixerContextMenu()\n{\n\tcreateMixerContextMenu();\n\tmixerMenu->popup(QCursor::pos());\n}\n\nvoid AudioMixer::addControlForUuid(QString uuid)\n{\n\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.toUtf8().constData());\n\n\tQPointer<VolumeControl> newControl = createVolumeControl(source);\n\n\tvolumeList.insert({uuid, newControl});\n\tqueueLayoutUpdate();\n}\n\nvoid AudioMixer::removeControlForUuid(QString uuid)\n{\n\tauto item = volumeList.find(uuid);\n\tif (item != volumeList.end()) {\n\t\tVolumeControl *widget = item->second;\n\t\tif (widget) {\n\t\t\tactiveLayout()->removeWidget(widget);\n\t\t\twidget->deleteLater();\n\t\t}\n\n\t\tvolumeList.erase(item);\n\t}\n\n\tpreviewSources.erase(uuid);\n\tglobalSources.erase(uuid);\n}\n\nvoid AudioMixer::onFrontendEvent(obs_frontend_event event, void *data)\n{\n\tAudioMixer *mixer = static_cast<AudioMixer *>(data);\n\tmixer->handleFrontendEvent(event);\n}\n\nvoid AudioMixer::handleFrontendEvent(obs_frontend_event event)\n{\n\tswitch (event) {\n\tcase OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED:\n\tcase OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED:\n\tcase OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED:\n\t\tupdatePreviewSources();\n\t\tupdatePreviewHandlers();\n\t\tqueueLayoutUpdate();\n\t\tbreak;\n\tcase OBS_FRONTEND_EVENT_EXIT:\n\t\tobs_frontend_remove_event_callback(AudioMixer::onFrontendEvent, this);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nvoid AudioMixer::updatePreviewHandlers()\n{\n\tpreviewSignals.clear();\n\n\tbool isStudioMode = obs_frontend_preview_program_mode_active();\n\tif (isStudioMode) {\n\t\tOBSSourceAutoRelease previewSource = obs_frontend_get_current_preview_scene();\n\t\tif (!previewSource) {\n\t\t\treturn;\n\t\t}\n\n\t\tpreviewSignals.reserve(1);\n\n\t\tpreviewSignals.emplace_back(obs_source_get_signal_handler(previewSource), \"item_visible\",\n\t\t\t\t\t    AudioMixer::obsSceneItemVisibleChange, this);\n\t}\n}\n\nvoid AudioMixer::updateShowInactive()\n{\n\tbool settingShowInactive = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerShowInactive\");\n\tif (showInactive == settingShowInactive) {\n\t\treturn;\n\t}\n\n\tshowInactive = settingShowInactive;\n\n\tqueueLayoutUpdate();\n}\n\nvoid AudioMixer::updateKeepInactiveLast()\n{\n\tbool settingKeepInactiveLast = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerKeepInactiveLast\");\n\tif (keepInactiveLast == settingKeepInactiveLast) {\n\t\treturn;\n\t}\n\n\tkeepInactiveLast = settingKeepInactiveLast;\n\n\tqueueLayoutUpdate();\n}\n\nvoid AudioMixer::updateShowHidden()\n{\n\tbool settingShowHidden = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerShowHidden\");\n\tif (showHidden == settingShowHidden) {\n\t\treturn;\n\t}\n\n\tshowHidden = settingShowHidden;\n\n\ttoggleHiddenButton->setText(QTStr(\"Basic.AudioMixer.HiddenTotal\").arg(hiddenCount));\n\ttoggleHiddenButton->setChecked(showHidden);\n\tshowHiddenCheckBox->setChecked(showHidden);\n\n\tqueueLayoutUpdate();\n}\n\nvoid AudioMixer::updateKeepHiddenLast()\n{\n\tbool settingKeepHiddenLast = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MixerKeepHiddenLast\");\n\tif (keepHiddenLast == settingKeepHiddenLast) {\n\t\treturn;\n\t}\n\n\tkeepHiddenLast = settingKeepHiddenLast;\n\n\tqueueLayoutUpdate();\n}\n\nvoid AudioMixer::updateShowToolbar()\n{\n\tbool settingShowToolbar = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowListboxToolbars\");\n\n\tif (showToolbar == settingShowToolbar) {\n\t\treturn;\n\t}\n\n\tshowToolbar = settingShowToolbar;\n\n\tshowToolbar ? mixerToolbar->show() : mixerToolbar->hide();\n}\n\nvoid AudioMixer::obsSourceActivated(void *data, calldata_t *params)\n{\n\tobs_source_t *source = static_cast<obs_source_t *>(calldata_ptr(params, \"source\"));\n\tuint32_t flags = obs_source_get_output_flags(source);\n\n\tif (flags & OBS_SOURCE_AUDIO) {\n\t\tauto uuidPointer = obs_source_get_uuid(source);\n\t\tQMetaObject::invokeMethod(static_cast<AudioMixer *>(data), \"updateControlVisibility\",\n\t\t\t\t\t  Qt::QueuedConnection, Q_ARG(QString, QString::fromUtf8(uuidPointer)));\n\t}\n}\n\nvoid AudioMixer::obsSourceDeactivated(void *data, calldata_t *params)\n{\n\tobs_source_t *source = static_cast<obs_source_t *>(calldata_ptr(params, \"source\"));\n\tuint32_t flags = obs_source_get_output_flags(source);\n\n\tif (flags & OBS_SOURCE_AUDIO) {\n\t\tauto uuidPointer = obs_source_get_uuid(source);\n\t\tQMetaObject::invokeMethod(static_cast<AudioMixer *>(data), \"updateControlVisibility\",\n\t\t\t\t\t  Qt::QueuedConnection, Q_ARG(QString, QString::fromUtf8(uuidPointer)));\n\t}\n}\n\nvoid AudioMixer::obsSourceAudioActivated(void *data, calldata_t *params)\n{\n\tobs_source_t *source = static_cast<obs_source_t *>(calldata_ptr(params, \"source\"));\n\tuint32_t flags = obs_source_get_output_flags(source);\n\tbool audioActive = obs_source_audio_active(source);\n\n\tif (flags & OBS_SOURCE_AUDIO && audioActive) {\n\t\tauto uuidPointer = obs_source_get_uuid(source);\n\t\tQMetaObject::invokeMethod(static_cast<AudioMixer *>(data), \"addSource\", Qt::QueuedConnection,\n\t\t\t\t\t  Q_ARG(QString, QString::fromUtf8(uuidPointer)));\n\t}\n}\n\nvoid AudioMixer::obsSourceAudioDeactivated(void *data, calldata_t *params)\n{\n\tobs_source_t *source = static_cast<obs_source_t *>(calldata_ptr(params, \"source\"));\n\tuint32_t flags = obs_source_get_output_flags(source);\n\n\tif (flags & OBS_SOURCE_AUDIO) {\n\t\tauto uuidPointer = obs_source_get_uuid(source);\n\t\tQMetaObject::invokeMethod(static_cast<AudioMixer *>(data), \"removeSource\", Qt::QueuedConnection,\n\t\t\t\t\t  Q_ARG(QString, QString::fromUtf8(uuidPointer)));\n\t}\n}\n\nvoid AudioMixer::obsSourceCreate(void *data, calldata_t *params)\n{\n\tobs_source_t *source = static_cast<obs_source_t *>(calldata_ptr(params, \"source\"));\n\tuint32_t flags = obs_source_get_output_flags(source);\n\tbool audioActive = obs_source_audio_active(source);\n\n\tif (flags & OBS_SOURCE_AUDIO && audioActive) {\n\t\tauto uuidPointer = obs_source_get_uuid(source);\n\t\tQMetaObject::invokeMethod(static_cast<AudioMixer *>(data), \"addSource\", Qt::QueuedConnection,\n\t\t\t\t\t  Q_ARG(QString, QString::fromUtf8(uuidPointer)));\n\t}\n}\n\nvoid AudioMixer::obsSourceRemove(void *data, calldata_t *params)\n{\n\tobs_source_t *source = static_cast<obs_source_t *>(calldata_ptr(params, \"source\"));\n\tuint32_t flags = obs_source_get_output_flags(source);\n\n\tif (flags & OBS_SOURCE_AUDIO) {\n\t\tauto uuidPointer = obs_source_get_uuid(source);\n\t\tQMetaObject::invokeMethod(static_cast<AudioMixer *>(data), \"removeSource\", Qt::QueuedConnection,\n\t\t\t\t\t  Q_ARG(QString, QString::fromUtf8(uuidPointer)));\n\t}\n}\n\nvoid AudioMixer::obsSourceRename(void *data, calldata_t *)\n{\n\tQMetaObject::invokeMethod(static_cast<AudioMixer *>(data), \"queueLayoutUpdate\", Qt::QueuedConnection);\n}\n\nvoid AudioMixer::obsSceneItemVisibleChange(void *data, calldata_t *params)\n{\n\tobs_sceneitem_t *sceneItem = static_cast<obs_sceneitem_t *>(calldata_ptr(params, \"item\"));\n\tif (!sceneItem) {\n\t\treturn;\n\t}\n\n\tobs_source_t *source = obs_sceneitem_get_source(sceneItem);\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tuint32_t flags = obs_source_get_output_flags(source);\n\n\tif (flags & OBS_SOURCE_AUDIO) {\n\t\tQMetaObject::invokeMethod(static_cast<AudioMixer *>(data), \"updatePreviewSources\",\n\t\t\t\t\t  Qt::QueuedConnection);\n\n\t\tauto uuidPointer = obs_source_get_uuid(source);\n\t\tQMetaObject::invokeMethod(static_cast<AudioMixer *>(data), \"updateControlVisibility\",\n\t\t\t\t\t  Qt::QueuedConnection, Q_ARG(QString, QString::fromUtf8(uuidPointer)));\n\t}\n}\n"
  },
  {
    "path": "frontend/widgets/AudioMixer.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Taylor Giampaolo <warchamp7@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"OBSBasic.hpp\"\n\n#include <components/MenuCheckBox.hpp>\n#include <components/VolumeControl.hpp>\n\n#include <QObject>\n#include <QPointer>\n#include <QTimer>\n\n#include <unordered_set>\n\nclass QHBoxLayout;\nclass QMenu;\nclass QPushButton;\nclass QScrollArea;\nclass QStackedWidget;\nclass QToolBar;\nclass QVBoxLayout;\n\nclass AudioMixer : public QFrame {\n\tQ_OBJECT\n\n\tstruct RankedVolume {\n\t\tVolumeControl *control;\n\t\tint sortingWeight;\n\t};\n\npublic:\n\texplicit AudioMixer(QWidget *parent = 0);\n\t~AudioMixer();\n\n\tvoid refreshVolumeColors();\n\n\tvoid setMixerLayoutVertical(bool vertical);\n\tvoid createMixerContextMenu();\n\tvoid showMixerContextMenu();\n\nprivate:\n\tstd::vector<OBSSignal> signalHandlers;\n\tstd::vector<OBSSignal> previewSignals;\n\tstatic void onFrontendEvent(enum obs_frontend_event event, void *data);\n\tvoid handleFrontendEvent(enum obs_frontend_event event);\n\tvoid updatePreviewHandlers();\n\n\tstd::unordered_map<QString, QPointer<VolumeControl>> volumeList;\n\tvoid addControlForUuid(QString uuid);\n\tvoid removeControlForUuid(QString uuid);\n\n\tstd::unordered_set<QString> globalSources;\n\tstd::unordered_set<QString> previewSources;\n\tbool mixerVertical{false};\n\n\tint hiddenCount{0};\n\tbool showHidden{false};\n\tbool showInactive{false};\n\n\tbool keepInactiveLast{false};\n\tbool keepHiddenLast{false};\n\n\tbool showToolbar{true};\n\n\tQFrame *mixerFrame{nullptr};\n\tQVBoxLayout *mainLayout{nullptr};\n\tQVBoxLayout *mixerLayout{nullptr};\n\n\tQStackedWidget *stackedMixerArea{nullptr};\n\tQToolBar *mixerToolbar{nullptr};\n\tQAction *layoutButton{nullptr};\n\tQAction *advAudio{nullptr};\n\tQPushButton *optionsButton{nullptr};\n\tQPushButton *toggleHiddenButton{nullptr};\n\n\tQPointer<QMenu> mixerMenu;\n\tQPointer<MenuCheckBox> showHiddenCheckBox;\n\n\tQScrollArea *hMixerScrollArea{nullptr};\n\tQWidget *hVolumeWidgets{nullptr};\n\tQVBoxLayout *hVolumeControlLayout{nullptr};\n\n\tQScrollArea *vMixerScrollArea{nullptr};\n\tQWidget *vVolumeWidgets{nullptr};\n\tQHBoxLayout *vVolumeControlLayout{nullptr};\n\n\tQBoxLayout *activeLayout() const;\n\n\tvoid reloadVolumeControls();\n\tbool getMixerVisibilityForControl(VolumeControl *widget);\n\n\tvoid clearPreviewSources();\n\tbool isSourcePreviewed(obs_source_t *source);\n\tbool isSourceGlobal(obs_source_t *source);\n\tvoid clearVolumeControls();\n\tvoid updateShowInactive();\n\tvoid updateKeepInactiveLast();\n\tvoid updateShowHidden();\n\tvoid updateKeepHiddenLast();\n\tvoid updateShowToolbar();\n\n\tQTimer updateTimer;\n\tvoid updateVolumeLayouts();\n\n\t// OBS Callbacks\n\tstatic void obsSourceActivated(void *data, calldata_t *params);\n\tstatic void obsSourceDeactivated(void *data, calldata_t *params);\n\tstatic void obsSourceAudioActivated(void *data, calldata_t *params);\n\tstatic void obsSourceAudioDeactivated(void *data, calldata_t *params);\n\tstatic void obsSourceCreate(void *data, calldata_t *params);\n\tstatic void obsSourceRemove(void *data, calldata_t *params);\n\tstatic void obsSourceRename(void *data, calldata_t *params);\n\tstatic void obsSceneItemVisibleChange(void *data, calldata_t *params);\n\nprivate slots:\n\tvoid addSource(QString uuid);\n\tvoid removeSource(QString uuid);\n\tvoid updatePreviewSources();\n\tvoid updateGlobalSources();\n\tvoid unhideAllAudioControls();\n\tvoid queueLayoutUpdate();\n\n\tVolumeControl *createVolumeControl(obs_source_t *source);\n\tvoid updateControlVisibility(QString uuid);\n\n\tvoid updateLayout();\n\tvoid toggleShowInactive(bool checked);\n\tvoid toggleKeepInactiveLast(bool checked);\n\tvoid toggleShowHidden(bool checked);\n\tvoid toggleKeepHiddenLast(bool checked);\n\n\tvoid mixerContextMenuRequested();\n\nsignals:\n\tvoid advAudioPropertiesClicked();\n};\n"
  },
  {
    "path": "frontend/widgets/ColorSelect.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"ColorSelect.hpp\"\n#include \"moc_ColorSelect.cpp\"\n\nColorSelect::ColorSelect(QWidget *parent) : QWidget(parent), ui(new Ui::ColorSelect)\n{\n\tui->setupUi(this);\n}\n"
  },
  {
    "path": "frontend/widgets/ColorSelect.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_ColorSelect.h\"\n\n#include <QWidget>\n\nclass ColorSelect : public QWidget {\n\tQ_OBJECT\n\npublic:\n\texplicit ColorSelect(QWidget *parent = 0);\n\nprivate:\n\tstd::unique_ptr<Ui::ColorSelect> ui;\n};\n"
  },
  {
    "path": "frontend/widgets/OBSBasic.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n#include \"ui-config.h\"\n\n#include \"ColorSelect.hpp\"\n#include \"OBSBasicControls.hpp\"\n#include \"OBSBasicStats.hpp\"\n#include \"plugin-manager/PluginManager.hpp\"\n\n#include <obs-module.h>\n\n#ifdef YOUTUBE_ENABLED\n#include <docks/YouTubeAppDock.hpp>\n#endif\n#include <dialogs/NameDialog.hpp>\n#include <dialogs/OBSAbout.hpp>\n#include <dialogs/OBSBasicAdvAudio.hpp>\n#include <dialogs/OBSBasicFilters.hpp>\n#include <dialogs/OBSBasicInteraction.hpp>\n#include <dialogs/OBSBasicProperties.hpp>\n#include <dialogs/OBSBasicTransform.hpp>\n#include <models/SceneCollection.hpp>\n#include <settings/OBSBasicSettings.hpp>\n#include <utility/QuickTransition.hpp>\n#include <utility/SceneRenameDelegate.hpp>\n#if defined(_WIN32) || defined(WHATSNEW_ENABLED)\n#include <utility/WhatsNewInfoThread.hpp>\n#endif\n#include <widgets/AudioMixer.hpp>\n#include <widgets/OBSProjector.hpp>\n\n#include <OBSStudioAPI.hpp>\n#ifdef BROWSER_AVAILABLE\n#include <browser-panel.hpp>\n#endif\n#ifdef ENABLE_WAYLAND\n#include <obs-nix-platform.h>\n#endif\n#include <qt-wrappers.hpp>\n\n#include <QActionGroup>\n#include <QThread>\n#include <QWidgetAction>\n\n#ifdef _WIN32\n#include <sstream>\n#endif\n#include <string>\n#include <unordered_set>\n\n#ifdef _WIN32\n#define WIN32_LEAN_AND_MEAN\n#include \"Windows.h\"\n#endif\n\n#include \"moc_OBSBasic.cpp\"\n\nusing namespace std;\n\nextern bool portable_mode;\nextern bool disable_3p_plugins;\nextern bool opt_studio_mode;\nextern bool opt_always_on_top;\nextern bool opt_minimize_tray;\nextern std::string opt_starting_profile;\nextern std::string opt_starting_collection;\n\nextern bool safe_mode;\nextern bool opt_start_recording;\nextern bool opt_start_replaybuffer;\nextern bool opt_start_virtualcam;\n\nextern volatile long insideEventLoop;\nextern bool restart;\n\nextern bool EncoderAvailable(const char *encoder);\n\nextern void RegisterTwitchAuth();\nextern void RegisterRestreamAuth();\n#ifdef YOUTUBE_ENABLED\nextern void RegisterYoutubeAuth();\n#endif\n\nstruct QCef;\n\nextern QCef *cef;\nextern bool cef_js_avail;\n\nextern void DestroyPanelCookieManager();\nextern void CheckExistingCookieId();\n\nstatic void AddExtraModulePaths()\n{\n\tstring plugins_path, plugins_data_path;\n\tchar *s;\n\n\ts = getenv(\"OBS_PLUGINS_PATH\");\n\tif (s)\n\t\tplugins_path = s;\n\n\ts = getenv(\"OBS_PLUGINS_DATA_PATH\");\n\tif (s)\n\t\tplugins_data_path = s;\n\n\tif (!plugins_path.empty() && !plugins_data_path.empty()) {\n#if defined(__APPLE__)\n\t\tplugins_path += \"/%module%.plugin/Contents/MacOS\";\n\t\tplugins_data_path += \"/%module%.plugin/Contents/Resources\";\n\t\tobs_add_module_path(plugins_path.c_str(), plugins_data_path.c_str());\n#else\n\t\tstring data_path_with_module_suffix;\n\t\tdata_path_with_module_suffix += plugins_data_path;\n\t\tdata_path_with_module_suffix += \"/%module%\";\n\t\tobs_add_module_path(plugins_path.c_str(), data_path_with_module_suffix.c_str());\n#endif\n\t}\n\n\tif (portable_mode)\n\t\treturn;\n\n\tchar base_module_dir[512];\n#if defined(_WIN32)\n\tint ret = GetProgramDataPath(base_module_dir, sizeof(base_module_dir), \"obs-studio/plugins/%module%\");\n#elif defined(__APPLE__)\n\tint ret = GetAppConfigPath(base_module_dir, sizeof(base_module_dir), \"obs-studio/plugins/%module%.plugin\");\n#else\n\tint ret = GetAppConfigPath(base_module_dir, sizeof(base_module_dir), \"obs-studio/plugins/%module%\");\n#endif\n\n\tif (ret <= 0)\n\t\treturn;\n\n\tstring path = base_module_dir;\n#if defined(__APPLE__)\n\t/* User Application Support Search Path */\n\tobs_add_module_path((path + \"/Contents/MacOS\").c_str(), (path + \"/Contents/Resources\").c_str());\n\n#ifndef __aarch64__\n\t/* Legacy System Library Search Path */\n\tchar system_legacy_module_dir[PATH_MAX];\n\tGetProgramDataPath(system_legacy_module_dir, sizeof(system_legacy_module_dir), \"obs-studio/plugins/%module%\");\n\tstd::string path_system_legacy = system_legacy_module_dir;\n\tobs_add_module_path((path_system_legacy + \"/bin\").c_str(), (path_system_legacy + \"/data\").c_str());\n\n\t/* Legacy User Application Support Search Path */\n\tchar user_legacy_module_dir[PATH_MAX];\n\tGetAppConfigPath(user_legacy_module_dir, sizeof(user_legacy_module_dir), \"obs-studio/plugins/%module%\");\n\tstd::string path_user_legacy = user_legacy_module_dir;\n\tobs_add_module_path((path_user_legacy + \"/bin\").c_str(), (path_user_legacy + \"/data\").c_str());\n#endif\n#else\n#if ARCH_BITS == 64\n\tobs_add_module_path((path + \"/bin/64bit\").c_str(), (path + \"/data\").c_str());\n#else\n\tobs_add_module_path((path + \"/bin/32bit\").c_str(), (path + \"/data\").c_str());\n#endif\n#endif\n}\n\n/* First-party modules considered to be potentially unsafe to load in Safe Mode\n * due to them allowing external code (e.g. scripts) to modify OBS's state. */\nstatic const unordered_set<string> unsafe_modules = {\n\t\"frontend-tools\", // Scripting\n\t\"obs-websocket\",  // Allows outside modifications\n};\n\nstatic void SetSafeModuleNames()\n{\n#ifndef SAFE_MODULES\n\treturn;\n#else\n\tstring module;\n\tstringstream modules_(SAFE_MODULES);\n\n\twhile (getline(modules_, module, '|')) {\n\t\t/* When only disallowing third-party plugins, still add\n\t\t * \"unsafe\" bundled modules to the safe list. */\n\t\tif (disable_3p_plugins || !unsafe_modules.count(module))\n\t\t\tobs_add_safe_module(module.c_str());\n\t}\n#endif\n}\n\nstatic void SetCoreModuleNames()\n{\n#ifndef SAFE_MODULES\n\tthrow \"SAFE_MODULES not defined\";\n#else\n\tstd::string safeModules = SAFE_MODULES;\n\tif (safeModules.empty()) {\n\t\tthrow \"SAFE_MODULES is empty\";\n\t}\n\tstring module;\n\tstringstream modules_(SAFE_MODULES);\n\n\twhile (getline(modules_, module, '|')) {\n\t\tobs_add_core_module(module.c_str());\n\t}\n#endif\n}\n\nextern void setupDockAction(QDockWidget *dock);\n\nOBSBasic::OBSBasic(QWidget *parent) : OBSMainWindow(parent), undo_s(ui), ui(new Ui::OBSBasic)\n{\n\tcollections = {};\n\n\tsetAttribute(Qt::WA_NativeWindow);\n\n#ifdef TWITCH_ENABLED\n\tRegisterTwitchAuth();\n#endif\n#ifdef RESTREAM_ENABLED\n\tRegisterRestreamAuth();\n#endif\n#ifdef YOUTUBE_ENABLED\n\tRegisterYoutubeAuth();\n#endif\n\n\tsetAcceptDrops(true);\n\n\tsetContextMenuPolicy(Qt::CustomContextMenu);\n\n\tQEvent::registerEventType(QEvent::User + QEvent::Close);\n\n\tapi = InitializeAPIInterface(this);\n\n\tui->setupUi(this);\n\tui->previewDisabledWidget->setVisible(false);\n\n\t/* Set up streaming connections */\n\tconnect(\n\t\tthis, &OBSBasic::StreamingStarting, this, [this] { this->streamingStarting = true; },\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tthis, &OBSBasic::StreamingStarted, this, [this] { this->streamingStarting = false; },\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tthis, &OBSBasic::StreamingStopped, this, [this] { this->streamingStarting = false; },\n\t\tQt::DirectConnection);\n\n\t/* Set up recording connections */\n\tconnect(\n\t\tthis, &OBSBasic::RecordingStarted, this,\n\t\t[this]() {\n\t\t\tthis->recordingStarted = true;\n\t\t\tthis->recordingPaused = false;\n\t\t},\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tthis, &OBSBasic::RecordingPaused, this, [this]() { this->recordingPaused = true; },\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tthis, &OBSBasic::RecordingUnpaused, this, [this]() { this->recordingPaused = false; },\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tthis, &OBSBasic::RecordingStopped, this,\n\t\t[this]() {\n\t\t\tthis->recordingStarted = false;\n\t\t\tthis->recordingPaused = false;\n\t\t},\n\t\tQt::DirectConnection);\n\n\t/* Add controls dock */\n\tOBSBasicControls *controls = new OBSBasicControls(this);\n\tcontrolsDock = new OBSDock(this);\n\tcontrolsDock->setObjectName(QString::fromUtf8(\"controlsDock\"));\n\tcontrolsDock->setWindowTitle(QTStr(\"Basic.Main.Controls\"));\n\t/* Parenting is done there so controls will be deleted alongside controlsDock */\n\tcontrolsDock->setWidget(controls);\n\n\tconnect(controls, &OBSBasicControls::StreamButtonClicked, this, &OBSBasic::StreamActionTriggered);\n\n\tconnect(controls, &OBSBasicControls::StartStreamMenuActionClicked, this, &OBSBasic::StartStreaming);\n\tconnect(controls, &OBSBasicControls::StopStreamMenuActionClicked, this, &OBSBasic::StopStreaming);\n\tconnect(controls, &OBSBasicControls::ForceStopStreamMenuActionClicked, this, &OBSBasic::ForceStopStreaming);\n\n\tconnect(controls, &OBSBasicControls::BroadcastButtonClicked, this, &OBSBasic::BroadcastButtonClicked);\n\n\tconnect(controls, &OBSBasicControls::RecordButtonClicked, this, &OBSBasic::RecordActionTriggered);\n\tconnect(controls, &OBSBasicControls::PauseRecordButtonClicked, this, &OBSBasic::RecordPauseToggled);\n\n\tconnect(controls, &OBSBasicControls::ReplayBufferButtonClicked, this, &OBSBasic::ReplayBufferActionTriggered);\n\tconnect(controls, &OBSBasicControls::SaveReplayBufferButtonClicked, this, &OBSBasic::ReplayBufferSave);\n\n\tconnect(controls, &OBSBasicControls::VirtualCamButtonClicked, this, &OBSBasic::VirtualCamActionTriggered);\n\tconnect(controls, &OBSBasicControls::VirtualCamConfigButtonClicked, this, &OBSBasic::OpenVirtualCamConfig);\n\n\tconnect(controls, &OBSBasicControls::StudioModeButtonClicked, this, &OBSBasic::TogglePreviewProgramMode);\n\n\tconnect(controls, &OBSBasicControls::SettingsButtonClicked, this, &OBSBasic::on_action_Settings_triggered);\n\n\t/* Set up transitions combobox connections */\n\tconnect(this, &OBSBasic::TransitionAdded, this, [this](const QString &name, const QString &uuid) {\n\t\tQSignalBlocker sb(ui->transitions);\n\t\tui->transitions->addItem(name, uuid);\n\t});\n\tconnect(this, &OBSBasic::TransitionRenamed, this, [this](const QString &uuid, const QString &newName) {\n\t\tQSignalBlocker sb(ui->transitions);\n\t\tui->transitions->setItemText(ui->transitions->findData(uuid), newName);\n\t});\n\tconnect(this, &OBSBasic::TransitionRemoved, this, [this](const QString &uuid) {\n\t\tQSignalBlocker sb(ui->transitions);\n\t\tui->transitions->removeItem(ui->transitions->findData(uuid));\n\t});\n\tconnect(this, &OBSBasic::TransitionsCleared, this, [this]() {\n\t\tQSignalBlocker sb(ui->transitions);\n\t\tui->transitions->clear();\n\t});\n\n\tconnect(this, &OBSBasic::CurrentTransitionChanged, this, [this](const QString &uuid) {\n\t\tQSignalBlocker sb(ui->transitions);\n\t\tui->transitions->setCurrentIndex(ui->transitions->findData(uuid));\n\t});\n\n\tconnect(ui->transitions, &QComboBox::currentIndexChanged, this,\n\t\t[this]() { SetCurrentTransition(ui->transitions->currentData().toString()); });\n\n\tconnect(this, &OBSBasic::TransitionDurationChanged, this, [this](int duration) {\n\t\tQSignalBlocker sb(ui->transitionDuration);\n\t\tui->transitionDuration->setValue(duration);\n\t});\n\n\tconnect(ui->transitionDuration, &QSpinBox::valueChanged, this,\n\t\t[this](int value) { SetTransitionDuration(value); });\n\n\t/* Main window default layout */\n\tsetDockCornersVertical(true);\n\n\t/* Scenes and Sources dock on left\n\t * This specific arrangement can't be set up in Qt Designer */\n\taddDockWidget(Qt::LeftDockWidgetArea, ui->scenesDock);\n\tsplitDockWidget(ui->scenesDock, ui->sourcesDock, Qt::Vertical);\n\tint sideDockWidth = std::min(width() * 30 / 100, 320);\n\tresizeDocks({ui->scenesDock, ui->sourcesDock}, {sideDockWidth, sideDockWidth}, Qt::Horizontal);\n\taddDockWidget(Qt::BottomDockWidgetArea, controlsDock);\n\n\tstartingDockLayout = saveState();\n\n\tstatsDock = new OBSDock();\n\tstatsDock->setObjectName(QStringLiteral(\"statsDock\"));\n\tstatsDock->setFeatures(QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable |\n\t\t\t       QDockWidget::DockWidgetFloatable);\n\tstatsDock->setWindowTitle(QTStr(\"Basic.Stats\"));\n\taddDockWidget(Qt::BottomDockWidgetArea, statsDock);\n\tstatsDock->setVisible(false);\n\tstatsDock->setFloating(true);\n\tstatsDock->resize(700, 200);\n\n\tcopyActionsDynamicProperties();\n\n\tqRegisterMetaType<int64_t>(\"int64_t\");\n\tqRegisterMetaType<uint32_t>(\"uint32_t\");\n\tqRegisterMetaType<OBSScene>(\"OBSScene\");\n\tqRegisterMetaType<OBSSceneItem>(\"OBSSceneItem\");\n\tqRegisterMetaType<OBSSource>(\"OBSSource\");\n\tqRegisterMetaType<obs_hotkey_id>(\"obs_hotkey_id\");\n\tqRegisterMetaType<SavedProjectorInfo *>(\"SavedProjectorInfo *\");\n\n\tui->scenes->setAttribute(Qt::WA_MacShowFocusRect, false);\n\tui->sources->setAttribute(Qt::WA_MacShowFocusRect, false);\n\n\tbool sceneGrid = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"gridMode\");\n\tui->scenes->SetGridMode(sceneGrid);\n\n\tif (sceneGrid)\n\t\tui->actionSceneGridMode->setChecked(true);\n\telse\n\t\tui->actionSceneListMode->setChecked(true);\n\n\tui->scenes->setItemDelegate(new SceneRenameDelegate(ui->scenes));\n\n\tauto displayResize = [this]() {\n\t\tstruct obs_video_info ovi;\n\n\t\tif (obs_get_video_info(&ovi))\n\t\t\tResizePreview(ovi.base_width, ovi.base_height);\n\n\t\tUpdateContextBarVisibility();\n\t\tUpdatePreviewControls();\n\t\tdpi = devicePixelRatioF();\n\t};\n\tdpi = devicePixelRatioF();\n\n\tconnect(windowHandle(), &QWindow::screenChanged, this, displayResize);\n\tconnect(ui->preview, &OBSQTDisplay::DisplayResized, this, displayResize);\n\n\t/* TODO: Move these into window-basic-preview */\n\t/* Preview Scaling label */\n\tconnect(ui->preview, &OBSBasicPreview::scalingChanged, ui->previewScalePercent,\n\t\t&OBSPreviewScalingLabel::PreviewScaleChanged);\n\n\t/* Preview Scaling dropdown */\n\tconnect(ui->preview, &OBSBasicPreview::scalingChanged, ui->previewScalingMode,\n\t\t&OBSPreviewScalingComboBox::PreviewScaleChanged);\n\n\tconnect(ui->preview, &OBSBasicPreview::fixedScalingChanged, ui->previewScalingMode,\n\t\t&OBSPreviewScalingComboBox::PreviewFixedScalingChanged);\n\n\tconnect(ui->previewScalingMode, &OBSPreviewScalingComboBox::currentIndexChanged, this,\n\t\t&OBSBasic::PreviewScalingModeChanged);\n\n\t/* Preview Controls */\n\tconnect(ui->previewXScrollBar, &QScrollBar::sliderMoved, ui->preview, &OBSBasicPreview::xScrollBarChanged);\n\tconnect(ui->previewYScrollBar, &QScrollBar::valueChanged, ui->preview, &OBSBasicPreview::yScrollBarChanged);\n\n\tconnect(ui->previewZoomInButton, &QPushButton::clicked, ui->preview, &OBSBasicPreview::increaseScalingLevel);\n\tconnect(ui->previewZoomOutButton, &QPushButton::clicked, ui->preview, &OBSBasicPreview::decreaseScalingLevel);\n\n\t/* Preview Actions */\n\tconnect(ui->actionScaleWindow, &QAction::triggered, this, &OBSBasic::setPreviewScalingWindow);\n\tconnect(ui->actionScaleCanvas, &QAction::triggered, this, &OBSBasic::setPreviewScalingCanvas);\n\tconnect(ui->actionScaleOutput, &QAction::triggered, this, &OBSBasic::setPreviewScalingOutput);\n\n\tconnect(ui->actionPreviewZoomIn, &QAction::triggered, ui->preview, &OBSBasicPreview::increaseScalingLevel);\n\tconnect(ui->actionPreviewZoomOut, &QAction::triggered, ui->preview, &OBSBasicPreview::decreaseScalingLevel);\n\tconnect(ui->actionPreviewResetZoom, &QAction::triggered, ui->preview, &OBSBasicPreview::resetScalingLevel);\n\n\tconnect(this, &OBSBasic::CanvasResized, ui->previewScalingMode, &OBSPreviewScalingComboBox::CanvasResized);\n\tconnect(this, &OBSBasic::OutputResized, ui->previewScalingMode, &OBSPreviewScalingComboBox::OutputResized);\n\n\tdelete shortcutFilter;\n\tshortcutFilter = CreateShortcutFilter();\n\tinstallEventFilter(shortcutFilter);\n\n\tstringstream name;\n\tname << \"OBS \" << App()->GetVersionString();\n\tblog(LOG_INFO, \"%s\", name.str().c_str());\n\tblog(LOG_INFO, \"---------------------------------\");\n\n\tUpdateTitleBar();\n\n\tconnect(ui->scenes->itemDelegate(), &QAbstractItemDelegate::closeEditor, this, &OBSBasic::SceneNameEdited);\n\n\tcpuUsageInfo = os_cpu_usage_info_start();\n\tcpuUsageTimer = new QTimer(this);\n\tconnect(cpuUsageTimer.data(), &QTimer::timeout, ui->statusbar, &OBSBasicStatusBar::UpdateCPUUsage);\n\tcpuUsageTimer->start(3000);\n\n\tdiskFullTimer = new QTimer(this);\n\tconnect(diskFullTimer, &QTimer::timeout, this, &OBSBasic::CheckDiskSpaceRemaining);\n\n\trenameScene = new QAction(QTStr(\"Rename\"), ui->scenesDock);\n\trenameScene->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n\tconnect(renameScene, &QAction::triggered, this, &OBSBasic::EditSceneName);\n\tui->scenesDock->addAction(renameScene);\n\n\trenameSource = new QAction(QTStr(\"Rename\"), ui->sourcesDock);\n\trenameSource->setShortcutContext(Qt::WidgetWithChildrenShortcut);\n\tconnect(renameSource, &QAction::triggered, this, &OBSBasic::EditSceneItemName);\n\tui->sourcesDock->addAction(renameSource);\n\n#ifdef __APPLE__\n\trenameScene->setShortcut({Qt::Key_Return});\n\trenameSource->setShortcut({Qt::Key_Return});\n\n\tui->actionRemoveSource->setShortcuts({Qt::Key_Backspace});\n\tui->actionRemoveScene->setShortcuts({Qt::Key_Backspace});\n\n\tui->actionCheckForUpdates->setMenuRole(QAction::AboutQtRole);\n\tui->action_Settings->setMenuRole(QAction::PreferencesRole);\n\tui->actionShowMacPermissions->setMenuRole(QAction::ApplicationSpecificRole);\n\tdelete ui->actionE_xit;\n#else\n\trenameScene->setShortcut({Qt::Key_F2});\n\trenameSource->setShortcut({Qt::Key_F2});\n#endif\n\n#ifdef __linux__\n\tui->actionE_xit->setShortcut(Qt::CTRL | Qt::Key_Q);\n#endif\n\n#ifndef ENABLE_IDIAN_PLAYGROUND\n\tui->idianPlayground->setVisible(false);\n#endif\n\n\tauto addNudge = [this](const QKeySequence &seq, MoveDir direction, int distance) {\n\t\tQAction *nudge = new QAction(ui->preview);\n\t\tnudge->setShortcut(seq);\n\t\tnudge->setShortcutContext(Qt::WidgetShortcut);\n\t\tui->preview->addAction(nudge);\n\t\tconnect(nudge, &QAction::triggered, this,\n\t\t\t[this, distance, direction]() { Nudge(distance, direction); });\n\t};\n\n\taddNudge(Qt::Key_Up, MoveDir::Up, 1);\n\taddNudge(Qt::Key_Down, MoveDir::Down, 1);\n\taddNudge(Qt::Key_Left, MoveDir::Left, 1);\n\taddNudge(Qt::Key_Right, MoveDir::Right, 1);\n\taddNudge(Qt::SHIFT | Qt::Key_Up, MoveDir::Up, 10);\n\taddNudge(Qt::SHIFT | Qt::Key_Down, MoveDir::Down, 10);\n\taddNudge(Qt::SHIFT | Qt::Key_Left, MoveDir::Left, 10);\n\taddNudge(Qt::SHIFT | Qt::Key_Right, MoveDir::Right, 10);\n\n\t/* Setup dock toggle action\n\t * And hide all docks before restoring parent geometry */\n#define SETUP_DOCK(dock)                                    \\\n\tsetupDockAction(dock);                              \\\n\tui->menuDocks->addAction(dock->toggleViewAction()); \\\n\tdock->setVisible(false);\n\n\tSETUP_DOCK(ui->scenesDock);\n\tSETUP_DOCK(ui->sourcesDock);\n\tSETUP_DOCK(ui->mixerDock);\n\tSETUP_DOCK(ui->transitionsDock);\n\tSETUP_DOCK(controlsDock);\n\tSETUP_DOCK(statsDock);\n#undef SETUP_DOCK\n\n\t// Register shortcuts for Undo/Redo\n\tui->actionMainUndo->setShortcut(Qt::CTRL | Qt::Key_Z);\n\tQList<QKeySequence> shrt;\n\tshrt << QKeySequence((Qt::CTRL | Qt::SHIFT) | Qt::Key_Z) << QKeySequence(Qt::CTRL | Qt::Key_Y);\n\tui->actionMainRedo->setShortcuts(shrt);\n\n\tui->actionMainUndo->setShortcutContext(Qt::ApplicationShortcut);\n\tui->actionMainRedo->setShortcutContext(Qt::ApplicationShortcut);\n\n\tQPoint curPos;\n\n\t//restore parent window geometry\n\tconst char *geometry = config_get_string(App()->GetUserConfig(), \"BasicWindow\", \"geometry\");\n\tif (geometry != NULL) {\n\t\tQByteArray byteArray = QByteArray::fromBase64(QByteArray(geometry));\n\t\trestoreGeometry(byteArray);\n\n\t\tQRect windowGeometry = normalGeometry();\n\t\tif (!WindowPositionValid(windowGeometry)) {\n\t\t\tQRect rect = QGuiApplication::primaryScreen()->geometry();\n\t\t\tsetGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, size(), rect));\n\t\t}\n\n\t\tcurPos = pos();\n\t} else {\n\t\tQRect desktopRect = QGuiApplication::primaryScreen()->geometry();\n\t\tQSize adjSize = desktopRect.size() / 2 - size() / 2;\n\t\tcurPos = QPoint(adjSize.width(), adjSize.height());\n\t}\n\n\tQPoint curSize(width(), height());\n\n\tQPoint statsDockSize(statsDock->width(), statsDock->height());\n\tQPoint statsDockPos = curSize / 2 - statsDockSize / 2;\n\tQPoint newPos = curPos + statsDockPos;\n\tstatsDock->move(newPos);\n\n\tui->actionReleaseNotes->setVisible(true);\n\n\tui->previewDisabledWidget->setContextMenuPolicy(Qt::CustomContextMenu);\n\tconnect(ui->enablePreviewButton, &QPushButton::clicked, this, &OBSBasic::TogglePreview);\n\n\tconnect(ui->scenes, &SceneTree::scenesReordered, ui->scenes,\n\t\t[]() { OBSProjector::UpdateMultiviewProjectors(); });\n\n\tconnect(App(), &OBSApp::StyleChanged, this, [this]() { OnEvent(OBS_FRONTEND_EVENT_THEME_CHANGED); });\n#ifndef __APPLE__\n\tconnect(App(), &OBSApp::aboutToQuit, this, &OBSBasic::closeWindow);\n#endif\n\n\tQActionGroup *actionGroup = new QActionGroup(this);\n\tactionGroup->addAction(ui->actionSceneListMode);\n\tactionGroup->addAction(ui->actionSceneGridMode);\n\n\tUpdatePreviewSafeAreas();\n\tUpdatePreviewSpacingHelpers();\n\tUpdatePreviewOverflowSettings();\n}\n\nstatic const double scaled_vals[] = {1.0, 1.25, (1.0 / 0.75), 1.5, (1.0 / 0.6), 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 0.0};\n\n#ifdef __APPLE__ // macOS\n#define DEFAULT_CONTAINER \"hybrid_mov\"\n#else // Windows/Linux\n#define DEFAULT_CONTAINER \"hybrid_mp4\"\n#endif\n\nbool OBSBasic::InitBasicConfigDefaults()\n{\n\tQList<QScreen *> screens = QGuiApplication::screens();\n\n\tif (!screens.size()) {\n\t\tOBSErrorBox(NULL, \"There appears to be no monitors.  Er, this \"\n\t\t\t\t  \"technically shouldn't be possible.\");\n\t\treturn false;\n\t}\n\n\tQScreen *primaryScreen = QGuiApplication::primaryScreen();\n\n\tuint32_t cx = primaryScreen->size().width();\n\tuint32_t cy = primaryScreen->size().height();\n\n\tcx *= devicePixelRatioF();\n\tcy *= devicePixelRatioF();\n\n\tbool oldResolutionDefaults = config_get_bool(App()->GetUserConfig(), \"General\", \"Pre19Defaults\");\n\n\t/* use 1920x1080 for new default base res if main monitor is above\n\t * 1920x1080, but don't apply for people from older builds -- only to\n\t * new users */\n\tif (!oldResolutionDefaults && (cx * cy) > (1920 * 1080)) {\n\t\tcx = 1920;\n\t\tcy = 1080;\n\t}\n\n\tbool changed = false;\n\n\t/* ----------------------------------------------------- */\n\t/* move over old FFmpeg track settings                   */\n\tif (config_has_user_value(activeConfiguration, \"AdvOut\", \"FFAudioTrack\") &&\n\t    !config_has_user_value(activeConfiguration, \"AdvOut\", \"Pre22.1Settings\")) {\n\n\t\tint track = (int)config_get_int(activeConfiguration, \"AdvOut\", \"FFAudioTrack\");\n\t\tconfig_set_int(activeConfiguration, \"AdvOut\", \"FFAudioMixes\", 1LL << (track - 1));\n\t\tconfig_set_bool(activeConfiguration, \"AdvOut\", \"Pre22.1Settings\", true);\n\t\tchanged = true;\n\t}\n\n\t/* ----------------------------------------------------- */\n\t/* move over mixer values in advanced if older config */\n\tif (config_has_user_value(activeConfiguration, \"AdvOut\", \"RecTrackIndex\") &&\n\t    !config_has_user_value(activeConfiguration, \"AdvOut\", \"RecTracks\")) {\n\n\t\tuint64_t track = config_get_uint(activeConfiguration, \"AdvOut\", \"RecTrackIndex\");\n\t\ttrack = 1ULL << (track - 1);\n\t\tconfig_set_uint(activeConfiguration, \"AdvOut\", \"RecTracks\", track);\n\t\tconfig_remove_value(activeConfiguration, \"AdvOut\", \"RecTrackIndex\");\n\t\tchanged = true;\n\t}\n\n\t/* ----------------------------------------------------- */\n\t/* set twitch chat extensions to \"both\" if prev version  */\n\t/* is under 24.1                                         */\n\tif (config_get_bool(App()->GetUserConfig(), \"General\", \"Pre24.1Defaults\") &&\n\t    !config_has_user_value(activeConfiguration, \"Twitch\", \"AddonChoice\")) {\n\t\tconfig_set_int(activeConfiguration, \"Twitch\", \"AddonChoice\", 3);\n\t\tchanged = true;\n\t}\n\n\t/* ----------------------------------------------------- */\n\t/* move bitrate enforcement setting to new value         */\n\tif (config_has_user_value(activeConfiguration, \"SimpleOutput\", \"EnforceBitrate\") &&\n\t    !config_has_user_value(activeConfiguration, \"Stream1\", \"IgnoreRecommended\") &&\n\t    !config_has_user_value(activeConfiguration, \"Stream1\", \"MovedOldEnforce\")) {\n\t\tbool enforce = config_get_bool(activeConfiguration, \"SimpleOutput\", \"EnforceBitrate\");\n\t\tconfig_set_bool(activeConfiguration, \"Stream1\", \"IgnoreRecommended\", !enforce);\n\t\tconfig_set_bool(activeConfiguration, \"Stream1\", \"MovedOldEnforce\", true);\n\t\tchanged = true;\n\t}\n\n\t/* ----------------------------------------------------- */\n\t/* enforce minimum retry delay of 1 second prior to 27.1 */\n\tif (config_has_user_value(activeConfiguration, \"Output\", \"RetryDelay\")) {\n\t\tint retryDelay = config_get_uint(activeConfiguration, \"Output\", \"RetryDelay\");\n\t\tif (retryDelay < 1) {\n\t\t\tconfig_set_uint(activeConfiguration, \"Output\", \"RetryDelay\", 1);\n\t\t\tchanged = true;\n\t\t}\n\t}\n\n\t/* ----------------------------------------------------- */\n\t/* Migrate old container selection (if any) to new key.  */\n\n\tauto MigrateFormat = [&](const char *section) {\n\t\tbool has_old_key = config_has_user_value(activeConfiguration, section, \"RecFormat\");\n\t\tbool has_new_key = config_has_user_value(activeConfiguration, section, \"RecFormat2\");\n\t\tif (!has_new_key && !has_old_key)\n\t\t\treturn;\n\n\t\tstring old_format =\n\t\t\tconfig_get_string(activeConfiguration, section, has_new_key ? \"RecFormat2\" : \"RecFormat\");\n\t\tstring new_format = old_format;\n\t\tif (old_format == \"ts\")\n\t\t\tnew_format = \"mpegts\";\n\t\telse if (old_format == \"m3u8\")\n\t\t\tnew_format = \"hls\";\n\t\telse if (old_format == \"fmp4\")\n\t\t\tnew_format = \"fragmented_mp4\";\n\t\telse if (old_format == \"fmov\")\n\t\t\tnew_format = \"fragmented_mov\";\n\n\t\tif (new_format != old_format || !has_new_key) {\n\t\t\tconfig_set_string(activeConfiguration, section, \"RecFormat2\", new_format.c_str());\n\t\t\tchanged = true;\n\t\t}\n\t};\n\n\tMigrateFormat(\"AdvOut\");\n\tMigrateFormat(\"SimpleOutput\");\n\n\t/* ----------------------------------------------------- */\n\t/* Migrate output scale setting to GPU scaling options.  */\n\n\tif (config_get_bool(activeConfiguration, \"AdvOut\", \"Rescale\") &&\n\t    !config_has_user_value(activeConfiguration, \"AdvOut\", \"RescaleFilter\")) {\n\t\tconfig_set_int(activeConfiguration, \"AdvOut\", \"RescaleFilter\", OBS_SCALE_BILINEAR);\n\t}\n\n\tif (config_get_bool(activeConfiguration, \"AdvOut\", \"RecRescale\") &&\n\t    !config_has_user_value(activeConfiguration, \"AdvOut\", \"RecRescaleFilter\")) {\n\t\tconfig_set_int(activeConfiguration, \"AdvOut\", \"RecRescaleFilter\", OBS_SCALE_BILINEAR);\n\t}\n\n\t/* ----------------------------------------------------- */\n\n\tif (changed) {\n\t\tactiveConfiguration.SaveSafe(\"tmp\");\n\t}\n\n\t/* ----------------------------------------------------- */\n\n\tconfig_set_default_string(activeConfiguration, \"Output\", \"Mode\", \"Simple\");\n\n\tconfig_set_default_bool(activeConfiguration, \"Stream1\", \"IgnoreRecommended\", false);\n\tconfig_set_default_bool(activeConfiguration, \"Stream1\", \"EnableMultitrackVideo\", false);\n\tconfig_set_default_bool(activeConfiguration, \"Stream1\", \"MultitrackVideoMaximumAggregateBitrateAuto\", true);\n\tconfig_set_default_bool(activeConfiguration, \"Stream1\", \"MultitrackVideoMaximumVideoTracksAuto\", true);\n\n\tconfig_set_default_string(activeConfiguration, \"SimpleOutput\", \"FilePath\", GetDefaultVideoSavePath().c_str());\n\tconfig_set_default_string(activeConfiguration, \"SimpleOutput\", \"RecFormat2\", DEFAULT_CONTAINER);\n\tconfig_set_default_uint(activeConfiguration, \"SimpleOutput\", \"VBitrate\", 6000);\n\tconfig_set_default_uint(activeConfiguration, \"SimpleOutput\", \"ABitrate\", 160);\n\tconfig_set_default_bool(activeConfiguration, \"SimpleOutput\", \"UseAdvanced\", false);\n\tconfig_set_default_string(activeConfiguration, \"SimpleOutput\", \"Preset\", \"veryfast\");\n\tconfig_set_default_string(activeConfiguration, \"SimpleOutput\", \"NVENCPreset2\", \"p5\");\n\tconfig_set_default_string(activeConfiguration, \"SimpleOutput\", \"RecQuality\", \"Stream\");\n\tconfig_set_default_bool(activeConfiguration, \"SimpleOutput\", \"RecRB\", false);\n\tconfig_set_default_int(activeConfiguration, \"SimpleOutput\", \"RecRBTime\", 20);\n\tconfig_set_default_int(activeConfiguration, \"SimpleOutput\", \"RecRBSize\", 512);\n\tconfig_set_default_string(activeConfiguration, \"SimpleOutput\", \"RecRBPrefix\", \"Replay\");\n\tconfig_set_default_string(activeConfiguration, \"SimpleOutput\", \"StreamAudioEncoder\", \"aac\");\n\tconfig_set_default_string(activeConfiguration, \"SimpleOutput\", \"RecAudioEncoder\", \"aac\");\n\tconfig_set_default_uint(activeConfiguration, \"SimpleOutput\", \"RecTracks\", (1 << 0));\n\n\tconfig_set_default_bool(activeConfiguration, \"AdvOut\", \"ApplyServiceSettings\", true);\n\tconfig_set_default_bool(activeConfiguration, \"AdvOut\", \"UseRescale\", false);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"TrackIndex\", 1);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"VodTrackIndex\", 2);\n\tconfig_set_default_string(activeConfiguration, \"AdvOut\", \"Encoder\", \"obs_x264\");\n\n\tconfig_set_default_string(activeConfiguration, \"AdvOut\", \"RecType\", \"Standard\");\n\n\tconfig_set_default_string(activeConfiguration, \"AdvOut\", \"RecFilePath\", GetDefaultVideoSavePath().c_str());\n\tconfig_set_default_string(activeConfiguration, \"AdvOut\", \"RecFormat2\", DEFAULT_CONTAINER);\n\tconfig_set_default_bool(activeConfiguration, \"AdvOut\", \"RecUseRescale\", false);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"RecTracks\", (1 << 0));\n\tconfig_set_default_string(activeConfiguration, \"AdvOut\", \"RecEncoder\", \"none\");\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"FLVTrack\", 1);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"StreamMultiTrackAudioMixes\", 1);\n\tconfig_set_default_bool(activeConfiguration, \"AdvOut\", \"FFOutputToFile\", true);\n\tconfig_set_default_string(activeConfiguration, \"AdvOut\", \"FFFilePath\", GetDefaultVideoSavePath().c_str());\n\tconfig_set_default_string(activeConfiguration, \"AdvOut\", \"FFExtension\", \"mp4\");\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"FFVBitrate\", 6000);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"FFVGOPSize\", 250);\n\tconfig_set_default_bool(activeConfiguration, \"AdvOut\", \"FFUseRescale\", false);\n\tconfig_set_default_bool(activeConfiguration, \"AdvOut\", \"FFIgnoreCompat\", false);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"FFABitrate\", 160);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"FFAudioMixes\", 1);\n\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"Track1Bitrate\", 160);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"Track2Bitrate\", 160);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"Track3Bitrate\", 160);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"Track4Bitrate\", 160);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"Track5Bitrate\", 160);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"Track6Bitrate\", 160);\n\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"RecSplitFileTime\", 15);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"RecSplitFileSize\", 2048);\n\n\tconfig_set_default_bool(activeConfiguration, \"AdvOut\", \"RecRB\", false);\n\tconfig_set_default_uint(activeConfiguration, \"AdvOut\", \"RecRBTime\", 20);\n\tconfig_set_default_int(activeConfiguration, \"AdvOut\", \"RecRBSize\", 512);\n\n\tconfig_set_default_uint(activeConfiguration, \"Video\", \"BaseCX\", cx);\n\tconfig_set_default_uint(activeConfiguration, \"Video\", \"BaseCY\", cy);\n\n\t/* don't allow BaseCX/BaseCY to be susceptible to defaults changing */\n\tif (!config_has_user_value(activeConfiguration, \"Video\", \"BaseCX\") ||\n\t    !config_has_user_value(activeConfiguration, \"Video\", \"BaseCY\")) {\n\t\tconfig_set_uint(activeConfiguration, \"Video\", \"BaseCX\", cx);\n\t\tconfig_set_uint(activeConfiguration, \"Video\", \"BaseCY\", cy);\n\t\tconfig_save_safe(activeConfiguration, \"tmp\", nullptr);\n\t}\n\n\tconfig_set_default_string(activeConfiguration, \"Output\", \"FilenameFormatting\", \"%CCYY-%MM-%DD %hh-%mm-%ss\");\n\n\tconfig_set_default_bool(activeConfiguration, \"Output\", \"DelayEnable\", false);\n\tconfig_set_default_uint(activeConfiguration, \"Output\", \"DelaySec\", 20);\n\tconfig_set_default_bool(activeConfiguration, \"Output\", \"DelayPreserve\", true);\n\n\tconfig_set_default_bool(activeConfiguration, \"Output\", \"Reconnect\", true);\n\tconfig_set_default_uint(activeConfiguration, \"Output\", \"RetryDelay\", 2);\n\tconfig_set_default_uint(activeConfiguration, \"Output\", \"MaxRetries\", 25);\n\n\tconfig_set_default_string(activeConfiguration, \"Output\", \"BindIP\", \"default\");\n\tconfig_set_default_string(activeConfiguration, \"Output\", \"IPFamily\", \"IPv4+IPv6\");\n\tconfig_set_default_bool(activeConfiguration, \"Output\", \"NewSocketLoopEnable\", false);\n\tconfig_set_default_bool(activeConfiguration, \"Output\", \"LowLatencyEnable\", false);\n\n\tint i = 0;\n\tuint32_t scale_cx = cx;\n\tuint32_t scale_cy = cy;\n\n\t/* use a default scaled resolution that has a pixel count no higher\n\t * than 1280x720 */\n\twhile (((scale_cx * scale_cy) > (1280 * 720)) && scaled_vals[i] > 0.0) {\n\t\tdouble scale = scaled_vals[i++];\n\t\tscale_cx = uint32_t(double(cx) / scale);\n\t\tscale_cy = uint32_t(double(cy) / scale);\n\t}\n\n\tconfig_set_default_uint(activeConfiguration, \"Video\", \"OutputCX\", scale_cx);\n\tconfig_set_default_uint(activeConfiguration, \"Video\", \"OutputCY\", scale_cy);\n\n\t/* don't allow OutputCX/OutputCY to be susceptible to defaults\n\t * changing */\n\tif (!config_has_user_value(activeConfiguration, \"Video\", \"OutputCX\") ||\n\t    !config_has_user_value(activeConfiguration, \"Video\", \"OutputCY\")) {\n\t\tconfig_set_uint(activeConfiguration, \"Video\", \"OutputCX\", scale_cx);\n\t\tconfig_set_uint(activeConfiguration, \"Video\", \"OutputCY\", scale_cy);\n\t\tconfig_save_safe(activeConfiguration, \"tmp\", nullptr);\n\t}\n\n\tconfig_set_default_uint(activeConfiguration, \"Video\", \"FPSType\", 0);\n\tconfig_set_default_string(activeConfiguration, \"Video\", \"FPSCommon\", \"30\");\n\tconfig_set_default_uint(activeConfiguration, \"Video\", \"FPSInt\", 30);\n\tconfig_set_default_uint(activeConfiguration, \"Video\", \"FPSNum\", 30);\n\tconfig_set_default_uint(activeConfiguration, \"Video\", \"FPSDen\", 1);\n\tconfig_set_default_string(activeConfiguration, \"Video\", \"ScaleType\", \"bicubic\");\n\tconfig_set_default_string(activeConfiguration, \"Video\", \"ColorFormat\", \"NV12\");\n\tconfig_set_default_string(activeConfiguration, \"Video\", \"ColorSpace\", \"709\");\n\tconfig_set_default_string(activeConfiguration, \"Video\", \"ColorRange\", \"Partial\");\n\tconfig_set_default_uint(activeConfiguration, \"Video\", \"SdrWhiteLevel\", 300);\n\tconfig_set_default_uint(activeConfiguration, \"Video\", \"HdrNominalPeakLevel\", 1000);\n\n\tconfig_set_default_string(activeConfiguration, \"Audio\", \"MonitoringDeviceId\", \"default\");\n\tconfig_set_default_string(activeConfiguration, \"Audio\", \"MonitoringDeviceName\",\n\t\t\t\t  Str(\"Basic.Settings.Advanced.Audio.MonitoringDevice\"\n\t\t\t\t      \".Default\"));\n\tconfig_set_default_uint(activeConfiguration, \"Audio\", \"SampleRate\", 48000);\n\tconfig_set_default_string(activeConfiguration, \"Audio\", \"ChannelSetup\", \"Stereo\");\n\tconfig_set_default_double(activeConfiguration, \"Audio\", \"MeterDecayRate\", VOLUME_METER_DECAY_FAST);\n\tconfig_set_default_uint(activeConfiguration, \"Audio\", \"PeakMeterType\", 0);\n\n\tCheckExistingCookieId();\n\n\treturn true;\n}\n\nvoid OBSBasic::InitBasicConfigDefaults2()\n{\n\tbool oldEncDefaults = config_get_bool(App()->GetUserConfig(), \"General\", \"Pre23Defaults\");\n\tbool useNV = EncoderAvailable(\"ffmpeg_nvenc\") && !oldEncDefaults;\n\n\tconfig_set_default_string(activeConfiguration, \"SimpleOutput\", \"StreamEncoder\",\n\t\t\t\t  useNV ? SIMPLE_ENCODER_NVENC : SIMPLE_ENCODER_X264);\n\tconfig_set_default_string(activeConfiguration, \"SimpleOutput\", \"RecEncoder\",\n\t\t\t\t  useNV ? SIMPLE_ENCODER_NVENC : SIMPLE_ENCODER_X264);\n\n\tconst char *aac_default = \"ffmpeg_aac\";\n\tif (EncoderAvailable(\"CoreAudio_AAC\"))\n\t\taac_default = \"CoreAudio_AAC\";\n\telse if (EncoderAvailable(\"libfdk_aac\"))\n\t\taac_default = \"libfdk_aac\";\n\n\tconfig_set_default_string(activeConfiguration, \"AdvOut\", \"AudioEncoder\", aac_default);\n\tconfig_set_default_string(activeConfiguration, \"AdvOut\", \"RecAudioEncoder\", aac_default);\n}\n\nbool OBSBasic::InitBasicConfig()\n{\n\tProfileScope(\"OBSBasic::InitBasicConfig\");\n\n\tRefreshProfiles(true);\n\n\tconst std::string currentProfileName{config_get_string(App()->GetUserConfig(), \"Basic\", \"Profile\")};\n\tconst std::optional<OBSProfile> currentProfile = GetProfileByName(currentProfileName);\n\tconst std::optional<OBSProfile> foundProfile = GetProfileByName(opt_starting_profile);\n\n\ttry {\n\t\tif (foundProfile) {\n\t\t\tActivateProfile(foundProfile.value());\n\t\t} else if (currentProfile) {\n\t\t\tActivateProfile(currentProfile.value());\n\t\t} else {\n\t\t\tconst OBSProfile &newProfile = CreateProfile(currentProfileName);\n\t\t\tActivateProfile(newProfile);\n\t\t}\n\t} catch (const std::logic_error &) {\n\t\tOBSErrorBox(NULL, \"Failed to open basic.ini: %d\", -1);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid OBSBasic::InitOBSCallbacks()\n{\n\tProfileScope(\"OBSBasic::InitOBSCallbacks\");\n\n\tsignalHandlers.reserve(signalHandlers.size() + 6);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_create\", OBSBasic::SourceCreated, this);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_remove\", OBSBasic::SourceRemoved, this);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"source_rename\", OBSBasic::SourceRenamed, this);\n\tsignalHandlers.emplace_back(\n\t\tobs_get_signal_handler(), \"source_filter_add\",\n\t\t[](void *data, calldata_t *) {\n\t\t\tQMetaObject::invokeMethod(static_cast<OBSBasic *>(data), \"UpdateEditMenu\",\n\t\t\t\t\t\t  Qt::QueuedConnection);\n\t\t},\n\t\tthis);\n\tsignalHandlers.emplace_back(\n\t\tobs_get_signal_handler(), \"source_filter_remove\",\n\t\t[](void *data, calldata_t *) {\n\t\t\tQMetaObject::invokeMethod(static_cast<OBSBasic *>(data), \"UpdateEditMenu\",\n\t\t\t\t\t\t  Qt::QueuedConnection);\n\t\t},\n\t\tthis);\n\tsignalHandlers.emplace_back(obs_get_signal_handler(), \"canvas_remove\", OBSBasic::CanvasRemoved, this);\n}\n\n#define STARTUP_SEPARATOR \"==== Startup complete ===============================================\"\n#define SHUTDOWN_SEPARATOR \"==== Shutting down ==================================================\"\n\n#define UNSUPPORTED_ERROR                                                     \\\n\t\"Failed to initialize video:\\n\\nRequired graphics API functionality \" \\\n\t\"not found.  Your GPU may not be supported.\"\n\n#define UNKNOWN_ERROR                                                  \\\n\t\"Failed to initialize video.  Your GPU may not be supported, \" \\\n\t\"or your graphics drivers may need to be updated.\"\n\nstatic inline void LogEncoders()\n{\n\tconstexpr uint32_t hide_flags = OBS_ENCODER_CAP_DEPRECATED | OBS_ENCODER_CAP_INTERNAL;\n\n\tauto list_encoders = [](obs_encoder_type type) {\n\t\tsize_t idx = 0;\n\t\tconst char *encoder_type;\n\n\t\twhile (obs_enum_encoder_types(idx++, &encoder_type)) {\n\t\t\tif (obs_get_encoder_caps(encoder_type) & hide_flags ||\n\t\t\t    obs_get_encoder_type(encoder_type) != type) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tblog(LOG_INFO, \"\\t- %s (%s)\", encoder_type, obs_encoder_get_display_name(encoder_type));\n\t\t}\n\t};\n\n\tblog(LOG_INFO, \"---------------------------------\");\n\tblog(LOG_INFO, \"Available Encoders:\");\n\tblog(LOG_INFO, \"  Video Encoders:\");\n\tlist_encoders(OBS_ENCODER_VIDEO);\n\tblog(LOG_INFO, \"  Audio Encoders:\");\n\tlist_encoders(OBS_ENCODER_AUDIO);\n}\n\nvoid OBSBasic::OBSInit()\n{\n\tProfileScope(\"OBSBasic::OBSInit\");\n\n\tif (!InitBasicConfig())\n\t\tthrow \"Failed to load basic.ini\";\n\tif (!ResetAudio())\n\t\tthrow \"Failed to initialize audio\";\n\n\tint ret = 0;\n\n\tret = ResetVideo();\n\n\tswitch (ret) {\n\tcase OBS_VIDEO_MODULE_NOT_FOUND:\n\t\tthrow \"Failed to initialize video:  Graphics module not found\";\n\tcase OBS_VIDEO_NOT_SUPPORTED:\n\t\tthrow UNSUPPORTED_ERROR;\n\tcase OBS_VIDEO_INVALID_PARAM:\n\t\tthrow \"Failed to initialize video:  Invalid parameters\";\n\tdefault:\n\t\tif (ret != OBS_VIDEO_SUCCESS)\n\t\t\tthrow UNKNOWN_ERROR;\n\t}\n\n\t/* load audio monitoring */\n\tif (obs_audio_monitoring_available()) {\n\t\tconst char *device_name = config_get_string(activeConfiguration, \"Audio\", \"MonitoringDeviceName\");\n\t\tconst char *device_id = config_get_string(activeConfiguration, \"Audio\", \"MonitoringDeviceId\");\n\n\t\tobs_set_audio_monitoring_device(device_name, device_id);\n\n\t\tblog(LOG_INFO, \"Audio monitoring device:\\n\\tname: %s\\n\\tid: %s\", device_name, device_id);\n\t}\n\n\tInitOBSCallbacks();\n\tInitHotkeys();\n\tui->preview->Init();\n\n\t/* hack to prevent elgato from loading its own QtNetwork that it tries\n\t * to ship with */\n#if defined(_WIN32) && !defined(_DEBUG)\n\tLoadLibraryW(L\"Qt6Network\");\n#endif\n\tstruct obs_module_failure_info mfi;\n\n\t// Safe Mode disables third-party plugins so we don't need to add each path outside the OBS bundle/installation.\n\tif (safe_mode || disable_3p_plugins) {\n\t\tSetSafeModuleNames();\n\t} else {\n\t\tAddExtraModulePaths();\n\t}\n\n\t// Core modules are not allowed to be disabled by the user via plugin manager.\n\tSetCoreModuleNames();\n\n\t/* Modules can access frontend information (i.e. profile and scene collection data) during their initialization, and some modules (e.g. obs-websockets) are known to use the filesystem location of the current profile in their own code.\n\n     Thus the profile and scene collection discovery needs to happen before any access to that information (but after initializing global settings) to ensure legacy code gets valid path information.\n     */\n\tRefreshSceneCollections(true);\n\n\tApp()->loadAppModules(mfi);\n\n\tBPtr<char *> failed_modules = mfi.failed_modules;\n\n#ifdef BROWSER_AVAILABLE\n\tcef = obs_browser_init_panel();\n\tcef_js_avail = cef && obs_browser_qcef_version() >= 3;\n#endif\n\n\tvcamEnabled = (obs_get_output_flags(VIRTUAL_CAM_ID) & OBS_OUTPUT_VIDEO) != 0;\n\tif (vcamEnabled) {\n\t\temit VirtualCamEnabled();\n\t}\n\n\tUpdateProfileEncoders();\n\n\tLogEncoders();\n\n\tblog(LOG_INFO, STARTUP_SEPARATOR);\n\n\tif (!InitService())\n\t\tthrow \"Failed to initialize service\";\n\n\tResetOutputs();\n\tCreateHotkeys();\n\n\tInitPrimitives();\n\n\tsceneDuplicationMode = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SceneDuplicationMode\");\n\tswapScenesMode = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SwapScenesMode\");\n\teditPropertiesMode = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"EditPropertiesMode\");\n\n\tif (!opt_studio_mode) {\n\t\tSetPreviewProgramMode(config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"PreviewProgramMode\"));\n\t} else {\n\t\tSetPreviewProgramMode(true);\n\t\topt_studio_mode = false;\n\t}\n\n#define SET_VISIBILITY(name, control)                                                                \\\n\tdo {                                                                                         \\\n\t\tif (config_has_user_value(App()->GetUserConfig(), \"BasicWindow\", name)) {            \\\n\t\t\tbool visible = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", name); \\\n\t\t\tui->control->setChecked(visible);                                            \\\n\t\t}                                                                                    \\\n\t} while (false)\n\n\tSET_VISIBILITY(\"ShowListboxToolbars\", toggleListboxToolbars);\n\tSET_VISIBILITY(\"ShowStatusBar\", toggleStatusBar);\n#undef SET_VISIBILITY\n\n\tbool sourceIconsVisible = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowSourceIcons\");\n\tui->toggleSourceIcons->setChecked(sourceIconsVisible);\n\n\tbool contextVisible = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowContextToolbars\");\n\tui->toggleContextBar->setChecked(contextVisible);\n\tui->contextContainer->setVisible(contextVisible);\n\tif (contextVisible)\n\t\tUpdateContextBar(true);\n\tUpdateEditMenu();\n\n\t{\n\t\tProfileScope(\"OBSBasic::Load\");\n\t\tconst std::string sceneCollectionName{\n\t\t\tconfig_get_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\")};\n\t\tstd::optional<OBS::SceneCollection> configuredCollection =\n\t\t\tGetSceneCollectionByName(sceneCollectionName);\n\t\tstd::optional<OBS::SceneCollection> foundCollection = GetSceneCollectionByName(opt_starting_collection);\n\n\t\tif (foundCollection) {\n\t\t\tActivateSceneCollection(foundCollection.value());\n\t\t} else if (configuredCollection) {\n\t\t\tActivateSceneCollection(configuredCollection.value());\n\t\t} else {\n\t\t\tdisableSaving--;\n\t\t\tSetupNewSceneCollection(sceneCollectionName);\n\t\t\tdisableSaving++;\n\t\t}\n\n\t\tif (foundCollection || configuredCollection) {\n\t\t\tdisableSaving--;\n\t\t\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED);\n\t\t\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED);\n\t\t\tOnEvent(OBS_FRONTEND_EVENT_SCENE_CHANGED);\n\t\t\tOnEvent(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);\n\t\t\tdisableSaving++;\n\t\t}\n\t}\n\n\tloaded = true;\n\n\tpreviewEnabled = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"PreviewEnabled\");\n\n\tif (!previewEnabled && !IsPreviewProgramMode())\n\t\tQMetaObject::invokeMethod(this, \"EnablePreviewDisplay\", Qt::QueuedConnection,\n\t\t\t\t\t  Q_ARG(bool, previewEnabled));\n\telse if (!previewEnabled && IsPreviewProgramMode())\n\t\tQMetaObject::invokeMethod(this, \"EnablePreviewDisplay\", Qt::QueuedConnection, Q_ARG(bool, true));\n\n\tdisableSaving--;\n\n\tauto addDisplay = [this](OBSQTDisplay *window) {\n\t\tobs_display_add_draw_callback(window->GetDisplay(), OBSBasic::RenderMain, this);\n\n\t\tstruct obs_video_info ovi;\n\t\tif (obs_get_video_info(&ovi))\n\t\t\tResizePreview(ovi.base_width, ovi.base_height);\n\t};\n\n\tconnect(ui->preview, &OBSQTDisplay::DisplayCreated, this, addDisplay);\n\n\t/* Show the main window, unless the tray icon isn't available\n\t * or neither the setting nor flag for starting minimized is set. */\n\tbool sysTrayEnabled = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayEnabled\");\n\tbool sysTrayWhenStarted = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayWhenStarted\");\n\tbool hideWindowOnStart = QSystemTrayIcon::isSystemTrayAvailable() && sysTrayEnabled &&\n\t\t\t\t (opt_minimize_tray || sysTrayWhenStarted);\n\n#ifdef _WIN32\n\tSetWin32DropStyle(this);\n\n\tif (!hideWindowOnStart)\n\t\tshow();\n#endif\n\n\tbool alwaysOnTop = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"AlwaysOnTop\");\n\n#ifdef ENABLE_WAYLAND\n\tbool isWayland = obs_get_nix_platform() == OBS_NIX_PLATFORM_WAYLAND;\n#else\n\tbool isWayland = false;\n#endif\n\n\tif (!isWayland && (alwaysOnTop || opt_always_on_top)) {\n\t\tSetAlwaysOnTop(this, true);\n\t\tui->actionAlwaysOnTop->setChecked(true);\n\t} else if (isWayland) {\n\t\tif (opt_always_on_top)\n\t\t\tblog(LOG_INFO, \"Always On Top not available on Wayland, ignoring.\");\n\t\tui->actionAlwaysOnTop->setEnabled(false);\n\t\tui->actionAlwaysOnTop->setVisible(false);\n\t}\n\n#ifndef _WIN32\n\tif (!hideWindowOnStart)\n\t\tshow();\n#endif\n\n\t// Set up Audio Mixer dock\n\tAudioMixer *audioMixer = new AudioMixer(this);\n\tui->mixerDock->setWidget(audioMixer);\n\tui->mixerDock->setContextMenuPolicy(Qt::CustomContextMenu);\n\n\tconnect(ui->mixerDock, &QDockWidget::customContextMenuRequested, audioMixer, &AudioMixer::showMixerContextMenu);\n\n\t/* setup stats dock */\n\tOBSBasicStats *statsDlg = new OBSBasicStats(statsDock, false);\n\tstatsDock->setWidget(statsDlg);\n\n\t/* ----------------------------- */\n\t/* add custom browser docks      */\n#if defined(BROWSER_AVAILABLE) && defined(YOUTUBE_ENABLED)\n\tYouTubeAppDock::CleanupYouTubeUrls();\n#endif\n\n#ifdef BROWSER_AVAILABLE\n\tif (cef) {\n\t\tQAction *action = new QAction(QTStr(\"Basic.MainMenu.Docks.\"\n\t\t\t\t\t\t    \"CustomBrowserDocks\"),\n\t\t\t\t\t      this);\n\t\tui->menuDocks->insertAction(ui->scenesDock->toggleViewAction(), action);\n\t\tconnect(action, &QAction::triggered, this, &OBSBasic::ManageExtraBrowserDocks);\n\t\tui->menuDocks->insertSeparator(ui->scenesDock->toggleViewAction());\n\n\t\tLoadExtraBrowserDocks();\n\t}\n#endif\n\n#ifdef YOUTUBE_ENABLED\n\t/* setup YouTube app dock */\n\tif (YouTubeAppDock::IsYTServiceSelected())\n\t\tNewYouTubeAppDock();\n#endif\n\n\tconst char *dockStateStr = config_get_string(App()->GetUserConfig(), \"BasicWindow\", \"DockState\");\n\n\tif (!dockStateStr) {\n\t\ton_resetDocks_triggered(true);\n\t} else {\n\t\tQByteArray dockState = QByteArray::fromBase64(QByteArray(dockStateStr));\n\t\tif (!restoreState(dockState))\n\t\t\ton_resetDocks_triggered(true);\n\t}\n\n\tbool pre23Defaults = config_get_bool(App()->GetUserConfig(), \"General\", \"Pre23Defaults\");\n\tif (pre23Defaults) {\n\t\tbool resetDockLock23 = config_get_bool(App()->GetUserConfig(), \"General\", \"ResetDockLock23\");\n\t\tif (!resetDockLock23) {\n\t\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"ResetDockLock23\", true);\n\t\t\tconfig_remove_value(App()->GetUserConfig(), \"BasicWindow\", \"DocksLocked\");\n\t\t\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n\t\t}\n\t}\n\n\tbool docksLocked = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"DocksLocked\");\n\ton_lockDocks_toggled(docksLocked);\n\tui->lockDocks->blockSignals(true);\n\tui->lockDocks->setChecked(docksLocked);\n\tui->lockDocks->blockSignals(false);\n\n\tbool sideDocks = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SideDocks\");\n\tui->sideDocks->setChecked(sideDocks);\n\tsetDockCornersVertical(sideDocks);\n\n\tSystemTray(true);\n\n\tTaskbarOverlayInit();\n\n#ifdef __APPLE__\n\tdisableColorSpaceConversion(this);\n#endif\n\n\tbool has_last_version = config_has_user_value(App()->GetAppConfig(), \"General\", \"LastVersion\");\n\tbool first_run = config_get_bool(App()->GetUserConfig(), \"General\", \"FirstRun\");\n\n\tif (!first_run) {\n\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"FirstRun\", true);\n\t\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n\t}\n\n\tif (!first_run && !has_last_version && !Active())\n\t\tQMetaObject::invokeMethod(this, \"on_autoConfigure_triggered\", Qt::QueuedConnection);\n\n#if (defined(_WIN32) || defined(__APPLE__)) && (OBS_RELEASE_CANDIDATE > 0 || OBS_BETA > 0)\n\t/* Automatically set branch to \"beta\" the first time a pre-release build is run. */\n\tif (!config_get_bool(App()->GetAppConfig(), \"General\", \"AutoBetaOptIn\")) {\n\t\tconfig_set_string(App()->GetAppConfig(), \"General\", \"UpdateBranch\", \"beta\");\n\t\tconfig_set_bool(App()->GetAppConfig(), \"General\", \"AutoBetaOptIn\", true);\n\t\tconfig_save_safe(App()->GetAppConfig(), \"tmp\", nullptr);\n\t}\n#endif\n\tTimedCheckForUpdates();\n\n\temit userSettingChanged(\"BasicWindow\", \"VerticalVolumeControl\");\n\n\tif (config_get_bool(activeConfiguration, \"General\", \"OpenStatsOnStartup\"))\n\t\ton_stats_triggered();\n\n\tOBSBasicStats::InitializeValues();\n\n\t/* ----------------------- */\n\t/* Add multiview menu      */\n\n\tui->viewMenu->addSeparator();\n\n\tconnect(ui->viewMenu->menuAction(), &QAction::hovered, this, &OBSBasic::updateMultiviewProjectorMenu);\n\tOBSBasic::updateMultiviewProjectorMenu();\n\n\tui->sources->UpdateIcons();\n\n#if !defined(_WIN32)\n\tdelete ui->actionRepair;\n\tui->actionRepair = nullptr;\n#if !defined(__APPLE__)\n\tdelete ui->actionShowCrashLogs;\n\tdelete ui->actionUploadLastCrashLog;\n\tdelete ui->menuCrashLogs;\n\tdelete ui->actionCheckForUpdates;\n\tui->actionShowCrashLogs = nullptr;\n\tui->actionUploadLastCrashLog = nullptr;\n\tui->menuCrashLogs = nullptr;\n\tui->actionCheckForUpdates = nullptr;\n#endif\n#endif\n\n#ifdef __APPLE__\n\t/* Remove OBS' Fullscreen Interface menu in favor of the one macOS adds by default */\n\tdelete ui->actionFullscreenInterface;\n\tui->actionFullscreenInterface = nullptr;\n#else\n\t/* Don't show menu to raise macOS-only permissions dialog */\n\tdelete ui->actionShowMacPermissions;\n\tui->actionShowMacPermissions = nullptr;\n#endif\n\n#if defined(_WIN32) || defined(__APPLE__)\n\tif (App()->IsUpdaterDisabled()) {\n\t\tui->actionCheckForUpdates->setEnabled(false);\n#if defined(_WIN32)\n\t\tui->actionRepair->setEnabled(false);\n#endif\n\t}\n#endif\n\n#ifndef WHATSNEW_ENABLED\n\tdelete ui->actionShowWhatsNew;\n\tui->actionShowWhatsNew = nullptr;\n#endif\n\n\tif (safe_mode) {\n\t\tui->actionRestartSafe->setText(QTStr(\"Basic.MainMenu.Help.RestartNormal\"));\n\t}\n\n\tUpdatePreviewProgramIndicators();\n\tOnFirstLoad();\n\n\tif (!hideWindowOnStart)\n\t\tactivateWindow();\n\n\t/* ------------------------------------------- */\n\t/* display warning message for failed modules  */\n\n\tif (mfi.count) {\n\t\tQString failed_plugins;\n\n\t\tchar **plugin = mfi.failed_modules;\n\t\twhile (*plugin) {\n\t\t\tfailed_plugins += *plugin;\n\t\t\tfailed_plugins += \"\\n\";\n\t\t\tplugin++;\n\t\t}\n\n\t\tQString failed_msg = QTStr(\"PluginsFailedToLoad.Text\").arg(failed_plugins);\n\t\tOBSMessageBox::warning(this, QTStr(\"PluginsFailedToLoad.Title\"), failed_msg);\n\t}\n}\n\nvoid OBSBasic::OnFirstLoad()\n{\n\tOnEvent(OBS_FRONTEND_EVENT_FINISHED_LOADING);\n\n#ifdef WHATSNEW_ENABLED\n\t/* Attempt to load init screen if available */\n\tif (cef) {\n\t\tWhatsNewInfoThread *wnit = new WhatsNewInfoThread();\n\t\tconnect(wnit, &WhatsNewInfoThread::Result, this, &OBSBasic::ReceivedIntroJson, Qt::QueuedConnection);\n\n\t\tintroCheckThread.reset(wnit);\n\t\tintroCheckThread->start();\n\t}\n#endif\n\n\tAuth::Load();\n\n\tbool showLogViewerOnStartup = config_get_bool(App()->GetUserConfig(), \"LogViewer\", \"ShowLogStartup\");\n\n\tif (showLogViewerOnStartup)\n\t\ton_actionViewCurrentLog_triggered();\n}\n\nOBSBasic::~OBSBasic()\n{\n\tif (!isClosing()) {\n\t\tcloseWindow();\n\t}\n}\n\nvoid OBSBasic::applicationShutdown() noexcept\n{\n\t/* clear out UI event queue */\n\tQApplication::sendPostedEvents(nullptr);\n#ifndef __APPLE_\n\tQApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);\n#endif\n\n\tif (updateCheckThread && updateCheckThread->isRunning())\n\t\tupdateCheckThread->wait();\n\n\tif (patronJsonThread && patronJsonThread->isRunning())\n\t\tpatronJsonThread->wait();\n\n\tdelete screenshotData;\n\tdelete previewProjectorSource;\n\tdelete previewProjectorMain;\n\tdelete sourceProjector;\n\tdelete sceneProjectorMenu;\n\tdelete scaleFilteringMenu;\n\tdelete blendingModeMenu;\n\tdelete colorMenu;\n\tdelete colorWidgetAction;\n\tdelete colorSelect;\n\tdelete deinterlaceMenu;\n\tdelete perSceneTransitionMenu;\n\tdelete shortcutFilter;\n\tdelete programOptions;\n\tdelete program;\n\n\t/* XXX: any obs data must be released before calling obs_shutdown.\n\t * currently, we can't automate this with C++ RAII because of the\n\t * delicate nature of obs_shutdown needing to be freed before the UI\n\t * can be freed, and we have no control over the destruction order of\n\t * the Qt UI stuff, so we have to manually clear any references to\n\t * libobs. */\n\tdelete cpuUsageTimer;\n\tos_cpu_usage_info_destroy(cpuUsageInfo);\n\n\tobs_hotkey_set_callback_routing_func(nullptr, nullptr);\n\tClearHotkeys();\n\n\tservice = nullptr;\n\toutputHandler.reset();\n\n\tdelete interaction;\n\tdelete properties;\n\tdelete filters;\n\tdelete transformWindow;\n\tdelete advAudioWindow;\n\tdelete about;\n\tdelete remux;\n\n\tobs_display_remove_draw_callback(ui->preview->GetDisplay(), OBSBasic::RenderMain, this);\n\n\tobs_enter_graphics();\n\tgs_vertexbuffer_destroy(box);\n\tgs_vertexbuffer_destroy(boxLeft);\n\tgs_vertexbuffer_destroy(boxTop);\n\tgs_vertexbuffer_destroy(boxRight);\n\tgs_vertexbuffer_destroy(boxBottom);\n\tgs_vertexbuffer_destroy(circle);\n\tgs_vertexbuffer_destroy(actionSafeMargin);\n\tgs_vertexbuffer_destroy(graphicsSafeMargin);\n\tgs_vertexbuffer_destroy(fourByThreeSafeMargin);\n\tgs_vertexbuffer_destroy(leftLine);\n\tgs_vertexbuffer_destroy(topLine);\n\tgs_vertexbuffer_destroy(rightLine);\n\tobs_leave_graphics();\n\n\t/* When shutting down, sometimes source references can get in to the\n\t * event queue, and if we don't forcibly process those events they\n\t * won't get processed until after obs_shutdown has been called.  I\n\t * really wish there were a more elegant way to deal with this via C++,\n\t * but Qt doesn't use C++ in a normal way, so you can't really rely on\n\t * normal C++ behavior for your data to be freed in the order that you\n\t * expect or want it to. */\n\tQApplication::sendPostedEvents(nullptr);\n\n#ifdef BROWSER_AVAILABLE\n\tDestroyPanelCookieManager();\n\tdelete cef;\n\tcef = nullptr;\n#endif\n\n\thandledShutdown = true;\n}\n\nvoid OBSBasic::toggleMixerLayout()\n{\n\tbool vertical = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"VerticalVolumeControl\");\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"VerticalVolumeControl\", !vertical);\n\n\temit userSettingChanged(\"BasicWindow\", \"VerticalVolumeControl\");\n}\n\nstatic inline int AttemptToResetVideo(struct obs_video_info *ovi)\n{\n\treturn obs_reset_video(ovi);\n}\n\nstatic inline enum obs_scale_type GetScaleType(ConfigFile &activeConfiguration)\n{\n\tconst char *scaleTypeStr = config_get_string(activeConfiguration, \"Video\", \"ScaleType\");\n\n\tif (astrcmpi(scaleTypeStr, \"bilinear\") == 0)\n\t\treturn OBS_SCALE_BILINEAR;\n\telse if (astrcmpi(scaleTypeStr, \"lanczos\") == 0)\n\t\treturn OBS_SCALE_LANCZOS;\n\telse if (astrcmpi(scaleTypeStr, \"area\") == 0)\n\t\treturn OBS_SCALE_AREA;\n\telse\n\t\treturn OBS_SCALE_BICUBIC;\n}\n\nstatic inline enum video_format GetVideoFormatFromName(const char *name)\n{\n\tif (astrcmpi(name, \"I420\") == 0)\n\t\treturn VIDEO_FORMAT_I420;\n\telse if (astrcmpi(name, \"NV12\") == 0)\n\t\treturn VIDEO_FORMAT_NV12;\n\telse if (astrcmpi(name, \"I444\") == 0)\n\t\treturn VIDEO_FORMAT_I444;\n\telse if (astrcmpi(name, \"I010\") == 0)\n\t\treturn VIDEO_FORMAT_I010;\n\telse if (astrcmpi(name, \"P010\") == 0)\n\t\treturn VIDEO_FORMAT_P010;\n\telse if (astrcmpi(name, \"P216\") == 0)\n\t\treturn VIDEO_FORMAT_P216;\n\telse if (astrcmpi(name, \"P416\") == 0)\n\t\treturn VIDEO_FORMAT_P416;\n#if 0 //currently unsupported\n\telse if (astrcmpi(name, \"YVYU\") == 0)\n\t\treturn VIDEO_FORMAT_YVYU;\n\telse if (astrcmpi(name, \"YUY2\") == 0)\n\t\treturn VIDEO_FORMAT_YUY2;\n\telse if (astrcmpi(name, \"UYVY\") == 0)\n\t\treturn VIDEO_FORMAT_UYVY;\n#endif\n\telse\n\t\treturn VIDEO_FORMAT_BGRA;\n}\n\nstatic inline enum video_colorspace GetVideoColorSpaceFromName(const char *name)\n{\n\tenum video_colorspace colorspace = VIDEO_CS_SRGB;\n\tif (strcmp(name, \"601\") == 0)\n\t\tcolorspace = VIDEO_CS_601;\n\telse if (strcmp(name, \"709\") == 0)\n\t\tcolorspace = VIDEO_CS_709;\n\telse if (strcmp(name, \"2100PQ\") == 0)\n\t\tcolorspace = VIDEO_CS_2100_PQ;\n\telse if (strcmp(name, \"2100HLG\") == 0)\n\t\tcolorspace = VIDEO_CS_2100_HLG;\n\n\treturn colorspace;\n}\n\nint OBSBasic::ResetVideo()\n{\n\tif (outputHandler && outputHandler->Active())\n\t\treturn OBS_VIDEO_CURRENTLY_ACTIVE;\n\n\tProfileScope(\"OBSBasic::ResetVideo\");\n\n\tstruct obs_video_info ovi;\n\tint ret;\n\n\tGetConfigFPS(ovi.fps_num, ovi.fps_den);\n\n\tconst char *colorFormat = config_get_string(activeConfiguration, \"Video\", \"ColorFormat\");\n\tconst char *colorSpace = config_get_string(activeConfiguration, \"Video\", \"ColorSpace\");\n\tconst char *colorRange = config_get_string(activeConfiguration, \"Video\", \"ColorRange\");\n\n\tovi.graphics_module = App()->GetRenderModule();\n\tovi.base_width = (uint32_t)config_get_uint(activeConfiguration, \"Video\", \"BaseCX\");\n\tovi.base_height = (uint32_t)config_get_uint(activeConfiguration, \"Video\", \"BaseCY\");\n\tovi.output_width = (uint32_t)config_get_uint(activeConfiguration, \"Video\", \"OutputCX\");\n\tovi.output_height = (uint32_t)config_get_uint(activeConfiguration, \"Video\", \"OutputCY\");\n\tovi.output_format = GetVideoFormatFromName(colorFormat);\n\tovi.colorspace = GetVideoColorSpaceFromName(colorSpace);\n\tovi.range = astrcmpi(colorRange, \"Full\") == 0 ? VIDEO_RANGE_FULL : VIDEO_RANGE_PARTIAL;\n\tovi.adapter = config_get_uint(App()->GetUserConfig(), \"Video\", \"AdapterIdx\");\n\tovi.gpu_conversion = true;\n\tovi.scale_type = GetScaleType(activeConfiguration);\n\n\tif (ovi.base_width < 32 || ovi.base_height < 32) {\n\t\tovi.base_width = 1920;\n\t\tovi.base_height = 1080;\n\t\tconfig_set_uint(activeConfiguration, \"Video\", \"BaseCX\", 1920);\n\t\tconfig_set_uint(activeConfiguration, \"Video\", \"BaseCY\", 1080);\n\t}\n\n\tif (ovi.output_width < 32 || ovi.output_height < 32) {\n\t\tovi.output_width = ovi.base_width;\n\t\tovi.output_height = ovi.base_height;\n\t\tconfig_set_uint(activeConfiguration, \"Video\", \"OutputCX\", ovi.base_width);\n\t\tconfig_set_uint(activeConfiguration, \"Video\", \"OutputCY\", ovi.base_height);\n\t}\n\n\tret = AttemptToResetVideo(&ovi);\n\tif (ret == OBS_VIDEO_CURRENTLY_ACTIVE) {\n\t\tblog(LOG_WARNING, \"Tried to reset when already active\");\n\t\treturn ret;\n\t}\n\n\tif (ret == OBS_VIDEO_SUCCESS) {\n\t\tResizePreview(ovi.base_width, ovi.base_height);\n\t\tif (program)\n\t\t\tResizeProgram(ovi.base_width, ovi.base_height);\n\n\t\tconst float sdr_white_level = (float)config_get_uint(activeConfiguration, \"Video\", \"SdrWhiteLevel\");\n\t\tconst float hdr_nominal_peak_level =\n\t\t\t(float)config_get_uint(activeConfiguration, \"Video\", \"HdrNominalPeakLevel\");\n\t\tobs_set_video_levels(sdr_white_level, hdr_nominal_peak_level);\n\t\tOBSBasicStats::InitializeValues();\n\t\tOBSProjector::UpdateMultiviewProjectors();\n\n\t\tif (!collections.empty()) {\n\t\t\tconst OBS::SceneCollection currentSceneCollection = OBSBasic::GetCurrentSceneCollection();\n\n\t\t\tbool usingAbsoluteCoordinates = currentSceneCollection.getCoordinateMode() ==\n\t\t\t\t\t\t\tOBS::SceneCoordinateMode::Absolute;\n\t\t\tOBS::Rect migrationResolution = currentSceneCollection.getMigrationResolution();\n\n\t\t\tOBS::Rect videoResolution = OBS::Rect(ovi.base_width, ovi.base_height);\n\n\t\t\tbool canMigrate = usingAbsoluteCoordinates ||\n\t\t\t\t\t  (!migrationResolution.isZero() && migrationResolution != videoResolution);\n\n\t\t\tui->actionRemigrateSceneCollection->setEnabled(canMigrate);\n\t\t} else {\n\t\t\tui->actionRemigrateSceneCollection->setEnabled(false);\n\t\t}\n\n\t\temit CanvasResized(ovi.base_width, ovi.base_height);\n\t\temit OutputResized(ovi.output_width, ovi.output_height);\n\t}\n\n\treturn ret;\n}\n\nbool OBSBasic::ResetAudio()\n{\n\tProfileScope(\"OBSBasic::ResetAudio\");\n\n\tstruct obs_audio_info2 ai = {};\n\tai.samples_per_sec = config_get_uint(activeConfiguration, \"Audio\", \"SampleRate\");\n\n\tconst char *channelSetupStr = config_get_string(activeConfiguration, \"Audio\", \"ChannelSetup\");\n\n\tif (strcmp(channelSetupStr, \"Mono\") == 0)\n\t\tai.speakers = SPEAKERS_MONO;\n\telse if (strcmp(channelSetupStr, \"2.1\") == 0)\n\t\tai.speakers = SPEAKERS_2POINT1;\n\telse if (strcmp(channelSetupStr, \"4.0\") == 0)\n\t\tai.speakers = SPEAKERS_4POINT0;\n\telse if (strcmp(channelSetupStr, \"4.1\") == 0)\n\t\tai.speakers = SPEAKERS_4POINT1;\n\telse if (strcmp(channelSetupStr, \"5.1\") == 0)\n\t\tai.speakers = SPEAKERS_5POINT1;\n\telse if (strcmp(channelSetupStr, \"7.1\") == 0)\n\t\tai.speakers = SPEAKERS_7POINT1;\n\telse\n\t\tai.speakers = SPEAKERS_STEREO;\n\n\tbool lowLatencyAudioBuffering = config_get_bool(App()->GetUserConfig(), \"Audio\", \"LowLatencyAudioBuffering\");\n\tif (lowLatencyAudioBuffering) {\n\t\tai.max_buffering_ms = 20;\n\t\tai.fixed_buffering = true;\n\t}\n\n\treturn obs_reset_audio2(&ai);\n}\n\nvoid OBSBasic::close()\n{\n\tif (isClosePromptOpen() || isClosing()) {\n\t\treturn;\n\t}\n\n\tOBSMainWindow::close();\n}\n\nvoid OBSBasic::closeEvent(QCloseEvent *event)\n{\n\tif (isClosePromptOpen() || isClosing()) {\n\t\treturn;\n\t}\n\n\tif (isVisible()) {\n\t\tconfig_set_string(App()->GetUserConfig(), \"BasicWindow\", \"geometry\",\n\t\t\t\t  saveGeometry().toBase64().constData());\n\t}\n\n\tif (!isReadyToClose()) {\n\t\tevent->ignore();\n\n\t\tQTimer::singleShot(1000, this, &OBSBasic::close);\n\t\treturn;\n\t}\n\n\tif (remux && !remux->close()) {\n\t\tevent->ignore();\n\t\trestart = false;\n\n\t\treturn;\n\t}\n\n\tif (shouldPromptForClose()) {\n\t\tevent->ignore();\n\t\trestart = false;\n\n\t\tif (!isClosePromptOpen()) {\n\t\t\tbool shouldClose = promptToClose();\n\n\t\t\tif (shouldClose) {\n\t\t\t\tcloseWindow();\n\t\t\t}\n\t\t}\n\n\t\treturn;\n\t}\n\n\tQWidget::closeEvent(event);\n\tif (!event->isAccepted()) {\n\t\treturn;\n\t}\n\n\tcloseWindow();\n}\n\nbool OBSBasic::nativeEvent(const QByteArray &, void *message, qintptr *)\n{\n#ifdef _WIN32\n\tconst MSG &msg = *static_cast<MSG *>(message);\n\tswitch (msg.message) {\n\tcase WM_MOVE:\n\t\tfor (OBSQTDisplay *const display : findChildren<OBSQTDisplay *>()) {\n\t\t\tdisplay->OnMove();\n\t\t}\n\t\tbreak;\n\tcase WM_DISPLAYCHANGE:\n\t\tfor (OBSQTDisplay *const display : findChildren<OBSQTDisplay *>()) {\n\t\t\tdisplay->OnDisplayChange();\n\t\t}\n\t}\n#else\n\tUNUSED_PARAMETER(message);\n#endif\n\n\treturn false;\n}\n\nvoid OBSBasic::changeEvent(QEvent *event)\n{\n\tif (event->type() == QEvent::WindowStateChange) {\n\t\tQWindowStateChangeEvent *stateEvent = (QWindowStateChangeEvent *)event;\n\n\t\tif (isMinimized()) {\n\t\t\tif (trayIcon && trayIcon->isVisible() && sysTrayMinimizeToTray()) {\n\t\t\t\tToggleShowHide();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (previewEnabled)\n\t\t\t\tEnablePreviewDisplay(false);\n\t\t} else if (stateEvent->oldState() & Qt::WindowMinimized && isVisible()) {\n\t\t\tif (previewEnabled)\n\t\t\t\tEnablePreviewDisplay(true);\n\t\t}\n\t}\n}\n\nvoid OBSBasic::GetFPSCommon(uint32_t &num, uint32_t &den) const\n{\n\tconst char *val = config_get_string(activeConfiguration, \"Video\", \"FPSCommon\");\n\n\tif (strcmp(val, \"10\") == 0) {\n\t\tnum = 10;\n\t\tden = 1;\n\t} else if (strcmp(val, \"20\") == 0) {\n\t\tnum = 20;\n\t\tden = 1;\n\t} else if (strcmp(val, \"24 NTSC\") == 0) {\n\t\tnum = 24000;\n\t\tden = 1001;\n\t} else if (strcmp(val, \"25 PAL\") == 0) {\n\t\tnum = 25;\n\t\tden = 1;\n\t} else if (strcmp(val, \"29.97\") == 0) {\n\t\tnum = 30000;\n\t\tden = 1001;\n\t} else if (strcmp(val, \"48\") == 0) {\n\t\tnum = 48;\n\t\tden = 1;\n\t} else if (strcmp(val, \"50 PAL\") == 0) {\n\t\tnum = 50;\n\t\tden = 1;\n\t} else if (strcmp(val, \"59.94\") == 0) {\n\t\tnum = 60000;\n\t\tden = 1001;\n\t} else if (strcmp(val, \"60\") == 0) {\n\t\tnum = 60;\n\t\tden = 1;\n\t} else {\n\t\tnum = 30;\n\t\tden = 1;\n\t}\n}\n\nvoid OBSBasic::GetFPSInteger(uint32_t &num, uint32_t &den) const\n{\n\tnum = (uint32_t)config_get_uint(activeConfiguration, \"Video\", \"FPSInt\");\n\tden = 1;\n}\n\nvoid OBSBasic::GetFPSFraction(uint32_t &num, uint32_t &den) const\n{\n\tnum = (uint32_t)config_get_uint(activeConfiguration, \"Video\", \"FPSNum\");\n\tden = (uint32_t)config_get_uint(activeConfiguration, \"Video\", \"FPSDen\");\n}\n\nvoid OBSBasic::GetFPSNanoseconds(uint32_t &num, uint32_t &den) const\n{\n\tnum = 1000000000;\n\tden = (uint32_t)config_get_uint(activeConfiguration, \"Video\", \"FPSNS\");\n}\n\nvoid OBSBasic::GetConfigFPS(uint32_t &num, uint32_t &den) const\n{\n\tuint32_t type = config_get_uint(activeConfiguration, \"Video\", \"FPSType\");\n\n\tif (type == 1) //\"Integer\"\n\t\tGetFPSInteger(num, den);\n\telse if (type == 2) //\"Fraction\"\n\t\tGetFPSFraction(num, den);\n\t/*\n\t * \telse if (false) //\"Nanoseconds\", currently not implemented\n\t *\t\tGetFPSNanoseconds(num, den);\n\t */\n\telse\n\t\tGetFPSCommon(num, den);\n}\n\nconfig_t *OBSBasic::Config() const\n{\n\treturn activeConfiguration;\n}\n\nvoid OBSBasic::saveAll()\n{\n\tif (isVisible()) {\n\t\tconfig_set_string(App()->GetUserConfig(), \"BasicWindow\", \"geometry\",\n\t\t\t\t  saveGeometry().toBase64().constData());\n\t}\n\n\tAuth::Save();\n\tSaveProjectNow();\n\n\tconfig_set_string(App()->GetUserConfig(), \"BasicWindow\", \"DockState\", saveState().toBase64().constData());\n\n#ifdef BROWSER_AVAILABLE\n\tif (cef) {\n\t\tSaveExtraBrowserDocks();\n\t}\n#endif\n\n\tconfig_set_int(App()->GetAppConfig(), \"General\", \"LastVersion\", LIBOBS_API_VER);\n\tconfig_save_safe(App()->GetAppConfig(), \"tmp\", nullptr);\n\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"PreviewEnabled\", previewEnabled);\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"AlwaysOnTop\", ui->actionAlwaysOnTop->isChecked());\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SceneDuplicationMode\", sceneDuplicationMode);\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SwapScenesMode\", swapScenesMode);\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"EditPropertiesMode\", editPropertiesMode);\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"PreviewProgramMode\", IsPreviewProgramMode());\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"DocksLocked\", ui->lockDocks->isChecked());\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SideDocks\", ui->sideDocks->isChecked());\n\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n}\n\nbool OBSBasic::isReadyToClose()\n{\n\t/* Wait for multitrack video stream to start/finish processing in the background */\n\tif (setupStreamingGuard.valid() &&\n\t    setupStreamingGuard.wait_for(std::chrono::seconds{0}) != std::future_status::ready) {\n\t\treturn false;\n\t}\n\n\t/* Do not close window if inside of a temporary event loop because we\n\t * could be inside of an Auth::LoadUI call.  Keep trying once per\n\t * second until we've exit any known sub-loops. */\n\tif (os_atomic_load_long(&insideEventLoop) != 0) {\n\t\treturn false;\n\t}\n\n#ifdef YOUTUBE_ENABLED\n\t/* Also don't close the window if the youtube stream check is active */\n\tif (youtubeStreamCheckThread) {\n\t\treturn false;\n\t}\n#endif\n\n\treturn true;\n}\n\nbool OBSBasic::shouldPromptForClose()\n{\n\tbool confirmOnExit = config_get_bool(App()->GetUserConfig(), \"General\", \"ConfirmOnExit\");\n\tif (confirmOnExit && outputHandler && outputHandler->Active() && !clearingFailed) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool OBSBasic::promptToClose()\n{\n\tisClosePromptOpen_ = true;\n\n\tSetShowing(true);\n\tQMessageBox::StandardButton button =\n\t\tOBSMessageBox::question(this, QTStr(\"ConfirmExit.Title\"), QTStr(\"ConfirmExit.Text\"),\n\t\t\t\t\tQMessageBox::StandardButtons(QMessageBox::Ok | QMessageBox::Cancel));\n\n\tif (button == QMessageBox::Cancel) {\n\t\tisClosePromptOpen_ = false;\n\t\treturn false;\n\t}\n\n\tisClosePromptOpen_ = false;\n\treturn true;\n}\n\nvoid OBSBasic::closeWindow()\n{\n\tif (isClosing()) {\n\t\treturn;\n\t}\n\n\tblog(LOG_INFO, SHUTDOWN_SEPARATOR);\n\n\tisClosing_ = true;\n\n\t/* While closing, a resize event to OBSQTDisplay could be triggered.\n\t * The graphics thread on macOS dispatches a lambda function to be\n\t * executed asynchronously in the main thread. However, the display is\n\t * sometimes deleted before the lambda function is actually executed.\n\t * To avoid such a case, destroy displays earlier than others such as\n\t * deleting browser docks. */\n\tui->preview->DestroyDisplay();\n\tif (program)\n\t\tprogram->DestroyDisplay();\n\n\tif (outputHandler->VirtualCamActive())\n\t\toutputHandler->StopVirtualCam();\n\n\tif (introCheckThread)\n\t\tintroCheckThread->wait();\n\tif (whatsNewInitThread)\n\t\twhatsNewInitThread->wait();\n\tif (updateCheckThread)\n\t\tupdateCheckThread->wait();\n\tif (logUploadThread)\n\t\tlogUploadThread->wait();\n\tif (devicePropertiesThread && devicePropertiesThread->isRunning()) {\n\t\tdevicePropertiesThread->wait();\n\t\tdevicePropertiesThread.reset();\n\t}\n\n\tQApplication::sendPostedEvents(nullptr);\n\n\tsignalHandlers.clear();\n\tdelete extraBrowsers;\n\n\tsaveAll();\n\n\tauth.reset();\n\n#ifdef BROWSER_AVAILABLE\n\tClearExtraBrowserDocks();\n\n#ifdef YOUTUBE_ENABLED\n\tif (youtubeAppDock) {\n\t\tRemoveDockWidget(youtubeAppDock->objectName());\n\t\tyoutubeAppDock = nullptr;\n\t}\n#endif\n#endif\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCRIPTING_SHUTDOWN);\n\n\tdisableSaving++;\n\n\t/* Clear all scene data (dialogs, widgets, widget sub-items, scenes,\n\t * sources, etc) so that all references are released before shutdown */\n\tClearSceneData();\n\n\tOnEvent(OBS_FRONTEND_EVENT_EXIT);\n\n\t// Destroys the frontend API so plugins can't continue calling it\n\tobs_frontend_set_callbacks_internal(nullptr);\n\tapi = nullptr;\n\n\tapplicationShutdown();\n\n#ifndef __APPLE__\n\tdeleteLater();\n\n\temit mainWindowClosed();\n\n\tQMetaObject::invokeMethod(App(), \"quit\", Qt::QueuedConnection);\n#else\n\tQMetaObject::invokeMethod(App(), \"quit\", Qt::QueuedConnection);\n#endif\n}\n\nvoid OBSBasic::UpdateEditMenu()\n{\n\tQModelIndexList items = GetAllSelectedSourceItems();\n\tint totalCount = items.count();\n\tsize_t filter_count = 0;\n\n\tif (totalCount == 1) {\n\t\tOBSSceneItem sceneItem = ui->sources->Get(GetTopSelectedSourceItem());\n\t\tOBSSource source = obs_sceneitem_get_source(sceneItem);\n\t\tfilter_count = obs_source_filter_count(source);\n\t}\n\n\tbool allowPastingDuplicate = !!clipboard.size();\n\tfor (size_t i = clipboard.size(); i > 0; i--) {\n\t\tconst size_t idx = i - 1;\n\t\tOBSWeakSource &weak = clipboard[idx].weak_source;\n\t\tif (obs_weak_source_expired(weak)) {\n\t\t\tclipboard.erase(clipboard.begin() + idx);\n\t\t\tcontinue;\n\t\t}\n\t\tOBSSourceAutoRelease strong = obs_weak_source_get_source(weak.Get());\n\t\tif (allowPastingDuplicate && obs_source_get_output_flags(strong) & OBS_SOURCE_DO_NOT_DUPLICATE)\n\t\t\tallowPastingDuplicate = false;\n\t}\n\n\tint videoCount = 0;\n\tbool canTransformMultiple = false;\n\tfor (int i = 0; i < totalCount; i++) {\n\t\tOBSSceneItem item = ui->sources->Get(items.value(i).row());\n\t\tOBSSource source = obs_sceneitem_get_source(item);\n\t\tconst uint32_t flags = obs_source_get_output_flags(source);\n\t\tconst bool hasVideo = (flags & OBS_SOURCE_VIDEO) != 0;\n\t\tif (hasVideo && !obs_sceneitem_locked(item))\n\t\t\tcanTransformMultiple = true;\n\n\t\tif (hasVideo)\n\t\t\tvideoCount++;\n\t}\n\tconst bool canTransformSingle = videoCount == 1 && totalCount == 1;\n\n\tOBSSceneItem curItem = GetCurrentSceneItem();\n\tbool locked = curItem && obs_sceneitem_locked(curItem);\n\n\tui->actionCopySource->setEnabled(totalCount > 0);\n\tui->actionEditTransform->setEnabled(canTransformSingle && !locked);\n\tui->actionCopyTransform->setEnabled(canTransformSingle);\n\tui->actionPasteTransform->setEnabled(canTransformMultiple && hasCopiedTransform && videoCount > 0);\n\tui->actionCopyFilters->setEnabled(filter_count > 0);\n\tui->actionPasteFilters->setEnabled(!obs_weak_source_expired(copyFiltersSource()) && totalCount > 0);\n\tui->actionPasteRef->setEnabled(!!clipboard.size());\n\tui->actionPasteDup->setEnabled(allowPastingDuplicate);\n\n\tui->actionMoveUp->setEnabled(totalCount > 0);\n\tui->actionMoveDown->setEnabled(totalCount > 0);\n\tui->actionMoveToTop->setEnabled(totalCount > 0);\n\tui->actionMoveToBottom->setEnabled(totalCount > 0);\n\n\tui->actionResetTransform->setEnabled(canTransformMultiple);\n\tui->actionRotate90CW->setEnabled(canTransformMultiple);\n\tui->actionRotate90CCW->setEnabled(canTransformMultiple);\n\tui->actionRotate180->setEnabled(canTransformMultiple);\n\tui->actionFlipHorizontal->setEnabled(canTransformMultiple);\n\tui->actionFlipVertical->setEnabled(canTransformMultiple);\n\tui->actionFitToScreen->setEnabled(canTransformMultiple);\n\tui->actionStretchToScreen->setEnabled(canTransformMultiple);\n\tui->actionCenterToScreen->setEnabled(canTransformMultiple);\n\tui->actionVerticalCenter->setEnabled(canTransformMultiple);\n\tui->actionHorizontalCenter->setEnabled(canTransformMultiple);\n}\n\nvoid OBSBasic::UpdateTitleBar()\n{\n\tstringstream name;\n\n\tconst char *profile = config_get_string(App()->GetUserConfig(), \"Basic\", \"Profile\");\n\tconst char *sceneCollection = config_get_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\");\n\n\tname << \"OBS \";\n\tif (previewProgramMode)\n\t\tname << \"Studio \";\n\n\tname << App()->GetVersionString(false);\n\tif (safe_mode)\n\t\tname << \" (\" << Str(\"TitleBar.SafeMode\") << \")\";\n\tif (App()->IsPortableMode())\n\t\tname << \" - \" << Str(\"TitleBar.PortableMode\");\n\n\tname << \" - \" << Str(\"TitleBar.Profile\") << \": \" << profile;\n\tname << \" - \" << Str(\"TitleBar.Scenes\") << \": \" << sceneCollection;\n\n\tsetWindowTitle(QT_UTF8(name.str().c_str()));\n}\n\nOBSBasic *OBSBasic::Get()\n{\n\treturn reinterpret_cast<OBSBasic *>(App()->GetMainWindow());\n}\n\nvoid OBSBasic::UpdatePatronJson(const QString &text, const QString &error)\n{\n\tif (!error.isEmpty())\n\t\treturn;\n\n\tpatronJson = QT_TO_UTF8(text);\n}\n\nvoid OBSBasic::SetDisplayAffinity(QWindow *window)\n{\n\tif (!SetDisplayAffinitySupported())\n\t\treturn;\n\n\tbool hideFromCapture = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"HideOBSWindowsFromCapture\");\n\n\t// Don't hide projectors, those are designed to be visible / captured\n\tif (window->property(\"isOBSProjectorWindow\") == true)\n\t\treturn;\n\n#ifdef _WIN32\n\tHWND hwnd = (HWND)window->winId();\n\n\tDWORD curAffinity;\n\tif (GetWindowDisplayAffinity(hwnd, &curAffinity)) {\n\t\tif (hideFromCapture && curAffinity != WDA_EXCLUDEFROMCAPTURE)\n\t\t\tSetWindowDisplayAffinity(hwnd, WDA_EXCLUDEFROMCAPTURE);\n\t\telse if (!hideFromCapture && curAffinity != WDA_NONE)\n\t\t\tSetWindowDisplayAffinity(hwnd, WDA_NONE);\n\t}\n\n#else\n\t// TODO: Implement for other platforms if possible. Don't forget to\n\t// implement SetDisplayAffinitySupported too!\n\tUNUSED_PARAMETER(hideFromCapture);\n#endif\n}\n\nvoid OBSBasic::OnEvent(enum obs_frontend_event event)\n{\n\tif (api)\n\t\tapi->on_event(event);\n}\n\nOBSPromptResult OBSBasic::PromptForName(const OBSPromptRequest &request, const OBSPromptCallback &callback)\n{\n\tOBSPromptResult result;\n\n\tfor (;;) {\n\t\tresult.success = false;\n\n\t\tif (request.withOption && !request.optionPrompt.empty()) {\n\t\t\tresult.optionValue = request.optionValue;\n\n\t\t\tresult.success = NameDialog::AskForNameWithOption(\n\t\t\t\tthis, request.title.c_str(), request.prompt.c_str(), result.promptValue,\n\t\t\t\trequest.optionPrompt.c_str(), result.optionValue,\n\t\t\t\t(request.promptValue.empty() ? nullptr : request.promptValue.c_str()));\n\n\t\t} else {\n\t\t\tresult.success = NameDialog::AskForName(\n\t\t\t\tthis, request.title.c_str(), request.prompt.c_str(), result.promptValue,\n\t\t\t\t(request.promptValue.empty() ? nullptr : request.promptValue.c_str()));\n\t\t}\n\n\t\tif (!result.success) {\n\t\t\tbreak;\n\t\t}\n\n\t\tif (result.promptValue.empty()) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!callback(result)) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\t\t\tcontinue;\n\t\t}\n\n\t\tbreak;\n\t}\n\n\treturn result;\n}\n\nvoid OBSBasic::on_actionOpenPluginManager_triggered()\n{\n\tApp()->pluginManagerOpenDialog();\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic.hpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"ui_OBSBasic.h\"\n#include \"OBSMainWindow.hpp\"\n\n#include <OBSApp.hpp>\n#include <components/AccessibleAlignmentSelector.hpp>\n#include <oauth/Auth.hpp>\n#include <utility/BasicOutputHandler.hpp>\n#include <utility/OBSCanvas.hpp>\n#include <utility/PreviewProgramSizeObserver.hpp>\n#include <utility/VCamConfig.hpp>\n#include <utility/platform.hpp>\n#include <utility/undo_stack.hpp>\n\n#include <obs-frontend-internal.hpp>\n#include <obs.hpp>\n#include <qt-wrappers.hpp>\n\n#include <graphics/matrix4.h>\n#include <util/platform.h>\n#include <util/threading.h>\n#include <util/util.hpp>\n\n#include <QAccessible>\n#include <QSystemTrayIcon>\n\n#include <deque>\n\nextern volatile bool recording_paused;\n\nclass ColorSelect;\nclass OBSAbout;\nclass OBSBasicAdvAudio;\nclass OBSBasicFilters;\nclass OBSBasicInteraction;\nclass OBSBasicProperties;\nclass OBSBasicTransform;\nclass OBSLogViewer;\nclass OBSMissingFiles;\nclass OBSProjector;\nclass VolumeControl;\n#ifdef YOUTUBE_ENABLED\nclass YouTubeAppDock;\n#endif\nclass QMessageBox;\nclass QWidgetAction;\nstruct QuickTransition;\n\nnamespace OBS {\nclass SceneCollection;\nstruct Rect;\nenum class LogFileType;\n} // namespace OBS\n\n#define SIMPLE_ENCODER_X264 \"x264\"\n#define SIMPLE_ENCODER_X264_LOWCPU \"x264_lowcpu\"\n#define SIMPLE_ENCODER_QSV \"qsv\"\n#define SIMPLE_ENCODER_QSV_AV1 \"qsv_av1\"\n#define SIMPLE_ENCODER_NVENC \"nvenc\"\n#define SIMPLE_ENCODER_NVENC_AV1 \"nvenc_av1\"\n#define SIMPLE_ENCODER_NVENC_HEVC \"nvenc_hevc\"\n#define SIMPLE_ENCODER_AMD \"amd\"\n#define SIMPLE_ENCODER_AMD_HEVC \"amd_hevc\"\n#define SIMPLE_ENCODER_AMD_AV1 \"amd_av1\"\n#define SIMPLE_ENCODER_APPLE_H264 \"apple_h264\"\n#define SIMPLE_ENCODER_APPLE_HEVC \"apple_hevc\"\n\n#define PREVIEW_EDGE_SIZE 10\n\nenum class ProjectorType;\n\nenum class QtDataRole {\n\tOBSRef = Qt::UserRole,\n\tOBSSignals,\n};\n\nstruct SavedProjectorInfo {\n\tProjectorType type;\n\tint monitor;\n\tstd::string geometry;\n\tstd::string name;\n\tbool alwaysOnTop;\n\tbool alwaysOnTopOverridden;\n};\n\nstruct SourceCopyInfo {\n\tOBSWeakSource weak_source;\n\tbool visible;\n\tobs_sceneitem_crop crop;\n\tobs_transform_info transform;\n\tobs_blending_method blend_method;\n\tobs_blending_type blend_mode;\n\tobs_scale_type scale_type;\n\tconst char *show_transition_id;\n\tconst char *hide_transition_id;\n\tOBSData show_transition_settings;\n\tOBSData hide_transition_settings;\n\tuint32_t show_transition_duration;\n\tuint32_t hide_transition_duration;\n\tOBSData private_settings;\n};\n\nstruct OBSProfile {\n\tstd::string name;\n\tstd::string directoryName;\n\tstd::filesystem::path path;\n\tstd::filesystem::path profileFile;\n};\n\nstruct OBSPromptResult {\n\tbool success;\n\tstd::string promptValue;\n\tbool optionValue;\n};\n\nstruct OBSPromptRequest {\n\tstd::string title;\n\tstd::string prompt;\n\tstd::string promptValue;\n\tbool withOption;\n\tstd::string optionPrompt;\n\tbool optionValue;\n};\n\nusing OBSPromptCallback = std::function<bool(const OBSPromptResult &result)>;\n\nusing OBSProfileCache = std::map<std::string, OBSProfile>;\nusing SceneCollection = OBS::SceneCollection;\nusing OBSSceneCollectionCache = std::unordered_map<std::string, SceneCollection>;\n\ntemplate<typename T> static T GetOBSRef(QListWidgetItem *item)\n{\n\treturn item->data(static_cast<int>(QtDataRole::OBSRef)).value<T>();\n}\n\ntemplate<typename T> static void SetOBSRef(QListWidgetItem *item, T &&val)\n{\n\titem->setData(static_cast<int>(QtDataRole::OBSRef), QVariant::fromValue(val));\n}\n\n#ifdef _WIN32\nstatic inline void UpdateProcessPriority()\n{\n\tconst char *priority = config_get_string(App()->GetAppConfig(), \"General\", \"ProcessPriority\");\n\tif (priority && strcmp(priority, \"Normal\") != 0)\n\t\tSetProcessPriority(priority);\n}\n\nstatic inline void ClearProcessPriority()\n{\n\tconst char *priority = config_get_string(App()->GetAppConfig(), \"General\", \"ProcessPriority\");\n\tif (priority && strcmp(priority, \"Normal\") != 0)\n\t\tSetProcessPriority(\"Normal\");\n}\n#else\n#define UpdateProcessPriority() \\\n\tdo {                    \\\n\t} while (false)\n#define ClearProcessPriority() \\\n\tdo {                   \\\n\t} while (false)\n#endif\n\nclass OBSBasic : public OBSMainWindow {\n\tQ_OBJECT\n\tQ_PROPERTY(QIcon imageIcon READ GetImageIcon WRITE SetImageIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon colorIcon READ GetColorIcon WRITE SetColorIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon slideshowIcon READ GetSlideshowIcon WRITE SetSlideshowIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon audioInputIcon READ GetAudioInputIcon WRITE SetAudioInputIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon audioOutputIcon READ GetAudioOutputIcon WRITE SetAudioOutputIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon desktopCapIcon READ GetDesktopCapIcon WRITE SetDesktopCapIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon windowCapIcon READ GetWindowCapIcon WRITE SetWindowCapIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon gameCapIcon READ GetGameCapIcon WRITE SetGameCapIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon cameraIcon READ GetCameraIcon WRITE SetCameraIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon textIcon READ GetTextIcon WRITE SetTextIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon mediaIcon READ GetMediaIcon WRITE SetMediaIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon browserIcon READ GetBrowserIcon WRITE SetBrowserIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon groupIcon READ GetGroupIcon WRITE SetGroupIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon sceneIcon READ GetSceneIcon WRITE SetSceneIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon defaultIcon READ GetDefaultIcon WRITE SetDefaultIcon DESIGNABLE true)\n\tQ_PROPERTY(QIcon audioProcessOutputIcon READ GetAudioProcessOutputIcon WRITE SetAudioProcessOutputIcon\n\t\t\t   DESIGNABLE true)\n\n\tfriend class OBSAbout;\n\tfriend class OBSBasicPreview;\n\tfriend class OBSBasicStatusBar;\n\tfriend class OBSBasicSourceSelect;\n\tfriend class OBSBasicSettings;\n\tfriend class Auth;\n\tfriend class AutoConfig;\n\tfriend class AutoConfigStreamPage;\n\tfriend class ExtraBrowsersModel;\n\tfriend class OBSYoutubeActions;\n\tfriend struct BasicOutputHandler;\n\tfriend struct OBSStudioAPI;\n\tfriend class ScreenshotObj;\n\n\tenum class MoveDir { Up, Down, Left, Right };\n\n\tenum DropType {\n\t\tDropType_RawText,\n\t\tDropType_Text,\n\t\tDropType_Image,\n\t\tDropType_Media,\n\t\tDropType_Html,\n\t\tDropType_Url,\n\t};\n\n\tenum ContextBarSize { ContextBarSize_Minimized, ContextBarSize_Reduced, ContextBarSize_Normal };\n\n\tenum class CenterType {\n\t\tScene,\n\t\tVertical,\n\t\tHorizontal,\n\t};\n\n\t/* -------------------------------------\n\t * MARK: - General\n\t * -------------------------------------\n\t */\nprivate:\n\tobs_frontend_callbacks *api = nullptr;\n\tstd::vector<OBSSignal> signalHandlers;\n\n\tbool loaded = false;\n\tbool isClosing_ = false;\n\tbool isClosePromptOpen_ = false;\n\tbool handledShutdown = false;\n\n\t// TODO: Remove, orphaned variable\n\tbool copyVisible = true;\n\t// TODO: Unused thread pointer, remove.\n\tQScopedPointer<QThread> devicePropertiesThread;\n\n\tQScopedPointer<QThread> logUploadThread;\n\n\tConfigFile activeConfiguration;\n\n\tQScopedPointer<QThread> patronJsonThread;\n\tstd::string patronJson;\n\n\tstd::unique_ptr<Ui::OBSBasic> ui;\n\n\tvoid OnEvent(enum obs_frontend_event event);\n\n\tbool InitBasicConfigDefaults();\n\tvoid InitBasicConfigDefaults2();\n\tbool InitBasicConfig();\n\n\tvoid InitOBSCallbacks();\n\n\tvoid OnFirstLoad();\n\n\tvoid GetFPSCommon(uint32_t &num, uint32_t &den) const;\n\tvoid GetFPSInteger(uint32_t &num, uint32_t &den) const;\n\tvoid GetFPSFraction(uint32_t &num, uint32_t &den) const;\n\tvoid GetFPSNanoseconds(uint32_t &num, uint32_t &den) const;\n\tvoid GetConfigFPS(uint32_t &num, uint32_t &den) const;\n\n\tOBSPromptResult PromptForName(const OBSPromptRequest &request, const OBSPromptCallback &callback);\n\n\t// TODO: Remove, orphaned instance method\n\tvoid NewProject();\n\t// TODO: Remove, orphaned instance method\n\tvoid LoadProject();\n\npublic slots:\n\tvoid close();\n\tvoid UpdatePatronJson(const QString &text, const QString &error);\n\tvoid UpdateEditMenu();\n\tvoid applicationShutdown() noexcept;\n\tvoid toggleMixerLayout();\n\npublic:\n\t/* `undo_s` needs to be declared after `ui` to prevent an uninitialized\n\t * warning for `ui` while initializing `undo_s`. */\n\tundo_stack undo_s;\n\n\texplicit OBSBasic(QWidget *parent = 0);\n\tvirtual ~OBSBasic();\n\n\tvirtual void OBSInit() override;\n\n\tvirtual config_t *Config() const override;\n\n\tint ResetVideo();\n\tbool ResetAudio();\n\n\tvoid UpdateTitleBar();\n\n\tstatic OBSBasic *Get();\n\n\tvoid SetDisplayAffinity(QWindow *window);\n\n\tvoid saveAll();\n\tbool shouldPromptForClose();\n\tinline bool isClosing() { return isClosing_; }\n\tinline bool isClosePromptOpen() { return isClosePromptOpen_; }\n\tvoid closeWindow();\n\nprotected:\n\tbool isReadyToClose();\n\tbool promptToClose();\n\n\tvirtual void closeEvent(QCloseEvent *event) override;\n\tvirtual bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;\n\tvirtual void changeEvent(QEvent *event) override;\n\nsignals:\n\tvoid mainWindowClosed();\n\n\tvoid appSettingChanged(const std::string &category, const std::string &name);\n\tvoid userSettingChanged(const std::string &category, const std::string &name);\n\tvoid profileSettingChanged(const std::string &category, const std::string &name);\n\n\tvoid mixerStatusChanged(QString sourceUuid);\n\n\t/* -------------------------------------\n\t * MARK: - OAuth\n\t * -------------------------------------\n\t */\nprivate:\n\tstd::shared_ptr<Auth> auth;\n\npublic:\n\tinline Auth *GetAuth() { return auth.get(); }\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Browser\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<QWidget> extraBrowsers;\n\n#ifdef BROWSER_AVAILABLE\n\tQPointer<QAction> extraBrowserMenuDocksSeparator;\n\n\tQList<std::shared_ptr<QDockWidget>> extraBrowserDocks;\n\tQStringList extraBrowserDockNames;\n\tQStringList extraBrowserDockTargets;\n\n\tvoid ClearExtraBrowserDocks();\n\tvoid LoadExtraBrowserDocks();\n\tvoid SaveExtraBrowserDocks();\n\tvoid ManageExtraBrowserDocks();\n\tvoid AddExtraBrowserDock(const QString &title, const QString &url, const QString &uuid, bool firstCreate);\n#endif\n\npublic:\n\tstatic void InitBrowserPanelSafeBlock();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Clipboard\n\t * -------------------------------------\n\t */\nprivate:\n\tstd::deque<SourceCopyInfo> clipboard;\n\tOBSWeakSourceAutoRelease copyFiltersSource_;\n\tobs_transform_info copiedTransformInfo;\n\tobs_sceneitem_crop copiedCropInfo;\n\tbool hasCopiedTransform = false;\n\tint copySourceTransitionDuration;\n\tOBSWeakSourceAutoRelease copySourceTransition;\n\nprivate slots:\n\tvoid on_actionCopySource_triggered();\n\tvoid on_actionPasteRef_triggered();\n\tvoid on_actionPasteDup_triggered();\n\n\tvoid on_actionCopyFilters_triggered();\n\tvoid on_actionPasteFilters_triggered();\n\tvoid SourcePasteFilters(OBSSource source, OBSSource dstSource);\n\n\tvoid SceneCopyFilters();\n\tvoid ScenePasteFilters();\n\n\tvoid on_actionCopyTransform_triggered();\n\tvoid on_actionPasteTransform_triggered();\n\npublic slots:\n\tvoid actionCopyFilters();\n\tvoid actionPasteFilters();\n\npublic:\n\tobs_weak_source_t *copyFiltersSource() { return copyFiltersSource_; }\n\tOBSWeakSource copyFilter;\n\tvoid CreateFilterPasteUndoRedoAction(const QString &text, obs_source_t *source, obs_data_array_t *undo_array,\n\t\t\t\t\t     obs_data_array_t *redo_array);\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_ContextToolbar\n\t * -------------------------------------\n\t */\nprivate:\n\tContextBarSize contextBarSize = ContextBarSize_Normal;\n\n\tvoid SourceToolBarActionsSetEnabled();\n\n\tvoid copyActionsDynamicProperties();\n\nprivate slots:\n\tvoid on_toggleContextBar_toggled(bool visible);\n\npublic slots:\n\tvoid ShowContextBar();\n\tvoid HideContextBar();\n\n\tvoid ClearContextBar();\n\tvoid UpdateContextBar(bool force = false);\n\tvoid UpdateContextBarDeferred(bool force = false);\n\tvoid UpdateContextBarVisibility();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Docks\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<QDockWidget> statsDock;\n\tQByteArray startingDockLayout;\n\tQStringList extraDockNames;\n\tQList<std::shared_ptr<QDockWidget>> extraDocks;\n\n\tQStringList extraCustomDockNames;\n\tQList<QPointer<QDockWidget>> extraCustomDocks;\n\n\tQPointer<OBSDock> controlsDock;\n\tQPointer<OBSDock> mixerDock;\n\npublic:\n\tvoid AddDockWidget(QDockWidget *dock, Qt::DockWidgetArea area, bool extraBrowser = false);\n\tvoid RemoveDockWidget(const QString &name);\n\tbool IsDockObjectNameUsed(const QString &name);\n\tvoid AddCustomDockWidget(QDockWidget *dock);\n\tvoid setDockCornersVertical(bool vertical);\n\nprivate slots:\n\tvoid on_resetDocks_triggered(bool force = false);\n\tvoid on_lockDocks_toggled(bool lock);\n\tvoid on_sideDocks_toggled(bool side);\n\n\tvoid RepairCustomExtraDockName();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Dropfiles\n\t * -------------------------------------\n\t */\nprivate:\n\tvoid AddDropSource(const char *file, DropType image);\n\tvoid AddDropURL(QUrl url, QString &name, obs_data_t *settings, const obs_video_info &ovi);\n\tvoid ConfirmDropUrl(const QString &url);\n\tvoid dragEnterEvent(QDragEnterEvent *event) override;\n\tvoid dragLeaveEvent(QDragLeaveEvent *event) override;\n\tvoid dragMoveEvent(QDragMoveEvent *event) override;\n\tvoid dropEvent(QDropEvent *event) override;\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Hotkeys\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<QObject> shortcutFilter;\n\tobs_hotkey_id statsHotkey = 0;\n\tobs_hotkey_id screenshotHotkey = 0;\n\tobs_hotkey_id sourceScreenshotHotkey = 0;\n\n\tobs_hotkey_pair_id streamingHotkeys, recordingHotkeys, pauseHotkeys, replayBufHotkeys, vcamHotkeys,\n\t\ttogglePreviewHotkeys, contextBarHotkeys;\n\tobs_hotkey_id forceStreamingStopHotkey, splitFileHotkey, addChapterHotkey;\n\n\tvoid InitHotkeys();\n\tvoid CreateHotkeys();\n\tvoid ClearHotkeys();\n\n\tstatic void HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed);\n\nprivate slots:\n\tvoid ProcessHotkey(obs_hotkey_id id, bool pressed);\n\tvoid ResetStatsHotkey();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Icons\n\t * -------------------------------------\n\t */\nprivate:\n\tQIcon imageIcon;\n\tQIcon colorIcon;\n\tQIcon slideshowIcon;\n\tQIcon audioInputIcon;\n\tQIcon audioOutputIcon;\n\tQIcon desktopCapIcon;\n\tQIcon windowCapIcon;\n\tQIcon gameCapIcon;\n\tQIcon cameraIcon;\n\tQIcon textIcon;\n\tQIcon mediaIcon;\n\tQIcon browserIcon;\n\tQIcon groupIcon;\n\tQIcon sceneIcon;\n\tQIcon defaultIcon;\n\tQIcon audioProcessOutputIcon;\n\n\tQIcon GetImageIcon() const;\n\tQIcon GetColorIcon() const;\n\tQIcon GetSlideshowIcon() const;\n\tQIcon GetAudioInputIcon() const;\n\tQIcon GetAudioOutputIcon() const;\n\tQIcon GetDesktopCapIcon() const;\n\tQIcon GetWindowCapIcon() const;\n\tQIcon GetGameCapIcon() const;\n\tQIcon GetCameraIcon() const;\n\tQIcon GetTextIcon() const;\n\tQIcon GetMediaIcon() const;\n\tQIcon GetBrowserIcon() const;\n\tQIcon GetDefaultIcon() const;\n\tQIcon GetAudioProcessOutputIcon() const;\n\nprivate slots:\n\tvoid SetImageIcon(const QIcon &icon);\n\tvoid SetColorIcon(const QIcon &icon);\n\tvoid SetSlideshowIcon(const QIcon &icon);\n\tvoid SetAudioInputIcon(const QIcon &icon);\n\tvoid SetAudioOutputIcon(const QIcon &icon);\n\tvoid SetDesktopCapIcon(const QIcon &icon);\n\tvoid SetWindowCapIcon(const QIcon &icon);\n\tvoid SetGameCapIcon(const QIcon &icon);\n\tvoid SetCameraIcon(const QIcon &icon);\n\tvoid SetTextIcon(const QIcon &icon);\n\tvoid SetMediaIcon(const QIcon &icon);\n\tvoid SetBrowserIcon(const QIcon &icon);\n\tvoid SetGroupIcon(const QIcon &icon);\n\tvoid SetSceneIcon(const QIcon &icon);\n\tvoid SetDefaultIcon(const QIcon &icon);\n\tvoid SetAudioProcessOutputIcon(const QIcon &icon);\n\npublic:\n\tQIcon GetSourceIcon(const char *id) const;\n\tQIcon GetGroupIcon() const;\n\tQIcon GetSceneIcon() const;\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_MainControls\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<OBSBasicInteraction> interaction;\n\tQPointer<OBSBasicProperties> properties;\n\tQPointer<OBSBasicTransform> transformWindow;\n\tQPointer<OBSBasicAdvAudio> advAudioWindow;\n\tQPointer<OBSBasicFilters> filters;\n\tQPointer<OBSAbout> about;\n\tQPointer<OBSLogViewer> logView;\n\tQPointer<QWidget> stats;\n\tQPointer<QWidget> remux;\n\tQPointer<QWidget> importer;\n\tQPointer<QAction> showHide;\n\tQPointer<QAction> exit;\n\n\tQPointer<QMenu> scaleFilteringMenu;\n\tQPointer<QMenu> blendingMethodMenu;\n\tQPointer<QMenu> blendingModeMenu;\n\tQPointer<QMenu> colorMenu;\n\tQPointer<QMenu> deinterlaceMenu;\n\n\tQPointer<QWidgetAction> colorWidgetAction;\n\tQPointer<ColorSelect> colorSelect;\n\n\tQList<QDialog *> visDialogs;\n\tQList<QDialog *> modalDialogs;\n\tQList<QMessageBox *> visMsgBoxes;\n\n\tQList<QPoint> visDlgPositions;\n\n\tvoid CloseDialogs();\n\tvoid EnumDialogs();\n\nprivate slots:\n\tvoid on_actionMainUndo_triggered();\n\tvoid on_actionMainRedo_triggered();\n\tvoid ToggleAlwaysOnTop();\n\n\tvoid SetShowing(bool showing);\n\n\tvoid ToggleShowHide();\n\n\tvoid on_actionShowAbout_triggered();\n\n\tvoid on_actionFullscreenInterface_triggered();\n\tvoid on_actionRemux_triggered();\n\tvoid on_action_Settings_triggered();\n\tvoid on_actionShowMacPermissions_triggered();\n\tvoid on_actionShowLogs_triggered();\n\tvoid on_actionUploadCurrentLog_triggered();\n\tvoid on_actionUploadLastLog_triggered();\n\tvoid on_actionViewCurrentLog_triggered();\n\tvoid on_actionCheckForUpdates_triggered();\n\tvoid on_actionRepair_triggered();\n\tvoid on_actionShowWhatsNew_triggered();\n\tvoid on_actionRestartSafe_triggered();\n\n\tvoid on_actionShowCrashLogs_triggered();\n\tvoid on_actionUploadLastCrashLog_triggered();\n\n\tvoid on_OBSBasic_customContextMenuRequested(const QPoint &pos);\n\n\tvoid on_actionHelpPortal_triggered();\n\tvoid on_actionWebsite_triggered();\n\tvoid on_actionDiscord_triggered();\n\tvoid on_actionReleaseNotes_triggered();\n\n\tvoid on_actionShowSettingsFolder_triggered();\n\tvoid on_actionShowProfileFolder_triggered();\n\n\tvoid on_actionAlwaysOnTop_triggered();\n\n\tvoid on_toggleListboxToolbars_toggled(bool visible);\n\tvoid on_toggleStatusBar_toggled(bool visible);\n\n\tvoid on_autoConfigure_triggered();\n\tvoid on_stats_triggered();\n\tvoid on_idianPlayground_triggered();\n\n\tvoid on_resetUI_triggered();\n\n\tvoid logUploadFinished(const QString &text, const QString &error, OBS::LogFileType uploadType);\n\n\tvoid updateCheckFinished();\n\npublic slots:\n\tvoid on_actionAdvAudioProperties_triggered();\n\npublic:\n\tvoid ResetUI();\n\n\tvoid CreateInteractionWindow(obs_source_t *source);\n\tvoid CreateFiltersWindow(obs_source_t *source);\n\tvoid CreateEditTransformWindow(obs_sceneitem_t *item);\n\tvoid CreatePropertiesWindow(obs_source_t *source);\n\n\tvoid UploadLog(const char *subdir, const char *file, OBS::LogFileType uploadType);\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_MainMenu\n\t * -------------------------------------\n\t */\nprivate:\n\tvoid setupMenuItemStateHandlers();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_OutputHandler\n\t * -------------------------------------\n\t */\nprivate:\n\tstd::unique_ptr<BasicOutputHandler> outputHandler;\n\tstd::optional<std::pair<uint32_t, uint32_t>> lastOutputResolution;\n\n\tint disableOutputsRef = 0;\n\n\tinline void OnActivate(bool force = false)\n\t{\n\t\tif (ui->profileMenu->isEnabled() || force) {\n\t\t\tui->profileMenu->setEnabled(false);\n\t\t\tui->autoConfigure->setEnabled(false);\n\t\t\tApp()->IncrementSleepInhibition();\n\t\t\tUpdateProcessPriority();\n\n\t\t\tstruct obs_video_info ovi;\n\t\t\tobs_get_video_info(&ovi);\n\t\t\tlastOutputResolution = {ovi.base_width, ovi.base_height};\n\n\t\t\tTaskbarOverlaySetStatus(TaskbarOverlayStatusActive);\n\t\t\tif (trayIcon && trayIcon->isVisible()) {\n#ifdef __APPLE__\n\t\t\t\tQIcon trayMask = QIcon(\":/res/images/tray_active_macos.svg\");\n\t\t\t\ttrayMask.setIsMask(true);\n\t\t\t\ttrayIcon->setIcon(QIcon::fromTheme(\"obs-tray\", trayMask));\n#else\n\t\t\t\ttrayIcon->setIcon(\n\t\t\t\t\tQIcon::fromTheme(\"obs-tray-active\", QIcon(\":/res/images/tray_active.png\")));\n#endif\n\t\t\t}\n\t\t}\n\t}\n\n\tinline void OnDeactivate()\n\t{\n\t\tif (!outputHandler->Active() && !ui->profileMenu->isEnabled()) {\n\t\t\tui->profileMenu->setEnabled(true);\n\t\t\tui->autoConfigure->setEnabled(true);\n\t\t\tApp()->DecrementSleepInhibition();\n\t\t\tClearProcessPriority();\n\n\t\t\tTaskbarOverlaySetStatus(TaskbarOverlayStatusInactive);\n\t\t\tif (trayIcon && trayIcon->isVisible()) {\n#ifdef __APPLE__\n\t\t\t\tQIcon trayIconFile = QIcon(\":/res/images/obs_macos.svg\");\n\t\t\t\ttrayIconFile.setIsMask(true);\n#else\n\t\t\t\tQIcon trayIconFile = QIcon(\":/res/images/obs.png\");\n#endif\n\t\t\t\ttrayIcon->setIcon(QIcon::fromTheme(\"obs-tray\", trayIconFile));\n\t\t\t}\n\t\t} else if (outputHandler->Active() && trayIcon && trayIcon->isVisible()) {\n\t\t\tif (os_atomic_load_bool(&recording_paused)) {\n#ifdef __APPLE__\n\t\t\t\tQIcon trayIconFile = QIcon(\":/res/images/obs_paused_macos.svg\");\n\t\t\t\ttrayIconFile.setIsMask(true);\n#else\n\t\t\t\tQIcon trayIconFile = QIcon(\":/res/images/obs_paused.png\");\n#endif\n\t\t\t\ttrayIcon->setIcon(QIcon::fromTheme(\"obs-tray-paused\", trayIconFile));\n\t\t\t\tTaskbarOverlaySetStatus(TaskbarOverlayStatusPaused);\n\t\t\t} else {\n#ifdef __APPLE__\n\t\t\t\tQIcon trayIconFile = QIcon(\":/res/images/tray_active_macos.svg\");\n\t\t\t\ttrayIconFile.setIsMask(true);\n#else\n\t\t\t\tQIcon trayIconFile = QIcon(\":/res/images/tray_active.png\");\n#endif\n\t\t\t\ttrayIcon->setIcon(QIcon::fromTheme(\"obs-tray-active\", trayIconFile));\n\t\t\t\tTaskbarOverlaySetStatus(TaskbarOverlayStatusActive);\n\t\t\t}\n\t\t}\n\t}\n\n\tbool IsFFmpegOutputToURL() const;\n\tbool OutputPathValid();\n\tvoid OutputPathInvalidMessage();\n\n\t// TODO: Unimplemented, remove.\n\tvoid SetupEncoders();\n\t// TODO: Unimplemented, remove.\n\tvoid TempFileOutput(const char *path, int vBitrate, int aBitrate);\n\t// TODO: Unimplemented, remove.\n\tvoid TempStreamOutput(const char *url, const char *key, int vBitrate, int aBitrate);\n\npublic:\n\tbool Active() const;\n\tvoid ResetOutputs();\n\n\tinline void EnableOutputs(bool enable)\n\t{\n\t\tif (enable) {\n\t\t\tif (--disableOutputsRef < 0)\n\t\t\t\tdisableOutputsRef = 0;\n\t\t} else {\n\t\t\tdisableOutputsRef++;\n\t\t}\n\t}\n\n\tconst char *GetCurrentOutputPath();\n\nprivate slots:\n\tvoid ResizeOutputSizeOfSource();\n\nprivate slots:\n\tvoid on_actionOpenPluginManager_triggered();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Preview\n\t * -------------------------------------\n\t */\nprivate:\n\tbool previewEnabled = true;\n\tQPointer<QTimer> nudge_timer;\n\tbool recent_nudge = false;\n\n\tgs_vertbuffer_t *box = nullptr;\n\tgs_vertbuffer_t *boxLeft = nullptr;\n\tgs_vertbuffer_t *boxTop = nullptr;\n\tgs_vertbuffer_t *boxRight = nullptr;\n\tgs_vertbuffer_t *boxBottom = nullptr;\n\tgs_vertbuffer_t *circle = nullptr;\n\n\tgs_vertbuffer_t *actionSafeMargin = nullptr;\n\tgs_vertbuffer_t *graphicsSafeMargin = nullptr;\n\tgs_vertbuffer_t *fourByThreeSafeMargin = nullptr;\n\tgs_vertbuffer_t *leftLine = nullptr;\n\tgs_vertbuffer_t *topLine = nullptr;\n\tgs_vertbuffer_t *rightLine = nullptr;\n\n\tint previewX = 0, previewY = 0;\n\tint previewCX = 0, previewCY = 0;\n\tfloat previewScale = 0.0f;\n\tbool drawSafeAreas = false;\n\n\tQColor selectionColor;\n\tQColor cropColor;\n\tQColor hoverColor;\n\n\tbool drawSpacingHelpers = true;\n\n\tfloat dpi = 1.0;\n\n\tvoid DrawBackdrop(float cx, float cy);\n\tvoid InitPrimitives();\n\tvoid UpdatePreviewScalingMenu();\n\n\tvoid Nudge(int dist, MoveDir dir);\n\n\tvoid UpdateProjectorHideCursor();\n\tvoid UpdateProjectorAlwaysOnTop(bool top);\n\tvoid ResetProjectors();\n\n\tvoid UpdatePreviewSafeAreas();\n\n\tQColor GetCropColor() const;\n\tQColor GetHoverColor() const;\n\n\tvoid UpdatePreviewSpacingHelpers();\n\n\tfloat GetDevicePixelRatio();\n\n\tvoid UpdatePreviewOverflowSettings();\n\tvoid UpdatePreviewControls();\n\n\t/* OBS Callbacks */\n\tstatic void RenderMain(void *data, uint32_t cx, uint32_t cy);\n\n\tvoid ResizePreview(uint32_t cx, uint32_t cy);\n\nprivate slots:\n\tvoid PreviewScalingModeChanged(int value);\n\n\tvoid ColorChange();\n\n\tvoid EnablePreview();\n\tvoid DisablePreview();\n\n\tvoid setPreviewScalingWindow();\n\tvoid setPreviewScalingCanvas();\n\tvoid setPreviewScalingOutput();\n\n\tvoid on_actionLockPreview_triggered();\n\n\tvoid on_scalingMenu_aboutToShow();\n\n\tvoid on_preview_customContextMenuRequested();\n\tvoid on_previewDisabledWidget_customContextMenuRequested();\n\n\tvoid EnablePreviewDisplay(bool enable);\n\tvoid TogglePreview();\n\npublic:\n\tinline void GetDisplayRect(int &x, int &y, int &cx, int &cy)\n\t{\n\t\tx = previewX;\n\t\ty = previewY;\n\t\tcx = previewCX;\n\t\tcy = previewCY;\n\t}\n\n\tQColor GetSelectionColor() const;\n\nsignals:\n\tvoid CanvasResized(uint32_t width, uint32_t height);\n\tvoid OutputResized(uint32_t width, uint32_t height);\n\n\t/* Preview signals */\n\tvoid PreviewZoomIn();\n\tvoid PreviewZoomOut();\n\tvoid PreviewResetZoom();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Profiles\n\t * -------------------------------------\n\t */\nprivate:\n\tOBSProfileCache profiles{};\n\n\tvoid SetupNewProfile(const std::string &profileName, bool useWizard = false);\n\tvoid SetupDuplicateProfile(const std::string &profileName);\n\tvoid SetupRenameProfile(const std::string &profileName);\n\n\tconst OBSProfile &CreateProfile(const std::string &profileName);\n\tvoid RemoveProfile(OBSProfile profile);\n\n\tvoid ChangeProfile();\n\n\tvoid RefreshProfileCache();\n\n\tvoid RefreshProfiles(bool refreshCache = false);\n\n\tvoid ActivateProfile(const OBSProfile &profile, bool reset = false);\n\tvoid UpdateProfileEncoders();\n\tstd::vector<std::string> GetRestartRequirements(const ConfigFile &config) const;\n\tvoid ResetProfileData();\n\tvoid CheckForSimpleModeX264Fallback();\n\npublic:\n\tinline const OBSProfileCache &GetProfileCache() const noexcept { return profiles; };\n\n\tconst OBSProfile &GetCurrentProfile() const;\n\n\tstd::optional<OBSProfile> GetProfileByName(const std::string &profileName) const;\n\tstd::optional<OBSProfile> GetProfileByDirectoryName(const std::string &directoryName) const;\n\nprivate slots:\n\tvoid on_actionNewProfile_triggered();\n\tvoid on_actionDupProfile_triggered();\n\tvoid on_actionRenameProfile_triggered();\n\tvoid on_actionRemoveProfile_triggered(bool skipConfirmation = false);\n\tvoid on_actionImportProfile_triggered();\n\tvoid on_actionExportProfile_triggered();\n\npublic slots:\n\tbool CreateNewProfile(const QString &name);\n\tbool CreateDuplicateProfile(const QString &name);\n\tvoid DeleteProfile(const QString &profileName);\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Projectors\n\t * -------------------------------------\n\t */\nprivate:\n\tstd::vector<OBSProjector *> projectors;\n\tQPointer<QMenu> previewProjector;\n\tQPointer<QMenu> previewProjectorSource;\n\tQPointer<QMenu> previewProjectorMain;\n\n\tvoid updateMultiviewProjectorMenu();\n\tvoid ClearProjectors();\n\tOBSProjector *OpenProjector(obs_source_t *source, int monitor, ProjectorType type);\n\n\tobs_data_array_t *SaveProjectors();\n\tvoid LoadSavedProjectors(obs_data_array_t *savedProjectors);\n\nprivate slots:\n\tvoid OpenSavedProjector(SavedProjectorInfo *info);\n\n\tvoid OpenPreviewProjector();\n\tvoid OpenSourceProjector();\n\tvoid OpenMultiviewProjector();\n\tvoid OpenSceneProjector();\n\n\tvoid OpenPreviewWindow();\n\tvoid OpenSourceWindow();\n\tvoid OpenSceneWindow();\n\tvoid openMultiviewWindow();\n\npublic:\n\tvoid DeleteProjector(OBSProjector *projector);\n\n\tstatic QList<QString> GetProjectorMenuMonitorsFormatted();\n\ttemplate<typename Receiver, typename... Args>\n\tstatic void AddProjectorMenuMonitors(QMenu *parent, Receiver *target, void (Receiver::*slot)(Args...))\n\t{\n\t\tauto projectors = GetProjectorMenuMonitorsFormatted();\n\t\tfor (int i = 0; i < projectors.size(); i++) {\n\t\t\tQString str = projectors[i];\n\t\t\tQAction *action = parent->addAction(str, target, slot);\n\t\t\taction->setProperty(\"monitor\", i);\n\t\t}\n\t}\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Recording\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<QTimer> diskFullTimer;\n\tbool recordingStopping = false;\n\tbool recordingStarted = false;\n\tbool isRecordingPausable = false;\n\tbool recordingPaused = false;\n\n\tvoid AutoRemux(QString input, bool no_show = false);\n\tvoid UpdateIsRecordingPausable();\n\n\tbool LowDiskSpace();\n\tvoid DiskSpaceMessage();\n\nprivate slots:\n\tvoid on_actionShow_Recordings_triggered();\n\n\t/* Record action (start/stop) slot */\n\tvoid RecordActionTriggered();\n\n\t/* Record pause (pause/unpause) slot */\n\tvoid RecordPauseToggled();\n\npublic slots:\n\tvoid StartRecording();\n\tvoid StopRecording();\n\n\tvoid RecordingStart();\n\tvoid RecordStopping();\n\tvoid RecordingStop(int code, QString last_error);\n\tvoid RecordingFileChanged(QString lastRecordingPath);\n\n\tvoid PauseRecording();\n\tvoid UnpauseRecording();\n\n\tvoid CheckDiskSpaceRemaining();\n\n\tbool RecordingActive();\n\nsignals:\n\t/* Recording signals */\n\tvoid RecordingStarted(bool pausable = false);\n\tvoid RecordingPaused();\n\tvoid RecordingUnpaused();\n\tvoid RecordingStopping();\n\tvoid RecordingStopped();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_ReplayBuffer\n\t * -------------------------------------\n\t */\nprivate:\n\tbool replayBufferStopping = false;\n\tstd::string lastReplay;\n\npublic slots:\n\tvoid ShowReplayBufferPauseWarning();\n\tvoid StartReplayBuffer();\n\tvoid StopReplayBuffer();\n\n\tvoid ReplayBufferStart();\n\tvoid ReplayBufferSave();\n\tvoid ReplayBufferSaved();\n\tvoid ReplayBufferStopping();\n\tvoid ReplayBufferStop(int code);\n\n\tbool ReplayBufferActive();\n\nprivate slots:\n\t/* Replay Buffer action (start/stop) slot */\n\tvoid ReplayBufferActionTriggered();\n\nsignals:\n\t/* Replay Buffer signals */\n\tvoid ReplayBufEnabled(bool enabled);\n\tvoid ReplayBufStarted();\n\tvoid ReplayBufStopping();\n\tvoid ReplayBufStopped();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_SceneCollections\n\t * -------------------------------------\n\t */\nprivate:\n\tOBSDataAutoRelease collectionModuleData;\n\tlong disableSaving = 1;\n\tbool projectChanged = false;\n\tbool clearingFailed = false;\n\n\tQPointer<OBSMissingFiles> missDialog;\n\n\tOBSSceneCollectionCache collections;\n\n\tvoid DisableRelativeCoordinates(bool disable);\n\tvoid CreateDefaultScene(bool firstStart);\n\tvoid Save(SceneCollection &collection);\n\tvoid LoadData(obs_data_t *data, SceneCollection &collection);\n\tvoid Load(SceneCollection &collection);\n\n\tvoid ClearSceneData();\n\tvoid LogScenes();\n\tvoid SaveProjectNow();\n\tvoid ShowMissingFilesDialog(obs_missing_files_t *files);\n\n\tvoid SetupNewSceneCollection(const std::string &collectionName);\n\tvoid SetupDuplicateSceneCollection(const std::string &collectionName);\n\tvoid SetupRenameSceneCollection(const std::string &collectionName);\n\n\tSceneCollection &CreateSceneCollection(const std::string &collectionName);\n\tvoid RemoveSceneCollection(SceneCollection collection);\n\n\tbool CreateDuplicateSceneCollection(const QString &name);\n\tvoid DeleteSceneCollection(const QString &name);\n\tvoid ChangeSceneCollection();\n\n\tvoid RefreshSceneCollectionCache();\n\n\tvoid RefreshSceneCollections(bool refreshCache = false);\n\tvoid ActivateSceneCollection(SceneCollection &collection);\n\npublic slots:\n\tvoid DeferSaveBegin();\n\tvoid DeferSaveEnd();\n\n\tvoid SaveProjectDeferred();\n\tvoid SaveProject();\n\n\tbool CreateNewSceneCollection(const QString &name);\n\nprivate slots:\n\tvoid on_actionShowMissingFiles_triggered();\n\n\tvoid on_actionNewSceneCollection_triggered();\n\tvoid on_actionDupSceneCollection_triggered();\n\tvoid on_actionRenameSceneCollection_triggered();\n\tvoid on_actionRemoveSceneCollection_triggered(bool skipConfirmation = false);\n\tvoid on_actionImportSceneCollection_triggered();\n\tvoid on_actionExportSceneCollection_triggered();\n\tvoid on_actionRemigrateSceneCollection_triggered();\n\npublic:\n\tinline bool SavingDisabled() const { return disableSaving; }\n\n\tinline const OBSSceneCollectionCache &GetSceneCollectionCache() const noexcept { return collections; };\n\n\tSceneCollection &GetCurrentSceneCollection();\n\n\tstd::optional<SceneCollection> GetSceneCollectionByName(const std::string &collectionName) const;\n\tstd::optional<SceneCollection> GetSceneCollectionByFileName(const std::string &fileName) const;\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Canvases\n\t * -------------------------------------\n\t */\nprivate:\n\tstd::vector<OBS::Canvas> canvases;\n\n\tstatic void CanvasRemoved(void *data, calldata_t *params);\n\tvoid ClearCanvases();\n\npublic:\n\tconst std::vector<OBS::Canvas> &GetCanvases() const noexcept { return canvases; }\n\n\tconst OBS::Canvas &AddCanvas(const std::string &name, obs_video_info *ovi = nullptr, int flags = 0);\n\npublic slots:\n\tbool RemoveCanvas(OBSCanvas canvas);\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_SceneItems\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<QMenu> sourceProjector;\n\tQPointer<QAction> renameSource;\n\n\tvoid CreateFirstRunSources();\n\n\tOBSSceneItem GetSceneItem(QListWidgetItem *item);\n\tOBSSceneItem GetCurrentSceneItem();\n\n\tbool QueryRemoveSource(obs_source_t *source);\n\tint GetTopSelectedSourceItem();\n\n\tQModelIndexList GetAllSelectedSourceItems();\n\n\t// TODO: Move back to transitions\n\tQMenu *CreateVisibilityTransitionMenu(bool visible);\n\tvoid CenterSelectedSceneItems(const CenterType &centerType);\n\n\t/* OBS Callbacks */\n\tstatic void SourceCreated(void *data, calldata_t *params);\n\tstatic void SourceRemoved(void *data, calldata_t *params);\n\tstatic void SourceRenamed(void *data, calldata_t *params);\n\n\tvoid AddSource(const char *id);\n\tQMenu *CreateAddSourcePopupMenu();\n\tvoid AddSourcePopupMenu(const QPoint &pos);\n\nprivate slots:\n\tvoid RenameSources(OBSSource source, QString newName, QString prevName);\n\n\tvoid ReorderSources(OBSScene scene);\n\tvoid RefreshSources(OBSScene scene);\n\n\tvoid SetDeinterlacingMode();\n\tvoid SetDeinterlacingOrder();\n\n\tvoid SetScaleFilter();\n\n\tvoid SetBlendingMethod();\n\tvoid SetBlendingMode();\n\n\tvoid on_actionRotate90CW_triggered();\n\tvoid on_actionRotate90CCW_triggered();\n\tvoid on_actionRotate180_triggered();\n\tvoid on_actionFlipHorizontal_triggered();\n\tvoid on_actionFlipVertical_triggered();\n\tvoid on_actionFitToScreen_triggered();\n\tvoid on_actionStretchToScreen_triggered();\n\tvoid on_actionCenterToScreen_triggered();\n\tvoid on_actionVerticalCenter_triggered();\n\tvoid on_actionHorizontalCenter_triggered();\n\n\tvoid on_actionEditTransform_triggered();\n\n\tvoid on_sources_customContextMenuRequested(const QPoint &pos);\n\n\t// Source Context Buttons\n\tvoid on_sourcePropertiesButton_clicked();\n\tvoid on_sourceFiltersButton_clicked();\n\tvoid on_sourceInteractButton_clicked();\n\n\tvoid on_actionAddSource_triggered();\n\tvoid on_actionRemoveSource_triggered();\n\tvoid on_actionInteract_triggered();\n\tvoid on_actionSourceProperties_triggered();\n\tvoid on_actionSourceUp_triggered();\n\tvoid on_actionSourceDown_triggered();\n\n\tvoid on_actionMoveUp_triggered();\n\tvoid on_actionMoveDown_triggered();\n\tvoid on_actionMoveToTop_triggered();\n\tvoid on_actionMoveToBottom_triggered();\n\n\tvoid on_toggleSourceIcons_toggled(bool visible);\n\n\tvoid OpenFilters(OBSSource source = nullptr);\n\tvoid OpenProperties(OBSSource source = nullptr);\n\tvoid OpenInteraction(OBSSource source = nullptr);\n\tvoid OpenEditTransform(OBSSceneItem item = nullptr);\n\npublic:\n\tvoid ResetAudioDevice(const char *sourceId, const char *deviceId, const char *deviceDesc, int channel);\n\n\tQMenu *AddDeinterlacingMenu(QMenu *menu, obs_source_t *source);\n\tQMenu *AddScaleFilteringMenu(QMenu *menu, obs_sceneitem_t *item);\n\tQMenu *AddBlendingMethodMenu(QMenu *menu, obs_sceneitem_t *item);\n\tQMenu *AddBlendingModeMenu(QMenu *menu, obs_sceneitem_t *item);\n\tQMenu *AddBackgroundColorMenu(QMenu *menu, QWidgetAction *widgetAction, ColorSelect *select,\n\t\t\t\t      obs_sceneitem_t *item);\n\tvoid CreateSourcePopupMenu(int idx, bool preview);\n\n\tvoid actionOpenSourceFilters();\n\tvoid actionOpenSourceProperties();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Scenes\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<QMenu> sceneProjectorMenu;\n\tQPointer<QAction> renameScene;\n\tstd::atomic<obs_scene_t *> currentScene = nullptr;\n\tOBSWeakSource lastScene;\n\tOBSWeakSource swapScene;\n\n\tvoid LoadSceneListOrder(obs_data_array_t *array);\n\tobs_data_array_t *SaveSceneListOrder();\n\tvoid ChangeSceneIndex(bool relative, int idx, int invalidIdx);\n\n\tvoid MoveSceneItem(enum obs_order_movement movement, const QString &action_name);\n\n\t/* OBS Callbacks */\n\tstatic void SceneReordered(void *data, calldata_t *params);\n\tstatic void SceneRefreshed(void *data, calldata_t *params);\n\tstatic void SceneItemAdded(void *data, calldata_t *params);\n\npublic slots:\n\tvoid on_actionResetTransform_triggered();\n\nprivate slots:\n\tvoid AddSceneItem(OBSSceneItem item);\n\tvoid AddScene(OBSSource source);\n\tvoid RemoveScene(OBSSource source);\n\n\tvoid DuplicateSelectedScene();\n\tvoid RemoveSelectedScene();\n\n\tSourceTreeItem *GetItemWidgetFromSceneItem(obs_sceneitem_t *sceneItem);\n\n\tvoid on_actionSceneFilters_triggered();\n\n\tvoid on_scenes_currentItemChanged(QListWidgetItem *current, QListWidgetItem *prev);\n\tvoid on_scenes_customContextMenuRequested(const QPoint &pos);\n\n\tvoid GridActionClicked();\n\tvoid on_actionSceneListMode_triggered();\n\tvoid on_actionSceneGridMode_triggered();\n\tvoid on_actionAddScene_triggered();\n\tvoid on_actionRemoveScene_triggered();\n\tvoid on_actionSceneUp_triggered();\n\tvoid on_actionSceneDown_triggered();\n\tvoid on_scenes_itemDoubleClicked(QListWidgetItem *item);\n\n\tvoid MoveSceneToTop();\n\tvoid MoveSceneToBottom();\n\n\tvoid EditSceneName();\n\tvoid EditSceneItemName();\n\n\tvoid SceneNameEdited(QWidget *editor);\n\tvoid OpenSceneFilters();\n\npublic:\n\tstatic OBSData BackupScene(obs_scene_t *scene, std::vector<obs_source_t *> *sources = nullptr);\n\tstatic inline OBSData BackupScene(obs_source_t *scene_source, std::vector<obs_source_t *> *sources = nullptr)\n\t{\n\t\tobs_scene_t *scene = obs_scene_from_source(scene_source);\n\t\treturn BackupScene(scene, sources);\n\t}\n\n\tOBSScene GetCurrentScene();\n\n\tinline OBSSource GetCurrentSceneSource()\n\t{\n\t\tOBSScene curScene = GetCurrentScene();\n\t\treturn OBSSource(obs_scene_get_source(curScene));\n\t}\n\n\tvoid CreateSceneUndoRedoAction(const QString &action_name, OBSData undo_data, OBSData redo_data);\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Screenshots\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<QObject> screenshotData;\n\tstd::string lastScreenshot;\n\nprivate slots:\n\tvoid Screenshot(OBSSource source_ = nullptr);\n\tvoid ScreenshotSelectedSource();\n\tvoid ScreenshotProgram();\n\tvoid ScreenshotScene();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Service\n\t * -------------------------------------\n\t */\nprivate:\n\tOBSService service;\n\n\tbool InitService();\n\npublic:\n\tobs_service_t *GetService();\n\tvoid SetService(obs_service_t *service);\n\n\tvoid SaveService();\n\tbool LoadService();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_StatusBar\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<QTimer> cpuUsageTimer;\n\tos_cpu_usage_info_t *cpuUsageInfo = nullptr;\n\npublic:\n\tinline double GetCPUUsage() const { return os_cpu_usage_info_query(cpuUsageInfo); }\n\tvoid ShowStatusBarMessage(const QString &message);\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Streaming\n\t * -------------------------------------\n\t */\nprivate:\n\tstd::shared_future<void> setupStreamingGuard;\n\tbool streamingStopping = false;\n\tbool streamingStarting = false;\n\npublic slots:\n\tvoid DisplayStreamStartError();\n\tvoid StartStreaming();\n\tvoid StopStreaming();\n\tvoid ForceStopStreaming();\n\n\tvoid StreamDelayStarting(int sec);\n\tvoid StreamDelayStopping(int sec);\n\n\tvoid StreamingStart();\n\tvoid StreamStopping();\n\tvoid StreamingStop(int errorcode, QString last_error);\n\n\tbool StreamingActive();\n\nprivate slots:\n\t/* Stream action (start/stop) slot */\n\tvoid StreamActionTriggered();\n\nsignals:\n\t/* Streaming signals */\n\tvoid StreamingPreparing();\n\tvoid StreamingStarting(bool broadcastAutoStart);\n\tvoid StreamingStarted(bool withDelay = false);\n\tvoid StreamingStopping();\n\tvoid StreamingStopped(bool withDelay = false);\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_StudioMode\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<QMenu> studioProgramProjector;\n\tQPointer<QWidget> programWidget;\n\tQPointer<QVBoxLayout> programLayout;\n\tQPointer<QLabel> programLabel;\n\tQPointer<QWidget> programOptions;\n\tQPointer<OBSQTDisplay> program;\n\tOBSWeakSource lastProgramScene;\n\n\tbool editPropertiesMode = false;\n\tbool sceneDuplicationMode = true;\n\n\tOBSWeakSource programScene;\n\tvolatile bool previewProgramMode = false;\n\n\tobs_hotkey_pair_id togglePreviewProgramHotkeys = 0;\n\n\tint programX = 0, programY = 0;\n\tint programCX = 0, programCY = 0;\n\tfloat programScale = 0.0f;\n\tQPointer<PreviewProgramSizeObserver> sizeObserver;\n\n\tvoid CreateProgramDisplay();\n\tvoid CreateProgramOptions();\n\tvoid SetPreviewProgramMode(bool enabled);\n\tvoid ResizeProgram(uint32_t cx, uint32_t cy);\n\tstatic void RenderProgram(void *data, uint32_t cx, uint32_t cy);\n\n\tvoid UpdatePreviewProgramIndicators();\n\nprivate slots:\n\tvoid EnablePreviewProgram();\n\tvoid DisablePreviewProgram();\n\n\tvoid ProgramViewContextMenuRequested();\n\n\tvoid OpenStudioProgramProjector();\n\tvoid OpenStudioProgramWindow();\n\n\t/* Studio Mode toggle slot */\n\tvoid TogglePreviewProgramMode();\n\npublic:\n\tOBSSource GetProgramSource();\n\n\tinline bool IsPreviewProgramMode() const { return os_atomic_load_bool(&previewProgramMode); }\n\nsignals:\n\t/* Studio Mode signal */\n\tvoid PreviewProgramModeChanged(bool enabled);\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_SysTray\n\t * -------------------------------------\n\t */\nprivate:\n\tQPointer<QSystemTrayIcon> trayIcon;\n\tQPointer<QAction> sysTrayStream;\n\tQPointer<QAction> sysTrayRecord;\n\tQPointer<QAction> sysTrayReplayBuffer;\n\tQPointer<QAction> sysTrayVirtualCam;\n\tQPointer<QMenu> trayMenu;\n\n\tbool sysTrayMinimizeToTray();\n\tvoid updateSysTrayProjectorMenu();\n\nprivate slots:\n\tvoid IconActivated(QSystemTrayIcon::ActivationReason reason);\n\npublic:\n\tvoid SysTrayNotify(const QString &text, QSystemTrayIcon::MessageIcon n);\n\n\tvoid SystemTrayInit();\n\tvoid SystemTray(bool firstStarted);\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Transitions\n\t * -------------------------------------\n\t */\nprivate:\n\tstd::vector<OBSDataAutoRelease> safeModeTransitions;\n\tQPointer<QPushButton> transitionButton;\n\tQPointer<QMenu> perSceneTransitionMenu;\n\n\tstd::unordered_map<std::string, OBSSource> transitions;\n\t// FIXME: Any code accessing this collection relies on order of insertion\n\tstd::vector<std::string> transitionUuids;\n\t// FIXME: Replace usages of a name to identify a transition\n\tstd::unordered_map<std::string, std::string> transitionNameToUuids;\n\tint transitionDuration;\n\tstd::string currentTransitionUuid;\n\tobs_source_t *fadeTransition;\n\tobs_source_t *cutTransition;\n\tstd::vector<QuickTransition> quickTransitions;\n\tbool swapScenesMode = true;\n\n\tobs_hotkey_id transitionHotkey = 0;\n\n\tint quickTransitionIdCounter = 1;\n\tbool overridingTransition = false;\n\n\tQSlider *tBar;\n\tbool tBarActive = false;\n\n\tOBSSource prevFTBSource = nullptr;\n\n\tvoid InitDefaultTransitions();\n\tvoid InitTransition(obs_source_t *transition);\n\tobs_source_t *FindTransition(const char *name);\n\tOBSSource GetCurrentTransition();\n\tobs_data_array_t *SaveTransitions();\n\tvoid LoadTransitions(obs_data_array_t *transitions, obs_load_source_cb cb, void *private_data);\n\n\tvoid AddQuickTransitionId(int id);\n\tvoid AddQuickTransition();\n\tvoid AddQuickTransitionHotkey(QuickTransition *qt);\n\tvoid RemoveQuickTransitionHotkey(QuickTransition *qt);\n\tvoid LoadQuickTransitions(obs_data_array_t *array);\n\tobs_data_array_t *SaveQuickTransitions();\n\tvoid ClearQuickTransitionWidgets();\n\tvoid RefreshQuickTransitions();\n\t// TODO: Remove orphaned method.\n\tvoid DisableQuickTransitionWidgets();\n\tvoid EnableTransitionWidgets(bool enable);\n\tvoid CreateDefaultQuickTransitions();\n\n\tQuickTransition *GetQuickTransition(int id);\n\tint GetQuickTransitionIdx(int id);\n\tQMenu *CreateTransitionMenu(QWidget *parent, QuickTransition *qt);\n\tvoid ClearQuickTransitions();\n\tvoid QuickTransitionClicked();\n\tvoid QuickTransitionChange();\n\tvoid QuickTransitionChangeDuration(int value);\n\tvoid QuickTransitionRemoveClicked();\n\n\tOBSSource GetOverrideTransition(OBSSource source);\n\tint GetOverrideTransitionDuration(OBSSource source);\n\n\tQMenu *CreatePerSceneTransitionMenu();\n\n\tvoid SetCurrentScene(obs_scene_t *scene, bool force = false);\n\n\tvoid PasteShowHideTransition(obs_sceneitem_t *item, bool show, obs_source_t *tr, int duration);\n\n\tvoid UpdateCurrentTransition(const std::string &uuid, bool setTransition);\n\npublic slots:\n\tvoid SetCurrentScene(OBSSource scene, bool force = false);\n\n\tvoid SetTransition(OBSSource transition);\n\tvoid OverrideTransition(OBSSource transition);\n\tvoid TransitionToScene(OBSScene scene, bool force = false);\n\tvoid TransitionToScene(OBSSource scene, bool force = false, bool quickTransition = false, int quickDuration = 0,\n\t\t\t       bool black = false, bool manual = false);\n\n\tvoid SetCurrentTransition(const QString &uuid);\n\n\tvoid SetTransitionDuration(int duration);\n\nprivate slots:\n\tvoid AddTransition(const char *id);\n\tvoid RenameTransition(OBSSource transition);\n\n\tvoid TransitionClicked();\n\tvoid TransitionStarted();\n\tvoid TransitionStopped();\n\tvoid TransitionFullyStopped();\n\tvoid TriggerQuickTransition(int id);\n\n\tvoid TBarChanged(int value);\n\tvoid TBarReleased();\n\n\tvoid on_transitionAdd_clicked();\n\tvoid on_transitionRemove_clicked();\n\tvoid on_transitionProps_clicked();\n\n\tvoid ShowTransitionProperties();\n\tvoid HideTransitionProperties();\n\nsignals:\n\tvoid TransitionAdded(const QString &name, const QString &uuid);\n\tvoid TransitionRenamed(const QString &uuid, const QString &newName);\n\tvoid TransitionRemoved(const QString &uuid);\n\tvoid TransitionsCleared();\n\n\tvoid CurrentTransitionChanged(const QString &uuid);\n\n\tvoid TransitionDurationChanged(const int &duration);\n\npublic:\n\tint GetTransitionDuration();\n\tint GetTbarPosition();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_Updater\n\t * -------------------------------------\n\t */\nprivate:\n\tQScopedPointer<QThread> whatsNewInitThread;\n\tQScopedPointer<QThread> updateCheckThread;\n\tQScopedPointer<QThread> introCheckThread;\n\n\tvoid TimedCheckForUpdates();\n\tvoid CheckForUpdates(bool manualUpdate);\n\n\tvoid MacBranchesFetched(const QString &branch, bool manualUpdate);\n\tvoid ReceivedIntroJson(const QString &text);\n\tvoid ShowWhatsNew(const QString &url);\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_VirtualCam\n\t * -------------------------------------\n\t */\nprivate:\n\tbool vcamEnabled = false;\n\tVCamConfig vcamConfig;\n\tbool restartingVCam = false;\n\npublic slots:\n\tvoid StartVirtualCam();\n\tvoid StopVirtualCam();\n\n\tvoid OnVirtualCamStart();\n\tvoid OnVirtualCamStop(int code);\n\n\tbool VirtualCamActive();\n\nprivate slots:\n\tvoid UpdateVirtualCamConfig(const VCamConfig &config);\n\tvoid RestartVirtualCam(const VCamConfig &config);\n\tvoid RestartingVirtualCam();\n\n\t/* Virtual Cam action (start/stop) slots */\n\tvoid VirtualCamActionTriggered();\n\n\tvoid OpenVirtualCamConfig();\n\npublic:\n\tinline bool VCamEnabled() const { return vcamEnabled; }\n\nsignals:\n\t/* Virtual Camera signals */\n\tvoid VirtualCamEnabled();\n\tvoid VirtualCamStarted();\n\tvoid VirtualCamStopped();\n\n\t/* -------------------------------------\n\t * MARK: - OBSBasic_YouTube\n\t * -------------------------------------\n\t */\n\nprivate:\n\tbool autoStartBroadcast = true;\n\tbool autoStopBroadcast = true;\n\tbool broadcastActive = false;\n\tbool broadcastReady = false;\n\tQPointer<QThread> youtubeStreamCheckThread;\n\n#ifdef YOUTUBE_ENABLED\n\tQPointer<YouTubeAppDock> youtubeAppDock;\n\tuint64_t lastYouTubeAppDockCreationTime = 0;\n\n\tvoid YoutubeStreamCheck(const std::string &key);\n\tvoid ShowYouTubeAutoStartWarning();\n\tvoid YouTubeActionDialogOk(const QString &broadcast_id, const QString &stream_id, const QString &key,\n\t\t\t\t   bool autostart, bool autostop, bool start_now);\n#endif\n\n\tvoid BroadcastButtonClicked();\n\tvoid SetBroadcastFlowEnabled(bool enabled);\n\npublic:\n#ifdef YOUTUBE_ENABLED\n\tvoid NewYouTubeAppDock();\n\tvoid DeleteYouTubeAppDock();\n\tYouTubeAppDock *GetYouTubeAppDock();\n#endif\n\npublic slots:\n\tvoid SetupBroadcast();\n\nsignals:\n\t/* Broadcast Flow signals */\n\tvoid BroadcastFlowEnabled(bool enabled);\n\tvoid BroadcastStreamReady(bool ready);\n\tvoid BroadcastStreamActive();\n\tvoid BroadcastStreamStarted(bool autoStop);\n};\n"
  },
  {
    "path": "frontend/widgets/OBSBasicControls.cpp",
    "content": "#include \"OBSBasicControls.hpp\"\n#include \"OBSBasic.hpp\"\n#include \"qt-wrappers.hpp\"\n\n#include \"moc_OBSBasicControls.cpp\"\n\nOBSBasicControls::OBSBasicControls(OBSBasic *main) : QFrame(nullptr), ui(new Ui::OBSBasicControls)\n{\n\t/* Create UI elements */\n\tui->setupUi(this);\n\n\tstreamButtonMenu.reset(new QMenu());\n\tstartStreamAction = streamButtonMenu->addAction(QTStr(\"Basic.Main.StartStreaming\"));\n\tstopStreamAction = streamButtonMenu->addAction(QTStr(\"Basic.Main.StopStreaming\"));\n\tQAction *forceStopStreamAction = streamButtonMenu->addAction(QTStr(\"Basic.Main.ForceStopStreaming\"));\n\n\t/* Transfer buttons signals as OBSBasicControls signals */\n\tconnect(\n\t\tui->streamButton, &QPushButton::clicked, this, [this]() { emit this->StreamButtonClicked(); },\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tui->broadcastButton, &QPushButton::clicked, this, [this]() { emit this->BroadcastButtonClicked(); },\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tui->recordButton, &QPushButton::clicked, this, [this]() { emit this->RecordButtonClicked(); },\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tui->pauseRecordButton, &QPushButton::clicked, this, [this]() { emit this->PauseRecordButtonClicked(); },\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tui->replayBufferButton, &QPushButton::clicked, this,\n\t\t[this]() { emit this->ReplayBufferButtonClicked(); }, Qt::DirectConnection);\n\tconnect(\n\t\tui->saveReplayButton, &QPushButton::clicked, this,\n\t\t[this]() { emit this->SaveReplayBufferButtonClicked(); }, Qt::DirectConnection);\n\tconnect(\n\t\tui->virtualCamButton, &QPushButton::clicked, this, [this]() { emit this->VirtualCamButtonClicked(); },\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tui->virtualCamConfigButton, &QPushButton::clicked, this,\n\t\t[this]() { emit this->VirtualCamConfigButtonClicked(); }, Qt::DirectConnection);\n\tconnect(\n\t\tui->modeSwitch, &QPushButton::clicked, this, [this]() { emit this->StudioModeButtonClicked(); },\n\t\tQt::DirectConnection);\n\tconnect(\n\t\tui->settingsButton, &QPushButton::clicked, this, [this]() { emit this->SettingsButtonClicked(); },\n\t\tQt::DirectConnection);\n\n\t/* Transfer menu actions signals as OBSBasicControls signals */\n\tconnect(\n\t\tstartStreamAction.get(), &QAction::triggered, this,\n\t\t[this]() { emit this->StartStreamMenuActionClicked(); }, Qt::DirectConnection);\n\tconnect(\n\t\tstopStreamAction.get(), &QAction::triggered, this,\n\t\t[this]() { emit this->StopStreamMenuActionClicked(); }, Qt::DirectConnection);\n\tconnect(\n\t\tforceStopStreamAction, &QAction::triggered, this,\n\t\t[this]() { emit this->ForceStopStreamMenuActionClicked(); }, Qt::DirectConnection);\n\n\t/* Set up default visibility */\n\tui->broadcastButton->setVisible(false);\n\tui->pauseRecordButton->setVisible(false);\n\tui->replayBufferButton->setVisible(false);\n\tui->saveReplayButton->setVisible(false);\n\tui->virtualCamButton->setVisible(false);\n\tui->virtualCamConfigButton->setVisible(false);\n\n\t/* Set up state update connections */\n\tconnect(main, &OBSBasic::StreamingPreparing, this, &OBSBasicControls::StreamingPreparing);\n\tconnect(main, &OBSBasic::StreamingStarting, this, &OBSBasicControls::StreamingStarting);\n\tconnect(main, &OBSBasic::StreamingStarted, this, &OBSBasicControls::StreamingStarted);\n\tconnect(main, &OBSBasic::StreamingStopping, this, &OBSBasicControls::StreamingStopping);\n\tconnect(main, &OBSBasic::StreamingStopped, this, &OBSBasicControls::StreamingStopped);\n\n\tconnect(main, &OBSBasic::BroadcastStreamReady, this, &OBSBasicControls::BroadcastStreamReady);\n\tconnect(main, &OBSBasic::BroadcastStreamActive, this, &OBSBasicControls::BroadcastStreamActive);\n\tconnect(main, &OBSBasic::BroadcastStreamStarted, this, &OBSBasicControls::BroadcastStreamStarted);\n\n\tconnect(main, &OBSBasic::RecordingStarted, this, &OBSBasicControls::RecordingStarted);\n\tconnect(main, &OBSBasic::RecordingPaused, this, &OBSBasicControls::RecordingPaused);\n\tconnect(main, &OBSBasic::RecordingUnpaused, this, &OBSBasicControls::RecordingUnpaused);\n\tconnect(main, &OBSBasic::RecordingStopping, this, &OBSBasicControls::RecordingStopping);\n\tconnect(main, &OBSBasic::RecordingStopped, this, &OBSBasicControls::RecordingStopped);\n\n\tconnect(main, &OBSBasic::ReplayBufStarted, this, &OBSBasicControls::ReplayBufferStarted);\n\tconnect(main, &OBSBasic::ReplayBufStopping, this, &OBSBasicControls::ReplayBufferStopping);\n\tconnect(main, &OBSBasic::ReplayBufStopped, this, &OBSBasicControls::ReplayBufferStopped);\n\n\tconnect(main, &OBSBasic::VirtualCamStarted, this, &OBSBasicControls::VirtualCamStarted);\n\tconnect(main, &OBSBasic::VirtualCamStopped, this, &OBSBasicControls::VirtualCamStopped);\n\n\tconnect(main, &OBSBasic::PreviewProgramModeChanged, this, &OBSBasicControls::UpdateStudioModeState);\n\n\t/* Set up enablement connection */\n\tconnect(main, &OBSBasic::BroadcastFlowEnabled, this, &OBSBasicControls::EnableBroadcastFlow);\n\tconnect(main, &OBSBasic::ReplayBufEnabled, this, &OBSBasicControls::EnableReplayBufferButtons);\n\tconnect(main, &OBSBasic::VirtualCamEnabled, this, &OBSBasicControls::EnableVirtualCamButtons);\n}\n\nvoid OBSBasicControls::StreamingPreparing()\n{\n\tui->streamButton->setEnabled(false);\n\tui->streamButton->setText(QTStr(\"Basic.Main.PreparingStream\"));\n}\n\nvoid OBSBasicControls::StreamingStarting(bool broadcastAutoStart)\n{\n\tui->streamButton->setText(QTStr(\"Basic.Main.Connecting\"));\n\n\tif (!broadcastAutoStart) {\n\t\t// well, we need to disable button while stream is not active\n\t\tui->broadcastButton->setEnabled(false);\n\n\t\tui->broadcastButton->setText(QTStr(\"Basic.Main.StartBroadcast\"));\n\n\t\tui->broadcastButton->setProperty(\"broadcastState\", \"ready\");\n\t\tui->broadcastButton->style()->unpolish(ui->broadcastButton);\n\t\tui->broadcastButton->style()->polish(ui->broadcastButton);\n\t}\n}\n\nvoid OBSBasicControls::StreamingStarted(bool withDelay)\n{\n\tui->streamButton->setEnabled(true);\n\tsetClasses(ui->streamButton, \"state-active\");\n\tui->streamButton->setText(QTStr(\"Basic.Main.StopStreaming\"));\n\n\tif (withDelay) {\n\t\tui->streamButton->setMenu(streamButtonMenu.get());\n\t\tstartStreamAction->setVisible(false);\n\t\tstopStreamAction->setVisible(true);\n\t}\n}\n\nvoid OBSBasicControls::StreamingStopping()\n{\n\tui->streamButton->setText(QTStr(\"Basic.Main.StoppingStreaming\"));\n}\n\nvoid OBSBasicControls::StreamingStopped(bool withDelay)\n{\n\tui->streamButton->setEnabled(true);\n\tsetClasses(ui->streamButton, \"\");\n\tui->streamButton->setText(QTStr(\"Basic.Main.StartStreaming\"));\n\n\tif (withDelay) {\n\t\tif (!ui->streamButton->menu())\n\t\t\tui->streamButton->setMenu(streamButtonMenu.get());\n\n\t\tstartStreamAction->setVisible(true);\n\t\tstopStreamAction->setVisible(false);\n\t} else {\n\t\tui->streamButton->setMenu(nullptr);\n\t}\n}\n\nvoid OBSBasicControls::BroadcastStreamReady(bool ready)\n{\n\tsetClasses(ui->broadcastButton, ready ? \"state-active\" : \"\");\n}\n\nvoid OBSBasicControls::BroadcastStreamActive()\n{\n\tui->broadcastButton->setEnabled(true);\n}\n\nvoid OBSBasicControls::BroadcastStreamStarted(bool autoStop)\n{\n\tui->broadcastButton->setText(QTStr(autoStop ? \"Basic.Main.AutoStopEnabled\" : \"Basic.Main.StopBroadcast\"));\n\tif (autoStop)\n\t\tui->broadcastButton->setEnabled(false);\n\n\tui->broadcastButton->setProperty(\"broadcastState\", \"active\");\n\tui->broadcastButton->style()->unpolish(ui->broadcastButton);\n\tui->broadcastButton->style()->polish(ui->broadcastButton);\n}\n\nvoid OBSBasicControls::RecordingStarted(bool pausable)\n{\n\tsetClasses(ui->recordButton, \"state-active\");\n\tui->recordButton->setText(QTStr(\"Basic.Main.StopRecording\"));\n\n\tif (pausable) {\n\t\tui->pauseRecordButton->setVisible(pausable);\n\t\tRecordingUnpaused();\n\t}\n}\n\nvoid OBSBasicControls::RecordingPaused()\n{\n\tQString text = QTStr(\"Basic.Main.UnpauseRecording\");\n\n\tsetClasses(ui->pauseRecordButton, \"icon-media-pause state-active\");\n\tui->pauseRecordButton->setAccessibleName(text);\n\tui->pauseRecordButton->setToolTip(text);\n\n\tui->saveReplayButton->setEnabled(false);\n}\n\nvoid OBSBasicControls::RecordingUnpaused()\n{\n\tQString text = QTStr(\"Basic.Main.PauseRecording\");\n\n\tsetClasses(ui->pauseRecordButton, \"icon-media-pause\");\n\tui->pauseRecordButton->setAccessibleName(text);\n\tui->pauseRecordButton->setToolTip(text);\n\n\tui->saveReplayButton->setEnabled(true);\n}\n\nvoid OBSBasicControls::RecordingStopping()\n{\n\tui->recordButton->setText(QTStr(\"Basic.Main.StoppingRecording\"));\n}\n\nvoid OBSBasicControls::RecordingStopped()\n{\n\tsetClasses(ui->recordButton, \"\");\n\tui->recordButton->setText(QTStr(\"Basic.Main.StartRecording\"));\n\n\tui->pauseRecordButton->setVisible(false);\n}\n\nvoid OBSBasicControls::ReplayBufferStarted()\n{\n\tsetClasses(ui->replayBufferButton, \"state-active\");\n\tui->replayBufferButton->setText(QTStr(\"Basic.Main.StopReplayBuffer\"));\n\n\tui->saveReplayButton->setVisible(true);\n}\n\nvoid OBSBasicControls::ReplayBufferStopping()\n{\n\tui->replayBufferButton->setText(QTStr(\"Basic.Main.StoppingReplayBuffer\"));\n}\n\nvoid OBSBasicControls::ReplayBufferStopped()\n{\n\tsetClasses(ui->replayBufferButton, \"\");\n\tui->replayBufferButton->setText(QTStr(\"Basic.Main.StartReplayBuffer\"));\n\n\tui->saveReplayButton->setVisible(false);\n}\n\nvoid OBSBasicControls::VirtualCamStarted()\n{\n\tsetClasses(ui->virtualCamButton, \"state-active\");\n\tui->virtualCamButton->setText(QTStr(\"Basic.Main.StopVirtualCam\"));\n}\n\nvoid OBSBasicControls::VirtualCamStopped()\n{\n\tsetClasses(ui->virtualCamButton, \"\");\n\tui->virtualCamButton->setText(QTStr(\"Basic.Main.StartVirtualCam\"));\n}\n\nvoid OBSBasicControls::UpdateStudioModeState(bool enabled)\n{\n\tsetClasses(ui->modeSwitch, enabled ? \"state-active\" : \"\");\n}\n\nvoid OBSBasicControls::EnableBroadcastFlow(bool enabled)\n{\n\tui->broadcastButton->setVisible(enabled);\n\tui->broadcastButton->setEnabled(enabled);\n\n\tui->broadcastButton->setText(QTStr(\"Basic.Main.SetupBroadcast\"));\n\n\tui->broadcastButton->setProperty(\"broadcastState\", \"idle\");\n\tui->broadcastButton->style()->unpolish(ui->broadcastButton);\n\tui->broadcastButton->style()->polish(ui->broadcastButton);\n}\n\nvoid OBSBasicControls::EnableReplayBufferButtons(bool enabled)\n{\n\tui->replayBufferButton->setVisible(enabled);\n}\n\nvoid OBSBasicControls::EnableVirtualCamButtons()\n{\n\tui->virtualCamButton->setVisible(true);\n\tui->virtualCamConfigButton->setVisible(true);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasicControls.hpp",
    "content": "#pragma once\n\n#include \"ui_OBSBasicControls.h\"\n\n#include <QFrame>\n#include <QPointer>\n#include <QScopedPointer>\n\n#include <memory>\n\nclass OBSBasic;\n\nclass OBSBasicControls : public QFrame {\n\tQ_OBJECT\n\n\tstd::unique_ptr<Ui::OBSBasicControls> ui;\n\n\tQScopedPointer<QMenu> streamButtonMenu;\n\tQPointer<QAction> startStreamAction;\n\tQPointer<QAction> stopStreamAction;\n\nprivate slots:\n\tvoid StreamingPreparing();\n\tvoid StreamingStarting(bool broadcastAutoStart);\n\tvoid StreamingStarted(bool withDelay);\n\tvoid StreamingStopping();\n\tvoid StreamingStopped(bool withDelay);\n\n\tvoid BroadcastStreamReady(bool ready);\n\tvoid BroadcastStreamActive();\n\tvoid BroadcastStreamStarted(bool autoStop);\n\n\tvoid RecordingStarted(bool pausable);\n\tvoid RecordingPaused();\n\tvoid RecordingUnpaused();\n\tvoid RecordingStopping();\n\tvoid RecordingStopped();\n\n\tvoid ReplayBufferStarted();\n\tvoid ReplayBufferStopping();\n\tvoid ReplayBufferStopped();\n\n\tvoid VirtualCamStarted();\n\tvoid VirtualCamStopped();\n\n\tvoid UpdateStudioModeState(bool enabled);\n\n\tvoid EnableBroadcastFlow(bool enabled);\n\tvoid EnableReplayBufferButtons(bool enabled);\n\tvoid EnableVirtualCamButtons();\n\npublic:\n\tOBSBasicControls(OBSBasic *main);\n\tinline ~OBSBasicControls() {}\n\nsignals:\n\tvoid StreamButtonClicked();\n\tvoid BroadcastButtonClicked();\n\tvoid RecordButtonClicked();\n\tvoid PauseRecordButtonClicked();\n\tvoid ReplayBufferButtonClicked();\n\tvoid SaveReplayBufferButtonClicked();\n\tvoid VirtualCamButtonClicked();\n\tvoid VirtualCamConfigButtonClicked();\n\tvoid StudioModeButtonClicked();\n\tvoid SettingsButtonClicked();\n\n\tvoid StartStreamMenuActionClicked();\n\tvoid StopStreamMenuActionClicked();\n\tvoid ForceStopStreamMenuActionClicked();\n};\n"
  },
  {
    "path": "frontend/widgets/OBSBasicPreview.cpp",
    "content": "#include \"OBSBasicPreview.hpp\"\n\n#include <utility/display-helpers.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include \"moc_OBSBasicPreview.cpp\"\n\n#define HANDLE_RADIUS 4.0f\n#define HANDLE_SEL_RADIUS (HANDLE_RADIUS * 1.5f)\n#define HELPER_ROT_BREAKPOINT 45.0f\n\n/* TODO: make C++ math classes and clean up code here later */\n\nOBSBasicPreview::OBSBasicPreview(QWidget *parent, Qt::WindowFlags flags) : OBSQTDisplay(parent, flags)\n{\n\tResetScrollingOffset();\n\tsetMouseTracking(true);\n}\n\nOBSBasicPreview::~OBSBasicPreview()\n{\n\tobs_enter_graphics();\n\n\tif (overflow)\n\t\tgs_texture_destroy(overflow);\n\tif (rectFill)\n\t\tgs_vertexbuffer_destroy(rectFill);\n\tif (circleFill)\n\t\tgs_vertexbuffer_destroy(circleFill);\n\tif (stripedLineEffect)\n\t\tgs_effect_destroy(stripedLineEffect);\n\n\tobs_leave_graphics();\n}\n\nvoid OBSBasicPreview::Init()\n{\n\tstd::string effect_path;\n\tGetDataFilePath(\"striped_line.effect\", effect_path);\n\n\tobs_enter_graphics();\n\tstripedLineEffect = gs_effect_create_from_file(effect_path.c_str(), nullptr);\n\tsolidEffect = obs_get_base_effect(OBS_EFFECT_SOLID);\n\tobs_leave_graphics();\n}\n\nvec2 OBSBasicPreview::GetMouseEventPos(QMouseEvent *event)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tfloat pixelRatio = main->GetDevicePixelRatio();\n\tfloat scale = pixelRatio / main->previewScale;\n\tQPoint qtPos = event->pos();\n\tvec2 pos;\n\tvec2_set(&pos, (qtPos.x() - main->previewX / pixelRatio) * scale,\n\t\t (qtPos.y() - main->previewY / pixelRatio) * scale);\n\n\treturn pos;\n}\n\nstatic void RotatePos(vec2 *pos, float rot)\n{\n\tfloat cosR = cos(rot);\n\tfloat sinR = sin(rot);\n\n\tvec2 newPos;\n\n\tnewPos.x = cosR * pos->x - sinR * pos->y;\n\tnewPos.y = sinR * pos->x + cosR * pos->y;\n\n\tvec2_copy(pos, &newPos);\n}\n\nstruct SceneFindData {\n\tconst vec2 &pos;\n\tOBSSceneItem item;\n\tbool selectBelow;\n\n\tobs_sceneitem_t *group = nullptr;\n\n\tSceneFindData(const SceneFindData &) = delete;\n\tSceneFindData(SceneFindData &&) = delete;\n\tSceneFindData &operator=(const SceneFindData &) = delete;\n\tSceneFindData &operator=(SceneFindData &&) = delete;\n\n\tinline SceneFindData(const vec2 &pos_, bool selectBelow_) : pos(pos_), selectBelow(selectBelow_) {}\n};\n\nstruct SceneFindBoxData {\n\tconst vec2 &startPos;\n\tconst vec2 &pos;\n\tstd::vector<obs_sceneitem_t *> sceneItems;\n\n\tSceneFindBoxData(const SceneFindData &) = delete;\n\tSceneFindBoxData(SceneFindData &&) = delete;\n\tSceneFindBoxData &operator=(const SceneFindData &) = delete;\n\tSceneFindBoxData &operator=(SceneFindData &&) = delete;\n\n\tinline SceneFindBoxData(const vec2 &startPos_, const vec2 &pos_) : startPos(startPos_), pos(pos_) {}\n};\n\nstatic bool SceneItemHasVideo(obs_sceneitem_t *item)\n{\n\tobs_source_t *source = obs_sceneitem_get_source(item);\n\tuint32_t flags = obs_source_get_output_flags(source);\n\treturn (flags & OBS_SOURCE_VIDEO) != 0;\n}\n\nstatic bool CloseFloat(float a, float b, float epsilon = 0.01)\n{\n\tusing std::abs;\n\treturn abs(a - b) <= epsilon;\n}\n\nstatic bool FindItemAtPos(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tSceneFindData *data = static_cast<SceneFindData *>(param);\n\tmatrix4 transform;\n\tmatrix4 invTransform;\n\tvec3 transformedPos;\n\tvec3 pos3;\n\tvec3 pos3_;\n\n\tif (!SceneItemHasVideo(item))\n\t\treturn true;\n\tif (obs_sceneitem_locked(item))\n\t\treturn true;\n\n\tvec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);\n\n\tobs_sceneitem_get_box_transform(item, &transform);\n\n\tmatrix4_inv(&invTransform, &transform);\n\tvec3_transform(&transformedPos, &pos3, &invTransform);\n\tvec3_transform(&pos3_, &transformedPos, &transform);\n\n\tif (CloseFloat(pos3.x, pos3_.x) && CloseFloat(pos3.y, pos3_.y) && transformedPos.x >= 0.0f &&\n\t    transformedPos.x <= 1.0f && transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {\n\t\tif (data->selectBelow && obs_sceneitem_selected(item)) {\n\t\t\tif (data->item)\n\t\t\t\treturn false;\n\t\t\telse\n\t\t\t\tdata->selectBelow = false;\n\t\t}\n\n\t\tdata->item = item;\n\t}\n\n\treturn true;\n}\n\nstatic vec3 GetTransformedPos(float x, float y, const matrix4 &mat)\n{\n\tvec3 result;\n\tvec3_set(&result, x, y, 0.0f);\n\tvec3_transform(&result, &result, &mat);\n\treturn result;\n}\n\nstatic inline vec2 GetOBSScreenSize()\n{\n\tobs_video_info ovi;\n\tvec2 size;\n\tvec2_zero(&size);\n\n\tif (obs_get_video_info(&ovi)) {\n\t\tsize.x = float(ovi.base_width);\n\t\tsize.y = float(ovi.base_height);\n\t}\n\n\treturn size;\n}\n\nvec3 OBSBasicPreview::GetSnapOffset(const vec3 &tl, const vec3 &br)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tvec2 screenSize = GetOBSScreenSize();\n\tvec3 clampOffset;\n\n\tvec3_zero(&clampOffset);\n\n\tconst bool snap = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SnappingEnabled\");\n\tif (snap == false)\n\t\treturn clampOffset;\n\n\tconst bool screenSnap = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ScreenSnapping\");\n\tconst bool centerSnap = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"CenterSnapping\");\n\n\tconst float clampDist =\n\t\tconfig_get_double(App()->GetUserConfig(), \"BasicWindow\", \"SnapDistance\") / main->previewScale;\n\tconst float centerX = br.x - (br.x - tl.x) / 2.0f;\n\tconst float centerY = br.y - (br.y - tl.y) / 2.0f;\n\n\t// Left screen edge.\n\tif (screenSnap && fabsf(tl.x) < clampDist)\n\t\tclampOffset.x = -tl.x;\n\t// Right screen edge.\n\tif (screenSnap && fabsf(clampOffset.x) < EPSILON && fabsf(screenSize.x - br.x) < clampDist)\n\t\tclampOffset.x = screenSize.x - br.x;\n\t// Horizontal center.\n\tif (centerSnap && fabsf(screenSize.x - (br.x - tl.x)) > clampDist &&\n\t    fabsf(screenSize.x / 2.0f - centerX) < clampDist)\n\t\tclampOffset.x = screenSize.x / 2.0f - centerX;\n\n\t// Top screen edge.\n\tif (screenSnap && fabsf(tl.y) < clampDist)\n\t\tclampOffset.y = -tl.y;\n\t// Bottom screen edge.\n\tif (screenSnap && fabsf(clampOffset.y) < EPSILON && fabsf(screenSize.y - br.y) < clampDist)\n\t\tclampOffset.y = screenSize.y - br.y;\n\t// Vertical center.\n\tif (centerSnap && fabsf(screenSize.y - (br.y - tl.y)) > clampDist &&\n\t    fabsf(screenSize.y / 2.0f - centerY) < clampDist)\n\t\tclampOffset.y = screenSize.y / 2.0f - centerY;\n\n\treturn clampOffset;\n}\n\nOBSSceneItem OBSBasicPreview::GetItemAtPos(const vec2 &pos, bool selectBelow)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tOBSScene scene = main->GetCurrentScene();\n\tif (!scene)\n\t\treturn OBSSceneItem();\n\n\tSceneFindData data(pos, selectBelow);\n\tobs_scene_enum_items(scene, FindItemAtPos, &data);\n\treturn data.item;\n}\n\nstatic bool CheckItemSelected(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tSceneFindData *data = static_cast<SceneFindData *>(param);\n\tmatrix4 transform;\n\tvec3 transformedPos;\n\tvec3 pos3;\n\n\tif (!SceneItemHasVideo(item))\n\t\treturn true;\n\tif (obs_sceneitem_is_group(item)) {\n\t\tdata->group = item;\n\t\tobs_sceneitem_group_enum_items(item, CheckItemSelected, param);\n\t\tdata->group = nullptr;\n\n\t\tif (data->item) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tvec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);\n\n\tobs_sceneitem_get_box_transform(item, &transform);\n\n\tif (data->group) {\n\t\tmatrix4 parent_transform;\n\t\tobs_sceneitem_get_draw_transform(data->group, &parent_transform);\n\t\tmatrix4_mul(&transform, &transform, &parent_transform);\n\t}\n\n\tmatrix4_inv(&transform, &transform);\n\tvec3_transform(&transformedPos, &pos3, &transform);\n\n\tif (transformedPos.x >= 0.0f && transformedPos.x <= 1.0f && transformedPos.y >= 0.0f &&\n\t    transformedPos.y <= 1.0f) {\n\t\tif (obs_sceneitem_selected(item)) {\n\t\t\tdata->item = item;\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool OBSBasicPreview::SelectedAtPos(const vec2 &pos)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tOBSScene scene = main->GetCurrentScene();\n\tif (!scene)\n\t\treturn false;\n\n\tSceneFindData data(pos, false);\n\tobs_scene_enum_items(scene, CheckItemSelected, &data);\n\treturn !!data.item;\n}\n\nstruct HandleFindData {\n\tconst vec2 &pos;\n\tconst float radius;\n\tmatrix4 parent_xform;\n\n\tOBSSceneItem item;\n\tItemHandle handle = ItemHandle::None;\n\tfloat angle = 0.0f;\n\tvec2 rotatePoint;\n\tvec2 offsetPoint;\n\n\tfloat angleOffset = 0.0f;\n\n\tHandleFindData(const HandleFindData &) = delete;\n\tHandleFindData(HandleFindData &&) = delete;\n\tHandleFindData &operator=(const HandleFindData &) = delete;\n\tHandleFindData &operator=(HandleFindData &&) = delete;\n\n\tinline HandleFindData(const vec2 &pos_, float scale) : pos(pos_), radius(HANDLE_SEL_RADIUS / scale)\n\t{\n\t\tmatrix4_identity(&parent_xform);\n\t}\n\n\tinline HandleFindData(const HandleFindData &hfd, obs_sceneitem_t *parent)\n\t\t: pos(hfd.pos),\n\t\t  radius(hfd.radius),\n\t\t  item(hfd.item),\n\t\t  handle(hfd.handle),\n\t\t  angle(hfd.angle),\n\t\t  rotatePoint(hfd.rotatePoint),\n\t\t  offsetPoint(hfd.offsetPoint)\n\t{\n\t\tobs_sceneitem_get_draw_transform(parent, &parent_xform);\n\t}\n};\n\nstatic bool FindHandleAtPos(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tHandleFindData &data = *static_cast<HandleFindData *>(param);\n\n\tif (!obs_sceneitem_selected(item)) {\n\t\tif (obs_sceneitem_is_group(item)) {\n\t\t\tHandleFindData newData(data, item);\n\t\t\tnewData.angleOffset = obs_sceneitem_get_rot(item);\n\n\t\t\tobs_sceneitem_group_enum_items(item, FindHandleAtPos, &newData);\n\n\t\t\tdata.item = newData.item;\n\t\t\tdata.handle = newData.handle;\n\t\t\tdata.angle = newData.angle;\n\t\t\tdata.rotatePoint = newData.rotatePoint;\n\t\t\tdata.offsetPoint = newData.offsetPoint;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tmatrix4 transform;\n\tvec3 pos3;\n\tfloat closestHandle = data.radius;\n\n\tvec3_set(&pos3, data.pos.x, data.pos.y, 0.0f);\n\n\tobs_sceneitem_get_box_transform(item, &transform);\n\n\tauto TestHandle = [&](float x, float y, ItemHandle handle) {\n\t\tvec3 handlePos = GetTransformedPos(x, y, transform);\n\t\tvec3_transform(&handlePos, &handlePos, &data.parent_xform);\n\n\t\tfloat dist = vec3_dist(&handlePos, &pos3);\n\t\tif (dist < data.radius) {\n\t\t\tif (dist < closestHandle) {\n\t\t\t\tclosestHandle = dist;\n\t\t\t\tdata.handle = handle;\n\t\t\t\tdata.item = item;\n\t\t\t}\n\t\t}\n\t};\n\n\tTestHandle(0.0f, 0.0f, ItemHandle::TopLeft);\n\tTestHandle(0.5f, 0.0f, ItemHandle::TopCenter);\n\tTestHandle(1.0f, 0.0f, ItemHandle::TopRight);\n\tTestHandle(0.0f, 0.5f, ItemHandle::CenterLeft);\n\tTestHandle(1.0f, 0.5f, ItemHandle::CenterRight);\n\tTestHandle(0.0f, 1.0f, ItemHandle::BottomLeft);\n\tTestHandle(0.5f, 1.0f, ItemHandle::BottomCenter);\n\tTestHandle(1.0f, 1.0f, ItemHandle::BottomRight);\n\n\tvec2 scale;\n\tobs_sceneitem_get_scale(item, &scale);\n\tobs_bounds_type boundsType = obs_sceneitem_get_bounds_type(item);\n\tvec2 rotHandleOffset;\n\tvec2_set(&rotHandleOffset, 0.0f, HANDLE_RADIUS * data.radius * 1.5 - data.radius);\n\tbool invertx = scale.x < 0.0f && boundsType == OBS_BOUNDS_NONE;\n\tfloat angle =\n\t\tatan2(invertx ? transform.x.y * -1.0f : transform.x.y, invertx ? transform.x.x * -1.0f : transform.x.x);\n\tRotatePos(&rotHandleOffset, angle);\n\tRotatePos(&rotHandleOffset, RAD(data.angleOffset));\n\n\tbool inverty = scale.y < 0.0f && boundsType == OBS_BOUNDS_NONE;\n\tvec3 handlePos = GetTransformedPos(0.5f, inverty ? 1.0f : 0.0f, transform);\n\tvec3_transform(&handlePos, &handlePos, &data.parent_xform);\n\thandlePos.x -= rotHandleOffset.x;\n\thandlePos.y -= rotHandleOffset.y;\n\n\tfloat dist = vec3_dist(&handlePos, &pos3);\n\tif (dist < data.radius) {\n\t\tif (dist < closestHandle) {\n\t\t\tclosestHandle = dist;\n\t\t\tdata.item = item;\n\t\t\tdata.angle = obs_sceneitem_get_rot(item);\n\t\t\tdata.handle = ItemHandle::Rot;\n\n\t\t\tvec2_set(&data.rotatePoint, transform.t.x + transform.x.x / 2 + transform.y.x / 2,\n\t\t\t\t transform.t.y + transform.x.y / 2 + transform.y.y / 2);\n\n\t\t\tobs_sceneitem_get_pos(item, &data.offsetPoint);\n\t\t\tdata.offsetPoint.x -= data.rotatePoint.x;\n\t\t\tdata.offsetPoint.y -= data.rotatePoint.y;\n\n\t\t\tRotatePos(&data.offsetPoint, -RAD(obs_sceneitem_get_rot(item)));\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic vec2 GetItemSize(obs_sceneitem_t *item)\n{\n\tobs_bounds_type boundsType = obs_sceneitem_get_bounds_type(item);\n\tvec2 size;\n\n\tif (boundsType != OBS_BOUNDS_NONE) {\n\t\tobs_sceneitem_get_bounds(item, &size);\n\t} else {\n\t\tobs_source_t *source = obs_sceneitem_get_source(item);\n\t\tobs_sceneitem_crop crop;\n\t\tvec2 scale;\n\n\t\tobs_sceneitem_get_scale(item, &scale);\n\t\tobs_sceneitem_get_crop(item, &crop);\n\t\tsize.x = fmaxf(float((int)obs_source_get_width(source) - crop.left - crop.right), 0.0f);\n\t\tsize.y = fmaxf(float((int)obs_source_get_height(source) - crop.top - crop.bottom), 0.0f);\n\t\tvec2_mul(&size, &size, &scale);\n\t}\n\n\treturn size;\n}\n\nvoid OBSBasicPreview::GetStretchHandleData(const vec2 &pos, bool ignoreGroup)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tOBSScene scene = main->GetCurrentScene();\n\tif (!scene)\n\t\treturn;\n\n\tfloat scale = main->previewScale / main->GetDevicePixelRatio();\n\tvec2 scaled_pos = pos;\n\tvec2_divf(&scaled_pos, &scaled_pos, scale);\n\tHandleFindData data(scaled_pos, scale);\n\tobs_scene_enum_items(scene, FindHandleAtPos, &data);\n\n\tstretchItem = std::move(data.item);\n\tstretchHandle = data.handle;\n\n\trotateAngle = data.angle;\n\trotatePoint = data.rotatePoint;\n\toffsetPoint = data.offsetPoint;\n\n\tif (stretchHandle != ItemHandle::None) {\n\t\tmatrix4 boxTransform;\n\t\tvec3 itemUL;\n\t\tfloat itemRot;\n\n\t\tstretchItemSize = GetItemSize(stretchItem);\n\n\t\tobs_sceneitem_get_box_transform(stretchItem, &boxTransform);\n\t\titemRot = obs_sceneitem_get_rot(stretchItem);\n\t\tvec3_from_vec4(&itemUL, &boxTransform.t);\n\n\t\t/* build the item space conversion matrices */\n\t\tmatrix4_identity(&itemToScreen);\n\t\tmatrix4_rotate_aa4f(&itemToScreen, &itemToScreen, 0.0f, 0.0f, 1.0f, RAD(itemRot));\n\t\tmatrix4_translate3f(&itemToScreen, &itemToScreen, itemUL.x, itemUL.y, 0.0f);\n\n\t\tmatrix4_identity(&screenToItem);\n\t\tmatrix4_translate3f(&screenToItem, &screenToItem, -itemUL.x, -itemUL.y, 0.0f);\n\t\tmatrix4_rotate_aa4f(&screenToItem, &screenToItem, 0.0f, 0.0f, 1.0f, RAD(-itemRot));\n\n\t\tobs_sceneitem_get_crop(stretchItem, &startCrop);\n\t\tobs_sceneitem_get_pos(stretchItem, &startItemPos);\n\n\t\tobs_source_t *source = obs_sceneitem_get_source(stretchItem);\n\t\tcropSize.x = float(obs_source_get_width(source) - startCrop.left - startCrop.right);\n\t\tcropSize.y = float(obs_source_get_height(source) - startCrop.top - startCrop.bottom);\n\n\t\tstretchGroup = obs_sceneitem_get_group(scene, stretchItem);\n\t\tif (stretchGroup && !ignoreGroup) {\n\t\t\tobs_sceneitem_get_draw_transform(stretchGroup, &invGroupTransform);\n\t\t\tmatrix4_inv(&invGroupTransform, &invGroupTransform);\n\t\t\tobs_sceneitem_defer_group_resize_begin(stretchGroup);\n\t\t} else {\n\t\t\tstretchGroup = nullptr;\n\t\t}\n\t}\n}\n\nvoid OBSBasicPreview::keyPressEvent(QKeyEvent *event)\n{\n\tif (!IsFixedScaling() || event->isAutoRepeat()) {\n\t\tOBSQTDisplay::keyPressEvent(event);\n\t\treturn;\n\t}\n\n\tswitch (event->key()) {\n\tcase Qt::Key_Space:\n\t\tsetCursor(Qt::OpenHandCursor);\n\t\tscrollMode = true;\n\t\tbreak;\n\t}\n\n\tOBSQTDisplay::keyPressEvent(event);\n}\n\nvoid OBSBasicPreview::keyReleaseEvent(QKeyEvent *event)\n{\n\tif (event->isAutoRepeat()) {\n\t\tOBSQTDisplay::keyReleaseEvent(event);\n\t\treturn;\n\t}\n\n\tswitch (event->key()) {\n\tcase Qt::Key_Space:\n\t\tscrollMode = false;\n\t\tsetCursor(Qt::ArrowCursor);\n\t\tbreak;\n\t}\n\n\tOBSQTDisplay::keyReleaseEvent(event);\n}\n\nvoid OBSBasicPreview::wheelEvent(QWheelEvent *event)\n{\n\tif (scrollMode && IsFixedScaling()) {\n\t\tconst int delta = event->angleDelta().y();\n\t\tif (delta != 0) {\n\t\t\tif (delta > 0)\n\t\t\t\tincreaseScalingLevel();\n\t\t\telse\n\t\t\t\tdecreaseScalingLevel();\n\t\t}\n\t}\n\n\tOBSQTDisplay::wheelEvent(event);\n}\n\nvoid OBSBasicPreview::mousePressEvent(QMouseEvent *event)\n{\n\tQPointF pos = event->position();\n\n\tif (scrollMode && IsFixedScaling() && event->button() == Qt::LeftButton) {\n\t\tsetCursor(Qt::ClosedHandCursor);\n\t\tscrollingFrom.x = pos.x();\n\t\tscrollingFrom.y = pos.y();\n\t\treturn;\n\t}\n\n\tif (event->button() == Qt::RightButton) {\n\t\tscrollMode = false;\n\t\tsetCursor(Qt::ArrowCursor);\n\t}\n\n\tif (locked) {\n\t\tOBSQTDisplay::mousePressEvent(event);\n\t\treturn;\n\t}\n\n\tOBSBasic *main = OBSBasic::Get();\n\tfloat pixelRatio = main->GetDevicePixelRatio();\n\tfloat x = pos.x() - main->previewX / pixelRatio;\n\tfloat y = pos.y() - main->previewY / pixelRatio;\n\tQt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();\n\tbool altDown = (modifiers & Qt::AltModifier);\n\tbool shiftDown = (modifiers & Qt::ShiftModifier);\n\tbool ctrlDown = (modifiers & Qt::ControlModifier);\n\n\tOBSQTDisplay::mousePressEvent(event);\n\n\tif (event->button() != Qt::LeftButton && event->button() != Qt::RightButton)\n\t\treturn;\n\n\tif (event->button() == Qt::LeftButton)\n\t\tmouseDown = true;\n\n\t{\n\t\tstd::lock_guard<std::mutex> lock(selectMutex);\n\t\tselectedItems.clear();\n\t}\n\n\tif (altDown)\n\t\tcropping = true;\n\n\tif (altDown || shiftDown || ctrlDown) {\n\t\tvec2 s;\n\t\tSceneFindBoxData data(s, s);\n\n\t\tobs_scene_enum_items(main->GetCurrentScene(), FindSelected, &data);\n\n\t\tstd::lock_guard<std::mutex> lock(selectMutex);\n\t\tselectedItems = data.sceneItems;\n\t}\n\n\tvec2_set(&startPos, x, y);\n\tGetStretchHandleData(startPos, false);\n\n\tvec2_divf(&startPos, &startPos, main->previewScale / pixelRatio);\n\tstartPos.x = std::round(startPos.x);\n\tstartPos.y = std::round(startPos.y);\n\n\tmouseOverItems = SelectedAtPos(startPos);\n\tvec2_zero(&lastMoveOffset);\n\n\tmousePos = startPos;\n\twrapper = obs_scene_save_transform_states(main->GetCurrentScene(), true);\n\tchanged = false;\n}\n\nvoid OBSBasicPreview::UpdateCursor(uint32_t &flags)\n{\n\tif (!stretchItem || obs_sceneitem_locked(stretchItem)) {\n\t\tunsetCursor();\n\t\treturn;\n\t}\n\n\tif (!flags && (cursor().shape() != Qt::OpenHandCursor || !scrollMode))\n\t\tunsetCursor();\n\tif ((cursor().shape() != Qt::ArrowCursor) || flags == 0)\n\t\treturn;\n\n\tif (flags & ITEM_ROT) {\n\t\tsetCursor(Qt::OpenHandCursor);\n\t\treturn;\n\t}\n\n\tfloat rotation = obs_sceneitem_get_rot(stretchItem);\n\tvec2 scale;\n\tobs_sceneitem_get_scale(stretchItem, &scale);\n\n\tif (rotation < 0.0f)\n\t\trotation = 360.0f + rotation;\n\n\tint octant = int(std::round(rotation / 45.0f));\n\tbool isCorner = (flags & (flags - 1)) != 0;\n\n\tif ((scale.x < 0.0f) && isCorner)\n\t\tflags ^= ITEM_LEFT | ITEM_RIGHT;\n\tif ((scale.y < 0.0f) && isCorner)\n\t\tflags ^= ITEM_TOP | ITEM_BOTTOM;\n\n\tif (octant % 4 >= 2) {\n\t\tif (isCorner) {\n\t\t\tflags ^= ITEM_TOP | ITEM_BOTTOM;\n\t\t} else {\n\t\t\tflags = (flags >> 2) | (flags << 2);\n\t\t}\n\t}\n\n\tif (octant % 2 == 1) {\n\t\tif (isCorner) {\n\t\t\tflags &= (flags % 3 == 0) ? ~ITEM_TOP & ~ITEM_BOTTOM : ~ITEM_LEFT & ~ITEM_RIGHT;\n\t\t} else {\n\t\t\tflags = (flags % 4 == 0) ? flags | flags >> ((flags / 2) - 1)\n\t\t\t\t\t\t : flags | ((flags >> 2) | (flags << 2));\n\t\t}\n\t}\n\n\tif ((flags & ITEM_LEFT && flags & ITEM_TOP) || (flags & ITEM_RIGHT && flags & ITEM_BOTTOM))\n\t\tsetCursor(Qt::SizeFDiagCursor);\n\telse if ((flags & ITEM_LEFT && flags & ITEM_BOTTOM) || (flags & ITEM_RIGHT && flags & ITEM_TOP))\n\t\tsetCursor(Qt::SizeBDiagCursor);\n\telse if (flags & ITEM_LEFT || flags & ITEM_RIGHT)\n\t\tsetCursor(Qt::SizeHorCursor);\n\telse if (flags & ITEM_TOP || flags & ITEM_BOTTOM)\n\t\tsetCursor(Qt::SizeVerCursor);\n}\n\nstatic bool select_one(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tobs_sceneitem_t *selectedItem = static_cast<obs_sceneitem_t *>(param);\n\tif (obs_sceneitem_is_group(item))\n\t\tobs_sceneitem_group_enum_items(item, select_one, param);\n\n\tobs_sceneitem_select(item, (selectedItem == item));\n\n\treturn true;\n}\n\nvoid OBSBasicPreview::DoSelect(const vec2 &pos)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tOBSScene scene = main->GetCurrentScene();\n\tOBSSceneItem item = GetItemAtPos(pos, true);\n\n\tobs_scene_enum_items(scene, select_one, (obs_sceneitem_t *)item);\n}\n\nvoid OBSBasicPreview::DoCtrlSelect(const vec2 &pos)\n{\n\tOBSSceneItem item = GetItemAtPos(pos, false);\n\tif (!item)\n\t\treturn;\n\n\tbool selected = obs_sceneitem_selected(item);\n\tobs_sceneitem_select(item, !selected);\n}\n\nvoid OBSBasicPreview::ProcessClick(const vec2 &pos)\n{\n\tQt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();\n\n\tif (modifiers & Qt::ControlModifier)\n\t\tDoCtrlSelect(pos);\n\telse\n\t\tDoSelect(pos);\n}\n\nvoid OBSBasicPreview::mouseReleaseEvent(QMouseEvent *event)\n{\n\tif (scrollMode)\n\t\tsetCursor(Qt::OpenHandCursor);\n\n\tif (locked) {\n\t\tOBSQTDisplay::mouseReleaseEvent(event);\n\t\treturn;\n\t}\n\n\tif (mouseDown) {\n\t\tvec2 pos = GetMouseEventPos(event);\n\n\t\tif (!mouseMoved)\n\t\t\tProcessClick(pos);\n\n\t\tif (selectionBox) {\n\t\t\tQt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();\n\n\t\t\tbool altDown = modifiers & Qt::AltModifier;\n\t\t\tbool shiftDown = modifiers & Qt::ShiftModifier;\n\t\t\tbool ctrlDown = modifiers & Qt::ControlModifier;\n\n\t\t\tstd::lock_guard<std::mutex> lock(selectMutex);\n\t\t\tif (altDown || ctrlDown || shiftDown) {\n\t\t\t\tfor (size_t i = 0; i < selectedItems.size(); i++) {\n\t\t\t\t\tobs_sceneitem_select(selectedItems[i], true);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (size_t i = 0; i < hoveredPreviewItems.size(); i++) {\n\t\t\t\tbool select = true;\n\t\t\t\tobs_sceneitem_t *item = hoveredPreviewItems[i];\n\n\t\t\t\tif (altDown) {\n\t\t\t\t\tselect = false;\n\t\t\t\t} else if (ctrlDown) {\n\t\t\t\t\tselect = !obs_sceneitem_selected(item);\n\t\t\t\t}\n\n\t\t\t\tobs_sceneitem_select(hoveredPreviewItems[i], select);\n\t\t\t}\n\t\t}\n\n\t\tif (stretchGroup) {\n\t\t\tobs_sceneitem_defer_group_resize_end(stretchGroup);\n\t\t}\n\n\t\tstretchItem = nullptr;\n\t\tstretchGroup = nullptr;\n\t\tmouseDown = false;\n\t\tmouseMoved = false;\n\t\tcropping = false;\n\t\tselectionBox = false;\n\t\tunsetCursor();\n\n\t\tOBSSceneItem item = GetItemAtPos(pos, true);\n\n\t\tstd::lock_guard<std::mutex> lock(selectMutex);\n\t\thoveredPreviewItems.clear();\n\t\thoveredPreviewItems.push_back(item);\n\t\tselectedItems.clear();\n\t}\n\tOBSBasic *main = OBSBasic::Get();\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(main->GetCurrentScene(), true);\n\n\tauto undo_redo = [](const std::string &data) {\n\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(dat, \"scene_uuid\"));\n\t\tOBSBasic::Get()->SetCurrentScene(source.Get(), true);\n\n\t\tobs_scene_load_transform_states(data.c_str());\n\t};\n\n\tif (wrapper && rwrapper) {\n\t\tstd::string undo_data(obs_data_get_json(wrapper));\n\t\tstd::string redo_data(obs_data_get_json(rwrapper));\n\t\tif (changed && undo_data.compare(redo_data) != 0)\n\t\t\tmain->undo_s.add_action(\n\t\t\t\tQTStr(\"Undo.Transform\").arg(obs_source_get_name(main->GetCurrentSceneSource())),\n\t\t\t\tundo_redo, undo_redo, undo_data, redo_data);\n\t}\n\n\twrapper = nullptr;\n}\n\nstruct SelectedItemBounds {\n\tbool first = true;\n\tvec3 tl, br;\n};\n\nstatic bool AddItemBounds(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tSelectedItemBounds *data = static_cast<SelectedItemBounds *>(param);\n\tvec3 t[4];\n\n\tauto add_bounds = [data, &t]() {\n\t\tfor (const vec3 &v : t) {\n\t\t\tif (data->first) {\n\t\t\t\tvec3_copy(&data->tl, &v);\n\t\t\t\tvec3_copy(&data->br, &v);\n\t\t\t\tdata->first = false;\n\t\t\t} else {\n\t\t\t\tvec3_min(&data->tl, &data->tl, &v);\n\t\t\t\tvec3_max(&data->br, &data->br, &v);\n\t\t\t}\n\t\t}\n\t};\n\n\tif (obs_sceneitem_is_group(item)) {\n\t\tSelectedItemBounds sib;\n\t\tobs_sceneitem_group_enum_items(item, AddItemBounds, &sib);\n\n\t\tif (!sib.first) {\n\t\t\tmatrix4 xform;\n\t\t\tobs_sceneitem_get_draw_transform(item, &xform);\n\n\t\t\tvec3_set(&t[0], sib.tl.x, sib.tl.y, 0.0f);\n\t\t\tvec3_set(&t[1], sib.tl.x, sib.br.y, 0.0f);\n\t\t\tvec3_set(&t[2], sib.br.x, sib.tl.y, 0.0f);\n\t\t\tvec3_set(&t[3], sib.br.x, sib.br.y, 0.0f);\n\t\t\tvec3_transform(&t[0], &t[0], &xform);\n\t\t\tvec3_transform(&t[1], &t[1], &xform);\n\t\t\tvec3_transform(&t[2], &t[2], &xform);\n\t\t\tvec3_transform(&t[3], &t[3], &xform);\n\t\t\tadd_bounds();\n\t\t}\n\t}\n\tif (!obs_sceneitem_selected(item))\n\t\treturn true;\n\n\tmatrix4 boxTransform;\n\tobs_sceneitem_get_box_transform(item, &boxTransform);\n\n\tt[0] = GetTransformedPos(0.0f, 0.0f, boxTransform);\n\tt[1] = GetTransformedPos(1.0f, 0.0f, boxTransform);\n\tt[2] = GetTransformedPos(0.0f, 1.0f, boxTransform);\n\tt[3] = GetTransformedPos(1.0f, 1.0f, boxTransform);\n\tadd_bounds();\n\n\treturn true;\n}\n\nstruct OffsetData {\n\tfloat clampDist;\n\tvec3 tl, br, offset;\n};\n\nstatic bool GetSourceSnapOffset(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tOffsetData *data = static_cast<OffsetData *>(param);\n\n\tif (obs_sceneitem_selected(item))\n\t\treturn true;\n\n\tmatrix4 boxTransform;\n\tobs_sceneitem_get_box_transform(item, &boxTransform);\n\n\tvec3 t[4] = {GetTransformedPos(0.0f, 0.0f, boxTransform), GetTransformedPos(1.0f, 0.0f, boxTransform),\n\t\t     GetTransformedPos(0.0f, 1.0f, boxTransform), GetTransformedPos(1.0f, 1.0f, boxTransform)};\n\n\tbool first = true;\n\tvec3 tl, br;\n\tvec3_zero(&tl);\n\tvec3_zero(&br);\n\tfor (const vec3 &v : t) {\n\t\tif (first) {\n\t\t\tvec3_copy(&tl, &v);\n\t\t\tvec3_copy(&br, &v);\n\t\t\tfirst = false;\n\t\t} else {\n\t\t\tvec3_min(&tl, &tl, &v);\n\t\t\tvec3_max(&br, &br, &v);\n\t\t}\n\t}\n\n\t// Snap to other source edges\n#define EDGE_SNAP(l, r, x, y)                                                                         \\\n\tdo {                                                                                          \\\n\t\tdouble dist = fabsf(l.x - data->r.x);                                                 \\\n\t\tif (dist < data->clampDist && fabsf(data->offset.x) < EPSILON && data->tl.y < br.y && \\\n\t\t    data->br.y > tl.y && (fabsf(data->offset.x) > dist || data->offset.x < EPSILON))  \\\n\t\t\tdata->offset.x = l.x - data->r.x;                                             \\\n\t} while (false)\n\n\tEDGE_SNAP(tl, br, x, y);\n\tEDGE_SNAP(tl, br, y, x);\n\tEDGE_SNAP(br, tl, x, y);\n\tEDGE_SNAP(br, tl, y, x);\n#undef EDGE_SNAP\n\n\treturn true;\n}\n\nvoid OBSBasicPreview::SnapItemMovement(vec2 &offset)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tOBSScene scene = main->GetCurrentScene();\n\n\tSelectedItemBounds data;\n\tobs_scene_enum_items(scene, AddItemBounds, &data);\n\n\tdata.tl.x += offset.x;\n\tdata.tl.y += offset.y;\n\tdata.br.x += offset.x;\n\tdata.br.y += offset.y;\n\n\tvec3 snapOffset = GetSnapOffset(data.tl, data.br);\n\n\tconst bool snap = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SnappingEnabled\");\n\tconst bool sourcesSnap = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SourceSnapping\");\n\tif (snap == false)\n\t\treturn;\n\tif (sourcesSnap == false) {\n\t\toffset.x += snapOffset.x;\n\t\toffset.y += snapOffset.y;\n\t\treturn;\n\t}\n\n\tconst float clampDist =\n\t\tconfig_get_double(App()->GetUserConfig(), \"BasicWindow\", \"SnapDistance\") / main->previewScale;\n\n\tOffsetData offsetData;\n\toffsetData.clampDist = clampDist;\n\toffsetData.tl = data.tl;\n\toffsetData.br = data.br;\n\tvec3_copy(&offsetData.offset, &snapOffset);\n\n\tobs_scene_enum_items(scene, GetSourceSnapOffset, &offsetData);\n\n\tif (fabsf(offsetData.offset.x) > EPSILON || fabsf(offsetData.offset.y) > EPSILON) {\n\t\toffset.x += offsetData.offset.x;\n\t\toffset.y += offsetData.offset.y;\n\t} else {\n\t\toffset.x += snapOffset.x;\n\t\toffset.y += snapOffset.y;\n\t}\n}\n\nstatic bool move_items(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tif (obs_sceneitem_locked(item))\n\t\treturn true;\n\n\tbool selected = obs_sceneitem_selected(item);\n\tvec2 *offset = static_cast<vec2 *>(param);\n\n\tif (obs_sceneitem_is_group(item) && !selected) {\n\t\tmatrix4 transform;\n\t\tvec3 new_offset;\n\t\tvec3_set(&new_offset, offset->x, offset->y, 0.0f);\n\n\t\tobs_sceneitem_get_draw_transform(item, &transform);\n\t\tvec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f);\n\t\tmatrix4_inv(&transform, &transform);\n\t\tvec3_transform(&new_offset, &new_offset, &transform);\n\t\tobs_sceneitem_group_enum_items(item, move_items, &new_offset);\n\t}\n\n\tif (selected) {\n\t\tvec2 pos;\n\t\tobs_sceneitem_get_pos(item, &pos);\n\t\tvec2_add(&pos, &pos, offset);\n\t\tobs_sceneitem_set_pos(item, &pos);\n\t}\n\n\treturn true;\n}\n\nvoid OBSBasicPreview::MoveItems(const vec2 &pos)\n{\n\tQt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();\n\tOBSBasic *main = OBSBasic::Get();\n\tOBSScene scene = main->GetCurrentScene();\n\n\tvec2 offset, moveOffset;\n\tvec2_sub(&offset, &pos, &startPos);\n\tvec2_sub(&moveOffset, &offset, &lastMoveOffset);\n\n\tif (!(modifiers & Qt::ControlModifier))\n\t\tSnapItemMovement(moveOffset);\n\n\tvec2_add(&lastMoveOffset, &lastMoveOffset, &moveOffset);\n\n\tobs_scene_enum_items(scene, move_items, &moveOffset);\n}\n\nstatic bool CounterClockwise(float x1, float x2, float x3, float y1, float y2, float y3)\n{\n\treturn (y3 - y1) * (x2 - x1) > (y2 - y1) * (x3 - x1);\n}\n\nstatic bool IntersectLine(float x1, float x2, float x3, float x4, float y1, float y2, float y3, float y4)\n{\n\tbool a = CounterClockwise(x1, x2, x3, y1, y2, y3);\n\tbool b = CounterClockwise(x1, x2, x4, y1, y2, y4);\n\tbool c = CounterClockwise(x3, x4, x1, y3, y4, y1);\n\tbool d = CounterClockwise(x3, x4, x2, y3, y4, y2);\n\n\treturn (a != b) && (c != d);\n}\n\nstatic bool IntersectBox(matrix4 transform, float x1, float x2, float y1, float y2)\n{\n\tfloat x3, x4, y3, y4;\n\n\tx3 = transform.t.x;\n\ty3 = transform.t.y;\n\tx4 = x3 + transform.x.x;\n\ty4 = y3 + transform.x.y;\n\n\tif (IntersectLine(x1, x1, x3, x4, y1, y2, y3, y4) || IntersectLine(x1, x2, x3, x4, y1, y1, y3, y4) ||\n\t    IntersectLine(x2, x2, x3, x4, y1, y2, y3, y4) || IntersectLine(x1, x2, x3, x4, y2, y2, y3, y4))\n\t\treturn true;\n\n\tx4 = x3 + transform.y.x;\n\ty4 = y3 + transform.y.y;\n\n\tif (IntersectLine(x1, x1, x3, x4, y1, y2, y3, y4) || IntersectLine(x1, x2, x3, x4, y1, y1, y3, y4) ||\n\t    IntersectLine(x2, x2, x3, x4, y1, y2, y3, y4) || IntersectLine(x1, x2, x3, x4, y2, y2, y3, y4))\n\t\treturn true;\n\n\tx3 = transform.t.x + transform.x.x;\n\ty3 = transform.t.y + transform.x.y;\n\tx4 = x3 + transform.y.x;\n\ty4 = y3 + transform.y.y;\n\n\tif (IntersectLine(x1, x1, x3, x4, y1, y2, y3, y4) || IntersectLine(x1, x2, x3, x4, y1, y1, y3, y4) ||\n\t    IntersectLine(x2, x2, x3, x4, y1, y2, y3, y4) || IntersectLine(x1, x2, x3, x4, y2, y2, y3, y4))\n\t\treturn true;\n\n\tx3 = transform.t.x + transform.y.x;\n\ty3 = transform.t.y + transform.y.y;\n\tx4 = x3 + transform.x.x;\n\ty4 = y3 + transform.x.y;\n\n\tif (IntersectLine(x1, x1, x3, x4, y1, y2, y3, y4) || IntersectLine(x1, x2, x3, x4, y1, y1, y3, y4) ||\n\t    IntersectLine(x2, x2, x3, x4, y1, y2, y3, y4) || IntersectLine(x1, x2, x3, x4, y2, y2, y3, y4))\n\t\treturn true;\n\n\treturn false;\n}\n#undef PI\n\nbool OBSBasicPreview::FindSelected(obs_scene_t *, obs_sceneitem_t *item, void *param)\n{\n\tSceneFindBoxData *data = static_cast<SceneFindBoxData *>(param);\n\n\tif (obs_sceneitem_selected(item))\n\t\tdata->sceneItems.push_back(item);\n\n\treturn true;\n}\n\nstatic bool FindItemsInBox(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tSceneFindBoxData *data = static_cast<SceneFindBoxData *>(param);\n\tmatrix4 transform;\n\tmatrix4 invTransform;\n\tvec3 transformedPos;\n\tvec3 pos3;\n\tvec3 pos3_;\n\n\tvec2 pos_min, pos_max;\n\tvec2_min(&pos_min, &data->startPos, &data->pos);\n\tvec2_max(&pos_max, &data->startPos, &data->pos);\n\n\tconst float x1 = pos_min.x;\n\tconst float x2 = pos_max.x;\n\tconst float y1 = pos_min.y;\n\tconst float y2 = pos_max.y;\n\n\tif (!SceneItemHasVideo(item))\n\t\treturn true;\n\tif (obs_sceneitem_locked(item))\n\t\treturn true;\n\tif (!obs_sceneitem_visible(item))\n\t\treturn true;\n\n\tvec3_set(&pos3, data->pos.x, data->pos.y, 0.0f);\n\n\tobs_sceneitem_get_box_transform(item, &transform);\n\n\tmatrix4_inv(&invTransform, &transform);\n\tvec3_transform(&transformedPos, &pos3, &invTransform);\n\tvec3_transform(&pos3_, &transformedPos, &transform);\n\n\tif (CloseFloat(pos3.x, pos3_.x) && CloseFloat(pos3.y, pos3_.y) && transformedPos.x >= 0.0f &&\n\t    transformedPos.x <= 1.0f && transformedPos.y >= 0.0f && transformedPos.y <= 1.0f) {\n\n\t\tdata->sceneItems.push_back(item);\n\t\treturn true;\n\t}\n\n\tif (transform.t.x > x1 && transform.t.x < x2 && transform.t.y > y1 && transform.t.y < y2) {\n\n\t\tdata->sceneItems.push_back(item);\n\t\treturn true;\n\t}\n\n\tif (transform.t.x + transform.x.x > x1 && transform.t.x + transform.x.x < x2 &&\n\t    transform.t.y + transform.x.y > y1 && transform.t.y + transform.x.y < y2) {\n\n\t\tdata->sceneItems.push_back(item);\n\t\treturn true;\n\t}\n\n\tif (transform.t.x + transform.y.x > x1 && transform.t.x + transform.y.x < x2 &&\n\t    transform.t.y + transform.y.y > y1 && transform.t.y + transform.y.y < y2) {\n\n\t\tdata->sceneItems.push_back(item);\n\t\treturn true;\n\t}\n\n\tif (transform.t.x + transform.x.x + transform.y.x > x1 && transform.t.x + transform.x.x + transform.y.x < x2 &&\n\t    transform.t.y + transform.x.y + transform.y.y > y1 && transform.t.y + transform.x.y + transform.y.y < y2) {\n\n\t\tdata->sceneItems.push_back(item);\n\t\treturn true;\n\t}\n\n\tif (transform.t.x + 0.5 * (transform.x.x + transform.y.x) > x1 &&\n\t    transform.t.x + 0.5 * (transform.x.x + transform.y.x) < x2 &&\n\t    transform.t.y + 0.5 * (transform.x.y + transform.y.y) > y1 &&\n\t    transform.t.y + 0.5 * (transform.x.y + transform.y.y) < y2) {\n\n\t\tdata->sceneItems.push_back(item);\n\t\treturn true;\n\t}\n\n\tif (IntersectBox(transform, x1, x2, y1, y2)) {\n\t\tdata->sceneItems.push_back(item);\n\t\treturn true;\n\t}\n\n\treturn true;\n}\n\nvoid OBSBasicPreview::BoxItems(const vec2 &startPos, const vec2 &pos)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tOBSScene scene = main->GetCurrentScene();\n\tif (!scene)\n\t\treturn;\n\n\tif (cursor().shape() != Qt::CrossCursor)\n\t\tsetCursor(Qt::CrossCursor);\n\n\tSceneFindBoxData data(startPos, pos);\n\tobs_scene_enum_items(scene, FindItemsInBox, &data);\n\n\tstd::lock_guard<std::mutex> lock(selectMutex);\n\thoveredPreviewItems = data.sceneItems;\n}\n\nvec3 OBSBasicPreview::CalculateStretchPos(const vec3 &tl, const vec3 &br)\n{\n\tuint32_t alignment = obs_sceneitem_get_alignment(stretchItem);\n\tvec3 pos;\n\n\tvec3_zero(&pos);\n\n\tif (alignment & OBS_ALIGN_LEFT)\n\t\tpos.x = tl.x;\n\telse if (alignment & OBS_ALIGN_RIGHT)\n\t\tpos.x = br.x;\n\telse\n\t\tpos.x = (br.x - tl.x) * 0.5f + tl.x;\n\n\tif (alignment & OBS_ALIGN_TOP)\n\t\tpos.y = tl.y;\n\telse if (alignment & OBS_ALIGN_BOTTOM)\n\t\tpos.y = br.y;\n\telse\n\t\tpos.y = (br.y - tl.y) * 0.5f + tl.y;\n\n\treturn pos;\n}\n\nvoid OBSBasicPreview::ClampAspect(vec3 &tl, vec3 &br, vec2 &size, const vec2 &baseSize)\n{\n\tfloat baseAspect = baseSize.x / baseSize.y;\n\tfloat aspect = size.x / size.y;\n\tuint32_t stretchFlags = (uint32_t)stretchHandle;\n\n\tif (stretchHandle == ItemHandle::TopLeft || stretchHandle == ItemHandle::TopRight ||\n\t    stretchHandle == ItemHandle::BottomLeft || stretchHandle == ItemHandle::BottomRight) {\n\t\tif (aspect < baseAspect) {\n\t\t\tif ((size.y >= 0.0f && size.x >= 0.0f) || (size.y <= 0.0f && size.x <= 0.0f))\n\t\t\t\tsize.x = size.y * baseAspect;\n\t\t\telse\n\t\t\t\tsize.x = size.y * baseAspect * -1.0f;\n\t\t} else {\n\t\t\tif ((size.y >= 0.0f && size.x >= 0.0f) || (size.y <= 0.0f && size.x <= 0.0f))\n\t\t\t\tsize.y = size.x / baseAspect;\n\t\t\telse\n\t\t\t\tsize.y = size.x / baseAspect * -1.0f;\n\t\t}\n\n\t} else if (stretchHandle == ItemHandle::TopCenter || stretchHandle == ItemHandle::BottomCenter) {\n\t\tif ((size.y >= 0.0f && size.x >= 0.0f) || (size.y <= 0.0f && size.x <= 0.0f))\n\t\t\tsize.x = size.y * baseAspect;\n\t\telse\n\t\t\tsize.x = size.y * baseAspect * -1.0f;\n\n\t} else if (stretchHandle == ItemHandle::CenterLeft || stretchHandle == ItemHandle::CenterRight) {\n\t\tif ((size.y >= 0.0f && size.x >= 0.0f) || (size.y <= 0.0f && size.x <= 0.0f))\n\t\t\tsize.y = size.x / baseAspect;\n\t\telse\n\t\t\tsize.y = size.x / baseAspect * -1.0f;\n\t}\n\n\tsize.x = std::round(size.x);\n\tsize.y = std::round(size.y);\n\n\tif (stretchFlags & ITEM_LEFT)\n\t\ttl.x = br.x - size.x;\n\telse if (stretchFlags & ITEM_RIGHT)\n\t\tbr.x = tl.x + size.x;\n\n\tif (stretchFlags & ITEM_TOP)\n\t\ttl.y = br.y - size.y;\n\telse if (stretchFlags & ITEM_BOTTOM)\n\t\tbr.y = tl.y + size.y;\n}\n\nvoid OBSBasicPreview::SnapStretchingToScreen(vec3 &tl, vec3 &br)\n{\n\tuint32_t stretchFlags = (uint32_t)stretchHandle;\n\tvec3 newTL = GetTransformedPos(tl.x, tl.y, itemToScreen);\n\tvec3 newTR = GetTransformedPos(br.x, tl.y, itemToScreen);\n\tvec3 newBL = GetTransformedPos(tl.x, br.y, itemToScreen);\n\tvec3 newBR = GetTransformedPos(br.x, br.y, itemToScreen);\n\tvec3 boundingTL;\n\tvec3 boundingBR;\n\n\tvec3_copy(&boundingTL, &newTL);\n\tvec3_min(&boundingTL, &boundingTL, &newTR);\n\tvec3_min(&boundingTL, &boundingTL, &newBL);\n\tvec3_min(&boundingTL, &boundingTL, &newBR);\n\n\tvec3_copy(&boundingBR, &newTL);\n\tvec3_max(&boundingBR, &boundingBR, &newTR);\n\tvec3_max(&boundingBR, &boundingBR, &newBL);\n\tvec3_max(&boundingBR, &boundingBR, &newBR);\n\n\tvec3 offset = GetSnapOffset(boundingTL, boundingBR);\n\tvec3_add(&offset, &offset, &newTL);\n\tvec3_transform(&offset, &offset, &screenToItem);\n\tvec3_sub(&offset, &offset, &tl);\n\n\tif (stretchFlags & ITEM_LEFT)\n\t\ttl.x += offset.x;\n\telse if (stretchFlags & ITEM_RIGHT)\n\t\tbr.x += offset.x;\n\n\tif (stretchFlags & ITEM_TOP)\n\t\ttl.y += offset.y;\n\telse if (stretchFlags & ITEM_BOTTOM)\n\t\tbr.y += offset.y;\n}\n\nstatic float maxfunc(float x, float y)\n{\n\treturn x > y ? x : y;\n}\n\nstatic float minfunc(float x, float y)\n{\n\treturn x < y ? x : y;\n}\n\nvoid OBSBasicPreview::CropItem(const vec2 &pos)\n{\n\tobs_bounds_type boundsType = obs_sceneitem_get_bounds_type(stretchItem);\n\tuint32_t stretchFlags = (uint32_t)stretchHandle;\n\tuint32_t align = obs_sceneitem_get_alignment(stretchItem);\n\tvec3 tl, br, pos3;\n\n\tvec3_zero(&tl);\n\tvec3_set(&br, stretchItemSize.x, stretchItemSize.y, 0.0f);\n\n\tvec3_set(&pos3, pos.x, pos.y, 0.0f);\n\tvec3_transform(&pos3, &pos3, &screenToItem);\n\n\tobs_sceneitem_crop crop = startCrop;\n\tvec2 scale, rawscale;\n\n\tobs_sceneitem_get_scale(stretchItem, &rawscale);\n\tvec2_set(&scale, boundsType == OBS_BOUNDS_NONE ? rawscale.x : fabsf(rawscale.x),\n\t\t boundsType == OBS_BOUNDS_NONE ? rawscale.y : fabsf(rawscale.y));\n\n\tvec2 max_tl;\n\tvec2 max_br;\n\n\tvec2_set(&max_tl, float(-crop.left) * scale.x, float(-crop.top) * scale.y);\n\tvec2_set(&max_br, stretchItemSize.x + crop.right * scale.x, stretchItemSize.y + crop.bottom * scale.y);\n\n\ttypedef std::function<float(float, float)> minmax_func_t;\n\n\tminmax_func_t min_x = scale.x < 0.0f && boundsType == OBS_BOUNDS_NONE ? maxfunc : minfunc;\n\tminmax_func_t min_y = scale.y < 0.0f && boundsType == OBS_BOUNDS_NONE ? maxfunc : minfunc;\n\tminmax_func_t max_x = scale.x < 0.0f && boundsType == OBS_BOUNDS_NONE ? minfunc : maxfunc;\n\tminmax_func_t max_y = scale.y < 0.0f && boundsType == OBS_BOUNDS_NONE ? minfunc : maxfunc;\n\n\tpos3.x = min_x(pos3.x, max_br.x);\n\tpos3.x = max_x(pos3.x, max_tl.x);\n\tpos3.y = min_y(pos3.y, max_br.y);\n\tpos3.y = max_y(pos3.y, max_tl.y);\n\n\tif (stretchFlags & ITEM_LEFT) {\n\t\tfloat maxX = stretchItemSize.x - (2.0 * scale.x);\n\t\tpos3.x = tl.x = min_x(pos3.x, maxX);\n\n\t} else if (stretchFlags & ITEM_RIGHT) {\n\t\tfloat minX = (2.0 * scale.x);\n\t\tpos3.x = br.x = max_x(pos3.x, minX);\n\t}\n\n\tif (stretchFlags & ITEM_TOP) {\n\t\tfloat maxY = stretchItemSize.y - (2.0 * scale.y);\n\t\tpos3.y = tl.y = min_y(pos3.y, maxY);\n\n\t} else if (stretchFlags & ITEM_BOTTOM) {\n\t\tfloat minY = (2.0 * scale.y);\n\t\tpos3.y = br.y = max_y(pos3.y, minY);\n\t}\n\n#define ALIGN_X (ITEM_LEFT | ITEM_RIGHT)\n#define ALIGN_Y (ITEM_TOP | ITEM_BOTTOM)\n\tvec3 newPos;\n\tvec3_zero(&newPos);\n\n\tuint32_t align_x = (align & ALIGN_X);\n\tuint32_t align_y = (align & ALIGN_Y);\n\tif (align_x == (stretchFlags & ALIGN_X) && align_x != 0)\n\t\tnewPos.x = pos3.x;\n\telse if (align & ITEM_RIGHT)\n\t\tnewPos.x = stretchItemSize.x;\n\telse if (!(align & ITEM_LEFT))\n\t\tnewPos.x = stretchItemSize.x * 0.5f;\n\n\tif (align_y == (stretchFlags & ALIGN_Y) && align_y != 0)\n\t\tnewPos.y = pos3.y;\n\telse if (align & ITEM_BOTTOM)\n\t\tnewPos.y = stretchItemSize.y;\n\telse if (!(align & ITEM_TOP))\n\t\tnewPos.y = stretchItemSize.y * 0.5f;\n#undef ALIGN_X\n#undef ALIGN_Y\n\n\tcrop = startCrop;\n\n\tif (stretchFlags & ITEM_LEFT)\n\t\tcrop.left += int(std::round(tl.x / scale.x));\n\telse if (stretchFlags & ITEM_RIGHT)\n\t\tcrop.right += int(std::round((stretchItemSize.x - br.x) / scale.x));\n\n\tif (stretchFlags & ITEM_TOP)\n\t\tcrop.top += int(std::round(tl.y / scale.y));\n\telse if (stretchFlags & ITEM_BOTTOM)\n\t\tcrop.bottom += int(std::round((stretchItemSize.y - br.y) / scale.y));\n\n\tvec3_transform(&newPos, &newPos, &itemToScreen);\n\tnewPos.x = std::round(newPos.x);\n\tnewPos.y = std::round(newPos.y);\n\n#if 0\n\tvec3 curPos;\n\tvec3_zero(&curPos);\n\tobs_sceneitem_get_pos(stretchItem, (vec2*)&curPos);\n\tblog(LOG_DEBUG, \"curPos {%d, %d} - newPos {%d, %d}\",\n\t\t\tint(curPos.x), int(curPos.y),\n\t\t\tint(newPos.x), int(newPos.y));\n\tblog(LOG_DEBUG, \"crop {%d, %d, %d, %d}\",\n\t\t\tcrop.left, crop.top,\n\t\t\tcrop.right, crop.bottom);\n#endif\n\n\tobs_sceneitem_defer_update_begin(stretchItem);\n\tobs_sceneitem_set_crop(stretchItem, &crop);\n\tif (boundsType == OBS_BOUNDS_NONE)\n\t\tobs_sceneitem_set_pos(stretchItem, (vec2 *)&newPos);\n\tobs_sceneitem_defer_update_end(stretchItem);\n}\n\nvoid OBSBasicPreview::StretchItem(const vec2 &pos)\n{\n\tQt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();\n\tobs_bounds_type boundsType = obs_sceneitem_get_bounds_type(stretchItem);\n\tuint32_t stretchFlags = (uint32_t)stretchHandle;\n\tbool shiftDown = (modifiers & Qt::ShiftModifier);\n\tvec3 tl, br, pos3;\n\n\tvec3_zero(&tl);\n\tvec3_set(&br, stretchItemSize.x, stretchItemSize.y, 0.0f);\n\n\tvec3_set(&pos3, pos.x, pos.y, 0.0f);\n\tvec3_transform(&pos3, &pos3, &screenToItem);\n\n\tif (stretchFlags & ITEM_LEFT)\n\t\ttl.x = pos3.x;\n\telse if (stretchFlags & ITEM_RIGHT)\n\t\tbr.x = pos3.x;\n\n\tif (stretchFlags & ITEM_TOP)\n\t\ttl.y = pos3.y;\n\telse if (stretchFlags & ITEM_BOTTOM)\n\t\tbr.y = pos3.y;\n\n\tif (!(modifiers & Qt::ControlModifier))\n\t\tSnapStretchingToScreen(tl, br);\n\n\tobs_source_t *source = obs_sceneitem_get_source(stretchItem);\n\n\tuint32_t source_cx = obs_source_get_width(source);\n\tuint32_t source_cy = obs_source_get_height(source);\n\n\t/* if the source's internal size has been set to 0 for whatever reason\n\t * while resizing, do not update transform, otherwise source will be\n\t * stuck invisible until a complete transform reset */\n\tif (!source_cx || !source_cy)\n\t\treturn;\n\n\tvec2 baseSize;\n\tvec2_set(&baseSize, float(source_cx), float(source_cy));\n\n\tvec2 size;\n\tvec2_set(&size, br.x - tl.x, br.y - tl.y);\n\n\tif (boundsType != OBS_BOUNDS_NONE) {\n\t\tif (shiftDown)\n\t\t\tClampAspect(tl, br, size, baseSize);\n\n\t\tif (tl.x > br.x)\n\t\t\tstd::swap(tl.x, br.x);\n\t\tif (tl.y > br.y)\n\t\t\tstd::swap(tl.y, br.y);\n\n\t\tvec2_abs(&size, &size);\n\n\t\tobs_sceneitem_set_bounds(stretchItem, &size);\n\t} else {\n\t\tobs_sceneitem_crop crop;\n\t\tobs_sceneitem_get_crop(stretchItem, &crop);\n\n\t\tbaseSize.x -= float(crop.left + crop.right);\n\t\tbaseSize.y -= float(crop.top + crop.bottom);\n\n\t\tif (!shiftDown)\n\t\t\tClampAspect(tl, br, size, baseSize);\n\n\t\tvec2_div(&size, &size, &baseSize);\n\t\tobs_sceneitem_set_scale(stretchItem, &size);\n\t}\n\n\tpos3 = CalculateStretchPos(tl, br);\n\tvec3_transform(&pos3, &pos3, &itemToScreen);\n\n\tvec2 newPos;\n\tvec2_set(&newPos, std::round(pos3.x), std::round(pos3.y));\n\tobs_sceneitem_set_pos(stretchItem, &newPos);\n}\n\nvoid OBSBasicPreview::RotateItem(const vec2 &pos)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tOBSScene scene = main->GetCurrentScene();\n\tQt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();\n\tbool shiftDown = (modifiers & Qt::ShiftModifier);\n\tbool ctrlDown = (modifiers & Qt::ControlModifier);\n\n\tvec2 pos2;\n\tvec2_copy(&pos2, &pos);\n\n\tfloat angle = atan2(pos2.y - rotatePoint.y, pos2.x - rotatePoint.x) + RAD(90);\n\n#define ROT_SNAP(rot, thresh)                      \\\n\tif (abs(angle - RAD(rot)) < RAD(thresh)) { \\\n\t\tangle = RAD(rot);                  \\\n\t}\n\n\tif (shiftDown) {\n\t\tfor (int i = 0; i <= 360 / 15; i++) {\n\t\t\tROT_SNAP(i * 15 - 90, 7.5);\n\t\t}\n\t} else if (!ctrlDown) {\n\t\tROT_SNAP(rotateAngle, 5)\n\n\t\tROT_SNAP(-90, 5)\n\t\tROT_SNAP(-45, 5)\n\t\tROT_SNAP(0, 5)\n\t\tROT_SNAP(45, 5)\n\t\tROT_SNAP(90, 5)\n\t\tROT_SNAP(135, 5)\n\t\tROT_SNAP(180, 5)\n\t\tROT_SNAP(225, 5)\n\t\tROT_SNAP(270, 5)\n\t\tROT_SNAP(315, 5)\n\t}\n#undef ROT_SNAP\n\n\tvec2 pos3;\n\tvec2_copy(&pos3, &offsetPoint);\n\tRotatePos(&pos3, angle);\n\tpos3.x += rotatePoint.x;\n\tpos3.y += rotatePoint.y;\n\n\tobs_sceneitem_set_rot(stretchItem, DEG(angle));\n\tobs_sceneitem_set_pos(stretchItem, &pos3);\n}\n\nvoid OBSBasicPreview::mouseMoveEvent(QMouseEvent *event)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tchanged = true;\n\n\tQPointF qtPos = event->position();\n\n\tfloat pixelRatio = main->GetDevicePixelRatio();\n\n\tif (scrollMode && event->buttons() == Qt::LeftButton) {\n\t\tscrollingOffset.x += pixelRatio * (qtPos.x() - scrollingFrom.x);\n\t\tscrollingOffset.y += pixelRatio * (qtPos.y() - scrollingFrom.y);\n\t\tscrollingFrom.x = qtPos.x();\n\t\tscrollingFrom.y = qtPos.y();\n\t\temit DisplayResized();\n\t\treturn;\n\t}\n\n\tif (locked)\n\t\treturn;\n\n\tbool updateCursor = false;\n\n\tif (mouseDown) {\n\t\tvec2 pos = GetMouseEventPos(event);\n\n\t\tif (!mouseMoved && !mouseOverItems && stretchHandle == ItemHandle::None) {\n\t\t\tProcessClick(startPos);\n\t\t\tmouseOverItems = SelectedAtPos(startPos);\n\t\t}\n\n\t\tpos.x = std::round(pos.x);\n\t\tpos.y = std::round(pos.y);\n\n\t\tif (stretchHandle != ItemHandle::None) {\n\t\t\tif (obs_sceneitem_locked(stretchItem))\n\t\t\t\treturn;\n\n\t\t\tselectionBox = false;\n\n\t\t\tOBSScene scene = main->GetCurrentScene();\n\t\t\tobs_sceneitem_t *group = obs_sceneitem_get_group(scene, stretchItem);\n\t\t\tif (group) {\n\t\t\t\tvec3 group_pos;\n\t\t\t\tvec3_set(&group_pos, pos.x, pos.y, 0.0f);\n\t\t\t\tvec3_transform(&group_pos, &group_pos, &invGroupTransform);\n\t\t\t\tpos.x = group_pos.x;\n\t\t\t\tpos.y = group_pos.y;\n\t\t\t}\n\n\t\t\tif (stretchHandle == ItemHandle::Rot) {\n\t\t\t\tRotateItem(pos);\n\t\t\t\tsetCursor(Qt::ClosedHandCursor);\n\t\t\t} else if (cropping)\n\t\t\t\tCropItem(pos);\n\t\t\telse\n\t\t\t\tStretchItem(pos);\n\n\t\t} else if (mouseOverItems) {\n\t\t\tif (cursor().shape() != Qt::SizeAllCursor)\n\t\t\t\tsetCursor(Qt::SizeAllCursor);\n\t\t\tselectionBox = false;\n\t\t\tMoveItems(pos);\n\t\t} else {\n\t\t\tselectionBox = true;\n\t\t\tif (!mouseMoved)\n\t\t\t\tDoSelect(startPos);\n\t\t\tBoxItems(startPos, pos);\n\t\t}\n\n\t\tmouseMoved = true;\n\t\tmousePos = pos;\n\t} else {\n\t\tvec2 pos = GetMouseEventPos(event);\n\t\tOBSSceneItem item = GetItemAtPos(pos, true);\n\n\t\tstd::lock_guard<std::mutex> lock(selectMutex);\n\t\thoveredPreviewItems.clear();\n\t\thoveredPreviewItems.push_back(item);\n\n\t\tif (!mouseMoved && hoveredPreviewItems.size() > 0) {\n\t\t\tmousePos = pos;\n\t\t\tOBSBasic *main = OBSBasic::Get();\n\t\t\tfloat scale = main->GetDevicePixelRatio();\n\t\t\tfloat x = qtPos.x() - main->previewX / scale;\n\t\t\tfloat y = qtPos.y() - main->previewY / scale;\n\t\t\tvec2_set(&startPos, x, y);\n\t\t\tupdateCursor = true;\n\t\t}\n\t}\n\n\tif (updateCursor) {\n\t\tGetStretchHandleData(startPos, true);\n\t\tuint32_t stretchFlags = (uint32_t)stretchHandle;\n\t\tUpdateCursor(stretchFlags);\n\t}\n}\n\nvoid OBSBasicPreview::leaveEvent(QEvent *)\n{\n\tstd::lock_guard<std::mutex> lock(selectMutex);\n\tif (!selectionBox)\n\t\thoveredPreviewItems.clear();\n}\n\nstatic void DrawLine(float x1, float y1, float x2, float y2, float thickness, vec2 scale)\n{\n\tfloat cx;\n\tfloat cy;\n\tvec2 thickness_relative;\n\tbool is_x_axis = !close_float(x1, x2, TINY_EPSILON) || close_float(y1, y2, TINY_EPSILON);\n\n\tvec2_abs(&scale, &scale);\n\n\tthickness_relative.x = thickness / scale.x;\n\tthickness_relative.y = thickness / scale.y;\n\n\tif (is_x_axis) {\n\t\tcx = fabsf(x2 - x1) + thickness_relative.x;\n\t\tcy = thickness_relative.y;\n\t} else {\n\t\tcy = fabsf(y2 - y1) + thickness_relative.y;\n\t\tcx = thickness_relative.x;\n\t}\n\n\tx1 -= thickness_relative.x * 0.5f;\n\ty1 -= thickness_relative.y * 0.5f;\n\n\tgs_matrix_push();\n\tgs_matrix_translate3f(x1, y1, 0.0f);\n\tgs_draw_quadf(NULL, 0, cx, cy);\n\tgs_matrix_pop();\n}\n\nstatic void DrawSquareAtPos(float x, float y, float pixelRatio)\n{\n\tstruct vec3 pos;\n\tvec3_set(&pos, x, y, 0.0f);\n\n\tstruct matrix4 matrix;\n\tgs_matrix_get(&matrix);\n\tvec3_transform(&pos, &pos, &matrix);\n\n\tgs_matrix_push();\n\tgs_matrix_identity();\n\tgs_matrix_translate(&pos);\n\n\tgs_matrix_translate3f(-HANDLE_RADIUS * pixelRatio, -HANDLE_RADIUS * pixelRatio, 0.0f);\n\tgs_matrix_scale3f(HANDLE_RADIUS * pixelRatio * 2, HANDLE_RADIUS * pixelRatio * 2, 1.0f);\n\tgs_draw(GS_TRISTRIP, 0, 0);\n\n\tgs_matrix_pop();\n}\n\nstatic void DrawRotationHandle(gs_vertbuffer_t *circle, float rot, float pixelRatio, bool invert)\n{\n\tstruct vec3 pos;\n\tvec3_set(&pos, 0.5f, invert ? 1.0f : 0.0f, 0.0f);\n\n\tstruct matrix4 matrix;\n\tgs_matrix_get(&matrix);\n\tvec3_transform(&pos, &pos, &matrix);\n\n\tconstexpr float thickness = 0.68f;\n\n\tgs_matrix_push();\n\tgs_matrix_identity();\n\tgs_matrix_translate(&pos);\n\n\tgs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD(rot));\n\tgs_matrix_translate3f(-HANDLE_RADIUS * 1.5 * pixelRatio, -HANDLE_RADIUS * 1.5 * pixelRatio, 0.0f);\n\tgs_matrix_scale3f(HANDLE_RADIUS * 3 * pixelRatio, HANDLE_RADIUS * 3 * pixelRatio, 1.0f);\n\n\tgs_matrix_push();\n\tgs_matrix_translate3f(0.5f - thickness / 2.0f / HANDLE_RADIUS, -2.0f, 0.0f);\n\tgs_draw_quadf(NULL, 0, thickness / HANDLE_RADIUS, 2.5f);\n\tgs_matrix_pop();\n\n\tgs_matrix_translate3f(0.0f, -HANDLE_RADIUS * 2 / 3, 0.0f);\n\n\tgs_load_vertexbuffer(circle);\n\tgs_draw(GS_TRISTRIP, 0, 0);\n\n\tgs_matrix_pop();\n}\n\nvoid OBSBasicPreview::DrawStripedLine(float x1, float y1, float x2, float y2, float thickness, vec2 scale)\n{\n\tfloat cx;\n\tfloat cy;\n\tfloat dist;\n\tfloat dist_scaled;\n\tvec2 thickness_relative;\n\tbool is_x_axis = !close_float(x1, x2, TINY_EPSILON) || close_float(y1, y2, TINY_EPSILON);\n\n\tvec2_abs(&scale, &scale);\n\n\tthickness_relative.x = thickness / scale.x;\n\tthickness_relative.y = thickness / scale.y;\n\n\tif (is_x_axis) {\n\t\tdist = fabsf(x2 - x1);\n\t\tdist_scaled = dist * scale.x;\n\t\tcx = dist + thickness_relative.x;\n\t\tcy = thickness_relative.y;\n\t} else {\n\t\tdist = fabsf(y2 - y1);\n\t\tdist_scaled = dist * scale.y;\n\t\tcy = dist + thickness_relative.y;\n\t\tcx = thickness_relative.x;\n\t}\n\n\tx1 -= thickness_relative.x * 0.5f;\n\ty1 -= thickness_relative.y * 0.5f;\n\n\tfloat stripe_length = dist_scaled / 15.0f;\n\tfloat f_stripes_inv = 1.0f / (dist_scaled / stripe_length);\n\n\tstruct vec2 size;\n\tstruct vec2 count_inv;\n\tif (is_x_axis) {\n\t\tvec2_set(&size, dist_scaled, 0.0f);\n\t\tvec2_set(&count_inv, f_stripes_inv, 0.0f);\n\t} else {\n\t\tvec2_set(&size, 0.0f, dist_scaled);\n\t\tvec2_set(&count_inv, 0.0f, f_stripes_inv);\n\t}\n\n\tgs_eparam_t *size_param = gs_effect_get_param_by_name(stripedLineEffect, \"size\");\n\tgs_eparam_t *count_inv_param = gs_effect_get_param_by_name(stripedLineEffect, \"count_inv\");\n\n\tgs_effect_set_vec2(size_param, &size);\n\tgs_effect_set_vec2(count_inv_param, &count_inv);\n\n\tgs_matrix_push();\n\tgs_matrix_translate3f(x1, y1, 0.0f);\n\twhile (gs_effect_loop(stripedLineEffect, \"StripedLine\")) {\n\t\tgs_draw_quadf(nullptr, 0, cx, cy);\n\t}\n\tgs_matrix_pop();\n}\n\nstatic void DrawRect(float thickness, vec2 scale)\n{\n\tDrawLine(0.0f, 0.0f, 0.0f, 1.0f, thickness, scale);\n\tDrawLine(0.0f, 0.0f, 1.0f, 0.0f, thickness, scale);\n\tDrawLine(1.0f, 0.0f, 1.0f, 1.0f, thickness, scale);\n\tDrawLine(0.0f, 1.0f, 1.0f, 1.0f, thickness, scale);\n}\n\nstatic inline bool crop_enabled(const obs_sceneitem_crop *crop)\n{\n\treturn crop->left > 0 || crop->top > 0 || crop->right > 0 || crop->bottom > 0;\n}\n\nbool OBSBasicPreview::DrawSelectedOverflow(obs_scene_t *, obs_sceneitem_t *item, void *param)\n{\n\tif (obs_sceneitem_locked(item))\n\t\treturn true;\n\n\tif (!SceneItemHasVideo(item))\n\t\treturn true;\n\n\tOBSBasicPreview *prev = static_cast<OBSBasicPreview *>(param);\n\n\tif (!prev->GetOverflowSelectionHidden() && !obs_sceneitem_visible(item))\n\t\treturn true;\n\n\tif (obs_sceneitem_is_group(item)) {\n\t\tmatrix4 mat;\n\t\tobs_sceneitem_get_draw_transform(item, &mat);\n\n\t\tgs_matrix_push();\n\t\tgs_matrix_mul(&mat);\n\t\tobs_sceneitem_group_enum_items(item, DrawSelectedOverflow, param);\n\t\tgs_matrix_pop();\n\t}\n\n\tif (!prev->GetOverflowAlwaysVisible() && !obs_sceneitem_selected(item))\n\t\treturn true;\n\n\tmatrix4 boxTransform;\n\tmatrix4 invBoxTransform;\n\tobs_sceneitem_get_box_transform(item, &boxTransform);\n\tmatrix4_inv(&invBoxTransform, &boxTransform);\n\n\tvec3 bounds[] = {\n\t\t{{{0.f, 0.f, 0.f}}},\n\t\t{{{1.f, 0.f, 0.f}}},\n\t\t{{{0.f, 1.f, 0.f}}},\n\t\t{{{1.f, 1.f, 0.f}}},\n\t};\n\n\tbool visible = std::all_of(std::begin(bounds), std::end(bounds), [&](const vec3 &b) {\n\t\tvec3 pos;\n\t\tvec3_transform(&pos, &b, &boxTransform);\n\t\tvec3_transform(&pos, &pos, &invBoxTransform);\n\t\treturn CloseFloat(pos.x, b.x) && CloseFloat(pos.y, b.y);\n\t});\n\n\tif (!visible)\n\t\treturn true;\n\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, \"DrawSelectedOverflow\");\n\n\tobs_transform_info info;\n\tobs_sceneitem_get_info2(item, &info);\n\n\tgs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_REPEAT);\n\tgs_eparam_t *image = gs_effect_get_param_by_name(solid, \"image\");\n\tgs_eparam_t *scale = gs_effect_get_param_by_name(solid, \"scale\");\n\n\tvec2 s;\n\tvec2_set(&s, boxTransform.x.x / 96, boxTransform.y.y / 96);\n\n\tgs_effect_set_vec2(scale, &s);\n\tgs_effect_set_texture_srgb(image, prev->overflow);\n\n\tgs_matrix_push();\n\tgs_matrix_mul(&boxTransform);\n\n\tobs_sceneitem_crop crop;\n\tobs_sceneitem_get_crop(item, &crop);\n\n\tconst bool currentFramebufferSrgbState = gs_framebuffer_srgb_enabled();\n\tgs_enable_framebuffer_srgb(true);\n\n\twhile (gs_effect_loop(solid, \"Draw\")) {\n\t\tgs_draw_sprite(prev->overflow, 0, 1, 1);\n\t}\n\n\tgs_enable_framebuffer_srgb(currentFramebufferSrgbState);\n\tgs_matrix_pop();\n\n\tGS_DEBUG_MARKER_END();\n\n\treturn true;\n}\n\nbool OBSBasicPreview::DrawSelectedItem(obs_scene_t *, obs_sceneitem_t *item, void *param)\n{\n\tif (obs_sceneitem_locked(item))\n\t\treturn true;\n\n\tif (!SceneItemHasVideo(item))\n\t\treturn true;\n\n\tOBSBasicPreview *prev = static_cast<OBSBasicPreview *>(param);\n\n\tif (obs_sceneitem_is_group(item)) {\n\t\tmatrix4 mat;\n\t\tobs_transform_info groupInfo;\n\t\tobs_sceneitem_get_draw_transform(item, &mat);\n\t\tobs_sceneitem_get_info2(item, &groupInfo);\n\n\t\tprev->groupRot = groupInfo.rot;\n\n\t\tgs_matrix_push();\n\t\tgs_matrix_mul(&mat);\n\t\tobs_sceneitem_group_enum_items(item, DrawSelectedItem, prev);\n\t\tgs_matrix_pop();\n\n\t\tprev->groupRot = 0.0f;\n\t}\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tfloat pixelRatio = main->GetDevicePixelRatio();\n\n\tbool hovered = false;\n\t{\n\t\tstd::lock_guard<std::mutex> lock(prev->selectMutex);\n\t\tfor (size_t i = 0; i < prev->hoveredPreviewItems.size(); i++) {\n\t\t\tif (prev->hoveredPreviewItems[i] == item) {\n\t\t\t\thovered = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tbool selected = obs_sceneitem_selected(item);\n\n\tif (!selected && !hovered)\n\t\treturn true;\n\n\tmatrix4 boxTransform;\n\tmatrix4 invBoxTransform;\n\tobs_sceneitem_get_box_transform(item, &boxTransform);\n\tmatrix4_inv(&invBoxTransform, &boxTransform);\n\n\tvec3 bounds[] = {\n\t\t{{{0.f, 0.f, 0.f}}},\n\t\t{{{1.f, 0.f, 0.f}}},\n\t\t{{{0.f, 1.f, 0.f}}},\n\t\t{{{1.f, 1.f, 0.f}}},\n\t};\n\n\tmain->GetCameraIcon();\n\n\tQColor selColor = main->GetSelectionColor();\n\tQColor cropColor = main->GetCropColor();\n\tQColor hoverColor = main->GetHoverColor();\n\n\tvec4 red;\n\tvec4 green;\n\tvec4 blue;\n\n\tvec4_set(&red, selColor.redF(), selColor.greenF(), selColor.blueF(), 1.0f);\n\tvec4_set(&green, cropColor.redF(), cropColor.greenF(), cropColor.blueF(), 1.0f);\n\tvec4_set(&blue, hoverColor.redF(), hoverColor.greenF(), hoverColor.blueF(), 1.0f);\n\n\tbool visible = std::all_of(std::begin(bounds), std::end(bounds), [&](const vec3 &b) {\n\t\tvec3 pos;\n\t\tvec3_transform(&pos, &b, &boxTransform);\n\t\tvec3_transform(&pos, &pos, &invBoxTransform);\n\t\treturn CloseFloat(pos.x, b.x) && CloseFloat(pos.y, b.y);\n\t});\n\n\tif (!visible)\n\t\treturn true;\n\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, \"DrawSelectedItem\");\n\n\tmatrix4 curTransform;\n\tvec2 boxScale;\n\tgs_matrix_get(&curTransform);\n\tobs_sceneitem_get_box_scale(item, &boxScale);\n\tboxScale.x *= curTransform.x.x;\n\tboxScale.y *= curTransform.y.y;\n\n\tobs_transform_info info;\n\tobs_sceneitem_get_info2(item, &info);\n\n\tgs_matrix_push();\n\tgs_matrix_mul(&boxTransform);\n\n\tobs_sceneitem_crop crop;\n\tobs_sceneitem_get_crop(item, &crop);\n\n\tif (info.bounds_type == OBS_BOUNDS_NONE && crop_enabled(&crop)) {\n#define DRAW_SIDE(side, x1, y1, x2, y2)                                                                \\\n\tif (hovered && !selected) {                                                                    \\\n\t\tgs_eparam_t *colParam = gs_effect_get_param_by_name(prev->solidEffect, \"color\");       \\\n\t\tgs_effect_set_vec4(colParam, &blue);                                                   \\\n\t\twhile (gs_effect_loop(prev->solidEffect, \"Solid\")) {                                   \\\n\t\t\tDrawLine(x1, y1, x2, y2, HANDLE_RADIUS *pixelRatio / 2, boxScale);             \\\n\t\t}                                                                                      \\\n\t} else if (crop.side > 0) {                                                                    \\\n\t\tgs_eparam_t *colParam = gs_effect_get_param_by_name(prev->stripedLineEffect, \"color\"); \\\n\t\tgs_effect_set_vec4(colParam, &green);                                                  \\\n\t\tprev->DrawStripedLine(x1, y1, x2, y2, HANDLE_RADIUS *pixelRatio / 2, boxScale);        \\\n\t} else {                                                                                       \\\n\t\tgs_eparam_t *colParam = gs_effect_get_param_by_name(prev->solidEffect, \"color\");       \\\n\t\tgs_effect_set_vec4(colParam, &red);                                                    \\\n\t\twhile (gs_effect_loop(prev->solidEffect, \"Solid\")) {                                   \\\n\t\t\tDrawLine(x1, y1, x2, y2, HANDLE_RADIUS *pixelRatio / 2, boxScale);             \\\n\t\t}                                                                                      \\\n\t}\n\n\t\tDRAW_SIDE(left, 0.0f, 0.0f, 0.0f, 1.0f);\n\t\tDRAW_SIDE(top, 0.0f, 0.0f, 1.0f, 0.0f);\n\t\tDRAW_SIDE(right, 1.0f, 0.0f, 1.0f, 1.0f);\n\t\tDRAW_SIDE(bottom, 0.0f, 1.0f, 1.0f, 1.0f);\n#undef DRAW_SIDE\n\t} else {\n\t\tif (!selected) {\n\t\t\tgs_eparam_t *colParam = gs_effect_get_param_by_name(prev->solidEffect, \"color\");\n\t\t\tgs_effect_set_vec4(colParam, &blue);\n\t\t\twhile (gs_effect_loop(prev->solidEffect, \"Solid\")) {\n\t\t\t\tDrawRect(HANDLE_RADIUS * pixelRatio / 2, boxScale);\n\t\t\t}\n\t\t} else {\n\t\t\tgs_eparam_t *colParam = gs_effect_get_param_by_name(prev->solidEffect, \"color\");\n\t\t\tgs_effect_set_vec4(colParam, &red);\n\t\t\twhile (gs_effect_loop(prev->solidEffect, \"Solid\")) {\n\t\t\t\tDrawRect(HANDLE_RADIUS * pixelRatio / 2, boxScale);\n\t\t\t}\n\t\t}\n\t}\n\n\tgs_eparam_t *colParam = gs_effect_get_param_by_name(prev->solidEffect, \"color\");\n\n\tgs_technique_t *tech = gs_effect_get_technique(prev->solidEffect, \"Solid\");\n\tgs_technique_begin(tech);\n\tgs_technique_begin_pass(tech, 0);\n\n\tgs_load_vertexbuffer(main->box);\n\tgs_effect_set_vec4(colParam, &red);\n\n\tif (selected) {\n\t\tDrawSquareAtPos(0.0f, 0.0f, pixelRatio);\n\t\tDrawSquareAtPos(0.0f, 1.0f, pixelRatio);\n\t\tDrawSquareAtPos(1.0f, 0.0f, pixelRatio);\n\t\tDrawSquareAtPos(1.0f, 1.0f, pixelRatio);\n\t\tDrawSquareAtPos(0.5f, 0.0f, pixelRatio);\n\t\tDrawSquareAtPos(0.0f, 0.5f, pixelRatio);\n\t\tDrawSquareAtPos(0.5f, 1.0f, pixelRatio);\n\t\tDrawSquareAtPos(1.0f, 0.5f, pixelRatio);\n\n\t\tif (!prev->circleFill) {\n\t\t\tgs_render_start(true);\n\n\t\t\tfloat angle = 180;\n\t\t\tfor (int i = 0, l = 40; i < l; i++) {\n\t\t\t\tgs_vertex2f(sin(RAD(angle)) / 2 + 0.5f, cos(RAD(angle)) / 2 + 0.5f);\n\t\t\t\tangle += 360 / l;\n\t\t\t\tgs_vertex2f(sin(RAD(angle)) / 2 + 0.5f, cos(RAD(angle)) / 2 + 0.5f);\n\t\t\t\tgs_vertex2f(0.5f, 1.0f);\n\t\t\t}\n\n\t\t\tprev->circleFill = gs_render_save();\n\t\t}\n\n\t\tbool invert = info.scale.y < 0.0f && info.bounds_type == OBS_BOUNDS_NONE;\n\t\tDrawRotationHandle(prev->circleFill, info.rot + prev->groupRot, pixelRatio, invert);\n\t}\n\n\tgs_matrix_pop();\n\n\tgs_technique_end_pass(tech);\n\tgs_technique_end(tech);\n\n\tGS_DEBUG_MARKER_END();\n\n\treturn true;\n}\n\nbool OBSBasicPreview::DrawSelectionBox(float x1, float y1, float x2, float y2, gs_vertbuffer_t *rectFill)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tfloat pixelRatio = main->GetDevicePixelRatio();\n\n\tx1 = std::round(x1);\n\tx2 = std::round(x2);\n\ty1 = std::round(y1);\n\ty2 = std::round(y2);\n\n\tgs_effect_t *eff = gs_get_effect();\n\tgs_eparam_t *colParam = gs_effect_get_param_by_name(eff, \"color\");\n\n\tvec4 fillColor;\n\tvec4_set(&fillColor, 0.7f, 0.7f, 0.7f, 0.5f);\n\n\tvec4 borderColor;\n\tvec4_set(&borderColor, 1.0f, 1.0f, 1.0f, 1.0f);\n\n\tvec2 scale;\n\tvec2_set(&scale, std::abs(x2 - x1), std::abs(y2 - y1));\n\n\tgs_matrix_push();\n\tgs_matrix_identity();\n\n\tgs_matrix_translate3f(x1, y1, 0.0f);\n\tgs_matrix_scale3f(x2 - x1, y2 - y1, 1.0f);\n\n\tgs_effect_set_vec4(colParam, &fillColor);\n\tgs_load_vertexbuffer(rectFill);\n\tgs_draw(GS_TRISTRIP, 0, 0);\n\n\tgs_effect_set_vec4(colParam, &borderColor);\n\tDrawRect(HANDLE_RADIUS * pixelRatio / 2, scale);\n\n\tgs_matrix_pop();\n\n\treturn true;\n}\n\nvoid OBSBasicPreview::DrawOverflow()\n{\n\tif (locked)\n\t\treturn;\n\n\tif (overflowHidden)\n\t\treturn;\n\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, \"DrawOverflow\");\n\n\tif (!overflow) {\n\t\tstd::string path;\n\t\tGetDataFilePath(\"images/overflow.png\", path);\n\t\toverflow = gs_texture_create_from_file(path.c_str());\n\t}\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tOBSScene scene = main->GetCurrentScene();\n\n\tif (scene) {\n\t\tgs_matrix_push();\n\t\tgs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);\n\t\tobs_scene_enum_items(scene, DrawSelectedOverflow, this);\n\t\tgs_matrix_pop();\n\t}\n\n\tgs_load_vertexbuffer(nullptr);\n\n\tGS_DEBUG_MARKER_END();\n}\n\nvoid OBSBasicPreview::DrawSceneEditing()\n{\n\tif (locked)\n\t\treturn;\n\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, \"DrawSceneEditing\");\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tOBSScene scene = main->GetCurrentScene();\n\n\tif (scene) {\n\t\tgs_matrix_push();\n\t\tgs_matrix_scale3f(main->previewScale, main->previewScale, 1.0f);\n\t\tobs_scene_enum_items(scene, DrawSelectedItem, this);\n\t\tgs_matrix_pop();\n\t}\n\n\tif (selectionBox) {\n\t\tif (!rectFill) {\n\t\t\tgs_render_start(true);\n\n\t\t\tgs_vertex2f(0.0f, 0.0f);\n\t\t\tgs_vertex2f(1.0f, 0.0f);\n\t\t\tgs_vertex2f(0.0f, 1.0f);\n\t\t\tgs_vertex2f(1.0f, 1.0f);\n\n\t\t\trectFill = gs_render_save();\n\t\t}\n\n\t\twhile (gs_effect_loop(solidEffect, \"Solid\")) {\n\t\t\tDrawSelectionBox(startPos.x * main->previewScale, startPos.y * main->previewScale,\n\t\t\t\t\t mousePos.x * main->previewScale, mousePos.y * main->previewScale, rectFill);\n\t\t}\n\t}\n\n\tgs_load_vertexbuffer(nullptr);\n\n\tGS_DEBUG_MARKER_END();\n}\n\nvoid OBSBasicPreview::ResetScrollingOffset()\n{\n\tvec2_zero(&scrollingOffset);\n}\n\nvoid OBSBasicPreview::SetScalingLevel(int32_t newScalingLevelVal)\n{\n\tnewScalingLevelVal = std::clamp(newScalingLevelVal, -MAX_SCALING_LEVEL, MAX_SCALING_LEVEL);\n\tfloat newScalingAmountVal = pow(ZOOM_SENSITIVITY, float(newScalingLevelVal));\n\tscalingLevel = newScalingLevelVal;\n\tSetScalingAmount(newScalingAmountVal);\n}\n\nvoid OBSBasicPreview::SetScalingAmount(float newScalingAmountVal)\n{\n\tscrollingOffset.x *= newScalingAmountVal / scalingAmount;\n\tscrollingOffset.y *= newScalingAmountVal / scalingAmount;\n\n\tif (scalingAmount == newScalingAmountVal)\n\t\treturn;\n\n\tscalingAmount = newScalingAmountVal;\n\temit scalingChanged(scalingAmount);\n\n\tif (!fixedScaling) {\n\t\tscalingLevel = std::round(log(scalingAmount) / log(ZOOM_SENSITIVITY));\n\t}\n}\n\nvoid OBSBasicPreview::SetScalingLevelAndAmount(int32_t newScalingLevelVal, float newScalingAmountVal)\n{\n\tnewScalingLevelVal = std::clamp(newScalingLevelVal, -MAX_SCALING_LEVEL, MAX_SCALING_LEVEL);\n\tscalingLevel = newScalingLevelVal;\n\tSetScalingAmount(newScalingAmountVal);\n}\n\nvoid OBSBasicPreview::increaseScalingLevel()\n{\n\tSetFixedScaling(true);\n\tSetScalingLevel(scalingLevel + 1);\n\n\temit DisplayResized();\n}\n\nvoid OBSBasicPreview::decreaseScalingLevel()\n{\n\tSetFixedScaling(true);\n\tSetScalingLevel(scalingLevel - 1);\n\n\temit DisplayResized();\n}\n\nvoid OBSBasicPreview::resetScalingLevel()\n{\n\tSetScalingLevel(0);\n\tResetScrollingOffset();\n\n\temit DisplayResized();\n}\n\nOBSBasicPreview *OBSBasicPreview::Get()\n{\n\treturn OBSBasic::Get()->ui->preview;\n}\n\nstatic obs_source_t *CreateLabel(float pixelRatio, int i)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tOBSDataAutoRelease font = obs_data_create();\n\n#if defined(_WIN32)\n\tobs_data_set_string(font, \"face\", \"Arial\");\n#elif defined(__APPLE__)\n\tobs_data_set_string(font, \"face\", \"Helvetica\");\n#else\n\tobs_data_set_string(font, \"face\", \"Monospace\");\n#endif\n\tobs_data_set_int(font, \"flags\", 1); // Bold text\n\tobs_data_set_int(font, \"size\", 16 * pixelRatio);\n\n\tobs_data_set_obj(settings, \"font\", font);\n\tobs_data_set_bool(settings, \"outline\", true);\n\n#ifdef _WIN32\n\tobs_data_set_int(settings, \"outline_color\", 0x000000);\n\tobs_data_set_int(settings, \"outline_size\", 3);\n\tconst char *text_source_id = \"text_gdiplus\";\n#else\n\tconst char *text_source_id = \"text_ft2_source\";\n#endif\n\n\tDStr name;\n\tdstr_printf(name, \"Preview spacing label %d\", i);\n\treturn obs_source_create_private(text_source_id, name, settings);\n}\n\nstatic void SetLabelText(int sourceIndex, int px)\n{\n\tOBSBasicPreview *prev = OBSBasicPreview::Get();\n\n\tif (px == prev->spacerPx[sourceIndex])\n\t\treturn;\n\n\tstd::string text = std::to_string(px) + \" px\";\n\n\tobs_source_t *source = prev->spacerLabel[sourceIndex];\n\n\tOBSDataAutoRelease settings = obs_source_get_settings(source);\n\tobs_data_set_string(settings, \"text\", text.c_str());\n\tobs_source_update(source, settings);\n\n\tprev->spacerPx[sourceIndex] = px;\n}\n\nstatic void DrawLabel(OBSSource source, vec3 &pos, vec3 &viewport)\n{\n\tif (!source)\n\t\treturn;\n\n\tvec3_mul(&pos, &pos, &viewport);\n\n\tgs_matrix_push();\n\tgs_matrix_identity();\n\tgs_matrix_translate(&pos);\n\tobs_source_video_render(source);\n\tgs_matrix_pop();\n}\n\nstatic void DrawSpacingLine(vec3 &start, vec3 &end, vec3 &viewport, float pixelRatio)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tmatrix4 transform;\n\tmatrix4_identity(&transform);\n\ttransform.x.x = viewport.x;\n\ttransform.y.y = viewport.y;\n\n\tgs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);\n\tgs_technique_t *tech = gs_effect_get_technique(solid, \"Solid\");\n\n\tQColor selColor = main->GetSelectionColor();\n\tvec4 color;\n\tvec4_set(&color, selColor.redF(), selColor.greenF(), selColor.blueF(), 1.0f);\n\n\tgs_effect_set_vec4(gs_effect_get_param_by_name(solid, \"color\"), &color);\n\n\tgs_technique_begin(tech);\n\tgs_technique_begin_pass(tech, 0);\n\n\tgs_matrix_push();\n\tgs_matrix_mul(&transform);\n\n\tvec2 scale;\n\tvec2_set(&scale, viewport.x, viewport.y);\n\n\tDrawLine(start.x, start.y, end.x, end.y, pixelRatio * (HANDLE_RADIUS / 2), scale);\n\n\tgs_matrix_pop();\n\n\tgs_load_vertexbuffer(nullptr);\n\n\tgs_technique_end_pass(tech);\n\tgs_technique_end(tech);\n}\n\nstatic void RenderSpacingHelper(int sourceIndex, vec3 &start, vec3 &end, vec3 &viewport, float pixelRatio)\n{\n\tbool horizontal = (sourceIndex == 2 || sourceIndex == 3);\n\n\t// If outside of preview, don't render\n\tif (!((horizontal && (end.x >= start.x)) || (!horizontal && (end.y >= start.y))))\n\t\treturn;\n\n\tfloat length = vec3_dist(&start, &end);\n\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tfloat px;\n\n\tif (horizontal) {\n\t\tpx = length * ovi.base_width;\n\t} else {\n\t\tpx = length * ovi.base_height;\n\t}\n\n\tif (px <= 0.0f)\n\t\treturn;\n\n\tOBSBasicPreview *prev = OBSBasicPreview::Get();\n\tobs_source_t *source = prev->spacerLabel[sourceIndex];\n\tvec3 labelSize, labelPos;\n\tvec3_set(&labelSize, obs_source_get_width(source), obs_source_get_height(source), 1.0f);\n\n\tvec3_div(&labelSize, &labelSize, &viewport);\n\n\tvec3 labelMargin;\n\tvec3_set(&labelMargin, SPACER_LABEL_MARGIN * pixelRatio, SPACER_LABEL_MARGIN * pixelRatio, 1.0f);\n\tvec3_div(&labelMargin, &labelMargin, &viewport);\n\n\tvec3_set(&labelPos, end.x, end.y, end.z);\n\tif (horizontal) {\n\t\tlabelPos.x -= (end.x - start.x) / 2;\n\t\tlabelPos.x -= labelSize.x / 2;\n\t\tlabelPos.y -= labelMargin.y + (labelSize.y / 2) + (HANDLE_RADIUS / viewport.y);\n\t} else {\n\t\tlabelPos.y -= (end.y - start.y) / 2;\n\t\tlabelPos.y -= labelSize.y / 2;\n\t\tlabelPos.x += labelMargin.x;\n\t}\n\n\tDrawSpacingLine(start, end, viewport, pixelRatio);\n\tSetLabelText(sourceIndex, (int)px);\n\tDrawLabel(source, labelPos, viewport);\n}\n\nvoid OBSBasicPreview::DrawSpacingHelpers()\n{\n\tif (locked)\n\t\treturn;\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tvec2 s;\n\tSceneFindBoxData data(s, s);\n\n\tOBSScene scene = main->GetCurrentScene();\n\tobs_scene_enum_items(scene, FindSelected, &data);\n\n\tif (data.sceneItems.size() != 1)\n\t\treturn;\n\n\tOBSSceneItem item = data.sceneItems[0];\n\tif (!item)\n\t\treturn;\n\n\tif (obs_sceneitem_locked(item))\n\t\treturn;\n\n\tvec2 itemSize = GetItemSize(item);\n\tif (itemSize.x == 0.0f || itemSize.y == 0.0f)\n\t\treturn;\n\n\tobs_sceneitem_t *parentGroup = obs_sceneitem_get_group(scene, item);\n\tif (parentGroup && obs_sceneitem_locked(parentGroup))\n\t\treturn;\n\n\tmatrix4 boxTransform;\n\tobs_sceneitem_get_box_transform(item, &boxTransform);\n\n\tobs_transform_info oti;\n\tobs_sceneitem_get_info2(item, &oti);\n\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tvec3 size;\n\tvec3_set(&size, ovi.base_width, ovi.base_height, 1.0f);\n\n\t// Init box transform side locations\n\tvec3 left, right, top, bottom;\n\n\tvec3_set(&left, 0.0f, 0.5f, 1.0f);\n\tvec3_set(&right, 1.0f, 0.5f, 1.0f);\n\tvec3_set(&top, 0.5f, 0.0f, 1.0f);\n\tvec3_set(&bottom, 0.5f, 1.0f, 1.0f);\n\n\t// Decide which side to use with box transform, based on rotation\n\t// Seems hacky, probably a better way to do it\n\tfloat rot = oti.rot;\n\n\tif (parentGroup) {\n\t\tobs_transform_info groupOti;\n\t\tobs_sceneitem_get_info2(parentGroup, &groupOti);\n\n\t\t//Correct the scene item rotation angle\n\t\trot = oti.rot + groupOti.rot;\n\n\t\t// Correct the scene item box transform\n\t\t// Based on scale, rotation angle, position of parent's group\n\t\tmatrix4_scale3f(&boxTransform, &boxTransform, groupOti.scale.x, groupOti.scale.y, 1.0f);\n\t\tmatrix4_rotate_aa4f(&boxTransform, &boxTransform, 0.0f, 0.0f, 1.0f, RAD(groupOti.rot));\n\t\tmatrix4_translate3f(&boxTransform, &boxTransform, groupOti.pos.x, groupOti.pos.y, 0.0f);\n\t}\n\n\t// Switch top/bottom or right/left if scale is negative\n\tif (oti.scale.x < 0.0f && oti.bounds_type == OBS_BOUNDS_NONE) {\n\t\tvec3 l = left;\n\t\tvec3 r = right;\n\n\t\tvec3_copy(&left, &r);\n\t\tvec3_copy(&right, &l);\n\t}\n\n\tif (oti.scale.y < 0.0f && oti.bounds_type == OBS_BOUNDS_NONE) {\n\t\tvec3 t = top;\n\t\tvec3 b = bottom;\n\n\t\tvec3_copy(&top, &b);\n\t\tvec3_copy(&bottom, &t);\n\t}\n\n\tif (rot >= HELPER_ROT_BREAKPOINT) {\n\t\tfor (float i = HELPER_ROT_BREAKPOINT; i <= 360.0f; i += 90.0f) {\n\t\t\tif (rot < i)\n\t\t\t\tbreak;\n\n\t\t\tvec3 l = left;\n\t\t\tvec3 r = right;\n\t\t\tvec3 t = top;\n\t\t\tvec3 b = bottom;\n\n\t\t\tvec3_copy(&top, &l);\n\t\t\tvec3_copy(&right, &t);\n\t\t\tvec3_copy(&bottom, &r);\n\t\t\tvec3_copy(&left, &b);\n\t\t}\n\t} else if (rot <= -HELPER_ROT_BREAKPOINT) {\n\t\tfor (float i = -HELPER_ROT_BREAKPOINT; i >= -360.0f; i -= 90.0f) {\n\t\t\tif (rot > i)\n\t\t\t\tbreak;\n\n\t\t\tvec3 l = left;\n\t\t\tvec3 r = right;\n\t\t\tvec3 t = top;\n\t\t\tvec3 b = bottom;\n\n\t\t\tvec3_copy(&top, &r);\n\t\t\tvec3_copy(&right, &b);\n\t\t\tvec3_copy(&bottom, &l);\n\t\t\tvec3_copy(&left, &t);\n\t\t}\n\t}\n\n\t// Get sides of box transform\n\tleft = GetTransformedPos(left.x, left.y, boxTransform);\n\tright = GetTransformedPos(right.x, right.y, boxTransform);\n\ttop = GetTransformedPos(top.x, top.y, boxTransform);\n\tbottom = GetTransformedPos(bottom.x, bottom.y, boxTransform);\n\n\tbottom.y = size.y - bottom.y;\n\tright.x = size.x - right.x;\n\n\t// Init viewport\n\tvec3 viewport;\n\tvec3_set(&viewport, main->previewCX, main->previewCY, 1.0f);\n\n\tvec3_div(&left, &left, &viewport);\n\tvec3_div(&right, &right, &viewport);\n\tvec3_div(&top, &top, &viewport);\n\tvec3_div(&bottom, &bottom, &viewport);\n\n\tvec3_mulf(&left, &left, main->previewScale);\n\tvec3_mulf(&right, &right, main->previewScale);\n\tvec3_mulf(&top, &top, main->previewScale);\n\tvec3_mulf(&bottom, &bottom, main->previewScale);\n\n\t// Draw spacer lines and labels\n\tvec3 start, end;\n\n\tfloat pixelRatio = main->GetDevicePixelRatio();\n\tfor (int i = 0; i < 4; i++) {\n\t\tif (!spacerLabel[i])\n\t\t\tspacerLabel[i] = CreateLabel(pixelRatio, i);\n\t}\n\n\tvec3_set(&start, top.x, 0.0f, 1.0f);\n\tvec3_set(&end, top.x, top.y, 1.0f);\n\tRenderSpacingHelper(0, start, end, viewport, pixelRatio);\n\n\tvec3_set(&start, bottom.x, 1.0f - bottom.y, 1.0f);\n\tvec3_set(&end, bottom.x, 1.0f, 1.0f);\n\tRenderSpacingHelper(1, start, end, viewport, pixelRatio);\n\n\tvec3_set(&start, 0.0f, left.y, 1.0f);\n\tvec3_set(&end, left.x, left.y, 1.0f);\n\tRenderSpacingHelper(2, start, end, viewport, pixelRatio);\n\n\tvec3_set(&start, 1.0f - right.x, right.y, 1.0f);\n\tvec3_set(&end, 1.0f, right.y, 1.0f);\n\tRenderSpacingHelper(3, start, end, viewport, pixelRatio);\n}\n\nvoid OBSBasicPreview::ClampScrollingOffsets()\n{\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tQSize targetSize = GetPixelSize(this);\n\n\tvec3 target, offset;\n\tvec3_set(&target, (float)targetSize.width(), (float)targetSize.height(), 1.0f);\n\n\tvec3_set(&offset, (float)ovi.base_width, (float)ovi.base_height, 1.0f);\n\tvec3_mulf(&offset, &offset, scalingAmount);\n\n\tvec3_sub(&offset, &offset, &target);\n\n\tvec3_mulf(&offset, &offset, 0.5f);\n\tvec3_maxf(&offset, &offset, 0.0f);\n\n\tvec3_divf(&target, &target, 2.0f);\n\tvec3_add(&offset, &offset, &target);\n\n\tscrollingOffset.x = std::clamp(scrollingOffset.x, -offset.x, offset.x);\n\tscrollingOffset.y = std::clamp(scrollingOffset.y, -offset.y, offset.y);\n\n\tUpdateXScrollBar(offset.x);\n\tUpdateYScrollBar(offset.y);\n}\n\nvoid OBSBasicPreview::xScrollBarChanged(int value)\n{\n\tupdatingXScrollBar = true;\n\tscrollingOffset.x = float(-value);\n\n\tupdatingXScrollBar = false;\n\temit DisplayResized();\n}\n\nvoid OBSBasicPreview::yScrollBarChanged(int value)\n{\n\tupdatingYScrollBar = true;\n\tscrollingOffset.y = float(-value);\n\n\tupdatingYScrollBar = false;\n\temit DisplayResized();\n}\n\nvoid OBSBasicPreview::UpdateXScrollBar(float cx)\n{\n\tif (updatingXScrollBar)\n\t\treturn;\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tif (!main->ui->previewXScrollBar->isVisible())\n\t\treturn;\n\n\tmain->ui->previewXScrollBar->setRange(int(-cx), int(cx));\n\n\tQSize targetSize = GetPixelSize(this);\n\tmain->ui->previewXScrollBar->setPageStep(targetSize.width() / std::min(scalingAmount, 1.0f));\n\n\tQSignalBlocker sig(main->ui->previewXScrollBar);\n\tmain->ui->previewXScrollBar->setValue(int(-scrollingOffset.x));\n}\n\nvoid OBSBasicPreview::UpdateYScrollBar(float cy)\n{\n\tif (updatingYScrollBar)\n\t\treturn;\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tif (!main->ui->previewYScrollBar->isVisible())\n\t\treturn;\n\n\tmain->ui->previewYScrollBar->setRange(int(-cy), int(cy));\n\n\tQSize targetSize = GetPixelSize(this);\n\tmain->ui->previewYScrollBar->setPageStep(targetSize.height() / std::min(scalingAmount, 1.0f));\n\n\tQSignalBlocker sig(main->ui->previewYScrollBar);\n\tmain->ui->previewYScrollBar->setValue(int(-scrollingOffset.y));\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasicPreview.hpp",
    "content": "#pragma once\n\n#include \"OBSQTDisplay.hpp\"\n\n#include <graphics/matrix4.h>\n\n#include <mutex>\n\n#define ITEM_LEFT (1 << 0)\n#define ITEM_RIGHT (1 << 1)\n#define ITEM_TOP (1 << 2)\n#define ITEM_BOTTOM (1 << 3)\n#define ITEM_ROT (1 << 4)\n\n#define MAX_SCALING_LEVEL 32\n#define MAX_SCALING_AMOUNT 8.0f\n#define ZOOM_SENSITIVITY pow(MAX_SCALING_AMOUNT, 1.0f / MAX_SCALING_LEVEL)\n\n#define SPACER_LABEL_MARGIN 6.0f\n\nenum class ItemHandle : uint32_t {\n\tNone = 0,\n\tTopLeft = ITEM_TOP | ITEM_LEFT,\n\tTopCenter = ITEM_TOP,\n\tTopRight = ITEM_TOP | ITEM_RIGHT,\n\tCenterLeft = ITEM_LEFT,\n\tCenterRight = ITEM_RIGHT,\n\tBottomLeft = ITEM_BOTTOM | ITEM_LEFT,\n\tBottomCenter = ITEM_BOTTOM,\n\tBottomRight = ITEM_BOTTOM | ITEM_RIGHT,\n\tRot = ITEM_ROT\n};\n\nclass OBSBasicPreview : public OBSQTDisplay {\n\tQ_OBJECT\n\n\tfriend class SourceTree;\n\tfriend class SourceTreeItem;\n\nprivate:\n\tobs_sceneitem_crop startCrop;\n\tvec2 startItemPos;\n\tvec2 cropSize;\n\tOBSSceneItem stretchGroup;\n\tOBSSceneItem stretchItem;\n\tItemHandle stretchHandle = ItemHandle::None;\n\tfloat rotateAngle;\n\tvec2 rotatePoint;\n\tvec2 offsetPoint;\n\tvec2 stretchItemSize;\n\tmatrix4 screenToItem;\n\tmatrix4 itemToScreen;\n\tmatrix4 invGroupTransform;\n\n\tgs_texture_t *overflow = nullptr;\n\tgs_vertbuffer_t *rectFill = nullptr;\n\tgs_vertbuffer_t *circleFill = nullptr;\n\tgs_effect_t *solidEffect = nullptr;\n\tgs_effect_t *stripedLineEffect = nullptr;\n\n\tvec2 startPos;\n\tvec2 mousePos;\n\tvec2 lastMoveOffset;\n\tvec2 scrollingFrom;\n\tvec2 scrollingOffset;\n\tbool mouseDown = false;\n\tbool mouseMoved = false;\n\tbool mouseOverItems = false;\n\tbool cropping = false;\n\tbool locked = false;\n\tbool scrollMode = false;\n\tbool fixedScaling = false;\n\tbool selectionBox = false;\n\tbool overflowHidden = false;\n\tbool overflowSelectionHidden = false;\n\tbool overflowAlwaysVisible = false;\n\tint32_t scalingLevel = 0;\n\tfloat scalingAmount = 1.0f;\n\tfloat groupRot = 0.0f;\n\tbool updatingXScrollBar = false;\n\tbool updatingYScrollBar = false;\n\n\tstd::vector<obs_sceneitem_t *> hoveredPreviewItems;\n\tstd::vector<obs_sceneitem_t *> selectedItems;\n\tstd::mutex selectMutex;\n\n\tstatic vec2 GetMouseEventPos(QMouseEvent *event);\n\tstatic bool FindSelected(obs_scene_t *scene, obs_sceneitem_t *item, void *param);\n\tstatic bool DrawSelectedOverflow(obs_scene_t *scene, obs_sceneitem_t *item, void *param);\n\tstatic bool DrawSelectedItem(obs_scene_t *scene, obs_sceneitem_t *item, void *param);\n\tstatic bool DrawSelectionBox(float x1, float y1, float x2, float y2, gs_vertbuffer_t *box);\n\n\tstatic OBSSceneItem GetItemAtPos(const vec2 &pos, bool selectBelow);\n\tstatic bool SelectedAtPos(const vec2 &pos);\n\n\tstatic void DoSelect(const vec2 &pos);\n\tstatic void DoCtrlSelect(const vec2 &pos);\n\n\tstatic vec3 GetSnapOffset(const vec3 &tl, const vec3 &br);\n\n\tvoid GetStretchHandleData(const vec2 &pos, bool ignoreGroup);\n\n\tvoid UpdateCursor(uint32_t &flags);\n\n\tvoid SnapStretchingToScreen(vec3 &tl, vec3 &br);\n\tvoid ClampAspect(vec3 &tl, vec3 &br, vec2 &size, const vec2 &baseSize);\n\tvec3 CalculateStretchPos(const vec3 &tl, const vec3 &br);\n\tvoid CropItem(const vec2 &pos);\n\tvoid StretchItem(const vec2 &pos);\n\tvoid RotateItem(const vec2 &pos);\n\n\tstatic void SnapItemMovement(vec2 &offset);\n\tvoid MoveItems(const vec2 &pos);\n\tvoid BoxItems(const vec2 &startPos, const vec2 &pos);\n\n\tvoid ProcessClick(const vec2 &pos);\n\n\tvoid DrawStripedLine(float x1, float y1, float x2, float y2, float thickness, vec2 scale);\n\n\tOBSDataAutoRelease wrapper = nullptr;\n\tbool changed;\n\npublic:\n\tOBSBasicPreview(QWidget *parent, Qt::WindowFlags flags = Qt::WindowFlags());\n\t~OBSBasicPreview();\n\n\tvoid Init();\n\n\tstatic OBSBasicPreview *Get();\n\n\tvirtual void keyPressEvent(QKeyEvent *event) override;\n\tvirtual void keyReleaseEvent(QKeyEvent *event) override;\n\n\tvirtual void wheelEvent(QWheelEvent *event) override;\n\n\tvirtual void mousePressEvent(QMouseEvent *event) override;\n\tvirtual void mouseReleaseEvent(QMouseEvent *event) override;\n\tvirtual void mouseMoveEvent(QMouseEvent *event) override;\n\tvirtual void leaveEvent(QEvent *event) override;\n\n\tvoid DrawOverflow();\n\tvoid DrawSceneEditing();\n\n\tinline void SetLocked(bool newLockedVal) { locked = newLockedVal; }\n\tinline void ToggleLocked() { locked = !locked; }\n\tinline bool Locked() const { return locked; }\n\n\tinline void SetFixedScaling(bool newFixedScalingVal)\n\t{\n\t\tif (fixedScaling == newFixedScalingVal)\n\t\t\treturn;\n\n\t\tfixedScaling = newFixedScalingVal;\n\t\temit fixedScalingChanged(fixedScaling);\n\t}\n\tinline bool IsFixedScaling() const { return fixedScaling; }\n\n\tvoid SetScalingLevel(int32_t newScalingLevelVal);\n\tvoid SetScalingAmount(float newScalingAmountVal);\n\tvoid SetScalingLevelAndAmount(int32_t newScalingLevelVal, float newScalingAmountVal);\n\tvoid increaseScalingLevel();\n\tvoid decreaseScalingLevel();\n\tvoid resetScalingLevel();\n\tinline int32_t GetScalingLevel() const { return scalingLevel; }\n\tinline float GetScalingAmount() const { return scalingAmount; }\n\n\tvoid ResetScrollingOffset();\n\tinline void SetScrollingOffset(float x, float y) { vec2_set(&scrollingOffset, x, y); }\n\tinline float GetScrollX() const { return scrollingOffset.x; }\n\tinline float GetScrollY() const { return scrollingOffset.y; }\n\n\tvoid xScrollBarChanged(int value);\n\tvoid yScrollBarChanged(int value);\n\n\tinline void SetOverflowHidden(bool hidden) { overflowHidden = hidden; }\n\tinline void SetOverflowSelectionHidden(bool hidden) { overflowSelectionHidden = hidden; }\n\tinline void SetOverflowAlwaysVisible(bool visible) { overflowAlwaysVisible = visible; }\n\n\tinline bool GetOverflowSelectionHidden() const { return overflowSelectionHidden; }\n\tinline bool GetOverflowAlwaysVisible() const { return overflowAlwaysVisible; }\n\n\t/* use libobs allocator for alignment because the matrices itemToScreen\n\t * and screenToItem may contain SSE data, which will cause SSE\n\t * instructions to crash if the data is not aligned to at least a 16\n\t * byte boundary. */\n\tstatic inline void *operator new(size_t size) { return bmalloc(size); }\n\tstatic inline void operator delete(void *ptr) { bfree(ptr); }\n\n\tOBSSourceAutoRelease spacerLabel[4];\n\tint spacerPx[4] = {0};\n\n\tvoid DrawSpacingHelpers();\n\tvoid ClampScrollingOffsets();\n\tvoid UpdateXScrollBar(float cx);\n\tvoid UpdateYScrollBar(float cy);\n\nsignals:\n\tvoid scalingChanged(float scalingAmount);\n\tvoid fixedScalingChanged(bool isFixed);\n};\n"
  },
  {
    "path": "frontend/widgets/OBSBasicStats.cpp",
    "content": "#include \"OBSBasicStats.hpp\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QGridLayout>\n#include <QLabel>\n#include <QPushButton>\n#include <QScrollArea>\n#include <QVBoxLayout>\n\n#include \"moc_OBSBasicStats.cpp\"\n\n#define TIMER_INTERVAL 2000\n#define REC_TIME_LEFT_INTERVAL 30000\n\nvoid OBSBasicStats::OBSFrontendEvent(enum obs_frontend_event event, void *ptr)\n{\n\tOBSBasicStats *stats = static_cast<OBSBasicStats *>(ptr);\n\n\tswitch (event) {\n\tcase OBS_FRONTEND_EVENT_RECORDING_STARTED:\n\t\tstats->StartRecTimeLeft();\n\t\tbreak;\n\tcase OBS_FRONTEND_EVENT_RECORDING_STOPPED:\n\t\tstats->ResetRecTimeLeft();\n\t\tbreak;\n\tcase OBS_FRONTEND_EVENT_EXIT:\n\t\t// This is only reached when the non-closable (dock) stats\n\t\t// window is being cleaned up. The closable stats window is\n\t\t// already gone by this point as it's deleted on close.\n\t\tobs_frontend_remove_event_callback(OBSFrontendEvent, stats);\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nstatic QString MakeTimeLeftText(int hours, int minutes)\n{\n\treturn QTStr(\"Basic.Stats.DiskFullIn.Text\").arg(QString::number(hours), QString::number(minutes));\n}\n\nstatic QString MakeMissedFramesText(uint32_t total_lagged, uint32_t total_rendered, long double num)\n{\n\treturn QString(\"%1 / %2 (%3%)\")\n\t\t.arg(QString::number(total_lagged), QString::number(total_rendered), QString::number(num, 'f', 1));\n}\n\nOBSBasicStats::OBSBasicStats(QWidget *parent, bool closable)\n\t: QFrame(parent),\n\t  cpu_info(os_cpu_usage_info_start()),\n\t  timer(this),\n\t  recTimeLeft(this)\n{\n\tQVBoxLayout *mainLayout = new QVBoxLayout();\n\tQGridLayout *topLayout = new QGridLayout();\n\toutputLayout = new QGridLayout();\n\n\tbitrates.reserve(REC_TIME_LEFT_INTERVAL / TIMER_INTERVAL);\n\n\tint row = 0;\n\n\tauto newStatBare = [&](QString name, QWidget *label, int col) {\n\t\tQLabel *typeLabel = new QLabel(name, this);\n\t\ttopLayout->addWidget(typeLabel, row, col);\n\t\ttopLayout->addWidget(label, row++, col + 1);\n\t};\n\n\tauto newStat = [&](const char *strLoc, QWidget *label, int col) {\n\t\tstd::string str = \"Basic.Stats.\";\n\t\tstr += strLoc;\n\t\tnewStatBare(QTStr(str.c_str()), label, col);\n\t};\n\n\t/* --------------------------------------------- */\n\n\tcpuUsage = new QLabel(this);\n\thddSpace = new QLabel(this);\n\trecordTimeLeft = new QLabel(this);\n\tmemUsage = new QLabel(this);\n\n\tQString str = MakeTimeLeftText(99999, 59);\n\tint textWidth = recordTimeLeft->fontMetrics().boundingRect(str).width();\n\trecordTimeLeft->setMinimumWidth(textWidth);\n\n\tnewStat(\"CPUUsage\", cpuUsage, 0);\n\tnewStat(\"HDDSpaceAvailable\", hddSpace, 0);\n\tnewStat(\"DiskFullIn\", recordTimeLeft, 0);\n\tnewStat(\"MemoryUsage\", memUsage, 0);\n\n\tfps = new QLabel(this);\n\trenderTime = new QLabel(this);\n\tskippedFrames = new QLabel(this);\n\tmissedFrames = new QLabel(this);\n\n\tstr = MakeMissedFramesText(999999, 999999, 99.99);\n\ttextWidth = missedFrames->fontMetrics().boundingRect(str).width();\n\tmissedFrames->setMinimumWidth(textWidth);\n\n\trow = 0;\n\n\tnewStatBare(\"FPS\", fps, 2);\n\tnewStat(\"AverageTimeToRender\", renderTime, 2);\n\tnewStat(\"MissedFrames\", missedFrames, 2);\n\tnewStat(\"SkippedFrames\", skippedFrames, 2);\n\n\t/* --------------------------------------------- */\n\tQPushButton *closeButton = nullptr;\n\tif (closable)\n\t\tcloseButton = new QPushButton(QTStr(\"Close\"));\n\tQPushButton *resetButton = new QPushButton(QTStr(\"Reset\"));\n\tQHBoxLayout *buttonLayout = new QHBoxLayout;\n\tbuttonLayout->addStretch();\n\tbuttonLayout->addWidget(resetButton);\n\tif (closable)\n\t\tbuttonLayout->addWidget(closeButton);\n\n\t/* --------------------------------------------- */\n\n\tint col = 0;\n\tauto addOutputCol = [&](const char *loc) {\n\t\tQLabel *label = new QLabel(QTStr(loc), this);\n\t\tlabel->setStyleSheet(\"font-weight: bold\");\n\t\toutputLayout->addWidget(label, 0, col++);\n\t};\n\n\taddOutputCol(\"Basic.Settings.Output\");\n\taddOutputCol(\"Basic.Stats.Status\");\n\taddOutputCol(\"Basic.Stats.DroppedFrames\");\n\taddOutputCol(\"Basic.Stats.MegabytesSent\");\n\taddOutputCol(\"Basic.Stats.Bitrate\");\n\n\t/* --------------------------------------------- */\n\n\tAddOutputLabels(QTStr(\"Basic.Stats.Output.Stream\"));\n\tAddOutputLabels(QTStr(\"Basic.Stats.Output.Recording\"));\n\n\t/* --------------------------------------------- */\n\n\tQVBoxLayout *outputContainerLayout = new QVBoxLayout();\n\toutputContainerLayout->addLayout(outputLayout);\n\toutputContainerLayout->addStretch();\n\n\tQWidget *widget = new QWidget(this);\n\twidget->setLayout(outputContainerLayout);\n\n\tQScrollArea *scrollArea = new QScrollArea(this);\n\tscrollArea->setWidget(widget);\n\tscrollArea->setWidgetResizable(true);\n\n\t/* --------------------------------------------- */\n\n\tmainLayout->addLayout(topLayout);\n\tmainLayout->addWidget(scrollArea);\n\tmainLayout->addLayout(buttonLayout);\n\tsetLayout(mainLayout);\n\n\t/* --------------------------------------------- */\n\tif (closable)\n\t\tconnect(closeButton, &QPushButton::clicked, this, [this]() { close(); });\n\tconnect(resetButton, &QPushButton::clicked, this, [this]() { Reset(); });\n\n\tdelete shortcutFilter;\n\tshortcutFilter = CreateShortcutFilter();\n\tinstallEventFilter(shortcutFilter);\n\n\tresize(800, 280);\n\n\tsetWindowTitle(QTStr(\"Basic.Stats\"));\n#ifndef __APPLE__\n\tsetWindowIcon(QIcon::fromTheme(\"obs\", QIcon(\":/res/images/obs.png\")));\n#endif\n\n\tsetWindowModality(Qt::NonModal);\n\tsetAttribute(Qt::WA_DeleteOnClose, true);\n\n\tQObject::connect(&timer, &QTimer::timeout, this, &OBSBasicStats::Update);\n\ttimer.setInterval(TIMER_INTERVAL);\n\n\tif (isVisible())\n\t\ttimer.start();\n\n\tUpdate();\n\n\tQObject::connect(&recTimeLeft, &QTimer::timeout, this, &OBSBasicStats::RecordingTimeLeft);\n\trecTimeLeft.setInterval(REC_TIME_LEFT_INTERVAL);\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tconst char *geometry = config_get_string(main->Config(), \"Stats\", \"geometry\");\n\tif (geometry != NULL) {\n\t\tQByteArray byteArray = QByteArray::fromBase64(QByteArray(geometry));\n\t\trestoreGeometry(byteArray);\n\n\t\tQRect windowGeometry = normalGeometry();\n\t\tif (!WindowPositionValid(windowGeometry)) {\n\t\t\tQRect rect = QGuiApplication::primaryScreen()->geometry();\n\t\t\tsetGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, size(), rect));\n\t\t}\n\t}\n\n\tobs_frontend_add_event_callback(OBSFrontendEvent, this);\n\n\tif (obs_frontend_recording_active())\n\t\tStartRecTimeLeft();\n}\n\nvoid OBSBasicStats::closeEvent(QCloseEvent *event)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tif (isVisible()) {\n\t\tconfig_set_string(main->Config(), \"Stats\", \"geometry\", saveGeometry().toBase64().constData());\n\t\tconfig_save_safe(main->Config(), \"tmp\", nullptr);\n\t}\n\n\t// This code is only reached when the non-dockable stats window is\n\t// manually closed or OBS is exiting.\n\tobs_frontend_remove_event_callback(OBSFrontendEvent, this);\n\n\tQWidget::closeEvent(event);\n}\n\nOBSBasicStats::~OBSBasicStats()\n{\n\tdelete shortcutFilter;\n\tos_cpu_usage_info_destroy(cpu_info);\n}\n\nvoid OBSBasicStats::AddOutputLabels(QString name)\n{\n\tOutputLabels ol;\n\tol.name = new QLabel(name, this);\n\tol.status = new QLabel(this);\n\tol.droppedFrames = new QLabel(this);\n\tol.megabytesSent = new QLabel(this);\n\tol.bitrate = new QLabel(this);\n\n\tint col = 0;\n\tint row = outputLabels.size() + 1;\n\toutputLayout->addWidget(ol.name, row, col++);\n\toutputLayout->addWidget(ol.status, row, col++);\n\toutputLayout->addWidget(ol.droppedFrames, row, col++);\n\toutputLayout->addWidget(ol.megabytesSent, row, col++);\n\toutputLayout->addWidget(ol.bitrate, row, col++);\n\toutputLabels.push_back(ol);\n}\n\nstatic uint32_t first_encoded = 0xFFFFFFFF;\nstatic uint32_t first_skipped = 0xFFFFFFFF;\nstatic uint32_t first_rendered = 0xFFFFFFFF;\nstatic uint32_t first_lagged = 0xFFFFFFFF;\n\nvoid OBSBasicStats::InitializeValues()\n{\n\tvideo_t *video = obs_get_video();\n\tfirst_encoded = video_output_get_total_frames(video);\n\tfirst_skipped = video_output_get_skipped_frames(video);\n\tfirst_rendered = obs_get_total_frames();\n\tfirst_lagged = obs_get_lagged_frames();\n}\n\nvoid OBSBasicStats::Update()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\t/* TODO: Un-hardcode */\n\n\tstruct obs_video_info ovi = {};\n\tobs_get_video_info(&ovi);\n\n\tOBSOutputAutoRelease strOutput = obs_frontend_get_streaming_output();\n\tOBSOutputAutoRelease recOutput = obs_frontend_get_recording_output();\n\n\tif (!strOutput && !recOutput)\n\t\treturn;\n\n\t/* ------------------------------------------- */\n\t/* general usage                               */\n\n\tdouble curFPS = obs_get_active_fps();\n\tdouble obsFPS = (double)ovi.fps_num / (double)ovi.fps_den;\n\n\tQString str = QString::number(curFPS, 'f', 2);\n\tfps->setText(str);\n\n\tif (curFPS < (obsFPS * 0.8))\n\t\tsetClasses(fps, \"text-danger\");\n\telse if (curFPS < (obsFPS * 0.95))\n\t\tsetClasses(fps, \"text-warning\");\n\telse\n\t\tsetClasses(fps, \"\");\n\n\t/* ------------------ */\n\n\tdouble usage = os_cpu_usage_info_query(cpu_info);\n\tstr = QString::number(usage, 'g', 2) + QStringLiteral(\"%\");\n\tcpuUsage->setText(str);\n\n\t/* ------------------ */\n\n\tconst char *path = main->GetCurrentOutputPath();\n\n#define MBYTE (1024ULL * 1024ULL)\n#define GBYTE (1024ULL * 1024ULL * 1024ULL)\n#define TBYTE (1024ULL * 1024ULL * 1024ULL * 1024ULL)\n\tnum_bytes = os_get_free_disk_space(path);\n\tQString abrv = QStringLiteral(\" MB\");\n\tlong double num;\n\n\tnum = (long double)num_bytes / (1024.0l * 1024.0l);\n\tif (num_bytes > TBYTE) {\n\t\tnum /= 1024.0l * 1024.0l;\n\t\tabrv = QStringLiteral(\" TB\");\n\t} else if (num_bytes > GBYTE) {\n\t\tnum /= 1024.0l;\n\t\tabrv = QStringLiteral(\" GB\");\n\t}\n\n\tstr = QString::number(num, 'f', 1) + abrv;\n\thddSpace->setText(str);\n\n\tif (num_bytes < GBYTE)\n\t\tsetClasses(hddSpace, \"text-danger\");\n\telse if (num_bytes < (5 * GBYTE))\n\t\tsetClasses(hddSpace, \"text-warning\");\n\telse\n\t\tsetClasses(hddSpace, \"\");\n\n\t/* ------------------ */\n\n\tnum = (long double)os_get_proc_resident_size() / (1024.0l * 1024.0l);\n\n\tstr = QString::number(num, 'f', 1) + QStringLiteral(\" MB\");\n\tmemUsage->setText(str);\n\n\t/* ------------------ */\n\n\tnum = (long double)obs_get_average_frame_time_ns() / 1000000.0l;\n\n\tstr = QString::number(num, 'f', 1) + QStringLiteral(\" ms\");\n\trenderTime->setText(str);\n\n\tlong double fpsFrameTime = (long double)ovi.fps_den * 1000.0l / (long double)ovi.fps_num;\n\n\tif (num > fpsFrameTime)\n\t\tsetClasses(renderTime, \"text-danger\");\n\telse if (num > fpsFrameTime * 0.75l)\n\t\tsetClasses(renderTime, \"text-warning\");\n\telse\n\t\tsetClasses(renderTime, \"\");\n\n\t/* ------------------ */\n\n\tvideo_t *video = obs_get_video();\n\tuint32_t total_encoded = video_output_get_total_frames(video);\n\tuint32_t total_skipped = video_output_get_skipped_frames(video);\n\n\tif (total_encoded < first_encoded || total_skipped < first_skipped) {\n\t\tfirst_encoded = total_encoded;\n\t\tfirst_skipped = total_skipped;\n\t}\n\ttotal_encoded -= first_encoded;\n\ttotal_skipped -= first_skipped;\n\n\tnum = total_encoded ? (long double)total_skipped / (long double)total_encoded : 0.0l;\n\tnum *= 100.0l;\n\n\tstr = QString(\"%1 / %2 (%3%)\")\n\t\t      .arg(QString::number(total_skipped), QString::number(total_encoded),\n\t\t\t   QString::number(num, 'f', 1));\n\tskippedFrames->setText(str);\n\n\tif (num > 5.0l)\n\t\tsetClasses(skippedFrames, \"text-danger\");\n\telse if (num > 1.0l)\n\t\tsetClasses(skippedFrames, \"text-warning\");\n\telse\n\t\tsetClasses(skippedFrames, \"\");\n\n\t/* ------------------ */\n\n\tuint32_t total_rendered = obs_get_total_frames();\n\tuint32_t total_lagged = obs_get_lagged_frames();\n\n\tif (total_rendered < first_rendered || total_lagged < first_lagged) {\n\t\tfirst_rendered = total_rendered;\n\t\tfirst_lagged = total_lagged;\n\t}\n\ttotal_rendered -= first_rendered;\n\ttotal_lagged -= first_lagged;\n\n\tnum = total_rendered ? (long double)total_lagged / (long double)total_rendered : 0.0l;\n\tnum *= 100.0l;\n\n\tstr = MakeMissedFramesText(total_lagged, total_rendered, num);\n\tmissedFrames->setText(str);\n\n\tif (num > 5.0l)\n\t\tsetClasses(missedFrames, \"text-danger\");\n\telse if (num > 1.0l)\n\t\tsetClasses(missedFrames, \"text-warning\");\n\telse\n\t\tsetClasses(missedFrames, \"\");\n\n\t/* ------------------------------------------- */\n\t/* recording/streaming stats                   */\n\n\toutputLabels[0].Update(strOutput, false);\n\toutputLabels[1].Update(recOutput, true);\n\n\tif (obs_output_active(recOutput)) {\n\t\tlong double kbps = outputLabels[1].kbps;\n\t\tbitrates.push_back(kbps);\n\t}\n}\n\nvoid OBSBasicStats::StartRecTimeLeft()\n{\n\tif (recTimeLeft.isActive())\n\t\tResetRecTimeLeft();\n\n\trecordTimeLeft->setText(QTStr(\"Calculating\"));\n\trecTimeLeft.start();\n}\n\nvoid OBSBasicStats::ResetRecTimeLeft()\n{\n\tif (recTimeLeft.isActive()) {\n\t\tbitrates.clear();\n\t\trecTimeLeft.stop();\n\t\trecordTimeLeft->setText(QTStr(\"\"));\n\t}\n}\n\nvoid OBSBasicStats::RecordingTimeLeft()\n{\n\tif (bitrates.empty())\n\t\treturn;\n\n\tlong double averageBitrate = accumulate(bitrates.begin(), bitrates.end(), 0.0) / (long double)bitrates.size();\n\tif (averageBitrate == 0)\n\t\treturn;\n\n\tlong double bytesPerSec = (averageBitrate / 8.0l) * 1000.0l;\n\tlong double secondsUntilFull = (long double)num_bytes / bytesPerSec;\n\n\tbitrates.clear();\n\n\tint totalMinutes = (int)secondsUntilFull / 60;\n\tint minutes = totalMinutes % 60;\n\tint hours = totalMinutes / 60;\n\n\tQString text = MakeTimeLeftText(hours, minutes);\n\trecordTimeLeft->setText(text);\n\trecordTimeLeft->setMinimumWidth(recordTimeLeft->width());\n}\n\nvoid OBSBasicStats::Reset()\n{\n\ttimer.start();\n\n\tfirst_encoded = 0xFFFFFFFF;\n\tfirst_skipped = 0xFFFFFFFF;\n\tfirst_rendered = 0xFFFFFFFF;\n\tfirst_lagged = 0xFFFFFFFF;\n\n\tOBSOutputAutoRelease strOutput = obs_frontend_get_streaming_output();\n\tOBSOutputAutoRelease recOutput = obs_frontend_get_recording_output();\n\n\toutputLabels[0].Reset(strOutput);\n\toutputLabels[1].Reset(recOutput);\n\tUpdate();\n}\n\nvoid OBSBasicStats::OutputLabels::Update(obs_output_t *output, bool rec)\n{\n\tuint64_t totalBytes = output ? obs_output_get_total_bytes(output) : 0;\n\tuint64_t curTime = os_gettime_ns();\n\tuint64_t bytesSent = totalBytes;\n\n\tif (bytesSent < lastBytesSent)\n\t\tbytesSent = 0;\n\tif (bytesSent == 0)\n\t\tlastBytesSent = 0;\n\n\tuint64_t bitsBetween = (bytesSent - lastBytesSent) * 8;\n\tlong double timePassed = (long double)(curTime - lastBytesSentTime) / 1000000000.0l;\n\tkbps = (long double)bitsBetween / timePassed / 1000.0l;\n\n\tif (timePassed < 0.01l)\n\t\tkbps = 0.0l;\n\n\tQString str = QTStr(\"Basic.Stats.Status.Inactive\");\n\tQString styling;\n\tbool active = output ? obs_output_active(output) : false;\n\tif (rec) {\n\t\tif (active)\n\t\t\tstr = QTStr(\"Basic.Stats.Status.Recording\");\n\t} else {\n\t\tif (active) {\n\t\t\tbool reconnecting = output ? obs_output_reconnecting(output) : false;\n\n\t\t\tif (reconnecting) {\n\t\t\t\tstr = QTStr(\"Basic.Stats.Status.Reconnecting\");\n\t\t\t\tstyling = \"text-danger\";\n\t\t\t} else {\n\t\t\t\tstr = QTStr(\"Basic.Stats.Status.Live\");\n\t\t\t\tstyling = \"text-success\";\n\t\t\t}\n\t\t}\n\t}\n\n\tstatus->setText(str);\n\tsetClasses(status, styling);\n\n\tlong double num = (long double)totalBytes / (1024.0l * 1024.0l);\n\tconst char *unit = \"MiB\";\n\tif (num > 1024) {\n\t\tnum /= 1024;\n\t\tunit = \"GiB\";\n\t}\n\tmegabytesSent->setText(QString(\"%1 %2\").arg(num, 0, 'f', 1).arg(unit));\n\n\tnum = kbps;\n\tunit = \"kb/s\";\n\tif (num >= 10'000) {\n\t\tnum /= 1000;\n\t\tunit = \"Mb/s\";\n\t}\n\tbitrate->setText(QString(\"%1 %2\").arg(num, 0, 'f', 0).arg(unit));\n\n\tif (!rec) {\n\t\tint total = output ? obs_output_get_total_frames(output) : 0;\n\t\tint dropped = output ? obs_output_get_frames_dropped(output) : 0;\n\n\t\tif (total < first_total || dropped < first_dropped) {\n\t\t\tfirst_total = 0;\n\t\t\tfirst_dropped = 0;\n\t\t}\n\n\t\ttotal -= first_total;\n\t\tdropped -= first_dropped;\n\n\t\tnum = total ? (long double)dropped / (long double)total * 100.0l : 0.0l;\n\n\t\tstr = QString(\"%1 / %2 (%3%)\")\n\t\t\t      .arg(QString::number(dropped), QString::number(total), QString::number(num, 'f', 1));\n\t\tdroppedFrames->setText(str);\n\n\t\tif (num > 5.0l)\n\t\t\tsetClasses(droppedFrames, \"text-danger\");\n\t\telse if (num > 1.0l)\n\t\t\tsetClasses(droppedFrames, \"text-warning\");\n\t\telse\n\t\t\tsetClasses(droppedFrames, \"\");\n\t}\n\n\tlastBytesSent = bytesSent;\n\tlastBytesSentTime = curTime;\n}\n\nvoid OBSBasicStats::OutputLabels::Reset(obs_output_t *output)\n{\n\tif (!output)\n\t\treturn;\n\n\tfirst_total = obs_output_get_total_frames(output);\n\tfirst_dropped = obs_output_get_frames_dropped(output);\n}\n\nvoid OBSBasicStats::showEvent(QShowEvent *)\n{\n\ttimer.start(TIMER_INTERVAL);\n}\n\nvoid OBSBasicStats::hideEvent(QHideEvent *)\n{\n\ttimer.stop();\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasicStats.hpp",
    "content": "#pragma once\n\n#include <obs-frontend-api.h>\n#include <obs.hpp>\n#include <util/platform.h>\n\n#include <QFrame>\n#include <QPointer>\n#include <QTimer>\n\nclass QLabel;\nclass QGridLayout;\n\nclass OBSBasicStats : public QFrame {\n\tQ_OBJECT\n\n\tQLabel *fps = nullptr;\n\tQLabel *cpuUsage = nullptr;\n\tQLabel *hddSpace = nullptr;\n\tQLabel *recordTimeLeft = nullptr;\n\tQLabel *memUsage = nullptr;\n\n\tQLabel *renderTime = nullptr;\n\tQLabel *skippedFrames = nullptr;\n\tQLabel *missedFrames = nullptr;\n\n\tQGridLayout *outputLayout = nullptr;\n\n\tos_cpu_usage_info_t *cpu_info = nullptr;\n\n\tQTimer timer;\n\tQTimer recTimeLeft;\n\tuint64_t num_bytes = 0;\n\tstd::vector<long double> bitrates;\n\n\tstruct OutputLabels {\n\t\tQPointer<QLabel> name;\n\t\tQPointer<QLabel> status;\n\t\tQPointer<QLabel> droppedFrames;\n\t\tQPointer<QLabel> megabytesSent;\n\t\tQPointer<QLabel> bitrate;\n\n\t\tuint64_t lastBytesSent = 0;\n\t\tuint64_t lastBytesSentTime = 0;\n\n\t\tint first_total = 0;\n\t\tint first_dropped = 0;\n\n\t\tvoid Update(obs_output_t *output, bool rec);\n\t\tvoid Reset(obs_output_t *output);\n\n\t\tlong double kbps = 0.0l;\n\t};\n\n\tQList<OutputLabels> outputLabels;\n\n\tvoid AddOutputLabels(QString name);\n\tvoid Update();\n\n\tvirtual void closeEvent(QCloseEvent *event) override;\n\n\tstatic void OBSFrontendEvent(enum obs_frontend_event event, void *ptr);\n\npublic:\n\tOBSBasicStats(QWidget *parent = nullptr, bool closable = true);\n\t~OBSBasicStats();\n\n\tstatic void InitializeValues();\n\n\tvoid StartRecTimeLeft();\n\tvoid ResetRecTimeLeft();\n\nprivate:\n\tQPointer<QObject> shortcutFilter;\n\nprivate slots:\n\tvoid RecordingTimeLeft();\n\npublic slots:\n\tvoid Reset();\n\nprotected:\n\tvirtual void showEvent(QShowEvent *event) override;\n\tvirtual void hideEvent(QHideEvent *event) override;\n};\n"
  },
  {
    "path": "frontend/widgets/OBSBasicStatusBar.cpp",
    "content": "#include \"OBSBasicStatusBar.hpp\"\n#include \"ui_StatusBarWidget.h\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include \"moc_OBSBasicStatusBar.cpp\"\n\nstatic constexpr int bitrateUpdateSeconds = 2;\nstatic constexpr int congestionUpdateSeconds = 4;\nstatic constexpr float excellentThreshold = 0.0f;\nstatic constexpr float goodThreshold = 0.3333f;\nstatic constexpr float mediocreThreshold = 0.6667f;\nstatic constexpr float badThreshold = 1.0f;\n\nOBSBasicStatusBar::OBSBasicStatusBar(QWidget *parent)\n\t: QStatusBar(parent),\n\t  excellentPixmap(QIcon(\":/res/images/network-excellent.svg\").pixmap(QSize(16, 16))),\n\t  goodPixmap(QIcon(\":/res/images/network-good.svg\").pixmap(QSize(16, 16))),\n\t  mediocrePixmap(QIcon(\":/res/images/network-mediocre.svg\").pixmap(QSize(16, 16))),\n\t  badPixmap(QIcon(\":/res/images/network-bad.svg\").pixmap(QSize(16, 16))),\n\t  recordingActivePixmap(QIcon(\":/res/images/recording-active.svg\").pixmap(QSize(16, 16))),\n\t  recordingPausePixmap(QIcon(\":/res/images/recording-pause.svg\").pixmap(QSize(16, 16))),\n\t  streamingActivePixmap(QIcon(\":/res/images/streaming-active.svg\").pixmap(QSize(16, 16)))\n{\n\tcongestionArray.reserve(congestionUpdateSeconds);\n\n\tstatusWidget = new StatusBarWidget(this);\n\tstatusWidget->ui->delayInfo->setText(\"\");\n\tstatusWidget->ui->droppedFrames->setText(QTStr(\"DroppedFrames\").arg(\"0\", \"0.0\"));\n\tstatusWidget->ui->statusIcon->setPixmap(inactivePixmap);\n\tstatusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);\n\tstatusWidget->ui->streamTime->setDisabled(true);\n\tstatusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);\n\tstatusWidget->ui->recordTime->setDisabled(true);\n\tstatusWidget->ui->delayFrame->hide();\n\tstatusWidget->ui->issuesFrame->hide();\n\tstatusWidget->ui->kbps->hide();\n\n\taddPermanentWidget(statusWidget, 1);\n\tsetMinimumHeight(statusWidget->height());\n\n\tUpdateIcons();\n\tconnect(App(), &OBSApp::StyleChanged, this, &OBSBasicStatusBar::UpdateIcons);\n\n\tmessageTimer = new QTimer(this);\n\tmessageTimer->setSingleShot(true);\n\tconnect(messageTimer, &QTimer::timeout, this, &OBSBasicStatusBar::clearMessage);\n\n\tclearMessage();\n}\n\nvoid OBSBasicStatusBar::Activate()\n{\n\tif (!active) {\n\t\trefreshTimer = new QTimer(this);\n\t\tconnect(refreshTimer, &QTimer::timeout, this, &OBSBasicStatusBar::UpdateStatusBar);\n\n\t\tint skipped = video_output_get_skipped_frames(obs_get_video());\n\t\tint total = video_output_get_total_frames(obs_get_video());\n\n\t\ttotalStreamSeconds = 0;\n\t\ttotalRecordSeconds = 0;\n\t\tlastSkippedFrameCount = 0;\n\t\tstartSkippedFrameCount = skipped;\n\t\tstartTotalFrameCount = total;\n\n\t\trefreshTimer->start(1000);\n\t\tactive = true;\n\n\t\tif (streamOutput) {\n\t\t\tstatusWidget->ui->statusIcon->setPixmap(inactivePixmap);\n\t\t}\n\t}\n\n\tif (streamOutput) {\n\t\tstatusWidget->ui->streamIcon->setPixmap(streamingActivePixmap);\n\t\tstatusWidget->ui->streamTime->setDisabled(false);\n\t\tstatusWidget->ui->issuesFrame->show();\n\t\tstatusWidget->ui->kbps->show();\n\t\tfirstCongestionUpdate = true;\n\t}\n\n\tif (recordOutput) {\n\t\tstatusWidget->ui->recordIcon->setPixmap(recordingActivePixmap);\n\t\tstatusWidget->ui->recordTime->setDisabled(false);\n\t}\n}\n\nvoid OBSBasicStatusBar::Deactivate()\n{\n\tOBSBasic *main = qobject_cast<OBSBasic *>(parent());\n\tif (!main)\n\t\treturn;\n\n\tif (!streamOutput) {\n\t\tstatusWidget->ui->streamTime->setText(QString(\"00:00:00\"));\n\t\tstatusWidget->ui->streamTime->setDisabled(true);\n\t\tstatusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);\n\t\tstatusWidget->ui->statusIcon->setPixmap(inactivePixmap);\n\t\tstatusWidget->ui->delayFrame->hide();\n\t\tstatusWidget->ui->issuesFrame->hide();\n\t\tstatusWidget->ui->kbps->hide();\n\t\ttotalStreamSeconds = 0;\n\t\tcongestionArray.clear();\n\t\tdisconnected = false;\n\t\tfirstCongestionUpdate = false;\n\t}\n\n\tif (!recordOutput) {\n\t\tstatusWidget->ui->recordTime->setText(QString(\"00:00:00\"));\n\t\tstatusWidget->ui->recordTime->setDisabled(true);\n\t\tstatusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);\n\t\ttotalRecordSeconds = 0;\n\t}\n\n\tif (main->outputHandler && !main->outputHandler->Active()) {\n\t\tdelete refreshTimer;\n\n\t\tstatusWidget->ui->delayInfo->setText(\"\");\n\t\tstatusWidget->ui->droppedFrames->setText(QTStr(\"DroppedFrames\").arg(\"0\", \"0.0\"));\n\t\tstatusWidget->ui->kbps->setText(\"0 kbps\");\n\n\t\tdelaySecTotal = 0;\n\t\tdelaySecStarting = 0;\n\t\tdelaySecStopping = 0;\n\t\treconnectTimeout = 0;\n\t\tactive = false;\n\t\toverloadedNotify = true;\n\n\t\tstatusWidget->ui->statusIcon->setPixmap(inactivePixmap);\n\t}\n}\n\nvoid OBSBasicStatusBar::UpdateDelayMsg()\n{\n\tQString msg;\n\n\tif (delaySecTotal) {\n\t\tif (delaySecStarting && !delaySecStopping) {\n\t\t\tmsg = QTStr(\"Basic.StatusBar.DelayStartingIn\");\n\t\t\tmsg = msg.arg(QString::number(delaySecStarting));\n\n\t\t} else if (!delaySecStarting && delaySecStopping) {\n\t\t\tmsg = QTStr(\"Basic.StatusBar.DelayStoppingIn\");\n\t\t\tmsg = msg.arg(QString::number(delaySecStopping));\n\n\t\t} else if (delaySecStarting && delaySecStopping) {\n\t\t\tmsg = QTStr(\"Basic.StatusBar.DelayStartingStoppingIn\");\n\t\t\tmsg = msg.arg(QString::number(delaySecStopping), QString::number(delaySecStarting));\n\t\t} else {\n\t\t\tmsg = QTStr(\"Basic.StatusBar.Delay\");\n\t\t\tmsg = msg.arg(QString::number(delaySecTotal));\n\t\t}\n\n\t\tif (!statusWidget->ui->delayFrame->isVisible())\n\t\t\tstatusWidget->ui->delayFrame->show();\n\n\t\tstatusWidget->ui->delayInfo->setText(msg);\n\t}\n}\n\nvoid OBSBasicStatusBar::UpdateBandwidth()\n{\n\tif (!streamOutput)\n\t\treturn;\n\n\tif (++seconds < bitrateUpdateSeconds)\n\t\treturn;\n\n\tOBSOutput output = OBSGetStrongRef(streamOutput);\n\tif (!output)\n\t\treturn;\n\n\tuint64_t bytesSent = obs_output_get_total_bytes(output);\n\tuint64_t bytesSentTime = os_gettime_ns();\n\n\tif (bytesSent < lastBytesSent)\n\t\tbytesSent = 0;\n\tif (bytesSent == 0)\n\t\tlastBytesSent = 0;\n\n\tuint64_t bitsBetween = (bytesSent - lastBytesSent) * 8;\n\n\tdouble timePassed = double(bytesSentTime - lastBytesSentTime) / 1000000000.0;\n\n\tdouble kbitsPerSec = double(bitsBetween) / timePassed / 1000.0;\n\n\tQString text;\n\ttext += QString::number(kbitsPerSec, 'f', 0) + QString(\" kbps\");\n\n\tstatusWidget->ui->kbps->setText(text);\n\tstatusWidget->ui->kbps->setMinimumWidth(statusWidget->ui->kbps->width());\n\n\tif (!statusWidget->ui->kbps->isVisible())\n\t\tstatusWidget->ui->kbps->show();\n\n\tlastBytesSent = bytesSent;\n\tlastBytesSentTime = bytesSentTime;\n\tseconds = 0;\n}\n\nvoid OBSBasicStatusBar::UpdateCPUUsage()\n{\n\tOBSBasic *main = qobject_cast<OBSBasic *>(parent());\n\tif (!main)\n\t\treturn;\n\n\tQString text;\n\ttext += QString(\"CPU: \") + QString::number(main->GetCPUUsage(), 'f', 1) + QString(\"%\");\n\n\tstatusWidget->ui->cpuUsage->setText(text);\n\tstatusWidget->ui->cpuUsage->setMinimumWidth(statusWidget->ui->cpuUsage->width());\n\n\tUpdateCurrentFPS();\n}\n\nvoid OBSBasicStatusBar::UpdateCurrentFPS()\n{\n\tstruct obs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\tfloat targetFPS = (float)ovi.fps_num / (float)ovi.fps_den;\n\n\tQString text = QString::asprintf(\"%.2f / %.2f FPS\", obs_get_active_fps(), targetFPS);\n\n\tstatusWidget->ui->fpsCurrent->setText(text);\n\tstatusWidget->ui->fpsCurrent->setMinimumWidth(statusWidget->ui->fpsCurrent->width());\n}\n\nvoid OBSBasicStatusBar::UpdateStreamTime()\n{\n\ttotalStreamSeconds++;\n\n\tint seconds = totalStreamSeconds % 60;\n\tint totalMinutes = totalStreamSeconds / 60;\n\tint minutes = totalMinutes % 60;\n\tint hours = totalMinutes / 60;\n\n\tQString text = QString::asprintf(\"%02d:%02d:%02d\", hours, minutes, seconds);\n\tstatusWidget->ui->streamTime->setText(text);\n\tif (streamOutput && !statusWidget->ui->streamTime->isEnabled())\n\t\tstatusWidget->ui->streamTime->setDisabled(false);\n\n\tif (reconnectTimeout > 0) {\n\t\tQString msg = QTStr(\"Basic.StatusBar.Reconnecting\")\n\t\t\t\t      .arg(QString::number(retries), QString::number(reconnectTimeout));\n\t\tshowMessage(msg);\n\t\tdisconnected = true;\n\t\tstatusWidget->ui->statusIcon->setPixmap(disconnectedPixmap);\n\t\tcongestionArray.clear();\n\t\treconnectTimeout--;\n\n\t} else if (retries > 0) {\n\t\tQString msg = QTStr(\"Basic.StatusBar.AttemptingReconnect\");\n\t\tshowMessage(msg.arg(QString::number(retries)));\n\t}\n\n\tif (delaySecStopping > 0 || delaySecStarting > 0) {\n\t\tif (delaySecStopping > 0)\n\t\t\t--delaySecStopping;\n\t\tif (delaySecStarting > 0)\n\t\t\t--delaySecStarting;\n\t\tUpdateDelayMsg();\n\t}\n}\n\nextern volatile bool recording_paused;\n\nvoid OBSBasicStatusBar::UpdateRecordTime()\n{\n\tbool paused = os_atomic_load_bool(&recording_paused);\n\n\tif (!paused) {\n\t\ttotalRecordSeconds++;\n\n\t\tif (recordOutput && !statusWidget->ui->recordTime->isEnabled())\n\t\t\tstatusWidget->ui->recordTime->setDisabled(false);\n\t} else {\n\t\tstatusWidget->ui->recordIcon->setPixmap(streamPauseIconToggle ? recordingPauseInactivePixmap\n\t\t\t\t\t\t\t\t\t      : recordingPausePixmap);\n\n\t\tstreamPauseIconToggle = !streamPauseIconToggle;\n\t}\n\n\tUpdateRecordTimeLabel();\n}\n\nvoid OBSBasicStatusBar::UpdateRecordTimeLabel()\n{\n\tint seconds = totalRecordSeconds % 60;\n\tint totalMinutes = totalRecordSeconds / 60;\n\tint minutes = totalMinutes % 60;\n\tint hours = totalMinutes / 60;\n\n\tQString text = QString::asprintf(\"%02d:%02d:%02d\", hours, minutes, seconds);\n\tif (os_atomic_load_bool(&recording_paused)) {\n\t\ttext += QStringLiteral(\" (PAUSED)\");\n\t}\n\n\tstatusWidget->ui->recordTime->setText(text);\n}\n\nvoid OBSBasicStatusBar::UpdateDroppedFrames()\n{\n\tif (!streamOutput)\n\t\treturn;\n\n\tOBSOutput output = OBSGetStrongRef(streamOutput);\n\tif (!output)\n\t\treturn;\n\n\tint totalDropped = obs_output_get_frames_dropped(output);\n\tint totalFrames = obs_output_get_total_frames(output);\n\tdouble percent = (double)totalDropped / (double)totalFrames * 100.0;\n\n\tif (!totalFrames)\n\t\treturn;\n\n\tQString text = QTStr(\"DroppedFrames\");\n\ttext = text.arg(QString::number(totalDropped), QString::number(percent, 'f', 1));\n\tstatusWidget->ui->droppedFrames->setText(text);\n\n\tif (!statusWidget->ui->issuesFrame->isVisible())\n\t\tstatusWidget->ui->issuesFrame->show();\n\n\t/* ----------------------------------- *\n\t * calculate congestion color          */\n\n\tfloat congestion = obs_output_get_congestion(output);\n\tfloat avgCongestion = (congestion + lastCongestion) * 0.5f;\n\tif (avgCongestion < congestion)\n\t\tavgCongestion = congestion;\n\tif (avgCongestion > 1.0f)\n\t\tavgCongestion = 1.0f;\n\n\tlastCongestion = congestion;\n\n\tif (disconnected)\n\t\treturn;\n\n\tbool update = firstCongestionUpdate;\n\tfloat congestionOverTime = avgCongestion;\n\n\tif (congestionArray.size() >= congestionUpdateSeconds) {\n\t\tcongestionOverTime = accumulate(congestionArray.begin(), congestionArray.end(), 0.0f) /\n\t\t\t\t     (float)congestionArray.size();\n\t\tcongestionArray.clear();\n\t\tupdate = true;\n\t} else {\n\t\tcongestionArray.emplace_back(avgCongestion);\n\t}\n\n\tif (update) {\n\t\tif (congestionOverTime <= excellentThreshold + EPSILON)\n\t\t\tstatusWidget->ui->statusIcon->setPixmap(excellentPixmap);\n\t\telse if (congestionOverTime <= goodThreshold)\n\t\t\tstatusWidget->ui->statusIcon->setPixmap(goodPixmap);\n\t\telse if (congestionOverTime <= mediocreThreshold)\n\t\t\tstatusWidget->ui->statusIcon->setPixmap(mediocrePixmap);\n\t\telse if (congestionOverTime <= badThreshold)\n\t\t\tstatusWidget->ui->statusIcon->setPixmap(badPixmap);\n\n\t\tfirstCongestionUpdate = false;\n\t}\n}\n\nvoid OBSBasicStatusBar::OBSOutputReconnect(void *data, calldata_t *params)\n{\n\tOBSBasicStatusBar *statusBar = static_cast<OBSBasicStatusBar *>(data);\n\n\tint seconds = (int)calldata_int(params, \"timeout_sec\");\n\tQMetaObject::invokeMethod(statusBar, \"Reconnect\", Q_ARG(int, seconds));\n}\n\nvoid OBSBasicStatusBar::OBSOutputReconnectSuccess(void *data, calldata_t *)\n{\n\tOBSBasicStatusBar *statusBar = static_cast<OBSBasicStatusBar *>(data);\n\n\tQMetaObject::invokeMethod(statusBar, \"ReconnectSuccess\");\n}\n\nvoid OBSBasicStatusBar::Reconnect(int seconds)\n{\n\tOBSBasic *main = qobject_cast<OBSBasic *>(parent());\n\n\tif (!retries)\n\t\tmain->SysTrayNotify(QTStr(\"Basic.SystemTray.Message.Reconnecting\"), QSystemTrayIcon::Warning);\n\n\treconnectTimeout = seconds;\n\n\tif (streamOutput) {\n\t\tOBSOutput output = OBSGetStrongRef(streamOutput);\n\t\tif (!output)\n\t\t\treturn;\n\n\t\tdelaySecTotal = obs_output_get_active_delay(output);\n\t\tUpdateDelayMsg();\n\n\t\tretries++;\n\t}\n}\n\nvoid OBSBasicStatusBar::ReconnectClear()\n{\n\tretries = 0;\n\treconnectTimeout = 0;\n\tseconds = -1;\n\tlastBytesSent = 0;\n\tlastBytesSentTime = os_gettime_ns();\n\tdelaySecTotal = 0;\n\tUpdateDelayMsg();\n}\n\nvoid OBSBasicStatusBar::ReconnectSuccess()\n{\n\tOBSBasic *main = qobject_cast<OBSBasic *>(parent());\n\n\tQString msg = QTStr(\"Basic.StatusBar.ReconnectSuccessful\");\n\tshowMessage(msg, 4000);\n\tmain->SysTrayNotify(msg, QSystemTrayIcon::Information);\n\tReconnectClear();\n\n\tif (streamOutput) {\n\t\tOBSOutput output = OBSGetStrongRef(streamOutput);\n\t\tif (!output)\n\t\t\treturn;\n\n\t\tdelaySecTotal = obs_output_get_active_delay(output);\n\t\tUpdateDelayMsg();\n\t\tdisconnected = false;\n\t\tfirstCongestionUpdate = true;\n\t}\n}\n\nvoid OBSBasicStatusBar::UpdateStatusBar()\n{\n\tOBSBasic *main = qobject_cast<OBSBasic *>(parent());\n\n\tUpdateBandwidth();\n\n\tif (streamOutput)\n\t\tUpdateStreamTime();\n\n\tif (recordOutput)\n\t\tUpdateRecordTime();\n\n\tUpdateDroppedFrames();\n\n\tint skipped = video_output_get_skipped_frames(obs_get_video());\n\tint total = video_output_get_total_frames(obs_get_video());\n\n\tskipped -= startSkippedFrameCount;\n\ttotal -= startTotalFrameCount;\n\n\tint diff = skipped - lastSkippedFrameCount;\n\tdouble percentage = double(skipped) / double(total) * 100.0;\n\n\tif (diff > 10 && percentage >= 0.1f) {\n\t\tshowMessage(QTStr(\"HighResourceUsage\"), 4000);\n\t\tif (!main->isVisible() && overloadedNotify) {\n\t\t\tmain->SysTrayNotify(QTStr(\"HighResourceUsage\"), QSystemTrayIcon::Warning);\n\t\t\toverloadedNotify = false;\n\t\t}\n\t}\n\n\tlastSkippedFrameCount = skipped;\n}\n\nvoid OBSBasicStatusBar::StreamDelayStarting(int sec)\n{\n\tOBSBasic *main = qobject_cast<OBSBasic *>(parent());\n\tif (!main || !main->outputHandler)\n\t\treturn;\n\n\tOBSOutputAutoRelease output = obs_frontend_get_streaming_output();\n\tstreamOutput = OBSGetWeakRef(output);\n\n\tdelaySecTotal = delaySecStarting = sec;\n\tUpdateDelayMsg();\n\tActivate();\n}\n\nvoid OBSBasicStatusBar::StreamDelayStopping(int sec)\n{\n\tdelaySecTotal = delaySecStopping = sec;\n\tUpdateDelayMsg();\n}\n\nvoid OBSBasicStatusBar::StreamStarted(obs_output_t *output)\n{\n\tstreamOutput = OBSGetWeakRef(output);\n\n\tstreamSigs.emplace_back(obs_output_get_signal_handler(output), \"reconnect\", OBSOutputReconnect, this);\n\tstreamSigs.emplace_back(obs_output_get_signal_handler(output), \"reconnect_success\", OBSOutputReconnectSuccess,\n\t\t\t\tthis);\n\n\tretries = 0;\n\tlastBytesSent = 0;\n\tlastBytesSentTime = os_gettime_ns();\n\tActivate();\n}\n\nvoid OBSBasicStatusBar::StreamStopped()\n{\n\tif (streamOutput) {\n\t\tstreamSigs.clear();\n\n\t\tReconnectClear();\n\t\tstreamOutput = nullptr;\n\t\tclearMessage();\n\t\tDeactivate();\n\t}\n}\n\nvoid OBSBasicStatusBar::RecordingStarted(obs_output_t *output)\n{\n\trecordOutput = OBSGetWeakRef(output);\n\tActivate();\n}\n\nvoid OBSBasicStatusBar::RecordingStopped()\n{\n\trecordOutput = nullptr;\n\tDeactivate();\n}\n\nvoid OBSBasicStatusBar::RecordingPaused()\n{\n\tif (recordOutput) {\n\t\tstatusWidget->ui->recordIcon->setPixmap(recordingPausePixmap);\n\t\tstreamPauseIconToggle = true;\n\t}\n\n\tUpdateRecordTimeLabel();\n}\n\nvoid OBSBasicStatusBar::RecordingUnpaused()\n{\n\tif (recordOutput) {\n\t\tstatusWidget->ui->recordIcon->setPixmap(recordingActivePixmap);\n\t}\n\n\tUpdateRecordTimeLabel();\n}\n\nstatic QPixmap GetPixmap(const QString &filename)\n{\n\tQString path = obs_frontend_is_theme_dark() ? \"theme:Dark/\" : \":/res/images/\";\n\treturn QIcon(path + filename).pixmap(QSize(16, 16));\n}\n\nvoid OBSBasicStatusBar::UpdateIcons()\n{\n\tdisconnectedPixmap = GetPixmap(\"network-disconnected.svg\");\n\tinactivePixmap = GetPixmap(\"network-inactive.svg\");\n\n\tstreamingInactivePixmap = GetPixmap(\"streaming-inactive.svg\");\n\n\trecordingInactivePixmap = GetPixmap(\"recording-inactive.svg\");\n\trecordingPauseInactivePixmap = GetPixmap(\"recording-pause-inactive.svg\");\n\n\tbool streaming = obs_frontend_streaming_active();\n\n\tif (!streaming) {\n\t\tstatusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);\n\t\tstatusWidget->ui->statusIcon->setPixmap(inactivePixmap);\n\t} else {\n\t\tif (disconnected)\n\t\t\tstatusWidget->ui->statusIcon->setPixmap(disconnectedPixmap);\n\t}\n\n\tbool recording = obs_frontend_recording_active();\n\n\tif (!recording)\n\t\tstatusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);\n}\n\nvoid OBSBasicStatusBar::showMessage(const QString &message, int timeout)\n{\n\tmessageTimer->stop();\n\n\tstatusWidget->ui->message->setText(message);\n\n\tif (timeout)\n\t\tmessageTimer->start(timeout);\n}\n\nvoid OBSBasicStatusBar::clearMessage()\n{\n\tstatusWidget->ui->message->setText(\"\");\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasicStatusBar.hpp",
    "content": "#pragma once\n\n#include \"StatusBarWidget.hpp\"\n\n#include <obs.hpp>\n\n#include <QPointer>\n#include <QStatusBar>\n\nclass QTimer;\n\nclass OBSBasicStatusBar : public QStatusBar {\n\tQ_OBJECT\n\nprivate:\n\tStatusBarWidget *statusWidget = nullptr;\n\n\tOBSWeakOutputAutoRelease streamOutput;\n\tstd::vector<OBSSignal> streamSigs;\n\tOBSWeakOutputAutoRelease recordOutput;\n\tbool active = false;\n\tbool overloadedNotify = true;\n\tbool streamPauseIconToggle = false;\n\tbool disconnected = false;\n\tbool firstCongestionUpdate = false;\n\n\tstd::vector<float> congestionArray;\n\n\tint retries = 0;\n\tint totalStreamSeconds = 0;\n\tint totalRecordSeconds = 0;\n\n\tint reconnectTimeout = 0;\n\n\tint delaySecTotal = 0;\n\tint delaySecStarting = 0;\n\tint delaySecStopping = 0;\n\n\tint startSkippedFrameCount = 0;\n\tint startTotalFrameCount = 0;\n\tint lastSkippedFrameCount = 0;\n\n\tint seconds = 0;\n\tuint64_t lastBytesSent = 0;\n\tuint64_t lastBytesSentTime = 0;\n\n\tQPixmap excellentPixmap;\n\tQPixmap goodPixmap;\n\tQPixmap mediocrePixmap;\n\tQPixmap badPixmap;\n\tQPixmap disconnectedPixmap;\n\tQPixmap inactivePixmap;\n\n\tQPixmap recordingActivePixmap;\n\tQPixmap recordingPausePixmap;\n\tQPixmap recordingPauseInactivePixmap;\n\tQPixmap recordingInactivePixmap;\n\tQPixmap streamingActivePixmap;\n\tQPixmap streamingInactivePixmap;\n\n\tfloat lastCongestion = 0.0f;\n\n\tQPointer<QTimer> refreshTimer;\n\tQPointer<QTimer> messageTimer;\n\n\tobs_output_t *GetOutput();\n\n\tvoid Activate();\n\tvoid Deactivate();\n\n\tvoid UpdateDelayMsg();\n\tvoid UpdateBandwidth();\n\tvoid UpdateStreamTime();\n\tvoid UpdateRecordTime();\n\tvoid UpdateRecordTimeLabel();\n\tvoid UpdateDroppedFrames();\n\n\tstatic void OBSOutputReconnect(void *data, calldata_t *params);\n\tstatic void OBSOutputReconnectSuccess(void *data, calldata_t *params);\n\npublic slots:\n\tvoid UpdateCPUUsage();\n\n\tvoid clearMessage();\n\tvoid showMessage(const QString &message, int timeout = 0);\n\nprivate slots:\n\tvoid Reconnect(int seconds);\n\tvoid ReconnectSuccess();\n\tvoid UpdateStatusBar();\n\tvoid UpdateCurrentFPS();\n\tvoid UpdateIcons();\n\npublic:\n\tOBSBasicStatusBar(QWidget *parent);\n\n\tvoid StreamDelayStarting(int sec);\n\tvoid StreamDelayStopping(int sec);\n\tvoid StreamStarted(obs_output_t *output);\n\tvoid StreamStopped();\n\tvoid RecordingStarted(obs_output_t *output);\n\tvoid RecordingStopped();\n\tvoid RecordingPaused();\n\tvoid RecordingUnpaused();\n\n\tvoid ReconnectClear();\n};\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Browser.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#ifdef BROWSER_AVAILABLE\n#include <dialogs/OBSExtraBrowsers.hpp>\n#include <docks/BrowserDock.hpp>\n\n#include <json11.hpp>\n#include <qt-wrappers.hpp>\n\n#include <QDir>\n\nusing namespace json11;\n#endif\n\n#include <random>\n\nstruct QCef;\nstruct QCefCookieManager;\n\nQCef *cef = nullptr;\nQCefCookieManager *panel_cookies = nullptr;\nbool cef_js_avail = false;\n\n#ifdef BROWSER_AVAILABLE\nvoid OBSBasic::ClearExtraBrowserDocks()\n{\n\textraBrowserDockTargets.clear();\n\textraBrowserDockNames.clear();\n\textraBrowserDocks.clear();\n}\n\nvoid OBSBasic::LoadExtraBrowserDocks()\n{\n\tconst char *jsonStr = config_get_string(App()->GetUserConfig(), \"BasicWindow\", \"ExtraBrowserDocks\");\n\n\tstd::string err;\n\tJson json = Json::parse(jsonStr, err);\n\tif (!err.empty())\n\t\treturn;\n\n\tJson::array array = json.array_items();\n\tif (!array.empty())\n\t\textraBrowserMenuDocksSeparator = ui->menuDocks->addSeparator();\n\n\tfor (Json &item : array) {\n\t\tstd::string title = item[\"title\"].string_value();\n\t\tstd::string url = item[\"url\"].string_value();\n\t\tstd::string uuid = item[\"uuid\"].string_value();\n\n\t\tAddExtraBrowserDock(title.c_str(), url.c_str(), uuid.c_str(), false);\n\t}\n}\n\nvoid OBSBasic::SaveExtraBrowserDocks()\n{\n\tJson::array array;\n\tfor (int i = 0; i < extraBrowserDocks.size(); i++) {\n\t\tQDockWidget *dock = extraBrowserDocks[i].get();\n\t\tQString title = extraBrowserDockNames[i];\n\t\tQString url = extraBrowserDockTargets[i];\n\t\tQString uuid = dock->property(\"uuid\").toString();\n\t\tJson::object obj{\n\t\t\t{\"title\", QT_TO_UTF8(title)},\n\t\t\t{\"url\", QT_TO_UTF8(url)},\n\t\t\t{\"uuid\", QT_TO_UTF8(uuid)},\n\t\t};\n\t\tarray.push_back(obj);\n\t}\n\n\tstd::string output = Json(array).dump();\n\tconfig_set_string(App()->GetUserConfig(), \"BasicWindow\", \"ExtraBrowserDocks\", output.c_str());\n}\n\nvoid OBSBasic::ManageExtraBrowserDocks()\n{\n\tif (!extraBrowsers.isNull()) {\n\t\textraBrowsers->show();\n\t\textraBrowsers->raise();\n\t\treturn;\n\t}\n\n\textraBrowsers = new OBSExtraBrowsers(this);\n\textraBrowsers->show();\n}\n\nvoid OBSBasic::AddExtraBrowserDock(const QString &title, const QString &url, const QString &uuid, bool firstCreate)\n{\n\tstatic int panel_version = -1;\n\tif (panel_version == -1) {\n\t\tpanel_version = obs_browser_qcef_version();\n\t}\n\n\tBrowserDock *dock = new BrowserDock(title);\n\tQString bId(uuid.isEmpty() ? QUuid::createUuid().toString() : uuid);\n\tbId.replace(QRegularExpression(\"[{}-]\"), \"\");\n\tdock->setProperty(\"uuid\", bId);\n\tdock->setObjectName(title + OBJ_NAME_SUFFIX);\n\tdock->resize(460, 600);\n\tdock->setMinimumSize(80, 80);\n\tdock->setWindowTitle(title);\n\tdock->setAllowedAreas(Qt::AllDockWidgetAreas);\n\n\tQCefWidget *browser = cef->create_widget(dock, QT_TO_UTF8(url), nullptr);\n\tif (browser && panel_version >= 1)\n\t\tbrowser->allowAllPopups(true);\n\n\tdock->SetWidget(browser);\n\n\t/* Add support for Twitch Dashboard panels */\n\tif (url.contains(\"twitch.tv/popout\") && url.contains(\"dashboard/live\")) {\n\t\tQRegularExpression re(\"twitch.tv\\\\/popout\\\\/([^/]+)\\\\/\");\n\t\tQRegularExpressionMatch match = re.match(url);\n\t\tQString username = match.captured(1);\n\t\tif (username.length() > 0) {\n\t\t\tstd::string script;\n\t\t\tscript = \"Object.defineProperty(document, 'referrer', { get: () => '\";\n\t\t\tscript += \"https://twitch.tv/\";\n\t\t\tscript += QT_TO_UTF8(username);\n\t\t\tscript += \"/dashboard/live\";\n\t\t\tscript += \"'});\";\n\t\t\tbrowser->setStartupScript(script);\n\t\t}\n\t}\n\n\tAddDockWidget(dock, Qt::RightDockWidgetArea, true);\n\textraBrowserDocks.push_back(std::shared_ptr<QDockWidget>(dock));\n\textraBrowserDockNames.push_back(title);\n\textraBrowserDockTargets.push_back(url);\n\n\tif (firstCreate) {\n\t\tdock->setFloating(true);\n\n\t\tQPoint curPos = pos();\n\t\tQSize wSizeD2 = size() / 2;\n\t\tQSize dSizeD2 = dock->size() / 2;\n\n\t\tcurPos.setX(curPos.x() + wSizeD2.width() - dSizeD2.width());\n\t\tcurPos.setY(curPos.y() + wSizeD2.height() - dSizeD2.height());\n\n\t\tdock->move(curPos);\n\t\tdock->setVisible(true);\n\t}\n}\n#endif\n\nstatic std::string GenId()\n{\n\tstd::random_device rd;\n\tstd::mt19937_64 e2(rd());\n\tstd::uniform_int_distribution<uint64_t> dist(0, 0xFFFFFFFFFFFFFFFF);\n\n\tuint64_t id = dist(e2);\n\n\tchar id_str[20];\n\tsnprintf(id_str, sizeof(id_str), \"%016llX\", (unsigned long long)id);\n\treturn std::string(id_str);\n}\n\nvoid CheckExistingCookieId()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tif (config_has_user_value(main->Config(), \"Panels\", \"CookieId\"))\n\t\treturn;\n\n\tconfig_set_string(main->Config(), \"Panels\", \"CookieId\", GenId().c_str());\n}\n\n#ifdef BROWSER_AVAILABLE\nstatic void InitPanelCookieManager()\n{\n\tif (!cef)\n\t\treturn;\n\tif (panel_cookies)\n\t\treturn;\n\n\tCheckExistingCookieId();\n\n\tOBSBasic *main = OBSBasic::Get();\n\tconst char *cookie_id = config_get_string(main->Config(), \"Panels\", \"CookieId\");\n\n\tstd::string sub_path;\n\tsub_path += \"obs_profile_cookies/\";\n\tsub_path += cookie_id;\n\n\tpanel_cookies = cef->create_cookie_manager(sub_path);\n}\n#endif\n\nvoid DestroyPanelCookieManager()\n{\n#ifdef BROWSER_AVAILABLE\n\tif (panel_cookies) {\n\t\tpanel_cookies->FlushStore();\n\t\tdelete panel_cookies;\n\t\tpanel_cookies = nullptr;\n\t}\n#endif\n}\n\nvoid DeleteCookies()\n{\n#ifdef BROWSER_AVAILABLE\n\tif (panel_cookies) {\n\t\tpanel_cookies->DeleteCookies(\"\", \"\");\n\t}\n#endif\n}\n\nvoid DuplicateCurrentCookieProfile(ConfigFile &config)\n{\n#ifdef BROWSER_AVAILABLE\n\tif (cef) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tstd::string cookie_id = config_get_string(main->Config(), \"Panels\", \"CookieId\");\n\n\t\tstd::string src_path;\n\t\tsrc_path += \"obs_profile_cookies/\";\n\t\tsrc_path += cookie_id;\n\n\t\tstd::string new_id = GenId();\n\n\t\tstd::string dst_path;\n\t\tdst_path += \"obs_profile_cookies/\";\n\t\tdst_path += new_id;\n\n\t\tBPtr<char> src_path_full = cef->get_cookie_path(src_path);\n\t\tBPtr<char> dst_path_full = cef->get_cookie_path(dst_path);\n\n\t\tQDir srcDir(src_path_full.Get());\n\t\tQDir dstDir(dst_path_full.Get());\n\n\t\tif (srcDir.exists()) {\n\t\t\tif (!dstDir.exists())\n\t\t\t\tdstDir.mkdir(dst_path_full.Get());\n\n\t\t\tQStringList files = srcDir.entryList(QDir::Files);\n\t\t\tfor (const QString &file : files) {\n\t\t\t\tQString src = QString(src_path_full);\n\t\t\t\tQString dst = QString(dst_path_full);\n\t\t\t\tsrc += QDir::separator() + file;\n\t\t\t\tdst += QDir::separator() + file;\n\t\t\t\tQFile::copy(src, dst);\n\t\t\t}\n\t\t}\n\n\t\tconfig_set_string(config, \"Panels\", \"CookieId\", cookie_id.c_str());\n\t\tconfig_set_string(main->Config(), \"Panels\", \"CookieId\", new_id.c_str());\n\t}\n#else\n\tUNUSED_PARAMETER(config);\n#endif\n}\n\nvoid OBSBasic::InitBrowserPanelSafeBlock()\n{\n#ifdef BROWSER_AVAILABLE\n\tif (!cef)\n\t\treturn;\n\tif (cef->init_browser()) {\n\t\tInitPanelCookieManager();\n\t\treturn;\n\t}\n\n\tExecThreadedWithoutBlocking([] { cef->wait_for_browser_init(); }, QTStr(\"BrowserPanelInit.Title\"),\n\t\t\t\t    QTStr(\"BrowserPanelInit.Text\"));\n\tInitPanelCookieManager();\n#endif\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Canvases.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Dennis Sädtler <saedtler@twitch.tv>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\nvoid OBSBasic::CanvasRemoved(void *data, calldata_t *params)\n{\n\tobs_canvas_t *canvas = static_cast<obs_canvas_t *>(calldata_ptr(params, \"canvas\"));\n\tQMetaObject::invokeMethod(static_cast<OBSBasic *>(data), \"RemoveCanvas\", Q_ARG(OBSCanvas, OBSCanvas(canvas)));\n}\n\nconst OBS::Canvas &OBSBasic::AddCanvas(const std::string &name, obs_video_info *ovi, int flags)\n{\n\tOBSCanvas canvas = obs_canvas_create(name.c_str(), ovi, flags);\n\tauto &it = canvases.emplace_back(canvas);\n\tOnEvent(OBS_FRONTEND_EVENT_CANVAS_ADDED);\n\treturn it;\n}\n\nbool OBSBasic::RemoveCanvas(OBSCanvas canvas)\n{\n\tbool removed = false;\n\tif (!canvas)\n\t\treturn removed;\n\n\tauto canvas_it = std::find(std::begin(canvases), std::end(canvases), canvas);\n\tif (canvas_it != std::end(canvases)) {\n\t\t// Move canvas to a temporary object to delay removal of the canvas and calls to its signal handlers\n\t\t// until after erase() completes. This is to avoid issues with recursion coming from the\n\t\t// CanvasRemoved() signal handler.\n\t\tOBS::Canvas tmp = std::move(*canvas_it);\n\t\tcanvases.erase(canvas_it);\n\t\tremoved = true;\n\t}\n\n\tif (removed)\n\t\tOnEvent(OBS_FRONTEND_EVENT_CANVAS_REMOVED);\n\n\treturn removed;\n}\n\nvoid OBSBasic::ClearCanvases()\n{\n\t// Delete canvases one-by-one to ensure OBS_FRONTEND_EVENT_CANVAS_REMOVED is sent for each\n\twhile (!canvases.empty()) {\n\t\tRemoveCanvas(OBSCanvas(canvases.back()));\n\t}\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Clipboard.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <components/VolumeControl.hpp>\n#include <dialogs/OBSBasicFilters.hpp>\n#include <dialogs/OBSBasicSourceSelect.hpp>\n\nextern void undo_redo(const std::string &data);\n\nvoid OBSBasic::on_actionCopyTransform_triggered()\n{\n\tOBSSceneItem item = GetCurrentSceneItem();\n\n\tobs_sceneitem_get_info2(item, &copiedTransformInfo);\n\tobs_sceneitem_get_crop(item, &copiedCropInfo);\n\n\tui->actionPasteTransform->setEnabled(true);\n\thasCopiedTransform = true;\n}\n\nvoid OBSBasic::on_actionPasteTransform_triggered()\n{\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tauto func = [](obs_scene_t *, obs_sceneitem_t *item, void *) {\n\t\tif (!obs_sceneitem_selected(item))\n\t\t\treturn true;\n\t\tif (obs_sceneitem_locked(item))\n\t\t\treturn true;\n\n\t\tOBSBasic *main = OBSBasic::Get();\n\n\t\tobs_sceneitem_defer_update_begin(item);\n\t\tobs_sceneitem_set_info2(item, &main->copiedTransformInfo);\n\t\tobs_sceneitem_set_crop(item, &main->copiedCropInfo);\n\t\tobs_sceneitem_defer_update_end(item);\n\n\t\treturn true;\n\t};\n\n\tobs_scene_enum_items(GetCurrentScene(), func, nullptr);\n\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(QTStr(\"Undo.Transform.Paste\").arg(obs_source_get_name(GetCurrentSceneSource())), undo_redo,\n\t\t\t  undo_redo, undo_data, redo_data);\n}\n\nvoid OBSBasic::on_actionCopySource_triggered()\n{\n\tclipboard.clear();\n\n\tfor (auto &selectedSource : GetAllSelectedSourceItems()) {\n\t\tOBSSceneItem item = ui->sources->Get(selectedSource.row());\n\t\tif (!item)\n\t\t\tcontinue;\n\n\t\tOBSSource source = obs_sceneitem_get_source(item);\n\n\t\tSourceCopyInfo copyInfo;\n\t\tcopyInfo.weak_source = OBSGetWeakRef(source);\n\t\tobs_sceneitem_get_info2(item, &copyInfo.transform);\n\t\tobs_sceneitem_get_crop(item, &copyInfo.crop);\n\t\tcopyInfo.blend_method = obs_sceneitem_get_blending_method(item);\n\t\tcopyInfo.blend_mode = obs_sceneitem_get_blending_mode(item);\n\t\tcopyInfo.visible = obs_sceneitem_visible(item);\n\t\tcopyInfo.scale_type = obs_sceneitem_get_scale_filter(item);\n\t\tcopyInfo.show_transition_id = obs_source_get_id(obs_sceneitem_get_transition(item, true));\n\t\tcopyInfo.hide_transition_id = obs_source_get_id(obs_sceneitem_get_transition(item, false));\n\n\t\tOBSDataAutoRelease newShowData = obs_data_create();\n\t\tOBSDataAutoRelease oldShowData = obs_source_get_settings(obs_sceneitem_get_transition(item, true));\n\t\tobs_data_apply(newShowData, oldShowData);\n\t\tcopyInfo.show_transition_settings = newShowData.Get();\n\n\t\tOBSDataAutoRelease newHideData = obs_data_create();\n\t\tOBSDataAutoRelease oldHideData = obs_source_get_settings(obs_sceneitem_get_transition(item, false));\n\t\tobs_data_apply(newHideData, oldHideData);\n\t\tcopyInfo.hide_transition_settings = newHideData.Get();\n\n\t\tcopyInfo.show_transition_duration = obs_sceneitem_get_transition_duration(item, true);\n\t\tcopyInfo.hide_transition_duration = obs_sceneitem_get_transition_duration(item, false);\n\n\t\tOBSDataAutoRelease newPrivData = obs_data_create();\n\t\tOBSDataAutoRelease oldPrivData = obs_sceneitem_get_private_settings(item);\n\t\tobs_data_apply(newPrivData, oldPrivData);\n\t\tcopyInfo.private_settings = newPrivData.Get();\n\n\t\tclipboard.push_back(copyInfo);\n\t}\n\n\tUpdateEditMenu();\n}\n\nvoid OBSBasic::on_actionPasteRef_triggered()\n{\n\tOBSSource scene_source = GetCurrentSceneSource();\n\tOBSData undo_data = BackupScene(scene_source);\n\tOBSScene scene = GetCurrentScene();\n\n\tundo_s.push_disabled();\n\n\tfor (size_t i = clipboard.size(); i > 0; i--) {\n\t\tSourceCopyInfo &copyInfo = clipboard[i - 1];\n\n\t\tOBSSource source = OBSGetStrongRef(copyInfo.weak_source);\n\t\tif (!source)\n\t\t\tcontinue;\n\n\t\tconst char *name = obs_source_get_name(source);\n\n\t\t/* do not allow duplicate refs of the same group in the same\n\t\t * scene */\n\t\tif (!!obs_scene_get_group(scene, name)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tOBSBasicSourceSelect::SourcePaste(copyInfo, false);\n\t\tRefreshSources(scene);\n\t}\n\n\tundo_s.pop_disabled();\n\n\tQString action_name = QTStr(\"Undo.PasteSourceRef\");\n\tconst char *scene_name = obs_source_get_name(scene_source);\n\n\tOBSData redo_data = BackupScene(scene_source);\n\tCreateSceneUndoRedoAction(action_name.arg(scene_name), undo_data, redo_data);\n}\n\nvoid OBSBasic::on_actionPasteDup_triggered()\n{\n\tOBSSource scene_source = GetCurrentSceneSource();\n\tOBSData undo_data = BackupScene(scene_source);\n\n\tundo_s.push_disabled();\n\n\tfor (size_t i = clipboard.size(); i > 0; i--) {\n\t\tSourceCopyInfo &copyInfo = clipboard[i - 1];\n\t\tOBSBasicSourceSelect::SourcePaste(copyInfo, true);\n\t\tRefreshSources(GetCurrentScene());\n\t}\n\n\tundo_s.pop_disabled();\n\n\tQString action_name = QTStr(\"Undo.PasteSource\");\n\tconst char *scene_name = obs_source_get_name(scene_source);\n\n\tOBSData redo_data = BackupScene(scene_source);\n\tCreateSceneUndoRedoAction(action_name.arg(scene_name), undo_data, redo_data);\n}\n\nvoid OBSBasic::SourcePasteFilters(OBSSource source, OBSSource dstSource)\n{\n\tif (source == dstSource)\n\t\treturn;\n\n\tOBSDataArrayAutoRelease undo_array = obs_source_backup_filters(dstSource);\n\tobs_source_copy_filters(dstSource, source);\n\tOBSDataArrayAutoRelease redo_array = obs_source_backup_filters(dstSource);\n\n\tconst char *srcName = obs_source_get_name(source);\n\tconst char *dstName = obs_source_get_name(dstSource);\n\tQString text = QTStr(\"Undo.Filters.Paste.Multiple\").arg(srcName, dstName);\n\n\tCreateFilterPasteUndoRedoAction(text, dstSource, undo_array, redo_array);\n}\n\nvoid OBSBasic::actionCopyFilters()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\tobs_source_t *source = action->property(\"source\").value<OBSSource>();\n\n\tif (!source) {\n\t\treturn;\n\t}\n\n\tcopyFiltersSource_ = obs_source_get_weak_source(source);\n\tui->actionPasteFilters->setEnabled(true);\n}\n\nvoid OBSBasic::actionPasteFilters()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\tobs_source_t *dstSource = action->property(\"source\").value<OBSSource>();\n\n\tif (!dstSource) {\n\t\treturn;\n\t}\n\n\tOBSSourceAutoRelease source = obs_weak_source_get_source(copyFiltersSource());\n\n\tSourcePasteFilters(source.Get(), dstSource);\n}\n\nvoid OBSBasic::SceneCopyFilters()\n{\n\tcopyFiltersSource_ = obs_source_get_weak_source(GetCurrentSceneSource());\n\tui->actionPasteFilters->setEnabled(true);\n}\n\nvoid OBSBasic::ScenePasteFilters()\n{\n\tOBSSourceAutoRelease source = obs_weak_source_get_source(copyFiltersSource());\n\n\tOBSSource dstSource = GetCurrentSceneSource();\n\n\tSourcePasteFilters(source.Get(), dstSource);\n}\n\nvoid OBSBasic::on_actionCopyFilters_triggered()\n{\n\tOBSSceneItem item = GetCurrentSceneItem();\n\n\tif (!item)\n\t\treturn;\n\n\tOBSSource source = obs_sceneitem_get_source(item);\n\n\tcopyFiltersSource_ = obs_source_get_weak_source(source);\n\n\tui->actionPasteFilters->setEnabled(true);\n}\n\nvoid OBSBasic::CreateFilterPasteUndoRedoAction(const QString &text, obs_source_t *source, obs_data_array_t *undo_array,\n\t\t\t\t\t       obs_data_array_t *redo_array)\n{\n\tauto undo_redo = [this](const std::string &json) {\n\t\tOBSDataAutoRelease data = obs_data_create_from_json(json.c_str());\n\t\tOBSDataArrayAutoRelease array = obs_data_get_array(data, \"array\");\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(data, \"uuid\"));\n\n\t\tobs_source_restore_filters(source, array);\n\n\t\tif (filters)\n\t\t\tfilters->UpdateSource(source);\n\t};\n\n\tconst char *uuid = obs_source_get_uuid(source);\n\n\tOBSDataAutoRelease undo_data = obs_data_create();\n\tOBSDataAutoRelease redo_data = obs_data_create();\n\tobs_data_set_array(undo_data, \"array\", undo_array);\n\tobs_data_set_array(redo_data, \"array\", redo_array);\n\tobs_data_set_string(undo_data, \"uuid\", uuid);\n\tobs_data_set_string(redo_data, \"uuid\", uuid);\n\n\tundo_s.add_action(text, undo_redo, undo_redo, obs_data_get_json(undo_data), obs_data_get_json(redo_data));\n}\n\nvoid OBSBasic::on_actionPasteFilters_triggered()\n{\n\tOBSSourceAutoRelease source = obs_weak_source_get_source(copyFiltersSource());\n\n\tOBSSceneItem sceneItem = GetCurrentSceneItem();\n\tOBSSource dstSource = obs_sceneitem_get_source(sceneItem);\n\n\tSourcePasteFilters(source.Get(), dstSource);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_ContextToolbar.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <components/ApplicationAudioCaptureToolbar.hpp>\n#include <components/AudioCaptureToolbar.hpp>\n#include <components/BrowserToolbar.hpp>\n#include <components/ColorSourceToolbar.hpp>\n#include <components/DeviceCaptureToolbar.hpp>\n#include <components/DisplayCaptureToolbar.hpp>\n#include <components/GameCaptureToolbar.hpp>\n#include <components/ImageSourceToolbar.hpp>\n#include <components/MediaControls.hpp>\n#include <components/TextSourceToolbar.hpp>\n#include <components/WindowCaptureToolbar.hpp>\n\n#include <qt-wrappers.hpp>\n\nvoid OBSBasic::copyActionsDynamicProperties()\n{\n\t// Themes need the QAction dynamic properties\n\tfor (QAction *x : ui->scenesToolbar->actions()) {\n\t\tQWidget *temp = ui->scenesToolbar->widgetForAction(x);\n\n\t\tif (!temp)\n\t\t\tcontinue;\n\n\t\tfor (QByteArray &y : x->dynamicPropertyNames()) {\n\t\t\ttemp->setProperty(y.constData(), x->property(y.constData()));\n\t\t}\n\t}\n\n\tfor (QAction *x : ui->sourcesToolbar->actions()) {\n\t\tQWidget *temp = ui->sourcesToolbar->widgetForAction(x);\n\n\t\tif (!temp)\n\t\t\tcontinue;\n\n\t\tfor (QByteArray &y : x->dynamicPropertyNames()) {\n\t\t\ttemp->setProperty(y.constData(), x->property(y.constData()));\n\t\t}\n\t}\n}\n\nvoid OBSBasic::ClearContextBar()\n{\n\tQLayoutItem *la = ui->emptySpace->layout()->itemAt(0);\n\tif (la) {\n\t\tdelete la->widget();\n\t\tui->emptySpace->layout()->removeItem(la);\n\t}\n}\n\nvoid OBSBasic::UpdateContextBarVisibility()\n{\n\tint width = ui->centralwidget->size().width();\n\n\tContextBarSize contextBarSizeNew;\n\tif (width >= 740) {\n\t\tcontextBarSizeNew = ContextBarSize_Normal;\n\t} else if (width >= 600) {\n\t\tcontextBarSizeNew = ContextBarSize_Reduced;\n\t} else {\n\t\tcontextBarSizeNew = ContextBarSize_Minimized;\n\t}\n\n\tif (contextBarSize == contextBarSizeNew)\n\t\treturn;\n\n\tcontextBarSize = contextBarSizeNew;\n\tUpdateContextBarDeferred();\n}\n\nstatic bool is_network_media_source(obs_source_t *source, const char *id)\n{\n\tif (strcmp(id, \"ffmpeg_source\") != 0)\n\t\treturn false;\n\n\tOBSDataAutoRelease s = obs_source_get_settings(source);\n\tbool is_local_file = obs_data_get_bool(s, \"is_local_file\");\n\n\treturn !is_local_file;\n}\n\nvoid OBSBasic::UpdateContextBarDeferred(bool force)\n{\n\tQMetaObject::invokeMethod(this, \"UpdateContextBar\", Qt::QueuedConnection, Q_ARG(bool, force));\n}\n\nvoid OBSBasic::SourceToolBarActionsSetEnabled()\n{\n\tbool enable = false;\n\tbool disableProps = false;\n\n\tOBSSceneItem item = GetCurrentSceneItem();\n\n\tif (item) {\n\t\tOBSSource source = obs_sceneitem_get_source(item);\n\t\tdisableProps = !obs_source_configurable(source);\n\n\t\tenable = true;\n\t}\n\n\tif (disableProps)\n\t\tui->actionSourceProperties->setEnabled(false);\n\telse\n\t\tui->actionSourceProperties->setEnabled(enable);\n\n\tui->actionRemoveSource->setEnabled(enable);\n\tui->actionSourceUp->setEnabled(enable);\n\tui->actionSourceDown->setEnabled(enable);\n\n\tRefreshToolBarStyling(ui->sourcesToolbar);\n}\n\nvoid OBSBasic::UpdateContextBar(bool force)\n{\n\tSourceToolBarActionsSetEnabled();\n\n\tif (!ui->contextContainer->isVisible() && !force)\n\t\treturn;\n\n\tOBSSceneItem item = GetCurrentSceneItem();\n\n\tif (item) {\n\t\tobs_source_t *source = obs_sceneitem_get_source(item);\n\n\t\tbool updateNeeded = true;\n\t\tQLayoutItem *la = ui->emptySpace->layout()->itemAt(0);\n\t\tif (la) {\n\t\t\tif (SourceToolbar *toolbar = dynamic_cast<SourceToolbar *>(la->widget())) {\n\t\t\t\tif (toolbar->GetSource() == source)\n\t\t\t\t\tupdateNeeded = false;\n\t\t\t} else if (MediaControls *toolbar = dynamic_cast<MediaControls *>(la->widget())) {\n\t\t\t\tif (toolbar->GetSource() == source)\n\t\t\t\t\tupdateNeeded = false;\n\t\t\t}\n\t\t}\n\n\t\tconst char *id = obs_source_get_unversioned_id(source);\n\t\tuint32_t flags = obs_source_get_output_flags(source);\n\n\t\tui->sourceInteractButton->setVisible(flags & OBS_SOURCE_INTERACTION);\n\n\t\tif (contextBarSize >= ContextBarSize_Reduced && (updateNeeded || force)) {\n\t\t\tClearContextBar();\n\t\t\tif (flags & OBS_SOURCE_CONTROLLABLE_MEDIA) {\n\t\t\t\tif (!is_network_media_source(source, id)) {\n\t\t\t\t\tMediaControls *mediaControls = new MediaControls(ui->emptySpace);\n\t\t\t\t\tmediaControls->SetSource(source);\n\n\t\t\t\t\tui->emptySpace->layout()->addWidget(mediaControls);\n\t\t\t\t}\n\t\t\t} else if (strcmp(id, \"browser_source\") == 0) {\n\t\t\t\tBrowserToolbar *c = new BrowserToolbar(ui->emptySpace, source);\n\t\t\t\tui->emptySpace->layout()->addWidget(c);\n\n\t\t\t} else if (strcmp(id, \"wasapi_input_capture\") == 0 ||\n\t\t\t\t   strcmp(id, \"wasapi_output_capture\") == 0 ||\n\t\t\t\t   strcmp(id, \"coreaudio_input_capture\") == 0 ||\n\t\t\t\t   strcmp(id, \"coreaudio_output_capture\") == 0 ||\n\t\t\t\t   strcmp(id, \"pulse_input_capture\") == 0 || strcmp(id, \"pulse_output_capture\") == 0 ||\n\t\t\t\t   strcmp(id, \"alsa_input_capture\") == 0) {\n\t\t\t\tAudioCaptureToolbar *c = new AudioCaptureToolbar(ui->emptySpace, source);\n\t\t\t\tc->Init();\n\t\t\t\tui->emptySpace->layout()->addWidget(c);\n\n\t\t\t} else if (strcmp(id, \"wasapi_process_output_capture\") == 0) {\n\t\t\t\tApplicationAudioCaptureToolbar *c =\n\t\t\t\t\tnew ApplicationAudioCaptureToolbar(ui->emptySpace, source);\n\t\t\t\tc->Init();\n\t\t\t\tui->emptySpace->layout()->addWidget(c);\n\n\t\t\t} else if (strcmp(id, \"window_capture\") == 0 || strcmp(id, \"xcomposite_input\") == 0) {\n\t\t\t\tWindowCaptureToolbar *c = new WindowCaptureToolbar(ui->emptySpace, source);\n\t\t\t\tc->Init();\n\t\t\t\tui->emptySpace->layout()->addWidget(c);\n\n\t\t\t} else if (strcmp(id, \"monitor_capture\") == 0 || strcmp(id, \"display_capture\") == 0 ||\n\t\t\t\t   strcmp(id, \"xshm_input\") == 0) {\n\t\t\t\tDisplayCaptureToolbar *c = new DisplayCaptureToolbar(ui->emptySpace, source);\n\t\t\t\tc->Init();\n\t\t\t\tui->emptySpace->layout()->addWidget(c);\n\n\t\t\t} else if (strcmp(id, \"dshow_input\") == 0) {\n\t\t\t\tDeviceCaptureToolbar *c = new DeviceCaptureToolbar(ui->emptySpace, source);\n\t\t\t\tui->emptySpace->layout()->addWidget(c);\n\n\t\t\t} else if (strcmp(id, \"game_capture\") == 0) {\n\t\t\t\tGameCaptureToolbar *c = new GameCaptureToolbar(ui->emptySpace, source);\n\t\t\t\tui->emptySpace->layout()->addWidget(c);\n\n\t\t\t} else if (strcmp(id, \"image_source\") == 0) {\n\t\t\t\tImageSourceToolbar *c = new ImageSourceToolbar(ui->emptySpace, source);\n\t\t\t\tui->emptySpace->layout()->addWidget(c);\n\n\t\t\t} else if (strcmp(id, \"color_source\") == 0) {\n\t\t\t\tColorSourceToolbar *c = new ColorSourceToolbar(ui->emptySpace, source);\n\t\t\t\tui->emptySpace->layout()->addWidget(c);\n\n\t\t\t} else if (strcmp(id, \"text_ft2_source\") == 0 || strcmp(id, \"text_gdiplus\") == 0) {\n\t\t\t\tTextSourceToolbar *c = new TextSourceToolbar(ui->emptySpace, source);\n\t\t\t\tui->emptySpace->layout()->addWidget(c);\n\t\t\t}\n\t\t} else if (contextBarSize == ContextBarSize_Minimized) {\n\t\t\tClearContextBar();\n\t\t}\n\n\t\tQIcon icon;\n\n\t\tif (strcmp(id, \"scene\") == 0)\n\t\t\ticon = GetSceneIcon();\n\t\telse if (strcmp(id, \"group\") == 0)\n\t\t\ticon = GetGroupIcon();\n\t\telse\n\t\t\ticon = GetSourceIcon(id);\n\n\t\tQPixmap pixmap = icon.pixmap(QSize(16, 16));\n\t\tui->contextSourceIcon->setPixmap(pixmap);\n\t\tui->contextSourceIconSpacer->hide();\n\t\tui->contextSourceIcon->show();\n\n\t\tconst char *name = obs_source_get_name(source);\n\t\tui->contextSourceLabel->setText(name);\n\n\t\tui->sourceFiltersButton->setEnabled(true);\n\t\tui->sourcePropertiesButton->setEnabled(obs_source_configurable(source));\n\t} else {\n\t\tClearContextBar();\n\t\tui->contextSourceIcon->hide();\n\t\tui->contextSourceIconSpacer->show();\n\t\tui->contextSourceLabel->setText(QTStr(\"ContextBar.NoSelectedSource\"));\n\n\t\tui->sourceFiltersButton->setEnabled(false);\n\t\tui->sourcePropertiesButton->setEnabled(false);\n\t\tui->sourceInteractButton->setVisible(false);\n\t}\n\n\tif (contextBarSize == ContextBarSize_Normal) {\n\t\tui->sourcePropertiesButton->setText(QTStr(\"Properties\"));\n\t\tui->sourceFiltersButton->setText(QTStr(\"Filters\"));\n\t\tui->sourceInteractButton->setText(QTStr(\"Interact\"));\n\t} else {\n\t\tui->sourcePropertiesButton->setText(\"\");\n\t\tui->sourceFiltersButton->setText(\"\");\n\t\tui->sourceInteractButton->setText(\"\");\n\t}\n}\n\nvoid OBSBasic::ShowContextBar()\n{\n\ton_toggleContextBar_toggled(true);\n\tui->toggleContextBar->setChecked(true);\n}\n\nvoid OBSBasic::HideContextBar()\n{\n\ton_toggleContextBar_toggled(false);\n\tui->toggleContextBar->setChecked(false);\n}\n\nvoid OBSBasic::on_toggleContextBar_toggled(bool visible)\n{\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowContextToolbars\", visible);\n\tthis->ui->contextContainer->setVisible(visible);\n\tUpdateContextBar(true);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Docks.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <qt-wrappers.hpp>\n\nvoid setupDockAction(QDockWidget *dock)\n{\n\tQAction *action = dock->toggleViewAction();\n\n\tauto neverDisable = [action]() {\n\t\tQSignalBlocker block(action);\n\t\taction->setEnabled(true);\n\t};\n\n\tauto newToggleView = [dock](bool check) {\n\t\tQSignalBlocker block(dock);\n\t\tdock->setVisible(check);\n\t};\n\n\t// Replace the slot connected by default\n\tQObject::disconnect(action, &QAction::triggered, nullptr, 0);\n\tQObject::connect(action, &QAction::triggered, dock, newToggleView);\n\n\t// Make the action unable to be disabled\n\tQObject::connect(action, &QAction::enabledChanged, action, neverDisable);\n}\n\nvoid OBSBasic::on_resetDocks_triggered(bool force)\n{\n#ifdef BROWSER_AVAILABLE\n\tif ((extraDocks.size() || extraCustomDocks.size() || extraBrowserDocks.size()) && !force)\n#else\n\tif ((extraDocks.size() || extraCustomDocks.size()) && !force)\n#endif\n\t{\n\t\tQMessageBox::StandardButton button =\n\t\t\tOBSMessageBox::question(this, QTStr(\"ResetUIWarning.Title\"), QTStr(\"ResetUIWarning.Text\"));\n\n\t\tif (button == QMessageBox::No)\n\t\t\treturn;\n\t}\n\n#define RESET_DOCKLIST(dockList)                                                                               \\\n\tfor (int i = dockList.size() - 1; i >= 0; i--) {                                                       \\\n\t\tdockList[i]->setVisible(true);                                                                 \\\n\t\tdockList[i]->setFloating(true);                                                                \\\n\t\tdockList[i]->move(frameGeometry().topLeft() + rect().center() - dockList[i]->rect().center()); \\\n\t\tdockList[i]->setVisible(false);                                                                \\\n\t}\n\n\tRESET_DOCKLIST(extraDocks)\n\tRESET_DOCKLIST(extraCustomDocks)\n#ifdef BROWSER_AVAILABLE\n\tRESET_DOCKLIST(extraBrowserDocks)\n#endif\n#undef RESET_DOCKLIST\n\n\trestoreState(startingDockLayout);\n\tui->sideDocks->setChecked(true);\n\n\tint cx = width();\n\tint bottomDocksHeight = height();\n\n\tbottomDocksHeight = bottomDocksHeight * 225 / 1000;\n\n\tui->scenesDock->setVisible(true);\n\tui->sourcesDock->setVisible(true);\n\tui->mixerDock->setVisible(true);\n\tui->transitionsDock->setVisible(true);\n\tcontrolsDock->setVisible(true);\n\tstatsDock->setVisible(false);\n\tstatsDock->setFloating(true);\n\n\tQList<QDockWidget *> bottomDocks{ui->mixerDock, ui->transitionsDock, controlsDock};\n\n\tresizeDocks(bottomDocks, {bottomDocksHeight, bottomDocksHeight, bottomDocksHeight}, Qt::Vertical);\n\tresizeDocks(bottomDocks, {cx * 45 / 100, cx * 14 / 100, cx * 16 / 100}, Qt::Horizontal);\n\n\tint sideDockWidth = std::min(width() * 30 / 100, 280);\n\tresizeDocks({ui->scenesDock, ui->sourcesDock}, {sideDockWidth, sideDockWidth}, Qt::Horizontal);\n\n\tactivateWindow();\n}\n\nvoid OBSBasic::on_lockDocks_toggled(bool lock)\n{\n\tQDockWidget::DockWidgetFeatures features =\n\t\tlock ? QDockWidget::NoDockWidgetFeatures\n\t\t     : (QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable |\n\t\t\tQDockWidget::DockWidgetFloatable);\n\n\tQDockWidget::DockWidgetFeatures mainFeatures = features;\n\tmainFeatures &= ~QDockWidget::QDockWidget::DockWidgetClosable;\n\n\tui->scenesDock->setFeatures(mainFeatures);\n\tui->sourcesDock->setFeatures(mainFeatures);\n\tui->mixerDock->setFeatures(mainFeatures);\n\tui->transitionsDock->setFeatures(mainFeatures);\n\tcontrolsDock->setFeatures(mainFeatures);\n\tstatsDock->setFeatures(features);\n\n\tfor (int i = extraDocks.size() - 1; i >= 0; i--)\n\t\textraDocks[i]->setFeatures(features);\n\n\tfor (int i = extraCustomDocks.size() - 1; i >= 0; i--)\n\t\textraCustomDocks[i]->setFeatures(features);\n\n#ifdef BROWSER_AVAILABLE\n\tfor (int i = extraBrowserDocks.size() - 1; i >= 0; i--)\n\t\textraBrowserDocks[i]->setFeatures(features);\n#endif\n}\n\nvoid OBSBasic::on_sideDocks_toggled(bool side)\n{\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"SideDocks\", side);\n\n\tsetDockCornersVertical(side);\n}\n\nvoid OBSBasic::AddDockWidget(QDockWidget *dock, Qt::DockWidgetArea area, bool extraBrowser)\n{\n\tif (dock->objectName().isEmpty())\n\t\treturn;\n\n\tbool lock = ui->lockDocks->isChecked();\n\tQDockWidget::DockWidgetFeatures features =\n\t\tlock ? QDockWidget::NoDockWidgetFeatures\n\t\t     : (QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable |\n\t\t\tQDockWidget::DockWidgetFloatable);\n\n\tsetupDockAction(dock);\n\tdock->setFeatures(features);\n\taddDockWidget(area, dock);\n\n#ifdef BROWSER_AVAILABLE\n\tif (extraBrowser && extraBrowserMenuDocksSeparator.isNull())\n\t\textraBrowserMenuDocksSeparator = ui->menuDocks->addSeparator();\n\n\tif (!extraBrowser && !extraBrowserMenuDocksSeparator.isNull())\n\t\tui->menuDocks->insertAction(extraBrowserMenuDocksSeparator, dock->toggleViewAction());\n\telse\n\t\tui->menuDocks->addAction(dock->toggleViewAction());\n\n\tif (extraBrowser)\n\t\treturn;\n#else\n\tUNUSED_PARAMETER(extraBrowser);\n\n\tui->menuDocks->addAction(dock->toggleViewAction());\n#endif\n\n\textraDockNames.push_back(dock->objectName());\n\textraDocks.push_back(std::shared_ptr<QDockWidget>(dock));\n}\n\nvoid OBSBasic::RemoveDockWidget(const QString &name)\n{\n\tif (extraDockNames.contains(name)) {\n\t\tint idx = extraDockNames.indexOf(name);\n\t\textraDockNames.removeAt(idx);\n\t\textraDocks[idx].reset();\n\t\textraDocks.removeAt(idx);\n\t} else if (extraCustomDockNames.contains(name)) {\n\t\tint idx = extraCustomDockNames.indexOf(name);\n\t\textraCustomDockNames.removeAt(idx);\n\t\tremoveDockWidget(extraCustomDocks[idx]);\n\t\textraCustomDocks.removeAt(idx);\n\t}\n}\n\nbool OBSBasic::IsDockObjectNameUsed(const QString &name)\n{\n\tQStringList list;\n\tlist << \"scenesDock\"\n\t     << \"sourcesDock\"\n\t     << \"mixerDock\"\n\t     << \"transitionsDock\"\n\t     << \"controlsDock\"\n\t     << \"statsDock\";\n\tlist << extraDockNames;\n\tlist << extraCustomDockNames;\n\n\treturn list.contains(name);\n}\n\nvoid OBSBasic::AddCustomDockWidget(QDockWidget *dock)\n{\n\t// Prevent the object name from being changed\n\tconnect(dock, &QObject::objectNameChanged, this, &OBSBasic::RepairCustomExtraDockName);\n\n\tbool lock = ui->lockDocks->isChecked();\n\tQDockWidget::DockWidgetFeatures features =\n\t\tlock ? QDockWidget::NoDockWidgetFeatures\n\t\t     : (QDockWidget::DockWidgetClosable | QDockWidget::DockWidgetMovable |\n\t\t\tQDockWidget::DockWidgetFloatable);\n\n\tdock->setFeatures(features);\n\taddDockWidget(Qt::RightDockWidgetArea, dock);\n\n\textraCustomDockNames.push_back(dock->objectName());\n\textraCustomDocks.push_back(dock);\n}\n\nvoid OBSBasic::setDockCornersVertical(bool vertical)\n{\n\tif (vertical) {\n\t\tsetCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);\n\t\tsetCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea);\n\t\tsetCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);\n\t\tsetCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);\n\t} else {\n\t\tsetCorner(Qt::TopLeftCorner, Qt::TopDockWidgetArea);\n\t\tsetCorner(Qt::TopRightCorner, Qt::TopDockWidgetArea);\n\t\tsetCorner(Qt::BottomLeftCorner, Qt::BottomDockWidgetArea);\n\t\tsetCorner(Qt::BottomRightCorner, Qt::BottomDockWidgetArea);\n\t}\n}\n\nvoid OBSBasic::RepairCustomExtraDockName()\n{\n\tQDockWidget *dock = reinterpret_cast<QDockWidget *>(sender());\n\tint idx = extraCustomDocks.indexOf(dock);\n\tQSignalBlocker block(dock);\n\n\tif (idx == -1) {\n\t\tblog(LOG_WARNING, \"A custom dock got its object name changed\");\n\t\treturn;\n\t}\n\n\tblog(LOG_WARNING, \"The custom dock '%s' got its object name restored\", QT_TO_UTF8(extraCustomDockNames[idx]));\n\n\tdock->setObjectName(extraCustomDockNames[idx]);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Dropfiles.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <qt-wrappers.hpp>\n\n#include <QFileInfo>\n#include <QMimeData>\n#ifdef _WIN32\n#include <QSettings>\n#endif\n#include <QUrlQuery>\n\nusing namespace std;\n\nstatic const char *textExtensions[] = {\"txt\", \"log\", nullptr};\n\nstatic const char *imageExtensions[] = {\"bmp\", \"gif\", \"jpeg\", \"jpg\",\n#ifdef _WIN32\n\t\t\t\t\t\"jxr\",\n#endif\n\t\t\t\t\t\"png\", \"tga\", \"webp\", nullptr};\n\nstatic const char *htmlExtensions[] = {\"htm\", \"html\", nullptr};\n\nstatic const char *mediaExtensions[] = {\n\t\"3ga\", \"669\",  \"a52\", \"aac\",  \"ac3\",   \"adt\",   \"adts\",  \"aif\",  \"aifc\", \"aiff\", \"amb\",  \"amr\",  \"aob\", \"ape\",\n\t\"au\",  \"awb\",  \"caf\", \"dts\",  \"flac\",  \"it\",    \"kar\",   \"m4a\",  \"m4b\",  \"m4p\",  \"m5p\",  \"mid\",  \"mka\", \"mlp\",\n\t\"mod\", \"mpa\",  \"mp1\", \"mp2\",  \"mp3\",   \"mpc\",   \"mpga\",  \"mus\",  \"oga\",  \"ogg\",  \"oma\",  \"opus\", \"qcp\", \"ra\",\n\t\"rmi\", \"s3m\",  \"sid\", \"spx\",  \"tak\",   \"thd\",   \"tta\",   \"voc\",  \"vqf\",  \"w64\",  \"wav\",  \"wma\",  \"wv\",  \"xa\",\n\t\"xm\",  \"3g2\",  \"3gp\", \"3gp2\", \"3gpp\",  \"amv\",   \"asf\",   \"avi\",  \"bik\",  \"crf\",  \"divx\", \"drc\",  \"dv\",  \"evo\",\n\t\"f4v\", \"flv\",  \"gvi\", \"gxf\",  \"iso\",   \"m1v\",   \"m2v\",   \"m2t\",  \"m2ts\", \"m4v\",  \"mkv\",  \"mov\",  \"mp2\", \"mp2v\",\n\t\"mp4\", \"mp4v\", \"mpe\", \"mpeg\", \"mpeg1\", \"mpeg2\", \"mpeg4\", \"mpg\",  \"mpv2\", \"mts\",  \"mtv\",  \"mxf\",  \"mxg\", \"nsv\",\n\t\"nuv\", \"ogg\",  \"ogm\", \"ogv\",  \"ogx\",   \"ps\",    \"rec\",   \"rm\",   \"rmvb\", \"rpl\",  \"thp\",  \"tod\",  \"ts\",  \"tts\",\n\t\"txd\", \"vob\",  \"vro\", \"webm\", \"wm\",    \"wmv\",   \"wtv\",   nullptr};\n\nstatic string GenerateSourceName(const char *base)\n{\n\tstring name;\n\tint inc = 0;\n\n\tfor (;; inc++) {\n\t\tname = base;\n\n\t\tif (inc) {\n\t\t\tname += \" (\";\n\t\t\tname += to_string(inc + 1);\n\t\t\tname += \")\";\n\t\t}\n\n\t\tOBSSourceAutoRelease source = obs_get_source_by_name(name.c_str());\n\n\t\tif (!source)\n\t\t\treturn name;\n\t}\n}\n\n#ifdef _WIN32\nstatic QString ReadWindowsURLFile(const QString &file)\n{\n\tQSettings iniFile(file, QSettings::IniFormat);\n\tQVariant url = iniFile.value(\"InternetShortcut/URL\");\n\treturn url.toString();\n}\n#endif\n\nvoid OBSBasic::AddDropURL(QUrl url, QString &name, obs_data_t *settings, const obs_video_info &ovi)\n{\n\tQUrlQuery query = QUrlQuery(url.query(QUrl::FullyEncoded));\n\n\tint cx = (int)ovi.base_width;\n\tint cy = (int)ovi.base_height;\n\n\tif (query.hasQueryItem(\"layer-width\"))\n\t\tcx = query.queryItemValue(\"layer-width\").toInt();\n\tif (query.hasQueryItem(\"layer-height\"))\n\t\tcy = query.queryItemValue(\"layer-height\").toInt();\n\tif (query.hasQueryItem(\"layer-css\")) {\n\t\t// QUrl::FullyDecoded does NOT properly decode a\n\t\t// application/x-www-form-urlencoded space represented as '+'\n\t\t// Thus, this is manually filtered out before QUrl's\n\t\t// decoding kicks in again. This is to allow JavaScript's\n\t\t// default searchParams.append function to simply append css\n\t\t// to the query parameters, which is the intended usecase for this.\n\t\tQString fullyEncoded = query.queryItemValue(\"layer-css\", QUrl::FullyEncoded);\n\t\tfullyEncoded = fullyEncoded.replace(\"+\", \"%20\");\n\t\tQString decoded = QUrl::fromPercentEncoding(QByteArray::fromStdString(QT_TO_UTF8(fullyEncoded)));\n\t\tobs_data_set_string(settings, \"css\", QT_TO_UTF8(decoded));\n\t}\n\n\tobs_data_set_int(settings, \"width\", cx);\n\tobs_data_set_int(settings, \"height\", cy);\n\n\tname = query.hasQueryItem(\"layer-name\") ? query.queryItemValue(\"layer-name\", QUrl::FullyDecoded) : url.host();\n\n\tquery.removeQueryItem(\"layer-width\");\n\tquery.removeQueryItem(\"layer-height\");\n\tquery.removeQueryItem(\"layer-name\");\n\tquery.removeQueryItem(\"layer-css\");\n\turl.setQuery(query);\n\n\tobs_data_set_string(settings, \"url\", QT_TO_UTF8(url.url()));\n}\n\nvoid OBSBasic::AddDropSource(const char *data, DropType image)\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tOBSDataAutoRelease settings = obs_data_create();\n\tconst char *type = nullptr;\n\tQString name;\n\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tswitch (image) {\n\tcase DropType_RawText:\n\t\tobs_data_set_string(settings, \"text\", data);\n#ifdef _WIN32\n\t\ttype = \"text_gdiplus\";\n#else\n\t\ttype = \"text_ft2_source\";\n#endif\n\t\tbreak;\n\tcase DropType_Text:\n#ifdef _WIN32\n\t\tobs_data_set_bool(settings, \"read_from_file\", true);\n\t\tobs_data_set_string(settings, \"file\", data);\n\t\tname = QUrl::fromLocalFile(QString(data)).fileName();\n\t\ttype = \"text_gdiplus\";\n#else\n\t\tobs_data_set_bool(settings, \"from_file\", true);\n\t\tobs_data_set_string(settings, \"text_file\", data);\n\t\ttype = \"text_ft2_source\";\n#endif\n\t\tbreak;\n\tcase DropType_Image:\n\t\tobs_data_set_string(settings, \"file\", data);\n\t\tname = QUrl::fromLocalFile(QString(data)).fileName();\n\t\ttype = \"image_source\";\n\t\tbreak;\n\tcase DropType_Media:\n\t\tobs_data_set_string(settings, \"local_file\", data);\n\t\tname = QUrl::fromLocalFile(QString(data)).fileName();\n\t\ttype = \"ffmpeg_source\";\n\t\tbreak;\n\tcase DropType_Html:\n\t\tobs_data_set_bool(settings, \"is_local_file\", true);\n\t\tobs_data_set_string(settings, \"local_file\", data);\n\t\tobs_data_set_int(settings, \"width\", ovi.base_width);\n\t\tobs_data_set_int(settings, \"height\", ovi.base_height);\n\t\tname = QUrl::fromLocalFile(QString(data)).fileName();\n\t\ttype = \"browser_source\";\n\t\tbreak;\n\tcase DropType_Url:\n\t\tAddDropURL(QUrl(data), name, settings, ovi);\n\t\ttype = \"browser_source\";\n\t\tbreak;\n\t}\n\n\ttype = obs_get_latest_input_type_id(type);\n\n\tif (type == nullptr || !obs_source_get_display_name(type)) {\n\t\treturn;\n\t}\n\n\tif (name.isEmpty())\n\t\tname = obs_source_get_display_name(type);\n\tstd::string sourceName = GenerateSourceName(QT_TO_UTF8(name));\n\tOBSSourceAutoRelease source = obs_source_create(type, sourceName.c_str(), settings, nullptr);\n\tif (source) {\n\t\tOBSDataAutoRelease wrapper = obs_save_source(source);\n\n\t\tOBSScene scene = main->GetCurrentScene();\n\t\tstd::string sceneUUID = obs_source_get_uuid(obs_scene_get_source(scene));\n\t\tstd::string sourceUUID = obs_source_get_uuid(source);\n\n\t\tauto undo = [sceneUUID, sourceUUID](const std::string &) {\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(sourceUUID.c_str());\n\t\t\tobs_source_remove(source);\n\t\t\tOBSSourceAutoRelease scene = obs_get_source_by_uuid(sceneUUID.c_str());\n\t\t\tOBSBasic::Get()->SetCurrentScene(scene.Get(), true);\n\t\t};\n\t\tauto redo = [sceneUUID, sourceName, type](const std::string &data) {\n\t\t\tOBSSourceAutoRelease scene = obs_get_source_by_uuid(sceneUUID.c_str());\n\t\t\tOBSBasic::Get()->SetCurrentScene(scene.Get(), true);\n\n\t\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\t\tOBSSourceAutoRelease source = obs_load_source(dat);\n\t\t\tobs_scene_add(obs_scene_from_source(scene), source.Get());\n\t\t};\n\n\t\tundo_s.add_action(QTStr(\"Undo.Add\").arg(sourceName.c_str()), undo, redo, \"\",\n\t\t\t\t  std::string(obs_data_get_json(wrapper)));\n\t\tobs_scene_add(scene, source);\n\t}\n}\n\nvoid OBSBasic::dragEnterEvent(QDragEnterEvent *event)\n{\n\t// refuse drops of our own widgets\n\tif (event->source() != nullptr) {\n\t\tevent->setDropAction(Qt::IgnoreAction);\n\t\treturn;\n\t}\n\n\tevent->acceptProposedAction();\n}\n\nvoid OBSBasic::dragLeaveEvent(QDragLeaveEvent *event)\n{\n\tevent->accept();\n}\n\nvoid OBSBasic::dragMoveEvent(QDragMoveEvent *event)\n{\n\tevent->acceptProposedAction();\n}\n\nvoid OBSBasic::ConfirmDropUrl(const QString &url)\n{\n\tif (url.left(7).compare(\"http://\", Qt::CaseInsensitive) == 0 ||\n\t    url.left(8).compare(\"https://\", Qt::CaseInsensitive) == 0) {\n\n\t\tactivateWindow();\n\n\t\tQString msg = QTStr(\"AddUrl.Text\");\n\t\tmsg += \"\\n\\n\";\n\t\tmsg += QTStr(\"AddUrl.Text.Url\").arg(url);\n\n\t\tQMessageBox messageBox(this);\n\t\tmessageBox.setWindowTitle(QTStr(\"AddUrl.Title\"));\n\t\tmessageBox.setText(msg);\n\n\t\tQPushButton *yesButton = messageBox.addButton(QTStr(\"Yes\"), QMessageBox::YesRole);\n\t\tQPushButton *noButton = messageBox.addButton(QTStr(\"No\"), QMessageBox::NoRole);\n\t\tmessageBox.setDefaultButton(yesButton);\n\t\tmessageBox.setEscapeButton(noButton);\n\t\tmessageBox.setIcon(QMessageBox::Question);\n\t\tmessageBox.exec();\n\n\t\tif (messageBox.clickedButton() == yesButton)\n\t\t\tAddDropSource(QT_TO_UTF8(url), DropType_Url);\n\t}\n}\n\nvoid OBSBasic::dropEvent(QDropEvent *event)\n{\n\tconst QMimeData *mimeData = event->mimeData();\n\n\tif (mimeData->hasUrls()) {\n\t\tQList<QUrl> urls = mimeData->urls();\n\n\t\tfor (int i = 0; i < urls.size(); i++) {\n\t\t\tQUrl url = urls[i];\n\t\t\tQString file = url.toLocalFile();\n\t\t\tQFileInfo fileInfo(file);\n\n\t\t\tif (!fileInfo.exists()) {\n\t\t\t\tConfirmDropUrl(url.url());\n\t\t\t\tcontinue;\n\t\t\t}\n\n#ifdef _WIN32\n\t\t\tif (fileInfo.suffix().compare(\"url\", Qt::CaseInsensitive) == 0) {\n\t\t\t\tQString urlTarget = ReadWindowsURLFile(file);\n\t\t\t\tif (!urlTarget.isEmpty()) {\n\t\t\t\t\tConfirmDropUrl(urlTarget);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t} else if (fileInfo.isShortcut()) {\n\t\t\t\tfile = fileInfo.symLinkTarget();\n\t\t\t\tfileInfo = QFileInfo(file);\n\t\t\t\tif (!fileInfo.exists()) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\tQString suffixQStr = fileInfo.suffix();\n\t\t\tQByteArray suffixArray = suffixQStr.toUtf8();\n\t\t\tconst char *suffix = suffixArray.constData();\n\t\t\tbool found = false;\n\n\t\t\tconst char **cmp;\n\n#define CHECK_SUFFIX(extensions, type)                         \\\n\tcmp = extensions;                                      \\\n\twhile (*cmp) {                                         \\\n\t\tif (astrcmpi(*cmp, suffix) == 0) {             \\\n\t\t\tAddDropSource(QT_TO_UTF8(file), type); \\\n\t\t\tfound = true;                          \\\n\t\t\tbreak;                                 \\\n\t\t}                                              \\\n                                                               \\\n\t\tcmp++;                                         \\\n\t}                                                      \\\n                                                               \\\n\tif (found)                                             \\\n\t\tcontinue;\n\n\t\t\tCHECK_SUFFIX(textExtensions, DropType_Text);\n\t\t\tCHECK_SUFFIX(htmlExtensions, DropType_Html);\n\t\t\tCHECK_SUFFIX(imageExtensions, DropType_Image);\n\t\t\tCHECK_SUFFIX(mediaExtensions, DropType_Media);\n\n#undef CHECK_SUFFIX\n\t\t}\n\t} else if (mimeData->hasText()) {\n\t\tAddDropSource(QT_TO_UTF8(mimeData->text()), DropType_RawText);\n\t}\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Hotkeys.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <widgets/OBSBasicStats.hpp>\n\nvoid OBSBasic::InitHotkeys()\n{\n\tProfileScope(\"OBSBasic::InitHotkeys\");\n\n\tstruct obs_hotkeys_translations t = {};\n\tt.insert = Str(\"Hotkeys.Insert\");\n\tt.del = Str(\"Hotkeys.Delete\");\n\tt.home = Str(\"Hotkeys.Home\");\n\tt.end = Str(\"Hotkeys.End\");\n\tt.page_up = Str(\"Hotkeys.PageUp\");\n\tt.page_down = Str(\"Hotkeys.PageDown\");\n\tt.num_lock = Str(\"Hotkeys.NumLock\");\n\tt.scroll_lock = Str(\"Hotkeys.ScrollLock\");\n\tt.caps_lock = Str(\"Hotkeys.CapsLock\");\n\tt.backspace = Str(\"Hotkeys.Backspace\");\n\tt.tab = Str(\"Hotkeys.Tab\");\n\tt.print = Str(\"Hotkeys.Print\");\n\tt.pause = Str(\"Hotkeys.Pause\");\n\tt.left = Str(\"Hotkeys.Left\");\n\tt.right = Str(\"Hotkeys.Right\");\n\tt.up = Str(\"Hotkeys.Up\");\n\tt.down = Str(\"Hotkeys.Down\");\n#ifdef _WIN32\n\tt.meta = Str(\"Hotkeys.Windows\");\n#else\n\tt.meta = Str(\"Hotkeys.Super\");\n#endif\n\tt.menu = Str(\"Hotkeys.Menu\");\n\tt.space = Str(\"Hotkeys.Space\");\n\tt.numpad_num = Str(\"Hotkeys.NumpadNum\");\n\tt.numpad_multiply = Str(\"Hotkeys.NumpadMultiply\");\n\tt.numpad_divide = Str(\"Hotkeys.NumpadDivide\");\n\tt.numpad_plus = Str(\"Hotkeys.NumpadAdd\");\n\tt.numpad_minus = Str(\"Hotkeys.NumpadSubtract\");\n\tt.numpad_decimal = Str(\"Hotkeys.NumpadDecimal\");\n\tt.apple_keypad_num = Str(\"Hotkeys.AppleKeypadNum\");\n\tt.apple_keypad_multiply = Str(\"Hotkeys.AppleKeypadMultiply\");\n\tt.apple_keypad_divide = Str(\"Hotkeys.AppleKeypadDivide\");\n\tt.apple_keypad_plus = Str(\"Hotkeys.AppleKeypadAdd\");\n\tt.apple_keypad_minus = Str(\"Hotkeys.AppleKeypadSubtract\");\n\tt.apple_keypad_decimal = Str(\"Hotkeys.AppleKeypadDecimal\");\n\tt.apple_keypad_equal = Str(\"Hotkeys.AppleKeypadEqual\");\n\tt.mouse_num = Str(\"Hotkeys.MouseButton\");\n\tt.escape = Str(\"Hotkeys.Escape\");\n\tobs_hotkeys_set_translations(&t);\n\n\tobs_hotkeys_set_audio_hotkeys_translations(Str(\"Mute\"), Str(\"Unmute\"), Str(\"Push-to-mute\"),\n\t\t\t\t\t\t   Str(\"Push-to-talk\"));\n\n\tobs_hotkeys_set_sceneitem_hotkeys_translations(Str(\"SceneItemShow\"), Str(\"SceneItemHide\"));\n\n\tobs_hotkey_enable_callback_rerouting(true);\n\tobs_hotkey_set_callback_routing_func(OBSBasic::HotkeyTriggered, this);\n}\n\nvoid OBSBasic::ProcessHotkey(obs_hotkey_id id, bool pressed)\n{\n\tobs_hotkey_trigger_routed_callback(id, pressed);\n}\n\nvoid OBSBasic::HotkeyTriggered(void *data, obs_hotkey_id id, bool pressed)\n{\n\tOBSBasic &basic = *static_cast<OBSBasic *>(data);\n\tQMetaObject::invokeMethod(&basic, \"ProcessHotkey\", Q_ARG(obs_hotkey_id, id), Q_ARG(bool, pressed));\n}\n\nvoid OBSBasic::CreateHotkeys()\n{\n\tProfileScope(\"OBSBasic::CreateHotkeys\");\n\n\tauto LoadHotkeyData = [&](const char *name) -> OBSData {\n\t\tconst char *info = config_get_string(activeConfiguration, \"Hotkeys\", name);\n\t\tif (!info)\n\t\t\treturn {};\n\n\t\tOBSDataAutoRelease data = obs_data_create_from_json(info);\n\t\tif (!data)\n\t\t\treturn {};\n\n\t\treturn data.Get();\n\t};\n\n\tauto LoadHotkey = [&](obs_hotkey_id id, const char *name) {\n\t\tOBSDataArrayAutoRelease array = obs_data_get_array(LoadHotkeyData(name), \"bindings\");\n\n\t\tobs_hotkey_load(id, array);\n\t};\n\n\tauto LoadHotkeyPair = [&](obs_hotkey_pair_id id, const char *name0, const char *name1,\n\t\t\t\t  const char *oldName = NULL) {\n\t\tif (oldName) {\n\t\t\tconst auto info = config_get_string(activeConfiguration, \"Hotkeys\", oldName);\n\t\t\tif (info) {\n\t\t\t\tconfig_set_string(activeConfiguration, \"Hotkeys\", name0, info);\n\t\t\t\tconfig_set_string(activeConfiguration, \"Hotkeys\", name1, info);\n\t\t\t\tconfig_remove_value(activeConfiguration, \"Hotkeys\", oldName);\n\t\t\t\tactiveConfiguration.Save();\n\t\t\t}\n\t\t}\n\t\tOBSDataArrayAutoRelease array0 = obs_data_get_array(LoadHotkeyData(name0), \"bindings\");\n\t\tOBSDataArrayAutoRelease array1 = obs_data_get_array(LoadHotkeyData(name1), \"bindings\");\n\n\t\tobs_hotkey_pair_load(id, array0, array1);\n\t};\n\n#define MAKE_CALLBACK(pred, method, log_action)                            \\\n\t[](void *data, obs_hotkey_pair_id, obs_hotkey_t *, bool pressed) { \\\n\t\tOBSBasic &basic = *static_cast<OBSBasic *>(data);          \\\n\t\tif ((pred) && pressed) {                                   \\\n\t\t\tblog(LOG_INFO, log_action \" due to hotkey\");       \\\n\t\t\tmethod();                                          \\\n\t\t\treturn true;                                       \\\n\t\t}                                                          \\\n\t\treturn false;                                              \\\n\t}\n\n\tstreamingHotkeys = obs_hotkey_pair_register_frontend(\n\t\t\"OBSBasic.StartStreaming\", Str(\"Basic.Main.StartStreaming\"), \"OBSBasic.StopStreaming\",\n\t\tStr(\"Basic.Main.StopStreaming\"),\n\t\tMAKE_CALLBACK(!basic.outputHandler->StreamingActive() && !basic.streamingStarting, basic.StartStreaming,\n\t\t\t      \"Starting stream\"),\n\t\tMAKE_CALLBACK(basic.outputHandler->StreamingActive() && !basic.streamingStarting, basic.StopStreaming,\n\t\t\t      \"Stopping stream\"),\n\t\tthis, this);\n\tLoadHotkeyPair(streamingHotkeys, \"OBSBasic.StartStreaming\", \"OBSBasic.StopStreaming\");\n\n\tauto cb = [](void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed) {\n\t\tOBSBasic &basic = *static_cast<OBSBasic *>(data);\n\t\tif (basic.outputHandler->StreamingActive() && pressed) {\n\t\t\tbasic.ForceStopStreaming();\n\t\t}\n\t};\n\n\tforceStreamingStopHotkey = obs_hotkey_register_frontend(\"OBSBasic.ForceStopStreaming\",\n\t\t\t\t\t\t\t\tStr(\"Basic.Main.ForceStopStreaming\"), cb, this);\n\tLoadHotkey(forceStreamingStopHotkey, \"OBSBasic.ForceStopStreaming\");\n\n\trecordingHotkeys = obs_hotkey_pair_register_frontend(\n\t\t\"OBSBasic.StartRecording\", Str(\"Basic.Main.StartRecording\"), \"OBSBasic.StopRecording\",\n\t\tStr(\"Basic.Main.StopRecording\"),\n\t\tMAKE_CALLBACK(!basic.outputHandler->RecordingActive() && !basic.recordingStarted, basic.StartRecording,\n\t\t\t      \"Starting recording\"),\n\t\tMAKE_CALLBACK(basic.outputHandler->RecordingActive() && basic.recordingStarted, basic.StopRecording,\n\t\t\t      \"Stopping recording\"),\n\t\tthis, this);\n\tLoadHotkeyPair(recordingHotkeys, \"OBSBasic.StartRecording\", \"OBSBasic.StopRecording\");\n\n\tpauseHotkeys =\n\t\tobs_hotkey_pair_register_frontend(\"OBSBasic.PauseRecording\", Str(\"Basic.Main.PauseRecording\"),\n\t\t\t\t\t\t  \"OBSBasic.UnpauseRecording\", Str(\"Basic.Main.UnpauseRecording\"),\n\t\t\t\t\t\t  MAKE_CALLBACK(basic.isRecordingPausable && !basic.recordingPaused,\n\t\t\t\t\t\t\t\tbasic.PauseRecording, \"Pausing recording\"),\n\t\t\t\t\t\t  MAKE_CALLBACK(basic.isRecordingPausable && basic.recordingPaused,\n\t\t\t\t\t\t\t\tbasic.UnpauseRecording, \"Unpausing recording\"),\n\t\t\t\t\t\t  this, this);\n\tLoadHotkeyPair(pauseHotkeys, \"OBSBasic.PauseRecording\", \"OBSBasic.UnpauseRecording\");\n\n\tsplitFileHotkey = obs_hotkey_register_frontend(\n\t\t\"OBSBasic.SplitFile\", Str(\"Basic.Main.SplitFile\"),\n\t\t[](void *, obs_hotkey_id, obs_hotkey_t *, bool pressed) {\n\t\t\tif (pressed)\n\t\t\t\tobs_frontend_recording_split_file();\n\t\t},\n\t\tthis);\n\tLoadHotkey(splitFileHotkey, \"OBSBasic.SplitFile\");\n\n\taddChapterHotkey = obs_hotkey_register_frontend(\n\t\t\"OBSBasic.AddChapterMarker\", Str(\"Basic.Main.AddChapterMarker\"),\n\t\t[](void *, obs_hotkey_id, obs_hotkey_t *, bool pressed) {\n\t\t\tif (pressed)\n\t\t\t\tobs_frontend_recording_add_chapter(nullptr);\n\t\t},\n\t\tthis);\n\tLoadHotkey(addChapterHotkey, \"OBSBasic.AddChapterMarker\");\n\n\treplayBufHotkeys =\n\t\tobs_hotkey_pair_register_frontend(\"OBSBasic.StartReplayBuffer\", Str(\"Basic.Main.StartReplayBuffer\"),\n\t\t\t\t\t\t  \"OBSBasic.StopReplayBuffer\", Str(\"Basic.Main.StopReplayBuffer\"),\n\t\t\t\t\t\t  MAKE_CALLBACK(!basic.outputHandler->ReplayBufferActive(),\n\t\t\t\t\t\t\t\tbasic.StartReplayBuffer, \"Starting replay buffer\"),\n\t\t\t\t\t\t  MAKE_CALLBACK(basic.outputHandler->ReplayBufferActive(),\n\t\t\t\t\t\t\t\tbasic.StopReplayBuffer, \"Stopping replay buffer\"),\n\t\t\t\t\t\t  this, this);\n\tLoadHotkeyPair(replayBufHotkeys, \"OBSBasic.StartReplayBuffer\", \"OBSBasic.StopReplayBuffer\");\n\n\tif (vcamEnabled) {\n\t\tvcamHotkeys = obs_hotkey_pair_register_frontend(\n\t\t\t\"OBSBasic.StartVirtualCam\", Str(\"Basic.Main.StartVirtualCam\"), \"OBSBasic.StopVirtualCam\",\n\t\t\tStr(\"Basic.Main.StopVirtualCam\"),\n\t\t\tMAKE_CALLBACK(!basic.outputHandler->VirtualCamActive(), basic.StartVirtualCam,\n\t\t\t\t      \"Starting virtual camera\"),\n\t\t\tMAKE_CALLBACK(basic.outputHandler->VirtualCamActive(), basic.StopVirtualCam,\n\t\t\t\t      \"Stopping virtual camera\"),\n\t\t\tthis, this);\n\t\tLoadHotkeyPair(vcamHotkeys, \"OBSBasic.StartVirtualCam\", \"OBSBasic.StopVirtualCam\");\n\t}\n\n\ttogglePreviewHotkeys = obs_hotkey_pair_register_frontend(\n\t\t\"OBSBasic.EnablePreview\", Str(\"Basic.Main.PreviewConextMenu.Enable\"), \"OBSBasic.DisablePreview\",\n\t\tStr(\"Basic.Main.Preview.Disable\"),\n\t\tMAKE_CALLBACK(!basic.previewEnabled, basic.EnablePreview, \"Enabling preview\"),\n\t\tMAKE_CALLBACK(basic.previewEnabled, basic.DisablePreview, \"Disabling preview\"), this, this);\n\tLoadHotkeyPair(togglePreviewHotkeys, \"OBSBasic.EnablePreview\", \"OBSBasic.DisablePreview\");\n\n\ttogglePreviewProgramHotkeys = obs_hotkey_pair_register_frontend(\n\t\t\"OBSBasic.EnablePreviewProgram\", Str(\"Basic.EnablePreviewProgramMode\"),\n\t\t\"OBSBasic.DisablePreviewProgram\", Str(\"Basic.DisablePreviewProgramMode\"),\n\t\tMAKE_CALLBACK(!basic.IsPreviewProgramMode(), basic.EnablePreviewProgram, \"Enabling preview program\"),\n\t\tMAKE_CALLBACK(basic.IsPreviewProgramMode(), basic.DisablePreviewProgram, \"Disabling preview program\"),\n\t\tthis, this);\n\tLoadHotkeyPair(togglePreviewProgramHotkeys, \"OBSBasic.EnablePreviewProgram\", \"OBSBasic.DisablePreviewProgram\",\n\t\t       \"OBSBasic.TogglePreviewProgram\");\n\n\tcontextBarHotkeys = obs_hotkey_pair_register_frontend(\n\t\t\"OBSBasic.ShowContextBar\", Str(\"Basic.Main.ShowContextBar\"), \"OBSBasic.HideContextBar\",\n\t\tStr(\"Basic.Main.HideContextBar\"),\n\t\tMAKE_CALLBACK(!basic.ui->contextContainer->isVisible(), basic.ShowContextBar, \"Showing Context Bar\"),\n\t\tMAKE_CALLBACK(basic.ui->contextContainer->isVisible(), basic.HideContextBar, \"Hiding Context Bar\"),\n\t\tthis, this);\n\tLoadHotkeyPair(contextBarHotkeys, \"OBSBasic.ShowContextBar\", \"OBSBasic.HideContextBar\");\n#undef MAKE_CALLBACK\n\n\tauto transition = [](void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed) {\n\t\tif (pressed)\n\t\t\tQMetaObject::invokeMethod(static_cast<OBSBasic *>(data), \"TransitionClicked\",\n\t\t\t\t\t\t  Qt::QueuedConnection);\n\t};\n\n\ttransitionHotkey = obs_hotkey_register_frontend(\"OBSBasic.Transition\", Str(\"Transition\"), transition, this);\n\tLoadHotkey(transitionHotkey, \"OBSBasic.Transition\");\n\n\tauto resetStats = [](void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed) {\n\t\tif (pressed)\n\t\t\tQMetaObject::invokeMethod(static_cast<OBSBasic *>(data), \"ResetStatsHotkey\",\n\t\t\t\t\t\t  Qt::QueuedConnection);\n\t};\n\n\tstatsHotkey =\n\t\tobs_hotkey_register_frontend(\"OBSBasic.ResetStats\", Str(\"Basic.Stats.ResetStats\"), resetStats, this);\n\tLoadHotkey(statsHotkey, \"OBSBasic.ResetStats\");\n\n\tauto screenshot = [](void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed) {\n\t\tif (pressed)\n\t\t\tQMetaObject::invokeMethod(static_cast<OBSBasic *>(data), \"Screenshot\", Qt::QueuedConnection);\n\t};\n\n\tscreenshotHotkey = obs_hotkey_register_frontend(\"OBSBasic.Screenshot\", Str(\"Screenshot\"), screenshot, this);\n\tLoadHotkey(screenshotHotkey, \"OBSBasic.Screenshot\");\n\n\tauto screenshotSource = [](void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed) {\n\t\tif (pressed)\n\t\t\tQMetaObject::invokeMethod(static_cast<OBSBasic *>(data), \"ScreenshotSelectedSource\",\n\t\t\t\t\t\t  Qt::QueuedConnection);\n\t};\n\n\tsourceScreenshotHotkey = obs_hotkey_register_frontend(\"OBSBasic.SelectedSourceScreenshot\",\n\t\t\t\t\t\t\t      Str(\"Screenshot.SourceHotkey\"), screenshotSource, this);\n\tLoadHotkey(sourceScreenshotHotkey, \"OBSBasic.SelectedSourceScreenshot\");\n}\n\nvoid OBSBasic::ClearHotkeys()\n{\n\tobs_hotkey_pair_unregister(streamingHotkeys);\n\tobs_hotkey_pair_unregister(recordingHotkeys);\n\tobs_hotkey_pair_unregister(pauseHotkeys);\n\tobs_hotkey_unregister(splitFileHotkey);\n\tobs_hotkey_unregister(addChapterHotkey);\n\tobs_hotkey_pair_unregister(replayBufHotkeys);\n\tobs_hotkey_pair_unregister(vcamHotkeys);\n\tobs_hotkey_pair_unregister(togglePreviewHotkeys);\n\tobs_hotkey_pair_unregister(contextBarHotkeys);\n\tobs_hotkey_pair_unregister(togglePreviewProgramHotkeys);\n\tobs_hotkey_unregister(forceStreamingStopHotkey);\n\tobs_hotkey_unregister(transitionHotkey);\n\tobs_hotkey_unregister(statsHotkey);\n\tobs_hotkey_unregister(screenshotHotkey);\n\tobs_hotkey_unregister(sourceScreenshotHotkey);\n}\n\nvoid OBSBasic::ResetStatsHotkey()\n{\n\tconst QList<OBSBasicStats *> list = findChildren<OBSBasicStats *>();\n\n\tfor (OBSBasicStats *s : list) {\n\t\ts->Reset();\n\t}\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Icons.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\nQIcon OBSBasic::GetSourceIcon(const char *id) const\n{\n\tobs_icon_type type = obs_source_get_icon_type(id);\n\n\tswitch (type) {\n\tcase OBS_ICON_TYPE_IMAGE:\n\t\treturn GetImageIcon();\n\tcase OBS_ICON_TYPE_COLOR:\n\t\treturn GetColorIcon();\n\tcase OBS_ICON_TYPE_SLIDESHOW:\n\t\treturn GetSlideshowIcon();\n\tcase OBS_ICON_TYPE_AUDIO_INPUT:\n\t\treturn GetAudioInputIcon();\n\tcase OBS_ICON_TYPE_AUDIO_OUTPUT:\n\t\treturn GetAudioOutputIcon();\n\tcase OBS_ICON_TYPE_DESKTOP_CAPTURE:\n\t\treturn GetDesktopCapIcon();\n\tcase OBS_ICON_TYPE_WINDOW_CAPTURE:\n\t\treturn GetWindowCapIcon();\n\tcase OBS_ICON_TYPE_GAME_CAPTURE:\n\t\treturn GetGameCapIcon();\n\tcase OBS_ICON_TYPE_CAMERA:\n\t\treturn GetCameraIcon();\n\tcase OBS_ICON_TYPE_TEXT:\n\t\treturn GetTextIcon();\n\tcase OBS_ICON_TYPE_MEDIA:\n\t\treturn GetMediaIcon();\n\tcase OBS_ICON_TYPE_BROWSER:\n\t\treturn GetBrowserIcon();\n\tcase OBS_ICON_TYPE_CUSTOM:\n\t\t//TODO: Add ability for sources to define custom icons\n\t\treturn GetDefaultIcon();\n\tcase OBS_ICON_TYPE_PROCESS_AUDIO_OUTPUT:\n\t\treturn GetAudioProcessOutputIcon();\n\tdefault:\n\t\treturn GetDefaultIcon();\n\t}\n}\n\nvoid OBSBasic::SetImageIcon(const QIcon &icon)\n{\n\timageIcon = icon;\n}\n\nvoid OBSBasic::SetColorIcon(const QIcon &icon)\n{\n\tcolorIcon = icon;\n}\n\nvoid OBSBasic::SetSlideshowIcon(const QIcon &icon)\n{\n\tslideshowIcon = icon;\n}\n\nvoid OBSBasic::SetAudioInputIcon(const QIcon &icon)\n{\n\taudioInputIcon = icon;\n}\n\nvoid OBSBasic::SetAudioOutputIcon(const QIcon &icon)\n{\n\taudioOutputIcon = icon;\n}\n\nvoid OBSBasic::SetDesktopCapIcon(const QIcon &icon)\n{\n\tdesktopCapIcon = icon;\n}\n\nvoid OBSBasic::SetWindowCapIcon(const QIcon &icon)\n{\n\twindowCapIcon = icon;\n}\n\nvoid OBSBasic::SetGameCapIcon(const QIcon &icon)\n{\n\tgameCapIcon = icon;\n}\n\nvoid OBSBasic::SetCameraIcon(const QIcon &icon)\n{\n\tcameraIcon = icon;\n}\n\nvoid OBSBasic::SetTextIcon(const QIcon &icon)\n{\n\ttextIcon = icon;\n}\n\nvoid OBSBasic::SetMediaIcon(const QIcon &icon)\n{\n\tmediaIcon = icon;\n}\n\nvoid OBSBasic::SetBrowserIcon(const QIcon &icon)\n{\n\tbrowserIcon = icon;\n}\n\nvoid OBSBasic::SetGroupIcon(const QIcon &icon)\n{\n\tgroupIcon = icon;\n}\n\nvoid OBSBasic::SetSceneIcon(const QIcon &icon)\n{\n\tsceneIcon = icon;\n}\n\nvoid OBSBasic::SetDefaultIcon(const QIcon &icon)\n{\n\tdefaultIcon = icon;\n}\n\nvoid OBSBasic::SetAudioProcessOutputIcon(const QIcon &icon)\n{\n\taudioProcessOutputIcon = icon;\n}\n\nQIcon OBSBasic::GetImageIcon() const\n{\n\treturn imageIcon;\n}\n\nQIcon OBSBasic::GetColorIcon() const\n{\n\treturn colorIcon;\n}\n\nQIcon OBSBasic::GetSlideshowIcon() const\n{\n\treturn slideshowIcon;\n}\n\nQIcon OBSBasic::GetAudioInputIcon() const\n{\n\treturn audioInputIcon;\n}\n\nQIcon OBSBasic::GetAudioOutputIcon() const\n{\n\treturn audioOutputIcon;\n}\n\nQIcon OBSBasic::GetDesktopCapIcon() const\n{\n\treturn desktopCapIcon;\n}\n\nQIcon OBSBasic::GetWindowCapIcon() const\n{\n\treturn windowCapIcon;\n}\n\nQIcon OBSBasic::GetGameCapIcon() const\n{\n\treturn gameCapIcon;\n}\n\nQIcon OBSBasic::GetCameraIcon() const\n{\n\treturn cameraIcon;\n}\n\nQIcon OBSBasic::GetTextIcon() const\n{\n\treturn textIcon;\n}\n\nQIcon OBSBasic::GetMediaIcon() const\n{\n\treturn mediaIcon;\n}\n\nQIcon OBSBasic::GetBrowserIcon() const\n{\n\treturn browserIcon;\n}\n\nQIcon OBSBasic::GetGroupIcon() const\n{\n\treturn groupIcon;\n}\n\nQIcon OBSBasic::GetSceneIcon() const\n{\n\treturn sceneIcon;\n}\n\nQIcon OBSBasic::GetDefaultIcon() const\n{\n\treturn defaultIcon;\n}\n\nQIcon OBSBasic::GetAudioProcessOutputIcon() const\n{\n\treturn audioProcessOutputIcon;\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_MainControls.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n#include \"OBSBasicStats.hpp\"\n\n#include <dialogs/LogUploadDialog.hpp>\n#include <dialogs/OBSAbout.hpp>\n#include <dialogs/OBSBasicAdvAudio.hpp>\n#include <dialogs/OBSBasicFilters.hpp>\n#include <dialogs/OBSBasicInteraction.hpp>\n#include <dialogs/OBSBasicProperties.hpp>\n#include <dialogs/OBSBasicTransform.hpp>\n#ifdef ENABLE_IDIAN_PLAYGROUND\n#include <dialogs/OBSIdianPlayground.hpp>\n#endif\n#include <dialogs/OBSLogViewer.hpp>\n#ifdef __APPLE__\n#include <dialogs/OBSPermissions.hpp>\n#endif\n#include <dialogs/OBSRemux.hpp>\n#include <settings/OBSBasicSettings.hpp>\n#ifdef _WIN32\n#include <utility/AutoUpdateThread.hpp>\n#endif\n#include <utility/RemoteTextThread.hpp>\n#if defined(_WIN32) || defined(WHATSNEW_ENABLED)\n#include <utility/WhatsNewInfoThread.hpp>\n#endif\n#include <wizards/AutoConfig.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QDesktopServices>\n\n#ifdef _WIN32\n#include <sstream>\n#endif\n\nextern bool restart;\nextern bool restart_safe;\nextern volatile long insideEventLoop;\nextern bool safe_mode;\n\nstruct QCef;\nstruct QCefCookieManager;\n\nextern QCef *cef;\nextern QCefCookieManager *panel_cookies;\n\nusing namespace std;\nusing LogUploadDialog = OBS::LogUploadDialog;\nusing LogUploadType = OBS::LogFileType;\n\nvoid OBSBasic::CreateInteractionWindow(obs_source_t *source)\n{\n\tbool closed = true;\n\tif (interaction)\n\t\tclosed = interaction->close();\n\n\tif (!closed)\n\t\treturn;\n\n\tinteraction = new OBSBasicInteraction(this, source);\n\tinteraction->Init();\n\tinteraction->setAttribute(Qt::WA_DeleteOnClose, true);\n}\n\nvoid OBSBasic::CreatePropertiesWindow(obs_source_t *source)\n{\n\tbool closed = true;\n\tif (properties)\n\t\tclosed = properties->close();\n\n\tif (!closed)\n\t\treturn;\n\n\tproperties = new OBSBasicProperties(this, source);\n\tproperties->Init();\n\tproperties->setAttribute(Qt::WA_DeleteOnClose, true);\n}\n\nvoid OBSBasic::CreateFiltersWindow(obs_source_t *source)\n{\n\tbool closed = true;\n\tif (filters)\n\t\tclosed = filters->close();\n\n\tif (!closed)\n\t\treturn;\n\n\tfilters = new OBSBasicFilters(this, source);\n\tfilters->Init();\n\tfilters->setAttribute(Qt::WA_DeleteOnClose, true);\n}\n\nvoid OBSBasic::updateCheckFinished()\n{\n\tui->actionCheckForUpdates->setEnabled(true);\n\tui->actionRepair->setEnabled(true);\n}\n\nvoid OBSBasic::ResetUI()\n{\n\tbool studioPortraitLayout = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"StudioPortraitLayout\");\n\n\tif (studioPortraitLayout) {\n\t\tui->previewLayout->setDirection(QBoxLayout::BottomToTop);\n\t} else {\n\t\tui->previewLayout->setDirection(QBoxLayout::LeftToRight);\n\t}\n\n\tUpdatePreviewProgramIndicators();\n}\n\nvoid OBSBasic::CloseDialogs()\n{\n\tQList<QDialog *> childDialogs = this->findChildren<QDialog *>();\n\tif (!childDialogs.isEmpty()) {\n\t\tfor (int i = 0; i < childDialogs.size(); ++i) {\n\t\t\tchildDialogs.at(i)->close();\n\t\t}\n\t}\n\n\tif (!stats.isNull())\n\t\tstats->close(); //call close to save Stats geometry\n\tif (!remux.isNull())\n\t\tremux->close();\n}\n\nvoid OBSBasic::EnumDialogs()\n{\n\tvisDialogs.clear();\n\tmodalDialogs.clear();\n\tvisMsgBoxes.clear();\n\n\t/* fill list of Visible dialogs and Modal dialogs */\n\tQList<QDialog *> dialogs = findChildren<QDialog *>();\n\tfor (QDialog *dialog : dialogs) {\n\t\tif (dialog->isVisible())\n\t\t\tvisDialogs.append(dialog);\n\t\tif (dialog->isModal())\n\t\t\tmodalDialogs.append(dialog);\n\t}\n\n\t/* fill list of Visible message boxes */\n\tQList<QMessageBox *> msgBoxes = findChildren<QMessageBox *>();\n\tfor (QMessageBox *msgbox : msgBoxes) {\n\t\tif (msgbox->isVisible())\n\t\t\tvisMsgBoxes.append(msgbox);\n\t}\n}\n\nvoid OBSBasic::on_actionRemux_triggered()\n{\n\tif (!remux.isNull()) {\n\t\tremux->show();\n\t\tremux->raise();\n\t\treturn;\n\t}\n\n\tconst char *mode = config_get_string(activeConfiguration, \"Output\", \"Mode\");\n\tconst char *path = strcmp(mode, \"Advanced\") ? config_get_string(activeConfiguration, \"SimpleOutput\", \"FilePath\")\n\t\t\t\t\t\t    : config_get_string(activeConfiguration, \"AdvOut\", \"RecFilePath\");\n\n\tOBSRemux *remuxDlg;\n\tremuxDlg = new OBSRemux(path, this);\n\tremuxDlg->show();\n\tremux = remuxDlg;\n}\n\nvoid OBSBasic::on_action_Settings_triggered()\n{\n\tstatic bool settings_already_executing = false;\n\n\t/* Do not load settings window if inside of a temporary event loop\n\t * because we could be inside of an Auth::LoadUI call.  Keep trying\n\t * once per second until we've exit any known sub-loops. */\n\tif (os_atomic_load_long(&insideEventLoop) != 0) {\n\t\tQTimer::singleShot(1000, this, &OBSBasic::on_action_Settings_triggered);\n\t\treturn;\n\t}\n\n\tif (settings_already_executing) {\n\t\treturn;\n\t}\n\n\tsettings_already_executing = true;\n\n\t{\n\t\tOBSBasicSettings settings(this);\n\t\tsettings.exec();\n\t}\n\n\tsettings_already_executing = false;\n\n\tif (restart) {\n\t\tQMessageBox::StandardButton button =\n\t\t\tOBSMessageBox::question(this, QTStr(\"Restart\"), QTStr(\"NeedsRestart\"));\n\n\t\tif (button == QMessageBox::Yes)\n\t\t\tclose();\n\t\telse\n\t\t\trestart = false;\n\t}\n}\n\nvoid OBSBasic::on_actionShowMacPermissions_triggered()\n{\n#ifdef __APPLE__\n\tOBSPermissions check(this, CheckPermission(kScreenCapture), CheckPermission(kVideoDeviceAccess),\n\t\t\t     CheckPermission(kAudioDeviceAccess), CheckPermission(kInputMonitoring));\n\tcheck.exec();\n#endif\n}\n\nvoid OBSBasic::on_actionAdvAudioProperties_triggered()\n{\n\tif (advAudioWindow != nullptr) {\n\t\tadvAudioWindow->raise();\n\t\treturn;\n\t}\n\n\tbool iconsVisible = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowSourceIcons\");\n\n\tadvAudioWindow = new OBSBasicAdvAudio(this);\n\tadvAudioWindow->show();\n\tadvAudioWindow->setAttribute(Qt::WA_DeleteOnClose, true);\n\tadvAudioWindow->SetIconsVisible(iconsVisible);\n}\n\nstatic BPtr<char> ReadLogFile(const char *subdir, const char *log)\n{\n\tchar logDir[512];\n\tif (GetAppConfigPath(logDir, sizeof(logDir), subdir) <= 0)\n\t\treturn nullptr;\n\n\tstring path = logDir;\n\tpath += \"/\";\n\tpath += log;\n\n\tBPtr<char> file = os_quick_read_utf8_file(path.c_str());\n\tif (!file)\n\t\tblog(LOG_WARNING, \"Failed to read log file %s\", path.c_str());\n\n\treturn file;\n}\n\nvoid OBSBasic::UploadLog(const char *subdir, const char *file, const LogUploadType uploadType)\n{\n\tBPtr<char> fileString{ReadLogFile(subdir, file)};\n\n\tif (!fileString || !*fileString) {\n\t\tOBSApp *app = App();\n\t\temit app->logUploadFailed(uploadType, QTStr(\"LogUploadDialog.Errors.NoLogFile\"));\n\t\treturn;\n\t}\n\n\tui->menuLogFiles->setEnabled(false);\n\n\tstringstream ss;\n\tss << \"OBS \" << App()->GetVersionString(false) << \" log file uploaded at \" << CurrentDateTimeString()\n\t   << ((uploadType == OBS::LogFileType::CurrentAppLog) ? \" (Active Log)\" : \" (Complete Log)\") << \"\\n\\n\"\n\t   << fileString;\n\n\tif (logUploadThread) {\n\t\tlogUploadThread->wait();\n\t}\n\n\tRemoteTextThread *thread = new RemoteTextThread(\"https://obsproject.com/logs/upload\", \"text/plain\", ss.str());\n\n\tlogUploadThread.reset(thread);\n\n\tconnect(thread, &RemoteTextThread::Result, this, [this, uploadType](const QString &text, const QString &error) {\n\t\tlogUploadFinished(text, error, uploadType);\n\t});\n\n\tlogUploadThread->start();\n}\n\nvoid OBSBasic::on_actionShowLogs_triggered()\n{\n\tchar logDir[512];\n\tif (GetAppConfigPath(logDir, sizeof(logDir), \"obs-studio/logs\") <= 0)\n\t\treturn;\n\n\tQUrl url = QUrl::fromLocalFile(QT_UTF8(logDir));\n\tQDesktopServices::openUrl(url);\n}\n\nvoid OBSBasic::on_actionUploadCurrentLog_triggered()\n{\n\tui->menuLogFiles->setEnabled(false);\n\n\tLogUploadDialog uploadDialog{this, LogUploadType::CurrentAppLog};\n\n\tuploadDialog.exec();\n\n\tui->menuLogFiles->setEnabled(true);\n}\n\nvoid OBSBasic::on_actionUploadLastLog_triggered()\n{\n\tui->menuLogFiles->setEnabled(false);\n\n\tLogUploadDialog uploadDialog{this, LogUploadType::LastAppLog};\n\n\tuploadDialog.exec();\n\n\tui->menuLogFiles->setEnabled(true);\n}\n\nvoid OBSBasic::on_actionViewCurrentLog_triggered()\n{\n\tif (!logView)\n\t\tlogView = new OBSLogViewer();\n\n\tlogView->show();\n\tlogView->setWindowState((logView->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);\n\tlogView->activateWindow();\n\tlogView->raise();\n}\n\nvoid OBSBasic::on_actionShowCrashLogs_triggered()\n{\n\tApp()->openCrashLogDirectory();\n}\n\nvoid OBSBasic::on_actionUploadLastCrashLog_triggered()\n{\n\tui->menuCrashLogs->setEnabled(false);\n\n\tLogUploadDialog uploadDialog{this, LogUploadType::CrashLog};\n\n\tuploadDialog.exec();\n\n\tui->menuCrashLogs->setEnabled(true);\n}\n\nvoid OBSBasic::on_actionCheckForUpdates_triggered()\n{\n\tCheckForUpdates(true);\n}\n\nvoid OBSBasic::on_actionRepair_triggered()\n{\n#if defined(_WIN32)\n\tui->actionCheckForUpdates->setEnabled(false);\n\tui->actionRepair->setEnabled(false);\n\n\tif (updateCheckThread && updateCheckThread->isRunning())\n\t\treturn;\n\n\tupdateCheckThread.reset(new AutoUpdateThread(false, true));\n\tupdateCheckThread->start();\n#endif\n}\n\nvoid OBSBasic::on_actionRestartSafe_triggered()\n{\n\tQMessageBox::StandardButton button = OBSMessageBox::question(\n\t\tthis, QTStr(\"Restart\"), safe_mode ? QTStr(\"SafeMode.RestartNormal\") : QTStr(\"SafeMode.Restart\"));\n\n\tif (button == QMessageBox::Yes) {\n\t\trestart = safe_mode;\n\t\trestart_safe = !safe_mode;\n\t\tclose();\n\t}\n}\n\nvoid OBSBasic::logUploadFinished(const QString &text, const QString &error, LogUploadType uploadType)\n{\n\tOBSApp *app = App();\n\n\tif (text.isEmpty()) {\n\t\temit app->logUploadFailed(uploadType, error);\n\t} else {\n\t\tOBSDataAutoRelease returnData = obs_data_create_from_json(QT_TO_UTF8(text));\n\t\tstring resURL = obs_data_get_string(returnData, \"url\");\n\t\tQString logURL = resURL.c_str();\n\n\t\temit app->logUploadFinished(uploadType, logURL);\n\t}\n}\n\nvoid OBSBasic::on_actionHelpPortal_triggered()\n{\n\tQUrl url = QUrl(\"https://obsproject.com/help\", QUrl::TolerantMode);\n\tQDesktopServices::openUrl(url);\n}\n\nvoid OBSBasic::on_actionWebsite_triggered()\n{\n\tQUrl url = QUrl(\"https://obsproject.com\", QUrl::TolerantMode);\n\tQDesktopServices::openUrl(url);\n}\n\nvoid OBSBasic::on_actionDiscord_triggered()\n{\n\tQUrl url = QUrl(\"https://obsproject.com/discord\", QUrl::TolerantMode);\n\tQDesktopServices::openUrl(url);\n}\n\nvoid OBSBasic::on_actionShowWhatsNew_triggered()\n{\n#ifdef WHATSNEW_ENABLED\n\tif (introCheckThread && introCheckThread->isRunning())\n\t\treturn;\n\tif (!cef)\n\t\treturn;\n\n\tconfig_set_int(App()->GetAppConfig(), \"General\", \"InfoIncrement\", -1);\n\n\tWhatsNewInfoThread *wnit = new WhatsNewInfoThread();\n\tconnect(wnit, &WhatsNewInfoThread::Result, this, &OBSBasic::ReceivedIntroJson, Qt::QueuedConnection);\n\n\tintroCheckThread.reset(wnit);\n\tintroCheckThread->start();\n#endif\n}\n\nvoid OBSBasic::on_actionReleaseNotes_triggered()\n{\n\tQString addr(\"https://github.com/obsproject/obs-studio/releases\");\n\tQUrl url(QString(\"%1/%2\").arg(addr, obs_get_version_string()), QUrl::TolerantMode);\n\tQDesktopServices::openUrl(url);\n}\n\nvoid OBSBasic::on_actionShowSettingsFolder_triggered()\n{\n\tconst std::string userConfigPath = App()->userConfigLocation.u8string() + \"/obs-studio\";\n\tconst QString userConfigLocation = QString::fromStdString(userConfigPath);\n\n\tQDesktopServices::openUrl(QUrl::fromLocalFile(userConfigLocation));\n}\n\nvoid OBSBasic::on_actionShowProfileFolder_triggered()\n{\n\ttry {\n\t\tconst OBSProfile &currentProfile = GetCurrentProfile();\n\t\tQString currentProfileLocation = QString::fromStdString(currentProfile.path.u8string());\n\n\t\tQDesktopServices::openUrl(QUrl::fromLocalFile(currentProfileLocation));\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t}\n}\n\nvoid OBSBasic::on_actionAlwaysOnTop_triggered()\n{\n#ifndef _WIN32\n\t/* Make sure all dialogs are safely and successfully closed before\n\t * switching the always on top mode due to the fact that windows all\n\t * have to be recreated, so queue the actual toggle to happen after\n\t * all events related to closing the dialogs have finished */\n\tCloseDialogs();\n#endif\n\n\tQMetaObject::invokeMethod(this, \"ToggleAlwaysOnTop\", Qt::QueuedConnection);\n}\n\nvoid OBSBasic::ToggleAlwaysOnTop()\n{\n\tbool isAlwaysOnTop = IsAlwaysOnTop(this);\n\n\tui->actionAlwaysOnTop->setChecked(!isAlwaysOnTop);\n\tSetAlwaysOnTop(this, !isAlwaysOnTop);\n\n\tshow();\n}\n\nvoid OBSBasic::CreateEditTransformWindow(obs_sceneitem_t *item)\n{\n\tif (transformWindow)\n\t\ttransformWindow->close();\n\ttransformWindow = new OBSBasicTransform(item, this);\n\tconnect(ui->scenes, &QListWidget::currentItemChanged, transformWindow, &OBSBasicTransform::onSceneChanged);\n\ttransformWindow->show();\n\ttransformWindow->setAttribute(Qt::WA_DeleteOnClose, true);\n}\n\nvoid OBSBasic::on_actionFullscreenInterface_triggered()\n{\n\tif (!isFullScreen())\n\t\tshowFullScreen();\n\telse\n\t\tshowNormal();\n}\n\nvoid OBSBasic::on_resetUI_triggered()\n{\n\ton_resetDocks_triggered();\n\tsetPreviewScalingWindow();\n\n\tui->toggleListboxToolbars->setChecked(true);\n\tui->toggleContextBar->setChecked(true);\n\tui->toggleSourceIcons->setChecked(true);\n\tui->toggleStatusBar->setChecked(true);\n\tui->scenes->SetGridMode(false);\n\tui->actionSceneListMode->setChecked(true);\n\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"gridMode\", false);\n}\n\nvoid OBSBasic::on_toggleListboxToolbars_toggled(bool visible)\n{\n\tui->sourcesToolbar->setVisible(visible);\n\tui->scenesToolbar->setVisible(visible);\n\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowListboxToolbars\", visible);\n\temit userSettingChanged(\"BasicWindow\", \"ShowListboxToolbars\");\n}\n\nvoid OBSBasic::on_toggleStatusBar_toggled(bool visible)\n{\n\tui->statusbar->setVisible(visible);\n\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowStatusBar\", visible);\n}\n\nvoid OBSBasic::SetShowing(bool showing)\n{\n\tif (!showing && isVisible()) {\n\t\tconfig_set_string(App()->GetUserConfig(), \"BasicWindow\", \"geometry\",\n\t\t\t\t  saveGeometry().toBase64().constData());\n\n\t\t/* hide all visible child dialogs */\n\t\tvisDlgPositions.clear();\n\t\tif (!visDialogs.isEmpty()) {\n\t\t\tfor (QDialog *dlg : visDialogs) {\n\t\t\t\tvisDlgPositions.append(dlg->pos());\n\t\t\t\tdlg->hide();\n\t\t\t}\n\t\t}\n\n\t\tif (showHide)\n\t\t\tshowHide->setText(QTStr(\"Basic.SystemTray.Show\"));\n\t\tQTimer::singleShot(0, this, &OBSBasic::hide);\n\n\t\tif (previewEnabled)\n\t\t\tEnablePreviewDisplay(false);\n\n#ifdef __APPLE__\n\t\tEnableOSXDockIcon(false);\n#endif\n\n\t} else if (showing && !isVisible()) {\n\t\tif (showHide)\n\t\t\tshowHide->setText(QTStr(\"Basic.SystemTray.Hide\"));\n\t\tQTimer::singleShot(0, this, &OBSBasic::show);\n\n\t\tif (previewEnabled)\n\t\t\tEnablePreviewDisplay(true);\n\n#ifdef __APPLE__\n\t\tEnableOSXDockIcon(true);\n#endif\n\n\t\t/* raise and activate window to ensure it is on top */\n\t\traise();\n\t\tactivateWindow();\n\n\t\t/* show all child dialogs that was visible earlier */\n\t\tif (!visDialogs.isEmpty()) {\n\t\t\tfor (int i = 0; i < visDialogs.size(); ++i) {\n\t\t\t\tQDialog *dlg = visDialogs[i];\n\t\t\t\tdlg->move(visDlgPositions[i]);\n\t\t\t\tdlg->show();\n\t\t\t}\n\t\t}\n\n\t\t/* Unminimize window if it was hidden to tray instead of task\n\t\t * bar. */\n\t\tif (sysTrayMinimizeToTray()) {\n\t\t\tQt::WindowStates state;\n\t\t\tstate = windowState() & ~Qt::WindowMinimized;\n\t\t\tstate |= Qt::WindowActive;\n\t\t\tsetWindowState(state);\n\t\t}\n\t}\n}\n\nvoid OBSBasic::ToggleShowHide()\n{\n\tbool showing = isVisible();\n\tif (showing) {\n\t\t/* check for modal dialogs */\n\t\tEnumDialogs();\n\t\tif (!modalDialogs.isEmpty() || !visMsgBoxes.isEmpty())\n\t\t\treturn;\n\t}\n\tSetShowing(!showing);\n}\n\nvoid OBSBasic::on_actionMainUndo_triggered()\n{\n\tundo_s.undo();\n}\n\nvoid OBSBasic::on_actionMainRedo_triggered()\n{\n\tundo_s.redo();\n}\n\nvoid OBSBasic::on_autoConfigure_triggered()\n{\n\tAutoConfig test(this);\n\ttest.setModal(true);\n\ttest.show();\n\ttest.exec();\n}\n\nvoid OBSBasic::on_stats_triggered()\n{\n\tif (!stats.isNull()) {\n\t\tstats->show();\n\t\tstats->raise();\n\t\treturn;\n\t}\n\n\tOBSBasicStats *statsDlg;\n\tstatsDlg = new OBSBasicStats(nullptr);\n\tstatsDlg->show();\n\tstats = statsDlg;\n}\n\nvoid OBSBasic::on_idianPlayground_triggered()\n{\n#ifdef ENABLE_IDIAN_PLAYGROUND\n\tOBSIdianPlayground playground(this);\n\tplayground.setModal(true);\n\tplayground.show();\n\tplayground.exec();\n#endif\n}\n\nvoid OBSBasic::on_actionShowAbout_triggered()\n{\n\tif (about)\n\t\tabout->close();\n\n\tabout = new OBSAbout(this);\n\tabout->show();\n\n\tabout->setAttribute(Qt::WA_DeleteOnClose, true);\n}\n\nvoid OBSBasic::on_OBSBasic_customContextMenuRequested(const QPoint &pos)\n{\n\tQWidget *widget = childAt(pos);\n\tconst char *className = nullptr;\n\tQString objName;\n\tif (widget != nullptr) {\n\t\tclassName = widget->metaObject()->className();\n\t\tobjName = widget->objectName();\n\t}\n\n\tQPoint globalPos = mapToGlobal(pos);\n\tif (className && strstr(className, \"Dock\") != nullptr && !objName.isEmpty()) {\n\t\tif (objName.compare(\"scenesDock\") == 0) {\n\t\t\tui->scenes->customContextMenuRequested(globalPos);\n\t\t} else if (objName.compare(\"sourcesDock\") == 0) {\n\t\t\tui->sources->customContextMenuRequested(globalPos);\n\t\t}\n\t} else if (!className) {\n\t\tui->menuDocks->exec(globalPos);\n\t}\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_OutputHandler.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <qt-wrappers.hpp>\n\n#include <QDir>\n\nvoid OBSBasic::ResetOutputs()\n{\n\tProfileScope(\"OBSBasic::ResetOutputs\");\n\n\tconst char *mode = config_get_string(activeConfiguration, \"Output\", \"Mode\");\n\tbool advOut = astrcmpi(mode, \"Advanced\") == 0;\n\n\tif ((!outputHandler || !outputHandler->Active()) &&\n\t    (!setupStreamingGuard.valid() ||\n\t     setupStreamingGuard.wait_for(std::chrono::seconds{0}) == std::future_status::ready)) {\n\t\toutputHandler.reset();\n\t\toutputHandler.reset(advOut ? CreateAdvancedOutputHandler(this) : CreateSimpleOutputHandler(this));\n\n\t\temit ReplayBufEnabled(outputHandler->replayBuffer);\n\n\t\tif (sysTrayReplayBuffer)\n\t\t\tsysTrayReplayBuffer->setEnabled(!!outputHandler->replayBuffer);\n\n\t\tUpdateIsRecordingPausable();\n\t} else {\n\t\toutputHandler->Update();\n\t}\n}\n\nbool OBSBasic::Active() const\n{\n\tif (!outputHandler)\n\t\treturn false;\n\treturn outputHandler->Active();\n}\n\nvoid OBSBasic::ResizeOutputSizeOfSource()\n{\n\tif (obs_video_active())\n\t\treturn;\n\n\tQMessageBox resize_output(this);\n\tresize_output.setText(QTStr(\"ResizeOutputSizeOfSource.Text\") + \"\\n\\n\" +\n\t\t\t      QTStr(\"ResizeOutputSizeOfSource.Continue\"));\n\tQAbstractButton *Yes = resize_output.addButton(QTStr(\"Yes\"), QMessageBox::YesRole);\n\tresize_output.addButton(QTStr(\"No\"), QMessageBox::NoRole);\n\tresize_output.setIcon(QMessageBox::Warning);\n\tresize_output.setWindowTitle(QTStr(\"ResizeOutputSizeOfSource\"));\n\tresize_output.exec();\n\n\tif (resize_output.clickedButton() != Yes)\n\t\treturn;\n\n\tOBSSource source = obs_sceneitem_get_source(GetCurrentSceneItem());\n\n\tint width = obs_source_get_width(source);\n\tint height = obs_source_get_height(source);\n\n\twidth = ((width + 3) / 4) * 4;   // Round width up to the nearest multiple of 4\n\theight = ((height + 1) / 2) * 2; // Round height up to the nearest multiple of 2\n\n\tconfig_set_uint(activeConfiguration, \"Video\", \"BaseCX\", width);\n\tconfig_set_uint(activeConfiguration, \"Video\", \"BaseCY\", height);\n\tconfig_set_uint(activeConfiguration, \"Video\", \"OutputCX\", width);\n\tconfig_set_uint(activeConfiguration, \"Video\", \"OutputCY\", height);\n\n\tResetVideo();\n\tResetOutputs();\n\tactiveConfiguration.SaveSafe(\"tmp\");\n\ton_actionFitToScreen_triggered();\n}\n\nconst char *OBSBasic::GetCurrentOutputPath()\n{\n\tconst char *path = nullptr;\n\tconst char *mode = config_get_string(Config(), \"Output\", \"Mode\");\n\n\tif (strcmp(mode, \"Advanced\") == 0) {\n\t\tconst char *advanced_mode = config_get_string(Config(), \"AdvOut\", \"RecType\");\n\n\t\tif (strcmp(advanced_mode, \"FFmpeg\") == 0) {\n\t\t\tpath = config_get_string(Config(), \"AdvOut\", \"FFFilePath\");\n\t\t} else {\n\t\t\tpath = config_get_string(Config(), \"AdvOut\", \"RecFilePath\");\n\t\t}\n\t} else {\n\t\tpath = config_get_string(Config(), \"SimpleOutput\", \"FilePath\");\n\t}\n\n\treturn path;\n}\n\nvoid OBSBasic::OutputPathInvalidMessage()\n{\n\tblog(LOG_ERROR, \"Recording stopped because of bad output path\");\n\n\tOBSMessageBox::critical(this, QTStr(\"Output.BadPath.Title\"), QTStr(\"Output.BadPath.Text\"));\n}\n\nbool OBSBasic::IsFFmpegOutputToURL() const\n{\n\tconst char *mode = config_get_string(Config(), \"Output\", \"Mode\");\n\tif (strcmp(mode, \"Advanced\") == 0) {\n\t\tconst char *advanced_mode = config_get_string(Config(), \"AdvOut\", \"RecType\");\n\t\tif (strcmp(advanced_mode, \"FFmpeg\") == 0) {\n\t\t\tbool is_local = config_get_bool(Config(), \"AdvOut\", \"FFOutputToFile\");\n\t\t\tif (!is_local)\n\t\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nbool OBSBasic::OutputPathValid()\n{\n\tif (IsFFmpegOutputToURL())\n\t\treturn true;\n\n\tconst char *path = GetCurrentOutputPath();\n\treturn path && *path && QDir(path).exists();\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Preview.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <utility/display-helpers.hpp>\n#include <widgets/OBSProjector.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QColorDialog>\n\n#include <sstream>\n\nextern void undo_redo(const std::string &data);\n\nusing namespace std;\n\nvoid OBSBasic::InitPrimitives()\n{\n\tProfileScope(\"OBSBasic::InitPrimitives\");\n\n\tobs_enter_graphics();\n\n\tgs_render_start(true);\n\tgs_vertex2f(0.0f, 0.0f);\n\tgs_vertex2f(0.0f, 1.0f);\n\tgs_vertex2f(1.0f, 0.0f);\n\tgs_vertex2f(1.0f, 1.0f);\n\tbox = gs_render_save();\n\n\tgs_render_start(true);\n\tgs_vertex2f(0.0f, 0.0f);\n\tgs_vertex2f(0.0f, 1.0f);\n\tboxLeft = gs_render_save();\n\n\tgs_render_start(true);\n\tgs_vertex2f(0.0f, 0.0f);\n\tgs_vertex2f(1.0f, 0.0f);\n\tboxTop = gs_render_save();\n\n\tgs_render_start(true);\n\tgs_vertex2f(1.0f, 0.0f);\n\tgs_vertex2f(1.0f, 1.0f);\n\tboxRight = gs_render_save();\n\n\tgs_render_start(true);\n\tgs_vertex2f(0.0f, 1.0f);\n\tgs_vertex2f(1.0f, 1.0f);\n\tboxBottom = gs_render_save();\n\n\tgs_render_start(true);\n\tfor (int i = 0; i <= 360; i += (360 / 20)) {\n\t\tfloat pos = RAD(float(i));\n\t\tgs_vertex2f(cosf(pos), sinf(pos));\n\t}\n\tcircle = gs_render_save();\n\n\tInitSafeAreas(&actionSafeMargin, &graphicsSafeMargin, &fourByThreeSafeMargin, &leftLine, &topLine, &rightLine);\n\tobs_leave_graphics();\n}\n\nvoid OBSBasic::UpdatePreviewScalingMenu()\n{\n\tbool fixedScaling = ui->preview->IsFixedScaling();\n\tfloat scalingAmount = ui->preview->GetScalingAmount();\n\tif (!fixedScaling) {\n\t\tui->actionScaleWindow->setChecked(true);\n\t\tui->actionScaleCanvas->setChecked(false);\n\t\tui->actionScaleOutput->setChecked(false);\n\t\treturn;\n\t}\n\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tui->actionScaleWindow->setChecked(false);\n\tui->actionScaleCanvas->setChecked(scalingAmount == 1.0f);\n\tui->actionScaleOutput->setChecked(scalingAmount == float(ovi.output_width) / float(ovi.base_width));\n}\n\nvoid OBSBasic::DrawBackdrop(float cx, float cy)\n{\n\tif (!box)\n\t\treturn;\n\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, \"DrawBackdrop\");\n\n\tgs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);\n\tgs_eparam_t *color = gs_effect_get_param_by_name(solid, \"color\");\n\tgs_technique_t *tech = gs_effect_get_technique(solid, \"Solid\");\n\n\tvec4 colorVal;\n\tvec4_set(&colorVal, 0.0f, 0.0f, 0.0f, 1.0f);\n\tgs_effect_set_vec4(color, &colorVal);\n\n\tgs_technique_begin(tech);\n\tgs_technique_begin_pass(tech, 0);\n\tgs_matrix_push();\n\tgs_matrix_identity();\n\tgs_matrix_scale3f(float(cx), float(cy), 1.0f);\n\n\tgs_load_vertexbuffer(box);\n\tgs_draw(GS_TRISTRIP, 0, 0);\n\n\tgs_matrix_pop();\n\tgs_technique_end_pass(tech);\n\tgs_technique_end(tech);\n\n\tgs_load_vertexbuffer(nullptr);\n\n\tGS_DEBUG_MARKER_END();\n}\n\nvoid OBSBasic::RenderMain(void *data, uint32_t, uint32_t)\n{\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, \"RenderMain\");\n\n\tOBSBasic *window = static_cast<OBSBasic *>(data);\n\tobs_video_info ovi;\n\n\tobs_get_video_info(&ovi);\n\n\twindow->previewCX = int(window->previewScale * float(ovi.base_width));\n\twindow->previewCY = int(window->previewScale * float(ovi.base_height));\n\n\tgs_viewport_push();\n\tgs_projection_push();\n\n\tobs_display_t *display = window->ui->preview->GetDisplay();\n\tuint32_t width, height;\n\tobs_display_size(display, &width, &height);\n\tfloat right = float(width) - window->previewX;\n\tfloat bottom = float(height) - window->previewY;\n\n\tgs_ortho(-window->previewX, right, -window->previewY, bottom, -100.0f, 100.0f);\n\n\twindow->ui->preview->DrawOverflow();\n\n\t/* --------------------------------------- */\n\n\tgs_ortho(0.0f, float(ovi.base_width), 0.0f, float(ovi.base_height), -100.0f, 100.0f);\n\tgs_set_viewport(window->previewX, window->previewY, window->previewCX, window->previewCY);\n\n\tif (window->IsPreviewProgramMode()) {\n\t\twindow->DrawBackdrop(float(ovi.base_width), float(ovi.base_height));\n\n\t\tOBSScene scene = window->GetCurrentScene();\n\t\tobs_source_t *source = obs_scene_get_source(scene);\n\t\tif (source)\n\t\t\tobs_source_video_render(source);\n\t} else {\n\t\tobs_render_main_texture_src_color_only();\n\t}\n\tgs_load_vertexbuffer(nullptr);\n\n\t/* --------------------------------------- */\n\n\tgs_ortho(-window->previewX, right, -window->previewY, bottom, -100.0f, 100.0f);\n\tgs_reset_viewport();\n\n\tuint32_t targetCX = window->previewCX;\n\tuint32_t targetCY = window->previewCY;\n\n\tif (window->drawSafeAreas) {\n\t\tRenderSafeAreas(window->actionSafeMargin, targetCX, targetCY);\n\t\tRenderSafeAreas(window->graphicsSafeMargin, targetCX, targetCY);\n\t\tRenderSafeAreas(window->fourByThreeSafeMargin, targetCX, targetCY);\n\t\tRenderSafeAreas(window->leftLine, targetCX, targetCY);\n\t\tRenderSafeAreas(window->topLine, targetCX, targetCY);\n\t\tRenderSafeAreas(window->rightLine, targetCX, targetCY);\n\t}\n\n\twindow->ui->preview->DrawSceneEditing();\n\n\tif (window->drawSpacingHelpers)\n\t\twindow->ui->preview->DrawSpacingHelpers();\n\n\t/* --------------------------------------- */\n\n\tgs_projection_pop();\n\tgs_viewport_pop();\n\n\tGS_DEBUG_MARKER_END();\n}\n\nvoid OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)\n{\n\tQSize targetSize;\n\tbool isFixedScaling;\n\tobs_video_info ovi;\n\n\t/* resize preview panel to fix to the top section of the window */\n\ttargetSize = GetPixelSize(ui->preview);\n\n\tisFixedScaling = ui->preview->IsFixedScaling();\n\tobs_get_video_info(&ovi);\n\n\tif (isFixedScaling) {\n\t\tpreviewScale = ui->preview->GetScalingAmount();\n\n\t\tui->preview->ClampScrollingOffsets();\n\n\t\tGetCenterPosFromFixedScale(int(cx), int(cy), targetSize.width() - PREVIEW_EDGE_SIZE * 2,\n\t\t\t\t\t   targetSize.height() - PREVIEW_EDGE_SIZE * 2, previewX, previewY,\n\t\t\t\t\t   previewScale);\n\t\tpreviewX += ui->preview->GetScrollX();\n\t\tpreviewY += ui->preview->GetScrollY();\n\n\t} else {\n\t\tGetScaleAndCenterPos(int(cx), int(cy), targetSize.width() - PREVIEW_EDGE_SIZE * 2,\n\t\t\t\t     targetSize.height() - PREVIEW_EDGE_SIZE * 2, previewX, previewY, previewScale);\n\t}\n\n\tui->preview->SetScalingAmount(previewScale);\n\n\tpreviewX += float(PREVIEW_EDGE_SIZE);\n\tpreviewY += float(PREVIEW_EDGE_SIZE);\n}\n\nvoid OBSBasic::on_preview_customContextMenuRequested()\n{\n\tCreateSourcePopupMenu(GetTopSelectedSourceItem(), true);\n}\n\nvoid OBSBasic::on_previewDisabledWidget_customContextMenuRequested()\n{\n\tQMenu popup(this);\n\tdelete previewProjectorMain;\n\n\tQAction *action = popup.addAction(QTStr(\"Basic.Main.PreviewConextMenu.Enable\"), this, &OBSBasic::TogglePreview);\n\taction->setCheckable(true);\n\taction->setChecked(obs_display_enabled(ui->preview->GetDisplay()));\n\n\tpreviewProjectorMain = new QMenu(QTStr(\"Projector.Open.Preview\"));\n\tAddProjectorMenuMonitors(previewProjectorMain, this, &OBSBasic::OpenPreviewProjector);\n\tpreviewProjectorMain->addSeparator();\n\tpreviewProjectorMain->addAction(QTStr(\"Projector.Window\"), this, &OBSBasic::OpenPreviewWindow);\n\n\tpopup.addMenu(previewProjectorMain);\n\tpopup.exec(QCursor::pos());\n}\n\nvoid OBSBasic::EnablePreviewDisplay(bool enable)\n{\n\tobs_display_set_enabled(ui->preview->GetDisplay(), enable);\n\tui->previewContainer->setVisible(enable);\n\tui->previewDisabledWidget->setVisible(!enable);\n}\n\nvoid OBSBasic::TogglePreview()\n{\n\tpreviewEnabled = !previewEnabled;\n\tEnablePreviewDisplay(previewEnabled);\n}\n\nvoid OBSBasic::EnablePreview()\n{\n\tif (previewProgramMode)\n\t\treturn;\n\n\tpreviewEnabled = true;\n\tEnablePreviewDisplay(true);\n}\n\nvoid OBSBasic::DisablePreview()\n{\n\tif (previewProgramMode)\n\t\treturn;\n\n\tpreviewEnabled = false;\n\tEnablePreviewDisplay(false);\n}\n\nstatic bool nudge_callback(obs_scene_t *, obs_sceneitem_t *item, void *param)\n{\n\tif (obs_sceneitem_locked(item))\n\t\treturn true;\n\n\tstruct vec2 &offset = *static_cast<struct vec2 *>(param);\n\tstruct vec2 pos;\n\n\tif (!obs_sceneitem_selected(item)) {\n\t\tif (obs_sceneitem_is_group(item)) {\n\t\t\tstruct vec3 offset3;\n\t\t\tvec3_set(&offset3, offset.x, offset.y, 0.0f);\n\n\t\t\tstruct matrix4 matrix;\n\t\t\tobs_sceneitem_get_draw_transform(item, &matrix);\n\t\t\tvec4_set(&matrix.t, 0.0f, 0.0f, 0.0f, 1.0f);\n\t\t\tmatrix4_inv(&matrix, &matrix);\n\t\t\tvec3_transform(&offset3, &offset3, &matrix);\n\n\t\t\tstruct vec2 new_offset;\n\t\t\tvec2_set(&new_offset, offset3.x, offset3.y);\n\t\t\tobs_sceneitem_group_enum_items(item, nudge_callback, &new_offset);\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tobs_sceneitem_get_pos(item, &pos);\n\tvec2_add(&pos, &pos, &offset);\n\tobs_sceneitem_set_pos(item, &pos);\n\treturn true;\n}\n\nvoid OBSBasic::Nudge(int dist, MoveDir dir)\n{\n\tif (ui->preview->Locked())\n\t\treturn;\n\n\tstruct vec2 offset;\n\tvec2_set(&offset, 0.0f, 0.0f);\n\n\tswitch (dir) {\n\tcase MoveDir::Up:\n\t\toffset.y = (float)-dist;\n\t\tbreak;\n\tcase MoveDir::Down:\n\t\toffset.y = (float)dist;\n\t\tbreak;\n\tcase MoveDir::Left:\n\t\toffset.x = (float)-dist;\n\t\tbreak;\n\tcase MoveDir::Right:\n\t\toffset.x = (float)dist;\n\t\tbreak;\n\t}\n\n\tif (!recent_nudge) {\n\t\trecent_nudge = true;\n\t\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), true);\n\t\tstd::string undo_data(obs_data_get_json(wrapper));\n\n\t\tnudge_timer = new QTimer;\n\t\tQObject::connect(nudge_timer, &QTimer::timeout, this, [this, &recent_nudge = recent_nudge, undo_data]() {\n\t\t\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), true);\n\t\t\tstd::string redo_data(obs_data_get_json(rwrapper));\n\n\t\t\tundo_s.add_action(QTStr(\"Undo.Transform\").arg(obs_source_get_name(GetCurrentSceneSource())),\n\t\t\t\t\t  undo_redo, undo_redo, undo_data, redo_data);\n\n\t\t\trecent_nudge = false;\n\t\t});\n\t\tconnect(nudge_timer, &QTimer::timeout, nudge_timer, &QTimer::deleteLater);\n\t\tnudge_timer->setSingleShot(true);\n\t}\n\n\tif (nudge_timer) {\n\t\tnudge_timer->stop();\n\t\tnudge_timer->start(1000);\n\t} else {\n\t\tblog(LOG_ERROR, \"No nudge timer!\");\n\t}\n\n\tobs_scene_enum_items(GetCurrentScene(), nudge_callback, &offset);\n}\n\nvoid OBSBasic::on_actionLockPreview_triggered()\n{\n\tui->preview->ToggleLocked();\n\tui->actionLockPreview->setChecked(ui->preview->Locked());\n}\n\nvoid OBSBasic::on_scalingMenu_aboutToShow()\n{\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tQAction *action = ui->actionScaleCanvas;\n\tQString text = QTStr(\"Basic.MainMenu.Edit.Scale.Canvas\");\n\ttext = text.arg(QString::number(ovi.base_width), QString::number(ovi.base_height));\n\taction->setText(text);\n\n\taction = ui->actionScaleOutput;\n\ttext = QTStr(\"Basic.MainMenu.Edit.Scale.Output\");\n\ttext = text.arg(QString::number(ovi.output_width), QString::number(ovi.output_height));\n\taction->setText(text);\n\taction->setVisible(!(ovi.output_width == ovi.base_width && ovi.output_height == ovi.base_height));\n\n\tUpdatePreviewScalingMenu();\n}\n\nvoid OBSBasic::setPreviewScalingWindow()\n{\n\tui->preview->SetFixedScaling(false);\n\tui->preview->ResetScrollingOffset();\n\n\temit ui->preview->DisplayResized();\n}\n\nvoid OBSBasic::setPreviewScalingCanvas()\n{\n\tui->preview->SetFixedScaling(true);\n\tui->preview->SetScalingLevel(0);\n\n\temit ui->preview->DisplayResized();\n}\n\nvoid OBSBasic::setPreviewScalingOutput()\n{\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tui->preview->SetFixedScaling(true);\n\tfloat scalingAmount = float(ovi.output_width) / float(ovi.base_width);\n\t// log base ZOOM_SENSITIVITY of x = log(x) / log(ZOOM_SENSITIVITY)\n\tint32_t approxScalingLevel = int32_t(round(log(scalingAmount) / log(ZOOM_SENSITIVITY)));\n\tui->preview->SetScalingLevelAndAmount(approxScalingLevel, scalingAmount);\n\temit ui->preview->DisplayResized();\n}\n\nstatic void ConfirmColor(SourceTree *sources, const QColor &color, QModelIndexList selectedItems)\n{\n\tfor (int x = 0; x < selectedItems.count(); x++) {\n\t\tSourceTreeItem *treeItem = sources->GetItemWidget(selectedItems[x].row());\n\t\ttreeItem->setStyleSheet(\"background: \" + color.name(QColor::HexArgb));\n\t\ttreeItem->style()->unpolish(treeItem);\n\t\ttreeItem->style()->polish(treeItem);\n\n\t\tOBSSceneItem sceneItem = sources->Get(selectedItems[x].row());\n\t\tOBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneItem);\n\t\tobs_data_set_int(privData, \"color-preset\", 1);\n\t\tobs_data_set_string(privData, \"color\", QT_TO_UTF8(color.name(QColor::HexArgb)));\n\t}\n}\n\nvoid OBSBasic::ColorChange()\n{\n\tQModelIndexList selectedItems = ui->sources->selectionModel()->selectedIndexes();\n\tQAction *action = qobject_cast<QAction *>(sender());\n\tQPushButton *colorButton = qobject_cast<QPushButton *>(sender());\n\n\tif (selectedItems.count() == 0)\n\t\treturn;\n\n\tif (colorButton) {\n\t\tint preset = colorButton->property(\"bgColor\").value<int>();\n\n\t\tfor (int x = 0; x < selectedItems.count(); x++) {\n\t\t\tSourceTreeItem *treeItem = ui->sources->GetItemWidget(selectedItems[x].row());\n\t\t\ttreeItem->setStyleSheet(\"\");\n\t\t\ttreeItem->setProperty(\"bgColor\", preset);\n\t\t\ttreeItem->style()->unpolish(treeItem);\n\t\t\ttreeItem->style()->polish(treeItem);\n\n\t\t\tOBSSceneItem sceneItem = ui->sources->Get(selectedItems[x].row());\n\t\t\tOBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneItem);\n\t\t\tobs_data_set_int(privData, \"color-preset\", preset + 1);\n\t\t\tobs_data_set_string(privData, \"color\", \"\");\n\t\t}\n\n\t\tfor (int i = 1; i < 9; i++) {\n\t\t\tstringstream button;\n\t\t\tbutton << \"preset\" << i;\n\t\t\tQPushButton *cButton =\n\t\t\t\tcolorButton->parentWidget()->findChild<QPushButton *>(button.str().c_str());\n\t\t\tcButton->setStyleSheet(\"border: 1px solid black\");\n\t\t}\n\n\t\tcolorButton->setStyleSheet(\"border: 2px solid black\");\n\t} else if (action) {\n\t\tint preset = action->property(\"bgColor\").value<int>();\n\n\t\tif (preset == 1) {\n\t\t\tOBSSceneItem curSceneItem = GetCurrentSceneItem();\n\t\t\tSourceTreeItem *curTreeItem = GetItemWidgetFromSceneItem(curSceneItem);\n\t\t\tOBSDataAutoRelease curPrivData = obs_sceneitem_get_private_settings(curSceneItem);\n\n\t\t\tint oldPreset = obs_data_get_int(curPrivData, \"color-preset\");\n\t\t\tconst QString oldSheet = curTreeItem->styleSheet();\n\n\t\t\tauto liveChangeColor = [=](const QColor &color) {\n\t\t\t\tif (color.isValid()) {\n\t\t\t\t\tcurTreeItem->setStyleSheet(\"background: \" + color.name(QColor::HexArgb));\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tauto changedColor = [this, selectedItems](const QColor &color) {\n\t\t\t\tif (color.isValid()) {\n\t\t\t\t\tConfirmColor(ui->sources, color, selectedItems);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tauto rejected = [=]() {\n\t\t\t\tif (oldPreset == 1) {\n\t\t\t\t\tcurTreeItem->setStyleSheet(oldSheet);\n\t\t\t\t\tcurTreeItem->setProperty(\"bgColor\", 0);\n\t\t\t\t} else if (oldPreset == 0) {\n\t\t\t\t\tcurTreeItem->setStyleSheet(\"background: none\");\n\t\t\t\t\tcurTreeItem->setProperty(\"bgColor\", 0);\n\t\t\t\t} else {\n\t\t\t\t\tcurTreeItem->setStyleSheet(\"\");\n\t\t\t\t\tcurTreeItem->setProperty(\"bgColor\", oldPreset - 1);\n\t\t\t\t}\n\n\t\t\t\tcurTreeItem->style()->unpolish(curTreeItem);\n\t\t\t\tcurTreeItem->style()->polish(curTreeItem);\n\t\t\t};\n\n\t\t\tQColorDialog::ColorDialogOptions options = QColorDialog::ShowAlphaChannel;\n\n\t\t\tconst char *oldColor = obs_data_get_string(curPrivData, \"color\");\n\t\t\tconst char *customColor = *oldColor != 0 ? oldColor : \"#55FF0000\";\n#ifdef __linux__\n\t\t\t// TODO: Revisit hang on Ubuntu with native dialog\n\t\t\toptions |= QColorDialog::DontUseNativeDialog;\n#endif\n\n\t\t\tQColorDialog *colorDialog = new QColorDialog(this);\n\t\t\tcolorDialog->setOptions(options);\n\t\t\tcolorDialog->setCurrentColor(QColor(customColor));\n\t\t\tconnect(colorDialog, &QColorDialog::currentColorChanged, this, liveChangeColor);\n\t\t\tconnect(colorDialog, &QColorDialog::colorSelected, this, changedColor);\n\t\t\tconnect(colorDialog, &QColorDialog::rejected, this, rejected);\n\t\t\tcolorDialog->open();\n\t\t} else {\n\t\t\tfor (int x = 0; x < selectedItems.count(); x++) {\n\t\t\t\tSourceTreeItem *treeItem = ui->sources->GetItemWidget(selectedItems[x].row());\n\t\t\t\ttreeItem->setStyleSheet(\"background: none\");\n\t\t\t\ttreeItem->setProperty(\"bgColor\", preset);\n\t\t\t\ttreeItem->style()->unpolish(treeItem);\n\t\t\t\ttreeItem->style()->polish(treeItem);\n\n\t\t\t\tOBSSceneItem sceneItem = ui->sources->Get(selectedItems[x].row());\n\t\t\t\tOBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneItem);\n\t\t\t\tobs_data_set_int(privData, \"color-preset\", preset);\n\t\t\t\tobs_data_set_string(privData, \"color\", \"\");\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid OBSBasic::UpdateProjectorHideCursor()\n{\n\tfor (size_t i = 0; i < projectors.size(); i++)\n\t\tprojectors[i]->SetHideCursor();\n}\n\nvoid OBSBasic::UpdateProjectorAlwaysOnTop(bool top)\n{\n\tfor (size_t i = 0; i < projectors.size(); i++)\n\t\tSetAlwaysOnTop(projectors[i], top);\n}\n\nvoid OBSBasic::ResetProjectors()\n{\n\tOBSDataArrayAutoRelease savedProjectorList = SaveProjectors();\n\tClearProjectors();\n\tLoadSavedProjectors(savedProjectorList);\n}\n\nvoid OBSBasic::UpdatePreviewSafeAreas()\n{\n\tdrawSafeAreas = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowSafeAreas\");\n}\n\nvoid OBSBasic::UpdatePreviewOverflowSettings()\n{\n\tbool hidden = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"OverflowHidden\");\n\tbool select = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"OverflowSelectionHidden\");\n\tbool always = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"OverflowAlwaysVisible\");\n\n\tui->preview->SetOverflowHidden(hidden);\n\tui->preview->SetOverflowSelectionHidden(select);\n\tui->preview->SetOverflowAlwaysVisible(always);\n}\n\nstatic inline QColor color_from_int(long long val)\n{\n\treturn QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);\n}\n\nQColor OBSBasic::GetSelectionColor() const\n{\n\tif (config_get_bool(App()->GetUserConfig(), \"Accessibility\", \"OverrideColors\")) {\n\t\treturn color_from_int(config_get_int(App()->GetUserConfig(), \"Accessibility\", \"SelectRed\"));\n\t} else {\n\t\treturn QColor::fromRgb(255, 0, 0);\n\t}\n}\n\nQColor OBSBasic::GetCropColor() const\n{\n\tif (config_get_bool(App()->GetUserConfig(), \"Accessibility\", \"OverrideColors\")) {\n\t\treturn color_from_int(config_get_int(App()->GetUserConfig(), \"Accessibility\", \"SelectGreen\"));\n\t} else {\n\t\treturn QColor::fromRgb(0, 255, 0);\n\t}\n}\n\nQColor OBSBasic::GetHoverColor() const\n{\n\tif (config_get_bool(App()->GetUserConfig(), \"Accessibility\", \"OverrideColors\")) {\n\t\treturn color_from_int(config_get_int(App()->GetUserConfig(), \"Accessibility\", \"SelectBlue\"));\n\t} else {\n\t\treturn QColor::fromRgb(0, 127, 255);\n\t}\n}\n\nvoid OBSBasic::UpdatePreviewSpacingHelpers()\n{\n\tdrawSpacingHelpers = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SpacingHelpersEnabled\");\n}\n\nfloat OBSBasic::GetDevicePixelRatio()\n{\n\treturn dpi;\n}\n\nvoid OBSBasic::UpdatePreviewControls()\n{\n\tconst int scalingLevel = ui->preview->GetScalingLevel();\n\n\tif (!ui->preview->IsFixedScaling()) {\n\t\tui->previewXScrollBar->setRange(0, 0);\n\t\tui->previewYScrollBar->setRange(0, 0);\n\n\t\tui->actionPreviewResetZoom->setEnabled(false);\n\n\t\treturn;\n\t}\n\n\tconst bool minZoom = scalingLevel == MAX_SCALING_LEVEL;\n\tconst bool maxZoom = scalingLevel == -MAX_SCALING_LEVEL;\n\n\tui->actionPreviewZoomIn->setEnabled(!minZoom);\n\tui->previewZoomInButton->setEnabled(!minZoom);\n\n\tui->actionPreviewZoomOut->setEnabled(!maxZoom);\n\tui->previewZoomOutButton->setEnabled(!maxZoom);\n\n\tui->actionPreviewResetZoom->setEnabled(scalingLevel != 0);\n}\n\nvoid OBSBasic::PreviewScalingModeChanged(int value)\n{\n\tswitch (value) {\n\tcase 0:\n\t\tsetPreviewScalingWindow();\n\t\tbreak;\n\tcase 1:\n\t\tsetPreviewScalingCanvas();\n\t\tbreak;\n\tcase 2:\n\t\tsetPreviewScalingOutput();\n\t\tbreak;\n\t};\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Profiles.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#ifdef YOUTUBE_ENABLED\n#include <docks/YouTubeAppDock.hpp>\n#endif\n#include <wizards/AutoConfig.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QDir>\n#include <QFile>\n\n// MARK: Constant Expressions\n\nconstexpr std::string_view OBSProfilePath = \"/obs-studio/basic/profiles/\";\nconstexpr std::string_view OBSProfileSettingsFile = \"basic.ini\";\n\n// MARK: Forward Declarations\n\nextern bool restart;\n\nextern void DestroyPanelCookieManager();\nextern void DuplicateCurrentCookieProfile(ConfigFile &config);\nextern void CheckExistingCookieId();\nextern void DeleteCookies();\n\n// MARK: - Anonymous Namespace\nnamespace {\nQList<QString> sortedProfiles{};\n\nvoid updateSortedProfiles(const OBSProfileCache &profiles)\n{\n\tconst QLocale locale = QLocale::system();\n\tQList<QString> newList{};\n\n\tfor (auto [profileName, _] : profiles) {\n\t\tQString entry = QString::fromStdString(profileName);\n\t\tnewList.append(entry);\n\t}\n\n\tstd::sort(newList.begin(), newList.end(), [&locale](const QString &lhs, const QString &rhs) -> bool {\n\t\tint result = QString::localeAwareCompare(locale.toLower(lhs), locale.toLower(rhs));\n\n\t\treturn (result < 0);\n\t});\n\n\tsortedProfiles.swap(newList);\n}\n} // namespace\n\n// MARK: - Main Profile Management Functions\n\nvoid OBSBasic::SetupNewProfile(const std::string &profileName, bool useWizard)\n{\n\tconst OBSProfile &newProfile = CreateProfile(profileName);\n\n\tconfig_set_bool(App()->GetUserConfig(), \"Basic\", \"ConfigOnNewProfile\", useWizard);\n\n\tActivateProfile(newProfile, true);\n\n\tblog(LOG_INFO, \"Created profile '%s' (clean, %s)\", newProfile.name.c_str(), newProfile.directoryName.c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n\n\tif (useWizard) {\n\t\tAutoConfig wizard(this);\n\t\twizard.setModal(true);\n\t\twizard.show();\n\t\twizard.exec();\n\t}\n}\n\nvoid OBSBasic::SetupDuplicateProfile(const std::string &profileName)\n{\n\tconst OBSProfile &newProfile = CreateProfile(profileName);\n\tconst OBSProfile &currentProfile = GetCurrentProfile();\n\n\tconst auto copyOptions = std::filesystem::copy_options::recursive |\n\t\t\t\t std::filesystem::copy_options::overwrite_existing;\n\n\ttry {\n\t\tstd::filesystem::copy(currentProfile.path, newProfile.path, copyOptions);\n\t} catch (const std::filesystem::filesystem_error &error) {\n\t\tblog(LOG_DEBUG, \"%s\", error.what());\n\t\tthrow std::logic_error(\"Failed to copy files for cloned profile: \" + newProfile.name);\n\t}\n\n\tActivateProfile(newProfile);\n\n\tblog(LOG_INFO, \"Created profile '%s' (duplicate, %s)\", newProfile.name.c_str(),\n\t     newProfile.directoryName.c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n}\n\nvoid OBSBasic::SetupRenameProfile(const std::string &profileName)\n{\n\tconst OBSProfile &newProfile = CreateProfile(profileName);\n\tconst OBSProfile currentProfile = GetCurrentProfile();\n\n\tconst auto copyOptions = std::filesystem::copy_options::recursive |\n\t\t\t\t std::filesystem::copy_options::overwrite_existing;\n\n\ttry {\n\t\tstd::filesystem::copy(currentProfile.path, newProfile.path, copyOptions);\n\t} catch (const std::filesystem::filesystem_error &error) {\n\t\tblog(LOG_DEBUG, \"%s\", error.what());\n\t\tthrow std::logic_error(\"Failed to copy files for profile: \" + currentProfile.name);\n\t}\n\n\tprofiles.erase(currentProfile.name);\n\n\tActivateProfile(newProfile);\n\tRemoveProfile(currentProfile);\n\n\tblog(LOG_INFO, \"Renamed profile '%s' to '%s' (%s)\", currentProfile.name.c_str(), newProfile.name.c_str(),\n\t     newProfile.directoryName.c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n\n\tOnEvent(OBS_FRONTEND_EVENT_PROFILE_RENAMED);\n}\n\n// MARK: - Profile File Management Functions\n\nconst OBSProfile &OBSBasic::CreateProfile(const std::string &profileName)\n{\n\tif (const auto &foundProfile = GetProfileByName(profileName)) {\n\t\tthrow std::invalid_argument(\"Profile already exists: \" + profileName);\n\t}\n\n\tstd::string directoryName;\n\tif (!GetFileSafeName(profileName.c_str(), directoryName)) {\n\t\tthrow std::invalid_argument(\"Failed to create safe directory for new profile: \" + profileName);\n\t}\n\n\tstd::string profileDirectory;\n\tprofileDirectory.reserve(App()->userProfilesLocation.u8string().size() + OBSProfilePath.size() +\n\t\t\t\t directoryName.size());\n\tprofileDirectory.append(App()->userProfilesLocation.u8string()).append(OBSProfilePath).append(directoryName);\n\n\tif (!GetClosestUnusedFileName(profileDirectory, nullptr)) {\n\t\tthrow std::invalid_argument(\"Failed to get closest directory name for new profile: \" + profileName);\n\t}\n\n\tconst std::filesystem::path profileDirectoryPath = std::filesystem::u8path(profileDirectory);\n\n\ttry {\n\t\tstd::filesystem::create_directory(profileDirectoryPath);\n\t} catch (const std::filesystem::filesystem_error error) {\n\t\tthrow std::logic_error(\"Failed to create directory for new profile: \" + profileDirectory);\n\t}\n\n\tconst std::filesystem::path profileFile =\n\t\tprofileDirectoryPath / std::filesystem::u8path(OBSProfileSettingsFile);\n\n\tauto [iterator, success] =\n\t\tprofiles.try_emplace(profileName, OBSProfile{profileName, profileDirectoryPath.filename().u8string(),\n\t\t\t\t\t\t\t     profileDirectoryPath, profileFile});\n\n\tOnEvent(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED);\n\n\treturn iterator->second;\n}\n\nvoid OBSBasic::RemoveProfile(OBSProfile profile)\n{\n\ttry {\n\t\tstd::filesystem::remove_all(profile.path);\n\t} catch (const std::filesystem::filesystem_error &error) {\n\t\tblog(LOG_DEBUG, \"%s\", error.what());\n\t\tthrow std::logic_error(\"Failed to remove profile directory: \" + profile.directoryName);\n\t}\n\n\tblog(LOG_INFO, \"------------------------------------------------\");\n\tblog(LOG_INFO, \"Removed profile '%s' (%s)\", profile.name.c_str(), profile.directoryName.c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n}\n\n// MARK: - Profile UI Handling Functions\n\nbool OBSBasic::CreateNewProfile(const QString &name)\n{\n\ttry {\n\t\tSetupNewProfile(name.toStdString());\n\t\treturn true;\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t\treturn false;\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t\treturn false;\n\t}\n}\n\nbool OBSBasic::CreateDuplicateProfile(const QString &name)\n{\n\ttry {\n\t\tSetupDuplicateProfile(name.toStdString());\n\t\treturn true;\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t\treturn false;\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t\treturn false;\n\t}\n}\n\nvoid OBSBasic::DeleteProfile(const QString &name)\n{\n\tconst std::string_view currentProfileName{config_get_string(App()->GetUserConfig(), \"Basic\", \"Profile\")};\n\n\tif (currentProfileName == name.toStdString()) {\n\t\ton_actionRemoveProfile_triggered();\n\t\treturn;\n\t}\n\n\tauto foundProfile = GetProfileByName(name.toStdString());\n\n\tif (!foundProfile) {\n\t\tblog(LOG_ERROR, \"Invalid profile name: %s\", QT_TO_UTF8(name));\n\t\treturn;\n\t}\n\n\tRemoveProfile(foundProfile.value());\n\tprofiles.erase(name.toStdString());\n\n\tRefreshProfiles();\n\n\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n\n\tOnEvent(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED);\n}\n\nvoid OBSBasic::ChangeProfile()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\n\tif (!action) {\n\t\treturn;\n\t}\n\n\tconst std::string_view currentProfileName{config_get_string(App()->GetUserConfig(), \"Basic\", \"Profile\")};\n\tconst QVariant qProfileName = action->property(\"profile_name\");\n\tconst std::string selectedProfileName{qProfileName.toString().toStdString()};\n\n\tif (currentProfileName == selectedProfileName) {\n\t\taction->setChecked(true);\n\t\treturn;\n\t}\n\n\tconst std::optional<OBSProfile> foundProfile = GetProfileByName(selectedProfileName);\n\n\tif (!foundProfile) {\n\t\tconst std::string errorMessage{\"Selected profile not found: \"};\n\n\t\tthrow std::invalid_argument(errorMessage + selectedProfileName.data());\n\t}\n\n\tconst OBSProfile &selectedProfile = foundProfile.value();\n\n\tOnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGING);\n\n\tActivateProfile(selectedProfile, true);\n\n\tblog(LOG_INFO, \"Switched to profile '%s' (%s)\", selectedProfile.name.c_str(),\n\t     selectedProfile.directoryName.c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n}\n\nvoid OBSBasic::RefreshProfiles(bool refreshCache)\n{\n\tstd::string_view currentProfileName{config_get_string(App()->GetUserConfig(), \"Basic\", \"Profile\")};\n\n\tQList<QAction *> menuActions = ui->profileMenu->actions();\n\n\tfor (auto &action : menuActions) {\n\t\tQVariant variant = action->property(\"file_name\");\n\n\t\tif (variant.typeName() != nullptr) {\n\t\t\tdelete action;\n\t\t}\n\t}\n\n\tif (refreshCache) {\n\t\tRefreshProfileCache();\n\t}\n\tupdateSortedProfiles(profiles);\n\n\tsize_t numAddedProfiles = 0;\n\tfor (auto &name : sortedProfiles) {\n\t\tconst std::string profileName = name.toStdString();\n\t\ttry {\n\t\t\tconst OBSProfile &profile = profiles.at(profileName);\n\t\t\tconst QString qProfileName = QString().fromStdString(profileName);\n\n\t\t\tQAction *action = new QAction(qProfileName, this);\n\t\t\taction->setProperty(\"profile_name\", qProfileName);\n\t\t\taction->setProperty(\"file_name\", QString().fromStdString(profile.directoryName));\n\t\t\tconnect(action, &QAction::triggered, this, &OBSBasic::ChangeProfile);\n\t\t\taction->setCheckable(true);\n\t\t\taction->setChecked(profileName == currentProfileName);\n\n\t\t\tui->profileMenu->addAction(action);\n\n\t\t\tnumAddedProfiles += 1;\n\t\t} catch (const std::out_of_range &error) {\n\t\t\tblog(LOG_ERROR, \"No profile with name %s found in profile cache.\\n%s\", profileName.c_str(),\n\t\t\t     error.what());\n\t\t}\n\t}\n\n\tui->actionRemoveProfile->setEnabled(numAddedProfiles > 1);\n}\n\n// MARK: - Profile Cache Functions\n\n/// Refreshes profile cache data with profile state found on local file system.\nvoid OBSBasic::RefreshProfileCache()\n{\n\tstd::map<std::string, OBSProfile> foundProfiles{};\n\n\tconst std::filesystem::path profilesPath =\n\t\tApp()->userProfilesLocation / std::filesystem::u8path(OBSProfilePath.substr(1));\n\tconst std::filesystem::path profileSettingsFile = std::filesystem::u8path(OBSProfileSettingsFile);\n\n\tif (!std::filesystem::exists(profilesPath)) {\n\t\tblog(LOG_WARNING, \"Failed to get profiles config path\");\n\t\treturn;\n\t}\n\n\tfor (const auto &entry : std::filesystem::directory_iterator(profilesPath)) {\n\t\tif (!entry.is_directory()) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst auto profileCandidate = entry.path() / profileSettingsFile;\n\n\t\tConfigFile config;\n\n\t\tif (config.Open(profileCandidate.u8string().c_str(), CONFIG_OPEN_EXISTING) != CONFIG_SUCCESS) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tstd::string candidateName;\n\t\tconst char *configName = config_get_string(config, \"General\", \"Name\");\n\n\t\tif (configName) {\n\t\t\tcandidateName = configName;\n\t\t} else {\n\t\t\tcandidateName = entry.path().filename().u8string();\n\t\t}\n\n\t\tfoundProfiles.try_emplace(candidateName, OBSProfile{candidateName, entry.path().filename().u8string(),\n\t\t\t\t\t\t\t\t    entry.path(), profileCandidate});\n\t}\n\n\tprofiles.swap(foundProfiles);\n}\n\nconst OBSProfile &OBSBasic::GetCurrentProfile() const\n{\n\tstd::string currentProfileName{config_get_string(App()->GetUserConfig(), \"Basic\", \"Profile\")};\n\n\tif (currentProfileName.empty()) {\n\t\tthrow std::invalid_argument(\"No valid profile name in configuration Basic->Profile\");\n\t}\n\n\tconst auto &foundProfile = profiles.find(currentProfileName);\n\n\tif (foundProfile != profiles.end()) {\n\t\treturn foundProfile->second;\n\t} else {\n\t\tthrow std::invalid_argument(\"Profile not found in profile list: \" + currentProfileName);\n\t}\n}\n\nstd::optional<OBSProfile> OBSBasic::GetProfileByName(const std::string &profileName) const\n{\n\tauto foundProfile = profiles.find(profileName);\n\n\tif (foundProfile == profiles.end()) {\n\t\treturn {};\n\t} else {\n\t\treturn foundProfile->second;\n\t}\n}\n\nstd::optional<OBSProfile> OBSBasic::GetProfileByDirectoryName(const std::string &directoryName) const\n{\n\tfor (auto &[iterator, profile] : profiles) {\n\t\tif (profile.directoryName == directoryName) {\n\t\t\treturn profile;\n\t\t}\n\t}\n\n\treturn {};\n}\n\n// MARK: - Qt Slot Functions\n\nvoid OBSBasic::on_actionNewProfile_triggered()\n{\n\tbool useProfileWizard = config_get_bool(App()->GetUserConfig(), \"Basic\", \"ConfigOnNewProfile\");\n\n\tconst OBSPromptCallback profilePromptCallback = [this](const OBSPromptResult &result) {\n\t\tif (GetProfileByName(result.promptValue)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tconst OBSPromptRequest request{Str(\"AddProfile.Title\"),          Str(\"AddProfile.Text\"), \"\", true,\n\t\t\t\t       Str(\"AddProfile.WizardCheckbox\"), useProfileWizard};\n\n\tOBSPromptResult result = PromptForName(request, profilePromptCallback);\n\n\tif (!result.success) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tSetupNewProfile(result.promptValue, result.optionValue);\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t}\n}\n\nvoid OBSBasic::on_actionDupProfile_triggered()\n{\n\tconst OBSPromptCallback profilePromptCallback = [this](const OBSPromptResult &result) {\n\t\tif (GetProfileByName(result.promptValue)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tconst OBSPromptRequest request{Str(\"AddProfile.Title\"), Str(\"AddProfile.Text\")};\n\n\tOBSPromptResult result = PromptForName(request, profilePromptCallback);\n\n\tif (!result.success) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tSetupDuplicateProfile(result.promptValue);\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t}\n}\n\nvoid OBSBasic::on_actionRenameProfile_triggered()\n{\n\tconst std::string currentProfileName = config_get_string(App()->GetUserConfig(), \"Basic\", \"Profile\");\n\n\tconst OBSPromptCallback profilePromptCallback = [this](const OBSPromptResult &result) {\n\t\tif (GetProfileByName(result.promptValue)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tconst OBSPromptRequest request{Str(\"RenameProfile.Title\"), Str(\"AddProfile.Text\"), currentProfileName};\n\n\tOBSPromptResult result = PromptForName(request, profilePromptCallback);\n\n\tif (!result.success) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tSetupRenameProfile(result.promptValue);\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t}\n}\n\nvoid OBSBasic::on_actionRemoveProfile_triggered(bool skipConfirmation)\n{\n\tif (profiles.size() < 2) {\n\t\treturn;\n\t}\n\n\tOBSProfile currentProfile;\n\n\ttry {\n\t\tcurrentProfile = GetCurrentProfile();\n\n\t\tif (!skipConfirmation) {\n\t\t\tconst QString confirmationText =\n\t\t\t\tQTStr(\"ConfirmRemove.Text\").arg(QString::fromStdString(currentProfile.name));\n\n\t\t\tconst QMessageBox::StandardButton button =\n\t\t\t\tOBSMessageBox::question(this, QTStr(\"ConfirmRemove.Title\"), confirmationText);\n\n\t\t\tif (button == QMessageBox::No) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tOnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGING);\n\n\t\tprofiles.erase(currentProfile.name);\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t}\n\n\tconst OBSProfile &newProfile = profiles.begin()->second;\n\n\tActivateProfile(newProfile, true);\n\tRemoveProfile(currentProfile);\n\n#ifdef YOUTUBE_ENABLED\n\tif (YouTubeAppDock::IsYTServiceSelected() && !youtubeAppDock)\n\t\tNewYouTubeAppDock();\n#endif\n\n\tblog(LOG_INFO, \"Switched to profile '%s' (%s)\", newProfile.name.c_str(), newProfile.directoryName.c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n}\n\nvoid OBSBasic::on_actionImportProfile_triggered()\n{\n\tconst QString home = QDir::homePath();\n\n\tconst QString sourceDirectory = SelectDirectory(this, QTStr(\"Basic.MainMenu.Profile.Import\"), home);\n\n\tif (!sourceDirectory.isEmpty() && !sourceDirectory.isNull()) {\n\t\tconst std::filesystem::path sourcePath = std::filesystem::u8path(sourceDirectory.toStdString());\n\t\tconst std::string directoryName = sourcePath.filename().u8string();\n\n\t\tif (auto profile = GetProfileByDirectoryName(directoryName)) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"Basic.MainMenu.Profile.Import\"),\n\t\t\t\t\t       QTStr(\"Basic.MainMenu.Profile.Exists\"));\n\t\t\treturn;\n\t\t}\n\n\t\tstd::string destinationPathString;\n\t\tdestinationPathString.reserve(App()->userProfilesLocation.u8string().size() + OBSProfilePath.size() +\n\t\t\t\t\t      directoryName.size());\n\t\tdestinationPathString.append(App()->userProfilesLocation.u8string())\n\t\t\t.append(OBSProfilePath)\n\t\t\t.append(directoryName);\n\n\t\tconst std::filesystem::path destinationPath = std::filesystem::u8path(destinationPathString);\n\n\t\ttry {\n\t\t\tstd::filesystem::create_directory(destinationPath);\n\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\tblog(LOG_WARNING, \"Failed to create profile directory '%s':\\n%s\", directoryName.c_str(),\n\t\t\t     error.what());\n\t\t\treturn;\n\t\t}\n\n\t\tconst std::array<std::pair<std::string, bool>, 4> profileFiles{{\n\t\t\t{\"basic.ini\", true},\n\t\t\t{\"service.json\", false},\n\t\t\t{\"streamEncoder.json\", false},\n\t\t\t{\"recordEncoder.json\", false},\n\t\t}};\n\n\t\tfor (auto &[file, isMandatory] : profileFiles) {\n\t\t\tconst std::filesystem::path sourceFile = sourcePath / std::filesystem::u8path(file);\n\n\t\t\tif (!std::filesystem::exists(sourceFile)) {\n\t\t\t\tif (isMandatory) {\n\t\t\t\t\tblog(LOG_ERROR,\n\t\t\t\t\t     \"Failed to import profile from directory '%s' - necessary file '%s' not found\",\n\t\t\t\t\t     directoryName.c_str(), file.c_str());\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst std::filesystem::path destinationFile = destinationPath / std::filesystem::u8path(file);\n\n\t\t\ttry {\n\t\t\t\tstd::filesystem::copy(sourceFile, destinationFile);\n\t\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\t\tblog(LOG_WARNING, \"Failed to copy import file '%s' for profile '%s':\\n%s\", file.c_str(),\n\t\t\t\t     directoryName.c_str(), error.what());\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tRefreshProfiles(true);\n\t}\n}\n\nvoid OBSBasic::on_actionExportProfile_triggered()\n{\n\tconst OBSProfile &currentProfile = GetCurrentProfile();\n\n\tconst QString home = QDir::homePath();\n\n\tconst QString destinationDirectory = SelectDirectory(this, QTStr(\"Basic.MainMenu.Profile.Export\"), home);\n\n\tconst std::array<std::string, 4> profileFiles{\"basic.ini\", \"service.json\", \"streamEncoder.json\",\n\t\t\t\t\t\t      \"recordEncoder.json\"};\n\n\tif (!destinationDirectory.isEmpty() && !destinationDirectory.isNull()) {\n\t\tconst std::filesystem::path sourcePath = currentProfile.path;\n\t\tconst std::filesystem::path destinationPath =\n\t\t\tstd::filesystem::u8path(destinationDirectory.toStdString()) /\n\t\t\tstd::filesystem::u8path(currentProfile.directoryName);\n\n\t\tif (!std::filesystem::exists(destinationPath)) {\n\t\t\tstd::filesystem::create_directory(destinationPath);\n\t\t}\n\n\t\tstd::filesystem::copy_options copyOptions = std::filesystem::copy_options::overwrite_existing;\n\n\t\tfor (auto &file : profileFiles) {\n\t\t\tconst std::filesystem::path sourceFile = sourcePath / std::filesystem::u8path(file);\n\n\t\t\tif (!std::filesystem::exists(sourceFile)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst std::filesystem::path destinationFile = destinationPath / std::filesystem::u8path(file);\n\n\t\t\ttry {\n\t\t\t\tstd::filesystem::copy(sourceFile, destinationFile, copyOptions);\n\t\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\t\tblog(LOG_WARNING, \"Failed to copy export file '%s' for profile '%s'\\n%s\", file.c_str(),\n\t\t\t\t     currentProfile.name.c_str(), error.what());\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n}\n\n// MARK: - Profile Management Helper Functions\n\nvoid OBSBasic::ActivateProfile(const OBSProfile &profile, bool reset)\n{\n\tConfigFile config;\n\tif (config.Open(profile.profileFile.u8string().c_str(), CONFIG_OPEN_ALWAYS) != CONFIG_SUCCESS) {\n\t\tthrow std::logic_error(\"failed to open configuration file of new profile: \" +\n\t\t\t\t       profile.profileFile.string());\n\t}\n\n\tconfig_set_string(config, \"General\", \"Name\", profile.name.c_str());\n\tconfig.SaveSafe(\"tmp\");\n\n\tstd::vector<std::string> restartRequirements;\n\n\tif (activeConfiguration) {\n\t\tAuth::Save();\n\n\t\tif (reset) {\n\t\t\tauth.reset();\n\t\t\tDestroyPanelCookieManager();\n#ifdef YOUTUBE_ENABLED\n\t\t\tif (youtubeAppDock) {\n\t\t\t\tDeleteYouTubeAppDock();\n\t\t\t}\n#endif\n\t\t}\n\t\trestartRequirements = GetRestartRequirements(config);\n\n\t\tactiveConfiguration.SaveSafe(\"tmp\");\n\t}\n\n\tactiveConfiguration.Swap(config);\n\n\tconfig_set_string(App()->GetUserConfig(), \"Basic\", \"Profile\", profile.name.c_str());\n\tconfig_set_string(App()->GetUserConfig(), \"Basic\", \"ProfileDir\", profile.directoryName.c_str());\n\n\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n\n\tInitBasicConfigDefaults();\n\n\tif (reset) {\n\t\tUpdateProfileEncoders();\n\t\tResetProfileData();\n\t}\n\n\tRefreshProfiles();\n\n\tUpdateTitleBar();\n\temit profileSettingChanged(\"Audio\", \"MeterDecayRate\");\n\n\tAuth::Load();\n#ifdef YOUTUBE_ENABLED\n\tif (YouTubeAppDock::IsYTServiceSelected() && !youtubeAppDock)\n\t\tNewYouTubeAppDock();\n#endif\n\n\tOnEvent(OBS_FRONTEND_EVENT_PROFILE_CHANGED);\n\n\tif (!restartRequirements.empty()) {\n\t\tstd::string requirements = std::accumulate(\n\t\t\tstd::next(restartRequirements.begin()), restartRequirements.end(), restartRequirements[0],\n\t\t\t[](std::string a, std::string b) { return std::move(a) + \"\\n\" + b; });\n\n\t\tQMessageBox::StandardButton button = OBSMessageBox::question(\n\t\t\tthis, QTStr(\"Restart\"), QTStr(\"LoadProfileNeedsRestart\").arg(requirements.c_str()));\n\n\t\tif (button == QMessageBox::Yes) {\n\t\t\trestart = true;\n\t\t\tclose();\n\t\t}\n\t}\n}\n\nvoid OBSBasic::UpdateProfileEncoders()\n{\n\tInitBasicConfigDefaults2();\n\tCheckForSimpleModeX264Fallback();\n}\n\nvoid OBSBasic::ResetProfileData()\n{\n\tResetVideo();\n\tservice = nullptr;\n\tInitService();\n\tResetOutputs();\n\tClearHotkeys();\n\tCreateHotkeys();\n\n\t/* load audio monitoring */\n\tif (obs_audio_monitoring_available()) {\n\t\tconst char *device_name = config_get_string(activeConfiguration, \"Audio\", \"MonitoringDeviceName\");\n\t\tconst char *device_id = config_get_string(activeConfiguration, \"Audio\", \"MonitoringDeviceId\");\n\n\t\tobs_set_audio_monitoring_device(device_name, device_id);\n\n\t\tblog(LOG_INFO, \"Audio monitoring device:\\n\\tname: %s\\n\\tid: %s\", device_name, device_id);\n\t}\n}\n\nstd::vector<std::string> OBSBasic::GetRestartRequirements(const ConfigFile &config) const\n{\n\tstd::vector<std::string> result;\n\n\tconst char *oldSpeakers = config_get_string(activeConfiguration, \"Audio\", \"ChannelSetup\");\n\tconst char *newSpeakers = config_get_string(config, \"Audio\", \"ChannelSetup\");\n\n\tuint64_t oldSampleRate = config_get_uint(activeConfiguration, \"Audio\", \"SampleRate\");\n\tuint64_t newSampleRate = config_get_uint(config, \"Audio\", \"SampleRate\");\n\n\tif (oldSpeakers != NULL && newSpeakers != NULL) {\n\t\tif (std::string_view{oldSpeakers} != std::string_view{newSpeakers}) {\n\t\t\tresult.emplace_back(Str(\"Basic.Settings.Audio.Channels\"));\n\t\t}\n\t}\n\n\tif (oldSampleRate != 0 && newSampleRate != 0) {\n\t\tif (oldSampleRate != newSampleRate) {\n\t\t\tresult.emplace_back(Str(\"Basic.Settings.Audio.SampleRate\"));\n\t\t}\n\t}\n\n\treturn result;\n}\n\nvoid OBSBasic::CheckForSimpleModeX264Fallback()\n{\n\tconst char *curStreamEncoder = config_get_string(activeConfiguration, \"SimpleOutput\", \"StreamEncoder\");\n\tconst char *curRecEncoder = config_get_string(activeConfiguration, \"SimpleOutput\", \"RecEncoder\");\n\tbool qsv_supported = false;\n\tbool qsv_av1_supported = false;\n\tbool amd_supported = false;\n\tbool nve_supported = false;\n#ifdef ENABLE_HEVC\n\tbool amd_hevc_supported = false;\n\tbool nve_hevc_supported = false;\n\tbool apple_hevc_supported = false;\n#endif\n\tbool amd_av1_supported = false;\n\tbool apple_supported = false;\n\tbool changed = false;\n\tsize_t idx = 0;\n\tconst char *id;\n\n\twhile (obs_enum_encoder_types(idx++, &id)) {\n\t\tif (strcmp(id, \"h264_texture_amf\") == 0)\n\t\t\tamd_supported = true;\n\t\telse if (strcmp(id, \"obs_qsv11\") == 0)\n\t\t\tqsv_supported = true;\n\t\telse if (strcmp(id, \"obs_qsv11_av1\") == 0)\n\t\t\tqsv_av1_supported = true;\n\t\telse if (strcmp(id, \"ffmpeg_nvenc\") == 0)\n\t\t\tnve_supported = true;\n#ifdef ENABLE_HEVC\n\t\telse if (strcmp(id, \"h265_texture_amf\") == 0)\n\t\t\tamd_hevc_supported = true;\n\t\telse if (strcmp(id, \"ffmpeg_hevc_nvenc\") == 0)\n\t\t\tnve_hevc_supported = true;\n#endif\n\t\telse if (strcmp(id, \"av1_texture_amf\") == 0)\n\t\t\tamd_av1_supported = true;\n\t\telse if (strcmp(id, \"com.apple.videotoolbox.videoencoder.ave.avc\") == 0)\n\t\t\tapple_supported = true;\n#ifdef ENABLE_HEVC\n\t\telse if (strcmp(id, \"com.apple.videotoolbox.videoencoder.ave.hevc\") == 0)\n\t\t\tapple_hevc_supported = true;\n#endif\n\t}\n\n\tauto CheckEncoder = [&](const char *&name) {\n\t\tif (strcmp(name, SIMPLE_ENCODER_QSV) == 0) {\n\t\t\tif (!qsv_supported) {\n\t\t\t\tchanged = true;\n\t\t\t\tname = SIMPLE_ENCODER_X264;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (strcmp(name, SIMPLE_ENCODER_QSV_AV1) == 0) {\n\t\t\tif (!qsv_av1_supported) {\n\t\t\t\tchanged = true;\n\t\t\t\tname = SIMPLE_ENCODER_X264;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (strcmp(name, SIMPLE_ENCODER_NVENC) == 0) {\n\t\t\tif (!nve_supported) {\n\t\t\t\tchanged = true;\n\t\t\t\tname = SIMPLE_ENCODER_X264;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (strcmp(name, SIMPLE_ENCODER_NVENC_AV1) == 0) {\n\t\t\tif (!nve_supported) {\n\t\t\t\tchanged = true;\n\t\t\t\tname = SIMPLE_ENCODER_X264;\n\t\t\t\treturn false;\n\t\t\t}\n#ifdef ENABLE_HEVC\n\t\t} else if (strcmp(name, SIMPLE_ENCODER_AMD_HEVC) == 0) {\n\t\t\tif (!amd_hevc_supported) {\n\t\t\t\tchanged = true;\n\t\t\t\tname = SIMPLE_ENCODER_X264;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (strcmp(name, SIMPLE_ENCODER_NVENC_HEVC) == 0) {\n\t\t\tif (!nve_hevc_supported) {\n\t\t\t\tchanged = true;\n\t\t\t\tname = SIMPLE_ENCODER_X264;\n\t\t\t\treturn false;\n\t\t\t}\n#endif\n\t\t} else if (strcmp(name, SIMPLE_ENCODER_AMD) == 0) {\n\t\t\tif (!amd_supported) {\n\t\t\t\tchanged = true;\n\t\t\t\tname = SIMPLE_ENCODER_X264;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (strcmp(name, SIMPLE_ENCODER_AMD_AV1) == 0) {\n\t\t\tif (!amd_av1_supported) {\n\t\t\t\tchanged = true;\n\t\t\t\tname = SIMPLE_ENCODER_X264;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else if (strcmp(name, SIMPLE_ENCODER_APPLE_H264) == 0) {\n\t\t\tif (!apple_supported) {\n\t\t\t\tchanged = true;\n\t\t\t\tname = SIMPLE_ENCODER_X264;\n\t\t\t\treturn false;\n\t\t\t}\n#ifdef ENABLE_HEVC\n\t\t} else if (strcmp(name, SIMPLE_ENCODER_APPLE_HEVC) == 0) {\n\t\t\tif (!apple_hevc_supported) {\n\t\t\t\tchanged = true;\n\t\t\t\tname = SIMPLE_ENCODER_X264;\n\t\t\t\treturn false;\n\t\t\t}\n#endif\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tif (!CheckEncoder(curStreamEncoder))\n\t\tconfig_set_string(activeConfiguration, \"SimpleOutput\", \"StreamEncoder\", curStreamEncoder);\n\tif (!CheckEncoder(curRecEncoder))\n\t\tconfig_set_string(activeConfiguration, \"SimpleOutput\", \"RecEncoder\", curRecEncoder);\n\tif (changed) {\n\t\tactiveConfiguration.SaveSafe(\"tmp\");\n\t}\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Projectors.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n#include \"OBSProjector.hpp\"\n\nobs_data_array_t *OBSBasic::SaveProjectors()\n{\n\tobs_data_array_t *savedProjectors = obs_data_array_create();\n\n\tauto saveProjector = [savedProjectors](OBSProjector *projector) {\n\t\tif (!projector)\n\t\t\treturn;\n\n\t\tOBSDataAutoRelease data = obs_data_create();\n\t\tProjectorType type = projector->GetProjectorType();\n\n\t\tswitch (type) {\n\t\tcase ProjectorType::Scene:\n\t\tcase ProjectorType::Source: {\n\t\t\tOBSSource source = projector->GetSource();\n\t\t\tconst char *name = obs_source_get_name(source);\n\t\t\tobs_data_set_string(data, \"name\", name);\n\t\t\tbreak;\n\t\t}\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\tobs_data_set_int(data, \"monitor\", projector->GetMonitor());\n\t\tobs_data_set_int(data, \"type\", static_cast<int>(type));\n\t\tobs_data_set_string(data, \"geometry\", projector->saveGeometry().toBase64().constData());\n\n\t\tif (projector->IsAlwaysOnTopOverridden())\n\t\t\tobs_data_set_bool(data, \"alwaysOnTop\", projector->IsAlwaysOnTop());\n\n\t\tobs_data_set_bool(data, \"alwaysOnTopOverridden\", projector->IsAlwaysOnTopOverridden());\n\n\t\tobs_data_array_push_back(savedProjectors, data);\n\t};\n\n\tfor (size_t i = 0; i < projectors.size(); i++)\n\t\tsaveProjector(static_cast<OBSProjector *>(projectors[i]));\n\n\treturn savedProjectors;\n}\n\nvoid OBSBasic::LoadSavedProjectors(obs_data_array_t *array)\n{\n\tsize_t num = obs_data_array_count(array);\n\n\tfor (size_t i = 0; i < num; i++) {\n\t\tOBSDataAutoRelease data = obs_data_array_item(array, i);\n\t\tSavedProjectorInfo info = {};\n\n\t\tinfo.monitor = obs_data_get_int(data, \"monitor\");\n\t\tinfo.type = static_cast<ProjectorType>(obs_data_get_int(data, \"type\"));\n\t\tinfo.geometry = std::string(obs_data_get_string(data, \"geometry\"));\n\t\tinfo.name = std::string(obs_data_get_string(data, \"name\"));\n\t\tinfo.alwaysOnTop = obs_data_get_bool(data, \"alwaysOnTop\");\n\t\tinfo.alwaysOnTopOverridden = obs_data_get_bool(data, \"alwaysOnTopOverridden\");\n\n\t\tOpenSavedProjector(&info);\n\t}\n}\n\nvoid OBSBasic::updateMultiviewProjectorMenu()\n{\n\tui->multiviewProjectorMenu->clear();\n\tAddProjectorMenuMonitors(ui->multiviewProjectorMenu, this, &OBSBasic::OpenMultiviewProjector);\n\tui->multiviewProjectorMenu->addSeparator();\n\tui->multiviewProjectorMenu->addAction(QTStr(\"Projector.Window\"), this, &OBSBasic::openMultiviewWindow);\n}\n\nvoid OBSBasic::ClearProjectors()\n{\n\tfor (size_t i = 0; i < projectors.size(); i++) {\n\t\tif (projectors[i])\n\t\t\tdelete projectors[i];\n\t}\n\n\tprojectors.clear();\n}\n\nQList<QString> OBSBasic::GetProjectorMenuMonitorsFormatted()\n{\n\tQList<QString> projectorsFormatted;\n\tQList<QScreen *> screens = QGuiApplication::screens();\n\tfor (int i = 0; i < screens.size(); i++) {\n\t\tQScreen *screen = screens[i];\n\t\tQRect screenGeometry = screen->geometry();\n\t\tqreal screenPixelRatio = screen->devicePixelRatio();\n\t\tQString name = \"\";\n#if defined(__APPLE__) || defined(_WIN32)\n\t\tname = screen->name();\n#else\n\t\tname = screen->model().simplified();\n\n\t\tif (name.length() > 1 && name.endsWith(\"-\"))\n\t\t\tname.chop(1);\n#endif\n\t\tname = name.simplified();\n\n\t\tif (name.length() == 0) {\n\t\t\tname = QString(\"%1 %2\").arg(QTStr(\"Display\")).arg(QString::number(i + 1));\n\t\t}\n\n\t\tint screenPixelWidth = std::round((screenGeometry.width() * screenPixelRatio) * 0.5f) * 2;\n\t\tint screenPixelHeight = std::round((screenGeometry.height() * screenPixelRatio) * 0.5f) * 2;\n\n\t\tQString str = QString(\"%1: %2x%3 @ %4,%5\")\n\t\t\t\t      .arg(name, QString::number(screenPixelWidth), QString::number(screenPixelHeight),\n\t\t\t\t\t   QString::number(screenGeometry.x()), QString::number(screenGeometry.y()));\n\t\tprojectorsFormatted.push_back(str);\n\t}\n\treturn projectorsFormatted;\n}\n\nvoid OBSBasic::DeleteProjector(OBSProjector *projector)\n{\n\tfor (size_t i = 0; i < projectors.size(); i++) {\n\t\tif (projectors[i] == projector) {\n\t\t\tprojectors[i]->deleteLater();\n\t\t\tprojectors.erase(projectors.begin() + i);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nOBSProjector *OBSBasic::OpenProjector(obs_source_t *source, int monitor, ProjectorType type)\n{\n\t/* seriously?  10 monitors? */\n\tif (monitor > 9 || monitor > QGuiApplication::screens().size() - 1)\n\t\treturn nullptr;\n\n\tbool closeProjectors = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"CloseExistingProjectors\");\n\n\tif (closeProjectors && monitor > -1) {\n\t\tfor (size_t i = projectors.size(); i > 0; i--) {\n\t\t\tsize_t idx = i - 1;\n\t\t\tif (projectors[idx]->GetMonitor() == monitor)\n\t\t\t\tDeleteProjector(projectors[idx]);\n\t\t}\n\t}\n\n\tOBSProjector *projector = new OBSProjector(nullptr, source, monitor, type);\n\n\tprojectors.emplace_back(projector);\n\n\treturn projector;\n}\n\nvoid OBSBasic::OpenPreviewProjector()\n{\n\tint monitor = sender()->property(\"monitor\").toInt();\n\tOpenProjector(nullptr, monitor, ProjectorType::Preview);\n}\n\nvoid OBSBasic::OpenSourceProjector()\n{\n\tint monitor = sender()->property(\"monitor\").toInt();\n\tOBSSceneItem item = GetCurrentSceneItem();\n\tif (!item)\n\t\treturn;\n\n\tOpenProjector(obs_sceneitem_get_source(item), monitor, ProjectorType::Source);\n}\n\nvoid OBSBasic::OpenMultiviewProjector()\n{\n\tint monitor = sender()->property(\"monitor\").toInt();\n\tOpenProjector(nullptr, monitor, ProjectorType::Multiview);\n}\n\nvoid OBSBasic::OpenSceneProjector()\n{\n\tint monitor = sender()->property(\"monitor\").toInt();\n\tOBSScene scene = GetCurrentScene();\n\tif (!scene)\n\t\treturn;\n\n\tOpenProjector(obs_scene_get_source(scene), monitor, ProjectorType::Scene);\n}\n\nvoid OBSBasic::OpenPreviewWindow()\n{\n\tOpenProjector(nullptr, -1, ProjectorType::Preview);\n}\n\nvoid OBSBasic::OpenSourceWindow()\n{\n\tOBSSceneItem item = GetCurrentSceneItem();\n\tif (!item)\n\t\treturn;\n\n\tOBSSource source = obs_sceneitem_get_source(item);\n\n\tOpenProjector(obs_sceneitem_get_source(item), -1, ProjectorType::Source);\n}\n\nvoid OBSBasic::OpenSceneWindow()\n{\n\tOBSScene scene = GetCurrentScene();\n\tif (!scene)\n\t\treturn;\n\n\tOBSSource source = obs_scene_get_source(scene);\n\n\tOpenProjector(obs_scene_get_source(scene), -1, ProjectorType::Scene);\n}\n\nvoid OBSBasic::OpenSavedProjector(SavedProjectorInfo *info)\n{\n\tif (info) {\n\t\tOBSProjector *projector = nullptr;\n\t\tswitch (info->type) {\n\t\tcase ProjectorType::Source:\n\t\tcase ProjectorType::Scene: {\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_name(info->name.c_str());\n\t\t\tif (!source)\n\t\t\t\treturn;\n\n\t\t\tprojector = OpenProjector(source, info->monitor, info->type);\n\t\t\tbreak;\n\t\t}\n\t\tdefault: {\n\t\t\tprojector = OpenProjector(nullptr, info->monitor, info->type);\n\t\t\tbreak;\n\t\t}\n\t\t}\n\n\t\tif (projector && !info->geometry.empty() && info->monitor < 0) {\n\t\t\tQByteArray byteArray = QByteArray::fromBase64(QByteArray(info->geometry.c_str()));\n\t\t\tprojector->restoreGeometry(byteArray);\n\n\t\t\tif (!WindowPositionValid(projector->normalGeometry())) {\n\t\t\t\tQRect rect = QGuiApplication::primaryScreen()->geometry();\n\t\t\t\tprojector->setGeometry(\n\t\t\t\t\tQStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, size(), rect));\n\t\t\t}\n\n\t\t\tif (info->alwaysOnTopOverridden)\n\t\t\t\tprojector->SetIsAlwaysOnTop(info->alwaysOnTop, true);\n\t\t}\n\t}\n}\n\nvoid OBSBasic::openMultiviewWindow()\n{\n\tOpenProjector(nullptr, -1, ProjectorType::Multiview);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Recording.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <components/UIValidation.hpp>\n#include <dialogs/OBSRemux.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QDesktopServices>\n#include <QFileInfo>\n\nvoid OBSBasic::on_actionShow_Recordings_triggered()\n{\n\tconst char *mode = config_get_string(activeConfiguration, \"Output\", \"Mode\");\n\tconst char *type = config_get_string(activeConfiguration, \"AdvOut\", \"RecType\");\n\tconst char *adv_path = strcmp(type, \"Standard\")\n\t\t\t\t       ? config_get_string(activeConfiguration, \"AdvOut\", \"FFFilePath\")\n\t\t\t\t       : config_get_string(activeConfiguration, \"AdvOut\", \"RecFilePath\");\n\tconst char *path = strcmp(mode, \"Advanced\") ? config_get_string(activeConfiguration, \"SimpleOutput\", \"FilePath\")\n\t\t\t\t\t\t    : adv_path;\n\tQDesktopServices::openUrl(QUrl::fromLocalFile(path));\n}\n\n#define RECORDING_START \"==== Recording Start ===============================================\"\n#define RECORDING_STOP \"==== Recording Stop ================================================\"\n\nextern volatile bool replaybuf_active;\n\nvoid OBSBasic::AutoRemux(QString input, bool no_show)\n{\n\tauto config = Config();\n\n\tbool autoRemux = config_get_bool(config, \"Video\", \"AutoRemux\");\n\n\tif (!autoRemux)\n\t\treturn;\n\n\tbool isSimpleMode = false;\n\n\tconst char *mode = config_get_string(config, \"Output\", \"Mode\");\n\tif (!mode) {\n\t\tisSimpleMode = true;\n\t} else {\n\t\tisSimpleMode = strcmp(mode, \"Simple\") == 0;\n\t}\n\n\tif (!isSimpleMode) {\n\t\tconst char *recType = config_get_string(config, \"AdvOut\", \"RecType\");\n\n\t\tbool ffmpegOutput = astrcmpi(recType, \"FFmpeg\") == 0;\n\n\t\tif (ffmpegOutput)\n\t\t\treturn;\n\t}\n\n\tif (input.isEmpty())\n\t\treturn;\n\n\tQFileInfo fi(input);\n\tQString suffix = fi.suffix();\n\n\t/* do not remux if lossless */\n\tif (suffix.compare(\"avi\", Qt::CaseInsensitive) == 0) {\n\t\treturn;\n\t}\n\n\tQString path = fi.path();\n\n\tQString output = input;\n\toutput.resize(output.size() - suffix.size());\n\n\tconst obs_encoder_t *videoEncoder = obs_output_get_video_encoder(outputHandler->fileOutput);\n\tconst char *vCodecName = obs_encoder_get_codec(videoEncoder);\n\tconst char *format = config_get_string(config, isSimpleMode ? \"SimpleOutput\" : \"AdvOut\", \"RecFormat2\");\n\n\t/* Retain original container for fMP4/fMOV */\n\tif (strncmp(format, \"fragmented\", 10) == 0) {\n\t\toutput += \"remuxed.\" + suffix;\n\t} else if (strcmp(vCodecName, \"prores\") == 0) {\n\t\toutput += \"mov\";\n\t} else {\n\t\toutput += \"mp4\";\n\t}\n\n\tOBSRemux *remux = new OBSRemux(QT_TO_UTF8(path), this, true);\n\tif (!no_show)\n\t\tremux->show();\n\tremux->AutoRemux(input, output);\n}\n\nvoid OBSBasic::StartRecording()\n{\n\tif (outputHandler->RecordingActive())\n\t\treturn;\n\tif (disableOutputsRef)\n\t\treturn;\n\n\tif (!OutputPathValid()) {\n\t\tOutputPathInvalidMessage();\n\t\treturn;\n\t}\n\n\tif (!IsFFmpegOutputToURL() && LowDiskSpace()) {\n\t\tDiskSpaceMessage();\n\t\treturn;\n\t}\n\n\tOnEvent(OBS_FRONTEND_EVENT_RECORDING_STARTING);\n\n\tSaveProject();\n\n\toutputHandler->StartRecording();\n}\n\nvoid OBSBasic::RecordStopping()\n{\n\temit RecordingStopping();\n\n\tif (sysTrayRecord)\n\t\tsysTrayRecord->setText(QTStr(\"Basic.Main.StoppingRecording\"));\n\n\trecordingStopping = true;\n\tOnEvent(OBS_FRONTEND_EVENT_RECORDING_STOPPING);\n}\n\nvoid OBSBasic::StopRecording()\n{\n\tSaveProject();\n\n\tif (outputHandler->RecordingActive())\n\t\toutputHandler->StopRecording(recordingStopping);\n\n\tOnDeactivate();\n}\n\nvoid OBSBasic::RecordingStart()\n{\n\tui->statusbar->RecordingStarted(outputHandler->fileOutput);\n\temit RecordingStarted(isRecordingPausable);\n\n\tif (sysTrayRecord)\n\t\tsysTrayRecord->setText(QTStr(\"Basic.Main.StopRecording\"));\n\n\trecordingStopping = false;\n\tOnEvent(OBS_FRONTEND_EVENT_RECORDING_STARTED);\n\n\tif (!diskFullTimer->isActive())\n\t\tdiskFullTimer->start(1000);\n\n\tOnActivate();\n\n\tblog(LOG_INFO, RECORDING_START);\n}\n\nvoid OBSBasic::RecordingStop(int code, QString last_error)\n{\n\tui->statusbar->RecordingStopped();\n\temit RecordingStopped();\n\n\tif (sysTrayRecord)\n\t\tsysTrayRecord->setText(QTStr(\"Basic.Main.StartRecording\"));\n\n\tblog(LOG_INFO, RECORDING_STOP);\n\n\tif (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) {\n\t\tOBSMessageBox::critical(this, QTStr(\"Output.RecordFail.Title\"), QTStr(\"Output.RecordFail.Unsupported\"));\n\n\t} else if (code == OBS_OUTPUT_ENCODE_ERROR && isVisible()) {\n\t\tQString msg = last_error.isEmpty()\n\t\t\t\t      ? QTStr(\"Output.RecordError.EncodeErrorMsg\")\n\t\t\t\t      : QTStr(\"Output.RecordError.EncodeErrorMsg.LastError\").arg(last_error);\n\t\tOBSMessageBox::warning(this, QTStr(\"Output.RecordError.Title\"), msg);\n\n\t} else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) {\n\t\tOBSMessageBox::warning(this, QTStr(\"Output.RecordNoSpace.Title\"), QTStr(\"Output.RecordNoSpace.Msg\"));\n\n\t} else if (code != OBS_OUTPUT_SUCCESS && isVisible()) {\n\n\t\tconst char *errorDescription;\n\t\tDStr errorMessage;\n\t\tbool use_last_error = true;\n\n\t\terrorDescription = Str(\"Output.RecordError.Msg\");\n\n\t\tif (use_last_error && !last_error.isEmpty())\n\t\t\tdstr_printf(errorMessage, \"%s<br><br>%s\", errorDescription, QT_TO_UTF8(last_error));\n\t\telse\n\t\t\tdstr_copy(errorMessage, errorDescription);\n\n\t\tOBSMessageBox::critical(this, QTStr(\"Output.RecordError.Title\"), QT_UTF8(errorMessage));\n\n\t} else if (code == OBS_OUTPUT_UNSUPPORTED && !isVisible()) {\n\t\tSysTrayNotify(QTStr(\"Output.RecordFail.Unsupported\"), QSystemTrayIcon::Warning);\n\n\t} else if (code == OBS_OUTPUT_NO_SPACE && !isVisible()) {\n\t\tSysTrayNotify(QTStr(\"Output.RecordNoSpace.Msg\"), QSystemTrayIcon::Warning);\n\n\t} else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) {\n\t\tSysTrayNotify(QTStr(\"Output.RecordError.Msg\"), QSystemTrayIcon::Warning);\n\t} else if (code == OBS_OUTPUT_SUCCESS) {\n\t\tif (outputHandler) {\n\t\t\tstd::string path = outputHandler->lastRecordingPath;\n\t\t\tQString str = QTStr(\"Basic.StatusBar.RecordingSavedTo\");\n\t\t\tShowStatusBarMessage(str.arg(QT_UTF8(path.c_str())));\n\t\t}\n\t}\n\n\tOnEvent(OBS_FRONTEND_EVENT_RECORDING_STOPPED);\n\n\tif (diskFullTimer->isActive())\n\t\tdiskFullTimer->stop();\n\n\tAutoRemux(outputHandler->lastRecordingPath.c_str());\n\n\tOnDeactivate();\n}\n\nvoid OBSBasic::RecordingFileChanged(QString lastRecordingPath)\n{\n\tQString str = QTStr(\"Basic.StatusBar.RecordingSavedTo\");\n\tShowStatusBarMessage(str.arg(lastRecordingPath));\n\n\tAutoRemux(lastRecordingPath, true);\n}\n\nvoid OBSBasic::RecordActionTriggered()\n{\n\tif (outputHandler->RecordingActive()) {\n\t\tbool confirm = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"WarnBeforeStoppingRecord\");\n\n\t\tif (confirm && isVisible()) {\n\t\t\tQMessageBox::StandardButton button = OBSMessageBox::question(\n\t\t\t\tthis, QTStr(\"ConfirmStopRecord.Title\"), QTStr(\"ConfirmStopRecord.Text\"),\n\t\t\t\tQMessageBox::Yes | QMessageBox::No, QMessageBox::No);\n\n\t\t\tif (button == QMessageBox::No)\n\t\t\t\treturn;\n\t\t}\n\t\tStopRecording();\n\t} else {\n\t\tif (!UIValidation::NoSourcesConfirmation(this))\n\t\t\treturn;\n\n\t\tStartRecording();\n\t}\n}\n\nbool OBSBasic::RecordingActive()\n{\n\tif (!outputHandler)\n\t\treturn false;\n\treturn outputHandler->RecordingActive();\n}\n\nvoid OBSBasic::PauseRecording()\n{\n\tif (!isRecordingPausable || !outputHandler || !outputHandler->fileOutput ||\n\t    os_atomic_load_bool(&recording_paused))\n\t\treturn;\n\n\tobs_output_t *output = outputHandler->fileOutput;\n\n\tif (obs_output_pause(output, true)) {\n\t\tos_atomic_set_bool(&recording_paused, true);\n\n\t\temit RecordingPaused();\n\n\t\tui->statusbar->RecordingPaused();\n\n\t\tTaskbarOverlaySetStatus(TaskbarOverlayStatusPaused);\n\t\tif (trayIcon && trayIcon->isVisible()) {\n#ifdef __APPLE__\n\t\t\tQIcon trayIconFile = QIcon(\":/res/images/obs_paused_macos.svg\");\n\t\t\ttrayIconFile.setIsMask(true);\n#else\n\t\t\tQIcon trayIconFile = QIcon(\":/res/images/obs_paused.png\");\n#endif\n\t\t\ttrayIcon->setIcon(QIcon::fromTheme(\"obs-tray-paused\", trayIconFile));\n\t\t}\n\n\t\tOnEvent(OBS_FRONTEND_EVENT_RECORDING_PAUSED);\n\n\t\tif (os_atomic_load_bool(&replaybuf_active))\n\t\t\tShowReplayBufferPauseWarning();\n\t}\n}\n\nvoid OBSBasic::UnpauseRecording()\n{\n\tif (!isRecordingPausable || !outputHandler || !outputHandler->fileOutput ||\n\t    !os_atomic_load_bool(&recording_paused))\n\t\treturn;\n\n\tobs_output_t *output = outputHandler->fileOutput;\n\n\tif (obs_output_pause(output, false)) {\n\t\tos_atomic_set_bool(&recording_paused, false);\n\n\t\temit RecordingUnpaused();\n\n\t\tui->statusbar->RecordingUnpaused();\n\n\t\tTaskbarOverlaySetStatus(TaskbarOverlayStatusActive);\n\t\tif (trayIcon && trayIcon->isVisible()) {\n#ifdef __APPLE__\n\t\t\tQIcon trayIconFile = QIcon(\":/res/images/tray_active_macos.svg\");\n\t\t\ttrayIconFile.setIsMask(true);\n#else\n\t\t\tQIcon trayIconFile = QIcon(\":/res/images/tray_active.png\");\n#endif\n\t\t\ttrayIcon->setIcon(QIcon::fromTheme(\"obs-tray-active\", trayIconFile));\n\t\t}\n\n\t\tOnEvent(OBS_FRONTEND_EVENT_RECORDING_UNPAUSED);\n\t}\n}\n\nvoid OBSBasic::RecordPauseToggled()\n{\n\tif (!isRecordingPausable || !outputHandler || !outputHandler->fileOutput)\n\t\treturn;\n\n\tobs_output_t *output = outputHandler->fileOutput;\n\tbool enable = !obs_output_paused(output);\n\n\tif (enable)\n\t\tPauseRecording();\n\telse\n\t\tUnpauseRecording();\n}\n\nvoid OBSBasic::UpdateIsRecordingPausable()\n{\n\tconst char *mode = config_get_string(activeConfiguration, \"Output\", \"Mode\");\n\tbool adv = astrcmpi(mode, \"Advanced\") == 0;\n\tbool shared = true;\n\n\tif (adv) {\n\t\tconst char *recType = config_get_string(activeConfiguration, \"AdvOut\", \"RecType\");\n\n\t\tif (astrcmpi(recType, \"FFmpeg\") == 0) {\n\t\t\tshared = config_get_bool(activeConfiguration, \"AdvOut\", \"FFOutputToFile\");\n\t\t} else {\n\t\t\tconst char *recordEncoder = config_get_string(activeConfiguration, \"AdvOut\", \"RecEncoder\");\n\t\t\tshared = astrcmpi(recordEncoder, \"none\") == 0;\n\t\t}\n\t} else {\n\t\tconst char *quality = config_get_string(activeConfiguration, \"SimpleOutput\", \"RecQuality\");\n\t\tshared = strcmp(quality, \"Stream\") == 0;\n\t}\n\n\tisRecordingPausable = !shared;\n}\n\n#define MBYTE (1024ULL * 1024ULL)\n#define MBYTES_LEFT_STOP_REC 50ULL\n#define MAX_BYTES_LEFT (MBYTES_LEFT_STOP_REC * MBYTE)\n\nvoid OBSBasic::DiskSpaceMessage()\n{\n\tblog(LOG_ERROR, \"Recording stopped because of low disk space\");\n\n\tOBSMessageBox::critical(this, QTStr(\"Output.RecordNoSpace.Title\"), QTStr(\"Output.RecordNoSpace.Msg\"));\n}\n\nbool OBSBasic::LowDiskSpace()\n{\n\tconst char *path;\n\n\tpath = GetCurrentOutputPath();\n\tif (!path)\n\t\treturn false;\n\n\tuint64_t num_bytes = os_get_free_disk_space(path);\n\n\tif (num_bytes < (MAX_BYTES_LEFT))\n\t\treturn true;\n\telse\n\t\treturn false;\n}\n\nvoid OBSBasic::CheckDiskSpaceRemaining()\n{\n\tif (LowDiskSpace()) {\n\t\tStopRecording();\n\t\tStopReplayBuffer();\n\n\t\tDiskSpaceMessage();\n\t}\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_ReplayBuffer.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <components/UIValidation.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QCheckBox>\n\n#define REPLAY_BUFFER_START \"==== Replay Buffer Start ===========================================\"\n#define REPLAY_BUFFER_STOP \"==== Replay Buffer Stop ============================================\"\n\nvoid OBSBasic::ReplayBufferActionTriggered()\n{\n\tif (outputHandler->ReplayBufferActive())\n\t\tStopReplayBuffer();\n\telse\n\t\tStartReplayBuffer();\n};\n\nvoid OBSBasic::ShowReplayBufferPauseWarning()\n{\n\tauto msgBox = []() {\n\t\tQMessageBox msgbox(App()->GetMainWindow());\n\t\tmsgbox.setWindowTitle(QTStr(\"Output.ReplayBuffer.\"\n\t\t\t\t\t    \"PauseWarning.Title\"));\n\t\tmsgbox.setText(QTStr(\"Output.ReplayBuffer.\"\n\t\t\t\t     \"PauseWarning.Text\"));\n\t\tmsgbox.setIcon(QMessageBox::Icon::Information);\n\t\tmsgbox.addButton(QMessageBox::Ok);\n\n\t\tQCheckBox *cb = new QCheckBox(QTStr(\"DoNotShowAgain\"));\n\t\tmsgbox.setCheckBox(cb);\n\n\t\tmsgbox.exec();\n\n\t\tif (cb->isChecked()) {\n\t\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"WarnedAboutReplayBufferPausing\", true);\n\t\t\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n\t\t}\n\t};\n\n\tbool warned = config_get_bool(App()->GetUserConfig(), \"General\", \"WarnedAboutReplayBufferPausing\");\n\tif (!warned) {\n\t\tQMetaObject::invokeMethod(App(), \"Exec\", Qt::QueuedConnection, Q_ARG(VoidFunc, msgBox));\n\t}\n}\n\nvoid OBSBasic::StartReplayBuffer()\n{\n\tif (!outputHandler || !outputHandler->replayBuffer)\n\t\treturn;\n\tif (outputHandler->ReplayBufferActive())\n\t\treturn;\n\tif (disableOutputsRef)\n\t\treturn;\n\n\tif (!UIValidation::NoSourcesConfirmation(this))\n\t\treturn;\n\n\tif (!OutputPathValid()) {\n\t\tOutputPathInvalidMessage();\n\t\treturn;\n\t}\n\n\tif (LowDiskSpace()) {\n\t\tDiskSpaceMessage();\n\t\treturn;\n\t}\n\n\tOnEvent(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTING);\n\n\tSaveProject();\n\n\tif (outputHandler->StartReplayBuffer() && os_atomic_load_bool(&recording_paused)) {\n\t\tShowReplayBufferPauseWarning();\n\t}\n}\n\nvoid OBSBasic::ReplayBufferStopping()\n{\n\tif (!outputHandler || !outputHandler->replayBuffer)\n\t\treturn;\n\n\temit ReplayBufStopping();\n\n\tif (sysTrayReplayBuffer)\n\t\tsysTrayReplayBuffer->setText(QTStr(\"Basic.Main.StoppingReplayBuffer\"));\n\n\treplayBufferStopping = true;\n\tOnEvent(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPING);\n}\n\nvoid OBSBasic::StopReplayBuffer()\n{\n\tif (!outputHandler || !outputHandler->replayBuffer)\n\t\treturn;\n\n\tSaveProject();\n\n\tif (outputHandler->ReplayBufferActive())\n\t\toutputHandler->StopReplayBuffer(replayBufferStopping);\n\n\tOnDeactivate();\n}\n\nvoid OBSBasic::ReplayBufferStart()\n{\n\tif (!outputHandler || !outputHandler->replayBuffer)\n\t\treturn;\n\n\temit ReplayBufStarted();\n\n\tif (sysTrayReplayBuffer)\n\t\tsysTrayReplayBuffer->setText(QTStr(\"Basic.Main.StopReplayBuffer\"));\n\n\treplayBufferStopping = false;\n\tOnEvent(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STARTED);\n\n\tOnActivate();\n\n\tblog(LOG_INFO, REPLAY_BUFFER_START);\n}\n\nvoid OBSBasic::ReplayBufferSave()\n{\n\tif (!outputHandler || !outputHandler->replayBuffer)\n\t\treturn;\n\tif (!outputHandler->ReplayBufferActive())\n\t\treturn;\n\n\tcalldata_t cd = {0};\n\tproc_handler_t *ph = obs_output_get_proc_handler(outputHandler->replayBuffer);\n\tproc_handler_call(ph, \"save\", &cd);\n\tcalldata_free(&cd);\n}\n\nvoid OBSBasic::ReplayBufferSaved()\n{\n\tif (!outputHandler || !outputHandler->replayBuffer)\n\t\treturn;\n\tif (!outputHandler->ReplayBufferActive())\n\t\treturn;\n\n\tcalldata_t cd = {0};\n\tproc_handler_t *ph = obs_output_get_proc_handler(outputHandler->replayBuffer);\n\tproc_handler_call(ph, \"get_last_replay\", &cd);\n\tstd::string path = calldata_string(&cd, \"path\");\n\tQString msg = QTStr(\"Basic.StatusBar.ReplayBufferSavedTo\").arg(QT_UTF8(path.c_str()));\n\tShowStatusBarMessage(msg);\n\tlastReplay = path;\n\tcalldata_free(&cd);\n\n\tOnEvent(OBS_FRONTEND_EVENT_REPLAY_BUFFER_SAVED);\n\n\tAutoRemux(QT_UTF8(path.c_str()));\n}\n\nvoid OBSBasic::ReplayBufferStop(int code)\n{\n\tif (!outputHandler || !outputHandler->replayBuffer)\n\t\treturn;\n\n\temit ReplayBufStopped();\n\n\tif (sysTrayReplayBuffer)\n\t\tsysTrayReplayBuffer->setText(QTStr(\"Basic.Main.StartReplayBuffer\"));\n\n\tblog(LOG_INFO, REPLAY_BUFFER_STOP);\n\n\tif (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) {\n\t\tOBSMessageBox::critical(this, QTStr(\"Output.RecordFail.Title\"), QTStr(\"Output.RecordFail.Unsupported\"));\n\n\t} else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) {\n\t\tOBSMessageBox::warning(this, QTStr(\"Output.RecordNoSpace.Title\"), QTStr(\"Output.RecordNoSpace.Msg\"));\n\n\t} else if (code != OBS_OUTPUT_SUCCESS && isVisible()) {\n\t\tOBSMessageBox::critical(this, QTStr(\"Output.RecordError.Title\"), QTStr(\"Output.RecordError.Msg\"));\n\n\t} else if (code == OBS_OUTPUT_UNSUPPORTED && !isVisible()) {\n\t\tSysTrayNotify(QTStr(\"Output.RecordFail.Unsupported\"), QSystemTrayIcon::Warning);\n\n\t} else if (code == OBS_OUTPUT_NO_SPACE && !isVisible()) {\n\t\tSysTrayNotify(QTStr(\"Output.RecordNoSpace.Msg\"), QSystemTrayIcon::Warning);\n\n\t} else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) {\n\t\tSysTrayNotify(QTStr(\"Output.RecordError.Msg\"), QSystemTrayIcon::Warning);\n\t}\n\n\tOnEvent(OBS_FRONTEND_EVENT_REPLAY_BUFFER_STOPPED);\n\n\tOnDeactivate();\n}\n\nbool OBSBasic::ReplayBufferActive()\n{\n\tif (!outputHandler)\n\t\treturn false;\n\treturn outputHandler->ReplayBufferActive();\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_SceneCollections.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <dialogs/OBSMissingFiles.hpp>\n#include <importer/OBSImporter.hpp>\n#include <models/SceneCollection.hpp>\n#include <utility/item-widget-helpers.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QDir>\n\n#include <filesystem>\n#include <string>\n#include <vector>\n\nextern bool safe_mode;\nextern bool opt_start_streaming;\nextern bool opt_start_recording;\nextern bool opt_start_virtualcam;\nextern bool opt_start_replaybuffer;\nextern std::string opt_starting_scene;\n\nusing SceneCoordinateMode = OBS::SceneCoordinateMode;\nusing SceneCollection = OBS::SceneCollection;\n\n// MARK: Constant Expressions\n\nstatic constexpr std::string_view SceneCollectionPath = \"/obs-studio/basic/scenes/\";\n\nnamespace DataKeys {\nstatic constexpr std::string_view AbsoluteCoordinates = \"AbsoluteCoordinates\";\nstatic constexpr std::string_view MigrationResolution = \"migration_resolution\";\n} // namespace DataKeys\n\nnamespace L10N {\nstatic constexpr std::string_view Migrate = \"Basic.MainMenu.SceneCollection.Migrate\";\nstatic constexpr std::string_view Remigrate = \"Basic.MainMenu.SceneCollection.Remigrate\";\n} // namespace L10N\n\n// MARK: - Anonymous Namespace\nnamespace {\nQList<QString> sortedSceneCollections{};\n\nvoid updateSortedSceneCollections(const OBSSceneCollectionCache &collections)\n{\n\tconst QLocale locale = QLocale::system();\n\tQList<QString> newList{};\n\n\tfor (auto [collectionName, _] : collections) {\n\t\tQString entry = QString::fromStdString(collectionName);\n\t\tnewList.append(entry);\n\t}\n\n\tstd::sort(newList.begin(), newList.end(), [&locale](const QString &lhs, const QString &rhs) -> bool {\n\t\tint result = QString::localeAwareCompare(locale.toLower(lhs), locale.toLower(rhs));\n\n\t\treturn (result < 0);\n\t});\n\n\tsortedSceneCollections.swap(newList);\n}\n\nvoid cleanBackupCollision(const SceneCollection &collection)\n{\n\tstd::filesystem::path backupFilePath = collection.getFilePath();\n\tbackupFilePath.replace_extension(\".json.bak\");\n\n\tif (std::filesystem::exists(backupFilePath)) {\n\t\ttry {\n\t\t\tstd::filesystem::remove(backupFilePath);\n\t\t} catch (std::filesystem::filesystem_error &) {\n\t\t\tthrow std::logic_error(\"Failed to remove pre-existing scene collection backup file: \" +\n\t\t\t\t\t       backupFilePath.u8string());\n\t\t}\n\t}\n}\n\nvoid updateRemigrationMenuItem(SceneCoordinateMode mode, QAction *menuItem)\n{\n\tbool isAbsoluteCoordinateMode = mode == SceneCoordinateMode::Absolute;\n\n\tOBSDataAutoRelease privateData = obs_get_private_data();\n\tobs_data_set_bool(privateData, DataKeys::AbsoluteCoordinates.data(), isAbsoluteCoordinateMode);\n\n\tif (isAbsoluteCoordinateMode) {\n\t\tmenuItem->setText(QTStr(L10N::Migrate.data()));\n\t} else {\n\t\tmenuItem->setText(QTStr(L10N::Remigrate.data()));\n\t}\n\n\tmenuItem->setEnabled(isAbsoluteCoordinateMode);\n}\n\nvoid removeRelativePositionData(obs_data_t *settings)\n{\n\tOBSDataArrayAutoRelease sources = obs_data_get_array(settings, \"sources\");\n\n\tauto iterateCallback = [](obs_data_t *data, void *) {\n\t\tconst std::string_view id{obs_data_get_string(data, \"id\")};\n\t\tif (id != \"scene\" && id != \"group\") {\n\t\t\treturn;\n\t\t}\n\n\t\tOBSDataAutoRelease settings = obs_data_get_obj(data, \"settings\");\n\t\tOBSDataArrayAutoRelease items = obs_data_get_array(settings, \"items\");\n\n\t\tauto cleanupCallback = [](obs_data_t *data, void *) {\n\t\t\tobs_data_unset_user_value(data, \"pos_rel\");\n\t\t\tobs_data_unset_user_value(data, \"scale_rel\");\n\t\t\tobs_data_unset_user_value(data, \"scale_ref\");\n\t\t\tobs_data_unset_user_value(data, \"bounds_rel\");\n\t\t};\n\n\t\tobs_data_array_enum(items, cleanupCallback, nullptr);\n\t};\n\n\tobs_data_array_enum(sources, iterateCallback, nullptr);\n}\n\n} // namespace\n\n// MARK: - Main Scene Collection Management Functions\n\nvoid OBSBasic::SetupNewSceneCollection(const std::string &collectionName)\n{\n\tif (collectionName.empty()) {\n\t\tthrow std::logic_error(\"Cannot create new scene collection with empty collection name\");\n\t}\n\n\tSceneCollection &newCollection = CreateSceneCollection(collectionName);\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING);\n\n\tcleanBackupCollision(newCollection);\n\tActivateSceneCollection(newCollection);\n\n\tblog(LOG_INFO, \"Created scene collection '%s' (clean, %s)\", newCollection.getName().c_str(),\n\t     newCollection.getFileName().c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n}\n\nvoid OBSBasic::SetupDuplicateSceneCollection(const std::string &collectionName)\n{\n\tconst SceneCollection &currentCollection = GetCurrentSceneCollection();\n\tSceneCollection &newCollection = CreateSceneCollection(collectionName);\n\n\tSaveProjectNow();\n\n\tconst auto copyOptions = std::filesystem::copy_options::overwrite_existing;\n\n\ttry {\n\t\tstd::filesystem::copy(currentCollection.getFilePath(), newCollection.getFilePath(), copyOptions);\n\t} catch (const std::filesystem::filesystem_error &error) {\n\t\tblog(LOG_DEBUG, \"%s\", error.what());\n\t\tthrow std::logic_error(\"Failed to copy file for cloned scene collection: \" + newCollection.getName());\n\t}\n\n\tOBSDataAutoRelease collection = obs_data_create_from_json_file(newCollection.getFileName().c_str());\n\n\tobs_data_set_string(collection, \"name\", newCollection.getName().c_str());\n\n\tOBSDataArrayAutoRelease sources = obs_data_get_array(collection, \"sources\");\n\n\tif (sources) {\n\t\tobs_data_erase(collection, \"sources\");\n\n\t\tobs_data_array_enum(\n\t\t\tsources,\n\t\t\t[](obs_data_t *data, void *) -> void {\n\t\t\t\tconst char *uuid = os_generate_uuid();\n\n\t\t\t\tobs_data_set_string(data, \"uuid\", uuid);\n\n\t\t\t\tbfree((void *)uuid);\n\t\t\t},\n\t\t\tnullptr);\n\n\t\tobs_data_set_array(collection, \"sources\", sources);\n\t}\n\n\tobs_data_save_json_safe(collection, newCollection.getFileName().c_str(), \"tmp\", nullptr);\n\n\tcleanBackupCollision(newCollection);\n\tActivateSceneCollection(newCollection);\n\n\tblog(LOG_INFO, \"Created scene collection '%s' (duplicate, %s)\", newCollection.getName().c_str(),\n\t     newCollection.getFileName().c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n}\n\nvoid OBSBasic::SetupRenameSceneCollection(const std::string &collectionName)\n{\n\tconst SceneCollection currentCollection = GetCurrentSceneCollection();\n\tSceneCollection &newCollection = CreateSceneCollection(collectionName);\n\n\tSaveProjectNow();\n\n\tconst auto copyOptions = std::filesystem::copy_options::overwrite_existing;\n\n\ttry {\n\t\tstd::filesystem::copy(currentCollection.getFilePath(), newCollection.getFilePath(), copyOptions);\n\t} catch (const std::filesystem::filesystem_error &error) {\n\t\tblog(LOG_DEBUG, \"%s\", error.what());\n\t\tthrow std::logic_error(\"Failed to copy file for scene collection: \" + currentCollection.getName());\n\t}\n\n\tcollections.erase(currentCollection.getName());\n\n\tOBSDataAutoRelease collection = obs_data_create_from_json_file(newCollection.getFileName().c_str());\n\tobs_data_set_string(collection, \"name\", newCollection.getName().c_str());\n\n\tobs_data_save_json_safe(collection, newCollection.getFileName().c_str(), \"tmp\", nullptr);\n\n\tcleanBackupCollision(newCollection);\n\tActivateSceneCollection(newCollection);\n\tRemoveSceneCollection(currentCollection);\n\n\tblog(LOG_INFO, \"Renamed scene collection '%s' to '%s' (%s)\", currentCollection.getName().c_str(),\n\t     newCollection.getName().c_str(), newCollection.getFileName().c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_RENAMED);\n}\n\n// MARK: - Scene Collection File Management Functions\n\nSceneCollection &OBSBasic::CreateSceneCollection(const std::string &collectionName)\n{\n\tif (const auto &foundCollection = GetSceneCollectionByName(collectionName)) {\n\t\tthrow std::invalid_argument(\"Scene collection already exists: \" + collectionName);\n\t}\n\n\tstd::string fileName;\n\tif (!GetFileSafeName(collectionName.c_str(), fileName)) {\n\t\tthrow std::invalid_argument(\"Failed to create safe directory for new scene collection: \" +\n\t\t\t\t\t    collectionName);\n\t}\n\n\tstd::string collectionFile;\n\tcollectionFile.reserve(App()->userScenesLocation.u8string().size() + SceneCollectionPath.size() +\n\t\t\t       fileName.size());\n\tcollectionFile.append(App()->userScenesLocation.u8string()).append(SceneCollectionPath).append(fileName);\n\n\tif (!GetClosestUnusedFileName(collectionFile, \"json\")) {\n\t\tthrow std::invalid_argument(\"Failed to get closest file name for new scene collection: \" + fileName);\n\t}\n\n\tstd::filesystem::path collectionFilePath = std::filesystem::u8path(collectionFile);\n\n\tauto [iterator, success] =\n\t\tcollections.try_emplace(collectionName, collectionName, std::move(collectionFilePath));\n\n\treturn iterator->second;\n}\n\nvoid OBSBasic::RemoveSceneCollection(SceneCollection collection)\n{\n\ttry {\n\t\tstd::filesystem::remove(collection.getFilePath());\n\t} catch (const std::filesystem::filesystem_error &error) {\n\t\tblog(LOG_DEBUG, \"%s\", error.what());\n\t\tthrow std::logic_error(\"Failed to remove scene collection file: \" + collection.getFileName());\n\t}\n\n\tblog(LOG_INFO, \"Removed scene collection '%s' (%s)\", collection.getName().c_str(),\n\t     collection.getFileName().c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n}\n\n// MARK: - Scene Collection UI Handling Functions\n\nbool OBSBasic::CreateNewSceneCollection(const QString &name)\n{\n\ttry {\n\t\tSetupNewSceneCollection(name.toStdString());\n\t\treturn true;\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t\treturn false;\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t\treturn false;\n\t}\n}\n\nbool OBSBasic::CreateDuplicateSceneCollection(const QString &name)\n{\n\ttry {\n\t\tSetupDuplicateSceneCollection(name.toStdString());\n\t\treturn true;\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t\treturn false;\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t\treturn false;\n\t}\n}\n\nvoid OBSBasic::DeleteSceneCollection(const QString &name)\n{\n\tconst std::string_view currentCollectionName{\n\t\tconfig_get_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\")};\n\n\tif (currentCollectionName == name.toStdString()) {\n\t\ton_actionRemoveSceneCollection_triggered();\n\t\treturn;\n\t}\n\n\tSceneCollection currentCollection = GetCurrentSceneCollection();\n\n\tRemoveSceneCollection(currentCollection);\n\n\tcollections.erase(name.toStdString());\n\n\tRefreshSceneCollections();\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED);\n}\n\nvoid OBSBasic::ChangeSceneCollection()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\n\tif (!action) {\n\t\treturn;\n\t}\n\n\tconst std::string_view currentCollectionName{\n\t\tconfig_get_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\")};\n\tconst QVariant qCollectionName = action->property(\"collection_name\");\n\tconst std::string selectedCollectionName{qCollectionName.toString().toStdString()};\n\n\tif (currentCollectionName == selectedCollectionName) {\n\t\taction->setChecked(true);\n\t\treturn;\n\t}\n\n\tauto foundCollection = GetSceneCollectionByName(selectedCollectionName);\n\n\tif (!foundCollection) {\n\t\tconst std::string errorMessage{\"Selected scene collection not found: \"};\n\n\t\tthrow std::invalid_argument(errorMessage + currentCollectionName.data());\n\t}\n\n\tSceneCollection &selectedCollection = foundCollection.value();\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING);\n\n\tActivateSceneCollection(selectedCollection);\n\n\tblog(LOG_INFO, \"Switched to scene collection '%s' (%s)\", selectedCollection.getName().c_str(),\n\t     selectedCollection.getFileName().c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n}\n\nvoid OBSBasic::RefreshSceneCollections(bool refreshCache)\n{\n\tstd::string_view currentCollectionName{config_get_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\")};\n\n\tQList<QAction *> menuActions = ui->sceneCollectionMenu->actions();\n\n\tfor (auto &action : menuActions) {\n\t\tQVariant variant = action->property(\"file_name\");\n\t\tif (variant.typeName() != nullptr) {\n\t\t\tdelete action;\n\t\t}\n\t}\n\n\tif (refreshCache) {\n\t\tRefreshSceneCollectionCache();\n\t}\n\n\tupdateSortedSceneCollections(collections);\n\n\tsize_t numAddedCollections = 0;\n\tfor (auto &name : sortedSceneCollections) {\n\t\tconst std::string collectionName = name.toStdString();\n\t\ttry {\n\t\t\tconst SceneCollection &collection = collections.at(collectionName);\n\t\t\tconst QString qCollectionName = QString().fromStdString(collectionName);\n\n\t\t\tQAction *action = new QAction(qCollectionName, this);\n\t\t\taction->setProperty(\"collection_name\", qCollectionName);\n\t\t\taction->setProperty(\"file_name\", QString().fromStdString(collection.getFileName()));\n\t\t\tconnect(action, &QAction::triggered, this, &OBSBasic::ChangeSceneCollection);\n\t\t\taction->setCheckable(true);\n\t\t\taction->setChecked(collectionName == currentCollectionName);\n\n\t\t\tui->sceneCollectionMenu->addAction(action);\n\n\t\t\tnumAddedCollections += 1;\n\t\t} catch (const std::out_of_range &error) {\n\t\t\tblog(LOG_ERROR, \"No scene collection with name %s found in scene collection cache.\\n%s\",\n\t\t\t     collectionName.c_str(), error.what());\n\t\t}\n\t}\n\n\tui->actionRemoveSceneCollection->setEnabled(numAddedCollections > 1);\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tmain->ui->actionPasteFilters->setEnabled(false);\n\tmain->ui->actionPasteRef->setEnabled(false);\n\tmain->ui->actionPasteDup->setEnabled(false);\n}\n\n// MARK: - Scene Collection Cache Functions\n\nvoid OBSBasic::RefreshSceneCollectionCache()\n{\n\tOBSSceneCollectionCache foundCollections{};\n\n\tconst std::filesystem::path collectionsPath =\n\t\tApp()->userScenesLocation / std::filesystem::u8path(SceneCollectionPath.substr(1));\n\n\tif (!std::filesystem::exists(collectionsPath)) {\n\t\tblog(LOG_WARNING, \"Failed to get scene collections config path\");\n\t\treturn;\n\t}\n\n\tfor (const auto &entry : std::filesystem::directory_iterator(collectionsPath)) {\n\t\tif (entry.is_directory()) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.path().extension().u8string() != \".json\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tOBSDataAutoRelease collectionData =\n\t\t\tobs_data_create_from_json_file_safe(entry.path().u8string().c_str(), \"bak\");\n\n\t\tstd::string candidateName;\n\t\tstd::string collectionName = obs_data_get_string(collectionData, \"name\");\n\n\t\tif (collectionName.empty()) {\n\t\t\tcandidateName = entry.path().stem().u8string();\n\t\t} else {\n\t\t\tcandidateName = std::move(collectionName);\n\t\t}\n\n\t\tfoundCollections.try_emplace(candidateName, candidateName, entry.path());\n\t}\n\n\tcollections.swap(foundCollections);\n}\n\nSceneCollection &OBSBasic::GetCurrentSceneCollection()\n{\n\tstd::string currentCollectionName{config_get_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\")};\n\n\tif (currentCollectionName.empty()) {\n\t\tthrow std::invalid_argument(\"No valid scene collection name in configuration Basic->SceneCollection\");\n\t}\n\n\tconst auto &foundCollection = collections.find(currentCollectionName);\n\n\tif (foundCollection != collections.end()) {\n\t\treturn foundCollection->second;\n\t} else {\n\t\tthrow std::invalid_argument(\"Scene collection not found in collection list: \" + currentCollectionName);\n\t}\n}\n\nstd::optional<SceneCollection> OBSBasic::GetSceneCollectionByName(const std::string &collectionName) const\n{\n\tauto foundCollection = collections.find(collectionName);\n\n\tif (foundCollection == collections.end()) {\n\t\treturn {};\n\t} else {\n\t\treturn foundCollection->second;\n\t}\n}\n\nstd::optional<SceneCollection> OBSBasic::GetSceneCollectionByFileName(const std::string &fileName) const\n{\n\tfor (auto &[iterator, collection] : collections) {\n\t\tif (collection.getFileName() == fileName) {\n\t\t\treturn collection;\n\t\t}\n\t}\n\n\treturn {};\n}\n\n// MARK: - Qt Slot Functions\n\nvoid OBSBasic::on_actionNewSceneCollection_triggered()\n{\n\tconst OBSPromptCallback sceneCollectionCallback = [this](const OBSPromptResult &result) {\n\t\tif (GetSceneCollectionByName(result.promptValue)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tconst OBSPromptRequest request{Str(\"Basic.Main.AddSceneCollection.Title\"),\n\t\t\t\t       Str(\"Basic.Main.AddSceneCollection.Text\")};\n\n\tOBSPromptResult result = PromptForName(request, sceneCollectionCallback);\n\n\tif (!result.success) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tSetupNewSceneCollection(result.promptValue);\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t}\n}\n\nvoid OBSBasic::on_actionDupSceneCollection_triggered()\n{\n\tconst OBSPromptCallback sceneCollectionCallback = [this](const OBSPromptResult &result) {\n\t\tif (GetSceneCollectionByName(result.promptValue)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tconst OBSPromptRequest request{Str(\"Basic.Main.AddSceneCollection.Title\"),\n\t\t\t\t       Str(\"Basic.Main.AddSceneCollection.Text\")};\n\n\tOBSPromptResult result = PromptForName(request, sceneCollectionCallback);\n\n\tif (!result.success) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tSetupDuplicateSceneCollection(result.promptValue);\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t}\n}\n\nvoid OBSBasic::on_actionRenameSceneCollection_triggered()\n{\n\tconst SceneCollection &currentCollection = GetCurrentSceneCollection();\n\n\tconst OBSPromptCallback sceneCollectionCallback = [this](const OBSPromptResult &result) {\n\t\tif (GetSceneCollectionByName(result.promptValue)) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tconst OBSPromptRequest request{Str(\"Basic.Main.RenameSceneCollection.Title\"),\n\t\t\t\t       Str(\"Basic.Main.AddSceneCollection.Text\"), currentCollection.getName()};\n\n\tOBSPromptResult result = PromptForName(request, sceneCollectionCallback);\n\n\tif (!result.success) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tSetupRenameSceneCollection(result.promptValue);\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t}\n}\n\nvoid OBSBasic::on_actionRemoveSceneCollection_triggered(bool skipConfirmation)\n{\n\tif (collections.size() < 2) {\n\t\treturn;\n\t}\n\n\tSceneCollection currentCollection;\n\n\ttry {\n\t\tcurrentCollection = GetCurrentSceneCollection();\n\n\t\tif (!skipConfirmation) {\n\t\t\tconst QString confirmationText =\n\t\t\t\tQTStr(\"ConfirmRemove.Text\").arg(QString::fromStdString(currentCollection.getName()));\n\t\t\tconst QMessageBox::StandardButton button =\n\t\t\t\tOBSMessageBox::question(this, QTStr(\"ConfirmRemove.Title\"), confirmationText);\n\n\t\t\tif (button == QMessageBox::No) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING);\n\n\t\tcollections.erase(currentCollection.getName());\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t} catch (const std::logic_error &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t}\n\n\tSceneCollection &newCollection = collections.begin()->second;\n\n\tActivateSceneCollection(newCollection);\n\tRemoveSceneCollection(currentCollection);\n\n\tblog(LOG_INFO, \"Switched to scene collection '%s' (%s)\", newCollection.getName().c_str(),\n\t     newCollection.getFileName().c_str());\n\tblog(LOG_INFO, \"------------------------------------------------\");\n}\n\nvoid OBSBasic::on_actionImportSceneCollection_triggered()\n{\n\tOBSImporter imp(this);\n\timp.exec();\n\n\tRefreshSceneCollections(true);\n}\n\nvoid OBSBasic::on_actionExportSceneCollection_triggered()\n{\n\tSaveProjectNow();\n\n\tconst SceneCollection &currentCollection = GetCurrentSceneCollection();\n\n\tconst QString home = QDir::homePath();\n\n\tconst QString destinationFileName = SaveFile(this, QTStr(\"Basic.MainMenu.SceneCollection.Export\"),\n\t\t\t\t\t\t     home + \"/\" + currentCollection.getFileName().c_str(),\n\t\t\t\t\t\t     \"JSON Files (*.json)\");\n\n\tif (!destinationFileName.isEmpty() && !destinationFileName.isNull()) {\n\t\tconst std::filesystem::path sourceFile = currentCollection.getFilePath();\n\t\tconst std::filesystem::path destinationFile =\n\t\t\tstd::filesystem::u8path(destinationFileName.toStdString());\n\n\t\tOBSDataAutoRelease collection = obs_data_create_from_json_file(sourceFile.u8string().c_str());\n\n\t\tOBSDataArrayAutoRelease sources = obs_data_get_array(collection, \"sources\");\n\t\tif (!sources) {\n\t\t\tblog(LOG_WARNING, \"No sources in exported scene collection\");\n\t\t\treturn;\n\t\t}\n\n\t\tobs_data_erase(collection, \"sources\");\n\n\t\tusing OBSDataVector = std::vector<OBSData>;\n\n\t\tOBSDataVector sourceItems;\n\t\tobs_data_array_enum(\n\t\t\tsources,\n\t\t\t[](obs_data_t *data, void *vector) -> void {\n\t\t\t\tOBSDataVector &sourceItems{*static_cast<OBSDataVector *>(vector)};\n\t\t\t\tsourceItems.push_back(data);\n\t\t\t},\n\t\t\t&sourceItems);\n\n\t\tstd::sort(sourceItems.begin(), sourceItems.end(), [](const OBSData &a, const OBSData &b) {\n\t\t\treturn astrcmpi(obs_data_get_string(a, \"name\"), obs_data_get_string(b, \"name\")) < 0;\n\t\t});\n\n\t\tOBSDataArrayAutoRelease newSources = obs_data_array_create();\n\t\tfor (auto &item : sourceItems) {\n\t\t\tobs_data_array_push_back(newSources, item);\n\t\t}\n\n\t\tobs_data_set_array(collection, \"sources\", newSources);\n\t\tobs_data_save_json_pretty_safe(collection, destinationFile.u8string().c_str(), \"tmp\", \"bak\");\n\t}\n}\n\nvoid OBSBasic::on_actionRemigrateSceneCollection_triggered()\n{\n\tif (Active()) {\n\t\tOBSMessageBox::warning(this, QTStr(\"Basic.Main.RemigrateSceneCollection.Title\"),\n\t\t\t\t       QTStr(\"Basic.Main.RemigrateSceneCollection.CannotMigrate.Active\"));\n\t\treturn;\n\t}\n\n\tSceneCollection &currentCollection = GetCurrentSceneCollection();\n\tSceneCoordinateMode currentCoordinateMode = currentCollection.getCoordinateMode();\n\tOBS::Rect currentMigrationResolution = currentCollection.getMigrationResolution();\n\n\tif (currentCoordinateMode == SceneCoordinateMode::Relative && currentMigrationResolution.isZero()) {\n\t\tOBSMessageBox::warning(\n\t\t\tthis, QTStr(\"Basic.Main.RemigrateSceneCollection.Title\"),\n\t\t\tQTStr(\"Basic.Main.RemigrateSceneCollection.CannotMigrate.UnknownBaseResolution\"));\n\t\treturn;\n\t}\n\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tOBS::Rect videoResolution = OBS::Rect(ovi.base_width, ovi.base_height);\n\n\tif (currentCoordinateMode == SceneCoordinateMode::Relative && currentMigrationResolution == videoResolution) {\n\t\tOBSMessageBox::warning(\n\t\t\tthis, QTStr(\"Basic.Main.RemigrateSceneCollection.Title\"),\n\t\t\tQTStr(\"Basic.Main.RemigrateSceneCollection.CannotMigrate.BaseResolutionMatches\"));\n\t\treturn;\n\t}\n\n\tQString name = QString::fromStdString(currentCollection.getName());\n\tQString message =\n\t\tQTStr(\"Basic.Main.RemigrateSceneCollection.Text\").arg(name).arg(ovi.base_width).arg(ovi.base_height);\n\n\tauto answer = OBSMessageBox::question(this, QTStr(\"Basic.Main.RemigrateSceneCollection.Title\"), message);\n\n\tif (answer == QMessageBox::No)\n\t\treturn;\n\n\tlastOutputResolution = {ovi.base_width, ovi.base_height};\n\tif (currentCoordinateMode == SceneCoordinateMode::Relative) {\n\t\t/* Temporarily change resolution to migration resolution */\n\t\tovi.base_width = currentMigrationResolution.getWidth<uint32_t>();\n\t\tovi.base_height = currentMigrationResolution.getHeight<uint32_t>();\n\n\t\tif (obs_reset_video(&ovi) != OBS_VIDEO_SUCCESS) {\n\t\t\tOBSMessageBox::critical(\n\t\t\t\tthis, QTStr(\"Basic.Main.RemigrateSceneCollection.Title\"),\n\t\t\t\tQTStr(\"Basic.Main.RemigrateSceneCollection.CannotMigrate.FailedVideoReset\"));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGING);\n\n\t// Temporarily switch the coordinate mode of the current scene collection to \"absolute\" mode to force the\n\t// collection to be saved as a \"version 1\" variant. By resetting the coordinate mode to its original mode after\n\t// saving the collection, the activation process will migrate it automatically.\n\n\tcurrentCollection.setCoordinateMode(SceneCoordinateMode::Absolute);\n\tSaveProjectNow();\n\tcurrentCollection.setCoordinateMode(currentCoordinateMode);\n\n\t/* Reset video if we potentially changed to a temporary resolution */\n\tif (currentCoordinateMode == SceneCoordinateMode::Relative) {\n\t\tResetVideo();\n\t}\n\n\tActivateSceneCollection(currentCollection);\n}\n\n// MARK: - Scene Collection Management Helper Functions\n\nvoid OBSBasic::ActivateSceneCollection(SceneCollection &collection)\n{\n\tconst std::string currentCollectionName{config_get_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\")};\n\n\tif (auto foundCollection = GetSceneCollectionByName(currentCollectionName)) {\n\t\tif (collection.getName() != foundCollection.value().getName()) {\n\t\t\tSaveProjectNow();\n\t\t}\n\t}\n\n\tconfig_set_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\", collection.getName().c_str());\n\tconfig_set_string(App()->GetUserConfig(), \"Basic\", \"SceneCollectionFile\", collection.getFileName().c_str());\n\n\tLoad(collection);\n\n\tRefreshSceneCollections();\n\n\tUpdateTitleBar();\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED);\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED);\n}\n\n// MARK: - OBSBasic Scene Collection Functions\n\nusing namespace std;\n\nnamespace {\nconstexpr std::string_view DESKTOP_AUDIO_1{\"DesktopAudioDevice1\"};\nconstexpr std::string_view DESKTOP_AUDIO_2{\"DesktopAudioDevice2\"};\nconstexpr std::string_view AUX_AUDIO_1{\"AuxAudioDevice1\"};\nconstexpr std::string_view AUX_AUDIO_2{\"AuxAudioDevice2\"};\nconstexpr std::string_view AUX_AUDIO_3{\"AuxAudioDevice3\"};\nconstexpr std::string_view AUX_AUDIO_4{\"AuxAudioDevice4\"};\n} // namespace\n\nvoid OBSBasic::Save(SceneCollection &collection)\n{\n\tOBSDataAutoRelease saveData = obs_data_create();\n\n\t// Scene collection name\n\tconst char *sceneCollection = config_get_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\");\n\tobs_data_set_string(saveData, \"name\", sceneCollection);\n\n\t// Global audio sources\n\tvector<OBSSource> audioSources;\n\taudioSources.reserve(6);\n\n\tauto SaveAudioDevice = [&](const std::string_view &key, int channel) {\n\t\tif (OBSSourceAutoRelease source = obs_get_output_source(channel)) {\n\t\t\taudioSources.emplace_back(source.Get());\n\t\t\tOBSDataAutoRelease data = obs_save_source(source);\n\t\t\tobs_data_set_obj(saveData, key.data(), data);\n\t\t}\n\t};\n\n\tSaveAudioDevice(DESKTOP_AUDIO_1, 1);\n\tSaveAudioDevice(DESKTOP_AUDIO_2, 2);\n\tSaveAudioDevice(AUX_AUDIO_1, 3);\n\tSaveAudioDevice(AUX_AUDIO_2, 4);\n\tSaveAudioDevice(AUX_AUDIO_3, 5);\n\tSaveAudioDevice(AUX_AUDIO_4, 6);\n\n\t// Non-global sources\n\n\t// Save all non-scene sources first\n\tauto FilterAudioSources = [&](obs_source_t *source) {\n\t\tif (obs_source_is_group(source) || obs_source_is_scene(source))\n\t\t\treturn false;\n\n\t\treturn std::find(begin(audioSources), end(audioSources), source) == end(audioSources);\n\t};\n\tusing FilterAudioSources_t = decltype(FilterAudioSources);\n\n\tOBSDataArrayAutoRelease sourcesArray = obs_save_sources_filtered(\n\t\t[](void *data, obs_source_t *source) {\n\t\t\tauto &func = *static_cast<FilterAudioSources_t *>(data);\n\t\t\treturn func(source);\n\t\t},\n\t\tstatic_cast<void *>(&FilterAudioSources));\n\n\t// Saving groups separately ensures they won't be loaded in older versions.\n\t// TODO: Get rid of this at some point. Groups were introduced in 22.0\n\tOBSDataArrayAutoRelease groupsArray = obs_data_array_create();\n\n\tauto sourcesAndGroups = std::make_pair(sourcesArray.Get(), groupsArray.Get());\n\tusing sourcesAndGroups_t = decltype(sourcesAndGroups);\n\n\tauto exportSceneItemsCallback = [](void *param, obs_source_t *source) -> bool {\n\t\tauto sourcesArrays = static_cast<sourcesAndGroups_t *>(param);\n\t\tOBSDataAutoRelease source_data = obs_save_source(source);\n\n\t\tif (obs_source_is_scene(source)) {\n\t\t\tobs_data_array_push_back(sourcesArrays->first, source_data);\n\t\t} else {\n\t\t\tobs_data_array_push_back(sourcesArrays->second, source_data);\n\t\t}\n\t\treturn true;\n\t};\n\n\tobs_enum_scenes(exportSceneItemsCallback, &sourcesAndGroups);\n\n\t// Iterate over our additional canvases (if any), save their scenes and groups.\n\tfor (const auto &canvas : canvases) {\n\t\t// Do not store sources from ephemeral canvases.\n\t\tif (obs_canvas_get_flags(canvas) & EPHEMERAL)\n\t\t\tcontinue;\n\n\t\tobs_canvas_enum_scenes(canvas, exportSceneItemsCallback, &sourcesAndGroups);\n\t}\n\n\tobs_data_set_array(saveData, \"sources\", sourcesArray);\n\tobs_data_set_array(saveData, \"groups\", groupsArray);\n\n\t// UI scene order\n\tOBSDataArrayAutoRelease sceneOrder = SaveSceneListOrder();\n\tobs_data_set_array(saveData, \"scene_order\", sceneOrder);\n\n\t// Current preview/program scenes\n\tOBSScene scene = GetCurrentScene();\n\tOBSSource curProgramScene = OBSGetStrongRef(programScene);\n\tif (!curProgramScene)\n\t\tcurProgramScene = obs_scene_get_source(scene);\n\n\tobs_data_set_string(saveData, \"current_scene\", obs_source_get_name(obs_scene_get_source(scene)));\n\tobs_data_set_string(saveData, \"current_program_scene\", obs_source_get_name(curProgramScene));\n\n\t// Canvases\n\tOBSDataArrayAutoRelease savedCanvases = OBS::Canvas::SaveCanvases(canvases);\n\tobs_data_set_array(saveData, \"canvases\", savedCanvases);\n\n\t// Transitions\n\tOBSSourceAutoRelease transition = obs_get_output_source(0);\n\tOBSDataArrayAutoRelease transitionsData = SaveTransitions();\n\tOBSDataArrayAutoRelease quickTransitionData = SaveQuickTransitions();\n\n\tobs_data_set_string(saveData, \"current_transition\", obs_source_get_name(transition));\n\tobs_data_set_int(saveData, \"transition_duration\", transitionDuration);\n\tobs_data_set_array(saveData, \"transitions\", transitionsData);\n\tobs_data_set_array(saveData, \"quick_transitions\", quickTransitionData);\n\n\t// Projectors\n\tOBSDataArrayAutoRelease savedProjectorList = SaveProjectors();\n\tobs_data_set_array(saveData, \"saved_projectors\", savedProjectorList);\n\n\t// UI state\n\tobs_data_set_bool(saveData, \"preview_locked\", ui->preview->Locked());\n\tobs_data_set_bool(saveData, \"scaling_enabled\", ui->preview->IsFixedScaling());\n\tobs_data_set_int(saveData, \"scaling_level\", ui->preview->GetScalingLevel());\n\tobs_data_set_double(saveData, \"scaling_off_x\", ui->preview->GetScrollX());\n\tobs_data_set_double(saveData, \"scaling_off_y\", ui->preview->GetScrollY());\n\n\t// Virtual Cam\n\tif (vcamEnabled) {\n\t\tOBSDataAutoRelease obj = obs_data_create();\n\n\t\tobs_data_set_int(obj, \"type2\", (int)vcamConfig.type);\n\t\tswitch (vcamConfig.type) {\n\t\tcase VCamOutputType::Invalid:\n\t\tcase VCamOutputType::ProgramView:\n\t\tcase VCamOutputType::PreviewOutput:\n\t\t\tbreak;\n\t\tcase VCamOutputType::SceneOutput:\n\t\t\tobs_data_set_string(obj, \"scene\", vcamConfig.scene.c_str());\n\t\t\tbreak;\n\t\tcase VCamOutputType::SourceOutput:\n\t\t\tobs_data_set_string(obj, \"source\", vcamConfig.source.c_str());\n\t\t\tbreak;\n\t\t}\n\n\t\tobs_data_set_obj(saveData, \"virtual-camera\", obj);\n\t}\n\n\t// Module-specific data\n\tif (api) {\n\t\tif (!collectionModuleData)\n\t\t\tcollectionModuleData = obs_data_create();\n\n\t\tapi->on_save(collectionModuleData);\n\t\tobs_data_set_obj(saveData, \"modules\", collectionModuleData);\n\t}\n\n\t// Relative coordinates metadata\n\tif (lastOutputResolution) {\n\t\tOBSDataAutoRelease res = obs_data_create();\n\t\tobs_data_set_int(res, \"x\", lastOutputResolution->first);\n\t\tobs_data_set_int(res, \"y\", lastOutputResolution->second);\n\t\tobs_data_set_obj(saveData, \"resolution\", res);\n\t}\n\n\tOBS::Rect migrationResolution = collection.getMigrationResolution();\n\tSceneCoordinateMode coordinateMode = collection.getCoordinateMode();\n\n\tif (coordinateMode == SceneCoordinateMode::Absolute) {\n\t\tremoveRelativePositionData(saveData);\n\t}\n\n\tif (!migrationResolution.isZero() && coordinateMode == SceneCoordinateMode::Relative) {\n\t\tOBSDataAutoRelease resolutionData = obs_data_create();\n\n\t\tobs_data_set_int(resolutionData, \"x\", migrationResolution.getWidth<long long>());\n\t\tobs_data_set_int(resolutionData, \"y\", migrationResolution.getHeight<long long>());\n\n\t\tobs_data_set_obj(saveData, DataKeys::MigrationResolution.data(), resolutionData);\n\t}\n\n\t// Version\n\tint sceneCollectionVersion = collection.getVersion();\n\tobs_data_set_int(saveData, \"version\", sceneCollectionVersion);\n\n\tconst std::string collectionFileName = collection.getFilePathString();\n\tbool success = obs_data_save_json_pretty_safe(saveData, collectionFileName.c_str(), \"tmp\", \"bak\");\n\n\tif (!success) {\n\t\tblog(LOG_ERROR, \"Could not save scene data to %s\", collectionFileName.c_str());\n\t}\n}\n\nvoid OBSBasic::DeferSaveBegin()\n{\n\tos_atomic_inc_long(&disableSaving);\n}\n\nvoid OBSBasic::DeferSaveEnd()\n{\n\tlong result = os_atomic_dec_long(&disableSaving);\n\tif (result == 0) {\n\t\tSaveProject();\n\t}\n}\n\nstatic void LogFilter(obs_source_t *, obs_source_t *filter, void *v_val);\n\nstatic void LoadAudioDevice(const char *name, int channel, obs_data_t *parent)\n{\n\tOBSDataAutoRelease data = obs_data_get_obj(parent, name);\n\tif (!data)\n\t\treturn;\n\n\tOBSSourceAutoRelease source = obs_load_source(data);\n\tif (!source)\n\t\treturn;\n\n\tobs_set_output_source(channel, source);\n\n\tconst char *source_name = obs_source_get_name(source);\n\tblog(LOG_INFO, \"[Loaded global audio device]: '%s'\", source_name);\n\tobs_source_enum_filters(source, LogFilter, (void *)(intptr_t)1);\n\tobs_monitoring_type monitoring_type = obs_source_get_monitoring_type(source);\n\tif (monitoring_type != OBS_MONITORING_TYPE_NONE) {\n\t\tconst char *type = (monitoring_type == OBS_MONITORING_TYPE_MONITOR_ONLY) ? \"monitor only\"\n\t\t\t\t\t\t\t\t\t\t\t : \"monitor and output\";\n\n\t\tblog(LOG_INFO, \"    - monitoring: %s\", type);\n\t}\n}\n\nvoid OBSBasic::CreateDefaultScene(bool firstStart)\n{\n\tdisableSaving++;\n\n\tClearSceneData();\n\tInitDefaultTransitions();\n\tCreateDefaultQuickTransitions();\n\tSetTransitionDuration(300);\n\tSetTransition(fadeTransition);\n\n\tupdateRemigrationMenuItem(SceneCoordinateMode::Relative, ui->actionRemigrateSceneCollection);\n\n\tOBSSceneAutoRelease scene = obs_scene_create(Str(\"Basic.Scene\"));\n\n\tif (firstStart)\n\t\tCreateFirstRunSources();\n\n\tSetCurrentScene(scene, true);\n\n\tdisableSaving--;\n}\n\nstatic void LogFilter(obs_source_t *, obs_source_t *filter, void *v_val)\n{\n\tconst char *name = obs_source_get_name(filter);\n\tconst char *id = obs_source_get_id(filter);\n\tint val = (int)(intptr_t)v_val;\n\tstring indent;\n\n\tfor (int i = 0; i < val; i++)\n\t\tindent += \"    \";\n\n\tblog(LOG_INFO, \"%s- filter: '%s' (%s)\", indent.c_str(), name, id);\n}\n\nstatic bool LogSceneItem(obs_scene_t *, obs_sceneitem_t *item, void *v_val)\n{\n\tobs_source_t *source = obs_sceneitem_get_source(item);\n\tconst char *name = obs_source_get_name(source);\n\tconst char *id = obs_source_get_id(source);\n\tint indent_count = (int)(intptr_t)v_val;\n\tstring indent;\n\n\tfor (int i = 0; i < indent_count; i++)\n\t\tindent += \"    \";\n\n\tblog(LOG_INFO, \"%s- source: '%s' (%s)\", indent.c_str(), name, id);\n\n\tobs_monitoring_type monitoring_type = obs_source_get_monitoring_type(source);\n\n\tif (monitoring_type != OBS_MONITORING_TYPE_NONE) {\n\t\tconst char *type = (monitoring_type == OBS_MONITORING_TYPE_MONITOR_ONLY) ? \"monitor only\"\n\t\t\t\t\t\t\t\t\t\t\t : \"monitor and output\";\n\n\t\tblog(LOG_INFO, \"    %s- monitoring: %s\", indent.c_str(), type);\n\t}\n\tint child_indent = 1 + indent_count;\n\tobs_source_enum_filters(source, LogFilter, (void *)(intptr_t)child_indent);\n\n\tobs_source_t *show_tn = obs_sceneitem_get_transition(item, true);\n\tobs_source_t *hide_tn = obs_sceneitem_get_transition(item, false);\n\tif (show_tn)\n\t\tblog(LOG_INFO, \"    %s- show: '%s' (%s)\", indent.c_str(), obs_source_get_name(show_tn),\n\t\t     obs_source_get_id(show_tn));\n\tif (hide_tn)\n\t\tblog(LOG_INFO, \"    %s- hide: '%s' (%s)\", indent.c_str(), obs_source_get_name(hide_tn),\n\t\t     obs_source_get_id(hide_tn));\n\n\tif (obs_sceneitem_is_group(item))\n\t\tobs_sceneitem_group_enum_items(item, LogSceneItem, (void *)(intptr_t)child_indent);\n\treturn true;\n}\n\nvoid OBSBasic::LogScenes()\n{\n\tblog(LOG_INFO, \"------------------------------------------------\");\n\tblog(LOG_INFO, \"Loaded scenes:\");\n\n\tfor (int i = 0; i < ui->scenes->count(); i++) {\n\t\tQListWidgetItem *item = ui->scenes->item(i);\n\t\tOBSScene scene = GetOBSRef<OBSScene>(item);\n\n\t\tobs_source_t *source = obs_scene_get_source(scene);\n\t\tconst char *name = obs_source_get_name(source);\n\n\t\tblog(LOG_INFO, \"- scene '%s':\", name);\n\t\tobs_scene_enum_items(scene, LogSceneItem, (void *)(intptr_t)1);\n\t\tobs_source_enum_filters(source, LogFilter, (void *)(intptr_t)1);\n\t}\n\n\tblog(LOG_INFO, \"------------------------------------------------\");\n}\n\nvoid OBSBasic::Load(SceneCollection &collection)\n{\n\tdisableSaving++;\n\n\tlastOutputResolution.reset();\n\tcollection.setMigrationResolution(0, 0);\n\n\tobs_data_t *data = obs_data_create_from_json_file_safe(collection.getFilePathString().c_str(), \"bak\");\n\n\tif (!data) {\n\t\tdisableSaving--;\n\t\tconst std::filesystem::path filePath = collection.getFilePath();\n\n\t\tif (std::filesystem::exists(filePath)) {\n\t\t\tstd::filesystem::path backupFilePath = filePath;\n\t\t\tbackupFilePath.replace_extension(\".json.invalid\");\n\n\t\t\tblog(LOG_WARNING,\n\t\t\t     \"File exists but appears to be corrupt, renaming \"\n\t\t\t     \"to \\\"%s\\\" before continuing.\",\n\t\t\t     backupFilePath.filename().u8string().c_str());\n\n\t\t\ttry {\n\t\t\t\tstd::filesystem::rename(filePath, backupFilePath);\n\t\t\t} catch (const std::filesystem::filesystem_error &error) {\n\t\t\t\tblog(LOG_ERROR, \"Failed renaming corrupt file:\\n%s\", error.what());\n\t\t\t}\n\t\t}\n\n\t\tblog(LOG_INFO, \"No scene file found, creating default scene\");\n\n\t\tbool hasFirstRun = config_get_bool(App()->GetUserConfig(), \"General\", \"FirstRun\");\n\n\t\tCreateDefaultScene(!hasFirstRun);\n\t\tSaveProject();\n\t\treturn;\n\t}\n\n\tLoadData(data, collection);\n}\n\nstatic inline void AddMissingFiles(void *data, obs_source_t *source)\n{\n\tobs_missing_files_t *f = (obs_missing_files_t *)data;\n\tobs_missing_files_t *sf = obs_source_get_missing_files(source);\n\n\tobs_missing_files_append(f, sf);\n\tobs_missing_files_destroy(sf);\n}\n\nvoid OBSBasic::LoadData(obs_data_t *data, SceneCollection &collection)\n{\n\tClearSceneData();\n\tClearContextBar();\n\n\t/* Exit OBS if clearing scene data failed for some reason. */\n\tif (clearingFailed) {\n\t\tOBSMessageBox::critical(this, QTStr(\"SourceLeak.Title\"), QTStr(\"SourceLeak.Text\"));\n\t\tclose();\n\t\treturn;\n\t}\n\n\tInitDefaultTransitions();\n\n\tif (devicePropertiesThread && devicePropertiesThread->isRunning()) {\n\t\tdevicePropertiesThread->wait();\n\t\tdevicePropertiesThread.reset();\n\t}\n\n\tQApplication::sendPostedEvents(nullptr);\n\n\tOBSDataAutoRelease modulesObj = obs_data_get_obj(data, \"modules\");\n\tif (api)\n\t\tapi->on_preload(modulesObj);\n\n\t/* Keep a reference to \"modules\" data so plugins that are not loaded do\n\t * not have their collection specific data lost. */\n\tcollectionModuleData = obs_data_get_obj(data, \"modules\");\n\n\tOBSDataArrayAutoRelease sceneOrder = obs_data_get_array(data, \"scene_order\");\n\tOBSDataArrayAutoRelease sources = obs_data_get_array(data, \"sources\");\n\tOBSDataArrayAutoRelease groups = obs_data_get_array(data, \"groups\");\n\tOBSDataArrayAutoRelease transitionsData = obs_data_get_array(data, \"transitions\");\n\tOBSDataArrayAutoRelease collection_canvases = obs_data_get_array(data, \"canvases\");\n\tconst char *sceneName = obs_data_get_string(data, \"current_scene\");\n\tconst char *programSceneName = obs_data_get_string(data, \"current_program_scene\");\n\tconst char *transitionName = obs_data_get_string(data, \"current_transition\");\n\n\tif (!opt_starting_scene.empty()) {\n\t\tprogramSceneName = opt_starting_scene.c_str();\n\t\tif (!IsPreviewProgramMode())\n\t\t\tsceneName = opt_starting_scene.c_str();\n\t}\n\n\tint newDuration = obs_data_get_int(data, \"transition_duration\");\n\tif (!newDuration)\n\t\tnewDuration = 300;\n\n\tif (!transitionName)\n\t\ttransitionName = obs_source_get_name(fadeTransition);\n\n\tconst char *curSceneCollection = config_get_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\");\n\n\tobs_data_set_default_string(data, \"name\", curSceneCollection);\n\n\tconst char *name = obs_data_get_string(data, \"name\");\n\tOBSSourceAutoRelease curScene;\n\tOBSSourceAutoRelease curProgramScene;\n\tobs_source_t *curTransition;\n\n\tif (!name || !*name)\n\t\tname = curSceneCollection;\n\n\tLoadAudioDevice(DESKTOP_AUDIO_1.data(), 1, data);\n\tLoadAudioDevice(DESKTOP_AUDIO_2.data(), 2, data);\n\tLoadAudioDevice(AUX_AUDIO_1.data(), 3, data);\n\tLoadAudioDevice(AUX_AUDIO_2.data(), 4, data);\n\tLoadAudioDevice(AUX_AUDIO_3.data(), 5, data);\n\tLoadAudioDevice(AUX_AUDIO_4.data(), 6, data);\n\n\tif (collection_canvases)\n\t\tcanvases = OBS::Canvas::LoadCanvases(collection_canvases);\n\n\tif (!sources) {\n\t\tsources = std::move(groups);\n\t} else {\n\t\tobs_data_array_push_back_array(sources, groups);\n\t}\n\n\tbool resetVideo = false;\n\tobs_video_info ovi;\n\n\tint64_t version = obs_data_get_int(data, \"version\");\n\tOBSDataAutoRelease res = obs_data_get_obj(data, \"resolution\");\n\n\tOBS::Rect collectionSize{};\n\n\tif (res) {\n\t\tcollectionSize.setWidth(obs_data_get_int(res, \"x\"));\n\t\tcollectionSize.setHeight(obs_data_get_int(res, \"y\"));\n\t}\n\n\t/* Only migrate legacy collection if resolution is saved. */\n\tif (version < 2 && !collectionSize.isZero()) {\n\t\tobs_get_video_info(&ovi);\n\n\t\tcollection.setMigrationResolution(collectionSize);\n\n\t\tOBS::Rect outputSize{ovi.base_width, ovi.base_height};\n\n\t\tif (outputSize != collectionSize) {\n\t\t\tovi.base_width = collectionSize.getWidth<uint32_t>();\n\t\t\tovi.base_height = collectionSize.getHeight<uint32_t>();\n\n\t\t\t/* Attempt to reset to last known canvas resolution for migration. */\n\t\t\tresetVideo = obs_reset_video(&ovi) == OBS_VIDEO_SUCCESS;\n\n\t\t\tif (!resetVideo) {\n\t\t\t\tcollection.setCoordinateMode(OBS::SceneCoordinateMode::Absolute);\n\t\t\t}\n\t\t}\n\n\t\t// If migration is necessary, attempt to back up version 1 variant of the scene ecollection.\n\t\tif (collection.getCoordinateMode() == SceneCoordinateMode::Absolute) {\n\t\t\tstd::filesystem::path backupFilePath = collection.getFilePath();\n\t\t\tbackupFilePath.replace_extension(\".json.v1\");\n\n\t\t\tif (!std::filesystem::exists(backupFilePath)) {\n\t\t\t\tbool success = obs_data_save_json_pretty_safe(data, backupFilePath.u8string().c_str(),\n\t\t\t\t\t\t\t\t\t      \"tmp\", nullptr);\n\n\t\t\t\tif (!success) {\n\t\t\t\t\tblog(LOG_WARNING,\n\t\t\t\t\t     \"Failed to create a backup of existing scene collection data\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcollection.setCoordinateMode(SceneCoordinateMode::Relative);\n\t} else if (version < 2) {\n\t\tcollection.setCoordinateMode(SceneCoordinateMode::Absolute);\n\t} else {\n\t\tOBSDataAutoRelease migrationResolution = obs_data_get_obj(data, \"migration_resolution\");\n\n\t\tif (migrationResolution) {\n\t\t\tcollection.setMigrationResolution(obs_data_get_int(migrationResolution, \"x\"),\n\t\t\t\t\t\t\t  obs_data_get_int(migrationResolution, \"y\"));\n\t\t}\n\t}\n\n\t// FIXME: Migrate to actual SceneCollection container with reference-based API\n\t// The collection passed as function argument might be a copy generated via std::optional and thus\n\t// might not represent the actual collection within the collection, meaning all changes would be lost after\n\t// the function stack is unwound (i.e., the change would not be written into the scene collection file\n\t// during OBS shutdown).\n\t// Explicitly updating the collection inside the container ensures the changes \"stick\".\n\n\tcollections[collection.getName()] = collection;\n\n\tupdateRemigrationMenuItem(collection.getCoordinateMode(), ui->actionRemigrateSceneCollection);\n\n\tobs_missing_files_t *files = obs_missing_files_create();\n\tobs_load_sources(sources, AddMissingFiles, files);\n\n\tif (resetVideo)\n\t\tResetVideo();\n\tif (transitionsData)\n\t\tLoadTransitions(transitionsData, AddMissingFiles, files);\n\tif (sceneOrder)\n\t\tLoadSceneListOrder(sceneOrder);\n\n\tcurTransition = FindTransition(transitionName);\n\tif (!curTransition)\n\t\tcurTransition = fadeTransition;\n\n\tSetTransitionDuration(newDuration);\n\tSetTransition(curTransition);\n\nretryScene:\n\tcurScene = obs_get_source_by_name(sceneName);\n\tcurProgramScene = obs_get_source_by_name(programSceneName);\n\n\t/* if the starting scene command line parameter is bad at all,\n\t * fall back to original settings */\n\tif (!opt_starting_scene.empty() && (!curScene || !curProgramScene)) {\n\t\tsceneName = obs_data_get_string(data, \"current_scene\");\n\t\tprogramSceneName = obs_data_get_string(data, \"current_program_scene\");\n\t\topt_starting_scene.clear();\n\t\tgoto retryScene;\n\t}\n\n\tif (!curScene) {\n\t\tauto find_scene_cb = [](void *source_ptr, obs_source_t *scene) {\n\t\t\t*static_cast<OBSSourceAutoRelease *>(source_ptr) = obs_source_get_ref(scene);\n\t\t\treturn false;\n\t\t};\n\t\tobs_enum_scenes(find_scene_cb, &curScene);\n\t}\n\n\tSetCurrentScene(curScene.Get(), true);\n\n\tif (!curProgramScene)\n\t\tcurProgramScene = std::move(curScene);\n\tif (IsPreviewProgramMode())\n\t\tTransitionToScene(curProgramScene.Get(), true);\n\n\t/* ------------------- */\n\n\tbool projectorSave = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SaveProjectors\");\n\n\tif (projectorSave) {\n\t\tOBSDataArrayAutoRelease savedProjectors = obs_data_get_array(data, \"saved_projectors\");\n\n\t\tif (savedProjectors) {\n\t\t\tLoadSavedProjectors(savedProjectors);\n\t\t\tactivateWindow();\n\t\t}\n\t}\n\n\t/* ------------------- */\n\n\tconfig_set_string(App()->GetUserConfig(), \"Basic\", \"SceneCollection\", collection.getName().c_str());\n\tconfig_set_string(App()->GetUserConfig(), \"Basic\", \"SceneCollectionFile\", collection.getFileName().c_str());\n\n\tOBSDataArrayAutoRelease quickTransitionData = obs_data_get_array(data, \"quick_transitions\");\n\tLoadQuickTransitions(quickTransitionData);\n\n\tRefreshQuickTransitions();\n\n\tbool previewLocked = obs_data_get_bool(data, \"preview_locked\");\n\tui->preview->SetLocked(previewLocked);\n\tui->actionLockPreview->setChecked(previewLocked);\n\n\t/* ---------------------- */\n\n\tbool fixedScaling = obs_data_get_bool(data, \"scaling_enabled\");\n\tint scalingLevel = (int)obs_data_get_int(data, \"scaling_level\");\n\tfloat scrollOffX = (float)obs_data_get_double(data, \"scaling_off_x\");\n\tfloat scrollOffY = (float)obs_data_get_double(data, \"scaling_off_y\");\n\n\tif (fixedScaling) {\n\t\tui->preview->SetScalingLevel(scalingLevel);\n\t\tui->preview->SetScrollingOffset(scrollOffX, scrollOffY);\n\t}\n\tui->preview->SetFixedScaling(fixedScaling);\n\n\temit ui->preview->DisplayResized();\n\n\tif (vcamEnabled) {\n\t\tOBSDataAutoRelease obj = obs_data_get_obj(data, \"virtual-camera\");\n\n\t\tvcamConfig.type = (VCamOutputType)obs_data_get_int(obj, \"type2\");\n\t\tif (vcamConfig.type == VCamOutputType::Invalid)\n\t\t\tvcamConfig.type = (VCamOutputType)obs_data_get_int(obj, \"type\");\n\n\t\tif (vcamConfig.type == VCamOutputType::Invalid) {\n\t\t\tVCamInternalType internal = (VCamInternalType)obs_data_get_int(obj, \"internal\");\n\n\t\t\tswitch (internal) {\n\t\t\tcase VCamInternalType::Default:\n\t\t\t\tvcamConfig.type = VCamOutputType::ProgramView;\n\t\t\t\tbreak;\n\t\t\tcase VCamInternalType::Preview:\n\t\t\t\tvcamConfig.type = VCamOutputType::PreviewOutput;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tvcamConfig.scene = obs_data_get_string(obj, \"scene\");\n\t\tvcamConfig.source = obs_data_get_string(obj, \"source\");\n\t}\n\n\tif (!collectionSize.isZero()) {\n\t\tlastOutputResolution = {collectionSize.getWidth<uint32_t>(), collectionSize.getHeight<uint32_t>()};\n\t}\n\n\t/* ---------------------- */\n\n\tif (api)\n\t\tapi->on_load(modulesObj);\n\n\tobs_data_release(data);\n\n\tif (!opt_starting_scene.empty())\n\t\topt_starting_scene.clear();\n\n\tif (opt_start_streaming && !safe_mode) {\n\t\tblog(LOG_INFO, \"Starting stream due to command line parameter\");\n\t\tQMetaObject::invokeMethod(this, \"StartStreaming\", Qt::QueuedConnection);\n\t\topt_start_streaming = false;\n\t}\n\n\tif (opt_start_recording && !safe_mode) {\n\t\tblog(LOG_INFO, \"Starting recording due to command line parameter\");\n\t\tQMetaObject::invokeMethod(this, \"StartRecording\", Qt::QueuedConnection);\n\t\topt_start_recording = false;\n\t}\n\n\tif (opt_start_replaybuffer && !safe_mode) {\n\t\tQMetaObject::invokeMethod(this, \"StartReplayBuffer\", Qt::QueuedConnection);\n\t\topt_start_replaybuffer = false;\n\t}\n\n\tif (opt_start_virtualcam && !safe_mode) {\n\t\tQMetaObject::invokeMethod(this, \"StartVirtualCam\", Qt::QueuedConnection);\n\t\topt_start_virtualcam = false;\n\t}\n\n\tLogScenes();\n\n\tif (!App()->IsMissingFilesCheckDisabled())\n\t\tShowMissingFilesDialog(files);\n\n\tdisableSaving--;\n\n\tif (vcamEnabled)\n\t\toutputHandler->UpdateVirtualCamOutputSource();\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_CHANGED);\n\tOnEvent(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);\n}\n\nvoid OBSBasic::SaveProjectNow()\n{\n\tif (disableSaving)\n\t\treturn;\n\n\tprojectChanged = true;\n\tSaveProjectDeferred();\n}\n\nvoid OBSBasic::SaveProject()\n{\n\tif (disableSaving)\n\t\treturn;\n\n\tprojectChanged = true;\n\tQMetaObject::invokeMethod(this, \"SaveProjectDeferred\", Qt::QueuedConnection);\n}\n\nvoid OBSBasic::SaveProjectDeferred()\n{\n\tif (disableSaving)\n\t\treturn;\n\n\tif (!projectChanged)\n\t\treturn;\n\n\tprojectChanged = false;\n\n\ttry {\n\t\tOBS::SceneCollection &currentCollection = GetCurrentSceneCollection();\n\n\t\tSave(currentCollection);\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t}\n}\n\nvoid OBSBasic::ClearSceneData()\n{\n\tdisableSaving++;\n\n\tsetCursor(Qt::WaitCursor);\n\n\tCloseDialogs();\n\n\tClearListItems(ui->scenes);\n\tui->sources->Clear();\n\tClearQuickTransitions();\n\n\tcurrentTransitionUuid.clear();\n\ttransitions.clear();\n\ttransitionNameToUuids.clear();\n\ttransitionUuids.clear();\n\temit TransitionsCleared();\n\n\tClearProjectors();\n\n\tfor (int i = 0; i < MAX_CHANNELS; i++)\n\t\tobs_set_output_source(i, nullptr);\n\n\t/* Reset VCam to default to clear its private scene and any references\n\t * it holds. It will be reconfigured during loading. */\n\tif (vcamEnabled) {\n\t\tvcamConfig.type = VCamOutputType::ProgramView;\n\t\toutputHandler->UpdateVirtualCamOutputSource();\n\t}\n\n\tcollectionModuleData = nullptr;\n\tlastScene = nullptr;\n\tswapScene = nullptr;\n\tprogramScene = nullptr;\n\tprevFTBSource = nullptr;\n\n\tclipboard.clear();\n\tcopyFiltersSource_ = nullptr;\n\tcopyFilter = nullptr;\n\n\tauto cb = [](void *, obs_source_t *source) {\n\t\tobs_source_remove(source);\n\t\treturn true;\n\t};\n\n\tobs_enum_scenes(cb, nullptr);\n\tobs_enum_sources(cb, nullptr);\n\n\tfor (const auto &canvas : canvases) {\n\t\tobs_canvas_enum_scenes(canvas, cb, nullptr);\n\t}\n\n\tClearCanvases();\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP);\n\n\tundo_s.clear();\n\n\t/* using QEvent::DeferredDelete explicitly is the only way to ensure\n\t * that deleteLater events are processed at this point */\n\tQApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);\n\n\tdo {\n\t\tQApplication::sendPostedEvents(nullptr);\n\t} while (obs_wait_for_destroy_queue());\n\n\t/* Pump Qt events one final time to give remaining signals time to be\n\t * processed (since this happens after the destroy thread finishes and\n\t * the audio/video threads have processed their tasks). */\n\tQApplication::sendPostedEvents(nullptr);\n\n\tunsetCursor();\n\n\t/* If scene data wasn't actually cleared, e.g. faulty plugin holding a\n\t * reference, they will still be in the hash table, enumerate them and\n\t * store the names for logging purposes. */\n\tauto cb2 = [](void *param, obs_source_t *source) {\n\t\tauto orphans = static_cast<vector<string> *>(param);\n\t\torphans->push_back(obs_source_get_name(source));\n\t\treturn true;\n\t};\n\n\tvector<string> orphan_sources;\n\tobs_enum_sources(cb2, &orphan_sources);\n\n\tif (!orphan_sources.empty()) {\n\t\t/* Avoid logging list twice in case it gets called after\n\t\t * setting the flag the first time. */\n\t\tif (!clearingFailed) {\n\t\t\t/* This ugly mess exists to join a vector of strings\n\t\t\t * with a user-defined delimiter. */\n\t\t\tstring orphan_names =\n\t\t\t\tstd::accumulate(orphan_sources.begin(), orphan_sources.end(), string(\"\"),\n\t\t\t\t\t\t[](string a, string b) { return std::move(a) + \"\\n- \" + b; });\n\n\t\t\tblog(LOG_ERROR, \"Not all sources were cleared when clearing scene data:\\n%s\\n\",\n\t\t\t     orphan_names.c_str());\n\t\t}\n\n\t\t/* We do not decrement disableSaving here to avoid OBS\n\t\t * overwriting user data with garbage. */\n\t\tclearingFailed = true;\n\t} else {\n\t\tdisableSaving--;\n\n\t\tblog(LOG_INFO, \"All scene data cleared\");\n\t\tblog(LOG_INFO, \"------------------------------------------------\");\n\t}\n}\n\nvoid OBSBasic::ShowMissingFilesDialog(obs_missing_files_t *files)\n{\n\tif (obs_missing_files_count(files) > 0) {\n\t\t/* When loading the missing files dialog on launch, the\n\t\t* window hasn't fully initialized by this point on macOS,\n\t\t* so put this at the end of the current task queue. Fixes\n\t\t* a bug where the window is behind OBS on startup. */\n\t\tQTimer::singleShot(0, [this, files] {\n\t\t\tmissDialog = new OBSMissingFiles(files, this);\n\t\t\tmissDialog->setAttribute(Qt::WA_DeleteOnClose, true);\n\t\t\tmissDialog->show();\n\t\t\tmissDialog->raise();\n\t\t});\n\t} else {\n\t\tobs_missing_files_destroy(files);\n\n\t\t/* Only raise dialog if triggered manually */\n\t\tif (!disableSaving)\n\t\t\tOBSMessageBox::information(this, QTStr(\"MissingFiles.NoMissing.Title\"),\n\t\t\t\t\t\t   QTStr(\"MissingFiles.NoMissing.Text\"));\n\t}\n}\n\nvoid OBSBasic::on_actionShowMissingFiles_triggered()\n{\n\tobs_missing_files_t *files = obs_missing_files_create();\n\n\tauto cb_sources = [](void *data, obs_source_t *source) {\n\t\tAddMissingFiles(data, source);\n\t\treturn true;\n\t};\n\n\tobs_enum_all_sources(cb_sources, files);\n\tShowMissingFilesDialog(files);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_SceneItems.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n#include \"ColorSelect.hpp\"\n#include \"OBSProjector.hpp\"\n\n#include <components/VolumeControl.hpp>\n#include <dialogs/NameDialog.hpp>\n#include <dialogs/OBSBasicAdvAudio.hpp>\n#include <dialogs/OBSBasicSourceSelect.hpp>\n#include <utility/item-widget-helpers.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QWidgetAction>\n\n#include <sstream>\n\nusing namespace std;\n\nnamespace {\nbool isHiddenInMixer(obs_source_t *source)\n{\n\tOBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);\n\tbool hidden = obs_data_get_bool(priv_settings, \"mixer_hidden\");\n\n\treturn hidden;\n}\n\nvoid setHiddenInMixer(obs_source_t *source, bool hidden)\n{\n\tOBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);\n\tobs_data_set_bool(priv_settings, \"mixer_hidden\", hidden);\n}\n} // namespace\n\nstatic inline bool HasAudioDevices(const char *source_id)\n{\n\tconst char *output_id = source_id;\n\tobs_properties_t *props = obs_get_source_properties(output_id);\n\tsize_t count = 0;\n\n\tif (!props)\n\t\treturn false;\n\n\tobs_property_t *devices = obs_properties_get(props, \"device_id\");\n\tif (devices)\n\t\tcount = obs_property_list_item_count(devices);\n\n\tobs_properties_destroy(props);\n\n\treturn count != 0;\n}\n\nvoid OBSBasic::CreateFirstRunSources()\n{\n\tbool hasDesktopAudio = HasAudioDevices(App()->OutputAudioSource());\n\tbool hasInputAudio = HasAudioDevices(App()->InputAudioSource());\n\n#ifdef __APPLE__\n\t/* On macOS 13 and above, the SCK based audio capture provides a\n\t * better alternative to the device-based audio capture. */\n\tif (__builtin_available(macOS 13.0, *)) {\n\t\thasDesktopAudio = false;\n\t}\n#endif\n\n\tif (hasDesktopAudio)\n\t\tResetAudioDevice(App()->OutputAudioSource(), \"default\", Str(\"Basic.DesktopDevice1\"), 1);\n\tif (hasInputAudio)\n\t\tResetAudioDevice(App()->InputAudioSource(), \"default\", Str(\"Basic.AuxDevice1\"), 3);\n}\n\nOBSSceneItem OBSBasic::GetSceneItem(QListWidgetItem *item)\n{\n\treturn item ? GetOBSRef<OBSSceneItem>(item) : nullptr;\n}\n\nOBSSceneItem OBSBasic::GetCurrentSceneItem()\n{\n\treturn ui->sources->Get(GetTopSelectedSourceItem());\n}\n\nstatic void RenameListValues(QListWidget *listWidget, const QString &newName, const QString &prevName)\n{\n\tQList<QListWidgetItem *> items = listWidget->findItems(prevName, Qt::MatchExactly);\n\n\tfor (int i = 0; i < items.count(); i++)\n\t\titems[i]->setText(newName);\n}\n\nvoid OBSBasic::RenameSources(OBSSource source, QString newName, QString prevName)\n{\n\tRenameListValues(ui->scenes, newName, prevName);\n\n\tif (vcamConfig.type == VCamOutputType::SourceOutput && prevName == QString::fromStdString(vcamConfig.source))\n\t\tvcamConfig.source = newName.toStdString();\n\tif (vcamConfig.type == VCamOutputType::SceneOutput && prevName == QString::fromStdString(vcamConfig.scene))\n\t\tvcamConfig.scene = newName.toStdString();\n\n\tSaveProject();\n\n\tobs_scene_t *scene = obs_scene_from_source(source);\n\tif (scene)\n\t\tOBSProjector::UpdateMultiviewProjectors();\n\n\tUpdateContextBar();\n\tUpdatePreviewProgramIndicators();\n}\n\nbool OBSBasic::QueryRemoveSource(obs_source_t *source)\n{\n\tif (obs_source_get_type(source) == OBS_SOURCE_TYPE_SCENE && !obs_source_is_group(source)) {\n\t\tint count = ui->scenes->count();\n\n\t\tif (count == 1) {\n\t\t\tOBSMessageBox::information(this, QTStr(\"FinalScene.Title\"), QTStr(\"FinalScene.Text\"));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tconst char *name = obs_source_get_name(source);\n\n\tQString text = QTStr(\"ConfirmRemove.Text\").arg(QT_UTF8(name));\n\n\tQMessageBox remove_source(this);\n\tremove_source.setText(text);\n\tQPushButton *Yes = remove_source.addButton(QTStr(\"Yes\"), QMessageBox::YesRole);\n\tremove_source.setDefaultButton(Yes);\n\tremove_source.addButton(QTStr(\"No\"), QMessageBox::NoRole);\n\tremove_source.setIcon(QMessageBox::Question);\n\tremove_source.setWindowTitle(QTStr(\"ConfirmRemove.Title\"));\n\tremove_source.exec();\n\n\treturn Yes == remove_source.clickedButton();\n}\n\nvoid OBSBasic::ReorderSources(OBSScene scene)\n{\n\tif (scene != GetCurrentScene() || ui->sources->IgnoreReorder())\n\t\treturn;\n\n\tui->sources->ReorderItems();\n\tSaveProject();\n}\n\nvoid OBSBasic::RefreshSources(OBSScene scene)\n{\n\tif (scene != GetCurrentScene() || ui->sources->IgnoreReorder())\n\t\treturn;\n\n\tui->sources->RefreshItems();\n\tSaveProject();\n}\n\nvoid OBSBasic::SourceCreated(void *data, calldata_t *params)\n{\n\tobs_source_t *source = (obs_source_t *)calldata_ptr(params, \"source\");\n\n\tif (obs_scene_from_source(source) != NULL)\n\t\tQMetaObject::invokeMethod(static_cast<OBSBasic *>(data), \"AddScene\", WaitConnection(),\n\t\t\t\t\t  Q_ARG(OBSSource, OBSSource(source)));\n}\n\nvoid OBSBasic::SourceRemoved(void *data, calldata_t *params)\n{\n\tobs_source_t *source = (obs_source_t *)calldata_ptr(params, \"source\");\n\n\tif (obs_scene_from_source(source) != NULL)\n\t\tQMetaObject::invokeMethod(static_cast<OBSBasic *>(data), \"RemoveScene\",\n\t\t\t\t\t  Q_ARG(OBSSource, OBSSource(source)));\n}\n\nvoid OBSBasic::SourceRenamed(void *data, calldata_t *params)\n{\n\tobs_source_t *source = (obs_source_t *)calldata_ptr(params, \"source\");\n\tconst char *newName = calldata_string(params, \"new_name\");\n\tconst char *prevName = calldata_string(params, \"prev_name\");\n\n\tQMetaObject::invokeMethod(static_cast<OBSBasic *>(data), \"RenameSources\", Q_ARG(OBSSource, source),\n\t\t\t\t  Q_ARG(QString, QT_UTF8(newName)), Q_ARG(QString, QT_UTF8(prevName)));\n\n\tblog(LOG_INFO, \"Source '%s' renamed to '%s'\", prevName, newName);\n}\n\nextern char *get_new_source_name(const char *name, const char *format);\n\nvoid OBSBasic::ResetAudioDevice(const char *sourceId, const char *deviceId, const char *deviceDesc, int channel)\n{\n\tbool disable = deviceId && strcmp(deviceId, \"disabled\") == 0;\n\tOBSSourceAutoRelease source;\n\tOBSDataAutoRelease settings;\n\n\tsource = obs_get_output_source(channel);\n\tif (source) {\n\t\tif (disable) {\n\t\t\tobs_set_output_source(channel, nullptr);\n\t\t} else {\n\t\t\tsettings = obs_source_get_settings(source);\n\t\t\tconst char *oldId = obs_data_get_string(settings, \"device_id\");\n\t\t\tif (strcmp(oldId, deviceId) != 0) {\n\t\t\t\tobs_data_set_string(settings, \"device_id\", deviceId);\n\t\t\t\tobs_source_update(source, settings);\n\t\t\t}\n\t\t}\n\n\t} else if (!disable) {\n\t\tBPtr<char> name = get_new_source_name(deviceDesc, \"%s (%d)\");\n\n\t\tsettings = obs_data_create();\n\t\tobs_data_set_string(settings, \"device_id\", deviceId);\n\t\tsource = obs_source_create(sourceId, name, settings, nullptr);\n\n\t\tobs_set_output_source(channel, source);\n\t}\n}\n\nvoid OBSBasic::SetDeinterlacingMode()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\tobs_deinterlace_mode mode = (obs_deinterlace_mode)action->property(\"mode\").toInt();\n\tOBSSceneItem sceneItem = GetCurrentSceneItem();\n\tobs_source_t *source = obs_sceneitem_get_source(sceneItem);\n\n\tobs_deinterlace_mode oldMode = obs_source_get_deinterlace_mode(source);\n\tobs_source_set_deinterlace_mode(source, mode);\n\n\tauto undo_redo = [](const std::string &uuid, obs_deinterlace_mode val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tif (source)\n\t\t\tobs_source_set_deinterlace_mode(source, val);\n\t};\n\n\tconst char *uuid = obs_source_get_uuid(source);\n\n\tif (uuid && *uuid) {\n\t\tQString actionString = QTStr(\"Undo.DeinterlacingMode\").arg(obs_source_get_name(source));\n\n\t\tauto undoFunction = std::bind(undo_redo, std::placeholders::_1, oldMode);\n\t\tauto redoFunction = std::bind(undo_redo, std::placeholders::_1, mode);\n\n\t\tundo_s.add_action(actionString, undoFunction, redoFunction, uuid, uuid);\n\t}\n}\n\nvoid OBSBasic::SetDeinterlacingOrder()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\tobs_deinterlace_field_order order = (obs_deinterlace_field_order)action->property(\"order\").toInt();\n\tOBSSceneItem sceneItem = GetCurrentSceneItem();\n\tobs_source_t *source = obs_sceneitem_get_source(sceneItem);\n\n\tobs_deinterlace_field_order oldOrder = obs_source_get_deinterlace_field_order(source);\n\tobs_source_set_deinterlace_field_order(source, order);\n\n\tauto undo_redo = [](const std::string &uuid, obs_deinterlace_field_order val) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());\n\t\tif (source)\n\t\t\tobs_source_set_deinterlace_field_order(source, val);\n\t};\n\n\tconst char *uuid = obs_source_get_uuid(source);\n\n\tif (uuid && *uuid) {\n\t\tQString actionString = QTStr(\"Undo.DeinterlacingOrder\").arg(obs_source_get_name(source));\n\n\t\tauto undoFunction = std::bind(undo_redo, std::placeholders::_1, oldOrder);\n\t\tauto redoFunction = std::bind(undo_redo, std::placeholders::_1, order);\n\n\t\tundo_s.add_action(actionString, undoFunction, redoFunction, uuid, uuid);\n\t}\n}\n\nQMenu *OBSBasic::AddDeinterlacingMenu(QMenu *menu, obs_source_t *source)\n{\n\tobs_deinterlace_mode deinterlaceMode = obs_source_get_deinterlace_mode(source);\n\tobs_deinterlace_field_order deinterlaceOrder = obs_source_get_deinterlace_field_order(source);\n\tQAction *action;\n\n#define ADD_MODE(name, mode)                                                             \\\n\taction = menu->addAction(QTStr(\"\" name), this, &OBSBasic::SetDeinterlacingMode); \\\n\taction->setProperty(\"mode\", (int)mode);                                          \\\n\taction->setCheckable(true);                                                      \\\n\taction->setChecked(deinterlaceMode == mode);\n\n\tADD_MODE(\"Disable\", OBS_DEINTERLACE_MODE_DISABLE);\n\tADD_MODE(\"Deinterlacing.Discard\", OBS_DEINTERLACE_MODE_DISCARD);\n\tADD_MODE(\"Deinterlacing.Retro\", OBS_DEINTERLACE_MODE_RETRO);\n\tADD_MODE(\"Deinterlacing.Blend\", OBS_DEINTERLACE_MODE_BLEND);\n\tADD_MODE(\"Deinterlacing.Blend2x\", OBS_DEINTERLACE_MODE_BLEND_2X);\n\tADD_MODE(\"Deinterlacing.Linear\", OBS_DEINTERLACE_MODE_LINEAR);\n\tADD_MODE(\"Deinterlacing.Linear2x\", OBS_DEINTERLACE_MODE_LINEAR_2X);\n\tADD_MODE(\"Deinterlacing.Yadif\", OBS_DEINTERLACE_MODE_YADIF);\n\tADD_MODE(\"Deinterlacing.Yadif2x\", OBS_DEINTERLACE_MODE_YADIF_2X);\n#undef ADD_MODE\n\n\tmenu->addSeparator();\n\n#define ADD_ORDER(name, order)                                                                          \\\n\taction = menu->addAction(QTStr(\"Deinterlacing.\" name), this, &OBSBasic::SetDeinterlacingOrder); \\\n\taction->setProperty(\"order\", (int)order);                                                       \\\n\taction->setCheckable(true);                                                                     \\\n\taction->setChecked(deinterlaceOrder == order);\n\n\tADD_ORDER(\"TopFieldFirst\", OBS_DEINTERLACE_FIELD_ORDER_TOP);\n\tADD_ORDER(\"BottomFieldFirst\", OBS_DEINTERLACE_FIELD_ORDER_BOTTOM);\n#undef ADD_ORDER\n\n\treturn menu;\n}\n\nvoid OBSBasic::SetScaleFilter()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\tobs_scale_type mode = (obs_scale_type)action->property(\"mode\").toInt();\n\tOBSSceneItem sceneItem = GetCurrentSceneItem();\n\n\tobs_scale_type oldMode = obs_sceneitem_get_scale_filter(sceneItem);\n\tobs_sceneitem_set_scale_filter(sceneItem, mode);\n\n\tauto undo_redo = [](const std::string &uuid, int64_t id, obs_scale_type val) {\n\t\tOBSSourceAutoRelease s = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_scene_t *sc = obs_group_or_scene_from_source(s);\n\t\tobs_sceneitem_t *si = obs_scene_find_sceneitem_by_id(sc, id);\n\t\tif (si)\n\t\t\tobs_sceneitem_set_scale_filter(si, val);\n\t};\n\n\tOBSSource source = obs_sceneitem_get_source(sceneItem);\n\tOBSSource sceneSource = obs_scene_get_source(obs_sceneitem_get_scene(sceneItem));\n\tint64_t id = obs_sceneitem_get_id(sceneItem);\n\tconst char *name = obs_source_get_name(sceneSource);\n\tconst char *uuid = obs_source_get_uuid(sceneSource);\n\n\tif (uuid && *uuid) {\n\t\tQString actionString = QTStr(\"Undo.ScaleFiltering\").arg(obs_source_get_name(source), name);\n\n\t\tauto undoFunction = std::bind(undo_redo, std::placeholders::_1, id, oldMode);\n\t\tauto redoFunction = std::bind(undo_redo, std::placeholders::_1, id, mode);\n\n\t\tundo_s.add_action(actionString, undoFunction, redoFunction, uuid, uuid);\n\t}\n}\n\nQMenu *OBSBasic::AddScaleFilteringMenu(QMenu *menu, obs_sceneitem_t *item)\n{\n\tobs_scale_type scaleFilter = obs_sceneitem_get_scale_filter(item);\n\tQAction *action;\n\n#define ADD_MODE(name, mode)                                                       \\\n\taction = menu->addAction(QTStr(\"\" name), this, &OBSBasic::SetScaleFilter); \\\n\taction->setProperty(\"mode\", (int)mode);                                    \\\n\taction->setCheckable(true);                                                \\\n\taction->setChecked(scaleFilter == mode);\n\n\tADD_MODE(\"Disable\", OBS_SCALE_DISABLE);\n\tADD_MODE(\"ScaleFiltering.Point\", OBS_SCALE_POINT);\n\tADD_MODE(\"ScaleFiltering.Bilinear\", OBS_SCALE_BILINEAR);\n\tADD_MODE(\"ScaleFiltering.Bicubic\", OBS_SCALE_BICUBIC);\n\tADD_MODE(\"ScaleFiltering.Lanczos\", OBS_SCALE_LANCZOS);\n\tADD_MODE(\"ScaleFiltering.Area\", OBS_SCALE_AREA);\n#undef ADD_MODE\n\n\treturn menu;\n}\n\nvoid OBSBasic::SetBlendingMethod()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\tobs_blending_method method = (obs_blending_method)action->property(\"method\").toInt();\n\tOBSSceneItem sceneItem = GetCurrentSceneItem();\n\n\tobs_blending_method oldMethod = obs_sceneitem_get_blending_method(sceneItem);\n\tobs_sceneitem_set_blending_method(sceneItem, method);\n\n\tauto undo_redo = [](const std::string &uuid, int64_t id, obs_blending_method val) {\n\t\tOBSSourceAutoRelease s = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_scene_t *sc = obs_group_or_scene_from_source(s);\n\t\tobs_sceneitem_t *si = obs_scene_find_sceneitem_by_id(sc, id);\n\t\tif (si)\n\t\t\tobs_sceneitem_set_blending_method(si, val);\n\t};\n\n\tOBSSource source = obs_sceneitem_get_source(sceneItem);\n\tOBSSource sceneSource = obs_scene_get_source(obs_sceneitem_get_scene(sceneItem));\n\tint64_t id = obs_sceneitem_get_id(sceneItem);\n\tconst char *name = obs_source_get_name(sceneSource);\n\tconst char *uuid = obs_source_get_uuid(sceneSource);\n\n\tif (uuid && *uuid) {\n\t\tQString actionString = QTStr(\"Undo.BlendingMethod\").arg(obs_source_get_name(source), name);\n\n\t\tauto undoFunction = std::bind(undo_redo, std::placeholders::_1, id, oldMethod);\n\t\tauto redoFunction = std::bind(undo_redo, std::placeholders::_1, id, method);\n\n\t\tundo_s.add_action(actionString, undoFunction, redoFunction, uuid, uuid);\n\t}\n}\n\nQMenu *OBSBasic::AddBlendingMethodMenu(QMenu *menu, obs_sceneitem_t *item)\n{\n\tobs_blending_method blendingMethod = obs_sceneitem_get_blending_method(item);\n\tQAction *action;\n\n#define ADD_MODE(name, method)                                                        \\\n\taction = menu->addAction(QTStr(\"\" name), this, &OBSBasic::SetBlendingMethod); \\\n\taction->setProperty(\"method\", (int)method);                                   \\\n\taction->setCheckable(true);                                                   \\\n\taction->setChecked(blendingMethod == method);\n\n\tADD_MODE(\"BlendingMethod.Default\", OBS_BLEND_METHOD_DEFAULT);\n\tADD_MODE(\"BlendingMethod.SrgbOff\", OBS_BLEND_METHOD_SRGB_OFF);\n#undef ADD_MODE\n\n\treturn menu;\n}\n\nvoid OBSBasic::SetBlendingMode()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\tobs_blending_type mode = (obs_blending_type)action->property(\"mode\").toInt();\n\tOBSSceneItem sceneItem = GetCurrentSceneItem();\n\n\tobs_blending_type oldMode = obs_sceneitem_get_blending_mode(sceneItem);\n\tobs_sceneitem_set_blending_mode(sceneItem, mode);\n\n\tauto undo_redo = [](const std::string &uuid, int64_t id, obs_blending_type val) {\n\t\tOBSSourceAutoRelease s = obs_get_source_by_uuid(uuid.c_str());\n\t\tobs_scene_t *sc = obs_group_or_scene_from_source(s);\n\t\tobs_sceneitem_t *si = obs_scene_find_sceneitem_by_id(sc, id);\n\t\tif (si)\n\t\t\tobs_sceneitem_set_blending_mode(si, val);\n\t};\n\n\tOBSSource source = obs_sceneitem_get_source(sceneItem);\n\tOBSSource sceneSource = obs_scene_get_source(obs_sceneitem_get_scene(sceneItem));\n\tint64_t id = obs_sceneitem_get_id(sceneItem);\n\tconst char *name = obs_source_get_name(sceneSource);\n\tconst char *uuid = obs_source_get_uuid(sceneSource);\n\n\tif (uuid && *uuid) {\n\t\tQString actionString = QTStr(\"Undo.BlendingMode\").arg(obs_source_get_name(source), name);\n\n\t\tauto undoFunction = std::bind(undo_redo, std::placeholders::_1, id, oldMode);\n\t\tauto redoFunction = std::bind(undo_redo, std::placeholders::_1, id, mode);\n\n\t\tundo_s.add_action(actionString, undoFunction, redoFunction, uuid, uuid);\n\t}\n}\n\nQMenu *OBSBasic::AddBlendingModeMenu(QMenu *menu, obs_sceneitem_t *item)\n{\n\tobs_blending_type blendingMode = obs_sceneitem_get_blending_mode(item);\n\tQAction *action;\n\n#define ADD_MODE(name, mode)                                                        \\\n\taction = menu->addAction(QTStr(\"\" name), this, &OBSBasic::SetBlendingMode); \\\n\taction->setProperty(\"mode\", (int)mode);                                     \\\n\taction->setCheckable(true);                                                 \\\n\taction->setChecked(blendingMode == mode);\n\n\tADD_MODE(\"BlendingMode.Normal\", OBS_BLEND_NORMAL);\n\tADD_MODE(\"BlendingMode.Additive\", OBS_BLEND_ADDITIVE);\n\tADD_MODE(\"BlendingMode.Subtract\", OBS_BLEND_SUBTRACT);\n\tADD_MODE(\"BlendingMode.Screen\", OBS_BLEND_SCREEN);\n\tADD_MODE(\"BlendingMode.Multiply\", OBS_BLEND_MULTIPLY);\n\tADD_MODE(\"BlendingMode.Lighten\", OBS_BLEND_LIGHTEN);\n\tADD_MODE(\"BlendingMode.Darken\", OBS_BLEND_DARKEN);\n#undef ADD_MODE\n\n\treturn menu;\n}\n\nQMenu *OBSBasic::AddBackgroundColorMenu(QMenu *menu, QWidgetAction *widgetAction, ColorSelect *select,\n\t\t\t\t\tobs_sceneitem_t *item)\n{\n\tQAction *action;\n\n\tmenu->setStyleSheet(QString(\"*[bgColor=\\\"1\\\"]{background-color:rgba(255,68,68,33%);}\"\n\t\t\t\t    \"*[bgColor=\\\"2\\\"]{background-color:rgba(255,255,68,33%);}\"\n\t\t\t\t    \"*[bgColor=\\\"3\\\"]{background-color:rgba(68,255,68,33%);}\"\n\t\t\t\t    \"*[bgColor=\\\"4\\\"]{background-color:rgba(68,255,255,33%);}\"\n\t\t\t\t    \"*[bgColor=\\\"5\\\"]{background-color:rgba(68,68,255,33%);}\"\n\t\t\t\t    \"*[bgColor=\\\"6\\\"]{background-color:rgba(255,68,255,33%);}\"\n\t\t\t\t    \"*[bgColor=\\\"7\\\"]{background-color:rgba(68,68,68,33%);}\"\n\t\t\t\t    \"*[bgColor=\\\"8\\\"]{background-color:rgba(255,255,255,33%);}\"));\n\n\tobs_data_t *privData = obs_sceneitem_get_private_settings(item);\n\tobs_data_release(privData);\n\n\tobs_data_set_default_int(privData, \"color-preset\", 0);\n\tint preset = obs_data_get_int(privData, \"color-preset\");\n\n\taction = menu->addAction(QTStr(\"Clear\"), this, &OBSBasic::ColorChange);\n\taction->setCheckable(true);\n\taction->setProperty(\"bgColor\", 0);\n\taction->setChecked(preset == 0);\n\n\taction = menu->addAction(QTStr(\"CustomColor\"), this, &OBSBasic::ColorChange);\n\taction->setCheckable(true);\n\taction->setProperty(\"bgColor\", 1);\n\taction->setChecked(preset == 1);\n\n\tmenu->addSeparator();\n\n\twidgetAction->setDefaultWidget(select);\n\n\tfor (int i = 1; i < 9; i++) {\n\t\tstringstream button;\n\t\tbutton << \"preset\" << i;\n\t\tQPushButton *colorButton = select->findChild<QPushButton *>(button.str().c_str());\n\t\tif (preset == i + 1)\n\t\t\tcolorButton->setStyleSheet(\"border: 2px solid black\");\n\n\t\tcolorButton->setProperty(\"bgColor\", i);\n\t\tconnect(colorButton, &QPushButton::released, this, &OBSBasic::ColorChange);\n\t}\n\n\tmenu->addAction(widgetAction);\n\n\treturn menu;\n}\n\nvoid OBSBasic::CreateSourcePopupMenu(int idx, bool preview)\n{\n\tQMenu popup(this);\n\tdelete previewProjectorSource;\n\tdelete sourceProjector;\n\tdelete scaleFilteringMenu;\n\tdelete blendingMethodMenu;\n\tdelete blendingModeMenu;\n\tdelete colorMenu;\n\tdelete colorWidgetAction;\n\tdelete colorSelect;\n\tdelete deinterlaceMenu;\n\n\tOBSSceneItem sceneItem;\n\tobs_source_t *source;\n\tuint32_t flags;\n\tbool isAsyncVideo = false;\n\tbool hasAudio = false;\n\tbool hasVideo = false;\n\n\tbool sourceSelected = idx != -1;\n\n\tif (sourceSelected) {\n\t\tsceneItem = ui->sources->Get(idx);\n\t\tsource = obs_sceneitem_get_source(sceneItem);\n\t\tflags = obs_source_get_output_flags(source);\n\t\tisAsyncVideo = (flags & OBS_SOURCE_ASYNC_VIDEO) == OBS_SOURCE_ASYNC_VIDEO;\n\t\thasAudio = (flags & OBS_SOURCE_AUDIO) == OBS_SOURCE_AUDIO;\n\t\thasVideo = (flags & OBS_SOURCE_VIDEO) == OBS_SOURCE_VIDEO;\n\t}\n\n\t// Add new source\n\tQPointer<QMenu> addSourceMenu = CreateAddSourcePopupMenu();\n\tif (addSourceMenu) {\n\t\tpopup.addMenu(addSourceMenu);\n\t\tpopup.addSeparator();\n\t}\n\n\t// Preview menu entries\n\tif (preview) {\n\t\tQAction *action =\n\t\t\tpopup.addAction(QTStr(\"Basic.Main.PreviewConextMenu.Enable\"), this, &OBSBasic::TogglePreview);\n\t\taction->setCheckable(true);\n\t\taction->setChecked(obs_display_enabled(ui->preview->GetDisplay()));\n\t\tif (IsPreviewProgramMode())\n\t\t\taction->setEnabled(false);\n\n\t\tpopup.addAction(ui->actionLockPreview);\n\t\tpopup.addMenu(ui->scalingMenu);\n\n\t\tpopup.addSeparator();\n\t}\n\n\t// Projector menu entries\n\tif (preview) {\n\t\tpreviewProjectorSource = new QMenu(QTStr(\"Projector.Open.Preview\"));\n\t\tAddProjectorMenuMonitors(previewProjectorSource, this, &OBSBasic::OpenPreviewProjector);\n\t\tpreviewProjectorSource->addSeparator();\n\t\tpreviewProjectorSource->addAction(QTStr(\"Projector.Window\"), this, &OBSBasic::OpenPreviewWindow);\n\n\t\tpopup.addMenu(previewProjectorSource);\n\t}\n\n\tif (hasVideo) {\n\t\tsourceProjector = new QMenu(QTStr(\"Projector.Open.Source\"));\n\t\tAddProjectorMenuMonitors(sourceProjector, this, &OBSBasic::OpenSourceProjector);\n\t\tsourceProjector->addSeparator();\n\t\tsourceProjector->addAction(QTStr(\"Projector.Window\"), this, &OBSBasic::OpenSourceWindow);\n\n\t\tpopup.addMenu(sourceProjector);\n\t}\n\n\tpopup.addSeparator();\n\n\t// Screenshot menu entries\n\tif (preview) {\n\t\tpopup.addAction(QTStr(\"Screenshot.Preview\"), this, &OBSBasic::ScreenshotScene);\n\t}\n\n\tif (hasVideo) {\n\t\tpopup.addAction(QTStr(\"Screenshot.Source\"), this, &OBSBasic::ScreenshotSelectedSource);\n\t}\n\n\tpopup.addSeparator();\n\n\tif (sourceSelected) {\n\t\t// Sources list menu entries\n\t\tif (!preview) {\n\t\t\tcolorMenu = new QMenu(QTStr(\"ChangeBG\"));\n\t\t\tcolorWidgetAction = new QWidgetAction(colorMenu);\n\t\t\tcolorSelect = new ColorSelect(colorMenu);\n\t\t\tpopup.addMenu(AddBackgroundColorMenu(colorMenu, colorWidgetAction, colorSelect, sceneItem));\n\n\t\t\tif (hasAudio) {\n\t\t\t\tbool isHidden = isHiddenInMixer(source);\n\n\t\t\t\tQAction *actionHideMixer =\n\t\t\t\t\tpopup.addAction(QTStr(\"HideMixer\"), this, [source, isHidden]() {\n\t\t\t\t\t\tsetHiddenInMixer(source, !isHidden);\n\n\t\t\t\t\t\tOBSBasic *main = OBSBasic::Get();\n\t\t\t\t\t\temit main->mixerStatusChanged(obs_source_get_uuid(source));\n\t\t\t\t\t});\n\t\t\t\tactionHideMixer->setCheckable(true);\n\t\t\t\tactionHideMixer->setChecked(isHidden);\n\t\t\t}\n\t\t\tpopup.addSeparator();\n\t\t}\n\n\t\t// Scene item menu entries\n\t\tif (hasVideo && source) {\n\t\t\tscaleFilteringMenu = new QMenu(QTStr(\"ScaleFiltering\"));\n\t\t\tpopup.addMenu(AddScaleFilteringMenu(scaleFilteringMenu, sceneItem));\n\t\t\tblendingModeMenu = new QMenu(QTStr(\"BlendingMode\"));\n\t\t\tpopup.addMenu(AddBlendingModeMenu(blendingModeMenu, sceneItem));\n\t\t\tblendingMethodMenu = new QMenu(QTStr(\"BlendingMethod\"));\n\t\t\tpopup.addMenu(AddBlendingMethodMenu(blendingMethodMenu, sceneItem));\n\t\t\tif (isAsyncVideo) {\n\t\t\t\tdeinterlaceMenu = new QMenu(QTStr(\"Deinterlacing\"));\n\t\t\t\tpopup.addMenu(AddDeinterlacingMenu(deinterlaceMenu, source));\n\t\t\t}\n\n\t\t\tpopup.addMenu(CreateVisibilityTransitionMenu(true));\n\t\t\tpopup.addMenu(CreateVisibilityTransitionMenu(false));\n\n\t\t\tpopup.addSeparator();\n\n\t\t\tQAction *resizeOutput = popup.addAction(QTStr(\"ResizeOutputSizeOfSource\"), this,\n\t\t\t\t\t\t\t\t&OBSBasic::ResizeOutputSizeOfSource);\n\n\t\t\tint width = obs_source_get_width(source);\n\t\t\tint height = obs_source_get_height(source);\n\n\t\t\tresizeOutput->setEnabled(!obs_video_active());\n\n\t\t\tif (width < 32 || height < 32)\n\t\t\t\tresizeOutput->setEnabled(false);\n\t\t}\n\n\t\tpopup.addSeparator();\n\n\t\tpopup.addMenu(ui->orderMenu);\n\n\t\tif (hasVideo) {\n\t\t\tpopup.addMenu(ui->transformMenu);\n\t\t}\n\n\t\tpopup.addSeparator();\n\n\t\t// Source grouping\n\t\tif (ui->sources->MultipleBaseSelected()) {\n\t\t\tpopup.addSeparator();\n\t\t\tpopup.addAction(QTStr(\"Basic.Main.GroupItems\"), ui->sources, &SourceTree::GroupSelectedItems);\n\n\t\t} else if (ui->sources->GroupsSelected()) {\n\t\t\tpopup.addSeparator();\n\t\t\tpopup.addAction(QTStr(\"Basic.Main.Ungroup\"), ui->sources, &SourceTree::UngroupSelectedGroups);\n\t\t}\n\t\tpopup.addSeparator();\n\n\t\tpopup.addAction(ui->actionCopySource);\n\t\tpopup.addAction(ui->actionPasteRef);\n\t\tpopup.addAction(ui->actionPasteDup);\n\t\tpopup.addSeparator();\n\n\t\tif (hasVideo || hasAudio) {\n\t\t\tpopup.addAction(ui->actionCopyFilters);\n\t\t\tpopup.addAction(ui->actionPasteFilters);\n\t\t\tpopup.addSeparator();\n\t\t}\n\n\t\tpopup.addAction(ui->actionRemoveSource);\n\t\tpopup.addAction(renameSource);\n\t\tpopup.addSeparator();\n\n\t\tif (flags && flags & OBS_SOURCE_INTERACTION)\n\t\t\tpopup.addAction(QTStr(\"Interact\"), this, &OBSBasic::on_actionInteract_triggered);\n\n\t\tpopup.addAction(QTStr(\"Filters\"), this, [&]() { OpenFilters(); });\n\t\tQAction *action =\n\t\t\tpopup.addAction(QTStr(\"Properties\"), this, &OBSBasic::on_actionSourceProperties_triggered);\n\t\taction->setEnabled(obs_source_configurable(source));\n\t} else {\n\t\tpopup.addAction(ui->actionPasteRef);\n\t\tpopup.addAction(ui->actionPasteDup);\n\t}\n\n\tpopup.exec(QCursor::pos());\n}\n\nvoid OBSBasic::actionOpenSourceFilters()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\tif (!action->property(\"source\").isValid()) {\n\t\treturn;\n\t}\n\n\tobs_source_t *source = action->property(\"source\").value<OBSSource>();\n\n\tCreateFiltersWindow(source);\n}\n\nvoid OBSBasic::actionOpenSourceProperties()\n{\n\tQAction *action = reinterpret_cast<QAction *>(sender());\n\tif (!action->property(\"source\").isValid()) {\n\t\treturn;\n\t}\n\n\tobs_source_t *source = action->property(\"source\").value<OBSSource>();\n\n\tCreatePropertiesWindow(source);\n}\n\nvoid OBSBasic::on_sources_customContextMenuRequested(const QPoint &pos)\n{\n\tif (ui->scenes->count()) {\n\t\tQModelIndex idx = ui->sources->indexAt(pos);\n\t\tCreateSourcePopupMenu(idx.row(), false);\n\t}\n}\n\nstatic inline bool should_show_properties(obs_source_t *source, const char *id)\n{\n\tif (!source)\n\t\treturn false;\n\tif (strcmp(id, \"group\") == 0)\n\t\treturn false;\n\tif (!obs_source_configurable(source))\n\t\treturn false;\n\n\tuint32_t caps = obs_source_get_output_flags(source);\n\tif ((caps & OBS_SOURCE_CAP_DONT_SHOW_PROPERTIES) != 0)\n\t\treturn false;\n\n\treturn true;\n}\n\nvoid OBSBasic::AddSource(const char *id)\n{\n\tif (id && *id) {\n\t\tOBSBasicSourceSelect sourceSelect(this, id, undo_s);\n\t\tsourceSelect.exec();\n\t\tif (should_show_properties(sourceSelect.newSource, id)) {\n\t\t\tCreatePropertiesWindow(sourceSelect.newSource);\n\t\t}\n\t}\n}\n\nQMenu *OBSBasic::CreateAddSourcePopupMenu()\n{\n\tconst char *unversioned_type;\n\tconst char *type;\n\tbool foundValues = false;\n\tbool foundDeprecated = false;\n\tsize_t idx = 0;\n\n\tQMenu *popup = new QMenu(QTStr(\"AddSource\"), this);\n\tQMenu *deprecated = new QMenu(QTStr(\"Deprecated\"), popup);\n\n\tauto getActionAfter = [](QMenu *menu, const QString &name) {\n\t\tQList<QAction *> actions = menu->actions();\n\n\t\tfor (QAction *menuAction : actions) {\n\t\t\tif (menuAction->text().compare(name, Qt::CaseInsensitive) >= 0)\n\t\t\t\treturn menuAction;\n\t\t}\n\n\t\treturn (QAction *)nullptr;\n\t};\n\n\tauto addSource = [this, getActionAfter](QMenu *popup, const char *type, const char *name) {\n\t\tQString qname = QT_UTF8(name);\n\t\tQAction *popupItem = new QAction(qname, this);\n\t\tconnect(popupItem, &QAction::triggered, this, [this, type]() { AddSource(type); });\n\n\t\tQIcon icon;\n\n\t\tif (strcmp(type, \"scene\") == 0)\n\t\t\ticon = GetSceneIcon();\n\t\telse\n\t\t\ticon = GetSourceIcon(type);\n\n\t\tpopupItem->setIcon(icon);\n\n\t\tQAction *after = getActionAfter(popup, qname);\n\t\tpopup->insertAction(after, popupItem);\n\t};\n\n\twhile (obs_enum_input_types2(idx++, &type, &unversioned_type)) {\n\t\tconst char *name = obs_source_get_display_name(type);\n\t\tuint32_t caps = obs_get_source_output_flags(type);\n\n\t\tif ((caps & OBS_SOURCE_CAP_DISABLED) != 0)\n\t\t\tcontinue;\n\n\t\tif ((caps & OBS_SOURCE_DEPRECATED) == 0) {\n\t\t\taddSource(popup, unversioned_type, name);\n\t\t} else {\n\t\t\taddSource(deprecated, unversioned_type, name);\n\t\t\tfoundDeprecated = true;\n\t\t}\n\t\tfoundValues = true;\n\t}\n\n\taddSource(popup, \"scene\", Str(\"Basic.Scene\"));\n\n\tpopup->addSeparator();\n\tQAction *addGroup = new QAction(QTStr(\"Group\"), this);\n\taddGroup->setIcon(GetGroupIcon());\n\tconnect(addGroup, &QAction::triggered, this, [this]() { AddSource(\"group\"); });\n\tpopup->addAction(addGroup);\n\n\tif (!foundDeprecated) {\n\t\tdelete deprecated;\n\t\tdeprecated = nullptr;\n\t}\n\n\tif (!foundValues) {\n\t\tdelete popup;\n\t\tpopup = nullptr;\n\n\t} else if (foundDeprecated) {\n\t\tpopup->addSeparator();\n\t\tpopup->addMenu(deprecated);\n\t}\n\n\treturn popup;\n}\n\nvoid OBSBasic::AddSourcePopupMenu(const QPoint &pos)\n{\n\tif (!GetCurrentScene()) {\n\t\t// Tell the user he needs a scene first (help beginners).\n\t\tOBSMessageBox::information(this, QTStr(\"Basic.Main.AddSourceHelp.Title\"),\n\t\t\t\t\t   QTStr(\"Basic.Main.AddSourceHelp.Text\"));\n\t\treturn;\n\t}\n\n\tQScopedPointer<QMenu> popup(CreateAddSourcePopupMenu());\n\tif (popup)\n\t\tpopup->exec(pos);\n}\n\nvoid OBSBasic::on_actionAddSource_triggered()\n{\n\tAddSourcePopupMenu(QCursor::pos());\n}\n\nstatic bool remove_items(obs_scene_t *, obs_sceneitem_t *item, void *param)\n{\n\tvector<OBSSceneItem> &items = *static_cast<vector<OBSSceneItem> *>(param);\n\n\tif (obs_sceneitem_selected(item)) {\n\t\titems.emplace_back(item);\n\t} else if (obs_sceneitem_is_group(item)) {\n\t\tobs_sceneitem_group_enum_items(item, remove_items, &items);\n\t}\n\treturn true;\n};\n\nvoid OBSBasic::on_actionRemoveSource_triggered()\n{\n\tvector<OBSSceneItem> items;\n\tOBSScene scene = GetCurrentScene();\n\tobs_source_t *scene_source = obs_scene_get_source(scene);\n\n\tobs_scene_enum_items(scene, remove_items, &items);\n\n\tif (!items.size())\n\t\treturn;\n\n\t/* ------------------------------------- */\n\t/* confirm action with user              */\n\n\tbool confirmed = false;\n\n\tif (items.size() > 1) {\n\t\tQString text = QTStr(\"ConfirmRemove.TextMultiple\").arg(QString::number(items.size()));\n\n\t\tQMessageBox remove_items(this);\n\t\tremove_items.setText(text);\n\t\tQPushButton *Yes = remove_items.addButton(QTStr(\"Yes\"), QMessageBox::YesRole);\n\t\tremove_items.setDefaultButton(Yes);\n\t\tremove_items.addButton(QTStr(\"No\"), QMessageBox::NoRole);\n\t\tremove_items.setIcon(QMessageBox::Question);\n\t\tremove_items.setWindowTitle(QTStr(\"ConfirmRemove.Title\"));\n\t\tremove_items.exec();\n\n\t\tconfirmed = Yes == remove_items.clickedButton();\n\t} else {\n\t\tOBSSceneItem &item = items[0];\n\t\tobs_source_t *source = obs_sceneitem_get_source(item);\n\t\tif (source && QueryRemoveSource(source))\n\t\t\tconfirmed = true;\n\t}\n\tif (!confirmed)\n\t\treturn;\n\n\t/* ----------------------------------------------- */\n\t/* save undo data                                  */\n\n\tOBSData undo_data = BackupScene(scene_source);\n\n\t/* ----------------------------------------------- */\n\t/* remove items                                    */\n\n\tfor (auto &item : items)\n\t\tobs_sceneitem_remove(item);\n\n\t/* ----------------------------------------------- */\n\t/* save redo data                                  */\n\n\tOBSData redo_data = BackupScene(scene_source);\n\n\t/* ----------------------------------------------- */\n\t/* add undo/redo action                            */\n\n\tQString action_name;\n\tif (items.size() > 1) {\n\t\taction_name = QTStr(\"Undo.Sources.Multi\").arg(QString::number(items.size()));\n\t} else {\n\t\tQString str = QTStr(\"Undo.Delete\");\n\t\taction_name = str.arg(obs_source_get_name(obs_sceneitem_get_source(items[0])));\n\t}\n\n\tCreateSceneUndoRedoAction(action_name, undo_data, redo_data);\n}\n\nvoid OBSBasic::on_actionInteract_triggered()\n{\n\tOBSSceneItem item = GetCurrentSceneItem();\n\tOBSSource source = obs_sceneitem_get_source(item);\n\n\tif (source)\n\t\tCreateInteractionWindow(source);\n}\n\nvoid OBSBasic::on_actionSourceProperties_triggered()\n{\n\tOBSSceneItem item = GetCurrentSceneItem();\n\tOBSSource source = obs_sceneitem_get_source(item);\n\n\tif (source)\n\t\tCreatePropertiesWindow(source);\n}\n\nvoid OBSBasic::on_actionSourceUp_triggered()\n{\n\tMoveSceneItem(OBS_ORDER_MOVE_UP, QTStr(\"Undo.MoveUp\"));\n}\n\nvoid OBSBasic::on_actionSourceDown_triggered()\n{\n\tMoveSceneItem(OBS_ORDER_MOVE_DOWN, QTStr(\"Undo.MoveDown\"));\n}\n\nvoid OBSBasic::on_actionMoveUp_triggered()\n{\n\tMoveSceneItem(OBS_ORDER_MOVE_UP, QTStr(\"Undo.MoveUp\"));\n}\n\nvoid OBSBasic::on_actionMoveDown_triggered()\n{\n\tMoveSceneItem(OBS_ORDER_MOVE_DOWN, QTStr(\"Undo.MoveDown\"));\n}\n\nvoid OBSBasic::on_actionMoveToTop_triggered()\n{\n\tMoveSceneItem(OBS_ORDER_MOVE_TOP, QTStr(\"Undo.MoveToTop\"));\n}\n\nvoid OBSBasic::on_actionMoveToBottom_triggered()\n{\n\tMoveSceneItem(OBS_ORDER_MOVE_BOTTOM, QTStr(\"Undo.MoveToBottom\"));\n}\n\nvoid OBSBasic::OpenFilters(OBSSource source)\n{\n\tif (source == nullptr) {\n\t\tOBSSceneItem item = GetCurrentSceneItem();\n\t\tsource = obs_sceneitem_get_source(item);\n\t}\n\tCreateFiltersWindow(source);\n}\n\nvoid OBSBasic::OpenProperties(OBSSource source)\n{\n\tif (source == nullptr) {\n\t\tOBSSceneItem item = GetCurrentSceneItem();\n\t\tsource = obs_sceneitem_get_source(item);\n\t}\n\tCreatePropertiesWindow(source);\n}\n\nvoid OBSBasic::OpenInteraction(OBSSource source)\n{\n\tif (source == nullptr) {\n\t\tOBSSceneItem item = GetCurrentSceneItem();\n\t\tsource = obs_sceneitem_get_source(item);\n\t}\n\tCreateInteractionWindow(source);\n}\n\nvoid OBSBasic::OpenEditTransform(OBSSceneItem item)\n{\n\tif (!item)\n\t\titem = GetCurrentSceneItem();\n\tif (!item)\n\t\treturn;\n\tCreateEditTransformWindow(item);\n}\n\nint OBSBasic::GetTopSelectedSourceItem()\n{\n\tQModelIndexList selectedItems = ui->sources->selectionModel()->selectedIndexes();\n\treturn selectedItems.count() ? selectedItems[0].row() : -1;\n}\n\nQModelIndexList OBSBasic::GetAllSelectedSourceItems()\n{\n\treturn ui->sources->selectionModel()->selectedIndexes();\n}\n\nvoid OBSBasic::on_actionEditTransform_triggered()\n{\n\tconst auto item = GetCurrentSceneItem();\n\tif (!item)\n\t\treturn;\n\tCreateEditTransformWindow(item);\n}\n\nvoid undo_redo(const std::string &data)\n{\n\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\tOBSSourceAutoRelease source = obs_get_source_by_uuid(obs_data_get_string(dat, \"scene_uuid\"));\n\tOBSBasic::Get()->SetCurrentScene(source.Get(), true);\n\n\tobs_scene_load_transform_states(data.c_str());\n}\n\nstatic void GetItemBox(obs_sceneitem_t *item, vec3 &tl, vec3 &br)\n{\n\tmatrix4 boxTransform;\n\tobs_sceneitem_get_box_transform(item, &boxTransform);\n\n\tvec3_set(&tl, M_INFINITE, M_INFINITE, 0.0f);\n\tvec3_set(&br, -M_INFINITE, -M_INFINITE, 0.0f);\n\n\tauto GetMinPos = [&](float x, float y) {\n\t\tvec3 pos;\n\t\tvec3_set(&pos, x, y, 0.0f);\n\t\tvec3_transform(&pos, &pos, &boxTransform);\n\t\tvec3_min(&tl, &tl, &pos);\n\t\tvec3_max(&br, &br, &pos);\n\t};\n\n\tGetMinPos(0.0f, 0.0f);\n\tGetMinPos(1.0f, 0.0f);\n\tGetMinPos(0.0f, 1.0f);\n\tGetMinPos(1.0f, 1.0f);\n}\n\nstatic vec3 GetItemTL(obs_sceneitem_t *item)\n{\n\tvec3 tl, br;\n\tGetItemBox(item, tl, br);\n\treturn tl;\n}\n\nstatic void SetItemTL(obs_sceneitem_t *item, const vec3 &tl)\n{\n\tvec3 newTL;\n\tvec2 pos;\n\n\tobs_sceneitem_get_pos(item, &pos);\n\tnewTL = GetItemTL(item);\n\tpos.x += tl.x - newTL.x;\n\tpos.y += tl.y - newTL.y;\n\tobs_sceneitem_set_pos(item, &pos);\n}\n\nstatic bool RotateSelectedSources(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tif (obs_sceneitem_is_group(item))\n\t\tobs_sceneitem_group_enum_items(item, RotateSelectedSources, param);\n\tif (!obs_sceneitem_selected(item))\n\t\treturn true;\n\tif (obs_sceneitem_locked(item))\n\t\treturn true;\n\n\tfloat rot = *static_cast<float *>(param);\n\n\tvec3 tl = GetItemTL(item);\n\n\trot += obs_sceneitem_get_rot(item);\n\tif (rot >= 360.0f)\n\t\trot -= 360.0f;\n\telse if (rot <= -360.0f)\n\t\trot += 360.0f;\n\tobs_sceneitem_set_rot(item, rot);\n\n\tobs_sceneitem_force_update_transform(item);\n\n\tSetItemTL(item, tl);\n\n\treturn true;\n};\n\nvoid OBSBasic::on_actionRotate90CW_triggered()\n{\n\tfloat f90CW = 90.0f;\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tobs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f90CW);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(\n\t\tQTStr(\"Undo.Transform.Rotate\").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),\n\t\tundo_redo, undo_redo, undo_data, redo_data);\n}\n\nvoid OBSBasic::on_actionRotate90CCW_triggered()\n{\n\tfloat f90CCW = -90.0f;\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tobs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f90CCW);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(\n\t\tQTStr(\"Undo.Transform.Rotate\").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),\n\t\tundo_redo, undo_redo, undo_data, redo_data);\n}\n\nvoid OBSBasic::on_actionRotate180_triggered()\n{\n\tfloat f180 = 180.0f;\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tobs_scene_enum_items(GetCurrentScene(), RotateSelectedSources, &f180);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(\n\t\tQTStr(\"Undo.Transform.Rotate\").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),\n\t\tundo_redo, undo_redo, undo_data, redo_data);\n}\n\nstatic bool MultiplySelectedItemScale(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tvec2 &mul = *static_cast<vec2 *>(param);\n\n\tif (obs_sceneitem_is_group(item))\n\t\tobs_sceneitem_group_enum_items(item, MultiplySelectedItemScale, param);\n\tif (!obs_sceneitem_selected(item))\n\t\treturn true;\n\tif (obs_sceneitem_locked(item))\n\t\treturn true;\n\n\tvec3 tl = GetItemTL(item);\n\n\tvec2 scale;\n\tobs_sceneitem_get_scale(item, &scale);\n\tvec2_mul(&scale, &scale, &mul);\n\tobs_sceneitem_set_scale(item, &scale);\n\n\tobs_sceneitem_force_update_transform(item);\n\n\tSetItemTL(item, tl);\n\n\treturn true;\n}\n\nvoid OBSBasic::on_actionFlipHorizontal_triggered()\n{\n\tvec2 scale;\n\tvec2_set(&scale, -1.0f, 1.0f);\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tobs_scene_enum_items(GetCurrentScene(), MultiplySelectedItemScale, &scale);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(\n\t\tQTStr(\"Undo.Transform.HFlip\").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),\n\t\tundo_redo, undo_redo, undo_data, redo_data);\n}\n\nvoid OBSBasic::on_actionFlipVertical_triggered()\n{\n\tvec2 scale;\n\tvec2_set(&scale, 1.0f, -1.0f);\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tobs_scene_enum_items(GetCurrentScene(), MultiplySelectedItemScale, &scale);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(\n\t\tQTStr(\"Undo.Transform.VFlip\").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),\n\t\tundo_redo, undo_redo, undo_data, redo_data);\n}\n\nstatic bool CenterAlignSelectedItems(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tobs_bounds_type boundsType = *static_cast<obs_bounds_type *>(param);\n\n\tif (obs_sceneitem_is_group(item))\n\t\tobs_sceneitem_group_enum_items(item, CenterAlignSelectedItems, param);\n\tif (!obs_sceneitem_selected(item))\n\t\treturn true;\n\tif (obs_sceneitem_locked(item))\n\t\treturn true;\n\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tobs_transform_info itemInfo;\n\tvec2_set(&itemInfo.pos, 0.0f, 0.0f);\n\tvec2_set(&itemInfo.scale, 1.0f, 1.0f);\n\titemInfo.alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP;\n\titemInfo.rot = 0.0f;\n\n\tvec2_set(&itemInfo.bounds, float(ovi.base_width), float(ovi.base_height));\n\titemInfo.bounds_type = boundsType;\n\titemInfo.bounds_alignment = OBS_ALIGN_CENTER;\n\titemInfo.crop_to_bounds = obs_sceneitem_get_bounds_crop(item);\n\n\tobs_sceneitem_set_info2(item, &itemInfo);\n\n\treturn true;\n}\n\nvoid OBSBasic::on_actionFitToScreen_triggered()\n{\n\tobs_bounds_type boundsType = OBS_BOUNDS_SCALE_INNER;\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tobs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems, &boundsType);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(\n\t\tQTStr(\"Undo.Transform.FitToScreen\").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),\n\t\tundo_redo, undo_redo, undo_data, redo_data);\n}\n\nvoid OBSBasic::on_actionStretchToScreen_triggered()\n{\n\tobs_bounds_type boundsType = OBS_BOUNDS_STRETCH;\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tobs_scene_enum_items(GetCurrentScene(), CenterAlignSelectedItems, &boundsType);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(QTStr(\"Undo.Transform.StretchToScreen\")\n\t\t\t\t  .arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),\n\t\t\t  undo_redo, undo_redo, undo_data, redo_data);\n}\n\nvoid OBSBasic::CenterSelectedSceneItems(const CenterType &centerType)\n{\n\tQModelIndexList selectedItems = GetAllSelectedSourceItems();\n\n\tif (!selectedItems.count())\n\t\treturn;\n\n\tvector<OBSSceneItem> items;\n\n\t// Filter out items that have no size\n\tfor (int x = 0; x < selectedItems.count(); x++) {\n\t\tOBSSceneItem item = ui->sources->Get(selectedItems[x].row());\n\t\tobs_transform_info oti;\n\t\tobs_sceneitem_get_info2(item, &oti);\n\n\t\tobs_source_t *source = obs_sceneitem_get_source(item);\n\t\tfloat width = float(obs_source_get_width(source)) * oti.scale.x;\n\t\tfloat height = float(obs_source_get_height(source)) * oti.scale.y;\n\n\t\tif (width == 0.0f || height == 0.0f)\n\t\t\tcontinue;\n\n\t\titems.emplace_back(item);\n\t}\n\n\tif (!items.size())\n\t\treturn;\n\n\t// Get center x, y coordinates of items\n\tvec3 center;\n\n\tfloat top = M_INFINITE;\n\tfloat left = M_INFINITE;\n\tfloat right = 0.0f;\n\tfloat bottom = 0.0f;\n\n\tfor (auto &item : items) {\n\t\tvec3 tl, br;\n\n\t\tGetItemBox(item, tl, br);\n\n\t\tleft = std::min(tl.x, left);\n\t\ttop = std::min(tl.y, top);\n\t\tright = std::max(br.x, right);\n\t\tbottom = std::max(br.y, bottom);\n\t}\n\n\tcenter.x = (right + left) / 2.0f;\n\tcenter.y = (top + bottom) / 2.0f;\n\tcenter.z = 0.0f;\n\n\t// Get coordinates of screen center\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tvec3 screenCenter;\n\tvec3_set(&screenCenter, float(ovi.base_width), float(ovi.base_height), 0.0f);\n\n\tvec3_mulf(&screenCenter, &screenCenter, 0.5f);\n\n\t// Calculate difference between screen center and item center\n\tvec3 offset;\n\tvec3_sub(&offset, &screenCenter, &center);\n\n\t// Shift items by offset\n\tfor (auto &item : items) {\n\t\tvec3 tl, br;\n\n\t\tGetItemBox(item, tl, br);\n\n\t\tvec3_add(&tl, &tl, &offset);\n\n\t\tvec3 itemTL = GetItemTL(item);\n\n\t\tif (centerType == CenterType::Vertical)\n\t\t\ttl.x = itemTL.x;\n\t\telse if (centerType == CenterType::Horizontal)\n\t\t\ttl.y = itemTL.y;\n\n\t\tSetItemTL(item, tl);\n\t}\n}\n\nvoid OBSBasic::on_actionCenterToScreen_triggered()\n{\n\tCenterType centerType = CenterType::Scene;\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tCenterSelectedSceneItems(centerType);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(\n\t\tQTStr(\"Undo.Transform.Center\").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),\n\t\tundo_redo, undo_redo, undo_data, redo_data);\n}\n\nvoid OBSBasic::on_actionVerticalCenter_triggered()\n{\n\tCenterType centerType = CenterType::Vertical;\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tCenterSelectedSceneItems(centerType);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(\n\t\tQTStr(\"Undo.Transform.VCenter\").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),\n\t\tundo_redo, undo_redo, undo_data, redo_data);\n}\n\nvoid OBSBasic::on_actionHorizontalCenter_triggered()\n{\n\tCenterType centerType = CenterType::Horizontal;\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\tCenterSelectedSceneItems(centerType);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(\n\t\tQTStr(\"Undo.Transform.HCenter\").arg(obs_source_get_name(obs_scene_get_source(GetCurrentScene()))),\n\t\tundo_redo, undo_redo, undo_data, redo_data);\n}\n\nvoid OBSBasic::on_toggleSourceIcons_toggled(bool visible)\n{\n\tui->sources->SetIconsVisible(visible);\n\tif (advAudioWindow != nullptr)\n\t\tadvAudioWindow->SetIconsVisible(visible);\n\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"ShowSourceIcons\", visible);\n}\n\nvoid OBSBasic::on_sourcePropertiesButton_clicked()\n{\n\ton_actionSourceProperties_triggered();\n}\n\nvoid OBSBasic::on_sourceFiltersButton_clicked()\n{\n\tOpenFilters();\n}\n\nvoid OBSBasic::on_sourceInteractButton_clicked()\n{\n\ton_actionInteract_triggered();\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Scenes.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n#include \"OBSProjector.hpp\"\n\n#include <dialogs/NameDialog.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QLineEdit>\n#include <QWidgetAction>\n\n#include <vector>\n\nusing namespace std;\n\nnamespace {\n\ntemplate<typename OBSRef> struct SignalContainer {\n\tOBSRef ref;\n\tvector<shared_ptr<OBSSignal>> handlers;\n};\n\nQDataStream &operator<<(QDataStream &out, const SignalContainer<OBSScene> &v)\n{\n\tout << v.ref;\n\treturn out;\n}\n\nQDataStream &operator>>(QDataStream &in, SignalContainer<OBSScene> &v)\n{\n\tin >> v.ref;\n\treturn in;\n}\n\n} // namespace\n\nQ_DECLARE_METATYPE(obs_order_movement);\nQ_DECLARE_METATYPE(SignalContainer<OBSScene>);\n\nextern void undo_redo(const std::string &data);\n\nobs_data_array_t *OBSBasic::SaveSceneListOrder()\n{\n\tobs_data_array_t *sceneOrder = obs_data_array_create();\n\n\tfor (int i = 0; i < ui->scenes->count(); i++) {\n\t\tOBSDataAutoRelease data = obs_data_create();\n\t\tobs_data_set_string(data, \"name\", QT_TO_UTF8(ui->scenes->item(i)->text()));\n\t\tobs_data_array_push_back(sceneOrder, data);\n\t}\n\n\treturn sceneOrder;\n}\n\nstatic void ReorderItemByName(QListWidget *lw, const char *name, int newIndex)\n{\n\tfor (int i = 0; i < lw->count(); i++) {\n\t\tQListWidgetItem *item = lw->item(i);\n\n\t\tif (strcmp(name, QT_TO_UTF8(item->text())) == 0) {\n\t\t\tif (newIndex != i) {\n\t\t\t\titem = lw->takeItem(i);\n\t\t\t\tlw->insertItem(newIndex, item);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nvoid OBSBasic::LoadSceneListOrder(obs_data_array_t *array)\n{\n\tsize_t num = obs_data_array_count(array);\n\n\tfor (size_t i = 0; i < num; i++) {\n\t\tOBSDataAutoRelease data = obs_data_array_item(array, i);\n\t\tconst char *name = obs_data_get_string(data, \"name\");\n\n\t\tReorderItemByName(ui->scenes, name, (int)i);\n\t}\n}\n\nOBSScene OBSBasic::GetCurrentScene()\n{\n\treturn currentScene.load();\n}\n\nvoid OBSBasic::AddScene(OBSSource source)\n{\n\tconst char *name = obs_source_get_name(source);\n\tobs_scene_t *scene = obs_scene_from_source(source);\n\n\tQListWidgetItem *item = new QListWidgetItem(QT_UTF8(name));\n\tSetOBSRef(item, OBSScene(scene));\n\tui->scenes->insertItem(ui->scenes->currentRow() + 1, item);\n\n\tobs_hotkey_register_source(\n\t\tsource, \"OBSBasic.SelectScene\", Str(\"Basic.Hotkeys.SelectScene\"),\n\t\t[](void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed) {\n\t\t\tOBSBasic *main = OBSBasic::Get();\n\n\t\t\tauto potential_source = static_cast<obs_source_t *>(data);\n\t\t\tOBSSourceAutoRelease source = obs_source_get_ref(potential_source);\n\t\t\tif (source && pressed)\n\t\t\t\tmain->SetCurrentScene(source.Get());\n\t\t},\n\t\tstatic_cast<obs_source_t *>(source));\n\n\tsignal_handler_t *handler = obs_source_get_signal_handler(source);\n\n\tSignalContainer<OBSScene> container;\n\tcontainer.ref = scene;\n\tcontainer.handlers.assign({\n\t\tstd::make_shared<OBSSignal>(handler, \"item_add\", OBSBasic::SceneItemAdded, this),\n\t\tstd::make_shared<OBSSignal>(handler, \"reorder\", OBSBasic::SceneReordered, this),\n\t\tstd::make_shared<OBSSignal>(handler, \"refresh\", OBSBasic::SceneRefreshed, this),\n\t});\n\n\titem->setData(static_cast<int>(QtDataRole::OBSSignals), QVariant::fromValue(container));\n\n\t/* if the scene already has items (a duplicated scene) add them */\n\tauto addSceneItem = [this](obs_sceneitem_t *item) {\n\t\tAddSceneItem(item);\n\t};\n\n\tusing addSceneItem_t = decltype(addSceneItem);\n\n\tobs_scene_enum_items(\n\t\tscene,\n\t\t[](obs_scene_t *, obs_sceneitem_t *item, void *param) {\n\t\t\taddSceneItem_t *func;\n\t\t\tfunc = static_cast<addSceneItem_t *>(param);\n\t\t\t(*func)(item);\n\t\t\treturn true;\n\t\t},\n\t\t&addSceneItem);\n\n\tSaveProject();\n\n\tif (!disableSaving) {\n\t\tobs_source_t *source = obs_scene_get_source(scene);\n\t\tblog(LOG_INFO, \"User added scene '%s'\", obs_source_get_name(source));\n\n\t\tOBSProjector::UpdateMultiviewProjectors();\n\t}\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);\n}\n\nvoid OBSBasic::RemoveScene(OBSSource source)\n{\n\tobs_scene_t *scene = obs_scene_from_source(source);\n\n\tbool foundItem = false;\n\tQListWidgetItem *sel = nullptr;\n\tint count = ui->scenes->count();\n\n\tfor (int i = 0; i < count; i++) {\n\t\tauto item = ui->scenes->item(i);\n\t\tauto cur_scene = GetOBSRef<OBSScene>(item);\n\t\tif (cur_scene != scene)\n\t\t\tcontinue;\n\n\t\tfoundItem = true;\n\t\tsel = item;\n\t\tbreak;\n\t}\n\n\tif (sel != nullptr) {\n\t\tif (sel == ui->scenes->currentItem())\n\t\t\tui->sources->Clear();\n\t\tdelete sel;\n\t}\n\n\tSaveProject();\n\n\tif (!disableSaving) {\n\t\tblog(LOG_INFO, \"User Removed scene '%s'\", obs_source_get_name(source));\n\n\t\tOBSProjector::UpdateMultiviewProjectors();\n\t}\n\n\tif (foundItem) {\n\t\tOnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);\n\t}\n}\n\nstatic bool select_one(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *param)\n{\n\tobs_sceneitem_t *selectedItem = static_cast<obs_sceneitem_t *>(param);\n\tif (obs_sceneitem_is_group(item))\n\t\tobs_sceneitem_group_enum_items(item, select_one, param);\n\n\tobs_sceneitem_select(item, (selectedItem == item));\n\n\treturn true;\n}\n\nvoid OBSBasic::AddSceneItem(OBSSceneItem item)\n{\n\tobs_scene_t *scene = obs_sceneitem_get_scene(item);\n\n\tif (GetCurrentScene() == scene)\n\t\tui->sources->Add(item);\n\n\tSaveProject();\n\n\tif (!disableSaving) {\n\t\tobs_source_t *sceneSource = obs_scene_get_source(scene);\n\t\tobs_source_t *itemSource = obs_sceneitem_get_source(item);\n\t\tblog(LOG_INFO, \"User added source '%s' (%s) to scene '%s'\", obs_source_get_name(itemSource),\n\t\t     obs_source_get_id(itemSource), obs_source_get_name(sceneSource));\n\n\t\tobs_scene_enum_items(scene, select_one, (obs_sceneitem_t *)item);\n\t}\n}\n\nvoid OBSBasic::DuplicateSelectedScene()\n{\n\tOBSScene curScene = GetCurrentScene();\n\n\tif (!curScene)\n\t\treturn;\n\n\tOBSSource curSceneSource = obs_scene_get_source(curScene);\n\tQString format{obs_source_get_name(curSceneSource)};\n\tformat += \" %1\";\n\n\tint i = 2;\n\tQString placeHolderText = format.arg(i);\n\tOBSSourceAutoRelease source = nullptr;\n\twhile ((source = obs_get_source_by_name(QT_TO_UTF8(placeHolderText)))) {\n\t\tplaceHolderText = format.arg(++i);\n\t}\n\n\tfor (;;) {\n\t\tstring name;\n\t\tbool accepted = NameDialog::AskForName(this, QTStr(\"Basic.Main.AddSceneDlg.Title\"),\n\t\t\t\t\t\t       QTStr(\"Basic.Main.AddSceneDlg.Text\"), name, placeHolderText);\n\t\tif (!accepted)\n\t\t\treturn;\n\n\t\tif (name.empty()) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\t\tcontinue;\n\t\t}\n\n\t\tobs_source_t *source = obs_get_source_by_name(name.c_str());\n\t\tif (source) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\n\t\t\tobs_source_release(source);\n\t\t\tcontinue;\n\t\t}\n\n\t\tOBSSceneAutoRelease scene = obs_scene_duplicate(curScene, name.c_str(), OBS_SCENE_DUP_REFS);\n\t\tsource = obs_scene_get_source(scene);\n\t\tSetCurrentScene(source, true);\n\n\t\tauto undo = [](const std::string &data) {\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_name(data.c_str());\n\t\t\tobs_source_remove(source);\n\t\t};\n\n\t\tauto redo = [this, name](const std::string &data) {\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_name(data.c_str());\n\t\t\tobs_scene_t *scene = obs_scene_from_source(source);\n\t\t\tscene = obs_scene_duplicate(scene, name.c_str(), OBS_SCENE_DUP_REFS);\n\t\t\tsource = obs_scene_get_source(scene);\n\t\t\tSetCurrentScene(source.Get(), true);\n\t\t};\n\n\t\tundo_s.add_action(QTStr(\"Undo.Scene.Duplicate\").arg(obs_source_get_name(source)), undo, redo,\n\t\t\t\t  obs_source_get_name(source), obs_source_get_name(obs_scene_get_source(curScene)));\n\n\t\tbreak;\n\t}\n}\n\nstatic bool save_undo_source_enum(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *p)\n{\n\tobs_source_t *source = obs_sceneitem_get_source(item);\n\tif (obs_obj_is_private(source) && !obs_source_removed(source))\n\t\treturn true;\n\n\tobs_data_array_t *array = (obs_data_array_t *)p;\n\n\t/* check if the source is already stored in the array */\n\tconst char *name = obs_source_get_name(source);\n\tconst size_t count = obs_data_array_count(array);\n\tfor (size_t i = 0; i < count; i++) {\n\t\tOBSDataAutoRelease sourceData = obs_data_array_item(array, i);\n\t\tif (strcmp(name, obs_data_get_string(sourceData, \"name\")) == 0)\n\t\t\treturn true;\n\t}\n\n\tif (obs_source_is_group(source))\n\t\tobs_scene_enum_items(obs_group_from_source(source), save_undo_source_enum, p);\n\n\tOBSDataAutoRelease source_data = obs_save_source(source);\n\tobs_data_array_push_back(array, source_data);\n\treturn true;\n}\n\nstatic inline void RemoveSceneAndReleaseNested(obs_source_t *source)\n{\n\tobs_source_remove(source);\n\tauto cb = [](void *, obs_source_t *source) {\n\t\tif (strcmp(obs_source_get_id(source), \"scene\") == 0)\n\t\t\tobs_scene_prune_sources(obs_scene_from_source(source));\n\t\treturn true;\n\t};\n\tobs_enum_scenes(cb, NULL);\n}\n\nvoid OBSBasic::RemoveSelectedScene()\n{\n\tOBSScene scene = GetCurrentScene();\n\tobs_source_t *source = obs_scene_get_source(scene);\n\n\tif (!source || !QueryRemoveSource(source)) {\n\t\treturn;\n\t}\n\n\t/* ------------------------------ */\n\t/* save all sources in scene      */\n\n\tOBSDataArrayAutoRelease sources_in_deleted_scene = obs_data_array_create();\n\n\tobs_scene_enum_items(scene, save_undo_source_enum, sources_in_deleted_scene);\n\n\tOBSDataAutoRelease scene_data = obs_save_source(source);\n\tobs_data_array_push_back(sources_in_deleted_scene, scene_data);\n\n\t/* ----------------------------------------------- */\n\t/* save all scenes and groups the scene is used in */\n\n\tOBSDataArrayAutoRelease scene_used_in_other_scenes = obs_data_array_create();\n\n\tstruct other_scenes_cb_data {\n\t\tobs_source_t *oldScene;\n\t\tobs_data_array_t *scene_used_in_other_scenes;\n\t} other_scenes_cb_data;\n\tother_scenes_cb_data.oldScene = source;\n\tother_scenes_cb_data.scene_used_in_other_scenes = scene_used_in_other_scenes;\n\n\tauto other_scenes_cb = [](void *data_ptr, obs_source_t *scene) {\n\t\tstruct other_scenes_cb_data *data = (struct other_scenes_cb_data *)data_ptr;\n\t\tif (strcmp(obs_source_get_name(scene), obs_source_get_name(data->oldScene)) == 0)\n\t\t\treturn true;\n\t\tobs_sceneitem_t *item = obs_scene_find_source(obs_group_or_scene_from_source(scene),\n\t\t\t\t\t\t\t      obs_source_get_name(data->oldScene));\n\t\tif (item) {\n\t\t\tOBSDataAutoRelease scene_data =\n\t\t\t\tobs_save_source(obs_scene_get_source(obs_sceneitem_get_scene(item)));\n\t\t\tobs_data_array_push_back(data->scene_used_in_other_scenes, scene_data);\n\t\t}\n\t\treturn true;\n\t};\n\tobs_enum_scenes(other_scenes_cb, &other_scenes_cb_data);\n\n\t/* --------------------------- */\n\t/* undo/redo                   */\n\n\tauto undo = [this](const std::string &json) {\n\t\tOBSDataAutoRelease base = obs_data_create_from_json(json.c_str());\n\t\tOBSDataArrayAutoRelease sources_in_deleted_scene = obs_data_get_array(base, \"sources_in_deleted_scene\");\n\t\tOBSDataArrayAutoRelease scene_used_in_other_scenes =\n\t\t\tobs_data_get_array(base, \"scene_used_in_other_scenes\");\n\t\tint savedIndex = (int)obs_data_get_int(base, \"index\");\n\t\tstd::vector<OBSSource> sources;\n\n\t\t/* create missing sources */\n\t\tsize_t count = obs_data_array_count(sources_in_deleted_scene);\n\t\tsources.reserve(count);\n\n\t\tfor (size_t i = 0; i < count; i++) {\n\t\t\tOBSDataAutoRelease data = obs_data_array_item(sources_in_deleted_scene, i);\n\t\t\tconst char *name = obs_data_get_string(data, \"name\");\n\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_name(name);\n\t\t\tif (!source) {\n\t\t\t\tsource = obs_load_source(data);\n\t\t\t\tsources.push_back(source.Get());\n\t\t\t}\n\t\t}\n\n\t\t/* actually load sources now */\n\t\tfor (obs_source_t *source : sources)\n\t\t\tobs_source_load2(source);\n\n\t\t/* Add scene to scenes and groups it was nested in */\n\t\tfor (size_t i = 0; i < obs_data_array_count(scene_used_in_other_scenes); i++) {\n\t\t\tOBSDataAutoRelease data = obs_data_array_item(scene_used_in_other_scenes, i);\n\t\t\tconst char *name = obs_data_get_string(data, \"name\");\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_name(name);\n\n\t\t\tOBSDataAutoRelease settings = obs_data_get_obj(data, \"settings\");\n\t\t\tOBSDataArrayAutoRelease items = obs_data_get_array(settings, \"items\");\n\n\t\t\t/* Clear scene, but keep a reference to all sources in the scene to make sure they don't get destroyed */\n\t\t\tstd::vector<OBSSource> existing_sources;\n\t\t\tauto cb = [](obs_scene_t *, obs_sceneitem_t *item, void *data) {\n\t\t\t\tstd::vector<OBSSource> *existing = (std::vector<OBSSource> *)data;\n\t\t\t\tOBSSource source = obs_sceneitem_get_source(item);\n\t\t\t\tobs_sceneitem_remove(item);\n\t\t\t\texisting->push_back(source);\n\t\t\t\treturn true;\n\t\t\t};\n\t\t\tobs_scene_enum_items(obs_group_or_scene_from_source(source), cb, (void *)&existing_sources);\n\n\t\t\t/* Re-add sources to the scene */\n\t\t\tobs_sceneitems_add(obs_group_or_scene_from_source(source), items);\n\t\t}\n\n\t\tobs_source_t *scene_source = sources.back();\n\t\tOBSScene scene = obs_scene_from_source(scene_source);\n\t\tSetCurrentScene(scene, true);\n\n\t\t/* set original index in list box */\n\t\tui->scenes->blockSignals(true);\n\t\tint curIndex = ui->scenes->currentRow();\n\t\tQListWidgetItem *item = ui->scenes->takeItem(curIndex);\n\t\tui->scenes->insertItem(savedIndex, item);\n\t\tui->scenes->setCurrentRow(savedIndex);\n\t\tcurrentScene = scene.Get();\n\t\tui->scenes->blockSignals(false);\n\t};\n\n\tauto redo = [](const std::string &name) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_name(name.c_str());\n\t\tRemoveSceneAndReleaseNested(source);\n\t};\n\n\tOBSDataAutoRelease data = obs_data_create();\n\tobs_data_set_array(data, \"sources_in_deleted_scene\", sources_in_deleted_scene);\n\tobs_data_set_array(data, \"scene_used_in_other_scenes\", scene_used_in_other_scenes);\n\tobs_data_set_int(data, \"index\", ui->scenes->currentRow());\n\n\tconst char *scene_name = obs_source_get_name(source);\n\tundo_s.add_action(QTStr(\"Undo.Delete\").arg(scene_name), undo, redo, obs_data_get_json(data), scene_name);\n\n\t/* --------------------------- */\n\t/* remove                      */\n\n\tRemoveSceneAndReleaseNested(source);\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);\n}\n\nvoid OBSBasic::SceneReordered(void *data, calldata_t *params)\n{\n\tOBSBasic *window = static_cast<OBSBasic *>(data);\n\n\tobs_scene_t *scene = (obs_scene_t *)calldata_ptr(params, \"scene\");\n\n\tQMetaObject::invokeMethod(window, \"ReorderSources\", Q_ARG(OBSScene, OBSScene(scene)));\n}\n\nvoid OBSBasic::SceneRefreshed(void *data, calldata_t *params)\n{\n\tOBSBasic *window = static_cast<OBSBasic *>(data);\n\n\tobs_scene_t *scene = (obs_scene_t *)calldata_ptr(params, \"scene\");\n\n\tQMetaObject::invokeMethod(window, \"RefreshSources\", Q_ARG(OBSScene, OBSScene(scene)));\n}\n\nvoid OBSBasic::SceneItemAdded(void *data, calldata_t *params)\n{\n\tOBSBasic *window = static_cast<OBSBasic *>(data);\n\n\tobs_sceneitem_t *item = (obs_sceneitem_t *)calldata_ptr(params, \"item\");\n\n\tQMetaObject::invokeMethod(window, \"AddSceneItem\", Q_ARG(OBSSceneItem, OBSSceneItem(item)));\n}\n\nvoid OBSBasic::on_scenes_currentItemChanged(QListWidgetItem *current, QListWidgetItem *)\n{\n\tOBSSource source;\n\n\tbool forceSceneChange = false;\n\n\tif (current) {\n\t\tOBSScene scene = GetOBSRef<OBSScene>(current);\n\t\tsource = obs_scene_get_source(scene);\n\n\t\tbool oldSceneIsRemoved = obs_source_removed(obs_scene_get_source(currentScene));\n\t\tforceSceneChange = oldSceneIsRemoved;\n\n\t\tcurrentScene = scene;\n\t} else {\n\t\tcurrentScene = NULL;\n\t}\n\n\tSetCurrentScene(source, forceSceneChange);\n\n\tif (vcamEnabled && vcamConfig.type == VCamOutputType::PreviewOutput)\n\t\toutputHandler->UpdateVirtualCamOutputSource();\n\n\tOnEvent(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);\n\n\tUpdateContextBar();\n}\n\nvoid OBSBasic::EditSceneName()\n{\n\tui->scenesDock->removeAction(renameScene);\n\tQListWidgetItem *item = ui->scenes->currentItem();\n\tQt::ItemFlags flags = item->flags();\n\n\titem->setFlags(flags | Qt::ItemIsEditable);\n\tui->scenes->editItem(item);\n\titem->setFlags(flags);\n}\n\nvoid OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)\n{\n\tQListWidgetItem *item = ui->scenes->itemAt(pos);\n\n\tQMenu popup(this);\n\tQMenu order(QTStr(\"Basic.MainMenu.Edit.Order\"), this);\n\n\tpopup.addAction(QTStr(\"AddScene\") + \"...\", this, &OBSBasic::on_actionAddScene_triggered);\n\n\tif (item) {\n\t\tQAction *copyFilters = new QAction(QTStr(\"Copy.Filters\"), this);\n\t\tcopyFilters->setEnabled(false);\n\t\tconnect(copyFilters, &QAction::triggered, this, &OBSBasic::SceneCopyFilters);\n\t\tQAction *pasteFilters = new QAction(QTStr(\"Paste.Filters\"), this);\n\t\tpasteFilters->setEnabled(!obs_weak_source_expired(copyFiltersSource()));\n\t\tconnect(pasteFilters, &QAction::triggered, this, &OBSBasic::ScenePasteFilters);\n\n\t\tpopup.addSeparator();\n\t\tpopup.addAction(QTStr(\"Duplicate\"), this, &OBSBasic::DuplicateSelectedScene);\n\t\tpopup.addAction(copyFilters);\n\t\tpopup.addAction(pasteFilters);\n\t\tpopup.addSeparator();\n\t\tpopup.addAction(renameScene);\n\t\tpopup.addAction(ui->actionRemoveScene);\n\t\tpopup.addSeparator();\n\n\t\torder.addAction(QTStr(\"Basic.MainMenu.Edit.Order.MoveUp\"), this, &OBSBasic::on_actionSceneUp_triggered);\n\t\torder.addAction(QTStr(\"Basic.MainMenu.Edit.Order.MoveDown\"), this,\n\t\t\t\t&OBSBasic::on_actionSceneDown_triggered);\n\t\torder.addSeparator();\n\t\torder.addAction(QTStr(\"Basic.MainMenu.Edit.Order.MoveToTop\"), this, &OBSBasic::MoveSceneToTop);\n\t\torder.addAction(QTStr(\"Basic.MainMenu.Edit.Order.MoveToBottom\"), this, &OBSBasic::MoveSceneToBottom);\n\t\tpopup.addMenu(&order);\n\n\t\tpopup.addSeparator();\n\n\t\tdelete sceneProjectorMenu;\n\t\tsceneProjectorMenu = new QMenu(QTStr(\"Projector.Open.Scene\"));\n\t\tAddProjectorMenuMonitors(sceneProjectorMenu, this, &OBSBasic::OpenSceneProjector);\n\t\tsceneProjectorMenu->addSeparator();\n\t\tsceneProjectorMenu->addAction(QTStr(\"Projector.Window\"), this, &OBSBasic::OpenSceneWindow);\n\n\t\tpopup.addMenu(sceneProjectorMenu);\n\t\tpopup.addSeparator();\n\n\t\tpopup.addAction(QTStr(\"Screenshot.Scene\"), this, &OBSBasic::ScreenshotScene);\n\t\tpopup.addSeparator();\n\t\tpopup.addAction(QTStr(\"Filters\"), this, &OBSBasic::OpenSceneFilters);\n\n\t\tpopup.addSeparator();\n\n\t\tdelete perSceneTransitionMenu;\n\t\tperSceneTransitionMenu = CreatePerSceneTransitionMenu();\n\t\tpopup.addMenu(perSceneTransitionMenu);\n\n\t\t/* ---------------------- */\n\n\t\tQAction *multiviewAction = popup.addAction(QTStr(\"ShowInMultiview\"));\n\n\t\tOBSSource source = GetCurrentSceneSource();\n\t\tOBSDataAutoRelease data = obs_source_get_private_settings(source);\n\n\t\tobs_data_set_default_bool(data, \"show_in_multiview\", true);\n\t\tbool show = obs_data_get_bool(data, \"show_in_multiview\");\n\n\t\tmultiviewAction->setCheckable(true);\n\t\tmultiviewAction->setChecked(show);\n\n\t\tauto showInMultiview = [](OBSData data) {\n\t\t\tbool show = obs_data_get_bool(data, \"show_in_multiview\");\n\t\t\tobs_data_set_bool(data, \"show_in_multiview\", !show);\n\t\t\tOBSProjector::UpdateMultiviewProjectors();\n\t\t};\n\n\t\tconnect(multiviewAction, &QAction::triggered, multiviewAction, std::bind(showInMultiview, data.Get()));\n\n\t\tcopyFilters->setEnabled(obs_source_filter_count(source) > 0);\n\t}\n\n\tpopup.addSeparator();\n\n\tbool grid = ui->scenes->GetGridMode();\n\n\tQAction *gridAction = new QAction(grid ? QTStr(\"Basic.Main.ListMode\") : QTStr(\"Basic.Main.GridMode\"), this);\n\tconnect(gridAction, &QAction::triggered, this, &OBSBasic::GridActionClicked);\n\tpopup.addAction(gridAction);\n\n\tpopup.exec(QCursor::pos());\n}\n\nvoid OBSBasic::on_actionSceneListMode_triggered()\n{\n\tui->scenes->SetGridMode(false);\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"gridMode\", false);\n}\n\nvoid OBSBasic::on_actionSceneGridMode_triggered()\n{\n\tui->scenes->SetGridMode(true);\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"gridMode\", true);\n}\n\nvoid OBSBasic::GridActionClicked()\n{\n\tbool gridMode = !ui->scenes->GetGridMode();\n\tui->scenes->SetGridMode(gridMode);\n\n\tif (gridMode)\n\t\tui->actionSceneGridMode->setChecked(true);\n\telse\n\t\tui->actionSceneListMode->setChecked(true);\n\n\tconfig_set_bool(App()->GetUserConfig(), \"BasicWindow\", \"gridMode\", gridMode);\n}\n\nvoid OBSBasic::on_actionAddScene_triggered()\n{\n\tstring name;\n\tQString format{QTStr(\"Basic.Main.DefaultSceneName.Text\")};\n\n\tint i = 2;\n\tQString placeHolderText = format.arg(i);\n\tOBSSourceAutoRelease source = nullptr;\n\twhile ((source = obs_get_source_by_name(QT_TO_UTF8(placeHolderText)))) {\n\t\tplaceHolderText = format.arg(++i);\n\t}\n\n\tbool accepted = NameDialog::AskForName(this, QTStr(\"Basic.Main.AddSceneDlg.Title\"),\n\t\t\t\t\t       QTStr(\"Basic.Main.AddSceneDlg.Text\"), name, placeHolderText);\n\n\tif (accepted) {\n\t\tif (name.empty()) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\t\ton_actionAddScene_triggered();\n\t\t\treturn;\n\t\t}\n\n\t\tOBSSourceAutoRelease source = obs_get_source_by_name(name.c_str());\n\t\tif (source) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\n\t\t\ton_actionAddScene_triggered();\n\t\t\treturn;\n\t\t}\n\n\t\tauto undo_fn = [](const std::string &data) {\n\t\t\tobs_source_t *t = obs_get_source_by_name(data.c_str());\n\t\t\tif (t) {\n\t\t\t\tobs_source_remove(t);\n\t\t\t\tobs_source_release(t);\n\t\t\t}\n\t\t};\n\n\t\tauto redo_fn = [this](const std::string &data) {\n\t\t\tOBSSceneAutoRelease scene = obs_scene_create(data.c_str());\n\t\t\tobs_source_t *source = obs_scene_get_source(scene);\n\t\t\tSetCurrentScene(source, true);\n\t\t};\n\t\tundo_s.add_action(QTStr(\"Undo.Add\").arg(QString(name.c_str())), undo_fn, redo_fn, name, name);\n\n\t\tOBSSceneAutoRelease scene = obs_scene_create(name.c_str());\n\t\tobs_source_t *scene_source = obs_scene_get_source(scene);\n\t\tSetCurrentScene(scene_source);\n\t}\n}\n\nvoid OBSBasic::on_actionRemoveScene_triggered()\n{\n\tRemoveSelectedScene();\n}\n\nvoid OBSBasic::ChangeSceneIndex(bool relative, int offset, int invalidIdx)\n{\n\tint idx = ui->scenes->currentRow();\n\tif (idx == -1 || idx == invalidIdx)\n\t\treturn;\n\n\tui->scenes->blockSignals(true);\n\tQListWidgetItem *item = ui->scenes->takeItem(idx);\n\n\tif (!relative)\n\t\tidx = 0;\n\n\tui->scenes->insertItem(idx + offset, item);\n\tui->scenes->setCurrentRow(idx + offset);\n\titem->setSelected(true);\n\tcurrentScene = GetOBSRef<OBSScene>(item).Get();\n\tui->scenes->blockSignals(false);\n\n\tOBSProjector::UpdateMultiviewProjectors();\n}\n\nvoid OBSBasic::on_actionSceneUp_triggered()\n{\n\tChangeSceneIndex(true, -1, 0);\n}\n\nvoid OBSBasic::on_actionSceneDown_triggered()\n{\n\tChangeSceneIndex(true, 1, ui->scenes->count() - 1);\n}\n\nvoid OBSBasic::MoveSceneToTop()\n{\n\tChangeSceneIndex(false, 0, 0);\n}\n\nvoid OBSBasic::MoveSceneToBottom()\n{\n\tChangeSceneIndex(false, ui->scenes->count() - 1, ui->scenes->count() - 1);\n}\n\nvoid OBSBasic::EditSceneItemName()\n{\n\tint idx = GetTopSelectedSourceItem();\n\tui->sources->Edit(idx);\n}\n\nvoid OBSBasic::on_scenes_itemDoubleClicked(QListWidgetItem *witem)\n{\n\tif (!witem)\n\t\treturn;\n\n\tif (IsPreviewProgramMode()) {\n\t\tbool doubleClickSwitch =\n\t\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"TransitionOnDoubleClick\");\n\n\t\tif (doubleClickSwitch)\n\t\t\tTransitionClicked();\n\t}\n}\n\nOBSData OBSBasic::BackupScene(obs_scene_t *scene, std::vector<obs_source_t *> *sources)\n{\n\tOBSDataArrayAutoRelease undo_array = obs_data_array_create();\n\n\tif (!sources) {\n\t\tobs_scene_enum_items(scene, save_undo_source_enum, undo_array);\n\t} else {\n\t\tfor (obs_source_t *source : *sources) {\n\t\t\tobs_data_t *source_data = obs_save_source(source);\n\t\t\tobs_data_array_push_back(undo_array, source_data);\n\t\t\tobs_data_release(source_data);\n\t\t}\n\t}\n\n\tOBSDataAutoRelease scene_data = obs_save_source(obs_scene_get_source(scene));\n\tobs_data_array_push_back(undo_array, scene_data);\n\n\tOBSDataAutoRelease data = obs_data_create();\n\n\tobs_data_set_array(data, \"array\", undo_array);\n\tobs_data_get_json(data);\n\treturn data.Get();\n}\n\nstatic bool add_source_enum(obs_scene_t *, obs_sceneitem_t *item, void *p)\n{\n\tauto sources = static_cast<std::vector<OBSSource> *>(p);\n\tsources->push_back(obs_sceneitem_get_source(item));\n\treturn true;\n}\n\nvoid OBSBasic::CreateSceneUndoRedoAction(const QString &action_name, OBSData undo_data, OBSData redo_data)\n{\n\tauto undo_redo = [this](const std::string &json) {\n\t\tOBSDataAutoRelease base = obs_data_create_from_json(json.c_str());\n\t\tOBSDataArrayAutoRelease array = obs_data_get_array(base, \"array\");\n\t\tstd::vector<OBSSource> sources;\n\t\tstd::vector<OBSSource> old_sources;\n\n\t\t/* create missing sources */\n\t\tconst size_t count = obs_data_array_count(array);\n\t\tsources.reserve(count);\n\n\t\tfor (size_t i = 0; i < count; i++) {\n\t\t\tOBSDataAutoRelease data = obs_data_array_item(array, i);\n\t\t\tconst char *name = obs_data_get_string(data, \"name\");\n\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_name(name);\n\t\t\tif (!source)\n\t\t\t\tsource = obs_load_source(data);\n\n\t\t\tsources.push_back(source.Get());\n\n\t\t\t/* update scene/group settings to restore their\n\t\t\t * contents to their saved settings */\n\t\t\tobs_scene_t *scene = obs_group_or_scene_from_source(source);\n\t\t\tif (scene) {\n\t\t\t\tobs_scene_enum_items(scene, add_source_enum, &old_sources);\n\t\t\t\tOBSDataAutoRelease scene_settings = obs_data_get_obj(data, \"settings\");\n\t\t\t\tobs_source_update(source, scene_settings);\n\t\t\t}\n\t\t}\n\n\t\t/* actually load sources now */\n\t\tfor (obs_source_t *source : sources)\n\t\t\tobs_source_load2(source);\n\n\t\tui->sources->RefreshItems();\n\t};\n\n\tconst char *undo_json = obs_data_get_last_json(undo_data);\n\tconst char *redo_json = obs_data_get_last_json(redo_data);\n\n\tundo_s.add_action(action_name, undo_redo, undo_redo, undo_json, redo_json);\n}\n\nvoid OBSBasic::MoveSceneItem(enum obs_order_movement movement, const QString &action_name)\n{\n\tOBSSceneItem item = GetCurrentSceneItem();\n\tobs_source_t *source = obs_sceneitem_get_source(item);\n\n\tif (!source)\n\t\treturn;\n\n\tOBSScene scene = GetCurrentScene();\n\tstd::vector<obs_source_t *> sources;\n\tif (scene != obs_sceneitem_get_scene(item))\n\t\tsources.push_back(obs_scene_get_source(obs_sceneitem_get_scene(item)));\n\n\tOBSData undo_data = BackupScene(scene, &sources);\n\n\tobs_sceneitem_set_order(item, movement);\n\n\tconst char *source_name = obs_source_get_name(source);\n\tconst char *scene_name = obs_source_get_name(obs_scene_get_source(scene));\n\n\tOBSData redo_data = BackupScene(scene, &sources);\n\tCreateSceneUndoRedoAction(action_name.arg(source_name, scene_name), undo_data, redo_data);\n}\n\nstatic void RenameListItem(OBSBasic *parent, QListWidget *listWidget, obs_source_t *source, const string &name)\n{\n\tconst char *prevName = obs_source_get_name(source);\n\tif (name == prevName)\n\t\treturn;\n\n\tOBSSourceAutoRelease foundSource = obs_get_source_by_name(name.c_str());\n\tQListWidgetItem *listItem = listWidget->currentItem();\n\n\tif (foundSource || name.empty()) {\n\t\tlistItem->setText(QT_UTF8(prevName));\n\n\t\tif (foundSource) {\n\t\t\tOBSMessageBox::warning(parent, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\t\t} else if (name.empty()) {\n\t\t\tOBSMessageBox::warning(parent, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\t}\n\t} else {\n\t\tauto undo = [prev = std::string(prevName)](const std::string &data) {\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());\n\t\t\tobs_source_set_name(source, prev.c_str());\n\t\t};\n\n\t\tauto redo = [name](const std::string &data) {\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());\n\t\t\tobs_source_set_name(source, name.c_str());\n\t\t};\n\n\t\tstd::string source_uuid(obs_source_get_uuid(source));\n\t\tparent->undo_s.add_action(QTStr(\"Undo.Rename\").arg(name.c_str()), undo, redo, source_uuid, source_uuid);\n\n\t\tlistItem->setText(QT_UTF8(name.c_str()));\n\t\tobs_source_set_name(source, name.c_str());\n\t}\n}\n\nvoid OBSBasic::SceneNameEdited(QWidget *editor)\n{\n\tOBSScene scene = GetCurrentScene();\n\tQLineEdit *edit = qobject_cast<QLineEdit *>(editor);\n\tstring text = QT_TO_UTF8(edit->text().trimmed());\n\n\tif (!scene)\n\t\treturn;\n\n\tobs_source_t *source = obs_scene_get_source(scene);\n\tRenameListItem(this, ui->scenes, source, text);\n\n\tui->scenesDock->addAction(renameScene);\n\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED);\n}\n\nvoid OBSBasic::OpenSceneFilters()\n{\n\tOBSScene scene = GetCurrentScene();\n\tOBSSource source = obs_scene_get_source(scene);\n\n\tCreateFiltersWindow(source);\n}\n\nstatic bool reset_tr(obs_scene_t * /* scene */, obs_sceneitem_t *item, void *)\n{\n\tif (obs_sceneitem_is_group(item))\n\t\tobs_sceneitem_group_enum_items(item, reset_tr, nullptr);\n\tif (!obs_sceneitem_selected(item))\n\t\treturn true;\n\tif (obs_sceneitem_locked(item))\n\t\treturn true;\n\n\tobs_sceneitem_defer_update_begin(item);\n\n\tobs_transform_info info;\n\tvec2_set(&info.pos, 0.0f, 0.0f);\n\tvec2_set(&info.scale, 1.0f, 1.0f);\n\tinfo.rot = 0.0f;\n\tinfo.alignment = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;\n\tinfo.bounds_type = OBS_BOUNDS_NONE;\n\tinfo.bounds_alignment = OBS_ALIGN_CENTER;\n\tinfo.crop_to_bounds = false;\n\tvec2_set(&info.bounds, 0.0f, 0.0f);\n\tobs_sceneitem_set_info2(item, &info);\n\n\tobs_sceneitem_crop crop = {};\n\tobs_sceneitem_set_crop(item, &crop);\n\n\tobs_sceneitem_defer_update_end(item);\n\n\treturn true;\n}\n\nvoid OBSBasic::on_actionResetTransform_triggered()\n{\n\tOBSScene scene = GetCurrentScene();\n\n\tOBSDataAutoRelease wrapper = obs_scene_save_transform_states(scene, false);\n\tobs_scene_enum_items(scene, reset_tr, nullptr);\n\tOBSDataAutoRelease rwrapper = obs_scene_save_transform_states(scene, false);\n\n\tstd::string undo_data(obs_data_get_json(wrapper));\n\tstd::string redo_data(obs_data_get_json(rwrapper));\n\tundo_s.add_action(QTStr(\"Undo.Transform.Reset\").arg(obs_source_get_name(obs_scene_get_source(scene))),\n\t\t\t  undo_redo, undo_redo, undo_data, redo_data);\n\n\tobs_scene_enum_items(GetCurrentScene(), reset_tr, nullptr);\n}\n\nSourceTreeItem *OBSBasic::GetItemWidgetFromSceneItem(obs_sceneitem_t *sceneItem)\n{\n\tint i = 0;\n\tSourceTreeItem *treeItem = ui->sources->GetItemWidget(i);\n\tOBSSceneItem item = ui->sources->Get(i);\n\tint64_t id = obs_sceneitem_get_id(sceneItem);\n\twhile (treeItem && obs_sceneitem_get_id(item) != id) {\n\t\ti++;\n\t\ttreeItem = ui->sources->GetItemWidget(i);\n\t\titem = ui->sources->Get(i);\n\t}\n\tif (treeItem)\n\t\treturn treeItem;\n\n\treturn nullptr;\n}\n\nvoid OBSBasic::on_actionSceneFilters_triggered()\n{\n\tOBSSource sceneSource = GetCurrentSceneSource();\n\n\tif (sceneSource)\n\t\tOpenFilters(sceneSource);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Screenshots.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <utility/ScreenshotObj.hpp>\n\n#include <qt-wrappers.hpp>\n\nvoid OBSBasic::Screenshot(OBSSource source)\n{\n\tif (!!screenshotData) {\n\t\tblog(LOG_WARNING, \"Cannot take new screenshot, \"\n\t\t\t\t  \"screenshot currently in progress\");\n\t\treturn;\n\t}\n\n\tscreenshotData = new ScreenshotObj(source);\n}\n\nvoid OBSBasic::ScreenshotSelectedSource()\n{\n\tOBSSceneItem item = GetCurrentSceneItem();\n\tif (item) {\n\t\tScreenshot(obs_sceneitem_get_source(item));\n\t} else {\n\t\tblog(LOG_INFO, \"Could not take a source screenshot: \"\n\t\t\t       \"no source selected\");\n\t}\n}\n\nvoid OBSBasic::ScreenshotProgram()\n{\n\tScreenshot(GetProgramSource());\n}\n\nvoid OBSBasic::ScreenshotScene()\n{\n\tScreenshot(GetCurrentSceneSource());\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Service.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\nconstexpr std::string_view OBSServiceFileName = \"service.json\";\n\nvoid OBSBasic::SaveService()\n{\n\tif (!service)\n\t\treturn;\n\n\tconst OBSProfile &currentProfile = GetCurrentProfile();\n\n\tconst std::filesystem::path jsonFilePath = currentProfile.path / std::filesystem::u8path(OBSServiceFileName);\n\n\tOBSDataAutoRelease data = obs_data_create();\n\tOBSDataAutoRelease settings = obs_service_get_settings(service);\n\n\tobs_data_set_string(data, \"type\", obs_service_get_type(service));\n\tobs_data_set_obj(data, \"settings\", settings);\n\n\tif (!obs_data_save_json_safe(data, jsonFilePath.u8string().c_str(), \"tmp\", \"bak\")) {\n\t\tblog(LOG_WARNING, \"Failed to save service\");\n\t}\n}\n\nbool OBSBasic::LoadService()\n{\n\tOBSDataAutoRelease data;\n\n\ttry {\n\t\tconst OBSProfile &currentProfile = GetCurrentProfile();\n\n\t\tconst std::filesystem::path jsonFilePath =\n\t\t\tcurrentProfile.path / std::filesystem::u8path(OBSServiceFileName);\n\n\t\tdata = obs_data_create_from_json_file_safe(jsonFilePath.u8string().c_str(), \"bak\");\n\n\t\tif (!data) {\n\t\t\treturn false;\n\t\t}\n\t} catch (const std::invalid_argument &error) {\n\t\tblog(LOG_ERROR, \"%s\", error.what());\n\t\treturn false;\n\t}\n\n\tconst char *type;\n\tobs_data_set_default_string(data, \"type\", \"rtmp_common\");\n\ttype = obs_data_get_string(data, \"type\");\n\n\tOBSDataAutoRelease settings = obs_data_get_obj(data, \"settings\");\n\tOBSDataAutoRelease hotkey_data = obs_data_get_obj(data, \"hotkeys\");\n\n\tservice = obs_service_create(type, \"default_service\", settings, hotkey_data);\n\tobs_service_release(service);\n\n\tif (!service)\n\t\treturn false;\n\n\t/* Enforce Opus on WHIP if needed */\n\tif (strcmp(obs_service_get_protocol(service), \"WHIP\") == 0) {\n\t\tconst char *option = config_get_string(activeConfiguration, \"SimpleOutput\", \"StreamAudioEncoder\");\n\t\tif (strcmp(option, \"opus\") != 0)\n\t\t\tconfig_set_string(activeConfiguration, \"SimpleOutput\", \"StreamAudioEncoder\", \"opus\");\n\n\t\toption = config_get_string(activeConfiguration, \"AdvOut\", \"AudioEncoder\");\n\n\t\tconst char *encoder_codec = obs_get_encoder_codec(option);\n\t\tif (!encoder_codec || strcmp(encoder_codec, \"opus\") != 0)\n\t\t\tconfig_set_string(activeConfiguration, \"AdvOut\", \"AudioEncoder\", \"ffmpeg_opus\");\n\t}\n\n\treturn true;\n}\n\nbool OBSBasic::InitService()\n{\n\tProfileScope(\"OBSBasic::InitService\");\n\n\tif (LoadService())\n\t\treturn true;\n\n\tservice = obs_service_create(\"rtmp_common\", \"default_service\", nullptr, nullptr);\n\tif (!service)\n\t\treturn false;\n\tobs_service_release(service);\n\n\treturn true;\n}\n\nobs_service_t *OBSBasic::GetService()\n{\n\tif (!service) {\n\t\tservice = obs_service_create(\"rtmp_common\", NULL, NULL, nullptr);\n\t\tobs_service_release(service);\n\t}\n\treturn service;\n}\n\nvoid OBSBasic::SetService(obs_service_t *newService)\n{\n\tif (newService) {\n\t\tservice = newService;\n\t}\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_StatusBar.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\nvoid OBSBasic::ShowStatusBarMessage(const QString &message)\n{\n\tui->statusbar->clearMessage();\n\tui->statusbar->showMessage(message, 10000);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Streaming.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <components/UIValidation.hpp>\n#ifdef YOUTUBE_ENABLED\n#include <docks/YouTubeAppDock.hpp>\n#include <utility/YoutubeApiWrappers.hpp>\n#endif\n\n#include <qt-wrappers.hpp>\n\n#define STREAMING_START \"==== Streaming Start ===============================================\"\n#define STREAMING_STOP \"==== Streaming Stop ================================================\"\n\nvoid OBSBasic::DisplayStreamStartError()\n{\n\tQString message = !outputHandler->lastError.empty() ? QTStr(outputHandler->lastError.c_str())\n\t\t\t\t\t\t\t    : QTStr(\"Output.StartFailedGeneric\");\n\n\temit StreamingStopped();\n\n\tif (sysTrayStream) {\n\t\tsysTrayStream->setText(QTStr(\"Basic.Main.StartStreaming\"));\n\t\tsysTrayStream->setEnabled(true);\n\t}\n\n\tQMessageBox::critical(this, QTStr(\"Output.StartStreamFailed\"), message);\n}\n\nvoid OBSBasic::StartStreaming()\n{\n\tif (outputHandler->StreamingActive())\n\t\treturn;\n\tif (disableOutputsRef)\n\t\treturn;\n\n\tif (auth && auth->broadcastFlow()) {\n\t\tif (!broadcastActive && !broadcastReady) {\n\t\t\tQMessageBox no_broadcast(this);\n\t\t\tno_broadcast.setText(QTStr(\"Output.NoBroadcast.Text\"));\n\t\t\tQPushButton *SetupBroadcast =\n\t\t\t\tno_broadcast.addButton(QTStr(\"Basic.Main.SetupBroadcast\"), QMessageBox::YesRole);\n\t\t\tno_broadcast.setDefaultButton(SetupBroadcast);\n\t\t\tno_broadcast.addButton(QTStr(\"Close\"), QMessageBox::NoRole);\n\t\t\tno_broadcast.setIcon(QMessageBox::Information);\n\t\t\tno_broadcast.setWindowTitle(QTStr(\"Output.NoBroadcast.Title\"));\n\t\t\tno_broadcast.exec();\n\n\t\t\tif (no_broadcast.clickedButton() == SetupBroadcast)\n\t\t\t\tQMetaObject::invokeMethod(this, \"SetupBroadcast\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\temit StreamingPreparing();\n\n\tif (sysTrayStream) {\n\t\tsysTrayStream->setEnabled(false);\n\t\tsysTrayStream->setText(\"Basic.Main.PreparingStream\");\n\t}\n\n\tauto finish_stream_setup = [&](bool setupStreamingResult) {\n\t\tif (!setupStreamingResult) {\n\t\t\tDisplayStreamStartError();\n\t\t\treturn;\n\t\t}\n\n\t\tOnEvent(OBS_FRONTEND_EVENT_STREAMING_STARTING);\n\n\t\tSaveProject();\n\n\t\temit StreamingStarting(autoStartBroadcast);\n\n\t\tif (sysTrayStream)\n\t\t\tsysTrayStream->setText(\"Basic.Main.Connecting\");\n\n\t\tif (!outputHandler->StartStreaming(service)) {\n\t\t\tDisplayStreamStartError();\n\t\t\treturn;\n\t\t}\n\n\t\tif (autoStartBroadcast) {\n\t\t\temit BroadcastStreamStarted(autoStopBroadcast);\n\t\t\tbroadcastActive = true;\n\t\t}\n\n\t\tbool recordWhenStreaming =\n\t\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"RecordWhenStreaming\");\n\t\tif (recordWhenStreaming)\n\t\t\tStartRecording();\n\n\t\tbool replayBufferWhileStreaming =\n\t\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ReplayBufferWhileStreaming\");\n\t\tif (replayBufferWhileStreaming)\n\t\t\tStartReplayBuffer();\n\n#ifdef YOUTUBE_ENABLED\n\t\tif (!autoStartBroadcast)\n\t\t\tOBSBasic::ShowYouTubeAutoStartWarning();\n#endif\n\t};\n\n\tsetupStreamingGuard = outputHandler->SetupStreaming(service, finish_stream_setup);\n}\n\nvoid OBSBasic::StopStreaming()\n{\n\tSaveProject();\n\n\tif (outputHandler->StreamingActive())\n\t\toutputHandler->StopStreaming(streamingStopping);\n\n\t// special case: force reset broadcast state if\n\t// no autostart and no autostop selected\n\tif (!autoStartBroadcast && !broadcastActive) {\n\t\tbroadcastActive = false;\n\t\tautoStartBroadcast = true;\n\t\tautoStopBroadcast = true;\n\t\tbroadcastReady = false;\n\t}\n\n\tif (autoStopBroadcast) {\n\t\tbroadcastActive = false;\n\t\tbroadcastReady = false;\n\t}\n\n\temit BroadcastStreamReady(broadcastReady);\n\n\tOnDeactivate();\n\n\tbool recordWhenStreaming = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"RecordWhenStreaming\");\n\tbool keepRecordingWhenStreamStops =\n\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"KeepRecordingWhenStreamStops\");\n\tif (recordWhenStreaming && !keepRecordingWhenStreamStops)\n\t\tStopRecording();\n\n\tbool replayBufferWhileStreaming =\n\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ReplayBufferWhileStreaming\");\n\tbool keepReplayBufferStreamStops =\n\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"KeepReplayBufferStreamStops\");\n\tif (replayBufferWhileStreaming && !keepReplayBufferStreamStops)\n\t\tStopReplayBuffer();\n}\n\nvoid OBSBasic::ForceStopStreaming()\n{\n\tSaveProject();\n\n\tif (outputHandler->StreamingActive())\n\t\toutputHandler->StopStreaming(true);\n\n\t// special case: force reset broadcast state if\n\t// no autostart and no autostop selected\n\tif (!autoStartBroadcast && !broadcastActive) {\n\t\tbroadcastActive = false;\n\t\tautoStartBroadcast = true;\n\t\tautoStopBroadcast = true;\n\t\tbroadcastReady = false;\n\t}\n\n\tif (autoStopBroadcast) {\n\t\tbroadcastActive = false;\n\t\tbroadcastReady = false;\n\t}\n\n\temit BroadcastStreamReady(broadcastReady);\n\n\tOnDeactivate();\n\n\tbool recordWhenStreaming = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"RecordWhenStreaming\");\n\tbool keepRecordingWhenStreamStops =\n\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"KeepRecordingWhenStreamStops\");\n\tif (recordWhenStreaming && !keepRecordingWhenStreamStops)\n\t\tStopRecording();\n\n\tbool replayBufferWhileStreaming =\n\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ReplayBufferWhileStreaming\");\n\tbool keepReplayBufferStreamStops =\n\t\tconfig_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"KeepReplayBufferStreamStops\");\n\tif (replayBufferWhileStreaming && !keepReplayBufferStreamStops)\n\t\tStopReplayBuffer();\n}\n\nvoid OBSBasic::StreamDelayStarting(int sec)\n{\n\temit StreamingStarted(true);\n\n\tif (sysTrayStream) {\n\t\tsysTrayStream->setText(QTStr(\"Basic.Main.StopStreaming\"));\n\t\tsysTrayStream->setEnabled(true);\n\t}\n\n\tui->statusbar->StreamDelayStarting(sec);\n\n\tOnActivate();\n}\n\nvoid OBSBasic::StreamDelayStopping(int sec)\n{\n\temit StreamingStopped(true);\n\n\tif (sysTrayStream) {\n\t\tsysTrayStream->setText(QTStr(\"Basic.Main.StartStreaming\"));\n\t\tsysTrayStream->setEnabled(true);\n\t}\n\n\tui->statusbar->StreamDelayStopping(sec);\n\n\tOnEvent(OBS_FRONTEND_EVENT_STREAMING_STOPPING);\n}\n\nvoid OBSBasic::StreamingStart()\n{\n\temit StreamingStarted();\n\tOBSOutputAutoRelease output = obs_frontend_get_streaming_output();\n\tui->statusbar->StreamStarted(output);\n\n\tif (sysTrayStream) {\n\t\tsysTrayStream->setText(QTStr(\"Basic.Main.StopStreaming\"));\n\t\tsysTrayStream->setEnabled(true);\n\t}\n\n#ifdef YOUTUBE_ENABLED\n\tif (!autoStartBroadcast) {\n\t\t// get a current stream key\n\t\tobs_service_t *service_obj = GetService();\n\t\tOBSDataAutoRelease settings = obs_service_get_settings(service_obj);\n\t\tstd::string key = obs_data_get_string(settings, \"stream_id\");\n\t\tif (!key.empty() && !youtubeStreamCheckThread) {\n\t\t\tyoutubeStreamCheckThread = CreateQThread([this, key] { YoutubeStreamCheck(key); });\n\t\t\tyoutubeStreamCheckThread->setObjectName(\"YouTubeStreamCheckThread\");\n\t\t\tyoutubeStreamCheckThread->start();\n\t\t}\n\t}\n#endif\n\n\tOnEvent(OBS_FRONTEND_EVENT_STREAMING_STARTED);\n\n\tOnActivate();\n\n#ifdef YOUTUBE_ENABLED\n\tif (YouTubeAppDock::IsYTServiceSelected())\n\t\tyoutubeAppDock->IngestionStarted();\n#endif\n\n\tblog(LOG_INFO, STREAMING_START);\n}\n\nvoid OBSBasic::StreamStopping()\n{\n\temit StreamingStopping();\n\n\tif (sysTrayStream)\n\t\tsysTrayStream->setText(QTStr(\"Basic.Main.StoppingStreaming\"));\n\n\tstreamingStopping = true;\n\tOnEvent(OBS_FRONTEND_EVENT_STREAMING_STOPPING);\n}\n\nvoid OBSBasic::StreamingStop(int code, QString last_error)\n{\n\tconst char *errorDescription = \"\";\n\tDStr errorMessage;\n\tbool use_last_error = false;\n\tbool encode_error = false;\n\tbool should_reconnect = false;\n\n\t/* Ignore stream key error for multitrack output if its internal reconnect handling is active. */\n\tif (code == OBS_OUTPUT_INVALID_STREAM && outputHandler->multitrackVideo &&\n\t    outputHandler->multitrackVideo->RestartOnError()) {\n\t\tcode = OBS_OUTPUT_SUCCESS;\n\t\tshould_reconnect = true;\n\t}\n\n\tswitch (code) {\n\tcase OBS_OUTPUT_BAD_PATH:\n\t\terrorDescription = Str(\"Output.ConnectFail.BadPath\");\n\t\tbreak;\n\n\tcase OBS_OUTPUT_CONNECT_FAILED:\n\t\tuse_last_error = true;\n\t\terrorDescription = Str(\"Output.ConnectFail.ConnectFailed\");\n\t\tbreak;\n\n\tcase OBS_OUTPUT_INVALID_STREAM:\n\t\terrorDescription = Str(\"Output.ConnectFail.InvalidStream\");\n\t\tbreak;\n\n\tcase OBS_OUTPUT_ENCODE_ERROR:\n\t\tencode_error = true;\n\t\tbreak;\n\n\tcase OBS_OUTPUT_HDR_DISABLED:\n\t\terrorDescription = Str(\"Output.ConnectFail.HdrDisabled\");\n\t\tbreak;\n\n\tdefault:\n\tcase OBS_OUTPUT_ERROR:\n\t\tuse_last_error = true;\n\t\terrorDescription = Str(\"Output.ConnectFail.Error\");\n\t\tbreak;\n\n\tcase OBS_OUTPUT_DISCONNECTED:\n\t\t/* doesn't happen if output is set to reconnect.  note that\n\t\t * reconnects are handled in the output, not in the UI */\n\t\tuse_last_error = true;\n\t\terrorDescription = Str(\"Output.ConnectFail.Disconnected\");\n\t}\n\n\tif (use_last_error && !last_error.isEmpty())\n\t\tdstr_printf(errorMessage, \"%s\\n\\n%s\", errorDescription, QT_TO_UTF8(last_error));\n\telse\n\t\tdstr_copy(errorMessage, errorDescription);\n\n\tui->statusbar->StreamStopped();\n\n\temit StreamingStopped();\n\n\tif (sysTrayStream) {\n\t\tsysTrayStream->setText(QTStr(\"Basic.Main.StartStreaming\"));\n\t\tsysTrayStream->setEnabled(true);\n\t}\n\n\tstreamingStopping = false;\n\tOnEvent(OBS_FRONTEND_EVENT_STREAMING_STOPPED);\n\n\tOnDeactivate();\n\n#ifdef YOUTUBE_ENABLED\n\tif (YouTubeAppDock::IsYTServiceSelected())\n\t\tyoutubeAppDock->IngestionStopped();\n#endif\n\n\tblog(LOG_INFO, STREAMING_STOP);\n\n\tif (encode_error) {\n\t\tQString msg = last_error.isEmpty() ? QTStr(\"Output.StreamEncodeError.Msg\")\n\t\t\t\t\t\t   : QTStr(\"Output.StreamEncodeError.Msg.LastError\").arg(last_error);\n\t\tOBSMessageBox::information(this, QTStr(\"Output.StreamEncodeError.Title\"), msg);\n\n\t} else if (code != OBS_OUTPUT_SUCCESS && isVisible()) {\n\t\tOBSMessageBox::information(this, QTStr(\"Output.ConnectFail.Title\"), QT_UTF8(errorMessage));\n\n\t} else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) {\n\t\tSysTrayNotify(QT_UTF8(errorDescription), QSystemTrayIcon::Warning);\n\t}\n\n\t// Reset broadcast button state/text\n\tif (!broadcastActive)\n\t\tSetBroadcastFlowEnabled(auth && auth->broadcastFlow());\n\tif (should_reconnect)\n\t\tQMetaObject::invokeMethod(this, \"StartStreaming\", Qt::QueuedConnection);\n}\n\nvoid OBSBasic::StreamActionTriggered()\n{\n\tif (outputHandler->StreamingActive()) {\n\t\tbool confirm = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"WarnBeforeStoppingStream\");\n\n#ifdef YOUTUBE_ENABLED\n\t\tif (isVisible() && auth && IsYouTubeService(auth->service()) && autoStopBroadcast) {\n\t\t\tQMessageBox::StandardButton button = OBSMessageBox::question(\n\t\t\t\tthis, QTStr(\"ConfirmStop.Title\"), QTStr(\"YouTube.Actions.AutoStopStreamingWarning\"),\n\t\t\t\tQMessageBox::Yes | QMessageBox::No, QMessageBox::No);\n\n\t\t\tif (button == QMessageBox::No)\n\t\t\t\treturn;\n\n\t\t\tconfirm = false;\n\t\t}\n#endif\n\t\tif (confirm && isVisible()) {\n\t\t\tQMessageBox::StandardButton button =\n\t\t\t\tOBSMessageBox::question(this, QTStr(\"ConfirmStop.Title\"), QTStr(\"ConfirmStop.Text\"),\n\t\t\t\t\t\t\tQMessageBox::Yes | QMessageBox::No, QMessageBox::No);\n\n\t\t\tif (button == QMessageBox::No)\n\t\t\t\treturn;\n\t\t}\n\n\t\tStopStreaming();\n\t} else {\n\t\tif (!UIValidation::NoSourcesConfirmation(this))\n\t\t\treturn;\n\n\t\tAuth *auth = GetAuth();\n\n\t\tauto action = (auth && auth->external()) ? StreamSettingsAction::ContinueStream\n\t\t\t\t\t\t\t : UIValidation::StreamSettingsConfirmation(this, service);\n\t\tswitch (action) {\n\t\tcase StreamSettingsAction::ContinueStream:\n\t\t\tbreak;\n\t\tcase StreamSettingsAction::OpenSettings:\n\t\t\ton_action_Settings_triggered();\n\t\t\treturn;\n\t\tcase StreamSettingsAction::Cancel:\n\t\t\treturn;\n\t\t}\n\n\t\tbool confirm = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"WarnBeforeStartingStream\");\n\n\t\tbool bwtest = false;\n\n\t\tif (this->auth) {\n\t\t\tOBSDataAutoRelease settings = obs_service_get_settings(service);\n\t\t\tbwtest = obs_data_get_bool(settings, \"bwtest\");\n\t\t\t// Disable confirmation if this is going to open broadcast setup\n\t\t\tif (auth && auth->broadcastFlow() && !broadcastReady && !broadcastActive)\n\t\t\t\tconfirm = false;\n\t\t}\n\n\t\tif (bwtest && isVisible()) {\n\t\t\tQMessageBox::StandardButton button = OBSMessageBox::question(this, QTStr(\"ConfirmBWTest.Title\"),\n\t\t\t\t\t\t\t\t\t\t     QTStr(\"ConfirmBWTest.Text\"));\n\n\t\t\tif (button == QMessageBox::No)\n\t\t\t\treturn;\n\t\t} else if (confirm && isVisible()) {\n\t\t\tQMessageBox::StandardButton button =\n\t\t\t\tOBSMessageBox::question(this, QTStr(\"ConfirmStart.Title\"), QTStr(\"ConfirmStart.Text\"),\n\t\t\t\t\t\t\tQMessageBox::Yes | QMessageBox::No, QMessageBox::No);\n\n\t\t\tif (button == QMessageBox::No)\n\t\t\t\treturn;\n\t\t}\n\n\t\tStartStreaming();\n\t}\n}\n\nbool OBSBasic::StreamingActive()\n{\n\tif (!outputHandler)\n\t\treturn false;\n\treturn outputHandler->StreamingActive();\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_StudioMode.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n#include \"OBSProjector.hpp\"\n\n#include <utility/display-helpers.hpp>\n#include <utility/QuickTransition.hpp>\n\n#include <qt-wrappers.hpp>\n#include <slider-ignorewheel.hpp>\n\n#include <QToolTip>\n\nvoid OBSBasic::CreateProgramDisplay()\n{\n\tprogram = new OBSQTDisplay();\n\n\tprogram->setContextMenuPolicy(Qt::CustomContextMenu);\n\tconnect(program.data(), &QWidget::customContextMenuRequested, this, &OBSBasic::ProgramViewContextMenuRequested);\n\n\tauto displayResize = [this]() {\n\t\tstruct obs_video_info ovi;\n\n\t\tif (obs_get_video_info(&ovi))\n\t\t\tResizeProgram(ovi.base_width, ovi.base_height);\n\t};\n\n\tconnect(program.data(), &OBSQTDisplay::DisplayResized, this, displayResize);\n\n\tauto addDisplay = [this](OBSQTDisplay *window) {\n\t\tobs_display_add_draw_callback(window->GetDisplay(), OBSBasic::RenderProgram, this);\n\n\t\tstruct obs_video_info ovi;\n\t\tif (obs_get_video_info(&ovi))\n\t\t\tResizeProgram(ovi.base_width, ovi.base_height);\n\t};\n\n\tconnect(program.data(), &OBSQTDisplay::DisplayCreated, this, addDisplay);\n\n\tprogram->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);\n}\n\n#define T_BAR_PRECISION 1024\n#define T_BAR_PRECISION_F ((float)T_BAR_PRECISION)\n#define T_BAR_CLAMP (T_BAR_PRECISION / 10)\n\nvoid OBSBasic::CreateProgramOptions()\n{\n\tprogramOptions = new QWidget();\n\tQVBoxLayout *layout = new QVBoxLayout();\n\tlayout->setSpacing(4);\n\n\tQPushButton *configTransitions = new QPushButton();\n\tconfigTransitions->setProperty(\"class\", \"icon-dots-vert\");\n\n\tQHBoxLayout *mainButtonLayout = new QHBoxLayout();\n\tmainButtonLayout->setSpacing(2);\n\n\ttransitionButton = new QPushButton(QTStr(\"Transition\"));\n\ttransitionButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);\n\n\tQHBoxLayout *quickTransitionsLayout = new QHBoxLayout();\n\tquickTransitionsLayout->setSpacing(2);\n\n\tQPushButton *addQuickTransition = new QPushButton();\n\taddQuickTransition->setProperty(\"class\", \"icon-plus\");\n\n\tQLabel *quickTransitionsLabel = new QLabel(QTStr(\"QuickTransitions\"));\n\tquickTransitionsLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);\n\n\tquickTransitionsLayout->addWidget(quickTransitionsLabel);\n\tquickTransitionsLayout->addWidget(addQuickTransition);\n\n\tmainButtonLayout->addWidget(transitionButton);\n\tmainButtonLayout->addWidget(configTransitions);\n\n\ttBar = new SliderIgnoreClick(Qt::Horizontal);\n\ttBar->setMinimum(0);\n\ttBar->setMaximum(T_BAR_PRECISION - 1);\n\n\ttBar->setProperty(\"class\", \"slider-tbar\");\n\n\tconnect(tBar, &QSlider::valueChanged, this, &OBSBasic::TBarChanged);\n\tconnect(tBar, &QSlider::sliderReleased, this, &OBSBasic::TBarReleased);\n\n\tlayout->addStretch(0);\n\tlayout->addLayout(mainButtonLayout);\n\tlayout->addLayout(quickTransitionsLayout);\n\tlayout->addWidget(tBar);\n\tlayout->addStretch(0);\n\n\tprogramOptions->setLayout(layout);\n\n\tauto onAdd = [this]() {\n\t\tQScopedPointer<QMenu> menu(CreateTransitionMenu(this, nullptr));\n\t\tmenu->exec(QCursor::pos());\n\t};\n\n\tauto onConfig = [this]() {\n\t\tQMenu menu(this);\n\t\tQAction *action;\n\n\t\tauto toggleEditProperties = [this]() {\n\t\t\teditPropertiesMode = !editPropertiesMode;\n\n\t\t\tOBSSource actualScene = OBSGetStrongRef(programScene);\n\t\t\tif (actualScene)\n\t\t\t\tTransitionToScene(actualScene, true);\n\t\t};\n\n\t\tauto toggleSwapScenesMode = [this]() {\n\t\t\tswapScenesMode = !swapScenesMode;\n\t\t};\n\n\t\tauto toggleSceneDuplication = [this]() {\n\t\t\tsceneDuplicationMode = !sceneDuplicationMode;\n\n\t\t\tOBSSource actualScene = OBSGetStrongRef(programScene);\n\t\t\tif (actualScene)\n\t\t\t\tTransitionToScene(actualScene, true);\n\t\t};\n\n\t\tauto showToolTip = [&]() {\n\t\t\tQAction *act = menu.activeAction();\n\t\t\tQToolTip::showText(QCursor::pos(), act->toolTip(), &menu, menu.actionGeometry(act));\n\t\t};\n\n\t\taction = menu.addAction(QTStr(\"QuickTransitions.DuplicateScene\"));\n\t\taction->setToolTip(QTStr(\"QuickTransitions.DuplicateSceneTT\"));\n\t\taction->setCheckable(true);\n\t\taction->setChecked(sceneDuplicationMode);\n\t\tconnect(action, &QAction::triggered, this, toggleSceneDuplication);\n\t\tconnect(action, &QAction::hovered, action, showToolTip);\n\n\t\taction = menu.addAction(QTStr(\"QuickTransitions.EditProperties\"));\n\t\taction->setToolTip(QTStr(\"QuickTransitions.EditPropertiesTT\"));\n\t\taction->setCheckable(true);\n\t\taction->setChecked(editPropertiesMode);\n\t\taction->setEnabled(sceneDuplicationMode);\n\t\tconnect(action, &QAction::triggered, this, toggleEditProperties);\n\t\tconnect(action, &QAction::hovered, action, showToolTip);\n\n\t\taction = menu.addAction(QTStr(\"QuickTransitions.SwapScenes\"));\n\t\taction->setToolTip(QTStr(\"QuickTransitions.SwapScenesTT\"));\n\t\taction->setCheckable(true);\n\t\taction->setChecked(swapScenesMode);\n\t\tconnect(action, &QAction::triggered, this, toggleSwapScenesMode);\n\t\tconnect(action, &QAction::hovered, action, showToolTip);\n\n\t\tmenu.exec(QCursor::pos());\n\t};\n\n\tconnect(transitionButton.data(), &QAbstractButton::clicked, this, &OBSBasic::TransitionClicked);\n\tconnect(addQuickTransition, &QAbstractButton::clicked, this, onAdd);\n\tconnect(configTransitions, &QAbstractButton::clicked, this, onConfig);\n}\n\nvoid OBSBasic::TogglePreviewProgramMode()\n{\n\tSetPreviewProgramMode(!IsPreviewProgramMode());\n}\n\nvoid OBSBasic::SetPreviewProgramMode(bool enabled)\n{\n\tif (IsPreviewProgramMode() == enabled) {\n\t\treturn;\n\t}\n\n\tos_atomic_set_bool(&previewProgramMode, enabled);\n\temit PreviewProgramModeChanged(enabled);\n\n\tif (IsPreviewProgramMode()) {\n\t\tif (!previewEnabled) {\n\t\t\tEnablePreviewDisplay(true);\n\t\t}\n\n\t\tCreateProgramDisplay();\n\t\tCreateProgramOptions();\n\n\t\tOBSScene curScene = GetCurrentScene();\n\n\t\tOBSSceneAutoRelease dup;\n\t\tif (sceneDuplicationMode) {\n\t\t\tdup = obs_scene_duplicate(curScene, obs_source_get_name(obs_scene_get_source(curScene)),\n\t\t\t\t\t\t  editPropertiesMode ? OBS_SCENE_DUP_PRIVATE_COPY\n\t\t\t\t\t\t\t\t     : OBS_SCENE_DUP_PRIVATE_REFS);\n\t\t} else {\n\t\t\tdup = std::move(OBSScene(curScene));\n\t\t}\n\n\t\tOBSSourceAutoRelease transition = obs_get_output_source(0);\n\t\tobs_source_t *dup_source = obs_scene_get_source(dup);\n\t\tobs_transition_set(transition, dup_source);\n\n\t\tif (curScene) {\n\t\t\tobs_source_t *source = obs_scene_get_source(curScene);\n\t\t\tobs_source_inc_showing(source);\n\t\t\tlastScene = OBSGetWeakRef(source);\n\t\t\tprogramScene = OBSGetWeakRef(source);\n\t\t}\n\n\t\tRefreshQuickTransitions();\n\n\t\tprogramLabel = new QLabel(QTStr(\"StudioMode.ProgramSceneLabel\"), this);\n\t\tprogramLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);\n\t\tprogramLabel->setProperty(\"class\", \"label-preview-title\");\n\n\t\tprogramWidget = new QWidget();\n\t\tprogramLayout = new QVBoxLayout();\n\t\tprogramLayout->setContentsMargins(0, 0, 0, 0);\n\t\tprogramLayout->setSpacing(0);\n\n\t\tprogramLayout->addWidget(programLabel);\n\t\tprogramLayout->addWidget(program);\n\n\t\tprogramWidget->setLayout(programLayout);\n\n\t\tui->previewLayout->addWidget(programOptions);\n\t\tui->previewLayout->addWidget(programWidget);\n\t\tui->previewLayout->setAlignment(programOptions, Qt::AlignCenter);\n\n\t\tsizeObserver = new PreviewProgramSizeObserver(ui->preview, program, this);\n\n\t\tOnEvent(OBS_FRONTEND_EVENT_STUDIO_MODE_ENABLED);\n\n\t\tblog(LOG_INFO, \"Switched to Preview/Program mode\");\n\t\tblog(LOG_INFO, \"-----------------------------\"\n\t\t\t       \"-------------------\");\n\t} else {\n\t\tOBSSource actualProgramScene = OBSGetStrongRef(programScene);\n\t\tif (!actualProgramScene) {\n\t\t\tactualProgramScene = GetCurrentSceneSource();\n\t\t} else {\n\t\t\tSetCurrentScene(actualProgramScene, true);\n\t\t}\n\t\tTransitionToScene(actualProgramScene, true);\n\n\t\tdelete programOptions;\n\t\tdelete program;\n\t\tdelete programLabel;\n\t\tdelete programWidget;\n\t\tsizeObserver->deleteLater();\n\n\t\tif (lastScene) {\n\t\t\tOBSSource actualLastScene = OBSGetStrongRef(lastScene);\n\t\t\tif (actualLastScene)\n\t\t\t\tobs_source_dec_showing(actualLastScene);\n\t\t\tlastScene = nullptr;\n\t\t}\n\n\t\tprogramScene = nullptr;\n\t\tswapScene = nullptr;\n\t\tprevFTBSource = nullptr;\n\n\t\tfor (QuickTransition &qt : quickTransitions) {\n\t\t\tqt.button = nullptr;\n\t\t}\n\n\t\tif (!previewEnabled) {\n\t\t\tEnablePreviewDisplay(false);\n\t\t}\n\n\t\tui->transitions->setEnabled(true);\n\t\ttBarActive = false;\n\n\t\tOnEvent(OBS_FRONTEND_EVENT_STUDIO_MODE_DISABLED);\n\n\t\tblog(LOG_INFO, \"Switched to regular Preview mode\");\n\t\tblog(LOG_INFO, \"-----------------------------\"\n\t\t\t       \"-------------------\");\n\t}\n\n\tResetUI();\n\tUpdateTitleBar();\n}\n\nvoid OBSBasic::RenderProgram(void *data, uint32_t, uint32_t)\n{\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, \"RenderProgram\");\n\n\tOBSBasic *window = static_cast<OBSBasic *>(data);\n\tobs_video_info ovi;\n\n\tobs_get_video_info(&ovi);\n\n\twindow->programCX = int(window->programScale * float(ovi.base_width));\n\twindow->programCY = int(window->programScale * float(ovi.base_height));\n\n\tgs_viewport_push();\n\tgs_projection_push();\n\n\t/* --------------------------------------- */\n\n\tgs_ortho(0.0f, float(ovi.base_width), 0.0f, float(ovi.base_height), -100.0f, 100.0f);\n\tgs_set_viewport(window->programX, window->programY, window->programCX, window->programCY);\n\n\tobs_render_main_texture_src_color_only();\n\tgs_load_vertexbuffer(nullptr);\n\n\t/* --------------------------------------- */\n\n\tgs_projection_pop();\n\tgs_viewport_pop();\n\n\tGS_DEBUG_MARKER_END();\n}\n\nvoid OBSBasic::ResizeProgram(uint32_t cx, uint32_t cy)\n{\n\tQSize targetSize;\n\n\t/* resize program panel to fix to the top section of the window */\n\ttargetSize = GetPixelSize(program);\n\tGetScaleAndCenterPos(int(cx), int(cy), targetSize.width() - PREVIEW_EDGE_SIZE * 2,\n\t\t\t     targetSize.height() - PREVIEW_EDGE_SIZE * 2, programX, programY, programScale);\n\n\tprogramX += float(PREVIEW_EDGE_SIZE);\n\tprogramY += float(PREVIEW_EDGE_SIZE);\n}\n\nvoid OBSBasic::UpdatePreviewProgramIndicators()\n{\n\tbool labels = previewProgramMode ? config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"StudioModeLabels\")\n\t\t\t\t\t : false;\n\n\tui->previewLabel->setVisible(labels);\n\n\tif (programLabel)\n\t\tprogramLabel->setVisible(labels);\n\n\tif (!labels)\n\t\treturn;\n\n\tQString preview =\n\t\tQTStr(\"StudioMode.PreviewSceneName\").arg(QT_UTF8(obs_source_get_name(GetCurrentSceneSource())));\n\n\tQString program = QTStr(\"StudioMode.ProgramSceneName\").arg(QT_UTF8(obs_source_get_name(GetProgramSource())));\n\n\tif (ui->previewLabel->text() != preview)\n\t\tui->previewLabel->setText(preview);\n\n\tif (programLabel && programLabel->text() != program)\n\t\tprogramLabel->setText(program);\n}\n\nOBSSource OBSBasic::GetProgramSource()\n{\n\treturn OBSGetStrongRef(programScene);\n}\n\nvoid OBSBasic::ProgramViewContextMenuRequested()\n{\n\tQMenu popup(this);\n\tQPointer<QMenu> studioProgramProjector;\n\n\tstudioProgramProjector = new QMenu(QTStr(\"Projector.Open.Program\"));\n\tAddProjectorMenuMonitors(studioProgramProjector, this, &OBSBasic::OpenStudioProgramProjector);\n\tstudioProgramProjector->addSeparator();\n\tstudioProgramProjector->addAction(QTStr(\"Projector.Window\"), this, &OBSBasic::OpenStudioProgramWindow);\n\n\tpopup.addMenu(studioProgramProjector);\n\n\tpopup.addSeparator();\n\tpopup.addAction(QTStr(\"Screenshot.StudioProgram\"), this, &OBSBasic::ScreenshotProgram);\n\n\tpopup.exec(QCursor::pos());\n}\n\nvoid OBSBasic::EnablePreviewProgram()\n{\n\tSetPreviewProgramMode(true);\n}\n\nvoid OBSBasic::DisablePreviewProgram()\n{\n\tSetPreviewProgramMode(false);\n}\n\nvoid OBSBasic::OpenStudioProgramProjector()\n{\n\tint monitor = sender()->property(\"monitor\").toInt();\n\tOpenProjector(nullptr, monitor, ProjectorType::StudioProgram);\n}\n\nvoid OBSBasic::OpenStudioProgramWindow()\n{\n\tOpenProjector(nullptr, -1, ProjectorType::StudioProgram);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_SysTray.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\nextern bool opt_minimize_tray;\n\nvoid OBSBasic::SystemTrayInit()\n{\n#ifdef __APPLE__\n\tQIcon trayIconFile = QIcon(\":/res/images/obs_macos.svg\");\n\ttrayIconFile.setIsMask(true);\n#else\n\tQIcon trayIconFile = QIcon(\":/res/images/obs.png\");\n#endif\n\ttrayIcon = new QSystemTrayIcon(QIcon::fromTheme(\"obs-tray\", trayIconFile), this);\n\ttrayIcon->setToolTip(\"OBS Studio\");\n\n\ttrayMenu = new QMenu(this);\n\n\tshowHide = new QAction(QTStr(\"Basic.SystemTray.Show\"), trayMenu);\n\tsysTrayStream = new QAction(\n\t\tStreamingActive() ? QTStr(\"Basic.Main.StopStreaming\") : QTStr(\"Basic.Main.StartStreaming\"), trayMenu);\n\tsysTrayRecord = new QAction(\n\t\tRecordingActive() ? QTStr(\"Basic.Main.StopRecording\") : QTStr(\"Basic.Main.StartRecording\"), trayMenu);\n\tsysTrayReplayBuffer = new QAction(ReplayBufferActive() ? QTStr(\"Basic.Main.StopReplayBuffer\")\n\t\t\t\t\t\t\t       : QTStr(\"Basic.Main.StartReplayBuffer\"),\n\t\t\t\t\t  trayMenu);\n\tsysTrayVirtualCam = new QAction(VirtualCamActive() ? QTStr(\"Basic.Main.StopVirtualCam\")\n\t\t\t\t\t\t\t   : QTStr(\"Basic.Main.StartVirtualCam\"),\n\t\t\t\t\ttrayMenu);\n\texit = new QAction(QTStr(\"Exit\"), trayMenu);\n\n\tpreviewProjector = new QMenu(QTStr(\"Projector.Open.Preview\"), trayMenu);\n\tstudioProgramProjector = new QMenu(QTStr(\"Projector.Open.Program\"), trayMenu);\n\tOBSBasic::updateSysTrayProjectorMenu();\n\n\ttrayMenu->addAction(showHide);\n\ttrayMenu->addSeparator();\n\ttrayMenu->addMenu(previewProjector);\n\ttrayMenu->addMenu(studioProgramProjector);\n\ttrayMenu->addSeparator();\n\ttrayMenu->addAction(sysTrayStream);\n\ttrayMenu->addAction(sysTrayRecord);\n\ttrayMenu->addAction(sysTrayReplayBuffer);\n\ttrayMenu->addAction(sysTrayVirtualCam);\n\ttrayMenu->addSeparator();\n\ttrayMenu->addAction(exit);\n\ttrayIcon->setContextMenu(trayMenu);\n\ttrayIcon->show();\n\n\tif (outputHandler && !outputHandler->replayBuffer)\n\t\tsysTrayReplayBuffer->setEnabled(false);\n\n\tsysTrayVirtualCam->setEnabled(vcamEnabled);\n\n\tif (Active())\n\t\tOnActivate(true);\n\n\tconnect(trayIcon.data(), &QSystemTrayIcon::activated, this, &OBSBasic::IconActivated);\n\tconnect(showHide, &QAction::triggered, this, &OBSBasic::ToggleShowHide);\n\tconnect(sysTrayStream, &QAction::triggered, this, &OBSBasic::StreamActionTriggered);\n\tconnect(sysTrayRecord, &QAction::triggered, this, &OBSBasic::RecordActionTriggered);\n\tconnect(sysTrayReplayBuffer.data(), &QAction::triggered, this, &OBSBasic::ReplayBufferActionTriggered);\n\tconnect(sysTrayVirtualCam.data(), &QAction::triggered, this, &OBSBasic::VirtualCamActionTriggered);\n\tconnect(exit, &QAction::triggered, this, &OBSBasic::close);\n}\n\nvoid OBSBasic::IconActivated(QSystemTrayIcon::ActivationReason reason)\n{\n\tOBSBasic::updateSysTrayProjectorMenu();\n\n#ifdef __APPLE__\n\tUNUSED_PARAMETER(reason);\n#else\n\tif (reason == QSystemTrayIcon::Trigger) {\n\t\tEnablePreviewDisplay(previewEnabled && !isVisible());\n\t\tToggleShowHide();\n\t}\n#endif\n}\n\nvoid OBSBasic::SysTrayNotify(const QString &text, QSystemTrayIcon::MessageIcon n)\n{\n\tif (trayIcon && trayIcon->isVisible() && QSystemTrayIcon::supportsMessages()) {\n\t\tQSystemTrayIcon::MessageIcon icon = QSystemTrayIcon::MessageIcon(n);\n\t\ttrayIcon->showMessage(\"OBS Studio\", text, icon, 10000);\n\t}\n}\n\nvoid OBSBasic::SystemTray(bool firstStarted)\n{\n\tif (!QSystemTrayIcon::isSystemTrayAvailable())\n\t\treturn;\n\tif (!trayIcon && !firstStarted)\n\t\treturn;\n\n\tbool sysTrayWhenStarted = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayWhenStarted\");\n\tbool sysTrayEnabled = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayEnabled\");\n\n\tif (firstStarted)\n\t\tSystemTrayInit();\n\n\tif (!sysTrayEnabled) {\n\t\ttrayIcon->hide();\n\t} else {\n\t\ttrayIcon->show();\n\t\tif (firstStarted && (sysTrayWhenStarted || opt_minimize_tray)) {\n\t\t\tEnablePreviewDisplay(false);\n#ifdef __APPLE__\n\t\t\tEnableOSXDockIcon(false);\n#endif\n\t\t\topt_minimize_tray = false;\n\t\t}\n\t}\n\n\tif (isVisible())\n\t\tshowHide->setText(QTStr(\"Basic.SystemTray.Hide\"));\n\telse\n\t\tshowHide->setText(QTStr(\"Basic.SystemTray.Show\"));\n}\n\nbool OBSBasic::sysTrayMinimizeToTray()\n{\n\treturn config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"SysTrayMinimizeToTray\");\n}\n\nvoid OBSBasic::updateSysTrayProjectorMenu()\n{\n\tpreviewProjector->clear();\n\tstudioProgramProjector->clear();\n\tAddProjectorMenuMonitors(previewProjector, this, &OBSBasic::OpenPreviewProjector);\n\tpreviewProjector->addSeparator();\n\tpreviewProjector->addAction(QTStr(\"Projector.Window\"), this, &OBSBasic::OpenPreviewWindow);\n\tAddProjectorMenuMonitors(studioProgramProjector, this, &OBSBasic::OpenStudioProgramProjector);\n\tstudioProgramProjector->addSeparator();\n\tstudioProgramProjector->addAction(QTStr(\"Projector.Window\"), this, &OBSBasic::OpenStudioProgramWindow);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Transitions.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <algorithm>\n\n#include <components/MenuButton.hpp>\n#include <dialogs/NameDialog.hpp>\n#include <utility/display-helpers.hpp>\n#include <utility/QuickTransition.hpp>\n\n#include <qt-wrappers.hpp>\n#include <slider-ignorewheel.hpp>\n\n#include <QToolTip>\n#include <QWidgetAction>\n\nextern bool disable_3p_plugins;\nextern bool safe_mode;\n\nstatic inline QString MakeQuickTransitionText(QuickTransition *qt)\n{\n\tQString name;\n\n\tif (!qt->fadeToBlack)\n\t\tname = QT_UTF8(obs_source_get_name(qt->source));\n\telse\n\t\tname = QTStr(\"FadeToBlack\");\n\n\tif (!obs_transition_fixed(qt->source))\n\t\tname += QString(\" (%1ms)\").arg(QString::number(qt->duration));\n\treturn name;\n}\n\nvoid OBSBasic::InitDefaultTransitions()\n{\n\tstd::vector<OBSSource> defaultTransitions;\n\tsize_t idx = 0;\n\tconst char *id;\n\n\t/* automatically add transitions that have no configuration (things\n\t * such as cut/fade/etc) */\n\twhile (obs_enum_transition_types(idx++, &id)) {\n\t\tif (!obs_is_source_configurable(id)) {\n\t\t\tconst char *name = obs_source_get_display_name(id);\n\n\t\t\tOBSSourceAutoRelease tr = obs_source_create_private(id, name, NULL);\n\t\t\tInitTransition(tr);\n\t\t\tdefaultTransitions.emplace_back(tr);\n\n\t\t\tif (strcmp(id, \"fade_transition\") == 0)\n\t\t\t\tfadeTransition = tr;\n\t\t\telse if (strcmp(id, \"cut_transition\") == 0)\n\t\t\t\tcutTransition = tr;\n\t\t}\n\t}\n\n\tfor (OBSSource &tr : defaultTransitions) {\n\t\tstd::string uuid = obs_source_get_uuid(tr);\n\n\t\ttransitions.insert({uuid, OBSSource(tr)});\n\t\ttransitionNameToUuids.insert({obs_source_get_name(tr), uuid});\n\t\ttransitionUuids.push_back(uuid);\n\n\t\temit TransitionAdded(QT_UTF8(obs_source_get_name(tr)), QString::fromStdString(uuid));\n\t}\n\n\tUpdateCurrentTransition(transitionUuids.back(), true);\n}\n\nvoid OBSBasic::AddQuickTransitionHotkey(QuickTransition *qt)\n{\n\tDStr hotkeyId;\n\tQString hotkeyName;\n\n\tdstr_printf(hotkeyId, \"OBSBasic.QuickTransition.%d\", qt->id);\n\thotkeyName = QTStr(\"QuickTransitions.HotkeyName\").arg(MakeQuickTransitionText(qt));\n\n\tauto quickTransition = [](void *data, obs_hotkey_id, obs_hotkey_t *, bool pressed) {\n\t\tint id = (int)(uintptr_t)data;\n\t\tOBSBasic *main = OBSBasic::Get();\n\n\t\tif (pressed)\n\t\t\tQMetaObject::invokeMethod(main, \"TriggerQuickTransition\", Qt::QueuedConnection, Q_ARG(int, id));\n\t};\n\n\tqt->hotkey = obs_hotkey_register_frontend(hotkeyId->array, QT_TO_UTF8(hotkeyName), quickTransition,\n\t\t\t\t\t\t  (void *)(uintptr_t)qt->id);\n}\n\nvoid OBSBasic::TriggerQuickTransition(int id)\n{\n\tQuickTransition *qt = GetQuickTransition(id);\n\n\tif (qt && previewProgramMode) {\n\t\tOBSScene scene = GetCurrentScene();\n\t\tobs_source_t *source = obs_scene_get_source(scene);\n\n\t\tif (GetCurrentTransition() != qt->source) {\n\t\t\tOverrideTransition(qt->source);\n\t\t\toverridingTransition = true;\n\t\t}\n\n\t\tTransitionToScene(source, false, true, qt->duration, qt->fadeToBlack);\n\t}\n}\n\nvoid OBSBasic::RemoveQuickTransitionHotkey(QuickTransition *qt)\n{\n\tobs_hotkey_unregister(qt->hotkey);\n}\n\nvoid OBSBasic::InitTransition(obs_source_t *transition)\n{\n\tauto onTransitionStart = [](void *data, calldata_t *) {\n\t\tOBSBasic *window = (OBSBasic *)data;\n\t\tQMetaObject::invokeMethod(window, \"TransitionStarted\", Qt::QueuedConnection);\n\t};\n\n\tauto onTransitionStop = [](void *data, calldata_t *) {\n\t\tOBSBasic *window = (OBSBasic *)data;\n\t\tQMetaObject::invokeMethod(window, \"TransitionStopped\", Qt::QueuedConnection);\n\t};\n\n\tauto onTransitionFullStop = [](void *data, calldata_t *) {\n\t\tOBSBasic *window = (OBSBasic *)data;\n\t\tQMetaObject::invokeMethod(window, \"TransitionFullyStopped\", Qt::QueuedConnection);\n\t};\n\n\tsignal_handler_t *handler = obs_source_get_signal_handler(transition);\n\tsignal_handler_connect(handler, \"transition_start\", onTransitionStart, this);\n\tsignal_handler_connect(handler, \"transition_video_stop\", onTransitionStop, this);\n\tsignal_handler_connect(handler, \"transition_stop\", onTransitionFullStop, this);\n}\n\nstatic inline OBSSource GetTransitionComboItem(QComboBox *combo, int idx)\n{\n\treturn combo->itemData(idx).value<OBSSource>();\n}\n\nvoid OBSBasic::CreateDefaultQuickTransitions()\n{\n\t/* non-configurable transitions are always available, so add them\n\t * to the \"default quick transitions\" list */\n\tquickTransitions.emplace_back(cutTransition, 300, quickTransitionIdCounter++);\n\tquickTransitions.emplace_back(fadeTransition, 300, quickTransitionIdCounter++);\n\tquickTransitions.emplace_back(fadeTransition, 300, quickTransitionIdCounter++, true);\n}\n\nvoid OBSBasic::LoadQuickTransitions(obs_data_array_t *array)\n{\n\tsize_t count = obs_data_array_count(array);\n\n\tquickTransitionIdCounter = 1;\n\n\tfor (size_t i = 0; i < count; i++) {\n\t\tOBSDataAutoRelease data = obs_data_array_item(array, i);\n\t\tOBSDataArrayAutoRelease hotkeys = obs_data_get_array(data, \"hotkeys\");\n\t\tconst char *name = obs_data_get_string(data, \"name\");\n\t\tint duration = obs_data_get_int(data, \"duration\");\n\t\tint id = obs_data_get_int(data, \"id\");\n\t\tbool toBlack = obs_data_get_bool(data, \"fade_to_black\");\n\n\t\tif (id) {\n\t\t\tobs_source_t *source = FindTransition(name);\n\t\t\tif (source) {\n\t\t\t\tquickTransitions.emplace_back(source, duration, id, toBlack);\n\n\t\t\t\tif (quickTransitionIdCounter <= id)\n\t\t\t\t\tquickTransitionIdCounter = id + 1;\n\n\t\t\t\tint idx = (int)quickTransitions.size() - 1;\n\t\t\t\tAddQuickTransitionHotkey(&quickTransitions[idx]);\n\t\t\t\tobs_hotkey_load(quickTransitions[idx].hotkey, hotkeys);\n\t\t\t}\n\t\t}\n\t}\n}\n\nobs_data_array_t *OBSBasic::SaveQuickTransitions()\n{\n\tobs_data_array_t *array = obs_data_array_create();\n\n\tfor (QuickTransition &qt : quickTransitions) {\n\t\tOBSDataAutoRelease data = obs_data_create();\n\t\tOBSDataArrayAutoRelease hotkeys = obs_hotkey_save(qt.hotkey);\n\n\t\tobs_data_set_string(data, \"name\", obs_source_get_name(qt.source));\n\t\tobs_data_set_int(data, \"duration\", qt.duration);\n\t\tobs_data_set_array(data, \"hotkeys\", hotkeys);\n\t\tobs_data_set_int(data, \"id\", qt.id);\n\t\tobs_data_set_bool(data, \"fade_to_black\", qt.fadeToBlack);\n\n\t\tobs_data_array_push_back(array, data);\n\t}\n\n\treturn array;\n}\n\nobs_source_t *OBSBasic::FindTransition(const char *name)\n{\n\tauto nameToUuid = transitionNameToUuids.find(name);\n\n\tif (nameToUuid != transitionNameToUuids.end()) {\n\t\tauto transition = transitions.find(nameToUuid->second);\n\n\t\tif (transition == transitions.end())\n\t\t\treturn nullptr;\n\n\t\treturn transition->second;\n\t}\n\n\treturn nullptr;\n}\n\nvoid OBSBasic::TransitionToScene(OBSScene scene, bool force)\n{\n\tobs_source_t *source = obs_scene_get_source(scene);\n\tTransitionToScene(source, force);\n}\n\nvoid OBSBasic::TransitionStarted()\n{\n\tEnableTransitionWidgets(false);\n}\n\nvoid OBSBasic::TransitionStopped()\n{\n\tif (swapScenesMode) {\n\t\tOBSSource scene = OBSGetStrongRef(swapScene);\n\t\tif (scene)\n\t\t\tSetCurrentScene(scene);\n\t}\n\n\tEnableTransitionWidgets(true);\n\tUpdatePreviewProgramIndicators();\n\n\tOnEvent(OBS_FRONTEND_EVENT_TRANSITION_STOPPED);\n\tOnEvent(OBS_FRONTEND_EVENT_SCENE_CHANGED);\n\n\tswapScene = nullptr;\n}\n\nvoid OBSBasic::OverrideTransition(OBSSource transition)\n{\n\tOBSSourceAutoRelease oldTransition = obs_get_output_source(0);\n\n\tif (transition != oldTransition) {\n\t\tobs_transition_swap_begin(transition, oldTransition);\n\t\tobs_set_output_source(0, transition);\n\t\tobs_transition_swap_end(transition, oldTransition);\n\t}\n}\n\nvoid OBSBasic::TransitionFullyStopped()\n{\n\tEnableTransitionWidgets(true);\n\n\tif (overridingTransition) {\n\t\tOverrideTransition(GetCurrentTransition());\n\t\toverridingTransition = false;\n\t}\n}\n\nvoid OBSBasic::TransitionToScene(OBSSource source, bool force, bool quickTransition, int quickDuration, bool black,\n\t\t\t\t bool manual)\n{\n\tobs_scene_t *scene = obs_scene_from_source(source);\n\tbool usingPreviewProgram = IsPreviewProgramMode();\n\tif (!scene)\n\t\treturn;\n\n\tif (usingPreviewProgram) {\n\t\tif (!tBarActive)\n\t\t\tlastProgramScene = programScene;\n\t\tprogramScene = OBSGetWeakRef(source);\n\n\t\tif (!force && !black) {\n\t\t\tOBSSource lastScene = OBSGetStrongRef(lastProgramScene);\n\n\t\t\tif (!sceneDuplicationMode && lastScene == source)\n\t\t\t\treturn;\n\n\t\t\tif (swapScenesMode && lastScene && lastScene != GetCurrentSceneSource())\n\t\t\t\tswapScene = lastProgramScene;\n\t\t}\n\t}\n\n\tif (usingPreviewProgram && sceneDuplicationMode) {\n\t\tscene = obs_scene_duplicate(scene, obs_source_get_name(obs_scene_get_source(scene)),\n\t\t\t\t\t    editPropertiesMode ? OBS_SCENE_DUP_PRIVATE_COPY\n\t\t\t\t\t\t\t       : OBS_SCENE_DUP_PRIVATE_REFS);\n\t\tsource = obs_scene_get_source(scene);\n\t}\n\n\tOBSSourceAutoRelease transition = obs_get_output_source(0);\n\tif (!transition) {\n\t\tif (usingPreviewProgram && sceneDuplicationMode)\n\t\t\tobs_scene_release(scene);\n\t\treturn;\n\t}\n\n\tfloat t = obs_transition_get_time(transition);\n\tbool stillTransitioning = t < 1.0f && t > 0.0f;\n\n\t// If actively transitioning, block new transitions from starting\n\tif (usingPreviewProgram && stillTransitioning)\n\t\tgoto cleanup;\n\n\tif (usingPreviewProgram) {\n\t\tif (!black && !manual) {\n\t\t\tconst char *sceneName = obs_source_get_name(source);\n\t\t\tblog(LOG_INFO, \"User switched Program to scene '%s'\", sceneName);\n\n\t\t} else if (black && !prevFTBSource) {\n\t\t\tOBSSourceAutoRelease target = obs_transition_get_active_source(transition);\n\t\t\tconst char *sceneName = obs_source_get_name(target);\n\t\t\tblog(LOG_INFO, \"User faded from scene '%s' to black\", sceneName);\n\n\t\t} else if (black && prevFTBSource) {\n\t\t\tconst char *sceneName = obs_source_get_name(prevFTBSource);\n\t\t\tblog(LOG_INFO, \"User faded from black to scene '%s'\", sceneName);\n\n\t\t} else if (manual) {\n\t\t\tconst char *sceneName = obs_source_get_name(source);\n\t\t\tblog(LOG_INFO, \"User started manual transition to scene '%s'\", sceneName);\n\t\t}\n\t}\n\n\tif (force) {\n\t\tobs_transition_set(transition, source);\n\t\tOnEvent(OBS_FRONTEND_EVENT_SCENE_CHANGED);\n\t} else {\n\t\tint duration = GetTransitionDuration();\n\n\t\t/* check for scene override */\n\t\tOBSSource trOverride = GetOverrideTransition(source);\n\n\t\tif (trOverride && !overridingTransition && !quickTransition) {\n\t\t\ttransition = std::move(trOverride);\n\t\t\tduration = GetOverrideTransitionDuration(source);\n\t\t\tOverrideTransition(transition.Get());\n\t\t\toverridingTransition = true;\n\t\t}\n\n\t\tif (black && !prevFTBSource) {\n\t\t\tprevFTBSource = source;\n\t\t\tsource = nullptr;\n\t\t} else if (black && prevFTBSource) {\n\t\t\tsource = prevFTBSource;\n\t\t\tprevFTBSource = nullptr;\n\t\t} else if (!black) {\n\t\t\tprevFTBSource = nullptr;\n\t\t}\n\n\t\tif (quickTransition)\n\t\t\tduration = quickDuration;\n\n\t\tenum obs_transition_mode mode = manual ? OBS_TRANSITION_MODE_MANUAL : OBS_TRANSITION_MODE_AUTO;\n\n\t\tbool success = obs_transition_start(transition, mode, duration, source);\n\n\t\tif (!success)\n\t\t\tTransitionFullyStopped();\n\t}\n\ncleanup:\n\tif (usingPreviewProgram && sceneDuplicationMode)\n\t\tobs_scene_release(scene);\n}\n\nstatic inline void SetComboTransition(QComboBox *combo, obs_source_t *tr)\n{\n\tint idx = combo->findData(QVariant::fromValue<OBSSource>(tr));\n\tif (idx != -1) {\n\t\tcombo->blockSignals(true);\n\t\tcombo->setCurrentIndex(idx);\n\t\tcombo->blockSignals(false);\n\t}\n}\n\nvoid OBSBasic::SetTransition(OBSSource transition)\n{\n\tOBSSourceAutoRelease oldTransition = obs_get_output_source(0);\n\n\tif (oldTransition && obs_transition_is_active(oldTransition)) {\n\t\treturn;\n\t}\n\n\tif (oldTransition && transition) {\n\t\tstd::string uuid = obs_source_get_uuid(transition);\n\t\tobs_transition_swap_begin(transition, oldTransition);\n\t\tif (currentTransitionUuid != uuid)\n\t\t\tUpdateCurrentTransition(uuid, false);\n\t\tobs_set_output_source(0, transition);\n\t\tobs_transition_swap_end(transition, oldTransition);\n\t} else {\n\t\tobs_set_output_source(0, transition);\n\t}\n\n\tbool fixed = transition ? obs_transition_fixed(transition) : false;\n\tui->transitionDurationLabel->setVisible(!fixed);\n\tui->transitionDuration->setVisible(!fixed);\n\n\tbool configurable = transition ? obs_source_configurable(transition) : false;\n\tui->transitionRemove->setEnabled(configurable);\n\tui->transitionProps->setEnabled(configurable);\n\n\tOnEvent(OBS_FRONTEND_EVENT_TRANSITION_CHANGED);\n}\n\nOBSSource OBSBasic::GetCurrentTransition()\n{\n\tauto transition = transitions.find(currentTransitionUuid);\n\n\tif (transition == transitions.end())\n\t\treturn nullptr;\n\n\treturn transition->second;\n}\n\nvoid OBSBasic::AddTransition(const char *id)\n{\n\tstd::string name;\n\tQString placeHolderText = QT_UTF8(obs_source_get_display_name(id));\n\tQString format = placeHolderText + \" (%1)\";\n\tobs_source_t *source = nullptr;\n\tint i = 1;\n\n\twhile ((FindTransition(QT_TO_UTF8(placeHolderText)))) {\n\t\tplaceHolderText = format.arg(++i);\n\t}\n\n\tbool accepted = NameDialog::AskForName(this, QTStr(\"TransitionNameDlg.Title\"), QTStr(\"TransitionNameDlg.Text\"),\n\t\t\t\t\t       name, placeHolderText);\n\n\tif (accepted) {\n\t\tstd::string uuid;\n\n\t\tif (name.empty()) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\t\tAddTransition(id);\n\t\t\treturn;\n\t\t}\n\n\t\tsource = FindTransition(name.c_str());\n\t\tif (source) {\n\t\t\tOBSMessageBox::warning(this, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\n\t\t\tAddTransition(id);\n\t\t\treturn;\n\t\t}\n\n\t\tsource = obs_source_create_private(id, name.c_str(), NULL);\n\t\tInitTransition(source);\n\n\t\tuuid = obs_source_get_uuid(source);\n\t\ttransitions.insert({uuid, source});\n\t\ttransitionNameToUuids.insert({name, uuid});\n\t\ttransitionUuids.push_back(uuid);\n\n\t\temit TransitionAdded(QString::fromStdString(name), QString::fromStdString(uuid));\n\n\t\tUpdateCurrentTransition(uuid, true);\n\n\t\tCreatePropertiesWindow(source);\n\t\tobs_source_release(source);\n\n\t\tOnEvent(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED);\n\n\t\tClearQuickTransitionWidgets();\n\t\tRefreshQuickTransitions();\n\t}\n}\n\nvoid OBSBasic::on_transitionAdd_clicked()\n{\n\tbool foundConfigurableTransitions = false;\n\tQMenu menu(this);\n\tsize_t idx = 0;\n\tconst char *id;\n\n\twhile (obs_enum_transition_types(idx++, &id)) {\n\t\tif (obs_is_source_configurable(id)) {\n\t\t\tconst char *name = obs_source_get_display_name(id);\n\t\t\tQAction *action = new QAction(name, this);\n\n\t\t\tconnect(action, &QAction::triggered, this, [this, id]() { AddTransition(id); });\n\n\t\t\tmenu.addAction(action);\n\t\t\tfoundConfigurableTransitions = true;\n\t\t}\n\t}\n\n\tif (foundConfigurableTransitions)\n\t\tmenu.exec(QCursor::pos());\n}\n\nvoid OBSBasic::on_transitionRemove_clicked()\n{\n\tauto transitionIterator = transitions.find(currentTransitionUuid);\n\tOBSSource tr;\n\tconst char *name;\n\n\tif (transitionIterator == transitions.end())\n\t\treturn;\n\n\ttr = transitionIterator->second;\n\n\tif (!tr || !obs_source_configurable(tr) || !QueryRemoveSource(tr))\n\t\treturn;\n\n\tfor (size_t i = quickTransitions.size(); i > 0; i--) {\n\t\tQuickTransition &qt = quickTransitions[i - 1];\n\t\tif (qt.source == tr) {\n\t\t\tif (qt.button)\n\t\t\t\tqt.button->deleteLater();\n\t\t\tRemoveQuickTransitionHotkey(&qt);\n\t\t\tquickTransitions.erase(quickTransitions.begin() + i - 1);\n\t\t}\n\t}\n\n\tname = obs_source_get_name(tr);\n\tif (name)\n\t\ttransitionNameToUuids.erase(std::string(name));\n\n\ttransitionUuids.erase(std::find(transitionUuids.begin(), transitionUuids.end(), currentTransitionUuid));\n\ttransitions.erase(currentTransitionUuid);\n\temit TransitionRemoved(QString::fromStdString(currentTransitionUuid));\n\n\tUpdateCurrentTransition(transitionUuids.back(), true);\n\n\tOnEvent(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED);\n\n\tClearQuickTransitionWidgets();\n\tRefreshQuickTransitions();\n}\n\nvoid OBSBasic::RenameTransition(OBSSource transition)\n{\n\tstd::string name;\n\tstd::string oldName = obs_source_get_name(transition);\n\tstd::string uuid = obs_source_get_uuid(transition);\n\tQString placeHolderText = QString::fromStdString(oldName);\n\tobs_source_t *source = nullptr;\n\n\tbool accepted = NameDialog::AskForName(this, QTStr(\"TransitionNameDlg.Title\"), QTStr(\"TransitionNameDlg.Text\"),\n\t\t\t\t\t       name, placeHolderText);\n\n\tif (!accepted) {\n\t\treturn;\n\t}\n\n\tif (name.empty()) {\n\t\tOBSMessageBox::warning(this, QTStr(\"NoNameEntered.Title\"), QTStr(\"NoNameEntered.Text\"));\n\t\tRenameTransition(transition);\n\t\treturn;\n\t}\n\n\tif (name == oldName) {\n\t\treturn;\n\t}\n\n\tsource = FindTransition(name.c_str());\n\tif (source) {\n\t\tOBSMessageBox::warning(this, QTStr(\"NameExists.Title\"), QTStr(\"NameExists.Text\"));\n\n\t\tRenameTransition(transition);\n\t\treturn;\n\t}\n\n\tobs_source_set_name(transition, name.c_str());\n\n\tif (transitionNameToUuids.find(oldName) == transitionNameToUuids.end())\n\t\treturn;\n\n\ttransitionNameToUuids.erase(oldName);\n\ttransitionNameToUuids.insert({name, uuid});\n\n\temit TransitionRenamed(QString::fromStdString(uuid), QString::fromStdString(name));\n\n\tOnEvent(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED);\n\n\tClearQuickTransitionWidgets();\n\tRefreshQuickTransitions();\n}\n\nvoid OBSBasic::on_transitionProps_clicked()\n{\n\tOBSSource source = GetCurrentTransition();\n\n\tif (!obs_source_configurable(source))\n\t\treturn;\n\n\tauto properties = [&]() {\n\t\tCreatePropertiesWindow(source);\n\t};\n\n\tQMenu menu(this);\n\n\tQAction *action = new QAction(QTStr(\"Rename\"), &menu);\n\tconnect(action, &QAction::triggered, this, [this, source]() { RenameTransition(source); });\n\tmenu.addAction(action);\n\n\taction = new QAction(QTStr(\"Properties\"), &menu);\n\tconnect(action, &QAction::triggered, this, properties);\n\tmenu.addAction(action);\n\n\tmenu.exec(QCursor::pos());\n}\n\nQuickTransition *OBSBasic::GetQuickTransition(int id)\n{\n\tfor (QuickTransition &qt : quickTransitions) {\n\t\tif (qt.id == id)\n\t\t\treturn &qt;\n\t}\n\n\treturn nullptr;\n}\n\nint OBSBasic::GetQuickTransitionIdx(int id)\n{\n\tfor (int idx = 0; idx < (int)quickTransitions.size(); idx++) {\n\t\tQuickTransition &qt = quickTransitions[idx];\n\n\t\tif (qt.id == id)\n\t\t\treturn idx;\n\t}\n\n\treturn -1;\n}\n\nvoid OBSBasic::SetCurrentScene(obs_scene_t *scene, bool force)\n{\n\tobs_source_t *source = obs_scene_get_source(scene);\n\tSetCurrentScene(source, force);\n}\n\nvoid OBSBasic::SetCurrentScene(OBSSource scene, bool force)\n{\n\tif (!IsPreviewProgramMode()) {\n\t\tTransitionToScene(scene, force);\n\t} else {\n\t\tOBSSource actualLastScene = OBSGetStrongRef(lastScene);\n\t\tif (actualLastScene != scene) {\n\t\t\tif (scene)\n\t\t\t\tobs_source_inc_showing(scene);\n\t\t\tif (actualLastScene)\n\t\t\t\tobs_source_dec_showing(actualLastScene);\n\t\t\tlastScene = OBSGetWeakRef(scene);\n\t\t}\n\t}\n\n\tif (obs_scene_get_source(GetCurrentScene()) != scene) {\n\t\tfor (int i = 0; i < ui->scenes->count(); i++) {\n\t\t\tQListWidgetItem *item = ui->scenes->item(i);\n\t\t\tOBSScene itemScene = GetOBSRef<OBSScene>(item);\n\t\t\tobs_source_t *source = obs_scene_get_source(itemScene);\n\n\t\t\tif (source == scene) {\n\t\t\t\tui->scenes->blockSignals(true);\n\t\t\t\tcurrentScene = itemScene.Get();\n\t\t\t\tui->scenes->setCurrentItem(item);\n\t\t\t\tui->scenes->blockSignals(false);\n\n\t\t\t\tif (vcamEnabled && vcamConfig.type == VCamOutputType::PreviewOutput)\n\t\t\t\t\toutputHandler->UpdateVirtualCamOutputSource();\n\n\t\t\t\tOnEvent(OBS_FRONTEND_EVENT_PREVIEW_SCENE_CHANGED);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tUpdateContextBar(true);\n\tUpdatePreviewProgramIndicators();\n\n\tif (scene) {\n\t\tbool userSwitched = (!force && !disableSaving);\n\t\tblog(LOG_INFO, \"%s to scene '%s'\", userSwitched ? \"User switched\" : \"Switched\",\n\t\t     obs_source_get_name(scene));\n\t}\n}\n\nvoid OBSBasic::TransitionClicked()\n{\n\tif (previewProgramMode)\n\t\tTransitionToScene(GetCurrentScene());\n}\n\n#define T_BAR_PRECISION 1024\n#define T_BAR_PRECISION_F ((float)T_BAR_PRECISION)\n#define T_BAR_CLAMP (T_BAR_PRECISION / 10)\n\nvoid OBSBasic::TBarReleased()\n{\n\tint val = tBar->value();\n\n\tOBSSourceAutoRelease transition = obs_get_output_source(0);\n\n\tif ((tBar->maximum() - val) <= T_BAR_CLAMP) {\n\t\tobs_transition_set_manual_time(transition, 1.0f);\n\t\ttBar->blockSignals(true);\n\t\ttBar->setValue(0);\n\t\ttBar->blockSignals(false);\n\t\ttBarActive = false;\n\t\tEnableTransitionWidgets(true);\n\n\t\tOBSSourceAutoRelease target = obs_transition_get_active_source(transition);\n\t\tconst char *sceneName = obs_source_get_name(target);\n\t\tblog(LOG_INFO, \"Manual transition to scene '%s' finished\", sceneName);\n\t} else if (val <= T_BAR_CLAMP) {\n\t\tobs_transition_set_manual_time(transition, 0.0f);\n\t\tTransitionFullyStopped();\n\t\ttBar->blockSignals(true);\n\t\ttBar->setValue(0);\n\t\ttBar->blockSignals(false);\n\t\ttBarActive = false;\n\t\tEnableTransitionWidgets(true);\n\t\tprogramScene = lastProgramScene;\n\t\tblog(LOG_INFO, \"Manual transition cancelled\");\n\t}\n\n\ttBar->clearFocus();\n}\n\nstatic bool ValidTBarTransition(OBSSource transition)\n{\n\tif (!transition)\n\t\treturn false;\n\n\tQString id = QT_UTF8(obs_source_get_id(transition));\n\n\tif (id == \"cut_transition\" || id == \"obs_stinger_transition\")\n\t\treturn false;\n\n\treturn true;\n}\n\nvoid OBSBasic::TBarChanged(int value)\n{\n\tOBSSourceAutoRelease transition = obs_get_output_source(0);\n\n\tif (!tBarActive) {\n\t\tOBSSource sceneSource = GetCurrentSceneSource();\n\t\tOBSSource tBarTr = GetOverrideTransition(sceneSource);\n\n\t\tif (!ValidTBarTransition(tBarTr)) {\n\t\t\ttBarTr = GetCurrentTransition();\n\n\t\t\tif (!ValidTBarTransition(tBarTr))\n\t\t\t\ttBarTr = FindTransition(obs_source_get_display_name(\"fade_transition\"));\n\n\t\t\tOverrideTransition(tBarTr);\n\t\t\toverridingTransition = true;\n\n\t\t\ttransition = std::move(tBarTr);\n\t\t}\n\n\t\tobs_transition_set_manual_torque(transition, 8.0f, 0.05f);\n\t\tTransitionToScene(sceneSource, false, false, false, 0, true);\n\t\ttBarActive = true;\n\t}\n\n\tfloat clampedValue = std::clamp<float>((float)value / T_BAR_PRECISION_F, 0.01f, 0.99f);\n\tobs_transition_set_manual_time(transition, clampedValue);\n\n\tOnEvent(OBS_FRONTEND_EVENT_TBAR_VALUE_CHANGED);\n}\n\nint OBSBasic::GetTbarPosition()\n{\n\treturn tBar->value();\n}\n\nstatic inline void ResetQuickTransitionText(QuickTransition *qt)\n{\n\tqt->button->setText(MakeQuickTransitionText(qt));\n}\n\nQMenu *OBSBasic::CreatePerSceneTransitionMenu()\n{\n\tOBSSource scene = GetCurrentSceneSource();\n\tQMenu *menu = new QMenu(QTStr(\"TransitionOverride\"));\n\n\tOBSDataAutoRelease data = obs_source_get_private_settings(scene);\n\n\tobs_data_set_default_int(data, \"transition_duration\", 300);\n\n\tconst char *curTransition = obs_data_get_string(data, \"transition\");\n\tint curDuration = (int)obs_data_get_int(data, \"transition_duration\");\n\n\tQSpinBox *duration = new QSpinBox(menu);\n\tduration->setMinimum(50);\n\tduration->setSuffix(\" ms\");\n\tduration->setMaximum(20000);\n\tduration->setSingleStep(50);\n\tduration->setValue(curDuration);\n\n\tauto setTransition = [this](QAction *action) {\n\t\tstd::string uuid = action->property(\"transition_uuid\").toString().toStdString();\n\t\tOBSSource scene = GetCurrentSceneSource();\n\t\tOBSDataAutoRelease data = obs_source_get_private_settings(scene);\n\t\tauto transitionIter = transitions.find(uuid);\n\t\tOBSSource transition;\n\n\t\tif (uuid.empty()) {\n\t\t\tobs_data_set_string(data, \"transition\", \"\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (transitionIter == transitions.end())\n\t\t\treturn;\n\n\t\ttransition = transitionIter->second;\n\n\t\tif (transition) {\n\t\t\tconst char *name = obs_source_get_name(transition);\n\t\t\tobs_data_set_string(data, \"transition\", name);\n\t\t}\n\t};\n\n\tauto setDuration = [this](int duration) {\n\t\tOBSSource scene = GetCurrentSceneSource();\n\t\tOBSDataAutoRelease data = obs_source_get_private_settings(scene);\n\n\t\tobs_data_set_int(data, \"transition_duration\", duration);\n\t};\n\n\tconnect(duration, &QSpinBox::valueChanged, this, setDuration);\n\n\tauto addAction = [&](const std::string &uuid = \"\") {\n\t\tconst char *name = \"\";\n\t\tQAction *action;\n\n\t\tif (!uuid.empty()) {\n\t\t\tauto transition = transitions.find(uuid);\n\t\t\tif (transition == transitions.end())\n\t\t\t\treturn;\n\n\t\t\tname = obs_source_get_name(transition->second);\n\t\t}\n\n\t\tbool match = (name && strcmp(name, curTransition) == 0);\n\n\t\tif (!name || !*name)\n\t\t\tname = Str(\"None\");\n\n\t\taction = menu->addAction(QT_UTF8(name));\n\t\taction->setProperty(\"transition_uuid\", QString::fromStdString(uuid));\n\t\taction->setCheckable(true);\n\t\taction->setChecked(match);\n\n\t\tconnect(action, &QAction::triggered, this, std::bind(setTransition, action));\n\t};\n\n\taddAction();\n\tfor (const auto &[uuid, transition] : transitions)\n\t\taddAction(uuid);\n\n\tQWidgetAction *durationAction = new QWidgetAction(menu);\n\tdurationAction->setDefaultWidget(duration);\n\n\tmenu->addSeparator();\n\tmenu->addAction(durationAction);\n\treturn menu;\n}\n\nvoid OBSBasic::ShowTransitionProperties()\n{\n\tOBSSceneItem item = GetCurrentSceneItem();\n\tOBSSource source = obs_sceneitem_get_transition(item, true);\n\n\tif (source)\n\t\tCreatePropertiesWindow(source);\n}\n\nvoid OBSBasic::HideTransitionProperties()\n{\n\tOBSSceneItem item = GetCurrentSceneItem();\n\tOBSSource source = obs_sceneitem_get_transition(item, false);\n\n\tif (source)\n\t\tCreatePropertiesWindow(source);\n}\n\nvoid OBSBasic::PasteShowHideTransition(obs_sceneitem_t *item, bool show, obs_source_t *tr, int duration)\n{\n\tint64_t sceneItemId = obs_sceneitem_get_id(item);\n\tstd::string sceneUUID = obs_source_get_uuid(obs_scene_get_source(obs_sceneitem_get_scene(item)));\n\n\tauto undo_redo = [sceneUUID, sceneItemId, show](const std::string &data) {\n\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(sceneUUID.c_str());\n\t\tobs_scene_t *scene = obs_scene_from_source(source);\n\t\tobs_sceneitem_t *i = obs_scene_find_sceneitem_by_id(scene, sceneItemId);\n\t\tif (i) {\n\t\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\t\tobs_sceneitem_transition_load(i, dat, show);\n\t\t}\n\t};\n\n\tOBSDataAutoRelease oldTransitionData = obs_sceneitem_transition_save(item, show);\n\n\tOBSSourceAutoRelease dup = obs_source_duplicate(tr, obs_source_get_name(tr), true);\n\tobs_sceneitem_set_transition(item, show, dup);\n\tobs_sceneitem_set_transition_duration(item, show, duration);\n\n\tOBSDataAutoRelease transitionData = obs_sceneitem_transition_save(item, show);\n\n\tstd::string undo_data(obs_data_get_json(oldTransitionData));\n\tstd::string redo_data(obs_data_get_json(transitionData));\n\tif (undo_data.compare(redo_data) == 0)\n\t\treturn;\n\n\tQString text = show ? QTStr(\"Undo.ShowTransition\") : QTStr(\"Undo.HideTransition\");\n\tconst char *name = obs_source_get_name(obs_sceneitem_get_source(item));\n\tundo_s.add_action(text.arg(name), undo_redo, undo_redo, undo_data, redo_data);\n}\n\nQMenu *OBSBasic::CreateVisibilityTransitionMenu(bool visible)\n{\n\tOBSSceneItem si = GetCurrentSceneItem();\n\n\tQMenu *menu = new QMenu(QTStr(visible ? \"ShowTransition\" : \"HideTransition\"));\n\tQAction *action;\n\n\tOBSSource curTransition = obs_sceneitem_get_transition(si, visible);\n\tconst char *curId = curTransition ? obs_source_get_id(curTransition) : nullptr;\n\tint curDuration = (int)obs_sceneitem_get_transition_duration(si, visible);\n\n\tif (curDuration <= 0)\n\t\tcurDuration = obs_frontend_get_transition_duration();\n\n\tQSpinBox *duration = new QSpinBox(menu);\n\tduration->setMinimum(50);\n\tduration->setSuffix(\" ms\");\n\tduration->setMaximum(20000);\n\tduration->setSingleStep(50);\n\tduration->setValue(curDuration);\n\n\tauto setTransition = [this](QAction *action, bool visible) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\n\t\tQString id = action->property(\"transition_id\").toString();\n\t\tOBSSceneItem sceneItem = main->GetCurrentSceneItem();\n\t\tint64_t sceneItemId = obs_sceneitem_get_id(sceneItem);\n\t\tstd::string sceneUUID = obs_source_get_uuid(obs_scene_get_source(obs_sceneitem_get_scene(sceneItem)));\n\n\t\tauto undo_redo = [sceneUUID, sceneItemId, visible](const std::string &data) {\n\t\t\tOBSSourceAutoRelease source = obs_get_source_by_uuid(sceneUUID.c_str());\n\t\t\tobs_scene_t *scene = obs_scene_from_source(source);\n\t\t\tobs_sceneitem_t *i = obs_scene_find_sceneitem_by_id(scene, sceneItemId);\n\t\t\tif (i) {\n\t\t\t\tOBSDataAutoRelease dat = obs_data_create_from_json(data.c_str());\n\t\t\t\tobs_sceneitem_transition_load(i, dat, visible);\n\t\t\t}\n\t\t};\n\t\tOBSDataAutoRelease oldTransitionData = obs_sceneitem_transition_save(sceneItem, visible);\n\t\tif (id.isNull() || id.isEmpty()) {\n\t\t\tobs_sceneitem_set_transition(sceneItem, visible, nullptr);\n\t\t\tobs_sceneitem_set_transition_duration(sceneItem, visible, 0);\n\t\t} else {\n\t\t\tOBSSource tr = obs_sceneitem_get_transition(sceneItem, visible);\n\n\t\t\tif (!tr || strcmp(QT_TO_UTF8(id), obs_source_get_id(tr)) != 0) {\n\t\t\t\tQString name = QT_UTF8(obs_source_get_name(obs_sceneitem_get_source(sceneItem)));\n\t\t\t\tname += \" \";\n\t\t\t\tname += QTStr(visible ? \"ShowTransition\" : \"HideTransition\");\n\t\t\t\ttr = obs_source_create_private(QT_TO_UTF8(id), QT_TO_UTF8(name), nullptr);\n\t\t\t\tobs_sceneitem_set_transition(sceneItem, visible, tr);\n\t\t\t\tobs_source_release(tr);\n\n\t\t\t\tint duration = (int)obs_sceneitem_get_transition_duration(sceneItem, visible);\n\t\t\t\tif (duration <= 0) {\n\t\t\t\t\tduration = obs_frontend_get_transition_duration();\n\t\t\t\t\tobs_sceneitem_set_transition_duration(sceneItem, visible, duration);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (obs_source_configurable(tr))\n\t\t\t\tCreatePropertiesWindow(tr);\n\t\t}\n\t\tOBSDataAutoRelease newTransitionData = obs_sceneitem_transition_save(sceneItem, visible);\n\t\tstd::string undo_data(obs_data_get_json(oldTransitionData));\n\t\tstd::string redo_data(obs_data_get_json(newTransitionData));\n\t\tif (undo_data.compare(redo_data) != 0)\n\t\t\tmain->undo_s.add_action(QTStr(visible ? \"Undo.ShowTransition\" : \"Undo.HideTransition\")\n\t\t\t\t\t\t\t.arg(obs_source_get_name(obs_sceneitem_get_source(sceneItem))),\n\t\t\t\t\t\tundo_redo, undo_redo, undo_data, redo_data);\n\t};\n\tauto setDuration = [visible](int duration) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\n\t\tOBSSceneItem item = main->GetCurrentSceneItem();\n\t\tobs_sceneitem_set_transition_duration(item, visible, duration);\n\t};\n\tconnect(duration, &QSpinBox::valueChanged, duration, setDuration);\n\n\taction = menu->addAction(QT_UTF8(Str(\"None\")));\n\taction->setProperty(\"transition_id\", QT_UTF8(\"\"));\n\taction->setCheckable(true);\n\taction->setChecked(!curId);\n\tconnect(action, &QAction::triggered, this, std::bind(setTransition, action, visible));\n\tsize_t idx = 0;\n\tconst char *id;\n\twhile (obs_enum_transition_types(idx++, &id)) {\n\t\tconst char *name = obs_source_get_display_name(id);\n\t\tconst bool match = id && curId && strcmp(id, curId) == 0;\n\t\taction = menu->addAction(QT_UTF8(name));\n\t\taction->setProperty(\"transition_id\", QT_UTF8(id));\n\t\taction->setCheckable(true);\n\t\taction->setChecked(match);\n\t\tconnect(action, &QAction::triggered, this, std::bind(setTransition, action, visible));\n\t}\n\n\tQWidgetAction *durationAction = new QWidgetAction(menu);\n\tdurationAction->setDefaultWidget(duration);\n\n\tmenu->addSeparator();\n\tmenu->addAction(durationAction);\n\tif (curId && obs_is_source_configurable(curId)) {\n\t\tmenu->addSeparator();\n\t\tmenu->addAction(QTStr(\"Properties\"), this,\n\t\t\t\tvisible ? &OBSBasic::ShowTransitionProperties : &OBSBasic::HideTransitionProperties);\n\t}\n\n\tauto copyTransition = [this](QAction *, bool visible) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tOBSSceneItem item = main->GetCurrentSceneItem();\n\t\tobs_source_t *tr = obs_sceneitem_get_transition(item, visible);\n\t\tint trDur = obs_sceneitem_get_transition_duration(item, visible);\n\t\tmain->copySourceTransition = obs_source_get_weak_source(tr);\n\t\tmain->copySourceTransitionDuration = trDur;\n\t};\n\tmenu->addSeparator();\n\taction = menu->addAction(QT_UTF8(Str(\"Copy\")));\n\taction->setEnabled(curId != nullptr);\n\tconnect(action, &QAction::triggered, this, std::bind(copyTransition, action, visible));\n\n\tauto pasteTransition = [this](QAction *, bool show) {\n\t\tOBSBasic *main = OBSBasic::Get();\n\t\tOBSSource tr = OBSGetStrongRef(main->copySourceTransition);\n\t\tint trDuration = main->copySourceTransitionDuration;\n\t\tif (!tr)\n\t\t\treturn;\n\n\t\tfor (auto &selectedSource : GetAllSelectedSourceItems()) {\n\t\t\tOBSSceneItem item = main->ui->sources->Get(selectedSource.row());\n\t\t\tif (!item)\n\t\t\t\tcontinue;\n\n\t\t\tPasteShowHideTransition(item, show, tr, trDuration);\n\t\t}\n\t};\n\n\taction = menu->addAction(QT_UTF8(Str(\"Paste\")));\n\taction->setEnabled(!!OBSGetStrongRef(copySourceTransition));\n\tconnect(action, &QAction::triggered, this, std::bind(pasteTransition, action, visible));\n\treturn menu;\n}\n\nQMenu *OBSBasic::CreateTransitionMenu(QWidget *parent, QuickTransition *qt)\n{\n\tQMenu *menu = new QMenu(parent);\n\tQAction *action;\n\n\tif (qt) {\n\t\taction = menu->addAction(QTStr(\"Remove\"));\n\t\taction->setProperty(\"id\", qt->id);\n\t\tconnect(action, &QAction::triggered, this, &OBSBasic::QuickTransitionRemoveClicked);\n\n\t\tmenu->addSeparator();\n\t}\n\n\tQSpinBox *duration = new QSpinBox(menu);\n\tif (qt)\n\t\tduration->setProperty(\"id\", qt->id);\n\tduration->setMinimum(50);\n\tduration->setSuffix(\" ms\");\n\tduration->setMaximum(20000);\n\tduration->setSingleStep(50);\n\tduration->setValue(qt ? qt->duration : 300);\n\n\tif (qt) {\n\t\tconnect(duration, (void(QSpinBox::*)(int)) & QSpinBox::valueChanged, this,\n\t\t\t&OBSBasic::QuickTransitionChangeDuration);\n\t}\n\n\taction = menu->addAction(QTStr(\"FadeToBlack\"));\n\taction->setProperty(\"fadeToBlack\", true);\n\n\tif (qt) {\n\t\taction->setProperty(\"id\", qt->id);\n\t\tconnect(action, &QAction::triggered, this, &OBSBasic::QuickTransitionChange);\n\t} else {\n\t\taction->setProperty(\"duration\", QVariant::fromValue<QWidget *>(duration));\n\t\tconnect(action, &QAction::triggered, this, &OBSBasic::AddQuickTransition);\n\t}\n\n\tfor (const auto &[uuid, transition] : transitions) {\n\t\tif (!transition)\n\t\t\tcontinue;\n\n\t\taction = menu->addAction(obs_source_get_name(transition));\n\t\taction->setProperty(\"transition_uuid\", QString::fromStdString(uuid));\n\n\t\tif (qt) {\n\t\t\taction->setProperty(\"id\", qt->id);\n\t\t\tconnect(action, &QAction::triggered, this, &OBSBasic::QuickTransitionChange);\n\t\t} else {\n\t\t\taction->setProperty(\"duration\", QVariant::fromValue<QWidget *>(duration));\n\t\t\tconnect(action, &QAction::triggered, this, &OBSBasic::AddQuickTransition);\n\t\t}\n\t}\n\n\tQWidgetAction *durationAction = new QWidgetAction(menu);\n\tdurationAction->setDefaultWidget(duration);\n\n\tmenu->addSeparator();\n\tmenu->addAction(durationAction);\n\treturn menu;\n}\n\nvoid OBSBasic::AddQuickTransitionId(int id)\n{\n\tQuickTransition *qt = GetQuickTransition(id);\n\tif (!qt)\n\t\treturn;\n\n\t/* --------------------------------- */\n\n\tQPushButton *button = new MenuButton();\n\tbutton->setProperty(\"id\", id);\n\n\tqt->button = button;\n\tResetQuickTransitionText(qt);\n\n\t/* --------------------------------- */\n\n\tQMenu *buttonMenu = CreateTransitionMenu(button, qt);\n\n\t/* --------------------------------- */\n\n\tbutton->setMenu(buttonMenu);\n\tconnect(button, &QAbstractButton::clicked, this, &OBSBasic::QuickTransitionClicked);\n\n\tQVBoxLayout *programLayout = reinterpret_cast<QVBoxLayout *>(programOptions->layout());\n\n\tint idx = 3;\n\tfor (;; idx++) {\n\t\tQLayoutItem *item = programLayout->itemAt(idx);\n\t\tif (!item)\n\t\t\tbreak;\n\n\t\tQWidget *widget = item->widget();\n\t\tif (!widget || !widget->property(\"id\").isValid())\n\t\t\tbreak;\n\t}\n\n\tprogramLayout->insertWidget(idx, button);\n}\n\nvoid OBSBasic::AddQuickTransition()\n{\n\tstd::string transitionUuid = sender()->property(\"transition_uuid\").toString().toStdString();\n\tQSpinBox *duration = sender()->property(\"duration\").value<QSpinBox *>();\n\tbool fadeToBlack = sender()->property(\"fadeToBlack\").value<bool>();\n\tauto transitionIter = transitions.find(transitionUuid);\n\tOBSSource transition;\n\n\tif (!fadeToBlack && (transitionIter == transitions.end()))\n\t\treturn;\n\n\ttransition = fadeToBlack ? OBSSource(fadeTransition) : transitionIter->second;\n\n\tif (!transition)\n\t\treturn;\n\n\tint id = quickTransitionIdCounter++;\n\n\tquickTransitions.emplace_back(transition, duration->value(), id, fadeToBlack);\n\tAddQuickTransitionId(id);\n\n\tint idx = (int)quickTransitions.size() - 1;\n\tAddQuickTransitionHotkey(&quickTransitions[idx]);\n}\n\nvoid OBSBasic::ClearQuickTransitions()\n{\n\tfor (QuickTransition &qt : quickTransitions)\n\t\tRemoveQuickTransitionHotkey(&qt);\n\tquickTransitions.clear();\n\n\tif (!programOptions)\n\t\treturn;\n\n\tQVBoxLayout *programLayout = reinterpret_cast<QVBoxLayout *>(programOptions->layout());\n\n\tfor (int idx = 0;; idx++) {\n\t\tQLayoutItem *item = programLayout->itemAt(idx);\n\t\tif (!item)\n\t\t\tbreak;\n\n\t\tQWidget *widget = item->widget();\n\t\tif (!widget)\n\t\t\tcontinue;\n\n\t\tint id = widget->property(\"id\").toInt();\n\t\tif (id != 0) {\n\t\t\tdelete widget;\n\t\t\tidx--;\n\t\t}\n\t}\n}\n\nvoid OBSBasic::QuickTransitionClicked()\n{\n\tint id = sender()->property(\"id\").toInt();\n\tTriggerQuickTransition(id);\n}\n\nvoid OBSBasic::QuickTransitionChange()\n{\n\tint id = sender()->property(\"id\").toInt();\n\tstd::string transitionUuid = sender()->property(\"transition_uuid\").toString().toStdString();\n\tbool fadeToBlack = sender()->property(\"fadeToBlack\").value<bool>();\n\tQuickTransition *qt = GetQuickTransition(id);\n\n\tif (qt) {\n\t\tauto transitionIter = transitions.find(transitionUuid);\n\t\tOBSSource tr;\n\n\t\tif (!fadeToBlack && (transitionIter == transitions.end()))\n\t\t\treturn;\n\n\t\ttr = fadeToBlack ? OBSSource(fadeTransition) : transitionIter->second;\n\n\t\tif (tr) {\n\t\t\tqt->source = tr;\n\t\t\tqt->fadeToBlack = fadeToBlack;\n\t\t\tResetQuickTransitionText(qt);\n\t\t}\n\t}\n}\n\nvoid OBSBasic::QuickTransitionChangeDuration(int value)\n{\n\tint id = sender()->property(\"id\").toInt();\n\tQuickTransition *qt = GetQuickTransition(id);\n\n\tif (qt) {\n\t\tqt->duration = value;\n\t\tResetQuickTransitionText(qt);\n\t}\n}\n\nvoid OBSBasic::QuickTransitionRemoveClicked()\n{\n\tint id = sender()->property(\"id\").toInt();\n\tint idx = GetQuickTransitionIdx(id);\n\tif (idx == -1)\n\t\treturn;\n\n\tQuickTransition &qt = quickTransitions[idx];\n\n\tif (qt.button)\n\t\tqt.button->deleteLater();\n\n\tRemoveQuickTransitionHotkey(&qt);\n\tquickTransitions.erase(quickTransitions.begin() + idx);\n}\n\nvoid OBSBasic::ClearQuickTransitionWidgets()\n{\n\tif (!IsPreviewProgramMode())\n\t\treturn;\n\n\tQVBoxLayout *programLayout = reinterpret_cast<QVBoxLayout *>(programOptions->layout());\n\n\tfor (int idx = 0;; idx++) {\n\t\tQLayoutItem *item = programLayout->itemAt(idx);\n\t\tif (!item)\n\t\t\tbreak;\n\n\t\tQWidget *widget = item->widget();\n\t\tif (!widget)\n\t\t\tcontinue;\n\n\t\tint id = widget->property(\"id\").toInt();\n\t\tif (id != 0) {\n\t\t\tdelete widget;\n\t\t\tidx--;\n\t\t}\n\t}\n}\n\nvoid OBSBasic::RefreshQuickTransitions()\n{\n\tif (!IsPreviewProgramMode())\n\t\treturn;\n\n\tfor (QuickTransition &qt : quickTransitions)\n\t\tAddQuickTransitionId(qt.id);\n}\n\nvoid OBSBasic::EnableTransitionWidgets(bool enable)\n{\n\tui->transitions->setEnabled(enable);\n\n\tif (!enable) {\n\t\tui->transitionProps->setEnabled(false);\n\t} else {\n\t\tbool configurable = obs_source_configurable(GetCurrentTransition());\n\t\tui->transitionProps->setEnabled(configurable);\n\t}\n\n\tif (!IsPreviewProgramMode())\n\t\treturn;\n\n\tQVBoxLayout *programLayout = reinterpret_cast<QVBoxLayout *>(programOptions->layout());\n\n\tfor (int idx = 0;; idx++) {\n\t\tQLayoutItem *item = programLayout->itemAt(idx);\n\t\tif (!item)\n\t\t\tbreak;\n\n\t\tQPushButton *button = qobject_cast<QPushButton *>(item->widget());\n\t\tif (!button)\n\t\t\tcontinue;\n\n\t\tbutton->setEnabled(enable);\n\t}\n\n\tif (transitionButton)\n\t\ttransitionButton->setEnabled(enable);\n}\n\nobs_data_array_t *OBSBasic::SaveTransitions()\n{\n\tobs_data_array_t *transitionsData = obs_data_array_create();\n\n\tfor (const auto &[uuid, transition] : transitions) {\n\t\tif (!transition || !obs_source_configurable(transition.Get()))\n\t\t\tcontinue;\n\n\t\tOBSDataAutoRelease sourceData = obs_data_create();\n\t\tOBSDataAutoRelease settings = obs_source_get_settings(transition.Get());\n\n\t\tobs_data_set_string(sourceData, \"name\", obs_source_get_name(transition.Get()));\n\t\tobs_data_set_string(sourceData, \"id\", obs_obj_get_id(transition.Get()));\n\t\tobs_data_set_obj(sourceData, \"settings\", settings);\n\n\t\tobs_data_array_push_back(transitionsData, sourceData);\n\t}\n\n\tfor (const OBSDataAutoRelease &transition : safeModeTransitions) {\n\t\tobs_data_array_push_back(transitionsData, transition);\n\t}\n\n\treturn transitionsData;\n}\n\nvoid OBSBasic::LoadTransitions(obs_data_array_t *transitionsData, obs_load_source_cb cb, void *private_data)\n{\n\tsize_t count = obs_data_array_count(transitionsData);\n\n\tsafeModeTransitions.clear();\n\tfor (size_t i = 0; i < count; i++) {\n\t\tOBSDataAutoRelease item = obs_data_array_item(transitionsData, i);\n\t\tconst char *name = obs_data_get_string(item, \"name\");\n\t\tconst char *id = obs_data_get_string(item, \"id\");\n\t\tOBSDataAutoRelease settings = obs_data_get_obj(item, \"settings\");\n\n\t\tOBSSourceAutoRelease source = obs_source_create_private(id, name, settings);\n\t\tif (!obs_obj_invalid(source)) {\n\t\t\tstd::string uuid = obs_source_get_uuid(source);\n\t\t\tInitTransition(source);\n\n\t\t\ttransitions.insert({uuid, OBSSource(source)});\n\t\t\ttransitionNameToUuids.insert({name, uuid});\n\t\t\ttransitionUuids.push_back(uuid);\n\n\t\t\temit TransitionAdded(QT_UTF8(name), QString::fromStdString(uuid));\n\n\t\t\tif (cb)\n\t\t\t\tcb(private_data, source);\n\t\t} else if (safe_mode || disable_3p_plugins) {\n\t\t\tsafeModeTransitions.push_back(std::move(item));\n\t\t}\n\t}\n\n\tUpdateCurrentTransition(transitionUuids.back(), true);\n}\n\nOBSSource OBSBasic::GetOverrideTransition(OBSSource source)\n{\n\tif (!source)\n\t\treturn nullptr;\n\n\tOBSDataAutoRelease data = obs_source_get_private_settings(source);\n\n\tconst char *trOverrideName = obs_data_get_string(data, \"transition\");\n\n\tOBSSource trOverride = nullptr;\n\n\tif (trOverrideName && *trOverrideName)\n\t\ttrOverride = FindTransition(trOverrideName);\n\n\treturn trOverride;\n}\n\nint OBSBasic::GetOverrideTransitionDuration(OBSSource source)\n{\n\tif (!source)\n\t\treturn 300;\n\n\tOBSDataAutoRelease data = obs_source_get_private_settings(source);\n\tobs_data_set_default_int(data, \"transition_duration\", 300);\n\n\treturn (int)obs_data_get_int(data, \"transition_duration\");\n}\n\nint OBSBasic::GetTransitionDuration()\n{\n\treturn transitionDuration;\n}\n\nvoid OBSBasic::UpdateCurrentTransition(const std::string &uuid, bool setTransition)\n{\n\tauto transitionIter = transitions.find(uuid);\n\n\tif (currentTransitionUuid == uuid || transitionIter == transitions.end())\n\t\treturn;\n\n\tcurrentTransitionUuid = uuid;\n\n\tif (setTransition)\n\t\tSetTransition(transitionIter->second);\n\n\temit CurrentTransitionChanged(QString::fromStdString(uuid));\n}\n\nvoid OBSBasic::SetCurrentTransition(const QString &uuid)\n{\n\tauto transitionIter = transitions.find(uuid.toStdString());\n\n\tif (currentTransitionUuid == uuid.toStdString() || transitionIter == transitions.end())\n\t\treturn;\n\n\tcurrentTransitionUuid = uuid.toStdString();\n\tSetTransition(transitionIter->second);\n\n\temit CurrentTransitionChanged(uuid);\n}\n\nvoid OBSBasic::SetTransitionDuration(int duration)\n{\n\tduration = std::max(duration, 50);\n\tduration = std::min(duration, 20000);\n\n\tif (duration == transitionDuration)\n\t\treturn;\n\n\ttransitionDuration = duration;\n\n\temit TransitionDurationChanged(transitionDuration);\n\n\tOnEvent(OBS_FRONTEND_EVENT_TRANSITION_DURATION_CHANGED);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_Updater.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <dialogs/OBSWhatsNew.hpp>\n\n#ifdef _WIN32\n#include <utility/AutoUpdateThread.hpp>\n#endif\n#ifdef ENABLE_SPARKLE_UPDATER\n#include <utility/MacUpdateThread.hpp>\n#include <utility/OBSSparkle.hpp>\n#endif\n#if defined(_WIN32) || defined(WHATSNEW_ENABLED)\n#include <utility/WhatsNewBrowserInitThread.hpp>\n#include <utility/models/whatsnew.hpp>\n#endif\n\n#ifdef BROWSER_AVAILABLE\n#include <browser-panel.hpp>\n#endif\n#include <qt-wrappers.hpp>\n\n#ifdef _WIN32\n#define UPDATE_CHECK_INTERVAL (60 * 60 * 24 * 4) /* 4 days */\n#endif\n\nstruct QCef;\nstruct QCefCookieManager;\n\nextern QCef *cef;\nextern QCefCookieManager *panel_cookies;\n\nusing namespace std;\n\nnamespace {\n\nQPointer<OBSWhatsNew> obsWhatsNew;\n\ntemplate<typename OBSRef> struct SignalContainer {\n\tOBSRef ref;\n\tvector<shared_ptr<OBSSignal>> handlers;\n};\n} // namespace\n\nvoid OBSBasic::ReceivedIntroJson(const QString &text)\n{\n#ifdef WHATSNEW_ENABLED\n\tif (isClosing()) {\n\t\treturn;\n\t}\n\n\tWhatsNewList items;\n\ttry {\n\t\tnlohmann::json json = nlohmann::json::parse(text.toStdString());\n\t\titems = json.get<WhatsNewList>();\n\t} catch (nlohmann::json::exception &e) {\n\t\tblog(LOG_WARNING, \"Parsing whatsnew data failed: %s\", e.what());\n\t\treturn;\n\t}\n\n\tstd::string info_url;\n\tint info_increment = -1;\n\n\t/* check to see if there's an info page for this version */\n\tfor (const WhatsNewItem &item : items) {\n\t\tif (item.os) {\n\t\t\tWhatsNewPlatforms platforms = *item.os;\n#ifdef _WIN32\n\t\t\tif (!platforms.windows)\n\t\t\t\tcontinue;\n#elif defined(__APPLE__)\n\t\t\tif (!platforms.macos)\n\t\t\t\tcontinue;\n#else\n\t\t\tif (!platforms.linux)\n\t\t\t\tcontinue;\n#endif\n\t\t}\n\n\t\tint major = 0;\n\t\tint minor = 0;\n\n\t\tsscanf(item.version.c_str(), \"%d.%d\", &major, &minor);\n\t\tif (major == LIBOBS_API_MAJOR_VER && minor == LIBOBS_API_MINOR_VER &&\n\t\t    item.RC == OBS_RELEASE_CANDIDATE && item.Beta == OBS_BETA) {\n\t\t\tinfo_url = item.url;\n\t\t\tinfo_increment = item.increment;\n\t\t}\n\t}\n\n\t/* this version was not found, or no info for this version */\n\tif (info_increment == -1) {\n\t\treturn;\n\t}\n\n#if OBS_RELEASE_CANDIDATE > 0\n\tconstexpr const char *lastInfoVersion = \"InfoLastRCVersion\";\n#elif OBS_BETA > 0\n\tconstexpr const char *lastInfoVersion = \"InfoLastBetaVersion\";\n#else\n\tconstexpr const char *lastInfoVersion = \"InfoLastVersion\";\n#endif\n\tconstexpr uint64_t currentVersion = (uint64_t)LIBOBS_API_VER << 16ULL | OBS_RELEASE_CANDIDATE << 8ULL |\n\t\t\t\t\t    OBS_BETA;\n\tuint64_t lastVersion = config_get_uint(App()->GetAppConfig(), \"General\", lastInfoVersion);\n\tint current_version_increment = -1;\n\n\tif ((lastVersion & ~0xFFFF0000ULL) < (currentVersion & ~0xFFFF0000ULL)) {\n\t\tconfig_set_int(App()->GetAppConfig(), \"General\", \"InfoIncrement\", -1);\n\t\tconfig_set_uint(App()->GetAppConfig(), \"General\", lastInfoVersion, currentVersion);\n\t} else {\n\t\tcurrent_version_increment = config_get_int(App()->GetAppConfig(), \"General\", \"InfoIncrement\");\n\t}\n\n\tif (info_increment <= current_version_increment) {\n\t\treturn;\n\t}\n\n\tconfig_set_int(App()->GetAppConfig(), \"General\", \"InfoIncrement\", info_increment);\n\tconfig_save_safe(App()->GetAppConfig(), \"tmp\", nullptr);\n\n\tcef->init_browser();\n\n\tWhatsNewBrowserInitThread *wnbit = new WhatsNewBrowserInitThread(QT_UTF8(info_url.c_str()));\n\n\tconnect(wnbit, &WhatsNewBrowserInitThread::Result, this, &OBSBasic::ShowWhatsNew, Qt::QueuedConnection);\n\n\twhatsNewInitThread.reset(wnbit);\n\twhatsNewInitThread->start();\n\n#else\n\tUNUSED_PARAMETER(text);\n#endif\n}\n\nvoid OBSBasic::ShowWhatsNew(const QString &url)\n{\n#ifdef BROWSER_AVAILABLE\n\tif (isClosing()) {\n\t\treturn;\n\t}\n\n\tif (obsWhatsNew) {\n\t\tobsWhatsNew->close();\n\t}\n\n\tobsWhatsNew = new OBSWhatsNew(this, QT_TO_UTF8(url));\n#else\n\tUNUSED_PARAMETER(url);\n#endif\n}\n\nvoid OBSBasic::TimedCheckForUpdates()\n{\n\tif (App()->IsUpdaterDisabled())\n\t\treturn;\n\tif (!config_get_bool(App()->GetAppConfig(), \"General\", \"EnableAutoUpdates\"))\n\t\treturn;\n\n#if defined(ENABLE_SPARKLE_UPDATER)\n\tCheckForUpdates(false);\n#elif _WIN32\n\tlong long lastUpdate = config_get_int(App()->GetAppConfig(), \"General\", \"LastUpdateCheck\");\n\tuint32_t lastVersion = config_get_int(App()->GetAppConfig(), \"General\", \"LastVersion\");\n\n\tif (lastVersion < LIBOBS_API_VER) {\n\t\tlastUpdate = 0;\n\t\tconfig_set_int(App()->GetAppConfig(), \"General\", \"LastUpdateCheck\", 0);\n\t}\n\n\tlong long t = (long long)time(nullptr);\n\tlong long secs = t - lastUpdate;\n\n\tif (secs > UPDATE_CHECK_INTERVAL)\n\t\tCheckForUpdates(false);\n#endif\n}\n\nvoid OBSBasic::CheckForUpdates(bool manualUpdate)\n{\n#if _WIN32\n\tui->actionCheckForUpdates->setEnabled(false);\n\tui->actionRepair->setEnabled(false);\n\n\tif (updateCheckThread && updateCheckThread->isRunning())\n\t\treturn;\n\tupdateCheckThread.reset(new AutoUpdateThread(manualUpdate));\n\tupdateCheckThread->start();\n#elif defined(ENABLE_SPARKLE_UPDATER)\n\tui->actionCheckForUpdates->setEnabled(false);\n\n\tif (updateCheckThread && updateCheckThread->isRunning())\n\t\treturn;\n\n\tMacUpdateThread *mut = new MacUpdateThread(manualUpdate);\n\tconnect(mut, &MacUpdateThread::Result, this, &OBSBasic::MacBranchesFetched, Qt::QueuedConnection);\n\tupdateCheckThread.reset(mut);\n\tupdateCheckThread->start();\n#else\n\tUNUSED_PARAMETER(manualUpdate);\n#endif\n}\n\nvoid OBSBasic::MacBranchesFetched(const QString &branch, bool manualUpdate)\n{\n#ifdef ENABLE_SPARKLE_UPDATER\n\tstatic OBSSparkle *updater;\n\n\tif (!updater) {\n\t\tupdater = new OBSSparkle(QT_TO_UTF8(branch), ui->actionCheckForUpdates);\n\t\treturn;\n\t}\n\n\tupdater->setBranch(QT_TO_UTF8(branch));\n\tupdater->checkForUpdates(manualUpdate);\n#else\n\tUNUSED_PARAMETER(branch);\n\tUNUSED_PARAMETER(manualUpdate);\n#endif\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_VirtualCam.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#include <components/UIValidation.hpp>\n#include <dialogs/OBSBasicVCamConfig.hpp>\n\n#define VIRTUAL_CAM_START \"==== Virtual Camera Start ==========================================\"\n#define VIRTUAL_CAM_STOP \"==== Virtual Camera Stop ===========================================\"\n\nvoid OBSBasic::StartVirtualCam()\n{\n\tif (!outputHandler || !outputHandler->virtualCam)\n\t\treturn;\n\tif (outputHandler->VirtualCamActive())\n\t\treturn;\n\tif (disableOutputsRef)\n\t\treturn;\n\n\tSaveProject();\n\n\toutputHandler->StartVirtualCam();\n}\n\nvoid OBSBasic::StopVirtualCam()\n{\n\tif (!outputHandler || !outputHandler->virtualCam)\n\t\treturn;\n\n\tSaveProject();\n\n\tif (outputHandler->VirtualCamActive())\n\t\toutputHandler->StopVirtualCam();\n\n\tOnDeactivate();\n}\n\nvoid OBSBasic::OnVirtualCamStart()\n{\n\tif (!outputHandler || !outputHandler->virtualCam)\n\t\treturn;\n\n\temit VirtualCamStarted();\n\n\tif (sysTrayVirtualCam)\n\t\tsysTrayVirtualCam->setText(QTStr(\"Basic.Main.StopVirtualCam\"));\n\n\tOnEvent(OBS_FRONTEND_EVENT_VIRTUALCAM_STARTED);\n\n\tOnActivate();\n\n\tblog(LOG_INFO, VIRTUAL_CAM_START);\n}\n\nvoid OBSBasic::OnVirtualCamStop(int)\n{\n\tif (!outputHandler || !outputHandler->virtualCam)\n\t\treturn;\n\n\temit VirtualCamStopped();\n\n\tif (sysTrayVirtualCam)\n\t\tsysTrayVirtualCam->setText(QTStr(\"Basic.Main.StartVirtualCam\"));\n\n\tOnEvent(OBS_FRONTEND_EVENT_VIRTUALCAM_STOPPED);\n\n\tblog(LOG_INFO, VIRTUAL_CAM_STOP);\n\n\tOnDeactivate();\n\n\tif (!restartingVCam)\n\t\treturn;\n\n\t/* Restarting needs to be delayed to make sure that the virtual camera\n\t * implementation is stopped and avoid race condition. */\n\tQTimer::singleShot(100, this, &OBSBasic::RestartingVirtualCam);\n}\n\nvoid OBSBasic::VirtualCamActionTriggered()\n{\n\tif (outputHandler->VirtualCamActive()) {\n\t\tStopVirtualCam();\n\t} else {\n\t\tif (!UIValidation::NoSourcesConfirmation(this))\n\t\t\treturn;\n\n\t\tStartVirtualCam();\n\t}\n}\n\nvoid OBSBasic::OpenVirtualCamConfig()\n{\n\tOBSBasicVCamConfig dialog(vcamConfig, outputHandler->VirtualCamActive(), this);\n\n\tconnect(&dialog, &OBSBasicVCamConfig::Accepted, this, &OBSBasic::UpdateVirtualCamConfig);\n\tconnect(&dialog, &OBSBasicVCamConfig::AcceptedAndRestart, this, &OBSBasic::RestartVirtualCam);\n\n\tdialog.exec();\n}\n\nvoid log_vcam_changed(const VCamConfig &config, bool starting)\n{\n\tconst char *action = starting ? \"Starting\" : \"Changing\";\n\n\tswitch (config.type) {\n\tcase VCamOutputType::Invalid:\n\t\tbreak;\n\tcase VCamOutputType::ProgramView:\n\t\tblog(LOG_INFO, \"%s Virtual Camera output to Program\", action);\n\t\tbreak;\n\tcase VCamOutputType::PreviewOutput:\n\t\tblog(LOG_INFO, \"%s Virtual Camera output to Preview\", action);\n\t\tbreak;\n\tcase VCamOutputType::SceneOutput:\n\t\tblog(LOG_INFO, \"%s Virtual Camera output to Scene : %s\", action, config.scene.c_str());\n\t\tbreak;\n\tcase VCamOutputType::SourceOutput:\n\t\tblog(LOG_INFO, \"%s Virtual Camera output to Source : %s\", action, config.source.c_str());\n\t\tbreak;\n\t}\n}\n\nvoid OBSBasic::UpdateVirtualCamConfig(const VCamConfig &config)\n{\n\tvcamConfig = config;\n\n\toutputHandler->UpdateVirtualCamOutputSource();\n\tlog_vcam_changed(config, false);\n}\n\nvoid OBSBasic::RestartVirtualCam(const VCamConfig &config)\n{\n\trestartingVCam = true;\n\n\tStopVirtualCam();\n\n\tvcamConfig = config;\n}\n\nvoid OBSBasic::RestartingVirtualCam()\n{\n\tif (!restartingVCam)\n\t\treturn;\n\n\toutputHandler->UpdateVirtualCamOutputSource();\n\tStartVirtualCam();\n\trestartingVCam = false;\n}\n\nbool OBSBasic::VirtualCamActive()\n{\n\tif (!outputHandler)\n\t\treturn false;\n\treturn outputHandler->VirtualCamActive();\n}\n"
  },
  {
    "path": "frontend/widgets/OBSBasic_YouTube.cpp",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Zachary Lund <admin@computerquip.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"OBSBasic.hpp\"\n\n#ifdef YOUTUBE_ENABLED\n#include <dialogs/OBSYoutubeActions.hpp>\n#include <docks/YouTubeAppDock.hpp>\n#include <utility/YoutubeApiWrappers.hpp>\n#endif\n\n#include <qt-wrappers.hpp>\n\nusing namespace std;\n\nextern bool cef_js_avail;\n\n#ifdef YOUTUBE_ENABLED\nvoid OBSBasic::YouTubeActionDialogOk(const QString &broadcast_id, const QString &stream_id, const QString &key,\n\t\t\t\t     bool autostart, bool autostop, bool start_now)\n{\n\t//blog(LOG_DEBUG, \"Stream key: %s\", QT_TO_UTF8(key));\n\tobs_service_t *service_obj = GetService();\n\tOBSDataAutoRelease settings = obs_service_get_settings(service_obj);\n\n\tconst std::string a_key = QT_TO_UTF8(key);\n\tobs_data_set_string(settings, \"key\", a_key.c_str());\n\n\tconst std::string b_id = QT_TO_UTF8(broadcast_id);\n\tobs_data_set_string(settings, \"broadcast_id\", b_id.c_str());\n\n\tconst std::string s_id = QT_TO_UTF8(stream_id);\n\tobs_data_set_string(settings, \"stream_id\", s_id.c_str());\n\n\tobs_service_update(service_obj, settings);\n\tautoStartBroadcast = autostart;\n\tautoStopBroadcast = autostop;\n\tbroadcastReady = true;\n\n\temit BroadcastStreamReady(broadcastReady);\n\n\tif (start_now)\n\t\tQMetaObject::invokeMethod(this, \"StartStreaming\");\n}\n\nvoid OBSBasic::YoutubeStreamCheck(const std::string &key)\n{\n\tYoutubeApiWrappers *apiYouTube(dynamic_cast<YoutubeApiWrappers *>(GetAuth()));\n\tif (!apiYouTube) {\n\t\t/* technically we should never get here -Lain */\n\t\tQMetaObject::invokeMethod(this, \"ForceStopStreaming\", Qt::QueuedConnection);\n\t\tyoutubeStreamCheckThread->deleteLater();\n\t\tblog(LOG_ERROR, \"==========================================\");\n\t\tblog(LOG_ERROR, \"%s: Uh, hey, we got here\", __FUNCTION__);\n\t\tblog(LOG_ERROR, \"==========================================\");\n\t\treturn;\n\t}\n\n\tint timeout = 0;\n\tjson11::Json json;\n\tQString id = key.c_str();\n\n\twhile (StreamingActive()) {\n\t\tif (timeout == 14) {\n\t\t\tQMetaObject::invokeMethod(this, \"ForceStopStreaming\", Qt::QueuedConnection);\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!apiYouTube->FindStream(id, json)) {\n\t\t\tQMetaObject::invokeMethod(this, \"DisplayStreamStartError\", Qt::QueuedConnection);\n\t\t\tQMetaObject::invokeMethod(this, \"StopStreaming\", Qt::QueuedConnection);\n\t\t\tbreak;\n\t\t}\n\n\t\tauto item = json[\"items\"][0];\n\t\tauto status = item[\"status\"][\"streamStatus\"].string_value();\n\t\tif (status == \"active\") {\n\t\t\temit BroadcastStreamActive();\n\t\t\tbreak;\n\t\t} else {\n\t\t\tQThread::sleep(1);\n\t\t\ttimeout++;\n\t\t}\n\t}\n\n\tyoutubeStreamCheckThread->deleteLater();\n}\n\nvoid OBSBasic::ShowYouTubeAutoStartWarning()\n{\n\tauto msgBox = []() {\n\t\tQMessageBox msgbox(App()->GetMainWindow());\n\t\tmsgbox.setWindowTitle(QTStr(\"YouTube.Actions.AutoStartStreamingWarning.Title\"));\n\t\tmsgbox.setText(QTStr(\"YouTube.Actions.AutoStartStreamingWarning\"));\n\t\tmsgbox.setIcon(QMessageBox::Icon::Information);\n\t\tmsgbox.addButton(QMessageBox::Ok);\n\n\t\tQCheckBox *cb = new QCheckBox(QTStr(\"DoNotShowAgain\"));\n\t\tmsgbox.setCheckBox(cb);\n\n\t\tmsgbox.exec();\n\n\t\tif (cb->isChecked()) {\n\t\t\tconfig_set_bool(App()->GetUserConfig(), \"General\", \"WarnedAboutYouTubeAutoStart\", true);\n\t\t\tconfig_save_safe(App()->GetUserConfig(), \"tmp\", nullptr);\n\t\t}\n\t};\n\n\tbool warned = config_get_bool(App()->GetUserConfig(), \"General\", \"WarnedAboutYouTubeAutoStart\");\n\tif (!warned) {\n\t\tQMetaObject::invokeMethod(App(), \"Exec\", Qt::QueuedConnection, Q_ARG(VoidFunc, msgBox));\n\t}\n}\n#endif\n\nvoid OBSBasic::BroadcastButtonClicked()\n{\n\tif (!broadcastReady || (!broadcastActive && !outputHandler->StreamingActive())) {\n\t\tSetupBroadcast();\n\t\treturn;\n\t}\n\n\tif (!autoStartBroadcast) {\n#ifdef YOUTUBE_ENABLED\n\t\tstd::shared_ptr<YoutubeApiWrappers> ytAuth = dynamic_pointer_cast<YoutubeApiWrappers>(auth);\n\t\tif (ytAuth.get()) {\n\t\t\tif (!ytAuth->StartLatestBroadcast()) {\n\t\t\t\tauto last_error = ytAuth->GetLastError();\n\t\t\t\tif (last_error.isEmpty())\n\t\t\t\t\tlast_error = QTStr(\"YouTube.Actions.Error.YouTubeApi\");\n\t\t\t\tif (!ytAuth->GetTranslatedError(last_error))\n\t\t\t\t\tlast_error = QTStr(\"YouTube.Actions.Error.BroadcastTransitionFailed\")\n\t\t\t\t\t\t\t     .arg(last_error, ytAuth->GetBroadcastId());\n\n\t\t\t\tOBSMessageBox::warning(this, QTStr(\"Output.BroadcastStartFailed\"), last_error, true);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n#endif\n\t\tbroadcastActive = true;\n\t\tautoStartBroadcast = true; // and clear the flag\n\n\t\temit BroadcastStreamStarted(autoStopBroadcast);\n\t} else if (!autoStopBroadcast) {\n#ifdef YOUTUBE_ENABLED\n\t\tbool confirm = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"WarnBeforeStoppingStream\");\n\t\tif (confirm && isVisible()) {\n\t\t\tQMessageBox::StandardButton button = OBSMessageBox::question(\n\t\t\t\tthis, QTStr(\"ConfirmStop.Title\"), QTStr(\"YouTube.Actions.AutoStopStreamingWarning\"),\n\t\t\t\tQMessageBox::Yes | QMessageBox::No, QMessageBox::No);\n\n\t\t\tif (button == QMessageBox::No)\n\t\t\t\treturn;\n\t\t}\n\n\t\tstd::shared_ptr<YoutubeApiWrappers> ytAuth = dynamic_pointer_cast<YoutubeApiWrappers>(auth);\n\t\tif (ytAuth.get()) {\n\t\t\tif (!ytAuth->StopLatestBroadcast()) {\n\t\t\t\tauto last_error = ytAuth->GetLastError();\n\t\t\t\tif (last_error.isEmpty())\n\t\t\t\t\tlast_error = QTStr(\"YouTube.Actions.Error.YouTubeApi\");\n\t\t\t\tif (!ytAuth->GetTranslatedError(last_error))\n\t\t\t\t\tlast_error = QTStr(\"YouTube.Actions.Error.BroadcastTransitionFailed\")\n\t\t\t\t\t\t\t     .arg(last_error, ytAuth->GetBroadcastId());\n\n\t\t\t\tOBSMessageBox::warning(this, QTStr(\"Output.BroadcastStopFailed\"), last_error, true);\n\t\t\t}\n\t\t}\n#endif\n\t\tbroadcastActive = false;\n\t\tbroadcastReady = false;\n\n\t\tautoStopBroadcast = true;\n\t\tQMetaObject::invokeMethod(this, \"StopStreaming\");\n\t\temit BroadcastStreamReady(broadcastReady);\n\t\tSetBroadcastFlowEnabled(true);\n\t}\n}\n\nvoid OBSBasic::SetBroadcastFlowEnabled(bool enabled)\n{\n\temit BroadcastFlowEnabled(enabled);\n}\n\nvoid OBSBasic::SetupBroadcast()\n{\n#ifdef YOUTUBE_ENABLED\n\tAuth *const auth = GetAuth();\n\tif (IsYouTubeService(auth->service())) {\n\t\tOBSYoutubeActions dialog(this, auth, broadcastReady);\n\t\tconnect(&dialog, &OBSYoutubeActions::ok, this, &OBSBasic::YouTubeActionDialogOk);\n\t\tdialog.exec();\n\t}\n#endif\n}\n\n#ifdef YOUTUBE_ENABLED\nYouTubeAppDock *OBSBasic::GetYouTubeAppDock()\n{\n\treturn youtubeAppDock;\n}\n\n#ifndef SEC_TO_NSEC\n#define SEC_TO_NSEC 1000000000\n#endif\n\nvoid OBSBasic::NewYouTubeAppDock()\n{\n\tif (!cef_js_avail)\n\t\treturn;\n\n\t/* make sure that the youtube app dock can't be immediately recreated.\n\t * dumb hack. blame chromium. or this particular dock. or both. if CEF\n\t * creates/destroys/creates a widget too quickly it can lead to a\n\t * crash. */\n\tuint64_t ts = os_gettime_ns();\n\tif ((ts - lastYouTubeAppDockCreationTime) < (5ULL * SEC_TO_NSEC))\n\t\treturn;\n\n\tlastYouTubeAppDockCreationTime = ts;\n\n\tif (youtubeAppDock)\n\t\tRemoveDockWidget(youtubeAppDock->objectName());\n\n\tyoutubeAppDock = new YouTubeAppDock(\"YouTube Live Control Panel\");\n}\n\nvoid OBSBasic::DeleteYouTubeAppDock()\n{\n\tif (!cef_js_avail)\n\t\treturn;\n\n\tif (youtubeAppDock)\n\t\tRemoveDockWidget(youtubeAppDock->objectName());\n\n\tyoutubeAppDock = nullptr;\n}\n#endif\n"
  },
  {
    "path": "frontend/widgets/OBSMainWindow.hpp",
    "content": "#pragma once\n\n#include <util/config-file.h>\n\n#include <QMainWindow>\n\nclass OBSMainWindow : public QMainWindow {\n\tQ_OBJECT\n\npublic:\n\tinline OBSMainWindow(QWidget *parent) : QMainWindow(parent) {}\n\n\tvirtual config_t *Config() const = 0;\n\tvirtual void OBSInit() = 0;\n};\n"
  },
  {
    "path": "frontend/widgets/OBSProjector.cpp",
    "content": "#include \"OBSProjector.hpp\"\n\n#include <OBSApp.hpp>\n#include <components/Multiview.hpp>\n#include <utility/display-helpers.hpp>\n#include <utility/platform.hpp>\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include <QScreen>\n#include <QWindow>\n\n#include \"moc_OBSProjector.cpp\"\n\nstatic QList<OBSProjector *> multiviewProjectors;\n\nstatic bool updatingMultiview = false, mouseSwitching, transitionOnDoubleClick;\n\nOBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor, ProjectorType type_)\n\t: OBSQTDisplay(widget, Qt::Window),\n\t  weakSource(OBSGetWeakRef(source_))\n{\n\tOBSSource source = GetSource();\n\tif (source) {\n\t\tsigs.emplace_back(obs_source_get_signal_handler(source), \"rename\", OBSSourceRenamed, this);\n\t\tsigs.emplace_back(obs_source_get_signal_handler(source), \"destroy\", OBSSourceDestroyed, this);\n\t}\n\n\tisAlwaysOnTop = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"ProjectorAlwaysOnTop\");\n\n\tif (isAlwaysOnTop)\n\t\tsetWindowFlags(Qt::WindowStaysOnTopHint);\n\n\t// Mark the window as a projector so SetDisplayAffinity\n\t// can skip it\n\twindowHandle()->setProperty(\"isOBSProjectorWindow\", true);\n\n#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)\n\t// Prevents resizing of projector windows\n\tsetAttribute(Qt::WA_PaintOnScreen, false);\n#endif\n\n\ttype = type_;\n#ifndef __APPLE__\n\tsetWindowIcon(QIcon::fromTheme(\"obs\", QIcon(\":/res/images/obs.png\")));\n#endif\n\n\tif (monitor == -1)\n\t\tresize(480, 270);\n\telse\n\t\tSetMonitor(monitor);\n\n\tif (source)\n\t\tUpdateProjectorTitle(QT_UTF8(obs_source_get_name(source)));\n\telse\n\t\tUpdateProjectorTitle(QString());\n\n\tQAction *action = new QAction(this);\n\taction->setShortcut(Qt::Key_Escape);\n\taddAction(action);\n\tconnect(action, &QAction::triggered, this, &OBSProjector::EscapeTriggered);\n\n\tsetAttribute(Qt::WA_DeleteOnClose, true);\n\n\t//disable application quit when last window closed\n\tsetAttribute(Qt::WA_QuitOnClose, false);\n\n\tinstallEventFilter(CreateShortcutFilter());\n\n\tauto addDrawCallback = [this]() {\n\t\tbool isMultiview = type == ProjectorType::Multiview;\n\t\tobs_display_add_draw_callback(GetDisplay(), isMultiview ? OBSRenderMultiview : OBSRender, this);\n\t\tobs_display_set_background_color(GetDisplay(), 0x000000);\n\t};\n\n\tconnect(this, &OBSQTDisplay::DisplayCreated, this, addDrawCallback);\n\tconnect(App(), &QGuiApplication::screenRemoved, this, &OBSProjector::ScreenRemoved);\n\n\tif (type == ProjectorType::Multiview) {\n\t\tmultiview = new Multiview();\n\n\t\tUpdateMultiview();\n\n\t\tmultiviewProjectors.push_back(this);\n\t}\n\n\tApp()->IncrementSleepInhibition();\n\n\tif (source)\n\t\tobs_source_inc_showing(source);\n\n\tready = true;\n\n\tshow();\n\n\t// We need it here to allow keyboard input in X11 to listen to Escape\n\tactivateWindow();\n}\n\nOBSProjector::~OBSProjector()\n{\n\tsigs.clear();\n\n\tbool isMultiview = type == ProjectorType::Multiview;\n\tobs_display_remove_draw_callback(GetDisplay(), isMultiview ? OBSRenderMultiview : OBSRender, this);\n\n\tOBSSource source = GetSource();\n\tif (source)\n\t\tobs_source_dec_showing(source);\n\n\tif (isMultiview) {\n\t\tdelete multiview;\n\t\tmultiviewProjectors.removeAll(this);\n\t}\n\n\tApp()->DecrementSleepInhibition();\n}\n\nvoid OBSProjector::SetMonitor(int monitor)\n{\n\tsavedMonitor = monitor;\n\tsetGeometry(QGuiApplication::screens()[monitor]->geometry());\n\tshowFullScreen();\n\tSetHideCursor();\n}\n\nvoid OBSProjector::SetHideCursor()\n{\n\tif (savedMonitor == -1)\n\t\treturn;\n\n\tbool hideCursor = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"HideProjectorCursor\");\n\n\tif (hideCursor && type != ProjectorType::Multiview)\n\t\tsetCursor(Qt::BlankCursor);\n\telse\n\t\tsetCursor(Qt::ArrowCursor);\n}\n\nvoid OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy)\n{\n\tOBSProjector *window = (OBSProjector *)data;\n\n\tif (updatingMultiview || !window->ready)\n\t\treturn;\n\n\twindow->multiview->Render(cx, cy);\n}\n\nvoid OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)\n{\n\tOBSProjector *window = static_cast<OBSProjector *>(data);\n\n\tif (!window->ready)\n\t\treturn;\n\n\tOBSBasic *main = OBSBasic::Get();\n\tOBSSource source = window->GetSource();\n\n\tuint32_t targetCX;\n\tuint32_t targetCY;\n\tint x, y;\n\tint newCX, newCY;\n\tfloat scale;\n\n\tif (source) {\n\t\ttargetCX = std::max(obs_source_get_width(source), 1u);\n\t\ttargetCY = std::max(obs_source_get_height(source), 1u);\n\t} else {\n\t\tstruct obs_video_info ovi;\n\t\tobs_get_video_info(&ovi);\n\t\ttargetCX = ovi.base_width;\n\t\ttargetCY = ovi.base_height;\n\t}\n\n\tGetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale);\n\n\tnewCX = int(scale * float(targetCX));\n\tnewCY = int(scale * float(targetCY));\n\n\tstartRegion(x, y, newCX, newCY, 0.0f, float(targetCX), 0.0f, float(targetCY));\n\n\tif (window->type == ProjectorType::Preview && main->IsPreviewProgramMode()) {\n\t\tOBSSource curSource = main->GetCurrentSceneSource();\n\n\t\tif (source != curSource) {\n\t\t\tobs_source_dec_showing(source);\n\t\t\tobs_source_inc_showing(curSource);\n\t\t\tsource = curSource;\n\t\t\twindow->weakSource = OBSGetWeakRef(source);\n\t\t}\n\t} else if (window->type == ProjectorType::Preview && !main->IsPreviewProgramMode()) {\n\t\twindow->weakSource = nullptr;\n\t}\n\n\tif (source)\n\t\tobs_source_video_render(source);\n\telse\n\t\tobs_render_main_texture();\n\n\tendRegion();\n}\n\nvoid OBSProjector::OBSSourceRenamed(void *data, calldata_t *params)\n{\n\tOBSProjector *window = static_cast<OBSProjector *>(data);\n\tQString oldName = calldata_string(params, \"prev_name\");\n\tQString newName = calldata_string(params, \"new_name\");\n\n\tQMetaObject::invokeMethod(window, \"RenameProjector\", Q_ARG(QString, oldName), Q_ARG(QString, newName));\n}\n\nvoid OBSProjector::OBSSourceDestroyed(void *data, calldata_t *)\n{\n\tOBSProjector *window = static_cast<OBSProjector *>(data);\n\tQMetaObject::invokeMethod(window, \"EscapeTriggered\");\n}\n\nvoid OBSProjector::mouseDoubleClickEvent(QMouseEvent *event)\n{\n\tOBSQTDisplay::mouseDoubleClickEvent(event);\n\n\tif (!mouseSwitching)\n\t\treturn;\n\n\tif (!transitionOnDoubleClick)\n\t\treturn;\n\n\t// Only MultiView projectors handle double click\n\tif (this->type != ProjectorType::Multiview)\n\t\treturn;\n\n\tOBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();\n\tif (!main->IsPreviewProgramMode())\n\t\treturn;\n\n\tif (event->button() == Qt::LeftButton) {\n\t\tQPoint pos = event->pos();\n\t\tOBSSource src = multiview->GetSourceByPosition(pos.x(), pos.y());\n\t\tif (!src)\n\t\t\treturn;\n\n\t\tif (main->GetProgramSource() != src)\n\t\t\tmain->TransitionToScene(src);\n\t}\n}\n\nvoid OBSProjector::mousePressEvent(QMouseEvent *event)\n{\n\tOBSQTDisplay::mousePressEvent(event);\n\n\tif (event->button() == Qt::RightButton) {\n\t\tQMenu *projectorMenu = new QMenu(QTStr(\"Fullscreen\"));\n\t\tOBSBasic::AddProjectorMenuMonitors(projectorMenu, this, &OBSProjector::OpenFullScreenProjector);\n\n\t\tQMenu popup(this);\n\t\tpopup.addMenu(projectorMenu);\n\n\t\tif (GetMonitor() > -1) {\n\t\t\tpopup.addAction(QTStr(\"Windowed\"), this, &OBSProjector::OpenWindowedProjector);\n\n\t\t} else if (!this->isMaximized()) {\n\t\t\tpopup.addAction(QTStr(\"Projector.ResizeWindowToContent\"), this, &OBSProjector::ResizeToContent);\n\t\t}\n\n\t\tQAction *alwaysOnTopButton = new QAction(QTStr(\"Basic.MainMenu.View.AlwaysOnTop\"), this);\n\t\talwaysOnTopButton->setCheckable(true);\n\t\talwaysOnTopButton->setChecked(isAlwaysOnTop);\n\n\t\tconnect(alwaysOnTopButton, &QAction::toggled, this, &OBSProjector::AlwaysOnTopToggled);\n\n\t\tpopup.addAction(alwaysOnTopButton);\n\n\t\tpopup.addAction(QTStr(\"Close\"), this, &OBSProjector::EscapeTriggered);\n\t\tpopup.exec(QCursor::pos());\n\t} else if (event->button() == Qt::LeftButton) {\n\t\t// Only MultiView projectors handle left click\n\t\tif (this->type != ProjectorType::Multiview)\n\t\t\treturn;\n\n\t\tif (!mouseSwitching)\n\t\t\treturn;\n\n\t\tQPoint pos = event->pos();\n\t\tOBSSource src = multiview->GetSourceByPosition(pos.x(), pos.y());\n\t\tif (!src)\n\t\t\treturn;\n\n\t\tOBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();\n\t\tif (main->GetCurrentSceneSource() != src)\n\t\t\tmain->SetCurrentScene(src, false);\n\t}\n}\n\nvoid OBSProjector::EscapeTriggered()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tmain->DeleteProjector(this);\n}\n\nvoid OBSProjector::UpdateMultiview()\n{\n\tMultiviewLayout multiviewLayout =\n\t\tstatic_cast<MultiviewLayout>(config_get_int(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewLayout\"));\n\n\tbool drawLabel = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewDrawNames\");\n\n\tbool drawSafeArea = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewDrawAreas\");\n\n\tmouseSwitching = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"MultiviewMouseSwitch\");\n\n\ttransitionOnDoubleClick = config_get_bool(App()->GetUserConfig(), \"BasicWindow\", \"TransitionOnDoubleClick\");\n\n\tmultiview->Update(multiviewLayout, drawLabel, drawSafeArea);\n}\n\nvoid OBSProjector::UpdateProjectorTitle(QString name)\n{\n\tQString title = nullptr;\n\tswitch (type) {\n\tcase ProjectorType::Scene:\n\t\ttitle = QTStr(\"Projector.Title\") + \" - \" + QTStr(\"Projector.Title.Scene\").arg(name);\n\t\tbreak;\n\tcase ProjectorType::Source:\n\t\ttitle = QTStr(\"Projector.Title\") + \" - \" + QTStr(\"Projector.Title.Source\").arg(name);\n\t\tbreak;\n\tcase ProjectorType::Preview:\n\t\ttitle = QTStr(\"Projector.Title\") + \" - \" + QTStr(\"StudioMode.Preview\");\n\t\tbreak;\n\tcase ProjectorType::StudioProgram:\n\t\ttitle = QTStr(\"Projector.Title\") + \" - \" + QTStr(\"StudioMode.Program\");\n\t\tbreak;\n\tcase ProjectorType::Multiview:\n\t\ttitle = QTStr(\"Projector.Title\") + \" - \" + QTStr(\"Projector.Title.Multiview\");\n\t\tbreak;\n\tdefault:\n\t\ttitle = name;\n\t\tbreak;\n\t}\n\n\tsetWindowTitle(title);\n}\n\nOBSSource OBSProjector::GetSource()\n{\n\treturn OBSGetStrongRef(weakSource);\n}\n\nProjectorType OBSProjector::GetProjectorType()\n{\n\treturn type;\n}\n\nint OBSProjector::GetMonitor()\n{\n\treturn savedMonitor;\n}\n\nvoid OBSProjector::UpdateMultiviewProjectors()\n{\n\tobs_enter_graphics();\n\tupdatingMultiview = true;\n\tobs_leave_graphics();\n\n\tfor (auto &projector : multiviewProjectors)\n\t\tprojector->UpdateMultiview();\n\n\tobs_enter_graphics();\n\tupdatingMultiview = false;\n\tobs_leave_graphics();\n}\n\nvoid OBSProjector::RenameProjector(QString oldName, QString newName)\n{\n\tif (oldName == newName)\n\t\treturn;\n\n\tUpdateProjectorTitle(newName);\n}\n\nvoid OBSProjector::OpenFullScreenProjector()\n{\n\tif (!isFullScreen())\n\t\tprevGeometry = geometry();\n\n\tint monitor = sender()->property(\"monitor\").toInt();\n\tSetMonitor(monitor);\n\n\tOBSSource source = GetSource();\n\tUpdateProjectorTitle(QT_UTF8(obs_source_get_name(source)));\n}\n\nvoid OBSProjector::OpenWindowedProjector()\n{\n\tshowFullScreen();\n\tshowNormal();\n\tsetCursor(Qt::ArrowCursor);\n\n\tif (!prevGeometry.isNull())\n\t\tsetGeometry(prevGeometry);\n\telse\n\t\tresize(480, 270);\n\n\tsavedMonitor = -1;\n\n\tOBSSource source = GetSource();\n\tUpdateProjectorTitle(QT_UTF8(obs_source_get_name(source)));\n}\n\nvoid OBSProjector::ResizeToContent()\n{\n\tOBSSource source = GetSource();\n\tuint32_t targetCX;\n\tuint32_t targetCY;\n\tint x, y, newX, newY;\n\tfloat scale;\n\n\tif (source) {\n\t\ttargetCX = std::max(obs_source_get_width(source), 1u);\n\t\ttargetCY = std::max(obs_source_get_height(source), 1u);\n\t} else {\n\t\tstruct obs_video_info ovi;\n\t\tobs_get_video_info(&ovi);\n\t\ttargetCX = ovi.base_width;\n\t\ttargetCY = ovi.base_height;\n\t}\n\n\tQSize size = this->size();\n\tGetScaleAndCenterPos(targetCX, targetCY, size.width(), size.height(), x, y, scale);\n\n\tnewX = size.width() - (x * 2);\n\tnewY = size.height() - (y * 2);\n\tresize(newX, newY);\n}\n\nvoid OBSProjector::AlwaysOnTopToggled(bool isAlwaysOnTop)\n{\n\tSetIsAlwaysOnTop(isAlwaysOnTop, true);\n}\n\nvoid OBSProjector::closeEvent(QCloseEvent *event)\n{\n\tEscapeTriggered();\n\tevent->accept();\n}\n\nbool OBSProjector::IsAlwaysOnTop() const\n{\n\treturn isAlwaysOnTop;\n}\n\nbool OBSProjector::IsAlwaysOnTopOverridden() const\n{\n\treturn isAlwaysOnTopOverridden;\n}\n\nvoid OBSProjector::SetIsAlwaysOnTop(bool isAlwaysOnTop, bool isOverridden)\n{\n\tthis->isAlwaysOnTop = isAlwaysOnTop;\n\tthis->isAlwaysOnTopOverridden = isOverridden;\n\n\tSetAlwaysOnTop(this, isAlwaysOnTop);\n}\n\nvoid OBSProjector::ScreenRemoved(QScreen *screen)\n{\n\tif (GetMonitor() < 0)\n\t\treturn;\n\n\tif (screen == this->screen())\n\t\tEscapeTriggered();\n}\n"
  },
  {
    "path": "frontend/widgets/OBSProjector.hpp",
    "content": "#pragma once\n\n#include \"OBSQTDisplay.hpp\"\n\nclass Multiview;\n\nenum class ProjectorType {\n\tSource,\n\tScene,\n\tPreview,\n\tStudioProgram,\n\tMultiview,\n};\n\nclass OBSProjector : public OBSQTDisplay {\n\tQ_OBJECT\n\nprivate:\n\tOBSWeakSourceAutoRelease weakSource;\n\tstd::vector<OBSSignal> sigs;\n\n\tstatic void OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy);\n\tstatic void OBSRender(void *data, uint32_t cx, uint32_t cy);\n\tstatic void OBSSourceRenamed(void *data, calldata_t *params);\n\tstatic void OBSSourceDestroyed(void *data, calldata_t *params);\n\n\tvoid mousePressEvent(QMouseEvent *event) override;\n\tvoid mouseDoubleClickEvent(QMouseEvent *event) override;\n\tvoid closeEvent(QCloseEvent *event) override;\n\n\tbool isAlwaysOnTop;\n\tbool isAlwaysOnTopOverridden = false;\n\tint savedMonitor = -1;\n\tProjectorType type = ProjectorType::Source;\n\n\tMultiview *multiview = nullptr;\n\n\tbool ready = false;\n\n\tvoid UpdateMultiview();\n\tvoid UpdateProjectorTitle(QString name);\n\n\tQRect prevGeometry;\n\tvoid SetMonitor(int monitor);\n\nprivate slots:\n\tvoid EscapeTriggered();\n\tvoid OpenFullScreenProjector();\n\tvoid ResizeToContent();\n\tvoid OpenWindowedProjector();\n\tvoid AlwaysOnTopToggled(bool alwaysOnTop);\n\tvoid ScreenRemoved(QScreen *screen);\n\tvoid RenameProjector(QString oldName, QString newName);\n\npublic:\n\tOBSProjector(QWidget *widget, obs_source_t *source_, int monitor, ProjectorType type_);\n\t~OBSProjector();\n\n\tOBSSource GetSource();\n\tProjectorType GetProjectorType();\n\tint GetMonitor();\n\tstatic void UpdateMultiviewProjectors();\n\tvoid SetHideCursor();\n\n\tbool IsAlwaysOnTop() const;\n\tbool IsAlwaysOnTopOverridden() const;\n\tvoid SetIsAlwaysOnTop(bool isAlwaysOnTop, bool isOverridden);\n};\n"
  },
  {
    "path": "frontend/widgets/OBSQTDisplay.cpp",
    "content": "#include \"OBSQTDisplay.hpp\"\n\n#include <utility/display-helpers.hpp>\n#include <utility/SurfaceEventFilter.hpp>\n\n#if !defined(_WIN32) && !defined(__APPLE__)\n#include <obs-nix-platform.h>\n#endif\n\n#include <QWindow>\n#ifdef ENABLE_WAYLAND\n#include <QApplication>\n#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)\n#include <qpa/qplatformnativeinterface.h>\n#endif\n#endif\n\n#ifdef _WIN32\n#define WIN32_LEAN_AND_MEAN\n#include <Windows.h>\n#endif\n\n#include \"moc_OBSQTDisplay.cpp\"\n\nstatic inline long long color_to_int(const QColor &color)\n{\n\tauto shift = [&](unsigned val, int shift) {\n\t\treturn ((val & 0xff) << shift);\n\t};\n\n\treturn shift(color.red(), 0) | shift(color.green(), 8) | shift(color.blue(), 16) | shift(color.alpha(), 24);\n}\n\nstatic inline QColor rgba_to_color(uint32_t rgba)\n{\n\treturn QColor::fromRgb(rgba & 0xFF, (rgba >> 8) & 0xFF, (rgba >> 16) & 0xFF, (rgba >> 24) & 0xFF);\n}\n\nstatic bool QTToGSWindow(QWindow *window, gs_window &gswindow)\n{\n\tbool success = true;\n\n#ifdef _WIN32\n\tgswindow.hwnd = (HWND)window->winId();\n#elif __APPLE__\n\tgswindow.view = (id)window->winId();\n#else\n\tswitch (obs_get_nix_platform()) {\n\tcase OBS_NIX_PLATFORM_X11_EGL:\n\t\tgswindow.id = window->winId();\n\t\tgswindow.display = obs_get_nix_platform_display();\n\t\tbreak;\n#ifdef ENABLE_WAYLAND\n\tcase OBS_NIX_PLATFORM_WAYLAND: {\n#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)\n\t\tQPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();\n\t\tgswindow.display = native->nativeResourceForWindow(\"surface\", window);\n#else\n\t\tgswindow.display = (void *)window->winId();\n#endif\n\t\tsuccess = gswindow.display != nullptr;\n\t\tbreak;\n\t}\n#endif\n\tdefault:\n\t\tsuccess = false;\n\t\tbreak;\n\t}\n#endif\n\treturn success;\n}\n\nOBSQTDisplay::OBSQTDisplay(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags)\n{\n\tsetAttribute(Qt::WA_PaintOnScreen);\n\tsetAttribute(Qt::WA_StaticContents);\n\tsetAttribute(Qt::WA_NoSystemBackground);\n\tsetAttribute(Qt::WA_OpaquePaintEvent);\n\tsetAttribute(Qt::WA_DontCreateNativeAncestors);\n\tsetAttribute(Qt::WA_NativeWindow);\n\n\tauto windowVisible = [this](bool visible) {\n\t\tif (!visible) {\n#if !defined(_WIN32) && !defined(__APPLE__)\n\t\t\tdisplay = nullptr;\n#endif\n\t\t\treturn;\n\t\t}\n\n\t\tif (!display) {\n\t\t\tCreateDisplay();\n\t\t} else {\n\t\t\tQSize size = GetPixelSize(this);\n\t\t\tobs_display_resize(display, size.width(), size.height());\n\t\t}\n\t};\n\n\tauto screenChanged = [this](QScreen *) {\n\t\tCreateDisplay();\n\n\t\tQSize size = GetPixelSize(this);\n\t\tobs_display_resize(display, size.width(), size.height());\n\t};\n\n\tconnect(windowHandle(), &QWindow::visibleChanged, this, windowVisible);\n\tconnect(windowHandle(), &QWindow::screenChanged, this, screenChanged);\n\n\twindowHandle()->installEventFilter(new SurfaceEventFilter(this));\n}\n\nQColor OBSQTDisplay::GetDisplayBackgroundColor() const\n{\n\treturn rgba_to_color(backgroundColor);\n}\n\nvoid OBSQTDisplay::SetDisplayBackgroundColor(const QColor &color)\n{\n\tuint32_t newBackgroundColor = (uint32_t)color_to_int(color);\n\n\tif (newBackgroundColor != backgroundColor) {\n\t\tbackgroundColor = newBackgroundColor;\n\t\tUpdateDisplayBackgroundColor();\n\t}\n}\n\nvoid OBSQTDisplay::UpdateDisplayBackgroundColor()\n{\n\tobs_display_set_background_color(display, backgroundColor);\n}\n\nvoid OBSQTDisplay::CreateDisplay()\n{\n\tif (display)\n\t\treturn;\n\n\tif (destroying)\n\t\treturn;\n\n\tif (!windowHandle()->isExposed())\n\t\treturn;\n\n\tQSize size = GetPixelSize(this);\n\n\tgs_init_data info = {};\n\tinfo.cx = size.width();\n\tinfo.cy = size.height();\n\tinfo.format = GS_BGRA;\n\tinfo.zsformat = GS_ZS_NONE;\n\n\tif (!QTToGSWindow(windowHandle(), info.window))\n\t\treturn;\n\n\tdisplay = obs_display_create(&info, backgroundColor);\n\n\temit DisplayCreated(this);\n}\n\nvoid OBSQTDisplay::paintEvent(QPaintEvent *event)\n{\n\tCreateDisplay();\n\n\tQWidget::paintEvent(event);\n}\n\nvoid OBSQTDisplay::moveEvent(QMoveEvent *event)\n{\n\tQWidget::moveEvent(event);\n\n\tOnMove();\n}\n\nbool OBSQTDisplay::nativeEvent(const QByteArray &, void *message, qintptr *)\n{\n#ifdef _WIN32\n\tconst MSG &msg = *static_cast<MSG *>(message);\n\tswitch (msg.message) {\n\tcase WM_DISPLAYCHANGE:\n\t\tOnDisplayChange();\n\t}\n#else\n\tUNUSED_PARAMETER(message);\n#endif\n\n\treturn false;\n}\n\nvoid OBSQTDisplay::resizeEvent(QResizeEvent *event)\n{\n\tQWidget::resizeEvent(event);\n\n\tCreateDisplay();\n\n\tif (isVisible() && display) {\n\t\tQSize size = GetPixelSize(this);\n\t\tobs_display_resize(display, size.width(), size.height());\n\t}\n\n\temit DisplayResized();\n}\n\nQPaintEngine *OBSQTDisplay::paintEngine() const\n{\n\treturn nullptr;\n}\n\nvoid OBSQTDisplay::OnMove()\n{\n\tif (display)\n\t\tobs_display_update_color_space(display);\n}\n\nvoid OBSQTDisplay::OnDisplayChange()\n{\n\tif (display)\n\t\tobs_display_update_color_space(display);\n}\n"
  },
  {
    "path": "frontend/widgets/OBSQTDisplay.hpp",
    "content": "#pragma once\n\n#include <obs.hpp>\n\n#include <QWidget>\n\n#define GREY_COLOR_BACKGROUND 0xFF4C4C4C\n\nclass OBSQTDisplay : public QWidget {\n\tQ_OBJECT\n\tQ_PROPERTY(QColor displayBackgroundColor MEMBER backgroundColor READ GetDisplayBackgroundColor WRITE\n\t\t\t   SetDisplayBackgroundColor)\n\n\tOBSDisplay display;\n\tbool destroying = false;\n\nprotected:\n\tvirtual void paintEvent(QPaintEvent *event) override;\n\tvirtual void moveEvent(QMoveEvent *event) override;\n\tvirtual void resizeEvent(QResizeEvent *event) override;\n\tvirtual bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result) override;\n\nsignals:\n\tvoid DisplayCreated(OBSQTDisplay *window);\n\tvoid DisplayResized();\n\npublic:\n\tOBSQTDisplay(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());\n\t~OBSQTDisplay() { display = nullptr; }\n\n\tvirtual QPaintEngine *paintEngine() const override;\n\n\tinline obs_display_t *GetDisplay() const { return display; }\n\n\tuint32_t backgroundColor = GREY_COLOR_BACKGROUND;\n\n\tQColor GetDisplayBackgroundColor() const;\n\tvoid SetDisplayBackgroundColor(const QColor &color);\n\tvoid UpdateDisplayBackgroundColor();\n\tvoid CreateDisplay();\n\tvoid DestroyDisplay()\n\t{\n\t\tdisplay = nullptr;\n\t\tdestroying = true;\n\t};\n\n\tvoid OnMove();\n\tvoid OnDisplayChange();\n};\n"
  },
  {
    "path": "frontend/widgets/StatusBarWidget.cpp",
    "content": "#include \"StatusBarWidget.hpp\"\n#include \"ui_StatusBarWidget.h\"\n#include \"moc_StatusBarWidget.cpp\"\n\nStatusBarWidget::StatusBarWidget(QWidget *parent) : QWidget(parent), ui(new Ui::StatusBarWidget)\n{\n\tui->setupUi(this);\n}\n\nStatusBarWidget::~StatusBarWidget() {}\n"
  },
  {
    "path": "frontend/widgets/StatusBarWidget.hpp",
    "content": "#pragma once\n\n#include <QWidget>\n\nclass OBSBasicStatusBar;\nclass Ui_StatusBarWidget;\n\nclass StatusBarWidget : public QWidget {\n\tQ_OBJECT\n\n\tfriend class OBSBasicStatusBar;\n\nprivate:\n\tstd::unique_ptr<Ui_StatusBarWidget> ui;\n\npublic:\n\tStatusBarWidget(QWidget *parent = nullptr);\n\t~StatusBarWidget();\n};\n"
  },
  {
    "path": "frontend/wizards/AutoConfig.cpp",
    "content": "#include \"AutoConfig.hpp\"\n#include \"AutoConfigStartPage.hpp\"\n#include \"AutoConfigStreamPage.hpp\"\n#include \"AutoConfigTestPage.hpp\"\n#include \"AutoConfigVideoPage.hpp\"\n#include \"ui_AutoConfigStartPage.h\"\n#include \"ui_AutoConfigStreamPage.h\"\n#include \"ui_AutoConfigVideoPage.h\"\n\n#ifdef YOUTUBE_ENABLED\n#include <docks/YouTubeAppDock.hpp>\n#include <utility/YoutubeApiWrappers.hpp>\n#endif\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include \"moc_AutoConfig.cpp\"\n\nconstexpr std::string_view OBSServiceFileName = \"service.json\";\n\nenum class ListOpt : int {\n\tShowAll = 1,\n\tCustom,\n};\n\nstatic OBSData OpenServiceSettings(std::string &type)\n{\n\tconst OBSBasic *basic = OBSBasic::Get();\n\tconst OBSProfile &currentProfile = basic->GetCurrentProfile();\n\n\tconst std::filesystem::path jsonFilePath = currentProfile.path / std::filesystem::u8path(OBSServiceFileName);\n\n\tif (!std::filesystem::exists(jsonFilePath)) {\n\t\treturn OBSData();\n\t}\n\n\tOBSDataAutoRelease data = obs_data_create_from_json_file_safe(jsonFilePath.u8string().c_str(), \"bak\");\n\n\tobs_data_set_default_string(data, \"type\", \"rtmp_common\");\n\ttype = obs_data_get_string(data, \"type\");\n\n\tOBSDataAutoRelease settings = obs_data_get_obj(data, \"settings\");\n\n\treturn settings.Get();\n}\n\nstatic void GetServiceInfo(std::string &type, std::string &service, std::string &server, std::string &key)\n{\n\tOBSData settings = OpenServiceSettings(type);\n\n\tservice = obs_data_get_string(settings, \"service\");\n\tserver = obs_data_get_string(settings, \"server\");\n\tkey = obs_data_get_string(settings, \"key\");\n}\n\nAutoConfig::AutoConfig(QWidget *parent) : QWizard(parent)\n{\n\tEnableThreadedMessageBoxes(true);\n\n\tcalldata_t cd = {0};\n\tcalldata_set_int(&cd, \"seconds\", 5);\n\n\tproc_handler_t *ph = obs_get_proc_handler();\n\tproc_handler_call(ph, \"twitch_ingests_refresh\", &cd);\n\tproc_handler_call(ph, \"amazon_ivs_ingests_refresh\", &cd);\n\tcalldata_free(&cd);\n\n\tOBSBasic *main = OBSBasic::Get();\n\tmain->EnableOutputs(false);\n\n\tinstallEventFilter(CreateShortcutFilter());\n\n\tstd::string serviceType;\n\tGetServiceInfo(serviceType, serviceName, server, key);\n#if defined(_WIN32) || defined(__APPLE__)\n\tsetWizardStyle(QWizard::ModernStyle);\n#endif\n\tstreamPage = new AutoConfigStreamPage();\n\n\tsetPage(StartPage, new AutoConfigStartPage());\n\tsetPage(VideoPage, new AutoConfigVideoPage());\n\tsetPage(StreamPage, streamPage);\n\tsetPage(TestPage, new AutoConfigTestPage());\n\tsetWindowTitle(QTStr(\"Basic.AutoConfig\"));\n\tsetWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);\n\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tbaseResolutionCX = ovi.base_width;\n\tbaseResolutionCY = ovi.base_height;\n\n\t/* ----------------------------------------- */\n\t/* check to see if Twitch's \"auto\" available */\n\n\tOBSDataAutoRelease twitchSettings = obs_data_create();\n\n\tobs_data_set_string(twitchSettings, \"service\", \"Twitch\");\n\n\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\tobs_properties_apply_settings(props, twitchSettings);\n\n\tobs_property_t *p = obs_properties_get(props, \"server\");\n\tconst char *first = obs_property_list_item_string(p, 0);\n\ttwitchAuto = strcmp(first, \"auto\") == 0;\n\n\tobs_properties_destroy(props);\n\n\t/* ----------------------------------------- */\n\t/* check to see if Amazon IVS \"auto\" entries are available */\n\n\tOBSDataAutoRelease amazonIVSSettings = obs_data_create();\n\n\tobs_data_set_string(amazonIVSSettings, \"service\", \"Amazon IVS\");\n\n\tprops = obs_get_service_properties(\"rtmp_common\");\n\tobs_properties_apply_settings(props, amazonIVSSettings);\n\n\tp = obs_properties_get(props, \"server\");\n\tfirst = obs_property_list_item_string(p, 0);\n\tamazonIVSAuto = strncmp(first, \"auto\", 4) == 0;\n\n\tobs_properties_destroy(props);\n\n\t/* ----------------------------------------- */\n\t/* load service/servers                      */\n\n\tcustomServer = serviceType == \"rtmp_custom\";\n\n\tQComboBox *serviceList = streamPage->ui->service;\n\n\tif (!serviceName.empty()) {\n\t\tserviceList->blockSignals(true);\n\n\t\tint count = serviceList->count();\n\t\tbool found = false;\n\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tQString name = serviceList->itemText(i);\n\n\t\t\tif (name == serviceName.c_str()) {\n\t\t\t\tserviceList->setCurrentIndex(i);\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!found) {\n\t\t\tserviceList->insertItem(0, serviceName.c_str());\n\t\t\tserviceList->setCurrentIndex(0);\n\t\t}\n\n\t\tserviceList->blockSignals(false);\n\t}\n\n\tstreamPage->UpdateServerList();\n\tstreamPage->UpdateKeyLink();\n\tstreamPage->UpdateMoreInfoLink();\n\tstreamPage->lastService.clear();\n\n\tif (!customServer) {\n\t\tQComboBox *serverList = streamPage->ui->server;\n\t\tint idx = serverList->findData(QString(server.c_str()));\n\t\tif (idx == -1)\n\t\t\tidx = 0;\n\n\t\tserverList->setCurrentIndex(idx);\n\t} else {\n\t\tstreamPage->ui->customServer->setText(server.c_str());\n\t\tint idx = streamPage->ui->service->findData(QVariant((int)ListOpt::Custom));\n\t\tstreamPage->ui->service->setCurrentIndex(idx);\n\t}\n\n\tif (!key.empty())\n\t\tstreamPage->ui->key->setText(key.c_str());\n\n\tTestHardwareEncoding();\n\n\tint bitrate = config_get_int(main->Config(), \"SimpleOutput\", \"VBitrate\");\n\tbool multitrackVideoEnabled = config_has_user_value(main->Config(), \"Stream1\", \"EnableMultitrackVideo\")\n\t\t\t\t\t      ? config_get_bool(main->Config(), \"Stream1\", \"EnableMultitrackVideo\")\n\t\t\t\t\t      : true;\n\tstreamPage->ui->bitrate->setValue(bitrate);\n\tstreamPage->ui->useMultitrackVideo->setChecked(hardwareEncodingAvailable && multitrackVideoEnabled);\n\tstreamPage->ServiceChanged();\n\n\tif (!hardwareEncodingAvailable) {\n\t\tdelete streamPage->ui->preferHardware;\n\t\tstreamPage->ui->preferHardware = nullptr;\n\t} else {\n\t\t/* Newer generations of NVENC have a high enough quality to\n\t\t * bitrate ratio that if NVENC is available, it makes sense to\n\t\t * just always prefer hardware encoding by default */\n\t\tbool preferHardware = nvencAvailable || appleAvailable || os_get_physical_cores() <= 4;\n\t\tstreamPage->ui->preferHardware->setChecked(preferHardware);\n\t}\n\n\tsetOptions(QWizard::WizardOptions());\n\tsetButtonText(QWizard::FinishButton, QTStr(\"Basic.AutoConfig.ApplySettings\"));\n\tsetButtonText(QWizard::BackButton, QTStr(\"Back\"));\n\tsetButtonText(QWizard::NextButton, QTStr(\"Next\"));\n\tsetButtonText(QWizard::CancelButton, QTStr(\"Cancel\"));\n}\n\nAutoConfig::~AutoConfig()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\tmain->EnableOutputs(true);\n\tEnableThreadedMessageBoxes(false);\n}\n\nvoid AutoConfig::TestHardwareEncoding()\n{\n\tsize_t idx = 0;\n\tconst char *id;\n\twhile (obs_enum_encoder_types(idx++, &id)) {\n\t\tif (strcmp(id, \"ffmpeg_nvenc\") == 0)\n\t\t\thardwareEncodingAvailable = nvencAvailable = true;\n\t\telse if (strcmp(id, \"obs_qsv11\") == 0)\n\t\t\thardwareEncodingAvailable = qsvAvailable = true;\n\t\telse if (strcmp(id, \"h264_texture_amf\") == 0)\n\t\t\thardwareEncodingAvailable = vceAvailable = true;\n#ifdef __APPLE__\n\t\telse if (strcmp(id, \"com.apple.videotoolbox.videoencoder.ave.avc\") == 0\n#ifndef __aarch64__\n\t\t\t && os_get_emulation_status() == true\n#endif\n\t\t)\n\t\t\tif (__builtin_available(macOS 13.0, *))\n\t\t\t\thardwareEncodingAvailable = appleAvailable = true;\n#endif\n\t}\n}\n\nbool AutoConfig::CanTestServer(const char *server)\n{\n\tif (!testRegions || (regionUS && regionEU && regionAsia && regionOther))\n\t\treturn true;\n\n\tif (service == Service::Twitch) {\n\t\tif (astrcmp_n(server, \"US West:\", 8) == 0 || astrcmp_n(server, \"US East:\", 8) == 0 ||\n\t\t    astrcmp_n(server, \"US Central:\", 11) == 0) {\n\t\t\treturn regionUS;\n\t\t} else if (astrcmp_n(server, \"EU:\", 3) == 0) {\n\t\t\treturn regionEU;\n\t\t} else if (astrcmp_n(server, \"Asia:\", 5) == 0) {\n\t\t\treturn regionAsia;\n\t\t} else if (regionOther) {\n\t\t\treturn true;\n\t\t}\n\t} else {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid AutoConfig::done(int result)\n{\n\tQWizard::done(result);\n\n\tif (result == QDialog::Accepted) {\n\t\tif (type == Type::Streaming)\n\t\t\tSaveStreamSettings();\n\t\tSaveSettings();\n\n#ifdef YOUTUBE_ENABLED\n\t\tif (YouTubeAppDock::IsYTServiceSelected()) {\n\t\t\tOBSBasic *main = OBSBasic::Get();\n\t\t\tmain->NewYouTubeAppDock();\n\t\t}\n#endif\n\t}\n}\n\ninline const char *AutoConfig::GetEncoderId(Encoder enc)\n{\n\tswitch (enc) {\n\tcase Encoder::NVENC:\n\t\treturn SIMPLE_ENCODER_NVENC;\n\tcase Encoder::QSV:\n\t\treturn SIMPLE_ENCODER_QSV;\n\tcase Encoder::AMD:\n\t\treturn SIMPLE_ENCODER_AMD;\n\tcase Encoder::Apple:\n\t\treturn SIMPLE_ENCODER_APPLE_H264;\n\tdefault:\n\t\treturn SIMPLE_ENCODER_X264;\n\t}\n};\n\nvoid AutoConfig::SaveStreamSettings()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\t/* ---------------------------------- */\n\t/* save service                       */\n\n\tconst char *service_id = customServer ? \"rtmp_custom\" : \"rtmp_common\";\n\n\tobs_service_t *oldService = main->GetService();\n\tOBSDataAutoRelease hotkeyData = obs_hotkeys_save_service(oldService);\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tif (!customServer)\n\t\tobs_data_set_string(settings, \"service\", serviceName.c_str());\n\tobs_data_set_string(settings, \"server\", server.c_str());\n#ifdef YOUTUBE_ENABLED\n\tif (!streamPage->auth || !IsYouTubeService(serviceName))\n\t\tobs_data_set_string(settings, \"key\", key.c_str());\n#else\n\tobs_data_set_string(settings, \"key\", key.c_str());\n#endif\n\n\tOBSServiceAutoRelease newService = obs_service_create(service_id, \"default_service\", settings, hotkeyData);\n\n\tif (!newService)\n\t\treturn;\n\n\tmain->SetService(newService);\n\tmain->SaveService();\n\tmain->auth = streamPage->auth;\n\tif (!!main->auth) {\n\t\tmain->auth->LoadUI();\n\t\tmain->SetBroadcastFlowEnabled(main->auth->broadcastFlow());\n\t} else {\n\t\tmain->SetBroadcastFlowEnabled(false);\n\t}\n\n\t/* ---------------------------------- */\n\t/* save stream settings               */\n\n\tconfig_set_int(main->Config(), \"SimpleOutput\", \"VBitrate\", idealBitrate);\n\tconfig_set_string(main->Config(), \"SimpleOutput\", \"StreamEncoder\", GetEncoderId(streamingEncoder));\n\tconfig_remove_value(main->Config(), \"SimpleOutput\", \"UseAdvanced\");\n\n\tconfig_set_bool(main->Config(), \"Stream1\", \"EnableMultitrackVideo\", multitrackVideo.testSuccessful);\n\n\tif (multitrackVideo.targetBitrate.has_value())\n\t\tconfig_set_int(main->Config(), \"Stream1\", \"MultitrackVideoTargetBitrate\",\n\t\t\t       *multitrackVideo.targetBitrate);\n\telse\n\t\tconfig_remove_value(main->Config(), \"Stream1\", \"MultitrackVideoTargetBitrate\");\n\n\tif (multitrackVideo.bitrate.has_value() && multitrackVideo.targetBitrate.has_value() &&\n\t    (static_cast<double>(*multitrackVideo.bitrate) / *multitrackVideo.targetBitrate) >= 0.90) {\n\t\tconfig_set_bool(main->Config(), \"Stream1\", \"MultitrackVideoMaximumAggregateBitrateAuto\", true);\n\t} else if (multitrackVideo.bitrate.has_value()) {\n\t\tconfig_set_bool(main->Config(), \"Stream1\", \"MultitrackVideoMaximumAggregateBitrateAuto\", false);\n\t\tconfig_set_int(main->Config(), \"Stream1\", \"MultitrackVideoMaximumAggregateBitrate\",\n\t\t\t       *multitrackVideo.bitrate);\n\t}\n}\n\nvoid AutoConfig::SaveSettings()\n{\n\tOBSBasic *main = OBSBasic::Get();\n\n\tif (recordingEncoder != Encoder::Stream)\n\t\tconfig_set_string(main->Config(), \"SimpleOutput\", \"RecEncoder\", GetEncoderId(recordingEncoder));\n\n\tconst char *quality = recordingQuality == Quality::High ? \"Small\" : \"Stream\";\n\n\tconfig_set_string(main->Config(), \"Output\", \"Mode\", \"Simple\");\n\tconfig_set_string(main->Config(), \"SimpleOutput\", \"RecQuality\", quality);\n\tconfig_set_int(main->Config(), \"Video\", \"BaseCX\", baseResolutionCX);\n\tconfig_set_int(main->Config(), \"Video\", \"BaseCY\", baseResolutionCY);\n\tconfig_set_int(main->Config(), \"Video\", \"OutputCX\", idealResolutionCX);\n\tconfig_set_int(main->Config(), \"Video\", \"OutputCY\", idealResolutionCY);\n\n\tif (fpsType != FPSType::UseCurrent) {\n\t\tconfig_set_uint(main->Config(), \"Video\", \"FPSType\", 0);\n\t\tconfig_set_string(main->Config(), \"Video\", \"FPSCommon\", std::to_string(idealFPSNum).c_str());\n\t}\n\n\tmain->ResetVideo();\n\tmain->ResetOutputs();\n\tconfig_save_safe(main->Config(), \"tmp\", nullptr);\n}\n"
  },
  {
    "path": "frontend/wizards/AutoConfig.hpp",
    "content": "#pragma once\n\n#include <QWizard>\n\nclass AutoConfigStreamPage;\n\nclass AutoConfig : public QWizard {\n\tQ_OBJECT\n\n\tfriend class AutoConfigStartPage;\n\tfriend class AutoConfigVideoPage;\n\tfriend class AutoConfigStreamPage;\n\tfriend class AutoConfigTestPage;\n\n\tenum class Type {\n\t\tInvalid,\n\t\tStreaming,\n\t\tRecording,\n\t\tVirtualCam,\n\t};\n\n\tenum class Service {\n\t\tTwitch,\n\t\tYouTube,\n\t\tAmazonIVS,\n\t\tOther,\n\t};\n\n\tenum class Encoder {\n\t\tx264,\n\t\tNVENC,\n\t\tQSV,\n\t\tAMD,\n\t\tApple,\n\t\tStream,\n\t};\n\n\tenum class Quality {\n\t\tStream,\n\t\tHigh,\n\t};\n\n\tenum class FPSType : int {\n\t\tPreferHighFPS,\n\t\tPreferHighRes,\n\t\tUseCurrent,\n\t\tfps30,\n\t\tfps60,\n\t};\n\n\tstruct StreamServer {\n\t\tstd::string name;\n\t\tstd::string address;\n\t};\n\n\tstatic inline const char *GetEncoderId(Encoder enc);\n\n\tAutoConfigStreamPage *streamPage = nullptr;\n\n\tService service = Service::Other;\n\tQuality recordingQuality = Quality::Stream;\n\tEncoder recordingEncoder = Encoder::Stream;\n\tEncoder streamingEncoder = Encoder::x264;\n\tType type = Type::Streaming;\n\tFPSType fpsType = FPSType::PreferHighFPS;\n\tint idealBitrate = 2500;\n\tstruct {\n\t\tstd::optional<int> targetBitrate;\n\t\tstd::optional<int> bitrate;\n\t\tbool testSuccessful = false;\n\t} multitrackVideo;\n\tint baseResolutionCX = 1920;\n\tint baseResolutionCY = 1080;\n\tint idealResolutionCX = 1280;\n\tint idealResolutionCY = 720;\n\tint idealFPSNum = 60;\n\tint idealFPSDen = 1;\n\tstd::string serviceName;\n\tstd::string serverName;\n\tstd::string server;\n\tstd::vector<StreamServer> serviceConfigServers;\n\tstd::string key;\n\n\tbool hardwareEncodingAvailable = false;\n\tbool nvencAvailable = false;\n\tbool qsvAvailable = false;\n\tbool vceAvailable = false;\n\tbool appleAvailable = false;\n\n\tint startingBitrate = 2500;\n\tbool customServer = false;\n\tbool bandwidthTest = false;\n\tbool testMultitrackVideo = false;\n\tbool testRegions = true;\n\tbool twitchAuto = false;\n\tbool amazonIVSAuto = false;\n\tbool regionUS = true;\n\tbool regionEU = true;\n\tbool regionAsia = true;\n\tbool regionOther = true;\n\tbool preferHighFPS = false;\n\tbool preferHardware = false;\n\tint specificFPSNum = 0;\n\tint specificFPSDen = 0;\n\n\tvoid TestHardwareEncoding();\n\tbool CanTestServer(const char *server);\n\n\tvirtual void done(int result) override;\n\n\tvoid SaveStreamSettings();\n\tvoid SaveSettings();\n\npublic:\n\tAutoConfig(QWidget *parent);\n\t~AutoConfig();\n\n\tenum Page {\n\t\tStartPage,\n\t\tVideoPage,\n\t\tStreamPage,\n\t\tTestPage,\n\t};\n};\n"
  },
  {
    "path": "frontend/wizards/AutoConfigStartPage.cpp",
    "content": "#include \"AutoConfigStartPage.hpp\"\n#include \"AutoConfig.hpp\"\n#include \"ui_AutoConfigStartPage.h\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include \"moc_AutoConfigStartPage.cpp\"\n\n#define wiz reinterpret_cast<AutoConfig *>(wizard())\n\nAutoConfigStartPage::AutoConfigStartPage(QWidget *parent) : QWizardPage(parent), ui(new Ui_AutoConfigStartPage)\n{\n\tui->setupUi(this);\n\tsetTitle(QTStr(\"Basic.AutoConfig.StartPage\"));\n\tsetSubTitle(QTStr(\"Basic.AutoConfig.StartPage.SubTitle\"));\n\n\tOBSBasic *main = OBSBasic::Get();\n\tif (main->VCamEnabled()) {\n\t\tQRadioButton *prioritizeVCam =\n\t\t\tnew QRadioButton(QTStr(\"Basic.AutoConfig.StartPage.PrioritizeVirtualCam\"), this);\n\t\tQBoxLayout *box = reinterpret_cast<QBoxLayout *>(layout());\n\t\tbox->insertWidget(2, prioritizeVCam);\n\n\t\tconnect(prioritizeVCam, &QPushButton::clicked, this, &AutoConfigStartPage::PrioritizeVCam);\n\t}\n}\n\nAutoConfigStartPage::~AutoConfigStartPage() {}\n\nint AutoConfigStartPage::nextId() const\n{\n\treturn wiz->type == AutoConfig::Type::VirtualCam ? AutoConfig::TestPage : AutoConfig::VideoPage;\n}\n\nvoid AutoConfigStartPage::on_prioritizeStreaming_clicked()\n{\n\twiz->type = AutoConfig::Type::Streaming;\n}\n\nvoid AutoConfigStartPage::on_prioritizeRecording_clicked()\n{\n\twiz->type = AutoConfig::Type::Recording;\n}\n\nvoid AutoConfigStartPage::PrioritizeVCam()\n{\n\twiz->type = AutoConfig::Type::VirtualCam;\n}\n"
  },
  {
    "path": "frontend/wizards/AutoConfigStartPage.hpp",
    "content": "#pragma once\n\n#include <QWizardPage>\n\nclass Ui_AutoConfigStartPage;\n\nclass AutoConfigStartPage : public QWizardPage {\n\tQ_OBJECT\n\n\tfriend class AutoConfig;\n\n\tstd::unique_ptr<Ui_AutoConfigStartPage> ui;\n\npublic:\n\tAutoConfigStartPage(QWidget *parent = nullptr);\n\t~AutoConfigStartPage();\n\n\tvirtual int nextId() const override;\n\npublic slots:\n\tvoid on_prioritizeStreaming_clicked();\n\tvoid on_prioritizeRecording_clicked();\n\tvoid PrioritizeVCam();\n};\n"
  },
  {
    "path": "frontend/wizards/AutoConfigStreamPage.cpp",
    "content": "#include \"AutoConfigStreamPage.hpp\"\n#include \"AutoConfig.hpp\"\n#include \"ui_AutoConfigStreamPage.h\"\n\n#include <oauth/OAuth.hpp>\n#include <utility/GoLiveAPI_Network.hpp>\n#include <utility/GoLiveAPI_PostData.hpp>\n#include <utility/MultitrackVideoError.hpp>\n#ifdef YOUTUBE_ENABLED\n#include <utility/YoutubeApiWrappers.hpp>\n#endif\n#include <widgets/OBSBasic.hpp>\n\n#include <qt-wrappers.hpp>\n\n#include \"moc_AutoConfigStreamPage.cpp\"\n\nenum class ListOpt : int {\n\tShowAll = 1,\n\tCustom,\n};\n\nstruct QCef;\nextern QCef *cef;\n\n#define wiz reinterpret_cast<AutoConfig *>(wizard())\n\nAutoConfigStreamPage::AutoConfigStreamPage(QWidget *parent) : QWizardPage(parent), ui(new Ui_AutoConfigStreamPage)\n{\n\tui->setupUi(this);\n\tui->bitrateLabel->setVisible(false);\n\tui->bitrate->setVisible(false);\n\tui->connectAccount2->setVisible(false);\n\tui->disconnectAccount->setVisible(false);\n\tui->useMultitrackVideo->setVisible(false);\n\n\tui->connectedAccountLabel->setVisible(false);\n\tui->connectedAccountText->setVisible(false);\n\n\tint vertSpacing = ui->topLayout->verticalSpacing();\n\n\tQMargins m = ui->topLayout->contentsMargins();\n\tm.setBottom(vertSpacing / 2);\n\tui->topLayout->setContentsMargins(m);\n\n\tm = ui->loginPageLayout->contentsMargins();\n\tm.setTop(vertSpacing / 2);\n\tui->loginPageLayout->setContentsMargins(m);\n\n\tm = ui->streamkeyPageLayout->contentsMargins();\n\tm.setTop(vertSpacing / 2);\n\tui->streamkeyPageLayout->setContentsMargins(m);\n\n\tsetTitle(QTStr(\"Basic.AutoConfig.StreamPage\"));\n\tsetSubTitle(QTStr(\"Basic.AutoConfig.StreamPage.SubTitle\"));\n\n\tLoadServices(false);\n\n\tconnect(ui->service, &QComboBox::currentIndexChanged, this, &AutoConfigStreamPage::ServiceChanged);\n\tconnect(ui->customServer, &QLineEdit::textChanged, this, &AutoConfigStreamPage::ServiceChanged);\n\tconnect(ui->customServer, &QLineEdit::textChanged, this, &AutoConfigStreamPage::UpdateKeyLink);\n\tconnect(ui->customServer, &QLineEdit::editingFinished, this, &AutoConfigStreamPage::UpdateKeyLink);\n\tconnect(ui->doBandwidthTest, &QCheckBox::toggled, this, &AutoConfigStreamPage::ServiceChanged);\n\n\tconnect(ui->service, &QComboBox::currentIndexChanged, this, &AutoConfigStreamPage::UpdateServerList);\n\n\tconnect(ui->service, &QComboBox::currentIndexChanged, this, &AutoConfigStreamPage::UpdateKeyLink);\n\tconnect(ui->service, &QComboBox::currentIndexChanged, this, &AutoConfigStreamPage::UpdateMoreInfoLink);\n\n\tconnect(ui->useStreamKeyAdv, &QPushButton::clicked, this, [&]() {\n\t\tui->streamKeyWidget->setVisible(true);\n\t\tui->streamKeyLabel->setVisible(true);\n\t\tui->useStreamKeyAdv->setVisible(false);\n\t});\n\n\tconnect(ui->key, &QLineEdit::textChanged, this, &AutoConfigStreamPage::UpdateCompleted);\n\tconnect(ui->regionUS, &QCheckBox::toggled, this, &AutoConfigStreamPage::UpdateCompleted);\n\tconnect(ui->regionEU, &QCheckBox::toggled, this, &AutoConfigStreamPage::UpdateCompleted);\n\tconnect(ui->regionAsia, &QCheckBox::toggled, this, &AutoConfigStreamPage::UpdateCompleted);\n\tconnect(ui->regionOther, &QCheckBox::toggled, this, &AutoConfigStreamPage::UpdateCompleted);\n}\n\nAutoConfigStreamPage::~AutoConfigStreamPage() {}\n\nbool AutoConfigStreamPage::isComplete() const\n{\n\treturn ready;\n}\n\nint AutoConfigStreamPage::nextId() const\n{\n\treturn AutoConfig::TestPage;\n}\n\ninline bool AutoConfigStreamPage::IsCustomService() const\n{\n\treturn ui->service->currentData().toInt() == (int)ListOpt::Custom;\n}\n\nbool AutoConfigStreamPage::validatePage()\n{\n\tOBSDataAutoRelease service_settings = obs_data_create();\n\n\twiz->customServer = IsCustomService();\n\n\tconst char *serverType = wiz->customServer ? \"rtmp_custom\" : \"rtmp_common\";\n\n\tif (!wiz->customServer) {\n\t\tobs_data_set_string(service_settings, \"service\", QT_TO_UTF8(ui->service->currentText()));\n\t}\n\n\tOBSServiceAutoRelease service = obs_service_create(serverType, \"temp_service\", service_settings, nullptr);\n\n\tint bitrate;\n\tif (!ui->doBandwidthTest->isChecked()) {\n\t\tbitrate = ui->bitrate->value();\n\t\twiz->idealBitrate = bitrate;\n\t} else {\n\t\t/* Default test target is 10 Mbps */\n\t\tbitrate = 10000;\n#ifdef YOUTUBE_ENABLED\n\t\tif (IsYouTubeService(wiz->serviceName)) {\n\t\t\t/* Adjust upper bound to YouTube limits\n\t\t\t * for resolutions above 1080p */\n\t\t\tif (wiz->baseResolutionCY > 1440)\n\t\t\t\tbitrate = 51000;\n\t\t\telse if (wiz->baseResolutionCY > 1080)\n\t\t\t\tbitrate = 18000;\n\t\t}\n#endif\n\t}\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_int(settings, \"bitrate\", bitrate);\n\tobs_service_apply_encoder_settings(service, settings, nullptr);\n\n\tif (wiz->customServer) {\n\t\tQString server = ui->customServer->text().trimmed();\n\t\twiz->server = wiz->serverName = QT_TO_UTF8(server);\n\t} else {\n\t\twiz->serverName = QT_TO_UTF8(ui->server->currentText());\n\t\twiz->server = QT_TO_UTF8(ui->server->currentData().toString());\n\t}\n\n\twiz->bandwidthTest = ui->doBandwidthTest->isChecked();\n\twiz->startingBitrate = (int)obs_data_get_int(settings, \"bitrate\");\n\twiz->idealBitrate = wiz->startingBitrate;\n\twiz->regionUS = ui->regionUS->isChecked();\n\twiz->regionEU = ui->regionEU->isChecked();\n\twiz->regionAsia = ui->regionAsia->isChecked();\n\twiz->regionOther = ui->regionOther->isChecked();\n\twiz->serviceName = QT_TO_UTF8(ui->service->currentText());\n\tif (ui->preferHardware)\n\t\twiz->preferHardware = ui->preferHardware->isChecked();\n\twiz->key = QT_TO_UTF8(ui->key->text());\n\n\tif (!wiz->customServer) {\n\t\tif (wiz->serviceName == \"Twitch\")\n\t\t\twiz->service = AutoConfig::Service::Twitch;\n#ifdef YOUTUBE_ENABLED\n\t\telse if (IsYouTubeService(wiz->serviceName))\n\t\t\twiz->service = AutoConfig::Service::YouTube;\n#endif\n\t\telse if (wiz->serviceName == \"Amazon IVS\")\n\t\t\twiz->service = AutoConfig::Service::AmazonIVS;\n\t\telse\n\t\t\twiz->service = AutoConfig::Service::Other;\n\t} else {\n\t\twiz->service = AutoConfig::Service::Other;\n\t}\n\n\tif (wiz->service == AutoConfig::Service::Twitch) {\n\t\twiz->testMultitrackVideo = ui->useMultitrackVideo->isChecked();\n\n\t\tif (wiz->testMultitrackVideo) {\n\t\t\tstd::vector<OBSCanvasAutoRelease> canvases;\n\t\t\tcanvases.emplace_back(obs_get_main_canvas());\n\t\t\tauto postData = constructGoLivePost(QString::fromStdString(wiz->key), std::nullopt,\n\t\t\t\t\t\t\t    std::nullopt, false, canvases);\n\n\t\t\tOBSDataAutoRelease service_settings = obs_service_get_settings(service);\n\t\t\tauto multitrack_video_name = QTStr(\"Basic.Settings.Stream.MultitrackVideoLabel\");\n\t\t\tif (obs_data_has_user_value(service_settings, \"multitrack_video_name\")) {\n\t\t\t\tmultitrack_video_name = obs_data_get_string(service_settings, \"multitrack_video_name\");\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tauto config = DownloadGoLiveConfig(this, MultitrackVideoAutoConfigURL(service),\n\t\t\t\t\t\t\t\t   postData, multitrack_video_name);\n\n\t\t\t\tfor (const auto &endpoint : config.ingest_endpoints) {\n\t\t\t\t\tif (qstrnicmp(\"RTMP\", endpoint.protocol.c_str(), 4) != 0)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tstd::string address = endpoint.url_template;\n\t\t\t\t\tauto pos = address.find(\"/{stream_key}\");\n\t\t\t\t\tif (pos != address.npos)\n\t\t\t\t\t\taddress.erase(pos);\n\n\t\t\t\t\twiz->serviceConfigServers.push_back({address, address});\n\t\t\t\t}\n\n\t\t\t\tint multitrackVideoBitrate = 0;\n\t\t\t\tfor (auto &encoder_config : config.encoder_configurations) {\n\t\t\t\t\tauto it = encoder_config.settings.find(\"bitrate\");\n\t\t\t\t\tif (it == encoder_config.settings.end())\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif (!it->is_number_integer())\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tint bitrate = 0;\n\t\t\t\t\tit->get_to(bitrate);\n\t\t\t\t\tmultitrackVideoBitrate += bitrate;\n\t\t\t\t}\n\n\t\t\t\tif (multitrackVideoBitrate > 0) {\n\t\t\t\t\twiz->startingBitrate = multitrackVideoBitrate;\n\t\t\t\t\twiz->idealBitrate = multitrackVideoBitrate;\n\t\t\t\t\twiz->multitrackVideo.targetBitrate = multitrackVideoBitrate;\n\t\t\t\t\twiz->multitrackVideo.testSuccessful = true;\n\t\t\t\t}\n\t\t\t} catch (const MultitrackVideoError & /*err*/) {\n\t\t\t\t// FIXME: do something sensible\n\t\t\t}\n\t\t}\n\t}\n\n\tif (wiz->service != AutoConfig::Service::Twitch && wiz->service != AutoConfig::Service::YouTube &&\n\t    wiz->service != AutoConfig::Service::AmazonIVS && wiz->bandwidthTest) {\n\t\tQMessageBox::StandardButton button;\n#define WARNING_TEXT(x) QTStr(\"Basic.AutoConfig.StreamPage.StreamWarning.\" x)\n\t\tbutton = OBSMessageBox::question(this, WARNING_TEXT(\"Title\"), WARNING_TEXT(\"Text\"));\n#undef WARNING_TEXT\n\n\t\tif (button == QMessageBox::No)\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid AutoConfigStreamPage::on_show_clicked()\n{\n\tif (ui->key->echoMode() == QLineEdit::Password) {\n\t\tui->key->setEchoMode(QLineEdit::Normal);\n\t\tui->show->setText(QTStr(\"Hide\"));\n\t} else {\n\t\tui->key->setEchoMode(QLineEdit::Password);\n\t\tui->show->setText(QTStr(\"Show\"));\n\t}\n}\n\nvoid AutoConfigStreamPage::OnOAuthStreamKeyConnected()\n{\n\tOAuthStreamKey *a = reinterpret_cast<OAuthStreamKey *>(auth.get());\n\n\tif (a) {\n\t\tbool validKey = !a->key().empty();\n\n\t\tif (validKey)\n\t\t\tui->key->setText(QT_UTF8(a->key().c_str()));\n\n\t\tui->streamKeyWidget->setVisible(false);\n\t\tui->streamKeyLabel->setVisible(false);\n\t\tui->connectAccount2->setVisible(false);\n\t\tui->disconnectAccount->setVisible(true);\n\t\tui->useStreamKeyAdv->setVisible(false);\n\n\t\tui->connectedAccountLabel->setVisible(false);\n\t\tui->connectedAccountText->setVisible(false);\n\n#ifdef YOUTUBE_ENABLED\n\t\tif (IsYouTubeService(a->service())) {\n\t\t\tui->key->clear();\n\n\t\t\tui->connectedAccountLabel->setVisible(true);\n\t\t\tui->connectedAccountText->setVisible(true);\n\n\t\t\tui->connectedAccountText->setText(QTStr(\"Auth.LoadingChannel.Title\"));\n\n\t\t\tYoutubeApiWrappers *ytAuth = reinterpret_cast<YoutubeApiWrappers *>(a);\n\t\t\tChannelDescription cd;\n\t\t\tif (ytAuth->GetChannelDescription(cd)) {\n\t\t\t\tui->connectedAccountText->setText(cd.title);\n\n\t\t\t\t/* Create throwaway stream key for bandwidth test */\n\t\t\t\tif (ui->doBandwidthTest->isChecked()) {\n\t\t\t\t\tStreamDescription stream = {\"\", \"\", \"OBS Studio Test Stream\"};\n\t\t\t\t\tif (ytAuth->InsertStream(stream)) {\n\t\t\t\t\t\tui->key->setText(stream.name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\t}\n\n\tui->stackedWidget->setCurrentIndex((int)Section::StreamKey);\n\tUpdateCompleted();\n}\n\nvoid AutoConfigStreamPage::OnAuthConnected()\n{\n\tstd::string service = QT_TO_UTF8(ui->service->currentText());\n\tAuth::Type type = Auth::AuthType(service);\n\n\tif (type == Auth::Type::OAuth_StreamKey || type == Auth::Type::OAuth_LinkedAccount) {\n\t\tOnOAuthStreamKeyConnected();\n\t}\n}\n\nvoid AutoConfigStreamPage::on_connectAccount_clicked()\n{\n\tstd::string service = QT_TO_UTF8(ui->service->currentText());\n\n\tOAuth::DeleteCookies(service);\n\n\tauth = OAuthStreamKey::Login(this, service);\n\tif (!!auth) {\n\t\tOnAuthConnected();\n\n\t\tui->useStreamKeyAdv->setVisible(false);\n\t}\n}\n\n#define DISCONNECT_COMFIRM_TITLE \"Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Title\"\n#define DISCONNECT_COMFIRM_TEXT \"Basic.AutoConfig.StreamPage.DisconnectAccount.Confirm.Text\"\n\nvoid AutoConfigStreamPage::on_disconnectAccount_clicked()\n{\n\tQMessageBox::StandardButton button;\n\n\tbutton = OBSMessageBox::question(this, QTStr(DISCONNECT_COMFIRM_TITLE), QTStr(DISCONNECT_COMFIRM_TEXT));\n\n\tif (button == QMessageBox::No) {\n\t\treturn;\n\t}\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tmain->auth.reset();\n\tauth.reset();\n\n\tstd::string service = QT_TO_UTF8(ui->service->currentText());\n\n#ifdef BROWSER_AVAILABLE\n\tOAuth::DeleteCookies(service);\n#endif\n\n\treset_service_ui_fields(service);\n\n\tui->streamKeyWidget->setVisible(true);\n\tui->streamKeyLabel->setVisible(true);\n\tui->key->setText(\"\");\n\n\tui->connectedAccountLabel->setVisible(false);\n\tui->connectedAccountText->setVisible(false);\n\n\t/* Restore key link when disconnecting account */\n\tUpdateKeyLink();\n}\n\nvoid AutoConfigStreamPage::on_useStreamKey_clicked()\n{\n\tui->stackedWidget->setCurrentIndex((int)Section::StreamKey);\n\tUpdateCompleted();\n}\n\nvoid AutoConfigStreamPage::on_preferHardware_clicked()\n{\n\tauto *main = OBSBasic::Get();\n\tbool multitrackVideoEnabled = config_has_user_value(main->Config(), \"Stream1\", \"EnableMultitrackVideo\")\n\t\t\t\t\t      ? config_get_bool(main->Config(), \"Stream1\", \"EnableMultitrackVideo\")\n\t\t\t\t\t      : true;\n\n\tui->useMultitrackVideo->setEnabled(ui->preferHardware->isChecked());\n\tui->multitrackVideoInfo->setEnabled(ui->preferHardware->isChecked());\n\tui->useMultitrackVideo->setChecked(ui->preferHardware->isChecked() && multitrackVideoEnabled);\n}\n\nstatic inline bool is_auth_service(const std::string &service)\n{\n\treturn Auth::AuthType(service) != Auth::Type::None;\n}\n\nstatic inline bool is_external_oauth(const std::string &service)\n{\n\treturn Auth::External(service);\n}\n\nvoid AutoConfigStreamPage::reset_service_ui_fields(std::string &service)\n{\n#ifdef YOUTUBE_ENABLED\n\t// when account is already connected:\n\tOAuthStreamKey *a = reinterpret_cast<OAuthStreamKey *>(auth.get());\n\tif (a && service == a->service() && IsYouTubeService(a->service())) {\n\t\tui->connectedAccountLabel->setVisible(true);\n\t\tui->connectedAccountText->setVisible(true);\n\t\tui->connectAccount2->setVisible(false);\n\t\tui->disconnectAccount->setVisible(true);\n\t\treturn;\n\t}\n#endif\n\n\tbool external_oauth = is_external_oauth(service);\n\tif (external_oauth) {\n\t\tui->streamKeyWidget->setVisible(false);\n\t\tui->streamKeyLabel->setVisible(false);\n\t\tui->connectAccount2->setVisible(true);\n\t\tui->useStreamKeyAdv->setVisible(true);\n\n\t\tui->stackedWidget->setCurrentIndex((int)Section::StreamKey);\n\n\t} else if (cef) {\n\t\tQString key = ui->key->text();\n\t\tbool can_auth = is_auth_service(service);\n\t\tint page = can_auth && key.isEmpty() ? (int)Section::Connect : (int)Section::StreamKey;\n\n\t\tui->stackedWidget->setCurrentIndex(page);\n\t\tui->streamKeyWidget->setVisible(true);\n\t\tui->streamKeyLabel->setVisible(true);\n\t\tui->connectAccount2->setVisible(can_auth);\n\t\tui->useStreamKeyAdv->setVisible(false);\n\t} else {\n\t\tui->connectAccount2->setVisible(false);\n\t\tui->useStreamKeyAdv->setVisible(false);\n\t}\n\n\tui->connectedAccountLabel->setVisible(false);\n\tui->connectedAccountText->setVisible(false);\n\tui->disconnectAccount->setVisible(false);\n}\n\nvoid AutoConfigStreamPage::ServiceChanged()\n{\n\tbool showMore = ui->service->currentData().toInt() == (int)ListOpt::ShowAll;\n\tif (showMore)\n\t\treturn;\n\n\tstd::string service = QT_TO_UTF8(ui->service->currentText());\n\tbool regionBased = service == \"Twitch\";\n\tbool testBandwidth = ui->doBandwidthTest->isChecked();\n\tbool custom = IsCustomService();\n\n\tbool ertmp_multitrack_video_available = service == \"Twitch\";\n\n\tbool custom_disclaimer = false;\n\tauto multitrack_video_name = QTStr(\"Basic.Settings.Stream.MultitrackVideoLabel\");\n\tif (!custom) {\n\t\tOBSDataAutoRelease service_settings = obs_data_create();\n\t\tobs_data_set_string(service_settings, \"service\", service.c_str());\n\t\tOBSServiceAutoRelease obs_service =\n\t\t\tobs_service_create(\"rtmp_common\", \"temp service\", service_settings, nullptr);\n\n\t\tif (obs_data_has_user_value(service_settings, \"multitrack_video_name\")) {\n\t\t\tmultitrack_video_name = obs_data_get_string(service_settings, \"multitrack_video_name\");\n\t\t}\n\n\t\tif (obs_data_has_user_value(service_settings, \"multitrack_video_disclaimer\")) {\n\t\t\tui->multitrackVideoInfo->setText(\n\t\t\t\tobs_data_get_string(service_settings, \"multitrack_video_disclaimer\"));\n\t\t\tcustom_disclaimer = true;\n\t\t}\n\t}\n\n\tif (!custom_disclaimer) {\n\t\tui->multitrackVideoInfo->setText(\n\t\t\tQTStr(\"MultitrackVideo.Info\").arg(multitrack_video_name, service.c_str()));\n\t}\n\n\tui->multitrackVideoInfo->setVisible(ertmp_multitrack_video_available);\n\tui->useMultitrackVideo->setVisible(ertmp_multitrack_video_available);\n\tui->useMultitrackVideo->setText(\n\t\tQTStr(\"Basic.AutoConfig.StreamPage.UseMultitrackVideo\").arg(multitrack_video_name));\n\tui->multitrackVideoInfo->setEnabled(wiz->hardwareEncodingAvailable);\n\tui->useMultitrackVideo->setEnabled(wiz->hardwareEncodingAvailable);\n\n\treset_service_ui_fields(service);\n\n\t/* Test three closest servers if \"Auto\" is available for Twitch */\n\tif ((service == \"Twitch\" && wiz->twitchAuto) || (service == \"Amazon IVS\" && wiz->amazonIVSAuto))\n\t\tregionBased = false;\n\n\tui->streamkeyPageLayout->removeWidget(ui->serverLabel);\n\tui->streamkeyPageLayout->removeWidget(ui->serverStackedWidget);\n\n\tif (custom) {\n\t\tui->streamkeyPageLayout->insertRow(1, ui->serverLabel, ui->serverStackedWidget);\n\n\t\tui->region->setVisible(false);\n\t\tui->serverStackedWidget->setCurrentIndex(1);\n\t\tui->serverStackedWidget->setVisible(true);\n\t\tui->serverLabel->setVisible(true);\n\t} else {\n\t\tif (!testBandwidth)\n\t\t\tui->streamkeyPageLayout->insertRow(2, ui->serverLabel, ui->serverStackedWidget);\n\n\t\tui->region->setVisible(regionBased && testBandwidth);\n\t\tui->serverStackedWidget->setCurrentIndex(0);\n\t\tui->serverStackedWidget->setHidden(testBandwidth);\n\t\tui->serverLabel->setHidden(testBandwidth);\n\t}\n\n\twiz->testRegions = regionBased && testBandwidth;\n\n\tui->bitrateLabel->setHidden(testBandwidth);\n\tui->bitrate->setHidden(testBandwidth);\n\n\tOBSBasic *main = OBSBasic::Get();\n\n\tif (main->auth) {\n\t\tauto system_auth_service = main->auth->service();\n\t\tbool service_check = service.find(system_auth_service) != std::string::npos;\n#ifdef YOUTUBE_ENABLED\n\t\tservice_check = service_check ? service_check\n\t\t\t\t\t      : IsYouTubeService(system_auth_service) && IsYouTubeService(service);\n#endif\n\t\tif (service_check) {\n\t\t\tauth.reset();\n\t\t\tauth = main->auth;\n\t\t\tOnAuthConnected();\n\t\t}\n\t}\n\n\tUpdateCompleted();\n}\n\nvoid AutoConfigStreamPage::UpdateMoreInfoLink()\n{\n\tif (IsCustomService()) {\n\t\tui->moreInfoButton->hide();\n\t\treturn;\n\t}\n\n\tQString serviceName = ui->service->currentText();\n\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\tobs_property_t *services = obs_properties_get(props, \"service\");\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(serviceName));\n\tobs_property_modified(services, settings);\n\n\tconst char *more_info_link = obs_data_get_string(settings, \"more_info_link\");\n\n\tif (!more_info_link || (*more_info_link == '\\0')) {\n\t\tui->moreInfoButton->hide();\n\t} else {\n\t\tui->moreInfoButton->setTargetUrl(QUrl(more_info_link));\n\t\tui->moreInfoButton->show();\n\t}\n\tobs_properties_destroy(props);\n}\n\nvoid AutoConfigStreamPage::UpdateKeyLink()\n{\n\tQString serviceName = ui->service->currentText();\n\tQString customServer = ui->customServer->text().trimmed();\n\tQString streamKeyLink;\n\n\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\tobs_property_t *services = obs_properties_get(props, \"service\");\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(serviceName));\n\tobs_property_modified(services, settings);\n\n\tstreamKeyLink = obs_data_get_string(settings, \"stream_key_link\");\n\n\tif (customServer.contains(\"fbcdn.net\") && IsCustomService()) {\n\t\tstreamKeyLink = \"https://www.facebook.com/live/producer?ref=OBS\";\n\t}\n\n\tif (serviceName == \"Dacast\") {\n\t\tui->streamKeyLabel->setText(QTStr(\"Basic.AutoConfig.StreamPage.EncoderKey\"));\n\t\tui->streamKeyLabel->setToolTip(\"\");\n\t} else if (!IsCustomService()) {\n\t\tui->streamKeyLabel->setText(QTStr(\"Basic.AutoConfig.StreamPage.StreamKey\"));\n\t\tui->streamKeyLabel->setToolTip(\"\");\n\t} else {\n\t\t/* add tooltips for stream key */\n\t\tQString file = !App()->IsThemeDark() ? \":/res/images/help.svg\" : \":/res/images/help_light.svg\";\n\t\tQString lStr = \"<html>%1 <img src='%2' style=' \\\n\t\t\t\tvertical-align: bottom;  \\\n\t\t\t\t' /></html>\";\n\n\t\tui->streamKeyLabel->setText(lStr.arg(QTStr(\"Basic.AutoConfig.StreamPage.StreamKey\"), file));\n\t\tui->streamKeyLabel->setToolTip(QTStr(\"Basic.AutoConfig.StreamPage.StreamKey.ToolTip\"));\n\t}\n\n\tif (QString(streamKeyLink).isNull() || QString(streamKeyLink).isEmpty()) {\n\t\tui->streamKeyButton->hide();\n\t} else {\n\t\tui->streamKeyButton->setTargetUrl(QUrl(streamKeyLink));\n\t\tui->streamKeyButton->show();\n\t}\n\tobs_properties_destroy(props);\n}\n\nvoid AutoConfigStreamPage::LoadServices(bool showAll)\n{\n\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tobs_data_set_bool(settings, \"show_all\", showAll);\n\n\tobs_property_t *prop = obs_properties_get(props, \"show_all\");\n\tobs_property_modified(prop, settings);\n\n\tui->service->blockSignals(true);\n\tui->service->clear();\n\n\tQStringList names;\n\n\tobs_property_t *services = obs_properties_get(props, \"service\");\n\tsize_t services_count = obs_property_list_item_count(services);\n\tfor (size_t i = 0; i < services_count; i++) {\n\t\tconst char *name = obs_property_list_item_string(services, i);\n\t\tnames.push_back(name);\n\t}\n\n\tif (showAll)\n\t\tnames.sort(Qt::CaseInsensitive);\n\n\tfor (QString &name : names)\n\t\tui->service->addItem(name);\n\n\tif (!showAll) {\n\t\tui->service->addItem(QTStr(\"Basic.AutoConfig.StreamPage.Service.ShowAll\"),\n\t\t\t\t     QVariant((int)ListOpt::ShowAll));\n\t}\n\n\tui->service->insertItem(0, QTStr(\"Basic.AutoConfig.StreamPage.Service.Custom\"), QVariant((int)ListOpt::Custom));\n\n\tif (!lastService.isEmpty()) {\n\t\tint idx = ui->service->findText(lastService);\n\t\tif (idx != -1)\n\t\t\tui->service->setCurrentIndex(idx);\n\t}\n\n\tobs_properties_destroy(props);\n\n\tui->service->blockSignals(false);\n}\n\nvoid AutoConfigStreamPage::UpdateServerList()\n{\n\tQString serviceName = ui->service->currentText();\n\tbool showMore = ui->service->currentData().toInt() == (int)ListOpt::ShowAll;\n\n\tif (showMore) {\n\t\tLoadServices(true);\n\t\tui->service->showPopup();\n\t\treturn;\n\t} else {\n\t\tlastService = serviceName;\n\t}\n\n\tobs_properties_t *props = obs_get_service_properties(\"rtmp_common\");\n\tobs_property_t *services = obs_properties_get(props, \"service\");\n\n\tOBSDataAutoRelease settings = obs_data_create();\n\n\tobs_data_set_string(settings, \"service\", QT_TO_UTF8(serviceName));\n\tobs_property_modified(services, settings);\n\n\tobs_property_t *servers = obs_properties_get(props, \"server\");\n\n\tui->server->clear();\n\n\tsize_t servers_count = obs_property_list_item_count(servers);\n\tfor (size_t i = 0; i < servers_count; i++) {\n\t\tconst char *name = obs_property_list_item_name(servers, i);\n\t\tconst char *server = obs_property_list_item_string(servers, i);\n\t\tui->server->addItem(name, server);\n\t}\n\n\tobs_properties_destroy(props);\n}\n\nvoid AutoConfigStreamPage::UpdateCompleted()\n{\n\tconst bool custom = IsCustomService();\n\tif (ui->stackedWidget->currentIndex() == (int)Section::Connect ||\n\t    (ui->key->text().isEmpty() && !auth && !custom)) {\n\t\tready = false;\n\t} else {\n\t\tif (custom) {\n\t\t\tready = !ui->customServer->text().isEmpty();\n\t\t} else {\n\t\t\tready = !wiz->testRegions || ui->regionUS->isChecked() || ui->regionEU->isChecked() ||\n\t\t\t\tui->regionAsia->isChecked() || ui->regionOther->isChecked();\n\t\t}\n\t}\n\temit completeChanged();\n}\n"
  },
  {
    "path": "frontend/wizards/AutoConfigStreamPage.hpp",
    "content": "#pragma once\n\n#include <QWizardPage>\n\nclass Auth;\nclass Ui_AutoConfigStreamPage;\n\nclass AutoConfigStreamPage : public QWizardPage {\n\tQ_OBJECT\n\n\tfriend class AutoConfig;\n\n\tenum class Section : int {\n\t\tConnect,\n\t\tStreamKey,\n\t};\n\n\tstd::shared_ptr<Auth> auth;\n\n\tstd::unique_ptr<Ui_AutoConfigStreamPage> ui;\n\tQString lastService;\n\tbool ready = false;\n\n\tvoid LoadServices(bool showAll);\n\tinline bool IsCustomService() const;\n\npublic:\n\tAutoConfigStreamPage(QWidget *parent = nullptr);\n\t~AutoConfigStreamPage();\n\n\tvirtual bool isComplete() const override;\n\tvirtual int nextId() const override;\n\tvirtual bool validatePage() override;\n\n\tvoid OnAuthConnected();\n\tvoid OnOAuthStreamKeyConnected();\n\npublic slots:\n\tvoid on_show_clicked();\n\tvoid on_connectAccount_clicked();\n\tvoid on_disconnectAccount_clicked();\n\tvoid on_useStreamKey_clicked();\n\tvoid on_preferHardware_clicked();\n\tvoid ServiceChanged();\n\tvoid UpdateKeyLink();\n\tvoid UpdateMoreInfoLink();\n\tvoid UpdateServerList();\n\tvoid UpdateCompleted();\n\n\tvoid reset_service_ui_fields(std::string &service);\n};\n"
  },
  {
    "path": "frontend/wizards/AutoConfigTestPage.cpp",
    "content": "#include \"AutoConfigTestPage.hpp\"\n#include \"AutoConfig.hpp\"\n#include \"TestMode.hpp\"\n#include \"ui_AutoConfigTestPage.h\"\n\n#include <widgets/OBSBasic.hpp>\n\n#include <graphics/math-extra.h>\n#include <qt-wrappers.hpp>\n\n#include <QFormLayout>\n\n#include \"moc_AutoConfigTestPage.cpp\"\n\n#define TEST_STR(x) \"Basic.AutoConfig.TestPage.\" x\n#define SUBTITLE_TESTING TEST_STR(\"SubTitle.Testing\")\n#define SUBTITLE_COMPLETE TEST_STR(\"SubTitle.Complete\")\n#define TEST_BW TEST_STR(\"TestingBandwidth\")\n#define TEST_BW_NO_OUTPUT TEST_STR(\"TestingBandwidth.NoOutput\")\n#define TEST_BW_CONNECTING TEST_STR(\"TestingBandwidth.Connecting\")\n#define TEST_BW_CONNECT_FAIL TEST_STR(\"TestingBandwidth.ConnectFailed\")\n#define TEST_BW_SERVER TEST_STR(\"TestingBandwidth.Server\")\n#define TEST_RES_VAL TEST_STR(\"TestingRes.Resolution\")\n#define TEST_RES_FAIL TEST_STR(\"TestingRes.Fail\")\n#define TEST_SE TEST_STR(\"TestingStreamEncoder\")\n#define TEST_RE TEST_STR(\"TestingRecordingEncoder\")\n#define TEST_RESULT_SE TEST_STR(\"Result.StreamingEncoder\")\n#define TEST_RESULT_RE TEST_STR(\"Result.RecordingEncoder\")\n\n#define wiz reinterpret_cast<AutoConfig *>(wizard())\n\nusing namespace std;\n\nvoid AutoConfigTestPage::StartBandwidthStage()\n{\n\tui->progressLabel->setText(QTStr(TEST_BW));\n\ttestThread = std::thread([this]() { TestBandwidthThread(); });\n}\n\nvoid AutoConfigTestPage::StartStreamEncoderStage()\n{\n\tui->progressLabel->setText(QTStr(TEST_SE));\n\ttestThread = std::thread([this]() { TestStreamEncoderThread(); });\n}\n\nvoid AutoConfigTestPage::StartRecordingEncoderStage()\n{\n\tui->progressLabel->setText(QTStr(TEST_RE));\n\ttestThread = std::thread([this]() { TestRecordingEncoderThread(); });\n}\n\nvoid AutoConfigTestPage::GetServers(std::vector<ServerInfo> &servers)\n{\n\tOBSDataAutoRelease settings = obs_data_create();\n\tobs_data_set_string(settings, \"service\", wiz->serviceName.c_str());\n\n\tobs_properties_t *ppts = obs_get_service_properties(\"rtmp_common\");\n\tobs_property_t *p = obs_properties_get(ppts, \"service\");\n\tobs_property_modified(p, settings);\n\n\tp = obs_properties_get(ppts, \"server\");\n\tsize_t count = obs_property_list_item_count(p);\n\tservers.reserve(count);\n\n\tfor (size_t i = 0; i < count; i++) {\n\t\tconst char *name = obs_property_list_item_name(p, i);\n\t\tconst char *server = obs_property_list_item_string(p, i);\n\n\t\tif (wiz->CanTestServer(name)) {\n\t\t\tServerInfo info(name, server);\n\t\t\tservers.push_back(info);\n\t\t}\n\t}\n\n\tobs_properties_destroy(ppts);\n}\n\nstatic inline void string_depad_key(string &key)\n{\n\twhile (!key.empty()) {\n\t\tchar ch = key.back();\n\t\tif (ch == ' ' || ch == '\\t' || ch == '\\n' || ch == '\\r')\n\t\t\tkey.pop_back();\n\t\telse\n\t\t\tbreak;\n\t}\n}\n\nextern const char *FindAudioEncoderFromCodec(const char *type);\n\nstatic bool return_first_id(void *data, const char *id)\n{\n\tconst char **output = (const char **)data;\n\n\t*output = id;\n\treturn false;\n}\n\nvoid AutoConfigTestPage::TestBandwidthThread()\n{\n\tbool connected = false;\n\tbool stopped = false;\n\n\tTestMode testMode;\n\ttestMode.SetVideo(128, 128, 60, 1);\n\n\tQMetaObject::invokeMethod(this, \"Progress\", Q_ARG(int, 0));\n\n\t/*\n\t * create encoders\n\t * create output\n\t * test for 10 seconds\n\t */\n\n\tQMetaObject::invokeMethod(this, \"UpdateMessage\", Q_ARG(QString, QStringLiteral(\"\")));\n\n\t/* -----------------------------------*/\n\t/* create obs objects                 */\n\n\tconst char *serverType = wiz->customServer ? \"rtmp_custom\" : \"rtmp_common\";\n\n\tOBSEncoderAutoRelease vencoder = obs_video_encoder_create(\"obs_x264\", \"test_x264\", nullptr, nullptr);\n\tOBSEncoderAutoRelease aencoder = obs_audio_encoder_create(\"ffmpeg_aac\", \"test_aac\", nullptr, 0, nullptr);\n\tOBSServiceAutoRelease service = obs_service_create(serverType, \"test_service\", nullptr, nullptr);\n\n\t/* -----------------------------------*/\n\t/* configure settings                 */\n\n\t// service: \"service\", \"server\", \"key\"\n\t// vencoder: \"bitrate\", \"rate_control\",\n\t//           obs_service_apply_encoder_settings\n\t// aencoder: \"bitrate\"\n\t// output: \"bind_ip\" via main config -> \"Output\", \"BindIP\"\n\t//         obs_output_set_service\n\n\tOBSDataAutoRelease service_settings = obs_data_create();\n\tOBSDataAutoRelease vencoder_settings = obs_data_create();\n\tOBSDataAutoRelease aencoder_settings = obs_data_create();\n\tOBSDataAutoRelease output_settings = obs_data_create();\n\n\tstd::string key = wiz->key;\n\tif (wiz->service == AutoConfig::Service::Twitch || wiz->service == AutoConfig::Service::AmazonIVS) {\n\t\tstring_depad_key(key);\n\t\tkey += \"?bandwidthtest\";\n\t} else if (wiz->serviceName == \"Restream.io\" || wiz->serviceName == \"Restream.io - RTMP\") {\n\t\tstring_depad_key(key);\n\t\tkey += \"?test=true\";\n\t}\n\n\tobs_data_set_string(service_settings, \"service\", wiz->serviceName.c_str());\n\tobs_data_set_string(service_settings, \"key\", key.c_str());\n\n\tobs_data_set_int(vencoder_settings, \"bitrate\", wiz->startingBitrate);\n\tobs_data_set_string(vencoder_settings, \"rate_control\", \"CBR\");\n\tobs_data_set_string(vencoder_settings, \"preset\", \"veryfast\");\n\tobs_data_set_int(vencoder_settings, \"keyint_sec\", 2);\n\n\tobs_data_set_int(aencoder_settings, \"bitrate\", 32);\n\n\tOBSBasic *main = OBSBasic::Get();\n\tconst char *bind_ip = config_get_string(main->Config(), \"Output\", \"BindIP\");\n\tobs_data_set_string(output_settings, \"bind_ip\", bind_ip);\n\n\tconst char *ip_family = config_get_string(main->Config(), \"Output\", \"IPFamily\");\n\tobs_data_set_string(output_settings, \"ip_family\", ip_family);\n\n\t/* -----------------------------------*/\n\t/* determine which servers to test    */\n\n\tstd::vector<ServerInfo> servers;\n\tif (wiz->customServer)\n\t\tservers.emplace_back(wiz->server.c_str(), wiz->server.c_str());\n\telse\n\t\tGetServers(servers);\n\n\t/* just use the first server if it only has one alternate server,\n\t * or if using Restream or Nimo TV due to their \"auto\" servers */\n\tif (servers.size() < 3 || wiz->serviceName.substr(0, 11) == \"Restream.io\" || wiz->serviceName == \"Nimo TV\") {\n\t\tservers.resize(1);\n\n\t} else if ((wiz->service == AutoConfig::Service::Twitch && wiz->twitchAuto) ||\n\t\t   (wiz->service == AutoConfig::Service::AmazonIVS && wiz->amazonIVSAuto)) {\n\t\t/* if using Twitch and \"Auto\" is available, test 3 closest\n\t\t * server */\n\t\tservers.erase(servers.begin() + 1);\n\t\tservers.resize(3);\n\t} else if (wiz->service == AutoConfig::Service::YouTube) {\n\t\t/* Only test first set of primary + backup servers */\n\t\tservers.resize(2);\n\t}\n\n\tif (!wiz->serviceConfigServers.empty()) {\n\t\tif (wiz->service == AutoConfig::Service::Twitch && wiz->twitchAuto) {\n\t\t\t// servers from Twitch service config replace the \"auto\" entry\n\t\t\tservers.erase(servers.begin());\n\t\t}\n\n\t\tfor (auto it = std::rbegin(wiz->serviceConfigServers); it != std::rend(wiz->serviceConfigServers);\n\t\t     it++) {\n\t\t\tauto same_server =\n\t\t\t\tstd::find_if(std::begin(servers), std::end(servers),\n\t\t\t\t\t     [&](const ServerInfo &si) { return si.address == it->address; });\n\t\t\tif (same_server != std::end(servers))\n\t\t\t\tservers.erase(same_server);\n\t\t\tservers.emplace(std::begin(servers), it->name.c_str(), it->address.c_str());\n\t\t}\n\n\t\tif (wiz->service == AutoConfig::Service::Twitch && wiz->twitchAuto) {\n\t\t\t// see above, only test 3 servers\n\t\t\t// rtmps urls are currently counted as separate servers\n\t\t\tservers.resize(3);\n\t\t}\n\t}\n\n\t/* -----------------------------------*/\n\t/* apply service settings             */\n\n\tobs_service_update(service, service_settings);\n\tobs_service_apply_encoder_settings(service, vencoder_settings, aencoder_settings);\n\n\tif (wiz->multitrackVideo.testSuccessful) {\n\t\tobs_data_set_int(vencoder_settings, \"bitrate\", wiz->startingBitrate);\n\t}\n\n\t/* -----------------------------------*/\n\t/* create output                      */\n\n\t/* Check if the service has a preferred output type */\n\tconst char *output_type = obs_service_get_preferred_output_type(service);\n\tif (!output_type || (obs_get_output_flags(output_type) & OBS_OUTPUT_SERVICE) == 0) {\n\t\t/* Otherwise, prefer first-party output types */\n\t\tconst char *protocol = obs_service_get_protocol(service);\n\n\t\tif (can_use_output(protocol, \"rtmp_output\", \"RTMP\", \"RTMPS\")) {\n\t\t\toutput_type = \"rtmp_output\";\n\t\t} else if (can_use_output(protocol, \"ffmpeg_hls_muxer\", \"HLS\")) {\n\t\t\toutput_type = \"ffmpeg_hls_muxer\";\n\t\t} else if (can_use_output(protocol, \"ffmpeg_mpegts_muxer\", \"SRT\", \"RIST\")) {\n\t\t\toutput_type = \"ffmpeg_mpegts_muxer\";\n\t\t}\n\n\t\t/* If third-party protocol, use the first enumerated type */\n\t\tif (!output_type)\n\t\t\tobs_enum_output_types_with_protocol(protocol, &output_type, return_first_id);\n\n\t\t/* If none, fail */\n\t\tif (!output_type) {\n\t\t\tQMetaObject::invokeMethod(this, \"Failure\", Q_ARG(QString, QTStr(TEST_BW_NO_OUTPUT)));\n\t\t\treturn;\n\t\t}\n\t}\n\n\tOBSOutputAutoRelease output = obs_output_create(output_type, \"test_stream\", nullptr, nullptr);\n\tobs_output_update(output, output_settings);\n\n\tconst char *audio_codec = obs_output_get_supported_audio_codecs(output);\n\n\tif (strcmp(audio_codec, \"aac\") != 0) {\n\t\tconst char *id = FindAudioEncoderFromCodec(audio_codec);\n\t\taencoder = obs_audio_encoder_create(id, \"test_audio\", nullptr, 0, nullptr);\n\t}\n\n\t/* -----------------------------------*/\n\t/* connect encoders/services/outputs  */\n\n\tobs_encoder_update(vencoder, vencoder_settings);\n\tobs_encoder_update(aencoder, aencoder_settings);\n\tobs_encoder_set_video(vencoder, obs_get_video());\n\tobs_encoder_set_audio(aencoder, obs_get_audio());\n\n\tobs_output_set_video_encoder(output, vencoder);\n\tobs_output_set_audio_encoder(output, aencoder, 0);\n\tobs_output_set_reconnect_settings(output, 0, 0);\n\n\tobs_output_set_service(output, service);\n\n\t/* -----------------------------------*/\n\t/* connect signals                    */\n\n\tauto on_started = [&]() {\n\t\tunique_lock<mutex> lock(m);\n\t\tconnected = true;\n\t\tstopped = false;\n\t\tcv.notify_one();\n\t};\n\n\tauto on_stopped = [&]() {\n\t\tunique_lock<mutex> lock(m);\n\t\tconnected = false;\n\t\tstopped = true;\n\t\tcv.notify_one();\n\t};\n\n\tusing on_started_t = decltype(on_started);\n\tusing on_stopped_t = decltype(on_stopped);\n\n\tauto pre_on_started = [](void *data, calldata_t *) {\n\t\ton_started_t &on_started = *static_cast<on_started_t *>(data);\n\t\ton_started();\n\t};\n\n\tauto pre_on_stopped = [](void *data, calldata_t *) {\n\t\ton_stopped_t &on_stopped = *static_cast<on_stopped_t *>(data);\n\t\ton_stopped();\n\t};\n\n\tsignal_handler *sh = obs_output_get_signal_handler(output);\n\tsignal_handler_connect(sh, \"start\", pre_on_started, &on_started);\n\tsignal_handler_connect(sh, \"stop\", pre_on_stopped, &on_stopped);\n\n\t/* -----------------------------------*/\n\t/* test servers                       */\n\n\tbool success = false;\n\n\tfor (size_t i = 0; i < servers.size(); i++) {\n\t\tauto &server = servers[i];\n\n\t\tconnected = false;\n\t\tstopped = false;\n\n\t\tint per = int((i + 1) * 100 / servers.size());\n\t\tQMetaObject::invokeMethod(this, \"Progress\", Q_ARG(int, per));\n\t\tQMetaObject::invokeMethod(this, \"UpdateMessage\",\n\t\t\t\t\t  Q_ARG(QString, QTStr(TEST_BW_CONNECTING).arg(server.name.c_str())));\n\n\t\tobs_data_set_string(service_settings, \"server\", server.address.c_str());\n\t\tobs_service_update(service, service_settings);\n\n\t\tif (!obs_output_start(output))\n\t\t\tcontinue;\n\n\t\tunique_lock<mutex> ul(m);\n\t\tif (cancel) {\n\t\t\tul.unlock();\n\t\t\tobs_output_force_stop(output);\n\t\t\treturn;\n\t\t}\n\t\tif (!stopped && !connected)\n\t\t\tcv.wait(ul);\n\t\tif (cancel) {\n\t\t\tul.unlock();\n\t\t\tobs_output_force_stop(output);\n\t\t\treturn;\n\t\t}\n\t\tif (!connected)\n\t\t\tcontinue;\n\n\t\tQMetaObject::invokeMethod(this, \"UpdateMessage\",\n\t\t\t\t\t  Q_ARG(QString, QTStr(TEST_BW_SERVER).arg(server.name.c_str())));\n\n\t\t/* ignore first 2.5 seconds due to possible buffering skewing\n\t\t * the result */\n\t\tcv.wait_for(ul, chrono::milliseconds(2500));\n\t\tif (stopped)\n\t\t\tcontinue;\n\t\tif (cancel) {\n\t\t\tul.unlock();\n\t\t\tobs_output_force_stop(output);\n\t\t\treturn;\n\t\t}\n\n\t\t/* continue test */\n\t\tint start_bytes = (int)obs_output_get_total_bytes(output);\n\t\tuint64_t t_start = os_gettime_ns();\n\n\t\tcv.wait_for(ul, chrono::seconds(10));\n\t\tif (stopped)\n\t\t\tcontinue;\n\t\tif (cancel) {\n\t\t\tul.unlock();\n\t\t\tobs_output_force_stop(output);\n\t\t\treturn;\n\t\t}\n\n\t\tobs_output_stop(output);\n\t\tcv.wait(ul);\n\n\t\tuint64_t total_time = os_gettime_ns() - t_start;\n\t\tif (total_time == 0)\n\t\t\ttotal_time = 1;\n\n\t\tint total_bytes = (int)obs_output_get_total_bytes(output) - start_bytes;\n\t\tuint64_t bitrate = util_mul_div64(total_bytes, 8ULL * 1000000000ULL / 1000ULL, total_time);\n\t\tif (obs_output_get_frames_dropped(output) || (int)bitrate < (wiz->startingBitrate * 75 / 100)) {\n\t\t\tserver.bitrate = (int)bitrate * 70 / 100;\n\t\t} else {\n\t\t\tserver.bitrate = wiz->startingBitrate;\n\t\t}\n\n\t\tserver.ms = obs_output_get_connect_time_ms(output);\n\t\tsuccess = true;\n\t}\n\n\tif (!success) {\n\t\tQMetaObject::invokeMethod(this, \"Failure\", Q_ARG(QString, QTStr(TEST_BW_CONNECT_FAIL)));\n\t\treturn;\n\t}\n\n\tint bestBitrate = 0;\n\tint bestMS = 0x7FFFFFFF;\n\tstring bestServer;\n\tstring bestServerName;\n\n\tfor (auto &server : servers) {\n\t\tbool close = abs(server.bitrate - bestBitrate) < 400;\n\n\t\tif ((!close && server.bitrate > bestBitrate) || (close && server.ms < bestMS)) {\n\t\t\tbestServer = server.address;\n\t\t\tbestServerName = server.name;\n\t\t\tbestBitrate = server.bitrate;\n\t\t\tbestMS = server.ms;\n\t\t}\n\t}\n\n\twiz->server = std::move(bestServer);\n\twiz->serverName = std::move(bestServerName);\n\twiz->idealBitrate = bestBitrate;\n\n\tQMetaObject::invokeMethod(this, \"NextStage\");\n}\n\n/* this is used to estimate the lower bitrate limit for a given\n * resolution/fps.  yes, it is a totally arbitrary equation that gets\n * the closest to the expected values */\nstatic long double EstimateBitrateVal(int cx, int cy, int fps_num, int fps_den)\n{\n\tlong fps = (long double)fps_num / (long double)fps_den;\n\tlong double areaVal = pow((long double)(cx * cy), 0.85l);\n\treturn areaVal * sqrt(pow(fps, 1.1l));\n}\n\nstatic long double EstimateMinBitrate(int cx, int cy, int fps_num, int fps_den)\n{\n\tlong double val = EstimateBitrateVal(1920, 1080, 60, 1) / 5800.0l;\n\treturn EstimateBitrateVal(cx, cy, fps_num, fps_den) / val;\n}\n\nstatic long double EstimateUpperBitrate(int cx, int cy, int fps_num, int fps_den)\n{\n\tlong double val = EstimateBitrateVal(1280, 720, 30, 1) / 3000.0l;\n\treturn EstimateBitrateVal(cx, cy, fps_num, fps_den) / val;\n}\n\nstruct Result {\n\tint cx;\n\tint cy;\n\tint fps_num;\n\tint fps_den;\n\n\tinline Result(int cx_, int cy_, int fps_num_, int fps_den_)\n\t\t: cx(cx_),\n\t\t  cy(cy_),\n\t\t  fps_num(fps_num_),\n\t\t  fps_den(fps_den_)\n\t{\n\t}\n};\n\nstatic void CalcBaseRes(int &baseCX, int &baseCY)\n{\n\tconst int maxBaseArea = 1920 * 1200;\n\tconst int clipResArea = 1920 * 1080;\n\n\t/* if base resolution unusually high, recalculate to a more reasonable\n\t * value to start the downscaling at, based upon 1920x1080's area.\n\t *\n\t * for 16:9 resolutions this will always change the starting value to\n\t * 1920x1080 */\n\tif ((baseCX * baseCY) > maxBaseArea) {\n\t\tlong double xyAspect = (long double)baseCX / (long double)baseCY;\n\t\tbaseCY = (int)sqrt((long double)clipResArea / xyAspect);\n\t\tbaseCX = (int)((long double)baseCY * xyAspect);\n\t}\n}\n\nbool AutoConfigTestPage::TestSoftwareEncoding()\n{\n\tTestMode testMode;\n\tQMetaObject::invokeMethod(this, \"UpdateMessage\", Q_ARG(QString, QStringLiteral(\"\")));\n\n\t/* -----------------------------------*/\n\t/* create obs objects                 */\n\n\tOBSEncoderAutoRelease vencoder = obs_video_encoder_create(\"obs_x264\", \"test_x264\", nullptr, nullptr);\n\tOBSEncoderAutoRelease aencoder = obs_audio_encoder_create(\"ffmpeg_aac\", \"test_aac\", nullptr, 0, nullptr);\n\tOBSOutputAutoRelease output = obs_output_create(\"null_output\", \"null\", nullptr, nullptr);\n\n\t/* -----------------------------------*/\n\t/* configure settings                 */\n\n\tOBSDataAutoRelease aencoder_settings = obs_data_create();\n\tOBSDataAutoRelease vencoder_settings = obs_data_create();\n\tobs_data_set_int(aencoder_settings, \"bitrate\", 32);\n\n\tif (wiz->type != AutoConfig::Type::Recording) {\n\t\tobs_data_set_int(vencoder_settings, \"keyint_sec\", 2);\n\t\tobs_data_set_int(vencoder_settings, \"bitrate\", wiz->idealBitrate);\n\t\tobs_data_set_string(vencoder_settings, \"rate_control\", \"CBR\");\n\t\tobs_data_set_string(vencoder_settings, \"profile\", \"main\");\n\t\tobs_data_set_string(vencoder_settings, \"preset\", \"veryfast\");\n\t} else {\n\t\tobs_data_set_int(vencoder_settings, \"crf\", 20);\n\t\tobs_data_set_string(vencoder_settings, \"rate_control\", \"CRF\");\n\t\tobs_data_set_string(vencoder_settings, \"profile\", \"high\");\n\t\tobs_data_set_string(vencoder_settings, \"preset\", \"veryfast\");\n\t}\n\n\t/* -----------------------------------*/\n\t/* apply settings                     */\n\n\tobs_encoder_update(vencoder, vencoder_settings);\n\tobs_encoder_update(aencoder, aencoder_settings);\n\n\t/* -----------------------------------*/\n\t/* connect encoders/services/outputs  */\n\n\tobs_output_set_video_encoder(output, vencoder);\n\tobs_output_set_audio_encoder(output, aencoder, 0);\n\n\t/* -----------------------------------*/\n\t/* connect signals                    */\n\n\tauto on_stopped = [&]() {\n\t\tunique_lock<mutex> lock(m);\n\t\tcv.notify_one();\n\t};\n\n\tusing on_stopped_t = decltype(on_stopped);\n\n\tauto pre_on_stopped = [](void *data, calldata_t *) {\n\t\ton_stopped_t &on_stopped = *static_cast<on_stopped_t *>(data);\n\t\ton_stopped();\n\t};\n\n\tsignal_handler *sh = obs_output_get_signal_handler(output);\n\tsignal_handler_connect(sh, \"deactivate\", pre_on_stopped, &on_stopped);\n\n\t/* -----------------------------------*/\n\t/* calculate starting resolution      */\n\n\tint baseCX = wiz->baseResolutionCX;\n\tint baseCY = wiz->baseResolutionCY;\n\tCalcBaseRes(baseCX, baseCY);\n\n\t/* -----------------------------------*/\n\t/* calculate starting test rates      */\n\n\tint pcores = os_get_physical_cores();\n\tint lcores = os_get_logical_cores();\n\tint maxDataRate;\n\tif (lcores > 8 || pcores > 4) {\n\t\t/* superb */\n\t\tmaxDataRate = 1920 * 1200 * 60 + 1000;\n\n\t} else if (lcores > 4 && pcores == 4) {\n\t\t/* great */\n\t\tmaxDataRate = 1920 * 1080 * 60 + 1000;\n\n\t} else if (pcores == 4) {\n\t\t/* okay */\n\t\tmaxDataRate = 1920 * 1080 * 30 + 1000;\n\n\t} else {\n\t\t/* toaster */\n\t\tmaxDataRate = 960 * 540 * 30 + 1000;\n\t}\n\n\t/* -----------------------------------*/\n\t/* perform tests                      */\n\n\tvector<Result> results;\n\tint i = 0;\n\tint count = 1;\n\n\tauto testRes = [&](int cy, int fps_num, int fps_den, bool force) {\n\t\tint per = ++i * 100 / count;\n\t\tQMetaObject::invokeMethod(this, \"Progress\", Q_ARG(int, per));\n\n\t\tif (cy > baseCY)\n\t\t\treturn true;\n\n\t\t/* no need for more than 3 tests max */\n\t\tif (results.size() >= 3)\n\t\t\treturn true;\n\n\t\tif (!fps_num || !fps_den) {\n\t\t\tfps_num = wiz->specificFPSNum;\n\t\t\tfps_den = wiz->specificFPSDen;\n\t\t}\n\n\t\tlong double fps = ((long double)fps_num / (long double)fps_den);\n\n\t\tint cx = int(((long double)baseCX / (long double)baseCY) * (long double)cy);\n\n\t\tif (!force && wiz->type != AutoConfig::Type::Recording) {\n\t\t\tint est = EstimateMinBitrate(cx, cy, fps_num, fps_den);\n\t\t\tif (est > wiz->idealBitrate)\n\t\t\t\treturn true;\n\t\t}\n\n\t\tlong double rate = (long double)cx * (long double)cy * fps;\n\t\tif (!force && rate > maxDataRate)\n\t\t\treturn true;\n\n\t\ttestMode.SetVideo(cx, cy, fps_num, fps_den);\n\n\t\tobs_encoder_set_video(vencoder, obs_get_video());\n\t\tobs_encoder_set_audio(aencoder, obs_get_audio());\n\t\tobs_encoder_update(vencoder, vencoder_settings);\n\n\t\tobs_output_set_media(output, obs_get_video(), obs_get_audio());\n\n\t\tQString cxStr = QString::number(cx);\n\t\tQString cyStr = QString::number(cy);\n\n\t\tQString fpsStr = (fps_den > 1) ? QString::number(fps, 'f', 2) : QString::number(fps, 'g', 2);\n\n\t\tQMetaObject::invokeMethod(this, \"UpdateMessage\",\n\t\t\t\t\t  Q_ARG(QString, QTStr(TEST_RES_VAL).arg(cxStr, cyStr, fpsStr)));\n\n\t\tunique_lock<mutex> ul(m);\n\t\tif (cancel)\n\t\t\treturn false;\n\n\t\tif (!obs_output_start(output)) {\n\t\t\tQMetaObject::invokeMethod(this, \"Failure\", Q_ARG(QString, QTStr(TEST_RES_FAIL)));\n\t\t\treturn false;\n\t\t}\n\n\t\tcv.wait_for(ul, chrono::seconds(5));\n\n\t\tobs_output_stop(output);\n\t\tcv.wait(ul);\n\n\t\tint skipped = (int)video_output_get_skipped_frames(obs_get_video());\n\t\tif (force || skipped <= 10)\n\t\t\tresults.emplace_back(cx, cy, fps_num, fps_den);\n\n\t\treturn !cancel;\n\t};\n\n\tif (wiz->specificFPSNum && wiz->specificFPSDen) {\n\t\tcount = 7;\n\t\tif (!testRes(2160, 0, 0, false))\n\t\t\treturn false;\n\t\tif (!testRes(1440, 0, 0, false))\n\t\t\treturn false;\n\t\tif (!testRes(1080, 0, 0, false))\n\t\t\treturn false;\n\t\tif (!testRes(720, 0, 0, false))\n\t\t\treturn false;\n\t\tif (!testRes(480, 0, 0, false))\n\t\t\treturn false;\n\t\tif (!testRes(360, 0, 0, false))\n\t\t\treturn false;\n\t\tif (!testRes(240, 0, 0, true))\n\t\t\treturn false;\n\t} else {\n\t\tcount = 14;\n\t\tif (!testRes(2160, 60, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(2160, 30, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(1440, 60, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(1440, 30, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(1080, 60, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(1080, 30, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(720, 60, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(720, 30, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(480, 60, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(480, 30, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(360, 60, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(360, 30, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(240, 60, 1, false))\n\t\t\treturn false;\n\t\tif (!testRes(240, 30, 1, true))\n\t\t\treturn false;\n\t}\n\n\t/* -----------------------------------*/\n\t/* find preferred settings            */\n\n\tint minArea = 960 * 540 + 1000;\n\n\tif (!wiz->specificFPSNum && wiz->preferHighFPS && results.size() > 1) {\n\t\tResult &result1 = results[0];\n\t\tResult &result2 = results[1];\n\n\t\tif (result1.fps_num == 30 && result2.fps_num == 60) {\n\t\t\tint nextArea = result2.cx * result2.cy;\n\t\t\tif (nextArea >= minArea)\n\t\t\t\tresults.erase(results.begin());\n\t\t}\n\t}\n\n\tResult result = results.front();\n\twiz->idealResolutionCX = result.cx;\n\twiz->idealResolutionCY = result.cy;\n\twiz->idealFPSNum = result.fps_num;\n\twiz->idealFPSDen = result.fps_den;\n\n\tlong double fUpperBitrate = EstimateUpperBitrate(result.cx, result.cy, result.fps_num, result.fps_den);\n\n\tint upperBitrate = int(floor(fUpperBitrate / 50.0l) * 50.0l);\n\n\tif (wiz->streamingEncoder != AutoConfig::Encoder::x264) {\n\t\tupperBitrate *= 114;\n\t\tupperBitrate /= 100;\n\t}\n\n\tif (wiz->testMultitrackVideo && wiz->multitrackVideo.testSuccessful &&\n\t    !wiz->multitrackVideo.bitrate.has_value())\n\t\twiz->multitrackVideo.bitrate = wiz->idealBitrate;\n\n\tif (wiz->idealBitrate > upperBitrate)\n\t\twiz->idealBitrate = upperBitrate;\n\n\tsoftwareTested = true;\n\treturn true;\n}\n\nvoid AutoConfigTestPage::FindIdealHardwareResolution()\n{\n\tint baseCX = wiz->baseResolutionCX;\n\tint baseCY = wiz->baseResolutionCY;\n\tCalcBaseRes(baseCX, baseCY);\n\n\tvector<Result> results;\n\n\tint pcores = os_get_physical_cores();\n\tint maxDataRate;\n\tif (pcores >= 4) {\n\t\tmaxDataRate = 1920 * 1200 * 60 + 1000;\n\t} else {\n\t\tmaxDataRate = 1280 * 720 * 30 + 1000;\n\t}\n\n\tauto testRes = [&](int cy, int fps_num, int fps_den, bool force) {\n\t\tif (cy > baseCY)\n\t\t\treturn;\n\n\t\tif (results.size() >= 3)\n\t\t\treturn;\n\n\t\tif (!fps_num || !fps_den) {\n\t\t\tfps_num = wiz->specificFPSNum;\n\t\t\tfps_den = wiz->specificFPSDen;\n\t\t}\n\n\t\tlong double fps = ((long double)fps_num / (long double)fps_den);\n\n\t\tint cx = int(((long double)baseCX / (long double)baseCY) * (long double)cy);\n\n\t\tlong double rate = (long double)cx * (long double)cy * fps;\n\t\tif (!force && rate > maxDataRate)\n\t\t\treturn;\n\n\t\tAutoConfig::Encoder encType = wiz->streamingEncoder;\n\t\tbool nvenc = encType == AutoConfig::Encoder::NVENC;\n\n\t\tint minBitrate = EstimateMinBitrate(cx, cy, fps_num, fps_den);\n\n\t\t/* most hardware encoders don't have a good quality to bitrate\n\t\t * ratio, so increase the minimum bitrate estimate for them.\n\t\t * NVENC currently is the exception because of the improvements\n\t\t * its made to its quality in recent generations. */\n\t\tif (!nvenc)\n\t\t\tminBitrate = minBitrate * 114 / 100;\n\n\t\tif (wiz->type == AutoConfig::Type::Recording)\n\t\t\tforce = true;\n\t\tif (force || wiz->idealBitrate >= minBitrate)\n\t\t\tresults.emplace_back(cx, cy, fps_num, fps_den);\n\t};\n\n\tif (wiz->specificFPSNum && wiz->specificFPSDen) {\n\t\ttestRes(2160, 0, 0, false);\n\t\ttestRes(1440, 0, 0, false);\n\t\ttestRes(1080, 0, 0, false);\n\t\ttestRes(720, 0, 0, false);\n\t\ttestRes(480, 0, 0, false);\n\t\ttestRes(360, 0, 0, false);\n\t\ttestRes(240, 0, 0, true);\n\t} else {\n\t\ttestRes(2160, 60, 1, false);\n\t\ttestRes(2160, 30, 1, false);\n\t\ttestRes(1440, 60, 1, false);\n\t\ttestRes(1440, 30, 1, false);\n\t\ttestRes(1080, 60, 1, false);\n\t\ttestRes(1080, 30, 1, false);\n\t\ttestRes(720, 60, 1, false);\n\t\ttestRes(720, 30, 1, false);\n\t\ttestRes(480, 60, 1, false);\n\t\ttestRes(480, 30, 1, false);\n\t\ttestRes(360, 60, 1, false);\n\t\ttestRes(360, 30, 1, false);\n\t\ttestRes(240, 60, 1, false);\n\t\ttestRes(240, 30, 1, true);\n\t}\n\n\tint minArea = 960 * 540 + 1000;\n\n\tif (!wiz->specificFPSNum && wiz->preferHighFPS && results.size() > 1) {\n\t\tResult &result1 = results[0];\n\t\tResult &result2 = results[1];\n\n\t\tif (result1.fps_num == 30 && result2.fps_num == 60) {\n\t\t\tint nextArea = result2.cx * result2.cy;\n\t\t\tif (nextArea >= minArea)\n\t\t\t\tresults.erase(results.begin());\n\t\t}\n\t}\n\n\tResult result = results.front();\n\twiz->idealResolutionCX = result.cx;\n\twiz->idealResolutionCY = result.cy;\n\twiz->idealFPSNum = result.fps_num;\n\twiz->idealFPSDen = result.fps_den;\n}\n\nvoid AutoConfigTestPage::TestStreamEncoderThread()\n{\n\tbool preferHardware = wiz->preferHardware;\n\tif (!softwareTested) {\n\t\tif (!preferHardware || !wiz->hardwareEncodingAvailable) {\n\t\t\tif (!TestSoftwareEncoding()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!softwareTested) {\n\t\tif (wiz->nvencAvailable)\n\t\t\twiz->streamingEncoder = AutoConfig::Encoder::NVENC;\n\t\telse if (wiz->qsvAvailable)\n\t\t\twiz->streamingEncoder = AutoConfig::Encoder::QSV;\n\t\telse if (wiz->appleAvailable)\n\t\t\twiz->streamingEncoder = AutoConfig::Encoder::Apple;\n\t\telse\n\t\t\twiz->streamingEncoder = AutoConfig::Encoder::AMD;\n\t} else {\n\t\twiz->streamingEncoder = AutoConfig::Encoder::x264;\n\t}\n\n#ifdef __linux__\n\t// On linux CBR rate control is not guaranteed so fallback to x264.\n\tif (wiz->streamingEncoder == AutoConfig::Encoder::QSV) {\n\t\twiz->streamingEncoder = AutoConfig::Encoder::x264;\n\t\tif (!TestSoftwareEncoding()) {\n\t\t\treturn;\n\t\t}\n\t}\n#endif\n\n\tif (preferHardware && !softwareTested && wiz->hardwareEncodingAvailable)\n\t\tFindIdealHardwareResolution();\n\n\tQMetaObject::invokeMethod(this, \"NextStage\");\n}\n\nvoid AutoConfigTestPage::TestRecordingEncoderThread()\n{\n\tif (!wiz->hardwareEncodingAvailable && !softwareTested) {\n\t\tif (!TestSoftwareEncoding()) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (wiz->type == AutoConfig::Type::Recording && wiz->hardwareEncodingAvailable)\n\t\tFindIdealHardwareResolution();\n\n\twiz->recordingQuality = AutoConfig::Quality::High;\n\n\tbool recordingOnly = wiz->type == AutoConfig::Type::Recording;\n\n\tif (wiz->hardwareEncodingAvailable) {\n\t\tif (wiz->nvencAvailable)\n\t\t\twiz->recordingEncoder = AutoConfig::Encoder::NVENC;\n\t\telse if (wiz->qsvAvailable)\n\t\t\twiz->recordingEncoder = AutoConfig::Encoder::QSV;\n\t\telse if (wiz->appleAvailable)\n\t\t\twiz->recordingEncoder = AutoConfig::Encoder::Apple;\n\t\telse\n\t\t\twiz->recordingEncoder = AutoConfig::Encoder::AMD;\n\t} else {\n\t\twiz->recordingEncoder = AutoConfig::Encoder::x264;\n\t}\n\n\tif (wiz->recordingEncoder != AutoConfig::Encoder::NVENC) {\n\t\tif (!recordingOnly) {\n\t\t\twiz->recordingEncoder = AutoConfig::Encoder::Stream;\n\t\t\twiz->recordingQuality = AutoConfig::Quality::Stream;\n\t\t}\n\t}\n\n\tQMetaObject::invokeMethod(this, \"NextStage\");\n}\n\n#define ENCODER_TEXT(x) \"Basic.Settings.Output.Simple.Encoder.\" x\n#define ENCODER_SOFTWARE ENCODER_TEXT(\"Software\")\n#define ENCODER_NVENC ENCODER_TEXT(\"Hardware.NVENC.H264\")\n#define ENCODER_QSV ENCODER_TEXT(\"Hardware.QSV.H264\")\n#define ENCODER_AMD ENCODER_TEXT(\"Hardware.AMD.H264\")\n#define ENCODER_APPLE ENCODER_TEXT(\"Hardware.Apple.H264\")\n\n#define QUALITY_SAME \"Basic.Settings.Output.Simple.RecordingQuality.Stream\"\n#define QUALITY_HIGH \"Basic.Settings.Output.Simple.RecordingQuality.Small\"\n\nvoid set_closest_res(int &cx, int &cy, struct obs_service_resolution *res_list, size_t count)\n{\n\tint best_pixel_diff = 0x7FFFFFFF;\n\tint start_cx = cx;\n\tint start_cy = cy;\n\n\tfor (size_t i = 0; i < count; i++) {\n\t\tstruct obs_service_resolution &res = res_list[i];\n\t\tint pixel_cx_diff = abs(start_cx - res.cx);\n\t\tint pixel_cy_diff = abs(start_cy - res.cy);\n\t\tint pixel_diff = pixel_cx_diff + pixel_cy_diff;\n\n\t\tif (pixel_diff < best_pixel_diff) {\n\t\t\tbest_pixel_diff = pixel_diff;\n\t\t\tcx = res.cx;\n\t\t\tcy = res.cy;\n\t\t}\n\t}\n}\n\nvoid AutoConfigTestPage::FinalizeResults()\n{\n\tui->stackedWidget->setCurrentIndex(1);\n\tsetSubTitle(QTStr(SUBTITLE_COMPLETE));\n\n\tQFormLayout *form = results;\n\n\tauto encName = [](AutoConfig::Encoder enc) -> QString {\n\t\tswitch (enc) {\n\t\tcase AutoConfig::Encoder::x264:\n\t\t\treturn QTStr(ENCODER_SOFTWARE);\n\t\tcase AutoConfig::Encoder::NVENC:\n\t\t\treturn QTStr(ENCODER_NVENC);\n\t\tcase AutoConfig::Encoder::QSV:\n\t\t\treturn QTStr(ENCODER_QSV);\n\t\tcase AutoConfig::Encoder::AMD:\n\t\t\treturn QTStr(ENCODER_AMD);\n\t\tcase AutoConfig::Encoder::Apple:\n\t\t\treturn QTStr(ENCODER_APPLE);\n\t\tcase AutoConfig::Encoder::Stream:\n\t\t\treturn QTStr(QUALITY_SAME);\n\t\t}\n\n\t\treturn QTStr(ENCODER_SOFTWARE);\n\t};\n\n\tauto newLabel = [this](const char *str) -> QLabel * {\n\t\treturn new QLabel(QTStr(str), this);\n\t};\n\n\tif (wiz->type == AutoConfig::Type::Streaming) {\n\t\tconst char *serverType = wiz->customServer ? \"rtmp_custom\" : \"rtmp_common\";\n\n\t\tOBSServiceAutoRelease service = obs_service_create(serverType, \"temp_service\", nullptr, nullptr);\n\n\t\tOBSDataAutoRelease service_settings = obs_data_create();\n\t\tOBSDataAutoRelease vencoder_settings = obs_data_create();\n\n\t\tif (wiz->testMultitrackVideo && wiz->multitrackVideo.testSuccessful &&\n\t\t    !wiz->multitrackVideo.bitrate.has_value())\n\t\t\twiz->multitrackVideo.bitrate = wiz->idealBitrate;\n\n\t\tobs_data_set_int(vencoder_settings, \"bitrate\", wiz->idealBitrate);\n\n\t\tobs_data_set_string(service_settings, \"service\", wiz->serviceName.c_str());\n\t\tobs_service_update(service, service_settings);\n\t\tobs_service_apply_encoder_settings(service, vencoder_settings, nullptr);\n\n\t\tBPtr<obs_service_resolution> res_list;\n\t\tsize_t res_count;\n\t\tint maxFPS;\n\t\tobs_service_get_supported_resolutions(service, &res_list, &res_count);\n\t\tobs_service_get_max_fps(service, &maxFPS);\n\n\t\tif (res_list) {\n\t\t\tset_closest_res(wiz->idealResolutionCX, wiz->idealResolutionCY, res_list, res_count);\n\t\t}\n\t\tif (maxFPS) {\n\t\t\tdouble idealFPS = (double)wiz->idealFPSNum / (double)wiz->idealFPSDen;\n\t\t\tif (idealFPS > (double)maxFPS) {\n\t\t\t\twiz->idealFPSNum = maxFPS;\n\t\t\t\twiz->idealFPSDen = 1;\n\t\t\t}\n\t\t}\n\n\t\twiz->idealBitrate = (int)obs_data_get_int(vencoder_settings, \"bitrate\");\n\n\t\tif (!wiz->customServer)\n\t\t\tform->addRow(newLabel(\"Basic.AutoConfig.StreamPage.Service\"),\n\t\t\t\t     new QLabel(wiz->serviceName.c_str(), ui->finishPage));\n\t\tform->addRow(newLabel(\"Basic.AutoConfig.StreamPage.Server\"),\n\t\t\t     new QLabel(wiz->serverName.c_str(), ui->finishPage));\n\t\tform->addRow(newLabel(\"Basic.Settings.Stream.MultitrackVideoLabel\"),\n\t\t\t     newLabel(wiz->multitrackVideo.testSuccessful ? \"Yes\" : \"No\"));\n\n\t\tif (wiz->multitrackVideo.testSuccessful) {\n\t\t\tform->addRow(newLabel(\"Basic.Settings.Output.VideoBitrate\"), newLabel(\"Automatic\"));\n\t\t\tform->addRow(newLabel(TEST_RESULT_SE), newLabel(\"Automatic\"));\n\t\t\tform->addRow(newLabel(\"Basic.AutoConfig.TestPage.Result.StreamingResolution\"),\n\t\t\t\t     newLabel(\"Automatic\"));\n\t\t} else {\n\t\t\tform->addRow(newLabel(\"Basic.Settings.Output.VideoBitrate\"),\n\t\t\t\t     new QLabel(QString::number(wiz->idealBitrate), ui->finishPage));\n\t\t\tform->addRow(newLabel(TEST_RESULT_SE),\n\t\t\t\t     new QLabel(encName(wiz->streamingEncoder), ui->finishPage));\n\t\t}\n\t}\n\n\tQString baseRes =\n\t\tQString(\"%1x%2\").arg(QString::number(wiz->baseResolutionCX), QString::number(wiz->baseResolutionCY));\n\tQString scaleRes =\n\t\tQString(\"%1x%2\").arg(QString::number(wiz->idealResolutionCX), QString::number(wiz->idealResolutionCY));\n\n\tif (wiz->recordingEncoder != AutoConfig::Encoder::Stream ||\n\t    wiz->recordingQuality != AutoConfig::Quality::Stream)\n\t\tform->addRow(newLabel(TEST_RESULT_RE), new QLabel(encName(wiz->recordingEncoder), ui->finishPage));\n\n\tQString recQuality;\n\n\tswitch (wiz->recordingQuality) {\n\tcase AutoConfig::Quality::High:\n\t\trecQuality = QTStr(QUALITY_HIGH);\n\t\tbreak;\n\tcase AutoConfig::Quality::Stream:\n\t\trecQuality = QTStr(QUALITY_SAME);\n\t\tbreak;\n\t}\n\n\tform->addRow(newLabel(\"Basic.Settings.Output.Simple.RecordingQuality\"), new QLabel(recQuality, ui->finishPage));\n\n\tlong double fps = (long double)wiz->idealFPSNum / (long double)wiz->idealFPSDen;\n\n\tQString fpsStr = (wiz->idealFPSDen > 1) ? QString::number(fps, 'f', 2) : QString::number(fps, 'g', 2);\n\n\tform->addRow(newLabel(\"Basic.Settings.Video.BaseResolution\"), new QLabel(baseRes, ui->finishPage));\n\tform->addRow(newLabel(\"Basic.Settings.Video.ScaledResolution\"), new QLabel(scaleRes, ui->finishPage));\n\tform->addRow(newLabel(\"Basic.Settings.Video.FPS\"), new QLabel(fpsStr, ui->finishPage));\n\n\t// FIXME: form layout is super squished, probably need to set proper sizepolicy on all widgets?\n}\n\n#define STARTING_SEPARATOR \"\\n==== Auto-config wizard testing commencing ======\\n\"\n#define STOPPING_SEPARATOR \"\\n==== Auto-config wizard testing stopping ========\\n\"\n\nvoid AutoConfigTestPage::NextStage()\n{\n\tif (testThread.joinable())\n\t\ttestThread.join();\n\tif (cancel)\n\t\treturn;\n\n\tui->subProgressLabel->setText(QString());\n\n\t/* make it skip to bandwidth stage if only set to config recording */\n\tif (stage == Stage::Starting) {\n\t\tif (!started) {\n\t\t\tblog(LOG_INFO, STARTING_SEPARATOR);\n\t\t\tstarted = true;\n\t\t}\n\n\t\tif (wiz->type != AutoConfig::Type::Streaming) {\n\t\t\tstage = Stage::StreamEncoder;\n\t\t} else if (!wiz->bandwidthTest) {\n\t\t\tstage = Stage::BandwidthTest;\n\t\t}\n\t}\n\n\tif (stage == Stage::Starting) {\n\t\tstage = Stage::BandwidthTest;\n\t\tStartBandwidthStage();\n\n\t} else if (stage == Stage::BandwidthTest) {\n\t\tstage = Stage::StreamEncoder;\n\t\tStartStreamEncoderStage();\n\n\t} else if (stage == Stage::StreamEncoder) {\n\t\tstage = Stage::RecordingEncoder;\n\t\tStartRecordingEncoderStage();\n\n\t} else {\n\t\tstage = Stage::Finished;\n\t\tFinalizeResults();\n\t\temit completeChanged();\n\t}\n}\n\nvoid AutoConfigTestPage::UpdateMessage(QString message)\n{\n\tui->subProgressLabel->setText(message);\n}\n\nvoid AutoConfigTestPage::Failure(QString message)\n{\n\tui->errorLabel->setText(message);\n\tui->stackedWidget->setCurrentIndex(2);\n}\n\nvoid AutoConfigTestPage::Progress(int percentage)\n{\n\tui->progressBar->setValue(percentage);\n}\n\nAutoConfigTestPage::AutoConfigTestPage(QWidget *parent) : QWizardPage(parent), ui(new Ui_AutoConfigTestPage)\n{\n\tui->setupUi(this);\n\tsetTitle(QTStr(\"Basic.AutoConfig.TestPage\"));\n\tsetSubTitle(QTStr(SUBTITLE_TESTING));\n\tsetCommitPage(true);\n}\n\nAutoConfigTestPage::~AutoConfigTestPage()\n{\n\tif (testThread.joinable()) {\n\t\t{\n\t\t\tunique_lock<mutex> ul(m);\n\t\t\tcancel = true;\n\t\t\tcv.notify_one();\n\t\t}\n\t\ttestThread.join();\n\t}\n\n\tif (started)\n\t\tblog(LOG_INFO, STOPPING_SEPARATOR);\n}\n\nvoid AutoConfigTestPage::initializePage()\n{\n\tif (wiz->type == AutoConfig::Type::VirtualCam) {\n\t\twiz->idealResolutionCX = wiz->baseResolutionCX;\n\t\twiz->idealResolutionCY = wiz->baseResolutionCY;\n\t\twiz->idealFPSNum = 30;\n\t\twiz->idealFPSDen = 1;\n\t\tstage = Stage::Finished;\n\t} else {\n\t\tstage = Stage::Starting;\n\t}\n\n\tsetSubTitle(QTStr(SUBTITLE_TESTING));\n\tsoftwareTested = false;\n\tcancel = false;\n\tDeleteLayout(results);\n\tresults = new QFormLayout();\n\tresults->setContentsMargins(0, 0, 0, 0);\n\tui->finishPageLayout->insertLayout(1, results);\n\tui->stackedWidget->setCurrentIndex(0);\n\tNextStage();\n}\n\nvoid AutoConfigTestPage::cleanupPage()\n{\n\tif (testThread.joinable()) {\n\t\t{\n\t\t\tunique_lock<mutex> ul(m);\n\t\t\tcancel = true;\n\t\t\tcv.notify_one();\n\t\t}\n\t\ttestThread.join();\n\t}\n}\n\nbool AutoConfigTestPage::isComplete() const\n{\n\treturn stage == Stage::Finished;\n}\n\nint AutoConfigTestPage::nextId() const\n{\n\treturn -1;\n}\n"
  },
  {
    "path": "frontend/wizards/AutoConfigTestPage.hpp",
    "content": "#pragma once\n\n#include <QPointer>\n#include <QWizardPage>\n\n#include <condition_variable>\n#include <mutex>\n#include <thread>\n\nclass QFormLayout;\nclass Ui_AutoConfigTestPage;\n\nclass AutoConfigTestPage : public QWizardPage {\n\tQ_OBJECT\n\n\tfriend class AutoConfig;\n\n\tQPointer<QFormLayout> results;\n\n\tstd::unique_ptr<Ui_AutoConfigTestPage> ui;\n\tstd::thread testThread;\n\tstd::condition_variable cv;\n\tstd::mutex m;\n\tbool cancel = false;\n\tbool started = false;\n\n\tenum class Stage {\n\t\tStarting,\n\t\tBandwidthTest,\n\t\tStreamEncoder,\n\t\tRecordingEncoder,\n\t\tFinished,\n\t};\n\n\tStage stage = Stage::Starting;\n\tbool softwareTested = false;\n\n\tvoid StartBandwidthStage();\n\tvoid StartStreamEncoderStage();\n\tvoid StartRecordingEncoderStage();\n\n\tvoid FindIdealHardwareResolution();\n\tbool TestSoftwareEncoding();\n\n\tvoid TestBandwidthThread();\n\tvoid TestStreamEncoderThread();\n\tvoid TestRecordingEncoderThread();\n\n\tvoid FinalizeResults();\n\n\tstruct ServerInfo {\n\t\tstd::string name;\n\t\tstd::string address;\n\t\tint bitrate = 0;\n\t\tint ms = -1;\n\n\t\tinline ServerInfo() {}\n\n\t\tinline ServerInfo(const char *name_, const char *address_) : name(name_), address(address_) {}\n\t};\n\n\tvoid GetServers(std::vector<ServerInfo> &servers);\n\npublic:\n\tAutoConfigTestPage(QWidget *parent = nullptr);\n\t~AutoConfigTestPage();\n\n\tvirtual void initializePage() override;\n\tvirtual void cleanupPage() override;\n\tvirtual bool isComplete() const override;\n\tvirtual int nextId() const override;\n\npublic slots:\n\tvoid NextStage();\n\tvoid UpdateMessage(QString message);\n\tvoid Failure(QString message);\n\tvoid Progress(int percentage);\n};\n"
  },
  {
    "path": "frontend/wizards/AutoConfigVideoPage.cpp",
    "content": "#include \"AutoConfigVideoPage.hpp\"\n#include \"AutoConfig.hpp\"\n#include \"ui_AutoConfigVideoPage.h\"\n\n#include <OBSApp.hpp>\n\n#include <QScreen>\n\n#include \"moc_AutoConfigVideoPage.cpp\"\n\n#define RES_TEXT(x) \"Basic.AutoConfig.VideoPage.\" x\n#define RES_USE_CURRENT RES_TEXT(\"BaseResolution.UseCurrent\")\n#define RES_USE_DISPLAY RES_TEXT(\"BaseResolution.Display\")\n#define FPS_USE_CURRENT RES_TEXT(\"FPS.UseCurrent\")\n#define FPS_PREFER_HIGH_FPS RES_TEXT(\"FPS.PreferHighFPS\")\n#define FPS_PREFER_HIGH_RES RES_TEXT(\"FPS.PreferHighRes\")\n\n#define wiz reinterpret_cast<AutoConfig *>(wizard())\n\nAutoConfigVideoPage::AutoConfigVideoPage(QWidget *parent) : QWizardPage(parent), ui(new Ui_AutoConfigVideoPage)\n{\n\tui->setupUi(this);\n\n\tsetTitle(QTStr(\"Basic.AutoConfig.VideoPage\"));\n\tsetSubTitle(QTStr(\"Basic.AutoConfig.VideoPage.SubTitle\"));\n\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tlong double fpsVal = (long double)ovi.fps_num / (long double)ovi.fps_den;\n\n\tQString fpsStr = (ovi.fps_den > 1) ? QString::number(fpsVal, 'f', 2) : QString::number(fpsVal, 'g');\n\n\tui->fps->addItem(QTStr(FPS_PREFER_HIGH_FPS), (int)AutoConfig::FPSType::PreferHighFPS);\n\tui->fps->addItem(QTStr(FPS_PREFER_HIGH_RES), (int)AutoConfig::FPSType::PreferHighRes);\n\tui->fps->addItem(QTStr(FPS_USE_CURRENT).arg(fpsStr), (int)AutoConfig::FPSType::UseCurrent);\n\tui->fps->addItem(QStringLiteral(\"30\"), (int)AutoConfig::FPSType::fps30);\n\tui->fps->addItem(QStringLiteral(\"60\"), (int)AutoConfig::FPSType::fps60);\n\tui->fps->setCurrentIndex(0);\n\n\tQString cxStr = QString::number(ovi.base_width);\n\tQString cyStr = QString::number(ovi.base_height);\n\n\tint encRes = int(ovi.base_width << 16) | int(ovi.base_height);\n\n\t// Auto config only supports testing down to 240p, don't allow current\n\t// resolution if it's lower than that.\n\tif (ovi.base_height >= 240)\n\t\tui->canvasRes->addItem(QTStr(RES_USE_CURRENT).arg(cxStr, cyStr), (int)encRes);\n\n\tQList<QScreen *> screens = QGuiApplication::screens();\n\tfor (int i = 0; i < screens.size(); i++) {\n\t\tQScreen *screen = screens[i];\n\t\tQSize as = screen->size();\n\t\tint as_width = as.width();\n\t\tint as_height = as.height();\n\n\t\t// Calculate physical screen resolution based on the virtual screen resolution\n\t\t// They might differ if scaling is enabled, e.g. for HiDPI screens\n\t\tas_width = round(as_width * screen->devicePixelRatio());\n\t\tas_height = round(as_height * screen->devicePixelRatio());\n\n\t\tencRes = as_width << 16 | as_height;\n\n\t\tQString str =\n\t\t\tQTStr(RES_USE_DISPLAY)\n\t\t\t\t.arg(QString::number(i + 1), QString::number(as_width), QString::number(as_height));\n\n\t\tui->canvasRes->addItem(str, encRes);\n\t}\n\n\tauto addRes = [&](int cx, int cy) {\n\t\tencRes = (cx << 16) | cy;\n\t\tQString str = QString(\"%1x%2\").arg(QString::number(cx), QString::number(cy));\n\t\tui->canvasRes->addItem(str, encRes);\n\t};\n\n\taddRes(1920, 1080);\n\taddRes(1280, 720);\n\n\tui->canvasRes->setCurrentIndex(0);\n}\n\nAutoConfigVideoPage::~AutoConfigVideoPage() {}\n\nint AutoConfigVideoPage::nextId() const\n{\n\treturn wiz->type == AutoConfig::Type::Recording ? AutoConfig::TestPage : AutoConfig::StreamPage;\n}\n\nbool AutoConfigVideoPage::validatePage()\n{\n\tint encRes = ui->canvasRes->currentData().toInt();\n\twiz->baseResolutionCX = encRes >> 16;\n\twiz->baseResolutionCY = encRes & 0xFFFF;\n\twiz->fpsType = (AutoConfig::FPSType)ui->fps->currentData().toInt();\n\n\tobs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\n\tswitch (wiz->fpsType) {\n\tcase AutoConfig::FPSType::PreferHighFPS:\n\t\twiz->specificFPSNum = 0;\n\t\twiz->specificFPSDen = 0;\n\t\twiz->preferHighFPS = true;\n\t\tbreak;\n\tcase AutoConfig::FPSType::PreferHighRes:\n\t\twiz->specificFPSNum = 0;\n\t\twiz->specificFPSDen = 0;\n\t\twiz->preferHighFPS = false;\n\t\tbreak;\n\tcase AutoConfig::FPSType::UseCurrent:\n\t\twiz->specificFPSNum = ovi.fps_num;\n\t\twiz->specificFPSDen = ovi.fps_den;\n\t\twiz->preferHighFPS = false;\n\t\tbreak;\n\tcase AutoConfig::FPSType::fps30:\n\t\twiz->specificFPSNum = 30;\n\t\twiz->specificFPSDen = 1;\n\t\twiz->preferHighFPS = false;\n\t\tbreak;\n\tcase AutoConfig::FPSType::fps60:\n\t\twiz->specificFPSNum = 60;\n\t\twiz->specificFPSDen = 1;\n\t\twiz->preferHighFPS = false;\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "frontend/wizards/AutoConfigVideoPage.hpp",
    "content": "#pragma once\n\n#include <QWizardPage>\n\nclass Ui_AutoConfigVideoPage;\n\nclass AutoConfigVideoPage : public QWizardPage {\n\tQ_OBJECT\n\n\tfriend class AutoConfig;\n\n\tstd::unique_ptr<Ui_AutoConfigVideoPage> ui;\n\npublic:\n\tAutoConfigVideoPage(QWidget *parent = nullptr);\n\t~AutoConfigVideoPage();\n\n\tvirtual int nextId() const override;\n\tvirtual bool validatePage() override;\n};\n"
  },
  {
    "path": "frontend/wizards/TestMode.hpp",
    "content": "#pragma once\n\n#include <graphics/math-extra.h>\n#include <obs.hpp>\n\nclass TestMode {\n\tobs_video_info ovi;\n\tOBSSource source[6];\n\n\tstatic void render_rand(void *, uint32_t cx, uint32_t cy)\n\t{\n\t\tgs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);\n\t\tgs_eparam_t *randomvals[3] = {gs_effect_get_param_by_name(solid, \"randomvals1\"),\n\t\t\t\t\t      gs_effect_get_param_by_name(solid, \"randomvals2\"),\n\t\t\t\t\t      gs_effect_get_param_by_name(solid, \"randomvals3\")};\n\n\t\tstruct vec4 r;\n\n\t\tfor (int i = 0; i < 3; i++) {\n\t\t\tvec4_set(&r, rand_float(true) * 100.0f, rand_float(true) * 100.0f,\n\t\t\t\t rand_float(true) * 50000.0f + 10000.0f, 0.0f);\n\t\t\tgs_effect_set_vec4(randomvals[i], &r);\n\t\t}\n\n\t\twhile (gs_effect_loop(solid, \"Random\"))\n\t\t\tgs_draw_sprite(nullptr, 0, cx, cy);\n\t}\n\npublic:\n\tinline TestMode()\n\t{\n\t\tobs_get_video_info(&ovi);\n\t\tobs_add_main_render_callback(render_rand, this);\n\n\t\tfor (uint32_t i = 0; i < 6; i++) {\n\t\t\tsource[i] = obs_get_output_source(i);\n\t\t\tobs_source_release(source[i]);\n\t\t\tobs_set_output_source(i, nullptr);\n\t\t}\n\t}\n\n\tinline ~TestMode()\n\t{\n\t\tfor (uint32_t i = 0; i < 6; i++)\n\t\t\tobs_set_output_source(i, source[i]);\n\n\t\tobs_remove_main_render_callback(render_rand, this);\n\t\tobs_reset_video(&ovi);\n\t}\n\n\tinline void SetVideo(int cx, int cy, int fps_num, int fps_den)\n\t{\n\t\tobs_video_info newOVI = ovi;\n\n\t\tnewOVI.output_width = (uint32_t)cx;\n\t\tnewOVI.output_height = (uint32_t)cy;\n\t\tnewOVI.fps_num = (uint32_t)fps_num;\n\t\tnewOVI.fps_den = (uint32_t)fps_den;\n\n\t\tobs_reset_video(&newOVI);\n\t}\n};\n"
  },
  {
    "path": "libobs/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.28...3.30)\n\ninclude(cmake/obs-version.cmake)\n\nif(OS_WINDOWS AND NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME)\n  include(cmake/os-windows.cmake)\n  return()\nendif()\n\nfind_package(SIMDe REQUIRED)\nfind_package(Threads REQUIRED)\n\nfind_package(FFmpeg 6.1 REQUIRED avformat avutil swscale swresample OPTIONAL_COMPONENTS avcodec)\n\nfind_package(ZLIB REQUIRED)\nfind_package(Uthash REQUIRED)\n\nfind_package(jansson REQUIRED)\nif(NOT TARGET OBS::caption)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/deps/libcaption\" \"${CMAKE_BINARY_DIR}/deps/libcaption\")\nendif()\n\nadd_library(libobs SHARED)\nadd_library(OBS::libobs ALIAS libobs)\n\ntarget_sources(\n  libobs\n  PRIVATE\n    $<$<BOOL:${ENABLE_HEVC}>:obs-hevc.c>\n    $<$<BOOL:${ENABLE_HEVC}>:obs-hevc.h>\n    obs-audio-controls.c\n    obs-audio-controls.h\n    obs-audio.c\n    obs-av1.c\n    obs-av1.h\n    obs-avc.c\n    obs-avc.h\n    obs-canvas.c\n    obs-config.h\n    obs-data.c\n    obs-data.h\n    obs-defs.h\n    obs-display.c\n    obs-encoder.c\n    obs-encoder.h\n    obs-ffmpeg-compat.h\n    obs-hotkey-name-map.c\n    obs-hotkey.c\n    obs-hotkey.h\n    obs-hotkeys.h\n    obs-interaction.h\n    obs-internal.h\n    obs-missing-files.c\n    obs-missing-files.h\n    obs-module.c\n    obs-module.h\n    obs-nal.c\n    obs-nal.h\n    obs-output-delay.c\n    obs-output.c\n    obs-output.h\n    obs-properties.c\n    obs-properties.h\n    obs-scene.c\n    obs-scene.h\n    obs-service.c\n    obs-service.h\n    obs-source-deinterlace.c\n    obs-source-transition.c\n    obs-source.c\n    obs-source.h\n    obs-video-gpu-encode.c\n    obs-video.c\n    obs-view.c\n    obs.c\n    obs.h\n    obs.hpp\n)\n\ntarget_sources(\n  libobs\n  PRIVATE\n    util/array-serializer.c\n    util/array-serializer.h\n    util/base.c\n    util/base.h\n    util/bitstream.c\n    util/bitstream.h\n    util/bmem.c\n    util/bmem.h\n    util/buffered-file-serializer.c\n    util/buffered-file-serializer.h\n    util/c99defs.h\n    util/cf-lexer.c\n    util/cf-lexer.h\n    util/cf-parser.c\n    util/cf-parser.h\n    util/config-file.c\n    util/config-file.h\n    util/crc32.c\n    util/crc32.h\n    util/curl/curl-helper.h\n    util/darray.h\n    util/deque.h\n    util/dstr.c\n    util/dstr.h\n    util/file-serializer.c\n    util/file-serializer.h\n    util/lexer.c\n    util/lexer.h\n    util/pipe.c\n    util/pipe.h\n    util/platform.c\n    util/platform.h\n    util/profiler.c\n    util/profiler.h\n    util/profiler.hpp\n    util/serializer.h\n    util/source-profiler.c\n    util/source-profiler.h\n    util/sse-intrin.h\n    util/task.c\n    util/task.h\n    util/text-lookup.c\n    util/text-lookup.h\n    util/threading.h\n    util/utf8.c\n    util/utf8.h\n    util/uthash.h\n    util/util.hpp\n    util/util_uint128.h\n    util/util_uint64.h\n)\n\ntarget_sources(\n  libobs\n  PRIVATE\n    callback/calldata.c\n    callback/calldata.h\n    callback/decl.c\n    callback/decl.h\n    callback/proc.c\n    callback/proc.h\n    callback/signal.c\n    callback/signal.h\n)\n\ntarget_sources(\n  libobs\n  PRIVATE\n    media-io/audio-io.c\n    media-io/audio-io.h\n    media-io/audio-math.h\n    media-io/audio-resampler-ffmpeg.c\n    media-io/audio-resampler.h\n    media-io/format-conversion.c\n    media-io/format-conversion.h\n    media-io/frame-rate.h\n    media-io/media-io-defs.h\n    media-io/media-remux.c\n    media-io/media-remux.h\n    media-io/video-fourcc.c\n    media-io/video-frame.c\n    media-io/video-frame.h\n    media-io/video-io.c\n    media-io/video-io.h\n    media-io/video-matrices.c\n    media-io/video-scaler-ffmpeg.c\n    media-io/video-scaler.h\n)\n\ntarget_sources(\n  libobs\n  PRIVATE\n    graphics/axisang.c\n    graphics/axisang.h\n    graphics/bounds.c\n    graphics/bounds.h\n    graphics/device-exports.h\n    graphics/effect-parser.c\n    graphics/effect-parser.h\n    graphics/effect.c\n    graphics/effect.h\n    graphics/graphics-ffmpeg.c\n    graphics/graphics-imports.c\n    graphics/graphics-internal.h\n    graphics/graphics.c\n    graphics/graphics.h\n    graphics/half.h\n    graphics/image-file.c\n    graphics/image-file.h\n    graphics/input.h\n    graphics/libnsgif/libnsgif.c\n    graphics/libnsgif/libnsgif.h\n    graphics/math-defs.h\n    graphics/math-extra.c\n    graphics/math-extra.h\n    graphics/matrix3.c\n    graphics/matrix3.h\n    graphics/matrix4.c\n    graphics/matrix4.h\n    graphics/plane.c\n    graphics/plane.h\n    graphics/quat.c\n    graphics/quat.h\n    graphics/shader-parser.c\n    graphics/shader-parser.h\n    graphics/srgb.h\n    graphics/texture-render.c\n    graphics/vec2.c\n    graphics/vec2.h\n    graphics/vec3.c\n    graphics/vec3.h\n    graphics/vec4.c\n    graphics/vec4.h\n)\n\n# Temporarily disables deprecation warnings while obs_data_autoselect_* is deprecated.\nif(CMAKE_C_COMPILER_ID STREQUAL \"MSVC\")\n  set_source_files_properties(obs-data.c PROPERTIES COMPILE_OPTIONS \"/wd4996\")\nelseif(\n  CMAKE_C_COMPILER_ID STREQUAL \"GNU\"\n  OR CMAKE_C_COMPILER_ID STREQUAL \"Clang\"\n  OR CMAKE_C_COMPILER_ID STREQUAL \"AppleClang\"\n)\n  set_source_files_properties(obs-data.c PROPERTIES COMPILE_OPTIONS \"-Wno-deprecated-declarations\")\nendif()\n\ntarget_compile_features(libobs PUBLIC cxx_std_17)\n\ntarget_compile_definitions(\n  libobs\n  PRIVATE IS_LIBOBS\n  PUBLIC\n    $<BUILD_INTERFACE:$<$<BOOL:${ENABLE_HEVC}>:ENABLE_HEVC>>\n    $<BUILD_INTERFACE:$<$<BOOL:${ENABLE_FFMPEG_MUX_DEBUG}>:SHOW_SUBPROCESSES>>\n)\n\ntarget_link_libraries(\n  libobs\n  PRIVATE\n    OBS::caption\n    OBS::libobs-version\n    FFmpeg::avcodec\n    FFmpeg::avformat\n    FFmpeg::avutil\n    FFmpeg::swscale\n    FFmpeg::swresample\n    jansson::jansson\n    Uthash::Uthash\n    ZLIB::ZLIB\n  PUBLIC SIMDe::SIMDe Threads::Threads\n)\n\nif(OS_WINDOWS)\n  include(cmake/os-windows.cmake)\nelseif(OS_MACOS)\n  include(cmake/os-macos.cmake)\nelseif(OS_LINUX)\n  include(cmake/os-linux.cmake)\nelseif(OS_FREEBSD OR OS_OPENBSD)\n  include(cmake/os-freebsd.cmake)\nendif()\n\nconfigure_file(obsconfig.h.in \"${CMAKE_BINARY_DIR}/config/obsconfig.h\" @ONLY)\n\ntarget_include_directories(\n  libobs\n  PUBLIC \"$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/config>\" \"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\"\n)\n\nset(\n  public_headers\n  callback/calldata.h\n  callback/decl.h\n  callback/proc.h\n  callback/signal.h\n  graphics/axisang.h\n  graphics/bounds.h\n  graphics/effect-parser.h\n  graphics/effect.h\n  graphics/graphics.h\n  graphics/image-file.h\n  graphics/input.h\n  graphics/libnsgif/libnsgif.h\n  graphics/math-defs.h\n  graphics/math-extra.h\n  graphics/matrix3.h\n  graphics/matrix4.h\n  graphics/plane.h\n  graphics/quat.h\n  graphics/shader-parser.h\n  graphics/srgb.h\n  graphics/vec2.h\n  graphics/vec3.h\n  graphics/vec4.h\n  media-io/audio-io.h\n  media-io/audio-math.h\n  media-io/audio-resampler.h\n  media-io/format-conversion.h\n  media-io/frame-rate.h\n  media-io/media-io-defs.h\n  media-io/media-remux.h\n  media-io/video-frame.h\n  media-io/video-io.h\n  media-io/video-scaler.h\n  obs-audio-controls.h\n  obs-avc.h\n  obs-config.h\n  obs-data.h\n  obs-defs.h\n  obs-encoder.h\n  obs-hotkey.h\n  obs-hotkeys.h\n  obs-interaction.h\n  obs-missing-files.h\n  obs-module.h\n  obs-nal.h\n  obs-nix-platform.h\n  obs-output.h\n  obs-properties.h\n  obs-service.h\n  obs-source.h\n  obs.h\n  obs.hpp\n  util/array-serializer.h\n  util/base.h\n  util/bitstream.h\n  util/bmem.h\n  util/c99defs.h\n  util/cf-lexer.h\n  util/cf-parser.h\n  util/config-file.h\n  util/crc32.h\n  util/darray.h\n  util/deque.h\n  util/dstr.h\n  util/dstr.hpp\n  util/file-serializer.h\n  util/lexer.h\n  util/pipe.h\n  util/platform.h\n  util/profiler.h\n  util/profiler.hpp\n  util/serializer.h\n  util/sse-intrin.h\n  util/task.h\n  util/text-lookup.h\n  util/threading-posix.h\n  util/threading.h\n  util/uthash.h\n  util/util.hpp\n  util/util_uint128.h\n  util/util_uint64.h\n)\n\nif(OS_WINDOWS)\n  list(\n    APPEND public_headers\n    util/threading-windows.h\n    util/windows/ComPtr.hpp\n    util/windows/CoTaskMemPtr.hpp\n    util/windows/device-enum.h\n    util/windows/HRError.hpp\n    util/windows/win-registry.h\n    util/windows/win-version.h\n    util/windows/window-helpers.h\n    util/windows/WinHandle.hpp\n  )\nelseif(OS_MACOS)\n  list(APPEND public_headers util/apple/cfstring-utils.h)\nendif()\n\nif(ENABLE_HEVC)\n  list(APPEND public_headers obs-hevc.h)\nendif()\n\nset_property(TARGET libobs APPEND PROPERTY OBS_PUBLIC_HEADERS ${public_headers})\n\nset_target_properties_obs(\n  libobs\n  PROPERTIES FOLDER core\n             VERSION 0\n             SOVERSION \"${OBS_VERSION_MAJOR}\"\n)\n\ntarget_export(libobs)\n"
  },
  {
    "path": "libobs/audio-monitoring/null/null-audio-monitoring.c",
    "content": "#include <obs-internal.h>\n\nbool obs_audio_monitoring_available(void)\n{\n\treturn false;\n}\n\nvoid obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)\n{\n\tUNUSED_PARAMETER(cb);\n\tUNUSED_PARAMETER(data);\n}\n\nstruct audio_monitor *audio_monitor_create(obs_source_t *source)\n{\n\tUNUSED_PARAMETER(source);\n\treturn NULL;\n}\n\nvoid audio_monitor_reset(struct audio_monitor *monitor)\n{\n\tUNUSED_PARAMETER(monitor);\n}\n\nvoid audio_monitor_destroy(struct audio_monitor *monitor)\n{\n\tUNUSED_PARAMETER(monitor);\n}\n\nbool devices_match(const char *id1, const char *id2)\n{\n\tUNUSED_PARAMETER(id1);\n\tUNUSED_PARAMETER(id2);\n\treturn false;\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/osx/coreaudio-enum-devices.c",
    "content": "#include <CoreFoundation/CFString.h>\n#include <CoreAudio/CoreAudio.h>\n\n#include \"../../obs-internal.h\"\n#include \"../../util/dstr.h\"\n#include \"../../util/apple/cfstring-utils.h\"\n\n#include \"mac-helpers.h\"\n\nstatic bool obs_enum_audio_monitoring_device(obs_enum_audio_device_cb cb, void *data, AudioDeviceID id,\n\t\t\t\t\t     bool allow_inputs)\n{\n\tUInt32 size = 0;\n\tCFStringRef cf_name = NULL;\n\tCFStringRef cf_uid = NULL;\n\tchar *name = NULL;\n\tchar *uid = NULL;\n\tOSStatus stat;\n\tbool cont = true;\n\n\tAudioObjectPropertyAddress addr = {kAudioDevicePropertyStreams, kAudioDevicePropertyScopeOutput,\n\t\t\t\t\t   kAudioObjectPropertyElementMain};\n\n\t/* Check if the device is capable of audio output. */\n\tAudioObjectGetPropertyDataSize(id, &addr, 0, NULL, &size);\n\tif (!allow_inputs && !size)\n\t\treturn true;\n\n\tsize = sizeof(CFStringRef);\n\n\taddr.mSelector = kAudioDevicePropertyDeviceUID;\n\tstat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_uid);\n\tif (!success(stat, \"get audio device UID\"))\n\t\tgoto fail;\n\n\taddr.mSelector = kAudioDevicePropertyDeviceNameCFString;\n\tstat = AudioObjectGetPropertyData(id, &addr, 0, NULL, &size, &cf_name);\n\tif (!success(stat, \"get audio device name\"))\n\t\tgoto fail;\n\n\tname = cfstr_copy_cstr(cf_name, kCFStringEncodingUTF8);\n\tif (!name) {\n\t\tblog(LOG_WARNING, \"%s: failed to convert name\", __FUNCTION__);\n\t\tgoto fail;\n\t}\n\n\tuid = cfstr_copy_cstr(cf_uid, kCFStringEncodingUTF8);\n\tif (!uid) {\n\t\tblog(LOG_WARNING, \"%s: failed to convert uid\", __FUNCTION__);\n\t\tgoto fail;\n\t}\n\n\tcont = cb(data, name, uid);\n\nfail:\n\tbfree(name);\n\tbfree(uid);\n\tif (cf_name)\n\t\tCFRelease(cf_name);\n\tif (cf_uid)\n\t\tCFRelease(cf_uid);\n\treturn cont;\n}\n\nstatic void enum_audio_devices(obs_enum_audio_device_cb cb, void *data, bool allow_inputs)\n{\n\tAudioObjectPropertyAddress addr = {kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,\n\t\t\t\t\t   kAudioObjectPropertyElementMain};\n\n\tUInt32 size = 0;\n\tUInt32 count;\n\tOSStatus stat;\n\tAudioDeviceID *ids;\n\n\tstat = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &size);\n\tif (!success(stat, \"get data size\"))\n\t\treturn;\n\n\tids = malloc(size);\n\tcount = size / sizeof(AudioDeviceID);\n\n\tstat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, ids);\n\tif (success(stat, \"get data\")) {\n\t\tfor (UInt32 i = 0; i < count; i++) {\n\t\t\tif (!obs_enum_audio_monitoring_device(cb, data, ids[i], allow_inputs))\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tfree(ids);\n}\n\nvoid obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)\n{\n\tenum_audio_devices(cb, data, false);\n}\n\nstatic bool alloc_default_id(void *data, const char *name, const char *id)\n{\n\tchar **p_id = data;\n\tUNUSED_PARAMETER(name);\n\n\t*p_id = bstrdup(id);\n\treturn false;\n}\n\nstatic void get_default_id(char **p_id)\n{\n\tAudioObjectPropertyAddress addr = {kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,\n\t\t\t\t\t   kAudioObjectPropertyElementMain};\n\n\tif (*p_id)\n\t\treturn;\n\n\tOSStatus stat;\n\tAudioDeviceID id = 0;\n\tUInt32 size = sizeof(id);\n\n\tstat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, &id);\n\tif (success(stat, \"AudioObjectGetPropertyData\"))\n\t\tobs_enum_audio_monitoring_device(alloc_default_id, p_id, id, true);\n\tif (!*p_id)\n\t\t*p_id = bzalloc(1);\n}\n\nstruct device_name_info {\n\tconst char *id;\n\tchar *name;\n};\n\nstatic bool enum_device_name(void *data, const char *name, const char *id)\n{\n\tstruct device_name_info *info = data;\n\n\tif (strcmp(info->id, id) == 0) {\n\t\tinfo->name = bstrdup(name);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool devices_match(const char *id1, const char *id2)\n{\n\tstruct device_name_info info = {0};\n\tchar *default_id = NULL;\n\tchar *name1 = NULL;\n\tchar *name2 = NULL;\n\tbool match;\n\n\tif (!id1 || !id2)\n\t\treturn false;\n\n\tif (strcmp(id1, \"default\") == 0) {\n\t\tget_default_id(&default_id);\n\t\tid1 = default_id;\n\t}\n\tif (strcmp(id2, \"default\") == 0) {\n\t\tget_default_id(&default_id);\n\t\tid2 = default_id;\n\t}\n\n\tinfo.id = id1;\n\tenum_audio_devices(enum_device_name, &info, true);\n\tname1 = info.name;\n\n\tinfo.name = NULL;\n\tinfo.id = id2;\n\tenum_audio_devices(enum_device_name, &info, true);\n\tname2 = info.name;\n\n\tmatch = name1 && name2 && strcmp(name1, name2) == 0;\n\tbfree(default_id);\n\tbfree(name1);\n\tbfree(name2);\n\n\treturn match;\n}\n\nstatic inline bool device_is_input(const char *device)\n{\n\treturn astrstri(device, \"soundflower\") == NULL && astrstri(device, \"wavtap\") == NULL &&\n\t       astrstri(device, \"soundsiphon\") == NULL && astrstri(device, \"ishowu\") == NULL &&\n\t       astrstri(device, \"blackhole\") == NULL && astrstri(device, \"loopback\") == NULL &&\n\t       astrstri(device, \"groundcontrol\") == NULL && astrstri(device, \"vbcable\") == NULL;\n}\n\nstatic bool find_loopback_cb(void *param, const char *name, const char *id)\n{\n\tUNUSED_PARAMETER(name);\n\tchar **p_id = param;\n\n\tif (!device_is_input(id)) {\n\t\t*p_id = bstrdup(id);\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nvoid get_desktop_default_id(char **p_id)\n{\n\tif (*p_id)\n\t\treturn;\n\n\tAudioObjectPropertyAddress addr = {kAudioHardwarePropertyDefaultSystemOutputDevice,\n\t\t\t\t\t   kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMain};\n\n\tAudioDeviceID id = 0;\n\tUInt32 size = sizeof(id);\n\tOSStatus stat = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, &id);\n\n\tif (success(stat, \"AudioObjectGetPropertyData\")) {\n\t\t/* Try system default output first */\n\t\tobs_enum_audio_monitoring_device(alloc_default_id, p_id, id, false);\n\n\t\t/* If not a loopback, try to find a virtual (non-input) device instead */\n\t\tif (*p_id && device_is_input(*p_id)) {\n\t\t\tbfree(*p_id);\n\t\t\t*p_id = NULL;\n\t\t\tenum_audio_devices(find_loopback_cb, p_id, false);\n\t\t}\n\t}\n\n\tif (!*p_id)\n\t\t*p_id = bzalloc(1);\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/osx/coreaudio-monitoring-available.c",
    "content": "#include \"../../obs-internal.h\"\n\nbool obs_audio_monitoring_available(void)\n{\n\treturn true;\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/osx/coreaudio-output.c",
    "content": "#include <AudioUnit/AudioUnit.h>\n#include <AudioToolbox/AudioQueue.h>\n#include <CoreFoundation/CFString.h>\n#include <CoreAudio/CoreAudio.h>\n\n#include \"../../media-io/audio-resampler.h\"\n#include \"../../util/deque.h\"\n#include \"../../util/threading.h\"\n#include \"../../util/platform.h\"\n#include \"../../obs-internal.h\"\n#include \"../../util/darray.h\"\n\n#include \"mac-helpers.h\"\n\nstruct audio_monitor {\n\tobs_source_t *source;\n\tAudioQueueRef queue;\n\tAudioQueueBufferRef buffers[3];\n\n\tpthread_mutex_t mutex;\n\tstruct deque empty_buffers;\n\tstruct deque new_data;\n\taudio_resampler_t *resampler;\n\tsize_t buffer_size;\n\tsize_t wait_size;\n\tuint32_t channels;\n\n\tvolatile bool active;\n\tbool paused;\n\tbool ignore;\n};\n\nstatic inline bool fill_buffer(struct audio_monitor *monitor)\n{\n\tAudioQueueBufferRef buf;\n\tOSStatus stat;\n\n\tif (monitor->new_data.size < monitor->buffer_size) {\n\t\treturn false;\n\t}\n\n\tdeque_pop_front(&monitor->empty_buffers, &buf, sizeof(buf));\n\tdeque_pop_front(&monitor->new_data, buf->mAudioData, monitor->buffer_size);\n\n\tbuf->mAudioDataByteSize = (UInt32)monitor->buffer_size;\n\n\tstat = AudioQueueEnqueueBuffer(monitor->queue, buf, 0, NULL);\n\tif (!success(stat, \"AudioQueueEnqueueBuffer\")) {\n\t\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, \"Failed to enqueue buffer\");\n\t\tAudioQueueStop(monitor->queue, false);\n\t}\n\treturn true;\n}\n\nstatic void on_audio_pause(void *data, calldata_t *calldata)\n{\n\tUNUSED_PARAMETER(calldata);\n\tstruct audio_monitor *monitor = data;\n\tpthread_mutex_lock(&monitor->mutex);\n\tdeque_free(&monitor->new_data);\n\tpthread_mutex_unlock(&monitor->mutex);\n}\n\nstatic void on_audio_playback(void *param, obs_source_t *source, const struct audio_data *audio_data, bool muted)\n{\n\tstruct audio_monitor *monitor = param;\n\tfloat vol = source->user_volume;\n\tuint32_t bytes;\n\n\tif (!os_atomic_load_bool(&monitor->active)) {\n\t\treturn;\n\t}\n\n\tif (os_atomic_load_long(&source->activate_refs) == 0) {\n\t\treturn;\n\t}\n\n\tuint8_t *resample_data[MAX_AV_PLANES];\n\tuint32_t resample_frames;\n\tuint64_t ts_offset;\n\tbool success;\n\n\tsuccess = audio_resampler_resample(monitor->resampler, resample_data, &resample_frames, &ts_offset,\n\t\t\t\t\t   (const uint8_t *const *)audio_data->data, (uint32_t)audio_data->frames);\n\tif (!success) {\n\t\treturn;\n\t}\n\n\tbytes = sizeof(float) * monitor->channels * resample_frames;\n\n\tif (muted) {\n\t\tmemset(resample_data[0], 0, bytes);\n\t} else {\n\t\t/* apply volume */\n\t\tif (!close_float(vol, 1.0f, EPSILON)) {\n\t\t\tregister float *cur = (float *)resample_data[0];\n\t\t\tregister float *end = cur + resample_frames * monitor->channels;\n\n\t\t\twhile (cur < end)\n\t\t\t\t*(cur++) *= vol;\n\t\t}\n\t}\n\n\tpthread_mutex_lock(&monitor->mutex);\n\tdeque_push_back(&monitor->new_data, resample_data[0], bytes);\n\n\tif (monitor->new_data.size >= monitor->wait_size) {\n\t\tmonitor->wait_size = 0;\n\n\t\twhile (monitor->empty_buffers.size > 0) {\n\t\t\tif (!fill_buffer(monitor)) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (monitor->paused) {\n\t\t\tAudioQueueStart(monitor->queue, NULL);\n\t\t\tmonitor->paused = false;\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&monitor->mutex);\n}\n\nstatic void buffer_audio(void *data, AudioQueueRef aq, AudioQueueBufferRef buf)\n{\n\tstruct audio_monitor *monitor = data;\n\n\tpthread_mutex_lock(&monitor->mutex);\n\tdeque_push_back(&monitor->empty_buffers, &buf, sizeof(buf));\n\twhile (monitor->empty_buffers.size > 0) {\n\t\tif (!fill_buffer(monitor)) {\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (monitor->empty_buffers.size == sizeof(buf) * 3) {\n\t\tmonitor->paused = true;\n\t\tmonitor->wait_size = monitor->buffer_size * 3;\n\t\tAudioQueuePause(monitor->queue);\n\t}\n\tpthread_mutex_unlock(&monitor->mutex);\n\n\tUNUSED_PARAMETER(aq);\n}\n\nextern bool devices_match(const char *id1, const char *id2);\n\nstatic bool audio_monitor_init(struct audio_monitor *monitor, obs_source_t *source)\n{\n\tconst struct audio_output_info *info = audio_output_get_info(obs->audio.audio);\n\tuint32_t channels = get_audio_channels(info->speakers);\n\tOSStatus stat;\n\n\tAudioStreamBasicDescription desc = {.mSampleRate = (Float64)info->samples_per_sec,\n\t\t\t\t\t    .mFormatID = kAudioFormatLinearPCM,\n\t\t\t\t\t    .mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked,\n\t\t\t\t\t    .mBytesPerPacket = sizeof(float) * channels,\n\t\t\t\t\t    .mFramesPerPacket = 1,\n\t\t\t\t\t    .mBytesPerFrame = sizeof(float) * channels,\n\t\t\t\t\t    .mChannelsPerFrame = channels,\n\t\t\t\t\t    .mBitsPerChannel = sizeof(float) * 8};\n\n\tmonitor->source = source;\n\n\tmonitor->channels = channels;\n\tmonitor->buffer_size = channels * sizeof(float) * info->samples_per_sec / 100 * 3;\n\tmonitor->wait_size = monitor->buffer_size * 3;\n\n\tpthread_mutex_init_value(&monitor->mutex);\n\n\tconst char *uid = obs->audio.monitoring_device_id;\n\tif (!uid || !*uid) {\n\t\treturn false;\n\t}\n\n\tif (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) {\n\t\tobs_data_t *s = obs_source_get_settings(source);\n\t\tconst char *s_dev_id = obs_data_get_string(s, \"device_id\");\n\t\tbool match = devices_match(s_dev_id, uid);\n\t\tobs_data_release(s);\n\n\t\tif (match) {\n\t\t\tmonitor->ignore = true;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tstat = AudioQueueNewOutput(&desc, buffer_audio, monitor, NULL, NULL, 0, &monitor->queue);\n\tif (!success(stat, \"AudioStreamBasicDescription\")) {\n\t\treturn false;\n\t}\n\n\tif (strcmp(uid, \"default\") != 0) {\n\t\tCFStringRef cf_uid =\n\t\t\tCFStringCreateWithBytes(NULL, (const UInt8 *)uid, strlen(uid), kCFStringEncodingUTF8, false);\n\n\t\tstat = AudioQueueSetProperty(monitor->queue, kAudioQueueProperty_CurrentDevice, &cf_uid,\n\t\t\t\t\t     sizeof(cf_uid));\n\t\tCFRelease(cf_uid);\n\n\t\tif (!success(stat, \"set current device\")) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tstat = AudioQueueSetParameter(monitor->queue, kAudioQueueParam_Volume, 1.0);\n\tif (!success(stat, \"set volume\")) {\n\t\treturn false;\n\t}\n\n\tfor (size_t i = 0; i < 3; i++) {\n\t\tstat = AudioQueueAllocateBuffer(monitor->queue, (UInt32)monitor->buffer_size, &monitor->buffers[i]);\n\t\tif (!success(stat, \"allocation of buffer\")) {\n\t\t\treturn false;\n\t\t}\n\n\t\tdeque_push_back(&monitor->empty_buffers, &monitor->buffers[i], sizeof(monitor->buffers[i]));\n\t}\n\n\tif (pthread_mutex_init(&monitor->mutex, NULL) != 0) {\n\t\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, \"Failed to init mutex\");\n\t\treturn false;\n\t}\n\n\tstruct resample_info from = {.samples_per_sec = info->samples_per_sec,\n\t\t\t\t     .speakers = info->speakers,\n\t\t\t\t     .format = AUDIO_FORMAT_FLOAT_PLANAR};\n\tstruct resample_info to = {.samples_per_sec = info->samples_per_sec,\n\t\t\t\t   .speakers = info->speakers,\n\t\t\t\t   .format = AUDIO_FORMAT_FLOAT};\n\n\tmonitor->resampler = audio_resampler_create(&to, &from);\n\tif (!monitor->resampler) {\n\t\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, \"Failed to create resampler\");\n\t\treturn false;\n\t}\n\n\tstat = AudioQueueStart(monitor->queue, NULL);\n\tif (!success(stat, \"start\")) {\n\t\treturn false;\n\t}\n\n\tmonitor->active = true;\n\treturn true;\n}\n\nstatic void audio_monitor_free(struct audio_monitor *monitor)\n{\n\tif (monitor->source) {\n\t\tobs_source_remove_audio_capture_callback(monitor->source, on_audio_playback, monitor);\n\t\tobs_source_remove_audio_pause_callback(monitor->source, on_audio_pause, monitor);\n\t}\n\tif (monitor->active) {\n\t\tAudioQueueStop(monitor->queue, true);\n\t}\n\tfor (size_t i = 0; i < 3; i++) {\n\t\tif (monitor->buffers[i]) {\n\t\t\tAudioQueueFreeBuffer(monitor->queue, monitor->buffers[i]);\n\t\t}\n\t}\n\tif (monitor->queue) {\n\t\tAudioQueueDispose(monitor->queue, true);\n\t}\n\n\taudio_resampler_destroy(monitor->resampler);\n\tdeque_free(&monitor->empty_buffers);\n\tdeque_free(&monitor->new_data);\n\tpthread_mutex_destroy(&monitor->mutex);\n}\n\nstatic void audio_monitor_init_final(struct audio_monitor *monitor)\n{\n\tif (monitor->ignore)\n\t\treturn;\n\n\tobs_source_add_audio_capture_callback(monitor->source, on_audio_playback, monitor);\n\tobs_source_add_audio_pause_callback(monitor->source, on_audio_pause, monitor);\n}\n\nstruct audio_monitor *audio_monitor_create(obs_source_t *source)\n{\n\tstruct audio_monitor *monitor = bzalloc(sizeof(*monitor));\n\n\tif (!audio_monitor_init(monitor, source)) {\n\t\tgoto fail;\n\t}\n\n\tpthread_mutex_lock(&obs->audio.monitoring_mutex);\n\tda_push_back(obs->audio.monitors, &monitor);\n\tpthread_mutex_unlock(&obs->audio.monitoring_mutex);\n\n\taudio_monitor_init_final(monitor);\n\treturn monitor;\n\nfail:\n\taudio_monitor_free(monitor);\n\tbfree(monitor);\n\treturn NULL;\n}\n\nvoid audio_monitor_reset(struct audio_monitor *monitor)\n{\n\tbool success;\n\n\tobs_source_t *source = monitor->source;\n\taudio_monitor_free(monitor);\n\tmemset(monitor, 0, sizeof(*monitor));\n\n\tsuccess = audio_monitor_init(monitor, source);\n\tif (success)\n\t\taudio_monitor_init_final(monitor);\n}\n\nvoid audio_monitor_destroy(struct audio_monitor *monitor)\n{\n\tif (monitor) {\n\t\taudio_monitor_free(monitor);\n\n\t\tpthread_mutex_lock(&obs->audio.monitoring_mutex);\n\t\tda_erase_item(obs->audio.monitors, &monitor);\n\t\tpthread_mutex_unlock(&obs->audio.monitoring_mutex);\n\n\t\tbfree(monitor);\n\t}\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/osx/mac-helpers.h",
    "content": "#pragma once\n\nstatic bool success_(OSStatus stat, const char *func, const char *call)\n{\n\tif (stat != noErr) {\n\t\tblog(LOG_WARNING, \"%s: %s failed: %d\", func, call, (int)stat);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n#define success(stat, call) success_(stat, __FUNCTION__, call)\n"
  },
  {
    "path": "libobs/audio-monitoring/pulse/pulseaudio-enum-devices.c",
    "content": "#include <obs-internal.h>\n#include \"pulseaudio-wrapper.h\"\n\nstatic void pulseaudio_output_info(pa_context *c, const pa_sink_info *i, int eol, void *userdata)\n{\n\tUNUSED_PARAMETER(c);\n\tif (eol != 0)\n\t\tgoto skip;\n\n\tstruct enum_cb *ecb = (struct enum_cb *)userdata;\n\tif (ecb->cont)\n\t\tecb->cont = ecb->cb(ecb->data, i->description, i->name);\n\nskip:\n\tpulseaudio_signal(0);\n}\n\nvoid obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)\n{\n\tstruct enum_cb *ecb = bzalloc(sizeof(struct enum_cb));\n\tecb->cb = cb;\n\tecb->data = data;\n\tecb->cont = 1;\n\n\tpulseaudio_init();\n\tpa_sink_info_cb_t pa_cb = pulseaudio_output_info;\n\tpulseaudio_get_sink_info_list(pa_cb, (void *)ecb);\n\tpulseaudio_unref();\n\n\tbfree(ecb);\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/pulse/pulseaudio-monitoring-available.c",
    "content": "#include <obs-internal.h>\n\nbool obs_audio_monitoring_available(void)\n{\n\treturn true;\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/pulse/pulseaudio-output.c",
    "content": "#include \"obs-internal.h\"\n#include \"pulseaudio-wrapper.h\"\n\n#define PULSE_DATA(voidptr) struct audio_monitor *data = voidptr;\n#define blog(level, msg, ...) blog(level, \"pulse-am: \" msg, ##__VA_ARGS__)\n\nstruct audio_monitor {\n\tobs_source_t *source;\n\tpa_stream *stream;\n\tchar *device;\n\tpa_buffer_attr attr;\n\tenum speaker_layout speakers;\n\tpa_sample_format_t format;\n\tuint_fast32_t samples_per_sec;\n\tuint_fast32_t bytes_per_frame;\n\tuint_fast8_t channels;\n\n\tuint_fast32_t packets;\n\tuint_fast64_t frames;\n\n\tstruct deque new_data;\n\taudio_resampler_t *resampler;\n\n\tbool ignore;\n\tpthread_mutex_t playback_mutex;\n};\n\nstatic enum speaker_layout pulseaudio_channels_to_obs_speakers(uint_fast32_t channels)\n{\n\tswitch (channels) {\n\tcase 0:\n\t\treturn SPEAKERS_UNKNOWN;\n\tcase 1:\n\t\treturn SPEAKERS_MONO;\n\tcase 2:\n\t\treturn SPEAKERS_STEREO;\n\tcase 3:\n\t\treturn SPEAKERS_2POINT1;\n\tcase 4:\n\t\treturn SPEAKERS_4POINT0;\n\tcase 5:\n\t\treturn SPEAKERS_4POINT1;\n\tcase 6:\n\t\treturn SPEAKERS_5POINT1;\n\tcase 8:\n\t\treturn SPEAKERS_7POINT1;\n\tdefault:\n\t\treturn SPEAKERS_UNKNOWN;\n\t}\n}\n\nstatic enum audio_format pulseaudio_to_obs_audio_format(pa_sample_format_t format)\n{\n\tswitch (format) {\n\tcase PA_SAMPLE_U8:\n\t\treturn AUDIO_FORMAT_U8BIT;\n\tcase PA_SAMPLE_S16LE:\n\t\treturn AUDIO_FORMAT_16BIT;\n\tcase PA_SAMPLE_S32LE:\n\t\treturn AUDIO_FORMAT_32BIT;\n\tcase PA_SAMPLE_FLOAT32LE:\n\t\treturn AUDIO_FORMAT_FLOAT;\n\tdefault:\n\t\treturn AUDIO_FORMAT_UNKNOWN;\n\t}\n}\n\nstatic pa_channel_map pulseaudio_channel_map(enum speaker_layout layout)\n{\n\tpa_channel_map ret;\n\n\tret.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;\n\tret.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;\n\tret.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;\n\tret.map[3] = PA_CHANNEL_POSITION_LFE;\n\tret.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;\n\tret.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;\n\tret.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;\n\tret.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;\n\n\tswitch (layout) {\n\tcase SPEAKERS_MONO:\n\t\tret.channels = 1;\n\t\tret.map[0] = PA_CHANNEL_POSITION_MONO;\n\t\tbreak;\n\n\tcase SPEAKERS_STEREO:\n\t\tret.channels = 2;\n\t\tbreak;\n\n\tcase SPEAKERS_2POINT1:\n\t\tret.channels = 3;\n\t\tret.map[2] = PA_CHANNEL_POSITION_LFE;\n\t\tbreak;\n\n\tcase SPEAKERS_4POINT0:\n\t\tret.channels = 4;\n\t\tret.map[3] = PA_CHANNEL_POSITION_REAR_CENTER;\n\t\tbreak;\n\n\tcase SPEAKERS_4POINT1:\n\t\tret.channels = 5;\n\t\tret.map[4] = PA_CHANNEL_POSITION_REAR_CENTER;\n\t\tbreak;\n\n\tcase SPEAKERS_5POINT1:\n\t\tret.channels = 6;\n\t\tbreak;\n\n\tcase SPEAKERS_7POINT1:\n\t\tret.channels = 8;\n\t\tbreak;\n\n\tcase SPEAKERS_UNKNOWN:\n\tdefault:\n\t\tret.channels = 0;\n\t\tbreak;\n\t}\n\n\treturn ret;\n}\n\nstatic void process_byte(void *p, size_t frames, size_t channels, float vol)\n{\n\tregister uint8_t *cur = (uint8_t *)p;\n\tregister uint8_t *end = cur + frames * channels;\n\n\tfor (; cur < end; cur++)\n\t\t*cur = ((int)*cur - 128) * vol + 128;\n}\n\nstatic void process_s16(void *p, size_t frames, size_t channels, float vol)\n{\n\tregister int16_t *cur = (int16_t *)p;\n\tregister int16_t *end = cur + frames * channels;\n\n\twhile (cur < end)\n\t\t*(cur++) *= vol;\n}\n\nstatic void process_s32(void *p, size_t frames, size_t channels, float vol)\n{\n\tregister int32_t *cur = (int32_t *)p;\n\tregister int32_t *end = cur + frames * channels;\n\n\twhile (cur < end)\n\t\t*(cur++) *= vol;\n}\n\nstatic void process_float(void *p, size_t frames, size_t channels, float vol)\n{\n\tregister float *cur = (float *)p;\n\tregister float *end = cur + frames * channels;\n\n\twhile (cur < end)\n\t\t*(cur++) *= vol;\n}\n\nvoid process_volume(const struct audio_monitor *monitor, float vol, uint8_t *const *resample_data,\n\t\t    uint32_t resample_frames)\n{\n\tswitch (monitor->format) {\n\tcase PA_SAMPLE_U8:\n\t\tprocess_byte(resample_data[0], resample_frames, monitor->channels, vol);\n\t\tbreak;\n\tcase PA_SAMPLE_S16LE:\n\t\tprocess_s16(resample_data[0], resample_frames, monitor->channels, vol);\n\t\tbreak;\n\tcase PA_SAMPLE_S32LE:\n\t\tprocess_s32(resample_data[0], resample_frames, monitor->channels, vol);\n\t\tbreak;\n\tcase PA_SAMPLE_FLOAT32LE:\n\t\tprocess_float(resample_data[0], resample_frames, monitor->channels, vol);\n\t\tbreak;\n\tdefault:\n\t\t// just ignore\n\t\tbreak;\n\t}\n}\n\nstatic void do_stream_write(void *param)\n{\n\tPULSE_DATA(param);\n\tuint8_t *buffer = NULL;\n\n\tpulseaudio_lock();\n\tpthread_mutex_lock(&data->playback_mutex);\n\n\t// If we have grown a large buffer internally, grow the pulse buffer to match so we can write our data out.\n\tif (data->new_data.size > data->attr.tlength * 2) {\n\t\tdata->attr.fragsize = (uint32_t)-1;\n\t\tdata->attr.maxlength = (uint32_t)-1;\n\t\tdata->attr.prebuf = (uint32_t)-1;\n\t\tdata->attr.minreq = (uint32_t)-1;\n\t\tdata->attr.tlength = data->new_data.size;\n\t\tpa_stream_set_buffer_attr(data->stream, &data->attr, NULL, NULL);\n\t}\n\n\t// Buffer up enough data before we start playing.\n\tif (pa_stream_is_corked(data->stream)) {\n\t\tif (data->new_data.size >= data->attr.tlength) {\n\t\t\tpa_stream_cork(data->stream, 0, NULL, NULL);\n\t\t} else {\n\t\t\tgoto finish;\n\t\t}\n\t}\n\n\twhile (data->new_data.size > 0) {\n\t\tsize_t bytesToFill = data->new_data.size;\n\t\tif (pa_stream_begin_write(data->stream, (void **)&buffer, &bytesToFill))\n\t\t\tgoto finish;\n\n\t\t// PA may request we submit more or less data than we have.\n\t\t// Wait for more data if we cannot perform a full write.\n\t\tif (bytesToFill > data->new_data.size) {\n\t\t\tpa_stream_cancel_write(data->stream);\n\t\t\tgoto finish;\n\t\t}\n\n\t\tdeque_pop_front(&data->new_data, buffer, bytesToFill);\n\n\t\tpa_stream_write(data->stream, buffer, bytesToFill, NULL, 0LL, PA_SEEK_RELATIVE);\n\t}\n\nfinish:\n\tpthread_mutex_unlock(&data->playback_mutex);\n\tpulseaudio_unlock();\n}\n\nstatic void on_audio_playback(void *param, obs_source_t *source, const struct audio_data *audio_data, bool muted)\n{\n\tstruct audio_monitor *monitor = param;\n\tfloat vol = source->user_volume;\n\tsize_t bytes;\n\n\tuint8_t *resample_data[MAX_AV_PLANES];\n\tuint32_t resample_frames;\n\tuint64_t ts_offset;\n\tbool success;\n\n\tif (pthread_mutex_trylock(&monitor->playback_mutex) != 0)\n\t\treturn;\n\n\tif (os_atomic_load_long(&source->activate_refs) == 0)\n\t\tgoto unlock;\n\n\tsuccess = audio_resampler_resample(monitor->resampler, resample_data, &resample_frames, &ts_offset,\n\t\t\t\t\t   (const uint8_t *const *)audio_data->data, (uint32_t)audio_data->frames);\n\n\tif (!success)\n\t\tgoto unlock;\n\n\tbytes = monitor->bytes_per_frame * resample_frames;\n\n\tif (muted) {\n\t\tmemset(resample_data[0], 0, bytes);\n\t} else {\n\t\tif (!close_float(vol, 1.0f, EPSILON)) {\n\t\t\tprocess_volume(monitor, vol, resample_data, resample_frames);\n\t\t}\n\t}\n\n\tdeque_push_back(&monitor->new_data, resample_data[0], bytes);\n\tmonitor->packets++;\n\tmonitor->frames += resample_frames;\n\nunlock:\n\tpthread_mutex_unlock(&monitor->playback_mutex);\n\tdo_stream_write(param);\n}\n\nstatic void pulseaudio_server_info(pa_context *c, const pa_server_info *i, void *userdata)\n{\n\tUNUSED_PARAMETER(c);\n\tUNUSED_PARAMETER(userdata);\n\n\tblog(LOG_INFO, \"Server name: '%s %s'\", i->server_name, i->server_version);\n\n\tpulseaudio_signal(0);\n}\n\nstatic void pulseaudio_sink_info(pa_context *c, const pa_sink_info *i, int eol, void *userdata)\n{\n\tUNUSED_PARAMETER(c);\n\tPULSE_DATA(userdata);\n\t// An error occurred\n\tif (eol < 0) {\n\t\tdata->format = PA_SAMPLE_INVALID;\n\t\tgoto skip;\n\t}\n\t// Terminating call for multi instance callbacks\n\tif (eol > 0)\n\t\tgoto skip;\n\n\tblog(LOG_INFO, \"Audio format: %s, %\" PRIu32 \" Hz, %\" PRIu8 \" channels\",\n\t     pa_sample_format_to_string(i->sample_spec.format), i->sample_spec.rate, i->sample_spec.channels);\n\n\tpa_sample_format_t format = i->sample_spec.format;\n\tif (pulseaudio_to_obs_audio_format(format) == AUDIO_FORMAT_UNKNOWN) {\n\t\tformat = PA_SAMPLE_FLOAT32LE;\n\n\t\tblog(LOG_INFO,\n\t\t     \"Sample format %s not supported by OBS,\"\n\t\t     \"using %s instead for recording\",\n\t\t     pa_sample_format_to_string(i->sample_spec.format), pa_sample_format_to_string(format));\n\t}\n\n\tuint8_t channels = i->sample_spec.channels;\n\tif (pulseaudio_channels_to_obs_speakers(channels) == SPEAKERS_UNKNOWN) {\n\t\tchannels = 2;\n\n\t\tblog(LOG_INFO,\n\t\t     \"%c channels not supported by OBS,\"\n\t\t     \"using %c instead for recording\",\n\t\t     i->sample_spec.channels, channels);\n\t}\n\n\tdata->format = format;\n\tdata->samples_per_sec = i->sample_spec.rate;\n\tdata->channels = channels;\nskip:\n\tpulseaudio_signal(0);\n}\n\nstatic void pulseaudio_stop_playback(struct audio_monitor *monitor)\n{\n\tif (monitor->stream) {\n\t\t/* Stop the stream */\n\t\tpulseaudio_lock();\n\t\tpa_stream_disconnect(monitor->stream);\n\t\tpulseaudio_unlock();\n\n\t\t/* Remove the callbacks, to ensure we no longer try to do anything\n\t\t * with this stream object */\n\t\tpulseaudio_write_callback(monitor->stream, NULL, NULL);\n\n\t\t/* Unreference the stream and drop it. PA will free it when it can. */\n\t\tpulseaudio_lock();\n\t\tpa_stream_unref(monitor->stream);\n\t\tpulseaudio_unlock();\n\t\tmonitor->stream = NULL;\n\t}\n\n\tblog(LOG_INFO, \"Stopped Monitoring in '%s'\", monitor->device);\n\tblog(LOG_INFO, \"Got %\" PRIuFAST32 \" packets with %\" PRIuFAST64 \" frames\", monitor->packets, monitor->frames);\n\n\tmonitor->packets = 0;\n\tmonitor->frames = 0;\n}\n\nstatic bool audio_monitor_init(struct audio_monitor *monitor, obs_source_t *source)\n{\n\tpthread_mutex_init_value(&monitor->playback_mutex);\n\n\tmonitor->source = source;\n\n\tconst char *id = obs->audio.monitoring_device_id;\n\tif (!id)\n\t\treturn false;\n\n\tif (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) {\n\t\tobs_data_t *s = obs_source_get_settings(source);\n\t\tconst char *s_dev_id = obs_data_get_string(s, \"device_id\");\n\t\tbool match = devices_match(s_dev_id, id);\n\t\tobs_data_release(s);\n\n\t\tif (match) {\n\t\t\tmonitor->ignore = true;\n\t\t\tblog(LOG_INFO, \"Prevented feedback-loop in '%s'\", s_dev_id);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tpulseaudio_init();\n\n\tif (strcmp(id, \"default\") == 0)\n\t\tget_default_id(&monitor->device);\n\telse\n\t\tmonitor->device = bstrdup(id);\n\n\tif (!monitor->device)\n\t\treturn false;\n\n\tif (pulseaudio_get_server_info(pulseaudio_server_info, (void *)monitor) < 0) {\n\t\tblog(LOG_ERROR, \"Unable to get server info !\");\n\t\treturn false;\n\t}\n\n\tif (pulseaudio_get_sink_info(pulseaudio_sink_info, monitor->device, (void *)monitor) < 0) {\n\t\tblog(LOG_ERROR, \"Unable to get sink info !\");\n\t\treturn false;\n\t}\n\tif (monitor->format == PA_SAMPLE_INVALID) {\n\t\tblog(LOG_ERROR, \"An error occurred while getting the source info!\");\n\t\treturn false;\n\t}\n\n\tpa_sample_spec spec;\n\tspec.format = monitor->format;\n\tspec.rate = (uint32_t)monitor->samples_per_sec;\n\tspec.channels = monitor->channels;\n\n\tif (!pa_sample_spec_valid(&spec)) {\n\t\tblog(LOG_ERROR, \"Sample spec is not valid\");\n\t\treturn false;\n\t}\n\n\tconst struct audio_output_info *info = audio_output_get_info(obs->audio.audio);\n\n\tstruct resample_info from = {.samples_per_sec = info->samples_per_sec,\n\t\t\t\t     .speakers = info->speakers,\n\t\t\t\t     .format = AUDIO_FORMAT_FLOAT_PLANAR};\n\tstruct resample_info to = {.samples_per_sec = (uint32_t)monitor->samples_per_sec,\n\t\t\t\t   .speakers = pulseaudio_channels_to_obs_speakers(monitor->channels),\n\t\t\t\t   .format = pulseaudio_to_obs_audio_format(monitor->format)};\n\n\tmonitor->resampler = audio_resampler_create(&to, &from);\n\tif (!monitor->resampler) {\n\t\tblog(LOG_WARNING, \"%s: %s\", __FUNCTION__, \"Failed to create resampler\");\n\t\treturn false;\n\t}\n\n\tmonitor->speakers = pulseaudio_channels_to_obs_speakers(spec.channels);\n\tmonitor->bytes_per_frame = pa_frame_size(&spec);\n\n\tpa_channel_map channel_map = pulseaudio_channel_map(monitor->speakers);\n\n\tmonitor->stream = pulseaudio_stream_new(obs_source_get_name(monitor->source), &spec, &channel_map);\n\tif (!monitor->stream) {\n\t\tblog(LOG_ERROR, \"Unable to create stream\");\n\t\treturn false;\n\t}\n\n\tmonitor->attr.fragsize = (uint32_t)-1;\n\tmonitor->attr.maxlength = (uint32_t)-1;\n\tmonitor->attr.minreq = (uint32_t)-1;\n\tmonitor->attr.prebuf = (uint32_t)-1;\n\tmonitor->attr.tlength = pa_usec_to_bytes(25000, &spec);\n\n\tpa_stream_flags_t flags = PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_START_CORKED;\n\n\tint_fast32_t ret = pulseaudio_connect_playback(monitor->stream, monitor->device, &monitor->attr, flags);\n\tif (ret < 0) {\n\t\tpulseaudio_stop_playback(monitor);\n\t\tblog(LOG_ERROR, \"Unable to connect to stream\");\n\t\treturn false;\n\t}\n\n\tblog(LOG_INFO, \"Started Monitoring in '%s'\", monitor->device);\n\treturn true;\n}\n\nstatic void audio_monitor_init_final(struct audio_monitor *monitor)\n{\n\tif (monitor->ignore)\n\t\treturn;\n\n\tobs_source_add_audio_capture_callback(monitor->source, on_audio_playback, monitor);\n}\n\nstatic inline void audio_monitor_free(struct audio_monitor *monitor)\n{\n\tif (monitor->ignore)\n\t\treturn;\n\n\tif (monitor->source)\n\t\tobs_source_remove_audio_capture_callback(monitor->source, on_audio_playback, monitor);\n\n\taudio_resampler_destroy(monitor->resampler);\n\tdeque_free(&monitor->new_data);\n\n\tif (monitor->stream)\n\t\tpulseaudio_stop_playback(monitor);\n\tpulseaudio_unref();\n\n\tbfree(monitor->device);\n}\n\nstruct audio_monitor *audio_monitor_create(obs_source_t *source)\n{\n\tstruct audio_monitor monitor = {0};\n\tstruct audio_monitor *out;\n\n\tif (!audio_monitor_init(&monitor, source))\n\t\tgoto fail;\n\n\tout = bmemdup(&monitor, sizeof(monitor));\n\n\tpthread_mutex_lock(&obs->audio.monitoring_mutex);\n\tda_push_back(obs->audio.monitors, &out);\n\tpthread_mutex_unlock(&obs->audio.monitoring_mutex);\n\n\taudio_monitor_init_final(out);\n\treturn out;\n\nfail:\n\taudio_monitor_free(&monitor);\n\treturn NULL;\n}\n\nvoid audio_monitor_reset(struct audio_monitor *monitor)\n{\n\tstruct audio_monitor new_monitor = {0};\n\tbool success;\n\taudio_monitor_free(monitor);\n\n\tpthread_mutex_lock(&monitor->playback_mutex);\n\tsuccess = audio_monitor_init(&new_monitor, monitor->source);\n\tpthread_mutex_unlock(&monitor->playback_mutex);\n\n\tif (success) {\n\t\t*monitor = new_monitor;\n\t\taudio_monitor_init_final(monitor);\n\t} else {\n\t\taudio_monitor_free(&new_monitor);\n\t}\n}\n\nvoid audio_monitor_destroy(struct audio_monitor *monitor)\n{\n\tif (monitor) {\n\t\taudio_monitor_free(monitor);\n\n\t\tpthread_mutex_lock(&obs->audio.monitoring_mutex);\n\t\tda_erase_item(obs->audio.monitors, &monitor);\n\t\tpthread_mutex_unlock(&obs->audio.monitoring_mutex);\n\n\t\tbfree(monitor);\n\t}\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/pulse/pulseaudio-wrapper.c",
    "content": "/*\nCopyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>\nCopyright (C) 2017 by Fabio Madia <admshao@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include <pthread.h>\n\n#include <pulse/thread-mainloop.h>\n\n#include <util/base.h>\n#include <obs.h>\n\n#include \"pulseaudio-wrapper.h\"\n\n/* global data */\nstatic uint_fast32_t pulseaudio_refs = 0;\nstatic pthread_mutex_t pulseaudio_mutex = PTHREAD_MUTEX_INITIALIZER;\nstatic pa_threaded_mainloop *pulseaudio_mainloop = NULL;\nstatic pa_context *pulseaudio_context = NULL;\n\nstatic void pulseaudio_default_devices(pa_context *c, const pa_server_info *i, void *userdata)\n{\n\tUNUSED_PARAMETER(c);\n\tstruct pulseaudio_default_output *d = (struct pulseaudio_default_output *)userdata;\n\td->default_sink_name = bstrdup(i->default_sink_name);\n\tpulseaudio_signal(0);\n}\n\nvoid get_default_id(char **id)\n{\n\tpulseaudio_init();\n\tstruct pulseaudio_default_output *pdo = bzalloc(sizeof(struct pulseaudio_default_output));\n\tpulseaudio_get_server_info((pa_server_info_cb_t)pulseaudio_default_devices, (void *)pdo);\n\n\tif (!pdo->default_sink_name || !*pdo->default_sink_name) {\n\t\t*id = bzalloc(1);\n\t} else {\n\t\t*id = bzalloc(strlen(pdo->default_sink_name) + 9);\n\t\tstrcat(*id, pdo->default_sink_name);\n\t\tbfree(pdo->default_sink_name);\n\t}\n\n\tbfree(pdo);\n\tpulseaudio_unref();\n}\n\n/**\n * Checks whether a sound source (id1) is the .monitor device for the\n * selected monitoring output (id2).\n */\nbool devices_match(const char *id1, const char *id2)\n{\n\tbool match;\n\tchar *name_default = NULL;\n\tchar *name1 = NULL;\n\tchar *name2 = NULL;\n\n\tif (!id1 || !id2)\n\t\treturn false;\n\n\tif (strcmp(id1, \"default\") == 0) {\n\t\tget_default_id(&name_default);\n\t\tname1 = bzalloc(strlen(name_default) + 9);\n\t\tstrcat(name1, name_default);\n\t\tstrcat(name1, \".monitor\");\n\t} else {\n\t\tname1 = bstrdup(id1);\n\t}\n\n\tif (strcmp(id2, \"default\") == 0) {\n\t\tif (!name_default)\n\t\t\tget_default_id(&name_default);\n\t\tname2 = bzalloc(strlen(name_default) + 9);\n\t\tstrcat(name2, name_default);\n\t\tstrcat(name2, \".monitor\");\n\t} else {\n\t\tname2 = bzalloc(strlen(id2) + 9);\n\t\tstrcat(name2, id2);\n\t\tstrcat(name2, \".monitor\");\n\t}\n\n\tmatch = strcmp(name1, name2) == 0;\n\tbfree(name_default);\n\tbfree(name1);\n\tbfree(name2);\n\treturn match;\n}\n\n/**\n * context status change callback\n *\n * @todo this is currently a noop, we want to reconnect here if the connection\n *       is lost ...\n */\nstatic void pulseaudio_context_state_changed(pa_context *c, void *userdata)\n{\n\tUNUSED_PARAMETER(userdata);\n\tUNUSED_PARAMETER(c);\n\n\tpulseaudio_signal(0);\n}\n\n/**\n * get the default properties\n */\nstatic pa_proplist *pulseaudio_properties()\n{\n\tpa_proplist *p = pa_proplist_new();\n\n\tpa_proplist_sets(p, PA_PROP_APPLICATION_NAME, \"OBS\");\n\tpa_proplist_sets(p, PA_PROP_APPLICATION_ICON_NAME, \"obs\");\n\tpa_proplist_sets(p, PA_PROP_MEDIA_ROLE, \"production\");\n\n\treturn p;\n}\n\n/**\n * Initialize the pulse audio context with properties and callback\n */\nstatic void pulseaudio_init_context()\n{\n\tpulseaudio_lock();\n\n\tpa_proplist *p = pulseaudio_properties();\n\tpulseaudio_context =\n\t\tpa_context_new_with_proplist(pa_threaded_mainloop_get_api(pulseaudio_mainloop), \"OBS-Monitor\", p);\n\n\tpa_context_set_state_callback(pulseaudio_context, pulseaudio_context_state_changed, NULL);\n\n\tpa_context_connect(pulseaudio_context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);\n\tpa_proplist_free(p);\n\n\tpulseaudio_unlock();\n}\n\n/**\n * wait for context to be ready\n */\nstatic int_fast32_t pulseaudio_context_ready()\n{\n\tpulseaudio_lock();\n\n\tif (!PA_CONTEXT_IS_GOOD(pa_context_get_state(pulseaudio_context))) {\n\t\tpulseaudio_unlock();\n\t\treturn -1;\n\t}\n\n\twhile (pa_context_get_state(pulseaudio_context) != PA_CONTEXT_READY)\n\t\tpulseaudio_wait();\n\n\tpulseaudio_unlock();\n\treturn 0;\n}\n\nint_fast32_t pulseaudio_init()\n{\n\tpthread_mutex_lock(&pulseaudio_mutex);\n\n\tif (pulseaudio_refs == 0) {\n\t\tpulseaudio_mainloop = pa_threaded_mainloop_new();\n\t\tpa_threaded_mainloop_start(pulseaudio_mainloop);\n\n\t\tpulseaudio_init_context();\n\t}\n\n\tpulseaudio_refs++;\n\n\tpthread_mutex_unlock(&pulseaudio_mutex);\n\n\treturn 0;\n}\n\nvoid pulseaudio_unref()\n{\n\tpthread_mutex_lock(&pulseaudio_mutex);\n\n\tif (--pulseaudio_refs == 0) {\n\t\tpulseaudio_lock();\n\t\tif (pulseaudio_context != NULL) {\n\t\t\tpa_context_disconnect(pulseaudio_context);\n\t\t\tpa_context_unref(pulseaudio_context);\n\t\t\tpulseaudio_context = NULL;\n\t\t}\n\t\tpulseaudio_unlock();\n\n\t\tif (pulseaudio_mainloop != NULL) {\n\t\t\tpa_threaded_mainloop_stop(pulseaudio_mainloop);\n\t\t\tpa_threaded_mainloop_free(pulseaudio_mainloop);\n\t\t\tpulseaudio_mainloop = NULL;\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&pulseaudio_mutex);\n}\n\nvoid pulseaudio_lock()\n{\n\tpa_threaded_mainloop_lock(pulseaudio_mainloop);\n}\n\nvoid pulseaudio_unlock()\n{\n\tpa_threaded_mainloop_unlock(pulseaudio_mainloop);\n}\n\nvoid pulseaudio_wait()\n{\n\tpa_threaded_mainloop_wait(pulseaudio_mainloop);\n}\n\nvoid pulseaudio_signal(int wait_for_accept)\n{\n\tpa_threaded_mainloop_signal(pulseaudio_mainloop, wait_for_accept);\n}\n\nvoid pulseaudio_accept()\n{\n\tpa_threaded_mainloop_accept(pulseaudio_mainloop);\n}\n\nint_fast32_t pulseaudio_get_source_info_list(pa_source_info_cb_t cb, void *userdata)\n{\n\tif (pulseaudio_context_ready() < 0)\n\t\treturn -1;\n\n\tpulseaudio_lock();\n\n\tpa_operation *op = pa_context_get_source_info_list(pulseaudio_context, cb, userdata);\n\tif (!op) {\n\t\tpulseaudio_unlock();\n\t\treturn -1;\n\t}\n\twhile (pa_operation_get_state(op) == PA_OPERATION_RUNNING)\n\t\tpulseaudio_wait();\n\tpa_operation_unref(op);\n\n\tpulseaudio_unlock();\n\n\treturn 0;\n}\n\nint_fast32_t pulseaudio_get_source_info(pa_source_info_cb_t cb, const char *name, void *userdata)\n{\n\tif (pulseaudio_context_ready() < 0)\n\t\treturn -1;\n\n\tpulseaudio_lock();\n\n\tpa_operation *op = pa_context_get_source_info_by_name(pulseaudio_context, name, cb, userdata);\n\tif (!op) {\n\t\tpulseaudio_unlock();\n\t\treturn -1;\n\t}\n\twhile (pa_operation_get_state(op) == PA_OPERATION_RUNNING)\n\t\tpulseaudio_wait();\n\tpa_operation_unref(op);\n\n\tpulseaudio_unlock();\n\n\treturn 0;\n}\n\nint_fast32_t pulseaudio_get_sink_info_list(pa_sink_info_cb_t cb, void *userdata)\n{\n\tif (pulseaudio_context_ready() < 0)\n\t\treturn -1;\n\n\tpulseaudio_lock();\n\n\tpa_operation *op = pa_context_get_sink_info_list(pulseaudio_context, cb, userdata);\n\tif (!op) {\n\t\tpulseaudio_unlock();\n\t\treturn -1;\n\t}\n\twhile (pa_operation_get_state(op) == PA_OPERATION_RUNNING)\n\t\tpulseaudio_wait();\n\tpa_operation_unref(op);\n\n\tpulseaudio_unlock();\n\n\treturn 0;\n}\n\nint_fast32_t pulseaudio_get_sink_info(pa_sink_info_cb_t cb, const char *name, void *userdata)\n{\n\tif (pulseaudio_context_ready() < 0)\n\t\treturn -1;\n\n\tpulseaudio_lock();\n\n\tpa_operation *op = pa_context_get_sink_info_by_name(pulseaudio_context, name, cb, userdata);\n\tif (!op) {\n\t\tpulseaudio_unlock();\n\t\treturn -1;\n\t}\n\twhile (pa_operation_get_state(op) == PA_OPERATION_RUNNING)\n\t\tpulseaudio_wait();\n\tpa_operation_unref(op);\n\n\tpulseaudio_unlock();\n\n\treturn 0;\n}\n\nint_fast32_t pulseaudio_get_server_info(pa_server_info_cb_t cb, void *userdata)\n{\n\tif (pulseaudio_context_ready() < 0)\n\t\treturn -1;\n\n\tpulseaudio_lock();\n\n\tpa_operation *op = pa_context_get_server_info(pulseaudio_context, cb, userdata);\n\tif (!op) {\n\t\tpulseaudio_unlock();\n\t\treturn -1;\n\t}\n\twhile (pa_operation_get_state(op) == PA_OPERATION_RUNNING)\n\t\tpulseaudio_wait();\n\tpa_operation_unref(op);\n\n\tpulseaudio_unlock();\n\treturn 0;\n}\n\npa_stream *pulseaudio_stream_new(const char *name, const pa_sample_spec *ss, const pa_channel_map *map)\n{\n\tif (pulseaudio_context_ready() < 0)\n\t\treturn NULL;\n\n\tpulseaudio_lock();\n\n\tpa_proplist *p = pulseaudio_properties();\n\tpa_stream *s = pa_stream_new_with_proplist(pulseaudio_context, name, ss, map, p);\n\tpa_proplist_free(p);\n\n\tpulseaudio_unlock();\n\treturn s;\n}\n\nint_fast32_t pulseaudio_connect_playback(pa_stream *s, const char *name, const pa_buffer_attr *attr,\n\t\t\t\t\t pa_stream_flags_t flags)\n{\n\tif (pulseaudio_context_ready() < 0)\n\t\treturn -1;\n\n\tsize_t dev_len = strlen(name);\n\tchar *device = bzalloc(dev_len + 1);\n\tmemcpy(device, name, dev_len);\n\n\tpulseaudio_lock();\n\tint_fast32_t ret = pa_stream_connect_playback(s, device, attr, flags, NULL, NULL);\n\tpulseaudio_unlock();\n\n\tbfree(device);\n\treturn ret;\n}\n\nvoid pulseaudio_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata)\n{\n\tif (pulseaudio_context_ready() < 0)\n\t\treturn;\n\n\tpulseaudio_lock();\n\tpa_stream_set_write_callback(p, cb, userdata);\n\tpulseaudio_unlock();\n}\n\nvoid pulseaudio_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata)\n{\n\tif (pulseaudio_context_ready() < 0)\n\t\treturn;\n\n\tpulseaudio_lock();\n\tpa_stream_set_underflow_callback(p, cb, userdata);\n\tpulseaudio_unlock();\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/pulse/pulseaudio-wrapper.h",
    "content": "/*\nCopyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>\nCopyright (C) 2017 by Fabio Madia <admshao@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include <inttypes.h>\n#include <pulse/stream.h>\n#include <pulse/context.h>\n#include <pulse/introspect.h>\n\n#pragma once\n\nstruct pulseaudio_default_output {\n\tchar *default_sink_name;\n};\n\nstruct enum_cb {\n\tobs_enum_audio_device_cb cb;\n\tvoid *data;\n\tint cont;\n};\n\nvoid get_default_id(char **id);\n\nbool devices_match(const char *id1, const char *id2);\n\n/**\n * Initialize the pulseaudio mainloop and increase the reference count\n */\nint_fast32_t pulseaudio_init();\n\n/**\n * Unreference the pulseaudio mainloop, when the reference count reaches\n * zero the mainloop will automatically be destroyed\n */\nvoid pulseaudio_unref();\n\n/**\n * Lock the mainloop\n *\n * In order to allow for multiple threads to use the same mainloop pulseaudio\n * provides it's own locking mechanism. This function should be called before\n * using any pulseaudio function that is in any way related to the mainloop or\n * context.\n *\n * @note use of this function may cause deadlocks\n *\n * @warning do not use with pulseaudio_ wrapper functions\n */\nvoid pulseaudio_lock();\n\n/**\n * Unlock the mainloop\n *\n * @see pulseaudio_lock()\n */\nvoid pulseaudio_unlock();\n\n/**\n * Wait for events to happen\n *\n * This function should be called when waiting for an event to happen.\n */\nvoid pulseaudio_wait();\n\n/**\n * Wait for accept signal from calling thread\n *\n * This function tells the pulseaudio mainloop whether the data provided to\n * the callback should be retained until the calling thread executes\n * pulseaudio_accept()\n *\n * If wait_for_accept is 0 the function returns and the data is freed.\n */\nvoid pulseaudio_signal(int wait_for_accept);\n\n/**\n * Signal the waiting callback to return\n *\n * This function is used in conjunction with pulseaudio_signal()\n */\nvoid pulseaudio_accept();\n\n/**\n * Request source information\n *\n * The function will block until the operation was executed and the mainloop\n * called the provided callback function.\n *\n * @return negative on error\n *\n * @note The function will block until the server context is ready.\n *\n * @warning call without active locks\n */\nint_fast32_t pulseaudio_get_source_info_list(pa_source_info_cb_t cb, void *userdata);\n\n/**\n * Request source information from a specific source\n *\n * The function will block until the operation was executed and the mainloop\n * called the provided callback function.\n *\n * @param cb pointer to the callback function\n * @param name the source name to get information for\n * @param userdata pointer to userdata the callback will be called with\n *\n * @return negative on error\n *\n * @note The function will block until the server context is ready.\n *\n * @warning call without active locks\n */\nint_fast32_t pulseaudio_get_source_info(pa_source_info_cb_t cb, const char *name, void *userdata);\n\n/**\n * Request sink information\n *\n * The function will block until the operation was executed and the mainloop\n * called the provided callback function.\n *\n * @return negative on error\n *\n * @note The function will block until the server context is ready.\n *\n * @warning call without active locks\n */\nint_fast32_t pulseaudio_get_sink_info_list(pa_sink_info_cb_t cb, void *userdata);\n\n/**\n * Request sink information from a specific sink\n *\n * The function will block until the operation was executed and the mainloop\n * called the provided callback function.\n *\n * @param cb pointer to the callback function\n * @param name the sink name to get information for\n * @param userdata pointer to userdata the callback will be called with\n *\n * @return negative on error\n *\n * @note The function will block until the server context is ready.\n *\n * @warning call without active locks\n */\nint_fast32_t pulseaudio_get_sink_info(pa_sink_info_cb_t cb, const char *name, void *userdata);\n\n/**\n * Request server information\n *\n * The function will block until the operation was executed and the mainloop\n * called the provided callback function.\n *\n * @return negative on error\n *\n * @note The function will block until the server context is ready.\n *\n * @warning call without active locks\n */\nint_fast32_t pulseaudio_get_server_info(pa_server_info_cb_t cb, void *userdata);\n\n/**\n * Create a new stream with the default properties\n *\n * @note The function will block until the server context is ready.\n *\n * @warning call without active locks\n */\npa_stream *pulseaudio_stream_new(const char *name, const pa_sample_spec *ss, const pa_channel_map *map);\n\n/**\n * Connect to a pulseaudio playback stream\n *\n * @param s pa_stream to connect to. NULL for default\n * @param attr pa_buffer_attr\n * @param name Device name. NULL for default device\n * @param flags pa_stream_flags_t\n * @return negative on error\n */\nint_fast32_t pulseaudio_connect_playback(pa_stream *s, const char *name, const pa_buffer_attr *attr,\n\t\t\t\t\t pa_stream_flags_t flags);\n\n/**\n * Sets a callback function for when data can be written to the stream\n *\n * @param p pa_stream to connect to. NULL for default\n * @param cb pa_stream_request_cb_t\n * @param userdata pointer to userdata the callback will be called with\n */\nvoid pulseaudio_write_callback(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);\n\n/**\n * Sets a callback function for when an underflow happen\n *\n * @param p pa_stream to connect to. NULL for default\n * @param cb pa_stream_notify_cb_t\n * @param userdata pointer to userdata the callback will be called with\n */\nvoid pulseaudio_set_underflow_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);\n"
  },
  {
    "path": "libobs/audio-monitoring/win32/wasapi-enum-devices.c",
    "content": "#include \"../../obs-internal.h\"\n\n#include \"wasapi-output.h\"\n\n#include <propsys.h>\n\n#ifdef __MINGW32__\n\n#ifdef DEFINE_PROPERTYKEY\n#undef DEFINE_PROPERTYKEY\n#endif\n#define DEFINE_PROPERTYKEY(id, a, b, c, d, e, f, g, h, i, j, k, l) \\\n\tconst PROPERTYKEY id = {{a,                                \\\n\t\t\t\t b,                                \\\n\t\t\t\t c,                                \\\n\t\t\t\t {                                 \\\n\t\t\t\t\t d,                        \\\n\t\t\t\t\t e,                        \\\n\t\t\t\t\t f,                        \\\n\t\t\t\t\t g,                        \\\n\t\t\t\t\t h,                        \\\n\t\t\t\t\t i,                        \\\n\t\t\t\t\t j,                        \\\n\t\t\t\t\t k,                        \\\n\t\t\t\t }},                               \\\n\t\t\t\tl};\nDEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0,\n\t\t   14);\n\n#else\n\n#include <functiondiscoverykeys_devpkey.h>\n\n#endif\n\nstatic bool get_device_info(obs_enum_audio_device_cb cb, void *data, IMMDeviceCollection *collection, UINT idx)\n{\n\tIPropertyStore *store = NULL;\n\tIMMDevice *device = NULL;\n\tPROPVARIANT name_var;\n\tchar utf8_name[512];\n\tWCHAR *w_id = NULL;\n\tchar utf8_id[512];\n\tbool cont = true;\n\tHRESULT hr;\n\n\thr = collection->lpVtbl->Item(collection, idx, &device);\n\tif (FAILED(hr)) {\n\t\tgoto fail;\n\t}\n\n\thr = device->lpVtbl->GetId(device, &w_id);\n\tif (FAILED(hr)) {\n\t\tgoto fail;\n\t}\n\n\thr = device->lpVtbl->OpenPropertyStore(device, STGM_READ, &store);\n\tif (FAILED(hr)) {\n\t\tgoto fail;\n\t}\n\n\tPropVariantInit(&name_var);\n\thr = store->lpVtbl->GetValue(store, &PKEY_Device_FriendlyName, &name_var);\n\tif (FAILED(hr)) {\n\t\tgoto fail;\n\t}\n\n\tos_wcs_to_utf8(w_id, 0, utf8_id, 512);\n\tos_wcs_to_utf8(name_var.pwszVal, 0, utf8_name, 512);\n\n\tcont = cb(data, utf8_name, utf8_id);\n\tPropVariantClear(&name_var);\n\nfail:\n\tsafe_release(store);\n\tsafe_release(device);\n\tif (w_id)\n\t\tCoTaskMemFree(w_id);\n\treturn cont;\n}\n\nvoid obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data)\n{\n\tIMMDeviceEnumerator *enumerator = NULL;\n\tIMMDeviceCollection *collection = NULL;\n\tUINT count;\n\tHRESULT hr;\n\n\thr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, &enumerator);\n\tif (FAILED(hr)) {\n\t\tgoto fail;\n\t}\n\n\thr = enumerator->lpVtbl->EnumAudioEndpoints(enumerator, eRender, DEVICE_STATE_ACTIVE, &collection);\n\tif (FAILED(hr)) {\n\t\tgoto fail;\n\t}\n\n\thr = collection->lpVtbl->GetCount(collection, &count);\n\tif (FAILED(hr)) {\n\t\tgoto fail;\n\t}\n\n\tfor (UINT i = 0; i < count; i++) {\n\t\tif (!get_device_info(cb, data, collection, i)) {\n\t\t\tbreak;\n\t\t}\n\t}\n\nfail:\n\tsafe_release(enumerator);\n\tsafe_release(collection);\n}\n\nstatic void get_default_id(char **p_id)\n{\n\tIMMDeviceEnumerator *immde = NULL;\n\tIMMDevice *device = NULL;\n\tWCHAR *w_id = NULL;\n\tHRESULT hr;\n\n\tif (*p_id)\n\t\treturn;\n\n\thr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, &immde);\n\tif (FAILED(hr)) {\n\t\tgoto fail;\n\t}\n\n\thr = immde->lpVtbl->GetDefaultAudioEndpoint(immde, eRender, eConsole, &device);\n\tif (FAILED(hr)) {\n\t\tgoto fail;\n\t}\n\n\thr = device->lpVtbl->GetId(device, &w_id);\n\tif (FAILED(hr)) {\n\t\tgoto fail;\n\t}\n\n\tos_wcs_to_utf8_ptr(w_id, 0, p_id);\n\nfail:\n\tif (!*p_id)\n\t\t*p_id = bzalloc(1);\n\tif (immde)\n\t\timmde->lpVtbl->Release(immde);\n\tif (device)\n\t\tdevice->lpVtbl->Release(device);\n\tif (w_id)\n\t\tCoTaskMemFree(w_id);\n}\n\nbool devices_match(const char *id1, const char *id2)\n{\n\tchar *default_id = NULL;\n\tbool match;\n\n\tif (!id1 || !id2)\n\t\treturn false;\n\n\tif (strcmp(id1, \"default\") == 0) {\n\t\tget_default_id(&default_id);\n\t\tid1 = default_id;\n\t}\n\tif (strcmp(id2, \"default\") == 0) {\n\t\tget_default_id(&default_id);\n\t\tid2 = default_id;\n\t}\n\n\tmatch = strcmp(id1, id2) == 0;\n\tbfree(default_id);\n\n\treturn match;\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/win32/wasapi-monitoring-available.c",
    "content": "#include \"../../obs-internal.h\"\n\nbool obs_audio_monitoring_available(void)\n{\n\treturn true;\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/win32/wasapi-output.c",
    "content": "#include \"../../media-io/audio-resampler.h\"\n#include \"../../util/deque.h\"\n#include \"../../util/platform.h\"\n#include \"../../util/darray.h\"\n#include \"../../util/util_uint64.h\"\n#include \"../../obs-internal.h\"\n\n#include \"wasapi-output.h\"\n\n#define ACTUALLY_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \\\n\tEXTERN_C const GUID DECLSPEC_SELECTANY name = {l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}}\n\n#define do_log(level, format, ...) \\\n\tblog(level, \"[audio monitoring: '%s'] \" format, obs_source_get_name(monitor->source), ##__VA_ARGS__)\n\n#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)\n#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)\n#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)\n\nACTUALLY_DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69,\n\t\t     0x2E);\nACTUALLY_DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17,\n\t\t     0xE6);\nACTUALLY_DEFINE_GUID(IID_IAudioClient, 0x1CB9AD4C, 0xDBFA, 0x4C32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2);\nACTUALLY_DEFINE_GUID(IID_IAudioRenderClient, 0xF294ACFC, 0x3146, 0x4483, 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60,\n\t\t     0xE2);\n\nstruct audio_monitor {\n\tobs_source_t *source;\n\tIAudioClient *client;\n\tIAudioRenderClient *render;\n\n\tuint64_t last_recv_time;\n\tuint64_t prev_video_ts;\n\tuint64_t time_since_prev;\n\taudio_resampler_t *resampler;\n\tuint32_t sample_rate;\n\tuint32_t channels;\n\tbool source_has_video;\n\tbool ignore;\n\n\tint64_t lowest_audio_offset;\n\tstruct deque delay_buffer;\n\tuint32_t delay_size;\n\n\tDARRAY(float) buf;\n\tSRWLOCK playback_mutex;\n};\n\n/* #define DEBUG_AUDIO */\n\nstatic bool process_audio_delay(struct audio_monitor *monitor, float **data, uint32_t *frames, uint64_t ts,\n\t\t\t\tuint32_t pad)\n{\n\tobs_source_t *s = monitor->source;\n\tuint64_t last_frame_ts = s->last_frame_ts;\n\tuint64_t cur_time = os_gettime_ns();\n\tuint64_t front_ts;\n\tuint64_t cur_ts;\n\tint64_t diff;\n\tuint32_t blocksize = monitor->channels * sizeof(float);\n\n\t/* cut off audio if long-since leftover audio in delay buffer */\n\tif (cur_time - monitor->last_recv_time > 1000000000)\n\t\tdeque_free(&monitor->delay_buffer);\n\tmonitor->last_recv_time = cur_time;\n\n\tts += monitor->source->sync_offset;\n\n\tdeque_push_back(&monitor->delay_buffer, &ts, sizeof(ts));\n\tdeque_push_back(&monitor->delay_buffer, frames, sizeof(*frames));\n\tdeque_push_back(&monitor->delay_buffer, *data, *frames * blocksize);\n\n\tif (!monitor->prev_video_ts) {\n\t\tmonitor->prev_video_ts = last_frame_ts;\n\n\t} else if (monitor->prev_video_ts == last_frame_ts) {\n\t\tmonitor->time_since_prev += util_mul_div64(*frames, 1000000000ULL, monitor->sample_rate);\n\t} else {\n\t\tmonitor->time_since_prev = 0;\n\t}\n\n\twhile (monitor->delay_buffer.size != 0) {\n\t\tsize_t size;\n\t\tbool bad_diff;\n\n\t\tdeque_peek_front(&monitor->delay_buffer, &cur_ts, sizeof(ts));\n\t\tfront_ts = cur_ts - util_mul_div64(pad, 1000000000ULL, monitor->sample_rate);\n\t\tdiff = (int64_t)front_ts - (int64_t)last_frame_ts;\n\t\tbad_diff = !last_frame_ts || llabs(diff) > 5000000000 || monitor->time_since_prev > 100000000ULL;\n\n\t\t/* delay audio if rushing */\n\t\tif (!bad_diff && diff > 75000000) {\n#ifdef DEBUG_AUDIO\n\t\t\tblog(LOG_INFO,\n\t\t\t     \"audio rushing, cutting audio, \"\n\t\t\t     \"diff: %lld, delay buffer size: %lu, \"\n\t\t\t     \"v: %llu: a: %llu\",\n\t\t\t     diff, (int)monitor->delay_buffer.size, last_frame_ts, front_ts);\n#endif\n\t\t\treturn false;\n\t\t}\n\n\t\tdeque_pop_front(&monitor->delay_buffer, NULL, sizeof(ts));\n\t\tdeque_pop_front(&monitor->delay_buffer, frames, sizeof(*frames));\n\n\t\tsize = *frames * blocksize;\n\t\tda_resize(monitor->buf, size);\n\t\tdeque_pop_front(&monitor->delay_buffer, monitor->buf.array, size);\n\n\t\t/* cut audio if dragging */\n\t\tif (!bad_diff && diff < -75000000 && monitor->delay_buffer.size > 0) {\n#ifdef DEBUG_AUDIO\n\t\t\tblog(LOG_INFO,\n\t\t\t     \"audio dragging, cutting audio, \"\n\t\t\t     \"diff: %lld, delay buffer size: %lu, \"\n\t\t\t     \"v: %llu: a: %llu\",\n\t\t\t     diff, (int)monitor->delay_buffer.size, last_frame_ts, front_ts);\n#endif\n\t\t\tcontinue;\n\t\t}\n\n\t\t*data = monitor->buf.array;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic enum speaker_layout convert_speaker_layout(DWORD layout, WORD channels)\n{\n\tswitch (layout) {\n\tcase KSAUDIO_SPEAKER_2POINT1:\n\t\treturn SPEAKERS_2POINT1;\n\tcase KSAUDIO_SPEAKER_SURROUND:\n\t\treturn SPEAKERS_4POINT0;\n\tcase KSAUDIO_SPEAKER_4POINT1:\n\t\treturn SPEAKERS_4POINT1;\n\tcase KSAUDIO_SPEAKER_5POINT1:\n\t\treturn SPEAKERS_5POINT1;\n\tcase KSAUDIO_SPEAKER_7POINT1:\n\t\treturn SPEAKERS_7POINT1;\n\t}\n\n\treturn (enum speaker_layout)channels;\n}\n\nstatic bool audio_monitor_init_wasapi(struct audio_monitor *monitor)\n{\n\tbool success = false;\n\tIMMDeviceEnumerator *immde = NULL;\n\tWAVEFORMATEX *wfex = NULL;\n\tUINT32 frames;\n\tHRESULT hr;\n\n\t/* ------------------------------------------ *\n\t * Init device                                */\n\n\thr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void **)&immde);\n\tif (FAILED(hr)) {\n\t\twarn(\"%s: Failed to create IMMDeviceEnumerator: %08lX\", __FUNCTION__, hr);\n\t\treturn false;\n\t}\n\n\tIMMDevice *device = NULL;\n\tconst char *const id = obs->audio.monitoring_device_id;\n\tif (strcmp(id, \"default\") == 0) {\n\t\thr = immde->lpVtbl->GetDefaultAudioEndpoint(immde, eRender, eConsole, &device);\n\t} else {\n\t\twchar_t w_id[512];\n\t\tos_utf8_to_wcs(id, 0, w_id, 512);\n\n\t\thr = immde->lpVtbl->GetDevice(immde, w_id, &device);\n\t}\n\n\tif (FAILED(hr)) {\n\t\twarn(\"%s: Failed to get device: %08lX\", __FUNCTION__, hr);\n\t\tgoto fail;\n\t}\n\n\t/* ------------------------------------------ *\n\t * Init client                                */\n\n\thr = device->lpVtbl->Activate(device, &IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&monitor->client);\n\tdevice->lpVtbl->Release(device);\n\tif (FAILED(hr)) {\n\t\twarn(\"%s: Failed to activate device: %08lX\", __FUNCTION__, hr);\n\t\tgoto fail;\n\t}\n\n\thr = monitor->client->lpVtbl->GetMixFormat(monitor->client, &wfex);\n\tif (FAILED(hr)) {\n\t\twarn(\"%s: Failed to get mix format: %08lX\", __FUNCTION__, hr);\n\t\tgoto fail;\n\t}\n\n\thr = monitor->client->lpVtbl->Initialize(monitor->client, AUDCLNT_SHAREMODE_SHARED, 0, 10000000, 0, wfex, NULL);\n\tif (FAILED(hr)) {\n\t\twarn(\"%s: Failed to initialize: %08lX\", __FUNCTION__, hr);\n\t\tgoto fail;\n\t}\n\n\t/* ------------------------------------------ *\n\t * Init resampler                             */\n\n\tconst struct audio_output_info *info = audio_output_get_info(obs->audio.audio);\n\tWAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE *)wfex;\n\tstruct resample_info from;\n\tstruct resample_info to;\n\n\tfrom.samples_per_sec = info->samples_per_sec;\n\tfrom.speakers = info->speakers;\n\tfrom.format = AUDIO_FORMAT_FLOAT_PLANAR;\n\n\tto.samples_per_sec = (uint32_t)wfex->nSamplesPerSec;\n\tto.speakers = convert_speaker_layout(ext->dwChannelMask, wfex->nChannels);\n\tto.format = AUDIO_FORMAT_FLOAT;\n\n\tmonitor->sample_rate = (uint32_t)wfex->nSamplesPerSec;\n\tmonitor->channels = wfex->nChannels;\n\tmonitor->resampler = audio_resampler_create(&to, &from);\n\tif (!monitor->resampler) {\n\t\tgoto fail;\n\t}\n\n\t/* ------------------------------------------ *\n\t * Init client                                */\n\n\thr = monitor->client->lpVtbl->GetBufferSize(monitor->client, &frames);\n\tif (FAILED(hr)) {\n\t\twarn(\"%s: Failed to get buffer size: %08lX\", __FUNCTION__, hr);\n\t\tgoto fail;\n\t}\n\n\thr = monitor->client->lpVtbl->GetService(monitor->client, &IID_IAudioRenderClient, (void **)&monitor->render);\n\tif (FAILED(hr)) {\n\t\twarn(\"%s: Failed to get IAudioRenderClient: %08lX\", __FUNCTION__, hr);\n\t\tgoto fail;\n\t}\n\n\thr = monitor->client->lpVtbl->Start(monitor->client);\n\tif (FAILED(hr)) {\n\t\twarn(\"%s: Failed to start audio: %08lX\", __FUNCTION__, hr);\n\t\tgoto fail;\n\t}\n\n\tsuccess = true;\n\nfail:\n\tsafe_release(immde);\n\tif (wfex)\n\t\tCoTaskMemFree(wfex);\n\treturn success;\n}\n\nstatic void audio_monitor_free_for_reconnect(struct audio_monitor *monitor)\n{\n\tif (monitor->client)\n\t\tmonitor->client->lpVtbl->Stop(monitor->client);\n\n\tif (monitor->render) {\n\t\tmonitor->render->lpVtbl->Release(monitor->render);\n\t\tmonitor->render = NULL;\n\t}\n\n\tif (monitor->client) {\n\t\tmonitor->client->lpVtbl->Stop(monitor->client);\n\t\tmonitor->client->lpVtbl->Release(monitor->client);\n\t\tmonitor->client = NULL;\n\t}\n\n\taudio_resampler_destroy(monitor->resampler);\n\tmonitor->resampler = NULL;\n\n\tdeque_free(&monitor->delay_buffer);\n\tda_free(monitor->buf);\n}\n\nstatic void on_audio_playback(void *param, obs_source_t *source, const struct audio_data *audio_data, bool muted)\n{\n\tstruct audio_monitor *monitor = param;\n\tuint8_t *resample_data[MAX_AV_PLANES];\n\tfloat vol = source->user_volume;\n\tuint32_t resample_frames;\n\tuint64_t ts_offset;\n\tbool success;\n\tBYTE *output;\n\n\tif (!TryAcquireSRWLockExclusive(&monitor->playback_mutex)) {\n\t\treturn;\n\t}\n\tif (os_atomic_load_long(&source->activate_refs) == 0) {\n\t\tgoto unlock;\n\t}\n\n\tif (!monitor->client && !audio_monitor_init_wasapi(monitor)) {\n\t\tgoto free_for_reconnect;\n\t}\n\n\tsuccess = audio_resampler_resample(monitor->resampler, resample_data, &resample_frames, &ts_offset,\n\t\t\t\t\t   (const uint8_t *const *)audio_data->data, (uint32_t)audio_data->frames);\n\tif (!success) {\n\t\tgoto unlock;\n\t}\n\n\tUINT32 pad = 0;\n\tHRESULT hr = monitor->client->lpVtbl->GetCurrentPadding(monitor->client, &pad);\n\tif (FAILED(hr)) {\n\t\tgoto free_for_reconnect;\n\t}\n\n\tbool decouple_audio = source->async_unbuffered && source->async_decoupled;\n\n\tif (monitor->source_has_video && !decouple_audio) {\n\t\tuint64_t ts = audio_data->timestamp - ts_offset;\n\n\t\tif (!process_audio_delay(monitor, (float **)(&resample_data[0]), &resample_frames, ts, pad)) {\n\t\t\tgoto unlock;\n\t\t}\n\t}\n\n\tIAudioRenderClient *const render = monitor->render;\n\thr = render->lpVtbl->GetBuffer(render, resample_frames, &output);\n\tif (FAILED(hr)) {\n\t\tgoto free_for_reconnect;\n\t}\n\n\tif (!muted) {\n\t\t/* apply volume */\n\t\tif (!close_float(vol, 1.0f, EPSILON)) {\n\t\t\tregister float *cur = (float *)resample_data[0];\n\t\t\tregister float *end = cur + resample_frames * monitor->channels;\n\n\t\t\twhile (cur < end)\n\t\t\t\t*(cur++) *= vol;\n\t\t}\n\t\tmemcpy(output, resample_data[0], resample_frames * monitor->channels * sizeof(float));\n\t}\n\n\thr = render->lpVtbl->ReleaseBuffer(render, resample_frames, muted ? AUDCLNT_BUFFERFLAGS_SILENT : 0);\n\tif (FAILED(hr)) {\n\t\tgoto free_for_reconnect;\n\t}\n\n\tgoto unlock;\n\nfree_for_reconnect:\n\taudio_monitor_free_for_reconnect(monitor);\nunlock:\n\tReleaseSRWLockExclusive(&monitor->playback_mutex);\n}\n\nstatic inline void audio_monitor_free(struct audio_monitor *monitor)\n{\n\tif (monitor->ignore)\n\t\treturn;\n\n\tif (monitor->source) {\n\t\tobs_source_remove_audio_capture_callback(monitor->source, on_audio_playback, monitor);\n\t}\n\n\tif (monitor->client)\n\t\tmonitor->client->lpVtbl->Stop(monitor->client);\n\n\tsafe_release(monitor->client);\n\tsafe_release(monitor->render);\n\taudio_resampler_destroy(monitor->resampler);\n\tdeque_free(&monitor->delay_buffer);\n\tda_free(monitor->buf);\n}\n\nextern bool devices_match(const char *id1, const char *id2);\n\nstatic bool audio_monitor_init(struct audio_monitor *monitor, obs_source_t *source)\n{\n\tmonitor->source = source;\n\n\tconst char *id = obs->audio.monitoring_device_id;\n\tif (!id) {\n\t\twarn(\"%s: No device ID set\", __FUNCTION__);\n\t\treturn false;\n\t}\n\n\tif (source->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) {\n\t\tobs_data_t *s = obs_source_get_settings(source);\n\t\tconst char *s_dev_id = obs_data_get_string(s, \"device_id\");\n\t\tbool match = devices_match(s_dev_id, id);\n\t\tobs_data_release(s);\n\n\t\tif (match) {\n\t\t\tmonitor->ignore = true;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tInitializeSRWLock(&monitor->playback_mutex);\n\n\treturn audio_monitor_init_wasapi(monitor);\n}\n\nstatic void audio_monitor_init_final(struct audio_monitor *monitor)\n{\n\tif (monitor->ignore)\n\t\treturn;\n\n\tmonitor->source_has_video = (monitor->source->info.output_flags & OBS_SOURCE_VIDEO) != 0;\n\tobs_source_add_audio_capture_callback(monitor->source, on_audio_playback, monitor);\n}\n\nstruct audio_monitor *audio_monitor_create(obs_source_t *source)\n{\n\tstruct audio_monitor monitor = {0};\n\tstruct audio_monitor *out;\n\n\tif (!audio_monitor_init(&monitor, source)) {\n\t\tgoto fail;\n\t}\n\n\tout = bmemdup(&monitor, sizeof(monitor));\n\n\tpthread_mutex_lock(&obs->audio.monitoring_mutex);\n\tda_push_back(obs->audio.monitors, &out);\n\tpthread_mutex_unlock(&obs->audio.monitoring_mutex);\n\n\taudio_monitor_init_final(out);\n\treturn out;\n\nfail:\n\taudio_monitor_free(&monitor);\n\treturn NULL;\n}\n\nvoid audio_monitor_reset(struct audio_monitor *monitor)\n{\n\tstruct audio_monitor new_monitor = {0};\n\tbool success;\n\n\tAcquireSRWLockExclusive(&monitor->playback_mutex);\n\tsuccess = audio_monitor_init(&new_monitor, monitor->source);\n\tReleaseSRWLockExclusive(&monitor->playback_mutex);\n\n\tif (success) {\n\t\tobs_source_t *source = monitor->source;\n\t\taudio_monitor_free(monitor);\n\t\t*monitor = new_monitor;\n\t\taudio_monitor_init_final(monitor);\n\t} else {\n\t\taudio_monitor_free(&new_monitor);\n\t}\n}\n\nvoid audio_monitor_destroy(struct audio_monitor *monitor)\n{\n\tif (monitor) {\n\t\taudio_monitor_free(monitor);\n\n\t\tpthread_mutex_lock(&obs->audio.monitoring_mutex);\n\t\tda_erase_item(obs->audio.monitors, &monitor);\n\t\tpthread_mutex_unlock(&obs->audio.monitoring_mutex);\n\n\t\tbfree(monitor);\n\t}\n}\n"
  },
  {
    "path": "libobs/audio-monitoring/win32/wasapi-output.h",
    "content": "#pragma once\n\n#include <windows.h>\n#include <mmdeviceapi.h>\n#include <audioclient.h>\n\n#ifndef KSAUDIO_SPEAKER_2POINT1\n#define KSAUDIO_SPEAKER_2POINT1 (KSAUDIO_SPEAKER_STEREO | SPEAKER_LOW_FREQUENCY)\n#endif\n\n#define KSAUDIO_SPEAKER_SURROUND_AVUTIL (KSAUDIO_SPEAKER_STEREO | SPEAKER_FRONT_CENTER)\n\n#ifndef KSAUDIO_SPEAKER_4POINT1\n#define KSAUDIO_SPEAKER_4POINT1 (KSAUDIO_SPEAKER_SURROUND | SPEAKER_LOW_FREQUENCY)\n#endif\n\n#define safe_release(ptr)                          \\\n\tdo {                                       \\\n\t\tif (ptr) {                         \\\n\t\t\tptr->lpVtbl->Release(ptr); \\\n\t\t}                                  \\\n\t} while (false)\n"
  },
  {
    "path": "libobs/callback/calldata.c",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include <string.h>\n\n#include \"../util/bmem.h\"\n#include \"../util/base.h\"\n\n#include \"calldata.h\"\n\n/*\n *   Uses a data stack.  Probably more complex than it should be, but reduces\n * fetching.\n *\n *   Stack format is:\n *     [size_t    param1_name_size]\n *     [char[]    param1_name]\n *     [size_t    param1_data_size]\n *     [uint8_t[] param1_data]\n *     [size_t    param2_name_size]\n *     [char[]    param2_name]\n *     [size_t    param2_data_size]\n *     [uint8_t[] param2_data]\n *     [...]\n *     [size_t    0]\n *\n *   Strings and string sizes always include the null terminator to allow for\n * direct referencing.\n */\n\nstatic inline size_t cd_serialize_size(uint8_t **pos)\n{\n\tsize_t size = 0;\n\tmemcpy(&size, *pos, sizeof(size_t));\n\t*pos += sizeof(size_t);\n\treturn size;\n}\n\nstatic inline const char *cd_serialize_string(uint8_t **pos)\n{\n\tsize_t size = cd_serialize_size(pos);\n\tconst char *str = (const char *)*pos;\n\n\t*pos += size;\n\n\treturn (size != 0) ? str : NULL;\n}\n\nstatic bool cd_getparam(const calldata_t *data, const char *name, uint8_t **pos)\n{\n\tsize_t name_size;\n\n\tif (!data->size)\n\t\treturn false;\n\n\t*pos = data->stack;\n\n\tname_size = cd_serialize_size(pos);\n\twhile (name_size != 0) {\n\t\tconst char *param_name = (const char *)*pos;\n\t\tsize_t param_size;\n\n\t\t*pos += name_size;\n\t\tif (strcmp(param_name, name) == 0)\n\t\t\treturn true;\n\n\t\tparam_size = cd_serialize_size(pos);\n\t\t*pos += param_size;\n\n\t\tname_size = cd_serialize_size(pos);\n\t}\n\n\t*pos -= sizeof(size_t);\n\treturn false;\n}\n\nstatic inline void cd_copy_string(uint8_t **pos, const char *str, size_t len)\n{\n\tif (!len)\n\t\tlen = strlen(str) + 1;\n\n\tmemcpy(*pos, &len, sizeof(size_t));\n\t*pos += sizeof(size_t);\n\tmemcpy(*pos, str, len);\n\t*pos += len;\n}\n\nstatic inline void cd_copy_data(uint8_t **pos, const void *in, size_t size)\n{\n\tmemcpy(*pos, &size, sizeof(size_t));\n\t*pos += sizeof(size_t);\n\n\tif (size) {\n\t\tmemcpy(*pos, in, size);\n\t\t*pos += size;\n\t}\n}\n\nstatic inline void cd_set_first_param(calldata_t *data, const char *name, const void *in, size_t size)\n{\n\tuint8_t *pos;\n\tsize_t capacity;\n\tsize_t name_len = strlen(name) + 1;\n\n\tcapacity = sizeof(size_t) * 3 + name_len + size;\n\tdata->size = capacity;\n\n\tif (capacity < 128)\n\t\tcapacity = 128;\n\n\tdata->capacity = capacity;\n\tdata->stack = bmalloc(capacity);\n\n\tpos = data->stack;\n\tcd_copy_string(&pos, name, name_len);\n\tcd_copy_data(&pos, in, size);\n\tmemset(pos, 0, sizeof(size_t));\n}\n\nstatic inline bool cd_ensure_capacity(calldata_t *data, uint8_t **pos, size_t new_size)\n{\n\tsize_t offset;\n\tsize_t new_capacity;\n\n\tif (new_size < data->capacity)\n\t\treturn true;\n\tif (data->fixed) {\n\t\tblog(LOG_ERROR, \"Tried to go above fixed calldata stack size!\");\n\t\treturn false;\n\t}\n\n\toffset = *pos - data->stack;\n\n\tnew_capacity = data->capacity * 2;\n\tif (new_capacity < new_size)\n\t\tnew_capacity = new_size;\n\n\tdata->stack = brealloc(data->stack, new_capacity);\n\tdata->capacity = new_capacity;\n\n\t*pos = data->stack + offset;\n\treturn true;\n}\n\n/* ------------------------------------------------------------------------- */\n\nbool calldata_get_data(const calldata_t *data, const char *name, void *out, size_t size)\n{\n\tuint8_t *pos;\n\tsize_t data_size;\n\n\tif (!data || !name || !*name)\n\t\treturn false;\n\n\tif (!cd_getparam(data, name, &pos))\n\t\treturn false;\n\n\tdata_size = cd_serialize_size(&pos);\n\tif (data_size != size)\n\t\treturn false;\n\n\tmemcpy(out, pos, size);\n\treturn true;\n}\n\nvoid calldata_set_data(calldata_t *data, const char *name, const void *in, size_t size)\n{\n\tuint8_t *pos = NULL;\n\n\tif (!data || !name || !*name)\n\t\treturn;\n\n\tif (!data->fixed && !data->stack) {\n\t\tcd_set_first_param(data, name, in, size);\n\t\treturn;\n\t}\n\n\tif (cd_getparam(data, name, &pos)) {\n\t\tsize_t cur_size;\n\t\tmemcpy(&cur_size, pos, sizeof(size_t));\n\n\t\tif (cur_size < size) {\n\t\t\tsize_t offset = size - cur_size;\n\t\t\tsize_t bytes = data->size;\n\n\t\t\tif (!cd_ensure_capacity(data, &pos, bytes + offset))\n\t\t\t\treturn;\n\t\t\tmemmove(pos + offset, pos, bytes - (pos - data->stack));\n\t\t\tdata->size += offset;\n\n\t\t} else if (cur_size > size) {\n\t\t\tsize_t offset = cur_size - size;\n\t\t\tsize_t bytes = data->size - offset;\n\n\t\t\tmemmove(pos, pos + offset, bytes - (pos - data->stack));\n\t\t\tdata->size -= offset;\n\t\t}\n\n\t\tcd_copy_data(&pos, in, size);\n\n\t} else {\n\t\tsize_t name_len = strlen(name) + 1;\n\t\tsize_t offset = name_len + size + sizeof(size_t) * 2;\n\t\tif (!cd_ensure_capacity(data, &pos, data->size + offset))\n\t\t\treturn;\n\t\tdata->size += offset;\n\n\t\tcd_copy_string(&pos, name, 0);\n\t\tcd_copy_data(&pos, in, size);\n\t\tmemset(pos, 0, sizeof(size_t));\n\t}\n}\n\nbool calldata_get_string(const calldata_t *data, const char *name, const char **str)\n{\n\tuint8_t *pos;\n\tif (!data || !name || !*name)\n\t\treturn false;\n\n\tif (!cd_getparam(data, name, &pos))\n\t\treturn false;\n\n\t*str = cd_serialize_string(&pos);\n\treturn true;\n}\n"
  },
  {
    "path": "libobs/callback/calldata.h",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#pragma once\n\n#include <string.h>\n#include \"../util/c99defs.h\"\n#include \"../util/bmem.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Procedure call data structure\n *\n *   This is used to store parameters (and return value) sent to/from signals,\n * procedures, and callbacks.\n */\n\nenum call_param_type {\n\tCALL_PARAM_TYPE_VOID,\n\tCALL_PARAM_TYPE_INT,\n\tCALL_PARAM_TYPE_FLOAT,\n\tCALL_PARAM_TYPE_BOOL,\n\tCALL_PARAM_TYPE_PTR,\n\tCALL_PARAM_TYPE_STRING\n};\n\n#define CALL_PARAM_IN (1 << 0)\n#define CALL_PARAM_OUT (1 << 1)\n\nstruct calldata {\n\tuint8_t *stack;\n\tsize_t size;     /* size of the stack, in bytes */\n\tsize_t capacity; /* capacity of the stack, in bytes */\n\tbool fixed;      /* fixed size (using call stack) */\n};\n\ntypedef struct calldata calldata_t;\n\nstatic inline void calldata_init(struct calldata *data)\n{\n\tmemset(data, 0, sizeof(struct calldata));\n}\n\nstatic inline void calldata_clear(struct calldata *data);\n\nstatic inline void calldata_init_fixed(struct calldata *data, uint8_t *stack, size_t size)\n{\n\tdata->stack = stack;\n\tdata->capacity = size;\n\tdata->fixed = true;\n\tdata->size = 0;\n\tcalldata_clear(data);\n}\n\nstatic inline void calldata_free(struct calldata *data)\n{\n\tif (!data->fixed)\n\t\tbfree(data->stack);\n}\n\nEXPORT bool calldata_get_data(const calldata_t *data, const char *name, void *out, size_t size);\nEXPORT void calldata_set_data(calldata_t *data, const char *name, const void *in, size_t new_size);\n\nstatic inline void calldata_clear(struct calldata *data)\n{\n\tif (data->stack) {\n\t\tdata->size = sizeof(size_t);\n\t\tmemset(data->stack, 0, sizeof(size_t));\n\t}\n}\n\nstatic inline calldata_t *calldata_create(void)\n{\n\treturn (calldata_t *)bzalloc(sizeof(struct calldata));\n}\n\nstatic inline void calldata_destroy(calldata_t *cd)\n{\n\tcalldata_free(cd);\n\tbfree(cd);\n}\n\n/* ------------------------------------------------------------------------- */\n/* NOTE: 'get' functions return true only if parameter exists, and is the\n *       same type.  They return false otherwise. */\n\nstatic inline bool calldata_get_int(const calldata_t *data, const char *name, long long *val)\n{\n\treturn calldata_get_data(data, name, val, sizeof(*val));\n}\n\nstatic inline bool calldata_get_float(const calldata_t *data, const char *name, double *val)\n{\n\treturn calldata_get_data(data, name, val, sizeof(*val));\n}\n\nstatic inline bool calldata_get_bool(const calldata_t *data, const char *name, bool *val)\n{\n\treturn calldata_get_data(data, name, val, sizeof(*val));\n}\n\nstatic inline bool calldata_get_ptr(const calldata_t *data, const char *name, void *p_ptr)\n{\n\treturn calldata_get_data(data, name, p_ptr, sizeof(p_ptr));\n}\n\nEXPORT bool calldata_get_string(const calldata_t *data, const char *name, const char **str);\n\n/* ------------------------------------------------------------------------- */\n/* call if you know your data is valid */\n\nstatic inline long long calldata_int(const calldata_t *data, const char *name)\n{\n\tlong long val = 0;\n\tcalldata_get_int(data, name, &val);\n\treturn val;\n}\n\nstatic inline double calldata_float(const calldata_t *data, const char *name)\n{\n\tdouble val = 0.0;\n\tcalldata_get_float(data, name, &val);\n\treturn val;\n}\n\nstatic inline bool calldata_bool(const calldata_t *data, const char *name)\n{\n\tbool val = false;\n\tcalldata_get_bool(data, name, &val);\n\treturn val;\n}\n\nstatic inline void *calldata_ptr(const calldata_t *data, const char *name)\n{\n\tvoid *val = NULL;\n\tcalldata_get_ptr(data, name, &val);\n\treturn val;\n}\n\nstatic inline const char *calldata_string(const calldata_t *data, const char *name)\n{\n\tconst char *val = NULL;\n\tcalldata_get_string(data, name, &val);\n\treturn val;\n}\n\n/* ------------------------------------------------------------------------- */\n\nstatic inline void calldata_set_int(calldata_t *data, const char *name, long long val)\n{\n\tcalldata_set_data(data, name, &val, sizeof(val));\n}\n\nstatic inline void calldata_set_float(calldata_t *data, const char *name, double val)\n{\n\tcalldata_set_data(data, name, &val, sizeof(val));\n}\n\nstatic inline void calldata_set_bool(calldata_t *data, const char *name, bool val)\n{\n\tcalldata_set_data(data, name, &val, sizeof(val));\n}\n\nstatic inline void calldata_set_ptr(calldata_t *data, const char *name, void *ptr)\n{\n\tcalldata_set_data(data, name, &ptr, sizeof(ptr));\n}\n\nstatic inline void calldata_set_string(calldata_t *data, const char *name, const char *str)\n{\n\tif (str)\n\t\tcalldata_set_data(data, name, str, strlen(str) + 1);\n\telse\n\t\tcalldata_set_data(data, name, NULL, 0);\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/callback/decl.c",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"../util/cf-parser.h\"\n#include \"decl.h\"\n\nstatic inline void err_specifier_exists(struct cf_parser *cfp, const char *storage)\n{\n\tcf_adderror(cfp, \"'$1' specifier already exists\", LEX_ERROR, storage, NULL, NULL);\n}\n\nstatic inline void err_reserved_name(struct cf_parser *cfp, const char *name)\n{\n\tcf_adderror(cfp, \"'$1' is a reserved name\", LEX_ERROR, name, NULL, NULL);\n}\n\nstatic inline void err_existing_name(struct cf_parser *cfp, const char *name)\n{\n\tcf_adderror(cfp, \"'$1' already exists\", LEX_ERROR, name, NULL, NULL);\n}\n\nstatic bool is_in_out_specifier(struct cf_parser *cfp, struct strref *name, uint32_t *type)\n{\n\tif (strref_cmp(name, \"in\") == 0) {\n\t\tif (*type & CALL_PARAM_IN)\n\t\t\terr_specifier_exists(cfp, \"in\");\n\n\t\t*type |= CALL_PARAM_IN;\n\n\t} else if (strref_cmp(name, \"out\") == 0) {\n\t\tif (*type & CALL_PARAM_OUT)\n\t\t\terr_specifier_exists(cfp, \"out\");\n\n\t\t*type |= CALL_PARAM_OUT;\n\n\t} else {\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n#define TYPE_OR_STORAGE \"type or storage specifier\"\n\nstatic bool get_type(struct strref *ref, enum call_param_type *type, bool is_return)\n{\n\tif (strref_cmp(ref, \"int\") == 0)\n\t\t*type = CALL_PARAM_TYPE_INT;\n\telse if (strref_cmp(ref, \"float\") == 0)\n\t\t*type = CALL_PARAM_TYPE_FLOAT;\n\telse if (strref_cmp(ref, \"bool\") == 0)\n\t\t*type = CALL_PARAM_TYPE_BOOL;\n\telse if (strref_cmp(ref, \"ptr\") == 0)\n\t\t*type = CALL_PARAM_TYPE_PTR;\n\telse if (strref_cmp(ref, \"string\") == 0)\n\t\t*type = CALL_PARAM_TYPE_STRING;\n\telse if (is_return && strref_cmp(ref, \"void\") == 0)\n\t\t*type = CALL_PARAM_TYPE_VOID;\n\telse\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic bool is_reserved_name(const char *str)\n{\n\treturn (strcmp(str, \"int\") == 0) || (strcmp(str, \"float\") == 0) || (strcmp(str, \"bool\") == 0) ||\n\t       (strcmp(str, \"ptr\") == 0) || (strcmp(str, \"string\") == 0) || (strcmp(str, \"void\") == 0) ||\n\t       (strcmp(str, \"return\") == 0);\n}\n\nstatic bool name_exists(struct decl_info *decl, const char *name)\n{\n\tfor (size_t i = 0; i < decl->params.num; i++) {\n\t\tconst char *param_name = decl->params.array[i].name;\n\n\t\tif (strcmp(name, param_name) == 0)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic int parse_param(struct cf_parser *cfp, struct decl_info *decl)\n{\n\tstruct strref ref;\n\tint code;\n\tstruct decl_param param = {0};\n\n\t/* get storage specifiers */\n\tcode = cf_next_name_ref(cfp, &ref, TYPE_OR_STORAGE, \",\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\twhile (is_in_out_specifier(cfp, &ref, &param.flags)) {\n\t\tcode = cf_next_name_ref(cfp, &ref, TYPE_OR_STORAGE, \",\");\n\t\tif (code != PARSE_SUCCESS)\n\t\t\treturn code;\n\t}\n\n\t/* parameters not marked with specifiers are input parameters */\n\tif (param.flags == 0)\n\t\tparam.flags = CALL_PARAM_IN;\n\n\tif (!get_type(&ref, &param.type, false)) {\n\t\tcf_adderror_expecting(cfp, \"type\");\n\t\tcf_go_to_token(cfp, \",\", \")\");\n\t\treturn PARSE_CONTINUE;\n\t}\n\n\t/* name */\n\tcode = cf_next_name(cfp, &param.name, \"parameter name\", \",\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tif (name_exists(decl, param.name))\n\t\terr_existing_name(cfp, param.name);\n\n\tif (is_reserved_name(param.name))\n\t\terr_reserved_name(cfp, param.name);\n\n\tda_push_back(decl->params, &param);\n\treturn PARSE_SUCCESS;\n}\n\nstatic void parse_params(struct cf_parser *cfp, struct decl_info *decl)\n{\n\tstruct cf_token peek;\n\tint code;\n\n\tif (!cf_peek_valid_token(cfp, &peek))\n\t\treturn;\n\n\twhile (peek.type == CFTOKEN_NAME) {\n\t\tcode = parse_param(cfp, decl);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn;\n\n\t\tif (code != PARSE_CONTINUE && !cf_next_valid_token(cfp))\n\t\t\treturn;\n\n\t\tif (cf_token_is(cfp, \")\"))\n\t\t\tbreak;\n\t\telse if (cf_token_should_be(cfp, \",\", \",\", NULL) == PARSE_EOF)\n\t\t\treturn;\n\n\t\tif (!cf_peek_valid_token(cfp, &peek))\n\t\t\treturn;\n\t}\n\n\tif (!cf_token_is(cfp, \")\"))\n\t\tcf_next_token_should_be(cfp, \")\", NULL, NULL);\n}\n\nstatic void print_errors(struct cf_parser *cfp, const char *decl_string)\n{\n\tchar *errors = error_data_buildstring(&cfp->error_list);\n\n\tif (errors) {\n\t\tblog(LOG_WARNING, \"Errors/warnings for '%s':\\n\\n%s\", decl_string, errors);\n\n\t\tbfree(errors);\n\t}\n}\n\nbool parse_decl_string(struct decl_info *decl, const char *decl_string)\n{\n\tstruct cf_parser cfp;\n\tstruct strref ret_type;\n\tstruct decl_param ret_param = {0};\n\tint code;\n\tbool success = false;\n\n\tdecl->decl_string = decl_string;\n\tret_param.flags = CALL_PARAM_OUT;\n\n\tcf_parser_init(&cfp);\n\tif (!cf_parser_parse(&cfp, decl_string, \"declaration\"))\n\t\tgoto fail;\n\n\tcode = cf_get_name_ref(&cfp, &ret_type, \"return type\", NULL);\n\tif (code == PARSE_EOF)\n\t\tgoto fail;\n\n\tif (!get_type(&ret_type, &ret_param.type, true))\n\t\tcf_adderror_expecting(&cfp, \"return type\");\n\n\tcode = cf_next_name(&cfp, &decl->name, \"function name\", \"(\");\n\tif (code == PARSE_EOF)\n\t\tgoto fail;\n\n\tif (is_reserved_name(decl->name))\n\t\terr_reserved_name(&cfp, decl->name);\n\n\tcode = cf_next_token_should_be(&cfp, \"(\", \"(\", NULL);\n\tif (code == PARSE_EOF)\n\t\tgoto fail;\n\n\tparse_params(&cfp, decl);\n\tsuccess = true;\n\nfail:\n\tif (error_data_has_errors(&cfp.error_list))\n\t\tsuccess = false;\n\n\tif (success && ret_param.type != CALL_PARAM_TYPE_VOID) {\n\t\tret_param.name = bstrdup(\"return\");\n\t\tda_push_back(decl->params, &ret_param);\n\t}\n\n\tif (!success)\n\t\tdecl_info_free(decl);\n\n\tprint_errors(&cfp, decl_string);\n\n\tcf_parser_free(&cfp);\n\treturn success;\n}\n"
  },
  {
    "path": "libobs/callback/decl.h",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#pragma once\n\n#include \"calldata.h\"\n#include \"../util/darray.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct decl_param {\n\tchar *name;\n\tenum call_param_type type;\n\tuint32_t flags;\n};\n\nstatic inline void decl_param_free(struct decl_param *param)\n{\n\tif (param->name)\n\t\tbfree(param->name);\n\tmemset(param, 0, sizeof(struct decl_param));\n}\n\nstruct decl_info {\n\tchar *name;\n\tconst char *decl_string;\n\tDARRAY(struct decl_param) params;\n};\n\nstatic inline void decl_info_free(struct decl_info *decl)\n{\n\tif (decl) {\n\t\tfor (size_t i = 0; i < decl->params.num; i++)\n\t\t\tdecl_param_free(decl->params.array + i);\n\t\tda_free(decl->params);\n\n\t\tbfree(decl->name);\n\t\tmemset(decl, 0, sizeof(struct decl_info));\n\t}\n}\n\nEXPORT bool parse_decl_string(struct decl_info *decl, const char *decl_string);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/callback/proc.c",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"../util/darray.h\"\n#include \"../util/threading.h\"\n\n#include \"decl.h\"\n#include \"proc.h\"\n\nstruct proc_info {\n\tstruct decl_info func;\n\tvoid *data;\n\tproc_handler_proc_t callback;\n};\n\nstatic inline void proc_info_free(struct proc_info *pi)\n{\n\tdecl_info_free(&pi->func);\n}\n\nstruct proc_handler {\n\t/* TODO: replace with hash table lookup? */\n\tpthread_mutex_t mutex;\n\tDARRAY(struct proc_info) procs;\n};\n\nstatic struct proc_info *getproc(proc_handler_t *handler, const char *name)\n{\n\tfor (size_t i = 0; i < handler->procs.num; i++) {\n\t\tstruct proc_info *info = handler->procs.array + i;\n\n\t\tif (strcmp(info->func.name, name) == 0) {\n\t\t\treturn info;\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\n/* ------------------------------------------------------------------------- */\n\nproc_handler_t *proc_handler_create(void)\n{\n\tstruct proc_handler *handler = bmalloc(sizeof(struct proc_handler));\n\n\tif (pthread_mutex_init_recursive(&handler->mutex) != 0) {\n\t\tblog(LOG_ERROR, \"Couldn't create proc_handler mutex\");\n\t\tbfree(handler);\n\t\treturn NULL;\n\t}\n\n\tda_init(handler->procs);\n\treturn handler;\n}\n\nvoid proc_handler_destroy(proc_handler_t *handler)\n{\n\tif (!handler)\n\t\treturn;\n\n\tfor (size_t i = 0; i < handler->procs.num; i++)\n\t\tproc_info_free(handler->procs.array + i);\n\n\tda_free(handler->procs);\n\tpthread_mutex_destroy(&handler->mutex);\n\tbfree(handler);\n}\n\nvoid proc_handler_add(proc_handler_t *handler, const char *decl_string, proc_handler_proc_t proc, void *data)\n{\n\tif (!handler)\n\t\treturn;\n\n\tstruct proc_info pi;\n\tmemset(&pi, 0, sizeof(struct proc_info));\n\n\tif (!parse_decl_string(&pi.func, decl_string)) {\n\t\tblog(LOG_ERROR, \"Function declaration invalid: %s\", decl_string);\n\t\treturn;\n\t}\n\n\tpi.callback = proc;\n\tpi.data = data;\n\n\tpthread_mutex_lock(&handler->mutex);\n\n\tstruct proc_info *existing = getproc(handler, pi.func.name);\n\tif (existing) {\n\t\tblog(LOG_WARNING, \"Procedure '%s' already exists\", pi.func.name);\n\t\tproc_info_free(&pi);\n\t} else {\n\t\tda_push_back(handler->procs, &pi);\n\t}\n\n\tpthread_mutex_unlock(&handler->mutex);\n}\n\nbool proc_handler_call(proc_handler_t *handler, const char *name, calldata_t *params)\n{\n\tif (!handler)\n\t\treturn false;\n\n\tpthread_mutex_lock(&handler->mutex);\n\tstruct proc_info *info = getproc(handler, name);\n\tstruct proc_info info_copy;\n\tif (info)\n\t\tinfo_copy = *info;\n\tpthread_mutex_unlock(&handler->mutex);\n\n\tif (!info)\n\t\treturn false;\n\n\tinfo_copy.callback(info_copy.data, params);\n\treturn true;\n}\n"
  },
  {
    "path": "libobs/callback/proc.h",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n\n#include \"calldata.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Procedure handler\n *\n *   This handler is used to allow access to one or more procedures that can be\n *   added and called without having to have direct access to declarations or\n *   procedure callback pointers.\n */\n\nstruct proc_handler;\ntypedef struct proc_handler proc_handler_t;\ntypedef void (*proc_handler_proc_t)(void *, calldata_t *);\n\nEXPORT proc_handler_t *proc_handler_create(void);\nEXPORT void proc_handler_destroy(proc_handler_t *handler);\n\nEXPORT void proc_handler_add(proc_handler_t *handler, const char *decl_string, proc_handler_proc_t proc, void *data);\n\n/**\n * Calls a function in a procedure handler.  Returns false if the named\n * procedure is not found.\n */\nEXPORT bool proc_handler_call(proc_handler_t *handler, const char *name, calldata_t *params);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/callback/signal.c",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"../util/darray.h\"\n#include \"../util/threading.h\"\n\n#include \"decl.h\"\n#include \"signal.h\"\n\nstruct signal_callback {\n\tsignal_callback_t callback;\n\tvoid *data;\n\tbool remove;\n\tbool keep_ref;\n};\n\nstruct signal_info {\n\tstruct decl_info func;\n\tDARRAY(struct signal_callback) callbacks;\n\tpthread_mutex_t mutex;\n\tbool signalling;\n\n\tstruct signal_info *next;\n};\n\nstatic inline struct signal_info *signal_info_create(struct decl_info *info)\n{\n\tstruct signal_info *si = bmalloc(sizeof(struct signal_info));\n\tsi->func = *info;\n\tsi->next = NULL;\n\tsi->signalling = false;\n\tda_init(si->callbacks);\n\n\tif (pthread_mutex_init_recursive(&si->mutex) != 0) {\n\t\tblog(LOG_ERROR, \"Could not create signal\");\n\n\t\tdecl_info_free(&si->func);\n\t\tbfree(si);\n\t\treturn NULL;\n\t}\n\n\treturn si;\n}\n\nstatic inline void signal_info_destroy(struct signal_info *si)\n{\n\tif (si) {\n\t\tpthread_mutex_destroy(&si->mutex);\n\t\tdecl_info_free(&si->func);\n\t\tda_free(si->callbacks);\n\t\tbfree(si);\n\t}\n}\n\nstatic inline size_t signal_get_callback_idx(struct signal_info *si, signal_callback_t callback, void *data)\n{\n\tfor (size_t i = 0; i < si->callbacks.num; i++) {\n\t\tstruct signal_callback *sc = si->callbacks.array + i;\n\n\t\tif (sc->callback == callback && sc->data == data)\n\t\t\treturn i;\n\t}\n\n\treturn DARRAY_INVALID;\n}\n\nstruct global_callback_info {\n\tglobal_signal_callback_t callback;\n\tvoid *data;\n\tlong signaling;\n\tbool remove;\n};\n\nstruct signal_handler {\n\tstruct signal_info *first;\n\tpthread_mutex_t mutex;\n\tvolatile long refs;\n\n\tDARRAY(struct global_callback_info) global_callbacks;\n\tpthread_mutex_t global_callbacks_mutex;\n};\n\nstatic struct signal_info *getsignal(signal_handler_t *handler, const char *name, struct signal_info **p_last)\n{\n\tstruct signal_info *signal, *last = NULL;\n\n\tsignal = handler->first;\n\twhile (signal != NULL) {\n\t\tif (strcmp(signal->func.name, name) == 0)\n\t\t\tbreak;\n\n\t\tlast = signal;\n\t\tsignal = signal->next;\n\t}\n\n\tif (p_last)\n\t\t*p_last = last;\n\treturn signal;\n}\n\n/* ------------------------------------------------------------------------- */\n\nsignal_handler_t *signal_handler_create(void)\n{\n\tstruct signal_handler *handler = bzalloc(sizeof(struct signal_handler));\n\thandler->first = NULL;\n\thandler->refs = 1;\n\n\tif (pthread_mutex_init(&handler->mutex, NULL) != 0) {\n\t\tblog(LOG_ERROR, \"Couldn't create signal handler mutex!\");\n\t\tbfree(handler);\n\t\treturn NULL;\n\t}\n\tif (pthread_mutex_init_recursive(&handler->global_callbacks_mutex) != 0) {\n\t\tblog(LOG_ERROR, \"Couldn't create signal handler global \"\n\t\t\t\t\"callbacks mutex!\");\n\t\tpthread_mutex_destroy(&handler->mutex);\n\t\tbfree(handler);\n\t\treturn NULL;\n\t}\n\n\treturn handler;\n}\n\nstatic void signal_handler_actually_destroy(signal_handler_t *handler)\n{\n\tstruct signal_info *sig = handler->first;\n\twhile (sig != NULL) {\n\t\tstruct signal_info *next = sig->next;\n\t\tsignal_info_destroy(sig);\n\t\tsig = next;\n\t}\n\n\tda_free(handler->global_callbacks);\n\tpthread_mutex_destroy(&handler->global_callbacks_mutex);\n\tpthread_mutex_destroy(&handler->mutex);\n\tbfree(handler);\n}\n\nvoid signal_handler_destroy(signal_handler_t *handler)\n{\n\tif (handler && os_atomic_dec_long(&handler->refs) == 0) {\n\t\tsignal_handler_actually_destroy(handler);\n\t}\n}\n\nbool signal_handler_add(signal_handler_t *handler, const char *signal_decl)\n{\n\tstruct decl_info func = {0};\n\tstruct signal_info *sig, *last;\n\tbool success = true;\n\n\tif (!parse_decl_string(&func, signal_decl)) {\n\t\tblog(LOG_ERROR, \"Signal declaration invalid: %s\", signal_decl);\n\t\treturn false;\n\t}\n\n\tpthread_mutex_lock(&handler->mutex);\n\n\tsig = getsignal(handler, func.name, &last);\n\tif (sig) {\n\t\tblog(LOG_WARNING, \"Signal declaration '%s' exists\", func.name);\n\t\tdecl_info_free(&func);\n\t\tsuccess = false;\n\t} else {\n\t\tsig = signal_info_create(&func);\n\t\tif (!last)\n\t\t\thandler->first = sig;\n\t\telse\n\t\t\tlast->next = sig;\n\t}\n\n\tpthread_mutex_unlock(&handler->mutex);\n\n\treturn success;\n}\n\nstatic void signal_handler_connect_internal(signal_handler_t *handler, const char *signal, signal_callback_t callback,\n\t\t\t\t\t    void *data, bool keep_ref)\n{\n\tstruct signal_info *sig, *last;\n\tstruct signal_callback cb_data = {callback, data, false, keep_ref};\n\tsize_t idx;\n\n\tif (!handler)\n\t\treturn;\n\n\tpthread_mutex_lock(&handler->mutex);\n\tsig = getsignal(handler, signal, &last);\n\tpthread_mutex_unlock(&handler->mutex);\n\n\tif (!sig) {\n\t\tblog(LOG_WARNING,\n\t\t     \"signal_handler_connect: \"\n\t\t     \"signal '%s' not found\",\n\t\t     signal);\n\t\treturn;\n\t}\n\n\t/* -------------- */\n\n\tpthread_mutex_lock(&sig->mutex);\n\n\tif (keep_ref)\n\t\tos_atomic_inc_long(&handler->refs);\n\n\tidx = signal_get_callback_idx(sig, callback, data);\n\tif (keep_ref || idx == DARRAY_INVALID)\n\t\tda_push_back(sig->callbacks, &cb_data);\n\n\tpthread_mutex_unlock(&sig->mutex);\n}\n\nvoid signal_handler_connect(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data)\n{\n\tsignal_handler_connect_internal(handler, signal, callback, data, false);\n}\n\nvoid signal_handler_connect_ref(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data)\n{\n\tsignal_handler_connect_internal(handler, signal, callback, data, true);\n}\n\nstatic inline struct signal_info *getsignal_locked(signal_handler_t *handler, const char *name)\n{\n\tstruct signal_info *sig;\n\n\tif (!handler)\n\t\treturn NULL;\n\n\tpthread_mutex_lock(&handler->mutex);\n\tsig = getsignal(handler, name, NULL);\n\tpthread_mutex_unlock(&handler->mutex);\n\n\treturn sig;\n}\n\nvoid signal_handler_disconnect(signal_handler_t *handler, const char *signal, signal_callback_t callback, void *data)\n{\n\tstruct signal_info *sig = getsignal_locked(handler, signal);\n\tbool keep_ref = false;\n\tsize_t idx;\n\n\tif (!sig)\n\t\treturn;\n\n\tpthread_mutex_lock(&sig->mutex);\n\n\tidx = signal_get_callback_idx(sig, callback, data);\n\tif (idx != DARRAY_INVALID) {\n\t\tif (sig->signalling) {\n\t\t\tsig->callbacks.array[idx].remove = true;\n\t\t} else {\n\t\t\tkeep_ref = sig->callbacks.array[idx].keep_ref;\n\t\t\tda_erase(sig->callbacks, idx);\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&sig->mutex);\n\n\tif (keep_ref && os_atomic_dec_long(&handler->refs) == 0) {\n\t\tsignal_handler_actually_destroy(handler);\n\t}\n}\n\nstatic THREAD_LOCAL struct signal_callback *current_signal_cb = NULL;\nstatic THREAD_LOCAL struct global_callback_info *current_global_cb = NULL;\n\nvoid signal_handler_remove_current(void)\n{\n\tif (current_signal_cb)\n\t\tcurrent_signal_cb->remove = true;\n\telse if (current_global_cb)\n\t\tcurrent_global_cb->remove = true;\n}\n\nvoid signal_handler_signal(signal_handler_t *handler, const char *signal, calldata_t *params)\n{\n\tstruct signal_info *sig = getsignal_locked(handler, signal);\n\tlong remove_refs = 0;\n\n\tif (!sig)\n\t\treturn;\n\n\tpthread_mutex_lock(&sig->mutex);\n\tsig->signalling = true;\n\n\tfor (size_t i = 0; i < sig->callbacks.num; i++) {\n\t\tstruct signal_callback *cb = sig->callbacks.array + i;\n\t\tif (!cb->remove) {\n\t\t\tcurrent_signal_cb = cb;\n\t\t\tcb->callback(cb->data, params);\n\t\t\tcurrent_signal_cb = NULL;\n\t\t}\n\t}\n\n\tfor (size_t i = sig->callbacks.num; i > 0; i--) {\n\t\tstruct signal_callback *cb = sig->callbacks.array + i - 1;\n\t\tif (cb->remove) {\n\t\t\tif (cb->keep_ref)\n\t\t\t\tremove_refs++;\n\n\t\t\tda_erase(sig->callbacks, i - 1);\n\t\t}\n\t}\n\n\tsig->signalling = false;\n\tpthread_mutex_unlock(&sig->mutex);\n\n\tpthread_mutex_lock(&handler->global_callbacks_mutex);\n\n\tif (handler->global_callbacks.num) {\n\t\tfor (size_t i = 0; i < handler->global_callbacks.num; i++) {\n\t\t\tstruct global_callback_info *cb = handler->global_callbacks.array + i;\n\n\t\t\tif (!cb->remove) {\n\t\t\t\tcb->signaling++;\n\t\t\t\tcurrent_global_cb = cb;\n\t\t\t\tcb->callback(cb->data, signal, params);\n\t\t\t\tcurrent_global_cb = NULL;\n\t\t\t\tcb->signaling--;\n\t\t\t}\n\t\t}\n\n\t\tfor (size_t i = handler->global_callbacks.num; i > 0; i--) {\n\t\t\tstruct global_callback_info *cb = handler->global_callbacks.array + (i - 1);\n\n\t\t\tif (cb->remove && !cb->signaling)\n\t\t\t\tda_erase(handler->global_callbacks, i - 1);\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&handler->global_callbacks_mutex);\n\n\tif (remove_refs) {\n\t\tos_atomic_set_long(&handler->refs, os_atomic_load_long(&handler->refs) - remove_refs);\n\t}\n}\n\nvoid signal_handler_connect_global(signal_handler_t *handler, global_signal_callback_t callback, void *data)\n{\n\tstruct global_callback_info cb_data = {callback, data, 0, false};\n\tsize_t idx;\n\n\tif (!handler || !callback)\n\t\treturn;\n\n\tpthread_mutex_lock(&handler->global_callbacks_mutex);\n\n\tidx = da_find(handler->global_callbacks, &cb_data, 0);\n\tif (idx == DARRAY_INVALID)\n\t\tda_push_back(handler->global_callbacks, &cb_data);\n\n\tpthread_mutex_unlock(&handler->global_callbacks_mutex);\n}\n\nvoid signal_handler_disconnect_global(signal_handler_t *handler, global_signal_callback_t callback, void *data)\n{\n\tstruct global_callback_info cb_data = {callback, data, 0, false};\n\tsize_t idx;\n\n\tif (!handler || !callback)\n\t\treturn;\n\n\tpthread_mutex_lock(&handler->global_callbacks_mutex);\n\n\tidx = da_find(handler->global_callbacks, &cb_data, 0);\n\tif (idx != DARRAY_INVALID) {\n\t\tstruct global_callback_info *cb = handler->global_callbacks.array + idx;\n\n\t\tif (cb->signaling)\n\t\t\tcb->remove = true;\n\t\telse\n\t\t\tda_erase(handler->global_callbacks, idx);\n\t}\n\n\tpthread_mutex_unlock(&handler->global_callbacks_mutex);\n}\n"
  },
  {
    "path": "libobs/callback/signal.h",
    "content": "/*\n * Copyright (c) 2023 Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n\n#include \"calldata.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Signal handler\n *\n *   This is used to create a signal handler which can broadcast events\n * to one or more callbacks connected to a signal.\n */\n\nstruct signal_handler;\ntypedef struct signal_handler signal_handler_t;\ntypedef void (*global_signal_callback_t)(void *, const char *, calldata_t *);\ntypedef void (*signal_callback_t)(void *, calldata_t *);\n\nEXPORT signal_handler_t *signal_handler_create(void);\nEXPORT void signal_handler_destroy(signal_handler_t *handler);\n\nEXPORT bool signal_handler_add(signal_handler_t *handler, const char *signal_decl);\n\nstatic inline bool signal_handler_add_array(signal_handler_t *handler, const char **signal_decls)\n{\n\tbool success = true;\n\tif (!signal_decls)\n\t\treturn false;\n\n\twhile (*signal_decls)\n\t\tif (!signal_handler_add(handler, *(signal_decls++)))\n\t\t\tsuccess = false;\n\n\treturn success;\n}\n\nEXPORT void signal_handler_connect(signal_handler_t *handler, const char *signal, signal_callback_t callback,\n\t\t\t\t   void *data);\nEXPORT void signal_handler_connect_ref(signal_handler_t *handler, const char *signal, signal_callback_t callback,\n\t\t\t\t       void *data);\nEXPORT void signal_handler_disconnect(signal_handler_t *handler, const char *signal, signal_callback_t callback,\n\t\t\t\t      void *data);\n\nEXPORT void signal_handler_connect_global(signal_handler_t *handler, global_signal_callback_t callback, void *data);\nEXPORT void signal_handler_disconnect_global(signal_handler_t *handler, global_signal_callback_t callback, void *data);\n\nEXPORT void signal_handler_remove_current(void);\n\nEXPORT void signal_handler_signal(signal_handler_t *handler, const char *signal, calldata_t *params);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/cmake/libobsConfig.cmake.in",
    "content": "@PACKAGE_INIT@\n\ninclude(CMakeFindDependencyMacro)\n\nlist(APPEND CMAKE_MODULE_PATH \"${CMAKE_CURRENT_LIST_DIR}/finders\")\n\nif(MSVC)\n  find_dependency(w32-pthreads REQUIRED)\nendif()\nfind_dependency(SIMDe REQUIRED)\nfind_dependency(Threads REQUIRED)\n\ninclude(\"${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake\")\ncheck_required_components(\"@PROJECT_NAME@\")\n\nset_property(TARGET OBS::libobs APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS HAVE_OBSCONFIG_H)\n"
  },
  {
    "path": "libobs/cmake/linux/libobs.pc.in",
    "content": "prefix=@CMAKE_INSTALL_PREFIX@\nexec_prefix=${prefix}\nlibdir=${prefix}/lib\nincludedir=${prefix}/include\n\nName: libobs\nDescription: OBS Studio core compositor library\nVersion: @OBS_VERSION_CANONICAL@\n\nRequires:\nLibs: -L${libdir} -lobs\nLibs.private: -pthread -lm\nCflags: -I${includedir} -std=gnu@CMAKE_C_STANDARD@ -fPIC -fvisibility=hidden -fopenmp-simd -Werror -DHAVE_OBSCONFIG_H\n"
  },
  {
    "path": "libobs/cmake/macos/entitlements.plist",
    "content": "<!--?xml version=\"1.0\" encoding=\"UTF-8\"?-->\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n    <dict>\n        <key>com.apple.security.cs.disable-library-validation</key>\n        <true/>\n    </dict>\n</plist>\n"
  },
  {
    "path": "libobs/cmake/obs-version.cmake",
    "content": "add_library(libobs-version OBJECT)\nadd_library(OBS::libobs-version ALIAS libobs-version)\n\nconfigure_file(obsversion.c.in obsversion.c @ONLY)\n\ntarget_sources(libobs-version PRIVATE obsversion.c PUBLIC obsversion.h)\n\ntarget_include_directories(libobs-version PUBLIC \"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>\")\n\nset_property(TARGET libobs-version PROPERTY FOLDER core)\n"
  },
  {
    "path": "libobs/cmake/os-freebsd.cmake",
    "content": "find_package(LibUUID REQUIRED)\nfind_package(X11 REQUIRED)\nfind_package(X11_XCB REQUIRED)\nfind_package(XCB REQUIRED XCB OPTIONAL_COMPONENTS XINPUT)\nfind_package(Gio)\nfind_package(Sysinfo REQUIRED)\n\nset(CMAKE_M_LIBS \"\")\ninclude(CheckCSourceCompiles)\nset(LIBM_TEST_SOURCE \"#include<math.h>\\nfloat f; int main(){sqrt(f);return 0;}\")\ncheck_c_source_compiles(\"${LIBM_TEST_SOURCE}\" HAVE_MATH_IN_STD_LIB)\n\nset(CMAKE_REQUIRED_INCLUDES \"/usr/local/include\")\nset(UUID_TEST_SOURCE \"#include<uuid/uuid.h>\\nint main(){return 0;}\")\ncheck_c_source_compiles(\"${UUID_TEST_SOURCE}\" HAVE_UUID_HEADER)\n\nif(NOT HAVE_UUID_HEADER)\n  message(FATAL_ERROR \"Required system header <uuid/uuid.h> not found.\")\nendif()\n\ntarget_sources(\n  libobs\n  PRIVATE\n    obs-nix-platform.c\n    obs-nix-platform.h\n    obs-nix-x11.c\n    obs-nix.c\n    util/pipe-posix.c\n    util/platform-nix.c\n    util/threading-posix.c\n    util/threading-posix.h\n)\n\ntarget_compile_definitions(\n  libobs\n  PRIVATE\n    OBS_INSTALL_PREFIX=\"${OBS_INSTALL_PREFIX}\"\n    $<$<COMPILE_LANG_AND_ID:C,GNU>:ENABLE_DARRAY_TYPE_TEST>\n    $<$<COMPILE_LANG_AND_ID:CXX,GNU>:ENABLE_DARRAY_TYPE_TEST>\n)\n\ntarget_link_libraries(\n  libobs\n  PRIVATE\n    X11::XCB\n    XCB::XCB\n    LibUUID::LibUUID\n    Sysinfo::Sysinfo\n    ${CMAKE_DL_LIBS}\n    $<$<NOT:$<BOOL:${HAVE_MATH_IN_STD_LIB}>>:m>\n    $<$<TARGET_EXISTS:XCB::XINPUT>:XCB::XINPUT>\n)\n\nif(ENABLE_PULSEAUDIO)\n  find_package(PulseAudio REQUIRED)\n\n  target_sources(\n    libobs\n    PRIVATE\n      audio-monitoring/pulse/pulseaudio-enum-devices.c\n      audio-monitoring/pulse/pulseaudio-monitoring-available.c\n      audio-monitoring/pulse/pulseaudio-output.c\n      audio-monitoring/pulse/pulseaudio-wrapper.c\n      audio-monitoring/pulse/pulseaudio-wrapper.h\n  )\n\n  target_link_libraries(libobs PRIVATE PulseAudio::PulseAudio)\n  target_enable_feature(libobs \"PulseAudio audio monitoring (FreeBSD)\")\nelse()\n  target_sources(libobs PRIVATE audio-monitoring/null/null-audio-monitoring.c)\n  target_disable_feature(libobs \"PulseAudio audio monitoring (FreeBSD)\")\nendif()\n\nif(TARGET gio::gio)\n  target_sources(libobs PRIVATE util/platform-nix-dbus.c util/platform-nix-portal.c)\n  target_link_libraries(libobs PRIVATE gio::gio)\nendif()\n\nif(ENABLE_WAYLAND)\n  find_package(Wayland REQUIRED Client)\n  find_package(Xkbcommon REQUIRED)\n\n  target_sources(libobs PRIVATE obs-nix-wayland.c)\n  target_link_libraries(libobs PRIVATE Wayland::Client xkbcommon::xkbcommon)\n  target_enable_feature(libobs \"Wayland compositor support (FreeBSD)\")\nelse()\n  target_disable_feature(libobs \"Wayland compositor support (FreebSD)\")\nendif()\n\nset_target_properties(libobs PROPERTIES OUTPUT_NAME obs)\n"
  },
  {
    "path": "libobs/cmake/os-linux.cmake",
    "content": "find_package(LibUUID REQUIRED)\nfind_package(X11 REQUIRED)\nfind_package(X11_XCB REQUIRED)\nfind_package(XCB REQUIRED XCB OPTIONAL_COMPONENTS XINPUT)\nfind_package(Gio)\n\ntarget_sources(\n  libobs\n  PRIVATE\n    obs-nix-platform.c\n    obs-nix-platform.h\n    obs-nix-x11.c\n    obs-nix.c\n    util/pipe-posix.c\n    util/platform-nix.c\n    util/threading-posix.c\n    util/threading-posix.h\n)\n\ntarget_compile_definitions(\n  libobs\n  PRIVATE\n    OBS_INSTALL_PREFIX=\"${OBS_INSTALL_PREFIX}\"\n    $<$<COMPILE_LANG_AND_ID:C,GNU>:ENABLE_DARRAY_TYPE_TEST>\n    $<$<COMPILE_LANG_AND_ID:CXX,GNU>:ENABLE_DARRAY_TYPE_TEST>\n)\n\nif(CMAKE_C_COMPILER_ID STREQUAL GNU)\n  # * Silence type-limits warning in line 292 of libobs/utils/utf8.c\n  if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.3.0)\n    target_compile_options(libobs PRIVATE -Wno-error=type-limits)\n  endif()\nendif()\n\nset(CMAKE_M_LIBS \"\")\ninclude(CheckCSourceCompiles)\nset(LIBM_TEST_SOURCE \"#include<math.h>\\nfloat f; int main(){sqrt(f);return 0;}\")\ncheck_c_source_compiles(\"${LIBM_TEST_SOURCE}\" HAVE_MATH_IN_STD_LIB)\n\nset(UUID_TEST_SOURCE \"#include<uuid/uuid.h>\\nint main(){return 0;}\")\ncheck_c_source_compiles(\"${UUID_TEST_SOURCE}\" HAVE_UUID_HEADER)\n\nif(NOT HAVE_UUID_HEADER)\n  message(FATAL_ERROR \"Required system header <uuid/uuid.h> not found.\")\nendif()\n\ntarget_link_libraries(\n  libobs\n  PRIVATE\n    X11::X11\n    X11::XCB\n    XCB::XCB\n    LibUUID::LibUUID\n    ${CMAKE_DL_LIBS}\n    $<$<NOT:$<BOOL:${HAVE_MATH_IN_STD_LIB}>>:m>\n    $<$<TARGET_EXISTS:XCB::XINPUT>:XCB::XINPUT>\n)\n\nif(ENABLE_PULSEAUDIO)\n  find_package(PulseAudio REQUIRED)\n\n  target_sources(\n    libobs\n    PRIVATE\n      audio-monitoring/pulse/pulseaudio-enum-devices.c\n      audio-monitoring/pulse/pulseaudio-monitoring-available.c\n      audio-monitoring/pulse/pulseaudio-output.c\n      audio-monitoring/pulse/pulseaudio-wrapper.c\n      audio-monitoring/pulse/pulseaudio-wrapper.h\n  )\n\n  target_link_libraries(libobs PRIVATE PulseAudio::PulseAudio)\n  target_enable_feature(libobs \"PulseAudio audio monitoring (Linux)\")\nelse()\n  target_sources(libobs PRIVATE audio-monitoring/null/null-audio-monitoring.c)\n  target_disable_feature(libobs \"PulseAudio audio monitoring (Linux)\")\nendif()\n\nif(TARGET gio::gio)\n  target_sources(libobs PRIVATE util/platform-nix-dbus.c util/platform-nix-portal.c)\n  target_link_libraries(libobs PRIVATE gio::gio)\nendif()\n\nif(ENABLE_WAYLAND)\n  find_package(Wayland REQUIRED Client)\n  find_package(Xkbcommon REQUIRED)\n\n  target_sources(libobs PRIVATE obs-nix-wayland.c)\n  target_link_libraries(libobs PRIVATE Wayland::Client xkbcommon::xkbcommon)\n  target_enable_feature(libobs \"Wayland compositor support (Linux)\")\nelse()\n  target_disable_feature(libobs \"Wayland compositor support (Linux)\")\nendif()\n\nset_target_properties(libobs PROPERTIES OUTPUT_NAME obs)\n"
  },
  {
    "path": "libobs/cmake/os-macos.cmake",
    "content": "target_link_libraries(\n  libobs\n  PRIVATE\n    \"$<LINK_LIBRARY:FRAMEWORK,AppKit.framework>\"\n    \"$<LINK_LIBRARY:FRAMEWORK,AudioToolbox.framework>\"\n    \"$<LINK_LIBRARY:FRAMEWORK,AudioUnit.framework>\"\n    \"$<LINK_LIBRARY:FRAMEWORK,Carbon.framework>\"\n    \"$<LINK_LIBRARY:FRAMEWORK,Cocoa.framework>\"\n    \"$<LINK_LIBRARY:FRAMEWORK,CoreAudio.framework>\"\n    \"$<LINK_LIBRARY:FRAMEWORK,IOKit.framework>\"\n)\n\ntarget_sources(\n  libobs\n  PRIVATE\n    audio-monitoring/osx/coreaudio-enum-devices.c\n    audio-monitoring/osx/coreaudio-monitoring-available.c\n    audio-monitoring/osx/coreaudio-output.c\n    audio-monitoring/osx/mac-helpers.h\n    obs-cocoa.m\n    util/apple/cfstring-utils.h\n    util/pipe-posix.c\n    util/platform-cocoa.m\n    util/platform-nix.c\n    util/threading-posix.c\n    util/threading-posix.h\n)\n\ntarget_compile_options(libobs PUBLIC \"$<$<NOT:$<COMPILE_LANGUAGE:Swift>>:-Wno-strict-prototypes;-Wno-shorten-64-to-32>\")\n\nset_property(SOURCE obs-cocoa.m util/platform-cocoa.m PROPERTY COMPILE_OPTIONS -fobjc-arc)\nset_property(TARGET libobs PROPERTY FRAMEWORK TRUE)\n"
  },
  {
    "path": "libobs/cmake/os-windows.cmake",
    "content": "if(NOT TARGET OBS::obfuscate)\n  add_library(obs-obfuscate INTERFACE)\n  add_library(OBS::obfuscate ALIAS obs-obfuscate)\n  target_sources(obs-obfuscate INTERFACE util/windows/obfuscate.c util/windows/obfuscate.h)\n  target_include_directories(obs-obfuscate INTERFACE \"${CMAKE_CURRENT_SOURCE_DIR}\")\nendif()\n\nif(NOT TARGET OBS::comutils)\n  add_library(obs-comutils INTERFACE)\n  add_library(OBS::COMutils ALIAS obs-comutils)\n  target_sources(obs-comutils INTERFACE util/windows/ComPtr.hpp)\n  target_include_directories(obs-comutils INTERFACE \"${CMAKE_CURRENT_SOURCE_DIR}\")\nendif()\n\nif(NOT TARGET OBS::winhandle)\n  add_library(obs-winhandle INTERFACE)\n  add_library(OBS::winhandle ALIAS obs-winhandle)\n  target_sources(obs-winhandle INTERFACE util/windows/WinHandle.hpp)\n  target_include_directories(obs-winhandle INTERFACE \"${CMAKE_CURRENT_SOURCE_DIR}\")\nendif()\n\nif(NOT TARGET OBS::threading-windows)\n  add_library(obs-threading-windows INTERFACE)\n  add_library(OBS::threading-windows ALIAS obs-threading-windows)\n  target_sources(obs-threading-windows INTERFACE util/threading-windows.h)\n  target_include_directories(obs-threading-windows INTERFACE \"${CMAKE_CURRENT_SOURCE_DIR}\")\nendif()\n\nif(NOT TARGET OBS::w32-pthreads)\n  add_subdirectory(\"${CMAKE_SOURCE_DIR}/deps/w32-pthreads\" \"${CMAKE_BINARY_DIR}/deps/w32-pthreads\")\nendif()\n\nif(NOT OBS_PARENT_ARCHITECTURE STREQUAL CMAKE_VS_PLATFORM_NAME)\n  return()\nendif()\n\nconfigure_file(cmake/windows/obs-module.rc.in libobs.rc)\n\ntarget_sources(\n  libobs\n  PRIVATE\n    audio-monitoring/win32/wasapi-enum-devices.c\n    audio-monitoring/win32/wasapi-monitoring-available.c\n    audio-monitoring/win32/wasapi-output.c\n    audio-monitoring/win32/wasapi-output.h\n    libobs.rc\n    obs-win-crash-handler.c\n    obs-windows.c\n    util/pipe-windows.c\n    util/platform-windows.c\n    util/threading-windows.c\n    util/threading-windows.h\n    util/windows/CoTaskMemPtr.hpp\n    util/windows/device-enum.c\n    util/windows/device-enum.h\n    util/windows/HRError.hpp\n    util/windows/win-registry.h\n    util/windows/win-version.h\n    util/windows/window-helpers.c\n    util/windows/window-helpers.h\n)\n\ntarget_compile_options(libobs PRIVATE $<$<COMPILE_LANGUAGE:C,CXX>:/EHc->)\n\nset_source_files_properties(\n  obs-win-crash-handler.c\n  PROPERTIES COMPILE_DEFINITIONS OBS_VERSION=\"${OBS_VERSION_CANONICAL}\"\n)\n\ntarget_link_libraries(\n  libobs\n  PRIVATE Avrt Dwmapi Dxgi winmm Rpcrt4 OBS::obfuscate OBS::winhandle OBS::COMutils\n  PUBLIC OBS::w32-pthreads\n)\n\ntarget_link_options(libobs PRIVATE /IGNORE:4098 /SAFESEH:NO)\n\nset_target_properties(libobs PROPERTIES PREFIX \"\" OUTPUT_NAME \"obs\")\n"
  },
  {
    "path": "libobs/cmake/windows/obs-module.rc.in",
    "content": "1 VERSIONINFO\nFILEVERSION ${OBS_VERSION_MAJOR},${OBS_VERSION_MINOR},${OBS_VERSION_PATCH},0\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"040904B0\"\n    BEGIN\n      VALUE \"CompanyName\", \"${OBS_COMPANY_NAME}\"\n      VALUE \"FileDescription\", \"OBS Library\"\n      VALUE \"FileVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"ProductName\", \"${OBS_PRODUCT_NAME}\"\n      VALUE \"ProductVersion\", \"${OBS_VERSION_CANONICAL}\"\n      VALUE \"Comments\", \"${OBS_COMMENTS}\"\n      VALUE \"LegalCopyright\", \"${OBS_LEGAL_COPYRIGHT}\"\n      VALUE \"InternalName\", \"libobs\"\n      VALUE \"OriginalFilename\", \"libobs\"\n    END\n  END\n\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x0409, 0x04B0\n  END\nEND\n"
  },
  {
    "path": "libobs/data/area.effect",
    "content": "#include \"color.effect\"\n\nuniform float4x4 ViewProj;\nuniform float2 base_dimension;\nuniform float2 base_dimension_i;\nuniform texture2d image;\nuniform float multiplier;\n\nsampler_state textureSampler {\n\tFilter    = Linear;\n\tAddressU  = Clamp;\n\tAddressV  = Clamp;\n};\n\nstruct VertData {\n\tfloat4 pos : POSITION;\n\tfloat2 uv  : TEXCOORD0;\n};\n\nstruct VertInOut {\n\tfloat2 uv  : TEXCOORD0;\n\tfloat4 pos : POSITION;\n};\n\nstruct FragData {\n\tfloat2 uv : TEXCOORD0;\n};\n\nVertInOut VSDefault(VertData vert_in)\n{\n\tVertInOut vert_out;\n\tvert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);\n\tvert_out.uv  = vert_in.uv;\n\treturn vert_out;\n}\n\nfloat4 DrawArea(FragData frag_in)\n{\n\tfloat2 uv = frag_in.uv;\n\tfloat2 uv_delta = float2(ddx(uv.x), ddy(uv.y));\n\n\t// Handle potential OpenGL flip.\n\tif (obs_glsl_compile)\n\t\tuv_delta.y = abs(uv_delta.y);\n\n\tfloat2 uv_min = uv - 0.5 * uv_delta;\n\tfloat2 uv_max = uv_min + uv_delta;\n\n\tfloat2 load_index_begin = floor(uv_min * base_dimension);\n\tfloat2 load_index_end = ceil(uv_max * base_dimension);\n\n\tfloat2 target_dimension = 1.0 / uv_delta;\n\tfloat2 target_pos = uv * target_dimension;\n\tfloat2 target_pos_min = target_pos - 0.5;\n\tfloat2 target_pos_max = target_pos + 0.5;\n\tfloat2 scale = base_dimension_i * target_dimension;\n\n\tfloat4 total_color = float4(0.0, 0.0, 0.0, 0.0);\n\n\tfloat load_index_y = load_index_begin.y;\n\tdo {\n\t\tfloat source_y_min = load_index_y * scale.y;\n\t\tfloat source_y_max = source_y_min + scale.y;\n\t\tfloat y_min = max(source_y_min, target_pos_min.y);\n\t\tfloat y_max = min(source_y_max, target_pos_max.y);\n\t\tfloat height = y_max - y_min;\n\n\t\tfloat load_index_x = load_index_begin.x;\n\t\tdo {\n\t\t\tfloat source_x_min = load_index_x * scale.x;\n\t\t\tfloat source_x_max = source_x_min + scale.x;\n\t\t\tfloat x_min = max(source_x_min, target_pos_min.x);\n\t\t\tfloat x_max = min(source_x_max, target_pos_max.x);\n\t\t\tfloat width = x_max - x_min;\n\t\t\tfloat area = width * height;\n\n\t\t\tfloat4 color = image.Load(int3(load_index_x, load_index_y, 0));\n\t\t\ttotal_color += area * color;\n\n\t\t\t++load_index_x;\n\t\t} while (load_index_x < load_index_end.x);\n\n\t\t++load_index_y;\n\t} while (load_index_y < load_index_end.y);\n\n\treturn total_color;\n}\n\nfloat4 PSDrawAreaRGBA(FragData frag_in) : TARGET\n{\n\treturn DrawArea(frag_in);\n}\n\nfloat4 PSDrawAreaRGBAMultiply(FragData frag_in) : TARGET\n{\n\tfloat4 rgba = DrawArea(frag_in);\n\trgba.rgb *= multiplier;\n\treturn rgba;\n}\n\nfloat4 PSDrawAreaRGBATonemap(FragData frag_in) : TARGET\n{\n\tfloat4 rgba = DrawArea(frag_in);\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawAreaRGBAMultiplyTonemap(FragData frag_in) : TARGET\n{\n\tfloat4 rgba = DrawArea(frag_in);\n\trgba.rgb *= multiplier;\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 DrawAreaUpscale(FragData frag_in)\n{\n\tfloat2 uv = frag_in.uv;\n\tfloat2 uv_delta = float2(ddx(uv.x), ddy(uv.y));\n\n\t// Handle potential OpenGL flip.\n\tif (obs_glsl_compile)\n\t\tuv_delta.y = abs(uv_delta.y);\n\n\tfloat2 uv_min = uv - 0.5 * uv_delta;\n\tfloat2 uv_max = uv_min + uv_delta;\n\n\tfloat2 load_index_first = floor(uv_min * base_dimension);\n\tfloat2 load_index_last = ceil(uv_max * base_dimension) - 1.0;\n\n\tif (load_index_first.x < load_index_last.x) {\n\t\tfloat uv_boundary_x = load_index_last.x * base_dimension_i.x;\n\t\tuv.x = ((uv.x - uv_boundary_x) / uv_delta.x) * base_dimension_i.x + uv_boundary_x;\n\t} else\n\t\tuv.x = (load_index_first.x + 0.5) * base_dimension_i.x;\n\tif (load_index_first.y < load_index_last.y) {\n\t\tfloat uv_boundary_y = load_index_last.y * base_dimension_i.y;\n\t\tuv.y = ((uv.y - uv_boundary_y) / uv_delta.y) * base_dimension_i.y + uv_boundary_y;\n\t} else\n\t\tuv.y = (load_index_first.y + 0.5) * base_dimension_i.y;\n\n\treturn image.Sample(textureSampler, uv);\n}\n\nfloat4 PSDrawAreaRGBAUpscale(FragData frag_in) : TARGET\n{\n\treturn DrawAreaUpscale(frag_in);\n}\n\nfloat4 PSDrawAreaRGBAUpscaleMultiply(FragData frag_in) : TARGET\n{\n\tfloat4 rgba = DrawAreaUpscale(frag_in);\n\trgba.rgb *= multiplier;\n\treturn rgba;\n}\n\nfloat4 PSDrawAreaRGBAUpscaleTonemap(FragData frag_in) : TARGET\n{\n\tfloat4 rgba = DrawAreaUpscale(frag_in);\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawAreaRGBAUpscaleMultiplyTonemap(FragData frag_in) : TARGET\n{\n\tfloat4 rgba = DrawAreaUpscale(frag_in);\n\trgba.rgb *= multiplier;\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\ntechnique Draw\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAreaRGBA(frag_in);\n\t}\n}\n\ntechnique DrawMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAreaRGBAMultiply(frag_in);\n\t}\n}\n\ntechnique DrawTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAreaRGBATonemap(frag_in);\n\t}\n}\n\ntechnique DrawMultiplyTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAreaRGBAMultiplyTonemap(frag_in);\n\t}\n}\n\ntechnique DrawUpscale\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAreaRGBAUpscale(frag_in);\n\t}\n}\n\ntechnique DrawUpscaleMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAreaRGBAUpscaleMultiply(frag_in);\n\t}\n}\n\ntechnique DrawUpscaleTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAreaRGBAUpscaleTonemap(frag_in);\n\t}\n}\n\ntechnique DrawUpscaleMultiplyTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAreaRGBAUpscaleMultiplyTonemap(frag_in);\n\t}\n}\n"
  },
  {
    "path": "libobs/data/bicubic_scale.effect",
    "content": "/*\n * bicubic sharper (better for downscaling)\n * note - this shader is adapted from the GPL bsnes shader, very good stuff\n * there.\n */\n\n#include \"color.effect\"\n\nuniform float4x4 ViewProj;\nuniform texture2d image;\nuniform float2 base_dimension;\nuniform float2 base_dimension_i;\nuniform float undistort_factor = 1.0;\nuniform float multiplier;\n\nsampler_state textureSampler {\n\tFilter    = Linear;\n\tAddressU  = Clamp;\n\tAddressV  = Clamp;\n};\n\nstruct VertData {\n\tfloat4 pos : POSITION;\n\tfloat2 uv  : TEXCOORD0;\n};\n\nstruct VertOut {\n\tfloat2 uv  : TEXCOORD0;\n\tfloat4 pos : POSITION;\n};\n\nstruct FragData {\n\tfloat2 uv : TEXCOORD0;\n};\n\nVertOut VSDefault(VertData v_in)\n{\n\tVertOut vert_out;\n\tvert_out.uv = v_in.uv * base_dimension;\n\tvert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);\n\treturn vert_out;\n}\n\nfloat4 weight4(float x)\n{\n\t/* Sharper version.  May look better in some cases. B=0, C=0.75 */\n\treturn float4(\n\t\t((-0.75 * x + 1.5) * x - 0.75) * x,\n\t\t(1.25 * x - 2.25) * x * x + 1.0,\n\t\t((-1.25 * x + 1.5) * x + 0.75) * x,\n\t\t(0.75 * x - 0.75) * x * x);\n}\n\nfloat AspectUndistortX(float x, float a)\n{\n\t// The higher the power, the longer the linear part will be.\n\treturn (1.0 - a) * (x * x * x * x * x) + a * x;\n}\n\nfloat AspectUndistortU(float u)\n{\n\t// Normalize texture coord to -1.0 to 1.0 range, and back.\n\treturn AspectUndistortX((u - 0.5) * 2.0, undistort_factor) * 0.5 + 0.5;\n}\n\nfloat2 undistort_coord(float xpos, float ypos)\n{\n\treturn float2(AspectUndistortU(xpos), ypos);\n}\n\nfloat4 undistort_pixel(float xpos, float ypos)\n{\n\treturn image.Sample(textureSampler, undistort_coord(xpos, ypos));\n}\n\nfloat4 undistort_line(float4 xpos, float ypos, float4 rowtaps)\n{\n\treturn undistort_pixel(xpos.x, ypos) * rowtaps.x +\n\t       undistort_pixel(xpos.y, ypos) * rowtaps.y +\n\t       undistort_pixel(xpos.z, ypos) * rowtaps.z +\n\t       undistort_pixel(xpos.w, ypos) * rowtaps.w;\n}\n\nfloat4 DrawBicubic(FragData f_in, bool undistort)\n{\n\tfloat2 pos = f_in.uv;\n\tfloat2 pos1 = floor(pos - 0.5) + 0.5;\n\tfloat2 f = pos - pos1;\n\n\tfloat4 rowtaps = weight4(f.x);\n\tfloat4 coltaps = weight4(f.y);\n\n\tfloat2 uv1 = pos1 * base_dimension_i;\n\tfloat2 uv0 = uv1 - base_dimension_i;\n\tfloat2 uv2 = uv1 + base_dimension_i;\n\tfloat2 uv3 = uv2 + base_dimension_i;\n\n\tif (undistort) {\n\t\tfloat4 xpos = float4(uv0.x, uv1.x, uv2.x, uv3.x);\n\t\treturn undistort_line(xpos, uv0.y, rowtaps) * coltaps.x +\n\t\t       undistort_line(xpos, uv1.y, rowtaps) * coltaps.y +\n\t\t       undistort_line(xpos, uv2.y, rowtaps) * coltaps.z +\n\t\t       undistort_line(xpos, uv3.y, rowtaps) * coltaps.w;\n\t}\n\n\tfloat u_weight_sum = rowtaps.y + rowtaps.z;\n\tfloat u_middle_offset = rowtaps.z * base_dimension_i.x / u_weight_sum;\n\tfloat u_middle = uv1.x + u_middle_offset;\n\n\tfloat v_weight_sum = coltaps.y + coltaps.z;\n\tfloat v_middle_offset = coltaps.z * base_dimension_i.y / v_weight_sum;\n\tfloat v_middle = uv1.y + v_middle_offset;\n\n\tint2 coord_top_left = int2(max(uv0 * base_dimension, 0.5));\n\tint2 coord_bottom_right = int2(min(uv3 * base_dimension, base_dimension - 0.5));\n\n\tfloat4 top = image.Load(int3(coord_top_left, 0)) * rowtaps.x;\n\ttop += image.Sample(textureSampler, float2(u_middle, uv0.y)) * u_weight_sum;\n\ttop += image.Load(int3(coord_bottom_right.x, coord_top_left.y, 0)) * rowtaps.w;\n\tfloat4 total = top * coltaps.x;\n\n\tfloat4 middle = image.Sample(textureSampler, float2(uv0.x, v_middle)) * rowtaps.x;\n\tmiddle += image.Sample(textureSampler, float2(u_middle, v_middle)) * u_weight_sum;\n\tmiddle += image.Sample(textureSampler, float2(uv3.x, v_middle)) * rowtaps.w;\n\ttotal += middle * v_weight_sum;\n\n\tfloat4 bottom = image.Load(int3(coord_top_left.x, coord_bottom_right.y, 0)) * rowtaps.x;\n\tbottom += image.Sample(textureSampler, float2(u_middle, uv3.y)) * u_weight_sum;\n\tbottom += image.Load(int3(coord_bottom_right, 0)) * rowtaps.w;\n\ttotal += bottom * coltaps.w;\n\n\treturn total;\n}\n\nfloat4 PSDrawBicubicRGBA(FragData f_in, bool undistort) : TARGET\n{\n\treturn DrawBicubic(f_in, undistort);\n}\n\nfloat4 PSDrawBicubicRGBAMultiply(FragData f_in, bool undistort) : TARGET\n{\n\tfloat4 rgba = DrawBicubic(f_in, undistort);\n\trgba.rgb *= multiplier;\n\treturn rgba;\n}\n\nfloat4 PSDrawBicubicRGBATonemap(FragData f_in, bool undistort) : TARGET\n{\n\tfloat4 rgba = DrawBicubic(f_in, undistort);\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawBicubicRGBAMultiplyTonemap(FragData f_in, bool undistort) : TARGET\n{\n\tfloat4 rgba = DrawBicubic(f_in, undistort);\n\trgba.rgb *= multiplier;\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\ntechnique Draw\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader = PSDrawBicubicRGBA(f_in, false);\n\t}\n}\n\ntechnique DrawMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawBicubicRGBAMultiply(f_in, false);\n\t}\n}\n\ntechnique DrawTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawBicubicRGBATonemap(f_in, false);\n\t}\n}\n\ntechnique DrawMultiplyTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawBicubicRGBAMultiplyTonemap(f_in, false);\n\t}\n}\n\ntechnique DrawUndistort\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader = PSDrawBicubicRGBA(f_in, true);\n\t}\n}\n\ntechnique DrawUndistortMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawBicubicRGBAMultiply(f_in, true);\n\t}\n}\n\ntechnique DrawUndistortTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawBicubicRGBATonemap(f_in, true);\n\t}\n}\n\ntechnique DrawUndistortMultiplyTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawBicubicRGBAMultiplyTonemap(f_in, true);\n\t}\n}\n"
  },
  {
    "path": "libobs/data/bilinear_lowres_scale.effect",
    "content": "/*\n * bilinear low res scaling, samples 8 pixels of a larger image to scale to a\n * low resolution image below half size\n */\n\n#include \"color.effect\"\n\nuniform float4x4 ViewProj;\nuniform texture2d image;\nuniform float multiplier;\n\nsampler_state textureSampler {\n\tFilter    = Linear;\n\tAddressU  = Clamp;\n\tAddressV  = Clamp;\n};\n\nstruct VertData {\n\tfloat4 pos : POSITION;\n\tfloat2 uv  : TEXCOORD0;\n};\n\nVertData VSDefault(VertData v_in)\n{\n\tVertData vert_out;\n\tvert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);\n\tvert_out.uv  = v_in.uv;\n\treturn vert_out;\n}\n\nfloat4 pixel(float2 uv)\n{\n\treturn image.Sample(textureSampler, uv);\n}\n\nfloat4 DrawLowresBilinear(VertData f_in)\n{\n\tfloat2 uv = f_in.uv;\n\tfloat2 stepxy  = float2(ddx(uv.x), ddy(uv.y));\n\tfloat2 stepxy1 = stepxy * 0.0625;\n\tfloat2 stepxy3 = stepxy * 0.1875;\n\tfloat2 stepxy5 = stepxy * 0.3125;\n\tfloat2 stepxy7 = stepxy * 0.4375;\n\n\t// Simulate Direct3D 8-sample pattern\n\tfloat4 out_color;\n\tout_color  = pixel(uv + float2( stepxy1.x, -stepxy3.y));\n\tout_color += pixel(uv + float2(-stepxy1.x,  stepxy3.y));\n\tout_color += pixel(uv + float2( stepxy5.x,  stepxy1.y));\n\tout_color += pixel(uv + float2(-stepxy3.x, -stepxy5.y));\n\tout_color += pixel(uv + float2(-stepxy5.x,  stepxy5.y));\n\tout_color += pixel(uv + float2(-stepxy7.x, -stepxy1.y));\n\tout_color += pixel(uv + float2( stepxy3.x,  stepxy7.y));\n\tout_color += pixel(uv + float2( stepxy7.x, -stepxy7.y));\n\treturn out_color * 0.125;\n}\n\nfloat4 PSDrawLowresBilinearRGBA(VertData f_in) : TARGET\n{\n\treturn DrawLowresBilinear(f_in);\n}\n\nfloat4 PSDrawLowresBilinearRGBAMultiply(VertData f_in) : TARGET\n{\n\tfloat4 rgba = DrawLowresBilinear(f_in);\n\trgba.rgb *= multiplier;\n\treturn rgba;\n}\n\nfloat4 PSDrawLowresBilinearRGBATonemap(VertData f_in) : TARGET\n{\n\tfloat4 rgba = DrawLowresBilinear(f_in);\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawLowresBilinearRGBAMultiplyTonemap(VertData f_in) : TARGET\n{\n\tfloat4 rgba = DrawLowresBilinear(f_in);\n\trgba.rgb *= multiplier;\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\ntechnique Draw\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLowresBilinearRGBA(f_in);\n\t}\n}\n\ntechnique DrawMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLowresBilinearRGBAMultiply(f_in);\n\t}\n}\n\ntechnique DrawTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLowresBilinearRGBATonemap(f_in);\n\t}\n}\n\ntechnique DrawMultiplyTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLowresBilinearRGBAMultiplyTonemap(f_in);\n\t}\n}\n"
  },
  {
    "path": "libobs/data/color.effect",
    "content": "float srgb_linear_to_nonlinear_channel(float u)\n{\n\treturn (u <= 0.0031308) ? (12.92 * u) : ((1.055 * pow(u, 1. / 2.4)) - 0.055);\n}\n\nfloat3 srgb_linear_to_nonlinear(float3 v)\n{\n\treturn float3(srgb_linear_to_nonlinear_channel(v.r), srgb_linear_to_nonlinear_channel(v.g), srgb_linear_to_nonlinear_channel(v.b));\n}\n\nfloat srgb_nonlinear_to_linear_channel(float u)\n{\n\treturn (u <= 0.04045) ? (u / 12.92) : pow(mad(u, 1. / 1.055, .055 / 1.055), 2.4);\n}\n\nfloat3 srgb_nonlinear_to_linear(float3 v)\n{\n\treturn float3(srgb_nonlinear_to_linear_channel(v.r), srgb_nonlinear_to_linear_channel(v.g), srgb_nonlinear_to_linear_channel(v.b));\n}\n\nfloat3 rec709_to_rec2020(float3 v)\n{\n\tfloat r = dot(v, float3(0.62740389593469903, 0.32928303837788370, 0.043313065687417225));\n\tfloat g = dot(v, float3(0.069097289358232075, 0.91954039507545871, 0.011362315566309178));\n\tfloat b = dot(v, float3(0.016391438875150280, 0.088013307877225749, 0.89559525324762401));\n\treturn float3(r, g, b);\n}\n\nfloat3 d65p3_to_rec709(float3 v)\n{\n\tfloat r = dot(v, float3(1.2249401762805598, -0.22494017628055996, 0.));\n\tfloat g = dot(v, float3(-0.042056954709688163, 1.0420569547096881, 0.));\n\tfloat b = dot(v, float3(-0.019637554590334432, -0.078636045550631889, 1.0982736001409663));\n\treturn float3(r, g, b);\n}\n\nfloat3 rec2020_to_rec709(float3 v)\n{\n\tfloat r = dot(v, float3(1.6604910021084345, -0.58764113878854951, -0.072849863319884883));\n\tfloat g = dot(v, float3(-0.12455047452159074, 1.1328998971259603, -0.0083494226043694768));\n\tfloat b = dot(v, float3(-0.018150763354905303, -0.10057889800800739, 1.1187296613629127));\n\treturn float3(r, g, b);\n}\n\nfloat3 reinhard(float3 rgb)\n{\n\trgb /= rgb + float3(1., 1., 1.);\n\trgb = saturate(rgb);\n\trgb = pow(rgb, float3(1. / 2.4, 1. / 2.4, 1. / 2.4));\n\trgb = srgb_nonlinear_to_linear(rgb);\n\treturn rgb;\n}\n\nfloat linear_to_st2084_channel(float x)\n{\n\tfloat c = pow(abs(x), 0.1593017578);\n\treturn pow((0.8359375 + 18.8515625 * c) / (1. + 18.6875 * c), 78.84375);\n}\n\nfloat3 linear_to_st2084(float3 rgb)\n{\n\treturn float3(linear_to_st2084_channel(rgb.r), linear_to_st2084_channel(rgb.g), linear_to_st2084_channel(rgb.b));\n}\n\nfloat st2084_to_linear_channel(float u)\n{\n\tfloat c = pow(abs(u), 1. / 78.84375);\n\treturn pow(abs(max(c - 0.8359375, 0.) / (18.8515625 - 18.6875 * c)), 1. / 0.1593017578);\n}\n\nfloat3 st2084_to_linear(float3 rgb)\n{\n\treturn float3(st2084_to_linear_channel(rgb.r), st2084_to_linear_channel(rgb.g), st2084_to_linear_channel(rgb.b));\n}\n\nfloat eetf_0_Lmax(float maxRGB1_pq, float Lw, float Lmax)\n{\n\tfloat Lw_pq = linear_to_st2084_channel(Lw / 10000.);\n\tfloat E1 = saturate(maxRGB1_pq / Lw_pq); // Ensure normalization in case Lw is a lie\n\tfloat maxLum = linear_to_st2084_channel(Lmax / 10000.) / Lw_pq;\n\tfloat KS = (1.5 * maxLum) - 0.5;\n\tfloat E2 = E1;\n\tif (E1 > KS)\n\t{\n\t\tfloat T = (E1 - KS) / (1. - KS);\n\t\tfloat Tsquared = T * T;\n\t\tfloat Tcubed = Tsquared * T;\n\t\tfloat P = (2. * Tcubed - 3. * Tsquared + 1.) * KS + (Tcubed - 2. * Tsquared + T) * (1. - KS) + (-2. * Tcubed + 3. * Tsquared) * maxLum;\n\t\tE2 = P;\n\t}\n\tfloat E3 = E2;\n\tfloat E4 = E3 * Lw_pq;\n\treturn E4;\n}\n\nfloat3 maxRGB_eetf_internal(float3 rgb_linear, float maxRGB1_linear, float maxRGB1_pq, float Lw, float Lmax)\n{\n\tfloat maxRGB2_pq = eetf_0_Lmax(maxRGB1_pq, Lw, Lmax);\n\tfloat maxRGB2_linear = st2084_to_linear_channel(maxRGB2_pq);\n\n\t// avoid divide-by-zero possibility\n\tmaxRGB1_linear = max(6.10352e-5, maxRGB1_linear);\n\n\trgb_linear *= maxRGB2_linear / maxRGB1_linear;\n\treturn rgb_linear;\n}\n\nfloat3 maxRGB_eetf_pq_to_linear(float3 rgb_pq, float Lw, float Lmax)\n{\n\tfloat3 rgb_linear = st2084_to_linear(rgb_pq);\n\tfloat maxRGB1_linear = max(max(rgb_linear.r, rgb_linear.g), rgb_linear.b);\n\tfloat maxRGB1_pq = max(max(rgb_pq.r, rgb_pq.g), rgb_pq.b);\n\treturn maxRGB_eetf_internal(rgb_linear, maxRGB1_linear, maxRGB1_pq, Lw, Lmax);\n}\n\nfloat3 maxRGB_eetf_linear_to_linear(float3 rgb_linear, float Lw, float Lmax)\n{\n\tfloat maxRGB1_linear = max(max(rgb_linear.r, rgb_linear.g), rgb_linear.b);\n\tfloat maxRGB1_pq = linear_to_st2084_channel(maxRGB1_linear);\n\treturn maxRGB_eetf_internal(rgb_linear, maxRGB1_linear, maxRGB1_pq, Lw, Lmax);\n}\n\nfloat3 st2084_to_linear_eetf(float3 rgb, float Lw, float Lmax)\n{\n\treturn (Lw > Lmax) ? maxRGB_eetf_pq_to_linear(rgb, Lw, Lmax) : st2084_to_linear(rgb);\n}\n\nfloat linear_to_hlg_channel(float u)\n{\n\tfloat ln2_i = 1. / log(2.);\n\tfloat m = 0.17883277 / ln2_i;\n\treturn (u <= (1. / 12.)) ? sqrt(3. * u) : ((log2((12. * u) - 0.28466892) * m) + 0.55991073);\n}\n\nfloat3 linear_to_hlg(float3 rgb, float Lw)\n{\n\trgb = saturate(rgb);\n\n\tif (Lw > 1000.)\n\t{\n\t\trgb = maxRGB_eetf_linear_to_linear(rgb, Lw, 1000.);\n\t\trgb *= 10000. / Lw;\n\t}\n\telse\n\t{\n\t\trgb *= 10.;\n\t}\n\n\tfloat Yd = dot(rgb, float3(0.2627, 0.678, 0.0593));\n\n\t// avoid inf from pow(0., negative) by using smallest positive normal number\n\tYd = max(6.10352e-5, Yd);\n\n\trgb *= pow(Yd, -1. / 6.);\n\treturn float3(linear_to_hlg_channel(rgb.r), linear_to_hlg_channel(rgb.g), linear_to_hlg_channel(rgb.b));\n}\n\nfloat hlg_to_linear_channel(float u)\n{\n\tfloat ln2_i = 1. / log(2.);\n\tfloat m = ln2_i / 0.17883277;\n\tfloat a = -ln2_i * 0.55991073 / 0.17883277;\n\treturn (u <= 0.5) ? ((u * u) / 3.) : ((exp2(u * m + a) + 0.28466892) / 12.);\n}\n\nfloat3 hlg_to_linear(float3 v, float exponent)\n{\n\tfloat3 rgb = float3(hlg_to_linear_channel(v.r), hlg_to_linear_channel(v.g), hlg_to_linear_channel(v.b));\n\tfloat Ys = dot(rgb, float3(0.2627, 0.678, 0.0593));\n\trgb *= pow(Ys, exponent);\n\treturn rgb;\n}\n"
  },
  {
    "path": "libobs/data/default.effect",
    "content": "#include \"color.effect\"\n\nuniform float4x4 ViewProj;\nuniform texture2d image;\nuniform float multiplier;\n\nsampler_state def_sampler {\n\tFilter   = Linear;\n\tAddressU = Clamp;\n\tAddressV = Clamp;\n};\n\nstruct VertInOut {\n\tfloat4 pos : POSITION;\n\tfloat2 uv  : TEXCOORD0;\n};\n\nVertInOut VSDefault(VertInOut vert_in)\n{\n\tVertInOut vert_out;\n\tvert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);\n\tvert_out.uv  = vert_in.uv;\n\treturn vert_out;\n}\n\nfloat4 PSDrawBare(VertInOut vert_in) : TARGET\n{\n\treturn image.Sample(def_sampler, vert_in.uv);\n}\n\nfloat4 PSDrawAlphaDivide(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb *= (rgba.a > 0.) ? (1. / rgba.a) : 0.;\n\treturn rgba;\n}\n\nfloat4 PSDrawAlphaDivideTonemap(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb *= (rgba.a > 0.) ? (1. / rgba.a) : 0.;\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawAlphaDivideR10L(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb *= (rgba.a > 0.) ? (multiplier / rgba.a) : 0.;\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = linear_to_st2084(rgba.rgb);\n\tuint3 rgb1023 = uint3(mad(rgba.rgb, 876., 64.5));\n\tuint b = (rgb1023.b & 0x3Fu) << 2;\n\tuint g = ((rgb1023.b & 0x3C0u) >> 6) | ((rgb1023.g & 0xFu) << 4);\n\tuint r = ((rgb1023.g & 0x3F0u) >> 4) | ((rgb1023.r & 0x3u) << 6);\n\tuint a = ((rgb1023.r & 0x3FCu) >> 2);\n\treturn float4(uint4(r, g, b, a)) / 255.;\n}\n\nfloat4 PSDrawNonlinearAlpha(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);\n\trgba.rgb *= rgba.a;\n\trgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawNonlinearAlphaMultiply(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb = srgb_linear_to_nonlinear(rgba.rgb);\n\trgba.rgb *= rgba.a;\n\trgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);\n\trgba.rgb *= multiplier;\n\treturn rgba;\n}\n\nfloat4 PSDrawSrgbDecompress(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawSrgbDecompressMultiply(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);\n\trgba.rgb *= multiplier;\n\treturn rgba;\n}\n\nfloat4 PSDrawMultiply(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb *= multiplier;\n\treturn rgba;\n}\n\nfloat4 PSDrawTonemap(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawMultiplyTonemap(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb *= multiplier;\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawPQ(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb = st2084_to_linear(rgba.rgb) * multiplier;\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawTonemapPQ(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb = st2084_to_linear(rgba.rgb) * multiplier;\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawD65P3(VertInOut vert_in) : TARGET\n{\n    float4 rgba = image.Sample(def_sampler, vert_in.uv);\n    rgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);\n    rgba.rgb = d65p3_to_rec709(rgba.rgb);\n    return rgba;\n}\n\ntechnique Draw\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawBare(vert_in);\n\t}\n}\n\ntechnique DrawAlphaDivide\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAlphaDivide(vert_in);\n\t}\n}\n\ntechnique DrawAlphaDivideTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAlphaDivideTonemap(vert_in);\n\t}\n}\n\ntechnique DrawAlphaDivideR10L\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawAlphaDivideR10L(vert_in);\n\t}\n}\n\ntechnique DrawNonlinearAlpha\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawNonlinearAlpha(vert_in);\n\t}\n}\n\ntechnique DrawNonlinearAlphaMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawNonlinearAlphaMultiply(vert_in);\n\t}\n}\n\ntechnique DrawSrgbDecompress\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawSrgbDecompress(vert_in);\n\t}\n}\n\ntechnique DrawSrgbDecompressMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawSrgbDecompressMultiply(vert_in);\n\t}\n}\n\ntechnique DrawMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawMultiply(vert_in);\n\t}\n}\n\ntechnique DrawTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawTonemap(vert_in);\n\t}\n}\n\ntechnique DrawMultiplyTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawMultiplyTonemap(vert_in);\n\t}\n}\n\ntechnique DrawPQ\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawPQ(vert_in);\n\t}\n}\n\ntechnique DrawTonemapPQ\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawTonemapPQ(vert_in);\n\t}\n}\n\ntechnique DrawD65P3\n{\n    pass\n    {\n        vertex_shader = VSDefault(vert_in);\n        pixel_shader  = PSDrawD65P3(vert_in);\n    }\n}\n"
  },
  {
    "path": "libobs/data/default_rect.effect",
    "content": "#include \"color.effect\"\n\nuniform float4x4 ViewProj;\nuniform texture_rect image;\n\nsampler_state def_sampler {\n\tFilter   = Linear;\n\tAddressU = Clamp;\n\tAddressV = Clamp;\n};\n\nstruct VertInOut {\n\tfloat4 pos : POSITION;\n\tfloat2 uv  : TEXCOORD0;\n};\n\nVertInOut VSDefault(VertInOut vert_in)\n{\n\tVertInOut vert_out;\n\tvert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);\n\tvert_out.uv  = vert_in.uv;\n\treturn vert_out;\n}\n\nfloat4 PSDrawBare(VertInOut vert_in) : TARGET\n{\n\treturn image.Sample(def_sampler, vert_in.uv);\n}\n\nfloat4 PSDrawD65P3(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);\n\trgba.rgb = d65p3_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawOpaque(VertInOut vert_in) : TARGET\n{\n\treturn float4(image.Sample(def_sampler, vert_in.uv).rgb, 1.0);\n}\n\nfloat4 PSDrawSrgbDecompress(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\trgba.rgb = srgb_nonlinear_to_linear(rgba.rgb);\n\treturn rgba;\n}\n\ntechnique Draw\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawBare(vert_in);\n\t}\n}\n\ntechnique DrawD65P3\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawD65P3(vert_in);\n\t}\n}\n\ntechnique DrawOpaque\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawOpaque(vert_in);\n\t}\n}\n\ntechnique DrawSrgbDecompress\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawSrgbDecompress(vert_in);\n\t}\n}\n"
  },
  {
    "path": "libobs/data/deinterlace_base.effect",
    "content": "/*\n * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>\n *                    John R. Bradley <jrb@turrettech.com>\n *                    Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"color.effect\"\n\nuniform float4x4 ViewProj;\nuniform texture2d image;\nuniform float multiplier;\n\nuniform texture2d previous_image;\nuniform float2 dimensions;\nuniform int field_order;\nuniform bool frame2;\n\nsampler_state textureSampler {\n\tFilter    = Linear;\n\tAddressU  = Clamp;\n\tAddressV  = Clamp;\n};\n\nstruct VertData {\n\tfloat4 pos : POSITION;\n\tfloat2 uv  : TEXCOORD0;\n};\n\nint3 select(int2 texel, int x, int y)\n{\n\treturn int3(texel + int2(x, y), 0);\n}\n\nfloat4 load_at_prev(int2 texel, int x, int y)\n{\n\treturn previous_image.Load(select(texel, x, y));\n}\n\nfloat4 load_at_image(int2 texel, int x, int y)\n{\n\treturn image.Load(select(texel, x, y));\n}\n\nfloat4 load_at(int2 texel, int x, int y, int field)\n{\n\tif(field == 0)\n\t\treturn load_at_image(texel, x, y);\n\telse\n\t\treturn load_at_prev(texel, x, y);\n}\n\n#define YADIF_UPDATE(c, level) \t\\\n\tif(score.c < spatial_score.c) \\\n\t{ \\\n\t\tspatial_score.c = score.c; \\\n\t\tspatial_pred.c = (load_at(texel, level, -1, field) + load_at(texel, -level, 1, field)).c / 2; \\\n\n#define YADIF_CHECK_ONE(level, c) \\\n{ \\\n\tfloat4 score = abs(load_at(texel, -1 + level, 1, field) - load_at(texel, -1 - level, -1, field)) + \\\n\t               abs(load_at(texel, level,      1, field) - load_at(texel, -level,     -1, field)) + \\\n\t               abs(load_at(texel, 1 + level,  1, field) - load_at(texel, 1 - level,  -1, field)); \\\n\tYADIF_UPDATE(c, level) } \\\n}\n\n#define YADIF_CHECK(level) \\\n{ \\\n\tfloat4 score = abs(load_at(texel, -1 + level, 1, field) - load_at(texel, -1 - level, -1, field)) + \\\n\t               abs(load_at(texel, level,      1, field) - load_at(texel, -level,     -1, field)) + \\\n\t               abs(load_at(texel, 1 + level,  1, field) - load_at(texel, 1 - level,  -1, field)); \\\n\tYADIF_UPDATE(r, level) YADIF_CHECK_ONE(level * 2, r) } \\\n\tYADIF_UPDATE(g, level) YADIF_CHECK_ONE(level * 2, g) } \\\n\tYADIF_UPDATE(b, level) YADIF_CHECK_ONE(level * 2, b) } \\\n\tYADIF_UPDATE(a, level) YADIF_CHECK_ONE(level * 2, a) } \\\n}\n\nfloat4 texel_at_yadif(int2 texel, int field, bool mode0)\n{\n\tif((texel.y % 2) == field)\n\t\treturn load_at(texel, 0, 0, field);\n\n\t#define YADIF_AVG(x_off, y_off) ((load_at_prev(texel, x_off, y_off) + load_at_image(texel, x_off, y_off))/2)\n\tfloat4 c = load_at(texel, 0, 1, field),\n\t       d = YADIF_AVG(0, 0),\n\t       e = load_at(texel, 0, -1, field);\n\n\tfloat4 temporal_diff0 = (abs(load_at_prev(texel,  0, 0)      -     load_at_image(texel, 0, 0)))      / 2,\n\t       temporal_diff1 = (abs(load_at_prev(texel,  0, 1) - c) + abs(load_at_prev(texel,  0, -1) - e)) / 2,\n\t       temporal_diff2 = (abs(load_at_image(texel, 0, 1) - c) + abs(load_at_image(texel, 0, -1) - e)) / 2,\n\t       diff = max(temporal_diff0, max(temporal_diff1, temporal_diff2));\n\n\tfloat4 spatial_pred = (c + e) / 2,\n\t       spatial_score = abs(load_at(texel, -1, 1, field) - load_at(texel, -1, -1, field)) +\n\t                       abs(c - e) +\n\t                       abs(load_at(texel, 1,  1, field) - load_at(texel, 1,  -1, field)) - 1;\n\n\tYADIF_CHECK(-1)\n\tYADIF_CHECK(1)\n\n\tif (mode0) {\n\t\tfloat4 b = YADIF_AVG(0, 2),\n\t\t       f = YADIF_AVG(0, -2);\n\n\t\tfloat4 max_ = max(d - e, max(d - c, min(b - c, f - e))),\n\t\t       min_ = min(d - e, min(d - c, max(b - c, f - e)));\n\n\t\tdiff = max(diff, max(min_, -max_));\n\t} else {\n\t\tdiff = max(diff, max(min(d - e, d - c), -max(d - e, d - c)));\n\t}\n\n#define YADIF_SPATIAL(c) \\\n{ \\\n\tif(spatial_pred.c > d.c + diff.c) \\\n\t\tspatial_pred.c = d.c + diff.c; \\\n\telse if(spatial_pred.c < d.c - diff.c) \\\n\t\tspatial_pred.c = d.c - diff.c; \\\n}\n\n\tYADIF_SPATIAL(r)\n\tYADIF_SPATIAL(g)\n\tYADIF_SPATIAL(b)\n\tYADIF_SPATIAL(a)\n\n\treturn spatial_pred;\n}\n\nfloat4 texel_at_yadif_2x(int2 texel, int field, bool mode0)\n{\n\tfield = frame2 ? (1 - field) : field;\n\treturn texel_at_yadif(texel, field, mode0);\n}\n\nfloat4 texel_at_discard(int2 texel, int field)\n{\n\ttexel.y = texel.y / 2 * 2;\n\treturn load_at_image(texel, 0, field);\n}\n\nfloat4 texel_at_discard_2x(int2 texel, int field)\n{\n\tfield = frame2 ? field : (1 - field);\n\treturn texel_at_discard(texel, field);\n}\n\nfloat4 texel_at_blend(int2 texel, int field)\n{\n\treturn (load_at_image(texel, 0, 0) + load_at_image(texel, 0, 1)) / 2;\n}\n\nfloat4 texel_at_blend_2x(int2 texel, int field)\n{\n\tif (!frame2)\n\t\treturn (load_at_image(texel, 0, 0) +\n\t\t        load_at_prev(texel, 0, 1)) / 2;\n\telse\n\t\treturn (load_at_image(texel, 0, 0) +\n\t\t        load_at_image(texel, 0, 1)) / 2;\n}\n\nfloat4 texel_at_linear(int2 texel, int field)\n{\n\tif ((texel.y % 2) == field)\n\t\treturn load_at_image(texel, 0, 0);\n\treturn (load_at_image(texel, 0, -1) + load_at_image(texel, 0, 1)) / 2;\n}\n\nfloat4 texel_at_linear_2x(int2 texel, int field)\n{\n\tfield = frame2 ? field : (1 - field);\n\treturn texel_at_linear(texel, field);\n}\n\nfloat4 texel_at_yadif_discard(int2 texel, int field)\n{\n\treturn (texel_at_yadif(texel, field, true) + texel_at_discard(texel, field)) / 2;\n}\n\nfloat4 texel_at_yadif_discard_2x(int2 texel, int field)\n{\n\tfield = frame2 ? (1 - field) : field;\n\treturn (texel_at_yadif(texel, field, true) + texel_at_discard(texel, field)) / 2;\n}\n\nint2 pixel_uv(float2 uv)\n{\n\treturn int2(uv * dimensions);\n}\n\nfloat4 PSYadifMode0RGBA(VertData v_in) : TARGET\n{\n\treturn texel_at_yadif(pixel_uv(v_in.uv), field_order, true);\n}\n\nfloat4 PSYadifMode0RGBA_2x(VertData v_in) : TARGET\n{\n\treturn texel_at_yadif_2x(pixel_uv(v_in.uv), field_order, true);\n}\n\nfloat4 PSYadifMode2RGBA(VertData v_in) : TARGET\n{\n\treturn texel_at_yadif(pixel_uv(v_in.uv), field_order, false);\n}\n\nfloat4 PSYadifMode2RGBA_2x(VertData v_in) : TARGET\n{\n\treturn texel_at_yadif_2x(pixel_uv(v_in.uv), field_order, false);\n}\n\nfloat4 PSYadifDiscardRGBA(VertData v_in) : TARGET\n{\n\treturn texel_at_yadif_discard(pixel_uv(v_in.uv), field_order);\n}\n\nfloat4 PSYadifDiscardRGBA_2x(VertData v_in) : TARGET\n{\n\treturn texel_at_yadif_discard_2x(pixel_uv(v_in.uv), field_order);\n}\n\nfloat4 PSLinearRGBA(VertData v_in) : TARGET\n{\n\treturn texel_at_linear(pixel_uv(v_in.uv), field_order);\n}\n\nfloat4 PSLinearRGBA_2x(VertData v_in) : TARGET\n{\n\treturn texel_at_linear_2x(pixel_uv(v_in.uv), field_order);\n}\n\nfloat4 PSDiscardRGBA(VertData v_in) : TARGET\n{\n\treturn texel_at_discard(pixel_uv(v_in.uv), field_order);\n}\n\nfloat4 PSDiscardRGBA_2x(VertData v_in) : TARGET\n{\n\treturn texel_at_discard_2x(pixel_uv(v_in.uv), field_order);\n}\n\nfloat4 PSBlendRGBA(VertData v_in) : TARGET\n{\n\treturn texel_at_blend(pixel_uv(v_in.uv), field_order);\n}\n\nfloat4 PSBlendRGBA_2x(VertData v_in) : TARGET\n{\n\treturn texel_at_blend_2x(pixel_uv(v_in.uv), field_order);\n}\n\nVertData VSDefault(VertData v_in)\n{\n\tVertData vert_out;\n\tvert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);\n\tvert_out.uv  = v_in.uv;\n\treturn vert_out;\n}\n\n#define TECHNIQUE(rgba_ps, rgba_ps_multiply, rgba_ps_tonemap, rgba_ps_multiply_tonemap) \\\nfloat4 rgba_ps_multiply(VertData v_in) : TARGET \\\n{ \\\n\tfloat4 rgba = rgba_ps(v_in); \\\n\trgba.rgb *= multiplier; \\\n\treturn rgba; \\\n} \\\nfloat4 rgba_ps_tonemap(VertData v_in) : TARGET \\\n{ \\\n\tfloat4 rgba = rgba_ps(v_in); \\\n\trgba.rgb = rec709_to_rec2020(rgba.rgb); \\\n\trgba.rgb = reinhard(rgba.rgb); \\\n\trgba.rgb = rec2020_to_rec709(rgba.rgb); \\\n\treturn rgba; \\\n} \\\nfloat4 rgba_ps_multiply_tonemap(VertData v_in) : TARGET \\\n{ \\\n\tfloat4 rgba = rgba_ps(v_in); \\\n\trgba.rgb *= multiplier; \\\n\trgba.rgb = rec709_to_rec2020(rgba.rgb); \\\n\trgba.rgb = reinhard(rgba.rgb); \\\n\trgba.rgb = rec2020_to_rec709(rgba.rgb); \\\n\treturn rgba; \\\n} \\\ntechnique Draw \\\n{ \\\n\tpass \\\n\t{ \\\n\t\tvertex_shader = VSDefault(v_in); \\\n\t\tpixel_shader  = rgba_ps(v_in); \\\n\t} \\\n} \\\ntechnique DrawMultiply \\\n{ \\\n\tpass \\\n\t{ \\\n\t\tvertex_shader = VSDefault(v_in); \\\n\t\tpixel_shader  = rgba_ps_multiply(v_in); \\\n\t} \\\n} \\\ntechnique DrawTonemap \\\n{ \\\n\tpass \\\n\t{ \\\n\t\tvertex_shader = VSDefault(v_in); \\\n\t\tpixel_shader  = rgba_ps_tonemap(v_in); \\\n\t} \\\n} \\\ntechnique DrawMultiplyTonemap \\\n{ \\\n\tpass \\\n\t{ \\\n\t\tvertex_shader = VSDefault(v_in); \\\n\t\tpixel_shader  = rgba_ps_multiply_tonemap(v_in); \\\n\t} \\\n}\n"
  },
  {
    "path": "libobs/data/deinterlace_blend.effect",
    "content": "/*\n * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>\n *                    John R. Bradley <jrb@turrettech.com>\n *                    Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"deinterlace_base.effect\"\n\nTECHNIQUE(PSBlendRGBA, PSBlendRGBA_multiply, PSBlendRGBA_tonemap, PSBlendRGBA_multiply_tonemap);\n"
  },
  {
    "path": "libobs/data/deinterlace_blend_2x.effect",
    "content": "/*\n * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>\n *                    John R. Bradley <jrb@turrettech.com>\n *                    Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"deinterlace_base.effect\"\n\nTECHNIQUE(PSBlendRGBA_2x, PSBlendRGBA_2x_multiply, PSBlendRGBA_2x_tonemap, PSBlendRGBA_2x_multiply_tonemap);\n"
  },
  {
    "path": "libobs/data/deinterlace_discard.effect",
    "content": "/*\n * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>\n *                    John R. Bradley <jrb@turrettech.com>\n *                    Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"deinterlace_base.effect\"\n\nTECHNIQUE(PSDiscardRGBA, PSDiscardRGBA_multiply, PSDiscardRGBA_tonemap, PSDiscardRGBA_multiply_tonemap);\n"
  },
  {
    "path": "libobs/data/deinterlace_discard_2x.effect",
    "content": "/*\n * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>\n *                    John R. Bradley <jrb@turrettech.com>\n *                    Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"deinterlace_base.effect\"\n\nTECHNIQUE(PSDiscardRGBA_2x, PSDiscardRGBA_2x_multiply, PSDiscardRGBA_2x_tonemap, PSDiscardRGBA_2x_multiply_tonemap);\n"
  },
  {
    "path": "libobs/data/deinterlace_linear.effect",
    "content": "/*\n * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>\n *                    John R. Bradley <jrb@turrettech.com>\n *                    Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"deinterlace_base.effect\"\n\nTECHNIQUE(PSLinearRGBA, PSLinearRGBA_multiply, PSLinearRGBA_tonemap, PSLinearRGBA_multiply_tonemap);\n"
  },
  {
    "path": "libobs/data/deinterlace_linear_2x.effect",
    "content": "/*\n * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>\n *                    John R. Bradley <jrb@turrettech.com>\n *                    Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"deinterlace_base.effect\"\n\nTECHNIQUE(PSLinearRGBA_2x, PSLinearRGBA_2x_multiply, PSLinearRGBA_2x_tonemap, PSLinearRGBA_2x_multiply_tonemap);\n"
  },
  {
    "path": "libobs/data/deinterlace_yadif.effect",
    "content": "/*\n * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>\n *                    John R. Bradley <jrb@turrettech.com>\n *                    Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"deinterlace_base.effect\"\n\nTECHNIQUE(PSYadifMode0RGBA, PSYadifMode0RGBA_multiply, PSYadifMode0RGBA_tonemap, PSYadifMode0RGBA_multiply_tonemap);\n"
  },
  {
    "path": "libobs/data/deinterlace_yadif_2x.effect",
    "content": "/*\n * Copyright (c) 2023 Ruwen Hahn <palana@stunned.de>\n *                    John R. Bradley <jrb@turrettech.com>\n *                    Lain Bailey <lain@obsproject.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include \"deinterlace_base.effect\"\n\nTECHNIQUE(PSYadifMode0RGBA_2x, PSYadifMode0RGBA_2x_multiply, PSYadifMode0RGBA_2x_tonemap, PSYadifMode0RGBA_2x_multiply_tonemap);\n"
  },
  {
    "path": "libobs/data/format_conversion.effect",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"color.effect\"\n\nuniform float     width;\nuniform float     height;\nuniform float     width_i;\nuniform float     height_i;\nuniform float     width_d2;\nuniform float     height_d2;\nuniform float     width_x2_i;\nuniform float     height_x2_i;\nuniform float     maximum_over_sdr_white_nits;\nuniform float     sdr_white_nits_over_maximum;\nuniform float     hlg_exponent;\nuniform float     hdr_lw;\nuniform float     hdr_lmax;\n\nuniform float4    color_vec0;\nuniform float4    color_vec1;\nuniform float4    color_vec2;\nuniform float3    color_range_min = {0.0, 0.0, 0.0};\nuniform float3    color_range_max = {1.0, 1.0, 1.0};\n\nuniform texture2d image;\nuniform texture2d image1;\nuniform texture2d image2;\nuniform texture2d image3;\n\nsampler_state def_sampler {\n\tFilter   = Linear;\n\tAddressU = Clamp;\n\tAddressV = Clamp;\n};\n\nstruct FragPos {\n\tfloat4 pos : POSITION;\n};\n\nstruct VertTexPos {\n\tfloat2 uv  : TEXCOORD0;\n\tfloat4 pos : POSITION;\n};\n\nstruct VertTexTexPos {\n\tfloat4 uvuv  : TEXCOORD0;\n\tfloat4 pos : POSITION;\n};\n\nstruct VertTexPosWide {\n\tfloat3 uuv : TEXCOORD0;\n\tfloat4 pos : POSITION;\n};\n\nstruct VertTexPosWideWide {\n\tfloat4 uuvv : TEXCOORD0;\n\tfloat4 pos : POSITION;\n};\n\nstruct FragTex {\n\tfloat2 uv : TEXCOORD0;\n};\n\nstruct FragTexTex {\n\tfloat4 uvuv : TEXCOORD0;\n};\n\nstruct FragTexWide {\n\tfloat3 uuv : TEXCOORD0;\n};\n\nstruct FragTexWideWide {\n\tfloat4 uuvv : TEXCOORD0;\n};\n\nFragPos VSPos(uint id : VERTEXID)\n{\n\tfloat idHigh = float(id >> 1);\n\tfloat idLow = float(id & uint(1));\n\n\tfloat x = idHigh * 4.0 - 1.0;\n\tfloat y = idLow * 4.0 - 1.0;\n\n\tFragPos vert_out;\n\tvert_out.pos = float4(x, y, 0.0, 1.0);\n\treturn vert_out;\n}\n\nVertTexPosWide VSTexPos_Left(uint id : VERTEXID)\n{\n\tfloat idHigh = float(id >> 1);\n\tfloat idLow = float(id & uint(1));\n\n\tfloat x = idHigh * 4.0 - 1.0;\n\tfloat y = idLow * 4.0 - 1.0;\n\n\tfloat u_right = idHigh * 2.0;\n\tfloat u_left = u_right - width_i;\n\tfloat v = obs_glsl_compile ? (idLow * 2.0) : (1.0 - idLow * 2.0);\n\n\tVertTexPosWide vert_out;\n\tvert_out.uuv = float3(u_left, u_right, v);\n\tvert_out.pos = float4(x, y, 0.0, 1.0);\n\treturn vert_out;\n}\n\nVertTexPosWideWide VSTexPos_TopLeft(uint id : VERTEXID)\n{\n\tfloat idHigh = float(id >> 1);\n\tfloat idLow = float(id & uint(1));\n\n\tfloat x = idHigh * 4.0 - 1.0;\n\tfloat y = idLow * 4.0 - 1.0;\n\n\tfloat u_right = idHigh * 2.0;\n\tfloat u_left = u_right - width_i;\n\tfloat v_bottom;\n\tfloat v_top;\n\tif (obs_glsl_compile) {\n\t\tv_bottom = idLow * 2.0;\n\t\tv_top = v_bottom + height_i;\n\t} else {\n\t\tv_bottom = 1.0 - idLow * 2.0;\n\t\tv_top = v_bottom - height_i;\n\t}\n\n\tVertTexPosWideWide vert_out;\n\tvert_out.uuvv = float4(u_left, u_right, v_top, v_bottom);\n\tvert_out.pos = float4(x, y, 0.0, 1.0);\n\treturn vert_out;\n}\n\nVertTexTexPos VSPacked422Left_Reverse(uint id : VERTEXID)\n{\n\tfloat idHigh = float(id >> 1);\n\tfloat idLow = float(id & uint(1));\n\n\tfloat x = idHigh * 4. - 1.;\n\tfloat y = idLow * 4. - 1.;\n\n\tfloat u = idHigh * 2.;\n\tfloat v = idLow * 2.;\n\tv = obs_glsl_compile ? v : (1. - v);\n\n\tVertTexTexPos vert_out;\n\tvert_out.uvuv = float4(width_d2 * u, height * v, u + width_x2_i, v);\n\tvert_out.pos = float4(x, y, 0., 1.);\n\treturn vert_out;\n}\n\nVertTexPos VS420Left_Reverse(uint id : VERTEXID)\n{\n\tfloat idHigh = float(id >> 1);\n\tfloat idLow = float(id & uint(1));\n\n\tfloat x = idHigh * 4. - 1.;\n\tfloat y = idLow * 4. - 1.;\n\n\tfloat u = idHigh * 2. + width_x2_i;\n\tfloat v = idLow * 2.;\n\tv = obs_glsl_compile ? v : (1. - v);\n\n\tVertTexPos vert_out;\n\tvert_out.uv = float2(u, v);\n\tvert_out.pos = float4(x, y, 0., 1.);\n\treturn vert_out;\n}\n\nVertTexPos VS420TopLeft_Reverse(uint id : VERTEXID)\n{\n\tfloat idHigh = float(id >> 1);\n\tfloat idLow = float(id & uint(1));\n\n\tfloat x = idHigh * 4. - 1.;\n\tfloat y = idLow * 4. - 1.;\n\n\tfloat u = idHigh * 2. + width_x2_i;\n\tfloat v = idLow * 2. - height_x2_i;\n\tv = obs_glsl_compile ? v : (1. - v);\n\n\tVertTexPos vert_out;\n\tvert_out.uv = float2(u, v);\n\tvert_out.pos = float4(x, y, 0., 1.);\n\treturn vert_out;\n}\n\nVertTexPos VS422Left_Reverse(uint id : VERTEXID)\n{\n\tfloat idHigh = float(id >> 1);\n\tfloat idLow = float(id & uint(1));\n\n\tfloat x = idHigh * 4.0 - 1.0;\n\tfloat y = idLow * 4.0 - 1.0;\n\n\tfloat u = idHigh * 2.0 + width_x2_i;\n\tfloat v = obs_glsl_compile ? (idLow * 2.0) : (1.0 - idLow * 2.0);\n\n\tVertTexPos vert_out;\n\tvert_out.uv = float2(u, v);\n\tvert_out.pos = float4(x, y, 0.0, 1.0);\n\treturn vert_out;\n}\n\nfloat PS_Y(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\treturn y;\n}\n\nfloat PS_P010_PQ_Y_709_2020(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_st2084(rgb);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\ty = floor(saturate(y) * 1023. + 0.5) * (64. / 65535.);\n\treturn y;\n}\n\nfloat PS_P010_HLG_Y_709_2020(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_hlg(rgb, hdr_lw);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\ty = floor(saturate(y) * 1023. + 0.5) * (64. / 65535.);\n\treturn y;\n}\n\nfloat PS_P010_SRGB_Y(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;\n\trgb = srgb_linear_to_nonlinear(rgb);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\ty = floor(saturate(y) * 1023. + 0.5) * (64. / 65535.);\n\treturn y;\n}\n\nfloat PS_P216_PQ_Y_709_2020(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_st2084(rgb);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\treturn y;\n}\n\nfloat PS_P216_HLG_Y_709_2020(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_hlg(rgb, hdr_lw);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\treturn y;\n}\n\nfloat PS_P216_SRGB_Y(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;\n\trgb = srgb_linear_to_nonlinear(rgb);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\treturn y;\n}\n\nfloat PS_P416_PQ_Y_709_2020(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_st2084(rgb);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\treturn y;\n}\n\nfloat PS_P416_HLG_Y_709_2020(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_hlg(rgb, hdr_lw);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\treturn y;\n}\n\nfloat PS_P416_SRGB_Y(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;\n\trgb = srgb_linear_to_nonlinear(rgb);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\treturn y;\n}\n\nfloat PS_I010_PQ_Y_709_2020(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_st2084(rgb);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\treturn y * (1023. / 65535.);\n}\n\nfloat PS_I010_HLG_Y_709_2020(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb * sdr_white_nits_over_maximum;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_hlg(rgb, hdr_lw);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\treturn y * (1023. / 65535.);\n}\n\nfloat PS_I010_SRGB_Y(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;\n\trgb = srgb_linear_to_nonlinear(rgb);\n\tfloat y = dot(color_vec0.xyz, rgb) + color_vec0.w;\n\treturn y * (1023. / 65535.);\n}\n\nfloat2 PS_UV_Wide(FragTexWide frag_in) : TARGET\n{\n\tfloat3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;\n\tfloat3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;\n\tfloat3 rgb = (rgb_left + rgb_right) * 0.5;\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\treturn float2(u, v);\n}\n\nfloat2 PS_P010_PQ_UV_709_2020_WideWide(FragTexWideWide frag_in) : TARGET\n{\n\tfloat3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;\n\tfloat3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;\n\tfloat3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;\n\tfloat3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;\n\tfloat3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_st2084(rgb);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\tfloat2 uv = float2(u, v);\n\tuv = floor(saturate(uv) * 1023. + 0.5) * (64. / 65535.);\n\treturn uv;\n}\n\nfloat2 PS_P010_HLG_UV_709_2020_WideWide(FragTexWideWide frag_in) : TARGET\n{\n\tfloat3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;\n\tfloat3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;\n\tfloat3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;\n\tfloat3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;\n\tfloat3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_hlg(rgb, hdr_lw);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\tfloat2 uv = float2(u, v);\n\tuv = floor(saturate(uv) * 1023. + 0.5) * (64. / 65535.);\n\treturn uv;\n}\n\nfloat2 PS_P010_SRGB_UV_Wide(FragTexWide frag_in) : TARGET\n{\n\tfloat3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;\n\tfloat3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;\n\tfloat3 rgb = (rgb_left + rgb_right) * 0.5;\n\trgb = srgb_linear_to_nonlinear(rgb);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\tfloat2 uv = float2(u, v);\n\tuv = floor(saturate(uv) * 1023. + 0.5) * (64. / 65535.);\n\treturn uv;\n}\n\nfloat2 PS_P216_PQ_UV_709_2020_Wide(FragTexWide frag_in) : TARGET\n{\n\tfloat3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;\n\tfloat3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;\n\tfloat3 rgb = (rgb_left + rgb_right) * (0.5 * sdr_white_nits_over_maximum);\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_st2084(rgb);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\tfloat2 uv = float2(u, v);\n\treturn uv;\n}\n\nfloat2 PS_P216_HLG_UV_709_2020_Wide(FragTexWide frag_in) : TARGET\n{\n\tfloat3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;\n\tfloat3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;\n\tfloat3 rgb = (rgb_left + rgb_right) * (0.5 * sdr_white_nits_over_maximum);\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_hlg(rgb, hdr_lw);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\tfloat2 uv = float2(u, v);\n\treturn uv;\n}\n\nfloat2 PS_P216_SRGB_UV_Wide(FragTexWide frag_in) : TARGET\n{\n\tfloat3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;\n\tfloat3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;\n\tfloat3 rgb = (rgb_left + rgb_right) * 0.5;\n\trgb = srgb_linear_to_nonlinear(rgb);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\tfloat2 uv = float2(u, v);\n\treturn uv;\n}\n\nfloat2 PS_P416_PQ_UV_709_2020(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_st2084(rgb);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\tfloat2 uv = float2(u, v);\n\treturn uv;\n}\n\nfloat2 PS_P416_HLG_UV_709_2020(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_hlg(rgb, hdr_lw);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\tfloat2 uv = float2(u, v);\n\treturn uv;\n}\n\nfloat2 PS_P416_SRGB_UV(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;\n\trgb = srgb_linear_to_nonlinear(rgb);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\tfloat2 uv = float2(u, v);\n\treturn uv;\n}\n\nfloat PS_U(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\treturn u;\n}\n\nfloat PS_V(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = image.Load(int3(frag_in.pos.xy, 0)).rgb;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\treturn v;\n}\n\nfloat PS_U_Wide(FragTexWide frag_in) : TARGET\n{\n\tfloat3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;\n\tfloat3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;\n\tfloat3 rgb = (rgb_left + rgb_right) * 0.5;\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\treturn u;\n}\n\nfloat PS_V_Wide(FragTexWide frag_in) : TARGET\n{\n\tfloat3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;\n\tfloat3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;\n\tfloat3 rgb = (rgb_left + rgb_right) * 0.5;\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\treturn v;\n}\n\nfloat PS_I010_PQ_U_709_2020_WideWide(FragTexWideWide frag_in) : TARGET\n{\n\tfloat3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;\n\tfloat3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;\n\tfloat3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;\n\tfloat3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;\n\tfloat3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_st2084(rgb);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\treturn u * (1023. / 65535.);\n}\n\nfloat PS_I010_HLG_U_709_2020_WideWide(FragTexWideWide frag_in) : TARGET\n{\n\tfloat3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;\n\tfloat3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;\n\tfloat3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;\n\tfloat3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;\n\tfloat3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_hlg(rgb, hdr_lw);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\treturn u * (1023. / 65535.);\n}\n\nfloat PS_I010_SRGB_U_Wide(FragTexWide frag_in) : TARGET\n{\n\tfloat3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;\n\tfloat3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;\n\tfloat3 rgb = (rgb_left + rgb_right) * 0.5;\n\trgb = srgb_linear_to_nonlinear(rgb);\n\tfloat u = dot(color_vec1.xyz, rgb) + color_vec1.w;\n\treturn u * (1023. / 65535.);\n}\n\nfloat PS_I010_PQ_V_709_2020_WideWide(FragTexWideWide frag_in) : TARGET\n{\n\tfloat3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;\n\tfloat3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;\n\tfloat3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;\n\tfloat3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;\n\tfloat3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_st2084(rgb);\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\treturn v * (1023. / 65535.);\n}\n\nfloat PS_I010_HLG_V_709_2020_WideWide(FragTexWideWide frag_in) : TARGET\n{\n\tfloat3 rgb_topleft = image.Sample(def_sampler, frag_in.uuvv.xz).rgb;\n\tfloat3 rgb_topright = image.Sample(def_sampler, frag_in.uuvv.yz).rgb;\n\tfloat3 rgb_bottomleft = image.Sample(def_sampler, frag_in.uuvv.xw).rgb;\n\tfloat3 rgb_bottomright = image.Sample(def_sampler, frag_in.uuvv.yw).rgb;\n\tfloat3 rgb = (rgb_topleft + rgb_topright + rgb_bottomleft + rgb_bottomright) * (0.25 * sdr_white_nits_over_maximum);\n\trgb = rec709_to_rec2020(rgb);\n\trgb = linear_to_hlg(rgb, hdr_lw);\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\treturn v * (1023. / 65535.);\n}\n\nfloat PS_I010_SRGB_V_Wide(FragTexWide frag_in) : TARGET\n{\n\tfloat3 rgb_left = image.Sample(def_sampler, frag_in.uuv.xz).rgb;\n\tfloat3 rgb_right = image.Sample(def_sampler, frag_in.uuv.yz).rgb;\n\tfloat3 rgb = (rgb_left + rgb_right) * 0.5;\n\trgb = srgb_linear_to_nonlinear(rgb);\n\tfloat v = dot(color_vec2.xyz, rgb) + color_vec2.w;\n\treturn v * (1023. / 65535.);\n}\n\nfloat3 YUV_to_RGB(float3 yuv)\n{\n\tyuv = clamp(yuv, color_range_min, color_range_max);\n\tfloat r = dot(color_vec0.xyz, yuv) + color_vec0.w;\n\tfloat g = dot(color_vec1.xyz, yuv) + color_vec1.w;\n\tfloat b = dot(color_vec2.xyz, yuv) + color_vec2.w;\n\treturn float3(r, g, b);\n}\n\nfloat3 PSUYVY_Reverse(FragTexTex frag_in) : TARGET\n{\n\tfloat2 y01 = image.Load(int3(frag_in.uvuv.xy, 0)).yw;\n\tfloat2 cbcr = image.Sample(def_sampler, frag_in.uvuv.zw, 0).zx;\n\tfloat leftover = frac(frag_in.uvuv.x);\n\tfloat y = (leftover < 0.5) ? y01.x : y01.y;\n\tfloat3 yuv = float3(y, cbcr);\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\treturn rgb;\n}\n\nfloat3 PSYUY2_Reverse(FragTexTex frag_in) : TARGET\n{\n\tfloat2 y01 = image.Load(int3(frag_in.uvuv.xy, 0)).zx;\n\tfloat2 cbcr = image.Sample(def_sampler, frag_in.uvuv.zw, 0).yw;\n\tfloat leftover = frac(frag_in.uvuv.x);\n\tfloat y = (leftover < 0.5) ? y01.x : y01.y;\n\tfloat3 yuv = float3(y, cbcr);\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\treturn rgb;\n}\n\nfloat4 PSYUY2_PQ_Reverse(FragTexTex frag_in) : TARGET\n{\n\tfloat2 y01 = image.Load(int3(frag_in.uvuv.xy, 0)).zx;\n\tfloat2 cbcr = image.Sample(def_sampler, frag_in.uvuv.zw, 0).yw;\n\tfloat leftover = frac(frag_in.uvuv.x);\n\tfloat y = (leftover < 0.5) ? y01.x : y01.y;\n\tfloat3 yuv = float3(y, cbcr);\n\tfloat3 pq = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSYUY2_HLG_Reverse(FragTexTex frag_in) : TARGET\n{\n\tfloat2 y01 = image.Load(int3(frag_in.uvuv.xy, 0)).zx;\n\tfloat2 cbcr = image.Sample(def_sampler, frag_in.uvuv.zw, 0).yw;\n\tfloat leftover = frac(frag_in.uvuv.x);\n\tfloat y = (leftover < 0.5) ? y01.x : y01.y;\n\tfloat3 yuv = float3(y, cbcr);\n\tfloat3 hlg = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat3 PSYVYU_Reverse(FragTexTex frag_in) : TARGET\n{\n\tfloat2 y01 = image.Load(int3(frag_in.uvuv.xy, 0)).zx;\n\tfloat2 cbcr = image.Sample(def_sampler, frag_in.uvuv.zw, 0).wy;\n\tfloat leftover = frac(frag_in.uvuv.x);\n\tfloat y = (leftover < 0.5) ? y01.x : y01.y;\n\tfloat3 yuv = float3(y, cbcr);\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\treturn rgb;\n}\n\nfloat3 PSPlanar420_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\treturn rgb;\n}\n\nfloat4 PSPlanar420_PQ_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat3 pq = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSPlanar420_HLG_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat3 hlg = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSPlanar420A_Reverse(VertTexPos frag_in) : TARGET\n{\n\tint3 xy0_luma = int3(frag_in.pos.xy, 0);\n\tfloat y = image.Load(xy0_luma).x;\n\tfloat alpha = image3.Load(xy0_luma).x;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat4 rgba = float4(YUV_to_RGB(yuv), alpha);\n\treturn rgba;\n}\n\nfloat3 PSPlanar422_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\treturn rgb;\n}\n\nfloat4 PSPlanar422_10LE_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tyuv *= 65535. / 1023.;\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\trgb = srgb_nonlinear_to_linear(rgb);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSPlanar422_10LE_PQ_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tyuv *= 65535. / 1023.;\n\tfloat3 pq = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSPlanar422_10LE_HLG_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tyuv *= 65535. / 1023.;\n\tfloat3 hlg = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSPlanar422A_Reverse(VertTexPos frag_in) : TARGET\n{\n\tint3 xy0_luma = int3(frag_in.pos.xy, 0);\n\tfloat y = image.Load(xy0_luma).x;\n\tfloat alpha = image3.Load(xy0_luma).x;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat4 rgba = float4(YUV_to_RGB(yuv), alpha);\n\treturn rgba;\n}\n\nfloat3 PSPlanar444_Reverse(FragPos frag_in) : TARGET\n{\n\tint3 xy0 = int3(frag_in.pos.xy, 0);\n\tfloat y = image.Load(xy0).x;\n\tfloat cb = image1.Load(xy0).x;\n\tfloat cr = image2.Load(xy0).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\treturn rgb;\n}\n\nfloat4 PSPlanar444_12LE_Reverse(FragPos frag_in) : TARGET\n{\n\tint3 xy0 = int3(frag_in.pos.xy, 0);\n\tfloat y = image.Load(xy0).x;\n\tfloat cb = image1.Load(xy0).x;\n\tfloat cr = image2.Load(xy0).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tyuv *= 65535. / 4095.;\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\trgb = srgb_nonlinear_to_linear(rgb);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSPlanar444_12LE_PQ_Reverse(FragPos frag_in) : TARGET\n{\n\tint3 xy0 = int3(frag_in.pos.xy, 0);\n\tfloat y = image.Load(xy0).x;\n\tfloat cb = image1.Load(xy0).x;\n\tfloat cr = image2.Load(xy0).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tyuv *= 65535. / 4095;\n\tfloat3 pq = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSPlanar444_12LE_HLG_Reverse(FragPos frag_in) : TARGET\n{\n\tint3 xy0 = int3(frag_in.pos.xy, 0);\n\tfloat y = image.Load(xy0).x;\n\tfloat cb = image1.Load(xy0).x;\n\tfloat cr = image2.Load(xy0).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tyuv *= 65535. / 4095;\n\tfloat3 hlg = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSPlanar444A_Reverse(FragPos frag_in) : TARGET\n{\n\tint3 xy0 = int3(frag_in.pos.xy, 0);\n\tfloat y = image.Load(xy0).x;\n\tfloat cb = image1.Load(xy0).x;\n\tfloat cr = image2.Load(xy0).x;\n\tfloat alpha = image3.Load(xy0).x;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat4 rgba = float4(YUV_to_RGB(yuv), alpha);\n\treturn rgba;\n}\n\nfloat4 PSPlanar444A_12LE_Reverse(FragPos frag_in) : TARGET\n{\n\tint3 xy0 = int3(frag_in.pos.xy, 0);\n\tfloat y = image.Load(xy0).x;\n\tfloat cb = image1.Load(xy0).x;\n\tfloat cr = image2.Load(xy0).x;\n\tfloat alpha = image3.Load(xy0).x * 16.;\n\tfloat3 yuv = float3(y, cb, cr);\n\tyuv *= 65535. / 4095.;\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\trgb = srgb_nonlinear_to_linear(rgb);\n\treturn float4(rgb, alpha);\n}\n\nfloat4 PSAYUV_Reverse(FragPos frag_in) : TARGET\n{\n\tfloat4 yuva = image.Load(int3(frag_in.pos.xy, 0));\n\tfloat4 rgba = float4(YUV_to_RGB(yuva.xyz), yuva.a);\n\treturn rgba;\n}\n\nfloat3 PSNV12_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;\n\tfloat3 yuv = float3(y, cbcr);\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\treturn rgb;\n}\n\nfloat4 PSNV12_PQ_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;\n\tfloat3 yuv = float3(y, cbcr);\n\tfloat3 pq = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSNV12_HLG_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;\n\tfloat3 yuv = float3(y, cbcr);\n\tfloat3 hlg = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSI010_SRGB_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat ratio = 65535. / 1023.;\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x * ratio;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x * ratio;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x * ratio;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\trgb = srgb_nonlinear_to_linear(rgb);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSI010_PQ_2020_709_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat ratio = 65535. / 1023.;\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x * ratio;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x * ratio;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x * ratio;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat3 pq = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSI010_HLG_2020_709_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat ratio = 65535. / 1023.;\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x * ratio;\n\tfloat cb = image1.Sample(def_sampler, frag_in.uv).x * ratio;\n\tfloat cr = image2.Sample(def_sampler, frag_in.uv).x * ratio;\n\tfloat3 yuv = float3(y, cb, cr);\n\tfloat3 hlg = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSP010_SRGB_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;\n\tfloat3 yuv_65535 = floor(float3(y, cbcr) * 65535. + 0.5);\n\tfloat3 yuv_1023 = floor(yuv_65535 * 0.015625);\n\tfloat3 yuv = yuv_1023 / 1023.;\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\trgb = srgb_nonlinear_to_linear(rgb);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSP010_PQ_2020_709_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;\n\tfloat3 yuv_65535 = floor(float3(y, cbcr) * 65535. + 0.5);\n\tfloat3 yuv_1023 = floor(yuv_65535 * 0.015625);\n\tfloat3 yuv = yuv_1023 / 1023.;\n\tfloat3 pq = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSP010_HLG_2020_709_Reverse(VertTexPos frag_in) : TARGET\n{\n\tfloat y = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat2 cbcr = image1.Sample(def_sampler, frag_in.uv).xy;\n\tfloat3 yuv_65535 = floor(float3(y, cbcr) * 65535. + 0.5);\n\tfloat3 yuv_1023 = floor(yuv_65535 * 0.015625);\n\tfloat3 yuv = yuv_1023 / 1023.;\n\tfloat3 hlg = YUV_to_RGB(yuv);\n\tfloat3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat3 compute_v210_reverse(float2 pos)\n{\n\tuint x = uint(pos.x);\n\tuint packed_x = x % 6u;\n\tuint base_x = x / 6u * 4u;\n\tfloat y, cb, cr;\n\tif (packed_x == 0u)\n\t{\n\t\tfloat3 word0_rgb = image.Load(int3(base_x, pos.y, 0)).rgb;\n\t\ty = word0_rgb.y;\n\t\tcb = word0_rgb.x;\n\t\tcr = word0_rgb.z;\n\t}\n\telse if (packed_x == 1u)\n\t{\n\t\tfloat2 word0_rb = image.Load(int3(base_x, pos.y, 0)).rb;\n\t\tfloat2 word1_rg = image.Load(int3(base_x + 1u, pos.y, 0)).rg;\n\t\ty = word1_rg.x;\n\t\tcb = (word0_rb.x + word1_rg.y) * 0.5;\n\t\tcr = (word0_rb.y + image.Load(int3(base_x + 2u, pos.y, 0)).r) * 0.5;\n\t}\n\telse if (packed_x == 2u)\n\t{\n\t\tfloat2 word1_gb = image.Load(int3(base_x + 1u, pos.y, 0)).gb;\n\t\ty = word1_gb.y;\n\t\tcb = word1_gb.x;\n\t\tcr = image.Load(int3(base_x + 2u, pos.y, 0)).r;\n\t}\n\telse if (packed_x == 3u)\n\t{\n\t\tfloat2 word2_rb = image.Load(int3(base_x + 2u, pos.y, 0)).rb;\n\t\ty = image.Load(int3(base_x + 2u, pos.y, 0)).g;\n\t\tcb = (image.Load(int3(base_x + 1u, pos.y, 0)).g + word2_rb.y) * 0.5;\n\t\tcr = (word2_rb.x + image.Load(int3(base_x + 3u, pos.y, 0)).g) * 0.5;\n\t}\n\telse if (packed_x == 4u)\n\t{\n\t\tfloat2 word3_rg = image.Load(int3(base_x + 3u, pos.y, 0)).rg;\n\t\ty = word3_rg.x;\n\t\tcb = image.Load(int3(base_x + 2u, pos.y, 0)).b;\n\t\tcr = word3_rg.y;\n\t}\n\telse\n\t{\n\t\tfloat2 word3_gb = image.Load(int3(base_x + 3u, pos.y, 0)).gb;\n\t\ty = word3_gb.y;\n\t\tcb = image.Load(int3(base_x + 2u, pos.y, 0)).b;\n\t\tcr = word3_gb.x;\n\t\tuint base_x_4 = base_x + 4u;\n\t\tif ((pos.x + 1.) < width)\n\t\t{\n\t\t\tfloat2 word4_gb = image.Load(int3(base_x + 4u, pos.y, 0)).rb;\n\t\t\tcb = (cb + word4_gb.x) * 0.5;\n\t\t\tcr = (cr + word4_gb.y) * 0.5;\n\t\t}\n\t}\n\tfloat3 yuv_65535 = floor(float3(y, cb, cr) * 65535. + 0.5);\n\tfloat3 yuv_1023 = floor(yuv_65535 * 0.015625);\n\tfloat3 yuv = yuv_1023 / 1023.;\n\tfloat3 rgb = YUV_to_RGB(yuv);\n\treturn rgb;\n}\n\nfloat4 PSV210_SRGB_Reverse(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = compute_v210_reverse(frag_in.pos.xy);\n\trgb = srgb_nonlinear_to_linear(rgb);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSV210_PQ_2020_709_Reverse(FragPos frag_in) : TARGET\n{\n\tfloat3 pq = compute_v210_reverse(frag_in.pos.xy);\n\tfloat3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSV210_HLG_2020_709_Reverse(FragPos frag_in) : TARGET\n{\n\tfloat3 hlg = compute_v210_reverse(frag_in.pos.xy);\n\tfloat3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat3 PSY800_Limited(FragPos frag_in) : TARGET\n{\n\tfloat limited = image.Load(int3(frag_in.pos.xy, 0)).x;\n\tfloat full = (255.0 / 219.0) * limited - (16.0 / 219.0);\n\treturn float3(full, full, full);\n}\n\nfloat3 PSY800_Full(FragPos frag_in) : TARGET\n{\n\tfloat3 full = image.Load(int3(frag_in.pos.xy, 0)).xxx;\n\treturn full;\n}\n\nfloat4 PSRGB_Limited(FragPos frag_in) : TARGET\n{\n\tfloat4 rgba = image.Load(int3(frag_in.pos.xy, 0));\n\trgba.rgb = (255.0 / 219.0) * rgba.rgb - (16.0 / 219.0);\n\treturn rgba;\n}\n\nfloat3 PSBGR3_Limited(FragPos frag_in) : TARGET\n{\n\tfloat x = frag_in.pos.x * 3.0;\n\tfloat y = frag_in.pos.y;\n\tfloat b = image.Load(int3(x - 1.0, y, 0)).x;\n\tfloat g = image.Load(int3(x, y, 0)).x;\n\tfloat r = image.Load(int3(x + 1.0, y, 0)).x;\n\tfloat3 rgb = float3(r, g, b);\n\trgb = (255.0 / 219.0) * rgb - (16.0 / 219.0);\n\treturn rgb;\n}\n\nfloat3 PSBGR3_Full(FragPos frag_in) : TARGET\n{\n\tfloat x = frag_in.pos.x * 3.0;\n\tfloat y = frag_in.pos.y;\n\tfloat b = image.Load(int3(x - 1.0, y, 0)).x;\n\tfloat g = image.Load(int3(x, y, 0)).x;\n\tfloat r = image.Load(int3(x + 1.0, y, 0)).x;\n\tfloat3 rgb = float3(r, g, b);\n\treturn rgb;\n}\n\nfloat3 compute_r10l_reverse(float2 pos, bool limited)\n{\n\tfloat4 xyzw = image.Load(int3(pos, 0)).bgra;\n\tuint4 xyzw255 = uint4(mad(xyzw, 255., 0.5));\n\tuint r = ((xyzw255.z & 0xC0u) >> 6) | (xyzw255.w << 2);\n\tuint g = ((xyzw255.y & 0xFu) >> 4) | ((xyzw255.z & 0x3Fu) << 4);\n\tuint b = (xyzw255.x >> 2) | ((xyzw255.y & 0xFu) << 6);\n\tfloat3 rgb = float3(uint3(r, g, b));\n\tif (limited)\n\t{\n\t\trgb = mad(rgb, 1. / 876., -16. / 219.);\n\t}\n\telse\n\t{\n\t\trgb /= 1023.;\n\t}\n\n\treturn rgb;\n}\n\nfloat4 PSR10L_SRGB_Full_Reverse(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = compute_r10l_reverse(frag_in.pos.xy, false);\n\trgb = srgb_nonlinear_to_linear(rgb);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSR10L_PQ_2020_709_Full_Reverse(FragPos frag_in) : TARGET\n{\n\tfloat3 pq = compute_r10l_reverse(frag_in.pos.xy, false);\n\tfloat3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSR10L_HLG_2020_709_Full_Reverse(FragPos frag_in) : TARGET\n{\n\tfloat3 hlg = compute_r10l_reverse(frag_in.pos.xy, false);\n\tfloat3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSR10L_SRGB_Limited_Reverse(FragPos frag_in) : TARGET\n{\n\tfloat3 rgb = compute_r10l_reverse(frag_in.pos.xy, true);\n\trgb = srgb_nonlinear_to_linear(rgb);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSR10L_PQ_2020_709_Limited_Reverse(FragPos frag_in) : TARGET\n{\n\tfloat3 pq = compute_r10l_reverse(frag_in.pos.xy, true);\n\tfloat3 hdr2020 = st2084_to_linear_eetf(pq, hdr_lw, hdr_lmax) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\nfloat4 PSR10L_HLG_2020_709_Limited_Reverse(FragPos frag_in) : TARGET\n{\n\tfloat3 hlg = compute_r10l_reverse(frag_in.pos.xy, true);\n\tfloat3 hdr2020 = hlg_to_linear(hlg, hlg_exponent) * maximum_over_sdr_white_nits;\n\tfloat3 rgb = rec2020_to_rec709(hdr2020);\n\treturn float4(rgb, 1.);\n}\n\ntechnique Planar_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_Y(frag_in);\n\t}\n}\n\ntechnique Planar_U\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_U(frag_in);\n\t}\n}\n\ntechnique Planar_V\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_V(frag_in);\n\t}\n}\n\ntechnique Planar_U_Left\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_Left(id);\n\t\tpixel_shader  = PS_U_Wide(frag_in);\n\t}\n}\n\ntechnique Planar_V_Left\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_Left(id);\n\t\tpixel_shader  = PS_V_Wide(frag_in);\n\t}\n}\n\ntechnique NV12_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_Y(frag_in);\n\t}\n}\n\ntechnique NV12_UV\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_Left(id);\n\t\tpixel_shader  = PS_UV_Wide(frag_in);\n\t}\n}\n\ntechnique I010_PQ_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_I010_PQ_Y_709_2020(frag_in);\n\t}\n}\n\ntechnique I010_HLG_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_I010_HLG_Y_709_2020(frag_in);\n\t}\n}\n\ntechnique I010_SRGB_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_I010_SRGB_Y(frag_in);\n\t}\n}\n\ntechnique I010_PQ_U\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_TopLeft(id);\n\t\tpixel_shader  = PS_I010_PQ_U_709_2020_WideWide(frag_in);\n\t}\n}\n\ntechnique I010_HLG_U\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_TopLeft(id);\n\t\tpixel_shader  = PS_I010_HLG_U_709_2020_WideWide(frag_in);\n\t}\n}\n\ntechnique I010_SRGB_U\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_Left(id);\n\t\tpixel_shader  = PS_I010_SRGB_U_Wide(frag_in);\n\t}\n}\n\ntechnique I010_PQ_V\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_TopLeft(id);\n\t\tpixel_shader  = PS_I010_PQ_V_709_2020_WideWide(frag_in);\n\t}\n}\n\ntechnique I010_HLG_V\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_TopLeft(id);\n\t\tpixel_shader  = PS_I010_HLG_V_709_2020_WideWide(frag_in);\n\t}\n}\n\ntechnique I010_SRGB_V\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_Left(id);\n\t\tpixel_shader  = PS_I010_SRGB_V_Wide(frag_in);\n\t}\n}\n\ntechnique P010_PQ_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P010_PQ_Y_709_2020(frag_in);\n\t}\n}\n\ntechnique P010_HLG_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P010_HLG_Y_709_2020(frag_in);\n\t}\n}\n\ntechnique P010_SRGB_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P010_SRGB_Y(frag_in);\n\t}\n}\n\ntechnique P010_PQ_UV\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_TopLeft(id);\n\t\tpixel_shader  = PS_P010_PQ_UV_709_2020_WideWide(frag_in);\n\t}\n}\n\ntechnique P010_HLG_UV\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_TopLeft(id);\n\t\tpixel_shader  = PS_P010_HLG_UV_709_2020_WideWide(frag_in);\n\t}\n}\n\ntechnique P010_SRGB_UV\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_Left(id);\n\t\tpixel_shader  = PS_P010_SRGB_UV_Wide(frag_in);\n\t}\n}\n\ntechnique P216_PQ_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P216_PQ_Y_709_2020(frag_in);\n\t}\n}\n\ntechnique P216_HLG_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P216_HLG_Y_709_2020(frag_in);\n\t}\n}\n\ntechnique P216_SRGB_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P216_SRGB_Y(frag_in);\n\t}\n}\n\ntechnique P216_PQ_UV\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_Left(id);\n\t\tpixel_shader  = PS_P216_PQ_UV_709_2020_Wide(frag_in);\n\t}\n}\n\ntechnique P216_HLG_UV\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_Left(id);\n\t\tpixel_shader  = PS_P216_HLG_UV_709_2020_Wide(frag_in);\n\t}\n}\n\ntechnique P216_SRGB_UV\n{\n\tpass\n\t{\n\t\tvertex_shader = VSTexPos_Left(id);\n\t\tpixel_shader  = PS_P216_SRGB_UV_Wide(frag_in);\n\t}\n}\n\ntechnique P416_PQ_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P416_PQ_Y_709_2020(frag_in);\n\t}\n}\n\ntechnique P416_HLG_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P416_HLG_Y_709_2020(frag_in);\n\t}\n}\n\ntechnique P416_SRGB_Y\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P416_SRGB_Y(frag_in);\n\t}\n}\n\ntechnique P416_PQ_UV\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P416_PQ_UV_709_2020(frag_in);\n\t}\n}\n\ntechnique P416_HLG_UV\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P416_HLG_UV_709_2020(frag_in);\n\t}\n}\n\ntechnique P416_SRGB_UV\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PS_P416_SRGB_UV(frag_in);\n\t}\n}\n\ntechnique UYVY_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPacked422Left_Reverse(id);\n\t\tpixel_shader  = PSUYVY_Reverse(frag_in);\n\t}\n}\n\ntechnique YUY2_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPacked422Left_Reverse(id);\n\t\tpixel_shader  = PSYUY2_Reverse(frag_in);\n\t}\n}\n\ntechnique YUY2_PQ_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPacked422Left_Reverse(id);\n\t\tpixel_shader  = PSYUY2_PQ_Reverse(frag_in);\n\t}\n}\n\ntechnique YUY2_HLG_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPacked422Left_Reverse(id);\n\t\tpixel_shader  = PSYUY2_HLG_Reverse(frag_in);\n\t}\n}\n\ntechnique YVYU_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPacked422Left_Reverse(id);\n\t\tpixel_shader  = PSYVYU_Reverse(frag_in);\n\t}\n}\n\ntechnique I420_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420Left_Reverse(id);\n\t\tpixel_shader  = PSPlanar420_Reverse(frag_in);\n\t}\n}\n\ntechnique I420_PQ_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420TopLeft_Reverse(id);\n\t\tpixel_shader  = PSPlanar420_PQ_Reverse(frag_in);\n\t}\n}\n\ntechnique I420_HLG_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420TopLeft_Reverse(id);\n\t\tpixel_shader  = PSPlanar420_HLG_Reverse(frag_in);\n\t}\n}\n\ntechnique I40A_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420Left_Reverse(id);\n\t\tpixel_shader  = PSPlanar420A_Reverse(frag_in);\n\t}\n}\n\ntechnique I422_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS422Left_Reverse(id);\n\t\tpixel_shader  = PSPlanar422_Reverse(frag_in);\n\t}\n}\n\ntechnique I210_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS422Left_Reverse(id);\n\t\tpixel_shader  = PSPlanar422_10LE_Reverse(frag_in);\n\t}\n}\n\ntechnique I210_PQ_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS422Left_Reverse(id);\n\t\tpixel_shader  = PSPlanar422_10LE_PQ_Reverse(frag_in);\n\t}\n}\n\ntechnique I210_HLG_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS422Left_Reverse(id);\n\t\tpixel_shader  = PSPlanar422_10LE_HLG_Reverse(frag_in);\n\t}\n}\n\ntechnique I42A_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS422Left_Reverse(id);\n\t\tpixel_shader  = PSPlanar422A_Reverse(frag_in);\n\t}\n}\n\ntechnique I444_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSPlanar444_Reverse(frag_in);\n\t}\n}\n\ntechnique I412_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSPlanar444_12LE_Reverse(frag_in);\n\t}\n}\n\ntechnique I412_PQ_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSPlanar444_12LE_PQ_Reverse(frag_in);\n\t}\n}\n\ntechnique I412_HLG_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSPlanar444_12LE_HLG_Reverse(frag_in);\n\t}\n}\n\ntechnique YUVA_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSPlanar444A_Reverse(frag_in);\n\t}\n}\n\ntechnique YA2L_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSPlanar444A_12LE_Reverse(frag_in);\n\t}\n}\n\ntechnique AYUV_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSAYUV_Reverse(frag_in);\n\t}\n}\n\ntechnique NV12_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420Left_Reverse(id);\n\t\tpixel_shader  = PSNV12_Reverse(frag_in);\n\t}\n}\n\ntechnique NV12_PQ_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420TopLeft_Reverse(id);\n\t\tpixel_shader  = PSNV12_PQ_Reverse(frag_in);\n\t}\n}\n\ntechnique NV12_HLG_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420TopLeft_Reverse(id);\n\t\tpixel_shader  = PSNV12_HLG_Reverse(frag_in);\n\t}\n}\n\ntechnique I010_SRGB_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420Left_Reverse(id);\n\t\tpixel_shader  = PSI010_SRGB_Reverse(frag_in);\n\t}\n}\n\ntechnique I010_PQ_2020_709_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420TopLeft_Reverse(id);\n\t\tpixel_shader  = PSI010_PQ_2020_709_Reverse(frag_in);\n\t}\n}\n\ntechnique I010_HLG_2020_709_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420TopLeft_Reverse(id);\n\t\tpixel_shader  = PSI010_HLG_2020_709_Reverse(frag_in);\n\t}\n}\n\ntechnique P010_SRGB_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420Left_Reverse(id);\n\t\tpixel_shader  = PSP010_SRGB_Reverse(frag_in);\n\t}\n}\n\ntechnique P010_PQ_2020_709_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420TopLeft_Reverse(id);\n\t\tpixel_shader  = PSP010_PQ_2020_709_Reverse(frag_in);\n\t}\n}\n\ntechnique P010_HLG_2020_709_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VS420TopLeft_Reverse(id);\n\t\tpixel_shader  = PSP010_HLG_2020_709_Reverse(frag_in);\n\t}\n}\n\ntechnique V210_SRGB_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSV210_SRGB_Reverse(frag_in);\n\t}\n}\n\ntechnique V210_PQ_2020_709_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSV210_PQ_2020_709_Reverse(frag_in);\n\t}\n}\n\ntechnique V210_HLG_2020_709_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSV210_HLG_2020_709_Reverse(frag_in);\n\t}\n}\n\ntechnique Y800_Limited\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSY800_Limited(frag_in);\n\t}\n}\n\ntechnique Y800_Full\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSY800_Full(frag_in);\n\t}\n}\n\ntechnique RGB_Limited\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSRGB_Limited(frag_in);\n\t}\n}\n\ntechnique BGR3_Limited\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSBGR3_Limited(frag_in);\n\t}\n}\n\ntechnique BGR3_Full\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSBGR3_Full(frag_in);\n\t}\n}\n\ntechnique R10L_SRGB_Full_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSR10L_SRGB_Full_Reverse(frag_in);\n\t}\n}\n\ntechnique R10L_PQ_2020_709_Full_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSR10L_PQ_2020_709_Full_Reverse(frag_in);\n\t}\n}\n\ntechnique R10L_HLG_2020_709_Full_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSR10L_HLG_2020_709_Full_Reverse(frag_in);\n\t}\n}\n\ntechnique R10L_SRGB_Limited_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSR10L_SRGB_Limited_Reverse(frag_in);\n\t}\n}\n\ntechnique R10L_PQ_2020_709_Limited_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSR10L_PQ_2020_709_Limited_Reverse(frag_in);\n\t}\n}\n\ntechnique R10L_HLG_2020_709_Limited_Reverse\n{\n\tpass\n\t{\n\t\tvertex_shader = VSPos(id);\n\t\tpixel_shader  = PSR10L_HLG_2020_709_Limited_Reverse(frag_in);\n\t}\n}\n"
  },
  {
    "path": "libobs/data/lanczos_scale.effect",
    "content": "/*\n * lanczos sharper\n * note - this shader is adapted from the GPL bsnes shader, very good stuff\n * there.\n */\n\n#include \"color.effect\"\n\nuniform float4x4 ViewProj;\nuniform texture2d image;\nuniform float2 base_dimension;\nuniform float2 base_dimension_i;\nuniform float undistort_factor = 1.0;\nuniform float multiplier;\n\nsampler_state textureSampler\n{\n\tAddressU  = Clamp;\n\tAddressV  = Clamp;\n\tFilter    = Linear;\n};\n\nstruct VertData {\n\tfloat4 pos : POSITION;\n\tfloat2 uv  : TEXCOORD0;\n};\n\nstruct VertOut {\n\tfloat2 uv : TEXCOORD0;\n\tfloat4 pos : POSITION;\n};\n\nstruct FragData {\n\tfloat2 uv : TEXCOORD0;\n};\n\nVertOut VSDefault(VertData v_in)\n{\n\tVertOut vert_out;\n\tvert_out.uv  = v_in.uv * base_dimension;\n\tvert_out.pos = mul(float4(v_in.pos.xyz, 1.0), ViewProj);\n\n\treturn vert_out;\n}\n\nfloat weight(float x)\n{\n\tfloat x_pi = x * 3.141592654;\n\treturn 3.0 * sin(x_pi) * sin(x_pi * (1.0 / 3.0)) / (x_pi * x_pi);\n}\n\nvoid weight6(float f_neg, out float3 tap012, out float3 tap345)\n{\n\ttap012 = float3(\n\t\tweight(f_neg - 2.0),\n\t\tweight(f_neg - 1.0),\n\t\tmin(1.0, weight(f_neg))); // Replace NaN with 1.0.\n\ttap345 = float3(\n\t\tweight(f_neg + 1.0),\n\t\tweight(f_neg + 2.0),\n\t\tweight(f_neg + 3.0));\n\n\t// Normalize weights\n\tfloat sum = tap012.x + tap012.y + tap012.z + tap345.x + tap345.y + tap345.z;\n\tfloat sum_i = 1.0 / sum;\n\ttap012 = tap012 * sum_i;\n\ttap345 = tap345 * sum_i;\n}\n\nfloat AspectUndistortX(float x, float a)\n{\n\t// The higher the power, the longer the linear part will be.\n\treturn (1.0 - a) * (x * x * x * x * x) + a * x;\n}\n\nfloat AspectUndistortU(float u)\n{\n\t// Normalize texture coord to -1.0 to 1.0 range, and back.\n\treturn AspectUndistortX((u - 0.5) * 2.0, undistort_factor) * 0.5 + 0.5;\n}\n\nfloat2 undistort_coord(float xpos, float ypos)\n{\n\treturn float2(AspectUndistortU(xpos), ypos);\n}\n\nfloat4 undistort_pixel(float xpos, float ypos)\n{\n\treturn image.Sample(textureSampler, undistort_coord(xpos, ypos));\n}\n\nfloat4 undistort_line(float3 xpos012, float3 xpos345, float ypos, float3 rowtap012,\n\t\tfloat3 rowtap345)\n{\n\treturn\n\t\tundistort_pixel(xpos012.x, ypos) * rowtap012.x +\n\t\tundistort_pixel(xpos012.y, ypos) * rowtap012.y +\n\t\tundistort_pixel(xpos012.z, ypos) * rowtap012.z +\n\t\tundistort_pixel(xpos345.x, ypos) * rowtap345.x +\n\t\tundistort_pixel(xpos345.y, ypos) * rowtap345.y +\n\t\tundistort_pixel(xpos345.z, ypos) * rowtap345.z;\n}\n\nfloat4 DrawLanczos(FragData f_in, bool undistort)\n{\n\tfloat2 pos = f_in.uv;\n\tfloat2 pos2 = floor(pos - 0.5) + 0.5;\n\tfloat2 f_neg = pos2 - pos;\n\n\tfloat3 rowtap012, rowtap345;\n\tweight6(f_neg.x, rowtap012, rowtap345);\n\n\tfloat3 coltap012, coltap345;\n\tweight6(f_neg.y, coltap012, coltap345);\n\n\tfloat2 uv2 = pos2 * base_dimension_i;\n\tfloat2 uv1 = uv2 - base_dimension_i;\n\tfloat2 uv0 = uv1 - base_dimension_i;\n\tfloat2 uv3 = uv2 + base_dimension_i;\n\tfloat2 uv4 = uv3 + base_dimension_i;\n\tfloat2 uv5 = uv4 + base_dimension_i;\n\n\tif (undistort) {\n\t\tfloat3 xpos012 = float3(uv0.x, uv1.x, uv2.x);\n\t\tfloat3 xpos345 = float3(uv3.x, uv4.x, uv5.x);\n\t\treturn undistort_line(xpos012, xpos345, uv0.y, rowtap012, rowtap345) * coltap012.x +\n\t\t       undistort_line(xpos012, xpos345, uv1.y, rowtap012, rowtap345) * coltap012.y +\n\t\t       undistort_line(xpos012, xpos345, uv2.y, rowtap012, rowtap345) * coltap012.z +\n\t\t       undistort_line(xpos012, xpos345, uv3.y, rowtap012, rowtap345) * coltap345.x +\n\t\t       undistort_line(xpos012, xpos345, uv4.y, rowtap012, rowtap345) * coltap345.y +\n\t\t       undistort_line(xpos012, xpos345, uv5.y, rowtap012, rowtap345) * coltap345.z;\n\t}\n\n\tfloat u_weight_sum = rowtap012.z + rowtap345.x;\n\tfloat u_middle_offset = rowtap345.x * base_dimension_i.x / u_weight_sum;\n\tfloat u_middle = uv2.x + u_middle_offset;\n\n\tfloat v_weight_sum = coltap012.z + coltap345.x;\n\tfloat v_middle_offset = coltap345.x * base_dimension_i.y / v_weight_sum;\n\tfloat v_middle = uv2.y + v_middle_offset;\n\n\tfloat2 coord_limit = base_dimension - 0.5;\n\tfloat2 coord0_f = max(uv0 * base_dimension, 0.5);\n\tfloat2 coord1_f = max(uv1 * base_dimension, 0.5);\n\tfloat2 coord4_f = min(uv4 * base_dimension, coord_limit);\n\tfloat2 coord5_f = min(uv5 * base_dimension, coord_limit);\n\n\tint2 coord0 = int2(coord0_f);\n\tint2 coord1 = int2(coord1_f);\n\tint2 coord4 = int2(coord4_f);\n\tint2 coord5 = int2(coord5_f);\n\n\tfloat4 row0 = image.Load(int3(coord0, 0)) * rowtap012.x;\n\trow0 += image.Load(int3(coord1.x, coord0.y, 0)) * rowtap012.y;\n\trow0 += image.Sample(textureSampler, float2(u_middle, uv0.y)) * u_weight_sum;\n\trow0 += image.Load(int3(coord4.x, coord0.y, 0)) * rowtap345.y;\n\trow0 += image.Load(int3(coord5.x, coord0.y, 0)) * rowtap345.z;\n\tfloat4 total = row0 * coltap012.x;\n\n\tfloat4 row1 = image.Load(int3(coord0.x, coord1.y, 0)) * rowtap012.x;\n\trow1 += image.Load(int3(coord1.x, coord1.y, 0)) * rowtap012.y;\n\trow1 += image.Sample(textureSampler, float2(u_middle, uv1.y)) * u_weight_sum;\n\trow1 += image.Load(int3(coord4.x, coord1.y, 0)) * rowtap345.y;\n\trow1 += image.Load(int3(coord5.x, coord1.y, 0)) * rowtap345.z;\n\ttotal += row1 * coltap012.y;\n\n\tfloat4 row23 = image.Sample(textureSampler, float2(uv0.x, v_middle)) * rowtap012.x;\n\trow23 += image.Sample(textureSampler, float2(uv1.x, v_middle)) * rowtap012.y;\n\trow23 += image.Sample(textureSampler, float2(u_middle, v_middle)) * u_weight_sum;\n\trow23 += image.Sample(textureSampler, float2(uv4.x, v_middle)) * rowtap345.y;\n\trow23 += image.Sample(textureSampler, float2(uv5.x, v_middle)) * rowtap345.z;\n\ttotal += row23 * v_weight_sum;\n\n\tfloat4 row4 = image.Load(int3(coord0.x, coord4.y, 0)) * rowtap012.x;\n\trow4 += image.Load(int3(coord1.x, coord4.y, 0)) * rowtap012.y;\n\trow4 += image.Sample(textureSampler, float2(u_middle, uv4.y)) * u_weight_sum;\n\trow4 += image.Load(int3(coord4.x, coord4.y, 0)) * rowtap345.y;\n\trow4 += image.Load(int3(coord5.x, coord4.y, 0)) * rowtap345.z;\n\ttotal += row4 * coltap345.y;\n\n\tfloat4 row5 = image.Load(int3(coord0.x, coord5.y, 0)) * rowtap012.x;\n\trow5 += image.Load(int3(coord1.x, coord5.y, 0)) * rowtap012.y;\n\trow5 += image.Sample(textureSampler, float2(u_middle, uv5.y)) * u_weight_sum;\n\trow5 += image.Load(int3(coord4.x, coord5.y, 0)) * rowtap345.y;\n\trow5 += image.Load(int3(coord5, 0)) * rowtap345.z;\n\ttotal += row5 * coltap345.z;\n\n\treturn total;\n}\n\nfloat4 PSDrawLanczosRGBA(FragData f_in, bool undistort) : TARGET\n{\n\treturn DrawLanczos(f_in, undistort);\n}\n\nfloat4 PSDrawLanczosRGBAMultiply(FragData f_in, bool undistort) : TARGET\n{\n\tfloat4 rgba = DrawLanczos(f_in, undistort);\n\trgba.rgb *= multiplier;\n\treturn rgba;\n}\n\nfloat4 PSDrawLanczosRGBATonemap(FragData f_in, bool undistort) : TARGET\n{\n\tfloat4 rgba = DrawLanczos(f_in, undistort);\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\nfloat4 PSDrawLanczosRGBAMultiplyTonemap(FragData f_in, bool undistort) : TARGET\n{\n\tfloat4 rgba = DrawLanczos(f_in, undistort);\n\trgba.rgb *= multiplier;\n\trgba.rgb = rec709_to_rec2020(rgba.rgb);\n\trgba.rgb = reinhard(rgba.rgb);\n\trgba.rgb = rec2020_to_rec709(rgba.rgb);\n\treturn rgba;\n}\n\ntechnique Draw\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLanczosRGBA(f_in, false);\n\t}\n}\n\ntechnique DrawMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLanczosRGBAMultiply(f_in, false);\n\t}\n}\n\ntechnique DrawTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLanczosRGBATonemap(f_in, false);\n\t}\n}\n\ntechnique DrawMultiplyTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLanczosRGBAMultiplyTonemap(f_in, false);\n\t}\n}\n\ntechnique DrawUndistort\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLanczosRGBA(f_in, true);\n\t}\n}\n\ntechnique DrawUndistortMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLanczosRGBAMultiply(f_in, true);\n\t}\n}\n\ntechnique DrawUndistortTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLanczosRGBATonemap(f_in, true);\n\t}\n}\n\ntechnique DrawUndistortMultiplyTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(v_in);\n\t\tpixel_shader  = PSDrawLanczosRGBAMultiplyTonemap(f_in, true);\n\t}\n}\n"
  },
  {
    "path": "libobs/data/opaque.effect",
    "content": "#include \"color.effect\"\n\nuniform float4x4 ViewProj;\nuniform texture2d image;\nuniform float multiplier;\n\nsampler_state def_sampler {\n\tFilter   = Linear;\n\tAddressU = Clamp;\n\tAddressV = Clamp;\n};\n\nstruct VertInOut {\n\tfloat4 pos : POSITION;\n\tfloat2 uv  : TEXCOORD0;\n};\n\nVertInOut VSDefault(VertInOut vert_in)\n{\n\tVertInOut vert_out;\n\tvert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);\n\tvert_out.uv  = vert_in.uv;\n\treturn vert_out;\n}\n\nfloat4 PSDraw(VertInOut vert_in) : TARGET\n{\n\treturn float4(image.Sample(def_sampler, vert_in.uv).rgb, 1.0);\n}\n\nfloat4 PSDrawSrgbDecompress(VertInOut vert_in) : TARGET\n{\n\tfloat3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;\n\trgb = srgb_nonlinear_to_linear(rgb);\n\treturn float4(rgb, 1.0);\n}\n\nfloat4 PSDrawSrgbDecompressMultiply(VertInOut vert_in) : TARGET\n{\n\tfloat3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;\n\trgb = srgb_nonlinear_to_linear(rgb);\n\trgb *= multiplier;\n\treturn float4(rgb, 1.0);\n}\n\nfloat4 PSDrawMultiply(VertInOut vert_in) : TARGET\n{\n\tfloat3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;\n\trgb *= multiplier;\n\treturn float4(rgb, 1.0);\n}\n\nfloat4 PSDrawTonemap(VertInOut vert_in) : TARGET\n{\n\tfloat3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = reinhard(rgb);\n\trgb = rec2020_to_rec709(rgb);\n\treturn float4(rgb, 1.0);\n}\n\nfloat4 PSDrawMultiplyTonemap(VertInOut vert_in) : TARGET\n{\n\tfloat3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;\n\trgb *= multiplier;\n\trgb = rec709_to_rec2020(rgb);\n\trgb = reinhard(rgb);\n\trgb = rec2020_to_rec709(rgb);\n\treturn float4(rgb, 1.0);\n}\n\nfloat4 PSDrawPQ(VertInOut vert_in) : TARGET\n{\n\tfloat3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;\n\trgb = st2084_to_linear(rgb) * multiplier;\n\trgb = rec2020_to_rec709(rgb);\n\treturn float4(rgb, 1.0);\n}\n\nfloat4 PSDrawTonemapPQ(VertInOut vert_in) : TARGET\n{\n\tfloat3 rgb = image.Sample(def_sampler, vert_in.uv).rgb;\n\trgb = st2084_to_linear(rgb) * multiplier;\n\trgb = reinhard(rgb);\n\trgb = rec2020_to_rec709(rgb);\n\treturn float4(rgb, 1.0);\n}\n\ntechnique Draw\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDraw(vert_in);\n\t}\n}\n\ntechnique DrawSrgbDecompress\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawSrgbDecompress(vert_in);\n\t}\n}\n\ntechnique DrawSrgbDecompressMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawSrgbDecompressMultiply(vert_in);\n\t}\n}\n\ntechnique DrawMultiply\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawMultiply(vert_in);\n\t}\n}\n\ntechnique DrawTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawTonemap(vert_in);\n\t}\n}\n\ntechnique DrawMultiplyTonemap\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawMultiplyTonemap(vert_in);\n\t}\n}\n\ntechnique DrawPQ\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawPQ(vert_in);\n\t}\n}\n\ntechnique DrawTonemapPQ\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawTonemapPQ(vert_in);\n\t}\n}\n"
  },
  {
    "path": "libobs/data/premultiplied_alpha.effect",
    "content": "uniform float4x4 ViewProj;\nuniform texture2d image;\n\nsampler_state def_sampler {\n\tFilter   = Linear;\n\tAddressU = Clamp;\n\tAddressV = Clamp;\n};\n\nstruct VertInOut {\n\tfloat4 pos : POSITION;\n\tfloat2 uv  : TEXCOORD0;\n};\n\nVertInOut VSDefault(VertInOut vert_in)\n{\n\tVertInOut vert_out;\n\tvert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);\n\tvert_out.uv  = vert_in.uv;\n\treturn vert_out;\n}\n\nfloat4 PSDraw(VertInOut vert_in) : TARGET\n{\n\tfloat4 rgba = image.Sample(def_sampler, vert_in.uv);\n\tif (rgba.a > 0.0)\n\t\trgba.rgb /= rgba.a;\n\treturn saturate(rgba);\n}\n\ntechnique Draw\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDraw(vert_in);\n\t}\n}\n"
  },
  {
    "path": "libobs/data/repeat.effect",
    "content": "uniform float4x4 ViewProj;\nuniform texture2d image;\nuniform float2 scale;\n\nsampler_state def_sampler {\n\tFilter   = Linear;\n\tAddressU = Repeat;\n\tAddressV = Repeat;\n};\n\nstruct VertInOut {\n\tfloat4 pos : POSITION;\n\tfloat2 uv  : TEXCOORD0;\n};\n\nVertInOut VSDefault(VertInOut vert_in)\n{\n\tVertInOut vert_out;\n\tvert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);\n\tvert_out.uv  = vert_in.uv * scale;\n\treturn vert_out;\n}\n\nfloat4 PSDrawBare(VertInOut vert_in) : TARGET\n{\n\treturn image.Sample(def_sampler, vert_in.uv);\n}\n\ntechnique Draw\n{\n\tpass\n\t{\n\t\tvertex_shader = VSDefault(vert_in);\n\t\tpixel_shader  = PSDrawBare(vert_in);\n\t}\n}\n"
  },
  {
    "path": "libobs/data/solid.effect",
    "content": "uniform float4x4 ViewProj;\nuniform float4 color = {1.0, 1.0, 1.0, 1.0};\n\nuniform float4 randomvals1;\nuniform float4 randomvals2;\nuniform float4 randomvals3;\n\nstruct SolidVertInOut {\n\tfloat4 pos : POSITION;\n};\n\nSolidVertInOut VSSolid(SolidVertInOut vert_in)\n{\n\tSolidVertInOut vert_out;\n\tvert_out.pos = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);\n\treturn vert_out;\n}\n\nfloat4 PSSolid(SolidVertInOut vert_in) : TARGET\n{\n\treturn color;\n}\n\nfloat rand(float4 pos, float4 rand_vals)\n{\n\treturn 0.5 + 0.5 * frac(sin(dot(pos.xy, float2(rand_vals.x, rand_vals.y))) * rand_vals.z);\n}\n\nfloat4 PSRandom(SolidVertInOut vert_in) : TARGET\n{\n\treturn float4(rand(vert_in.pos, randomvals1),\n\t              rand(vert_in.pos, randomvals2),\n\t              rand(vert_in.pos, randomvals3),\n\t              1.0);\n}\n\nstruct SolidColoredVertInOut {\n\tfloat4 pos   : POSITION;\n\tfloat4 color : COLOR;\n};\n\nSolidColoredVertInOut VSSolidColored(SolidColoredVertInOut vert_in)\n{\n\tSolidColoredVertInOut vert_out;\n\tvert_out.pos   = mul(float4(vert_in.pos.xyz, 1.0), ViewProj);\n\tvert_out.color = vert_in.color;\n\treturn vert_out;\n}\n\nfloat4 PSSolidColored(SolidColoredVertInOut vert_in) : TARGET\n{\n\treturn vert_in.color * color;\n}\n\ntechnique Solid\n{\n\tpass\n\t{\n\t\tvertex_shader = VSSolid(vert_in);\n\t\tpixel_shader  = PSSolid(vert_in);\n\t}\n}\n\ntechnique SolidColored\n{\n\tpass\n\t{\n\t\tvertex_shader = VSSolidColored(vert_in);\n\t\tpixel_shader  = PSSolidColored(vert_in);\n\t}\n}\n\ntechnique Random\n{\n\tpass\n\t{\n\t\tvertex_shader = VSSolid(vert_in);\n\t\tpixel_shader  = PSRandom(vert_in);\n\t}\n}\n"
  },
  {
    "path": "libobs/graphics/axisang.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"axisang.h\"\n#include \"quat.h\"\n\nvoid axisang_from_quat(struct axisang *dst, const struct quat *q)\n{\n\tfloat len, leni;\n\n\tlen = q->x * q->x + q->y * q->y + q->z * q->z;\n\tif (!close_float(len, 0.0f, EPSILON)) {\n\t\tleni = 1.0f / sqrtf(len);\n\t\tdst->x = q->x * leni;\n\t\tdst->y = q->y * leni;\n\t\tdst->z = q->z * leni;\n\t\tdst->w = acosf(q->w) * 2.0f;\n\t} else {\n\t\tdst->x = 0.0f;\n\t\tdst->y = 0.0f;\n\t\tdst->z = 0.0f;\n\t\tdst->w = 0.0f;\n\t}\n}\n"
  },
  {
    "path": "libobs/graphics/axisang.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct quat;\n\nstruct axisang {\n\tunion {\n\t\tstruct {\n\t\t\tfloat x, y, z, w;\n\t\t};\n\t\tfloat ptr[4];\n\t};\n};\n\nstatic inline void axisang_zero(struct axisang *dst)\n{\n\tdst->x = 0.0f;\n\tdst->y = 0.0f;\n\tdst->z = 0.0f;\n\tdst->w = 0.0f;\n}\n\nstatic inline void axisang_copy(struct axisang *dst, struct axisang *aa)\n{\n\tdst->x = aa->x;\n\tdst->y = aa->y;\n\tdst->z = aa->z;\n\tdst->w = aa->w;\n}\n\nstatic inline void axisang_set(struct axisang *dst, float x, float y, float z, float w)\n{\n\tdst->x = x;\n\tdst->y = y;\n\tdst->z = z;\n\tdst->w = w;\n}\n\nEXPORT void axisang_from_quat(struct axisang *dst, const struct quat *q);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/basemath.hpp",
    "content": "#pragma once\n\n/* TODO: C++ math wrappers */\n"
  },
  {
    "path": "libobs/graphics/bounds.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <string.h>\n#include \"bounds.h\"\n#include \"matrix3.h\"\n#include \"matrix4.h\"\n#include \"plane.h\"\n\nvoid bounds_move(struct bounds *dst, const struct bounds *b, const struct vec3 *v)\n{\n\tvec3_add(&dst->min, &b->min, v);\n\tvec3_add(&dst->max, &b->max, v);\n}\n\nvoid bounds_scale(struct bounds *dst, const struct bounds *b, const struct vec3 *v)\n{\n\tvec3_mul(&dst->min, &b->min, v);\n\tvec3_mul(&dst->max, &b->max, v);\n}\n\nvoid bounds_merge(struct bounds *dst, const struct bounds *b1, const struct bounds *b2)\n{\n\tvec3_min(&dst->min, &b1->min, &b2->min);\n\tvec3_max(&dst->max, &b1->max, &b2->max);\n}\n\nvoid bounds_merge_point(struct bounds *dst, const struct bounds *b, const struct vec3 *v)\n{\n\tvec3_min(&dst->min, &b->min, v);\n\tvec3_max(&dst->max, &b->max, v);\n}\n\nvoid bounds_get_point(struct vec3 *dst, const struct bounds *b, unsigned int i)\n{\n\tif (i > 8)\n\t\treturn;\n\n\t/*\n\t * Note:\n\t * 0 = min.x,min.y,min.z\n\t * 1 = min.x,min.y,MAX.z\n\t * 2 = min.x,MAX.y,min.z\n\t * 3 = min.x,MAX.y,MAX.z\n\t * 4 = MAX.x,min.y,min.z\n\t * 5 = MAX.x,min.y,MAX.z\n\n\t * 6 = MAX.x,MAX.y,min.z\n\t * 7 = MAX.x,MAX.y,MAX.z\n\t */\n\n\tif (i > 3) {\n\t\tdst->x = b->max.x;\n\t\ti -= 4;\n\t} else {\n\t\tdst->x = b->min.x;\n\t}\n\n\tif (i > 1) {\n\t\tdst->y = b->max.y;\n\t\ti -= 2;\n\t} else {\n\t\tdst->y = b->min.y;\n\t}\n\n\tdst->z = (i == 1) ? b->max.z : b->min.z;\n}\n\nvoid bounds_get_center(struct vec3 *dst, const struct bounds *b)\n{\n\tvec3_sub(dst, &b->max, &b->min);\n\tvec3_mulf(dst, dst, 0.5f);\n\tvec3_add(dst, dst, &b->min);\n}\n\nvoid bounds_transform(struct bounds *dst, const struct bounds *b, const struct matrix4 *m)\n{\n\tstruct bounds temp;\n\tbool b_init = false;\n\tint i;\n\n\tmemset(&temp, 0, sizeof(temp));\n\n\tfor (i = 0; i < 8; i++) {\n\t\tstruct vec3 p;\n\t\tbounds_get_point(&p, b, i);\n\t\tvec3_transform(&p, &p, m);\n\n\t\tif (!b_init) {\n\t\t\tvec3_copy(&temp.min, &p);\n\t\t\tvec3_copy(&temp.max, &p);\n\t\t\tb_init = true;\n\t\t} else {\n\t\t\tif (p.x < temp.min.x)\n\t\t\t\ttemp.min.x = p.x;\n\t\t\telse if (p.x > temp.max.x)\n\t\t\t\ttemp.max.x = p.x;\n\n\t\t\tif (p.y < temp.min.y)\n\t\t\t\ttemp.min.y = p.y;\n\t\t\telse if (p.y > temp.max.y)\n\t\t\t\ttemp.max.y = p.y;\n\n\t\t\tif (p.z < temp.min.z)\n\t\t\t\ttemp.min.z = p.z;\n\t\t\telse if (p.z > temp.max.z)\n\t\t\t\ttemp.max.z = p.z;\n\t\t}\n\t}\n\n\tbounds_copy(dst, &temp);\n}\n\nvoid bounds_transform3x4(struct bounds *dst, const struct bounds *b, const struct matrix3 *m)\n{\n\tstruct bounds temp;\n\tbool b_init = false;\n\tint i;\n\n\tmemset(&temp, 0, sizeof(temp));\n\n\tfor (i = 0; i < 8; i++) {\n\t\tstruct vec3 p;\n\t\tbounds_get_point(&p, b, i);\n\t\tvec3_transform3x4(&p, &p, m);\n\n\t\tif (!b_init) {\n\t\t\tvec3_copy(&temp.min, &p);\n\t\t\tvec3_copy(&temp.max, &p);\n\t\t\tb_init = true;\n\t\t} else {\n\t\t\tif (p.x < temp.min.x)\n\t\t\t\ttemp.min.x = p.x;\n\t\t\telse if (p.x > temp.max.x)\n\t\t\t\ttemp.max.x = p.x;\n\n\t\t\tif (p.y < temp.min.y)\n\t\t\t\ttemp.min.y = p.y;\n\t\t\telse if (p.y > temp.max.y)\n\t\t\t\ttemp.max.y = p.y;\n\n\t\t\tif (p.z < temp.min.z)\n\t\t\t\ttemp.min.z = p.z;\n\t\t\telse if (p.z > temp.max.z)\n\t\t\t\ttemp.max.z = p.z;\n\t\t}\n\t}\n\n\tbounds_copy(dst, &temp);\n}\n\nbool bounds_intersection_ray(const struct bounds *b, const struct vec3 *orig, const struct vec3 *dir, float *t)\n{\n\tfloat t_max = M_INFINITE;\n\tfloat t_min = -M_INFINITE;\n\tstruct vec3 center, max_offset, box_offset;\n\tint i;\n\n\tbounds_get_center(&center, b);\n\tvec3_sub(&max_offset, &b->max, &center);\n\tvec3_sub(&box_offset, &center, orig);\n\n\tfor (i = 0; i < 3; i++) {\n\t\tfloat e = box_offset.ptr[i];\n\t\tfloat f = dir->ptr[i];\n\n\t\tif (fabsf(f) > 0.0f) {\n\t\t\tfloat fi = 1.0f / f;\n\t\t\tfloat t1 = (e + max_offset.ptr[i]) * fi;\n\t\t\tfloat t2 = (e - max_offset.ptr[i]) * fi;\n\t\t\tif (t1 > t2) {\n\t\t\t\tif (t2 > t_min)\n\t\t\t\t\tt_min = t2;\n\t\t\t\tif (t1 < t_max)\n\t\t\t\t\tt_max = t1;\n\t\t\t} else {\n\t\t\t\tif (t1 > t_min)\n\t\t\t\t\tt_min = t1;\n\t\t\t\tif (t2 < t_max)\n\t\t\t\t\tt_max = t2;\n\t\t\t}\n\t\t\tif (t_min > t_max)\n\t\t\t\treturn false;\n\t\t\tif (t_max < 0.0f)\n\t\t\t\treturn false;\n\t\t} else if ((-e - max_offset.ptr[i]) > 0.0f || (-e + max_offset.ptr[i]) < 0.0f) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t*t = (t_min > 0.0f) ? t_min : t_max;\n\treturn true;\n}\n\nbool bounds_intersection_line(const struct bounds *b, const struct vec3 *p1, const struct vec3 *p2, float *t)\n{\n\tstruct vec3 dir;\n\tfloat length;\n\n\tvec3_sub(&dir, p2, p1);\n\tlength = vec3_len(&dir);\n\tif (length <= TINY_EPSILON)\n\t\treturn false;\n\n\tvec3_mulf(&dir, &dir, 1.0f / length);\n\n\tif (!bounds_intersection_ray(b, p1, &dir, t))\n\t\treturn false;\n\n\t*t /= length;\n\treturn true;\n}\n\nbool bounds_plane_test(const struct bounds *b, const struct plane *p)\n{\n\tstruct vec3 vmin, vmax;\n\tint i;\n\n\tfor (i = 0; i < 3; i++) {\n\t\tif (p->dir.ptr[i] >= 0.0f) {\n\t\t\tvmin.ptr[i] = b->min.ptr[i];\n\t\t\tvmax.ptr[i] = b->max.ptr[i];\n\t\t} else {\n\t\t\tvmin.ptr[i] = b->max.ptr[i];\n\t\t\tvmax.ptr[i] = b->min.ptr[i];\n\t\t}\n\t}\n\n\tif (vec3_plane_dist(&vmin, p) > 0.0f)\n\t\treturn BOUNDS_OUTSIDE;\n\n\tif (vec3_plane_dist(&vmax, p) >= 0.0f)\n\t\treturn BOUNDS_PARTIAL;\n\n\treturn BOUNDS_INSIDE;\n}\n\nbool bounds_under_plane(const struct bounds *b, const struct plane *p)\n{\n\tstruct vec3 vmin;\n\n\tvmin.x = (p->dir.x < 0.0f) ? b->max.x : b->min.x;\n\tvmin.y = (p->dir.y < 0.0f) ? b->max.y : b->min.y;\n\tvmin.z = (p->dir.z < 0.0f) ? b->max.z : b->min.z;\n\n\treturn (vec3_dot(&vmin, &p->dir) <= p->dist);\n}\n\nbool bounds_intersects(const struct bounds *b, const struct bounds *test, float epsilon)\n{\n\treturn ((b->min.x - test->max.x) <= epsilon) && ((test->min.x - b->max.x) <= epsilon) &&\n\t       ((b->min.y - test->max.y) <= epsilon) && ((test->min.y - b->max.y) <= epsilon) &&\n\t       ((b->min.z - test->max.z) <= epsilon) && ((test->min.z - b->max.z) <= epsilon);\n}\n\nbool bounds_intersects_obb(const struct bounds *b, const struct bounds *test, const struct matrix4 *m, float epsilon)\n{\n\tstruct bounds b_tr, test_tr;\n\tstruct matrix4 m_inv;\n\n\tmatrix4_inv(&m_inv, m);\n\n\tbounds_transform(&b_tr, b, m);\n\tbounds_transform(&test_tr, test, &m_inv);\n\n\treturn bounds_intersects(b, &test_tr, epsilon) && bounds_intersects(&b_tr, test, epsilon);\n}\n\nbool bounds_intersects_obb3x4(const struct bounds *b, const struct bounds *test, const struct matrix3 *m, float epsilon)\n{\n\tstruct bounds b_tr, test_tr;\n\tstruct matrix3 m_inv;\n\n\tmatrix3_transpose(&m_inv, m);\n\n\tbounds_transform3x4(&b_tr, b, m);\n\tbounds_transform3x4(&test_tr, test, &m_inv);\n\n\treturn bounds_intersects(b, &test_tr, epsilon) && bounds_intersects(&b_tr, test, epsilon);\n}\n\nstatic inline float vec3or_offset_len(const struct bounds *b, const struct vec3 *v)\n{\n\tstruct vec3 temp1, temp2;\n\tvec3_sub(&temp1, &b->max, &b->min);\n\tvec3_abs(&temp2, v);\n\treturn vec3_dot(&temp1, &temp2);\n}\n\nfloat bounds_min_dist(const struct bounds *b, const struct plane *p)\n{\n\tstruct vec3 center;\n\tfloat vec_len = vec3or_offset_len(b, &p->dir) * 0.5f;\n\tfloat center_dist;\n\n\tbounds_get_center(&center, b);\n\tcenter_dist = vec3_plane_dist(&center, p);\n\n\treturn p->dist + center_dist - vec_len;\n}\n"
  },
  {
    "path": "libobs/graphics/bounds.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"math-defs.h\"\n#include \"vec3.h\"\n\n/*\n * Axis Aligned Bounding Box\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define BOUNDS_OUTSIDE 1\n#define BOUNDS_INSIDE 2\n#define BOUNDS_PARTIAL 3\n\nstruct bounds {\n\tstruct vec3 min, max;\n};\n\nstatic inline void bounds_zero(struct bounds *dst)\n{\n\tvec3_zero(&dst->min);\n\tvec3_zero(&dst->max);\n}\n\nstatic inline void bounds_copy(struct bounds *dst, const struct bounds *b)\n{\n\tvec3_copy(&dst->min, &b->min);\n\tvec3_copy(&dst->max, &b->max);\n}\n\nEXPORT void bounds_move(struct bounds *dst, const struct bounds *b, const struct vec3 *v);\n\nEXPORT void bounds_scale(struct bounds *dst, const struct bounds *b, const struct vec3 *v);\n\nEXPORT void bounds_merge(struct bounds *dst, const struct bounds *b1, const struct bounds *b2);\nEXPORT void bounds_merge_point(struct bounds *dst, const struct bounds *b, const struct vec3 *v);\n\nEXPORT void bounds_get_point(struct vec3 *dst, const struct bounds *b, unsigned int i);\nEXPORT void bounds_get_center(struct vec3 *dst, const struct bounds *b);\n\n/**\n * Note: transforms as OBB, then converts back to AABB, which can result in\n * the actual size becoming larger than it originally was.\n */\nEXPORT void bounds_transform(struct bounds *dst, const struct bounds *b, const struct matrix4 *m);\nEXPORT void bounds_transform3x4(struct bounds *dst, const struct bounds *b, const struct matrix3 *m);\n\nEXPORT bool bounds_intersection_ray(const struct bounds *b, const struct vec3 *orig, const struct vec3 *dir, float *t);\nEXPORT bool bounds_intersection_line(const struct bounds *b, const struct vec3 *p1, const struct vec3 *p2, float *t);\n\nEXPORT bool bounds_plane_test(const struct bounds *b, const struct plane *p);\nEXPORT bool bounds_under_plane(const struct bounds *b, const struct plane *p);\n\nstatic inline bool bounds_inside(const struct bounds *b, const struct bounds *test)\n{\n\treturn test->min.x >= b->min.x && test->min.y >= b->min.y && test->min.z >= b->min.z &&\n\t       test->max.x <= b->max.x && test->max.y <= b->max.y && test->max.z <= b->max.z;\n}\n\nstatic inline bool bounds_vec3_inside(const struct bounds *b, const struct vec3 *v)\n{\n\treturn v->x >= (b->min.x - EPSILON) && v->x <= (b->max.x + EPSILON) && v->y >= (b->min.y - EPSILON) &&\n\t       v->y <= (b->max.y + EPSILON) && v->z >= (b->min.z - EPSILON) && v->z <= (b->max.z + EPSILON);\n}\n\nEXPORT bool bounds_intersects(const struct bounds *b, const struct bounds *test, float epsilon);\nEXPORT bool bounds_intersects_obb(const struct bounds *b, const struct bounds *test, const struct matrix4 *m,\n\t\t\t\t  float epsilon);\nEXPORT bool bounds_intersects_obb3x4(const struct bounds *b, const struct bounds *test, const struct matrix3 *m,\n\t\t\t\t     float epsilon);\n\nstatic inline bool bounds_intersects_ray(const struct bounds *b, const struct vec3 *orig, const struct vec3 *dir)\n{\n\tfloat t;\n\treturn bounds_intersection_ray(b, orig, dir, &t);\n}\n\nstatic inline bool bounds_intersects_line(const struct bounds *b, const struct vec3 *p1, const struct vec3 *p2)\n{\n\tfloat t;\n\treturn bounds_intersection_line(b, p1, p2, &t);\n}\n\nEXPORT float bounds_min_dist(const struct bounds *b, const struct plane *p);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/device-exports.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <util/c99defs.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nEXPORT const char *device_get_name(void);\nEXPORT const char *gpu_get_driver_version(void);\nEXPORT const char *gpu_get_renderer(void);\nEXPORT uint64_t gpu_get_dmem(void);\nEXPORT uint64_t gpu_get_smem(void);\nEXPORT int device_get_type(void);\nEXPORT bool device_enum_adapters(gs_device_t *device, bool (*callback)(void *param, const char *name, uint32_t id),\n\t\t\t\t void *param);\nEXPORT const char *device_preprocessor_name(void);\nEXPORT int device_create(gs_device_t **device, uint32_t adapter);\nEXPORT void device_destroy(gs_device_t *device);\nEXPORT void device_enter_context(gs_device_t *device);\nEXPORT void device_leave_context(gs_device_t *device);\nEXPORT void *device_get_device_obj(gs_device_t *device);\nEXPORT gs_swapchain_t *device_swapchain_create(gs_device_t *device, const struct gs_init_data *data);\nEXPORT void device_resize(gs_device_t *device, uint32_t x, uint32_t y);\nEXPORT enum gs_color_space device_get_color_space(gs_device_t *device);\nEXPORT void device_update_color_space(gs_device_t *device);\nEXPORT void device_get_size(const gs_device_t *device, uint32_t *x, uint32_t *y);\nEXPORT uint32_t device_get_width(const gs_device_t *device);\nEXPORT uint32_t device_get_height(const gs_device_t *device);\nEXPORT gs_texture_t *device_texture_create(gs_device_t *device, uint32_t width, uint32_t height,\n\t\t\t\t\t   enum gs_color_format color_format, uint32_t levels, const uint8_t **data,\n\t\t\t\t\t   uint32_t flags);\nEXPORT gs_texture_t *device_cubetexture_create(gs_device_t *device, uint32_t size, enum gs_color_format color_format,\n\t\t\t\t\t       uint32_t levels, const uint8_t **data, uint32_t flags);\nEXPORT gs_texture_t *device_voltexture_create(gs_device_t *device, uint32_t width, uint32_t height, uint32_t depth,\n\t\t\t\t\t      enum gs_color_format color_format, uint32_t levels,\n\t\t\t\t\t      const uint8_t *const *data, uint32_t flags);\nEXPORT gs_zstencil_t *device_zstencil_create(gs_device_t *device, uint32_t width, uint32_t height,\n\t\t\t\t\t     enum gs_zstencil_format format);\nEXPORT gs_stagesurf_t *device_stagesurface_create(gs_device_t *device, uint32_t width, uint32_t height,\n\t\t\t\t\t\t  enum gs_color_format color_format);\nEXPORT gs_samplerstate_t *device_samplerstate_create(gs_device_t *device, const struct gs_sampler_info *info);\nEXPORT gs_shader_t *device_vertexshader_create(gs_device_t *device, const char *shader, const char *file,\n\t\t\t\t\t       char **error_string);\nEXPORT gs_shader_t *device_pixelshader_create(gs_device_t *device, const char *shader, const char *file,\n\t\t\t\t\t      char **error_string);\nEXPORT gs_vertbuffer_t *device_vertexbuffer_create(gs_device_t *device, struct gs_vb_data *data, uint32_t flags);\nEXPORT gs_indexbuffer_t *device_indexbuffer_create(gs_device_t *device, enum gs_index_type type, void *indices,\n\t\t\t\t\t\t   size_t num, uint32_t flags);\nEXPORT gs_timer_t *device_timer_create(gs_device_t *device);\nEXPORT gs_timer_range_t *device_timer_range_create(gs_device_t *device);\nEXPORT enum gs_texture_type device_get_texture_type(const gs_texture_t *texture);\nEXPORT void device_load_vertexbuffer(gs_device_t *device, gs_vertbuffer_t *vertbuffer);\nEXPORT void device_load_indexbuffer(gs_device_t *device, gs_indexbuffer_t *indexbuffer);\nEXPORT void device_load_texture(gs_device_t *device, gs_texture_t *tex, int unit);\nEXPORT void device_load_texture_srgb(gs_device_t *device, gs_texture_t *tex, int unit);\nEXPORT void device_load_samplerstate(gs_device_t *device, gs_samplerstate_t *samplerstate, int unit);\nEXPORT void device_load_vertexshader(gs_device_t *device, gs_shader_t *vertshader);\nEXPORT void device_load_pixelshader(gs_device_t *device, gs_shader_t *pixelshader);\nEXPORT void device_load_default_samplerstate(gs_device_t *device, bool b_3d, int unit);\nEXPORT gs_shader_t *device_get_vertex_shader(const gs_device_t *device);\nEXPORT gs_shader_t *device_get_pixel_shader(const gs_device_t *device);\nEXPORT gs_texture_t *device_get_render_target(const gs_device_t *device);\nEXPORT gs_zstencil_t *device_get_zstencil_target(const gs_device_t *device);\nEXPORT void device_set_render_target(gs_device_t *device, gs_texture_t *tex, gs_zstencil_t *zstencil);\nEXPORT void device_set_render_target_with_color_space(gs_device_t *device, gs_texture_t *tex, gs_zstencil_t *zstencil,\n\t\t\t\t\t\t      enum gs_color_space space);\nEXPORT void device_set_cube_render_target(gs_device_t *device, gs_texture_t *cubetex, int side,\n\t\t\t\t\t  gs_zstencil_t *zstencil);\nEXPORT void device_enable_framebuffer_srgb(gs_device_t *device, bool enable);\nEXPORT bool device_framebuffer_srgb_enabled(gs_device_t *device);\nEXPORT void device_copy_texture(gs_device_t *device, gs_texture_t *dst, gs_texture_t *src);\nEXPORT void device_copy_texture_region(gs_device_t *device, gs_texture_t *dst, uint32_t dst_x, uint32_t dst_y,\n\t\t\t\t       gs_texture_t *src, uint32_t src_x, uint32_t src_y, uint32_t src_w,\n\t\t\t\t       uint32_t src_h);\nEXPORT void device_stage_texture(gs_device_t *device, gs_stagesurf_t *dst, gs_texture_t *src);\nEXPORT void device_begin_frame(gs_device_t *device);\nEXPORT void device_begin_scene(gs_device_t *device);\nEXPORT void device_draw(gs_device_t *device, enum gs_draw_mode draw_mode, uint32_t start_vert, uint32_t num_verts);\nEXPORT void device_end_scene(gs_device_t *device);\nEXPORT void device_load_swapchain(gs_device_t *device, gs_swapchain_t *swapchain);\nEXPORT void device_clear(gs_device_t *device, uint32_t clear_flags, const struct vec4 *color, float depth,\n\t\t\t uint8_t stencil);\nEXPORT bool device_is_present_ready(gs_device_t *device);\nEXPORT void device_present(gs_device_t *device);\nEXPORT void device_flush(gs_device_t *device);\nEXPORT void device_set_cull_mode(gs_device_t *device, enum gs_cull_mode mode);\nEXPORT enum gs_cull_mode device_get_cull_mode(const gs_device_t *device);\nEXPORT void device_enable_blending(gs_device_t *device, bool enable);\nEXPORT void device_enable_depth_test(gs_device_t *device, bool enable);\nEXPORT void device_enable_stencil_test(gs_device_t *device, bool enable);\nEXPORT void device_enable_stencil_write(gs_device_t *device, bool enable);\nEXPORT void device_enable_color(gs_device_t *device, bool red, bool green, bool blue, bool alpha);\nEXPORT void device_blend_function(gs_device_t *device, enum gs_blend_type src, enum gs_blend_type dest);\nEXPORT void device_blend_function_separate(gs_device_t *device, enum gs_blend_type src_c, enum gs_blend_type dest_c,\n\t\t\t\t\t   enum gs_blend_type src_a, enum gs_blend_type dest_a);\nEXPORT void device_blend_op(gs_device_t *device, enum gs_blend_op_type op);\n\nEXPORT void device_depth_function(gs_device_t *device, enum gs_depth_test test);\nEXPORT void device_stencil_function(gs_device_t *device, enum gs_stencil_side side, enum gs_depth_test test);\nEXPORT void device_stencil_op(gs_device_t *device, enum gs_stencil_side side, enum gs_stencil_op_type fail,\n\t\t\t      enum gs_stencil_op_type zfail, enum gs_stencil_op_type zpass);\nEXPORT void device_set_viewport(gs_device_t *device, int x, int y, int width, int height);\nEXPORT void device_get_viewport(const gs_device_t *device, struct gs_rect *rect);\nEXPORT void device_set_scissor_rect(gs_device_t *device, const struct gs_rect *rect);\nEXPORT void device_ortho(gs_device_t *device, float left, float right, float top, float bottom, float znear,\n\t\t\t float zfar);\nEXPORT void device_frustum(gs_device_t *device, float left, float right, float top, float bottom, float znear,\n\t\t\t   float zfar);\nEXPORT void device_projection_push(gs_device_t *device);\nEXPORT void device_projection_pop(gs_device_t *device);\nEXPORT void device_debug_marker_begin(gs_device_t *device, const char *markername, const float color[4]);\nEXPORT void device_debug_marker_end(gs_device_t *device);\nEXPORT bool device_is_monitor_hdr(gs_device_t *device, void *monitor);\nEXPORT bool device_shared_texture_available(void);\nEXPORT bool device_nv12_available(gs_device_t *device);\nEXPORT bool device_p010_available(gs_device_t *device);\n\n#ifdef __APPLE__\nEXPORT gs_texture_t *device_texture_create_from_iosurface(gs_device_t *device, void *iosurf);\nEXPORT gs_texture_t *device_texture_open_shared(gs_device_t *device, uint32_t handle);\n#endif\n\n#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)\n\nEXPORT gs_texture_t *device_texture_create_from_dmabuf(gs_device_t *device, unsigned int width, unsigned int height,\n\t\t\t\t\t\t       uint32_t drm_format, enum gs_color_format color_format,\n\t\t\t\t\t\t       uint32_t n_planes, const int *fds, const uint32_t *strides,\n\t\t\t\t\t\t       const uint32_t *offsets, const uint64_t *modifiers);\n\nEXPORT bool device_query_dmabuf_capabilities(gs_device_t *device, enum gs_dmabuf_flags *gs_dmabuf_flags,\n\t\t\t\t\t     uint32_t **drm_formats, size_t *n_formats);\n\nEXPORT bool device_query_dmabuf_modifiers_for_format(gs_device_t *device, uint32_t drm_format, uint64_t **modifiers,\n\t\t\t\t\t\t     size_t *n_modifiers);\n\nEXPORT gs_texture_t *device_texture_create_from_pixmap(gs_device_t *device, uint32_t width, uint32_t height,\n\t\t\t\t\t\t       enum gs_color_format color_format, uint32_t target,\n\t\t\t\t\t\t       void *pixmap);\n\nEXPORT bool device_query_sync_capabilities(gs_device_t *device);\n\nEXPORT gs_sync_t *device_sync_create(gs_device_t *device);\n\nEXPORT gs_sync_t *device_sync_create_from_syncobj_timeline_point(gs_device_t *device, int syncobj_fd,\n\t\t\t\t\t\t\t\t uint64_t timeline_point);\n\nEXPORT void device_sync_destroy(gs_device_t *device, gs_sync_t *sync);\n\nEXPORT bool device_sync_export_syncobj_timeline_point(gs_device_t *device, gs_sync_t *sync, int syncobj_fd,\n\t\t\t\t\t\t      uint64_t timeline_point);\n\nEXPORT bool device_sync_signal_syncobj_timeline_point(gs_device_t *device, int syncobj_fd, uint64_t timeline_point);\n\nEXPORT bool device_sync_wait(gs_device_t *device, gs_sync_t *sync);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/effect-parser.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <assert.h>\n#include <limits.h>\n#include \"../util/platform.h\"\n#include \"effect-parser.h\"\n#include \"effect.h\"\n\ntypedef DARRAY(struct dstr) dstr_array_t;\n\nstatic inline bool ep_parse_param_assign(struct effect_parser *ep, struct ep_param *param);\n\nstatic enum gs_shader_param_type get_effect_param_type(const char *type)\n{\n\tif (strcmp(type, \"float\") == 0)\n\t\treturn GS_SHADER_PARAM_FLOAT;\n\telse if (strcmp(type, \"float2\") == 0)\n\t\treturn GS_SHADER_PARAM_VEC2;\n\telse if (strcmp(type, \"float3\") == 0)\n\t\treturn GS_SHADER_PARAM_VEC3;\n\telse if (strcmp(type, \"float4\") == 0)\n\t\treturn GS_SHADER_PARAM_VEC4;\n\telse if (strcmp(type, \"int2\") == 0)\n\t\treturn GS_SHADER_PARAM_INT2;\n\telse if (strcmp(type, \"int3\") == 0)\n\t\treturn GS_SHADER_PARAM_INT3;\n\telse if (strcmp(type, \"int4\") == 0)\n\t\treturn GS_SHADER_PARAM_INT4;\n\telse if (astrcmp_n(type, \"texture\", 7) == 0)\n\t\treturn GS_SHADER_PARAM_TEXTURE;\n\telse if (strcmp(type, \"float4x4\") == 0)\n\t\treturn GS_SHADER_PARAM_MATRIX4X4;\n\telse if (strcmp(type, \"bool\") == 0)\n\t\treturn GS_SHADER_PARAM_BOOL;\n\telse if (strcmp(type, \"int\") == 0)\n\t\treturn GS_SHADER_PARAM_INT;\n\telse if (strcmp(type, \"string\") == 0)\n\t\treturn GS_SHADER_PARAM_STRING;\n\n\treturn GS_SHADER_PARAM_UNKNOWN;\n}\n\nvoid ep_free(struct effect_parser *ep)\n{\n\tsize_t i;\n\tfor (i = 0; i < ep->params.num; i++)\n\t\tep_param_free(ep->params.array + i);\n\tfor (i = 0; i < ep->structs.num; i++)\n\t\tep_struct_free(ep->structs.array + i);\n\tfor (i = 0; i < ep->funcs.num; i++)\n\t\tep_func_free(ep->funcs.array + i);\n\tfor (i = 0; i < ep->samplers.num; i++)\n\t\tep_sampler_free(ep->samplers.array + i);\n\tfor (i = 0; i < ep->techniques.num; i++)\n\t\tep_technique_free(ep->techniques.array + i);\n\n\tep->cur_pass = NULL;\n\tcf_parser_free(&ep->cfp);\n\tda_free(ep->params);\n\tda_free(ep->structs);\n\tda_free(ep->funcs);\n\tda_free(ep->samplers);\n\tda_free(ep->techniques);\n}\n\nstatic inline struct ep_func *ep_getfunc(struct effect_parser *ep, const char *name)\n{\n\tsize_t i;\n\tfor (i = 0; i < ep->funcs.num; i++) {\n\t\tif (strcmp(name, ep->funcs.array[i].name) == 0)\n\t\t\treturn ep->funcs.array + i;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct ep_struct *ep_getstruct(struct effect_parser *ep, const char *name)\n{\n\tsize_t i;\n\tfor (i = 0; i < ep->structs.num; i++) {\n\t\tif (strcmp(name, ep->structs.array[i].name) == 0)\n\t\t\treturn ep->structs.array + i;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct ep_sampler *ep_getsampler(struct effect_parser *ep, const char *name)\n{\n\tsize_t i;\n\tfor (i = 0; i < ep->samplers.num; i++) {\n\t\tif (strcmp(name, ep->samplers.array[i].name) == 0)\n\t\t\treturn ep->samplers.array + i;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct ep_param *ep_getparam(struct effect_parser *ep, const char *name)\n{\n\tsize_t i;\n\tfor (i = 0; i < ep->params.num; i++) {\n\t\tif (strcmp(name, ep->params.array[i].name) == 0)\n\t\t\treturn ep->params.array + i;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct ep_param *ep_getannotation(struct ep_param *param, const char *name)\n{\n\tsize_t i;\n\tfor (i = 0; i < param->annotations.num; i++) {\n\t\tif (strcmp(name, param->annotations.array[i].name) == 0)\n\t\t\treturn param->annotations.array + i;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct ep_func *ep_getfunc_strref(struct effect_parser *ep, const struct strref *ref)\n{\n\tsize_t i;\n\tfor (i = 0; i < ep->funcs.num; i++) {\n\t\tif (strref_cmp(ref, ep->funcs.array[i].name) == 0)\n\t\t\treturn ep->funcs.array + i;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct ep_struct *ep_getstruct_strref(struct effect_parser *ep, const struct strref *ref)\n{\n\tsize_t i;\n\tfor (i = 0; i < ep->structs.num; i++) {\n\t\tif (strref_cmp(ref, ep->structs.array[i].name) == 0)\n\t\t\treturn ep->structs.array + i;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct ep_sampler *ep_getsampler_strref(struct effect_parser *ep, const struct strref *ref)\n{\n\tsize_t i;\n\tfor (i = 0; i < ep->samplers.num; i++) {\n\t\tif (strref_cmp(ref, ep->samplers.array[i].name) == 0)\n\t\t\treturn ep->samplers.array + i;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct ep_param *ep_getparam_strref(struct effect_parser *ep, const struct strref *ref)\n{\n\tsize_t i;\n\tfor (i = 0; i < ep->params.num; i++) {\n\t\tif (strref_cmp(ref, ep->params.array[i].name) == 0)\n\t\t\treturn ep->params.array + i;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline int ep_parse_struct_var(struct effect_parser *ep, struct ep_var *var)\n{\n\tint code;\n\n\t/* -------------------------------------- */\n\t/* variable type */\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&ep->cfp, \";\"))\n\t\treturn PARSE_CONTINUE;\n\tif (cf_token_is(&ep->cfp, \"}\"))\n\t\treturn PARSE_BREAK;\n\n\tcode = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, \"type name\", \";\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tcf_copy_token(&ep->cfp, &var->type);\n\n\t/* -------------------------------------- */\n\t/* variable name */\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&ep->cfp, \";\"))\n\t\treturn PARSE_UNEXPECTED_CONTINUE;\n\tif (cf_token_is(&ep->cfp, \"}\"))\n\t\treturn PARSE_UNEXPECTED_BREAK;\n\n\tcode = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, \"variable name\", \";\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tcf_copy_token(&ep->cfp, &var->name);\n\n\t/* -------------------------------------- */\n\t/* variable mapping if any (POSITION, TEXCOORD, etc) */\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&ep->cfp, \":\")) {\n\t\tif (!cf_next_valid_token(&ep->cfp))\n\t\t\treturn PARSE_EOF;\n\n\t\tif (cf_token_is(&ep->cfp, \";\"))\n\t\t\treturn PARSE_UNEXPECTED_CONTINUE;\n\t\tif (cf_token_is(&ep->cfp, \"}\"))\n\t\t\treturn PARSE_UNEXPECTED_BREAK;\n\n\t\tcode = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, \"mapping name\", \";\");\n\t\tif (code != PARSE_SUCCESS)\n\t\t\treturn code;\n\n\t\tcf_copy_token(&ep->cfp, &var->mapping);\n\n\t\tif (!cf_next_valid_token(&ep->cfp))\n\t\t\treturn PARSE_EOF;\n\t}\n\n\t/* -------------------------------------- */\n\n\tif (!cf_token_is(&ep->cfp, \";\")) {\n\t\tif (!cf_go_to_valid_token(&ep->cfp, \";\", \"}\"))\n\t\t\treturn PARSE_EOF;\n\t\treturn PARSE_CONTINUE;\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic void ep_parse_struct(struct effect_parser *ep)\n{\n\tstruct ep_struct eps;\n\tep_struct_init(&eps);\n\n\tif (cf_next_name(&ep->cfp, &eps.name, \"name\", \";\") != PARSE_SUCCESS)\n\t\tgoto error;\n\tif (cf_next_token_should_be(&ep->cfp, \"{\", \";\", NULL) != PARSE_SUCCESS)\n\t\tgoto error;\n\n\t/* get structure variables */\n\twhile (true) {\n\t\tbool do_break = false;\n\t\tstruct ep_var var;\n\n\t\tep_var_init(&var);\n\n\t\tswitch (ep_parse_struct_var(ep, &var)) {\n\n\t\tcase PARSE_UNEXPECTED_CONTINUE:\n\t\t\tcf_adderror_syntax_error(&ep->cfp);\n\t\t\t/* Falls through. */\n\t\tcase PARSE_CONTINUE:\n\t\t\tep_var_free(&var);\n\t\t\tcontinue;\n\n\t\tcase PARSE_UNEXPECTED_BREAK:\n\t\t\tcf_adderror_syntax_error(&ep->cfp);\n\t\t\t/* Falls through. */\n\t\tcase PARSE_BREAK:\n\t\t\tep_var_free(&var);\n\t\t\tdo_break = true;\n\t\t\tbreak;\n\n\t\tcase PARSE_EOF:\n\t\t\tep_var_free(&var);\n\t\t\tgoto error;\n\t\t}\n\n\t\tif (do_break)\n\t\t\tbreak;\n\n\t\tda_push_back(eps.vars, &var);\n\t}\n\n\tif (cf_next_token_should_be(&ep->cfp, \";\", NULL, NULL) != PARSE_SUCCESS)\n\t\tgoto error;\n\n\tda_push_back(ep->structs, &eps);\n\treturn;\n\nerror:\n\tep_struct_free(&eps);\n}\n\nstatic inline int ep_parse_param_annotation_var(struct effect_parser *ep, struct ep_param *var)\n{\n\tint code;\n\n\t/* -------------------------------------- */\n\t/* variable type */\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&ep->cfp, \";\"))\n\t\treturn PARSE_CONTINUE;\n\tif (cf_token_is(&ep->cfp, \">\"))\n\t\treturn PARSE_BREAK;\n\n\tcode = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, \"type name\", \";\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tbfree(var->type);\n\tcf_copy_token(&ep->cfp, &var->type);\n\n\t/* -------------------------------------- */\n\t/* variable name */\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&ep->cfp, \";\")) {\n\t\tcf_adderror_expecting(&ep->cfp, \"variable name\");\n\t\treturn PARSE_UNEXPECTED_CONTINUE;\n\t}\n\tif (cf_token_is(&ep->cfp, \">\")) {\n\t\tcf_adderror_expecting(&ep->cfp, \"variable name\");\n\t\treturn PARSE_UNEXPECTED_BREAK;\n\t}\n\n\tcode = cf_token_is_type(&ep->cfp, CFTOKEN_NAME, \"variable name\", \";\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tbfree(var->name);\n\tcf_copy_token(&ep->cfp, &var->name);\n\n\t/* -------------------------------------- */\n\t/* variable mapping if any (POSITION, TEXCOORD, etc) */\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&ep->cfp, \":\")) {\n\t\tcf_adderror_expecting(&ep->cfp, \"= or ;\");\n\t\treturn PARSE_UNEXPECTED_BREAK;\n\t} else if (cf_token_is(&ep->cfp, \">\")) {\n\t\tcf_adderror_expecting(&ep->cfp, \"= or ;\");\n\t\treturn PARSE_UNEXPECTED_BREAK;\n\t} else if (cf_token_is(&ep->cfp, \"=\")) {\n\t\tif (!ep_parse_param_assign(ep, var)) {\n\t\t\tcf_adderror_expecting(&ep->cfp, \"assignment value\");\n\t\t\treturn PARSE_UNEXPECTED_BREAK;\n\t\t}\n\t}\n\n\t/* -------------------------------------- */\n\n\tif (!cf_token_is(&ep->cfp, \";\")) {\n\t\tif (!cf_go_to_valid_token(&ep->cfp, \";\", \">\")) {\n\t\t\tcf_adderror_expecting(&ep->cfp, \"; or >\");\n\t\t\treturn PARSE_EOF;\n\t\t}\n\t\treturn PARSE_CONTINUE;\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic int ep_parse_annotations(struct effect_parser *ep, ep_param_array_t *annotations)\n{\n\tif (!cf_token_is(&ep->cfp, \"<\")) {\n\t\tcf_adderror_expecting(&ep->cfp, \"<\");\n\t\tgoto error;\n\t}\n\n\t/* get annotation variables */\n\twhile (true) {\n\t\tbool do_break = false;\n\t\tstruct ep_param var;\n\n\t\tep_param_init(&var, bstrdup(\"\"), bstrdup(\"\"), false, false, false);\n\n\t\tswitch (ep_parse_param_annotation_var(ep, &var)) {\n\t\tcase PARSE_UNEXPECTED_CONTINUE:\n\t\t\tcf_adderror_syntax_error(&ep->cfp);\n\t\t\t/* Falls through. */\n\t\tcase PARSE_CONTINUE:\n\t\t\tep_param_free(&var);\n\t\t\tcontinue;\n\n\t\tcase PARSE_UNEXPECTED_BREAK:\n\t\t\tcf_adderror_syntax_error(&ep->cfp);\n\t\t\t/* Falls through. */\n\t\tcase PARSE_BREAK:\n\t\t\tep_param_free(&var);\n\t\t\tdo_break = true;\n\t\t\tbreak;\n\n\t\tcase PARSE_EOF:\n\t\t\tep_param_free(&var);\n\t\t\tgoto error;\n\t\t}\n\n\t\tif (do_break)\n\t\t\tbreak;\n\n\t\tda_push_back(*annotations, &var);\n\t}\n\n\tif (!cf_token_is(&ep->cfp, \">\")) {\n\t\tcf_adderror_expecting(&ep->cfp, \">\");\n\t\tgoto error;\n\t}\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\tgoto error;\n\n\treturn true;\n\nerror:\n\treturn false;\n}\n\nstatic int ep_parse_param_annotations(struct effect_parser *ep, struct ep_param *param)\n{\n\treturn ep_parse_annotations(ep, &param->annotations);\n}\n\nstatic inline int ep_parse_pass_command_call(struct effect_parser *ep, cf_token_array_t *call)\n{\n\tstruct cf_token end_token;\n\tcf_token_clear(&end_token);\n\n\twhile (!cf_token_is(&ep->cfp, \";\")) {\n\t\tif (cf_token_is(&ep->cfp, \"}\")) {\n\t\t\tcf_adderror_expecting(&ep->cfp, \";\");\n\t\t\treturn PARSE_CONTINUE;\n\t\t}\n\n\t\tda_push_back(*call, ep->cfp.cur_token);\n\t\tif (!cf_next_valid_token(&ep->cfp))\n\t\t\treturn PARSE_EOF;\n\t}\n\n\tda_push_back(*call, ep->cfp.cur_token);\n\tda_push_back(*call, &end_token);\n\treturn PARSE_SUCCESS;\n}\n\nstatic int ep_parse_pass_command(struct effect_parser *ep, struct ep_pass *pass)\n{\n\tcf_token_array_t *call;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&ep->cfp, \"vertex_shader\") || cf_token_is(&ep->cfp, \"vertex_program\")) {\n\t\tcall = &pass->vertex_program;\n\n\t} else if (cf_token_is(&ep->cfp, \"pixel_shader\") || cf_token_is(&ep->cfp, \"pixel_program\")) {\n\t\tcall = &pass->fragment_program;\n\n\t} else {\n\t\tcf_adderror_syntax_error(&ep->cfp);\n\t\tif (!cf_go_to_valid_token(&ep->cfp, \";\", \"}\"))\n\t\t\treturn PARSE_EOF;\n\t\treturn PARSE_CONTINUE;\n\t}\n\n\tif (cf_next_token_should_be(&ep->cfp, \"=\", \";\", \"}\") != PARSE_SUCCESS)\n\t\treturn PARSE_CONTINUE;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\tif (cf_token_is(&ep->cfp, \"compile\")) {\n\t\tcf_adderror(&ep->cfp, \"compile keyword not necessary\", LEX_WARNING, NULL, NULL, NULL);\n\t\tif (!cf_next_valid_token(&ep->cfp))\n\t\t\treturn PARSE_EOF;\n\t}\n\n\treturn ep_parse_pass_command_call(ep, call);\n}\n\nstatic int ep_parse_pass(struct effect_parser *ep, struct ep_pass *pass)\n{\n\tstruct cf_token peek;\n\n\tif (!cf_token_is(&ep->cfp, \"pass\"))\n\t\treturn PARSE_UNEXPECTED_CONTINUE;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (!cf_token_is(&ep->cfp, \"{\")) {\n\t\tpass->name = bstrdup_n(ep->cfp.cur_token->str.array, ep->cfp.cur_token->str.len);\n\t\tif (!cf_next_valid_token(&ep->cfp))\n\t\t\treturn PARSE_EOF;\n\t}\n\n\tif (!cf_peek_valid_token(&ep->cfp, &peek))\n\t\treturn PARSE_EOF;\n\n\twhile (strref_cmp(&peek.str, \"}\") != 0) {\n\t\tint ret = ep_parse_pass_command(ep, pass);\n\t\tif (ret < 0 && ret != PARSE_CONTINUE)\n\t\t\treturn ret;\n\n\t\tif (!cf_peek_valid_token(&ep->cfp, &peek))\n\t\t\treturn PARSE_EOF;\n\t}\n\n\t/* token is '}' */\n\tcf_next_token(&ep->cfp);\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic void ep_parse_technique(struct effect_parser *ep)\n{\n\tstruct ep_technique ept;\n\tep_technique_init(&ept);\n\n\tif (cf_next_name(&ep->cfp, &ept.name, \"name\", \";\") != PARSE_SUCCESS)\n\t\tgoto error;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn;\n\n\tif (!cf_token_is(&ep->cfp, \"{\")) {\n\t\tif (!cf_go_to_token(&ep->cfp, \";\", NULL)) {\n\t\t\tcf_adderror_expecting(&ep->cfp, \";\");\n\t\t\treturn;\n\t\t}\n\n\t\tcf_adderror_expecting(&ep->cfp, \"{\");\n\t\tgoto error;\n\t}\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\tgoto error;\n\n\twhile (!cf_token_is(&ep->cfp, \"}\")) {\n\t\tstruct ep_pass pass;\n\t\tep_pass_init(&pass);\n\n\t\tswitch (ep_parse_pass(ep, &pass)) {\n\t\tcase PARSE_UNEXPECTED_CONTINUE:\n\t\t\tep_pass_free(&pass);\n\t\t\tif (!cf_go_to_token(&ep->cfp, \"}\", NULL))\n\t\t\t\tgoto error;\n\t\t\tcontinue;\n\t\tcase PARSE_EOF:\n\t\t\tep_pass_free(&pass);\n\t\t\tgoto error;\n\t\t}\n\n\t\tda_push_back(ept.passes, &pass);\n\n\t\tif (!cf_next_valid_token(&ep->cfp))\n\t\t\tgoto error;\n\t}\n\n\t/* pass the current token (which is '}') if we reached here */\n\tcf_next_token(&ep->cfp);\n\n\tda_push_back(ep->techniques, &ept);\n\treturn;\n\nerror:\n\tcf_next_token(&ep->cfp);\n\tep_technique_free(&ept);\n}\n\nstatic int ep_parse_sampler_state_item(struct effect_parser *ep, struct ep_sampler *eps)\n{\n\tint ret;\n\tchar *state = NULL;\n\tstruct dstr value = {0};\n\n\tret = cf_next_name(&ep->cfp, &state, \"state name\", \";\");\n\tif (ret != PARSE_SUCCESS)\n\t\tgoto fail;\n\n\tret = cf_next_token_should_be(&ep->cfp, \"=\", \";\", NULL);\n\tif (ret != PARSE_SUCCESS)\n\t\tgoto fail;\n\n\tfor (;;) {\n\t\tconst char *cur_str;\n\n\t\tif (!cf_next_valid_token(&ep->cfp))\n\t\t\treturn PARSE_EOF;\n\n\t\tcur_str = ep->cfp.cur_token->str.array;\n\t\tif (*cur_str == ';')\n\t\t\tbreak;\n\n\t\tdstr_ncat(&value, cur_str, ep->cfp.cur_token->str.len);\n\t}\n\n\tif (value.len) {\n\t\tda_push_back(eps->states, &state);\n\t\tda_push_back(eps->values, &value.array);\n\t}\n\n\treturn ret;\n\nfail:\n\tbfree(state);\n\tdstr_free(&value);\n\treturn ret;\n}\n\nstatic void ep_parse_sampler_state(struct effect_parser *ep)\n{\n\tstruct ep_sampler eps;\n\tstruct cf_token peek;\n\tep_sampler_init(&eps);\n\n\tif (cf_next_name(&ep->cfp, &eps.name, \"name\", \";\") != PARSE_SUCCESS)\n\t\tgoto error;\n\tif (cf_next_token_should_be(&ep->cfp, \"{\", \";\", NULL) != PARSE_SUCCESS)\n\t\tgoto error;\n\n\tif (!cf_peek_valid_token(&ep->cfp, &peek))\n\t\tgoto error;\n\n\twhile (strref_cmp(&peek.str, \"}\") != 0) {\n\t\tint ret = ep_parse_sampler_state_item(ep, &eps);\n\t\tif (ret == PARSE_EOF)\n\t\t\tgoto error;\n\n\t\tif (!cf_peek_valid_token(&ep->cfp, &peek))\n\t\t\tgoto error;\n\t}\n\n\tif (cf_next_token_should_be(&ep->cfp, \"}\", \";\", NULL) != PARSE_SUCCESS)\n\t\tgoto error;\n\tif (cf_next_token_should_be(&ep->cfp, \";\", NULL, NULL) != PARSE_SUCCESS)\n\t\tgoto error;\n\n\tda_push_back(ep->samplers, &eps);\n\treturn;\n\nerror:\n\tep_sampler_free(&eps);\n}\n\nstatic inline int ep_check_for_keyword(struct effect_parser *ep, const char *keyword, bool *val)\n{\n\tbool new_val = cf_token_is(&ep->cfp, keyword);\n\tif (new_val) {\n\t\tif (!cf_next_valid_token(&ep->cfp))\n\t\t\treturn PARSE_EOF;\n\n\t\tif (new_val && *val)\n\t\t\tcf_adderror(&ep->cfp, \"'$1' keyword already specified\", LEX_WARNING, keyword, NULL, NULL);\n\t\t*val = new_val;\n\n\t\treturn PARSE_CONTINUE;\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic inline int ep_parse_func_param(struct effect_parser *ep, struct ep_func *func, struct ep_var *var)\n{\n\tint code;\n\tbool var_type_keyword = false;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tcode = ep_check_for_keyword(ep, \"in\", &var_type_keyword);\n\tif (code == PARSE_EOF)\n\t\treturn PARSE_EOF;\n\telse if (var_type_keyword)\n\t\tvar->var_type = EP_VAR_IN;\n\n\tif (!var_type_keyword) {\n\t\tcode = ep_check_for_keyword(ep, \"inout\", &var_type_keyword);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn PARSE_EOF;\n\t\telse if (var_type_keyword)\n\t\t\tvar->var_type = EP_VAR_INOUT;\n\t}\n\n\tif (!var_type_keyword) {\n\t\tcode = ep_check_for_keyword(ep, \"out\", &var_type_keyword);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn PARSE_EOF;\n\t\telse if (var_type_keyword)\n\t\t\tvar->var_type = EP_VAR_OUT;\n\t}\n\n\tif (!var_type_keyword) {\n\t\tcode = ep_check_for_keyword(ep, \"uniform\", &var_type_keyword);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn PARSE_EOF;\n\t\telse if (var_type_keyword)\n\t\t\tvar->var_type = EP_VAR_UNIFORM;\n\t}\n\n\tcode = cf_get_name(&ep->cfp, &var->type, \"type\", \")\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tcode = cf_next_name(&ep->cfp, &var->name, \"name\", \")\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&ep->cfp, \":\")) {\n\t\tcode = cf_next_name(&ep->cfp, &var->mapping, \"mapping specifier\", \")\");\n\t\tif (code != PARSE_SUCCESS)\n\t\t\treturn code;\n\n\t\tif (!cf_next_valid_token(&ep->cfp))\n\t\t\treturn PARSE_EOF;\n\t}\n\n\tif (ep_getstruct(ep, var->type) != NULL)\n\t\tda_push_back(func->struct_deps, &var->type);\n\telse if (ep_getsampler(ep, var->type) != NULL)\n\t\tda_push_back(func->sampler_deps, &var->type);\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic bool ep_parse_func_params(struct effect_parser *ep, struct ep_func *func)\n{\n\tstruct cf_token peek;\n\tint code;\n\n\tcf_token_clear(&peek);\n\n\tif (!cf_peek_valid_token(&ep->cfp, &peek))\n\t\treturn false;\n\n\tif (*peek.str.array == ')') {\n\t\tcf_next_token(&ep->cfp);\n\t\tgoto exit;\n\t}\n\n\tdo {\n\t\tstruct ep_var var;\n\t\tep_var_init(&var);\n\n\t\tif (!cf_token_is(&ep->cfp, \"(\") && !cf_token_is(&ep->cfp, \",\"))\n\t\t\tcf_adderror_syntax_error(&ep->cfp);\n\n\t\tcode = ep_parse_func_param(ep, func, &var);\n\t\tif (code != PARSE_SUCCESS) {\n\t\t\tep_var_free(&var);\n\n\t\t\tif (code == PARSE_CONTINUE)\n\t\t\t\tgoto exit;\n\t\t\telse if (code == PARSE_EOF)\n\t\t\t\treturn false;\n\t\t}\n\n\t\tda_push_back(func->param_vars, &var);\n\t} while (!cf_token_is(&ep->cfp, \")\"));\n\nexit:\n\treturn true;\n}\n\nstatic inline bool ep_process_struct_dep(struct effect_parser *ep, struct ep_func *func)\n{\n\tstruct ep_struct *val = ep_getstruct_strref(ep, &ep->cfp.cur_token->str);\n\tif (val)\n\t\tda_push_back(func->struct_deps, &val->name);\n\treturn val != NULL;\n}\n\nstatic inline bool ep_process_func_dep(struct effect_parser *ep, struct ep_func *func)\n{\n\tstruct ep_func *val = ep_getfunc_strref(ep, &ep->cfp.cur_token->str);\n\tif (val)\n\t\tda_push_back(func->func_deps, &val->name);\n\treturn val != NULL;\n}\n\nstatic inline bool ep_process_sampler_dep(struct effect_parser *ep, struct ep_func *func)\n{\n\tstruct ep_sampler *val = ep_getsampler_strref(ep, &ep->cfp.cur_token->str);\n\tif (val)\n\t\tda_push_back(func->sampler_deps, &val->name);\n\treturn val != NULL;\n}\n\nstatic inline bool ep_process_param_dep(struct effect_parser *ep, struct ep_func *func)\n{\n\tstruct ep_param *val = ep_getparam_strref(ep, &ep->cfp.cur_token->str);\n\tif (val)\n\t\tda_push_back(func->param_deps, &val->name);\n\treturn val != NULL;\n}\n\nstatic inline bool ep_parse_func_contents(struct effect_parser *ep, struct ep_func *func)\n{\n\tint braces = 1;\n\n\tdstr_cat_strref(&func->contents, &ep->cfp.cur_token->str);\n\n\twhile (braces > 0) {\n\t\tif ((ep->cfp.cur_token++)->type == CFTOKEN_NONE)\n\t\t\treturn false;\n\n\t\tif (ep->cfp.cur_token->type == CFTOKEN_SPACETAB || ep->cfp.cur_token->type == CFTOKEN_NEWLINE) {\n\t\t} else if (cf_token_is(&ep->cfp, \"{\")) {\n\t\t\tbraces++;\n\t\t} else if (cf_token_is(&ep->cfp, \"}\")) {\n\t\t\tbraces--;\n\t\t} else if (ep_process_struct_dep(ep, func) || ep_process_func_dep(ep, func) ||\n\t\t\t   ep_process_sampler_dep(ep, func) || ep_process_param_dep(ep, func)) {\n\t\t}\n\n\t\tdstr_cat_strref(&func->contents, &ep->cfp.cur_token->str);\n\t}\n\n\treturn true;\n}\n\nstatic void ep_parse_function(struct effect_parser *ep, char *type, char *name)\n{\n\tstruct ep_func func;\n\tint code;\n\n\tep_func_init(&func, type, name);\n\tif (ep_getstruct(ep, type))\n\t\tda_push_back(func.struct_deps, &func.ret_type);\n\n\tif (!ep_parse_func_params(ep, &func))\n\t\tgoto error;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\tgoto error;\n\n\t/* if function is mapped to something, for example COLOR */\n\tif (cf_token_is(&ep->cfp, \":\")) {\n\t\tcode = cf_next_name(&ep->cfp, &func.mapping, \"mapping specifier\", \"{\");\n\t\tif (code == PARSE_EOF)\n\t\t\tgoto error;\n\t\telse if (code != PARSE_CONTINUE) {\n\t\t\tif (!cf_next_valid_token(&ep->cfp))\n\t\t\t\tgoto error;\n\t\t}\n\t}\n\n\tif (!cf_token_is(&ep->cfp, \"{\")) {\n\t\tcf_adderror_expecting(&ep->cfp, \"{\");\n\t\tgoto error;\n\t}\n\n\tif (!ep_parse_func_contents(ep, &func))\n\t\tgoto error;\n\n\t/* it is established that the current token is '}' if we reach this */\n\tcf_next_token(&ep->cfp);\n\n\tda_push_back(ep->funcs, &func);\n\treturn;\n\nerror:\n\tep_func_free(&func);\n}\n\n/* parses \"array[count]\" */\nstatic bool ep_parse_param_array(struct effect_parser *ep, struct ep_param *param)\n{\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn false;\n\n\tif (ep->cfp.cur_token->type != CFTOKEN_NUM ||\n\t    !valid_int_str(ep->cfp.cur_token->str.array, ep->cfp.cur_token->str.len))\n\t\treturn false;\n\n\tparam->array_count = (int)strtol(ep->cfp.cur_token->str.array, NULL, 10);\n\n\tif (cf_next_token_should_be(&ep->cfp, \"]\", \";\", NULL) == PARSE_EOF)\n\t\treturn false;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic inline int ep_parse_param_assign_texture(struct effect_parser *ep, struct ep_param *param)\n{\n\tint code;\n\tchar *str;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tcode = cf_token_is_type(&ep->cfp, CFTOKEN_STRING, \"texture path string\", \";\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tstr = cf_literal_to_str(ep->cfp.cur_token->str.array, ep->cfp.cur_token->str.len);\n\n\tif (str) {\n\t\tda_copy_array(param->default_val, str, strlen(str) + 1);\n\t\tbfree(str);\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic inline int ep_parse_param_assign_string(struct effect_parser *ep, struct ep_param *param)\n{\n\tint code;\n\tchar *str = NULL;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tcode = cf_token_is_type(&ep->cfp, CFTOKEN_STRING, \"string\", \";\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tstr = cf_literal_to_str(ep->cfp.cur_token->str.array, ep->cfp.cur_token->str.len);\n\n\tif (str) {\n\t\tda_copy_array(param->default_val, str, strlen(str) + 1);\n\t\tbfree(str);\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic inline int ep_parse_param_assign_intfloat(struct effect_parser *ep, struct ep_param *param, bool is_float)\n{\n\tint code;\n\tbool is_negative = false;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&ep->cfp, \"-\")) {\n\t\tis_negative = true;\n\n\t\tif (!cf_next_token(&ep->cfp))\n\t\t\treturn PARSE_EOF;\n\t}\n\n\tcode = cf_token_is_type(&ep->cfp, CFTOKEN_NUM, \"numeric value\", \";\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tif (is_float) {\n\t\tfloat f = (float)os_strtod(ep->cfp.cur_token->str.array);\n\t\tif (is_negative)\n\t\t\tf = -f;\n\t\tda_push_back_array(param->default_val, (uint8_t *)&f, sizeof(float));\n\t} else {\n\t\tlong l = strtol(ep->cfp.cur_token->str.array, NULL, 10);\n\t\tif (is_negative)\n\t\t\tl = -l;\n\t\tda_push_back_array(param->default_val, (uint8_t *)&l, sizeof(long));\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic inline int ep_parse_param_assign_bool(struct effect_parser *ep, struct ep_param *param)\n{\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&ep->cfp, \"true\")) {\n\t\tlong l = 1;\n\t\tda_push_back_array(param->default_val, (uint8_t *)&l, sizeof(long));\n\t\treturn PARSE_SUCCESS;\n\t} else if (cf_token_is(&ep->cfp, \"false\")) {\n\t\tlong l = 0;\n\t\tda_push_back_array(param->default_val, (uint8_t *)&l, sizeof(long));\n\t\treturn PARSE_SUCCESS;\n\t}\n\n\tcf_adderror_expecting(&ep->cfp, \"true or false\");\n\n\treturn PARSE_EOF;\n}\n\n/*\n * parses assignment for float1, float2, float3, float4, int1, int2, int3, int4,\n * and any combination for float3x3, float4x4, int3x3, int4x4, etc\n*/\nstatic inline int ep_parse_param_assign_intfloat_array(struct effect_parser *ep, struct ep_param *param, bool is_float)\n{\n\tconst char *intfloat_type = param->type + (is_float ? 5 : 3);\n\tint intfloat_count = 0, code, i;\n\n\t/* -------------------------------------------- */\n\n\tif (intfloat_type[0] < '1' || intfloat_type[0] > '4')\n\t\tcf_adderror(&ep->cfp, \"Invalid row count\", LEX_ERROR, NULL, NULL, NULL);\n\n\tintfloat_count = intfloat_type[0] - '0';\n\n\tif (intfloat_type[1] == 'x') {\n\t\tif (intfloat_type[2] < '1' || intfloat_type[2] > '4')\n\t\t\tcf_adderror(&ep->cfp, \"Invalid column count\", LEX_ERROR, NULL, NULL, NULL);\n\n\t\tintfloat_count *= intfloat_type[2] - '0';\n\t}\n\n\t/* -------------------------------------------- */\n\n\tcode = cf_next_token_should_be(&ep->cfp, \"{\", \";\", NULL);\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tfor (i = 0; i < intfloat_count; i++) {\n\t\tchar *next = ((i + 1) < intfloat_count) ? \",\" : \"}\";\n\n\t\tcode = ep_parse_param_assign_intfloat(ep, param, is_float);\n\t\tif (code != PARSE_SUCCESS)\n\t\t\treturn code;\n\n\t\tcode = cf_next_token_should_be(&ep->cfp, next, \";\", NULL);\n\t\tif (code != PARSE_SUCCESS)\n\t\t\treturn code;\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic int ep_parse_param_assignment_val(struct effect_parser *ep, struct ep_param *param)\n{\n\tif (param->is_texture)\n\t\treturn ep_parse_param_assign_texture(ep, param);\n\telse if (strcmp(param->type, \"int\") == 0)\n\t\treturn ep_parse_param_assign_intfloat(ep, param, false);\n\telse if (strcmp(param->type, \"float\") == 0)\n\t\treturn ep_parse_param_assign_intfloat(ep, param, true);\n\telse if (astrcmp_n(param->type, \"int\", 3) == 0)\n\t\treturn ep_parse_param_assign_intfloat_array(ep, param, false);\n\telse if (astrcmp_n(param->type, \"float\", 5) == 0)\n\t\treturn ep_parse_param_assign_intfloat_array(ep, param, true);\n\telse if (astrcmp_n(param->type, \"string\", 6) == 0)\n\t\treturn ep_parse_param_assign_string(ep, param);\n\telse if (strcmp(param->type, \"bool\") == 0)\n\t\treturn ep_parse_param_assign_bool(ep, param);\n\n\tcf_adderror(&ep->cfp, \"Invalid type '$1' used for assignment\", LEX_ERROR, param->type, NULL, NULL);\n\n\treturn PARSE_CONTINUE;\n}\n\nstatic inline bool ep_parse_param_assign(struct effect_parser *ep, struct ep_param *param)\n{\n\tif (ep_parse_param_assignment_val(ep, param) != PARSE_SUCCESS)\n\t\treturn false;\n\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\treturn false;\n\n\treturn true;\n}\n\n/* static bool ep_parse_param_property(struct effect_parser *ep,\n\t\tstruct ep_param *param)\n{\n} */\n\nstatic void ep_parse_param(struct effect_parser *ep, char *type, char *name, bool is_property, bool is_const,\n\t\t\t   bool is_uniform)\n{\n\tstruct ep_param param;\n\tep_param_init(&param, type, name, is_property, is_const, is_uniform);\n\n\tif (cf_token_is(&ep->cfp, \";\"))\n\t\tgoto complete;\n\tif (cf_token_is(&ep->cfp, \"[\") && !ep_parse_param_array(ep, &param))\n\t\tgoto error;\n\tif (cf_token_is(&ep->cfp, \"<\") && !ep_parse_param_annotations(ep, &param))\n\t\tgoto error;\n\tif (cf_token_is(&ep->cfp, \"=\") && !ep_parse_param_assign(ep, &param))\n\t\tgoto error;\n\t/*\n\tif (cf_token_is(&ep->cfp, \"<\") && !ep_parse_param_property(ep, &param))\n\t\tgoto error; */\n\tif (!cf_token_is(&ep->cfp, \";\"))\n\t\tgoto error;\n\ncomplete:\n\tda_push_back(ep->params, &param);\n\treturn;\n\nerror:\n\tep_param_free(&param);\n}\n\nstatic bool ep_get_var_specifiers(struct effect_parser *ep, bool *is_property, bool *is_const, bool *is_uniform)\n{\n\twhile (true) {\n\t\tint code;\n\t\tcode = ep_check_for_keyword(ep, \"property\", is_property);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn false;\n\t\telse if (code == PARSE_CONTINUE)\n\t\t\tcontinue;\n\n\t\tcode = ep_check_for_keyword(ep, \"const\", is_const);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn false;\n\t\telse if (code == PARSE_CONTINUE)\n\t\t\tcontinue;\n\n\t\tcode = ep_check_for_keyword(ep, \"uniform\", is_uniform);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn false;\n\t\telse if (code == PARSE_CONTINUE)\n\t\t\tcontinue;\n\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\nstatic inline void report_invalid_func_keyword(struct effect_parser *ep, const char *name, bool val)\n{\n\tif (val)\n\t\tcf_adderror(&ep->cfp,\n\t\t\t    \"'$1' keyword cannot be used with a \"\n\t\t\t    \"function\",\n\t\t\t    LEX_ERROR, name, NULL, NULL);\n}\n\nstatic void ep_parse_other(struct effect_parser *ep)\n{\n\tbool is_property = false, is_const = false, is_uniform = false;\n\tchar *type = NULL, *name = NULL;\n\n\tif (!ep_get_var_specifiers(ep, &is_property, &is_const, &is_uniform))\n\t\tgoto error;\n\n\tif (cf_get_name(&ep->cfp, &type, \"type\", \";\") != PARSE_SUCCESS)\n\t\tgoto error;\n\tif (cf_next_name(&ep->cfp, &name, \"name\", \";\") != PARSE_SUCCESS)\n\t\tgoto error;\n\tif (!cf_next_valid_token(&ep->cfp))\n\t\tgoto error;\n\n\tif (cf_token_is(&ep->cfp, \"(\")) {\n\t\treport_invalid_func_keyword(ep, \"property\", is_property);\n\t\treport_invalid_func_keyword(ep, \"const\", is_const);\n\t\treport_invalid_func_keyword(ep, \"uniform\", is_uniform);\n\n\t\tep_parse_function(ep, type, name);\n\t\treturn;\n\t} else {\n\t\tep_parse_param(ep, type, name, is_property, is_const, is_uniform);\n\t\treturn;\n\t}\n\nerror:\n\tbfree(type);\n\tbfree(name);\n}\n\nstatic bool ep_compile(struct effect_parser *ep);\n\nextern const char *gs_preprocessor_name(void);\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\nstatic void debug_get_default_value(struct gs_effect_param *param, char *buffer, unsigned long long buf_size)\n{\n\tif (param->default_val.num == 0) {\n\t\tsnprintf(buffer, buf_size, \"(null)\");\n\t\treturn;\n\t}\n\n\tswitch (param->type) {\n\tcase GS_SHADER_PARAM_STRING:\n\t\tsnprintf(buffer, buf_size, \"'%.*s'\", param->default_val.num, param->default_val.array);\n\t\tbreak;\n\tcase GS_SHADER_PARAM_INT:\n\t\tsnprintf(buffer, buf_size, \"%ld\", *(int *)(param->default_val.array + 0));\n\t\tbreak;\n\tcase GS_SHADER_PARAM_INT2:\n\t\tsnprintf(buffer, buf_size, \"%ld,%ld\", *(int *)(param->default_val.array + 0),\n\t\t\t *(int *)(param->default_val.array + 4));\n\t\tbreak;\n\tcase GS_SHADER_PARAM_INT3:\n\t\tsnprintf(buffer, buf_size, \"%ld,%ld,%ld\", *(int *)(param->default_val.array + 0),\n\t\t\t *(int *)(param->default_val.array + 4), *(int *)(param->default_val.array + 8));\n\t\tbreak;\n\tcase GS_SHADER_PARAM_INT4:\n\t\tsnprintf(buffer, buf_size, \"%ld,%ld,%ld,%ld\", *(int *)(param->default_val.array + 0),\n\t\t\t *(int *)(param->default_val.array + 4), *(int *)(param->default_val.array + 8),\n\t\t\t *(int *)(param->default_val.array + 12));\n\t\tbreak;\n\tcase GS_SHADER_PARAM_FLOAT:\n\t\tsnprintf(buffer, buf_size, \"%e\", *(float *)(param->default_val.array + 0));\n\t\tbreak;\n\tcase GS_SHADER_PARAM_VEC2:\n\t\tsnprintf(buffer, buf_size, \"%e,%e\", *(float *)(param->default_val.array + 0),\n\t\t\t *(float *)(param->default_val.array + 4));\n\t\tbreak;\n\tcase GS_SHADER_PARAM_VEC3:\n\t\tsnprintf(buffer, buf_size, \"%e,%e,%e\", *(float *)(param->default_val.array + 0),\n\t\t\t *(float *)(param->default_val.array + 4), *(float *)(param->default_val.array + 8));\n\t\tbreak;\n\tcase GS_SHADER_PARAM_VEC4:\n\t\tsnprintf(buffer, buf_size, \"%e,%e,%e,%e\", *(float *)(param->default_val.array + 0),\n\t\t\t *(float *)(param->default_val.array + 4), *(float *)(param->default_val.array + 8),\n\t\t\t *(float *)(param->default_val.array + 12));\n\t\tbreak;\n\tcase GS_SHADER_PARAM_MATRIX4X4:\n\t\tsnprintf(buffer, buf_size,\n\t\t\t \"[[%e,%e,%e,%e],[%e,%e,%e,%e],\"\n\t\t\t \"[%e,%e,%e,%e],[%e,%e,%e,%e]]\",\n\t\t\t *(float *)(param->default_val.array + 0), *(float *)(param->default_val.array + 4),\n\t\t\t *(float *)(param->default_val.array + 8), *(float *)(param->default_val.array + 12),\n\t\t\t *(float *)(param->default_val.array + 16), *(float *)(param->default_val.array + 20),\n\t\t\t *(float *)(param->default_val.array + 24), *(float *)(param->default_val.array + 28),\n\t\t\t *(float *)(param->default_val.array + 32), *(float *)(param->default_val.array + 36),\n\t\t\t *(float *)(param->default_val.array + 40), *(float *)(param->default_val.array + 44),\n\t\t\t *(float *)(param->default_val.array + 48), *(float *)(param->default_val.array + 52),\n\t\t\t *(float *)(param->default_val.array + 56), *(float *)(param->default_val.array + 60));\n\t\tbreak;\n\tcase GS_SHADER_PARAM_BOOL:\n\t\tsnprintf(buffer, buf_size, \"%s\", (*param->default_val.array) != 0 ? \"true\\0\" : \"false\\0\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_UNKNOWN:\n\tcase GS_SHADER_PARAM_TEXTURE:\n\t\tsnprintf(buffer, buf_size, \"<unknown>\");\n\t\tbreak;\n\t}\n}\n\nstatic void debug_param(struct gs_effect_param *param, struct ep_param *param_in, unsigned long long idx,\n\t\t\tconst char *offset)\n{\n\tchar _debug_type[4096];\n\tswitch (param->type) {\n\tcase GS_SHADER_PARAM_STRING:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"string\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_INT:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"int\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_INT2:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"int2\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_INT3:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"int3\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_INT4:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"int4\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_FLOAT:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"float\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_VEC2:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"float2\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_VEC3:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"float3\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_VEC4:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"float4\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_MATRIX4X4:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"float4x4\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_BOOL:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"bool\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_UNKNOWN:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"unknown\");\n\t\tbreak;\n\tcase GS_SHADER_PARAM_TEXTURE:\n\t\tsnprintf(_debug_type, sizeof(_debug_type), \"texture\");\n\t\tbreak;\n\t}\n\n\tchar _debug_buf[4096];\n\tdebug_get_default_value(param, _debug_buf, sizeof(_debug_buf));\n\tif (param->annotations.num > 0) {\n\t\tblog(LOG_DEBUG, \"%s[%4lld] %.*s '%s' with value %.*s and %lld annotations:\", offset, idx,\n\t\t     sizeof(_debug_type), _debug_type, param->name, sizeof(_debug_buf), _debug_buf,\n\t\t     param->annotations.num);\n\t} else {\n\t\tblog(LOG_DEBUG, \"%s[%4lld] %.*s '%s' with value %.*s.\", offset, idx, sizeof(_debug_type), _debug_type,\n\t\t     param->name, sizeof(_debug_buf), _debug_buf);\n\t}\n}\n\nstatic void debug_param_annotation(struct gs_effect_param *param, struct ep_param *param_in, unsigned long long idx,\n\t\t\t\t   const char *offset)\n{\n\tchar _debug_buf[4096];\n\tdebug_get_default_value(param, _debug_buf, sizeof(_debug_buf));\n\tblog(LOG_DEBUG, \"%s[%4lld] %s '%s' with value %.*s\", offset, idx, param_in->type, param->name,\n\t     sizeof(_debug_buf), _debug_buf);\n}\n\nstatic void debug_print_string(const char *offset, const char *str)\n{\n\t// Bypass 4096 limit in def_log_handler.\n\tchar const *begin = str;\n\tunsigned long long line = 1;\n\tfor (char const *here = begin; here[0] != '\\0'; here++) {\n\t\tchar const *str = begin;\n\t\tunsigned long long len = here - begin;\n\t\tbool is_line = false;\n\n\t\tif (here[0] == '\\r') {\n\t\t\tis_line = true;\n\t\t\tif (here[1] == '\\n') {\n\t\t\t\there += 1;\n\t\t\t}\n\t\t\tbegin = here + 1;\n\t\t} else if (here[0] == '\\n') {\n\t\t\tis_line = true;\n\t\t\tbegin = here + 1;\n\t\t}\n\n\t\tif (is_line) {\n\t\t\tblog(LOG_DEBUG, \"\\t\\t\\t\\t[%4lld] %.*s\", line, len, str);\n\t\t\tline++;\n\t\t}\n\t}\n\tif (begin[0] != '\\0') {\n\t\t// Final line was not written.\n\t\tblog(LOG_DEBUG, \"\\t\\t\\t\\t[%4lld] %*s\", line, strlen(begin), begin);\n\t}\n}\n#endif\n\nbool ep_parse(struct effect_parser *ep, gs_effect_t *effect, const char *effect_string, const char *file)\n{\n\tbool success;\n\n\tconst char *graphics_preprocessor = gs_preprocessor_name();\n\n\tif (graphics_preprocessor) {\n\t\tstruct cf_def def;\n\n\t\tcf_def_init(&def);\n\t\tdef.name.str.array = graphics_preprocessor;\n\t\tdef.name.str.len = strlen(graphics_preprocessor);\n\n\t\tstrref_copy(&def.name.unmerged_str, &def.name.str);\n\t\tcf_preprocessor_add_def(&ep->cfp.pp, &def);\n\t}\n\n\tep->effect = effect;\n\tif (!cf_parser_parse(&ep->cfp, effect_string, file))\n\t\treturn false;\n\n\twhile (ep->cfp.cur_token && ep->cfp.cur_token->type != CFTOKEN_NONE) {\n\t\tif (cf_token_is(&ep->cfp, \";\") || is_whitespace(*ep->cfp.cur_token->str.array)) {\n\t\t\t/* do nothing */\n\t\t\tep->cfp.cur_token++;\n\n\t\t} else if (cf_token_is(&ep->cfp, \"struct\")) {\n\t\t\tep_parse_struct(ep);\n\n\t\t} else if (cf_token_is(&ep->cfp, \"technique\")) {\n\t\t\tep_parse_technique(ep);\n\n\t\t} else if (cf_token_is(&ep->cfp, \"sampler_state\")) {\n\t\t\tep_parse_sampler_state(ep);\n\n\t\t} else if (cf_token_is(&ep->cfp, \"{\")) {\n\t\t\t/* add error and pass braces */\n\t\t\tcf_adderror(&ep->cfp, \"Unexpected code segment\", LEX_ERROR, NULL, NULL, NULL);\n\t\t\tcf_pass_pair(&ep->cfp, '{', '}');\n\n\t\t} else {\n\t\t\t/* parameters and functions */\n\t\t\tep_parse_other(ep);\n\t\t}\n\t}\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\n\tblog(LOG_DEBUG, \"================================================================================\");\n\tblog(LOG_DEBUG, \"Effect Parser reformatted shader '%s' to:\", file);\n\tdebug_print_string(\"\\t\", ep->cfp.lex.reformatted);\n#endif\n\n\tsuccess = !error_data_has_errors(&ep->cfp.error_list);\n\tif (success)\n\t\tsuccess = ep_compile(ep);\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\n\tblog(LOG_DEBUG, \"================================================================================\");\n#endif\n\n\treturn success;\n}\n\n/* ------------------------------------------------------------------------- */\n\nstatic inline void ep_write_param(struct dstr *shader, struct ep_param *param, dstr_array_t *used_params)\n{\n\tif (param->written)\n\t\treturn;\n\n\tif (param->is_const) {\n\t\tdstr_cat(shader, \"const \");\n\t} else if (param->is_uniform) {\n\t\tstruct dstr new;\n\t\tdstr_init_copy(&new, param->name);\n\t\tda_push_back(*used_params, &new);\n\n\t\tdstr_cat(shader, \"uniform \");\n\t}\n\n\tdstr_cat(shader, param->type);\n\tdstr_cat(shader, \" \");\n\tdstr_cat(shader, param->name);\n\n\tif (param->array_count)\n\t\tdstr_catf(shader, \"[%u]\", param->array_count);\n\n\tdstr_cat(shader, \";\\n\");\n\n\tparam->written = true;\n}\n\nstatic inline void ep_write_func_param_deps(struct effect_parser *ep, struct dstr *shader, struct ep_func *func,\n\t\t\t\t\t    dstr_array_t *used_params)\n{\n\tsize_t i;\n\tfor (i = 0; i < func->param_deps.num; i++) {\n\t\tconst char *name = func->param_deps.array[i];\n\t\tstruct ep_param *param = ep_getparam(ep, name);\n\t\tep_write_param(shader, param, used_params);\n\t}\n\n\tif (func->param_deps.num)\n\t\tdstr_cat(shader, \"\\n\\n\");\n}\n\nstatic void ep_write_sampler(struct dstr *shader, struct ep_sampler *sampler)\n{\n\tsize_t i;\n\n\tif (sampler->written)\n\t\treturn;\n\n\tdstr_cat(shader, \"sampler_state \");\n\tdstr_cat(shader, sampler->name);\n\tdstr_cat(shader, \" {\");\n\n\tfor (i = 0; i < sampler->values.num; i++) {\n\t\tdstr_cat(shader, \"\\n\\t\");\n\t\tdstr_cat(shader, sampler->states.array[i]);\n\t\tdstr_cat(shader, \" = \");\n\t\tdstr_cat(shader, sampler->values.array[i]);\n\t\tdstr_cat(shader, \";\\n\");\n\t}\n\n\tdstr_cat(shader, \"\\n};\\n\");\n\tsampler->written = true;\n}\n\nstatic inline void ep_write_func_sampler_deps(struct effect_parser *ep, struct dstr *shader, struct ep_func *func)\n{\n\tsize_t i;\n\tfor (i = 0; i < func->sampler_deps.num; i++) {\n\t\tconst char *name = func->sampler_deps.array[i];\n\n\t\tstruct ep_sampler *sampler = ep_getsampler(ep, name);\n\t\tep_write_sampler(shader, sampler);\n\t\tdstr_cat(shader, \"\\n\");\n\t}\n}\n\nstatic inline void ep_write_var(struct dstr *shader, struct ep_var *var)\n{\n\tif (var->var_type == EP_VAR_INOUT)\n\t\tdstr_cat(shader, \"inout \");\n\telse if (var->var_type == EP_VAR_OUT)\n\t\tdstr_cat(shader, \"out \");\n\telse if (var->var_type == EP_VAR_UNIFORM)\n\t\tdstr_cat(shader, \"uniform \");\n\t// The \"in\" input modifier is implied by default, so leave it blank\n\t// in that case.\n\n\tdstr_cat(shader, var->type);\n\tdstr_cat(shader, \" \");\n\tdstr_cat(shader, var->name);\n\n\tif (var->mapping) {\n\t\tdstr_cat(shader, \" : \");\n\t\tdstr_cat(shader, var->mapping);\n\t}\n}\n\nstatic void ep_write_struct(struct dstr *shader, struct ep_struct *st)\n{\n\tsize_t i;\n\n\tif (st->written)\n\t\treturn;\n\n\tdstr_cat(shader, \"struct \");\n\tdstr_cat(shader, st->name);\n\tdstr_cat(shader, \" {\");\n\n\tfor (i = 0; i < st->vars.num; i++) {\n\t\tdstr_cat(shader, \"\\n\\t\");\n\t\tep_write_var(shader, st->vars.array + i);\n\t\tdstr_cat(shader, \";\");\n\t}\n\n\tdstr_cat(shader, \"\\n};\\n\");\n\tst->written = true;\n}\n\nstatic inline void ep_write_func_struct_deps(struct effect_parser *ep, struct dstr *shader, struct ep_func *func)\n{\n\tsize_t i;\n\tfor (i = 0; i < func->struct_deps.num; i++) {\n\t\tconst char *name = func->struct_deps.array[i];\n\t\tstruct ep_struct *st = ep_getstruct(ep, name);\n\n\t\tif (!st->written) {\n\t\t\tep_write_struct(shader, st);\n\t\t\tdstr_cat(shader, \"\\n\");\n\t\t\tst->written = true;\n\t\t}\n\t}\n}\n\nstatic void ep_write_func(struct effect_parser *ep, struct dstr *shader, struct ep_func *func,\n\t\t\t  dstr_array_t *used_params);\n\nstatic inline void ep_write_func_func_deps(struct effect_parser *ep, struct dstr *shader, struct ep_func *func,\n\t\t\t\t\t   dstr_array_t *used_params)\n{\n\tsize_t i;\n\tfor (i = 0; i < func->func_deps.num; i++) {\n\t\tconst char *name = func->func_deps.array[i];\n\t\tstruct ep_func *func_dep = ep_getfunc(ep, name);\n\n\t\tif (!func_dep->written) {\n\t\t\tep_write_func(ep, shader, func_dep, used_params);\n\t\t\tdstr_cat(shader, \"\\n\\n\");\n\t\t}\n\t}\n}\n\nstatic void ep_write_func(struct effect_parser *ep, struct dstr *shader, struct ep_func *func,\n\t\t\t  dstr_array_t *used_params)\n{\n\tsize_t i;\n\n\tfunc->written = true;\n\n\tep_write_func_param_deps(ep, shader, func, used_params);\n\tep_write_func_sampler_deps(ep, shader, func);\n\tep_write_func_struct_deps(ep, shader, func);\n\tep_write_func_func_deps(ep, shader, func, used_params);\n\n\t/* ------------------------------------ */\n\n\tdstr_cat(shader, func->ret_type);\n\tdstr_cat(shader, \" \");\n\tdstr_cat(shader, func->name);\n\tdstr_cat(shader, \"(\");\n\n\tfor (i = 0; i < func->param_vars.num; i++) {\n\t\tstruct ep_var *var = func->param_vars.array + i;\n\n\t\tif (i)\n\t\t\tdstr_cat(shader, \", \");\n\t\tep_write_var(shader, var);\n\t}\n\n\tdstr_cat(shader, \")\\n\");\n\tdstr_cat_dstr(shader, &func->contents);\n\tdstr_cat(shader, \"\\n\");\n}\n\n/* writes mapped vars used by the call as parameters for main */\nstatic void ep_write_main_params(struct effect_parser *ep, struct dstr *shader, struct dstr *param_str,\n\t\t\t\t struct ep_func *func)\n{\n\tsize_t i;\n\tbool empty_params = dstr_is_empty(param_str);\n\n\tfor (i = 0; i < func->param_vars.num; i++) {\n\t\tstruct ep_var *var = func->param_vars.array + i;\n\t\tstruct ep_struct *st = NULL;\n\t\tbool mapped = (var->mapping != NULL);\n\n\t\tif (!mapped) {\n\t\t\tst = ep_getstruct(ep, var->type);\n\t\t\tif (st)\n\t\t\t\tmapped = ep_struct_mapped(st);\n\t\t}\n\n\t\tif (mapped) {\n\t\t\tdstr_cat(shader, var->type);\n\t\t\tdstr_cat(shader, \" \");\n\t\t\tdstr_cat(shader, var->name);\n\n\t\t\tif (!st) {\n\t\t\t\tdstr_cat(shader, \" : \");\n\t\t\t\tdstr_cat(shader, var->mapping);\n\t\t\t}\n\n\t\t\tif (!dstr_is_empty(param_str))\n\t\t\t\tdstr_cat(param_str, \", \");\n\t\t\tdstr_cat(param_str, var->name);\n\t\t}\n\t}\n\n\tif (!empty_params)\n\t\tdstr_cat(param_str, \", \");\n}\n\nstatic void ep_write_main(struct effect_parser *ep, struct dstr *shader, struct ep_func *func, struct dstr *call_str)\n{\n\tstruct dstr param_str;\n\tstruct dstr adjusted_call;\n\n\tdstr_init(&param_str);\n\tdstr_init_copy_dstr(&adjusted_call, call_str);\n\n\tdstr_cat(shader, \"\\n\");\n\tdstr_cat(shader, func->ret_type);\n\tdstr_cat(shader, \" main(\");\n\n\tep_write_main_params(ep, shader, &param_str, func);\n\n\tdstr_cat(shader, \")\");\n\tif (func->mapping) {\n\t\tdstr_cat(shader, \" : \");\n\t\tdstr_cat(shader, func->mapping);\n\t}\n\n\tdstr_cat(shader, \"\\n{\\n\\treturn \");\n\tdstr_cat_dstr(shader, &adjusted_call);\n\tdstr_cat(shader, \"\\n}\\n\");\n\n\tdstr_free(&adjusted_call);\n\tdstr_free(&param_str);\n}\n\nstatic inline void ep_reset_written(struct effect_parser *ep)\n{\n\tsize_t i;\n\tfor (i = 0; i < ep->params.num; i++)\n\t\tep->params.array[i].written = false;\n\tfor (i = 0; i < ep->structs.num; i++)\n\t\tep->structs.array[i].written = false;\n\tfor (i = 0; i < ep->funcs.num; i++)\n\t\tep->funcs.array[i].written = false;\n\tfor (i = 0; i < ep->samplers.num; i++)\n\t\tep->samplers.array[i].written = false;\n}\n\nstatic void ep_makeshaderstring(struct effect_parser *ep, struct dstr *shader, cf_token_array_t *shader_call,\n\t\t\t\tdstr_array_t *used_params)\n{\n\tstruct cf_token *token = shader_call->array;\n\tstruct cf_token *func_name;\n\tstruct ep_func *func;\n\tstruct dstr call_str;\n\n\tdstr_init(&call_str);\n\n\tif (!token)\n\t\treturn;\n\n\twhile (token->type != CFTOKEN_NONE && is_whitespace(*token->str.array))\n\t\ttoken++;\n\n\tif (token->type == CFTOKEN_NONE || strref_cmp(&token->str, \"NULL\") == 0)\n\t\treturn;\n\n\tfunc_name = token;\n\n\twhile (token->type != CFTOKEN_NONE) {\n\t\tstruct ep_param *param = ep_getparam_strref(ep, &token->str);\n\t\tif (param)\n\t\t\tep_write_param(shader, param, used_params);\n\n\t\tdstr_cat_strref(&call_str, &token->str);\n\t\ttoken++;\n\t}\n\n\tfunc = ep_getfunc_strref(ep, &func_name->str);\n\tif (!func)\n\t\treturn;\n\n\tep_write_func(ep, shader, func, used_params);\n\tep_write_main(ep, shader, func, &call_str);\n\n\tdstr_free(&call_str);\n\n\tep_reset_written(ep);\n}\n\nstatic void ep_compile_annotations(ep_param_array_t *ep_annotations, gs_effect_param_array_t *gsp_annotations,\n\t\t\t\t   struct effect_parser *ep)\n{\n\tda_resize(*gsp_annotations, ep_annotations->num);\n\n\tsize_t i;\n\tfor (i = 0; i < ep_annotations->num; i++) {\n\t\tstruct gs_effect_param *param = gsp_annotations->array + i;\n\t\tstruct ep_param *param_in = ep_annotations->array + i;\n\n\t\tparam->name = bstrdup(param_in->name);\n\t\tparam->section = EFFECT_ANNOTATION;\n\t\tparam->effect = ep->effect;\n\t\tda_move(param->default_val, param_in->default_val);\n\n\t\tparam->type = get_effect_param_type(param_in->type);\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\n\t\tdebug_param(param, param_in, i, \"\\t\\t\");\n#endif\n\t}\n}\n\nstatic void ep_compile_param_annotations(struct ep_param *ep_param_input, struct gs_effect_param *gs_effect_input,\n\t\t\t\t\t struct effect_parser *ep)\n{\n\tep_compile_annotations(&(ep_param_input->annotations), &(gs_effect_input->annotations), ep);\n}\n\nstatic void ep_compile_param(struct effect_parser *ep, size_t idx)\n{\n\tstruct gs_effect_param *param;\n\tstruct ep_param *param_in;\n\n\tparam = ep->effect->params.array + idx;\n\tparam_in = ep->params.array + idx;\n\tparam_in->param = param;\n\n\tparam->name = bstrdup(param_in->name);\n\tparam->section = EFFECT_PARAM;\n\tparam->effect = ep->effect;\n\tda_move(param->default_val, param_in->default_val);\n\n\tparam->type = get_effect_param_type(param_in->type);\n\n\tif (strcmp(param_in->name, \"ViewProj\") == 0)\n\t\tep->effect->view_proj = param;\n\telse if (strcmp(param_in->name, \"World\") == 0)\n\t\tep->effect->world = param;\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\n\tdebug_param(param, param_in, idx, \"\\t\");\n#endif\n\n\tep_compile_param_annotations(param_in, param, ep);\n}\n\nstatic bool ep_compile_pass_shaderparams(struct effect_parser *ep, pass_shaderparam_array_t *pass_params,\n\t\t\t\t\t dstr_array_t *used_params, gs_shader_t *shader)\n{\n\tsize_t i;\n\tda_resize(*pass_params, used_params->num);\n\n\tfor (i = 0; i < pass_params->num; i++) {\n\t\tstruct dstr *param_name = used_params->array + i;\n\t\tstruct pass_shaderparam *param = pass_params->array + i;\n\n\t\tparam->eparam = gs_effect_get_param_by_name(ep->effect, param_name->array);\n\t\tparam->sparam = gs_shader_get_param_by_name(shader, param_name->array);\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\n\t\tdebug_param(param->eparam, 0, i, \"\\t\\t\\t\\t\");\n#endif\n\n\t\tif (!param->sparam) {\n\t\t\tblog(LOG_ERROR, \"Effect shader parameter not found\");\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic inline bool ep_compile_pass_shader(struct effect_parser *ep, struct gs_effect_technique *tech,\n\t\t\t\t\t  struct gs_effect_pass *pass, struct ep_pass *pass_in, size_t pass_idx,\n\t\t\t\t\t  enum gs_shader_type type)\n{\n\tstruct dstr shader_str;\n\tstruct dstr location;\n\tdstr_array_t used_params;\n\tpass_shaderparam_array_t *pass_params = NULL;\n\tgs_shader_t *shader = NULL;\n\tbool success = true;\n\tchar *errors = NULL;\n\n\tdstr_init(&shader_str);\n\tda_init(used_params);\n\tdstr_init(&location);\n\n\tdstr_copy(&location, ep->cfp.lex.file);\n\tif (type == GS_SHADER_VERTEX)\n\t\tdstr_cat(&location, \" (Vertex \");\n\telse if (type == GS_SHADER_PIXEL)\n\t\tdstr_cat(&location, \" (Pixel \");\n\t/*else if (type == SHADER_GEOMETRY)\n\t\tdstr_cat(&location, \" (Geometry \");*/\n\n\tassert(pass_idx <= UINT_MAX);\n\tdstr_catf(&location, \"shader, technique %s, pass %u)\", tech->name, (unsigned)pass_idx);\n\n\tif (type == GS_SHADER_VERTEX) {\n\t\tep_makeshaderstring(ep, &shader_str, &pass_in->vertex_program, &used_params);\n\n\t\tpass->vertshader = gs_vertexshader_create(shader_str.array, location.array, &errors);\n\n\t\tshader = pass->vertshader;\n\t\tpass_params = &pass->vertshader_params;\n\t} else if (type == GS_SHADER_PIXEL) {\n\t\tep_makeshaderstring(ep, &shader_str, &pass_in->fragment_program, &used_params);\n\n\t\tpass->pixelshader = gs_pixelshader_create(shader_str.array, location.array, &errors);\n\n\t\tshader = pass->pixelshader;\n\t\tpass_params = &pass->pixelshader_params;\n\t}\n\n\tif (errors && strlen(errors)) {\n\t\tcf_adderror(&ep->cfp, \"Error creating shader: $1\", LEX_ERROR, errors, NULL, NULL);\n\t}\n\tbfree(errors);\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\n\tblog(LOG_DEBUG, \"\\t\\t\\t%s Shader:\", type == GS_SHADER_VERTEX ? \"Vertex\" : \"Fragment\");\n\tblog(LOG_DEBUG, \"\\t\\t\\tCode:\");\n\tdebug_print_string(\"\\t\\t\\t\\t\\t\", shader_str.array);\n\tblog(LOG_DEBUG, \"\\t\\t\\tParameters:\");\n#endif\n\n\tif (shader)\n\t\tsuccess = ep_compile_pass_shaderparams(ep, pass_params, &used_params, shader);\n\telse\n\t\tsuccess = false;\n\n\tdstr_free(&location);\n\tdstr_array_free(used_params.array, used_params.num);\n\tda_free(used_params);\n\tdstr_free(&shader_str);\n\n\treturn success;\n}\n\nstatic bool ep_compile_pass(struct effect_parser *ep, struct gs_effect_technique *tech, struct ep_technique *tech_in,\n\t\t\t    size_t idx)\n{\n\tstruct gs_effect_pass *pass;\n\tstruct ep_pass *pass_in;\n\tbool success = true;\n\n\tpass = tech->passes.array + idx;\n\tpass_in = tech_in->passes.array + idx;\n\n\tpass->name = bstrdup(pass_in->name);\n\tpass->section = EFFECT_PASS;\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\n\tblog(LOG_DEBUG, \"\\t\\t[%4lld] Pass '%s':\", idx, pass->name);\n#endif\n\n\tif (!ep_compile_pass_shader(ep, tech, pass, pass_in, idx, GS_SHADER_VERTEX)) {\n\t\tsuccess = false;\n\t\tblog(LOG_ERROR, \"Pass (%zu) <%s> missing vertex shader!\", idx, pass->name ? pass->name : \"\");\n\t}\n\tif (!ep_compile_pass_shader(ep, tech, pass, pass_in, idx, GS_SHADER_PIXEL)) {\n\t\tsuccess = false;\n\t\tblog(LOG_ERROR, \"Pass (%zu) <%s> missing pixel shader!\", idx, pass->name ? pass->name : \"\");\n\t}\n\treturn success;\n}\n\nstatic inline bool ep_compile_technique(struct effect_parser *ep, size_t idx)\n{\n\tstruct gs_effect_technique *tech;\n\tstruct ep_technique *tech_in;\n\tbool success = true;\n\tsize_t i;\n\n\ttech = ep->effect->techniques.array + idx;\n\ttech_in = ep->techniques.array + idx;\n\n\ttech->name = bstrdup(tech_in->name);\n\ttech->section = EFFECT_TECHNIQUE;\n\ttech->effect = ep->effect;\n\n\tda_resize(tech->passes, tech_in->passes.num);\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\n\tblog(LOG_DEBUG, \"\\t[%4lld] Technique '%s' has %lld passes:\", idx, tech->name, tech->passes.num);\n#endif\n\n\tfor (i = 0; i < tech->passes.num; i++) {\n\t\tif (!ep_compile_pass(ep, tech, tech_in, i))\n\t\t\tsuccess = false;\n\t}\n\n\treturn success;\n}\n\nstatic bool ep_compile(struct effect_parser *ep)\n{\n\tbool success = true;\n\tsize_t i;\n\n\tassert(ep->effect);\n\n\tda_resize(ep->effect->params, ep->params.num);\n\tda_resize(ep->effect->techniques, ep->techniques.num);\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\n\tblog(LOG_DEBUG, \"Shader has %lld parameters:\", ep->params.num);\n#endif\n\n\tfor (i = 0; i < ep->params.num; i++)\n\t\tep_compile_param(ep, i);\n\n#if defined(_DEBUG) && defined(_DEBUG_SHADERS)\n\tblog(LOG_DEBUG, \"Shader has %lld techniques:\", ep->techniques.num);\n#endif\n\n\tfor (i = 0; i < ep->techniques.num; i++) {\n\t\tif (!ep_compile_technique(ep, i))\n\t\t\tsuccess = false;\n\t}\n\n\treturn success;\n}\n"
  },
  {
    "path": "libobs/graphics/effect-parser.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/darray.h\"\n#include \"../util/cf-parser.h\"\n#include \"graphics.h\"\n#include \"shader-parser.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct dstr;\n\ntypedef DARRAY(struct ep_param) ep_param_array_t;\ntypedef DARRAY(struct ep_var) ep_var_array_t;\n\n/*\n * The effect parser takes an effect file and converts it into individual\n * shaders for each technique's pass.  It automatically writes all dependent\n * structures/functions/parameters to the shader and builds shader text for\n * each shader component of each pass.\n */\n\n/* ------------------------------------------------------------------------- */\n/* effect parser var data */\n\nenum ep_var_type { EP_VAR_NONE, EP_VAR_IN = EP_VAR_NONE, EP_VAR_INOUT, EP_VAR_OUT, EP_VAR_UNIFORM };\n\nstruct ep_var {\n\tchar *type, *name, *mapping;\n\tenum ep_var_type var_type;\n};\n\nstatic inline void ep_var_init(struct ep_var *epv)\n{\n\tmemset(epv, 0, sizeof(struct ep_var));\n}\n\nstatic inline void ep_var_free(struct ep_var *epv)\n{\n\tbfree(epv->type);\n\tbfree(epv->name);\n\tbfree(epv->mapping);\n}\n\n/* ------------------------------------------------------------------------- */\n/* effect parser param data */\n\nstruct ep_param {\n\tchar *type, *name;\n\tDARRAY(uint8_t) default_val;\n\tDARRAY(char *) properties;\n\tstruct gs_effect_param *param;\n\tbool is_const, is_property, is_uniform, is_texture, written;\n\tint writeorder, array_count;\n\tep_param_array_t annotations;\n};\n\nstatic inline void ep_param_init(struct ep_param *epp, char *type, char *name, bool is_property, bool is_const,\n\t\t\t\t bool is_uniform)\n{\n\tepp->type = type;\n\tepp->name = name;\n\tepp->is_property = is_property;\n\tepp->is_const = is_const;\n\tepp->is_uniform = is_uniform;\n\tepp->is_texture = (astrcmp_n(epp->type, \"texture\", 7) == 0);\n\tepp->written = false;\n\tepp->writeorder = false;\n\tepp->array_count = 0;\n\tda_init(epp->default_val);\n\tda_init(epp->properties);\n\tda_init(epp->annotations);\n}\n\nstatic inline void ep_param_free(struct ep_param *epp)\n{\n\tbfree(epp->type);\n\tbfree(epp->name);\n\tda_free(epp->default_val);\n\tda_free(epp->properties);\n\n\tfor (size_t i = 0; i < epp->annotations.num; i++)\n\t\tep_param_free(epp->annotations.array + i);\n\tda_free(epp->annotations);\n}\n\n/* ------------------------------------------------------------------------- */\n/* effect parser struct data */\n\nstruct ep_struct {\n\tchar *name;\n\tep_var_array_t vars; /* struct ep_var */\n\tbool written;\n};\n\nstatic inline bool ep_struct_mapped(struct ep_struct *eps)\n{\n\tif (eps->vars.num > 0)\n\t\treturn eps->vars.array[0].mapping != NULL;\n\n\treturn false;\n}\n\nstatic inline void ep_struct_init(struct ep_struct *eps)\n{\n\tmemset(eps, 0, sizeof(struct ep_struct));\n}\n\nstatic inline void ep_struct_free(struct ep_struct *eps)\n{\n\tsize_t i;\n\n\tbfree(eps->name);\n\tfor (i = 0; i < eps->vars.num; i++)\n\t\tep_var_free(eps->vars.array + i);\n\tda_free(eps->vars);\n}\n\n/* ------------------------------------------------------------------------- */\n/* effect parser sampler data */\n\nstruct ep_sampler {\n\tchar *name;\n\tDARRAY(char *) states;\n\tDARRAY(char *) values;\n\n\tbool written;\n};\n\nstatic inline void ep_sampler_init(struct ep_sampler *eps)\n{\n\tmemset(eps, 0, sizeof(struct ep_sampler));\n}\n\nstatic inline void ep_sampler_free(struct ep_sampler *eps)\n{\n\tsize_t i;\n\n\tfor (i = 0; i < eps->states.num; i++)\n\t\tbfree(eps->states.array[i]);\n\tfor (i = 0; i < eps->values.num; i++)\n\t\tbfree(eps->values.array[i]);\n\n\tbfree(eps->name);\n\tda_free(eps->states);\n\tda_free(eps->values);\n}\n\n/* ------------------------------------------------------------------------- */\n/* effect parser pass data */\n\nstruct ep_pass {\n\tchar *name;\n\tcf_token_array_t vertex_program;\n\tcf_token_array_t fragment_program;\n\tstruct gs_effect_pass *pass;\n};\n\nstatic inline void ep_pass_init(struct ep_pass *epp)\n{\n\tmemset(epp, 0, sizeof(struct ep_pass));\n}\n\nstatic inline void ep_pass_free(struct ep_pass *epp)\n{\n\tbfree(epp->name);\n\tda_free(epp->vertex_program);\n\tda_free(epp->fragment_program);\n}\n\n/* ------------------------------------------------------------------------- */\n/* effect parser technique data */\n\nstruct ep_technique {\n\tchar *name;\n\tDARRAY(struct ep_pass) passes; /* struct ep_pass */\n};\n\nstatic inline void ep_technique_init(struct ep_technique *ept)\n{\n\tmemset(ept, 0, sizeof(struct ep_technique));\n}\n\nstatic inline void ep_technique_free(struct ep_technique *ept)\n{\n\tsize_t i;\n\n\tfor (i = 0; i < ept->passes.num; i++)\n\t\tep_pass_free(ept->passes.array + i);\n\n\tbfree(ept->name);\n\tda_free(ept->passes);\n}\n\n/* ------------------------------------------------------------------------- */\n/* effect parser function data */\n\nstruct ep_func {\n\tchar *name, *ret_type, *mapping;\n\tstruct dstr contents;\n\tep_var_array_t param_vars;\n\tDARRAY(char *) func_deps;\n\tDARRAY(char *) struct_deps;\n\tDARRAY(char *) param_deps;\n\tDARRAY(char *) sampler_deps;\n\tbool written;\n};\n\nstatic inline void ep_func_init(struct ep_func *epf, char *ret_type, char *name)\n{\n\tmemset(epf, 0, sizeof(struct ep_func));\n\tepf->name = name;\n\tepf->ret_type = ret_type;\n}\n\nstatic inline void ep_func_free(struct ep_func *epf)\n{\n\tsize_t i;\n\tfor (i = 0; i < epf->param_vars.num; i++)\n\t\tep_var_free(epf->param_vars.array + i);\n\n\tbfree(epf->name);\n\tbfree(epf->ret_type);\n\tbfree(epf->mapping);\n\tdstr_free(&epf->contents);\n\tda_free(epf->param_vars);\n\tda_free(epf->func_deps);\n\tda_free(epf->struct_deps);\n\tda_free(epf->param_deps);\n\tda_free(epf->sampler_deps);\n}\n\n/* ------------------------------------------------------------------------- */\n\nstruct effect_parser {\n\tgs_effect_t *effect;\n\n\tep_param_array_t params;\n\tDARRAY(struct ep_struct) structs;\n\tDARRAY(struct ep_func) funcs;\n\tDARRAY(struct ep_sampler) samplers;\n\tDARRAY(struct ep_technique) techniques;\n\n\t/* internal vars */\n\tDARRAY(struct cf_lexer) files;\n\tcf_token_array_t tokens;\n\tstruct gs_effect_pass *cur_pass;\n\n\tstruct cf_parser cfp;\n};\n\nstatic inline void ep_init(struct effect_parser *ep)\n{\n\tda_init(ep->params);\n\tda_init(ep->structs);\n\tda_init(ep->funcs);\n\tda_init(ep->samplers);\n\tda_init(ep->techniques);\n\tda_init(ep->files);\n\tda_init(ep->tokens);\n\n\tep->cur_pass = NULL;\n\tcf_parser_init(&ep->cfp);\n}\n\nextern void ep_free(struct effect_parser *ep);\n\nextern bool ep_parse(struct effect_parser *ep, gs_effect_t *effect, const char *effect_string, const char *file);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/effect.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"effect.h\"\n#include \"graphics-internal.h\"\n#include \"vec2.h\"\n#include \"vec3.h\"\n#include \"vec4.h\"\n\nvoid gs_effect_actually_destroy(gs_effect_t *effect)\n{\n\teffect_free(effect);\n\tbfree(effect);\n}\n\nvoid gs_effect_destroy(gs_effect_t *effect)\n{\n\tif (effect) {\n\t\tif (!effect->cached)\n\t\t\tgs_effect_actually_destroy(effect);\n\t}\n}\n\ngs_technique_t *gs_effect_get_technique(const gs_effect_t *effect, const char *name)\n{\n\tif (!effect)\n\t\treturn NULL;\n\n\tfor (size_t i = 0; i < effect->techniques.num; i++) {\n\t\tstruct gs_effect_technique *tech = effect->techniques.array + i;\n\t\tif (strcmp(tech->name, name) == 0)\n\t\t\treturn tech;\n\t}\n\n\treturn NULL;\n}\n\ngs_technique_t *gs_effect_get_current_technique(const gs_effect_t *effect)\n{\n\tif (!effect)\n\t\treturn NULL;\n\n\treturn effect->cur_technique;\n}\n\nbool gs_effect_loop(gs_effect_t *effect, const char *name)\n{\n\tif (!effect) {\n\t\treturn false;\n\t}\n\n\tif (!effect->looping) {\n\t\tgs_technique_t *tech;\n\n\t\tif (!!gs_get_effect()) {\n\t\t\tblog(LOG_WARNING, \"gs_effect_loop: An effect is \"\n\t\t\t\t\t  \"already active\");\n\t\t\treturn false;\n\t\t}\n\n\t\ttech = gs_effect_get_technique(effect, name);\n\t\tif (!tech) {\n\t\t\tblog(LOG_WARNING,\n\t\t\t     \"gs_effect_loop: Technique '%s' \"\n\t\t\t     \"not found.\",\n\t\t\t     name);\n\t\t\treturn false;\n\t\t}\n\n\t\tgs_technique_begin(tech);\n\n\t\teffect->looping = true;\n\t} else {\n\t\tgs_technique_end_pass(effect->cur_technique);\n\t}\n\n\tif (!gs_technique_begin_pass(effect->cur_technique, effect->loop_pass++)) {\n\t\tgs_technique_end(effect->cur_technique);\n\t\teffect->looping = false;\n\t\teffect->loop_pass = 0;\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nsize_t gs_technique_begin(gs_technique_t *tech)\n{\n\tif (!tech)\n\t\treturn 0;\n\n\ttech->effect->cur_technique = tech;\n\ttech->effect->graphics->cur_effect = tech->effect;\n\n\treturn tech->passes.num;\n}\n\nvoid gs_technique_end(gs_technique_t *tech)\n{\n\tif (!tech)\n\t\treturn;\n\n\tstruct gs_effect *effect = tech->effect;\n\tstruct gs_effect_param *params = effect->params.array;\n\tsize_t i;\n\n\tgs_load_vertexshader(NULL);\n\tgs_load_pixelshader(NULL);\n\n\ttech->effect->cur_technique = NULL;\n\ttech->effect->graphics->cur_effect = NULL;\n\n\tfor (i = 0; i < effect->params.num; i++) {\n\t\tstruct gs_effect_param *param = params + i;\n\n\t\tda_resize(param->cur_val, 0);\n\t\tparam->changed = false;\n\t\tif (param->next_sampler)\n\t\t\tparam->next_sampler = NULL;\n\t}\n}\n\nstatic inline void reset_params(pass_shaderparam_array_t *shaderparams)\n{\n\tstruct pass_shaderparam *params = shaderparams->array;\n\tsize_t i;\n\n\tfor (i = 0; i < shaderparams->num; i++)\n\t\tparams[i].eparam->changed = false;\n}\n\nstatic void upload_shader_params(pass_shaderparam_array_t *pass_params, bool changed_only)\n{\n\tstruct pass_shaderparam *params = pass_params->array;\n\tsize_t i;\n\n\tfor (i = 0; i < pass_params->num; i++) {\n\t\tstruct pass_shaderparam *param = params + i;\n\t\tstruct gs_effect_param *eparam = param->eparam;\n\t\tgs_sparam_t *sparam = param->sparam;\n\n\t\tif (eparam->next_sampler)\n\t\t\tgs_shader_set_next_sampler(sparam, eparam->next_sampler);\n\n\t\tif (changed_only && !eparam->changed)\n\t\t\tcontinue;\n\n\t\tif (!eparam->cur_val.num) {\n\t\t\tif (eparam->default_val.num)\n\t\t\t\tda_copy(eparam->cur_val, eparam->default_val);\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tgs_shader_set_val(sparam, eparam->cur_val.array, eparam->cur_val.num);\n\t}\n}\n\nstatic inline void upload_parameters(struct gs_effect *effect, bool changed_only)\n{\n\tpass_shaderparam_array_t *vshader_params, *pshader_params;\n\n\tif (!effect->cur_pass)\n\t\treturn;\n\n\tvshader_params = &effect->cur_pass->vertshader_params;\n\tpshader_params = &effect->cur_pass->pixelshader_params;\n\n\tupload_shader_params(vshader_params, changed_only);\n\tupload_shader_params(pshader_params, changed_only);\n\treset_params(vshader_params);\n\treset_params(pshader_params);\n}\n\nvoid gs_effect_update_params(gs_effect_t *effect)\n{\n\tif (effect)\n\t\tupload_parameters(effect, true);\n}\n\nbool gs_technique_begin_pass(gs_technique_t *tech, size_t idx)\n{\n\tstruct gs_effect_pass *passes;\n\tstruct gs_effect_pass *cur_pass;\n\n\tif (!tech || idx >= tech->passes.num)\n\t\treturn false;\n\n\tpasses = tech->passes.array;\n\tcur_pass = passes + idx;\n\n\ttech->effect->cur_pass = cur_pass;\n\tgs_load_vertexshader(cur_pass->vertshader);\n\tgs_load_pixelshader(cur_pass->pixelshader);\n\tupload_parameters(tech->effect, false);\n\n\treturn true;\n}\n\nbool gs_technique_begin_pass_by_name(gs_technique_t *tech, const char *name)\n{\n\tif (!tech)\n\t\treturn false;\n\n\tfor (size_t i = 0; i < tech->passes.num; i++) {\n\t\tstruct gs_effect_pass *pass = tech->passes.array + i;\n\t\tif (strcmp(pass->name, name) == 0) {\n\t\t\tgs_technique_begin_pass(tech, i);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic inline void clear_tex_params(pass_shaderparam_array_t *in_params)\n{\n\tstruct pass_shaderparam *params = in_params->array;\n\n\tfor (size_t i = 0; i < in_params->num; i++) {\n\t\tstruct pass_shaderparam *param = params + i;\n\t\tstruct gs_shader_param_info info;\n\n\t\tgs_shader_get_param_info(param->sparam, &info);\n\t\tif (info.type == GS_SHADER_PARAM_TEXTURE)\n\t\t\tgs_shader_set_texture(param->sparam, NULL);\n\t}\n}\n\nvoid gs_technique_end_pass(gs_technique_t *tech)\n{\n\tif (!tech)\n\t\treturn;\n\n\tstruct gs_effect_pass *pass = tech->effect->cur_pass;\n\tif (!pass)\n\t\treturn;\n\n\tclear_tex_params(&pass->vertshader_params);\n\tclear_tex_params(&pass->pixelshader_params);\n\ttech->effect->cur_pass = NULL;\n}\n\nsize_t gs_effect_get_num_params(const gs_effect_t *effect)\n{\n\treturn effect ? effect->params.num : 0;\n}\n\ngs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect, size_t param)\n{\n\tif (!effect)\n\t\treturn NULL;\n\n\tstruct gs_effect_param *params = effect->params.array;\n\tif (param >= effect->params.num)\n\t\treturn NULL;\n\n\treturn params + param;\n}\n\ngs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect, const char *name)\n{\n\tif (!effect)\n\t\treturn NULL;\n\n\tstruct gs_effect_param *params = effect->params.array;\n\n\tfor (size_t i = 0; i < effect->params.num; i++) {\n\t\tstruct gs_effect_param *param = params + i;\n\n\t\tif (strcmp(param->name, name) == 0)\n\t\t\treturn param;\n\t}\n\n\treturn NULL;\n}\n\nsize_t gs_param_get_num_annotations(const gs_eparam_t *param)\n{\n\treturn param ? param->annotations.num : 0;\n}\n\ngs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param, size_t annotation)\n{\n\tif (!param)\n\t\treturn NULL;\n\n\tstruct gs_effect_param *params = param->annotations.array;\n\tif (annotation > param->annotations.num)\n\t\treturn NULL;\n\n\treturn params + annotation;\n}\n\ngs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param, const char *name)\n{\n\tif (!param)\n\t\treturn NULL;\n\tstruct gs_effect_param *params = param->annotations.array;\n\n\tfor (size_t i = 0; i < param->annotations.num; i++) {\n\t\tstruct gs_effect_param *g_param = params + i;\n\t\tif (strcmp(g_param->name, name) == 0)\n\t\t\treturn g_param;\n\t}\n\treturn NULL;\n}\n\ngs_epass_t *gs_technique_get_pass_by_idx(const gs_technique_t *technique, size_t pass)\n{\n\tif (!technique)\n\t\treturn NULL;\n\tstruct gs_effect_pass *passes = technique->passes.array;\n\n\tif (pass > technique->passes.num)\n\t\treturn NULL;\n\n\treturn passes + pass;\n}\n\ngs_epass_t *gs_technique_get_pass_by_name(const gs_technique_t *technique, const char *name)\n{\n\tif (!technique)\n\t\treturn NULL;\n\tstruct gs_effect_pass *passes = technique->passes.array;\n\n\tfor (size_t i = 0; i < technique->passes.num; i++) {\n\t\tstruct gs_effect_pass *g_pass = passes + i;\n\t\tif (strcmp(g_pass->name, name) == 0)\n\t\t\treturn g_pass;\n\t}\n\treturn NULL;\n}\n\ngs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect)\n{\n\treturn effect ? effect->view_proj : NULL;\n}\n\ngs_eparam_t *gs_effect_get_world_matrix(const gs_effect_t *effect)\n{\n\treturn effect ? effect->world : NULL;\n}\n\nvoid gs_effect_get_param_info(const gs_eparam_t *param, struct gs_effect_param_info *info)\n{\n\tif (!param)\n\t\treturn;\n\n\tinfo->name = param->name;\n\tinfo->type = param->type;\n}\n\nstatic inline void effect_setval_inline(gs_eparam_t *param, const void *data, size_t size)\n{\n\tbool size_changed;\n\n\tif (!param) {\n\t\tblog(LOG_ERROR, \"effect_setval_inline: invalid param\");\n\t\treturn;\n\t}\n\n\tif (!data) {\n\t\tblog(LOG_ERROR, \"effect_setval_inline: invalid data\");\n\t\treturn;\n\t}\n\n\tsize_changed = param->cur_val.num != size;\n\n\tif (size_changed)\n\t\tda_resize(param->cur_val, size);\n\n\tif (size_changed || memcmp(param->cur_val.array, data, size) != 0) {\n\t\tmemcpy(param->cur_val.array, data, size);\n\t\tparam->changed = true;\n\t}\n}\n\n#ifndef min\n#define min(a, b) (((a) < (b)) ? (a) : (b))\n#endif\nstatic inline void effect_getval_inline(gs_eparam_t *param, void *data, size_t size)\n{\n\tif (!param) {\n\t\tblog(LOG_ERROR, \"effect_getval_inline: invalid param\");\n\t\treturn;\n\t}\n\n\tif (!data) {\n\t\tblog(LOG_ERROR, \"effect_getval_inline: invalid data\");\n\t\treturn;\n\t}\n\n\tsize_t bytes = min(size, param->cur_val.num);\n\n\tmemcpy(data, param->cur_val.array, bytes);\n}\n\nstatic inline void effect_getdefaultval_inline(gs_eparam_t *param, void *data, size_t size)\n{\n\tif (!param) {\n\t\tblog(LOG_ERROR, \"effect_getdefaultval_inline: invalid param\");\n\t\treturn;\n\t}\n\n\tif (!data) {\n\t\tblog(LOG_ERROR, \"effect_getdefaultval_inline: invalid data\");\n\t\treturn;\n\t}\n\n\tsize_t bytes = min(size, param->default_val.num);\n\n\tmemcpy(data, param->default_val.array, bytes);\n}\n\nvoid gs_effect_set_bool(gs_eparam_t *param, bool val)\n{\n\tint b_val = (int)val;\n\teffect_setval_inline(param, &b_val, sizeof(int));\n}\n\nvoid gs_effect_set_float(gs_eparam_t *param, float val)\n{\n\teffect_setval_inline(param, &val, sizeof(float));\n}\n\nvoid gs_effect_set_int(gs_eparam_t *param, int val)\n{\n\teffect_setval_inline(param, &val, sizeof(int));\n}\n\nvoid gs_effect_set_matrix4(gs_eparam_t *param, const struct matrix4 *val)\n{\n\teffect_setval_inline(param, val, sizeof(struct matrix4));\n}\n\nvoid gs_effect_set_vec2(gs_eparam_t *param, const struct vec2 *val)\n{\n\teffect_setval_inline(param, val, sizeof(struct vec2));\n}\n\nvoid gs_effect_set_vec3(gs_eparam_t *param, const struct vec3 *val)\n{\n\teffect_setval_inline(param, val, sizeof(float) * 3);\n}\n\nvoid gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val)\n{\n\teffect_setval_inline(param, val, sizeof(struct vec4));\n}\n\nvoid gs_effect_set_color(gs_eparam_t *param, uint32_t argb)\n{\n\tstruct vec4 v_color;\n\tvec4_from_bgra(&v_color, argb);\n\teffect_setval_inline(param, &v_color, sizeof(struct vec4));\n}\n\nvoid gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val)\n{\n\tstruct gs_shader_texture shader_tex;\n\tshader_tex.tex = val;\n\tshader_tex.srgb = false;\n\teffect_setval_inline(param, &shader_tex, sizeof(shader_tex));\n}\n\nvoid gs_effect_set_texture_srgb(gs_eparam_t *param, gs_texture_t *val)\n{\n\tstruct gs_shader_texture shader_tex;\n\tshader_tex.tex = val;\n\tshader_tex.srgb = true;\n\teffect_setval_inline(param, &shader_tex, sizeof(shader_tex));\n}\n\nvoid gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size)\n{\n\teffect_setval_inline(param, val, size);\n}\n\nvoid *gs_effect_get_val(gs_eparam_t *param)\n{\n\tif (!param) {\n\t\tblog(LOG_ERROR, \"gs_effect_get_val: invalid param\");\n\t\treturn NULL;\n\t}\n\tsize_t size = param->cur_val.num;\n\tvoid *data;\n\n\tif (size)\n\t\tdata = (void *)bzalloc(size);\n\telse\n\t\treturn NULL;\n\n\teffect_getval_inline(param, data, size);\n\n\treturn data;\n}\n\nsize_t gs_effect_get_val_size(gs_eparam_t *param)\n{\n\treturn param ? param->cur_val.num : 0;\n}\n\nvoid *gs_effect_get_default_val(gs_eparam_t *param)\n{\n\tif (!param) {\n\t\tblog(LOG_ERROR, \"gs_effect_get_default_val: invalid param\");\n\t\treturn NULL;\n\t}\n\tsize_t size = param->default_val.num;\n\tvoid *data;\n\n\tif (size)\n\t\tdata = (void *)bzalloc(size);\n\telse\n\t\treturn NULL;\n\n\teffect_getdefaultval_inline(param, data, size);\n\n\treturn data;\n}\n\nsize_t gs_effect_get_default_val_size(gs_eparam_t *param)\n{\n\treturn param ? param->default_val.num : 0;\n}\n\nvoid gs_effect_set_default(gs_eparam_t *param)\n{\n\teffect_setval_inline(param, param->default_val.array, param->default_val.num);\n}\n\nvoid gs_effect_set_next_sampler(gs_eparam_t *param, gs_samplerstate_t *sampler)\n{\n\tif (!param) {\n\t\tblog(LOG_ERROR, \"gs_effect_set_next_sampler: invalid param\");\n\t\treturn;\n\t}\n\n\tif (param->type == GS_SHADER_PARAM_TEXTURE)\n\t\tparam->next_sampler = sampler;\n}\n"
  },
  {
    "path": "libobs/graphics/effect.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"effect-parser.h\"\n#include \"graphics.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef DARRAY(struct gs_effect_param) gs_effect_param_array_t;\ntypedef DARRAY(struct pass_shaderparam) pass_shaderparam_array_t;\n\n/*\n * Effects introduce a means of bundling together shader text into one\n * file with shared functions and parameters.  This is done because often\n * shaders must be duplicated when you need to alter minor aspects of the code\n * that cannot be done via constants.  Effects allow developers to easily\n * switch shaders and set constants that can be used between shaders.\n *\n * Effects are built via the effect parser, and shaders are automatically\n * generated for each technique's pass.\n */\n\n/* ------------------------------------------------------------------------- */\n\nenum effect_section { EFFECT_PARAM, EFFECT_TECHNIQUE, EFFECT_SAMPLER, EFFECT_PASS, EFFECT_ANNOTATION };\n\n/* ------------------------------------------------------------------------- */\n\nstruct gs_effect_param {\n\tchar *name;\n\tenum effect_section section;\n\n\tenum gs_shader_param_type type;\n\n\tbool changed;\n\tDARRAY(uint8_t) cur_val;\n\tDARRAY(uint8_t) default_val;\n\n\tgs_effect_t *effect;\n\tgs_samplerstate_t *next_sampler;\n\n\t/*char *full_name;\n\tfloat scroller_min, scroller_max, scroller_inc, scroller_mul;*/\n\tgs_effect_param_array_t annotations;\n};\n\nstatic inline void effect_param_init(struct gs_effect_param *param)\n{\n\tmemset(param, 0, sizeof(struct gs_effect_param));\n\tda_init(param->annotations);\n}\n\nstatic inline void effect_param_free(struct gs_effect_param *param)\n{\n\tbfree(param->name);\n\t//bfree(param->full_name);\n\tda_free(param->cur_val);\n\tda_free(param->default_val);\n\n\tsize_t i;\n\tfor (i = 0; i < param->annotations.num; i++)\n\t\teffect_param_free(param->annotations.array + i);\n\n\tda_free(param->annotations);\n}\n\nEXPORT void effect_param_parse_property(gs_eparam_t *param, const char *property);\n\n/* ------------------------------------------------------------------------- */\n\nstruct pass_shaderparam {\n\tstruct gs_effect_param *eparam;\n\tgs_sparam_t *sparam;\n};\n\nstruct gs_effect_pass {\n\tchar *name;\n\tenum effect_section section;\n\n\tgs_shader_t *vertshader;\n\tgs_shader_t *pixelshader;\n\tpass_shaderparam_array_t vertshader_params;\n\tpass_shaderparam_array_t pixelshader_params;\n};\n\nstatic inline void effect_pass_init(struct gs_effect_pass *pass)\n{\n\tmemset(pass, 0, sizeof(struct gs_effect_pass));\n}\n\nstatic inline void effect_pass_free(struct gs_effect_pass *pass)\n{\n\tbfree(pass->name);\n\tda_free(pass->vertshader_params);\n\tda_free(pass->pixelshader_params);\n\n\tgs_shader_destroy(pass->vertshader);\n\tgs_shader_destroy(pass->pixelshader);\n}\n\n/* ------------------------------------------------------------------------- */\n\nstruct gs_effect_technique {\n\tchar *name;\n\tenum effect_section section;\n\tstruct gs_effect *effect;\n\n\tDARRAY(struct gs_effect_pass) passes;\n};\n\nstatic inline void effect_technique_init(struct gs_effect_technique *t)\n{\n\tmemset(t, 0, sizeof(struct gs_effect_technique));\n}\n\nstatic inline void effect_technique_free(struct gs_effect_technique *t)\n{\n\tsize_t i;\n\tfor (i = 0; i < t->passes.num; i++)\n\t\teffect_pass_free(t->passes.array + i);\n\n\tda_free(t->passes);\n\tbfree(t->name);\n}\n\n/* ------------------------------------------------------------------------- */\n\nstruct gs_effect {\n\tbool processing;\n\tbool cached;\n\tchar *effect_path, *effect_dir;\n\n\tgs_effect_param_array_t params;\n\tDARRAY(struct gs_effect_technique) techniques;\n\n\tstruct gs_effect_technique *cur_technique;\n\tstruct gs_effect_pass *cur_pass;\n\n\tgs_eparam_t *view_proj, *world, *scale;\n\tgraphics_t *graphics;\n\n\tstruct gs_effect *next;\n\n\tsize_t loop_pass;\n\tbool looping;\n};\n\nstatic inline void effect_init(gs_effect_t *effect)\n{\n\tmemset(effect, 0, sizeof(struct gs_effect));\n}\n\nstatic inline void effect_free(gs_effect_t *effect)\n{\n\tsize_t i;\n\tfor (i = 0; i < effect->params.num; i++)\n\t\teffect_param_free(effect->params.array + i);\n\tfor (i = 0; i < effect->techniques.num; i++)\n\t\teffect_technique_free(effect->techniques.array + i);\n\n\tda_free(effect->params);\n\tda_free(effect->techniques);\n\n\tbfree(effect->effect_path);\n\tbfree(effect->effect_dir);\n\teffect->effect_path = NULL;\n\teffect->effect_dir = NULL;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/graphics-ffmpeg.c",
    "content": "#include \"graphics.h\"\n\n#include \"half.h\"\n#include \"srgb.h\"\n#include <obs-ffmpeg-compat.h>\n#include <util/dstr.h>\n#include <util/platform.h>\n\n#include <libavcodec/avcodec.h>\n#include <libavformat/avformat.h>\n#include <libavutil/imgutils.h>\n#include <libswscale/swscale.h>\n\n#ifdef _WIN32\n#include <wincodec.h>\n#pragma comment(lib, \"windowscodecs.lib\")\n#endif\n\nstruct ffmpeg_image {\n\tconst char *file;\n\tAVFormatContext *fmt_ctx;\n\tAVCodecContext *decoder_ctx;\n\n\tint cx, cy;\n\tenum AVPixelFormat format;\n};\n\nstatic bool ffmpeg_image_open_decoder_context(struct ffmpeg_image *info)\n{\n\tAVFormatContext *const fmt_ctx = info->fmt_ctx;\n\tint ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, 1, NULL, 0);\n\tif (ret < 0) {\n\t\tblog(LOG_WARNING, \"Couldn't find video stream in file '%s': %s\", info->file, av_err2str(ret));\n\t\treturn false;\n\t}\n\n\tAVStream *const stream = fmt_ctx->streams[ret];\n\tAVCodecParameters *const codecpar = stream->codecpar;\n\tconst AVCodec *const decoder = avcodec_find_decoder(codecpar->codec_id); // fix discarded-qualifiers\n\n\tif (!decoder) {\n\t\tblog(LOG_WARNING, \"Failed to find decoder for file '%s'\", info->file);\n\t\treturn false;\n\t}\n\n\tAVCodecContext *const decoder_ctx = avcodec_alloc_context3(decoder);\n\tavcodec_parameters_to_context(decoder_ctx, codecpar);\n\n\tinfo->decoder_ctx = decoder_ctx;\n\tinfo->cx = codecpar->width;\n\tinfo->cy = codecpar->height;\n\tinfo->format = codecpar->format;\n\n\tret = avcodec_open2(decoder_ctx, decoder, NULL);\n\tif (ret < 0) {\n\t\tblog(LOG_WARNING,\n\t\t     \"Failed to open video codec for file '%s': \"\n\t\t     \"%s\",\n\t\t     info->file, av_err2str(ret));\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstatic void ffmpeg_image_free(struct ffmpeg_image *info)\n{\n\tavcodec_free_context(&info->decoder_ctx);\n\tavformat_close_input(&info->fmt_ctx);\n}\n\nstatic bool ffmpeg_image_init(struct ffmpeg_image *info, const char *file)\n{\n\tint ret;\n\n\tif (!file || !*file)\n\t\treturn false;\n\n\tmemset(info, 0, sizeof(struct ffmpeg_image));\n\tinfo->file = file;\n\n\tret = avformat_open_input(&info->fmt_ctx, file, NULL, NULL);\n\tif (ret < 0) {\n\t\tblog(LOG_WARNING, \"Failed to open file '%s': %s\", info->file, av_err2str(ret));\n\t\treturn false;\n\t}\n\n\tret = avformat_find_stream_info(info->fmt_ctx, NULL);\n\tif (ret < 0) {\n\t\tblog(LOG_WARNING,\n\t\t     \"Could not find stream info for file '%s':\"\n\t\t     \" %s\",\n\t\t     info->file, av_err2str(ret));\n\t\tgoto fail;\n\t}\n\n\tif (!ffmpeg_image_open_decoder_context(info))\n\t\tgoto fail;\n\n\treturn true;\n\nfail:\n\tffmpeg_image_free(info);\n\treturn false;\n}\n\n#ifdef _MSC_VER\n#define obs_bswap16(v) _byteswap_ushort(v)\n#else\n#define obs_bswap16(v) __builtin_bswap16(v)\n#endif\n\nstatic void *ffmpeg_image_copy_data_straight(struct ffmpeg_image *info, AVFrame *frame)\n{\n\tconst size_t linesize = (size_t)info->cx * 4;\n\tconst size_t totalsize = info->cy * linesize;\n\tvoid *data = bmalloc(totalsize);\n\n\tconst size_t src_linesize = frame->linesize[0];\n\tif (linesize != src_linesize) {\n\t\tconst size_t min_line = linesize < src_linesize ? linesize : src_linesize;\n\n\t\tuint8_t *dst = data;\n\t\tconst uint8_t *src = frame->data[0];\n\t\tfor (int y = 0; y < info->cy; y++) {\n\t\t\tmemcpy(dst, src, min_line);\n\t\t\tdst += linesize;\n\t\t\tsrc += src_linesize;\n\t\t}\n\t} else {\n\t\tmemcpy(data, frame->data[0], totalsize);\n\t}\n\n\treturn data;\n}\n\nstatic inline size_t get_dst_position(const size_t w, const size_t h, const size_t x, const size_t y, int orient)\n{\n\tsize_t res_x = 0;\n\tsize_t res_y = 0;\n\n\tif (orient == 2) {\n\t\t/*\n\t\t * Orientation 2: Flip X\n\t\t *\n\t\t * 888888        888888\n\t\t *     88   ->   88\n\t\t *   8888   ->   8888\n\t\t *     88   ->   88\n\t\t *     88        88\n\t\t *\n\t\t * (0, 0) -> (w, 0)\n\t\t * (0, h) -> (w, h)\n\t\t * (w, 0) -> (0, 0)\n\t\t * (w, h) -> (0, h)\n\t\t *\n\t\t * (w - x, y)\n\t\t */\n\n\t\tres_x = w - 1 - x;\n\t\tres_y = y;\n\n\t} else if (orient == 3) {\n\t\t/*\n\t\t * Orientation 3: 180 degree\n\t\t *\n\t\t *     88        888888\n\t\t *     88   ->   88\n\t\t *   8888   ->   8888\n\t\t *     88   ->   88\n\t\t * 888888        88\n\t\t *\n\t\t * (0, 0) -> (w, h)\n\t\t * (0, h) -> (w, 0)\n\t\t * (w, 0) -> (0, h)\n\t\t * (w, h) -> (0, 0)\n\t\t *\n\t\t * (w - x, h - y)\n\t\t */\n\n\t\tres_x = w - 1 - x;\n\t\tres_y = h - 1 - y;\n\n\t} else if (orient == 4) {\n\t\t/*\n\t\t * Orientation 4: Flip Y\n\t\t *\n\t\t * 88            888888\n\t\t * 88       ->   88\n\t\t * 8888     ->   8888\n\t\t * 88       ->   88\n\t\t * 888888        88\n\t\t *\n\t\t * (0, 0) -> (0, h)\n\t\t * (0, h) -> (0, 0)\n\t\t * (w, 0) -> (w, h)\n\t\t * (w, h) -> (w, 0)\n\t\t *\n\t\t * (x, h - y)\n\t\t */\n\n\t\tres_x = x;\n\t\tres_y = h - 1 - y;\n\n\t} else if (orient == 5) {\n\t\t/*\n\t\t * Orientation 5: Flip Y + 90 degree CW\n\t\t *\n\t\t * 8888888888        888888\n\t\t * 88  88       ->   88\n\t\t * 88           ->   8888\n\t\t *              ->   88\n\t\t *                   88\n\t\t *\n\t\t * (0, 0) -> (0, 0)\n\t\t * (0, h) -> (w, 0)\n\t\t * (w, 0) -> (0, h)\n\t\t * (w, h) -> (w, h)\n\t\t *\n\t\t * (y, x)\n\t\t */\n\n\t\tres_x = y;\n\t\tres_y = x;\n\n\t} else if (orient == 6) {\n\t\t/*\n\t\t * Orientation 6: 90 degree CW\n\t\t *\n\t\t * 88                888888\n\t\t * 88  88       ->   88\n\t\t * 8888888888   ->   8888\n\t\t *              ->   88\n\t\t *                   88\n\t\t *\n\t\t * (0, 0) -> (w, 0)\n\t\t * (0, h) -> (0, 0)\n\t\t * (w, 0) -> (w, h)\n\t\t * (w, h) -> (0, h)\n\t\t *\n\t\t * (w - y, x)\n\t\t */\n\n\t\tres_x = w - 1 - y;\n\t\tres_y = x;\n\n\t} else if (orient == 7) {\n\t\t/*\n\t\t * Orientation 7: Flip Y + 90 degree CCW\n\t\t *\n\t\t *         88        888888\n\t\t *     88  88   ->   88\n\t\t * 8888888888   ->   8888\n\t\t *              ->   88\n\t\t *                   88\n\t\t *\n\t\t * (0, 0) -> (w, h)\n\t\t * (0, h) -> (0, h)\n\t\t * (w, 0) -> (w, 0)\n\t\t * (w, h) -> (0, 0)\n\t\t *\n\t\t * (w - y, h - x)\n\t\t */\n\n\t\tres_x = w - 1 - y;\n\t\tres_y = h - 1 - x;\n\n\t} else if (orient == 8) {\n\t\t/*\n\t\t * Orientation 8: 90 degree CCW\n\t\t *\n\t\t * 8888888888        888888\n\t\t *     88  88   ->   88\n\t\t *         88   ->   8888\n\t\t *              ->   88\n\t\t *                   88\n\t\t *\n\t\t * (0, 0) -> (0, h)\n\t\t * (0, h) -> (w, h)\n\t\t * (w, 0) -> (0, 0)\n\t\t * (w, h) -> (w, 0)\n\t\t *\n\t\t * (y, h - x)\n\t\t */\n\n\t\tres_x = y;\n\t\tres_y = h - 1 - x;\n\t}\n\n\treturn (res_x + res_y * w) * 4;\n}\n\n#define TILE_SIZE 16\n#define MIN(a, b) (((a) < (b)) ? (a) : (b))\n\nstatic void *ffmpeg_image_orient(struct ffmpeg_image *info, void *in_data, int orient)\n{\n\tconst size_t sx = (size_t)info->cx;\n\tconst size_t sy = (size_t)info->cy;\n\tuint8_t *data = NULL;\n\n\tif (orient == 0 || orient == 1)\n\t\treturn in_data;\n\n\tdata = bmalloc(sx * 4 * sy);\n\n\tif (orient >= 5 && orient < 9) {\n\t\tinfo->cx = (int)sy;\n\t\tinfo->cy = (int)sx;\n\t}\n\n\tuint8_t *src = in_data;\n\n\tsize_t off_dst;\n\tsize_t off_src = 0;\n\tfor (size_t y0 = 0; y0 < sy; y0 += TILE_SIZE) {\n\t\tfor (size_t x0 = 0; x0 < sx; x0 += TILE_SIZE) {\n\t\t\tsize_t lim_x = MIN((size_t)sx, x0 + TILE_SIZE);\n\t\t\tsize_t lim_y = MIN((size_t)sy, y0 + TILE_SIZE);\n\n\t\t\tfor (size_t y = y0; y < lim_y; y++) {\n\t\t\t\tfor (size_t x = x0; x < lim_x; x++) {\n\t\t\t\t\toff_src = (x + y * sx) * 4;\n\n\t\t\t\t\toff_dst = get_dst_position(info->cx, info->cy, x, y, orient);\n\n\t\t\t\t\tmemcpy(data + off_dst, src + off_src, 4);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tbfree(in_data);\n\treturn data;\n}\n\nstatic void *ffmpeg_image_reformat_frame(struct ffmpeg_image *info, AVFrame *frame, enum gs_image_alpha_mode alpha_mode)\n{\n\tstruct SwsContext *sws_ctx = NULL;\n\tvoid *data = NULL;\n\tint ret = 0;\n\n\tAVDictionary *dict = frame->metadata;\n\tAVDictionaryEntry *entry = NULL;\n\n\tint orient = 0;\n\n\tif (dict) {\n\t\tentry = av_dict_get(dict, \"Orientation\", NULL, AV_DICT_MATCH_CASE);\n\t\tif (entry && entry->value) {\n\t\t\torient = atoi(entry->value);\n\t\t}\n\t}\n\n\tif (info->format == AV_PIX_FMT_BGR0) {\n\t\tdata = ffmpeg_image_copy_data_straight(info, frame);\n\t} else if (info->format == AV_PIX_FMT_RGBA || info->format == AV_PIX_FMT_BGRA) {\n\t\tif (alpha_mode == GS_IMAGE_ALPHA_STRAIGHT) {\n\t\t\tdata = ffmpeg_image_copy_data_straight(info, frame);\n\t\t} else {\n\t\t\tconst size_t linesize = (size_t)info->cx * 4;\n\t\t\tconst size_t totalsize = info->cy * linesize;\n\t\t\tdata = bmalloc(totalsize);\n\t\t\tconst size_t src_linesize = frame->linesize[0];\n\t\t\tconst size_t min_line = linesize < src_linesize ? linesize : src_linesize;\n\t\t\tuint8_t *dst = data;\n\t\t\tconst uint8_t *src = frame->data[0];\n\t\t\tconst size_t row_elements = min_line >> 2;\n\t\t\tif (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY_SRGB) {\n\t\t\t\tfor (int y = 0; y < info->cy; y++) {\n\t\t\t\t\tgs_premultiply_xyza_srgb_loop_restrict(dst, src, row_elements);\n\t\t\t\t\tdst += linesize;\n\t\t\t\t\tsrc += src_linesize;\n\t\t\t\t}\n\t\t\t} else if (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY) {\n\t\t\t\tfor (int y = 0; y < info->cy; y++) {\n\t\t\t\t\tgs_premultiply_xyza_loop_restrict(dst, src, row_elements);\n\t\t\t\t\tdst += linesize;\n\t\t\t\t\tsrc += src_linesize;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else if (info->format == AV_PIX_FMT_RGBA64BE) {\n\t\tconst size_t dst_linesize = (size_t)info->cx * 4;\n\t\tdata = bmalloc(info->cy * dst_linesize);\n\t\tconst size_t src_linesize = frame->linesize[0];\n\t\tconst size_t src_min_line = (dst_linesize * 2) < src_linesize ? (dst_linesize * 2) : src_linesize;\n\t\tconst size_t row_elements = src_min_line >> 3;\n\t\tuint8_t *dst = data;\n\t\tconst uint8_t *src = frame->data[0];\n\t\tuint16_t value[4];\n\t\tfloat f[4];\n\t\tif (alpha_mode == GS_IMAGE_ALPHA_STRAIGHT) {\n\t\t\tfor (int y = 0; y < info->cy; y++) {\n\t\t\t\tfor (size_t x = 0; x < row_elements; ++x) {\n\t\t\t\t\tmemcpy(value, src, sizeof(value));\n\t\t\t\t\tf[0] = (float)obs_bswap16(value[0]) / 65535.0f;\n\t\t\t\t\tf[1] = (float)obs_bswap16(value[1]) / 65535.0f;\n\t\t\t\t\tf[2] = (float)obs_bswap16(value[2]) / 65535.0f;\n\t\t\t\t\tf[3] = (float)obs_bswap16(value[3]) / 65535.0f;\n\t\t\t\t\tgs_float4_to_u8x4(dst, f);\n\t\t\t\t\tdst += sizeof(*dst) * 4;\n\t\t\t\t\tsrc += sizeof(value);\n\t\t\t\t}\n\n\t\t\t\tsrc += src_linesize - src_min_line;\n\t\t\t}\n\t\t} else if (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY_SRGB) {\n\t\t\tfor (int y = 0; y < info->cy; y++) {\n\t\t\t\tfor (size_t x = 0; x < row_elements; ++x) {\n\t\t\t\t\tmemcpy(value, src, sizeof(value));\n\t\t\t\t\tf[0] = (float)obs_bswap16(value[0]) / 65535.0f;\n\t\t\t\t\tf[1] = (float)obs_bswap16(value[1]) / 65535.0f;\n\t\t\t\t\tf[2] = (float)obs_bswap16(value[2]) / 65535.0f;\n\t\t\t\t\tf[3] = (float)obs_bswap16(value[3]) / 65535.0f;\n\t\t\t\t\tgs_float3_srgb_nonlinear_to_linear(f);\n\t\t\t\t\tgs_premultiply_float4(f);\n\t\t\t\t\tgs_float3_srgb_linear_to_nonlinear(f);\n\t\t\t\t\tgs_float4_to_u8x4(dst, f);\n\t\t\t\t\tdst += sizeof(*dst) * 4;\n\t\t\t\t\tsrc += sizeof(value);\n\t\t\t\t}\n\n\t\t\t\tsrc += src_linesize - src_min_line;\n\t\t\t}\n\t\t} else if (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY) {\n\t\t\tfor (int y = 0; y < info->cy; y++) {\n\t\t\t\tfor (size_t x = 0; x < row_elements; ++x) {\n\t\t\t\t\tmemcpy(value, src, sizeof(value));\n\t\t\t\t\tf[0] = (float)obs_bswap16(value[0]) / 65535.0f;\n\t\t\t\t\tf[1] = (float)obs_bswap16(value[1]) / 65535.0f;\n\t\t\t\t\tf[2] = (float)obs_bswap16(value[2]) / 65535.0f;\n\t\t\t\t\tf[3] = (float)obs_bswap16(value[3]) / 65535.0f;\n\t\t\t\t\tgs_premultiply_float4(f);\n\t\t\t\t\tgs_float4_to_u8x4(dst, f);\n\t\t\t\t\tdst += sizeof(*dst) * 4;\n\t\t\t\t\tsrc += sizeof(value);\n\t\t\t\t}\n\n\t\t\t\tsrc += src_linesize - src_min_line;\n\t\t\t}\n\t\t}\n\n\t\tinfo->format = AV_PIX_FMT_RGBA;\n\t} else {\n\t\tstatic const enum AVPixelFormat format = AV_PIX_FMT_BGRA;\n\n\t\tsws_ctx = sws_getContext(info->cx, info->cy, info->format, info->cx, info->cy, format, SWS_POINT, NULL,\n\t\t\t\t\t NULL, NULL);\n\t\tif (!sws_ctx) {\n\t\t\tblog(LOG_WARNING,\n\t\t\t     \"Failed to create scale context \"\n\t\t\t     \"for '%s'\",\n\t\t\t     info->file);\n\t\t\tgoto fail;\n\t\t}\n\n\t\tuint8_t *pointers[4];\n\t\tint linesizes[4];\n\t\tret = av_image_alloc(pointers, linesizes, info->cx, info->cy, format, 32);\n\t\tif (ret < 0) {\n\t\t\tblog(LOG_WARNING, \"av_image_alloc failed for '%s': %s\", info->file, av_err2str(ret));\n\t\t\tsws_freeContext(sws_ctx);\n\t\t\tgoto fail;\n\t\t}\n\n\t\tret = sws_scale(sws_ctx, (const uint8_t *const *)frame->data, frame->linesize, 0, info->cy, pointers,\n\t\t\t\tlinesizes);\n\t\tsws_freeContext(sws_ctx);\n\n\t\tif (ret < 0) {\n\t\t\tblog(LOG_WARNING, \"sws_scale failed for '%s': %s\", info->file, av_err2str(ret));\n\t\t\tav_freep(pointers);\n\t\t\tgoto fail;\n\t\t}\n\n\t\tconst size_t linesize = (size_t)info->cx * 4;\n\t\tdata = bmalloc(info->cy * linesize);\n\t\tconst uint8_t *src = pointers[0];\n\t\tuint8_t *dst = data;\n\t\tfor (size_t y = 0; y < (size_t)info->cy; y++) {\n\t\t\tmemcpy(dst, src, linesize);\n\t\t\tdst += linesize;\n\t\t\tsrc += linesizes[0];\n\t\t}\n\n\t\tav_freep(pointers);\n\n\t\tif (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY_SRGB) {\n\t\t\tgs_premultiply_xyza_srgb_loop(data, (size_t)info->cx * info->cy);\n\t\t} else if (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY) {\n\t\t\tgs_premultiply_xyza_loop(data, (size_t)info->cx * info->cy);\n\t\t}\n\n\t\tinfo->format = format;\n\t}\n\n\tdata = ffmpeg_image_orient(info, data, orient);\n\nfail:\n\treturn data;\n}\n\nstatic void *ffmpeg_image_decode(struct ffmpeg_image *info, enum gs_image_alpha_mode alpha_mode)\n{\n\tAVPacket packet = {0};\n\tvoid *data = NULL;\n\tAVFrame *frame = av_frame_alloc();\n\tint got_frame = 0;\n\tint ret;\n\n\tif (!frame) {\n\t\tblog(LOG_WARNING, \"Failed to create frame data for '%s'\", info->file);\n\t\treturn NULL;\n\t}\n\n\tret = av_read_frame(info->fmt_ctx, &packet);\n\tif (ret < 0) {\n\t\tblog(LOG_WARNING, \"Failed to read image frame from '%s': %s\", info->file, av_err2str(ret));\n\t\tgoto fail;\n\t}\n\n\twhile (!got_frame) {\n\t\tret = avcodec_send_packet(info->decoder_ctx, &packet);\n\t\tif (ret == 0)\n\t\t\tret = avcodec_receive_frame(info->decoder_ctx, frame);\n\n\t\tgot_frame = (ret == 0);\n\n\t\tif (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))\n\t\t\tret = 0;\n\n\t\tif (ret < 0) {\n\t\t\tblog(LOG_WARNING, \"Failed to decode frame for '%s': %s\", info->file, av_err2str(ret));\n\t\t\tgoto fail;\n\t\t}\n\t}\n\n\tdata = ffmpeg_image_reformat_frame(info, frame, alpha_mode);\n\nfail:\n\tav_packet_unref(&packet);\n\tav_frame_free(&frame);\n\treturn data;\n}\n\nvoid gs_init_image_deps(void) {}\n\nvoid gs_free_image_deps(void) {}\n\nstatic inline enum gs_color_format convert_format(enum AVPixelFormat format)\n{\n\tswitch (format) {\n\tcase AV_PIX_FMT_RGBA:\n\t\treturn GS_RGBA;\n\tcase AV_PIX_FMT_BGRA:\n\t\treturn GS_BGRA;\n\tcase AV_PIX_FMT_BGR0:\n\t\treturn GS_BGRX;\n\tcase AV_PIX_FMT_RGBA64BE:\n\t\treturn GS_RGBA16;\n\tdefault:\n\t\treturn GS_BGRX;\n\t}\n}\n\nuint8_t *gs_create_texture_file_data(const char *file, enum gs_color_format *format, uint32_t *cx_out, uint32_t *cy_out)\n{\n\tstruct ffmpeg_image image;\n\tuint8_t *data = NULL;\n\n\tif (ffmpeg_image_init(&image, file)) {\n\t\tdata = ffmpeg_image_decode(&image, GS_IMAGE_ALPHA_STRAIGHT);\n\t\tif (data) {\n\t\t\t*format = convert_format(image.format);\n\t\t\t*cx_out = (uint32_t)image.cx;\n\t\t\t*cy_out = (uint32_t)image.cy;\n\t\t}\n\n\t\tffmpeg_image_free(&image);\n\t}\n\n\treturn data;\n}\n\n#ifdef _WIN32\nstatic float pq_to_linear(float u)\n{\n\tconst float common = powf(u, 1.f / 78.84375f);\n\treturn powf(fabsf(max(common - 0.8359375f, 0.f) / (18.8515625f - 18.6875f * common)), 1.f / 0.1593017578f);\n}\n\nstatic void convert_pq_to_cccs(const BYTE *intermediate, const UINT intermediate_size, BYTE *bytes)\n{\n\tconst BYTE *src_cursor = intermediate;\n\tconst BYTE *src_cursor_end = src_cursor + intermediate_size;\n\tBYTE *dst_cursor = bytes;\n\tuint32_t rgb10;\n\tstruct half rgba16[4];\n\trgba16[3].u = 0x3c00;\n\twhile (src_cursor < src_cursor_end) {\n\t\tmemcpy(&rgb10, src_cursor, sizeof(rgb10));\n\t\tconst float blue = (float)(rgb10 & 0x3ff) / 1023.f;\n\t\tconst float green = (float)((rgb10 >> 10) & 0x3ff) / 1023.f;\n\t\tconst float red = (float)((rgb10 >> 20) & 0x3ff) / 1023.f;\n\t\tconst float red2020 = pq_to_linear(red);\n\t\tconst float green2020 = pq_to_linear(green);\n\t\tconst float blue2020 = pq_to_linear(blue);\n\t\tconst float red709 = 1.6604910021084345f * red2020 - 0.58764113878854951f * green2020 -\n\t\t\t\t     0.072849863319884883f * blue2020;\n\t\tconst float green709 = -0.12455047452159074f * red2020 + 1.1328998971259603f * green2020 -\n\t\t\t\t       0.0083494226043694768f * blue2020;\n\t\tconst float blue709 = -0.018150763354905303f * red2020 - 0.10057889800800739f * green2020 +\n\t\t\t\t      1.1187296613629127f * blue2020;\n\t\trgba16[0] = half_from_float(red709 * 125.f);\n\t\trgba16[1] = half_from_float(green709 * 125.f);\n\t\trgba16[2] = half_from_float(blue709 * 125.f);\n\t\tmemcpy(dst_cursor, &rgba16, sizeof(rgba16));\n\t\tsrc_cursor += 4;\n\t\tdst_cursor += 8;\n\t}\n}\n\nstatic void *wic_image_init_internal(const char *file, IWICBitmapFrameDecode *pFrame, enum gs_color_format *format,\n\t\t\t\t     uint32_t *cx_out, uint32_t *cy_out, enum gs_color_space *space)\n{\n\tBYTE *bytes = NULL;\n\n\tWICPixelFormatGUID pixelFormat;\n\tHRESULT hr = pFrame->lpVtbl->GetPixelFormat(pFrame, &pixelFormat);\n\tif (SUCCEEDED(hr)) {\n\t\tconst bool scrgb = memcmp(&pixelFormat, &GUID_WICPixelFormat64bppRGBAHalf, sizeof(pixelFormat)) == 0;\n\t\tconst bool pq10 = memcmp(&pixelFormat, &GUID_WICPixelFormat32bppBGR101010, sizeof(pixelFormat)) == 0;\n\t\tif (scrgb || pq10) {\n\t\t\tUINT width, height;\n\t\t\thr = pFrame->lpVtbl->GetSize(pFrame, &width, &height);\n\t\t\tif (SUCCEEDED(hr)) {\n\t\t\t\tconst UINT pitch = 8 * width;\n\t\t\t\tconst UINT size = pitch * height;\n\t\t\t\tbytes = bmalloc(size);\n\t\t\t\tif (bytes) {\n\t\t\t\t\tbool success = false;\n\t\t\t\t\tif (pq10) {\n\t\t\t\t\t\tconst UINT intermediate_pitch = 4 * width;\n\t\t\t\t\t\tconst UINT intermediate_size = intermediate_pitch * height;\n\t\t\t\t\t\tBYTE *intermediate = bmalloc(intermediate_size);\n\t\t\t\t\t\tif (intermediate) {\n\t\t\t\t\t\t\thr = pFrame->lpVtbl->CopyPixels(pFrame, NULL,\n\t\t\t\t\t\t\t\t\t\t\tintermediate_pitch,\n\t\t\t\t\t\t\t\t\t\t\tintermediate_size,\n\t\t\t\t\t\t\t\t\t\t\tintermediate);\n\t\t\t\t\t\t\tsuccess = SUCCEEDED(hr);\n\t\t\t\t\t\t\tif (success) {\n\t\t\t\t\t\t\t\tconvert_pq_to_cccs(intermediate, intermediate_size,\n\t\t\t\t\t\t\t\t\t\t   bytes);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tblog(LOG_WARNING,\n\t\t\t\t\t\t\t\t     \"WIC: Failed to CopyPixels intermediate for file: %s\",\n\t\t\t\t\t\t\t\t     file);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbfree(intermediate);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tblog(LOG_WARNING,\n\t\t\t\t\t\t\t     \"WIC: Failed to allocate intermediate for file: %s\", file);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\thr = pFrame->lpVtbl->CopyPixels(pFrame, NULL, pitch, size, bytes);\n\t\t\t\t\t\tsuccess = SUCCEEDED(hr);\n\t\t\t\t\t\tif (!success) {\n\t\t\t\t\t\t\tblog(LOG_WARNING, \"WIC: Failed to CopyPixels for file: %s\",\n\t\t\t\t\t\t\t     file);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (success) {\n\t\t\t\t\t\t*format = GS_RGBA16F;\n\t\t\t\t\t\t*cx_out = width;\n\t\t\t\t\t\t*cy_out = height;\n\t\t\t\t\t\t*space = GS_CS_709_SCRGB;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbfree(bytes);\n\t\t\t\t\t\tbytes = NULL;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tblog(LOG_WARNING, \"WIC: Failed to allocate for file: %s\", file);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tblog(LOG_WARNING, \"WIC: Failed to GetSize of frame for file: %s\", file);\n\t\t\t}\n\t\t} else {\n\t\t\tblog(LOG_WARNING,\n\t\t\t     \"WIC: Only handle GUID_WICPixelFormat32bppBGR101010 and GUID_WICPixelFormat64bppRGBAHalf for now\");\n\t\t}\n\t} else {\n\t\tblog(LOG_WARNING, \"WIC: Failed to GetPixelFormat for file: %s\", file);\n\t}\n\n\treturn bytes;\n}\n\nstatic void *wic_image_init(const struct ffmpeg_image *info, const char *file, enum gs_color_format *format,\n\t\t\t    uint32_t *cx_out, uint32_t *cy_out, enum gs_color_space *space)\n{\n\tconst size_t len = strlen(file);\n\tif (len <= 4 && astrcmpi(file + len - 4, \".jxr\") != 0) {\n\t\tblog(LOG_WARNING, \"WIC: Only handle JXR for WIC images for now\");\n\t\treturn NULL;\n\t}\n\n\tBYTE *bytes = NULL;\n\n\twchar_t *file_w = NULL;\n\tos_utf8_to_wcs_ptr(file, 0, &file_w);\n\tif (file_w) {\n\t\tIWICImagingFactory *pFactory = NULL;\n\t\tHRESULT hr = CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,\n\t\t\t\t\t      &IID_IWICImagingFactory, &pFactory);\n\t\tif (SUCCEEDED(hr)) {\n\t\t\tIWICBitmapDecoder *pDecoder = NULL;\n\t\t\thr = pFactory->lpVtbl->CreateDecoderFromFilename(pFactory, file_w, NULL, GENERIC_READ,\n\t\t\t\t\t\t\t\t\t WICDecodeMetadataCacheOnDemand, &pDecoder);\n\t\t\tif (SUCCEEDED(hr)) {\n\t\t\t\tIWICBitmapFrameDecode *pFrame = NULL;\n\t\t\t\thr = pDecoder->lpVtbl->GetFrame(pDecoder, 0, &pFrame);\n\t\t\t\tif (SUCCEEDED(hr)) {\n\t\t\t\t\tbytes = wic_image_init_internal(file, pFrame, format, cx_out, cy_out, space);\n\n\t\t\t\t\tpFrame->lpVtbl->Release(pFrame);\n\t\t\t\t} else {\n\t\t\t\t\tblog(LOG_WARNING, \"WIC: Failed to create IWICBitmapFrameDecode from file: %s\",\n\t\t\t\t\t     file);\n\t\t\t\t}\n\n\t\t\t\tpDecoder->lpVtbl->Release(pDecoder);\n\t\t\t} else {\n\t\t\t\tblog(LOG_WARNING, \"WIC: Failed to create IWICBitmapDecoder from file: %s\", file);\n\t\t\t}\n\n\t\t\tpFactory->lpVtbl->Release(pFactory);\n\t\t} else {\n\t\t\tblog(LOG_WARNING, \"WIC: Failed to create IWICImagingFactory\");\n\t\t}\n\n\t\tbfree(file_w);\n\t} else {\n\t\tblog(LOG_WARNING, \"WIC: Failed to widen file name: %s\", file);\n\t}\n\n\treturn bytes;\n}\n#endif\n\nuint8_t *gs_create_texture_file_data2(const char *file, enum gs_image_alpha_mode alpha_mode,\n\t\t\t\t      enum gs_color_format *format, uint32_t *cx_out, uint32_t *cy_out)\n{\n\tenum gs_color_space unused;\n\treturn gs_create_texture_file_data3(file, alpha_mode, format, cx_out, cy_out, &unused);\n}\n\nuint8_t *gs_create_texture_file_data3(const char *file, enum gs_image_alpha_mode alpha_mode,\n\t\t\t\t      enum gs_color_format *format, uint32_t *cx_out, uint32_t *cy_out,\n\t\t\t\t      enum gs_color_space *space)\n{\n\tstruct ffmpeg_image image;\n\tuint8_t *data = NULL;\n\n\tif (ffmpeg_image_init(&image, file)) {\n\t\tdata = ffmpeg_image_decode(&image, alpha_mode);\n\t\tif (data) {\n\t\t\t*format = convert_format(image.format);\n\t\t\t*cx_out = (uint32_t)image.cx;\n\t\t\t*cy_out = (uint32_t)image.cy;\n\t\t\t*space = GS_CS_SRGB;\n\t\t}\n\n\t\tffmpeg_image_free(&image);\n\t}\n\n#ifdef _WIN32\n\tif (data == NULL) {\n\t\tdata = wic_image_init(&image, file, format, cx_out, cy_out, space);\n\t}\n#endif\n\n\treturn data;\n}\n"
  },
  {
    "path": "libobs/graphics/graphics-imports.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"../util/base.h\"\n#include \"../util/dstr.h\"\n#include \"../util/platform.h\"\n#include \"graphics-internal.h\"\n\n#define GRAPHICS_IMPORT(func)                                     \\\n\tdo {                                                      \\\n\t\texports->func = os_dlsym(module, #func);          \\\n\t\tif (!exports->func) {                             \\\n\t\t\tsuccess = false;                          \\\n\t\t\tblog(LOG_ERROR,                           \\\n\t\t\t     \"Could not load function '%s' from \" \\\n\t\t\t     \"module '%s'\",                       \\\n\t\t\t     #func, module_name);                 \\\n\t\t}                                                 \\\n\t} while (false)\n\n#define GRAPHICS_IMPORT_OPTIONAL(func)                   \\\n\tdo {                                             \\\n\t\texports->func = os_dlsym(module, #func); \\\n\t} while (false)\n\nbool load_graphics_imports(struct gs_exports *exports, void *module, const char *module_name)\n{\n\tbool success = true;\n\n\tGRAPHICS_IMPORT(device_get_name);\n\tGRAPHICS_IMPORT_OPTIONAL(gpu_get_driver_version);\n\tGRAPHICS_IMPORT_OPTIONAL(gpu_get_renderer);\n\tGRAPHICS_IMPORT_OPTIONAL(gpu_get_dmem);\n\tGRAPHICS_IMPORT_OPTIONAL(gpu_get_smem);\n\tGRAPHICS_IMPORT(device_get_type);\n\tGRAPHICS_IMPORT_OPTIONAL(device_enum_adapters);\n\tGRAPHICS_IMPORT(device_preprocessor_name);\n\tGRAPHICS_IMPORT(device_create);\n\tGRAPHICS_IMPORT(device_destroy);\n\tGRAPHICS_IMPORT(device_enter_context);\n\tGRAPHICS_IMPORT(device_leave_context);\n\tGRAPHICS_IMPORT(device_get_device_obj);\n\tGRAPHICS_IMPORT(device_swapchain_create);\n\tGRAPHICS_IMPORT(device_resize);\n\tGRAPHICS_IMPORT(device_get_color_space);\n\tGRAPHICS_IMPORT(device_update_color_space);\n\tGRAPHICS_IMPORT(device_get_size);\n\tGRAPHICS_IMPORT(device_get_width);\n\tGRAPHICS_IMPORT(device_get_height);\n\tGRAPHICS_IMPORT(device_texture_create);\n\tGRAPHICS_IMPORT(device_cubetexture_create);\n\tGRAPHICS_IMPORT(device_voltexture_create);\n\tGRAPHICS_IMPORT(device_zstencil_create);\n\tGRAPHICS_IMPORT(device_stagesurface_create);\n\tGRAPHICS_IMPORT(device_samplerstate_create);\n\tGRAPHICS_IMPORT(device_vertexshader_create);\n\tGRAPHICS_IMPORT(device_pixelshader_create);\n\tGRAPHICS_IMPORT(device_vertexbuffer_create);\n\tGRAPHICS_IMPORT(device_indexbuffer_create);\n\tGRAPHICS_IMPORT(device_timer_create);\n\tGRAPHICS_IMPORT(device_timer_range_create);\n\tGRAPHICS_IMPORT(device_get_texture_type);\n\tGRAPHICS_IMPORT(device_load_vertexbuffer);\n\tGRAPHICS_IMPORT(device_load_indexbuffer);\n\tGRAPHICS_IMPORT(device_load_texture);\n\tGRAPHICS_IMPORT(device_load_samplerstate);\n\tGRAPHICS_IMPORT(device_load_vertexshader);\n\tGRAPHICS_IMPORT(device_load_pixelshader);\n\tGRAPHICS_IMPORT(device_load_default_samplerstate);\n\tGRAPHICS_IMPORT(device_get_vertex_shader);\n\tGRAPHICS_IMPORT(device_get_pixel_shader);\n\tGRAPHICS_IMPORT(device_get_render_target);\n\tGRAPHICS_IMPORT(device_get_zstencil_target);\n\tGRAPHICS_IMPORT(device_set_render_target);\n\tGRAPHICS_IMPORT(device_set_render_target_with_color_space);\n\tGRAPHICS_IMPORT(device_set_cube_render_target);\n\tGRAPHICS_IMPORT(device_enable_framebuffer_srgb);\n\tGRAPHICS_IMPORT(device_framebuffer_srgb_enabled);\n\tGRAPHICS_IMPORT(device_copy_texture_region);\n\tGRAPHICS_IMPORT(device_copy_texture);\n\tGRAPHICS_IMPORT(device_stage_texture);\n\tGRAPHICS_IMPORT(device_begin_frame);\n\tGRAPHICS_IMPORT(device_begin_scene);\n\tGRAPHICS_IMPORT(device_draw);\n\tGRAPHICS_IMPORT(device_load_swapchain);\n\tGRAPHICS_IMPORT(device_end_scene);\n\tGRAPHICS_IMPORT(device_clear);\n\tGRAPHICS_IMPORT(device_is_present_ready);\n\tGRAPHICS_IMPORT(device_present);\n\tGRAPHICS_IMPORT(device_flush);\n\tGRAPHICS_IMPORT(device_set_cull_mode);\n\tGRAPHICS_IMPORT(device_get_cull_mode);\n\tGRAPHICS_IMPORT(device_enable_blending);\n\tGRAPHICS_IMPORT(device_enable_depth_test);\n\tGRAPHICS_IMPORT(device_enable_stencil_test);\n\tGRAPHICS_IMPORT(device_enable_stencil_write);\n\tGRAPHICS_IMPORT(device_enable_color);\n\tGRAPHICS_IMPORT(device_blend_function);\n\tGRAPHICS_IMPORT(device_blend_function_separate);\n\tGRAPHICS_IMPORT(device_blend_op);\n\tGRAPHICS_IMPORT(device_depth_function);\n\tGRAPHICS_IMPORT(device_stencil_function);\n\tGRAPHICS_IMPORT(device_stencil_op);\n\tGRAPHICS_IMPORT(device_set_viewport);\n\tGRAPHICS_IMPORT(device_get_viewport);\n\tGRAPHICS_IMPORT(device_set_scissor_rect);\n\tGRAPHICS_IMPORT(device_ortho);\n\tGRAPHICS_IMPORT(device_frustum);\n\tGRAPHICS_IMPORT(device_projection_push);\n\tGRAPHICS_IMPORT(device_projection_pop);\n\n\tGRAPHICS_IMPORT(gs_swapchain_destroy);\n\n\tGRAPHICS_IMPORT(gs_texture_destroy);\n\tGRAPHICS_IMPORT(gs_texture_get_width);\n\tGRAPHICS_IMPORT(gs_texture_get_height);\n\tGRAPHICS_IMPORT(gs_texture_get_color_format);\n\tGRAPHICS_IMPORT(gs_texture_map);\n\tGRAPHICS_IMPORT(gs_texture_unmap);\n\tGRAPHICS_IMPORT_OPTIONAL(gs_texture_is_rect);\n\tGRAPHICS_IMPORT(gs_texture_get_obj);\n\n\tGRAPHICS_IMPORT(gs_cubetexture_destroy);\n\tGRAPHICS_IMPORT(gs_cubetexture_get_size);\n\tGRAPHICS_IMPORT(gs_cubetexture_get_color_format);\n\n\tGRAPHICS_IMPORT(gs_voltexture_destroy);\n\tGRAPHICS_IMPORT(gs_voltexture_get_width);\n\tGRAPHICS_IMPORT(gs_voltexture_get_height);\n\tGRAPHICS_IMPORT(gs_voltexture_get_depth);\n\tGRAPHICS_IMPORT(gs_voltexture_get_color_format);\n\n\tGRAPHICS_IMPORT(gs_stagesurface_destroy);\n\tGRAPHICS_IMPORT(gs_stagesurface_get_width);\n\tGRAPHICS_IMPORT(gs_stagesurface_get_height);\n\tGRAPHICS_IMPORT(gs_stagesurface_get_color_format);\n\tGRAPHICS_IMPORT(gs_stagesurface_map);\n\tGRAPHICS_IMPORT(gs_stagesurface_unmap);\n\n\tGRAPHICS_IMPORT(gs_zstencil_destroy);\n\n\tGRAPHICS_IMPORT(gs_samplerstate_destroy);\n\n\tGRAPHICS_IMPORT(gs_vertexbuffer_destroy);\n\tGRAPHICS_IMPORT(gs_vertexbuffer_flush);\n\tGRAPHICS_IMPORT(gs_vertexbuffer_flush_direct);\n\tGRAPHICS_IMPORT(gs_vertexbuffer_get_data);\n\n\tGRAPHICS_IMPORT(gs_indexbuffer_destroy);\n\tGRAPHICS_IMPORT(gs_indexbuffer_flush);\n\tGRAPHICS_IMPORT(gs_indexbuffer_flush_direct);\n\tGRAPHICS_IMPORT(gs_indexbuffer_get_data);\n\tGRAPHICS_IMPORT(gs_indexbuffer_get_num_indices);\n\tGRAPHICS_IMPORT(gs_indexbuffer_get_type);\n\n\tGRAPHICS_IMPORT(gs_timer_destroy);\n\tGRAPHICS_IMPORT(gs_timer_begin);\n\tGRAPHICS_IMPORT(gs_timer_end);\n\tGRAPHICS_IMPORT(gs_timer_get_data);\n\tGRAPHICS_IMPORT(gs_timer_range_destroy);\n\tGRAPHICS_IMPORT(gs_timer_range_begin);\n\tGRAPHICS_IMPORT(gs_timer_range_end);\n\tGRAPHICS_IMPORT(gs_timer_range_get_data);\n\n\tGRAPHICS_IMPORT(gs_shader_destroy);\n\tGRAPHICS_IMPORT(gs_shader_get_num_params);\n\tGRAPHICS_IMPORT(gs_shader_get_param_by_idx);\n\tGRAPHICS_IMPORT(gs_shader_get_param_by_name);\n\tGRAPHICS_IMPORT(gs_shader_get_viewproj_matrix);\n\tGRAPHICS_IMPORT(gs_shader_get_world_matrix);\n\tGRAPHICS_IMPORT(gs_shader_get_param_info);\n\tGRAPHICS_IMPORT(gs_shader_set_bool);\n\tGRAPHICS_IMPORT(gs_shader_set_float);\n\tGRAPHICS_IMPORT(gs_shader_set_int);\n\tGRAPHICS_IMPORT(gs_shader_set_matrix3);\n\tGRAPHICS_IMPORT(gs_shader_set_matrix4);\n\tGRAPHICS_IMPORT(gs_shader_set_vec2);\n\tGRAPHICS_IMPORT(gs_shader_set_vec3);\n\tGRAPHICS_IMPORT(gs_shader_set_vec4);\n\tGRAPHICS_IMPORT(gs_shader_set_texture);\n\tGRAPHICS_IMPORT(gs_shader_set_val);\n\tGRAPHICS_IMPORT(gs_shader_set_default);\n\tGRAPHICS_IMPORT(gs_shader_set_next_sampler);\n\n\tGRAPHICS_IMPORT_OPTIONAL(device_nv12_available);\n\tGRAPHICS_IMPORT_OPTIONAL(device_p010_available);\n\tGRAPHICS_IMPORT_OPTIONAL(device_texture_create_nv12);\n\tGRAPHICS_IMPORT_OPTIONAL(device_texture_create_p010);\n\n\tGRAPHICS_IMPORT(device_is_monitor_hdr);\n\n\tGRAPHICS_IMPORT(device_debug_marker_begin);\n\tGRAPHICS_IMPORT(device_debug_marker_end);\n\n\tGRAPHICS_IMPORT_OPTIONAL(gs_get_adapter_count);\n\n\t/* OSX/Cocoa specific functions */\n#ifdef __APPLE__\n\tGRAPHICS_IMPORT(device_shared_texture_available);\n\tGRAPHICS_IMPORT(device_texture_open_shared);\n\tGRAPHICS_IMPORT(device_texture_create_from_iosurface);\n\tGRAPHICS_IMPORT(gs_texture_rebind_iosurface);\n\n\t/* win32 specific functions */\n#elif _WIN32\n\tGRAPHICS_IMPORT(device_gdi_texture_available);\n\tGRAPHICS_IMPORT(device_shared_texture_available);\n\tGRAPHICS_IMPORT_OPTIONAL(device_get_duplicator_monitor_info);\n\tGRAPHICS_IMPORT_OPTIONAL(device_duplicator_get_monitor_index);\n\tGRAPHICS_IMPORT_OPTIONAL(device_duplicator_create);\n\tGRAPHICS_IMPORT_OPTIONAL(gs_duplicator_destroy);\n\tGRAPHICS_IMPORT_OPTIONAL(gs_duplicator_update_frame);\n\tGRAPHICS_IMPORT_OPTIONAL(gs_duplicator_get_texture);\n\tGRAPHICS_IMPORT_OPTIONAL(gs_duplicator_get_color_space);\n\tGRAPHICS_IMPORT_OPTIONAL(gs_duplicator_get_sdr_white_level);\n\tGRAPHICS_IMPORT_OPTIONAL(device_can_adapter_fast_clear);\n\tGRAPHICS_IMPORT_OPTIONAL(device_texture_create_gdi);\n\tGRAPHICS_IMPORT_OPTIONAL(gs_texture_get_dc);\n\tGRAPHICS_IMPORT_OPTIONAL(gs_texture_release_dc);\n\tGRAPHICS_IMPORT_OPTIONAL(device_texture_open_shared);\n\tGRAPHICS_IMPORT_OPTIONAL(device_texture_open_nt_shared);\n\tGRAPHICS_IMPORT_OPTIONAL(device_texture_get_shared_handle);\n\tGRAPHICS_IMPORT_OPTIONAL(device_texture_wrap_obj);\n\tGRAPHICS_IMPORT_OPTIONAL(device_texture_acquire_sync);\n\tGRAPHICS_IMPORT_OPTIONAL(device_texture_release_sync);\n\tGRAPHICS_IMPORT_OPTIONAL(device_stagesurface_create_nv12);\n\tGRAPHICS_IMPORT_OPTIONAL(device_stagesurface_create_p010);\n\tGRAPHICS_IMPORT_OPTIONAL(device_register_loss_callbacks);\n\tGRAPHICS_IMPORT_OPTIONAL(device_unregister_loss_callbacks);\n#elif defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)\n\tGRAPHICS_IMPORT(device_texture_create_from_dmabuf);\n\tGRAPHICS_IMPORT(device_query_dmabuf_capabilities);\n\tGRAPHICS_IMPORT(device_query_dmabuf_modifiers_for_format);\n\tGRAPHICS_IMPORT(device_texture_create_from_pixmap);\n\tGRAPHICS_IMPORT(device_query_sync_capabilities);\n\tGRAPHICS_IMPORT(device_sync_create);\n\tGRAPHICS_IMPORT(device_sync_create_from_syncobj_timeline_point);\n\tGRAPHICS_IMPORT(device_sync_destroy);\n\tGRAPHICS_IMPORT(device_sync_export_syncobj_timeline_point);\n\tGRAPHICS_IMPORT(device_sync_signal_syncobj_timeline_point);\n\tGRAPHICS_IMPORT(device_sync_wait);\n#endif\n\n\treturn success;\n}\n"
  },
  {
    "path": "libobs/graphics/graphics-internal.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/threading.h\"\n#include \"../util/darray.h\"\n#include \"graphics.h\"\n#include \"matrix3.h\"\n#include \"matrix4.h\"\n\n/* ========================================================================= *\n * Exports                                                                   *\n * ========================================================================= */\n\nstruct gs_exports {\n\tconst char *(*device_get_name)(void);\n\tconst char *(*gpu_get_driver_version)(void);\n\tconst char *(*gpu_get_renderer)(void);\n\tuint64_t (*gpu_get_dmem)(void);\n\tuint64_t (*gpu_get_smem)(void);\n\tint (*device_get_type)(void);\n\tbool (*device_enum_adapters)(gs_device_t *device, bool (*callback)(void *, const char *, uint32_t), void *);\n\tconst char *(*device_preprocessor_name)(void);\n\tint (*device_create)(gs_device_t **device, uint32_t adapter);\n\tvoid (*device_destroy)(gs_device_t *device);\n\tvoid (*device_enter_context)(gs_device_t *device);\n\tvoid (*device_leave_context)(gs_device_t *device);\n\tvoid *(*device_get_device_obj)(gs_device_t *device);\n\tgs_swapchain_t *(*device_swapchain_create)(gs_device_t *device, const struct gs_init_data *data);\n\tvoid (*device_resize)(gs_device_t *device, uint32_t x, uint32_t y);\n\tenum gs_color_space (*device_get_color_space)(gs_device_t *device);\n\tvoid (*device_update_color_space)(gs_device_t *device);\n\tvoid (*device_get_size)(const gs_device_t *device, uint32_t *x, uint32_t *y);\n\tuint32_t (*device_get_width)(const gs_device_t *device);\n\tuint32_t (*device_get_height)(const gs_device_t *device);\n\tgs_texture_t *(*device_texture_create)(gs_device_t *device, uint32_t width, uint32_t height,\n\t\t\t\t\t       enum gs_color_format color_format, uint32_t levels, const uint8_t **data,\n\t\t\t\t\t       uint32_t flags);\n\tgs_texture_t *(*device_cubetexture_create)(gs_device_t *device, uint32_t size,\n\t\t\t\t\t\t   enum gs_color_format color_format, uint32_t levels,\n\t\t\t\t\t\t   const uint8_t **data, uint32_t flags);\n\tgs_texture_t *(*device_voltexture_create)(gs_device_t *device, uint32_t width, uint32_t height, uint32_t depth,\n\t\t\t\t\t\t  enum gs_color_format color_format, uint32_t levels,\n\t\t\t\t\t\t  const uint8_t *const *data, uint32_t flags);\n\tgs_zstencil_t *(*device_zstencil_create)(gs_device_t *device, uint32_t width, uint32_t height,\n\t\t\t\t\t\t enum gs_zstencil_format format);\n\tgs_stagesurf_t *(*device_stagesurface_create)(gs_device_t *device, uint32_t width, uint32_t height,\n\t\t\t\t\t\t      enum gs_color_format color_format);\n\tgs_samplerstate_t *(*device_samplerstate_create)(gs_device_t *device, const struct gs_sampler_info *info);\n\tgs_shader_t *(*device_vertexshader_create)(gs_device_t *device, const char *shader, const char *file,\n\t\t\t\t\t\t   char **error_string);\n\tgs_shader_t *(*device_pixelshader_create)(gs_device_t *device, const char *shader, const char *file,\n\t\t\t\t\t\t  char **error_string);\n\tgs_vertbuffer_t *(*device_vertexbuffer_create)(gs_device_t *device, struct gs_vb_data *data, uint32_t flags);\n\tgs_indexbuffer_t *(*device_indexbuffer_create)(gs_device_t *device, enum gs_index_type type, void *indices,\n\t\t\t\t\t\t       size_t num, uint32_t flags);\n\tgs_timer_t *(*device_timer_create)(gs_device_t *device);\n\tgs_timer_range_t *(*device_timer_range_create)(gs_device_t *device);\n\tenum gs_texture_type (*device_get_texture_type)(const gs_texture_t *texture);\n\tvoid (*device_load_vertexbuffer)(gs_device_t *device, gs_vertbuffer_t *vertbuffer);\n\tvoid (*device_load_indexbuffer)(gs_device_t *device, gs_indexbuffer_t *indexbuffer);\n\tvoid (*device_load_texture)(gs_device_t *device, gs_texture_t *tex, int unit);\n\tvoid (*device_load_samplerstate)(gs_device_t *device, gs_samplerstate_t *samplerstate, int unit);\n\tvoid (*device_load_vertexshader)(gs_device_t *device, gs_shader_t *vertshader);\n\tvoid (*device_load_pixelshader)(gs_device_t *device, gs_shader_t *pixelshader);\n\tvoid (*device_load_default_samplerstate)(gs_device_t *device, bool b_3d, int unit);\n\tgs_shader_t *(*device_get_vertex_shader)(const gs_device_t *device);\n\tgs_shader_t *(*device_get_pixel_shader)(const gs_device_t *device);\n\tgs_texture_t *(*device_get_render_target)(const gs_device_t *device);\n\tgs_zstencil_t *(*device_get_zstencil_target)(const gs_device_t *device);\n\tvoid (*device_set_render_target)(gs_device_t *device, gs_texture_t *tex, gs_zstencil_t *zstencil);\n\tvoid (*device_set_render_target_with_color_space)(gs_device_t *device, gs_texture_t *tex,\n\t\t\t\t\t\t\t  gs_zstencil_t *zstencil, enum gs_color_space space);\n\tvoid (*device_set_cube_render_target)(gs_device_t *device, gs_texture_t *cubetex, int side,\n\t\t\t\t\t      gs_zstencil_t *zstencil);\n\tvoid (*device_enable_framebuffer_srgb)(gs_device_t *device, bool enable);\n\tbool (*device_framebuffer_srgb_enabled)(gs_device_t *device);\n\tvoid (*device_copy_texture)(gs_device_t *device, gs_texture_t *dst, gs_texture_t *src);\n\tvoid (*device_copy_texture_region)(gs_device_t *device, gs_texture_t *dst, uint32_t dst_x, uint32_t dst_y,\n\t\t\t\t\t   gs_texture_t *src, uint32_t src_x, uint32_t src_y, uint32_t src_w,\n\t\t\t\t\t   uint32_t src_h);\n\tvoid (*device_stage_texture)(gs_device_t *device, gs_stagesurf_t *dst, gs_texture_t *src);\n\tvoid (*device_begin_frame)(gs_device_t *device);\n\tvoid (*device_begin_scene)(gs_device_t *device);\n\tvoid (*device_draw)(gs_device_t *device, enum gs_draw_mode draw_mode, uint32_t start_vert, uint32_t num_verts);\n\tvoid (*device_end_scene)(gs_device_t *device);\n\tvoid (*device_load_swapchain)(gs_device_t *device, gs_swapchain_t *swaphchain);\n\tvoid (*device_clear)(gs_device_t *device, uint32_t clear_flags, const struct vec4 *color, float depth,\n\t\t\t     uint8_t stencil);\n\tbool (*device_is_present_ready)(gs_device_t *device);\n\tvoid (*device_present)(gs_device_t *device);\n\tvoid (*device_flush)(gs_device_t *device);\n\tvoid (*device_set_cull_mode)(gs_device_t *device, enum gs_cull_mode mode);\n\tenum gs_cull_mode (*device_get_cull_mode)(const gs_device_t *device);\n\tvoid (*device_enable_blending)(gs_device_t *device, bool enable);\n\tvoid (*device_enable_depth_test)(gs_device_t *device, bool enable);\n\tvoid (*device_enable_stencil_test)(gs_device_t *device, bool enable);\n\tvoid (*device_enable_stencil_write)(gs_device_t *device, bool enable);\n\tvoid (*device_enable_color)(gs_device_t *device, bool red, bool green, bool blue, bool alpha);\n\tvoid (*device_blend_function)(gs_device_t *device, enum gs_blend_type src, enum gs_blend_type dest);\n\tvoid (*device_blend_function_separate)(gs_device_t *device, enum gs_blend_type src_c, enum gs_blend_type dest_c,\n\t\t\t\t\t       enum gs_blend_type src_a, enum gs_blend_type dest_a);\n\tvoid (*device_blend_op)(gs_device_t *device, enum gs_blend_op_type op);\n\n\tvoid (*device_depth_function)(gs_device_t *device, enum gs_depth_test test);\n\tvoid (*device_stencil_function)(gs_device_t *device, enum gs_stencil_side side, enum gs_depth_test test);\n\tvoid (*device_stencil_op)(gs_device_t *device, enum gs_stencil_side side, enum gs_stencil_op_type fail,\n\t\t\t\t  enum gs_stencil_op_type zfail, enum gs_stencil_op_type zpass);\n\tvoid (*device_set_viewport)(gs_device_t *device, int x, int y, int width, int height);\n\tvoid (*device_get_viewport)(const gs_device_t *device, struct gs_rect *rect);\n\tvoid (*device_set_scissor_rect)(gs_device_t *device, const struct gs_rect *rect);\n\tvoid (*device_ortho)(gs_device_t *device, float left, float right, float top, float bottom, float znear,\n\t\t\t     float zfar);\n\tvoid (*device_frustum)(gs_device_t *device, float left, float right, float top, float bottom, float znear,\n\t\t\t       float zfar);\n\tvoid (*device_projection_push)(gs_device_t *device);\n\tvoid (*device_projection_pop)(gs_device_t *device);\n\n\tvoid (*gs_swapchain_destroy)(gs_swapchain_t *swapchain);\n\n\tvoid (*gs_texture_destroy)(gs_texture_t *tex);\n\tuint32_t (*gs_texture_get_width)(const gs_texture_t *tex);\n\tuint32_t (*gs_texture_get_height)(const gs_texture_t *tex);\n\tenum gs_color_format (*gs_texture_get_color_format)(const gs_texture_t *tex);\n\tbool (*gs_texture_map)(gs_texture_t *tex, uint8_t **ptr, uint32_t *linesize);\n\tvoid (*gs_texture_unmap)(gs_texture_t *tex);\n\tbool (*gs_texture_is_rect)(const gs_texture_t *tex);\n\tvoid *(*gs_texture_get_obj)(const gs_texture_t *tex);\n\n\tvoid (*gs_cubetexture_destroy)(gs_texture_t *cubetex);\n\tuint32_t (*gs_cubetexture_get_size)(const gs_texture_t *cubetex);\n\tenum gs_color_format (*gs_cubetexture_get_color_format)(const gs_texture_t *cubetex);\n\n\tvoid (*gs_voltexture_destroy)(gs_texture_t *voltex);\n\tuint32_t (*gs_voltexture_get_width)(const gs_texture_t *voltex);\n\tuint32_t (*gs_voltexture_get_height)(const gs_texture_t *voltex);\n\tuint32_t (*gs_voltexture_get_depth)(const gs_texture_t *voltex);\n\tenum gs_color_format (*gs_voltexture_get_color_format)(const gs_texture_t *voltex);\n\n\tvoid (*gs_stagesurface_destroy)(gs_stagesurf_t *stagesurf);\n\tuint32_t (*gs_stagesurface_get_width)(const gs_stagesurf_t *stagesurf);\n\tuint32_t (*gs_stagesurface_get_height)(const gs_stagesurf_t *stagesurf);\n\tenum gs_color_format (*gs_stagesurface_get_color_format)(const gs_stagesurf_t *stagesurf);\n\tbool (*gs_stagesurface_map)(gs_stagesurf_t *stagesurf, uint8_t **data, uint32_t *linesize);\n\tvoid (*gs_stagesurface_unmap)(gs_stagesurf_t *stagesurf);\n\n\tvoid (*gs_zstencil_destroy)(gs_zstencil_t *zstencil);\n\n\tvoid (*gs_samplerstate_destroy)(gs_samplerstate_t *samplerstate);\n\n\tvoid (*gs_vertexbuffer_destroy)(gs_vertbuffer_t *vertbuffer);\n\tvoid (*gs_vertexbuffer_flush)(gs_vertbuffer_t *vertbuffer);\n\tvoid (*gs_vertexbuffer_flush_direct)(gs_vertbuffer_t *vertbuffer, const struct gs_vb_data *data);\n\tstruct gs_vb_data *(*gs_vertexbuffer_get_data)(const gs_vertbuffer_t *vertbuffer);\n\n\tvoid (*gs_indexbuffer_destroy)(gs_indexbuffer_t *indexbuffer);\n\tvoid (*gs_indexbuffer_flush)(gs_indexbuffer_t *indexbuffer);\n\tvoid (*gs_indexbuffer_flush_direct)(gs_indexbuffer_t *indexbuffer, const void *data);\n\tvoid *(*gs_indexbuffer_get_data)(const gs_indexbuffer_t *indexbuffer);\n\tsize_t (*gs_indexbuffer_get_num_indices)(const gs_indexbuffer_t *indexbuffer);\n\tenum gs_index_type (*gs_indexbuffer_get_type)(const gs_indexbuffer_t *indexbuffer);\n\n\tvoid (*gs_timer_destroy)(gs_timer_t *timer);\n\tvoid (*gs_timer_begin)(gs_timer_t *timer);\n\tvoid (*gs_timer_end)(gs_timer_t *timer);\n\tbool (*gs_timer_get_data)(gs_timer_t *timer, uint64_t *ticks);\n\tvoid (*gs_timer_range_destroy)(gs_timer_range_t *range);\n\tbool (*gs_timer_range_begin)(gs_timer_range_t *range);\n\tbool (*gs_timer_range_end)(gs_timer_range_t *range);\n\tbool (*gs_timer_range_get_data)(gs_timer_range_t *range, bool *disjoint, uint64_t *frequency);\n\n\tvoid (*gs_shader_destroy)(gs_shader_t *shader);\n\tint (*gs_shader_get_num_params)(const gs_shader_t *shader);\n\tgs_sparam_t *(*gs_shader_get_param_by_idx)(gs_shader_t *shader, uint32_t param);\n\tgs_sparam_t *(*gs_shader_get_param_by_name)(gs_shader_t *shader, const char *name);\n\tgs_sparam_t *(*gs_shader_get_viewproj_matrix)(const gs_shader_t *shader);\n\tgs_sparam_t *(*gs_shader_get_world_matrix)(const gs_shader_t *shader);\n\tvoid (*gs_shader_get_param_info)(const gs_sparam_t *param, struct gs_shader_param_info *info);\n\tvoid (*gs_shader_set_bool)(gs_sparam_t *param, bool val);\n\tvoid (*gs_shader_set_float)(gs_sparam_t *param, float val);\n\tvoid (*gs_shader_set_int)(gs_sparam_t *param, int val);\n\tvoid (*gs_shader_set_matrix3)(gs_sparam_t *param, const struct matrix3 *val);\n\tvoid (*gs_shader_set_matrix4)(gs_sparam_t *param, const struct matrix4 *val);\n\tvoid (*gs_shader_set_vec2)(gs_sparam_t *param, const struct vec2 *val);\n\tvoid (*gs_shader_set_vec3)(gs_sparam_t *param, const struct vec3 *val);\n\tvoid (*gs_shader_set_vec4)(gs_sparam_t *param, const struct vec4 *val);\n\tvoid (*gs_shader_set_texture)(gs_sparam_t *param, gs_texture_t *val);\n\tvoid (*gs_shader_set_val)(gs_sparam_t *param, const void *val, size_t size);\n\tvoid (*gs_shader_set_default)(gs_sparam_t *param);\n\tvoid (*gs_shader_set_next_sampler)(gs_sparam_t *param, gs_samplerstate_t *sampler);\n\n\tbool (*device_nv12_available)(gs_device_t *device);\n\tbool (*device_p010_available)(gs_device_t *device);\n\tbool (*device_texture_create_nv12)(gs_device_t *device, gs_texture_t **tex_y, gs_texture_t **tex_uv,\n\t\t\t\t\t   uint32_t width, uint32_t height, uint32_t flags);\n\tbool (*device_texture_create_p010)(gs_device_t *device, gs_texture_t **tex_y, gs_texture_t **tex_uv,\n\t\t\t\t\t   uint32_t width, uint32_t height, uint32_t flags);\n\n\tbool (*device_is_monitor_hdr)(gs_device_t *device, void *monitor);\n\n\tvoid (*device_debug_marker_begin)(gs_device_t *device, const char *markername, const float color[4]);\n\tvoid (*device_debug_marker_end)(gs_device_t *device);\n\n\tuint32_t (*gs_get_adapter_count)(void);\n\n#ifdef __APPLE__\n\t/* OSX/Cocoa specific functions */\n\tgs_texture_t *(*device_texture_create_from_iosurface)(gs_device_t *dev, void *iosurf);\n\tgs_texture_t *(*device_texture_open_shared)(gs_device_t *dev, uint32_t handle);\n\tbool (*gs_texture_rebind_iosurface)(gs_texture_t *texture, void *iosurf);\n\tbool (*device_shared_texture_available)(void);\n\n#elif _WIN32\n\tbool (*device_gdi_texture_available)(void);\n\tbool (*device_shared_texture_available)(void);\n\n\tbool (*device_get_duplicator_monitor_info)(gs_device_t *device, int monitor_idx,\n\t\t\t\t\t\t   struct gs_monitor_info *monitor_info);\n\tint (*device_duplicator_get_monitor_index)(gs_device_t *device, void *monitor);\n\n\tgs_duplicator_t *(*device_duplicator_create)(gs_device_t *device, int monitor_idx);\n\tvoid (*gs_duplicator_destroy)(gs_duplicator_t *duplicator);\n\n\tbool (*gs_duplicator_update_frame)(gs_duplicator_t *duplicator);\n\tgs_texture_t *(*gs_duplicator_get_texture)(gs_duplicator_t *duplicator);\n\tenum gs_color_space (*gs_duplicator_get_color_space)(gs_duplicator_t *duplicator);\n\tfloat (*gs_duplicator_get_sdr_white_level)(gs_duplicator_t *duplicator);\n\n\tbool (*device_can_adapter_fast_clear)(gs_device_t *device);\n\n\tgs_texture_t *(*device_texture_create_gdi)(gs_device_t *device, uint32_t width, uint32_t height);\n\n\tvoid *(*gs_texture_get_dc)(gs_texture_t *gdi_tex);\n\tvoid (*gs_texture_release_dc)(gs_texture_t *gdi_tex);\n\n\tgs_texture_t *(*device_texture_open_shared)(gs_device_t *device, uint32_t handle);\n\tgs_texture_t *(*device_texture_open_nt_shared)(gs_device_t *device, uint32_t handle);\n\tuint32_t (*device_texture_get_shared_handle)(gs_texture_t *tex);\n\tgs_texture_t *(*device_texture_wrap_obj)(gs_device_t *device, void *obj);\n\tint (*device_texture_acquire_sync)(gs_texture_t *tex, uint64_t key, uint32_t ms);\n\tint (*device_texture_release_sync)(gs_texture_t *tex, uint64_t key);\n\n\tgs_stagesurf_t *(*device_stagesurface_create_nv12)(gs_device_t *device, uint32_t width, uint32_t height);\n\tgs_stagesurf_t *(*device_stagesurface_create_p010)(gs_device_t *device, uint32_t width, uint32_t height);\n\tvoid (*device_register_loss_callbacks)(gs_device_t *device, const struct gs_device_loss *callbacks);\n\tvoid (*device_unregister_loss_callbacks)(gs_device_t *device, void *data);\n#elif defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)\n\tstruct gs_texture *(*device_texture_create_from_dmabuf)(gs_device_t *device, unsigned int width,\n\t\t\t\t\t\t\t\tunsigned int height, uint32_t drm_format,\n\t\t\t\t\t\t\t\tenum gs_color_format color_format, uint32_t n_planes,\n\t\t\t\t\t\t\t\tconst int *fds, const uint32_t *strides,\n\t\t\t\t\t\t\t\tconst uint32_t *offsets, const uint64_t *modifiers);\n\tbool (*device_query_dmabuf_capabilities)(gs_device_t *device, enum gs_dmabuf_flags *dmabuf_flags,\n\t\t\t\t\t\t uint32_t **drm_formats, size_t *n_formats);\n\tbool (*device_query_dmabuf_modifiers_for_format)(gs_device_t *device, uint32_t drm_format, uint64_t **modifiers,\n\t\t\t\t\t\t\t size_t *n_modifiers);\n\tstruct gs_texture *(*device_texture_create_from_pixmap)(gs_device_t *device, uint32_t width, uint32_t height,\n\t\t\t\t\t\t\t\tenum gs_color_format color_format, uint32_t target,\n\t\t\t\t\t\t\t\tvoid *pixmap);\n\tbool (*device_query_sync_capabilities)(gs_device_t *device);\n\tgs_sync_t *(*device_sync_create)(gs_device_t *device);\n\tgs_sync_t *(*device_sync_create_from_syncobj_timeline_point)(gs_device_t *device, int syncobj_fd,\n\t\t\t\t\t\t\t\t     uint64_t timeline_point);\n\tvoid (*device_sync_destroy)(gs_device_t *device, gs_sync_t *sync);\n\tbool (*device_sync_export_syncobj_timeline_point)(gs_device_t *device, gs_sync_t *sync, int syncobj_fd,\n\t\t\t\t\t\t\t  uint64_t timeline_point);\n\tbool (*device_sync_signal_syncobj_timeline_point)(gs_device_t *device, int syncobj_fd, uint64_t timeline_point);\n\tbool (*device_sync_wait)(gs_device_t *device, gs_sync_t *sync);\n#endif\n};\n\n/* ========================================================================= *\n * Graphics Subsystem Data                                                   *\n * ========================================================================= */\n\nstruct blend_state {\n\tbool enabled;\n\tenum gs_blend_type src_c;\n\tenum gs_blend_type dest_c;\n\tenum gs_blend_type src_a;\n\tenum gs_blend_type dest_a;\n\tenum gs_blend_op_type op;\n};\n\nstruct graphics_subsystem {\n\tvoid *module;\n\tgs_device_t *device;\n\tstruct gs_exports exports;\n\n\tDARRAY(struct gs_rect) viewport_stack;\n\n\tDARRAY(struct matrix4) matrix_stack;\n\tsize_t cur_matrix;\n\n\tstruct matrix4 projection;\n\tstruct gs_effect *cur_effect;\n\n\tgs_vertbuffer_t *sprite_buffer;\n\tgs_vertbuffer_t *flipped_sprite_buffer;\n\tgs_vertbuffer_t *subregion_buffer;\n\n\tbool using_immediate;\n\tstruct gs_vb_data *vbd;\n\tgs_vertbuffer_t *immediate_vertbuffer;\n\tDARRAY(struct vec3) verts;\n\tDARRAY(struct vec3) norms;\n\tDARRAY(uint32_t) colors;\n\tDARRAY(struct vec2) texverts[16];\n\n\tpthread_mutex_t effect_mutex;\n\tstruct gs_effect *first_effect;\n\n\tpthread_mutex_t mutex;\n\tvolatile long ref;\n\n\tstruct blend_state cur_blend_state;\n\tDARRAY(struct blend_state) blend_state_stack;\n\n\tbool linear_srgb;\n};\n"
  },
  {
    "path": "libobs/graphics/graphics.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <assert.h>\n\n#include \"../util/base.h\"\n#include \"../util/bmem.h\"\n#include \"../util/platform.h\"\n#include \"graphics-internal.h\"\n#include \"vec2.h\"\n#include \"vec3.h\"\n#include \"quat.h\"\n#include \"axisang.h\"\n#include \"effect-parser.h\"\n#include \"effect.h\"\n\n#ifdef near\n#undef near\n#endif\n#ifdef far\n#undef far\n#endif\n\nstatic THREAD_LOCAL graphics_t *thread_graphics = NULL;\n\nstatic inline bool gs_obj_valid(const void *obj, const char *f, const char *name)\n{\n\tif (!obj) {\n\t\tblog(LOG_DEBUG, \"%s: Null '%s' parameter\", f, name);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstatic inline bool gs_valid(const char *f)\n{\n\tif (!thread_graphics) {\n\t\tblog(LOG_DEBUG, \"%s: called while not in a graphics context\", f);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n#define ptr_valid(ptr, func) gs_obj_valid(ptr, func, #ptr)\n\n#define gs_valid_p(func, param1) (gs_valid(func) && ptr_valid(param1, func))\n\n#define gs_valid_p2(func, param1, param2) (gs_valid(func) && ptr_valid(param1, func) && ptr_valid(param2, func))\n\n#define gs_valid_p3(func, param1, param2, param3) \\\n\t(gs_valid(func) && ptr_valid(param1, func) && ptr_valid(param2, func) && ptr_valid(param3, func))\n\n#define IMMEDIATE_COUNT 512\n\nvoid gs_enum_adapters(bool (*callback)(void *param, const char *name, uint32_t id), void *param)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_enum_adapters\", callback))\n\t\treturn;\n\n\tif (graphics->exports.device_enum_adapters) {\n\t\tif (graphics->exports.device_enum_adapters(graphics->device, callback, param)) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/* If the subsystem does not currently support device enumeration of\n\t * adapters or fails to enumerate adapters, just set it to one adapter\n\t * named \"Default\" */\n\tcallback(param, \"Default\", 0);\n}\n\nextern void gs_init_image_deps(void);\nextern void gs_free_image_deps(void);\n\nbool load_graphics_imports(struct gs_exports *exports, void *module, const char *module_name);\n\nstatic bool graphics_init_immediate_vb(struct graphics_subsystem *graphics)\n{\n\tstruct gs_vb_data *vbd;\n\n\tvbd = gs_vbdata_create();\n\tvbd->num = IMMEDIATE_COUNT;\n\tvbd->points = bmalloc(sizeof(struct vec3) * IMMEDIATE_COUNT);\n\tvbd->normals = bmalloc(sizeof(struct vec3) * IMMEDIATE_COUNT);\n\tvbd->colors = bmalloc(sizeof(uint32_t) * IMMEDIATE_COUNT);\n\tvbd->num_tex = 1;\n\tvbd->tvarray = bmalloc(sizeof(struct gs_tvertarray));\n\tvbd->tvarray[0].width = 2;\n\tvbd->tvarray[0].array = bmalloc(sizeof(struct vec2) * IMMEDIATE_COUNT);\n\n\tgraphics->immediate_vertbuffer =\n\t\tgraphics->exports.device_vertexbuffer_create(graphics->device, vbd, GS_DYNAMIC);\n\tif (!graphics->immediate_vertbuffer)\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic bool graphics_init_sprite_vbs(struct graphics_subsystem *graphics)\n{\n\tstruct gs_vb_data *vbd;\n\n\tvbd = gs_vbdata_create();\n\tvbd->num = 4;\n\tvbd->points = bzalloc(sizeof(struct vec3) * 4);\n\tvbd->num_tex = 1;\n\tvbd->tvarray = bzalloc(sizeof(struct gs_tvertarray));\n\tvbd->tvarray[0].width = 2;\n\tvbd->tvarray[0].array = bzalloc(sizeof(struct vec2) * 4);\n\n\tvbd->points[1].x = 1.0f;\n\tvbd->points[2].y = 1.0f;\n\tvbd->points[3].x = 1.0f;\n\tvbd->points[3].y = 1.0f;\n\n\tstruct vec2 *uvs = vbd->tvarray[0].array;\n\tuvs[1].x = 1.0f;\n\tuvs[2].y = 1.0f;\n\tuvs[3].x = 1.0f;\n\tuvs[3].y = 1.0f;\n\n\tgraphics->sprite_buffer = gs_vertexbuffer_create(vbd, GS_DUP_BUFFER);\n\tif (!graphics->sprite_buffer)\n\t\treturn false;\n\n\tgraphics->subregion_buffer = gs_vertexbuffer_create(vbd, GS_DUP_BUFFER | GS_DYNAMIC);\n\tif (!graphics->subregion_buffer)\n\t\treturn false;\n\n\tuvs[0].y = 1.0f;\n\tuvs[1].y = 1.0f;\n\tuvs[2].y = 0.0f;\n\tuvs[3].y = 0.0f;\n\n\tgraphics->flipped_sprite_buffer = gs_vertexbuffer_create(vbd, 0);\n\tif (!graphics->flipped_sprite_buffer)\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic bool graphics_init(struct graphics_subsystem *graphics)\n{\n\tstruct matrix4 top_mat;\n\n\tmatrix4_identity(&top_mat);\n\tda_push_back(graphics->matrix_stack, &top_mat);\n\n\tgraphics->exports.device_enter_context(graphics->device);\n\n\tthread_graphics = graphics;\n\n\tif (!graphics_init_immediate_vb(graphics))\n\t\treturn false;\n\tif (!graphics_init_sprite_vbs(graphics))\n\t\treturn false;\n\tif (pthread_mutex_init(&graphics->mutex, NULL) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&graphics->effect_mutex, NULL) != 0)\n\t\treturn false;\n\n\tgraphics->exports.device_blend_function_separate(graphics->device, GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA,\n\t\t\t\t\t\t\t GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);\n\tgraphics->cur_blend_state.enabled = true;\n\tgraphics->cur_blend_state.src_c = GS_BLEND_SRCALPHA;\n\tgraphics->cur_blend_state.dest_c = GS_BLEND_INVSRCALPHA;\n\tgraphics->cur_blend_state.src_a = GS_BLEND_ONE;\n\tgraphics->cur_blend_state.dest_a = GS_BLEND_INVSRCALPHA;\n\n\tgraphics->cur_blend_state.op = GS_BLEND_OP_ADD;\n\tgraphics->exports.device_blend_op(graphics->device, graphics->cur_blend_state.op);\n\n\tgraphics->exports.device_leave_context(graphics->device);\n\n\tgs_init_image_deps();\n\tthread_graphics = NULL;\n\treturn true;\n}\n\nint gs_create(graphics_t **pgraphics, const char *module, uint32_t adapter)\n{\n\tint errcode = GS_ERROR_FAIL;\n\n\tgraphics_t *graphics = bzalloc(sizeof(struct graphics_subsystem));\n\tpthread_mutex_init_value(&graphics->mutex);\n\tpthread_mutex_init_value(&graphics->effect_mutex);\n\n\tgraphics->module = os_dlopen(module);\n\tif (!graphics->module) {\n\t\terrcode = GS_ERROR_MODULE_NOT_FOUND;\n\t\tgoto error;\n\t}\n\n\tif (!load_graphics_imports(&graphics->exports, graphics->module, module))\n\t\tgoto error;\n\n\terrcode = graphics->exports.device_create(&graphics->device, adapter);\n\tif (errcode != GS_SUCCESS)\n\t\tgoto error;\n\n\tif (!graphics_init(graphics)) {\n\t\terrcode = GS_ERROR_FAIL;\n\t\tgoto error;\n\t}\n\n\t*pgraphics = graphics;\n\treturn errcode;\n\nerror:\n\tgs_destroy(graphics);\n\treturn errcode;\n}\n\nextern void gs_effect_actually_destroy(gs_effect_t *effect);\n\nvoid gs_destroy(graphics_t *graphics)\n{\n\tif (!ptr_valid(graphics, \"gs_destroy\"))\n\t\treturn;\n\n\twhile (thread_graphics)\n\t\tgs_leave_context();\n\n\tif (graphics->device) {\n\t\tstruct gs_effect *effect = graphics->first_effect;\n\n\t\tthread_graphics = graphics;\n\t\tgraphics->exports.device_enter_context(graphics->device);\n\n\t\twhile (effect) {\n\t\t\tstruct gs_effect *next = effect->next;\n\t\t\tgs_effect_actually_destroy(effect);\n\t\t\teffect = next;\n\t\t}\n\n\t\tgraphics->exports.gs_vertexbuffer_destroy(graphics->subregion_buffer);\n\t\tgraphics->exports.gs_vertexbuffer_destroy(graphics->flipped_sprite_buffer);\n\t\tgraphics->exports.gs_vertexbuffer_destroy(graphics->sprite_buffer);\n\t\tgraphics->exports.gs_vertexbuffer_destroy(graphics->immediate_vertbuffer);\n\t\tgraphics->exports.device_destroy(graphics->device);\n\n\t\tthread_graphics = NULL;\n\t}\n\n\tpthread_mutex_destroy(&graphics->mutex);\n\tpthread_mutex_destroy(&graphics->effect_mutex);\n\tda_free(graphics->matrix_stack);\n\tda_free(graphics->viewport_stack);\n\tda_free(graphics->blend_state_stack);\n\tif (graphics->module)\n\t\tos_dlclose(graphics->module);\n\tbfree(graphics);\n\n\tgs_free_image_deps();\n}\n\nvoid gs_enter_context(graphics_t *graphics)\n{\n\tif (!ptr_valid(graphics, \"gs_enter_context\"))\n\t\treturn;\n\n\tbool is_current = thread_graphics == graphics;\n\tif (thread_graphics && !is_current) {\n\t\twhile (thread_graphics)\n\t\t\tgs_leave_context();\n\t}\n\n\tif (!is_current) {\n\t\tpthread_mutex_lock(&graphics->mutex);\n\t\tgraphics->exports.device_enter_context(graphics->device);\n\t\tthread_graphics = graphics;\n\t}\n\n\tos_atomic_inc_long(&graphics->ref);\n}\n\nvoid gs_leave_context(void)\n{\n\tif (gs_valid(\"gs_leave_context\")) {\n\t\tif (!os_atomic_dec_long(&thread_graphics->ref)) {\n\t\t\tgraphics_t *graphics = thread_graphics;\n\n\t\t\tgraphics->exports.device_leave_context(graphics->device);\n\t\t\tpthread_mutex_unlock(&graphics->mutex);\n\t\t\tthread_graphics = NULL;\n\t\t}\n\t}\n}\n\ngraphics_t *gs_get_context(void)\n{\n\treturn thread_graphics;\n}\n\nvoid *gs_get_device_obj(void)\n{\n\tif (!gs_valid(\"gs_get_device_obj\"))\n\t\treturn NULL;\n\n\treturn thread_graphics->exports.device_get_device_obj(thread_graphics->device);\n}\n\nconst char *gs_get_device_name(void)\n{\n\treturn gs_valid(\"gs_get_device_name\") ? thread_graphics->exports.device_get_name() : NULL;\n}\n\nconst char *gs_get_driver_version(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_driver_version\"))\n\t\treturn NULL;\n\n\tif (graphics->exports.gpu_get_driver_version)\n\t\treturn (graphics->exports.gpu_get_driver_version());\n\telse\n\t\treturn NULL;\n}\n\nconst char *gs_get_renderer(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_renderer\"))\n\t\treturn NULL;\n\n\tif (graphics->exports.gpu_get_renderer)\n\t\treturn (graphics->exports.gpu_get_renderer());\n\telse\n\t\treturn NULL;\n}\n\nuint64_t gs_get_gpu_dmem(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_gpu_dmem\"))\n\t\treturn 0;\n\n\tif (graphics->exports.gpu_get_dmem)\n\t\treturn (graphics->exports.gpu_get_dmem());\n\telse\n\t\treturn 0;\n}\n\nuint64_t gs_get_gpu_smem(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_gpu_smem\"))\n\t\treturn 0;\n\n\tif (graphics->exports.gpu_get_smem)\n\t\treturn (graphics->exports.gpu_get_smem());\n\telse\n\t\treturn 0;\n}\n\nint gs_get_device_type(void)\n{\n\treturn gs_valid(\"gs_get_device_type\") ? thread_graphics->exports.device_get_type() : -1;\n}\n\nstatic inline struct matrix4 *top_matrix(graphics_t *graphics)\n{\n\treturn graphics->matrix_stack.array + graphics->cur_matrix;\n}\n\nvoid gs_matrix_push(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_matrix_push\"))\n\t\treturn;\n\n\tstruct matrix4 mat, *top_mat = top_matrix(graphics);\n\n\tmemcpy(&mat, top_mat, sizeof(struct matrix4));\n\tda_push_back(graphics->matrix_stack, &mat);\n\tgraphics->cur_matrix++;\n}\n\nvoid gs_matrix_pop(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_matrix_pop\"))\n\t\treturn;\n\n\tif (graphics->cur_matrix == 0) {\n\t\tblog(LOG_ERROR, \"Tried to pop last matrix on stack\");\n\t\treturn;\n\t}\n\n\tda_erase(graphics->matrix_stack, graphics->cur_matrix);\n\tgraphics->cur_matrix--;\n}\n\nvoid gs_matrix_identity(void)\n{\n\tstruct matrix4 *top_mat;\n\n\tif (!gs_valid(\"gs_matrix_identity\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat)\n\t\tmatrix4_identity(top_mat);\n}\n\nvoid gs_matrix_transpose(void)\n{\n\tstruct matrix4 *top_mat;\n\n\tif (!gs_valid(\"gs_matrix_transpose\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat)\n\t\tmatrix4_transpose(top_mat, top_mat);\n}\n\nvoid gs_matrix_set(const struct matrix4 *matrix)\n{\n\tstruct matrix4 *top_mat;\n\n\tif (!gs_valid(\"gs_matrix_set\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat)\n\t\tmatrix4_copy(top_mat, matrix);\n}\n\nvoid gs_matrix_get(struct matrix4 *dst)\n{\n\tstruct matrix4 *top_mat;\n\n\tif (!gs_valid(\"gs_matrix_get\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat)\n\t\tmatrix4_copy(dst, top_mat);\n}\n\nvoid gs_matrix_mul(const struct matrix4 *matrix)\n{\n\tstruct matrix4 *top_mat;\n\n\tif (!gs_valid(\"gs_matrix_mul\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat)\n\t\tmatrix4_mul(top_mat, matrix, top_mat);\n}\n\nvoid gs_matrix_rotquat(const struct quat *rot)\n{\n\tstruct matrix4 *top_mat;\n\n\tif (!gs_valid(\"gs_matrix_rotquat\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat)\n\t\tmatrix4_rotate_i(top_mat, rot, top_mat);\n}\n\nvoid gs_matrix_rotaa(const struct axisang *rot)\n{\n\tstruct matrix4 *top_mat;\n\n\tif (!gs_valid(\"gs_matrix_rotaa\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat)\n\t\tmatrix4_rotate_aa_i(top_mat, rot, top_mat);\n}\n\nvoid gs_matrix_translate(const struct vec3 *pos)\n{\n\tstruct matrix4 *top_mat;\n\n\tif (!gs_valid(\"gs_matrix_translate\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat)\n\t\tmatrix4_translate3v_i(top_mat, pos, top_mat);\n}\n\nvoid gs_matrix_scale(const struct vec3 *scale)\n{\n\tstruct matrix4 *top_mat;\n\n\tif (!gs_valid(\"gs_matrix_scale\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat)\n\t\tmatrix4_scale_i(top_mat, scale, top_mat);\n}\n\nvoid gs_matrix_rotaa4f(float x, float y, float z, float angle)\n{\n\tstruct matrix4 *top_mat;\n\tstruct axisang aa;\n\n\tif (!gs_valid(\"gs_matrix_rotaa4f\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat) {\n\t\taxisang_set(&aa, x, y, z, angle);\n\t\tmatrix4_rotate_aa_i(top_mat, &aa, top_mat);\n\t}\n}\n\nvoid gs_matrix_translate3f(float x, float y, float z)\n{\n\tstruct matrix4 *top_mat;\n\tstruct vec3 p;\n\n\tif (!gs_valid(\"gs_matrix_translate3f\"))\n\t\treturn;\n\n\ttop_mat = top_matrix(thread_graphics);\n\tif (top_mat) {\n\t\tvec3_set(&p, x, y, z);\n\t\tmatrix4_translate3v_i(top_mat, &p, top_mat);\n\t}\n}\n\nvoid gs_matrix_scale3f(float x, float y, float z)\n{\n\tstruct matrix4 *top_mat = top_matrix(thread_graphics);\n\tstruct vec3 p;\n\n\tif (top_mat) {\n\t\tvec3_set(&p, x, y, z);\n\t\tmatrix4_scale_i(top_mat, &p, top_mat);\n\t}\n}\n\nstatic inline void reset_immediate_arrays(graphics_t *graphics)\n{\n\tda_init(graphics->verts);\n\tda_init(graphics->norms);\n\tda_init(graphics->colors);\n\tfor (size_t i = 0; i < 16; i++)\n\t\tda_init(graphics->texverts[i]);\n}\n\nvoid gs_render_start(bool b_new)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_render_start\"))\n\t\treturn;\n\n\tgraphics->using_immediate = !b_new;\n\treset_immediate_arrays(graphics);\n\n\tif (b_new) {\n\t\tgraphics->vbd = gs_vbdata_create();\n\t} else {\n\t\tgraphics->vbd = gs_vertexbuffer_get_data(graphics->immediate_vertbuffer);\n\t\tmemset(graphics->vbd->colors, 0xFF, sizeof(uint32_t) * IMMEDIATE_COUNT);\n\n\t\tgraphics->verts.array = graphics->vbd->points;\n\t\tgraphics->norms.array = graphics->vbd->normals;\n\t\tgraphics->colors.array = graphics->vbd->colors;\n\t\tgraphics->texverts[0].array = graphics->vbd->tvarray[0].array;\n\n\t\tgraphics->verts.capacity = IMMEDIATE_COUNT;\n\t\tgraphics->norms.capacity = IMMEDIATE_COUNT;\n\t\tgraphics->colors.capacity = IMMEDIATE_COUNT;\n\t\tgraphics->texverts[0].capacity = IMMEDIATE_COUNT;\n\t}\n}\n\nstatic inline size_t min_size(const size_t a, const size_t b)\n{\n\treturn (a < b) ? a : b;\n}\n\nvoid gs_render_stop(enum gs_draw_mode mode)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tsize_t i, num;\n\n\tif (!gs_valid(\"gs_render_stop\"))\n\t\treturn;\n\n\tnum = graphics->verts.num;\n\tif (!num) {\n\t\tif (!graphics->using_immediate) {\n\t\t\tda_free(graphics->verts);\n\t\t\tda_free(graphics->norms);\n\t\t\tda_free(graphics->colors);\n\t\t\tfor (i = 0; i < 16; i++)\n\t\t\t\tda_free(graphics->texverts[i]);\n\t\t\tgs_vbdata_destroy(graphics->vbd);\n\t\t}\n\n\t\treturn;\n\t}\n\n\tif (graphics->norms.num && (graphics->norms.num != graphics->verts.num)) {\n\t\tblog(LOG_ERROR, \"gs_render_stop: normal count does \"\n\t\t\t\t\"not match vertex count\");\n\t\tnum = min_size(num, graphics->norms.num);\n\t}\n\n\tif (graphics->colors.num && (graphics->colors.num != graphics->verts.num)) {\n\t\tblog(LOG_ERROR, \"gs_render_stop: color count does \"\n\t\t\t\t\"not match vertex count\");\n\t\tnum = min_size(num, graphics->colors.num);\n\t}\n\n\tif (graphics->texverts[0].num && (graphics->texverts[0].num != graphics->verts.num)) {\n\t\tblog(LOG_ERROR, \"gs_render_stop: texture vertex count does \"\n\t\t\t\t\"not match vertex count\");\n\t\tnum = min_size(num, graphics->texverts[0].num);\n\t}\n\n\tif (graphics->using_immediate) {\n\t\tgs_vertexbuffer_flush(graphics->immediate_vertbuffer);\n\n\t\tgs_load_vertexbuffer(graphics->immediate_vertbuffer);\n\t\tgs_load_indexbuffer(NULL);\n\t\tgs_draw(mode, 0, (uint32_t)num);\n\n\t\treset_immediate_arrays(graphics);\n\t} else {\n\t\tgs_vertbuffer_t *vb = gs_render_save();\n\n\t\tgs_load_vertexbuffer(vb);\n\t\tgs_load_indexbuffer(NULL);\n\t\tgs_draw(mode, 0, 0);\n\n\t\tgs_vertexbuffer_destroy(vb);\n\t}\n\n\tgraphics->vbd = NULL;\n}\n\ngs_vertbuffer_t *gs_render_save(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tsize_t num_tex, i;\n\n\tif (!gs_valid(\"gs_render_save\"))\n\t\treturn NULL;\n\tif (graphics->using_immediate)\n\t\treturn NULL;\n\n\tif (!graphics->verts.num) {\n\t\tgs_vbdata_destroy(graphics->vbd);\n\t\treturn NULL;\n\t}\n\n\tfor (num_tex = 0; num_tex < 16; num_tex++)\n\t\tif (!graphics->texverts[num_tex].num)\n\t\t\tbreak;\n\n\tgraphics->vbd->points = graphics->verts.array;\n\tgraphics->vbd->normals = graphics->norms.array;\n\tgraphics->vbd->colors = graphics->colors.array;\n\tgraphics->vbd->num = graphics->verts.num;\n\tgraphics->vbd->num_tex = num_tex;\n\n\tif (graphics->vbd->num_tex) {\n\t\tgraphics->vbd->tvarray = bmalloc(sizeof(struct gs_tvertarray) * num_tex);\n\n\t\tfor (i = 0; i < num_tex; i++) {\n\t\t\tgraphics->vbd->tvarray[i].width = 2;\n\t\t\tgraphics->vbd->tvarray[i].array = graphics->texverts[i].array;\n\t\t}\n\t}\n\n\treset_immediate_arrays(graphics);\n\n\treturn gs_vertexbuffer_create(graphics->vbd, 0);\n}\n\nvoid gs_vertex2f(float x, float y)\n{\n\tstruct vec3 v3;\n\tvec3_set(&v3, x, y, 0.0f);\n\tgs_vertex3v(&v3);\n}\n\nvoid gs_vertex3f(float x, float y, float z)\n{\n\tstruct vec3 v3;\n\tvec3_set(&v3, x, y, z);\n\tgs_vertex3v(&v3);\n}\n\nvoid gs_normal3f(float x, float y, float z)\n{\n\tstruct vec3 v3;\n\tvec3_set(&v3, x, y, z);\n\tgs_normal3v(&v3);\n}\n\nstatic inline bool validvertsize(graphics_t *graphics, size_t num, const char *name)\n{\n\tif (graphics->using_immediate && num == IMMEDIATE_COUNT) {\n\t\tblog(LOG_ERROR,\n\t\t     \"%s: tried to use over %u \"\n\t\t     \"for immediate rendering\",\n\t\t     name, IMMEDIATE_COUNT);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid gs_color(uint32_t color)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_color\"))\n\t\treturn;\n\tif (!validvertsize(graphics, graphics->colors.num, \"gs_color\"))\n\t\treturn;\n\n\tda_push_back(graphics->colors, &color);\n}\n\nvoid gs_texcoord(float x, float y, int unit)\n{\n\tstruct vec2 v2;\n\tvec2_set(&v2, x, y);\n\tgs_texcoord2v(&v2, unit);\n}\n\nvoid gs_vertex2v(const struct vec2 *v)\n{\n\tstruct vec3 v3;\n\tvec3_set(&v3, v->x, v->y, 0.0f);\n\tgs_vertex3v(&v3);\n}\n\nvoid gs_vertex3v(const struct vec3 *v)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_vertex3v\"))\n\t\treturn;\n\tif (!validvertsize(graphics, graphics->verts.num, \"gs_vertex\"))\n\t\treturn;\n\n\tda_push_back(graphics->verts, v);\n}\n\nvoid gs_normal3v(const struct vec3 *v)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_normal3v\"))\n\t\treturn;\n\tif (!validvertsize(graphics, graphics->norms.num, \"gs_normal\"))\n\t\treturn;\n\n\tda_push_back(graphics->norms, v);\n}\n\nvoid gs_color4v(const struct vec4 *v)\n{\n\t/* TODO */\n\tUNUSED_PARAMETER(v);\n}\n\nvoid gs_texcoord2v(const struct vec2 *v, int unit)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_texcoord2v\"))\n\t\treturn;\n\tif (!validvertsize(graphics, graphics->texverts[unit].num, \"gs_texcoord\"))\n\t\treturn;\n\n\tda_push_back(graphics->texverts[unit], v);\n}\n\ninput_t *gs_get_input(void)\n{\n\t/* TODO */\n\treturn NULL;\n}\n\ngs_effect_t *gs_get_effect(void)\n{\n\tif (!gs_valid(\"gs_get_effect\"))\n\t\treturn NULL;\n\n\treturn thread_graphics ? thread_graphics->cur_effect : NULL;\n}\n\nstatic inline struct gs_effect *find_cached_effect(const char *filename)\n{\n\tstruct gs_effect *effect = thread_graphics->first_effect;\n\n\twhile (effect) {\n\t\tif (strcmp(effect->effect_path, filename) == 0)\n\t\t\tbreak;\n\t\teffect = effect->next;\n\t}\n\n\treturn effect;\n}\n\ngs_effect_t *gs_effect_create_from_file(const char *file, char **error_string)\n{\n\tchar *file_string;\n\tgs_effect_t *effect = NULL;\n\n\tif (!gs_valid_p(\"gs_effect_create_from_file\", file))\n\t\treturn NULL;\n\n\teffect = find_cached_effect(file);\n\tif (effect)\n\t\treturn effect;\n\n\tfile_string = os_quick_read_utf8_file(file);\n\tif (!file_string) {\n\t\tblog(LOG_ERROR, \"Could not load effect file '%s'\", file);\n\t\treturn NULL;\n\t}\n\n\teffect = gs_effect_create(file_string, file, error_string);\n\tbfree(file_string);\n\n\treturn effect;\n}\n\ngs_effect_t *gs_effect_create(const char *effect_string, const char *filename, char **error_string)\n{\n\tif (!gs_valid_p(\"gs_effect_create\", effect_string))\n\t\treturn NULL;\n\n\tstruct gs_effect *effect = bzalloc(sizeof(struct gs_effect));\n\tstruct effect_parser parser;\n\tbool success;\n\n\teffect->graphics = thread_graphics;\n\teffect->effect_path = bstrdup(filename);\n\n\tep_init(&parser);\n\tsuccess = ep_parse(&parser, effect, effect_string, filename);\n\tif (!success) {\n\t\tif (error_string)\n\t\t\t*error_string = error_data_buildstring(&parser.cfp.error_list);\n\t\tgs_effect_destroy(effect);\n\t\teffect = NULL;\n\t}\n\n\tif (effect) {\n\t\tpthread_mutex_lock(&thread_graphics->effect_mutex);\n\n\t\tif (effect->effect_path) {\n\t\t\teffect->cached = true;\n\t\t\teffect->next = thread_graphics->first_effect;\n\t\t\tthread_graphics->first_effect = effect;\n\t\t}\n\n\t\tpthread_mutex_unlock(&thread_graphics->effect_mutex);\n\t}\n\n\tep_free(&parser);\n\treturn effect;\n}\n\ngs_shader_t *gs_vertexshader_create_from_file(const char *file, char **error_string)\n{\n\tif (!gs_valid_p(\"gs_vertexshader_create_from_file\", file))\n\t\treturn NULL;\n\n\tchar *file_string;\n\tgs_shader_t *shader = NULL;\n\n\tfile_string = os_quick_read_utf8_file(file);\n\tif (!file_string) {\n\t\tblog(LOG_ERROR, \"Could not load vertex shader file '%s'\", file);\n\t\treturn NULL;\n\t}\n\n\tshader = gs_vertexshader_create(file_string, file, error_string);\n\tbfree(file_string);\n\n\treturn shader;\n}\n\ngs_shader_t *gs_pixelshader_create_from_file(const char *file, char **error_string)\n{\n\tchar *file_string;\n\tgs_shader_t *shader = NULL;\n\n\tif (!gs_valid_p(\"gs_pixelshader_create_from_file\", file))\n\t\treturn NULL;\n\n\tfile_string = os_quick_read_utf8_file(file);\n\tif (!file_string) {\n\t\tblog(LOG_ERROR, \"Could not load pixel shader file '%s'\", file);\n\t\treturn NULL;\n\t}\n\n\tshader = gs_pixelshader_create(file_string, file, error_string);\n\tbfree(file_string);\n\n\treturn shader;\n}\n\ngs_texture_t *gs_texture_create_from_file(const char *file)\n{\n\tenum gs_color_format format;\n\tuint32_t cx;\n\tuint32_t cy;\n\tuint8_t *data = gs_create_texture_file_data(file, &format, &cx, &cy);\n\tgs_texture_t *tex = NULL;\n\n\tif (data) {\n\t\ttex = gs_texture_create(cx, cy, format, 1, (const uint8_t **)&data, 0);\n\t\tbfree(data);\n\t}\n\n\treturn tex;\n}\n\nstatic inline void assign_sprite_rect(float *start, float *end, float size, bool flip)\n{\n\tif (!flip) {\n\t\t*start = 0.0f;\n\t\t*end = size;\n\t} else {\n\t\t*start = size;\n\t\t*end = 0.0f;\n\t}\n}\n\nstatic inline void assign_sprite_uv(float *start, float *end, bool flip)\n{\n\tif (!flip) {\n\t\t*start = 0.0f;\n\t\t*end = 1.0f;\n\t} else {\n\t\t*start = 1.0f;\n\t\t*end = 0.0f;\n\t}\n}\n\nstatic void build_sprite(struct gs_vb_data *data, float fcx, float fcy, float start_u, float end_u, float start_v,\n\t\t\t float end_v)\n{\n\tstruct vec2 *tvarray = data->tvarray[0].array;\n\n\tvec3_zero(data->points);\n\tvec3_set(data->points + 1, fcx, 0.0f, 0.0f);\n\tvec3_set(data->points + 2, 0.0f, fcy, 0.0f);\n\tvec3_set(data->points + 3, fcx, fcy, 0.0f);\n\tvec2_set(tvarray, start_u, start_v);\n\tvec2_set(tvarray + 1, end_u, start_v);\n\tvec2_set(tvarray + 2, start_u, end_v);\n\tvec2_set(tvarray + 3, end_u, end_v);\n}\n\nstatic inline void build_sprite_norm(struct gs_vb_data *data, float fcx, float fcy, uint32_t flip)\n{\n\tfloat start_u, end_u;\n\tfloat start_v, end_v;\n\n\tassign_sprite_uv(&start_u, &end_u, (flip & GS_FLIP_U) != 0);\n\tassign_sprite_uv(&start_v, &end_v, (flip & GS_FLIP_V) != 0);\n\tbuild_sprite(data, fcx, fcy, start_u, end_u, start_v, end_v);\n}\n\nstatic inline void build_subsprite_norm(struct gs_vb_data *data, float fsub_x, float fsub_y, float fsub_cx,\n\t\t\t\t\tfloat fsub_cy, float fcx, float fcy, uint32_t flip)\n{\n\tfloat start_u, end_u;\n\tfloat start_v, end_v;\n\n\tif ((flip & GS_FLIP_U) == 0) {\n\t\tstart_u = fsub_x / fcx;\n\t\tend_u = (fsub_x + fsub_cx) / fcx;\n\t} else {\n\t\tstart_u = (fsub_x + fsub_cx) / fcx;\n\t\tend_u = fsub_x / fcx;\n\t}\n\n\tif ((flip & GS_FLIP_V) == 0) {\n\t\tstart_v = fsub_y / fcy;\n\t\tend_v = (fsub_y + fsub_cy) / fcy;\n\t} else {\n\t\tstart_v = (fsub_y + fsub_cy) / fcy;\n\t\tend_v = fsub_y / fcy;\n\t}\n\n\tbuild_sprite(data, fsub_cx, fsub_cy, start_u, end_u, start_v, end_v);\n}\n\nstatic inline void build_sprite_rect(struct gs_vb_data *data, gs_texture_t *tex, float fcx, float fcy, uint32_t flip)\n{\n\tfloat start_u, end_u;\n\tfloat start_v, end_v;\n\tfloat width = (float)gs_texture_get_width(tex);\n\tfloat height = (float)gs_texture_get_height(tex);\n\n\tassign_sprite_rect(&start_u, &end_u, width, (flip & GS_FLIP_U) != 0);\n\tassign_sprite_rect(&start_v, &end_v, height, (flip & GS_FLIP_V) != 0);\n\tbuild_sprite(data, fcx, fcy, start_u, end_u, start_v, end_v);\n}\n\nvoid gs_draw_quadf(gs_texture_t *tex, uint32_t flip, float width, float height)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tfloat fcx, fcy;\n\tstruct gs_vb_data *data;\n\n\tif (tex) {\n\t\tif (gs_get_texture_type(tex) != GS_TEXTURE_2D) {\n\t\t\tblog(LOG_ERROR, \"A sprite must be a 2D texture\");\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tif (width == 0.0f || height == 0.0f) {\n\t\t\tblog(LOG_ERROR, \"A sprite cannot be drawn without \"\n\t\t\t\t\t\"a width/height\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tfcx = width != 0.0f ? width : (float)gs_texture_get_width(tex);\n\tfcy = height != 0.0f ? height : (float)gs_texture_get_height(tex);\n\n\tgs_matrix_push();\n\tgs_matrix_scale3f(fcx, fcy, 1.0f);\n\n\tgs_load_indexbuffer(NULL);\n\n\tif (tex && gs_texture_is_rect(tex)) {\n\t\tdata = gs_vertexbuffer_get_data(graphics->subregion_buffer);\n\t\tbuild_sprite_rect(data, tex, 1.0f, 1.0f, flip);\n\t\tgs_vertexbuffer_flush(graphics->subregion_buffer);\n\t\tgs_load_vertexbuffer(graphics->subregion_buffer);\n\t\tgs_draw(GS_TRISTRIP, 0, 0);\n\t} else {\n\t\tgs_load_vertexbuffer(flip ? graphics->flipped_sprite_buffer : graphics->sprite_buffer);\n\t\tgs_draw(GS_TRISTRIP, 0, 0);\n\t}\n\n\tgs_matrix_pop();\n}\n\nvoid gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width, uint32_t height)\n{\n\tgs_draw_quadf(tex, flip, (float)width, (float)height);\n}\n\nvoid gs_draw_sprite_subregion(gs_texture_t *tex, uint32_t flip, uint32_t sub_x, uint32_t sub_y, uint32_t sub_cx,\n\t\t\t      uint32_t sub_cy)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tuint32_t cx, cy;\n\tfloat fcx, fcy;\n\tstruct gs_vb_data *data;\n\n\tif (tex) {\n\t\tif (gs_get_texture_type(tex) != GS_TEXTURE_2D) {\n\t\t\tblog(LOG_ERROR, \"A sprite must be a 2D texture\");\n\t\t\treturn;\n\t\t}\n\t}\n\n\tcx = gs_texture_get_width(tex);\n\tcy = gs_texture_get_height(tex);\n\n\tif (sub_x == 0 && sub_y == 0 && sub_cx == cx && sub_cy == cy) {\n\t\tgs_draw_sprite(tex, flip, 0, 0);\n\t\treturn;\n\t}\n\n\tfcx = (float)cx;\n\tfcy = (float)cy;\n\n\tdata = gs_vertexbuffer_get_data(graphics->subregion_buffer);\n\tbuild_subsprite_norm(data, (float)sub_x, (float)sub_y, (float)sub_cx, (float)sub_cy, fcx, fcy, flip);\n\n\tgs_vertexbuffer_flush(graphics->subregion_buffer);\n\tgs_load_vertexbuffer(graphics->subregion_buffer);\n\tgs_load_indexbuffer(NULL);\n\n\tgs_draw(GS_TRISTRIP, 0, 0);\n}\n\nvoid gs_draw_cube_backdrop(gs_texture_t *cubetex, const struct quat *rot, float left, float right, float top,\n\t\t\t   float bottom, float znear)\n{\n\t/* TODO */\n\tUNUSED_PARAMETER(cubetex);\n\tUNUSED_PARAMETER(rot);\n\tUNUSED_PARAMETER(left);\n\tUNUSED_PARAMETER(right);\n\tUNUSED_PARAMETER(top);\n\tUNUSED_PARAMETER(bottom);\n\tUNUSED_PARAMETER(znear);\n}\n\nvoid gs_reset_viewport(void)\n{\n\tuint32_t cx, cy;\n\n\tif (!gs_valid(\"gs_reset_viewport\"))\n\t\treturn;\n\n\tgs_get_size(&cx, &cy);\n\tgs_set_viewport(0, 0, (int)cx, (int)cy);\n}\n\nvoid gs_set_2d_mode(void)\n{\n\tuint32_t cx, cy;\n\n\tif (!gs_valid(\"gs_set_2d_mode\"))\n\t\treturn;\n\n\tgs_get_size(&cx, &cy);\n\tgs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -1.0, -1024.0f);\n}\n\nvoid gs_set_3d_mode(double fovy, double znear, double zvar)\n{\n\t/* TODO */\n\tUNUSED_PARAMETER(fovy);\n\tUNUSED_PARAMETER(znear);\n\tUNUSED_PARAMETER(zvar);\n}\n\nvoid gs_viewport_push(void)\n{\n\tif (!gs_valid(\"gs_viewport_push\"))\n\t\treturn;\n\n\tstruct gs_rect *rect = da_push_back_new(thread_graphics->viewport_stack);\n\tgs_get_viewport(rect);\n}\n\nvoid gs_viewport_pop(void)\n{\n\tstruct gs_rect *rect;\n\n\tif (!gs_valid(\"gs_viewport_pop\"))\n\t\treturn;\n\tif (!thread_graphics->viewport_stack.num)\n\t\treturn;\n\n\trect = da_end(thread_graphics->viewport_stack);\n\tgs_set_viewport(rect->x, rect->y, rect->cx, rect->cy);\n\tda_pop_back(thread_graphics->viewport_stack);\n}\n\nvoid gs_texture_set_image(gs_texture_t *tex, const uint8_t *data, uint32_t linesize, bool flip)\n{\n\tuint8_t *ptr;\n\tuint32_t linesize_out;\n\tsize_t row_copy;\n\tsize_t height;\n\n\tif (!gs_valid_p2(\"gs_texture_set_image\", tex, data))\n\t\treturn;\n\n\tif (!gs_texture_map(tex, &ptr, &linesize_out))\n\t\treturn;\n\n\trow_copy = (linesize < linesize_out) ? linesize : linesize_out;\n\n\theight = gs_texture_get_height(tex);\n\n\tif (flip) {\n\t\tuint8_t *const end = ptr + height * linesize_out;\n\t\tdata += (height - 1) * linesize;\n\t\twhile (ptr < end) {\n\t\t\tmemcpy(ptr, data, row_copy);\n\t\t\tptr += linesize_out;\n\t\t\tdata -= linesize;\n\t\t}\n\n\t} else if (linesize == linesize_out) {\n\t\tmemcpy(ptr, data, row_copy * height);\n\n\t} else {\n\t\tuint8_t *const end = ptr + height * linesize_out;\n\t\twhile (ptr < end) {\n\t\t\tmemcpy(ptr, data, row_copy);\n\t\t\tptr += linesize_out;\n\t\t\tdata += linesize;\n\t\t}\n\t}\n\n\tgs_texture_unmap(tex);\n}\n\nvoid gs_cubetexture_set_image(gs_texture_t *cubetex, uint32_t side, const void *data, uint32_t linesize, bool invert)\n{\n\t/* TODO */\n\tUNUSED_PARAMETER(cubetex);\n\tUNUSED_PARAMETER(side);\n\tUNUSED_PARAMETER(data);\n\tUNUSED_PARAMETER(linesize);\n\tUNUSED_PARAMETER(invert);\n}\n\nvoid gs_perspective(float angle, float aspect, float near, float far)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tfloat xmin, xmax, ymin, ymax;\n\n\tif (!gs_valid(\"gs_perspective\"))\n\t\treturn;\n\n\tymax = near * tanf(RAD(angle) * 0.5f);\n\tymin = -ymax;\n\n\txmin = ymin * aspect;\n\txmax = ymax * aspect;\n\n\tgraphics->exports.device_frustum(graphics->device, xmin, xmax, ymin, ymax, near, far);\n}\n\nvoid gs_blend_state_push(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_blend_state_push\"))\n\t\treturn;\n\n\tda_push_back(graphics->blend_state_stack, &graphics->cur_blend_state);\n}\n\nvoid gs_blend_state_pop(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tstruct blend_state *state;\n\n\tif (!gs_valid(\"gs_blend_state_pop\"))\n\t\treturn;\n\n\tstate = da_end(graphics->blend_state_stack);\n\tif (!state)\n\t\treturn;\n\n\tgs_enable_blending(state->enabled);\n\tgs_blend_function_separate(state->src_c, state->dest_c, state->src_a, state->dest_a);\n\tgs_blend_op(state->op);\n\n\tda_pop_back(graphics->blend_state_stack);\n}\n\nvoid gs_reset_blend_state(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_preprocessor_name\"))\n\t\treturn;\n\n\tif (!graphics->cur_blend_state.enabled)\n\t\tgs_enable_blending(true);\n\n\tif (graphics->cur_blend_state.src_c != GS_BLEND_SRCALPHA ||\n\t    graphics->cur_blend_state.dest_c != GS_BLEND_INVSRCALPHA ||\n\t    graphics->cur_blend_state.src_a != GS_BLEND_ONE ||\n\t    graphics->cur_blend_state.dest_a != GS_BLEND_INVSRCALPHA) {\n\t\tgs_blend_function_separate(GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA, GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);\n\t\tgs_blend_op(GS_BLEND_OP_ADD);\n\t}\n}\n\n/* ------------------------------------------------------------------------- */\n\nconst char *gs_preprocessor_name(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_preprocessor_name\"))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_preprocessor_name();\n}\n\ngs_swapchain_t *gs_swapchain_create(const struct gs_init_data *data)\n{\n\tstruct gs_init_data new_data = *data;\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_swapchain_create\", data))\n\t\treturn NULL;\n\n\tif (new_data.num_backbuffers == 0)\n\t\tnew_data.num_backbuffers = 1;\n\n\treturn graphics->exports.device_swapchain_create(graphics->device, &new_data);\n}\n\nvoid gs_resize(uint32_t x, uint32_t y)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_resize\"))\n\t\treturn;\n\n\tgraphics->exports.device_resize(graphics->device, x, y);\n}\n\nvoid gs_update_color_space(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_update_color_space\"))\n\t\treturn;\n\n\tgraphics->exports.device_update_color_space(graphics->device);\n}\n\nvoid gs_get_size(uint32_t *x, uint32_t *y)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_size\"))\n\t\treturn;\n\n\tgraphics->exports.device_get_size(graphics->device, x, y);\n}\n\nuint32_t gs_get_width(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_width\"))\n\t\treturn 0;\n\n\treturn graphics->exports.device_get_width(graphics->device);\n}\n\nuint32_t gs_get_height(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_height\"))\n\t\treturn 0;\n\n\treturn graphics->exports.device_get_height(graphics->device);\n}\n\nstatic inline bool is_pow2(uint32_t size)\n{\n\treturn size >= 2 && (size & (size - 1)) == 0;\n}\n\ngs_texture_t *gs_texture_create(uint32_t width, uint32_t height, enum gs_color_format color_format, uint32_t levels,\n\t\t\t\tconst uint8_t **data, uint32_t flags)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tbool pow2tex = is_pow2(width) && is_pow2(height);\n\tbool uses_mipmaps = (flags & GS_BUILD_MIPMAPS || levels != 1);\n\n\tif (!gs_valid(\"gs_texture_create\"))\n\t\treturn NULL;\n\n\tif (uses_mipmaps && !pow2tex) {\n\t\tblog(LOG_WARNING, \"Cannot use mipmaps with a \"\n\t\t\t\t  \"non-power-of-two texture.  Disabling \"\n\t\t\t\t  \"mipmaps for this texture.\");\n\n\t\tuses_mipmaps = false;\n\t\tflags &= ~GS_BUILD_MIPMAPS;\n\t\tlevels = 1;\n\t}\n\n\tif (uses_mipmaps && flags & GS_RENDER_TARGET) {\n\t\tblog(LOG_WARNING, \"Cannot use mipmaps with render targets.  \"\n\t\t\t\t  \"Disabling mipmaps for this texture.\");\n\t\tflags &= ~GS_BUILD_MIPMAPS;\n\t\tlevels = 1;\n\t}\n\n\treturn graphics->exports.device_texture_create(graphics->device, width, height, color_format, levels, data,\n\t\t\t\t\t\t       flags);\n}\n\n#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)\n\ngs_texture_t *gs_texture_create_from_dmabuf(unsigned int width, unsigned int height, uint32_t drm_format,\n\t\t\t\t\t    enum gs_color_format color_format, uint32_t n_planes, const int *fds,\n\t\t\t\t\t    const uint32_t *strides, const uint32_t *offsets, const uint64_t *modifiers)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_texture_create_from_dmabuf(\n\t\tgraphics->device, width, height, drm_format, color_format, n_planes, fds, strides, offsets, modifiers);\n}\n\nbool gs_query_dmabuf_capabilities(enum gs_dmabuf_flags *dmabuf_flags, uint32_t **drm_formats, size_t *n_formats)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_query_dmabuf_capabilities(graphics->device, dmabuf_flags, drm_formats,\n\t\t\t\t\t\t\t\t  n_formats);\n}\n\nbool gs_query_dmabuf_modifiers_for_format(uint32_t drm_format, uint64_t **modifiers, size_t *n_modifiers)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_query_dmabuf_modifiers_for_format(graphics->device, drm_format, modifiers,\n\t\t\t\t\t\t\t\t\t  n_modifiers);\n}\n\ngs_texture_t *gs_texture_create_from_pixmap(uint32_t width, uint32_t height, enum gs_color_format color_format,\n\t\t\t\t\t    uint32_t target, void *pixmap)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_texture_create_from_pixmap(graphics->device, width, height, color_format,\n\t\t\t\t\t\t\t\t   target, pixmap);\n}\n\nbool gs_query_sync_capabilities(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_query_sync_capabilities(graphics->device);\n}\n\ngs_sync_t *gs_sync_create(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_sync_create(graphics->device);\n}\n\ngs_sync_t *gs_sync_create_from_syncobj_timeline_point(int syncobj_fd, uint64_t timeline_point)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_sync_create_from_syncobj_timeline_point(graphics->device, syncobj_fd,\n\t\t\t\t\t\t\t\t\t\ttimeline_point);\n}\n\nvoid gs_sync_destroy(gs_sync_t *sync)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_sync_destroy(graphics->device, sync);\n}\n\nbool gs_sync_export_syncobj_timeline_point(gs_sync_t *sync, int syncobj_fd, uint64_t timeline_point)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_sync_export_syncobj_timeline_point(graphics->device, sync, syncobj_fd,\n\t\t\t\t\t\t\t\t\t   timeline_point);\n}\n\nbool gs_sync_signal_syncobj_timeline_point(int syncobj_fd, uint64_t timeline_point)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_sync_signal_syncobj_timeline_point(graphics->device, syncobj_fd,\n\t\t\t\t\t\t\t\t\t   timeline_point);\n}\n\nbool gs_sync_wait(gs_sync_t *sync)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\treturn graphics->exports.device_sync_wait(graphics->device, sync);\n}\n\n#endif\n\ngs_texture_t *gs_cubetexture_create(uint32_t size, enum gs_color_format color_format, uint32_t levels,\n\t\t\t\t    const uint8_t **data, uint32_t flags)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tbool pow2tex = is_pow2(size);\n\tbool uses_mipmaps = (flags & GS_BUILD_MIPMAPS || levels != 1);\n\n\tif (!gs_valid(\"gs_cubetexture_create\"))\n\t\treturn NULL;\n\n\tif (uses_mipmaps && !pow2tex) {\n\t\tblog(LOG_WARNING, \"Cannot use mipmaps with a \"\n\t\t\t\t  \"non-power-of-two texture.  Disabling \"\n\t\t\t\t  \"mipmaps for this texture.\");\n\n\t\tuses_mipmaps = false;\n\t\tflags &= ~GS_BUILD_MIPMAPS;\n\t\tlevels = 1;\n\t}\n\n\tif (uses_mipmaps && flags & GS_RENDER_TARGET) {\n\t\tblog(LOG_WARNING, \"Cannot use mipmaps with render targets.  \"\n\t\t\t\t  \"Disabling mipmaps for this texture.\");\n\t\tflags &= ~GS_BUILD_MIPMAPS;\n\t\tlevels = 1;\n\t\tdata = NULL;\n\t}\n\n\treturn graphics->exports.device_cubetexture_create(graphics->device, size, color_format, levels, data, flags);\n}\n\ngs_texture_t *gs_voltexture_create(uint32_t width, uint32_t height, uint32_t depth, enum gs_color_format color_format,\n\t\t\t\t   uint32_t levels, const uint8_t **data, uint32_t flags)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_voltexture_create\"))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_voltexture_create(graphics->device, width, height, depth, color_format, levels,\n\t\t\t\t\t\t\t  data, flags);\n}\n\ngs_zstencil_t *gs_zstencil_create(uint32_t width, uint32_t height, enum gs_zstencil_format format)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_zstencil_create\"))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_zstencil_create(graphics->device, width, height, format);\n}\n\ngs_stagesurf_t *gs_stagesurface_create(uint32_t width, uint32_t height, enum gs_color_format color_format)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_stagesurface_create\"))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_stagesurface_create(graphics->device, width, height, color_format);\n}\n\ngs_samplerstate_t *gs_samplerstate_create(const struct gs_sampler_info *info)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_samplerstate_create\", info))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_samplerstate_create(graphics->device, info);\n}\n\ngs_shader_t *gs_vertexshader_create(const char *shader, const char *file, char **error_string)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_vertexshader_create\", shader))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_vertexshader_create(graphics->device, shader, file, error_string);\n}\n\ngs_shader_t *gs_pixelshader_create(const char *shader, const char *file, char **error_string)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_pixelshader_create\", shader))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_pixelshader_create(graphics->device, shader, file, error_string);\n}\n\ngs_vertbuffer_t *gs_vertexbuffer_create(struct gs_vb_data *data, uint32_t flags)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_vertexbuffer_create\"))\n\t\treturn NULL;\n\n\tif (data && data->num && (flags & GS_DUP_BUFFER) != 0) {\n\t\tstruct gs_vb_data *new_data = gs_vbdata_create();\n\n\t\tnew_data->num = data->num;\n\n#define DUP_VAL(val)                                                                        \\\n\tdo {                                                                                \\\n\t\tif (data->val)                                                              \\\n\t\t\tnew_data->val = bmemdup(data->val, sizeof(*data->val) * data->num); \\\n\t} while (false)\n\n\t\tDUP_VAL(points);\n\t\tDUP_VAL(normals);\n\t\tDUP_VAL(tangents);\n\t\tDUP_VAL(colors);\n#undef DUP_VAL\n\n\t\tif (data->tvarray && data->num_tex) {\n\t\t\tnew_data->num_tex = data->num_tex;\n\t\t\tnew_data->tvarray = bzalloc(sizeof(struct gs_tvertarray) * data->num_tex);\n\n\t\t\tfor (size_t i = 0; i < data->num_tex; i++) {\n\t\t\t\tstruct gs_tvertarray *tv = &data->tvarray[i];\n\t\t\t\tstruct gs_tvertarray *new_tv = &new_data->tvarray[i];\n\t\t\t\tsize_t size = tv->width * sizeof(float);\n\n\t\t\t\tnew_tv->width = tv->width;\n\t\t\t\tnew_tv->array = bmemdup(tv->array, size * data->num);\n\t\t\t}\n\t\t}\n\n\t\tdata = new_data;\n\t}\n\n\treturn graphics->exports.device_vertexbuffer_create(graphics->device, data, flags);\n}\n\ngs_indexbuffer_t *gs_indexbuffer_create(enum gs_index_type type, void *indices, size_t num, uint32_t flags)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_indexbuffer_create\"))\n\t\treturn NULL;\n\n\tif (indices && num && (flags & GS_DUP_BUFFER) != 0) {\n\t\tsize_t size = type == GS_UNSIGNED_SHORT ? 2 : 4;\n\t\tindices = bmemdup(indices, size * num);\n\t}\n\n\treturn graphics->exports.device_indexbuffer_create(graphics->device, type, indices, num, flags);\n}\n\ngs_timer_t *gs_timer_create()\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_timer_create\"))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_timer_create(graphics->device);\n}\n\ngs_timer_range_t *gs_timer_range_create()\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_timer_range_create\"))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_timer_range_create(graphics->device);\n}\n\nenum gs_texture_type gs_get_texture_type(const gs_texture_t *texture)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_get_texture_type\", texture))\n\t\treturn GS_TEXTURE_2D;\n\n\treturn graphics->exports.device_get_texture_type(texture);\n}\n\nvoid gs_load_vertexbuffer(gs_vertbuffer_t *vertbuffer)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_load_vertexbuffer\"))\n\t\treturn;\n\n\tgraphics->exports.device_load_vertexbuffer(graphics->device, vertbuffer);\n}\n\nvoid gs_load_indexbuffer(gs_indexbuffer_t *indexbuffer)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_load_indexbuffer\"))\n\t\treturn;\n\n\tgraphics->exports.device_load_indexbuffer(graphics->device, indexbuffer);\n}\n\nvoid gs_load_texture(gs_texture_t *tex, int unit)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_load_texture\"))\n\t\treturn;\n\n\tgraphics->exports.device_load_texture(graphics->device, tex, unit);\n}\n\nvoid gs_load_samplerstate(gs_samplerstate_t *samplerstate, int unit)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_load_samplerstate\"))\n\t\treturn;\n\n\tgraphics->exports.device_load_samplerstate(graphics->device, samplerstate, unit);\n}\n\nvoid gs_load_vertexshader(gs_shader_t *vertshader)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_load_vertexshader\"))\n\t\treturn;\n\n\tgraphics->exports.device_load_vertexshader(graphics->device, vertshader);\n}\n\nvoid gs_load_pixelshader(gs_shader_t *pixelshader)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_load_pixelshader\"))\n\t\treturn;\n\n\tgraphics->exports.device_load_pixelshader(graphics->device, pixelshader);\n}\n\nvoid gs_load_default_samplerstate(bool b_3d, int unit)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_load_default_samplerstate\"))\n\t\treturn;\n\n\tgraphics->exports.device_load_default_samplerstate(graphics->device, b_3d, unit);\n}\n\ngs_shader_t *gs_get_vertex_shader(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_vertex_shader\"))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_get_vertex_shader(graphics->device);\n}\n\ngs_shader_t *gs_get_pixel_shader(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_pixel_shader\"))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_get_pixel_shader(graphics->device);\n}\n\nenum gs_color_space gs_get_color_space(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_color_space\"))\n\t\treturn GS_CS_SRGB;\n\n\treturn graphics->exports.device_get_color_space(graphics->device);\n}\n\ngs_texture_t *gs_get_render_target(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_render_target\"))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_get_render_target(graphics->device);\n}\n\ngs_zstencil_t *gs_get_zstencil_target(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_zstencil_target\"))\n\t\treturn NULL;\n\n\treturn graphics->exports.device_get_zstencil_target(graphics->device);\n}\n\nvoid gs_set_render_target(gs_texture_t *tex, gs_zstencil_t *zstencil)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_set_render_target\"))\n\t\treturn;\n\n\tgraphics->exports.device_set_render_target(graphics->device, tex, zstencil);\n}\n\nvoid gs_set_render_target_with_color_space(gs_texture_t *tex, gs_zstencil_t *zstencil, enum gs_color_space space)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_set_render_target_with_color_space\"))\n\t\treturn;\n\n\tgraphics->exports.device_set_render_target_with_color_space(graphics->device, tex, zstencil, space);\n}\n\nvoid gs_set_cube_render_target(gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_set_cube_render_target\"))\n\t\treturn;\n\n\tgraphics->exports.device_set_cube_render_target(graphics->device, cubetex, side, zstencil);\n}\n\nvoid gs_enable_framebuffer_srgb(bool enable)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_enable_framebuffer_srgb\"))\n\t\treturn;\n\n\tgraphics->exports.device_enable_framebuffer_srgb(graphics->device, enable);\n}\n\nbool gs_framebuffer_srgb_enabled(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_framebuffer_srgb_enabled\"))\n\t\treturn false;\n\n\treturn graphics->exports.device_framebuffer_srgb_enabled(graphics->device);\n}\n\nbool gs_get_linear_srgb(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_linear_srgb\"))\n\t\treturn false;\n\n\treturn graphics->linear_srgb;\n}\n\nbool gs_set_linear_srgb(bool linear_srgb)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_set_linear_srgb\"))\n\t\treturn false;\n\n\tconst bool previous = graphics->linear_srgb;\n\tgraphics->linear_srgb = linear_srgb;\n\treturn previous;\n}\n\nvoid gs_copy_texture(gs_texture_t *dst, gs_texture_t *src)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p2(\"gs_copy_texture\", dst, src))\n\t\treturn;\n\n\tgraphics->exports.device_copy_texture(graphics->device, dst, src);\n}\n\nvoid gs_copy_texture_region(gs_texture_t *dst, uint32_t dst_x, uint32_t dst_y, gs_texture_t *src, uint32_t src_x,\n\t\t\t    uint32_t src_y, uint32_t src_w, uint32_t src_h)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_copy_texture_region\", dst))\n\t\treturn;\n\n\tgraphics->exports.device_copy_texture_region(graphics->device, dst, dst_x, dst_y, src, src_x, src_y, src_w,\n\t\t\t\t\t\t     src_h);\n}\n\nvoid gs_stage_texture(gs_stagesurf_t *dst, gs_texture_t *src)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_stage_texture\"))\n\t\treturn;\n\n\tgraphics->exports.device_stage_texture(graphics->device, dst, src);\n}\n\nvoid gs_begin_frame(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_begin_frame\"))\n\t\treturn;\n\n\tgraphics->exports.device_begin_frame(graphics->device);\n}\n\nvoid gs_begin_scene(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_begin_scene\"))\n\t\treturn;\n\n\tgraphics->exports.device_begin_scene(graphics->device);\n}\n\nvoid gs_draw(enum gs_draw_mode draw_mode, uint32_t start_vert, uint32_t num_verts)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_draw\"))\n\t\treturn;\n\n\tgraphics->exports.device_draw(graphics->device, draw_mode, start_vert, num_verts);\n}\n\nvoid gs_end_scene(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_end_scene\"))\n\t\treturn;\n\n\tgraphics->exports.device_end_scene(graphics->device);\n}\n\nvoid gs_load_swapchain(gs_swapchain_t *swapchain)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_load_swapchain\"))\n\t\treturn;\n\n\tgraphics->exports.device_load_swapchain(graphics->device, swapchain);\n}\n\nvoid gs_clear(uint32_t clear_flags, const struct vec4 *color, float depth, uint8_t stencil)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_clear\"))\n\t\treturn;\n\n\tgraphics->exports.device_clear(graphics->device, clear_flags, color, depth, stencil);\n}\n\nbool gs_is_present_ready(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_is_present_ready\"))\n\t\treturn false;\n\n\treturn graphics->exports.device_is_present_ready(graphics->device);\n}\n\nvoid gs_present(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_present\"))\n\t\treturn;\n\n\tgraphics->exports.device_present(graphics->device);\n}\n\nvoid gs_flush(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_flush\"))\n\t\treturn;\n\n\tgraphics->exports.device_flush(graphics->device);\n}\n\nvoid gs_set_cull_mode(enum gs_cull_mode mode)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_set_cull_mode\"))\n\t\treturn;\n\n\tgraphics->exports.device_set_cull_mode(graphics->device, mode);\n}\n\nenum gs_cull_mode gs_get_cull_mode(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_get_cull_mode\"))\n\t\treturn GS_NEITHER;\n\n\treturn graphics->exports.device_get_cull_mode(graphics->device);\n}\n\nvoid gs_enable_blending(bool enable)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_enable_blending\"))\n\t\treturn;\n\n\tgraphics->cur_blend_state.enabled = enable;\n\tgraphics->exports.device_enable_blending(graphics->device, enable);\n}\n\nvoid gs_enable_depth_test(bool enable)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_enable_depth_test\"))\n\t\treturn;\n\n\tgraphics->exports.device_enable_depth_test(graphics->device, enable);\n}\n\nvoid gs_enable_stencil_test(bool enable)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_enable_stencil_test\"))\n\t\treturn;\n\n\tgraphics->exports.device_enable_stencil_test(graphics->device, enable);\n}\n\nvoid gs_enable_stencil_write(bool enable)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_enable_stencil_write\"))\n\t\treturn;\n\n\tgraphics->exports.device_enable_stencil_write(graphics->device, enable);\n}\n\nvoid gs_enable_color(bool red, bool green, bool blue, bool alpha)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_enable_color\"))\n\t\treturn;\n\n\tgraphics->exports.device_enable_color(graphics->device, red, green, blue, alpha);\n}\n\nvoid gs_blend_function(enum gs_blend_type src, enum gs_blend_type dest)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_blend_function\"))\n\t\treturn;\n\n\tgraphics->cur_blend_state.src_c = src;\n\tgraphics->cur_blend_state.dest_c = dest;\n\tgraphics->cur_blend_state.src_a = src;\n\tgraphics->cur_blend_state.dest_a = dest;\n\tgraphics->exports.device_blend_function(graphics->device, src, dest);\n}\n\nvoid gs_blend_function_separate(enum gs_blend_type src_c, enum gs_blend_type dest_c, enum gs_blend_type src_a,\n\t\t\t\tenum gs_blend_type dest_a)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_blend_function_separate\"))\n\t\treturn;\n\n\tgraphics->cur_blend_state.src_c = src_c;\n\tgraphics->cur_blend_state.dest_c = dest_c;\n\tgraphics->cur_blend_state.src_a = src_a;\n\tgraphics->cur_blend_state.dest_a = dest_a;\n\tgraphics->exports.device_blend_function_separate(graphics->device, src_c, dest_c, src_a, dest_a);\n}\n\nvoid gs_blend_op(enum gs_blend_op_type op)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_blend_op\"))\n\t\treturn;\n\n\tgraphics->cur_blend_state.op = op;\n\tgraphics->exports.device_blend_op(graphics->device, graphics->cur_blend_state.op);\n}\n\nvoid gs_depth_function(enum gs_depth_test test)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_depth_function\"))\n\t\treturn;\n\n\tgraphics->exports.device_depth_function(graphics->device, test);\n}\n\nvoid gs_stencil_function(enum gs_stencil_side side, enum gs_depth_test test)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_stencil_function\"))\n\t\treturn;\n\n\tgraphics->exports.device_stencil_function(graphics->device, side, test);\n}\n\nvoid gs_stencil_op(enum gs_stencil_side side, enum gs_stencil_op_type fail, enum gs_stencil_op_type zfail,\n\t\t   enum gs_stencil_op_type zpass)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_stencil_op\"))\n\t\treturn;\n\n\tgraphics->exports.device_stencil_op(graphics->device, side, fail, zfail, zpass);\n}\n\nvoid gs_set_viewport(int x, int y, int width, int height)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_set_viewport\"))\n\t\treturn;\n\n\tgraphics->exports.device_set_viewport(graphics->device, x, y, width, height);\n}\n\nvoid gs_get_viewport(struct gs_rect *rect)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_get_viewport\", rect))\n\t\treturn;\n\n\tgraphics->exports.device_get_viewport(graphics->device, rect);\n}\n\nvoid gs_set_scissor_rect(const struct gs_rect *rect)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_set_scissor_rect\"))\n\t\treturn;\n\n\tgraphics->exports.device_set_scissor_rect(graphics->device, rect);\n}\n\nvoid gs_ortho(float left, float right, float top, float bottom, float znear, float zfar)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_ortho\"))\n\t\treturn;\n\n\tgraphics->exports.device_ortho(graphics->device, left, right, top, bottom, znear, zfar);\n}\n\nvoid gs_frustum(float left, float right, float top, float bottom, float znear, float zfar)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_frustum\"))\n\t\treturn;\n\n\tgraphics->exports.device_frustum(graphics->device, left, right, top, bottom, znear, zfar);\n}\n\nvoid gs_projection_push(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_projection_push\"))\n\t\treturn;\n\n\tgraphics->exports.device_projection_push(graphics->device);\n}\n\nvoid gs_projection_pop(void)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_projection_pop\"))\n\t\treturn;\n\n\tgraphics->exports.device_projection_pop(graphics->device);\n}\n\nvoid gs_swapchain_destroy(gs_swapchain_t *swapchain)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_swapchain_destroy\"))\n\t\treturn;\n\tif (!swapchain)\n\t\treturn;\n\n\tgraphics->exports.gs_swapchain_destroy(swapchain);\n}\n\nvoid gs_shader_destroy(gs_shader_t *shader)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_shader_destroy\"))\n\t\treturn;\n\tif (!shader)\n\t\treturn;\n\n\tgraphics->exports.gs_shader_destroy(shader);\n}\n\nint gs_shader_get_num_params(const gs_shader_t *shader)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_shader_get_num_params\", shader))\n\t\treturn 0;\n\n\treturn graphics->exports.gs_shader_get_num_params(shader);\n}\n\ngs_sparam_t *gs_shader_get_param_by_idx(gs_shader_t *shader, uint32_t param)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_shader_get_param_by_idx\", shader))\n\t\treturn NULL;\n\n\treturn graphics->exports.gs_shader_get_param_by_idx(shader, param);\n}\n\ngs_sparam_t *gs_shader_get_param_by_name(gs_shader_t *shader, const char *name)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p2(\"gs_shader_get_param_by_name\", shader, name))\n\t\treturn NULL;\n\n\treturn graphics->exports.gs_shader_get_param_by_name(shader, name);\n}\n\ngs_sparam_t *gs_shader_get_viewproj_matrix(const gs_shader_t *shader)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_shader_get_viewproj_matrix\", shader))\n\t\treturn NULL;\n\n\treturn graphics->exports.gs_shader_get_viewproj_matrix(shader);\n}\n\ngs_sparam_t *gs_shader_get_world_matrix(const gs_shader_t *shader)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_shader_get_world_matrix\", shader))\n\t\treturn NULL;\n\n\treturn graphics->exports.gs_shader_get_world_matrix(shader);\n}\n\nvoid gs_shader_get_param_info(const gs_sparam_t *param, struct gs_shader_param_info *info)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p2(\"gs_shader_get_param_info\", param, info))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_get_param_info(param, info);\n}\n\nvoid gs_shader_set_bool(gs_sparam_t *param, bool val)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_shader_set_bool\", param))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_bool(param, val);\n}\n\nvoid gs_shader_set_float(gs_sparam_t *param, float val)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_shader_set_float\", param))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_float(param, val);\n}\n\nvoid gs_shader_set_int(gs_sparam_t *param, int val)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_shader_set_int\", param))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_int(param, val);\n}\n\nvoid gs_shader_set_matrix3(gs_sparam_t *param, const struct matrix3 *val)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p2(\"gs_shader_set_matrix3\", param, val))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_matrix3(param, val);\n}\n\nvoid gs_shader_set_matrix4(gs_sparam_t *param, const struct matrix4 *val)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p2(\"gs_shader_set_matrix4\", param, val))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_matrix4(param, val);\n}\n\nvoid gs_shader_set_vec2(gs_sparam_t *param, const struct vec2 *val)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p2(\"gs_shader_set_vec2\", param, val))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_vec2(param, val);\n}\n\nvoid gs_shader_set_vec3(gs_sparam_t *param, const struct vec3 *val)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p2(\"gs_shader_set_vec3\", param, val))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_vec3(param, val);\n}\n\nvoid gs_shader_set_vec4(gs_sparam_t *param, const struct vec4 *val)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p2(\"gs_shader_set_vec4\", param, val))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_vec4(param, val);\n}\n\nvoid gs_shader_set_texture(gs_sparam_t *param, gs_texture_t *val)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_shader_set_texture\", param))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_texture(param, val);\n}\n\nvoid gs_shader_set_val(gs_sparam_t *param, const void *val, size_t size)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p2(\"gs_shader_set_val\", param, val))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_val(param, val, size);\n}\n\nvoid gs_shader_set_default(gs_sparam_t *param)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_shader_set_default\", param))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_default(param);\n}\n\nvoid gs_shader_set_next_sampler(gs_sparam_t *param, gs_samplerstate_t *sampler)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_shader_set_next_sampler\", param))\n\t\treturn;\n\n\tgraphics->exports.gs_shader_set_next_sampler(param, sampler);\n}\n\nvoid gs_texture_destroy(gs_texture_t *tex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_texture_destroy\"))\n\t\treturn;\n\tif (!tex)\n\t\treturn;\n\n\tgraphics->exports.gs_texture_destroy(tex);\n}\n\nuint32_t gs_texture_get_width(const gs_texture_t *tex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_texture_get_width\", tex))\n\t\treturn 0;\n\n\treturn graphics->exports.gs_texture_get_width(tex);\n}\n\nuint32_t gs_texture_get_height(const gs_texture_t *tex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_texture_get_height\", tex))\n\t\treturn 0;\n\n\treturn graphics->exports.gs_texture_get_height(tex);\n}\n\nenum gs_color_format gs_texture_get_color_format(const gs_texture_t *tex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_texture_get_color_format\", tex))\n\t\treturn GS_UNKNOWN;\n\n\treturn graphics->exports.gs_texture_get_color_format(tex);\n}\n\nbool gs_texture_map(gs_texture_t *tex, uint8_t **ptr, uint32_t *linesize)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p3(\"gs_texture_map\", tex, ptr, linesize))\n\t\treturn false;\n\n\treturn graphics->exports.gs_texture_map(tex, ptr, linesize);\n}\n\nvoid gs_texture_unmap(gs_texture_t *tex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_texture_unmap\", tex))\n\t\treturn;\n\n\tgraphics->exports.gs_texture_unmap(tex);\n}\n\nbool gs_texture_is_rect(const gs_texture_t *tex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_texture_is_rect\", tex))\n\t\treturn false;\n\n\tif (graphics->exports.gs_texture_is_rect)\n\t\treturn graphics->exports.gs_texture_is_rect(tex);\n\telse\n\t\treturn false;\n}\n\nvoid *gs_texture_get_obj(gs_texture_t *tex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_texture_get_obj\", tex))\n\t\treturn NULL;\n\n\treturn graphics->exports.gs_texture_get_obj(tex);\n}\n\nvoid gs_cubetexture_destroy(gs_texture_t *cubetex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_cubetexture_destroy\"))\n\t\treturn;\n\tif (!cubetex)\n\t\treturn;\n\n\tgraphics->exports.gs_cubetexture_destroy(cubetex);\n}\n\nuint32_t gs_cubetexture_get_size(const gs_texture_t *cubetex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_cubetexture_get_size\", cubetex))\n\t\treturn 0;\n\n\treturn graphics->exports.gs_cubetexture_get_size(cubetex);\n}\n\nenum gs_color_format gs_cubetexture_get_color_format(const gs_texture_t *cubetex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_cubetexture_get_color_format\", cubetex))\n\t\treturn GS_UNKNOWN;\n\n\treturn graphics->exports.gs_cubetexture_get_color_format(cubetex);\n}\n\nvoid gs_voltexture_destroy(gs_texture_t *voltex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_voltexture_destroy\"))\n\t\treturn;\n\tif (!voltex)\n\t\treturn;\n\n\tgraphics->exports.gs_voltexture_destroy(voltex);\n}\n\nuint32_t gs_voltexture_get_width(const gs_texture_t *voltex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_voltexture_get_width\", voltex))\n\t\treturn 0;\n\n\treturn graphics->exports.gs_voltexture_get_width(voltex);\n}\n\nuint32_t gs_voltexture_get_height(const gs_texture_t *voltex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_voltexture_get_height\", voltex))\n\t\treturn 0;\n\n\treturn graphics->exports.gs_voltexture_get_height(voltex);\n}\n\nuint32_t gs_voltexture_get_depth(const gs_texture_t *voltex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_voltexture_get_depth\", voltex))\n\t\treturn 0;\n\n\treturn graphics->exports.gs_voltexture_get_depth(voltex);\n}\n\nenum gs_color_format gs_voltexture_get_color_format(const gs_texture_t *voltex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_voltexture_get_color_format\", voltex))\n\t\treturn GS_UNKNOWN;\n\n\treturn graphics->exports.gs_voltexture_get_color_format(voltex);\n}\n\nvoid gs_stagesurface_destroy(gs_stagesurf_t *stagesurf)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_stagesurface_destroy\"))\n\t\treturn;\n\tif (!stagesurf)\n\t\treturn;\n\n\tgraphics->exports.gs_stagesurface_destroy(stagesurf);\n}\n\nuint32_t gs_stagesurface_get_width(const gs_stagesurf_t *stagesurf)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_stagesurface_get_width\", stagesurf))\n\t\treturn 0;\n\n\treturn graphics->exports.gs_stagesurface_get_width(stagesurf);\n}\n\nuint32_t gs_stagesurface_get_height(const gs_stagesurf_t *stagesurf)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_stagesurface_get_height\", stagesurf))\n\t\treturn 0;\n\n\treturn graphics->exports.gs_stagesurface_get_height(stagesurf);\n}\n\nenum gs_color_format gs_stagesurface_get_color_format(const gs_stagesurf_t *stagesurf)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_stagesurface_get_color_format\", stagesurf))\n\t\treturn GS_UNKNOWN;\n\n\treturn graphics->exports.gs_stagesurface_get_color_format(stagesurf);\n}\n\nbool gs_stagesurface_map(gs_stagesurf_t *stagesurf, uint8_t **data, uint32_t *linesize)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p3(\"gs_stagesurface_map\", stagesurf, data, linesize))\n\t\treturn 0;\n\n\treturn graphics->exports.gs_stagesurface_map(stagesurf, data, linesize);\n}\n\nvoid gs_stagesurface_unmap(gs_stagesurf_t *stagesurf)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_stagesurface_unmap\", stagesurf))\n\t\treturn;\n\n\tgraphics->exports.gs_stagesurface_unmap(stagesurf);\n}\n\nvoid gs_zstencil_destroy(gs_zstencil_t *zstencil)\n{\n\tif (!gs_valid(\"gs_zstencil_destroy\"))\n\t\treturn;\n\tif (!zstencil)\n\t\treturn;\n\n\tthread_graphics->exports.gs_zstencil_destroy(zstencil);\n}\n\nvoid gs_samplerstate_destroy(gs_samplerstate_t *samplerstate)\n{\n\tif (!gs_valid(\"gs_samplerstate_destroy\"))\n\t\treturn;\n\tif (!samplerstate)\n\t\treturn;\n\n\tthread_graphics->exports.gs_samplerstate_destroy(samplerstate);\n}\n\nvoid gs_vertexbuffer_destroy(gs_vertbuffer_t *vertbuffer)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_vertexbuffer_destroy\"))\n\t\treturn;\n\tif (!vertbuffer)\n\t\treturn;\n\n\tgraphics->exports.gs_vertexbuffer_destroy(vertbuffer);\n}\n\nvoid gs_vertexbuffer_flush(gs_vertbuffer_t *vertbuffer)\n{\n\tif (!gs_valid_p(\"gs_vertexbuffer_flush\", vertbuffer))\n\t\treturn;\n\n\tthread_graphics->exports.gs_vertexbuffer_flush(vertbuffer);\n}\n\nvoid gs_vertexbuffer_flush_direct(gs_vertbuffer_t *vertbuffer, const struct gs_vb_data *data)\n{\n\tif (!gs_valid_p2(\"gs_vertexbuffer_flush_direct\", vertbuffer, data))\n\t\treturn;\n\n\tthread_graphics->exports.gs_vertexbuffer_flush_direct(vertbuffer, data);\n}\n\nstruct gs_vb_data *gs_vertexbuffer_get_data(const gs_vertbuffer_t *vertbuffer)\n{\n\tif (!gs_valid_p(\"gs_vertexbuffer_get_data\", vertbuffer))\n\t\treturn NULL;\n\n\treturn thread_graphics->exports.gs_vertexbuffer_get_data(vertbuffer);\n}\n\nvoid gs_indexbuffer_destroy(gs_indexbuffer_t *indexbuffer)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_indexbuffer_destroy\"))\n\t\treturn;\n\tif (!indexbuffer)\n\t\treturn;\n\n\tgraphics->exports.gs_indexbuffer_destroy(indexbuffer);\n}\n\nvoid gs_indexbuffer_flush(gs_indexbuffer_t *indexbuffer)\n{\n\tif (!gs_valid_p(\"gs_indexbuffer_flush\", indexbuffer))\n\t\treturn;\n\n\tthread_graphics->exports.gs_indexbuffer_flush(indexbuffer);\n}\n\nvoid gs_indexbuffer_flush_direct(gs_indexbuffer_t *indexbuffer, const void *data)\n{\n\tif (!gs_valid_p2(\"gs_indexbuffer_flush_direct\", indexbuffer, data))\n\t\treturn;\n\n\tthread_graphics->exports.gs_indexbuffer_flush_direct(indexbuffer, data);\n}\n\nvoid *gs_indexbuffer_get_data(const gs_indexbuffer_t *indexbuffer)\n{\n\tif (!gs_valid_p(\"gs_indexbuffer_get_data\", indexbuffer))\n\t\treturn NULL;\n\n\treturn thread_graphics->exports.gs_indexbuffer_get_data(indexbuffer);\n}\n\nsize_t gs_indexbuffer_get_num_indices(const gs_indexbuffer_t *indexbuffer)\n{\n\tif (!gs_valid_p(\"gs_indexbuffer_get_num_indices\", indexbuffer))\n\t\treturn 0;\n\n\treturn thread_graphics->exports.gs_indexbuffer_get_num_indices(indexbuffer);\n}\n\nenum gs_index_type gs_indexbuffer_get_type(const gs_indexbuffer_t *indexbuffer)\n{\n\tif (!gs_valid_p(\"gs_indexbuffer_get_type\", indexbuffer))\n\t\treturn (enum gs_index_type)0;\n\n\treturn thread_graphics->exports.gs_indexbuffer_get_type(indexbuffer);\n}\n\nvoid gs_timer_destroy(gs_timer_t *timer)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_timer_destroy\"))\n\t\treturn;\n\tif (!timer)\n\t\treturn;\n\n\tgraphics->exports.gs_timer_destroy(timer);\n}\n\nvoid gs_timer_begin(gs_timer_t *timer)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_timer_begin\"))\n\t\treturn;\n\tif (!timer)\n\t\treturn;\n\n\tgraphics->exports.gs_timer_begin(timer);\n}\n\nvoid gs_timer_end(gs_timer_t *timer)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_timer_end\"))\n\t\treturn;\n\tif (!timer)\n\t\treturn;\n\n\tgraphics->exports.gs_timer_end(timer);\n}\n\nbool gs_timer_get_data(gs_timer_t *timer, uint64_t *ticks)\n{\n\tif (!gs_valid_p2(\"gs_timer_get_data\", timer, ticks))\n\t\treturn false;\n\n\treturn thread_graphics->exports.gs_timer_get_data(timer, ticks);\n}\n\nvoid gs_timer_range_destroy(gs_timer_range_t *range)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_timer_range_destroy\"))\n\t\treturn;\n\tif (!range)\n\t\treturn;\n\n\tgraphics->exports.gs_timer_range_destroy(range);\n}\n\nvoid gs_timer_range_begin(gs_timer_range_t *range)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_timer_range_begin\"))\n\t\treturn;\n\tif (!range)\n\t\treturn;\n\n\tgraphics->exports.gs_timer_range_begin(range);\n}\n\nvoid gs_timer_range_end(gs_timer_range_t *range)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_timer_range_end\"))\n\t\treturn;\n\tif (!range)\n\t\treturn;\n\n\tgraphics->exports.gs_timer_range_end(range);\n}\n\nbool gs_timer_range_get_data(gs_timer_range_t *range, bool *disjoint, uint64_t *frequency)\n{\n\tif (!gs_valid_p2(\"gs_timer_range_get_data\", disjoint, frequency))\n\t\treturn false;\n\n\treturn thread_graphics->exports.gs_timer_range_get_data(range, disjoint, frequency);\n}\n\nbool gs_nv12_available(void)\n{\n\tif (!gs_valid(\"gs_nv12_available\"))\n\t\treturn false;\n\n\tif (!thread_graphics->exports.device_nv12_available)\n\t\treturn false;\n\n\treturn thread_graphics->exports.device_nv12_available(thread_graphics->device);\n}\n\nbool gs_p010_available(void)\n{\n\tif (!gs_valid(\"gs_p010_available\"))\n\t\treturn false;\n\n\tif (!thread_graphics->exports.device_p010_available)\n\t\treturn false;\n\n\treturn thread_graphics->exports.device_p010_available(thread_graphics->device);\n}\n\nbool gs_is_monitor_hdr(void *monitor)\n{\n\tif (!gs_valid(\"gs_is_monitor_hdr\"))\n\t\treturn false;\n\n\treturn thread_graphics->exports.device_is_monitor_hdr(thread_graphics->device, monitor);\n}\n\nvoid gs_debug_marker_begin(const float color[4], const char *markername)\n{\n\tif (!gs_valid(\"gs_debug_marker_begin\"))\n\t\treturn;\n\n\tif (!markername)\n\t\tmarkername = \"(null)\";\n\n\tthread_graphics->exports.device_debug_marker_begin(thread_graphics->device, markername, color);\n}\n\nvoid gs_debug_marker_begin_format(const float color[4], const char *format, ...)\n{\n\tif (!gs_valid(\"gs_debug_marker_begin\"))\n\t\treturn;\n\n\tif (format) {\n\t\tchar markername[64];\n\t\tva_list args;\n\t\tva_start(args, format);\n\t\tvsnprintf(markername, sizeof(markername), format, args);\n\t\tva_end(args);\n\t\tthread_graphics->exports.device_debug_marker_begin(thread_graphics->device, markername, color);\n\t} else {\n\t\tgs_debug_marker_begin(color, NULL);\n\t}\n}\n\nvoid gs_debug_marker_end(void)\n{\n\tif (!gs_valid(\"gs_debug_marker_end\"))\n\t\treturn;\n\n\tthread_graphics->exports.device_debug_marker_end(thread_graphics->device);\n}\n\nbool gs_texture_create_nv12(gs_texture_t **tex_y, gs_texture_t **tex_uv, uint32_t width, uint32_t height,\n\t\t\t    uint32_t flags)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tbool success = false;\n\n\tif (!gs_valid(\"gs_texture_create_nv12\"))\n\t\treturn false;\n\n\tif ((width & 1) == 1 || (height & 1) == 1) {\n\t\tblog(LOG_ERROR, \"NV12 textures must have dimensions \"\n\t\t\t\t\"divisible by 2.\");\n\t\treturn false;\n\t}\n\n\tif (graphics->exports.device_texture_create_nv12) {\n\t\tsuccess = graphics->exports.device_texture_create_nv12(graphics->device, tex_y, tex_uv, width, height,\n\t\t\t\t\t\t\t\t       flags);\n\t\tif (success)\n\t\t\treturn true;\n\t}\n\n\t*tex_y = gs_texture_create(width, height, GS_R8, 1, NULL, flags);\n\t*tex_uv = gs_texture_create(width / 2, height / 2, GS_R8G8, 1, NULL, flags);\n\n\tif (!*tex_y || !*tex_uv) {\n\t\tif (*tex_y)\n\t\t\tgs_texture_destroy(*tex_y);\n\t\tif (*tex_uv)\n\t\t\tgs_texture_destroy(*tex_uv);\n\t\t*tex_y = NULL;\n\t\t*tex_uv = NULL;\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nbool gs_texture_create_p010(gs_texture_t **tex_y, gs_texture_t **tex_uv, uint32_t width, uint32_t height,\n\t\t\t    uint32_t flags)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tbool success = false;\n\n\tif (!gs_valid(\"gs_texture_create_p010\"))\n\t\treturn false;\n\n\tif ((width & 1) == 1 || (height & 1) == 1) {\n\t\tblog(LOG_ERROR, \"P010 textures must have dimensions \"\n\t\t\t\t\"divisible by 2.\");\n\t\treturn false;\n\t}\n\n\tif (graphics->exports.device_texture_create_p010) {\n\t\tsuccess = graphics->exports.device_texture_create_p010(graphics->device, tex_y, tex_uv, width, height,\n\t\t\t\t\t\t\t\t       flags);\n\t\tif (success)\n\t\t\treturn true;\n\t}\n\n\t*tex_y = gs_texture_create(width, height, GS_R16, 1, NULL, flags);\n\t*tex_uv = gs_texture_create(width / 2, height / 2, GS_RG16, 1, NULL, flags);\n\n\tif (!*tex_y || !*tex_uv) {\n\t\tif (*tex_y)\n\t\t\tgs_texture_destroy(*tex_y);\n\t\tif (*tex_uv)\n\t\t\tgs_texture_destroy(*tex_uv);\n\t\t*tex_y = NULL;\n\t\t*tex_uv = NULL;\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nuint32_t gs_get_adapter_count(void)\n{\n\tif (!gs_valid(\"gs_get_adapter_count\"))\n\t\treturn 0;\n\tif (!thread_graphics->exports.gs_get_adapter_count)\n\t\treturn 0;\n\n\treturn thread_graphics->exports.gs_get_adapter_count();\n}\n\n#ifdef __APPLE__\n\n/** Platform specific functions */\ngs_texture_t *gs_texture_create_from_iosurface(void *iosurf)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_texture_create_from_iosurface\", iosurf))\n\t\treturn NULL;\n\tif (!graphics->exports.device_texture_create_from_iosurface)\n\t\treturn NULL;\n\n\treturn graphics->exports.device_texture_create_from_iosurface(graphics->device, iosurf);\n}\n\nbool gs_texture_rebind_iosurface(gs_texture_t *texture, void *iosurf)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid_p(\"gs_texture_rebind_iosurface\", texture))\n\t\treturn false;\n\tif (!graphics->exports.gs_texture_rebind_iosurface)\n\t\treturn false;\n\n\treturn graphics->exports.gs_texture_rebind_iosurface(texture, iosurf);\n}\n\nbool gs_shared_texture_available(void)\n{\n\tif (!gs_valid(\"gs_shared_texture_available\"))\n\t\treturn false;\n\n\treturn thread_graphics->exports.device_shared_texture_available();\n}\n\ngs_texture_t *gs_texture_open_shared(uint32_t handle)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tif (!gs_valid(\"gs_texture_open_shared\"))\n\t\treturn NULL;\n\n\tif (graphics->exports.device_texture_open_shared)\n\t\treturn graphics->exports.device_texture_open_shared(graphics->device, handle);\n\treturn NULL;\n}\n\n#elif _WIN32\n\nbool gs_gdi_texture_available(void)\n{\n\tif (!gs_valid(\"gs_gdi_texture_available\"))\n\t\treturn false;\n\n\treturn thread_graphics->exports.device_gdi_texture_available();\n}\n\nbool gs_shared_texture_available(void)\n{\n\tif (!gs_valid(\"gs_shared_texture_available\"))\n\t\treturn false;\n\n\treturn thread_graphics->exports.device_shared_texture_available();\n}\n\nbool gs_get_duplicator_monitor_info(int monitor_idx, struct gs_monitor_info *monitor_info)\n{\n\tif (!gs_valid_p(\"gs_get_duplicator_monitor_info\", monitor_info))\n\t\treturn false;\n\tif (!thread_graphics->exports.device_get_duplicator_monitor_info)\n\t\treturn false;\n\n\treturn thread_graphics->exports.device_get_duplicator_monitor_info(thread_graphics->device, monitor_idx,\n\t\t\t\t\t\t\t\t\t   monitor_info);\n}\n\nint gs_duplicator_get_monitor_index(void *monitor)\n{\n\tif (!gs_valid(\"gs_duplicator_get_monitor_index\"))\n\t\treturn false;\n\tif (!thread_graphics->exports.device_duplicator_get_monitor_index)\n\t\treturn false;\n\n\treturn thread_graphics->exports.device_duplicator_get_monitor_index(thread_graphics->device, monitor);\n}\n\ngs_duplicator_t *gs_duplicator_create(int monitor_idx)\n{\n\tif (!gs_valid(\"gs_duplicator_create\"))\n\t\treturn NULL;\n\tif (!thread_graphics->exports.device_duplicator_create)\n\t\treturn NULL;\n\n\treturn thread_graphics->exports.device_duplicator_create(thread_graphics->device, monitor_idx);\n}\n\nvoid gs_duplicator_destroy(gs_duplicator_t *duplicator)\n{\n\tif (!gs_valid(\"gs_duplicator_destroy\"))\n\t\treturn;\n\tif (!duplicator)\n\t\treturn;\n\tif (!thread_graphics->exports.gs_duplicator_destroy)\n\t\treturn;\n\n\tthread_graphics->exports.gs_duplicator_destroy(duplicator);\n}\n\nbool gs_duplicator_update_frame(gs_duplicator_t *duplicator)\n{\n\tif (!gs_valid_p(\"gs_duplicator_update_frame\", duplicator))\n\t\treturn false;\n\tif (!thread_graphics->exports.gs_duplicator_update_frame)\n\t\treturn false;\n\n\treturn thread_graphics->exports.gs_duplicator_update_frame(duplicator);\n}\n\nbool gs_can_adapter_fast_clear(void)\n{\n\tif (!gs_valid(\"gs_can_adapter_fast_clear\"))\n\t\treturn false;\n\tif (!thread_graphics->exports.device_can_adapter_fast_clear)\n\t\treturn false;\n\n\treturn thread_graphics->exports.device_can_adapter_fast_clear(thread_graphics->device);\n}\n\ngs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator)\n{\n\tif (!gs_valid_p(\"gs_duplicator_get_texture\", duplicator))\n\t\treturn NULL;\n\tif (!thread_graphics->exports.gs_duplicator_get_texture)\n\t\treturn NULL;\n\n\treturn thread_graphics->exports.gs_duplicator_get_texture(duplicator);\n}\n\nenum gs_color_space gs_duplicator_get_color_space(gs_duplicator_t *duplicator)\n{\n\tif (!gs_valid_p(\"gs_duplicator_get_color_space\", duplicator))\n\t\treturn GS_CS_SRGB;\n\tif (!thread_graphics->exports.gs_duplicator_get_color_space)\n\t\treturn GS_CS_SRGB;\n\n\treturn thread_graphics->exports.gs_duplicator_get_color_space(duplicator);\n}\n\nfloat gs_duplicator_get_sdr_white_level(gs_duplicator_t *duplicator)\n{\n\tif (!gs_valid_p(\"gs_duplicator_get_sdr_white_level\", duplicator))\n\t\treturn 80.f;\n\tif (!thread_graphics->exports.gs_duplicator_get_sdr_white_level)\n\t\treturn 80.f;\n\n\treturn thread_graphics->exports.gs_duplicator_get_sdr_white_level(duplicator);\n}\n\n/** creates a windows GDI-lockable texture */\ngs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_texture_create_gdi\"))\n\t\treturn NULL;\n\n\tif (graphics->exports.device_texture_create_gdi)\n\t\treturn graphics->exports.device_texture_create_gdi(graphics->device, width, height);\n\treturn NULL;\n}\n\nvoid *gs_texture_get_dc(gs_texture_t *gdi_tex)\n{\n\tif (!gs_valid_p(\"gs_texture_release_dc\", gdi_tex))\n\t\treturn NULL;\n\n\tif (thread_graphics->exports.gs_texture_get_dc)\n\t\treturn thread_graphics->exports.gs_texture_get_dc(gdi_tex);\n\treturn NULL;\n}\n\nvoid gs_texture_release_dc(gs_texture_t *gdi_tex)\n{\n\tif (!gs_valid_p(\"gs_texture_release_dc\", gdi_tex))\n\t\treturn;\n\n\tif (thread_graphics->exports.gs_texture_release_dc)\n\t\tthread_graphics->exports.gs_texture_release_dc(gdi_tex);\n}\n\ngs_texture_t *gs_texture_open_shared(uint32_t handle)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tif (!gs_valid(\"gs_texture_open_shared\"))\n\t\treturn NULL;\n\n\tif (graphics->exports.device_texture_open_shared)\n\t\treturn graphics->exports.device_texture_open_shared(graphics->device, handle);\n\treturn NULL;\n}\n\ngs_texture_t *gs_texture_open_nt_shared(uint32_t handle)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tif (!gs_valid(\"gs_texture_open_nt_shared\"))\n\t\treturn NULL;\n\n\tif (graphics->exports.device_texture_open_nt_shared)\n\t\treturn graphics->exports.device_texture_open_nt_shared(graphics->device, handle);\n\treturn NULL;\n}\n\nuint32_t gs_texture_get_shared_handle(gs_texture_t *tex)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tif (!gs_valid(\"gs_texture_get_shared_handle\"))\n\t\treturn GS_INVALID_HANDLE;\n\n\tif (graphics->exports.device_texture_get_shared_handle)\n\t\treturn graphics->exports.device_texture_get_shared_handle(tex);\n\treturn GS_INVALID_HANDLE;\n}\n\ngs_texture_t *gs_texture_wrap_obj(void *obj)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tif (!gs_valid(\"gs_texture_wrap_obj\"))\n\t\treturn NULL;\n\n\tif (graphics->exports.device_texture_wrap_obj)\n\t\treturn graphics->exports.device_texture_wrap_obj(graphics->device, obj);\n\treturn NULL;\n}\n\nint gs_texture_acquire_sync(gs_texture_t *tex, uint64_t key, uint32_t ms)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tif (!gs_valid(\"gs_texture_acquire_sync\"))\n\t\treturn -1;\n\n\tif (graphics->exports.device_texture_acquire_sync)\n\t\treturn graphics->exports.device_texture_acquire_sync(tex, key, ms);\n\treturn -1;\n}\n\nint gs_texture_release_sync(gs_texture_t *tex, uint64_t key)\n{\n\tgraphics_t *graphics = thread_graphics;\n\tif (!gs_valid(\"gs_texture_release_sync\"))\n\t\treturn -1;\n\n\tif (graphics->exports.device_texture_release_sync)\n\t\treturn graphics->exports.device_texture_release_sync(tex, key);\n\treturn -1;\n}\n\ngs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, uint32_t height)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_stagesurface_create_nv12\"))\n\t\treturn NULL;\n\n\tif ((width & 1) == 1 || (height & 1) == 1) {\n\t\tblog(LOG_ERROR, \"NV12 textures must have dimensions \"\n\t\t\t\t\"divisible by 2.\");\n\t\treturn NULL;\n\t}\n\n\tif (graphics->exports.device_stagesurface_create_nv12)\n\t\treturn graphics->exports.device_stagesurface_create_nv12(graphics->device, width, height);\n\n\treturn NULL;\n}\n\ngs_stagesurf_t *gs_stagesurface_create_p010(uint32_t width, uint32_t height)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_stagesurface_create_p010\"))\n\t\treturn NULL;\n\n\tif ((width & 1) == 1 || (height & 1) == 1) {\n\t\tblog(LOG_ERROR, \"P010 textures must have dimensions \"\n\t\t\t\t\"divisible by 2.\");\n\t\treturn NULL;\n\t}\n\n\tif (graphics->exports.device_stagesurface_create_p010)\n\t\treturn graphics->exports.device_stagesurface_create_p010(graphics->device, width, height);\n\n\treturn NULL;\n}\n\nvoid gs_register_loss_callbacks(const struct gs_device_loss *callbacks)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_register_loss_callbacks\"))\n\t\treturn;\n\n\tif (graphics->exports.device_register_loss_callbacks)\n\t\tgraphics->exports.device_register_loss_callbacks(graphics->device, callbacks);\n}\n\nvoid gs_unregister_loss_callbacks(void *data)\n{\n\tgraphics_t *graphics = thread_graphics;\n\n\tif (!gs_valid(\"gs_unregister_loss_callbacks\"))\n\t\treturn;\n\n\tif (graphics->exports.device_unregister_loss_callbacks)\n\t\tgraphics->exports.device_unregister_loss_callbacks(graphics->device, data);\n}\n\n#endif\n"
  },
  {
    "path": "libobs/graphics/graphics.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/bmem.h\"\n#include \"input.h\"\n#ifdef __APPLE__\n#include <objc/objc-runtime.h>\n#endif\n\n/*\n * This is an API-independent graphics subsystem wrapper.\n *\n *   This allows the use of OpenGL and different Direct3D versions through\n * one shared interface.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define GS_MAX_TEXTURES 8\n\nstruct vec2;\nstruct vec3;\nstruct vec4;\nstruct quat;\nstruct axisang;\nstruct plane;\nstruct matrix3;\nstruct matrix4;\n\nenum gs_draw_mode {\n\tGS_POINTS,\n\tGS_LINES,\n\tGS_LINESTRIP,\n\tGS_TRIS,\n\tGS_TRISTRIP,\n};\n\nenum gs_color_format {\n\tGS_UNKNOWN,\n\tGS_A8,\n\tGS_R8,\n\tGS_RGBA,\n\tGS_BGRX,\n\tGS_BGRA,\n\tGS_R10G10B10A2,\n\tGS_RGBA16,\n\tGS_R16,\n\tGS_RGBA16F,\n\tGS_RGBA32F,\n\tGS_RG16F,\n\tGS_RG32F,\n\tGS_R16F,\n\tGS_R32F,\n\tGS_DXT1,\n\tGS_DXT3,\n\tGS_DXT5,\n\tGS_R8G8,\n\tGS_RGBA_UNORM,\n\tGS_BGRX_UNORM,\n\tGS_BGRA_UNORM,\n\tGS_RG16,\n};\n\nenum gs_color_space {\n\tGS_CS_SRGB,         /* SDR */\n\tGS_CS_SRGB_16F,     /* High-precision SDR */\n\tGS_CS_709_EXTENDED, /* Canvas, Mac EDR (HDR) */\n\tGS_CS_709_SCRGB,    /* 1.0 = 80 nits, Windows/Linux HDR */\n};\n\nenum gs_zstencil_format {\n\tGS_ZS_NONE,\n\tGS_Z16,\n\tGS_Z24_S8,\n\tGS_Z32F,\n\tGS_Z32F_S8X24,\n};\n\nenum gs_index_type {\n\tGS_UNSIGNED_SHORT,\n\tGS_UNSIGNED_LONG,\n};\n\nenum gs_cull_mode {\n\tGS_BACK,\n\tGS_FRONT,\n\tGS_NEITHER,\n};\n\nenum gs_blend_type {\n\tGS_BLEND_ZERO,\n\tGS_BLEND_ONE,\n\tGS_BLEND_SRCCOLOR,\n\tGS_BLEND_INVSRCCOLOR,\n\tGS_BLEND_SRCALPHA,\n\tGS_BLEND_INVSRCALPHA,\n\tGS_BLEND_DSTCOLOR,\n\tGS_BLEND_INVDSTCOLOR,\n\tGS_BLEND_DSTALPHA,\n\tGS_BLEND_INVDSTALPHA,\n\tGS_BLEND_SRCALPHASAT,\n};\n\nenum gs_blend_op_type {\n\tGS_BLEND_OP_ADD,\n\tGS_BLEND_OP_SUBTRACT,\n\tGS_BLEND_OP_REVERSE_SUBTRACT,\n\tGS_BLEND_OP_MIN,\n\tGS_BLEND_OP_MAX\n};\n\nenum gs_depth_test {\n\tGS_NEVER,\n\tGS_LESS,\n\tGS_LEQUAL,\n\tGS_EQUAL,\n\tGS_GEQUAL,\n\tGS_GREATER,\n\tGS_NOTEQUAL,\n\tGS_ALWAYS,\n};\n\nenum gs_stencil_side {\n\tGS_STENCIL_FRONT = 1,\n\tGS_STENCIL_BACK,\n\tGS_STENCIL_BOTH,\n};\n\nenum gs_stencil_op_type {\n\tGS_KEEP,\n\tGS_ZERO,\n\tGS_REPLACE,\n\tGS_INCR,\n\tGS_DECR,\n\tGS_INVERT,\n};\n\nenum gs_cube_sides {\n\tGS_POSITIVE_X,\n\tGS_NEGATIVE_X,\n\tGS_POSITIVE_Y,\n\tGS_NEGATIVE_Y,\n\tGS_POSITIVE_Z,\n\tGS_NEGATIVE_Z,\n};\n\nenum gs_sample_filter {\n\tGS_FILTER_POINT,\n\tGS_FILTER_LINEAR,\n\tGS_FILTER_ANISOTROPIC,\n\tGS_FILTER_MIN_MAG_POINT_MIP_LINEAR,\n\tGS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,\n\tGS_FILTER_MIN_POINT_MAG_MIP_LINEAR,\n\tGS_FILTER_MIN_LINEAR_MAG_MIP_POINT,\n\tGS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR,\n\tGS_FILTER_MIN_MAG_LINEAR_MIP_POINT,\n};\n\nenum gs_address_mode {\n\tGS_ADDRESS_CLAMP,\n\tGS_ADDRESS_WRAP,\n\tGS_ADDRESS_MIRROR,\n\tGS_ADDRESS_BORDER,\n\tGS_ADDRESS_MIRRORONCE,\n};\n\nenum gs_texture_type {\n\tGS_TEXTURE_2D,\n\tGS_TEXTURE_3D,\n\tGS_TEXTURE_CUBE,\n};\n\nstruct gs_device_loss {\n\tvoid (*device_loss_release)(void *data);\n\tvoid (*device_loss_rebuild)(void *device, void *data);\n\tvoid *data;\n};\n\nstruct gs_monitor_info {\n\tint rotation_degrees;\n\tlong x;\n\tlong y;\n\tlong cx;\n\tlong cy;\n};\n\nstruct gs_tvertarray {\n\tsize_t width;\n\tvoid *array;\n};\n\nstruct gs_vb_data {\n\tsize_t num;\n\tstruct vec3 *points;\n\tstruct vec3 *normals;\n\tstruct vec3 *tangents;\n\tuint32_t *colors;\n\n\tsize_t num_tex;\n\tstruct gs_tvertarray *tvarray;\n};\n\nstatic inline struct gs_vb_data *gs_vbdata_create(void)\n{\n\treturn (struct gs_vb_data *)bzalloc(sizeof(struct gs_vb_data));\n}\n\nstatic inline void gs_vbdata_destroy(struct gs_vb_data *data)\n{\n\tuint32_t i;\n\tif (!data)\n\t\treturn;\n\n\tbfree(data->points);\n\tbfree(data->normals);\n\tbfree(data->tangents);\n\tbfree(data->colors);\n\tfor (i = 0; i < data->num_tex; i++)\n\t\tbfree(data->tvarray[i].array);\n\tbfree(data->tvarray);\n\tbfree(data);\n}\n\nstruct gs_sampler_info {\n\tenum gs_sample_filter filter;\n\tenum gs_address_mode address_u;\n\tenum gs_address_mode address_v;\n\tenum gs_address_mode address_w;\n\tint max_anisotropy;\n\tuint32_t border_color;\n};\n\nstruct gs_display_mode {\n\tuint32_t width;\n\tuint32_t height;\n\tuint32_t bits;\n\tuint32_t freq;\n};\n\nstruct gs_rect {\n\tint x;\n\tint y;\n\tint cx;\n\tint cy;\n};\n\n/* wrapped opaque data types */\n\nstruct gs_texture;\nstruct gs_stage_surface;\nstruct gs_zstencil_buffer;\nstruct gs_vertex_buffer;\nstruct gs_index_buffer;\nstruct gs_sampler_state;\nstruct gs_shader;\nstruct gs_swap_chain;\nstruct gs_timer;\nstruct gs_texrender;\nstruct gs_shader_param;\nstruct gs_effect;\nstruct gs_effect_technique;\nstruct gs_effect_pass;\nstruct gs_effect_param;\nstruct gs_device;\nstruct graphics_subsystem;\n\ntypedef struct gs_texture gs_texture_t;\ntypedef struct gs_stage_surface gs_stagesurf_t;\ntypedef struct gs_zstencil_buffer gs_zstencil_t;\ntypedef struct gs_vertex_buffer gs_vertbuffer_t;\ntypedef struct gs_index_buffer gs_indexbuffer_t;\ntypedef struct gs_sampler_state gs_samplerstate_t;\ntypedef struct gs_swap_chain gs_swapchain_t;\ntypedef struct gs_timer gs_timer_t;\ntypedef struct gs_timer_range gs_timer_range_t;\ntypedef struct gs_texture_render gs_texrender_t;\ntypedef struct gs_shader gs_shader_t;\ntypedef struct gs_shader_param gs_sparam_t;\ntypedef struct gs_effect gs_effect_t;\ntypedef struct gs_effect_technique gs_technique_t;\ntypedef struct gs_effect_pass gs_epass_t;\ntypedef struct gs_effect_param gs_eparam_t;\ntypedef struct gs_device gs_device_t;\ntypedef void gs_sync_t;\ntypedef struct graphics_subsystem graphics_t;\n\n/* ---------------------------------------------------\n * shader functions\n * --------------------------------------------------- */\n\nenum gs_shader_param_type {\n\tGS_SHADER_PARAM_UNKNOWN,\n\tGS_SHADER_PARAM_BOOL,\n\tGS_SHADER_PARAM_FLOAT,\n\tGS_SHADER_PARAM_INT,\n\tGS_SHADER_PARAM_STRING,\n\tGS_SHADER_PARAM_VEC2,\n\tGS_SHADER_PARAM_VEC3,\n\tGS_SHADER_PARAM_VEC4,\n\tGS_SHADER_PARAM_INT2,\n\tGS_SHADER_PARAM_INT3,\n\tGS_SHADER_PARAM_INT4,\n\tGS_SHADER_PARAM_MATRIX4X4,\n\tGS_SHADER_PARAM_TEXTURE,\n};\n\nstruct gs_shader_texture {\n\tgs_texture_t *tex;\n\tbool srgb;\n};\n\n#ifndef SWIG\nstruct gs_shader_param_info {\n\tenum gs_shader_param_type type;\n\tconst char *name;\n};\n\nenum gs_shader_type {\n\tGS_SHADER_VERTEX,\n\tGS_SHADER_PIXEL,\n};\n\nEXPORT void gs_shader_destroy(gs_shader_t *shader);\n\nEXPORT int gs_shader_get_num_params(const gs_shader_t *shader);\nEXPORT gs_sparam_t *gs_shader_get_param_by_idx(gs_shader_t *shader, uint32_t param);\nEXPORT gs_sparam_t *gs_shader_get_param_by_name(gs_shader_t *shader, const char *name);\n\nEXPORT gs_sparam_t *gs_shader_get_viewproj_matrix(const gs_shader_t *shader);\nEXPORT gs_sparam_t *gs_shader_get_world_matrix(const gs_shader_t *shader);\n\nEXPORT void gs_shader_get_param_info(const gs_sparam_t *param, struct gs_shader_param_info *info);\nEXPORT void gs_shader_set_bool(gs_sparam_t *param, bool val);\nEXPORT void gs_shader_set_float(gs_sparam_t *param, float val);\nEXPORT void gs_shader_set_int(gs_sparam_t *param, int val);\nEXPORT void gs_shader_set_matrix3(gs_sparam_t *param, const struct matrix3 *val);\nEXPORT void gs_shader_set_matrix4(gs_sparam_t *param, const struct matrix4 *val);\nEXPORT void gs_shader_set_vec2(gs_sparam_t *param, const struct vec2 *val);\nEXPORT void gs_shader_set_vec3(gs_sparam_t *param, const struct vec3 *val);\nEXPORT void gs_shader_set_vec4(gs_sparam_t *param, const struct vec4 *val);\nEXPORT void gs_shader_set_texture(gs_sparam_t *param, gs_texture_t *val);\nEXPORT void gs_shader_set_val(gs_sparam_t *param, const void *val, size_t size);\nEXPORT void gs_shader_set_default(gs_sparam_t *param);\nEXPORT void gs_shader_set_next_sampler(gs_sparam_t *param, gs_samplerstate_t *sampler);\n#endif\n\n/* ---------------------------------------------------\n * effect functions\n * --------------------------------------------------- */\n\n/*enum gs_effect_property_type {\n\tGS_EFFECT_NONE,\n\tGS_EFFECT_BOOL,\n\tGS_EFFECT_FLOAT,\n\tGS_EFFECT_COLOR,\n\tGS_EFFECT_TEXTURE\n};*/\n\n#ifndef SWIG\nstruct gs_effect_param_info {\n\tconst char *name;\n\tenum gs_shader_param_type type;\n\n\t/* const char *full_name;\n\tenum gs_effect_property_type prop_type;\n\n\tfloat min, max, inc, mul; */\n};\n#endif\n\nEXPORT void gs_effect_destroy(gs_effect_t *effect);\n\nEXPORT gs_technique_t *gs_effect_get_technique(const gs_effect_t *effect, const char *name);\n\nEXPORT gs_technique_t *gs_effect_get_current_technique(const gs_effect_t *effect);\n\nEXPORT size_t gs_technique_begin(gs_technique_t *technique);\nEXPORT void gs_technique_end(gs_technique_t *technique);\nEXPORT bool gs_technique_begin_pass(gs_technique_t *technique, size_t pass);\nEXPORT bool gs_technique_begin_pass_by_name(gs_technique_t *technique, const char *name);\nEXPORT void gs_technique_end_pass(gs_technique_t *technique);\nEXPORT gs_epass_t *gs_technique_get_pass_by_idx(const gs_technique_t *technique, size_t pass);\nEXPORT gs_epass_t *gs_technique_get_pass_by_name(const gs_technique_t *technique, const char *name);\n\nEXPORT size_t gs_effect_get_num_params(const gs_effect_t *effect);\nEXPORT gs_eparam_t *gs_effect_get_param_by_idx(const gs_effect_t *effect, size_t param);\nEXPORT gs_eparam_t *gs_effect_get_param_by_name(const gs_effect_t *effect, const char *name);\nEXPORT size_t gs_param_get_num_annotations(const gs_eparam_t *param);\nEXPORT gs_eparam_t *gs_param_get_annotation_by_idx(const gs_eparam_t *param, size_t annotation);\nEXPORT gs_eparam_t *gs_param_get_annotation_by_name(const gs_eparam_t *param, const char *name);\n\n/** Helper function to simplify effect usage.  Use with a while loop that\n * contains drawing functions.  Automatically handles techniques, passes, and\n * unloading. */\nEXPORT bool gs_effect_loop(gs_effect_t *effect, const char *name);\n\n/** used internally */\nEXPORT void gs_effect_update_params(gs_effect_t *effect);\n\nEXPORT gs_eparam_t *gs_effect_get_viewproj_matrix(const gs_effect_t *effect);\nEXPORT gs_eparam_t *gs_effect_get_world_matrix(const gs_effect_t *effect);\n\n#ifndef SWIG\nEXPORT void gs_effect_get_param_info(const gs_eparam_t *param, struct gs_effect_param_info *info);\n#endif\n\nEXPORT void gs_effect_set_bool(gs_eparam_t *param, bool val);\nEXPORT void gs_effect_set_float(gs_eparam_t *param, float val);\nEXPORT void gs_effect_set_int(gs_eparam_t *param, int val);\nEXPORT void gs_effect_set_matrix4(gs_eparam_t *param, const struct matrix4 *val);\nEXPORT void gs_effect_set_vec2(gs_eparam_t *param, const struct vec2 *val);\nEXPORT void gs_effect_set_vec3(gs_eparam_t *param, const struct vec3 *val);\nEXPORT void gs_effect_set_vec4(gs_eparam_t *param, const struct vec4 *val);\nEXPORT void gs_effect_set_texture(gs_eparam_t *param, gs_texture_t *val);\nEXPORT void gs_effect_set_texture_srgb(gs_eparam_t *param, gs_texture_t *val);\nEXPORT void gs_effect_set_val(gs_eparam_t *param, const void *val, size_t size);\nEXPORT void gs_effect_set_default(gs_eparam_t *param);\nEXPORT size_t gs_effect_get_val_size(gs_eparam_t *param);\nEXPORT void *gs_effect_get_val(gs_eparam_t *param);\nEXPORT size_t gs_effect_get_default_val_size(gs_eparam_t *param);\nEXPORT void *gs_effect_get_default_val(gs_eparam_t *param);\nEXPORT void gs_effect_set_next_sampler(gs_eparam_t *param, gs_samplerstate_t *sampler);\n\nEXPORT void gs_effect_set_color(gs_eparam_t *param, uint32_t argb);\n\n/* ---------------------------------------------------\n * texture render helper functions\n * --------------------------------------------------- */\n\nEXPORT gs_texrender_t *gs_texrender_create(enum gs_color_format format, enum gs_zstencil_format zsformat);\nEXPORT void gs_texrender_destroy(gs_texrender_t *texrender);\nEXPORT bool gs_texrender_begin(gs_texrender_t *texrender, uint32_t cx, uint32_t cy);\nEXPORT bool gs_texrender_begin_with_color_space(gs_texrender_t *texrender, uint32_t cx, uint32_t cy,\n\t\t\t\t\t\tenum gs_color_space space);\nEXPORT void gs_texrender_end(gs_texrender_t *texrender);\nEXPORT void gs_texrender_reset(gs_texrender_t *texrender);\nEXPORT gs_texture_t *gs_texrender_get_texture(const gs_texrender_t *texrender);\nEXPORT enum gs_color_format gs_texrender_get_format(const gs_texrender_t *texrender);\n\n/* ---------------------------------------------------\n * graphics subsystem\n * --------------------------------------------------- */\n\n#define GS_BUILD_MIPMAPS (1 << 0)\n#define GS_DYNAMIC (1 << 1)\n#define GS_RENDER_TARGET (1 << 2)\n#define GS_GL_DUMMYTEX (1 << 3) /**<< texture with no allocated texture data */\n#define GS_DUP_BUFFER \\\n\t(1 << 4) /**<< do not pass buffer ownership when\n\t\t\t\t *    creating a vertex/index buffer */\n#define GS_SHARED_TEX (1 << 5)\n#define GS_SHARED_KM_TEX (1 << 6)\n\n/* ---------------- */\n/* global functions */\n\n#define GS_SUCCESS 0\n#define GS_ERROR_FAIL -1\n#define GS_ERROR_MODULE_NOT_FOUND -2\n#define GS_ERROR_NOT_SUPPORTED -3\n\nstruct gs_window {\n#if defined(_WIN32)\n\tvoid *hwnd;\n#elif defined(__APPLE__)\n\t__unsafe_unretained id view;\n#elif defined(__linux__) || defined(__FreeBSD__)\n\t/* I'm not sure how portable defining id to uint32_t is. */\n\tuint32_t id;\n\tvoid *display;\n#endif\n};\n\nstruct gs_init_data {\n\tstruct gs_window window;\n\tuint32_t cx, cy;\n\tuint32_t num_backbuffers;\n\tenum gs_color_format format;\n\tenum gs_zstencil_format zsformat;\n\tuint32_t adapter;\n};\n\n#define GS_DEVICE_OPENGL 1\n#define GS_DEVICE_DIRECT3D_11 2\n#define GS_DEVICE_METAL 3\n\nEXPORT const char *gs_get_device_name(void);\nEXPORT const char *gs_get_driver_version(void);\nEXPORT const char *gs_get_renderer(void);\nEXPORT uint64_t gs_get_gpu_dmem(void);\nEXPORT uint64_t gs_get_gpu_smem(void);\nEXPORT int gs_get_device_type(void);\nEXPORT uint32_t gs_get_adapter_count(void);\nEXPORT void gs_enum_adapters(bool (*callback)(void *param, const char *name, uint32_t id), void *param);\n\nEXPORT int gs_create(graphics_t **graphics, const char *module, uint32_t adapter);\nEXPORT void gs_destroy(graphics_t *graphics);\n\nEXPORT void gs_enter_context(graphics_t *graphics);\nEXPORT void gs_leave_context(void);\nEXPORT graphics_t *gs_get_context(void);\nEXPORT void *gs_get_device_obj(void);\n\nEXPORT void gs_matrix_push(void);\nEXPORT void gs_matrix_pop(void);\nEXPORT void gs_matrix_identity(void);\nEXPORT void gs_matrix_transpose(void);\nEXPORT void gs_matrix_set(const struct matrix4 *matrix);\nEXPORT void gs_matrix_get(struct matrix4 *dst);\nEXPORT void gs_matrix_mul(const struct matrix4 *matrix);\nEXPORT void gs_matrix_rotquat(const struct quat *rot);\nEXPORT void gs_matrix_rotaa(const struct axisang *rot);\nEXPORT void gs_matrix_translate(const struct vec3 *pos);\nEXPORT void gs_matrix_scale(const struct vec3 *scale);\nEXPORT void gs_matrix_rotaa4f(float x, float y, float z, float angle);\nEXPORT void gs_matrix_translate3f(float x, float y, float z);\nEXPORT void gs_matrix_scale3f(float x, float y, float z);\n\nEXPORT void gs_render_start(bool b_new);\nEXPORT void gs_render_stop(enum gs_draw_mode mode);\nEXPORT gs_vertbuffer_t *gs_render_save(void);\nEXPORT void gs_vertex2f(float x, float y);\nEXPORT void gs_vertex3f(float x, float y, float z);\nEXPORT void gs_normal3f(float x, float y, float z);\nEXPORT void gs_color(uint32_t color);\nEXPORT void gs_texcoord(float x, float y, int unit);\nEXPORT void gs_vertex2v(const struct vec2 *v);\nEXPORT void gs_vertex3v(const struct vec3 *v);\nEXPORT void gs_normal3v(const struct vec3 *v);\nEXPORT void gs_color4v(const struct vec4 *v);\nEXPORT void gs_texcoord2v(const struct vec2 *v, int unit);\n\nEXPORT input_t *gs_get_input(void);\nEXPORT gs_effect_t *gs_get_effect(void);\n\nEXPORT gs_effect_t *gs_effect_create_from_file(const char *file, char **error_string);\nEXPORT gs_effect_t *gs_effect_create(const char *effect_string, const char *filename, char **error_string);\n\nEXPORT gs_shader_t *gs_vertexshader_create_from_file(const char *file, char **error_string);\nEXPORT gs_shader_t *gs_pixelshader_create_from_file(const char *file, char **error_string);\n\nenum gs_image_alpha_mode {\n\tGS_IMAGE_ALPHA_STRAIGHT,\n\tGS_IMAGE_ALPHA_PREMULTIPLY_SRGB,\n\tGS_IMAGE_ALPHA_PREMULTIPLY,\n};\n\nEXPORT gs_texture_t *gs_texture_create_from_file(const char *file);\nEXPORT uint8_t *gs_create_texture_file_data(const char *file, enum gs_color_format *format, uint32_t *cx, uint32_t *cy);\nEXPORT uint8_t *gs_create_texture_file_data2(const char *file, enum gs_image_alpha_mode alpha_mode,\n\t\t\t\t\t     enum gs_color_format *format, uint32_t *cx, uint32_t *cy);\nEXPORT uint8_t *gs_create_texture_file_data3(const char *file, enum gs_image_alpha_mode alpha_mode,\n\t\t\t\t\t     enum gs_color_format *format, uint32_t *cx, uint32_t *cy,\n\t\t\t\t\t     enum gs_color_space *space);\n\n#define GS_FLIP_U (1 << 0)\n#define GS_FLIP_V (1 << 1)\n\n/**\n * Draws a 2D sprite\n *\n *   If width or height is 0, the width or height of the texture will be used.\n * The flip value specifies whether the texture should be flipped on the U or V\n * axis with GS_FLIP_U and GS_FLIP_V.\n */\nEXPORT void gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width, uint32_t height);\nEXPORT void gs_draw_quadf(gs_texture_t *tex, uint32_t flip, float width, float height);\n\nEXPORT void gs_draw_sprite_subregion(gs_texture_t *tex, uint32_t flip, uint32_t x, uint32_t y, uint32_t cx,\n\t\t\t\t     uint32_t cy);\n\nEXPORT void gs_draw_cube_backdrop(gs_texture_t *cubetex, const struct quat *rot, float left, float right, float top,\n\t\t\t\t  float bottom, float znear);\n\n/** sets the viewport to current swap chain size */\nEXPORT void gs_reset_viewport(void);\n\n/** sets default screen-sized orthographic mode */\nEXPORT void gs_set_2d_mode(void);\n/** sets default screen-sized perspective mode */\nEXPORT void gs_set_3d_mode(double fovy, double znear, double zvar);\n\nEXPORT void gs_viewport_push(void);\nEXPORT void gs_viewport_pop(void);\n\nEXPORT void gs_texture_set_image(gs_texture_t *tex, const uint8_t *data, uint32_t linesize, bool invert);\nEXPORT void gs_cubetexture_set_image(gs_texture_t *cubetex, uint32_t side, const void *data, uint32_t linesize,\n\t\t\t\t     bool invert);\n\nEXPORT void gs_perspective(float fovy, float aspect, float znear, float zfar);\n\nEXPORT void gs_blend_state_push(void);\nEXPORT void gs_blend_state_pop(void);\nEXPORT void gs_reset_blend_state(void);\n\n/* -------------------------- */\n/* library-specific functions */\n\nEXPORT gs_swapchain_t *gs_swapchain_create(const struct gs_init_data *data);\n\nEXPORT void gs_resize(uint32_t x, uint32_t y);\nEXPORT void gs_update_color_space(void);\nEXPORT void gs_get_size(uint32_t *x, uint32_t *y);\nEXPORT uint32_t gs_get_width(void);\nEXPORT uint32_t gs_get_height(void);\n\nEXPORT gs_texture_t *gs_texture_create(uint32_t width, uint32_t height, enum gs_color_format color_format,\n\t\t\t\t       uint32_t levels, const uint8_t **data, uint32_t flags);\nEXPORT gs_texture_t *gs_cubetexture_create(uint32_t size, enum gs_color_format color_format, uint32_t levels,\n\t\t\t\t\t   const uint8_t **data, uint32_t flags);\nEXPORT gs_texture_t *gs_voltexture_create(uint32_t width, uint32_t height, uint32_t depth,\n\t\t\t\t\t  enum gs_color_format color_format, uint32_t levels, const uint8_t **data,\n\t\t\t\t\t  uint32_t flags);\n\nEXPORT gs_zstencil_t *gs_zstencil_create(uint32_t width, uint32_t height, enum gs_zstencil_format format);\n\nEXPORT gs_stagesurf_t *gs_stagesurface_create(uint32_t width, uint32_t height, enum gs_color_format color_format);\n\nEXPORT gs_samplerstate_t *gs_samplerstate_create(const struct gs_sampler_info *info);\n\nEXPORT gs_shader_t *gs_vertexshader_create(const char *shader, const char *file, char **error_string);\nEXPORT gs_shader_t *gs_pixelshader_create(const char *shader, const char *file, char **error_string);\n\nEXPORT gs_vertbuffer_t *gs_vertexbuffer_create(struct gs_vb_data *data, uint32_t flags);\nEXPORT gs_indexbuffer_t *gs_indexbuffer_create(enum gs_index_type type, void *indices, size_t num, uint32_t flags);\n\nEXPORT gs_timer_t *gs_timer_create();\nEXPORT gs_timer_range_t *gs_timer_range_create();\n\nEXPORT enum gs_texture_type gs_get_texture_type(const gs_texture_t *texture);\n\nEXPORT void gs_load_vertexbuffer(gs_vertbuffer_t *vertbuffer);\nEXPORT void gs_load_indexbuffer(gs_indexbuffer_t *indexbuffer);\nEXPORT void gs_load_texture(gs_texture_t *tex, int unit);\nEXPORT void gs_load_samplerstate(gs_samplerstate_t *samplerstate, int unit);\nEXPORT void gs_load_vertexshader(gs_shader_t *vertshader);\nEXPORT void gs_load_pixelshader(gs_shader_t *pixelshader);\n\nEXPORT void gs_load_default_samplerstate(bool b_3d, int unit);\n\nEXPORT gs_shader_t *gs_get_vertex_shader(void);\nEXPORT gs_shader_t *gs_get_pixel_shader(void);\n\nEXPORT enum gs_color_space gs_get_color_space(void);\nEXPORT gs_texture_t *gs_get_render_target(void);\nEXPORT gs_zstencil_t *gs_get_zstencil_target(void);\n\nEXPORT void gs_set_render_target(gs_texture_t *tex, gs_zstencil_t *zstencil);\nEXPORT void gs_set_render_target_with_color_space(gs_texture_t *tex, gs_zstencil_t *zstencil,\n\t\t\t\t\t\t  enum gs_color_space space);\nEXPORT void gs_set_cube_render_target(gs_texture_t *cubetex, int side, gs_zstencil_t *zstencil);\n\nEXPORT void gs_enable_framebuffer_srgb(bool enable);\nEXPORT bool gs_framebuffer_srgb_enabled(void);\n\nEXPORT bool gs_get_linear_srgb(void);\nEXPORT bool gs_set_linear_srgb(bool linear_srgb);\n\nEXPORT void gs_copy_texture(gs_texture_t *dst, gs_texture_t *src);\nEXPORT void gs_copy_texture_region(gs_texture_t *dst, uint32_t dst_x, uint32_t dst_y, gs_texture_t *src, uint32_t src_x,\n\t\t\t\t   uint32_t src_y, uint32_t src_w, uint32_t src_h);\nEXPORT void gs_stage_texture(gs_stagesurf_t *dst, gs_texture_t *src);\n\nEXPORT void gs_begin_frame(void);\nEXPORT void gs_begin_scene(void);\nEXPORT void gs_draw(enum gs_draw_mode draw_mode, uint32_t start_vert, uint32_t num_verts);\nEXPORT void gs_end_scene(void);\n\n#define GS_CLEAR_COLOR (1 << 0)\n#define GS_CLEAR_DEPTH (1 << 1)\n#define GS_CLEAR_STENCIL (1 << 2)\n\nEXPORT void gs_load_swapchain(gs_swapchain_t *swapchain);\nEXPORT void gs_clear(uint32_t clear_flags, const struct vec4 *color, float depth, uint8_t stencil);\nEXPORT bool gs_is_present_ready(void);\nEXPORT void gs_present(void);\nEXPORT void gs_flush(void);\n\nEXPORT void gs_set_cull_mode(enum gs_cull_mode mode);\nEXPORT enum gs_cull_mode gs_get_cull_mode(void);\n\nEXPORT void gs_enable_blending(bool enable);\nEXPORT void gs_enable_depth_test(bool enable);\nEXPORT void gs_enable_stencil_test(bool enable);\nEXPORT void gs_enable_stencil_write(bool enable);\nEXPORT void gs_enable_color(bool red, bool green, bool blue, bool alpha);\n\nEXPORT void gs_blend_function(enum gs_blend_type src, enum gs_blend_type dest);\nEXPORT void gs_blend_function_separate(enum gs_blend_type src_c, enum gs_blend_type dest_c, enum gs_blend_type src_a,\n\t\t\t\t       enum gs_blend_type dest_a);\nEXPORT void gs_blend_op(enum gs_blend_op_type op);\n\nEXPORT void gs_depth_function(enum gs_depth_test test);\n\nEXPORT void gs_stencil_function(enum gs_stencil_side side, enum gs_depth_test test);\nEXPORT void gs_stencil_op(enum gs_stencil_side side, enum gs_stencil_op_type fail, enum gs_stencil_op_type zfail,\n\t\t\t  enum gs_stencil_op_type zpass);\n\nEXPORT void gs_set_viewport(int x, int y, int width, int height);\nEXPORT void gs_get_viewport(struct gs_rect *rect);\nEXPORT void gs_set_scissor_rect(const struct gs_rect *rect);\n\nEXPORT void gs_ortho(float left, float right, float top, float bottom, float znear, float zfar);\nEXPORT void gs_frustum(float left, float right, float top, float bottom, float znear, float zfar);\n\nEXPORT void gs_projection_push(void);\nEXPORT void gs_projection_pop(void);\n\nEXPORT void gs_swapchain_destroy(gs_swapchain_t *swapchain);\n\nEXPORT void gs_texture_destroy(gs_texture_t *tex);\nEXPORT uint32_t gs_texture_get_width(const gs_texture_t *tex);\nEXPORT uint32_t gs_texture_get_height(const gs_texture_t *tex);\nEXPORT enum gs_color_format gs_texture_get_color_format(const gs_texture_t *tex);\nEXPORT bool gs_texture_map(gs_texture_t *tex, uint8_t **ptr, uint32_t *linesize);\nEXPORT void gs_texture_unmap(gs_texture_t *tex);\n/** special-case function (GL only) - specifies whether the texture is a\n * GL_TEXTURE_RECTANGLE type, which doesn't use normalized texture\n * coordinates, doesn't support mipmapping, and requires address clamping */\nEXPORT bool gs_texture_is_rect(const gs_texture_t *tex);\n/**\n * Gets a pointer to the context-specific object associated with the texture.\n * For example, for GL, this is a GLuint*.  For D3D11, ID3D11Texture2D*.\n */\nEXPORT void *gs_texture_get_obj(gs_texture_t *tex);\n\nEXPORT void gs_cubetexture_destroy(gs_texture_t *cubetex);\nEXPORT uint32_t gs_cubetexture_get_size(const gs_texture_t *cubetex);\nEXPORT enum gs_color_format gs_cubetexture_get_color_format(const gs_texture_t *cubetex);\n\nEXPORT void gs_voltexture_destroy(gs_texture_t *voltex);\nEXPORT uint32_t gs_voltexture_get_width(const gs_texture_t *voltex);\nEXPORT uint32_t gs_voltexture_get_height(const gs_texture_t *voltex);\nEXPORT uint32_t gs_voltexture_get_depth(const gs_texture_t *voltex);\nEXPORT enum gs_color_format gs_voltexture_get_color_format(const gs_texture_t *voltex);\n\nEXPORT void gs_stagesurface_destroy(gs_stagesurf_t *stagesurf);\nEXPORT uint32_t gs_stagesurface_get_width(const gs_stagesurf_t *stagesurf);\nEXPORT uint32_t gs_stagesurface_get_height(const gs_stagesurf_t *stagesurf);\nEXPORT enum gs_color_format gs_stagesurface_get_color_format(const gs_stagesurf_t *stagesurf);\nEXPORT bool gs_stagesurface_map(gs_stagesurf_t *stagesurf, uint8_t **data, uint32_t *linesize);\nEXPORT void gs_stagesurface_unmap(gs_stagesurf_t *stagesurf);\n\nEXPORT void gs_zstencil_destroy(gs_zstencil_t *zstencil);\n\nEXPORT void gs_samplerstate_destroy(gs_samplerstate_t *samplerstate);\n\nEXPORT void gs_vertexbuffer_destroy(gs_vertbuffer_t *vertbuffer);\nEXPORT void gs_vertexbuffer_flush(gs_vertbuffer_t *vertbuffer);\nEXPORT void gs_vertexbuffer_flush_direct(gs_vertbuffer_t *vertbuffer, const struct gs_vb_data *data);\nEXPORT struct gs_vb_data *gs_vertexbuffer_get_data(const gs_vertbuffer_t *vertbuffer);\n\nEXPORT void gs_indexbuffer_destroy(gs_indexbuffer_t *indexbuffer);\nEXPORT void gs_indexbuffer_flush(gs_indexbuffer_t *indexbuffer);\nEXPORT void gs_indexbuffer_flush_direct(gs_indexbuffer_t *indexbuffer, const void *data);\nEXPORT void *gs_indexbuffer_get_data(const gs_indexbuffer_t *indexbuffer);\nEXPORT size_t gs_indexbuffer_get_num_indices(const gs_indexbuffer_t *indexbuffer);\nEXPORT enum gs_index_type gs_indexbuffer_get_type(const gs_indexbuffer_t *indexbuffer);\n\nEXPORT void gs_timer_destroy(gs_timer_t *timer);\nEXPORT void gs_timer_begin(gs_timer_t *timer);\nEXPORT void gs_timer_end(gs_timer_t *timer);\nEXPORT bool gs_timer_get_data(gs_timer_t *timer, uint64_t *ticks);\nEXPORT void gs_timer_range_destroy(gs_timer_range_t *timer);\nEXPORT void gs_timer_range_begin(gs_timer_range_t *range);\nEXPORT void gs_timer_range_end(gs_timer_range_t *range);\nEXPORT bool gs_timer_range_get_data(gs_timer_range_t *range, bool *disjoint, uint64_t *frequency);\n\nEXPORT bool gs_nv12_available(void);\nEXPORT bool gs_p010_available(void);\nEXPORT bool gs_texture_create_nv12(gs_texture_t **tex_y, gs_texture_t **tex_uv, uint32_t width, uint32_t height,\n\t\t\t\t   uint32_t flags);\nEXPORT bool gs_texture_create_p010(gs_texture_t **tex_y, gs_texture_t **tex_uv, uint32_t width, uint32_t height,\n\t\t\t\t   uint32_t flags);\n\nEXPORT bool gs_is_monitor_hdr(void *monitor);\n\n#define GS_USE_DEBUG_MARKERS 0\n#if GS_USE_DEBUG_MARKERS\nstatic const float GS_DEBUG_COLOR_DEFAULT[] = {0.5f, 0.5f, 0.5f, 1.0f};\nstatic const float GS_DEBUG_COLOR_RENDER_VIDEO[] = {0.0f, 0.5f, 0.0f, 1.0f};\nstatic const float GS_DEBUG_COLOR_MAIN_TEXTURE[] = {0.0f, 0.25f, 0.0f, 1.0f};\nstatic const float GS_DEBUG_COLOR_DISPLAY[] = {0.0f, 0.5f, 0.5f, 1.0f};\nstatic const float GS_DEBUG_COLOR_SOURCE[] = {0.0f, 0.5f, 5.0f, 1.0f};\nstatic const float GS_DEBUG_COLOR_ITEM[] = {0.5f, 0.0f, 0.0f, 1.0f};\nstatic const float GS_DEBUG_COLOR_ITEM_TEXTURE[] = {0.25f, 0.0f, 0.0f, 1.0f};\nstatic const float GS_DEBUG_COLOR_CONVERT_FORMAT[] = {0.5f, 0.5f, 0.0f, 1.0f};\n#define GS_DEBUG_MARKER_BEGIN(color, markername) gs_debug_marker_begin(color, markername)\n#define GS_DEBUG_MARKER_BEGIN_FORMAT(color, format, ...) gs_debug_marker_begin_format(color, format, __VA_ARGS__)\n#define GS_DEBUG_MARKER_END() gs_debug_marker_end()\n#else\n#define GS_DEBUG_MARKER_BEGIN(color, markername) ((void)0)\n#define GS_DEBUG_MARKER_BEGIN_FORMAT(color, format, ...) ((void)0)\n#define GS_DEBUG_MARKER_END() ((void)0)\n#endif\n\nEXPORT void gs_debug_marker_begin(const float color[4], const char *markername);\nEXPORT void gs_debug_marker_begin_format(const float color[4], const char *format, ...);\nEXPORT void gs_debug_marker_end(void);\n\n#ifdef __APPLE__\n\n/** platform specific function for creating (GL_TEXTURE_RECTANGLE) textures\n * from shared surface resources */\nEXPORT gs_texture_t *gs_texture_create_from_iosurface(void *iosurf);\nEXPORT bool gs_texture_rebind_iosurface(gs_texture_t *texture, void *iosurf);\nEXPORT gs_texture_t *gs_texture_open_shared(uint32_t handle);\nEXPORT bool gs_shared_texture_available(void);\n\n#elif _WIN32\n\nEXPORT bool gs_gdi_texture_available(void);\nEXPORT bool gs_shared_texture_available(void);\n\nstruct gs_duplicator;\ntypedef struct gs_duplicator gs_duplicator_t;\n\n/**\n * Gets information about the monitor at the specific index, returns false\n * when there is no monitor at the specified index\n */\nEXPORT bool gs_get_duplicator_monitor_info(int monitor_idx, struct gs_monitor_info *monitor_info);\n\nEXPORT int gs_duplicator_get_monitor_index(void *monitor);\n\n/** creates a windows 8+ output duplicator (monitor capture) */\nEXPORT gs_duplicator_t *gs_duplicator_create(int monitor_idx);\nEXPORT void gs_duplicator_destroy(gs_duplicator_t *duplicator);\n\nEXPORT bool gs_duplicator_update_frame(gs_duplicator_t *duplicator);\nEXPORT gs_texture_t *gs_duplicator_get_texture(gs_duplicator_t *duplicator);\nEXPORT enum gs_color_space gs_duplicator_get_color_space(gs_duplicator_t *duplicator);\nEXPORT float gs_duplicator_get_sdr_white_level(gs_duplicator_t *duplicator);\n\nEXPORT bool gs_can_adapter_fast_clear(void);\n\n/** creates a windows GDI-lockable texture */\nEXPORT gs_texture_t *gs_texture_create_gdi(uint32_t width, uint32_t height);\n\nEXPORT void *gs_texture_get_dc(gs_texture_t *gdi_tex);\nEXPORT void gs_texture_release_dc(gs_texture_t *gdi_tex);\n\n/** creates a windows shared texture from a texture handle */\nEXPORT gs_texture_t *gs_texture_open_shared(uint32_t handle);\nEXPORT gs_texture_t *gs_texture_open_nt_shared(uint32_t handle);\n\n#define GS_INVALID_HANDLE (uint32_t) - 1\nEXPORT uint32_t gs_texture_get_shared_handle(gs_texture_t *tex);\n\nEXPORT gs_texture_t *gs_texture_wrap_obj(void *obj);\n\n#define GS_WAIT_INFINITE (uint32_t) - 1\n\n/**\n * acquires a lock on a keyed mutex texture.\n * returns -1 on generic failure, ETIMEDOUT if timed out\n */\nEXPORT int gs_texture_acquire_sync(gs_texture_t *tex, uint64_t key, uint32_t ms);\n\n/**\n * releases a lock on a keyed mutex texture to another device.\n * return 0 on success, -1 on error\n */\nEXPORT int gs_texture_release_sync(gs_texture_t *tex, uint64_t key);\n\nEXPORT gs_stagesurf_t *gs_stagesurface_create_nv12(uint32_t width, uint32_t height);\nEXPORT gs_stagesurf_t *gs_stagesurface_create_p010(uint32_t width, uint32_t height);\n\nEXPORT void gs_register_loss_callbacks(const struct gs_device_loss *callbacks);\nEXPORT void gs_unregister_loss_callbacks(void *data);\n\n#elif defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)\n\nEXPORT gs_texture_t *gs_texture_create_from_dmabuf(unsigned int width, unsigned int height, uint32_t drm_format,\n\t\t\t\t\t\t   enum gs_color_format color_format, uint32_t n_planes, const int *fds,\n\t\t\t\t\t\t   const uint32_t *strides, const uint32_t *offsets,\n\t\t\t\t\t\t   const uint64_t *modifiers);\n\nenum gs_dmabuf_flags {\n\tGS_DMABUF_FLAG_NONE = 0,\n\tGS_DMABUF_FLAG_IMPLICIT_MODIFIERS_SUPPORTED = (1 << 0),\n};\n\nEXPORT bool gs_query_dmabuf_capabilities(enum gs_dmabuf_flags *dmabuf_flags, uint32_t **drm_formats, size_t *n_formats);\n\nEXPORT bool gs_query_dmabuf_modifiers_for_format(uint32_t drm_format, uint64_t **modifiers, size_t *n_modifiers);\n\nEXPORT gs_texture_t *gs_texture_create_from_pixmap(uint32_t width, uint32_t height, enum gs_color_format color_format,\n\t\t\t\t\t\t   uint32_t target, void *pixmap);\n\nEXPORT bool gs_query_sync_capabilities(void);\n\nEXPORT gs_sync_t *gs_sync_create(void);\n\nEXPORT gs_sync_t *gs_sync_create_from_syncobj_timeline_point(int syncobj_fd, uint64_t timeline_point);\n\nEXPORT void gs_sync_destroy(gs_sync_t *sync);\n\nEXPORT bool gs_sync_export_syncobj_timeline_point(gs_sync_t *sync, int syncobj_fd, uint64_t timeline_point);\n\nEXPORT bool gs_sync_signal_syncobj_timeline_point(int syncobj_fd, uint64_t timeline_point);\n\nEXPORT bool gs_sync_wait(gs_sync_t *sync);\n#endif\n\n/* inline functions used by modules */\n\nstatic inline uint32_t gs_get_format_bpp(enum gs_color_format format)\n{\n\tswitch (format) {\n\tcase GS_DXT1:\n\t\treturn 4;\n\tcase GS_A8:\n\tcase GS_R8:\n\tcase GS_DXT3:\n\tcase GS_DXT5:\n\t\treturn 8;\n\tcase GS_R16:\n\tcase GS_R16F:\n\tcase GS_R8G8:\n\t\treturn 16;\n\tcase GS_RGBA:\n\tcase GS_BGRX:\n\tcase GS_BGRA:\n\tcase GS_R10G10B10A2:\n\tcase GS_RG16F:\n\tcase GS_R32F:\n\tcase GS_RGBA_UNORM:\n\tcase GS_BGRX_UNORM:\n\tcase GS_BGRA_UNORM:\n\tcase GS_RG16:\n\t\treturn 32;\n\tcase GS_RGBA16:\n\tcase GS_RGBA16F:\n\tcase GS_RG32F:\n\t\treturn 64;\n\tcase GS_RGBA32F:\n\t\treturn 128;\n\tcase GS_UNKNOWN:\n\t\treturn 0;\n\t}\n\n\treturn 0;\n}\n\nstatic inline bool gs_is_compressed_format(enum gs_color_format format)\n{\n\treturn (format == GS_DXT1 || format == GS_DXT3 || format == GS_DXT5);\n}\n\nstatic inline bool gs_is_srgb_format(enum gs_color_format format)\n{\n\tswitch (format) {\n\tcase GS_RGBA:\n\tcase GS_BGRX:\n\tcase GS_BGRA:\n\t\treturn true;\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nstatic inline enum gs_color_format gs_generalize_format(enum gs_color_format format)\n{\n\tswitch (format) {\n\tcase GS_RGBA_UNORM:\n\t\treturn GS_RGBA;\n\tcase GS_BGRX_UNORM:\n\t\treturn GS_BGRX;\n\tcase GS_BGRA_UNORM:\n\t\treturn GS_BGRA;\n\tdefault:\n\t\treturn format;\n\t}\n}\n\nstatic inline enum gs_color_format gs_get_format_from_space(enum gs_color_space space)\n{\n\tswitch (space) {\n\tcase GS_CS_SRGB:\n\t\tbreak;\n\tcase GS_CS_SRGB_16F:\n\tcase GS_CS_709_EXTENDED:\n\tcase GS_CS_709_SCRGB:\n\t\treturn GS_RGBA16F;\n\t}\n\n\treturn GS_RGBA;\n}\n\nstatic inline uint32_t gs_get_total_levels(uint32_t width, uint32_t height, uint32_t depth)\n{\n\tuint32_t size = width > height ? width : height;\n\tsize = size > depth ? size : depth;\n\tuint32_t num_levels = 1;\n\n\twhile (size > 1) {\n\t\tsize /= 2;\n\t\tnum_levels++;\n\t}\n\n\treturn num_levels;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/half.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n/******************************************************************************\n                               The MIT License (MIT)\n\nCopyright (c) 2011-2019 Microsoft Corp\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this \nsoftware and associated documentation files (the \"Software\"), to deal in the Software \nwithout restriction, including without limitation the rights to use, copy, modify, \nmerge, publish, distribute, sublicense, and/or sell copies of the Software, and to \npermit persons to whom the Software is furnished to do so, subject to the following \nconditions: \n\nThe above copyright notice and this permission notice shall be included in all copies \nor substantial portions of the Software.  \n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, \nINCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A \nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT \nHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF \nCONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE \nOR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n******************************************************************************/\n\n#pragma once\n\n#include \"math-defs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct half {\n\tuint16_t u;\n};\n\n/* adapted from DirectXMath XMConvertFloatToHalf */\nstatic inline struct half half_from_float(float f)\n{\n\tuint32_t Result;\n\n\tuint32_t IValue;\n\tmemcpy(&IValue, &f, sizeof(IValue));\n\tuint32_t Sign = (IValue & 0x80000000U) >> 16U;\n\tIValue = IValue & 0x7FFFFFFFU; // Hack off the sign\n\n\tif (IValue > 0x477FE000U) {\n\t\t// The number is too large to be represented as a half.  Saturate to infinity.\n\t\tif (((IValue & 0x7F800000) == 0x7F800000) && ((IValue & 0x7FFFFF) != 0)) {\n\t\t\tResult = 0x7FFF; // NAN\n\t\t} else {\n\t\t\tResult = 0x7C00U; // INF\n\t\t}\n\t} else if (!IValue) {\n\t\tResult = 0;\n\t} else {\n\t\tif (IValue < 0x38800000U) {\n\t\t\t// The number is too small to be represented as a normalized half.\n\t\t\t// Convert it to a denormalized value.\n\t\t\tuint32_t Shift = 113U - (IValue >> 23U);\n\t\t\tIValue = (0x800000U | (IValue & 0x7FFFFFU)) >> Shift;\n\t\t} else {\n\t\t\t// Rebias the exponent to represent the value as a normalized half.\n\t\t\tIValue += 0xC8000000U;\n\t\t}\n\n\t\tResult = ((IValue + 0x0FFFU + ((IValue >> 13U) & 1U)) >> 13U) & 0x7FFFU;\n\t}\n\n\tstruct half h;\n\th.u = (uint16_t)(Result | Sign);\n\treturn h;\n}\n\nstatic inline struct half half_from_bits(uint16_t u)\n{\n\tstruct half h;\n\th.u = u;\n\treturn h;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/image-file.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"image-file.h\"\n#include \"../util/base.h\"\n#include \"../util/platform.h\"\n#include \"../util/dstr.h\"\n#include \"vec4.h\"\n\n#define blog(level, format, ...) blog(level, \"%s: \" format, __FUNCTION__, __VA_ARGS__)\n\nstatic void *bi_def_bitmap_create(int width, int height)\n{\n\treturn bmalloc((size_t)4 * width * height);\n}\n\nstatic void bi_def_bitmap_set_opaque(void *bitmap, bool opaque)\n{\n\tUNUSED_PARAMETER(bitmap);\n\tUNUSED_PARAMETER(opaque);\n}\n\nstatic bool bi_def_bitmap_test_opaque(void *bitmap)\n{\n\tUNUSED_PARAMETER(bitmap);\n\treturn false;\n}\n\nstatic unsigned char *bi_def_bitmap_get_buffer(void *bitmap)\n{\n\treturn (unsigned char *)bitmap;\n}\n\nstatic void bi_def_bitmap_destroy(void *bitmap)\n{\n\tbfree(bitmap);\n}\n\nstatic void bi_def_bitmap_modified(void *bitmap)\n{\n\tUNUSED_PARAMETER(bitmap);\n}\n\nstatic inline int get_full_decoded_gif_size(gs_image_file_t *image)\n{\n\treturn image->gif.width * image->gif.height * 4 * image->gif.frame_count;\n}\n\nstatic inline void *alloc_mem(gs_image_file_t *image, uint64_t *mem_usage, size_t size)\n{\n\tUNUSED_PARAMETER(image);\n\n\tif (mem_usage)\n\t\t*mem_usage += size;\n\treturn bzalloc(size);\n}\n\nstatic bool init_animated_gif(gs_image_file_t *image, const char *path, uint64_t *mem_usage,\n\t\t\t      enum gs_image_alpha_mode alpha_mode)\n{\n\tbool is_animated_gif = true;\n\tgif_result result;\n\tuint64_t max_size;\n\tsize_t size, size_read;\n\tFILE *file;\n\n\timage->bitmap_callbacks.bitmap_create = bi_def_bitmap_create;\n\timage->bitmap_callbacks.bitmap_destroy = bi_def_bitmap_destroy;\n\timage->bitmap_callbacks.bitmap_get_buffer = bi_def_bitmap_get_buffer;\n\timage->bitmap_callbacks.bitmap_modified = bi_def_bitmap_modified;\n\timage->bitmap_callbacks.bitmap_set_opaque = bi_def_bitmap_set_opaque;\n\timage->bitmap_callbacks.bitmap_test_opaque = bi_def_bitmap_test_opaque;\n\n\tgif_create(&image->gif, &image->bitmap_callbacks);\n\n\tfile = os_fopen(path, \"rb\");\n\tif (!file) {\n\t\tblog(LOG_WARNING, \"Failed to open file '%s'\", path);\n\t\tgoto fail;\n\t}\n\n\tfseek(file, 0, SEEK_END);\n\tsize = (size_t)os_ftelli64(file);\n\tfseek(file, 0, SEEK_SET);\n\n\timage->gif_data = bmalloc(size);\n\tsize_read = fread(image->gif_data, 1, size, file);\n\tif (size_read != size) {\n\t\tblog(LOG_WARNING, \"Failed to fully read gif file '%s'.\", path);\n\t\tgoto fail;\n\t}\n\n\tdo {\n\t\tresult = gif_initialise(&image->gif, size, image->gif_data);\n\t\tif (result < 0) {\n\t\t\tblog(LOG_WARNING,\n\t\t\t     \"Failed to initialize gif '%s', \"\n\t\t\t     \"possible file corruption\",\n\t\t\t     path);\n\t\t\tgoto fail;\n\t\t}\n\t} while (result != GIF_OK);\n\n\tif (image->gif.width > 4096 || image->gif.height > 4096) {\n\t\tblog(LOG_WARNING, \"Bad texture dimensions (%dx%d) in '%s'\", image->gif.width, image->gif.height, path);\n\t\tgoto fail;\n\t}\n\n\tmax_size = (uint64_t)image->gif.width * (uint64_t)image->gif.height * (uint64_t)image->gif.frame_count * 4LLU;\n\n\tif ((uint64_t)get_full_decoded_gif_size(image) != max_size) {\n\t\tblog(LOG_WARNING, \"Gif '%s' overflowed maximum pointer size\", path);\n\t\tgoto fail;\n\t}\n\n\timage->is_animated_gif = (image->gif.frame_count > 1 && result >= 0);\n\tif (image->is_animated_gif) {\n\t\tgif_decode_frame(&image->gif, 0);\n\n\t\timage->animation_frame_cache = alloc_mem(image, mem_usage, image->gif.frame_count * sizeof(uint8_t *));\n\t\timage->animation_frame_data = alloc_mem(image, mem_usage, get_full_decoded_gif_size(image));\n\n\t\tfor (unsigned int i = 0; i < image->gif.frame_count; i++) {\n\t\t\tif (gif_decode_frame(&image->gif, i) != GIF_OK)\n\t\t\t\tblog(LOG_WARNING,\n\t\t\t\t     \"Couldn't decode frame %u \"\n\t\t\t\t     \"of '%s'\",\n\t\t\t\t     i, path);\n\t\t}\n\n\t\tgif_decode_frame(&image->gif, 0);\n\n\t\timage->cx = (uint32_t)image->gif.width;\n\t\timage->cy = (uint32_t)image->gif.height;\n\t\timage->format = GS_RGBA;\n\n\t\tif (mem_usage) {\n\t\t\t*mem_usage += (size_t)4 * image->cx * image->cy;\n\t\t\t*mem_usage += size;\n\t\t}\n\n\t\tif (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY_SRGB) {\n\t\t\tgs_premultiply_xyza_srgb_loop(image->gif.frame_image, (size_t)image->cx * image->cy);\n\t\t} else if (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY) {\n\t\t\tgs_premultiply_xyza_loop(image->gif.frame_image, (size_t)image->cx * image->cy);\n\t\t}\n\t} else {\n\t\tgif_finalise(&image->gif);\n\t\tbfree(image->gif_data);\n\t\timage->gif_data = NULL;\n\t\tis_animated_gif = false;\n\t\tgoto not_animated;\n\t}\n\n\timage->loaded = true;\n\nfail:\n\tif (!image->loaded)\n\t\tgs_image_file_free(image);\nnot_animated:\n\tif (file)\n\t\tfclose(file);\n\n\treturn is_animated_gif;\n}\n\nstatic void gs_image_file_init_internal(gs_image_file_t *image, const char *file, uint64_t *mem_usage,\n\t\t\t\t\tenum gs_color_space *space, enum gs_image_alpha_mode alpha_mode)\n{\n\tsize_t len;\n\n\tif (!image)\n\t\treturn;\n\n\tmemset(image, 0, sizeof(*image));\n\n\tif (!file)\n\t\treturn;\n\n\tlen = strlen(file);\n\n\tif (len > 4 && astrcmpi(file + len - 4, \".gif\") == 0) {\n\t\tif (init_animated_gif(image, file, mem_usage, alpha_mode)) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\timage->texture_data =\n\t\tgs_create_texture_file_data3(file, alpha_mode, &image->format, &image->cx, &image->cy, space);\n\n\tif (mem_usage) {\n\t\t*mem_usage += image->cx * image->cy * gs_get_format_bpp(image->format) / 8;\n\t}\n\n\timage->loaded = !!image->texture_data;\n\tif (!image->loaded) {\n\t\tblog(LOG_WARNING, \"Failed to load file '%s'\", file);\n\t\tgs_image_file_free(image);\n\t}\n}\n\nvoid gs_image_file_init(gs_image_file_t *image, const char *file)\n{\n\tenum gs_color_space unused;\n\tgs_image_file_init_internal(image, file, NULL, &unused, GS_IMAGE_ALPHA_STRAIGHT);\n}\n\nvoid gs_image_file_free(gs_image_file_t *image)\n{\n\tif (!image)\n\t\treturn;\n\n\tif (image->loaded) {\n\t\tif (image->is_animated_gif) {\n\t\t\tgif_finalise(&image->gif);\n\t\t\tbfree(image->animation_frame_cache);\n\t\t\tbfree(image->animation_frame_data);\n\t\t}\n\n\t\tgs_texture_destroy(image->texture);\n\t}\n\n\tbfree(image->texture_data);\n\tbfree(image->gif_data);\n\tmemset(image, 0, sizeof(*image));\n}\n\nvoid gs_image_file2_init(gs_image_file2_t *if2, const char *file)\n{\n\tenum gs_color_space unused;\n\tgs_image_file_init_internal(&if2->image, file, &if2->mem_usage, &unused, GS_IMAGE_ALPHA_STRAIGHT);\n}\n\nvoid gs_image_file3_init(gs_image_file3_t *if3, const char *file, enum gs_image_alpha_mode alpha_mode)\n{\n\tenum gs_color_space unused;\n\tgs_image_file_init_internal(&if3->image2.image, file, &if3->image2.mem_usage, &unused, alpha_mode);\n\tif3->alpha_mode = alpha_mode;\n}\n\nvoid gs_image_file4_init(gs_image_file4_t *if4, const char *file, enum gs_image_alpha_mode alpha_mode)\n{\n\tgs_image_file_init_internal(&if4->image3.image2.image, file, &if4->image3.image2.mem_usage, &if4->space,\n\t\t\t\t    alpha_mode);\n\tif4->image3.alpha_mode = alpha_mode;\n}\n\nvoid gs_image_file_init_texture(gs_image_file_t *image)\n{\n\tif (!image->loaded)\n\t\treturn;\n\n\tif (image->is_animated_gif) {\n\t\timage->texture = gs_texture_create(image->cx, image->cy, image->format, 1,\n\t\t\t\t\t\t   (const uint8_t **)&image->gif.frame_image, GS_DYNAMIC);\n\n\t} else {\n\t\timage->texture = gs_texture_create(image->cx, image->cy, image->format, 1,\n\t\t\t\t\t\t   (const uint8_t **)&image->texture_data, 0);\n\t\tbfree(image->texture_data);\n\t\timage->texture_data = NULL;\n\t}\n}\n\nstatic inline uint64_t get_time(gs_image_file_t *image, int i)\n{\n\tuint64_t val = (uint64_t)image->gif.frames[i].frame_delay * 10000000ULL;\n\tif (!val)\n\t\tval = 100000000;\n\treturn val;\n}\n\nstatic inline int calculate_new_frame(gs_image_file_t *image, uint64_t elapsed_time_ns, int loops)\n{\n\tint new_frame = image->cur_frame;\n\n\timage->cur_time += elapsed_time_ns;\n\tfor (;;) {\n\t\tuint64_t t = get_time(image, new_frame);\n\t\tif (image->cur_time <= t)\n\t\t\tbreak;\n\n\t\timage->cur_time -= t;\n\t\tif ((unsigned int)++new_frame == image->gif.frame_count) {\n\t\t\tif (!loops || ++image->cur_loop < loops) {\n\t\t\t\tnew_frame = 0;\n\t\t\t} else if (image->cur_loop == loops) {\n\t\t\t\tnew_frame--;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn new_frame;\n}\n\nstatic void decode_new_frame(gs_image_file_t *image, int new_frame, enum gs_image_alpha_mode alpha_mode)\n{\n\tif (!image->animation_frame_cache[new_frame]) {\n\t\tint last_frame;\n\n\t\t/* if looped, decode frame 0 */\n\t\tlast_frame = (new_frame < image->last_decoded_frame) ? 0 : image->last_decoded_frame + 1;\n\n\t\t/* decode missed frames */\n\t\tfor (int i = last_frame; i < new_frame; i++) {\n\t\t\tif (gif_decode_frame(&image->gif, i) != GIF_OK)\n\t\t\t\treturn;\n\t\t}\n\n\t\t/* decode actual desired frame */\n\t\tif (gif_decode_frame(&image->gif, new_frame) == GIF_OK) {\n\t\t\tconst size_t area = (size_t)image->gif.width * image->gif.height;\n\t\t\tsize_t pos = new_frame * area * 4;\n\t\t\timage->animation_frame_cache[new_frame] = image->animation_frame_data + pos;\n\n\t\t\tif (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY_SRGB) {\n\t\t\t\tgs_premultiply_xyza_srgb_loop(image->gif.frame_image, area);\n\t\t\t} else if (alpha_mode == GS_IMAGE_ALPHA_PREMULTIPLY) {\n\t\t\t\tgs_premultiply_xyza_loop(image->gif.frame_image, area);\n\t\t\t}\n\n\t\t\tmemcpy(image->animation_frame_cache[new_frame], image->gif.frame_image, area * 4);\n\n\t\t\timage->last_decoded_frame = new_frame;\n\t\t}\n\t}\n\n\timage->cur_frame = new_frame;\n}\n\nstatic bool gs_image_file_tick_internal(gs_image_file_t *image, uint64_t elapsed_time_ns,\n\t\t\t\t\tenum gs_image_alpha_mode alpha_mode)\n{\n\tint loops;\n\n\tif (!image->is_animated_gif || !image->loaded)\n\t\treturn false;\n\n\tloops = image->gif.loop_count;\n\tif (loops >= 0xFFFF)\n\t\tloops = 0;\n\n\tif (!loops || image->cur_loop < loops) {\n\t\tint new_frame = calculate_new_frame(image, elapsed_time_ns, loops);\n\n\t\tif (new_frame != image->cur_frame) {\n\t\t\tdecode_new_frame(image, new_frame, alpha_mode);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nbool gs_image_file_tick(gs_image_file_t *image, uint64_t elapsed_time_ns)\n{\n\treturn gs_image_file_tick_internal(image, elapsed_time_ns, false);\n}\n\nbool gs_image_file2_tick(gs_image_file2_t *if2, uint64_t elapsed_time_ns)\n{\n\treturn gs_image_file_tick_internal(&if2->image, elapsed_time_ns, false);\n}\n\nbool gs_image_file3_tick(gs_image_file3_t *if3, uint64_t elapsed_time_ns)\n{\n\treturn gs_image_file_tick_internal(&if3->image2.image, elapsed_time_ns, if3->alpha_mode);\n}\n\nbool gs_image_file4_tick(gs_image_file4_t *if4, uint64_t elapsed_time_ns)\n{\n\treturn gs_image_file_tick_internal(&if4->image3.image2.image, elapsed_time_ns, if4->image3.alpha_mode);\n}\n\nstatic void gs_image_file_update_texture_internal(gs_image_file_t *image, enum gs_image_alpha_mode alpha_mode)\n{\n\tif (!image->is_animated_gif || !image->loaded)\n\t\treturn;\n\n\tif (!image->animation_frame_cache[image->cur_frame])\n\t\tdecode_new_frame(image, image->cur_frame, alpha_mode);\n\n\tgs_texture_set_image(image->texture, image->animation_frame_cache[image->cur_frame], image->gif.width * 4,\n\t\t\t     false);\n}\n\nvoid gs_image_file_update_texture(gs_image_file_t *image)\n{\n\tgs_image_file_update_texture_internal(image, false);\n}\n\nvoid gs_image_file2_update_texture(gs_image_file2_t *if2)\n{\n\tgs_image_file_update_texture_internal(&if2->image, false);\n}\n\nvoid gs_image_file3_update_texture(gs_image_file3_t *if3)\n{\n\tgs_image_file_update_texture_internal(&if3->image2.image, if3->alpha_mode);\n}\n\nvoid gs_image_file4_update_texture(gs_image_file4_t *if4)\n{\n\tgs_image_file_update_texture_internal(&if4->image3.image2.image, if4->image3.alpha_mode);\n}\n"
  },
  {
    "path": "libobs/graphics/image-file.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"graphics.h\"\n#include \"libnsgif/libnsgif.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct gs_image_file {\n\tgs_texture_t *texture;\n\tenum gs_color_format format;\n\tuint32_t cx;\n\tuint32_t cy;\n\tbool is_animated_gif;\n\tbool frame_updated;\n\tbool loaded;\n\n\tgif_animation gif;\n\tuint8_t *gif_data;\n\tuint8_t **animation_frame_cache;\n\tuint8_t *animation_frame_data;\n\tuint64_t cur_time;\n\tint cur_frame;\n\tint cur_loop;\n\tint last_decoded_frame;\n\n\tuint8_t *texture_data;\n\tgif_bitmap_callback_vt bitmap_callbacks;\n};\n\nstruct gs_image_file2 {\n\tstruct gs_image_file image;\n\tuint64_t mem_usage;\n};\n\nstruct gs_image_file3 {\n\tstruct gs_image_file2 image2;\n\tenum gs_image_alpha_mode alpha_mode;\n};\n\nstruct gs_image_file4 {\n\tstruct gs_image_file3 image3;\n\tenum gs_color_space space;\n};\n\ntypedef struct gs_image_file gs_image_file_t;\ntypedef struct gs_image_file2 gs_image_file2_t;\ntypedef struct gs_image_file3 gs_image_file3_t;\ntypedef struct gs_image_file4 gs_image_file4_t;\n\nEXPORT void gs_image_file_init(gs_image_file_t *image, const char *file);\nEXPORT void gs_image_file_free(gs_image_file_t *image);\n\nEXPORT void gs_image_file_init_texture(gs_image_file_t *image);\nEXPORT bool gs_image_file_tick(gs_image_file_t *image, uint64_t elapsed_time_ns);\nEXPORT void gs_image_file_update_texture(gs_image_file_t *image);\n\nEXPORT void gs_image_file2_init(gs_image_file2_t *if2, const char *file);\n\nEXPORT bool gs_image_file2_tick(gs_image_file2_t *if2, uint64_t elapsed_time_ns);\nEXPORT void gs_image_file2_update_texture(gs_image_file2_t *if2);\n\nEXPORT void gs_image_file3_init(gs_image_file3_t *if3, const char *file, enum gs_image_alpha_mode alpha_mode);\n\nEXPORT bool gs_image_file3_tick(gs_image_file3_t *if3, uint64_t elapsed_time_ns);\nEXPORT void gs_image_file3_update_texture(gs_image_file3_t *if3);\n\nEXPORT void gs_image_file4_init(gs_image_file4_t *if4, const char *file, enum gs_image_alpha_mode alpha_mode);\n\nEXPORT bool gs_image_file4_tick(gs_image_file4_t *if4, uint64_t elapsed_time_ns);\nEXPORT void gs_image_file4_update_texture(gs_image_file4_t *if4);\n\nstatic inline void gs_image_file2_free(gs_image_file2_t *if2)\n{\n\tgs_image_file_free(&if2->image);\n\tif2->mem_usage = 0;\n}\n\nstatic inline void gs_image_file2_init_texture(gs_image_file2_t *if2)\n{\n\tgs_image_file_init_texture(&if2->image);\n}\n\nstatic inline void gs_image_file3_free(gs_image_file3_t *if3)\n{\n\tgs_image_file2_free(&if3->image2);\n}\n\nstatic inline void gs_image_file3_init_texture(gs_image_file3_t *if3)\n{\n\tgs_image_file2_init_texture(&if3->image2);\n}\n\nstatic inline void gs_image_file4_free(gs_image_file4_t *if4)\n{\n\tgs_image_file3_free(&if4->image3);\n}\n\nstatic inline void gs_image_file4_init_texture(gs_image_file4_t *if4)\n{\n\tgs_image_file3_init_texture(&if4->image3);\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/input.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n/* TODO: incomplete/may not be necessary */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* wrapped opaque data types */\nstruct input_subsystem;\ntypedef struct input_subsystem input_t;\n\nEXPORT int input_getbuttonstate(input_t *input, uint32_t button);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/libnsgif/.clang-format",
    "content": "Language: Cpp\nSortIncludes: false\nDisableFormat: true\n"
  },
  {
    "path": "libobs/graphics/libnsgif/LICENSE.libnsgif",
    "content": "libnsgif is licensed under the MIT license. The licensing statement\nand the full license are reproduced below.\n\n/*\n * Copyright 2003 James Bursa <bursa@users.sourceforge.net>\n * Copyright 2004 John Tytgat <John.Tytgat@aaug.net>\n * Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>\n * Copyright 2008 Sean Fox <dyntryx@gmail.com>\n *\n * This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/\n * Licenced under the MIT License,\n *                http://www.opensource.org/licenses/mit-license.php\n */\n\nThe MIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "libobs/graphics/libnsgif/libnsgif.c",
    "content": "/*\n * Copyright 2003 James Bursa <bursa@users.sourceforge.net>\n * Copyright 2004 John Tytgat <John.Tytgat@aaug.net>\n * Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>\n * Copyright 2008 Sean Fox <dyntryx@gmail.com>\n *\n * This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/\n * Licenced under the MIT License,\n *                http://www.opensource.org/licenses/mit-license.php\n */\n\n//#include <stdbool.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <stdio.h>\n#include \"libnsgif.h\"\n\n\n#ifdef NDEBUG\n#  define LOG(x) ((void) 0)\n#else\n#  define LOG(x) do { fprintf(stderr, x), fputc('\\n', stderr); } while (0)\n#endif /* NDEBUG */\n\n\n\n/*    READING GIF FILES\n    =================\n\n    The functions provided by this file allow for efficient progressive GIF\n    decoding. Whilst the initialisation does not ensure that there is\n    sufficient image data to complete the entire frame, it does ensure that\n    the information provided is valid. Any subsequent attempts to decode an\n    initialised GIF are guaranteed to succeed, and any bytes of the image\n    not present are assumed to be totally transparent.\n\n    To begin decoding a GIF, the 'gif' structure must be initialised with\n    the 'gif_data' and 'buffer_size' set to their initial values. The\n    'buffer_position' should initially be 0, and will be internally updated\n    as the decoding commences. The caller should then repeatedly call\n    gif_initialise() with the structure until the function returns 1, or\n    no more data is available.\n\n    Once the initialisation has begun, the decoder completes the variables\n    'frame_count' and 'frame_count_partial'. The former being the total\n    number of frames that have been successfully initialised, and the\n    latter being the number of frames that a partial amount of data is\n    available for. This assists the caller in managing the animation whilst\n    decoding is continuing.\n\n    To decode a frame, the caller must use gif_decode_frame() which updates\n    the current 'frame_image' to reflect the desired frame. The required\n    'disposal_method' is also updated to reflect how the frame should be\n    plotted. The caller must not assume that the current 'frame_image' will\n    be valid between calls if initialisation is still occurring, and should\n    either always request that the frame is decoded (no processing will\n    occur if the 'decoded_frame' has not been invalidated by initialisation)\n    or perform the check itself.\n\n    It should be noted that gif_finalise() should always be called, even if\n    no frames were initialised.  Additionally, it is the responsibility of\n    the caller to free 'gif_data'.\n\n    [rjw] - Fri 2nd April 2004\n*/\n\n/*    TO-DO LIST\n    =================\n\n    + Plain text and comment extensions could be implemented if there is any\n    interest in doing so.\n*/\n\n\n\n\n/*    Maximum colour table size\n*/\n#define GIF_MAX_COLOURS 256\n\n/*    Internal flag that the colour table needs to be processed\n*/\n#define GIF_PROCESS_COLOURS 0xaa000000\n\n/*    Internal flag that a frame is invalid/unprocessed\n*/\n#define GIF_INVALID_FRAME -1\n\n/* Transparent colour\n*/\n#define GIF_TRANSPARENT_COLOUR 0x00\n\n/*    GIF Flags\n*/\n#define GIF_FRAME_COMBINE 1\n#define GIF_FRAME_CLEAR 2\n#define GIF_FRAME_RESTORE 3\n#define GIF_FRAME_QUIRKS_RESTORE 4\n#define GIF_IMAGE_SEPARATOR 0x2c\n#define GIF_INTERLACE_MASK 0x40\n#define GIF_COLOUR_TABLE_MASK 0x80\n#define GIF_COLOUR_TABLE_SIZE_MASK 0x07\n#define GIF_EXTENSION_INTRODUCER 0x21\n#define GIF_EXTENSION_GRAPHIC_CONTROL 0xf9\n#define GIF_DISPOSAL_MASK 0x1c\n#define GIF_TRANSPARENCY_MASK 0x01\n#define GIF_EXTENSION_COMMENT 0xfe\n#define GIF_EXTENSION_PLAIN_TEXT 0x01\n#define GIF_EXTENSION_APPLICATION 0xff\n#define GIF_BLOCK_TERMINATOR 0x00\n#define GIF_TRAILER 0x3b\n\n/*    Internal GIF routines\n*/\nstatic gif_result gif_initialise_sprite(gif_animation *gif, unsigned int width, unsigned int height);\nstatic gif_result gif_initialise_frame(gif_animation *gif);\nstatic gif_result gif_initialise_frame_extensions(gif_animation *gif, const int frame);\nstatic gif_result gif_skip_frame_extensions(gif_animation *gif);\nstatic unsigned int gif_interlaced_line(int height, int y);\n\n\n\n/*    Internal LZW routines\n*/\nstatic void gif_init_LZW(gif_animation *gif);\nstatic bool gif_next_LZW(gif_animation *gif);\nstatic int gif_next_code(gif_animation *gif, int code_size);\n\nstatic const int maskTbl[16] = {0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f,\n                0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff};\n\n/**    Initialises necessary gif_animation members.\n*/\nvoid gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks) {\n    memset(gif, 0, sizeof(gif_animation));\n    gif->bitmap_callbacks = *bitmap_callbacks;\n    gif->decoded_frame = GIF_INVALID_FRAME;\n}\n\n\n/**    Initialises any workspace held by the animation and attempts to decode\n    any information that hasn't already been decoded.\n    If an error occurs, all previously decoded frames are retained.\n\n    @return GIF_FRAME_DATA_ERROR for GIF frame data error\n        GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process\n                  any more frames\n        GIF_INSUFFICIENT_MEMORY for memory error\n        GIF_DATA_ERROR for GIF error\n        GIF_INSUFFICIENT_DATA for insufficient data to do anything\n        GIF_OK for successful decoding\n        GIF_WORKING for successful decoding if more frames are expected\n*/\ngif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data) {\n    unsigned char *gif_data;\n    unsigned int index;\n    gif_result return_value;\n\n    /*     The GIF format is thoroughly documented; a full description\n     *    can be found at http://www.w3.org/Graphics/GIF/spec-gif89a.txt\n    */\n\n    /*    Initialize values\n    */\n    gif->buffer_size = (unsigned int)size;\n    gif->gif_data = data;\n    \n    /*    Check for sufficient data to be a GIF (6-byte header + 7-byte logical screen descriptor)\n    */\n    if (gif->buffer_size < 13) return GIF_INSUFFICIENT_DATA;\n\n    /*    Get our current processing position\n    */\n    gif_data = gif->gif_data + gif->buffer_position;\n\n    /*    See if we should initialise the GIF\n    */\n    if (gif->buffer_position == 0) {\n\n        /*    We want everything to be NULL before we start so we've no chance\n            of freeing bad pointers (paranoia)\n        */\n        gif->frame_image = NULL;\n        gif->frames = NULL;\n        gif->local_colour_table = NULL;\n        gif->global_colour_table = NULL;\n\n        /*    The caller may have been lazy and not reset any values\n        */\n        gif->frame_count = 0;\n        gif->frame_count_partial = 0;\n        gif->decoded_frame = GIF_INVALID_FRAME;\n\n        /* 6-byte GIF file header is:\n         *\n         *    +0    3CHARS    Signature ('GIF')\n         *    +3    3CHARS    Version ('87a' or '89a')\n         */\n        if (strncmp((const char *) gif_data, \"GIF\", 3) != 0)\n            return GIF_DATA_ERROR;\n        gif_data += 3;\n\n        /*    Ensure GIF reports version 87a or 89a\n        */\n/*        if ((strncmp(gif_data, \"87a\", 3) != 0) &&\n                (strncmp(gif_data, \"89a\", 3) != 0))\n            LOG((\"Unknown GIF format - proceeding anyway\"));\n*/        gif_data += 3;\n\n        /* 7-byte Logical Screen Descriptor is:\n         *\n         *    +0    SHORT    Logical Screen Width\n         *    +2    SHORT    Logical Screen Height\n         *    +4    CHAR    __Packed Fields__\n         *             1BIT    Global Colour Table Flag\n         *             3BITS    Colour Resolution\n         *             1BIT    Sort Flag\n         *             3BITS    Size of Global Colour Table\n         *    +5    CHAR    Background Colour Index\n         *    +6    CHAR    Pixel Aspect Ratio\n         */\n        gif->width = gif_data[0] | (gif_data[1] << 8);\n        gif->height = gif_data[2] | (gif_data[3] << 8);\n        gif->global_colours = (gif_data[4] & GIF_COLOUR_TABLE_MASK);\n        gif->colour_table_size = (2 << (gif_data[4] & GIF_COLOUR_TABLE_SIZE_MASK));\n        gif->background_index = gif_data[5];\n        gif->aspect_ratio = gif_data[6];\n        gif->loop_count = 1;\n        gif_data += 7;\n\n        /*    Some broken GIFs report the size as the screen size they were created in. As\n            such, we detect for the common cases and set the sizes as 0 if they are found\n            which results in the GIF being the maximum size of the frames.\n        */\n        if (((gif->width == 640) && (gif->height == 480)) ||\n                ((gif->width == 640) && (gif->height == 512)) ||\n                ((gif->width == 800) && (gif->height == 600)) ||\n                ((gif->width == 1024) && (gif->height == 768)) ||\n                ((gif->width == 1280) && (gif->height == 1024)) ||\n                ((gif->width == 1600) && (gif->height == 1200)) ||\n                ((gif->width == 0) || (gif->height == 0)) ||\n                ((gif->width > 2048) || (gif->height > 2048))) {\n            gif->width = 1;\n            gif->height = 1;\n        }\n\n        /*    Allocate some data irrespective of whether we've got any colour tables. We\n            always get the maximum size in case a GIF is lying to us. It's far better\n            to give the wrong colours than to trample over some memory somewhere.\n        */\n        gif->global_colour_table = (unsigned int *)calloc(GIF_MAX_COLOURS, sizeof(int));\n        gif->local_colour_table = (unsigned int *)calloc(GIF_MAX_COLOURS, sizeof(int));\n        if ((gif->global_colour_table == NULL) || (gif->local_colour_table == NULL)) {\n            gif_finalise(gif);\n            return GIF_INSUFFICIENT_MEMORY;\n        }\n\n        /*    Set the first colour to a value that will never occur in reality so we\n            know if we've processed it\n        */\n        gif->global_colour_table[0] = GIF_PROCESS_COLOURS;\n        \n        /*    Check if the GIF has no frame data (13-byte header + 1-byte termination block)\n         *    Although generally useless, the GIF specification does not expressly prohibit this\n         */\n        if (gif->buffer_size == 14) {\n            if (gif_data[0] == GIF_TRAILER)\n                return GIF_OK;\n            else\n                return GIF_INSUFFICIENT_DATA;\n        }\n\n        /*    Initialise enough workspace for 4 frames initially\n        */\n        if ((gif->frames = (gif_frame *)malloc(sizeof(gif_frame))) == NULL) {\n            gif_finalise(gif);\n            return GIF_INSUFFICIENT_MEMORY;\n        }\n        gif->frame_holders = 1;\n\n        /*    Initialise the sprite header\n        */\n        assert(gif->bitmap_callbacks.bitmap_create);\n        if ((gif->frame_image = gif->bitmap_callbacks.bitmap_create(gif->width, gif->height)) == NULL) {\n            gif_finalise(gif);\n            return GIF_INSUFFICIENT_MEMORY;\n        }\n\n        /*    Remember we've done this now\n        */\n        gif->buffer_position = (unsigned int)(gif_data - gif->gif_data);\n    }\n\n    /*    Do the colour map if we haven't already. As the top byte is always 0xff or 0x00\n        depending on the transparency we know if it's been filled in.\n    */\n    if (gif->global_colour_table[0] == GIF_PROCESS_COLOURS) {\n        /*    Check for a global colour map signified by bit 7\n        */\n        if (gif->global_colours) {\n            if (gif->buffer_size < (gif->colour_table_size * 3 + 12)) {\n                return GIF_INSUFFICIENT_DATA;\n            }\n            for (index = 0; index < gif->colour_table_size; index++) {\n                /* Gif colour map contents are r,g,b.\n                 *\n                 * We want to pack them bytewise into the \n                 * colour table, such that the red component\n                 * is in byte 0 and the alpha component is in\n                 * byte 3.\n                 */\n                unsigned char *entry = (unsigned char *) &gif->\n                        global_colour_table[index];\n\n                entry[0] = gif_data[0];    /* r */\n                entry[1] = gif_data[1];    /* g */\n                entry[2] = gif_data[2];    /* b */\n                entry[3] = 0xff;    /* a */\n\n                gif_data += 3;\n            }\n            gif->buffer_position = (unsigned int)(gif_data - gif->gif_data);\n        } else {\n            /*    Create a default colour table with the first two colours as black and white\n            */\n            unsigned int *entry = gif->global_colour_table;\n\n            entry[0] = 0x00000000;\n            /* Force Alpha channel to opaque */\n            ((unsigned char *) entry)[3] = 0xff;\n\n            entry[1] = 0xffffffff;\n        }\n    }\n\n    /*    Repeatedly try to initialise frames\n    */\n    while ((return_value = gif_initialise_frame(gif)) == GIF_WORKING);\n\n    /*    If there was a memory error tell the caller\n    */\n    if ((return_value == GIF_INSUFFICIENT_MEMORY) ||\n            (return_value == GIF_DATA_ERROR))\n        return return_value;\n\n    /*    If we didn't have some frames then a GIF_INSUFFICIENT_DATA becomes a\n        GIF_INSUFFICIENT_FRAME_DATA\n    */\n    if ((return_value == GIF_INSUFFICIENT_DATA) && (gif->frame_count_partial > 0))\n        return GIF_INSUFFICIENT_FRAME_DATA;\n\n    /*    Return how many we got\n    */\n    return return_value;\n}\n\n\n/**    Updates the sprite memory size\n\n    @return GIF_INSUFFICIENT_MEMORY for a memory error\n        GIF_OK for success\n*/\nstatic gif_result gif_initialise_sprite(gif_animation *gif, unsigned int width, unsigned int height) {\n    unsigned int max_width;\n    unsigned int max_height;\n    struct bitmap *buffer;\n\n    /*    Check if we've changed\n    */\n    if ((width <= gif->width) && (height <= gif->height))\n        return GIF_OK;\n\n    /*    Get our maximum values\n    */\n    max_width = (width > gif->width) ? width : gif->width;\n    max_height = (height > gif->height) ? height : gif->height;\n\n    /*    Allocate some more memory\n    */\n    assert(gif->bitmap_callbacks.bitmap_create);\n    if ((buffer = gif->bitmap_callbacks.bitmap_create(max_width, max_height)) == NULL)\n        return GIF_INSUFFICIENT_MEMORY;\n    assert(gif->bitmap_callbacks.bitmap_destroy);\n    gif->bitmap_callbacks.bitmap_destroy(gif->frame_image);\n    gif->frame_image = buffer;\n    gif->width = max_width;\n    gif->height = max_height;\n\n    /*    Invalidate our currently decoded image\n    */\n    gif->decoded_frame = GIF_INVALID_FRAME;\n    return GIF_OK;\n}\n\n\n/**    Attempts to initialise the next frame\n\n    @return GIF_INSUFFICIENT_DATA for insufficient data to do anything\n        GIF_FRAME_DATA_ERROR for GIF frame data error\n        GIF_INSUFFICIENT_MEMORY for insufficient memory to process\n        GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame\n        GIF_DATA_ERROR for GIF error (invalid frame header)\n        GIF_OK for successful decoding\n        GIF_WORKING for successful decoding if more frames are expected\n*/\nstatic gif_result gif_initialise_frame(gif_animation *gif) {\n    int frame;\n    gif_frame *temp_buf;\n\n    unsigned char *gif_data, *gif_end;\n    int gif_bytes;\n    unsigned int flags = 0;\n    unsigned int width, height, offset_x, offset_y;\n    unsigned int block_size, colour_table_size;\n    bool first_image = true;\n    gif_result return_value;\n\n    /*    Get the frame to decode and our data position\n    */\n    frame = gif->frame_count;\n\n    /*    Get our buffer position etc.\n    */\n    gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);\n    gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);\n    gif_bytes = (unsigned int)(gif_end - gif_data);\n\n    /*    Check if we've finished\n    */\n    if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) return GIF_OK;\n    \n    /*    Check if we have enough data\n     *    The shortest block of data is a 4-byte comment extension + 1-byte block terminator + 1-byte gif trailer\n    */\n    if (gif_bytes < 6) return GIF_INSUFFICIENT_DATA;\n\n    /*    We could theoretically get some junk data that gives us millions of frames, so\n        we ensure that we don't have a silly number\n    */\n    if (frame > 4096) return GIF_FRAME_DATA_ERROR;\n\n    /*    Get some memory to store our pointers in etc.\n    */\n    if ((int)gif->frame_holders <= frame) {\n        /*    Allocate more memory\n        */\n        if ((temp_buf = (gif_frame *)realloc(gif->frames,\n                    (frame + 1) * sizeof(gif_frame))) == NULL)\n            return GIF_INSUFFICIENT_MEMORY;\n        gif->frames = temp_buf;\n        gif->frame_holders = frame + 1;\n    }\n\n    /*    Store our frame pointer. We would do it when allocating except we\n        start off with one frame allocated so we can always use realloc.\n    */\n    gif->frames[frame].frame_pointer = gif->buffer_position;\n    gif->frames[frame].display = false;\n    gif->frames[frame].virgin = true;\n    gif->frames[frame].disposal_method = 0;\n    gif->frames[frame].transparency = false;\n    gif->frames[frame].frame_delay = 100;\n    gif->frames[frame].redraw_required = false;\n\n    /*    Invalidate any previous decoding we have of this frame\n    */\n    if (gif->decoded_frame == frame)\n        gif->decoded_frame = GIF_INVALID_FRAME;\n\n    /*    We pretend to initialise the frames, but really we just skip over all\n        the data contained within. This is all basically a cut down version of\n        gif_decode_frame that doesn't have any of the LZW bits in it.\n    */\n\n    /*    Initialise any extensions\n    */\n    gif->buffer_position = (unsigned int)(gif_data - gif->gif_data);\n    if ((return_value = gif_initialise_frame_extensions(gif, frame)) != GIF_OK)\n        return return_value;\n    gif_data = (gif->gif_data + gif->buffer_position);\n    gif_bytes = (unsigned int)(gif_end - gif_data);\n\n    /*    Check if we've finished\n    */\n    if ((gif_bytes = (unsigned int)(gif_end - gif_data)) < 1)\n        return GIF_INSUFFICIENT_FRAME_DATA;\n    else if (gif_data[0] == GIF_TRAILER) {\n        gif->buffer_position = (unsigned int)(gif_data - gif->gif_data);\n        gif->frame_count = frame + 1;\n        return GIF_OK;\n    }\n\n    /*    If we're not done, there should be an image descriptor\n    */\n    if (gif_data[0] != GIF_IMAGE_SEPARATOR) return GIF_FRAME_DATA_ERROR;\n\n    /*    Do some simple boundary checking\n    */\n    offset_x = gif_data[1] | (gif_data[2] << 8);\n    offset_y = gif_data[3] | (gif_data[4] << 8);\n    width = gif_data[5] | (gif_data[6] << 8);\n    height = gif_data[7] | (gif_data[8] << 8);\n\n    /*    Set up the redraw characteristics. We have to check for extending the area\n        due to multi-image frames.\n    */\n    if (!first_image) {\n        if (gif->frames[frame].redraw_x > offset_x) {\n            gif->frames[frame].redraw_width += (gif->frames[frame].redraw_x - offset_x);\n            gif->frames[frame].redraw_x = offset_x;\n        }\n        if (gif->frames[frame].redraw_y > offset_y) {\n            gif->frames[frame].redraw_height += (gif->frames[frame].redraw_y - offset_y);\n            gif->frames[frame].redraw_y = offset_y;\n        }\n        if ((offset_x + width) > (gif->frames[frame].redraw_x + gif->frames[frame].redraw_width))\n            gif->frames[frame].redraw_width = (offset_x + width) - gif->frames[frame].redraw_x;\n        if ((offset_y + height) > (gif->frames[frame].redraw_y + gif->frames[frame].redraw_height))\n            gif->frames[frame].redraw_height = (offset_y + height) - gif->frames[frame].redraw_y;\n    } else {\n        first_image = false;\n        gif->frames[frame].redraw_x = offset_x;\n        gif->frames[frame].redraw_y = offset_y;\n        gif->frames[frame].redraw_width = width;\n        gif->frames[frame].redraw_height = height;\n    }\n\n    /*    if we are clearing the background then we need to redraw enough to cover the previous\n        frame too\n    */\n    gif->frames[frame].redraw_required = ((gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) ||\n                        (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE));\n\n    /*    Boundary checking - shouldn't ever happen except with junk data\n    */\n    if (gif_initialise_sprite(gif, (offset_x + width), (offset_y + height)))\n        return GIF_INSUFFICIENT_MEMORY;\n\n    /*    Decode the flags\n    */\n    flags = gif_data[9];\n    colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);\n\n    /*    Move our data onwards and remember we've got a bit of this frame\n    */\n    gif_data += 10;\n    gif_bytes = (unsigned int)(gif_end - gif_data);\n    gif->frame_count_partial = frame + 1;\n\n    /*    Skip the local colour table\n    */\n    if (flags & GIF_COLOUR_TABLE_MASK) {\n        gif_data += 3 * colour_table_size;\n        if ((gif_bytes = (unsigned int)(gif_end - gif_data)) < 0)\n            return GIF_INSUFFICIENT_FRAME_DATA;\n    }\n\n    /*    Ensure we have a correct code size\n    */\n    if (gif_data[0] > GIF_MAX_LZW)\n        return GIF_DATA_ERROR;\n\n    /*    Move our pointer to the actual image data\n    */\n    gif_data++;\n    if (--gif_bytes < 0)\n        return GIF_INSUFFICIENT_FRAME_DATA;\n\n    /*    Repeatedly skip blocks until we get a zero block or run out of data\n     *    These blocks of image data are processed later by gif_decode_frame()\n    */\n    block_size = 0;\n    while (block_size != 1) {\n        block_size = gif_data[0] + 1;\n        /*    Check if the frame data runs off the end of the file\n        */\n        if ((int)(gif_bytes - block_size) < 0) {\n            /*    Try to recover by signaling the end of the gif.\n             *    Once we get garbage data, there is no logical\n             *    way to determine where the next frame is.\n             *    It's probably better to partially load the gif\n             *    than not at all.\n            */\n            if (gif_bytes >= 2) {\n                gif_data[0] = 0;\n                gif_data[1] = GIF_TRAILER;\n                gif_bytes = 1;\n                ++gif_data;\n                break;\n            } else\n                return GIF_INSUFFICIENT_FRAME_DATA;\n        } else {\n            gif_bytes -= block_size;\n            gif_data += block_size;\n        }\n    }\n\n    /*    Add the frame and set the display flag\n    */\n    gif->buffer_position = (unsigned int)(gif_data - gif->gif_data);\n    gif->frame_count = frame + 1;\n    gif->frames[frame].display = true;\n\n    /*    Check if we've finished\n    */\n    if (gif_bytes < 1)\n        return GIF_INSUFFICIENT_FRAME_DATA;\n    else\n        if (gif_data[0] == GIF_TRAILER) return GIF_OK;\n    return GIF_WORKING;\n}\n\n/**    Attempts to initialise the frame's extensions\n\n    @return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame\n        GIF_OK for successful initialisation\n*/\nstatic gif_result gif_initialise_frame_extensions(gif_animation *gif, const int frame) {\n    unsigned char *gif_data, *gif_end;\n    int gif_bytes;\n    unsigned int block_size;\n\n    /*    Get our buffer position etc.\n    */\n    gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);\n    gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);\n    \n    /*    Initialise the extensions\n    */\n    while (gif_data[0] == GIF_EXTENSION_INTRODUCER) {\n        ++gif_data;\n        gif_bytes = (unsigned int)(gif_end - gif_data);\n\n        /*    Switch on extension label\n        */\n        switch(gif_data[0]) {\n            /* 6-byte Graphic Control Extension is:\n             *\n             *    +0    CHAR    Graphic Control Label\n             *    +1    CHAR    Block Size\n             *    +2    CHAR    __Packed Fields__\n             *            3BITS    Reserved\n             *            3BITS    Disposal Method\n             *            1BIT    User Input Flag\n             *            1BIT    Transparent Color Flag\n             *    +3    SHORT    Delay Time\n             *    +5    CHAR    Transparent Color Index\n            */\n            case GIF_EXTENSION_GRAPHIC_CONTROL:\n                if (gif_bytes < 6) return GIF_INSUFFICIENT_FRAME_DATA;\n                gif->frames[frame].frame_delay = gif_data[3] | (gif_data[4] << 8);\n                if (gif_data[2] & GIF_TRANSPARENCY_MASK) {\n                    gif->frames[frame].transparency = true;\n                    gif->frames[frame].transparency_index = gif_data[5];\n                }\n                gif->frames[frame].disposal_method = ((gif_data[2] & GIF_DISPOSAL_MASK) >> 2);\n                /*    I have encountered documentation and GIFs in the wild that use\n                 *    0x04 to restore the previous frame, rather than the officially\n                 *    documented 0x03.  I believe some (older?) software may even actually\n                 *    export this way.  We handle this as a type of \"quirks\" mode.\n                */\n                if (gif->frames[frame].disposal_method == GIF_FRAME_QUIRKS_RESTORE)\n                    gif->frames[frame].disposal_method = GIF_FRAME_RESTORE;\n                gif_data += (2 + gif_data[1]);\n                break;\n\n            /* 14-byte+ Application Extension is:\n             *\n             *    +0    CHAR    Application Extension Label\n             *    +1    CHAR    Block Size\n             *    +2    8CHARS    Application Identifier\n             *    +10    3CHARS    Appl. Authentication Code\n             *    +13    1-256    Application Data (Data sub-blocks)\n            */\n            case GIF_EXTENSION_APPLICATION:\n                if (gif_bytes < 17) return GIF_INSUFFICIENT_FRAME_DATA;\n                if ((gif_data[1] == 0x0b) &&\n                    (strncmp((const char *) gif_data + 2,\n                        \"NETSCAPE2.0\", 11) == 0) &&\n                    (gif_data[13] == 0x03) &&\n                    (gif_data[14] == 0x01)) {\n                        gif->loop_count = gif_data[15] | (gif_data[16] << 8);\n                }\n                gif_data += (2 + gif_data[1]);\n                break;\n\n            /*    Move the pointer to the first data sub-block\n             *    Skip 1 byte for the extension label\n            */\n            case GIF_EXTENSION_COMMENT:\n                ++gif_data;\n                break;\n\n            /*    Move the pointer to the first data sub-block\n             *    Skip 2 bytes for the extension label and size fields\n             *    Skip the extension size itself\n            */\n            default:\n                gif_data += (2 + gif_data[1]);\n        }\n\n        /*    Repeatedly skip blocks until we get a zero block or run out of data\n         *    This data is ignored by this gif decoder\n        */\n        gif_bytes = (unsigned int)(gif_end - gif_data);\n        block_size = 0;\n        while (gif_data[0] != GIF_BLOCK_TERMINATOR) {\n            block_size = gif_data[0] + 1;\n            if ((gif_bytes -= block_size) < 0)\n                return GIF_INSUFFICIENT_FRAME_DATA;\n            gif_data += block_size;\n        }\n        ++gif_data;\n    }\n\n    /*    Set buffer position and return\n    */\n    gif->buffer_position = (unsigned int)(gif_data - gif->gif_data);\n    return GIF_OK;\n}\n\n\n/**    Decodes a GIF frame.\n\n    @return GIF_FRAME_DATA_ERROR for GIF frame data error\n        GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame\n        GIF_DATA_ERROR for GIF error (invalid frame header)\n        GIF_INSUFFICIENT_DATA for insufficient data to do anything\n        GIF_INSUFFICIENT_MEMORY for insufficient memory to process\n        GIF_OK for successful decoding\n        If a frame does not contain any image data, GIF_OK is returned and\n            gif->current_error is set to GIF_FRAME_NO_DISPLAY\n*/\ngif_result gif_decode_frame(gif_animation *gif, unsigned int frame) {\n    unsigned int index = 0;\n    unsigned char *gif_data, *gif_end;\n    int gif_bytes;\n    unsigned int width, height, offset_x, offset_y;\n    unsigned int flags, colour_table_size, interlace;\n    unsigned int *colour_table;\n    unsigned int *frame_data = 0;    // Set to 0 for no warnings\n    unsigned int *frame_scanline;\n    unsigned int save_buffer_position;\n    gif_result return_value = GIF_OK;\n    unsigned int x, y, decode_y, burst_bytes;\n    int last_undisposed_frame = (frame - 1);\n    register unsigned char colour;\n\n    /*    Ensure we have a frame to decode\n    */\n    if (frame >= gif->frame_count_partial)\n        return GIF_INSUFFICIENT_DATA;\n\n    /*    Ensure this frame is supposed to be decoded\n    */\n    if (gif->frames[frame].display == false) {\n        gif->current_error = GIF_FRAME_NO_DISPLAY;\n        return GIF_OK;\n    }\n\n    if ((!gif->clear_image) && ((int)frame == gif->decoded_frame))\n        return GIF_OK;\n\n    /*    Get the start of our frame data and the end of the GIF data\n    */\n    gif_data = gif->gif_data + gif->frames[frame].frame_pointer;\n    gif_end = gif->gif_data + gif->buffer_size;\n    gif_bytes = (unsigned int)(gif_end - gif_data);\n\n    /*    Check if we have enough data\n     *    The shortest block of data is a 10-byte image descriptor + 1-byte gif trailer\n    */\n    if (gif_bytes < 12) return GIF_INSUFFICIENT_FRAME_DATA;\n\n    /*    Save the buffer position\n    */\n    save_buffer_position = gif->buffer_position;\n    gif->buffer_position = (unsigned int)(gif_data - gif->gif_data);\n\n    /*    Skip any extensions because we all ready processed them\n    */\n    if ((return_value = gif_skip_frame_extensions(gif)) != GIF_OK)\n        goto gif_decode_frame_exit;\n    gif_data = (gif->gif_data + gif->buffer_position);\n    gif_bytes = (unsigned int)(gif_end - gif_data);\n\n    /*    Ensure we have enough data for the 10-byte image descriptor + 1-byte gif trailer\n    */\n    if (gif_bytes < 12) {\n        return_value = GIF_INSUFFICIENT_FRAME_DATA;\n        goto gif_decode_frame_exit;\n    }\n\n    /* 10-byte Image Descriptor is:\n     *\n     *    +0    CHAR    Image Separator (0x2c)\n     *    +1    SHORT    Image Left Position\n     *    +3    SHORT    Image Top Position\n     *    +5    SHORT    Width\n     *    +7    SHORT    Height\n     *    +9    CHAR    __Packed Fields__\n     *            1BIT    Local Colour Table Flag\n     *            1BIT    Interlace Flag\n     *            1BIT    Sort Flag\n     *            2BITS    Reserved\n     *            3BITS    Size of Local Colour Table\n    */\n    if (gif_data[0] != GIF_IMAGE_SEPARATOR) {\n        return_value = GIF_DATA_ERROR;\n        goto gif_decode_frame_exit;\n    }\n    offset_x = gif_data[1] | (gif_data[2] << 8);\n    offset_y = gif_data[3] | (gif_data[4] << 8);\n    width = gif_data[5] | (gif_data[6] << 8);\n    height = gif_data[7] | (gif_data[8] << 8);\n\n    /*    Boundary checking - shouldn't ever happen except unless the data has been\n        modified since initialisation.\n    */\n    if ((offset_x + width > gif->width) || (offset_y + height > gif->height)) {\n        return_value = GIF_DATA_ERROR;\n        goto gif_decode_frame_exit;\n    }\n\n    /*    Decode the flags\n    */\n    flags = gif_data[9];\n    colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);\n    interlace = flags & GIF_INTERLACE_MASK;\n\n    /*    Move our pointer to the colour table or image data (if no colour table is given)\n    */\n    gif_data += 10;\n    gif_bytes = (unsigned int)(gif_end - gif_data);\n\n    /*    Set up the colour table\n    */\n    if (flags & GIF_COLOUR_TABLE_MASK) {\n        if (gif_bytes < (int)(3 * colour_table_size)) {\n            return_value = GIF_INSUFFICIENT_FRAME_DATA;\n            goto gif_decode_frame_exit;\n        }\n        colour_table = gif->local_colour_table;\n        if (!gif->clear_image) {\n            for (index = 0; index < colour_table_size; index++) {\n                /* Gif colour map contents are r,g,b.\n                 *\n                 * We want to pack them bytewise into the \n                 * colour table, such that the red component\n                 * is in byte 0 and the alpha component is in\n                 * byte 3.\n                 */\n                unsigned char *entry = \n                    (unsigned char *) &colour_table[index];\n\n                entry[0] = gif_data[0];    /* r */\n                entry[1] = gif_data[1];    /* g */\n                entry[2] = gif_data[2];    /* b */\n                entry[3] = 0xff;    /* a */\n\n                gif_data += 3;\n            }\n        } else {\n            gif_data += 3 * colour_table_size;\n        }\n        gif_bytes = (unsigned int)(gif_end - gif_data);\n    } else {\n        colour_table = gif->global_colour_table;\n    }\n\n    /*    Check if we've finished\n    */\n    if (gif_bytes < 1) {\n        return_value = GIF_INSUFFICIENT_FRAME_DATA;\n        goto gif_decode_frame_exit;\n    } else if (gif_data[0] == GIF_TRAILER) {\n        return_value = GIF_OK;\n        goto gif_decode_frame_exit;\n    }\n\n    /*    Get the frame data\n    */\n    assert(gif->bitmap_callbacks.bitmap_get_buffer);\n    frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);\n    if (!frame_data)\n        return GIF_INSUFFICIENT_MEMORY;\n\n    /*    If we are clearing the image we just clear, if not decode\n    */\n    if (!gif->clear_image) {\n        /*    Ensure we have enough data for a 1-byte LZW code size + 1-byte gif trailer\n        */\n        if (gif_bytes < 2) {\n            return_value = GIF_INSUFFICIENT_FRAME_DATA;\n            goto gif_decode_frame_exit;\n        /*    If we only have a 1-byte LZW code size + 1-byte gif trailer, we're finished\n        */\n        } else if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) {\n            return_value = GIF_OK;\n            goto gif_decode_frame_exit;\n        }\n\n        /*    If the previous frame's disposal method requires we restore the background\n         *    colour or this is the first frame, clear the frame data\n        */\n        if ((frame == 0) || (gif->decoded_frame == GIF_INVALID_FRAME)) {\n            memset((char*)frame_data, GIF_TRANSPARENT_COLOUR, gif->width * gif->height * sizeof(int));\n            gif->decoded_frame = frame;\n            /* The line below would fill the image with its background color, but because GIFs support\n             * transparency we likely wouldn't want to do that. */\n            /* memset((char*)frame_data, colour_table[gif->background_index], gif->width * gif->height * sizeof(int)); */\n        } else if ((frame != 0) && (gif->frames[frame - 1].disposal_method == GIF_FRAME_CLEAR)) {\n            gif->clear_image = true;\n            if ((return_value = gif_decode_frame(gif, (frame - 1))) != GIF_OK)\n                goto gif_decode_frame_exit;\n            gif->clear_image = false;\n        /*    If the previous frame's disposal method requires we restore the previous\n         *    image, find the last image set to \"do not dispose\" and get that frame data\n        */\n        } else if ((frame != 0) && (gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) {\n                while ((last_undisposed_frame != -1) && (gif->frames[last_undisposed_frame--].disposal_method == GIF_FRAME_RESTORE)) {\n                }\n                /*    If we don't find one, clear the frame data\n                */\n                if (last_undisposed_frame == -1) {\n                    /* see notes above on transparency vs. background color */\n                    memset((char*)frame_data, GIF_TRANSPARENT_COLOUR, gif->width * gif->height * sizeof(int));\n                } else {\n                    if ((return_value = gif_decode_frame(gif, last_undisposed_frame)) != GIF_OK)\n                        goto gif_decode_frame_exit;\n                    /*    Get this frame's data\n                    */\n                    assert(gif->bitmap_callbacks.bitmap_get_buffer);\n                    frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);\n                    if (!frame_data)\n                        return GIF_INSUFFICIENT_MEMORY;\n                }\n        }\n        gif->decoded_frame = frame;\n\n        /*    Initialise the LZW decoding\n        */\n        gif->set_code_size = gif_data[0];\n        gif->buffer_position = (unsigned int)(gif_data - gif->gif_data + 1);\n\n        /*    Set our code variables\n        */\n        gif->code_size = gif->set_code_size + 1;\n        gif->clear_code = (1 << gif->set_code_size);\n        gif->end_code = gif->clear_code + 1;\n        gif->max_code_size = gif->clear_code << 1;\n        gif->max_code = gif->clear_code + 2;\n        gif->curbit = gif->lastbit = 0;\n        gif->last_byte = 2;\n        gif->get_done = false;\n        gif->direct = gif->buf;\n        gif_init_LZW(gif);\n\n        /*    Decompress the data\n        */\n        for (y = 0; y < height; y++) {\n            if (interlace)\n                decode_y = gif_interlaced_line(height, y) + offset_y;\n            else\n                decode_y = y + offset_y;\n            frame_scanline = frame_data + offset_x + (decode_y * gif->width);\n\n            /*    Rather than decoding pixel by pixel, we try to burst out streams\n                of data to remove the need for end-of data checks every pixel.\n            */\n            x = width;\n            while (x > 0) {\n                burst_bytes = (unsigned int)(gif->stack_pointer - gif->stack);\n                if (burst_bytes > 0) {\n                    if (burst_bytes > x)\n                        burst_bytes = x;\n                    x -= burst_bytes;\n                    while (burst_bytes-- > 0) {\n                        colour = *--gif->stack_pointer;\n                        if (((gif->frames[frame].transparency) &&\n                            (colour != gif->frames[frame].transparency_index)) ||\n                            (!gif->frames[frame].transparency))\n                                *frame_scanline = colour_table[colour];\n                        frame_scanline++;\n                    }\n                } else {\n                    if (!gif_next_LZW(gif)) {\n                        /*    Unexpected end of frame, try to recover\n                        */\n                        if (gif->current_error == GIF_END_OF_FRAME)\n                            return_value = GIF_OK;\n                        else\n                            return_value = gif->current_error;\n                        goto gif_decode_frame_exit;\n                    }\n                }\n            }\n        }\n    } else {\n        /*    Clear our frame\n        */\n        if (gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) {\n            for (y = 0; y < height; y++) {\n                frame_scanline = frame_data + offset_x + ((offset_y + y) * gif->width);\n                if (gif->frames[frame].transparency)\n                    memset(frame_scanline, GIF_TRANSPARENT_COLOUR, width * 4);\n                else\n                    memset(frame_scanline, colour_table[gif->background_index], width * 4);\n            }\n        }\n    }\ngif_decode_frame_exit:\n\n    /*    Check if we should test for optimisation\n    */\n    if (gif->frames[frame].virgin) {\n        if (gif->bitmap_callbacks.bitmap_test_opaque)\n            gif->frames[frame].opaque = gif->bitmap_callbacks.bitmap_test_opaque(gif->frame_image);\n        else\n            gif->frames[frame].opaque = false;\n        gif->frames[frame].virgin = false;\n    }\n    if (gif->bitmap_callbacks.bitmap_set_opaque)\n        gif->bitmap_callbacks.bitmap_set_opaque(gif->frame_image, gif->frames[frame].opaque);\n    if (gif->bitmap_callbacks.bitmap_modified)\n        gif->bitmap_callbacks.bitmap_modified(gif->frame_image);\n\n    /*    Restore the buffer position\n    */\n    gif->buffer_position = save_buffer_position;\n\n    /*    Success!\n    */\n    return return_value;\n\n}\n\n/**    Skips the frame's extensions (which have been previously initialised)\n\n    @return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame\n        GIF_OK for successful decoding\n*/\nstatic gif_result gif_skip_frame_extensions(gif_animation *gif) {\n    unsigned char *gif_data, *gif_end;\n    int gif_bytes;\n    unsigned int block_size;\n\n    /*    Get our buffer position etc.\n    */\n    gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);\n    gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);\n    gif_bytes = (unsigned int)(gif_end - gif_data);\n\n    /*    Skip the extensions\n    */\n    while (gif_data[0] == GIF_EXTENSION_INTRODUCER) {\n        ++gif_data;\n\n        /*    Switch on extension label\n        */\n        switch(gif_data[0]) {\n            /*    Move the pointer to the first data sub-block\n             *    1 byte for the extension label\n            */\n            case GIF_EXTENSION_COMMENT:\n                ++gif_data;\n                break;\n\n            /*    Move the pointer to the first data sub-block\n             *    2 bytes for the extension label and size fields\n             *    Skip the extension size itself\n            */\n            default:\n                gif_data += (2 + gif_data[1]);\n        }\n\n        /*    Repeatedly skip blocks until we get a zero block or run out of data\n         *    This data is ignored by this gif decoder\n        */\n        gif_bytes = (unsigned int)(gif_end - gif_data);\n        block_size = 0;\n        while (gif_data[0] != GIF_BLOCK_TERMINATOR) {\n            block_size = gif_data[0] + 1;\n            if ((gif_bytes -= block_size) < 0)\n                return GIF_INSUFFICIENT_FRAME_DATA;\n            gif_data += block_size;\n        }\n        ++gif_data;\n    }\n\n    /*    Set buffer position and return\n    */\n    gif->buffer_position = (unsigned int)(gif_data - gif->gif_data);\n    return GIF_OK;\n}\n\nstatic unsigned int gif_interlaced_line(int height, int y) {\n    if ((y << 3) < height) return (y << 3);\n    y -= ((height + 7) >> 3);\n    if ((y << 3) < (height - 4)) return (y << 3) + 4;\n    y -= ((height + 3) >> 3);\n    if ((y << 2) < (height - 2)) return (y << 2) + 2;\n    y -= ((height + 1) >> 2);\n    return (y << 1) + 1;\n}\n\n/*    Releases any workspace held by the animation\n*/\nvoid gif_finalise(gif_animation *gif) {\n    /*    Release all our memory blocks\n    */\n    if (gif->frame_image) {\n        assert(gif->bitmap_callbacks.bitmap_destroy);\n        gif->bitmap_callbacks.bitmap_destroy(gif->frame_image);\n    }\n    gif->frame_image = NULL;\n    free(gif->frames);\n    gif->frames = NULL;\n    free(gif->local_colour_table);\n    gif->local_colour_table = NULL;\n    free(gif->global_colour_table);\n    gif->global_colour_table = NULL;\n}\n\n/**\n * Initialise LZW decoding\n */\nvoid gif_init_LZW(gif_animation *gif) {\n    int i;\n\n    gif->current_error = 0;\n    if (gif->clear_code >= (1 << GIF_MAX_LZW)) {\n        gif->stack_pointer = gif->stack;\n        gif->current_error = GIF_FRAME_DATA_ERROR;\n        return;\n    }\n\n    /* initialise our table */\n    memset(gif->table, 0x00, (1 << GIF_MAX_LZW) * 8);\n    for (i = 0; i < gif->clear_code; ++i)\n        gif->table[1][i] = i;\n\n    /* update our LZW parameters */\n    gif->code_size = gif->set_code_size + 1;\n    gif->max_code_size = gif->clear_code << 1;\n    gif->max_code = gif->clear_code + 2;\n    gif->stack_pointer = gif->stack;\n    do {\n        gif->firstcode = gif->oldcode = gif_next_code(gif, gif->code_size);\n    } while (gif->firstcode == gif->clear_code);\n    *gif->stack_pointer++ =gif->firstcode;\n}\n\n\nstatic bool gif_next_LZW(gif_animation *gif) {\n    int code, incode;\n    int block_size;\n    int new_code;\n\n    code = gif_next_code(gif, gif->code_size);\n    if (code < 0) {\n          gif->current_error = code;\n        return false;\n    } else if (code == gif->clear_code) {\n        gif_init_LZW(gif);\n        return true;\n    } else if (code == gif->end_code) {\n        /* skip to the end of our data so multi-image GIFs work */\n        if (gif->zero_data_block) {\n            gif->current_error = GIF_FRAME_DATA_ERROR;\n            return false;\n        }\n        block_size = 0;\n        while (block_size != 1) {\n            block_size = gif->gif_data[gif->buffer_position] + 1;\n            gif->buffer_position += block_size;\n        }\n        gif->current_error = GIF_FRAME_DATA_ERROR;\n        return false;\n    }\n\n    incode = code;\n    if (code >= gif->max_code) {\n        if (gif->stack_pointer >= gif->stack + ((1 << GIF_MAX_LZW) * 2)) {\n            gif->current_error = GIF_FRAME_DATA_ERROR;\n            return false;\n        }\n        *gif->stack_pointer++ = gif->firstcode;\n        code = gif->oldcode;\n    }\n\n    /* The following loop is the most important in the GIF decoding cycle as every\n     * single pixel passes through it.\n     *\n     * Note: our gif->stack is always big enough to hold a complete decompressed chunk. */\n    while (code >= gif->clear_code) {\n        if (gif->stack_pointer >= gif->stack + ((1 << GIF_MAX_LZW) * 2)) {\n            gif->current_error = GIF_FRAME_DATA_ERROR;\n            return false;\n        }\n        *gif->stack_pointer++ = gif->table[1][code];\n        new_code = gif->table[0][code];\n        if (new_code < gif->clear_code) {\n            code = new_code;\n            break;\n        }\n\n        if (gif->stack_pointer >= gif->stack + ((1 << GIF_MAX_LZW) * 2)) {\n            gif->current_error = GIF_FRAME_DATA_ERROR;\n            return false;\n        }\n        *gif->stack_pointer++ = gif->table[1][new_code];\n        code = gif->table[0][new_code];\n        if (code == new_code) {\n              gif->current_error = GIF_FRAME_DATA_ERROR;\n            return false;\n        }\n    }\n\n    if (gif->stack_pointer >= gif->stack + ((1 << GIF_MAX_LZW) * 2)) {\n        gif->current_error = GIF_FRAME_DATA_ERROR;\n        return false;\n    }\n    *gif->stack_pointer++ = gif->firstcode = gif->table[1][code];\n\n    if ((code = gif->max_code) < (1 << GIF_MAX_LZW)) {\n        gif->table[0][code] = gif->oldcode;\n        gif->table[1][code] = gif->firstcode;\n        ++gif->max_code;\n        if ((gif->max_code >= gif->max_code_size) && (gif->max_code_size < (1 << GIF_MAX_LZW))) {\n            gif->max_code_size = gif->max_code_size << 1;\n            ++gif->code_size;\n        }\n    }\n    gif->oldcode = incode;\n    return true;\n}\n\nstatic int gif_next_code(gif_animation *gif, int code_size) {\n    int i, j, end, count, ret;\n    unsigned char *b;\n\n    (void)code_size;\n\n    end = gif->curbit + gif->code_size;\n    if (end >= gif->lastbit) {\n        if (gif->get_done)\n            return GIF_END_OF_FRAME;\n        gif->buf[0] = gif->direct[gif->last_byte - 2];\n        gif->buf[1] = gif->direct[gif->last_byte - 1];\n\n        /* get the next block */\n        gif->direct = gif->gif_data + gif->buffer_position;\n        gif->zero_data_block = ((count = gif->direct[0]) == 0);\n        if ((gif->buffer_position + count) >= gif->buffer_size)\n            return GIF_INSUFFICIENT_FRAME_DATA;\n        if (count == 0)\n            gif->get_done = true;\n        else {\n            gif->direct -= 1;\n            gif->buf[2] = gif->direct[2];\n            gif->buf[3] = gif->direct[3];\n        }\n        gif->buffer_position += count + 1;\n\n        /* update our variables */\n        gif->last_byte = 2 + count;\n        gif->curbit = (gif->curbit - gif->lastbit) + 16;\n        gif->lastbit = (2 + count) << 3;\n        end = gif->curbit + gif->code_size;\n    }\n\n    i = gif->curbit >> 3;\n    if (i < 2)\n        b = gif->buf;\n    else\n        b = gif->direct;\n\n    ret = b[i];\n    j = (end >> 3) - 1;\n    if (i <= j) {\n        ret |= (b[i + 1] << 8);\n        if (i < j)\n            ret |= (b[i + 2] << 16);\n    }\n    ret = (ret >> (gif->curbit % 8)) & maskTbl[gif->code_size];\n    gif->curbit += gif->code_size;\n    return ret;\n}\n"
  },
  {
    "path": "libobs/graphics/libnsgif/libnsgif.h",
    "content": "/*\n * Copyright 2004 Richard Wilson <richard.wilson@netsurf-browser.org>\n * Copyright 2008 Sean Fox <dyntryx@gmail.com>\n *\n * This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/\n * Licenced under the MIT License,\n *                http://www.opensource.org/licenses/mit-license.php\n */\n\n/** \\file\n * Progressive animated GIF file decoding (interface).\n */\n\n#ifndef _LIBNSGIF_H_\n#define _LIBNSGIF_H_\n\n#include <stdbool.h>\n#include <inttypes.h>\n\n#if defined(__cplusplus)\nextern \"C\"\n{\n#endif\n/*    Error return values\n*/\ntypedef enum {\n    GIF_WORKING = 1,\n    GIF_OK = 0,\n    GIF_INSUFFICIENT_FRAME_DATA = -1,\n    GIF_FRAME_DATA_ERROR = -2,\n    GIF_INSUFFICIENT_DATA = -3,\n    GIF_DATA_ERROR = -4,\n    GIF_INSUFFICIENT_MEMORY = -5,\n    GIF_FRAME_NO_DISPLAY = -6,\n    GIF_END_OF_FRAME = -7\n} gif_result;\n\n/*    Maximum LZW bits available\n*/\n#define GIF_MAX_LZW 12\n\n/*    The GIF frame data\n*/\ntypedef struct gif_frame {\n    bool display;                /**< whether the frame should be displayed/animated */\n    unsigned int frame_delay;        /**< delay (in 100th second intervals) before animating the frame */\n    /**    Internal members are listed below\n    */\n    unsigned int frame_pointer;        /**< offset (in bytes) to the GIF frame data */\n    bool virgin;                /**< whether the frame has previously been used */\n    bool opaque;                /**< whether the frame is totally opaque */\n    bool redraw_required;            /**< whether a forcable screen redraw is required */\n    unsigned char disposal_method;        /**< how the previous frame should be disposed; affects plotting */\n    bool transparency;             /**< whether we acknowledge transparency */\n    unsigned char transparency_index;    /**< the index designating a transparent pixel */\n    unsigned int redraw_x;            /**< x co-ordinate of redraw rectangle */\n    unsigned int redraw_y;            /**< y co-ordinate of redraw rectangle */\n    unsigned int redraw_width;        /**< width of redraw rectangle */\n    unsigned int redraw_height;        /**< height of redraw rectangle */\n} gif_frame;\n\n/*    API for Bitmap callbacks\n*/\ntypedef void* (*gif_bitmap_cb_create)(int width, int height);\ntypedef void (*gif_bitmap_cb_destroy)(void *bitmap);\ntypedef unsigned char* (*gif_bitmap_cb_get_buffer)(void *bitmap);\ntypedef void (*gif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);\ntypedef bool (*gif_bitmap_cb_test_opaque)(void *bitmap);\ntypedef void (*gif_bitmap_cb_modified)(void *bitmap);\n\n/*    The Bitmap callbacks function table\n*/\ntypedef struct gif_bitmap_callback_vt {\n    gif_bitmap_cb_create bitmap_create;        /**< Create a bitmap. */\n    gif_bitmap_cb_destroy bitmap_destroy;        /**< Free a bitmap. */\n    gif_bitmap_cb_get_buffer bitmap_get_buffer;    /**< Return a pointer to the pixel data in a bitmap. */\n    /**    Members below are optional\n    */\n    gif_bitmap_cb_set_opaque bitmap_set_opaque;    /**< Sets whether a bitmap should be plotted opaque. */\n    gif_bitmap_cb_test_opaque bitmap_test_opaque;    /**< Tests whether a bitmap has an opaque alpha channel. */\n    gif_bitmap_cb_modified bitmap_modified;    /**< The bitmap image has changed, so flush any persistent cache. */\n} gif_bitmap_callback_vt;\n\n/*    The GIF animation data\n*/\ntypedef struct gif_animation {\n    gif_bitmap_callback_vt bitmap_callbacks;    /**< callbacks for bitmap functions */\n    unsigned char *gif_data;            /**< pointer to GIF data */\n    unsigned int width;                /**< width of GIF (may increase during decoding) */\n    unsigned int height;                /**< height of GIF (may increase during decoding) */\n    unsigned int frame_count;            /**< number of frames decoded */\n    unsigned int frame_count_partial;        /**< number of frames partially decoded */\n    gif_frame *frames;                /**< decoded frames */\n    int decoded_frame;                /**< current frame decoded to bitmap */\n    void *frame_image;                /**< currently decoded image; stored as bitmap from bitmap_create callback */\n    int loop_count;                    /**< number of times to loop animation */\n    gif_result current_error;            /**< current error type, or 0 for none*/\n    /**    Internal members are listed below\n    */\n    unsigned int buffer_position;            /**< current index into GIF data */\n    unsigned int buffer_size;            /**< total number of bytes of GIF data available */\n    unsigned int frame_holders;            /**< current number of frame holders */\n    unsigned int background_index;            /**< index in the colour table for the background colour */\n    unsigned int aspect_ratio;            /**< image aspect ratio (ignored) */\n    unsigned int colour_table_size;        /**< size of colour table (in entries) */\n    bool global_colours;                /**< whether the GIF has a global colour table */\n    unsigned int *global_colour_table;        /**< global colour table */\n    unsigned int *local_colour_table;        /**< local colour table */\n\n\n    /*    General LZW values. They are NO LONGER shared for all GIFs being decoded BECAUSE\n          THAT IS A TERRIBLE IDEA TO SAVE 10Kb or so per GIF.\n    */\n    unsigned char buf[4];\n    unsigned char *direct;\n\n    int table[2][(1 << GIF_MAX_LZW)];\n    unsigned char stack[(1 << GIF_MAX_LZW) * 2];\n    unsigned char *stack_pointer;\n    int code_size, set_code_size;\n    int max_code, max_code_size;\n    int clear_code, end_code;\n    int curbit, lastbit, last_byte;\n    int firstcode, oldcode;\n    bool zero_data_block;\n    bool get_done;\n\n    /*    Whether to clear the decoded image rather than plot\n    */\n    bool clear_image;\n} gif_animation;\n\nvoid gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);\ngif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);\ngif_result gif_decode_frame(gif_animation *gif, unsigned int frame);\nvoid gif_finalise(gif_animation *gif);\n\n#if defined(__cplusplus)\n};\n#endif\n\n#endif\n"
  },
  {
    "path": "libobs/graphics/math-defs.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n#include <math.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifndef M_PI\n#define M_PI 3.1415926535897932384626433832795f\n#endif\n\n#define RAD(val) ((val) * 0.0174532925199432957692369076848f)\n#define DEG(val) ((val) * 57.295779513082320876798154814105f)\n#define LARGE_EPSILON 1e-2f\n#define EPSILON 1e-4f\n#define TINY_EPSILON 1e-5f\n#define M_INFINITE 3.4e38f\n\nstatic inline bool close_float(float f1, float f2, float precision)\n{\n\treturn fabsf(f1 - f2) <= precision;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/math-extra.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <stdlib.h>\n#include \"vec2.h\"\n#include \"vec3.h\"\n#include \"math-defs.h\"\n#include \"math-extra.h\"\n\nvoid polar_to_cart(struct vec3 *dst, const struct vec3 *v)\n{\n\tstruct vec3 cart;\n\tfloat sinx = cosf(v->x);\n\tfloat sinx_z = v->z * sinx;\n\n\tcart.x = sinx_z * sinf(v->y);\n\tcart.z = sinx_z * cosf(v->y);\n\tcart.y = v->z * sinf(v->x);\n\n\tvec3_copy(dst, &cart);\n}\n\nvoid cart_to_polar(struct vec3 *dst, const struct vec3 *v)\n{\n\tstruct vec3 polar;\n\tpolar.z = vec3_len(v);\n\n\tif (close_float(polar.z, 0.0f, EPSILON)) {\n\t\tvec3_zero(&polar);\n\t} else {\n\t\tpolar.x = asinf(v->y / polar.z);\n\t\tpolar.y = atan2f(v->x, v->z);\n\t}\n\n\tvec3_copy(dst, &polar);\n}\n\nvoid norm_to_polar(struct vec2 *dst, const struct vec3 *norm)\n{\n\tdst->x = atan2f(norm->x, norm->z);\n\tdst->y = asinf(norm->y);\n}\n\nvoid polar_to_norm(struct vec3 *dst, const struct vec2 *polar)\n{\n\tfloat sinx = sinf(polar->x);\n\n\tdst->x = sinx * cosf(polar->y);\n\tdst->y = sinx * sinf(polar->y);\n\tdst->z = cosf(polar->x);\n}\n\nfloat calc_torquef(float val1, float val2, float torque, float min_adjust, float t)\n{\n\tfloat out = val1;\n\tfloat dist;\n\tbool over;\n\n\tif (close_float(val1, val2, EPSILON))\n\t\treturn val2;\n\n\tdist = (val2 - val1) * torque;\n\tover = dist > 0.0f;\n\n\tif (over) {\n\t\tif (dist < min_adjust) /* prevents from going too slow */\n\t\t\tdist = min_adjust;\n\t\tout += dist * t; /* add torque */\n\t\tif (out > val2)  /* clamp if overshoot */\n\t\t\tout = val2;\n\t} else {\n\t\tif (dist > -min_adjust)\n\t\t\tdist = -min_adjust;\n\t\tout += dist * t;\n\t\tif (out < val2)\n\t\t\tout = val2;\n\t}\n\n\treturn out;\n}\n\nvoid calc_torque(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2, float torque, float min_adjust,\n\t\t float t)\n{\n\tstruct vec3 line, dir;\n\tfloat orig_dist, torque_dist, adjust_dist;\n\n\tif (vec3_close(v1, v2, EPSILON)) {\n\t\tvec3_copy(dst, v2);\n\t\treturn;\n\t}\n\n\tvec3_sub(&line, v2, v1);\n\torig_dist = vec3_len(&line);\n\tvec3_mulf(&dir, &line, 1.0f / orig_dist);\n\n\ttorque_dist = orig_dist * torque; /* use distance to determine speed */\n\tif (torque_dist < min_adjust)     /* prevent from going too slow */\n\t\ttorque_dist = min_adjust;\n\n\tadjust_dist = torque_dist * t;\n\n\tif (adjust_dist <= (orig_dist - LARGE_EPSILON)) {\n\t\tvec3_mulf(dst, &dir, adjust_dist);\n\t\tvec3_add(dst, dst, v1); /* add torque */\n\t} else {\n\t\tvec3_copy(dst, v2); /* clamp if overshoot */\n\t}\n}\n\nfloat rand_float(int positive_only)\n{\n\tif (positive_only)\n\t\treturn (float)((double)rand() / (double)RAND_MAX);\n\telse\n\t\treturn (float)(((double)rand() / (double)RAND_MAX * 2.0) - 1.0);\n}\n"
  },
  {
    "path": "libobs/graphics/math-extra.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n\n/*\n * A few general math functions that I couldn't really decide where to put.\n *\n *   Polar/Cart conversion, torque functions (for smooth movement), percentage,\n * random floats.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct vec2;\nstruct vec3;\n\nEXPORT void polar_to_cart(struct vec3 *dst, const struct vec3 *v);\nEXPORT void cart_to_polar(struct vec3 *dst, const struct vec3 *v);\n\nEXPORT void norm_to_polar(struct vec2 *dst, const struct vec3 *norm);\nEXPORT void polar_to_norm(struct vec3 *dst, const struct vec2 *polar);\n\nEXPORT float calc_torquef(float val1, float val2, float torque, float min_adjust, float t);\n\nEXPORT void calc_torque(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2, float torque, float min_adjust,\n\t\t\tfloat t);\n\nstatic inline float get_percentage(float start, float end, float mid)\n{\n\treturn (mid - start) / (end - start);\n}\n\nstatic inline float get_percentagei(int start, int end, int mid)\n{\n\treturn (float)(mid - start) / (float)(end - start);\n}\n\nEXPORT float rand_float(int positive_only);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/matrix3.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <string.h>\n#include \"matrix3.h\"\n#include \"matrix4.h\"\n#include \"plane.h\"\n#include \"quat.h\"\n\nvoid matrix3_from_quat(struct matrix3 *dst, const struct quat *q)\n{\n\tfloat norm = quat_dot(q, q);\n\tfloat s = (norm > 0.0f) ? (2.0f / norm) : 0.0f;\n\n\tfloat xx = q->x * q->x * s;\n\tfloat yy = q->y * q->y * s;\n\tfloat zz = q->z * q->z * s;\n\tfloat xy = q->x * q->y * s;\n\tfloat xz = q->x * q->z * s;\n\tfloat yz = q->y * q->z * s;\n\tfloat wx = q->w * q->x * s;\n\tfloat wy = q->w * q->y * s;\n\tfloat wz = q->w * q->z * s;\n\n\tvec3_set(&dst->x, 1.0f - (yy + zz), xy + wz, xz - wy);\n\tvec3_set(&dst->y, xy - wz, 1.0f - (xx + zz), yz + wx);\n\tvec3_set(&dst->z, xz + wy, yz - wx, 1.0f - (xx + yy));\n\tvec3_zero(&dst->t);\n}\n\nvoid matrix3_from_axisang(struct matrix3 *dst, const struct axisang *aa)\n{\n\tstruct quat q;\n\tquat_from_axisang(&q, aa);\n\tmatrix3_from_quat(dst, &q);\n}\n\nvoid matrix3_from_matrix4(struct matrix3 *dst, const struct matrix4 *m)\n{\n\tdst->x.m = m->x.m;\n\tdst->y.m = m->y.m;\n\tdst->z.m = m->z.m;\n\tdst->t.m = m->t.m;\n\tdst->x.w = 0.0f;\n\tdst->y.w = 0.0f;\n\tdst->z.w = 0.0f;\n\tdst->t.w = 0.0f;\n}\n\nvoid matrix3_mul(struct matrix3 *dst, const struct matrix3 *m1, const struct matrix3 *m2)\n{\n\tif (dst == m2) {\n\t\tstruct matrix3 temp;\n\t\tvec3_rotate(&temp.x, &m1->x, m2);\n\t\tvec3_rotate(&temp.y, &m1->y, m2);\n\t\tvec3_rotate(&temp.z, &m1->z, m2);\n\t\tvec3_transform3x4(&temp.t, &m1->t, m2);\n\t\tmatrix3_copy(dst, &temp);\n\t} else {\n\t\tvec3_rotate(&dst->x, &m1->x, m2);\n\t\tvec3_rotate(&dst->y, &m1->y, m2);\n\t\tvec3_rotate(&dst->z, &m1->z, m2);\n\t\tvec3_transform3x4(&dst->t, &m1->t, m2);\n\t}\n}\n\nvoid matrix3_rotate(struct matrix3 *dst, const struct matrix3 *m, const struct quat *q)\n{\n\tstruct matrix3 temp;\n\tmatrix3_from_quat(&temp, q);\n\tmatrix3_mul(dst, m, &temp);\n}\n\nvoid matrix3_rotate_aa(struct matrix3 *dst, const struct matrix3 *m, const struct axisang *aa)\n{\n\tstruct matrix3 temp;\n\tmatrix3_from_axisang(&temp, aa);\n\tmatrix3_mul(dst, m, &temp);\n}\n\nvoid matrix3_scale(struct matrix3 *dst, const struct matrix3 *m, const struct vec3 *v)\n{\n\tvec3_mul(&dst->x, &m->x, v);\n\tvec3_mul(&dst->y, &m->y, v);\n\tvec3_mul(&dst->z, &m->z, v);\n\tvec3_mul(&dst->t, &m->t, v);\n}\n\nvoid matrix3_transpose(struct matrix3 *dst, const struct matrix3 *m)\n{\n\t__m128 tmp1, tmp2;\n\tvec3_rotate(&dst->t, &m->t, m);\n\tvec3_neg(&dst->t, &dst->t);\n\n\ttmp1 = _mm_movelh_ps(m->x.m, m->y.m);\n\ttmp2 = _mm_movehl_ps(m->y.m, m->x.m);\n\tdst->x.m = _mm_shuffle_ps(tmp1, m->z.m, _MM_SHUFFLE(3, 0, 2, 0));\n\tdst->y.m = _mm_shuffle_ps(tmp1, m->z.m, _MM_SHUFFLE(3, 1, 3, 1));\n\tdst->z.m = _mm_shuffle_ps(tmp2, m->z.m, _MM_SHUFFLE(3, 2, 2, 0));\n}\n\nvoid matrix3_inv(struct matrix3 *dst, const struct matrix3 *m)\n{\n\tstruct matrix4 m4;\n\tmatrix4_from_matrix3(&m4, m);\n\tmatrix4_inv((struct matrix4 *)dst, &m4);\n\tdst->t.w = 0.0f;\n}\n\nvoid matrix3_mirror(struct matrix3 *dst, const struct matrix3 *m, const struct plane *p)\n{\n\tvec3_mirrorv(&dst->x, &m->x, &p->dir);\n\tvec3_mirrorv(&dst->y, &m->y, &p->dir);\n\tvec3_mirrorv(&dst->z, &m->z, &p->dir);\n\tvec3_mirror(&dst->t, &m->t, p);\n}\n\nvoid matrix3_mirrorv(struct matrix3 *dst, const struct matrix3 *m, const struct vec3 *v)\n{\n\tvec3_mirrorv(&dst->x, &m->x, v);\n\tvec3_mirrorv(&dst->y, &m->y, v);\n\tvec3_mirrorv(&dst->z, &m->z, v);\n\tvec3_mirrorv(&dst->t, &m->t, v);\n}\n"
  },
  {
    "path": "libobs/graphics/matrix3.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"vec3.h\"\n#include \"axisang.h\"\n\n/* 3x4 Matrix */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct matrix4;\n\nstruct matrix3 {\n\tstruct vec3 x;\n\tstruct vec3 y;\n\tstruct vec3 z;\n\tstruct vec3 t;\n};\n\nstatic inline void matrix3_copy(struct matrix3 *dst, const struct matrix3 *m)\n{\n\tvec3_copy(&dst->x, &m->x);\n\tvec3_copy(&dst->y, &m->y);\n\tvec3_copy(&dst->z, &m->z);\n\tvec3_copy(&dst->t, &m->t);\n}\n\nstatic inline void matrix3_identity(struct matrix3 *dst)\n{\n\tvec3_zero(&dst->x);\n\tvec3_zero(&dst->y);\n\tvec3_zero(&dst->z);\n\tvec3_zero(&dst->t);\n\tdst->x.x = dst->y.y = dst->z.z = 1.0f;\n}\n\nEXPORT void matrix3_from_quat(struct matrix3 *dst, const struct quat *q);\nEXPORT void matrix3_from_axisang(struct matrix3 *dst, const struct axisang *aa);\nEXPORT void matrix3_from_matrix4(struct matrix3 *dst, const struct matrix4 *m);\n\nEXPORT void matrix3_mul(struct matrix3 *dst, const struct matrix3 *m1, const struct matrix3 *m2);\nstatic inline void matrix3_translate(struct matrix3 *dst, const struct matrix3 *m, const struct vec3 *v)\n{\n\tvec3_sub(&dst->t, &m->t, v);\n}\n\nEXPORT void matrix3_rotate(struct matrix3 *dst, const struct matrix3 *m, const struct quat *q);\nEXPORT void matrix3_rotate_aa(struct matrix3 *dst, const struct matrix3 *m, const struct axisang *aa);\nEXPORT void matrix3_scale(struct matrix3 *dst, const struct matrix3 *m, const struct vec3 *v);\nEXPORT void matrix3_transpose(struct matrix3 *dst, const struct matrix3 *m);\nEXPORT void matrix3_inv(struct matrix3 *dst, const struct matrix3 *m);\n\nEXPORT void matrix3_mirror(struct matrix3 *dst, const struct matrix3 *m, const struct plane *p);\nEXPORT void matrix3_mirrorv(struct matrix3 *dst, const struct matrix3 *m, const struct vec3 *v);\n\nstatic inline void matrix3_translate3f(struct matrix3 *dst, const struct matrix3 *m, float x, float y, float z)\n{\n\tstruct vec3 v;\n\tvec3_set(&v, x, y, z);\n\tmatrix3_translate(dst, m, &v);\n}\n\nstatic inline void matrix3_rotate_aa4f(struct matrix3 *dst, const struct matrix3 *m, float x, float y, float z,\n\t\t\t\t       float rot)\n{\n\tstruct axisang aa;\n\taxisang_set(&aa, x, y, z, rot);\n\tmatrix3_rotate_aa(dst, m, &aa);\n}\n\nstatic inline void matrix3_scale3f(struct matrix3 *dst, const struct matrix3 *m, float x, float y, float z)\n{\n\tstruct vec3 v;\n\tvec3_set(&v, x, y, z);\n\tmatrix3_scale(dst, m, &v);\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/matrix4.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"math-defs.h\"\n#include \"matrix4.h\"\n#include \"matrix3.h\"\n#include \"quat.h\"\n\nvoid matrix4_from_matrix3(struct matrix4 *dst, const struct matrix3 *m)\n{\n\tdst->x.m = m->x.m;\n\tdst->y.m = m->y.m;\n\tdst->z.m = m->z.m;\n\tdst->t.m = m->t.m;\n\tdst->t.w = 1.0f;\n}\n\nvoid matrix4_from_quat(struct matrix4 *dst, const struct quat *q)\n{\n\tfloat norm = quat_dot(q, q);\n\tfloat s = (norm > 0.0f) ? (2.0f / norm) : 0.0f;\n\n\tfloat xx = q->x * q->x * s;\n\tfloat yy = q->y * q->y * s;\n\tfloat zz = q->z * q->z * s;\n\tfloat xy = q->x * q->y * s;\n\tfloat xz = q->x * q->z * s;\n\tfloat yz = q->y * q->z * s;\n\tfloat wx = q->w * q->x * s;\n\tfloat wy = q->w * q->y * s;\n\tfloat wz = q->w * q->z * s;\n\n\tvec4_set(&dst->x, 1.0f - (yy + zz), xy + wz, xz - wy, 0.0f);\n\tvec4_set(&dst->y, xy - wz, 1.0f - (xx + zz), yz + wx, 0.0f);\n\tvec4_set(&dst->z, xz + wy, yz - wx, 1.0f - (xx + yy), 0.0f);\n\tvec4_set(&dst->t, 0.0f, 0.0f, 0.0f, 1.0f);\n}\n\nvoid matrix4_from_axisang(struct matrix4 *dst, const struct axisang *aa)\n{\n\tstruct quat q;\n\tquat_from_axisang(&q, aa);\n\tmatrix4_from_quat(dst, &q);\n}\n\nvoid matrix4_mul(struct matrix4 *dst, const struct matrix4 *m1, const struct matrix4 *m2)\n{\n\tstruct matrix4 transposed;\n\tstruct matrix4 out;\n\n\tmatrix4_transpose(&transposed, m2);\n\n\tout.x.x = vec4_dot(&m1->x, &transposed.x);\n\tout.x.y = vec4_dot(&m1->x, &transposed.y);\n\tout.x.z = vec4_dot(&m1->x, &transposed.z);\n\tout.x.w = vec4_dot(&m1->x, &transposed.t);\n\tout.y.x = vec4_dot(&m1->y, &transposed.x);\n\tout.y.y = vec4_dot(&m1->y, &transposed.y);\n\tout.y.z = vec4_dot(&m1->y, &transposed.z);\n\tout.y.w = vec4_dot(&m1->y, &transposed.t);\n\tout.z.x = vec4_dot(&m1->z, &transposed.x);\n\tout.z.y = vec4_dot(&m1->z, &transposed.y);\n\tout.z.z = vec4_dot(&m1->z, &transposed.z);\n\tout.z.w = vec4_dot(&m1->z, &transposed.t);\n\tout.t.x = vec4_dot(&m1->t, &transposed.x);\n\tout.t.y = vec4_dot(&m1->t, &transposed.y);\n\tout.t.z = vec4_dot(&m1->t, &transposed.z);\n\tout.t.w = vec4_dot(&m1->t, &transposed.t);\n\n\tmatrix4_copy(dst, &out);\n}\n\nvoid matrix4_mul_4x3_only(struct matrix4 *dst, const struct matrix4 *m1, const struct matrix4 *m2)\n{\n\tstruct matrix4 transposed;\n\tstruct vec4 x;\n\tstruct vec4 y;\n\tstruct vec4 z;\n\n\tmatrix4_transpose(&transposed, m2);\n\n\tx.x = vec4_dot(&m1->x, &transposed.x);\n\tx.y = vec4_dot(&m1->x, &transposed.y);\n\tx.z = vec4_dot(&m1->x, &transposed.z);\n\tx.w = vec4_dot(&m1->x, &transposed.t);\n\ty.x = vec4_dot(&m1->y, &transposed.x);\n\ty.y = vec4_dot(&m1->y, &transposed.y);\n\ty.z = vec4_dot(&m1->y, &transposed.z);\n\ty.w = vec4_dot(&m1->y, &transposed.t);\n\tz.x = vec4_dot(&m1->z, &transposed.x);\n\tz.y = vec4_dot(&m1->z, &transposed.y);\n\tz.z = vec4_dot(&m1->z, &transposed.z);\n\tz.w = vec4_dot(&m1->z, &transposed.t);\n\n\tvec4_copy(&dst->x, &x);\n\tvec4_copy(&dst->y, &y);\n\tvec4_copy(&dst->z, &z);\n\tvec4_copy(&dst->t, &m2->t);\n}\n\nstatic inline void get_3x3_submatrix(float *dst, const struct matrix4 *m, int i, int j)\n{\n\tconst float *mf = (const float *)m;\n\tint ti, tj, idst, jdst;\n\n\tfor (ti = 0; ti < 4; ti++) {\n\t\tif (ti < i)\n\t\t\tidst = ti;\n\t\telse if (ti > i)\n\t\t\tidst = ti - 1;\n\t\telse\n\t\t\tcontinue;\n\n\t\tfor (tj = 0; tj < 4; tj++) {\n\t\t\tif (tj < j)\n\t\t\t\tjdst = tj;\n\t\t\telse if (tj > j)\n\t\t\t\tjdst = tj - 1;\n\t\t\telse\n\t\t\t\tcontinue;\n\n\t\t\tdst[(idst * 3) + jdst] = mf[(ti * 4) + tj];\n\t\t}\n\t}\n}\n\nstatic inline float get_3x3_determinant(const float *m)\n{\n\treturn (m[0] * ((m[4] * m[8]) - (m[7] * m[5]))) - (m[1] * ((m[3] * m[8]) - (m[6] * m[5]))) +\n\t       (m[2] * ((m[3] * m[7]) - (m[6] * m[4])));\n}\n\nfloat matrix4_determinant(const struct matrix4 *m)\n{\n\tconst float *mf = (const float *)m;\n\tfloat det, result = 0.0f, i = 1.0f;\n\tfloat m3x3[9];\n\tint n;\n\n\tfor (n = 0; n < 4; n++, i = -i) { // NOLINT(clang-tidy-cert-flp30-c)\n\t\tget_3x3_submatrix(m3x3, m, 0, n);\n\n\t\tdet = get_3x3_determinant(m3x3);\n\t\tresult += mf[n] * det * i;\n\t}\n\n\treturn result;\n}\n\nvoid matrix4_translate3v(struct matrix4 *dst, const struct matrix4 *m, const struct vec3 *v)\n{\n\tstruct matrix4 temp;\n\tvec4_set(&temp.x, 1.0f, 0.0f, 0.0f, 0.0f);\n\tvec4_set(&temp.y, 0.0f, 1.0f, 0.0f, 0.0f);\n\tvec4_set(&temp.z, 0.0f, 0.0f, 1.0f, 0.0f);\n\tvec4_from_vec3(&temp.t, v);\n\n\tmatrix4_mul(dst, m, &temp);\n}\n\nvoid matrix4_translate4v(struct matrix4 *dst, const struct matrix4 *m, const struct vec4 *v)\n{\n\tstruct matrix4 temp;\n\tvec4_set(&temp.x, 1.0f, 0.0f, 0.0f, 0.0f);\n\tvec4_set(&temp.y, 0.0f, 1.0f, 0.0f, 0.0f);\n\tvec4_set(&temp.z, 0.0f, 0.0f, 1.0f, 0.0f);\n\tvec4_copy(&temp.t, v);\n\n\tmatrix4_mul(dst, m, &temp);\n}\n\nvoid matrix4_rotate(struct matrix4 *dst, const struct matrix4 *m, const struct quat *q)\n{\n\tstruct matrix4 temp;\n\tmatrix4_from_quat(&temp, q);\n\tmatrix4_mul(dst, m, &temp);\n}\n\nvoid matrix4_rotate_aa(struct matrix4 *dst, const struct matrix4 *m, const struct axisang *aa)\n{\n\tstruct matrix4 temp;\n\tmatrix4_from_axisang(&temp, aa);\n\tmatrix4_mul(dst, m, &temp);\n}\n\nvoid matrix4_scale(struct matrix4 *dst, const struct matrix4 *m, const struct vec3 *v)\n{\n\tstruct matrix4 temp;\n\tvec4_set(&temp.x, v->x, 0.0f, 0.0f, 0.0f);\n\tvec4_set(&temp.y, 0.0f, v->y, 0.0f, 0.0f);\n\tvec4_set(&temp.z, 0.0f, 0.0f, v->z, 0.0f);\n\tvec4_set(&temp.t, 0.0f, 0.0f, 0.0f, 1.0f);\n\tmatrix4_mul(dst, m, &temp);\n}\n\nvoid matrix4_translate3v_i(struct matrix4 *dst, const struct vec3 *v, const struct matrix4 *m)\n{\n\tstruct matrix4 transposed;\n\tstruct vec4 v4;\n\tstruct vec4 t;\n\n\tvec4_from_vec3(&v4, v);\n\tmatrix4_transpose(&transposed, m);\n\tt.x = vec4_dot(&v4, &transposed.x);\n\tt.y = vec4_dot(&v4, &transposed.y);\n\tt.z = vec4_dot(&v4, &transposed.z);\n\tt.w = vec4_dot(&v4, &transposed.t);\n\tvec4_copy(&dst->x, &m->x);\n\tvec4_copy(&dst->y, &m->y);\n\tvec4_copy(&dst->z, &m->z);\n\tvec4_copy(&dst->t, &t);\n}\n\nvoid matrix4_translate4v_i(struct matrix4 *dst, const struct vec4 *v, const struct matrix4 *m)\n{\n\tstruct matrix4 transposed;\n\tstruct vec4 t;\n\n\tmatrix4_transpose(&transposed, m);\n\tt.x = vec4_dot(v, &transposed.x);\n\tt.y = vec4_dot(v, &transposed.y);\n\tt.z = vec4_dot(v, &transposed.z);\n\tt.w = vec4_dot(v, &transposed.t);\n\tvec4_copy(&dst->x, &m->x);\n\tvec4_copy(&dst->y, &m->y);\n\tvec4_copy(&dst->z, &m->z);\n\tvec4_copy(&dst->t, &t);\n}\n\nvoid matrix4_rotate_i(struct matrix4 *dst, const struct quat *q, const struct matrix4 *m)\n{\n\tstruct matrix4 temp;\n\tmatrix4_from_quat(&temp, q);\n\tmatrix4_mul_4x3_only(dst, &temp, m);\n}\n\nvoid matrix4_rotate_aa_i(struct matrix4 *dst, const struct axisang *aa, const struct matrix4 *m)\n{\n\tstruct matrix4 temp;\n\tmatrix4_from_axisang(&temp, aa);\n\tmatrix4_mul_4x3_only(dst, &temp, m);\n}\n\nvoid matrix4_scale_i(struct matrix4 *dst, const struct vec3 *v, const struct matrix4 *m)\n{\n\tstruct matrix4 temp;\n\tvec4_set(&temp.x, v->x, 0.0f, 0.0f, 0.0f);\n\tvec4_set(&temp.y, 0.0f, v->y, 0.0f, 0.0f);\n\tvec4_set(&temp.z, 0.0f, 0.0f, v->z, 0.0f);\n\tvec4_set(&temp.t, 0.0f, 0.0f, 0.0f, 1.0f);\n\tmatrix4_mul_4x3_only(dst, &temp, m);\n}\n\nbool matrix4_inv(struct matrix4 *dst, const struct matrix4 *m)\n{\n\tstruct vec4 *dstv;\n\tfloat det;\n\tfloat m3x3[9];\n\tint i, j, sign;\n\n\tif (dst == m) {\n\t\tstruct matrix4 temp = *m;\n\t\treturn matrix4_inv(dst, &temp);\n\t}\n\n\tdstv = (struct vec4 *)dst;\n\tdet = matrix4_determinant(m);\n\n\tif (fabs(det) < 0.0005f)\n\t\treturn false;\n\n\tfor (i = 0; i < 4; i++) {\n\t\tfor (j = 0; j < 4; j++) {\n\t\t\tsign = 1 - ((i + j) % 2) * 2;\n\t\t\tget_3x3_submatrix(m3x3, m, i, j);\n\t\t\tdstv[j].ptr[i] = get_3x3_determinant(m3x3) * (float)sign / det;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid matrix4_transpose(struct matrix4 *dst, const struct matrix4 *m)\n{\n\tif (dst == m) {\n\t\tstruct matrix4 temp = *m;\n\t\tmatrix4_transpose(dst, &temp);\n\t\treturn;\n\t}\n\n#ifdef NO_INTRINSICS\n\tdst->x.x = m->x.x;\n\tdst->x.y = m->y.x;\n\tdst->x.z = m->z.x;\n\tdst->x.w = m->t.x;\n\tdst->y.x = m->x.y;\n\tdst->y.y = m->y.y;\n\tdst->y.z = m->z.y;\n\tdst->y.w = m->t.y;\n\tdst->z.x = m->x.z;\n\tdst->z.y = m->y.z;\n\tdst->z.z = m->z.z;\n\tdst->z.w = m->t.z;\n\tdst->t.x = m->x.w;\n\tdst->t.y = m->y.w;\n\tdst->t.z = m->z.w;\n\tdst->t.w = m->t.w;\n#else\n\t__m128 a0 = _mm_unpacklo_ps(m->x.m, m->z.m);\n\t__m128 a1 = _mm_unpacklo_ps(m->y.m, m->t.m);\n\t__m128 a2 = _mm_unpackhi_ps(m->x.m, m->z.m);\n\t__m128 a3 = _mm_unpackhi_ps(m->y.m, m->t.m);\n\n\tdst->x.m = _mm_unpacklo_ps(a0, a1);\n\tdst->y.m = _mm_unpackhi_ps(a0, a1);\n\tdst->z.m = _mm_unpacklo_ps(a2, a3);\n\tdst->t.m = _mm_unpackhi_ps(a2, a3);\n#endif\n}\n"
  },
  {
    "path": "libobs/graphics/matrix4.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"vec3.h\"\n#include \"vec4.h\"\n#include \"axisang.h\"\n\n/* 4x4 Matrix */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct matrix3;\n\nstruct matrix4 {\n\tstruct vec4 x, y, z, t;\n};\n\nstatic inline void matrix4_copy(struct matrix4 *dst, const struct matrix4 *m)\n{\n\tdst->x.m = m->x.m;\n\tdst->y.m = m->y.m;\n\tdst->z.m = m->z.m;\n\tdst->t.m = m->t.m;\n}\n\nstatic inline void matrix4_identity(struct matrix4 *dst)\n{\n\tvec4_zero(&dst->x);\n\tvec4_zero(&dst->y);\n\tvec4_zero(&dst->z);\n\tvec4_zero(&dst->t);\n\tdst->x.x = 1.0f;\n\tdst->y.y = 1.0f;\n\tdst->z.z = 1.0f;\n\tdst->t.w = 1.0f;\n}\n\nEXPORT void matrix4_from_matrix3(struct matrix4 *dst, const struct matrix3 *m);\nEXPORT void matrix4_from_quat(struct matrix4 *dst, const struct quat *q);\nEXPORT void matrix4_from_axisang(struct matrix4 *dst, const struct axisang *aa);\n\nEXPORT void matrix4_mul(struct matrix4 *dst, const struct matrix4 *m1, const struct matrix4 *m2);\n\nEXPORT float matrix4_determinant(const struct matrix4 *m);\n\nEXPORT void matrix4_translate3v(struct matrix4 *dst, const struct matrix4 *m, const struct vec3 *v);\nEXPORT void matrix4_translate4v(struct matrix4 *dst, const struct matrix4 *m, const struct vec4 *v);\nEXPORT void matrix4_rotate(struct matrix4 *dst, const struct matrix4 *m, const struct quat *q);\nEXPORT void matrix4_rotate_aa(struct matrix4 *dst, const struct matrix4 *m, const struct axisang *aa);\nEXPORT void matrix4_scale(struct matrix4 *dst, const struct matrix4 *m, const struct vec3 *v);\nEXPORT bool matrix4_inv(struct matrix4 *dst, const struct matrix4 *m);\nEXPORT void matrix4_transpose(struct matrix4 *dst, const struct matrix4 *m);\n\nEXPORT void matrix4_translate3v_i(struct matrix4 *dst, const struct vec3 *v, const struct matrix4 *m);\nEXPORT void matrix4_translate4v_i(struct matrix4 *dst, const struct vec4 *v, const struct matrix4 *m);\nEXPORT void matrix4_rotate_i(struct matrix4 *dst, const struct quat *q, const struct matrix4 *m);\nEXPORT void matrix4_rotate_aa_i(struct matrix4 *dst, const struct axisang *aa, const struct matrix4 *m);\nEXPORT void matrix4_scale_i(struct matrix4 *dst, const struct vec3 *v, const struct matrix4 *m);\n\nstatic inline void matrix4_translate3f(struct matrix4 *dst, const struct matrix4 *m, float x, float y, float z)\n{\n\tstruct vec3 v;\n\tvec3_set(&v, x, y, z);\n\tmatrix4_translate3v(dst, m, &v);\n}\n\nstatic inline void matrix4_rotate_aa4f(struct matrix4 *dst, const struct matrix4 *m, float x, float y, float z,\n\t\t\t\t       float rot)\n{\n\tstruct axisang aa;\n\taxisang_set(&aa, x, y, z, rot);\n\tmatrix4_rotate_aa(dst, m, &aa);\n}\n\nstatic inline void matrix4_scale3f(struct matrix4 *dst, const struct matrix4 *m, float x, float y, float z)\n{\n\tstruct vec3 v;\n\tvec3_set(&v, x, y, z);\n\tmatrix4_scale(dst, m, &v);\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/plane.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"../util/c99defs.h\"\n#include \"matrix3.h\"\n#include \"plane.h\"\n\nvoid plane_from_tri(struct plane *dst, const struct vec3 *v1, const struct vec3 *v2, const struct vec3 *v3)\n{\n\tstruct vec3 temp;\n\n\tvec3_sub(&temp, v2, v1);\n\tvec3_sub(&dst->dir, v3, v1);\n\tvec3_cross(&dst->dir, &temp, &dst->dir);\n\tvec3_norm(&dst->dir, &dst->dir);\n\tdst->dist = vec3_dot(v1, &dst->dir);\n}\n\nvoid plane_transform(struct plane *dst, const struct plane *p, const struct matrix4 *m)\n{\n\tstruct vec3 temp;\n\n\tvec3_zero(&temp);\n\n\tvec3_transform(&dst->dir, &p->dir, m);\n\tvec3_norm(&dst->dir, &dst->dir);\n\n\tvec3_transform(&temp, &temp, m);\n\tdst->dist = p->dist - vec3_dot(&dst->dir, &temp);\n}\n\nvoid plane_transform3x4(struct plane *dst, const struct plane *p, const struct matrix3 *m)\n{\n\tstruct vec3 temp;\n\n\tvec3_transform3x4(&dst->dir, &p->dir, m);\n\tvec3_norm(&dst->dir, &dst->dir);\n\n\tvec3_transform3x4(&temp, &m->t, m);\n\tdst->dist = p->dist - vec3_dot(&dst->dir, &temp);\n}\n\nbool plane_intersection_ray(const struct plane *p, const struct vec3 *orig, const struct vec3 *dir, float *t)\n{\n\tfloat c = vec3_dot(&p->dir, dir);\n\n\tif (fabsf(c) < EPSILON) {\n\t\t*t = 0.0f;\n\t\treturn false;\n\t} else {\n\t\t*t = (p->dist - vec3_dot(&p->dir, orig)) / c;\n\t\treturn true;\n\t}\n}\n\nbool plane_intersection_line(const struct plane *p, const struct vec3 *v1, const struct vec3 *v2, float *t)\n{\n\tfloat p1_dist, p2_dist, p1_abs_dist, dist2;\n\tbool p1_over, p2_over;\n\n\tp1_dist = vec3_plane_dist(v1, p);\n\tp2_dist = vec3_plane_dist(v2, p);\n\n\tif (close_float(p1_dist, 0.0f, EPSILON)) {\n\t\tif (close_float(p2_dist, 0.0f, EPSILON))\n\t\t\treturn false;\n\n\t\t*t = 0.0f;\n\t\treturn true;\n\t} else if (close_float(p2_dist, 0.0f, EPSILON)) {\n\t\t*t = 1.0f;\n\t\treturn true;\n\t}\n\n\tp1_over = (p1_dist > 0.0f);\n\tp2_over = (p2_dist > 0.0f);\n\n\tif (p1_over == p2_over)\n\t\treturn false;\n\n\tp1_abs_dist = fabsf(p1_dist);\n\tdist2 = p1_abs_dist + fabsf(p2_dist);\n\tif (dist2 < EPSILON)\n\t\treturn false;\n\n\t*t = p1_abs_dist / dist2;\n\treturn true;\n}\n\nbool plane_tri_inside(const struct plane *p, const struct vec3 *v1, const struct vec3 *v2, const struct vec3 *v3,\n\t\t      float precision)\n{\n\t/* bit 1: part or all is behind the plane      */\n\t/* bit 2: part or all is in front of the plane */\n\tint sides = 0;\n\tfloat d1 = vec3_plane_dist(v1, p);\n\tfloat d2 = vec3_plane_dist(v2, p);\n\tfloat d3 = vec3_plane_dist(v3, p);\n\n\tif (d1 >= precision)\n\t\tsides = 2;\n\telse if (d1 <= -precision)\n\t\tsides = 1;\n\n\tif (d2 >= precision)\n\t\tsides |= 2;\n\telse if (d2 <= -precision)\n\t\tsides |= 1;\n\n\tif (d3 >= precision)\n\t\tsides |= 2;\n\telse if (d3 <= -precision)\n\t\tsides |= 1;\n\n\treturn sides;\n}\n\nbool plane_line_inside(const struct plane *p, const struct vec3 *v1, const struct vec3 *v2, float precision)\n{\n\t/* bit 1: part or all is behind the plane      */\n\t/* bit 2: part or all is in front of the plane */\n\tint sides = 0;\n\tfloat d1 = vec3_plane_dist(v1, p);\n\tfloat d2 = vec3_plane_dist(v2, p);\n\n\tif (d1 >= precision)\n\t\tsides = 2;\n\telse if (d1 <= -precision)\n\t\tsides = 1;\n\n\tif (d2 >= precision)\n\t\tsides |= 2;\n\telse if (d2 <= -precision)\n\t\tsides |= 1;\n\n\treturn sides;\n}\n"
  },
  {
    "path": "libobs/graphics/plane.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"math-defs.h\"\n#include \"vec3.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct matrix3;\nstruct matrix4;\n\nstruct plane {\n\tstruct vec3 dir;\n\tfloat dist;\n};\n\nstatic inline void plane_copy(struct plane *dst, const struct plane *p)\n{\n\tvec3_copy(&dst->dir, &p->dir);\n\tdst->dist = p->dist;\n}\n\nstatic inline void plane_set(struct plane *dst, const struct vec3 *dir, float dist)\n{\n\tvec3_copy(&dst->dir, dir);\n\tdst->dist = dist;\n}\n\nstatic inline void plane_setf(struct plane *dst, float a, float b, float c, float d)\n{\n\tvec3_set(&dst->dir, a, b, c);\n\tdst->dist = d;\n}\n\nEXPORT void plane_from_tri(struct plane *dst, const struct vec3 *v1, const struct vec3 *v2, const struct vec3 *v3);\n\nEXPORT void plane_transform(struct plane *dst, const struct plane *p, const struct matrix4 *m);\nEXPORT void plane_transform3x4(struct plane *dst, const struct plane *p, const struct matrix3 *m);\n\nEXPORT bool plane_intersection_ray(const struct plane *p, const struct vec3 *orig, const struct vec3 *dir, float *t);\nEXPORT bool plane_intersection_line(const struct plane *p, const struct vec3 *v1, const struct vec3 *v2, float *t);\n\nEXPORT bool plane_tri_inside(const struct plane *p, const struct vec3 *v1, const struct vec3 *v2, const struct vec3 *v3,\n\t\t\t     float precision);\n\nEXPORT bool plane_line_inside(const struct plane *p, const struct vec3 *v1, const struct vec3 *v2, float precision);\n\nstatic inline bool plane_close(const struct plane *p1, const struct plane *p2, float precision)\n{\n\treturn vec3_close(&p1->dir, &p2->dir, precision) && close_float(p1->dist, p2->dist, precision);\n}\n\nstatic inline bool plane_coplanar(const struct plane *p1, const struct plane *p2, float precision)\n{\n\tfloat cos_angle = vec3_dot(&p1->dir, &p2->dir);\n\n\tif (close_float(cos_angle, 1.0f, precision))\n\t\treturn close_float(p1->dist, p2->dist, precision);\n\telse if (close_float(cos_angle, -1.0f, precision))\n\t\treturn close_float(-p1->dist, p2->dist, precision);\n\n\treturn false;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/quat.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"quat.h\"\n#include \"vec3.h\"\n#include \"matrix3.h\"\n#include \"matrix4.h\"\n#include \"axisang.h\"\n\nstatic inline void quat_vec3(struct vec3 *v, const struct quat *q)\n{\n\tv->m = q->m;\n\tv->w = 0.0f;\n}\n\nvoid quat_mul(struct quat *dst, const struct quat *q1, const struct quat *q2)\n{\n\tstruct vec3 q1axis, q2axis;\n\tstruct vec3 temp1, temp2;\n\n\tquat_vec3(&q1axis, q1);\n\tquat_vec3(&q2axis, q2);\n\n\tvec3_mulf(&temp1, &q2axis, q1->w);\n\tvec3_mulf(&temp2, &q1axis, q2->w);\n\tvec3_add(&temp1, &temp1, &temp2);\n\tvec3_cross(&temp2, &q1axis, &q2axis);\n\tvec3_add((struct vec3 *)dst, &temp1, &temp2);\n\n\tdst->w = (q1->w * q2->w) - vec3_dot(&q1axis, &q2axis);\n}\n\nvoid quat_from_axisang(struct quat *dst, const struct axisang *aa)\n{\n\tfloat halfa = aa->w * 0.5f;\n\tfloat sine = sinf(halfa);\n\n\tdst->x = aa->x * sine;\n\tdst->y = aa->y * sine;\n\tdst->z = aa->z * sine;\n\tdst->w = cosf(halfa);\n}\n\nstruct f4x4 {\n\tfloat ptr[4][4];\n};\n\nvoid quat_from_matrix3(struct quat *dst, const struct matrix3 *m)\n{\n\tquat_from_matrix4(dst, (const struct matrix4 *)m);\n}\n\nvoid quat_from_matrix4(struct quat *dst, const struct matrix4 *m)\n{\n\tfloat tr = (m->x.x + m->y.y + m->z.z);\n\tfloat inv_half;\n\tfloat four_d;\n\tint i, j, k;\n\n\tif (tr > 0.0f) {\n\t\tfour_d = sqrtf(tr + 1.0f);\n\t\tdst->w = four_d * 0.5f;\n\n\t\tinv_half = 0.5f / four_d;\n\t\tdst->x = (m->y.z - m->z.y) * inv_half;\n\t\tdst->y = (m->z.x - m->x.z) * inv_half;\n\t\tdst->z = (m->x.y - m->y.x) * inv_half;\n\t} else {\n\t\tstruct f4x4 *val = (struct f4x4 *)m;\n\n\t\ti = (m->x.x > m->y.y) ? 0 : 1;\n\n\t\tif (m->z.z > val->ptr[i][i])\n\t\t\ti = 2;\n\n\t\tj = (i + 1) % 3;\n\t\tk = (i + 2) % 3;\n\n\t\t/* ---------------------------------- */\n\n\t\tfour_d = sqrtf((val->ptr[i][i] - val->ptr[j][j] - val->ptr[k][k]) + 1.0f);\n\n\t\tdst->ptr[i] = four_d * 0.5f;\n\n\t\tinv_half = 0.5f / four_d;\n\t\tdst->ptr[j] = (val->ptr[i][j] + val->ptr[j][i]) * inv_half;\n\t\tdst->ptr[k] = (val->ptr[i][k] + val->ptr[k][i]) * inv_half;\n\t\tdst->w = (val->ptr[j][k] - val->ptr[k][j]) * inv_half;\n\t}\n}\n\nvoid quat_get_dir(struct vec3 *dst, const struct quat *q)\n{\n\tstruct matrix3 m;\n\tmatrix3_from_quat(&m, q);\n\tvec3_copy(dst, &m.z);\n}\n\nvoid quat_set_look_dir(struct quat *dst, const struct vec3 *dir)\n{\n\tstruct vec3 new_dir;\n\tstruct quat xz_rot, yz_rot;\n\tbool xz_valid;\n\tbool yz_valid;\n\tstruct axisang aa;\n\n\tvec3_norm(&new_dir, dir);\n\tvec3_neg(&new_dir, &new_dir);\n\n\tquat_identity(&xz_rot);\n\tquat_identity(&yz_rot);\n\n\txz_valid = close_float(new_dir.x, 0.0f, EPSILON) || close_float(new_dir.z, 0.0f, EPSILON);\n\tyz_valid = close_float(new_dir.y, 0.0f, EPSILON);\n\n\tif (xz_valid) {\n\t\taxisang_set(&aa, 0.0f, 1.0f, 0.0f, atan2f(new_dir.x, new_dir.z));\n\n\t\tquat_from_axisang(&xz_rot, &aa);\n\t}\n\tif (yz_valid) {\n\t\taxisang_set(&aa, -1.0f, 0.0f, 0.0f, asinf(new_dir.y));\n\t\tquat_from_axisang(&yz_rot, &aa);\n\t}\n\n\tif (!xz_valid)\n\t\tquat_copy(dst, &yz_rot);\n\telse if (!yz_valid)\n\t\tquat_copy(dst, &xz_rot);\n\telse\n\t\tquat_mul(dst, &xz_rot, &yz_rot);\n}\n\nvoid quat_log(struct quat *dst, const struct quat *q)\n{\n\tfloat angle = acosf(q->w);\n\tfloat sine = sinf(angle);\n\tfloat w = q->w;\n\n\tquat_copy(dst, q);\n\tdst->w = 0.0f;\n\n\tif ((fabsf(w) < 1.0f) && (fabsf(sine) >= EPSILON)) {\n\t\tsine = angle / sine;\n\t\tquat_mulf(dst, dst, sine);\n\t}\n}\n\nvoid quat_exp(struct quat *dst, const struct quat *q)\n{\n\tfloat length = sqrtf(q->x * q->x + q->y * q->y + q->z * q->z);\n\tfloat sine = sinf(length);\n\n\tquat_copy(dst, q);\n\tsine = (length > EPSILON) ? (sine / length) : 1.0f;\n\tquat_mulf(dst, dst, sine);\n\tdst->w = cosf(length);\n}\n\nvoid quat_interpolate(struct quat *dst, const struct quat *q1, const struct quat *q2, float t)\n{\n\tfloat dot = quat_dot(q1, q2);\n\tfloat anglef = acosf(dot);\n\tfloat sine, sinei, sinet, sineti;\n\tstruct quat temp;\n\n\tif (anglef >= EPSILON) {\n\t\tsine = sinf(anglef);\n\t\tsinei = 1 / sine;\n\t\tsinet = sinf(anglef * t) * sinei;\n\t\tsineti = sinf(anglef * (1.0f - t)) * sinei;\n\n\t\tquat_mulf(&temp, q1, sineti);\n\t\tquat_mulf(dst, q2, sinet);\n\t\tquat_add(dst, &temp, dst);\n\t} else {\n\t\tquat_sub(&temp, q2, q1);\n\t\tquat_mulf(&temp, &temp, t);\n\t\tquat_add(dst, &temp, q1);\n\t}\n}\n\nvoid quat_get_tangent(struct quat *dst, const struct quat *prev, const struct quat *q, const struct quat *next)\n{\n\tstruct quat temp;\n\n\tquat_sub(&temp, q, prev);\n\tquat_add(&temp, &temp, next);\n\tquat_sub(&temp, &temp, q);\n\tquat_mulf(dst, &temp, 0.5f);\n}\n\nvoid quat_interpolate_cubic(struct quat *dst, const struct quat *q1, const struct quat *q2, const struct quat *m1,\n\t\t\t    const struct quat *m2, float t)\n{\n\tstruct quat temp1, temp2;\n\n\tquat_interpolate(&temp1, q1, q2, t);\n\tquat_interpolate(&temp2, m1, m2, t);\n\tquat_interpolate(dst, &temp1, &temp2, 2.0f * (1.0f - t) * t);\n}\n"
  },
  {
    "path": "libobs/graphics/quat.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n#include \"math-defs.h\"\n#include \"vec3.h\"\n\n#include \"../util/sse-intrin.h\"\n\n/*\n * Quaternion math\n *\n *   Generally used to represent rotational data more than anything.  Allows\n * for efficient and correct rotational interpolation without suffering from\n * things like gimbal lock.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct matrix3;\nstruct matrix4;\nstruct axisang;\n\nstruct quat {\n\tunion {\n\t\tstruct {\n\t\t\tfloat x, y, z, w;\n\t\t};\n\t\tfloat ptr[4];\n\t\t__m128 m;\n\t};\n};\n\nstatic inline void quat_identity(struct quat *q)\n{\n\tq->m = _mm_setzero_ps();\n\tq->w = 1.0f;\n}\n\nstatic inline void quat_set(struct quat *dst, float x, float y, float z, float w)\n{\n\tdst->m = _mm_set_ps(x, y, z, w);\n}\n\nstatic inline void quat_copy(struct quat *dst, const struct quat *q)\n{\n\tdst->m = q->m;\n}\n\nstatic inline void quat_add(struct quat *dst, const struct quat *q1, const struct quat *q2)\n{\n\tdst->m = _mm_add_ps(q1->m, q2->m);\n}\n\nstatic inline void quat_sub(struct quat *dst, const struct quat *q1, const struct quat *q2)\n{\n\tdst->m = _mm_sub_ps(q1->m, q2->m);\n}\n\nEXPORT void quat_mul(struct quat *dst, const struct quat *q1, const struct quat *q2);\n\nstatic inline void quat_addf(struct quat *dst, const struct quat *q, float f)\n{\n\tdst->m = _mm_add_ps(q->m, _mm_set1_ps(f));\n}\n\nstatic inline void quat_subf(struct quat *dst, const struct quat *q, float f)\n{\n\tdst->m = _mm_sub_ps(q->m, _mm_set1_ps(f));\n}\n\nstatic inline void quat_mulf(struct quat *dst, const struct quat *q, float f)\n{\n\tdst->m = _mm_mul_ps(q->m, _mm_set1_ps(f));\n}\n\nstatic inline void quat_divf(struct quat *dst, const struct quat *q, float f)\n{\n\tdst->m = _mm_div_ps(q->m, _mm_set1_ps(f));\n}\n\nstatic inline float quat_dot(const struct quat *q1, const struct quat *q2)\n{\n\tstruct vec3 add;\n\t__m128 mul = _mm_mul_ps(q1->m, q2->m);\n\tadd.m = _mm_add_ps(_mm_movehl_ps(mul, mul), mul);\n\tadd.m = _mm_add_ps(_mm_shuffle_ps(add.m, add.m, 0x55), add.m);\n\treturn add.x;\n}\n\nstatic inline void quat_inv(struct quat *dst, const struct quat *q)\n{\n\tdst->x = -q->x;\n\tdst->y = -q->y;\n\tdst->z = -q->z;\n}\n\nstatic inline void quat_neg(struct quat *dst, const struct quat *q)\n{\n\tdst->x = -q->x;\n\tdst->y = -q->y;\n\tdst->z = -q->z;\n\tdst->w = -q->w;\n}\n\nstatic inline float quat_len(const struct quat *q)\n{\n\tfloat dot_val = quat_dot(q, q);\n\treturn (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;\n}\n\nstatic inline float quat_dist(const struct quat *q1, const struct quat *q2)\n{\n\tstruct quat temp;\n\tfloat dot_val;\n\n\tquat_sub(&temp, q1, q2);\n\tdot_val = quat_dot(&temp, &temp);\n\treturn (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;\n}\n\nstatic inline void quat_norm(struct quat *dst, const struct quat *q)\n{\n\tfloat dot_val = quat_dot(q, q);\n\tdst->m = (dot_val > 0.0f) ? _mm_mul_ps(q->m, _mm_set1_ps(1.0f / sqrtf(dot_val))) : _mm_setzero_ps();\n}\n\nstatic inline bool quat_close(const struct quat *q1, const struct quat *q2, float epsilon)\n{\n\tstruct quat test;\n\tquat_sub(&test, q1, q2);\n\treturn test.x < epsilon && test.y < epsilon && test.z < epsilon && test.w < epsilon;\n}\n\nEXPORT void quat_from_axisang(struct quat *dst, const struct axisang *aa);\nEXPORT void quat_from_matrix3(struct quat *dst, const struct matrix3 *m);\nEXPORT void quat_from_matrix4(struct quat *dst, const struct matrix4 *m);\n\nEXPORT void quat_get_dir(struct vec3 *dst, const struct quat *q);\nEXPORT void quat_set_look_dir(struct quat *dst, const struct vec3 *dir);\n\nEXPORT void quat_log(struct quat *dst, const struct quat *q);\nEXPORT void quat_exp(struct quat *dst, const struct quat *q);\n\nEXPORT void quat_interpolate(struct quat *dst, const struct quat *q1, const struct quat *q2, float t);\nEXPORT void quat_get_tangent(struct quat *dst, const struct quat *prev, const struct quat *q, const struct quat *next);\nEXPORT void quat_interpolate_cubic(struct quat *dst, const struct quat *q1, const struct quat *q2,\n\t\t\t\t   const struct quat *m1, const struct quat *m2, float t);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/shader-parser.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"../util/platform.h\"\n#include \"shader-parser.h\"\n\nenum gs_shader_param_type get_shader_param_type(const char *type)\n{\n\tif (strcmp(type, \"float\") == 0)\n\t\treturn GS_SHADER_PARAM_FLOAT;\n\telse if (strcmp(type, \"float2\") == 0)\n\t\treturn GS_SHADER_PARAM_VEC2;\n\telse if (strcmp(type, \"float3\") == 0)\n\t\treturn GS_SHADER_PARAM_VEC3;\n\telse if (strcmp(type, \"float4\") == 0)\n\t\treturn GS_SHADER_PARAM_VEC4;\n\telse if (strcmp(type, \"int2\") == 0)\n\t\treturn GS_SHADER_PARAM_INT2;\n\telse if (strcmp(type, \"int3\") == 0)\n\t\treturn GS_SHADER_PARAM_INT3;\n\telse if (strcmp(type, \"int4\") == 0)\n\t\treturn GS_SHADER_PARAM_INT4;\n\telse if (astrcmp_n(type, \"texture\", 7) == 0)\n\t\treturn GS_SHADER_PARAM_TEXTURE;\n\telse if (strcmp(type, \"float4x4\") == 0)\n\t\treturn GS_SHADER_PARAM_MATRIX4X4;\n\telse if (strcmp(type, \"bool\") == 0)\n\t\treturn GS_SHADER_PARAM_BOOL;\n\telse if (strcmp(type, \"int\") == 0)\n\t\treturn GS_SHADER_PARAM_INT;\n\telse if (strcmp(type, \"string\") == 0)\n\t\treturn GS_SHADER_PARAM_STRING;\n\n\treturn GS_SHADER_PARAM_UNKNOWN;\n}\n\nenum gs_sample_filter get_sample_filter(const char *filter)\n{\n\tif (astrcmpi(filter, \"Anisotropy\") == 0)\n\t\treturn GS_FILTER_ANISOTROPIC;\n\n\telse if (astrcmpi(filter, \"Point\") == 0 || strcmp(filter, \"MIN_MAG_MIP_POINT\") == 0)\n\t\treturn GS_FILTER_POINT;\n\n\telse if (astrcmpi(filter, \"Linear\") == 0 || strcmp(filter, \"MIN_MAG_MIP_LINEAR\") == 0)\n\t\treturn GS_FILTER_LINEAR;\n\n\telse if (strcmp(filter, \"MIN_MAG_POINT_MIP_LINEAR\") == 0)\n\t\treturn GS_FILTER_MIN_MAG_POINT_MIP_LINEAR;\n\n\telse if (strcmp(filter, \"MIN_POINT_MAG_LINEAR_MIP_POINT\") == 0)\n\t\treturn GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;\n\n\telse if (strcmp(filter, \"MIN_POINT_MAG_MIP_LINEAR\") == 0)\n\t\treturn GS_FILTER_MIN_POINT_MAG_MIP_LINEAR;\n\n\telse if (strcmp(filter, \"MIN_LINEAR_MAG_MIP_POINT\") == 0)\n\t\treturn GS_FILTER_MIN_LINEAR_MAG_MIP_POINT;\n\n\telse if (strcmp(filter, \"MIN_LINEAR_MAG_POINT_MIP_LINEAR\") == 0)\n\t\treturn GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;\n\n\telse if (strcmp(filter, \"MIN_MAG_LINEAR_MIP_POINT\") == 0)\n\t\treturn GS_FILTER_MIN_MAG_LINEAR_MIP_POINT;\n\n\treturn GS_FILTER_LINEAR;\n}\n\nextern enum gs_address_mode get_address_mode(const char *mode)\n{\n\tif (astrcmpi(mode, \"Wrap\") == 0 || astrcmpi(mode, \"Repeat\") == 0)\n\t\treturn GS_ADDRESS_WRAP;\n\telse if (astrcmpi(mode, \"Clamp\") == 0 || astrcmpi(mode, \"None\") == 0)\n\t\treturn GS_ADDRESS_CLAMP;\n\telse if (astrcmpi(mode, \"Mirror\") == 0)\n\t\treturn GS_ADDRESS_MIRROR;\n\telse if (astrcmpi(mode, \"Border\") == 0)\n\t\treturn GS_ADDRESS_BORDER;\n\telse if (astrcmpi(mode, \"MirrorOnce\") == 0)\n\t\treturn GS_ADDRESS_MIRRORONCE;\n\n\treturn GS_ADDRESS_CLAMP;\n}\n\nvoid shader_sampler_convert(struct shader_sampler *ss, struct gs_sampler_info *info)\n{\n\tsize_t i;\n\tmemset(info, 0, sizeof(struct gs_sampler_info));\n\n\tinfo->max_anisotropy = 1;\n\n\tfor (i = 0; i < ss->states.num; i++) {\n\t\tconst char *state = ss->states.array[i];\n\t\tconst char *value = ss->values.array[i];\n\n\t\tif (astrcmpi(state, \"Filter\") == 0)\n\t\t\tinfo->filter = get_sample_filter(value);\n\t\telse if (astrcmpi(state, \"AddressU\") == 0)\n\t\t\tinfo->address_u = get_address_mode(value);\n\t\telse if (astrcmpi(state, \"AddressV\") == 0)\n\t\t\tinfo->address_v = get_address_mode(value);\n\t\telse if (astrcmpi(state, \"AddressW\") == 0)\n\t\t\tinfo->address_w = get_address_mode(value);\n\t\telse if (astrcmpi(state, \"MaxAnisotropy\") == 0)\n\t\t\tinfo->max_anisotropy = (int)strtol(value, NULL, 10);\n\t\telse if (astrcmpi(state, \"BorderColor\") == 0)\n\t\t\tinfo->border_color = strtol(value + 1, NULL, 16);\n\t}\n}\n\n/* ------------------------------------------------------------------------- */\n\nstatic int sp_parse_sampler_state_item(struct shader_parser *sp, struct shader_sampler *ss)\n{\n\tint ret;\n\tchar *state = NULL, *value = NULL;\n\n\tret = cf_next_name(&sp->cfp, &state, \"state name\", \";\");\n\tif (ret != PARSE_SUCCESS)\n\t\tgoto fail;\n\n\tret = cf_next_token_should_be(&sp->cfp, \"=\", \";\", NULL);\n\tif (ret != PARSE_SUCCESS)\n\t\tgoto fail;\n\n\tret = cf_next_token_copy(&sp->cfp, &value);\n\tif (ret != PARSE_SUCCESS)\n\t\tgoto fail;\n\n\tret = cf_next_token_should_be(&sp->cfp, \";\", \";\", NULL);\n\tif (ret != PARSE_SUCCESS)\n\t\tgoto fail;\n\n\tda_push_back(ss->states, &state);\n\tda_push_back(ss->values, &value);\n\treturn ret;\n\nfail:\n\tbfree(state);\n\tbfree(value);\n\treturn ret;\n}\n\nstatic void sp_parse_sampler_state(struct shader_parser *sp)\n{\n\tstruct shader_sampler ss;\n\tstruct cf_token peek;\n\tshader_sampler_init(&ss);\n\n\tif (cf_next_name(&sp->cfp, &ss.name, \"name\", \";\") != PARSE_SUCCESS)\n\t\tgoto error;\n\tif (cf_next_token_should_be(&sp->cfp, \"{\", \";\", NULL) != PARSE_SUCCESS)\n\t\tgoto error;\n\n\tif (!cf_peek_valid_token(&sp->cfp, &peek))\n\t\tgoto error;\n\n\twhile (strref_cmp(&peek.str, \"}\") != 0) {\n\t\tint ret = sp_parse_sampler_state_item(sp, &ss);\n\t\tif (ret == PARSE_EOF)\n\t\t\tgoto error;\n\n\t\tif (!cf_peek_valid_token(&sp->cfp, &peek))\n\t\t\tgoto error;\n\t}\n\n\tif (cf_next_token_should_be(&sp->cfp, \"}\", \";\", NULL) != PARSE_SUCCESS)\n\t\tgoto error;\n\tif (cf_next_token_should_be(&sp->cfp, \";\", NULL, NULL) != PARSE_SUCCESS)\n\t\tgoto error;\n\n\tda_push_back(sp->samplers, &ss);\n\treturn;\n\nerror:\n\tshader_sampler_free(&ss);\n}\n\nstatic inline int sp_parse_struct_var(struct shader_parser *sp, struct shader_var *var)\n{\n\tint code;\n\n\t/* -------------------------------------- */\n\t/* variable type */\n\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&sp->cfp, \";\"))\n\t\treturn PARSE_CONTINUE;\n\tif (cf_token_is(&sp->cfp, \"}\"))\n\t\treturn PARSE_BREAK;\n\n\tcode = cf_token_is_type(&sp->cfp, CFTOKEN_NAME, \"type name\", \";\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tcf_copy_token(&sp->cfp, &var->type);\n\n\t/* -------------------------------------- */\n\t/* variable name */\n\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&sp->cfp, \";\"))\n\t\treturn PARSE_UNEXPECTED_CONTINUE;\n\tif (cf_token_is(&sp->cfp, \"}\"))\n\t\treturn PARSE_UNEXPECTED_BREAK;\n\n\tcode = cf_token_is_type(&sp->cfp, CFTOKEN_NAME, \"variable name\", \";\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tcf_copy_token(&sp->cfp, &var->name);\n\n\t/* -------------------------------------- */\n\t/* variable mapping if any (POSITION, TEXCOORD, etc) */\n\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&sp->cfp, \":\")) {\n\t\tif (!cf_next_valid_token(&sp->cfp))\n\t\t\treturn PARSE_EOF;\n\n\t\tif (cf_token_is(&sp->cfp, \";\"))\n\t\t\treturn PARSE_UNEXPECTED_CONTINUE;\n\t\tif (cf_token_is(&sp->cfp, \"}\"))\n\t\t\treturn PARSE_UNEXPECTED_BREAK;\n\n\t\tcode = cf_token_is_type(&sp->cfp, CFTOKEN_NAME, \"mapping name\", \";\");\n\t\tif (code != PARSE_SUCCESS)\n\t\t\treturn code;\n\n\t\tcf_copy_token(&sp->cfp, &var->mapping);\n\n\t\tif (!cf_next_valid_token(&sp->cfp))\n\t\t\treturn PARSE_EOF;\n\t}\n\n\t/* -------------------------------------- */\n\n\tif (!cf_token_is(&sp->cfp, \";\")) {\n\t\tif (!cf_go_to_valid_token(&sp->cfp, \";\", \"}\"))\n\t\t\treturn PARSE_EOF;\n\t\treturn PARSE_CONTINUE;\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic void sp_parse_struct(struct shader_parser *sp)\n{\n\tstruct shader_struct ss;\n\tshader_struct_init(&ss);\n\n\tif (cf_next_name(&sp->cfp, &ss.name, \"name\", \";\") != PARSE_SUCCESS)\n\t\tgoto error;\n\tif (cf_next_token_should_be(&sp->cfp, \"{\", \";\", NULL) != PARSE_SUCCESS)\n\t\tgoto error;\n\n\t/* get structure variables */\n\twhile (true) {\n\t\tbool do_break = false;\n\t\tstruct shader_var var;\n\n\t\tshader_var_init(&var);\n\n\t\tswitch (sp_parse_struct_var(sp, &var)) {\n\n\t\tcase PARSE_UNEXPECTED_CONTINUE:\n\t\t\tcf_adderror_syntax_error(&sp->cfp);\n\t\t\t/* Falls through. */\n\t\tcase PARSE_CONTINUE:\n\t\t\tshader_var_free(&var);\n\t\t\tcontinue;\n\n\t\tcase PARSE_UNEXPECTED_BREAK:\n\t\t\tcf_adderror_syntax_error(&sp->cfp);\n\t\t\t/* Falls through. */\n\t\tcase PARSE_BREAK:\n\t\t\tshader_var_free(&var);\n\t\t\tdo_break = true;\n\t\t\tbreak;\n\n\t\tcase PARSE_EOF:\n\t\t\tshader_var_free(&var);\n\t\t\tgoto error;\n\t\t}\n\n\t\tif (do_break)\n\t\t\tbreak;\n\n\t\tda_push_back(ss.vars, &var);\n\t}\n\n\tif (cf_next_token_should_be(&sp->cfp, \";\", NULL, NULL) != PARSE_SUCCESS)\n\t\tgoto error;\n\n\tda_push_back(sp->structs, &ss);\n\treturn;\n\nerror:\n\tshader_struct_free(&ss);\n}\n\nstatic inline int sp_check_for_keyword(struct shader_parser *sp, const char *keyword, bool *val)\n{\n\tbool new_val = cf_token_is(&sp->cfp, keyword);\n\tif (new_val) {\n\t\tif (!cf_next_valid_token(&sp->cfp))\n\t\t\treturn PARSE_EOF;\n\n\t\tif (new_val && *val)\n\t\t\tcf_adderror(&sp->cfp, \"'$1' keyword already specified\", LEX_WARNING, keyword, NULL, NULL);\n\t\t*val = new_val;\n\n\t\treturn PARSE_CONTINUE;\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic inline int sp_parse_func_param(struct shader_parser *sp, struct shader_var *var)\n{\n\tint code;\n\tbool var_type_keyword = false;\n\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\treturn PARSE_EOF;\n\n\tcode = sp_check_for_keyword(sp, \"in\", &var_type_keyword);\n\tif (code == PARSE_EOF)\n\t\treturn PARSE_EOF;\n\telse if (var_type_keyword)\n\t\tvar->var_type = SHADER_VAR_IN;\n\n\tif (!var_type_keyword) {\n\t\tcode = sp_check_for_keyword(sp, \"inout\", &var_type_keyword);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn PARSE_EOF;\n\t\telse if (var_type_keyword)\n\t\t\tvar->var_type = SHADER_VAR_INOUT;\n\t}\n\n\tif (!var_type_keyword) {\n\t\tcode = sp_check_for_keyword(sp, \"out\", &var_type_keyword);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn PARSE_EOF;\n\t\telse if (var_type_keyword)\n\t\t\tvar->var_type = SHADER_VAR_OUT;\n\t}\n\n\tif (!var_type_keyword) {\n\t\tcode = sp_check_for_keyword(sp, \"uniform\", &var_type_keyword);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn PARSE_EOF;\n\t\telse if (var_type_keyword)\n\t\t\tvar->var_type = SHADER_VAR_UNIFORM;\n\t}\n\n\tcode = cf_get_name(&sp->cfp, &var->type, \"type\", \")\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tcode = cf_next_name(&sp->cfp, &var->name, \"name\", \")\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&sp->cfp, \":\")) {\n\t\tcode = cf_next_name(&sp->cfp, &var->mapping, \"mapping specifier\", \")\");\n\t\tif (code != PARSE_SUCCESS)\n\t\t\treturn code;\n\n\t\tif (!cf_next_valid_token(&sp->cfp))\n\t\t\treturn PARSE_EOF;\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic bool sp_parse_func_params(struct shader_parser *sp, struct shader_func *func)\n{\n\tstruct cf_token peek;\n\tint code;\n\n\tcf_token_clear(&peek);\n\n\tif (!cf_peek_valid_token(&sp->cfp, &peek))\n\t\treturn false;\n\n\tif (*peek.str.array == ')') {\n\t\tcf_next_token(&sp->cfp);\n\t\tgoto exit;\n\t}\n\n\tdo {\n\t\tstruct shader_var var;\n\t\tshader_var_init(&var);\n\n\t\tif (!cf_token_is(&sp->cfp, \"(\") && !cf_token_is(&sp->cfp, \",\"))\n\t\t\tcf_adderror_syntax_error(&sp->cfp);\n\n\t\tcode = sp_parse_func_param(sp, &var);\n\t\tif (code != PARSE_SUCCESS) {\n\t\t\tshader_var_free(&var);\n\n\t\t\tif (code == PARSE_CONTINUE)\n\t\t\t\tgoto exit;\n\t\t\telse if (code == PARSE_EOF)\n\t\t\t\treturn false;\n\t\t}\n\n\t\tda_push_back(func->params, &var);\n\t} while (!cf_token_is(&sp->cfp, \")\"));\n\nexit:\n\treturn true;\n}\n\nstatic void sp_parse_function(struct shader_parser *sp, char *type, char *name)\n{\n\tstruct shader_func func;\n\n\tshader_func_init(&func, type, name);\n\tif (!sp_parse_func_params(sp, &func))\n\t\tgoto error;\n\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\tgoto error;\n\n\t/* if function is mapped to something, for example COLOR */\n\tif (cf_token_is(&sp->cfp, \":\")) {\n\t\tchar *mapping = NULL;\n\t\tint errorcode = cf_next_name(&sp->cfp, &mapping, \"mapping\", \"{\");\n\t\tif (errorcode != PARSE_SUCCESS)\n\t\t\tgoto error;\n\n\t\tfunc.mapping = mapping;\n\n\t\tif (!cf_next_valid_token(&sp->cfp))\n\t\t\tgoto error;\n\t}\n\n\tif (!cf_token_is(&sp->cfp, \"{\")) {\n\t\tcf_adderror_expecting(&sp->cfp, \"{\");\n\t\tgoto error;\n\t}\n\n\tfunc.start = sp->cfp.cur_token;\n\tif (!cf_pass_pair(&sp->cfp, '{', '}'))\n\t\tgoto error;\n\n\t/* it is established that the current token is '}' if we reach this */\n\tcf_next_token(&sp->cfp);\n\n\tfunc.end = sp->cfp.cur_token;\n\tda_push_back(sp->funcs, &func);\n\treturn;\n\nerror:\n\tshader_func_free(&func);\n}\n\n/* parses \"array[count]\" */\nstatic bool sp_parse_param_array(struct shader_parser *sp, struct shader_var *param)\n{\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\treturn false;\n\n\tif (sp->cfp.cur_token->type != CFTOKEN_NUM ||\n\t    !valid_int_str(sp->cfp.cur_token->str.array, sp->cfp.cur_token->str.len))\n\t\treturn false;\n\n\tparam->array_count = (int)strtol(sp->cfp.cur_token->str.array, NULL, 10);\n\n\tif (cf_next_token_should_be(&sp->cfp, \"]\", \";\", NULL) == PARSE_EOF)\n\t\treturn false;\n\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic inline int sp_parse_param_assign_intfloat(struct shader_parser *sp, struct shader_var *param, bool is_float)\n{\n\tint code;\n\tbool is_negative = false;\n\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\treturn PARSE_EOF;\n\n\tif (cf_token_is(&sp->cfp, \"-\")) {\n\t\tis_negative = true;\n\n\t\tif (!cf_next_token(&sp->cfp))\n\t\t\treturn PARSE_EOF;\n\t}\n\n\tcode = cf_token_is_type(&sp->cfp, CFTOKEN_NUM, \"numeric value\", \";\");\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tif (is_float) {\n\t\tfloat f = (float)os_strtod(sp->cfp.cur_token->str.array);\n\t\tif (is_negative)\n\t\t\tf = -f;\n\t\tda_push_back_array(param->default_val, (uint8_t *)&f, sizeof(float));\n\t} else {\n\t\tlong l = strtol(sp->cfp.cur_token->str.array, NULL, 10);\n\t\tif (is_negative)\n\t\t\tl = -l;\n\t\tda_push_back_array(param->default_val, (uint8_t *)&l, sizeof(long));\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\n/*\n * parses assignment for float1, float2, float3, float4, and any combination\n * for float3x3, float4x4, etc\n */\nstatic inline int sp_parse_param_assign_float_array(struct shader_parser *sp, struct shader_var *param)\n{\n\tconst char *float_type = param->type + 5;\n\tint float_count = 0, code, i;\n\n\t/* -------------------------------------------- */\n\n\tif (float_type[0] < '1' || float_type[0] > '4')\n\t\tcf_adderror(&sp->cfp, \"Invalid row count\", LEX_ERROR, NULL, NULL, NULL);\n\n\tfloat_count = float_type[0] - '0';\n\n\tif (float_type[1] == 'x') {\n\t\tif (float_type[2] < '1' || float_type[2] > '4')\n\t\t\tcf_adderror(&sp->cfp, \"Invalid column count\", LEX_ERROR, NULL, NULL, NULL);\n\n\t\tfloat_count *= float_type[2] - '0';\n\t}\n\n\t/* -------------------------------------------- */\n\n\tcode = cf_next_token_should_be(&sp->cfp, \"{\", \";\", NULL);\n\tif (code != PARSE_SUCCESS)\n\t\treturn code;\n\n\tfor (i = 0; i < float_count; i++) {\n\t\tchar *next = ((i + 1) < float_count) ? \",\" : \"}\";\n\n\t\tcode = sp_parse_param_assign_intfloat(sp, param, true);\n\t\tif (code != PARSE_SUCCESS)\n\t\t\treturn code;\n\n\t\tcode = cf_next_token_should_be(&sp->cfp, next, \";\", NULL);\n\t\tif (code != PARSE_SUCCESS)\n\t\t\treturn code;\n\t}\n\n\treturn PARSE_SUCCESS;\n}\n\nstatic int sp_parse_param_assignment_val(struct shader_parser *sp, struct shader_var *param)\n{\n\tif (strcmp(param->type, \"int\") == 0)\n\t\treturn sp_parse_param_assign_intfloat(sp, param, false);\n\telse if (strcmp(param->type, \"float\") == 0)\n\t\treturn sp_parse_param_assign_intfloat(sp, param, true);\n\telse if (astrcmp_n(param->type, \"float\", 5) == 0)\n\t\treturn sp_parse_param_assign_float_array(sp, param);\n\n\tcf_adderror(&sp->cfp, \"Invalid type '$1' used for assignment\", LEX_ERROR, param->type, NULL, NULL);\n\n\treturn PARSE_CONTINUE;\n}\n\nstatic inline bool sp_parse_param_assign(struct shader_parser *sp, struct shader_var *param)\n{\n\tif (sp_parse_param_assignment_val(sp, param) != PARSE_SUCCESS)\n\t\treturn false;\n\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic void sp_parse_param(struct shader_parser *sp, char *type, char *name, bool is_const, bool is_uniform)\n{\n\tstruct shader_var param;\n\tshader_var_init_param(&param, type, name, is_uniform, is_const);\n\n\tif (cf_token_is(&sp->cfp, \";\"))\n\t\tgoto complete;\n\tif (cf_token_is(&sp->cfp, \"[\") && !sp_parse_param_array(sp, &param))\n\t\tgoto error;\n\tif (cf_token_is(&sp->cfp, \"=\") && !sp_parse_param_assign(sp, &param))\n\t\tgoto error;\n\tif (!cf_token_is(&sp->cfp, \";\"))\n\t\tgoto error;\n\ncomplete:\n\tda_push_back(sp->params, &param);\n\treturn;\n\nerror:\n\tshader_var_free(&param);\n}\n\nstatic bool sp_get_var_specifiers(struct shader_parser *sp, bool *is_const, bool *is_uniform)\n{\n\twhile (true) {\n\t\tint code = sp_check_for_keyword(sp, \"const\", is_const);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn false;\n\t\telse if (code == PARSE_CONTINUE)\n\t\t\tcontinue;\n\n\t\tcode = sp_check_for_keyword(sp, \"uniform\", is_uniform);\n\t\tif (code == PARSE_EOF)\n\t\t\treturn false;\n\t\telse if (code == PARSE_CONTINUE)\n\t\t\tcontinue;\n\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\nstatic inline void report_invalid_func_keyword(struct shader_parser *sp, const char *name, bool val)\n{\n\tif (val)\n\t\tcf_adderror(&sp->cfp,\n\t\t\t    \"'$1' keyword cannot be used with a \"\n\t\t\t    \"function\",\n\t\t\t    LEX_ERROR, name, NULL, NULL);\n}\n\nstatic void sp_parse_other(struct shader_parser *sp)\n{\n\tbool is_const = false, is_uniform = false;\n\tchar *type = NULL, *name = NULL;\n\n\tif (!sp_get_var_specifiers(sp, &is_const, &is_uniform))\n\t\tgoto error;\n\n\tif (cf_get_name(&sp->cfp, &type, \"type\", \";\") != PARSE_SUCCESS)\n\t\tgoto error;\n\tif (cf_next_name(&sp->cfp, &name, \"name\", \";\") != PARSE_SUCCESS)\n\t\tgoto error;\n\n\tif (!cf_next_valid_token(&sp->cfp))\n\t\tgoto error;\n\n\tif (cf_token_is(&sp->cfp, \"(\")) {\n\t\treport_invalid_func_keyword(sp, \"const\", is_const);\n\t\treport_invalid_func_keyword(sp, \"uniform\", is_uniform);\n\n\t\tsp_parse_function(sp, type, name);\n\t\treturn;\n\t} else {\n\t\tsp_parse_param(sp, type, name, is_const, is_uniform);\n\t\treturn;\n\t}\n\nerror:\n\tbfree(type);\n\tbfree(name);\n}\n\nbool shader_parse(struct shader_parser *sp, const char *shader, const char *file)\n{\n\tif (!cf_parser_parse(&sp->cfp, shader, file))\n\t\treturn false;\n\n\twhile (sp->cfp.cur_token && sp->cfp.cur_token->type != CFTOKEN_NONE) {\n\t\tif (cf_token_is(&sp->cfp, \";\") || is_whitespace(*sp->cfp.cur_token->str.array)) {\n\t\t\tsp->cfp.cur_token++;\n\n\t\t} else if (cf_token_is(&sp->cfp, \"struct\")) {\n\t\t\tsp_parse_struct(sp);\n\n\t\t} else if (cf_token_is(&sp->cfp, \"sampler_state\")) {\n\t\t\tsp_parse_sampler_state(sp);\n\n\t\t} else if (cf_token_is(&sp->cfp, \"{\")) {\n\t\t\tcf_adderror(&sp->cfp, \"Unexpected code segment\", LEX_ERROR, NULL, NULL, NULL);\n\t\t\tcf_pass_pair(&sp->cfp, '{', '}');\n\n\t\t} else {\n\t\t\t/* parameters and functions */\n\t\t\tsp_parse_other(sp);\n\t\t}\n\t}\n\n\treturn !error_data_has_errors(&sp->cfp.error_list);\n}\n"
  },
  {
    "path": "libobs/graphics/shader-parser.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/cf-parser.h\"\n#include \"graphics.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nEXPORT enum gs_shader_param_type get_shader_param_type(const char *type);\nEXPORT enum gs_sample_filter get_sample_filter(const char *filter);\nEXPORT enum gs_address_mode get_address_mode(const char *address_mode);\n\n/*\n * Shader Parser\n *\n *   Parses a shader and extracts data such as shader constants, samplers,\n * and vertex input information.  Also allows the reformatting of shaders for\n * different libraries.  This is usually used only by graphics libraries,\n */\n\nenum shader_var_type {\n\tSHADER_VAR_NONE,\n\tSHADER_VAR_IN = SHADER_VAR_NONE,\n\tSHADER_VAR_INOUT,\n\tSHADER_VAR_OUT,\n\tSHADER_VAR_UNIFORM,\n\tSHADER_VAR_CONST\n};\n\nstruct shader_var {\n\tchar *type;\n\tchar *name;\n\tchar *mapping;\n\tenum shader_var_type var_type;\n\tint array_count;\n\tsize_t gl_sampler_id; /* optional: used/parsed by GL */\n\n\tDARRAY(uint8_t) default_val;\n};\n\nstatic inline void shader_var_init(struct shader_var *sv)\n{\n\tmemset(sv, 0, sizeof(struct shader_var));\n}\n\nstatic inline void shader_var_init_param(struct shader_var *sv, char *type, char *name, bool is_uniform, bool is_const)\n{\n\tif (is_uniform)\n\t\tsv->var_type = SHADER_VAR_UNIFORM;\n\telse if (is_const)\n\t\tsv->var_type = SHADER_VAR_CONST;\n\telse\n\t\tsv->var_type = SHADER_VAR_NONE;\n\n\tsv->type = type;\n\tsv->name = name;\n\tsv->mapping = NULL;\n\tsv->array_count = 0;\n\tsv->gl_sampler_id = (size_t)-1;\n\tda_init(sv->default_val);\n}\n\nstatic inline void shader_var_free(struct shader_var *sv)\n{\n\tbfree(sv->type);\n\tbfree(sv->name);\n\tbfree(sv->mapping);\n\tda_free(sv->default_val);\n}\n\n/* ------------------------------------------------------------------------- */\n\nstruct shader_sampler {\n\tchar *name;\n\tDARRAY(char *) states;\n\tDARRAY(char *) values;\n};\n\nstatic inline void shader_sampler_init(struct shader_sampler *ss)\n{\n\tmemset(ss, 0, sizeof(struct shader_sampler));\n}\n\nstatic inline void shader_sampler_free(struct shader_sampler *ss)\n{\n\tsize_t i;\n\tfor (i = 0; i < ss->states.num; i++)\n\t\tbfree(ss->states.array[i]);\n\tfor (i = 0; i < ss->values.num; i++)\n\t\tbfree(ss->values.array[i]);\n\n\tbfree(ss->name);\n\tda_free(ss->states);\n\tda_free(ss->values);\n}\n\nEXPORT void shader_sampler_convert(struct shader_sampler *ss, struct gs_sampler_info *info);\n\n/* ------------------------------------------------------------------------- */\n\nstruct shader_struct {\n\tchar *name;\n\tDARRAY(struct shader_var) vars;\n};\n\nstatic inline void shader_struct_init(struct shader_struct *ss)\n{\n\tmemset(ss, 0, sizeof(struct shader_struct));\n}\n\nstatic inline void shader_struct_free(struct shader_struct *ss)\n{\n\tsize_t i;\n\n\tfor (i = 0; i < ss->vars.num; i++)\n\t\tshader_var_free(ss->vars.array + i);\n\n\tbfree(ss->name);\n\tda_free(ss->vars);\n}\n\n/* ------------------------------------------------------------------------- */\n\nstruct shader_func {\n\tchar *name;\n\tchar *return_type;\n\tchar *mapping;\n\tDARRAY(struct shader_var) params;\n\n\tstruct cf_token *start, *end;\n};\n\nstatic inline void shader_func_init(struct shader_func *sf, char *return_type, char *name)\n{\n\tda_init(sf->params);\n\n\tsf->return_type = return_type;\n\tsf->mapping = NULL;\n\tsf->name = name;\n\tsf->start = NULL;\n\tsf->end = NULL;\n}\n\nstatic inline void shader_func_free(struct shader_func *sf)\n{\n\tsize_t i;\n\n\tfor (i = 0; i < sf->params.num; i++)\n\t\tshader_var_free(sf->params.array + i);\n\n\tbfree(sf->name);\n\tbfree(sf->return_type);\n\tbfree(sf->mapping);\n\tda_free(sf->params);\n}\n\n/* ------------------------------------------------------------------------- */\n\nstruct shader_parser {\n\tstruct cf_parser cfp;\n\n\tDARRAY(struct shader_var) params;\n\tDARRAY(struct shader_struct) structs;\n\tDARRAY(struct shader_sampler) samplers;\n\tDARRAY(struct shader_func) funcs;\n};\n\nstatic inline void shader_parser_init(struct shader_parser *sp)\n{\n\tcf_parser_init(&sp->cfp);\n\n\tda_init(sp->params);\n\tda_init(sp->structs);\n\tda_init(sp->samplers);\n\tda_init(sp->funcs);\n}\n\nstatic inline void shader_parser_free(struct shader_parser *sp)\n{\n\tsize_t i;\n\n\tfor (i = 0; i < sp->params.num; i++)\n\t\tshader_var_free(sp->params.array + i);\n\tfor (i = 0; i < sp->structs.num; i++)\n\t\tshader_struct_free(sp->structs.array + i);\n\tfor (i = 0; i < sp->samplers.num; i++)\n\t\tshader_sampler_free(sp->samplers.array + i);\n\tfor (i = 0; i < sp->funcs.num; i++)\n\t\tshader_func_free(sp->funcs.array + i);\n\n\tcf_parser_free(&sp->cfp);\n\tda_free(sp->params);\n\tda_free(sp->structs);\n\tda_free(sp->samplers);\n\tda_free(sp->funcs);\n}\n\nEXPORT bool shader_parse(struct shader_parser *sp, const char *shader, const char *file);\n\nstatic inline char *shader_parser_geterrors(struct shader_parser *sp)\n{\n\treturn error_data_buildstring(&sp->cfp.error_list);\n}\n\nstatic inline struct shader_var *shader_parser_getparam(struct shader_parser *sp, const char *param_name)\n{\n\tsize_t i;\n\tfor (i = 0; i < sp->params.num; i++) {\n\t\tstruct shader_var *param = sp->params.array + i;\n\t\tif (strcmp(param->name, param_name) == 0)\n\t\t\treturn param;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct shader_struct *shader_parser_getstruct(struct shader_parser *sp, const char *struct_name)\n{\n\tsize_t i;\n\tfor (i = 0; i < sp->structs.num; i++) {\n\t\tstruct shader_struct *st = sp->structs.array + i;\n\t\tif (strcmp(st->name, struct_name) == 0)\n\t\t\treturn st;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct shader_sampler *shader_parser_getsampler(struct shader_parser *sp, const char *sampler_name)\n{\n\tsize_t i;\n\tfor (i = 0; i < sp->samplers.num; i++) {\n\t\tstruct shader_sampler *sampler = sp->samplers.array + i;\n\t\tif (strcmp(sampler->name, sampler_name) == 0)\n\t\t\treturn sampler;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct shader_func *shader_parser_getfunc(struct shader_parser *sp, const char *func_name)\n{\n\tsize_t i;\n\tfor (i = 0; i < sp->funcs.num; i++) {\n\t\tstruct shader_func *func = sp->funcs.array + i;\n\t\tif (strcmp(func->name, func_name) == 0)\n\t\t\treturn func;\n\t}\n\n\treturn NULL;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/srgb.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include <math.h>\n#include <string.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstatic inline float gs_srgb_nonlinear_to_linear(float u)\n{\n\treturn (u <= 0.04045f) ? (u / 12.92f) : powf((u + 0.055f) / 1.055f, 2.4f);\n}\n\nstatic inline float gs_srgb_linear_to_nonlinear(float u)\n{\n\treturn (u <= 0.0031308f) ? (12.92f * u) : ((1.055f * powf(u, 1.0f / 2.4f)) - 0.055f);\n}\n\nstatic inline float gs_u8_to_float(uint8_t u)\n{\n\treturn (float)u / 255.0f;\n}\n\nstatic inline void gs_u8x4_to_float4(float *f, const uint8_t *u)\n{\n\tf[0] = gs_u8_to_float(u[0]);\n\tf[1] = gs_u8_to_float(u[1]);\n\tf[2] = gs_u8_to_float(u[2]);\n\tf[3] = gs_u8_to_float(u[3]);\n}\n\nstatic inline uint8_t gs_float_to_u8(float f)\n{\n\treturn (uint8_t)(f * 255.0f + 0.5f);\n}\n\nstatic inline void gs_premultiply_float4(float *f)\n{\n\tf[0] *= f[3];\n\tf[1] *= f[3];\n\tf[2] *= f[3];\n}\n\nstatic inline void gs_float3_to_u8x3(uint8_t *u, const float *f)\n{\n\tu[0] = gs_float_to_u8(f[0]);\n\tu[1] = gs_float_to_u8(f[1]);\n\tu[2] = gs_float_to_u8(f[2]);\n}\n\nstatic inline void gs_float4_to_u8x4(uint8_t *u, const float *f)\n{\n\tu[0] = gs_float_to_u8(f[0]);\n\tu[1] = gs_float_to_u8(f[1]);\n\tu[2] = gs_float_to_u8(f[2]);\n\tu[3] = gs_float_to_u8(f[3]);\n}\n\nstatic inline void gs_float3_srgb_nonlinear_to_linear(float *f)\n{\n\tf[0] = gs_srgb_nonlinear_to_linear(f[0]);\n\tf[1] = gs_srgb_nonlinear_to_linear(f[1]);\n\tf[2] = gs_srgb_nonlinear_to_linear(f[2]);\n}\n\nstatic inline void gs_float3_srgb_linear_to_nonlinear(float *f)\n{\n\tf[0] = gs_srgb_linear_to_nonlinear(f[0]);\n\tf[1] = gs_srgb_linear_to_nonlinear(f[1]);\n\tf[2] = gs_srgb_linear_to_nonlinear(f[2]);\n}\n\nstatic inline void gs_premultiply_xyza(uint8_t *data)\n{\n\tuint8_t u[4];\n\tfloat f[4];\n\tmemcpy(&u, data, sizeof(u));\n\tgs_u8x4_to_float4(f, u);\n\tgs_premultiply_float4(f);\n\tgs_float3_to_u8x3(u, f);\n\tmemcpy(data, &u, sizeof(u));\n}\n\nstatic inline void gs_premultiply_xyza_srgb(uint8_t *data)\n{\n\tuint8_t u[4];\n\tfloat f[4];\n\tmemcpy(&u, data, sizeof(u));\n\tgs_u8x4_to_float4(f, u);\n\tgs_float3_srgb_nonlinear_to_linear(f);\n\tgs_premultiply_float4(f);\n\tgs_float3_srgb_linear_to_nonlinear(f);\n\tgs_float3_to_u8x3(u, f);\n\tmemcpy(data, &u, sizeof(u));\n}\n\nstatic inline void gs_premultiply_xyza_restrict(uint8_t *__restrict dst, const uint8_t *__restrict src)\n{\n\tuint8_t u[4];\n\tfloat f[4];\n\tmemcpy(&u, src, sizeof(u));\n\tgs_u8x4_to_float4(f, u);\n\tgs_premultiply_float4(f);\n\tgs_float3_to_u8x3(u, f);\n\tmemcpy(dst, &u, sizeof(u));\n}\n\nstatic inline void gs_premultiply_xyza_srgb_restrict(uint8_t *__restrict dst, const uint8_t *__restrict src)\n{\n\tuint8_t u[4];\n\tfloat f[4];\n\tmemcpy(&u, src, sizeof(u));\n\tgs_u8x4_to_float4(f, u);\n\tgs_float3_srgb_nonlinear_to_linear(f);\n\tgs_premultiply_float4(f);\n\tgs_float3_srgb_linear_to_nonlinear(f);\n\tgs_float3_to_u8x3(u, f);\n\tmemcpy(dst, &u, sizeof(u));\n}\n\nstatic inline void gs_premultiply_xyza_loop(uint8_t *data, size_t texel_count)\n{\n\tfor (size_t i = 0; i < texel_count; ++i) {\n\t\tgs_premultiply_xyza(data);\n\t\tdata += 4;\n\t}\n}\n\nstatic inline void gs_premultiply_xyza_srgb_loop(uint8_t *data, size_t texel_count)\n{\n\tfor (size_t i = 0; i < texel_count; ++i) {\n\t\tgs_premultiply_xyza_srgb(data);\n\t\tdata += 4;\n\t}\n}\n\nstatic inline void gs_premultiply_xyza_loop_restrict(uint8_t *__restrict dst, const uint8_t *__restrict src,\n\t\t\t\t\t\t     size_t texel_count)\n{\n\tfor (size_t i = 0; i < texel_count; ++i) {\n\t\tgs_premultiply_xyza_restrict(dst, src);\n\t\tdst += 4;\n\t\tsrc += 4;\n\t}\n}\n\nstatic inline void gs_premultiply_xyza_srgb_loop_restrict(uint8_t *__restrict dst, const uint8_t *__restrict src,\n\t\t\t\t\t\t\t  size_t texel_count)\n{\n\tfor (size_t i = 0; i < texel_count; ++i) {\n\t\tgs_premultiply_xyza_srgb_restrict(dst, src);\n\t\tdst += 4;\n\t\tsrc += 4;\n\t}\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/texture-render.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n/*\n *   This is a set of helper functions to more easily render to textures\n * without having to duplicate too much code.\n */\n\n#include <assert.h>\n#include \"graphics.h\"\n\nstruct gs_texture_render {\n\tgs_texture_t *target, *prev_target;\n\tgs_zstencil_t *zs, *prev_zs;\n\tenum gs_color_space prev_space;\n\n\tuint32_t cx, cy;\n\n\tenum gs_color_format format;\n\tenum gs_zstencil_format zsformat;\n\n\tbool rendered;\n};\n\ngs_texrender_t *gs_texrender_create(enum gs_color_format format, enum gs_zstencil_format zsformat)\n{\n\tstruct gs_texture_render *texrender;\n\ttexrender = bzalloc(sizeof(struct gs_texture_render));\n\ttexrender->format = format;\n\ttexrender->zsformat = zsformat;\n\n\treturn texrender;\n}\n\nvoid gs_texrender_destroy(gs_texrender_t *texrender)\n{\n\tif (texrender) {\n\t\tgs_texture_destroy(texrender->target);\n\t\tgs_zstencil_destroy(texrender->zs);\n\t\tbfree(texrender);\n\t}\n}\n\nstatic bool texrender_resetbuffer(gs_texrender_t *texrender, uint32_t cx, uint32_t cy)\n{\n\tif (!texrender)\n\t\treturn false;\n\n\tgs_texture_destroy(texrender->target);\n\tgs_zstencil_destroy(texrender->zs);\n\n\ttexrender->target = NULL;\n\ttexrender->zs = NULL;\n\ttexrender->cx = cx;\n\ttexrender->cy = cy;\n\n\ttexrender->target = gs_texture_create(cx, cy, texrender->format, 1, NULL, GS_RENDER_TARGET);\n\tif (!texrender->target)\n\t\treturn false;\n\n\tif (texrender->zsformat != GS_ZS_NONE) {\n\t\ttexrender->zs = gs_zstencil_create(cx, cy, texrender->zsformat);\n\t\tif (!texrender->zs) {\n\t\t\tgs_texture_destroy(texrender->target);\n\t\t\ttexrender->target = NULL;\n\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool gs_texrender_begin(gs_texrender_t *texrender, uint32_t cx, uint32_t cy)\n{\n\treturn gs_texrender_begin_with_color_space(texrender, cx, cy, GS_CS_SRGB);\n}\n\nbool gs_texrender_begin_with_color_space(gs_texrender_t *texrender, uint32_t cx, uint32_t cy, enum gs_color_space space)\n{\n\tif (!texrender || texrender->rendered)\n\t\treturn false;\n\n\tif (!cx || !cy)\n\t\treturn false;\n\n\tif (texrender->cx != cx || texrender->cy != cy)\n\t\tif (!texrender_resetbuffer(texrender, cx, cy))\n\t\t\treturn false;\n\n\tif (!texrender->target)\n\t\treturn false;\n\n\tgs_viewport_push();\n\tgs_projection_push();\n\tgs_matrix_push();\n\tgs_matrix_identity();\n\n\ttexrender->prev_target = gs_get_render_target();\n\ttexrender->prev_zs = gs_get_zstencil_target();\n\ttexrender->prev_space = gs_get_color_space();\n\tgs_set_render_target_with_color_space(texrender->target, texrender->zs, space);\n\n\tgs_set_viewport(0, 0, texrender->cx, texrender->cy);\n\n\treturn true;\n}\n\nvoid gs_texrender_end(gs_texrender_t *texrender)\n{\n\tif (!texrender)\n\t\treturn;\n\n\tgs_set_render_target_with_color_space(texrender->prev_target, texrender->prev_zs, texrender->prev_space);\n\n\tgs_matrix_pop();\n\tgs_projection_pop();\n\tgs_viewport_pop();\n\n\ttexrender->rendered = true;\n}\n\nvoid gs_texrender_reset(gs_texrender_t *texrender)\n{\n\tif (texrender)\n\t\ttexrender->rendered = false;\n}\n\ngs_texture_t *gs_texrender_get_texture(const gs_texrender_t *texrender)\n{\n\treturn texrender ? texrender->target : NULL;\n}\n\nenum gs_color_format gs_texrender_get_format(const gs_texrender_t *texrender)\n{\n\treturn texrender->format;\n}\n"
  },
  {
    "path": "libobs/graphics/vec2.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <math.h>\n#include \"math-extra.h\"\n#include \"math-defs.h\"\n#include \"vec2.h\"\n\nvoid vec2_abs(struct vec2 *dst, const struct vec2 *v)\n{\n\tvec2_set(dst, fabsf(v->x), fabsf(v->y));\n}\n\nvoid vec2_floor(struct vec2 *dst, const struct vec2 *v)\n{\n\tvec2_set(dst, floorf(v->x), floorf(v->y));\n}\n\nvoid vec2_ceil(struct vec2 *dst, const struct vec2 *v)\n{\n\tvec2_set(dst, ceilf(v->x), ceilf(v->y));\n}\n\nint vec2_close(const struct vec2 *v1, const struct vec2 *v2, float epsilon)\n{\n\treturn close_float(v1->x, v2->x, epsilon) && close_float(v1->y, v2->y, epsilon);\n}\n\nvoid vec2_norm(struct vec2 *dst, const struct vec2 *v)\n{\n\tfloat len = vec2_len(v);\n\n\tif (len > 0.0f) {\n\t\tlen = 1.0f / len;\n\t\tvec2_mulf(dst, v, len);\n\t}\n}\n"
  },
  {
    "path": "libobs/graphics/vec2.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n#include <math.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct vec2 {\n\tunion {\n\t\tstruct {\n\t\t\tfloat x, y;\n\t\t};\n\t\tfloat ptr[2];\n\t};\n};\n\nstatic inline void vec2_zero(struct vec2 *dst)\n{\n\tdst->x = 0.0f;\n\tdst->y = 0.0f;\n}\n\nstatic inline void vec2_set(struct vec2 *dst, float x, float y)\n{\n\tdst->x = x;\n\tdst->y = y;\n}\n\nstatic inline void vec2_copy(struct vec2 *dst, const struct vec2 *v)\n{\n\tdst->x = v->x;\n\tdst->y = v->y;\n}\n\nstatic inline void vec2_add(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2)\n{\n\tvec2_set(dst, v1->x + v2->x, v1->y + v2->y);\n}\n\nstatic inline void vec2_sub(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2)\n{\n\tvec2_set(dst, v1->x - v2->x, v1->y - v2->y);\n}\n\nstatic inline void vec2_mul(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2)\n{\n\tvec2_set(dst, v1->x * v2->x, v1->y * v2->y);\n}\n\nstatic inline void vec2_div(struct vec2 *dst, const struct vec2 *v1, const struct vec2 *v2)\n{\n\tvec2_set(dst, v1->x / v2->x, v1->y / v2->y);\n}\n\nstatic inline void vec2_addf(struct vec2 *dst, const struct vec2 *v, float f)\n{\n\tvec2_set(dst, v->x + f, v->y + f);\n}\n\nstatic inline void vec2_subf(struct vec2 *dst, const struct vec2 *v, float f)\n{\n\tvec2_set(dst, v->x - f, v->y - f);\n}\n\nstatic inline void vec2_mulf(struct vec2 *dst, const struct vec2 *v, float f)\n{\n\tvec2_set(dst, v->x * f, v->y * f);\n}\n\nstatic inline void vec2_divf(struct vec2 *dst, const struct vec2 *v, float f)\n{\n\tvec2_set(dst, v->x / f, v->y / f);\n}\n\nstatic inline void vec2_neg(struct vec2 *dst, const struct vec2 *v)\n{\n\tvec2_set(dst, -v->x, -v->y);\n}\n\nstatic inline float vec2_dot(const struct vec2 *v1, const struct vec2 *v2)\n{\n\treturn v1->x * v2->x + v1->y * v2->y;\n}\n\nstatic inline float vec2_len(const struct vec2 *v)\n{\n\treturn sqrtf(v->x * v->x + v->y * v->y);\n}\n\nstatic inline float vec2_dist(const struct vec2 *v1, const struct vec2 *v2)\n{\n\tstruct vec2 temp;\n\tvec2_sub(&temp, v1, v2);\n\treturn vec2_len(&temp);\n}\n\nstatic inline void vec2_minf(struct vec2 *dst, const struct vec2 *v, float val)\n{\n\tdst->x = (v->x < val) ? v->x : val;\n\tdst->y = (v->y < val) ? v->y : val;\n}\n\nstatic inline void vec2_min(struct vec2 *dst, const struct vec2 *v, const struct vec2 *min_v)\n{\n\tdst->x = (v->x < min_v->x) ? v->x : min_v->x;\n\tdst->y = (v->y < min_v->y) ? v->y : min_v->y;\n}\n\nstatic inline void vec2_maxf(struct vec2 *dst, const struct vec2 *v, float val)\n{\n\tdst->x = (v->x > val) ? v->x : val;\n\tdst->y = (v->y > val) ? v->y : val;\n}\n\nstatic inline void vec2_max(struct vec2 *dst, const struct vec2 *v, const struct vec2 *max_v)\n{\n\tdst->x = (v->x > max_v->x) ? v->x : max_v->x;\n\tdst->y = (v->y > max_v->y) ? v->y : max_v->y;\n}\n\nEXPORT void vec2_abs(struct vec2 *dst, const struct vec2 *v);\nEXPORT void vec2_floor(struct vec2 *dst, const struct vec2 *v);\nEXPORT void vec2_ceil(struct vec2 *dst, const struct vec2 *v);\nEXPORT int vec2_close(const struct vec2 *v1, const struct vec2 *v2, float epsilon);\nEXPORT void vec2_norm(struct vec2 *dst, const struct vec2 *v);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/vec3.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"vec3.h\"\n#include \"vec4.h\"\n#include \"quat.h\"\n#include \"axisang.h\"\n#include \"plane.h\"\n#include \"matrix3.h\"\n#include \"math-extra.h\"\n\nvoid vec3_from_vec4(struct vec3 *dst, const struct vec4 *v)\n{\n\tdst->m = v->m;\n\tdst->w = 0.0f;\n}\n\nfloat vec3_plane_dist(const struct vec3 *v, const struct plane *p)\n{\n\treturn vec3_dot(v, &p->dir) - p->dist;\n}\n\nvoid vec3_rotate(struct vec3 *dst, const struct vec3 *v, const struct matrix3 *m)\n{\n\tstruct vec3 temp;\n\tvec3_copy(&temp, v);\n\n\tdst->x = vec3_dot(&temp, &m->x);\n\tdst->y = vec3_dot(&temp, &m->y);\n\tdst->z = vec3_dot(&temp, &m->z);\n\tdst->w = 0.0f;\n}\n\nvoid vec3_transform(struct vec3 *dst, const struct vec3 *v, const struct matrix4 *m)\n{\n\tstruct vec4 v4;\n\tvec4_from_vec3(&v4, v);\n\tvec4_transform(&v4, &v4, m);\n\tvec3_from_vec4(dst, &v4);\n}\n\nvoid vec3_transform3x4(struct vec3 *dst, const struct vec3 *v, const struct matrix3 *m)\n{\n\tstruct vec3 temp;\n\tvec3_sub(&temp, v, &m->t);\n\n\tdst->x = vec3_dot(&temp, &m->x);\n\tdst->y = vec3_dot(&temp, &m->y);\n\tdst->z = vec3_dot(&temp, &m->z);\n\tdst->w = 0.0f;\n}\n\nvoid vec3_mirror(struct vec3 *dst, const struct vec3 *v, const struct plane *p)\n{\n\tstruct vec3 temp;\n\tvec3_mulf(&temp, &p->dir, vec3_plane_dist(v, p) * 2.0f);\n\tvec3_sub(dst, v, &temp);\n}\n\nvoid vec3_mirrorv(struct vec3 *dst, const struct vec3 *v, const struct vec3 *vec)\n{\n\tstruct vec3 temp;\n\tvec3_mulf(&temp, vec, vec3_dot(v, vec) * 2.0f);\n\tvec3_sub(dst, v, &temp);\n}\n\nvoid vec3_rand(struct vec3 *dst, int positive_only)\n{\n\tdst->x = rand_float(positive_only);\n\tdst->y = rand_float(positive_only);\n\tdst->z = rand_float(positive_only);\n\tdst->w = 0.0f;\n}\n"
  },
  {
    "path": "libobs/graphics/vec3.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"math-defs.h\"\n#include \"vec4.h\"\n\n#include \"../util/sse-intrin.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct plane;\nstruct matrix3;\nstruct matrix4;\nstruct quat;\n\nstruct vec3 {\n\tunion {\n\t\tstruct {\n\t\t\tfloat x, y, z, w;\n\t\t};\n\t\tfloat ptr[4];\n\t\t__m128 m;\n\t};\n};\n\nstatic inline void vec3_zero(struct vec3 *v)\n{\n\tv->m = _mm_setzero_ps();\n}\n\nstatic inline void vec3_set(struct vec3 *dst, float x, float y, float z)\n{\n\tdst->m = _mm_set_ps(0.0f, z, y, x);\n}\n\nstatic inline void vec3_copy(struct vec3 *dst, const struct vec3 *v)\n{\n\tdst->m = v->m;\n}\n\nEXPORT void vec3_from_vec4(struct vec3 *dst, const struct vec4 *v);\n\nstatic inline void vec3_add(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n{\n\tdst->m = _mm_add_ps(v1->m, v2->m);\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_sub(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n{\n\tdst->m = _mm_sub_ps(v1->m, v2->m);\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_mul(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n{\n\tdst->m = _mm_mul_ps(v1->m, v2->m);\n}\n\nstatic inline void vec3_div(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n{\n\tdst->m = _mm_div_ps(v1->m, v2->m);\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_addf(struct vec3 *dst, const struct vec3 *v, float f)\n{\n\tdst->m = _mm_add_ps(v->m, _mm_set1_ps(f));\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_subf(struct vec3 *dst, const struct vec3 *v, float f)\n{\n\tdst->m = _mm_sub_ps(v->m, _mm_set1_ps(f));\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_mulf(struct vec3 *dst, const struct vec3 *v, float f)\n{\n\tdst->m = _mm_mul_ps(v->m, _mm_set1_ps(f));\n}\n\nstatic inline void vec3_divf(struct vec3 *dst, const struct vec3 *v, float f)\n{\n\tdst->m = _mm_div_ps(v->m, _mm_set1_ps(f));\n\tdst->w = 0.0f;\n}\n\nstatic inline float vec3_dot(const struct vec3 *v1, const struct vec3 *v2)\n{\n\tstruct vec3 add;\n\t__m128 mul = _mm_mul_ps(v1->m, v2->m);\n\tadd.m = _mm_add_ps(_mm_movehl_ps(mul, mul), mul);\n\tadd.m = _mm_add_ps(_mm_shuffle_ps(add.m, add.m, 0x55), add.m);\n\treturn add.x;\n}\n\nstatic inline void vec3_cross(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n{\n\t__m128 s1v1 = _mm_shuffle_ps(v1->m, v1->m, _MM_SHUFFLE(3, 0, 2, 1));\n\t__m128 s1v2 = _mm_shuffle_ps(v2->m, v2->m, _MM_SHUFFLE(3, 1, 0, 2));\n\t__m128 s2v1 = _mm_shuffle_ps(v1->m, v1->m, _MM_SHUFFLE(3, 1, 0, 2));\n\t__m128 s2v2 = _mm_shuffle_ps(v2->m, v2->m, _MM_SHUFFLE(3, 0, 2, 1));\n\tdst->m = _mm_sub_ps(_mm_mul_ps(s1v1, s1v2), _mm_mul_ps(s2v1, s2v2));\n}\n\nstatic inline void vec3_neg(struct vec3 *dst, const struct vec3 *v)\n{\n\tdst->x = -v->x;\n\tdst->y = -v->y;\n\tdst->z = -v->z;\n\tdst->w = 0.0f;\n}\n\nstatic inline float vec3_len(const struct vec3 *v)\n{\n\tfloat dot_val = vec3_dot(v, v);\n\treturn (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;\n}\n\nstatic inline float vec3_dist(const struct vec3 *v1, const struct vec3 *v2)\n{\n\tstruct vec3 temp;\n\tfloat dot_val;\n\n\tvec3_sub(&temp, v1, v2);\n\tdot_val = vec3_dot(&temp, &temp);\n\treturn (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;\n}\n\nstatic inline void vec3_norm(struct vec3 *dst, const struct vec3 *v)\n{\n\tfloat dot_val = vec3_dot(v, v);\n\tdst->m = (dot_val > 0.0f) ? _mm_mul_ps(v->m, _mm_set1_ps(1.0f / sqrtf(dot_val))) : _mm_setzero_ps();\n}\n\nstatic inline bool vec3_close(const struct vec3 *v1, const struct vec3 *v2, float epsilon)\n{\n\tstruct vec3 test;\n\tvec3_sub(&test, v1, v2);\n\treturn test.x < epsilon && test.y < epsilon && test.z < epsilon;\n}\n\nstatic inline void vec3_min(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n{\n\tdst->m = _mm_min_ps(v1->m, v2->m);\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_minf(struct vec3 *dst, const struct vec3 *v, float f)\n{\n\tdst->m = _mm_min_ps(v->m, _mm_set1_ps(f));\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_max(struct vec3 *dst, const struct vec3 *v1, const struct vec3 *v2)\n{\n\tdst->m = _mm_max_ps(v1->m, v2->m);\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_maxf(struct vec3 *dst, const struct vec3 *v, float f)\n{\n\tdst->m = _mm_max_ps(v->m, _mm_set1_ps(f));\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_abs(struct vec3 *dst, const struct vec3 *v)\n{\n\tdst->x = fabsf(v->x);\n\tdst->y = fabsf(v->y);\n\tdst->z = fabsf(v->z);\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_floor(struct vec3 *dst, const struct vec3 *v)\n{\n\tdst->x = floorf(v->x);\n\tdst->y = floorf(v->y);\n\tdst->z = floorf(v->z);\n\tdst->w = 0.0f;\n}\n\nstatic inline void vec3_ceil(struct vec3 *dst, const struct vec3 *v)\n{\n\tdst->x = ceilf(v->x);\n\tdst->y = ceilf(v->y);\n\tdst->z = ceilf(v->z);\n\tdst->w = 0.0f;\n}\n\nEXPORT float vec3_plane_dist(const struct vec3 *v, const struct plane *p);\n\nEXPORT void vec3_transform(struct vec3 *dst, const struct vec3 *v, const struct matrix4 *m);\n\nEXPORT void vec3_rotate(struct vec3 *dst, const struct vec3 *v, const struct matrix3 *m);\nEXPORT void vec3_transform3x4(struct vec3 *dst, const struct vec3 *v, const struct matrix3 *m);\n\nEXPORT void vec3_mirror(struct vec3 *dst, const struct vec3 *v, const struct plane *p);\nEXPORT void vec3_mirrorv(struct vec3 *dst, const struct vec3 *v, const struct vec3 *vec);\n\nEXPORT void vec3_rand(struct vec3 *dst, int positive_only);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/graphics/vec4.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"vec4.h\"\n#include \"vec3.h\"\n#include \"matrix4.h\"\n\nvoid vec4_from_vec3(struct vec4 *dst, const struct vec3 *v)\n{\n\tdst->m = v->m;\n\tdst->w = 1.0f;\n}\n\nvoid vec4_transform(struct vec4 *dst, const struct vec4 *v, const struct matrix4 *m)\n{\n\tstruct vec4 temp;\n\tstruct matrix4 transpose;\n\n\tmatrix4_transpose(&transpose, m);\n\n\ttemp.x = vec4_dot(&transpose.x, v);\n\ttemp.y = vec4_dot(&transpose.y, v);\n\ttemp.z = vec4_dot(&transpose.z, v);\n\ttemp.w = vec4_dot(&transpose.t, v);\n\n\tvec4_copy(dst, &temp);\n}\n"
  },
  {
    "path": "libobs/graphics/vec4.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"math-defs.h\"\n#include \"srgb.h\"\n\n#include \"../util/sse-intrin.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct vec3;\nstruct matrix4;\n\nstruct vec4 {\n\tunion {\n\t\tstruct {\n\t\t\tfloat x, y, z, w;\n\t\t};\n\t\tfloat ptr[4];\n\t\t__m128 m;\n\t};\n};\n\nstatic inline void vec4_zero(struct vec4 *v)\n{\n\tv->m = _mm_setzero_ps();\n}\n\nstatic inline void vec4_set(struct vec4 *dst, float x, float y, float z, float w)\n{\n\tdst->m = _mm_set_ps(w, z, y, x);\n}\n\nstatic inline void vec4_copy(struct vec4 *dst, const struct vec4 *v)\n{\n\tdst->m = v->m;\n}\n\nEXPORT void vec4_from_vec3(struct vec4 *dst, const struct vec3 *v);\n\nstatic inline void vec4_add(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2)\n{\n\tdst->m = _mm_add_ps(v1->m, v2->m);\n}\n\nstatic inline void vec4_sub(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2)\n{\n\tdst->m = _mm_sub_ps(v1->m, v2->m);\n}\n\nstatic inline void vec4_mul(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2)\n{\n\tdst->m = _mm_mul_ps(v1->m, v2->m);\n}\n\nstatic inline void vec4_div(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2)\n{\n\tdst->m = _mm_div_ps(v1->m, v2->m);\n}\n\nstatic inline void vec4_addf(struct vec4 *dst, const struct vec4 *v, float f)\n{\n\tdst->m = _mm_add_ps(v->m, _mm_set1_ps(f));\n}\n\nstatic inline void vec4_subf(struct vec4 *dst, const struct vec4 *v, float f)\n{\n\tdst->m = _mm_sub_ps(v->m, _mm_set1_ps(f));\n}\n\nstatic inline void vec4_mulf(struct vec4 *dst, const struct vec4 *v, float f)\n{\n\tdst->m = _mm_mul_ps(v->m, _mm_set1_ps(f));\n}\n\nstatic inline void vec4_divf(struct vec4 *dst, const struct vec4 *v, float f)\n{\n\tdst->m = _mm_div_ps(v->m, _mm_set1_ps(f));\n}\n\nstatic inline float vec4_dot(const struct vec4 *v1, const struct vec4 *v2)\n{\n\tstruct vec4 add;\n\t__m128 mul = _mm_mul_ps(v1->m, v2->m);\n\tadd.m = _mm_add_ps(_mm_movehl_ps(mul, mul), mul);\n\tadd.m = _mm_add_ps(_mm_shuffle_ps(add.m, add.m, 0x55), add.m);\n\treturn add.x;\n}\n\nstatic inline void vec4_neg(struct vec4 *dst, const struct vec4 *v)\n{\n\tdst->x = -v->x;\n\tdst->y = -v->y;\n\tdst->z = -v->z;\n\tdst->w = -v->w;\n}\n\nstatic inline float vec4_len(const struct vec4 *v)\n{\n\tfloat dot_val = vec4_dot(v, v);\n\treturn (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;\n}\n\nstatic inline float vec4_dist(const struct vec4 *v1, const struct vec4 *v2)\n{\n\tstruct vec4 temp;\n\tfloat dot_val;\n\n\tvec4_sub(&temp, v1, v2);\n\tdot_val = vec4_dot(&temp, &temp);\n\treturn (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;\n}\n\nstatic inline void vec4_norm(struct vec4 *dst, const struct vec4 *v)\n{\n\tfloat dot_val = vec4_dot(v, v);\n\tdst->m = (dot_val > 0.0f) ? _mm_mul_ps(v->m, _mm_set1_ps(1.0f / sqrtf(dot_val))) : _mm_setzero_ps();\n}\n\nstatic inline int vec4_close(const struct vec4 *v1, const struct vec4 *v2, float epsilon)\n{\n\tstruct vec4 test;\n\tvec4_sub(&test, v1, v2);\n\treturn test.x < epsilon && test.y < epsilon && test.z < epsilon && test.w < epsilon;\n}\n\nstatic inline void vec4_min(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2)\n{\n\tdst->m = _mm_min_ps(v1->m, v2->m);\n}\n\nstatic inline void vec4_minf(struct vec4 *dst, const struct vec4 *v, float f)\n{\n\tdst->m = _mm_min_ps(v->m, _mm_set1_ps(f));\n}\n\nstatic inline void vec4_max(struct vec4 *dst, const struct vec4 *v1, const struct vec4 *v2)\n{\n\tdst->m = _mm_max_ps(v1->m, v2->m);\n}\n\nstatic inline void vec4_maxf(struct vec4 *dst, const struct vec4 *v, float f)\n{\n\tdst->m = _mm_max_ps(v->m, _mm_set1_ps(f));\n}\n\nstatic inline void vec4_abs(struct vec4 *dst, const struct vec4 *v)\n{\n\tdst->x = fabsf(v->x);\n\tdst->y = fabsf(v->y);\n\tdst->z = fabsf(v->z);\n\tdst->w = fabsf(v->w);\n}\n\nstatic inline void vec4_floor(struct vec4 *dst, const struct vec4 *v)\n{\n\tdst->x = floorf(v->x);\n\tdst->y = floorf(v->y);\n\tdst->z = floorf(v->z);\n\tdst->w = floorf(v->w);\n}\n\nstatic inline void vec4_ceil(struct vec4 *dst, const struct vec4 *v)\n{\n\tdst->x = ceilf(v->x);\n\tdst->y = ceilf(v->y);\n\tdst->z = ceilf(v->z);\n\tdst->w = ceilf(v->w);\n}\n\nstatic inline uint32_t vec4_to_rgba(const struct vec4 *src)\n{\n\tfloat f[4];\n\tmemcpy(f, src->ptr, sizeof(f));\n\tuint8_t u[4];\n\tgs_float4_to_u8x4(u, f);\n\tuint32_t val;\n\tmemcpy(&val, u, sizeof(val));\n\treturn val;\n}\n\nstatic inline uint32_t vec4_to_bgra(const struct vec4 *src)\n{\n\tfloat f[4];\n\tmemcpy(f, src->ptr, sizeof(f));\n\tuint8_t u[4];\n\tgs_float4_to_u8x4(u, f);\n\tuint8_t temp = u[0];\n\tu[0] = u[2];\n\tu[2] = temp;\n\tuint32_t val;\n\tmemcpy(&val, u, sizeof(val));\n\treturn val;\n}\n\nstatic inline void vec4_from_rgba(struct vec4 *dst, uint32_t rgba)\n{\n\tuint8_t u[4];\n\tmemcpy(u, &rgba, sizeof(u));\n\tgs_u8x4_to_float4(dst->ptr, u);\n}\n\nstatic inline void vec4_from_bgra(struct vec4 *dst, uint32_t bgra)\n{\n\tuint8_t u[4];\n\tmemcpy(u, &bgra, sizeof(u));\n\tuint8_t temp = u[0];\n\tu[0] = u[2];\n\tu[2] = temp;\n\tgs_u8x4_to_float4(dst->ptr, u);\n}\n\nstatic inline void vec4_from_rgba_srgb(struct vec4 *dst, uint32_t rgba)\n{\n\tvec4_from_rgba(dst, rgba);\n\tgs_float3_srgb_nonlinear_to_linear(dst->ptr);\n}\n\nEXPORT void vec4_transform(struct vec4 *dst, const struct vec4 *v, const struct matrix4 *m);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/media-io/audio-io.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <math.h>\n#include <inttypes.h>\n\n#include \"../util/threading.h\"\n#include \"../util/darray.h\"\n#include \"../util/deque.h\"\n#include \"../util/platform.h\"\n#include \"../util/profiler.h\"\n#include \"../util/util_uint64.h\"\n\n#include \"audio-io.h\"\n#include \"audio-resampler.h\"\n\n#ifdef _WIN32\n#define WIN32_LEAN_AND_MEAN\n#include <Windows.h>\n#include <avrt.h>\n#endif\n\nextern profiler_name_store_t *obs_get_profiler_name_store(void);\n\n/* #define DEBUG_AUDIO */\n\n#define nop()                    \\\n\tdo {                     \\\n\t\tint invalid = 0; \\\n\t} while (0)\n\nstruct audio_input {\n\tstruct audio_convert_info conversion;\n\taudio_resampler_t *resampler;\n\n\taudio_output_callback_t callback;\n\tvoid *param;\n};\n\nstatic inline void audio_input_free(struct audio_input *input)\n{\n\taudio_resampler_destroy(input->resampler);\n}\n\nstruct audio_mix {\n\tDARRAY(struct audio_input) inputs;\n\tfloat buffer[MAX_AUDIO_CHANNELS][AUDIO_OUTPUT_FRAMES];\n\tfloat buffer_unclamped[MAX_AUDIO_CHANNELS][AUDIO_OUTPUT_FRAMES];\n};\n\nstruct audio_output {\n\tstruct audio_output_info info;\n\tsize_t block_size;\n\tsize_t channels;\n\tsize_t planes;\n\n\tpthread_t thread;\n\tos_event_t *stop_event;\n\n\tbool initialized;\n\n\taudio_input_callback_t input_cb;\n\tvoid *input_param;\n\tpthread_mutex_t input_mutex;\n\tstruct audio_mix mixes[MAX_AUDIO_MIXES];\n};\n\n/* ------------------------------------------------------------------------- */\n\nstatic bool resample_audio_output(struct audio_input *input, struct audio_data *data)\n{\n\tbool success = true;\n\n\tif (input->resampler) {\n\t\tuint8_t *output[MAX_AV_PLANES];\n\t\tuint32_t frames;\n\t\tuint64_t offset;\n\n\t\tmemset(output, 0, sizeof(output));\n\n\t\tsuccess = audio_resampler_resample(input->resampler, output, &frames, &offset,\n\t\t\t\t\t\t   (const uint8_t *const *)data->data, data->frames);\n\n\t\tfor (size_t i = 0; i < MAX_AV_PLANES; i++)\n\t\t\tdata->data[i] = output[i];\n\t\tdata->frames = frames;\n\t\tdata->timestamp -= offset;\n\t}\n\n\treturn success;\n}\n\nstatic inline void do_audio_output(struct audio_output *audio, size_t mix_idx, uint64_t timestamp, uint32_t frames)\n{\n\tstruct audio_mix *mix = &audio->mixes[mix_idx];\n\tstruct audio_data data;\n\n\tpthread_mutex_lock(&audio->input_mutex);\n\n\tfor (size_t i = mix->inputs.num; i > 0; i--) {\n\t\tstruct audio_input *input = mix->inputs.array + (i - 1);\n\n\t\tfloat(*buf)[AUDIO_OUTPUT_FRAMES] = input->conversion.allow_clipping ? mix->buffer_unclamped\n\t\t\t\t\t\t\t\t\t\t    : mix->buffer;\n\t\tfor (size_t i = 0; i < audio->planes; i++)\n\t\t\tdata.data[i] = (uint8_t *)buf[i];\n\n\t\tdata.frames = frames;\n\t\tdata.timestamp = timestamp;\n\n\t\tif (resample_audio_output(input, &data))\n\t\t\tinput->callback(input->param, mix_idx, &data);\n\t}\n\n\tpthread_mutex_unlock(&audio->input_mutex);\n}\n\nstatic inline void clamp_audio_output(struct audio_output *audio, size_t bytes)\n{\n\tsize_t float_size = bytes / sizeof(float);\n\n\tfor (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {\n\t\tstruct audio_mix *mix = &audio->mixes[mix_idx];\n\n\t\t/* do not process mixing if a specific mix is inactive */\n\t\tif (!mix->inputs.num)\n\t\t\tcontinue;\n\n\t\tfor (size_t plane = 0; plane < audio->planes; plane++) {\n\t\t\tfloat *mix_data = mix->buffer[plane];\n\t\t\tfloat *mix_end = &mix_data[float_size];\n\t\t\t/* Unclamped mix is copied directly. */\n\t\t\tmemcpy(mix->buffer_unclamped[plane], mix_data, bytes);\n\n\t\t\twhile (mix_data < mix_end) {\n\t\t\t\tfloat val = *mix_data;\n\t\t\t\tval = (val == val) ? val : 0.0f;\n\t\t\t\tval = (val > 1.0f) ? 1.0f : val;\n\t\t\t\tval = (val < -1.0f) ? -1.0f : val;\n\t\t\t\t*(mix_data++) = val;\n\t\t\t}\n\t\t}\n\t}\n}\n\nstatic void input_and_output(struct audio_output *audio, uint64_t audio_time, uint64_t prev_time)\n{\n\tsize_t bytes = AUDIO_OUTPUT_FRAMES * audio->block_size;\n\tstruct audio_output_data data[MAX_AUDIO_MIXES];\n\tuint32_t active_mixes = 0;\n\tuint64_t new_ts = 0;\n\tbool success;\n\n\tmemset(data, 0, sizeof(data));\n\n#ifdef DEBUG_AUDIO\n\tblog(LOG_DEBUG, \"audio_time: %llu, prev_time: %llu, bytes: %lu\", audio_time, prev_time, bytes);\n#endif\n\n\t/* get mixers */\n\tpthread_mutex_lock(&audio->input_mutex);\n\tfor (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\tif (audio->mixes[i].inputs.num)\n\t\t\tactive_mixes |= (1 << i);\n\t}\n\tpthread_mutex_unlock(&audio->input_mutex);\n\n\t/* clear mix buffers */\n\tfor (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {\n\t\tstruct audio_mix *mix = &audio->mixes[mix_idx];\n\n\t\tmemset(mix->buffer, 0, sizeof(mix->buffer));\n\n\t\tfor (size_t i = 0; i < audio->planes; i++)\n\t\t\tdata[mix_idx].data[i] = mix->buffer[i];\n\t}\n\n\t/* get new audio data */\n\tsuccess = audio->input_cb(audio->input_param, prev_time, audio_time, &new_ts, active_mixes, data);\n\tif (!success)\n\t\treturn;\n\n\t/* clamps audio data to -1.0..1.0 */\n\tclamp_audio_output(audio, bytes);\n\n\t/* output */\n\tfor (size_t i = 0; i < MAX_AUDIO_MIXES; i++)\n\t\tdo_audio_output(audio, i, new_ts, AUDIO_OUTPUT_FRAMES);\n}\n\nstatic void *audio_thread(void *param)\n{\n#ifdef _WIN32\n\tDWORD unused = 0;\n\tconst HANDLE handle = AvSetMmThreadCharacteristics(L\"Audio\", &unused);\n#endif\n\n\tstruct audio_output *audio = param;\n\tsize_t rate = audio->info.samples_per_sec;\n\tuint64_t samples = 0;\n\tuint64_t start_time = os_gettime_ns();\n\tuint64_t prev_time = start_time;\n\n\tos_set_thread_name(\"audio-io: audio thread\");\n\n\tconst char *audio_thread_name =\n\t\tprofile_store_name(obs_get_profiler_name_store(), \"audio_thread(%s)\", audio->info.name);\n\n\twhile (os_event_try(audio->stop_event) == EAGAIN) {\n\t\tsamples += AUDIO_OUTPUT_FRAMES;\n\t\tuint64_t audio_time = start_time + audio_frames_to_ns(rate, samples);\n\n\t\tos_sleepto_ns_fast(audio_time);\n\n\t\tprofile_start(audio_thread_name);\n\n\t\tinput_and_output(audio, audio_time, prev_time);\n\t\tprev_time = audio_time;\n\n\t\tprofile_end(audio_thread_name);\n\n\t\tprofile_reenable_thread();\n\t}\n\n#ifdef _WIN32\n\tif (handle)\n\t\tAvRevertMmThreadCharacteristics(handle);\n#endif\n\n\treturn NULL;\n}\n\n/* ------------------------------------------------------------------------- */\n\nstatic size_t audio_get_input_idx(const audio_t *audio, size_t mix_idx, audio_output_callback_t callback, void *param)\n{\n\tconst struct audio_mix *mix = &audio->mixes[mix_idx];\n\n\tfor (size_t i = 0; i < mix->inputs.num; i++) {\n\t\tstruct audio_input *input = mix->inputs.array + i;\n\n\t\tif (input->callback == callback && input->param == param)\n\t\t\treturn i;\n\t}\n\n\treturn DARRAY_INVALID;\n}\n\nstatic inline bool audio_input_init(struct audio_input *input, struct audio_output *audio)\n{\n\tif (input->conversion.format != audio->info.format ||\n\t    input->conversion.samples_per_sec != audio->info.samples_per_sec ||\n\t    input->conversion.speakers != audio->info.speakers) {\n\t\tstruct resample_info from = {.format = audio->info.format,\n\t\t\t\t\t     .samples_per_sec = audio->info.samples_per_sec,\n\t\t\t\t\t     .speakers = audio->info.speakers};\n\n\t\tstruct resample_info to = {.format = input->conversion.format,\n\t\t\t\t\t   .samples_per_sec = input->conversion.samples_per_sec,\n\t\t\t\t\t   .speakers = input->conversion.speakers};\n\n\t\tinput->resampler = audio_resampler_create(&to, &from);\n\t\tif (!input->resampler) {\n\t\t\tblog(LOG_ERROR, \"audio_input_init: Failed to \"\n\t\t\t\t\t\"create resampler\");\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\tinput->resampler = NULL;\n\t}\n\n\treturn true;\n}\n\nbool audio_output_connect(audio_t *audio, size_t mi, const struct audio_convert_info *conversion,\n\t\t\t  audio_output_callback_t callback, void *param)\n{\n\tbool success = false;\n\n\tif (!audio || mi >= MAX_AUDIO_MIXES)\n\t\treturn false;\n\n\tpthread_mutex_lock(&audio->input_mutex);\n\n\tif (audio_get_input_idx(audio, mi, callback, param) == DARRAY_INVALID) {\n\t\tstruct audio_mix *mix = &audio->mixes[mi];\n\t\tstruct audio_input input = {\n\t\t\t.callback = callback,\n\t\t\t.param = param,\n\t\t};\n\n\t\tif (conversion) {\n\t\t\tinput.conversion = *conversion;\n\t\t} else {\n\t\t\tinput.conversion.format = audio->info.format;\n\t\t\tinput.conversion.speakers = audio->info.speakers;\n\t\t\tinput.conversion.samples_per_sec = audio->info.samples_per_sec;\n\t\t}\n\n\t\tif (input.conversion.format == AUDIO_FORMAT_UNKNOWN)\n\t\t\tinput.conversion.format = audio->info.format;\n\t\tif (input.conversion.speakers == SPEAKERS_UNKNOWN)\n\t\t\tinput.conversion.speakers = audio->info.speakers;\n\t\tif (input.conversion.samples_per_sec == 0)\n\t\t\tinput.conversion.samples_per_sec = audio->info.samples_per_sec;\n\n\t\tsuccess = audio_input_init(&input, audio);\n\t\tif (success)\n\t\t\tda_push_back(mix->inputs, &input);\n\t}\n\n\tpthread_mutex_unlock(&audio->input_mutex);\n\n\treturn success;\n}\n\nvoid audio_output_disconnect(audio_t *audio, size_t mix_idx, audio_output_callback_t callback, void *param)\n{\n\tif (!audio || mix_idx >= MAX_AUDIO_MIXES)\n\t\treturn;\n\n\tpthread_mutex_lock(&audio->input_mutex);\n\n\tsize_t idx = audio_get_input_idx(audio, mix_idx, callback, param);\n\tif (idx != DARRAY_INVALID) {\n\t\tstruct audio_mix *mix = &audio->mixes[mix_idx];\n\t\taudio_input_free(mix->inputs.array + idx);\n\t\tda_erase(mix->inputs, idx);\n\t}\n\n\tpthread_mutex_unlock(&audio->input_mutex);\n}\n\nstatic inline bool valid_audio_params(const struct audio_output_info *info)\n{\n\treturn info->format && info->name && info->samples_per_sec > 0 && info->speakers > 0;\n}\n\nint audio_output_open(audio_t **audio, struct audio_output_info *info)\n{\n\tstruct audio_output *out;\n\tbool planar = is_audio_planar(info->format);\n\n\tif (!valid_audio_params(info))\n\t\treturn AUDIO_OUTPUT_INVALIDPARAM;\n\n\tout = bzalloc(sizeof(struct audio_output));\n\tif (!out)\n\t\tgoto fail0;\n\n\tmemcpy(&out->info, info, sizeof(struct audio_output_info));\n\tout->channels = get_audio_channels(info->speakers);\n\tout->planes = planar ? out->channels : 1;\n\tout->input_cb = info->input_callback;\n\tout->input_param = info->input_param;\n\tout->block_size = (planar ? 1 : out->channels) * get_audio_bytes_per_channel(info->format);\n\n\tif (pthread_mutex_init_recursive(&out->input_mutex) != 0)\n\t\tgoto fail0;\n\tif (os_event_init(&out->stop_event, OS_EVENT_TYPE_MANUAL) != 0)\n\t\tgoto fail1;\n\tif (pthread_create(&out->thread, NULL, audio_thread, out) != 0)\n\t\tgoto fail2;\n\n\tout->initialized = true;\n\t*audio = out;\n\treturn AUDIO_OUTPUT_SUCCESS;\n\nfail2:\n\tos_event_destroy(out->stop_event);\nfail1:\n\tpthread_mutex_destroy(&out->input_mutex);\nfail0:\n\taudio_output_close(out);\n\treturn AUDIO_OUTPUT_FAIL;\n}\n\nvoid audio_output_close(audio_t *audio)\n{\n\tvoid *thread_ret;\n\n\tif (!audio)\n\t\treturn;\n\n\tif (audio->initialized) {\n\t\tos_event_signal(audio->stop_event);\n\t\tpthread_join(audio->thread, &thread_ret);\n\t\tos_event_destroy(audio->stop_event);\n\t\tpthread_mutex_destroy(&audio->input_mutex);\n\t}\n\n\tfor (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {\n\t\tstruct audio_mix *mix = &audio->mixes[mix_idx];\n\n\t\tfor (size_t i = 0; i < mix->inputs.num; i++)\n\t\t\taudio_input_free(mix->inputs.array + i);\n\n\t\tda_free(mix->inputs);\n\t}\n\tbfree(audio);\n}\n\nconst struct audio_output_info *audio_output_get_info(const audio_t *audio)\n{\n\treturn audio ? &audio->info : NULL;\n}\n\nbool audio_output_active(const audio_t *audio)\n{\n\tif (!audio)\n\t\treturn false;\n\n\tfor (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {\n\t\tconst struct audio_mix *mix = &audio->mixes[mix_idx];\n\n\t\tif (mix->inputs.num != 0)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nsize_t audio_output_get_block_size(const audio_t *audio)\n{\n\treturn audio->block_size;\n}\n\nsize_t audio_output_get_planes(const audio_t *audio)\n{\n\treturn audio->planes;\n}\n\nsize_t audio_output_get_channels(const audio_t *audio)\n{\n\treturn audio->channels;\n}\n\nuint32_t audio_output_get_sample_rate(const audio_t *audio)\n{\n\treturn audio->info.samples_per_sec;\n}\n"
  },
  {
    "path": "libobs/media-io/audio-io.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"media-io-defs.h\"\n#include \"../util/c99defs.h\"\n#include \"../util/util_uint64.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define MAX_AUDIO_MIXES 6\n#define MAX_AUDIO_CHANNELS 8\n#define MAX_DEVICE_INPUT_CHANNELS 64\n#define AUDIO_OUTPUT_FRAMES 1024\n\n#define TOTAL_AUDIO_SIZE (MAX_AUDIO_MIXES * MAX_AUDIO_CHANNELS * AUDIO_OUTPUT_FRAMES * sizeof(float))\n\n/*\n * Base audio output component.  Use this to create an audio output track\n * for the media.\n */\n\nstruct audio_output;\ntypedef struct audio_output audio_t;\n\nenum audio_format {\n\tAUDIO_FORMAT_UNKNOWN,\n\n\tAUDIO_FORMAT_U8BIT,\n\tAUDIO_FORMAT_16BIT,\n\tAUDIO_FORMAT_32BIT,\n\tAUDIO_FORMAT_FLOAT,\n\n\tAUDIO_FORMAT_U8BIT_PLANAR,\n\tAUDIO_FORMAT_16BIT_PLANAR,\n\tAUDIO_FORMAT_32BIT_PLANAR,\n\tAUDIO_FORMAT_FLOAT_PLANAR,\n};\n\n/**\n * The speaker layout describes where the speakers are located in the room.\n * For OBS it dictates:\n *  *  how many channels are available and\n *  *  which channels are used for which speakers.\n *\n * Standard channel layouts where retrieved from ffmpeg documentation at:\n *     https://trac.ffmpeg.org/wiki/AudioChannelManipulation\n */\nenum speaker_layout {\n\tSPEAKERS_UNKNOWN,     /**< Unknown setting, fallback is stereo. */\n\tSPEAKERS_MONO,        /**< Channels: MONO */\n\tSPEAKERS_STEREO,      /**< Channels: FL, FR */\n\tSPEAKERS_2POINT1,     /**< Channels: FL, FR, LFE */\n\tSPEAKERS_4POINT0,     /**< Channels: FL, FR, FC, RC */\n\tSPEAKERS_4POINT1,     /**< Channels: FL, FR, FC, LFE, RC */\n\tSPEAKERS_5POINT1,     /**< Channels: FL, FR, FC, LFE, RL, RR */\n\tSPEAKERS_7POINT1 = 8, /**< Channels: FL, FR, FC, LFE, RL, RR, SL, SR */\n};\n\nstruct audio_data {\n\tuint8_t *data[MAX_AV_PLANES];\n\tuint32_t frames;\n\tuint64_t timestamp;\n};\n\nstruct audio_output_data {\n\tfloat *data[MAX_AUDIO_CHANNELS];\n};\n\ntypedef bool (*audio_input_callback_t)(void *param, uint64_t start_ts, uint64_t end_ts, uint64_t *new_ts,\n\t\t\t\t       uint32_t active_mixers, struct audio_output_data *mixes);\n\nstruct audio_output_info {\n\tconst char *name;\n\n\tuint32_t samples_per_sec;\n\tenum audio_format format;\n\tenum speaker_layout speakers;\n\n\taudio_input_callback_t input_callback;\n\tvoid *input_param;\n};\n\nstruct audio_convert_info {\n\tuint32_t samples_per_sec;\n\tenum audio_format format;\n\tenum speaker_layout speakers;\n\tbool allow_clipping;\n};\n\nstatic inline uint32_t get_audio_channels(enum speaker_layout speakers)\n{\n\tswitch (speakers) {\n\tcase SPEAKERS_MONO:\n\t\treturn 1;\n\tcase SPEAKERS_STEREO:\n\t\treturn 2;\n\tcase SPEAKERS_2POINT1:\n\t\treturn 3;\n\tcase SPEAKERS_4POINT0:\n\t\treturn 4;\n\tcase SPEAKERS_4POINT1:\n\t\treturn 5;\n\tcase SPEAKERS_5POINT1:\n\t\treturn 6;\n\tcase SPEAKERS_7POINT1:\n\t\treturn 8;\n\tcase SPEAKERS_UNKNOWN:\n\t\treturn 0;\n\t}\n\n\treturn 0;\n}\n\nstatic inline size_t get_audio_bytes_per_channel(enum audio_format format)\n{\n\tswitch (format) {\n\tcase AUDIO_FORMAT_U8BIT:\n\tcase AUDIO_FORMAT_U8BIT_PLANAR:\n\t\treturn 1;\n\n\tcase AUDIO_FORMAT_16BIT:\n\tcase AUDIO_FORMAT_16BIT_PLANAR:\n\t\treturn 2;\n\n\tcase AUDIO_FORMAT_FLOAT:\n\tcase AUDIO_FORMAT_FLOAT_PLANAR:\n\tcase AUDIO_FORMAT_32BIT:\n\tcase AUDIO_FORMAT_32BIT_PLANAR:\n\t\treturn 4;\n\n\tcase AUDIO_FORMAT_UNKNOWN:\n\t\treturn 0;\n\t}\n\n\treturn 0;\n}\n\nstatic inline bool is_audio_planar(enum audio_format format)\n{\n\tswitch (format) {\n\tcase AUDIO_FORMAT_U8BIT:\n\tcase AUDIO_FORMAT_16BIT:\n\tcase AUDIO_FORMAT_32BIT:\n\tcase AUDIO_FORMAT_FLOAT:\n\t\treturn false;\n\n\tcase AUDIO_FORMAT_U8BIT_PLANAR:\n\tcase AUDIO_FORMAT_FLOAT_PLANAR:\n\tcase AUDIO_FORMAT_16BIT_PLANAR:\n\tcase AUDIO_FORMAT_32BIT_PLANAR:\n\t\treturn true;\n\n\tcase AUDIO_FORMAT_UNKNOWN:\n\t\treturn false;\n\t}\n\n\treturn false;\n}\n\nstatic inline size_t get_audio_planes(enum audio_format format, enum speaker_layout speakers)\n{\n\treturn (is_audio_planar(format) ? get_audio_channels(speakers) : 1);\n}\n\nstatic inline size_t get_audio_size(enum audio_format format, enum speaker_layout speakers, uint32_t frames)\n{\n\tbool planar = is_audio_planar(format);\n\n\treturn (planar ? 1 : get_audio_channels(speakers)) * get_audio_bytes_per_channel(format) * frames;\n}\n\nstatic inline size_t get_total_audio_size(enum audio_format format, enum speaker_layout speakers, uint32_t frames)\n{\n\treturn get_audio_channels(speakers) * get_audio_bytes_per_channel(format) * frames;\n}\n\nstatic inline uint64_t audio_frames_to_ns(size_t sample_rate, uint64_t frames)\n{\n\treturn util_mul_div64(frames, 1000000000ULL, sample_rate);\n}\n\nstatic inline uint64_t ns_to_audio_frames(size_t sample_rate, uint64_t frames)\n{\n\treturn util_mul_div64(frames, sample_rate, 1000000000ULL);\n}\n\n#define AUDIO_OUTPUT_SUCCESS 0\n#define AUDIO_OUTPUT_INVALIDPARAM -1\n#define AUDIO_OUTPUT_FAIL -2\n\nEXPORT int audio_output_open(audio_t **audio, struct audio_output_info *info);\nEXPORT void audio_output_close(audio_t *audio);\n\ntypedef void (*audio_output_callback_t)(void *param, size_t mix_idx, struct audio_data *data);\n\nEXPORT bool audio_output_connect(audio_t *video, size_t mix_idx, const struct audio_convert_info *conversion,\n\t\t\t\t audio_output_callback_t callback, void *param);\nEXPORT void audio_output_disconnect(audio_t *video, size_t mix_idx, audio_output_callback_t callback, void *param);\n\nEXPORT bool audio_output_active(const audio_t *audio);\n\nEXPORT size_t audio_output_get_block_size(const audio_t *audio);\nEXPORT size_t audio_output_get_planes(const audio_t *audio);\nEXPORT size_t audio_output_get_channels(const audio_t *audio);\nEXPORT uint32_t audio_output_get_sample_rate(const audio_t *audio);\nEXPORT const struct audio_output_info *audio_output_get_info(const audio_t *audio);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/media-io/audio-math.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n#include <math.h>\n\n#ifdef _MSC_VER\n#include <float.h>\n\n#pragma warning(push)\n#pragma warning(disable : 4056)\n#pragma warning(disable : 4756)\n#endif\n\nstatic inline float mul_to_db(const float mul)\n{\n\treturn (mul == 0.0f) ? -INFINITY : (20.0f * log10f(mul));\n}\n\nstatic inline float db_to_mul(const float db)\n{\n\treturn isfinite((double)db) ? powf(10.0f, db / 20.0f) : 0.0f;\n}\n\n#ifdef _MSC_VER\n#pragma warning(pop)\n#endif\n"
  },
  {
    "path": "libobs/media-io/audio-resampler-ffmpeg.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"../util/bmem.h\"\n#include \"audio-resampler.h\"\n#include \"audio-io.h\"\n#include <libavutil/avutil.h>\n#include <libavformat/avformat.h>\n#include <libswresample/swresample.h>\n\nstruct audio_resampler {\n\tstruct SwrContext *context;\n\tbool opened;\n\n\tuint32_t input_freq;\n\tenum AVSampleFormat input_format;\n\tuint8_t *output_buffer[MAX_AV_PLANES];\n\tenum AVSampleFormat output_format;\n\tint output_size;\n\tuint32_t output_ch;\n\tuint32_t output_freq;\n\tuint32_t output_planes;\n#if LIBSWRESAMPLE_VERSION_INT < AV_VERSION_INT(4, 5, 100)\n\tuint64_t input_layout;\n\tuint64_t output_layout;\n#else\n\tAVChannelLayout input_ch_layout;\n\tAVChannelLayout output_ch_layout;\n#endif\n};\n\nstatic inline enum AVSampleFormat convert_audio_format(enum audio_format format)\n{\n\tswitch (format) {\n\tcase AUDIO_FORMAT_UNKNOWN:\n\t\treturn AV_SAMPLE_FMT_S16;\n\tcase AUDIO_FORMAT_U8BIT:\n\t\treturn AV_SAMPLE_FMT_U8;\n\tcase AUDIO_FORMAT_16BIT:\n\t\treturn AV_SAMPLE_FMT_S16;\n\tcase AUDIO_FORMAT_32BIT:\n\t\treturn AV_SAMPLE_FMT_S32;\n\tcase AUDIO_FORMAT_FLOAT:\n\t\treturn AV_SAMPLE_FMT_FLT;\n\tcase AUDIO_FORMAT_U8BIT_PLANAR:\n\t\treturn AV_SAMPLE_FMT_U8P;\n\tcase AUDIO_FORMAT_16BIT_PLANAR:\n\t\treturn AV_SAMPLE_FMT_S16P;\n\tcase AUDIO_FORMAT_32BIT_PLANAR:\n\t\treturn AV_SAMPLE_FMT_S32P;\n\tcase AUDIO_FORMAT_FLOAT_PLANAR:\n\t\treturn AV_SAMPLE_FMT_FLTP;\n\t}\n\n\t/* shouldn't get here */\n\treturn AV_SAMPLE_FMT_S16;\n}\n\n#if LIBSWRESAMPLE_VERSION_INT < AV_VERSION_INT(4, 5, 100)\nstatic inline uint64_t convert_speaker_layout(enum speaker_layout layout)\n{\n\tswitch (layout) {\n\tcase SPEAKERS_UNKNOWN:\n\t\treturn 0;\n\tcase SPEAKERS_MONO:\n\t\treturn AV_CH_LAYOUT_MONO;\n\tcase SPEAKERS_STEREO:\n\t\treturn AV_CH_LAYOUT_STEREO;\n\tcase SPEAKERS_2POINT1:\n\t\treturn AV_CH_LAYOUT_SURROUND;\n\tcase SPEAKERS_4POINT0:\n\t\treturn AV_CH_LAYOUT_4POINT0;\n\tcase SPEAKERS_4POINT1:\n\t\treturn AV_CH_LAYOUT_4POINT1;\n\tcase SPEAKERS_5POINT1:\n\t\treturn AV_CH_LAYOUT_5POINT1_BACK;\n\tcase SPEAKERS_7POINT1:\n\t\treturn AV_CH_LAYOUT_7POINT1;\n\t}\n\n\t/* shouldn't get here */\n\treturn 0;\n}\n#endif\n\naudio_resampler_t *audio_resampler_create(const struct resample_info *dst, const struct resample_info *src)\n{\n\tstruct audio_resampler *rs = bzalloc(sizeof(struct audio_resampler));\n\tint errcode;\n\n\trs->opened = false;\n\trs->input_freq = src->samples_per_sec;\n\trs->input_format = convert_audio_format(src->format);\n\trs->output_size = 0;\n\trs->output_ch = get_audio_channels(dst->speakers);\n\trs->output_freq = dst->samples_per_sec;\n\trs->output_format = convert_audio_format(dst->format);\n\trs->output_planes = is_audio_planar(dst->format) ? rs->output_ch : 1;\n\n#if (LIBSWRESAMPLE_VERSION_INT < AV_VERSION_INT(4, 5, 100))\n\trs->input_layout = convert_speaker_layout(src->speakers);\n\trs->output_layout = convert_speaker_layout(dst->speakers);\n\trs->context = swr_alloc_set_opts(NULL, rs->output_layout, rs->output_format, dst->samples_per_sec,\n\t\t\t\t\t rs->input_layout, rs->input_format, src->samples_per_sec, 0, NULL);\n#else\n\tint nb_ch = get_audio_channels(src->speakers);\n\tav_channel_layout_default(&rs->input_ch_layout, nb_ch);\n\tav_channel_layout_default(&rs->output_ch_layout, rs->output_ch);\n\tif (src->speakers == SPEAKERS_4POINT1)\n\t\trs->input_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_4POINT1;\n\n\tif (dst->speakers == SPEAKERS_4POINT1)\n\t\trs->output_ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_4POINT1;\n\n\tswr_alloc_set_opts2(&rs->context, &rs->output_ch_layout, rs->output_format, dst->samples_per_sec,\n\t\t\t    &rs->input_ch_layout, rs->input_format, src->samples_per_sec, 0, NULL);\n#endif\n\n\tif (!rs->context) {\n\t\tblog(LOG_ERROR, \"swr_alloc_set_opts failed\");\n\t\taudio_resampler_destroy(rs);\n\t\treturn NULL;\n\t}\n\n#if (LIBSWRESAMPLE_VERSION_INT < AV_VERSION_INT(4, 5, 100))\n\tif (rs->input_layout == AV_CH_LAYOUT_MONO && rs->output_ch > 1) {\n#else\n\tAVChannelLayout test_ch = AV_CHANNEL_LAYOUT_MONO;\n\tif (av_channel_layout_compare(&rs->input_ch_layout, &test_ch) == 0 && rs->output_ch > 1) {\n#endif\n\t\tconst double matrix[MAX_AUDIO_CHANNELS][MAX_AUDIO_CHANNELS] = {\n\t\t\t{1},\n\t\t\t{1, 1},\n\t\t\t{1, 1, 0},\n\t\t\t{1, 1, 1, 1},\n\t\t\t{1, 1, 1, 0, 1},\n\t\t\t{1, 1, 1, 1, 1, 1},\n\t\t\t{1, 1, 1, 0, 1, 1, 1},\n\t\t\t{1, 1, 1, 0, 1, 1, 1, 1},\n\t\t};\n\t\tif (swr_set_matrix(rs->context, matrix[rs->output_ch - 1], 1) < 0)\n\t\t\tblog(LOG_DEBUG, \"swr_set_matrix failed for mono upmix\\n\");\n\t}\n\n\terrcode = swr_init(rs->context);\n\tif (errcode != 0) {\n\t\tblog(LOG_ERROR, \"avresample_open failed: error code %d\", errcode);\n\t\taudio_resampler_destroy(rs);\n\t\treturn NULL;\n\t}\n\n\treturn rs;\n}\n\nvoid audio_resampler_destroy(audio_resampler_t *rs)\n{\n\tif (rs) {\n\t\tif (rs->context)\n\t\t\tswr_free(&rs->context);\n\t\tif (rs->output_buffer[0])\n\t\t\tav_freep(&rs->output_buffer[0]);\n\n\t\tbfree(rs);\n\t}\n}\n\nbool audio_resampler_resample(audio_resampler_t *rs, uint8_t *output[], uint32_t *out_frames, uint64_t *ts_offset,\n\t\t\t      const uint8_t *const input[], uint32_t in_frames)\n{\n\tif (!rs)\n\t\treturn false;\n\n\tstruct SwrContext *context = rs->context;\n\tint ret;\n\n\tint64_t delay = swr_get_delay(context, rs->input_freq);\n\tint estimated = (int)av_rescale_rnd(delay + (int64_t)in_frames, (int64_t)rs->output_freq,\n\t\t\t\t\t    (int64_t)rs->input_freq, AV_ROUND_UP);\n\n\t*ts_offset = (uint64_t)swr_get_delay(context, 1000000000);\n\n\t/* resize the buffer if bigger */\n\tif (estimated > rs->output_size) {\n\t\tif (rs->output_buffer[0])\n\t\t\tav_freep(&rs->output_buffer[0]);\n\n\t\tav_samples_alloc(rs->output_buffer, NULL, rs->output_ch, estimated, rs->output_format, 0);\n\n\t\trs->output_size = estimated;\n\t}\n\n\tret = swr_convert(context, rs->output_buffer, rs->output_size, (const uint8_t **)input, in_frames);\n\n\tif (ret < 0) {\n\t\tblog(LOG_ERROR, \"swr_convert failed: %d\", ret);\n\t\treturn false;\n\t}\n\n\tfor (uint32_t i = 0; i < rs->output_planes; i++)\n\t\toutput[i] = rs->output_buffer[i];\n\n\t*out_frames = (uint32_t)ret;\n\treturn true;\n}\n"
  },
  {
    "path": "libobs/media-io/audio-resampler.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n#include \"audio-io.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct audio_resampler;\ntypedef struct audio_resampler audio_resampler_t;\n\nstruct resample_info {\n\tuint32_t samples_per_sec;\n\tenum audio_format format;\n\tenum speaker_layout speakers;\n};\n\nEXPORT audio_resampler_t *audio_resampler_create(const struct resample_info *dst, const struct resample_info *src);\nEXPORT void audio_resampler_destroy(audio_resampler_t *resampler);\n\nEXPORT bool audio_resampler_resample(audio_resampler_t *resampler, uint8_t *output[], uint32_t *out_frames,\n\t\t\t\t     uint64_t *ts_offset, const uint8_t *const input[], uint32_t in_frames);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/media-io/format-conversion.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"format-conversion.h\"\n\n#include \"../util/sse-intrin.h\"\n\n/* ...surprisingly, if I don't use a macro to force inlining, it causes the\n * CPU usage to boost by a tremendous amount in debug builds. */\n\n#define get_m128_32_0(val) (*((uint32_t *)&val))\n#define get_m128_32_1(val) (*(((uint32_t *)&val) + 1))\n\n#define pack_shift(lum_plane, lum_pos0, lum_pos1, line1, line2, mask, sh)                           \\\n\tdo {                                                                                        \\\n\t\t__m128i pack_val = _mm_packs_epi32(_mm_srli_si128(_mm_and_si128(line1, mask), sh),  \\\n\t\t\t\t\t\t   _mm_srli_si128(_mm_and_si128(line2, mask), sh)); \\\n\t\tpack_val = _mm_packus_epi16(pack_val, pack_val);                                    \\\n                                                                                                    \\\n\t\t*(uint32_t *)(lum_plane + lum_pos0) = get_m128_32_0(pack_val);                      \\\n\t\t*(uint32_t *)(lum_plane + lum_pos1) = get_m128_32_1(pack_val);                      \\\n\t} while (false)\n\n#define pack_val(lum_plane, lum_pos0, lum_pos1, line1, line2, mask)                                         \\\n\tdo {                                                                                                \\\n\t\t__m128i pack_val = _mm_packs_epi32(_mm_and_si128(line1, mask), _mm_and_si128(line2, mask)); \\\n\t\tpack_val = _mm_packus_epi16(pack_val, pack_val);                                            \\\n                                                                                                            \\\n\t\t*(uint32_t *)(lum_plane + lum_pos0) = get_m128_32_0(pack_val);                              \\\n\t\t*(uint32_t *)(lum_plane + lum_pos1) = get_m128_32_1(pack_val);                              \\\n\t} while (false)\n\n#define pack_ch_1plane(uv_plane, chroma_pos, line1, line2, uv_mask)                                            \\\n\tdo {                                                                                                   \\\n\t\t__m128i add_val = _mm_add_epi64(_mm_and_si128(line1, uv_mask), _mm_and_si128(line2, uv_mask)); \\\n\t\t__m128i avg_val = _mm_add_epi64(add_val, _mm_shuffle_epi32(add_val, _MM_SHUFFLE(2, 3, 0, 1))); \\\n\t\tavg_val = _mm_srai_epi16(avg_val, 2);                                                          \\\n\t\tavg_val = _mm_shuffle_epi32(avg_val, _MM_SHUFFLE(3, 1, 2, 0));                                 \\\n\t\tavg_val = _mm_packus_epi16(avg_val, avg_val);                                                  \\\n                                                                                                               \\\n\t\t*(uint32_t *)(uv_plane + chroma_pos) = get_m128_32_0(avg_val);                                 \\\n\t} while (false)\n\n#define pack_ch_2plane(u_plane, v_plane, chroma_pos, line1, line2, uv_mask)                                    \\\n\tdo {                                                                                                   \\\n\t\tuint32_t packed_vals;                                                                          \\\n                                                                                                               \\\n\t\t__m128i add_val = _mm_add_epi64(_mm_and_si128(line1, uv_mask), _mm_and_si128(line2, uv_mask)); \\\n\t\t__m128i avg_val = _mm_add_epi64(add_val, _mm_shuffle_epi32(add_val, _MM_SHUFFLE(2, 3, 0, 1))); \\\n\t\tavg_val = _mm_srai_epi16(avg_val, 2);                                                          \\\n\t\tavg_val = _mm_shuffle_epi32(avg_val, _MM_SHUFFLE(3, 1, 2, 0));                                 \\\n\t\tavg_val = _mm_shufflelo_epi16(avg_val, _MM_SHUFFLE(3, 1, 2, 0));                               \\\n\t\tavg_val = _mm_packus_epi16(avg_val, avg_val);                                                  \\\n                                                                                                               \\\n\t\tpacked_vals = get_m128_32_0(avg_val);                                                          \\\n                                                                                                               \\\n\t\t*(uint16_t *)(u_plane + chroma_pos) = (uint16_t)(packed_vals);                                 \\\n\t\t*(uint16_t *)(v_plane + chroma_pos) = (uint16_t)(packed_vals >> 16);                           \\\n\t} while (false)\n\nstatic FORCE_INLINE uint32_t min_uint32(uint32_t a, uint32_t b)\n{\n\treturn a < b ? a : b;\n}\n\nvoid compress_uyvx_to_i420(const uint8_t *input, uint32_t in_linesize, uint32_t start_y, uint32_t end_y,\n\t\t\t   uint8_t *output[], const uint32_t out_linesize[])\n{\n\tuint8_t *lum_plane = output[0];\n\tuint8_t *u_plane = output[1];\n\tuint8_t *v_plane = output[2];\n\tuint32_t width = min_uint32(in_linesize, out_linesize[0]);\n\tuint32_t y;\n\n\t__m128i lum_mask = _mm_set1_epi32(0x0000FF00);\n\t__m128i uv_mask = _mm_set1_epi16(0x00FF);\n\n\tfor (y = start_y; y < end_y; y += 2) {\n\t\tuint32_t y_pos = y * in_linesize;\n\t\tuint32_t chroma_y_pos = (y >> 1) * out_linesize[1];\n\t\tuint32_t lum_y_pos = y * out_linesize[0];\n\t\tuint32_t x;\n\n\t\tfor (x = 0; x < width; x += 4) {\n\t\t\tconst uint8_t *img = input + y_pos + x * 4;\n\t\t\tuint32_t lum_pos0 = lum_y_pos + x;\n\t\t\tuint32_t lum_pos1 = lum_pos0 + out_linesize[0];\n\n\t\t\t__m128i line1 = _mm_load_si128((const __m128i *)img);\n\t\t\t__m128i line2 = _mm_load_si128((const __m128i *)(img + in_linesize));\n\n\t\t\tpack_shift(lum_plane, lum_pos0, lum_pos1, line1, line2, lum_mask, 1);\n\t\t\tpack_ch_2plane(u_plane, v_plane, chroma_y_pos + (x >> 1), line1, line2, uv_mask);\n\t\t}\n\t}\n}\n\nvoid compress_uyvx_to_nv12(const uint8_t *input, uint32_t in_linesize, uint32_t start_y, uint32_t end_y,\n\t\t\t   uint8_t *output[], const uint32_t out_linesize[])\n{\n\tuint8_t *lum_plane = output[0];\n\tuint8_t *chroma_plane = output[1];\n\tuint32_t width = min_uint32(in_linesize, out_linesize[0]);\n\tuint32_t y;\n\n\t__m128i lum_mask = _mm_set1_epi32(0x0000FF00);\n\t__m128i uv_mask = _mm_set1_epi16(0x00FF);\n\n\tfor (y = start_y; y < end_y; y += 2) {\n\t\tuint32_t y_pos = y * in_linesize;\n\t\tuint32_t chroma_y_pos = (y >> 1) * out_linesize[1];\n\t\tuint32_t lum_y_pos = y * out_linesize[0];\n\t\tuint32_t x;\n\n\t\tfor (x = 0; x < width; x += 4) {\n\t\t\tconst uint8_t *img = input + y_pos + x * 4;\n\t\t\tuint32_t lum_pos0 = lum_y_pos + x;\n\t\t\tuint32_t lum_pos1 = lum_pos0 + out_linesize[0];\n\n\t\t\t__m128i line1 = _mm_load_si128((const __m128i *)img);\n\t\t\t__m128i line2 = _mm_load_si128((const __m128i *)(img + in_linesize));\n\n\t\t\tpack_shift(lum_plane, lum_pos0, lum_pos1, line1, line2, lum_mask, 1);\n\t\t\tpack_ch_1plane(chroma_plane, chroma_y_pos + x, line1, line2, uv_mask);\n\t\t}\n\t}\n}\n\nvoid convert_uyvx_to_i444(const uint8_t *input, uint32_t in_linesize, uint32_t start_y, uint32_t end_y,\n\t\t\t  uint8_t *output[], const uint32_t out_linesize[])\n{\n\tuint8_t *lum_plane = output[0];\n\tuint8_t *u_plane = output[1];\n\tuint8_t *v_plane = output[2];\n\tuint32_t width = min_uint32(in_linesize, out_linesize[0]);\n\tuint32_t y;\n\n\t__m128i lum_mask = _mm_set1_epi32(0x0000FF00);\n\t__m128i u_mask = _mm_set1_epi32(0x000000FF);\n\t__m128i v_mask = _mm_set1_epi32(0x00FF0000);\n\n\tfor (y = start_y; y < end_y; y += 2) {\n\t\tuint32_t y_pos = y * in_linesize;\n\t\tuint32_t lum_y_pos = y * out_linesize[0];\n\t\tuint32_t x;\n\n\t\tfor (x = 0; x < width; x += 4) {\n\t\t\tconst uint8_t *img = input + y_pos + x * 4;\n\t\t\tuint32_t lum_pos0 = lum_y_pos + x;\n\t\t\tuint32_t lum_pos1 = lum_pos0 + out_linesize[0];\n\n\t\t\t__m128i line1 = _mm_load_si128((const __m128i *)img);\n\t\t\t__m128i line2 = _mm_load_si128((const __m128i *)(img + in_linesize));\n\n\t\t\tpack_shift(lum_plane, lum_pos0, lum_pos1, line1, line2, lum_mask, 1);\n\t\t\tpack_val(u_plane, lum_pos0, lum_pos1, line1, line2, u_mask);\n\t\t\tpack_shift(v_plane, lum_pos0, lum_pos1, line1, line2, v_mask, 2);\n\t\t}\n\t}\n}\n\nvoid decompress_420(const uint8_t *const input[], const uint32_t in_linesize[], uint32_t start_y, uint32_t end_y,\n\t\t    uint8_t *output, uint32_t out_linesize)\n{\n\tuint32_t start_y_d2 = start_y / 2;\n\tuint32_t width_d2 = in_linesize[0] / 2;\n\tuint32_t height_d2 = end_y / 2;\n\tuint32_t y;\n\n\tfor (y = start_y_d2; y < height_d2; y++) {\n\t\tconst uint8_t *chroma0 = input[1] + y * in_linesize[1];\n\t\tconst uint8_t *chroma1 = input[2] + y * in_linesize[2];\n\t\tregister const uint8_t *lum0, *lum1;\n\t\tregister uint32_t *output0, *output1;\n\t\tuint32_t x;\n\n\t\tlum0 = input[0] + y * 2 * in_linesize[0];\n\t\tlum1 = lum0 + in_linesize[0];\n\t\toutput0 = (uint32_t *)(output + y * 2 * out_linesize);\n\t\toutput1 = (uint32_t *)((uint8_t *)output0 + out_linesize);\n\n\t\tfor (x = 0; x < width_d2; x++) {\n\t\t\tuint32_t out;\n\t\t\tout = (*(chroma0++) << 8) | *(chroma1++);\n\n\t\t\t*(output0++) = (*(lum0++) << 16) | out;\n\t\t\t*(output0++) = (*(lum0++) << 16) | out;\n\n\t\t\t*(output1++) = (*(lum1++) << 16) | out;\n\t\t\t*(output1++) = (*(lum1++) << 16) | out;\n\t\t}\n\t}\n}\n\nvoid decompress_nv12(const uint8_t *const input[], const uint32_t in_linesize[], uint32_t start_y, uint32_t end_y,\n\t\t     uint8_t *output, uint32_t out_linesize)\n{\n\tuint32_t start_y_d2 = start_y / 2;\n\tuint32_t width_d2 = min_uint32(in_linesize[0], out_linesize) / 2;\n\tuint32_t height_d2 = end_y / 2;\n\tuint32_t y;\n\n\tfor (y = start_y_d2; y < height_d2; y++) {\n\t\tconst uint16_t *chroma;\n\t\tregister const uint8_t *lum0, *lum1;\n\t\tregister uint32_t *output0, *output1;\n\t\tuint32_t x;\n\n\t\tchroma = (const uint16_t *)(input[1] + y * in_linesize[1]);\n\t\tlum0 = input[0] + y * 2 * in_linesize[0];\n\t\tlum1 = lum0 + in_linesize[0];\n\t\toutput0 = (uint32_t *)(output + y * 2 * out_linesize);\n\t\toutput1 = (uint32_t *)((uint8_t *)output0 + out_linesize);\n\n\t\tfor (x = 0; x < width_d2; x++) {\n\t\t\tuint32_t out = *(chroma++) << 8;\n\n\t\t\t*(output0++) = *(lum0++) | out;\n\t\t\t*(output0++) = *(lum0++) | out;\n\n\t\t\t*(output1++) = *(lum1++) | out;\n\t\t\t*(output1++) = *(lum1++) | out;\n\t\t}\n\t}\n}\n\nvoid decompress_422(const uint8_t *input, uint32_t in_linesize, uint32_t start_y, uint32_t end_y, uint8_t *output,\n\t\t    uint32_t out_linesize, bool leading_lum)\n{\n\tuint32_t width_d2 = min_uint32(in_linesize, out_linesize) / 2;\n\tuint32_t y;\n\n\tregister const uint32_t *input32;\n\tregister const uint32_t *input32_end;\n\tregister uint32_t *output32;\n\n\tif (leading_lum) {\n\t\tfor (y = start_y; y < end_y; y++) {\n\t\t\tinput32 = (const uint32_t *)(input + y * in_linesize);\n\t\t\tinput32_end = input32 + width_d2;\n\t\t\toutput32 = (uint32_t *)(output + y * out_linesize);\n\n\t\t\twhile (input32 < input32_end) {\n\t\t\t\tregister uint32_t dw = *input32;\n\n\t\t\t\toutput32[0] = dw;\n\t\t\t\tdw &= 0xFFFFFF00;\n\t\t\t\tdw |= (uint8_t)(dw >> 16);\n\t\t\t\toutput32[1] = dw;\n\n\t\t\t\toutput32 += 2;\n\t\t\t\tinput32++;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor (y = start_y; y < end_y; y++) {\n\t\t\tinput32 = (const uint32_t *)(input + y * in_linesize);\n\t\t\tinput32_end = input32 + width_d2;\n\t\t\toutput32 = (uint32_t *)(output + y * out_linesize);\n\n\t\t\twhile (input32 < input32_end) {\n\t\t\t\tregister uint32_t dw = *input32;\n\n\t\t\t\toutput32[0] = dw;\n\t\t\t\tdw &= 0xFFFF00FF;\n\t\t\t\tdw |= (dw >> 16) & 0xFF00;\n\t\t\t\toutput32[1] = dw;\n\n\t\t\t\toutput32 += 2;\n\t\t\t\tinput32++;\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "libobs/media-io/format-conversion.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Functions for converting to and from packed 444 YUV\n */\n\nEXPORT void compress_uyvx_to_i420(const uint8_t *input, uint32_t in_linesize, uint32_t start_y, uint32_t end_y,\n\t\t\t\t  uint8_t *output[], const uint32_t out_linesize[]);\n\nEXPORT void compress_uyvx_to_nv12(const uint8_t *input, uint32_t in_linesize, uint32_t start_y, uint32_t end_y,\n\t\t\t\t  uint8_t *output[], const uint32_t out_linesize[]);\n\nEXPORT void convert_uyvx_to_i444(const uint8_t *input, uint32_t in_linesize, uint32_t start_y, uint32_t end_y,\n\t\t\t\t uint8_t *output[], const uint32_t out_linesize[]);\n\nEXPORT void decompress_nv12(const uint8_t *const input[], const uint32_t in_linesize[], uint32_t start_y,\n\t\t\t    uint32_t end_y, uint8_t *output, uint32_t out_linesize);\n\nEXPORT void decompress_420(const uint8_t *const input[], const uint32_t in_linesize[], uint32_t start_y, uint32_t end_y,\n\t\t\t   uint8_t *output, uint32_t out_linesize);\n\nEXPORT void decompress_422(const uint8_t *input, uint32_t in_linesize, uint32_t start_y, uint32_t end_y,\n\t\t\t   uint8_t *output, uint32_t out_linesize, bool leading_lum);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/media-io/frame-rate.h",
    "content": "#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct media_frames_per_second {\n\tuint32_t numerator;\n\tuint32_t denominator;\n};\n\nstatic inline double media_frames_per_second_to_frame_interval(struct media_frames_per_second fps)\n{\n\treturn (double)fps.denominator / fps.numerator;\n}\n\nstatic inline double media_frames_per_second_to_fps(struct media_frames_per_second fps)\n{\n\treturn (double)fps.numerator / fps.denominator;\n}\n\nstatic inline bool media_frames_per_second_is_valid(struct media_frames_per_second fps)\n{\n\treturn fps.numerator && fps.denominator;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/media-io/media-io-defs.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#define MAX_AV_PLANES 8\n"
  },
  {
    "path": "libobs/media-io/media-remux.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"media-remux.h\"\n\n#include \"../util/base.h\"\n#include \"../util/bmem.h\"\n#include \"../util/platform.h\"\n\n#include <libavformat/avformat.h>\n#include <libavcodec/version.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n\nstruct media_remux_job {\n\tint64_t in_size;\n\tAVFormatContext *ifmt_ctx, *ofmt_ctx;\n};\n\nstatic inline void init_size(media_remux_job_t job, const char *in_filename)\n{\n#ifdef _MSC_VER\n\tstruct _stat64 st = {0};\n\t_stat64(in_filename, &st);\n#else\n\tstruct stat st = {0};\n\tstat(in_filename, &st);\n#endif\n\tjob->in_size = st.st_size;\n}\n\nstatic inline bool init_input(media_remux_job_t job, const char *in_filename)\n{\n\tint ret = avformat_open_input(&job->ifmt_ctx, in_filename, NULL, NULL);\n\tif (ret < 0) {\n\t\tblog(LOG_ERROR, \"media_remux: Could not open input file '%s'\", in_filename);\n\t\treturn false;\n\t}\n\n\tret = avformat_find_stream_info(job->ifmt_ctx, NULL);\n\tif (ret < 0) {\n\t\tblog(LOG_ERROR, \"media_remux: Failed to retrieve input stream\"\n\t\t\t\t\" information\");\n\t\treturn false;\n\t}\n\n#ifndef NDEBUG\n\tav_dump_format(job->ifmt_ctx, 0, in_filename, false);\n#endif\n\treturn true;\n}\n\nstatic inline bool init_output(media_remux_job_t job, const char *out_filename)\n{\n\tint ret;\n\n\tavformat_alloc_output_context2(&job->ofmt_ctx, NULL, NULL, out_filename);\n\tif (!job->ofmt_ctx) {\n\t\tblog(LOG_ERROR, \"media_remux: Could not create output context\");\n\t\treturn false;\n\t}\n\n\tfor (unsigned i = 0; i < job->ifmt_ctx->nb_streams; i++) {\n\t\tAVStream *in_stream = job->ifmt_ctx->streams[i];\n\t\tAVStream *out_stream = avformat_new_stream(job->ofmt_ctx, NULL);\n\t\tif (!out_stream) {\n\t\t\tblog(LOG_ERROR, \"media_remux: Failed to allocate output\"\n\t\t\t\t\t\" stream\");\n\t\t\treturn false;\n\t\t}\n\n\t\tret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);\n\n\t\tif (ret < 0) {\n\t\t\tblog(LOG_ERROR, \"media_remux: Failed to copy parameters\");\n\t\t\treturn false;\n\t\t}\n\n\t\tav_dict_copy(&out_stream->metadata, in_stream->metadata, 0);\n\n\t\tif (in_stream->codecpar->codec_id == AV_CODEC_ID_HEVC && job->ofmt_ctx->oformat->codec_tag &&\n\t\t    av_codec_get_id(job->ofmt_ctx->oformat->codec_tag, MKTAG('h', 'v', 'c', '1')) ==\n\t\t\t    out_stream->codecpar->codec_id) {\n\t\t\t// Tag HEVC files with industry standard HVC1 tag for wider device compatibility\n\t\t\t// when HVC1 tag is supported by out stream codec\n\t\t\tout_stream->codecpar->codec_tag = MKTAG('h', 'v', 'c', '1');\n\t\t} else {\n\t\t\t// Otherwise tag 0 to let FFmpeg automatically select the appropriate tag\n\t\t\tout_stream->codecpar->codec_tag = 0;\n\t\t}\n\n\t\tif (in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {\n\n\t\t\tav_channel_layout_default(&out_stream->codecpar->ch_layout,\n\t\t\t\t\t\t  in_stream->codecpar->ch_layout.nb_channels);\n\t\t\t/* The avutil default channel layout for 5 channels is\n\t\t\t * 5.0, which OBS does not support. Manually set 5\n\t\t\t * channels to 4.1. */\n\t\t\tif (in_stream->codecpar->ch_layout.nb_channels == 5)\n\t\t\t\tout_stream->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_4POINT1;\n\t\t}\n\t}\n\n#ifndef NDEBUG\n\tav_dump_format(job->ofmt_ctx, 0, out_filename, true);\n#endif\n\n\tif (!(job->ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {\n\t\tret = avio_open(&job->ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);\n\t\tif (ret < 0) {\n\t\t\tblog(LOG_ERROR,\n\t\t\t     \"media_remux: Failed to open output\"\n\t\t\t     \" file '%s'\",\n\t\t\t     out_filename);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nbool media_remux_job_create(media_remux_job_t *job, const char *in_filename, const char *out_filename)\n{\n\tif (!job)\n\t\treturn false;\n\n\t*job = NULL;\n\tif (!os_file_exists(in_filename))\n\t\treturn false;\n\n\tif (strcmp(in_filename, out_filename) == 0)\n\t\treturn false;\n\n\t*job = (media_remux_job_t)bzalloc(sizeof(struct media_remux_job));\n\tif (!*job)\n\t\treturn false;\n\n\tinit_size(*job, in_filename);\n\n\tif (!init_input(*job, in_filename))\n\t\tgoto fail;\n\n\tif (!init_output(*job, out_filename))\n\t\tgoto fail;\n\n\treturn true;\n\nfail:\n\tmedia_remux_job_destroy(*job);\n\treturn false;\n}\n\nstatic inline void process_packet(AVPacket *pkt, AVStream *in_stream, AVStream *out_stream)\n{\n\tpkt->pts = av_rescale_q_rnd(pkt->pts, in_stream->time_base, out_stream->time_base,\n\t\t\t\t    AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);\n\tpkt->dts = av_rescale_q_rnd(pkt->dts, in_stream->time_base, out_stream->time_base,\n\t\t\t\t    AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);\n\tpkt->duration = (int)av_rescale_q(pkt->duration, in_stream->time_base, out_stream->time_base);\n\tpkt->pos = -1;\n}\n\nstatic inline int process_packets(media_remux_job_t job, media_remux_progress_callback callback, void *data)\n{\n\tAVPacket pkt;\n\n\tint ret, throttle = 0;\n\tfor (;;) {\n\t\tret = av_read_frame(job->ifmt_ctx, &pkt);\n\t\tif (ret < 0) {\n\t\t\tif (ret != AVERROR_EOF)\n\t\t\t\tblog(LOG_ERROR,\n\t\t\t\t     \"media_remux: Error reading\"\n\t\t\t\t     \" packet: %s\",\n\t\t\t\t     av_err2str(ret));\n\t\t\tbreak;\n\t\t}\n\n\t\tif (callback != NULL && throttle++ > 10) {\n\t\t\tfloat progress = pkt.pos / (float)job->in_size * 100.f;\n\t\t\tif (!callback(data, progress))\n\t\t\t\tbreak;\n\t\t\tthrottle = 0;\n\t\t}\n\n\t\tprocess_packet(&pkt, job->ifmt_ctx->streams[pkt.stream_index],\n\t\t\t       job->ofmt_ctx->streams[pkt.stream_index]);\n\n\t\tret = av_interleaved_write_frame(job->ofmt_ctx, &pkt);\n\t\tav_packet_unref(&pkt);\n\n\t\tif (ret < 0) {\n\t\t\tblog(LOG_ERROR, \"media_remux: Error muxing packet: %s\", av_err2str(ret));\n\n\t\t\t/* Treat \"Invalid data found when processing input\" and\n\t\t\t * \"Invalid argument\" as non-fatal */\n\t\t\tif (ret == AVERROR_INVALIDDATA || ret == -EINVAL)\n\t\t\t\tcontinue;\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn ret;\n}\n\nbool media_remux_job_process(media_remux_job_t job, media_remux_progress_callback callback, void *data)\n{\n\tint ret;\n\tbool success = false;\n\n\tif (!job)\n\t\treturn success;\n\n\tret = avformat_write_header(job->ofmt_ctx, NULL);\n\tif (ret < 0) {\n\t\tblog(LOG_ERROR, \"media_remux: Error opening output file: %s\", av_err2str(ret));\n\t\treturn success;\n\t}\n\n\tif (callback != NULL)\n\t\tcallback(data, 0.f);\n\n\tret = process_packets(job, callback, data);\n\tsuccess = ret >= 0 || ret == AVERROR_EOF;\n\n\tret = av_write_trailer(job->ofmt_ctx);\n\tif (ret < 0) {\n\t\tblog(LOG_ERROR, \"media_remux: av_write_trailer: %s\", av_err2str(ret));\n\t\tsuccess = false;\n\t}\n\n\tif (callback != NULL)\n\t\tcallback(data, 100.f);\n\n\treturn success;\n}\n\nvoid media_remux_job_destroy(media_remux_job_t job)\n{\n\tif (!job)\n\t\treturn;\n\n\tavformat_close_input(&job->ifmt_ctx);\n\n\tif (job->ofmt_ctx && !(job->ofmt_ctx->oformat->flags & AVFMT_NOFILE))\n\t\tavio_close(job->ofmt_ctx->pb);\n\n\tavformat_free_context(job->ofmt_ctx);\n\n\tbfree(job);\n}\n"
  },
  {
    "path": "libobs/media-io/media-remux.h",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"../util/c99defs.h\"\n\n#pragma once\n\nstruct media_remux_job;\ntypedef struct media_remux_job *media_remux_job_t;\n\ntypedef bool(media_remux_progress_callback)(void *data, float percent);\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nEXPORT bool media_remux_job_create(media_remux_job_t *job, const char *in_filename, const char *out_filename);\nEXPORT bool media_remux_job_process(media_remux_job_t job, media_remux_progress_callback callback, void *data);\nEXPORT void media_remux_job_destroy(media_remux_job_t job);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/media-io/video-fourcc.c",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"../util/c99defs.h\"\n#include \"video-io.h\"\n\n#define MAKE_FOURCC(a, b, c, d) ((uint32_t)(((d) << 24) | ((c) << 16) | ((b) << 8) | (a)))\n\nenum video_format video_format_from_fourcc(uint32_t fourcc)\n{\n\tswitch (fourcc) {\n\tcase MAKE_FOURCC('U', 'Y', 'V', 'Y'):\n\tcase MAKE_FOURCC('H', 'D', 'Y', 'C'):\n\tcase MAKE_FOURCC('U', 'Y', 'N', 'V'):\n\tcase MAKE_FOURCC('U', 'Y', 'N', 'Y'):\n\tcase MAKE_FOURCC('u', 'y', 'v', '1'):\n\tcase MAKE_FOURCC('2', 'v', 'u', 'y'):\n\tcase MAKE_FOURCC('2', 'V', 'u', 'y'):\n\t\treturn VIDEO_FORMAT_UYVY;\n\n\tcase MAKE_FOURCC('Y', 'U', 'Y', '2'):\n\tcase MAKE_FOURCC('Y', '4', '2', '2'):\n\tcase MAKE_FOURCC('V', '4', '2', '2'):\n\tcase MAKE_FOURCC('V', 'Y', 'U', 'Y'):\n\tcase MAKE_FOURCC('Y', 'U', 'N', 'V'):\n\tcase MAKE_FOURCC('y', 'u', 'v', '2'):\n\tcase MAKE_FOURCC('y', 'u', 'v', 's'):\n\t\treturn VIDEO_FORMAT_YUY2;\n\n\tcase MAKE_FOURCC('Y', 'V', 'Y', 'U'):\n\t\treturn VIDEO_FORMAT_YVYU;\n\n\tcase MAKE_FOURCC('Y', '8', '0', '0'):\n\t\treturn VIDEO_FORMAT_Y800;\n\t}\n\treturn VIDEO_FORMAT_NONE;\n}\n"
  },
  {
    "path": "libobs/media-io/video-frame.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n#include <assert.h>\n#include \"video-frame.h\"\n\n#define HALF(size) ((size + 1) / 2)\n#define ALIGN(size, alignment) *size = (*size + alignment - 1) & (~(alignment - 1));\n\nstatic inline void align_size(size_t *size, size_t alignment)\n{\n\tALIGN(size, alignment);\n}\n\nstatic inline void align_uint32(uint32_t *size, size_t alignment)\n{\n\tALIGN(size, (uint32_t)alignment);\n}\n\n/* assumes already-zeroed array */\nvoid video_frame_get_linesizes(uint32_t linesize[MAX_AV_PLANES], enum video_format format, uint32_t width)\n{\n\tswitch (format) {\n\tdefault:\n\tcase VIDEO_FORMAT_NONE:\n\t\tbreak;\n\tcase VIDEO_FORMAT_BGR3: /* one plane: triple width */\n\t\tlinesize[0] = width * 3;\n\t\tbreak;\n\tcase VIDEO_FORMAT_RGBA: /* one plane: quadruple width */\n\tcase VIDEO_FORMAT_BGRA:\n\tcase VIDEO_FORMAT_BGRX:\n\tcase VIDEO_FORMAT_AYUV:\n\tcase VIDEO_FORMAT_R10L:\n\t\tlinesize[0] = width * 4;\n\t\tbreak;\n\tcase VIDEO_FORMAT_P416: /* two planes: double width, quadruple width */\n\t\tlinesize[0] = width * 2;\n\t\tlinesize[1] = width * 4;\n\t\tbreak;\n\tcase VIDEO_FORMAT_I420: /* three planes: full width, half width, half width */\n\tcase VIDEO_FORMAT_I422:\n\t\tlinesize[0] = width;\n\t\tlinesize[1] = HALF(width);\n\t\tlinesize[2] = HALF(width);\n\t\tbreak;\n\tcase VIDEO_FORMAT_I210: /* three planes: double width, full width, full width */\n\tcase VIDEO_FORMAT_I010:\n\t\tlinesize[0] = width * 2;\n\t\tlinesize[1] = width;\n\t\tlinesize[2] = width;\n\t\tbreak;\n\tcase VIDEO_FORMAT_I40A: /* four planes: full width, half width, half width, full width */\n\tcase VIDEO_FORMAT_I42A:\n\t\tlinesize[0] = width;\n\t\tlinesize[1] = HALF(width);\n\t\tlinesize[2] = HALF(width);\n\t\tlinesize[3] = width;\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_YVYU: /* one plane: double width */\n\tcase VIDEO_FORMAT_YUY2:\n\tcase VIDEO_FORMAT_UYVY:\n\t\tlinesize[0] = width * 2;\n\t\tbreak;\n\tcase VIDEO_FORMAT_P010: /* two planes: all double width */\n\tcase VIDEO_FORMAT_P216:\n\t\tlinesize[0] = width * 2;\n\t\tlinesize[1] = width * 2;\n\t\tbreak;\n\tcase VIDEO_FORMAT_I412: /* three planes: all double width */\n\t\tlinesize[0] = width * 2;\n\t\tlinesize[1] = width * 2;\n\t\tlinesize[2] = width * 2;\n\t\tbreak;\n\tcase VIDEO_FORMAT_YA2L: /* four planes: all double width */\n\t\tlinesize[0] = width * 2;\n\t\tlinesize[1] = width * 2;\n\t\tlinesize[2] = width * 2;\n\t\tlinesize[3] = width * 2;\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_Y800: /* one plane: full width */\n\t\tlinesize[0] = width;\n\t\tbreak;\n\tcase VIDEO_FORMAT_NV12: /* two planes: all full width */\n\t\tlinesize[0] = width;\n\t\tlinesize[1] = width;\n\t\tbreak;\n\tcase VIDEO_FORMAT_I444: /* three planes: all full width */\n\t\tlinesize[0] = width;\n\t\tlinesize[1] = width;\n\t\tlinesize[2] = width;\n\t\tbreak;\n\tcase VIDEO_FORMAT_YUVA: /* four planes: all full width */\n\t\tlinesize[0] = width;\n\t\tlinesize[1] = width;\n\t\tlinesize[2] = width;\n\t\tlinesize[3] = width;\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_V210: { /* one plane: bruh (Little Endian Compressed) */\n\t\talign_uint32(&width, 48);\n\t\tlinesize[0] = ((width + 5) / 6) * 16;\n\t\tbreak;\n\t}\n\t}\n}\n\nvoid video_frame_get_plane_heights(uint32_t heights[MAX_AV_PLANES], enum video_format format, uint32_t height)\n{\n\tswitch (format) {\n\tdefault:\n\tcase VIDEO_FORMAT_NONE:\n\t\treturn;\n\n\tcase VIDEO_FORMAT_I420: /* three planes: full height, half height, half height */\n\tcase VIDEO_FORMAT_I010:\n\t\theights[0] = height;\n\t\theights[1] = HALF(height);\n\t\theights[2] = HALF(height);\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_NV12: /* two planes: full height, half height */\n\tcase VIDEO_FORMAT_P010:\n\t\theights[0] = height;\n\t\theights[1] = HALF(height);\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_Y800: /* one plane: full height */\n\tcase VIDEO_FORMAT_YVYU:\n\tcase VIDEO_FORMAT_YUY2:\n\tcase VIDEO_FORMAT_UYVY:\n\tcase VIDEO_FORMAT_RGBA:\n\tcase VIDEO_FORMAT_BGRA:\n\tcase VIDEO_FORMAT_BGRX:\n\tcase VIDEO_FORMAT_BGR3:\n\tcase VIDEO_FORMAT_AYUV:\n\tcase VIDEO_FORMAT_V210:\n\tcase VIDEO_FORMAT_R10L:\n\t\theights[0] = height;\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_I444: /* three planes: all full height */\n\tcase VIDEO_FORMAT_I422:\n\tcase VIDEO_FORMAT_I210:\n\tcase VIDEO_FORMAT_I412:\n\t\theights[0] = height;\n\t\theights[1] = height;\n\t\theights[2] = height;\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_I40A: /* four planes: full height, half height, half height, full height */\n\t\theights[0] = height;\n\t\theights[1] = HALF(height);\n\t\theights[2] = HALF(height);\n\t\theights[3] = height;\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_I42A: /* four planes: all full height */\n\tcase VIDEO_FORMAT_YUVA:\n\tcase VIDEO_FORMAT_YA2L:\n\t\theights[0] = height;\n\t\theights[1] = height;\n\t\theights[2] = height;\n\t\theights[3] = height;\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_P216: /* two planes: all full height */\n\tcase VIDEO_FORMAT_P416:\n\t\theights[0] = height;\n\t\theights[1] = height;\n\t\tbreak;\n\t}\n}\n\nvoid video_frame_init(struct video_frame *frame, enum video_format format, uint32_t width, uint32_t height)\n{\n\tsize_t size = 0;\n\tuint32_t linesizes[MAX_AV_PLANES];\n\tuint32_t heights[MAX_AV_PLANES];\n\tsize_t offsets[MAX_AV_PLANES];\n\tint alignment = base_get_alignment();\n\n\tif (!frame)\n\t\treturn;\n\n\tmemset(frame, 0, sizeof(struct video_frame));\n\tmemset(linesizes, 0, sizeof(linesizes));\n\tmemset(heights, 0, sizeof(heights));\n\tmemset(offsets, 0, sizeof(offsets));\n\n\t/* determine linesizes for each plane */\n\tvideo_frame_get_linesizes(linesizes, format, width);\n\n\t/* determine line count for each plane */\n\tvideo_frame_get_plane_heights(heights, format, height);\n\n\t/* calculate total buffer required */\n\tfor (uint32_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\tif (!linesizes[i] || !heights[i])\n\t\t\tcontinue;\n\t\tsize_t plane_size = (size_t)linesizes[i] * (size_t)heights[i];\n\t\talign_size(&plane_size, alignment);\n\t\tsize += plane_size;\n\t\toffsets[i] = size;\n\t}\n\n\t/* allocate memory */\n\tframe->data[0] = bmalloc(size);\n\tframe->linesize[0] = linesizes[0];\n\n\t/* apply plane data pointers according to offsets */\n\tfor (uint32_t i = 1; i < MAX_AV_PLANES; i++) {\n\t\tif (!linesizes[i] || !heights[i])\n\t\t\tcontinue;\n\t\tframe->data[i] = frame->data[0] + offsets[i - 1];\n\t\tframe->linesize[i] = linesizes[i];\n\t}\n}\n\nvoid video_frame_copy(struct video_frame *dst, const struct video_frame *src, enum video_format format, uint32_t cy)\n{\n\tuint32_t heights[MAX_AV_PLANES];\n\n\tmemset(heights, 0, sizeof(heights));\n\n\t/* determine line count for each plane */\n\tvideo_frame_get_plane_heights(heights, format, cy);\n\n\t/* copy each plane */\n\tfor (uint32_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\tif (!heights[i])\n\t\t\tcontinue;\n\n\t\tif (src->linesize[i] == dst->linesize[i]) {\n\t\t\tmemcpy(dst->data[i], src->data[i], src->linesize[i] * heights[i]);\n\t\t} else { /* linesizes which do not match must be copied line-by-line */\n\t\t\tsize_t src_linesize = src->linesize[i];\n\t\t\tsize_t dst_linesize = dst->linesize[i];\n\t\t\t/* determine how much we can write (frames with different line sizes require more )*/\n\t\t\tsize_t linesize = src_linesize < dst_linesize ? src_linesize : dst_linesize;\n\t\t\tfor (uint32_t y = 0; y < heights[i]; y++) {\n\t\t\t\tuint8_t *src_pos = src->data[i] + (src_linesize * y);\n\t\t\t\tuint8_t *dst_pos = dst->data[i] + (dst_linesize * y);\n\t\t\t\tmemcpy(dst_pos, src_pos, linesize);\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "libobs/media-io/video-frame.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/bmem.h\"\n#include \"video-io.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct video_frame {\n\tuint8_t *data[MAX_AV_PLANES];\n\tuint32_t linesize[MAX_AV_PLANES];\n};\n\nEXPORT void video_frame_init(struct video_frame *frame, enum video_format format, uint32_t width, uint32_t height);\n\nstatic inline void video_frame_free(struct video_frame *frame)\n{\n\tif (frame) {\n\t\tbfree(frame->data[0]);\n\t\tmemset(frame, 0, sizeof(struct video_frame));\n\t}\n}\n\nstatic inline struct video_frame *video_frame_create(enum video_format format, uint32_t width, uint32_t height)\n{\n\tstruct video_frame *frame;\n\n\tframe = (struct video_frame *)bzalloc(sizeof(struct video_frame));\n\tvideo_frame_init(frame, format, width, height);\n\treturn frame;\n}\n\nstatic inline void video_frame_destroy(struct video_frame *frame)\n{\n\tif (frame) {\n\t\tbfree(frame->data[0]);\n\t\tbfree(frame);\n\t}\n}\n\nEXPORT void video_frame_copy(struct video_frame *dst, const struct video_frame *src, enum video_format format,\n\t\t\t     uint32_t height);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/media-io/video-io.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <assert.h>\n#include <inttypes.h>\n#include \"../util/bmem.h\"\n#include \"../util/platform.h\"\n#include \"../util/profiler.h\"\n#include \"../util/threading.h\"\n#include \"../util/darray.h\"\n#include \"../util/util_uint64.h\"\n\n#include \"format-conversion.h\"\n#include \"video-io.h\"\n#include \"video-frame.h\"\n#include \"video-scaler.h\"\n\nextern profiler_name_store_t *obs_get_profiler_name_store(void);\n\n#define MAX_CONVERT_BUFFERS 3\n#define MAX_CACHE_SIZE 16\n\nstruct cached_frame_info {\n\tstruct video_data frame;\n\tint skipped;\n\tint count;\n};\n\nstruct video_input {\n\tstruct video_scale_info conversion;\n\tvideo_scaler_t *scaler;\n\tstruct video_frame frame[MAX_CONVERT_BUFFERS];\n\tint cur_frame;\n\n\t// allow outputting at fractions of main composition FPS,\n\t// e.g. 60 FPS with frame_rate_divisor = 1 turns into 30 FPS\n\t//\n\t// a separate counter is used in favor of using remainder calculations\n\t// to allow \"inputs\" started at the same time to start on the same frame\n\t// whereas with remainder calculation the frame alignment would depend on\n\t// the total frame count at the time the encoder was started\n\tuint32_t frame_rate_divisor;\n\tuint32_t frame_rate_divisor_counter;\n\n\tvoid (*callback)(void *param, struct video_data *frame);\n\tvoid *param;\n};\n\nstatic inline void video_input_free(struct video_input *input)\n{\n\tfor (size_t i = 0; i < MAX_CONVERT_BUFFERS; i++)\n\t\tvideo_frame_free(&input->frame[i]);\n\tvideo_scaler_destroy(input->scaler);\n}\n\nstruct video_output {\n\tstruct video_output_info info;\n\n\tpthread_t thread;\n\tpthread_mutex_t data_mutex;\n\tbool stop;\n\n\tos_sem_t *update_semaphore;\n\tuint64_t frame_time;\n\tvolatile long skipped_frames;\n\tvolatile long total_frames;\n\n\tpthread_mutex_t input_mutex;\n\tDARRAY(struct video_input) inputs;\n\n\tsize_t available_frames;\n\tsize_t first_added;\n\tsize_t last_added;\n\tstruct cached_frame_info cache[MAX_CACHE_SIZE];\n\n\tstruct video_output *parent;\n\n\tvolatile bool raw_active;\n\tvolatile long gpu_refs;\n};\n\n/* ------------------------------------------------------------------------- */\n\nstatic inline bool scale_video_output(struct video_input *input, struct video_data *data)\n{\n\tbool success = true;\n\n\tif (input->scaler) {\n\t\tstruct video_frame *frame;\n\n\t\tif (++input->cur_frame == MAX_CONVERT_BUFFERS)\n\t\t\tinput->cur_frame = 0;\n\n\t\tframe = &input->frame[input->cur_frame];\n\n\t\tsuccess = video_scaler_scale(input->scaler, frame->data, frame->linesize,\n\t\t\t\t\t     (const uint8_t *const *)data->data, data->linesize);\n\n\t\tif (success) {\n\t\t\tfor (size_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\t\t\tdata->data[i] = frame->data[i];\n\t\t\t\tdata->linesize[i] = frame->linesize[i];\n\t\t\t}\n\t\t} else {\n\t\t\tblog(LOG_WARNING, \"video-io: Could not scale frame!\");\n\t\t}\n\t}\n\n\treturn success;\n}\n\nstatic inline bool video_output_cur_frame(struct video_output *video)\n{\n\tstruct cached_frame_info *frame_info;\n\tbool complete;\n\tbool skipped;\n\n\t/* -------------------------------- */\n\n\tpthread_mutex_lock(&video->data_mutex);\n\n\tframe_info = &video->cache[video->first_added];\n\n\tpthread_mutex_unlock(&video->data_mutex);\n\n\t/* -------------------------------- */\n\n\tpthread_mutex_lock(&video->input_mutex);\n\n\tfor (size_t i = 0; i < video->inputs.num; i++) {\n\t\tstruct video_input *input = video->inputs.array + i;\n\t\tstruct video_data frame = frame_info->frame;\n\n\t\t// an explicit counter is used instead of remainder calculation\n\t\t// to allow multiple encoders started at the same time to start on\n\t\t// the same frame\n\t\tuint32_t skip = input->frame_rate_divisor_counter++;\n\t\tif (input->frame_rate_divisor_counter == input->frame_rate_divisor)\n\t\t\tinput->frame_rate_divisor_counter = 0;\n\n\t\tif (skip)\n\t\t\tcontinue;\n\n\t\tif (scale_video_output(input, &frame))\n\t\t\tinput->callback(input->param, &frame);\n\t}\n\n\tpthread_mutex_unlock(&video->input_mutex);\n\n\t/* -------------------------------- */\n\n\tpthread_mutex_lock(&video->data_mutex);\n\n\tframe_info->frame.timestamp += video->frame_time;\n\tcomplete = --frame_info->count == 0;\n\tskipped = frame_info->skipped > 0;\n\n\tif (complete) {\n\t\tif (++video->first_added == video->info.cache_size)\n\t\t\tvideo->first_added = 0;\n\n\t\tif (++video->available_frames == video->info.cache_size)\n\t\t\tvideo->last_added = video->first_added;\n\t} else if (skipped) {\n\t\t--frame_info->skipped;\n\t\tos_atomic_inc_long(&video->skipped_frames);\n\t}\n\n\tpthread_mutex_unlock(&video->data_mutex);\n\n\t/* -------------------------------- */\n\n\treturn complete;\n}\n\nstatic void *video_thread(void *param)\n{\n\tstruct video_output *video = param;\n\n\tos_set_thread_name(\"video-io: video thread\");\n\n\tconst char *video_thread_name =\n\t\tprofile_store_name(obs_get_profiler_name_store(), \"video_thread(%s)\", video->info.name);\n\n\twhile (os_sem_wait(video->update_semaphore) == 0) {\n\t\tif (video->stop)\n\t\t\tbreak;\n\n\t\tprofile_start(video_thread_name);\n\t\twhile (!video->stop && !video_output_cur_frame(video)) {\n\t\t\tos_atomic_inc_long(&video->total_frames);\n\t\t}\n\n\t\tos_atomic_inc_long(&video->total_frames);\n\t\tprofile_end(video_thread_name);\n\n\t\tprofile_reenable_thread();\n\t}\n\n\treturn NULL;\n}\n\n/* ------------------------------------------------------------------------- */\n\nstatic inline bool valid_video_params(const struct video_output_info *info)\n{\n\treturn info->height != 0 && info->width != 0 && info->fps_den != 0 && info->fps_num != 0;\n}\n\nstatic inline void init_cache(struct video_output *video)\n{\n\tif (video->info.cache_size > MAX_CACHE_SIZE)\n\t\tvideo->info.cache_size = MAX_CACHE_SIZE;\n\n\tfor (size_t i = 0; i < video->info.cache_size; i++) {\n\t\tstruct video_frame *frame;\n\t\tframe = (struct video_frame *)&video->cache[i];\n\n\t\tvideo_frame_init(frame, video->info.format, video->info.width, video->info.height);\n\t}\n\n\tvideo->available_frames = video->info.cache_size;\n}\n\nint video_output_open(video_t **video, struct video_output_info *info)\n{\n\tstruct video_output *out;\n\n\tif (!valid_video_params(info))\n\t\treturn VIDEO_OUTPUT_INVALIDPARAM;\n\n\tout = bzalloc(sizeof(struct video_output));\n\tif (!out)\n\t\tgoto fail0;\n\n\tmemcpy(&out->info, info, sizeof(struct video_output_info));\n\tout->frame_time = util_mul_div64(1000000000ULL, info->fps_den, info->fps_num);\n\n\tif (pthread_mutex_init_recursive(&out->data_mutex) != 0)\n\t\tgoto fail0;\n\tif (pthread_mutex_init_recursive(&out->input_mutex) != 0)\n\t\tgoto fail1;\n\tif (os_sem_init(&out->update_semaphore, 0) != 0)\n\t\tgoto fail2;\n\tif (pthread_create(&out->thread, NULL, video_thread, out) != 0)\n\t\tgoto fail3;\n\n\tinit_cache(out);\n\n\t*video = out;\n\treturn VIDEO_OUTPUT_SUCCESS;\n\nfail3:\n\tos_sem_destroy(out->update_semaphore);\nfail2:\n\tpthread_mutex_destroy(&out->input_mutex);\nfail1:\n\tpthread_mutex_destroy(&out->data_mutex);\nfail0:\n\tbfree(out);\n\treturn VIDEO_OUTPUT_FAIL;\n}\n\nvoid video_output_close(video_t *video)\n{\n\tif (!video)\n\t\treturn;\n\n\tvideo_output_stop(video);\n\n\tpthread_mutex_lock(&video->input_mutex);\n\n\tfor (size_t i = 0; i < video->inputs.num; i++)\n\t\tvideo_input_free(&video->inputs.array[i]);\n\tda_free(video->inputs);\n\n\tfor (size_t i = 0; i < video->info.cache_size; i++)\n\t\tvideo_frame_free((struct video_frame *)&video->cache[i]);\n\n\tpthread_mutex_unlock(&video->input_mutex);\n\tos_sem_destroy(video->update_semaphore);\n\tpthread_mutex_destroy(&video->data_mutex);\n\tpthread_mutex_destroy(&video->input_mutex);\n\n\tbfree(video);\n}\n\nstatic size_t video_get_input_idx(const video_t *video, void (*callback)(void *param, struct video_data *frame),\n\t\t\t\t  void *param)\n{\n\tfor (size_t i = 0; i < video->inputs.num; i++) {\n\t\tstruct video_input *input = video->inputs.array + i;\n\t\tif (input->callback == callback && input->param == param)\n\t\t\treturn i;\n\t}\n\n\treturn DARRAY_INVALID;\n}\n\nstatic bool match_range(enum video_range_type a, enum video_range_type b)\n{\n\treturn (a == VIDEO_RANGE_FULL) == (b == VIDEO_RANGE_FULL);\n}\n\nstatic enum video_colorspace collapse_space(enum video_colorspace cs)\n{\n\tswitch (cs) {\n\tcase VIDEO_CS_SRGB:\n\t\tcs = VIDEO_CS_709;\n\t\tbreak;\n\tcase VIDEO_CS_2100_HLG:\n\t\tcs = VIDEO_CS_2100_PQ;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn cs;\n}\n\nstatic bool match_space(enum video_colorspace a, enum video_colorspace b)\n{\n\treturn (a == VIDEO_CS_DEFAULT) || (b == VIDEO_CS_DEFAULT) || (collapse_space(a) == collapse_space(b));\n}\n\nstatic inline bool video_input_init(struct video_input *input, struct video_output *video)\n{\n\tif (input->conversion.width != video->info.width || input->conversion.height != video->info.height ||\n\t    input->conversion.format != video->info.format ||\n\t    !match_range(input->conversion.range, video->info.range) ||\n\t    !match_space(input->conversion.colorspace, video->info.colorspace)) {\n\t\tstruct video_scale_info from = {.format = video->info.format,\n\t\t\t\t\t\t.width = video->info.width,\n\t\t\t\t\t\t.height = video->info.height,\n\t\t\t\t\t\t.range = video->info.range,\n\t\t\t\t\t\t.colorspace = video->info.colorspace};\n\n\t\tint ret = video_scaler_create(&input->scaler, &input->conversion, &from, VIDEO_SCALE_FAST_BILINEAR);\n\t\tif (ret != VIDEO_SCALER_SUCCESS) {\n\t\t\tif (ret == VIDEO_SCALER_BAD_CONVERSION)\n\t\t\t\tblog(LOG_ERROR, \"video_input_init: Bad \"\n\t\t\t\t\t\t\"scale conversion type\");\n\t\t\telse\n\t\t\t\tblog(LOG_ERROR, \"video_input_init: Failed to \"\n\t\t\t\t\t\t\"create scaler\");\n\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (size_t i = 0; i < MAX_CONVERT_BUFFERS; i++)\n\t\t\tvideo_frame_init(&input->frame[i], input->conversion.format, input->conversion.width,\n\t\t\t\t\t input->conversion.height);\n\t}\n\n\treturn true;\n}\n\nstatic inline void reset_frames(video_t *video)\n{\n\tos_atomic_set_long(&video->skipped_frames, 0);\n\tos_atomic_set_long(&video->total_frames, 0);\n}\n\nstatic const video_t *get_const_root(const video_t *video)\n{\n\twhile (video->parent)\n\t\tvideo = video->parent;\n\treturn video;\n}\n\nstatic video_t *get_root(video_t *video)\n{\n\twhile (video->parent)\n\t\tvideo = video->parent;\n\treturn video;\n}\n\nbool video_output_connect(video_t *video, const struct video_scale_info *conversion,\n\t\t\t  void (*callback)(void *param, struct video_data *frame), void *param)\n{\n\treturn video_output_connect2(video, conversion, 1, callback, param);\n}\n\nbool video_output_connect2(video_t *video, const struct video_scale_info *conversion, uint32_t frame_rate_divisor,\n\t\t\t   void (*callback)(void *param, struct video_data *frame), void *param)\n{\n\tbool success = false;\n\n\tvideo = get_root(video);\n\n\tif (!video || !callback || frame_rate_divisor == 0)\n\t\treturn false;\n\n\tpthread_mutex_lock(&video->input_mutex);\n\n\tif (video_get_input_idx(video, callback, param) == DARRAY_INVALID) {\n\t\tstruct video_input input;\n\t\tmemset(&input, 0, sizeof(input));\n\n\t\tinput.callback = callback;\n\t\tinput.param = param;\n\n\t\tinput.frame_rate_divisor = frame_rate_divisor;\n\n\t\tif (conversion) {\n\t\t\tinput.conversion = *conversion;\n\t\t} else {\n\t\t\tinput.conversion.format = video->info.format;\n\t\t\tinput.conversion.width = video->info.width;\n\t\t\tinput.conversion.height = video->info.height;\n\t\t\tinput.conversion.range = video->info.range;\n\t\t\tinput.conversion.colorspace = video->info.colorspace;\n\t\t}\n\n\t\tif (input.conversion.width == 0)\n\t\t\tinput.conversion.width = video->info.width;\n\t\tif (input.conversion.height == 0)\n\t\t\tinput.conversion.height = video->info.height;\n\n\t\tsuccess = video_input_init(&input, video);\n\t\tif (success) {\n\t\t\tif (video->inputs.num == 0) {\n\t\t\t\tif (!os_atomic_load_long(&video->gpu_refs)) {\n\t\t\t\t\treset_frames(video);\n\t\t\t\t}\n\t\t\t\tos_atomic_set_bool(&video->raw_active, true);\n\t\t\t}\n\t\t\tda_push_back(video->inputs, &input);\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&video->input_mutex);\n\n\treturn success;\n}\n\nstatic void log_skipped(video_t *video)\n{\n\tlong skipped = os_atomic_load_long(&video->skipped_frames);\n\tdouble percentage_skipped = (double)skipped / (double)os_atomic_load_long(&video->total_frames) * 100.0;\n\n\tif (skipped)\n\t\tblog(LOG_INFO,\n\t\t     \"Video stopped, number of \"\n\t\t     \"skipped frames due \"\n\t\t     \"to encoding lag: \"\n\t\t     \"%ld/%ld (%0.1f%%)\",\n\t\t     video->skipped_frames, video->total_frames, percentage_skipped);\n}\n\nvoid video_output_disconnect(video_t *video, void (*callback)(void *param, struct video_data *frame), void *param)\n{\n\tvideo_output_disconnect2(video, callback, param);\n}\n\nbool video_output_disconnect2(video_t *video, void (*callback)(void *param, struct video_data *frame), void *param)\n{\n\tif (!video || !callback)\n\t\treturn false;\n\n\tvideo = get_root(video);\n\n\tpthread_mutex_lock(&video->input_mutex);\n\n\tsize_t idx = video_get_input_idx(video, callback, param);\n\tif (idx != DARRAY_INVALID) {\n\t\tvideo_input_free(video->inputs.array + idx);\n\t\tda_erase(video->inputs, idx);\n\n\t\tif (video->inputs.num == 0) {\n\t\t\tos_atomic_set_bool(&video->raw_active, false);\n\t\t\tif (!os_atomic_load_long(&video->gpu_refs)) {\n\t\t\t\tlog_skipped(video);\n\t\t\t}\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&video->input_mutex);\n\n\treturn idx != DARRAY_INVALID;\n}\n\nbool video_output_active(const video_t *video)\n{\n\tif (!video)\n\t\treturn false;\n\treturn os_atomic_load_bool(&get_const_root(video)->raw_active);\n}\n\nconst struct video_output_info *video_output_get_info(const video_t *video)\n{\n\treturn video ? &video->info : NULL;\n}\n\nbool video_output_lock_frame(video_t *video, struct video_frame *frame, int count, uint64_t timestamp)\n{\n\tstruct cached_frame_info *cfi;\n\tbool locked;\n\n\tif (!video)\n\t\treturn false;\n\n\tvideo = get_root(video);\n\n\tpthread_mutex_lock(&video->data_mutex);\n\n\tif (video->available_frames == 0) {\n\t\tvideo->cache[video->last_added].count += count;\n\t\tvideo->cache[video->last_added].skipped += count;\n\t\tlocked = false;\n\n\t} else {\n\t\tif (video->available_frames != video->info.cache_size) {\n\t\t\tif (++video->last_added == video->info.cache_size)\n\t\t\t\tvideo->last_added = 0;\n\t\t}\n\n\t\tcfi = &video->cache[video->last_added];\n\t\tcfi->frame.timestamp = timestamp;\n\t\tcfi->count = count;\n\t\tcfi->skipped = 0;\n\n\t\tmemcpy(frame, &cfi->frame, sizeof(*frame));\n\n\t\tlocked = true;\n\t}\n\n\tpthread_mutex_unlock(&video->data_mutex);\n\n\treturn locked;\n}\n\nvoid video_output_unlock_frame(video_t *video)\n{\n\tif (!video)\n\t\treturn;\n\n\tvideo = get_root(video);\n\n\tpthread_mutex_lock(&video->data_mutex);\n\n\tvideo->available_frames--;\n\tos_sem_post(video->update_semaphore);\n\n\tpthread_mutex_unlock(&video->data_mutex);\n}\n\nuint64_t video_output_get_frame_time(const video_t *video)\n{\n\treturn video ? video->frame_time : 0;\n}\n\nvoid video_output_stop(video_t *video)\n{\n\tvoid *thread_ret;\n\n\tif (!video)\n\t\treturn;\n\n\tvideo = get_root(video);\n\n\tif (!video->stop) {\n\t\tvideo->stop = true;\n\t\tos_sem_post(video->update_semaphore);\n\t\tpthread_join(video->thread, &thread_ret);\n\t}\n}\n\nbool video_output_stopped(video_t *video)\n{\n\tif (!video)\n\t\treturn true;\n\n\treturn get_root(video)->stop;\n}\n\nenum video_format video_output_get_format(const video_t *video)\n{\n\treturn video ? get_const_root(video)->info.format : VIDEO_FORMAT_NONE;\n}\n\nuint32_t video_output_get_width(const video_t *video)\n{\n\treturn video ? get_const_root(video)->info.width : 0;\n}\n\nuint32_t video_output_get_height(const video_t *video)\n{\n\treturn video ? get_const_root(video)->info.height : 0;\n}\n\ndouble video_output_get_frame_rate(const video_t *video)\n{\n\tif (!video)\n\t\treturn 0.0;\n\n\tvideo = get_const_root(video);\n\n\treturn (double)video->info.fps_num / (double)video->info.fps_den;\n}\n\nuint32_t video_output_get_skipped_frames(const video_t *video)\n{\n\treturn (uint32_t)os_atomic_load_long(&get_const_root(video)->skipped_frames);\n}\n\nuint32_t video_output_get_total_frames(const video_t *video)\n{\n\treturn (uint32_t)os_atomic_load_long(&get_const_root(video)->total_frames);\n}\n\n/* Note: These four functions below are a very slight bit of a hack.  If the\n * texture encoder thread is active while the raw encoder thread is active, the\n * total frame count will just be doubled while they're both active.  Which is\n * fine.  What's more important is having a relatively accurate skipped frame\n * count. */\n\nvoid video_output_inc_texture_encoders(video_t *video)\n{\n\tvideo = get_root(video);\n\n\tif (os_atomic_inc_long(&video->gpu_refs) == 1 && !os_atomic_load_bool(&video->raw_active)) {\n\t\treset_frames(video);\n\t}\n}\n\nvoid video_output_dec_texture_encoders(video_t *video)\n{\n\tvideo = get_root(video);\n\n\tif (os_atomic_dec_long(&video->gpu_refs) == 0 && !os_atomic_load_bool(&video->raw_active)) {\n\t\tlog_skipped(video);\n\t}\n}\n\nvoid video_output_inc_texture_frames(video_t *video)\n{\n\tos_atomic_inc_long(&get_root(video)->total_frames);\n}\n\nvoid video_output_inc_texture_skipped_frames(video_t *video)\n{\n\tos_atomic_inc_long(&get_root(video)->skipped_frames);\n}\n\nvideo_t *video_output_create_with_frame_rate_divisor(video_t *video, uint32_t divisor)\n{\n\t// `divisor == 1` would result in the same frame rate,\n\t// resulting in an unnecessary additional video output\n\tif (!video || divisor == 0 || divisor == 1)\n\t\treturn NULL;\n\n\tvideo_t *new_video = bzalloc(sizeof(video_t));\n\tmemcpy(new_video, video, sizeof(*new_video));\n\tnew_video->parent = video;\n\tnew_video->info.fps_den *= divisor;\n\n\treturn new_video;\n}\n\nvoid video_output_free_frame_rate_divisor(video_t *video)\n{\n\tif (video && video->parent)\n\t\tbfree(video);\n}\n"
  },
  {
    "path": "libobs/media-io/video-io.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"media-io-defs.h\"\n#include \"../util/c99defs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct video_frame;\n\n/* Base video output component.  Use this to create a video output track. */\n\nstruct video_output;\ntypedef struct video_output video_t;\n\nenum video_format {\n\tVIDEO_FORMAT_NONE,\n\n\t/* planar 4:2:0 formats */\n\tVIDEO_FORMAT_I420, /* three-plane */\n\tVIDEO_FORMAT_NV12, /* two-plane, luma and packed chroma */\n\n\t/* packed 4:2:2 formats */\n\tVIDEO_FORMAT_YVYU,\n\tVIDEO_FORMAT_YUY2, /* YUYV */\n\tVIDEO_FORMAT_UYVY,\n\n\t/* packed uncompressed formats */\n\tVIDEO_FORMAT_RGBA,\n\tVIDEO_FORMAT_BGRA,\n\tVIDEO_FORMAT_BGRX,\n\tVIDEO_FORMAT_Y800, /* grayscale */\n\n\t/* planar 4:4:4 */\n\tVIDEO_FORMAT_I444,\n\n\t/* more packed uncompressed formats */\n\tVIDEO_FORMAT_BGR3,\n\n\t/* planar 4:2:2 */\n\tVIDEO_FORMAT_I422,\n\n\t/* planar 4:2:0 with alpha */\n\tVIDEO_FORMAT_I40A,\n\n\t/* planar 4:2:2 with alpha */\n\tVIDEO_FORMAT_I42A,\n\n\t/* planar 4:4:4 with alpha */\n\tVIDEO_FORMAT_YUVA,\n\n\t/* packed 4:4:4 with alpha */\n\tVIDEO_FORMAT_AYUV,\n\n\t/* planar 4:2:0 format, 10 bpp */\n\tVIDEO_FORMAT_I010, /* three-plane */\n\tVIDEO_FORMAT_P010, /* two-plane, luma and packed chroma */\n\n\t/* planar 4:2:2 format, 10 bpp */\n\tVIDEO_FORMAT_I210,\n\n\t/* planar 4:4:4 format, 12 bpp */\n\tVIDEO_FORMAT_I412,\n\n\t/* planar 4:4:4:4 format, 12 bpp */\n\tVIDEO_FORMAT_YA2L,\n\n\t/* planar 4:2:2 format, 16 bpp */\n\tVIDEO_FORMAT_P216, /* two-plane, luma and packed chroma */\n\n\t/* planar 4:4:4 format, 16 bpp */\n\tVIDEO_FORMAT_P416, /* two-plane, luma and packed chroma */\n\n\t/* packed 4:2:2 format, 10 bpp */\n\tVIDEO_FORMAT_V210,\n\n\t/* packed uncompressed 10-bit format */\n\tVIDEO_FORMAT_R10L,\n};\n\nenum video_trc {\n\tVIDEO_TRC_DEFAULT,\n\tVIDEO_TRC_SRGB,\n\tVIDEO_TRC_PQ,\n\tVIDEO_TRC_HLG,\n};\n\nenum video_colorspace {\n\tVIDEO_CS_DEFAULT,\n\tVIDEO_CS_601,\n\tVIDEO_CS_709,\n\tVIDEO_CS_SRGB,\n\tVIDEO_CS_2100_PQ,\n\tVIDEO_CS_2100_HLG,\n};\n\nenum video_range_type {\n\tVIDEO_RANGE_DEFAULT,\n\tVIDEO_RANGE_PARTIAL,\n\tVIDEO_RANGE_FULL,\n};\n\nstruct video_data {\n\tuint8_t *data[MAX_AV_PLANES];\n\tuint32_t linesize[MAX_AV_PLANES];\n\tuint64_t timestamp;\n};\n\nstruct video_output_info {\n\tconst char *name;\n\n\tenum video_format format;\n\tuint32_t fps_num;\n\tuint32_t fps_den;\n\tuint32_t width;\n\tuint32_t height;\n\tsize_t cache_size;\n\n\tenum video_colorspace colorspace;\n\tenum video_range_type range;\n};\n\nstatic inline bool format_is_yuv(enum video_format format)\n{\n\tswitch (format) {\n\tcase VIDEO_FORMAT_I420:\n\tcase VIDEO_FORMAT_NV12:\n\tcase VIDEO_FORMAT_I422:\n\tcase VIDEO_FORMAT_I210:\n\tcase VIDEO_FORMAT_YVYU:\n\tcase VIDEO_FORMAT_YUY2:\n\tcase VIDEO_FORMAT_UYVY:\n\tcase VIDEO_FORMAT_I444:\n\tcase VIDEO_FORMAT_I412:\n\tcase VIDEO_FORMAT_I40A:\n\tcase VIDEO_FORMAT_I42A:\n\tcase VIDEO_FORMAT_YUVA:\n\tcase VIDEO_FORMAT_YA2L:\n\tcase VIDEO_FORMAT_AYUV:\n\tcase VIDEO_FORMAT_I010:\n\tcase VIDEO_FORMAT_P010:\n\tcase VIDEO_FORMAT_P216:\n\tcase VIDEO_FORMAT_P416:\n\tcase VIDEO_FORMAT_V210:\n\t\treturn true;\n\tcase VIDEO_FORMAT_NONE:\n\tcase VIDEO_FORMAT_RGBA:\n\tcase VIDEO_FORMAT_BGRA:\n\tcase VIDEO_FORMAT_BGRX:\n\tcase VIDEO_FORMAT_Y800:\n\tcase VIDEO_FORMAT_BGR3:\n\tcase VIDEO_FORMAT_R10L:\n\t\treturn false;\n\t}\n\n\treturn false;\n}\n\nstatic inline const char *get_video_format_name(enum video_format format)\n{\n\tswitch (format) {\n\tcase VIDEO_FORMAT_I420:\n\t\treturn \"I420\";\n\tcase VIDEO_FORMAT_NV12:\n\t\treturn \"NV12\";\n\tcase VIDEO_FORMAT_I422:\n\t\treturn \"I422\";\n\tcase VIDEO_FORMAT_I210:\n\t\treturn \"I210\";\n\tcase VIDEO_FORMAT_YVYU:\n\t\treturn \"YVYU\";\n\tcase VIDEO_FORMAT_YUY2:\n\t\treturn \"YUY2\";\n\tcase VIDEO_FORMAT_UYVY:\n\t\treturn \"UYVY\";\n\tcase VIDEO_FORMAT_RGBA:\n\t\treturn \"RGBA\";\n\tcase VIDEO_FORMAT_BGRA:\n\t\treturn \"BGRA\";\n\tcase VIDEO_FORMAT_BGRX:\n\t\treturn \"BGRX\";\n\tcase VIDEO_FORMAT_I444:\n\t\treturn \"I444\";\n\tcase VIDEO_FORMAT_I412:\n\t\treturn \"I412\";\n\tcase VIDEO_FORMAT_Y800:\n\t\treturn \"Y800\";\n\tcase VIDEO_FORMAT_BGR3:\n\t\treturn \"BGR3\";\n\tcase VIDEO_FORMAT_I40A:\n\t\treturn \"I40A\";\n\tcase VIDEO_FORMAT_I42A:\n\t\treturn \"I42A\";\n\tcase VIDEO_FORMAT_YUVA:\n\t\treturn \"YUVA\";\n\tcase VIDEO_FORMAT_YA2L:\n\t\treturn \"YA2L\";\n\tcase VIDEO_FORMAT_AYUV:\n\t\treturn \"AYUV\";\n\tcase VIDEO_FORMAT_I010:\n\t\treturn \"I010\";\n\tcase VIDEO_FORMAT_P010:\n\t\treturn \"P010\";\n\tcase VIDEO_FORMAT_P216:\n\t\treturn \"P216\";\n\tcase VIDEO_FORMAT_P416:\n\t\treturn \"P416\";\n\tcase VIDEO_FORMAT_V210:\n\t\treturn \"v210\";\n\tcase VIDEO_FORMAT_R10L:\n\t\treturn \"R10l\";\n\tcase VIDEO_FORMAT_NONE:;\n\t}\n\n\treturn \"None\";\n}\n\nstatic inline const char *get_video_colorspace_name(enum video_colorspace cs)\n{\n\tswitch (cs) {\n\tcase VIDEO_CS_DEFAULT:\n\tcase VIDEO_CS_709:\n\t\treturn \"Rec. 709\";\n\tcase VIDEO_CS_SRGB:\n\t\treturn \"sRGB\";\n\tcase VIDEO_CS_601:\n\t\treturn \"Rec. 601\";\n\tcase VIDEO_CS_2100_PQ:\n\t\treturn \"Rec. 2100 (PQ)\";\n\tcase VIDEO_CS_2100_HLG:\n\t\treturn \"Rec. 2100 (HLG)\";\n\t}\n\n\treturn \"Unknown\";\n}\n\nstatic inline enum video_range_type resolve_video_range(enum video_format format, enum video_range_type range)\n{\n\tif (range == VIDEO_RANGE_DEFAULT) {\n\t\trange = format_is_yuv(format) ? VIDEO_RANGE_PARTIAL : VIDEO_RANGE_FULL;\n\t}\n\n\treturn range;\n}\n\nstatic inline const char *get_video_range_name(enum video_format format, enum video_range_type range)\n{\n\trange = resolve_video_range(format, range);\n\treturn range == VIDEO_RANGE_FULL ? \"Full\" : \"Partial\";\n}\n\nenum video_scale_type {\n\tVIDEO_SCALE_DEFAULT,\n\tVIDEO_SCALE_POINT,\n\tVIDEO_SCALE_FAST_BILINEAR,\n\tVIDEO_SCALE_BILINEAR,\n\tVIDEO_SCALE_BICUBIC,\n};\n\nstruct video_scale_info {\n\tenum video_format format;\n\tuint32_t width;\n\tuint32_t height;\n\tenum video_range_type range;\n\tenum video_colorspace colorspace;\n};\n\nEXPORT enum video_format video_format_from_fourcc(uint32_t fourcc);\n\nEXPORT bool video_format_get_parameters(enum video_colorspace color_space, enum video_range_type range,\n\t\t\t\t\tfloat matrix[16], float min_range[3], float max_range[3]);\nEXPORT bool video_format_get_parameters_for_format(enum video_colorspace color_space, enum video_range_type range,\n\t\t\t\t\t\t   enum video_format format, float matrix[16], float min_range[3],\n\t\t\t\t\t\t   float max_range[3]);\n\n#define VIDEO_OUTPUT_SUCCESS 0\n#define VIDEO_OUTPUT_INVALIDPARAM -1\n#define VIDEO_OUTPUT_FAIL -2\n\nEXPORT int video_output_open(video_t **video, struct video_output_info *info);\nEXPORT void video_output_close(video_t *video);\n\nEXPORT bool video_output_connect(video_t *video, const struct video_scale_info *conversion,\n\t\t\t\t void (*callback)(void *param, struct video_data *frame), void *param);\nEXPORT bool video_output_connect2(video_t *video, const struct video_scale_info *conversion,\n\t\t\t\t  uint32_t frame_rate_divisor, void (*callback)(void *param, struct video_data *frame),\n\t\t\t\t  void *param);\nEXPORT void video_output_disconnect(video_t *video, void (*callback)(void *param, struct video_data *frame),\n\t\t\t\t    void *param);\nEXPORT bool video_output_disconnect2(video_t *video, void (*callback)(void *param, struct video_data *frame),\n\t\t\t\t     void *param);\n\nEXPORT bool video_output_active(const video_t *video);\n\nEXPORT const struct video_output_info *video_output_get_info(const video_t *video);\nEXPORT bool video_output_lock_frame(video_t *video, struct video_frame *frame, int count, uint64_t timestamp);\nEXPORT void video_output_unlock_frame(video_t *video);\nEXPORT uint64_t video_output_get_frame_time(const video_t *video);\nEXPORT void video_output_stop(video_t *video);\nEXPORT bool video_output_stopped(video_t *video);\n\nEXPORT enum video_format video_output_get_format(const video_t *video);\nEXPORT uint32_t video_output_get_width(const video_t *video);\nEXPORT uint32_t video_output_get_height(const video_t *video);\nEXPORT double video_output_get_frame_rate(const video_t *video);\n\nEXPORT uint32_t video_output_get_skipped_frames(const video_t *video);\nEXPORT uint32_t video_output_get_total_frames(const video_t *video);\n\nextern void video_output_inc_texture_encoders(video_t *video);\nextern void video_output_dec_texture_encoders(video_t *video);\nextern void video_output_inc_texture_frames(video_t *video);\nextern void video_output_inc_texture_skipped_frames(video_t *video);\n\nextern video_t *video_output_create_with_frame_rate_divisor(video_t *video, uint32_t divisor);\nextern void video_output_free_frame_rate_divisor(video_t *video);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/media-io/video-matrices.c",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"video-io.h\"\n\n#include \"../util/bmem.h\"\n#include \"../graphics/matrix3.h\"\n\n#include <assert.h>\n\n//#define LOG_MATRICES\n\nstatic struct {\n\tfloat range_min[3];\n\tfloat range_max[3];\n\tfloat black_levels[2][3];\n\n\tfloat float_range_min[3];\n\tfloat float_range_max[3];\n} bpp_info[9];\n\nstatic struct {\n\tenum video_colorspace const color_space;\n\tfloat const Kb, Kr;\n\tfloat matrix[OBS_COUNTOF(bpp_info)][2][16];\n} format_info[] = {\n\t{\n\t\tVIDEO_CS_601,\n\t\t0.114f,\n\t\t0.299f,\n\t},\n\t{\n\t\tVIDEO_CS_709,\n\t\t0.0722f,\n\t\t0.2126f,\n\t},\n\t{\n\t\tVIDEO_CS_2100_PQ,\n\t\t0.0593f,\n\t\t0.2627f,\n\t},\n};\n\n#define NUM_FORMATS (sizeof(format_info) / sizeof(format_info[0]))\n\n#ifdef LOG_MATRICES\nstatic void log_matrix(float const matrix[16])\n{\n\tblog(LOG_DEBUG,\n\t     \"\\n% f, % f, % f, % f\"\n\t     \"\\n% f, % f, % f, % f\"\n\t     \"\\n% f, % f, % f, % f\"\n\t     \"\\n% f, % f, % f, % f\",\n\t     matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6], matrix[7], matrix[8],\n\t     matrix[9], matrix[10], matrix[11], matrix[12], matrix[13], matrix[14], matrix[15]);\n}\n#endif\n\nstatic void initialize_matrix(float const Kb, float const Kr, float bit_range_max, float const range_min[3],\n\t\t\t      float const range_max[3], float const black_levels[3], float matrix[16])\n{\n\tstruct matrix3 color_matrix;\n\n\tfloat const yvals = range_max[0] - range_min[0];\n\tfloat const uvals = (range_max[1] - range_min[1]) / 2.f;\n\tfloat const vvals = (range_max[2] - range_min[2]) / 2.f;\n\n\tfloat const yscale = bit_range_max / yvals;\n\tfloat const uscale = bit_range_max / uvals;\n\tfloat const vscale = bit_range_max / vvals;\n\n\tfloat const Kg = (1.f - Kb - Kr);\n\n\tvec3_set(&color_matrix.x, yscale, 0.f, vscale * (1.f - Kr));\n\tvec3_set(&color_matrix.y, yscale, uscale * (Kb - 1.f) * Kb / Kg, vscale * (Kr - 1.f) * Kr / Kg);\n\tvec3_set(&color_matrix.z, yscale, uscale * (1.f - Kb), 0.f);\n\n\tstruct vec3 offsets, multiplied;\n\tvec3_set(&offsets, -black_levels[0] / bit_range_max, -black_levels[1] / bit_range_max,\n\t\t -black_levels[2] / bit_range_max);\n\tvec3_rotate(&multiplied, &offsets, &color_matrix);\n\n\tmatrix[0] = color_matrix.x.x;\n\tmatrix[1] = color_matrix.x.y;\n\tmatrix[2] = color_matrix.x.z;\n\tmatrix[3] = multiplied.x;\n\n\tmatrix[4] = color_matrix.y.x;\n\tmatrix[5] = color_matrix.y.y;\n\tmatrix[6] = color_matrix.y.z;\n\tmatrix[7] = multiplied.y;\n\n\tmatrix[8] = color_matrix.z.x;\n\tmatrix[9] = color_matrix.z.y;\n\tmatrix[10] = color_matrix.z.z;\n\tmatrix[11] = multiplied.z;\n\n\tmatrix[12] = matrix[13] = matrix[14] = 0.;\n\tmatrix[15] = 1.;\n\n#ifdef LOG_MATRICES\n\tlog_matrix(matrix);\n#endif\n}\n\nstatic void initialize_matrices()\n{\n\tstatic const float full_range_min3[] = {0, 0, 0};\n\n\tfloat min_value = 16.f;\n\tfloat max_luma = 235.f;\n\tfloat max_chroma = 240.f;\n\tfloat range = 256.f;\n\tfor (uint32_t bpp = 8; bpp <= 16; ++bpp) {\n\t\tconst uint32_t bpp_index = bpp - 8;\n\t\tbpp_info[bpp_index].range_min[0] = min_value;\n\t\tbpp_info[bpp_index].range_min[1] = min_value;\n\t\tbpp_info[bpp_index].range_min[2] = min_value;\n\t\tbpp_info[bpp_index].range_max[0] = max_luma;\n\t\tbpp_info[bpp_index].range_max[1] = max_chroma;\n\t\tbpp_info[bpp_index].range_max[2] = max_chroma;\n\t\tconst float mid_chroma = 0.5f * (min_value + max_chroma);\n\t\tbpp_info[bpp_index].black_levels[0][0] = min_value;\n\t\tbpp_info[bpp_index].black_levels[0][1] = mid_chroma;\n\t\tbpp_info[bpp_index].black_levels[0][2] = mid_chroma;\n\t\tbpp_info[bpp_index].black_levels[1][0] = 0.f;\n\t\tbpp_info[bpp_index].black_levels[1][1] = mid_chroma;\n\t\tbpp_info[bpp_index].black_levels[1][2] = mid_chroma;\n\t\tconst float range_max = range - 1.f;\n\t\tbpp_info[bpp_index].float_range_min[0] = min_value / range_max;\n\t\tbpp_info[bpp_index].float_range_min[1] = min_value / range_max;\n\t\tbpp_info[bpp_index].float_range_min[2] = min_value / range_max;\n\t\tbpp_info[bpp_index].float_range_max[0] = max_luma / range_max;\n\t\tbpp_info[bpp_index].float_range_max[1] = max_chroma / range_max;\n\t\tbpp_info[bpp_index].float_range_max[2] = max_chroma / range_max;\n\n\t\tfor (size_t i = 0; i < NUM_FORMATS; i++) {\n\t\t\tfloat full_range_max3[] = {range_max, range_max, range_max};\n\t\t\tinitialize_matrix(format_info[i].Kb, format_info[i].Kr, range_max, full_range_min3,\n\t\t\t\t\t  full_range_max3, bpp_info[bpp_index].black_levels[1],\n\t\t\t\t\t  format_info[i].matrix[bpp_index][1]);\n\n\t\t\tinitialize_matrix(format_info[i].Kb, format_info[i].Kr, range_max,\n\t\t\t\t\t  bpp_info[bpp_index].range_min, bpp_info[bpp_index].range_max,\n\t\t\t\t\t  bpp_info[bpp_index].black_levels[0], format_info[i].matrix[bpp_index][0]);\n\t\t}\n\n\t\tmin_value *= 2.f;\n\t\tmax_luma *= 2.f;\n\t\tmax_chroma *= 2.f;\n\t\trange *= 2.f;\n\t}\n}\n\nstatic bool matrices_initialized = false;\n\nstatic const float full_min[3] = {0.0f, 0.0f, 0.0f};\nstatic const float full_max[3] = {1.0f, 1.0f, 1.0f};\n\nstatic bool video_format_get_parameters_for_bpc(enum video_colorspace color_space, enum video_range_type range,\n\t\t\t\t\t\tfloat matrix[16], float range_min[3], float range_max[3], uint32_t bpc)\n{\n\tif (!matrices_initialized) {\n\t\tinitialize_matrices();\n\t\tmatrices_initialized = true;\n\t}\n\n\tif ((color_space == VIDEO_CS_DEFAULT) || (color_space == VIDEO_CS_SRGB))\n\t\tcolor_space = VIDEO_CS_709;\n\telse if (color_space == VIDEO_CS_2100_HLG)\n\t\tcolor_space = VIDEO_CS_2100_PQ;\n\n\tif (bpc < 8)\n\t\tbpc = 8;\n\tif (bpc > 16)\n\t\tbpc = 16;\n\tconst uint32_t bpc_index = bpc - 8;\n\tassert(bpc_index < OBS_COUNTOF(bpp_info));\n\n\tbool success = false;\n\n\tfor (size_t i = 0; i < NUM_FORMATS; i++) {\n\t\tsuccess = format_info[i].color_space == color_space;\n\t\tif (success) {\n\t\t\tconst bool full_range = range == VIDEO_RANGE_FULL;\n\t\t\tmemcpy(matrix, format_info[i].matrix[bpc_index][full_range], sizeof(float) * 16);\n\n\t\t\tif (range_min) {\n\t\t\t\tconst float *src_range_min = full_range ? full_min\n\t\t\t\t\t\t\t\t\t: bpp_info[bpc_index].float_range_min;\n\t\t\t\tmemcpy(range_min, src_range_min, sizeof(float) * 3);\n\t\t\t}\n\n\t\t\tif (range_max) {\n\t\t\t\tconst float *src_range_max = full_range ? full_max\n\t\t\t\t\t\t\t\t\t: bpp_info[bpc_index].float_range_max;\n\t\t\t\tmemcpy(range_max, src_range_max, sizeof(float) * 3);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn success;\n}\n\nbool video_format_get_parameters(enum video_colorspace color_space, enum video_range_type range, float matrix[16],\n\t\t\t\t float range_min[3], float range_max[3])\n{\n\tuint32_t bpc = (color_space == VIDEO_CS_2100_PQ || color_space == VIDEO_CS_2100_HLG) ? 10 : 8;\n\n\treturn video_format_get_parameters_for_bpc(color_space, range, matrix, range_min, range_max, bpc);\n}\n\nbool video_format_get_parameters_for_format(enum video_colorspace color_space, enum video_range_type range,\n\t\t\t\t\t    enum video_format format, float matrix[16], float range_min[3],\n\t\t\t\t\t    float range_max[3])\n{\n\tuint32_t bpc;\n\tswitch (format) {\n\tcase VIDEO_FORMAT_I010:\n\tcase VIDEO_FORMAT_P010:\n\tcase VIDEO_FORMAT_I210:\n\tcase VIDEO_FORMAT_V210:\n\tcase VIDEO_FORMAT_R10L:\n\t\tbpc = 10;\n\t\tbreak;\n\tcase VIDEO_FORMAT_I412:\n\tcase VIDEO_FORMAT_YA2L:\n\t\tbpc = 12;\n\t\tbreak;\n\tcase VIDEO_FORMAT_P216:\n\tcase VIDEO_FORMAT_P416:\n\t\tbpc = 16;\n\t\tbreak;\n\tdefault:\n\t\tbpc = 8;\n\t\tbreak;\n\t}\n\n\treturn video_format_get_parameters_for_bpc(color_space, range, matrix, range_min, range_max, bpc);\n}\n"
  },
  {
    "path": "libobs/media-io/video-scaler-ffmpeg.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"../util/bmem.h\"\n#include \"video-scaler.h\"\n\n#include <libavutil/imgutils.h>\n#include <libavutil/opt.h>\n#include <libswscale/swscale.h>\n\nstruct video_scaler {\n\tstruct SwsContext *swscale;\n\tint src_height;\n\tint dst_heights[4];\n\tuint8_t *dst_pointers[4];\n\tint dst_linesizes[4];\n};\n\nstatic inline enum AVPixelFormat get_ffmpeg_video_format(enum video_format format)\n{\n\tswitch (format) {\n\tcase VIDEO_FORMAT_I420:\n\t\treturn AV_PIX_FMT_YUV420P;\n\tcase VIDEO_FORMAT_NV12:\n\t\treturn AV_PIX_FMT_NV12;\n\tcase VIDEO_FORMAT_YUY2:\n\t\treturn AV_PIX_FMT_YUYV422;\n\tcase VIDEO_FORMAT_UYVY:\n\t\treturn AV_PIX_FMT_UYVY422;\n\tcase VIDEO_FORMAT_YVYU:\n\t\treturn AV_PIX_FMT_YVYU422;\n\tcase VIDEO_FORMAT_RGBA:\n\t\treturn AV_PIX_FMT_RGBA;\n\tcase VIDEO_FORMAT_BGRA:\n\t\treturn AV_PIX_FMT_BGRA;\n\tcase VIDEO_FORMAT_BGRX:\n\t\treturn AV_PIX_FMT_BGRA;\n\tcase VIDEO_FORMAT_Y800:\n\t\treturn AV_PIX_FMT_GRAY8;\n\tcase VIDEO_FORMAT_I444:\n\t\treturn AV_PIX_FMT_YUV444P;\n\tcase VIDEO_FORMAT_I412:\n\t\treturn AV_PIX_FMT_YUV444P12LE;\n\tcase VIDEO_FORMAT_BGR3:\n\t\treturn AV_PIX_FMT_BGR24;\n\tcase VIDEO_FORMAT_I422:\n\t\treturn AV_PIX_FMT_YUV422P;\n\tcase VIDEO_FORMAT_I210:\n\t\treturn AV_PIX_FMT_YUV422P10LE;\n\tcase VIDEO_FORMAT_I40A:\n\t\treturn AV_PIX_FMT_YUVA420P;\n\tcase VIDEO_FORMAT_I42A:\n\t\treturn AV_PIX_FMT_YUVA422P;\n\tcase VIDEO_FORMAT_YUVA:\n\t\treturn AV_PIX_FMT_YUVA444P;\n\tcase VIDEO_FORMAT_YA2L:\n\t\treturn AV_PIX_FMT_YUVA444P12LE;\n\tcase VIDEO_FORMAT_I010:\n\t\treturn AV_PIX_FMT_YUV420P10LE;\n\tcase VIDEO_FORMAT_P010:\n\t\treturn AV_PIX_FMT_P010LE;\n\tcase VIDEO_FORMAT_P216:\n\t\treturn AV_PIX_FMT_P216LE;\n\tcase VIDEO_FORMAT_P416:\n\t\treturn AV_PIX_FMT_P416LE;\n\tcase VIDEO_FORMAT_NONE:\n\tcase VIDEO_FORMAT_AYUV:\n\tdefault:\n\t\treturn AV_PIX_FMT_NONE;\n\t}\n}\n\nstatic inline int get_ffmpeg_scale_type(enum video_scale_type type)\n{\n\tswitch (type) {\n\tcase VIDEO_SCALE_DEFAULT:\n\t\treturn SWS_FAST_BILINEAR;\n\tcase VIDEO_SCALE_POINT:\n\t\treturn SWS_POINT;\n\tcase VIDEO_SCALE_FAST_BILINEAR:\n\t\treturn SWS_FAST_BILINEAR;\n\tcase VIDEO_SCALE_BILINEAR:\n\t\treturn SWS_BILINEAR | SWS_AREA;\n\tcase VIDEO_SCALE_BICUBIC:\n\t\treturn SWS_BICUBIC;\n\t}\n\n\treturn SWS_POINT;\n}\n\nstatic inline const int *get_ffmpeg_coeffs(enum video_colorspace cs)\n{\n\tint colorspace = SWS_CS_ITU709;\n\n\tswitch (cs) {\n\tcase VIDEO_CS_DEFAULT:\n\tcase VIDEO_CS_709:\n\tcase VIDEO_CS_SRGB:\n\tdefault:\n\t\tcolorspace = SWS_CS_ITU709;\n\t\tbreak;\n\tcase VIDEO_CS_601:\n\t\tcolorspace = SWS_CS_ITU601;\n\t\tbreak;\n\tcase VIDEO_CS_2100_PQ:\n\tcase VIDEO_CS_2100_HLG:\n\t\tcolorspace = SWS_CS_BT2020;\n\t}\n\n\treturn sws_getCoefficients(colorspace);\n}\n\nstatic inline int get_ffmpeg_range_type(enum video_range_type type)\n{\n\tswitch (type) {\n\tcase VIDEO_RANGE_DEFAULT:\n\t\treturn 0;\n\tcase VIDEO_RANGE_PARTIAL:\n\t\treturn 0;\n\tcase VIDEO_RANGE_FULL:\n\t\treturn 1;\n\t}\n\n\treturn 0;\n}\n\n#define FIXED_1_0 (1 << 16)\n\nint video_scaler_create(video_scaler_t **scaler_out, const struct video_scale_info *dst,\n\t\t\tconst struct video_scale_info *src, enum video_scale_type type)\n{\n\tenum AVPixelFormat format_src = get_ffmpeg_video_format(src->format);\n\tenum AVPixelFormat format_dst = get_ffmpeg_video_format(dst->format);\n\tint scale_type = get_ffmpeg_scale_type(type);\n\tconst int *coeff_src = get_ffmpeg_coeffs(src->colorspace);\n\tconst int *coeff_dst = get_ffmpeg_coeffs(dst->colorspace);\n\tint range_src = get_ffmpeg_range_type(src->range);\n\tint range_dst = get_ffmpeg_range_type(dst->range);\n\tstruct video_scaler *scaler;\n\tint ret;\n\n\tif (!scaler_out)\n\t\treturn VIDEO_SCALER_FAILED;\n\n\tif (format_src == AV_PIX_FMT_NONE || format_dst == AV_PIX_FMT_NONE)\n\t\treturn VIDEO_SCALER_BAD_CONVERSION;\n\n\tscaler = bzalloc(sizeof(struct video_scaler));\n\tscaler->src_height = src->height;\n\n\tconst AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(format_dst);\n\tbool has_plane[4] = {0};\n\tfor (size_t i = 0; i < 4; i++)\n\t\thas_plane[desc->comp[i].plane] = 1;\n\n\tscaler->dst_heights[0] = dst->height;\n\tfor (size_t i = 1; i < 4; ++i) {\n\t\tif (has_plane[i]) {\n\t\t\tconst int s = (i == 1 || i == 2) ? desc->log2_chroma_h : 0;\n\t\t\tscaler->dst_heights[i] = dst->height >> s;\n\t\t}\n\t}\n\n\tret = av_image_alloc(scaler->dst_pointers, scaler->dst_linesizes, dst->width, dst->height, format_dst, 32);\n\tif (ret < 0) {\n\t\tblog(LOG_WARNING, \"video_scaler_create: av_image_alloc failed: %d\", ret);\n\t\tgoto fail;\n\t}\n\n\tscaler->swscale = sws_alloc_context();\n\tif (!scaler->swscale) {\n\t\tblog(LOG_ERROR, \"video_scaler_create: Could not create \"\n\t\t\t\t\"swscale\");\n\t\tgoto fail;\n\t}\n\n\tav_opt_set_int(scaler->swscale, \"sws_flags\", scale_type, 0);\n\tav_opt_set_int(scaler->swscale, \"srcw\", src->width, 0);\n\tav_opt_set_int(scaler->swscale, \"srch\", src->height, 0);\n\tav_opt_set_int(scaler->swscale, \"dstw\", dst->width, 0);\n\tav_opt_set_int(scaler->swscale, \"dsth\", dst->height, 0);\n\tav_opt_set_int(scaler->swscale, \"src_format\", format_src, 0);\n\tav_opt_set_int(scaler->swscale, \"dst_format\", format_dst, 0);\n\tav_opt_set_int(scaler->swscale, \"src_range\", range_src, 0);\n\tav_opt_set_int(scaler->swscale, \"dst_range\", range_dst, 0);\n\tif (sws_init_context(scaler->swscale, NULL, NULL) < 0) {\n\t\tblog(LOG_ERROR, \"video_scaler_create: sws_init_context failed\");\n\t\tgoto fail;\n\t}\n\n\tret = sws_setColorspaceDetails(scaler->swscale, coeff_src, range_src, coeff_dst, range_dst, 0, FIXED_1_0,\n\t\t\t\t       FIXED_1_0);\n\tif (ret < 0) {\n\t\tblog(LOG_DEBUG, \"video_scaler_create: \"\n\t\t\t\t\"sws_setColorspaceDetails failed, ignoring\");\n\t}\n\n\t*scaler_out = scaler;\n\treturn VIDEO_SCALER_SUCCESS;\n\nfail:\n\tvideo_scaler_destroy(scaler);\n\treturn VIDEO_SCALER_FAILED;\n}\n\nvoid video_scaler_destroy(video_scaler_t *scaler)\n{\n\tif (scaler) {\n\t\tsws_freeContext(scaler->swscale);\n\n\t\tif (scaler->dst_pointers[0])\n\t\t\tav_freep(scaler->dst_pointers);\n\n\t\tbfree(scaler);\n\t}\n}\n\nbool video_scaler_scale(video_scaler_t *scaler, uint8_t *output[], const uint32_t out_linesize[],\n\t\t\tconst uint8_t *const input[], const uint32_t in_linesize[])\n{\n\tif (!scaler)\n\t\treturn false;\n\n\tint ret = sws_scale(scaler->swscale, input, (const int *)in_linesize, 0, scaler->src_height,\n\t\t\t    scaler->dst_pointers, scaler->dst_linesizes);\n\tif (ret <= 0) {\n\t\tblog(LOG_ERROR, \"video_scaler_scale: sws_scale failed: %d\", ret);\n\t\treturn false;\n\t}\n\n\tfor (size_t plane = 0; plane < 4; ++plane) {\n\t\tif (!scaler->dst_pointers[plane])\n\t\t\tcontinue;\n\n\t\tconst size_t scaled_linesize = scaler->dst_linesizes[plane];\n\t\tconst size_t plane_linesize = out_linesize[plane];\n\t\tuint8_t *dst = output[plane];\n\t\tconst uint8_t *src = scaler->dst_pointers[plane];\n\t\tconst size_t height = scaler->dst_heights[plane];\n\t\tif (scaled_linesize == plane_linesize) {\n\t\t\tmemcpy(dst, src, scaled_linesize * height);\n\t\t} else {\n\t\t\tsize_t linesize = scaled_linesize;\n\t\t\tif (linesize > plane_linesize)\n\t\t\t\tlinesize = plane_linesize;\n\n\t\t\tfor (size_t y = 0; y < height; y++) {\n\t\t\t\tmemcpy(dst, src, linesize);\n\t\t\t\tdst += plane_linesize;\n\t\t\t\tsrc += scaled_linesize;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn true;\n}\n"
  },
  {
    "path": "libobs/media-io/video-scaler.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"../util/c99defs.h\"\n#include \"video-io.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct video_scaler;\ntypedef struct video_scaler video_scaler_t;\n\n#define VIDEO_SCALER_SUCCESS 0\n#define VIDEO_SCALER_BAD_CONVERSION -1\n#define VIDEO_SCALER_FAILED -2\n\nEXPORT int video_scaler_create(video_scaler_t **scaler, const struct video_scale_info *dst,\n\t\t\t       const struct video_scale_info *src, enum video_scale_type type);\nEXPORT void video_scaler_destroy(video_scaler_t *scaler);\n\nEXPORT bool video_scaler_scale(video_scaler_t *scaler, uint8_t *output[], const uint32_t out_linesize[],\n\t\t\t       const uint8_t *const input[], const uint32_t in_linesize[]);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-audio-controls.c",
    "content": "/*\nCopyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include <math.h>\n\n#include \"util/sse-intrin.h\"\n\n#include \"util/threading.h\"\n#include \"util/bmem.h\"\n#include \"media-io/audio-math.h\"\n#include \"obs.h\"\n#include \"obs-internal.h\"\n\n#include \"obs-audio-controls.h\"\n\n/* These are pointless warnings generated not by our code, but by a standard\n * library macro, INFINITY */\n#ifdef _MSC_VER\n#pragma warning(disable : 4056)\n#pragma warning(disable : 4756)\n#endif\n\n#define CLAMP(x, min, max) ((x) < min ? min : ((x) > max ? max : (x)))\n\nstruct fader_cb {\n\tobs_fader_changed_t callback;\n\tvoid *param;\n};\n\nstruct obs_fader {\n\tpthread_mutex_t mutex;\n\tobs_fader_conversion_t def_to_db;\n\tobs_fader_conversion_t db_to_def;\n\tobs_source_t *source;\n\tenum obs_fader_type type;\n\tfloat max_db;\n\tfloat min_db;\n\tfloat cur_db;\n\tbool ignore_next_signal;\n\n\tpthread_mutex_t callback_mutex;\n\tDARRAY(struct fader_cb) callbacks;\n};\n\nstruct meter_cb {\n\tobs_volmeter_updated_t callback;\n\tvoid *param;\n};\n\nstruct obs_volmeter {\n\tpthread_mutex_t mutex;\n\tobs_source_t *source;\n\tenum obs_fader_type type;\n\tfloat cur_db;\n\n\tpthread_mutex_t callback_mutex;\n\tDARRAY(struct meter_cb) callbacks;\n\n\tenum obs_peak_meter_type peak_meter_type;\n\tunsigned int update_ms;\n\tfloat prev_samples[MAX_AUDIO_CHANNELS][4];\n\n\tfloat magnitude[MAX_AUDIO_CHANNELS];\n\tfloat peak[MAX_AUDIO_CHANNELS];\n};\n\nstatic float cubic_def_to_db(const float def)\n{\n\tif (def == 1.0f)\n\t\treturn 0.0f;\n\telse if (def <= 0.0f)\n\t\treturn -INFINITY;\n\n\treturn mul_to_db(def * def * def);\n}\n\nstatic float cubic_db_to_def(const float db)\n{\n\tif (db == 0.0f)\n\t\treturn 1.0f;\n\telse if (db == -INFINITY)\n\t\treturn 0.0f;\n\n\treturn cbrtf(db_to_mul(db));\n}\n\nstatic float iec_def_to_db(const float def)\n{\n\tif (def == 1.0f)\n\t\treturn 0.0f;\n\telse if (def <= 0.0f)\n\t\treturn -INFINITY;\n\n\tfloat db;\n\n\tif (def >= 0.75f)\n\t\tdb = (def - 1.0f) / 0.25f * 9.0f;\n\telse if (def >= 0.5f)\n\t\tdb = (def - 0.75f) / 0.25f * 11.0f - 9.0f;\n\telse if (def >= 0.3f)\n\t\tdb = (def - 0.5f) / 0.2f * 10.0f - 20.0f;\n\telse if (def >= 0.15f)\n\t\tdb = (def - 0.3f) / 0.15f * 10.0f - 30.0f;\n\telse if (def >= 0.075f)\n\t\tdb = (def - 0.15f) / 0.075f * 10.0f - 40.0f;\n\telse if (def >= 0.025f)\n\t\tdb = (def - 0.075f) / 0.05f * 10.0f - 50.0f;\n\telse if (def >= 0.001f)\n\t\tdb = (def - 0.025f) / 0.025f * 90.0f - 60.0f;\n\telse\n\t\tdb = -INFINITY;\n\n\treturn db;\n}\n\nstatic float iec_db_to_def(const float db)\n{\n\tif (db == 0.0f)\n\t\treturn 1.0f;\n\telse if (db == -INFINITY)\n\t\treturn 0.0f;\n\n\tfloat def;\n\n\tif (db >= -9.0f)\n\t\tdef = (db + 9.0f) / 9.0f * 0.25f + 0.75f;\n\telse if (db >= -20.0f)\n\t\tdef = (db + 20.0f) / 11.0f * 0.25f + 0.5f;\n\telse if (db >= -30.0f)\n\t\tdef = (db + 30.0f) / 10.0f * 0.2f + 0.3f;\n\telse if (db >= -40.0f)\n\t\tdef = (db + 40.0f) / 10.0f * 0.15f + 0.15f;\n\telse if (db >= -50.0f)\n\t\tdef = (db + 50.0f) / 10.0f * 0.075f + 0.075f;\n\telse if (db >= -60.0f)\n\t\tdef = (db + 60.0f) / 10.0f * 0.05f + 0.025f;\n\telse if (db >= -114.0f)\n\t\tdef = (db + 150.0f) / 90.0f * 0.025f;\n\telse\n\t\tdef = 0.0f;\n\n\treturn def;\n}\n\n#define LOG_OFFSET_DB 6.0f\n#define LOG_RANGE_DB 96.0f\n/* equals -log10f(LOG_OFFSET_DB) */\n#define LOG_OFFSET_VAL -0.77815125038364363f\n/* equals -log10f(-LOG_RANGE_DB + LOG_OFFSET_DB) */\n#define LOG_RANGE_VAL -2.00860017176191756f\n\nstatic float log_def_to_db(const float def)\n{\n\tif (def >= 1.0f)\n\t\treturn 0.0f;\n\telse if (def <= 0.0f)\n\t\treturn -INFINITY;\n\n\treturn -(LOG_RANGE_DB + LOG_OFFSET_DB) * powf((LOG_RANGE_DB + LOG_OFFSET_DB) / LOG_OFFSET_DB, -def) +\n\t       LOG_OFFSET_DB;\n}\n\nstatic float log_db_to_def(const float db)\n{\n\tif (db >= 0.0f)\n\t\treturn 1.0f;\n\telse if (db <= -96.0f)\n\t\treturn 0.0f;\n\n\treturn (-log10f(-db + LOG_OFFSET_DB) - LOG_RANGE_VAL) / (LOG_OFFSET_VAL - LOG_RANGE_VAL);\n}\n\nstatic void signal_volume_changed(struct obs_fader *fader, const float db)\n{\n\tpthread_mutex_lock(&fader->callback_mutex);\n\tfor (size_t i = fader->callbacks.num; i > 0; i--) {\n\t\tstruct fader_cb cb = fader->callbacks.array[i - 1];\n\t\tcb.callback(cb.param, db);\n\t}\n\tpthread_mutex_unlock(&fader->callback_mutex);\n}\n\nstatic void signal_levels_updated(struct obs_volmeter *volmeter, const float magnitude[MAX_AUDIO_CHANNELS],\n\t\t\t\t  const float peak[MAX_AUDIO_CHANNELS], const float input_peak[MAX_AUDIO_CHANNELS])\n{\n\tpthread_mutex_lock(&volmeter->callback_mutex);\n\tfor (size_t i = volmeter->callbacks.num; i > 0; i--) {\n\t\tstruct meter_cb cb = volmeter->callbacks.array[i - 1];\n\t\tcb.callback(cb.param, magnitude, peak, input_peak);\n\t}\n\tpthread_mutex_unlock(&volmeter->callback_mutex);\n}\n\nstatic void fader_source_volume_changed(void *vptr, calldata_t *calldata)\n{\n\tstruct obs_fader *fader = (struct obs_fader *)vptr;\n\n\tpthread_mutex_lock(&fader->mutex);\n\n\tif (fader->ignore_next_signal) {\n\t\tfader->ignore_next_signal = false;\n\t\tpthread_mutex_unlock(&fader->mutex);\n\t\treturn;\n\t}\n\n\tconst float mul = (float)calldata_float(calldata, \"volume\");\n\tconst float db = mul_to_db(mul);\n\tfader->cur_db = db;\n\n\tpthread_mutex_unlock(&fader->mutex);\n\n\tsignal_volume_changed(fader, db);\n}\n\nstatic void volmeter_source_volume_changed(void *vptr, calldata_t *calldata)\n{\n\tstruct obs_volmeter *volmeter = (struct obs_volmeter *)vptr;\n\n\tpthread_mutex_lock(&volmeter->mutex);\n\n\tfloat mul = (float)calldata_float(calldata, \"volume\");\n\tvolmeter->cur_db = mul_to_db(mul);\n\n\tpthread_mutex_unlock(&volmeter->mutex);\n}\n\nstatic void fader_source_destroyed(void *vptr, calldata_t *calldata)\n{\n\tUNUSED_PARAMETER(calldata);\n\tstruct obs_fader *fader = (struct obs_fader *)vptr;\n\n\tobs_fader_detach_source(fader);\n}\n\nstatic void volmeter_source_destroyed(void *vptr, calldata_t *calldata)\n{\n\tUNUSED_PARAMETER(calldata);\n\tstruct obs_volmeter *volmeter = (struct obs_volmeter *)vptr;\n\n\tobs_volmeter_detach_source(volmeter);\n}\n\nstatic int get_nr_channels_from_audio_data(const struct audio_data *data)\n{\n\tint nr_channels = 0;\n\tfor (int i = 0; i < MAX_AV_PLANES; i++) {\n\t\tif (data->data[i])\n\t\t\tnr_channels++;\n\t}\n\treturn CLAMP(nr_channels, 0, MAX_AUDIO_CHANNELS);\n}\n\n/* msb(h, g, f, e) lsb(d, c, b, a)   -->  msb(h, h, g, f) lsb(e, d, c, b)\n */\n#define SHIFT_RIGHT_2PS(msb, lsb)                                               \\\n\t{                                                                       \\\n\t\t__m128 tmp = _mm_shuffle_ps(lsb, msb, _MM_SHUFFLE(0, 0, 3, 3)); \\\n\t\tlsb = _mm_shuffle_ps(lsb, tmp, _MM_SHUFFLE(2, 1, 2, 1));        \\\n\t\tmsb = _mm_shuffle_ps(msb, msb, _MM_SHUFFLE(3, 3, 2, 1));        \\\n\t}\n\n/* x(d, c, b, a) --> (|d|, |c|, |b|, |a|)\n */\n#define abs_ps(v) _mm_andnot_ps(_mm_set1_ps(-0.f), v)\n\n/* Take cross product of a vector with a matrix resulting in vector.\n */\n#define VECTOR_MATRIX_CROSS_PS(out, v, m0, m1, m2, m3)    \\\n\t{                                                 \\\n\t\tout = _mm_mul_ps(v, m0);                  \\\n\t\t__m128 mul1 = _mm_mul_ps(v, m1);          \\\n\t\t__m128 mul2 = _mm_mul_ps(v, m2);          \\\n\t\t__m128 mul3 = _mm_mul_ps(v, m3);          \\\n                                                          \\\n\t\t_MM_TRANSPOSE4_PS(out, mul1, mul2, mul3); \\\n                                                          \\\n\t\tout = _mm_add_ps(out, mul1);              \\\n\t\tout = _mm_add_ps(out, mul2);              \\\n\t\tout = _mm_add_ps(out, mul3);              \\\n\t}\n\n/* x4(d, c, b, a)  -->  max(a, b, c, d)\n */\n#define hmax_ps(r, x4)                     \\\n\tdo {                               \\\n\t\tfloat x4_mem[4];           \\\n\t\t_mm_storeu_ps(x4_mem, x4); \\\n\t\tr = x4_mem[0];             \\\n\t\tr = fmaxf(r, x4_mem[1]);   \\\n\t\tr = fmaxf(r, x4_mem[2]);   \\\n\t\tr = fmaxf(r, x4_mem[3]);   \\\n\t} while (false)\n\n/* Calculate the true peak over a set of samples.\n * The algorithm implements 5x oversampling by using Whittaker-Shannon\n * interpolation over four samples.\n *\n * The four samples have location t=-1.5, -0.5, +0.5, +1.5\n * The oversamples are taken at locations t=-0.3, -0.1, +0.1, +0.3\n *\n * @param previous_samples  Last 4 samples from the previous iteration.\n * @param samples           The samples to find the peak in.\n * @param nr_samples        Number of sets of 4 samples.\n * @returns 5 times oversampled true-peak from the set of samples.\n */\nstatic float get_true_peak(__m128 previous_samples, const float *samples, size_t nr_samples)\n{\n\t/* These are normalized-sinc parameters for interpolating over sample\n\t * points which are located at x-coords: -1.5, -0.5, +0.5, +1.5.\n\t * And oversample points at x-coords: -0.3, -0.1, 0.1, 0.3. */\n\tconst __m128 m3 = _mm_set_ps(-0.155915f, 0.935489f, 0.233872f, -0.103943f);\n\tconst __m128 m1 = _mm_set_ps(-0.216236f, 0.756827f, 0.504551f, -0.189207f);\n\tconst __m128 p1 = _mm_set_ps(-0.189207f, 0.504551f, 0.756827f, -0.216236f);\n\tconst __m128 p3 = _mm_set_ps(-0.103943f, 0.233872f, 0.935489f, -0.155915f);\n\n\t__m128 work = previous_samples;\n\t__m128 peak = previous_samples;\n\tfor (size_t i = 0; (i + 3) < nr_samples; i += 4) {\n\t\t__m128 new_work = _mm_load_ps(&samples[i]);\n\t\t__m128 intrp_samples;\n\n\t\t/* Include the actual sample values in the peak. */\n\t\t__m128 abs_new_work = abs_ps(new_work);\n\t\tpeak = _mm_max_ps(peak, abs_new_work);\n\n\t\t/* Shift in the next point. */\n\t\tSHIFT_RIGHT_2PS(new_work, work);\n\t\tVECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3);\n\t\tpeak = _mm_max_ps(peak, abs_ps(intrp_samples));\n\n\t\tSHIFT_RIGHT_2PS(new_work, work);\n\t\tVECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3);\n\t\tpeak = _mm_max_ps(peak, abs_ps(intrp_samples));\n\n\t\tSHIFT_RIGHT_2PS(new_work, work);\n\t\tVECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3);\n\t\tpeak = _mm_max_ps(peak, abs_ps(intrp_samples));\n\n\t\tSHIFT_RIGHT_2PS(new_work, work);\n\t\tVECTOR_MATRIX_CROSS_PS(intrp_samples, work, m3, m1, p1, p3);\n\t\tpeak = _mm_max_ps(peak, abs_ps(intrp_samples));\n\t}\n\n\tfloat r;\n\thmax_ps(r, peak);\n\treturn r;\n}\n\n/* points contain the first four samples to calculate the sinc interpolation\n * over. They will have come from a previous iteration.\n */\nstatic float get_sample_peak(__m128 previous_samples, const float *samples, size_t nr_samples)\n{\n\t__m128 peak = previous_samples;\n\tfor (size_t i = 0; (i + 3) < nr_samples; i += 4) {\n\t\t__m128 new_work = _mm_load_ps(&samples[i]);\n\t\tpeak = _mm_max_ps(peak, abs_ps(new_work));\n\t}\n\n\tfloat r;\n\thmax_ps(r, peak);\n\treturn r;\n}\n\nstatic void volmeter_process_peak_last_samples(obs_volmeter_t *volmeter, int channel_nr, float *samples,\n\t\t\t\t\t       size_t nr_samples)\n{\n\t/* Take the last 4 samples that need to be used for the next peak\n\t * calculation. If there are less than 4 samples in total the new\n\t * samples shift out the old samples. */\n\n\tswitch (nr_samples) {\n\tcase 0:\n\t\tbreak;\n\tcase 1:\n\t\tvolmeter->prev_samples[channel_nr][0] = volmeter->prev_samples[channel_nr][1];\n\t\tvolmeter->prev_samples[channel_nr][1] = volmeter->prev_samples[channel_nr][2];\n\t\tvolmeter->prev_samples[channel_nr][2] = volmeter->prev_samples[channel_nr][3];\n\t\tvolmeter->prev_samples[channel_nr][3] = samples[nr_samples - 1];\n\t\tbreak;\n\tcase 2:\n\t\tvolmeter->prev_samples[channel_nr][0] = volmeter->prev_samples[channel_nr][2];\n\t\tvolmeter->prev_samples[channel_nr][1] = volmeter->prev_samples[channel_nr][3];\n\t\tvolmeter->prev_samples[channel_nr][2] = samples[nr_samples - 2];\n\t\tvolmeter->prev_samples[channel_nr][3] = samples[nr_samples - 1];\n\t\tbreak;\n\tcase 3:\n\t\tvolmeter->prev_samples[channel_nr][0] = volmeter->prev_samples[channel_nr][3];\n\t\tvolmeter->prev_samples[channel_nr][1] = samples[nr_samples - 3];\n\t\tvolmeter->prev_samples[channel_nr][2] = samples[nr_samples - 2];\n\t\tvolmeter->prev_samples[channel_nr][3] = samples[nr_samples - 1];\n\t\tbreak;\n\tdefault:\n\t\tvolmeter->prev_samples[channel_nr][0] = samples[nr_samples - 4];\n\t\tvolmeter->prev_samples[channel_nr][1] = samples[nr_samples - 3];\n\t\tvolmeter->prev_samples[channel_nr][2] = samples[nr_samples - 2];\n\t\tvolmeter->prev_samples[channel_nr][3] = samples[nr_samples - 1];\n\t}\n}\n\nstatic void volmeter_process_peak(obs_volmeter_t *volmeter, const struct audio_data *data, int nr_channels)\n{\n\tint nr_samples = data->frames;\n\tint channel_nr = 0;\n\tfor (int plane_nr = 0; channel_nr < nr_channels; plane_nr++) {\n\t\tfloat *samples = (float *)data->data[plane_nr];\n\t\tif (!samples) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (((uintptr_t)samples & 0xf) > 0) {\n\t\t\tprintf(\"Audio plane %i is not aligned %p skipping \"\n\t\t\t       \"peak volume measurement.\\n\",\n\t\t\t       plane_nr, samples);\n\t\t\tvolmeter->peak[channel_nr] = 1.0;\n\t\t\tchannel_nr++;\n\t\t\tcontinue;\n\t\t}\n\n\t\t/* volmeter->prev_samples may not be aligned to 16 bytes;\n\t\t * use unaligned load. */\n\t\t__m128 previous_samples = _mm_loadu_ps(volmeter->prev_samples[channel_nr]);\n\n\t\tfloat peak;\n\t\tswitch (volmeter->peak_meter_type) {\n\t\tcase TRUE_PEAK_METER:\n\t\t\tpeak = get_true_peak(previous_samples, samples, nr_samples);\n\t\t\tbreak;\n\n\t\tcase SAMPLE_PEAK_METER:\n\t\tdefault:\n\t\t\tpeak = get_sample_peak(previous_samples, samples, nr_samples);\n\t\t\tbreak;\n\t\t}\n\n\t\tvolmeter_process_peak_last_samples(volmeter, channel_nr, samples, nr_samples);\n\n\t\tvolmeter->peak[channel_nr] = peak;\n\n\t\tchannel_nr++;\n\t}\n\n\t/* Clear the peak of the channels that have not been handled. */\n\tfor (; channel_nr < MAX_AUDIO_CHANNELS; channel_nr++) {\n\t\tvolmeter->peak[channel_nr] = 0.0;\n\t}\n}\n\nstatic void volmeter_process_magnitude(obs_volmeter_t *volmeter, const struct audio_data *data, int nr_channels)\n{\n\tsize_t nr_samples = data->frames;\n\n\tint channel_nr = 0;\n\tfor (int plane_nr = 0; channel_nr < nr_channels; plane_nr++) {\n\t\tfloat *samples = (float *)data->data[plane_nr];\n\t\tif (!samples) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfloat sum = 0.0;\n\t\tfor (size_t i = 0; i < nr_samples; i++) {\n\t\t\tfloat sample = samples[i];\n\t\t\tsum += sample * sample;\n\t\t}\n\t\tvolmeter->magnitude[channel_nr] = sqrtf(sum / nr_samples);\n\n\t\tchannel_nr++;\n\t}\n}\n\nstatic void volmeter_process_audio_data(obs_volmeter_t *volmeter, const struct audio_data *data)\n{\n\tint nr_channels = get_nr_channels_from_audio_data(data);\n\n\tvolmeter_process_peak(volmeter, data, nr_channels);\n\tvolmeter_process_magnitude(volmeter, data, nr_channels);\n}\n\nstatic void volmeter_source_data_received(void *vptr, obs_source_t *source, const struct audio_data *data, bool muted)\n{\n\tstruct obs_volmeter *volmeter = (struct obs_volmeter *)vptr;\n\tfloat mul;\n\tfloat magnitude[MAX_AUDIO_CHANNELS];\n\tfloat peak[MAX_AUDIO_CHANNELS];\n\tfloat input_peak[MAX_AUDIO_CHANNELS];\n\n\tpthread_mutex_lock(&volmeter->mutex);\n\n\tvolmeter_process_audio_data(volmeter, data);\n\n\t// Adjust magnitude/peak based on the volume level set by the user.\n\t// And convert to dB.\n\tmul = muted && !obs_source_muted(source) ? 0.0f : db_to_mul(volmeter->cur_db);\n\tfor (int channel_nr = 0; channel_nr < MAX_AUDIO_CHANNELS; channel_nr++) {\n\t\tmagnitude[channel_nr] = mul_to_db(volmeter->magnitude[channel_nr] * mul);\n\t\tpeak[channel_nr] = mul_to_db(volmeter->peak[channel_nr] * mul);\n\n\t\t/* The input-peak is NOT adjusted with volume, so that the user\n\t\t * can check the input-gain. */\n\t\tinput_peak[channel_nr] = mul_to_db(volmeter->peak[channel_nr]);\n\t}\n\n\tpthread_mutex_unlock(&volmeter->mutex);\n\n\tsignal_levels_updated(volmeter, magnitude, peak, input_peak);\n}\n\nobs_fader_t *obs_fader_create(enum obs_fader_type type)\n{\n\tstruct obs_fader *fader = bzalloc(sizeof(struct obs_fader));\n\tif (!fader)\n\t\treturn NULL;\n\n\tpthread_mutex_init_value(&fader->mutex);\n\tpthread_mutex_init_value(&fader->callback_mutex);\n\tif (pthread_mutex_init(&fader->mutex, NULL) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init(&fader->callback_mutex, NULL) != 0)\n\t\tgoto fail;\n\n\tswitch (type) {\n\tcase OBS_FADER_CUBIC:\n\t\tfader->def_to_db = cubic_def_to_db;\n\t\tfader->db_to_def = cubic_db_to_def;\n\t\tfader->max_db = 0.0f;\n\t\tfader->min_db = -INFINITY;\n\t\tbreak;\n\tcase OBS_FADER_IEC:\n\t\tfader->def_to_db = iec_def_to_db;\n\t\tfader->db_to_def = iec_db_to_def;\n\t\tfader->max_db = 0.0f;\n\t\tfader->min_db = -INFINITY;\n\t\tbreak;\n\tcase OBS_FADER_LOG:\n\t\tfader->def_to_db = log_def_to_db;\n\t\tfader->db_to_def = log_db_to_def;\n\t\tfader->max_db = 0.0f;\n\t\tfader->min_db = -96.0f;\n\t\tbreak;\n\tdefault:\n\t\tgoto fail;\n\t\tbreak;\n\t}\n\tfader->type = type;\n\n\treturn fader;\nfail:\n\tobs_fader_destroy(fader);\n\treturn NULL;\n}\n\nvoid obs_fader_destroy(obs_fader_t *fader)\n{\n\tif (!fader)\n\t\treturn;\n\n\tobs_fader_detach_source(fader);\n\tda_free(fader->callbacks);\n\tpthread_mutex_destroy(&fader->callback_mutex);\n\tpthread_mutex_destroy(&fader->mutex);\n\n\tbfree(fader);\n}\n\nbool obs_fader_set_db(obs_fader_t *fader, const float db)\n{\n\tif (!fader)\n\t\treturn false;\n\n\tpthread_mutex_lock(&fader->mutex);\n\n\tbool clamped = false;\n\tfader->cur_db = db;\n\n\tif (fader->cur_db > fader->max_db) {\n\t\tfader->cur_db = fader->max_db;\n\t\tclamped = true;\n\t}\n\tif (fader->cur_db < fader->min_db) {\n\t\tfader->cur_db = -INFINITY;\n\t\tclamped = true;\n\t}\n\n\tfader->ignore_next_signal = true;\n\tobs_source_t *src = fader->source;\n\tconst float mul = db_to_mul(fader->cur_db);\n\n\tpthread_mutex_unlock(&fader->mutex);\n\n\tif (src)\n\t\tobs_source_set_volume(src, mul);\n\n\treturn !clamped;\n}\n\nfloat obs_fader_get_db(obs_fader_t *fader)\n{\n\tif (!fader)\n\t\treturn 0.0f;\n\n\tpthread_mutex_lock(&fader->mutex);\n\tconst float db = fader->cur_db;\n\tpthread_mutex_unlock(&fader->mutex);\n\n\treturn db;\n}\n\nbool obs_fader_set_deflection(obs_fader_t *fader, const float def)\n{\n\tif (!fader)\n\t\treturn false;\n\n\treturn obs_fader_set_db(fader, fader->def_to_db(def));\n}\n\nfloat obs_fader_get_deflection(obs_fader_t *fader)\n{\n\tif (!fader)\n\t\treturn 0.0f;\n\n\tpthread_mutex_lock(&fader->mutex);\n\tconst float def = fader->db_to_def(fader->cur_db);\n\tpthread_mutex_unlock(&fader->mutex);\n\n\treturn def;\n}\n\nbool obs_fader_set_mul(obs_fader_t *fader, const float mul)\n{\n\tif (!fader)\n\t\treturn false;\n\n\treturn obs_fader_set_db(fader, mul_to_db(mul));\n}\n\nfloat obs_fader_get_mul(obs_fader_t *fader)\n{\n\tif (!fader)\n\t\treturn 0.0f;\n\n\tpthread_mutex_lock(&fader->mutex);\n\tconst float mul = db_to_mul(fader->cur_db);\n\tpthread_mutex_unlock(&fader->mutex);\n\n\treturn mul;\n}\n\nbool obs_fader_attach_source(obs_fader_t *fader, obs_source_t *source)\n{\n\tsignal_handler_t *sh;\n\tfloat vol;\n\n\tif (!fader || !source)\n\t\treturn false;\n\n\tobs_fader_detach_source(fader);\n\n\tsh = obs_source_get_signal_handler(source);\n\tsignal_handler_connect(sh, \"volume\", fader_source_volume_changed, fader);\n\tsignal_handler_connect(sh, \"destroy\", fader_source_destroyed, fader);\n\tvol = obs_source_get_volume(source);\n\n\tpthread_mutex_lock(&fader->mutex);\n\n\tfader->source = source;\n\tfader->cur_db = mul_to_db(vol);\n\n\tpthread_mutex_unlock(&fader->mutex);\n\n\treturn true;\n}\n\nvoid obs_fader_detach_source(obs_fader_t *fader)\n{\n\tsignal_handler_t *sh;\n\tobs_source_t *source;\n\n\tif (!fader)\n\t\treturn;\n\n\tpthread_mutex_lock(&fader->mutex);\n\tsource = fader->source;\n\tfader->source = NULL;\n\tpthread_mutex_unlock(&fader->mutex);\n\n\tif (!source)\n\t\treturn;\n\n\tsh = obs_source_get_signal_handler(source);\n\tsignal_handler_disconnect(sh, \"volume\", fader_source_volume_changed, fader);\n\tsignal_handler_disconnect(sh, \"destroy\", fader_source_destroyed, fader);\n}\n\nvoid obs_fader_add_callback(obs_fader_t *fader, obs_fader_changed_t callback, void *param)\n{\n\tstruct fader_cb cb = {callback, param};\n\n\tif (!obs_ptr_valid(fader, \"obs_fader_add_callback\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&fader->callback_mutex);\n\tda_push_back(fader->callbacks, &cb);\n\tpthread_mutex_unlock(&fader->callback_mutex);\n}\n\nvoid obs_fader_remove_callback(obs_fader_t *fader, obs_fader_changed_t callback, void *param)\n{\n\tstruct fader_cb cb = {callback, param};\n\n\tif (!obs_ptr_valid(fader, \"obs_fader_remove_callback\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&fader->callback_mutex);\n\tda_erase_item(fader->callbacks, &cb);\n\tpthread_mutex_unlock(&fader->callback_mutex);\n}\n\nobs_volmeter_t *obs_volmeter_create(enum obs_fader_type type)\n{\n\tstruct obs_volmeter *volmeter = bzalloc(sizeof(struct obs_volmeter));\n\tif (!volmeter)\n\t\treturn NULL;\n\n\tpthread_mutex_init_value(&volmeter->mutex);\n\tpthread_mutex_init_value(&volmeter->callback_mutex);\n\tif (pthread_mutex_init(&volmeter->mutex, NULL) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init(&volmeter->callback_mutex, NULL) != 0)\n\t\tgoto fail;\n\n\tvolmeter->type = type;\n\n\treturn volmeter;\nfail:\n\tobs_volmeter_destroy(volmeter);\n\treturn NULL;\n}\n\nvoid obs_volmeter_destroy(obs_volmeter_t *volmeter)\n{\n\tif (!volmeter)\n\t\treturn;\n\n\tobs_volmeter_detach_source(volmeter);\n\tda_free(volmeter->callbacks);\n\tpthread_mutex_destroy(&volmeter->callback_mutex);\n\tpthread_mutex_destroy(&volmeter->mutex);\n\n\tbfree(volmeter);\n}\n\nbool obs_volmeter_attach_source(obs_volmeter_t *volmeter, obs_source_t *source)\n{\n\tsignal_handler_t *sh;\n\tfloat vol;\n\n\tif (!volmeter || !source)\n\t\treturn false;\n\n\tobs_volmeter_detach_source(volmeter);\n\n\tsh = obs_source_get_signal_handler(source);\n\tsignal_handler_connect(sh, \"volume\", volmeter_source_volume_changed, volmeter);\n\tsignal_handler_connect(sh, \"destroy\", volmeter_source_destroyed, volmeter);\n\tobs_source_add_audio_capture_callback(source, volmeter_source_data_received, volmeter);\n\tvol = obs_source_get_volume(source);\n\n\tpthread_mutex_lock(&volmeter->mutex);\n\n\tvolmeter->source = source;\n\tvolmeter->cur_db = mul_to_db(vol);\n\n\tpthread_mutex_unlock(&volmeter->mutex);\n\n\treturn true;\n}\n\nvoid obs_volmeter_detach_source(obs_volmeter_t *volmeter)\n{\n\tsignal_handler_t *sh;\n\tobs_source_t *source;\n\n\tif (!volmeter)\n\t\treturn;\n\n\tpthread_mutex_lock(&volmeter->mutex);\n\tsource = volmeter->source;\n\tvolmeter->source = NULL;\n\tpthread_mutex_unlock(&volmeter->mutex);\n\n\tif (!source)\n\t\treturn;\n\n\tsh = obs_source_get_signal_handler(source);\n\tsignal_handler_disconnect(sh, \"volume\", volmeter_source_volume_changed, volmeter);\n\tsignal_handler_disconnect(sh, \"destroy\", volmeter_source_destroyed, volmeter);\n\tobs_source_remove_audio_capture_callback(source, volmeter_source_data_received, volmeter);\n}\n\nvoid obs_volmeter_set_peak_meter_type(obs_volmeter_t *volmeter, enum obs_peak_meter_type peak_meter_type)\n{\n\tpthread_mutex_lock(&volmeter->mutex);\n\tvolmeter->peak_meter_type = peak_meter_type;\n\tpthread_mutex_unlock(&volmeter->mutex);\n}\n\nint obs_volmeter_get_nr_channels(obs_volmeter_t *volmeter)\n{\n\tint source_nr_audio_channels;\n\tint obs_nr_audio_channels;\n\n\tif (volmeter->source) {\n\t\tsource_nr_audio_channels = get_audio_channels(volmeter->source->sample_info.speakers);\n\t} else {\n\t\tsource_nr_audio_channels = 0;\n\t}\n\n\tstruct obs_audio_info audio_info;\n\tif (obs_get_audio_info(&audio_info)) {\n\t\tobs_nr_audio_channels = get_audio_channels(audio_info.speakers);\n\t} else {\n\t\tobs_nr_audio_channels = 2;\n\t}\n\n\treturn CLAMP(source_nr_audio_channels, 0, obs_nr_audio_channels);\n}\n\nvoid obs_volmeter_add_callback(obs_volmeter_t *volmeter, obs_volmeter_updated_t callback, void *param)\n{\n\tstruct meter_cb cb = {callback, param};\n\n\tif (!obs_ptr_valid(volmeter, \"obs_volmeter_add_callback\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&volmeter->callback_mutex);\n\tda_push_back(volmeter->callbacks, &cb);\n\tpthread_mutex_unlock(&volmeter->callback_mutex);\n}\n\nvoid obs_volmeter_remove_callback(obs_volmeter_t *volmeter, obs_volmeter_updated_t callback, void *param)\n{\n\tstruct meter_cb cb = {callback, param};\n\n\tif (!obs_ptr_valid(volmeter, \"obs_volmeter_remove_callback\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&volmeter->callback_mutex);\n\tda_erase_item(volmeter->callbacks, &cb);\n\tpthread_mutex_unlock(&volmeter->callback_mutex);\n}\n\nfloat obs_mul_to_db(float mul)\n{\n\treturn mul_to_db(mul);\n}\n\nfloat obs_db_to_mul(float db)\n{\n\treturn db_to_mul(db);\n}\n\nobs_fader_conversion_t obs_fader_db_to_def(obs_fader_t *fader)\n{\n\treturn fader->db_to_def;\n}\n"
  },
  {
    "path": "libobs/obs-audio-controls.h",
    "content": "/*\nCopyright (C) 2014 by Leonhard Oelke <leonhard@in-verted.de>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include \"obs.h\"\n\n/**\n * @file\n * @brief header for audio controls\n *\n * @brief Audio controls for use in GUIs\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @brief Fader types\n */\nenum obs_fader_type {\n\t/**\n\t * @brief A simple cubic fader for controlling audio levels\n\t *\n\t * This is a very common type of software fader since it yields good\n\t * results while being quite performant.\n\t * The input value is mapped to mul values with the simple formula x^3.\n\t */\n\tOBS_FADER_CUBIC,\n\t/**\n\t * @brief A fader compliant to IEC 60-268-18\n\t *\n\t * This type of fader has several segments with different slopes that\n\t * map deflection linearly to dB values. The segments are defined as\n\t * in the following table:\n\t *\n\t@code\n\tDeflection           | Volume\n\t------------------------------------------\n\t[ 100   %, 75   % ]  | [   0 dB,   -9 dB ]\n\t[  75   %, 50   % ]  | [  -9 dB,  -20 dB ]\n\t[  50   %, 30   % ]  | [ -20 dB,  -30 dB ]\n\t[  30   %, 15   % ]  | [ -30 dB,  -40 dB ]\n\t[  15   %,  7.5 % ]  | [ -40 dB,  -50 dB ]\n\t[   7.5 %,  2.5 % ]  | [ -50 dB,  -60 dB ]\n\t[   2.5 %,  0   % ]  | [ -60 dB, -inf dB ]\n\t@endcode\n\t */\n\tOBS_FADER_IEC,\n\t/**\n\t * @brief Logarithmic fader\n\t */\n\tOBS_FADER_LOG\n};\n\n/**\n * @brief Peak meter types\n */\nenum obs_peak_meter_type {\n\t/**\n\t * @brief A simple peak meter measuring the maximum of all samples.\n\t *\n\t * This was a very common type of peak meter used for audio, but\n\t * is not very accurate with regards to further audio processing.\n\t */\n\tSAMPLE_PEAK_METER,\n\n\t/**\n\t * @brief An accurate peak meter measure the maximum of inter-samples.\n\t *\n\t * This meter is more computational intensive due to 4x oversampling\n\t * to determine the true peak to an accuracy of +/- 0.5 dB.\n\t */\n\tTRUE_PEAK_METER\n};\n\n/**\n * @brief Create a fader\n * @param type the type of the fader\n * @return pointer to the fader object\n *\n * A fader object is used to map input values from a gui element to dB and\n * subsequently multiplier values used by libobs to mix audio.\n * The current \"position\" of the fader is internally stored as dB value.\n */\nEXPORT obs_fader_t *obs_fader_create(enum obs_fader_type type);\n\n/**\n * @brief Destroy a fader\n * @param fader pointer to the fader object\n *\n * Destroy the fader and free all related data\n */\nEXPORT void obs_fader_destroy(obs_fader_t *fader);\n\n/**\n * @brief Set the fader dB value\n * @param fader pointer to the fader object\n * @param db new dB value\n * @return true if value was set without clamping\n */\nEXPORT bool obs_fader_set_db(obs_fader_t *fader, const float db);\n\n/**\n * @brief Get the current fader dB value\n * @param fader pointer to the fader object\n * @return current fader dB value\n */\nEXPORT float obs_fader_get_db(obs_fader_t *fader);\n\n/**\n * @brief Set the fader value from deflection\n * @param fader pointer to the fader object\n * @param def new deflection\n * @return true if value was set without clamping\n *\n * This sets the new fader value from the supplied deflection, in case the\n * resulting value was clamped due to limits this function will return false.\n * The deflection is typically in the range [0.0, 1.0] but may be higher in\n * order to provide some amplification. In order for this to work the high dB\n * limit has to be set.\n */\nEXPORT bool obs_fader_set_deflection(obs_fader_t *fader, const float def);\n\n/**\n * @brief Get the current fader deflection\n * @param fader pointer to the fader object\n * @return current fader deflection\n */\nEXPORT float obs_fader_get_deflection(obs_fader_t *fader);\n\n/**\n * @brief Set the fader value from multiplier\n * @param fader pointer to the fader object\n * @return true if the value was set without clamping\n */\nEXPORT bool obs_fader_set_mul(obs_fader_t *fader, const float mul);\n\n/**\n * @brief Get the current fader multiplier value\n * @param fader pointer to the fader object\n * @return current fader multiplier\n */\nEXPORT float obs_fader_get_mul(obs_fader_t *fader);\n\n/**\n * @brief Attach the fader to a source\n * @param fader pointer to the fader object\n * @param source pointer to the source object\n * @return true on success\n *\n * When the fader is attached to a source it will automatically sync it's state\n * to the volume of the source.\n */\nEXPORT bool obs_fader_attach_source(obs_fader_t *fader, obs_source_t *source);\n\n/**\n * @brief Detach the fader from the currently attached source\n * @param fader pointer to the fader object\n */\nEXPORT void obs_fader_detach_source(obs_fader_t *fader);\n\ntypedef void (*obs_fader_changed_t)(void *param, float db);\n\nEXPORT void obs_fader_add_callback(obs_fader_t *fader, obs_fader_changed_t callback, void *param);\nEXPORT void obs_fader_remove_callback(obs_fader_t *fader, obs_fader_changed_t callback, void *param);\n\n/**\n * @brief Create a volume meter\n * @param type the mapping type to use for the volume meter\n * @return pointer to the volume meter object\n *\n * A volume meter object is used to prepare the sound levels reported by audio\n * sources for display in a GUI.\n * It will automatically take source volume into account and map the levels\n * to a range [0.0f, 1.0f].\n */\nEXPORT obs_volmeter_t *obs_volmeter_create(enum obs_fader_type type);\n\n/**\n * @brief Destroy a volume meter\n * @param volmeter pointer to the volmeter object\n *\n * Destroy the volume meter and free all related data\n */\nEXPORT void obs_volmeter_destroy(obs_volmeter_t *volmeter);\n\n/**\n * @brief Attach the volume meter to a source\n * @param volmeter pointer to the volume meter object\n * @param source pointer to the source object\n * @return true on success\n *\n * When the volume meter is attached to a source it will start to listen to\n * volume updates on the source and after preparing the data emit its own\n * signal.\n */\nEXPORT bool obs_volmeter_attach_source(obs_volmeter_t *volmeter, obs_source_t *source);\n\n/**\n * @brief Detach the volume meter from the currently attached source\n * @param volmeter pointer to the volume meter object\n */\nEXPORT void obs_volmeter_detach_source(obs_volmeter_t *volmeter);\n\n/**\n * @brief Set the peak meter type for the volume meter\n * @param volmeter pointer to the volume meter object\n * @param peak_meter_type set if true-peak needs to be measured.\n */\nEXPORT void obs_volmeter_set_peak_meter_type(obs_volmeter_t *volmeter, enum obs_peak_meter_type peak_meter_type);\n\n/**\n * @brief Get the number of channels which are configured for this source.\n * @param volmeter pointer to the volume meter object\n */\nEXPORT int obs_volmeter_get_nr_channels(obs_volmeter_t *volmeter);\n\ntypedef void (*obs_volmeter_updated_t)(void *param, const float magnitude[MAX_AUDIO_CHANNELS],\n\t\t\t\t       const float peak[MAX_AUDIO_CHANNELS],\n\t\t\t\t       const float input_peak[MAX_AUDIO_CHANNELS]);\n\nEXPORT void obs_volmeter_add_callback(obs_volmeter_t *volmeter, obs_volmeter_updated_t callback, void *param);\nEXPORT void obs_volmeter_remove_callback(obs_volmeter_t *volmeter, obs_volmeter_updated_t callback, void *param);\n\nEXPORT float obs_mul_to_db(float mul);\nEXPORT float obs_db_to_mul(float db);\n\ntypedef float (*obs_fader_conversion_t)(const float val);\n\nEXPORT obs_fader_conversion_t obs_fader_db_to_def(obs_fader_t *fader);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-audio.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <inttypes.h>\n#include \"obs-internal.h\"\n#include \"util/util_uint64.h\"\n\nstruct ts_info {\n\tuint64_t start;\n\tuint64_t end;\n};\n\n#define DEBUG_AUDIO 0\n#define DEBUG_LAGGED_AUDIO 0\n\nstatic void push_audio_tree(obs_source_t *parent, obs_source_t *source, void *p)\n{\n\tstruct obs_core_audio *audio = p;\n\n\tif (da_find(audio->render_order, &source, 0) == DARRAY_INVALID) {\n\t\tobs_source_t *s = obs_source_get_ref(source);\n\t\tif (s) {\n\t\t\tda_push_back(audio->render_order, &s);\n\t\t\ts->audio_is_duplicated = false;\n\t\t}\n\t}\n\n\tUNUSED_PARAMETER(parent);\n}\n\nstatic inline bool is_individual_audio_source(obs_source_t *source)\n{\n\treturn source->info.type == OBS_SOURCE_TYPE_INPUT && (source->info.output_flags & OBS_SOURCE_AUDIO) &&\n\t       !(source->info.output_flags & OBS_SOURCE_COMPOSITE);\n}\n\n/*\n * This version of push_audio_tree checks whether any source is an Audio Output Capture source ('Desktop Audio',\n * 'wasapi_output_capture' on Windows, 'pulse_output_capture' on Linux, 'coreaudio_output_capture' on macOS), & if the\n * corresponding device is the monitoring device. It then sets the core audio bool 'prevent_monitoring_duplication' to\n * true, which will silence all monitored sources (unless the Audio Output Capture source is muted).\n * Moreover, it has the purpose of detecting sources which appear several times in the audio tree. They are then tagged\n * as such to avoid their mixing in scenes and transitions and mixed directly as root_nodes.\n */\nstatic void push_audio_tree2(obs_source_t *parent, obs_source_t *source, void *p)\n{\n\tif (obs_source_removed(source))\n\t\treturn;\n\n\tstruct obs_core_audio *audio = p;\n\tsize_t idx = da_find(audio->render_order, &source, 0);\n\n\tif (idx == DARRAY_INVALID) {\n\t\t/* First time we see this source → add to render order */\n\t\tobs_source_t *s = obs_source_get_ref(source);\n\t\tif (s) {\n\t\t\tda_push_back(audio->render_order, &s);\n\t\t\ts->audio_is_duplicated = false;\n\t\t}\n\t} else {\n\t\t/* Source already present in tree → mark as duplicated if applicable */\n\t\tobs_source_t *s = audio->render_order.array[idx];\n\t\tif (is_individual_audio_source(s) && !s->audio_is_duplicated) {\n\t\t\tda_push_back(audio->root_nodes, &source);\n\t\t\ts->audio_is_duplicated = true;\n\t\t}\n\t}\n\tUNUSED_PARAMETER(parent);\n}\n\nstatic inline size_t convert_time_to_frames(size_t sample_rate, uint64_t t)\n{\n\treturn (size_t)util_mul_div64(t, sample_rate, 1000000000ULL);\n}\n\nstatic inline void mix_audio(struct audio_output_data *mixes, obs_source_t *source, size_t channels, size_t sample_rate,\n\t\t\t     struct ts_info *ts)\n{\n\tsize_t total_floats = AUDIO_OUTPUT_FRAMES;\n\tsize_t start_point = 0;\n\n\tif (source->audio_ts < ts->start || ts->end <= source->audio_ts)\n\t\treturn;\n\n\tif (source->audio_ts != ts->start) {\n\t\tstart_point = convert_time_to_frames(sample_rate, source->audio_ts - ts->start);\n\t\tif (start_point == AUDIO_OUTPUT_FRAMES)\n\t\t\treturn;\n\n\t\ttotal_floats -= start_point;\n\t}\n\n\tfor (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {\n\t\tfor (size_t ch = 0; ch < channels; ch++) {\n\t\t\tregister float *mix = mixes[mix_idx].data[ch];\n\t\t\tregister float *aud = source->audio_output_buf[mix_idx][ch];\n\t\t\tregister float *end;\n\n\t\t\tmix += start_point;\n\t\t\tend = aud + total_floats;\n\n\t\t\twhile (aud < end)\n\t\t\t\t*(mix++) += *(aud++);\n\t\t}\n\t}\n}\n\nstatic bool ignore_audio(obs_source_t *source, size_t channels, size_t sample_rate, uint64_t start_ts)\n{\n\tsize_t num_floats = source->audio_input_buf[0].size / sizeof(float);\n\tconst char *name = obs_source_get_name(source);\n\n\tif (!source->audio_ts && num_floats) {\n#if DEBUG_LAGGED_AUDIO == 1\n\t\tblog(LOG_DEBUG, \"[src: %s] no timestamp, but audio available?\", name);\n#endif\n\t\tfor (size_t ch = 0; ch < channels; ch++)\n\t\t\tdeque_pop_front(&source->audio_input_buf[ch], NULL, source->audio_input_buf[0].size);\n\t\tsource->last_audio_input_buf_size = 0;\n\t\treturn false;\n\t}\n\n\tif (num_floats) {\n\t\t/* round up the number of samples to drop */\n\t\tsize_t drop = (size_t)util_mul_div64(start_ts - source->audio_ts - 1, sample_rate, 1000000000ULL) + 1;\n\t\tif (drop > num_floats)\n\t\t\tdrop = num_floats;\n\n#if DEBUG_LAGGED_AUDIO == 1\n\t\tblog(LOG_DEBUG, \"[src: %s] ignored %\" PRIu64 \"/%\" PRIu64 \" samples\", name, (uint64_t)drop,\n\t\t     (uint64_t)num_floats);\n#endif\n\t\tfor (size_t ch = 0; ch < channels; ch++)\n\t\t\tdeque_pop_front(&source->audio_input_buf[ch], NULL, drop * sizeof(float));\n\n\t\tsource->last_audio_input_buf_size = 0;\n\t\tsource->audio_ts += util_mul_div64(drop, 1000000000ULL, sample_rate);\n\t\tblog(LOG_DEBUG, \"[src: %s] ts lag after ignoring: %\" PRIu64, name, start_ts - source->audio_ts);\n\n\t\t/* rounding error, adjust */\n\t\tif (source->audio_ts == (start_ts - 1))\n\t\t\tsource->audio_ts = start_ts;\n\n\t\t/* source is back in sync */\n\t\tif (source->audio_ts >= start_ts)\n\t\t\treturn true;\n\t} else {\n#if DEBUG_LAGGED_AUDIO == 1\n\t\tblog(LOG_DEBUG, \"[src: %s] no samples to ignore! ts = %\" PRIu64, name, source->audio_ts);\n#endif\n\t}\n\n\tif (!source->audio_pending || num_floats) {\n\t\tblog(LOG_WARNING,\n\t\t     \"Source %s audio is lagging (over by %.02f ms) \"\n\t\t     \"at max audio buffering. Restarting source audio.\",\n\t\t     name, (start_ts - source->audio_ts) / 1000000.);\n\t}\n\n\tsource->audio_pending = true;\n\tsource->audio_ts = 0;\n\t/* tell the timestamp adjustment code in source_output_audio_data to\n\t * reset everything, and hopefully fix the timestamps */\n\tsource->timing_set = false;\n\treturn false;\n}\n\nstatic bool discard_if_stopped(obs_source_t *source, size_t channels)\n{\n\tsize_t last_size;\n\tsize_t size;\n\n\tlast_size = source->last_audio_input_buf_size;\n\tsize = source->audio_input_buf[0].size;\n\n\tif (!size)\n\t\treturn false;\n\n\t/* if perpetually pending data, it means the audio has stopped,\n\t * so clear the audio data */\n\tif (last_size == size) {\n\t\tif (!source->pending_stop) {\n\t\t\tsource->pending_stop = true;\n#if DEBUG_AUDIO == 1\n\t\t\tblog(LOG_DEBUG, \"doing pending stop trick: '%s'\", source->context.name);\n#endif\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (size_t ch = 0; ch < channels; ch++)\n\t\t\tdeque_pop_front(&source->audio_input_buf[ch], NULL, source->audio_input_buf[ch].size);\n\n\t\tsource->pending_stop = false;\n\t\tsource->audio_ts = 0;\n\t\tsource->last_audio_input_buf_size = 0;\n#if DEBUG_AUDIO == 1\n\t\tblog(LOG_DEBUG, \"source audio data appears to have \"\n\t\t\t\t\"stopped, clearing\");\n#endif\n\t\treturn true;\n\t} else {\n\t\tsource->last_audio_input_buf_size = size;\n\t\treturn false;\n\t}\n}\n\n#define MAX_AUDIO_SIZE (AUDIO_OUTPUT_FRAMES * sizeof(float))\n\nstatic inline void discard_audio(struct obs_core_audio *audio, obs_source_t *source, size_t channels,\n\t\t\t\t size_t sample_rate, struct ts_info *ts)\n{\n\tsize_t total_floats = AUDIO_OUTPUT_FRAMES;\n\tsize_t size;\n\t/* debug assert only */\n\tUNUSED_PARAMETER(audio);\n\n#if DEBUG_AUDIO == 1\n\tbool is_audio_source = source->info.output_flags & OBS_SOURCE_AUDIO;\n#endif\n\n\tif (source->info.audio_render) {\n\t\tsource->audio_ts = 0;\n\t\treturn;\n\t}\n\n\tif (ts->end <= source->audio_ts) {\n#if DEBUG_AUDIO == 1\n\t\tblog(LOG_DEBUG,\n\t\t     \"can't discard, source \"\n\t\t     \"timestamp (%\" PRIu64 \") >= \"\n\t\t     \"end timestamp (%\" PRIu64 \")\",\n\t\t     source->audio_ts, ts->end);\n#endif\n\t\treturn;\n\t}\n\n\tif (source->audio_ts < (ts->start - 1)) {\n\t\tif (source->audio_pending && source->audio_input_buf[0].size < MAX_AUDIO_SIZE &&\n\t\t    discard_if_stopped(source, channels))\n\t\t\treturn;\n\n#if DEBUG_AUDIO == 1\n\t\tif (is_audio_source) {\n\t\t\tblog(LOG_DEBUG,\n\t\t\t     \"can't discard, source \"\n\t\t\t     \"timestamp (%\" PRIu64 \") < \"\n\t\t\t     \"start timestamp (%\" PRIu64 \")\",\n\t\t\t     source->audio_ts, ts->start);\n\t\t}\n\n\t\t/* ignore_audio should have already run and marked this source\n\t\t * pending, unless we *just* added buffering */\n\t\tassert(audio->total_buffering_ticks < audio->max_buffering_ticks || source->audio_pending ||\n\t\t       !source->audio_ts || audio->buffering_wait_ticks);\n#endif\n\t\treturn;\n\t}\n\n\tif (source->audio_ts != ts->start && source->audio_ts != (ts->start - 1)) {\n\t\tsize_t start_point = convert_time_to_frames(sample_rate, source->audio_ts - ts->start);\n\t\tif (start_point == AUDIO_OUTPUT_FRAMES) {\n#if DEBUG_AUDIO == 1\n\t\t\tif (is_audio_source)\n\t\t\t\tblog(LOG_DEBUG, \"can't discard, start point is \"\n\t\t\t\t\t\t\"at audio frame count\");\n#endif\n\t\t\treturn;\n\t\t}\n\n\t\ttotal_floats -= start_point;\n\t}\n\n\tsize = total_floats * sizeof(float);\n\n\tif (source->audio_input_buf[0].size < size) {\n\t\tif (discard_if_stopped(source, channels))\n\t\t\treturn;\n\n#if DEBUG_AUDIO == 1\n\t\tif (is_audio_source)\n\t\t\tblog(LOG_DEBUG, \"can't discard, data still pending\");\n#endif\n\t\tsource->audio_ts = ts->end;\n\t\treturn;\n\t}\n\n\tfor (size_t ch = 0; ch < channels; ch++)\n\t\tdeque_pop_front(&source->audio_input_buf[ch], NULL, size);\n\n\tsource->last_audio_input_buf_size = 0;\n\n#if DEBUG_AUDIO == 1\n\tif (is_audio_source)\n\t\tblog(LOG_DEBUG, \"audio discarded, new ts: %\" PRIu64, ts->end);\n#endif\n\n\tsource->pending_stop = false;\n\tsource->audio_ts = ts->end;\n}\n\nstatic inline bool audio_buffering_maxed(struct obs_core_audio *audio)\n{\n\treturn audio->total_buffering_ticks == audio->max_buffering_ticks;\n}\n\nstatic void set_fixed_audio_buffering(struct obs_core_audio *audio, size_t sample_rate, struct ts_info *ts)\n{\n\tstruct ts_info new_ts;\n\tsize_t total_ms;\n\tint ticks;\n\n\tif (audio_buffering_maxed(audio))\n\t\treturn;\n\n\tif (!audio->buffering_wait_ticks)\n\t\taudio->buffered_ts = ts->start;\n\n\tticks = audio->max_buffering_ticks - audio->total_buffering_ticks;\n\taudio->total_buffering_ticks += ticks;\n\n\ttotal_ms = audio->total_buffering_ticks * AUDIO_OUTPUT_FRAMES * 1000 / sample_rate;\n\n\tblog(LOG_INFO,\n\t     \"Enabling fixed audio buffering, total \"\n\t     \"audio buffering is now %d milliseconds\",\n\t     (int)total_ms);\n\n\tnew_ts.start =\n\t\taudio->buffered_ts - audio_frames_to_ns(sample_rate, audio->buffering_wait_ticks * AUDIO_OUTPUT_FRAMES);\n\n\twhile (ticks--) {\n\t\tconst uint64_t cur_ticks = ++audio->buffering_wait_ticks;\n\n\t\tnew_ts.end = new_ts.start;\n\t\tnew_ts.start = audio->buffered_ts - audio_frames_to_ns(sample_rate, cur_ticks * AUDIO_OUTPUT_FRAMES);\n\n#if DEBUG_AUDIO == 1\n\t\tblog(LOG_DEBUG, \"add buffered ts: %\" PRIu64 \"-%\" PRIu64, new_ts.start, new_ts.end);\n#endif\n\n\t\tdeque_push_front(&audio->buffered_timestamps, &new_ts, sizeof(new_ts));\n\t}\n\n\t*ts = new_ts;\n}\n\nstatic void add_audio_buffering(struct obs_core_audio *audio, size_t sample_rate, struct ts_info *ts, uint64_t min_ts,\n\t\t\t\tconst char *buffering_name)\n{\n\tstruct ts_info new_ts;\n\tuint64_t offset;\n\tuint64_t frames;\n\tsize_t total_ms;\n\tsize_t ms;\n\tint ticks;\n\n\tif (audio_buffering_maxed(audio))\n\t\treturn;\n\n\tif (!audio->buffering_wait_ticks)\n\t\taudio->buffered_ts = ts->start;\n\n\toffset = ts->start - min_ts;\n\tframes = ns_to_audio_frames(sample_rate, offset);\n\tticks = (int)((frames + AUDIO_OUTPUT_FRAMES - 1) / AUDIO_OUTPUT_FRAMES);\n\n\taudio->total_buffering_ticks += ticks;\n\n\tif (audio->total_buffering_ticks >= audio->max_buffering_ticks) {\n\t\tticks -= audio->total_buffering_ticks - audio->max_buffering_ticks;\n\t\taudio->total_buffering_ticks = audio->max_buffering_ticks;\n\t\tblog(LOG_WARNING, \"Max audio buffering reached!\");\n\t}\n\n\tms = ticks * AUDIO_OUTPUT_FRAMES * 1000 / sample_rate;\n\ttotal_ms = audio->total_buffering_ticks * AUDIO_OUTPUT_FRAMES * 1000 / sample_rate;\n\n\tblog(LOG_INFO,\n\t     \"adding %d milliseconds of audio buffering, total \"\n\t     \"audio buffering is now %d milliseconds\"\n\t     \" (source: %s)\\n\",\n\t     (int)ms, (int)total_ms, buffering_name);\n#if DEBUG_AUDIO == 1\n\tblog(LOG_DEBUG,\n\t     \"min_ts (%\" PRIu64 \") < start timestamp \"\n\t     \"(%\" PRIu64 \")\",\n\t     min_ts, ts->start);\n\tblog(LOG_DEBUG, \"old buffered ts: %\" PRIu64 \"-%\" PRIu64, ts->start, ts->end);\n#endif\n\n\tnew_ts.start =\n\t\taudio->buffered_ts - audio_frames_to_ns(sample_rate, audio->buffering_wait_ticks * AUDIO_OUTPUT_FRAMES);\n\n\twhile (ticks--) {\n\t\tconst uint64_t cur_ticks = ++audio->buffering_wait_ticks;\n\n\t\tnew_ts.end = new_ts.start;\n\t\tnew_ts.start = audio->buffered_ts - audio_frames_to_ns(sample_rate, cur_ticks * AUDIO_OUTPUT_FRAMES);\n\n#if DEBUG_AUDIO == 1\n\t\tblog(LOG_DEBUG, \"add buffered ts: %\" PRIu64 \"-%\" PRIu64, new_ts.start, new_ts.end);\n#endif\n\n\t\tdeque_push_front(&audio->buffered_timestamps, &new_ts, sizeof(new_ts));\n\t}\n\n\t*ts = new_ts;\n}\n\nstatic bool audio_buffer_insufficient(struct obs_source *source, size_t sample_rate, uint64_t min_ts)\n{\n\tsize_t total_floats = AUDIO_OUTPUT_FRAMES;\n\tsize_t size;\n\n\tif (source->info.audio_render || source->audio_pending || !source->audio_ts) {\n\t\treturn false;\n\t}\n\n\tif (source->audio_ts != min_ts && source->audio_ts != (min_ts - 1)) {\n\t\tsize_t start_point = convert_time_to_frames(sample_rate, source->audio_ts - min_ts);\n\t\tif (start_point >= AUDIO_OUTPUT_FRAMES)\n\t\t\treturn false;\n\n\t\ttotal_floats -= start_point;\n\t}\n\n\tsize = total_floats * sizeof(float);\n\n\tif (source->audio_input_buf[0].size < size) {\n\t\tsource->audio_pending = true;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic inline const char *find_min_ts(struct obs_core_data *data, uint64_t *min_ts)\n{\n\tobs_source_t *buffering_source = NULL;\n\tstruct obs_source *source = data->first_audio_source;\n\twhile (source) {\n\t\tif (!source->audio_pending && source->audio_ts && source->audio_ts < *min_ts) {\n\t\t\t*min_ts = source->audio_ts;\n\t\t\tbuffering_source = source;\n\t\t}\n\n\t\tsource = (struct obs_source *)source->next_audio_source;\n\t}\n\treturn buffering_source ? obs_source_get_name(buffering_source) : NULL;\n}\n\nstatic inline bool mark_invalid_sources(struct obs_core_data *data, size_t sample_rate, uint64_t min_ts)\n{\n\tbool recalculate = false;\n\n\tstruct obs_source *source = data->first_audio_source;\n\twhile (source) {\n\t\trecalculate |= audio_buffer_insufficient(source, sample_rate, min_ts);\n\t\tsource = (struct obs_source *)source->next_audio_source;\n\t}\n\n\treturn recalculate;\n}\n\nstatic inline const char *calc_min_ts(struct obs_core_data *data, size_t sample_rate, uint64_t *min_ts)\n{\n\tconst char *buffering_name = find_min_ts(data, min_ts);\n\tif (mark_invalid_sources(data, sample_rate, *min_ts))\n\t\tbuffering_name = find_min_ts(data, min_ts);\n\treturn buffering_name;\n}\n\nstatic inline void release_audio_sources(struct obs_core_audio *audio)\n{\n\tfor (size_t i = 0; i < audio->render_order.num; i++)\n\t\tobs_source_release(audio->render_order.array[i]);\n}\n\nstatic inline void execute_audio_tasks(void)\n{\n\tstruct obs_core_audio *audio = &obs->audio;\n\tbool tasks_remaining = true;\n\n\twhile (tasks_remaining) {\n\t\tpthread_mutex_lock(&audio->task_mutex);\n\t\tif (audio->tasks.size) {\n\t\t\tstruct obs_task_info info;\n\t\t\tdeque_pop_front(&audio->tasks, &info, sizeof(info));\n\t\t\tinfo.task(info.param);\n\t\t}\n\t\ttasks_remaining = !!audio->tasks.size;\n\t\tpthread_mutex_unlock(&audio->task_mutex);\n\t}\n}\n\n/* In case monitoring and an 'Audio Output Capture' source have the same device, one silences all the monitored\n * sources unless the 'Audio Output Capture' is muted.\n */\nstatic inline bool should_silence_monitored_source(obs_source_t *source, struct obs_core_audio *audio)\n{\n\tobs_source_t *dup_src = audio->monitoring_duplicating_source;\n\n\tif (!dup_src || !obs_source_active(dup_src))\n\t\treturn false;\n\n\tbool fader_muted = close_float(audio->monitoring_duplicating_source->volume, 0.0f, 0.0001f);\n\tbool output_capture_unmuted = !audio->monitoring_duplicating_source->muted && !fader_muted;\n\n\tif (output_capture_unmuted) {\n\t\tif (source->monitoring_type == OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT &&\n\t\t    source != audio->monitoring_duplicating_source) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic inline void clear_audio_output_buf(obs_source_t *source, struct obs_core_audio *audio)\n{\n\tif (!audio->monitoring_duplicating_source)\n\t\treturn;\n\n\tuint32_t aoc_mixers = audio->monitoring_duplicating_source->audio_mixers;\n\tuint32_t source_mixers = source->audio_mixers;\n\n\tfor (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {\n\t\tuint32_t mix_and_val = (1 << mix);\n\t\tif ((aoc_mixers & mix_and_val) && (source_mixers & mix_and_val)) {\n\t\t\tfor (size_t ch = 0; ch < MAX_AUDIO_CHANNELS; ch++) {\n\t\t\t\tfloat *buf = source->audio_output_buf[mix][ch];\n\t\t\t\tif (buf)\n\t\t\t\t\tmemset(buf, 0, AUDIO_OUTPUT_FRAMES * sizeof(float));\n\t\t\t}\n\t\t}\n\t}\n}\n\nbool audio_callback(void *param, uint64_t start_ts_in, uint64_t end_ts_in, uint64_t *out_ts, uint32_t mixers,\n\t\t    struct audio_output_data *mixes)\n{\n\tstruct obs_core_data *data = &obs->data;\n\tstruct obs_core_audio *audio = &obs->audio;\n\tstruct obs_source *source;\n\tsize_t sample_rate = audio_output_get_sample_rate(audio->audio);\n\tsize_t channels = audio_output_get_channels(audio->audio);\n\tstruct ts_info ts = {start_ts_in, end_ts_in};\n\tsize_t audio_size;\n\tuint64_t min_ts;\n\n\tda_resize(audio->render_order, 0);\n\tda_resize(audio->root_nodes, 0);\n\n\tdeque_push_back(&audio->buffered_timestamps, &ts, sizeof(ts));\n\tdeque_peek_front(&audio->buffered_timestamps, &ts, sizeof(ts));\n\tmin_ts = ts.start;\n\n\taudio_size = AUDIO_OUTPUT_FRAMES * sizeof(float);\n\n#if DEBUG_AUDIO == 1\n\tblog(LOG_DEBUG, \"ts %llu-%llu\", ts.start, ts.end);\n#endif\n\n\t/* ------------------------------------------------ */\n\t/* build audio render order */\n\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t j = 0; j < obs->video.mixes.num; j++) {\n\t\tstruct obs_view *view = obs->video.mixes.array[j]->view;\n\t\tif (!view)\n\t\t\tcontinue;\n\n\t\tpthread_mutex_lock(&view->channels_mutex);\n\n\t\t/* NOTE: these are source channels, not audio channels */\n\t\tfor (uint32_t i = 0; i < MAX_CHANNELS; i++) {\n\t\t\tobs_source_t *source = view->channels[i];\n\t\t\tif (!source)\n\t\t\t\tcontinue;\n\t\t\tif (!obs_source_active(source))\n\t\t\t\tcontinue;\n\t\t\tif (obs_source_removed(source))\n\t\t\t\tcontinue;\n\n\t\t\t/* first, add top - level sources as root_nodes */\n\t\t\tif (obs->video.mixes.array[j]->mix_audio)\n\t\t\t\tda_push_back(audio->root_nodes, &source);\n\n\t\t\t/* Build audio tree, tag duplicate individual sources */\n\t\t\tobs_source_enum_active_tree(source, push_audio_tree2, audio);\n\n\t\t\t/* add top - level sources to audio tree */\n\t\t\tpush_audio_tree(NULL, source, audio);\n\t\t}\n\t\tpthread_mutex_unlock(&view->channels_mutex);\n\t}\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\n\tpthread_mutex_lock(&data->audio_sources_mutex);\n\n\tsource = data->first_audio_source;\n\twhile (source) {\n\t\tif (!obs_source_removed(source)) {\n\t\t\tpush_audio_tree(NULL, source, audio);\n\t\t}\n\t\tsource = (struct obs_source *)source->next_audio_source;\n\t}\n\n\tpthread_mutex_unlock(&data->audio_sources_mutex);\n\n\t/* ------------------------------------------------ */\n\t/* render audio data */\n\tfor (size_t i = 0; i < audio->render_order.num; i++) {\n\t\tobs_source_t *source = audio->render_order.array[i];\n\t\tobs_source_audio_render(source, mixers, channels, sample_rate, audio_size);\n\t\tif (should_silence_monitored_source(source, audio))\n\t\t\tclear_audio_output_buf(source, audio);\n\n\t\t/* if a source has gone backward in time and we can no\n\t\t * longer buffer, drop some or all of its audio */\n\t\tif (audio_buffering_maxed(audio) && source->audio_ts != 0 && source->audio_ts < ts.start) {\n\t\t\tif (source->info.audio_render) {\n\t\t\t\tblog(LOG_DEBUG,\n\t\t\t\t     \"render audio source %s timestamp has \"\n\t\t\t\t     \"gone backwards\",\n\t\t\t\t     obs_source_get_name(source));\n\n\t\t\t\t/* just avoid further damage */\n\t\t\t\tsource->audio_pending = true;\n#if DEBUG_AUDIO == 1\n\t\t\t\t/* this should really be fixed */\n\t\t\t\tassert(false);\n#endif\n\t\t\t} else {\n\t\t\t\tpthread_mutex_lock(&source->audio_buf_mutex);\n\t\t\t\tbool rerender = ignore_audio(source, channels, sample_rate, ts.start);\n\t\t\t\tpthread_mutex_unlock(&source->audio_buf_mutex);\n\n\t\t\t\t/* if we (potentially) recovered, re-render */\n\t\t\t\tif (rerender)\n\t\t\t\t\tobs_source_audio_render(source, mixers, channels, sample_rate, audio_size);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* ------------------------------------------------ */\n\t/* get minimum audio timestamp */\n\tpthread_mutex_lock(&data->audio_sources_mutex);\n\tconst char *buffering_name = calc_min_ts(data, sample_rate, &min_ts);\n\tpthread_mutex_unlock(&data->audio_sources_mutex);\n\n\t/* ------------------------------------------------ */\n\t/* if a source has gone backward in time, buffer    */\n\tif (audio->fixed_buffer) {\n\t\tif (!audio_buffering_maxed(audio)) {\n\t\t\tset_fixed_audio_buffering(audio, sample_rate, &ts);\n\t\t}\n\t} else if (min_ts < ts.start) {\n\t\tadd_audio_buffering(audio, sample_rate, &ts, min_ts, buffering_name);\n\t}\n\n\t/* ------------------------------------------------ */\n\t/* mix audio */\n\tif (!audio->buffering_wait_ticks) {\n\t\tfor (size_t i = 0; i < audio->root_nodes.num; i++) {\n\t\t\tobs_source_t *source = audio->root_nodes.array[i];\n\n\t\t\tif (source->audio_pending)\n\t\t\t\tcontinue;\n\n\t\t\tpthread_mutex_lock(&source->audio_buf_mutex);\n\n\t\t\tif (source->audio_output_buf[0][0] && source->audio_ts)\n\t\t\t\tmix_audio(mixes, source, channels, sample_rate, &ts);\n\n\t\t\tpthread_mutex_unlock(&source->audio_buf_mutex);\n\t\t}\n\t}\n\n\t/* ------------------------------------------------ */\n\t/* discard audio */\n\tpthread_mutex_lock(&data->audio_sources_mutex);\n\n\tsource = data->first_audio_source;\n\twhile (source) {\n\t\tpthread_mutex_lock(&source->audio_buf_mutex);\n\t\tdiscard_audio(audio, source, channels, sample_rate, &ts);\n\t\tpthread_mutex_unlock(&source->audio_buf_mutex);\n\n\t\tsource = (struct obs_source *)source->next_audio_source;\n\t}\n\n\tpthread_mutex_unlock(&data->audio_sources_mutex);\n\n\t/* ------------------------------------------------ */\n\t/* release audio sources */\n\trelease_audio_sources(audio);\n\n\tdeque_pop_front(&audio->buffered_timestamps, NULL, sizeof(ts));\n\n\t*out_ts = ts.start;\n\n\tif (audio->buffering_wait_ticks) {\n\t\taudio->buffering_wait_ticks--;\n\t\treturn false;\n\t}\n\n\texecute_audio_tasks();\n\n\tUNUSED_PARAMETER(param);\n\treturn true;\n}\n"
  },
  {
    "path": "libobs/obs-av1.c",
    "content": "// SPDX-FileCopyrightText: 2023 David Rosca <nowrep@gmail.com>\n//\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"obs-av1.h\"\n\n#include \"obs.h\"\n\nstatic inline uint64_t leb128(const uint8_t *buf, size_t size, size_t *len)\n{\n\tuint64_t value = 0;\n\tuint8_t leb128_byte;\n\n\t*len = 0;\n\n\tfor (int i = 0; i < 8; i++) {\n\t\tif (size-- < 1)\n\t\t\tbreak;\n\t\t(*len)++;\n\t\tleb128_byte = buf[i];\n\t\tvalue |= (leb128_byte & 0x7f) << (i * 7);\n\t\tif (!(leb128_byte & 0x80))\n\t\t\tbreak;\n\t}\n\n\treturn value;\n}\n\nstatic inline unsigned int get_bits(uint8_t val, unsigned int n, unsigned int count)\n{\n\treturn (val >> (8 - n - count)) & ((1 << (count - 1)) * 2 - 1);\n}\n\nstatic void parse_obu_header(const uint8_t *buf, size_t size, size_t *obu_start, size_t *obu_size, int *obu_type)\n{\n\tint extension_flag, has_size_field;\n\tsize_t size_len = 0;\n\n\t*obu_start = 0;\n\t*obu_size = 0;\n\t*obu_type = 0;\n\n\tif (size < 1)\n\t\treturn;\n\n\t*obu_type = get_bits(*buf, 1, 4);\n\textension_flag = get_bits(*buf, 5, 1);\n\thas_size_field = get_bits(*buf, 6, 1);\n\n\tif (extension_flag)\n\t\t(*obu_start)++;\n\n\t(*obu_start)++;\n\n\tif (has_size_field)\n\t\t*obu_size = (size_t)leb128(buf + *obu_start, size - *obu_start, &size_len);\n\telse\n\t\t*obu_size = size - 1;\n\n\t*obu_start += size_len;\n}\n\n// Pass a static 10 byte buffer in. The max size for a leb128.\nstatic inline void encode_uleb128(uint64_t val, uint8_t *out_buf, size_t *len_out)\n{\n\tsize_t num_bytes = 0;\n\tuint8_t b = val & 0x7f;\n\tval >>= 7;\n\twhile (val > 0) {\n\t\tout_buf[num_bytes] = b | 0x80;\n\t\t++num_bytes;\n\t\tb = val & 0x7f;\n\t\tval >>= 7;\n\t}\n\tout_buf[num_bytes] = b;\n\t++num_bytes;\n\t*len_out = num_bytes;\n}\n\n/* metadata_obu_itu_t35() is a public symbol. Maintain the function\n * and make it call the more general metadata_obu() function.\n */\nvoid metadata_obu_itu_t35(const uint8_t *itut_t35_buffer, size_t itut_bufsize, uint8_t **out_buffer,\n\t\t\t  size_t *outbuf_size)\n{\n\tmetadata_obu(itut_t35_buffer, itut_bufsize, out_buffer, outbuf_size, METADATA_TYPE_ITUT_T35);\n}\n\n// Create an OBU to carry AV1 metadata types, including captions and user private data\nvoid metadata_obu(const uint8_t *source_buffer, size_t source_bufsize, uint8_t **out_buffer, size_t *outbuf_size,\n\t\t  uint8_t metadata_type)\n{\n\t/* From the AV1 spec: 5.3.2 OBU Header Syntax\n\t * -------------\n\t * obu_forbidden_bit   (1)\n\t * obu_type            (4)  // In this case OBS_OBU_METADATA\n\t * obu_extension_flag  (1)\n\t * obu_has_size_field  (1)  // Must be set, size of OBU is variable\n\t * obu_reserved_1bit   (1)\n\t * if(obu_extension_flag == 1)\n\t *   // skip, because we aren't setting this\n\t */\n\n\tuint8_t obu_header_byte = (OBS_OBU_METADATA << 3) | (1 << 1);\n\n\t/* From the AV1 spec: 5.3.1 General OBU Syntax\n\t * if (obu_has_size_field)\n\t * \tobu_size    leb128()\n\t * else\n\t * \tobu_size = sz - 1 - obu_extension_flag\n\t *\n\t * \t// Skipping portions unrelated to this OBU type\t\n\t *\n\t * if (obu_type == OBU_METADATA)\n\t * \tmetdata_obu()\n\t * 5.8.1 General metadata OBU Syntax\n\t *\t// leb128(metadatatype) should always be 1 byte +1 for trailing bits\n\t * \tmetadata_type                     leb128()\n\t * 5.8.2 Metadata ITUT T35 syntax\n\t * \tif (metadata_type == METADATA_TYPE_ITUT_T35)\n         * \t\t// add ITUT T35 payload\n\t * 5.8.1 General metadata OBU Syntax\n\t * \t// trailing bits will always be 0x80 because\n\t *\t// everything in here is byte aligned\n\t * \ttrailing_bits( obu_size * 8 - payloadBits )\n\t */\n\n\tint64_t size_field = 1 + source_bufsize + 1;\n\tuint8_t size_buf[10];\n\tsize_t size_buf_size = 0;\n\tencode_uleb128(size_field, size_buf, &size_buf_size);\n\t// header + obu_size + metadata_type + metadata_payload + trailing_bits\n\t*outbuf_size = 1 + size_buf_size + 1 + source_bufsize + 1;\n\t*out_buffer = bzalloc(*outbuf_size);\n\tsize_t offset = 0;\n\t(*out_buffer)[0] = obu_header_byte;\n\t++offset;\n\tmemcpy((*out_buffer) + offset, size_buf, size_buf_size);\n\toffset += size_buf_size;\n\t(*out_buffer)[offset] = metadata_type;\n\t++offset;\n\tmemcpy((*out_buffer) + offset, source_buffer, source_bufsize);\n\toffset += source_bufsize;\n\n\t/* From AV1 spec: 6.2.1 General OBU semantics\n\t * ... Trailing bits are always present, unless the OBU consists of only\n\t * the header. Trailing bits achieve byte alignment when the payload of\n\t * an OBU is not byte aligned. The trailing bits may also used for\n\t * additional byte padding, and if used are taken into account in the\n\t * sz value. In all cases, the pattern used for the trailing bits\n\t * guarantees that all OBUs (except header-only OBUs) end with the same\n\t * pattern: one bit set to one, optionally followed by zeros. */\n\n\t(*out_buffer)[offset] = 0x80;\n}\n\nbool obs_av1_keyframe(const uint8_t *data, size_t size)\n{\n\tconst uint8_t *start = data, *end = data + size;\n\n\twhile (start < end) {\n\t\tsize_t obu_start, obu_size;\n\t\tint obu_type;\n\t\tparse_obu_header(start, end - start, &obu_start, &obu_size, &obu_type);\n\n\t\tif (obu_size) {\n\t\t\tif (obu_type == OBS_OBU_FRAME || obu_type == OBS_OBU_FRAME_HEADER) {\n\t\t\t\tuint8_t val = *(start + obu_start);\n\t\t\t\tif (!get_bits(val, 0, 1))                // show_existing_frame\n\t\t\t\t\treturn get_bits(val, 1, 2) == 0; // frame_type\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tstart += obu_start + obu_size;\n\t}\n\n\treturn false;\n}\n\nvoid obs_extract_av1_headers(const uint8_t *packet, size_t size, uint8_t **new_packet_data, size_t *new_packet_size,\n\t\t\t     uint8_t **header_data, size_t *header_size)\n{\n\tDARRAY(uint8_t) new_packet;\n\tDARRAY(uint8_t) header;\n\tconst uint8_t *start = packet, *end = packet + size;\n\n\tda_init(new_packet);\n\tda_init(header);\n\n\twhile (start < end) {\n\t\tsize_t obu_start, obu_size;\n\t\tint obu_type;\n\t\tparse_obu_header(start, end - start, &obu_start, &obu_size, &obu_type);\n\n\t\tif (obu_type == OBS_OBU_METADATA || obu_type == OBS_OBU_SEQUENCE_HEADER) {\n\t\t\tda_push_back_array(header, start, obu_start + obu_size);\n\t\t}\n\t\tda_push_back_array(new_packet, start, obu_start + obu_size);\n\n\t\tstart += obu_start + obu_size;\n\t}\n\n\t*new_packet_data = new_packet.array;\n\t*new_packet_size = new_packet.num;\n\t*header_data = header.array;\n\t*header_size = header.num;\n}\n"
  },
  {
    "path": "libobs/obs-av1.h",
    "content": "// SPDX-FileCopyrightText: 2023 David Rosca <nowrep@gmail.com>\n//\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"util/c99defs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum {\n\tOBS_OBU_SEQUENCE_HEADER = 1,\n\tOBS_OBU_TEMPORAL_DELIMITER = 2,\n\tOBS_OBU_FRAME_HEADER = 3,\n\tOBS_OBU_TILE_GROUP = 4,\n\tOBS_OBU_METADATA = 5,\n\tOBS_OBU_FRAME = 6,\n\tOBS_OBU_REDUNDANT_FRAME_HEADER = 7,\n\tOBS_OBU_TILE_LIST = 8,\n\tOBS_OBU_PADDING = 15,\n};\n\nenum av1_obu_metadata_type {\n\tMETADATA_TYPE_HDR_CLL = 1,\n\tMETADATA_TYPE_HDR_MDCV,\n\tMETADATA_TYPE_SCALABILITY,\n\tMETADATA_TYPE_ITUT_T35,\n\tMETADATA_TYPE_TIMECODE,\n\tMETADATA_TYPE_USER_PRIVATE_6\n};\n\n/* Helpers for parsing AV1 OB units.  */\n\nEXPORT bool obs_av1_keyframe(const uint8_t *data, size_t size);\nEXPORT void obs_extract_av1_headers(const uint8_t *packet, size_t size, uint8_t **new_packet_data,\n\t\t\t\t    size_t *new_packet_size, uint8_t **header_data, size_t *header_size);\n\nEXPORT void metadata_obu_itu_t35(const uint8_t *itut_t35_buffer, size_t itut_bufsize, uint8_t **out_buffer,\n\t\t\t\t size_t *outbuf_size);\nEXPORT void metadata_obu(const uint8_t *source_buffer, size_t source_bufsize, uint8_t **out_buffer, size_t *outbuf_size,\n\t\t\t uint8_t metadata_type);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-avc.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-avc.h\"\n\n#include \"obs.h\"\n#include \"obs-nal.h\"\n#include \"util/array-serializer.h\"\n#include \"util/bitstream.h\"\n\nbool obs_avc_keyframe(const uint8_t *data, size_t size)\n{\n\tconst uint8_t *nal_start, *nal_end;\n\tconst uint8_t *end = data + size;\n\n\tnal_start = obs_nal_find_startcode(data, end);\n\twhile (true) {\n\t\twhile (nal_start < end && !*(nal_start++))\n\t\t\t;\n\n\t\tif (nal_start == end)\n\t\t\tbreak;\n\n\t\tconst uint8_t type = nal_start[0] & 0x1F;\n\n\t\tif (type == OBS_NAL_SLICE_IDR || type == OBS_NAL_SLICE)\n\t\t\treturn type == OBS_NAL_SLICE_IDR;\n\n\t\tnal_end = obs_nal_find_startcode(nal_start, end);\n\t\tnal_start = nal_end;\n\t}\n\n\treturn false;\n}\n\nconst uint8_t *obs_avc_find_startcode(const uint8_t *p, const uint8_t *end)\n{\n\treturn obs_nal_find_startcode(p, end);\n}\n\nstatic int compute_avc_keyframe_priority(const uint8_t *nal_start, bool *is_keyframe, int priority)\n{\n\tconst int type = nal_start[0] & 0x1F;\n\tif (type == OBS_NAL_SLICE_IDR)\n\t\t*is_keyframe = true;\n\n\tconst int new_priority = nal_start[0] >> 5;\n\tif (priority < new_priority)\n\t\tpriority = new_priority;\n\n\treturn priority;\n}\n\nstatic void serialize_avc_data(struct serializer *s, const uint8_t *data, size_t size, bool *is_keyframe, int *priority)\n{\n\tconst uint8_t *const end = data + size;\n\tconst uint8_t *nal_start = obs_nal_find_startcode(data, end);\n\twhile (true) {\n\t\twhile (nal_start < end && !*(nal_start++))\n\t\t\t;\n\n\t\tif (nal_start == end)\n\t\t\tbreak;\n\n\t\t*priority = compute_avc_keyframe_priority(nal_start, is_keyframe, *priority);\n\n\t\tconst uint8_t *const nal_end = obs_nal_find_startcode(nal_start, end);\n\t\tconst size_t nal_size = nal_end - nal_start;\n\t\ts_wb32(s, (uint32_t)nal_size);\n\t\ts_write(s, nal_start, nal_size);\n\t\tnal_start = nal_end;\n\t}\n}\n\nvoid obs_parse_avc_packet(struct encoder_packet *avc_packet, const struct encoder_packet *src)\n{\n\tstruct array_output_data output;\n\tstruct serializer s;\n\tlong ref = 1;\n\n\tarray_output_serializer_init(&s, &output);\n\t*avc_packet = *src;\n\n\tserialize(&s, &ref, sizeof(ref));\n\tserialize_avc_data(&s, src->data, src->size, &avc_packet->keyframe, &avc_packet->priority);\n\n\tavc_packet->data = output.bytes.array + sizeof(ref);\n\tavc_packet->size = output.bytes.num - sizeof(ref);\n\tavc_packet->drop_priority = avc_packet->priority;\n}\n\nint obs_parse_avc_packet_priority(const struct encoder_packet *packet)\n{\n\tint priority = packet->priority;\n\n\tconst uint8_t *const data = packet->data;\n\tconst uint8_t *const end = data + packet->size;\n\tconst uint8_t *nal_start = obs_nal_find_startcode(data, end);\n\twhile (true) {\n\t\twhile (nal_start < end && !*(nal_start++))\n\t\t\t;\n\n\t\tif (nal_start == end)\n\t\t\tbreak;\n\n\t\tbool unused;\n\t\tpriority = compute_avc_keyframe_priority(nal_start, &unused, priority);\n\n\t\tnal_start = obs_nal_find_startcode(nal_start, end);\n\t}\n\n\treturn priority;\n}\n\nstatic inline bool has_start_code(const uint8_t *data)\n{\n\tif (data[0] != 0 || data[1] != 0)\n\t\treturn false;\n\n\treturn data[2] == 1 || (data[2] == 0 && data[3] == 1);\n}\n\nstatic void get_sps_pps(const uint8_t *data, size_t size, const uint8_t **sps, size_t *sps_size, const uint8_t **pps,\n\t\t\tsize_t *pps_size)\n{\n\tconst uint8_t *nal_start, *nal_end;\n\tconst uint8_t *end = data + size;\n\tint type;\n\n\tnal_start = obs_nal_find_startcode(data, end);\n\twhile (true) {\n\t\twhile (nal_start < end && !*(nal_start++))\n\t\t\t;\n\n\t\tif (nal_start == end)\n\t\t\tbreak;\n\n\t\tnal_end = obs_nal_find_startcode(nal_start, end);\n\n\t\ttype = nal_start[0] & 0x1F;\n\t\tif (type == OBS_NAL_SPS) {\n\t\t\t*sps = nal_start;\n\t\t\t*sps_size = nal_end - nal_start;\n\t\t} else if (type == OBS_NAL_PPS) {\n\t\t\t*pps = nal_start;\n\t\t\t*pps_size = nal_end - nal_start;\n\t\t}\n\n\t\tnal_start = nal_end;\n\t}\n}\n\nstatic inline uint8_t get_ue_golomb(struct bitstream_reader *gb)\n{\n\tint i = 0;\n\twhile (i < 32 && !bitstream_reader_read_bits(gb, 1))\n\t\ti++;\n\n\treturn bitstream_reader_read_bits(gb, i) + (1 << i) - 1;\n}\n\nstatic void get_sps_high_params(const uint8_t *sps, size_t size, uint8_t *chroma_format_idc, uint8_t *bit_depth_luma,\n\t\t\t\tuint8_t *bit_depth_chroma)\n{\n\tstruct bitstream_reader gb;\n\n\t/* Extract RBSP */\n\tuint8_t *rbsp = bzalloc(size);\n\n\tsize_t i = 0;\n\tsize_t rbsp_size = 0;\n\n\twhile (i + 2 < size) {\n\t\tif (sps[i] == 0 && sps[i + 1] == 0 && sps[i + 2] == 3) {\n\t\t\trbsp[rbsp_size++] = sps[i++];\n\t\t\trbsp[rbsp_size++] = sps[i++];\n\t\t\t// skip emulation_prevention_three_byte\n\t\t\ti++;\n\t\t} else {\n\t\t\trbsp[rbsp_size++] = sps[i++];\n\t\t}\n\t}\n\n\twhile (i < size)\n\t\trbsp[rbsp_size++] = sps[i++];\n\n\t/* Read relevant information from SPS */\n\tbitstream_reader_init(&gb, rbsp, rbsp_size);\n\n\t// skip a whole bunch of stuff we don't care about\n\tbitstream_reader_read_bits(&gb, 24); // profile, constraint flags, level\n\tget_ue_golomb(&gb);                  // id\n\n\t*chroma_format_idc = get_ue_golomb(&gb);\n\t// skip separate_colour_plane_flag\n\tif (*chroma_format_idc == 3)\n\t\tbitstream_reader_read_bits(&gb, 1);\n\n\t*bit_depth_luma = get_ue_golomb(&gb);\n\t*bit_depth_chroma = get_ue_golomb(&gb);\n\n\tbfree(rbsp);\n}\n\nsize_t obs_parse_avc_header(uint8_t **header, const uint8_t *data, size_t size)\n{\n\tstruct array_output_data output;\n\tstruct serializer s;\n\tconst uint8_t *sps = NULL, *pps = NULL;\n\tsize_t sps_size = 0, pps_size = 0;\n\n\tarray_output_serializer_init(&s, &output);\n\n\tif (size <= 6)\n\t\treturn 0;\n\n\tif (!has_start_code(data)) {\n\t\t*header = bmemdup(data, size);\n\t\treturn size;\n\t}\n\n\tget_sps_pps(data, size, &sps, &sps_size, &pps, &pps_size);\n\tif (!sps || !pps || sps_size < 4)\n\t\treturn 0;\n\n\ts_w8(&s, 0x01);\n\ts_write(&s, sps + 1, 3);\n\ts_w8(&s, 0xff);\n\ts_w8(&s, 0xe1);\n\n\ts_wb16(&s, (uint16_t)sps_size);\n\ts_write(&s, sps, sps_size);\n\ts_w8(&s, 0x01);\n\ts_wb16(&s, (uint16_t)pps_size);\n\ts_write(&s, pps, pps_size);\n\n\tuint8_t profile_idc = sps[1];\n\n\t/* Additional data required for high, high10, high422, high444 profiles.\n\t * See ISO/IEC 14496-15 Section 5.3.3.1.2. */\n\tif (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244) {\n\t\tuint8_t chroma_format_idc, bit_depth_luma, bit_depth_chroma;\n\t\tget_sps_high_params(sps + 1, sps_size - 1, &chroma_format_idc, &bit_depth_luma, &bit_depth_chroma);\n\n\t\t// reserved + chroma_format\n\t\ts_w8(&s, 0xfc | chroma_format_idc);\n\t\t// reserved + bit_depth_luma_minus8\n\t\ts_w8(&s, 0xf8 | bit_depth_luma);\n\t\t// reserved + bit_depth_chroma_minus8\n\t\ts_w8(&s, 0xf8 | bit_depth_chroma);\n\t\t// numOfSequenceParameterSetExt\n\t\ts_w8(&s, 0);\n\t}\n\n\t*header = output.bytes.array;\n\treturn output.bytes.num;\n}\n\nvoid obs_extract_avc_headers(const uint8_t *packet, size_t size, uint8_t **new_packet_data, size_t *new_packet_size,\n\t\t\t     uint8_t **header_data, size_t *header_size, uint8_t **sei_data, size_t *sei_size)\n{\n\tDARRAY(uint8_t) new_packet;\n\tDARRAY(uint8_t) header;\n\tDARRAY(uint8_t) sei;\n\tconst uint8_t *nal_start, *nal_end, *nal_codestart;\n\tconst uint8_t *end = packet + size;\n\n\tda_init(new_packet);\n\tda_init(header);\n\tda_init(sei);\n\n\tnal_start = obs_nal_find_startcode(packet, end);\n\tnal_end = NULL;\n\twhile (nal_end != end) {\n\t\tnal_codestart = nal_start;\n\n\t\twhile (nal_start < end && !*(nal_start++))\n\t\t\t;\n\n\t\tif (nal_start == end)\n\t\t\tbreak;\n\n\t\tconst uint8_t type = nal_start[0] & 0x1F;\n\n\t\tnal_end = obs_nal_find_startcode(nal_start, end);\n\t\tif (!nal_end)\n\t\t\tnal_end = end;\n\n\t\tif (type == OBS_NAL_SPS || type == OBS_NAL_PPS) {\n\t\t\tda_push_back_array(header, nal_codestart, nal_end - nal_codestart);\n\t\t} else if (type == OBS_NAL_SEI) {\n\t\t\tda_push_back_array(sei, nal_codestart, nal_end - nal_codestart);\n\n\t\t} else {\n\t\t\tda_push_back_array(new_packet, nal_codestart, nal_end - nal_codestart);\n\t\t}\n\n\t\tnal_start = nal_end;\n\t}\n\n\t*new_packet_data = new_packet.array;\n\t*new_packet_size = new_packet.num;\n\t*header_data = header.array;\n\t*header_size = header.num;\n\t*sei_data = sei.array;\n\t*sei_size = sei.num;\n}\n"
  },
  {
    "path": "libobs/obs-avc.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"obs-nal.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct encoder_packet;\n\nenum {\n\tOBS_NAL_UNKNOWN = 0,\n\tOBS_NAL_SLICE = 1,\n\tOBS_NAL_SLICE_DPA = 2,\n\tOBS_NAL_SLICE_DPB = 3,\n\tOBS_NAL_SLICE_DPC = 4,\n\tOBS_NAL_SLICE_IDR = 5,\n\tOBS_NAL_SEI = 6,\n\tOBS_NAL_SPS = 7,\n\tOBS_NAL_PPS = 8,\n\tOBS_NAL_AUD = 9,\n\tOBS_NAL_FILLER = 12,\n};\n\n/* Helpers for parsing AVC NAL units.  */\n\nEXPORT bool obs_avc_keyframe(const uint8_t *data, size_t size);\nEXPORT const uint8_t *obs_avc_find_startcode(const uint8_t *p, const uint8_t *end);\nEXPORT void obs_parse_avc_packet(struct encoder_packet *avc_packet, const struct encoder_packet *src);\nEXPORT int obs_parse_avc_packet_priority(const struct encoder_packet *packet);\nEXPORT size_t obs_parse_avc_header(uint8_t **header, const uint8_t *data, size_t size);\nEXPORT void obs_extract_avc_headers(const uint8_t *packet, size_t size, uint8_t **new_packet_data,\n\t\t\t\t    size_t *new_packet_size, uint8_t **header_data, size_t *header_size,\n\t\t\t\t    uint8_t **sei_data, size_t *sei_size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-canvas.c",
    "content": "/******************************************************************************\n    Copyright (C) 2025 by Dennis Sädtler <saedtler@twitch.tv>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs.h\"\n#include \"obs-internal.h\"\n#include \"obs-scene.h\"\n\n/* The primary canvas has static name/uuid. */\nstatic const char *MAIN_CANVAS_NAME = \"Main\";\nstatic const char *MAIN_CANVAS_UUID = \"6c69626f-6273-4c00-9d88-c5136d61696e\";\n/* Internal flag to mark a canvas as removed */\nstatic const uint32_t REMOVED = 1u << 31;\n\n/*** Signals ***/\n\nstatic const char *canvas_signals[] = {\n\t\"void destroy(ptr canvas)\",\n\t\"void remove(ptr canvas)\",\n\t\"void video_reset(ptr canvas)\",\n\n\t\"void source_add(ptr canvas, ptr source)\",\n\t\"void source_remove(ptr canvas, ptr source)\",\n\t\"void source_rename(ptr source, string new_name, string prev_name)\",\n\n\t\"void rename(ptr source, string new_name, string prev_name)\",\n\n\t\"void channel_change(ptr canvas, int channel, in out ptr source, ptr prev_source)\",\n\n\tNULL,\n};\n\nstatic inline void canvas_dosignal(obs_canvas_t *canvas, const char *signal_obs, const char *signal_source)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"canvas\", canvas);\n\tif (signal_obs)\n\t\tsignal_handler_signal(obs->signals, signal_obs, &data);\n\tif (signal_source)\n\t\tsignal_handler_signal(canvas->context.signals, signal_source, &data);\n}\n\nstatic inline void canvas_dosignal_source(const char *signal, obs_canvas_t *canvas, obs_source_t *source)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"canvas\", canvas);\n\tcalldata_set_ptr(&data, \"source\", source);\n\n\tsignal_handler_signal(canvas->context.signals, signal, &data);\n}\n\n/*** Reference Counting ***/\n\nvoid obs_canvas_release(obs_canvas_t *canvas)\n{\n\tif (!obs && canvas) {\n\t\tblog(LOG_WARNING, \"Tried to release a canvas when the OBS core is shut down!\");\n\t\treturn;\n\t}\n\n\tif (!canvas)\n\t\treturn;\n\n\tobs_weak_canvas_t *control = (obs_weak_canvas_t *)canvas->context.control;\n\tif (obs_ref_release(&control->ref)) {\n\t\tobs_canvas_destroy(canvas);\n\t\tobs_weak_canvas_release(control);\n\t}\n}\n\nvoid obs_weak_canvas_addref(obs_weak_canvas_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tobs_weak_ref_addref(&weak->ref);\n}\n\nvoid obs_weak_canvas_release(obs_weak_canvas_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tif (obs_weak_ref_release(&weak->ref))\n\t\tbfree(weak);\n}\n\nobs_canvas_t *obs_canvas_get_ref(obs_canvas_t *canvas)\n{\n\tif (!canvas)\n\t\treturn NULL;\n\n\treturn obs_weak_canvas_get_canvas((obs_weak_canvas_t *)canvas->context.control);\n}\n\nobs_weak_canvas_t *obs_canvas_get_weak_canvas(obs_canvas_t *canvas)\n{\n\tif (!canvas)\n\t\treturn NULL;\n\n\tobs_weak_canvas_t *weak = (obs_weak_canvas_t *)canvas->context.control;\n\tobs_weak_canvas_addref(weak);\n\treturn weak;\n}\n\nobs_canvas_t *obs_weak_canvas_get_canvas(obs_weak_canvas_t *weak)\n{\n\tif (!weak)\n\t\treturn NULL;\n\n\tif (obs_weak_ref_get_ref(&weak->ref))\n\t\treturn weak->canvas;\n\n\treturn NULL;\n}\n\n/*** Creation / Destruction ***/\n\nstatic obs_canvas_t *obs_canvas_create_internal(const char *name, const char *uuid, struct obs_video_info *ovi,\n\t\t\t\t\t\tuint32_t flags, bool private)\n{\n\tstruct obs_canvas *canvas = bzalloc(sizeof(struct obs_canvas));\n\tcanvas->flags = flags;\n\n\tif (!obs_context_data_init(&canvas->context, OBS_OBJ_TYPE_CANVAS, NULL, name, uuid, NULL, private))\n\t\treturn NULL;\n\n\tif (!signal_handler_add_array(canvas->context.signals, canvas_signals)) {\n\t\tobs_context_data_free(&canvas->context);\n\t\tbfree(canvas);\n\t\treturn NULL;\n\t}\n\n\tif (pthread_mutex_init_recursive(&canvas->sources_mutex) != 0) {\n\t\tobs_context_data_free(&canvas->context);\n\t\tbfree(canvas);\n\t\treturn NULL;\n\t}\n\n\tobs_view_init(&canvas->view, flags & ACTIVATE ? MAIN_VIEW : AUX_VIEW);\n\tobs_context_init_control(&canvas->context, canvas, (obs_destroy_cb)obs_canvas_destroy);\n\n\t/* A canvas can be created without a mix. */\n\tif (ovi) {\n\t\tcanvas->ovi = *ovi;\n\t\tcanvas->mix = obs_create_video_mix(ovi);\n\t\tif (canvas->mix) {\n\t\t\tcanvas->mix->view = &canvas->view;\n\t\t\tcanvas->mix->mix_audio = (flags & MIX_AUDIO) != 0;\n\n\t\t\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\t\t\tda_push_back(obs->video.mixes, &canvas->mix);\n\t\t\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\t\t}\n\t}\n\n\tobs_context_data_insert_uuid(&canvas->context, &obs->data.canvases_mutex, &obs->data.canvases);\n\tif (!private) {\n\t\tobs_context_data_insert_name(&canvas->context, &obs->data.canvases_mutex, &obs->data.named_canvases);\n\t\tcanvas_dosignal(canvas, \"canvas_create\", NULL);\n\t}\n\n\tblog(LOG_DEBUG, \"%scanvas '%s' (%s) created\", private ? \"private \" : \"\", canvas->context.name,\n\t     canvas->context.uuid);\n\n\treturn canvas;\n}\n\nobs_canvas_t *obs_create_main_canvas(void)\n{\n\tconst uint32_t main_flags = MAIN | PROGRAM;\n\treturn obs_canvas_create_internal(MAIN_CANVAS_NAME, MAIN_CANVAS_UUID, NULL, main_flags, false);\n}\n\nobs_canvas_t *obs_canvas_create(const char *name, struct obs_video_info *ovi, uint32_t flags)\n{\n\tflags &= ~MAIN; /* Prevent user from creating a MAIN canvas. */\n\treturn obs_canvas_create_internal(name, NULL, ovi, flags, false);\n}\n\nobs_canvas_t *obs_canvas_create_private(const char *name, struct obs_video_info *ovi, uint32_t flags)\n{\n\tflags &= ~MAIN; /* Prevent user from creating a MAIN canvas. */\n\treturn obs_canvas_create_internal(name, NULL, ovi, flags, true);\n}\n\nvoid obs_canvas_destroy(obs_canvas_t *canvas)\n{\n\tcanvas_dosignal(canvas, \"canvas_destroy\", \"destroy\");\n\n\tobs_canvas_clear_mix(canvas);\n\n\tobs_source_t *source = canvas->sources;\n\twhile (source) {\n\t\t/* Canvases can hold strong refs to scene sources, release them here. */\n\t\tif (canvas->flags & SCENE_REF && obs_source_is_scene(source))\n\t\t\tobs_source_release(source);\n\n\t\tsource = source->context.hh.next;\n\t}\n\n\tobs_context_data_remove_uuid(&canvas->context, &obs->data.canvases_mutex, &obs->data.canvases);\n\tif (!canvas->context.private) {\n\t\tobs_context_data_remove_name(&canvas->context, &obs->data.canvases_mutex, &obs->data.named_canvases);\n\t}\n\n\tblog(LOG_DEBUG, \"%scanvas '%s' (%s) destroyed\", canvas->context.private ? \"private \" : \"\", canvas->context.name,\n\t     canvas->context.uuid);\n\n\tpthread_mutex_destroy(&canvas->sources_mutex);\n\tobs_context_data_free(&canvas->context);\n\tobs_view_free(&canvas->view);\n\tbfree(canvas);\n}\n\n/*** Saving / Loading ***/\n\nobs_data_t *obs_save_canvas(obs_canvas_t *canvas)\n{\n\tif (canvas->flags & (EPHEMERAL | REMOVED))\n\t\treturn NULL;\n\n\tobs_data_t *canvas_data = obs_data_create();\n\n\tobs_data_set_string(canvas_data, \"name\", canvas->context.name);\n\tobs_data_set_string(canvas_data, \"uuid\", canvas->context.uuid);\n\tobs_data_set_bool(canvas_data, \"private\", canvas->context.private);\n\tobs_data_set_int(canvas_data, \"flags\", canvas->flags);\n\n\treturn canvas_data;\n}\n\nobs_canvas_t *obs_load_canvas(obs_data_t *data)\n{\n\tconst char *name = obs_data_get_string(data, \"name\");\n\tconst char *uuid = obs_data_get_string(data, \"uuid\");\n\tconst bool private = obs_data_get_bool(data, \"private\");\n\tuint32_t flags = (uint32_t)obs_data_get_int(data, \"flags\");\n\n\tflags &= ~MAIN; /* Prevent user from creating a MAIN canvas. */\n\treturn obs_canvas_create_internal(name, uuid, NULL, flags, private);\n}\n\n/*** Internal API ***/\n\n/* Free canvas mix (if any) */\nvoid obs_canvas_clear_mix(obs_canvas_t *canvas)\n{\n\tif (!canvas->mix)\n\t\treturn;\n\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0; i < obs->video.mixes.num; i++) {\n\t\tstruct obs_core_video_mix *mix = obs->video.mixes.array[i];\n\t\tif (mix == canvas->mix) {\n\t\t\tda_erase(obs->video.mixes, i);\n\t\t\tobs_free_video_mix(mix);\n\t\t\tbreak;\n\t\t}\n\t}\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\n\tcanvas->mix = NULL;\n}\n\n/* Clear mixes attached to canvases */\nvoid obs_free_canvas_mixes(void)\n{\n\tpthread_mutex_lock(&obs->data.canvases_mutex);\n\tstruct obs_context_data *ctx, *tmp;\n\tHASH_ITER (hh, (struct obs_context_data *)obs->data.canvases, ctx, tmp) {\n\t\tobs_canvas_t *canvas = (obs_canvas_t *)ctx;\n\t\tobs_canvas_clear_mix(canvas);\n\t}\n\tpthread_mutex_unlock(&obs->data.canvases_mutex);\n}\n\nbool obs_canvas_reset_video_internal(obs_canvas_t *canvas, struct obs_video_info *ovi)\n{\n\tif (!ovi && !canvas->mix)\n\t\treturn true;\n\n\tobs_canvas_clear_mix(canvas);\n\n\tif (ovi)\n\t\tcanvas->ovi = *ovi;\n\n\tcanvas->mix = obs_create_video_mix(&canvas->ovi);\n\tif (canvas->mix) {\n\t\tcanvas->mix->view = &canvas->view;\n\t\tcanvas->mix->mix_audio = (canvas->flags & MIX_AUDIO) != 0;\n\n\t\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\t\tda_push_back(obs->video.mixes, &canvas->mix);\n\t\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\t}\n\n\tcanvas_dosignal(canvas, \"canvas_video_reset\", \"video_reset\");\n\n\treturn !!canvas->mix;\n}\n\nvoid obs_canvas_insert_source(obs_canvas_t *canvas, obs_source_t *source)\n{\n\tif (canvas->flags & SCENE_REF && obs_source_is_scene(source))\n\t\tobs_source_get_ref(source);\n\tif (source->canvas)\n\t\tobs_canvas_remove_source(source);\n\n\tsource->canvas = obs_canvas_get_weak_canvas(canvas);\n\tobs_context_data_insert_name(&source->context, &canvas->sources_mutex, &canvas->sources);\n\tcanvas_dosignal_source(\"source_add\", canvas, source);\n}\n\nstatic bool remove_groups_items_cb(obs_scene_t *scene, obs_sceneitem_t *item, void *param)\n{\n\tUNUSED_PARAMETER(scene);\n\n\tobs_source_t *source = param;\n\tif (item->source == source)\n\t\tobs_sceneitem_remove(item);\n\n\treturn true;\n}\n\nstatic bool remove_groups_enum_cb(void *param, obs_source_t *scene_source)\n{\n\tobs_source_t *source = param;\n\tobs_scene_t *scene = obs_scene_from_source(scene_source);\n\n\tobs_scene_enum_items(scene, remove_groups_items_cb, source);\n\treturn true;\n}\n\nvoid obs_canvas_remove_source(obs_source_t *source)\n{\n\tobs_canvas_t *canvas = obs_weak_canvas_get_canvas(source->canvas);\n\tif (canvas) {\n\t\tobs_weak_canvas_release(source->canvas);\n\t\tobs_context_data_remove_name(&source->context, &canvas->sources_mutex, &canvas->sources);\n\n\t\tcanvas_dosignal_source(\"source_remove\", canvas, source);\n\t\tif (canvas->flags & SCENE_REF && obs_source_is_scene(source))\n\t\t\tobs_source_release(source);\n\n\t\t/* If source is a group, also remove it from all other scenes in the old canvas */\n\t\tif (obs_source_is_group(source))\n\t\t\tobs_canvas_enum_scenes(canvas, remove_groups_enum_cb, source);\n\n\t\tobs_canvas_release(canvas);\n\t}\n\tsource->canvas = NULL;\n}\n\nvoid obs_canvas_rename_source(obs_source_t *source, const char *name)\n{\n\tobs_canvas_t *canvas = obs_weak_canvas_get_canvas(source->canvas);\n\tif (canvas) {\n\t\tstruct calldata data;\n\t\tchar *prev_name = bstrdup(source->context.name);\n\n\t\tobs_context_data_setname_ht(&source->context, name, &canvas->sources);\n\n\t\tcalldata_init(&data);\n\t\tcalldata_set_ptr(&data, \"source\", source);\n\t\tcalldata_set_string(&data, \"new_name\", source->context.name);\n\t\tcalldata_set_string(&data, \"prev_name\", prev_name);\n\n\t\tsignal_handler_signal(source->context.signals, \"rename\", &data);\n\t\tsignal_handler_signal(canvas->context.signals, \"source_rename\", &data);\n\t\tif (canvas->flags & MAIN)\n\t\t\tsignal_handler_signal(obs->signals, \"source_rename\", &data);\n\n\t\tcalldata_free(&data);\n\t\tbfree(prev_name);\n\n\t\tobs_canvas_release(canvas);\n\t}\n}\n\n/*** Public Canvas Object API ***/\n\nbool obs_canvas_reset_video(obs_canvas_t *canvas, struct obs_video_info *ovi)\n{\n\tif (canvas->flags & MAIN || obs_video_active())\n\t\treturn false;\n\n\treturn obs_canvas_reset_video_internal(canvas, ovi);\n}\n\nvideo_t *obs_canvas_get_video(const obs_canvas_t *canvas)\n{\n\treturn canvas->mix ? canvas->mix->video : NULL;\n}\n\nbool obs_canvas_get_video_info(const obs_canvas_t *canvas, struct obs_video_info *ovi)\n{\n\tif (!obs->video.graphics || !canvas->mix)\n\t\treturn false;\n\n\t*ovi = canvas->ovi;\n\treturn true;\n}\n\nsignal_handler_t *obs_canvas_get_signal_handler(obs_canvas_t *canvas)\n{\n\treturn canvas->context.signals;\n}\n\nvoid obs_canvas_set_channel(obs_canvas_t *canvas, uint32_t channel, obs_source_t *source)\n{\n\tassert(channel < MAX_CHANNELS);\n\n\tif (channel >= MAX_CHANNELS)\n\t\treturn;\n\n\tstruct obs_view *view = &canvas->view;\n\n\tpthread_mutex_lock(&view->channels_mutex);\n\n\tsource = obs_source_get_ref(source);\n\n\tobs_source_t *prev_source = view->channels[channel];\n\n\tif (source == prev_source) {\n\t\tobs_source_release(source);\n\t\tpthread_mutex_unlock(&view->channels_mutex);\n\t\treturn;\n\t}\n\n\tstruct calldata params = {0};\n\tcalldata_set_ptr(&params, \"canvas\", canvas);\n\tcalldata_set_int(&params, \"channel\", channel);\n\tcalldata_set_ptr(&params, \"prev_source\", prev_source);\n\tcalldata_set_ptr(&params, \"source\", source);\n\n\tsignal_handler_signal(canvas->context.signals, \"channel_change\", &params);\n\tif (canvas->flags & MAIN)\n\t\tsignal_handler_signal(obs->signals, \"channel_change\", &params);\n\n\t/* For some reason the original implementation allows overriding the source from the callback,\n\t * so just in case support that here as well. This isn't used anywhere in OBS itself. */\n\tcalldata_get_ptr(&params, \"source\", &source);\n\tview->channels[channel] = source;\n\n\tcalldata_free(&params);\n\tpthread_mutex_unlock(&view->channels_mutex);\n\n\tif (source)\n\t\tobs_source_activate(source, view->type);\n\n\tif (prev_source) {\n\t\tobs_source_deactivate(prev_source, view->type);\n\t\tobs_source_release(prev_source);\n\t}\n}\n\nobs_source_t *obs_canvas_get_channel(obs_canvas_t *canvas, uint32_t channel)\n{\n\treturn obs_view_get_source(&canvas->view, channel);\n}\n\nobs_scene_t *obs_canvas_scene_create(obs_canvas_t *canvas, const char *name)\n{\n\tstruct obs_source *source = obs_source_create_canvas(canvas, \"scene\", name, NULL, NULL);\n\treturn source->context.data;\n}\n\nvoid obs_canvas_scene_remove(obs_scene_t *scene)\n{\n\tobs_canvas_remove_source(scene->source);\n}\n\nvoid obs_canvas_set_name(obs_canvas_t *canvas, const char *name)\n{\n\tif (!name || !*name)\n\t\treturn;\n\tif (canvas->flags & MAIN) /* Do not allow renaming main canvases. */\n\t\treturn;\n\tif (strcmp(name, canvas->context.name) == 0)\n\t\treturn;\n\n\tchar *prev_name = bstrdup(canvas->context.name);\n\n\tif (canvas->context.private)\n\t\tobs_context_data_setname(&canvas->context, name);\n\telse\n\t\tobs_context_data_setname_ht(&canvas->context, name, &obs->data.named_canvases);\n\n\tstruct calldata data;\n\tcalldata_init(&data);\n\tcalldata_set_ptr(&data, \"canvas\", canvas);\n\tcalldata_set_string(&data, \"new_name\", canvas->context.name);\n\tcalldata_set_string(&data, \"prev_name\", prev_name);\n\tsignal_handler_signal(canvas->context.signals, \"rename\", &data);\n\n\tif (!canvas->context.private)\n\t\tsignal_handler_signal(obs->signals, \"canvas_rename\", &data);\n\n\tcalldata_free(&data);\n\tbfree(prev_name);\n}\n\nconst char *obs_canvas_get_name(const obs_canvas_t *canvas)\n{\n\treturn canvas->context.name;\n}\n\nconst char *obs_canvas_get_uuid(const obs_canvas_t *canvas)\n{\n\treturn canvas->context.uuid;\n}\n\nuint32_t obs_canvas_get_flags(const obs_canvas_t *canvas)\n{\n\treturn canvas->flags;\n}\n\nstatic bool enum_move_cb(obs_scene_t *scene, obs_sceneitem_t *item, void *param)\n{\n\tUNUSED_PARAMETER(scene);\n\n\tobs_canvas_t *dst = param;\n\tobs_source_t *source = item->source;\n\n\tif (obs_source_is_group(source)) {\n\t\tobs_canvas_remove_source(source);\n\t\tobs_canvas_insert_source(dst, source);\n\t}\n\n\treturn true;\n}\n\nvoid obs_canvas_move_scene(obs_scene_t *scene, obs_canvas_t *dst)\n{\n\tobs_source_t *source = scene->source;\n\tobs_canvas_remove_source(source);\n\tobs_canvas_insert_source(dst, source);\n\n\t/* Also move all groups within this scene */\n\tobs_scene_enum_items(scene, enum_move_cb, dst);\n}\n\nvoid obs_canvas_remove(obs_canvas_t *canvas)\n{\n\t/* Do not allow removing the main canvas, or canvases already marked as removed. */\n\tif (canvas->flags & (REMOVED | MAIN))\n\t\treturn;\n\n\tobs_canvas_t *c = obs_canvas_get_ref(canvas);\n\tif (c) {\n\t\tc->flags |= REMOVED;\n\t\tcanvas_dosignal(c, \"canvas_remove\", \"remove\");\n\t\tobs_canvas_release(c);\n\t}\n}\n\nbool obs_canvas_removed(obs_canvas_t *canvas)\n{\n\treturn (canvas->flags & REMOVED) != 0;\n}\n\nbool obs_canvas_has_video(obs_canvas_t *canvas)\n{\n\treturn canvas->mix != NULL;\n}\n\nvoid obs_canvas_render(obs_canvas_t *canvas)\n{\n\tobs_view_render(&canvas->view);\n}\n"
  },
  {
    "path": "libobs/obs-cocoa.m",
    "content": "/******************************************************************************\n    Copyright (C) 2013 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"util/dstr.h\"\n#include \"obs.h\"\n#include \"obs-internal.h\"\n\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/sysctl.h>\n\n#include <Carbon/Carbon.h>\n#include <IOKit/hid/IOHIDDevice.h>\n#include <IOKit/hid/IOHIDManager.h>\n\n#import <AppKit/AppKit.h>\n\n// MARK: macOS Bundle Management\nconst char *get_module_extension(void)\n{\n    return \"\";\n}\n\nvoid add_default_module_paths(void)\n{\n    NSURL *pluginURL = [[NSBundle mainBundle] builtInPlugInsURL];\n    NSString *pluginModulePath = [[pluginURL path] stringByAppendingString:@\"/%module%.plugin/Contents/MacOS/\"];\n    NSString *pluginDataPath = [[pluginURL path] stringByAppendingString:@\"/%module%.plugin/Contents/Resources/\"];\n\n    obs_add_module_path(pluginModulePath.UTF8String, pluginDataPath.UTF8String);\n}\n\nchar *find_libobs_data_file(const char *file)\n{\n    NSBundle *frameworkBundle = [NSBundle bundleWithIdentifier:@\"com.obsproject.libobs\"];\n    NSString *libobsDataPath =\n        [[[frameworkBundle bundleURL] path] stringByAppendingFormat:@\"/%@/%s\", @\"Resources\", file];\n    size_t path_length = strlen(libobsDataPath.UTF8String);\n\n    char *path = bmalloc(path_length + 1);\n    snprintf(path, (path_length + 1), \"%s\", libobsDataPath.UTF8String);\n\n    return path;\n}\n\n// MARK: - macOS Hardware Info Helpers\n\nstatic void log_processor_name(void)\n{\n    char *name = NULL;\n    size_t size;\n    int ret;\n\n    ret = sysctlbyname(\"machdep.cpu.brand_string\", NULL, &size, NULL, 0);\n    if (ret != 0)\n        return;\n\n    name = bmalloc(size);\n\n    ret = sysctlbyname(\"machdep.cpu.brand_string\", name, &size, NULL, 0);\n    if (ret == 0)\n        blog(LOG_INFO, \"CPU Name: %s\", name);\n\n    bfree(name);\n}\n\nstatic void log_processor_speed(void)\n{\n    size_t size;\n    long long freq;\n    int ret;\n\n    size = sizeof(freq);\n    ret = sysctlbyname(\"hw.cpufrequency\", &freq, &size, NULL, 0);\n    if (ret == 0)\n        blog(LOG_INFO, \"CPU Speed: %lldMHz\", freq / 1000000);\n}\n\nstatic void log_model_name(void)\n{\n    char *name = NULL;\n    size_t size;\n    int ret;\n\n    ret = sysctlbyname(\"hw.model\", NULL, &size, NULL, 0);\n    if (ret != 0)\n        return;\n\n    name = bmalloc(size);\n\n    ret = sysctlbyname(\"hw.model\", name, &size, NULL, 0);\n    if (ret == 0)\n        blog(LOG_INFO, \"Model Identifier: %s\", name);\n\n    bfree(name);\n}\n\nstatic void log_processor_cores(void)\n{\n    blog(LOG_INFO, \"Physical Cores: %d, Logical Cores: %d\", os_get_physical_cores(), os_get_logical_cores());\n}\n\nstatic void log_emulation_status(void)\n{\n    blog(LOG_INFO, \"Rosetta translation used: %s\", os_get_emulation_status() ? \"true\" : \"false\");\n}\n\nstatic void log_available_memory(void)\n{\n    size_t size;\n    long long memory_available;\n    int ret;\n\n    size = sizeof(memory_available);\n    ret = sysctlbyname(\"hw.memsize\", &memory_available, &size, NULL, 0);\n    if (ret == 0)\n        blog(LOG_INFO, \"Physical Memory: %lldMB Total\", memory_available / 1024 / 1024);\n}\n\nstatic void log_os(void)\n{\n    NSProcessInfo *pi = [NSProcessInfo processInfo];\n    blog(LOG_INFO, \"OS Name: macOS\");\n    blog(LOG_INFO, \"OS Version: %s\", [[pi operatingSystemVersionString] UTF8String]);\n}\n\nstatic void log_kernel_version(void)\n{\n    char kernel_version[1024];\n    size_t size = sizeof(kernel_version);\n    int ret;\n\n    ret = sysctlbyname(\"kern.osrelease\", kernel_version, &size, NULL, 0);\n    if (ret == 0)\n        blog(LOG_INFO, \"Kernel Version: %s\", kernel_version);\n}\n\nvoid log_system_info(void)\n{\n    log_processor_name();\n    log_processor_speed();\n    log_processor_cores();\n    log_available_memory();\n    log_model_name();\n    log_os();\n    log_emulation_status();\n    log_kernel_version();\n}\n\n// MARK: - Type Conversion Utilities\n\nstatic bool dstr_from_cfstring(struct dstr *str, CFStringRef ref)\n{\n    CFIndex length = CFStringGetLength(ref);\n    CFIndex max_size = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;\n    assert(max_size > 0);\n\n    dstr_reserve(str, max_size);\n\n    if (!CFStringGetCString(ref, str->array, max_size, kCFStringEncodingUTF8))\n        return false;\n\n    str->len = strlen(str->array);\n    return true;\n}\n\n// MARK: - Graphics Thread Wrappers\n\nvoid *obs_graphics_thread_autorelease(void *param)\n{\n    @autoreleasepool {\n        return obs_graphics_thread(param);\n    }\n}\n\nbool obs_graphics_thread_loop_autorelease(struct obs_graphics_context *context)\n{\n    @autoreleasepool {\n        return obs_graphics_thread_loop(context);\n    }\n}\n\n// MARK: - macOS Hotkey Management\n\ntypedef struct obs_key_code {\n    int code;\n    bool is_valid;\n} obs_key_code_t;\n\ntypedef struct macOS_glyph_desc {\n    UniChar glyph;\n    char *desc;\n    bool is_glyph;\n    bool is_valid;\n} macOS_glyph_desc_t;\n\ntypedef struct obs_key_desc {\n    char *desc;\n    bool is_valid;\n} obs_key_desc_t;\n\nstatic int INVALID_KEY = 0xFF;\n\n/* clang-format off */\nstatic const obs_key_code_t virtual_keys[OBS_KEY_LAST_VALUE] = {\n    [OBS_KEY_A]            = {.code = kVK_ANSI_A,              .is_valid = true},\n    [OBS_KEY_B]            = {.code = kVK_ANSI_B,              .is_valid = true},\n    [OBS_KEY_C]            = {.code = kVK_ANSI_C,              .is_valid = true},\n    [OBS_KEY_D]            = {.code = kVK_ANSI_D,              .is_valid = true},\n    [OBS_KEY_E]            = {.code = kVK_ANSI_E,              .is_valid = true},\n    [OBS_KEY_F]            = {.code = kVK_ANSI_F,              .is_valid = true},\n    [OBS_KEY_G]            = {.code = kVK_ANSI_G,              .is_valid = true},\n    [OBS_KEY_H]            = {.code = kVK_ANSI_H,              .is_valid = true},\n    [OBS_KEY_I]            = {.code = kVK_ANSI_I,              .is_valid = true},\n    [OBS_KEY_J]            = {.code = kVK_ANSI_J,              .is_valid = true},\n    [OBS_KEY_K]            = {.code = kVK_ANSI_K,              .is_valid = true},\n    [OBS_KEY_L]            = {.code = kVK_ANSI_L,              .is_valid = true},\n    [OBS_KEY_M]            = {.code = kVK_ANSI_M,              .is_valid = true},\n    [OBS_KEY_N]            = {.code = kVK_ANSI_N,              .is_valid = true},\n    [OBS_KEY_O]            = {.code = kVK_ANSI_O,              .is_valid = true},\n    [OBS_KEY_P]            = {.code = kVK_ANSI_P,              .is_valid = true},\n    [OBS_KEY_Q]            = {.code = kVK_ANSI_Q,              .is_valid = true},\n    [OBS_KEY_R]            = {.code = kVK_ANSI_R,              .is_valid = true},\n    [OBS_KEY_S]            = {.code = kVK_ANSI_S,              .is_valid = true},\n    [OBS_KEY_T]            = {.code = kVK_ANSI_T,              .is_valid = true},\n    [OBS_KEY_U]            = {.code = kVK_ANSI_U,              .is_valid = true},\n    [OBS_KEY_V]            = {.code = kVK_ANSI_V,              .is_valid = true},\n    [OBS_KEY_W]            = {.code = kVK_ANSI_W,              .is_valid = true},\n    [OBS_KEY_X]            = {.code = kVK_ANSI_X,              .is_valid = true},\n    [OBS_KEY_Y]            = {.code = kVK_ANSI_Y,              .is_valid = true},\n    [OBS_KEY_Z]            = {.code = kVK_ANSI_Z,              .is_valid = true},\n    [OBS_KEY_1]            = {.code = kVK_ANSI_1,              .is_valid = true},\n    [OBS_KEY_2]            = {.code = kVK_ANSI_2,              .is_valid = true},\n    [OBS_KEY_3]            = {.code = kVK_ANSI_3,              .is_valid = true},\n    [OBS_KEY_4]            = {.code = kVK_ANSI_4,              .is_valid = true},\n    [OBS_KEY_5]            = {.code = kVK_ANSI_5,              .is_valid = true},\n    [OBS_KEY_6]            = {.code = kVK_ANSI_6,              .is_valid = true},\n    [OBS_KEY_7]            = {.code = kVK_ANSI_7,              .is_valid = true},\n    [OBS_KEY_8]            = {.code = kVK_ANSI_8,              .is_valid = true},\n    [OBS_KEY_9]            = {.code = kVK_ANSI_9,              .is_valid = true},\n    [OBS_KEY_0]            = {.code = kVK_ANSI_0,              .is_valid = true},\n    [OBS_KEY_RETURN]       = {.code = kVK_Return,              .is_valid = true},\n    [OBS_KEY_ESCAPE]       = {.code = kVK_Escape,              .is_valid = true},\n    [OBS_KEY_BACKSPACE]    = {.code = kVK_Delete,              .is_valid = true},\n    [OBS_KEY_TAB]          = {.code = kVK_Tab,                 .is_valid = true},\n    [OBS_KEY_SPACE]        = {.code = kVK_Space,               .is_valid = true},\n    [OBS_KEY_MINUS]        = {.code = kVK_ANSI_Minus,          .is_valid = true},\n    [OBS_KEY_EQUAL]        = {.code = kVK_ANSI_Equal,          .is_valid = true},\n    [OBS_KEY_BRACKETLEFT]  = {.code = kVK_ANSI_LeftBracket,    .is_valid = true},\n    [OBS_KEY_BRACKETRIGHT] = {.code = kVK_ANSI_RightBracket,   .is_valid = true},\n    [OBS_KEY_BACKSLASH]    = {.code = kVK_ANSI_Backslash,      .is_valid = true},\n    [OBS_KEY_SEMICOLON]    = {.code = kVK_ANSI_Semicolon,      .is_valid = true},\n    [OBS_KEY_QUOTE]        = {.code = kVK_ANSI_Quote,          .is_valid = true},\n    [OBS_KEY_DEAD_GRAVE]   = {.code = kVK_ANSI_Grave,          .is_valid = true},\n    [OBS_KEY_COMMA]        = {.code = kVK_ANSI_Comma,          .is_valid = true},\n    [OBS_KEY_PERIOD]       = {.code = kVK_ANSI_Period,         .is_valid = true},\n    [OBS_KEY_SLASH]        = {.code = kVK_ANSI_Slash,          .is_valid = true},\n    [OBS_KEY_CAPSLOCK]     = {.code = kVK_CapsLock,            .is_valid = true},\n    [OBS_KEY_SECTION]      = {.code = kVK_ISO_Section,         .is_valid = true},\n    [OBS_KEY_F1]           = {.code = kVK_F1,                  .is_valid = true},\n    [OBS_KEY_F2]           = {.code = kVK_F2,                  .is_valid = true},\n    [OBS_KEY_F3]           = {.code = kVK_F3,                  .is_valid = true},\n    [OBS_KEY_F4]           = {.code = kVK_F4,                  .is_valid = true},\n    [OBS_KEY_F5]           = {.code = kVK_F5,                  .is_valid = true},\n    [OBS_KEY_F6]           = {.code = kVK_F6,                  .is_valid = true},\n    [OBS_KEY_F7]           = {.code = kVK_F7,                  .is_valid = true},\n    [OBS_KEY_F8]           = {.code = kVK_F8,                  .is_valid = true},\n    [OBS_KEY_F9]           = {.code = kVK_F9,                  .is_valid = true},\n    [OBS_KEY_F10]          = {.code = kVK_F10,                 .is_valid = true},\n    [OBS_KEY_F11]          = {.code = kVK_F11,                 .is_valid = true},\n    [OBS_KEY_F12]          = {.code = kVK_F12,                 .is_valid = true},\n    [OBS_KEY_HELP]         = {.code = kVK_Help,                .is_valid = true},\n    [OBS_KEY_HOME]         = {.code = kVK_Home,                .is_valid = true},\n    [OBS_KEY_PAGEUP]       = {.code = kVK_PageUp,              .is_valid = true},\n    [OBS_KEY_DELETE]       = {.code = kVK_ForwardDelete,       .is_valid = true},\n    [OBS_KEY_END]          = {.code = kVK_End,                 .is_valid = true},\n    [OBS_KEY_PAGEDOWN]     = {.code = kVK_PageDown,            .is_valid = true},\n    [OBS_KEY_RIGHT]        = {.code = kVK_RightArrow,          .is_valid = true},\n    [OBS_KEY_LEFT]         = {.code = kVK_LeftArrow,           .is_valid = true},\n    [OBS_KEY_DOWN]         = {.code = kVK_DownArrow,           .is_valid = true},\n    [OBS_KEY_UP]           = {.code = kVK_UpArrow,             .is_valid = true},\n    [OBS_KEY_CLEAR]        = {.code = kVK_ANSI_KeypadClear,    .is_valid = true},\n    [OBS_KEY_NUMSLASH]     = {.code = kVK_ANSI_KeypadDivide,   .is_valid = true},\n    [OBS_KEY_NUMASTERISK]  = {.code = kVK_ANSI_KeypadMultiply, .is_valid = true},\n    [OBS_KEY_NUMMINUS]     = {.code = kVK_ANSI_KeypadMinus,    .is_valid = true},\n    [OBS_KEY_NUMPLUS]      = {.code = kVK_ANSI_KeypadPlus,     .is_valid = true},\n    [OBS_KEY_ENTER]        = {.code = kVK_ANSI_KeypadEnter,    .is_valid = true},\n    [OBS_KEY_NUM1]         = {.code = kVK_ANSI_Keypad1,        .is_valid = true},\n    [OBS_KEY_NUM2]         = {.code = kVK_ANSI_Keypad2,        .is_valid = true},\n    [OBS_KEY_NUM3]         = {.code = kVK_ANSI_Keypad3,        .is_valid = true},\n    [OBS_KEY_NUM4]         = {.code = kVK_ANSI_Keypad4,        .is_valid = true},\n    [OBS_KEY_NUM5]         = {.code = kVK_ANSI_Keypad5,        .is_valid = true},\n    [OBS_KEY_NUM6]         = {.code = kVK_ANSI_Keypad6,        .is_valid = true},\n    [OBS_KEY_NUM7]         = {.code = kVK_ANSI_Keypad7,        .is_valid = true},\n    [OBS_KEY_NUM8]         = {.code = kVK_ANSI_Keypad8,        .is_valid = true},\n    [OBS_KEY_NUM9]         = {.code = kVK_ANSI_Keypad9,        .is_valid = true},\n    [OBS_KEY_NUM0]         = {.code = kVK_ANSI_Keypad0,        .is_valid = true},\n    [OBS_KEY_NUMPERIOD]    = {.code = kVK_ANSI_KeypadDecimal,  .is_valid = true},\n    [OBS_KEY_NUMEQUAL]     = {.code = kVK_ANSI_KeypadEquals,   .is_valid = true},\n    [OBS_KEY_F13]          = {.code = kVK_F13,                 .is_valid = true},\n    [OBS_KEY_F14]          = {.code = kVK_F14,                 .is_valid = true},\n    [OBS_KEY_F15]          = {.code = kVK_F15,                 .is_valid = true},\n    [OBS_KEY_F16]          = {.code = kVK_F16,                 .is_valid = true},\n    [OBS_KEY_F17]          = {.code = kVK_F17,                 .is_valid = true},\n    [OBS_KEY_F18]          = {.code = kVK_F18,                 .is_valid = true},\n    [OBS_KEY_F19]          = {.code = kVK_F19,                 .is_valid = true},\n    [OBS_KEY_F20]          = {.code = kVK_F20,                 .is_valid = true},\n    [OBS_KEY_CONTROL]      = {.code = kVK_Control,             .is_valid = true},\n    [OBS_KEY_SHIFT]        = {.code = kVK_Shift,               .is_valid = true},\n    [OBS_KEY_ALT]          = {.code = kVK_Option,              .is_valid = true},\n    [OBS_KEY_META]         = {.code = kVK_Command,             .is_valid = true},\n};\n\nstatic const obs_key_desc_t key_descriptions[OBS_KEY_LAST_VALUE] = {\n    [OBS_KEY_SPACE]       = {.desc = \"Space\",      .is_valid = true},\n    [OBS_KEY_NUMEQUAL]    = {.desc = \"= (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUMASTERISK] = {.desc = \"* (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUMPLUS]     = {.desc = \"+ (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUMMINUS]    = {.desc = \"- (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUMPERIOD]   = {.desc = \". (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUMSLASH]    = {.desc = \"/ (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUM0]        = {.desc = \"0 (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUM1]        = {.desc = \"1 (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUM2]        = {.desc = \"2 (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUM3]        = {.desc = \"3 (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUM4]        = {.desc = \"4 (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUM5]        = {.desc = \"5 (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUM6]        = {.desc = \"6 (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUM7]        = {.desc = \"7 (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUM8]        = {.desc = \"8 (Keypad)\", .is_valid = true},\n    [OBS_KEY_NUM9]        = {.desc = \"9 (Keypad)\", .is_valid = true},\n    [OBS_KEY_MOUSE1]      = {.desc = \"Mouse 1\",    .is_valid = true},\n    [OBS_KEY_MOUSE2]      = {.desc = \"Mouse 2\",    .is_valid = true},\n    [OBS_KEY_MOUSE3]      = {.desc = \"Mouse 3\",    .is_valid = true},\n    [OBS_KEY_MOUSE4]      = {.desc = \"Mouse 4\",    .is_valid = true},\n    [OBS_KEY_MOUSE5]      = {.desc = \"Mouse 5\",    .is_valid = true},\n    [OBS_KEY_MOUSE6]      = {.desc = \"Mouse 6\",    .is_valid = true},\n    [OBS_KEY_MOUSE7]      = {.desc = \"Mouse 7\",    .is_valid = true},\n    [OBS_KEY_MOUSE8]      = {.desc = \"Mouse 8\",    .is_valid = true},\n    [OBS_KEY_MOUSE9]      = {.desc = \"Mouse 9\",    .is_valid = true},\n    [OBS_KEY_MOUSE10]     = {.desc = \"Mouse 10\",   .is_valid = true},\n    [OBS_KEY_MOUSE11]     = {.desc = \"Mouse 11\",   .is_valid = true},\n    [OBS_KEY_MOUSE12]     = {.desc = \"Mouse 12\",   .is_valid = true},\n    [OBS_KEY_MOUSE13]     = {.desc = \"Mouse 13\",   .is_valid = true},\n    [OBS_KEY_MOUSE14]     = {.desc = \"Mouse 14\",   .is_valid = true},\n    [OBS_KEY_MOUSE15]     = {.desc = \"Mouse 15\",   .is_valid = true},\n    [OBS_KEY_MOUSE16]     = {.desc = \"Mouse 16\",   .is_valid = true},\n    [OBS_KEY_MOUSE17]     = {.desc = \"Mouse 17\",   .is_valid = true},\n    [OBS_KEY_MOUSE18]     = {.desc = \"Mouse 18\",   .is_valid = true},\n    [OBS_KEY_MOUSE19]     = {.desc = \"Mouse 19\",   .is_valid = true},\n    [OBS_KEY_MOUSE20]     = {.desc = \"Mouse 20\",   .is_valid = true},\n    [OBS_KEY_MOUSE21]     = {.desc = \"Mouse 21\",   .is_valid = true},\n    [OBS_KEY_MOUSE22]     = {.desc = \"Mouse 22\",   .is_valid = true},\n    [OBS_KEY_MOUSE23]     = {.desc = \"Mouse 23\",   .is_valid = true},\n    [OBS_KEY_MOUSE24]     = {.desc = \"Mouse 24\",   .is_valid = true},\n    [OBS_KEY_MOUSE25]     = {.desc = \"Mouse 25\",   .is_valid = true},\n    [OBS_KEY_MOUSE26]     = {.desc = \"Mouse 26\",   .is_valid = true},\n    [OBS_KEY_MOUSE27]     = {.desc = \"Mouse 27\",   .is_valid = true},\n    [OBS_KEY_MOUSE28]     = {.desc = \"Mouse 28\",   .is_valid = true},\n    [OBS_KEY_MOUSE29]     = {.desc = \"Mouse 29\",   .is_valid = true},\n};\n\nstatic const macOS_glyph_desc_t key_glyphs[(keyCodeMask >> 8)] = {\n    [kVK_Return]           = {.glyph = 0x21A9, .is_glyph = true, .is_valid = true},\n    [kVK_Escape]           = {.glyph = 0x238B, .is_glyph = true, .is_valid = true},\n    [kVK_Delete]           = {.glyph = 0x232B, .is_glyph = true, .is_valid = true},\n    [kVK_Tab]              = {.glyph = 0x21e5, .is_glyph = true, .is_valid = true},\n    [kVK_CapsLock]         = {.glyph = 0x21EA, .is_glyph = true, .is_valid = true},\n    [kVK_ANSI_KeypadClear] = {.glyph = 0x2327, .is_glyph = true, .is_valid = true},\n    [kVK_ANSI_KeypadEnter] = {.glyph = 0x2305, .is_glyph = true, .is_valid = true},\n    [kVK_Help]             = {.glyph = 0x003F, .is_glyph = true, .is_valid = true},\n    [kVK_Home]             = {.glyph = 0x2196, .is_glyph = true, .is_valid = true},\n    [kVK_PageUp]           = {.glyph = 0x21de, .is_glyph = true, .is_valid = true},\n    [kVK_ForwardDelete]    = {.glyph = 0x2326, .is_glyph = true, .is_valid = true},\n    [kVK_End]              = {.glyph = 0x2198, .is_glyph = true, .is_valid = true},\n    [kVK_PageDown]         = {.glyph = 0x21df, .is_glyph = true, .is_valid = true},\n    [kVK_Control]          = {.glyph = kControlUnicode, .is_glyph = true, .is_valid = true},\n    [kVK_Shift]            = {.glyph = kShiftUnicode, .is_glyph = true, .is_valid = true},\n    [kVK_Option]           = {.glyph = kOptionUnicode, .is_glyph = true, .is_valid = true},\n    [kVK_Command]          = {.glyph = kCommandUnicode, .is_glyph = true, .is_valid = true},\n    [kVK_RightControl]     = {.glyph = kControlUnicode, .is_glyph = true, .is_valid = true},\n    [kVK_RightShift]       = {.glyph = kShiftUnicode, .is_glyph = true, .is_valid = true},\n    [kVK_RightOption]      = {.glyph = kOptionUnicode, .is_glyph = true, .is_valid = true},\n    [kVK_F1]               = {.desc = \"F1\", .is_valid = true},\n    [kVK_F2]               = {.desc = \"F2\", .is_valid = true},\n    [kVK_F3]               = {.desc = \"F3\", .is_valid = true},\n    [kVK_F4]               = {.desc = \"F4\", .is_valid = true},\n    [kVK_F5]               = {.desc = \"F5\", .is_valid = true},\n    [kVK_F6]               = {.desc = \"F6\", .is_valid = true},\n    [kVK_F7]               = {.desc = \"F7\", .is_valid = true},\n    [kVK_F8]               = {.desc = \"F8\", .is_valid = true},\n    [kVK_F9]               = {.desc = \"F9\", .is_valid = true},\n    [kVK_F10]              = {.desc = \"F10\", .is_valid = true},\n    [kVK_F11]              = {.desc = \"F11\", .is_valid = true},\n    [kVK_F12]              = {.desc = \"F12\", .is_valid = true},\n    [kVK_F13]              = {.desc = \"F13\", .is_valid = true},\n    [kVK_F14]              = {.desc = \"F14\", .is_valid = true},\n    [kVK_F15]              = {.desc = \"F15\", .is_valid = true},\n    [kVK_F16]              = {.desc = \"F16\", .is_valid = true},\n    [kVK_F17]              = {.desc = \"F17\", .is_valid = true},\n    [kVK_F18]              = {.desc = \"F18\", .is_valid = true},\n    [kVK_F19]              = {.desc = \"F19\", .is_valid = true},\n    [kVK_F20]              = {.desc = \"F20\", .is_valid = true}\n};\n\n/* clang-format on */\n\nstruct obs_hotkeys_platform {\n    volatile long refs;\n    CFMachPortRef eventTap;\n    bool is_key_down[OBS_KEY_LAST_VALUE];\n    TISInputSourceRef tis;\n    CFDataRef layout_data;\n    UCKeyboardLayout *layout;\n};\n\n// MARK: macOS Hotkey Implementation\n#define OBS_COCOA_MODIFIER_SIZE (int) 7\n\nstatic char string_control[OBS_COCOA_MODIFIER_SIZE];\nstatic char string_option[OBS_COCOA_MODIFIER_SIZE];\nstatic char string_shift[OBS_COCOA_MODIFIER_SIZE];\nstatic char string_command[OBS_COCOA_MODIFIER_SIZE];\nstatic dispatch_once_t onceToken;\n\nstatic void hotkeys_retain(obs_hotkeys_platform_t *platform)\n{\n    os_atomic_inc_long(&platform->refs);\n}\n\nstatic void hotkeys_release(obs_hotkeys_platform_t *platform)\n{\n    if (os_atomic_dec_long(&platform->refs) == -1) {\n        if (platform->tis) {\n            CFRelease(platform->tis);\n            platform->tis = NULL;\n        }\n\n        if (platform->layout_data) {\n            CFRelease(platform->layout_data);\n            platform->layout_data = NULL;\n        }\n\n        if (platform->eventTap) {\n            CGEventTapEnable(platform->eventTap, false);\n            CFRelease(platform->eventTap);\n            platform->eventTap = NULL;\n        }\n\n        bfree(platform);\n    }\n}\n\nstatic bool obs_key_to_localized_string(obs_key_t key, struct dstr *str)\n{\n    if (key < OBS_KEY_LAST_VALUE && !key_descriptions[key].is_valid) {\n        return false;\n    }\n\n    dstr_copy(str, obs_get_hotkey_translation(key, key_descriptions[key].desc));\n    return true;\n}\n\nstatic bool key_code_to_string(int code, struct dstr *str)\n{\n    if (code < INVALID_KEY) {\n        macOS_glyph_desc_t glyph = key_glyphs[code];\n\n        if (glyph.is_valid && glyph.is_glyph && glyph.glyph > 0) {\n            dstr_from_wcs(str, (wchar_t[]) {glyph.glyph, 0});\n        } else if (glyph.is_valid && glyph.desc) {\n            dstr_copy(str, glyph.desc);\n        } else {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nstatic bool log_layout_name(TISInputSourceRef tis)\n{\n    struct dstr layout_name = {0};\n    CFStringRef sid = (CFStringRef) TISGetInputSourceProperty(tis, kTISPropertyInputSourceID);\n\n    if (!sid) {\n        blog(LOG_ERROR, \"hotkeys-cocoa: Unable to get input source ID\");\n        return false;\n    }\n\n    if (!dstr_from_cfstring(&layout_name, sid)) {\n        blog(LOG_ERROR, \"hotkeys-cocoa: Unable to convert input source ID\");\n        dstr_free(&layout_name);\n        return false;\n    }\n\n    blog(LOG_INFO, \"hotkeys-cocoa: Using keyboard layout '%s'\", layout_name.array);\n\n    dstr_free(&layout_name);\n    return true;\n}\n\n// MARK: macOS Hotkey CoreFoundation Callbacks\n\nstatic CGEventRef KeyboardEventProc(CGEventTapProxy proxy __unused, CGEventType type, CGEventRef event, void *userInfo)\n{\n    obs_hotkeys_platform_t *platform = userInfo;\n\n    const CGEventFlags flags = CGEventGetFlags(event);\n    platform->is_key_down[OBS_KEY_SHIFT] = !!(flags & kCGEventFlagMaskShift);\n    platform->is_key_down[OBS_KEY_ALT] = !!(flags & kCGEventFlagMaskAlternate);\n    platform->is_key_down[OBS_KEY_META] = !!(flags & kCGEventFlagMaskCommand);\n    platform->is_key_down[OBS_KEY_CONTROL] = !!(flags & kCGEventFlagMaskControl);\n\n    switch (type) {\n        case kCGEventKeyDown: {\n            const int64_t keycode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);\n            platform->is_key_down[obs_key_from_virtual_key(keycode)] = true;\n            break;\n        }\n        case kCGEventKeyUp: {\n            const int64_t keycode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);\n            platform->is_key_down[obs_key_from_virtual_key(keycode)] = false;\n            break;\n        }\n        case kCGEventFlagsChanged: {\n            break;\n        }\n        case kCGEventTapDisabledByTimeout: {\n            blog(LOG_DEBUG, \"[hotkeys-cocoa]: Hotkey event tap disabled by timeout. Reenabling...\");\n            CGEventTapEnable(platform->eventTap, true);\n            break;\n        }\n        default: {\n            blog(LOG_WARNING, \"[hotkeys-cocoa]: Received unexpected event with code '%d'\", type);\n        }\n    }\n    return event;\n}\n\nstatic void InputMethodChangedProc(CFNotificationCenterRef center __unused, void *observer,\n                                   CFNotificationName name __unused, const void *object __unused,\n                                   CFDictionaryRef userInfo __unused)\n{\n    struct obs_core_hotkeys *hotkeys = observer;\n    obs_hotkeys_platform_t *platform = hotkeys->platform_context;\n\n    pthread_mutex_lock(&hotkeys->mutex);\n\n    if (platform->layout_data) {\n        CFRelease(platform->layout_data);\n    }\n\n    platform->tis = TISCopyCurrentKeyboardLayoutInputSource();\n    platform->layout_data = (CFDataRef) TISGetInputSourceProperty(platform->tis, kTISPropertyUnicodeKeyLayoutData);\n\n    if (!platform->layout_data) {\n        blog(LOG_ERROR, \"hotkeys-cocoa: Failed to retrieve keyboard layout data\");\n        hotkeys->platform_context = NULL;\n        pthread_mutex_unlock(&hotkeys->mutex);\n        hotkeys_release(platform);\n        return;\n    }\n\n    CFRetain(platform->layout_data);\n    platform->layout = (UCKeyboardLayout *) CFDataGetBytePtr(platform->layout_data);\n\n    pthread_mutex_unlock(&hotkeys->mutex);\n}\n\n// MARK: macOS Hotkey API Implementation\n\nbool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)\n{\n    CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), hotkeys, InputMethodChangedProc,\n                                    kTISNotifySelectedKeyboardInputSourceChanged, NULL,\n                                    CFNotificationSuspensionBehaviorDeliverImmediately);\n\n    obs_hotkeys_platform_t *platform = bzalloc(sizeof(obs_hotkeys_platform_t));\n\n    const bool has_event_access = CGPreflightListenEventAccess();\n    if (has_event_access) {\n        platform->eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly,\n                                              CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp) |\n                                                  CGEventMaskBit(kCGEventFlagsChanged),\n                                              KeyboardEventProc, platform);\n        if (!platform->eventTap) {\n            blog(LOG_WARNING, \"[hotkeys-cocoa]: Couldn't create hotkey event tap.\");\n            hotkeys_release(platform);\n            platform = NULL;\n            return false;\n        }\n        CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, platform->eventTap, 0);\n        CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);\n        CFRelease(source);\n\n        CGEventTapEnable(platform->eventTap, true);\n    } else {\n        blog(LOG_WARNING, \"[hotkeys-cocoa]: No event permissions, could not add global hotkeys.\");\n    }\n\n    platform->tis = TISCopyCurrentKeyboardLayoutInputSource();\n    platform->layout_data = (CFDataRef) TISGetInputSourceProperty(platform->tis, kTISPropertyUnicodeKeyLayoutData);\n\n    if (!platform->layout_data) {\n        blog(LOG_ERROR, \"hotkeys-cocoa: Failed to retrieve keyboard layout data\");\n        hotkeys_release(platform);\n        platform = NULL;\n        return false;\n    }\n\n    CFRetain(platform->layout_data);\n    platform->layout = (UCKeyboardLayout *) CFDataGetBytePtr(platform->layout_data);\n\n    obs_hotkeys_platform_t *currentPlatform;\n\n    pthread_mutex_lock(&hotkeys->mutex);\n\n    currentPlatform = hotkeys->platform_context;\n\n    if (platform && currentPlatform && platform->layout_data == currentPlatform->layout_data) {\n        pthread_mutex_unlock(&hotkeys->mutex);\n        hotkeys_release(platform);\n        return true;\n    }\n\n    hotkeys->platform_context = platform;\n\n    log_layout_name(platform->tis);\n\n    pthread_mutex_unlock(&hotkeys->mutex);\n\n    calldata_t parameters = {0};\n    signal_handler_signal(hotkeys->signals, \"hotkey_layout_change\", &parameters);\n\n    if (currentPlatform) {\n        hotkeys_release(currentPlatform);\n    }\n\n    bool hasPlatformContext = hotkeys->platform_context != NULL;\n\n    return hasPlatformContext;\n}\n\nvoid obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)\n{\n    CFNotificationCenterRemoveEveryObserver(CFNotificationCenterGetDistributedCenter(), hotkeys);\n\n    hotkeys_release(hotkeys->platform_context);\n}\n\nint obs_key_to_virtual_key(obs_key_t key)\n{\n    if (virtual_keys[key].is_valid) {\n        return virtual_keys[key].code;\n    } else {\n        return INVALID_KEY;\n    }\n}\n\nobs_key_t obs_key_from_virtual_key(int keyCode)\n{\n    for (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {\n        if (virtual_keys[i].is_valid && virtual_keys[i].code == keyCode) {\n            return (obs_key_t) i;\n        }\n    }\n\n    return OBS_KEY_NONE;\n}\n\nbool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *platform, obs_key_t key)\n{\n    if (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {\n        int button = key - 1;\n\n        NSUInteger buttons = [NSEvent pressedMouseButtons];\n\n        if ((buttons & (1 << button)) != 0) {\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    if (!platform) {\n        return false;\n    }\n\n    if (key >= OBS_KEY_LAST_VALUE) {\n        return false;\n    }\n\n    return platform->is_key_down[key];\n}\n\nstatic void unichar_to_utf8(const UniChar *character, char *buffer)\n{\n    CFStringRef string = CFStringCreateWithCharactersNoCopy(NULL, character, 2, kCFAllocatorNull);\n\n    if (!string) {\n        blog(LOG_ERROR, \"hotkey-cocoa: Unable to create CFStringRef with UniChar character\");\n        return;\n    }\n\n    if (!CFStringGetCString(string, buffer, OBS_COCOA_MODIFIER_SIZE, kCFStringEncodingUTF8)) {\n        blog(LOG_ERROR, \"hotkey-cocoa: Error while populating buffer with glyph %d (0x%x)\", character[0], character[0]);\n    }\n\n    CFRelease(string);\n}\n\nvoid obs_key_combination_to_str(obs_key_combination_t key, struct dstr *str)\n{\n    struct dstr keyString = {0};\n\n    if (key.key != OBS_KEY_NONE) {\n        obs_key_to_str(key.key, &keyString);\n    }\n\n    dispatch_once(&onceToken, ^{\n        const UniChar controlCharacter[] = {kControlUnicode, 0};\n        const UniChar optionCharacter[] = {kOptionUnicode, 0};\n        const UniChar shiftCharacter[] = {kShiftUnicode, 0};\n        const UniChar commandCharacter[] = {kCommandUnicode, 0};\n\n        unichar_to_utf8(controlCharacter, string_control);\n        unichar_to_utf8(optionCharacter, string_option);\n        unichar_to_utf8(shiftCharacter, string_shift);\n        unichar_to_utf8(commandCharacter, string_command);\n    });\n\n    const char *modifier_control = (key.modifiers & INTERACT_CONTROL_KEY) ? string_control : \"\";\n    const char *modifier_option = (key.modifiers & INTERACT_ALT_KEY) ? string_option : \"\";\n    const char *modifier_shift = (key.modifiers & INTERACT_SHIFT_KEY) ? string_shift : \"\";\n    const char *modifier_command = (key.modifiers & INTERACT_COMMAND_KEY) ? string_command : \"\";\n    const char *modifier_key = keyString.len ? keyString.array : \"\";\n\n    dstr_printf(str, \"%s%s%s%s%s\", modifier_control, modifier_option, modifier_shift, modifier_command, modifier_key);\n\n    dstr_free(&keyString);\n}\n\nvoid obs_key_to_str(obs_key_t key, struct dstr *str)\n{\n    const UniCharCount maxLength = 16;\n    UniChar buffer[16];\n\n    if (obs_key_to_localized_string(key, str)) {\n        return;\n    }\n\n    int code = obs_key_to_virtual_key(key);\n    if (key_code_to_string(code, str)) {\n        return;\n    }\n\n    if (code == INVALID_KEY) {\n        const char *keyName = obs_key_to_name(key);\n        blog(LOG_ERROR, \"hotkey-cocoa: Got invalid key while translating '%d' (%s)\", key, keyName);\n        dstr_copy(str, keyName);\n        return;\n    }\n\n    struct obs_hotkeys_platform *platform = NULL;\n\n    if (obs) {\n        pthread_mutex_lock(&obs->hotkeys.mutex);\n        platform = obs->hotkeys.platform_context;\n        hotkeys_retain(platform);\n        pthread_mutex_unlock(&obs->hotkeys.mutex);\n    }\n\n    if (!platform) {\n        const char *keyName = obs_key_to_name(key);\n        blog(LOG_ERROR, \"hotkey-cocoa: Could not get hotkey platform while translating '%d' (%s)\", key, keyName);\n        dstr_copy(str, keyName);\n        return;\n    }\n\n    UInt32 deadKeyState = 0;\n    UniCharCount length = 0;\n\n    OSStatus err = UCKeyTranslate(platform->layout, code, kUCKeyActionDown, ((alphaLock >> 8) & 0xFF), LMGetKbdType(),\n                                  kUCKeyTranslateNoDeadKeysBit, &deadKeyState, maxLength, &length, buffer);\n\n    if (err == noErr && length <= 0 && deadKeyState) {\n        err = UCKeyTranslate(platform->layout, kVK_Space, kUCKeyActionDown, ((alphaLock >> 8) & 0xFF), LMGetKbdType(),\n                             kUCKeyTranslateNoDeadKeysBit, &deadKeyState, maxLength, &length, buffer);\n    }\n\n    hotkeys_release(platform);\n\n    if (err != noErr) {\n        const char *keyName = obs_key_to_name(key);\n        blog(LOG_ERROR, \"hotkey-cocoa: Error while translating '%d' (0x%x, %s) to string: %d\", key, code, keyName, err);\n        dstr_copy(str, keyName);\n        return;\n    }\n\n    if (length == 0) {\n        const char *keyName = obs_key_to_name(key);\n        blog(LOG_ERROR, \"hotkey-cocoa: Got zero length string while translating '%d' (0x%x, %s) to string\", key, code,\n             keyName);\n        dstr_copy(str, keyName);\n        return;\n    }\n\n    CFStringRef string = CFStringCreateWithCharactersNoCopy(NULL, buffer, length, kCFAllocatorNull);\n\n    if (!string) {\n        const char *keyName = obs_key_to_name(key);\n        blog(LOG_ERROR, \"hotkey-cocoa: Could not create CFStringRef while translating '%d' (0x%x, %s) to string\", key,\n             code, keyName);\n        dstr_copy(str, keyName);\n        return;\n    }\n\n    if (!dstr_from_cfstring(str, string)) {\n        const char *keyName = obs_key_to_name(key);\n        blog(LOG_ERROR, \"hotkey-cocoa: Could not translate CFStringRef to CString while translating '%d' (0x%x, %s)\",\n             key, code, keyName);\n        CFRelease(string);\n        dstr_copy(str, keyName);\n        return;\n    }\n\n    CFRelease(string);\n    return;\n}\n"
  },
  {
    "path": "libobs/obs-config.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n/*\n * LIBOBS_API_VER is returned by module_version in each module.\n *\n * Libobs uses semantic versioning.  See http://semver.org/ for more\n * information.\n */\n\n/*\n * Increment if major breaking API changes\n */\n#define LIBOBS_API_MAJOR_VER 32\n\n/*\n * Increment if backward-compatible additions\n *\n * Reset to zero each major version\n */\n#define LIBOBS_API_MINOR_VER 1\n\n/*\n * Increment if backward-compatible bug fix\n *\n * Reset to zero each major or minor version\n */\n#define LIBOBS_API_PATCH_VER 0\n\n#define MAKE_SEMANTIC_VERSION(major, minor, patch) ((major << 24) | (minor << 16) | patch)\n\n#define LIBOBS_API_VER MAKE_SEMANTIC_VERSION(LIBOBS_API_MAJOR_VER, LIBOBS_API_MINOR_VER, LIBOBS_API_PATCH_VER)\n\n#include \"obsconfig.h\"\n\n#define OBS_INSTALL_DATA_PATH OBS_INSTALL_PREFIX \"/\" OBS_DATA_PATH\n"
  },
  {
    "path": "libobs/obs-data.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"util/bmem.h\"\n#include \"util/threading.h\"\n#include \"util/dstr.h\"\n#include \"util/darray.h\"\n#include \"util/platform.h\"\n#include \"util/uthash.h\"\n#include \"graphics/vec2.h\"\n#include \"graphics/vec3.h\"\n#include \"graphics/vec4.h\"\n#include \"graphics/quat.h\"\n#include \"obs-data.h\"\n\n#include <jansson.h>\n\nstruct obs_data_item {\n\tvolatile long ref;\n\tconst char *name;\n\tstruct obs_data *parent;\n\tUT_hash_handle hh;\n\tenum obs_data_type type;\n\tsize_t name_len;\n\tsize_t data_len;\n\tsize_t data_size;\n\tsize_t default_len;\n\tsize_t default_size;\n\tsize_t autoselect_size;\n\tsize_t capacity;\n};\n\nstruct obs_data {\n\tvolatile long ref;\n\tchar *json;\n\tstruct obs_data_item *items;\n};\n\nstruct obs_data_array {\n\tvolatile long ref;\n\tDARRAY(obs_data_t *) objects;\n};\n\nstruct obs_data_number {\n\tenum obs_data_number_type type;\n\tunion {\n\t\tlong long int_val;\n\t\tdouble double_val;\n\t};\n};\n\n/* ------------------------------------------------------------------------- */\n/* Item structure, designed to be one allocation only */\n\nstatic inline size_t get_align_size(size_t size)\n{\n\tconst size_t alignment = base_get_alignment();\n\treturn (size + alignment - 1) & ~(alignment - 1);\n}\n\n/* ensures data after the name has alignment (in case of SSE) */\nstatic inline size_t get_name_align_size(const char *name)\n{\n\tsize_t name_size = strlen(name) + 1;\n\tsize_t alignment = base_get_alignment();\n\tsize_t total_size;\n\n\ttotal_size = sizeof(struct obs_data_item) + (name_size + alignment - 1);\n\ttotal_size &= ~(alignment - 1);\n\n\treturn total_size - sizeof(struct obs_data_item);\n}\n\nstatic inline char *get_item_name(struct obs_data_item *item)\n{\n\treturn (char *)item + sizeof(struct obs_data_item);\n}\n\nstatic inline void *get_data_ptr(obs_data_item_t *item)\n{\n\treturn (uint8_t *)get_item_name(item) + item->name_len;\n}\n\nstatic inline void *get_item_data(struct obs_data_item *item)\n{\n\tif (!item->data_size && !item->default_size && !item->autoselect_size)\n\t\treturn NULL;\n\treturn get_data_ptr(item);\n}\n\nstatic inline void *get_default_data_ptr(obs_data_item_t *item)\n{\n\treturn (uint8_t *)get_data_ptr(item) + item->data_len;\n}\n\nstatic inline void *get_item_default_data(struct obs_data_item *item)\n{\n\treturn item->default_size ? get_default_data_ptr(item) : NULL;\n}\n\nstatic inline void *get_autoselect_data_ptr(obs_data_item_t *item)\n{\n\treturn (uint8_t *)get_default_data_ptr(item) + item->default_len;\n}\n\nstatic inline void *get_item_autoselect_data(struct obs_data_item *item)\n{\n\treturn item->autoselect_size ? get_autoselect_data_ptr(item) : NULL;\n}\n\nstatic inline size_t obs_data_item_total_size(struct obs_data_item *item)\n{\n\treturn sizeof(struct obs_data_item) + item->name_len + item->data_len + item->default_len +\n\t       item->autoselect_size;\n}\n\nstatic inline obs_data_t *get_item_obj(struct obs_data_item *item)\n{\n\tif (!item)\n\t\treturn NULL;\n\n\tobs_data_t **data = get_item_data(item);\n\tif (!data)\n\t\treturn NULL;\n\n\treturn *data;\n}\n\nstatic inline obs_data_t *get_item_default_obj(struct obs_data_item *item)\n{\n\tif (!item || !item->default_size)\n\t\treturn NULL;\n\n\treturn *(obs_data_t **)get_default_data_ptr(item);\n}\n\nstatic inline obs_data_t *get_item_autoselect_obj(struct obs_data_item *item)\n{\n\tif (!item || !item->autoselect_size)\n\t\treturn NULL;\n\n\treturn *(obs_data_t **)get_autoselect_data_ptr(item);\n}\n\nstatic inline obs_data_array_t *get_item_array(struct obs_data_item *item)\n{\n\tobs_data_array_t **array;\n\n\tif (!item)\n\t\treturn NULL;\n\n\tarray = (obs_data_array_t **)get_item_data(item);\n\treturn array ? *array : NULL;\n}\n\nstatic inline obs_data_array_t *get_item_default_array(struct obs_data_item *item)\n{\n\tif (!item || !item->default_size)\n\t\treturn NULL;\n\n\treturn *(obs_data_array_t **)get_default_data_ptr(item);\n}\n\nstatic inline obs_data_array_t *get_item_autoselect_array(struct obs_data_item *item)\n{\n\tif (!item || !item->autoselect_size)\n\t\treturn NULL;\n\n\treturn *(obs_data_array_t **)get_autoselect_data_ptr(item);\n}\n\nstatic inline void item_data_release(struct obs_data_item *item)\n{\n\tif (!obs_data_item_has_user_value(item))\n\t\treturn;\n\n\tif (item->type == OBS_DATA_OBJECT) {\n\t\tobs_data_t *obj = get_item_obj(item);\n\t\tobs_data_release(obj);\n\n\t} else if (item->type == OBS_DATA_ARRAY) {\n\t\tobs_data_array_t *array = get_item_array(item);\n\t\tobs_data_array_release(array);\n\t}\n}\n\nstatic inline void item_default_data_release(struct obs_data_item *item)\n{\n\tif (item->type == OBS_DATA_OBJECT) {\n\t\tobs_data_t *obj = get_item_default_obj(item);\n\t\tobs_data_release(obj);\n\n\t} else if (item->type == OBS_DATA_ARRAY) {\n\t\tobs_data_array_t *array = get_item_default_array(item);\n\t\tobs_data_array_release(array);\n\t}\n}\n\nstatic inline void item_autoselect_data_release(struct obs_data_item *item)\n{\n\tif (item->type == OBS_DATA_OBJECT) {\n\t\tobs_data_t *obj = get_item_autoselect_obj(item);\n\t\tobs_data_release(obj);\n\n\t} else if (item->type == OBS_DATA_ARRAY) {\n\t\tobs_data_array_t *array = get_item_autoselect_array(item);\n\t\tobs_data_array_release(array);\n\t}\n}\n\nstatic inline void item_data_addref(struct obs_data_item *item)\n{\n\tif (item->type == OBS_DATA_OBJECT) {\n\t\tobs_data_t *obj = get_item_obj(item);\n\t\tobs_data_addref(obj);\n\n\t} else if (item->type == OBS_DATA_ARRAY) {\n\t\tobs_data_array_t *array = get_item_array(item);\n\t\tobs_data_array_addref(array);\n\t}\n}\n\nstatic inline void item_default_data_addref(struct obs_data_item *item)\n{\n\tif (!item->data_size)\n\t\treturn;\n\n\tif (item->type == OBS_DATA_OBJECT) {\n\t\tobs_data_t *obj = get_item_default_obj(item);\n\t\tobs_data_addref(obj);\n\n\t} else if (item->type == OBS_DATA_ARRAY) {\n\t\tobs_data_array_t *array = get_item_default_array(item);\n\t\tobs_data_array_addref(array);\n\t}\n}\n\nstatic inline void item_autoselect_data_addref(struct obs_data_item *item)\n{\n\tif (item->type == OBS_DATA_OBJECT) {\n\t\tobs_data_t *obj = get_item_autoselect_obj(item);\n\t\tobs_data_addref(obj);\n\n\t} else if (item->type == OBS_DATA_ARRAY) {\n\t\tobs_data_array_t *array = get_item_autoselect_array(item);\n\t\tobs_data_array_addref(array);\n\t}\n}\n\nstatic struct obs_data_item *obs_data_item_create(const char *name, const void *data, size_t size,\n\t\t\t\t\t\t  enum obs_data_type type, bool default_data, bool autoselect_data)\n{\n\tstruct obs_data_item *item;\n\tsize_t name_size, total_size;\n\n\tif (!name || !data)\n\t\treturn NULL;\n\n\tname_size = get_name_align_size(name);\n\ttotal_size = name_size + sizeof(struct obs_data_item) + size;\n\n\titem = bzalloc(total_size);\n\n\titem->capacity = total_size;\n\titem->type = type;\n\titem->name_len = name_size;\n\titem->ref = 1;\n\n\tif (default_data) {\n\t\titem->default_len = size;\n\t\titem->default_size = size;\n\n\t} else if (autoselect_data) {\n\t\titem->autoselect_size = size;\n\n\t} else {\n\t\titem->data_len = size;\n\t\titem->data_size = size;\n\t}\n\n\tchar *name_ptr = get_item_name(item);\n\titem->name = name_ptr;\n\n\tstrcpy(name_ptr, name);\n\tmemcpy(get_item_data(item), data, size);\n\n\titem_data_addref(item);\n\treturn item;\n}\n\nstatic inline void obs_data_item_detach(struct obs_data_item *item)\n{\n\tif (item->parent) {\n\t\tHASH_DEL(item->parent->items, item);\n\t\titem->parent = NULL;\n\t}\n}\n\nstatic inline void obs_data_item_reattach(struct obs_data *parent, struct obs_data_item *item)\n{\n\tif (parent) {\n\t\tHASH_ADD_STR(parent->items, name, item);\n\t\titem->parent = parent;\n\t}\n}\n\nstatic struct obs_data_item *obs_data_item_ensure_capacity(struct obs_data_item *item)\n{\n\tsize_t new_size = obs_data_item_total_size(item);\n\tstruct obs_data_item *new_item;\n\n\tif (item->capacity >= new_size)\n\t\treturn item;\n\n\tstruct obs_data *parent = item->parent;\n\tobs_data_item_detach(item);\n\n\tnew_item = brealloc(item, new_size);\n\tnew_item->capacity = new_size;\n\tnew_item->name = get_item_name(new_item);\n\n\tobs_data_item_reattach(parent, new_item);\n\n\treturn new_item;\n}\n\nstatic inline void obs_data_item_destroy(struct obs_data_item *item)\n{\n\tif (item->parent)\n\t\tHASH_DEL(item->parent->items, item);\n\n\titem_data_release(item);\n\titem_default_data_release(item);\n\titem_autoselect_data_release(item);\n\tobs_data_item_detach(item);\n\tbfree(item);\n}\n\nstatic inline void move_data(obs_data_item_t *old_item, void *old_data, obs_data_item_t *item, void *data, size_t len)\n{\n\tptrdiff_t old_offset = (uint8_t *)old_data - (uint8_t *)old_item;\n\tptrdiff_t new_offset = (uint8_t *)data - (uint8_t *)item;\n\n\tif (!old_data)\n\t\treturn;\n\n\tmemmove((uint8_t *)item + new_offset, (uint8_t *)item + old_offset, len);\n}\n\nstatic inline void obs_data_item_setdata(struct obs_data_item **p_item, const void *data, size_t size,\n\t\t\t\t\t enum obs_data_type type)\n{\n\tif (!p_item || !*p_item)\n\t\treturn;\n\n\tstruct obs_data_item *item = *p_item;\n\tptrdiff_t old_default_data_pos = (uint8_t *)get_default_data_ptr(item) - (uint8_t *)item;\n\titem_data_release(item);\n\n\titem->data_size = size;\n\titem->type = type;\n\titem->data_len = (item->default_size || item->autoselect_size) ? get_align_size(size) : size;\n\titem = obs_data_item_ensure_capacity(item);\n\n\tif (item->default_size || item->autoselect_size)\n\t\tmemmove(get_default_data_ptr(item), (uint8_t *)item + old_default_data_pos,\n\t\t\titem->default_len + item->autoselect_size);\n\n\tif (size) {\n\t\tmemcpy(get_item_data(item), data, size);\n\t\titem_data_addref(item);\n\t}\n\n\t*p_item = item;\n}\n\nstatic inline void obs_data_item_set_default_data(struct obs_data_item **p_item, const void *data, size_t size,\n\t\t\t\t\t\t  enum obs_data_type type)\n{\n\tif (!p_item || !*p_item)\n\t\treturn;\n\n\tstruct obs_data_item *item = *p_item;\n\tvoid *old_autoselect_data = get_autoselect_data_ptr(item);\n\titem_default_data_release(item);\n\n\titem->type = type;\n\titem->default_size = size;\n\titem->default_len = item->autoselect_size ? get_align_size(size) : size;\n\titem->data_len = item->data_size ? get_align_size(item->data_size) : 0;\n\titem = obs_data_item_ensure_capacity(item);\n\n\tif (item->autoselect_size)\n\t\tmove_data(*p_item, old_autoselect_data, item, get_autoselect_data_ptr(item), item->autoselect_size);\n\n\tif (size) {\n\t\tmemcpy(get_item_default_data(item), data, size);\n\t\titem_default_data_addref(item);\n\t}\n\n\t*p_item = item;\n}\n\nstatic inline void obs_data_item_set_autoselect_data(struct obs_data_item **p_item, const void *data, size_t size,\n\t\t\t\t\t\t     enum obs_data_type type)\n{\n\tif (!p_item || !*p_item)\n\t\treturn;\n\n\tstruct obs_data_item *item = *p_item;\n\titem_autoselect_data_release(item);\n\n\titem->autoselect_size = size;\n\titem->type = type;\n\titem->data_len = item->data_size ? get_align_size(item->data_size) : 0;\n\titem->default_len = item->default_size ? get_align_size(item->default_size) : 0;\n\titem = obs_data_item_ensure_capacity(item);\n\n\tif (size) {\n\t\tmemcpy(get_item_autoselect_data(item), data, size);\n\t\titem_autoselect_data_addref(item);\n\t}\n\n\t*p_item = item;\n}\n\n/* ------------------------------------------------------------------------- */\n\nstatic void obs_data_add_json_item(obs_data_t *data, const char *key, json_t *json);\n\nstatic inline void obs_data_add_json_object_data(obs_data_t *data, json_t *jobj)\n{\n\tconst char *item_key;\n\tjson_t *jitem;\n\n\tjson_object_foreach (jobj, item_key, jitem) {\n\t\tobs_data_add_json_item(data, item_key, jitem);\n\t}\n}\n\nstatic inline void obs_data_add_json_object(obs_data_t *data, const char *key, json_t *jobj)\n{\n\tobs_data_t *sub_obj = obs_data_create();\n\n\tobs_data_add_json_object_data(sub_obj, jobj);\n\tobs_data_set_obj(data, key, sub_obj);\n\tobs_data_release(sub_obj);\n}\n\nstatic void obs_data_add_json_array(obs_data_t *data, const char *key, json_t *jarray)\n{\n\tobs_data_array_t *array = obs_data_array_create();\n\tsize_t idx;\n\tjson_t *jitem;\n\n\tjson_array_foreach (jarray, idx, jitem) {\n\t\tobs_data_t *item;\n\n\t\tif (!json_is_object(jitem))\n\t\t\tcontinue;\n\n\t\titem = obs_data_create();\n\t\tobs_data_add_json_object_data(item, jitem);\n\t\tobs_data_array_push_back(array, item);\n\t\tobs_data_release(item);\n\t}\n\n\tobs_data_set_array(data, key, array);\n\tobs_data_array_release(array);\n}\n\nstatic inline void obs_data_add_json_null(obs_data_t *data, const char *key)\n{\n\tobs_data_set_obj(data, key, NULL);\n}\n\nstatic void obs_data_add_json_item(obs_data_t *data, const char *key, json_t *json)\n{\n\tif (json_is_object(json))\n\t\tobs_data_add_json_object(data, key, json);\n\telse if (json_is_array(json))\n\t\tobs_data_add_json_array(data, key, json);\n\telse if (json_is_string(json))\n\t\tobs_data_set_string(data, key, json_string_value(json));\n\telse if (json_is_integer(json))\n\t\tobs_data_set_int(data, key, json_integer_value(json));\n\telse if (json_is_real(json))\n\t\tobs_data_set_double(data, key, json_real_value(json));\n\telse if (json_is_true(json))\n\t\tobs_data_set_bool(data, key, true);\n\telse if (json_is_false(json))\n\t\tobs_data_set_bool(data, key, false);\n\telse if (json_is_null(json))\n\t\tobs_data_add_json_null(data, key);\n}\n\n/* ------------------------------------------------------------------------- */\n\nstatic inline void set_json_string(json_t *json, const char *name, obs_data_item_t *item)\n{\n\tconst char *val = obs_data_item_get_string(item);\n\tjson_object_set_new(json, name, json_string(val));\n}\n\nstatic inline void set_json_number(json_t *json, const char *name, obs_data_item_t *item)\n{\n\tenum obs_data_number_type type = obs_data_item_numtype(item);\n\n\tif (type == OBS_DATA_NUM_INT) {\n\t\tlong long val = obs_data_item_get_int(item);\n\t\tjson_object_set_new(json, name, json_integer(val));\n\t} else {\n\t\tdouble val = obs_data_item_get_double(item);\n\t\tjson_object_set_new(json, name, json_real(val));\n\t}\n}\n\nstatic inline void set_json_bool(json_t *json, const char *name, obs_data_item_t *item)\n{\n\tbool val = obs_data_item_get_bool(item);\n\tjson_object_set_new(json, name, val ? json_true() : json_false());\n}\n\nstatic json_t *obs_data_to_json(obs_data_t *data, bool with_defaults);\n\nstatic inline void set_json_obj(json_t *json, const char *name, obs_data_item_t *item, bool with_defaults)\n{\n\tobs_data_t *obj = obs_data_item_get_obj(item);\n\tjson_object_set_new(json, name, obs_data_to_json(obj, with_defaults));\n\tobs_data_release(obj);\n}\n\nstatic inline void set_json_array(json_t *json, const char *name, obs_data_item_t *item, bool with_defaults)\n{\n\tjson_t *jarray = json_array();\n\tobs_data_array_t *array = obs_data_item_get_array(item);\n\tsize_t count = obs_data_array_count(array);\n\n\tfor (size_t idx = 0; idx < count; idx++) {\n\t\tobs_data_t *sub_item = obs_data_array_item(array, idx);\n\t\tjson_t *jitem = obs_data_to_json(sub_item, with_defaults);\n\t\tjson_array_append_new(jarray, jitem);\n\t\tobs_data_release(sub_item);\n\t}\n\n\tjson_object_set_new(json, name, jarray);\n\tobs_data_array_release(array);\n}\n\nstatic json_t *obs_data_to_json(obs_data_t *data, bool with_defaults)\n{\n\tif (!data)\n\t\treturn json_null();\n\n\tjson_t *json = json_object();\n\n\tobs_data_item_t *item = NULL;\n\tobs_data_item_t *temp = NULL;\n\n\tHASH_ITER (hh, data->items, item, temp) {\n\t\tenum obs_data_type type = obs_data_item_gettype(item);\n\t\tconst char *name = get_item_name(item);\n\n\t\tif (!with_defaults && !obs_data_item_has_user_value(item))\n\t\t\tcontinue;\n\n\t\tif (type == OBS_DATA_STRING)\n\t\t\tset_json_string(json, name, item);\n\t\telse if (type == OBS_DATA_NUMBER)\n\t\t\tset_json_number(json, name, item);\n\t\telse if (type == OBS_DATA_BOOLEAN)\n\t\t\tset_json_bool(json, name, item);\n\t\telse if (type == OBS_DATA_OBJECT)\n\t\t\tset_json_obj(json, name, item, with_defaults);\n\t\telse if (type == OBS_DATA_ARRAY)\n\t\t\tset_json_array(json, name, item, with_defaults);\n\t}\n\n\treturn json;\n}\n\n/* ------------------------------------------------------------------------- */\n\nobs_data_t *obs_data_create()\n{\n\tstruct obs_data *data = bzalloc(sizeof(struct obs_data));\n\tdata->ref = 1;\n\n\treturn data;\n}\n\nobs_data_t *obs_data_create_from_json(const char *json_string)\n{\n\tobs_data_t *data = obs_data_create();\n\n\tjson_error_t error;\n\tjson_t *root = json_loads(json_string, JSON_REJECT_DUPLICATES, &error);\n\n\tif (root) {\n\t\tobs_data_add_json_object_data(data, root);\n\t\tjson_decref(root);\n\t} else {\n\t\tblog(LOG_ERROR,\n\t\t     \"obs-data.c: [obs_data_create_from_json] \"\n\t\t     \"Failed reading json string (%d): %s\",\n\t\t     error.line, error.text);\n\t\tobs_data_release(data);\n\t\tdata = NULL;\n\t}\n\n\treturn data;\n}\n\nobs_data_t *obs_data_create_from_json_file(const char *json_file)\n{\n\tchar *file_data = os_quick_read_utf8_file(json_file);\n\tobs_data_t *data = NULL;\n\n\tif (file_data) {\n\t\tdata = obs_data_create_from_json(file_data);\n\t\tbfree(file_data);\n\t}\n\n\treturn data;\n}\n\nobs_data_t *obs_data_create_from_json_file_safe(const char *json_file, const char *backup_ext)\n{\n\tobs_data_t *file_data = obs_data_create_from_json_file(json_file);\n\tif (!file_data && backup_ext && *backup_ext) {\n\t\tstruct dstr backup_file = {0};\n\n\t\tdstr_copy(&backup_file, json_file);\n\t\tif (*backup_ext != '.')\n\t\t\tdstr_cat(&backup_file, \".\");\n\t\tdstr_cat(&backup_file, backup_ext);\n\n\t\tif (os_file_exists(backup_file.array)) {\n\t\t\tblog(LOG_WARNING, \"obs-data.c: \"\n\t\t\t\t\t  \"[obs_data_create_from_json_file_safe] \"\n\t\t\t\t\t  \"attempting backup file\");\n\n\t\t\t/* delete current file if corrupt to prevent it from\n\t\t\t * being backed up again */\n\t\t\tos_rename(backup_file.array, json_file);\n\n\t\t\tfile_data = obs_data_create_from_json_file(json_file);\n\t\t}\n\n\t\tdstr_free(&backup_file);\n\t}\n\n\treturn file_data;\n}\n\nvoid obs_data_addref(obs_data_t *data)\n{\n\tif (data)\n\t\tos_atomic_inc_long(&data->ref);\n}\n\nstatic inline void obs_data_destroy(struct obs_data *data)\n{\n\tstruct obs_data_item *item, *temp;\n\n\tHASH_ITER (hh, data->items, item, temp) {\n\t\tobs_data_item_detach(item);\n\t\tobs_data_item_release(&item);\n\t}\n\n\t/* NOTE: don't use bfree for json text, allocated by json */\n\tfree(data->json);\n\tbfree(data);\n}\n\nvoid obs_data_release(obs_data_t *data)\n{\n\tif (!data)\n\t\treturn;\n\n\tif (os_atomic_dec_long(&data->ref) == 0)\n\t\tobs_data_destroy(data);\n}\n\nstatic const char *obs_data_get_json_internal(obs_data_t *data, bool pretty, bool with_defaults)\n{\n\tif (!data)\n\t\treturn NULL;\n\n\tsize_t flags = JSON_PRESERVE_ORDER;\n\n\tif (pretty)\n\t\tflags |= JSON_INDENT(4);\n\telse\n\t\tflags |= JSON_COMPACT;\n\n\t/* NOTE: don't use libobs bfree for json text */\n\tfree(data->json);\n\tdata->json = NULL;\n\n\tjson_t *root = obs_data_to_json(data, with_defaults);\n\tdata->json = json_dumps(root, flags);\n\tjson_decref(root);\n\n\treturn data->json;\n}\n\nconst char *obs_data_get_json(obs_data_t *data)\n{\n\treturn obs_data_get_json_internal(data, false, false);\n}\n\nconst char *obs_data_get_json_with_defaults(obs_data_t *data)\n{\n\treturn obs_data_get_json_internal(data, false, true);\n}\n\nconst char *obs_data_get_json_pretty(obs_data_t *data)\n{\n\treturn obs_data_get_json_internal(data, true, false);\n}\n\nconst char *obs_data_get_json_pretty_with_defaults(obs_data_t *data)\n{\n\treturn obs_data_get_json_internal(data, true, true);\n}\n\nconst char *obs_data_get_last_json(obs_data_t *data)\n{\n\treturn data ? data->json : NULL;\n}\n\nbool obs_data_save_json(obs_data_t *data, const char *file)\n{\n\tconst char *json = obs_data_get_json(data);\n\n\tif (json && *json) {\n\t\treturn os_quick_write_utf8_file(file, json, strlen(json), false);\n\t}\n\n\treturn false;\n}\n\nbool obs_data_save_json_safe(obs_data_t *data, const char *file, const char *temp_ext, const char *backup_ext)\n{\n\tconst char *json = obs_data_get_json(data);\n\n\tif (json && *json) {\n\t\treturn os_quick_write_utf8_file_safe(file, json, strlen(json), false, temp_ext, backup_ext);\n\t}\n\n\treturn false;\n}\n\nbool obs_data_save_json_pretty_safe(obs_data_t *data, const char *file, const char *temp_ext, const char *backup_ext)\n{\n\tconst char *json = obs_data_get_json_pretty(data);\n\n\tif (json && *json) {\n\t\treturn os_quick_write_utf8_file_safe(file, json, strlen(json), false, temp_ext, backup_ext);\n\t}\n\n\treturn false;\n}\n\nstatic void get_defaults_array_cb(obs_data_t *data, void *vp)\n{\n\tobs_data_array_t *defs = (obs_data_array_t *)vp;\n\tobs_data_t *obs_defaults = obs_data_get_defaults(data);\n\n\tobs_data_array_push_back(defs, obs_defaults);\n\n\tobs_data_release(obs_defaults);\n}\n\nobs_data_t *obs_data_get_defaults(obs_data_t *data)\n{\n\tobs_data_t *defaults = obs_data_create();\n\n\tif (!data)\n\t\treturn defaults;\n\n\tstruct obs_data_item *item, *temp;\n\n\tHASH_ITER (hh, data->items, item, temp) {\n\t\tconst char *name = get_item_name(item);\n\t\tswitch (item->type) {\n\t\tcase OBS_DATA_NULL:\n\t\t\tbreak;\n\n\t\tcase OBS_DATA_STRING: {\n\t\t\tconst char *str = obs_data_get_default_string(data, name);\n\t\t\tobs_data_set_string(defaults, name, str);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBS_DATA_NUMBER: {\n\t\t\tswitch (obs_data_item_numtype(item)) {\n\t\t\tcase OBS_DATA_NUM_DOUBLE: {\n\t\t\t\tdouble val = obs_data_get_default_double(data, name);\n\t\t\t\tobs_data_set_double(defaults, name, val);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase OBS_DATA_NUM_INT: {\n\t\t\t\tlong long val = obs_data_get_default_int(data, name);\n\t\t\t\tobs_data_set_int(defaults, name, val);\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase OBS_DATA_NUM_INVALID:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBS_DATA_BOOLEAN: {\n\t\t\tbool val = obs_data_get_default_bool(data, name);\n\t\t\tobs_data_set_bool(defaults, name, val);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBS_DATA_OBJECT: {\n\t\t\tobs_data_t *val = obs_data_get_default_obj(data, name);\n\t\t\tobs_data_t *defs = obs_data_get_defaults(val);\n\n\t\t\tobs_data_set_obj(defaults, name, defs);\n\n\t\t\tobs_data_release(defs);\n\t\t\tobs_data_release(val);\n\t\t\tbreak;\n\t\t}\n\n\t\tcase OBS_DATA_ARRAY: {\n\t\t\tobs_data_array_t *arr = obs_data_get_default_array(data, name);\n\t\t\tobs_data_array_t *defs = obs_data_array_create();\n\n\t\t\tobs_data_array_enum(arr, get_defaults_array_cb, (void *)defs);\n\t\t\tobs_data_set_array(defaults, name, defs);\n\n\t\t\tobs_data_array_release(defs);\n\t\t\tobs_data_array_release(arr);\n\t\t\tbreak;\n\t\t}\n\t\t}\n\t}\n\n\treturn defaults;\n}\n\nstatic struct obs_data_item *get_item(struct obs_data *data, const char *name)\n{\n\tif (!data)\n\t\treturn NULL;\n\n\tstruct obs_data_item *item;\n\tHASH_FIND_STR(data->items, name, item);\n\treturn item;\n}\n\nstatic void set_item_data(struct obs_data *data, struct obs_data_item **item, const char *name, const void *ptr,\n\t\t\t  size_t size, enum obs_data_type type, bool default_data, bool autoselect_data)\n{\n\tobs_data_item_t *new_item = NULL;\n\n\tif ((!item || !*item) && data) {\n\t\tnew_item = obs_data_item_create(name, ptr, size, type, default_data, autoselect_data);\n\t\tnew_item->parent = data;\n\t\tHASH_ADD_STR(data->items, name, new_item);\n\n\t} else if (default_data) {\n\t\tobs_data_item_set_default_data(item, ptr, size, type);\n\t} else if (autoselect_data) {\n\t\tobs_data_item_set_autoselect_data(item, ptr, size, type);\n\t} else {\n\t\tobs_data_item_setdata(item, ptr, size, type);\n\t}\n}\n\nstatic inline void set_item(struct obs_data *data, obs_data_item_t **item, const char *name, const void *ptr,\n\t\t\t    size_t size, enum obs_data_type type)\n{\n\tobs_data_item_t *actual_item = NULL;\n\n\tif (!data && !item)\n\t\treturn;\n\n\tif (!item) {\n\t\tactual_item = get_item(data, name);\n\t\titem = &actual_item;\n\t}\n\n\tset_item_data(data, item, name, ptr, size, type, false, false);\n}\n\nstatic inline void set_item_def(struct obs_data *data, obs_data_item_t **item, const char *name, const void *ptr,\n\t\t\t\tsize_t size, enum obs_data_type type)\n{\n\tobs_data_item_t *actual_item = NULL;\n\n\tif (!data && !item)\n\t\treturn;\n\n\tif (!item) {\n\t\tactual_item = get_item(data, name);\n\t\titem = &actual_item;\n\t}\n\n\tif (*item && (*item)->type != type)\n\t\treturn;\n\n\tset_item_data(data, item, name, ptr, size, type, true, false);\n}\n\nstatic inline void set_item_auto(struct obs_data *data, obs_data_item_t **item, const char *name, const void *ptr,\n\t\t\t\t size_t size, enum obs_data_type type)\n{\n\tobs_data_item_t *actual_item = NULL;\n\n\tif (!data && !item)\n\t\treturn;\n\n\tif (!item) {\n\t\tactual_item = get_item(data, name);\n\t\titem = &actual_item;\n\t}\n\n\tset_item_data(data, item, name, ptr, size, type, false, true);\n}\n\nstatic void copy_obj(struct obs_data *data, const char *name, struct obs_data *obj,\n\t\t     void (*callback)(obs_data_t *, const char *, obs_data_t *))\n{\n\tif (obj) {\n\t\tobs_data_t *new_obj = obs_data_create();\n\t\tobs_data_apply(new_obj, obj);\n\t\tcallback(data, name, new_obj);\n\t\tobs_data_release(new_obj);\n\t}\n}\n\nstatic void copy_array(struct obs_data *data, const char *name, struct obs_data_array *array,\n\t\t       void (*callback)(obs_data_t *, const char *, obs_data_array_t *))\n{\n\tif (array) {\n\t\tobs_data_array_t *new_array = obs_data_array_create();\n\t\tda_reserve(new_array->objects, array->objects.num);\n\n\t\tfor (size_t i = 0; i < array->objects.num; i++) {\n\t\t\tobs_data_t *new_obj = obs_data_create();\n\t\t\tobs_data_t *obj = array->objects.array[i];\n\n\t\t\tobs_data_apply(new_obj, obj);\n\t\t\tobs_data_array_push_back(new_array, new_obj);\n\n\t\t\tobs_data_release(new_obj);\n\t\t}\n\n\t\tcallback(data, name, new_array);\n\t\tobs_data_array_release(new_array);\n\t}\n}\n\nstatic inline void copy_item(struct obs_data *data, struct obs_data_item *item)\n{\n\tconst char *name = get_item_name(item);\n\tvoid *ptr = get_item_data(item);\n\n\tif (item->type == OBS_DATA_OBJECT) {\n\t\tobs_data_t **obj = item->data_size ? ptr : NULL;\n\n\t\tif (obj)\n\t\t\tcopy_obj(data, name, *obj, obs_data_set_obj);\n\n\t} else if (item->type == OBS_DATA_ARRAY) {\n\t\tobs_data_array_t **array = item->data_size ? ptr : NULL;\n\n\t\tif (array)\n\t\t\tcopy_array(data, name, *array, obs_data_set_array);\n\n\t} else {\n\t\tif (item->data_size)\n\t\t\tset_item(data, NULL, name, ptr, item->data_size, item->type);\n\t}\n}\n\nvoid obs_data_apply(obs_data_t *target, obs_data_t *apply_data)\n{\n\tif (!target || !apply_data || target == apply_data)\n\t\treturn;\n\n\tstruct obs_data_item *item, *temp;\n\n\tHASH_ITER (hh, apply_data->items, item, temp) {\n\t\tcopy_item(target, item);\n\t}\n}\n\nvoid obs_data_erase(obs_data_t *data, const char *name)\n{\n\tstruct obs_data_item *item = get_item(data, name);\n\n\tif (item) {\n\t\tobs_data_item_detach(item);\n\t\tobs_data_item_release(&item);\n\t}\n}\n\nstatic inline void clear_item(struct obs_data_item *item)\n{\n\tvoid *ptr = get_item_data(item);\n\tsize_t size;\n\n\tif (item->data_len) {\n\t\tif (item->type == OBS_DATA_OBJECT) {\n\t\t\tobs_data_t **obj = item->data_size ? ptr : NULL;\n\n\t\t\tif (obj && *obj)\n\t\t\t\tobs_data_release(*obj);\n\n\t\t} else if (item->type == OBS_DATA_ARRAY) {\n\t\t\tobs_data_array_t **array = item->data_size ? ptr : NULL;\n\n\t\t\tif (array && *array)\n\t\t\t\tobs_data_array_release(*array);\n\t\t}\n\n\t\tsize = item->default_len + item->autoselect_size;\n\t\tif (size)\n\t\t\tmemmove(ptr, (uint8_t *)ptr + item->data_len, size);\n\n\t\titem->data_size = 0;\n\t\titem->data_len = 0;\n\t}\n}\n\nvoid obs_data_clear(obs_data_t *target)\n{\n\tif (!target)\n\t\treturn;\n\n\tstruct obs_data_item *item, *temp;\n\tHASH_ITER (hh, target->items, item, temp) {\n\t\tclear_item(item);\n\t}\n}\n\ntypedef void (*set_item_t)(obs_data_t *, obs_data_item_t **, const char *, const void *, size_t, enum obs_data_type);\n\nstatic inline void obs_set_string(obs_data_t *data, obs_data_item_t **item, const char *name, const char *val,\n\t\t\t\t  set_item_t set_item_)\n{\n\tif (!val)\n\t\tval = \"\";\n\tset_item_(data, item, name, val, strlen(val) + 1, OBS_DATA_STRING);\n}\n\nstatic inline void obs_set_int(obs_data_t *data, obs_data_item_t **item, const char *name, long long val,\n\t\t\t       set_item_t set_item_)\n{\n\tstruct obs_data_number num;\n\tnum.type = OBS_DATA_NUM_INT;\n\tnum.int_val = val;\n\tset_item_(data, item, name, &num, sizeof(struct obs_data_number), OBS_DATA_NUMBER);\n}\n\nstatic inline void obs_set_double(obs_data_t *data, obs_data_item_t **item, const char *name, double val,\n\t\t\t\t  set_item_t set_item_)\n{\n\tstruct obs_data_number num;\n\tnum.type = OBS_DATA_NUM_DOUBLE;\n\tnum.double_val = val;\n\tset_item_(data, item, name, &num, sizeof(struct obs_data_number), OBS_DATA_NUMBER);\n}\n\nstatic inline void obs_set_bool(obs_data_t *data, obs_data_item_t **item, const char *name, bool val,\n\t\t\t\tset_item_t set_item_)\n{\n\tset_item_(data, item, name, &val, sizeof(bool), OBS_DATA_BOOLEAN);\n}\n\nstatic inline void obs_set_obj(obs_data_t *data, obs_data_item_t **item, const char *name, obs_data_t *obj,\n\t\t\t       set_item_t set_item_)\n{\n\tset_item_(data, item, name, &obj, sizeof(obs_data_t *), OBS_DATA_OBJECT);\n}\n\nstatic inline void obs_set_array(obs_data_t *data, obs_data_item_t **item, const char *name, obs_data_array_t *array,\n\t\t\t\t set_item_t set_item_)\n{\n\tset_item_(data, item, name, &array, sizeof(obs_data_t *), OBS_DATA_ARRAY);\n}\n\nstatic inline void obs_take_obj(obs_data_t *data, obs_data_item_t **item, const char *name, obs_data_t *obj,\n\t\t\t\tset_item_t set_item_)\n{\n\tobs_set_obj(data, item, name, obj, set_item_);\n\tobs_data_release(obj);\n}\n\nvoid obs_data_set_string(obs_data_t *data, const char *name, const char *val)\n{\n\tobs_set_string(data, NULL, name, val, set_item);\n}\n\nvoid obs_data_set_int(obs_data_t *data, const char *name, long long val)\n{\n\tobs_set_int(data, NULL, name, val, set_item);\n}\n\nvoid obs_data_set_double(obs_data_t *data, const char *name, double val)\n{\n\tobs_set_double(data, NULL, name, val, set_item);\n}\n\nvoid obs_data_set_bool(obs_data_t *data, const char *name, bool val)\n{\n\tobs_set_bool(data, NULL, name, val, set_item);\n}\n\nvoid obs_data_set_obj(obs_data_t *data, const char *name, obs_data_t *obj)\n{\n\tobs_set_obj(data, NULL, name, obj, set_item);\n}\n\nvoid obs_data_set_array(obs_data_t *data, const char *name, obs_data_array_t *array)\n{\n\tobs_set_array(data, NULL, name, array, set_item);\n}\n\nvoid obs_data_set_default_string(obs_data_t *data, const char *name, const char *val)\n{\n\tobs_set_string(data, NULL, name, val, set_item_def);\n}\n\nvoid obs_data_set_default_int(obs_data_t *data, const char *name, long long val)\n{\n\tobs_set_int(data, NULL, name, val, set_item_def);\n}\n\nvoid obs_data_set_default_double(obs_data_t *data, const char *name, double val)\n{\n\tobs_set_double(data, NULL, name, val, set_item_def);\n}\n\nvoid obs_data_set_default_bool(obs_data_t *data, const char *name, bool val)\n{\n\tobs_set_bool(data, NULL, name, val, set_item_def);\n}\n\nvoid obs_data_set_default_obj(obs_data_t *data, const char *name, obs_data_t *obj)\n{\n\tobs_set_obj(data, NULL, name, obj, set_item_def);\n}\n\nvoid obs_data_set_default_array(obs_data_t *data, const char *name, obs_data_array_t *arr)\n{\n\tobs_set_array(data, NULL, name, arr, set_item_def);\n}\n\nvoid obs_data_set_autoselect_string(obs_data_t *data, const char *name, const char *val)\n{\n\tobs_set_string(data, NULL, name, val, set_item_auto);\n}\n\nvoid obs_data_set_autoselect_int(obs_data_t *data, const char *name, long long val)\n{\n\tobs_set_int(data, NULL, name, val, set_item_auto);\n}\n\nvoid obs_data_set_autoselect_double(obs_data_t *data, const char *name, double val)\n{\n\tobs_set_double(data, NULL, name, val, set_item_auto);\n}\n\nvoid obs_data_set_autoselect_bool(obs_data_t *data, const char *name, bool val)\n{\n\tobs_set_bool(data, NULL, name, val, set_item_auto);\n}\n\nvoid obs_data_set_autoselect_obj(obs_data_t *data, const char *name, obs_data_t *obj)\n{\n\tobs_set_obj(data, NULL, name, obj, set_item_auto);\n}\n\nvoid obs_data_set_autoselect_array(obs_data_t *data, const char *name, obs_data_array_t *arr)\n{\n\tobs_set_array(data, NULL, name, arr, set_item_auto);\n}\n\nconst char *obs_data_get_string(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_string(get_item(data, name));\n}\n\nlong long obs_data_get_int(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_int(get_item(data, name));\n}\n\ndouble obs_data_get_double(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_double(get_item(data, name));\n}\n\nbool obs_data_get_bool(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_bool(get_item(data, name));\n}\n\nobs_data_t *obs_data_get_obj(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_obj(get_item(data, name));\n}\n\nobs_data_array_t *obs_data_get_array(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_array(get_item(data, name));\n}\n\nconst char *obs_data_get_default_string(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_default_string(get_item(data, name));\n}\n\nlong long obs_data_get_default_int(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_default_int(get_item(data, name));\n}\n\ndouble obs_data_get_default_double(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_default_double(get_item(data, name));\n}\n\nbool obs_data_get_default_bool(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_default_bool(get_item(data, name));\n}\n\nobs_data_t *obs_data_get_default_obj(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_default_obj(get_item(data, name));\n}\n\nobs_data_array_t *obs_data_get_default_array(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_default_array(get_item(data, name));\n}\n\nconst char *obs_data_get_autoselect_string(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_autoselect_string(get_item(data, name));\n}\n\nlong long obs_data_get_autoselect_int(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_autoselect_int(get_item(data, name));\n}\n\ndouble obs_data_get_autoselect_double(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_autoselect_double(get_item(data, name));\n}\n\nbool obs_data_get_autoselect_bool(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_autoselect_bool(get_item(data, name));\n}\n\nobs_data_t *obs_data_get_autoselect_obj(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_autoselect_obj(get_item(data, name));\n}\n\nobs_data_array_t *obs_data_get_autoselect_array(obs_data_t *data, const char *name)\n{\n\treturn obs_data_item_get_autoselect_array(get_item(data, name));\n}\n\nobs_data_array_t *obs_data_array_create()\n{\n\tstruct obs_data_array *array = bzalloc(sizeof(struct obs_data_array));\n\tarray->ref = 1;\n\n\treturn array;\n}\n\nvoid obs_data_array_addref(obs_data_array_t *array)\n{\n\tif (array)\n\t\tos_atomic_inc_long(&array->ref);\n}\n\nstatic inline void obs_data_array_destroy(obs_data_array_t *array)\n{\n\tif (array) {\n\t\tfor (size_t i = 0; i < array->objects.num; i++)\n\t\t\tobs_data_release(array->objects.array[i]);\n\t\tda_free(array->objects);\n\t\tbfree(array);\n\t}\n}\n\nvoid obs_data_array_release(obs_data_array_t *array)\n{\n\tif (!array)\n\t\treturn;\n\n\tif (os_atomic_dec_long(&array->ref) == 0)\n\t\tobs_data_array_destroy(array);\n}\n\nsize_t obs_data_array_count(obs_data_array_t *array)\n{\n\treturn array ? array->objects.num : 0;\n}\n\nobs_data_t *obs_data_array_item(obs_data_array_t *array, size_t idx)\n{\n\tobs_data_t *data;\n\n\tif (!array)\n\t\treturn NULL;\n\n\tdata = (idx < array->objects.num) ? array->objects.array[idx] : NULL;\n\n\tif (data)\n\t\tos_atomic_inc_long(&data->ref);\n\treturn data;\n}\n\nsize_t obs_data_array_push_back(obs_data_array_t *array, obs_data_t *obj)\n{\n\tif (!array || !obj)\n\t\treturn 0;\n\n\tos_atomic_inc_long(&obj->ref);\n\treturn da_push_back(array->objects, &obj);\n}\n\nvoid obs_data_array_insert(obs_data_array_t *array, size_t idx, obs_data_t *obj)\n{\n\tif (!array || !obj)\n\t\treturn;\n\n\tos_atomic_inc_long(&obj->ref);\n\tda_insert(array->objects, idx, &obj);\n}\n\nvoid obs_data_array_push_back_array(obs_data_array_t *array, obs_data_array_t *array2)\n{\n\tif (!array || !array2)\n\t\treturn;\n\n\tfor (size_t i = 0; i < array2->objects.num; i++) {\n\t\tobs_data_t *obj = array2->objects.array[i];\n\t\tobs_data_addref(obj);\n\t}\n\tda_push_back_da(array->objects, array2->objects);\n}\n\nvoid obs_data_array_erase(obs_data_array_t *array, size_t idx)\n{\n\tif (array) {\n\t\tobs_data_release(array->objects.array[idx]);\n\t\tda_erase(array->objects, idx);\n\t}\n}\n\nvoid obs_data_array_enum(obs_data_array_t *array, void (*cb)(obs_data_t *data, void *param), void *param)\n{\n\tif (array && cb) {\n\t\tfor (size_t i = 0; i < array->objects.num; i++) {\n\t\t\tcb(array->objects.array[i], param);\n\t\t}\n\t}\n}\n\n/* ------------------------------------------------------------------------- */\n/* Item status inspection */\n\nbool obs_data_has_user_value(obs_data_t *data, const char *name)\n{\n\treturn data && obs_data_item_has_user_value(get_item(data, name));\n}\n\nbool obs_data_has_default_value(obs_data_t *data, const char *name)\n{\n\treturn data && obs_data_item_has_default_value(get_item(data, name));\n}\n\nbool obs_data_has_autoselect_value(obs_data_t *data, const char *name)\n{\n\treturn data && obs_data_item_has_autoselect_value(get_item(data, name));\n}\n\nbool obs_data_item_has_user_value(obs_data_item_t *item)\n{\n\treturn item && item->data_size;\n}\n\nbool obs_data_item_has_default_value(obs_data_item_t *item)\n{\n\treturn item && item->default_size;\n}\n\nbool obs_data_item_has_autoselect_value(obs_data_item_t *item)\n{\n\treturn item && item->autoselect_size;\n}\n\n/* ------------------------------------------------------------------------- */\n/* Clearing data values */\n\nvoid obs_data_unset_user_value(obs_data_t *data, const char *name)\n{\n\tobs_data_item_unset_user_value(get_item(data, name));\n}\n\nvoid obs_data_unset_default_value(obs_data_t *data, const char *name)\n{\n\tobs_data_item_unset_default_value(get_item(data, name));\n}\n\nvoid obs_data_unset_autoselect_value(obs_data_t *data, const char *name)\n{\n\tobs_data_item_unset_autoselect_value(get_item(data, name));\n}\n\nvoid obs_data_item_unset_user_value(obs_data_item_t *item)\n{\n\tif (!item || !item->data_size)\n\t\treturn;\n\n\tvoid *old_non_user_data = get_default_data_ptr(item);\n\n\titem_data_release(item);\n\titem->data_size = 0;\n\titem->data_len = 0;\n\n\tif (item->default_size || item->autoselect_size)\n\t\tmove_data(item, old_non_user_data, item, get_default_data_ptr(item),\n\t\t\t  item->default_len + item->autoselect_size);\n}\n\nvoid obs_data_item_unset_default_value(obs_data_item_t *item)\n{\n\tif (!item || !item->default_size)\n\t\treturn;\n\n\tvoid *old_autoselect_data = get_autoselect_data_ptr(item);\n\n\titem_default_data_release(item);\n\titem->default_size = 0;\n\titem->default_len = 0;\n\n\tif (item->autoselect_size)\n\t\tmove_data(item, old_autoselect_data, item, get_autoselect_data_ptr(item), item->autoselect_size);\n}\n\nvoid obs_data_item_unset_autoselect_value(obs_data_item_t *item)\n{\n\tif (!item || !item->autoselect_size)\n\t\treturn;\n\n\titem_autoselect_data_release(item);\n\titem->autoselect_size = 0;\n}\n\n/* ------------------------------------------------------------------------- */\n/* Item iteration */\n\nobs_data_item_t *obs_data_first(obs_data_t *data)\n{\n\tif (!data)\n\t\treturn NULL;\n\n\tif (data->items)\n\t\tos_atomic_inc_long(&data->items->ref);\n\treturn data->items;\n}\n\nobs_data_item_t *obs_data_item_byname(obs_data_t *data, const char *name)\n{\n\tif (!data)\n\t\treturn NULL;\n\n\tstruct obs_data_item *item = get_item(data, name);\n\tif (item)\n\t\tos_atomic_inc_long(&item->ref);\n\treturn item;\n}\n\nbool obs_data_item_next(obs_data_item_t **item)\n{\n\tif (item && *item) {\n\t\tobs_data_item_t *next = (*item)->hh.next;\n\t\tobs_data_item_release(item);\n\n\t\t*item = next;\n\n\t\tif (next) {\n\t\t\tos_atomic_inc_long(&next->ref);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid obs_data_item_release(obs_data_item_t **item)\n{\n\tif (item && *item) {\n\t\tlong ref = os_atomic_dec_long(&(*item)->ref);\n\t\tif (!ref) {\n\t\t\tobs_data_item_destroy(*item);\n\t\t\t*item = NULL;\n\t\t}\n\t}\n}\n\nvoid obs_data_item_remove(obs_data_item_t **item)\n{\n\tif (item && *item) {\n\t\tobs_data_item_detach(*item);\n\t\tobs_data_item_release(item);\n\t}\n}\n\nenum obs_data_type obs_data_item_gettype(obs_data_item_t *item)\n{\n\treturn item ? item->type : OBS_DATA_NULL;\n}\n\nenum obs_data_number_type obs_data_item_numtype(obs_data_item_t *item)\n{\n\tstruct obs_data_number *num;\n\n\tif (!item || item->type != OBS_DATA_NUMBER)\n\t\treturn OBS_DATA_NUM_INVALID;\n\n\tnum = get_item_data(item);\n\tif (!num)\n\t\treturn OBS_DATA_NUM_INVALID;\n\n\treturn num->type;\n}\n\nconst char *obs_data_item_get_name(obs_data_item_t *item)\n{\n\tif (!item)\n\t\treturn NULL;\n\n\treturn item->name;\n}\n\nvoid obs_data_item_set_string(obs_data_item_t **item, const char *val)\n{\n\tobs_set_string(NULL, item, NULL, val, set_item);\n}\n\nvoid obs_data_item_set_int(obs_data_item_t **item, long long val)\n{\n\tobs_set_int(NULL, item, NULL, val, set_item);\n}\n\nvoid obs_data_item_set_double(obs_data_item_t **item, double val)\n{\n\tobs_set_double(NULL, item, NULL, val, set_item);\n}\n\nvoid obs_data_item_set_bool(obs_data_item_t **item, bool val)\n{\n\tobs_set_bool(NULL, item, NULL, val, set_item);\n}\n\nvoid obs_data_item_set_obj(obs_data_item_t **item, obs_data_t *val)\n{\n\tobs_set_obj(NULL, item, NULL, val, set_item);\n}\n\nvoid obs_data_item_set_array(obs_data_item_t **item, obs_data_array_t *val)\n{\n\tobs_set_array(NULL, item, NULL, val, set_item);\n}\n\nvoid obs_data_item_set_default_string(obs_data_item_t **item, const char *val)\n{\n\tobs_set_string(NULL, item, NULL, val, set_item_def);\n}\n\nvoid obs_data_item_set_default_int(obs_data_item_t **item, long long val)\n{\n\tobs_set_int(NULL, item, NULL, val, set_item_def);\n}\n\nvoid obs_data_item_set_default_double(obs_data_item_t **item, double val)\n{\n\tobs_set_double(NULL, item, NULL, val, set_item_def);\n}\n\nvoid obs_data_item_set_default_bool(obs_data_item_t **item, bool val)\n{\n\tobs_set_bool(NULL, item, NULL, val, set_item_def);\n}\n\nvoid obs_data_item_set_default_obj(obs_data_item_t **item, obs_data_t *val)\n{\n\tobs_set_obj(NULL, item, NULL, val, set_item_def);\n}\n\nvoid obs_data_item_set_default_array(obs_data_item_t **item, obs_data_array_t *val)\n{\n\tobs_set_array(NULL, item, NULL, val, set_item_def);\n}\n\nvoid obs_data_item_set_autoselect_string(obs_data_item_t **item, const char *val)\n{\n\tobs_set_string(NULL, item, NULL, val, set_item_auto);\n}\n\nvoid obs_data_item_set_autoselect_int(obs_data_item_t **item, long long val)\n{\n\tobs_set_int(NULL, item, NULL, val, set_item_auto);\n}\n\nvoid obs_data_item_set_autoselect_double(obs_data_item_t **item, double val)\n{\n\tobs_set_double(NULL, item, NULL, val, set_item_auto);\n}\n\nvoid obs_data_item_set_autoselect_bool(obs_data_item_t **item, bool val)\n{\n\tobs_set_bool(NULL, item, NULL, val, set_item_auto);\n}\n\nvoid obs_data_item_set_autoselect_obj(obs_data_item_t **item, obs_data_t *val)\n{\n\tobs_set_obj(NULL, item, NULL, val, set_item_auto);\n}\n\nvoid obs_data_item_set_autoselect_array(obs_data_item_t **item, obs_data_array_t *val)\n{\n\tobs_set_array(NULL, item, NULL, val, set_item_auto);\n}\n\nstatic inline bool item_valid(struct obs_data_item *item, enum obs_data_type type)\n{\n\treturn item && item->type == type;\n}\n\ntypedef void *(*get_data_t)(obs_data_item_t *);\n\nstatic inline const char *data_item_get_string(obs_data_item_t *item, get_data_t get_data)\n{\n\tconst char *str;\n\n\treturn item_valid(item, OBS_DATA_STRING) && (str = get_data(item)) ? str : \"\";\n}\n\nstatic inline long long item_int(struct obs_data_item *item, get_data_t get_data)\n{\n\tstruct obs_data_number *num;\n\n\tif (item && (num = get_data(item))) {\n\t\treturn (num->type == OBS_DATA_NUM_INT) ? num->int_val : (long long)num->double_val;\n\t}\n\n\treturn 0;\n}\n\nstatic inline long long data_item_get_int(obs_data_item_t *item, get_data_t get_data)\n{\n\treturn item_int(item_valid(item, OBS_DATA_NUMBER) ? item : NULL, get_data);\n}\n\nstatic inline double item_double(struct obs_data_item *item, get_data_t get_data)\n{\n\tstruct obs_data_number *num;\n\n\tif (item && (num = get_data(item))) {\n\t\treturn (num->type == OBS_DATA_NUM_INT) ? (double)num->int_val : num->double_val;\n\t}\n\n\treturn 0.0;\n}\n\nstatic inline double data_item_get_double(obs_data_item_t *item, get_data_t get_data)\n{\n\treturn item_double(item_valid(item, OBS_DATA_NUMBER) ? item : NULL, get_data);\n}\n\nstatic inline bool data_item_get_bool(obs_data_item_t *item, get_data_t get_data)\n{\n\tbool *data;\n\n\treturn item_valid(item, OBS_DATA_BOOLEAN) && (data = get_data(item)) ? *data : false;\n}\n\ntypedef obs_data_t *(*get_obj_t)(obs_data_item_t *);\n\nstatic inline obs_data_t *data_item_get_obj(obs_data_item_t *item, get_obj_t get_obj)\n{\n\tobs_data_t *obj = item_valid(item, OBS_DATA_OBJECT) ? get_obj(item) : NULL;\n\n\tif (obj)\n\t\tos_atomic_inc_long(&obj->ref);\n\treturn obj;\n}\n\ntypedef obs_data_array_t *(*get_array_t)(obs_data_item_t *);\n\nstatic inline obs_data_array_t *data_item_get_array(obs_data_item_t *item, get_array_t get_array)\n{\n\tobs_data_array_t *array = item_valid(item, OBS_DATA_ARRAY) ? get_array(item) : NULL;\n\n\tif (array)\n\t\tos_atomic_inc_long(&array->ref);\n\treturn array;\n}\n\nconst char *obs_data_item_get_string(obs_data_item_t *item)\n{\n\treturn data_item_get_string(item, get_item_data);\n}\n\nlong long obs_data_item_get_int(obs_data_item_t *item)\n{\n\treturn data_item_get_int(item, get_item_data);\n}\n\ndouble obs_data_item_get_double(obs_data_item_t *item)\n{\n\treturn data_item_get_double(item, get_item_data);\n}\n\nbool obs_data_item_get_bool(obs_data_item_t *item)\n{\n\treturn data_item_get_bool(item, get_item_data);\n}\n\nobs_data_t *obs_data_item_get_obj(obs_data_item_t *item)\n{\n\treturn data_item_get_obj(item, get_item_obj);\n}\n\nobs_data_array_t *obs_data_item_get_array(obs_data_item_t *item)\n{\n\treturn data_item_get_array(item, get_item_array);\n}\n\nconst char *obs_data_item_get_default_string(obs_data_item_t *item)\n{\n\treturn data_item_get_string(item, get_item_default_data);\n}\n\nlong long obs_data_item_get_default_int(obs_data_item_t *item)\n{\n\treturn data_item_get_int(item, get_item_default_data);\n}\n\ndouble obs_data_item_get_default_double(obs_data_item_t *item)\n{\n\treturn data_item_get_double(item, get_item_default_data);\n}\n\nbool obs_data_item_get_default_bool(obs_data_item_t *item)\n{\n\treturn data_item_get_bool(item, get_item_default_data);\n}\n\nobs_data_t *obs_data_item_get_default_obj(obs_data_item_t *item)\n{\n\treturn data_item_get_obj(item, get_item_default_obj);\n}\n\nobs_data_array_t *obs_data_item_get_default_array(obs_data_item_t *item)\n{\n\treturn data_item_get_array(item, get_item_default_array);\n}\n\nconst char *obs_data_item_get_autoselect_string(obs_data_item_t *item)\n{\n\treturn data_item_get_string(item, get_item_autoselect_data);\n}\n\nlong long obs_data_item_get_autoselect_int(obs_data_item_t *item)\n{\n\treturn data_item_get_int(item, get_item_autoselect_data);\n}\n\ndouble obs_data_item_get_autoselect_double(obs_data_item_t *item)\n{\n\treturn data_item_get_double(item, get_item_autoselect_data);\n}\n\nbool obs_data_item_get_autoselect_bool(obs_data_item_t *item)\n{\n\treturn data_item_get_bool(item, get_item_autoselect_data);\n}\n\nobs_data_t *obs_data_item_get_autoselect_obj(obs_data_item_t *item)\n{\n\treturn data_item_get_obj(item, get_item_autoselect_obj);\n}\n\nobs_data_array_t *obs_data_item_get_autoselect_array(obs_data_item_t *item)\n{\n\treturn data_item_get_array(item, get_item_autoselect_array);\n}\n\n/* ------------------------------------------------------------------------- */\n/* Helper functions for certain structures */\n\ntypedef void (*set_obj_t)(obs_data_t *, const char *, obs_data_t *);\n\nstatic inline void set_vec2(obs_data_t *data, const char *name, const struct vec2 *val, set_obj_t set_obj)\n{\n\tobs_data_t *obj = obs_data_create();\n\tobs_data_set_double(obj, \"x\", val->x);\n\tobs_data_set_double(obj, \"y\", val->y);\n\tset_obj(data, name, obj);\n\tobs_data_release(obj);\n}\n\nstatic inline void set_vec3(obs_data_t *data, const char *name, const struct vec3 *val, set_obj_t set_obj)\n{\n\tobs_data_t *obj = obs_data_create();\n\tobs_data_set_double(obj, \"x\", val->x);\n\tobs_data_set_double(obj, \"y\", val->y);\n\tobs_data_set_double(obj, \"z\", val->z);\n\tset_obj(data, name, obj);\n\tobs_data_release(obj);\n}\n\nstatic inline void set_vec4(obs_data_t *data, const char *name, const struct vec4 *val, set_obj_t set_obj)\n{\n\tobs_data_t *obj = obs_data_create();\n\tobs_data_set_double(obj, \"x\", val->x);\n\tobs_data_set_double(obj, \"y\", val->y);\n\tobs_data_set_double(obj, \"z\", val->z);\n\tobs_data_set_double(obj, \"w\", val->w);\n\tset_obj(data, name, obj);\n\tobs_data_release(obj);\n}\n\nstatic inline void set_quat(obs_data_t *data, const char *name, const struct quat *val, set_obj_t set_obj)\n{\n\tobs_data_t *obj = obs_data_create();\n\tobs_data_set_double(obj, \"x\", val->x);\n\tobs_data_set_double(obj, \"y\", val->y);\n\tobs_data_set_double(obj, \"z\", val->z);\n\tobs_data_set_double(obj, \"w\", val->w);\n\tset_obj(data, name, obj);\n\tobs_data_release(obj);\n}\n\nvoid obs_data_set_vec2(obs_data_t *data, const char *name, const struct vec2 *val)\n{\n\tset_vec2(data, name, val, obs_data_set_obj);\n}\n\nvoid obs_data_set_vec3(obs_data_t *data, const char *name, const struct vec3 *val)\n{\n\tset_vec3(data, name, val, obs_data_set_obj);\n}\n\nvoid obs_data_set_vec4(obs_data_t *data, const char *name, const struct vec4 *val)\n{\n\tset_vec4(data, name, val, obs_data_set_obj);\n}\n\nvoid obs_data_set_quat(obs_data_t *data, const char *name, const struct quat *val)\n{\n\tset_quat(data, name, val, obs_data_set_obj);\n}\n\nvoid obs_data_set_default_vec2(obs_data_t *data, const char *name, const struct vec2 *val)\n{\n\tset_vec2(data, name, val, obs_data_set_default_obj);\n}\n\nvoid obs_data_set_default_vec3(obs_data_t *data, const char *name, const struct vec3 *val)\n{\n\tset_vec3(data, name, val, obs_data_set_default_obj);\n}\n\nvoid obs_data_set_default_vec4(obs_data_t *data, const char *name, const struct vec4 *val)\n{\n\tset_vec4(data, name, val, obs_data_set_default_obj);\n}\n\nvoid obs_data_set_default_quat(obs_data_t *data, const char *name, const struct quat *val)\n{\n\tset_quat(data, name, val, obs_data_set_default_obj);\n}\n\nvoid obs_data_set_autoselect_vec2(obs_data_t *data, const char *name, const struct vec2 *val)\n{\n\tset_vec2(data, name, val, obs_data_set_autoselect_obj);\n}\n\nvoid obs_data_set_autoselect_vec3(obs_data_t *data, const char *name, const struct vec3 *val)\n{\n\tset_vec3(data, name, val, obs_data_set_autoselect_obj);\n}\n\nvoid obs_data_set_autoselect_vec4(obs_data_t *data, const char *name, const struct vec4 *val)\n{\n\tset_vec4(data, name, val, obs_data_set_autoselect_obj);\n}\n\nvoid obs_data_set_autoselect_quat(obs_data_t *data, const char *name, const struct quat *val)\n{\n\tset_quat(data, name, val, obs_data_set_autoselect_obj);\n}\n\nstatic inline void get_vec2(obs_data_t *obj, struct vec2 *val)\n{\n\tif (!obj)\n\t\treturn;\n\n\tval->x = (float)obs_data_get_double(obj, \"x\");\n\tval->y = (float)obs_data_get_double(obj, \"y\");\n\tobs_data_release(obj);\n}\n\nstatic inline void get_vec3(obs_data_t *obj, struct vec3 *val)\n{\n\tif (!obj)\n\t\treturn;\n\n\tval->x = (float)obs_data_get_double(obj, \"x\");\n\tval->y = (float)obs_data_get_double(obj, \"y\");\n\tval->z = (float)obs_data_get_double(obj, \"z\");\n\tobs_data_release(obj);\n}\n\nstatic inline void get_vec4(obs_data_t *obj, struct vec4 *val)\n{\n\tif (!obj)\n\t\treturn;\n\n\tval->x = (float)obs_data_get_double(obj, \"x\");\n\tval->y = (float)obs_data_get_double(obj, \"y\");\n\tval->z = (float)obs_data_get_double(obj, \"z\");\n\tval->w = (float)obs_data_get_double(obj, \"w\");\n\tobs_data_release(obj);\n}\n\nstatic inline void get_quat(obs_data_t *obj, struct quat *val)\n{\n\tif (!obj)\n\t\treturn;\n\n\tval->x = (float)obs_data_get_double(obj, \"x\");\n\tval->y = (float)obs_data_get_double(obj, \"y\");\n\tval->z = (float)obs_data_get_double(obj, \"z\");\n\tval->w = (float)obs_data_get_double(obj, \"w\");\n\tobs_data_release(obj);\n}\n\nvoid obs_data_get_vec2(obs_data_t *data, const char *name, struct vec2 *val)\n{\n\tget_vec2(obs_data_get_obj(data, name), val);\n}\n\nvoid obs_data_get_vec3(obs_data_t *data, const char *name, struct vec3 *val)\n{\n\tget_vec3(obs_data_get_obj(data, name), val);\n}\n\nvoid obs_data_get_vec4(obs_data_t *data, const char *name, struct vec4 *val)\n{\n\tget_vec4(obs_data_get_obj(data, name), val);\n}\n\nvoid obs_data_get_quat(obs_data_t *data, const char *name, struct quat *val)\n{\n\tget_quat(obs_data_get_obj(data, name), val);\n}\n\nvoid obs_data_get_default_vec2(obs_data_t *data, const char *name, struct vec2 *val)\n{\n\tget_vec2(obs_data_get_default_obj(data, name), val);\n}\n\nvoid obs_data_get_default_vec3(obs_data_t *data, const char *name, struct vec3 *val)\n{\n\tget_vec3(obs_data_get_default_obj(data, name), val);\n}\n\nvoid obs_data_get_default_vec4(obs_data_t *data, const char *name, struct vec4 *val)\n{\n\tget_vec4(obs_data_get_default_obj(data, name), val);\n}\n\nvoid obs_data_get_default_quat(obs_data_t *data, const char *name, struct quat *val)\n{\n\tget_quat(obs_data_get_default_obj(data, name), val);\n}\n\nvoid obs_data_get_autoselect_vec2(obs_data_t *data, const char *name, struct vec2 *val)\n{\n\tget_vec2(obs_data_get_autoselect_obj(data, name), val);\n}\n\nvoid obs_data_get_autoselect_vec3(obs_data_t *data, const char *name, struct vec3 *val)\n{\n\tget_vec3(obs_data_get_autoselect_obj(data, name), val);\n}\n\nvoid obs_data_get_autoselect_vec4(obs_data_t *data, const char *name, struct vec4 *val)\n{\n\tget_vec4(obs_data_get_autoselect_obj(data, name), val);\n}\n\nvoid obs_data_get_autoselect_quat(obs_data_t *data, const char *name, struct quat *val)\n{\n\tget_quat(obs_data_get_autoselect_obj(data, name), val);\n}\n\n/* ------------------------------------------------------------------------- */\n/* Helper functions for media_frames_per_seconds */\n\nstatic inline obs_data_t *make_frames_per_second(struct media_frames_per_second fps, const char *option)\n{\n\tobs_data_t *obj = obs_data_create();\n\n\tif (!option) {\n\t\tobs_data_set_double(obj, \"numerator\", fps.numerator);\n\t\tobs_data_set_double(obj, \"denominator\", fps.denominator);\n\n\t} else {\n\t\tobs_data_set_string(obj, \"option\", option);\n\t}\n\n\treturn obj;\n}\n\nvoid obs_data_set_frames_per_second(obs_data_t *data, const char *name, struct media_frames_per_second fps,\n\t\t\t\t    const char *option)\n{\n\tobs_take_obj(data, NULL, name, make_frames_per_second(fps, option), set_item);\n}\n\nvoid obs_data_set_default_frames_per_second(obs_data_t *data, const char *name, struct media_frames_per_second fps,\n\t\t\t\t\t    const char *option)\n{\n\tobs_take_obj(data, NULL, name, make_frames_per_second(fps, option), set_item_def);\n}\n\nvoid obs_data_set_autoselect_frames_per_second(obs_data_t *data, const char *name, struct media_frames_per_second fps,\n\t\t\t\t\t       const char *option)\n{\n\tobs_take_obj(data, NULL, name, make_frames_per_second(fps, option), set_item_auto);\n}\n\nstatic inline bool get_option(obs_data_t *data, const char **option)\n{\n\tif (!option)\n\t\treturn false;\n\n\tstruct obs_data_item *opt = obs_data_item_byname(data, \"option\");\n\tif (!opt)\n\t\treturn false;\n\n\t*option = obs_data_item_get_string(opt);\n\tobs_data_item_release(&opt);\n\n\tobs_data_release(data);\n\n\treturn true;\n}\n\n#define CLAMP(x, min, max) ((x) < min ? min : ((x) > max ? max : (x)))\n\nstatic inline bool get_frames_per_second(obs_data_t *data, struct media_frames_per_second *fps, const char **option)\n{\n\tif (!data)\n\t\treturn false;\n\n\tif (get_option(data, option))\n\t\treturn true;\n\n\tif (!fps)\n\t\tgoto free;\n\n\tstruct obs_data_item *num = obs_data_item_byname(data, \"numerator\");\n\tstruct obs_data_item *den = obs_data_item_byname(data, \"denominator\");\n\tif (!num || !den) {\n\t\tobs_data_item_release(&num);\n\t\tobs_data_item_release(&den);\n\t\tgoto free;\n\t}\n\n\tlong long num_ll = obs_data_item_get_int(num);\n\tlong long den_ll = obs_data_item_get_int(den);\n\n\tfps->numerator = (uint32_t)CLAMP(num_ll, 0, (long long)UINT32_MAX);\n\tfps->denominator = (uint32_t)CLAMP(den_ll, 0, (long long)UINT32_MAX);\n\n\tobs_data_item_release(&num);\n\tobs_data_item_release(&den);\n\n\tobs_data_release(data);\n\n\treturn media_frames_per_second_is_valid(*fps);\n\nfree:\n\tobs_data_release(data);\n\treturn false;\n}\n\nbool obs_data_get_frames_per_second(obs_data_t *data, const char *name, struct media_frames_per_second *fps,\n\t\t\t\t    const char **option)\n{\n\treturn get_frames_per_second(obs_data_get_obj(data, name), fps, option);\n}\n\nbool obs_data_get_default_frames_per_second(obs_data_t *data, const char *name, struct media_frames_per_second *fps,\n\t\t\t\t\t    const char **option)\n{\n\treturn get_frames_per_second(obs_data_get_default_obj(data, name), fps, option);\n}\n\nbool obs_data_get_autoselect_frames_per_second(obs_data_t *data, const char *name, struct media_frames_per_second *fps,\n\t\t\t\t\t       const char **option)\n{\n\treturn get_frames_per_second(obs_data_get_autoselect_obj(data, name), fps, option);\n}\n\nvoid obs_data_item_set_frames_per_second(obs_data_item_t **item, struct media_frames_per_second fps, const char *option)\n{\n\tobs_take_obj(NULL, item, NULL, make_frames_per_second(fps, option), set_item);\n}\n\nvoid obs_data_item_set_default_frames_per_second(obs_data_item_t **item, struct media_frames_per_second fps,\n\t\t\t\t\t\t const char *option)\n{\n\tobs_take_obj(NULL, item, NULL, make_frames_per_second(fps, option), set_item_def);\n}\n\nvoid obs_data_item_set_autoselect_frames_per_second(obs_data_item_t **item, struct media_frames_per_second fps,\n\t\t\t\t\t\t    const char *option)\n{\n\tobs_take_obj(NULL, item, NULL, make_frames_per_second(fps, option), set_item_auto);\n}\n\nbool obs_data_item_get_frames_per_second(obs_data_item_t *item, struct media_frames_per_second *fps,\n\t\t\t\t\t const char **option)\n{\n\treturn get_frames_per_second(obs_data_item_get_obj(item), fps, option);\n}\n\nbool obs_data_item_get_default_frames_per_second(obs_data_item_t *item, struct media_frames_per_second *fps,\n\t\t\t\t\t\t const char **option)\n{\n\treturn get_frames_per_second(obs_data_item_get_default_obj(item), fps, option);\n}\n\nbool obs_data_item_get_autoselect_frames_per_second(obs_data_item_t *item, struct media_frames_per_second *fps,\n\t\t\t\t\t\t    const char **option)\n{\n\treturn get_frames_per_second(obs_data_item_get_autoselect_obj(item), fps, option);\n}\n"
  },
  {
    "path": "libobs/obs-data.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"util/c99defs.h\"\n#include \"media-io/frame-rate.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct vec2;\nstruct vec3;\nstruct vec4;\nstruct quat;\n\n/*\n * OBS data settings storage\n *\n *   This is used for retrieving or setting the data settings for things such\n * as sources, encoders, etc.  This is designed for JSON serialization.\n */\n\nstruct obs_data;\nstruct obs_data_item;\nstruct obs_data_array;\ntypedef struct obs_data obs_data_t;\ntypedef struct obs_data_item obs_data_item_t;\ntypedef struct obs_data_array obs_data_array_t;\n\nenum obs_data_type {\n\tOBS_DATA_NULL,\n\tOBS_DATA_STRING,\n\tOBS_DATA_NUMBER,\n\tOBS_DATA_BOOLEAN,\n\tOBS_DATA_OBJECT,\n\tOBS_DATA_ARRAY\n};\n\nenum obs_data_number_type { OBS_DATA_NUM_INVALID, OBS_DATA_NUM_INT, OBS_DATA_NUM_DOUBLE };\n\n/* ------------------------------------------------------------------------- */\n/* Main usage functions */\n\nEXPORT obs_data_t *obs_data_create();\nEXPORT obs_data_t *obs_data_create_from_json(const char *json_string);\nEXPORT obs_data_t *obs_data_create_from_json_file(const char *json_file);\nEXPORT obs_data_t *obs_data_create_from_json_file_safe(const char *json_file, const char *backup_ext);\nEXPORT void obs_data_addref(obs_data_t *data);\nEXPORT void obs_data_release(obs_data_t *data);\n\nEXPORT const char *obs_data_get_json(obs_data_t *data);\nEXPORT const char *obs_data_get_json_with_defaults(obs_data_t *data);\nEXPORT const char *obs_data_get_json_pretty(obs_data_t *data);\nEXPORT const char *obs_data_get_json_pretty_with_defaults(obs_data_t *data);\nEXPORT const char *obs_data_get_last_json(obs_data_t *data);\nEXPORT bool obs_data_save_json(obs_data_t *data, const char *file);\nEXPORT bool obs_data_save_json_safe(obs_data_t *data, const char *file, const char *temp_ext, const char *backup_ext);\nEXPORT bool obs_data_save_json_pretty_safe(obs_data_t *data, const char *file, const char *temp_ext,\n\t\t\t\t\t   const char *backup_ext);\n\nEXPORT void obs_data_apply(obs_data_t *target, obs_data_t *apply_data);\n\nEXPORT void obs_data_erase(obs_data_t *data, const char *name);\nEXPORT void obs_data_clear(obs_data_t *data);\n\n/* Set functions */\nEXPORT void obs_data_set_string(obs_data_t *data, const char *name, const char *val);\nEXPORT void obs_data_set_int(obs_data_t *data, const char *name, long long val);\nEXPORT void obs_data_set_double(obs_data_t *data, const char *name, double val);\nEXPORT void obs_data_set_bool(obs_data_t *data, const char *name, bool val);\nEXPORT void obs_data_set_obj(obs_data_t *data, const char *name, obs_data_t *obj);\nEXPORT void obs_data_set_array(obs_data_t *data, const char *name, obs_data_array_t *array);\n\n/*\n * Creates an obs_data_t * filled with all default values.\n */\nEXPORT obs_data_t *obs_data_get_defaults(obs_data_t *data);\n\n/*\n * Default value functions.\n */\nEXPORT void obs_data_set_default_string(obs_data_t *data, const char *name, const char *val);\nEXPORT void obs_data_set_default_int(obs_data_t *data, const char *name, long long val);\nEXPORT void obs_data_set_default_double(obs_data_t *data, const char *name, double val);\nEXPORT void obs_data_set_default_bool(obs_data_t *data, const char *name, bool val);\nEXPORT void obs_data_set_default_obj(obs_data_t *data, const char *name, obs_data_t *obj);\nEXPORT void obs_data_set_default_array(obs_data_t *data, const char *name, obs_data_array_t *arr);\n\n/*\n * Application overrides\n * Use these to communicate the actual values of settings in case the user\n * settings aren't appropriate\n */\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_string(obs_data_t *data, const char *name, const char *val);\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_int(obs_data_t *data, const char *name, long long val);\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_double(obs_data_t *data, const char *name, double val);\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_bool(obs_data_t *data, const char *name, bool val);\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_obj(obs_data_t *data, const char *name, obs_data_t *obj);\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_array(obs_data_t *data, const char *name, obs_data_array_t *arr);\n\n/*\n * Get functions\n */\nEXPORT const char *obs_data_get_string(obs_data_t *data, const char *name);\nEXPORT long long obs_data_get_int(obs_data_t *data, const char *name);\nEXPORT double obs_data_get_double(obs_data_t *data, const char *name);\nEXPORT bool obs_data_get_bool(obs_data_t *data, const char *name);\nEXPORT obs_data_t *obs_data_get_obj(obs_data_t *data, const char *name);\nEXPORT obs_data_array_t *obs_data_get_array(obs_data_t *data, const char *name);\n\nEXPORT const char *obs_data_get_default_string(obs_data_t *data, const char *name);\nEXPORT long long obs_data_get_default_int(obs_data_t *data, const char *name);\nEXPORT double obs_data_get_default_double(obs_data_t *data, const char *name);\nEXPORT bool obs_data_get_default_bool(obs_data_t *data, const char *name);\nEXPORT obs_data_t *obs_data_get_default_obj(obs_data_t *data, const char *name);\nEXPORT obs_data_array_t *obs_data_get_default_array(obs_data_t *data, const char *name);\n\nOBS_DEPRECATED EXPORT const char *obs_data_get_autoselect_string(obs_data_t *data, const char *name);\nOBS_DEPRECATED EXPORT long long obs_data_get_autoselect_int(obs_data_t *data, const char *name);\nOBS_DEPRECATED EXPORT double obs_data_get_autoselect_double(obs_data_t *data, const char *name);\nOBS_DEPRECATED EXPORT bool obs_data_get_autoselect_bool(obs_data_t *data, const char *name);\nOBS_DEPRECATED EXPORT obs_data_t *obs_data_get_autoselect_obj(obs_data_t *data, const char *name);\nOBS_DEPRECATED EXPORT obs_data_array_t *obs_data_get_autoselect_array(obs_data_t *data, const char *name);\n\n/* Array functions */\nEXPORT obs_data_array_t *obs_data_array_create();\nEXPORT void obs_data_array_addref(obs_data_array_t *array);\nEXPORT void obs_data_array_release(obs_data_array_t *array);\n\nEXPORT size_t obs_data_array_count(obs_data_array_t *array);\nEXPORT obs_data_t *obs_data_array_item(obs_data_array_t *array, size_t idx);\nEXPORT size_t obs_data_array_push_back(obs_data_array_t *array, obs_data_t *obj);\nEXPORT void obs_data_array_insert(obs_data_array_t *array, size_t idx, obs_data_t *obj);\nEXPORT void obs_data_array_push_back_array(obs_data_array_t *array, obs_data_array_t *array2);\nEXPORT void obs_data_array_erase(obs_data_array_t *array, size_t idx);\nEXPORT void obs_data_array_enum(obs_data_array_t *array, void (*cb)(obs_data_t *data, void *param), void *param);\n\n/* ------------------------------------------------------------------------- */\n/* Item status inspection */\n\nEXPORT bool obs_data_has_user_value(obs_data_t *data, const char *name);\nEXPORT bool obs_data_has_default_value(obs_data_t *data, const char *name);\nOBS_DEPRECATED EXPORT bool obs_data_has_autoselect_value(obs_data_t *data, const char *name);\n\nEXPORT bool obs_data_item_has_user_value(obs_data_item_t *data);\nEXPORT bool obs_data_item_has_default_value(obs_data_item_t *data);\nOBS_DEPRECATED EXPORT bool obs_data_item_has_autoselect_value(obs_data_item_t *data);\n\n/* ------------------------------------------------------------------------- */\n/* Clearing data values */\n\nEXPORT void obs_data_unset_user_value(obs_data_t *data, const char *name);\nEXPORT void obs_data_unset_default_value(obs_data_t *data, const char *name);\nOBS_DEPRECATED EXPORT void obs_data_unset_autoselect_value(obs_data_t *data, const char *name);\n\nEXPORT void obs_data_item_unset_user_value(obs_data_item_t *data);\nEXPORT void obs_data_item_unset_default_value(obs_data_item_t *data);\nOBS_DEPRECATED EXPORT void obs_data_item_unset_autoselect_value(obs_data_item_t *data);\n\n/* ------------------------------------------------------------------------- */\n/* Item iteration */\n\nEXPORT obs_data_item_t *obs_data_first(obs_data_t *data);\nEXPORT obs_data_item_t *obs_data_item_byname(obs_data_t *data, const char *name);\nEXPORT bool obs_data_item_next(obs_data_item_t **item);\nEXPORT void obs_data_item_release(obs_data_item_t **item);\nEXPORT void obs_data_item_remove(obs_data_item_t **item);\n\n/* Gets Item type */\nEXPORT enum obs_data_type obs_data_item_gettype(obs_data_item_t *item);\nEXPORT enum obs_data_number_type obs_data_item_numtype(obs_data_item_t *item);\nEXPORT const char *obs_data_item_get_name(obs_data_item_t *item);\n\n/* Item set functions */\nEXPORT void obs_data_item_set_string(obs_data_item_t **item, const char *val);\nEXPORT void obs_data_item_set_int(obs_data_item_t **item, long long val);\nEXPORT void obs_data_item_set_double(obs_data_item_t **item, double val);\nEXPORT void obs_data_item_set_bool(obs_data_item_t **item, bool val);\nEXPORT void obs_data_item_set_obj(obs_data_item_t **item, obs_data_t *val);\nEXPORT void obs_data_item_set_array(obs_data_item_t **item, obs_data_array_t *val);\n\nEXPORT void obs_data_item_set_default_string(obs_data_item_t **item, const char *val);\nEXPORT void obs_data_item_set_default_int(obs_data_item_t **item, long long val);\nEXPORT void obs_data_item_set_default_double(obs_data_item_t **item, double val);\nEXPORT void obs_data_item_set_default_bool(obs_data_item_t **item, bool val);\nEXPORT void obs_data_item_set_default_obj(obs_data_item_t **item, obs_data_t *val);\nEXPORT void obs_data_item_set_default_array(obs_data_item_t **item, obs_data_array_t *val);\n\nOBS_DEPRECATED EXPORT void obs_data_item_set_autoselect_string(obs_data_item_t **item, const char *val);\nOBS_DEPRECATED EXPORT void obs_data_item_set_autoselect_int(obs_data_item_t **item, long long val);\nOBS_DEPRECATED EXPORT void obs_data_item_set_autoselect_double(obs_data_item_t **item, double val);\nOBS_DEPRECATED EXPORT void obs_data_item_set_autoselect_bool(obs_data_item_t **item, bool val);\nOBS_DEPRECATED EXPORT void obs_data_item_set_autoselect_obj(obs_data_item_t **item, obs_data_t *val);\nOBS_DEPRECATED EXPORT void obs_data_item_set_autoselect_array(obs_data_item_t **item, obs_data_array_t *val);\n\n/* Item get functions */\nEXPORT const char *obs_data_item_get_string(obs_data_item_t *item);\nEXPORT long long obs_data_item_get_int(obs_data_item_t *item);\nEXPORT double obs_data_item_get_double(obs_data_item_t *item);\nEXPORT bool obs_data_item_get_bool(obs_data_item_t *item);\nEXPORT obs_data_t *obs_data_item_get_obj(obs_data_item_t *item);\nEXPORT obs_data_array_t *obs_data_item_get_array(obs_data_item_t *item);\n\nEXPORT const char *obs_data_item_get_default_string(obs_data_item_t *item);\nEXPORT long long obs_data_item_get_default_int(obs_data_item_t *item);\nEXPORT double obs_data_item_get_default_double(obs_data_item_t *item);\nEXPORT bool obs_data_item_get_default_bool(obs_data_item_t *item);\nEXPORT obs_data_t *obs_data_item_get_default_obj(obs_data_item_t *item);\nEXPORT obs_data_array_t *obs_data_item_get_default_array(obs_data_item_t *item);\n\nOBS_DEPRECATED EXPORT const char *obs_data_item_get_autoselect_string(obs_data_item_t *item);\nOBS_DEPRECATED EXPORT long long obs_data_item_get_autoselect_int(obs_data_item_t *item);\nOBS_DEPRECATED EXPORT double obs_data_item_get_autoselect_double(obs_data_item_t *item);\nOBS_DEPRECATED EXPORT bool obs_data_item_get_autoselect_bool(obs_data_item_t *item);\nOBS_DEPRECATED EXPORT obs_data_t *obs_data_item_get_autoselect_obj(obs_data_item_t *item);\nOBS_DEPRECATED EXPORT obs_data_array_t *obs_data_item_get_autoselect_array(obs_data_item_t *item);\n\n/* ------------------------------------------------------------------------- */\n/* Helper functions for certain structures */\nEXPORT void obs_data_set_vec2(obs_data_t *data, const char *name, const struct vec2 *val);\nEXPORT void obs_data_set_vec3(obs_data_t *data, const char *name, const struct vec3 *val);\nEXPORT void obs_data_set_vec4(obs_data_t *data, const char *name, const struct vec4 *val);\nEXPORT void obs_data_set_quat(obs_data_t *data, const char *name, const struct quat *val);\n\nEXPORT void obs_data_set_default_vec2(obs_data_t *data, const char *name, const struct vec2 *val);\nEXPORT void obs_data_set_default_vec3(obs_data_t *data, const char *name, const struct vec3 *val);\nEXPORT void obs_data_set_default_vec4(obs_data_t *data, const char *name, const struct vec4 *val);\nEXPORT void obs_data_set_default_quat(obs_data_t *data, const char *name, const struct quat *val);\n\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_vec2(obs_data_t *data, const char *name, const struct vec2 *val);\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_vec3(obs_data_t *data, const char *name, const struct vec3 *val);\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_vec4(obs_data_t *data, const char *name, const struct vec4 *val);\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_quat(obs_data_t *data, const char *name, const struct quat *val);\n\nEXPORT void obs_data_get_vec2(obs_data_t *data, const char *name, struct vec2 *val);\nEXPORT void obs_data_get_vec3(obs_data_t *data, const char *name, struct vec3 *val);\nEXPORT void obs_data_get_vec4(obs_data_t *data, const char *name, struct vec4 *val);\nEXPORT void obs_data_get_quat(obs_data_t *data, const char *name, struct quat *val);\n\nEXPORT void obs_data_get_default_vec2(obs_data_t *data, const char *name, struct vec2 *val);\nEXPORT void obs_data_get_default_vec3(obs_data_t *data, const char *name, struct vec3 *val);\nEXPORT void obs_data_get_default_vec4(obs_data_t *data, const char *name, struct vec4 *val);\nEXPORT void obs_data_get_default_quat(obs_data_t *data, const char *name, struct quat *val);\n\nOBS_DEPRECATED EXPORT void obs_data_get_autoselect_vec2(obs_data_t *data, const char *name, struct vec2 *val);\nOBS_DEPRECATED EXPORT void obs_data_get_autoselect_vec3(obs_data_t *data, const char *name, struct vec3 *val);\nOBS_DEPRECATED EXPORT void obs_data_get_autoselect_vec4(obs_data_t *data, const char *name, struct vec4 *val);\nOBS_DEPRECATED EXPORT void obs_data_get_autoselect_quat(obs_data_t *data, const char *name, struct quat *val);\n\n/* ------------------------------------------------------------------------- */\n/* Helper functions for media_frames_per_second/OBS_PROPERTY_FRAME_RATE */\nEXPORT void obs_data_set_frames_per_second(obs_data_t *data, const char *name, struct media_frames_per_second fps,\n\t\t\t\t\t   const char *option);\nEXPORT void obs_data_set_default_frames_per_second(obs_data_t *data, const char *name,\n\t\t\t\t\t\t   struct media_frames_per_second fps, const char *option);\nOBS_DEPRECATED EXPORT void obs_data_set_autoselect_frames_per_second(obs_data_t *data, const char *name,\n\t\t\t\t\t\t\t\t     struct media_frames_per_second fps,\n\t\t\t\t\t\t\t\t     const char *option);\n\nEXPORT bool obs_data_get_frames_per_second(obs_data_t *data, const char *name, struct media_frames_per_second *fps,\n\t\t\t\t\t   const char **option);\nEXPORT bool obs_data_get_default_frames_per_second(obs_data_t *data, const char *name,\n\t\t\t\t\t\t   struct media_frames_per_second *fps, const char **option);\nEXPORT bool obs_data_get_autoselect_frames_per_second(obs_data_t *data, const char *name,\n\t\t\t\t\t\t      struct media_frames_per_second *fps, const char **option);\n\nEXPORT void obs_data_item_set_frames_per_second(obs_data_item_t **item, struct media_frames_per_second fps,\n\t\t\t\t\t\tconst char *option);\nEXPORT void obs_data_item_set_default_frames_per_second(obs_data_item_t **item, struct media_frames_per_second fps,\n\t\t\t\t\t\t\tconst char *option);\nOBS_DEPRECATED EXPORT void obs_data_item_set_autoselect_frames_per_second(obs_data_item_t **item,\n\t\t\t\t\t\t\t\t\t  struct media_frames_per_second fps,\n\t\t\t\t\t\t\t\t\t  const char *option);\n\nEXPORT bool obs_data_item_get_frames_per_second(obs_data_item_t *item, struct media_frames_per_second *fps,\n\t\t\t\t\t\tconst char **option);\nEXPORT bool obs_data_item_get_default_frames_per_second(obs_data_item_t *item, struct media_frames_per_second *fps,\n\t\t\t\t\t\t\tconst char **option);\nOBS_DEPRECATED EXPORT bool obs_data_item_get_autoselect_frames_per_second(obs_data_item_t *item,\n\t\t\t\t\t\t\t\t\t  struct media_frames_per_second *fps,\n\t\t\t\t\t\t\t\t\t  const char **option);\n\n/* ------------------------------------------------------------------------- */\n/* OBS-specific functions */\n\nstatic inline obs_data_t *obs_data_newref(obs_data_t *data)\n{\n\tif (data)\n\t\tobs_data_addref(data);\n\telse\n\t\tdata = obs_data_create();\n\n\treturn data;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-defs.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n/** Maximum number of source channels for output and per display */\n#define MAX_CHANNELS 64\n\n#define OBS_ALIGN_CENTER (0)\n#define OBS_ALIGN_LEFT (1 << 0)\n#define OBS_ALIGN_RIGHT (1 << 1)\n#define OBS_ALIGN_TOP (1 << 2)\n#define OBS_ALIGN_BOTTOM (1 << 3)\n\n#define MODULE_SUCCESS 0\n#define MODULE_ERROR -1\n#define MODULE_FAILED_TO_OPEN -2\n#define MODULE_FILE_NOT_FOUND MODULE_FAILED_TO_OPEN /* DEPRECATED! */\n#define MODULE_MISSING_EXPORTS -3\n#define MODULE_INCOMPATIBLE_VER -4\n#define MODULE_HARDCODED_SKIP -5\n\n#define OBS_OUTPUT_SUCCESS 0\n#define OBS_OUTPUT_BAD_PATH -1\n#define OBS_OUTPUT_CONNECT_FAILED -2\n#define OBS_OUTPUT_INVALID_STREAM -3\n#define OBS_OUTPUT_ERROR -4\n#define OBS_OUTPUT_DISCONNECTED -5\n#define OBS_OUTPUT_UNSUPPORTED -6\n#define OBS_OUTPUT_NO_SPACE -7\n#define OBS_OUTPUT_ENCODE_ERROR -8\n#define OBS_OUTPUT_HDR_DISABLED -9\n\n#define OBS_VIDEO_SUCCESS 0\n#define OBS_VIDEO_FAIL -1\n#define OBS_VIDEO_NOT_SUPPORTED -2\n#define OBS_VIDEO_INVALID_PARAM -3\n#define OBS_VIDEO_CURRENTLY_ACTIVE -4\n#define OBS_VIDEO_MODULE_NOT_FOUND -5\n"
  },
  {
    "path": "libobs/obs-display.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"graphics/vec4.h\"\n#include \"obs.h\"\n#include \"obs-internal.h\"\n\nbool obs_display_init(struct obs_display *display, const struct gs_init_data *graphics_data)\n{\n\tpthread_mutex_init_value(&display->draw_callbacks_mutex);\n\tpthread_mutex_init_value(&display->draw_info_mutex);\n\n#if defined(_WIN32)\n\t/* Conservative test for NVIDIA flickering in multi-GPU setups */\n\tdisplay->use_clear_workaround = gs_get_adapter_count() > 1 && !gs_can_adapter_fast_clear();\n#else\n\tdisplay->use_clear_workaround = false;\n#endif\n\n\tif (graphics_data) {\n\t\tdisplay->swap = gs_swapchain_create(graphics_data);\n\t\tif (!display->swap) {\n\t\t\tblog(LOG_ERROR, \"obs_display_init: Failed to \"\n\t\t\t\t\t\"create swap chain\");\n\t\t\treturn false;\n\t\t}\n\n\t\tconst uint32_t cx = graphics_data->cx;\n\t\tconst uint32_t cy = graphics_data->cy;\n\t\tdisplay->cx = cx;\n\t\tdisplay->cy = cy;\n\t\tdisplay->next_cx = cx;\n\t\tdisplay->next_cy = cy;\n\t}\n\n\tif (pthread_mutex_init(&display->draw_callbacks_mutex, NULL) != 0) {\n\t\tblog(LOG_ERROR, \"obs_display_init: Failed to create mutex\");\n\t\treturn false;\n\t}\n\tif (pthread_mutex_init(&display->draw_info_mutex, NULL) != 0) {\n\t\tblog(LOG_ERROR, \"obs_display_init: Failed to create mutex\");\n\t\treturn false;\n\t}\n\n\tdisplay->enabled = true;\n\treturn true;\n}\n\nobs_display_t *obs_display_create(const struct gs_init_data *graphics_data, uint32_t background_color)\n{\n\tstruct obs_display *display = bzalloc(sizeof(struct obs_display));\n\n\tgs_enter_context(obs->video.graphics);\n\n\tdisplay->background_color = background_color;\n\n\tif (!obs_display_init(display, graphics_data)) {\n\t\tobs_display_destroy(display);\n\t\tdisplay = NULL;\n\t} else {\n\t\tpthread_mutex_lock(&obs->data.displays_mutex);\n\t\tdisplay->prev_next = &obs->data.first_display;\n\t\tdisplay->next = obs->data.first_display;\n\t\tobs->data.first_display = display;\n\t\tif (display->next)\n\t\t\tdisplay->next->prev_next = &display->next;\n\t\tpthread_mutex_unlock(&obs->data.displays_mutex);\n\t}\n\n\tgs_leave_context();\n\n\treturn display;\n}\n\nvoid obs_display_free(obs_display_t *display)\n{\n\tpthread_mutex_destroy(&display->draw_callbacks_mutex);\n\tpthread_mutex_destroy(&display->draw_info_mutex);\n\tda_free(display->draw_callbacks);\n\n\tif (display->swap) {\n\t\tgs_swapchain_destroy(display->swap);\n\t\tdisplay->swap = NULL;\n\t}\n}\n\nvoid obs_display_destroy(obs_display_t *display)\n{\n\tif (display) {\n\t\tpthread_mutex_lock(&obs->data.displays_mutex);\n\t\tif (display->prev_next)\n\t\t\t*display->prev_next = display->next;\n\t\tif (display->next)\n\t\t\tdisplay->next->prev_next = display->prev_next;\n\t\tpthread_mutex_unlock(&obs->data.displays_mutex);\n\n\t\tobs_enter_graphics();\n\t\tobs_display_free(display);\n\t\tobs_leave_graphics();\n\n\t\tbfree(display);\n\t}\n}\n\nvoid obs_display_resize(obs_display_t *display, uint32_t cx, uint32_t cy)\n{\n\tif (!display)\n\t\treturn;\n\n\tpthread_mutex_lock(&display->draw_info_mutex);\n\n\tdisplay->next_cx = cx;\n\tdisplay->next_cy = cy;\n\n\tpthread_mutex_unlock(&display->draw_info_mutex);\n}\n\nvoid obs_display_update_color_space(obs_display_t *display)\n{\n\tif (!display)\n\t\treturn;\n\n\tpthread_mutex_lock(&display->draw_info_mutex);\n\n\tdisplay->update_color_space = true;\n\n\tpthread_mutex_unlock(&display->draw_info_mutex);\n}\n\nvoid obs_display_add_draw_callback(obs_display_t *display, void (*draw)(void *param, uint32_t cx, uint32_t cy),\n\t\t\t\t   void *param)\n{\n\tif (!display)\n\t\treturn;\n\n\tstruct draw_callback data = {draw, param};\n\n\tpthread_mutex_lock(&display->draw_callbacks_mutex);\n\tda_push_back(display->draw_callbacks, &data);\n\tpthread_mutex_unlock(&display->draw_callbacks_mutex);\n}\n\nvoid obs_display_remove_draw_callback(obs_display_t *display, void (*draw)(void *param, uint32_t cx, uint32_t cy),\n\t\t\t\t      void *param)\n{\n\tif (!display)\n\t\treturn;\n\n\tstruct draw_callback data = {draw, param};\n\n\tpthread_mutex_lock(&display->draw_callbacks_mutex);\n\tda_erase_item(display->draw_callbacks, &data);\n\tpthread_mutex_unlock(&display->draw_callbacks_mutex);\n}\n\nstatic inline bool render_display_begin(struct obs_display *display, uint32_t cx, uint32_t cy, bool update_color_space)\n{\n\tstruct vec4 clear_color;\n\n\tgs_load_swapchain(display->swap);\n\n\tif ((display->cx != cx) || (display->cy != cy)) {\n\t\tgs_resize(cx, cy);\n\t\tdisplay->cx = cx;\n\t\tdisplay->cy = cy;\n\t} else if (update_color_space) {\n\t\tgs_update_color_space();\n\t}\n\n\tconst bool success = gs_is_present_ready();\n\tif (success) {\n\t\tgs_begin_scene();\n\n\t\t/*\n\t\t * In contrast to OpenGL or Direct3D 11, Metal and Direct3D 12 require the clear color to use linear gamma\n\t\t * as either the load command to clear the render target (Metal) or the explicit clear command seem to operate\n\t\t * on the render target in linear space.\n\t\t *\n\t\t * As OpenGL is implemented via Metal on Apple Silicon Macs and \"glClear\" has to be emulated via an explicit\n\t\t * render pass that returns the clear color for every fragment, the color becomes subject to automatic sRGB\n\t\t * gamma encoding if the render target uses an sRGB color format.\n\t\t */\n#if defined(__APPLE__) && defined(__aarch64__)\n\t\tvec4_from_rgba_srgb(&clear_color, display->background_color);\n#else\n\t\tif (gs_get_color_space() == GS_CS_SRGB)\n\t\t\tvec4_from_rgba(&clear_color, display->background_color);\n\t\telse\n\t\t\tvec4_from_rgba_srgb(&clear_color, display->background_color);\n#endif\n\t\tclear_color.w = 1.0f;\n\n\t\tconst bool use_clear_workaround = display->use_clear_workaround;\n\n\t\tuint32_t clear_flags = GS_CLEAR_DEPTH | GS_CLEAR_STENCIL;\n\t\tif (!use_clear_workaround)\n\t\t\tclear_flags |= GS_CLEAR_COLOR;\n\t\tgs_clear(clear_flags, &clear_color, 1.0f, 0);\n\n\t\tgs_enable_depth_test(false);\n\t\t/* gs_enable_blending(false); */\n\t\tgs_set_cull_mode(GS_NEITHER);\n\n\t\tgs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);\n\t\tgs_set_viewport(0, 0, cx, cy);\n\n\t\tif (use_clear_workaround) {\n\t\t\tgs_effect_t *const solid_effect = obs->video.solid_effect;\n\t\t\tgs_effect_set_vec4(gs_effect_get_param_by_name(solid_effect, \"color\"), &clear_color);\n\t\t\twhile (gs_effect_loop(solid_effect, \"Solid\"))\n\t\t\t\tgs_draw_sprite(NULL, 0, cx, cy);\n\t\t}\n\t}\n\n\treturn success;\n}\n\nstatic inline void render_display_end()\n{\n\tgs_end_scene();\n}\n\nvoid render_display(struct obs_display *display)\n{\n\tuint32_t cx, cy;\n\tbool update_color_space;\n\n\tif (!display || !display->enabled)\n\t\treturn;\n\n\t/* -------------------------------------------- */\n\n\tpthread_mutex_lock(&display->draw_info_mutex);\n\n\tcx = display->next_cx;\n\tcy = display->next_cy;\n\tupdate_color_space = display->update_color_space;\n\n\tdisplay->update_color_space = false;\n\n\tpthread_mutex_unlock(&display->draw_info_mutex);\n\n\t/* -------------------------------------------- */\n\n\tif (render_display_begin(display, cx, cy, update_color_space)) {\n\t\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DISPLAY, \"obs_display\");\n\n\t\tpthread_mutex_lock(&display->draw_callbacks_mutex);\n\n\t\tfor (size_t i = 0; i < display->draw_callbacks.num; i++) {\n\t\t\tstruct draw_callback *callback;\n\t\t\tcallback = display->draw_callbacks.array + i;\n\n\t\t\tcallback->draw(callback->param, cx, cy);\n\t\t}\n\n\t\tpthread_mutex_unlock(&display->draw_callbacks_mutex);\n\n\t\trender_display_end();\n\n\t\tGS_DEBUG_MARKER_END();\n\n\t\tgs_present();\n\t}\n}\n\nvoid obs_display_set_enabled(obs_display_t *display, bool enable)\n{\n\tif (display)\n\t\tdisplay->enabled = enable;\n}\n\nbool obs_display_enabled(obs_display_t *display)\n{\n\treturn display ? display->enabled : false;\n}\n\nvoid obs_display_set_background_color(obs_display_t *display, uint32_t color)\n{\n\tif (display)\n\t\tdisplay->background_color = color;\n}\n\nvoid obs_display_size(obs_display_t *display, uint32_t *width, uint32_t *height)\n{\n\t*width = 0;\n\t*height = 0;\n\n\tif (display) {\n\t\tpthread_mutex_lock(&display->draw_info_mutex);\n\n\t\t*width = display->cx;\n\t\t*height = display->cy;\n\n\t\tpthread_mutex_unlock(&display->draw_info_mutex);\n\t}\n}\n"
  },
  {
    "path": "libobs/obs-encoder.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs.h\"\n#include \"obs-internal.h\"\n#include \"util/util_uint64.h\"\n\n#define encoder_active(encoder) os_atomic_load_bool(&encoder->active)\n#define set_encoder_active(encoder, val) os_atomic_set_bool(&encoder->active, val)\n\n#define get_weak(encoder) ((obs_weak_encoder_t *)encoder->context.control)\n\nstatic void encoder_set_video(obs_encoder_t *encoder, video_t *video);\n\nstruct obs_encoder_info *find_encoder(const char *id)\n{\n\tfor (size_t i = 0; i < obs->encoder_types.num; i++) {\n\t\tstruct obs_encoder_info *info = obs->encoder_types.array + i;\n\n\t\tif (strcmp(info->id, id) == 0)\n\t\t\treturn info;\n\t}\n\n\treturn NULL;\n}\n\nconst char *obs_encoder_get_display_name(const char *id)\n{\n\tstruct obs_encoder_info *ei = find_encoder(id);\n\treturn ei ? ei->get_name(ei->type_data) : NULL;\n}\n\nobs_module_t *obs_encoder_get_module(const char *id)\n{\n\tobs_module_t *module = obs->first_module;\n\twhile (module) {\n\t\tfor (size_t i = 0; i < module->encoders.num; i++) {\n\t\t\tif (strcmp(module->encoders.array[i], id) == 0) {\n\t\t\t\treturn module;\n\t\t\t}\n\t\t}\n\t\tmodule = module->next;\n\t}\n\n\tmodule = obs->first_disabled_module;\n\twhile (module) {\n\t\tfor (size_t i = 0; i < module->encoders.num; i++) {\n\t\t\tif (strcmp(module->encoders.array[i], id) == 0) {\n\t\t\t\treturn module;\n\t\t\t}\n\t\t}\n\t\tmodule = module->next;\n\t}\n\n\treturn NULL;\n}\n\nenum obs_module_load_state obs_encoder_load_state(const char *id)\n{\n\tobs_module_t *module = obs_encoder_get_module(id);\n\tif (!module) {\n\t\treturn OBS_MODULE_MISSING;\n\t}\n\treturn module->load_state;\n}\n\nstatic bool init_encoder(struct obs_encoder *encoder, const char *name, obs_data_t *settings, obs_data_t *hotkey_data)\n{\n\tpthread_mutex_init_value(&encoder->init_mutex);\n\tpthread_mutex_init_value(&encoder->callbacks_mutex);\n\tpthread_mutex_init_value(&encoder->outputs_mutex);\n\tpthread_mutex_init_value(&encoder->pause.mutex);\n\tpthread_mutex_init_value(&encoder->roi_mutex);\n\n\tif (!obs_context_data_init(&encoder->context, OBS_OBJ_TYPE_ENCODER, settings, name, NULL, hotkey_data, false))\n\t\treturn false;\n\tif (pthread_mutex_init_recursive(&encoder->init_mutex) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init_recursive(&encoder->callbacks_mutex) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&encoder->outputs_mutex, NULL) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&encoder->pause.mutex, NULL) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&encoder->roi_mutex, NULL) != 0)\n\t\treturn false;\n\n\tif (encoder->orig_info.get_defaults) {\n\t\tencoder->orig_info.get_defaults(encoder->context.settings);\n\t}\n\tif (encoder->orig_info.get_defaults2) {\n\t\tencoder->orig_info.get_defaults2(encoder->context.settings, encoder->orig_info.type_data);\n\t}\n\n\treturn true;\n}\n\nstatic struct obs_encoder *create_encoder(const char *id, enum obs_encoder_type type, const char *name,\n\t\t\t\t\t  obs_data_t *settings, size_t mixer_idx, obs_data_t *hotkey_data)\n{\n\tstruct obs_encoder *encoder;\n\tstruct obs_encoder_info *ei = find_encoder(id);\n\tbool success;\n\n\tif (ei && ei->type != type)\n\t\treturn NULL;\n\n\tencoder = bzalloc(sizeof(struct obs_encoder));\n\tencoder->mixer_idx = mixer_idx;\n\n\tif (!ei) {\n\t\tblog(LOG_ERROR, \"Encoder ID '%s' not found\", id);\n\n\t\tencoder->info.id = bstrdup(id);\n\t\tencoder->info.type = type;\n\t\tencoder->owns_info_id = true;\n\t\tencoder->orig_info = encoder->info;\n\t} else {\n\t\tencoder->info = *ei;\n\t\tencoder->orig_info = *ei;\n\t}\n\n\tsuccess = init_encoder(encoder, name, settings, hotkey_data);\n\tif (!success) {\n\t\tblog(LOG_ERROR, \"creating encoder '%s' (%s) failed\", name, id);\n\t\tobs_encoder_destroy(encoder);\n\t\treturn NULL;\n\t}\n\n\tobs_context_init_control(&encoder->context, encoder, (obs_destroy_cb)obs_encoder_destroy);\n\tobs_context_data_insert(&encoder->context, &obs->data.encoders_mutex, &obs->data.first_encoder);\n\n\tif (type == OBS_ENCODER_VIDEO) {\n\t\tencoder->frame_rate_divisor = 1;\n\t}\n\n\tblog(LOG_DEBUG, \"encoder '%s' (%s) created\", name, id);\n\n\tif (ei && ei->caps & OBS_ENCODER_CAP_DEPRECATED) {\n\t\tblog(LOG_WARNING, \"Encoder ID '%s' is deprecated and may be removed in a future version.\", id);\n\t}\n\n\treturn encoder;\n}\n\nobs_encoder_t *obs_video_encoder_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data)\n{\n\tif (!name || !id)\n\t\treturn NULL;\n\treturn create_encoder(id, OBS_ENCODER_VIDEO, name, settings, 0, hotkey_data);\n}\n\nobs_encoder_t *obs_audio_encoder_create(const char *id, const char *name, obs_data_t *settings, size_t mixer_idx,\n\t\t\t\t\tobs_data_t *hotkey_data)\n{\n\tif (!name || !id)\n\t\treturn NULL;\n\treturn create_encoder(id, OBS_ENCODER_AUDIO, name, settings, mixer_idx, hotkey_data);\n}\n\nstatic void receive_video(void *param, struct video_data *frame);\nstatic void receive_audio(void *param, size_t mix_idx, struct audio_data *data);\n\nstatic inline void get_audio_info(const struct obs_encoder *encoder, struct audio_convert_info *info)\n{\n\tconst struct audio_output_info *aoi;\n\taoi = audio_output_get_info(encoder->media);\n\n\tif (info->format == AUDIO_FORMAT_UNKNOWN)\n\t\tinfo->format = aoi->format;\n\tif (!info->samples_per_sec)\n\t\tinfo->samples_per_sec = aoi->samples_per_sec;\n\tif (info->speakers == SPEAKERS_UNKNOWN)\n\t\tinfo->speakers = aoi->speakers;\n\n\tif (encoder->info.get_audio_info)\n\t\tencoder->info.get_audio_info(encoder->context.data, info);\n}\n\nstatic inline void get_video_info(struct obs_encoder *encoder, struct video_scale_info *info)\n{\n\tconst struct video_output_info *voi;\n\tvoi = video_output_get_info(encoder->media);\n\n\tinfo->format = voi->format;\n\tinfo->colorspace = voi->colorspace;\n\tinfo->range = voi->range;\n\tinfo->width = obs_encoder_get_width(encoder);\n\tinfo->height = obs_encoder_get_height(encoder);\n\n\tif (encoder->info.get_video_info)\n\t\tencoder->info.get_video_info(encoder->context.data, info);\n\n\t/**\n\t * Prevent video output from performing an actual scale. If GPU scaling is\n\t * enabled, the voi will contain the scaled size. Therefore, GPU scaling\n\t * takes priority over self-scaling functionality.\n\t */\n\tif ((encoder->info.caps & OBS_ENCODER_CAP_SCALING) != 0) {\n\t\tinfo->width = voi->width;\n\t\tinfo->height = voi->height;\n\t}\n}\n\nstatic inline bool gpu_encode_available(const struct obs_encoder *encoder)\n{\n\tstruct obs_core_video_mix *video = get_mix_for_video(encoder->media);\n\tif (!video)\n\t\treturn false;\n\treturn (encoder->info.caps & OBS_ENCODER_CAP_PASS_TEXTURE) != 0 &&\n\t       (video->using_p010_tex || video->using_nv12_tex);\n}\n\n/**\n * GPU based rescaling is currently implemented via core video mixes,\n * i.e. a core mix with matching width/height/format/colorspace/range\n * will be created if it doesn't exist already to generate encoder\n * input\n */\nstatic void maybe_set_up_gpu_rescale(struct obs_encoder *encoder)\n{\n\tstruct obs_core_video_mix *mix, *current_mix;\n\tbool create_mix = true;\n\tstruct obs_video_info ovi;\n\tconst struct video_output_info *info;\n\tuint32_t width;\n\tuint32_t height;\n\tenum video_format format;\n\tenum video_colorspace space;\n\tenum video_range_type range;\n\n\tif (!encoder->media)\n\t\treturn;\n\tif (encoder->gpu_scale_type == OBS_SCALE_DISABLE)\n\t\treturn;\n\tif (!encoder->scaled_height && !encoder->scaled_width && encoder->preferred_format == VIDEO_FORMAT_NONE &&\n\t    encoder->preferred_space == VIDEO_CS_DEFAULT && encoder->preferred_range == VIDEO_RANGE_DEFAULT)\n\t\treturn;\n\n\tinfo = video_output_get_info(encoder->media);\n\twidth = encoder->scaled_width ? encoder->scaled_width : info->width;\n\theight = encoder->scaled_height ? encoder->scaled_height : info->height;\n\tformat = encoder->preferred_format != VIDEO_FORMAT_NONE ? encoder->preferred_format : info->format;\n\tspace = encoder->preferred_space != VIDEO_CS_DEFAULT ? encoder->preferred_space : info->colorspace;\n\trange = encoder->preferred_range != VIDEO_RANGE_DEFAULT ? encoder->preferred_range : info->range;\n\n\tcurrent_mix = get_mix_for_video(encoder->media);\n\tif (!current_mix)\n\t\treturn;\n\n\t/* Store original video_t so it can be restored if scaling is disabled. */\n\tif (!current_mix->encoder_only_mix)\n\t\tencoder->original_video = encoder->media;\n\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0; i < obs->video.mixes.num; i++) {\n\t\tstruct obs_core_video_mix *current = obs->video.mixes.array[i];\n\t\tconst struct video_output_info *voi = video_output_get_info(current->video);\n\t\tif (current_mix->view != current->view)\n\t\t\tcontinue;\n\n\t\tif (current->ovi.scale_type != encoder->gpu_scale_type)\n\t\t\tcontinue;\n\n\t\tif (voi->width != width || voi->height != height)\n\t\t\tcontinue;\n\n\t\tif (voi->format != format || voi->colorspace != space || voi->range != range)\n\t\t\tcontinue;\n\n\t\tcurrent->encoder_refs += 1;\n\t\tobs_encoder_set_video(encoder, current->video);\n\t\tcreate_mix = false;\n\t\tbreak;\n\t}\n\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\n\tif (!create_mix)\n\t\treturn;\n\n\tovi = current_mix->ovi;\n\n\tovi.output_format = format;\n\tovi.colorspace = space;\n\tovi.range = range;\n\n\tovi.output_height = height;\n\tovi.output_width = width;\n\tovi.scale_type = encoder->gpu_scale_type;\n\n\tovi.gpu_conversion = true;\n\n\tmix = obs_create_video_mix(&ovi);\n\tif (!mix)\n\t\treturn;\n\n\tmix->encoder_only_mix = true;\n\tmix->encoder_refs = 1;\n\tmix->view = current_mix->view;\n\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\n\t// double check that nobody else added a matching mix while we've created our mix\n\tfor (size_t i = 0; i < obs->video.mixes.num; i++) {\n\t\tstruct obs_core_video_mix *current = obs->video.mixes.array[i];\n\t\tconst struct video_output_info *voi = video_output_get_info(current->video);\n\t\tif (current->view != current_mix->view)\n\t\t\tcontinue;\n\n\t\tif (current->ovi.scale_type != encoder->gpu_scale_type)\n\t\t\tcontinue;\n\n\t\tif (voi->width != width || voi->height != height)\n\t\t\tcontinue;\n\n\t\tif (voi->format != format || voi->colorspace != space || voi->range != range)\n\t\t\tcontinue;\n\n\t\tobs_encoder_set_video(encoder, current->video);\n\t\tcreate_mix = false;\n\t\tbreak;\n\t}\n\n\tif (!create_mix) {\n\t\tobs_free_video_mix(mix);\n\t} else {\n\t\tda_push_back(obs->video.mixes, &mix);\n\t\tobs_encoder_set_video(encoder, mix->video);\n\t}\n\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n}\n\nstatic void add_connection(struct obs_encoder *encoder)\n{\n\tif (encoder->info.type == OBS_ENCODER_AUDIO) {\n\t\tstruct audio_convert_info audio_info = {0};\n\t\tget_audio_info(encoder, &audio_info);\n\n\t\taudio_output_connect(encoder->media, encoder->mixer_idx, &audio_info, receive_audio, encoder);\n\t} else {\n\t\tstruct video_scale_info info = {0};\n\t\tget_video_info(encoder, &info);\n\n\t\tif (gpu_encode_available(encoder)) {\n\t\t\tstart_gpu_encode(encoder);\n\t\t} else {\n\t\t\tstart_raw_video(encoder->media, &info, encoder->frame_rate_divisor, receive_video, encoder);\n\t\t}\n\t}\n\n\tif (encoder->encoder_group) {\n\t\tpthread_mutex_lock(&encoder->encoder_group->mutex);\n\t\tencoder->encoder_group->num_encoders_started += 1;\n\t\tbool ready = encoder->encoder_group->num_encoders_started >= encoder->encoder_group->encoders.num;\n\t\tpthread_mutex_unlock(&encoder->encoder_group->mutex);\n\t\tif (ready)\n\t\t\tadd_ready_encoder_group(encoder);\n\t}\n\n\tset_encoder_active(encoder, true);\n}\n\nvoid obs_encoder_group_actually_destroy(obs_encoder_group_t *group);\nstatic void remove_connection(struct obs_encoder *encoder, bool shutdown)\n{\n\tif (encoder->info.type == OBS_ENCODER_AUDIO) {\n\t\taudio_output_disconnect(encoder->media, encoder->mixer_idx, receive_audio, encoder);\n\t} else {\n\t\tif (gpu_encode_available(encoder)) {\n\t\t\tstop_gpu_encode(encoder);\n\t\t} else {\n\t\t\tstop_raw_video(encoder->media, receive_video, encoder);\n\t\t}\n\t}\n\n\tif (encoder->encoder_group) {\n\t\tpthread_mutex_lock(&encoder->encoder_group->mutex);\n\t\tif (--encoder->encoder_group->num_encoders_started == 0)\n\t\t\tencoder->encoder_group->start_timestamp = 0;\n\t\tpthread_mutex_unlock(&encoder->encoder_group->mutex);\n\t}\n\n\t/* obs_encoder_shutdown locks init_mutex, so don't call it on encode\n\t * errors, otherwise you can get a deadlock with outputs when they end\n\t * data capture, which will lock init_mutex and the video callback\n\t * mutex in the reverse order.  instead, call shutdown before starting\n\t * up again */\n\tif (shutdown)\n\t\tobs_encoder_shutdown(encoder);\n\tencoder->initialized = false;\n\n\tset_encoder_active(encoder, false);\n}\n\nstatic inline void free_audio_buffers(struct obs_encoder *encoder)\n{\n\tfor (size_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\tdeque_free(&encoder->audio_input_buffer[i]);\n\t\tbfree(encoder->audio_output_buffer[i]);\n\t\tencoder->audio_output_buffer[i] = NULL;\n\t}\n}\n\nvoid obs_encoder_destroy(obs_encoder_t *encoder)\n{\n\tif (encoder) {\n\t\tpthread_mutex_lock(&encoder->outputs_mutex);\n\t\tfor (size_t i = 0; i < encoder->outputs.num; i++) {\n\t\t\tstruct obs_output *output = encoder->outputs.array[i];\n\t\t\t// This happens while the output is still \"active\", so\n\t\t\t// remove without checking active\n\t\t\tobs_output_remove_encoder_internal(output, encoder);\n\t\t}\n\t\tda_free(encoder->outputs);\n\t\tpthread_mutex_unlock(&encoder->outputs_mutex);\n\n\t\tblog(LOG_DEBUG, \"encoder '%s' destroyed\", encoder->context.name);\n\n\t\tobs_encoder_set_group(encoder, NULL);\n\n\t\tfree_audio_buffers(encoder);\n\n\t\tif (encoder->context.data)\n\t\t\tencoder->info.destroy(encoder->context.data);\n\t\tda_free(encoder->callbacks);\n\t\tda_free(encoder->roi);\n\t\tda_free(encoder->encoder_packet_times);\n\t\tpthread_mutex_destroy(&encoder->init_mutex);\n\t\tpthread_mutex_destroy(&encoder->callbacks_mutex);\n\t\tpthread_mutex_destroy(&encoder->outputs_mutex);\n\t\tpthread_mutex_destroy(&encoder->pause.mutex);\n\t\tpthread_mutex_destroy(&encoder->roi_mutex);\n\t\tobs_context_data_free(&encoder->context);\n\t\tif (encoder->owns_info_id)\n\t\t\tbfree((void *)encoder->info.id);\n\t\tif (encoder->last_error_message)\n\t\t\tbfree(encoder->last_error_message);\n\t\tif (encoder->fps_override)\n\t\t\tvideo_output_free_frame_rate_divisor(encoder->fps_override);\n\t\tbfree(encoder);\n\t}\n}\n\nconst char *obs_encoder_get_name(const obs_encoder_t *encoder)\n{\n\treturn obs_encoder_valid(encoder, \"obs_encoder_get_name\") ? encoder->context.name : NULL;\n}\n\nvoid obs_encoder_set_name(obs_encoder_t *encoder, const char *name)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_set_name\"))\n\t\treturn;\n\n\tif (name && *name && strcmp(name, encoder->context.name) != 0)\n\t\tobs_context_data_setname(&encoder->context, name);\n}\n\nstatic inline obs_data_t *get_defaults(const struct obs_encoder_info *info)\n{\n\tobs_data_t *settings = obs_data_create();\n\tif (info->get_defaults) {\n\t\tinfo->get_defaults(settings);\n\t}\n\tif (info->get_defaults2) {\n\t\tinfo->get_defaults2(settings, info->type_data);\n\t}\n\treturn settings;\n}\n\nobs_data_t *obs_encoder_defaults(const char *id)\n{\n\tconst struct obs_encoder_info *info = find_encoder(id);\n\treturn (info) ? get_defaults(info) : NULL;\n}\n\nobs_data_t *obs_encoder_get_defaults(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_defaults\"))\n\t\treturn NULL;\n\n\treturn get_defaults(&encoder->info);\n}\n\nobs_properties_t *obs_get_encoder_properties(const char *id)\n{\n\tconst struct obs_encoder_info *ei = find_encoder(id);\n\tif (ei && (ei->get_properties || ei->get_properties2)) {\n\t\tobs_data_t *defaults = get_defaults(ei);\n\t\tobs_properties_t *properties = NULL;\n\n\t\tif (ei->get_properties2) {\n\t\t\tproperties = ei->get_properties2(NULL, ei->type_data);\n\t\t} else if (ei->get_properties) {\n\t\t\tproperties = ei->get_properties(NULL);\n\t\t}\n\n\t\tobs_properties_apply_settings(properties, defaults);\n\t\tobs_data_release(defaults);\n\t\treturn properties;\n\t}\n\treturn NULL;\n}\n\nobs_properties_t *obs_encoder_properties(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_properties\"))\n\t\treturn NULL;\n\n\tif (encoder->orig_info.get_properties2) {\n\t\tobs_properties_t *props;\n\t\tprops = encoder->orig_info.get_properties2(encoder->context.data, encoder->orig_info.type_data);\n\t\tobs_properties_apply_settings(props, encoder->context.settings);\n\t\treturn props;\n\n\t} else if (encoder->orig_info.get_properties) {\n\t\tobs_properties_t *props;\n\t\tprops = encoder->orig_info.get_properties(encoder->context.data);\n\t\tobs_properties_apply_settings(props, encoder->context.settings);\n\t\treturn props;\n\t}\n\n\treturn NULL;\n}\n\nvoid obs_encoder_update(obs_encoder_t *encoder, obs_data_t *settings)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_update\"))\n\t\treturn;\n\n\tobs_data_apply(encoder->context.settings, settings);\n\n\t// Encoder isn't initialized yet, only apply changes to settings\n\tif (!encoder->context.data)\n\t\treturn;\n\n\t// Encoder doesn't support updates\n\tif (!encoder->info.update)\n\t\treturn;\n\n\t// If the encoder is active we defer the update as it may not be\n\t// reentrant. Setting reconfigure_requested to true makes the changes\n\t// apply at the next possible moment in the encoder / GPU encoder\n\t// thread.\n\tif (encoder_active(encoder)) {\n\t\tencoder->reconfigure_requested = true;\n\t} else {\n\t\tencoder->info.update(encoder->context.data, encoder->context.settings);\n\t}\n}\n\nbool obs_encoder_get_extra_data(const obs_encoder_t *encoder, uint8_t **extra_data, size_t *size)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_get_extra_data\"))\n\t\treturn false;\n\n\tif (encoder->info.get_extra_data && encoder->context.data)\n\t\treturn encoder->info.get_extra_data(encoder->context.data, extra_data, size);\n\n\treturn false;\n}\n\nobs_data_t *obs_encoder_get_settings(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_get_settings\"))\n\t\treturn NULL;\n\n\tobs_data_addref(encoder->context.settings);\n\treturn encoder->context.settings;\n}\n\nstatic inline void reset_audio_buffers(struct obs_encoder *encoder)\n{\n\tfree_audio_buffers(encoder);\n\n\tfor (size_t i = 0; i < encoder->planes; i++)\n\t\tencoder->audio_output_buffer[i] = bmalloc(encoder->framesize_bytes);\n}\n\nstatic void intitialize_audio_encoder(struct obs_encoder *encoder)\n{\n\tstruct audio_convert_info info = {0};\n\tget_audio_info(encoder, &info);\n\n\tencoder->samplerate = info.samples_per_sec;\n\tencoder->planes = get_audio_planes(info.format, info.speakers);\n\tencoder->blocksize = get_audio_size(info.format, info.speakers, 1);\n\tencoder->framesize = encoder->info.get_frame_size(encoder->context.data);\n\n\tencoder->framesize_bytes = encoder->blocksize * encoder->framesize;\n\treset_audio_buffers(encoder);\n}\n\nstatic THREAD_LOCAL bool can_reroute = false;\n\nstatic inline bool obs_encoder_initialize_internal(obs_encoder_t *encoder)\n{\n\tif (!encoder->media) {\n\t\tblog(LOG_ERROR, \"obs_encoder_initialize_internal: encoder '%s' has no media set\",\n\t\t     encoder->context.name);\n\t\treturn false;\n\t}\n\n\tif (encoder_active(encoder))\n\t\treturn true;\n\tif (encoder->initialized)\n\t\treturn true;\n\n\tobs_encoder_shutdown(encoder);\n\n\tmaybe_set_up_gpu_rescale(encoder);\n\n\tif (encoder->orig_info.create) {\n\t\tcan_reroute = true;\n\t\tencoder->info = encoder->orig_info;\n\t\tencoder->context.data = encoder->orig_info.create(encoder->context.settings, encoder);\n\t\tcan_reroute = false;\n\t}\n\tif (!encoder->context.data)\n\t\treturn false;\n\n\tif (encoder->orig_info.type == OBS_ENCODER_AUDIO)\n\t\tintitialize_audio_encoder(encoder);\n\n\tencoder->initialized = true;\n\treturn true;\n}\n\nvoid *obs_encoder_create_rerouted(obs_encoder_t *encoder, const char *reroute_id)\n{\n\tif (!obs_ptr_valid(encoder, \"obs_encoder_reroute\"))\n\t\treturn NULL;\n\tif (!obs_ptr_valid(reroute_id, \"obs_encoder_reroute\"))\n\t\treturn NULL;\n\tif (!can_reroute)\n\t\treturn NULL;\n\n\tconst struct obs_encoder_info *ei = find_encoder(reroute_id);\n\tif (ei) {\n\t\tif (ei->type != encoder->orig_info.type || astrcmpi(ei->codec, encoder->orig_info.codec) != 0) {\n\t\t\treturn NULL;\n\t\t}\n\t\tencoder->info = *ei;\n\t\treturn encoder->info.create(encoder->context.settings, encoder);\n\t}\n\n\treturn NULL;\n}\n\nbool obs_encoder_initialize(obs_encoder_t *encoder)\n{\n\tbool success;\n\n\tif (!encoder)\n\t\treturn false;\n\n\tpthread_mutex_lock(&encoder->init_mutex);\n\tsuccess = obs_encoder_initialize_internal(encoder);\n\tpthread_mutex_unlock(&encoder->init_mutex);\n\n\treturn success;\n}\n\n/**\n * free video mix if it's an encoder only video mix\n * see `maybe_set_up_gpu_rescale`\n */\nstatic void maybe_clear_encoder_core_video_mix(obs_encoder_t *encoder)\n{\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0; i < obs->video.mixes.num; i++) {\n\t\tstruct obs_core_video_mix *mix = obs->video.mixes.array[i];\n\t\tif (mix->video != encoder->media)\n\t\t\tcontinue;\n\n\t\tif (!mix->encoder_only_mix)\n\t\t\tbreak;\n\n\t\tencoder_set_video(encoder, encoder->original_video);\n\t\tmix->encoder_refs -= 1;\n\t\tif (mix->encoder_refs == 0) {\n\t\t\tda_erase(obs->video.mixes, i);\n\t\t\tobs_free_video_mix(mix);\n\t\t}\n\t}\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n}\n\nvoid obs_encoder_shutdown(obs_encoder_t *encoder)\n{\n\tpthread_mutex_lock(&encoder->init_mutex);\n\tif (encoder->context.data) {\n\t\tencoder->info.destroy(encoder->context.data);\n\t\tencoder->context.data = NULL;\n\t\tencoder->first_received = false;\n\t\tencoder->offset_usec = 0;\n\t\tencoder->start_ts = 0;\n\t\tencoder->frame_rate_divisor_counter = 0;\n\t\tmaybe_clear_encoder_core_video_mix(encoder);\n\n\t\tfor (size_t i = 0; i < encoder->paired_encoders.num; i++) {\n\t\t\tobs_weak_encoder_release(encoder->paired_encoders.array[i]);\n\t\t}\n\t\tda_free(encoder->paired_encoders);\n\t}\n\tobs_encoder_set_last_error(encoder, NULL);\n\tpthread_mutex_unlock(&encoder->init_mutex);\n}\n\nstatic inline size_t get_callback_idx(const struct obs_encoder *encoder, encoded_callback_t new_packet, void *param)\n{\n\tfor (size_t i = 0; i < encoder->callbacks.num; i++) {\n\t\tstruct encoder_callback *cb = encoder->callbacks.array + i;\n\n\t\tif (cb->new_packet == new_packet && cb->param == param)\n\t\t\treturn i;\n\t}\n\n\treturn DARRAY_INVALID;\n}\n\nvoid pause_reset(struct pause_data *pause)\n{\n\tpthread_mutex_lock(&pause->mutex);\n\tpause->last_video_ts = 0;\n\tpause->ts_start = 0;\n\tpause->ts_end = 0;\n\tpause->ts_offset = 0;\n\tpthread_mutex_unlock(&pause->mutex);\n}\n\nstatic inline void obs_encoder_start_internal(obs_encoder_t *encoder, encoded_callback_t new_packet, void *param)\n{\n\tstruct encoder_callback cb = {false, new_packet, param};\n\tbool first = false;\n\n\tif (!encoder->context.data || !encoder->media)\n\t\treturn;\n\n\tpthread_mutex_lock(&encoder->callbacks_mutex);\n\n\tfirst = (encoder->callbacks.num == 0);\n\n\tsize_t idx = get_callback_idx(encoder, new_packet, param);\n\tif (idx == DARRAY_INVALID)\n\t\tda_push_back(encoder->callbacks, &cb);\n\n\tpthread_mutex_unlock(&encoder->callbacks_mutex);\n\n\tif (first) {\n\t\tos_atomic_set_bool(&encoder->paused, false);\n\t\tpause_reset(&encoder->pause);\n\n\t\tencoder->cur_pts = 0;\n\t\tadd_connection(encoder);\n\t}\n}\n\nvoid obs_encoder_start(obs_encoder_t *encoder, encoded_callback_t new_packet, void *param)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_start\"))\n\t\treturn;\n\tif (!obs_ptr_valid(new_packet, \"obs_encoder_start\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&encoder->init_mutex);\n\tobs_encoder_start_internal(encoder, new_packet, param);\n\tpthread_mutex_unlock(&encoder->init_mutex);\n}\n\nvoid obs_encoder_stop(obs_encoder_t *encoder, encoded_callback_t new_packet, void *param)\n{\n\tbool last = false;\n\tsize_t idx;\n\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_stop\"))\n\t\treturn;\n\tif (!obs_ptr_valid(new_packet, \"obs_encoder_stop\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&encoder->init_mutex);\n\tpthread_mutex_lock(&encoder->callbacks_mutex);\n\n\tidx = get_callback_idx(encoder, new_packet, param);\n\tif (idx != DARRAY_INVALID) {\n\t\tda_erase(encoder->callbacks, idx);\n\t\tlast = (encoder->callbacks.num == 0);\n\t}\n\n\tpthread_mutex_unlock(&encoder->callbacks_mutex);\n\n\tencoder->encoder_packet_times.num = 0;\n\n\tif (last) {\n\t\tremove_connection(encoder, true);\n\t\tpthread_mutex_unlock(&encoder->init_mutex);\n\n\t\tstruct obs_encoder_group *group = encoder->encoder_group;\n\n\t\t/* Destroying the group all the way back here prevents a race\n\t\t * where destruction of the group can prematurely destroy the\n\t\t * encoder within internal functions. This is the point where it\n\t\t * is safe to destroy the group, even if the encoder is then\n\t\t * also destroyed. */\n\t\tif (group) {\n\t\t\tpthread_mutex_lock(&group->mutex);\n\t\t\tif (group->destroy_on_stop && group->num_encoders_started == 0)\n\t\t\t\tobs_encoder_group_actually_destroy(group);\n\t\t\telse\n\t\t\t\tpthread_mutex_unlock(&group->mutex);\n\t\t}\n\n\t\t/* init_mutex already unlocked */\n\t\treturn;\n\t}\n\n\tpthread_mutex_unlock(&encoder->init_mutex);\n}\n\nconst char *obs_encoder_get_codec(const obs_encoder_t *encoder)\n{\n\treturn obs_encoder_valid(encoder, \"obs_encoder_get_codec\") ? encoder->info.codec : NULL;\n}\n\nconst char *obs_get_encoder_codec(const char *id)\n{\n\tstruct obs_encoder_info *info = find_encoder(id);\n\treturn info ? info->codec : NULL;\n}\n\nenum obs_encoder_type obs_encoder_get_type(const obs_encoder_t *encoder)\n{\n\treturn obs_encoder_valid(encoder, \"obs_encoder_get_type\") ? encoder->info.type : OBS_ENCODER_AUDIO;\n}\n\nenum obs_encoder_type obs_get_encoder_type(const char *id)\n{\n\tstruct obs_encoder_info *info = find_encoder(id);\n\treturn info ? info->type : OBS_ENCODER_AUDIO;\n}\n\nuint32_t obs_encoder_get_encoded_frames(const obs_encoder_t *encoder)\n{\n\treturn obs_encoder_valid(encoder, \"obs_output_get_encoded_frames\") ? encoder->encoded_frames : 0;\n}\n\nvoid obs_encoder_set_scaled_size(obs_encoder_t *encoder, uint32_t width, uint32_t height)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_set_scaled_size\"))\n\t\treturn;\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_set_scaled_size: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\tif (encoder_active(encoder)) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Cannot set the scaled \"\n\t\t     \"resolution while the encoder is active\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\tif (encoder->initialized) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Cannot set the scaled resolution \"\n\t\t     \"after the encoder has been initialized\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\n\tconst struct video_output_info *voi;\n\tvoi = video_output_get_info(encoder->media);\n\tif (voi && voi->width == width && voi->height == height) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Scaled resolution \"\n\t\t     \"matches output resolution, scaling \"\n\t\t     \"disabled\",\n\t\t     obs_encoder_get_name(encoder));\n\t\tencoder->scaled_width = encoder->scaled_height = 0;\n\t\treturn;\n\t}\n\n\tencoder->scaled_width = width;\n\tencoder->scaled_height = height;\n}\n\nvoid obs_encoder_set_gpu_scale_type(obs_encoder_t *encoder, enum obs_scale_type gpu_scale_type)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_set_gpu_scale_type\"))\n\t\treturn;\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_set_gpu_scale_type: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\tif (encoder_active(encoder)) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Cannot enable GPU scaling \"\n\t\t     \"while the encoder is active\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\tif (encoder->initialized) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Cannot enable GPU scaling \"\n\t\t     \"after the encoder has been initialized\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\n\tencoder->gpu_scale_type = gpu_scale_type;\n}\n\nbool obs_encoder_set_frame_rate_divisor(obs_encoder_t *encoder, uint32_t frame_rate_divisor)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_set_frame_rate_divisor\"))\n\t\treturn false;\n\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_set_frame_rate_divisor: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn false;\n\t}\n\n\tif (encoder_active(encoder)) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Cannot set frame rate divisor \"\n\t\t     \"while the encoder is active\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn false;\n\t}\n\n\tif (encoder->initialized) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Cannot set frame rate divisor \"\n\t\t     \"after the encoder has been initialized\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn false;\n\t}\n\n\tif (frame_rate_divisor == 0) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Cannot set frame \"\n\t\t     \"rate divisor to 0\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn false;\n\t}\n\n\tencoder->frame_rate_divisor = frame_rate_divisor;\n\n\tif (encoder->fps_override) {\n\t\tvideo_output_free_frame_rate_divisor(encoder->fps_override);\n\t\tencoder->fps_override = NULL;\n\t}\n\n\tif (encoder->media) {\n\t\tencoder->fps_override =\n\t\t\tvideo_output_create_with_frame_rate_divisor(encoder->media, encoder->frame_rate_divisor);\n\t}\n\n\treturn true;\n}\n\nbool obs_encoder_scaling_enabled(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_scaling_enabled\"))\n\t\treturn false;\n\n\treturn encoder->scaled_width || encoder->scaled_height;\n}\n\nuint32_t obs_encoder_get_width(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_get_width\"))\n\t\treturn 0;\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_get_width: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn 0;\n\t}\n\tif (!encoder->media)\n\t\treturn 0;\n\n\treturn encoder->scaled_width != 0 ? encoder->scaled_width : video_output_get_width(encoder->media);\n}\n\nuint32_t obs_encoder_get_height(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_get_height\"))\n\t\treturn 0;\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_get_height: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn 0;\n\t}\n\tif (!encoder->media)\n\t\treturn 0;\n\n\treturn encoder->scaled_height != 0 ? encoder->scaled_height : video_output_get_height(encoder->media);\n}\n\nbool obs_encoder_gpu_scaling_enabled(obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_gpu_scaling_enabled\"))\n\t\treturn 0;\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_gpu_scaling_enabled: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn 0;\n\t}\n\n\treturn encoder->gpu_scale_type != OBS_SCALE_DISABLE;\n}\n\nenum obs_scale_type obs_encoder_get_scale_type(obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_get_scale_type\"))\n\t\treturn 0;\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_get_scale_type: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn 0;\n\t}\n\n\treturn encoder->gpu_scale_type;\n}\n\nuint32_t obs_encoder_get_frame_rate_divisor(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_set_frame_rate_divisor\"))\n\t\treturn 0;\n\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_set_frame_rate_divisor: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn 0;\n\t}\n\n\treturn encoder->frame_rate_divisor;\n}\n\nuint32_t obs_encoder_get_sample_rate(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_get_sample_rate\"))\n\t\treturn 0;\n\tif (encoder->info.type != OBS_ENCODER_AUDIO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_get_sample_rate: \"\n\t\t     \"encoder '%s' is not an audio encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn 0;\n\t}\n\tif (!encoder->media)\n\t\treturn 0;\n\n\treturn encoder->samplerate != 0 ? encoder->samplerate : audio_output_get_sample_rate(encoder->media);\n}\n\nsize_t obs_encoder_get_frame_size(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_get_frame_size\"))\n\t\treturn 0;\n\tif (encoder->info.type != OBS_ENCODER_AUDIO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_get_frame_size: \"\n\t\t     \"encoder '%s' is not an audio encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn 0;\n\t}\n\n\treturn encoder->framesize;\n}\n\nsize_t obs_encoder_get_mixer_index(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_get_mixer_index\"))\n\t\treturn 0;\n\n\tif (encoder->info.type != OBS_ENCODER_AUDIO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_get_mixer_index: \"\n\t\t     \"encoder '%s' is not an audio encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn 0;\n\t}\n\n\treturn encoder->mixer_idx;\n}\n\nvoid obs_encoder_set_video(obs_encoder_t *encoder, video_t *video)\n{\n\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_set_video\"))\n\t\treturn;\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_set_video: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\tif (encoder_active(encoder)) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Cannot apply a new video_t \"\n\t\t     \"object while the encoder is active\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\tif (encoder->initialized) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Cannot apply a new video_t object \"\n\t\t     \"after the encoder has been initialized\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\n\tencoder_set_video(encoder, video);\n}\n\nstatic void encoder_set_video(obs_encoder_t *encoder, video_t *video)\n{\n\tconst struct video_output_info *voi;\n\n\tif (encoder->fps_override) {\n\t\tvideo_output_free_frame_rate_divisor(encoder->fps_override);\n\t\tencoder->fps_override = NULL;\n\t}\n\n\tif (video) {\n\t\tvoi = video_output_get_info(video);\n\t\tencoder->media = video;\n\t\tencoder->timebase_num = voi->fps_den;\n\t\tencoder->timebase_den = voi->fps_num;\n\n\t\tif (encoder->frame_rate_divisor) {\n\t\t\tencoder->fps_override =\n\t\t\t\tvideo_output_create_with_frame_rate_divisor(video, encoder->frame_rate_divisor);\n\t\t}\n\t} else {\n\t\tencoder->media = NULL;\n\t\tencoder->timebase_num = 0;\n\t\tencoder->timebase_den = 0;\n\t}\n}\n\nvoid obs_encoder_set_audio(obs_encoder_t *encoder, audio_t *audio)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_set_audio\"))\n\t\treturn;\n\tif (encoder->info.type != OBS_ENCODER_AUDIO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_set_audio: \"\n\t\t     \"encoder '%s' is not an audio encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\tif (encoder_active(encoder)) {\n\t\tblog(LOG_WARNING,\n\t\t     \"encoder '%s': Cannot apply a new audio_t \"\n\t\t     \"object while the encoder is active\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn;\n\t}\n\n\tif (audio) {\n\t\tencoder->media = audio;\n\t\tencoder->timebase_num = 1;\n\t\tencoder->timebase_den = audio_output_get_sample_rate(audio);\n\t} else {\n\t\tencoder->media = NULL;\n\t\tencoder->timebase_num = 0;\n\t\tencoder->timebase_den = 0;\n\t}\n}\n\nvideo_t *obs_encoder_video(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_video\"))\n\t\treturn NULL;\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_set_video: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn NULL;\n\t}\n\n\treturn encoder->fps_override ? encoder->fps_override : encoder->media;\n}\n\nvideo_t *obs_encoder_parent_video(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_parent_video\"))\n\t\treturn NULL;\n\tif (encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_parent_video: \"\n\t\t     \"encoder '%s' is not a video encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn NULL;\n\t}\n\n\treturn encoder->media;\n}\n\naudio_t *obs_encoder_audio(const obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_audio\"))\n\t\treturn NULL;\n\tif (encoder->info.type != OBS_ENCODER_AUDIO) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_encoder_set_audio: \"\n\t\t     \"encoder '%s' is not an audio encoder\",\n\t\t     obs_encoder_get_name(encoder));\n\t\treturn NULL;\n\t}\n\n\treturn encoder->media;\n}\n\nbool obs_encoder_active(const obs_encoder_t *encoder)\n{\n\treturn obs_encoder_valid(encoder, \"obs_encoder_active\") ? encoder_active(encoder) : false;\n}\n\nstatic inline bool get_sei(const struct obs_encoder *encoder, uint8_t **sei, size_t *size)\n{\n\tif (encoder->info.get_sei_data)\n\t\treturn encoder->info.get_sei_data(encoder->context.data, sei, size);\n\treturn false;\n}\n\nstatic void send_first_video_packet(struct obs_encoder *encoder, struct encoder_callback *cb,\n\t\t\t\t    struct encoder_packet *packet, struct encoder_packet_time *packet_time)\n{\n\tstruct encoder_packet first_packet;\n\tDARRAY(uint8_t) data;\n\tuint8_t *sei;\n\tsize_t size;\n\n\t/* always wait for first keyframe */\n\tif (!packet->keyframe)\n\t\treturn;\n\n\tda_init(data);\n\n\tif (!get_sei(encoder, &sei, &size) || !sei || !size) {\n\t\tcb->new_packet(cb->param, packet, packet_time);\n\t\tcb->sent_first_packet = true;\n\t\treturn;\n\t}\n\n\tda_push_back_array(data, sei, size);\n\tda_push_back_array(data, packet->data, packet->size);\n\n\tfirst_packet = *packet;\n\tfirst_packet.data = data.array;\n\tfirst_packet.size = data.num;\n\n\tcb->new_packet(cb->param, &first_packet, packet_time);\n\tcb->sent_first_packet = true;\n\n\tda_free(data);\n}\n\nstatic const char *send_packet_name = \"send_packet\";\nstatic inline void send_packet(struct obs_encoder *encoder, struct encoder_callback *cb, struct encoder_packet *packet,\n\t\t\t       struct encoder_packet_time *packet_time)\n{\n\tprofile_start(send_packet_name);\n\t/* include SEI in first video packet */\n\tif (encoder->info.type == OBS_ENCODER_VIDEO && !cb->sent_first_packet)\n\t\tsend_first_video_packet(encoder, cb, packet, packet_time);\n\telse\n\t\tcb->new_packet(cb->param, packet, packet_time);\n\tprofile_end(send_packet_name);\n}\n\nvoid full_stop(struct obs_encoder *encoder)\n{\n\tif (encoder) {\n\t\tpthread_mutex_lock(&encoder->outputs_mutex);\n\t\tfor (size_t i = 0; i < encoder->outputs.num; i++) {\n\t\t\tstruct obs_output *output = encoder->outputs.array[i];\n\t\t\tobs_output_force_stop(output);\n\n\t\t\tpthread_mutex_lock(&output->interleaved_mutex);\n\t\t\toutput->info.encoded_packet(output->context.data, NULL);\n\t\t\tpthread_mutex_unlock(&output->interleaved_mutex);\n\t\t}\n\t\tpthread_mutex_unlock(&encoder->outputs_mutex);\n\n\t\tpthread_mutex_lock(&encoder->callbacks_mutex);\n\t\tda_free(encoder->callbacks);\n\t\tpthread_mutex_unlock(&encoder->callbacks_mutex);\n\n\t\tremove_connection(encoder, false);\n\t}\n}\n\nvoid send_off_encoder_packet(obs_encoder_t *encoder, bool success, bool received, struct encoder_packet *pkt)\n{\n\tif (!success) {\n\t\tblog(LOG_ERROR, \"Error encoding with encoder '%s'\", encoder->context.name);\n\t\tfull_stop(encoder);\n\t\treturn;\n\t}\n\n\tif (received) {\n\t\tif (!encoder->first_received) {\n\t\t\tencoder->offset_usec = packet_dts_usec(pkt);\n\t\t\tencoder->first_received = true;\n\t\t}\n\n\t\t/* we use system time here to ensure sync with other encoders,\n\t\t * you do not want to use relative timestamps here */\n\t\tpkt->dts_usec = encoder->start_ts / 1000 + packet_dts_usec(pkt) - encoder->offset_usec;\n\t\tpkt->sys_dts_usec = pkt->dts_usec;\n\n\t\tpthread_mutex_lock(&encoder->pause.mutex);\n\t\tpkt->sys_dts_usec += encoder->pause.ts_offset / 1000;\n\t\tpthread_mutex_unlock(&encoder->pause.mutex);\n\n\t\t/* Find the encoder packet timing entry in the encoder\n\t\t * timing array with the corresponding PTS value, then remove\n\t\t * the entry from the array to ensure it doesn't continuously fill.\n\t\t */\n\t\tstruct encoder_packet_time ept_local;\n\t\tstruct encoder_packet_time *ept = NULL;\n\t\tbool found_ept = false;\n\t\tif (pkt->type == OBS_ENCODER_VIDEO) {\n\t\t\tfor (size_t i = encoder->encoder_packet_times.num; i > 0; i--) {\n\t\t\t\tept = &encoder->encoder_packet_times.array[i - 1];\n\t\t\t\tif (ept->pts == pkt->pts) {\n\t\t\t\t\tept_local = *ept;\n\t\t\t\t\tda_erase(encoder->encoder_packet_times, i - 1);\n\t\t\t\t\tfound_ept = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!found_ept)\n\t\t\t\tblog(LOG_DEBUG, \"%s: Encoder packet timing for PTS %\" PRId64 \" not found\", __FUNCTION__,\n\t\t\t\t     pkt->pts);\n\t\t}\n\n\t\tpthread_mutex_lock(&encoder->callbacks_mutex);\n\n\t\tfor (size_t i = encoder->callbacks.num; i > 0; i--) {\n\t\t\tstruct encoder_callback *cb;\n\t\t\tcb = encoder->callbacks.array + (i - 1);\n\t\t\tsend_packet(encoder, cb, pkt, found_ept ? &ept_local : NULL);\n\t\t}\n\n\t\tpthread_mutex_unlock(&encoder->callbacks_mutex);\n\n\t\t// Count number of video frames successfully encoded\n\t\tif (pkt->type == OBS_ENCODER_VIDEO)\n\t\t\tencoder->encoded_frames++;\n\t}\n}\n\nstatic const char *do_encode_name = \"do_encode\";\nbool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame, const uint64_t *frame_cts)\n{\n\tprofile_start(do_encode_name);\n\tif (!encoder->profile_encoder_encode_name)\n\t\tencoder->profile_encoder_encode_name =\n\t\t\tprofile_store_name(obs_get_profiler_name_store(), \"encode(%s)\", encoder->context.name);\n\n\tstruct encoder_packet pkt = {0};\n\tbool received = false;\n\tbool success;\n\tuint64_t fer_ts = 0;\n\n\tif (encoder->reconfigure_requested) {\n\t\tencoder->reconfigure_requested = false;\n\t\tencoder->info.update(encoder->context.data, encoder->context.settings);\n\t}\n\n\tpkt.timebase_num = encoder->timebase_num * encoder->frame_rate_divisor;\n\tpkt.timebase_den = encoder->timebase_den;\n\tpkt.encoder = encoder;\n\n\t/* Get the frame encode request timestamp. This\n\t * needs to be read just before the encode request.\n\t */\n\tfer_ts = os_gettime_ns();\n\n\tprofile_start(encoder->profile_encoder_encode_name);\n\tsuccess = encoder->info.encode(encoder->context.data, frame, &pkt, &received);\n\tprofile_end(encoder->profile_encoder_encode_name);\n\n\t/* Generate and enqueue the frame timing metrics, namely\n\t * the CTS (composition time), FER (frame encode request), FERC\n\t * (frame encode request complete) and current PTS. PTS is used to\n\t * associate the frame timing data with the encode packet. */\n\tif (frame_cts) {\n\t\tstruct encoder_packet_time *ept = da_push_back_new(encoder->encoder_packet_times);\n\t\t// Get the frame encode request complete timestamp\n\t\tif (success) {\n\t\t\tept->ferc = os_gettime_ns();\n\t\t} else {\n\t\t\t// Encode had error, set ferc to 0\n\t\t\tept->ferc = 0;\n\t\t}\n\t\tept->pts = frame->pts;\n\t\tept->cts = *frame_cts;\n\t\tept->fer = fer_ts;\n\t}\n\tsend_off_encoder_packet(encoder, success, received, &pkt);\n\n\tprofile_end(do_encode_name);\n\n\treturn success;\n}\n\nstatic inline bool video_pause_check_internal(struct pause_data *pause, uint64_t ts)\n{\n\tpause->last_video_ts = ts;\n\tif (!pause->ts_start) {\n\t\treturn false;\n\t}\n\n\tif (ts == pause->ts_end) {\n\t\tpause->ts_start = 0;\n\t\tpause->ts_end = 0;\n\n\t} else if (ts >= pause->ts_start) {\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool video_pause_check(struct pause_data *pause, uint64_t timestamp)\n{\n\tbool ignore_frame;\n\n\tpthread_mutex_lock(&pause->mutex);\n\tignore_frame = video_pause_check_internal(pause, timestamp);\n\tpthread_mutex_unlock(&pause->mutex);\n\n\treturn ignore_frame;\n}\n\nstatic const char *receive_video_name = \"receive_video\";\nstatic void receive_video(void *param, struct video_data *frame)\n{\n\tprofile_start(receive_video_name);\n\n\tstruct obs_encoder *encoder = param;\n\tstruct encoder_frame enc_frame;\n\n\tif (encoder->encoder_group && !encoder->start_ts) {\n\t\tstruct obs_encoder_group *group = encoder->encoder_group;\n\t\tbool ready = false;\n\t\tpthread_mutex_lock(&group->mutex);\n\t\tready = group->start_timestamp == frame->timestamp;\n\t\tpthread_mutex_unlock(&group->mutex);\n\t\tif (!ready)\n\t\t\tgoto wait_for_audio;\n\t}\n\n\tif (!encoder->first_received && encoder->paired_encoders.num) {\n\t\tfor (size_t i = 0; i < encoder->paired_encoders.num; i++) {\n\t\t\tobs_encoder_t *paired = obs_weak_encoder_get_encoder(encoder->paired_encoders.array[i]);\n\t\t\tif (!paired)\n\t\t\t\tcontinue;\n\n\t\t\tif (!paired->first_received || paired->first_raw_ts > frame->timestamp) {\n\t\t\t\tobs_encoder_release(paired);\n\t\t\t\tgoto wait_for_audio;\n\t\t\t}\n\n\t\t\tobs_encoder_release(paired);\n\t\t}\n\t}\n\n\tif (video_pause_check(&encoder->pause, frame->timestamp))\n\t\tgoto wait_for_audio;\n\n\tmemset(&enc_frame, 0, sizeof(struct encoder_frame));\n\n\tfor (size_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\tenc_frame.data[i] = frame->data[i];\n\t\tenc_frame.linesize[i] = frame->linesize[i];\n\t}\n\n\tif (!encoder->start_ts)\n\t\tencoder->start_ts = frame->timestamp;\n\n\tenc_frame.frames = 1;\n\tenc_frame.pts = encoder->cur_pts;\n\n\tif (do_encode(encoder, &enc_frame, &frame->timestamp))\n\t\tencoder->cur_pts += encoder->timebase_num * encoder->frame_rate_divisor;\n\nwait_for_audio:\n\tprofile_end(receive_video_name);\n}\n\nstatic void clear_audio(struct obs_encoder *encoder)\n{\n\tfor (size_t i = 0; i < encoder->planes; i++)\n\t\tdeque_free(&encoder->audio_input_buffer[i]);\n}\n\nstatic inline void push_back_audio(struct obs_encoder *encoder, struct audio_data *data, size_t size,\n\t\t\t\t   size_t offset_size)\n{\n\tif (offset_size >= size)\n\t\treturn;\n\n\tsize -= offset_size;\n\n\t/* push in to the circular buffer */\n\tfor (size_t i = 0; i < encoder->planes; i++)\n\t\tdeque_push_back(&encoder->audio_input_buffer[i], data->data[i] + offset_size, size);\n}\n\nstatic inline size_t calc_offset_size(struct obs_encoder *encoder, uint64_t v_start_ts, uint64_t a_start_ts)\n{\n\tuint64_t offset = v_start_ts - a_start_ts;\n\toffset = util_mul_div64(offset, encoder->samplerate, 1000000000ULL);\n\treturn (size_t)offset * encoder->blocksize;\n}\n\nstatic void start_from_buffer(struct obs_encoder *encoder, uint64_t v_start_ts)\n{\n\tsize_t size = encoder->audio_input_buffer[0].size;\n\tstruct audio_data audio = {0};\n\tsize_t offset_size = 0;\n\n\tfor (size_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\taudio.data[i] = encoder->audio_input_buffer[i].data;\n\t\tmemset(&encoder->audio_input_buffer[i], 0, sizeof(struct deque));\n\t}\n\n\tif (encoder->first_raw_ts < v_start_ts)\n\t\toffset_size = calc_offset_size(encoder, v_start_ts, encoder->first_raw_ts);\n\n\tpush_back_audio(encoder, &audio, size, offset_size);\n\n\tfor (size_t i = 0; i < MAX_AV_PLANES; i++)\n\t\tbfree(audio.data[i]);\n}\n\nstatic const char *buffer_audio_name = \"buffer_audio\";\nstatic bool buffer_audio(struct obs_encoder *encoder, struct audio_data *data)\n{\n\tprofile_start(buffer_audio_name);\n\n\tsize_t size = data->frames * encoder->blocksize;\n\tsize_t offset_size = 0;\n\tbool success = true;\n\n\tstruct obs_encoder *paired_encoder = NULL;\n\t/* Audio encoders can only be paired to one video encoder */\n\tif (encoder->paired_encoders.num) {\n\t\tpaired_encoder = obs_weak_encoder_get_encoder(encoder->paired_encoders.array[0]);\n\t}\n\n\tif (!encoder->start_ts && paired_encoder) {\n\t\tuint64_t end_ts = data->timestamp;\n\t\tuint64_t v_start_ts = paired_encoder->start_ts;\n\n\t\t/* no video yet, so don't start audio */\n\t\tif (!v_start_ts) {\n\t\t\tsuccess = false;\n\t\t\tgoto fail;\n\t\t}\n\n\t\t/* audio starting point still not synced with video starting\n\t\t * point, so don't start audio */\n\t\tend_ts += util_mul_div64(data->frames, 1000000000ULL, encoder->samplerate);\n\t\tif (end_ts <= v_start_ts) {\n\t\t\tsuccess = false;\n\t\t\tgoto fail;\n\t\t}\n\n\t\t/* ready to start audio, truncate if necessary */\n\t\tif (data->timestamp < v_start_ts)\n\t\t\toffset_size = calc_offset_size(encoder, v_start_ts, data->timestamp);\n\t\tif (data->timestamp <= v_start_ts)\n\t\t\tclear_audio(encoder);\n\n\t\tencoder->start_ts = v_start_ts;\n\n\t\t/* use currently buffered audio instead */\n\t\tif (v_start_ts < data->timestamp) {\n\t\t\tstart_from_buffer(encoder, v_start_ts);\n\t\t}\n\n\t} else if (!encoder->start_ts && !paired_encoder) {\n\t\tencoder->start_ts = data->timestamp;\n\t}\n\nfail:\n\tpush_back_audio(encoder, data, size, offset_size);\n\tobs_encoder_release(paired_encoder);\n\n\tprofile_end(buffer_audio_name);\n\treturn success;\n}\n\nstatic bool send_audio_data(struct obs_encoder *encoder)\n{\n\tstruct encoder_frame enc_frame;\n\n\tmemset(&enc_frame, 0, sizeof(struct encoder_frame));\n\n\tfor (size_t i = 0; i < encoder->planes; i++) {\n\t\tdeque_pop_front(&encoder->audio_input_buffer[i], encoder->audio_output_buffer[i],\n\t\t\t\tencoder->framesize_bytes);\n\n\t\tenc_frame.data[i] = encoder->audio_output_buffer[i];\n\t\tenc_frame.linesize[i] = (uint32_t)encoder->framesize_bytes;\n\t}\n\n\tenc_frame.frames = (uint32_t)encoder->framesize;\n\tenc_frame.pts = encoder->cur_pts;\n\n\tif (!do_encode(encoder, &enc_frame, NULL))\n\t\treturn false;\n\n\tencoder->cur_pts += encoder->framesize;\n\treturn true;\n}\n\nstatic void pause_audio(struct pause_data *pause, struct audio_data *data, size_t sample_rate)\n{\n\tuint64_t cutoff_frames = pause->ts_start - data->timestamp;\n\tcutoff_frames = ns_to_audio_frames(sample_rate, cutoff_frames);\n\n\tdata->frames = (uint32_t)cutoff_frames;\n}\n\nstatic void unpause_audio(struct pause_data *pause, struct audio_data *data, size_t sample_rate)\n{\n\tuint64_t cutoff_frames = pause->ts_end - data->timestamp;\n\tcutoff_frames = ns_to_audio_frames(sample_rate, cutoff_frames);\n\n\tfor (size_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\tif (!data->data[i])\n\t\t\tbreak;\n\t\tdata->data[i] += cutoff_frames * sizeof(float);\n\t}\n\n\tdata->timestamp = pause->ts_start;\n\tdata->frames = data->frames - (uint32_t)cutoff_frames;\n\tpause->ts_start = 0;\n\tpause->ts_end = 0;\n}\n\nstatic inline bool audio_pause_check_internal(struct pause_data *pause, struct audio_data *data, size_t sample_rate)\n{\n\tuint64_t end_ts;\n\n\tif (!pause->ts_start) {\n\t\treturn false;\n\t}\n\n\tend_ts = data->timestamp + audio_frames_to_ns(sample_rate, data->frames);\n\n\tif (pause->ts_start >= data->timestamp) {\n\t\tif (pause->ts_start <= end_ts) {\n\t\t\tpause_audio(pause, data, sample_rate);\n\t\t\treturn !data->frames;\n\t\t}\n\n\t} else {\n\t\tif (pause->ts_end >= data->timestamp && pause->ts_end <= end_ts) {\n\t\t\tunpause_audio(pause, data, sample_rate);\n\t\t\treturn !data->frames;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool audio_pause_check(struct pause_data *pause, struct audio_data *data, size_t sample_rate)\n{\n\tbool ignore_audio;\n\n\tpthread_mutex_lock(&pause->mutex);\n\tignore_audio = audio_pause_check_internal(pause, data, sample_rate);\n\tdata->timestamp -= pause->ts_offset;\n\tpthread_mutex_unlock(&pause->mutex);\n\n\treturn ignore_audio;\n}\n\nstatic const char *receive_audio_name = \"receive_audio\";\nstatic void receive_audio(void *param, size_t mix_idx, struct audio_data *in)\n{\n\tprofile_start(receive_audio_name);\n\n\tstruct obs_encoder *encoder = param;\n\tstruct audio_data audio = *in;\n\n\tif (!encoder->first_received) {\n\t\tencoder->first_raw_ts = audio.timestamp;\n\t\tencoder->first_received = true;\n\t\tclear_audio(encoder);\n\t}\n\n\tif (audio_pause_check(&encoder->pause, &audio, encoder->samplerate))\n\t\tgoto end;\n\n\tif (!buffer_audio(encoder, &audio))\n\t\tgoto end;\n\n\twhile (encoder->audio_input_buffer[0].size >= encoder->framesize_bytes) {\n\t\tif (!send_audio_data(encoder)) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tUNUSED_PARAMETER(mix_idx);\n\nend:\n\tprofile_end(receive_audio_name);\n}\n\nvoid obs_encoder_add_output(struct obs_encoder *encoder, struct obs_output *output)\n{\n\tif (!encoder || !output)\n\t\treturn;\n\n\tpthread_mutex_lock(&encoder->outputs_mutex);\n\tda_push_back(encoder->outputs, &output);\n\tpthread_mutex_unlock(&encoder->outputs_mutex);\n}\n\nvoid obs_encoder_remove_output(struct obs_encoder *encoder, struct obs_output *output)\n{\n\tif (!encoder || !output)\n\t\treturn;\n\n\tpthread_mutex_lock(&encoder->outputs_mutex);\n\tda_erase_item(encoder->outputs, &output);\n\tpthread_mutex_unlock(&encoder->outputs_mutex);\n}\n\nvoid obs_encoder_packet_create_instance(struct encoder_packet *dst, const struct encoder_packet *src)\n{\n\tlong *p_refs;\n\n\t*dst = *src;\n\tp_refs = bmalloc(src->size + sizeof(long));\n\tdst->data = (void *)(p_refs + 1);\n\t*p_refs = 1;\n\tmemcpy(dst->data, src->data, src->size);\n}\n\nvoid obs_encoder_packet_ref(struct encoder_packet *dst, struct encoder_packet *src)\n{\n\tif (!src)\n\t\treturn;\n\n\tif (src->data) {\n\t\tlong *p_refs = ((long *)src->data) - 1;\n\t\tos_atomic_inc_long(p_refs);\n\t}\n\n\t*dst = *src;\n}\n\nvoid obs_encoder_packet_release(struct encoder_packet *pkt)\n{\n\tif (!pkt)\n\t\treturn;\n\n\tif (pkt->data) {\n\t\tlong *p_refs = ((long *)pkt->data) - 1;\n\t\tif (os_atomic_dec_long(p_refs) == 0)\n\t\t\tbfree(p_refs);\n\t}\n\n\tmemset(pkt, 0, sizeof(struct encoder_packet));\n}\n\nvoid obs_encoder_set_preferred_video_format(obs_encoder_t *encoder, enum video_format format)\n{\n\tif (!encoder || encoder->info.type != OBS_ENCODER_VIDEO)\n\t\treturn;\n\n\tencoder->preferred_format = format;\n}\n\nenum video_format obs_encoder_get_preferred_video_format(const obs_encoder_t *encoder)\n{\n\tif (!encoder || encoder->info.type != OBS_ENCODER_VIDEO)\n\t\treturn VIDEO_FORMAT_NONE;\n\n\treturn encoder->preferred_format;\n}\n\nvoid obs_encoder_set_preferred_color_space(obs_encoder_t *encoder, enum video_colorspace colorspace)\n{\n\tif (!encoder || encoder->info.type != OBS_ENCODER_VIDEO)\n\t\treturn;\n\n\tencoder->preferred_space = colorspace;\n}\n\nenum video_colorspace obs_encoder_get_preferred_color_space(const obs_encoder_t *encoder)\n{\n\tif (!encoder || encoder->info.type != OBS_ENCODER_VIDEO)\n\t\treturn VIDEO_CS_DEFAULT;\n\n\treturn encoder->preferred_space;\n}\n\nvoid obs_encoder_set_preferred_range(obs_encoder_t *encoder, enum video_range_type range)\n{\n\tif (!encoder || encoder->info.type != OBS_ENCODER_VIDEO)\n\t\treturn;\n\n\tencoder->preferred_range = range;\n}\n\nenum video_range_type obs_encoder_get_preferred_range(const obs_encoder_t *encoder)\n{\n\tif (!encoder || encoder->info.type != OBS_ENCODER_VIDEO)\n\t\treturn VIDEO_RANGE_DEFAULT;\n\n\treturn encoder->preferred_range;\n}\n\nvoid obs_encoder_release(obs_encoder_t *encoder)\n{\n\tif (!encoder)\n\t\treturn;\n\n\tobs_weak_encoder_t *control = get_weak(encoder);\n\tif (obs_ref_release(&control->ref)) {\n\t\t// The order of operations is important here since\n\t\t// get_context_by_name in obs.c relies on weak refs\n\t\t// being alive while the context is listed\n\t\tobs_encoder_destroy(encoder);\n\t\tobs_weak_encoder_release(control);\n\t}\n}\n\nvoid obs_weak_encoder_addref(obs_weak_encoder_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tobs_weak_ref_addref(&weak->ref);\n}\n\nvoid obs_weak_encoder_release(obs_weak_encoder_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tif (obs_weak_ref_release(&weak->ref))\n\t\tbfree(weak);\n}\n\nobs_encoder_t *obs_encoder_get_ref(obs_encoder_t *encoder)\n{\n\tif (!encoder)\n\t\treturn NULL;\n\n\treturn obs_weak_encoder_get_encoder(get_weak(encoder));\n}\n\nobs_weak_encoder_t *obs_encoder_get_weak_encoder(obs_encoder_t *encoder)\n{\n\tif (!encoder)\n\t\treturn NULL;\n\n\tobs_weak_encoder_t *weak = get_weak(encoder);\n\tobs_weak_encoder_addref(weak);\n\treturn weak;\n}\n\nobs_encoder_t *obs_weak_encoder_get_encoder(obs_weak_encoder_t *weak)\n{\n\tif (!weak)\n\t\treturn NULL;\n\n\tif (obs_weak_ref_get_ref(&weak->ref))\n\t\treturn weak->encoder;\n\n\treturn NULL;\n}\n\nbool obs_weak_encoder_references_encoder(obs_weak_encoder_t *weak, obs_encoder_t *encoder)\n{\n\treturn weak && encoder && weak->encoder == encoder;\n}\n\nvoid *obs_encoder_get_type_data(obs_encoder_t *encoder)\n{\n\treturn obs_encoder_valid(encoder, \"obs_encoder_get_type_data\") ? encoder->orig_info.type_data : NULL;\n}\n\nconst char *obs_encoder_get_id(const obs_encoder_t *encoder)\n{\n\treturn obs_encoder_valid(encoder, \"obs_encoder_get_id\") ? encoder->orig_info.id : NULL;\n}\n\nuint32_t obs_get_encoder_caps(const char *encoder_id)\n{\n\tstruct obs_encoder_info *info = find_encoder(encoder_id);\n\treturn info ? info->caps : 0;\n}\n\nuint32_t obs_encoder_get_caps(const obs_encoder_t *encoder)\n{\n\treturn obs_encoder_valid(encoder, \"obs_encoder_get_caps\") ? encoder->orig_info.caps : 0;\n}\n\nbool obs_encoder_paused(const obs_encoder_t *encoder)\n{\n\treturn obs_encoder_valid(encoder, \"obs_encoder_paused\") ? os_atomic_load_bool(&encoder->paused) : false;\n}\n\nconst char *obs_encoder_get_last_error(obs_encoder_t *encoder)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_get_last_error\"))\n\t\treturn NULL;\n\n\treturn encoder->last_error_message;\n}\n\nvoid obs_encoder_set_last_error(obs_encoder_t *encoder, const char *message)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_set_last_error\"))\n\t\treturn;\n\n\tif (encoder->last_error_message)\n\t\tbfree(encoder->last_error_message);\n\n\tif (message)\n\t\tencoder->last_error_message = bstrdup(message);\n\telse\n\t\tencoder->last_error_message = NULL;\n}\n\nuint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder)\n{\n\treturn encoder ? encoder->pause.ts_offset : 0;\n}\n\nbool obs_encoder_has_roi(const obs_encoder_t *encoder)\n{\n\treturn encoder->roi.num > 0;\n}\n\nbool obs_encoder_add_roi(obs_encoder_t *encoder, const struct obs_encoder_roi *roi)\n{\n\tif (!roi)\n\t\treturn false;\n\tif (!(encoder->info.caps & OBS_ENCODER_CAP_ROI))\n\t\treturn false;\n\t/* Area smaller than the smallest possible block (16x16) */\n\tif (roi->bottom - roi->top < 16 || roi->right - roi->left < 16)\n\t\treturn false;\n\t/* Other invalid ROIs */\n\tif (roi->priority < -1.0f || roi->priority > 1.0f)\n\t\treturn false;\n\n\tpthread_mutex_lock(&encoder->roi_mutex);\n\tda_push_back(encoder->roi, roi);\n\tencoder->roi_increment++;\n\tpthread_mutex_unlock(&encoder->roi_mutex);\n\n\treturn true;\n}\n\nvoid obs_encoder_clear_roi(obs_encoder_t *encoder)\n{\n\tif (!encoder->roi.num)\n\t\treturn;\n\tpthread_mutex_lock(&encoder->roi_mutex);\n\tda_clear(encoder->roi);\n\tencoder->roi_increment++;\n\tpthread_mutex_unlock(&encoder->roi_mutex);\n}\n\nvoid obs_encoder_enum_roi(obs_encoder_t *encoder, void (*enum_proc)(void *, struct obs_encoder_roi *), void *param)\n{\n\tfloat scale_x = 0;\n\tfloat scale_y = 0;\n\n\t/* Scale ROI passed to callback to output size */\n\tif (encoder->scaled_height && encoder->scaled_width) {\n\t\tconst uint32_t width = video_output_get_width(encoder->media);\n\t\tconst uint32_t height = video_output_get_height(encoder->media);\n\n\t\tif (!width || !height)\n\t\t\treturn;\n\n\t\tscale_x = (float)encoder->scaled_width / (float)width;\n\t\tscale_y = (float)encoder->scaled_height / (float)height;\n\t}\n\n\tpthread_mutex_lock(&encoder->roi_mutex);\n\n\tsize_t idx = encoder->roi.num;\n\twhile (idx) {\n\t\tstruct obs_encoder_roi *roi = &encoder->roi.array[--idx];\n\n\t\tif (scale_x > 0 && scale_y > 0) {\n\t\t\tstruct obs_encoder_roi scaled_roi = {\n\t\t\t\t.top = (uint32_t)((float)roi->top * scale_y),\n\t\t\t\t.bottom = (uint32_t)((float)roi->bottom * scale_y),\n\t\t\t\t.left = (uint32_t)((float)roi->left * scale_x),\n\t\t\t\t.right = (uint32_t)((float)roi->right * scale_x),\n\t\t\t\t.priority = roi->priority,\n\t\t\t};\n\n\t\t\tenum_proc(param, &scaled_roi);\n\t\t} else {\n\t\t\tenum_proc(param, roi);\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&encoder->roi_mutex);\n}\n\nuint32_t obs_encoder_get_roi_increment(const obs_encoder_t *encoder)\n{\n\treturn encoder->roi_increment;\n}\n\nbool obs_encoder_set_group(obs_encoder_t *encoder, obs_encoder_group_t *group)\n{\n\tif (!obs_encoder_valid(encoder, \"obs_encoder_set_group\"))\n\t\treturn false;\n\n\tif (obs_encoder_active(encoder)) {\n\t\tblog(LOG_ERROR, \"obs_encoder_set_group: encoder '%s' is already active\", obs_encoder_get_name(encoder));\n\t\treturn false;\n\t}\n\n\tif (encoder->encoder_group) {\n\t\tstruct obs_encoder_group *old_group = encoder->encoder_group;\n\t\tpthread_mutex_lock(&old_group->mutex);\n\t\tif (old_group->num_encoders_started) {\n\t\t\tpthread_mutex_unlock(&old_group->mutex);\n\t\t\tblog(LOG_ERROR, \"obs_encoder_set_group: encoder '%s' existing group has started encoders\",\n\t\t\t     obs_encoder_get_name(encoder));\n\t\t\treturn false;\n\t\t}\n\t\tda_erase_item(old_group->encoders, &encoder);\n\t\tobs_encoder_release(encoder);\n\t\tpthread_mutex_unlock(&old_group->mutex);\n\t}\n\n\tif (!group)\n\t\treturn true;\n\n\tpthread_mutex_lock(&group->mutex);\n\n\tif (group->num_encoders_started) {\n\t\tpthread_mutex_unlock(&group->mutex);\n\t\tblog(LOG_ERROR, \"obs_encoder_set_group: specified group has started encoders\");\n\t\treturn false;\n\t}\n\n\tobs_encoder_t *ref = obs_encoder_get_ref(encoder);\n\tif (!ref) {\n\t\tpthread_mutex_unlock(&group->mutex);\n\t\treturn false;\n\t}\n\tda_push_back(group->encoders, &ref);\n\tencoder->encoder_group = group;\n\n\tpthread_mutex_unlock(&group->mutex);\n\n\treturn true;\n}\n\nobs_encoder_group_t *obs_encoder_group_create()\n{\n\tstruct obs_encoder_group *group = bzalloc(sizeof(struct obs_encoder_group));\n\n\tpthread_mutex_init_value(&group->mutex);\n\tif (pthread_mutex_init(&group->mutex, NULL) != 0) {\n\t\tbfree(group);\n\t\treturn NULL;\n\t}\n\n\treturn group;\n}\n\nvoid obs_encoder_group_actually_destroy(obs_encoder_group_t *group)\n{\n\tfor (size_t i = 0; i < group->encoders.num; i++) {\n\t\tstruct obs_encoder *encoder = group->encoders.array[i];\n\t\tencoder->encoder_group = NULL;\n\t\tobs_encoder_release(encoder);\n\t}\n\n\tda_free(group->encoders);\n\tpthread_mutex_unlock(&group->mutex);\n\tpthread_mutex_destroy(&group->mutex);\n\n\tbfree(group);\n}\n\nvoid obs_encoder_group_destroy(obs_encoder_group_t *group)\n{\n\tif (!group)\n\t\treturn;\n\n\tpthread_mutex_lock(&group->mutex);\n\n\tif (group->num_encoders_started) {\n\t\tgroup->destroy_on_stop = true;\n\t\tpthread_mutex_unlock(&group->mutex);\n\t\treturn;\n\t}\n\n\tobs_encoder_group_actually_destroy(group);\n}\n\nbool obs_encoder_video_tex_active(const obs_encoder_t *encoder, enum video_format format)\n{\n\tstruct obs_core_video_mix *mix = get_mix_for_video(encoder->media);\n\n\tif (format == VIDEO_FORMAT_NV12)\n\t\treturn mix->using_nv12_tex;\n\tif (format == VIDEO_FORMAT_P010)\n\t\treturn mix->using_p010_tex;\n\n\treturn false;\n}\n\nuint32_t obs_encoder_get_priming_samples(const obs_encoder_t *encoder)\n{\n\tif (encoder->info.get_priming_samples) {\n\t\treturn encoder->info.get_priming_samples(encoder->context.data);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "libobs/obs-encoder.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n/**\n * @file\n * @brief header for modules implementing encoders.\n *\n * Encoders are modules that implement some codec that can be used by libobs\n * to process output data.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct obs_encoder;\ntypedef struct obs_encoder obs_encoder_t;\n\n#define OBS_ENCODER_CAP_DEPRECATED (1 << 0)\n#define OBS_ENCODER_CAP_PASS_TEXTURE (1 << 1)\n#define OBS_ENCODER_CAP_DYN_BITRATE (1 << 2)\n#define OBS_ENCODER_CAP_INTERNAL (1 << 3)\n#define OBS_ENCODER_CAP_ROI (1 << 4)\n#define OBS_ENCODER_CAP_SCALING (1 << 5)\n\n/** Specifies the encoder type */\nenum obs_encoder_type {\n\tOBS_ENCODER_AUDIO, /**< The encoder provides an audio codec */\n\tOBS_ENCODER_VIDEO  /**< The encoder provides a video codec */\n};\n\n/* encoder_packet_time is used for timestamping events associated\n * with each video frame. This is useful for deriving absolute\n * timestamps (i.e. wall-clock based formats) and measuring latency.\n *\n * For each frame, there are four events of interest, described in\n * the encoder_packet_time struct, namely cts, fer, ferc, and pir.\n * The timebase of these four events is os_gettime_ns(), which provides\n * very high resolution timestamping, and the ability to convert the\n * timing to any other time format.\n *\n * Each frame follows a timeline in the following temporal order:\n *   CTS, FER, FERC, PIR\n *\n * PTS is the integer-based monotonically increasing value that is used\n * to associate an encoder_packet_time entry with a specific encoder_packet.\n */\nstruct encoder_packet_time {\n\t/* PTS used to associate uncompressed frames with encoded packets. */\n\tint64_t pts;\n\n\t/* Composition timestamp is when the frame was rendered,\n\t * captured via os_gettime_ns().\n\t */\n\tuint64_t cts;\n\n\t/* FERC (Frame Encode Request) is when the frame was\n\t * submitted to the encoder for encoding via the encode\n\t * callback (e.g. encode_texture2()), captured via os_gettime_ns().\n\t */\n\tuint64_t fer;\n\n\t/* FERC (Frame Encode Request Complete) is when\n\t * the associated FER event completed. If the encode\n\t * is synchronous with the call, this means FERC - FEC\n\t * measures the actual encode time, otherwise if the\n\t * encode is asynchronous, it measures the pipeline\n\t * delay between encode request and encode complete.\n\t * FERC is also captured via os_gettime_ns().\n\t */\n\tuint64_t ferc;\n\n\t/* PIR (Packet Interleave Request) is when the encoded packet\n\t * is interleaved with the stream. PIR is captured via\n\t * os_gettime_ns(). The difference between PIR and CTS gives\n\t * the total latency between frame rendering\n\t * and packet interleaving.\n\t */\n\tuint64_t pir;\n};\n\n/** Encoder output packet */\nstruct encoder_packet {\n\tuint8_t *data; /**< Packet data */\n\tsize_t size;   /**< Packet size */\n\n\tint64_t pts; /**< Presentation timestamp */\n\tint64_t dts; /**< Decode timestamp */\n\n\tint32_t timebase_num; /**< Timebase numerator */\n\tint32_t timebase_den; /**< Timebase denominator */\n\n\tenum obs_encoder_type type; /**< Encoder type */\n\n\tbool keyframe; /**< Is a keyframe */\n\n\t/* ---------------------------------------------------------------- */\n\t/* Internal video variables (will be parsed automatically) */\n\n\t/* DTS in microseconds */\n\tint64_t dts_usec;\n\n\t/* System DTS in microseconds */\n\tint64_t sys_dts_usec;\n\n\t/**\n\t * Packet priority\n\t *\n\t * This is generally use by video encoders to specify the priority\n\t * of the packet.\n\t */\n\tint priority;\n\n\t/**\n\t * Dropped packet priority\n\t *\n\t * If this packet needs to be dropped, the next packet must be of this\n\t * priority or higher to continue transmission.\n\t */\n\tint drop_priority;\n\n\t/** Audio track index (used with outputs) */\n\tsize_t track_idx;\n\n\t/** Encoder from which the track originated from */\n\tobs_encoder_t *encoder;\n};\n\n/** Encoder input frame */\nstruct encoder_frame {\n\t/** Data for the frame/audio */\n\tuint8_t *data[MAX_AV_PLANES];\n\n\t/** size of each plane */\n\tuint32_t linesize[MAX_AV_PLANES];\n\n\t/** Number of frames (audio only) */\n\tuint32_t frames;\n\n\t/** Presentation timestamp */\n\tint64_t pts;\n};\n\n/** Encoder region of interest */\nstruct obs_encoder_roi {\n\t/* The rectangle edges of the region are specified as number of pixels\n\t * from the input video's top and left edges (i.e. row/column 0). */\n\tuint32_t top;\n\tuint32_t bottom;\n\tuint32_t left;\n\tuint32_t right;\n\n\t/* Priority is specified as a float value between -1 and 1.\n\t * These are converted to encoder-specific values by the encoder.\n\t * Values above 0 tell the encoder to increase quality for that region,\n\t * values below tell it to worsen it.\n\t * Not all encoders support negative values and they may be ignored. */\n\tfloat priority;\n};\n\nstruct gs_texture;\n\n/** Encoder input texture */\nstruct encoder_texture {\n\t/** Shared texture handle, only set on Windows */\n\tuint32_t handle;\n\t/** Textures, length determined by format */\n\tstruct gs_texture *tex[4];\n};\n\n/**\n * Encoder interface\n *\n * Encoders have a limited usage with OBS.  You are not generally supposed to\n * implement every encoder out there.  Generally, these are limited or specific\n * encoders for h264/aac for streaming and recording.  It doesn't have to be\n * *just* h264 or aac of course, but generally those are the expected encoders.\n *\n * That being said, other encoders will be kept in mind for future use.\n */\nstruct obs_encoder_info {\n\t/* ----------------------------------------------------------------- */\n\t/* Required implementation*/\n\n\t/** Specifies the named identifier of this encoder */\n\tconst char *id;\n\n\t/** Specifies the encoder type (video or audio) */\n\tenum obs_encoder_type type;\n\n\t/** Specifies the codec */\n\tconst char *codec;\n\n\t/**\n\t * Gets the full translated name of this encoder\n\t *\n\t * @param  type_data  The type_data variable of this structure\n\t * @return            Translated name of the encoder\n\t */\n\tconst char *(*get_name)(void *type_data);\n\n\t/**\n\t * Creates the encoder with the specified settings\n\t *\n\t * @param  settings  Settings for the encoder\n\t * @param  encoder   OBS encoder context\n\t * @return           Data associated with this encoder context, or\n\t *                   NULL if initialization failed.\n\t */\n\tvoid *(*create)(obs_data_t *settings, obs_encoder_t *encoder);\n\n\t/**\n\t * Destroys the encoder data\n\t *\n\t * @param  data  Data associated with this encoder context\n\t */\n\tvoid (*destroy)(void *data);\n\n\t/**\n\t * Encodes frame(s), and outputs encoded packets as they become\n\t * available.\n\t *\n\t * @param       data             Data associated with this encoder\n\t *                               context\n\t * @param[in]   frame            Raw audio/video data to encode\n\t * @param[out]  packet           Encoder packet output, if any\n\t * @param[out]  received_packet  Set to true if a packet was received,\n\t *                               false otherwise\n\t * @return                       true if successful, false otherwise.\n\t */\n\tbool (*encode)(void *data, struct encoder_frame *frame, struct encoder_packet *packet, bool *received_packet);\n\n\t/** Audio encoder only:  Returns the frame size for this encoder */\n\tsize_t (*get_frame_size)(void *data);\n\n\t/* ----------------------------------------------------------------- */\n\t/* Optional implementation */\n\n\t/**\n\t * Gets the default settings for this encoder\n\t *\n\t * @param[out]  settings  Data to assign default settings to\n\t */\n\tvoid (*get_defaults)(obs_data_t *settings);\n\n\t/**\n\t * Gets the property information of this encoder\n\t *\n\t * @return         The properties data\n\t */\n\tobs_properties_t *(*get_properties)(void *data);\n\n\t/**\n\t * Updates the settings for this encoder (usually used for things like\n\t * changing bitrate while active)\n\t *\n\t * @param  data      Data associated with this encoder context\n\t * @param  settings  New settings for this encoder\n\t * @return           true if successful, false otherwise\n\t */\n\tbool (*update)(void *data, obs_data_t *settings);\n\n\t/**\n\t * Returns extra data associated with this encoder (usually header)\n\t *\n\t * @param  data             Data associated with this encoder context\n\t * @param[out]  extra_data  Pointer to receive the extra data\n\t * @param[out]  size        Pointer to receive the size of the extra\n\t *                          data\n\t * @return                  true if extra data available, false\n\t *                          otherwise\n\t */\n\tbool (*get_extra_data)(void *data, uint8_t **extra_data, size_t *size);\n\n\t/**\n\t * Gets the SEI data, if any\n\t *\n\t * @param       data      Data associated with this encoder context\n\t * @param[out]  sei_data  Pointer to receive the SEI data\n\t * @param[out]  size      Pointer to receive the SEI data size\n\t * @return                true if SEI data available, false otherwise\n\t */\n\tbool (*get_sei_data)(void *data, uint8_t **sei_data, size_t *size);\n\n\t/**\n\t * Returns desired audio format and sample information\n\t *\n\t * @param          data  Data associated with this encoder context\n\t * @param[in/out]  info  Audio format information\n\t */\n\tvoid (*get_audio_info)(void *data, struct audio_convert_info *info);\n\n\t/**\n\t * Returns desired video format information\n\t *\n\t * @param          data  Data associated with this encoder context\n\t * @param[in/out]  info  Video format information\n\t */\n\tvoid (*get_video_info)(void *data, struct video_scale_info *info);\n\n\tvoid *type_data;\n\tvoid (*free_type_data)(void *type_data);\n\n\tuint32_t caps;\n\n\t/**\n\t * Gets the default settings for this encoder\n\t *\n\t * If get_defaults is also defined both will be called, and the first\n\t * call will be to get_defaults, then to get_defaults2.\n\t *\n\t * @param[out]  settings  Data to assign default settings to\n\t * @param[in]   typedata  Type Data\n\t */\n\tvoid (*get_defaults2)(obs_data_t *settings, void *type_data);\n\n\t/**\n\t * Gets the property information of this encoder\n\t *\n\t * @param[in]   data      Pointer from create (or null)\n\t * @param[in]   typedata  Type Data\n\t * @return                The properties data\n\t */\n\tobs_properties_t *(*get_properties2)(void *data, void *type_data);\n\n\tbool (*encode_texture)(void *data, uint32_t handle, int64_t pts, uint64_t lock_key, uint64_t *next_key,\n\t\t\t       struct encoder_packet *packet, bool *received_packet);\n\n\tbool (*encode_texture2)(void *data, struct encoder_texture *texture, int64_t pts, uint64_t lock_key,\n\t\t\t\tuint64_t *next_key, struct encoder_packet *packet, bool *received_packet);\n\n\t/** Audio encoder only: Returns padding, in samples, that must be skipped at the start of the stream. */\n\tuint32_t (*get_priming_samples)(void *data);\n};\n\nEXPORT void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size);\n\n/**\n * Register an encoder definition to the current obs context.  This should be\n * used in obs_module_load.\n *\n * @param  info  Pointer to the source definition structure.\n */\n#define obs_register_encoder(info) obs_register_encoder_s(info, sizeof(struct obs_encoder_info))\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-ffmpeg-compat.h",
    "content": "#pragma once\n\n#include <libavcodec/avcodec.h>\n\n/* LIBAVCODEC_VERSION_CHECK checks for the right version of libav and FFmpeg\n * a is the major version\n * b and c the minor and micro versions of libav\n * d and e the minor and micro versions of FFmpeg */\n#define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e)                                                   \\\n\t((LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c)) || \\\n\t (LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e)))\n\n#define INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE\n"
  },
  {
    "path": "libobs/obs-hevc.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-hevc.h\"\n\n#include \"obs.h\"\n#include \"obs-nal.h\"\n#include \"util/array-serializer.h\"\n\nbool obs_hevc_keyframe(const uint8_t *data, size_t size)\n{\n\tconst uint8_t *nal_start, *nal_end;\n\tconst uint8_t *end = data + size;\n\n\tnal_start = obs_nal_find_startcode(data, end);\n\twhile (true) {\n\t\twhile (nal_start < end && !*(nal_start++))\n\t\t\t;\n\n\t\tif (nal_start == end)\n\t\t\tbreak;\n\n\t\tconst uint8_t type = (nal_start[0] & 0x7F) >> 1;\n\n\t\tif (type <= OBS_HEVC_NAL_RSV_IRAP_VCL23)\n\t\t\treturn type >= OBS_HEVC_NAL_BLA_W_LP;\n\n\t\tnal_end = obs_nal_find_startcode(nal_start, end);\n\t\tnal_start = nal_end;\n\t}\n\n\treturn false;\n}\n\nstatic int compute_hevc_keyframe_priority(const uint8_t *nal_start, bool *is_keyframe, int priority)\n{\n\tint new_priority;\n\t// HEVC contains NAL unit specifier at [6..1] bits of\n\t// the byte next to the startcode 0x000001\n\tconst int type = (nal_start[0] & 0x7F) >> 1;\n\n\tswitch (type) {\n\tcase OBS_HEVC_NAL_BLA_W_LP:\n\tcase OBS_HEVC_NAL_BLA_W_RADL:\n\tcase OBS_HEVC_NAL_BLA_N_LP:\n\tcase OBS_HEVC_NAL_IDR_W_RADL:\n\tcase OBS_HEVC_NAL_IDR_N_LP:\n\tcase OBS_HEVC_NAL_CRA_NUT:\n\tcase OBS_HEVC_NAL_RSV_IRAP_VCL22:\n\tcase OBS_HEVC_NAL_RSV_IRAP_VCL23:\n\t\t/* intra random access point (IRAP) picture, keyframe and highest priority */\n\t\t*is_keyframe = true;\n\t\tnew_priority = OBS_NAL_PRIORITY_HIGHEST;\n\t\tbreak;\n\tcase OBS_HEVC_NAL_TRAIL_R:\n\tcase OBS_HEVC_NAL_TSA_R:\n\tcase OBS_HEVC_NAL_STSA_R:\n\tcase OBS_HEVC_NAL_RADL_R:\n\tcase OBS_HEVC_NAL_RASL_R:\n\t\t/* sub-layer reference picture (mainly P-frames), high priority */\n\t\tnew_priority = OBS_NAL_PRIORITY_HIGH;\n\t\tbreak;\n\tcase OBS_HEVC_NAL_TRAIL_N:\n\tcase OBS_HEVC_NAL_TSA_N:\n\tcase OBS_HEVC_NAL_STSA_N:\n\tcase OBS_HEVC_NAL_RADL_N:\n\tcase OBS_HEVC_NAL_RASL_N:\n\t\t/* sub-layer non-reference (SLNR) picture (mainly B-frames), disposable */\n\t\tnew_priority = OBS_NAL_PRIORITY_DISPOSABLE;\n\t\tbreak;\n\tdefault:\n\t\tnew_priority = OBS_NAL_PRIORITY_DISPOSABLE;\n\t}\n\n\treturn priority > new_priority ? priority : new_priority;\n}\n\nstatic void serialize_hevc_data(struct serializer *s, const uint8_t *data, size_t size, bool *is_keyframe,\n\t\t\t\tint *priority)\n{\n\tconst uint8_t *const end = data + size;\n\tconst uint8_t *nal_start = obs_nal_find_startcode(data, end);\n\twhile (true) {\n\t\twhile (nal_start < end && !*(nal_start++))\n\t\t\t;\n\n\t\tif (nal_start == end)\n\t\t\tbreak;\n\n\t\t*priority = compute_hevc_keyframe_priority(nal_start, is_keyframe, *priority);\n\n\t\tconst uint8_t *const nal_end = obs_nal_find_startcode(nal_start, end);\n\t\tconst size_t nal_size = nal_end - nal_start;\n\t\ts_wb32(s, (uint32_t)nal_size);\n\t\ts_write(s, nal_start, nal_size);\n\t\tnal_start = nal_end;\n\t}\n}\n\nvoid obs_parse_hevc_packet(struct encoder_packet *hevc_packet, const struct encoder_packet *src)\n{\n\tstruct array_output_data output;\n\tstruct serializer s;\n\tlong ref = 1;\n\n\tarray_output_serializer_init(&s, &output);\n\t*hevc_packet = *src;\n\n\tserialize(&s, &ref, sizeof(ref));\n\tserialize_hevc_data(&s, src->data, src->size, &hevc_packet->keyframe, &hevc_packet->priority);\n\n\thevc_packet->data = output.bytes.array + sizeof(ref);\n\thevc_packet->size = output.bytes.num - sizeof(ref);\n\thevc_packet->drop_priority = hevc_packet->priority;\n}\n\nint obs_parse_hevc_packet_priority(const struct encoder_packet *packet)\n{\n\tint priority = packet->priority;\n\n\tconst uint8_t *const data = packet->data;\n\tconst uint8_t *const end = data + packet->size;\n\tconst uint8_t *nal_start = obs_nal_find_startcode(data, end);\n\twhile (true) {\n\t\twhile (nal_start < end && !*(nal_start++))\n\t\t\t;\n\n\t\tif (nal_start == end)\n\t\t\tbreak;\n\n\t\tbool unused;\n\t\tpriority = compute_hevc_keyframe_priority(nal_start, &unused, priority);\n\n\t\tnal_start = obs_nal_find_startcode(nal_start, end);\n\t}\n\n\treturn priority;\n}\n\nvoid obs_extract_hevc_headers(const uint8_t *packet, size_t size, uint8_t **new_packet_data, size_t *new_packet_size,\n\t\t\t      uint8_t **header_data, size_t *header_size, uint8_t **sei_data, size_t *sei_size)\n{\n\tDARRAY(uint8_t) new_packet;\n\tDARRAY(uint8_t) header;\n\tDARRAY(uint8_t) sei;\n\tconst uint8_t *nal_start, *nal_end, *nal_codestart;\n\tconst uint8_t *end = packet + size;\n\n\tda_init(new_packet);\n\tda_init(header);\n\tda_init(sei);\n\n\tnal_start = obs_nal_find_startcode(packet, end);\n\tnal_end = NULL;\n\twhile (nal_end != end) {\n\t\tnal_codestart = nal_start;\n\n\t\twhile (nal_start < end && !*(nal_start++))\n\t\t\t;\n\n\t\tif (nal_start == end)\n\t\t\tbreak;\n\n\t\tconst uint8_t type = (nal_start[0] & 0x7F) >> 1;\n\n\t\tnal_end = obs_nal_find_startcode(nal_start, end);\n\t\tif (!nal_end)\n\t\t\tnal_end = end;\n\n\t\tif (type == OBS_HEVC_NAL_VPS || type == OBS_HEVC_NAL_SPS || type == OBS_HEVC_NAL_PPS) {\n\t\t\tda_push_back_array(header, nal_codestart, nal_end - nal_codestart);\n\t\t} else if (type == OBS_HEVC_NAL_SEI_PREFIX || type == OBS_HEVC_NAL_SEI_SUFFIX) {\n\t\t\tda_push_back_array(sei, nal_codestart, nal_end - nal_codestart);\n\n\t\t} else {\n\t\t\tda_push_back_array(new_packet, nal_codestart, nal_end - nal_codestart);\n\t\t}\n\n\t\tnal_start = nal_end;\n\t}\n\n\t*new_packet_data = new_packet.array;\n\t*new_packet_size = new_packet.num;\n\t*header_data = header.array;\n\t*header_size = header.num;\n\t*sei_data = sei.array;\n\t*sei_size = sei.num;\n}\n"
  },
  {
    "path": "libobs/obs-hevc.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"util/c99defs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct encoder_packet;\n\nenum {\n\tOBS_HEVC_NAL_TRAIL_N = 0,\n\tOBS_HEVC_NAL_TRAIL_R = 1,\n\tOBS_HEVC_NAL_TSA_N = 2,\n\tOBS_HEVC_NAL_TSA_R = 3,\n\tOBS_HEVC_NAL_STSA_N = 4,\n\tOBS_HEVC_NAL_STSA_R = 5,\n\tOBS_HEVC_NAL_RADL_N = 6,\n\tOBS_HEVC_NAL_RADL_R = 7,\n\tOBS_HEVC_NAL_RASL_N = 8,\n\tOBS_HEVC_NAL_RASL_R = 9,\n\tOBS_HEVC_NAL_VCL_N10 = 10,\n\tOBS_HEVC_NAL_VCL_R11 = 11,\n\tOBS_HEVC_NAL_VCL_N12 = 12,\n\tOBS_HEVC_NAL_VCL_R13 = 13,\n\tOBS_HEVC_NAL_VCL_N14 = 14,\n\tOBS_HEVC_NAL_VCL_R15 = 15,\n\tOBS_HEVC_NAL_BLA_W_LP = 16,\n\tOBS_HEVC_NAL_BLA_W_RADL = 17,\n\tOBS_HEVC_NAL_BLA_N_LP = 18,\n\tOBS_HEVC_NAL_IDR_W_RADL = 19,\n\tOBS_HEVC_NAL_IDR_N_LP = 20,\n\tOBS_HEVC_NAL_CRA_NUT = 21,\n\tOBS_HEVC_NAL_RSV_IRAP_VCL22 = 22,\n\tOBS_HEVC_NAL_RSV_IRAP_VCL23 = 23,\n\tOBS_HEVC_NAL_RSV_VCL24 = 24,\n\tOBS_HEVC_NAL_RSV_VCL25 = 25,\n\tOBS_HEVC_NAL_RSV_VCL26 = 26,\n\tOBS_HEVC_NAL_RSV_VCL27 = 27,\n\tOBS_HEVC_NAL_RSV_VCL28 = 28,\n\tOBS_HEVC_NAL_RSV_VCL29 = 29,\n\tOBS_HEVC_NAL_RSV_VCL30 = 30,\n\tOBS_HEVC_NAL_RSV_VCL31 = 31,\n\tOBS_HEVC_NAL_VPS = 32,\n\tOBS_HEVC_NAL_SPS = 33,\n\tOBS_HEVC_NAL_PPS = 34,\n\tOBS_HEVC_NAL_AUD = 35,\n\tOBS_HEVC_NAL_EOS_NUT = 36,\n\tOBS_HEVC_NAL_EOB_NUT = 37,\n\tOBS_HEVC_NAL_FD_NUT = 38,\n\tOBS_HEVC_NAL_SEI_PREFIX = 39,\n\tOBS_HEVC_NAL_SEI_SUFFIX = 40,\n};\n\nEXPORT bool obs_hevc_keyframe(const uint8_t *data, size_t size);\nEXPORT void obs_parse_hevc_packet(struct encoder_packet *hevc_packet, const struct encoder_packet *src);\nEXPORT int obs_parse_hevc_packet_priority(const struct encoder_packet *packet);\nEXPORT void obs_extract_hevc_headers(const uint8_t *packet, size_t size, uint8_t **new_packet_data,\n\t\t\t\t     size_t *new_packet_size, uint8_t **header_data, size_t *header_size,\n\t\t\t\t     uint8_t **sei_data, size_t *sei_size);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-hotkey-name-map.c",
    "content": "/******************************************************************************\n    Copyright (C) 2014 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <string.h>\n#include <assert.h>\n\n#include <util/bmem.h>\n#include <util/c99defs.h>\n\n#include \"obs-internal.h\"\n\nstruct obs_hotkey_name_map;\n\ntypedef struct obs_hotkey_name_map_item obs_hotkey_name_map_item_t;\n\nstruct obs_hotkey_name_map_item {\n\tchar *key;\n\tint val;\n\tUT_hash_handle hh;\n};\n\nstatic void obs_hotkey_name_map_insert(obs_hotkey_name_map_item_t **hmap, const char *key, int v)\n{\n\tif (!hmap || !key)\n\t\treturn;\n\n\tobs_hotkey_name_map_item_t *t;\n\tHASH_FIND_STR(*hmap, key, t);\n\tif (t)\n\t\treturn;\n\n\tt = bzalloc(sizeof(obs_hotkey_name_map_item_t));\n\tt->key = bstrdup(key);\n\tt->val = v;\n\n\tHASH_ADD_STR(*hmap, key, t);\n}\n\nstatic bool obs_hotkey_name_map_lookup(obs_hotkey_name_map_item_t *hmap, const char *key, int *v)\n{\n\tif (!hmap || !key)\n\t\treturn false;\n\n\tobs_hotkey_name_map_item_t *elem;\n\n\tHASH_FIND_STR(hmap, key, elem);\n\n\tif (elem) {\n\t\t*v = elem->val;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic const char *obs_key_names[] = {\n#define OBS_HOTKEY(x) #x,\n#include \"obs-hotkeys.h\"\n#undef OBS_HOTKEY\n};\n\nconst char *obs_key_to_name(obs_key_t key)\n{\n\tif (key >= OBS_KEY_LAST_VALUE) {\n\t\tblog(LOG_ERROR,\n\t\t     \"obs-hotkey.c: queried unknown key \"\n\t\t     \"with code %d\",\n\t\t     (int)key);\n\t\treturn \"\";\n\t}\n\n\treturn obs_key_names[key];\n}\n\nstatic obs_key_t obs_key_from_name_fallback(const char *name)\n{\n#define OBS_HOTKEY(x)              \\\n\tif (strcmp(#x, name) == 0) \\\n\t\treturn x;\n#include \"obs-hotkeys.h\"\n#undef OBS_HOTKEY\n\treturn OBS_KEY_NONE;\n}\n\nstatic void init_name_map(void)\n{\n#define OBS_HOTKEY(x) obs_hotkey_name_map_insert(&obs->hotkeys.name_map, #x, x);\n#include \"obs-hotkeys.h\"\n#undef OBS_HOTKEY\n}\n\nobs_key_t obs_key_from_name(const char *name)\n{\n\tif (!obs)\n\t\treturn obs_key_from_name_fallback(name);\n\n\tif (pthread_once(&obs->hotkeys.name_map_init_token, init_name_map))\n\t\treturn obs_key_from_name_fallback(name);\n\n\tint v = 0;\n\tif (obs_hotkey_name_map_lookup(obs->hotkeys.name_map, name, &v))\n\t\treturn v;\n\n\treturn OBS_KEY_NONE;\n}\n\nvoid obs_hotkey_name_map_free(void)\n{\n\tif (!obs || !obs->hotkeys.name_map)\n\t\treturn;\n\n\tobs_hotkey_name_map_item_t *root = obs->hotkeys.name_map;\n\tobs_hotkey_name_map_item_t *n, *tmp;\n\n\tHASH_ITER (hh, root, n, tmp) {\n\t\tHASH_DEL(root, n);\n\t\tbfree(n->key);\n\t\tbfree(n);\n\t}\n}\n"
  },
  {
    "path": "libobs/obs-hotkey.c",
    "content": "/******************************************************************************\n    Copyright (C) 2014-2015 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <inttypes.h>\n\n#include \"obs-internal.h\"\n\n/* Since ids are just sequential size_t integers, we don't really need a\n * hash function to get an even distribution across buckets.\n * (Realistically this should never wrap, who has 4.29 billion hotkeys?!)  */\n#undef HASH_FUNCTION\n#define HASH_FUNCTION(s, len, hashv) (hashv) = *s % UINT_MAX\n\n/* Custom definitions to make adding/looking up size_t integers easier */\n#define HASH_ADD_HKEY(head, idfield, add) HASH_ADD(hh, head, idfield, sizeof(size_t), add)\n#define HASH_FIND_HKEY(head, id, out) HASH_FIND(hh, head, &(id), sizeof(size_t), out)\n\nstatic inline bool lock(void)\n{\n\tif (!obs)\n\t\treturn false;\n\n\tpthread_mutex_lock(&obs->hotkeys.mutex);\n\treturn true;\n}\n\nstatic inline void unlock(void)\n{\n\tpthread_mutex_unlock(&obs->hotkeys.mutex);\n}\n\nobs_hotkey_id obs_hotkey_get_id(const obs_hotkey_t *key)\n{\n\treturn key->id;\n}\n\nconst char *obs_hotkey_get_name(const obs_hotkey_t *key)\n{\n\treturn key->name;\n}\n\nconst char *obs_hotkey_get_description(const obs_hotkey_t *key)\n{\n\treturn key->description;\n}\n\nobs_hotkey_registerer_t obs_hotkey_get_registerer_type(const obs_hotkey_t *key)\n{\n\treturn key->registerer_type;\n}\n\nvoid *obs_hotkey_get_registerer(const obs_hotkey_t *key)\n{\n\treturn key->registerer;\n}\n\nobs_hotkey_id obs_hotkey_get_pair_partner_id(const obs_hotkey_t *key)\n{\n\treturn key->pair_partner_id;\n}\n\nobs_key_combination_t obs_hotkey_binding_get_key_combination(obs_hotkey_binding_t *binding)\n{\n\treturn binding->key;\n}\n\nobs_hotkey_id obs_hotkey_binding_get_hotkey_id(obs_hotkey_binding_t *binding)\n{\n\treturn binding->hotkey_id;\n}\n\nobs_hotkey_t *obs_hotkey_binding_get_hotkey(obs_hotkey_binding_t *binding)\n{\n\treturn binding->hotkey;\n}\n\nvoid obs_hotkey_set_name(obs_hotkey_id id, const char *name)\n{\n\tobs_hotkey_t *hotkey;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, id, hotkey);\n\tif (!hotkey)\n\t\treturn;\n\n\tbfree(hotkey->name);\n\thotkey->name = bstrdup(name);\n}\n\nvoid obs_hotkey_set_description(obs_hotkey_id id, const char *desc)\n{\n\tobs_hotkey_t *hotkey;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, id, hotkey);\n\tif (!hotkey)\n\t\treturn;\n\n\tbfree(hotkey->description);\n\thotkey->description = bstrdup(desc);\n}\n\nvoid obs_hotkey_pair_set_names(obs_hotkey_pair_id id, const char *name0, const char *name1)\n{\n\tobs_hotkey_pair_t *pair;\n\n\tHASH_FIND_HKEY(obs->hotkeys.hotkey_pairs, id, pair);\n\tif (!pair)\n\t\treturn;\n\n\tobs_hotkey_set_name(pair->id[0], name0);\n\tobs_hotkey_set_name(pair->id[1], name1);\n}\n\nvoid obs_hotkey_pair_set_descriptions(obs_hotkey_pair_id id, const char *desc0, const char *desc1)\n{\n\tobs_hotkey_pair_t *pair;\n\n\tHASH_FIND_HKEY(obs->hotkeys.hotkey_pairs, id, pair);\n\tif (!pair)\n\t\treturn;\n\n\tobs_hotkey_set_description(pair->id[0], desc0);\n\tobs_hotkey_set_description(pair->id[1], desc1);\n}\n\nstatic void hotkey_signal(const char *signal, obs_hotkey_t *hotkey)\n{\n\tcalldata_t data;\n\tcalldata_init(&data);\n\tcalldata_set_ptr(&data, \"key\", hotkey);\n\n\tsignal_handler_signal(obs->hotkeys.signals, signal, &data);\n\n\tcalldata_free(&data);\n}\n\nstatic inline void load_bindings(obs_hotkey_t *hotkey, obs_data_array_t *data);\n\nstatic inline void context_add_hotkey(struct obs_context_data *context, obs_hotkey_id id)\n{\n\tda_push_back(context->hotkeys, &id);\n}\n\nstatic inline obs_hotkey_id obs_hotkey_register_internal(obs_hotkey_registerer_t type, void *registerer,\n\t\t\t\t\t\t\t struct obs_context_data *context, const char *name,\n\t\t\t\t\t\t\t const char *description, obs_hotkey_func func, void *data)\n{\n\tif ((obs->hotkeys.next_id + 1) == OBS_INVALID_HOTKEY_ID)\n\t\tblog(LOG_WARNING, \"obs-hotkey: Available hotkey ids exhausted\");\n\n\tobs_hotkey_id result = obs->hotkeys.next_id++;\n\tobs_hotkey_t *hotkey = bzalloc(sizeof(obs_hotkey_t));\n\n\thotkey->id = result;\n\thotkey->name = bstrdup(name);\n\thotkey->description = bstrdup(description);\n\thotkey->func = func;\n\thotkey->data = data;\n\thotkey->registerer_type = type;\n\thotkey->registerer = registerer;\n\thotkey->pair_partner_id = OBS_INVALID_HOTKEY_PAIR_ID;\n\n\tHASH_ADD_HKEY(obs->hotkeys.hotkeys, id, hotkey);\n\n\tif (context) {\n\t\tobs_data_array_t *data = obs_data_get_array(context->hotkey_data, name);\n\t\tload_bindings(hotkey, data);\n\t\tobs_data_array_release(data);\n\n\t\tcontext_add_hotkey(context, result);\n\t}\n\n\thotkey_signal(\"hotkey_register\", hotkey);\n\n\treturn result;\n}\n\nobs_hotkey_id obs_hotkey_register_frontend(const char *name, const char *description, obs_hotkey_func func, void *data)\n{\n\tif (!lock())\n\t\treturn OBS_INVALID_HOTKEY_ID;\n\n\tobs_hotkey_id id =\n\t\tobs_hotkey_register_internal(OBS_HOTKEY_REGISTERER_FRONTEND, NULL, NULL, name, description, func, data);\n\n\tunlock();\n\treturn id;\n}\n\nobs_hotkey_id obs_hotkey_register_encoder(obs_encoder_t *encoder, const char *name, const char *description,\n\t\t\t\t\t  obs_hotkey_func func, void *data)\n{\n\tif (!encoder || !lock())\n\t\treturn OBS_INVALID_HOTKEY_ID;\n\n\tobs_hotkey_id id = obs_hotkey_register_internal(OBS_HOTKEY_REGISTERER_ENCODER,\n\t\t\t\t\t\t\tobs_encoder_get_weak_encoder(encoder), &encoder->context, name,\n\t\t\t\t\t\t\tdescription, func, data);\n\n\tunlock();\n\treturn id;\n}\n\nobs_hotkey_id obs_hotkey_register_output(obs_output_t *output, const char *name, const char *description,\n\t\t\t\t\t obs_hotkey_func func, void *data)\n{\n\tif (!output || !lock())\n\t\treturn OBS_INVALID_HOTKEY_ID;\n\n\tobs_hotkey_id id = obs_hotkey_register_internal(OBS_HOTKEY_REGISTERER_OUTPUT,\n\t\t\t\t\t\t\tobs_output_get_weak_output(output), &output->context, name,\n\t\t\t\t\t\t\tdescription, func, data);\n\n\tunlock();\n\treturn id;\n}\n\nobs_hotkey_id obs_hotkey_register_service(obs_service_t *service, const char *name, const char *description,\n\t\t\t\t\t  obs_hotkey_func func, void *data)\n{\n\tif (!service || !lock())\n\t\treturn OBS_INVALID_HOTKEY_ID;\n\n\tobs_hotkey_id id = obs_hotkey_register_internal(OBS_HOTKEY_REGISTERER_SERVICE,\n\t\t\t\t\t\t\tobs_service_get_weak_service(service), &service->context, name,\n\t\t\t\t\t\t\tdescription, func, data);\n\n\tunlock();\n\treturn id;\n}\n\nobs_hotkey_id obs_hotkey_register_source(obs_source_t *source, const char *name, const char *description,\n\t\t\t\t\t obs_hotkey_func func, void *data)\n{\n\tif (!source || source->context.private || !lock())\n\t\treturn OBS_INVALID_HOTKEY_ID;\n\n\tobs_hotkey_id id = obs_hotkey_register_internal(OBS_HOTKEY_REGISTERER_SOURCE,\n\t\t\t\t\t\t\tobs_source_get_weak_source(source), &source->context, name,\n\t\t\t\t\t\t\tdescription, func, data);\n\n\tunlock();\n\treturn id;\n}\n\nstatic obs_hotkey_pair_t *create_hotkey_pair(struct obs_context_data *context, obs_hotkey_active_func func0,\n\t\t\t\t\t     obs_hotkey_active_func func1, void *data0, void *data1)\n{\n\tif ((obs->hotkeys.next_pair_id + 1) == OBS_INVALID_HOTKEY_PAIR_ID)\n\t\tblog(LOG_WARNING, \"obs-hotkey: Available hotkey pair ids \"\n\t\t\t\t  \"exhausted\");\n\n\tobs_hotkey_pair_t *pair = bzalloc(sizeof(obs_hotkey_pair_t));\n\n\tpair->pair_id = obs->hotkeys.next_pair_id++;\n\tpair->func[0] = func0;\n\tpair->func[1] = func1;\n\tpair->id[0] = OBS_INVALID_HOTKEY_ID;\n\tpair->id[1] = OBS_INVALID_HOTKEY_ID;\n\tpair->data[0] = data0;\n\tpair->data[1] = data1;\n\n\tHASH_ADD_HKEY(obs->hotkeys.hotkey_pairs, pair_id, pair);\n\n\tif (context)\n\t\tda_push_back(context->hotkey_pairs, &pair->pair_id);\n\n\treturn pair;\n}\n\nstatic void obs_hotkey_pair_first_func(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed)\n{\n\tUNUSED_PARAMETER(id);\n\n\tobs_hotkey_pair_t *pair = data;\n\tif (pair->pressed1)\n\t\treturn;\n\n\tif (pair->pressed0 && !pressed)\n\t\tpair->pressed0 = false;\n\telse if (pair->func[0](pair->data[0], pair->pair_id, hotkey, pressed))\n\t\tpair->pressed0 = pressed;\n}\n\nstatic void obs_hotkey_pair_second_func(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed)\n{\n\tUNUSED_PARAMETER(id);\n\n\tobs_hotkey_pair_t *pair = data;\n\tif (pair->pressed0)\n\t\treturn;\n\n\tif (pair->pressed1 && !pressed)\n\t\tpair->pressed1 = false;\n\telse if (pair->func[1](pair->data[1], pair->pair_id, hotkey, pressed))\n\t\tpair->pressed1 = pressed;\n}\n\nstatic obs_hotkey_pair_id register_hotkey_pair_internal(obs_hotkey_registerer_t type, void *registerer,\n\t\t\t\t\t\t\tvoid *(*weak_ref)(void *), struct obs_context_data *context,\n\t\t\t\t\t\t\tconst char *name0, const char *description0, const char *name1,\n\t\t\t\t\t\t\tconst char *description1, obs_hotkey_active_func func0,\n\t\t\t\t\t\t\tobs_hotkey_active_func func1, void *data0, void *data1)\n{\n\n\tif (!lock())\n\t\treturn OBS_INVALID_HOTKEY_PAIR_ID;\n\n\tobs_hotkey_pair_t *pair = create_hotkey_pair(context, func0, func1, data0, data1);\n\n\tpair->id[0] = obs_hotkey_register_internal(type, weak_ref(registerer), context, name0, description0,\n\t\t\t\t\t\t   obs_hotkey_pair_first_func, pair);\n\n\tpair->id[1] = obs_hotkey_register_internal(type, weak_ref(registerer), context, name1, description1,\n\t\t\t\t\t\t   obs_hotkey_pair_second_func, pair);\n\n\tobs_hotkey_t *hotkey_1, *hotkey_2;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, pair->id[0], hotkey_1);\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, pair->id[1], hotkey_2);\n\n\tif (hotkey_1)\n\t\thotkey_1->pair_partner_id = pair->id[1];\n\tif (hotkey_2)\n\t\thotkey_2->pair_partner_id = pair->id[0];\n\n\tobs_hotkey_pair_id id = pair->pair_id;\n\n\tunlock();\n\treturn id;\n}\n\nstatic inline void *obs_id_(void *id_)\n{\n\treturn id_;\n}\n\nobs_hotkey_pair_id obs_hotkey_pair_register_frontend(const char *name0, const char *description0, const char *name1,\n\t\t\t\t\t\t     const char *description1, obs_hotkey_active_func func0,\n\t\t\t\t\t\t     obs_hotkey_active_func func1, void *data0, void *data1)\n{\n\treturn register_hotkey_pair_internal(OBS_HOTKEY_REGISTERER_FRONTEND, NULL, obs_id_, NULL, name0, description0,\n\t\t\t\t\t     name1, description1, func0, func1, data0, data1);\n}\n\nstatic inline void *weak_encoder_ref(void *ref)\n{\n\treturn obs_encoder_get_weak_encoder(ref);\n}\n\nobs_hotkey_pair_id obs_hotkey_pair_register_encoder(obs_encoder_t *encoder, const char *name0, const char *description0,\n\t\t\t\t\t\t    const char *name1, const char *description1,\n\t\t\t\t\t\t    obs_hotkey_active_func func0, obs_hotkey_active_func func1,\n\t\t\t\t\t\t    void *data0, void *data1)\n{\n\tif (!encoder)\n\t\treturn OBS_INVALID_HOTKEY_PAIR_ID;\n\treturn register_hotkey_pair_internal(OBS_HOTKEY_REGISTERER_ENCODER, encoder, weak_encoder_ref,\n\t\t\t\t\t     &encoder->context, name0, description0, name1, description1, func0, func1,\n\t\t\t\t\t     data0, data1);\n}\n\nstatic inline void *weak_output_ref(void *ref)\n{\n\treturn obs_output_get_weak_output(ref);\n}\n\nobs_hotkey_pair_id obs_hotkey_pair_register_output(obs_output_t *output, const char *name0, const char *description0,\n\t\t\t\t\t\t   const char *name1, const char *description1,\n\t\t\t\t\t\t   obs_hotkey_active_func func0, obs_hotkey_active_func func1,\n\t\t\t\t\t\t   void *data0, void *data1)\n{\n\tif (!output)\n\t\treturn OBS_INVALID_HOTKEY_PAIR_ID;\n\treturn register_hotkey_pair_internal(OBS_HOTKEY_REGISTERER_OUTPUT, output, weak_output_ref, &output->context,\n\t\t\t\t\t     name0, description0, name1, description1, func0, func1, data0, data1);\n}\n\nstatic inline void *weak_service_ref(void *ref)\n{\n\treturn obs_service_get_weak_service(ref);\n}\n\nobs_hotkey_pair_id obs_hotkey_pair_register_service(obs_service_t *service, const char *name0, const char *description0,\n\t\t\t\t\t\t    const char *name1, const char *description1,\n\t\t\t\t\t\t    obs_hotkey_active_func func0, obs_hotkey_active_func func1,\n\t\t\t\t\t\t    void *data0, void *data1)\n{\n\tif (!service)\n\t\treturn OBS_INVALID_HOTKEY_PAIR_ID;\n\treturn register_hotkey_pair_internal(OBS_HOTKEY_REGISTERER_SERVICE, service, weak_service_ref,\n\t\t\t\t\t     &service->context, name0, description0, name1, description1, func0, func1,\n\t\t\t\t\t     data0, data1);\n}\n\nstatic inline void *weak_source_ref(void *ref)\n{\n\treturn obs_source_get_weak_source(ref);\n}\n\nobs_hotkey_pair_id obs_hotkey_pair_register_source(obs_source_t *source, const char *name0, const char *description0,\n\t\t\t\t\t\t   const char *name1, const char *description1,\n\t\t\t\t\t\t   obs_hotkey_active_func func0, obs_hotkey_active_func func1,\n\t\t\t\t\t\t   void *data0, void *data1)\n{\n\tif (!source)\n\t\treturn OBS_INVALID_HOTKEY_PAIR_ID;\n\treturn register_hotkey_pair_internal(OBS_HOTKEY_REGISTERER_SOURCE, source, weak_source_ref, &source->context,\n\t\t\t\t\t     name0, description0, name1, description1, func0, func1, data0, data1);\n}\n\ntypedef bool (*obs_hotkey_binding_internal_enum_func)(void *data, size_t idx, obs_hotkey_binding_t *binding);\n\nstatic inline void enum_bindings(obs_hotkey_binding_internal_enum_func func, void *data)\n{\n\tconst size_t num = obs->hotkeys.bindings.num;\n\tobs_hotkey_binding_t *array = obs->hotkeys.bindings.array;\n\tfor (size_t i = 0; i < num; i++) {\n\t\tif (!func(data, i, &array[i]))\n\t\t\tbreak;\n\t}\n}\n\ntypedef bool (*obs_hotkey_internal_enum_func)(void *data, obs_hotkey_t *hotkey);\n\nstatic inline void enum_context_hotkeys(struct obs_context_data *context, obs_hotkey_internal_enum_func func,\n\t\t\t\t\tvoid *data)\n{\n\tconst size_t num = context->hotkeys.num;\n\tconst obs_hotkey_id *array = context->hotkeys.array;\n\tobs_hotkey_t *hotkey;\n\n\tfor (size_t i = 0; i < num; i++) {\n\t\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, array[i], hotkey);\n\t\tif (!hotkey)\n\t\t\tcontinue;\n\t\tif (!func(data, hotkey))\n\t\t\tbreak;\n\t}\n}\n\nstatic inline void load_modifier(uint32_t *modifiers, obs_data_t *data, const char *name, uint32_t flag)\n{\n\tif (obs_data_get_bool(data, name))\n\t\t*modifiers |= flag;\n}\n\nstatic inline void create_binding(obs_hotkey_t *hotkey, obs_key_combination_t combo)\n{\n\tobs_hotkey_binding_t *binding = da_push_back_new(obs->hotkeys.bindings);\n\tif (!binding)\n\t\treturn;\n\n\tbinding->key = combo;\n\tbinding->hotkey_id = hotkey->id;\n\tbinding->hotkey = hotkey;\n}\n\nstatic inline void load_binding(obs_hotkey_t *hotkey, obs_data_t *data)\n{\n\tif (!hotkey || !data)\n\t\treturn;\n\n\tobs_key_combination_t combo = {0};\n\tuint32_t *modifiers = &combo.modifiers;\n\tload_modifier(modifiers, data, \"shift\", INTERACT_SHIFT_KEY);\n\tload_modifier(modifiers, data, \"control\", INTERACT_CONTROL_KEY);\n\tload_modifier(modifiers, data, \"alt\", INTERACT_ALT_KEY);\n\tload_modifier(modifiers, data, \"command\", INTERACT_COMMAND_KEY);\n\n\tcombo.key = obs_key_from_name(obs_data_get_string(data, \"key\"));\n\tif (!modifiers && (combo.key == OBS_KEY_NONE || combo.key >= OBS_KEY_LAST_VALUE))\n\t\treturn;\n\n\tcreate_binding(hotkey, combo);\n}\n\nstatic inline void load_bindings(obs_hotkey_t *hotkey, obs_data_array_t *data)\n{\n\tconst size_t count = obs_data_array_count(data);\n\tfor (size_t i = 0; i < count; i++) {\n\t\tobs_data_t *item = obs_data_array_item(data, i);\n\t\tload_binding(hotkey, item);\n\t\tobs_data_release(item);\n\t}\n\n\tif (count)\n\t\thotkey_signal(\"hotkey_bindings_changed\", hotkey);\n}\n\nstatic inline bool remove_bindings(obs_hotkey_id id);\n\nvoid obs_hotkey_load_bindings(obs_hotkey_id id, obs_key_combination_t *combinations, size_t num)\n{\n\tif (!lock())\n\t\treturn;\n\n\tobs_hotkey_t *hotkey;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, id, hotkey);\n\tif (hotkey) {\n\t\tbool changed = remove_bindings(id);\n\t\tfor (size_t i = 0; i < num; i++)\n\t\t\tcreate_binding(hotkey, combinations[i]);\n\n\t\tif (num || changed)\n\t\t\thotkey_signal(\"hotkey_bindings_changed\", hotkey);\n\t}\n\n\tunlock();\n}\n\nvoid obs_hotkey_load(obs_hotkey_id id, obs_data_array_t *data)\n{\n\tif (!lock())\n\t\treturn;\n\n\tobs_hotkey_t *hotkey;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, id, hotkey);\n\tif (hotkey) {\n\t\tremove_bindings(id);\n\t\tload_bindings(hotkey, data);\n\t}\n\tunlock();\n}\n\nstatic inline bool enum_load_bindings(void *data, obs_hotkey_t *hotkey)\n{\n\tobs_data_array_t *hotkey_data = obs_data_get_array(data, hotkey->name);\n\tif (!hotkey_data)\n\t\treturn true;\n\n\tload_bindings(hotkey, hotkey_data);\n\tobs_data_array_release(hotkey_data);\n\treturn true;\n}\n\nvoid obs_hotkeys_load_encoder(obs_encoder_t *encoder, obs_data_t *hotkeys)\n{\n\tif (!encoder || !hotkeys)\n\t\treturn;\n\tif (!lock())\n\t\treturn;\n\n\tenum_context_hotkeys(&encoder->context, enum_load_bindings, hotkeys);\n\tunlock();\n}\n\nvoid obs_hotkeys_load_output(obs_output_t *output, obs_data_t *hotkeys)\n{\n\tif (!output || !hotkeys)\n\t\treturn;\n\tif (!lock())\n\t\treturn;\n\n\tenum_context_hotkeys(&output->context, enum_load_bindings, hotkeys);\n\tunlock();\n}\n\nvoid obs_hotkeys_load_service(obs_service_t *service, obs_data_t *hotkeys)\n{\n\tif (!service || !hotkeys)\n\t\treturn;\n\tif (!lock())\n\t\treturn;\n\n\tenum_context_hotkeys(&service->context, enum_load_bindings, hotkeys);\n\tunlock();\n}\n\nvoid obs_hotkeys_load_source(obs_source_t *source, obs_data_t *hotkeys)\n{\n\tif (!source || !hotkeys)\n\t\treturn;\n\tif (!lock())\n\t\treturn;\n\n\tenum_context_hotkeys(&source->context, enum_load_bindings, hotkeys);\n\tunlock();\n}\n\nvoid obs_hotkey_pair_load(obs_hotkey_pair_id id, obs_data_array_t *data0, obs_data_array_t *data1)\n{\n\tif ((!data0 && !data1) || !lock())\n\t\treturn;\n\n\tobs_hotkey_pair_t *pair;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkey_pairs, id, pair);\n\tif (!pair)\n\t\tgoto unlock;\n\n\tobs_hotkey_t *p1, *p2;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, pair->id[0], p1);\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, pair->id[1], p2);\n\n\tif (p1) {\n\t\tremove_bindings(pair->id[0]);\n\t\tload_bindings(p1, data0);\n\t}\n\tif (p2) {\n\t\tremove_bindings(pair->id[1]);\n\t\tload_bindings(p2, data1);\n\t}\n\nunlock:\n\tunlock();\n}\n\nstatic inline void save_modifier(uint32_t modifiers, obs_data_t *data, const char *name, uint32_t flag)\n{\n\tif ((modifiers & flag) == flag)\n\t\tobs_data_set_bool(data, name, true);\n}\n\nstruct save_bindings_helper_t {\n\tobs_data_array_t *array;\n\tobs_hotkey_t *hotkey;\n};\n\nstatic inline bool save_bindings_helper(void *data, size_t idx, obs_hotkey_binding_t *binding)\n{\n\tUNUSED_PARAMETER(idx);\n\tstruct save_bindings_helper_t *h = data;\n\n\tif (h->hotkey->id != binding->hotkey_id)\n\t\treturn true;\n\n\tobs_data_t *hotkey = obs_data_create();\n\n\tuint32_t modifiers = binding->key.modifiers;\n\tsave_modifier(modifiers, hotkey, \"shift\", INTERACT_SHIFT_KEY);\n\tsave_modifier(modifiers, hotkey, \"control\", INTERACT_CONTROL_KEY);\n\tsave_modifier(modifiers, hotkey, \"alt\", INTERACT_ALT_KEY);\n\tsave_modifier(modifiers, hotkey, \"command\", INTERACT_COMMAND_KEY);\n\n\tobs_data_set_string(hotkey, \"key\", obs_key_to_name(binding->key.key));\n\n\tobs_data_array_push_back(h->array, hotkey);\n\n\tobs_data_release(hotkey);\n\n\treturn true;\n}\n\nstatic inline obs_data_array_t *save_hotkey(obs_hotkey_t *hotkey)\n{\n\tobs_data_array_t *data = obs_data_array_create();\n\n\tstruct save_bindings_helper_t arg = {data, hotkey};\n\tenum_bindings(save_bindings_helper, &arg);\n\n\treturn data;\n}\n\nobs_data_array_t *obs_hotkey_save(obs_hotkey_id id)\n{\n\tobs_data_array_t *result = NULL;\n\n\tif (!lock())\n\t\treturn result;\n\n\tobs_hotkey_t *hotkey;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, id, hotkey);\n\tif (hotkey)\n\t\tresult = save_hotkey(hotkey);\n\n\tunlock();\n\n\treturn result;\n}\n\nvoid obs_hotkey_pair_save(obs_hotkey_pair_id id, obs_data_array_t **p_data0, obs_data_array_t **p_data1)\n{\n\tif ((!p_data0 && !p_data1) || !lock())\n\t\treturn;\n\n\tobs_hotkey_pair_t *pair;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkey_pairs, id, pair);\n\tif (!pair)\n\t\tgoto unlock;\n\n\tobs_hotkey_t *hotkey;\n\tif (p_data0) {\n\t\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, pair->id[0], hotkey);\n\t\tif (hotkey)\n\t\t\t*p_data0 = save_hotkey(hotkey);\n\t}\n\tif (p_data1) {\n\t\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, pair->id[1], hotkey);\n\t\tif (hotkey)\n\t\t\t*p_data1 = save_hotkey(hotkey);\n\t}\n\nunlock:\n\tunlock();\n}\n\nstatic inline bool enum_save_hotkey(void *data, obs_hotkey_t *hotkey)\n{\n\tobs_data_array_t *hotkey_data = save_hotkey(hotkey);\n\tobs_data_set_array(data, hotkey->name, hotkey_data);\n\tobs_data_array_release(hotkey_data);\n\treturn true;\n}\n\nstatic inline obs_data_t *save_context_hotkeys(struct obs_context_data *context)\n{\n\tif (!context->hotkeys.num)\n\t\treturn NULL;\n\n\tobs_data_t *result = obs_data_create();\n\tenum_context_hotkeys(context, enum_save_hotkey, result);\n\treturn result;\n}\n\nobs_data_t *obs_hotkeys_save_encoder(obs_encoder_t *encoder)\n{\n\tobs_data_t *result = NULL;\n\n\tif (!lock())\n\t\treturn result;\n\n\tresult = save_context_hotkeys(&encoder->context);\n\tunlock();\n\n\treturn result;\n}\n\nobs_data_t *obs_hotkeys_save_output(obs_output_t *output)\n{\n\tobs_data_t *result = NULL;\n\n\tif (!lock())\n\t\treturn result;\n\n\tresult = save_context_hotkeys(&output->context);\n\tunlock();\n\n\treturn result;\n}\n\nobs_data_t *obs_hotkeys_save_service(obs_service_t *service)\n{\n\tobs_data_t *result = NULL;\n\n\tif (!lock())\n\t\treturn result;\n\n\tresult = save_context_hotkeys(&service->context);\n\tunlock();\n\n\treturn result;\n}\n\nobs_data_t *obs_hotkeys_save_source(obs_source_t *source)\n{\n\tobs_data_t *result = NULL;\n\n\tif (!lock())\n\t\treturn result;\n\n\tresult = save_context_hotkeys(&source->context);\n\tunlock();\n\n\treturn result;\n}\n\nstruct binding_find_data {\n\tobs_hotkey_id id;\n\tsize_t *idx;\n\tbool found;\n};\n\nstatic inline bool binding_finder(void *data, size_t idx, obs_hotkey_binding_t *binding)\n{\n\tstruct binding_find_data *find = data;\n\tif (binding->hotkey_id != find->id)\n\t\treturn true;\n\n\t*find->idx = idx;\n\tfind->found = true;\n\treturn false;\n}\n\nstatic inline bool find_binding(obs_hotkey_id id, size_t *idx)\n{\n\tstruct binding_find_data data = {id, idx, false};\n\tenum_bindings(binding_finder, &data);\n\treturn data.found;\n}\n\nstatic inline void release_pressed_binding(obs_hotkey_binding_t *binding);\n\nstatic inline bool remove_bindings(obs_hotkey_id id)\n{\n\tbool removed = false;\n\tsize_t idx;\n\twhile (find_binding(id, &idx)) {\n\t\tobs_hotkey_binding_t *binding = &obs->hotkeys.bindings.array[idx];\n\n\t\tif (binding->pressed)\n\t\t\trelease_pressed_binding(binding);\n\n\t\tda_erase(obs->hotkeys.bindings, idx);\n\t\tremoved = true;\n\t}\n\n\treturn removed;\n}\n\nstatic void release_registerer(obs_hotkey_t *hotkey)\n{\n\tswitch (hotkey->registerer_type) {\n\tcase OBS_HOTKEY_REGISTERER_FRONTEND:\n\t\tbreak;\n\n\tcase OBS_HOTKEY_REGISTERER_ENCODER:\n\t\tobs_weak_encoder_release(hotkey->registerer);\n\t\tbreak;\n\n\tcase OBS_HOTKEY_REGISTERER_OUTPUT:\n\t\tobs_weak_output_release(hotkey->registerer);\n\t\tbreak;\n\n\tcase OBS_HOTKEY_REGISTERER_SERVICE:\n\t\tobs_weak_service_release(hotkey->registerer);\n\t\tbreak;\n\n\tcase OBS_HOTKEY_REGISTERER_SOURCE:\n\t\tobs_weak_source_release(hotkey->registerer);\n\t\tbreak;\n\t}\n\n\thotkey->registerer = NULL;\n}\n\nstatic inline void unregister_hotkey(obs_hotkey_id id)\n{\n\tif (id >= obs->hotkeys.next_id)\n\t\treturn;\n\n\tobs_hotkey_t *hotkey;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, id, hotkey);\n\tif (!hotkey)\n\t\treturn;\n\n\tHASH_DEL(obs->hotkeys.hotkeys, hotkey);\n\n\thotkey_signal(\"hotkey_unregister\", hotkey);\n\n\trelease_registerer(hotkey);\n\n\tif (hotkey->registerer_type == OBS_HOTKEY_REGISTERER_SOURCE)\n\t\tobs_weak_source_release(hotkey->registerer);\n\n\tbfree(hotkey->name);\n\tbfree(hotkey->description);\n\tbfree(hotkey);\n\n\tremove_bindings(id);\n}\n\nstatic inline void unregister_hotkey_pair(obs_hotkey_pair_id id)\n{\n\tif (id >= obs->hotkeys.next_pair_id)\n\t\treturn;\n\n\tobs_hotkey_pair_t *pair;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkey_pairs, id, pair);\n\tif (!pair)\n\t\treturn;\n\n\tunregister_hotkey(pair->id[0]);\n\tunregister_hotkey(pair->id[1]);\n\n\tHASH_DEL(obs->hotkeys.hotkey_pairs, pair);\n\tbfree(pair);\n}\n\nvoid obs_hotkey_unregister(obs_hotkey_id id)\n{\n\tif (!lock())\n\t\treturn;\n\n\tunregister_hotkey(id);\n\tunlock();\n}\n\nvoid obs_hotkey_pair_unregister(obs_hotkey_pair_id id)\n{\n\tif (!lock())\n\t\treturn;\n\n\tunregister_hotkey_pair(id);\n\tunlock();\n}\n\nstatic void context_release_hotkeys(struct obs_context_data *context)\n{\n\tif (!context->hotkeys.num)\n\t\tgoto cleanup;\n\n\tfor (size_t i = 0; i < context->hotkeys.num; i++)\n\t\tunregister_hotkey(context->hotkeys.array[i]);\n\ncleanup:\n\tda_free(context->hotkeys);\n}\n\nstatic void context_release_hotkey_pairs(struct obs_context_data *context)\n{\n\tif (!context->hotkey_pairs.num)\n\t\tgoto cleanup;\n\n\tfor (size_t i = 0; i < context->hotkey_pairs.num; i++)\n\t\tunregister_hotkey_pair(context->hotkey_pairs.array[i]);\n\ncleanup:\n\tda_free(context->hotkey_pairs);\n}\n\nvoid obs_hotkeys_context_release(struct obs_context_data *context)\n{\n\tif (!lock())\n\t\treturn;\n\n\tcontext_release_hotkeys(context);\n\tcontext_release_hotkey_pairs(context);\n\n\tobs_data_release(context->hotkey_data);\n\tunlock();\n}\n\nvoid obs_hotkeys_free(void)\n{\n\tobs_hotkey_t *hotkey, *tmp;\n\tHASH_ITER (hh, obs->hotkeys.hotkeys, hotkey, tmp) {\n\t\tHASH_DEL(obs->hotkeys.hotkeys, hotkey);\n\t\tbfree(hotkey->name);\n\t\tbfree(hotkey->description);\n\t\trelease_registerer(hotkey);\n\t\tbfree(hotkey);\n\t}\n\n\tobs_hotkey_pair_t *pair, *tmp2;\n\tHASH_ITER (hh, obs->hotkeys.hotkey_pairs, pair, tmp2) {\n\t\tHASH_DEL(obs->hotkeys.hotkey_pairs, pair);\n\t\tbfree(pair);\n\t}\n\n\tda_free(obs->hotkeys.bindings);\n\n\tfor (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {\n\t\tif (obs->hotkeys.translations[i]) {\n\t\t\tbfree(obs->hotkeys.translations[i]);\n\t\t\tobs->hotkeys.translations[i] = NULL;\n\t\t}\n\t}\n}\n\nvoid obs_enum_hotkeys(obs_hotkey_enum_func func, void *data)\n{\n\tif (!lock())\n\t\treturn;\n\n\tobs_hotkey_t *hk, *tmp;\n\tHASH_ITER (hh, obs->hotkeys.hotkeys, hk, tmp) {\n\t\tif (!func(data, hk->id, hk))\n\t\t\tbreak;\n\t}\n\n\tunlock();\n}\n\nvoid obs_enum_hotkey_bindings(obs_hotkey_binding_enum_func func, void *data)\n{\n\tif (!lock())\n\t\treturn;\n\n\tenum_bindings(func, data);\n\tunlock();\n}\n\nstatic inline bool modifiers_match(obs_hotkey_binding_t *binding, uint32_t modifiers_, bool strict_modifiers)\n{\n\tuint32_t modifiers = binding->key.modifiers;\n\tif (!strict_modifiers)\n\t\treturn (modifiers & modifiers_) == modifiers;\n\telse\n\t\treturn modifiers == modifiers_;\n}\n\nstatic inline bool is_pressed(obs_key_t key)\n{\n\treturn obs_hotkeys_platform_is_pressed(obs->hotkeys.platform_context, key);\n}\n\nstatic inline void press_released_binding(obs_hotkey_binding_t *binding)\n{\n\tbinding->pressed = true;\n\n\tobs_hotkey_t *hotkey = binding->hotkey;\n\tif (hotkey->pressed++)\n\t\treturn;\n\n\tif (!obs->hotkeys.reroute_hotkeys)\n\t\thotkey->func(hotkey->data, hotkey->id, hotkey, true);\n\telse if (obs->hotkeys.router_func)\n\t\tobs->hotkeys.router_func(obs->hotkeys.router_func_data, hotkey->id, true);\n}\n\nstatic inline void release_pressed_binding(obs_hotkey_binding_t *binding)\n{\n\tbinding->pressed = false;\n\n\tobs_hotkey_t *hotkey = binding->hotkey;\n\tif (--hotkey->pressed)\n\t\treturn;\n\n\tif (!obs->hotkeys.reroute_hotkeys)\n\t\thotkey->func(hotkey->data, hotkey->id, hotkey, false);\n\telse if (obs->hotkeys.router_func)\n\t\tobs->hotkeys.router_func(obs->hotkeys.router_func_data, hotkey->id, false);\n}\n\nstatic inline void handle_binding(obs_hotkey_binding_t *binding, uint32_t modifiers, bool no_press,\n\t\t\t\t  bool strict_modifiers, bool *pressed)\n{\n\tbool modifiers_match_ = modifiers_match(binding, modifiers, strict_modifiers);\n\tbool modifiers_only = binding->key.key == OBS_KEY_NONE;\n\n\tif (!strict_modifiers && !binding->key.modifiers)\n\t\tbinding->modifiers_match = true;\n\n\tif (modifiers_only)\n\t\tpressed = &modifiers_only;\n\n\tif (!binding->key.modifiers && modifiers_only)\n\t\tgoto reset;\n\n\tif ((!binding->modifiers_match && !modifiers_only) || !modifiers_match_)\n\t\tgoto reset;\n\n\tif ((pressed && !*pressed) || (!pressed && !is_pressed(binding->key.key)))\n\t\tgoto reset;\n\n\tif (binding->pressed || no_press)\n\t\treturn;\n\n\tpress_released_binding(binding);\n\treturn;\n\nreset:\n\tbinding->modifiers_match = modifiers_match_;\n\tif (!binding->pressed)\n\t\treturn;\n\n\trelease_pressed_binding(binding);\n}\n\nstruct obs_hotkey_internal_inject {\n\tobs_key_combination_t hotkey;\n\tbool pressed;\n\tbool strict_modifiers;\n};\n\nstatic inline bool inject_hotkey(void *data, size_t idx, obs_hotkey_binding_t *binding)\n{\n\tUNUSED_PARAMETER(idx);\n\tstruct obs_hotkey_internal_inject *event = data;\n\n\tif (modifiers_match(binding, event->hotkey.modifiers, event->strict_modifiers)) {\n\t\tbool pressed = binding->key.key == event->hotkey.key && event->pressed;\n\t\tif (binding->key.key == OBS_KEY_NONE)\n\t\t\tpressed = true;\n\n\t\tif (pressed) {\n\t\t\tbinding->modifiers_match = true;\n\t\t\tif (!binding->pressed)\n\t\t\t\tpress_released_binding(binding);\n\t\t}\n\t}\n\n\treturn true;\n}\n\nvoid obs_hotkey_inject_event(obs_key_combination_t hotkey, bool pressed)\n{\n\tif (!lock())\n\t\treturn;\n\n\tstruct obs_hotkey_internal_inject event = {\n\t\t{hotkey.modifiers, hotkey.key},\n\t\tpressed,\n\t\tobs->hotkeys.strict_modifiers,\n\t};\n\tenum_bindings(inject_hotkey, &event);\n\tunlock();\n}\n\nvoid obs_hotkey_enable_background_press(bool enable)\n{\n\tif (!lock())\n\t\treturn;\n\n\tobs->hotkeys.thread_disable_press = !enable;\n\tunlock();\n}\n\nstruct obs_query_hotkeys_helper {\n\tuint32_t modifiers;\n\tbool no_press;\n\tbool strict_modifiers;\n};\n\nstatic inline bool query_hotkey(void *data, size_t idx, obs_hotkey_binding_t *binding)\n{\n\tUNUSED_PARAMETER(idx);\n\n\tstruct obs_query_hotkeys_helper *param = (struct obs_query_hotkeys_helper *)data;\n\thandle_binding(binding, param->modifiers, param->no_press, param->strict_modifiers, NULL);\n\n\treturn true;\n}\n\nstatic inline void query_hotkeys()\n{\n\tuint32_t modifiers = 0;\n\tif (is_pressed(OBS_KEY_SHIFT))\n\t\tmodifiers |= INTERACT_SHIFT_KEY;\n\tif (is_pressed(OBS_KEY_CONTROL))\n\t\tmodifiers |= INTERACT_CONTROL_KEY;\n\tif (is_pressed(OBS_KEY_ALT))\n\t\tmodifiers |= INTERACT_ALT_KEY;\n\tif (is_pressed(OBS_KEY_META))\n\t\tmodifiers |= INTERACT_COMMAND_KEY;\n\n\tstruct obs_query_hotkeys_helper param = {\n\t\tmodifiers,\n\t\tobs->hotkeys.thread_disable_press,\n\t\tobs->hotkeys.strict_modifiers,\n\t};\n\tenum_bindings(query_hotkey, &param);\n}\n\n#define NBSP \"\\xC2\\xA0\"\n\nvoid *obs_hotkey_thread(void *arg)\n{\n\tUNUSED_PARAMETER(arg);\n\n\tos_set_thread_name(\"libobs: hotkey thread\");\n\n\tconst char *hotkey_thread_name =\n\t\tprofile_store_name(obs_get_profiler_name_store(), \"obs_hotkey_thread(%g\" NBSP \"ms)\", 25.);\n\tprofile_register_root(hotkey_thread_name, (uint64_t)25000000);\n\n\twhile (os_event_timedwait(obs->hotkeys.stop_event, 25) == ETIMEDOUT) {\n\t\tif (!lock())\n\t\t\tcontinue;\n\n\t\tprofile_start(hotkey_thread_name);\n\t\tquery_hotkeys();\n\t\tprofile_end(hotkey_thread_name);\n\n\t\tunlock();\n\n\t\tprofile_reenable_thread();\n\t}\n\treturn NULL;\n}\n\nvoid obs_hotkey_trigger_routed_callback(obs_hotkey_id id, bool pressed)\n{\n\tif (!lock())\n\t\treturn;\n\n\tif (!obs->hotkeys.reroute_hotkeys)\n\t\tgoto unlock;\n\n\tobs_hotkey_t *hotkey;\n\tHASH_FIND_HKEY(obs->hotkeys.hotkeys, id, hotkey);\n\tif (!hotkey)\n\t\tgoto unlock;\n\n\thotkey->func(hotkey->data, id, hotkey, pressed);\n\nunlock:\n\tunlock();\n}\n\nvoid obs_hotkey_set_callback_routing_func(obs_hotkey_callback_router_func func, void *data)\n{\n\tif (!lock())\n\t\treturn;\n\n\tobs->hotkeys.router_func = func;\n\tobs->hotkeys.router_func_data = data;\n\tunlock();\n}\n\nvoid obs_hotkey_enable_callback_rerouting(bool enable)\n{\n\tif (!lock())\n\t\treturn;\n\n\tobs->hotkeys.reroute_hotkeys = enable;\n\tunlock();\n}\n\nstatic void obs_set_key_translation(obs_key_t key, const char *translation)\n{\n\tbfree(obs->hotkeys.translations[key]);\n\tobs->hotkeys.translations[key] = NULL;\n\n\tif (translation)\n\t\tobs->hotkeys.translations[key] = bstrdup(translation);\n}\n\nvoid obs_hotkeys_set_translations_s(struct obs_hotkeys_translations *translations, size_t size)\n{\n#define ADD_TRANSLATION(key_name, var_name) \\\n\tif (t.var_name)                     \\\n\t\tobs_set_key_translation(key_name, t.var_name);\n\n\tstruct obs_hotkeys_translations t = {0};\n\tstruct dstr numpad = {0};\n\tstruct dstr mouse = {0};\n\tstruct dstr button = {0};\n\n\tif (!translations) {\n\t\treturn;\n\t}\n\n\tmemcpy(&t, translations, (size < sizeof(t)) ? size : sizeof(t));\n\n\tADD_TRANSLATION(OBS_KEY_INSERT, insert);\n\tADD_TRANSLATION(OBS_KEY_DELETE, del);\n\tADD_TRANSLATION(OBS_KEY_HOME, home);\n\tADD_TRANSLATION(OBS_KEY_END, end);\n\tADD_TRANSLATION(OBS_KEY_PAGEUP, page_up);\n\tADD_TRANSLATION(OBS_KEY_PAGEDOWN, page_down);\n\tADD_TRANSLATION(OBS_KEY_NUMLOCK, num_lock);\n\tADD_TRANSLATION(OBS_KEY_SCROLLLOCK, scroll_lock);\n\tADD_TRANSLATION(OBS_KEY_CAPSLOCK, caps_lock);\n\tADD_TRANSLATION(OBS_KEY_BACKSPACE, backspace);\n\tADD_TRANSLATION(OBS_KEY_TAB, tab);\n\tADD_TRANSLATION(OBS_KEY_PRINT, print);\n\tADD_TRANSLATION(OBS_KEY_PAUSE, pause);\n\tADD_TRANSLATION(OBS_KEY_SHIFT, shift);\n\tADD_TRANSLATION(OBS_KEY_ALT, alt);\n\tADD_TRANSLATION(OBS_KEY_CONTROL, control);\n\tADD_TRANSLATION(OBS_KEY_META, meta);\n\tADD_TRANSLATION(OBS_KEY_MENU, menu);\n\tADD_TRANSLATION(OBS_KEY_SPACE, space);\n\tADD_TRANSLATION(OBS_KEY_ESCAPE, escape);\n#ifdef __APPLE__\n\tconst char *numpad_str = t.apple_keypad_num;\n\tADD_TRANSLATION(OBS_KEY_NUMSLASH, apple_keypad_divide);\n\tADD_TRANSLATION(OBS_KEY_NUMASTERISK, apple_keypad_multiply);\n\tADD_TRANSLATION(OBS_KEY_NUMMINUS, apple_keypad_minus);\n\tADD_TRANSLATION(OBS_KEY_NUMPLUS, apple_keypad_plus);\n\tADD_TRANSLATION(OBS_KEY_NUMPERIOD, apple_keypad_decimal);\n\tADD_TRANSLATION(OBS_KEY_NUMEQUAL, apple_keypad_equal);\n#else\n\tconst char *numpad_str = t.numpad_num;\n\tADD_TRANSLATION(OBS_KEY_NUMSLASH, numpad_divide);\n\tADD_TRANSLATION(OBS_KEY_NUMASTERISK, numpad_multiply);\n\tADD_TRANSLATION(OBS_KEY_NUMMINUS, numpad_minus);\n\tADD_TRANSLATION(OBS_KEY_NUMPLUS, numpad_plus);\n\tADD_TRANSLATION(OBS_KEY_NUMPERIOD, numpad_decimal);\n#endif\n\n\tif (numpad_str) {\n\t\tdstr_copy(&numpad, numpad_str);\n\t\tdstr_depad(&numpad);\n\n\t\tif (dstr_find(&numpad, \"%1\") == NULL) {\n\t\t\tdstr_cat(&numpad, \" %1\");\n\t\t}\n\n#define ADD_NUMPAD_NUM(idx)                \\\n\tdstr_copy_dstr(&button, &numpad);  \\\n\tdstr_replace(&button, \"%1\", #idx); \\\n\tobs_set_key_translation(OBS_KEY_NUM##idx, button.array)\n\n\t\tADD_NUMPAD_NUM(0);\n\t\tADD_NUMPAD_NUM(1);\n\t\tADD_NUMPAD_NUM(2);\n\t\tADD_NUMPAD_NUM(3);\n\t\tADD_NUMPAD_NUM(4);\n\t\tADD_NUMPAD_NUM(5);\n\t\tADD_NUMPAD_NUM(6);\n\t\tADD_NUMPAD_NUM(7);\n\t\tADD_NUMPAD_NUM(8);\n\t\tADD_NUMPAD_NUM(9);\n\t}\n\n\tif (t.mouse_num) {\n\t\tdstr_copy(&mouse, t.mouse_num);\n\t\tdstr_depad(&mouse);\n\n\t\tif (dstr_find(&mouse, \"%1\") == NULL) {\n\t\t\tdstr_cat(&mouse, \" %1\");\n\t\t}\n\n#define ADD_MOUSE_NUM(idx)                 \\\n\tdstr_copy_dstr(&button, &mouse);   \\\n\tdstr_replace(&button, \"%1\", #idx); \\\n\tobs_set_key_translation(OBS_KEY_MOUSE##idx, button.array)\n\n\t\tADD_MOUSE_NUM(1);\n\t\tADD_MOUSE_NUM(2);\n\t\tADD_MOUSE_NUM(3);\n\t\tADD_MOUSE_NUM(4);\n\t\tADD_MOUSE_NUM(5);\n\t\tADD_MOUSE_NUM(6);\n\t\tADD_MOUSE_NUM(7);\n\t\tADD_MOUSE_NUM(8);\n\t\tADD_MOUSE_NUM(9);\n\t\tADD_MOUSE_NUM(10);\n\t\tADD_MOUSE_NUM(11);\n\t\tADD_MOUSE_NUM(12);\n\t\tADD_MOUSE_NUM(13);\n\t\tADD_MOUSE_NUM(14);\n\t\tADD_MOUSE_NUM(15);\n\t\tADD_MOUSE_NUM(16);\n\t\tADD_MOUSE_NUM(17);\n\t\tADD_MOUSE_NUM(18);\n\t\tADD_MOUSE_NUM(19);\n\t\tADD_MOUSE_NUM(20);\n\t\tADD_MOUSE_NUM(21);\n\t\tADD_MOUSE_NUM(22);\n\t\tADD_MOUSE_NUM(23);\n\t\tADD_MOUSE_NUM(24);\n\t\tADD_MOUSE_NUM(25);\n\t\tADD_MOUSE_NUM(26);\n\t\tADD_MOUSE_NUM(27);\n\t\tADD_MOUSE_NUM(28);\n\t\tADD_MOUSE_NUM(29);\n\t}\n\n\tdstr_free(&numpad);\n\tdstr_free(&mouse);\n\tdstr_free(&button);\n}\n\nconst char *obs_get_hotkey_translation(obs_key_t key, const char *def)\n{\n\tif (key == OBS_KEY_NONE) {\n\t\treturn NULL;\n\t}\n\n\treturn obs->hotkeys.translations[key] ? obs->hotkeys.translations[key] : def;\n}\n\nvoid obs_hotkey_update_atomic(obs_hotkey_atomic_update_func func, void *data)\n{\n\tif (!lock())\n\t\treturn;\n\n\tfunc(data);\n\n\tunlock();\n}\n\nvoid obs_hotkeys_set_audio_hotkeys_translations(const char *mute, const char *unmute, const char *push_to_mute,\n\t\t\t\t\t\tconst char *push_to_talk)\n{\n#define SET_T(n)               \\\n\tbfree(obs->hotkeys.n); \\\n\tobs->hotkeys.n = bstrdup(n)\n\tSET_T(mute);\n\tSET_T(unmute);\n\tSET_T(push_to_mute);\n\tSET_T(push_to_talk);\n#undef SET_T\n}\n\nvoid obs_hotkeys_set_sceneitem_hotkeys_translations(const char *show, const char *hide)\n{\n#define SET_T(n)                           \\\n\tbfree(obs->hotkeys.sceneitem_##n); \\\n\tobs->hotkeys.sceneitem_##n = bstrdup(n)\n\tSET_T(show);\n\tSET_T(hide);\n#undef SET_T\n}\n"
  },
  {
    "path": "libobs/obs-hotkey.h",
    "content": "/******************************************************************************\n    Copyright (C) 2014-2015 by Ruwen Hahn <palana@stunned.de>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef size_t obs_hotkey_id;\ntypedef size_t obs_hotkey_pair_id;\n\n#ifndef SWIG\n#define OBS_INVALID_HOTKEY_ID (~(obs_hotkey_id)0)\n#define OBS_INVALID_HOTKEY_PAIR_ID (~(obs_hotkey_pair_id)0)\n#else\nconst size_t OBS_INVALID_HOTKEY_ID = (size_t)-1;\nconst size_t OBS_INVALID_HOTKEY_PAIR_ID = (size_t)-1;\n#endif\n\n#define XINPUT_MOUSE_LEN 33\n\nenum obs_key {\n#define OBS_HOTKEY(x) x,\n#include \"obs-hotkeys.h\"\n#undef OBS_HOTKEY\n\tOBS_KEY_LAST_VALUE //not an actual key\n};\ntypedef enum obs_key obs_key_t;\n\nstruct obs_key_combination {\n\tuint32_t modifiers;\n\tobs_key_t key;\n};\ntypedef struct obs_key_combination obs_key_combination_t;\n\ntypedef struct obs_hotkey obs_hotkey_t;\ntypedef struct obs_hotkey_binding obs_hotkey_binding_t;\n\nenum obs_hotkey_registerer_type {\n\tOBS_HOTKEY_REGISTERER_FRONTEND,\n\tOBS_HOTKEY_REGISTERER_SOURCE,\n\tOBS_HOTKEY_REGISTERER_OUTPUT,\n\tOBS_HOTKEY_REGISTERER_ENCODER,\n\tOBS_HOTKEY_REGISTERER_SERVICE,\n};\ntypedef enum obs_hotkey_registerer_type obs_hotkey_registerer_t;\n\n/* getter functions */\n\nEXPORT obs_hotkey_id obs_hotkey_get_id(const obs_hotkey_t *key);\nEXPORT const char *obs_hotkey_get_name(const obs_hotkey_t *key);\nEXPORT const char *obs_hotkey_get_description(const obs_hotkey_t *key);\nEXPORT obs_hotkey_registerer_t obs_hotkey_get_registerer_type(const obs_hotkey_t *key);\nEXPORT void *obs_hotkey_get_registerer(const obs_hotkey_t *key);\nEXPORT obs_hotkey_id obs_hotkey_get_pair_partner_id(const obs_hotkey_t *key);\n\nEXPORT obs_key_combination_t obs_hotkey_binding_get_key_combination(obs_hotkey_binding_t *binding);\nEXPORT obs_hotkey_id obs_hotkey_binding_get_hotkey_id(obs_hotkey_binding_t *binding);\nEXPORT obs_hotkey_t *obs_hotkey_binding_get_hotkey(obs_hotkey_binding_t *binding);\n\n/* setter functions */\n\nEXPORT void obs_hotkey_set_name(obs_hotkey_id id, const char *name);\nEXPORT void obs_hotkey_set_description(obs_hotkey_id id, const char *desc);\nEXPORT void obs_hotkey_pair_set_names(obs_hotkey_pair_id id, const char *name0, const char *name1);\nEXPORT void obs_hotkey_pair_set_descriptions(obs_hotkey_pair_id id, const char *desc0, const char *desc1);\n\n#ifndef SWIG\nstruct obs_hotkeys_translations {\n\tconst char *insert;\n\tconst char *del;\n\tconst char *home;\n\tconst char *end;\n\tconst char *page_up;\n\tconst char *page_down;\n\tconst char *num_lock;\n\tconst char *scroll_lock;\n\tconst char *caps_lock;\n\tconst char *backspace;\n\tconst char *tab;\n\tconst char *print;\n\tconst char *pause;\n\tconst char *left;\n\tconst char *right;\n\tconst char *up;\n\tconst char *down;\n\tconst char *shift;\n\tconst char *alt;\n\tconst char *control;\n\tconst char *meta; /* windows/super key */\n\tconst char *menu;\n\tconst char *space;\n\tconst char *numpad_num; /* For example, \"Numpad %1\" */\n\tconst char *numpad_divide;\n\tconst char *numpad_multiply;\n\tconst char *numpad_minus;\n\tconst char *numpad_plus;\n\tconst char *numpad_decimal;\n\tconst char *apple_keypad_num; /* For example, \"%1 (Keypad)\" */\n\tconst char *apple_keypad_divide;\n\tconst char *apple_keypad_multiply;\n\tconst char *apple_keypad_minus;\n\tconst char *apple_keypad_plus;\n\tconst char *apple_keypad_decimal;\n\tconst char *apple_keypad_equal;\n\tconst char *mouse_num; /* For example, \"Mouse %1\" */\n\tconst char *escape;\n};\n\n/* This function is an optional way to provide translations for specific keys\n * that may not have translations.  If the operating system can provide\n * translations for these keys, it will use the operating system's translation\n * over these translations.  If no translations are specified, it will use\n * the default English translations for that specific operating system. */\nEXPORT void obs_hotkeys_set_translations_s(struct obs_hotkeys_translations *translations, size_t size);\n#endif\n\n#define obs_hotkeys_set_translations(translations) \\\n\tobs_hotkeys_set_translations_s(translations, sizeof(struct obs_hotkeys_translations))\n\nEXPORT void obs_hotkeys_set_audio_hotkeys_translations(const char *mute, const char *unmute, const char *push_to_mute,\n\t\t\t\t\t\t       const char *push_to_talk);\n\nEXPORT void obs_hotkeys_set_sceneitem_hotkeys_translations(const char *show, const char *hide);\n\n/* registering hotkeys (giving hotkeys a name and a function) */\n\ntypedef void (*obs_hotkey_func)(void *data, obs_hotkey_id id, obs_hotkey_t *hotkey, bool pressed);\n\nEXPORT obs_hotkey_id obs_hotkey_register_frontend(const char *name, const char *description, obs_hotkey_func func,\n\t\t\t\t\t\t  void *data);\n\nEXPORT obs_hotkey_id obs_hotkey_register_encoder(obs_encoder_t *encoder, const char *name, const char *description,\n\t\t\t\t\t\t obs_hotkey_func func, void *data);\n\nEXPORT obs_hotkey_id obs_hotkey_register_output(obs_output_t *output, const char *name, const char *description,\n\t\t\t\t\t\tobs_hotkey_func func, void *data);\n\nEXPORT obs_hotkey_id obs_hotkey_register_service(obs_service_t *service, const char *name, const char *description,\n\t\t\t\t\t\t obs_hotkey_func func, void *data);\n\nEXPORT obs_hotkey_id obs_hotkey_register_source(obs_source_t *source, const char *name, const char *description,\n\t\t\t\t\t\tobs_hotkey_func func, void *data);\n\ntypedef bool (*obs_hotkey_active_func)(void *data, obs_hotkey_pair_id id, obs_hotkey_t *hotkey, bool pressed);\n\nEXPORT obs_hotkey_pair_id obs_hotkey_pair_register_frontend(const char *name0, const char *description0,\n\t\t\t\t\t\t\t    const char *name1, const char *description1,\n\t\t\t\t\t\t\t    obs_hotkey_active_func func0, obs_hotkey_active_func func1,\n\t\t\t\t\t\t\t    void *data0, void *data1);\n\nEXPORT obs_hotkey_pair_id obs_hotkey_pair_register_encoder(obs_encoder_t *encoder, const char *name0,\n\t\t\t\t\t\t\t   const char *description0, const char *name1,\n\t\t\t\t\t\t\t   const char *description1, obs_hotkey_active_func func0,\n\t\t\t\t\t\t\t   obs_hotkey_active_func func1, void *data0, void *data1);\n\nEXPORT obs_hotkey_pair_id obs_hotkey_pair_register_output(obs_output_t *output, const char *name0,\n\t\t\t\t\t\t\t  const char *description0, const char *name1,\n\t\t\t\t\t\t\t  const char *description1, obs_hotkey_active_func func0,\n\t\t\t\t\t\t\t  obs_hotkey_active_func func1, void *data0, void *data1);\n\nEXPORT obs_hotkey_pair_id obs_hotkey_pair_register_service(obs_service_t *service, const char *name0,\n\t\t\t\t\t\t\t   const char *description0, const char *name1,\n\t\t\t\t\t\t\t   const char *description1, obs_hotkey_active_func func0,\n\t\t\t\t\t\t\t   obs_hotkey_active_func func1, void *data0, void *data1);\n\nEXPORT obs_hotkey_pair_id obs_hotkey_pair_register_source(obs_source_t *source, const char *name0,\n\t\t\t\t\t\t\t  const char *description0, const char *name1,\n\t\t\t\t\t\t\t  const char *description1, obs_hotkey_active_func func0,\n\t\t\t\t\t\t\t  obs_hotkey_active_func func1, void *data0, void *data1);\n\nEXPORT void obs_hotkey_unregister(obs_hotkey_id id);\n\nEXPORT void obs_hotkey_pair_unregister(obs_hotkey_pair_id id);\n\n/* loading hotkeys (associating a hotkey with a physical key and modifiers) */\n\nEXPORT void obs_hotkey_load_bindings(obs_hotkey_id id, obs_key_combination_t *combinations, size_t num);\n\nEXPORT void obs_hotkey_load(obs_hotkey_id id, obs_data_array_t *data);\n\nEXPORT void obs_hotkeys_load_encoder(obs_encoder_t *encoder, obs_data_t *hotkeys);\n\nEXPORT void obs_hotkeys_load_output(obs_output_t *output, obs_data_t *hotkeys);\n\nEXPORT void obs_hotkeys_load_service(obs_service_t *service, obs_data_t *hotkeys);\n\nEXPORT void obs_hotkeys_load_source(obs_source_t *source, obs_data_t *hotkeys);\n\nEXPORT void obs_hotkey_pair_load(obs_hotkey_pair_id id, obs_data_array_t *data0, obs_data_array_t *data1);\n\nEXPORT obs_data_array_t *obs_hotkey_save(obs_hotkey_id id);\n\nEXPORT void obs_hotkey_pair_save(obs_hotkey_pair_id id, obs_data_array_t **p_data0, obs_data_array_t **p_data1);\n\nEXPORT obs_data_t *obs_hotkeys_save_encoder(obs_encoder_t *encoder);\n\nEXPORT obs_data_t *obs_hotkeys_save_output(obs_output_t *output);\n\nEXPORT obs_data_t *obs_hotkeys_save_service(obs_service_t *service);\n\nEXPORT obs_data_t *obs_hotkeys_save_source(obs_source_t *source);\n\n/* enumerating hotkeys */\n\ntypedef bool (*obs_hotkey_enum_func)(void *data, obs_hotkey_id id, obs_hotkey_t *key);\n\nEXPORT void obs_enum_hotkeys(obs_hotkey_enum_func func, void *data);\n\n/* enumerating bindings */\n\ntypedef bool (*obs_hotkey_binding_enum_func)(void *data, size_t idx, obs_hotkey_binding_t *binding);\n\nEXPORT void obs_enum_hotkey_bindings(obs_hotkey_binding_enum_func func, void *data);\n\n/* hotkey event control */\n\nEXPORT void obs_hotkey_inject_event(obs_key_combination_t hotkey, bool pressed);\n\nEXPORT void obs_hotkey_enable_background_press(bool enable);\n\n/* hotkey callback routing (trigger callbacks through e.g. a UI thread) */\n\ntypedef void (*obs_hotkey_callback_router_func)(void *data, obs_hotkey_id id, bool pressed);\n\nEXPORT void obs_hotkey_set_callback_routing_func(obs_hotkey_callback_router_func func, void *data);\n\nEXPORT void obs_hotkey_trigger_routed_callback(obs_hotkey_id id, bool pressed);\n\n/* hotkey callbacks won't be processed if callback rerouting is enabled and no\n * router func is set */\nEXPORT void obs_hotkey_enable_callback_rerouting(bool enable);\n\n/* misc */\n\ntypedef void (*obs_hotkey_atomic_update_func)(void *);\nEXPORT void obs_hotkey_update_atomic(obs_hotkey_atomic_update_func func, void *data);\n\nstruct dstr;\nEXPORT void obs_key_to_str(obs_key_t key, struct dstr *str);\nEXPORT void obs_key_combination_to_str(obs_key_combination_t key, struct dstr *str);\n\nEXPORT obs_key_t obs_key_from_virtual_key(int code);\nEXPORT int obs_key_to_virtual_key(obs_key_t key);\n\nEXPORT const char *obs_key_to_name(obs_key_t key);\nEXPORT obs_key_t obs_key_from_name(const char *name);\n\nstatic inline bool obs_key_combination_is_empty(obs_key_combination_t combo)\n{\n\treturn !combo.modifiers && combo.key == OBS_KEY_NONE;\n}\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-hotkeys.h",
    "content": "OBS_HOTKEY(OBS_KEY_NONE)\n\nOBS_HOTKEY(OBS_KEY_RETURN)\nOBS_HOTKEY(OBS_KEY_ENTER)\nOBS_HOTKEY(OBS_KEY_ESCAPE)\nOBS_HOTKEY(OBS_KEY_TAB)\nOBS_HOTKEY(OBS_KEY_BACKTAB)\nOBS_HOTKEY(OBS_KEY_BACKSPACE)\nOBS_HOTKEY(OBS_KEY_INSERT)\nOBS_HOTKEY(OBS_KEY_DELETE)\nOBS_HOTKEY(OBS_KEY_PAUSE)\nOBS_HOTKEY(OBS_KEY_PRINT)\nOBS_HOTKEY(OBS_KEY_SYSREQ)\nOBS_HOTKEY(OBS_KEY_CLEAR)\nOBS_HOTKEY(OBS_KEY_HOME)\nOBS_HOTKEY(OBS_KEY_END)\nOBS_HOTKEY(OBS_KEY_LEFT)\nOBS_HOTKEY(OBS_KEY_UP)\nOBS_HOTKEY(OBS_KEY_RIGHT)\nOBS_HOTKEY(OBS_KEY_DOWN)\nOBS_HOTKEY(OBS_KEY_PAGEUP)\nOBS_HOTKEY(OBS_KEY_PAGEDOWN)\n\nOBS_HOTKEY(OBS_KEY_SHIFT)\nOBS_HOTKEY(OBS_KEY_CONTROL)\nOBS_HOTKEY(OBS_KEY_META)\nOBS_HOTKEY(OBS_KEY_ALT)\nOBS_HOTKEY(OBS_KEY_ALTGR)\nOBS_HOTKEY(OBS_KEY_CAPSLOCK)\nOBS_HOTKEY(OBS_KEY_NUMLOCK)\nOBS_HOTKEY(OBS_KEY_SCROLLLOCK)\n\nOBS_HOTKEY(OBS_KEY_F1)\nOBS_HOTKEY(OBS_KEY_F2)\nOBS_HOTKEY(OBS_KEY_F3)\nOBS_HOTKEY(OBS_KEY_F4)\nOBS_HOTKEY(OBS_KEY_F5)\nOBS_HOTKEY(OBS_KEY_F6)\nOBS_HOTKEY(OBS_KEY_F7)\nOBS_HOTKEY(OBS_KEY_F8)\nOBS_HOTKEY(OBS_KEY_F9)\nOBS_HOTKEY(OBS_KEY_F10)\nOBS_HOTKEY(OBS_KEY_F11)\nOBS_HOTKEY(OBS_KEY_F12)\nOBS_HOTKEY(OBS_KEY_F13)\nOBS_HOTKEY(OBS_KEY_F14)\nOBS_HOTKEY(OBS_KEY_F15)\nOBS_HOTKEY(OBS_KEY_F16)\nOBS_HOTKEY(OBS_KEY_F17)\nOBS_HOTKEY(OBS_KEY_F18)\nOBS_HOTKEY(OBS_KEY_F19)\nOBS_HOTKEY(OBS_KEY_F20)\nOBS_HOTKEY(OBS_KEY_F21)\nOBS_HOTKEY(OBS_KEY_F22)\nOBS_HOTKEY(OBS_KEY_F23)\nOBS_HOTKEY(OBS_KEY_F24)\nOBS_HOTKEY(OBS_KEY_F25)\nOBS_HOTKEY(OBS_KEY_F26)\nOBS_HOTKEY(OBS_KEY_F27)\nOBS_HOTKEY(OBS_KEY_F28)\nOBS_HOTKEY(OBS_KEY_F29)\nOBS_HOTKEY(OBS_KEY_F30)\nOBS_HOTKEY(OBS_KEY_F31)\nOBS_HOTKEY(OBS_KEY_F32)\nOBS_HOTKEY(OBS_KEY_F33)\nOBS_HOTKEY(OBS_KEY_F34)\nOBS_HOTKEY(OBS_KEY_F35)\n\nOBS_HOTKEY(OBS_KEY_MENU)\nOBS_HOTKEY(OBS_KEY_HYPER_L)\nOBS_HOTKEY(OBS_KEY_HYPER_R)\nOBS_HOTKEY(OBS_KEY_HELP)\nOBS_HOTKEY(OBS_KEY_DIRECTION_L)\nOBS_HOTKEY(OBS_KEY_DIRECTION_R)\n\nOBS_HOTKEY(OBS_KEY_SPACE)\nOBS_HOTKEY(OBS_KEY_EXCLAM)\nOBS_HOTKEY(OBS_KEY_QUOTEDBL)\nOBS_HOTKEY(OBS_KEY_NUMBERSIGN)\nOBS_HOTKEY(OBS_KEY_DOLLAR)\nOBS_HOTKEY(OBS_KEY_PERCENT)\nOBS_HOTKEY(OBS_KEY_AMPERSAND)\nOBS_HOTKEY(OBS_KEY_APOSTROPHE)\nOBS_HOTKEY(OBS_KEY_PARENLEFT)\nOBS_HOTKEY(OBS_KEY_PARENRIGHT)\nOBS_HOTKEY(OBS_KEY_ASTERISK)\nOBS_HOTKEY(OBS_KEY_PLUS)\nOBS_HOTKEY(OBS_KEY_COMMA)\nOBS_HOTKEY(OBS_KEY_MINUS)\nOBS_HOTKEY(OBS_KEY_PERIOD)\nOBS_HOTKEY(OBS_KEY_SLASH)\nOBS_HOTKEY(OBS_KEY_0)\nOBS_HOTKEY(OBS_KEY_1)\nOBS_HOTKEY(OBS_KEY_2)\nOBS_HOTKEY(OBS_KEY_3)\nOBS_HOTKEY(OBS_KEY_4)\nOBS_HOTKEY(OBS_KEY_5)\nOBS_HOTKEY(OBS_KEY_6)\nOBS_HOTKEY(OBS_KEY_7)\nOBS_HOTKEY(OBS_KEY_8)\nOBS_HOTKEY(OBS_KEY_9)\nOBS_HOTKEY(OBS_KEY_NUMEQUAL)\nOBS_HOTKEY(OBS_KEY_NUMASTERISK)\nOBS_HOTKEY(OBS_KEY_NUMPLUS)\nOBS_HOTKEY(OBS_KEY_NUMCOMMA)\nOBS_HOTKEY(OBS_KEY_NUMMINUS)\nOBS_HOTKEY(OBS_KEY_NUMPERIOD)\nOBS_HOTKEY(OBS_KEY_NUMSLASH)\nOBS_HOTKEY(OBS_KEY_NUM0)\nOBS_HOTKEY(OBS_KEY_NUM1)\nOBS_HOTKEY(OBS_KEY_NUM2)\nOBS_HOTKEY(OBS_KEY_NUM3)\nOBS_HOTKEY(OBS_KEY_NUM4)\nOBS_HOTKEY(OBS_KEY_NUM5)\nOBS_HOTKEY(OBS_KEY_NUM6)\nOBS_HOTKEY(OBS_KEY_NUM7)\nOBS_HOTKEY(OBS_KEY_NUM8)\nOBS_HOTKEY(OBS_KEY_NUM9)\nOBS_HOTKEY(OBS_KEY_COLON)\nOBS_HOTKEY(OBS_KEY_SEMICOLON)\nOBS_HOTKEY(OBS_KEY_QUOTE)\nOBS_HOTKEY(OBS_KEY_LESS)\nOBS_HOTKEY(OBS_KEY_EQUAL)\nOBS_HOTKEY(OBS_KEY_GREATER)\nOBS_HOTKEY(OBS_KEY_QUESTION)\nOBS_HOTKEY(OBS_KEY_AT)\nOBS_HOTKEY(OBS_KEY_A)\nOBS_HOTKEY(OBS_KEY_B)\nOBS_HOTKEY(OBS_KEY_C)\nOBS_HOTKEY(OBS_KEY_D)\nOBS_HOTKEY(OBS_KEY_E)\nOBS_HOTKEY(OBS_KEY_F)\nOBS_HOTKEY(OBS_KEY_G)\nOBS_HOTKEY(OBS_KEY_H)\nOBS_HOTKEY(OBS_KEY_I)\nOBS_HOTKEY(OBS_KEY_J)\nOBS_HOTKEY(OBS_KEY_K)\nOBS_HOTKEY(OBS_KEY_L)\nOBS_HOTKEY(OBS_KEY_M)\nOBS_HOTKEY(OBS_KEY_N)\nOBS_HOTKEY(OBS_KEY_O)\nOBS_HOTKEY(OBS_KEY_P)\nOBS_HOTKEY(OBS_KEY_Q)\nOBS_HOTKEY(OBS_KEY_R)\nOBS_HOTKEY(OBS_KEY_S)\nOBS_HOTKEY(OBS_KEY_T)\nOBS_HOTKEY(OBS_KEY_U)\nOBS_HOTKEY(OBS_KEY_V)\nOBS_HOTKEY(OBS_KEY_W)\nOBS_HOTKEY(OBS_KEY_X)\nOBS_HOTKEY(OBS_KEY_Y)\nOBS_HOTKEY(OBS_KEY_Z)\nOBS_HOTKEY(OBS_KEY_BRACKETLEFT)\nOBS_HOTKEY(OBS_KEY_BACKSLASH)\nOBS_HOTKEY(OBS_KEY_BRACKETRIGHT)\nOBS_HOTKEY(OBS_KEY_ASCIICIRCUM)\nOBS_HOTKEY(OBS_KEY_UNDERSCORE)\nOBS_HOTKEY(OBS_KEY_QUOTELEFT)\nOBS_HOTKEY(OBS_KEY_BRACELEFT)\nOBS_HOTKEY(OBS_KEY_BAR)\nOBS_HOTKEY(OBS_KEY_BRACERIGHT)\nOBS_HOTKEY(OBS_KEY_ASCIITILDE)\nOBS_HOTKEY(OBS_KEY_NOBREAKSPACE)\nOBS_HOTKEY(OBS_KEY_EXCLAMDOWN)\nOBS_HOTKEY(OBS_KEY_CENT)\nOBS_HOTKEY(OBS_KEY_STERLING)\nOBS_HOTKEY(OBS_KEY_CURRENCY)\nOBS_HOTKEY(OBS_KEY_YEN)\nOBS_HOTKEY(OBS_KEY_BROKENBAR)\nOBS_HOTKEY(OBS_KEY_SECTION)\nOBS_HOTKEY(OBS_KEY_DIAERESIS)\nOBS_HOTKEY(OBS_KEY_COPYRIGHT)\nOBS_HOTKEY(OBS_KEY_ORDFEMININE)\nOBS_HOTKEY(OBS_KEY_GUILLEMOTLEFT)\nOBS_HOTKEY(OBS_KEY_NOTSIGN)\nOBS_HOTKEY(OBS_KEY_HYPHEN)\nOBS_HOTKEY(OBS_KEY_REGISTERED)\nOBS_HOTKEY(OBS_KEY_MACRON)\nOBS_HOTKEY(OBS_KEY_DEGREE)\nOBS_HOTKEY(OBS_KEY_PLUSMINUS)\nOBS_HOTKEY(OBS_KEY_TWOSUPERIOR)\nOBS_HOTKEY(OBS_KEY_THREESUPERIOR)\nOBS_HOTKEY(OBS_KEY_ACUTE)\nOBS_HOTKEY(OBS_KEY_MU)\nOBS_HOTKEY(OBS_KEY_PARAGRAPH)\nOBS_HOTKEY(OBS_KEY_PERIODCENTERED)\nOBS_HOTKEY(OBS_KEY_CEDILLA)\nOBS_HOTKEY(OBS_KEY_ONESUPERIOR)\nOBS_HOTKEY(OBS_KEY_MASCULINE)\nOBS_HOTKEY(OBS_KEY_GUILLEMOTRIGHT)\nOBS_HOTKEY(OBS_KEY_ONEQUARTER)\nOBS_HOTKEY(OBS_KEY_ONEHALF)\nOBS_HOTKEY(OBS_KEY_THREEQUARTERS)\nOBS_HOTKEY(OBS_KEY_QUESTIONDOWN)\nOBS_HOTKEY(OBS_KEY_AGRAVE)\nOBS_HOTKEY(OBS_KEY_AACUTE)\nOBS_HOTKEY(OBS_KEY_ACIRCUMFLEX)\nOBS_HOTKEY(OBS_KEY_ATILDE)\nOBS_HOTKEY(OBS_KEY_ADIAERESIS)\nOBS_HOTKEY(OBS_KEY_ARING)\nOBS_HOTKEY(OBS_KEY_AE)\nOBS_HOTKEY(OBS_KEY_CCEDILLA)\nOBS_HOTKEY(OBS_KEY_EGRAVE)\nOBS_HOTKEY(OBS_KEY_EACUTE)\nOBS_HOTKEY(OBS_KEY_ECIRCUMFLEX)\nOBS_HOTKEY(OBS_KEY_EDIAERESIS)\nOBS_HOTKEY(OBS_KEY_IGRAVE)\nOBS_HOTKEY(OBS_KEY_IACUTE)\nOBS_HOTKEY(OBS_KEY_ICIRCUMFLEX)\nOBS_HOTKEY(OBS_KEY_IDIAERESIS)\nOBS_HOTKEY(OBS_KEY_ETH)\nOBS_HOTKEY(OBS_KEY_NTILDE)\nOBS_HOTKEY(OBS_KEY_OGRAVE)\nOBS_HOTKEY(OBS_KEY_OACUTE)\nOBS_HOTKEY(OBS_KEY_OCIRCUMFLEX)\nOBS_HOTKEY(OBS_KEY_OTILDE)\nOBS_HOTKEY(OBS_KEY_ODIAERESIS)\nOBS_HOTKEY(OBS_KEY_MULTIPLY)\nOBS_HOTKEY(OBS_KEY_OOBLIQUE)\nOBS_HOTKEY(OBS_KEY_UGRAVE)\nOBS_HOTKEY(OBS_KEY_UACUTE)\nOBS_HOTKEY(OBS_KEY_UCIRCUMFLEX)\nOBS_HOTKEY(OBS_KEY_UDIAERESIS)\nOBS_HOTKEY(OBS_KEY_YACUTE)\nOBS_HOTKEY(OBS_KEY_THORN)\nOBS_HOTKEY(OBS_KEY_SSHARP)\nOBS_HOTKEY(OBS_KEY_DIVISION)\nOBS_HOTKEY(OBS_KEY_YDIAERESIS)\nOBS_HOTKEY(OBS_KEY_MULTI_KEY)\nOBS_HOTKEY(OBS_KEY_CODEINPUT)\nOBS_HOTKEY(OBS_KEY_SINGLECANDIDATE)\nOBS_HOTKEY(OBS_KEY_MULTIPLECANDIDATE)\nOBS_HOTKEY(OBS_KEY_PREVIOUSCANDIDATE)\nOBS_HOTKEY(OBS_KEY_MODE_SWITCH)\nOBS_HOTKEY(OBS_KEY_KANJI)\nOBS_HOTKEY(OBS_KEY_MUHENKAN)\nOBS_HOTKEY(OBS_KEY_HENKAN)\nOBS_HOTKEY(OBS_KEY_ROMAJI)\nOBS_HOTKEY(OBS_KEY_HIRAGANA)\nOBS_HOTKEY(OBS_KEY_KATAKANA)\nOBS_HOTKEY(OBS_KEY_HIRAGANA_KATAKANA)\nOBS_HOTKEY(OBS_KEY_ZENKAKU)\nOBS_HOTKEY(OBS_KEY_HANKAKU)\nOBS_HOTKEY(OBS_KEY_ZENKAKU_HANKAKU)\nOBS_HOTKEY(OBS_KEY_TOUROKU)\nOBS_HOTKEY(OBS_KEY_MASSYO)\nOBS_HOTKEY(OBS_KEY_KANA_LOCK)\nOBS_HOTKEY(OBS_KEY_KANA_SHIFT)\nOBS_HOTKEY(OBS_KEY_EISU_SHIFT)\nOBS_HOTKEY(OBS_KEY_EISU_TOGGLE)\nOBS_HOTKEY(OBS_KEY_HANGUL)\nOBS_HOTKEY(OBS_KEY_HANGUL_START)\nOBS_HOTKEY(OBS_KEY_HANGUL_END)\nOBS_HOTKEY(OBS_KEY_HANGUL_HANJA)\nOBS_HOTKEY(OBS_KEY_HANGUL_JAMO)\nOBS_HOTKEY(OBS_KEY_HANGUL_ROMAJA)\nOBS_HOTKEY(OBS_KEY_HANGUL_JEONJA)\nOBS_HOTKEY(OBS_KEY_HANGUL_BANJA)\nOBS_HOTKEY(OBS_KEY_HANGUL_PREHANJA)\nOBS_HOTKEY(OBS_KEY_HANGUL_POSTHANJA)\nOBS_HOTKEY(OBS_KEY_HANGUL_SPECIAL)\nOBS_HOTKEY(OBS_KEY_DEAD_GRAVE)\nOBS_HOTKEY(OBS_KEY_DEAD_ACUTE)\nOBS_HOTKEY(OBS_KEY_DEAD_CIRCUMFLEX)\nOBS_HOTKEY(OBS_KEY_DEAD_TILDE)\nOBS_HOTKEY(OBS_KEY_DEAD_MACRON)\nOBS_HOTKEY(OBS_KEY_DEAD_BREVE)\nOBS_HOTKEY(OBS_KEY_DEAD_ABOVEDOT)\nOBS_HOTKEY(OBS_KEY_DEAD_DIAERESIS)\nOBS_HOTKEY(OBS_KEY_DEAD_ABOVERING)\nOBS_HOTKEY(OBS_KEY_DEAD_DOUBLEACUTE)\nOBS_HOTKEY(OBS_KEY_DEAD_CARON)\nOBS_HOTKEY(OBS_KEY_DEAD_CEDILLA)\nOBS_HOTKEY(OBS_KEY_DEAD_OGONEK)\nOBS_HOTKEY(OBS_KEY_DEAD_IOTA)\nOBS_HOTKEY(OBS_KEY_DEAD_VOICED_SOUND)\nOBS_HOTKEY(OBS_KEY_DEAD_SEMIVOICED_SOUND)\nOBS_HOTKEY(OBS_KEY_DEAD_BELOWDOT)\nOBS_HOTKEY(OBS_KEY_DEAD_HOOK)\nOBS_HOTKEY(OBS_KEY_DEAD_HORN)\nOBS_HOTKEY(OBS_KEY_BACK)\nOBS_HOTKEY(OBS_KEY_FORWARD)\nOBS_HOTKEY(OBS_KEY_STOP)\nOBS_HOTKEY(OBS_KEY_REFRESH)\nOBS_HOTKEY(OBS_KEY_VOLUMEDOWN)\nOBS_HOTKEY(OBS_KEY_VOLUMEMUTE)\nOBS_HOTKEY(OBS_KEY_VOLUMEUP)\nOBS_HOTKEY(OBS_KEY_BASSBOOST)\nOBS_HOTKEY(OBS_KEY_BASSUP)\nOBS_HOTKEY(OBS_KEY_BASSDOWN)\nOBS_HOTKEY(OBS_KEY_TREBLEUP)\nOBS_HOTKEY(OBS_KEY_TREBLEDOWN)\nOBS_HOTKEY(OBS_KEY_MEDIAPLAY)\nOBS_HOTKEY(OBS_KEY_MEDIASTOP)\nOBS_HOTKEY(OBS_KEY_MEDIAPREVIOUS)\nOBS_HOTKEY(OBS_KEY_MEDIANEXT)\nOBS_HOTKEY(OBS_KEY_MEDIARECORD)\nOBS_HOTKEY(OBS_KEY_MEDIAPAUSE)\nOBS_HOTKEY(OBS_KEY_MEDIATOGGLEPLAYPAUSE)\nOBS_HOTKEY(OBS_KEY_HOMEPAGE)\nOBS_HOTKEY(OBS_KEY_FAVORITES)\nOBS_HOTKEY(OBS_KEY_SEARCH)\nOBS_HOTKEY(OBS_KEY_STANDBY)\nOBS_HOTKEY(OBS_KEY_OPENURL)\nOBS_HOTKEY(OBS_KEY_LAUNCHMAIL)\nOBS_HOTKEY(OBS_KEY_LAUNCHMEDIA)\nOBS_HOTKEY(OBS_KEY_LAUNCH0)\nOBS_HOTKEY(OBS_KEY_LAUNCH1)\nOBS_HOTKEY(OBS_KEY_LAUNCH2)\nOBS_HOTKEY(OBS_KEY_LAUNCH3)\nOBS_HOTKEY(OBS_KEY_LAUNCH4)\nOBS_HOTKEY(OBS_KEY_LAUNCH5)\nOBS_HOTKEY(OBS_KEY_LAUNCH6)\nOBS_HOTKEY(OBS_KEY_LAUNCH7)\nOBS_HOTKEY(OBS_KEY_LAUNCH8)\nOBS_HOTKEY(OBS_KEY_LAUNCH9)\nOBS_HOTKEY(OBS_KEY_LAUNCHA)\nOBS_HOTKEY(OBS_KEY_LAUNCHB)\nOBS_HOTKEY(OBS_KEY_LAUNCHC)\nOBS_HOTKEY(OBS_KEY_LAUNCHD)\nOBS_HOTKEY(OBS_KEY_LAUNCHE)\nOBS_HOTKEY(OBS_KEY_LAUNCHF)\nOBS_HOTKEY(OBS_KEY_LAUNCHG)\nOBS_HOTKEY(OBS_KEY_LAUNCHH)\nOBS_HOTKEY(OBS_KEY_MONBRIGHTNESSUP)\nOBS_HOTKEY(OBS_KEY_MONBRIGHTNESSDOWN)\nOBS_HOTKEY(OBS_KEY_KEYBOARDLIGHTONOFF)\nOBS_HOTKEY(OBS_KEY_KEYBOARDBRIGHTNESSUP)\nOBS_HOTKEY(OBS_KEY_KEYBOARDBRIGHTNESSDOWN)\nOBS_HOTKEY(OBS_KEY_POWEROFF)\nOBS_HOTKEY(OBS_KEY_WAKEUP)\nOBS_HOTKEY(OBS_KEY_EJECT)\nOBS_HOTKEY(OBS_KEY_SCREENSAVER)\nOBS_HOTKEY(OBS_KEY_WWW)\nOBS_HOTKEY(OBS_KEY_MEMO)\nOBS_HOTKEY(OBS_KEY_LIGHTBULB)\nOBS_HOTKEY(OBS_KEY_SHOP)\nOBS_HOTKEY(OBS_KEY_HISTORY)\nOBS_HOTKEY(OBS_KEY_ADDFAVORITE)\nOBS_HOTKEY(OBS_KEY_HOTLINKS)\nOBS_HOTKEY(OBS_KEY_BRIGHTNESSADJUST)\nOBS_HOTKEY(OBS_KEY_FINANCE)\nOBS_HOTKEY(OBS_KEY_COMMUNITY)\nOBS_HOTKEY(OBS_KEY_AUDIOREWIND)\nOBS_HOTKEY(OBS_KEY_BACKFORWARD)\nOBS_HOTKEY(OBS_KEY_APPLICATIONLEFT)\nOBS_HOTKEY(OBS_KEY_APPLICATIONRIGHT)\nOBS_HOTKEY(OBS_KEY_BOOK)\nOBS_HOTKEY(OBS_KEY_CD)\nOBS_HOTKEY(OBS_KEY_CALCULATOR)\nOBS_HOTKEY(OBS_KEY_TODOLIST)\nOBS_HOTKEY(OBS_KEY_CLEARGRAB)\nOBS_HOTKEY(OBS_KEY_CLOSE)\nOBS_HOTKEY(OBS_KEY_COPY)\nOBS_HOTKEY(OBS_KEY_CUT)\nOBS_HOTKEY(OBS_KEY_DISPLAY)\nOBS_HOTKEY(OBS_KEY_DOS)\nOBS_HOTKEY(OBS_KEY_DOCUMENTS)\nOBS_HOTKEY(OBS_KEY_EXCEL)\nOBS_HOTKEY(OBS_KEY_EXPLORER)\nOBS_HOTKEY(OBS_KEY_GAME)\nOBS_HOTKEY(OBS_KEY_GO)\nOBS_HOTKEY(OBS_KEY_ITOUCH)\nOBS_HOTKEY(OBS_KEY_LOGOFF)\nOBS_HOTKEY(OBS_KEY_MARKET)\nOBS_HOTKEY(OBS_KEY_MEETING)\nOBS_HOTKEY(OBS_KEY_MENUKB)\nOBS_HOTKEY(OBS_KEY_MENUPB)\nOBS_HOTKEY(OBS_KEY_MYSITES)\nOBS_HOTKEY(OBS_KEY_NEWS)\nOBS_HOTKEY(OBS_KEY_OFFICEHOME)\nOBS_HOTKEY(OBS_KEY_OPTION)\nOBS_HOTKEY(OBS_KEY_PASTE)\nOBS_HOTKEY(OBS_KEY_PHONE)\nOBS_HOTKEY(OBS_KEY_CALENDAR)\nOBS_HOTKEY(OBS_KEY_REPLY)\nOBS_HOTKEY(OBS_KEY_RELOAD)\nOBS_HOTKEY(OBS_KEY_ROTATEWINDOWS)\nOBS_HOTKEY(OBS_KEY_ROTATIONPB)\nOBS_HOTKEY(OBS_KEY_ROTATIONKB)\nOBS_HOTKEY(OBS_KEY_SAVE)\nOBS_HOTKEY(OBS_KEY_SEND)\nOBS_HOTKEY(OBS_KEY_SPELL)\nOBS_HOTKEY(OBS_KEY_SPLITSCREEN)\nOBS_HOTKEY(OBS_KEY_SUPPORT)\nOBS_HOTKEY(OBS_KEY_TASKPANE)\nOBS_HOTKEY(OBS_KEY_TERMINAL)\nOBS_HOTKEY(OBS_KEY_TOOLS)\nOBS_HOTKEY(OBS_KEY_TRAVEL)\nOBS_HOTKEY(OBS_KEY_VIDEO)\nOBS_HOTKEY(OBS_KEY_WORD)\nOBS_HOTKEY(OBS_KEY_XFER)\nOBS_HOTKEY(OBS_KEY_ZOOMIN)\nOBS_HOTKEY(OBS_KEY_ZOOMOUT)\nOBS_HOTKEY(OBS_KEY_AWAY)\nOBS_HOTKEY(OBS_KEY_MESSENGER)\nOBS_HOTKEY(OBS_KEY_WEBCAM)\nOBS_HOTKEY(OBS_KEY_MAILFORWARD)\nOBS_HOTKEY(OBS_KEY_PICTURES)\nOBS_HOTKEY(OBS_KEY_MUSIC)\nOBS_HOTKEY(OBS_KEY_BATTERY)\nOBS_HOTKEY(OBS_KEY_BLUETOOTH)\nOBS_HOTKEY(OBS_KEY_WLAN)\nOBS_HOTKEY(OBS_KEY_UWB)\nOBS_HOTKEY(OBS_KEY_AUDIOFORWARD)\nOBS_HOTKEY(OBS_KEY_AUDIOREPEAT)\nOBS_HOTKEY(OBS_KEY_AUDIORANDOMPLAY)\nOBS_HOTKEY(OBS_KEY_SUBTITLE)\nOBS_HOTKEY(OBS_KEY_AUDIOCYCLETRACK)\nOBS_HOTKEY(OBS_KEY_TIME)\nOBS_HOTKEY(OBS_KEY_HIBERNATE)\nOBS_HOTKEY(OBS_KEY_VIEW)\nOBS_HOTKEY(OBS_KEY_TOPMENU)\nOBS_HOTKEY(OBS_KEY_POWERDOWN)\nOBS_HOTKEY(OBS_KEY_SUSPEND)\nOBS_HOTKEY(OBS_KEY_CONTRASTADJUST)\nOBS_HOTKEY(OBS_KEY_MEDIALAST)\nOBS_HOTKEY(OBS_KEY_CALL)\nOBS_HOTKEY(OBS_KEY_CAMERA)\nOBS_HOTKEY(OBS_KEY_CAMERAFOCUS)\nOBS_HOTKEY(OBS_KEY_CONTEXT1)\nOBS_HOTKEY(OBS_KEY_CONTEXT2)\nOBS_HOTKEY(OBS_KEY_CONTEXT3)\nOBS_HOTKEY(OBS_KEY_CONTEXT4)\nOBS_HOTKEY(OBS_KEY_FLIP)\nOBS_HOTKEY(OBS_KEY_HANGUP)\nOBS_HOTKEY(OBS_KEY_NO)\nOBS_HOTKEY(OBS_KEY_SELECT)\nOBS_HOTKEY(OBS_KEY_YES)\nOBS_HOTKEY(OBS_KEY_TOGGLECALLHANGUP)\nOBS_HOTKEY(OBS_KEY_VOICEDIAL)\nOBS_HOTKEY(OBS_KEY_LASTNUMBERREDIAL)\nOBS_HOTKEY(OBS_KEY_EXECUTE)\nOBS_HOTKEY(OBS_KEY_PRINTER)\nOBS_HOTKEY(OBS_KEY_PLAY)\nOBS_HOTKEY(OBS_KEY_SLEEP)\nOBS_HOTKEY(OBS_KEY_ZOOM)\nOBS_HOTKEY(OBS_KEY_CANCEL)\n\n#ifndef OBS_MOUSE_BUTTON\n#define OBS_MOUSE_BUTTON(x) OBS_HOTKEY(x)\n#define OBS_MOUSE_BUTTON_DEFAULT 1\n#endif\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE1)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE2)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE3)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE4)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE5)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE6)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE7)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE8)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE9)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE10)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE11)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE12)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE13)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE14)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE15)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE16)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE17)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE18)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE19)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE20)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE21)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE22)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE23)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE24)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE25)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE26)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE27)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE28)\nOBS_MOUSE_BUTTON(OBS_KEY_MOUSE29)\n#ifdef OBS_MOUSE_BUTTON_DEFAULT\n#undef OBS_MOUSE_BUTTON\n#undef OBS_MOUSE_BUTTON_DEFAULT\n#endif\n\nOBS_HOTKEY(OBS_KEY_BACKSLASH_RT102)\n\nOBS_HOTKEY(OBS_KEY_OPEN)\nOBS_HOTKEY(OBS_KEY_FIND)\nOBS_HOTKEY(OBS_KEY_REDO)\nOBS_HOTKEY(OBS_KEY_UNDO)\nOBS_HOTKEY(OBS_KEY_FRONT)\nOBS_HOTKEY(OBS_KEY_PROPS)\n\nOBS_HOTKEY(OBS_KEY_VK_CANCEL)\nOBS_HOTKEY(OBS_KEY_0x07)\nOBS_HOTKEY(OBS_KEY_0x0A)\nOBS_HOTKEY(OBS_KEY_0x0B)\nOBS_HOTKEY(OBS_KEY_0x0E)\nOBS_HOTKEY(OBS_KEY_0x0F)\nOBS_HOTKEY(OBS_KEY_0x16)\nOBS_HOTKEY(OBS_KEY_VK_JUNJA)\nOBS_HOTKEY(OBS_KEY_VK_FINAL)\nOBS_HOTKEY(OBS_KEY_0x1A)\nOBS_HOTKEY(OBS_KEY_VK_ACCEPT)\nOBS_HOTKEY(OBS_KEY_VK_MODECHANGE)\nOBS_HOTKEY(OBS_KEY_VK_SELECT)\nOBS_HOTKEY(OBS_KEY_VK_PRINT)\nOBS_HOTKEY(OBS_KEY_VK_EXECUTE)\nOBS_HOTKEY(OBS_KEY_VK_HELP)\nOBS_HOTKEY(OBS_KEY_0x30)\nOBS_HOTKEY(OBS_KEY_0x31)\nOBS_HOTKEY(OBS_KEY_0x32)\nOBS_HOTKEY(OBS_KEY_0x33)\nOBS_HOTKEY(OBS_KEY_0x34)\nOBS_HOTKEY(OBS_KEY_0x35)\nOBS_HOTKEY(OBS_KEY_0x36)\nOBS_HOTKEY(OBS_KEY_0x37)\nOBS_HOTKEY(OBS_KEY_0x38)\nOBS_HOTKEY(OBS_KEY_0x39)\nOBS_HOTKEY(OBS_KEY_0x3A)\nOBS_HOTKEY(OBS_KEY_0x3B)\nOBS_HOTKEY(OBS_KEY_0x3C)\nOBS_HOTKEY(OBS_KEY_0x3D)\nOBS_HOTKEY(OBS_KEY_0x3E)\nOBS_HOTKEY(OBS_KEY_0x3F)\nOBS_HOTKEY(OBS_KEY_0x40)\nOBS_HOTKEY(OBS_KEY_0x41)\nOBS_HOTKEY(OBS_KEY_0x42)\nOBS_HOTKEY(OBS_KEY_0x43)\nOBS_HOTKEY(OBS_KEY_0x44)\nOBS_HOTKEY(OBS_KEY_0x45)\nOBS_HOTKEY(OBS_KEY_0x46)\nOBS_HOTKEY(OBS_KEY_0x47)\nOBS_HOTKEY(OBS_KEY_0x48)\nOBS_HOTKEY(OBS_KEY_0x49)\nOBS_HOTKEY(OBS_KEY_0x4A)\nOBS_HOTKEY(OBS_KEY_0x4B)\nOBS_HOTKEY(OBS_KEY_0x4C)\nOBS_HOTKEY(OBS_KEY_0x4D)\nOBS_HOTKEY(OBS_KEY_0x4E)\nOBS_HOTKEY(OBS_KEY_0x4F)\nOBS_HOTKEY(OBS_KEY_0x50)\nOBS_HOTKEY(OBS_KEY_0x51)\nOBS_HOTKEY(OBS_KEY_0x52)\nOBS_HOTKEY(OBS_KEY_0x53)\nOBS_HOTKEY(OBS_KEY_0x54)\nOBS_HOTKEY(OBS_KEY_0x55)\nOBS_HOTKEY(OBS_KEY_0x56)\nOBS_HOTKEY(OBS_KEY_0x57)\nOBS_HOTKEY(OBS_KEY_0x58)\nOBS_HOTKEY(OBS_KEY_0x59)\nOBS_HOTKEY(OBS_KEY_0x5A)\nOBS_HOTKEY(OBS_KEY_VK_LWIN)\nOBS_HOTKEY(OBS_KEY_VK_RWIN)\nOBS_HOTKEY(OBS_KEY_VK_APPS)\nOBS_HOTKEY(OBS_KEY_0x5E)\nOBS_HOTKEY(OBS_KEY_VK_SLEEP)\nOBS_HOTKEY(OBS_KEY_VK_SEPARATOR)\nOBS_HOTKEY(OBS_KEY_0x88)\nOBS_HOTKEY(OBS_KEY_0x89)\nOBS_HOTKEY(OBS_KEY_0x8A)\nOBS_HOTKEY(OBS_KEY_0x8B)\nOBS_HOTKEY(OBS_KEY_0x8C)\nOBS_HOTKEY(OBS_KEY_0x8D)\nOBS_HOTKEY(OBS_KEY_0x8E)\nOBS_HOTKEY(OBS_KEY_0x8F)\nOBS_HOTKEY(OBS_KEY_VK_OEM_FJ_JISHO)\nOBS_HOTKEY(OBS_KEY_VK_OEM_FJ_LOYA)\nOBS_HOTKEY(OBS_KEY_VK_OEM_FJ_ROYA)\nOBS_HOTKEY(OBS_KEY_0x97)\nOBS_HOTKEY(OBS_KEY_0x98)\nOBS_HOTKEY(OBS_KEY_0x99)\nOBS_HOTKEY(OBS_KEY_0x9A)\nOBS_HOTKEY(OBS_KEY_0x9B)\nOBS_HOTKEY(OBS_KEY_0x9C)\nOBS_HOTKEY(OBS_KEY_0x9D)\nOBS_HOTKEY(OBS_KEY_0x9E)\nOBS_HOTKEY(OBS_KEY_0x9F)\nOBS_HOTKEY(OBS_KEY_VK_LSHIFT)\nOBS_HOTKEY(OBS_KEY_VK_RSHIFT)\nOBS_HOTKEY(OBS_KEY_VK_LCONTROL)\nOBS_HOTKEY(OBS_KEY_VK_RCONTROL)\nOBS_HOTKEY(OBS_KEY_VK_LMENU)\nOBS_HOTKEY(OBS_KEY_VK_RMENU)\nOBS_HOTKEY(OBS_KEY_VK_BROWSER_BACK)\nOBS_HOTKEY(OBS_KEY_VK_BROWSER_FORWARD)\nOBS_HOTKEY(OBS_KEY_VK_BROWSER_REFRESH)\nOBS_HOTKEY(OBS_KEY_VK_BROWSER_STOP)\nOBS_HOTKEY(OBS_KEY_VK_BROWSER_SEARCH)\nOBS_HOTKEY(OBS_KEY_VK_BROWSER_FAVORITES)\nOBS_HOTKEY(OBS_KEY_VK_BROWSER_HOME)\nOBS_HOTKEY(OBS_KEY_VK_VOLUME_MUTE)\nOBS_HOTKEY(OBS_KEY_VK_VOLUME_DOWN)\nOBS_HOTKEY(OBS_KEY_VK_VOLUME_UP)\nOBS_HOTKEY(OBS_KEY_VK_MEDIA_NEXT_TRACK)\nOBS_HOTKEY(OBS_KEY_VK_MEDIA_PREV_TRACK)\nOBS_HOTKEY(OBS_KEY_VK_MEDIA_STOP)\nOBS_HOTKEY(OBS_KEY_VK_MEDIA_PLAY_PAUSE)\nOBS_HOTKEY(OBS_KEY_VK_LAUNCH_MAIL)\nOBS_HOTKEY(OBS_KEY_VK_LAUNCH_MEDIA_SELECT)\nOBS_HOTKEY(OBS_KEY_VK_LAUNCH_APP1)\nOBS_HOTKEY(OBS_KEY_VK_LAUNCH_APP2)\nOBS_HOTKEY(OBS_KEY_0xB8)\nOBS_HOTKEY(OBS_KEY_0xB9)\nOBS_HOTKEY(OBS_KEY_0xC1)\nOBS_HOTKEY(OBS_KEY_0xC2)\nOBS_HOTKEY(OBS_KEY_0xC3)\nOBS_HOTKEY(OBS_KEY_0xC4)\nOBS_HOTKEY(OBS_KEY_0xC5)\nOBS_HOTKEY(OBS_KEY_0xC6)\nOBS_HOTKEY(OBS_KEY_0xC7)\nOBS_HOTKEY(OBS_KEY_0xC8)\nOBS_HOTKEY(OBS_KEY_0xC9)\nOBS_HOTKEY(OBS_KEY_0xCA)\nOBS_HOTKEY(OBS_KEY_0xCB)\nOBS_HOTKEY(OBS_KEY_0xCC)\nOBS_HOTKEY(OBS_KEY_0xCD)\nOBS_HOTKEY(OBS_KEY_0xCE)\nOBS_HOTKEY(OBS_KEY_0xCF)\nOBS_HOTKEY(OBS_KEY_0xD0)\nOBS_HOTKEY(OBS_KEY_0xD1)\nOBS_HOTKEY(OBS_KEY_0xD2)\nOBS_HOTKEY(OBS_KEY_0xD3)\nOBS_HOTKEY(OBS_KEY_0xD4)\nOBS_HOTKEY(OBS_KEY_0xD5)\nOBS_HOTKEY(OBS_KEY_0xD6)\nOBS_HOTKEY(OBS_KEY_0xD7)\nOBS_HOTKEY(OBS_KEY_0xD8)\nOBS_HOTKEY(OBS_KEY_0xD9)\nOBS_HOTKEY(OBS_KEY_0xDA)\nOBS_HOTKEY(OBS_KEY_VK_OEM_8)\nOBS_HOTKEY(OBS_KEY_0xE0)\nOBS_HOTKEY(OBS_KEY_VK_OEM_AX)\nOBS_HOTKEY(OBS_KEY_VK_ICO_HELP)\nOBS_HOTKEY(OBS_KEY_VK_ICO_00)\nOBS_HOTKEY(OBS_KEY_VK_PROCESSKEY)\nOBS_HOTKEY(OBS_KEY_VK_ICO_CLEAR)\nOBS_HOTKEY(OBS_KEY_VK_PACKET)\nOBS_HOTKEY(OBS_KEY_0xE8)\nOBS_HOTKEY(OBS_KEY_VK_OEM_RESET)\nOBS_HOTKEY(OBS_KEY_VK_OEM_JUMP)\nOBS_HOTKEY(OBS_KEY_VK_OEM_PA1)\nOBS_HOTKEY(OBS_KEY_VK_OEM_PA2)\nOBS_HOTKEY(OBS_KEY_VK_OEM_PA3)\nOBS_HOTKEY(OBS_KEY_VK_OEM_WSCTRL)\nOBS_HOTKEY(OBS_KEY_VK_OEM_CUSEL)\nOBS_HOTKEY(OBS_KEY_VK_OEM_ATTN)\nOBS_HOTKEY(OBS_KEY_VK_OEM_FINISH)\nOBS_HOTKEY(OBS_KEY_VK_OEM_COPY)\nOBS_HOTKEY(OBS_KEY_VK_OEM_AUTO)\nOBS_HOTKEY(OBS_KEY_VK_OEM_ENLW)\nOBS_HOTKEY(OBS_KEY_VK_ATTN)\nOBS_HOTKEY(OBS_KEY_VK_CRSEL)\nOBS_HOTKEY(OBS_KEY_VK_EXSEL)\nOBS_HOTKEY(OBS_KEY_VK_EREOF)\nOBS_HOTKEY(OBS_KEY_VK_PLAY)\nOBS_HOTKEY(OBS_KEY_VK_ZOOM)\nOBS_HOTKEY(OBS_KEY_VK_NONAME)\nOBS_HOTKEY(OBS_KEY_VK_PA1)\nOBS_HOTKEY(OBS_KEY_VK_OEM_CLEAR)\n"
  },
  {
    "path": "libobs/obs-interaction.h",
    "content": "/******************************************************************************\n Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n This program is free software: you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation, either version 2 of the License, or\n (at your option) any later version.\n\n This program is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n\n You should have received a copy of the GNU General Public License\n along with this program.  If not, see <http://www.gnu.org/licenses/>.\n ******************************************************************************/\n\n#pragma once\n\n#include \"util/c99defs.h\"\n\nenum obs_interaction_flags {\n\tINTERACT_NONE = 0,\n\tINTERACT_CAPS_KEY = 1,\n\tINTERACT_SHIFT_KEY = 1 << 1,\n\tINTERACT_CONTROL_KEY = 1 << 2,\n\tINTERACT_ALT_KEY = 1 << 3,\n\tINTERACT_MOUSE_LEFT = 1 << 4,\n\tINTERACT_MOUSE_MIDDLE = 1 << 5,\n\tINTERACT_MOUSE_RIGHT = 1 << 6,\n\tINTERACT_COMMAND_KEY = 1 << 7,\n\tINTERACT_NUMLOCK_KEY = 1 << 8,\n\tINTERACT_IS_KEY_PAD = 1 << 9,\n\tINTERACT_IS_LEFT = 1 << 10,\n\tINTERACT_IS_RIGHT = 1 << 11,\n};\n\nenum obs_mouse_button_type {\n\tMOUSE_LEFT,\n\tMOUSE_MIDDLE,\n\tMOUSE_RIGHT,\n};\n\nstruct obs_mouse_event {\n\tuint32_t modifiers;\n\tint32_t x;\n\tint32_t y;\n};\n\nstruct obs_key_event {\n\tuint32_t modifiers;\n\tchar *text;\n\tuint32_t native_modifiers;\n\tuint32_t native_scancode;\n\tuint32_t native_vkey;\n};\n"
  },
  {
    "path": "libobs/obs-internal.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"util/c99defs.h\"\n#include \"util/darray.h\"\n#include \"util/deque.h\"\n#include \"util/dstr.h\"\n#include \"util/threading.h\"\n#include \"util/platform.h\"\n#include \"util/profiler.h\"\n#include \"util/task.h\"\n#include \"util/uthash.h\"\n#include \"util/array-serializer.h\"\n#include \"callback/signal.h\"\n#include \"callback/proc.h\"\n\n#include \"graphics/graphics.h\"\n#include \"graphics/matrix4.h\"\n\n#include \"media-io/audio-resampler.h\"\n#include \"media-io/video-io.h\"\n#include \"media-io/audio-io.h\"\n\n#include \"obs.h\"\n\n#include <obsversion.h>\n#include <caption/caption.h>\n\n/* Custom helpers for the UUID hash table */\n#define HASH_FIND_UUID(head, uuid, out) HASH_FIND(hh_uuid, head, uuid, UUID_STR_LENGTH, out)\n#define HASH_ADD_UUID(head, uuid_field, add) HASH_ADD(hh_uuid, head, uuid_field[0], UUID_STR_LENGTH, add)\n\n#define NUM_TEXTURES 2\n#define NUM_CHANNELS 3\n#define MICROSECOND_DEN 1000000\n#define NUM_ENCODE_TEXTURES 10\n#define NUM_ENCODE_TEXTURE_FRAMES_TO_WAIT 1\n\nstatic inline int64_t packet_dts_usec(struct encoder_packet *packet)\n{\n\treturn packet->dts * MICROSECOND_DEN / packet->timebase_den;\n}\n\nstruct tick_callback {\n\tvoid (*tick)(void *param, float seconds);\n\tvoid *param;\n};\n\nstruct draw_callback {\n\tvoid (*draw)(void *param, uint32_t cx, uint32_t cy);\n\tvoid *param;\n};\n\nstruct rendered_callback {\n\tvoid (*rendered)(void *param);\n\tvoid *param;\n};\n\nstruct packet_callback {\n\tvoid (*packet_cb)(obs_output_t *output, struct encoder_packet *pkt, struct encoder_packet_time *pkt_time,\n\t\t\t  void *param);\n\tvoid *param;\n};\n\nstruct reconnect_callback {\n\tbool (*reconnect_cb)(void *data, obs_output_t *output, int code);\n\tvoid *param;\n};\n\n/* ------------------------------------------------------------------------- */\n/* validity checks */\n\nstatic inline bool obs_object_valid(const void *obj, const char *f, const char *t)\n{\n\tif (!obj) {\n\t\tblog(LOG_DEBUG, \"%s: Null '%s' parameter\", f, t);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\n#define obs_ptr_valid(ptr, func) obs_object_valid(ptr, func, #ptr)\n#define obs_source_valid obs_ptr_valid\n#define obs_output_valid obs_ptr_valid\n#define obs_encoder_valid obs_ptr_valid\n#define obs_service_valid obs_ptr_valid\n\n/* ------------------------------------------------------------------------- */\n/* modules */\n\nstruct obs_module {\n\tchar *mod_name;\n\tconst char *file;\n\tchar *bin_path;\n\tchar *data_path;\n\tvoid *module;\n\tbool loaded;\n\n\tenum obs_module_load_state load_state;\n\n\tbool (*load)(void);\n\tvoid (*unload)(void);\n\tvoid (*post_load)(void);\n\tvoid (*set_locale)(const char *locale);\n\tbool (*get_string)(const char *lookup_string, const char **translated_string);\n\tvoid (*free_locale)(void);\n\tuint32_t (*ver)(void);\n\tvoid (*set_pointer)(obs_module_t *module);\n\tconst char *(*name)(void);\n\tconst char *(*description)(void);\n\tconst char *(*author)(void);\n\n\tstruct obs_module_metadata *metadata;\n\n\tstruct obs_module *next;\n\n\tDARRAY(char *) sources;\n\tDARRAY(char *) outputs;\n\tDARRAY(char *) encoders;\n\tDARRAY(char *) services;\n};\n\nstruct obs_disabled_module {\n\tchar *mod_name;\n\n\tenum obs_module_load_state load_state;\n\n\tstruct obs_module_metadata *metadata;\n\tstruct obs_disabled_module *next;\n\n\tDARRAY(char *) sources;\n\tDARRAY(char *) outputs;\n\tDARRAY(char *) encoders;\n\tDARRAY(char *) services;\n};\n\nextern void free_module(struct obs_module *mod);\n\nstruct obs_module_path {\n\tchar *bin;\n\tchar *data;\n};\n\nstatic inline void free_module_path(struct obs_module_path *omp)\n{\n\tif (omp) {\n\t\tbfree(omp->bin);\n\t\tbfree(omp->data);\n\t}\n}\n\nstruct obs_module_metadata {\n\tchar *display_name;\n\tchar *version;\n\tchar *id;\n\tchar *os_arch;\n\tchar *description;\n\tchar *long_description;\n\tbool has_icon;\n\tbool has_banner;\n\tchar *repository_url;\n\tchar *support_url;\n\tchar *website_url;\n\tchar *name;\n};\n\nstatic inline void free_module_metadata(struct obs_module_metadata *omi)\n{\n\tif (omi) {\n\t\tbfree(omi->display_name);\n\t\tbfree(omi->version);\n\t\tbfree(omi->id);\n\t\tbfree(omi->os_arch);\n\t\tbfree(omi->description);\n\t\tbfree(omi->long_description);\n\t\tbfree(omi->repository_url);\n\t\tbfree(omi->support_url);\n\t\tbfree(omi->website_url);\n\t\tbfree(omi->name);\n\t}\n}\n\nstatic inline bool check_path(const char *data, const char *path, struct dstr *output)\n{\n\tdstr_copy(output, path);\n\tdstr_cat(output, data);\n\n\treturn os_file_exists(output->array);\n}\n\n/* ------------------------------------------------------------------------- */\n/* hotkeys */\n\nstruct obs_hotkey {\n\tobs_hotkey_id id;\n\tchar *name;\n\tchar *description;\n\n\tobs_hotkey_func func;\n\tvoid *data;\n\tint pressed;\n\n\tobs_hotkey_registerer_t registerer_type;\n\tvoid *registerer;\n\n\tobs_hotkey_id pair_partner_id;\n\n\tUT_hash_handle hh;\n};\n\nstruct obs_hotkey_pair {\n\tobs_hotkey_pair_id pair_id;\n\tobs_hotkey_id id[2];\n\tobs_hotkey_active_func func[2];\n\tbool pressed0;\n\tbool pressed1;\n\tvoid *data[2];\n\n\tUT_hash_handle hh;\n};\n\ntypedef struct obs_hotkey_pair obs_hotkey_pair_t;\n\ntypedef struct obs_hotkeys_platform obs_hotkeys_platform_t;\n\nvoid *obs_hotkey_thread(void *param);\n\nstruct obs_core_hotkeys;\nbool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys);\nvoid obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys);\nbool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context, obs_key_t key);\n\nconst char *obs_get_hotkey_translation(obs_key_t key, const char *def);\n\nstruct obs_context_data;\nvoid obs_hotkeys_context_release(struct obs_context_data *context);\n\nvoid obs_hotkeys_free(void);\n\nstruct obs_hotkey_binding {\n\tobs_key_combination_t key;\n\tbool pressed;\n\tbool modifiers_match;\n\n\tobs_hotkey_id hotkey_id;\n\tobs_hotkey_t *hotkey;\n};\n\nstruct obs_hotkey_name_map_item;\nvoid obs_hotkey_name_map_free(void);\n\n/* ------------------------------------------------------------------------- */\n/* views */\n\nenum view_type {\n\tINVALID_VIEW,\n\tMAIN_VIEW,\n\tAUX_VIEW,\n};\n\nstruct obs_view {\n\tpthread_mutex_t channels_mutex;\n\tobs_source_t *channels[MAX_CHANNELS];\n\tenum view_type type;\n};\n\nextern bool obs_view_init(struct obs_view *view, enum view_type type);\nextern void obs_view_free(struct obs_view *view);\n\n/* ------------------------------------------------------------------------- */\n/* displays */\n\nstruct obs_display {\n\tbool update_color_space;\n\tbool enabled;\n\tuint32_t cx, cy;\n\tuint32_t next_cx, next_cy;\n\tuint32_t background_color;\n\tgs_swapchain_t *swap;\n\tpthread_mutex_t draw_callbacks_mutex;\n\tpthread_mutex_t draw_info_mutex;\n\tDARRAY(struct draw_callback) draw_callbacks;\n\tbool use_clear_workaround;\n\n\tstruct obs_display *next;\n\tstruct obs_display **prev_next;\n};\n\nextern bool obs_display_init(struct obs_display *display, const struct gs_init_data *graphics_data);\nextern void obs_display_free(struct obs_display *display);\n\n/* ------------------------------------------------------------------------- */\n/* core */\n\nstruct obs_vframe_info {\n\tuint64_t timestamp;\n\tint count;\n};\n\nstruct obs_tex_frame {\n\tgs_texture_t *tex;\n\tgs_texture_t *tex_uv;\n\tuint32_t handle;\n\tuint64_t timestamp;\n\tuint64_t lock_key;\n\tint count;\n\tbool released;\n};\n\nstruct obs_task_info {\n\tobs_task_t task;\n\tvoid *param;\n};\n\nstruct obs_core_video_mix {\n\tstruct obs_view *view;\n\n\tgs_stagesurf_t *active_copy_surfaces[NUM_TEXTURES][NUM_CHANNELS];\n\tgs_stagesurf_t *copy_surfaces[NUM_TEXTURES][NUM_CHANNELS];\n\tgs_texture_t *convert_textures[NUM_CHANNELS];\n\tgs_texture_t *convert_textures_encode[NUM_CHANNELS];\n#ifdef _WIN32\n\tgs_stagesurf_t *copy_surfaces_encode[NUM_TEXTURES];\n#endif\n\tgs_texture_t *render_texture;\n\tgs_texture_t *output_texture;\n\tenum gs_color_space render_space;\n\tbool texture_rendered;\n\tbool textures_copied[NUM_TEXTURES];\n\tbool texture_converted;\n\tbool using_nv12_tex;\n\tbool using_p010_tex;\n\tstruct deque vframe_info_buffer;\n\tstruct deque vframe_info_buffer_gpu;\n\tgs_stagesurf_t *mapped_surfaces[NUM_CHANNELS];\n\tint cur_texture;\n\tvolatile long raw_active;\n\tvolatile long gpu_encoder_active;\n\tbool gpu_was_active;\n\tbool raw_was_active;\n\tbool was_active;\n\tpthread_mutex_t gpu_encoder_mutex;\n\tstruct deque gpu_encoder_queue;\n\tstruct deque gpu_encoder_avail_queue;\n\tDARRAY(obs_encoder_t *) gpu_encoders;\n\tos_sem_t *gpu_encode_semaphore;\n\tos_event_t *gpu_encode_inactive;\n\tpthread_t gpu_encode_thread;\n\tbool gpu_encode_thread_initialized;\n\tvolatile bool gpu_encode_stop;\n\n\tvideo_t *video;\n\tstruct obs_video_info ovi;\n\n\tbool gpu_conversion;\n\tconst char *conversion_techs[NUM_CHANNELS];\n\tbool conversion_needed;\n\tfloat conversion_width_i;\n\tfloat conversion_height_i;\n\n\tfloat color_matrix[16];\n\n\tbool encoder_only_mix;\n\tlong encoder_refs;\n\n\tbool mix_audio;\n};\n\nextern struct obs_core_video_mix *obs_create_video_mix(struct obs_video_info *ovi);\nextern void obs_free_video_mix(struct obs_core_video_mix *video);\n\nstruct obs_core_video {\n\tgraphics_t *graphics;\n\tgs_effect_t *default_effect;\n\tgs_effect_t *default_rect_effect;\n\tgs_effect_t *opaque_effect;\n\tgs_effect_t *solid_effect;\n\tgs_effect_t *repeat_effect;\n\tgs_effect_t *conversion_effect;\n\tgs_effect_t *bicubic_effect;\n\tgs_effect_t *lanczos_effect;\n\tgs_effect_t *area_effect;\n\tgs_effect_t *bilinear_lowres_effect;\n\tgs_effect_t *premultiplied_alpha_effect;\n\tgs_samplerstate_t *point_sampler;\n\n\tuint64_t video_time;\n\tuint64_t video_frame_interval_ns;\n\tuint64_t video_half_frame_interval_ns;\n\tuint64_t video_avg_frame_time_ns;\n\tdouble video_fps;\n\tpthread_t video_thread;\n\tuint32_t total_frames;\n\tuint32_t lagged_frames;\n\tbool thread_initialized;\n\n\tgs_texture_t *transparent_texture;\n\n\tgs_effect_t *deinterlace_discard_effect;\n\tgs_effect_t *deinterlace_discard_2x_effect;\n\tgs_effect_t *deinterlace_linear_effect;\n\tgs_effect_t *deinterlace_linear_2x_effect;\n\tgs_effect_t *deinterlace_blend_effect;\n\tgs_effect_t *deinterlace_blend_2x_effect;\n\tgs_effect_t *deinterlace_yadif_effect;\n\tgs_effect_t *deinterlace_yadif_2x_effect;\n\n\tfloat sdr_white_level;\n\tfloat hdr_nominal_peak_level;\n\n\tpthread_mutex_t task_mutex;\n\tstruct deque tasks;\n\n\tpthread_mutex_t encoder_group_mutex;\n\tDARRAY(obs_weak_encoder_t *) ready_encoder_groups;\n\n\tpthread_mutex_t mixes_mutex;\n\tDARRAY(struct obs_core_video_mix *) mixes;\n};\n\nextern void add_ready_encoder_group(obs_encoder_t *encoder);\n\nstruct audio_monitor;\n\nstruct obs_core_audio {\n\taudio_t *audio;\n\n\tDARRAY(struct obs_source *) render_order;\n\tDARRAY(struct obs_source *) root_nodes;\n\n\tuint64_t buffered_ts;\n\tstruct deque buffered_timestamps;\n\tuint64_t buffering_wait_ticks;\n\tint total_buffering_ticks;\n\tint max_buffering_ticks;\n\tbool fixed_buffer;\n\n\tpthread_mutex_t monitoring_mutex;\n\tDARRAY(struct audio_monitor *) monitors;\n\tchar *monitoring_device_name;\n\tchar *monitoring_device_id;\n\n\tpthread_mutex_t task_mutex;\n\tstruct deque tasks;\n\n\tstruct obs_source *monitoring_duplicating_source;\n};\n\n/* user sources, output channels, and displays */\nstruct obs_core_data {\n\t/* Hash tables (uthash) */\n\tstruct obs_source *sources;        /* Lookup by UUID (hh_uuid) */\n\tstruct obs_source *public_sources; /* Lookup by name (hh) */\n\n\tstruct obs_canvas *canvases;       /* Lookup by UUID (hh_uuid) */\n\tstruct obs_canvas *named_canvases; /* Lookup by name (hh) */\n\n\t/* Linked lists */\n\tstruct obs_source *first_audio_source;\n\tstruct obs_display *first_display;\n\tstruct obs_output *first_output;\n\tstruct obs_encoder *first_encoder;\n\tstruct obs_service *first_service;\n\n\tpthread_mutex_t sources_mutex;\n\tpthread_mutex_t displays_mutex;\n\tpthread_mutex_t outputs_mutex;\n\tpthread_mutex_t encoders_mutex;\n\tpthread_mutex_t services_mutex;\n\tpthread_mutex_t audio_sources_mutex;\n\tpthread_mutex_t draw_callbacks_mutex;\n\tpthread_mutex_t canvases_mutex;\n\tDARRAY(struct draw_callback) draw_callbacks;\n\tDARRAY(struct rendered_callback) rendered_callbacks;\n\tDARRAY(struct tick_callback) tick_callbacks;\n\n\t/* Main canvas, guaranteed to exist for the lifetime of the program */\n\tstruct obs_canvas *main_canvas;\n\n\tlong long unnamed_index;\n\n\tobs_data_t *private_data;\n\n\tvolatile bool valid;\n\n\tDARRAY(char *) protocols;\n\tDARRAY(obs_source_t *) sources_to_tick;\n};\n\n/* user hotkeys */\nstruct obs_core_hotkeys {\n\tpthread_mutex_t mutex;\n\tobs_hotkey_t *hotkeys;\n\tobs_hotkey_id next_id;\n\tobs_hotkey_pair_t *hotkey_pairs;\n\tobs_hotkey_pair_id next_pair_id;\n\n\tpthread_t hotkey_thread;\n\tbool hotkey_thread_initialized;\n\tos_event_t *stop_event;\n\tbool thread_disable_press;\n\tbool strict_modifiers;\n\tbool reroute_hotkeys;\n\tDARRAY(obs_hotkey_binding_t) bindings;\n\n\tobs_hotkey_callback_router_func router_func;\n\tvoid *router_func_data;\n\n\tobs_hotkeys_platform_t *platform_context;\n\n\tpthread_once_t name_map_init_token;\n\tstruct obs_hotkey_name_map_item *name_map;\n\n\tsignal_handler_t *signals;\n\n\tchar *translations[OBS_KEY_LAST_VALUE];\n\tchar *mute;\n\tchar *unmute;\n\tchar *push_to_mute;\n\tchar *push_to_talk;\n\tchar *sceneitem_show;\n\tchar *sceneitem_hide;\n};\n\ntypedef DARRAY(struct obs_source_info) obs_source_info_array_t;\n\nstruct obs_core {\n\tstruct obs_module *first_module;\n\tstruct obs_module *first_disabled_module;\n\n\tDARRAY(struct obs_module_path) module_paths;\n\tDARRAY(char *) safe_modules;\n\tDARRAY(char *) disabled_modules;\n\tDARRAY(char *) core_modules;\n\n\tobs_source_info_array_t source_types;\n\tobs_source_info_array_t input_types;\n\tobs_source_info_array_t filter_types;\n\tobs_source_info_array_t transition_types;\n\tDARRAY(struct obs_output_info) output_types;\n\tDARRAY(struct obs_encoder_info) encoder_types;\n\tDARRAY(struct obs_service_info) service_types;\n\n\tsignal_handler_t *signals;\n\tproc_handler_t *procs;\n\n\tchar *locale;\n\tchar *module_config_path;\n\tbool name_store_owned;\n\tprofiler_name_store_t *name_store;\n\n\t/* segmented into multiple sub-structures to keep things a bit more\n\t * clean and organized */\n\tstruct obs_core_video video;\n\tstruct obs_core_audio audio;\n\tstruct obs_core_data data;\n\tstruct obs_core_hotkeys hotkeys;\n\n\tos_task_queue_t *destruction_task_thread;\n\n\tobs_task_handler_t ui_task_handler;\n};\n\nextern struct obs_core *obs;\n\nstruct obs_graphics_context {\n\tuint64_t last_time;\n\tuint64_t interval;\n\tuint64_t frame_time_total_ns;\n\tuint64_t fps_total_ns;\n\tuint32_t fps_total_frames;\n\tconst char *video_thread_name;\n};\n\nextern void *obs_graphics_thread(void *param);\nextern bool obs_graphics_thread_loop(struct obs_graphics_context *context);\n#ifdef __APPLE__\nextern void *obs_graphics_thread_autorelease(void *param);\nextern bool obs_graphics_thread_loop_autorelease(struct obs_graphics_context *context);\n#endif\n\nextern gs_effect_t *obs_load_effect(gs_effect_t **effect, const char *file);\n\nextern bool audio_callback(void *param, uint64_t start_ts_in, uint64_t end_ts_in, uint64_t *out_ts, uint32_t mixers,\n\t\t\t   struct audio_output_data *mixes);\n\nextern struct obs_core_video_mix *get_mix_for_video(video_t *video);\n\nextern void start_raw_video(video_t *video, const struct video_scale_info *conversion, uint32_t frame_rate_divisor,\n\t\t\t    void (*callback)(void *param, struct video_data *frame), void *param);\nextern void stop_raw_video(video_t *video, void (*callback)(void *param, struct video_data *frame), void *param);\n\n/* ------------------------------------------------------------------------- */\n/* obs shared context data */\n\nstruct obs_weak_ref {\n\tvolatile long refs;\n\tvolatile long weak_refs;\n};\n\nstruct obs_weak_object {\n\tstruct obs_weak_ref ref;\n\tstruct obs_context_data *object;\n};\n\ntypedef void (*obs_destroy_cb)(void *obj);\n\nstruct obs_context_data {\n\tchar *name;\n\tconst char *uuid;\n\tvoid *data;\n\tobs_data_t *settings;\n\tsignal_handler_t *signals;\n\tproc_handler_t *procs;\n\tenum obs_obj_type type;\n\n\tstruct obs_weak_object *control;\n\tobs_destroy_cb destroy;\n\n\tDARRAY(obs_hotkey_id) hotkeys;\n\tDARRAY(obs_hotkey_pair_id) hotkey_pairs;\n\tobs_data_t *hotkey_data;\n\n\tDARRAY(char *) rename_cache;\n\tpthread_mutex_t rename_cache_mutex;\n\n\tpthread_mutex_t *mutex;\n\tstruct obs_context_data *next;\n\tstruct obs_context_data **prev_next;\n\n\tUT_hash_handle hh;\n\tUT_hash_handle hh_uuid;\n\n\tbool private;\n};\n\nextern bool obs_context_data_init(struct obs_context_data *context, enum obs_obj_type type, obs_data_t *settings,\n\t\t\t\t  const char *name, const char *uuid, obs_data_t *hotkey_data, bool private);\nextern void obs_context_init_control(struct obs_context_data *context, void *object, obs_destroy_cb destroy);\nextern void obs_context_data_free(struct obs_context_data *context);\n\nextern void obs_context_data_insert(struct obs_context_data *context, pthread_mutex_t *mutex, void *first);\nextern void obs_context_data_insert_name(struct obs_context_data *context, pthread_mutex_t *mutex, void *first);\nextern void obs_context_data_insert_uuid(struct obs_context_data *context, pthread_mutex_t *mutex, void *first_uuid);\n\nextern void obs_context_data_remove(struct obs_context_data *context);\nextern void obs_context_data_remove_name(struct obs_context_data *context, pthread_mutex_t *mutex, void *phead);\nextern void obs_context_data_remove_uuid(struct obs_context_data *context, pthread_mutex_t *mutex, void *puuid_head);\n\nextern void obs_context_wait(struct obs_context_data *context);\n\nextern void obs_context_data_setname(struct obs_context_data *context, const char *name);\nextern void obs_context_data_setname_ht(struct obs_context_data *context, const char *name, void *phead);\n\n/* ------------------------------------------------------------------------- */\n/* ref-counting  */\n\nstatic inline void obs_ref_addref(struct obs_weak_ref *ref)\n{\n\tos_atomic_inc_long(&ref->refs);\n}\n\nstatic inline bool obs_ref_release(struct obs_weak_ref *ref)\n{\n\treturn os_atomic_dec_long(&ref->refs) == -1;\n}\n\nstatic inline void obs_weak_ref_addref(struct obs_weak_ref *ref)\n{\n\tos_atomic_inc_long(&ref->weak_refs);\n}\n\nstatic inline bool obs_weak_ref_release(struct obs_weak_ref *ref)\n{\n\treturn os_atomic_dec_long(&ref->weak_refs) == -1;\n}\n\nstatic inline bool obs_weak_ref_get_ref(struct obs_weak_ref *ref)\n{\n\tlong owners = os_atomic_load_long(&ref->refs);\n\twhile (owners > -1) {\n\t\tif (os_atomic_compare_exchange_long(&ref->refs, &owners, owners + 1)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic inline bool obs_weak_ref_expired(struct obs_weak_ref *ref)\n{\n\tlong owners = os_atomic_load_long(&ref->refs);\n\treturn owners < 0;\n}\n\n/* ------------------------------------------------------------------------- */\n/* canvases */\n\nstruct obs_weak_canvas {\n\tstruct obs_weak_ref ref;\n\tstruct obs_canvas *canvas;\n};\n\nstruct obs_canvas {\n\tstruct obs_context_data context;\n\n\t/* obs_canvas_flags */\n\tuint32_t flags;\n\t/* Video info for this canvas, FPS ignored */\n\tstruct obs_video_info ovi;\n\n\t/* Hash table containing scenes (and groups) associated with this canvas */\n\tstruct obs_source *sources;\n\tpthread_mutex_t sources_mutex;\n\n\t/* For now, canvas objects mainly act as a proxy for the existing view and video mix objects,\n\t * though this may change in the future. */\n\tstruct obs_view view;\n\tstruct obs_core_video_mix *mix;\n};\n\nextern obs_canvas_t *obs_create_main_canvas(void);\nextern void obs_canvas_destroy(obs_canvas_t *canvas);\nextern void obs_canvas_clear_mix(obs_canvas_t *canvas);\nextern void obs_free_canvas_mixes(void);\nextern bool obs_canvas_reset_video_internal(obs_canvas_t *canvas, struct obs_video_info *ovi);\nextern void obs_canvas_insert_source(obs_canvas_t *canvas, obs_source_t *source);\nextern void obs_canvas_remove_source(obs_source_t *source);\nextern void obs_canvas_rename_source(obs_source_t *source, const char *name);\n\n/* ------------------------------------------------------------------------- */\n/* sources  */\n\nstruct async_frame {\n\tstruct obs_source_frame *frame;\n\tlong unused_count;\n\tbool used;\n};\n\nenum audio_action_type {\n\tAUDIO_ACTION_VOL,\n\tAUDIO_ACTION_MUTE,\n\tAUDIO_ACTION_PTT,\n\tAUDIO_ACTION_PTM,\n};\n\nstruct audio_action {\n\tuint64_t timestamp;\n\tenum audio_action_type type;\n\tunion {\n\t\tfloat vol;\n\t\tbool set;\n\t};\n};\n\nstruct obs_weak_source {\n\tstruct obs_weak_ref ref;\n\tstruct obs_source *source;\n};\n\nstruct audio_cb_info {\n\tobs_source_audio_capture_t callback;\n\tvoid *param;\n};\n\nstruct caption_cb_info {\n\tobs_source_caption_t callback;\n\tvoid *param;\n};\n\nenum media_action_type {\n\tMEDIA_ACTION_NONE,\n\tMEDIA_ACTION_PLAY_PAUSE,\n\tMEDIA_ACTION_RESTART,\n\tMEDIA_ACTION_STOP,\n\tMEDIA_ACTION_NEXT,\n\tMEDIA_ACTION_PREVIOUS,\n\tMEDIA_ACTION_SET_TIME,\n};\n\nstruct media_action {\n\tenum media_action_type type;\n\tunion {\n\t\tbool pause;\n\t\tint64_t ms;\n\t};\n};\n\nstruct obs_source {\n\tstruct obs_context_data context;\n\tstruct obs_source_info info;\n\n\t/* general exposed flags that can be set for the source */\n\tuint32_t flags;\n\tuint32_t default_flags;\n\tuint32_t last_obs_ver;\n\n\t/* indicates ownership of the info.id buffer */\n\tbool owns_info_id;\n\n\t/* signals to call the source update in the video thread */\n\tlong defer_update_count;\n\n\t/* ensures show/hide are only called once */\n\tvolatile long show_refs;\n\n\t/* ensures activate/deactivate are only called once */\n\tvolatile long activate_refs;\n\n\t/* source is in the process of being destroyed */\n\tvolatile long destroying;\n\n\t/* used to indicate that the source has been removed and all\n\t * references to it should be released (not exactly how I would prefer\n\t * to handle things but it's the best option) */\n\tbool removed;\n\n\t/*  used to indicate if the source should show up when queried for user ui */\n\tbool temp_removed;\n\n\tbool active;\n\tbool showing;\n\n\t/* used to temporarily disable sources if needed */\n\tbool enabled;\n\n\t/* hint to allow sources to render more quickly */\n\tbool texcoords_centered;\n\n\t/* timing (if video is present, is based upon video) */\n\tvolatile bool timing_set;\n\tvolatile uint64_t timing_adjust;\n\tuint64_t resample_offset;\n\tuint64_t next_audio_ts_min;\n\tuint64_t next_audio_sys_ts_min;\n\tuint64_t last_frame_ts;\n\tuint64_t last_sys_timestamp;\n\tbool async_rendered;\n\n\t/* audio */\n\tbool audio_failed;\n\tbool audio_pending;\n\tbool pending_stop;\n\tbool audio_active;\n\tbool user_muted;\n\tbool muted;\n\tstruct obs_source *next_audio_source;\n\tstruct obs_source **prev_next_audio_source;\n\tuint64_t audio_ts;\n\tstruct deque audio_input_buf[MAX_AUDIO_CHANNELS];\n\tsize_t last_audio_input_buf_size;\n\tDARRAY(struct audio_action) audio_actions;\n\tfloat *audio_output_buf[MAX_AUDIO_MIXES][MAX_AUDIO_CHANNELS];\n\tfloat *audio_mix_buf[MAX_AUDIO_CHANNELS];\n\tstruct resample_info sample_info;\n\taudio_resampler_t *resampler;\n\tpthread_mutex_t audio_actions_mutex;\n\tpthread_mutex_t audio_buf_mutex;\n\tpthread_mutex_t audio_mutex;\n\tpthread_mutex_t audio_cb_mutex;\n\tDARRAY(struct audio_cb_info) audio_cb_list;\n\tstruct obs_audio_data audio_data;\n\tsize_t audio_storage_size;\n\tuint32_t audio_mixers;\n\tfloat user_volume;\n\tfloat volume;\n\tint64_t sync_offset;\n\tint64_t last_sync_offset;\n\tfloat balance;\n\t/* audio_is_duplicated: tracks whether a source appears multiple times in the audio tree during this tick */\n\tbool audio_is_duplicated;\n\n\t/* async video data */\n\tgs_texture_t *async_textures[MAX_AV_PLANES];\n\tgs_texrender_t *async_texrender;\n\tstruct obs_source_frame *cur_async_frame;\n\tbool async_gpu_conversion;\n\tenum video_format async_format;\n\tbool async_full_range;\n\tuint8_t async_trc;\n\tenum video_format async_cache_format;\n\tbool async_cache_full_range;\n\tuint8_t async_cache_trc;\n\tenum gs_color_format async_texture_formats[MAX_AV_PLANES];\n\tint async_channel_count;\n\tlong async_rotation;\n\tbool async_flip;\n\tbool async_linear_alpha;\n\tbool async_active;\n\tbool async_update_texture;\n\tbool async_unbuffered;\n\tbool async_decoupled;\n\tstruct obs_source_frame *async_preload_frame;\n\tDARRAY(struct async_frame) async_cache;\n\tDARRAY(struct obs_source_frame *) async_frames;\n\tpthread_mutex_t async_mutex;\n\tuint32_t async_width;\n\tuint32_t async_height;\n\tuint32_t async_cache_width;\n\tuint32_t async_cache_height;\n\tuint32_t async_convert_width[MAX_AV_PLANES];\n\tuint32_t async_convert_height[MAX_AV_PLANES];\n\tuint64_t async_last_rendered_ts;\n\n\tpthread_mutex_t caption_cb_mutex;\n\tDARRAY(struct caption_cb_info) caption_cb_list;\n\n\t/* async video deinterlacing */\n\tuint64_t deinterlace_offset;\n\tuint64_t deinterlace_frame_ts;\n\tgs_effect_t *deinterlace_effect;\n\tstruct obs_source_frame *prev_async_frame;\n\tgs_texture_t *async_prev_textures[MAX_AV_PLANES];\n\tgs_texrender_t *async_prev_texrender;\n\tuint32_t deinterlace_half_duration;\n\tenum obs_deinterlace_mode deinterlace_mode;\n\tbool deinterlace_top_first;\n\tbool deinterlace_rendered;\n\n\t/* filters */\n\tstruct obs_source *filter_parent;\n\tstruct obs_source *filter_target;\n\tDARRAY(struct obs_source *) filters;\n\tpthread_mutex_t filter_mutex;\n\tgs_texrender_t *filter_texrender;\n\tenum obs_allow_direct_render allow_direct;\n\tbool rendering_filter;\n\tbool filter_bypass_active;\n\n\t/* sources specific hotkeys */\n\tobs_hotkey_pair_id mute_unmute_key;\n\tobs_hotkey_id push_to_mute_key;\n\tobs_hotkey_id push_to_talk_key;\n\tbool push_to_mute_enabled;\n\tbool push_to_mute_pressed;\n\tbool user_push_to_mute_pressed;\n\tbool push_to_talk_enabled;\n\tbool push_to_talk_pressed;\n\tbool user_push_to_talk_pressed;\n\tuint64_t push_to_mute_delay;\n\tuint64_t push_to_mute_stop_time;\n\tuint64_t push_to_talk_delay;\n\tuint64_t push_to_talk_stop_time;\n\n\t/* transitions */\n\tuint64_t transition_start_time;\n\tuint64_t transition_duration;\n\tpthread_mutex_t transition_tex_mutex;\n\tgs_texrender_t *transition_texrender[2];\n\tpthread_mutex_t transition_mutex;\n\tobs_source_t *transition_sources[2];\n\tfloat transition_manual_clamp;\n\tfloat transition_manual_torque;\n\tfloat transition_manual_target;\n\tfloat transition_manual_val;\n\tbool transitioning_video;\n\tbool transitioning_audio;\n\tbool transition_source_active[2];\n\tuint32_t transition_alignment;\n\tuint32_t transition_actual_cx;\n\tuint32_t transition_actual_cy;\n\tuint32_t transition_cx;\n\tuint32_t transition_cy;\n\tuint32_t transition_fixed_duration;\n\tbool transition_use_fixed_duration;\n\tenum obs_transition_mode transition_mode;\n\tenum obs_transition_scale_type transition_scale_type;\n\tstruct matrix4 transition_matrices[2];\n\n\t/* color space */\n\tgs_texrender_t *color_space_texrender;\n\n\t/* audio monitoring */\n\tstruct audio_monitor *monitor;\n\tenum obs_monitoring_type monitoring_type;\n\n\t/* media action queue */\n\tDARRAY(struct media_action) media_actions;\n\tpthread_mutex_t media_actions_mutex;\n\n\t/* private data */\n\tobs_data_t *private_settings;\n\n\t/* canvas this source belongs to (only used for scenes) */\n\tobs_weak_canvas_t *canvas;\n};\n\nextern struct obs_source_info *get_source_info(const char *id);\nextern struct obs_source_info *get_source_info2(const char *unversioned_id, uint32_t ver);\nextern bool obs_source_init_context(struct obs_source *source, obs_data_t *settings, const char *name, const char *uuid,\n\t\t\t\t    obs_data_t *hotkey_data, bool private);\n\nextern bool obs_transition_init(obs_source_t *transition);\nextern void obs_transition_free(obs_source_t *transition);\nextern void obs_transition_tick(obs_source_t *transition, float t);\nextern void obs_transition_enum_sources(obs_source_t *transition, obs_source_enum_proc_t enum_callback, void *param);\nextern void obs_transition_save(obs_source_t *source, obs_data_t *data);\nextern void obs_transition_load(obs_source_t *source, obs_data_t *data);\n\nstruct audio_monitor *audio_monitor_create(obs_source_t *source);\nvoid audio_monitor_reset(struct audio_monitor *monitor);\nextern void audio_monitor_destroy(struct audio_monitor *monitor);\n\nextern obs_source_t *obs_source_create_canvas(obs_canvas_t *canvas, const char *id, const char *name,\n\t\t\t\t\t      obs_data_t *settings, obs_data_t *hotkey_data);\nextern obs_source_t *obs_source_create_set_last_ver(obs_canvas_t *canvas, const char *id, const char *name,\n\t\t\t\t\t\t    const char *uuid, obs_data_t *settings, obs_data_t *hotkey_data,\n\t\t\t\t\t\t    uint32_t last_obs_ver, bool is_private);\n\nextern void obs_source_destroy(struct obs_source *source);\nextern void obs_source_addref(obs_source_t *source);\n\nstatic inline void obs_source_dosignal(struct obs_source *source, const char *signal_obs, const char *signal_source)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"source\", source);\n\tif (signal_obs && !source->context.private)\n\t\tsignal_handler_signal(obs->signals, signal_obs, &data);\n\tif (signal_source)\n\t\tsignal_handler_signal(source->context.signals, signal_source, &data);\n}\n\nstatic inline void obs_source_dosignal_canvas(struct obs_source *source, struct obs_canvas *canvas,\n\t\t\t\t\t      const char *signal_obs, const char *signal_source)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"source\", source);\n\tcalldata_set_ptr(&data, \"canvas\", canvas);\n\tif (signal_obs && !source->context.private)\n\t\tsignal_handler_signal(obs->signals, signal_obs, &data);\n\tif (signal_source)\n\t\tsignal_handler_signal(source->context.signals, signal_source, &data);\n}\n\n/* maximum timestamp variance in nanoseconds */\n#define MAX_TS_VAR 2000000000ULL\n\nstatic inline bool frame_out_of_bounds(const obs_source_t *source, uint64_t ts)\n{\n\tif (ts < source->last_frame_ts)\n\t\treturn ((source->last_frame_ts - ts) > MAX_TS_VAR);\n\telse\n\t\treturn ((ts - source->last_frame_ts) > MAX_TS_VAR);\n}\n\nstatic inline enum gs_color_format convert_video_format(enum video_format format, enum video_trc trc)\n{\n\tswitch (trc) {\n\tcase VIDEO_TRC_PQ:\n\tcase VIDEO_TRC_HLG:\n\t\treturn GS_RGBA16F;\n\tdefault:\n\t\tswitch (format) {\n\t\tcase VIDEO_FORMAT_RGBA:\n\t\t\treturn GS_RGBA;\n\t\tcase VIDEO_FORMAT_BGRA:\n\t\tcase VIDEO_FORMAT_I40A:\n\t\tcase VIDEO_FORMAT_I42A:\n\t\tcase VIDEO_FORMAT_YUVA:\n\t\tcase VIDEO_FORMAT_AYUV:\n\t\t\treturn GS_BGRA;\n\t\tcase VIDEO_FORMAT_I010:\n\t\tcase VIDEO_FORMAT_P010:\n\t\tcase VIDEO_FORMAT_I210:\n\t\tcase VIDEO_FORMAT_I412:\n\t\tcase VIDEO_FORMAT_YA2L:\n\t\tcase VIDEO_FORMAT_P216:\n\t\tcase VIDEO_FORMAT_P416:\n\t\tcase VIDEO_FORMAT_V210:\n\t\tcase VIDEO_FORMAT_R10L:\n\t\t\treturn GS_RGBA16F;\n\t\tdefault:\n\t\t\treturn GS_BGRX;\n\t\t}\n\t}\n}\n\nstatic inline enum gs_color_space convert_video_space(enum video_format format, enum video_trc trc)\n{\n\tenum gs_color_space space = GS_CS_SRGB;\n\tif (convert_video_format(format, trc) == GS_RGBA16F) {\n\t\tswitch (trc) {\n\t\tcase VIDEO_TRC_DEFAULT:\n\t\tcase VIDEO_TRC_SRGB:\n\t\t\tspace = GS_CS_SRGB_16F;\n\t\t\tbreak;\n\t\tcase VIDEO_TRC_PQ:\n\t\tcase VIDEO_TRC_HLG:\n\t\t\tspace = GS_CS_709_EXTENDED;\n\t\t}\n\t}\n\n\treturn space;\n}\n\nextern void obs_source_set_texcoords_centered(obs_source_t *source, bool centered);\nextern void obs_source_activate(obs_source_t *source, enum view_type type);\nextern void obs_source_deactivate(obs_source_t *source, enum view_type type);\nextern void obs_source_video_tick(obs_source_t *source, float seconds);\nextern float obs_source_get_target_volume(obs_source_t *source, obs_source_t *target);\nextern uint64_t obs_source_get_last_async_ts(const obs_source_t *source);\n\nextern void obs_source_audio_render(obs_source_t *source, uint32_t mixers, size_t channels, size_t sample_rate,\n\t\t\t\t    size_t size);\n\nextern void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy);\n\nextern struct obs_source_frame *filter_async_video(obs_source_t *source, struct obs_source_frame *in);\nextern bool update_async_texture(struct obs_source *source, const struct obs_source_frame *frame, gs_texture_t *tex,\n\t\t\t\t gs_texrender_t *texrender);\nextern bool update_async_textures(struct obs_source *source, const struct obs_source_frame *frame,\n\t\t\t\t  gs_texture_t *tex[MAX_AV_PLANES], gs_texrender_t *texrender);\nextern bool set_async_texture_size(struct obs_source *source, const struct obs_source_frame *frame);\nextern void remove_async_frame(obs_source_t *source, struct obs_source_frame *frame);\n\nextern void set_deinterlace_texture_size(obs_source_t *source);\nextern void deinterlace_process_last_frame(obs_source_t *source, uint64_t sys_time);\nextern void deinterlace_update_async_video(obs_source_t *source);\nextern void deinterlace_render(obs_source_t *s);\n\n/* ------------------------------------------------------------------------- */\n/* outputs  */\n\nenum delay_msg {\n\tDELAY_MSG_PACKET,\n\tDELAY_MSG_START,\n\tDELAY_MSG_STOP,\n};\n\nstruct delay_data {\n\tenum delay_msg msg;\n\tuint64_t ts;\n\tstruct encoder_packet packet;\n\tbool packet_time_valid;\n\tstruct encoder_packet_time packet_time;\n};\n\ntypedef void (*encoded_callback_t)(void *data, struct encoder_packet *packet, struct encoder_packet_time *frame_time);\n\nstruct obs_weak_output {\n\tstruct obs_weak_ref ref;\n\tstruct obs_output *output;\n};\n\n#define CAPTION_LINE_CHARS (32)\n#define CAPTION_LINE_BYTES (4 * CAPTION_LINE_CHARS)\nstruct caption_text {\n\tchar text[CAPTION_LINE_BYTES + 1];\n\tdouble display_duration;\n\tstruct caption_text *next;\n};\n\nstruct caption_track_data {\n\tstruct caption_text *caption_head;\n\tstruct caption_text *caption_tail;\n\tpthread_mutex_t caption_mutex;\n\tdouble caption_timestamp;\n\tdouble last_caption_timestamp;\n\tstruct deque caption_data;\n};\n\nstruct pause_data {\n\tpthread_mutex_t mutex;\n\tuint64_t last_video_ts;\n\tuint64_t ts_start;\n\tuint64_t ts_end;\n\tuint64_t ts_offset;\n};\n\nextern bool video_pause_check(struct pause_data *pause, uint64_t timestamp);\nextern bool audio_pause_check(struct pause_data *pause, struct audio_data *data, size_t sample_rate);\nextern void pause_reset(struct pause_data *pause);\n\nenum keyframe_group_track_status {\n\tKEYFRAME_TRACK_STATUS_NOT_SEEN = 0,\n\tKEYFRAME_TRACK_STATUS_SEEN = 1,\n\tKEYFRAME_TRACK_STATUS_SKIPPED = 2,\n};\n\nstruct keyframe_group_data {\n\tuintptr_t group_id;\n\tint64_t pts;\n\tuint32_t required_tracks;\n\tenum keyframe_group_track_status seen_on_track[MAX_OUTPUT_VIDEO_ENCODERS];\n};\n\nstruct obs_output {\n\tstruct obs_context_data context;\n\tstruct obs_output_info info;\n\n\t/* indicates ownership of the info.id buffer */\n\tbool owns_info_id;\n\n\tbool received_video[MAX_OUTPUT_VIDEO_ENCODERS];\n\tDARRAY(struct keyframe_group_data) keyframe_group_tracking;\n\tbool received_audio;\n\tvolatile bool data_active;\n\tvolatile bool end_data_capture_thread_active;\n\tint64_t video_offsets[MAX_OUTPUT_VIDEO_ENCODERS];\n\tint64_t audio_offsets[MAX_OUTPUT_AUDIO_ENCODERS];\n\tint64_t highest_audio_ts;\n\tint64_t highest_video_ts[MAX_OUTPUT_VIDEO_ENCODERS];\n\tpthread_t end_data_capture_thread;\n\tos_event_t *stopping_event;\n\tpthread_mutex_t interleaved_mutex;\n\tDARRAY(struct encoder_packet) interleaved_packets;\n\tsize_t interleaver_max_batch_size;\n\tint stop_code;\n\n\tint reconnect_retry_sec;\n\tint reconnect_retry_max;\n\tint reconnect_retries;\n\tuint32_t reconnect_retry_cur_msec;\n\tfloat reconnect_retry_exp;\n\tpthread_t reconnect_thread;\n\tos_event_t *reconnect_stop_event;\n\tvolatile bool reconnecting;\n\tvolatile bool reconnect_thread_active;\n\n\tuint32_t starting_drawn_count;\n\tuint32_t starting_lagged_count;\n\n\tint total_frames;\n\n\tvolatile bool active;\n\tvolatile bool paused;\n\tvideo_t *video;\n\taudio_t *audio;\n\tobs_encoder_t *video_encoders[MAX_OUTPUT_VIDEO_ENCODERS];\n\tobs_encoder_t *audio_encoders[MAX_OUTPUT_AUDIO_ENCODERS];\n\tobs_service_t *service;\n\tsize_t mixer_mask;\n\n\tstruct pause_data pause;\n\n\tstruct deque audio_buffer[MAX_AUDIO_MIXES][MAX_AV_PLANES];\n\tuint64_t audio_start_ts;\n\tuint64_t video_start_ts;\n\tsize_t audio_size;\n\tsize_t planes;\n\tsize_t sample_rate;\n\tsize_t total_audio_frames;\n\n\tuint32_t scaled_width;\n\tuint32_t scaled_height;\n\n\tbool video_conversion_set;\n\tbool audio_conversion_set;\n\tstruct video_scale_info video_conversion;\n\tstruct audio_convert_info audio_conversion;\n\n\t// captions are output per track\n\tstruct caption_track_data *caption_tracks[MAX_OUTPUT_VIDEO_ENCODERS];\n\n\tDARRAY(struct encoder_packet_time)\n\tencoder_packet_times[MAX_OUTPUT_VIDEO_ENCODERS];\n\n\t/* Packet callbacks */\n\tpthread_mutex_t pkt_callbacks_mutex;\n\tDARRAY(struct packet_callback) pkt_callbacks;\n\n\tstruct reconnect_callback reconnect_callback;\n\n\tbool valid;\n\n\tuint64_t active_delay_ns;\n\tencoded_callback_t delay_callback;\n\tstruct deque delay_data; /* struct delay_data */\n\tpthread_mutex_t delay_mutex;\n\tuint32_t delay_sec;\n\tuint32_t delay_flags;\n\tuint32_t delay_cur_flags;\n\tvolatile long delay_restart_refs;\n\tvolatile bool delay_active;\n\tvolatile bool delay_capturing;\n\n\tchar *last_error_message;\n\n\tfloat audio_data[MAX_AUDIO_CHANNELS][AUDIO_OUTPUT_FRAMES];\n};\n\nstatic inline void do_output_signal(struct obs_output *output, const char *signal)\n{\n\tstruct calldata params = {0};\n\tcalldata_set_ptr(&params, \"output\", output);\n\tsignal_handler_signal(output->context.signals, signal, &params);\n\tcalldata_free(&params);\n}\n\nextern void process_delay(void *data, struct encoder_packet *packet, struct encoder_packet_time *packet_time);\nextern void obs_output_cleanup_delay(obs_output_t *output);\nextern bool obs_output_delay_start(obs_output_t *output);\nextern void obs_output_delay_stop(obs_output_t *output);\nextern bool obs_output_actual_start(obs_output_t *output);\nextern void obs_output_actual_stop(obs_output_t *output, bool force, uint64_t ts);\n\nextern const struct obs_output_info *find_output(const char *id);\n\nextern void obs_output_remove_encoder(struct obs_output *output, struct obs_encoder *encoder);\n\nextern void obs_encoder_packet_create_instance(struct encoder_packet *dst, const struct encoder_packet *src);\nvoid obs_output_destroy(obs_output_t *output);\n\n/* ------------------------------------------------------------------------- */\n/* encoders  */\n\nstruct obs_weak_encoder {\n\tstruct obs_weak_ref ref;\n\tstruct obs_encoder *encoder;\n};\n\nstruct encoder_callback {\n\tbool sent_first_packet;\n\tencoded_callback_t new_packet;\n\tvoid *param;\n};\n\nstruct obs_encoder_group {\n\tpthread_mutex_t mutex;\n\t/* allows group to be destroyed even if some encoders are active */\n\tbool destroy_on_stop;\n\n\t/* holds strong references to all encoders */\n\tDARRAY(struct obs_encoder *) encoders;\n\n\tuint32_t num_encoders_started;\n\tuint64_t start_timestamp;\n};\n\nstruct obs_encoder {\n\tstruct obs_context_data context;\n\tstruct obs_encoder_info info;\n\n\t/* allows re-routing to another encoder */\n\tstruct obs_encoder_info orig_info;\n\n\tpthread_mutex_t init_mutex;\n\n\tuint32_t samplerate;\n\tsize_t planes;\n\tsize_t blocksize;\n\tsize_t framesize;\n\tsize_t framesize_bytes;\n\n\tsize_t mixer_idx;\n\n\t/* OBS_SCALE_DISABLE indicates GPU scaling is disabled */\n\tenum obs_scale_type gpu_scale_type;\n\n\tuint32_t scaled_width;\n\tuint32_t scaled_height;\n\tenum video_format preferred_format;\n\tenum video_colorspace preferred_space;\n\tenum video_range_type preferred_range;\n\n\tvolatile bool active;\n\tvolatile bool paused;\n\tbool initialized;\n\n\t/* indicates ownership of the info.id buffer */\n\tbool owns_info_id;\n\n\tuint32_t timebase_num;\n\tuint32_t timebase_den;\n\n\t// allow outputting at fractions of main composition FPS,\n\t// e.g. 60 FPS with frame_rate_divisor = 1 turns into 30 FPS\n\t//\n\t// a separate counter is used in favor of using remainder calculations\n\t// to allow \"inputs\" started at the same time to start on the same frame\n\t// whereas with remainder calculation the frame alignment would depend on\n\t// the total frame count at the time the encoder was started\n\tuint32_t frame_rate_divisor;\n\tuint32_t frame_rate_divisor_counter; // only used for GPU encoders\n\tvideo_t *fps_override;\n\n\t// Number of frames successfully encoded\n\tuint32_t encoded_frames;\n\n\t/* Regions of interest to prioritize during encoding */\n\tpthread_mutex_t roi_mutex;\n\tDARRAY(struct obs_encoder_roi) roi;\n\tuint32_t roi_increment;\n\n\tint64_t cur_pts;\n\n\tstruct deque audio_input_buffer[MAX_AV_PLANES];\n\tuint8_t *audio_output_buffer[MAX_AV_PLANES];\n\n\t/* if a video encoder is paired with an audio encoder, make it start\n\t * up at the specific timestamp.  if this is the audio encoder,\n\t * it waits until it's ready to sync up with video */\n\tbool first_received;\n\tDARRAY(struct obs_weak_encoder *) paired_encoders;\n\tint64_t offset_usec;\n\tuint64_t first_raw_ts;\n\tuint64_t start_ts;\n\n\t/* track encoders that are part of a gop-aligned multi track group */\n\tstruct obs_encoder_group *encoder_group;\n\n\tpthread_mutex_t outputs_mutex;\n\tDARRAY(obs_output_t *) outputs;\n\n\t/* stores the video/audio media output pointer.  video_t *or audio_t **/\n\tvoid *media;\n\t/* Stores the original video if GPU scaling is enabled and `media` can be overwritten. */\n\tvideo_t *original_video;\n\n\tpthread_mutex_t callbacks_mutex;\n\tDARRAY(struct encoder_callback) callbacks;\n\n\tDARRAY(struct encoder_packet_time) encoder_packet_times;\n\n\tstruct pause_data pause;\n\n\tconst char *profile_encoder_encode_name;\n\tchar *last_error_message;\n\n\t/* reconfigure encoder at next possible opportunity */\n\tbool reconfigure_requested;\n};\n\nextern struct obs_encoder_info *find_encoder(const char *id);\n\nextern bool obs_encoder_initialize(obs_encoder_t *encoder);\nextern void obs_encoder_shutdown(obs_encoder_t *encoder);\n\nextern void obs_encoder_start(obs_encoder_t *encoder, encoded_callback_t new_packet, void *param);\nextern void obs_encoder_stop(obs_encoder_t *encoder, encoded_callback_t new_packet, void *param);\n\nextern void obs_encoder_add_output(struct obs_encoder *encoder, struct obs_output *output);\nextern void obs_encoder_remove_output(struct obs_encoder *encoder, struct obs_output *output);\n\nextern bool start_gpu_encode(obs_encoder_t *encoder);\nextern void stop_gpu_encode(obs_encoder_t *encoder);\n\nextern bool do_encode(struct obs_encoder *encoder, struct encoder_frame *frame, const uint64_t *frame_cts);\nextern void send_off_encoder_packet(obs_encoder_t *encoder, bool success, bool received, struct encoder_packet *pkt);\n\nvoid obs_encoder_destroy(obs_encoder_t *encoder);\n\n/* ------------------------------------------------------------------------- */\n/* services */\n\nstruct obs_weak_service {\n\tstruct obs_weak_ref ref;\n\tstruct obs_service *service;\n};\n\nstruct obs_service {\n\tstruct obs_context_data context;\n\tstruct obs_service_info info;\n\n\t/* indicates ownership of the info.id buffer */\n\tbool owns_info_id;\n\n\tbool active;\n\tbool destroy;\n\tstruct obs_output *output;\n};\n\nextern const struct obs_service_info *find_service(const char *id);\n\nextern void obs_service_activate(struct obs_service *service);\nextern void obs_service_deactivate(struct obs_service *service, bool remove);\nextern bool obs_service_initialize(struct obs_service *service, struct obs_output *output);\n\nvoid obs_service_destroy(obs_service_t *service);\n\nvoid obs_output_remove_encoder_internal(struct obs_output *output, struct obs_encoder *encoder);\n\n/** Internal Source Profiler functions **/\n\n/* Start of frame in graphics loop */\nextern void source_profiler_frame_begin(void);\n/* Process data collected during frame */\nextern void source_profiler_frame_collect(void);\n\n/* Start/end of outputs being rendered (GPU timer begin/end) */\nextern void source_profiler_render_begin(void);\nextern void source_profiler_render_end(void);\n\n/* Reset settings, buffers, and GPU timers when video settings change */\nextern void source_profiler_reset_video(struct obs_video_info *ovi);\n\n/* Signal that source received an async frame */\nextern void source_profiler_async_frame_received(obs_source_t *source);\n\n/* Get timestamp for start of tick */\nextern uint64_t source_profiler_source_tick_start(void);\n/* Submit start timestamp for source */\nextern void source_profiler_source_tick_end(obs_source_t *source, uint64_t start);\n\n/* Obtain GPU timer and start timestamp for render start of a source. */\nextern uint64_t source_profiler_source_render_begin(gs_timer_t **timer);\n/* Submit start timestamp and GPU timer after rendering source */\nextern void source_profiler_source_render_end(obs_source_t *source, uint64_t start, gs_timer_t *timer);\n\n/* Remove source from profiler hashmaps */\nextern void source_profiler_remove_source(obs_source_t *source);\n"
  },
  {
    "path": "libobs/obs-missing-files.c",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"util/threading.h\"\n#include \"util/dstr.h\"\n#include \"obs-missing-files.h\"\n#include \"obs.h\"\n\nstruct obs_missing_file {\n\tvolatile long ref;\n\tchar *file_path;\n\tobs_missing_file_cb callback;\n\tint src_type;\n\tvoid *src;\n\tchar *src_name;\n\tvoid *data;\n};\n\nstruct obs_missing_files {\n\tDARRAY(struct obs_missing_file *) files;\n};\n\nobs_missing_files_t *obs_missing_files_create()\n{\n\tstruct obs_missing_files *files = bzalloc(sizeof(struct obs_missing_files));\n\n\treturn files;\n}\n\nvoid obs_missing_files_destroy(obs_missing_files_t *files)\n{\n\tfor (size_t i = 0; i < files->files.num; i++) {\n\t\tobs_missing_file_release(files->files.array[i]);\n\t}\n\n\tda_free(files->files);\n\tbfree(files);\n}\n\nvoid obs_missing_files_add_file(obs_missing_files_t *files, obs_missing_file_t *file)\n{\n\tda_insert(files->files, files->files.num, &file);\n}\n\nsize_t obs_missing_files_count(obs_missing_files_t *files)\n{\n\treturn files->files.num;\n}\n\nobs_missing_file_t *obs_missing_files_get_file(obs_missing_files_t *files, int idx)\n{\n\treturn files->files.array[idx];\n}\n\nvoid obs_missing_files_append(obs_missing_files_t *dst, obs_missing_files_t *src)\n{\n\tfor (size_t i = 0; i < src->files.num; i++) {\n\t\tobs_missing_file_t *file = src->files.array[i];\n\t\tobs_missing_files_add_file(dst, file);\n\t\tos_atomic_inc_long(&file->ref);\n\t}\n}\n\nobs_missing_file_t *obs_missing_file_create(const char *path, obs_missing_file_cb callback, int src_type, void *src,\n\t\t\t\t\t    void *data)\n{\n\tstruct obs_missing_file *file = bzalloc(sizeof(struct obs_missing_file));\n\n\tfile->file_path = bstrdup(path);\n\tfile->callback = callback;\n\tfile->src_type = src_type;\n\tfile->src = src;\n\tfile->data = data;\n\tfile->ref = 1;\n\n\tswitch (src_type) {\n\tcase OBS_MISSING_FILE_SOURCE:\n\t\tfile->src_name = bstrdup(obs_source_get_name(src));\n\t\tbreak;\n\tcase OBS_MISSING_FILE_SCRIPT:\n\t\tbreak;\n\t}\n\n\treturn file;\n}\n\nvoid obs_missing_file_release(obs_missing_file_t *file)\n{\n\tif (!file)\n\t\treturn;\n\n\tif (os_atomic_dec_long(&file->ref) == 0)\n\t\tobs_missing_file_destroy(file);\n}\n\nvoid obs_missing_file_destroy(obs_missing_file_t *file)\n{\n\tswitch (file->src_type) {\n\tcase OBS_MISSING_FILE_SOURCE:\n\t\tbfree(file->src_name);\n\t\tbreak;\n\tcase OBS_MISSING_FILE_SCRIPT:\n\t\tbreak;\n\t}\n\tbfree(file->file_path);\n\tbfree(file);\n}\n\nvoid obs_missing_file_issue_callback(obs_missing_file_t *file, const char *new_path)\n{\n\tswitch (file->src_type) {\n\tcase OBS_MISSING_FILE_SOURCE:\n\t\tobs_source_replace_missing_file(file->callback, (obs_source_t *)file->src, new_path, file->data);\n\t\tbreak;\n\tcase OBS_MISSING_FILE_SCRIPT:\n\t\tbreak;\n\t}\n}\n\nconst char *obs_missing_file_get_path(obs_missing_file_t *file)\n{\n\treturn file->file_path;\n}\n\nconst char *obs_missing_file_get_source_name(obs_missing_file_t *file)\n{\n\treturn file->src_name;\n}\n"
  },
  {
    "path": "libobs/obs-missing-files.h",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Dillon Pentz <dillon@vodbox.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"util/c99defs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef void (*obs_missing_file_cb)(void *src, const char *new_path, void *data);\n\nstruct obs_missing_file;\nstruct obs_missing_files;\ntypedef struct obs_missing_file obs_missing_file_t;\ntypedef struct obs_missing_files obs_missing_files_t;\n\nenum obs_missing_file_src { OBS_MISSING_FILE_SOURCE, OBS_MISSING_FILE_SCRIPT };\n\nEXPORT obs_missing_files_t *obs_missing_files_create();\nEXPORT obs_missing_file_t *obs_missing_file_create(const char *path, obs_missing_file_cb callback, int src_type,\n\t\t\t\t\t\t   void *src, void *data);\n\nEXPORT void obs_missing_files_add_file(obs_missing_files_t *files, obs_missing_file_t *file);\nEXPORT size_t obs_missing_files_count(obs_missing_files_t *files);\nEXPORT obs_missing_file_t *obs_missing_files_get_file(obs_missing_files_t *files, int idx);\nEXPORT void obs_missing_files_destroy(obs_missing_files_t *files);\nEXPORT void obs_missing_files_append(obs_missing_files_t *dst, obs_missing_files_t *src);\n\nEXPORT void obs_missing_file_issue_callback(obs_missing_file_t *file, const char *new_path);\nEXPORT const char *obs_missing_file_get_path(obs_missing_file_t *file);\nEXPORT const char *obs_missing_file_get_source_name(obs_missing_file_t *file);\nEXPORT void obs_missing_file_release(obs_missing_file_t *file);\nEXPORT void obs_missing_file_destroy(obs_missing_file_t *file);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-module.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"util/platform.h\"\n#include \"util/dstr.h\"\n\n#include \"obs-defs.h\"\n#include \"obs-internal.h\"\n#include \"obs-module.h\"\n\nextern const char *get_module_extension(void);\n\nobs_module_t *loadingModule = NULL;\n\nstatic inline int req_func_not_found(const char *name, const char *path)\n{\n\tblog(LOG_DEBUG,\n\t     \"Required module function '%s' in module '%s' not \"\n\t     \"found, loading of module failed\",\n\t     name, path);\n\treturn MODULE_MISSING_EXPORTS;\n}\n\nstatic int load_module_exports(struct obs_module *mod, const char *path)\n{\n\tmod->load = os_dlsym(mod->module, \"obs_module_load\");\n\tif (!mod->load)\n\t\treturn req_func_not_found(\"obs_module_load\", path);\n\n\tmod->set_pointer = os_dlsym(mod->module, \"obs_module_set_pointer\");\n\tif (!mod->set_pointer)\n\t\treturn req_func_not_found(\"obs_module_set_pointer\", path);\n\n\tmod->ver = os_dlsym(mod->module, \"obs_module_ver\");\n\tif (!mod->ver)\n\t\treturn req_func_not_found(\"obs_module_ver\", path);\n\n\t/* optional exports */\n\tmod->unload = os_dlsym(mod->module, \"obs_module_unload\");\n\tmod->post_load = os_dlsym(mod->module, \"obs_module_post_load\");\n\tmod->set_locale = os_dlsym(mod->module, \"obs_module_set_locale\");\n\tmod->free_locale = os_dlsym(mod->module, \"obs_module_free_locale\");\n\tmod->name = os_dlsym(mod->module, \"obs_module_name\");\n\tmod->description = os_dlsym(mod->module, \"obs_module_description\");\n\tmod->author = os_dlsym(mod->module, \"obs_module_author\");\n\tmod->get_string = os_dlsym(mod->module, \"obs_module_get_string\");\n\treturn MODULE_SUCCESS;\n}\n\nbool obs_module_get_locale_string(const obs_module_t *mod, const char *lookup_string, const char **translated_string)\n{\n\tif (mod->get_string) {\n\t\treturn mod->get_string(lookup_string, translated_string);\n\t}\n\n\treturn false;\n}\n\nconst char *obs_module_get_locale_text(const obs_module_t *mod, const char *text)\n{\n\tconst char *str = text;\n\tobs_module_get_locale_string(mod, text, &str);\n\treturn str;\n}\n\nstatic inline char *get_module_name(const char *file)\n{\n\tstatic size_t ext_len = 0;\n\tstruct dstr name = {0};\n\n\tif (ext_len == 0) {\n\t\tconst char *ext = get_module_extension();\n\t\text_len = strlen(ext);\n\t}\n\n\tdstr_copy(&name, file);\n\tdstr_resize(&name, name.len - ext_len);\n\treturn name.array;\n}\n\n#ifdef _WIN32\nextern void reset_win32_symbol_paths(void);\n#endif\n\nint obs_module_load_metadata(struct obs_module *mod)\n{\n\tstruct obs_module_metadata *md = NULL;\n\n\t/* Check if the metadata file exists */\n\tstruct dstr path = {0};\n\n\tdstr_copy(&path, mod->data_path);\n\tif (!dstr_is_empty(&path) && dstr_end(&path) != '/') {\n\t\tdstr_cat_ch(&path, '/');\n\t}\n\tdstr_cat(&path, \"manifest.json\");\n\n\tif (os_file_exists(path.array)) {\n\t\t/* If we find a metadata file, allocate a new metadata. */\n\t\tmd = bmalloc(sizeof(obs_module_metadata_t));\n\t\tobs_data_t *metadata = obs_data_create_from_json_file(path.array);\n\n\t\tmd->display_name = bstrdup(obs_data_get_string(metadata, \"display_name\"));\n\t\tmd->id = bstrdup(obs_data_get_string(metadata, \"id\"));\n\t\tmd->version = bstrdup(obs_data_get_string(metadata, \"version\"));\n\t\tmd->os_arch = bstrdup(obs_data_get_string(metadata, \"os_arch\"));\n\t\tmd->name = bstrdup(obs_data_get_string(metadata, \"name\"));\n\t\tmd->description = bstrdup(obs_data_get_string(metadata, \"description\"));\n\t\tmd->long_description = bstrdup(obs_data_get_string(metadata, \"long_description\"));\n\n\t\tobs_data_t *urls = obs_data_get_obj(metadata, \"urls\");\n\t\tmd->repository_url = bstrdup(obs_data_get_string(urls, \"repository\"));\n\t\tmd->website_url = bstrdup(obs_data_get_string(urls, \"website\"));\n\t\tmd->support_url = bstrdup(obs_data_get_string(urls, \"support\"));\n\t\tobs_data_release(urls);\n\n\t\tmd->has_banner = obs_data_get_bool(metadata, \"has_banner\");\n\t\tmd->has_icon = obs_data_get_bool(metadata, \"has_icon\");\n\t\tobs_data_release(metadata);\n\t}\n\tdstr_free(&path);\n\tmod->metadata = md;\n\treturn MODULE_SUCCESS;\n}\n\nint obs_open_module(obs_module_t **module, const char *path, const char *data_path)\n{\n\tstruct obs_module mod = {0};\n\tint errorcode;\n\n\tif (!module || !path || !obs)\n\t\treturn MODULE_ERROR;\n\n#ifdef __APPLE__\n\t/* HACK: Do not load obsolete obs-browser build on macOS; the\n\t * obs-browser plugin used to live in the Application Support\n\t * directory. */\n\tif (astrstri(path, \"Library/Application Support/obs-studio\") != NULL && astrstri(path, \"obs-browser\") != NULL) {\n\t\tblog(LOG_WARNING, \"Ignoring old obs-browser.so version\");\n\t\treturn MODULE_HARDCODED_SKIP;\n\t}\n#endif\n\n\tblog(LOG_DEBUG, \"---------------------------------\");\n\n\tmod.module = os_dlopen(path);\n\tif (!mod.module) {\n\t\tblog(LOG_WARNING, \"Module '%s' not loaded\", path);\n\t\treturn MODULE_FAILED_TO_OPEN;\n\t}\n\n\terrorcode = load_module_exports(&mod, path);\n\tif (errorcode != MODULE_SUCCESS)\n\t\treturn errorcode;\n\n\t/* Reject plugins compiled with a newer libobs. Patch version (lower 16-bit) is ignored. */\n\tuint32_t ver = mod.ver ? mod.ver() & 0xFFFF0000 : 0;\n\tif (ver > LIBOBS_API_VER) {\n\t\tblog(LOG_WARNING, \"Module '%s' compiled with newer libobs %d.%d\", path, (ver >> 24) & 0xFF,\n\t\t     (ver >> 16) & 0xFF);\n\t\treturn MODULE_INCOMPATIBLE_VER;\n\t}\n\n\tmod.bin_path = bstrdup(path);\n\tmod.file = strrchr(mod.bin_path, '/');\n\tmod.file = (!mod.file) ? mod.bin_path : (mod.file + 1);\n\tmod.mod_name = get_module_name(mod.file);\n\tmod.data_path = bstrdup(data_path);\n\tmod.next = obs->first_module;\n\tmod.load_state = OBS_MODULE_ENABLED;\n\n\tda_init(mod.sources);\n\tda_init(mod.outputs);\n\tda_init(mod.encoders);\n\tda_init(mod.services);\n\n\tif (mod.file) {\n\t\tblog(LOG_DEBUG, \"Loading module: %s\", mod.file);\n\t}\n\n\tobs_module_load_metadata(&mod);\n\n\t*module = bmemdup(&mod, sizeof(mod));\n\tobs->first_module = (*module);\n\tmod.set_pointer(*module);\n\n\tif (mod.set_locale)\n\t\tmod.set_locale(obs->locale);\n\n\treturn MODULE_SUCCESS;\n}\n\nbool obs_create_disabled_module(obs_module_t **module, const char *path, const char *data_path,\n\t\t\t\tenum obs_module_load_state state)\n{\n\tstruct obs_module mod = {0};\n\n\tmod.bin_path = bstrdup(path);\n\tmod.file = strrchr(mod.bin_path, '/');\n\tmod.file = (!mod.file) ? mod.bin_path : (mod.file + 1);\n\tmod.mod_name = get_module_name(mod.file);\n\tmod.data_path = bstrdup(data_path);\n\tmod.next = obs->first_disabled_module;\n\tmod.load_state = state;\n\n\tda_init(mod.sources);\n\tda_init(mod.outputs);\n\tda_init(mod.encoders);\n\tda_init(mod.services);\n\n\tobs_module_load_metadata(&mod);\n\n\t*module = bmemdup(&mod, sizeof(mod));\n\tobs->first_disabled_module = (*module);\n\n\treturn true;\n}\n\nbool obs_init_module(obs_module_t *module)\n{\n\tif (!module || !obs)\n\t\treturn false;\n\tif (module->loaded)\n\t\treturn true;\n\n\tconst char *profile_name =\n\t\tprofile_store_name(obs_get_profiler_name_store(), \"obs_init_module(%s)\", module->file);\n\tprofile_start(profile_name);\n\n\tloadingModule = module;\n\tmodule->loaded = module->load();\n\tloadingModule = NULL;\n\n\tif (!module->loaded)\n\t\tblog(LOG_WARNING, \"Failed to initialize module '%s'\", module->file);\n\n\tprofile_end(profile_name);\n\treturn module->loaded;\n}\n\nvoid obs_log_loaded_modules(void)\n{\n\tblog(LOG_INFO, \"  Loaded Modules:\");\n\n\tfor (obs_module_t *mod = obs->first_module; !!mod; mod = mod->next)\n\t\tblog(LOG_INFO, \"    %s\", mod->file);\n}\n\nconst char *obs_get_module_file_name(obs_module_t *module)\n{\n\treturn module ? module->file : NULL;\n}\n\nconst char *obs_get_module_name(obs_module_t *module)\n{\n\tif (module && module->metadata && module->metadata->display_name) {\n\t\treturn module->metadata->display_name;\n\t}\n\n\treturn (module && module->name) ? module->name() : NULL;\n}\n\nconst char *obs_get_module_author(obs_module_t *module)\n{\n\treturn (module && module->author) ? module->author() : NULL;\n}\n\nconst char *obs_get_module_description(obs_module_t *module)\n{\n\treturn (module && module->description) ? module->description() : NULL;\n}\n\nconst char *obs_get_module_binary_path(obs_module_t *module)\n{\n\treturn module ? module->bin_path : NULL;\n}\n\nconst char *obs_get_module_data_path(obs_module_t *module)\n{\n\treturn module ? module->data_path : NULL;\n}\n\nconst char *obs_get_module_id(obs_module_t *module)\n{\n\treturn module && module->metadata ? module->metadata->id : NULL;\n}\n\nconst char *obs_get_module_version(obs_module_t *module)\n{\n\treturn module && module->metadata ? module->metadata->version : NULL;\n}\n\nvoid obs_module_add_source(obs_module_t *module, const char *id)\n{\n\tchar *source_id = bstrdup(id);\n\tif (module) {\n\t\tda_push_back(module->sources, &source_id);\n\t}\n}\n\nvoid obs_module_add_output(obs_module_t *module, const char *id)\n{\n\tchar *output_id = bstrdup(id);\n\tif (module) {\n\t\tda_push_back(module->outputs, &output_id);\n\t}\n}\n\nvoid obs_module_add_encoder(obs_module_t *module, const char *id)\n{\n\tchar *encoder_id = bstrdup(id);\n\tif (module) {\n\t\tda_push_back(module->encoders, &encoder_id);\n\t}\n}\n\nvoid obs_module_add_service(obs_module_t *module, const char *id)\n{\n\tchar *service_id = bstrdup(id);\n\tif (module) {\n\t\tda_push_back(module->services, &service_id);\n\t}\n}\n\nobs_module_t *obs_get_module(const char *name)\n{\n\tobs_module_t *module = obs->first_module;\n\twhile (module) {\n\t\tif (strcmp(module->mod_name, name) == 0) {\n\t\t\treturn module;\n\t\t}\n\n\t\tmodule = module->next;\n\t}\n\n\treturn NULL;\n}\n\nobs_module_t *obs_get_disabled_module(const char *name)\n{\n\tobs_module_t *module = obs->first_disabled_module;\n\twhile (module) {\n\t\tif (strcmp(module->mod_name, name) == 0) {\n\t\t\treturn module;\n\t\t}\n\n\t\tmodule = module->next;\n\t}\n\n\treturn NULL;\n}\n\nvoid *obs_get_module_lib(obs_module_t *module)\n{\n\treturn module ? module->module : NULL;\n}\n\nchar *obs_find_module_file(obs_module_t *module, const char *file)\n{\n\tstruct dstr output = {0};\n\n\tif (!file)\n\t\tfile = \"\";\n\n\tif (!module)\n\t\treturn NULL;\n\n\tdstr_copy(&output, module->data_path);\n\tif (!dstr_is_empty(&output) && dstr_end(&output) != '/' && *file)\n\t\tdstr_cat_ch(&output, '/');\n\tdstr_cat(&output, file);\n\n\tif (!os_file_exists(output.array))\n\t\tdstr_free(&output);\n\treturn output.array;\n}\n\nchar *obs_module_get_config_path(obs_module_t *module, const char *file)\n{\n\tstruct dstr output = {0};\n\n\tdstr_copy(&output, obs->module_config_path);\n\tif (!dstr_is_empty(&output) && dstr_end(&output) != '/')\n\t\tdstr_cat_ch(&output, '/');\n\tdstr_cat(&output, module->mod_name);\n\tdstr_cat_ch(&output, '/');\n\tdstr_cat(&output, file);\n\n\treturn output.array;\n}\n\nvoid obs_add_module_path(const char *bin, const char *data)\n{\n\tstruct obs_module_path omp;\n\n\tif (!obs || !bin || !data)\n\t\treturn;\n\n\tomp.bin = bstrdup(bin);\n\tomp.data = bstrdup(data);\n\tda_push_back(obs->module_paths, &omp);\n}\n\nvoid obs_add_safe_module(const char *name)\n{\n\tif (!obs || !name)\n\t\treturn;\n\n\tchar *item = bstrdup(name);\n\tda_push_back(obs->safe_modules, &item);\n}\n\nvoid obs_add_core_module(const char *name)\n{\n\tif (!obs || !name)\n\t\treturn;\n\n\tchar *item = bstrdup(name);\n\tda_push_back(obs->core_modules, &item);\n}\n\nvoid obs_add_disabled_module(const char *name)\n{\n\tif (!obs || !name)\n\t\treturn;\n\n\tchar *item = bstrdup(name);\n\tda_push_back(obs->disabled_modules, &item);\n}\n\nextern void get_plugin_info(const char *path, bool *is_obs_plugin);\n\nstruct fail_info {\n\tstruct dstr fail_modules;\n\tsize_t fail_count;\n};\n\nstatic bool is_safe_module(const char *name)\n{\n\tif (!obs->safe_modules.num)\n\t\treturn true;\n\n\tfor (size_t i = 0; i < obs->safe_modules.num; i++) {\n\t\tif (strcmp(name, obs->safe_modules.array[i]) == 0)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic bool is_core_module(const char *name)\n{\n\tfor (size_t i = 0; i < obs->core_modules.num; i++) {\n\t\tif (strcmp(name, obs->core_modules.array[i]) == 0)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic bool is_disabled_module(const char *name)\n{\n\tif (obs->disabled_modules.num == 0)\n\t\treturn false;\n\n\tfor (size_t i = 0; i < obs->disabled_modules.num; i++) {\n\t\tif (strcmp(name, obs->disabled_modules.array[i]) == 0)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool obs_get_module_allow_disable(const char *name)\n{\n\treturn !is_core_module(name);\n}\n\nstatic void load_all_callback(void *param, const struct obs_module_info2 *info)\n{\n\tstruct fail_info *fail_info = param;\n\tobs_module_t *module;\n\tobs_module_t *disabled_module;\n\n\tbool is_obs_plugin;\n\n\tget_plugin_info(info->bin_path, &is_obs_plugin);\n\n\tif (!is_obs_plugin) {\n\t\tblog(LOG_WARNING, \"Skipping module '%s', not an OBS plugin\", info->bin_path);\n\t\treturn;\n\t}\n\n\tif (!is_safe_module(info->name)) {\n\t\tobs_create_disabled_module(&disabled_module, info->bin_path, info->data_path, OBS_MODULE_DISABLED_SAFE);\n\t\tblog(LOG_WARNING, \"Skipping module '%s', not on safe list\", info->name);\n\t\treturn;\n\t}\n\n\tif (is_disabled_module(info->name)) {\n\t\tobs_create_disabled_module(&disabled_module, info->bin_path, info->data_path, OBS_MODULE_DISABLED);\n\t\tblog(LOG_WARNING, \"Skipping module '%s', is disabled\", info->name);\n\t\treturn;\n\t}\n\n\tint code = obs_open_module(&module, info->bin_path, info->data_path);\n\tswitch (code) {\n\tcase MODULE_MISSING_EXPORTS:\n\t\tblog(LOG_DEBUG, \"Failed to load module file '%s', not an OBS plugin\", info->bin_path);\n\t\treturn;\n\tcase MODULE_FAILED_TO_OPEN:\n\t\tblog(LOG_DEBUG, \"Failed to load module file '%s', module failed to open\", info->bin_path);\n\t\tobs_create_disabled_module(&disabled_module, info->bin_path, info->data_path,\n\t\t\t\t\t   OBS_MODULE_FAILED_TO_OPEN);\n\t\tgoto load_failure;\n\tcase MODULE_ERROR:\n\t\tblog(LOG_DEBUG, \"Failed to load module file '%s' (unknown error)\", info->bin_path);\n\t\tgoto load_failure;\n\tcase MODULE_INCOMPATIBLE_VER:\n\t\tblog(LOG_DEBUG, \"Failed to load module file '%s', incompatible version\", info->bin_path);\n\t\tobs_create_disabled_module(&disabled_module, info->bin_path, info->data_path,\n\t\t\t\t\t   OBS_MODULE_FAILED_TO_OPEN);\n\t\tgoto load_failure;\n\tcase MODULE_HARDCODED_SKIP:\n\t\treturn;\n\t}\n\n\tif (!obs_init_module(module)) {\n\t\tfree_module(module);\n\t\tobs_create_disabled_module(&disabled_module, info->bin_path, info->data_path,\n\t\t\t\t\t   OBS_MODULE_FAILED_TO_INITIALIZE);\n\t}\n\n\tUNUSED_PARAMETER(param);\n\treturn;\n\nload_failure:\n\tif (fail_info) {\n\t\tdstr_cat(&fail_info->fail_modules, info->name);\n\t\tdstr_cat(&fail_info->fail_modules, \";\");\n\t\tfail_info->fail_count++;\n\t}\n}\n\nstatic const char *obs_load_all_modules_name = \"obs_load_all_modules\";\n#ifdef _WIN32\nstatic const char *reset_win32_symbol_paths_name = \"reset_win32_symbol_paths\";\n#endif\n\nvoid obs_load_all_modules(void)\n{\n\tprofile_start(obs_load_all_modules_name);\n\tobs_find_modules2(load_all_callback, NULL);\n#ifdef _WIN32\n\tprofile_start(reset_win32_symbol_paths_name);\n\treset_win32_symbol_paths();\n\tprofile_end(reset_win32_symbol_paths_name);\n#endif\n\tprofile_end(obs_load_all_modules_name);\n}\n\nstatic const char *obs_load_all_modules2_name = \"obs_load_all_modules2\";\n\nvoid obs_load_all_modules2(struct obs_module_failure_info *mfi)\n{\n\tstruct fail_info fail_info = {0};\n\tmemset(mfi, 0, sizeof(*mfi));\n\n\tprofile_start(obs_load_all_modules2_name);\n\tobs_find_modules2(load_all_callback, &fail_info);\n#ifdef _WIN32\n\tprofile_start(reset_win32_symbol_paths_name);\n\treset_win32_symbol_paths();\n\tprofile_end(reset_win32_symbol_paths_name);\n#endif\n\tprofile_end(obs_load_all_modules2_name);\n\n\tmfi->count = fail_info.fail_count;\n\tmfi->failed_modules = strlist_split(fail_info.fail_modules.array, ';', false);\n\tdstr_free(&fail_info.fail_modules);\n}\n\nvoid obs_module_failure_info_free(struct obs_module_failure_info *mfi)\n{\n\tif (mfi->failed_modules) {\n\t\tbfree(mfi->failed_modules);\n\t\tmfi->failed_modules = NULL;\n\t}\n}\n\nvoid obs_post_load_modules(void)\n{\n\tfor (obs_module_t *mod = obs->first_module; !!mod; mod = mod->next)\n\t\tif (mod->post_load)\n\t\t\tmod->post_load();\n}\n\nstatic inline void make_data_dir(struct dstr *parsed_data_dir, const char *data_dir, const char *name)\n{\n\tdstr_copy(parsed_data_dir, data_dir);\n\tdstr_replace(parsed_data_dir, \"%module%\", name);\n\tif (dstr_end(parsed_data_dir) == '/')\n\t\tdstr_resize(parsed_data_dir, parsed_data_dir->len - 1);\n}\n\nstatic char *make_data_directory(const char *module_name, const char *data_dir)\n{\n\tstruct dstr parsed_data_dir = {0};\n\tbool found = false;\n\n\tmake_data_dir(&parsed_data_dir, data_dir, module_name);\n\n\tfound = os_file_exists(parsed_data_dir.array);\n\n\tif (!found && astrcmpi_n(module_name, \"lib\", 3) == 0)\n\t\tmake_data_dir(&parsed_data_dir, data_dir, module_name + 3);\n\n\treturn parsed_data_dir.array;\n}\n\nstatic bool parse_binary_from_directory(struct dstr *parsed_bin_path, const char *bin_path, const char *file)\n{\n\tstruct dstr directory = {0};\n\tbool found = true;\n\n\tdstr_copy(&directory, bin_path);\n\tdstr_replace(&directory, \"%module%\", file);\n\tif (dstr_end(&directory) != '/')\n\t\tdstr_cat_ch(&directory, '/');\n\n\tdstr_copy_dstr(parsed_bin_path, &directory);\n\tdstr_cat(parsed_bin_path, file);\n#ifdef __APPLE__\n\tif (!os_file_exists(parsed_bin_path->array)) {\n\t\tdstr_cat(parsed_bin_path, \".so\");\n\t}\n#else\n\tdstr_cat(parsed_bin_path, get_module_extension());\n#endif\n\n\tif (!os_file_exists(parsed_bin_path->array)) {\n\t\t/* Legacy fallback: Check for plugin with .so suffix*/\n\t\tdstr_cat(parsed_bin_path, \".so\");\n\t\t/* if the file doesn't exist, check with 'lib' prefix */\n\t\tdstr_copy_dstr(parsed_bin_path, &directory);\n\t\tdstr_cat(parsed_bin_path, \"lib\");\n\t\tdstr_cat(parsed_bin_path, file);\n\t\tdstr_cat(parsed_bin_path, get_module_extension());\n\n\t\t/* if neither exist, don't include this as a library */\n\t\tif (!os_file_exists(parsed_bin_path->array)) {\n\t\t\tdstr_free(parsed_bin_path);\n\t\t\tfound = false;\n\t\t}\n\t}\n\n\tdstr_free(&directory);\n\treturn found;\n}\n\nstatic void process_found_module(struct obs_module_path *omp, const char *path, bool directory,\n\t\t\t\t obs_find_module_callback2_t callback, void *param)\n{\n\tstruct obs_module_info2 info;\n\tstruct dstr name = {0};\n\tstruct dstr parsed_bin_path = {0};\n\tconst char *file;\n\tchar *parsed_data_dir;\n\tbool bin_found = true;\n\n\tfile = strrchr(path, '/');\n\tfile = file ? (file + 1) : path;\n\n\tif (strcmp(file, \".\") == 0 || strcmp(file, \"..\") == 0)\n\t\treturn;\n\n\tdstr_copy(&name, file);\n\tchar *ext = strrchr(name.array, '.');\n\tif (ext)\n\t\tdstr_resize(&name, ext - name.array);\n\n\tif (!directory) {\n\t\tdstr_copy(&parsed_bin_path, path);\n\t} else {\n\t\tbin_found = parse_binary_from_directory(&parsed_bin_path, omp->bin, name.array);\n\t}\n\n\tparsed_data_dir = make_data_directory(name.array, omp->data);\n\n\tif (parsed_data_dir && bin_found) {\n\t\tinfo.bin_path = parsed_bin_path.array;\n\t\tinfo.data_path = parsed_data_dir;\n\t\tinfo.name = name.array;\n\t\tcallback(param, &info);\n\t}\n\n\tbfree(parsed_data_dir);\n\tdstr_free(&name);\n\tdstr_free(&parsed_bin_path);\n}\n\nstatic void find_modules_in_path(struct obs_module_path *omp, obs_find_module_callback2_t callback, void *param)\n{\n\tstruct dstr search_path = {0};\n\tchar *module_start;\n\tbool search_directories = false;\n\tos_glob_t *gi;\n\n\tdstr_copy(&search_path, omp->bin);\n\n\tmodule_start = strstr(search_path.array, \"%module%\");\n\tif (module_start) {\n\t\tdstr_resize(&search_path, module_start - search_path.array);\n\t\tsearch_directories = true;\n\t}\n\n\tif (!dstr_is_empty(&search_path) && dstr_end(&search_path) != '/')\n\t\tdstr_cat_ch(&search_path, '/');\n\n\tdstr_cat_ch(&search_path, '*');\n\tif (!search_directories)\n\t\tdstr_cat(&search_path, get_module_extension());\n\n\tif (os_glob(search_path.array, 0, &gi) == 0) {\n\t\tfor (size_t i = 0; i < gi->gl_pathc; i++) {\n\t\t\tif (search_directories == gi->gl_pathv[i].directory)\n\t\t\t\tprocess_found_module(omp, gi->gl_pathv[i].path, search_directories, callback, param);\n\t\t}\n\n\t\tos_globfree(gi);\n\t}\n\n\tdstr_free(&search_path);\n}\n\nvoid obs_find_modules2(obs_find_module_callback2_t callback, void *param)\n{\n\tif (!obs)\n\t\treturn;\n\n\tfor (size_t i = 0; i < obs->module_paths.num; i++) {\n\t\tstruct obs_module_path *omp = obs->module_paths.array + i;\n\t\tfind_modules_in_path(omp, callback, param);\n\t}\n}\n\nvoid obs_find_modules(obs_find_module_callback_t callback, void *param)\n{\n\t/* the structure is ABI compatible so we can just cast the callback */\n\tobs_find_modules2((obs_find_module_callback2_t)callback, param);\n}\n\nvoid obs_enum_modules(obs_enum_module_callback_t callback, void *param)\n{\n\tstruct obs_module *module;\n\tif (!obs)\n\t\treturn;\n\n\tmodule = obs->first_module;\n\twhile (module) {\n\t\tcallback(param, module);\n\t\tmodule = module->next;\n\t}\n}\n\nvoid free_module(struct obs_module *mod)\n{\n\tif (!mod)\n\t\treturn;\n\n\tif (mod->module) {\n\t\tif (mod->free_locale)\n\t\t\tmod->free_locale();\n\n\t\tif (mod->loaded && mod->unload)\n\t\t\tmod->unload();\n\n\t\t/* there is no real reason to close the dynamic libraries,\n\t\t * and sometimes this can cause issues. */\n\t\t/* os_dlclose(mod->module); */\n\t}\n\n\t/* Is this module an active / loaded module, or a disabled module? */\n\tif (mod->load_state == OBS_MODULE_ENABLED) {\n\t\tfor (obs_module_t *m = obs->first_module; !!m; m = m->next) {\n\t\t\tif (m->next == mod) {\n\t\t\t\tm->next = mod->next;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (obs->first_module == mod)\n\t\t\tobs->first_module = mod->next;\n\t} else {\n\t\tfor (obs_module_t *m = obs->first_disabled_module; !!m; m = m->next) {\n\t\t\tif (m->next == mod) {\n\t\t\t\tm->next = mod->next;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (obs->first_disabled_module == mod)\n\t\t\tobs->first_disabled_module = mod->next;\n\t}\n\n\tbfree(mod->mod_name);\n\tbfree(mod->bin_path);\n\tbfree(mod->data_path);\n\n\tfor (size_t i = 0; i < mod->sources.num; i++) {\n\t\tbfree(mod->sources.array[i]);\n\t}\n\tda_free(mod->sources);\n\n\tfor (size_t i = 0; i < mod->outputs.num; i++) {\n\t\tbfree(mod->outputs.array[i]);\n\t}\n\tda_free(mod->outputs);\n\n\tfor (size_t i = 0; i < mod->encoders.num; i++) {\n\t\tbfree(mod->encoders.array[i]);\n\t}\n\tda_free(mod->encoders);\n\n\tfor (size_t i = 0; i < mod->services.num; i++) {\n\t\tbfree(mod->services.array[i]);\n\t}\n\tda_free(mod->services);\n\n\tif (mod->metadata) {\n\t\tfree_module_metadata(mod->metadata);\n\t\tbfree(mod->metadata);\n\t}\n\tbfree(mod);\n}\n\nlookup_t *obs_module_load_locale(obs_module_t *module, const char *default_locale, const char *locale)\n{\n\tstruct dstr str = {0};\n\tlookup_t *lookup = NULL;\n\n\tif (!module || !default_locale || !locale) {\n\t\tblog(LOG_WARNING, \"obs_module_load_locale: Invalid parameters\");\n\t\treturn NULL;\n\t}\n\n\tdstr_copy(&str, \"locale/\");\n\tdstr_cat(&str, default_locale);\n\tdstr_cat(&str, \".ini\");\n\n\tchar *file = obs_find_module_file(module, str.array);\n\tif (file)\n\t\tlookup = text_lookup_create(file);\n\n\tbfree(file);\n\n\tif (!lookup) {\n\t\tblog(LOG_WARNING, \"Failed to load '%s' text for module: '%s'\", default_locale, module->file);\n\t\tgoto cleanup;\n\t}\n\n\tif (astrcmpi(locale, default_locale) == 0)\n\t\tgoto cleanup;\n\n\tdstr_copy(&str, \"/locale/\");\n\tdstr_cat(&str, locale);\n\tdstr_cat(&str, \".ini\");\n\n\tfile = obs_find_module_file(module, str.array);\n\n\tif (!text_lookup_add(lookup, file))\n\t\tblog(LOG_WARNING, \"Failed to load '%s' text for module: '%s'\", locale, module->file);\n\n\tbfree(file);\ncleanup:\n\tdstr_free(&str);\n\treturn lookup;\n}\n\n#define REGISTER_OBS_DEF(size_var, structure, dest, info)                                               \\\n\tdo {                                                                                            \\\n\t\tstruct structure data = {0};                                                            \\\n\t\tif (!size_var) {                                                                        \\\n\t\t\tblog(LOG_ERROR, \"Tried to register \" #structure \" outside of obs_module_load\"); \\\n\t\t\treturn;                                                                         \\\n\t\t}                                                                                       \\\n                                                                                                        \\\n\t\tif (size_var > sizeof(data)) {                                                          \\\n\t\t\tblog(LOG_ERROR,                                                                 \\\n\t\t\t     \"Tried to register \" #structure \" with size %llu which is more \"           \\\n\t\t\t     \"than libobs currently supports \"                                          \\\n\t\t\t     \"(%llu)\",                                                                  \\\n\t\t\t     (long long unsigned)size_var, (long long unsigned)sizeof(data));           \\\n\t\t\tgoto error;                                                                     \\\n\t\t}                                                                                       \\\n                                                                                                        \\\n\t\tmemcpy(&data, info, size_var);                                                          \\\n\t\tda_push_back(dest, &data);                                                              \\\n\t} while (false)\n\n#define HAS_VAL(type, info, val) ((offsetof(type, val) + sizeof(info->val) <= size) && info->val)\n\n#define CHECK_REQUIRED_VAL(type, info, val, func)                  \\\n\tdo {                                                       \\\n\t\tif (!HAS_VAL(type, info, val)) {                   \\\n\t\t\tblog(LOG_ERROR,                            \\\n\t\t\t     \"Required value '\" #val \"' for \"      \\\n\t\t\t     \"'%s' not found.  \" #func \" failed.\", \\\n\t\t\t     info->id);                            \\\n\t\t\tgoto error;                                \\\n\t\t}                                                  \\\n\t} while (false)\n\n#define CHECK_REQUIRED_VAL_EITHER(type, info, val1, val2, func)                 \\\n\tdo {                                                                    \\\n\t\tif (!HAS_VAL(type, info, val1) && !HAS_VAL(type, info, val2)) { \\\n\t\t\tblog(LOG_ERROR,                                         \\\n\t\t\t     \"Neither '\" #val1 \"' nor '\" #val2 \"' \"             \\\n\t\t\t     \"for '%s' found.  \" #func \" failed.\",              \\\n\t\t\t     info->id);                                         \\\n\t\t\tgoto error;                                             \\\n\t\t}                                                               \\\n\t} while (false)\n\n#define HANDLE_ERROR(size_var, structure, info)                                         \\\n\tdo {                                                                            \\\n\t\tstruct structure data = {0};                                            \\\n\t\tif (!size_var)                                                          \\\n\t\t\treturn;                                                         \\\n                                                                                        \\\n\t\tmemcpy(&data, info, sizeof(data) < size_var ? sizeof(data) : size_var); \\\n                                                                                        \\\n\t\tif (data.type_data && data.free_type_data)                              \\\n\t\t\tdata.free_type_data(data.type_data);                            \\\n\t} while (false)\n\n#define source_warn(format, ...) blog(LOG_WARNING, \"obs_register_source: \" format, ##__VA_ARGS__)\n#define output_warn(format, ...) blog(LOG_WARNING, \"obs_register_output: \" format, ##__VA_ARGS__)\n#define encoder_warn(format, ...) blog(LOG_WARNING, \"obs_register_encoder: \" format, ##__VA_ARGS__)\n#define service_warn(format, ...) blog(LOG_WARNING, \"obs_register_service: \" format, ##__VA_ARGS__)\n\nvoid obs_register_source_s(const struct obs_source_info *info, size_t size)\n{\n\tstruct obs_source_info data = {0};\n\tobs_source_info_array_t *array = NULL;\n\n\tif (info->type == OBS_SOURCE_TYPE_INPUT) {\n\t\tarray = &obs->input_types;\n\t} else if (info->type == OBS_SOURCE_TYPE_FILTER) {\n\t\tarray = &obs->filter_types;\n\t} else if (info->type == OBS_SOURCE_TYPE_TRANSITION) {\n\t\tarray = &obs->transition_types;\n\t} else if (info->type != OBS_SOURCE_TYPE_SCENE) {\n\t\tsource_warn(\"Tried to register unknown source type: %u\", info->type);\n\t\tgoto error;\n\t}\n\n\tif (get_source_info2(info->id, info->version)) {\n\t\tsource_warn(\"Source '%s' already exists!  \"\n\t\t\t    \"Duplicate library?\",\n\t\t\t    info->id);\n\t\tgoto error;\n\t}\n\n\tif (size > sizeof(data)) {\n\t\tsource_warn(\"Tried to register obs_source_info with size \"\n\t\t\t    \"%llu which is more than libobs currently \"\n\t\t\t    \"supports (%llu)\",\n\t\t\t    (long long unsigned)size, (long long unsigned)sizeof(data));\n\t\tgoto error;\n\t}\n\n\t/* NOTE: The assignment of data.module must occur before memcpy! */\n\tif (loadingModule) {\n\t\tchar *source_id = bstrdup(info->id);\n\t\tda_push_back(loadingModule->sources, &source_id);\n\t}\n\n\tmemcpy(&data, info, size);\n\n\t/* mark audio-only filters as an async filter categorically */\n\tif (data.type == OBS_SOURCE_TYPE_FILTER) {\n\t\tif ((data.output_flags & OBS_SOURCE_VIDEO) == 0)\n\t\t\tdata.output_flags |= OBS_SOURCE_ASYNC;\n\t}\n\n\tif (data.type == OBS_SOURCE_TYPE_TRANSITION) {\n\t\tif (data.get_width)\n\t\t\tsource_warn(\"get_width ignored registering \"\n\t\t\t\t    \"transition '%s'\",\n\t\t\t\t    data.id);\n\t\tif (data.get_height)\n\t\t\tsource_warn(\"get_height ignored registering \"\n\t\t\t\t    \"transition '%s'\",\n\t\t\t\t    data.id);\n\t\tdata.output_flags |= OBS_SOURCE_COMPOSITE | OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW;\n\t}\n\n\tif ((data.output_flags & OBS_SOURCE_COMPOSITE) != 0) {\n\t\tif ((data.output_flags & OBS_SOURCE_AUDIO) != 0) {\n\t\t\tsource_warn(\"Source '%s': Composite sources \"\n\t\t\t\t    \"cannot be audio sources\",\n\t\t\t\t    info->id);\n\t\t\tgoto error;\n\t\t}\n\t\tif ((data.output_flags & OBS_SOURCE_ASYNC) != 0) {\n\t\t\tsource_warn(\"Source '%s': Composite sources \"\n\t\t\t\t    \"cannot be async sources\",\n\t\t\t\t    info->id);\n\t\t\tgoto error;\n\t\t}\n\t}\n\n#define CHECK_REQUIRED_VAL_(info, val, func) CHECK_REQUIRED_VAL(struct obs_source_info, info, val, func)\n\tCHECK_REQUIRED_VAL_(info, get_name, obs_register_source);\n\n\tif (info->type != OBS_SOURCE_TYPE_FILTER && info->type != OBS_SOURCE_TYPE_TRANSITION &&\n\t    (info->output_flags & OBS_SOURCE_VIDEO) != 0 && (info->output_flags & OBS_SOURCE_ASYNC) == 0) {\n\t\tCHECK_REQUIRED_VAL_(info, get_width, obs_register_source);\n\t\tCHECK_REQUIRED_VAL_(info, get_height, obs_register_source);\n\t}\n\n\tif ((data.output_flags & OBS_SOURCE_COMPOSITE) != 0) {\n\t\tCHECK_REQUIRED_VAL_(info, audio_render, obs_register_source);\n\t}\n#undef CHECK_REQUIRED_VAL_\n\n\t/* version-related stuff */\n\tdata.unversioned_id = data.id;\n\tif (data.version) {\n\t\tstruct dstr versioned_id = {0};\n\t\tdstr_printf(&versioned_id, \"%s_v%d\", data.id, (int)data.version);\n\t\tdata.id = versioned_id.array;\n\t} else {\n\t\tdata.id = bstrdup(data.id);\n\t}\n\n\tif (array)\n\t\tda_push_back(*array, &data);\n\tda_push_back(obs->source_types, &data);\n\treturn;\n\nerror:\n\tHANDLE_ERROR(size, obs_source_info, info);\n}\n\nvoid obs_register_output_s(const struct obs_output_info *info, size_t size)\n{\n\tif (find_output(info->id)) {\n\t\toutput_warn(\"Output id '%s' already exists!  \"\n\t\t\t    \"Duplicate library?\",\n\t\t\t    info->id);\n\t\tgoto error;\n\t}\n\n#define CHECK_REQUIRED_VAL_(info, val, func) CHECK_REQUIRED_VAL(struct obs_output_info, info, val, func)\n\tCHECK_REQUIRED_VAL_(info, get_name, obs_register_output);\n\tCHECK_REQUIRED_VAL_(info, create, obs_register_output);\n\tCHECK_REQUIRED_VAL_(info, destroy, obs_register_output);\n\tCHECK_REQUIRED_VAL_(info, start, obs_register_output);\n\tCHECK_REQUIRED_VAL_(info, stop, obs_register_output);\n\n\tif (info->flags & OBS_OUTPUT_SERVICE)\n\t\tCHECK_REQUIRED_VAL_(info, protocols, obs_register_output);\n\n\tif (info->flags & OBS_OUTPUT_ENCODED) {\n\t\tCHECK_REQUIRED_VAL_(info, encoded_packet, obs_register_output);\n\t} else {\n\t\tif (info->flags & OBS_OUTPUT_VIDEO)\n\t\t\tCHECK_REQUIRED_VAL_(info, raw_video, obs_register_output);\n\n\t\tif (info->flags & OBS_OUTPUT_AUDIO) {\n\t\t\tif (info->flags & OBS_OUTPUT_MULTI_TRACK) {\n\t\t\t\tCHECK_REQUIRED_VAL_(info, raw_audio2, obs_register_output);\n\t\t\t} else {\n\t\t\t\tCHECK_REQUIRED_VAL_(info, raw_audio, obs_register_output);\n\t\t\t}\n\t\t}\n\t}\n#undef CHECK_REQUIRED_VAL_\n\n\tREGISTER_OBS_DEF(size, obs_output_info, obs->output_types, info);\n\n\tif (info->flags & OBS_OUTPUT_SERVICE) {\n\t\tchar **protocols = strlist_split(info->protocols, ';', false);\n\t\tfor (char **protocol = protocols; *protocol; ++protocol) {\n\t\t\tbool skip = false;\n\t\t\tfor (size_t i = 0; i < obs->data.protocols.num; i++) {\n\t\t\t\tif (strcmp(*protocol, obs->data.protocols.array[i]) == 0)\n\t\t\t\t\tskip = true;\n\t\t\t}\n\n\t\t\tif (skip)\n\t\t\t\tcontinue;\n\t\t\tchar *new_prtcl = bstrdup(*protocol);\n\t\t\tda_push_back(obs->data.protocols, &new_prtcl);\n\t\t}\n\t\tstrlist_free(protocols);\n\t}\n\n\tif (loadingModule) {\n\t\tchar *output_id = bstrdup(info->id);\n\t\tda_push_back(loadingModule->outputs, &output_id);\n\t}\n\n\treturn;\n\nerror:\n\tHANDLE_ERROR(size, obs_output_info, info);\n}\n\nvoid obs_register_encoder_s(const struct obs_encoder_info *info, size_t size)\n{\n\tif (find_encoder(info->id)) {\n\t\tencoder_warn(\"Encoder id '%s' already exists!  \"\n\t\t\t     \"Duplicate library?\",\n\t\t\t     info->id);\n\t\tgoto error;\n\t}\n\n\tif (((info->caps & OBS_ENCODER_CAP_PASS_TEXTURE) != 0 && info->caps & OBS_ENCODER_CAP_SCALING) != 0) {\n\t\tencoder_warn(\"Texture encoders cannot self-scale. Encoder id '%s' not registered.\", info->id);\n\t\tgoto error;\n\t}\n\n#define CHECK_REQUIRED_VAL_(info, val, func) CHECK_REQUIRED_VAL(struct obs_encoder_info, info, val, func)\n\tCHECK_REQUIRED_VAL_(info, get_name, obs_register_encoder);\n\tCHECK_REQUIRED_VAL_(info, create, obs_register_encoder);\n\tCHECK_REQUIRED_VAL_(info, destroy, obs_register_encoder);\n\n\tif ((info->caps & OBS_ENCODER_CAP_PASS_TEXTURE) != 0)\n\t\tCHECK_REQUIRED_VAL_EITHER(struct obs_encoder_info, info, encode_texture, encode_texture2,\n\t\t\t\t\t  obs_register_encoder);\n\telse\n\t\tCHECK_REQUIRED_VAL_(info, encode, obs_register_encoder);\n\n\tif (info->type == OBS_ENCODER_AUDIO)\n\t\tCHECK_REQUIRED_VAL_(info, get_frame_size, obs_register_encoder);\n#undef CHECK_REQUIRED_VAL_\n\n\tREGISTER_OBS_DEF(size, obs_encoder_info, obs->encoder_types, info);\n\n\tif (loadingModule) {\n\t\tchar *encoder_id = bstrdup(info->id);\n\t\tda_push_back(loadingModule->encoders, &encoder_id);\n\t}\n\n\treturn;\n\nerror:\n\tHANDLE_ERROR(size, obs_encoder_info, info);\n}\n\nvoid obs_register_service_s(const struct obs_service_info *info, size_t size)\n{\n\tif (find_service(info->id)) {\n\t\tservice_warn(\"Service id '%s' already exists!  \"\n\t\t\t     \"Duplicate library?\",\n\t\t\t     info->id);\n\t\tgoto error;\n\t}\n\n#define CHECK_REQUIRED_VAL_(info, val, func) CHECK_REQUIRED_VAL(struct obs_service_info, info, val, func)\n\tCHECK_REQUIRED_VAL_(info, get_name, obs_register_service);\n\tCHECK_REQUIRED_VAL_(info, create, obs_register_service);\n\tCHECK_REQUIRED_VAL_(info, destroy, obs_register_service);\n\tCHECK_REQUIRED_VAL_(info, get_protocol, obs_register_service);\n#undef CHECK_REQUIRED_VAL_\n\n\tREGISTER_OBS_DEF(size, obs_service_info, obs->service_types, info);\n\n\tif (loadingModule) {\n\t\tchar *service_id = bstrdup(info->id);\n\t\tda_push_back(loadingModule->services, &service_id);\n\t}\n\n\treturn;\n\nerror:\n\tHANDLE_ERROR(size, obs_service_info, info);\n}\n"
  },
  {
    "path": "libobs/obs-module.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"obs.h\"\n\n#ifdef __cplusplus\n#define MODULE_EXPORT extern \"C\" EXPORT\n#define MODULE_EXTERN extern \"C\"\n#else\n#define MODULE_EXPORT EXPORT\n#define MODULE_EXTERN extern\n#endif\n\n/**\n * @file\n * @brief This file is used by modules for module declaration and module\n *        exports.\n *\n * @page modules_page Modules\n * @brief Modules or plugins are libraries that can be loaded by libobs and\n *        subsequently interact with it.\n *\n * @section modules_overview_sec Overview\n *\n * Modules can provide a wide range of functionality to libobs, they for example\n * can feed captured audio or video to libobs, or interface with an encoder to\n * provide some codec to libobs.\n *\n * @section modules_basic_sec Creating a basic module\n *\n * In order to create a module for libobs you will need to build a shared\n * library that implements a basic interface for libobs to interact with.\n * The following code would create a simple source plugin without localization:\n *\n@code\n#include <obs-module.h>\n\nOBS_DECLARE_MODULE()\n\nextern struct obs_source_info my_source;\n\nbool obs_module_load(void)\n{\n\tobs_register_source(&my_source);\n\treturn true;\n}\n@endcode\n *\n * If you want to enable localization, you will need to also use the\n * @ref OBS_MODULE_USE_DEFAULT_LOCALE() macro.\n *\n * Other module types:\n * - @ref obs_register_encoder()\n * - @ref obs_register_service()\n * - @ref obs_register_output()\n *\n */\n\n/** Required: Declares a libobs module. */\n#define OBS_DECLARE_MODULE()                                             \\\n\tstatic obs_module_t *obs_module_pointer;                         \\\n\tMODULE_EXPORT void obs_module_set_pointer(obs_module_t *module); \\\n\tvoid obs_module_set_pointer(obs_module_t *module)                \\\n\t{                                                                \\\n\t\tobs_module_pointer = module;                             \\\n\t}                                                                \\\n\tobs_module_t *obs_current_module(void)                           \\\n\t{                                                                \\\n\t\treturn obs_module_pointer;                               \\\n\t}                                                                \\\n\tMODULE_EXPORT uint32_t obs_module_ver(void);                     \\\n\tuint32_t obs_module_ver(void)                                    \\\n\t{                                                                \\\n\t\treturn LIBOBS_API_VER;                                   \\\n\t}\n\n/**\n * Required: Called when the module is loaded.  Use this function to load all\n * the sources/encoders/outputs/services for your module, or anything else that\n * may need loading.\n *\n * @return           Return true to continue loading the module, otherwise\n *                   false to indicate failure and unload the module\n */\nMODULE_EXPORT bool obs_module_load(void);\n\n/** Optional: Called when the module is unloaded.  */\nMODULE_EXPORT void obs_module_unload(void);\n\n/** Optional: Called when all modules have finished loading */\nMODULE_EXPORT void obs_module_post_load(void);\n\n/** Called to set the current locale data for the module.  */\nMODULE_EXPORT void obs_module_set_locale(const char *locale);\n\n/** Called to free the current locale data for the module.  */\nMODULE_EXPORT void obs_module_free_locale(void);\n\n/** Optional: Use this macro in a module to use default locale handling. */\n#define OBS_MODULE_USE_DEFAULT_LOCALE(module_name, default_locale)                                        \\\n\tlookup_t *obs_module_lookup = NULL;                                                               \\\n\tconst char *obs_module_text(const char *val)                                                      \\\n\t{                                                                                                 \\\n\t\tconst char *out = val;                                                                    \\\n\t\ttext_lookup_getstr(obs_module_lookup, val, &out);                                         \\\n\t\treturn out;                                                                               \\\n\t}                                                                                                 \\\n\tbool obs_module_get_string(const char *val, const char **out)                                     \\\n\t{                                                                                                 \\\n\t\treturn text_lookup_getstr(obs_module_lookup, val, out);                                   \\\n\t}                                                                                                 \\\n\tvoid obs_module_set_locale(const char *locale)                                                    \\\n\t{                                                                                                 \\\n\t\tif (obs_module_lookup)                                                                    \\\n\t\t\ttext_lookup_destroy(obs_module_lookup);                                           \\\n\t\tobs_module_lookup = obs_module_load_locale(obs_current_module(), default_locale, locale); \\\n\t}                                                                                                 \\\n\tvoid obs_module_free_locale(void)                                                                 \\\n\t{                                                                                                 \\\n\t\ttext_lookup_destroy(obs_module_lookup);                                                   \\\n\t\tobs_module_lookup = NULL;                                                                 \\\n\t}\n\n/** Helper function for looking up locale if default locale handler was used */\nMODULE_EXTERN const char *obs_module_text(const char *lookup_string);\n\n/** Helper function for looking up locale if default locale handler was used,\n * returns true if text found, otherwise false */\nMODULE_EXPORT bool obs_module_get_string(const char *lookup_string, const char **translated_string);\n\n/** Helper function that returns the current module */\nMODULE_EXTERN obs_module_t *obs_current_module(void);\n\n/**\n * Returns the location to a module data file associated with the current\n * module.  Free with bfree when complete.  Equivalent to:\n *    obs_find_module_file(obs_current_module(), file);\n */\n#define obs_module_file(file) obs_find_module_file(obs_current_module(), file)\n\n/**\n * Returns the location to a module config file associated with the current\n * module.  Free with bfree when complete.  Will return NULL if configuration\n * directory is not set.  Equivalent to:\n *    obs_module_get_config_path(obs_current_module(), file);\n */\n#define obs_module_config_path(file) obs_module_get_config_path(obs_current_module(), file)\n\n/**\n * Optional: Declares the author(s) of the module\n *\n * @param name Author name(s)\n */\n#define OBS_MODULE_AUTHOR(name)                            \\\n\tMODULE_EXPORT const char *obs_module_author(void); \\\n\tconst char *obs_module_author(void)                \\\n\t{                                                  \\\n\t\treturn name;                               \\\n\t}\n\n/** Optional: Returns the full name of the module */\nMODULE_EXPORT const char *obs_module_name(void);\n\n/** Optional: Returns a description of the module */\nMODULE_EXPORT const char *obs_module_description(void);\n\n/** Returns the module's unique ID, or NULL if it doesn't have one */\nMODULE_EXPORT const char *obs_get_module_id(obs_module_t *module);\n\n/** Returns the module's semver version number or NULL if it doesn't have one */\nMODULE_EXPORT const char *obs_get_module_version(obs_module_t *module);\n"
  },
  {
    "path": "libobs/obs-nal.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-nal.h\"\n\n/* NOTE: I noticed that FFmpeg does some unusual special handling of certain\n * scenarios that I was unaware of, so instead of just searching for {0, 0, 1}\n * we'll just use the code from FFmpeg - http://www.ffmpeg.org/ */\nstatic const uint8_t *ff_avc_find_startcode_internal(const uint8_t *p, const uint8_t *end)\n{\n\tconst uint8_t *a = p + 4 - ((intptr_t)p & 3);\n\n\tfor (end -= 3; p < a && p < end; p++) {\n\t\tif (p[0] == 0 && p[1] == 0 && p[2] == 1)\n\t\t\treturn p;\n\t}\n\n\tfor (end -= 3; p < end; p += 4) {\n\t\tuint32_t x = *(const uint32_t *)p;\n\n\t\tif ((x - 0x01010101) & (~x) & 0x80808080) {\n\t\t\tif (p[1] == 0) {\n\t\t\t\tif (p[0] == 0 && p[2] == 1)\n\t\t\t\t\treturn p;\n\t\t\t\tif (p[2] == 0 && p[3] == 1)\n\t\t\t\t\treturn p + 1;\n\t\t\t}\n\n\t\t\tif (p[3] == 0) {\n\t\t\t\tif (p[2] == 0 && p[4] == 1)\n\t\t\t\t\treturn p + 2;\n\t\t\t\tif (p[4] == 0 && p[5] == 1)\n\t\t\t\t\treturn p + 3;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (end += 3; p < end; p++) {\n\t\tif (p[0] == 0 && p[1] == 0 && p[2] == 1)\n\t\t\treturn p;\n\t}\n\n\treturn end + 3;\n}\n\nconst uint8_t *obs_nal_find_startcode(const uint8_t *p, const uint8_t *end)\n{\n\tconst uint8_t *out = ff_avc_find_startcode_internal(p, end);\n\tif (p < out && out < end && !out[-1])\n\t\tout--;\n\treturn out;\n}\n"
  },
  {
    "path": "libobs/obs-nal.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"util/c99defs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum {\n\tOBS_NAL_PRIORITY_DISPOSABLE = 0,\n\tOBS_NAL_PRIORITY_LOW = 1,\n\tOBS_NAL_PRIORITY_HIGH = 2,\n\tOBS_NAL_PRIORITY_HIGHEST = 3,\n};\n\nEXPORT const uint8_t *obs_nal_find_startcode(const uint8_t *p, const uint8_t *end);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-nix-platform.c",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-nix-platform.h\"\n\n#include <assert.h>\n\nstatic enum obs_nix_platform_type obs_nix_platform = OBS_NIX_PLATFORM_X11_EGL;\n\nstatic void *obs_nix_platform_display = NULL;\n\nvoid obs_set_nix_platform(enum obs_nix_platform_type platform)\n{\n\tassert(platform != OBS_NIX_PLATFORM_INVALID);\n\tobs_nix_platform = platform;\n}\n\nenum obs_nix_platform_type obs_get_nix_platform(void)\n{\n\treturn obs_nix_platform;\n}\n\nvoid obs_set_nix_platform_display(void *display)\n{\n\tobs_nix_platform_display = display;\n}\n\nvoid *obs_get_nix_platform_display(void)\n{\n\treturn obs_nix_platform_display;\n}\n"
  },
  {
    "path": "libobs/obs-nix-platform.h",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"util/c99defs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum obs_nix_platform_type {\n\tOBS_NIX_PLATFORM_INVALID,\n\tOBS_NIX_PLATFORM_X11_EGL,\n\tOBS_NIX_PLATFORM_WAYLAND,\n};\n\n/**\n * Sets the Unix platform.\n * @param platform The platform to select.\n */\nEXPORT void obs_set_nix_platform(enum obs_nix_platform_type platform);\n/**\n * Gets the host platform.\n */\nEXPORT enum obs_nix_platform_type obs_get_nix_platform(void);\n/**\n * Sets the host platform's display connection.\n * @param display The host display connection.\n */\nEXPORT void obs_set_nix_platform_display(void *display);\n/**\n * Gets the host platform's display connection.\n */\nEXPORT void *obs_get_nix_platform_display(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-nix-wayland.c",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-internal.h\"\n#include \"obs-nix-platform.h\"\n#include \"obs-nix-wayland.h\"\n\n#include <unistd.h>\n#include <sys/mman.h>\n#include <string.h>\n\n#include <wayland-client.h>\n#include <xkbcommon/xkbcommon.h>\n\n// X11 only supports 256 scancodes, most keyboards dont have 256 keys so this should be reasonable.\n#define MAX_KEYCODES 256\n// X11 keymaps only have 4 shift levels, im not sure xkbcommon supports a way to shift the state into a higher level anyway.\n#define MAX_SHIFT_LEVELS 4\n\nstruct obs_hotkeys_platform {\n\tstruct wl_display *display;\n\tstruct wl_seat *seat;\n\tstruct wl_keyboard *keyboard;\n\tstruct xkb_context *xkb_context;\n\tstruct xkb_keymap *xkb_keymap;\n\tstruct xkb_state *xkb_state;\n\txkb_keysym_t key_to_sym[MAX_SHIFT_LEVELS][MAX_KEYCODES];\n\txkb_keysym_t obs_to_key[OBS_KEY_LAST_VALUE];\n\tuint32_t current_layout;\n};\n\nstatic obs_key_t obs_nix_wayland_key_from_virtual_key(int sym);\n\nstatic void load_keymap_data(struct xkb_keymap *keymap, xkb_keycode_t key, void *data)\n{\n\tobs_hotkeys_platform_t *plat = (obs_hotkeys_platform_t *)data;\n\tif (key >= MAX_KEYCODES)\n\t\treturn;\n\n\tconst xkb_keysym_t *syms;\n\tfor (int level = 0; level < MAX_SHIFT_LEVELS; level++) {\n\t\tint nsyms = xkb_keymap_key_get_syms_by_level(keymap, key, plat->current_layout, level, &syms);\n\t\tif (nsyms < 1)\n\t\t\tcontinue;\n\n\t\tobs_key_t obs_key = obs_nix_wayland_key_from_virtual_key(syms[0]);\n\t\t// This avoids ambiguity where multiple scancodes produce the same symbols.\n\t\t// e.g. LSGT and Shift+AB08 produce `<` on default US layout.\n\t\tif (!plat->obs_to_key[obs_key])\n\t\t\tplat->obs_to_key[obs_key] = key;\n\t\tplat->key_to_sym[level][key] = syms[0];\n\t}\n}\n\nstatic void rebuild_keymap_data(obs_hotkeys_platform_t *plat)\n{\n\tmemset(plat->key_to_sym, 0, sizeof(xkb_keysym_t) * MAX_SHIFT_LEVELS * MAX_KEYCODES);\n\tmemset(plat->obs_to_key, 0, sizeof(xkb_keysym_t) * OBS_KEY_LAST_VALUE);\n\txkb_keymap_key_for_each(plat->xkb_keymap, load_keymap_data, plat);\n}\n\nstatic void platform_keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd,\n\t\t\t\t     uint32_t size)\n{\n\tUNUSED_PARAMETER(keyboard);\n\tUNUSED_PARAMETER(format);\n\tobs_hotkeys_platform_t *plat = (obs_hotkeys_platform_t *)data;\n\n\tchar *keymap_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);\n\tif (keymap_shm == MAP_FAILED) {\n\t\tclose(fd);\n\t\treturn;\n\t}\n\n\tstruct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string(\n\t\tplat->xkb_context, keymap_shm, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);\n\tmunmap(keymap_shm, size);\n\tclose(fd);\n\n\t// cleanup old keymap and state\n\txkb_keymap_unref(plat->xkb_keymap);\n\txkb_state_unref(plat->xkb_state);\n\n\tplat->xkb_keymap = xkb_keymap;\n\tplat->xkb_state = xkb_state_new(xkb_keymap);\n\trebuild_keymap_data(plat);\n}\n\nstatic void platform_keyboard_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial,\n\t\t\t\t\tuint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,\n\t\t\t\t\tuint32_t group)\n{\n\tUNUSED_PARAMETER(keyboard);\n\tUNUSED_PARAMETER(serial);\n\tobs_hotkeys_platform_t *plat = (obs_hotkeys_platform_t *)data;\n\txkb_state_update_mask(plat->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);\n\n\tif (plat->current_layout != group) {\n\t\tplat->current_layout = group;\n\t\trebuild_keymap_data(plat);\n\t}\n}\n\nstatic void platform_keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time,\n\t\t\t\t  uint32_t key, uint32_t state)\n{\n\tUNUSED_PARAMETER(data);\n\tUNUSED_PARAMETER(keyboard);\n\tUNUSED_PARAMETER(serial);\n\tUNUSED_PARAMETER(time);\n\tUNUSED_PARAMETER(key);\n\tUNUSED_PARAMETER(state);\n\t// We have access to the keyboard input here, but behave like other\n\t// platforms and let Qt inform us of key events through the platform\n\t// callbacks.\n}\n\nstatic void platform_keyboard_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial,\n\t\t\t\t    struct wl_surface *surface, struct wl_array *keys)\n{\n\tUNUSED_PARAMETER(data);\n\tUNUSED_PARAMETER(keyboard);\n\tUNUSED_PARAMETER(serial);\n\tUNUSED_PARAMETER(surface);\n\tUNUSED_PARAMETER(keys);\n\t// Nothing to do here.\n}\n\nstatic void platform_keyboard_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial,\n\t\t\t\t    struct wl_surface *surface)\n{\n\tUNUSED_PARAMETER(data);\n\tUNUSED_PARAMETER(keyboard);\n\tUNUSED_PARAMETER(serial);\n\tUNUSED_PARAMETER(surface);\n\t// Nothing to do.\n}\n\nstatic void platform_keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, int32_t rate, int32_t delay)\n{\n\tUNUSED_PARAMETER(data);\n\tUNUSED_PARAMETER(keyboard);\n\tUNUSED_PARAMETER(rate);\n\tUNUSED_PARAMETER(delay);\n\t// Nothing to do.\n}\n\nconst struct wl_keyboard_listener keyboard_listener = {\n\t.keymap = platform_keyboard_keymap,\n\t.enter = platform_keyboard_enter,\n\t.leave = platform_keyboard_leave,\n\t.key = platform_keyboard_key,\n\t.modifiers = platform_keyboard_modifiers,\n\t.repeat_info = platform_keyboard_repeat_info,\n};\n\nstatic void platform_seat_capabilities(void *data, struct wl_seat *seat, uint32_t capabilities)\n{\n\tUNUSED_PARAMETER(seat);\n\tobs_hotkeys_platform_t *plat = (obs_hotkeys_platform_t *)data;\n\n\tbool kb_present = capabilities & WL_SEAT_CAPABILITY_KEYBOARD;\n\n\tif (kb_present && plat->keyboard == NULL) {\n\t\tplat->keyboard = wl_seat_get_keyboard(plat->seat);\n\t\twl_keyboard_add_listener(plat->keyboard, &keyboard_listener, plat);\n\t} else if (!kb_present && plat->keyboard != NULL) {\n\t\twl_keyboard_release(plat->keyboard);\n\t\tplat->keyboard = NULL;\n\t}\n}\nstatic void platform_seat_name(void *data, struct wl_seat *seat, const char *name)\n{\n\tUNUSED_PARAMETER(data);\n\tUNUSED_PARAMETER(seat);\n\tUNUSED_PARAMETER(name);\n\t// Nothing to do.\n}\n\nconst struct wl_seat_listener seat_listener = {\n\t.capabilities = platform_seat_capabilities,\n\t.name = platform_seat_name,\n};\n\nstatic void platform_registry_handler(void *data, struct wl_registry *registry, uint32_t id, const char *interface,\n\t\t\t\t      uint32_t version)\n{\n\tobs_hotkeys_platform_t *plat = (obs_hotkeys_platform_t *)data;\n\n\tif (strcmp(interface, wl_seat_interface.name) == 0) {\n\t\tif (version < 4) {\n\t\t\tblog(LOG_WARNING, \"[wayland] hotkeys disabled, compositor is too old\");\n\t\t\treturn;\n\t\t}\n\t\t// Only negotiate up to version 7, the current wl_seat at time of writing.\n\t\tplat->seat = wl_registry_bind(registry, id, &wl_seat_interface, version <= 7 ? version : 7);\n\t\twl_seat_add_listener(plat->seat, &seat_listener, plat);\n\t}\n}\n\nstatic void platform_registry_remover(void *data, struct wl_registry *registry, uint32_t id)\n{\n\tUNUSED_PARAMETER(data);\n\tUNUSED_PARAMETER(registry);\n\tUNUSED_PARAMETER(id);\n\t// Nothing to do.\n}\n\nconst struct wl_registry_listener registry_listener = {\n\t.global = platform_registry_handler,\n\t.global_remove = platform_registry_remover,\n};\n\nvoid obs_nix_wayland_log_info(void)\n{\n\tstruct wl_display *display = obs_get_nix_platform_display();\n\tif (display == NULL) {\n\t\tblog(LOG_INFO, \"Unable to connect to Wayland server\");\n\t\treturn;\n\t}\n\t//TODO: query some information about the wayland server if possible\n\tblog(LOG_INFO, \"Connected to Wayland server\");\n}\n\nstatic bool obs_nix_wayland_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)\n{\n\tstruct wl_display *display = obs_get_nix_platform_display();\n\thotkeys->platform_context = bzalloc(sizeof(obs_hotkeys_platform_t));\n\thotkeys->platform_context->display = display;\n\thotkeys->platform_context->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);\n\n\tstruct wl_registry *registry = wl_display_get_registry(display);\n\twl_registry_add_listener(registry, &registry_listener, hotkeys->platform_context);\n\twl_display_roundtrip(display);\n\treturn true;\n}\n\nstatic void obs_nix_wayland_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)\n{\n\tobs_hotkeys_platform_t *plat = hotkeys->platform_context;\n\txkb_context_unref(plat->xkb_context);\n\txkb_keymap_unref(plat->xkb_keymap);\n\txkb_state_unref(plat->xkb_state);\n\tbfree(plat);\n}\n\nstatic bool obs_nix_wayland_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context, obs_key_t key)\n{\n\tUNUSED_PARAMETER(context);\n\tUNUSED_PARAMETER(key);\n\t// This function is only used by the hotkey thread for capturing out of\n\t// focus hotkey triggers. Since wayland never delivers key events when out\n\t// of focus we leave this blank intentionally.\n\treturn false;\n}\n\nstatic void obs_nix_wayland_key_to_str(obs_key_t key, struct dstr *dstr)\n{\n\tif (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {\n\t\tif (obs->hotkeys.translations[key]) {\n\t\t\tdstr_copy(dstr, obs->hotkeys.translations[key]);\n\t\t} else {\n\t\t\tdstr_printf(dstr, \"Mouse %d\", (int)(key - OBS_KEY_MOUSE1 + 1));\n\t\t}\n\t\treturn;\n\t}\n\n\tif (key >= OBS_KEY_NUM0 && key <= OBS_KEY_NUM9) {\n\t\tif (obs->hotkeys.translations[key]) {\n\t\t\tdstr_copy(dstr, obs->hotkeys.translations[key]);\n\t\t} else {\n\t\t\tdstr_printf(dstr, \"Numpad %d\", (int)(key - OBS_KEY_NUM0));\n\t\t}\n\t\treturn;\n\t}\n\n#define translate_key(key, def) dstr_copy(dstr, obs_get_hotkey_translation(key, def))\n\n\tswitch (key) {\n\tcase OBS_KEY_INSERT:\n\t\treturn translate_key(key, \"Insert\");\n\tcase OBS_KEY_DELETE:\n\t\treturn translate_key(key, \"Delete\");\n\tcase OBS_KEY_HOME:\n\t\treturn translate_key(key, \"Home\");\n\tcase OBS_KEY_END:\n\t\treturn translate_key(key, \"End\");\n\tcase OBS_KEY_PAGEUP:\n\t\treturn translate_key(key, \"Page Up\");\n\tcase OBS_KEY_PAGEDOWN:\n\t\treturn translate_key(key, \"Page Down\");\n\tcase OBS_KEY_NUMLOCK:\n\t\treturn translate_key(key, \"Num Lock\");\n\tcase OBS_KEY_SCROLLLOCK:\n\t\treturn translate_key(key, \"Scroll Lock\");\n\tcase OBS_KEY_CAPSLOCK:\n\t\treturn translate_key(key, \"Caps Lock\");\n\tcase OBS_KEY_BACKSPACE:\n\t\treturn translate_key(key, \"Backspace\");\n\tcase OBS_KEY_TAB:\n\t\treturn translate_key(key, \"Tab\");\n\tcase OBS_KEY_PRINT:\n\t\treturn translate_key(key, \"Print\");\n\tcase OBS_KEY_PAUSE:\n\t\treturn translate_key(key, \"Pause\");\n\tcase OBS_KEY_LEFT:\n\t\treturn translate_key(key, \"Left\");\n\tcase OBS_KEY_RIGHT:\n\t\treturn translate_key(key, \"Right\");\n\tcase OBS_KEY_UP:\n\t\treturn translate_key(key, \"Up\");\n\tcase OBS_KEY_DOWN:\n\t\treturn translate_key(key, \"Down\");\n\tcase OBS_KEY_SHIFT:\n\t\treturn translate_key(key, \"Shift\");\n\tcase OBS_KEY_ALT:\n\t\treturn translate_key(key, \"Alt\");\n\tcase OBS_KEY_CONTROL:\n\t\treturn translate_key(key, \"Control\");\n\tcase OBS_KEY_META:\n\t\treturn translate_key(key, \"Super\");\n\tcase OBS_KEY_MENU:\n\t\treturn translate_key(key, \"Menu\");\n\tcase OBS_KEY_NUMASTERISK:\n\t\treturn translate_key(key, \"Numpad *\");\n\tcase OBS_KEY_NUMPLUS:\n\t\treturn translate_key(key, \"Numpad +\");\n\tcase OBS_KEY_NUMMINUS:\n\t\treturn translate_key(key, \"Numpad -\");\n\tcase OBS_KEY_NUMCOMMA:\n\t\treturn translate_key(key, \"Numpad ,\");\n\tcase OBS_KEY_NUMPERIOD:\n\t\treturn translate_key(key, \"Numpad .\");\n\tcase OBS_KEY_NUMSLASH:\n\t\treturn translate_key(key, \"Numpad /\");\n\tcase OBS_KEY_SPACE:\n\t\treturn translate_key(key, \"Space\");\n\tcase OBS_KEY_ESCAPE:\n\t\treturn translate_key(key, \"Escape\");\n\tdefault:;\n\t}\n\n\tif (key >= OBS_KEY_F1 && key <= OBS_KEY_F35) {\n\t\tdstr_printf(dstr, \"F%d\", (int)(key - OBS_KEY_F1 + 1));\n\t\treturn;\n\t}\n\n\tobs_hotkeys_platform_t *plat = obs->hotkeys.platform_context;\n\t// Translate the obs key back down to shift level 1 and then back to obs key.\n\txkb_keycode_t keycode = plat->obs_to_key[key];\n\txkb_keysym_t base_sym = plat->key_to_sym[0][keycode];\n\tif (base_sym != 0) {\n\t\tchar buf[16] = {0};\n\t\tif (xkb_keysym_to_utf8(base_sym, buf, 15)) {\n\t\t\t// Normally obs uses capital letters but we are shift level 1 (lower case).\n\t\t\tdstr_copy(dstr, buf);\n\t\t}\n\t}\n\n\tif (key != OBS_KEY_NONE && dstr_is_empty(dstr)) {\n\t\tdstr_copy(dstr, obs_key_to_name(key));\n\t}\n}\n\nstatic obs_key_t obs_nix_wayland_key_from_virtual_key(int sym)\n{\n\tswitch (sym) {\n\tcase XKB_KEY_0:\n\t\treturn OBS_KEY_0;\n\tcase XKB_KEY_1:\n\t\treturn OBS_KEY_1;\n\tcase XKB_KEY_2:\n\t\treturn OBS_KEY_2;\n\tcase XKB_KEY_3:\n\t\treturn OBS_KEY_3;\n\tcase XKB_KEY_4:\n\t\treturn OBS_KEY_4;\n\tcase XKB_KEY_5:\n\t\treturn OBS_KEY_5;\n\tcase XKB_KEY_6:\n\t\treturn OBS_KEY_6;\n\tcase XKB_KEY_7:\n\t\treturn OBS_KEY_7;\n\tcase XKB_KEY_8:\n\t\treturn OBS_KEY_8;\n\tcase XKB_KEY_9:\n\t\treturn OBS_KEY_9;\n\tcase XKB_KEY_A:\n\t\treturn OBS_KEY_A;\n\tcase XKB_KEY_a:\n\t\treturn OBS_KEY_A;\n\tcase XKB_KEY_Aacute:\n\t\treturn OBS_KEY_AACUTE;\n\tcase XKB_KEY_aacute:\n\t\treturn OBS_KEY_AACUTE;\n\tcase XKB_KEY_Acircumflex:\n\t\treturn OBS_KEY_ACIRCUMFLEX;\n\tcase XKB_KEY_acircumflex:\n\t\treturn OBS_KEY_ACIRCUMFLEX;\n\tcase XKB_KEY_acute:\n\t\treturn OBS_KEY_ACUTE;\n\tcase XKB_KEY_Adiaeresis:\n\t\treturn OBS_KEY_ADIAERESIS;\n\tcase XKB_KEY_adiaeresis:\n\t\treturn OBS_KEY_ADIAERESIS;\n\tcase XKB_KEY_AE:\n\t\treturn OBS_KEY_AE;\n\tcase XKB_KEY_ae:\n\t\treturn OBS_KEY_AE;\n\tcase XKB_KEY_Agrave:\n\t\treturn OBS_KEY_AGRAVE;\n\tcase XKB_KEY_agrave:\n\t\treturn OBS_KEY_AGRAVE;\n\tcase XKB_KEY_ampersand:\n\t\treturn OBS_KEY_AMPERSAND;\n\tcase XKB_KEY_apostrophe:\n\t\treturn OBS_KEY_APOSTROPHE;\n\tcase XKB_KEY_Aring:\n\t\treturn OBS_KEY_ARING;\n\tcase XKB_KEY_aring:\n\t\treturn OBS_KEY_ARING;\n\tcase XKB_KEY_asciicircum:\n\t\treturn OBS_KEY_ASCIICIRCUM;\n\tcase XKB_KEY_asciitilde:\n\t\treturn OBS_KEY_ASCIITILDE;\n\tcase XKB_KEY_asterisk:\n\t\treturn OBS_KEY_ASTERISK;\n\tcase XKB_KEY_at:\n\t\treturn OBS_KEY_AT;\n\tcase XKB_KEY_Atilde:\n\t\treturn OBS_KEY_ATILDE;\n\tcase XKB_KEY_atilde:\n\t\treturn OBS_KEY_ATILDE;\n\tcase XKB_KEY_B:\n\t\treturn OBS_KEY_B;\n\tcase XKB_KEY_b:\n\t\treturn OBS_KEY_B;\n\tcase XKB_KEY_backslash:\n\t\treturn OBS_KEY_BACKSLASH;\n\tcase XKB_KEY_BackSpace:\n\t\treturn OBS_KEY_BACKSPACE;\n\tcase XKB_KEY_BackTab:\n\t\treturn OBS_KEY_BACKTAB;\n\tcase XKB_KEY_bar:\n\t\treturn OBS_KEY_BAR;\n\tcase XKB_KEY_braceleft:\n\t\treturn OBS_KEY_BRACELEFT;\n\tcase XKB_KEY_braceright:\n\t\treturn OBS_KEY_BRACERIGHT;\n\tcase XKB_KEY_bracketleft:\n\t\treturn OBS_KEY_BRACKETLEFT;\n\tcase XKB_KEY_bracketright:\n\t\treturn OBS_KEY_BRACKETRIGHT;\n\tcase XKB_KEY_brokenbar:\n\t\treturn OBS_KEY_BROKENBAR;\n\tcase XKB_KEY_C:\n\t\treturn OBS_KEY_C;\n\tcase XKB_KEY_c:\n\t\treturn OBS_KEY_C;\n\tcase XKB_KEY_Cancel:\n\t\treturn OBS_KEY_CANCEL;\n\tcase XKB_KEY_Ccedilla:\n\t\treturn OBS_KEY_CCEDILLA;\n\tcase XKB_KEY_ccedilla:\n\t\treturn OBS_KEY_CCEDILLA;\n\tcase XKB_KEY_cedilla:\n\t\treturn OBS_KEY_CEDILLA;\n\tcase XKB_KEY_cent:\n\t\treturn OBS_KEY_CENT;\n\tcase XKB_KEY_Clear:\n\t\treturn OBS_KEY_CLEAR;\n\tcase XKB_KEY_Codeinput:\n\t\treturn OBS_KEY_CODEINPUT;\n\tcase XKB_KEY_colon:\n\t\treturn OBS_KEY_COLON;\n\tcase XKB_KEY_comma:\n\t\treturn OBS_KEY_COMMA;\n\tcase XKB_KEY_copyright:\n\t\treturn OBS_KEY_COPYRIGHT;\n\tcase XKB_KEY_currency:\n\t\treturn OBS_KEY_CURRENCY;\n\tcase XKB_KEY_D:\n\t\treturn OBS_KEY_D;\n\tcase XKB_KEY_d:\n\t\treturn OBS_KEY_D;\n\tcase XKB_KEY_dead_abovedot:\n\t\treturn OBS_KEY_DEAD_ABOVEDOT;\n\tcase XKB_KEY_dead_abovering:\n\t\treturn OBS_KEY_DEAD_ABOVERING;\n\tcase XKB_KEY_dead_acute:\n\t\treturn OBS_KEY_DEAD_ACUTE;\n\tcase XKB_KEY_dead_belowdot:\n\t\treturn OBS_KEY_DEAD_BELOWDOT;\n\tcase XKB_KEY_dead_breve:\n\t\treturn OBS_KEY_DEAD_BREVE;\n\tcase XKB_KEY_dead_caron:\n\t\treturn OBS_KEY_DEAD_CARON;\n\tcase XKB_KEY_dead_cedilla:\n\t\treturn OBS_KEY_DEAD_CEDILLA;\n\tcase XKB_KEY_dead_circumflex:\n\t\treturn OBS_KEY_DEAD_CIRCUMFLEX;\n\tcase XKB_KEY_dead_diaeresis:\n\t\treturn OBS_KEY_DEAD_DIAERESIS;\n\tcase XKB_KEY_dead_doubleacute:\n\t\treturn OBS_KEY_DEAD_DOUBLEACUTE;\n\tcase XKB_KEY_dead_grave:\n\t\treturn OBS_KEY_DEAD_GRAVE;\n\tcase XKB_KEY_dead_hook:\n\t\treturn OBS_KEY_DEAD_HOOK;\n\tcase XKB_KEY_dead_horn:\n\t\treturn OBS_KEY_DEAD_HORN;\n\tcase XKB_KEY_dead_iota:\n\t\treturn OBS_KEY_DEAD_IOTA;\n\tcase XKB_KEY_dead_macron:\n\t\treturn OBS_KEY_DEAD_MACRON;\n\tcase XKB_KEY_dead_ogonek:\n\t\treturn OBS_KEY_DEAD_OGONEK;\n\tcase XKB_KEY_dead_semivoiced_sound:\n\t\treturn OBS_KEY_DEAD_SEMIVOICED_SOUND;\n\tcase XKB_KEY_dead_tilde:\n\t\treturn OBS_KEY_DEAD_TILDE;\n\tcase XKB_KEY_dead_voiced_sound:\n\t\treturn OBS_KEY_DEAD_VOICED_SOUND;\n\tcase XKB_KEY_degree:\n\t\treturn OBS_KEY_DEGREE;\n\tcase XKB_KEY_Delete:\n\t\treturn OBS_KEY_DELETE;\n\tcase XKB_KEY_diaeresis:\n\t\treturn OBS_KEY_DIAERESIS;\n\tcase XKB_KEY_division:\n\t\treturn OBS_KEY_DIVISION;\n\tcase XKB_KEY_dollar:\n\t\treturn OBS_KEY_DOLLAR;\n\tcase XKB_KEY_Down:\n\t\treturn OBS_KEY_DOWN;\n\tcase XKB_KEY_E:\n\t\treturn OBS_KEY_E;\n\tcase XKB_KEY_e:\n\t\treturn OBS_KEY_E;\n\tcase XKB_KEY_Eacute:\n\t\treturn OBS_KEY_EACUTE;\n\tcase XKB_KEY_eacute:\n\t\treturn OBS_KEY_EACUTE;\n\tcase XKB_KEY_Ecircumflex:\n\t\treturn OBS_KEY_ECIRCUMFLEX;\n\tcase XKB_KEY_ecircumflex:\n\t\treturn OBS_KEY_ECIRCUMFLEX;\n\tcase XKB_KEY_Ediaeresis:\n\t\treturn OBS_KEY_EDIAERESIS;\n\tcase XKB_KEY_ediaeresis:\n\t\treturn OBS_KEY_EDIAERESIS;\n\tcase XKB_KEY_Egrave:\n\t\treturn OBS_KEY_EGRAVE;\n\tcase XKB_KEY_egrave:\n\t\treturn OBS_KEY_EGRAVE;\n\tcase XKB_KEY_Eisu_Shift:\n\t\treturn OBS_KEY_EISU_SHIFT;\n\tcase XKB_KEY_Eisu_toggle:\n\t\treturn OBS_KEY_EISU_TOGGLE;\n\tcase XKB_KEY_End:\n\t\treturn OBS_KEY_END;\n\tcase XKB_KEY_equal:\n\t\treturn OBS_KEY_EQUAL;\n\tcase XKB_KEY_Escape:\n\t\treturn OBS_KEY_ESCAPE;\n\tcase XKB_KEY_Eth:\n\t\treturn OBS_KEY_ETH;\n\tcase XKB_KEY_eth:\n\t\treturn OBS_KEY_ETH;\n\tcase XKB_KEY_exclam:\n\t\treturn OBS_KEY_EXCLAM;\n\tcase XKB_KEY_exclamdown:\n\t\treturn OBS_KEY_EXCLAMDOWN;\n\tcase XKB_KEY_Execute:\n\t\treturn OBS_KEY_EXECUTE;\n\tcase XKB_KEY_F:\n\t\treturn OBS_KEY_F;\n\tcase XKB_KEY_f:\n\t\treturn OBS_KEY_F;\n\tcase XKB_KEY_F1:\n\t\treturn OBS_KEY_F1;\n\tcase XKB_KEY_F10:\n\t\treturn OBS_KEY_F10;\n\tcase XKB_KEY_F11:\n\t\treturn OBS_KEY_F11;\n\tcase XKB_KEY_F12:\n\t\treturn OBS_KEY_F12;\n\tcase XKB_KEY_F13:\n\t\treturn OBS_KEY_F13;\n\tcase XKB_KEY_F14:\n\t\treturn OBS_KEY_F14;\n\tcase XKB_KEY_F15:\n\t\treturn OBS_KEY_F15;\n\tcase XKB_KEY_F16:\n\t\treturn OBS_KEY_F16;\n\tcase XKB_KEY_F17:\n\t\treturn OBS_KEY_F17;\n\tcase XKB_KEY_F18:\n\t\treturn OBS_KEY_F18;\n\tcase XKB_KEY_F19:\n\t\treturn OBS_KEY_F19;\n\tcase XKB_KEY_F2:\n\t\treturn OBS_KEY_F2;\n\tcase XKB_KEY_F20:\n\t\treturn OBS_KEY_F20;\n\tcase XKB_KEY_F21:\n\t\treturn OBS_KEY_F21;\n\tcase XKB_KEY_F22:\n\t\treturn OBS_KEY_F22;\n\tcase XKB_KEY_F23:\n\t\treturn OBS_KEY_F23;\n\tcase XKB_KEY_F24:\n\t\treturn OBS_KEY_F24;\n\tcase XKB_KEY_F25:\n\t\treturn OBS_KEY_F25;\n\tcase XKB_KEY_F26:\n\t\treturn OBS_KEY_F26;\n\tcase XKB_KEY_F27:\n\t\treturn OBS_KEY_F27;\n\tcase XKB_KEY_F28:\n\t\treturn OBS_KEY_F28;\n\tcase XKB_KEY_F29:\n\t\treturn OBS_KEY_F29;\n\tcase XKB_KEY_F3:\n\t\treturn OBS_KEY_F3;\n\tcase XKB_KEY_F30:\n\t\treturn OBS_KEY_F30;\n\tcase XKB_KEY_F31:\n\t\treturn OBS_KEY_F31;\n\tcase XKB_KEY_F32:\n\t\treturn OBS_KEY_F32;\n\tcase XKB_KEY_F33:\n\t\treturn OBS_KEY_F33;\n\tcase XKB_KEY_F34:\n\t\treturn OBS_KEY_F34;\n\tcase XKB_KEY_F35:\n\t\treturn OBS_KEY_F35;\n\tcase XKB_KEY_F4:\n\t\treturn OBS_KEY_F4;\n\tcase XKB_KEY_F5:\n\t\treturn OBS_KEY_F5;\n\tcase XKB_KEY_F6:\n\t\treturn OBS_KEY_F6;\n\tcase XKB_KEY_F7:\n\t\treturn OBS_KEY_F7;\n\tcase XKB_KEY_F8:\n\t\treturn OBS_KEY_F8;\n\tcase XKB_KEY_F9:\n\t\treturn OBS_KEY_F9;\n\tcase XKB_KEY_Find:\n\t\treturn OBS_KEY_FIND;\n\tcase XKB_KEY_G:\n\t\treturn OBS_KEY_G;\n\tcase XKB_KEY_g:\n\t\treturn OBS_KEY_G;\n\tcase XKB_KEY_greater:\n\t\treturn OBS_KEY_GREATER;\n\tcase XKB_KEY_guillemotleft:\n\t\treturn OBS_KEY_GUILLEMOTLEFT;\n\tcase XKB_KEY_guillemotright:\n\t\treturn OBS_KEY_GUILLEMOTRIGHT;\n\tcase XKB_KEY_H:\n\t\treturn OBS_KEY_H;\n\tcase XKB_KEY_h:\n\t\treturn OBS_KEY_H;\n\tcase XKB_KEY_Hangul:\n\t\treturn OBS_KEY_HANGUL;\n\tcase XKB_KEY_Hangul_Banja:\n\t\treturn OBS_KEY_HANGUL_BANJA;\n\tcase XKB_KEY_Hangul_End:\n\t\treturn OBS_KEY_HANGUL_END;\n\tcase XKB_KEY_Hangul_Hanja:\n\t\treturn OBS_KEY_HANGUL_HANJA;\n\tcase XKB_KEY_Hangul_Jamo:\n\t\treturn OBS_KEY_HANGUL_JAMO;\n\tcase XKB_KEY_Hangul_Jeonja:\n\t\treturn OBS_KEY_HANGUL_JEONJA;\n\tcase XKB_KEY_Hangul_PostHanja:\n\t\treturn OBS_KEY_HANGUL_POSTHANJA;\n\tcase XKB_KEY_Hangul_PreHanja:\n\t\treturn OBS_KEY_HANGUL_PREHANJA;\n\tcase XKB_KEY_Hangul_Romaja:\n\t\treturn OBS_KEY_HANGUL_ROMAJA;\n\tcase XKB_KEY_Hangul_Special:\n\t\treturn OBS_KEY_HANGUL_SPECIAL;\n\tcase XKB_KEY_Hangul_Start:\n\t\treturn OBS_KEY_HANGUL_START;\n\tcase XKB_KEY_Hankaku:\n\t\treturn OBS_KEY_HANKAKU;\n\tcase XKB_KEY_Help:\n\t\treturn OBS_KEY_HELP;\n\tcase XKB_KEY_Henkan:\n\t\treturn OBS_KEY_HENKAN;\n\tcase XKB_KEY_Hiragana:\n\t\treturn OBS_KEY_HIRAGANA;\n\tcase XKB_KEY_Hiragana_Katakana:\n\t\treturn OBS_KEY_HIRAGANA_KATAKANA;\n\tcase XKB_KEY_Home:\n\t\treturn OBS_KEY_HOME;\n\tcase XKB_KEY_Hyper_L:\n\t\treturn OBS_KEY_HYPER_L;\n\tcase XKB_KEY_Hyper_R:\n\t\treturn OBS_KEY_HYPER_R;\n\tcase XKB_KEY_hyphen:\n\t\treturn OBS_KEY_HYPHEN;\n\tcase XKB_KEY_I:\n\t\treturn OBS_KEY_I;\n\tcase XKB_KEY_i:\n\t\treturn OBS_KEY_I;\n\tcase XKB_KEY_Iacute:\n\t\treturn OBS_KEY_IACUTE;\n\tcase XKB_KEY_iacute:\n\t\treturn OBS_KEY_IACUTE;\n\tcase XKB_KEY_Icircumflex:\n\t\treturn OBS_KEY_ICIRCUMFLEX;\n\tcase XKB_KEY_icircumflex:\n\t\treturn OBS_KEY_ICIRCUMFLEX;\n\tcase XKB_KEY_Idiaeresis:\n\t\treturn OBS_KEY_IDIAERESIS;\n\tcase XKB_KEY_idiaeresis:\n\t\treturn OBS_KEY_IDIAERESIS;\n\tcase XKB_KEY_Igrave:\n\t\treturn OBS_KEY_IGRAVE;\n\tcase XKB_KEY_igrave:\n\t\treturn OBS_KEY_IGRAVE;\n\tcase XKB_KEY_Insert:\n\t\treturn OBS_KEY_INSERT;\n\tcase XKB_KEY_J:\n\t\treturn OBS_KEY_J;\n\tcase XKB_KEY_j:\n\t\treturn OBS_KEY_J;\n\tcase XKB_KEY_K:\n\t\treturn OBS_KEY_K;\n\tcase XKB_KEY_k:\n\t\treturn OBS_KEY_K;\n\tcase XKB_KEY_Kana_Lock:\n\t\treturn OBS_KEY_KANA_LOCK;\n\tcase XKB_KEY_Kana_Shift:\n\t\treturn OBS_KEY_KANA_SHIFT;\n\tcase XKB_KEY_Kanji:\n\t\treturn OBS_KEY_KANJI;\n\tcase XKB_KEY_Katakana:\n\t\treturn OBS_KEY_KATAKANA;\n\tcase XKB_KEY_L:\n\t\treturn OBS_KEY_L;\n\tcase XKB_KEY_l:\n\t\treturn OBS_KEY_L;\n\tcase XKB_KEY_Left:\n\t\treturn OBS_KEY_LEFT;\n\tcase XKB_KEY_less:\n\t\treturn OBS_KEY_LESS;\n\tcase XKB_KEY_M:\n\t\treturn OBS_KEY_M;\n\tcase XKB_KEY_m:\n\t\treturn OBS_KEY_M;\n\tcase XKB_KEY_macron:\n\t\treturn OBS_KEY_MACRON;\n\tcase XKB_KEY_masculine:\n\t\treturn OBS_KEY_MASCULINE;\n\tcase XKB_KEY_Massyo:\n\t\treturn OBS_KEY_MASSYO;\n\tcase XKB_KEY_Menu:\n\t\treturn OBS_KEY_MENU;\n\tcase XKB_KEY_minus:\n\t\treturn OBS_KEY_MINUS;\n\tcase XKB_KEY_Mode_switch:\n\t\treturn OBS_KEY_MODE_SWITCH;\n\tcase XKB_KEY_mu:\n\t\treturn OBS_KEY_MU;\n\tcase XKB_KEY_Muhenkan:\n\t\treturn OBS_KEY_MUHENKAN;\n\tcase XKB_KEY_MultipleCandidate:\n\t\treturn OBS_KEY_MULTIPLECANDIDATE;\n\tcase XKB_KEY_multiply:\n\t\treturn OBS_KEY_MULTIPLY;\n\tcase XKB_KEY_Multi_key:\n\t\treturn OBS_KEY_MULTI_KEY;\n\tcase XKB_KEY_N:\n\t\treturn OBS_KEY_N;\n\tcase XKB_KEY_n:\n\t\treturn OBS_KEY_N;\n\tcase XKB_KEY_nobreakspace:\n\t\treturn OBS_KEY_NOBREAKSPACE;\n\tcase XKB_KEY_notsign:\n\t\treturn OBS_KEY_NOTSIGN;\n\tcase XKB_KEY_Ntilde:\n\t\treturn OBS_KEY_NTILDE;\n\tcase XKB_KEY_ntilde:\n\t\treturn OBS_KEY_NTILDE;\n\tcase XKB_KEY_numbersign:\n\t\treturn OBS_KEY_NUMBERSIGN;\n\tcase XKB_KEY_O:\n\t\treturn OBS_KEY_O;\n\tcase XKB_KEY_o:\n\t\treturn OBS_KEY_O;\n\tcase XKB_KEY_Oacute:\n\t\treturn OBS_KEY_OACUTE;\n\tcase XKB_KEY_oacute:\n\t\treturn OBS_KEY_OACUTE;\n\tcase XKB_KEY_Ocircumflex:\n\t\treturn OBS_KEY_OCIRCUMFLEX;\n\tcase XKB_KEY_ocircumflex:\n\t\treturn OBS_KEY_OCIRCUMFLEX;\n\tcase XKB_KEY_Odiaeresis:\n\t\treturn OBS_KEY_ODIAERESIS;\n\tcase XKB_KEY_odiaeresis:\n\t\treturn OBS_KEY_ODIAERESIS;\n\tcase XKB_KEY_Ograve:\n\t\treturn OBS_KEY_OGRAVE;\n\tcase XKB_KEY_ograve:\n\t\treturn OBS_KEY_OGRAVE;\n\tcase XKB_KEY_onehalf:\n\t\treturn OBS_KEY_ONEHALF;\n\tcase XKB_KEY_onequarter:\n\t\treturn OBS_KEY_ONEQUARTER;\n\tcase XKB_KEY_onesuperior:\n\t\treturn OBS_KEY_ONESUPERIOR;\n\tcase XKB_KEY_Ooblique:\n\t\treturn OBS_KEY_OOBLIQUE;\n\tcase XKB_KEY_ooblique:\n\t\treturn OBS_KEY_OOBLIQUE;\n\tcase XKB_KEY_ordfeminine:\n\t\treturn OBS_KEY_ORDFEMININE;\n\tcase XKB_KEY_Otilde:\n\t\treturn OBS_KEY_OTILDE;\n\tcase XKB_KEY_otilde:\n\t\treturn OBS_KEY_OTILDE;\n\tcase XKB_KEY_P:\n\t\treturn OBS_KEY_P;\n\tcase XKB_KEY_p:\n\t\treturn OBS_KEY_P;\n\tcase XKB_KEY_paragraph:\n\t\treturn OBS_KEY_PARAGRAPH;\n\tcase XKB_KEY_parenleft:\n\t\treturn OBS_KEY_PARENLEFT;\n\tcase XKB_KEY_parenright:\n\t\treturn OBS_KEY_PARENRIGHT;\n\tcase XKB_KEY_Pause:\n\t\treturn OBS_KEY_PAUSE;\n\tcase XKB_KEY_percent:\n\t\treturn OBS_KEY_PERCENT;\n\tcase XKB_KEY_period:\n\t\treturn OBS_KEY_PERIOD;\n\tcase XKB_KEY_periodcentered:\n\t\treturn OBS_KEY_PERIODCENTERED;\n\tcase XKB_KEY_plus:\n\t\treturn OBS_KEY_PLUS;\n\tcase XKB_KEY_plusminus:\n\t\treturn OBS_KEY_PLUSMINUS;\n\tcase XKB_KEY_PreviousCandidate:\n\t\treturn OBS_KEY_PREVIOUSCANDIDATE;\n\tcase XKB_KEY_Print:\n\t\treturn OBS_KEY_PRINT;\n\tcase XKB_KEY_Q:\n\t\treturn OBS_KEY_Q;\n\tcase XKB_KEY_q:\n\t\treturn OBS_KEY_Q;\n\tcase XKB_KEY_question:\n\t\treturn OBS_KEY_QUESTION;\n\tcase XKB_KEY_questiondown:\n\t\treturn OBS_KEY_QUESTIONDOWN;\n\tcase XKB_KEY_quotedbl:\n\t\treturn OBS_KEY_QUOTEDBL;\n\tcase XKB_KEY_quoteleft:\n\t\treturn OBS_KEY_QUOTELEFT;\n\tcase XKB_KEY_R:\n\t\treturn OBS_KEY_R;\n\tcase XKB_KEY_r:\n\t\treturn OBS_KEY_R;\n\tcase XKB_KEY_Redo:\n\t\treturn OBS_KEY_REDO;\n\tcase XKB_KEY_registered:\n\t\treturn OBS_KEY_REGISTERED;\n\tcase XKB_KEY_Return:\n\t\treturn OBS_KEY_RETURN;\n\tcase XKB_KEY_Right:\n\t\treturn OBS_KEY_RIGHT;\n\tcase XKB_KEY_Romaji:\n\t\treturn OBS_KEY_ROMAJI;\n\tcase XKB_KEY_S:\n\t\treturn OBS_KEY_S;\n\tcase XKB_KEY_s:\n\t\treturn OBS_KEY_S;\n\tcase XKB_KEY_section:\n\t\treturn OBS_KEY_SECTION;\n\tcase XKB_KEY_Select:\n\t\treturn OBS_KEY_SELECT;\n\tcase XKB_KEY_semicolon:\n\t\treturn OBS_KEY_SEMICOLON;\n\tcase XKB_KEY_SingleCandidate:\n\t\treturn OBS_KEY_SINGLECANDIDATE;\n\tcase XKB_KEY_slash:\n\t\treturn OBS_KEY_SLASH;\n\tcase XKB_KEY_space:\n\t\treturn OBS_KEY_SPACE;\n\tcase XKB_KEY_ssharp:\n\t\treturn OBS_KEY_SSHARP;\n\tcase XKB_KEY_sterling:\n\t\treturn OBS_KEY_STERLING;\n\tcase XKB_KEY_T:\n\t\treturn OBS_KEY_T;\n\tcase XKB_KEY_t:\n\t\treturn OBS_KEY_T;\n\tcase XKB_KEY_Tab:\n\t\treturn OBS_KEY_TAB;\n\tcase XKB_KEY_Thorn:\n\t\treturn OBS_KEY_THORN;\n\tcase XKB_KEY_thorn:\n\t\treturn OBS_KEY_THORN;\n\tcase XKB_KEY_threequarters:\n\t\treturn OBS_KEY_THREEQUARTERS;\n\tcase XKB_KEY_threesuperior:\n\t\treturn OBS_KEY_THREESUPERIOR;\n\tcase XKB_KEY_Touroku:\n\t\treturn OBS_KEY_TOUROKU;\n\tcase XKB_KEY_twosuperior:\n\t\treturn OBS_KEY_TWOSUPERIOR;\n\tcase XKB_KEY_U:\n\t\treturn OBS_KEY_U;\n\tcase XKB_KEY_u:\n\t\treturn OBS_KEY_U;\n\tcase XKB_KEY_Uacute:\n\t\treturn OBS_KEY_UACUTE;\n\tcase XKB_KEY_uacute:\n\t\treturn OBS_KEY_UACUTE;\n\tcase XKB_KEY_Ucircumflex:\n\t\treturn OBS_KEY_UCIRCUMFLEX;\n\tcase XKB_KEY_ucircumflex:\n\t\treturn OBS_KEY_UCIRCUMFLEX;\n\tcase XKB_KEY_Udiaeresis:\n\t\treturn OBS_KEY_UDIAERESIS;\n\tcase XKB_KEY_udiaeresis:\n\t\treturn OBS_KEY_UDIAERESIS;\n\tcase XKB_KEY_Ugrave:\n\t\treturn OBS_KEY_UGRAVE;\n\tcase XKB_KEY_ugrave:\n\t\treturn OBS_KEY_UGRAVE;\n\tcase XKB_KEY_underscore:\n\t\treturn OBS_KEY_UNDERSCORE;\n\tcase XKB_KEY_Undo:\n\t\treturn OBS_KEY_UNDO;\n\tcase XKB_KEY_Up:\n\t\treturn OBS_KEY_UP;\n\tcase XKB_KEY_V:\n\t\treturn OBS_KEY_V;\n\tcase XKB_KEY_v:\n\t\treturn OBS_KEY_V;\n\tcase XKB_KEY_W:\n\t\treturn OBS_KEY_W;\n\tcase XKB_KEY_w:\n\t\treturn OBS_KEY_W;\n\tcase XKB_KEY_X:\n\t\treturn OBS_KEY_X;\n\tcase XKB_KEY_x:\n\t\treturn OBS_KEY_X;\n\tcase XKB_KEY_Y:\n\t\treturn OBS_KEY_Y;\n\tcase XKB_KEY_y:\n\t\treturn OBS_KEY_Y;\n\tcase XKB_KEY_Yacute:\n\t\treturn OBS_KEY_YACUTE;\n\tcase XKB_KEY_yacute:\n\t\treturn OBS_KEY_YACUTE;\n\tcase XKB_KEY_Ydiaeresis:\n\t\treturn OBS_KEY_YDIAERESIS;\n\tcase XKB_KEY_ydiaeresis:\n\t\treturn OBS_KEY_YDIAERESIS;\n\tcase XKB_KEY_yen:\n\t\treturn OBS_KEY_YEN;\n\tcase XKB_KEY_Z:\n\t\treturn OBS_KEY_Z;\n\tcase XKB_KEY_z:\n\t\treturn OBS_KEY_Z;\n\tcase XKB_KEY_Zenkaku:\n\t\treturn OBS_KEY_ZENKAKU;\n\tcase XKB_KEY_Zenkaku_Hankaku:\n\t\treturn OBS_KEY_ZENKAKU_HANKAKU;\n\n\tcase XKB_KEY_Page_Up:\n\t\treturn OBS_KEY_PAGEUP;\n\tcase XKB_KEY_Page_Down:\n\t\treturn OBS_KEY_PAGEDOWN;\n\n\tcase XKB_KEY_KP_Equal:\n\t\treturn OBS_KEY_NUMEQUAL;\n\tcase XKB_KEY_KP_Multiply:\n\t\treturn OBS_KEY_NUMASTERISK;\n\tcase XKB_KEY_KP_Add:\n\t\treturn OBS_KEY_NUMPLUS;\n\tcase XKB_KEY_KP_Separator:\n\t\treturn OBS_KEY_NUMCOMMA;\n\tcase XKB_KEY_KP_Subtract:\n\t\treturn OBS_KEY_NUMMINUS;\n\tcase XKB_KEY_KP_Decimal:\n\t\treturn OBS_KEY_NUMPERIOD;\n\tcase XKB_KEY_KP_Divide:\n\t\treturn OBS_KEY_NUMSLASH;\n\tcase XKB_KEY_KP_Enter:\n\t\treturn OBS_KEY_ENTER;\n\n\tcase XKB_KEY_KP_0:\n\t\treturn OBS_KEY_NUM0;\n\tcase XKB_KEY_KP_1:\n\t\treturn OBS_KEY_NUM1;\n\tcase XKB_KEY_KP_2:\n\t\treturn OBS_KEY_NUM2;\n\tcase XKB_KEY_KP_3:\n\t\treturn OBS_KEY_NUM3;\n\tcase XKB_KEY_KP_4:\n\t\treturn OBS_KEY_NUM4;\n\tcase XKB_KEY_KP_5:\n\t\treturn OBS_KEY_NUM5;\n\tcase XKB_KEY_KP_6:\n\t\treturn OBS_KEY_NUM6;\n\tcase XKB_KEY_KP_7:\n\t\treturn OBS_KEY_NUM7;\n\tcase XKB_KEY_KP_8:\n\t\treturn OBS_KEY_NUM8;\n\tcase XKB_KEY_KP_9:\n\t\treturn OBS_KEY_NUM9;\n\n\tcase XKB_KEY_XF86AudioPlay:\n\t\treturn OBS_KEY_VK_MEDIA_PLAY_PAUSE;\n\tcase XKB_KEY_XF86AudioStop:\n\t\treturn OBS_KEY_VK_MEDIA_STOP;\n\tcase XKB_KEY_XF86AudioPrev:\n\t\treturn OBS_KEY_VK_MEDIA_PREV_TRACK;\n\tcase XKB_KEY_XF86AudioNext:\n\t\treturn OBS_KEY_VK_MEDIA_NEXT_TRACK;\n\tcase XKB_KEY_XF86AudioMute:\n\t\treturn OBS_KEY_VK_VOLUME_MUTE;\n\tcase XKB_KEY_XF86AudioRaiseVolume:\n\t\treturn OBS_KEY_VK_VOLUME_DOWN;\n\tcase XKB_KEY_XF86AudioLowerVolume:\n\t\treturn OBS_KEY_VK_VOLUME_UP;\n\t}\n\treturn OBS_KEY_NONE;\n}\n\nstatic int obs_nix_wayland_key_to_virtual_key(obs_key_t key)\n{\n\tswitch (key) {\n\tcase OBS_KEY_0:\n\t\treturn XKB_KEY_0;\n\tcase OBS_KEY_1:\n\t\treturn XKB_KEY_1;\n\tcase OBS_KEY_2:\n\t\treturn XKB_KEY_2;\n\tcase OBS_KEY_3:\n\t\treturn XKB_KEY_3;\n\tcase OBS_KEY_4:\n\t\treturn XKB_KEY_4;\n\tcase OBS_KEY_5:\n\t\treturn XKB_KEY_5;\n\tcase OBS_KEY_6:\n\t\treturn XKB_KEY_6;\n\tcase OBS_KEY_7:\n\t\treturn XKB_KEY_7;\n\tcase OBS_KEY_8:\n\t\treturn XKB_KEY_8;\n\tcase OBS_KEY_9:\n\t\treturn XKB_KEY_9;\n\tcase OBS_KEY_A:\n\t\treturn XKB_KEY_A;\n\tcase OBS_KEY_AACUTE:\n\t\treturn XKB_KEY_Aacute;\n\tcase OBS_KEY_ACIRCUMFLEX:\n\t\treturn XKB_KEY_Acircumflex;\n\tcase OBS_KEY_ACUTE:\n\t\treturn XKB_KEY_acute;\n\tcase OBS_KEY_ADIAERESIS:\n\t\treturn XKB_KEY_Adiaeresis;\n\tcase OBS_KEY_AE:\n\t\treturn XKB_KEY_AE;\n\tcase OBS_KEY_AGRAVE:\n\t\treturn XKB_KEY_Agrave;\n\tcase OBS_KEY_AMPERSAND:\n\t\treturn XKB_KEY_ampersand;\n\tcase OBS_KEY_APOSTROPHE:\n\t\treturn XKB_KEY_apostrophe;\n\tcase OBS_KEY_ARING:\n\t\treturn XKB_KEY_Aring;\n\tcase OBS_KEY_ASCIICIRCUM:\n\t\treturn XKB_KEY_asciicircum;\n\tcase OBS_KEY_ASCIITILDE:\n\t\treturn XKB_KEY_asciitilde;\n\tcase OBS_KEY_ASTERISK:\n\t\treturn XKB_KEY_asterisk;\n\tcase OBS_KEY_AT:\n\t\treturn XKB_KEY_at;\n\tcase OBS_KEY_ATILDE:\n\t\treturn XKB_KEY_Atilde;\n\tcase OBS_KEY_B:\n\t\treturn XKB_KEY_B;\n\tcase OBS_KEY_BACKSLASH:\n\t\treturn XKB_KEY_backslash;\n\tcase OBS_KEY_BACKSPACE:\n\t\treturn XKB_KEY_BackSpace;\n\tcase OBS_KEY_BACKTAB:\n\t\treturn XKB_KEY_BackTab;\n\tcase OBS_KEY_BAR:\n\t\treturn XKB_KEY_bar;\n\tcase OBS_KEY_BRACELEFT:\n\t\treturn XKB_KEY_braceleft;\n\tcase OBS_KEY_BRACERIGHT:\n\t\treturn XKB_KEY_braceright;\n\tcase OBS_KEY_BRACKETLEFT:\n\t\treturn XKB_KEY_bracketleft;\n\tcase OBS_KEY_BRACKETRIGHT:\n\t\treturn XKB_KEY_bracketright;\n\tcase OBS_KEY_BROKENBAR:\n\t\treturn XKB_KEY_brokenbar;\n\tcase OBS_KEY_C:\n\t\treturn XKB_KEY_C;\n\tcase OBS_KEY_CANCEL:\n\t\treturn XKB_KEY_Cancel;\n\tcase OBS_KEY_CCEDILLA:\n\t\treturn XKB_KEY_Ccedilla;\n\tcase OBS_KEY_CEDILLA:\n\t\treturn XKB_KEY_cedilla;\n\tcase OBS_KEY_CENT:\n\t\treturn XKB_KEY_cent;\n\tcase OBS_KEY_CLEAR:\n\t\treturn XKB_KEY_Clear;\n\tcase OBS_KEY_CODEINPUT:\n\t\treturn XKB_KEY_Codeinput;\n\tcase OBS_KEY_COLON:\n\t\treturn XKB_KEY_colon;\n\tcase OBS_KEY_COMMA:\n\t\treturn XKB_KEY_comma;\n\tcase OBS_KEY_COPYRIGHT:\n\t\treturn XKB_KEY_copyright;\n\tcase OBS_KEY_CURRENCY:\n\t\treturn XKB_KEY_currency;\n\tcase OBS_KEY_D:\n\t\treturn XKB_KEY_D;\n\tcase OBS_KEY_DEAD_ABOVEDOT:\n\t\treturn XKB_KEY_dead_abovedot;\n\tcase OBS_KEY_DEAD_ABOVERING:\n\t\treturn XKB_KEY_dead_abovering;\n\tcase OBS_KEY_DEAD_ACUTE:\n\t\treturn XKB_KEY_dead_acute;\n\tcase OBS_KEY_DEAD_BELOWDOT:\n\t\treturn XKB_KEY_dead_belowdot;\n\tcase OBS_KEY_DEAD_BREVE:\n\t\treturn XKB_KEY_dead_breve;\n\tcase OBS_KEY_DEAD_CARON:\n\t\treturn XKB_KEY_dead_caron;\n\tcase OBS_KEY_DEAD_CEDILLA:\n\t\treturn XKB_KEY_dead_cedilla;\n\tcase OBS_KEY_DEAD_CIRCUMFLEX:\n\t\treturn XKB_KEY_dead_circumflex;\n\tcase OBS_KEY_DEAD_DIAERESIS:\n\t\treturn XKB_KEY_dead_diaeresis;\n\tcase OBS_KEY_DEAD_DOUBLEACUTE:\n\t\treturn XKB_KEY_dead_doubleacute;\n\tcase OBS_KEY_DEAD_GRAVE:\n\t\treturn XKB_KEY_dead_grave;\n\tcase OBS_KEY_DEAD_HOOK:\n\t\treturn XKB_KEY_dead_hook;\n\tcase OBS_KEY_DEAD_HORN:\n\t\treturn XKB_KEY_dead_horn;\n\tcase OBS_KEY_DEAD_IOTA:\n\t\treturn XKB_KEY_dead_iota;\n\tcase OBS_KEY_DEAD_MACRON:\n\t\treturn XKB_KEY_dead_macron;\n\tcase OBS_KEY_DEAD_OGONEK:\n\t\treturn XKB_KEY_dead_ogonek;\n\tcase OBS_KEY_DEAD_SEMIVOICED_SOUND:\n\t\treturn XKB_KEY_dead_semivoiced_sound;\n\tcase OBS_KEY_DEAD_TILDE:\n\t\treturn XKB_KEY_dead_tilde;\n\tcase OBS_KEY_DEAD_VOICED_SOUND:\n\t\treturn XKB_KEY_dead_voiced_sound;\n\tcase OBS_KEY_DEGREE:\n\t\treturn XKB_KEY_degree;\n\tcase OBS_KEY_DELETE:\n\t\treturn XKB_KEY_Delete;\n\tcase OBS_KEY_DIAERESIS:\n\t\treturn XKB_KEY_diaeresis;\n\tcase OBS_KEY_DIVISION:\n\t\treturn XKB_KEY_division;\n\tcase OBS_KEY_DOLLAR:\n\t\treturn XKB_KEY_dollar;\n\tcase OBS_KEY_DOWN:\n\t\treturn XKB_KEY_Down;\n\tcase OBS_KEY_E:\n\t\treturn XKB_KEY_E;\n\tcase OBS_KEY_EACUTE:\n\t\treturn XKB_KEY_Eacute;\n\tcase OBS_KEY_ECIRCUMFLEX:\n\t\treturn XKB_KEY_Ecircumflex;\n\tcase OBS_KEY_EDIAERESIS:\n\t\treturn XKB_KEY_Ediaeresis;\n\tcase OBS_KEY_EGRAVE:\n\t\treturn XKB_KEY_Egrave;\n\tcase OBS_KEY_EISU_SHIFT:\n\t\treturn XKB_KEY_Eisu_Shift;\n\tcase OBS_KEY_EISU_TOGGLE:\n\t\treturn XKB_KEY_Eisu_toggle;\n\tcase OBS_KEY_END:\n\t\treturn XKB_KEY_End;\n\tcase OBS_KEY_EQUAL:\n\t\treturn XKB_KEY_equal;\n\tcase OBS_KEY_ESCAPE:\n\t\treturn XKB_KEY_Escape;\n\tcase OBS_KEY_ETH:\n\t\treturn XKB_KEY_ETH;\n\tcase OBS_KEY_EXCLAM:\n\t\treturn XKB_KEY_exclam;\n\tcase OBS_KEY_EXCLAMDOWN:\n\t\treturn XKB_KEY_exclamdown;\n\tcase OBS_KEY_EXECUTE:\n\t\treturn XKB_KEY_Execute;\n\tcase OBS_KEY_F:\n\t\treturn XKB_KEY_F;\n\tcase OBS_KEY_F1:\n\t\treturn XKB_KEY_F1;\n\tcase OBS_KEY_F10:\n\t\treturn XKB_KEY_F10;\n\tcase OBS_KEY_F11:\n\t\treturn XKB_KEY_F11;\n\tcase OBS_KEY_F12:\n\t\treturn XKB_KEY_F12;\n\tcase OBS_KEY_F13:\n\t\treturn XKB_KEY_F13;\n\tcase OBS_KEY_F14:\n\t\treturn XKB_KEY_F14;\n\tcase OBS_KEY_F15:\n\t\treturn XKB_KEY_F15;\n\tcase OBS_KEY_F16:\n\t\treturn XKB_KEY_F16;\n\tcase OBS_KEY_F17:\n\t\treturn XKB_KEY_F17;\n\tcase OBS_KEY_F18:\n\t\treturn XKB_KEY_F18;\n\tcase OBS_KEY_F19:\n\t\treturn XKB_KEY_F19;\n\tcase OBS_KEY_F2:\n\t\treturn XKB_KEY_F2;\n\tcase OBS_KEY_F20:\n\t\treturn XKB_KEY_F20;\n\tcase OBS_KEY_F21:\n\t\treturn XKB_KEY_F21;\n\tcase OBS_KEY_F22:\n\t\treturn XKB_KEY_F22;\n\tcase OBS_KEY_F23:\n\t\treturn XKB_KEY_F23;\n\tcase OBS_KEY_F24:\n\t\treturn XKB_KEY_F24;\n\tcase OBS_KEY_F25:\n\t\treturn XKB_KEY_F25;\n\tcase OBS_KEY_F26:\n\t\treturn XKB_KEY_F26;\n\tcase OBS_KEY_F27:\n\t\treturn XKB_KEY_F27;\n\tcase OBS_KEY_F28:\n\t\treturn XKB_KEY_F28;\n\tcase OBS_KEY_F29:\n\t\treturn XKB_KEY_F29;\n\tcase OBS_KEY_F3:\n\t\treturn XKB_KEY_F3;\n\tcase OBS_KEY_F30:\n\t\treturn XKB_KEY_F30;\n\tcase OBS_KEY_F31:\n\t\treturn XKB_KEY_F31;\n\tcase OBS_KEY_F32:\n\t\treturn XKB_KEY_F32;\n\tcase OBS_KEY_F33:\n\t\treturn XKB_KEY_F33;\n\tcase OBS_KEY_F34:\n\t\treturn XKB_KEY_F34;\n\tcase OBS_KEY_F35:\n\t\treturn XKB_KEY_F35;\n\tcase OBS_KEY_F4:\n\t\treturn XKB_KEY_F4;\n\tcase OBS_KEY_F5:\n\t\treturn XKB_KEY_F5;\n\tcase OBS_KEY_F6:\n\t\treturn XKB_KEY_F6;\n\tcase OBS_KEY_F7:\n\t\treturn XKB_KEY_F7;\n\tcase OBS_KEY_F8:\n\t\treturn XKB_KEY_F8;\n\tcase OBS_KEY_F9:\n\t\treturn XKB_KEY_F9;\n\tcase OBS_KEY_FIND:\n\t\treturn XKB_KEY_Find;\n\tcase OBS_KEY_G:\n\t\treturn XKB_KEY_G;\n\tcase OBS_KEY_GREATER:\n\t\treturn XKB_KEY_greater;\n\tcase OBS_KEY_GUILLEMOTLEFT:\n\t\treturn XKB_KEY_guillemotleft;\n\tcase OBS_KEY_GUILLEMOTRIGHT:\n\t\treturn XKB_KEY_guillemotright;\n\tcase OBS_KEY_H:\n\t\treturn XKB_KEY_H;\n\tcase OBS_KEY_HANGUL:\n\t\treturn XKB_KEY_Hangul;\n\tcase OBS_KEY_HANGUL_BANJA:\n\t\treturn XKB_KEY_Hangul_Banja;\n\tcase OBS_KEY_HANGUL_END:\n\t\treturn XKB_KEY_Hangul_End;\n\tcase OBS_KEY_HANGUL_HANJA:\n\t\treturn XKB_KEY_Hangul_Hanja;\n\tcase OBS_KEY_HANGUL_JAMO:\n\t\treturn XKB_KEY_Hangul_Jamo;\n\tcase OBS_KEY_HANGUL_JEONJA:\n\t\treturn XKB_KEY_Hangul_Jeonja;\n\tcase OBS_KEY_HANGUL_POSTHANJA:\n\t\treturn XKB_KEY_Hangul_PostHanja;\n\tcase OBS_KEY_HANGUL_PREHANJA:\n\t\treturn XKB_KEY_Hangul_PreHanja;\n\tcase OBS_KEY_HANGUL_ROMAJA:\n\t\treturn XKB_KEY_Hangul_Romaja;\n\tcase OBS_KEY_HANGUL_SPECIAL:\n\t\treturn XKB_KEY_Hangul_Special;\n\tcase OBS_KEY_HANGUL_START:\n\t\treturn XKB_KEY_Hangul_Start;\n\tcase OBS_KEY_HANKAKU:\n\t\treturn XKB_KEY_Hankaku;\n\tcase OBS_KEY_HELP:\n\t\treturn XKB_KEY_Help;\n\tcase OBS_KEY_HENKAN:\n\t\treturn XKB_KEY_Henkan;\n\tcase OBS_KEY_HIRAGANA:\n\t\treturn XKB_KEY_Hiragana;\n\tcase OBS_KEY_HIRAGANA_KATAKANA:\n\t\treturn XKB_KEY_Hiragana_Katakana;\n\tcase OBS_KEY_HOME:\n\t\treturn XKB_KEY_Home;\n\tcase OBS_KEY_HYPER_L:\n\t\treturn XKB_KEY_Hyper_L;\n\tcase OBS_KEY_HYPER_R:\n\t\treturn XKB_KEY_Hyper_R;\n\tcase OBS_KEY_HYPHEN:\n\t\treturn XKB_KEY_hyphen;\n\tcase OBS_KEY_I:\n\t\treturn XKB_KEY_I;\n\tcase OBS_KEY_IACUTE:\n\t\treturn XKB_KEY_Iacute;\n\tcase OBS_KEY_ICIRCUMFLEX:\n\t\treturn XKB_KEY_Icircumflex;\n\tcase OBS_KEY_IDIAERESIS:\n\t\treturn XKB_KEY_Idiaeresis;\n\tcase OBS_KEY_IGRAVE:\n\t\treturn XKB_KEY_Igrave;\n\tcase OBS_KEY_INSERT:\n\t\treturn XKB_KEY_Insert;\n\tcase OBS_KEY_J:\n\t\treturn XKB_KEY_J;\n\tcase OBS_KEY_K:\n\t\treturn XKB_KEY_K;\n\tcase OBS_KEY_KANA_LOCK:\n\t\treturn XKB_KEY_Kana_Lock;\n\tcase OBS_KEY_KANA_SHIFT:\n\t\treturn XKB_KEY_Kana_Shift;\n\tcase OBS_KEY_KANJI:\n\t\treturn XKB_KEY_Kanji;\n\tcase OBS_KEY_KATAKANA:\n\t\treturn XKB_KEY_Katakana;\n\tcase OBS_KEY_L:\n\t\treturn XKB_KEY_L;\n\tcase OBS_KEY_LEFT:\n\t\treturn XKB_KEY_Left;\n\tcase OBS_KEY_LESS:\n\t\treturn XKB_KEY_less;\n\tcase OBS_KEY_M:\n\t\treturn XKB_KEY_M;\n\tcase OBS_KEY_MACRON:\n\t\treturn XKB_KEY_macron;\n\tcase OBS_KEY_MASCULINE:\n\t\treturn XKB_KEY_masculine;\n\tcase OBS_KEY_MASSYO:\n\t\treturn XKB_KEY_Massyo;\n\tcase OBS_KEY_MENU:\n\t\treturn XKB_KEY_Menu;\n\tcase OBS_KEY_MINUS:\n\t\treturn XKB_KEY_minus;\n\tcase OBS_KEY_MODE_SWITCH:\n\t\treturn XKB_KEY_Mode_switch;\n\tcase OBS_KEY_MU:\n\t\treturn XKB_KEY_mu;\n\tcase OBS_KEY_MUHENKAN:\n\t\treturn XKB_KEY_Muhenkan;\n\tcase OBS_KEY_MULTI_KEY:\n\t\treturn XKB_KEY_Multi_key;\n\tcase OBS_KEY_MULTIPLECANDIDATE:\n\t\treturn XKB_KEY_MultipleCandidate;\n\tcase OBS_KEY_MULTIPLY:\n\t\treturn XKB_KEY_multiply;\n\tcase OBS_KEY_N:\n\t\treturn XKB_KEY_N;\n\tcase OBS_KEY_NOBREAKSPACE:\n\t\treturn XKB_KEY_nobreakspace;\n\tcase OBS_KEY_NOTSIGN:\n\t\treturn XKB_KEY_notsign;\n\tcase OBS_KEY_NTILDE:\n\t\treturn XKB_KEY_Ntilde;\n\tcase OBS_KEY_NUMBERSIGN:\n\t\treturn XKB_KEY_numbersign;\n\tcase OBS_KEY_O:\n\t\treturn XKB_KEY_O;\n\tcase OBS_KEY_OACUTE:\n\t\treturn XKB_KEY_Oacute;\n\tcase OBS_KEY_OCIRCUMFLEX:\n\t\treturn XKB_KEY_Ocircumflex;\n\tcase OBS_KEY_ODIAERESIS:\n\t\treturn XKB_KEY_Odiaeresis;\n\tcase OBS_KEY_OGRAVE:\n\t\treturn XKB_KEY_Ograve;\n\tcase OBS_KEY_ONEHALF:\n\t\treturn XKB_KEY_onehalf;\n\tcase OBS_KEY_ONEQUARTER:\n\t\treturn XKB_KEY_onequarter;\n\tcase OBS_KEY_ONESUPERIOR:\n\t\treturn XKB_KEY_onesuperior;\n\tcase OBS_KEY_OOBLIQUE:\n\t\treturn XKB_KEY_Ooblique;\n\tcase OBS_KEY_ORDFEMININE:\n\t\treturn XKB_KEY_ordfeminine;\n\tcase OBS_KEY_OTILDE:\n\t\treturn XKB_KEY_Otilde;\n\tcase OBS_KEY_P:\n\t\treturn XKB_KEY_P;\n\tcase OBS_KEY_PARAGRAPH:\n\t\treturn XKB_KEY_paragraph;\n\tcase OBS_KEY_PARENLEFT:\n\t\treturn XKB_KEY_parenleft;\n\tcase OBS_KEY_PARENRIGHT:\n\t\treturn XKB_KEY_parenright;\n\tcase OBS_KEY_PAUSE:\n\t\treturn XKB_KEY_Pause;\n\tcase OBS_KEY_PERCENT:\n\t\treturn XKB_KEY_percent;\n\tcase OBS_KEY_PERIOD:\n\t\treturn XKB_KEY_period;\n\tcase OBS_KEY_PERIODCENTERED:\n\t\treturn XKB_KEY_periodcentered;\n\tcase OBS_KEY_PLUS:\n\t\treturn XKB_KEY_plus;\n\tcase OBS_KEY_PLUSMINUS:\n\t\treturn XKB_KEY_plusminus;\n\tcase OBS_KEY_PREVIOUSCANDIDATE:\n\t\treturn XKB_KEY_PreviousCandidate;\n\tcase OBS_KEY_PRINT:\n\t\treturn XKB_KEY_Print;\n\tcase OBS_KEY_Q:\n\t\treturn XKB_KEY_Q;\n\tcase OBS_KEY_QUESTION:\n\t\treturn XKB_KEY_question;\n\tcase OBS_KEY_QUESTIONDOWN:\n\t\treturn XKB_KEY_questiondown;\n\tcase OBS_KEY_QUOTEDBL:\n\t\treturn XKB_KEY_quotedbl;\n\tcase OBS_KEY_QUOTELEFT:\n\t\treturn XKB_KEY_quoteleft;\n\tcase OBS_KEY_R:\n\t\treturn XKB_KEY_R;\n\tcase OBS_KEY_REDO:\n\t\treturn XKB_KEY_Redo;\n\tcase OBS_KEY_REGISTERED:\n\t\treturn XKB_KEY_registered;\n\tcase OBS_KEY_RETURN:\n\t\treturn XKB_KEY_Return;\n\tcase OBS_KEY_RIGHT:\n\t\treturn XKB_KEY_Right;\n\tcase OBS_KEY_ROMAJI:\n\t\treturn XKB_KEY_Romaji;\n\tcase OBS_KEY_S:\n\t\treturn XKB_KEY_S;\n\tcase OBS_KEY_SECTION:\n\t\treturn XKB_KEY_section;\n\tcase OBS_KEY_SELECT:\n\t\treturn XKB_KEY_Select;\n\tcase OBS_KEY_SEMICOLON:\n\t\treturn XKB_KEY_semicolon;\n\tcase OBS_KEY_SINGLECANDIDATE:\n\t\treturn XKB_KEY_SingleCandidate;\n\tcase OBS_KEY_SLASH:\n\t\treturn XKB_KEY_slash;\n\tcase OBS_KEY_SPACE:\n\t\treturn XKB_KEY_space;\n\tcase OBS_KEY_SSHARP:\n\t\treturn XKB_KEY_ssharp;\n\tcase OBS_KEY_STERLING:\n\t\treturn XKB_KEY_sterling;\n\tcase OBS_KEY_T:\n\t\treturn XKB_KEY_T;\n\tcase OBS_KEY_TAB:\n\t\treturn XKB_KEY_Tab;\n\tcase OBS_KEY_THORN:\n\t\treturn XKB_KEY_THORN;\n\tcase OBS_KEY_THREEQUARTERS:\n\t\treturn XKB_KEY_threequarters;\n\tcase OBS_KEY_THREESUPERIOR:\n\t\treturn XKB_KEY_threesuperior;\n\tcase OBS_KEY_TOUROKU:\n\t\treturn XKB_KEY_Touroku;\n\tcase OBS_KEY_TWOSUPERIOR:\n\t\treturn XKB_KEY_twosuperior;\n\tcase OBS_KEY_U:\n\t\treturn XKB_KEY_U;\n\tcase OBS_KEY_UACUTE:\n\t\treturn XKB_KEY_Uacute;\n\tcase OBS_KEY_UCIRCUMFLEX:\n\t\treturn XKB_KEY_Ucircumflex;\n\tcase OBS_KEY_UDIAERESIS:\n\t\treturn XKB_KEY_Udiaeresis;\n\tcase OBS_KEY_UGRAVE:\n\t\treturn XKB_KEY_Ugrave;\n\tcase OBS_KEY_UNDERSCORE:\n\t\treturn XKB_KEY_underscore;\n\tcase OBS_KEY_UNDO:\n\t\treturn XKB_KEY_Undo;\n\tcase OBS_KEY_UP:\n\t\treturn XKB_KEY_Up;\n\tcase OBS_KEY_V:\n\t\treturn XKB_KEY_V;\n\tcase OBS_KEY_W:\n\t\treturn XKB_KEY_W;\n\tcase OBS_KEY_X:\n\t\treturn XKB_KEY_X;\n\tcase OBS_KEY_Y:\n\t\treturn XKB_KEY_Y;\n\tcase OBS_KEY_YACUTE:\n\t\treturn XKB_KEY_Yacute;\n\tcase OBS_KEY_YDIAERESIS:\n\t\treturn XKB_KEY_Ydiaeresis;\n\tcase OBS_KEY_YEN:\n\t\treturn XKB_KEY_yen;\n\tcase OBS_KEY_Z:\n\t\treturn XKB_KEY_Z;\n\tcase OBS_KEY_ZENKAKU:\n\t\treturn XKB_KEY_Zenkaku;\n\tcase OBS_KEY_ZENKAKU_HANKAKU:\n\t\treturn XKB_KEY_Zenkaku_Hankaku;\n\n\tcase OBS_KEY_PAGEUP:\n\t\treturn XKB_KEY_Page_Up;\n\tcase OBS_KEY_PAGEDOWN:\n\t\treturn XKB_KEY_Page_Down;\n\n\tcase OBS_KEY_NUMEQUAL:\n\t\treturn XKB_KEY_KP_Equal;\n\tcase OBS_KEY_NUMASTERISK:\n\t\treturn XKB_KEY_KP_Multiply;\n\tcase OBS_KEY_NUMPLUS:\n\t\treturn XKB_KEY_KP_Add;\n\tcase OBS_KEY_NUMCOMMA:\n\t\treturn XKB_KEY_KP_Separator;\n\tcase OBS_KEY_NUMMINUS:\n\t\treturn XKB_KEY_KP_Subtract;\n\tcase OBS_KEY_NUMPERIOD:\n\t\treturn XKB_KEY_KP_Decimal;\n\tcase OBS_KEY_NUMSLASH:\n\t\treturn XKB_KEY_KP_Divide;\n\tcase OBS_KEY_ENTER:\n\t\treturn XKB_KEY_KP_Enter;\n\n\tcase OBS_KEY_NUM0:\n\t\treturn XKB_KEY_KP_0;\n\tcase OBS_KEY_NUM1:\n\t\treturn XKB_KEY_KP_1;\n\tcase OBS_KEY_NUM2:\n\t\treturn XKB_KEY_KP_2;\n\tcase OBS_KEY_NUM3:\n\t\treturn XKB_KEY_KP_3;\n\tcase OBS_KEY_NUM4:\n\t\treturn XKB_KEY_KP_4;\n\tcase OBS_KEY_NUM5:\n\t\treturn XKB_KEY_KP_5;\n\tcase OBS_KEY_NUM6:\n\t\treturn XKB_KEY_KP_6;\n\tcase OBS_KEY_NUM7:\n\t\treturn XKB_KEY_KP_7;\n\tcase OBS_KEY_NUM8:\n\t\treturn XKB_KEY_KP_8;\n\tcase OBS_KEY_NUM9:\n\t\treturn XKB_KEY_KP_9;\n\n\tcase OBS_KEY_VK_MEDIA_PLAY_PAUSE:\n\t\treturn XKB_KEY_XF86AudioPlay;\n\tcase OBS_KEY_VK_MEDIA_STOP:\n\t\treturn XKB_KEY_XF86AudioStop;\n\tcase OBS_KEY_VK_MEDIA_PREV_TRACK:\n\t\treturn XKB_KEY_XF86AudioPrev;\n\tcase OBS_KEY_VK_MEDIA_NEXT_TRACK:\n\t\treturn XKB_KEY_XF86AudioNext;\n\tcase OBS_KEY_VK_VOLUME_MUTE:\n\t\treturn XKB_KEY_XF86AudioMute;\n\tcase OBS_KEY_VK_VOLUME_DOWN:\n\t\treturn XKB_KEY_XF86AudioRaiseVolume;\n\tcase OBS_KEY_VK_VOLUME_UP:\n\t\treturn XKB_KEY_XF86AudioLowerVolume;\n\tdefault:\n\t\tbreak;\n\t}\n\treturn 0;\n}\n\nstatic const struct obs_nix_hotkeys_vtable wayland_hotkeys_vtable = {\n\t.init = obs_nix_wayland_hotkeys_platform_init,\n\t.free = obs_nix_wayland_hotkeys_platform_free,\n\t.is_pressed = obs_nix_wayland_hotkeys_platform_is_pressed,\n\t.key_to_str = obs_nix_wayland_key_to_str,\n\t.key_from_virtual_key = obs_nix_wayland_key_from_virtual_key,\n\t.key_to_virtual_key = obs_nix_wayland_key_to_virtual_key,\n\n};\n\nconst struct obs_nix_hotkeys_vtable *obs_nix_wayland_get_hotkeys_vtable(void)\n{\n\treturn &wayland_hotkeys_vtable;\n}\n"
  },
  {
    "path": "libobs/obs-nix-wayland.h",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"obs-nix.h\"\n\nvoid obs_nix_wayland_log_info(void);\n\nconst struct obs_nix_hotkeys_vtable *obs_nix_wayland_get_hotkeys_vtable(void);\n"
  },
  {
    "path": "libobs/obs-nix-x11.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n    Copyright (C) 2014 by Zachary Lund <admin@computerquip.com>\n    Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-internal.h\"\n#include \"obs-nix-platform.h\"\n#include \"obs-nix-x11.h\"\n\n#include <xcb/xcb.h>\n#if defined(XCB_XINPUT_FOUND)\n#include <xcb/xinput.h>\n#endif\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n#include <X11/Xlib-xcb.h>\n#include <X11/XF86keysym.h>\n#include <X11/Sunkeysym.h>\n\nvoid obs_nix_x11_log_info(void)\n{\n\tDisplay *dpy = obs_get_nix_platform_display();\n\tif (!dpy) {\n\t\tblog(LOG_INFO, \"Unable to open X display\");\n\t\treturn;\n\t}\n\n\tint protocol_version = ProtocolVersion(dpy);\n\tint protocol_revision = ProtocolRevision(dpy);\n\tint vendor_release = VendorRelease(dpy);\n\tconst char *vendor_name = ServerVendor(dpy);\n\n\tif (strstr(vendor_name, \"X.Org\")) {\n\t\tblog(LOG_INFO,\n\t\t     \"Window System: X%d.%d, Vendor: %s, Version: %d\"\n\t\t     \".%d.%d\",\n\t\t     protocol_version, protocol_revision, vendor_name, vendor_release / 10000000,\n\t\t     (vendor_release / 100000) % 100, (vendor_release / 1000) % 100);\n\t} else {\n\t\tblog(LOG_INFO,\n\t\t     \"Window System: X%d.%d - vendor string: %s - \"\n\t\t     \"vendor release: %d\",\n\t\t     protocol_version, protocol_revision, vendor_name, vendor_release);\n\t}\n}\n\n/* So here's how linux works with key mapping:\n *\n * First, there's a global key symbol enum (xcb_keysym_t) which has unique\n * values for all possible symbols keys can have (e.g., '1' and '!' are\n * different values).\n *\n * Then there's a key code (xcb_keycode_t), which is basically an index to the\n * actual key itself on the keyboard (e.g., '1' and '!' will share the same\n * value).\n *\n * xcb_keysym_t values should be given to libobs, and libobs will translate it\n * to an obs_key_t, and although xcb_keysym_t can differ ('!' vs '1'), it will\n * get the obs_key_t value that represents the actual key pressed; in other\n * words it will be based on the key code rather than the key symbol.  The same\n * applies to checking key press states.\n */\n\nstruct keycode_list {\n\tDARRAY(xcb_keycode_t) list;\n};\n\nstruct obs_hotkeys_platform {\n\tDisplay *display;\n\txcb_keysym_t base_keysyms[OBS_KEY_LAST_VALUE];\n\tstruct keycode_list keycodes[OBS_KEY_LAST_VALUE];\n\txcb_keycode_t min_keycode;\n\txcb_keycode_t super_l_code;\n\txcb_keycode_t super_r_code;\n\n\t/* stores a copy of the keysym map for keycodes */\n\txcb_keysym_t *keysyms;\n\tint num_keysyms;\n\tint syms_per_code;\n\n#if defined(XCB_XINPUT_FOUND)\n\tbool pressed[XINPUT_MOUSE_LEN];\n\tbool update[XINPUT_MOUSE_LEN];\n\tbool button_pressed[XINPUT_MOUSE_LEN];\n#endif\n};\n\n#define MOUSE_1 (1 << 16)\n#define MOUSE_2 (2 << 16)\n#define MOUSE_3 (3 << 16)\n#define MOUSE_4 (4 << 16)\n#define MOUSE_5 (5 << 16)\n\nstatic int get_keysym(obs_key_t key)\n{\n\tswitch (key) {\n\tcase OBS_KEY_RETURN:\n\t\treturn XK_Return;\n\tcase OBS_KEY_ESCAPE:\n\t\treturn XK_Escape;\n\tcase OBS_KEY_TAB:\n\t\treturn XK_Tab;\n\tcase OBS_KEY_BACKSPACE:\n\t\treturn XK_BackSpace;\n\tcase OBS_KEY_INSERT:\n\t\treturn XK_Insert;\n\tcase OBS_KEY_DELETE:\n\t\treturn XK_Delete;\n\tcase OBS_KEY_PAUSE:\n\t\treturn XK_Pause;\n\tcase OBS_KEY_PRINT:\n\t\treturn XK_Print;\n\tcase OBS_KEY_HOME:\n\t\treturn XK_Home;\n\tcase OBS_KEY_END:\n\t\treturn XK_End;\n\tcase OBS_KEY_LEFT:\n\t\treturn XK_Left;\n\tcase OBS_KEY_UP:\n\t\treturn XK_Up;\n\tcase OBS_KEY_RIGHT:\n\t\treturn XK_Right;\n\tcase OBS_KEY_DOWN:\n\t\treturn XK_Down;\n\tcase OBS_KEY_PAGEUP:\n\t\treturn XK_Prior;\n\tcase OBS_KEY_PAGEDOWN:\n\t\treturn XK_Next;\n\n\tcase OBS_KEY_SHIFT:\n\t\treturn XK_Shift_L;\n\tcase OBS_KEY_CONTROL:\n\t\treturn XK_Control_L;\n\tcase OBS_KEY_ALT:\n\t\treturn XK_Alt_L;\n\tcase OBS_KEY_CAPSLOCK:\n\t\treturn XK_Caps_Lock;\n\tcase OBS_KEY_NUMLOCK:\n\t\treturn XK_Num_Lock;\n\tcase OBS_KEY_SCROLLLOCK:\n\t\treturn XK_Scroll_Lock;\n\n\tcase OBS_KEY_F1:\n\t\treturn XK_F1;\n\tcase OBS_KEY_F2:\n\t\treturn XK_F2;\n\tcase OBS_KEY_F3:\n\t\treturn XK_F3;\n\tcase OBS_KEY_F4:\n\t\treturn XK_F4;\n\tcase OBS_KEY_F5:\n\t\treturn XK_F5;\n\tcase OBS_KEY_F6:\n\t\treturn XK_F6;\n\tcase OBS_KEY_F7:\n\t\treturn XK_F7;\n\tcase OBS_KEY_F8:\n\t\treturn XK_F8;\n\tcase OBS_KEY_F9:\n\t\treturn XK_F9;\n\tcase OBS_KEY_F10:\n\t\treturn XK_F10;\n\tcase OBS_KEY_F11:\n\t\treturn XK_F11;\n\tcase OBS_KEY_F12:\n\t\treturn XK_F12;\n\tcase OBS_KEY_F13:\n\t\treturn XK_F13;\n\tcase OBS_KEY_F14:\n\t\treturn XK_F14;\n\tcase OBS_KEY_F15:\n\t\treturn XK_F15;\n\tcase OBS_KEY_F16:\n\t\treturn XK_F16;\n\tcase OBS_KEY_F17:\n\t\treturn XK_F17;\n\tcase OBS_KEY_F18:\n\t\treturn XK_F18;\n\tcase OBS_KEY_F19:\n\t\treturn XK_F19;\n\tcase OBS_KEY_F20:\n\t\treturn XK_F20;\n\tcase OBS_KEY_F21:\n\t\treturn XK_F21;\n\tcase OBS_KEY_F22:\n\t\treturn XK_F22;\n\tcase OBS_KEY_F23:\n\t\treturn XK_F23;\n\tcase OBS_KEY_F24:\n\t\treturn XK_F24;\n\tcase OBS_KEY_F25:\n\t\treturn XK_F25;\n\tcase OBS_KEY_F26:\n\t\treturn XK_F26;\n\tcase OBS_KEY_F27:\n\t\treturn XK_F27;\n\tcase OBS_KEY_F28:\n\t\treturn XK_F28;\n\tcase OBS_KEY_F29:\n\t\treturn XK_F29;\n\tcase OBS_KEY_F30:\n\t\treturn XK_F30;\n\tcase OBS_KEY_F31:\n\t\treturn XK_F31;\n\tcase OBS_KEY_F32:\n\t\treturn XK_F32;\n\tcase OBS_KEY_F33:\n\t\treturn XK_F33;\n\tcase OBS_KEY_F34:\n\t\treturn XK_F34;\n\tcase OBS_KEY_F35:\n\t\treturn XK_F35;\n\n\tcase OBS_KEY_MENU:\n\t\treturn XK_Menu;\n\tcase OBS_KEY_HYPER_L:\n\t\treturn XK_Hyper_L;\n\tcase OBS_KEY_HYPER_R:\n\t\treturn XK_Hyper_R;\n\tcase OBS_KEY_HELP:\n\t\treturn XK_Help;\n\tcase OBS_KEY_CANCEL:\n\t\treturn XK_Cancel;\n\tcase OBS_KEY_FIND:\n\t\treturn XK_Find;\n\tcase OBS_KEY_REDO:\n\t\treturn XK_Redo;\n\tcase OBS_KEY_UNDO:\n\t\treturn XK_Undo;\n\tcase OBS_KEY_SPACE:\n\t\treturn XK_space;\n\n\tcase OBS_KEY_COPY:\n\t\treturn XF86XK_Copy;\n\tcase OBS_KEY_CUT:\n\t\treturn XF86XK_Cut;\n\tcase OBS_KEY_OPEN:\n\t\treturn XF86XK_Open;\n\tcase OBS_KEY_PASTE:\n\t\treturn XF86XK_Paste;\n\tcase OBS_KEY_FRONT:\n\t\treturn SunXK_Front;\n\tcase OBS_KEY_PROPS:\n\t\treturn SunXK_Props;\n\n\tcase OBS_KEY_EXCLAM:\n\t\treturn XK_exclam;\n\tcase OBS_KEY_QUOTEDBL:\n\t\treturn XK_quotedbl;\n\tcase OBS_KEY_NUMBERSIGN:\n\t\treturn XK_numbersign;\n\tcase OBS_KEY_DOLLAR:\n\t\treturn XK_dollar;\n\tcase OBS_KEY_PERCENT:\n\t\treturn XK_percent;\n\tcase OBS_KEY_AMPERSAND:\n\t\treturn XK_ampersand;\n\tcase OBS_KEY_APOSTROPHE:\n\t\treturn XK_apostrophe;\n\tcase OBS_KEY_PARENLEFT:\n\t\treturn XK_parenleft;\n\tcase OBS_KEY_PARENRIGHT:\n\t\treturn XK_parenright;\n\tcase OBS_KEY_ASTERISK:\n\t\treturn XK_asterisk;\n\tcase OBS_KEY_PLUS:\n\t\treturn XK_plus;\n\tcase OBS_KEY_COMMA:\n\t\treturn XK_comma;\n\tcase OBS_KEY_MINUS:\n\t\treturn XK_minus;\n\tcase OBS_KEY_PERIOD:\n\t\treturn XK_period;\n\tcase OBS_KEY_SLASH:\n\t\treturn XK_slash;\n\tcase OBS_KEY_0:\n\t\treturn XK_0;\n\tcase OBS_KEY_1:\n\t\treturn XK_1;\n\tcase OBS_KEY_2:\n\t\treturn XK_2;\n\tcase OBS_KEY_3:\n\t\treturn XK_3;\n\tcase OBS_KEY_4:\n\t\treturn XK_4;\n\tcase OBS_KEY_5:\n\t\treturn XK_5;\n\tcase OBS_KEY_6:\n\t\treturn XK_6;\n\tcase OBS_KEY_7:\n\t\treturn XK_7;\n\tcase OBS_KEY_8:\n\t\treturn XK_8;\n\tcase OBS_KEY_9:\n\t\treturn XK_9;\n\tcase OBS_KEY_NUMEQUAL:\n\t\treturn XK_KP_Equal;\n\tcase OBS_KEY_NUMASTERISK:\n\t\treturn XK_KP_Multiply;\n\tcase OBS_KEY_NUMPLUS:\n\t\treturn XK_KP_Add;\n\tcase OBS_KEY_NUMCOMMA:\n\t\treturn XK_KP_Separator;\n\tcase OBS_KEY_NUMMINUS:\n\t\treturn XK_KP_Subtract;\n\tcase OBS_KEY_NUMPERIOD:\n\t\treturn XK_KP_Decimal;\n\tcase OBS_KEY_NUMSLASH:\n\t\treturn XK_KP_Divide;\n\tcase OBS_KEY_NUM0:\n\t\treturn XK_KP_0;\n\tcase OBS_KEY_NUM1:\n\t\treturn XK_KP_1;\n\tcase OBS_KEY_NUM2:\n\t\treturn XK_KP_2;\n\tcase OBS_KEY_NUM3:\n\t\treturn XK_KP_3;\n\tcase OBS_KEY_NUM4:\n\t\treturn XK_KP_4;\n\tcase OBS_KEY_NUM5:\n\t\treturn XK_KP_5;\n\tcase OBS_KEY_NUM6:\n\t\treturn XK_KP_6;\n\tcase OBS_KEY_NUM7:\n\t\treturn XK_KP_7;\n\tcase OBS_KEY_NUM8:\n\t\treturn XK_KP_8;\n\tcase OBS_KEY_NUM9:\n\t\treturn XK_KP_9;\n\tcase OBS_KEY_COLON:\n\t\treturn XK_colon;\n\tcase OBS_KEY_SEMICOLON:\n\t\treturn XK_semicolon;\n\tcase OBS_KEY_LESS:\n\t\treturn XK_less;\n\tcase OBS_KEY_EQUAL:\n\t\treturn XK_equal;\n\tcase OBS_KEY_GREATER:\n\t\treturn XK_greater;\n\tcase OBS_KEY_QUESTION:\n\t\treturn XK_question;\n\tcase OBS_KEY_AT:\n\t\treturn XK_at;\n\tcase OBS_KEY_A:\n\t\treturn XK_A;\n\tcase OBS_KEY_B:\n\t\treturn XK_B;\n\tcase OBS_KEY_C:\n\t\treturn XK_C;\n\tcase OBS_KEY_D:\n\t\treturn XK_D;\n\tcase OBS_KEY_E:\n\t\treturn XK_E;\n\tcase OBS_KEY_F:\n\t\treturn XK_F;\n\tcase OBS_KEY_G:\n\t\treturn XK_G;\n\tcase OBS_KEY_H:\n\t\treturn XK_H;\n\tcase OBS_KEY_I:\n\t\treturn XK_I;\n\tcase OBS_KEY_J:\n\t\treturn XK_J;\n\tcase OBS_KEY_K:\n\t\treturn XK_K;\n\tcase OBS_KEY_L:\n\t\treturn XK_L;\n\tcase OBS_KEY_M:\n\t\treturn XK_M;\n\tcase OBS_KEY_N:\n\t\treturn XK_N;\n\tcase OBS_KEY_O:\n\t\treturn XK_O;\n\tcase OBS_KEY_P:\n\t\treturn XK_P;\n\tcase OBS_KEY_Q:\n\t\treturn XK_Q;\n\tcase OBS_KEY_R:\n\t\treturn XK_R;\n\tcase OBS_KEY_S:\n\t\treturn XK_S;\n\tcase OBS_KEY_T:\n\t\treturn XK_T;\n\tcase OBS_KEY_U:\n\t\treturn XK_U;\n\tcase OBS_KEY_V:\n\t\treturn XK_V;\n\tcase OBS_KEY_W:\n\t\treturn XK_W;\n\tcase OBS_KEY_X:\n\t\treturn XK_X;\n\tcase OBS_KEY_Y:\n\t\treturn XK_Y;\n\tcase OBS_KEY_Z:\n\t\treturn XK_Z;\n\tcase OBS_KEY_BRACKETLEFT:\n\t\treturn XK_bracketleft;\n\tcase OBS_KEY_BACKSLASH:\n\t\treturn XK_backslash;\n\tcase OBS_KEY_BRACKETRIGHT:\n\t\treturn XK_bracketright;\n\tcase OBS_KEY_ASCIICIRCUM:\n\t\treturn XK_asciicircum;\n\tcase OBS_KEY_UNDERSCORE:\n\t\treturn XK_underscore;\n\tcase OBS_KEY_QUOTELEFT:\n\t\treturn XK_quoteleft;\n\tcase OBS_KEY_BRACELEFT:\n\t\treturn XK_braceleft;\n\tcase OBS_KEY_BAR:\n\t\treturn XK_bar;\n\tcase OBS_KEY_BRACERIGHT:\n\t\treturn XK_braceright;\n\tcase OBS_KEY_ASCIITILDE:\n\t\treturn XK_grave;\n\tcase OBS_KEY_NOBREAKSPACE:\n\t\treturn XK_nobreakspace;\n\tcase OBS_KEY_EXCLAMDOWN:\n\t\treturn XK_exclamdown;\n\tcase OBS_KEY_CENT:\n\t\treturn XK_cent;\n\tcase OBS_KEY_STERLING:\n\t\treturn XK_sterling;\n\tcase OBS_KEY_CURRENCY:\n\t\treturn XK_currency;\n\tcase OBS_KEY_YEN:\n\t\treturn XK_yen;\n\tcase OBS_KEY_BROKENBAR:\n\t\treturn XK_brokenbar;\n\tcase OBS_KEY_SECTION:\n\t\treturn XK_section;\n\tcase OBS_KEY_DIAERESIS:\n\t\treturn XK_diaeresis;\n\tcase OBS_KEY_COPYRIGHT:\n\t\treturn XK_copyright;\n\tcase OBS_KEY_ORDFEMININE:\n\t\treturn XK_ordfeminine;\n\tcase OBS_KEY_GUILLEMOTLEFT:\n\t\treturn XK_guillemotleft;\n\tcase OBS_KEY_NOTSIGN:\n\t\treturn XK_notsign;\n\tcase OBS_KEY_HYPHEN:\n\t\treturn XK_hyphen;\n\tcase OBS_KEY_REGISTERED:\n\t\treturn XK_registered;\n\tcase OBS_KEY_MACRON:\n\t\treturn XK_macron;\n\tcase OBS_KEY_DEGREE:\n\t\treturn XK_degree;\n\tcase OBS_KEY_PLUSMINUS:\n\t\treturn XK_plusminus;\n\tcase OBS_KEY_TWOSUPERIOR:\n\t\treturn XK_twosuperior;\n\tcase OBS_KEY_THREESUPERIOR:\n\t\treturn XK_threesuperior;\n\tcase OBS_KEY_ACUTE:\n\t\treturn XK_acute;\n\tcase OBS_KEY_MU:\n\t\treturn XK_mu;\n\tcase OBS_KEY_PARAGRAPH:\n\t\treturn XK_paragraph;\n\tcase OBS_KEY_PERIODCENTERED:\n\t\treturn XK_periodcentered;\n\tcase OBS_KEY_CEDILLA:\n\t\treturn XK_cedilla;\n\tcase OBS_KEY_ONESUPERIOR:\n\t\treturn XK_onesuperior;\n\tcase OBS_KEY_MASCULINE:\n\t\treturn XK_masculine;\n\tcase OBS_KEY_GUILLEMOTRIGHT:\n\t\treturn XK_guillemotright;\n\tcase OBS_KEY_ONEQUARTER:\n\t\treturn XK_onequarter;\n\tcase OBS_KEY_ONEHALF:\n\t\treturn XK_onehalf;\n\tcase OBS_KEY_THREEQUARTERS:\n\t\treturn XK_threequarters;\n\tcase OBS_KEY_QUESTIONDOWN:\n\t\treturn XK_questiondown;\n\tcase OBS_KEY_AGRAVE:\n\t\treturn XK_Agrave;\n\tcase OBS_KEY_AACUTE:\n\t\treturn XK_Aacute;\n\tcase OBS_KEY_ACIRCUMFLEX:\n\t\treturn XK_Acircumflex;\n\tcase OBS_KEY_ATILDE:\n\t\treturn XK_Atilde;\n\tcase OBS_KEY_ADIAERESIS:\n\t\treturn XK_Adiaeresis;\n\tcase OBS_KEY_ARING:\n\t\treturn XK_Aring;\n\tcase OBS_KEY_AE:\n\t\treturn XK_AE;\n\tcase OBS_KEY_CCEDILLA:\n\t\treturn XK_cedilla;\n\tcase OBS_KEY_EGRAVE:\n\t\treturn XK_Egrave;\n\tcase OBS_KEY_EACUTE:\n\t\treturn XK_Eacute;\n\tcase OBS_KEY_ECIRCUMFLEX:\n\t\treturn XK_Ecircumflex;\n\tcase OBS_KEY_EDIAERESIS:\n\t\treturn XK_Ediaeresis;\n\tcase OBS_KEY_IGRAVE:\n\t\treturn XK_Igrave;\n\tcase OBS_KEY_IACUTE:\n\t\treturn XK_Iacute;\n\tcase OBS_KEY_ICIRCUMFLEX:\n\t\treturn XK_Icircumflex;\n\tcase OBS_KEY_IDIAERESIS:\n\t\treturn XK_Idiaeresis;\n\tcase OBS_KEY_ETH:\n\t\treturn XK_ETH;\n\tcase OBS_KEY_NTILDE:\n\t\treturn XK_Ntilde;\n\tcase OBS_KEY_OGRAVE:\n\t\treturn XK_Ograve;\n\tcase OBS_KEY_OACUTE:\n\t\treturn XK_Oacute;\n\tcase OBS_KEY_OCIRCUMFLEX:\n\t\treturn XK_Ocircumflex;\n\tcase OBS_KEY_ODIAERESIS:\n\t\treturn XK_Odiaeresis;\n\tcase OBS_KEY_MULTIPLY:\n\t\treturn XK_multiply;\n\tcase OBS_KEY_OOBLIQUE:\n\t\treturn XK_Ooblique;\n\tcase OBS_KEY_UGRAVE:\n\t\treturn XK_Ugrave;\n\tcase OBS_KEY_UACUTE:\n\t\treturn XK_Uacute;\n\tcase OBS_KEY_UCIRCUMFLEX:\n\t\treturn XK_Ucircumflex;\n\tcase OBS_KEY_UDIAERESIS:\n\t\treturn XK_Udiaeresis;\n\tcase OBS_KEY_YACUTE:\n\t\treturn XK_Yacute;\n\tcase OBS_KEY_THORN:\n\t\treturn XK_Thorn;\n\tcase OBS_KEY_SSHARP:\n\t\treturn XK_ssharp;\n\tcase OBS_KEY_DIVISION:\n\t\treturn XK_division;\n\tcase OBS_KEY_YDIAERESIS:\n\t\treturn XK_Ydiaeresis;\n\tcase OBS_KEY_MULTI_KEY:\n\t\treturn XK_Multi_key;\n\tcase OBS_KEY_CODEINPUT:\n\t\treturn XK_Codeinput;\n\tcase OBS_KEY_SINGLECANDIDATE:\n\t\treturn XK_SingleCandidate;\n\tcase OBS_KEY_MULTIPLECANDIDATE:\n\t\treturn XK_MultipleCandidate;\n\tcase OBS_KEY_PREVIOUSCANDIDATE:\n\t\treturn XK_PreviousCandidate;\n\tcase OBS_KEY_MODE_SWITCH:\n\t\treturn XK_Mode_switch;\n\tcase OBS_KEY_KANJI:\n\t\treturn XK_Kanji;\n\tcase OBS_KEY_MUHENKAN:\n\t\treturn XK_Muhenkan;\n\tcase OBS_KEY_HENKAN:\n\t\treturn XK_Henkan;\n\tcase OBS_KEY_ROMAJI:\n\t\treturn XK_Romaji;\n\tcase OBS_KEY_HIRAGANA:\n\t\treturn XK_Hiragana;\n\tcase OBS_KEY_KATAKANA:\n\t\treturn XK_Katakana;\n\tcase OBS_KEY_HIRAGANA_KATAKANA:\n\t\treturn XK_Hiragana_Katakana;\n\tcase OBS_KEY_ZENKAKU:\n\t\treturn XK_Zenkaku;\n\tcase OBS_KEY_HANKAKU:\n\t\treturn XK_Hankaku;\n\tcase OBS_KEY_ZENKAKU_HANKAKU:\n\t\treturn XK_Zenkaku_Hankaku;\n\tcase OBS_KEY_TOUROKU:\n\t\treturn XK_Touroku;\n\tcase OBS_KEY_MASSYO:\n\t\treturn XK_Massyo;\n\tcase OBS_KEY_KANA_LOCK:\n\t\treturn XK_Kana_Lock;\n\tcase OBS_KEY_KANA_SHIFT:\n\t\treturn XK_Kana_Shift;\n\tcase OBS_KEY_EISU_SHIFT:\n\t\treturn XK_Eisu_Shift;\n\tcase OBS_KEY_EISU_TOGGLE:\n\t\treturn XK_Eisu_toggle;\n\tcase OBS_KEY_HANGUL:\n\t\treturn XK_Hangul;\n\tcase OBS_KEY_HANGUL_START:\n\t\treturn XK_Hangul_Start;\n\tcase OBS_KEY_HANGUL_END:\n\t\treturn XK_Hangul_End;\n\tcase OBS_KEY_HANGUL_HANJA:\n\t\treturn XK_Hangul_Hanja;\n\tcase OBS_KEY_HANGUL_JAMO:\n\t\treturn XK_Hangul_Jamo;\n\tcase OBS_KEY_HANGUL_ROMAJA:\n\t\treturn XK_Hangul_Romaja;\n\tcase OBS_KEY_HANGUL_BANJA:\n\t\treturn XK_Hangul_Banja;\n\tcase OBS_KEY_HANGUL_PREHANJA:\n\t\treturn XK_Hangul_PreHanja;\n\tcase OBS_KEY_HANGUL_POSTHANJA:\n\t\treturn XK_Hangul_PostHanja;\n\tcase OBS_KEY_HANGUL_SPECIAL:\n\t\treturn XK_Hangul_Special;\n\tcase OBS_KEY_DEAD_GRAVE:\n\t\treturn XK_dead_grave;\n\tcase OBS_KEY_DEAD_ACUTE:\n\t\treturn XK_dead_acute;\n\tcase OBS_KEY_DEAD_CIRCUMFLEX:\n\t\treturn XK_dead_circumflex;\n\tcase OBS_KEY_DEAD_TILDE:\n\t\treturn XK_dead_tilde;\n\tcase OBS_KEY_DEAD_MACRON:\n\t\treturn XK_dead_macron;\n\tcase OBS_KEY_DEAD_BREVE:\n\t\treturn XK_dead_breve;\n\tcase OBS_KEY_DEAD_ABOVEDOT:\n\t\treturn XK_dead_abovedot;\n\tcase OBS_KEY_DEAD_DIAERESIS:\n\t\treturn XK_dead_diaeresis;\n\tcase OBS_KEY_DEAD_ABOVERING:\n\t\treturn XK_dead_abovering;\n\tcase OBS_KEY_DEAD_DOUBLEACUTE:\n\t\treturn XK_dead_doubleacute;\n\tcase OBS_KEY_DEAD_CARON:\n\t\treturn XK_dead_caron;\n\tcase OBS_KEY_DEAD_CEDILLA:\n\t\treturn XK_dead_cedilla;\n\tcase OBS_KEY_DEAD_OGONEK:\n\t\treturn XK_dead_ogonek;\n\tcase OBS_KEY_DEAD_IOTA:\n\t\treturn XK_dead_iota;\n\tcase OBS_KEY_DEAD_VOICED_SOUND:\n\t\treturn XK_dead_voiced_sound;\n\tcase OBS_KEY_DEAD_SEMIVOICED_SOUND:\n\t\treturn XK_dead_semivoiced_sound;\n\tcase OBS_KEY_DEAD_BELOWDOT:\n\t\treturn XK_dead_belowdot;\n\tcase OBS_KEY_DEAD_HOOK:\n\t\treturn XK_dead_hook;\n\tcase OBS_KEY_DEAD_HORN:\n\t\treturn XK_dead_horn;\n\n\tcase OBS_KEY_MOUSE1:\n\t\treturn MOUSE_1;\n\tcase OBS_KEY_MOUSE2:\n\t\treturn MOUSE_2;\n\tcase OBS_KEY_MOUSE3:\n\t\treturn MOUSE_3;\n\tcase OBS_KEY_MOUSE4:\n\t\treturn MOUSE_4;\n\tcase OBS_KEY_MOUSE5:\n\t\treturn MOUSE_5;\n\n\tcase OBS_KEY_VK_MEDIA_PLAY_PAUSE:\n\t\treturn XF86XK_AudioPlay;\n\tcase OBS_KEY_VK_MEDIA_STOP:\n\t\treturn XF86XK_AudioStop;\n\tcase OBS_KEY_VK_MEDIA_PREV_TRACK:\n\t\treturn XF86XK_AudioPrev;\n\tcase OBS_KEY_VK_MEDIA_NEXT_TRACK:\n\t\treturn XF86XK_AudioNext;\n\tcase OBS_KEY_VK_VOLUME_MUTE:\n\t\treturn XF86XK_AudioMute;\n\tcase OBS_KEY_VK_VOLUME_DOWN:\n\t\treturn XF86XK_AudioRaiseVolume;\n\tcase OBS_KEY_VK_VOLUME_UP:\n\t\treturn XF86XK_AudioLowerVolume;\n\n\t/* TODO: Implement keys for non-US keyboards */\n\tdefault:;\n\t}\n\treturn 0;\n}\n\nstatic inline void fill_base_keysyms(struct obs_core_hotkeys *hotkeys)\n{\n\tfor (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++)\n\t\thotkeys->platform_context->base_keysyms[i] = get_keysym(i);\n}\n\nstatic obs_key_t key_from_base_keysym(obs_hotkeys_platform_t *context, xcb_keysym_t code)\n{\n\tfor (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {\n\t\tif (context->base_keysyms[i] == (xcb_keysym_t)code) {\n\t\t\treturn (obs_key_t)i;\n\t\t}\n\t}\n\n\tswitch (code) {\n\tcase XK_Shift_R:\n\t\treturn OBS_KEY_SHIFT;\n\tcase XK_Control_R:\n\t\treturn OBS_KEY_CONTROL;\n\tcase XK_Alt_R:\n\t\treturn OBS_KEY_ALT;\n\t}\n\n\treturn OBS_KEY_NONE;\n}\n\nstatic inline void add_key(obs_hotkeys_platform_t *context, obs_key_t key, int code)\n{\n\txcb_keycode_t kc = (xcb_keycode_t)code;\n\tda_push_back(context->keycodes[key].list, &kc);\n\n\tif (context->keycodes[key].list.num > 1) {\n\t\tblog(LOG_DEBUG,\n\t\t     \"found alternate keycode %d for %s \"\n\t\t     \"which already has keycode %d\",\n\t\t     code, obs_key_to_name(key), (int)context->keycodes[key].list.array[0]);\n\t}\n}\n\nstatic inline bool fill_keycodes(struct obs_core_hotkeys *hotkeys)\n{\n\tobs_hotkeys_platform_t *context = hotkeys->platform_context;\n\txcb_connection_t *connection = XGetXCBConnection(context->display);\n\tconst struct xcb_setup_t *setup = xcb_get_setup(connection);\n\txcb_get_keyboard_mapping_cookie_t cookie;\n\txcb_get_keyboard_mapping_reply_t *reply;\n\txcb_generic_error_t *error = NULL;\n\tint code;\n\n\tint mincode = setup->min_keycode;\n\tint maxcode = setup->max_keycode;\n\n\tcontext->min_keycode = setup->min_keycode;\n\n\tcookie = xcb_get_keyboard_mapping(connection, mincode, maxcode - mincode + 1);\n\n\treply = xcb_get_keyboard_mapping_reply(connection, cookie, &error);\n\n\tif (error || !reply) {\n\t\tblog(LOG_WARNING, \"xcb_get_keyboard_mapping_reply failed\");\n\t\tgoto error1;\n\t}\n\n\tconst xcb_keysym_t *keysyms = xcb_get_keyboard_mapping_keysyms(reply);\n\tint syms_per_code = (int)reply->keysyms_per_keycode;\n\n\tcontext->num_keysyms = (maxcode - mincode + 1) * syms_per_code;\n\tcontext->syms_per_code = syms_per_code;\n\tcontext->keysyms = bmemdup(keysyms, sizeof(xcb_keysym_t) * context->num_keysyms);\n\n\tfor (code = mincode; code <= maxcode; code++) {\n\t\tconst xcb_keysym_t *sym;\n\t\tobs_key_t key;\n\n\t\tsym = &keysyms[(code - mincode) * syms_per_code];\n\n\t\tfor (int i = 0; i < syms_per_code; i++) {\n\t\t\tif (!sym[i])\n\t\t\t\tbreak;\n\n\t\t\tif (sym[i] == XK_Super_L) {\n\t\t\t\tcontext->super_l_code = code;\n\t\t\t\tbreak;\n\t\t\t} else if (sym[i] == XK_Super_R) {\n\t\t\t\tcontext->super_r_code = code;\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tkey = key_from_base_keysym(context, sym[i]);\n\n\t\t\t\tif (key != OBS_KEY_NONE) {\n\t\t\t\t\tadd_key(context, key, code);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\nerror1:\n\tfree(reply);\n\tfree(error);\n\n\treturn error != NULL || reply == NULL;\n}\n\nstatic xcb_screen_t *default_screen(obs_hotkeys_platform_t *context, xcb_connection_t *connection)\n{\n\tint def_screen_idx = XDefaultScreen(context->display);\n\txcb_screen_iterator_t iter;\n\n\titer = xcb_setup_roots_iterator(xcb_get_setup(connection));\n\twhile (iter.rem) {\n\t\tif (def_screen_idx-- == 0)\n\t\t\treturn iter.data;\n\n\t\txcb_screen_next(&iter);\n\t}\n\n\treturn NULL;\n}\n\nstatic inline xcb_window_t root_window(obs_hotkeys_platform_t *context, xcb_connection_t *connection)\n{\n\txcb_screen_t *screen = default_screen(context, connection);\n\tif (screen)\n\t\treturn screen->root;\n\treturn 0;\n}\n\n#if defined(XCB_XINPUT_FOUND)\nstatic inline void registerMouseEvents(struct obs_core_hotkeys *hotkeys)\n{\n\tobs_hotkeys_platform_t *context = hotkeys->platform_context;\n\txcb_connection_t *connection = XGetXCBConnection(context->display);\n\txcb_window_t window = root_window(context, connection);\n\n\tstruct {\n\t\txcb_input_event_mask_t head;\n\t\txcb_input_xi_event_mask_t mask;\n\t} mask;\n\tmask.head.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;\n\tmask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t);\n\tmask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE;\n\n\txcb_input_xi_select_events(connection, window, 1, &mask.head);\n\txcb_flush(connection);\n}\n#endif\n\nstatic bool obs_nix_x11_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)\n{\n\t// Open a new X11 connection here, this avoids Qt masking events we care about.\n\tDisplay *display = XOpenDisplay(NULL);\n\tif (!display)\n\t\treturn false;\n\n\thotkeys->platform_context = bzalloc(sizeof(obs_hotkeys_platform_t));\n\thotkeys->platform_context->display = display;\n\n#if defined(XCB_XINPUT_FOUND)\n\tregisterMouseEvents(hotkeys);\n#endif\n\tfill_base_keysyms(hotkeys);\n\tfill_keycodes(hotkeys);\n\treturn true;\n}\n\nstatic void obs_nix_x11_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)\n{\n\tobs_hotkeys_platform_t *context = hotkeys->platform_context;\n\tif (!context)\n\t\treturn;\n\n\tfor (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++)\n\t\tda_free(context->keycodes[i].list);\n\n\tbfree(context->keysyms);\n\tXCloseDisplay(context->display);\n\tbfree(context);\n\n\thotkeys->platform_context = NULL;\n}\n\nstatic bool mouse_button_pressed(xcb_connection_t *connection, obs_hotkeys_platform_t *context, obs_key_t key)\n{\n\tbool ret = false;\n\n#if defined(XCB_XINPUT_FOUND)\n\tmemset(context->pressed, 0, XINPUT_MOUSE_LEN);\n\tmemset(context->update, 0, XINPUT_MOUSE_LEN);\n\n\txcb_generic_event_t *ev;\n\twhile ((ev = xcb_poll_for_event(connection))) {\n\t\tif ((ev->response_type & ~80) == XCB_GE_GENERIC) {\n\t\t\tswitch (((xcb_ge_event_t *)ev)->event_type) {\n\t\t\tcase XCB_INPUT_RAW_BUTTON_PRESS: {\n\t\t\t\txcb_input_raw_button_press_event_t *mot;\n\t\t\t\tmot = (xcb_input_raw_button_press_event_t *)ev;\n\t\t\t\tif (mot->detail < XINPUT_MOUSE_LEN) {\n\t\t\t\t\tcontext->pressed[mot->detail - 1] = true;\n\t\t\t\t\tcontext->update[mot->detail - 1] = true;\n\t\t\t\t} else {\n\t\t\t\t\tblog(LOG_WARNING, \"Unsupported button\");\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase XCB_INPUT_RAW_BUTTON_RELEASE: {\n\t\t\t\txcb_input_raw_button_release_event_t *mot;\n\t\t\t\tmot = (xcb_input_raw_button_release_event_t *)ev;\n\t\t\t\tif (mot->detail < XINPUT_MOUSE_LEN)\n\t\t\t\t\tcontext->update[mot->detail - 1] = true;\n\t\t\t\telse\n\t\t\t\t\tblog(LOG_WARNING, \"Unsupported button\");\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tfree(ev);\n\t}\n\n\t// Mouse 2 for OBS is Right Click and Mouse 3 is Wheel Click.\n\t// Mouse Wheel axis clicks (xinput mot->detail 4 5 6 7) are ignored.\n\tswitch (key) {\n\tcase OBS_KEY_MOUSE1:\n\t\tret = context->pressed[0] || context->button_pressed[0];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE2:\n\t\tret = context->pressed[2] || context->button_pressed[2];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE3:\n\t\tret = context->pressed[1] || context->button_pressed[1];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE4:\n\t\tret = context->pressed[7] || context->button_pressed[7];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE5:\n\t\tret = context->pressed[8] || context->button_pressed[8];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE6:\n\t\tret = context->pressed[9] || context->button_pressed[9];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE7:\n\t\tret = context->pressed[10] || context->button_pressed[10];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE8:\n\t\tret = context->pressed[11] || context->button_pressed[11];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE9:\n\t\tret = context->pressed[12] || context->button_pressed[12];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE10:\n\t\tret = context->pressed[13] || context->button_pressed[13];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE11:\n\t\tret = context->pressed[14] || context->button_pressed[14];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE12:\n\t\tret = context->pressed[15] || context->button_pressed[15];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE13:\n\t\tret = context->pressed[16] || context->button_pressed[16];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE14:\n\t\tret = context->pressed[17] || context->button_pressed[17];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE15:\n\t\tret = context->pressed[18] || context->button_pressed[18];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE16:\n\t\tret = context->pressed[19] || context->button_pressed[19];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE17:\n\t\tret = context->pressed[20] || context->button_pressed[20];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE18:\n\t\tret = context->pressed[21] || context->button_pressed[21];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE19:\n\t\tret = context->pressed[22] || context->button_pressed[22];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE20:\n\t\tret = context->pressed[23] || context->button_pressed[23];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE21:\n\t\tret = context->pressed[24] || context->button_pressed[24];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE22:\n\t\tret = context->pressed[25] || context->button_pressed[25];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE23:\n\t\tret = context->pressed[26] || context->button_pressed[26];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE24:\n\t\tret = context->pressed[27] || context->button_pressed[27];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE25:\n\t\tret = context->pressed[28] || context->button_pressed[28];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE26:\n\t\tret = context->pressed[29] || context->button_pressed[29];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE27:\n\t\tret = context->pressed[30] || context->button_pressed[30];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE28:\n\t\tret = context->pressed[31] || context->button_pressed[31];\n\t\tbreak;\n\tcase OBS_KEY_MOUSE29:\n\t\tret = context->pressed[32] || context->button_pressed[32];\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tfor (int i = 0; i != XINPUT_MOUSE_LEN; i++)\n\t\tif (context->update[i])\n\t\t\tcontext->button_pressed[i] = context->pressed[i];\n#else\n\txcb_generic_error_t *error = NULL;\n\txcb_query_pointer_cookie_t qpc;\n\txcb_query_pointer_reply_t *reply;\n\n\tqpc = xcb_query_pointer(connection, root_window(context, connection));\n\treply = xcb_query_pointer_reply(connection, qpc, &error);\n\n\tif (error) {\n\t\tblog(LOG_WARNING, \"xcb_query_pointer_reply failed\");\n\t} else {\n\t\tuint16_t buttons = reply->mask;\n\n\t\tswitch (key) {\n\t\tcase OBS_KEY_MOUSE1:\n\t\t\tret = buttons & XCB_BUTTON_MASK_1;\n\t\t\tbreak;\n\t\tcase OBS_KEY_MOUSE2:\n\t\t\tret = buttons & XCB_BUTTON_MASK_3;\n\t\t\tbreak;\n\t\tcase OBS_KEY_MOUSE3:\n\t\t\tret = buttons & XCB_BUTTON_MASK_2;\n\t\t\tbreak;\n\t\tdefault:;\n\t\t}\n\t}\n\n\tfree(reply);\n\tfree(error);\n#endif\n\treturn ret;\n}\n\nstatic inline bool keycode_pressed(xcb_query_keymap_reply_t *reply, xcb_keycode_t code)\n{\n\treturn (reply->keys[code / 8] & (1 << (code % 8))) != 0;\n}\n\nstatic bool key_pressed(xcb_connection_t *connection, obs_hotkeys_platform_t *context, obs_key_t key)\n{\n\tstruct keycode_list *codes = &context->keycodes[key];\n\txcb_generic_error_t *error = NULL;\n\txcb_query_keymap_reply_t *reply;\n\tbool pressed = false;\n\n\treply = xcb_query_keymap_reply(connection, xcb_query_keymap(connection), &error);\n\tif (error) {\n\t\tblog(LOG_WARNING, \"xcb_query_keymap failed\");\n\n\t} else if (key == OBS_KEY_META) {\n\t\tpressed = keycode_pressed(reply, context->super_l_code) ||\n\t\t\t  keycode_pressed(reply, context->super_r_code);\n\n\t} else {\n\t\tfor (size_t i = 0; i < codes->list.num; i++) {\n\t\t\tif (keycode_pressed(reply, codes->list.array[i])) {\n\t\t\t\tpressed = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tfree(reply);\n\tfree(error);\n\treturn pressed;\n}\n\nstatic bool obs_nix_x11_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context, obs_key_t key)\n{\n\txcb_connection_t *conn = XGetXCBConnection(context->display);\n\n\tif (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {\n\t\treturn mouse_button_pressed(conn, context, key);\n\t} else {\n\t\treturn key_pressed(conn, context, key);\n\t}\n}\n\nstatic bool get_key_translation(struct dstr *dstr, xcb_keycode_t keycode)\n{\n\txcb_connection_t *connection;\n\tchar name[128];\n\n\tconnection = XGetXCBConnection(obs->hotkeys.platform_context->display);\n\n\tXKeyEvent event = {0};\n\tevent.type = KeyPress;\n\tevent.display = obs->hotkeys.platform_context->display;\n\tevent.keycode = keycode;\n\tevent.root = root_window(obs->hotkeys.platform_context, connection);\n\tevent.window = event.root;\n\n\tif (keycode) {\n\t\tint len = XLookupString(&event, name, 128, NULL, NULL);\n\t\tif (len) {\n\t\t\tdstr_ncopy(dstr, name, len);\n\t\t\tdstr_to_upper(dstr);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic void obs_nix_x11_key_to_str(obs_key_t key, struct dstr *dstr)\n{\n\tif (key >= OBS_KEY_MOUSE1 && key <= OBS_KEY_MOUSE29) {\n\t\tif (obs->hotkeys.translations[key]) {\n\t\t\tdstr_copy(dstr, obs->hotkeys.translations[key]);\n\t\t} else {\n\t\t\tdstr_printf(dstr, \"Mouse %d\", (int)(key - OBS_KEY_MOUSE1 + 1));\n\t\t}\n\t\treturn;\n\t}\n\n\tif (key >= OBS_KEY_NUM0 && key <= OBS_KEY_NUM9) {\n\t\tif (obs->hotkeys.translations[key]) {\n\t\t\tdstr_copy(dstr, obs->hotkeys.translations[key]);\n\t\t} else {\n\t\t\tdstr_printf(dstr, \"Numpad %d\", (int)(key - OBS_KEY_NUM0));\n\t\t}\n\t\treturn;\n\t}\n\n#define translate_key(key, def) dstr_copy(dstr, obs_get_hotkey_translation(key, def))\n\n\tswitch (key) {\n\tcase OBS_KEY_INSERT:\n\t\treturn translate_key(key, \"Insert\");\n\tcase OBS_KEY_DELETE:\n\t\treturn translate_key(key, \"Delete\");\n\tcase OBS_KEY_HOME:\n\t\treturn translate_key(key, \"Home\");\n\tcase OBS_KEY_END:\n\t\treturn translate_key(key, \"End\");\n\tcase OBS_KEY_PAGEUP:\n\t\treturn translate_key(key, \"Page Up\");\n\tcase OBS_KEY_PAGEDOWN:\n\t\treturn translate_key(key, \"Page Down\");\n\tcase OBS_KEY_NUMLOCK:\n\t\treturn translate_key(key, \"Num Lock\");\n\tcase OBS_KEY_SCROLLLOCK:\n\t\treturn translate_key(key, \"Scroll Lock\");\n\tcase OBS_KEY_CAPSLOCK:\n\t\treturn translate_key(key, \"Caps Lock\");\n\tcase OBS_KEY_BACKSPACE:\n\t\treturn translate_key(key, \"Backspace\");\n\tcase OBS_KEY_TAB:\n\t\treturn translate_key(key, \"Tab\");\n\tcase OBS_KEY_PRINT:\n\t\treturn translate_key(key, \"Print\");\n\tcase OBS_KEY_PAUSE:\n\t\treturn translate_key(key, \"Pause\");\n\tcase OBS_KEY_LEFT:\n\t\treturn translate_key(key, \"Left\");\n\tcase OBS_KEY_RIGHT:\n\t\treturn translate_key(key, \"Right\");\n\tcase OBS_KEY_UP:\n\t\treturn translate_key(key, \"Up\");\n\tcase OBS_KEY_DOWN:\n\t\treturn translate_key(key, \"Down\");\n\tcase OBS_KEY_SHIFT:\n\t\treturn translate_key(key, \"Shift\");\n\tcase OBS_KEY_ALT:\n\t\treturn translate_key(key, \"Alt\");\n\tcase OBS_KEY_CONTROL:\n\t\treturn translate_key(key, \"Control\");\n\tcase OBS_KEY_META:\n\t\treturn translate_key(key, \"Super\");\n\tcase OBS_KEY_MENU:\n\t\treturn translate_key(key, \"Menu\");\n\tcase OBS_KEY_NUMASTERISK:\n\t\treturn translate_key(key, \"Numpad *\");\n\tcase OBS_KEY_NUMPLUS:\n\t\treturn translate_key(key, \"Numpad +\");\n\tcase OBS_KEY_NUMMINUS:\n\t\treturn translate_key(key, \"Numpad -\");\n\tcase OBS_KEY_NUMCOMMA:\n\t\treturn translate_key(key, \"Numpad ,\");\n\tcase OBS_KEY_NUMPERIOD:\n\t\treturn translate_key(key, \"Numpad .\");\n\tcase OBS_KEY_NUMSLASH:\n\t\treturn translate_key(key, \"Numpad /\");\n\tcase OBS_KEY_SPACE:\n\t\treturn translate_key(key, \"Space\");\n\tcase OBS_KEY_ESCAPE:\n\t\treturn translate_key(key, \"Escape\");\n\tdefault:;\n\t}\n\n\tif (key >= OBS_KEY_F1 && key <= OBS_KEY_F35) {\n\t\tdstr_printf(dstr, \"F%d\", (int)(key - OBS_KEY_F1 + 1));\n\t\treturn;\n\t}\n\n\tobs_hotkeys_platform_t *context = obs->hotkeys.platform_context;\n\tstruct keycode_list *keycodes = &context->keycodes[key];\n\n\tfor (size_t i = 0; i < keycodes->list.num; i++) {\n\t\tif (get_key_translation(dstr, keycodes->list.array[i])) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (key != OBS_KEY_NONE && dstr_is_empty(dstr)) {\n\t\tdstr_copy(dstr, obs_key_to_name(key));\n\t}\n}\n\nstatic obs_key_t key_from_keycode(obs_hotkeys_platform_t *context, xcb_keycode_t code)\n{\n\tfor (size_t i = 0; i < OBS_KEY_LAST_VALUE; i++) {\n\t\tstruct keycode_list *codes = &context->keycodes[i];\n\n\t\tfor (size_t j = 0; j < codes->list.num; j++) {\n\t\t\tif (codes->list.array[j] == code) {\n\t\t\t\treturn (obs_key_t)i;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn OBS_KEY_NONE;\n}\n\nstatic obs_key_t obs_nix_x11_key_from_virtual_key(int sym)\n{\n\tobs_hotkeys_platform_t *context = obs->hotkeys.platform_context;\n\tconst xcb_keysym_t *keysyms = context->keysyms;\n\tint syms_per_code = context->syms_per_code;\n\tint num_keysyms = context->num_keysyms;\n\n\tif (sym == 0)\n\t\treturn OBS_KEY_NONE;\n\n\tfor (int i = 0; i < num_keysyms; i++) {\n\t\tif (keysyms[i] == (xcb_keysym_t)sym) {\n\t\t\txcb_keycode_t code = (xcb_keycode_t)(i / syms_per_code);\n\t\t\tcode += context->min_keycode;\n\t\t\tobs_key_t key = key_from_keycode(context, code);\n\n\t\t\treturn key;\n\t\t}\n\t}\n\n\treturn OBS_KEY_NONE;\n}\n\nstatic int obs_nix_x11_key_to_virtual_key(obs_key_t key)\n{\n\tif (key == OBS_KEY_META)\n\t\treturn XK_Super_L;\n\n\treturn (int)obs->hotkeys.platform_context->base_keysyms[(int)key];\n}\n\nstatic const struct obs_nix_hotkeys_vtable x11_hotkeys_vtable = {\n\t.init = obs_nix_x11_hotkeys_platform_init,\n\t.free = obs_nix_x11_hotkeys_platform_free,\n\t.is_pressed = obs_nix_x11_hotkeys_platform_is_pressed,\n\t.key_to_str = obs_nix_x11_key_to_str,\n\t.key_from_virtual_key = obs_nix_x11_key_from_virtual_key,\n\t.key_to_virtual_key = obs_nix_x11_key_to_virtual_key,\n};\n\nconst struct obs_nix_hotkeys_vtable *obs_nix_x11_get_hotkeys_vtable(void)\n{\n\treturn &x11_hotkeys_vtable;\n}\n"
  },
  {
    "path": "libobs/obs-nix-x11.h",
    "content": "/******************************************************************************\n    Copyright (C) 2019 by Jason Francis <cycl0ps@tuta.io>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"obs-nix.h\"\n\nvoid obs_nix_x11_log_info(void);\n\nconst struct obs_nix_hotkeys_vtable *obs_nix_x11_get_hotkeys_vtable(void);\n"
  },
  {
    "path": "libobs/obs-nix.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n    Copyright (C) 2014 by Zachary Lund <admin@computerquip.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-internal.h\"\n#include \"obs-nix.h\"\n#include \"obs-nix-platform.h\"\n#include \"obs-nix-x11.h\"\n\n#include \"util/config-file.h\"\n\n#ifdef ENABLE_WAYLAND\n#include \"obs-nix-wayland.h\"\n#endif\n\n#if defined(__FreeBSD__)\n#define _GNU_SOURCE\n#endif\n#include <stdlib.h>\n#include <stdio.h>\n#include <unistd.h>\n#if defined(__FreeBSD__) || defined(__OpenBSD__)\n#include <sys/sysctl.h>\n#endif\n#if !defined(__OpenBSD__)\n#include <sys/sysinfo.h>\n#endif\n#include <sys/utsname.h>\n#include <inttypes.h>\n\nconst char *get_module_extension(void)\n{\n\treturn \".so\";\n}\n\n#define FLATPAK_PLUGIN_PATH \"/app/plugins\"\n\nstatic const char *module_bin[] = {\n\t\"../../obs-plugins/64bit\",\n\tOBS_INSTALL_PREFIX \"/\" OBS_PLUGIN_DESTINATION,\n\tFLATPAK_PLUGIN_PATH \"/\" OBS_PLUGIN_DESTINATION,\n};\n\nstatic const char *module_data[] = {\n\tOBS_DATA_PATH \"/obs-plugins/%module%\",\n\tOBS_INSTALL_DATA_PATH \"/obs-plugins/%module%\",\n\tFLATPAK_PLUGIN_PATH \"/share/obs/obs-plugins/%module%\",\n};\n\nstatic const int module_patterns_size = sizeof(module_bin) / sizeof(module_bin[0]);\n\nstatic const struct obs_nix_hotkeys_vtable *hotkeys_vtable = NULL;\n\nvoid add_default_module_paths(void)\n{\n\tchar *module_bin_path = os_get_executable_path_ptr(\"../\" OBS_PLUGIN_PATH);\n\tchar *module_data_path = os_get_executable_path_ptr(\"../\" OBS_DATA_PATH \"/obs-plugins/%module%\");\n\n\tif (module_bin_path && module_data_path) {\n\t\tchar *abs_module_bin_path = os_get_abs_path_ptr(module_bin_path);\n\t\tchar *abs_module_install_path = os_get_abs_path_ptr(OBS_INSTALL_PREFIX \"/\" OBS_PLUGIN_DESTINATION);\n\n\t\tif (abs_module_bin_path &&\n\t\t    (!abs_module_install_path || strcmp(abs_module_bin_path, abs_module_install_path) != 0)) {\n\t\t\tobs_add_module_path(module_bin_path, module_data_path);\n\t\t}\n\t\tbfree(abs_module_install_path);\n\t\tbfree(abs_module_bin_path);\n\t}\n\n\tbfree(module_bin_path);\n\tbfree(module_data_path);\n\n\tfor (int i = 0; i < module_patterns_size; i++) {\n\t\tobs_add_module_path(module_bin[i], module_data[i]);\n\t}\n}\n\n/*\n *   /usr/local/share/libobs\n *   /usr/share/libobs\n */\nchar *find_libobs_data_file(const char *file)\n{\n\tstruct dstr output;\n\tdstr_init(&output);\n\n\tif (check_path(file, OBS_DATA_PATH \"/libobs/\", &output))\n\t\treturn output.array;\n\n\tchar *relative_data_path = os_get_executable_path_ptr(\"../\" OBS_DATA_PATH \"/libobs/\");\n\tif (relative_data_path) {\n\t\tbool found = check_path(file, relative_data_path, &output);\n\n\t\tbfree(relative_data_path);\n\n\t\tif (found) {\n\t\t\treturn output.array;\n\t\t}\n\t}\n\n\tif (OBS_INSTALL_PREFIX[0] != 0) {\n\t\tif (check_path(file, OBS_INSTALL_DATA_PATH \"/libobs/\", &output))\n\t\t\treturn output.array;\n\t}\n\n\tdstr_free(&output);\n\treturn NULL;\n}\n\nstatic void log_processor_cores(void)\n{\n\tblog(LOG_INFO, \"Physical Cores: %d, Logical Cores: %d\", os_get_physical_cores(), os_get_logical_cores());\n}\n\n#if defined(__linux__)\nstatic void log_processor_info(void)\n{\n\tint physical_id = -1;\n\tint last_physical_id = -1;\n\tchar *line = NULL;\n\tsize_t linecap = 0;\n\n\tFILE *fp;\n\tstruct dstr proc_name;\n\tstruct dstr proc_speed;\n\n\tfp = fopen(\"/proc/cpuinfo\", \"r\");\n\tif (!fp)\n\t\treturn;\n\n\tdstr_init(&proc_name);\n\tdstr_init(&proc_speed);\n\n\twhile (getline(&line, &linecap, fp) != -1) {\n\t\tif (!strncmp(line, \"model name\", 10)) {\n\t\t\tchar *start = strchr(line, ':');\n\t\t\tif (!start || *(++start) == '\\0')\n\t\t\t\tcontinue;\n\n\t\t\tdstr_copy(&proc_name, start);\n\t\t\tdstr_resize(&proc_name, proc_name.len - 1);\n\t\t\tdstr_depad(&proc_name);\n\t\t}\n\n\t\tif (!strncmp(line, \"physical id\", 11)) {\n\t\t\tchar *start = strchr(line, ':');\n\t\t\tif (!start || *(++start) == '\\0')\n\t\t\t\tcontinue;\n\n\t\t\tphysical_id = atoi(start);\n\t\t}\n\n\t\tif (!strncmp(line, \"cpu MHz\", 7)) {\n\t\t\tchar *start = strchr(line, ':');\n\t\t\tif (!start || *(++start) == '\\0')\n\t\t\t\tcontinue;\n\n\t\t\tdstr_copy(&proc_speed, start);\n\t\t\tdstr_resize(&proc_speed, proc_speed.len - 1);\n\t\t\tdstr_depad(&proc_speed);\n\t\t}\n\n\t\tif (*line == '\\n' && physical_id != last_physical_id) {\n\t\t\tlast_physical_id = physical_id;\n\t\t\tblog(LOG_INFO, \"CPU Name: %s\", proc_name.array);\n\t\t\tblog(LOG_INFO, \"CPU Speed: %sMHz\", proc_speed.array);\n\t\t}\n\t}\n\n\tfclose(fp);\n\tdstr_free(&proc_name);\n\tdstr_free(&proc_speed);\n\tfree(line);\n}\n#elif defined(__FreeBSD__) || defined(__OpenBSD__)\nstatic void log_processor_speed(void)\n{\n#ifndef __OpenBSD__\n\tchar *line = NULL;\n\tsize_t linecap = 0;\n\tFILE *fp;\n\tstruct dstr proc_speed;\n\n\tfp = fopen(\"/var/run/dmesg.boot\", \"r\");\n\tif (!fp) {\n\t\tblog(LOG_INFO, \"CPU: Missing /var/run/dmesg.boot !\");\n\t\treturn;\n\t}\n\n\tdstr_init(&proc_speed);\n\n\twhile (getline(&line, &linecap, fp) != -1) {\n\t\tif (!strncmp(line, \"CPU: \", 5)) {\n\t\t\tchar *start = strrchr(line, '(');\n\t\t\tif (!start || *(++start) == '\\0')\n\t\t\t\tcontinue;\n\n\t\t\tsize_t len = strcspn(start, \"-\");\n\t\t\tdstr_ncopy(&proc_speed, start, len);\n\t\t}\n\t}\n\n\tblog(LOG_INFO, \"CPU Speed: %sMHz\", proc_speed.array);\n\n\tfclose(fp);\n\tdstr_free(&proc_speed);\n\tfree(line);\n#endif\n}\n\nstatic void log_processor_name(void)\n{\n\tint mib[2];\n\tsize_t len;\n\tchar *proc;\n\n\tmib[0] = CTL_HW;\n\tmib[1] = HW_MODEL;\n\n\tsysctl(mib, 2, NULL, &len, NULL, 0);\n\tproc = bmalloc(len);\n\tif (!proc)\n\t\treturn;\n\n\tsysctl(mib, 2, proc, &len, NULL, 0);\n\tblog(LOG_INFO, \"CPU Name: %s\", proc);\n\n\tbfree(proc);\n}\n\nstatic void log_processor_info(void)\n{\n\tlog_processor_name();\n\tlog_processor_speed();\n}\n#endif\n\nstatic void log_memory_info(void)\n{\n#if defined(__OpenBSD__)\n\tint mib[2];\n\tsize_t len;\n\tint64_t mem;\n\n\tmib[0] = CTL_HW;\n\tmib[1] = HW_PHYSMEM64;\n\tlen = sizeof(mem);\n\n\tif (sysctl(mib, 2, &mem, &len, NULL, 0) >= 0)\n\t\tblog(LOG_INFO, \"Physical Memory: %\" PRIi64 \"MB Total\", mem / 1024 / 1024);\n#else\n\tstruct sysinfo info;\n\tif (sysinfo(&info) < 0)\n\t\treturn;\n\n\tblog(LOG_INFO, \"Physical Memory: %\" PRIu64 \"MB Total, %\" PRIu64 \"MB Free\",\n\t     (uint64_t)info.totalram * info.mem_unit / 1024 / 1024,\n\t     ((uint64_t)info.freeram + (uint64_t)info.bufferram) * info.mem_unit / 1024 / 1024);\n#endif\n}\n\nstatic void log_kernel_version(void)\n{\n\tstruct utsname info;\n\tif (uname(&info) < 0)\n\t\treturn;\n\n\tblog(LOG_INFO, \"Kernel Version: %s %s\", info.sysname, info.release);\n}\n\n#if defined(__linux__) || defined(__FreeBSD__)\nstatic void log_distribution_info(void)\n{\n\tFILE *fp;\n\tchar *line = NULL;\n\tsize_t linecap = 0;\n\tstruct dstr distro;\n\tstruct dstr version;\n\n\tfp = fopen(\"/etc/os-release\", \"r\");\n\tif (!fp) {\n\t\tblog(LOG_INFO, \"Distribution: Missing /etc/os-release !\");\n\t\treturn;\n\t}\n\n\tdstr_init_copy(&distro, \"Unknown\");\n\tdstr_init_copy(&version, \"Unknown\");\n\n\twhile (getline(&line, &linecap, fp) != -1) {\n\t\tif (!strncmp(line, \"NAME\", 4)) {\n\t\t\tchar *start = strchr(line, '=');\n\t\t\tif (!start || *(++start) == '\\0')\n\t\t\t\tcontinue;\n\t\t\tdstr_copy(&distro, start);\n\t\t\tdstr_resize(&distro, distro.len - 1);\n\t\t}\n\n\t\tif (!strncmp(line, \"VERSION_ID\", 10)) {\n\t\t\tchar *start = strchr(line, '=');\n\t\t\tif (!start || *(++start) == '\\0')\n\t\t\t\tcontinue;\n\t\t\tdstr_copy(&version, start);\n\t\t\tdstr_resize(&version, version.len - 1);\n\t\t}\n\t}\n\n\tblog(LOG_INFO, \"Distribution: %s %s\", distro.array, version.array);\n\n\tfclose(fp);\n\tdstr_free(&version);\n\tdstr_free(&distro);\n\tfree(line);\n}\n\nstatic void log_flatpak_extensions(const char *extensions)\n{\n\tif (!extensions)\n\t\treturn;\n\n\tchar **exts_list = strlist_split(extensions, ';', false);\n\tfor (char **ext = exts_list; *ext != NULL; ext++) {\n\t\t// Log the extension name without its commit hash\n\t\tchar **name = strlist_split(*ext, '=', false);\n\t\tblog(LOG_INFO, \" - %s\", *name);\n\t\tstrlist_free(name);\n\t}\n\tstrlist_free(exts_list);\n}\n\nstatic void log_flatpak_info(void)\n{\n\tconfig_t *fp_info = NULL;\n\n\tif (config_open(&fp_info, \"/.flatpak-info\", CONFIG_OPEN_EXISTING) != CONFIG_SUCCESS) {\n\t\tblog(LOG_ERROR, \"Unable to open .flatpak-info file\");\n\t\treturn;\n\t}\n\n\tconst char *branch = config_get_string(fp_info, \"Instance\", \"branch\");\n\tconst char *arch = config_get_string(fp_info, \"Instance\", \"arch\");\n\tconst char *commit = config_get_string(fp_info, \"Instance\", \"app-commit\");\n\n\tconst char *runtime = config_get_string(fp_info, \"Application\", \"runtime\");\n\n\tconst char *app_exts = config_get_string(fp_info, \"Instance\", \"app-extensions\");\n\tconst char *runtime_exts = config_get_string(fp_info, \"Instance\", \"runtime-extensions\");\n\n\tconst char *fp_version = config_get_string(fp_info, \"Instance\", \"flatpak-version\");\n\n\tblog(LOG_INFO, \"Flatpak Branch: %s\", branch ? branch : \"none\");\n\tblog(LOG_INFO, \"Flatpak Arch: %s\", arch ? arch : \"unknown\");\n\tblog(LOG_INFO, \"Flatpak Commit: %s\", commit ? commit : \"unknown\");\n\n\tblog(LOG_INFO, \"Flatpak Runtime: %s\", runtime ? runtime : \"none\");\n\n\tif (app_exts) {\n\t\tblog(LOG_INFO, \"App Extensions:\");\n\t\tlog_flatpak_extensions(app_exts);\n\t}\n\n\tif (runtime_exts) {\n\t\tblog(LOG_INFO, \"Runtime Extensions:\");\n\t\tlog_flatpak_extensions(runtime_exts);\n\t}\n\n\tblog(LOG_INFO, \"Flatpak Framework Version: %s\", fp_version ? fp_version : \"unknown\");\n\n\tconfig_close(fp_info);\n}\n\nstatic void log_desktop_session_info(void)\n{\n\tchar *current_desktop = getenv(\"XDG_CURRENT_DESKTOP\");\n\tchar *session_desktop = getenv(\"XDG_SESSION_DESKTOP\");\n\tchar *session_type = getenv(\"XDG_SESSION_TYPE\");\n\n\tif (current_desktop && session_desktop)\n\t\tblog(LOG_INFO, \"Desktop Environment: %s (%s)\", current_desktop, session_desktop);\n\telse if (current_desktop || session_desktop)\n\t\tblog(LOG_INFO, \"Desktop Environment: %s\", current_desktop ? current_desktop : session_desktop);\n\n\tif (session_type)\n\t\tblog(LOG_INFO, \"Session Type: %s\", session_type);\n}\n#endif\n\nvoid log_system_info(void)\n{\n#if defined(__linux__) || defined(__FreeBSD__)\n\tlog_processor_info();\n#endif\n\tlog_processor_cores();\n\tlog_memory_info();\n\tlog_kernel_version();\n#if defined(__linux__) || defined(__FreeBSD__)\n\tif (access(\"/.flatpak-info\", F_OK) == 0)\n\t\tlog_flatpak_info();\n\telse\n\t\tlog_distribution_info();\n\n\tlog_desktop_session_info();\n#endif\n\tif (obs_get_nix_platform() == OBS_NIX_PLATFORM_X11_EGL)\n\t\tobs_nix_x11_log_info();\n}\n\nbool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys)\n{\n\tswitch (obs_get_nix_platform()) {\n\tcase OBS_NIX_PLATFORM_X11_EGL:\n\t\thotkeys_vtable = obs_nix_x11_get_hotkeys_vtable();\n\t\tbreak;\n#ifdef ENABLE_WAYLAND\n\tcase OBS_NIX_PLATFORM_WAYLAND:\n\t\thotkeys_vtable = obs_nix_wayland_get_hotkeys_vtable();\n\t\tbreak;\n#endif\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn hotkeys_vtable->init(hotkeys);\n}\n\nvoid obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys)\n{\n\thotkeys_vtable->free(hotkeys);\n\thotkeys_vtable = NULL;\n}\n\nbool obs_hotkeys_platform_is_pressed(obs_hotkeys_platform_t *context, obs_key_t key)\n{\n\treturn hotkeys_vtable->is_pressed(context, key);\n}\n\nvoid obs_key_to_str(obs_key_t key, struct dstr *dstr)\n{\n\treturn hotkeys_vtable->key_to_str(key, dstr);\n}\n\nobs_key_t obs_key_from_virtual_key(int sym)\n{\n\treturn hotkeys_vtable->key_from_virtual_key(sym);\n}\n\nint obs_key_to_virtual_key(obs_key_t key)\n{\n\treturn hotkeys_vtable->key_to_virtual_key(key);\n}\n\nstatic inline void add_combo_key(obs_key_t key, struct dstr *str)\n{\n\tstruct dstr key_str = {0};\n\n\tobs_key_to_str(key, &key_str);\n\n\tif (!dstr_is_empty(&key_str)) {\n\t\tif (!dstr_is_empty(str)) {\n\t\t\tdstr_cat(str, \" + \");\n\t\t}\n\t\tdstr_cat_dstr(str, &key_str);\n\t}\n\n\tdstr_free(&key_str);\n}\n\nvoid obs_key_combination_to_str(obs_key_combination_t combination, struct dstr *str)\n{\n\tif ((combination.modifiers & INTERACT_CONTROL_KEY) != 0) {\n\t\tadd_combo_key(OBS_KEY_CONTROL, str);\n\t}\n\tif ((combination.modifiers & INTERACT_COMMAND_KEY) != 0) {\n\t\tadd_combo_key(OBS_KEY_META, str);\n\t}\n\tif ((combination.modifiers & INTERACT_ALT_KEY) != 0) {\n\t\tadd_combo_key(OBS_KEY_ALT, str);\n\t}\n\tif ((combination.modifiers & INTERACT_SHIFT_KEY) != 0) {\n\t\tadd_combo_key(OBS_KEY_SHIFT, str);\n\t}\n\tif (combination.key != OBS_KEY_NONE) {\n\t\tadd_combo_key(combination.key, str);\n\t}\n}\n"
  },
  {
    "path": "libobs/obs-nix.h",
    "content": "/******************************************************************************\n    Copyright (C) 2020 by Georges Basile Stavracas Neto <georges.stavracas@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"obs-internal.h\"\n\nstruct obs_nix_hotkeys_vtable {\n\tbool (*init)(struct obs_core_hotkeys *hotkeys);\n\n\tvoid (*free)(struct obs_core_hotkeys *hotkeys);\n\n\tbool (*is_pressed)(obs_hotkeys_platform_t *context, obs_key_t key);\n\n\tvoid (*key_to_str)(obs_key_t key, struct dstr *dstr);\n\n\tobs_key_t (*key_from_virtual_key)(int sym);\n\n\tint (*key_to_virtual_key)(obs_key_t key);\n};\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-output-delay.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <inttypes.h>\n#include \"obs-internal.h\"\n\nstatic inline bool delay_active(const struct obs_output *output)\n{\n\treturn os_atomic_load_bool(&output->delay_active);\n}\n\nstatic inline bool delay_capturing(const struct obs_output *output)\n{\n\treturn os_atomic_load_bool(&output->delay_capturing);\n}\n\nstatic inline bool flag_encoded(const struct obs_output *output)\n{\n\treturn (output->info.flags & OBS_OUTPUT_ENCODED) != 0;\n}\n\nstatic inline bool log_flag_encoded(const struct obs_output *output, const char *func_name, bool inverse_log)\n{\n\tconst char *prefix = inverse_log ? \"n encoded\" : \" raw\";\n\tbool ret = flag_encoded(output);\n\tif ((!inverse_log && !ret) || (inverse_log && ret))\n\t\tblog(LOG_WARNING, \"Output '%s': Tried to use %s on a%s output\", output->context.name, func_name,\n\t\t     prefix);\n\treturn ret;\n}\n\nstatic inline void push_packet(struct obs_output *output, struct encoder_packet *packet,\n\t\t\t       struct encoder_packet_time *packet_time, uint64_t t)\n{\n\tstruct delay_data dd;\n\n\tdd.msg = DELAY_MSG_PACKET;\n\tdd.ts = t;\n\tdd.packet_time_valid = packet_time != NULL;\n\tif (packet_time != NULL)\n\t\tdd.packet_time = *packet_time;\n\tobs_encoder_packet_create_instance(&dd.packet, packet);\n\n\tpthread_mutex_lock(&output->delay_mutex);\n\tdeque_push_back(&output->delay_data, &dd, sizeof(dd));\n\tpthread_mutex_unlock(&output->delay_mutex);\n}\n\nstatic inline void process_delay_data(struct obs_output *output, struct delay_data *dd)\n{\n\tswitch (dd->msg) {\n\tcase DELAY_MSG_PACKET:\n\t\tif (!delay_active(output) || !delay_capturing(output))\n\t\t\tobs_encoder_packet_release(&dd->packet);\n\t\telse\n\t\t\toutput->delay_callback(output, &dd->packet, dd->packet_time_valid ? &dd->packet_time : NULL);\n\t\tbreak;\n\tcase DELAY_MSG_START:\n\t\tobs_output_actual_start(output);\n\t\tbreak;\n\tcase DELAY_MSG_STOP:\n\t\tobs_output_actual_stop(output, false, dd->ts);\n\t\tbreak;\n\t}\n}\n\nvoid obs_output_cleanup_delay(obs_output_t *output)\n{\n\tstruct delay_data dd;\n\n\twhile (output->delay_data.size) {\n\t\tdeque_pop_front(&output->delay_data, &dd, sizeof(dd));\n\t\tif (dd.msg == DELAY_MSG_PACKET) {\n\t\t\tobs_encoder_packet_release(&dd.packet);\n\t\t}\n\t}\n\n\toutput->active_delay_ns = 0;\n\tos_atomic_set_long(&output->delay_restart_refs, 0);\n}\n\nstatic inline bool pop_packet(struct obs_output *output, uint64_t t)\n{\n\tuint64_t elapsed_time;\n\tstruct delay_data dd;\n\tbool popped = false;\n\tbool preserve;\n\n\t/* ------------------------------------------------ */\n\n\tpreserve = (output->delay_cur_flags & OBS_OUTPUT_DELAY_PRESERVE) != 0;\n\n\tpthread_mutex_lock(&output->delay_mutex);\n\n\tif (output->delay_data.size) {\n\t\tdeque_peek_front(&output->delay_data, &dd, sizeof(dd));\n\t\telapsed_time = (t - dd.ts);\n\n\t\tif (preserve && output->reconnecting) {\n\t\t\toutput->active_delay_ns = elapsed_time;\n\n\t\t} else if (elapsed_time > output->active_delay_ns) {\n\t\t\tdeque_pop_front(&output->delay_data, NULL, sizeof(dd));\n\t\t\tpopped = true;\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&output->delay_mutex);\n\n\t/* ------------------------------------------------ */\n\n\tif (popped)\n\t\tprocess_delay_data(output, &dd);\n\n\treturn popped;\n}\n\nvoid process_delay(void *data, struct encoder_packet *packet, struct encoder_packet_time *packet_time)\n{\n\tstruct obs_output *output = data;\n\tuint64_t t = os_gettime_ns();\n\tpush_packet(output, packet, packet_time, t);\n\twhile (pop_packet(output, t))\n\t\t;\n}\n\nbool obs_output_delay_start(obs_output_t *output)\n{\n\tstruct delay_data dd = {\n\t\t.msg = DELAY_MSG_START,\n\t\t.ts = os_gettime_ns(),\n\t};\n\n\tif (!delay_active(output)) {\n\t\tbool can_begin = obs_output_can_begin_data_capture(output, 0);\n\t\tif (!can_begin)\n\t\t\treturn false;\n\t\tif (!obs_output_initialize_encoders(output, 0))\n\t\t\treturn false;\n\t}\n\n\tpthread_mutex_lock(&output->delay_mutex);\n\tdeque_push_back(&output->delay_data, &dd, sizeof(dd));\n\tpthread_mutex_unlock(&output->delay_mutex);\n\n\tos_atomic_inc_long(&output->delay_restart_refs);\n\n\tif (delay_active(output)) {\n\t\tdo_output_signal(output, \"starting\");\n\t\treturn true;\n\t}\n\n\tif (!obs_output_begin_data_capture(output, 0)) {\n\t\tobs_output_cleanup_delay(output);\n\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nvoid obs_output_delay_stop(obs_output_t *output)\n{\n\tstruct delay_data dd = {\n\t\t.msg = DELAY_MSG_STOP,\n\t\t.ts = os_gettime_ns(),\n\t};\n\n\tpthread_mutex_lock(&output->delay_mutex);\n\tdeque_push_back(&output->delay_data, &dd, sizeof(dd));\n\tpthread_mutex_unlock(&output->delay_mutex);\n\n\tdo_output_signal(output, \"stopping\");\n}\n\nvoid obs_output_set_delay(obs_output_t *output, uint32_t delay_sec, uint32_t flags)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_delay\"))\n\t\treturn;\n\tif (!log_flag_encoded(output, __FUNCTION__, false))\n\t\treturn;\n\n\toutput->delay_sec = delay_sec;\n\toutput->delay_flags = flags;\n}\n\nuint32_t obs_output_get_delay(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_set_delay\") ? output->delay_sec : 0;\n}\n\nuint32_t obs_output_get_active_delay(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_set_delay\") ? (uint32_t)(output->active_delay_ns / 1000000000ULL)\n\t\t\t\t\t\t\t\t: 0;\n}\n"
  },
  {
    "path": "libobs/obs-output.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <inttypes.h>\n#include \"util/platform.h\"\n#include \"util/util_uint64.h\"\n#include \"util/array-serializer.h\"\n#include \"graphics/math-extra.h\"\n#include \"obs.h\"\n#include \"obs-internal.h\"\n#include \"obs-av1.h\"\n\n#include <caption/caption.h>\n#include <caption/mpeg.h>\n\n#define get_weak(output) ((obs_weak_output_t *)output->context.control)\n\n#define RECONNECT_RETRY_MAX_MSEC (15 * 60 * 1000)\n#define RECONNECT_RETRY_BASE_EXP 1.5f\n\nstatic inline bool active(const struct obs_output *output)\n{\n\treturn os_atomic_load_bool(&output->active);\n}\n\nstatic inline bool reconnecting(const struct obs_output *output)\n{\n\treturn os_atomic_load_bool(&output->reconnecting);\n}\n\nstatic inline bool stopping(const struct obs_output *output)\n{\n\treturn os_event_try(output->stopping_event) == EAGAIN;\n}\n\nstatic inline bool delay_active(const struct obs_output *output)\n{\n\treturn os_atomic_load_bool(&output->delay_active);\n}\n\nstatic inline bool delay_capturing(const struct obs_output *output)\n{\n\treturn os_atomic_load_bool(&output->delay_capturing);\n}\n\nstatic inline bool data_capture_ending(const struct obs_output *output)\n{\n\treturn os_atomic_load_bool(&output->end_data_capture_thread_active);\n}\n\nstatic inline bool flag_encoded(const struct obs_output *output)\n{\n\treturn (output->info.flags & OBS_OUTPUT_ENCODED) != 0;\n}\n\nstatic inline bool log_flag_encoded(const struct obs_output *output, const char *func_name, bool inverse_log)\n{\n\tconst char *prefix = inverse_log ? \"n encoded\" : \" raw\";\n\tbool ret = flag_encoded(output);\n\tif ((!inverse_log && !ret) || (inverse_log && ret))\n\t\tblog(LOG_WARNING, \"Output '%s': Tried to use %s on a%s output\", output->context.name, func_name,\n\t\t     prefix);\n\treturn ret;\n}\n\nstatic inline bool flag_video(const struct obs_output *output)\n{\n\treturn (output->info.flags & OBS_OUTPUT_VIDEO) != 0;\n}\n\nstatic inline bool log_flag_video(const struct obs_output *output, const char *func_name)\n{\n\tbool ret = flag_video(output);\n\tif (!ret)\n\t\tblog(LOG_WARNING, \"Output '%s': Tried to use %s on a non-video output\", output->context.name,\n\t\t     func_name);\n\treturn ret;\n}\n\nstatic inline bool flag_audio(const struct obs_output *output)\n{\n\treturn (output->info.flags & OBS_OUTPUT_AUDIO) != 0;\n}\n\nstatic inline bool log_flag_audio(const struct obs_output *output, const char *func_name)\n{\n\tbool ret = flag_audio(output);\n\tif (!ret)\n\t\tblog(LOG_WARNING, \"Output '%s': Tried to use %s on a non-audio output\", output->context.name,\n\t\t     func_name);\n\treturn ret;\n}\n\nstatic inline bool flag_service(const struct obs_output *output)\n{\n\treturn (output->info.flags & OBS_OUTPUT_SERVICE) != 0;\n}\n\nstatic inline bool log_flag_service(const struct obs_output *output, const char *func_name)\n{\n\tbool ret = flag_service(output);\n\tif (!ret)\n\t\tblog(LOG_WARNING, \"Output '%s': Tried to use %s on a non-service output\", output->context.name,\n\t\t     func_name);\n\treturn ret;\n}\n\nconst struct obs_output_info *find_output(const char *id)\n{\n\tsize_t i;\n\tfor (i = 0; i < obs->output_types.num; i++)\n\t\tif (strcmp(obs->output_types.array[i].id, id) == 0)\n\t\t\treturn obs->output_types.array + i;\n\n\treturn NULL;\n}\n\nconst char *obs_output_get_display_name(const char *id)\n{\n\tconst struct obs_output_info *info = find_output(id);\n\treturn (info != NULL) ? info->get_name(info->type_data) : NULL;\n}\n\nobs_module_t *obs_output_get_module(const char *id)\n{\n\tobs_module_t *module = obs->first_module;\n\twhile (module) {\n\t\tfor (size_t i = 0; i < module->outputs.num; i++) {\n\t\t\tif (strcmp(module->outputs.array[i], id) == 0) {\n\t\t\t\treturn module;\n\t\t\t}\n\t\t}\n\t\tmodule = module->next;\n\t}\n\n\tmodule = obs->first_disabled_module;\n\twhile (module) {\n\t\tfor (size_t i = 0; i < module->outputs.num; i++) {\n\t\t\tif (strcmp(module->outputs.array[i], id) == 0) {\n\t\t\t\treturn module;\n\t\t\t}\n\t\t}\n\t\tmodule = module->next;\n\t}\n\n\treturn NULL;\n}\n\nenum obs_module_load_state obs_output_load_state(const char *id)\n{\n\tobs_module_t *module = obs_output_get_module(id);\n\tif (!module) {\n\t\treturn OBS_MODULE_MISSING;\n\t}\n\treturn module->load_state;\n}\n\nstatic const char *output_signals[] = {\n\t\"void start(ptr output)\",\n\t\"void stop(ptr output, int code)\",\n\t\"void pause(ptr output)\",\n\t\"void unpause(ptr output)\",\n\t\"void starting(ptr output)\",\n\t\"void stopping(ptr output)\",\n\t\"void activate(ptr output)\",\n\t\"void deactivate(ptr output)\",\n\t\"void reconnect(ptr output)\",\n\t\"void reconnect_success(ptr output)\",\n\tNULL,\n};\n\nstatic bool init_output_handlers(struct obs_output *output, const char *name, obs_data_t *settings,\n\t\t\t\t obs_data_t *hotkey_data)\n{\n\tif (!obs_context_data_init(&output->context, OBS_OBJ_TYPE_OUTPUT, settings, name, NULL, hotkey_data, false))\n\t\treturn false;\n\n\tsignal_handler_add_array(output->context.signals, output_signals);\n\treturn true;\n}\n\nobs_output_t *obs_output_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data)\n{\n\tconst struct obs_output_info *info = find_output(id);\n\tstruct obs_output *output;\n\tint ret;\n\n\toutput = bzalloc(sizeof(struct obs_output));\n\tpthread_mutex_init_value(&output->interleaved_mutex);\n\tpthread_mutex_init_value(&output->delay_mutex);\n\tpthread_mutex_init_value(&output->pause.mutex);\n\tpthread_mutex_init_value(&output->pkt_callbacks_mutex);\n\n\tif (pthread_mutex_init(&output->interleaved_mutex, NULL) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init(&output->delay_mutex, NULL) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init(&output->pause.mutex, NULL) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init(&output->pkt_callbacks_mutex, NULL) != 0)\n\t\tgoto fail;\n\tif (os_event_init(&output->stopping_event, OS_EVENT_TYPE_MANUAL) != 0)\n\t\tgoto fail;\n\tif (!init_output_handlers(output, name, settings, hotkey_data))\n\t\tgoto fail;\n\n\tos_event_signal(output->stopping_event);\n\n\tif (!info) {\n\t\tblog(LOG_ERROR, \"Output ID '%s' not found\", id);\n\n\t\toutput->info.id = bstrdup(id);\n\t\toutput->owns_info_id = true;\n\t} else {\n\t\toutput->info = *info;\n\t}\n\tif (!flag_encoded(output)) {\n\t\toutput->video = obs_get_video();\n\t\toutput->audio = obs_get_audio();\n\t}\n\tif (output->info.get_defaults)\n\t\toutput->info.get_defaults(output->context.settings);\n\n\tret = os_event_init(&output->reconnect_stop_event, OS_EVENT_TYPE_MANUAL);\n\tif (ret < 0)\n\t\tgoto fail;\n\n\toutput->reconnect_retry_sec = 2;\n\toutput->reconnect_retry_max = 20;\n\toutput->reconnect_retry_exp = RECONNECT_RETRY_BASE_EXP + (rand_float(0) * 0.05f);\n\toutput->valid = true;\n\n\tobs_context_init_control(&output->context, output, (obs_destroy_cb)obs_output_destroy);\n\tobs_context_data_insert(&output->context, &obs->data.outputs_mutex, &obs->data.first_output);\n\n\tif (info)\n\t\toutput->context.data = info->create(output->context.settings, output);\n\tif (!output->context.data)\n\t\tblog(LOG_ERROR, \"Failed to create output '%s'!\", name);\n\n\tblog(LOG_DEBUG, \"output '%s' (%s) created\", name, id);\n\treturn output;\n\nfail:\n\tobs_output_destroy(output);\n\treturn NULL;\n}\n\nstatic inline void free_packets(struct obs_output *output)\n{\n\tfor (size_t i = 0; i < output->interleaved_packets.num; i++)\n\t\tobs_encoder_packet_release(output->interleaved_packets.array + i);\n\tda_free(output->interleaved_packets);\n}\n\nstatic inline void clear_raw_audio_buffers(obs_output_t *output)\n{\n\tfor (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\tfor (size_t j = 0; j < MAX_AV_PLANES; j++) {\n\t\t\tdeque_free(&output->audio_buffer[i][j]);\n\t\t}\n\t}\n}\n\nstatic void destroy_caption_track(struct caption_track_data **ctrack_ptr)\n{\n\tif (!ctrack_ptr || !*ctrack_ptr) {\n\t\treturn;\n\t}\n\tstruct caption_track_data *ctrack = *ctrack_ptr;\n\tpthread_mutex_destroy(&ctrack->caption_mutex);\n\tdeque_free(&ctrack->caption_data);\n\tbfree(ctrack);\n\t*ctrack_ptr = NULL;\n}\n\nvoid obs_output_destroy(obs_output_t *output)\n{\n\tif (output) {\n\t\tobs_context_data_remove(&output->context);\n\n\t\tblog(LOG_DEBUG, \"output '%s' destroyed\", output->context.name);\n\n\t\tif (output->valid && active(output))\n\t\t\tobs_output_actual_stop(output, true, 0);\n\n\t\tos_event_wait(output->stopping_event);\n\t\tif (data_capture_ending(output))\n\t\t\tpthread_join(output->end_data_capture_thread, NULL);\n\n\t\tif (output->service)\n\t\t\toutput->service->output = NULL;\n\t\tif (output->context.data)\n\t\t\toutput->info.destroy(output->context.data);\n\n\t\tfree_packets(output);\n\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\t\tif (output->video_encoders[i]) {\n\t\t\t\tobs_encoder_remove_output(output->video_encoders[i], output);\n\t\t\t\tobs_encoder_release(output->video_encoders[i]);\n\t\t\t}\n\t\t\tif (output->caption_tracks[i]) {\n\t\t\t\tdestroy_caption_track(&output->caption_tracks[i]);\n\t\t\t}\n\t\t}\n\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\t\tif (output->audio_encoders[i]) {\n\t\t\t\tobs_encoder_remove_output(output->audio_encoders[i], output);\n\t\t\t\tobs_encoder_release(output->audio_encoders[i]);\n\t\t\t}\n\t\t}\n\n\t\tda_free(output->keyframe_group_tracking);\n\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++)\n\t\t\tda_free(output->encoder_packet_times[i]);\n\n\t\tda_free(output->pkt_callbacks);\n\n\t\tclear_raw_audio_buffers(output);\n\n\t\tos_event_destroy(output->stopping_event);\n\t\tpthread_mutex_destroy(&output->pause.mutex);\n\t\tpthread_mutex_destroy(&output->interleaved_mutex);\n\t\tpthread_mutex_destroy(&output->delay_mutex);\n\t\tpthread_mutex_destroy(&output->pkt_callbacks_mutex);\n\t\tos_event_destroy(output->reconnect_stop_event);\n\t\tobs_context_data_free(&output->context);\n\t\tdeque_free(&output->delay_data);\n\t\tif (output->owns_info_id)\n\t\t\tbfree((void *)output->info.id);\n\t\tif (output->last_error_message)\n\t\t\tbfree(output->last_error_message);\n\t\tbfree(output);\n\t}\n}\n\nconst char *obs_output_get_name(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_get_name\") ? output->context.name : NULL;\n}\n\nbool obs_output_actual_start(obs_output_t *output)\n{\n\tbool success = false;\n\n\tos_event_wait(output->stopping_event);\n\toutput->stop_code = 0;\n\tif (output->last_error_message) {\n\t\tbfree(output->last_error_message);\n\t\toutput->last_error_message = NULL;\n\t}\n\n\tif (output->context.data)\n\t\tsuccess = output->info.start(output->context.data);\n\n\tif (success) {\n\t\toutput->starting_drawn_count = obs->video.total_frames;\n\t\toutput->starting_lagged_count = obs->video.lagged_frames;\n\t}\n\n\tif (os_atomic_load_long(&output->delay_restart_refs))\n\t\tos_atomic_dec_long(&output->delay_restart_refs);\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tstruct caption_track_data *ctrack = output->caption_tracks[i];\n\t\tif (!ctrack) {\n\t\t\tcontinue;\n\t\t}\n\t\tpthread_mutex_lock(&ctrack->caption_mutex);\n\t\tctrack->caption_timestamp = 0;\n\t\tdeque_free(&ctrack->caption_data);\n\t\tdeque_init(&ctrack->caption_data);\n\t\tpthread_mutex_unlock(&ctrack->caption_mutex);\n\t}\n\n\treturn success;\n}\n\nbool obs_output_start(obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_start\"))\n\t\treturn false;\n\tif (!output->context.data)\n\t\treturn false;\n\n\tif (flag_service(output) &&\n\t    !(obs_service_can_try_to_connect(output->service) && obs_service_initialize(output->service, output)))\n\t\treturn false;\n\n\tif (output->delay_sec) {\n\t\treturn obs_output_delay_start(output);\n\t} else {\n\t\tif (obs_output_actual_start(output)) {\n\t\t\tdo_output_signal(output, \"starting\");\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n}\n\nstatic inline bool data_active(struct obs_output *output)\n{\n\treturn os_atomic_load_bool(&output->data_active);\n}\n\nstatic void log_frame_info(struct obs_output *output)\n{\n\tstruct obs_core_video *video = &obs->video;\n\n\tuint32_t drawn = video->total_frames - output->starting_drawn_count;\n\tuint32_t lagged = video->lagged_frames - output->starting_lagged_count;\n\n\tint dropped = obs_output_get_frames_dropped(output);\n\tint total = output->total_frames;\n\n\tdouble percentage_lagged = 0.0f;\n\tdouble percentage_dropped = 0.0f;\n\n\tif (drawn)\n\t\tpercentage_lagged = (double)lagged / (double)drawn * 100.0;\n\tif (dropped)\n\t\tpercentage_dropped = (double)dropped / (double)total * 100.0;\n\n\tblog(LOG_INFO, \"Output '%s': stopping\", output->context.name);\n\tif (!dropped || !total)\n\t\tblog(LOG_INFO, \"Output '%s': Total frames output: %d\", output->context.name, total);\n\telse\n\t\tblog(LOG_INFO,\n\t\t     \"Output '%s': Total frames output: %d\"\n\t\t     \" (%d attempted)\",\n\t\t     output->context.name, total - dropped, total);\n\n\tif (!lagged || !drawn)\n\t\tblog(LOG_INFO, \"Output '%s': Total drawn frames: %\" PRIu32, output->context.name, drawn);\n\telse\n\t\tblog(LOG_INFO, \"Output '%s': Total drawn frames: %\" PRIu32 \" (%\" PRIu32 \" attempted)\",\n\t\t     output->context.name, drawn - lagged, drawn);\n\n\tif (drawn && lagged)\n\t\tblog(LOG_INFO,\n\t\t     \"Output '%s': Number of lagged frames due \"\n\t\t     \"to rendering lag/stalls: %\" PRIu32 \" (%0.1f%%)\",\n\t\t     output->context.name, lagged, percentage_lagged);\n\tif (total && dropped)\n\t\tblog(LOG_INFO,\n\t\t     \"Output '%s': Number of dropped frames due \"\n\t\t     \"to insufficient bandwidth/connection stalls: \"\n\t\t     \"%d (%0.1f%%)\",\n\t\t     output->context.name, dropped, percentage_dropped);\n}\n\nstatic inline void signal_stop(struct obs_output *output);\n\nvoid obs_output_actual_stop(obs_output_t *output, bool force, uint64_t ts)\n{\n\tbool call_stop = true;\n\tbool was_reconnecting = false;\n\n\tif (stopping(output) && !force)\n\t\treturn;\n\n\tobs_output_pause(output, false);\n\n\tos_event_reset(output->stopping_event);\n\n\twas_reconnecting = reconnecting(output) && !delay_active(output);\n\tif (reconnecting(output)) {\n\t\tos_event_signal(output->reconnect_stop_event);\n\t\tif (output->reconnect_thread_active)\n\t\t\tpthread_join(output->reconnect_thread, NULL);\n\t}\n\n\tif (force) {\n\t\tif (delay_active(output)) {\n\t\t\tcall_stop = delay_capturing(output);\n\t\t\tos_atomic_set_bool(&output->delay_active, false);\n\t\t\tos_atomic_set_bool(&output->delay_capturing, false);\n\t\t\toutput->stop_code = OBS_OUTPUT_SUCCESS;\n\t\t\tobs_output_end_data_capture(output);\n\t\t\tos_event_signal(output->stopping_event);\n\t\t} else {\n\t\t\tcall_stop = true;\n\t\t}\n\t} else {\n\t\tcall_stop = true;\n\t}\n\n\tif (output->context.data && call_stop) {\n\t\toutput->info.stop(output->context.data, ts);\n\n\t} else if (was_reconnecting) {\n\t\toutput->stop_code = OBS_OUTPUT_SUCCESS;\n\t\tsignal_stop(output);\n\t\tos_event_signal(output->stopping_event);\n\t}\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tstruct caption_track_data *ctrack = output->caption_tracks[i];\n\t\tif (!ctrack) {\n\t\t\tcontinue;\n\t\t}\n\t\twhile (ctrack->caption_head) {\n\t\t\tctrack->caption_tail = ctrack->caption_head->next;\n\t\t\tbfree(ctrack->caption_head);\n\t\t\tctrack->caption_head = ctrack->caption_tail;\n\t\t}\n\t}\n\n\tda_clear(output->keyframe_group_tracking);\n}\n\nvoid obs_output_stop(obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_stop\"))\n\t\treturn;\n\tif (!output->context.data)\n\t\treturn;\n\tif (!active(output) && !reconnecting(output))\n\t\treturn;\n\tif (reconnecting(output)) {\n\t\tobs_output_force_stop(output);\n\t\treturn;\n\t}\n\n\tif (flag_encoded(output) && output->active_delay_ns) {\n\t\tobs_output_delay_stop(output);\n\t} else if (!stopping(output)) {\n\t\tdo_output_signal(output, \"stopping\");\n\t\tobs_output_actual_stop(output, false, os_gettime_ns());\n\t}\n}\n\nvoid obs_output_force_stop(obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_force_stop\"))\n\t\treturn;\n\n\tif (!stopping(output)) {\n\t\toutput->stop_code = 0;\n\t\tdo_output_signal(output, \"stopping\");\n\t}\n\tobs_output_actual_stop(output, true, 0);\n}\n\nbool obs_output_active(const obs_output_t *output)\n{\n\treturn (output != NULL) ? (active(output) || reconnecting(output)) : false;\n}\n\nuint32_t obs_output_get_flags(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_get_flags\") ? output->info.flags : 0;\n}\n\nuint32_t obs_get_output_flags(const char *id)\n{\n\tconst struct obs_output_info *info = find_output(id);\n\treturn info ? info->flags : 0;\n}\n\nstatic inline obs_data_t *get_defaults(const struct obs_output_info *info)\n{\n\tobs_data_t *settings = obs_data_create();\n\tif (info->get_defaults)\n\t\tinfo->get_defaults(settings);\n\treturn settings;\n}\n\nobs_data_t *obs_output_defaults(const char *id)\n{\n\tconst struct obs_output_info *info = find_output(id);\n\treturn (info) ? get_defaults(info) : NULL;\n}\n\nobs_properties_t *obs_get_output_properties(const char *id)\n{\n\tconst struct obs_output_info *info = find_output(id);\n\tif (info && info->get_properties) {\n\t\tobs_data_t *defaults = get_defaults(info);\n\t\tobs_properties_t *properties;\n\n\t\tproperties = info->get_properties(NULL);\n\t\tobs_properties_apply_settings(properties, defaults);\n\t\tobs_data_release(defaults);\n\t\treturn properties;\n\t}\n\treturn NULL;\n}\n\nobs_properties_t *obs_output_properties(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_properties\"))\n\t\treturn NULL;\n\n\tif (output && output->info.get_properties) {\n\t\tobs_properties_t *props;\n\t\tprops = output->info.get_properties(output->context.data);\n\t\tobs_properties_apply_settings(props, output->context.settings);\n\t\treturn props;\n\t}\n\n\treturn NULL;\n}\n\nvoid obs_output_update(obs_output_t *output, obs_data_t *settings)\n{\n\tif (!obs_output_valid(output, \"obs_output_update\"))\n\t\treturn;\n\n\tobs_data_apply(output->context.settings, settings);\n\n\tif (output->info.update)\n\t\toutput->info.update(output->context.data, output->context.settings);\n}\n\nobs_data_t *obs_output_get_settings(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_settings\"))\n\t\treturn NULL;\n\n\tobs_data_addref(output->context.settings);\n\treturn output->context.settings;\n}\n\nbool obs_output_can_pause(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_can_pause\") ? !!(output->info.flags & OBS_OUTPUT_CAN_PAUSE) : false;\n}\n\nstatic inline void end_pause(struct pause_data *pause, uint64_t ts)\n{\n\tif (!pause->ts_end) {\n\t\tpause->ts_end = ts;\n\t\tpause->ts_offset += pause->ts_end - pause->ts_start;\n\t}\n}\n\nstatic inline uint64_t get_closest_v_ts(struct pause_data *pause)\n{\n\tuint64_t interval = obs->video.video_frame_interval_ns;\n\tuint64_t i2 = interval * 2;\n\tuint64_t ts = os_gettime_ns();\n\n\treturn pause->last_video_ts + ((ts - pause->last_video_ts + i2) / interval) * interval;\n}\n\nstatic inline bool pause_can_start(struct pause_data *pause)\n{\n\treturn !pause->ts_start && !pause->ts_end;\n}\n\nstatic inline bool pause_can_stop(struct pause_data *pause)\n{\n\treturn !!pause->ts_start && !pause->ts_end;\n}\n\nstatic bool get_first_audio_encoder_index(const struct obs_output *output, size_t *index)\n{\n\tif (!index)\n\t\treturn false;\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tif (output->audio_encoders[i]) {\n\t\t\t*index = i;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic bool get_first_video_encoder_index(const struct obs_output *output, size_t *index)\n{\n\tif (!index)\n\t\treturn false;\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tif (output->video_encoders[i]) {\n\t\t\t*index = i;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic bool obs_encoded_output_pause(obs_output_t *output, bool pause)\n{\n\tobs_encoder_t *venc[MAX_OUTPUT_VIDEO_ENCODERS];\n\tobs_encoder_t *aenc[MAX_OUTPUT_AUDIO_ENCODERS];\n\tuint64_t closest_v_ts;\n\tbool success = false;\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++)\n\t\tvenc[i] = output->video_encoders[i];\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++)\n\t\taenc[i] = output->audio_encoders[i];\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tif (venc[i]) {\n\t\t\tpthread_mutex_lock(&venc[i]->pause.mutex);\n\t\t}\n\t}\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tif (aenc[i]) {\n\t\t\tpthread_mutex_lock(&aenc[i]->pause.mutex);\n\t\t}\n\t}\n\n\t/* ---------------------------- */\n\n\tsize_t first_venc_index;\n\tif (!get_first_video_encoder_index(output, &first_venc_index))\n\t\tgoto fail;\n\n\tclosest_v_ts = get_closest_v_ts(&venc[first_venc_index]->pause);\n\n\tif (pause) {\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\t\tif (venc[i] && !pause_can_start(&venc[i]->pause)) {\n\t\t\t\tgoto fail;\n\t\t\t}\n\t\t}\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\t\tif (aenc[i] && !pause_can_start(&aenc[i]->pause)) {\n\t\t\t\tgoto fail;\n\t\t\t}\n\t\t}\n\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\t\tif (venc[i]) {\n\t\t\t\tos_atomic_set_bool(&venc[i]->paused, true);\n\t\t\t\tvenc[i]->pause.ts_start = closest_v_ts;\n\t\t\t}\n\t\t}\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\t\tif (aenc[i]) {\n\t\t\t\tos_atomic_set_bool(&aenc[i]->paused, true);\n\t\t\t\taenc[i]->pause.ts_start = closest_v_ts;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\t\tif (venc[i] && !pause_can_stop(&venc[i]->pause)) {\n\t\t\t\tgoto fail;\n\t\t\t}\n\t\t}\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\t\tif (aenc[i] && !pause_can_stop(&aenc[i]->pause)) {\n\t\t\t\tgoto fail;\n\t\t\t}\n\t\t}\n\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\t\tif (venc[i]) {\n\t\t\t\tos_atomic_set_bool(&venc[i]->paused, false);\n\t\t\t\tend_pause(&venc[i]->pause, closest_v_ts);\n\t\t\t}\n\t\t}\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\t\tif (aenc[i]) {\n\t\t\t\tos_atomic_set_bool(&aenc[i]->paused, false);\n\t\t\t\tend_pause(&aenc[i]->pause, closest_v_ts);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* ---------------------------- */\n\n\tsuccess = true;\n\nfail:\n\tfor (size_t i = MAX_OUTPUT_AUDIO_ENCODERS; i > 0; i--) {\n\t\tif (aenc[i - 1]) {\n\t\t\tpthread_mutex_unlock(&aenc[i - 1]->pause.mutex);\n\t\t}\n\t}\n\tfor (size_t i = MAX_OUTPUT_VIDEO_ENCODERS; i > 0; i--) {\n\t\tif (venc[i - 1]) {\n\t\t\tpthread_mutex_unlock(&venc[i - 1]->pause.mutex);\n\t\t}\n\t}\n\n\treturn success;\n}\n\nstatic bool obs_raw_output_pause(obs_output_t *output, bool pause)\n{\n\tbool success;\n\tuint64_t closest_v_ts;\n\n\tpthread_mutex_lock(&output->pause.mutex);\n\tclosest_v_ts = get_closest_v_ts(&output->pause);\n\tif (pause) {\n\t\tsuccess = pause_can_start(&output->pause);\n\t\tif (success)\n\t\t\toutput->pause.ts_start = closest_v_ts;\n\t} else {\n\t\tsuccess = pause_can_stop(&output->pause);\n\t\tif (success)\n\t\t\tend_pause(&output->pause, closest_v_ts);\n\t}\n\tpthread_mutex_unlock(&output->pause.mutex);\n\n\treturn success;\n}\n\nbool obs_output_pause(obs_output_t *output, bool pause)\n{\n\tbool success;\n\n\tif (!obs_output_valid(output, \"obs_output_pause\"))\n\t\treturn false;\n\tif ((output->info.flags & OBS_OUTPUT_CAN_PAUSE) == 0)\n\t\treturn false;\n\tif (!os_atomic_load_bool(&output->active))\n\t\treturn false;\n\tif (os_atomic_load_bool(&output->paused) == pause)\n\t\treturn true;\n\n\tsuccess = flag_encoded(output) ? obs_encoded_output_pause(output, pause) : obs_raw_output_pause(output, pause);\n\tif (success) {\n\t\tos_atomic_set_bool(&output->paused, pause);\n\t\tdo_output_signal(output, pause ? \"pause\" : \"unpause\");\n\n\t\tblog(LOG_INFO, \"output %s %spaused\", output->context.name, pause ? \"\" : \"un\");\n\t}\n\treturn success;\n}\n\nbool obs_output_paused(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_paused\") ? os_atomic_load_bool(&output->paused) : false;\n}\n\nuint64_t obs_output_get_pause_offset(obs_output_t *output)\n{\n\tuint64_t offset;\n\n\tif (!obs_output_valid(output, \"obs_output_get_pause_offset\"))\n\t\treturn 0;\n\n\tpthread_mutex_lock(&output->pause.mutex);\n\toffset = output->pause.ts_offset;\n\tpthread_mutex_unlock(&output->pause.mutex);\n\n\treturn offset;\n}\n\nsignal_handler_t *obs_output_get_signal_handler(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_get_signal_handler\") ? output->context.signals : NULL;\n}\n\nproc_handler_t *obs_output_get_proc_handler(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_get_proc_handler\") ? output->context.procs : NULL;\n}\n\nvoid obs_output_set_media(obs_output_t *output, video_t *video, audio_t *audio)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_media\"))\n\t\treturn;\n\tif (log_flag_encoded(output, __FUNCTION__, true))\n\t\treturn;\n\n\tif (flag_video(output))\n\t\toutput->video = video;\n\tif (flag_audio(output))\n\t\toutput->audio = audio;\n}\n\nvideo_t *obs_output_video(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_video\"))\n\t\treturn NULL;\n\n\tif (!flag_encoded(output))\n\t\treturn output->video;\n\n\tobs_encoder_t *vencoder = obs_output_get_video_encoder(output);\n\treturn obs_encoder_video(vencoder);\n}\n\naudio_t *obs_output_audio(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_audio\"))\n\t\treturn NULL;\n\n\tif (!flag_encoded(output))\n\t\treturn output->audio;\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tif (output->audio_encoders[i])\n\t\t\treturn obs_encoder_audio(output->audio_encoders[i]);\n\t}\n\n\treturn NULL;\n}\n\nstatic inline size_t get_first_mixer(const obs_output_t *output)\n{\n\tfor (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {\n\t\tif ((((size_t)1 << i) & output->mixer_mask) != 0) {\n\t\t\treturn i;\n\t\t}\n\t}\n\n\treturn 0;\n}\n\nvoid obs_output_set_mixer(obs_output_t *output, size_t mixer_idx)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_mixer\"))\n\t\treturn;\n\tif (log_flag_encoded(output, __FUNCTION__, true))\n\t\treturn;\n\tif (active(output))\n\t\treturn;\n\n\toutput->mixer_mask = (size_t)1 << mixer_idx;\n}\n\nsize_t obs_output_get_mixer(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_mixer\"))\n\t\treturn 0;\n\n\treturn get_first_mixer(output);\n}\n\nvoid obs_output_set_mixers(obs_output_t *output, size_t mixers)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_mixers\"))\n\t\treturn;\n\tif (log_flag_encoded(output, __FUNCTION__, true))\n\t\treturn;\n\tif (active(output))\n\t\treturn;\n\n\toutput->mixer_mask = mixers;\n}\n\nsize_t obs_output_get_mixers(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_get_mixers\") ? output->mixer_mask : 0;\n}\n\nvoid obs_output_remove_encoder_internal(struct obs_output *output, struct obs_encoder *encoder)\n{\n\tif (!obs_output_valid(output, \"obs_output_remove_encoder_internal\"))\n\t\treturn;\n\n\tif (encoder->info.type == OBS_ENCODER_VIDEO) {\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\t\tobs_encoder_t *video = output->video_encoders[i];\n\t\t\tif (video == encoder) {\n\t\t\t\toutput->video_encoders[i] = NULL;\n\t\t\t\tobs_encoder_release(video);\n\t\t\t}\n\t\t}\n\t} else if (encoder->info.type == OBS_ENCODER_AUDIO) {\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\t\tobs_encoder_t *audio = output->audio_encoders[i];\n\t\t\tif (audio == encoder) {\n\t\t\t\toutput->audio_encoders[i] = NULL;\n\t\t\t\tobs_encoder_release(audio);\n\t\t\t}\n\t\t}\n\t}\n}\n\nvoid obs_output_remove_encoder(struct obs_output *output, struct obs_encoder *encoder)\n{\n\tif (!obs_output_valid(output, \"obs_output_remove_encoder\"))\n\t\treturn;\n\tif (active(output))\n\t\treturn;\n\n\tobs_output_remove_encoder_internal(output, encoder);\n}\n\nstatic struct caption_track_data *create_caption_track()\n{\n\tstruct caption_track_data *rval = bzalloc(sizeof(struct caption_track_data));\n\tpthread_mutex_init_value(&rval->caption_mutex);\n\n\tif (pthread_mutex_init(&rval->caption_mutex, NULL) != 0) {\n\t\tbfree(rval);\n\t\trval = NULL;\n\t}\n\treturn rval;\n}\n\nvoid obs_output_set_video_encoder2(obs_output_t *output, obs_encoder_t *encoder, size_t idx)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_video_encoder2\"))\n\t\treturn;\n\tif (!log_flag_encoded(output, __FUNCTION__, false) || !log_flag_video(output, __FUNCTION__))\n\t\treturn;\n\tif (encoder && encoder->info.type != OBS_ENCODER_VIDEO) {\n\t\tblog(LOG_WARNING, \"obs_output_set_video_encoder: \"\n\t\t\t\t  \"encoder passed is not a video encoder\");\n\t\treturn;\n\t}\n\tif (active(output)) {\n\t\tblog(LOG_WARNING,\n\t\t     \"%s: tried to set video encoder on output \\\"%s\\\" \"\n\t\t     \"while the output is still active!\",\n\t\t     __FUNCTION__, output->context.name);\n\t\treturn;\n\t}\n\n\tif ((output->info.flags & OBS_OUTPUT_MULTI_TRACK_VIDEO) != 0) {\n\t\tif (idx >= MAX_OUTPUT_VIDEO_ENCODERS) {\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tif (idx > 0) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (output->video_encoders[idx] == encoder)\n\t\treturn;\n\n\tobs_encoder_remove_output(output->video_encoders[idx], output);\n\tobs_encoder_release(output->video_encoders[idx]);\n\n\toutput->video_encoders[idx] = obs_encoder_get_ref(encoder);\n\tobs_encoder_add_output(output->video_encoders[idx], output);\n\n\tdestroy_caption_track(&output->caption_tracks[idx]);\n\tif (encoder != NULL) {\n\t\toutput->caption_tracks[idx] = create_caption_track();\n\t} else {\n\t\toutput->caption_tracks[idx] = NULL;\n\t}\n\n\t// Set preferred resolution on the default index to preserve old behavior\n\tif (idx == 0) {\n\t\t/* set the preferred resolution on the encoder */\n\t\tif (output->scaled_width && output->scaled_height)\n\t\t\tobs_encoder_set_scaled_size(output->video_encoders[idx], output->scaled_width,\n\t\t\t\t\t\t    output->scaled_height);\n\t}\n}\n\nvoid obs_output_set_video_encoder(obs_output_t *output, obs_encoder_t *encoder)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_video_encoder\"))\n\t\treturn;\n\n\tobs_output_set_video_encoder2(output, encoder, 0);\n}\n\nvoid obs_output_set_audio_encoder(obs_output_t *output, obs_encoder_t *encoder, size_t idx)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_audio_encoder\"))\n\t\treturn;\n\tif (!log_flag_encoded(output, __FUNCTION__, false) || !log_flag_audio(output, __FUNCTION__))\n\t\treturn;\n\tif (encoder && encoder->info.type != OBS_ENCODER_AUDIO) {\n\t\tblog(LOG_WARNING, \"obs_output_set_audio_encoder: \"\n\t\t\t\t  \"encoder passed is not an audio encoder\");\n\t\treturn;\n\t}\n\tif (active(output)) {\n\t\tblog(LOG_WARNING,\n\t\t     \"%s: tried to set audio encoder %d on output \\\"%s\\\" \"\n\t\t     \"while the output is still active!\",\n\t\t     __FUNCTION__, (int)idx, output->context.name);\n\t\treturn;\n\t}\n\n\tif ((output->info.flags & OBS_OUTPUT_MULTI_TRACK_AUDIO) != 0) {\n\t\tif (idx >= MAX_OUTPUT_AUDIO_ENCODERS) {\n\t\t\treturn;\n\t\t}\n\t} else {\n\t\tif (idx > 0) {\n\t\t\treturn;\n\t\t}\n\t}\n\n\tif (output->audio_encoders[idx] == encoder)\n\t\treturn;\n\n\tobs_encoder_remove_output(output->audio_encoders[idx], output);\n\tobs_encoder_release(output->audio_encoders[idx]);\n\n\toutput->audio_encoders[idx] = obs_encoder_get_ref(encoder);\n\tobs_encoder_add_output(output->audio_encoders[idx], output);\n}\n\nobs_encoder_t *obs_output_get_video_encoder2(const obs_output_t *output, size_t idx)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_video_encoder2\"))\n\t\treturn NULL;\n\n\tif (idx >= MAX_OUTPUT_VIDEO_ENCODERS)\n\t\treturn NULL;\n\n\treturn output->video_encoders[idx];\n}\n\nobs_encoder_t *obs_output_get_video_encoder(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_video_encoder\"))\n\t\treturn NULL;\n\n\tsize_t first_venc_idx;\n\tif (get_first_video_encoder_index(output, &first_venc_idx))\n\t\treturn obs_output_get_video_encoder2(output, first_venc_idx);\n\telse\n\t\treturn NULL;\n}\n\nobs_encoder_t *obs_output_get_audio_encoder(const obs_output_t *output, size_t idx)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_audio_encoder\"))\n\t\treturn NULL;\n\n\tif (idx >= MAX_OUTPUT_AUDIO_ENCODERS)\n\t\treturn NULL;\n\n\treturn output->audio_encoders[idx];\n}\n\nvoid obs_output_set_service(obs_output_t *output, obs_service_t *service)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_service\"))\n\t\treturn;\n\tif (!log_flag_service(output, __FUNCTION__) || active(output) || !service || service->active)\n\t\treturn;\n\n\tif (service->output)\n\t\tservice->output->service = NULL;\n\n\toutput->service = service;\n\tservice->output = output;\n}\n\nobs_service_t *obs_output_get_service(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_get_service\") ? output->service : NULL;\n}\n\nvoid obs_output_set_reconnect_settings(obs_output_t *output, int retry_count, int retry_sec)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_reconnect_settings\"))\n\t\treturn;\n\n\toutput->reconnect_retry_max = retry_count;\n\toutput->reconnect_retry_sec = retry_sec;\n}\n\nuint64_t obs_output_get_total_bytes(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_total_bytes\"))\n\t\treturn 0;\n\tif (!output->info.get_total_bytes)\n\t\treturn 0;\n\n\tif (delay_active(output) && !delay_capturing(output))\n\t\treturn 0;\n\n\treturn output->info.get_total_bytes(output->context.data);\n}\n\nint obs_output_get_frames_dropped(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_frames_dropped\"))\n\t\treturn 0;\n\tif (!output->info.get_dropped_frames)\n\t\treturn 0;\n\n\treturn output->info.get_dropped_frames(output->context.data);\n}\n\nint obs_output_get_total_frames(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_get_total_frames\") ? output->total_frames : 0;\n}\n\nvoid obs_output_set_preferred_size2(obs_output_t *output, uint32_t width, uint32_t height, size_t idx)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_preferred_size2\"))\n\t\treturn;\n\tif (!log_flag_video(output, __FUNCTION__))\n\t\treturn;\n\tif (idx >= MAX_OUTPUT_VIDEO_ENCODERS)\n\t\treturn;\n\n\tif (active(output)) {\n\t\tblog(LOG_WARNING,\n\t\t     \"output '%s': Cannot set the preferred \"\n\t\t     \"resolution while the output is active\",\n\t\t     obs_output_get_name(output));\n\t\treturn;\n\t}\n\n\t// Used for raw video output\n\tif (idx == 0) {\n\t\toutput->scaled_width = width;\n\t\toutput->scaled_height = height;\n\t}\n\n\tif (flag_encoded(output)) {\n\t\tif (output->video_encoders[idx])\n\t\t\tobs_encoder_set_scaled_size(output->video_encoders[idx], width, height);\n\t}\n}\n\nvoid obs_output_set_preferred_size(obs_output_t *output, uint32_t width, uint32_t height)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_preferred_size\"))\n\t\treturn;\n\tif (!log_flag_video(output, __FUNCTION__))\n\t\treturn;\n\n\tobs_output_set_preferred_size2(output, width, height, 0);\n}\n\nuint32_t obs_output_get_width2(const obs_output_t *output, size_t idx)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_width2\"))\n\t\treturn 0;\n\tif (!log_flag_video(output, __FUNCTION__))\n\t\treturn 0;\n\tif (idx >= MAX_OUTPUT_VIDEO_ENCODERS)\n\t\treturn 0;\n\n\tif (flag_encoded(output)) {\n\t\tif (output->video_encoders[idx])\n\t\t\treturn obs_encoder_get_width(output->video_encoders[idx]);\n\t\telse\n\t\t\treturn 0;\n\t} else\n\t\treturn output->scaled_width != 0 ? output->scaled_width : video_output_get_width(output->video);\n}\n\nuint32_t obs_output_get_width(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_width\"))\n\t\treturn 0;\n\tif (!log_flag_video(output, __FUNCTION__))\n\t\treturn 0;\n\n\treturn obs_output_get_width2(output, 0);\n}\n\nuint32_t obs_output_get_height2(const obs_output_t *output, size_t idx)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_height2\"))\n\t\treturn 0;\n\tif (!log_flag_video(output, __FUNCTION__))\n\t\treturn 0;\n\tif (idx >= MAX_OUTPUT_VIDEO_ENCODERS)\n\t\treturn 0;\n\n\tif (flag_encoded(output)) {\n\t\tif (output->video_encoders[idx])\n\t\t\treturn obs_encoder_get_height(output->video_encoders[idx]);\n\t\telse\n\t\t\treturn 0;\n\t} else\n\t\treturn output->scaled_height != 0 ? output->scaled_height : video_output_get_height(output->video);\n}\n\nuint32_t obs_output_get_height(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_height\"))\n\t\treturn 0;\n\tif (!log_flag_video(output, __FUNCTION__))\n\t\treturn 0;\n\n\treturn obs_output_get_height2(output, 0);\n}\n\nvoid obs_output_set_video_conversion(obs_output_t *output, const struct video_scale_info *conversion)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_video_conversion\"))\n\t\treturn;\n\tif (!obs_ptr_valid(conversion, \"obs_output_set_video_conversion\"))\n\t\treturn;\n\tif (log_flag_encoded(output, __FUNCTION__, true) || !log_flag_video(output, __FUNCTION__))\n\t\treturn;\n\n\toutput->video_conversion = *conversion;\n\toutput->video_conversion_set = true;\n}\n\nvoid obs_output_set_audio_conversion(obs_output_t *output, const struct audio_convert_info *conversion)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_audio_conversion\"))\n\t\treturn;\n\tif (!obs_ptr_valid(conversion, \"obs_output_set_audio_conversion\"))\n\t\treturn;\n\tif (log_flag_encoded(output, __FUNCTION__, true) || !log_flag_audio(output, __FUNCTION__))\n\t\treturn;\n\n\toutput->audio_conversion = *conversion;\n\toutput->audio_conversion_set = true;\n}\n\nstatic inline bool video_valid(const struct obs_output *output)\n{\n\tif (flag_encoded(output)) {\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\t\tif (output->video_encoders[i]) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t} else {\n\t\treturn output->video != NULL;\n\t}\n}\n\nstatic inline bool audio_valid(const struct obs_output *output)\n{\n\tif (flag_encoded(output)) {\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\t\tif (output->audio_encoders[i]) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\treturn output->audio != NULL;\n}\n\nstatic bool can_begin_data_capture(const struct obs_output *output)\n{\n\tif (flag_video(output) && !video_valid(output))\n\t\treturn false;\n\n\tif (flag_audio(output) && !audio_valid(output))\n\t\treturn false;\n\n\tif (flag_service(output) && !output->service)\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic inline bool has_scaling(const struct obs_output *output)\n{\n\tuint32_t video_width = video_output_get_width(output->video);\n\tuint32_t video_height = video_output_get_height(output->video);\n\n\treturn output->scaled_width && output->scaled_height &&\n\t       (video_width != output->scaled_width || video_height != output->scaled_height);\n}\n\nconst struct video_scale_info *obs_output_get_video_conversion(struct obs_output *output)\n{\n\tif (log_flag_encoded(output, __FUNCTION__, true) || !log_flag_video(output, __FUNCTION__))\n\t\treturn NULL;\n\n\tif (output->video_conversion_set) {\n\t\tif (!output->video_conversion.width)\n\t\t\toutput->video_conversion.width = obs_output_get_width(output);\n\n\t\tif (!output->video_conversion.height)\n\t\t\toutput->video_conversion.height = obs_output_get_height(output);\n\n\t\treturn &output->video_conversion;\n\n\t} else if (has_scaling(output)) {\n\t\tconst struct video_output_info *info = video_output_get_info(output->video);\n\n\t\toutput->video_conversion.format = info->format;\n\t\toutput->video_conversion.colorspace = VIDEO_CS_DEFAULT;\n\t\toutput->video_conversion.range = VIDEO_RANGE_DEFAULT;\n\t\toutput->video_conversion.width = output->scaled_width;\n\t\toutput->video_conversion.height = output->scaled_height;\n\t\treturn &output->video_conversion;\n\t}\n\n\treturn NULL;\n}\n\nstatic inline struct audio_convert_info *get_audio_conversion(struct obs_output *output)\n{\n\treturn output->audio_conversion_set ? &output->audio_conversion : NULL;\n}\n\nstatic size_t get_encoder_index(const struct obs_output *output, struct encoder_packet *pkt)\n{\n\tif (pkt->type == OBS_ENCODER_VIDEO) {\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\t\tstruct obs_encoder *encoder = output->video_encoders[i];\n\n\t\t\tif (encoder && pkt->encoder == encoder)\n\t\t\t\treturn i;\n\t\t}\n\t} else if (pkt->type == OBS_ENCODER_AUDIO) {\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\t\tstruct obs_encoder *encoder = output->audio_encoders[i];\n\n\t\t\tif (encoder && pkt->encoder == encoder)\n\t\t\t\treturn i;\n\t\t}\n\t}\n\n\tassert(false);\n\treturn 0;\n}\n\nstatic inline void check_received(struct obs_output *output, struct encoder_packet *out)\n{\n\tif (out->type == OBS_ENCODER_VIDEO) {\n\t\tif (!output->received_video[out->track_idx])\n\t\t\toutput->received_video[out->track_idx] = true;\n\t} else {\n\t\tif (!output->received_audio)\n\t\t\toutput->received_audio = true;\n\t}\n}\n\nstatic inline void apply_interleaved_packet_offset(struct obs_output *output, struct encoder_packet *out,\n\t\t\t\t\t\t   struct encoder_packet_time *packet_time)\n{\n\tint64_t offset;\n\n\t/* audio and video need to start at timestamp 0, and the encoders\n\t * may not currently be at 0 when we get data.  so, we store the\n\t * current dts as offset and subtract that value from the dts/pts\n\t * of the output packet. */\n\toffset = (out->type == OBS_ENCODER_VIDEO) ? output->video_offsets[out->track_idx]\n\t\t\t\t\t\t  : output->audio_offsets[out->track_idx];\n\n\tout->dts -= offset;\n\tout->pts -= offset;\n\tif (packet_time)\n\t\tpacket_time->pts -= offset;\n\n\t/* convert the newly adjusted dts to relative dts time to ensure proper\n\t * interleaving.  if we're using an audio encoder that's already been\n\t * started on another output, then the first audio packet may not be\n\t * quite perfectly synced up in terms of system time (and there's\n\t * nothing we can really do about that), but it will always at least be\n\t * within a 23ish millisecond threshold (at least for AAC) */\n\tout->dts_usec = packet_dts_usec(out);\n}\n\nstatic inline bool has_higher_opposing_ts(struct obs_output *output, struct encoder_packet *packet)\n{\n\tbool has_higher = true;\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tif (!output->video_encoders[i] || (packet->type == OBS_ENCODER_VIDEO && i == packet->track_idx))\n\t\t\tcontinue;\n\t\thas_higher = has_higher && output->highest_video_ts[i] > packet->dts_usec;\n\t}\n\n\treturn packet->type == OBS_ENCODER_AUDIO ? has_higher\n\t\t\t\t\t\t : (has_higher && output->highest_audio_ts > packet->dts_usec);\n}\n\nstatic size_t extract_buffer_from_sei(sei_t *sei, uint8_t **data_out)\n{\n\tif (!sei || !sei->head) {\n\t\treturn 0;\n\t}\n\t/* We should only need to get one payload, because the SEI that was\n\t * generated should only have one message, so no need to iterate. If\n\t * we did iterate, we would need to generate multiple OBUs. */\n\tsei_message_t *msg = sei_message_head(sei);\n\tint payload_size = (int)sei_message_size(msg);\n\tuint8_t *payload_data = sei_message_data(msg);\n\t*data_out = bmalloc(payload_size);\n\tmemcpy(*data_out, payload_data, payload_size);\n\treturn payload_size;\n}\n\nstatic const uint8_t nal_start[4] = {0, 0, 0, 1};\nstatic bool add_caption(struct obs_output *output, struct encoder_packet *out)\n{\n\tstruct encoder_packet backup = *out;\n\tsei_t sei;\n\tuint8_t *data = NULL;\n\tsize_t size;\n\tlong ref = 1;\n\tbool avc = false;\n\tbool hevc = false;\n\tbool av1 = false;\n\n\t/* Instead of exiting early for unsupported codecs, we will continue\n\t * processing to allow the freeing of caption data even if the captions\n\t * will not be included in the bitstream due to being unimplemented in\n\t * the given codec. */\n\tif (strcmp(out->encoder->info.codec, \"h264\") == 0) {\n\t\tavc = true;\n\t} else if (strcmp(out->encoder->info.codec, \"av1\") == 0) {\n\t\tav1 = true;\n#ifdef ENABLE_HEVC\n\t} else if (strcmp(out->encoder->info.codec, \"hevc\") == 0) {\n\t\thevc = true;\n#endif\n\t}\n\n\tDARRAY(uint8_t) out_data;\n\n\tif (out->priority > 1)\n\t\treturn false;\n\n\tstruct caption_track_data *ctrack = output->caption_tracks[out->track_idx];\n\tif (!ctrack) {\n\t\tblog(LOG_DEBUG, \"Caption track for index: %lu has not been initialized\", out->track_idx);\n\t\treturn false;\n\t}\n\n#ifdef ENABLE_HEVC\n\tuint8_t hevc_nal_header[2];\n\tif (hevc) {\n\t\tsize_t nal_header_index_start = 4;\n\t\t// Skip past the annex-b start code\n\t\tif (memcmp(out->data, nal_start + 1, 3) == 0) {\n\t\t\tnal_header_index_start = 3;\n\t\t} else if (memcmp(out->data, nal_start, 4) == 0) {\n\t\t\tnal_header_index_start = 4;\n\n\t\t} else {\n\t\t\t/* We shouldn't ever see this unless we start getting\n\t\t\t * packets without annex-b start codes. */\n\t\t\tblog(LOG_DEBUG, \"Annex-B start code not found. We may not \"\n\t\t\t\t\t\"generate a valid HEVC NAL unit header \"\n\t\t\t\t\t\"for our caption\");\n\t\t\treturn false;\n\t\t}\n\t\t/* We will use the same 2 byte NAL unit header for the CC SEI,\n\t\t * but swap the NAL types out. */\n\t\thevc_nal_header[0] = out->data[nal_header_index_start];\n\t\thevc_nal_header[1] = out->data[nal_header_index_start + 1];\n\t}\n#endif\n\tsei_init(&sei, 0.0);\n\n\tda_init(out_data);\n\tda_push_back_array(out_data, (uint8_t *)&ref, sizeof(ref));\n\tda_push_back_array(out_data, out->data, out->size);\n\n\tif (ctrack->caption_data.size > 0) {\n\n\t\tcea708_t cea708;\n\t\tcea708_init(&cea708, 0); // set up a new popon frame\n\t\tvoid *caption_buf = bzalloc(3 * sizeof(uint8_t));\n\n\t\twhile (ctrack->caption_data.size > 0) {\n\t\t\tdeque_pop_front(&ctrack->caption_data, caption_buf, 3 * sizeof(uint8_t));\n\n\t\t\tif ((((uint8_t *)caption_buf)[0] & 0x3) != 0) {\n\t\t\t\t// only send cea 608\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tuint16_t captionData = ((uint8_t *)caption_buf)[1];\n\t\t\tcaptionData = captionData << 8;\n\t\t\tcaptionData += ((uint8_t *)caption_buf)[2];\n\n\t\t\t// padding\n\t\t\tif (captionData == 0x8080) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (captionData == 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!eia608_parity_varify(captionData)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcea708_add_cc_data(&cea708, 1, ((uint8_t *)caption_buf)[0] & 0x3, captionData);\n\t\t}\n\n\t\tbfree(caption_buf);\n\n\t\tsei_message_t *msg = sei_message_new(sei_type_user_data_registered_itu_t_t35, 0, CEA608_MAX_SIZE);\n\t\tmsg->size = cea708_render(&cea708, sei_message_data(msg), sei_message_size(msg));\n\t\tsei_message_append(&sei, msg);\n\t} else if (ctrack->caption_head) {\n\t\tcaption_frame_t cf;\n\t\tcaption_frame_init(&cf);\n\t\tcaption_frame_from_text(&cf, &ctrack->caption_head->text[0]);\n\n\t\tsei_from_caption_frame(&sei, &cf);\n\n\t\tstruct caption_text *next = ctrack->caption_head->next;\n\t\tbfree(ctrack->caption_head);\n\t\tctrack->caption_head = next;\n\t}\n\n\tif (avc || hevc || av1) {\n\t\tif (avc || hevc) {\n\t\t\tdata = bmalloc(sei_render_size(&sei));\n\t\t\tsize = sei_render(&sei, data);\n\t\t}\n\t\t/* In each of these specs there is an identical structure that\n\t\t * carries caption information. It is named slightly differently\n\t\t * in each one. The metadata_itut_t35 in AV1 or the\n\t\t * user_data_registered_itu_t_t35 in HEVC/AVC. We have an AVC\n\t\t * SEI wrapped version of that here. We will strip it out and\n\t\t * repackage it slightly to fit the different codec carrying\n\t\t * mechanisms. A slightly modified SEI for HEVC and a metadata\n\t\t * OBU for AV1. */\n\t\tif (avc) {\n\t\t\t/* TODO: SEI should come after AUD/SPS/PPS,\n\t\t\t * but before any VCL */\n\t\t\tda_push_back_array(out_data, nal_start, 4);\n\t\t\tda_push_back_array(out_data, data, size);\n#ifdef ENABLE_HEVC\n\t\t} else if (hevc) {\n\t\t\t/* Only first NAL (VPS/PPS/SPS) should use the 4 byte\n\t\t\t * start code. SEIs use 3 byte version */\n\t\t\tda_push_back_array(out_data, nal_start + 1, 3);\n\t\t\t/* nal_unit_header( ) {\n\t\t\t * forbidden_zero_bit       f(1)\n\t\t\t * nal_unit_type            u(6)\n\t\t\t * nuh_layer_id             u(6)\n\t\t\t * nuh_temporal_id_plus1    u(3)\n\t\t\t * }\n\t\t\t */\n\t\t\tconst uint8_t suffix_sei_nal_type = 40;\n\t\t\t/* The first bit is always 0, so we just need to\n\t\t\t * save the last bit off the original header and\n\t\t\t * add the SEI NAL type. */\n\t\t\tuint8_t first_byte = (suffix_sei_nal_type << 1) | (0x01 & hevc_nal_header[0]);\n\t\t\thevc_nal_header[0] = first_byte;\n\t\t\t/* The HEVC NAL unit header is 2 byte instead of\n\t\t\t * one, otherwise everything else is the\n\t\t\t * same. */\n\t\t\tda_push_back_array(out_data, hevc_nal_header, 2);\n\t\t\tda_push_back_array(out_data, &data[1], size - 1);\n#endif\n\t\t} else if (av1) {\n\t\t\tuint8_t *obu_buffer = NULL;\n\t\t\tsize_t obu_buffer_size = 0;\n\t\t\tsize = extract_buffer_from_sei(&sei, &data);\n\t\t\tmetadata_obu(data, size, &obu_buffer, &obu_buffer_size, METADATA_TYPE_ITUT_T35);\n\t\t\tif (obu_buffer) {\n\t\t\t\tda_push_back_array(out_data, obu_buffer, obu_buffer_size);\n\t\t\t\tbfree(obu_buffer);\n\t\t\t}\n\t\t}\n\t\tif (data) {\n\t\t\tbfree(data);\n\t\t}\n\t\tobs_encoder_packet_release(out);\n\n\t\t*out = backup;\n\t\tout->data = (uint8_t *)out_data.array + sizeof(ref);\n\t\tout->size = out_data.num - sizeof(ref);\n\t}\n\tsei_free(&sei);\n\treturn avc || hevc || av1;\n}\n\nstatic inline void send_interleaved(struct obs_output *output)\n{\n\tstruct encoder_packet out = output->interleaved_packets.array[0];\n\tstruct encoder_packet_time ept_local = {0};\n\tbool found_ept = false;\n\n\tda_erase(output->interleaved_packets, 0);\n\n\tif (out.type == OBS_ENCODER_VIDEO) {\n\t\toutput->total_frames++;\n\n\t\tpthread_mutex_lock(&output->caption_tracks[out.track_idx]->caption_mutex);\n\n\t\tdouble frame_timestamp = (out.pts * out.timebase_num) / (double)out.timebase_den;\n\n\t\tstruct caption_track_data *ctrack = output->caption_tracks[out.track_idx];\n\n\t\tif (ctrack->caption_head && ctrack->caption_timestamp <= frame_timestamp) {\n\t\t\tblog(LOG_DEBUG, \"Sending caption: %f \\\"%s\\\"\", frame_timestamp, &ctrack->caption_head->text[0]);\n\n\t\t\tdouble display_duration = ctrack->caption_head->display_duration;\n\n\t\t\tif (add_caption(output, &out)) {\n\t\t\t\tctrack->caption_timestamp = frame_timestamp + display_duration;\n\t\t\t}\n\t\t}\n\n\t\tif (ctrack->caption_data.size > 0) {\n\t\t\tif (ctrack->last_caption_timestamp < frame_timestamp) {\n\t\t\t\tctrack->last_caption_timestamp = frame_timestamp;\n\t\t\t\tadd_caption(output, &out);\n\t\t\t}\n\t\t}\n\t\tpthread_mutex_unlock(&ctrack->caption_mutex);\n\n\t\t/* Iterate the array of encoder packet times to\n\t\t * find a matching PTS entry, and drain the array.\n\t\t * Packet timing currently applies to video only.\n\t\t */\n\t\tstruct encoder_packet_time *ept = NULL;\n\t\tsize_t num_ept = output->encoder_packet_times[out.track_idx].num;\n\t\tif (num_ept) {\n\t\t\tfor (size_t i = 0; i < num_ept; i++) {\n\t\t\t\tept = &output->encoder_packet_times[out.track_idx].array[i];\n\t\t\t\tif (ept->pts == out.pts) {\n\t\t\t\t\tept_local = *ept;\n\t\t\t\t\tda_erase(output->encoder_packet_times[out.track_idx], i);\n\t\t\t\t\tfound_ept = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (found_ept == false) {\n\t\t\t\tblog(LOG_DEBUG, \"%s: Track %lu encoder packet timing for PTS%\" PRId64 \" not found.\",\n\t\t\t\t     __FUNCTION__, out.track_idx, out.pts);\n\t\t\t}\n\t\t} else {\n\t\t\t// encoder_packet_times should not be empty; log if so.\n\t\t\tblog(LOG_DEBUG, \"%s: Track %lu encoder packet timing array empty.\", __FUNCTION__,\n\t\t\t     out.track_idx);\n\t\t}\n\t}\n\n\t/* Iterate the registered packet callback(s) and invoke\n\t * each one. The caption track logic further above should\n\t * eventually migrate to the packet callback mechanism.\n\t */\n\tpthread_mutex_lock(&output->pkt_callbacks_mutex);\n\tfor (size_t i = 0; i < output->pkt_callbacks.num; ++i) {\n\t\tstruct packet_callback *const callback = &output->pkt_callbacks.array[i];\n\t\t// Packet interleave request timestamp\n\t\tept_local.pir = os_gettime_ns();\n\t\tcallback->packet_cb(output, &out, found_ept ? &ept_local : NULL, callback->param);\n\t}\n\tpthread_mutex_unlock(&output->pkt_callbacks_mutex);\n\n\toutput->info.encoded_packet(output->context.data, &out);\n\tobs_encoder_packet_release(&out);\n}\n\nstatic inline void set_higher_ts(struct obs_output *output, struct encoder_packet *packet)\n{\n\tif (packet->type == OBS_ENCODER_VIDEO) {\n\t\tif (output->highest_video_ts[packet->track_idx] < packet->dts_usec)\n\t\t\toutput->highest_video_ts[packet->track_idx] = packet->dts_usec;\n\t} else {\n\t\tif (output->highest_audio_ts < packet->dts_usec)\n\t\t\toutput->highest_audio_ts = packet->dts_usec;\n\t}\n}\n\nstatic inline struct encoder_packet *find_first_packet_type(struct obs_output *output, enum obs_encoder_type type,\n\t\t\t\t\t\t\t    size_t audio_idx);\nstatic int find_first_packet_type_idx(struct obs_output *output, enum obs_encoder_type type, size_t audio_idx);\n\n/* gets the point where audio and video are closest together */\nstatic size_t get_interleaved_start_idx(struct obs_output *output)\n{\n\tint64_t closest_diff = 0x7FFFFFFFFFFFFFFFLL;\n\tstruct encoder_packet *first_video = find_first_packet_type(output, OBS_ENCODER_VIDEO, 0);\n\tsize_t video_idx = DARRAY_INVALID;\n\tsize_t idx = 0;\n\n\tfor (size_t i = 0; i < output->interleaved_packets.num; i++) {\n\t\tstruct encoder_packet *packet = &output->interleaved_packets.array[i];\n\t\tint64_t diff;\n\n\t\tif (packet->type != OBS_ENCODER_AUDIO) {\n\t\t\tif (packet == first_video)\n\t\t\t\tvideo_idx = i;\n\t\t\tcontinue;\n\t\t}\n\n\t\tdiff = llabs(packet->dts_usec - first_video->dts_usec);\n\t\tif (diff < closest_diff) {\n\t\t\tclosest_diff = diff;\n\t\t\tidx = i;\n\t\t}\n\t}\n\n\tidx = video_idx < idx ? video_idx : idx;\n\n\t/* Early AAC/Opus audio packets will be for \"priming\" the encoder and contain silence, but they should not be\n\t * discarded. Set the idx to the first audio packet if closest PTS was <= 0. */\n\tsize_t first_audio_idx = idx;\n\twhile (output->interleaved_packets.array[first_audio_idx].type != OBS_ENCODER_AUDIO)\n\t\tfirst_audio_idx++;\n\n\tif (output->interleaved_packets.array[first_audio_idx].pts <= 0) {\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\t\tint audio_idx = find_first_packet_type_idx(output, OBS_ENCODER_AUDIO, i);\n\t\t\tif (audio_idx >= 0 && (size_t)audio_idx < idx)\n\t\t\t\tidx = audio_idx;\n\t\t}\n\t}\n\n\treturn idx;\n}\n\nstatic int64_t get_encoder_duration(struct obs_encoder *encoder)\n{\n\treturn (encoder->timebase_num * 1000000LL / encoder->timebase_den) * encoder->framesize;\n}\n\nstatic int prune_premature_packets(struct obs_output *output)\n{\n\tstruct encoder_packet *video;\n\tint video_idx;\n\tint max_idx;\n\tint64_t duration_usec, max_audio_duration_usec = 0;\n\tint64_t max_diff = 0;\n\tint64_t diff = 0;\n\tint audio_encoders = 0;\n\n\tvideo_idx = find_first_packet_type_idx(output, OBS_ENCODER_VIDEO, 0);\n\tif (video_idx == -1)\n\t\treturn -1;\n\n\tmax_idx = video_idx;\n\tvideo = &output->interleaved_packets.array[video_idx];\n\tduration_usec = video->timebase_num * 1000000LL / video->timebase_den;\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tstruct encoder_packet *audio;\n\t\tint audio_idx;\n\t\tint64_t audio_duration_usec = 0;\n\n\t\tif (!output->audio_encoders[i])\n\t\t\tcontinue;\n\t\taudio_encoders++;\n\n\t\taudio_idx = find_first_packet_type_idx(output, OBS_ENCODER_AUDIO, i);\n\t\tif (audio_idx == -1) {\n\t\t\toutput->received_audio = false;\n\t\t\treturn -1;\n\t\t}\n\n\t\taudio = &output->interleaved_packets.array[audio_idx];\n\t\tif (audio_idx > max_idx)\n\t\t\tmax_idx = audio_idx;\n\n\t\tdiff = audio->dts_usec - video->dts_usec;\n\t\tif (diff > max_diff)\n\t\t\tmax_diff = diff;\n\n\t\taudio_duration_usec = get_encoder_duration(output->audio_encoders[i]);\n\t\tif (audio_duration_usec > max_audio_duration_usec)\n\t\t\tmax_audio_duration_usec = audio_duration_usec;\n\t}\n\n\t/* Once multiple audio encoders are running they are almost always out\n\t * of phase by ~Xms. If users change their video to > 100fps then it\n\t * becomes probable that this phase difference will be larger than the\n\t * video duration preventing us from ever finding a synchronization\n\t * point due to their larger frame duration. Instead give up on a tight\n\t * video sync. */\n\tif (audio_encoders > 1 && duration_usec < max_audio_duration_usec) {\n\t\tduration_usec = max_audio_duration_usec;\n\t}\n\n\treturn diff > duration_usec ? max_idx + 1 : 0;\n}\n\n#define DEBUG_STARTING_PACKETS 0\n\nstatic void discard_to_idx(struct obs_output *output, size_t idx)\n{\n\tfor (size_t i = 0; i < idx; i++) {\n\t\tstruct encoder_packet *packet = &output->interleaved_packets.array[i];\n#if DEBUG_STARTING_PACKETS == 1\n\t\tblog(LOG_DEBUG, \"discarding %s packet, dts: %lld, pts: %lld\",\n\t\t     packet->type == OBS_ENCODER_VIDEO ? \"video\" : \"audio\", packet->dts, packet->pts);\n#endif\n\t\tif (packet->type == OBS_ENCODER_VIDEO) {\n\t\t\tda_pop_front(output->encoder_packet_times[packet->track_idx]);\n\t\t}\n\t\tobs_encoder_packet_release(packet);\n\t}\n\n\tda_erase_range(output->interleaved_packets, 0, idx);\n}\n\nstatic bool prune_interleaved_packets(struct obs_output *output)\n{\n\tsize_t start_idx = 0;\n\tint prune_start = prune_premature_packets(output);\n\n#if DEBUG_STARTING_PACKETS == 1\n\tblog(LOG_DEBUG, \"--------- Pruning! %d ---------\", prune_start);\n\tfor (size_t i = 0; i < output->interleaved_packets.num; i++) {\n\t\tstruct encoder_packet *packet = &output->interleaved_packets.array[i];\n\t\tblog(LOG_DEBUG, \"packet: %s %d, ts: %lld, pruned = %s\",\n\t\t     packet->type == OBS_ENCODER_AUDIO ? \"audio\" : \"video\", (int)packet->track_idx, packet->dts_usec,\n\t\t     (int)i < prune_start ? \"true\" : \"false\");\n\t}\n#endif\n\n\t/* prunes the first video packet if it's too far away from audio */\n\tif (prune_start == -1)\n\t\treturn false;\n\telse if (prune_start != 0)\n\t\tstart_idx = (size_t)prune_start;\n\telse\n\t\tstart_idx = get_interleaved_start_idx(output);\n\n\tif (start_idx)\n\t\tdiscard_to_idx(output, start_idx);\n\n\treturn true;\n}\n\nstatic int find_first_packet_type_idx(struct obs_output *output, enum obs_encoder_type type, size_t idx)\n{\n\tfor (size_t i = 0; i < output->interleaved_packets.num; i++) {\n\t\tstruct encoder_packet *packet = &output->interleaved_packets.array[i];\n\n\t\tif (packet->type == type && packet->track_idx == idx)\n\t\t\treturn (int)i;\n\t}\n\n\treturn -1;\n}\n\nstatic int find_last_packet_type_idx(struct obs_output *output, enum obs_encoder_type type, size_t idx)\n{\n\tfor (size_t i = output->interleaved_packets.num; i > 0; i--) {\n\t\tstruct encoder_packet *packet = &output->interleaved_packets.array[i - 1];\n\n\t\tif (packet->type == type && packet->track_idx == idx)\n\t\t\treturn (int)(i - 1);\n\t}\n\n\treturn -1;\n}\n\nstatic inline struct encoder_packet *find_first_packet_type(struct obs_output *output, enum obs_encoder_type type,\n\t\t\t\t\t\t\t    size_t audio_idx)\n{\n\tint idx = find_first_packet_type_idx(output, type, audio_idx);\n\treturn (idx != -1) ? &output->interleaved_packets.array[idx] : NULL;\n}\n\nstatic inline struct encoder_packet *find_last_packet_type(struct obs_output *output, enum obs_encoder_type type,\n\t\t\t\t\t\t\t   size_t audio_idx)\n{\n\tint idx = find_last_packet_type_idx(output, type, audio_idx);\n\treturn (idx != -1) ? &output->interleaved_packets.array[idx] : NULL;\n}\n\nstatic bool get_audio_and_video_packets(struct obs_output *output, struct encoder_packet **video,\n\t\t\t\t\tstruct encoder_packet **audio)\n{\n\tbool found_video = false;\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tif (output->video_encoders[i]) {\n\t\t\tvideo[i] = find_first_packet_type(output, OBS_ENCODER_VIDEO, i);\n\t\t\tif (!video[i]) {\n\t\t\t\toutput->received_video[i] = false;\n\t\t\t\treturn false;\n\t\t\t} else {\n\t\t\t\tfound_video = true;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tif (output->audio_encoders[i]) {\n\t\t\taudio[i] = find_first_packet_type(output, OBS_ENCODER_AUDIO, i);\n\t\t\tif (!audio[i]) {\n\t\t\t\toutput->received_audio = false;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn found_video;\n}\n\nstatic bool initialize_interleaved_packets(struct obs_output *output)\n{\n\tstruct encoder_packet *video[MAX_OUTPUT_VIDEO_ENCODERS] = {0};\n\tstruct encoder_packet *audio[MAX_OUTPUT_AUDIO_ENCODERS] = {0};\n\tstruct encoder_packet *last_audio[MAX_OUTPUT_AUDIO_ENCODERS] = {0};\n\tsize_t start_idx;\n\tsize_t first_audio_idx;\n\tsize_t first_video_idx;\n\n\tif (!get_first_audio_encoder_index(output, &first_audio_idx))\n\t\treturn false;\n\tif (!get_first_video_encoder_index(output, &first_video_idx))\n\t\treturn false;\n\n\tif (!get_audio_and_video_packets(output, video, audio))\n\t\treturn false;\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tif (output->audio_encoders[i]) {\n\t\t\tlast_audio[i] = find_last_packet_type(output, OBS_ENCODER_AUDIO, i);\n\t\t}\n\t}\n\n\t/* ensure that there is audio past the first video packet */\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tif (output->audio_encoders[i]) {\n\t\t\tif (last_audio[i]->dts_usec < video[first_video_idx]->dts_usec) {\n\t\t\t\toutput->received_audio = false;\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* clear out excess starting audio if it hasn't been already */\n\tstart_idx = get_interleaved_start_idx(output);\n\tif (start_idx) {\n\t\tdiscard_to_idx(output, start_idx);\n\t\tif (!get_audio_and_video_packets(output, video, audio))\n\t\t\treturn false;\n\t}\n\n\t/* get new offsets */\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tif (output->video_encoders[i]) {\n\t\t\toutput->video_offsets[i] = video[i]->pts;\n\t\t}\n\t}\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tif (output->audio_encoders[i] && audio[i]->dts > 0) {\n\t\t\toutput->audio_offsets[i] = audio[i]->dts;\n\t\t}\n\t}\n#if DEBUG_STARTING_PACKETS == 1\n\tint64_t v = video[first_video_idx]->dts_usec;\n\tint64_t a = audio[first_audio_idx]->dts_usec;\n\tint64_t diff = v - a;\n\n\tblog(LOG_DEBUG,\n\t     \"output '%s' offset for video: %lld, audio: %lld, \"\n\t     \"diff: %lldms\",\n\t     output->context.name, v, a, diff / 1000LL);\n#endif\n\n\t/* subtract offsets from highest TS offset variables */\n\toutput->highest_audio_ts -= audio[first_audio_idx]->dts_usec;\n\n\t/* apply new offsets to all existing packet DTS/PTS values */\n\tfor (size_t i = 0; i < output->interleaved_packets.num; i++) {\n\t\tstruct encoder_packet *packet = &output->interleaved_packets.array[i];\n\t\tapply_interleaved_packet_offset(output, packet, NULL);\n\t}\n\n\treturn true;\n}\n\nstatic inline void insert_interleaved_packet(struct obs_output *output, struct encoder_packet *out)\n{\n\tsize_t idx;\n\tfor (idx = 0; idx < output->interleaved_packets.num; idx++) {\n\t\tstruct encoder_packet *cur_packet;\n\t\tcur_packet = output->interleaved_packets.array + idx;\n\n\t\t// sort video packets with same DTS by track index,\n\t\t// to prevent the pruning logic from removing additional\n\t\t// video tracks\n\t\tif (out->dts_usec == cur_packet->dts_usec && out->type == OBS_ENCODER_VIDEO &&\n\t\t    cur_packet->type == OBS_ENCODER_VIDEO && out->track_idx > cur_packet->track_idx)\n\t\t\tcontinue;\n\n\t\tif (out->dts_usec == cur_packet->dts_usec && out->type == OBS_ENCODER_VIDEO) {\n\t\t\tbreak;\n\t\t} else if (out->dts_usec < cur_packet->dts_usec) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tda_insert(output->interleaved_packets, idx, out);\n}\n\nstatic void resort_interleaved_packets(struct obs_output *output)\n{\n\tDARRAY(struct encoder_packet) old_array;\n\n\told_array.da = output->interleaved_packets.da;\n\tmemset(&output->interleaved_packets, 0, sizeof(output->interleaved_packets));\n\n\tfor (size_t i = 0; i < old_array.num; i++) {\n\t\tset_higher_ts(output, &old_array.array[i]);\n\n\t\tinsert_interleaved_packet(output, &old_array.array[i]);\n\t}\n\n\tda_free(old_array);\n}\n\nstatic void discard_unused_audio_packets(struct obs_output *output, int64_t dts_usec)\n{\n\tsize_t idx = 0;\n\n\tfor (; idx < output->interleaved_packets.num; idx++) {\n\t\tstruct encoder_packet *p = &output->interleaved_packets.array[idx];\n\n\t\tif (p->dts_usec >= dts_usec)\n\t\t\tbreak;\n\t}\n\n\tif (idx)\n\t\tdiscard_to_idx(output, idx);\n}\n\nstatic bool purge_encoder_group_keyframe_data(obs_output_t *output, size_t idx)\n{\n\tstruct keyframe_group_data *data = &output->keyframe_group_tracking.array[idx];\n\tuint32_t modified_count = 0;\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tif (data->seen_on_track[i] != KEYFRAME_TRACK_STATUS_NOT_SEEN)\n\t\t\tmodified_count += 1;\n\t}\n\n\tif (modified_count == data->required_tracks) {\n\t\tda_erase(output->keyframe_group_tracking, idx);\n\t\treturn true;\n\t}\n\treturn false;\n}\n\n/* Check whether keyframes are emitted from all grouped encoders, and log\n * if keyframes haven't been emitted from all grouped encoders. */\nstatic void check_encoder_group_keyframe_alignment(obs_output_t *output, struct encoder_packet *packet)\n{\n\tsize_t idx = 0;\n\tstruct keyframe_group_data insert_data = {0};\n\n\tif (!packet->keyframe || packet->type != OBS_ENCODER_VIDEO || !packet->encoder->encoder_group)\n\t\treturn;\n\n\tfor (; idx < output->keyframe_group_tracking.num;) {\n\t\tstruct keyframe_group_data *data = &output->keyframe_group_tracking.array[idx];\n\t\tif (data->pts > packet->pts)\n\t\t\tbreak;\n\t\tif (data->group_id != (uintptr_t)packet->encoder->encoder_group) {\n\t\t\tidx += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (data->pts < packet->pts) {\n\t\t\tif (data->seen_on_track[packet->track_idx] == KEYFRAME_TRACK_STATUS_NOT_SEEN) {\n\t\t\t\tblog(LOG_WARNING,\n\t\t\t\t     \"obs-output '%s': Missing keyframe with pts %\" PRIi64\n\t\t\t\t     \" for encoder '%s' (track: %zu)\",\n\t\t\t\t     obs_output_get_name(output), data->pts, obs_encoder_get_name(packet->encoder),\n\t\t\t\t     packet->track_idx);\n\t\t\t}\n\n\t\t\tdata->seen_on_track[packet->track_idx] = KEYFRAME_TRACK_STATUS_SKIPPED;\n\n\t\t\tif (!purge_encoder_group_keyframe_data(output, idx))\n\t\t\t\tidx += 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tdata->seen_on_track[packet->track_idx] = KEYFRAME_TRACK_STATUS_SEEN;\n\t\tpurge_encoder_group_keyframe_data(output, idx);\n\t\treturn;\n\t}\n\n\tinsert_data.group_id = (uintptr_t)packet->encoder->encoder_group;\n\tinsert_data.pts = packet->pts;\n\tinsert_data.seen_on_track[packet->track_idx] = KEYFRAME_TRACK_STATUS_SEEN;\n\n\tpthread_mutex_lock(&packet->encoder->encoder_group->mutex);\n\tinsert_data.required_tracks = packet->encoder->encoder_group->num_encoders_started;\n\tpthread_mutex_unlock(&packet->encoder->encoder_group->mutex);\n\n\tda_insert(output->keyframe_group_tracking, idx, &insert_data);\n}\n\nstatic void apply_ept_offsets(struct obs_output *output)\n{\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tfor (size_t j = 0; j < output->encoder_packet_times[i].num; j++) {\n\t\t\toutput->encoder_packet_times[i].array[j].pts -= output->video_offsets[i];\n\t\t}\n\t}\n}\n\nstatic inline size_t count_streamable_frames(struct obs_output *output)\n{\n\tsize_t eligible = 0;\n\n\tfor (size_t idx = 0; idx < output->interleaved_packets.num; idx++) {\n\t\tstruct encoder_packet *pkt = &output->interleaved_packets.array[idx];\n\n\t\t/* Only count an interleaved packet as streamable if there are packets of the opposing type and of a\n\t\t * higher timestamp in the interleave buffer. This ensures that the timestamps are monotonic. */\n\t\tif (!has_higher_opposing_ts(output, pkt))\n\t\t\tbreak;\n\n\t\teligible++;\n\t}\n\n\treturn eligible;\n}\n\nstatic void interleave_packets(void *data, struct encoder_packet *packet, struct encoder_packet_time *packet_time)\n{\n\tstruct obs_output *output = data;\n\tstruct encoder_packet out;\n\tbool was_started;\n\tbool received_video;\n\tstruct encoder_packet_time *output_packet_time = NULL;\n\n\tif (!active(output))\n\t\treturn;\n\n\tpacket->track_idx = get_encoder_index(output, packet);\n\n\tpthread_mutex_lock(&output->interleaved_mutex);\n\n\t/* if first video frame is not a keyframe, discard until received */\n\tif (packet->type == OBS_ENCODER_VIDEO && !output->received_video[packet->track_idx] && !packet->keyframe) {\n\t\tdiscard_unused_audio_packets(output, packet->dts_usec);\n\t\tpthread_mutex_unlock(&output->interleaved_mutex);\n\n\t\tif (output->active_delay_ns)\n\t\t\tobs_encoder_packet_release(packet);\n\t\treturn;\n\t}\n\n\treceived_video = true;\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tif (output->video_encoders[i])\n\t\t\treceived_video = received_video && output->received_video[i];\n\t}\n\n\tcheck_encoder_group_keyframe_alignment(output, packet);\n\n\twas_started = output->received_audio && received_video;\n\n\tif (output->active_delay_ns)\n\t\tout = *packet;\n\telse\n\t\tobs_encoder_packet_create_instance(&out, packet);\n\n\tif (packet_time) {\n\t\toutput_packet_time = da_push_back_new(output->encoder_packet_times[packet->track_idx]);\n\t\t*output_packet_time = *packet_time;\n\t}\n\n\tif (was_started)\n\t\tapply_interleaved_packet_offset(output, &out, output_packet_time);\n\telse\n\t\tcheck_received(output, packet);\n\n\tinsert_interleaved_packet(output, &out);\n\n\treceived_video = true;\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tif (output->video_encoders[i])\n\t\t\treceived_video = received_video && output->received_video[i];\n\t}\n\n\t/* when both video and audio have been received, we're ready\n\t * to start sending out packets (one at a time) */\n\tif (output->received_audio && received_video) {\n\t\tif (!was_started) {\n\t\t\tif (prune_interleaved_packets(output)) {\n\t\t\t\tif (initialize_interleaved_packets(output)) {\n\t\t\t\t\tresort_interleaved_packets(output);\n\t\t\t\t\tapply_ept_offsets(output);\n\t\t\t\t\tsend_interleaved(output);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tset_higher_ts(output, &out);\n\n\t\t\tsize_t streamable = count_streamable_frames(output);\n\t\t\tif (streamable) {\n\t\t\t\tsend_interleaved(output);\n\n\t\t\t\t/* If we have more eligible packets queued than we normally should have,\n\t\t\t\t * send one additional packet until we're back below the limit. */\n\t\t\t\tif (--streamable > output->interleaver_max_batch_size)\n\t\t\t\t\tsend_interleaved(output);\n\t\t\t}\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&output->interleaved_mutex);\n}\n\nstatic void default_encoded_callback(void *param, struct encoder_packet *packet,\n\t\t\t\t     struct encoder_packet_time *packet_time)\n{\n\tUNUSED_PARAMETER(packet_time);\n\tstruct obs_output *output = param;\n\n\tif (data_active(output)) {\n\t\tpacket->track_idx = get_encoder_index(output, packet);\n\n\t\toutput->info.encoded_packet(output->context.data, packet);\n\n\t\tif (packet->type == OBS_ENCODER_VIDEO)\n\t\t\toutput->total_frames++;\n\t}\n\n\tif (output->active_delay_ns)\n\t\tobs_encoder_packet_release(packet);\n}\n\nstatic void default_raw_video_callback(void *param, struct video_data *frame)\n{\n\tstruct obs_output *output = param;\n\n\tif (video_pause_check(&output->pause, frame->timestamp))\n\t\treturn;\n\n\tif (data_active(output))\n\t\toutput->info.raw_video(output->context.data, frame);\n\toutput->total_frames++;\n}\n\nstatic bool prepare_audio(struct obs_output *output, const struct audio_data *old, struct audio_data *new)\n{\n\tif ((output->info.flags & OBS_OUTPUT_VIDEO) == 0) {\n\t\t*new = *old;\n\t\treturn true;\n\t}\n\n\tif (!output->video_start_ts) {\n\t\tpthread_mutex_lock(&output->pause.mutex);\n\t\toutput->video_start_ts = output->pause.last_video_ts;\n\t\tpthread_mutex_unlock(&output->pause.mutex);\n\t}\n\n\tif (!output->video_start_ts)\n\t\treturn false;\n\n\t/* ------------------ */\n\n\t*new = *old;\n\n\tif (old->timestamp < output->video_start_ts) {\n\t\tuint64_t duration = util_mul_div64(old->frames, 1000000000ULL, output->sample_rate);\n\t\tuint64_t end_ts = (old->timestamp + duration);\n\t\tuint64_t cutoff;\n\n\t\tif (end_ts <= output->video_start_ts)\n\t\t\treturn false;\n\n\t\tcutoff = output->video_start_ts - old->timestamp;\n\t\tnew->timestamp += cutoff;\n\n\t\tcutoff = util_mul_div64(cutoff, output->sample_rate, 1000000000ULL);\n\n\t\tfor (size_t i = 0; i < output->planes; i++)\n\t\t\tnew->data[i] += output->audio_size *(uint32_t)cutoff;\n\t\tnew->frames -= (uint32_t)cutoff;\n\t}\n\n\treturn true;\n}\n\nstatic void default_raw_audio_callback(void *param, size_t mix_idx, struct audio_data *in)\n{\n\tstruct obs_output *output = param;\n\tstruct audio_data out;\n\tsize_t frame_size_bytes;\n\n\tif (!data_active(output))\n\t\treturn;\n\n\t/* -------------- */\n\n\tif (!prepare_audio(output, in, &out))\n\t\treturn;\n\tif (audio_pause_check(&output->pause, &out, output->sample_rate))\n\t\treturn;\n\tif (!output->audio_start_ts) {\n\t\toutput->audio_start_ts = out.timestamp;\n\t}\n\n\tframe_size_bytes = AUDIO_OUTPUT_FRAMES * output->audio_size;\n\n\tfor (size_t i = 0; i < output->planes; i++)\n\t\tdeque_push_back(&output->audio_buffer[mix_idx][i], out.data[i], out.frames * output->audio_size);\n\n\t/* -------------- */\n\n\twhile (output->audio_buffer[mix_idx][0].size > frame_size_bytes) {\n\t\tfor (size_t i = 0; i < output->planes; i++) {\n\t\t\tdeque_pop_front(&output->audio_buffer[mix_idx][i], output->audio_data[i], frame_size_bytes);\n\t\t\tout.data[i] = (uint8_t *)output->audio_data[i];\n\t\t}\n\n\t\tout.frames = AUDIO_OUTPUT_FRAMES;\n\t\tout.timestamp =\n\t\t\toutput->audio_start_ts + audio_frames_to_ns(output->sample_rate, output->total_audio_frames);\n\n\t\tpthread_mutex_lock(&output->pause.mutex);\n\t\tout.timestamp += output->pause.ts_offset;\n\t\tpthread_mutex_unlock(&output->pause.mutex);\n\n\t\toutput->total_audio_frames += AUDIO_OUTPUT_FRAMES;\n\n\t\tif (output->info.raw_audio2)\n\t\t\toutput->info.raw_audio2(output->context.data, mix_idx, &out);\n\t\telse\n\t\t\toutput->info.raw_audio(output->context.data, &out);\n\t}\n}\n\nstatic inline void start_audio_encoders(struct obs_output *output, encoded_callback_t encoded_callback)\n{\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tif (output->audio_encoders[i]) {\n\t\t\tobs_encoder_start(output->audio_encoders[i], encoded_callback, output);\n\t\t}\n\t}\n}\n\nstatic inline void start_video_encoders(struct obs_output *output, encoded_callback_t encoded_callback)\n{\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tif (output->video_encoders[i]) {\n\t\t\tobs_encoder_start(output->video_encoders[i], encoded_callback, output);\n\t\t}\n\t}\n}\n\nstatic inline void start_raw_audio(obs_output_t *output)\n{\n\tif (output->info.raw_audio2) {\n\t\tfor (int idx = 0; idx < MAX_AUDIO_MIXES; idx++) {\n\t\t\tif ((output->mixer_mask & ((size_t)1 << idx)) != 0) {\n\t\t\t\taudio_output_connect(output->audio, idx, get_audio_conversion(output),\n\t\t\t\t\t\t     default_raw_audio_callback, output);\n\t\t\t}\n\t\t}\n\t} else {\n\t\taudio_output_connect(output->audio, get_first_mixer(output), get_audio_conversion(output),\n\t\t\t\t     default_raw_audio_callback, output);\n\t}\n}\n\nstatic void reset_packet_data(obs_output_t *output)\n{\n\toutput->received_audio = false;\n\toutput->highest_audio_ts = 0;\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\toutput->encoder_packet_times[i].num = 0;\n\t}\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\toutput->received_video[i] = false;\n\t\toutput->video_offsets[i] = 0;\n\t\toutput->highest_video_ts[i] = INT64_MIN;\n\t}\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++)\n\t\toutput->audio_offsets[i] = 0;\n\n\tfree_packets(output);\n}\n\nstatic inline bool preserve_active(struct obs_output *output)\n{\n\treturn (output->delay_flags & OBS_OUTPUT_DELAY_PRESERVE) != 0;\n}\n\nstatic void hook_data_capture(struct obs_output *output)\n{\n\tencoded_callback_t encoded_callback;\n\tbool has_video = flag_video(output);\n\tbool has_audio = flag_audio(output);\n\n\tif (flag_encoded(output)) {\n\t\tpthread_mutex_lock(&output->interleaved_mutex);\n\t\treset_packet_data(output);\n\t\tpthread_mutex_unlock(&output->interleaved_mutex);\n\n\t\tencoded_callback = (has_video && has_audio) ? interleave_packets : default_encoded_callback;\n\n\t\tif (output->delay_sec) {\n\t\t\toutput->active_delay_ns = (uint64_t)output->delay_sec * 1000000000ULL;\n\t\t\toutput->delay_cur_flags = output->delay_flags;\n\t\t\toutput->delay_callback = encoded_callback;\n\t\t\tencoded_callback = process_delay;\n\t\t\tos_atomic_set_bool(&output->delay_active, true);\n\n\t\t\tblog(LOG_INFO,\n\t\t\t     \"Output '%s': %\" PRIu32 \" second delay \"\n\t\t\t     \"active, preserve on disconnect is %s\",\n\t\t\t     output->context.name, output->delay_sec, preserve_active(output) ? \"on\" : \"off\");\n\t\t}\n\n\t\tif (has_audio)\n\t\t\tstart_audio_encoders(output, encoded_callback);\n\t\tif (has_video)\n\t\t\tstart_video_encoders(output, encoded_callback);\n\t} else {\n\t\tif (has_video)\n\t\t\tstart_raw_video(output->video, obs_output_get_video_conversion(output), 1,\n\t\t\t\t\tdefault_raw_video_callback, output);\n\t\tif (has_audio)\n\t\t\tstart_raw_audio(output);\n\t}\n}\n\nstatic inline void signal_start(struct obs_output *output)\n{\n\tdo_output_signal(output, \"start\");\n}\n\nstatic inline void signal_reconnect(struct obs_output *output)\n{\n\tstruct calldata params;\n\tuint8_t stack[128];\n\n\tcalldata_init_fixed(&params, stack, sizeof(stack));\n\tcalldata_set_int(&params, \"timeout_sec\", output->reconnect_retry_cur_msec / 1000);\n\tcalldata_set_ptr(&params, \"output\", output);\n\tsignal_handler_signal(output->context.signals, \"reconnect\", &params);\n}\n\nstatic inline void signal_reconnect_success(struct obs_output *output)\n{\n\tdo_output_signal(output, \"reconnect_success\");\n}\n\nstatic inline void signal_stop(struct obs_output *output)\n{\n\tstruct calldata params;\n\n\tcalldata_init(&params);\n\tcalldata_set_string(&params, \"last_error\", obs_output_get_last_error(output));\n\tcalldata_set_int(&params, \"code\", output->stop_code);\n\tcalldata_set_ptr(&params, \"output\", output);\n\n\tsignal_handler_signal(output->context.signals, \"stop\", &params);\n\n\tcalldata_free(&params);\n}\n\nbool obs_output_can_begin_data_capture(const obs_output_t *output, uint32_t flags)\n{\n\tUNUSED_PARAMETER(flags);\n\n\tif (!obs_output_valid(output, \"obs_output_can_begin_data_capture\"))\n\t\treturn false;\n\n\tif (delay_active(output))\n\t\treturn true;\n\tif (active(output))\n\t\treturn false;\n\n\tif (data_capture_ending(output))\n\t\tpthread_join(output->end_data_capture_thread, NULL);\n\n\treturn can_begin_data_capture(output);\n}\n\nstatic inline bool initialize_audio_encoders(obs_output_t *output)\n{\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tobs_encoder_t *audio = output->audio_encoders[i];\n\n\t\tif (audio && !obs_encoder_initialize(audio)) {\n\t\t\tobs_output_set_last_error(output, obs_encoder_get_last_error(audio));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic inline bool initialize_video_encoders(obs_output_t *output)\n{\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tobs_encoder_t *video = output->video_encoders[i];\n\n\t\tif (video && !obs_encoder_initialize(video)) {\n\t\t\tobs_output_set_last_error(output, obs_encoder_get_last_error(video));\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\nstatic inline void pair_encoders(obs_output_t *output)\n{\n\tsize_t first_venc_idx;\n\tif (!get_first_video_encoder_index(output, &first_venc_idx))\n\t\treturn;\n\tstruct obs_encoder *video = output->video_encoders[first_venc_idx];\n\n\tpthread_mutex_lock(&video->init_mutex);\n\tif (video->active) {\n\t\tpthread_mutex_unlock(&video->init_mutex);\n\t\treturn;\n\t}\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tstruct obs_encoder *audio = output->audio_encoders[i];\n\t\tif (!audio)\n\t\t\tcontinue;\n\n\t\tpthread_mutex_lock(&audio->init_mutex);\n\t\tif (!audio->active && !audio->paired_encoders.num) {\n\t\t\tobs_weak_encoder_t *weak_audio = obs_encoder_get_weak_encoder(audio);\n\t\t\tobs_weak_encoder_t *weak_video = obs_encoder_get_weak_encoder(video);\n\t\t\tda_push_back(video->paired_encoders, &weak_audio);\n\t\t\tda_push_back(audio->paired_encoders, &weak_video);\n\t\t}\n\t\tpthread_mutex_unlock(&audio->init_mutex);\n\t}\n\tpthread_mutex_unlock(&video->init_mutex);\n}\n\nbool obs_output_initialize_encoders(obs_output_t *output, uint32_t flags)\n{\n\tUNUSED_PARAMETER(flags);\n\n\tif (!obs_output_valid(output, \"obs_output_initialize_encoders\"))\n\t\treturn false;\n\tif (!log_flag_encoded(output, __FUNCTION__, false))\n\t\treturn false;\n\tif (active(output))\n\t\treturn delay_active(output);\n\n\tif (flag_video(output) && !initialize_video_encoders(output))\n\t\treturn false;\n\tif (flag_audio(output) && !initialize_audio_encoders(output))\n\t\treturn false;\n\n\treturn true;\n}\n\nstatic bool begin_delayed_capture(obs_output_t *output)\n{\n\tif (delay_capturing(output))\n\t\treturn false;\n\n\tpthread_mutex_lock(&output->interleaved_mutex);\n\treset_packet_data(output);\n\tos_atomic_set_bool(&output->delay_capturing, true);\n\tpthread_mutex_unlock(&output->interleaved_mutex);\n\n\tif (reconnecting(output)) {\n\t\tsignal_reconnect_success(output);\n\t\tos_atomic_set_bool(&output->reconnecting, false);\n\t} else {\n\t\tsignal_start(output);\n\t}\n\n\treturn true;\n}\n\nstatic void reset_raw_output(obs_output_t *output)\n{\n\tclear_raw_audio_buffers(output);\n\n\tif (output->audio) {\n\t\tconst struct audio_output_info *aoi = audio_output_get_info(output->audio);\n\t\tstruct audio_convert_info conv = output->audio_conversion;\n\t\tstruct audio_convert_info info = {\n\t\t\taoi->samples_per_sec,\n\t\t\taoi->format,\n\t\t\taoi->speakers,\n\t\t};\n\n\t\tif (output->audio_conversion_set) {\n\t\t\tif (conv.samples_per_sec)\n\t\t\t\tinfo.samples_per_sec = conv.samples_per_sec;\n\t\t\tif (conv.format != AUDIO_FORMAT_UNKNOWN)\n\t\t\t\tinfo.format = conv.format;\n\t\t\tif (conv.speakers != SPEAKERS_UNKNOWN)\n\t\t\t\tinfo.speakers = conv.speakers;\n\t\t}\n\n\t\toutput->sample_rate = info.samples_per_sec;\n\t\toutput->planes = get_audio_planes(info.format, info.speakers);\n\t\toutput->total_audio_frames = 0;\n\t\toutput->audio_size = get_audio_size(info.format, info.speakers, 1);\n\t}\n\n\toutput->audio_start_ts = 0;\n\toutput->video_start_ts = 0;\n\n\tpause_reset(&output->pause);\n}\n\nstatic void calculate_batch_size(struct obs_output *output)\n{\n\tstruct obs_video_info ovi;\n\tobs_get_video_info(&ovi);\n\tDARRAY(uint64_t) intervals;\n\tda_init(intervals);\n\n\tuint64_t largest_interval = 0;\n\n\t/* Step 1: Calculate the largest interval between packets of any encoder. */\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tif (!output->video_encoders[i])\n\t\t\tcontinue;\n\n\t\tuint32_t den = ovi.fps_den * obs_encoder_get_frame_rate_divisor(output->video_encoders[i]);\n\t\tuint64_t encoder_interval = util_mul_div64(1000000000ULL, den, ovi.fps_num);\n\t\tda_push_back(intervals, &encoder_interval);\n\n\t\tlargest_interval = encoder_interval > largest_interval ? encoder_interval : largest_interval;\n\t}\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tif (!output->audio_encoders[i])\n\t\t\tcontinue;\n\n\t\tuint32_t sample_rate = obs_encoder_get_sample_rate(output->audio_encoders[i]);\n\t\tsize_t frame_size = obs_encoder_get_frame_size(output->audio_encoders[i]);\n\t\tuint64_t encoder_interval = util_mul_div64(1000000000ULL, frame_size, sample_rate);\n\t\tda_push_back(intervals, &encoder_interval);\n\n\t\tlargest_interval = encoder_interval > largest_interval ? encoder_interval : largest_interval;\n\t}\n\n\t/* Step 2: Calculate how many packets would fit into double that interval given each encoder's packet rate.\n\t * The doubling is done to provide some amount of wiggle room as the largest interval may not be evenly\n\t * divisible by all smaller ones. For example, 33.3... ms video (30 FPS) and 21.3... ms audio (48 kHz AAC). */\n\tfor (size_t i = 0; i < intervals.num; i++) {\n\t\tuint64_t num = (largest_interval * 2) / intervals.array[i];\n\t\toutput->interleaver_max_batch_size += num;\n\t}\n\n\tblog(LOG_DEBUG, \"Maximum interleaver batch size for '%s' calculated to be %zu packets\",\n\t     obs_output_get_name(output), output->interleaver_max_batch_size);\n\n\tda_free(intervals);\n}\n\nbool obs_output_begin_data_capture(obs_output_t *output, uint32_t flags)\n{\n\tUNUSED_PARAMETER(flags);\n\n\tif (!obs_output_valid(output, \"obs_output_begin_data_capture\"))\n\t\treturn false;\n\n\tif (delay_active(output))\n\t\treturn begin_delayed_capture(output);\n\tif (active(output))\n\t\treturn false;\n\n\toutput->total_frames = 0;\n\n\tif (!flag_encoded(output))\n\t\treset_raw_output(output);\n\n\tif (!can_begin_data_capture(output))\n\t\treturn false;\n\n\tif (flag_video(output) && flag_audio(output))\n\t\tpair_encoders(output);\n\n\tos_atomic_set_bool(&output->data_active, true);\n\thook_data_capture(output);\n\n\tcalculate_batch_size(output);\n\n\tif (flag_service(output))\n\t\tobs_service_activate(output->service);\n\n\tdo_output_signal(output, \"activate\");\n\tos_atomic_set_bool(&output->active, true);\n\n\tif (reconnecting(output)) {\n\t\tsignal_reconnect_success(output);\n\t\tos_atomic_set_bool(&output->reconnecting, false);\n\n\t} else if (delay_active(output)) {\n\t\tdo_output_signal(output, \"starting\");\n\n\t} else {\n\t\tsignal_start(output);\n\t}\n\n\treturn true;\n}\n\nstatic inline void stop_audio_encoders(obs_output_t *output, encoded_callback_t encoded_callback)\n{\n\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\tobs_encoder_t *audio = output->audio_encoders[i];\n\t\tif (audio)\n\t\t\tobs_encoder_stop(audio, encoded_callback, output);\n\t}\n}\n\nstatic inline void stop_video_encoders(obs_output_t *output, encoded_callback_t encoded_callback)\n{\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tobs_encoder_t *video = output->video_encoders[i];\n\t\tif (video)\n\t\t\tobs_encoder_stop(video, encoded_callback, output);\n\t}\n}\n\nstatic inline void stop_raw_audio(obs_output_t *output)\n{\n\tif (output->info.raw_audio2) {\n\t\tfor (int idx = 0; idx < MAX_AUDIO_MIXES; idx++) {\n\t\t\tif ((output->mixer_mask & ((size_t)1 << idx)) != 0) {\n\t\t\t\taudio_output_disconnect(output->audio, idx, default_raw_audio_callback, output);\n\t\t\t}\n\t\t}\n\t} else {\n\t\taudio_output_disconnect(output->audio, get_first_mixer(output), default_raw_audio_callback, output);\n\t}\n}\n\nstatic void *end_data_capture_thread(void *data)\n{\n\tencoded_callback_t encoded_callback;\n\tobs_output_t *output = data;\n\tbool has_video = flag_video(output);\n\tbool has_audio = flag_audio(output);\n\n\tif (flag_encoded(output)) {\n\t\tif (output->active_delay_ns)\n\t\t\tencoded_callback = process_delay;\n\t\telse\n\t\t\tencoded_callback = (has_video && has_audio) ? interleave_packets : default_encoded_callback;\n\n\t\tif (has_video)\n\t\t\tstop_video_encoders(output, encoded_callback);\n\t\tif (has_audio)\n\t\t\tstop_audio_encoders(output, encoded_callback);\n\t} else {\n\t\tif (has_video)\n\t\t\tstop_raw_video(output->video, default_raw_video_callback, output);\n\t\tif (has_audio)\n\t\t\tstop_raw_audio(output);\n\t}\n\n\tif (flag_service(output))\n\t\tobs_service_deactivate(output->service, false);\n\n\tif (output->active_delay_ns)\n\t\tobs_output_cleanup_delay(output);\n\n\tdo_output_signal(output, \"deactivate\");\n\tos_atomic_set_bool(&output->active, false);\n\tos_event_signal(output->stopping_event);\n\tos_atomic_set_bool(&output->end_data_capture_thread_active, false);\n\n\treturn NULL;\n}\n\nstatic void obs_output_end_data_capture_internal(obs_output_t *output, bool signal)\n{\n\tint ret;\n\n\tif (!obs_output_valid(output, \"obs_output_end_data_capture\"))\n\t\treturn;\n\n\tif (!active(output) || !data_active(output)) {\n\t\tif (signal) {\n\t\t\tsignal_stop(output);\n\t\t\toutput->stop_code = OBS_OUTPUT_SUCCESS;\n\t\t\tos_event_signal(output->stopping_event);\n\t\t}\n\t\treturn;\n\t}\n\n\tif (delay_active(output)) {\n\t\tos_atomic_set_bool(&output->delay_capturing, false);\n\n\t\tif (!os_atomic_load_long(&output->delay_restart_refs)) {\n\t\t\tos_atomic_set_bool(&output->delay_active, false);\n\t\t} else {\n\t\t\tos_event_signal(output->stopping_event);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tos_atomic_set_bool(&output->data_active, false);\n\n\tif (flag_video(output))\n\t\tlog_frame_info(output);\n\n\tif (data_capture_ending(output))\n\t\tpthread_join(output->end_data_capture_thread, NULL);\n\n\tos_atomic_set_bool(&output->end_data_capture_thread_active, true);\n\tret = pthread_create(&output->end_data_capture_thread, NULL, end_data_capture_thread, output);\n\tif (ret != 0) {\n\t\tblog(LOG_WARNING,\n\t\t     \"Failed to create end_data_capture_thread \"\n\t\t     \"for output '%s'!\",\n\t\t     output->context.name);\n\t\tend_data_capture_thread(output);\n\t}\n\n\tif (signal) {\n\t\tsignal_stop(output);\n\t\toutput->stop_code = OBS_OUTPUT_SUCCESS;\n\t}\n}\n\nvoid obs_output_end_data_capture(obs_output_t *output)\n{\n\tobs_output_end_data_capture_internal(output, true);\n}\n\nstatic void *reconnect_thread(void *param)\n{\n\tstruct obs_output *output = param;\n\n\toutput->reconnect_thread_active = true;\n\n\tif (os_event_timedwait(output->reconnect_stop_event, output->reconnect_retry_cur_msec) == ETIMEDOUT)\n\t\tobs_output_actual_start(output);\n\n\tif (os_event_try(output->reconnect_stop_event) == EAGAIN)\n\t\tpthread_detach(output->reconnect_thread);\n\telse\n\t\tos_atomic_set_bool(&output->reconnecting, false);\n\n\toutput->reconnect_thread_active = false;\n\treturn NULL;\n}\n\nstatic void output_reconnect(struct obs_output *output)\n{\n\tint ret;\n\n\tif (reconnecting(output) && os_event_try(output->reconnect_stop_event) != EAGAIN) {\n\t\tos_atomic_set_bool(&output->reconnecting, false);\n\t\treturn;\n\t}\n\n\tif (!reconnecting(output)) {\n\t\toutput->reconnect_retry_cur_msec = output->reconnect_retry_sec * 1000;\n\t\toutput->reconnect_retries = 0;\n\t}\n\n\tif (output->reconnect_retries >= output->reconnect_retry_max) {\n\t\toutput->stop_code = OBS_OUTPUT_DISCONNECTED;\n\t\tos_atomic_set_bool(&output->reconnecting, false);\n\t\tif (delay_active(output))\n\t\t\tos_atomic_set_bool(&output->delay_active, false);\n\t\tobs_output_end_data_capture(output);\n\t\treturn;\n\t}\n\n\tif (!reconnecting(output)) {\n\t\tos_atomic_set_bool(&output->reconnecting, true);\n\t\tos_event_reset(output->reconnect_stop_event);\n\t}\n\n\tif (output->reconnect_retries) {\n\t\toutput->reconnect_retry_cur_msec =\n\t\t\t(uint32_t)(output->reconnect_retry_cur_msec * output->reconnect_retry_exp);\n\t\tif (output->reconnect_retry_cur_msec > RECONNECT_RETRY_MAX_MSEC) {\n\t\t\toutput->reconnect_retry_cur_msec = RECONNECT_RETRY_MAX_MSEC;\n\t\t}\n\t}\n\n\toutput->reconnect_retries++;\n\n\toutput->stop_code = OBS_OUTPUT_DISCONNECTED;\n\tret = pthread_create(&output->reconnect_thread, NULL, &reconnect_thread, output);\n\tif (ret < 0) {\n\t\tblog(LOG_WARNING, \"Failed to create reconnect thread\");\n\t\tos_atomic_set_bool(&output->reconnecting, false);\n\t} else {\n\t\tblog(LOG_INFO, \"Output '%s': Reconnecting in %.02f seconds..\", output->context.name,\n\t\t     (float)(output->reconnect_retry_cur_msec / 1000.0));\n\n\t\tsignal_reconnect(output);\n\t}\n}\n\nstatic inline bool check_reconnect_cb(obs_output_t *output, int code)\n{\n\tif (!output->reconnect_callback.reconnect_cb)\n\t\treturn true;\n\n\treturn output->reconnect_callback.reconnect_cb(output->reconnect_callback.param, output, code);\n}\n\nstatic inline bool can_reconnect(obs_output_t *output, int code)\n{\n\tbool reconnect_active = output->reconnect_retry_max != 0;\n\n\tif (reconnect_active && !check_reconnect_cb(output, code))\n\t\treturn false;\n\n\treturn (reconnecting(output) && code != OBS_OUTPUT_SUCCESS) ||\n\t       (reconnect_active && code == OBS_OUTPUT_DISCONNECTED);\n}\n\nvoid obs_output_signal_stop(obs_output_t *output, int code)\n{\n\tif (!obs_output_valid(output, \"obs_output_signal_stop\"))\n\t\treturn;\n\n\toutput->stop_code = code;\n\n\tif (can_reconnect(output, code)) {\n\t\tif (delay_active(output))\n\t\t\tos_atomic_inc_long(&output->delay_restart_refs);\n\t\tobs_output_end_data_capture_internal(output, false);\n\t\toutput_reconnect(output);\n\t} else {\n\t\tif (delay_active(output))\n\t\t\tos_atomic_set_bool(&output->delay_active, false);\n\t\tif (reconnecting(output))\n\t\t\tos_atomic_set_bool(&output->reconnecting, false);\n\t\tobs_output_end_data_capture(output);\n\t}\n}\n\nvoid obs_output_release(obs_output_t *output)\n{\n\tif (!output)\n\t\treturn;\n\n\tobs_weak_output_t *control = get_weak(output);\n\tif (obs_ref_release(&control->ref)) {\n\t\t// The order of operations is important here since\n\t\t// get_context_by_name in obs.c relies on weak refs\n\t\t// being alive while the context is listed\n\t\tobs_output_destroy(output);\n\t\tobs_weak_output_release(control);\n\t}\n}\n\nvoid obs_weak_output_addref(obs_weak_output_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tobs_weak_ref_addref(&weak->ref);\n}\n\nvoid obs_weak_output_release(obs_weak_output_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tif (obs_weak_ref_release(&weak->ref))\n\t\tbfree(weak);\n}\n\nobs_output_t *obs_output_get_ref(obs_output_t *output)\n{\n\tif (!output)\n\t\treturn NULL;\n\n\treturn obs_weak_output_get_output(get_weak(output));\n}\n\nobs_weak_output_t *obs_output_get_weak_output(obs_output_t *output)\n{\n\tif (!output)\n\t\treturn NULL;\n\n\tobs_weak_output_t *weak = get_weak(output);\n\tobs_weak_output_addref(weak);\n\treturn weak;\n}\n\nobs_output_t *obs_weak_output_get_output(obs_weak_output_t *weak)\n{\n\tif (!weak)\n\t\treturn NULL;\n\n\tif (obs_weak_ref_get_ref(&weak->ref))\n\t\treturn weak->output;\n\n\treturn NULL;\n}\n\nbool obs_weak_output_references_output(obs_weak_output_t *weak, obs_output_t *output)\n{\n\treturn weak && output && weak->output == output;\n}\n\nvoid *obs_output_get_type_data(obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_get_type_data\") ? output->info.type_data : NULL;\n}\n\nconst char *obs_output_get_id(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, \"obs_output_get_id\") ? output->info.id : NULL;\n}\n\nvoid obs_output_caption(obs_output_t *output, const struct obs_source_cea_708 *captions)\n{\n\tfor (int i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tstruct caption_track_data *ctrack = output->caption_tracks[i];\n\t\tif (!ctrack) {\n\t\t\tcontinue;\n\t\t}\n\t\tpthread_mutex_lock(&ctrack->caption_mutex);\n\t\tfor (size_t i = 0; i < captions->packets; i++) {\n\t\t\tdeque_push_back(&ctrack->caption_data, captions->data + (i * 3), 3 * sizeof(uint8_t));\n\t\t}\n\t\tpthread_mutex_unlock(&ctrack->caption_mutex);\n\t}\n}\n\nstatic struct caption_text *caption_text_new(const char *text, size_t bytes, struct caption_text *tail,\n\t\t\t\t\t     struct caption_text **head, double display_duration)\n{\n\tstruct caption_text *next = bzalloc(sizeof(struct caption_text));\n\tsnprintf(&next->text[0], CAPTION_LINE_BYTES + 1, \"%.*s\", (int)bytes, text);\n\tnext->display_duration = display_duration;\n\n\tif (!*head) {\n\t\t*head = next;\n\t} else {\n\t\ttail->next = next;\n\t}\n\n\treturn next;\n}\n\nvoid obs_output_output_caption_text1(obs_output_t *output, const char *text)\n{\n\tif (!obs_output_valid(output, \"obs_output_output_caption_text1\"))\n\t\treturn;\n\tobs_output_output_caption_text2(output, text, 2.0f);\n}\n\nvoid obs_output_output_caption_text2(obs_output_t *output, const char *text, double display_duration)\n{\n\tif (!obs_output_valid(output, \"obs_output_output_caption_text2\"))\n\t\treturn;\n\tif (!active(output))\n\t\treturn;\n\n\t// split text into 32 character strings\n\tint size = (int)strlen(text);\n\tblog(LOG_DEBUG, \"Caption text: %s\", text);\n\n\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\tstruct caption_track_data *ctrack = output->caption_tracks[i];\n\t\tif (!ctrack) {\n\t\t\tcontinue;\n\t\t}\n\t\tpthread_mutex_lock(&ctrack->caption_mutex);\n\n\t\tctrack->caption_tail =\n\t\t\tcaption_text_new(text, size, ctrack->caption_tail, &ctrack->caption_head, display_duration);\n\n\t\tpthread_mutex_unlock(&ctrack->caption_mutex);\n\t}\n}\n\nfloat obs_output_get_congestion(obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_congestion\"))\n\t\treturn 0;\n\n\tif (output->info.get_congestion) {\n\t\tfloat val = output->info.get_congestion(output->context.data);\n\t\tif (val < 0.0f)\n\t\t\tval = 0.0f;\n\t\telse if (val > 1.0f)\n\t\t\tval = 1.0f;\n\t\treturn val;\n\t}\n\treturn 0;\n}\n\nint obs_output_get_connect_time_ms(obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_connect_time_ms\"))\n\t\treturn -1;\n\n\tif (output->info.get_connect_time_ms)\n\t\treturn output->info.get_connect_time_ms(output->context.data);\n\treturn -1;\n}\n\nconst char *obs_output_get_last_error(obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_last_error\"))\n\t\treturn NULL;\n\n\tif (output->last_error_message) {\n\t\treturn output->last_error_message;\n\t} else {\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {\n\t\t\tobs_encoder_t *vencoder = output->video_encoders[i];\n\t\t\tif (vencoder && vencoder->last_error_message) {\n\t\t\t\treturn vencoder->last_error_message;\n\t\t\t}\n\t\t}\n\n\t\tfor (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {\n\t\t\tobs_encoder_t *aencoder = output->audio_encoders[i];\n\t\t\tif (aencoder && aencoder->last_error_message) {\n\t\t\t\treturn aencoder->last_error_message;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn NULL;\n}\n\nvoid obs_output_set_last_error(obs_output_t *output, const char *message)\n{\n\tif (!obs_output_valid(output, \"obs_output_set_last_error\"))\n\t\treturn;\n\n\tif (output->last_error_message)\n\t\tbfree(output->last_error_message);\n\n\tif (message)\n\t\toutput->last_error_message = bstrdup(message);\n\telse\n\t\toutput->last_error_message = NULL;\n}\n\nbool obs_output_reconnecting(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_reconnecting\"))\n\t\treturn false;\n\n\treturn reconnecting(output);\n}\n\nconst char *obs_output_get_supported_video_codecs(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, __FUNCTION__) ? output->info.encoded_video_codecs : NULL;\n}\n\nconst char *obs_output_get_supported_audio_codecs(const obs_output_t *output)\n{\n\treturn obs_output_valid(output, __FUNCTION__) ? output->info.encoded_audio_codecs : NULL;\n}\n\nconst char *obs_output_get_protocols(const obs_output_t *output)\n{\n\tif (!obs_output_valid(output, \"obs_output_get_protocols\"))\n\t\treturn NULL;\n\n\treturn flag_service(output) ? output->info.protocols : NULL;\n}\n\nvoid obs_enum_output_types_with_protocol(const char *protocol, void *data, bool (*enum_cb)(void *data, const char *id))\n{\n\tif (!obs_is_output_protocol_registered(protocol))\n\t\treturn;\n\n\tsize_t protocol_len = strlen(protocol);\n\tfor (size_t i = 0; i < obs->output_types.num; i++) {\n\t\tif (!(obs->output_types.array[i].flags & OBS_OUTPUT_SERVICE))\n\t\t\tcontinue;\n\n\t\tconst char *substr = obs->output_types.array[i].protocols;\n\t\twhile (substr && substr[0] != '\\0') {\n\t\t\tconst char *next = strchr(substr, ';');\n\t\t\tsize_t len = next ? (size_t)(next - substr) : strlen(substr);\n\t\t\tif (protocol_len == len && strncmp(substr, protocol, len) == 0) {\n\t\t\t\tif (!enum_cb(data, obs->output_types.array[i].id))\n\t\t\t\t\treturn;\n\t\t\t}\n\t\t\tsubstr = next ? next + 1 : NULL;\n\t\t}\n\t}\n}\n\nconst char *obs_get_output_supported_video_codecs(const char *id)\n{\n\tconst struct obs_output_info *info = find_output(id);\n\treturn info ? info->encoded_video_codecs : NULL;\n}\n\nconst char *obs_get_output_supported_audio_codecs(const char *id)\n{\n\tconst struct obs_output_info *info = find_output(id);\n\treturn info ? info->encoded_audio_codecs : NULL;\n}\n\nvoid obs_output_add_packet_callback(obs_output_t *output,\n\t\t\t\t    void (*packet_cb)(obs_output_t *output, struct encoder_packet *pkt,\n\t\t\t\t\t\t      struct encoder_packet_time *pkt_time, void *param),\n\t\t\t\t    void *param)\n{\n\tstruct packet_callback data = {packet_cb, param};\n\n\tpthread_mutex_lock(&output->pkt_callbacks_mutex);\n\tda_insert(output->pkt_callbacks, 0, &data);\n\tpthread_mutex_unlock(&output->pkt_callbacks_mutex);\n}\n\nvoid obs_output_remove_packet_callback(obs_output_t *output,\n\t\t\t\t       void (*packet_cb)(obs_output_t *output, struct encoder_packet *pkt,\n\t\t\t\t\t\t\t struct encoder_packet_time *pkt_time, void *param),\n\t\t\t\t       void *param)\n{\n\tstruct packet_callback data = {packet_cb, param};\n\n\tpthread_mutex_lock(&output->pkt_callbacks_mutex);\n\tda_erase_item(output->pkt_callbacks, &data);\n\tpthread_mutex_unlock(&output->pkt_callbacks_mutex);\n}\n\nvoid obs_output_set_reconnect_callback(obs_output_t *output,\n\t\t\t\t       bool (*reconnect_cb)(void *data, obs_output_t *output, int code), void *param)\n{\n\tif (!reconnect_cb) {\n\t\toutput->reconnect_callback.reconnect_cb = NULL;\n\t\toutput->reconnect_callback.param = NULL;\n\t} else {\n\t\toutput->reconnect_callback.reconnect_cb = reconnect_cb;\n\t\toutput->reconnect_callback.param = param;\n\t}\n}\n"
  },
  {
    "path": "libobs/obs-output.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* obs_output_info.flags definitions */\n#define OBS_OUTPUT_VIDEO (1 << 0)\n#define OBS_OUTPUT_AUDIO (1 << 1)\n#define OBS_OUTPUT_AV (OBS_OUTPUT_VIDEO | OBS_OUTPUT_AUDIO)\n#define OBS_OUTPUT_ENCODED (1 << 2)\n#define OBS_OUTPUT_SERVICE (1 << 3)\n#define OBS_OUTPUT_MULTI_TRACK (1 << 4)\n#define OBS_OUTPUT_CAN_PAUSE (1 << 5)\n#define OBS_OUTPUT_MULTI_TRACK_AUDIO OBS_OUTPUT_MULTI_TRACK\n#define OBS_OUTPUT_MULTI_TRACK_VIDEO (1 << 6)\n#define OBS_OUTPUT_MULTI_TRACK_AV (OBS_OUTPUT_MULTI_TRACK_AUDIO | OBS_OUTPUT_MULTI_TRACK_VIDEO)\n\n#define MAX_OUTPUT_AUDIO_ENCODERS 6\n#define MAX_OUTPUT_VIDEO_ENCODERS 10\n\nstruct encoder_packet;\n\nstruct obs_output_info {\n\t/* required */\n\tconst char *id;\n\n\tuint32_t flags;\n\n\tconst char *(*get_name)(void *type_data);\n\n\tvoid *(*create)(obs_data_t *settings, obs_output_t *output);\n\tvoid (*destroy)(void *data);\n\n\tbool (*start)(void *data);\n\tvoid (*stop)(void *data, uint64_t ts);\n\n\tvoid (*raw_video)(void *data, struct video_data *frame);\n\tvoid (*raw_audio)(void *data, struct audio_data *frames);\n\n\tvoid (*encoded_packet)(void *data, struct encoder_packet *packet);\n\n\t/* optional */\n\tvoid (*update)(void *data, obs_data_t *settings);\n\n\tvoid (*get_defaults)(obs_data_t *settings);\n\n\tobs_properties_t *(*get_properties)(void *data);\n\n\tvoid (*unused1)(void *data);\n\n\tuint64_t (*get_total_bytes)(void *data);\n\n\tint (*get_dropped_frames)(void *data);\n\n\tvoid *type_data;\n\tvoid (*free_type_data)(void *type_data);\n\n\tfloat (*get_congestion)(void *data);\n\tint (*get_connect_time_ms)(void *data);\n\n\t/* only used with encoded outputs, separated with semicolon */\n\tconst char *encoded_video_codecs;\n\tconst char *encoded_audio_codecs;\n\n\t/* raw audio callback for multi track outputs */\n\tvoid (*raw_audio2)(void *data, size_t idx, struct audio_data *frames);\n\n\t/* required if OBS_OUTPUT_SERVICE */\n\tconst char *protocols;\n};\n\nEXPORT void obs_register_output_s(const struct obs_output_info *info, size_t size);\n\n#define obs_register_output(info) obs_register_output_s(info, sizeof(struct obs_output_info))\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-properties.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"util/bmem.h\"\n#include \"util/darray.h\"\n#include \"obs-internal.h\"\n#include \"obs-properties.h\"\n\nstatic inline void *get_property_data(struct obs_property *prop);\n\n/* ------------------------------------------------------------------------- */\n\nstruct float_data {\n\tdouble min, max, step;\n\tenum obs_number_type type;\n\tchar *suffix;\n};\n\nstruct int_data {\n\tint min, max, step;\n\tenum obs_number_type type;\n\tchar *suffix;\n};\n\nstruct list_item {\n\tchar *name;\n\tbool disabled;\n\n\tunion {\n\t\tchar *str;\n\t\tlong long ll;\n\t\tdouble d;\n\t\tbool b;\n\t};\n};\n\nstruct path_data {\n\tchar *filter;\n\tchar *default_path;\n\tenum obs_path_type type;\n};\n\nstruct text_data {\n\tenum obs_text_type type;\n\tbool monospace;\n\tenum obs_text_info_type info_type;\n\tbool info_word_wrap;\n};\n\nstruct list_data {\n\tDARRAY(struct list_item) items;\n\tenum obs_combo_type type;\n\tenum obs_combo_format format;\n};\n\nstruct editable_list_data {\n\tenum obs_editable_list_type type;\n\tchar *filter;\n\tchar *default_path;\n};\n\nstruct button_data {\n\tobs_property_clicked_t callback;\n\tenum obs_button_type type;\n\tchar *url;\n};\n\nstruct frame_rate_option {\n\tchar *name;\n\tchar *description;\n};\n\nstruct frame_rate_range {\n\tstruct media_frames_per_second min_time;\n\tstruct media_frames_per_second max_time;\n};\n\nstruct frame_rate_data {\n\tDARRAY(struct frame_rate_option) extra_options;\n\tDARRAY(struct frame_rate_range) ranges;\n};\n\nstruct group_data {\n\tenum obs_group_type type;\n\tobs_properties_t *content;\n};\n\nstatic inline void path_data_free(struct path_data *data)\n{\n\tbfree(data->default_path);\n\tif (data->type == OBS_PATH_FILE)\n\t\tbfree(data->filter);\n}\n\nstatic inline void editable_list_data_free(struct editable_list_data *data)\n{\n\tbfree(data->default_path);\n\tbfree(data->filter);\n}\n\nstatic inline void list_item_free(struct list_data *data, struct list_item *item)\n{\n\tbfree(item->name);\n\tif (data->format == OBS_COMBO_FORMAT_STRING)\n\t\tbfree(item->str);\n}\n\nstatic inline void list_data_free(struct list_data *data)\n{\n\tfor (size_t i = 0; i < data->items.num; i++)\n\t\tlist_item_free(data, data->items.array + i);\n\n\tda_free(data->items);\n}\n\nstatic inline void frame_rate_data_options_free(struct frame_rate_data *data)\n{\n\tfor (size_t i = 0; i < data->extra_options.num; i++) {\n\t\tstruct frame_rate_option *opt = &data->extra_options.array[i];\n\t\tbfree(opt->name);\n\t\tbfree(opt->description);\n\t}\n\n\tda_resize(data->extra_options, 0);\n}\n\nstatic inline void frame_rate_data_ranges_free(struct frame_rate_data *data)\n{\n\tda_resize(data->ranges, 0);\n}\n\nstatic inline void frame_rate_data_free(struct frame_rate_data *data)\n{\n\tframe_rate_data_options_free(data);\n\tframe_rate_data_ranges_free(data);\n\n\tda_free(data->extra_options);\n\tda_free(data->ranges);\n}\n\nstatic inline void group_data_free(struct group_data *data)\n{\n\tobs_properties_destroy(data->content);\n}\n\nstatic inline void int_data_free(struct int_data *data)\n{\n\tif (data->suffix)\n\t\tbfree(data->suffix);\n}\n\nstatic inline void float_data_free(struct float_data *data)\n{\n\tif (data->suffix)\n\t\tbfree(data->suffix);\n}\n\nstatic inline void button_data_free(struct button_data *data)\n{\n\tif (data->url)\n\t\tbfree(data->url);\n}\n\nstruct obs_properties;\n\nstruct obs_property {\n\tchar *name;\n\tchar *desc;\n\tchar *long_desc;\n\tvoid *priv;\n\tenum obs_property_type type;\n\tbool visible;\n\tbool enabled;\n\n\tstruct obs_properties *parent;\n\n\tobs_property_modified_t modified;\n\tobs_property_modified2_t modified2;\n\n\tUT_hash_handle hh;\n};\n\nstruct obs_properties {\n\tvoid *param;\n\tvoid (*destroy)(void *param);\n\tuint32_t flags;\n\tuint32_t groups;\n\n\tstruct obs_property *properties;\n\tstruct obs_property *parent;\n};\n\nobs_properties_t *obs_properties_create(void)\n{\n\tstruct obs_properties *props;\n\tprops = bzalloc(sizeof(struct obs_properties));\n\treturn props;\n}\n\nvoid obs_properties_set_param(obs_properties_t *props, void *param, void (*destroy)(void *param))\n{\n\tif (!props)\n\t\treturn;\n\n\tif (props->param && props->destroy)\n\t\tprops->destroy(props->param);\n\n\tprops->param = param;\n\tprops->destroy = destroy;\n}\n\nvoid obs_properties_set_flags(obs_properties_t *props, uint32_t flags)\n{\n\tif (props)\n\t\tprops->flags = flags;\n}\n\nuint32_t obs_properties_get_flags(obs_properties_t *props)\n{\n\treturn props ? props->flags : 0;\n}\n\nvoid *obs_properties_get_param(obs_properties_t *props)\n{\n\treturn props ? props->param : NULL;\n}\n\nobs_properties_t *obs_properties_create_param(void *param, void (*destroy)(void *param))\n{\n\tstruct obs_properties *props = obs_properties_create();\n\tobs_properties_set_param(props, param, destroy);\n\treturn props;\n}\n\nstatic void obs_property_destroy(struct obs_property *property)\n{\n\tif (property->type == OBS_PROPERTY_LIST)\n\t\tlist_data_free(get_property_data(property));\n\telse if (property->type == OBS_PROPERTY_PATH)\n\t\tpath_data_free(get_property_data(property));\n\telse if (property->type == OBS_PROPERTY_EDITABLE_LIST)\n\t\teditable_list_data_free(get_property_data(property));\n\telse if (property->type == OBS_PROPERTY_FRAME_RATE)\n\t\tframe_rate_data_free(get_property_data(property));\n\telse if (property->type == OBS_PROPERTY_GROUP)\n\t\tgroup_data_free(get_property_data(property));\n\telse if (property->type == OBS_PROPERTY_INT)\n\t\tint_data_free(get_property_data(property));\n\telse if (property->type == OBS_PROPERTY_FLOAT)\n\t\tfloat_data_free(get_property_data(property));\n\telse if (property->type == OBS_PROPERTY_BUTTON)\n\t\tbutton_data_free(get_property_data(property));\n\n\tbfree(property->name);\n\tbfree(property->desc);\n\tbfree(property->long_desc);\n\tbfree(property);\n}\n\nvoid obs_properties_destroy(obs_properties_t *props)\n{\n\tif (props) {\n\t\tstruct obs_property *p, *tmp;\n\n\t\tif (props->destroy && props->param)\n\t\t\tprops->destroy(props->param);\n\n\t\tHASH_ITER (hh, props->properties, p, tmp) {\n\t\t\tHASH_DEL(props->properties, p);\n\t\t\tobs_property_destroy(p);\n\t\t}\n\n\t\tbfree(props);\n\t}\n}\n\nobs_property_t *obs_properties_first(obs_properties_t *props)\n{\n\treturn (props != NULL) ? props->properties : NULL;\n}\n\nobs_property_t *obs_properties_get(obs_properties_t *props, const char *name)\n{\n\tstruct obs_property *property, *tmp;\n\n\tif (!props)\n\t\treturn NULL;\n\n\tHASH_FIND_STR(props->properties, name, property);\n\tif (property)\n\t\treturn property;\n\n\tif (!props->groups)\n\t\treturn NULL;\n\n\t/* Recursively check groups as well, if any */\n\tHASH_ITER (hh, props->properties, property, tmp) {\n\t\tif (property->type != OBS_PROPERTY_GROUP)\n\t\t\tcontinue;\n\n\t\tobs_properties_t *group = obs_property_group_content(property);\n\t\tobs_property_t *found = obs_properties_get(group, name);\n\t\tif (found)\n\t\t\treturn found;\n\t}\n\n\treturn NULL;\n}\n\nobs_properties_t *obs_properties_get_parent(obs_properties_t *props)\n{\n\treturn props->parent ? props->parent->parent : NULL;\n}\n\nvoid obs_properties_remove_by_name(obs_properties_t *props, const char *name)\n{\n\tif (!props)\n\t\treturn;\n\n\tstruct obs_property *cur, *tmp;\n\n\tHASH_FIND_STR(props->properties, name, cur);\n\n\tif (cur) {\n\t\tHASH_DELETE(hh, props->properties, cur);\n\n\t\tif (cur->type == OBS_PROPERTY_GROUP)\n\t\t\tprops->groups--;\n\n\t\tobs_property_destroy(cur);\n\t\treturn;\n\t}\n\n\tif (!props->groups)\n\t\treturn;\n\n\tHASH_ITER (hh, props->properties, cur, tmp) {\n\t\tif (cur->type != OBS_PROPERTY_GROUP)\n\t\t\tcontinue;\n\n\t\tobs_properties_remove_by_name(obs_property_group_content(cur), name);\n\t}\n}\n\ntypedef DARRAY(struct obs_property *) obs_property_da_t;\n\nvoid obs_properties_apply_settings_internal(obs_properties_t *props, obs_property_da_t *properties_with_callback)\n{\n\tstruct obs_property *p = props->properties;\n\n\twhile (p) {\n\t\tif (p->type == OBS_PROPERTY_GROUP) {\n\t\t\tobs_properties_apply_settings_internal(obs_property_group_content(p), properties_with_callback);\n\t\t}\n\t\tif (p->modified || p->modified2)\n\t\t\tda_push_back((*properties_with_callback), &p);\n\n\t\tp = p->hh.next;\n\t}\n}\n\nvoid obs_properties_apply_settings(obs_properties_t *props, obs_data_t *settings)\n{\n\tif (!props)\n\t\treturn;\n\n\tobs_property_da_t properties_with_callback;\n\tda_init(properties_with_callback);\n\n\tobs_properties_apply_settings_internal(props, &properties_with_callback);\n\n\twhile (properties_with_callback.num > 0) {\n\t\tstruct obs_property *p = *(struct obs_property **)da_end(properties_with_callback);\n\t\tif (p->modified)\n\t\t\tp->modified(props, p, settings);\n\t\telse if (p->modified2)\n\t\t\tp->modified2(p->priv, props, p, settings);\n\t\tda_pop_back(properties_with_callback);\n\t}\n\n\tda_free(properties_with_callback);\n}\n\n/* ------------------------------------------------------------------------- */\n\nstatic inline size_t get_property_size(enum obs_property_type type)\n{\n\tswitch (type) {\n\tcase OBS_PROPERTY_INVALID:\n\t\treturn 0;\n\tcase OBS_PROPERTY_BOOL:\n\t\treturn 0;\n\tcase OBS_PROPERTY_INT:\n\t\treturn sizeof(struct int_data);\n\tcase OBS_PROPERTY_FLOAT:\n\t\treturn sizeof(struct float_data);\n\tcase OBS_PROPERTY_TEXT:\n\t\treturn sizeof(struct text_data);\n\tcase OBS_PROPERTY_PATH:\n\t\treturn sizeof(struct path_data);\n\tcase OBS_PROPERTY_LIST:\n\t\treturn sizeof(struct list_data);\n\tcase OBS_PROPERTY_COLOR:\n\t\treturn 0;\n\tcase OBS_PROPERTY_BUTTON:\n\t\treturn sizeof(struct button_data);\n\tcase OBS_PROPERTY_FONT:\n\t\treturn 0;\n\tcase OBS_PROPERTY_EDITABLE_LIST:\n\t\treturn sizeof(struct editable_list_data);\n\tcase OBS_PROPERTY_FRAME_RATE:\n\t\treturn sizeof(struct frame_rate_data);\n\tcase OBS_PROPERTY_GROUP:\n\t\treturn sizeof(struct group_data);\n\tcase OBS_PROPERTY_COLOR_ALPHA:\n\t\treturn 0;\n\t}\n\n\treturn 0;\n}\n\nstatic inline struct obs_property *new_prop(struct obs_properties *props, const char *name, const char *desc,\n\t\t\t\t\t    enum obs_property_type type)\n{\n\tsize_t data_size = get_property_size(type);\n\tstruct obs_property *p;\n\n\tp = bzalloc(sizeof(struct obs_property) + data_size);\n\tp->parent = props;\n\tp->enabled = true;\n\tp->visible = true;\n\tp->type = type;\n\tp->name = bstrdup(name);\n\tp->desc = bstrdup(desc);\n\n\tHASH_ADD_STR(props->properties, name, p);\n\n\treturn p;\n}\n\nstatic inline obs_properties_t *get_topmost_parent(obs_properties_t *props)\n{\n\tobs_properties_t *parent = props;\n\tobs_properties_t *last_parent = parent;\n\twhile (parent) {\n\t\tlast_parent = parent;\n\t\tparent = obs_properties_get_parent(parent);\n\t}\n\treturn last_parent;\n}\n\nstatic inline bool contains_prop(struct obs_properties *props, const char *name)\n{\n\tstruct obs_property *p, *tmp;\n\tHASH_FIND_STR(props->properties, name, p);\n\n\tif (p) {\n\t\tblog(LOG_WARNING, \"Property '%s' exists\", name);\n\t\treturn true;\n\t}\n\n\tif (!props->groups)\n\t\treturn false;\n\n\tHASH_ITER (hh, props->properties, p, tmp) {\n\t\tif (p->type != OBS_PROPERTY_GROUP)\n\t\t\tcontinue;\n\t\tif (contains_prop(obs_property_group_content(p), name))\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic inline bool has_prop(struct obs_properties *props, const char *name)\n{\n\treturn contains_prop(get_topmost_parent(props), name);\n}\n\nstatic inline void *get_property_data(struct obs_property *prop)\n{\n\treturn (uint8_t *)prop + sizeof(struct obs_property);\n}\n\nstatic inline void *get_type_data(struct obs_property *prop, enum obs_property_type type)\n{\n\tif (!prop || prop->type != type)\n\t\treturn NULL;\n\n\treturn get_property_data(prop);\n}\n\nobs_property_t *obs_properties_add_bool(obs_properties_t *props, const char *name, const char *desc)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\treturn new_prop(props, name, desc, OBS_PROPERTY_BOOL);\n}\n\nstatic obs_property_t *add_int(obs_properties_t *props, const char *name, const char *desc, int min, int max, int step,\n\t\t\t       enum obs_number_type type)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\n\tstruct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_INT);\n\tstruct int_data *data = get_property_data(p);\n\tdata->min = min;\n\tdata->max = max;\n\tdata->step = step;\n\tdata->type = type;\n\treturn p;\n}\n\nstatic obs_property_t *add_flt(obs_properties_t *props, const char *name, const char *desc, double min, double max,\n\t\t\t       double step, enum obs_number_type type)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\n\tstruct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_FLOAT);\n\tstruct float_data *data = get_property_data(p);\n\tdata->min = min;\n\tdata->max = max;\n\tdata->step = step;\n\tdata->type = type;\n\treturn p;\n}\n\nobs_property_t *obs_properties_add_int(obs_properties_t *props, const char *name, const char *desc, int min, int max,\n\t\t\t\t       int step)\n{\n\treturn add_int(props, name, desc, min, max, step, OBS_NUMBER_SCROLLER);\n}\n\nobs_property_t *obs_properties_add_float(obs_properties_t *props, const char *name, const char *desc, double min,\n\t\t\t\t\t double max, double step)\n{\n\treturn add_flt(props, name, desc, min, max, step, OBS_NUMBER_SCROLLER);\n}\n\nobs_property_t *obs_properties_add_int_slider(obs_properties_t *props, const char *name, const char *desc, int min,\n\t\t\t\t\t      int max, int step)\n{\n\treturn add_int(props, name, desc, min, max, step, OBS_NUMBER_SLIDER);\n}\n\nobs_property_t *obs_properties_add_float_slider(obs_properties_t *props, const char *name, const char *desc, double min,\n\t\t\t\t\t\tdouble max, double step)\n{\n\treturn add_flt(props, name, desc, min, max, step, OBS_NUMBER_SLIDER);\n}\n\nobs_property_t *obs_properties_add_text(obs_properties_t *props, const char *name, const char *desc,\n\t\t\t\t\tenum obs_text_type type)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\n\tstruct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_TEXT);\n\tstruct text_data *data = get_property_data(p);\n\tdata->type = type;\n\tdata->info_type = OBS_TEXT_INFO_NORMAL;\n\tdata->info_word_wrap = true;\n\treturn p;\n}\n\nobs_property_t *obs_properties_add_path(obs_properties_t *props, const char *name, const char *desc,\n\t\t\t\t\tenum obs_path_type type, const char *filter, const char *default_path)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\n\tstruct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_PATH);\n\tstruct path_data *data = get_property_data(p);\n\tdata->type = type;\n\tdata->default_path = bstrdup(default_path);\n\n\tif (data->type == OBS_PATH_FILE)\n\t\tdata->filter = bstrdup(filter);\n\n\treturn p;\n}\n\nobs_property_t *obs_properties_add_list(obs_properties_t *props, const char *name, const char *desc,\n\t\t\t\t\tenum obs_combo_type type, enum obs_combo_format format)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\n\tif (type == OBS_COMBO_TYPE_EDITABLE && format != OBS_COMBO_FORMAT_STRING) {\n\t\tblog(LOG_WARNING,\n\t\t     \"List '%s', error: Editable combo boxes \"\n\t\t     \"must be of the 'string' type\",\n\t\t     name);\n\t\treturn NULL;\n\t}\n\n\tstruct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_LIST);\n\tstruct list_data *data = get_property_data(p);\n\tdata->format = format;\n\tdata->type = type;\n\n\treturn p;\n}\n\nobs_property_t *obs_properties_add_color(obs_properties_t *props, const char *name, const char *desc)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\treturn new_prop(props, name, desc, OBS_PROPERTY_COLOR);\n}\n\nobs_property_t *obs_properties_add_color_alpha(obs_properties_t *props, const char *name, const char *desc)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\treturn new_prop(props, name, desc, OBS_PROPERTY_COLOR_ALPHA);\n}\n\nobs_property_t *obs_properties_add_button(obs_properties_t *props, const char *name, const char *text,\n\t\t\t\t\t  obs_property_clicked_t callback)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\n\tstruct obs_property *p = new_prop(props, name, text, OBS_PROPERTY_BUTTON);\n\tstruct button_data *data = get_property_data(p);\n\tdata->callback = callback;\n\treturn p;\n}\n\nobs_property_t *obs_properties_add_button2(obs_properties_t *props, const char *name, const char *text,\n\t\t\t\t\t   obs_property_clicked_t callback, void *priv)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\n\tstruct obs_property *p = new_prop(props, name, text, OBS_PROPERTY_BUTTON);\n\tstruct button_data *data = get_property_data(p);\n\tdata->callback = callback;\n\tp->priv = priv;\n\treturn p;\n}\n\nobs_property_t *obs_properties_add_font(obs_properties_t *props, const char *name, const char *desc)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\treturn new_prop(props, name, desc, OBS_PROPERTY_FONT);\n}\n\nobs_property_t *obs_properties_add_editable_list(obs_properties_t *props, const char *name, const char *desc,\n\t\t\t\t\t\t enum obs_editable_list_type type, const char *filter,\n\t\t\t\t\t\t const char *default_path)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\tstruct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_EDITABLE_LIST);\n\n\tstruct editable_list_data *data = get_property_data(p);\n\tdata->type = type;\n\tdata->filter = bstrdup(filter);\n\tdata->default_path = bstrdup(default_path);\n\treturn p;\n}\n\nobs_property_t *obs_properties_add_frame_rate(obs_properties_t *props, const char *name, const char *desc)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\n\tstruct obs_property *p = new_prop(props, name, desc, OBS_PROPERTY_FRAME_RATE);\n\n\tstruct frame_rate_data *data = get_property_data(p);\n\tda_init(data->extra_options);\n\tda_init(data->ranges);\n\treturn p;\n}\n\nstatic bool check_property_group_recursion(obs_properties_t *parent, obs_properties_t *group)\n{\n\t/* Scan the group for the parent. */\n\tobs_property_t *p, *tmp;\n\n\tHASH_ITER (hh, group->properties, p, tmp) {\n\t\tif (p->type != OBS_PROPERTY_GROUP)\n\t\t\tcontinue;\n\n\t\tobs_properties_t *cprops = obs_property_group_content(p);\n\t\tif (cprops == parent) {\n\t\t\t/* Contains find_props */\n\t\t\treturn true;\n\t\t} else if (cprops == group) {\n\t\t\t/* Contains self, shouldn't be possible but\n\t\t\t\t * lets verify anyway. */\n\t\t\treturn true;\n\t\t}\n\t\tif (check_property_group_recursion(parent, cprops))\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic bool check_property_group_duplicates(obs_properties_t *parent, obs_properties_t *group)\n{\n\tobs_property_t *p, *tmp;\n\n\tHASH_ITER (hh, group->properties, p, tmp) {\n\t\tif (has_prop(parent, p->name))\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nobs_property_t *obs_properties_add_group(obs_properties_t *props, const char *name, const char *desc,\n\t\t\t\t\t enum obs_group_type type, obs_properties_t *group)\n{\n\tif (!props || has_prop(props, name))\n\t\treturn NULL;\n\tif (!group)\n\t\treturn NULL;\n\n\t/* Prevent recursion. */\n\tif (props == group)\n\t\treturn NULL;\n\tif (check_property_group_recursion(props, group))\n\t\treturn NULL;\n\n\t/* Prevent duplicate properties */\n\tif (check_property_group_duplicates(props, group))\n\t\treturn NULL;\n\n\tobs_property_t *p = new_prop(props, name, desc, OBS_PROPERTY_GROUP);\n\tprops->groups++;\n\tgroup->parent = p;\n\n\tstruct group_data *data = get_property_data(p);\n\tdata->type = type;\n\tdata->content = group;\n\treturn p;\n}\n\n/* ------------------------------------------------------------------------- */\n\nstatic inline bool is_combo(struct obs_property *p)\n{\n\treturn p->type == OBS_PROPERTY_LIST;\n}\n\nstatic inline struct list_data *get_list_data(struct obs_property *p)\n{\n\tif (!p || !is_combo(p))\n\t\treturn NULL;\n\n\treturn get_property_data(p);\n}\n\nstatic inline struct list_data *get_list_fmt_data(struct obs_property *p, enum obs_combo_format format)\n{\n\tstruct list_data *data = get_list_data(p);\n\treturn (data && data->format == format) ? data : NULL;\n}\n\n/* ------------------------------------------------------------------------- */\n\nbool obs_property_next(obs_property_t **p)\n{\n\tif (!p || !*p)\n\t\treturn false;\n\n\t*p = (*p)->hh.next;\n\treturn *p != NULL;\n}\n\nvoid obs_property_set_modified_callback(obs_property_t *p, obs_property_modified_t modified)\n{\n\tif (p)\n\t\tp->modified = modified;\n}\n\nvoid obs_property_set_modified_callback2(obs_property_t *p, obs_property_modified2_t modified2, void *priv)\n{\n\tif (p) {\n\t\tp->modified2 = modified2;\n\t\tp->priv = priv;\n\t}\n}\n\nbool obs_property_modified(obs_property_t *p, obs_data_t *settings)\n{\n\tif (p) {\n\t\tif (p->modified) {\n\t\t\tobs_properties_t *top = get_topmost_parent(p->parent);\n\t\t\treturn p->modified(top, p, settings);\n\t\t} else if (p->modified2) {\n\t\t\tobs_properties_t *top = get_topmost_parent(p->parent);\n\t\t\treturn p->modified2(p->priv, top, p, settings);\n\t\t}\n\t}\n\treturn false;\n}\n\nbool obs_property_button_clicked(obs_property_t *p, void *obj)\n{\n\tstruct obs_context_data *context = obj;\n\tif (p) {\n\t\tstruct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);\n\t\tif (data && data->callback) {\n\t\t\tobs_properties_t *top = get_topmost_parent(p->parent);\n\t\t\tif (p->priv)\n\t\t\t\treturn data->callback(top, p, p->priv);\n\t\t\treturn data->callback(top, p, (context ? context->data : NULL));\n\t\t}\n\t}\n\n\treturn false;\n}\n\nvoid obs_property_set_visible(obs_property_t *p, bool visible)\n{\n\tif (p)\n\t\tp->visible = visible;\n}\n\nvoid obs_property_set_enabled(obs_property_t *p, bool enabled)\n{\n\tif (p)\n\t\tp->enabled = enabled;\n}\n\nvoid obs_property_set_description(obs_property_t *p, const char *description)\n{\n\tif (p) {\n\t\tbfree(p->desc);\n\t\tp->desc = description && *description ? bstrdup(description) : NULL;\n\t}\n}\n\nvoid obs_property_set_long_description(obs_property_t *p, const char *long_desc)\n{\n\tif (p) {\n\t\tbfree(p->long_desc);\n\t\tp->long_desc = long_desc && *long_desc ? bstrdup(long_desc) : NULL;\n\t}\n}\n\nconst char *obs_property_name(obs_property_t *p)\n{\n\treturn p ? p->name : NULL;\n}\n\nconst char *obs_property_description(obs_property_t *p)\n{\n\treturn p ? p->desc : NULL;\n}\n\nconst char *obs_property_long_description(obs_property_t *p)\n{\n\treturn p ? p->long_desc : NULL;\n}\n\nenum obs_property_type obs_property_get_type(obs_property_t *p)\n{\n\treturn p ? p->type : OBS_PROPERTY_INVALID;\n}\n\nbool obs_property_enabled(obs_property_t *p)\n{\n\treturn p ? p->enabled : false;\n}\n\nbool obs_property_visible(obs_property_t *p)\n{\n\treturn p ? p->visible : false;\n}\n\nint obs_property_int_min(obs_property_t *p)\n{\n\tstruct int_data *data = get_type_data(p, OBS_PROPERTY_INT);\n\treturn data ? data->min : 0;\n}\n\nint obs_property_int_max(obs_property_t *p)\n{\n\tstruct int_data *data = get_type_data(p, OBS_PROPERTY_INT);\n\treturn data ? data->max : 0;\n}\n\nint obs_property_int_step(obs_property_t *p)\n{\n\tstruct int_data *data = get_type_data(p, OBS_PROPERTY_INT);\n\treturn data ? data->step : 0;\n}\n\nenum obs_number_type obs_property_int_type(obs_property_t *p)\n{\n\tstruct int_data *data = get_type_data(p, OBS_PROPERTY_INT);\n\treturn data ? data->type : OBS_NUMBER_SCROLLER;\n}\n\nconst char *obs_property_int_suffix(obs_property_t *p)\n{\n\tstruct int_data *data = get_type_data(p, OBS_PROPERTY_INT);\n\treturn data ? data->suffix : NULL;\n}\n\ndouble obs_property_float_min(obs_property_t *p)\n{\n\tstruct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);\n\treturn data ? data->min : 0;\n}\n\ndouble obs_property_float_max(obs_property_t *p)\n{\n\tstruct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);\n\treturn data ? data->max : 0;\n}\n\ndouble obs_property_float_step(obs_property_t *p)\n{\n\tstruct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);\n\treturn data ? data->step : 0;\n}\n\nconst char *obs_property_float_suffix(obs_property_t *p)\n{\n\tstruct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);\n\treturn data ? data->suffix : NULL;\n}\n\nenum obs_number_type obs_property_float_type(obs_property_t *p)\n{\n\tstruct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);\n\treturn data ? data->type : OBS_NUMBER_SCROLLER;\n}\n\nenum obs_text_type obs_property_text_type(obs_property_t *p)\n{\n\tstruct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);\n\treturn data ? data->type : OBS_TEXT_DEFAULT;\n}\n\nbool obs_property_text_monospace(obs_property_t *p)\n{\n\tstruct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);\n\treturn data ? data->monospace : false;\n}\n\nenum obs_text_info_type obs_property_text_info_type(obs_property_t *p)\n{\n\tstruct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);\n\treturn data ? data->info_type : OBS_TEXT_INFO_NORMAL;\n}\n\nbool obs_property_text_info_word_wrap(obs_property_t *p)\n{\n\tstruct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);\n\treturn data ? data->info_word_wrap : true;\n}\n\nenum obs_path_type obs_property_path_type(obs_property_t *p)\n{\n\tstruct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);\n\treturn data ? data->type : OBS_PATH_DIRECTORY;\n}\n\nconst char *obs_property_path_filter(obs_property_t *p)\n{\n\tstruct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);\n\treturn data ? data->filter : NULL;\n}\n\nconst char *obs_property_path_default_path(obs_property_t *p)\n{\n\tstruct path_data *data = get_type_data(p, OBS_PROPERTY_PATH);\n\treturn data ? data->default_path : NULL;\n}\n\nenum obs_combo_type obs_property_list_type(obs_property_t *p)\n{\n\tstruct list_data *data = get_list_data(p);\n\treturn data ? data->type : OBS_COMBO_TYPE_INVALID;\n}\n\nenum obs_combo_format obs_property_list_format(obs_property_t *p)\n{\n\tstruct list_data *data = get_list_data(p);\n\treturn data ? data->format : OBS_COMBO_FORMAT_INVALID;\n}\n\nvoid obs_property_int_set_limits(obs_property_t *p, int min, int max, int step)\n{\n\tstruct int_data *data = get_type_data(p, OBS_PROPERTY_INT);\n\tif (!data)\n\t\treturn;\n\n\tdata->min = min;\n\tdata->max = max;\n\tdata->step = step;\n}\n\nvoid obs_property_float_set_limits(obs_property_t *p, double min, double max, double step)\n{\n\tstruct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);\n\tif (!data)\n\t\treturn;\n\n\tdata->min = min;\n\tdata->max = max;\n\tdata->step = step;\n}\n\nvoid obs_property_int_set_suffix(obs_property_t *p, const char *suffix)\n{\n\tstruct int_data *data = get_type_data(p, OBS_PROPERTY_INT);\n\tif (!data)\n\t\treturn;\n\n\tbfree(data->suffix);\n\tdata->suffix = bstrdup(suffix);\n}\n\nvoid obs_property_float_set_suffix(obs_property_t *p, const char *suffix)\n{\n\tstruct float_data *data = get_type_data(p, OBS_PROPERTY_FLOAT);\n\tif (!data)\n\t\treturn;\n\n\tbfree(data->suffix);\n\tdata->suffix = bstrdup(suffix);\n}\n\nvoid obs_property_text_set_monospace(obs_property_t *p, bool monospace)\n{\n\tstruct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);\n\tif (!data)\n\t\treturn;\n\n\tdata->monospace = monospace;\n}\n\nvoid obs_property_text_set_info_type(obs_property_t *p, enum obs_text_info_type type)\n{\n\tstruct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);\n\tif (!data)\n\t\treturn;\n\n\tdata->info_type = type;\n}\n\nvoid obs_property_text_set_info_word_wrap(obs_property_t *p, bool word_wrap)\n{\n\tstruct text_data *data = get_type_data(p, OBS_PROPERTY_TEXT);\n\tif (!data)\n\t\treturn;\n\n\tdata->info_word_wrap = word_wrap;\n}\n\nvoid obs_property_button_set_type(obs_property_t *p, enum obs_button_type type)\n{\n\tstruct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);\n\tif (!data)\n\t\treturn;\n\n\tdata->type = type;\n}\n\nvoid obs_property_button_set_url(obs_property_t *p, char *url)\n{\n\tstruct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);\n\tif (!data)\n\t\treturn;\n\n\tdata->url = bstrdup(url);\n}\n\nvoid obs_property_list_clear(obs_property_t *p)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (data)\n\t\tlist_data_free(data);\n}\n\nstatic size_t add_item(struct list_data *data, const char *name, const void *val)\n{\n\tstruct list_item item = {NULL};\n\titem.name = bstrdup(name);\n\n\tif (data->format == OBS_COMBO_FORMAT_INT)\n\t\titem.ll = *(const long long *)val;\n\telse if (data->format == OBS_COMBO_FORMAT_FLOAT)\n\t\titem.d = *(const double *)val;\n\telse if (data->format == OBS_COMBO_FORMAT_BOOL)\n\t\titem.b = *(const bool *)val;\n\telse\n\t\titem.str = bstrdup(val);\n\n\treturn da_push_back(data->items, &item);\n}\n\nstatic void insert_item(struct list_data *data, size_t idx, const char *name, const void *val)\n{\n\tstruct list_item item = {NULL};\n\titem.name = bstrdup(name);\n\n\tif (data->format == OBS_COMBO_FORMAT_INT)\n\t\titem.ll = *(const long long *)val;\n\telse if (data->format == OBS_COMBO_FORMAT_FLOAT)\n\t\titem.d = *(const double *)val;\n\telse if (data->format == OBS_COMBO_FORMAT_BOOL)\n\t\titem.b = *(const bool *)val;\n\telse\n\t\titem.str = bstrdup(val);\n\n\tda_insert(data->items, idx, &item);\n}\n\nsize_t obs_property_list_add_string(obs_property_t *p, const char *name, const char *val)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (data && data->format == OBS_COMBO_FORMAT_STRING)\n\t\treturn add_item(data, name, val);\n\treturn 0;\n}\n\nsize_t obs_property_list_add_int(obs_property_t *p, const char *name, long long val)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (data && data->format == OBS_COMBO_FORMAT_INT)\n\t\treturn add_item(data, name, &val);\n\treturn 0;\n}\n\nsize_t obs_property_list_add_float(obs_property_t *p, const char *name, double val)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (data && data->format == OBS_COMBO_FORMAT_FLOAT)\n\t\treturn add_item(data, name, &val);\n\treturn 0;\n}\n\nsize_t obs_property_list_add_bool(obs_property_t *p, const char *name, bool val)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (data && data->format == OBS_COMBO_FORMAT_BOOL)\n\t\treturn add_item(data, name, &val);\n\treturn 0;\n}\n\nvoid obs_property_list_insert_string(obs_property_t *p, size_t idx, const char *name, const char *val)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (data && data->format == OBS_COMBO_FORMAT_STRING)\n\t\tinsert_item(data, idx, name, val);\n}\n\nvoid obs_property_list_insert_int(obs_property_t *p, size_t idx, const char *name, long long val)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (data && data->format == OBS_COMBO_FORMAT_INT)\n\t\tinsert_item(data, idx, name, &val);\n}\n\nvoid obs_property_list_insert_float(obs_property_t *p, size_t idx, const char *name, double val)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (data && data->format == OBS_COMBO_FORMAT_FLOAT)\n\t\tinsert_item(data, idx, name, &val);\n}\n\nvoid obs_property_list_insert_bool(obs_property_t *p, size_t idx, const char *name, bool val)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (data && data->format == OBS_COMBO_FORMAT_BOOL)\n\t\tinsert_item(data, idx, name, &val);\n}\n\nvoid obs_property_list_item_remove(obs_property_t *p, size_t idx)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (data && idx < data->items.num) {\n\t\tlist_item_free(data, data->items.array + idx);\n\t\tda_erase(data->items, idx);\n\t}\n}\n\nsize_t obs_property_list_item_count(obs_property_t *p)\n{\n\tstruct list_data *data = get_list_data(p);\n\treturn data ? data->items.num : 0;\n}\n\nbool obs_property_list_item_disabled(obs_property_t *p, size_t idx)\n{\n\tstruct list_data *data = get_list_data(p);\n\treturn (data && idx < data->items.num) ? data->items.array[idx].disabled : false;\n}\n\nvoid obs_property_list_item_disable(obs_property_t *p, size_t idx, bool disabled)\n{\n\tstruct list_data *data = get_list_data(p);\n\tif (!data || idx >= data->items.num)\n\t\treturn;\n\tdata->items.array[idx].disabled = disabled;\n}\n\nconst char *obs_property_list_item_name(obs_property_t *p, size_t idx)\n{\n\tstruct list_data *data = get_list_data(p);\n\treturn (data && idx < data->items.num) ? data->items.array[idx].name : NULL;\n}\n\nconst char *obs_property_list_item_string(obs_property_t *p, size_t idx)\n{\n\tstruct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_STRING);\n\treturn (data && idx < data->items.num) ? data->items.array[idx].str : NULL;\n}\n\nlong long obs_property_list_item_int(obs_property_t *p, size_t idx)\n{\n\tstruct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_INT);\n\treturn (data && idx < data->items.num) ? data->items.array[idx].ll : 0;\n}\n\ndouble obs_property_list_item_float(obs_property_t *p, size_t idx)\n{\n\tstruct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_FLOAT);\n\treturn (data && idx < data->items.num) ? data->items.array[idx].d : 0.0;\n}\n\nbool obs_property_list_item_bool(obs_property_t *p, size_t idx)\n{\n\tstruct list_data *data = get_list_fmt_data(p, OBS_COMBO_FORMAT_BOOL);\n\treturn (data && idx < data->items.num) ? data->items.array[idx].d : false;\n}\n\nenum obs_editable_list_type obs_property_editable_list_type(obs_property_t *p)\n{\n\tstruct editable_list_data *data = get_type_data(p, OBS_PROPERTY_EDITABLE_LIST);\n\treturn data ? data->type : OBS_EDITABLE_LIST_TYPE_STRINGS;\n}\n\nconst char *obs_property_editable_list_filter(obs_property_t *p)\n{\n\tstruct editable_list_data *data = get_type_data(p, OBS_PROPERTY_EDITABLE_LIST);\n\treturn data ? data->filter : NULL;\n}\n\nconst char *obs_property_editable_list_default_path(obs_property_t *p)\n{\n\tstruct editable_list_data *data = get_type_data(p, OBS_PROPERTY_EDITABLE_LIST);\n\treturn data ? data->default_path : NULL;\n}\n\n/* ------------------------------------------------------------------------- */\n/* OBS_PROPERTY_FRAME_RATE */\n\nvoid obs_property_frame_rate_clear(obs_property_t *p)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\tif (!data)\n\t\treturn;\n\n\tframe_rate_data_options_free(data);\n\tframe_rate_data_ranges_free(data);\n}\n\nvoid obs_property_frame_rate_options_clear(obs_property_t *p)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\tif (!data)\n\t\treturn;\n\n\tframe_rate_data_options_free(data);\n}\n\nvoid obs_property_frame_rate_fps_ranges_clear(obs_property_t *p)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\tif (!data)\n\t\treturn;\n\n\tframe_rate_data_ranges_free(data);\n}\n\nsize_t obs_property_frame_rate_option_add(obs_property_t *p, const char *name, const char *description)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\tif (!data)\n\t\treturn DARRAY_INVALID;\n\n\tstruct frame_rate_option *opt = da_push_back_new(data->extra_options);\n\n\topt->name = bstrdup(name);\n\topt->description = bstrdup(description);\n\n\treturn data->extra_options.num - 1;\n}\n\nsize_t obs_property_frame_rate_fps_range_add(obs_property_t *p, struct media_frames_per_second min,\n\t\t\t\t\t     struct media_frames_per_second max)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\tif (!data)\n\t\treturn DARRAY_INVALID;\n\n\tstruct frame_rate_range *rng = da_push_back_new(data->ranges);\n\n\trng->min_time = min;\n\trng->max_time = max;\n\n\treturn data->ranges.num - 1;\n}\n\nvoid obs_property_frame_rate_option_insert(obs_property_t *p, size_t idx, const char *name, const char *description)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\tif (!data)\n\t\treturn;\n\n\tstruct frame_rate_option *opt = da_insert_new(data->extra_options, idx);\n\n\topt->name = bstrdup(name);\n\topt->description = bstrdup(description);\n}\n\nvoid obs_property_frame_rate_fps_range_insert(obs_property_t *p, size_t idx, struct media_frames_per_second min,\n\t\t\t\t\t      struct media_frames_per_second max)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\tif (!data)\n\t\treturn;\n\n\tstruct frame_rate_range *rng = da_insert_new(data->ranges, idx);\n\n\trng->min_time = min;\n\trng->max_time = max;\n}\n\nsize_t obs_property_frame_rate_options_count(obs_property_t *p)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\treturn data ? data->extra_options.num : 0;\n}\n\nconst char *obs_property_frame_rate_option_name(obs_property_t *p, size_t idx)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\treturn data && data->extra_options.num > idx ? data->extra_options.array[idx].name : NULL;\n}\n\nconst char *obs_property_frame_rate_option_description(obs_property_t *p, size_t idx)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\treturn data && data->extra_options.num > idx ? data->extra_options.array[idx].description : NULL;\n}\n\nsize_t obs_property_frame_rate_fps_ranges_count(obs_property_t *p)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\treturn data ? data->ranges.num : 0;\n}\n\nstruct media_frames_per_second obs_property_frame_rate_fps_range_min(obs_property_t *p, size_t idx)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\treturn data && data->ranges.num > idx ? data->ranges.array[idx].min_time : (struct media_frames_per_second){0};\n}\nstruct media_frames_per_second obs_property_frame_rate_fps_range_max(obs_property_t *p, size_t idx)\n{\n\tstruct frame_rate_data *data = get_type_data(p, OBS_PROPERTY_FRAME_RATE);\n\treturn data && data->ranges.num > idx ? data->ranges.array[idx].max_time : (struct media_frames_per_second){0};\n}\n\nenum obs_group_type obs_property_group_type(obs_property_t *p)\n{\n\tstruct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP);\n\treturn data ? data->type : OBS_COMBO_INVALID;\n}\n\nobs_properties_t *obs_property_group_content(obs_property_t *p)\n{\n\tstruct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP);\n\treturn data ? data->content : NULL;\n}\n\nenum obs_button_type obs_property_button_type(obs_property_t *p)\n{\n\tstruct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);\n\treturn data ? data->type : OBS_BUTTON_DEFAULT;\n}\n\nconst char *obs_property_button_url(obs_property_t *p)\n{\n\tstruct button_data *data = get_type_data(p, OBS_PROPERTY_BUTTON);\n\treturn data ? data->url : \"\";\n}\n"
  },
  {
    "path": "libobs/obs-properties.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"util/c99defs.h\"\n#include \"obs-data.h\"\n#include \"media-io/frame-rate.h\"\n\n/**\n * @file\n * @brief libobs header for the properties system used in libobs\n *\n * @page properties Properties\n * @brief Platform and Toolkit independent settings implementation\n *\n * @section prop_overview_sec Overview\n *\n * libobs uses a property system which lets for example sources specify\n * settings that can be displayed to the user by the UI.\n *\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Only update when the user presses OK or Apply */\n#define OBS_PROPERTIES_DEFER_UPDATE (1 << 0)\n\nenum obs_property_type {\n\tOBS_PROPERTY_INVALID,\n\tOBS_PROPERTY_BOOL,\n\tOBS_PROPERTY_INT,\n\tOBS_PROPERTY_FLOAT,\n\tOBS_PROPERTY_TEXT,\n\tOBS_PROPERTY_PATH,\n\tOBS_PROPERTY_LIST,\n\tOBS_PROPERTY_COLOR,\n\tOBS_PROPERTY_BUTTON,\n\tOBS_PROPERTY_FONT,\n\tOBS_PROPERTY_EDITABLE_LIST,\n\tOBS_PROPERTY_FRAME_RATE,\n\tOBS_PROPERTY_GROUP,\n\tOBS_PROPERTY_COLOR_ALPHA,\n};\n\nenum obs_combo_format {\n\tOBS_COMBO_FORMAT_INVALID,\n\tOBS_COMBO_FORMAT_INT,\n\tOBS_COMBO_FORMAT_FLOAT,\n\tOBS_COMBO_FORMAT_STRING,\n\tOBS_COMBO_FORMAT_BOOL,\n};\n\nenum obs_combo_type {\n\tOBS_COMBO_TYPE_INVALID,\n\tOBS_COMBO_TYPE_EDITABLE,\n\tOBS_COMBO_TYPE_LIST,\n\tOBS_COMBO_TYPE_RADIO,\n};\n\nenum obs_editable_list_type {\n\tOBS_EDITABLE_LIST_TYPE_STRINGS,\n\tOBS_EDITABLE_LIST_TYPE_FILES,\n\tOBS_EDITABLE_LIST_TYPE_FILES_AND_URLS,\n};\n\nenum obs_path_type {\n\tOBS_PATH_FILE,\n\tOBS_PATH_FILE_SAVE,\n\tOBS_PATH_DIRECTORY,\n};\n\nenum obs_text_type {\n\tOBS_TEXT_DEFAULT,\n\tOBS_TEXT_PASSWORD,\n\tOBS_TEXT_MULTILINE,\n\tOBS_TEXT_INFO,\n};\n\nenum obs_text_info_type {\n\tOBS_TEXT_INFO_NORMAL,\n\tOBS_TEXT_INFO_WARNING,\n\tOBS_TEXT_INFO_ERROR,\n};\n\nenum obs_number_type {\n\tOBS_NUMBER_SCROLLER,\n\tOBS_NUMBER_SLIDER,\n};\n\nenum obs_group_type {\n\tOBS_COMBO_INVALID,\n\tOBS_GROUP_NORMAL,\n\tOBS_GROUP_CHECKABLE,\n};\n\nenum obs_button_type {\n\tOBS_BUTTON_DEFAULT,\n\tOBS_BUTTON_URL,\n};\n\n#define OBS_FONT_BOLD (1 << 0)\n#define OBS_FONT_ITALIC (1 << 1)\n#define OBS_FONT_UNDERLINE (1 << 2)\n#define OBS_FONT_STRIKEOUT (1 << 3)\n\nstruct obs_properties;\nstruct obs_property;\ntypedef struct obs_properties obs_properties_t;\ntypedef struct obs_property obs_property_t;\n\n/* ------------------------------------------------------------------------- */\n\nEXPORT obs_properties_t *obs_properties_create(void);\nEXPORT obs_properties_t *obs_properties_create_param(void *param, void (*destroy)(void *param));\nEXPORT void obs_properties_destroy(obs_properties_t *props);\n\nEXPORT void obs_properties_set_flags(obs_properties_t *props, uint32_t flags);\nEXPORT uint32_t obs_properties_get_flags(obs_properties_t *props);\n\nEXPORT void obs_properties_set_param(obs_properties_t *props, void *param, void (*destroy)(void *param));\nEXPORT void *obs_properties_get_param(obs_properties_t *props);\n\nEXPORT obs_property_t *obs_properties_first(obs_properties_t *props);\n\nEXPORT obs_property_t *obs_properties_get(obs_properties_t *props, const char *property);\n\nEXPORT obs_properties_t *obs_properties_get_parent(obs_properties_t *props);\n\n/** Remove a property from a properties list.\n *\n * Removes a property from a properties list. Only valid in either\n * get_properties or modified_callback(2). modified_callback(2) must return\n * true so that all UI properties are rebuilt and returning false is undefined\n * behavior.\n *\n * @param props Properties to remove from.\n * @param property Name of the property to remove.\n */\nEXPORT void obs_properties_remove_by_name(obs_properties_t *props, const char *property);\n\n/**\n * Applies settings to the properties by calling all the necessary\n * modification callbacks\n */\nEXPORT void obs_properties_apply_settings(obs_properties_t *props, obs_data_t *settings);\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Callback for when a button property is clicked.  If the properties\n * need to be refreshed due to changes to the property layout, return true,\n * otherwise return false.\n */\ntypedef bool (*obs_property_clicked_t)(obs_properties_t *props, obs_property_t *property, void *data);\n\nEXPORT obs_property_t *obs_properties_add_bool(obs_properties_t *props, const char *name, const char *description);\n\nEXPORT obs_property_t *obs_properties_add_int(obs_properties_t *props, const char *name, const char *description,\n\t\t\t\t\t      int min, int max, int step);\n\nEXPORT obs_property_t *obs_properties_add_float(obs_properties_t *props, const char *name, const char *description,\n\t\t\t\t\t\tdouble min, double max, double step);\n\nEXPORT obs_property_t *obs_properties_add_int_slider(obs_properties_t *props, const char *name, const char *description,\n\t\t\t\t\t\t     int min, int max, int step);\n\nEXPORT obs_property_t *obs_properties_add_float_slider(obs_properties_t *props, const char *name,\n\t\t\t\t\t\t       const char *description, double min, double max, double step);\n\nEXPORT obs_property_t *obs_properties_add_text(obs_properties_t *props, const char *name, const char *description,\n\t\t\t\t\t       enum obs_text_type type);\n\n/**\n * Adds a 'path' property.  Can be a directory or a file.\n *\n * If target is a file path, the filters should be this format, separated by\n * double semicolons, and extensions separated by space:\n *   \"Example types 1 and 2 (*.ex1 *.ex2);;Example type 3 (*.ex3)\"\n *\n * @param  props        Properties object\n * @param  name         Settings name\n * @param  description  Description (display name) of the property\n * @param  type         Type of path (directory or file)\n * @param  filter       If type is a file path, then describes the file filter\n *                      that the user can browse.  Items are separated via\n *                      double semicolons.  If multiple file types in a\n *                      filter, separate with space.\n */\nEXPORT obs_property_t *obs_properties_add_path(obs_properties_t *props, const char *name, const char *description,\n\t\t\t\t\t       enum obs_path_type type, const char *filter, const char *default_path);\n\nEXPORT obs_property_t *obs_properties_add_list(obs_properties_t *props, const char *name, const char *description,\n\t\t\t\t\t       enum obs_combo_type type, enum obs_combo_format format);\n\nEXPORT obs_property_t *obs_properties_add_color(obs_properties_t *props, const char *name, const char *description);\n\nEXPORT obs_property_t *obs_properties_add_color_alpha(obs_properties_t *props, const char *name,\n\t\t\t\t\t\t      const char *description);\n\nEXPORT obs_property_t *obs_properties_add_button(obs_properties_t *props, const char *name, const char *text,\n\t\t\t\t\t\t obs_property_clicked_t callback);\n\nEXPORT obs_property_t *obs_properties_add_button2(obs_properties_t *props, const char *name, const char *text,\n\t\t\t\t\t\t  obs_property_clicked_t callback, void *priv);\n\n/**\n * Adds a font selection property.\n *\n * A font is an obs_data sub-object which contains the following items:\n *   face:   face name string\n *   style:  style name string\n *   size:   size integer\n *   flags:  font flags integer (OBS_FONT_* defined above)\n */\nEXPORT obs_property_t *obs_properties_add_font(obs_properties_t *props, const char *name, const char *description);\n\nEXPORT obs_property_t *obs_properties_add_editable_list(obs_properties_t *props, const char *name,\n\t\t\t\t\t\t\tconst char *description, enum obs_editable_list_type type,\n\t\t\t\t\t\t\tconst char *filter, const char *default_path);\n\nEXPORT obs_property_t *obs_properties_add_frame_rate(obs_properties_t *props, const char *name,\n\t\t\t\t\t\t     const char *description);\n\nEXPORT obs_property_t *obs_properties_add_group(obs_properties_t *props, const char *name, const char *description,\n\t\t\t\t\t\tenum obs_group_type type, obs_properties_t *group);\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Optional callback for when a property is modified.  If the properties\n * need to be refreshed due to changes to the property layout, return true,\n * otherwise return false.\n */\ntypedef bool (*obs_property_modified_t)(obs_properties_t *props, obs_property_t *property, obs_data_t *settings);\ntypedef bool (*obs_property_modified2_t)(void *priv, obs_properties_t *props, obs_property_t *property,\n\t\t\t\t\t obs_data_t *settings);\n\nEXPORT void obs_property_set_modified_callback(obs_property_t *p, obs_property_modified_t modified);\nEXPORT void obs_property_set_modified_callback2(obs_property_t *p, obs_property_modified2_t modified, void *priv);\n\nEXPORT bool obs_property_modified(obs_property_t *p, obs_data_t *settings);\nEXPORT bool obs_property_button_clicked(obs_property_t *p, void *obj);\n\nEXPORT void obs_property_set_visible(obs_property_t *p, bool visible);\nEXPORT void obs_property_set_enabled(obs_property_t *p, bool enabled);\n\nEXPORT void obs_property_set_description(obs_property_t *p, const char *description);\nEXPORT void obs_property_set_long_description(obs_property_t *p, const char *long_description);\n\nEXPORT const char *obs_property_name(obs_property_t *p);\nEXPORT const char *obs_property_description(obs_property_t *p);\nEXPORT const char *obs_property_long_description(obs_property_t *p);\nEXPORT enum obs_property_type obs_property_get_type(obs_property_t *p);\nEXPORT bool obs_property_enabled(obs_property_t *p);\nEXPORT bool obs_property_visible(obs_property_t *p);\n\nEXPORT bool obs_property_next(obs_property_t **p);\n\nEXPORT int obs_property_int_min(obs_property_t *p);\nEXPORT int obs_property_int_max(obs_property_t *p);\nEXPORT int obs_property_int_step(obs_property_t *p);\nEXPORT enum obs_number_type obs_property_int_type(obs_property_t *p);\nEXPORT const char *obs_property_int_suffix(obs_property_t *p);\nEXPORT double obs_property_float_min(obs_property_t *p);\nEXPORT double obs_property_float_max(obs_property_t *p);\nEXPORT double obs_property_float_step(obs_property_t *p);\nEXPORT enum obs_number_type obs_property_float_type(obs_property_t *p);\nEXPORT const char *obs_property_float_suffix(obs_property_t *p);\nEXPORT enum obs_text_type obs_property_text_type(obs_property_t *p);\nEXPORT bool obs_property_text_monospace(obs_property_t *p);\nEXPORT enum obs_text_info_type obs_property_text_info_type(obs_property_t *p);\nEXPORT bool obs_property_text_info_word_wrap(obs_property_t *p);\nEXPORT enum obs_path_type obs_property_path_type(obs_property_t *p);\nEXPORT const char *obs_property_path_filter(obs_property_t *p);\nEXPORT const char *obs_property_path_default_path(obs_property_t *p);\nEXPORT enum obs_combo_type obs_property_list_type(obs_property_t *p);\nEXPORT enum obs_combo_format obs_property_list_format(obs_property_t *p);\n\nEXPORT void obs_property_int_set_limits(obs_property_t *p, int min, int max, int step);\nEXPORT void obs_property_float_set_limits(obs_property_t *p, double min, double max, double step);\nEXPORT void obs_property_int_set_suffix(obs_property_t *p, const char *suffix);\nEXPORT void obs_property_float_set_suffix(obs_property_t *p, const char *suffix);\nEXPORT void obs_property_text_set_monospace(obs_property_t *p, bool monospace);\nEXPORT void obs_property_text_set_info_type(obs_property_t *p, enum obs_text_info_type type);\nEXPORT void obs_property_text_set_info_word_wrap(obs_property_t *p, bool word_wrap);\n\nEXPORT void obs_property_button_set_type(obs_property_t *p, enum obs_button_type type);\nEXPORT void obs_property_button_set_url(obs_property_t *p, char *url);\n\nEXPORT void obs_property_list_clear(obs_property_t *p);\n\nEXPORT size_t obs_property_list_add_string(obs_property_t *p, const char *name, const char *val);\nEXPORT size_t obs_property_list_add_int(obs_property_t *p, const char *name, long long val);\nEXPORT size_t obs_property_list_add_float(obs_property_t *p, const char *name, double val);\nEXPORT size_t obs_property_list_add_bool(obs_property_t *p, const char *name, bool val);\n\nEXPORT void obs_property_list_insert_string(obs_property_t *p, size_t idx, const char *name, const char *val);\nEXPORT void obs_property_list_insert_int(obs_property_t *p, size_t idx, const char *name, long long val);\nEXPORT void obs_property_list_insert_float(obs_property_t *p, size_t idx, const char *name, double val);\nEXPORT void obs_property_list_insert_bool(obs_property_t *p, size_t idx, const char *name, bool val);\n\nEXPORT void obs_property_list_item_disable(obs_property_t *p, size_t idx, bool disabled);\nEXPORT bool obs_property_list_item_disabled(obs_property_t *p, size_t idx);\n\nEXPORT void obs_property_list_item_remove(obs_property_t *p, size_t idx);\n\nEXPORT size_t obs_property_list_item_count(obs_property_t *p);\nEXPORT const char *obs_property_list_item_name(obs_property_t *p, size_t idx);\nEXPORT const char *obs_property_list_item_string(obs_property_t *p, size_t idx);\nEXPORT long long obs_property_list_item_int(obs_property_t *p, size_t idx);\nEXPORT double obs_property_list_item_float(obs_property_t *p, size_t idx);\nEXPORT bool obs_property_list_item_bool(obs_property_t *p, size_t idx);\n\nEXPORT enum obs_editable_list_type obs_property_editable_list_type(obs_property_t *p);\nEXPORT const char *obs_property_editable_list_filter(obs_property_t *p);\nEXPORT const char *obs_property_editable_list_default_path(obs_property_t *p);\n\nEXPORT void obs_property_frame_rate_clear(obs_property_t *p);\nEXPORT void obs_property_frame_rate_options_clear(obs_property_t *p);\nEXPORT void obs_property_frame_rate_fps_ranges_clear(obs_property_t *p);\n\nEXPORT size_t obs_property_frame_rate_option_add(obs_property_t *p, const char *name, const char *description);\nEXPORT size_t obs_property_frame_rate_fps_range_add(obs_property_t *p, struct media_frames_per_second min,\n\t\t\t\t\t\t    struct media_frames_per_second max);\n\nEXPORT void obs_property_frame_rate_option_insert(obs_property_t *p, size_t idx, const char *name,\n\t\t\t\t\t\t  const char *description);\nEXPORT void obs_property_frame_rate_fps_range_insert(obs_property_t *p, size_t idx, struct media_frames_per_second min,\n\t\t\t\t\t\t     struct media_frames_per_second max);\n\nEXPORT size_t obs_property_frame_rate_options_count(obs_property_t *p);\nEXPORT const char *obs_property_frame_rate_option_name(obs_property_t *p, size_t idx);\nEXPORT const char *obs_property_frame_rate_option_description(obs_property_t *p, size_t idx);\n\nEXPORT size_t obs_property_frame_rate_fps_ranges_count(obs_property_t *p);\nEXPORT struct media_frames_per_second obs_property_frame_rate_fps_range_min(obs_property_t *p, size_t idx);\nEXPORT struct media_frames_per_second obs_property_frame_rate_fps_range_max(obs_property_t *p, size_t idx);\n\nEXPORT enum obs_group_type obs_property_group_type(obs_property_t *p);\nEXPORT obs_properties_t *obs_property_group_content(obs_property_t *p);\n\nEXPORT enum obs_button_type obs_property_button_type(obs_property_t *p);\nEXPORT const char *obs_property_button_url(obs_property_t *p);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-scene.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n                          Philippe Groarke <philippe.groarke@gmail.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"util/threading.h\"\n#include \"util/util_uint64.h\"\n#include \"graphics/math-defs.h\"\n#include \"obs-scene.h\"\n#include \"obs-internal.h\"\n\nconst struct obs_source_info group_info;\n\nstatic void resize_group(obs_sceneitem_t *group, bool scene_resize);\nstatic void resize_scene(obs_scene_t *scene);\nstatic void signal_parent(obs_scene_t *parent, const char *name, calldata_t *params);\nstatic void get_ungrouped_transform(obs_sceneitem_t *group, obs_sceneitem_t *item, struct vec2 *pos, struct vec2 *scale,\n\t\t\t\t    float *rot);\nstatic inline bool crop_enabled(const struct obs_sceneitem_crop *crop);\nstatic inline bool item_texture_enabled(const struct obs_scene_item *item);\nstatic void init_hotkeys(obs_scene_t *scene, obs_sceneitem_t *item, const char *name);\n\ntypedef DARRAY(struct obs_scene_item *) obs_scene_item_ptr_array_t;\n\n/* NOTE: For proper mutex lock order (preventing mutual cross-locks), never\n * lock the graphics mutex inside either of the scene mutexes.\n *\n * Another thing that must be done to prevent that cross-lock (and improve\n * performance), is to not create/release/update sources within the scene\n * mutexes.\n *\n * It's okay to lock the graphics mutex before locking either of the scene\n * mutexes, but not after.\n */\n\nstatic const char *obs_scene_signals[] = {\n\t\"void item_add(ptr scene, ptr item)\",\n\t\"void item_remove(ptr scene, ptr item)\",\n\t\"void reorder(ptr scene)\",\n\t\"void refresh(ptr scene)\",\n\t\"void item_visible(ptr scene, ptr item, bool visible)\",\n\t\"void item_select(ptr scene, ptr item)\",\n\t\"void item_deselect(ptr scene, ptr item)\",\n\t\"void item_transform(ptr scene, ptr item)\",\n\t\"void item_locked(ptr scene, ptr item, bool locked)\",\n\tNULL,\n};\n\nstatic const struct {\n\tenum gs_blend_type src_color;\n\tenum gs_blend_type src_alpha;\n\tenum gs_blend_type dst_color;\n\tenum gs_blend_type dst_alpha;\n\tenum gs_blend_op_type op;\n} obs_blend_mode_params[] = {\n\t/* clang-format off */\n\t// OBS_BLEND_NORMAL\n\t{\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_INVSRCALPHA,\n\t\tGS_BLEND_INVSRCALPHA,\n\t\tGS_BLEND_OP_ADD,\n\t},\n\t// OBS_BLEND_ADDITIVE\n\t{\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_OP_ADD,\n\t},\n\t// OBS_BLEND_SUBTRACT\n\t{\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_OP_REVERSE_SUBTRACT,\n\t},\n\t// OBS_BLEND_SCREEN\n\t{\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_INVSRCCOLOR,\n\t\tGS_BLEND_INVSRCALPHA,\n\t\tGS_BLEND_OP_ADD\n\t},\n\t// OBS_BLEND_MULTIPLY\n\t{\n\t\tGS_BLEND_DSTCOLOR,\n\t\tGS_BLEND_DSTALPHA,\n\t\tGS_BLEND_INVSRCALPHA,\n\t\tGS_BLEND_INVSRCALPHA,\n\t\tGS_BLEND_OP_ADD\n\t},\n\t// OBS_BLEND_LIGHTEN\n\t{\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_OP_MAX,\n\t},\n\t// OBS_BLEND_DARKEN\n\t{\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_ONE,\n\t\tGS_BLEND_OP_MIN,\n\t},\n\t/* clang-format on */\n};\n\nstatic inline void signal_item_remove(struct obs_scene *parent, struct obs_scene_item *item)\n{\n\tstruct calldata params;\n\tuint8_t stack[128];\n\n\tcalldata_init_fixed(&params, stack, sizeof(stack));\n\tcalldata_set_ptr(&params, \"item\", item);\n\n\tsignal_parent(parent, \"item_remove\", &params);\n}\n\nstatic const char *scene_getname(void *unused)\n{\n\tUNUSED_PARAMETER(unused);\n\treturn \"Scene\";\n}\n\nstatic const char *group_getname(void *unused)\n{\n\tUNUSED_PARAMETER(unused);\n\treturn \"Group\";\n}\n\nstatic void *scene_create(obs_data_t *settings, struct obs_source *source)\n{\n\tstruct obs_scene *scene = bzalloc(sizeof(struct obs_scene));\n\tscene->source = source;\n\n\tif (strcmp(source->info.id, group_info.id) == 0) {\n\t\tscene->is_group = true;\n\t\tscene->custom_size = true;\n\t\tscene->cx = 0;\n\t\tscene->cy = 0;\n\t}\n\n\tsignal_handler_add_array(obs_source_get_signal_handler(source), obs_scene_signals);\n\n\tif (pthread_mutex_init_recursive(&scene->audio_mutex) != 0) {\n\t\tblog(LOG_ERROR, \"scene_create: Couldn't initialize audio \"\n\t\t\t\t\"mutex\");\n\t\tgoto fail;\n\t}\n\tif (pthread_mutex_init_recursive(&scene->video_mutex) != 0) {\n\t\tblog(LOG_ERROR, \"scene_create: Couldn't initialize video \"\n\t\t\t\t\"mutex\");\n\t\tgoto fail;\n\t}\n\n\tscene->absolute_coordinates = obs_data_get_bool(obs->data.private_data, \"AbsoluteCoordinates\");\n\n\tUNUSED_PARAMETER(settings);\n\treturn scene;\n\nfail:\n\tbfree(scene);\n\treturn NULL;\n}\n\n#define audio_lock(scene) pthread_mutex_lock(&scene->audio_mutex)\n#define video_lock(scene) pthread_mutex_lock(&scene->video_mutex)\n#define audio_unlock(scene) pthread_mutex_unlock(&scene->audio_mutex)\n#define video_unlock(scene) pthread_mutex_unlock(&scene->video_mutex)\n\nstatic inline void full_lock(struct obs_scene *scene)\n{\n\tvideo_lock(scene);\n\taudio_lock(scene);\n}\n\nstatic inline void full_unlock(struct obs_scene *scene)\n{\n\taudio_unlock(scene);\n\tvideo_unlock(scene);\n}\n\nstatic void obs_sceneitem_remove_internal(obs_sceneitem_t *item);\n\nstatic void remove_all_items(struct obs_scene *scene)\n{\n\tstruct obs_scene_item *item;\n\tobs_scene_item_ptr_array_t items;\n\n\tda_init(items);\n\n\tfull_lock(scene);\n\n\titem = scene->first_item;\n\n\twhile (item) {\n\t\tstruct obs_scene_item *del_item = item;\n\t\titem = item->next;\n\n\t\tobs_sceneitem_remove_internal(del_item);\n\t\tda_push_back(items, &del_item);\n\t}\n\n\tfull_unlock(scene);\n\n\tfor (size_t i = 0; i < items.num; i++)\n\t\tobs_sceneitem_release(items.array[i]);\n\tda_free(items);\n}\n\nstatic void scene_destroy(void *data)\n{\n\tstruct obs_scene *scene = data;\n\n\tremove_all_items(scene);\n\n\tpthread_mutex_destroy(&scene->video_mutex);\n\tpthread_mutex_destroy(&scene->audio_mutex);\n\tbfree(scene);\n}\n\nstatic inline bool transition_active(obs_source_t *transition)\n{\n\treturn transition && (transition->transitioning_audio || transition->transitioning_video);\n}\n\nstatic void scene_enum_sources(void *data, obs_source_enum_proc_t enum_callback, void *param, bool active)\n{\n\tstruct obs_scene *scene = data;\n\tstruct obs_scene_item *item;\n\tstruct obs_scene_item *next;\n\n\tfull_lock(scene);\n\titem = scene->first_item;\n\n\twhile (item) {\n\t\tnext = item->next;\n\n\t\tobs_sceneitem_addref(item);\n\t\tif (active) {\n\t\t\tif (item->visible && transition_active(item->show_transition))\n\t\t\t\tenum_callback(scene->source, item->show_transition, param);\n\t\t\telse if (!item->visible && transition_active(item->hide_transition))\n\t\t\t\tenum_callback(scene->source, item->hide_transition, param);\n\t\t\tif (os_atomic_load_long(&item->active_refs) > 0)\n\t\t\t\tenum_callback(scene->source, item->source, param);\n\t\t} else {\n\t\t\tif (item->show_transition)\n\t\t\t\tenum_callback(scene->source, item->show_transition, param);\n\t\t\tif (item->hide_transition)\n\t\t\t\tenum_callback(scene->source, item->hide_transition, param);\n\t\t\tenum_callback(scene->source, item->source, param);\n\t\t}\n\n\t\tobs_sceneitem_release(item);\n\n\t\titem = next;\n\t}\n\n\tfull_unlock(scene);\n}\n\nstatic void scene_enum_active_sources(void *data, obs_source_enum_proc_t enum_callback, void *param)\n{\n\tscene_enum_sources(data, enum_callback, param, true);\n}\n\nstatic void scene_enum_all_sources(void *data, obs_source_enum_proc_t enum_callback, void *param)\n{\n\tscene_enum_sources(data, enum_callback, param, false);\n}\n\nstatic inline void detach_sceneitem(struct obs_scene_item *item)\n{\n\tif (item->prev)\n\t\titem->prev->next = item->next;\n\telse\n\t\titem->parent->first_item = item->next;\n\n\tif (item->next)\n\t\titem->next->prev = item->prev;\n\n\titem->parent = NULL;\n}\n\nstatic inline void attach_sceneitem(struct obs_scene *parent, struct obs_scene_item *item, struct obs_scene_item *prev)\n{\n\titem->prev = prev;\n\titem->parent = parent;\n\n\tif (prev) {\n\t\titem->next = prev->next;\n\t\tif (prev->next)\n\t\t\tprev->next->prev = item;\n\t\tprev->next = item;\n\t} else {\n\t\titem->next = parent->first_item;\n\t\tif (parent->first_item)\n\t\t\tparent->first_item->prev = item;\n\t\tparent->first_item = item;\n\t}\n}\n\nvoid add_alignment(struct vec2 *v, uint32_t align, int cx, int cy)\n{\n\tif (align & OBS_ALIGN_RIGHT)\n\t\tv->x += (float)cx;\n\telse if ((align & OBS_ALIGN_LEFT) == 0)\n\t\tv->x += (float)(cx / 2);\n\n\tif (align & OBS_ALIGN_BOTTOM)\n\t\tv->y += (float)cy;\n\telse if ((align & OBS_ALIGN_TOP) == 0)\n\t\tv->y += (float)(cy / 2);\n}\n\nstatic uint32_t scene_getwidth(void *data);\nstatic uint32_t scene_getheight(void *data);\nstatic uint32_t canvas_getwidth(obs_weak_canvas_t *weak);\nstatic uint32_t canvas_getheight(obs_weak_canvas_t *weak);\n\nstatic inline void get_scene_dimensions(const obs_sceneitem_t *item, float *x, float *y)\n{\n\tobs_scene_t *parent = item->parent;\n\tif (!parent || (parent->is_group && !parent->source->canvas)) {\n\t\t*x = (float)obs->data.main_canvas->mix->ovi.base_width;\n\t\t*y = (float)obs->data.main_canvas->mix->ovi.base_height;\n\t} else if (parent->is_group) {\n\t\t*x = (float)canvas_getwidth(parent->source->canvas);\n\t\t*y = (float)canvas_getheight(parent->source->canvas);\n\t} else {\n\t\t*x = (float)scene_getwidth(parent);\n\t\t*y = (float)scene_getheight(parent);\n\t}\n}\n\n/* Rounds absolute pixel values to next .5. */\nstatic inline void nudge_abs_values(struct vec2 *dst, const struct vec2 *v)\n{\n\tdst->x = floorf(v->x * 2.0f + 0.5f) / 2.0f;\n\tdst->y = floorf(v->y * 2.0f + 0.5f) / 2.0f;\n}\n\nstatic inline void pos_from_absolute(struct vec2 *dst, const struct vec2 *v, const obs_sceneitem_t *item)\n{\n\tfloat x, y;\n\tget_scene_dimensions(item, &x, &y);\n\t/* Scaled so that height (y) is [-1, 1]. */\n\tdst->x = (2 * v->x - x) / y;\n\tdst->y = 2 * v->y / y - 1.0f;\n}\n\nstatic inline void pos_to_absolute(struct vec2 *dst, const struct vec2 *v, const obs_sceneitem_t *item)\n{\n\tfloat x, y;\n\tget_scene_dimensions(item, &x, &y);\n\tdst->x = (v->x * y + x) / 2;\n\tdst->y = (v->y * y + y) / 2;\n\n\t/* In order for group resizing to behave properly they need all the precision\n\t * they can get, so do not nudge their values. */\n\tif (!item->is_group && !(item->parent && item->parent->is_group))\n\t\tnudge_abs_values(dst, dst);\n}\n\nstatic inline void size_from_absolute(struct vec2 *dst, const struct vec2 *v, const obs_sceneitem_t *item)\n{\n\tfloat x, y;\n\tget_scene_dimensions(item, &x, &y);\n\t/* The height of the canvas is from [-1, 1], so 2.0f * aspect is the\n\t * full width (depending on aspect ratio). */\n\tdst->x = (2 * v->x) / y;\n\tdst->y = 2 * v->y / y;\n}\n\nstatic inline void size_to_absolute(struct vec2 *dst, const struct vec2 *v, const obs_sceneitem_t *item)\n{\n\tfloat x, y;\n\tget_scene_dimensions(item, &x, &y);\n\tdst->x = (v->x * y) / 2;\n\tdst->y = (v->y * y) / 2;\n\n\tif (!item->is_group && !(item->parent && item->parent->is_group))\n\t\tnudge_abs_values(dst, dst);\n}\n\n/* Return item's scale value scaled from original to current canvas size. */\nstatic inline void item_canvas_scale(struct vec2 *dst, const obs_sceneitem_t *item)\n{\n\t/* Groups will themselves resize so their items do not need to be\n\t * rescaled manually. Nested scenes will use the updated canvas\n\t * resolution, so they also don't need manual adjustment. */\n\tif (item->is_group || item->is_scene) {\n\t\tvec2_copy(dst, &item->scale);\n\t\treturn;\n\t}\n\n\tfloat x, y;\n\tget_scene_dimensions(item, &x, &y);\n\tfloat scale_factor = y / item->scale_ref.y;\n\tvec2_mulf(dst, &item->scale, scale_factor);\n}\n\n/* Return scale value scaled to original canvas size. */\nstatic inline void item_relative_scale(struct vec2 *dst, const struct vec2 *v, const obs_sceneitem_t *item)\n{\n\tif (item->is_group || item->is_scene) {\n\t\tvec2_copy(dst, v);\n\t\treturn;\n\t}\n\n\tfloat x, y;\n\tget_scene_dimensions(item, &x, &y);\n\tfloat scale_factor = item->scale_ref.y / y;\n\tvec2_mulf(dst, v, scale_factor);\n}\n\nstatic inline bool crop_to_bounds(const struct obs_scene_item *item)\n{\n\treturn item->crop_to_bounds &&\n\t       (item->bounds_type == OBS_BOUNDS_SCALE_OUTER || item->bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT ||\n\t\titem->bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH);\n}\n\nstatic void calculate_bounds_data(struct obs_scene_item *item, struct vec2 *origin, struct vec2 *scale, uint32_t *cx,\n\t\t\t\t  uint32_t *cy)\n{\n\tstruct vec2 bounds;\n\tif (item->absolute_coordinates)\n\t\tvec2_copy(&bounds, &item->bounds);\n\telse\n\t\tsize_to_absolute(&bounds, &item->bounds, item);\n\n\tfloat width = (float)(*cx) * fabsf(scale->x);\n\tfloat height = (float)(*cy) * fabsf(scale->y);\n\tfloat item_aspect = width / height;\n\tfloat bounds_aspect = bounds.x / bounds.y;\n\tuint32_t bounds_type = item->bounds_type;\n\tfloat width_diff, height_diff;\n\n\tif (item->bounds_type == OBS_BOUNDS_MAX_ONLY)\n\t\tif (width > bounds.x || height > bounds.y)\n\t\t\tbounds_type = OBS_BOUNDS_SCALE_INNER;\n\n\tif (bounds_type == OBS_BOUNDS_SCALE_INNER || bounds_type == OBS_BOUNDS_SCALE_OUTER) {\n\t\tbool use_width = (bounds_aspect < item_aspect);\n\t\tfloat mul;\n\n\t\tif (item->bounds_type == OBS_BOUNDS_SCALE_OUTER)\n\t\t\tuse_width = !use_width;\n\n\t\tmul = use_width ? bounds.x / width : bounds.y / height;\n\n\t\tvec2_mulf(scale, scale, mul);\n\n\t} else if (bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH) {\n\t\tvec2_mulf(scale, scale, bounds.x / width);\n\n\t} else if (bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT) {\n\t\tvec2_mulf(scale, scale, bounds.y / height);\n\n\t} else if (bounds_type == OBS_BOUNDS_STRETCH) {\n\t\tscale->x = copysignf(bounds.x / (float)(*cx), scale->x);\n\t\tscale->y = copysignf(bounds.y / (float)(*cy), scale->y);\n\t}\n\n\twidth = (float)(*cx) * scale->x;\n\theight = (float)(*cy) * scale->y;\n\n\t/* Disregards flip when calculating size diff */\n\twidth_diff = bounds.x - fabsf(width);\n\theight_diff = bounds.y - fabsf(height);\n\t*cx = (uint32_t)bounds.x;\n\t*cy = (uint32_t)bounds.y;\n\n\tadd_alignment(origin, item->bounds_align, (int)-width_diff, (int)-height_diff);\n\n\t/* Set cropping if enabled and large enough size difference exists */\n\tif (crop_to_bounds(item) && (width_diff < -0.1 || height_diff < -0.1)) {\n\t\tbool crop_width = width_diff < -0.1;\n\t\tbool crop_flipped = crop_width ? width < 0.0f : height < 0.0f;\n\n\t\tfloat crop_diff = crop_width ? width_diff : height_diff;\n\t\tfloat crop_scale = crop_width ? scale->x : scale->y;\n\t\tfloat crop_origin = crop_width ? origin->x : origin->y;\n\n\t\t/* Only get alignment for relevant axis */\n\t\tuint32_t crop_align_mask = crop_width ? OBS_ALIGN_LEFT | OBS_ALIGN_RIGHT\n\t\t\t\t\t\t      : OBS_ALIGN_TOP | OBS_ALIGN_BOTTOM;\n\t\tuint32_t crop_align = item->bounds_align & crop_align_mask;\n\n\t\t/* Cropping values need to scaled to input source */\n\t\tfloat overdraw = fabsf(crop_diff / crop_scale);\n\n\t\t/* tl = top / left, br = bottom / right */\n\t\tfloat overdraw_tl;\n\t\tif (crop_align & (OBS_ALIGN_TOP | OBS_ALIGN_LEFT))\n\t\t\toverdraw_tl = 0;\n\t\telse if (crop_align & (OBS_ALIGN_BOTTOM | OBS_ALIGN_RIGHT))\n\t\t\toverdraw_tl = overdraw;\n\t\telse\n\t\t\toverdraw_tl = overdraw / 2;\n\n\t\tfloat overdraw_br = overdraw - overdraw_tl;\n\n\t\tint crop_br, crop_tl;\n\t\tif (crop_flipped) {\n\t\t\t/* Adjust origin for flips */\n\t\t\tif (crop_align == OBS_ALIGN_CENTER)\n\t\t\t\tcrop_origin *= 2;\n\t\t\telse if (crop_align & (OBS_ALIGN_TOP | OBS_ALIGN_LEFT))\n\t\t\t\tcrop_origin -= crop_diff;\n\n\t\t\t/* Note that crops are swapped if the axis is flipped */\n\t\t\tcrop_br = (int)roundf(overdraw_tl);\n\t\t\tcrop_tl = (int)roundf(overdraw_br);\n\t\t} else {\n\t\t\tcrop_origin = 0;\n\t\t\tcrop_br = (int)roundf(overdraw_br);\n\t\t\tcrop_tl = (int)roundf(overdraw_tl);\n\t\t}\n\n\t\tif (crop_width) {\n\t\t\titem->bounds_crop.right = crop_br;\n\t\t\titem->bounds_crop.left = crop_tl;\n\t\t\torigin->x = crop_origin;\n\t\t} else {\n\t\t\titem->bounds_crop.bottom = crop_br;\n\t\t\titem->bounds_crop.top = crop_tl;\n\t\t\torigin->y = crop_origin;\n\t\t}\n\t}\n\n\t/* Makes the item stay in-place in the box if flipped */\n\torigin->x += (width < 0.0f) ? width : 0.0f;\n\torigin->y += (height < 0.0f) ? height : 0.0f;\n}\n\nstatic inline uint32_t calc_cx(const struct obs_scene_item *item, uint32_t width)\n{\n\tuint32_t crop_cx = item->crop.left + item->crop.right + item->bounds_crop.left + item->bounds_crop.right;\n\treturn (crop_cx > width) ? 2 : (width - crop_cx);\n}\n\nstatic inline uint32_t calc_cy(const struct obs_scene_item *item, uint32_t height)\n{\n\tuint32_t crop_cy = item->crop.top + item->crop.bottom + item->bounds_crop.top + item->bounds_crop.bottom;\n\treturn (crop_cy > height) ? 2 : (height - crop_cy);\n}\n\n#ifdef DEBUG_TRANSFORM\nstatic inline void log_matrix(const struct matrix4 *mat, const char *name)\n{\n\tblog(LOG_DEBUG,\n\t     \"Matrix \\\"%s\\\":\\n\"\n\t     \"┏ %9.4f %9.4f %9.4f %9.4f ┓\\n\"\n\t     \"┃ %9.4f %9.4f %9.4f %9.4f ┃\\n\"\n\t     \"┃ %9.4f %9.4f %9.4f %9.4f ┃\\n\"\n\t     \"┗ %9.4f %9.4f %9.4f %9.4f ┛\",\n\t     name, mat->x.x, mat->x.y, mat->x.z, mat->x.w, mat->y.x, mat->y.y, mat->y.z, mat->y.w, mat->z.x, mat->z.y,\n\t     mat->z.z, mat->z.w, mat->t.x, mat->t.y, mat->t.z, mat->t.w);\n}\n#endif\n\nstatic inline void update_nested_scene_crop(struct obs_scene_item *item, uint32_t width, uint32_t height)\n{\n\tif (!item->last_height || !item->last_width)\n\t\treturn;\n\n\t/* Use last size and new size to calculate factor to adjust crop by. */\n\tfloat scale_x = (float)width / (float)item->last_width;\n\tfloat scale_y = (float)height / (float)item->last_height;\n\n\titem->crop.left = (int)((float)item->crop.left * scale_x);\n\titem->crop.right = (int)((float)item->crop.right * scale_x);\n\titem->crop.top = (int)((float)item->crop.top * scale_y);\n\titem->crop.bottom = (int)((float)item->crop.bottom * scale_y);\n}\n\nstatic void update_item_transform(struct obs_scene_item *item, bool update_tex)\n{\n\tuint32_t width;\n\tuint32_t height;\n\tuint32_t cx;\n\tuint32_t cy;\n\tstruct vec2 base_origin;\n\tstruct vec2 origin;\n\tstruct vec2 scale;\n\tstruct vec2 position;\n\tstruct calldata params;\n\tuint8_t stack[128];\n\n\tif (os_atomic_load_long(&item->defer_update) > 0)\n\t\treturn;\n\n\t/* Reset bounds crop */\n\tmemset(&item->bounds_crop, 0, sizeof(item->bounds_crop));\n\n\twidth = obs_source_get_width(item->source);\n\theight = obs_source_get_height(item->source);\n\n\t/* Adjust crop on nested scenes (if any) */\n\tif (update_tex && item->is_scene)\n\t\tupdate_nested_scene_crop(item, width, height);\n\n\tcx = calc_cx(item, width);\n\tcy = calc_cy(item, height);\n\titem->last_width = width;\n\titem->last_height = height;\n\n\twidth = cx;\n\theight = cy;\n\n\tvec2_zero(&base_origin);\n\tvec2_zero(&origin);\n\n\tif (!item->absolute_coordinates) {\n\t\titem_canvas_scale(&scale, item);\n\t\tpos_to_absolute(&position, &item->pos, item);\n\t} else {\n\t\tscale = item->scale;\n\t\tposition = item->pos;\n\t}\n\n\t/* ----------------------- */\n\n\tif (item->bounds_type != OBS_BOUNDS_NONE) {\n\t\tcalculate_bounds_data(item, &origin, &scale, &cx, &cy);\n\t} else {\n\t\tcx = (uint32_t)((float)cx * scale.x);\n\t\tcy = (uint32_t)((float)cy * scale.y);\n\t}\n\n\tadd_alignment(&origin, item->align, (int)cx, (int)cy);\n\n\tmatrix4_identity(&item->draw_transform);\n\tmatrix4_scale3f(&item->draw_transform, &item->draw_transform, scale.x, scale.y, 1.0f);\n\tmatrix4_translate3f(&item->draw_transform, &item->draw_transform, -origin.x, -origin.y, 0.0f);\n\tmatrix4_rotate_aa4f(&item->draw_transform, &item->draw_transform, 0.0f, 0.0f, 1.0f, RAD(item->rot));\n\tmatrix4_translate3f(&item->draw_transform, &item->draw_transform, position.x, position.y, 0.0f);\n\n#ifdef DEBUG_TRANSFORM\n\tblog(LOG_DEBUG, \"Transform updated for \\\"%s\\\":\", obs_source_get_name(item->source));\n\tlog_matrix(&item->draw_transform, \"draw_transform\");\n#endif\n\n\titem->output_scale = scale;\n\n\t/* ----------------------- */\n\n\tif (item->bounds_type != OBS_BOUNDS_NONE) {\n\t\tif (!item->absolute_coordinates)\n\t\t\tsize_to_absolute(&scale, &item->bounds, item);\n\t\telse\n\t\t\tvec2_copy(&scale, &item->bounds);\n\t} else {\n\t\tscale.x *= (float)width;\n\t\tscale.y *= (float)height;\n\t}\n\n\titem->box_scale = scale;\n\n\tadd_alignment(&base_origin, item->align, (int)scale.x, (int)scale.y);\n\n\tmatrix4_identity(&item->box_transform);\n\tmatrix4_scale3f(&item->box_transform, &item->box_transform, scale.x, scale.y, 1.0f);\n\tmatrix4_translate3f(&item->box_transform, &item->box_transform, -base_origin.x, -base_origin.y, 0.0f);\n\tmatrix4_rotate_aa4f(&item->box_transform, &item->box_transform, 0.0f, 0.0f, 1.0f, RAD(item->rot));\n\tmatrix4_translate3f(&item->box_transform, &item->box_transform, position.x, position.y, 0.0f);\n\n#ifdef DEBUG_TRANSFORM\n\tlog_matrix(&item->draw_transform, \"box_transform\");\n#endif\n\n\t/* ----------------------- */\n\n\tcalldata_init_fixed(&params, stack, sizeof(stack));\n\tcalldata_set_ptr(&params, \"item\", item);\n\tsignal_parent(item->parent, \"item_transform\", &params);\n\n\tif (!update_tex)\n\t\treturn;\n\n\tos_atomic_set_bool(&item->update_transform, false);\n}\n\nstatic inline bool source_size_changed(struct obs_scene_item *item)\n{\n\tuint32_t width = obs_source_get_width(item->source);\n\tuint32_t height = obs_source_get_height(item->source);\n\n\treturn item->last_width != width || item->last_height != height;\n}\n\nstatic inline bool crop_enabled(const struct obs_sceneitem_crop *crop)\n{\n\treturn crop->left || crop->right || crop->top || crop->bottom;\n}\n\nstatic inline bool scale_filter_enabled(const struct obs_scene_item *item)\n{\n\treturn item->scale_filter != OBS_SCALE_DISABLE;\n}\n\nstatic inline bool default_blending_enabled(const struct obs_scene_item *item)\n{\n\treturn item->blend_type == OBS_BLEND_NORMAL;\n}\n\nstatic inline bool item_is_scene(const struct obs_scene_item *item)\n{\n\treturn item->source && item->source->info.type == OBS_SOURCE_TYPE_SCENE;\n}\n\nstatic inline bool item_texture_enabled(const struct obs_scene_item *item)\n{\n\treturn crop_enabled(&item->crop) || crop_enabled(&item->bounds_crop) || scale_filter_enabled(item) ||\n\t       (item->blend_method == OBS_BLEND_METHOD_SRGB_OFF) || !default_blending_enabled(item) ||\n\t       (item_is_scene(item) && !item->is_group);\n}\n\nstatic void render_item_texture(struct obs_scene_item *item, enum gs_color_space current_space,\n\t\t\t\tenum gs_color_space source_space)\n{\n\tgs_texture_t *tex = gs_texrender_get_texture(item->item_render);\n\tif (!tex) {\n\t\treturn;\n\t}\n\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_ITEM_TEXTURE, \"render_item_texture\");\n\n\tgs_effect_t *effect = obs->video.default_effect;\n\tenum obs_scale_type type = item->scale_filter;\n\tuint32_t cx = gs_texture_get_width(tex);\n\tuint32_t cy = gs_texture_get_height(tex);\n\n\tbool upscale = false;\n\tif (type != OBS_SCALE_DISABLE) {\n\t\tif (type == OBS_SCALE_POINT) {\n\t\t\tgs_eparam_t *image = gs_effect_get_param_by_name(effect, \"image\");\n\t\t\tgs_effect_set_next_sampler(image, obs->video.point_sampler);\n\n\t\t} else if (!close_float(item->output_scale.x, 1.0f, EPSILON) ||\n\t\t\t   !close_float(item->output_scale.y, 1.0f, EPSILON)) {\n\t\t\tif (item->output_scale.x < 0.5f || item->output_scale.y < 0.5f) {\n\t\t\t\teffect = obs->video.bilinear_lowres_effect;\n\t\t\t} else if (type == OBS_SCALE_BICUBIC) {\n\t\t\t\teffect = obs->video.bicubic_effect;\n\t\t\t} else if (type == OBS_SCALE_LANCZOS) {\n\t\t\t\teffect = obs->video.lanczos_effect;\n\t\t\t} else if (type == OBS_SCALE_AREA) {\n\t\t\t\teffect = obs->video.area_effect;\n\t\t\t\tupscale = (item->output_scale.x >= 1.0f) && (item->output_scale.y >= 1.0f);\n\t\t\t}\n\n\t\t\tgs_eparam_t *const scale_param = gs_effect_get_param_by_name(effect, \"base_dimension\");\n\t\t\tif (scale_param) {\n\t\t\t\tstruct vec2 base_res = {(float)cx, (float)cy};\n\n\t\t\t\tgs_effect_set_vec2(scale_param, &base_res);\n\t\t\t}\n\n\t\t\tgs_eparam_t *const scale_i_param = gs_effect_get_param_by_name(effect, \"base_dimension_i\");\n\t\t\tif (scale_i_param) {\n\t\t\t\tstruct vec2 base_res_i = {1.0f / (float)cx, 1.0f / (float)cy};\n\n\t\t\t\tgs_effect_set_vec2(scale_i_param, &base_res_i);\n\t\t\t}\n\t\t}\n\t}\n\n\tfloat multiplier = 1.f;\n\n\tif (current_space == GS_CS_709_SCRGB) {\n\t\tswitch (source_space) {\n\t\tcase GS_CS_SRGB:\n\t\tcase GS_CS_SRGB_16F:\n\t\tcase GS_CS_709_EXTENDED:\n\t\t\tmultiplier = obs_get_video_sdr_white_level() / 80.f;\n\t\t\tbreak;\n\t\tcase GS_CS_709_SCRGB:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (source_space == GS_CS_709_SCRGB) {\n\t\tswitch (current_space) {\n\t\tcase GS_CS_SRGB:\n\t\tcase GS_CS_SRGB_16F:\n\t\tcase GS_CS_709_EXTENDED:\n\t\t\tmultiplier = 80.f / obs_get_video_sdr_white_level();\n\t\t\tbreak;\n\t\tcase GS_CS_709_SCRGB:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst char *tech_name = \"Draw\";\n\tif (upscale) {\n\t\ttech_name = \"DrawUpscale\";\n\t\tswitch (source_space) {\n\t\tcase GS_CS_SRGB:\n\t\tcase GS_CS_SRGB_16F:\n\t\t\tif (current_space == GS_CS_709_SCRGB)\n\t\t\t\ttech_name = \"DrawUpscaleMultiply\";\n\t\t\tbreak;\n\t\tcase GS_CS_709_EXTENDED:\n\t\t\tif (current_space == GS_CS_SRGB || current_space == GS_CS_SRGB_16F)\n\t\t\t\ttech_name = \"DrawUpscaleTonemap\";\n\t\t\telse if (current_space == GS_CS_709_SCRGB)\n\t\t\t\ttech_name = \"DrawUpscaleMultiply\";\n\t\t\tbreak;\n\t\tcase GS_CS_709_SCRGB:\n\t\t\tif (current_space == GS_CS_SRGB || current_space == GS_CS_SRGB_16F)\n\t\t\t\ttech_name = \"DrawUpscaleMultiplyTonemap\";\n\t\t\telse if (current_space == GS_CS_709_EXTENDED)\n\t\t\t\ttech_name = \"DrawUpscaleMultiply\";\n\t\t\tbreak;\n\t\t}\n\t} else {\n\t\tswitch (source_space) {\n\t\tcase GS_CS_SRGB:\n\t\tcase GS_CS_SRGB_16F:\n\t\t\tif (current_space == GS_CS_709_SCRGB)\n\t\t\t\ttech_name = \"DrawMultiply\";\n\t\t\tbreak;\n\t\tcase GS_CS_709_EXTENDED:\n\t\t\tif (current_space == GS_CS_SRGB || current_space == GS_CS_SRGB_16F)\n\t\t\t\ttech_name = \"DrawTonemap\";\n\t\t\telse if (current_space == GS_CS_709_SCRGB)\n\t\t\t\ttech_name = \"DrawMultiply\";\n\t\t\tbreak;\n\t\tcase GS_CS_709_SCRGB:\n\t\t\tif (current_space == GS_CS_SRGB || current_space == GS_CS_SRGB_16F)\n\t\t\t\ttech_name = \"DrawMultiplyTonemap\";\n\t\t\telse if (current_space == GS_CS_709_EXTENDED)\n\t\t\t\ttech_name = \"DrawMultiply\";\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tgs_eparam_t *const multiplier_param = gs_effect_get_param_by_name(effect, \"multiplier\");\n\tif (multiplier_param)\n\t\tgs_effect_set_float(multiplier_param, multiplier);\n\n\tgs_blend_state_push();\n\n\tgs_blend_function_separate(obs_blend_mode_params[item->blend_type].src_color,\n\t\t\t\t   obs_blend_mode_params[item->blend_type].dst_color,\n\t\t\t\t   obs_blend_mode_params[item->blend_type].src_alpha,\n\t\t\t\t   obs_blend_mode_params[item->blend_type].dst_alpha);\n\tgs_blend_op(obs_blend_mode_params[item->blend_type].op);\n\n\twhile (gs_effect_loop(effect, tech_name))\n\t\tobs_source_draw(tex, 0, 0, 0, 0, 0);\n\n\tgs_blend_state_pop();\n\n\tGS_DEBUG_MARKER_END();\n}\n\nstatic bool are_texcoords_centered(struct matrix4 *m)\n{\n\tstatic const struct matrix4 identity = {\n\t\t{1.0f, 0.0f, 0.0f, 0.0f},\n\t\t{0.0f, 1.0f, 0.0f, 0.0f},\n\t\t{0.0f, 0.0f, 1.0f, 0.0f},\n\t\t{0.0f, 0.0f, 0.0f, 1.0f},\n\t};\n\tstruct matrix4 copy = identity;\n\tcopy.t.x = floorf(m->t.x);\n\tcopy.t.y = floorf(m->t.y);\n\treturn memcmp(m, &copy, sizeof(*m)) == 0;\n}\n\nstatic inline void render_item(struct obs_scene_item *item)\n{\n\tGS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_ITEM, \"Item: %s\", obs_source_get_name(item->source));\n\n\tconst bool use_texrender = item_texture_enabled(item);\n\n\tobs_source_t *const source = item->source;\n\tconst enum gs_color_space current_space = gs_get_color_space();\n\tconst enum gs_color_space source_space = obs_source_get_color_space(source, 1, &current_space);\n\tconst enum gs_color_format format = gs_get_format_from_space(source_space);\n\n\tif (item->item_render && (!use_texrender || (gs_texrender_get_format(item->item_render) != format))) {\n\t\tgs_texrender_destroy(item->item_render);\n\t\titem->item_render = NULL;\n\t}\n\n\tif (!item->item_render && use_texrender) {\n\t\titem->item_render = gs_texrender_create(format, GS_ZS_NONE);\n\t}\n\n\tif (item->item_render) {\n\t\tuint32_t width = obs_source_get_width(item->source);\n\t\tuint32_t height = obs_source_get_height(item->source);\n\n\t\tif (!width || !height) {\n\t\t\tgoto cleanup;\n\t\t}\n\n\t\tuint32_t cx = calc_cx(item, width);\n\t\tuint32_t cy = calc_cy(item, height);\n\n\t\tif (cx && cy && gs_texrender_begin_with_color_space(item->item_render, cx, cy, source_space)) {\n\t\t\tfloat cx_scale = (float)width / (float)cx;\n\t\t\tfloat cy_scale = (float)height / (float)cy;\n\t\t\tstruct vec4 clear_color;\n\n\t\t\tvec4_zero(&clear_color);\n\t\t\tgs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);\n\t\t\tgs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f);\n\n\t\t\tgs_matrix_scale3f(cx_scale, cy_scale, 1.0f);\n\t\t\tgs_matrix_translate3f(-(float)(item->crop.left + item->bounds_crop.left),\n\t\t\t\t\t      -(float)(item->crop.top + item->bounds_crop.top), 0.0f);\n\n\t\t\tif (item->user_visible && transition_active(item->show_transition)) {\n\t\t\t\tconst int cx = obs_source_get_width(item->source);\n\t\t\t\tconst int cy = obs_source_get_height(item->source);\n\t\t\t\tobs_transition_set_size(item->show_transition, cx, cy);\n\t\t\t\tobs_source_video_render(item->show_transition);\n\t\t\t} else if (!item->user_visible && transition_active(item->hide_transition)) {\n\t\t\t\tconst int cx = obs_source_get_width(item->source);\n\t\t\t\tconst int cy = obs_source_get_height(item->source);\n\t\t\t\tobs_transition_set_size(item->hide_transition, cx, cy);\n\t\t\t\tobs_source_video_render(item->hide_transition);\n\t\t\t} else {\n\t\t\t\tobs_source_set_texcoords_centered(item->source, true);\n\t\t\t\tobs_source_video_render(item->source);\n\t\t\t\tobs_source_set_texcoords_centered(item->source, false);\n\t\t\t}\n\n\t\t\tgs_texrender_end(item->item_render);\n\t\t}\n\t}\n\n\tconst bool linear_srgb = !item->item_render || (item->blend_method != OBS_BLEND_METHOD_SRGB_OFF);\n\tconst bool previous = gs_set_linear_srgb(linear_srgb);\n\tgs_matrix_push();\n\tgs_matrix_mul(&item->draw_transform);\n\tif (item->item_render) {\n\t\trender_item_texture(item, current_space, source_space);\n\t} else if (item->user_visible && transition_active(item->show_transition)) {\n\t\tconst int cx = obs_source_get_width(item->source);\n\t\tconst int cy = obs_source_get_height(item->source);\n\t\tobs_transition_set_size(item->show_transition, cx, cy);\n\t\tobs_source_video_render(item->show_transition);\n\t} else if (!item->user_visible && transition_active(item->hide_transition)) {\n\t\tconst int cx = obs_source_get_width(item->source);\n\t\tconst int cy = obs_source_get_height(item->source);\n\t\tobs_transition_set_size(item->hide_transition, cx, cy);\n\t\tobs_source_video_render(item->hide_transition);\n\t} else {\n\t\tconst bool centered = are_texcoords_centered(&item->draw_transform);\n\t\tobs_source_set_texcoords_centered(item->source, centered);\n\t\tobs_source_video_render(item->source);\n\t\tobs_source_set_texcoords_centered(item->source, false);\n\t}\n\tgs_matrix_pop();\n\tgs_set_linear_srgb(previous);\n\ncleanup:\n\tGS_DEBUG_MARKER_END();\n}\n\nstatic void scene_video_tick(void *data, float seconds)\n{\n\tstruct obs_scene *scene = data;\n\tstruct obs_scene_item *item;\n\n\tvideo_lock(scene);\n\titem = scene->first_item;\n\twhile (item) {\n\t\tif (item->item_render)\n\t\t\tgs_texrender_reset(item->item_render);\n\t\titem = item->next;\n\t}\n\tvideo_unlock(scene);\n\n\tUNUSED_PARAMETER(seconds);\n}\n\n/* assumes video lock */\nstatic void update_transforms_and_prune_sources(obs_scene_t *scene, obs_scene_item_ptr_array_t *remove_items,\n\t\t\t\t\t\tobs_sceneitem_t *group_sceneitem, bool scene_size_changed)\n{\n\tstruct obs_scene_item *item = scene->first_item;\n\tbool rebuild_group = group_sceneitem && os_atomic_load_bool(&group_sceneitem->update_group_resize);\n\n\twhile (item) {\n\t\tif (obs_source_removed(item->source)) {\n\t\t\tstruct obs_scene_item *del_item = item;\n\t\t\titem = item->next;\n\n\t\t\tobs_sceneitem_remove_internal(del_item);\n\t\t\tda_push_back(*remove_items, &del_item);\n\t\t\trebuild_group = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (item->is_group) {\n\t\t\tobs_scene_t *group_scene = item->source->context.data;\n\n\t\t\tvideo_lock(group_scene);\n\t\t\tupdate_transforms_and_prune_sources(group_scene, remove_items, item, scene_size_changed);\n\t\t\tvideo_unlock(group_scene);\n\t\t}\n\n\t\tif (os_atomic_load_bool(&item->update_transform) || source_size_changed(item) || scene_size_changed) {\n\n\t\t\tupdate_item_transform(item, true);\n\t\t\trebuild_group = true;\n\t\t}\n\n\t\titem = item->next;\n\t}\n\n\tif (rebuild_group && group_sceneitem)\n\t\tresize_group(group_sceneitem, scene_size_changed);\n}\n\nstatic inline bool scene_size_changed(obs_scene_t *scene)\n{\n\tuint32_t width = scene_getwidth(scene);\n\tuint32_t height = scene_getheight(scene);\n\n\tif (width == scene->last_width && height == scene->last_height)\n\t\treturn false;\n\n\tscene->last_width = width;\n\tscene->last_height = height;\n\treturn true;\n}\n\nstatic void scene_video_render(void *data, gs_effect_t *effect)\n{\n\tobs_scene_item_ptr_array_t remove_items;\n\tstruct obs_scene *scene = data;\n\tstruct obs_scene_item *item;\n\n\tda_init(remove_items);\n\n\tvideo_lock(scene);\n\n\tif (!scene->is_group) {\n\t\tbool size_changed = scene_size_changed(scene);\n\t\tupdate_transforms_and_prune_sources(scene, &remove_items, NULL, size_changed);\n\t}\n\n\tgs_blend_state_push();\n\tgs_reset_blend_state();\n\n\titem = scene->first_item;\n\twhile (item) {\n\t\tif (item->user_visible || transition_active(item->hide_transition))\n\t\t\trender_item(item);\n\n\t\titem = item->next;\n\t}\n\n\tgs_blend_state_pop();\n\n\tvideo_unlock(scene);\n\n\tfor (size_t i = 0; i < remove_items.num; i++)\n\t\tobs_sceneitem_release(remove_items.array[i]);\n\tda_free(remove_items);\n\n\tUNUSED_PARAMETER(effect);\n}\n\nstatic void set_visibility(struct obs_scene_item *item, bool vis)\n{\n\tpthread_mutex_lock(&item->actions_mutex);\n\n\tda_resize(item->audio_actions, 0);\n\n\tif (os_atomic_load_long(&item->active_refs) > 0) {\n\t\tif (!vis)\n\t\t\tobs_source_remove_active_child(item->parent->source, item->source);\n\t} else if (vis) {\n\t\tobs_source_add_active_child(item->parent->source, item->source);\n\t}\n\n\tos_atomic_set_long(&item->active_refs, vis ? 1 : 0);\n\titem->visible = vis;\n\titem->user_visible = vis;\n\n\tpthread_mutex_unlock(&item->actions_mutex);\n}\n\nstatic obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene, obs_source_t *source, obs_sceneitem_t *insert_after,\n\t\t\t\t\t       int64_t id);\n\nstatic void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)\n{\n\tconst char *name = obs_data_get_string(item_data, \"name\");\n\tconst char *src_uuid = obs_data_get_string(item_data, \"source_uuid\");\n\tobs_source_t *source = NULL;\n\tconst char *scale_filter_str;\n\tconst char *blend_method_str;\n\tconst char *blend_str;\n\tstruct obs_scene_item *item;\n\tstruct calldata params;\n\tuint8_t stack[128];\n\tbool visible;\n\tbool lock;\n\n\tif (obs_data_get_bool(item_data, \"group_item_backup\"))\n\t\treturn;\n\n\tif (src_uuid && strlen(src_uuid) == UUID_STR_LENGTH)\n\t\tsource = obs_get_source_by_uuid(src_uuid);\n\n\t/* Fall back to name if UUID was not found or is not set. */\n\tif (!source)\n\t\tsource = obs_get_source_by_name(name);\n\n\tif (!source) {\n\t\tblog(LOG_WARNING,\n\t\t     \"[scene_load_item] Source %s not \"\n\t\t     \"found!\",\n\t\t     name);\n\t\treturn;\n\t}\n\n\titem = obs_scene_add_internal(scene, source, NULL, obs_data_get_int(item_data, \"id\"));\n\tif (!item) {\n\t\tblog(LOG_WARNING,\n\t\t     \"[scene_load_item] Could not add source '%s' \"\n\t\t     \"to scene '%s'!\",\n\t\t     name, obs_source_get_name(scene->source));\n\n\t\tobs_source_release(source);\n\t\treturn;\n\t}\n\n\tcalldata_init_fixed(&params, stack, sizeof(stack));\n\tcalldata_set_ptr(&params, \"scene\", scene);\n\tcalldata_set_ptr(&params, \"item\", item);\n\tsignal_handler_signal(scene->source->context.signals, \"item_add\", &params);\n\n\titem->is_group = strcmp(source->info.id, group_info.id) == 0;\n\n\tobs_data_set_default_int(item_data, \"align\", OBS_ALIGN_TOP | OBS_ALIGN_LEFT);\n\n\titem->rot = (float)obs_data_get_double(item_data, \"rot\");\n\titem->align = (uint32_t)obs_data_get_int(item_data, \"align\");\n\tvisible = obs_data_get_bool(item_data, \"visible\");\n\tlock = obs_data_get_bool(item_data, \"locked\");\n\n\tif (!item->absolute_coordinates && obs_data_has_user_value(item_data, \"pos_rel\") &&\n\t    obs_data_has_user_value(item_data, \"scale_rel\") && obs_data_has_user_value(item_data, \"scale_ref\")) {\n\t\tobs_data_get_vec2(item_data, \"pos_rel\", &item->pos);\n\t\tobs_data_get_vec2(item_data, \"scale_rel\", &item->scale);\n\t\tobs_data_get_vec2(item_data, \"scale_ref\", &item->scale_ref);\n\t} else {\n\t\tobs_data_get_vec2(item_data, \"pos\", &item->pos);\n\t\tif (!item->absolute_coordinates)\n\t\t\tpos_from_absolute(&item->pos, &item->pos, item);\n\t\tobs_data_get_vec2(item_data, \"scale\", &item->scale);\n\t}\n\n\tobs_data_release(item->private_settings);\n\titem->private_settings = obs_data_get_obj(item_data, \"private_settings\");\n\tif (!item->private_settings)\n\t\titem->private_settings = obs_data_create();\n\n\tset_visibility(item, visible);\n\tobs_sceneitem_set_locked(item, lock);\n\n\titem->bounds_type = (enum obs_bounds_type)obs_data_get_int(item_data, \"bounds_type\");\n\titem->bounds_align = (uint32_t)obs_data_get_int(item_data, \"bounds_align\");\n\titem->crop_to_bounds = obs_data_get_bool(item_data, \"bounds_crop\");\n\tobs_data_get_vec2(item_data, \"bounds\", &item->bounds);\n\n\tif (!item->absolute_coordinates && obs_data_has_user_value(item_data, \"bounds_rel\")) {\n\t\tobs_data_get_vec2(item_data, \"bounds_rel\", &item->bounds);\n\t} else {\n\t\tobs_data_get_vec2(item_data, \"bounds\", &item->bounds);\n\t\tif (!item->absolute_coordinates)\n\t\t\tsize_from_absolute(&item->bounds, &item->bounds, item);\n\t}\n\n\titem->crop.left = (uint32_t)obs_data_get_int(item_data, \"crop_left\");\n\titem->crop.top = (uint32_t)obs_data_get_int(item_data, \"crop_top\");\n\titem->crop.right = (uint32_t)obs_data_get_int(item_data, \"crop_right\");\n\titem->crop.bottom = (uint32_t)obs_data_get_int(item_data, \"crop_bottom\");\n\n\tscale_filter_str = obs_data_get_string(item_data, \"scale_filter\");\n\titem->scale_filter = OBS_SCALE_DISABLE;\n\n\tif (scale_filter_str) {\n\t\tif (astrcmpi(scale_filter_str, \"point\") == 0)\n\t\t\titem->scale_filter = OBS_SCALE_POINT;\n\t\telse if (astrcmpi(scale_filter_str, \"bilinear\") == 0)\n\t\t\titem->scale_filter = OBS_SCALE_BILINEAR;\n\t\telse if (astrcmpi(scale_filter_str, \"bicubic\") == 0)\n\t\t\titem->scale_filter = OBS_SCALE_BICUBIC;\n\t\telse if (astrcmpi(scale_filter_str, \"lanczos\") == 0)\n\t\t\titem->scale_filter = OBS_SCALE_LANCZOS;\n\t\telse if (astrcmpi(scale_filter_str, \"area\") == 0)\n\t\t\titem->scale_filter = OBS_SCALE_AREA;\n\t}\n\n\tblend_method_str = obs_data_get_string(item_data, \"blend_method\");\n\titem->blend_method = OBS_BLEND_METHOD_DEFAULT;\n\n\tif (blend_method_str) {\n\t\tif (astrcmpi(blend_method_str, \"srgb_off\") == 0)\n\t\t\titem->blend_method = OBS_BLEND_METHOD_SRGB_OFF;\n\t}\n\n\tblend_str = obs_data_get_string(item_data, \"blend_type\");\n\titem->blend_type = OBS_BLEND_NORMAL;\n\n\tif (blend_str) {\n\t\tif (astrcmpi(blend_str, \"additive\") == 0)\n\t\t\titem->blend_type = OBS_BLEND_ADDITIVE;\n\t\telse if (astrcmpi(blend_str, \"subtract\") == 0)\n\t\t\titem->blend_type = OBS_BLEND_SUBTRACT;\n\t\telse if (astrcmpi(blend_str, \"screen\") == 0)\n\t\t\titem->blend_type = OBS_BLEND_SCREEN;\n\t\telse if (astrcmpi(blend_str, \"multiply\") == 0)\n\t\t\titem->blend_type = OBS_BLEND_MULTIPLY;\n\t\telse if (astrcmpi(blend_str, \"lighten\") == 0)\n\t\t\titem->blend_type = OBS_BLEND_LIGHTEN;\n\t\telse if (astrcmpi(blend_str, \"darken\") == 0)\n\t\t\titem->blend_type = OBS_BLEND_DARKEN;\n\t}\n\n\tobs_data_t *show_data = obs_data_get_obj(item_data, \"show_transition\");\n\tif (show_data) {\n\t\tobs_sceneitem_transition_load(item, show_data, true);\n\t\tobs_data_release(show_data);\n\t}\n\n\tobs_data_t *hide_data = obs_data_get_obj(item_data, \"hide_transition\");\n\tif (hide_data) {\n\t\tobs_sceneitem_transition_load(item, hide_data, false);\n\t\tobs_data_release(hide_data);\n\t}\n\n\tobs_source_release(source);\n\n\tupdate_item_transform(item, false);\n}\n\nstatic void scene_load(void *data, obs_data_t *settings)\n{\n\tstruct obs_scene *scene = data;\n\tobs_data_array_t *items = obs_data_get_array(settings, \"items\");\n\tsize_t count, i;\n\n\tremove_all_items(scene);\n\n\tif (obs_data_get_bool(settings, \"custom_size\")) {\n\t\tscene->cx = (uint32_t)obs_data_get_int(settings, \"cx\");\n\t\tscene->cy = (uint32_t)obs_data_get_int(settings, \"cy\");\n\t\tscene->custom_size = true;\n\t}\n\n\tif (obs_data_has_user_value(settings, \"id_counter\"))\n\t\tscene->id_counter = obs_data_get_int(settings, \"id_counter\");\n\n\tscene->absolute_coordinates = obs_data_get_bool(obs->data.private_data, \"AbsoluteCoordinates\");\n\n\tif (!items)\n\t\treturn;\n\n\tcount = obs_data_array_count(items);\n\n\tfor (i = 0; i < count; i++) {\n\t\tobs_data_t *item_data = obs_data_array_item(items, i);\n\t\tscene_load_item(scene, item_data);\n\t\tobs_data_release(item_data);\n\t}\n\n\tobs_data_array_release(items);\n}\n\nstatic void scene_save(void *data, obs_data_t *settings);\n\nstatic void scene_save_item(obs_data_array_t *array, struct obs_scene_item *item, struct obs_scene_item *backup_group)\n{\n\tobs_data_t *item_data = obs_data_create();\n\tconst char *name = obs_source_get_name(item->source);\n\tconst char *src_uuid = obs_source_get_uuid(item->source);\n\tconst char *scale_filter;\n\tconst char *blend_method;\n\tconst char *blend_type;\n\tstruct vec2 pos = item->pos;\n\tstruct vec2 scale = item->scale;\n\tfloat rot = item->rot;\n\n\tif (backup_group) {\n\t\tget_ungrouped_transform(backup_group, item, &pos, &scale, &rot);\n\t}\n\n\tobs_data_set_string(item_data, \"name\", name);\n\tobs_data_set_string(item_data, \"source_uuid\", src_uuid);\n\tobs_data_set_bool(item_data, \"visible\", item->user_visible);\n\tobs_data_set_bool(item_data, \"locked\", item->locked);\n\tobs_data_set_double(item_data, \"rot\", rot);\n\tobs_data_set_vec2(item_data, \"scale_ref\", &item->scale_ref);\n\tobs_data_set_int(item_data, \"align\", (int)item->align);\n\tobs_data_set_int(item_data, \"bounds_type\", (int)item->bounds_type);\n\tobs_data_set_int(item_data, \"bounds_align\", (int)item->bounds_align);\n\tobs_data_set_bool(item_data, \"bounds_crop\", item->crop_to_bounds);\n\tobs_data_set_int(item_data, \"crop_left\", (int)item->crop.left);\n\tobs_data_set_int(item_data, \"crop_top\", (int)item->crop.top);\n\tobs_data_set_int(item_data, \"crop_right\", (int)item->crop.right);\n\tobs_data_set_int(item_data, \"crop_bottom\", (int)item->crop.bottom);\n\tobs_data_set_int(item_data, \"id\", item->id);\n\tobs_data_set_bool(item_data, \"group_item_backup\", !!backup_group);\n\n\tif (!item->absolute_coordinates) {\n\t\t/* For backwards compatibility, also store absolute values. */\n\t\tstruct vec2 tmp_abs;\n\n\t\tpos_to_absolute(&tmp_abs, &pos, item);\n\t\tobs_data_set_vec2(item_data, \"pos\", &tmp_abs);\n\t\tobs_data_set_vec2(item_data, \"pos_rel\", &pos);\n\n\t\titem_canvas_scale(&tmp_abs, item);\n\t\tobs_data_set_vec2(item_data, \"scale\", &tmp_abs);\n\t\tobs_data_set_vec2(item_data, \"scale_rel\", &scale);\n\n\t\tsize_to_absolute(&tmp_abs, &item->bounds, item);\n\t\tobs_data_set_vec2(item_data, \"bounds\", &tmp_abs);\n\t\tobs_data_set_vec2(item_data, \"bounds_rel\", &item->bounds);\n\t} else {\n\t\tobs_data_set_vec2(item_data, \"pos\", &pos);\n\t\tobs_data_set_vec2(item_data, \"scale\", &scale);\n\t\tobs_data_set_vec2(item_data, \"bounds\", &item->bounds);\n\t}\n\n\tif (item->is_group) {\n\t\tobs_scene_t *group_scene = item->source->context.data;\n\t\tobs_sceneitem_t *group_item;\n\n\t\t/* save group items as part of main scene, but ignored.\n\t\t * causes an automatic ungroup if scene collection file\n\t\t * is loaded in previous versions. */\n\t\tfull_lock(group_scene);\n\n\t\tgroup_item = group_scene->first_item;\n\t\twhile (group_item) {\n\t\t\tscene_save_item(array, group_item, item);\n\t\t\tgroup_item = group_item->next;\n\t\t}\n\n\t\tfull_unlock(group_scene);\n\t}\n\n\tif (item->scale_filter == OBS_SCALE_POINT)\n\t\tscale_filter = \"point\";\n\telse if (item->scale_filter == OBS_SCALE_BILINEAR)\n\t\tscale_filter = \"bilinear\";\n\telse if (item->scale_filter == OBS_SCALE_BICUBIC)\n\t\tscale_filter = \"bicubic\";\n\telse if (item->scale_filter == OBS_SCALE_LANCZOS)\n\t\tscale_filter = \"lanczos\";\n\telse if (item->scale_filter == OBS_SCALE_AREA)\n\t\tscale_filter = \"area\";\n\telse\n\t\tscale_filter = \"disable\";\n\n\tobs_data_set_string(item_data, \"scale_filter\", scale_filter);\n\n\tif (item->blend_method == OBS_BLEND_METHOD_SRGB_OFF)\n\t\tblend_method = \"srgb_off\";\n\telse\n\t\tblend_method = \"default\";\n\n\tobs_data_set_string(item_data, \"blend_method\", blend_method);\n\n\tif (item->blend_type == OBS_BLEND_NORMAL)\n\t\tblend_type = \"normal\";\n\telse if (item->blend_type == OBS_BLEND_ADDITIVE)\n\t\tblend_type = \"additive\";\n\telse if (item->blend_type == OBS_BLEND_SUBTRACT)\n\t\tblend_type = \"subtract\";\n\telse if (item->blend_type == OBS_BLEND_SCREEN)\n\t\tblend_type = \"screen\";\n\telse if (item->blend_type == OBS_BLEND_MULTIPLY)\n\t\tblend_type = \"multiply\";\n\telse if (item->blend_type == OBS_BLEND_LIGHTEN)\n\t\tblend_type = \"lighten\";\n\telse if (item->blend_type == OBS_BLEND_DARKEN)\n\t\tblend_type = \"darken\";\n\telse\n\t\tblend_type = \"normal\";\n\n\tobs_data_set_string(item_data, \"blend_type\", blend_type);\n\n\tobs_data_t *show_data = obs_sceneitem_transition_save(item, true);\n\tobs_data_set_obj(item_data, \"show_transition\", show_data);\n\tobs_data_release(show_data);\n\n\tobs_data_t *hide_data = obs_sceneitem_transition_save(item, false);\n\tobs_data_set_obj(item_data, \"hide_transition\", hide_data);\n\tobs_data_release(hide_data);\n\n\tobs_data_set_obj(item_data, \"private_settings\", item->private_settings);\n\n\tobs_data_array_push_back(array, item_data);\n\tobs_data_release(item_data);\n}\n\nstatic void scene_save(void *data, obs_data_t *settings)\n{\n\tstruct obs_scene *scene = data;\n\tobs_data_array_t *array = obs_data_array_create();\n\tstruct obs_scene_item *item;\n\n\tfull_lock(scene);\n\n\titem = scene->first_item;\n\twhile (item) {\n\t\tscene_save_item(array, item, NULL);\n\t\titem = item->next;\n\t}\n\n\tobs_data_set_int(settings, \"id_counter\", scene->id_counter);\n\tobs_data_set_bool(settings, \"custom_size\", scene->custom_size);\n\tif (scene->custom_size) {\n\t\tobs_data_set_int(settings, \"cx\", scene->cx);\n\t\tobs_data_set_int(settings, \"cy\", scene->cy);\n\t}\n\n\tfull_unlock(scene);\n\n\tobs_data_set_array(settings, \"items\", array);\n\tobs_data_array_release(array);\n}\n\nstatic uint32_t canvas_getwidth(obs_weak_canvas_t *weak)\n{\n\tuint32_t width = 0;\n\n\tobs_canvas_t *canvas = obs_weak_canvas_get_canvas(weak);\n\tif (canvas) {\n\t\twidth = canvas->ovi.base_width;\n\t\tobs_canvas_release(canvas);\n\t}\n\n\treturn width;\n}\n\nstatic uint32_t canvas_getheight(obs_weak_canvas_t *weak)\n{\n\tuint32_t height = 0;\n\n\tobs_canvas_t *canvas = obs_weak_canvas_get_canvas(weak);\n\tif (canvas) {\n\t\theight = canvas->ovi.base_height;\n\t\tobs_canvas_release(canvas);\n\t}\n\n\treturn height;\n}\n\nstatic uint32_t scene_getwidth(void *data)\n{\n\tobs_scene_t *scene = data;\n\tif (scene->custom_size)\n\t\treturn scene->cx;\n\tif (scene->source->canvas)\n\t\treturn canvas_getwidth(scene->source->canvas);\n\tif (obs->data.main_canvas->mix)\n\t\treturn obs->data.main_canvas->mix->ovi.base_width;\n\treturn 0;\n}\n\nstatic uint32_t scene_getheight(void *data)\n{\n\tobs_scene_t *scene = data;\n\tif (scene->custom_size)\n\t\treturn scene->cy;\n\tif (scene->source->canvas)\n\t\treturn canvas_getheight(scene->source->canvas);\n\tif (obs->data.main_canvas->mix)\n\t\treturn obs->data.main_canvas->mix->ovi.base_height;\n\treturn 0;\n}\n\nstatic void apply_scene_item_audio_actions(struct obs_scene_item *item, float *buf, uint64_t ts, size_t sample_rate)\n{\n\tbool cur_visible = item->visible;\n\tuint64_t frame_num = 0;\n\tsize_t deref_count = 0;\n\n\tpthread_mutex_lock(&item->actions_mutex);\n\n\tfor (size_t i = 0; i < item->audio_actions.num; i++) {\n\t\tstruct item_action action = item->audio_actions.array[i];\n\t\tuint64_t timestamp = action.timestamp;\n\t\tuint64_t new_frame_num;\n\n\t\tif (timestamp < ts)\n\t\t\ttimestamp = ts;\n\n\t\tnew_frame_num = util_mul_div64(timestamp - ts, sample_rate, 1000000000ULL);\n\n\t\tif (ts && new_frame_num >= AUDIO_OUTPUT_FRAMES)\n\t\t\tbreak;\n\n\t\tda_erase(item->audio_actions, i--);\n\n\t\titem->visible = action.visible;\n\t\tif (!item->visible)\n\t\t\tderef_count++;\n\n\t\tif (buf && new_frame_num > frame_num) {\n\t\t\tfor (; frame_num < new_frame_num; frame_num++)\n\t\t\t\tbuf[frame_num] = cur_visible ? 1.0f : 0.0f;\n\t\t}\n\n\t\tcur_visible = item->visible;\n\t}\n\n\tif (buf) {\n\t\tfor (; frame_num < AUDIO_OUTPUT_FRAMES; frame_num++)\n\t\t\tbuf[frame_num] = cur_visible ? 1.0f : 0.0f;\n\t}\n\n\tpthread_mutex_unlock(&item->actions_mutex);\n\n\twhile (deref_count--) {\n\t\tif (os_atomic_dec_long(&item->active_refs) == 0) {\n\t\t\tobs_source_remove_active_child(item->parent->source, item->source);\n\t\t}\n\t}\n}\n\nstatic bool apply_scene_item_volume(struct obs_scene_item *item, float *buf, uint64_t ts, size_t sample_rate)\n{\n\tbool actions_pending;\n\tstruct item_action action;\n\n\tpthread_mutex_lock(&item->actions_mutex);\n\n\tactions_pending = item->audio_actions.num > 0;\n\tif (actions_pending)\n\t\taction = item->audio_actions.array[0];\n\n\tpthread_mutex_unlock(&item->actions_mutex);\n\n\tif (actions_pending) {\n\t\tuint64_t duration = util_mul_div64(AUDIO_OUTPUT_FRAMES, 1000000000ULL, sample_rate);\n\n\t\tif (!ts || action.timestamp < (ts + duration)) {\n\t\t\tapply_scene_item_audio_actions(item, buf, ts, sample_rate);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic void process_all_audio_actions(struct obs_scene_item *item, size_t sample_rate)\n{\n\twhile (apply_scene_item_volume(item, NULL, 0, sample_rate))\n\t\t;\n}\n\nstatic void mix_audio_with_buf(float *p_out, float *p_in, float *buf_in, size_t pos, size_t count)\n{\n\tregister float *out = p_out + pos;\n\tregister float *buf = buf_in;\n\tregister float *in = p_in;\n\tregister float *end = in + count;\n\n\twhile (in < end)\n\t\t*(out++) += *(in++) * *(buf++);\n}\n\nstatic inline void mix_audio(float *p_out, float *p_in, size_t pos, size_t count)\n{\n\tregister float *out = p_out + pos;\n\tregister float *in = p_in;\n\tregister float *end = in + count;\n\n\twhile (in < end)\n\t\t*(out++) += *(in++);\n}\n\nstatic bool scene_audio_render(void *data, uint64_t *ts_out, struct obs_source_audio_mix *audio_output, uint32_t mixers,\n\t\t\t       size_t channels, size_t sample_rate)\n{\n\tuint64_t timestamp = 0;\n\tfloat buf[AUDIO_OUTPUT_FRAMES];\n\tstruct obs_source_audio_mix child_audio;\n\tstruct obs_scene *scene = data;\n\tstruct obs_scene_item *item;\n\n\taudio_lock(scene);\n\n\titem = scene->first_item;\n\twhile (item) {\n\t\tstruct obs_source *source;\n\t\tif (item->visible && transition_active(item->show_transition))\n\t\t\tsource = item->show_transition;\n\t\telse if (!item->visible && transition_active(item->hide_transition))\n\t\t\tsource = item->hide_transition;\n\t\telse\n\t\t\tsource = item->source;\n\n\t\tif (!obs_source_audio_pending(source) && (item->visible || transition_active(item->hide_transition))) {\n\t\t\tuint64_t source_ts = obs_source_get_audio_timestamp(source);\n\n\t\t\tif (source_ts && (!timestamp || source_ts < timestamp))\n\t\t\t\ttimestamp = source_ts;\n\t\t}\n\n\t\titem = item->next;\n\t}\n\n\tif (!timestamp) {\n\t\t/* just process all pending audio actions if no audio playing,\n\t\t * otherwise audio actions will just never be processed */\n\t\titem = scene->first_item;\n\t\twhile (item) {\n\t\t\tprocess_all_audio_actions(item, sample_rate);\n\t\t\titem = item->next;\n\t\t}\n\n\t\taudio_unlock(scene);\n\t\treturn false;\n\t}\n\n\titem = scene->first_item;\n\twhile (item) {\n\t\tuint64_t source_ts;\n\t\tsize_t pos;\n\t\tsize_t count;\n\t\tbool apply_buf;\n\t\tstruct obs_source *source;\n\n\t\tif (item->visible && transition_active(item->show_transition))\n\t\t\tsource = item->show_transition;\n\t\telse if (!item->visible && transition_active(item->hide_transition))\n\t\t\tsource = item->hide_transition;\n\t\telse\n\t\t\tsource = item->source;\n\n\t\tapply_buf = apply_scene_item_volume(item, buf, timestamp, sample_rate);\n\n\t\tif (obs_source_audio_pending(source)) {\n\t\t\titem = item->next;\n\t\t\tcontinue;\n\t\t}\n\n\t\tsource_ts = obs_source_get_audio_timestamp(source);\n\t\tif (!source_ts) {\n\t\t\titem = item->next;\n\t\t\tcontinue;\n\t\t}\n\n\t\tpos = (size_t)ns_to_audio_frames(sample_rate, source_ts - timestamp);\n\n\t\tif (pos >= AUDIO_OUTPUT_FRAMES) {\n\t\t\titem = item->next;\n\t\t\tcontinue;\n\t\t}\n\n\t\tcount = AUDIO_OUTPUT_FRAMES - pos;\n\n\t\tif (!apply_buf && !item->visible && !transition_active(item->hide_transition)) {\n\t\t\titem = item->next;\n\t\t\tcontinue;\n\t\t}\n\n\t\tobs_source_get_audio_mix(source, &child_audio);\n\n\t\tif (!source->audio_is_duplicated) {\n\t\t\tfor (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {\n\t\t\t\tif ((mixers & (1 << mix)) == 0)\n\t\t\t\t\tcontinue;\n\n\t\t\t\tfor (size_t ch = 0; ch < channels; ch++) {\n\t\t\t\t\tfloat *out = audio_output->output[mix].data[ch];\n\t\t\t\t\tfloat *in = child_audio.output[mix].data[ch];\n\t\t\t\t\tif (apply_buf)\n\t\t\t\t\t\tmix_audio_with_buf(out, in, buf, pos, count);\n\t\t\t\t\telse\n\t\t\t\t\t\tmix_audio(out, in, pos, count);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\titem = item->next;\n\t}\n\n\t*ts_out = timestamp;\n\taudio_unlock(scene);\n\n\treturn true;\n}\n\nenum gs_color_space scene_video_get_color_space(void *data, size_t count, const enum gs_color_space *preferred_spaces)\n{\n\tUNUSED_PARAMETER(data);\n\tUNUSED_PARAMETER(count);\n\tUNUSED_PARAMETER(preferred_spaces);\n\n\tenum gs_color_space space = GS_CS_SRGB;\n\tstruct obs_video_info ovi;\n\tif (obs_get_video_info(&ovi)) {\n\t\tif (ovi.colorspace == VIDEO_CS_2100_PQ || ovi.colorspace == VIDEO_CS_2100_HLG)\n\t\t\tspace = GS_CS_709_EXTENDED;\n\t}\n\n\treturn space;\n}\n\nconst struct obs_source_info scene_info = {\n\t.id = \"scene\",\n\t.type = OBS_SOURCE_TYPE_SCENE,\n\t.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_COMPOSITE | OBS_SOURCE_DO_NOT_DUPLICATE |\n\t\t\tOBS_SOURCE_SRGB | OBS_SOURCE_REQUIRES_CANVAS,\n\t.get_name = scene_getname,\n\t.create = scene_create,\n\t.destroy = scene_destroy,\n\t.video_tick = scene_video_tick,\n\t.video_render = scene_video_render,\n\t.audio_render = scene_audio_render,\n\t.get_width = scene_getwidth,\n\t.get_height = scene_getheight,\n\t.load = scene_load,\n\t.save = scene_save,\n\t.enum_active_sources = scene_enum_active_sources,\n\t.enum_all_sources = scene_enum_all_sources,\n\t.video_get_color_space = scene_video_get_color_space,\n};\n\nconst struct obs_source_info group_info = {\n\t.id = \"group\",\n\t.type = OBS_SOURCE_TYPE_SCENE,\n\t.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW | OBS_SOURCE_COMPOSITE | OBS_SOURCE_SRGB |\n\t\t\tOBS_SOURCE_REQUIRES_CANVAS,\n\t.get_name = group_getname,\n\t.create = scene_create,\n\t.destroy = scene_destroy,\n\t.video_tick = scene_video_tick,\n\t.video_render = scene_video_render,\n\t.audio_render = scene_audio_render,\n\t.get_width = scene_getwidth,\n\t.get_height = scene_getheight,\n\t.load = scene_load,\n\t.save = scene_save,\n\t.enum_active_sources = scene_enum_active_sources,\n\t.enum_all_sources = scene_enum_all_sources,\n\t.video_get_color_space = scene_video_get_color_space,\n};\n\nstatic inline obs_scene_t *create_id(obs_canvas_t *canvas, const char *id, const char *name)\n{\n\tstruct obs_source *source = obs_source_create_canvas(canvas, id, name, NULL, NULL);\n\treturn source->context.data;\n}\n\nstatic inline obs_scene_t *create_private_id(const char *id, const char *name)\n{\n\tstruct obs_source *source = obs_source_create_private(id, name, NULL);\n\treturn source->context.data;\n}\n\nobs_scene_t *obs_scene_create(const char *name)\n{\n\treturn create_id(obs->data.main_canvas, \"scene\", name);\n}\n\nobs_scene_t *obs_scene_create_private(const char *name)\n{\n\treturn create_private_id(\"scene\", name);\n}\n\nstatic obs_source_t *get_child_at_idx(obs_scene_t *scene, size_t idx)\n{\n\tstruct obs_scene_item *item = scene->first_item;\n\n\twhile (item && idx--)\n\t\titem = item->next;\n\treturn item ? item->source : NULL;\n}\n\nstatic inline obs_source_t *dup_child(obs_scene_item_ptr_array_t *old_items, size_t idx, obs_scene_t *new_scene,\n\t\t\t\t      bool private)\n{\n\tobs_source_t *source;\n\n\tsource = old_items->array[idx]->source;\n\n\t/* if the old item is referenced more than once in the old scene,\n\t * make sure they're referenced similarly in the new scene to reduce\n\t * load times */\n\tfor (size_t i = 0; i < idx; i++) {\n\t\tstruct obs_scene_item *item = old_items->array[i];\n\t\tif (item->source == source) {\n\t\t\tsource = get_child_at_idx(new_scene, i);\n\t\t\treturn obs_source_get_ref(source);\n\t\t}\n\t}\n\n\treturn obs_source_duplicate(source, private ? obs_source_get_name(source) : NULL, private);\n}\n\nstatic inline obs_source_t *new_ref(obs_source_t *source)\n{\n\treturn obs_source_get_ref(source);\n}\n\nstatic inline void duplicate_item_data(struct obs_scene_item *dst, struct obs_scene_item *src,\n\t\t\t\t       bool defer_texture_update, bool duplicate_hotkeys)\n{\n\tstruct obs_scene *dst_scene = dst->parent;\n\n\tif (!src->user_visible)\n\t\tset_visibility(dst, false);\n\n\tdst->selected = src->selected;\n\tdst->pos = src->pos;\n\tdst->rot = src->rot;\n\tdst->scale = src->scale;\n\tdst->align = src->align;\n\tdst->last_width = src->last_width;\n\tdst->last_height = src->last_height;\n\tdst->output_scale = src->output_scale;\n\tdst->scale_filter = src->scale_filter;\n\tdst->blend_method = src->blend_method;\n\tdst->blend_type = src->blend_type;\n\tdst->box_transform = src->box_transform;\n\tdst->box_scale = src->box_scale;\n\tdst->draw_transform = src->draw_transform;\n\tdst->bounds_type = src->bounds_type;\n\tdst->bounds_align = src->bounds_align;\n\tdst->bounds = src->bounds;\n\tdst->crop_to_bounds = src->crop_to_bounds;\n\tdst->bounds_crop = src->bounds_crop;\n\n\tif (src->show_transition) {\n\t\tobs_source_t *transition =\n\t\t\tobs_source_duplicate(src->show_transition, obs_source_get_name(src->show_transition), true);\n\t\tobs_sceneitem_set_transition(dst, true, transition);\n\t\tobs_source_release(transition);\n\t}\n\tif (src->hide_transition) {\n\t\tobs_source_t *transition =\n\t\t\tobs_source_duplicate(src->hide_transition, obs_source_get_name(src->hide_transition), true);\n\t\tobs_sceneitem_set_transition(dst, false, transition);\n\t\tobs_source_release(transition);\n\t}\n\tdst->show_transition_duration = src->show_transition_duration;\n\tdst->hide_transition_duration = src->hide_transition_duration;\n\n\tif (duplicate_hotkeys && !dst_scene->source->context.private) {\n\t\tstruct dstr show = {0};\n\t\tstruct dstr hide = {0};\n\t\tobs_data_array_t *data0 = NULL;\n\t\tobs_data_array_t *data1 = NULL;\n\n\t\tobs_hotkey_pair_save(src->toggle_visibility, &data0, &data1);\n\t\tobs_hotkey_pair_load(dst->toggle_visibility, data0, data1);\n\n\t\t/* Fix scene item ID */\n\t\tdstr_printf(&show, \"libobs.show_scene_item.%\" PRIi64, dst->id);\n\t\tdstr_printf(&hide, \"libobs.hide_scene_item.%\" PRIi64, dst->id);\n\t\tobs_hotkey_pair_set_names(dst->toggle_visibility, show.array, hide.array);\n\n\t\tobs_data_array_release(data0);\n\t\tobs_data_array_release(data1);\n\t\tdstr_free(&show);\n\t\tdstr_free(&hide);\n\t}\n\n\tobs_sceneitem_set_crop(dst, &src->crop);\n\tobs_sceneitem_set_locked(dst, src->locked);\n\n\tif (defer_texture_update) {\n\t\tos_atomic_set_bool(&dst->update_transform, true);\n\t}\n\n\tobs_data_apply(dst->private_settings, src->private_settings);\n}\n\nobs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name, enum obs_scene_duplicate_type type)\n{\n\tbool make_unique = type == OBS_SCENE_DUP_COPY || type == OBS_SCENE_DUP_PRIVATE_COPY;\n\tbool make_private = type == OBS_SCENE_DUP_PRIVATE_REFS || type == OBS_SCENE_DUP_PRIVATE_COPY;\n\tobs_scene_item_ptr_array_t items;\n\tstruct obs_scene *new_scene;\n\tstruct obs_scene_item *item;\n\tstruct obs_source *source;\n\n\tda_init(items);\n\n\tif (!obs_ptr_valid(scene, \"obs_scene_duplicate\"))\n\t\treturn NULL;\n\n\t/* --------------------------------- */\n\n\tfull_lock(scene);\n\n\titem = scene->first_item;\n\twhile (item) {\n\t\tda_push_back(items, &item);\n\t\tobs_sceneitem_addref(item);\n\t\titem = item->next;\n\t}\n\n\tfull_unlock(scene);\n\n\t/* --------------------------------- */\n\n\tobs_canvas_t *canvas = obs_weak_canvas_get_canvas(scene->source->canvas);\n\tnew_scene = make_private ? create_private_id(scene->source->info.id, name)\n\t\t\t\t : create_id(canvas, scene->source->info.id, name);\n\tobs_canvas_release(canvas);\n\n\tnew_scene->is_group = scene->is_group;\n\tnew_scene->custom_size = scene->custom_size;\n\tnew_scene->cx = scene->cx;\n\tnew_scene->cy = scene->cy;\n\tnew_scene->absolute_coordinates = scene->absolute_coordinates;\n\tnew_scene->last_width = scene->last_width;\n\tnew_scene->last_height = scene->last_height;\n\n\tobs_source_copy_filters(new_scene->source, scene->source);\n\n\tobs_data_apply(new_scene->source->private_settings, scene->source->private_settings);\n\n\t/* never duplicate sub-items for groups */\n\tif (scene->is_group)\n\t\tmake_unique = false;\n\n\tfor (size_t i = 0; i < items.num; i++) {\n\t\titem = items.array[i];\n\t\tsource = make_unique ? dup_child(&items, i, new_scene, make_private) : new_ref(item->source);\n\n\t\tif (source) {\n\t\t\tstruct obs_scene_item *new_item = obs_scene_add(new_scene, source);\n\n\t\t\tif (!new_item) {\n\t\t\t\tobs_source_release(source);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tduplicate_item_data(new_item, item, false, false);\n\n\t\t\tobs_source_release(source);\n\t\t}\n\t}\n\n\tfor (size_t i = 0; i < items.num; i++)\n\t\tobs_sceneitem_release(items.array[i]);\n\n\tif (new_scene->is_group)\n\t\tresize_scene(new_scene);\n\n\tda_free(items);\n\treturn new_scene;\n}\n\nstatic inline void obs_scene_addref(obs_scene_t *scene)\n{\n\tif (scene)\n\t\tobs_source_addref(scene->source);\n}\n\nobs_scene_t *obs_scene_get_ref(obs_scene_t *scene)\n{\n\tif (!scene)\n\t\treturn NULL;\n\tif (obs_source_get_ref(scene->source) != NULL)\n\t\treturn scene;\n\treturn NULL;\n}\n\nvoid obs_scene_release(obs_scene_t *scene)\n{\n\tif (scene)\n\t\tobs_source_release(scene->source);\n}\n\nobs_source_t *obs_scene_get_source(const obs_scene_t *scene)\n{\n\treturn scene ? scene->source : NULL;\n}\n\nobs_scene_t *obs_scene_from_source(const obs_source_t *source)\n{\n\tif (!source || strcmp(source->info.id, scene_info.id) != 0)\n\t\treturn NULL;\n\n\treturn source->context.data;\n}\n\nobs_scene_t *obs_group_from_source(const obs_source_t *source)\n{\n\tif (!source || strcmp(source->info.id, group_info.id) != 0)\n\t\treturn NULL;\n\n\treturn source->context.data;\n}\n\nobs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name)\n{\n\tstruct obs_scene_item *item;\n\n\tif (!scene)\n\t\treturn NULL;\n\n\tfull_lock(scene);\n\n\titem = scene->first_item;\n\twhile (item) {\n\t\tif (strcmp(item->source->context.name, name) == 0)\n\t\t\tbreak;\n\n\t\titem = item->next;\n\t}\n\n\tfull_unlock(scene);\n\n\treturn item;\n}\n\nobs_sceneitem_t *obs_scene_find_source_recursive(obs_scene_t *scene, const char *name)\n{\n\tstruct obs_scene_item *item;\n\n\tif (!scene)\n\t\treturn NULL;\n\n\tfull_lock(scene);\n\n\titem = scene->first_item;\n\twhile (item) {\n\t\tif (strcmp(item->source->context.name, name) == 0)\n\t\t\tbreak;\n\n\t\tif (item->is_group) {\n\t\t\tobs_scene_t *group = item->source->context.data;\n\t\t\tobs_sceneitem_t *child = obs_scene_find_source(group, name);\n\t\t\tif (child) {\n\t\t\t\titem = child;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\titem = item->next;\n\t}\n\n\tfull_unlock(scene);\n\n\treturn item;\n}\n\nobs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene, int64_t id)\n{\n\tstruct obs_scene_item *item;\n\n\tif (!scene)\n\t\treturn NULL;\n\n\tfull_lock(scene);\n\n\titem = scene->first_item;\n\twhile (item) {\n\t\tif (item->id == id)\n\t\t\tbreak;\n\n\t\titem = item->next;\n\t}\n\n\tfull_unlock(scene);\n\n\treturn item;\n}\n\nvoid obs_scene_enum_items(obs_scene_t *scene, bool (*callback)(obs_scene_t *, obs_sceneitem_t *, void *), void *param)\n{\n\tstruct obs_scene_item *item;\n\n\tif (!scene || !callback)\n\t\treturn;\n\n\tfull_lock(scene);\n\n\titem = scene->first_item;\n\twhile (item) {\n\t\tstruct obs_scene_item *next = item->next;\n\n\t\tobs_sceneitem_addref(item);\n\n\t\tif (!callback(scene, item, param)) {\n\t\t\tobs_sceneitem_release(item);\n\t\t\tbreak;\n\t\t}\n\n\t\tobs_sceneitem_release(item);\n\n\t\titem = next;\n\t}\n\n\tfull_unlock(scene);\n}\n\nstatic obs_sceneitem_t *sceneitem_get_ref(obs_sceneitem_t *si)\n{\n\tlong owners = os_atomic_load_long(&si->ref);\n\twhile (owners > 0) {\n\t\tif (os_atomic_compare_exchange_long(&si->ref, &owners, owners + 1)) {\n\t\t\treturn si;\n\t\t}\n\t}\n\treturn NULL;\n}\n\nstatic bool hotkey_show_sceneitem(void *data, obs_hotkey_pair_id id, obs_hotkey_t *hotkey, bool pressed)\n{\n\tUNUSED_PARAMETER(id);\n\tUNUSED_PARAMETER(hotkey);\n\n\tobs_sceneitem_t *si = sceneitem_get_ref(data);\n\tif (pressed && si && !si->user_visible) {\n\t\tobs_sceneitem_set_visible(si, true);\n\t\tobs_sceneitem_release(si);\n\t\treturn true;\n\t}\n\n\tobs_sceneitem_release(si);\n\treturn false;\n}\n\nstatic bool hotkey_hide_sceneitem(void *data, obs_hotkey_pair_id id, obs_hotkey_t *hotkey, bool pressed)\n{\n\tUNUSED_PARAMETER(id);\n\tUNUSED_PARAMETER(hotkey);\n\n\tobs_sceneitem_t *si = sceneitem_get_ref(data);\n\tif (pressed && si && si->user_visible) {\n\t\tobs_sceneitem_set_visible(si, false);\n\t\tobs_sceneitem_release(si);\n\t\treturn true;\n\t}\n\n\tobs_sceneitem_release(si);\n\treturn false;\n}\n\nstatic void init_hotkeys(obs_scene_t *scene, obs_sceneitem_t *item, const char *name)\n{\n\tstruct obs_data_array *hotkey_array;\n\tobs_data_t *hotkey_data = scene->source->context.hotkey_data;\n\n\tstruct dstr show = {0};\n\tstruct dstr hide = {0};\n\tstruct dstr legacy = {0};\n\tstruct dstr show_desc = {0};\n\tstruct dstr hide_desc = {0};\n\n\tdstr_printf(&show, \"libobs.show_scene_item.%\" PRIi64, item->id);\n\tdstr_printf(&hide, \"libobs.hide_scene_item.%\" PRIi64, item->id);\n\n\tdstr_copy(&show_desc, obs->hotkeys.sceneitem_show);\n\tdstr_replace(&show_desc, \"%1\", name);\n\tdstr_copy(&hide_desc, obs->hotkeys.sceneitem_hide);\n\tdstr_replace(&hide_desc, \"%1\", name);\n\n\t/* Check if legacy keys exists, migrate if necessary */\n\tdstr_printf(&legacy, \"libobs.show_scene_item.%s\", name);\n\thotkey_array = obs_data_get_array(hotkey_data, legacy.array);\n\tif (hotkey_array) {\n\t\tobs_data_set_array(hotkey_data, show.array, hotkey_array);\n\t\tobs_data_array_release(hotkey_array);\n\t}\n\n\tdstr_printf(&legacy, \"libobs.hide_scene_item.%s\", name);\n\thotkey_array = obs_data_get_array(hotkey_data, legacy.array);\n\tif (hotkey_array) {\n\t\tobs_data_set_array(hotkey_data, hide.array, hotkey_array);\n\t\tobs_data_array_release(hotkey_array);\n\t}\n\n\titem->toggle_visibility = obs_hotkey_pair_register_source(scene->source, show.array, show_desc.array,\n\t\t\t\t\t\t\t\t  hide.array, hide_desc.array, hotkey_show_sceneitem,\n\t\t\t\t\t\t\t\t  hotkey_hide_sceneitem, item, item);\n\n\tdstr_free(&show);\n\tdstr_free(&hide);\n\tdstr_free(&legacy);\n\tdstr_free(&show_desc);\n\tdstr_free(&hide_desc);\n}\n\nstatic void sceneitem_rename_hotkey(const obs_sceneitem_t *scene_item, const char *new_name)\n{\n\tstruct dstr show_desc = {0};\n\tstruct dstr hide_desc = {0};\n\n\tdstr_copy(&show_desc, obs->hotkeys.sceneitem_show);\n\tdstr_replace(&show_desc, \"%1\", new_name);\n\tdstr_copy(&hide_desc, obs->hotkeys.sceneitem_hide);\n\tdstr_replace(&hide_desc, \"%1\", new_name);\n\n\tobs_hotkey_pair_set_descriptions(scene_item->toggle_visibility, show_desc.array, hide_desc.array);\n\n\tdstr_free(&show_desc);\n\tdstr_free(&hide_desc);\n}\n\nstatic void sceneitem_renamed(void *param, calldata_t *data)\n{\n\tobs_sceneitem_t *scene_item = param;\n\tconst char *name = calldata_string(data, \"new_name\");\n\n\tsceneitem_rename_hotkey(scene_item, name);\n}\n\nstatic inline bool source_has_audio(obs_source_t *source)\n{\n\treturn (source->info.output_flags & (OBS_SOURCE_AUDIO | OBS_SOURCE_COMPOSITE)) != 0;\n}\n\nstatic obs_sceneitem_t *obs_scene_add_internal(obs_scene_t *scene, obs_source_t *source, obs_sceneitem_t *insert_after,\n\t\t\t\t\t       int64_t id)\n{\n\tstruct obs_scene_item *last;\n\tstruct obs_scene_item *item;\n\tpthread_mutex_t mutex;\n\n\tstruct item_action action = {.visible = true, .timestamp = os_gettime_ns()};\n\n\tif (!scene)\n\t\treturn NULL;\n\n\tsource = obs_source_get_ref(source);\n\tif (!source) {\n\t\tblog(LOG_ERROR, \"Tried to add a NULL source to a scene\");\n\t\treturn NULL;\n\t}\n\n\tif (source->removed) {\n\t\tblog(LOG_WARNING, \"Tried to add a removed source to a scene\");\n\t\tgoto release_source_and_fail;\n\t}\n\n\tif (pthread_mutex_init(&mutex, NULL) != 0) {\n\t\tblog(LOG_WARNING, \"Failed to create scene item mutex\");\n\t\tgoto release_source_and_fail;\n\t}\n\n\tif (!obs_source_add_active_child(scene->source, source)) {\n\t\tblog(LOG_WARNING, \"Failed to add source to scene due to \"\n\t\t\t\t  \"infinite source recursion\");\n\t\tpthread_mutex_destroy(&mutex);\n\t\tgoto release_source_and_fail;\n\t}\n\n\titem = bzalloc(sizeof(struct obs_scene_item));\n\titem->source = source;\n\titem->id = id ? id : ++scene->id_counter;\n\titem->parent = scene;\n\titem->ref = 1;\n\titem->align = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;\n\titem->actions_mutex = mutex;\n\titem->user_visible = true;\n\titem->locked = false;\n\titem->is_group = strcmp(source->info.id, group_info.id) == 0;\n\titem->is_scene = strcmp(source->info.id, scene_info.id) == 0;\n\titem->private_settings = obs_data_create();\n\titem->toggle_visibility = OBS_INVALID_HOTKEY_PAIR_ID;\n\titem->absolute_coordinates = scene->absolute_coordinates;\n\tos_atomic_set_long(&item->active_refs, 1);\n\tvec2_set(&item->scale, 1.0f, 1.0f);\n\tget_scene_dimensions(item, &item->scale_ref.x, &item->scale_ref.y);\n\tmatrix4_identity(&item->draw_transform);\n\tmatrix4_identity(&item->box_transform);\n\n\t/* Ensure initial position is still top-left corner in relative mode. */\n\tif (!item->absolute_coordinates)\n\t\tpos_from_absolute(&item->pos, &item->pos, item);\n\n\tif (source_has_audio(source)) {\n\t\titem->visible = false;\n\t\tda_push_back(item->audio_actions, &action);\n\t} else {\n\t\titem->visible = true;\n\t}\n\n\tfull_lock(scene);\n\n\tif (insert_after) {\n\t\tobs_sceneitem_t *next = insert_after->next;\n\t\tif (next)\n\t\t\tnext->prev = item;\n\t\titem->next = insert_after->next;\n\t\titem->prev = insert_after;\n\t\tinsert_after->next = item;\n\t} else {\n\t\tlast = scene->first_item;\n\t\tif (!last) {\n\t\t\tscene->first_item = item;\n\t\t} else {\n\t\t\twhile (last->next)\n\t\t\t\tlast = last->next;\n\n\t\t\tlast->next = item;\n\t\t\titem->prev = last;\n\t\t}\n\t}\n\n\tfull_unlock(scene);\n\n\tif (!scene->source->context.private)\n\t\tinit_hotkeys(scene, item, obs_source_get_name(source));\n\n\tsignal_handler_connect(obs_source_get_signal_handler(source), \"rename\", sceneitem_renamed, item);\n\n\treturn item;\n\nrelease_source_and_fail:\n\tobs_source_release(source);\n\treturn NULL;\n}\n\nobs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)\n{\n\tobs_sceneitem_t *item = obs_scene_add_internal(scene, source, NULL, 0);\n\tstruct calldata params;\n\tuint8_t stack[128];\n\n\tif (!item)\n\t\treturn NULL;\n\n\tcalldata_init_fixed(&params, stack, sizeof(stack));\n\tcalldata_set_ptr(&params, \"scene\", scene);\n\tcalldata_set_ptr(&params, \"item\", item);\n\tsignal_handler_signal(scene->source->context.signals, \"item_add\", &params);\n\treturn item;\n}\n\nstatic void obs_sceneitem_destroy(obs_sceneitem_t *item)\n{\n\tif (item) {\n\t\tif (item->item_render) {\n\t\t\tobs_enter_graphics();\n\t\t\tgs_texrender_destroy(item->item_render);\n\t\t\tobs_leave_graphics();\n\t\t}\n\t\tobs_data_release(item->private_settings);\n\t\tobs_hotkey_pair_unregister(item->toggle_visibility);\n\t\tpthread_mutex_destroy(&item->actions_mutex);\n\t\tsignal_handler_disconnect(obs_source_get_signal_handler(item->source), \"rename\", sceneitem_renamed,\n\t\t\t\t\t  item);\n\t\tif (item->show_transition)\n\t\t\tobs_source_release(item->show_transition);\n\t\tif (item->hide_transition)\n\t\t\tobs_source_release(item->hide_transition);\n\t\tif (item->source)\n\t\t\tobs_source_release(item->source);\n\t\tda_free(item->audio_actions);\n\t\tbfree(item);\n\t}\n}\n\nvoid obs_sceneitem_addref(obs_sceneitem_t *item)\n{\n\tif (item)\n\t\tos_atomic_inc_long(&item->ref);\n}\n\nvoid obs_sceneitem_release(obs_sceneitem_t *item)\n{\n\tif (!item)\n\t\treturn;\n\n\tif (os_atomic_dec_long(&item->ref) == 0)\n\t\tobs_sceneitem_destroy(item);\n}\n\nstatic void obs_sceneitem_remove_internal(obs_sceneitem_t *item)\n{\n\tobs_scene_t *parent = item->parent;\n\titem->removed = true;\n\n\tobs_sceneitem_select(item, false);\n\tset_visibility(item, false);\n\n\tdetach_sceneitem(item);\n\tsignal_item_remove(parent, item);\n\n\tobs_sceneitem_set_transition(item, true, NULL);\n\tobs_sceneitem_set_transition(item, false, NULL);\n}\n\nvoid obs_sceneitem_remove(obs_sceneitem_t *item)\n{\n\tobs_scene_t *scene;\n\n\tif (!item || item->removed)\n\t\treturn;\n\n\tscene = item->parent;\n\n\tassert(scene != NULL);\n\tassert(scene->source != NULL);\n\n\tfull_lock(scene);\n\tobs_sceneitem_remove_internal(item);\n\tfull_unlock(scene);\n\n\tobs_sceneitem_release(item);\n}\n\nvoid obs_sceneitem_save(obs_sceneitem_t *item, obs_data_array_t *arr)\n{\n\tscene_save_item(arr, item, NULL);\n}\n\nvoid sceneitem_restore(obs_data_t *data, void *vp)\n{\n\tobs_scene_t *scene = (obs_scene_t *)vp;\n\tscene_load_item(scene, data);\n}\n\nvoid obs_sceneitems_add(obs_scene_t *scene, obs_data_array_t *data)\n{\n\tobs_data_array_enum(data, sceneitem_restore, scene);\n}\n\nobs_scene_t *obs_sceneitem_get_scene(const obs_sceneitem_t *item)\n{\n\treturn item ? item->parent : NULL;\n}\n\nobs_source_t *obs_sceneitem_get_source(const obs_sceneitem_t *item)\n{\n\treturn item ? item->source : NULL;\n}\n\nstatic void signal_parent(obs_scene_t *parent, const char *command, calldata_t *params)\n{\n\tcalldata_set_ptr(params, \"scene\", parent);\n\tsignal_handler_signal(parent->source->context.signals, command, params);\n}\n\nstruct passthrough {\n\tobs_data_array_t *ids;\n\tobs_data_array_t *scenes_and_groups;\n\tbool all_items;\n};\n\nbool save_transform_states(obs_scene_t *scene, obs_sceneitem_t *item, void *vp_pass)\n{\n\tstruct passthrough *pass = (struct passthrough *)vp_pass;\n\tif (obs_sceneitem_selected(item) || pass->all_items) {\n\t\tobs_data_t *temp = obs_data_create();\n\t\tobs_data_array_t *item_ids = (obs_data_array_t *)pass->ids;\n\n\t\tstruct obs_transform_info info;\n\t\tstruct obs_sceneitem_crop crop;\n\t\tobs_sceneitem_get_info2(item, &info);\n\t\tobs_sceneitem_get_crop(item, &crop);\n\n\t\tstruct vec2 pos = info.pos;\n\t\tstruct vec2 scale = info.scale;\n\t\tfloat rot = info.rot;\n\t\tuint32_t alignment = info.alignment;\n\t\tuint32_t bounds_type = info.bounds_type;\n\t\tuint32_t bounds_alignment = info.bounds_alignment;\n\t\tbool crop_to_bounds = info.crop_to_bounds;\n\t\tstruct vec2 bounds = info.bounds;\n\n\t\tobs_data_set_int(temp, \"id\", obs_sceneitem_get_id(item));\n\t\tobs_data_set_vec2(temp, \"pos\", &pos);\n\t\tobs_data_set_vec2(temp, \"scale\", &scale);\n\t\tobs_data_set_double(temp, \"rot\", rot);\n\t\tobs_data_set_int(temp, \"alignment\", alignment);\n\t\tobs_data_set_int(temp, \"bounds_type\", bounds_type);\n\t\tobs_data_set_vec2(temp, \"bounds\", &bounds);\n\t\tobs_data_set_int(temp, \"bounds_alignment\", bounds_alignment);\n\t\tobs_data_set_bool(temp, \"crop_to_bounds\", crop_to_bounds);\n\t\tobs_data_set_int(temp, \"top\", crop.top);\n\t\tobs_data_set_int(temp, \"bottom\", crop.bottom);\n\t\tobs_data_set_int(temp, \"left\", crop.left);\n\t\tobs_data_set_int(temp, \"right\", crop.right);\n\n\t\tobs_data_array_push_back(item_ids, temp);\n\n\t\tobs_data_release(temp);\n\t}\n\n\tobs_source_t *item_source = obs_sceneitem_get_source(item);\n\n\tif (obs_source_is_group(item_source)) {\n\t\tobs_data_t *temp = obs_data_create();\n\t\tobs_data_array_t *nids = obs_data_array_create();\n\n\t\tobs_data_set_string(temp, \"scene_name\", obs_source_get_name(item_source));\n\t\tobs_data_set_string(temp, \"scene_uuid\", obs_source_get_uuid(item_source));\n\t\tobs_data_set_bool(temp, \"is_group\", true);\n\t\tobs_data_set_string(temp, \"group_parent\", obs_source_get_uuid(obs_scene_get_source(scene)));\n\n\t\tstruct passthrough npass = {nids, pass->scenes_and_groups, pass->all_items};\n\t\tobs_sceneitem_group_enum_items(item, save_transform_states, (void *)&npass);\n\n\t\tobs_data_set_array(temp, \"items\", nids);\n\n\t\tobs_data_array_push_back(pass->scenes_and_groups, temp);\n\n\t\tobs_data_release(temp);\n\t\tobs_data_array_release(nids);\n\t}\n\n\treturn true;\n}\n\nobs_data_t *obs_scene_save_transform_states(obs_scene_t *scene, bool all_items)\n{\n\tobs_data_t *wrapper = obs_data_create();\n\tobs_data_array_t *scenes_and_groups = obs_data_array_create();\n\tobs_data_array_t *item_ids = obs_data_array_create();\n\n\tstruct passthrough pass = {item_ids, scenes_and_groups, all_items};\n\n\tobs_data_t *temp = obs_data_create();\n\n\tobs_data_set_string(temp, \"scene_name\", obs_source_get_name(obs_scene_get_source(scene)));\n\tobs_data_set_string(temp, \"scene_uuid\", obs_source_get_uuid(obs_scene_get_source(scene)));\n\tobs_data_set_bool(temp, \"is_group\", false);\n\n\tobs_scene_enum_items(scene, save_transform_states, (void *)&pass);\n\n\tobs_data_set_array(temp, \"items\", item_ids);\n\tobs_data_array_push_back(scenes_and_groups, temp);\n\n\tobs_data_set_array(wrapper, \"scenes_and_groups\", scenes_and_groups);\n\n\tobs_data_array_release(item_ids);\n\tobs_data_array_release(scenes_and_groups);\n\tobs_data_release(temp);\n\n\treturn wrapper;\n}\n\nvoid load_transform_states(obs_data_t *temp, void *vp_scene)\n{\n\tobs_scene_t *scene = (obs_scene_t *)vp_scene;\n\tint64_t id = obs_data_get_int(temp, \"id\");\n\tobs_sceneitem_t *item = obs_scene_find_sceneitem_by_id(scene, id);\n\n\tstruct obs_transform_info info;\n\tstruct obs_sceneitem_crop crop;\n\tobs_data_get_vec2(temp, \"pos\", &info.pos);\n\tobs_data_get_vec2(temp, \"scale\", &info.scale);\n\tinfo.rot = (float)obs_data_get_double(temp, \"rot\");\n\tinfo.alignment = (uint32_t)obs_data_get_int(temp, \"alignment\");\n\tinfo.bounds_type = (enum obs_bounds_type)obs_data_get_int(temp, \"bounds_type\");\n\tinfo.bounds_alignment = (uint32_t)obs_data_get_int(temp, \"bounds_alignment\");\n\tobs_data_get_vec2(temp, \"bounds\", &info.bounds);\n\tinfo.crop_to_bounds = obs_data_get_bool(temp, \"crop_to_bounds\");\n\tcrop.top = (int)obs_data_get_int(temp, \"top\");\n\tcrop.bottom = (int)obs_data_get_int(temp, \"bottom\");\n\tcrop.left = (int)obs_data_get_int(temp, \"left\");\n\tcrop.right = (int)obs_data_get_int(temp, \"right\");\n\n\tobs_sceneitem_defer_update_begin(item);\n\n\tobs_sceneitem_set_info2(item, &info);\n\tobs_sceneitem_set_crop(item, &crop);\n\n\tobs_sceneitem_defer_update_end(item);\n}\n\nvoid iterate_scenes_and_groups_transform_states(obs_data_t *data, void *vp)\n{\n\tobs_data_array_t *items = obs_data_get_array(data, \"items\");\n\tobs_source_t *scene_source = obs_get_source_by_uuid(obs_data_get_string(data, \"scene_uuid\"));\n\tobs_scene_t *scene = obs_scene_from_source(scene_source);\n\n\tif (obs_data_get_bool(data, \"is_group\")) {\n\t\tobs_source_t *parent_source = obs_get_source_by_uuid(obs_data_get_string(data, \"group_parent\"));\n\t\tobs_scene_t *parent = obs_scene_from_source(parent_source);\n\t\tobs_sceneitem_t *group = obs_scene_get_group(parent, obs_data_get_string(data, \"scene_name\"));\n\t\tscene = obs_sceneitem_group_get_scene(group);\n\n\t\tobs_source_release(parent_source);\n\t}\n\n\tobs_data_array_enum(items, load_transform_states, (void *)scene);\n\n\tUNUSED_PARAMETER(vp);\n\n\tobs_data_array_release(items);\n\tobs_source_release(scene_source);\n}\n\nvoid obs_scene_load_transform_states(const char *data)\n{\n\tobs_data_t *dat = obs_data_create_from_json(data);\n\n\tobs_data_array_t *scenes_and_groups = obs_data_get_array(dat, \"scenes_and_groups\");\n\n\tobs_data_array_enum(scenes_and_groups, iterate_scenes_and_groups_transform_states, NULL);\n\n\tobs_data_release(dat);\n\tobs_data_array_release(scenes_and_groups);\n}\n\nvoid obs_sceneitem_select(obs_sceneitem_t *item, bool select)\n{\n\tstruct calldata params;\n\tuint8_t stack[128];\n\tconst char *command = select ? \"item_select\" : \"item_deselect\";\n\n\tif (!item || item->selected == select || !item->parent)\n\t\treturn;\n\n\titem->selected = select;\n\n\tcalldata_init_fixed(&params, stack, sizeof(stack));\n\tcalldata_set_ptr(&params, \"item\", item);\n\n\tsignal_parent(item->parent, command, &params);\n}\n\nbool obs_sceneitem_selected(const obs_sceneitem_t *item)\n{\n\treturn item ? item->selected : false;\n}\n\n#define do_update_transform(item)                                          \\\n\tdo {                                                               \\\n\t\tif (!item->parent || item->parent->is_group)               \\\n\t\t\tos_atomic_set_bool(&item->update_transform, true); \\\n\t\telse                                                       \\\n\t\t\tupdate_item_transform(item, false);                \\\n\t} while (false)\n\nvoid obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos)\n{\n\tif (item) {\n\t\tif (!item->absolute_coordinates)\n\t\t\tpos_from_absolute(&item->pos, pos, item);\n\t\telse\n\t\t\tvec2_copy(&item->pos, pos);\n\n\t\tdo_update_transform(item);\n\t}\n}\n\nvoid obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot)\n{\n\tif (item) {\n\t\titem->rot = rot;\n\t\tdo_update_transform(item);\n\t}\n}\n\nvoid obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale)\n{\n\tif (item) {\n\t\tif (!item->absolute_coordinates)\n\t\t\titem_relative_scale(&item->scale, scale, item);\n\t\telse\n\t\t\tvec2_copy(&item->scale, scale);\n\n\t\tdo_update_transform(item);\n\t}\n}\n\nvoid obs_sceneitem_set_alignment(obs_sceneitem_t *item, uint32_t alignment)\n{\n\tif (item) {\n\t\titem->align = alignment;\n\t\tdo_update_transform(item);\n\t}\n}\n\nstatic inline void signal_reorder(struct obs_scene_item *item)\n{\n\tconst char *command = NULL;\n\tstruct calldata params;\n\tuint8_t stack[128];\n\n\tcommand = \"reorder\";\n\n\tcalldata_init_fixed(&params, stack, sizeof(stack));\n\tsignal_parent(item->parent, command, &params);\n}\n\nstatic inline void signal_refresh(obs_scene_t *scene)\n{\n\tconst char *command = NULL;\n\tstruct calldata params;\n\tuint8_t stack[128];\n\n\tcommand = \"refresh\";\n\n\tcalldata_init_fixed(&params, stack, sizeof(stack));\n\tsignal_parent(scene, command, &params);\n}\n\nvoid obs_sceneitem_set_order(obs_sceneitem_t *item, enum obs_order_movement movement)\n{\n\tif (!item)\n\t\treturn;\n\n\tstruct obs_scene_item *next, *prev;\n\tstruct obs_scene *scene = obs_scene_get_ref(item->parent);\n\n\tif (!scene)\n\t\treturn;\n\n\tfull_lock(scene);\n\n\tnext = item->next;\n\tprev = item->prev;\n\n\tdetach_sceneitem(item);\n\n\tif (movement == OBS_ORDER_MOVE_DOWN) {\n\t\tattach_sceneitem(scene, item, prev ? prev->prev : NULL);\n\n\t} else if (movement == OBS_ORDER_MOVE_UP) {\n\t\tattach_sceneitem(scene, item, next ? next : prev);\n\n\t} else if (movement == OBS_ORDER_MOVE_TOP) {\n\t\tstruct obs_scene_item *last = next;\n\t\tif (!last) {\n\t\t\tlast = prev;\n\t\t} else {\n\t\t\twhile (last->next)\n\t\t\t\tlast = last->next;\n\t\t}\n\n\t\tattach_sceneitem(scene, item, last);\n\n\t} else if (movement == OBS_ORDER_MOVE_BOTTOM) {\n\t\tattach_sceneitem(scene, item, NULL);\n\t}\n\n\tfull_unlock(scene);\n\n\tsignal_reorder(item);\n\tobs_scene_release(scene);\n}\n\nint obs_sceneitem_get_order_position(obs_sceneitem_t *item)\n{\n\tstruct obs_scene *scene = item->parent;\n\tstruct obs_scene_item *next = scene->first_item;\n\n\tfull_lock(scene);\n\n\tint index = 0;\n\twhile (next && next != item) {\n\t\tnext = next->next;\n\t\t++index;\n\t}\n\n\tfull_unlock(scene);\n\n\treturn index;\n}\n\nvoid obs_sceneitem_set_order_position(obs_sceneitem_t *item, int position)\n{\n\tif (!item)\n\t\treturn;\n\n\tstruct obs_scene *scene = obs_scene_get_ref(item->parent);\n\n\tif (!scene)\n\t\treturn;\n\n\tfull_lock(scene);\n\n\tdetach_sceneitem(item);\n\n\tif (!scene->first_item || position == 0) {\n\t\tattach_sceneitem(scene, item, NULL);\n\t} else {\n\t\tstruct obs_scene_item *next = scene->first_item;\n\n\t\tfor (int i = position; i > 1; --i) {\n\t\t\tif (next->next == NULL)\n\t\t\t\tbreak;\n\t\t\tnext = next->next;\n\t\t}\n\n\t\tattach_sceneitem(scene, item, next);\n\t}\n\n\tfull_unlock(scene);\n\n\tsignal_reorder(item);\n\tobs_scene_release(scene);\n}\n\nvoid obs_sceneitem_set_bounds_type(obs_sceneitem_t *item, enum obs_bounds_type type)\n{\n\tif (item) {\n\t\titem->bounds_type = type;\n\t\tdo_update_transform(item);\n\t}\n}\n\nvoid obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item, uint32_t alignment)\n{\n\tif (item) {\n\t\titem->bounds_align = alignment;\n\t\tdo_update_transform(item);\n\t}\n}\n\nvoid obs_sceneitem_set_bounds_crop(obs_sceneitem_t *item, bool crop)\n{\n\tif (item) {\n\t\titem->crop_to_bounds = crop;\n\t\tdo_update_transform(item);\n\t}\n}\n\nvoid obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds)\n{\n\tif (item) {\n\t\tif (!item->absolute_coordinates)\n\t\t\tsize_from_absolute(&item->bounds, bounds, item);\n\t\telse\n\t\t\tvec2_copy(&item->bounds, bounds);\n\n\t\tdo_update_transform(item);\n\t}\n}\n\nvoid obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos)\n{\n\tif (!item)\n\t\treturn;\n\n\tif (!item->absolute_coordinates)\n\t\tpos_to_absolute(pos, &item->pos, item);\n\telse\n\t\tvec2_copy(pos, &item->pos);\n}\n\nfloat obs_sceneitem_get_rot(const obs_sceneitem_t *item)\n{\n\treturn item ? item->rot : 0.0f;\n}\n\nvoid obs_sceneitem_get_scale(const obs_sceneitem_t *item, struct vec2 *scale)\n{\n\tif (!item)\n\t\treturn;\n\n\tif (!item->absolute_coordinates)\n\t\titem_canvas_scale(scale, item);\n\telse\n\t\tvec2_copy(scale, &item->scale);\n}\n\nuint32_t obs_sceneitem_get_alignment(const obs_sceneitem_t *item)\n{\n\treturn item ? item->align : 0;\n}\n\nenum obs_bounds_type obs_sceneitem_get_bounds_type(const obs_sceneitem_t *item)\n{\n\treturn item ? item->bounds_type : OBS_BOUNDS_NONE;\n}\n\nuint32_t obs_sceneitem_get_bounds_alignment(const obs_sceneitem_t *item)\n{\n\treturn item ? item->bounds_align : 0;\n}\n\nbool obs_sceneitem_get_bounds_crop(const obs_sceneitem_t *item)\n{\n\treturn item ? item->crop_to_bounds : false;\n}\n\nvoid obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds)\n{\n\tif (!item)\n\t\treturn;\n\n\tif (!item->absolute_coordinates)\n\t\tsize_to_absolute(bounds, &item->bounds, item);\n\telse\n\t\tvec2_copy(bounds, &item->bounds);\n}\n\nstatic inline void scene_item_get_info_internal(const obs_sceneitem_t *item, struct obs_transform_info *info)\n{\n\tif (!item->absolute_coordinates) {\n\t\tpos_to_absolute(&info->pos, &item->pos, item);\n\t\titem_canvas_scale(&info->scale, item);\n\t\tsize_to_absolute(&info->bounds, &item->bounds, item);\n\t} else {\n\t\tinfo->pos = item->pos;\n\t\tinfo->scale = item->scale;\n\t\tinfo->bounds = item->bounds;\n\t}\n\tinfo->rot = item->rot;\n\tinfo->alignment = item->align;\n\tinfo->bounds_type = item->bounds_type;\n\tinfo->bounds_alignment = item->bounds_align;\n}\n\nvoid obs_sceneitem_get_info2(const obs_sceneitem_t *item, struct obs_transform_info *info)\n{\n\tif (item && info) {\n\t\tscene_item_get_info_internal(item, info);\n\t\tinfo->crop_to_bounds = item->crop_to_bounds;\n\t}\n}\n\nstatic inline void scene_item_set_info_internal(obs_sceneitem_t *item, const struct obs_transform_info *info)\n{\n\tif (!item->absolute_coordinates) {\n\t\tpos_from_absolute(&item->pos, &info->pos, item);\n\t\tsize_from_absolute(&item->bounds, &info->bounds, item);\n\t\tif (isfinite(info->scale.x) && isfinite(info->scale.y)) {\n\t\t\titem_relative_scale(&item->scale, &info->scale, item);\n\t\t}\n\t} else {\n\t\titem->pos = info->pos;\n\t\titem->bounds = info->bounds;\n\t\tif (isfinite(info->scale.x) && isfinite(info->scale.y)) {\n\t\t\titem->scale = info->scale;\n\t\t}\n\t}\n\n\titem->rot = info->rot;\n\titem->align = info->alignment;\n\titem->bounds_type = info->bounds_type;\n\titem->bounds_align = info->bounds_alignment;\n}\n\nvoid obs_sceneitem_set_info2(obs_sceneitem_t *item, const struct obs_transform_info *info)\n{\n\tif (item && info) {\n\t\tscene_item_set_info_internal(item, info);\n\t\titem->crop_to_bounds = info->crop_to_bounds;\n\t\tdo_update_transform(item);\n\t}\n}\n\nvoid obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform)\n{\n\tif (item)\n\t\tmatrix4_copy(transform, &item->draw_transform);\n}\n\nvoid obs_sceneitem_get_box_transform(const obs_sceneitem_t *item, struct matrix4 *transform)\n{\n\tif (item)\n\t\tmatrix4_copy(transform, &item->box_transform);\n}\n\nvoid obs_sceneitem_get_box_scale(const obs_sceneitem_t *item, struct vec2 *scale)\n{\n\tif (item)\n\t\t*scale = item->box_scale;\n}\n\nbool obs_sceneitem_visible(const obs_sceneitem_t *item)\n{\n\treturn item ? item->user_visible : false;\n}\n\nstatic bool group_item_transition(obs_scene_t *scene, obs_sceneitem_t *item, void *param)\n{\n\tif (!param || !item)\n\t\treturn true;\n\tconst bool visible = *(bool *)param;\n\tif (obs_sceneitem_visible(item))\n\t\tobs_sceneitem_do_transition(item, visible);\n\tUNUSED_PARAMETER(scene);\n\treturn true;\n}\n\nbool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible)\n{\n\tstruct calldata cd;\n\tuint8_t stack[256];\n\tstruct item_action action = {.visible = visible, .timestamp = os_gettime_ns()};\n\n\tif (!item)\n\t\treturn false;\n\n\tif (item->user_visible == visible)\n\t\treturn false;\n\n\tif (!item->parent)\n\t\treturn false;\n\n\tobs_sceneitem_do_transition(item, visible);\n\tif (obs_sceneitem_is_group(item))\n\t\tobs_sceneitem_group_enum_items(item, group_item_transition, &visible);\n\n\titem->user_visible = visible;\n\n\tif (visible) {\n\t\tif (os_atomic_inc_long(&item->active_refs) == 1) {\n\t\t\tif (!obs_source_add_active_child(item->parent->source, item->source)) {\n\t\t\t\tos_atomic_dec_long(&item->active_refs);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\tcalldata_init_fixed(&cd, stack, sizeof(stack));\n\tcalldata_set_ptr(&cd, \"item\", item);\n\tcalldata_set_bool(&cd, \"visible\", visible);\n\n\tsignal_parent(item->parent, \"item_visible\", &cd);\n\n\tif (source_has_audio(item->source)) {\n\t\tpthread_mutex_lock(&item->actions_mutex);\n\t\tda_push_back(item->audio_actions, &action);\n\t\tpthread_mutex_unlock(&item->actions_mutex);\n\t} else {\n\t\tset_visibility(item, visible);\n\t}\n\treturn true;\n}\n\nbool obs_sceneitem_locked(const obs_sceneitem_t *item)\n{\n\treturn item ? item->locked : false;\n}\n\nbool obs_sceneitem_set_locked(obs_sceneitem_t *item, bool lock)\n{\n\tstruct calldata cd;\n\tuint8_t stack[256];\n\n\tif (!item)\n\t\treturn false;\n\n\tif (item->locked == lock)\n\t\treturn false;\n\n\tif (!item->parent)\n\t\treturn false;\n\n\titem->locked = lock;\n\n\tcalldata_init_fixed(&cd, stack, sizeof(stack));\n\tcalldata_set_ptr(&cd, \"item\", item);\n\tcalldata_set_bool(&cd, \"locked\", lock);\n\n\tsignal_parent(item->parent, \"item_locked\", &cd);\n\n\treturn true;\n}\n\nstatic bool sceneitems_match(obs_scene_t *scene, obs_sceneitem_t *const *items, size_t size, bool *order_matches)\n{\n\tobs_sceneitem_t *item = scene->first_item;\n\n\tsize_t count = 0;\n\twhile (item) {\n\t\tbool found = false;\n\t\tfor (size_t i = 0; i < size; i++) {\n\t\t\tif (items[i] != item)\n\t\t\t\tcontinue;\n\n\t\t\tif (count != i)\n\t\t\t\t*order_matches = false;\n\n\t\t\tfound = true;\n\t\t\tbreak;\n\t\t}\n\n\t\tif (!found)\n\t\t\treturn false;\n\n\t\titem = item->next;\n\t\tcount += 1;\n\t}\n\n\treturn count == size;\n}\n\nbool obs_scene_reorder_items(obs_scene_t *scene, obs_sceneitem_t *const *item_order, size_t item_order_size)\n{\n\tif (!scene || !item_order_size)\n\t\treturn false;\n\n\tscene = obs_scene_get_ref(scene);\n\tif (!scene)\n\t\treturn false;\n\n\tfull_lock(scene);\n\n\tbool order_matches = true;\n\tif (!sceneitems_match(scene, item_order, item_order_size, &order_matches) || order_matches) {\n\t\tfull_unlock(scene);\n\t\tobs_scene_release(scene);\n\t\treturn false;\n\t}\n\n\tscene->first_item = item_order[0];\n\n\tobs_sceneitem_t *prev = NULL;\n\tfor (size_t i = 0; i < item_order_size; i++) {\n\t\titem_order[i]->prev = prev;\n\t\titem_order[i]->next = NULL;\n\n\t\tif (prev)\n\t\t\tprev->next = item_order[i];\n\n\t\tprev = item_order[i];\n\t}\n\n\tfull_unlock(scene);\n\n\tsignal_reorder(scene->first_item);\n\tobs_scene_release(scene);\n\treturn true;\n}\n\nvoid obs_scene_atomic_update(obs_scene_t *scene, obs_scene_atomic_update_func func, void *data)\n{\n\tif (!scene)\n\t\treturn;\n\n\tscene = obs_scene_get_ref(scene);\n\tif (!scene)\n\t\treturn;\n\n\tfull_lock(scene);\n\tfunc(data, scene);\n\tfull_unlock(scene);\n\tobs_scene_release(scene);\n}\n\nstatic inline bool crop_equal(const struct obs_sceneitem_crop *crop1, const struct obs_sceneitem_crop *crop2)\n{\n\treturn crop1->left == crop2->left && crop1->right == crop2->right && crop1->top == crop2->top &&\n\t       crop1->bottom == crop2->bottom;\n}\n\nvoid obs_sceneitem_set_crop(obs_sceneitem_t *item, const struct obs_sceneitem_crop *crop)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_set_crop\"))\n\t\treturn;\n\tif (!obs_ptr_valid(crop, \"obs_sceneitem_set_crop\"))\n\t\treturn;\n\tif (crop_equal(crop, &item->crop))\n\t\treturn;\n\n\tmemcpy(&item->crop, crop, sizeof(*crop));\n\n\tif (item->crop.left < 0)\n\t\titem->crop.left = 0;\n\tif (item->crop.right < 0)\n\t\titem->crop.right = 0;\n\tif (item->crop.top < 0)\n\t\titem->crop.top = 0;\n\tif (item->crop.bottom < 0)\n\t\titem->crop.bottom = 0;\n\n\tos_atomic_set_bool(&item->update_transform, true);\n}\n\nvoid obs_sceneitem_get_crop(const obs_sceneitem_t *item, struct obs_sceneitem_crop *crop)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_get_crop\"))\n\t\treturn;\n\tif (!obs_ptr_valid(crop, \"obs_sceneitem_get_crop\"))\n\t\treturn;\n\n\tmemcpy(crop, &item->crop, sizeof(*crop));\n}\n\nvoid obs_sceneitem_set_scale_filter(obs_sceneitem_t *item, enum obs_scale_type filter)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_set_scale_filter\"))\n\t\treturn;\n\n\titem->scale_filter = filter;\n\n\tos_atomic_set_bool(&item->update_transform, true);\n}\n\nenum obs_scale_type obs_sceneitem_get_scale_filter(obs_sceneitem_t *item)\n{\n\treturn obs_ptr_valid(item, \"obs_sceneitem_get_scale_filter\") ? item->scale_filter : OBS_SCALE_DISABLE;\n}\n\nvoid obs_sceneitem_set_blending_method(obs_sceneitem_t *item, enum obs_blending_method method)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_set_blending_method\"))\n\t\treturn;\n\n\titem->blend_method = method;\n}\n\nenum obs_blending_method obs_sceneitem_get_blending_method(obs_sceneitem_t *item)\n{\n\treturn obs_ptr_valid(item, \"obs_sceneitem_get_blending_method\") ? item->blend_method : OBS_BLEND_METHOD_DEFAULT;\n}\n\nvoid obs_sceneitem_set_blending_mode(obs_sceneitem_t *item, enum obs_blending_type type)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_set_blending_mode\"))\n\t\treturn;\n\n\titem->blend_type = type;\n\n\tos_atomic_set_bool(&item->update_transform, true);\n}\n\nenum obs_blending_type obs_sceneitem_get_blending_mode(obs_sceneitem_t *item)\n{\n\treturn obs_ptr_valid(item, \"obs_sceneitem_get_blending_mode\") ? item->blend_type : OBS_BLEND_NORMAL;\n}\n\nvoid obs_sceneitem_defer_update_begin(obs_sceneitem_t *item)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_defer_update_begin\"))\n\t\treturn;\n\n\tos_atomic_inc_long(&item->defer_update);\n}\n\nvoid obs_sceneitem_defer_update_end(obs_sceneitem_t *item)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_defer_update_end\"))\n\t\treturn;\n\n\tif (os_atomic_dec_long(&item->defer_update) == 0)\n\t\tdo_update_transform(item);\n}\n\nvoid obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_defer_group_resize_begin\"))\n\t\treturn;\n\n\tos_atomic_inc_long(&item->defer_group_resize);\n}\n\nvoid obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_defer_group_resize_end\"))\n\t\treturn;\n\n\tif (os_atomic_dec_long(&item->defer_group_resize) == 0)\n\t\tos_atomic_set_bool(&item->update_group_resize, true);\n}\n\nint64_t obs_sceneitem_get_id(const obs_sceneitem_t *item)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_get_id\"))\n\t\treturn 0;\n\n\treturn item->id;\n}\n\nvoid obs_sceneitem_set_id(obs_sceneitem_t *item, int64_t id)\n{\n\titem->id = id;\n}\n\nobs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item)\n{\n\tif (!obs_ptr_valid(item, \"obs_sceneitem_get_private_settings\"))\n\t\treturn NULL;\n\n\tobs_data_addref(item->private_settings);\n\treturn item->private_settings;\n}\n\nstatic inline void transform_val(struct vec2 *v2, struct matrix4 *transform)\n{\n\tstruct vec3 v;\n\tvec3_set(&v, v2->x, v2->y, 0.0f);\n\tvec3_transform(&v, &v, transform);\n\tv2->x = v.x;\n\tv2->y = v.y;\n}\n\nstatic void get_ungrouped_transform(obs_sceneitem_t *group, obs_sceneitem_t *item, struct vec2 *pos, struct vec2 *scale,\n\t\t\t\t    float *rot)\n{\n\tstruct matrix4 transform;\n\tstruct matrix4 mat;\n\tstruct vec4 x_base;\n\tstruct vec2 scale_abs, pos_abs;\n\n\tif (item->absolute_coordinates) {\n\t\tvec2_copy(&scale_abs, scale);\n\t\tvec2_copy(&pos_abs, pos);\n\t} else {\n\t\tsize_to_absolute(&scale_abs, scale, item);\n\t\tpos_to_absolute(&pos_abs, pos, item);\n\t}\n\n\tvec4_set(&x_base, 1.0f, 0.0f, 0.0f, 0.0f);\n\n\tmatrix4_copy(&transform, &group->draw_transform);\n\n\ttransform_val(&pos_abs, &transform);\n\tvec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f);\n\n\tvec4_set(&mat.x, scale_abs.x, 0.0f, 0.0f, 0.0f);\n\tvec4_set(&mat.y, 0.0f, scale_abs.y, 0.0f, 0.0f);\n\tvec4_set(&mat.z, 0.0f, 0.0f, 1.0f, 0.0f);\n\tvec4_set(&mat.t, 0.0f, 0.0f, 0.0f, 1.0f);\n\tmatrix4_mul(&mat, &mat, &transform);\n\n\tscale_abs.x = vec4_len(&mat.x) * (scale_abs.x > 0.0f ? 1.0f : -1.0f);\n\tscale_abs.y = vec4_len(&mat.y) * (scale_abs.y > 0.0f ? 1.0f : -1.0f);\n\n\tif (item->absolute_coordinates) {\n\t\tvec2_copy(scale, &scale_abs);\n\t\tvec2_copy(pos, &pos_abs);\n\t} else {\n\t\tsize_from_absolute(scale, &scale_abs, item);\n\t\tpos_from_absolute(pos, &pos_abs, item);\n\t}\n\n\t*rot += group->rot;\n}\n\nstatic void remove_group_transform(obs_sceneitem_t *group, obs_sceneitem_t *item)\n{\n\tobs_scene_t *parent = item->parent;\n\tif (!parent || !group)\n\t\treturn;\n\n\tget_ungrouped_transform(group, item, &item->pos, &item->scale, &item->rot);\n\n\tupdate_item_transform(item, false);\n}\n\nstatic void apply_group_transform(obs_sceneitem_t *item, obs_sceneitem_t *group)\n{\n\tstruct matrix4 transform;\n\tstruct matrix4 mat;\n\tstruct vec4 x_base;\n\n\tstruct vec2 scale_abs, pos_abs;\n\n\tif (item->absolute_coordinates) {\n\t\tvec2_copy(&scale_abs, &item->scale);\n\t\tvec2_copy(&pos_abs, &item->pos);\n\t} else {\n\t\tsize_to_absolute(&scale_abs, &item->scale, item);\n\t\tpos_to_absolute(&pos_abs, &item->pos, item);\n\t}\n\n\tvec4_set(&x_base, 1.0f, 0.0f, 0.0f, 0.0f);\n\n\tmatrix4_inv(&transform, &group->draw_transform);\n\n\ttransform_val(&pos_abs, &transform);\n\tvec4_set(&transform.t, 0.0f, 0.0f, 0.0f, 1.0f);\n\n\tvec4_set(&mat.x, scale_abs.x, 0.0f, 0.0f, 0.0f);\n\tvec4_set(&mat.y, 0.0f, scale_abs.y, 0.0f, 0.0f);\n\tvec4_set(&mat.z, 0.0f, 0.0f, 1.0f, 0.0f);\n\tvec4_set(&mat.t, 0.0f, 0.0f, 0.0f, 1.0f);\n\tmatrix4_mul(&mat, &mat, &transform);\n\n\tscale_abs.x = vec4_len(&mat.x) * (scale_abs.x > 0.0f ? 1.0f : -1.0f);\n\tscale_abs.y = vec4_len(&mat.y) * (scale_abs.y > 0.0f ? 1.0f : -1.0f);\n\n\tif (item->absolute_coordinates) {\n\t\tvec2_copy(&item->scale, &scale_abs);\n\t\tvec2_copy(&item->pos, &pos_abs);\n\t} else {\n\t\tsize_from_absolute(&item->scale, &scale_abs, item);\n\t\tpos_from_absolute(&item->pos, &pos_abs, item);\n\t}\n\n\titem->rot -= group->rot;\n\n\tupdate_item_transform(item, false);\n}\n\nstatic bool resize_scene_base(obs_scene_t *scene, struct vec2 *minv, struct vec2 *maxv, struct vec2 *scale)\n{\n\tvec2_set(minv, M_INFINITE, M_INFINITE);\n\tvec2_set(maxv, -M_INFINITE, -M_INFINITE);\n\n\tobs_sceneitem_t *item = scene->first_item;\n\tif (!item) {\n\t\tscene->cx = 0;\n\t\tscene->cy = 0;\n\t\treturn false;\n\t}\n\n\twhile (item) {\n#define get_min_max(x_val, y_val)                             \\\n\tdo {                                                  \\\n\t\tstruct vec3 v;                                \\\n\t\tvec3_set(&v, x_val, y_val, 0.0f);             \\\n\t\tvec3_transform(&v, &v, &item->box_transform); \\\n\t\tif (v.x < minv->x)                            \\\n\t\t\tminv->x = v.x;                        \\\n\t\tif (v.y < minv->y)                            \\\n\t\t\tminv->y = v.y;                        \\\n\t\tif (v.x > maxv->x)                            \\\n\t\t\tmaxv->x = v.x;                        \\\n\t\tif (v.y > maxv->y)                            \\\n\t\t\tmaxv->y = v.y;                        \\\n\t} while (false)\n\n\t\tget_min_max(0.0f, 0.0f);\n\t\tget_min_max(1.0f, 0.0f);\n\t\tget_min_max(0.0f, 1.0f);\n\t\tget_min_max(1.0f, 1.0f);\n#undef get_min_max\n\n\t\titem = item->next;\n\t}\n\n\titem = scene->first_item;\n\tif (item) {\n\t\tstruct vec2 minv_rel;\n\t\tif (!item->absolute_coordinates)\n\t\t\tsize_from_absolute(&minv_rel, minv, item);\n\t\telse\n\t\t\tvec2_copy(&minv_rel, minv);\n\n\t\twhile (item) {\n\t\t\tvec2_sub(&item->pos, &item->pos, &minv_rel);\n\t\t\tupdate_item_transform(item, false);\n\t\t\titem = item->next;\n\t\t}\n\t}\n\n\tvec2_sub(scale, maxv, minv);\n\tscene->cx = (uint32_t)ceilf(scale->x);\n\tscene->cy = (uint32_t)ceilf(scale->y);\n\treturn true;\n}\n\nstatic void resize_scene(obs_scene_t *scene)\n{\n\tstruct vec2 minv;\n\tstruct vec2 maxv;\n\tstruct vec2 scale;\n\tresize_scene_base(scene, &minv, &maxv, &scale);\n}\n\n/* assumes group scene and parent scene is locked */\nstatic void resize_group(obs_sceneitem_t *group, bool scene_resize)\n{\n\tobs_scene_t *scene = group->source->context.data;\n\tstruct vec2 minv;\n\tstruct vec2 maxv;\n\tstruct vec2 scale;\n\n\tif (os_atomic_load_long(&group->defer_group_resize) > 0)\n\t\treturn;\n\n\tif (!resize_scene_base(scene, &minv, &maxv, &scale))\n\t\treturn;\n\n\tif (group->bounds_type == OBS_BOUNDS_NONE && !scene_resize) {\n\t\tstruct vec2 new_pos;\n\n\t\tif ((group->align & OBS_ALIGN_LEFT) != 0)\n\t\t\tnew_pos.x = minv.x;\n\t\telse if ((group->align & OBS_ALIGN_RIGHT) != 0)\n\t\t\tnew_pos.x = maxv.x;\n\t\telse\n\t\t\tnew_pos.x = (maxv.x - minv.x) * 0.5f + minv.x;\n\n\t\tif ((group->align & OBS_ALIGN_TOP) != 0)\n\t\t\tnew_pos.y = minv.y;\n\t\telse if ((group->align & OBS_ALIGN_BOTTOM) != 0)\n\t\t\tnew_pos.y = maxv.y;\n\t\telse\n\t\t\tnew_pos.y = (maxv.y - minv.y) * 0.5f + minv.y;\n\n\t\ttransform_val(&new_pos, &group->draw_transform);\n\t\tif (!group->absolute_coordinates)\n\t\t\tpos_from_absolute(&new_pos, &new_pos, group);\n\n\t\tvec2_copy(&group->pos, &new_pos);\n\t}\n\n\tos_atomic_set_bool(&group->update_group_resize, false);\n\n\tupdate_item_transform(group, false);\n}\n\nobs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene, const char *name)\n{\n\treturn obs_scene_insert_group(scene, name, NULL, 0);\n}\n\nobs_sceneitem_t *obs_scene_add_group2(obs_scene_t *scene, const char *name, bool signal)\n{\n\treturn obs_scene_insert_group2(scene, name, NULL, 0, signal);\n}\n\nobs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, const char *name, obs_sceneitem_t **items, size_t count)\n{\n\tif (!scene)\n\t\treturn NULL;\n\n\t/* don't allow groups or sub-items of other groups */\n\tfor (size_t i = count; i > 0; i--) {\n\t\tobs_sceneitem_t *item = items[i - 1];\n\t\tif (item->parent != scene || item->is_group)\n\t\t\treturn NULL;\n\t}\n\n\tobs_canvas_t *canvas = obs_weak_canvas_get_canvas(scene->source->canvas);\n\tobs_scene_t *sub_scene = create_id(canvas, group_info.id, name);\n\tobs_canvas_release(canvas);\n\n\tobs_sceneitem_t *last_item = items ? items[count - 1] : NULL;\n\tobs_sceneitem_t *item = obs_scene_add_internal(scene, sub_scene->source, last_item, 0);\n\n\tif (!items || !count) {\n\t\tobs_scene_release(sub_scene);\n\t\treturn item;\n\t}\n\n\t/* ------------------------- */\n\n\tfull_lock(scene);\n\tfull_lock(sub_scene);\n\tsub_scene->first_item = items[0];\n\n\tfor (size_t i = count; i > 0; i--) {\n\t\tsize_t idx = i - 1;\n\t\tremove_group_transform(item, items[idx]);\n\t\tdetach_sceneitem(items[idx]);\n\t}\n\tfor (size_t i = 0; i < count; i++) {\n\t\tsize_t idx = i;\n\t\tif (idx != (count - 1)) {\n\t\t\tsize_t next_idx = idx + 1;\n\t\t\titems[idx]->next = items[next_idx];\n\t\t\titems[next_idx]->prev = items[idx];\n\t\t} else {\n\t\t\titems[idx]->next = NULL;\n\t\t}\n\t\titems[idx]->parent = sub_scene;\n\t\tapply_group_transform(items[idx], item);\n\t}\n\titems[0]->prev = NULL;\n\tresize_group(item, false);\n\tfull_unlock(sub_scene);\n\tfull_unlock(scene);\n\n\tstruct calldata params;\n\tuint8_t stack[128];\n\n\tcalldata_init_fixed(&params, stack, sizeof(stack));\n\tcalldata_set_ptr(&params, \"scene\", scene);\n\tcalldata_set_ptr(&params, \"item\", item);\n\tsignal_handler_signal(scene->source->context.signals, \"item_add\", &params);\n\n\t/* ------------------------- */\n\n\tobs_scene_release(sub_scene);\n\treturn item;\n}\n\nobs_sceneitem_t *obs_scene_insert_group2(obs_scene_t *scene, const char *name, obs_sceneitem_t **items, size_t count,\n\t\t\t\t\t bool signal)\n{\n\tobs_sceneitem_t *item = obs_scene_insert_group(scene, name, items, count);\n\tif (signal && item)\n\t\tsignal_refresh(scene);\n\treturn item;\n}\n\nobs_sceneitem_t *obs_scene_get_group(obs_scene_t *scene, const char *name)\n{\n\tif (!scene || !name || !*name) {\n\t\treturn NULL;\n\t}\n\n\tobs_sceneitem_t *group = NULL;\n\tobs_sceneitem_t *item;\n\n\tfull_lock(scene);\n\n\titem = scene->first_item;\n\twhile (item) {\n\t\tif (item->is_group && item->source->context.name) {\n\t\t\tif (strcmp(item->source->context.name, name) == 0) {\n\t\t\t\tgroup = item;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\titem = item->next;\n\t}\n\n\tfull_unlock(scene);\n\n\treturn group;\n}\n\nbool obs_sceneitem_is_group(obs_sceneitem_t *item)\n{\n\treturn item && item->is_group;\n}\n\nobs_scene_t *obs_sceneitem_group_get_scene(const obs_sceneitem_t *item)\n{\n\treturn (item && item->is_group) ? item->source->context.data : NULL;\n}\n\nvoid obs_sceneitem_group_ungroup(obs_sceneitem_t *item)\n{\n\tif (!item || !item->is_group)\n\t\treturn;\n\n\tobs_scene_t *scene = item->parent;\n\tobs_scene_t *subscene = item->source->context.data;\n\tobs_sceneitem_t *insert_after = item;\n\tobs_sceneitem_t *first;\n\tobs_sceneitem_t *last;\n\n\tsignal_item_remove(scene, item);\n\n\tfull_lock(scene);\n\n\t/* ------------------------- */\n\n\tfull_lock(subscene);\n\tfirst = subscene->first_item;\n\tlast = first;\n\twhile (last) {\n\t\tobs_sceneitem_t *dst;\n\n\t\tremove_group_transform(item, last);\n\t\tdst = obs_scene_add_internal(scene, last->source, insert_after, 0);\n\t\tduplicate_item_data(dst, last, true, true);\n\t\tapply_group_transform(last, item);\n\n\t\tif (!last->next)\n\t\t\tbreak;\n\n\t\tinsert_after = dst;\n\t\tlast = last->next;\n\t}\n\tfull_unlock(subscene);\n\n\t/* ------------------------- */\n\n\tdetach_sceneitem(item);\n\tfull_unlock(scene);\n\n\tobs_sceneitem_release(item);\n}\n\nvoid obs_sceneitem_group_ungroup2(obs_sceneitem_t *item, bool signal)\n{\n\tobs_scene_t *scene = item->parent;\n\tobs_sceneitem_group_ungroup(item);\n\tif (signal)\n\t\tsignal_refresh(scene);\n}\n\nvoid obs_sceneitem_group_add_item(obs_sceneitem_t *group, obs_sceneitem_t *item)\n{\n\tif (!group || !group->is_group || !item)\n\t\treturn;\n\n\tobs_scene_t *scene = group->parent;\n\tobs_scene_t *groupscene = group->source->context.data;\n\n\tif (item->parent != scene)\n\t\treturn;\n\n\tif (item->parent == groupscene)\n\t\treturn;\n\n\t/* ------------------------- */\n\n\tfull_lock(scene);\n\tfull_lock(groupscene);\n\n\tremove_group_transform(group, item);\n\n\tdetach_sceneitem(item);\n\tattach_sceneitem(groupscene, item, NULL);\n\n\tapply_group_transform(item, group);\n\n\tresize_group(group, false);\n\n\tfull_unlock(groupscene);\n\tfull_unlock(scene);\n\n\t/* ------------------------- */\n\n\tsignal_refresh(scene);\n}\n\nvoid obs_sceneitem_group_remove_item(obs_sceneitem_t *group, obs_sceneitem_t *item)\n{\n\tif (!item || !group || !group->is_group)\n\t\treturn;\n\n\tobs_scene_t *groupscene = item->parent;\n\tobs_scene_t *scene = group->parent;\n\n\t/* ------------------------- */\n\n\tfull_lock(scene);\n\tfull_lock(groupscene);\n\n\tremove_group_transform(group, item);\n\n\tdetach_sceneitem(item);\n\tattach_sceneitem(scene, item, NULL);\n\n\tresize_group(group, false);\n\n\tfull_unlock(groupscene);\n\tfull_unlock(scene);\n\n\t/* ------------------------- */\n\n\tsignal_refresh(scene);\n}\n\nstatic void build_current_order_info(obs_scene_t *scene, struct obs_sceneitem_order_info **items_out, size_t *size_out)\n{\n\tDARRAY(struct obs_sceneitem_order_info) items;\n\tda_init(items);\n\n\tobs_sceneitem_t *item = scene->first_item;\n\twhile (item) {\n\t\tstruct obs_sceneitem_order_info info = {0};\n\t\tinfo.item = item;\n\t\tda_push_back(items, &info);\n\n\t\tif (item->is_group) {\n\t\t\tobs_scene_t *sub_scene = item->source->context.data;\n\n\t\t\tfull_lock(sub_scene);\n\n\t\t\tobs_sceneitem_t *sub_item = sub_scene->first_item;\n\n\t\t\twhile (sub_item) {\n\t\t\t\tinfo.group = item;\n\t\t\t\tinfo.item = sub_item;\n\t\t\t\tda_push_back(items, &info);\n\n\t\t\t\tsub_item = sub_item->next;\n\t\t\t}\n\n\t\t\tfull_unlock(sub_scene);\n\t\t}\n\n\t\titem = item->next;\n\t}\n\n\t*items_out = items.array;\n\t*size_out = items.num;\n}\n\nstatic bool sceneitems_match2(obs_scene_t *scene, struct obs_sceneitem_order_info *items, size_t size)\n{\n\tstruct obs_sceneitem_order_info *cur_items;\n\tsize_t cur_size;\n\n\tbuild_current_order_info(scene, &cur_items, &cur_size);\n\tif (cur_size != size) {\n\t\tbfree(cur_items);\n\t\treturn false;\n\t}\n\n\tfor (size_t i = 0; i < size; i++) {\n\t\tstruct obs_sceneitem_order_info *new = &items[i];\n\t\tstruct obs_sceneitem_order_info *old = &cur_items[i];\n\n\t\tif (new->group != old->group || new->item != old->item) {\n\t\t\tbfree(cur_items);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tbfree(cur_items);\n\treturn true;\n}\n\nstatic obs_sceneitem_t *get_sceneitem_parent_group(obs_scene_t *scene, obs_sceneitem_t *group_subitem)\n{\n\tif (group_subitem->is_group)\n\t\treturn NULL;\n\n\tobs_sceneitem_t *item = scene->first_item;\n\twhile (item) {\n\t\tif (item->is_group && item->source->context.data == group_subitem->parent)\n\t\t\treturn item;\n\t\titem = item->next;\n\t}\n\n\treturn NULL;\n}\n\nstatic void obs_sceneitem_move_hotkeys(obs_scene_t *parent, obs_sceneitem_t *item)\n{\n\tobs_data_array_t *data0 = NULL;\n\tobs_data_array_t *data1 = NULL;\n\n\tobs_hotkey_pair_save(item->toggle_visibility, &data0, &data1);\n\tobs_hotkey_pair_unregister(item->toggle_visibility);\n\n\tinit_hotkeys(parent, item, obs_source_get_name(item->source));\n\tobs_hotkey_pair_load(item->toggle_visibility, data0, data1);\n\n\tobs_data_array_release(data0);\n\tobs_data_array_release(data1);\n}\n\nbool obs_scene_reorder_items2(obs_scene_t *scene, struct obs_sceneitem_order_info *item_order, size_t item_order_size)\n{\n\tif (!scene || !item_order_size || !item_order)\n\t\treturn false;\n\n\tscene = obs_scene_get_ref(scene);\n\tif (!scene)\n\t\treturn false;\n\n\tfull_lock(scene);\n\n\tif (sceneitems_match2(scene, item_order, item_order_size)) {\n\t\tfull_unlock(scene);\n\t\tobs_scene_release(scene);\n\t\treturn false;\n\t}\n\n\tfor (size_t i = 0; i < item_order_size; i++) {\n\t\tstruct obs_sceneitem_order_info *info = &item_order[i];\n\t\tif (!info->item->is_group) {\n\t\t\tobs_sceneitem_t *group = get_sceneitem_parent_group(scene, info->item);\n\t\t\tremove_group_transform(group, info->item);\n\t\t}\n\t}\n\n\tscene->first_item = item_order[0].item;\n\n\tobs_sceneitem_t *prev = NULL;\n\tfor (size_t i = 0; i < item_order_size; i++) {\n\t\tstruct obs_sceneitem_order_info *info = &item_order[i];\n\t\tobs_sceneitem_t *item = info->item;\n\n\t\tif (info->item->is_group) {\n\t\t\tobs_sceneitem_t *sub_prev = NULL;\n\t\t\tobs_scene_t *sub_scene = info->item->source->context.data;\n\n\t\t\tsub_scene->first_item = NULL;\n\n\t\t\tobs_scene_addref(sub_scene);\n\t\t\tfull_lock(sub_scene);\n\n\t\t\tfor (i++; i < item_order_size; i++) {\n\t\t\t\tstruct obs_sceneitem_order_info *sub_info = &item_order[i];\n\t\t\t\tobs_sceneitem_t *sub_item = sub_info->item;\n\n\t\t\t\tif (sub_info->group != info->item) {\n\t\t\t\t\ti--;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (!sub_scene->first_item)\n\t\t\t\t\tsub_scene->first_item = sub_item;\n\n\t\t\t\t/* Move hotkeys into group */\n\t\t\t\tobs_sceneitem_move_hotkeys(sub_scene, sub_item);\n\n\t\t\t\tsub_item->prev = sub_prev;\n\t\t\t\tsub_item->next = NULL;\n\t\t\t\tsub_item->parent = sub_scene;\n\n\t\t\t\tif (sub_prev)\n\t\t\t\t\tsub_prev->next = sub_item;\n\n\t\t\t\tapply_group_transform(sub_info->item, sub_info->group);\n\n\t\t\t\tsub_prev = sub_item;\n\t\t\t}\n\n\t\t\tresize_group(info->item, false);\n\t\t\tfull_unlock(sub_scene);\n\t\t\tobs_scene_release(sub_scene);\n\t\t}\n\n\t\t/* Move item hotkeys out of group */\n\t\tif (item->parent && obs_scene_is_group(item->parent))\n\t\t\tobs_sceneitem_move_hotkeys(scene, item);\n\n\t\titem->prev = prev;\n\t\titem->next = NULL;\n\t\titem->parent = scene;\n\n\t\tif (prev)\n\t\t\tprev->next = item;\n\n\t\tprev = item;\n\t}\n\n\tfull_unlock(scene);\n\n\tsignal_reorder(scene->first_item);\n\tobs_scene_release(scene);\n\treturn true;\n}\n\nobs_sceneitem_t *obs_sceneitem_get_group(obs_scene_t *scene, obs_sceneitem_t *group_subitem)\n{\n\tif (!scene || !group_subitem || group_subitem->is_group)\n\t\treturn NULL;\n\n\tfull_lock(scene);\n\tobs_sceneitem_t *group = get_sceneitem_parent_group(scene, group_subitem);\n\tfull_unlock(scene);\n\n\treturn group;\n}\n\nbool obs_source_is_group(const obs_source_t *source)\n{\n\treturn source && strcmp(source->info.id, group_info.id) == 0;\n}\n\nbool obs_source_type_is_group(const char *id)\n{\n\treturn id && strcmp(id, group_info.id) == 0;\n}\n\nbool obs_source_is_scene(const obs_source_t *source)\n{\n\treturn source && strcmp(source->info.id, scene_info.id) == 0;\n}\n\nbool obs_source_type_is_scene(const char *id)\n{\n\treturn id && strcmp(id, scene_info.id) == 0;\n}\n\nbool obs_scene_is_group(const obs_scene_t *scene)\n{\n\treturn scene ? scene->is_group : false;\n}\n\nvoid obs_sceneitem_group_enum_items(obs_sceneitem_t *group, bool (*callback)(obs_scene_t *, obs_sceneitem_t *, void *),\n\t\t\t\t    void *param)\n{\n\tif (!group || !group->is_group)\n\t\treturn;\n\n\tobs_scene_t *scene = group->source->context.data;\n\tif (scene)\n\t\tobs_scene_enum_items(scene, callback, param);\n}\n\nvoid obs_sceneitem_force_update_transform(obs_sceneitem_t *item)\n{\n\tif (!item)\n\t\treturn;\n\n\tif (os_atomic_set_bool(&item->update_transform, false))\n\t\tupdate_item_transform(item, false);\n}\n\nvoid obs_sceneitem_set_transition(obs_sceneitem_t *item, bool show, obs_source_t *transition)\n{\n\tif (!item)\n\t\treturn;\n\n\tobs_source_t **target = show ? &item->show_transition : &item->hide_transition;\n\tif (*target)\n\t\tobs_source_release(*target);\n\t*target = obs_source_get_ref(transition);\n}\n\nobs_source_t *obs_sceneitem_get_transition(obs_sceneitem_t *item, bool show)\n{\n\tif (!item)\n\t\treturn NULL;\n\n\treturn show ? item->show_transition : item->hide_transition;\n}\n\nvoid obs_sceneitem_set_transition_duration(obs_sceneitem_t *item, bool show, uint32_t duration_ms)\n{\n\tif (!item)\n\t\treturn;\n\tif (show)\n\t\titem->show_transition_duration = duration_ms;\n\telse\n\t\titem->hide_transition_duration = duration_ms;\n}\n\nuint32_t obs_sceneitem_get_transition_duration(obs_sceneitem_t *item, bool show)\n{\n\tif (!item)\n\t\treturn 0;\n\treturn show ? item->show_transition_duration : item->hide_transition_duration;\n}\n\nvoid obs_sceneitem_transition_stop(void *data, calldata_t *calldata)\n{\n\tobs_source_t *parent = data;\n\tobs_source_t *transition;\n\tcalldata_get_ptr(calldata, \"source\", &transition);\n\tobs_source_remove_active_child(parent, transition);\n\tsignal_handler_t *sh = obs_source_get_signal_handler(transition);\n\tif (sh)\n\t\tsignal_handler_disconnect(sh, \"transition_stop\", obs_sceneitem_transition_stop, parent);\n}\n\nvoid obs_sceneitem_do_transition(obs_sceneitem_t *item, bool visible)\n{\n\tif (!item)\n\t\treturn;\n\n\tif (transition_active(item->show_transition))\n\t\tobs_transition_force_stop(item->show_transition);\n\n\tif (transition_active(item->hide_transition))\n\t\tobs_transition_force_stop(item->hide_transition);\n\n\tobs_source_t *transition = obs_sceneitem_get_transition(item, visible);\n\tif (!transition)\n\t\treturn;\n\n\tint duration = (int)obs_sceneitem_get_transition_duration(item, visible);\n\n\tconst int cx = obs_source_get_width(item->source);\n\tconst int cy = obs_source_get_height(item->source);\n\tobs_transition_set_size(transition, cx, cy);\n\tobs_transition_set_alignment(transition, OBS_ALIGN_CENTER);\n\tobs_transition_set_scale_type(transition, OBS_TRANSITION_SCALE_ASPECT);\n\n\tif (duration == 0)\n\t\tduration = 300;\n\n\tobs_scene_t *scene = obs_sceneitem_get_scene(item);\n\tobs_source_t *parent = obs_scene_get_source(scene);\n\tobs_source_add_active_child(parent, transition);\n\n\tsignal_handler_t *sh = obs_source_get_signal_handler(transition);\n\tif (sh)\n\t\tsignal_handler_connect(sh, \"transition_stop\", obs_sceneitem_transition_stop, parent);\n\n\tif (!visible) {\n\t\tobs_transition_set(transition, item->source);\n\t\tobs_transition_start(transition, OBS_TRANSITION_MODE_AUTO, duration, NULL);\n\t} else {\n\t\tobs_transition_set(transition, NULL);\n\t\tobs_transition_start(transition, OBS_TRANSITION_MODE_AUTO, duration, item->source);\n\t}\n}\n\nvoid obs_sceneitem_transition_load(struct obs_scene_item *item, obs_data_t *data, bool show)\n{\n\tif (!item || !data)\n\t\treturn;\n\tconst char *id = obs_data_get_string(data, \"id\");\n\tif (id && strlen(id)) {\n\t\tconst char *tn = obs_data_get_string(data, \"name\");\n\t\tobs_data_t *s = obs_data_get_obj(data, \"transition\");\n\t\tobs_source_t *t = obs_source_create_private(id, tn, s);\n\t\tobs_sceneitem_set_transition(item, show, t);\n\t\tobs_source_release(t);\n\t\tobs_data_release(s);\n\t} else {\n\t\tobs_sceneitem_set_transition(item, show, NULL);\n\t}\n\tobs_sceneitem_set_transition_duration(item, show, (uint32_t)obs_data_get_int(data, \"duration\"));\n}\n\nobs_data_t *obs_sceneitem_transition_save(struct obs_scene_item *item, bool show)\n{\n\tobs_data_t *data = obs_data_create();\n\tstruct obs_source *transition = show ? item->show_transition : item->hide_transition;\n\tif (transition) {\n\t\tobs_data_set_string(data, \"id\", obs_source_get_unversioned_id(transition));\n\t\tobs_data_set_string(data, \"versioned_id\", obs_source_get_id(transition));\n\t\tobs_data_set_string(data, \"name\", obs_source_get_name(transition));\n\t\tobs_data_t *s = obs_source_get_settings(transition);\n\t\tobs_data_set_obj(data, \"transition\", s);\n\t\tobs_data_release(s);\n\t}\n\tobs_data_set_int(data, \"duration\", show ? item->show_transition_duration : item->hide_transition_duration);\n\treturn data;\n}\n\nvoid obs_scene_prune_sources(obs_scene_t *scene)\n{\n\tobs_scene_item_ptr_array_t remove_items;\n\tda_init(remove_items);\n\n\tvideo_lock(scene);\n\tupdate_transforms_and_prune_sources(scene, &remove_items, NULL, false);\n\tvideo_unlock(scene);\n\n\tfor (size_t i = 0; i < remove_items.num; i++)\n\t\tobs_sceneitem_release(remove_items.array[i]);\n\n\tda_free(remove_items);\n}\n"
  },
  {
    "path": "libobs/obs-scene.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"obs.h\"\n#include \"graphics/matrix4.h\"\n\n/* how obs scene! */\n\nstruct item_action {\n\tbool visible;\n\tuint64_t timestamp;\n};\n\nstruct obs_scene_item {\n\tvolatile long ref;\n\tvolatile bool removed;\n\n\tbool is_group;\n\tbool is_scene;\n\tbool update_transform;\n\tbool update_group_resize;\n\n\tint64_t id;\n\n\tstruct obs_scene *parent;\n\tstruct obs_source *source;\n\tvolatile long active_refs;\n\tvolatile long defer_update;\n\tvolatile long defer_group_resize;\n\tbool user_visible;\n\tbool visible;\n\tbool selected;\n\tbool locked;\n\n\tgs_texrender_t *item_render;\n\tstruct obs_sceneitem_crop crop;\n\n\tbool absolute_coordinates;\n\tstruct vec2 pos;\n\tstruct vec2 scale;\n\tstruct vec2 scale_ref;\n\tfloat rot;\n\tuint32_t align;\n\n\t/* last width/height of the source, this is used to check whether\n\t * the transform needs updating */\n\tuint32_t last_width;\n\tuint32_t last_height;\n\n\tstruct vec2 output_scale;\n\tenum obs_scale_type scale_filter;\n\n\tenum obs_blending_method blend_method;\n\tenum obs_blending_type blend_type;\n\n\tstruct matrix4 box_transform;\n\tstruct vec2 box_scale;\n\tstruct matrix4 draw_transform;\n\n\tenum obs_bounds_type bounds_type;\n\tuint32_t bounds_align;\n\tstruct vec2 bounds;\n\tbool crop_to_bounds;\n\tstruct obs_sceneitem_crop bounds_crop;\n\n\tobs_hotkey_pair_id toggle_visibility;\n\n\tobs_data_t *private_settings;\n\n\tpthread_mutex_t actions_mutex;\n\tDARRAY(struct item_action) audio_actions;\n\n\tstruct obs_source *show_transition;\n\tstruct obs_source *hide_transition;\n\tuint32_t show_transition_duration;\n\tuint32_t hide_transition_duration;\n\n\t/* would do **prev_next, but not really great for reordering */\n\tstruct obs_scene_item *prev;\n\tstruct obs_scene_item *next;\n};\n\nstruct obs_scene {\n\tstruct obs_source *source;\n\n\tbool is_group;\n\tbool custom_size;\n\tuint32_t cx;\n\tuint32_t cy;\n\n\tbool absolute_coordinates;\n\tuint32_t last_width;\n\tuint32_t last_height;\n\n\tint64_t id_counter;\n\n\tpthread_mutex_t video_mutex;\n\tpthread_mutex_t audio_mutex;\n\tstruct obs_scene_item *first_item;\n};\n"
  },
  {
    "path": "libobs/obs-service.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-internal.h\"\n\n#define get_weak(service) ((obs_weak_service_t *)service->context.control)\n\nconst struct obs_service_info *find_service(const char *id)\n{\n\tsize_t i;\n\tfor (i = 0; i < obs->service_types.num; i++)\n\t\tif (strcmp(obs->service_types.array[i].id, id) == 0)\n\t\t\treturn obs->service_types.array + i;\n\n\treturn NULL;\n}\n\nconst char *obs_service_get_display_name(const char *id)\n{\n\tconst struct obs_service_info *info = find_service(id);\n\treturn (info != NULL) ? info->get_name(info->type_data) : NULL;\n}\n\nobs_module_t *obs_service_get_module(const char *id)\n{\n\tobs_module_t *module = obs->first_module;\n\twhile (module) {\n\t\tfor (size_t i = 0; i < module->services.num; i++) {\n\t\t\tif (strcmp(module->services.array[i], id) == 0) {\n\t\t\t\treturn module;\n\t\t\t}\n\t\t}\n\t\tmodule = module->next;\n\t}\n\n\tmodule = obs->first_disabled_module;\n\twhile (module) {\n\t\tfor (size_t i = 0; i < module->services.num; i++) {\n\t\t\tif (strcmp(module->services.array[i], id) == 0) {\n\t\t\t\treturn module;\n\t\t\t}\n\t\t}\n\t\tmodule = module->next;\n\t}\n\n\treturn NULL;\n}\n\nenum obs_module_load_state obs_service_load_state(const char *id)\n{\n\tobs_module_t *module = obs_service_get_module(id);\n\tif (!module) {\n\t\treturn OBS_MODULE_MISSING;\n\t}\n\treturn module->load_state;\n}\n\nstatic obs_service_t *obs_service_create_internal(const char *id, const char *name, obs_data_t *settings,\n\t\t\t\t\t\t  obs_data_t *hotkey_data, bool private)\n{\n\tconst struct obs_service_info *info = find_service(id);\n\tstruct obs_service *service;\n\n\tif (!info) {\n\t\tblog(LOG_ERROR, \"Service '%s' not found\", id);\n\t\treturn NULL;\n\t}\n\n\tservice = bzalloc(sizeof(struct obs_service));\n\n\tif (!obs_context_data_init(&service->context, OBS_OBJ_TYPE_SERVICE, settings, name, NULL, hotkey_data,\n\t\t\t\t   private)) {\n\t\tbfree(service);\n\t\treturn NULL;\n\t}\n\n\tservice->info = *info;\n\tservice->context.data = service->info.create(service->context.settings, service);\n\tif (!service->context.data)\n\t\tblog(LOG_ERROR, \"Failed to create service '%s'!\", name);\n\n\tobs_context_init_control(&service->context, service, (obs_destroy_cb)obs_service_destroy);\n\tobs_context_data_insert(&service->context, &obs->data.services_mutex, &obs->data.first_service);\n\n\tblog(LOG_DEBUG, \"service '%s' (%s) created\", name, id);\n\treturn service;\n}\n\nobs_service_t *obs_service_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data)\n{\n\treturn obs_service_create_internal(id, name, settings, hotkey_data, false);\n}\n\nobs_service_t *obs_service_create_private(const char *id, const char *name, obs_data_t *settings)\n{\n\treturn obs_service_create_internal(id, name, settings, NULL, true);\n}\n\nstatic void actually_destroy_service(struct obs_service *service)\n{\n\tif (service->context.data)\n\t\tservice->info.destroy(service->context.data);\n\n\tif (service->output)\n\t\tservice->output->service = NULL;\n\n\tblog(LOG_DEBUG, \"service '%s' destroyed\", service->context.name);\n\n\tobs_context_data_free(&service->context);\n\tif (service->owns_info_id)\n\t\tbfree((void *)service->info.id);\n\tbfree(service);\n}\n\nvoid obs_service_destroy(obs_service_t *service)\n{\n\tif (service) {\n\t\tobs_context_data_remove(&service->context);\n\n\t\tservice->destroy = true;\n\n\t\t/* do NOT destroy the service until the service is no\n\t\t * longer in use */\n\t\tif (!service->active)\n\t\t\tactually_destroy_service(service);\n\t}\n}\n\nconst char *obs_service_get_name(const obs_service_t *service)\n{\n\treturn obs_service_valid(service, \"obs_service_get_name\") ? service->context.name : NULL;\n}\n\nstatic inline obs_data_t *get_defaults(const struct obs_service_info *info)\n{\n\tobs_data_t *settings = obs_data_create();\n\tif (info->get_defaults)\n\t\tinfo->get_defaults(settings);\n\treturn settings;\n}\n\nobs_data_t *obs_service_defaults(const char *id)\n{\n\tconst struct obs_service_info *info = find_service(id);\n\treturn (info) ? get_defaults(info) : NULL;\n}\n\nobs_properties_t *obs_get_service_properties(const char *id)\n{\n\tconst struct obs_service_info *info = find_service(id);\n\tif (info && info->get_properties) {\n\t\tobs_data_t *defaults = get_defaults(info);\n\t\tobs_properties_t *properties;\n\n\t\tproperties = info->get_properties(NULL);\n\t\tobs_properties_apply_settings(properties, defaults);\n\t\tobs_data_release(defaults);\n\t\treturn properties;\n\t}\n\treturn NULL;\n}\n\nobs_properties_t *obs_service_properties(const obs_service_t *service)\n{\n\tif (!obs_service_valid(service, \"obs_service_properties\"))\n\t\treturn NULL;\n\n\tif (service->info.get_properties) {\n\t\tobs_properties_t *props;\n\t\tprops = service->info.get_properties(service->context.data);\n\t\tobs_properties_apply_settings(props, service->context.settings);\n\t\treturn props;\n\t}\n\n\treturn NULL;\n}\n\nconst char *obs_service_get_type(const obs_service_t *service)\n{\n\treturn obs_service_valid(service, \"obs_service_get_type\") ? service->info.id : NULL;\n}\n\nvoid obs_service_update(obs_service_t *service, obs_data_t *settings)\n{\n\tif (!obs_service_valid(service, \"obs_service_update\"))\n\t\treturn;\n\n\tobs_data_apply(service->context.settings, settings);\n\n\tif (service->info.update)\n\t\tservice->info.update(service->context.data, service->context.settings);\n}\n\nobs_data_t *obs_service_get_settings(const obs_service_t *service)\n{\n\tif (!obs_service_valid(service, \"obs_service_get_settings\"))\n\t\treturn NULL;\n\n\tobs_data_addref(service->context.settings);\n\treturn service->context.settings;\n}\n\nsignal_handler_t *obs_service_get_signal_handler(const obs_service_t *service)\n{\n\treturn obs_service_valid(service, \"obs_service_get_signal_handler\") ? service->context.signals : NULL;\n}\n\nproc_handler_t *obs_service_get_proc_handler(const obs_service_t *service)\n{\n\treturn obs_service_valid(service, \"obs_service_get_proc_handler\") ? service->context.procs : NULL;\n}\n\nvoid obs_service_activate(struct obs_service *service)\n{\n\tif (!obs_service_valid(service, \"obs_service_activate\"))\n\t\treturn;\n\tif (!service->output) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_service_deactivate: service '%s' \"\n\t\t     \"is not assigned to an output\",\n\t\t     obs_service_get_name(service));\n\t\treturn;\n\t}\n\tif (service->active)\n\t\treturn;\n\n\tif (service->info.activate)\n\t\tservice->info.activate(service->context.data, service->context.settings);\n\tservice->active = true;\n}\n\nvoid obs_service_deactivate(struct obs_service *service, bool remove)\n{\n\tif (!obs_service_valid(service, \"obs_service_deactivate\"))\n\t\treturn;\n\tif (!service->output) {\n\t\tblog(LOG_WARNING,\n\t\t     \"obs_service_deactivate: service '%s' \"\n\t\t     \"is not assigned to an output\",\n\t\t     obs_service_get_name(service));\n\t\treturn;\n\t}\n\n\tif (!service->active)\n\t\treturn;\n\n\tif (service->info.deactivate)\n\t\tservice->info.deactivate(service->context.data);\n\tservice->active = false;\n\n\tif (service->destroy)\n\t\tactually_destroy_service(service);\n\telse if (remove)\n\t\tservice->output = NULL;\n}\n\nbool obs_service_initialize(struct obs_service *service, struct obs_output *output)\n{\n\tif (!obs_service_valid(service, \"obs_service_initialize\"))\n\t\treturn false;\n\tif (!obs_output_valid(output, \"obs_service_initialize\"))\n\t\treturn false;\n\n\tif (service->info.initialize)\n\t\treturn service->info.initialize(service->context.data, output);\n\treturn true;\n}\n\nvoid obs_service_apply_encoder_settings(obs_service_t *service, obs_data_t *video_encoder_settings,\n\t\t\t\t\tobs_data_t *audio_encoder_settings)\n{\n\tif (!obs_service_valid(service, \"obs_service_apply_encoder_settings\"))\n\t\treturn;\n\tif (!service->info.apply_encoder_settings)\n\t\treturn;\n\n\tif (video_encoder_settings || audio_encoder_settings)\n\t\tservice->info.apply_encoder_settings(service->context.data, video_encoder_settings,\n\t\t\t\t\t\t     audio_encoder_settings);\n}\n\nvoid obs_service_release(obs_service_t *service)\n{\n\tif (!service)\n\t\treturn;\n\n\tobs_weak_service_t *control = get_weak(service);\n\tif (obs_ref_release(&control->ref)) {\n\t\t// The order of operations is important here since\n\t\t// get_context_by_name in obs.c relies on weak refs\n\t\t// being alive while the context is listed\n\t\tobs_service_destroy(service);\n\t\tobs_weak_service_release(control);\n\t}\n}\n\nvoid obs_weak_service_addref(obs_weak_service_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tobs_weak_ref_addref(&weak->ref);\n}\n\nvoid obs_weak_service_release(obs_weak_service_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tif (obs_weak_ref_release(&weak->ref))\n\t\tbfree(weak);\n}\n\nobs_service_t *obs_service_get_ref(obs_service_t *service)\n{\n\tif (!service)\n\t\treturn NULL;\n\n\treturn obs_weak_service_get_service(get_weak(service));\n}\n\nobs_weak_service_t *obs_service_get_weak_service(obs_service_t *service)\n{\n\tif (!service)\n\t\treturn NULL;\n\n\tobs_weak_service_t *weak = get_weak(service);\n\tobs_weak_service_addref(weak);\n\treturn weak;\n}\n\nobs_service_t *obs_weak_service_get_service(obs_weak_service_t *weak)\n{\n\tif (!weak)\n\t\treturn NULL;\n\n\tif (obs_weak_ref_get_ref(&weak->ref))\n\t\treturn weak->service;\n\n\treturn NULL;\n}\n\nbool obs_weak_service_references_service(obs_weak_service_t *weak, obs_service_t *service)\n{\n\treturn weak && service && weak->service == service;\n}\n\nvoid *obs_service_get_type_data(obs_service_t *service)\n{\n\treturn obs_service_valid(service, \"obs_service_get_type_data\") ? service->info.type_data : NULL;\n}\n\nconst char *obs_service_get_id(const obs_service_t *service)\n{\n\treturn obs_service_valid(service, \"obs_service_get_id\") ? service->info.id : NULL;\n}\n\nvoid obs_service_get_supported_resolutions(const obs_service_t *service, struct obs_service_resolution **resolutions,\n\t\t\t\t\t   size_t *count)\n{\n\tif (!obs_service_valid(service, \"obs_service_supported_resolutions\"))\n\t\treturn;\n\tif (!obs_ptr_valid(resolutions, \"obs_service_supported_resolutions\"))\n\t\treturn;\n\tif (!obs_ptr_valid(count, \"obs_service_supported_resolutions\"))\n\t\treturn;\n\n\t*resolutions = NULL;\n\t*count = 0;\n\n\tif (service->info.get_supported_resolutions)\n\t\tservice->info.get_supported_resolutions(service->context.data, resolutions, count);\n}\n\nvoid obs_service_get_max_fps(const obs_service_t *service, int *fps)\n{\n\tif (!obs_service_valid(service, \"obs_service_get_max_fps\"))\n\t\treturn;\n\tif (!obs_ptr_valid(fps, \"obs_service_get_max_fps\"))\n\t\treturn;\n\n\t*fps = 0;\n\n\tif (service->info.get_max_fps)\n\t\tservice->info.get_max_fps(service->context.data, fps);\n}\n\nvoid obs_service_get_max_bitrate(const obs_service_t *service, int *video_bitrate, int *audio_bitrate)\n{\n\tif (video_bitrate)\n\t\t*video_bitrate = 0;\n\tif (audio_bitrate)\n\t\t*audio_bitrate = 0;\n\n\tif (!obs_service_valid(service, \"obs_service_get_max_bitrate\"))\n\t\treturn;\n\n\tif (service->info.get_max_bitrate)\n\t\tservice->info.get_max_bitrate(service->context.data, video_bitrate, audio_bitrate);\n}\n\nconst char **obs_service_get_supported_video_codecs(const obs_service_t *service)\n{\n\tif (service->info.get_supported_video_codecs)\n\t\treturn service->info.get_supported_video_codecs(service->context.data);\n\treturn NULL;\n}\n\nconst char **obs_service_get_supported_audio_codecs(const obs_service_t *service)\n{\n\tif (service->info.get_supported_audio_codecs)\n\t\treturn service->info.get_supported_audio_codecs(service->context.data);\n\treturn NULL;\n}\n\nconst char *obs_service_get_protocol(const obs_service_t *service)\n{\n\tif (!obs_service_valid(service, \"obs_service_get_protocol\"))\n\t\treturn NULL;\n\n\treturn service->info.get_protocol(service->context.data);\n}\n\nconst char *obs_service_get_preferred_output_type(const obs_service_t *service)\n{\n\tif (!obs_service_valid(service, \"obs_service_get_preferred_output_type\"))\n\t\treturn NULL;\n\n\tif (service->info.get_output_type)\n\t\treturn service->info.get_output_type(service->context.data);\n\treturn NULL;\n}\n\nconst char *obs_service_get_connect_info(const obs_service_t *service, uint32_t type)\n{\n\tif (!obs_service_valid(service, \"obs_service_get_info\"))\n\t\treturn NULL;\n\n\tif (!service->info.get_connect_info)\n\t\treturn NULL;\n\treturn service->info.get_connect_info(service->context.data, type);\n}\n\nbool obs_service_can_try_to_connect(const obs_service_t *service)\n{\n\tif (!obs_service_valid(service, \"obs_service_can_connect\"))\n\t\treturn false;\n\n\tif (!service->info.can_try_to_connect)\n\t\treturn true;\n\treturn service->info.can_try_to_connect(service->context.data);\n}\n"
  },
  {
    "path": "libobs/obs-service.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n/**\n * @file\n * @brief header for modules implementing services.\n *\n * Services are modules that implement provider specific settings for outputs.\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct obs_service_resolution {\n\tint cx;\n\tint cy;\n};\n\n/* NOTE: Odd numbers are reserved for custom info from third-party protocols */\nenum obs_service_connect_info {\n\tOBS_SERVICE_CONNECT_INFO_SERVER_URL = 0,\n\tOBS_SERVICE_CONNECT_INFO_STREAM_ID = 2,\n\tOBS_SERVICE_CONNECT_INFO_STREAM_KEY = 2, // Alias of OBS_SERVICE_CONNECT_INFO_STREAM_ID\n\tOBS_SERVICE_CONNECT_INFO_USERNAME = 4,\n\tOBS_SERVICE_CONNECT_INFO_PASSWORD = 6,\n\tOBS_SERVICE_CONNECT_INFO_ENCRYPT_PASSPHRASE = 8,\n\tOBS_SERVICE_CONNECT_INFO_BEARER_TOKEN = 10,\n};\n\nstruct obs_service_info {\n\t/* required */\n\tconst char *id;\n\n\tconst char *(*get_name)(void *type_data);\n\tvoid *(*create)(obs_data_t *settings, obs_service_t *service);\n\tvoid (*destroy)(void *data);\n\n\t/* optional */\n\tvoid (*activate)(void *data, obs_data_t *settings);\n\tvoid (*deactivate)(void *data);\n\n\tvoid (*update)(void *data, obs_data_t *settings);\n\n\tvoid (*get_defaults)(obs_data_t *settings);\n\n\tobs_properties_t *(*get_properties)(void *data);\n\n\t/**\n\t * Called when getting ready to start up an output, before the encoders\n\t * and output are initialized\n\t *\n\t * @param  data    Internal service data\n\t * @param  output  Output context\n\t * @return         true to allow the output to start up,\n\t *                 false to prevent output from starting up\n\t */\n\tbool (*initialize)(void *data, obs_output_t *output);\n\n\tconst char *(*get_url)(void *data);\n\tconst char *(*get_key)(void *data);\n\n\tconst char *(*get_username)(void *data);\n\tconst char *(*get_password)(void *data);\n\n\tbool (*deprecated_1)();\n\n\tvoid (*apply_encoder_settings)(void *data, obs_data_t *video_encoder_settings,\n\t\t\t\t       obs_data_t *audio_encoder_settings);\n\n\tvoid *type_data;\n\tvoid (*free_type_data)(void *type_data);\n\n\t/* TODO: Rename to 'get_preferred_output_type' once a API/ABI break happen */\n\tconst char *(*get_output_type)(void *data);\n\n\tvoid (*get_supported_resolutions)(void *data, struct obs_service_resolution **resolutions, size_t *count);\n\tvoid (*get_max_fps)(void *data, int *fps);\n\n\tvoid (*get_max_bitrate)(void *data, int *video_bitrate, int *audio_bitrate);\n\n\tconst char **(*get_supported_video_codecs)(void *data);\n\n\tconst char *(*get_protocol)(void *data);\n\n\tconst char **(*get_supported_audio_codecs)(void *data);\n\n\tconst char *(*get_connect_info)(void *data, uint32_t type);\n\n\tbool (*can_try_to_connect)(void *data);\n};\n\nEXPORT void obs_register_service_s(const struct obs_service_info *info, size_t size);\n\n#define obs_register_service(info) obs_register_service_s(info, sizeof(struct obs_service_info))\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obs-source-deinterlace.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-internal.h\"\n\nstatic bool ready_deinterlace_frames(obs_source_t *source, uint64_t sys_time)\n{\n\tstruct obs_source_frame *next_frame = source->async_frames.array[0];\n\tstruct obs_source_frame *prev_frame = NULL;\n\tstruct obs_source_frame *frame = NULL;\n\tuint64_t sys_offset = sys_time - source->last_sys_timestamp;\n\tuint64_t frame_time = next_frame->timestamp;\n\tuint64_t frame_offset = 0;\n\tsize_t idx = 1;\n\n\tif (source->async_unbuffered) {\n\t\twhile (source->async_frames.num > 2) {\n\t\t\tda_erase(source->async_frames, 0);\n\t\t\tremove_async_frame(source, next_frame);\n\t\t\tnext_frame = source->async_frames.array[0];\n\t\t}\n\n\t\tif (source->async_frames.num == 2) {\n\t\t\tbool prev_frame = true;\n\t\t\tif (source->async_unbuffered && source->deinterlace_offset) {\n\t\t\t\tconst uint64_t timestamp = source->async_frames.array[0]->timestamp;\n\t\t\t\tconst uint64_t after_timestamp = source->async_frames.array[1]->timestamp;\n\t\t\t\tconst uint64_t duration = after_timestamp - timestamp;\n\t\t\t\tconst uint64_t frame_end = timestamp + source->deinterlace_offset + duration;\n\t\t\t\tif (sys_time < frame_end) {\n\t\t\t\t\t// Don't skip ahead prematurely.\n\t\t\t\t\tprev_frame = false;\n\t\t\t\t\tsource->deinterlace_frame_ts = timestamp - duration;\n\t\t\t\t}\n\t\t\t}\n\t\t\tsource->async_frames.array[0]->prev_frame = prev_frame;\n\t\t}\n\t\tsource->deinterlace_offset = 0;\n\t\tsource->last_frame_ts = next_frame->timestamp;\n\t\treturn true;\n\t}\n\n\t/* account for timestamp invalidation */\n\tif (frame_out_of_bounds(source, frame_time)) {\n\t\tsource->last_frame_ts = next_frame->timestamp;\n\t\tsource->deinterlace_offset = 0;\n\t\treturn true;\n\t} else {\n\t\tframe_offset = frame_time - source->last_frame_ts;\n\t\tsource->last_frame_ts += sys_offset;\n\t}\n\n\twhile (source->last_frame_ts > next_frame->timestamp) {\n\n\t\t/* this tries to reduce the needless frame duplication, also\n\t\t * helps smooth out async rendering to frame boundaries.  In\n\t\t * other words, tries to keep the framerate as smooth as\n\t\t * possible */\n\t\tif ((source->last_frame_ts - next_frame->timestamp) < 2000000)\n\t\t\tbreak;\n\n\t\tif (prev_frame) {\n\t\t\tda_erase(source->async_frames, 0);\n\t\t\tremove_async_frame(source, prev_frame);\n\t\t}\n\n\t\tif (source->async_frames.num <= 2) {\n\t\t\tbool exit = true;\n\n\t\t\tif (prev_frame) {\n\t\t\t\tprev_frame->prev_frame = true;\n\n\t\t\t} else if (!frame && source->async_frames.num == 2) {\n\t\t\t\texit = false;\n\t\t\t}\n\n\t\t\tif (exit) {\n\t\t\t\tsource->deinterlace_offset = 0;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tif (frame)\n\t\t\tidx = 2;\n\t\telse\n\t\t\tidx = 1;\n\n\t\tprev_frame = frame;\n\t\tframe = next_frame;\n\t\tnext_frame = source->async_frames.array[idx];\n\n\t\t/* more timestamp checking and compensating */\n\t\tif ((next_frame->timestamp - frame_time) > MAX_TS_VAR) {\n\t\t\tsource->last_frame_ts = next_frame->timestamp - frame_offset;\n\t\t\tsource->deinterlace_offset = 0;\n\t\t}\n\n\t\tframe_time = next_frame->timestamp;\n\t\tframe_offset = frame_time - source->last_frame_ts;\n\t}\n\n\tif (prev_frame)\n\t\tprev_frame->prev_frame = true;\n\n\treturn frame != NULL;\n}\n\nstatic inline bool first_frame(obs_source_t *s)\n{\n\tif (s->last_frame_ts)\n\t\treturn false;\n\n\tif (s->async_frames.num >= 2)\n\t\ts->async_frames.array[0]->prev_frame = true;\n\treturn true;\n}\n\nstatic inline uint64_t uint64_diff(uint64_t ts1, uint64_t ts2)\n{\n\treturn (ts1 < ts2) ? (ts2 - ts1) : (ts1 - ts2);\n}\n\n#define TWOX_TOLERANCE 1000000\n#define TS_JUMP_THRESHOLD 70000000ULL\n\nstatic inline void deinterlace_get_closest_frames(obs_source_t *s, uint64_t sys_time)\n{\n\tuint64_t half_interval;\n\n\tif (s->async_unbuffered && s->deinterlace_offset) {\n\t\t// Want to keep frame if it has not elapsed.\n\t\tconst uint64_t frame_end = s->deinterlace_frame_ts + s->deinterlace_offset +\n\t\t\t\t\t   ((uint64_t)s->deinterlace_half_duration * 2) - TWOX_TOLERANCE;\n\t\tif (sys_time < frame_end) {\n\t\t\t// Process new frames if we think time jumped.\n\t\t\tconst uint64_t diff = frame_end - sys_time;\n\t\t\tif (diff < TS_JUMP_THRESHOLD) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!s->async_frames.num)\n\t\treturn;\n\n\thalf_interval = obs->video.video_half_frame_interval_ns;\n\n\tif (first_frame(s) || ready_deinterlace_frames(s, sys_time)) {\n\t\tuint64_t offset;\n\n\t\ts->prev_async_frame = NULL;\n\t\ts->cur_async_frame = s->async_frames.array[0];\n\n\t\tda_erase(s->async_frames, 0);\n\n\t\tif ((s->async_frames.num > 0) && s->cur_async_frame->prev_frame) {\n\t\t\ts->prev_async_frame = s->cur_async_frame;\n\t\t\ts->cur_async_frame = s->async_frames.array[0];\n\n\t\t\tda_erase(s->async_frames, 0);\n\n\t\t\ts->deinterlace_half_duration =\n\t\t\t\t(uint32_t)((s->cur_async_frame->timestamp - s->prev_async_frame->timestamp) / 2);\n\t\t} else {\n\t\t\ts->deinterlace_half_duration =\n\t\t\t\t(uint32_t)((s->cur_async_frame->timestamp - s->deinterlace_frame_ts) / 2);\n\t\t}\n\n\t\tif (!s->last_frame_ts)\n\t\t\ts->last_frame_ts = s->cur_async_frame->timestamp;\n\n\t\ts->deinterlace_frame_ts = s->cur_async_frame->timestamp;\n\n\t\toffset = obs->video.video_time - s->deinterlace_frame_ts;\n\n\t\tif (!s->deinterlace_offset) {\n\t\t\ts->deinterlace_offset = offset;\n\t\t} else {\n\t\t\tuint64_t offset_diff = uint64_diff(s->deinterlace_offset, offset);\n\t\t\tif (offset_diff > half_interval)\n\t\t\t\ts->deinterlace_offset = offset;\n\t\t}\n\t}\n}\n\nvoid deinterlace_process_last_frame(obs_source_t *s, uint64_t sys_time)\n{\n\tif (s->prev_async_frame) {\n\t\tremove_async_frame(s, s->prev_async_frame);\n\t\ts->prev_async_frame = NULL;\n\t}\n\tif (s->cur_async_frame) {\n\t\tremove_async_frame(s, s->cur_async_frame);\n\t\ts->cur_async_frame = NULL;\n\t}\n\n\tdeinterlace_get_closest_frames(s, sys_time);\n}\n\nvoid set_deinterlace_texture_size(obs_source_t *source)\n{\n\tconst enum gs_color_format format = convert_video_format(source->async_format, source->async_trc);\n\n\tif (source->async_gpu_conversion) {\n\t\tsource->async_prev_texrender = gs_texrender_create(format, GS_ZS_NONE);\n\n\t\tfor (int c = 0; c < source->async_channel_count; c++)\n\t\t\tsource->async_prev_textures[c] =\n\t\t\t\tgs_texture_create(source->async_convert_width[c], source->async_convert_height[c],\n\t\t\t\t\t\t  source->async_texture_formats[c], 1, NULL, GS_DYNAMIC);\n\t} else {\n\t\tsource->async_prev_textures[0] =\n\t\t\tgs_texture_create(source->async_width, source->async_height, format, 1, NULL, GS_DYNAMIC);\n\t}\n}\n\nvoid deinterlace_update_async_video(obs_source_t *source)\n{\n\tif (source->deinterlace_rendered)\n\t\treturn;\n\n\tsource->deinterlace_rendered = true;\n\n\tpthread_mutex_lock(&source->async_mutex);\n\n\tconst bool updated = source->cur_async_frame != NULL;\n\tstruct obs_source_frame *frame = source->prev_async_frame;\n\tsource->prev_async_frame = NULL;\n\n\tpthread_mutex_unlock(&source->async_mutex);\n\n\tif (frame) {\n\t\tos_atomic_inc_long(&frame->refs);\n\n\t\tif (set_async_texture_size(source, frame)) {\n\t\t\tupdate_async_textures(source, frame, source->async_prev_textures, source->async_prev_texrender);\n\t\t}\n\n\t\tobs_source_release_frame(source, frame);\n\n\t} else if (updated) { /* swap cur/prev if no previous texture */\n\t\tfor (size_t c = 0; c < MAX_AV_PLANES; c++) {\n\t\t\tgs_texture_t *prev_tex = source->async_prev_textures[c];\n\t\t\tsource->async_prev_textures[c] = source->async_textures[c];\n\t\t\tsource->async_textures[c] = prev_tex;\n\t\t}\n\n\t\tif (source->async_texrender) {\n\t\t\tgs_texrender_t *prev = source->async_prev_texrender;\n\t\t\tsource->async_prev_texrender = source->async_texrender;\n\t\t\tsource->async_texrender = prev;\n\t\t}\n\t}\n}\n\nstatic inline gs_effect_t *get_effect(enum obs_deinterlace_mode mode)\n{\n\tswitch (mode) {\n\tcase OBS_DEINTERLACE_MODE_DISABLE:\n\t\treturn NULL;\n\tcase OBS_DEINTERLACE_MODE_DISCARD:\n\t\treturn obs_load_effect(&obs->video.deinterlace_discard_effect, \"deinterlace_discard.effect\");\n\tcase OBS_DEINTERLACE_MODE_RETRO:\n\t\treturn obs_load_effect(&obs->video.deinterlace_discard_2x_effect, \"deinterlace_discard_2x.effect\");\n\tcase OBS_DEINTERLACE_MODE_BLEND:\n\t\treturn obs_load_effect(&obs->video.deinterlace_blend_effect, \"deinterlace_blend.effect\");\n\tcase OBS_DEINTERLACE_MODE_BLEND_2X:\n\t\treturn obs_load_effect(&obs->video.deinterlace_blend_2x_effect, \"deinterlace_blend_2x.effect\");\n\tcase OBS_DEINTERLACE_MODE_LINEAR:\n\t\treturn obs_load_effect(&obs->video.deinterlace_linear_effect, \"deinterlace_linear.effect\");\n\tcase OBS_DEINTERLACE_MODE_LINEAR_2X:\n\t\treturn obs_load_effect(&obs->video.deinterlace_linear_2x_effect, \"deinterlace_linear_2x.effect\");\n\tcase OBS_DEINTERLACE_MODE_YADIF:\n\t\treturn obs_load_effect(&obs->video.deinterlace_yadif_effect, \"deinterlace_yadif.effect\");\n\tcase OBS_DEINTERLACE_MODE_YADIF_2X:\n\t\treturn obs_load_effect(&obs->video.deinterlace_yadif_2x_effect, \"deinterlace_yadif_2x.effect\");\n\t}\n\n\treturn NULL;\n}\n\nstatic bool deinterlace_linear_required(enum obs_deinterlace_mode mode)\n{\n\tswitch (mode) {\n\tcase OBS_DEINTERLACE_MODE_DISABLE:\n\tcase OBS_DEINTERLACE_MODE_DISCARD:\n\tcase OBS_DEINTERLACE_MODE_RETRO:\n\t\treturn false;\n\tcase OBS_DEINTERLACE_MODE_BLEND:\n\tcase OBS_DEINTERLACE_MODE_BLEND_2X:\n\tcase OBS_DEINTERLACE_MODE_LINEAR:\n\tcase OBS_DEINTERLACE_MODE_LINEAR_2X:\n\tcase OBS_DEINTERLACE_MODE_YADIF:\n\tcase OBS_DEINTERLACE_MODE_YADIF_2X:\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nvoid deinterlace_render(obs_source_t *s)\n{\n\tgs_effect_t *effect = s->deinterlace_effect;\n\n\tgs_eparam_t *image = gs_effect_get_param_by_name(effect, \"image\");\n\tgs_eparam_t *prev = gs_effect_get_param_by_name(effect, \"previous_image\");\n\tgs_eparam_t *multiplier_param = gs_effect_get_param_by_name(effect, \"multiplier\");\n\tgs_eparam_t *field = gs_effect_get_param_by_name(effect, \"field_order\");\n\tgs_eparam_t *frame2 = gs_effect_get_param_by_name(effect, \"frame2\");\n\tgs_eparam_t *dimensions = gs_effect_get_param_by_name(effect, \"dimensions\");\n\tstruct vec2 size = {(float)s->async_width, (float)s->async_height};\n\n\tgs_texture_t *cur_tex = s->async_texrender ? gs_texrender_get_texture(s->async_texrender)\n\t\t\t\t\t\t   : s->async_textures[0];\n\tgs_texture_t *prev_tex = s->async_prev_texrender ? gs_texrender_get_texture(s->async_prev_texrender)\n\t\t\t\t\t\t\t : s->async_prev_textures[0];\n\n\tif (!cur_tex || !prev_tex || !s->async_width || !s->async_height)\n\t\treturn;\n\n\tconst enum gs_color_space source_space = convert_video_space(s->async_format, s->async_trc);\n\n\tconst bool linear_srgb = (source_space != GS_CS_SRGB) || gs_get_linear_srgb() ||\n\t\t\t\t deinterlace_linear_required(s->deinterlace_mode);\n\n\tconst enum gs_color_space current_space = gs_get_color_space();\n\tconst char *tech_name = \"Draw\";\n\tfloat multiplier = 1.0;\n\tswitch (source_space) {\n\tcase GS_CS_SRGB:\n\tcase GS_CS_SRGB_16F:\n\t\tif (current_space == GS_CS_709_SCRGB) {\n\t\t\ttech_name = \"DrawMultiply\";\n\t\t\tmultiplier = obs_get_video_sdr_white_level() / 80.0f;\n\t\t}\n\t\tbreak;\n\tcase GS_CS_709_EXTENDED:\n\t\tswitch (current_space) {\n\t\tcase GS_CS_SRGB:\n\t\tcase GS_CS_SRGB_16F:\n\t\t\ttech_name = \"DrawTonemap\";\n\t\t\tbreak;\n\t\tcase GS_CS_709_SCRGB:\n\t\t\ttech_name = \"DrawMultiply\";\n\t\t\tmultiplier = obs_get_video_sdr_white_level() / 80.0f;\n\t\t\tbreak;\n\t\tcase GS_CS_709_EXTENDED:\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase GS_CS_709_SCRGB:\n\t\tswitch (current_space) {\n\t\tcase GS_CS_SRGB:\n\t\tcase GS_CS_SRGB_16F:\n\t\t\ttech_name = \"DrawMultiplyTonemap\";\n\t\t\tmultiplier = 80.0f / obs_get_video_sdr_white_level();\n\t\t\tbreak;\n\t\tcase GS_CS_709_EXTENDED:\n\t\t\ttech_name = \"DrawMultiply\";\n\t\t\tmultiplier = 80.0f / obs_get_video_sdr_white_level();\n\t\t\tbreak;\n\t\tcase GS_CS_709_SCRGB:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst bool previous = gs_framebuffer_srgb_enabled();\n\tgs_enable_framebuffer_srgb(linear_srgb);\n\n\tif (linear_srgb) {\n\t\tgs_effect_set_texture_srgb(image, cur_tex);\n\t\tgs_effect_set_texture_srgb(prev, prev_tex);\n\t} else {\n\t\tgs_effect_set_texture(image, cur_tex);\n\t\tgs_effect_set_texture(prev, prev_tex);\n\t}\n\n\tgs_effect_set_float(multiplier_param, multiplier);\n\tgs_effect_set_int(field, s->deinterlace_top_first);\n\tgs_effect_set_vec2(dimensions, &size);\n\n\tconst uint64_t frame2_ts =\n\t\ts->deinterlace_frame_ts + s->deinterlace_offset + s->deinterlace_half_duration - TWOX_TOLERANCE;\n\tgs_effect_set_bool(frame2, obs->video.video_time >= frame2_ts);\n\n\twhile (gs_effect_loop(effect, tech_name))\n\t\tgs_draw_sprite(NULL, s->async_flip ? GS_FLIP_V : 0, s->async_width, s->async_height);\n\n\tgs_enable_framebuffer_srgb(previous);\n}\n\nstatic void enable_deinterlacing(obs_source_t *source, enum obs_deinterlace_mode mode)\n{\n\tobs_enter_graphics();\n\n\tif (source->async_format != VIDEO_FORMAT_NONE && source->async_width != 0 && source->async_height != 0)\n\t\tset_deinterlace_texture_size(source);\n\n\tsource->deinterlace_mode = mode;\n\tsource->deinterlace_effect = get_effect(mode);\n\n\tpthread_mutex_lock(&source->async_mutex);\n\tif (source->prev_async_frame) {\n\t\tremove_async_frame(source, source->prev_async_frame);\n\t\tsource->prev_async_frame = NULL;\n\t}\n\tpthread_mutex_unlock(&source->async_mutex);\n\n\tobs_leave_graphics();\n}\n\nstatic void disable_deinterlacing(obs_source_t *source)\n{\n\tobs_enter_graphics();\n\tgs_texture_destroy(source->async_prev_textures[0]);\n\tgs_texture_destroy(source->async_prev_textures[1]);\n\tgs_texture_destroy(source->async_prev_textures[2]);\n\tgs_texrender_destroy(source->async_prev_texrender);\n\tsource->deinterlace_mode = OBS_DEINTERLACE_MODE_DISABLE;\n\tsource->async_prev_textures[0] = NULL;\n\tsource->async_prev_textures[1] = NULL;\n\tsource->async_prev_textures[2] = NULL;\n\tsource->async_prev_texrender = NULL;\n\tobs_leave_graphics();\n}\n\nvoid obs_source_set_deinterlace_mode(obs_source_t *source, enum obs_deinterlace_mode mode)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_deinterlace_mode\"))\n\t\treturn;\n\tif (source->deinterlace_mode == mode)\n\t\treturn;\n\n\tif (source->deinterlace_mode == OBS_DEINTERLACE_MODE_DISABLE) {\n\t\tenable_deinterlacing(source, mode);\n\t} else if (mode == OBS_DEINTERLACE_MODE_DISABLE) {\n\t\tdisable_deinterlacing(source);\n\t} else {\n\t\tobs_enter_graphics();\n\t\tsource->deinterlace_mode = mode;\n\t\tsource->deinterlace_effect = get_effect(mode);\n\t\tobs_leave_graphics();\n\t}\n}\n\nenum obs_deinterlace_mode obs_source_get_deinterlace_mode(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_set_deinterlace_mode\") ? source->deinterlace_mode\n\t\t\t\t\t\t\t\t\t   : OBS_DEINTERLACE_MODE_DISABLE;\n}\n\nvoid obs_source_set_deinterlace_field_order(obs_source_t *source, enum obs_deinterlace_field_order field_order)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_deinterlace_field_order\"))\n\t\treturn;\n\n\tsource->deinterlace_top_first = field_order == OBS_DEINTERLACE_FIELD_ORDER_TOP;\n}\n\nenum obs_deinterlace_field_order obs_source_get_deinterlace_field_order(const obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_deinterlace_field_order\"))\n\t\treturn OBS_DEINTERLACE_FIELD_ORDER_TOP;\n\n\treturn source->deinterlace_top_first ? OBS_DEINTERLACE_FIELD_ORDER_TOP : OBS_DEINTERLACE_FIELD_ORDER_BOTTOM;\n}\n"
  },
  {
    "path": "libobs/obs-source-transition.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-internal.h\"\n#include \"util/util_uint64.h\"\n#include \"graphics/math-extra.h\"\n\n#define lock_transition(transition) pthread_mutex_lock(&transition->transition_mutex);\n#define unlock_transition(transition) pthread_mutex_unlock(&transition->transition_mutex);\n\n#define trylock_textures(transition) pthread_mutex_trylock(&transition->transition_tex_mutex)\n#define lock_textures(transition) pthread_mutex_lock(&transition->transition_tex_mutex)\n#define unlock_textures(transition) pthread_mutex_unlock(&transition->transition_tex_mutex)\n\nstatic inline bool transition_valid(const obs_source_t *transition, const char *func)\n{\n\tif (!obs_ptr_valid(transition, func))\n\t\treturn false;\n\telse if (transition->info.type != OBS_SOURCE_TYPE_TRANSITION)\n\t\treturn false;\n\n\treturn true;\n}\n\nbool obs_transition_init(obs_source_t *transition)\n{\n\tpthread_mutex_init_value(&transition->transition_mutex);\n\tpthread_mutex_init_value(&transition->transition_tex_mutex);\n\tif (pthread_mutex_init(&transition->transition_mutex, NULL) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&transition->transition_tex_mutex, NULL) != 0)\n\t\treturn false;\n\n\ttransition->transition_alignment = OBS_ALIGN_LEFT | OBS_ALIGN_TOP;\n\ttransition->transition_texrender[0] = gs_texrender_create(GS_RGBA, GS_ZS_NONE);\n\ttransition->transition_texrender[1] = gs_texrender_create(GS_RGBA, GS_ZS_NONE);\n\ttransition->transition_source_active[0] = true;\n\n\treturn transition->transition_texrender[0] != NULL && transition->transition_texrender[1] != NULL;\n}\n\nvoid obs_transition_free(obs_source_t *transition)\n{\n\tpthread_mutex_destroy(&transition->transition_mutex);\n\tpthread_mutex_destroy(&transition->transition_tex_mutex);\n\n\tgs_enter_context(obs->video.graphics);\n\tgs_texrender_destroy(transition->transition_texrender[0]);\n\tgs_texrender_destroy(transition->transition_texrender[1]);\n\tgs_leave_context();\n}\n\nvoid obs_transition_clear(obs_source_t *transition)\n{\n\tobs_source_t *s[2];\n\tbool active[2];\n\n\tif (!transition_valid(transition, \"obs_transition_clear\"))\n\t\treturn;\n\n\tlock_transition(transition);\n\tfor (size_t i = 0; i < 2; i++) {\n\t\ts[i] = transition->transition_sources[i];\n\t\tactive[i] = transition->transition_source_active[i];\n\t\ttransition->transition_sources[i] = NULL;\n\t\ttransition->transition_source_active[i] = false;\n\t}\n\ttransition->transitioning_video = false;\n\ttransition->transitioning_audio = false;\n\tunlock_transition(transition);\n\n\tfor (size_t i = 0; i < 2; i++) {\n\t\tif (s[i] && active[i])\n\t\t\tobs_source_remove_active_child(transition, s[i]);\n\t\tobs_source_release(s[i]);\n\t}\n}\n\nvoid add_alignment(struct vec2 *v, uint32_t align, int cx, int cy);\n\nstatic inline uint32_t get_cx(obs_source_t *tr)\n{\n\treturn tr->transition_cx ? tr->transition_cx : tr->transition_actual_cx;\n}\n\nstatic inline uint32_t get_cy(obs_source_t *tr)\n{\n\treturn tr->transition_cy ? tr->transition_cy : tr->transition_actual_cy;\n}\n\nstatic void recalculate_transition_matrix(obs_source_t *tr, size_t idx)\n{\n\tobs_source_t *child;\n\tstruct matrix4 mat;\n\tstruct vec2 pos;\n\tstruct vec2 scale;\n\tfloat tr_cx = (float)get_cx(tr);\n\tfloat tr_cy = (float)get_cy(tr);\n\tfloat source_cx;\n\tfloat source_cy;\n\tfloat tr_aspect = tr_cx / tr_cy;\n\tfloat source_aspect;\n\tenum obs_transition_scale_type scale_type = tr->transition_scale_type;\n\n\tlock_transition(tr);\n\n\tchild = tr->transition_sources[idx];\n\tif (!child) {\n\t\tunlock_transition(tr);\n\t\treturn;\n\t}\n\n\tsource_cx = (float)obs_source_get_width(child);\n\tsource_cy = (float)obs_source_get_height(child);\n\tunlock_transition(tr);\n\n\tif (source_cx == 0.0f || source_cy == 0.0f)\n\t\treturn;\n\n\tsource_aspect = source_cx / source_cy;\n\n\tif (scale_type == OBS_TRANSITION_SCALE_MAX_ONLY) {\n\t\tif (source_cx > tr_cx || source_cy > tr_cy) {\n\t\t\tscale_type = OBS_TRANSITION_SCALE_ASPECT;\n\t\t} else {\n\t\t\tscale.x = 1.0f;\n\t\t\tscale.y = 1.0f;\n\t\t}\n\t}\n\n\tif (scale_type == OBS_TRANSITION_SCALE_ASPECT) {\n\t\tbool use_width = tr_aspect < source_aspect;\n\t\tscale.x = scale.y = use_width ? tr_cx / source_cx : tr_cy / source_cy;\n\n\t} else if (scale_type == OBS_TRANSITION_SCALE_STRETCH) {\n\t\tscale.x = tr_cx / source_cx;\n\t\tscale.y = tr_cy / source_cy;\n\t}\n\n\tsource_cx *= scale.x;\n\tsource_cy *= scale.y;\n\n\tvec2_zero(&pos);\n\tadd_alignment(&pos, tr->transition_alignment, (int)(tr_cx - source_cx), (int)(tr_cy - source_cy));\n\n\tmatrix4_identity(&mat);\n\tmatrix4_scale3f(&mat, &mat, scale.x, scale.y, 1.0f);\n\tmatrix4_translate3f(&mat, &mat, pos.x, pos.y, 0.0f);\n\tmatrix4_copy(&tr->transition_matrices[idx], &mat);\n}\n\nstatic inline void recalculate_transition_matrices(obs_source_t *transition)\n{\n\trecalculate_transition_matrix(transition, 0);\n\trecalculate_transition_matrix(transition, 1);\n}\n\nstatic void recalculate_transition_size(obs_source_t *transition)\n{\n\tuint32_t cx = 0, cy = 0;\n\tobs_source_t *child;\n\n\tlock_transition(transition);\n\n\tfor (size_t i = 0; i < 2; i++) {\n\t\tchild = transition->transition_sources[i];\n\t\tif (child) {\n\t\t\tuint32_t new_cx = obs_source_get_width(child);\n\t\t\tuint32_t new_cy = obs_source_get_height(child);\n\t\t\tif (new_cx > cx)\n\t\t\t\tcx = new_cx;\n\t\t\tif (new_cy > cy)\n\t\t\t\tcy = new_cy;\n\t\t}\n\t}\n\n\tunlock_transition(transition);\n\n\ttransition->transition_actual_cx = cx;\n\ttransition->transition_actual_cy = cy;\n}\n\nvoid obs_transition_tick(obs_source_t *transition, float t)\n{\n\trecalculate_transition_size(transition);\n\trecalculate_transition_matrices(transition);\n\n\tif (transition->transition_mode == OBS_TRANSITION_MODE_MANUAL) {\n\t\tif (transition->transition_manual_torque == 0.0f) {\n\t\t\ttransition->transition_manual_val = transition->transition_manual_target;\n\t\t} else {\n\t\t\ttransition->transition_manual_val = calc_torquef(transition->transition_manual_val,\n\t\t\t\t\t\t\t\t\t transition->transition_manual_target,\n\t\t\t\t\t\t\t\t\t transition->transition_manual_torque,\n\t\t\t\t\t\t\t\t\t transition->transition_manual_clamp, t);\n\t\t}\n\t}\n\n\tif (trylock_textures(transition) == 0) {\n\t\tgs_texrender_reset(transition->transition_texrender[0]);\n\t\tgs_texrender_reset(transition->transition_texrender[1]);\n\t\tunlock_textures(transition);\n\t}\n}\n\nstatic void set_source(obs_source_t *transition, enum obs_transition_target target, obs_source_t *new_child,\n\t\t       bool (*callback)(obs_source_t *t, size_t idx, obs_source_t *c))\n{\n\tsize_t idx = (size_t)target;\n\tobs_source_t *old_child;\n\tbool add_success = true;\n\tbool already_active;\n\n\tif (new_child)\n\t\tnew_child = obs_source_get_ref(new_child);\n\n\tlock_transition(transition);\n\n\told_child = transition->transition_sources[idx];\n\n\tif (new_child == old_child) {\n\t\tunlock_transition(transition);\n\t\tobs_source_release(new_child);\n\t\treturn;\n\t}\n\n\talready_active = transition->transition_source_active[idx];\n\n\tif (already_active) {\n\t\tif (new_child)\n\t\t\tadd_success = obs_source_add_active_child(transition, new_child);\n\t\tif (old_child && add_success)\n\t\t\tobs_source_remove_active_child(transition, old_child);\n\t}\n\n\tif (callback && add_success)\n\t\tadd_success = callback(transition, idx, new_child);\n\n\ttransition->transition_sources[idx] = add_success ? new_child : NULL;\n\n\tunlock_transition(transition);\n\n\tif (add_success) {\n\t\tif (transition->transition_cx == 0 || transition->transition_cy == 0) {\n\t\t\trecalculate_transition_size(transition);\n\t\t\trecalculate_transition_matrices(transition);\n\t\t}\n\t} else {\n\t\tobs_source_release(new_child);\n\t}\n\n\tobs_source_release(old_child);\n}\n\nobs_source_t *obs_transition_get_source(obs_source_t *transition, enum obs_transition_target target)\n{\n\tsize_t idx = (size_t)target;\n\tobs_source_t *ret;\n\n\tif (!transition_valid(transition, \"obs_transition_get_source\"))\n\t\treturn NULL;\n\n\tlock_transition(transition);\n\tret = transition->transition_sources[idx];\n\tret = obs_source_get_ref(ret);\n\tunlock_transition(transition);\n\n\treturn ret;\n}\n\nobs_source_t *obs_transition_get_active_source(obs_source_t *transition)\n{\n\tobs_source_t *ret;\n\n\tif (!transition_valid(transition, \"obs_transition_get_source\"))\n\t\treturn NULL;\n\n\tlock_transition(transition);\n\tif (transition->transitioning_audio || transition->transitioning_video)\n\t\tret = transition->transition_sources[1];\n\telse\n\t\tret = transition->transition_sources[0];\n\tret = obs_source_get_ref(ret);\n\tunlock_transition(transition);\n\n\treturn ret;\n}\n\nstatic bool activate_transition(obs_source_t *transition, size_t idx, obs_source_t *child)\n{\n\tif (!transition->transition_source_active[idx]) {\n\t\tif (!obs_source_add_active_child(transition, child))\n\t\t\treturn false;\n\n\t\ttransition->transition_source_active[idx] = true;\n\t}\n\n\ttransition->transitioning_video = true;\n\ttransition->transitioning_audio = true;\n\treturn true;\n}\n\nstatic inline bool transition_active(obs_source_t *transition)\n{\n\treturn transition->transitioning_audio || transition->transitioning_video;\n}\n\nbool obs_transition_is_active(obs_source_t *transition)\n{\n\treturn transition_active(transition);\n}\n\nbool obs_transition_start(obs_source_t *transition, enum obs_transition_mode mode, uint32_t duration_ms,\n\t\t\t  obs_source_t *dest)\n{\n\tbool active;\n\tbool same_as_source;\n\tbool same_as_dest;\n\tbool same_mode;\n\n\tif (!transition_valid(transition, \"obs_transition_start\"))\n\t\treturn false;\n\n\tif (transition_active(transition)) {\n\t\tobs_transition_set(transition, transition->transition_sources[1]);\n\t}\n\n\tlock_transition(transition);\n\tsame_as_source = dest == transition->transition_sources[0];\n\tsame_as_dest = dest == transition->transition_sources[1];\n\tsame_mode = mode == transition->transition_mode;\n\tactive = transition_active(transition);\n\tunlock_transition(transition);\n\n\tif (same_as_source && !active)\n\t\treturn false;\n\tif (active && mode == OBS_TRANSITION_MODE_MANUAL && same_mode && same_as_dest)\n\t\treturn true;\n\n\tlock_transition(transition);\n\ttransition->transition_mode = mode;\n\ttransition->transition_manual_val = 0.0f;\n\ttransition->transition_manual_target = 0.0f;\n\tunlock_transition(transition);\n\n\tif (transition->info.transition_start)\n\t\ttransition->info.transition_start(transition->context.data);\n\n\tif (transition->transition_use_fixed_duration)\n\t\tduration_ms = transition->transition_fixed_duration;\n\n\tif (!active || (!same_as_dest && !same_as_source)) {\n\t\ttransition->transition_start_time = os_gettime_ns();\n\t\ttransition->transition_duration = (uint64_t)duration_ms * 1000000ULL;\n\t}\n\n\tset_source(transition, OBS_TRANSITION_SOURCE_B, dest, activate_transition);\n\tif (dest == NULL && same_as_dest && !same_as_source) {\n\t\ttransition->transitioning_video = true;\n\t\ttransition->transitioning_audio = true;\n\t}\n\n\tobs_source_dosignal(transition, \"source_transition_start\", \"transition_start\");\n\n\trecalculate_transition_size(transition);\n\trecalculate_transition_matrices(transition);\n\n\treturn true;\n}\n\nvoid obs_transition_set_manual_torque(obs_source_t *transition, float torque, float clamp)\n{\n\tlock_transition(transition);\n\ttransition->transition_manual_torque = torque;\n\ttransition->transition_manual_clamp = clamp;\n\tunlock_transition(transition);\n}\n\nvoid obs_transition_set_manual_time(obs_source_t *transition, float t)\n{\n\tenum obs_transition_mode mode;\n\n\tlock_transition(transition);\n\ttransition->transition_manual_target = t;\n\tmode = transition->transition_mode;\n\tunlock_transition(transition);\n\n\tif (mode == OBS_TRANSITION_MODE_MANUAL && t == 0.0f) {\n\t\tobs_transition_set(transition, transition->transition_sources[0]);\n\t}\n}\n\nvoid obs_transition_set(obs_source_t *transition, obs_source_t *source)\n{\n\tobs_source_t *s[2];\n\tbool active[2];\n\n\tif (!transition_valid(transition, \"obs_transition_set\"))\n\t\treturn;\n\n\tsource = obs_source_get_ref(source);\n\n\tif (transition_active(transition)) {\n\t\tobs_source_dosignal(transition, \"source_transition_stop\", \"transition_stop\");\n\t}\n\n\tlock_transition(transition);\n\tfor (size_t i = 0; i < 2; i++) {\n\t\ts[i] = transition->transition_sources[i];\n\t\tactive[i] = transition->transition_source_active[i];\n\t\ttransition->transition_sources[i] = NULL;\n\t\ttransition->transition_source_active[i] = false;\n\t}\n\ttransition->transition_source_active[0] = true;\n\ttransition->transition_sources[0] = source;\n\ttransition->transitioning_video = false;\n\ttransition->transitioning_audio = false;\n\ttransition->transition_manual_val = 0.0f;\n\ttransition->transition_manual_target = 0.0f;\n\tunlock_transition(transition);\n\n\tfor (size_t i = 0; i < 2; i++) {\n\t\tif (s[i] && active[i])\n\t\t\tobs_source_remove_active_child(transition, s[i]);\n\t\tobs_source_release(s[i]);\n\t}\n\n\tif (source)\n\t\tobs_source_add_active_child(transition, source);\n}\n\nstatic float calc_time(obs_source_t *transition, uint64_t ts)\n{\n\tif (transition->transition_mode == OBS_TRANSITION_MODE_MANUAL)\n\t\treturn transition->transition_manual_val;\n\n\tuint64_t end;\n\n\tif (ts <= transition->transition_start_time)\n\t\treturn 0.0f;\n\n\tend = transition->transition_duration;\n\tts -= transition->transition_start_time;\n\tif (ts >= end || end == 0)\n\t\treturn 1.0f;\n\n\treturn (float)((long double)ts / (long double)end);\n}\n\nstatic inline float get_video_time(obs_source_t *transition)\n{\n\tuint64_t ts = obs->video.video_time;\n\treturn calc_time(transition, ts);\n}\n\nfloat obs_transition_get_time(obs_source_t *transition)\n{\n\treturn get_video_time(transition);\n}\n\nstatic inline gs_texture_t *get_texture(obs_source_t *transition, enum obs_transition_target target)\n{\n\tsize_t idx = (size_t)target;\n\treturn gs_texrender_get_texture(transition->transition_texrender[idx]);\n}\n\nvoid obs_transition_set_scale_type(obs_source_t *transition, enum obs_transition_scale_type type)\n{\n\tif (!transition_valid(transition, \"obs_transition_set_scale_type\"))\n\t\treturn;\n\n\ttransition->transition_scale_type = type;\n}\n\nenum obs_transition_scale_type obs_transition_get_scale_type(const obs_source_t *transition)\n{\n\treturn transition_valid(transition, \"obs_transition_get_scale_type\") ? transition->transition_scale_type\n\t\t\t\t\t\t\t\t\t     : OBS_TRANSITION_SCALE_MAX_ONLY;\n}\n\nvoid obs_transition_set_alignment(obs_source_t *transition, uint32_t alignment)\n{\n\tif (!transition_valid(transition, \"obs_transition_set_alignment\"))\n\t\treturn;\n\n\ttransition->transition_alignment = alignment;\n}\n\nuint32_t obs_transition_get_alignment(const obs_source_t *transition)\n{\n\treturn transition_valid(transition, \"obs_transition_get_alignment\") ? transition->transition_alignment : 0;\n}\n\nvoid obs_transition_set_size(obs_source_t *transition, uint32_t cx, uint32_t cy)\n{\n\tif (!transition_valid(transition, \"obs_transition_set_size\"))\n\t\treturn;\n\n\ttransition->transition_cx = cx;\n\ttransition->transition_cy = cy;\n}\n\nvoid obs_transition_get_size(const obs_source_t *transition, uint32_t *cx, uint32_t *cy)\n{\n\tif (!transition_valid(transition, \"obs_transition_set_size\")) {\n\t\t*cx = 0;\n\t\t*cy = 0;\n\t\treturn;\n\t}\n\n\t*cx = transition->transition_cx;\n\t*cy = transition->transition_cy;\n}\n\nvoid obs_transition_save(obs_source_t *tr, obs_data_t *data)\n{\n\tobs_source_t *child;\n\n\tlock_transition(tr);\n\tchild = transition_active(tr) ? tr->transition_sources[1] : tr->transition_sources[0];\n\n\tobs_data_set_string(data, \"transition_source_a\", child ? child->context.name : \"\");\n\tobs_data_set_int(data, \"transition_alignment\", tr->transition_alignment);\n\tobs_data_set_int(data, \"transition_mode\", (int64_t)tr->transition_mode);\n\tobs_data_set_int(data, \"transition_scale_type\", (int64_t)tr->transition_scale_type);\n\tobs_data_set_int(data, \"transition_cx\", tr->transition_cx);\n\tobs_data_set_int(data, \"transition_cy\", tr->transition_cy);\n\tunlock_transition(tr);\n}\n\nvoid obs_transition_load(obs_source_t *tr, obs_data_t *data)\n{\n\tconst char *name = obs_data_get_string(data, \"transition_source_a\");\n\tint64_t alignment = obs_data_get_int(data, \"transition_alignment\");\n\tint64_t mode = obs_data_get_int(data, \"transition_mode\");\n\tint64_t scale_type = obs_data_get_int(data, \"transition_scale_type\");\n\tint64_t cx = obs_data_get_int(data, \"transition_cx\");\n\tint64_t cy = obs_data_get_int(data, \"transition_cy\");\n\tobs_source_t *source = NULL;\n\n\tif (name) {\n\t\tsource = obs_get_source_by_name(name);\n\t\tif (source) {\n\t\t\tif (!obs_source_add_active_child(tr, source)) {\n\t\t\t\tblog(LOG_WARNING,\n\t\t\t\t     \"Cannot set transition '%s' \"\n\t\t\t\t     \"to source '%s' due to \"\n\t\t\t\t     \"infinite recursion\",\n\t\t\t\t     tr->context.name, name);\n\t\t\t\tobs_source_release(source);\n\t\t\t\tsource = NULL;\n\t\t\t}\n\t\t} else {\n\t\t\tblog(LOG_WARNING,\n\t\t\t     \"Failed to find source '%s' for \"\n\t\t\t     \"transition '%s'\",\n\t\t\t     name, tr->context.name);\n\t\t}\n\t}\n\n\tlock_transition(tr);\n\ttr->transition_sources[0] = source;\n\ttr->transition_source_active[0] = true;\n\ttr->transition_alignment = (uint32_t)alignment;\n\ttr->transition_mode = (enum obs_transition_mode)mode;\n\ttr->transition_scale_type = (enum obs_transition_scale_type)scale_type;\n\ttr->transition_cx = (uint32_t)cx;\n\ttr->transition_cy = (uint32_t)cy;\n\tunlock_transition(tr);\n\n\trecalculate_transition_size(tr);\n\trecalculate_transition_matrices(tr);\n}\n\nstruct transition_state {\n\tobs_source_t *s[2];\n\tbool transitioning_video;\n\tbool transitioning_audio;\n};\n\nstatic inline void copy_transition_state(obs_source_t *transition, struct transition_state *state)\n{\n\tstate->s[0] = obs_source_get_ref(transition->transition_sources[0]);\n\tstate->s[1] = obs_source_get_ref(transition->transition_sources[1]);\n\n\tstate->transitioning_video = transition->transitioning_video;\n\tstate->transitioning_audio = transition->transitioning_audio;\n}\n\nvoid obs_transition_enum_sources(obs_source_t *transition, obs_source_enum_proc_t cb, void *param)\n{\n\tlock_transition(transition);\n\tfor (size_t i = 0; i < 2; i++) {\n\t\tif (transition->transition_sources[i])\n\t\t\tcb(transition, transition->transition_sources[i], param);\n\t}\n\tunlock_transition(transition);\n}\n\nstatic inline void render_child(obs_source_t *transition, obs_source_t *child, size_t idx, enum gs_color_space space)\n{\n\tuint32_t cx = get_cx(transition);\n\tuint32_t cy = get_cy(transition);\n\tstruct vec4 blank;\n\tif (!child)\n\t\treturn;\n\n\tenum gs_color_format format = gs_get_format_from_space(space);\n\tif (gs_texrender_get_format(transition->transition_texrender[idx]) != format) {\n\t\tgs_texrender_destroy(transition->transition_texrender[idx]);\n\t\ttransition->transition_texrender[idx] = gs_texrender_create(format, GS_ZS_NONE);\n\t}\n\n\tif (gs_texrender_begin_with_color_space(transition->transition_texrender[idx], cx, cy, space)) {\n\t\tvec4_zero(&blank);\n\t\tgs_clear(GS_CLEAR_COLOR, &blank, 0.0f, 0);\n\t\tgs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);\n\n\t\tgs_matrix_push();\n\t\tgs_matrix_mul(&transition->transition_matrices[idx]);\n\t\tobs_source_video_render(child);\n\t\tgs_matrix_pop();\n\n\t\tgs_texrender_end(transition->transition_texrender[idx]);\n\t}\n}\n\nstatic void obs_transition_stop(obs_source_t *transition)\n{\n\tobs_source_t *old_child = transition->transition_sources[0];\n\n\tif (old_child && transition->transition_source_active[0])\n\t\tobs_source_remove_active_child(transition, old_child);\n\tobs_source_release(old_child);\n\n\ttransition->transition_source_active[0] = true;\n\ttransition->transition_source_active[1] = false;\n\ttransition->transition_sources[0] = transition->transition_sources[1];\n\ttransition->transition_sources[1] = NULL;\n}\n\nstatic inline void handle_stop(obs_source_t *transition)\n{\n\tif (transition->info.transition_stop)\n\t\ttransition->info.transition_stop(transition->context.data);\n\tobs_source_dosignal(transition, \"source_transition_stop\", \"transition_stop\");\n}\n\nvoid obs_transition_force_stop(obs_source_t *transition)\n{\n\thandle_stop(transition);\n}\n\nvoid obs_transition_video_render(obs_source_t *transition, obs_transition_video_render_callback_t callback)\n{\n\tobs_transition_video_render2(transition, callback, obs->video.transparent_texture);\n}\n\nvoid obs_transition_video_render2(obs_source_t *transition, obs_transition_video_render_callback_t callback,\n\t\t\t\t  gs_texture_t *placeholder_texture)\n{\n\tstruct transition_state state;\n\tstruct matrix4 matrices[2];\n\tbool locked = false;\n\tbool stopped = false;\n\tbool video_stopped = false;\n\tfloat t;\n\n\tif (!transition_valid(transition, \"obs_transition_video_render\"))\n\t\treturn;\n\n\tt = get_video_time(transition);\n\n\tlock_transition(transition);\n\n\tif (t >= 1.0f && transition->transitioning_video) {\n\t\ttransition->transitioning_video = false;\n\t\tvideo_stopped = true;\n\n\t\tif (!transition->transitioning_audio) {\n\t\t\tobs_transition_stop(transition);\n\t\t\tstopped = true;\n\t\t}\n\t}\n\tcopy_transition_state(transition, &state);\n\tmatrices[0] = transition->transition_matrices[0];\n\tmatrices[1] = transition->transition_matrices[1];\n\n\tunlock_transition(transition);\n\n\tif (state.transitioning_video)\n\t\tlocked = trylock_textures(transition) == 0;\n\n\tif (state.transitioning_video && locked && callback) {\n\t\tgs_texture_t *tex[2];\n\t\tuint32_t cx;\n\t\tuint32_t cy;\n\n\t\tconst enum gs_color_space current_space = gs_get_color_space();\n\t\tconst enum gs_color_space source_space = obs_source_get_color_space(transition, 1, &current_space);\n\t\tfor (size_t i = 0; i < 2; i++) {\n\t\t\tif (state.s[i]) {\n\t\t\t\trender_child(transition, state.s[i], i, source_space);\n\t\t\t\ttex[i] = get_texture(transition, i);\n\t\t\t\tif (!tex[i])\n\t\t\t\t\ttex[i] = placeholder_texture;\n\t\t\t} else {\n\t\t\t\ttex[i] = placeholder_texture;\n\t\t\t}\n\t\t}\n\n\t\tcx = get_cx(transition);\n\t\tcy = get_cy(transition);\n\t\tif (cx && cy) {\n\t\t\tgs_blend_state_push();\n\t\t\tgs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);\n\n\t\t\tcallback(transition->context.data, tex[0], tex[1], t, cx, cy);\n\n\t\t\tgs_blend_state_pop();\n\t\t}\n\n\t} else if (state.transitioning_audio) {\n\t\tif (state.s[1]) {\n\t\t\tgs_matrix_push();\n\t\t\tgs_matrix_mul(&matrices[1]);\n\t\t\tobs_source_video_render(state.s[1]);\n\t\t\tgs_matrix_pop();\n\t\t}\n\t} else {\n\t\tif (state.s[0]) {\n\t\t\tgs_matrix_push();\n\t\t\tgs_matrix_mul(&matrices[0]);\n\t\t\tobs_source_video_render(state.s[0]);\n\t\t\tgs_matrix_pop();\n\t\t}\n\t}\n\n\tif (locked)\n\t\tunlock_textures(transition);\n\n\tobs_source_release(state.s[0]);\n\tobs_source_release(state.s[1]);\n\n\tif (video_stopped)\n\t\tobs_source_dosignal(transition, \"source_transition_video_stop\", \"transition_video_stop\");\n\tif (stopped)\n\t\thandle_stop(transition);\n}\n\nstatic enum gs_color_space mix_spaces(enum gs_color_space a, enum gs_color_space b)\n{\n\tif ((a == GS_CS_709_EXTENDED) || (a == GS_CS_709_SCRGB) || (b == GS_CS_709_EXTENDED) || (b == GS_CS_709_SCRGB))\n\t\treturn GS_CS_709_EXTENDED;\n\tif ((a == GS_CS_SRGB_16F) || (b == GS_CS_SRGB_16F))\n\t\treturn GS_CS_SRGB_16F;\n\treturn GS_CS_SRGB;\n}\n\nenum gs_color_space obs_transition_video_get_color_space(obs_source_t *transition)\n{\n\tobs_source_t *source0 = transition->transition_sources[0];\n\tobs_source_t *source1 = transition->transition_sources[1];\n\n\tconst enum gs_color_space preferred_spaces[] = {\n\t\tGS_CS_SRGB,\n\t\tGS_CS_SRGB_16F,\n\t\tGS_CS_709_EXTENDED,\n\t};\n\n\tenum gs_color_space space = GS_CS_SRGB;\n\n\tif (source0) {\n\t\tspace = mix_spaces(space, obs_source_get_color_space(source0, OBS_COUNTOF(preferred_spaces),\n\t\t\t\t\t\t\t\t     preferred_spaces));\n\t}\n\n\tif (source1) {\n\t\tspace = mix_spaces(space, obs_source_get_color_space(source1, OBS_COUNTOF(preferred_spaces),\n\t\t\t\t\t\t\t\t     preferred_spaces));\n\t}\n\n\treturn space;\n}\n\nbool obs_transition_video_render_direct(obs_source_t *transition, enum obs_transition_target target)\n{\n\tstruct transition_state state;\n\tstruct matrix4 matrices[2];\n\tbool stopped = false;\n\tbool video_stopped = false;\n\tbool render_b = target == OBS_TRANSITION_SOURCE_B;\n\tbool transitioning;\n\tfloat t;\n\n\tif (!transition_valid(transition, \"obs_transition_video_render\"))\n\t\treturn false;\n\n\tt = get_video_time(transition);\n\n\tlock_transition(transition);\n\n\tif (t >= 1.0f && transition->transitioning_video) {\n\t\ttransition->transitioning_video = false;\n\t\tvideo_stopped = true;\n\n\t\tif (!obs_source_active(transition))\n\t\t\ttransition->transitioning_audio = false;\n\n\t\tif (!transition->transitioning_audio) {\n\t\t\tobs_transition_stop(transition);\n\t\t\tstopped = true;\n\t\t}\n\t}\n\tcopy_transition_state(transition, &state);\n\ttransitioning = state.transitioning_audio || state.transitioning_video;\n\tmatrices[0] = transition->transition_matrices[0];\n\tmatrices[1] = transition->transition_matrices[1];\n\n\tunlock_transition(transition);\n\n\tint idx = (transitioning && render_b) ? 1 : 0;\n\tif (state.s[idx]) {\n\t\tgs_matrix_push();\n\t\tgs_matrix_mul(&matrices[idx]);\n\t\tobs_source_video_render(state.s[idx]);\n\t\tgs_matrix_pop();\n\t}\n\n\tobs_source_release(state.s[0]);\n\tobs_source_release(state.s[1]);\n\n\tif (video_stopped)\n\t\tobs_source_dosignal(transition, \"source_transition_video_stop\", \"transition_video_stop\");\n\tif (stopped)\n\t\thandle_stop(transition);\n\n\treturn transitioning;\n}\n\nstatic inline float get_sample_time(obs_source_t *transition, size_t sample_rate, size_t sample, uint64_t ts)\n{\n\tuint64_t sample_ts_offset = util_mul_div64(sample, 1000000000ULL, sample_rate);\n\tuint64_t i_ts = ts + sample_ts_offset;\n\treturn calc_time(transition, i_ts);\n}\n\nstatic inline void mix_child(obs_source_t *transition, float *out, float *in, size_t count, size_t sample_rate,\n\t\t\t     uint64_t ts, obs_transition_audio_mix_callback_t mix)\n{\n\tvoid *context_data = transition->context.data;\n\n\tfor (size_t i = 0; i < count; i++) {\n\t\tfloat t = get_sample_time(transition, sample_rate, i, ts);\n\t\tout[i] += in[i] * mix(context_data, t);\n\t}\n}\n\nstatic void process_audio(obs_source_t *transition, obs_source_t *child, struct obs_source_audio_mix *audio,\n\t\t\t  uint64_t min_ts, uint32_t mixers, size_t channels, size_t sample_rate,\n\t\t\t  obs_transition_audio_mix_callback_t mix)\n{\n\tbool valid = child && !child->audio_pending && child->audio_ts && !child->audio_is_duplicated;\n\tstruct obs_source_audio_mix child_audio;\n\tuint64_t ts;\n\tsize_t pos;\n\n\tif (!valid)\n\t\treturn;\n\n\tts = child->audio_ts;\n\tobs_source_get_audio_mix(child, &child_audio);\n\tpos = (size_t)ns_to_audio_frames(sample_rate, ts - min_ts);\n\n\tif (pos > AUDIO_OUTPUT_FRAMES)\n\t\treturn;\n\n\tfor (size_t mix_idx = 0; mix_idx < MAX_AUDIO_MIXES; mix_idx++) {\n\t\tstruct audio_output_data *output = &audio->output[mix_idx];\n\t\tstruct audio_output_data *input = &child_audio.output[mix_idx];\n\n\t\tif ((mixers & (1 << mix_idx)) == 0)\n\t\t\tcontinue;\n\n\t\tfor (size_t ch = 0; ch < channels; ch++) {\n\t\t\tfloat *out = output->data[ch];\n\t\t\tfloat *in = input->data[ch];\n\n\t\t\tmix_child(transition, out + pos, in, AUDIO_OUTPUT_FRAMES - pos, sample_rate, ts, mix);\n\t\t}\n\t}\n}\n\nstatic inline uint64_t calc_min_ts(obs_source_t *sources[2])\n{\n\tuint64_t min_ts = 0;\n\n\tfor (size_t i = 0; i < 2; i++) {\n\t\tif (sources[i] && !sources[i]->audio_pending && sources[i]->audio_ts) {\n\t\t\tif (!min_ts || sources[i]->audio_ts < min_ts)\n\t\t\t\tmin_ts = sources[i]->audio_ts;\n\t\t}\n\t}\n\n\treturn min_ts;\n}\n\nstatic inline bool stop_audio(obs_source_t *transition)\n{\n\ttransition->transitioning_audio = false;\n\tif (!transition->transitioning_video) {\n\t\tobs_transition_stop(transition);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool obs_transition_audio_render(obs_source_t *transition, uint64_t *ts_out, struct obs_source_audio_mix *audio,\n\t\t\t\t uint32_t mixers, size_t channels, size_t sample_rate,\n\t\t\t\t obs_transition_audio_mix_callback_t mix_a, obs_transition_audio_mix_callback_t mix_b)\n{\n\tobs_source_t *sources[2];\n\tstruct transition_state state = {0};\n\tbool stopped = false;\n\tuint64_t min_ts;\n\tfloat t;\n\n\tif (!transition_valid(transition, \"obs_transition_audio_render\"))\n\t\treturn false;\n\n\tlock_transition(transition);\n\n\tsources[0] = transition->transition_sources[0];\n\tsources[1] = transition->transition_sources[1];\n\n\tif (sources[0] && obs_source_removed(sources[0]))\n\t\tsources[0] = NULL;\n\n\tif (sources[1] && obs_source_removed(sources[1]))\n\t\tsources[1] = NULL;\n\n\tmin_ts = calc_min_ts(sources);\n\n\tif (min_ts) {\n\t\tt = calc_time(transition, min_ts);\n\n\t\tif (t >= 1.0f && transition->transitioning_audio)\n\t\t\tstopped = stop_audio(transition);\n\n\t\tsources[0] = transition->transition_sources[0];\n\t\tsources[1] = transition->transition_sources[1];\n\t\tmin_ts = calc_min_ts(sources);\n\t\tif (min_ts)\n\t\t\tcopy_transition_state(transition, &state);\n\n\t} else if (!transition->transitioning_video && transition->transitioning_audio) {\n\t\tstopped = stop_audio(transition);\n\t}\n\n\tunlock_transition(transition);\n\n\tif (min_ts) {\n\t\tif (state.transitioning_audio) {\n\t\t\tif (state.s[0])\n\t\t\t\tprocess_audio(transition, state.s[0], audio, min_ts, mixers, channels, sample_rate,\n\t\t\t\t\t      mix_a);\n\t\t\tif (state.s[1])\n\t\t\t\tprocess_audio(transition, state.s[1], audio, min_ts, mixers, channels, sample_rate,\n\t\t\t\t\t      mix_b);\n\t\t} else if (state.s[0]) {\n\t\t\tmemcpy(audio->output[0].data[0], state.s[0]->audio_output_buf[0][0], TOTAL_AUDIO_SIZE);\n\t\t}\n\n\t\tobs_source_release(state.s[0]);\n\t\tobs_source_release(state.s[1]);\n\t}\n\n\tif (stopped)\n\t\thandle_stop(transition);\n\n\t*ts_out = min_ts;\n\treturn !!min_ts;\n}\n\nvoid obs_transition_enable_fixed(obs_source_t *transition, bool enable, uint32_t duration)\n{\n\tif (!transition_valid(transition, \"obs_transition_enable_fixed\"))\n\t\treturn;\n\n\ttransition->transition_use_fixed_duration = enable;\n\ttransition->transition_fixed_duration = duration;\n}\n\nbool obs_transition_fixed(obs_source_t *transition)\n{\n\treturn transition_valid(transition, \"obs_transition_fixed\") ? transition->transition_use_fixed_duration : false;\n}\n\nstatic inline obs_source_t *copy_source_state(obs_source_t *tr_dest, obs_source_t *tr_source, size_t idx)\n{\n\tobs_source_t *old_child = tr_dest->transition_sources[idx];\n\tobs_source_t *new_child = obs_source_get_ref(tr_source->transition_sources[idx]);\n\tbool active = tr_source->transition_source_active[idx];\n\n\tif (old_child && tr_dest->transition_source_active[idx])\n\t\tobs_source_remove_active_child(tr_dest, old_child);\n\n\ttr_dest->transition_sources[idx] = new_child;\n\ttr_dest->transition_source_active[idx] = active;\n\n\tif (active && new_child)\n\t\tobs_source_add_active_child(tr_dest, new_child);\n\n\treturn old_child;\n}\n\nvoid obs_transition_swap_begin(obs_source_t *tr_dest, obs_source_t *tr_source)\n{\n\tobs_source_t *old_children[2];\n\n\tif (tr_dest == tr_source)\n\t\treturn;\n\n\tlock_textures(tr_source);\n\tlock_textures(tr_dest);\n\n\tlock_transition(tr_source);\n\tlock_transition(tr_dest);\n\n\tfor (size_t i = 0; i < 2; i++)\n\t\told_children[i] = copy_source_state(tr_dest, tr_source, i);\n\n\tunlock_transition(tr_dest);\n\tunlock_transition(tr_source);\n\n\tfor (size_t i = 0; i < 2; i++)\n\t\tobs_source_release(old_children[i]);\n}\n\nvoid obs_transition_swap_end(obs_source_t *tr_dest, obs_source_t *tr_source)\n{\n\tif (tr_dest == tr_source)\n\t\treturn;\n\n\tobs_transition_clear(tr_source);\n\n\tfor (size_t i = 0; i < 2; i++) {\n\t\tgs_texrender_t *dest = tr_dest->transition_texrender[i];\n\t\tgs_texrender_t *source = tr_source->transition_texrender[i];\n\n\t\ttr_dest->transition_texrender[i] = source;\n\t\ttr_source->transition_texrender[i] = dest;\n\t}\n\n\tunlock_textures(tr_dest);\n\tunlock_textures(tr_source);\n}\n"
  },
  {
    "path": "libobs/obs-source.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <inttypes.h>\n#include <math.h>\n\n#include \"media-io/format-conversion.h\"\n#include \"media-io/video-frame.h\"\n#include \"media-io/audio-io.h\"\n#include \"util/threading.h\"\n#include \"util/platform.h\"\n#include \"util/util_uint64.h\"\n#include \"callback/calldata.h\"\n#include \"graphics/matrix3.h\"\n#include \"graphics/vec3.h\"\n\n#include \"obs.h\"\n#include \"obs-internal.h\"\n\n#define get_weak(source) ((obs_weak_source_t *)source->context.control)\n\nstatic bool filter_compatible(obs_source_t *source, obs_source_t *filter);\n\nstatic inline bool data_valid(const struct obs_source *source, const char *f)\n{\n\treturn obs_source_valid(source, f) && source->context.data;\n}\n\nstatic inline bool deinterlacing_enabled(const struct obs_source *source)\n{\n\treturn source->deinterlace_mode != OBS_DEINTERLACE_MODE_DISABLE;\n}\n\nstatic inline bool destroying(const struct obs_source *source)\n{\n\treturn os_atomic_load_long(&source->destroying);\n}\n\nstruct obs_source_info *get_source_info(const char *id)\n{\n\tfor (size_t i = 0; i < obs->source_types.num; i++) {\n\t\tstruct obs_source_info *info = &obs->source_types.array[i];\n\t\tif (strcmp(info->id, id) == 0)\n\t\t\treturn info;\n\t}\n\n\treturn NULL;\n}\n\nstruct obs_source_info *get_source_info2(const char *unversioned_id, uint32_t ver)\n{\n\tfor (size_t i = 0; i < obs->source_types.num; i++) {\n\t\tstruct obs_source_info *info = &obs->source_types.array[i];\n\t\tif (strcmp(info->unversioned_id, unversioned_id) == 0 && info->version == ver)\n\t\t\treturn info;\n\t}\n\n\treturn NULL;\n}\n\nstatic const char *source_signals[] = {\n\t\"void destroy(ptr source)\",\n\t\"void remove(ptr source)\",\n\t\"void update(ptr source)\",\n\t\"void save(ptr source)\",\n\t\"void load(ptr source)\",\n\t\"void activate(ptr source)\",\n\t\"void deactivate(ptr source)\",\n\t\"void show(ptr source)\",\n\t\"void hide(ptr source)\",\n\t\"void mute(ptr source, bool muted)\",\n\t\"void push_to_mute_changed(ptr source, bool enabled)\",\n\t\"void push_to_mute_delay(ptr source, int delay)\",\n\t\"void push_to_talk_changed(ptr source, bool enabled)\",\n\t\"void push_to_talk_delay(ptr source, int delay)\",\n\t\"void enable(ptr source, bool enabled)\",\n\t\"void rename(ptr source, string new_name, string prev_name)\",\n\t\"void volume(ptr source, in out float volume)\",\n\t\"void update_properties(ptr source)\",\n\t\"void update_flags(ptr source, int flags)\",\n\t\"void audio_sync(ptr source, int out int offset)\",\n\t\"void audio_balance(ptr source, in out float balance)\",\n\t\"void audio_mixers(ptr source, in out int mixers)\",\n\t\"void audio_monitoring(ptr source, int type)\",\n\t\"void audio_activate(ptr source)\",\n\t\"void audio_deactivate(ptr source)\",\n\t\"void filter_add(ptr source, ptr filter)\",\n\t\"void filter_remove(ptr source, ptr filter)\",\n\t\"void reorder_filters(ptr source)\",\n\t\"void transition_start(ptr source)\",\n\t\"void transition_video_stop(ptr source)\",\n\t\"void transition_stop(ptr source)\",\n\t\"void media_play(ptr source)\",\n\t\"void media_pause(ptr source)\",\n\t\"void media_restart(ptr source)\",\n\t\"void media_stopped(ptr source)\",\n\t\"void media_next(ptr source)\",\n\t\"void media_previous(ptr source)\",\n\t\"void media_started(ptr source)\",\n\t\"void media_ended(ptr source)\",\n\tNULL,\n};\n\nbool obs_source_init_context(struct obs_source *source, obs_data_t *settings, const char *name, const char *uuid,\n\t\t\t     obs_data_t *hotkey_data, bool private)\n{\n\tif (!obs_context_data_init(&source->context, OBS_OBJ_TYPE_SOURCE, settings, name, uuid, hotkey_data, private))\n\t\treturn false;\n\n\treturn signal_handler_add_array(source->context.signals, source_signals);\n}\n\nconst char *obs_source_get_display_name(const char *id)\n{\n\tconst struct obs_source_info *info = get_source_info(id);\n\treturn (info != NULL) ? info->get_name(info->type_data) : NULL;\n}\n\nobs_module_t *obs_source_get_module(const char *id)\n{\n\tobs_module_t *module = obs->first_module;\n\twhile (module) {\n\t\tfor (size_t i = 0; i < module->sources.num; i++) {\n\t\t\tif (strcmp(module->sources.array[i], id) == 0) {\n\t\t\t\treturn module;\n\t\t\t}\n\t\t}\n\t\tmodule = module->next;\n\t}\n\n\tmodule = obs->first_disabled_module;\n\twhile (module) {\n\t\tfor (size_t i = 0; i < module->sources.num; i++) {\n\t\t\tif (strcmp(module->sources.array[i], id) == 0) {\n\t\t\t\treturn module;\n\t\t\t}\n\t\t}\n\t\tmodule = module->next;\n\t}\n\n\treturn NULL;\n}\n\nenum obs_module_load_state obs_source_load_state(const char *id)\n{\n\tif (!id)\n\t\treturn OBS_MODULE_INVALID;\n\n\tif (obs_source_type_is_scene(id) || obs_source_type_is_group(id))\n\t\treturn OBS_MODULE_ENABLED;\n\n\tobs_module_t *module = obs_source_get_module(id);\n\tif (!module) {\n\t\treturn OBS_MODULE_MISSING;\n\t}\n\treturn module->load_state;\n}\n\nstatic void allocate_audio_output_buffer(struct obs_source *source)\n{\n\tsize_t size = sizeof(float) * AUDIO_OUTPUT_FRAMES * MAX_AUDIO_CHANNELS * MAX_AUDIO_MIXES;\n\tfloat *ptr = bzalloc(size);\n\n\tfor (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {\n\t\tsize_t mix_pos = mix * AUDIO_OUTPUT_FRAMES * MAX_AUDIO_CHANNELS;\n\n\t\tfor (size_t i = 0; i < MAX_AUDIO_CHANNELS; i++) {\n\t\t\tsource->audio_output_buf[mix][i] = ptr + mix_pos + AUDIO_OUTPUT_FRAMES * i;\n\t\t}\n\t}\n}\n\nstatic void allocate_audio_mix_buffer(struct obs_source *source)\n{\n\tsize_t size = sizeof(float) * AUDIO_OUTPUT_FRAMES * MAX_AUDIO_CHANNELS;\n\tfloat *ptr = bzalloc(size);\n\n\tfor (size_t i = 0; i < MAX_AUDIO_CHANNELS; i++) {\n\t\tsource->audio_mix_buf[i] = ptr + AUDIO_OUTPUT_FRAMES * i;\n\t}\n}\n\nstatic inline bool is_audio_source(const struct obs_source *source)\n{\n\treturn source->info.output_flags & OBS_SOURCE_AUDIO;\n}\n\nstatic inline bool is_composite_source(const struct obs_source *source)\n{\n\treturn source->info.output_flags & OBS_SOURCE_COMPOSITE;\n}\n\nstatic inline bool requires_canvas(const struct obs_source *source)\n{\n\treturn source->info.output_flags & OBS_SOURCE_REQUIRES_CANVAS;\n}\n\nextern char *find_libobs_data_file(const char *file);\n\n/* internal initialization */\nstatic bool obs_source_init(struct obs_source *source)\n{\n\tsource->user_volume = 1.0f;\n\tsource->volume = 1.0f;\n\tsource->sync_offset = 0;\n\tsource->balance = 0.5f;\n\tsource->audio_active = true;\n\tpthread_mutex_init_value(&source->filter_mutex);\n\tpthread_mutex_init_value(&source->async_mutex);\n\tpthread_mutex_init_value(&source->audio_mutex);\n\tpthread_mutex_init_value(&source->audio_buf_mutex);\n\tpthread_mutex_init_value(&source->audio_cb_mutex);\n\tpthread_mutex_init_value(&source->caption_cb_mutex);\n\tpthread_mutex_init_value(&source->media_actions_mutex);\n\n\tif (pthread_mutex_init_recursive(&source->filter_mutex) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&source->audio_buf_mutex, NULL) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&source->audio_actions_mutex, NULL) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&source->audio_cb_mutex, NULL) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&source->audio_mutex, NULL) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init_recursive(&source->async_mutex) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&source->caption_cb_mutex, NULL) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&source->media_actions_mutex, NULL) != 0)\n\t\treturn false;\n\n\tif (is_audio_source(source) || is_composite_source(source))\n\t\tallocate_audio_output_buffer(source);\n\tif (source->info.audio_mix)\n\t\tallocate_audio_mix_buffer(source);\n\n\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION) {\n\t\tif (!obs_transition_init(source))\n\t\t\treturn false;\n\t}\n\n\tobs_context_init_control(&source->context, source, (obs_destroy_cb)obs_source_destroy);\n\n\tsource->deinterlace_top_first = true;\n\tsource->audio_mixers = 0xFF;\n\n\tsource->private_settings = obs_data_create();\n\treturn true;\n}\n\nstatic void obs_source_init_finalize(struct obs_source *source, obs_canvas_t *canvas)\n{\n\tif (is_audio_source(source)) {\n\t\tpthread_mutex_lock(&obs->data.audio_sources_mutex);\n\n\t\tsource->next_audio_source = obs->data.first_audio_source;\n\t\tsource->prev_next_audio_source = &obs->data.first_audio_source;\n\t\tif (obs->data.first_audio_source)\n\t\t\tobs->data.first_audio_source->prev_next_audio_source = &source->next_audio_source;\n\t\tobs->data.first_audio_source = source;\n\n\t\tpthread_mutex_unlock(&obs->data.audio_sources_mutex);\n\t}\n\n\tif (!source->context.private) {\n\t\tif (requires_canvas(source)) {\n\t\t\tobs_canvas_insert_source(canvas, source);\n\t\t} else {\n\t\t\tobs_context_data_insert_name(&source->context, &obs->data.sources_mutex,\n\t\t\t\t\t\t     &obs->data.public_sources);\n\t\t}\n\t}\n\tobs_context_data_insert_uuid(&source->context, &obs->data.sources_mutex, &obs->data.sources);\n}\n\nstatic bool obs_source_hotkey_mute(void *data, obs_hotkey_pair_id id, obs_hotkey_t *key, bool pressed)\n{\n\tUNUSED_PARAMETER(id);\n\tUNUSED_PARAMETER(key);\n\n\tstruct obs_source *source = data;\n\n\tif (!pressed || obs_source_muted(source))\n\t\treturn false;\n\n\tobs_source_set_muted(source, true);\n\treturn true;\n}\n\nstatic bool obs_source_hotkey_unmute(void *data, obs_hotkey_pair_id id, obs_hotkey_t *key, bool pressed)\n{\n\tUNUSED_PARAMETER(id);\n\tUNUSED_PARAMETER(key);\n\n\tstruct obs_source *source = data;\n\n\tif (!pressed || !obs_source_muted(source))\n\t\treturn false;\n\n\tobs_source_set_muted(source, false);\n\treturn true;\n}\n\nstatic void obs_source_hotkey_push_to_mute(void *data, obs_hotkey_id id, obs_hotkey_t *key, bool pressed)\n{\n\tstruct audio_action action = {.timestamp = os_gettime_ns(), .type = AUDIO_ACTION_PTM, .set = pressed};\n\n\tUNUSED_PARAMETER(id);\n\tUNUSED_PARAMETER(key);\n\n\tstruct obs_source *source = data;\n\n\tpthread_mutex_lock(&source->audio_actions_mutex);\n\tda_push_back(source->audio_actions, &action);\n\tpthread_mutex_unlock(&source->audio_actions_mutex);\n\n\tsource->user_push_to_mute_pressed = pressed;\n}\n\nstatic void obs_source_hotkey_push_to_talk(void *data, obs_hotkey_id id, obs_hotkey_t *key, bool pressed)\n{\n\tstruct audio_action action = {.timestamp = os_gettime_ns(), .type = AUDIO_ACTION_PTT, .set = pressed};\n\n\tUNUSED_PARAMETER(id);\n\tUNUSED_PARAMETER(key);\n\n\tstruct obs_source *source = data;\n\n\tpthread_mutex_lock(&source->audio_actions_mutex);\n\tda_push_back(source->audio_actions, &action);\n\tpthread_mutex_unlock(&source->audio_actions_mutex);\n\n\tsource->user_push_to_talk_pressed = pressed;\n}\n\nstatic void obs_source_init_audio_hotkeys(struct obs_source *source)\n{\n\tif (!(source->info.output_flags & OBS_SOURCE_AUDIO) || source->info.type != OBS_SOURCE_TYPE_INPUT) {\n\t\tsource->mute_unmute_key = OBS_INVALID_HOTKEY_ID;\n\t\tsource->push_to_talk_key = OBS_INVALID_HOTKEY_ID;\n\t\treturn;\n\t}\n\n\tsource->mute_unmute_key = obs_hotkey_pair_register_source(source, \"libobs.mute\", obs->hotkeys.mute,\n\t\t\t\t\t\t\t\t  \"libobs.unmute\", obs->hotkeys.unmute,\n\t\t\t\t\t\t\t\t  obs_source_hotkey_mute, obs_source_hotkey_unmute,\n\t\t\t\t\t\t\t\t  source, source);\n\n\tsource->push_to_mute_key = obs_hotkey_register_source(source, \"libobs.push-to-mute\", obs->hotkeys.push_to_mute,\n\t\t\t\t\t\t\t      obs_source_hotkey_push_to_mute, source);\n\n\tsource->push_to_talk_key = obs_hotkey_register_source(source, \"libobs.push-to-talk\", obs->hotkeys.push_to_talk,\n\t\t\t\t\t\t\t      obs_source_hotkey_push_to_talk, source);\n}\n\nvoid obs_source_audio_output_capture_device_activated(void *vptr, calldata_t *cd)\n{\n\tUNUSED_PARAMETER(vptr);\n\tobs_source_t *src = calldata_ptr(cd, \"source\");\n\tif (!src)\n\t\treturn;\n\n\tobs_data_t *settings = obs_source_get_settings(src);\n\tconst char *device_id = obs_data_get_string(settings, \"device_id\");\n\tobs_source_audio_output_capture_device_changed(src, device_id);\n\tobs_data_release(settings);\n}\n\nextern bool devices_match(const char *id1, const char *id2);\nvoid obs_source_audio_output_capture_device_changed(obs_source_t *src, const char *device_id)\n{\n\tstruct obs_core_audio *audio = &obs->audio;\n\n\tif (!audio->monitoring_device_name)\n\t\treturn;\n\n\tif (!(src->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR))\n\t\treturn;\n\n\tconst char *mon_id = audio->monitoring_device_id;\n\tbool id_match = false;\n\n#ifdef __APPLE__\n\textern void get_desktop_default_id(char **p_id);\n\tif (device_id && strcmp(device_id, \"default\") == 0) {\n\t\tchar *def_id = NULL;\n\t\tget_desktop_default_id(&def_id);\n\t\tid_match = devices_match(def_id, mon_id);\n\t\tif (def_id)\n\t\t\tbfree(def_id);\n\t} else {\n\t\tid_match = devices_match(device_id, mon_id);\n\t}\n#else\n\tid_match = devices_match(device_id, mon_id);\n#endif\n\tstruct calldata cd;\n\tuint8_t stack[128];\n\tcalldata_init_fixed(&cd, stack, sizeof(stack));\n\n\tif (id_match) {\n\t\tcalldata_set_ptr(&cd, \"source\", src);\n\t\tsignal_handler_signal(obs->signals, \"deduplication_changed\", &cd);\n\t\tsignal_handler_connect(src->context.signals, \"activate\",\n\t\t\t\t       obs_source_audio_output_capture_device_activated, NULL);\n\t\tblog(LOG_INFO,\n\t\t     \"Device for 'Audio Output Capture' source %s is also used for audio monitoring.\"\n\t\t     \"\\nDeduplication logic is being applied to all monitored sources.\",\n\t\t     src->context.name);\n\t} else {\n\t\tif (src == audio->monitoring_duplicating_source) {\n\t\t\tcalldata_set_ptr(&cd, \"source\", NULL);\n\t\t\tsignal_handler_disconnect(src->context.signals, \"activate\",\n\t\t\t\t\t\t  obs_source_audio_output_capture_device_activated, NULL);\n\t\t\tsignal_handler_signal(obs->signals, \"deduplication_changed\", &cd);\n\t\t\tblog(LOG_INFO, \"Deduplication logic stopped.\");\n\t\t}\n\t}\n}\n\nstatic obs_source_t *obs_source_create_internal(const char *id, const char *name, const char *uuid,\n\t\t\t\t\t\tobs_data_t *settings, obs_data_t *hotkey_data, bool private,\n\t\t\t\t\t\tuint32_t last_obs_ver, obs_canvas_t *canvas)\n{\n\tstruct obs_source *source = bzalloc(sizeof(struct obs_source));\n\n\tconst struct obs_source_info *info = get_source_info(id);\n\tif (!info) {\n\t\tblog(LOG_ERROR, \"Source ID '%s' not found\", id);\n\n\t\tsource->info.id = bstrdup(id);\n\t\tsource->owns_info_id = true;\n\t\tsource->info.unversioned_id = bstrdup(source->info.id);\n\t} else {\n\t\tsource->info = *info;\n\n\t\t/* Always mark filters as private so they aren't found by\n\t\t * source enum/search functions.\n\t\t *\n\t\t * XXX: Fix design flaws with filters */\n\t\tif (info->type == OBS_SOURCE_TYPE_FILTER)\n\t\tprivate = true;\n\t}\n\n\tsource->mute_unmute_key = OBS_INVALID_HOTKEY_PAIR_ID;\n\tsource->push_to_mute_key = OBS_INVALID_HOTKEY_ID;\n\tsource->push_to_talk_key = OBS_INVALID_HOTKEY_ID;\n\tsource->last_obs_ver = last_obs_ver;\n\n\tif (!obs_source_init_context(source, settings, name, uuid, hotkey_data, private))\n\t\tgoto fail;\n\n\tif (info) {\n\t\tif (info->get_defaults) {\n\t\t\tinfo->get_defaults(source->context.settings);\n\t\t}\n\t\tif (info->get_defaults2) {\n\t\t\tinfo->get_defaults2(info->type_data, source->context.settings);\n\t\t}\n\t}\n\n\tif (!obs_source_init(source))\n\t\tgoto fail;\n\n\t/* Scenes need canvases, fall back to using default canvas if none provided here. */\n\tif (requires_canvas(source) && !canvas) {\n\t\tblog(LOG_WARNING, \"Attempted to add Scene without specifying a canvas! Using default canvas instead.\");\n\t\tcanvas = obs->data.main_canvas;\n\t}\n\n\tif (!private)\n\t\tobs_source_init_audio_hotkeys(source);\n\n\t/* allow the source to be created even if creation fails so that the\n\t * user's data doesn't become lost */\n\tif (info && info->create)\n\t\tsource->context.data = info->create(source->context.settings, source);\n\tif ((!info || info->create) && !source->context.data)\n\t\tblog(LOG_ERROR, \"Failed to create source '%s'!\", name);\n\n\tblog(LOG_DEBUG, \"%ssource '%s' (%s) created\", private ? \"private \" : \"\", name, id);\n\n\tsource->flags = source->default_flags;\n\tsource->enabled = true;\n\n\t/* audio deduplication initialization */\n\tsource->audio_is_duplicated = false;\n\n\tobs_source_init_finalize(source, canvas);\n\tif (!private) {\n\t\tif (canvas)\n\t\t\tobs_source_dosignal_canvas(source, canvas, \"source_create_canvas\", NULL);\n\t\tif (!canvas || canvas == obs->data.main_canvas)\n\t\t\tobs_source_dosignal(source, \"source_create\", NULL);\n\t}\n\n\treturn source;\n\nfail:\n\tblog(LOG_ERROR, \"obs_source_create failed\");\n\tobs_source_destroy(source);\n\treturn NULL;\n}\n\nobs_source_t *obs_source_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data)\n{\n\treturn obs_source_create_internal(id, name, NULL, settings, hotkey_data, false, LIBOBS_API_VER, NULL);\n}\n\nobs_source_t *obs_source_create_private(const char *id, const char *name, obs_data_t *settings)\n{\n\treturn obs_source_create_internal(id, name, NULL, settings, NULL, true, LIBOBS_API_VER, NULL);\n}\n\nobs_source_t *obs_source_create_canvas(obs_canvas_t *canvas, const char *id, const char *name, obs_data_t *settings,\n\t\t\t\t       obs_data_t *hotkey_data)\n{\n\treturn obs_source_create_internal(id, name, NULL, settings, hotkey_data, false, LIBOBS_API_VER, canvas);\n}\n\nobs_source_t *obs_source_create_set_last_ver(obs_canvas_t *canvas, const char *id, const char *name, const char *uuid,\n\t\t\t\t\t     obs_data_t *settings, obs_data_t *hotkey_data, uint32_t last_obs_ver,\n\t\t\t\t\t     bool is_private)\n{\n\treturn obs_source_create_internal(id, name, uuid, settings, hotkey_data, is_private, last_obs_ver, canvas);\n}\n\nstatic char *get_new_filter_name(obs_source_t *dst, const char *name)\n{\n\tstruct dstr new_name = {0};\n\tint inc = 0;\n\n\tdstr_copy(&new_name, name);\n\n\tfor (;;) {\n\t\tobs_source_t *existing_filter = obs_source_get_filter_by_name(dst, new_name.array);\n\t\tif (!existing_filter)\n\t\t\tbreak;\n\n\t\tobs_source_release(existing_filter);\n\n\t\tdstr_printf(&new_name, \"%s %d\", name, ++inc + 1);\n\t}\n\n\treturn new_name.array;\n}\n\nstatic void duplicate_filters(obs_source_t *dst, obs_source_t *src, bool private)\n{\n\tDARRAY(obs_source_t *) filters;\n\n\tda_init(filters);\n\n\tpthread_mutex_lock(&src->filter_mutex);\n\tda_reserve(filters, src->filters.num);\n\tfor (size_t i = 0; i < src->filters.num; i++) {\n\t\tobs_source_t *s = obs_source_get_ref(src->filters.array[i]);\n\t\tif (s)\n\t\t\tda_push_back(filters, &s);\n\t}\n\tpthread_mutex_unlock(&src->filter_mutex);\n\n\tfor (size_t i = filters.num; i > 0; i--) {\n\t\tobs_source_t *src_filter = filters.array[i - 1];\n\t\tchar *new_name = get_new_filter_name(dst, src_filter->context.name);\n\t\tbool enabled = obs_source_enabled(src_filter);\n\n\t\tobs_source_t *dst_filter = obs_source_duplicate(src_filter, new_name, private);\n\t\tobs_source_set_enabled(dst_filter, enabled);\n\n\t\tbfree(new_name);\n\t\tobs_source_filter_add(dst, dst_filter);\n\t\tobs_source_release(dst_filter);\n\t\tobs_source_release(src_filter);\n\t}\n\n\tda_free(filters);\n}\n\nvoid obs_source_copy_filters(obs_source_t *dst, obs_source_t *src)\n{\n\tif (!obs_source_valid(dst, \"obs_source_copy_filters\"))\n\t\treturn;\n\tif (!obs_source_valid(src, \"obs_source_copy_filters\"))\n\t\treturn;\n\n\tduplicate_filters(dst, src, dst->context.private);\n}\n\nstatic void duplicate_filter(obs_source_t *dst, obs_source_t *filter)\n{\n\tif (!filter_compatible(dst, filter))\n\t\treturn;\n\n\tchar *new_name = get_new_filter_name(dst, filter->context.name);\n\tbool enabled = obs_source_enabled(filter);\n\n\tobs_source_t *dst_filter = obs_source_duplicate(filter, new_name, true);\n\tobs_source_set_enabled(dst_filter, enabled);\n\n\tbfree(new_name);\n\tobs_source_filter_add(dst, dst_filter);\n\tobs_source_release(dst_filter);\n}\n\nvoid obs_source_copy_single_filter(obs_source_t *dst, obs_source_t *filter)\n{\n\tif (!obs_source_valid(dst, \"obs_source_copy_single_filter\"))\n\t\treturn;\n\tif (!obs_source_valid(filter, \"obs_source_copy_single_filter\"))\n\t\treturn;\n\n\tduplicate_filter(dst, filter);\n}\n\nobs_source_t *obs_source_duplicate(obs_source_t *source, const char *new_name, bool create_private)\n{\n\tobs_source_t *new_source;\n\tobs_data_t *settings;\n\n\tif (!obs_source_valid(source, \"obs_source_duplicate\"))\n\t\treturn NULL;\n\n\tif (source->info.type == OBS_SOURCE_TYPE_SCENE) {\n\t\tobs_scene_t *scene = obs_scene_from_source(source);\n\t\tif (scene && !create_private) {\n\t\t\treturn obs_source_get_ref(source);\n\t\t}\n\t\tif (!scene)\n\t\t\tscene = obs_group_from_source(source);\n\t\tif (!scene)\n\t\t\treturn NULL;\n\n\t\tobs_scene_t *new_scene = obs_scene_duplicate(\n\t\t\tscene, new_name, create_private ? OBS_SCENE_DUP_PRIVATE_COPY : OBS_SCENE_DUP_COPY);\n\t\tobs_source_t *new_source = obs_scene_get_source(new_scene);\n\t\treturn new_source;\n\t}\n\n\tif ((source->info.output_flags & OBS_SOURCE_DO_NOT_DUPLICATE) != 0) {\n\t\treturn obs_source_get_ref(source);\n\t}\n\n\tsettings = obs_data_create();\n\tobs_data_apply(settings, source->context.settings);\n\n\tnew_source = create_private ? obs_source_create_private(source->info.id, new_name, settings)\n\t\t\t\t    : obs_source_create(source->info.id, new_name, settings, NULL);\n\n\tnew_source->audio_mixers = source->audio_mixers;\n\tnew_source->sync_offset = source->sync_offset;\n\tnew_source->user_volume = source->user_volume;\n\tnew_source->user_muted = source->user_muted;\n\tnew_source->volume = source->volume;\n\tnew_source->muted = source->muted;\n\tnew_source->flags = source->flags;\n\n\tobs_data_apply(new_source->private_settings, source->private_settings);\n\n\tif (source->info.type != OBS_SOURCE_TYPE_FILTER)\n\t\tduplicate_filters(new_source, source, create_private);\n\n\tobs_data_release(settings);\n\treturn new_source;\n}\n\nvoid obs_source_frame_init(struct obs_source_frame *frame, enum video_format format, uint32_t width, uint32_t height)\n{\n\tstruct video_frame vid_frame;\n\n\tif (!obs_ptr_valid(frame, \"obs_source_frame_init\"))\n\t\treturn;\n\n\tvideo_frame_init(&vid_frame, format, width, height);\n\tframe->format = format;\n\tframe->width = width;\n\tframe->height = height;\n\n\tfor (size_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\tframe->data[i] = vid_frame.data[i];\n\t\tframe->linesize[i] = vid_frame.linesize[i];\n\t}\n}\n\nstatic inline void obs_source_frame_decref(struct obs_source_frame *frame)\n{\n\tif (os_atomic_dec_long(&frame->refs) == 0)\n\t\tobs_source_frame_destroy(frame);\n}\n\nstatic bool obs_source_filter_remove_refless(obs_source_t *source, obs_source_t *filter);\nstatic void obs_source_destroy_defer(struct obs_source *source);\n\nvoid obs_source_destroy(struct obs_source *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_destroy\"))\n\t\treturn;\n\n\tif (os_atomic_set_long(&source->destroying, true) == true) {\n\t\tblog(LOG_ERROR, \"Double destroy just occurred. \"\n\t\t\t\t\"Something called addref on a source \"\n\t\t\t\t\"after it was already fully released, \"\n\t\t\t\t\"I guess.\");\n\t\treturn;\n\t}\n\n\tif (is_audio_source(source)) {\n\t\tpthread_mutex_lock(&source->audio_cb_mutex);\n\t\tda_free(source->audio_cb_list);\n\t\tpthread_mutex_unlock(&source->audio_cb_mutex);\n\t}\n\n\tpthread_mutex_lock(&source->caption_cb_mutex);\n\tda_free(source->caption_cb_list);\n\tpthread_mutex_unlock(&source->caption_cb_mutex);\n\n\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION)\n\t\tobs_transition_clear(source);\n\n\tpthread_mutex_lock(&obs->data.audio_sources_mutex);\n\tif (source->prev_next_audio_source) {\n\t\t*source->prev_next_audio_source = source->next_audio_source;\n\t\tif (source->next_audio_source)\n\t\t\tsource->next_audio_source->prev_next_audio_source = source->prev_next_audio_source;\n\t}\n\tpthread_mutex_unlock(&obs->data.audio_sources_mutex);\n\n\tif (source->filter_parent)\n\t\tobs_source_filter_remove_refless(source->filter_parent, source);\n\n\twhile (source->filters.num)\n\t\tobs_source_filter_remove(source, source->filters.array[0]);\n\n\tobs_context_data_remove_uuid(&source->context, &obs->data.sources_mutex, &obs->data.sources);\n\tif (!source->context.private) {\n\t\tif (requires_canvas(source)) {\n\t\t\tobs_canvas_remove_source(source);\n\t\t} else {\n\t\t\tobs_context_data_remove_name(&source->context, &obs->data.sources_mutex,\n\t\t\t\t\t\t     &obs->data.public_sources);\n\t\t}\n\t}\n\n\tsource_profiler_remove_source(source);\n\n\t/* defer source destroy */\n\tos_task_queue_queue_task(obs->destruction_task_thread, (os_task_t)obs_source_destroy_defer, source);\n}\n\nstatic void obs_source_destroy_defer(struct obs_source *source)\n{\n\tsize_t i;\n\n\t/* prevents the destruction of sources if destroy triggered inside of\n\t * a video tick call */\n\tobs_context_wait(&source->context);\n\n\tobs_source_dosignal(source, \"source_destroy\", \"destroy\");\n\n\tif (source->context.data) {\n\t\tsource->info.destroy(source->context.data);\n\t\tsource->context.data = NULL;\n\t}\n\n\tblog(LOG_DEBUG, \"%ssource '%s' destroyed\", source->context.private ? \"private \" : \"\", source->context.name);\n\n\taudio_monitor_destroy(source->monitor);\n\n\tobs_hotkey_unregister(source->push_to_talk_key);\n\tobs_hotkey_unregister(source->push_to_mute_key);\n\tobs_hotkey_pair_unregister(source->mute_unmute_key);\n\n\tfor (i = 0; i < source->async_cache.num; i++)\n\t\tobs_source_frame_decref(source->async_cache.array[i].frame);\n\n\tgs_enter_context(obs->video.graphics);\n\tif (source->async_texrender)\n\t\tgs_texrender_destroy(source->async_texrender);\n\tif (source->async_prev_texrender)\n\t\tgs_texrender_destroy(source->async_prev_texrender);\n\tfor (size_t c = 0; c < MAX_AV_PLANES; c++) {\n\t\tgs_texture_destroy(source->async_textures[c]);\n\t\tgs_texture_destroy(source->async_prev_textures[c]);\n\t}\n\tif (source->filter_texrender)\n\t\tgs_texrender_destroy(source->filter_texrender);\n\tif (source->color_space_texrender)\n\t\tgs_texrender_destroy(source->color_space_texrender);\n\tgs_leave_context();\n\n\tfor (i = 0; i < MAX_AV_PLANES; i++)\n\t\tbfree(source->audio_data.data[i]);\n\tfor (i = 0; i < MAX_AUDIO_CHANNELS; i++)\n\t\tdeque_free(&source->audio_input_buf[i]);\n\taudio_resampler_destroy(source->resampler);\n\tbfree(source->audio_output_buf[0][0]);\n\tbfree(source->audio_mix_buf[0]);\n\n\tobs_source_frame_destroy(source->async_preload_frame);\n\n\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION)\n\t\tobs_transition_free(source);\n\n\tda_free(source->audio_actions);\n\tda_free(source->audio_cb_list);\n\tda_free(source->caption_cb_list);\n\tda_free(source->async_cache);\n\tda_free(source->async_frames);\n\tda_free(source->filters);\n\tda_free(source->media_actions);\n\tpthread_mutex_destroy(&source->filter_mutex);\n\tpthread_mutex_destroy(&source->audio_actions_mutex);\n\tpthread_mutex_destroy(&source->audio_buf_mutex);\n\tpthread_mutex_destroy(&source->audio_cb_mutex);\n\tpthread_mutex_destroy(&source->audio_mutex);\n\tpthread_mutex_destroy(&source->caption_cb_mutex);\n\tpthread_mutex_destroy(&source->async_mutex);\n\tpthread_mutex_destroy(&source->media_actions_mutex);\n\tobs_data_release(source->private_settings);\n\tobs_context_data_free(&source->context);\n\n\tif (source->owns_info_id) {\n\t\tbfree((void *)source->info.id);\n\t\tbfree((void *)source->info.unversioned_id);\n\t}\n\n\tbfree(source);\n}\n\nvoid obs_source_addref(obs_source_t *source)\n{\n\tif (!source)\n\t\treturn;\n\n\tobs_ref_addref(&source->context.control->ref);\n}\n\nvoid obs_source_release(obs_source_t *source)\n{\n\tif (!obs && source) {\n\t\tblog(LOG_WARNING, \"Tried to release a source when the OBS \"\n\t\t\t\t  \"core is shut down!\");\n\t\treturn;\n\t}\n\n\tif (!source)\n\t\treturn;\n\n\tobs_weak_source_t *control = get_weak(source);\n\tif (obs_ref_release(&control->ref)) {\n\t\tobs_source_destroy(source);\n\t\tobs_weak_source_release(control);\n\t}\n}\n\nvoid obs_weak_source_addref(obs_weak_source_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tobs_weak_ref_addref(&weak->ref);\n}\n\nvoid obs_weak_source_release(obs_weak_source_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tif (obs_weak_ref_release(&weak->ref))\n\t\tbfree(weak);\n}\n\nobs_source_t *obs_source_get_ref(obs_source_t *source)\n{\n\tif (!source)\n\t\treturn NULL;\n\n\treturn obs_weak_source_get_source(get_weak(source));\n}\n\nobs_weak_source_t *obs_source_get_weak_source(obs_source_t *source)\n{\n\tif (!source)\n\t\treturn NULL;\n\n\tobs_weak_source_t *weak = get_weak(source);\n\tobs_weak_source_addref(weak);\n\treturn weak;\n}\n\nobs_source_t *obs_weak_source_get_source(obs_weak_source_t *weak)\n{\n\tif (!weak)\n\t\treturn NULL;\n\n\tif (obs_weak_ref_get_ref(&weak->ref))\n\t\treturn weak->source;\n\n\treturn NULL;\n}\n\nbool obs_weak_source_expired(obs_weak_source_t *weak)\n{\n\treturn weak ? obs_weak_ref_expired(&weak->ref) : true;\n}\n\nbool obs_weak_source_references_source(obs_weak_source_t *weak, obs_source_t *source)\n{\n\treturn weak && source && weak->source == source;\n}\n\nvoid obs_source_remove(obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_remove\"))\n\t\treturn;\n\n\tif (!source->removed) {\n\t\tobs_source_t *s = obs_source_get_ref(source);\n\t\tif (s) {\n\t\t\ts->removed = true;\n\t\t\tobs_source_dosignal(s, \"source_remove\", \"remove\");\n\t\t\t/* Remove from canvas if there is one. */\n\t\t\tif (source->canvas)\n\t\t\t\tobs_canvas_remove_source(s);\n\n\t\t\tobs_source_release(s);\n\t\t}\n\t}\n}\n\nbool obs_source_removed(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_removed\") ? source->removed : true;\n}\n\nstatic inline obs_data_t *get_defaults(const struct obs_source_info *info)\n{\n\tobs_data_t *settings = obs_data_create();\n\tif (info->get_defaults2)\n\t\tinfo->get_defaults2(info->type_data, settings);\n\telse if (info->get_defaults)\n\t\tinfo->get_defaults(settings);\n\treturn settings;\n}\n\nobs_data_t *obs_source_settings(const char *id)\n{\n\tconst struct obs_source_info *info = get_source_info(id);\n\treturn (info) ? get_defaults(info) : NULL;\n}\n\nobs_data_t *obs_get_source_defaults(const char *id)\n{\n\tconst struct obs_source_info *info = get_source_info(id);\n\treturn info ? get_defaults(info) : NULL;\n}\n\nobs_properties_t *obs_get_source_properties(const char *id)\n{\n\tconst struct obs_source_info *info = get_source_info(id);\n\tif (info && (info->get_properties || info->get_properties2)) {\n\t\tobs_data_t *defaults = get_defaults(info);\n\t\tobs_properties_t *props;\n\n\t\tif (info->get_properties2)\n\t\t\tprops = info->get_properties2(NULL, info->type_data);\n\t\telse\n\t\t\tprops = info->get_properties(NULL);\n\n\t\tobs_properties_apply_settings(props, defaults);\n\t\tobs_data_release(defaults);\n\t\treturn props;\n\t}\n\treturn NULL;\n}\n\nobs_missing_files_t *obs_source_get_missing_files(const obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_get_missing_files\"))\n\t\treturn obs_missing_files_create();\n\n\tif (source->info.missing_files) {\n\t\treturn source->info.missing_files(source->context.data);\n\t}\n\n\treturn obs_missing_files_create();\n}\n\nvoid obs_source_replace_missing_file(obs_missing_file_cb cb, obs_source_t *source, const char *new_path, void *data)\n{\n\tif (!data_valid(source, \"obs_source_replace_missing_file\"))\n\t\treturn;\n\n\tcb(source->context.data, new_path, data);\n}\n\nbool obs_is_source_configurable(const char *id)\n{\n\tconst struct obs_source_info *info = get_source_info(id);\n\treturn info && (info->get_properties || info->get_properties2);\n}\n\nbool obs_source_configurable(const obs_source_t *source)\n{\n\treturn data_valid(source, \"obs_source_configurable\") &&\n\t       (source->info.get_properties || source->info.get_properties2);\n}\n\nobs_properties_t *obs_source_properties(const obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_properties\"))\n\t\treturn NULL;\n\n\tif (source->info.get_properties2) {\n\t\tobs_properties_t *props;\n\t\tprops = source->info.get_properties2(source->context.data, source->info.type_data);\n\t\tobs_properties_apply_settings(props, source->context.settings);\n\t\treturn props;\n\n\t} else if (source->info.get_properties) {\n\t\tobs_properties_t *props;\n\t\tprops = source->info.get_properties(source->context.data);\n\t\tobs_properties_apply_settings(props, source->context.settings);\n\t\treturn props;\n\t}\n\n\treturn NULL;\n}\n\nuint32_t obs_source_get_output_flags(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_output_flags\") ? source->info.output_flags : 0;\n}\n\nuint32_t obs_get_source_output_flags(const char *id)\n{\n\tconst struct obs_source_info *info = get_source_info(id);\n\treturn info ? info->output_flags : 0;\n}\n\nstatic void obs_source_deferred_update(obs_source_t *source)\n{\n\tif (source->context.data && source->info.update) {\n\t\tlong count = os_atomic_load_long(&source->defer_update_count);\n\t\tsource->info.update(source->context.data, source->context.settings);\n\t\tos_atomic_compare_swap_long(&source->defer_update_count, count, 0);\n\t\tobs_source_dosignal(source, \"source_update\", \"update\");\n\t}\n}\n\nvoid obs_source_update(obs_source_t *source, obs_data_t *settings)\n{\n\tif (!obs_source_valid(source, \"obs_source_update\"))\n\t\treturn;\n\n\tif (settings) {\n\t\tobs_data_apply(source->context.settings, settings);\n\t}\n\n\tif (source->info.output_flags & OBS_SOURCE_VIDEO) {\n\t\tos_atomic_inc_long(&source->defer_update_count);\n\t} else if (source->context.data && source->info.update) {\n\t\tsource->info.update(source->context.data, source->context.settings);\n\t\tobs_source_dosignal(source, \"source_update\", \"update\");\n\t}\n}\n\nvoid obs_source_reset_settings(obs_source_t *source, obs_data_t *settings)\n{\n\tif (!obs_source_valid(source, \"obs_source_reset_settings\"))\n\t\treturn;\n\n\tobs_data_clear(source->context.settings);\n\tobs_source_update(source, settings);\n}\n\nvoid obs_source_update_properties(obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_update_properties\"))\n\t\treturn;\n\n\tobs_source_dosignal(source, NULL, \"update_properties\");\n}\n\nvoid obs_source_send_mouse_click(obs_source_t *source, const struct obs_mouse_event *event, int32_t type, bool mouse_up,\n\t\t\t\t uint32_t click_count)\n{\n\tif (!obs_source_valid(source, \"obs_source_send_mouse_click\"))\n\t\treturn;\n\n\tif (source->info.output_flags & OBS_SOURCE_INTERACTION) {\n\t\tif (source->info.mouse_click) {\n\t\t\tsource->info.mouse_click(source->context.data, event, type, mouse_up, click_count);\n\t\t}\n\t}\n}\n\nvoid obs_source_send_mouse_move(obs_source_t *source, const struct obs_mouse_event *event, bool mouse_leave)\n{\n\tif (!obs_source_valid(source, \"obs_source_send_mouse_move\"))\n\t\treturn;\n\n\tif (source->info.output_flags & OBS_SOURCE_INTERACTION) {\n\t\tif (source->info.mouse_move) {\n\t\t\tsource->info.mouse_move(source->context.data, event, mouse_leave);\n\t\t}\n\t}\n}\n\nvoid obs_source_send_mouse_wheel(obs_source_t *source, const struct obs_mouse_event *event, int x_delta, int y_delta)\n{\n\tif (!obs_source_valid(source, \"obs_source_send_mouse_wheel\"))\n\t\treturn;\n\n\tif (source->info.output_flags & OBS_SOURCE_INTERACTION) {\n\t\tif (source->info.mouse_wheel) {\n\t\t\tsource->info.mouse_wheel(source->context.data, event, x_delta, y_delta);\n\t\t}\n\t}\n}\n\nvoid obs_source_send_focus(obs_source_t *source, bool focus)\n{\n\tif (!obs_source_valid(source, \"obs_source_send_focus\"))\n\t\treturn;\n\n\tif (source->info.output_flags & OBS_SOURCE_INTERACTION) {\n\t\tif (source->info.focus) {\n\t\t\tsource->info.focus(source->context.data, focus);\n\t\t}\n\t}\n}\n\nvoid obs_source_send_key_click(obs_source_t *source, const struct obs_key_event *event, bool key_up)\n{\n\tif (!obs_source_valid(source, \"obs_source_send_key_click\"))\n\t\treturn;\n\n\tif (source->info.output_flags & OBS_SOURCE_INTERACTION) {\n\t\tif (source->info.key_click) {\n\t\t\tsource->info.key_click(source->context.data, event, key_up);\n\t\t}\n\t}\n}\n\nbool obs_source_get_texcoords_centered(obs_source_t *source)\n{\n\treturn source->texcoords_centered;\n}\n\nvoid obs_source_set_texcoords_centered(obs_source_t *source, bool centered)\n{\n\tsource->texcoords_centered = centered;\n}\n\nstatic void activate_source(obs_source_t *source)\n{\n\tif (source->context.data && source->info.activate)\n\t\tsource->info.activate(source->context.data);\n\tobs_source_dosignal(source, \"source_activate\", \"activate\");\n}\n\nstatic void deactivate_source(obs_source_t *source)\n{\n\tif (source->context.data && source->info.deactivate)\n\t\tsource->info.deactivate(source->context.data);\n\tobs_source_dosignal(source, \"source_deactivate\", \"deactivate\");\n}\n\nstatic void show_source(obs_source_t *source)\n{\n\tif (source->context.data && source->info.show)\n\t\tsource->info.show(source->context.data);\n\tobs_source_dosignal(source, \"source_show\", \"show\");\n}\n\nstatic void hide_source(obs_source_t *source)\n{\n\tif (source->context.data && source->info.hide)\n\t\tsource->info.hide(source->context.data);\n\tobs_source_dosignal(source, \"source_hide\", \"hide\");\n}\n\nstatic void activate_tree(obs_source_t *parent, obs_source_t *child, void *param)\n{\n\tos_atomic_inc_long(&child->activate_refs);\n\n\tUNUSED_PARAMETER(parent);\n\tUNUSED_PARAMETER(param);\n}\n\nstatic void deactivate_tree(obs_source_t *parent, obs_source_t *child, void *param)\n{\n\tos_atomic_dec_long(&child->activate_refs);\n\n\tUNUSED_PARAMETER(parent);\n\tUNUSED_PARAMETER(param);\n}\n\nstatic void show_tree(obs_source_t *parent, obs_source_t *child, void *param)\n{\n\tos_atomic_inc_long(&child->show_refs);\n\n\tUNUSED_PARAMETER(parent);\n\tUNUSED_PARAMETER(param);\n}\n\nstatic void hide_tree(obs_source_t *parent, obs_source_t *child, void *param)\n{\n\tos_atomic_dec_long(&child->show_refs);\n\n\tUNUSED_PARAMETER(parent);\n\tUNUSED_PARAMETER(param);\n}\n\nvoid obs_source_activate(obs_source_t *source, enum view_type type)\n{\n\tif (!obs_source_valid(source, \"obs_source_activate\"))\n\t\treturn;\n\n\tos_atomic_inc_long(&source->show_refs);\n\tobs_source_enum_active_tree(source, show_tree, NULL);\n\n\tif (type == MAIN_VIEW) {\n\t\tos_atomic_inc_long(&source->activate_refs);\n\t\tobs_source_enum_active_tree(source, activate_tree, NULL);\n\t}\n}\n\nvoid obs_source_deactivate(obs_source_t *source, enum view_type type)\n{\n\tif (!obs_source_valid(source, \"obs_source_deactivate\"))\n\t\treturn;\n\n\tif (os_atomic_load_long(&source->show_refs) > 0) {\n\t\tos_atomic_dec_long(&source->show_refs);\n\t\tobs_source_enum_active_tree(source, hide_tree, NULL);\n\t}\n\n\tif (type == MAIN_VIEW) {\n\t\tif (os_atomic_load_long(&source->activate_refs) > 0) {\n\t\t\tos_atomic_dec_long(&source->activate_refs);\n\t\t\tobs_source_enum_active_tree(source, deactivate_tree, NULL);\n\t\t}\n\t}\n}\n\nstatic inline struct obs_source_frame *get_closest_frame(obs_source_t *source, uint64_t sys_time);\n\nstatic void filter_frame(obs_source_t *source, struct obs_source_frame **ref_frame)\n{\n\tstruct obs_source_frame *frame = *ref_frame;\n\tif (frame) {\n\t\tos_atomic_inc_long(&frame->refs);\n\t\tframe = filter_async_video(source, frame);\n\t\tif (frame)\n\t\t\tos_atomic_dec_long(&frame->refs);\n\t}\n\n\t*ref_frame = frame;\n}\n\nvoid process_media_actions(obs_source_t *source)\n{\n\tstruct media_action action = {0};\n\n\tfor (;;) {\n\t\tpthread_mutex_lock(&source->media_actions_mutex);\n\t\tif (source->media_actions.num) {\n\t\t\taction = source->media_actions.array[0];\n\t\t\tda_pop_front(source->media_actions);\n\t\t} else {\n\t\t\taction.type = MEDIA_ACTION_NONE;\n\t\t}\n\t\tpthread_mutex_unlock(&source->media_actions_mutex);\n\n\t\tswitch (action.type) {\n\t\tcase MEDIA_ACTION_NONE:\n\t\t\treturn;\n\t\tcase MEDIA_ACTION_PLAY_PAUSE:\n\t\t\tsource->info.media_play_pause(source->context.data, action.pause);\n\n\t\t\tif (action.pause)\n\t\t\t\tobs_source_dosignal(source, NULL, \"media_pause\");\n\t\t\telse\n\t\t\t\tobs_source_dosignal(source, NULL, \"media_play\");\n\t\t\tbreak;\n\n\t\tcase MEDIA_ACTION_RESTART:\n\t\t\tsource->info.media_restart(source->context.data);\n\t\t\tobs_source_dosignal(source, NULL, \"media_restart\");\n\t\t\tbreak;\n\n\t\tcase MEDIA_ACTION_STOP:\n\t\t\tsource->info.media_stop(source->context.data);\n\t\t\tobs_source_dosignal(source, NULL, \"media_stopped\");\n\t\t\tbreak;\n\t\tcase MEDIA_ACTION_NEXT:\n\t\t\tsource->info.media_next(source->context.data);\n\t\t\tobs_source_dosignal(source, NULL, \"media_next\");\n\t\t\tbreak;\n\t\tcase MEDIA_ACTION_PREVIOUS:\n\t\t\tsource->info.media_previous(source->context.data);\n\t\t\tobs_source_dosignal(source, NULL, \"media_previous\");\n\t\t\tbreak;\n\t\tcase MEDIA_ACTION_SET_TIME:\n\t\t\tsource->info.media_set_time(source->context.data, action.ms);\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nstatic void async_tick(obs_source_t *source)\n{\n\tuint64_t sys_time = obs->video.video_time;\n\n\tpthread_mutex_lock(&source->async_mutex);\n\n\tif (deinterlacing_enabled(source)) {\n\t\tdeinterlace_process_last_frame(source, sys_time);\n\t} else {\n\t\tif (source->cur_async_frame) {\n\t\t\tremove_async_frame(source, source->cur_async_frame);\n\t\t\tsource->cur_async_frame = NULL;\n\t\t}\n\n\t\tsource->cur_async_frame = get_closest_frame(source, sys_time);\n\t}\n\n\tsource->last_sys_timestamp = sys_time;\n\n\tif (deinterlacing_enabled(source))\n\t\tfilter_frame(source, &source->prev_async_frame);\n\tfilter_frame(source, &source->cur_async_frame);\n\n\tif (source->cur_async_frame)\n\t\tsource->async_update_texture = set_async_texture_size(source, source->cur_async_frame);\n\n\tpthread_mutex_unlock(&source->async_mutex);\n}\n\nvoid obs_source_video_tick(obs_source_t *source, float seconds)\n{\n\tbool now_showing, now_active;\n\n\tif (!obs_source_valid(source, \"obs_source_video_tick\"))\n\t\treturn;\n\n\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION)\n\t\tobs_transition_tick(source, seconds);\n\n\tif ((source->info.output_flags & OBS_SOURCE_ASYNC) != 0)\n\t\tasync_tick(source);\n\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) != 0)\n\t\tprocess_media_actions(source);\n\n\tif (os_atomic_load_long(&source->defer_update_count) > 0)\n\t\tobs_source_deferred_update(source);\n\n\t/* reset the filter render texture information once every frame */\n\tif (source->filter_texrender)\n\t\tgs_texrender_reset(source->filter_texrender);\n\n\t/* call show/hide if the reference changed */\n\tnow_showing = !!source->show_refs;\n\tif (now_showing != source->showing) {\n\t\tif (now_showing) {\n\t\t\tshow_source(source);\n\t\t} else {\n\t\t\thide_source(source);\n\t\t}\n\n\t\tif (source->filters.num) {\n\t\t\tfor (size_t i = source->filters.num; i > 0; i--) {\n\t\t\t\tobs_source_t *filter = source->filters.array[i - 1];\n\t\t\t\tif (now_showing) {\n\t\t\t\t\tshow_source(filter);\n\t\t\t\t} else {\n\t\t\t\t\thide_source(filter);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tsource->showing = now_showing;\n\t}\n\n\t/* call activate/deactivate if the reference changed */\n\tnow_active = !!source->activate_refs;\n\tif (now_active != source->active) {\n\t\tif (now_active) {\n\t\t\tactivate_source(source);\n\t\t} else {\n\t\t\tdeactivate_source(source);\n\t\t}\n\n\t\tif (source->filters.num) {\n\t\t\tfor (size_t i = source->filters.num; i > 0; i--) {\n\t\t\t\tobs_source_t *filter = source->filters.array[i - 1];\n\t\t\t\tif (now_active) {\n\t\t\t\t\tactivate_source(filter);\n\t\t\t\t} else {\n\t\t\t\t\tdeactivate_source(filter);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tsource->active = now_active;\n\t}\n\n\tif (source->context.data && source->info.video_tick)\n\t\tsource->info.video_tick(source->context.data, seconds);\n\n\tsource->async_rendered = false;\n\tsource->deinterlace_rendered = false;\n}\n\n/* unless the value is 3+ hours worth of frames, this won't overflow */\nstatic inline uint64_t conv_frames_to_time(const size_t sample_rate, const size_t frames)\n{\n\tif (!sample_rate)\n\t\treturn 0;\n\n\treturn util_mul_div64(frames, 1000000000ULL, sample_rate);\n}\n\nstatic inline size_t conv_time_to_frames(const size_t sample_rate, const uint64_t duration)\n{\n\treturn (size_t)util_mul_div64(duration, sample_rate, 1000000000ULL);\n}\n\n/* maximum buffer size */\n#define MAX_BUF_SIZE (1000 * AUDIO_OUTPUT_FRAMES * sizeof(float))\n\n/* time threshold in nanoseconds to ensure audio timing is as seamless as\n * possible */\n#define TS_SMOOTHING_THRESHOLD 70000000ULL\n\nstatic inline void reset_audio_timing(obs_source_t *source, uint64_t timestamp, uint64_t os_time)\n{\n\tsource->timing_set = true;\n\tsource->timing_adjust = os_time - timestamp;\n}\n\nstatic void reset_audio_data(obs_source_t *source, uint64_t os_time)\n{\n\tfor (size_t i = 0; i < MAX_AUDIO_CHANNELS; i++) {\n\t\tif (source->audio_input_buf[i].size)\n\t\t\tdeque_pop_front(&source->audio_input_buf[i], NULL, source->audio_input_buf[i].size);\n\t}\n\n\tsource->last_audio_input_buf_size = 0;\n\tsource->audio_ts = os_time;\n\tsource->next_audio_sys_ts_min = os_time;\n}\n\nstatic void handle_ts_jump(obs_source_t *source, uint64_t expected, uint64_t ts, uint64_t diff, uint64_t os_time)\n{\n\tblog(LOG_DEBUG,\n\t     \"Timestamp for source '%s' jumped by '%\" PRIu64 \"', \"\n\t     \"expected value %\" PRIu64 \", input value %\" PRIu64,\n\t     source->context.name, diff, expected, ts);\n\n\tpthread_mutex_lock(&source->audio_buf_mutex);\n\treset_audio_timing(source, ts, os_time);\n\treset_audio_data(source, os_time);\n\tpthread_mutex_unlock(&source->audio_buf_mutex);\n}\n\nstatic void source_signal_audio_data(obs_source_t *source, const struct audio_data *in, bool muted)\n{\n\tpthread_mutex_lock(&source->audio_cb_mutex);\n\n\tfor (size_t i = source->audio_cb_list.num; i > 0; i--) {\n\t\tstruct audio_cb_info info = source->audio_cb_list.array[i - 1];\n\t\tinfo.callback(info.param, source, in, muted);\n\t}\n\n\tpthread_mutex_unlock(&source->audio_cb_mutex);\n}\n\nstatic inline uint64_t uint64_diff(uint64_t ts1, uint64_t ts2)\n{\n\treturn (ts1 < ts2) ? (ts2 - ts1) : (ts1 - ts2);\n}\n\nstatic inline size_t get_buf_placement(audio_t *audio, uint64_t offset)\n{\n\tuint32_t sample_rate = audio_output_get_sample_rate(audio);\n\treturn (size_t)util_mul_div64(offset, sample_rate, 1000000000ULL);\n}\n\nstatic void source_output_audio_place(obs_source_t *source, const struct audio_data *in)\n{\n\taudio_t *audio = obs->audio.audio;\n\tsize_t buf_placement;\n\tsize_t channels = audio_output_get_channels(audio);\n\tsize_t size = in->frames * sizeof(float);\n\n\tif (!source->audio_ts || in->timestamp < source->audio_ts)\n\t\treset_audio_data(source, in->timestamp);\n\n\tbuf_placement = get_buf_placement(audio, in->timestamp - source->audio_ts) * sizeof(float);\n\n#if DEBUG_AUDIO == 1\n\tblog(LOG_DEBUG, \"frames: %lu, size: %lu, placement: %lu, base_ts: %llu, ts: %llu\", (unsigned long)in->frames,\n\t     (unsigned long)source->audio_input_buf[0].size, (unsigned long)buf_placement, source->audio_ts,\n\t     in->timestamp);\n#endif\n\n\t/* do not allow the circular buffers to become too big */\n\tif ((buf_placement + size) > MAX_BUF_SIZE)\n\t\treturn;\n\n\tfor (size_t i = 0; i < channels; i++) {\n\t\tdeque_place(&source->audio_input_buf[i], buf_placement, in->data[i], size);\n\t\tdeque_pop_back(&source->audio_input_buf[i], NULL,\n\t\t\t       source->audio_input_buf[i].size - (buf_placement + size));\n\t}\n\n\tsource->last_audio_input_buf_size = 0;\n}\n\nstatic inline void source_output_audio_push_back(obs_source_t *source, const struct audio_data *in)\n{\n\taudio_t *audio = obs->audio.audio;\n\tsize_t channels = audio_output_get_channels(audio);\n\tsize_t size = in->frames * sizeof(float);\n\n\t/* do not allow the circular buffers to become too big */\n\tif ((source->audio_input_buf[0].size + size) > MAX_BUF_SIZE)\n\t\treturn;\n\n\tfor (size_t i = 0; i < channels; i++)\n\t\tdeque_push_back(&source->audio_input_buf[i], in->data[i], size);\n\n\t/* reset audio input buffer size to ensure that audio doesn't get\n\t * perpetually cut */\n\tsource->last_audio_input_buf_size = 0;\n}\n\nstatic inline bool source_muted(obs_source_t *source, uint64_t os_time)\n{\n\tif (source->push_to_mute_enabled && source->user_push_to_mute_pressed)\n\t\tsource->push_to_mute_stop_time = os_time + source->push_to_mute_delay * 1000000;\n\n\tif (source->push_to_talk_enabled && source->user_push_to_talk_pressed)\n\t\tsource->push_to_talk_stop_time = os_time + source->push_to_talk_delay * 1000000;\n\n\tbool push_to_mute_active = source->user_push_to_mute_pressed || os_time < source->push_to_mute_stop_time;\n\tbool push_to_talk_active = source->user_push_to_talk_pressed || os_time < source->push_to_talk_stop_time;\n\n\treturn !source->enabled || source->user_muted || (source->push_to_mute_enabled && push_to_mute_active) ||\n\t       (source->push_to_talk_enabled && !push_to_talk_active);\n}\n\nstatic void source_output_audio_data(obs_source_t *source, const struct audio_data *data)\n{\n\tsize_t sample_rate = audio_output_get_sample_rate(obs->audio.audio);\n\tstruct audio_data in = *data;\n\tuint64_t diff;\n\tuint64_t os_time = os_gettime_ns();\n\tint64_t sync_offset;\n\tbool using_direct_ts = false;\n\tbool push_back = false;\n\n\t/* detects 'directly' set timestamps as long as they're within\n\t * a certain threshold */\n\tif (uint64_diff(in.timestamp, os_time) < MAX_TS_VAR) {\n\t\tsource->timing_adjust = 0;\n\t\tsource->timing_set = true;\n\t\tusing_direct_ts = true;\n\t}\n\n\tif (!source->timing_set) {\n\t\treset_audio_timing(source, in.timestamp, os_time);\n\n\t} else if (source->next_audio_ts_min != 0) {\n\t\tdiff = uint64_diff(source->next_audio_ts_min, in.timestamp);\n\n\t\t/* smooth audio if within threshold */\n\t\tif (diff > MAX_TS_VAR && !using_direct_ts)\n\t\t\thandle_ts_jump(source, source->next_audio_ts_min, in.timestamp, diff, os_time);\n\t\telse if (diff < TS_SMOOTHING_THRESHOLD) {\n\t\t\tif (source->async_unbuffered && source->async_decoupled)\n\t\t\t\tsource->timing_adjust = os_time - in.timestamp;\n\t\t\tin.timestamp = source->next_audio_ts_min;\n\t\t} else {\n\t\t\tblog(LOG_DEBUG,\n\t\t\t     \"Audio timestamp for '%s' exceeded TS_SMOOTHING_THRESHOLD, diff=%\" PRIu64\n\t\t\t     \" ns, expected %\" PRIu64 \", input %\" PRIu64,\n\t\t\t     source->context.name, diff, source->next_audio_ts_min, in.timestamp);\n\t\t}\n\t}\n\n\tsource->next_audio_ts_min = in.timestamp + conv_frames_to_time(sample_rate, in.frames);\n\n\tin.timestamp += source->timing_adjust;\n\n\tpthread_mutex_lock(&source->audio_buf_mutex);\n\n\tif (source->next_audio_sys_ts_min == in.timestamp) {\n\t\tpush_back = true;\n\n\t} else if (source->next_audio_sys_ts_min) {\n\t\tdiff = uint64_diff(source->next_audio_sys_ts_min, in.timestamp);\n\n\t\tif (diff < TS_SMOOTHING_THRESHOLD) {\n\t\t\tpush_back = true;\n\n\t\t} else if (diff > MAX_TS_VAR) {\n\t\t\t/* This typically only happens if used with async video when\n\t\t\t * audio/video start transitioning in to a timestamp jump.\n\t\t\t * Audio will typically have a timestamp jump, and then video\n\t\t\t * will have a timestamp jump.  If that case is encountered,\n\t\t\t * just clear the audio data in that small window and force a\n\t\t\t * resync.  This handles all cases rather than just looping. */\n\t\t\treset_audio_timing(source, data->timestamp, os_time);\n\t\t\tin.timestamp = data->timestamp + source->timing_adjust;\n\t\t}\n\t}\n\n\tsync_offset = source->sync_offset;\n\tin.timestamp += sync_offset;\n\tin.timestamp -= source->resample_offset;\n\n\tsource->next_audio_sys_ts_min = source->next_audio_ts_min + source->timing_adjust;\n\n\tif (source->last_sync_offset != sync_offset) {\n\t\tif (source->last_sync_offset)\n\t\t\tpush_back = false;\n\t\tsource->last_sync_offset = sync_offset;\n\t}\n\n\tif (source->monitoring_type != OBS_MONITORING_TYPE_MONITOR_ONLY) {\n\t\tif (push_back && source->audio_ts)\n\t\t\tsource_output_audio_push_back(source, &in);\n\t\telse\n\t\t\tsource_output_audio_place(source, &in);\n\t}\n\n\tpthread_mutex_unlock(&source->audio_buf_mutex);\n\n\tsource_signal_audio_data(source, data, source_muted(source, os_time));\n}\n\nenum convert_type {\n\tCONVERT_NONE,\n\tCONVERT_NV12,\n\tCONVERT_420,\n\tCONVERT_420_PQ,\n\tCONVERT_420_A,\n\tCONVERT_422,\n\tCONVERT_422P10LE,\n\tCONVERT_422_A,\n\tCONVERT_422_PACK,\n\tCONVERT_444,\n\tCONVERT_444P12LE,\n\tCONVERT_444_A,\n\tCONVERT_444P12LE_A,\n\tCONVERT_444_A_PACK,\n\tCONVERT_800,\n\tCONVERT_RGB_LIMITED,\n\tCONVERT_BGR3,\n\tCONVERT_I010,\n\tCONVERT_P010,\n\tCONVERT_V210,\n\tCONVERT_R10L,\n};\n\nstatic inline enum convert_type get_convert_type(enum video_format format, bool full_range, uint8_t trc)\n{\n\tswitch (format) {\n\tcase VIDEO_FORMAT_I420:\n\t\treturn (trc == VIDEO_TRC_PQ) ? CONVERT_420_PQ : CONVERT_420;\n\tcase VIDEO_FORMAT_NV12:\n\t\treturn CONVERT_NV12;\n\tcase VIDEO_FORMAT_I444:\n\t\treturn CONVERT_444;\n\tcase VIDEO_FORMAT_I412:\n\t\treturn CONVERT_444P12LE;\n\tcase VIDEO_FORMAT_I422:\n\t\treturn CONVERT_422;\n\tcase VIDEO_FORMAT_I210:\n\t\treturn CONVERT_422P10LE;\n\n\tcase VIDEO_FORMAT_YVYU:\n\tcase VIDEO_FORMAT_YUY2:\n\tcase VIDEO_FORMAT_UYVY:\n\t\treturn CONVERT_422_PACK;\n\n\tcase VIDEO_FORMAT_Y800:\n\t\treturn CONVERT_800;\n\n\tcase VIDEO_FORMAT_NONE:\n\tcase VIDEO_FORMAT_RGBA:\n\tcase VIDEO_FORMAT_BGRA:\n\tcase VIDEO_FORMAT_BGRX:\n\t\treturn full_range ? CONVERT_NONE : CONVERT_RGB_LIMITED;\n\n\tcase VIDEO_FORMAT_BGR3:\n\t\treturn CONVERT_BGR3;\n\n\tcase VIDEO_FORMAT_I40A:\n\t\treturn CONVERT_420_A;\n\n\tcase VIDEO_FORMAT_I42A:\n\t\treturn CONVERT_422_A;\n\n\tcase VIDEO_FORMAT_YUVA:\n\t\treturn CONVERT_444_A;\n\n\tcase VIDEO_FORMAT_YA2L:\n\t\treturn CONVERT_444P12LE_A;\n\n\tcase VIDEO_FORMAT_AYUV:\n\t\treturn CONVERT_444_A_PACK;\n\n\tcase VIDEO_FORMAT_I010:\n\t\treturn CONVERT_I010;\n\n\tcase VIDEO_FORMAT_P010:\n\t\treturn CONVERT_P010;\n\n\tcase VIDEO_FORMAT_V210:\n\t\treturn CONVERT_V210;\n\n\tcase VIDEO_FORMAT_R10L:\n\t\treturn CONVERT_R10L;\n\n\tcase VIDEO_FORMAT_P216:\n\tcase VIDEO_FORMAT_P416:\n\t\t/* Unimplemented */\n\t\tbreak;\n\t}\n\n\treturn CONVERT_NONE;\n}\n\nstatic inline bool set_packed422_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tconst uint32_t width = frame->width;\n\tconst uint32_t height = frame->height;\n\tconst uint32_t half_width = (width + 1) / 2;\n\tsource->async_convert_width[0] = half_width;\n\tsource->async_convert_height[0] = height;\n\tsource->async_texture_formats[0] = GS_BGRA;\n\tsource->async_channel_count = 1;\n\treturn true;\n}\n\nstatic inline bool set_packed444_alpha_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tsource->async_convert_width[0] = frame->width;\n\tsource->async_convert_height[0] = frame->height;\n\tsource->async_texture_formats[0] = GS_BGRA;\n\tsource->async_channel_count = 1;\n\treturn true;\n}\n\nstatic inline bool set_planar444_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tsource->async_convert_width[0] = frame->width;\n\tsource->async_convert_width[1] = frame->width;\n\tsource->async_convert_width[2] = frame->width;\n\tsource->async_convert_height[0] = frame->height;\n\tsource->async_convert_height[1] = frame->height;\n\tsource->async_convert_height[2] = frame->height;\n\tsource->async_texture_formats[0] = GS_R8;\n\tsource->async_texture_formats[1] = GS_R8;\n\tsource->async_texture_formats[2] = GS_R8;\n\tsource->async_channel_count = 3;\n\treturn true;\n}\n\nstatic inline bool set_planar444_16_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tsource->async_convert_width[0] = frame->width;\n\tsource->async_convert_width[1] = frame->width;\n\tsource->async_convert_width[2] = frame->width;\n\tsource->async_convert_height[0] = frame->height;\n\tsource->async_convert_height[1] = frame->height;\n\tsource->async_convert_height[2] = frame->height;\n\tsource->async_texture_formats[0] = GS_R16;\n\tsource->async_texture_formats[1] = GS_R16;\n\tsource->async_texture_formats[2] = GS_R16;\n\tsource->async_channel_count = 3;\n\treturn true;\n}\n\nstatic inline bool set_planar444_alpha_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tsource->async_convert_width[0] = frame->width;\n\tsource->async_convert_width[1] = frame->width;\n\tsource->async_convert_width[2] = frame->width;\n\tsource->async_convert_width[3] = frame->width;\n\tsource->async_convert_height[0] = frame->height;\n\tsource->async_convert_height[1] = frame->height;\n\tsource->async_convert_height[2] = frame->height;\n\tsource->async_convert_height[3] = frame->height;\n\tsource->async_texture_formats[0] = GS_R8;\n\tsource->async_texture_formats[1] = GS_R8;\n\tsource->async_texture_formats[2] = GS_R8;\n\tsource->async_texture_formats[3] = GS_R8;\n\tsource->async_channel_count = 4;\n\treturn true;\n}\n\nstatic inline bool set_planar444_16_alpha_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tsource->async_convert_width[0] = frame->width;\n\tsource->async_convert_width[1] = frame->width;\n\tsource->async_convert_width[2] = frame->width;\n\tsource->async_convert_width[3] = frame->width;\n\tsource->async_convert_height[0] = frame->height;\n\tsource->async_convert_height[1] = frame->height;\n\tsource->async_convert_height[2] = frame->height;\n\tsource->async_convert_height[3] = frame->height;\n\tsource->async_texture_formats[0] = GS_R16;\n\tsource->async_texture_formats[1] = GS_R16;\n\tsource->async_texture_formats[2] = GS_R16;\n\tsource->async_texture_formats[3] = GS_R16;\n\tsource->async_channel_count = 4;\n\treturn true;\n}\n\nstatic inline bool set_planar420_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tconst uint32_t width = frame->width;\n\tconst uint32_t height = frame->height;\n\tconst uint32_t half_width = (width + 1) / 2;\n\tconst uint32_t half_height = (height + 1) / 2;\n\tsource->async_convert_width[0] = width;\n\tsource->async_convert_width[1] = half_width;\n\tsource->async_convert_width[2] = half_width;\n\tsource->async_convert_height[0] = height;\n\tsource->async_convert_height[1] = half_height;\n\tsource->async_convert_height[2] = half_height;\n\tsource->async_texture_formats[0] = GS_R8;\n\tsource->async_texture_formats[1] = GS_R8;\n\tsource->async_texture_formats[2] = GS_R8;\n\tsource->async_channel_count = 3;\n\treturn true;\n}\n\nstatic inline bool set_planar420_alpha_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tconst uint32_t width = frame->width;\n\tconst uint32_t height = frame->height;\n\tconst uint32_t half_width = (width + 1) / 2;\n\tconst uint32_t half_height = (height + 1) / 2;\n\tsource->async_convert_width[0] = width;\n\tsource->async_convert_width[1] = half_width;\n\tsource->async_convert_width[2] = half_width;\n\tsource->async_convert_width[3] = width;\n\tsource->async_convert_height[0] = height;\n\tsource->async_convert_height[1] = half_height;\n\tsource->async_convert_height[2] = half_height;\n\tsource->async_convert_height[3] = height;\n\tsource->async_texture_formats[0] = GS_R8;\n\tsource->async_texture_formats[1] = GS_R8;\n\tsource->async_texture_formats[2] = GS_R8;\n\tsource->async_texture_formats[3] = GS_R8;\n\tsource->async_channel_count = 4;\n\treturn true;\n}\n\nstatic inline bool set_planar422_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tconst uint32_t width = frame->width;\n\tconst uint32_t height = frame->height;\n\tconst uint32_t half_width = (width + 1) / 2;\n\tsource->async_convert_width[0] = width;\n\tsource->async_convert_width[1] = half_width;\n\tsource->async_convert_width[2] = half_width;\n\tsource->async_convert_height[0] = height;\n\tsource->async_convert_height[1] = height;\n\tsource->async_convert_height[2] = height;\n\tsource->async_texture_formats[0] = GS_R8;\n\tsource->async_texture_formats[1] = GS_R8;\n\tsource->async_texture_formats[2] = GS_R8;\n\tsource->async_channel_count = 3;\n\treturn true;\n}\nstatic inline bool set_planar422_16_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tconst uint32_t width = frame->width;\n\tconst uint32_t height = frame->height;\n\tconst uint32_t half_width = (width + 1) / 2;\n\tsource->async_convert_width[0] = width;\n\tsource->async_convert_width[1] = half_width;\n\tsource->async_convert_width[2] = half_width;\n\tsource->async_convert_height[0] = height;\n\tsource->async_convert_height[1] = height;\n\tsource->async_convert_height[2] = height;\n\tsource->async_texture_formats[0] = GS_R16;\n\tsource->async_texture_formats[1] = GS_R16;\n\tsource->async_texture_formats[2] = GS_R16;\n\tsource->async_channel_count = 3;\n\treturn true;\n}\n\nstatic inline bool set_planar422_alpha_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tconst uint32_t width = frame->width;\n\tconst uint32_t height = frame->height;\n\tconst uint32_t half_width = (width + 1) / 2;\n\tsource->async_convert_width[0] = width;\n\tsource->async_convert_width[1] = half_width;\n\tsource->async_convert_width[2] = half_width;\n\tsource->async_convert_width[3] = width;\n\tsource->async_convert_height[0] = height;\n\tsource->async_convert_height[1] = height;\n\tsource->async_convert_height[2] = height;\n\tsource->async_convert_height[3] = height;\n\tsource->async_texture_formats[0] = GS_R8;\n\tsource->async_texture_formats[1] = GS_R8;\n\tsource->async_texture_formats[2] = GS_R8;\n\tsource->async_texture_formats[3] = GS_R8;\n\tsource->async_channel_count = 4;\n\treturn true;\n}\n\nstatic inline bool set_nv12_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tconst uint32_t width = frame->width;\n\tconst uint32_t height = frame->height;\n\tconst uint32_t half_width = (width + 1) / 2;\n\tconst uint32_t half_height = (height + 1) / 2;\n\tsource->async_convert_width[0] = width;\n\tsource->async_convert_width[1] = half_width;\n\tsource->async_convert_height[0] = height;\n\tsource->async_convert_height[1] = half_height;\n\tsource->async_texture_formats[0] = GS_R8;\n\tsource->async_texture_formats[1] = GS_R8G8;\n\tsource->async_channel_count = 2;\n\treturn true;\n}\n\nstatic inline bool set_y800_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tsource->async_convert_width[0] = frame->width;\n\tsource->async_convert_height[0] = frame->height;\n\tsource->async_texture_formats[0] = GS_R8;\n\tsource->async_channel_count = 1;\n\treturn true;\n}\n\nstatic inline bool set_rgb_limited_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tsource->async_convert_width[0] = frame->width;\n\tsource->async_convert_height[0] = frame->height;\n\tsource->async_texture_formats[0] = convert_video_format(frame->format, frame->trc);\n\tsource->async_channel_count = 1;\n\treturn true;\n}\n\nstatic inline bool set_bgr3_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tsource->async_convert_width[0] = frame->width * 3;\n\tsource->async_convert_height[0] = frame->height;\n\tsource->async_texture_formats[0] = GS_R8;\n\tsource->async_channel_count = 1;\n\treturn true;\n}\n\nstatic inline bool set_i010_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tconst uint32_t width = frame->width;\n\tconst uint32_t height = frame->height;\n\tconst uint32_t half_width = (width + 1) / 2;\n\tconst uint32_t half_height = (height + 1) / 2;\n\tsource->async_convert_width[0] = width;\n\tsource->async_convert_width[1] = half_width;\n\tsource->async_convert_width[2] = half_width;\n\tsource->async_convert_height[0] = height;\n\tsource->async_convert_height[1] = half_height;\n\tsource->async_convert_height[2] = half_height;\n\tsource->async_texture_formats[0] = GS_R16;\n\tsource->async_texture_formats[1] = GS_R16;\n\tsource->async_texture_formats[2] = GS_R16;\n\tsource->async_channel_count = 3;\n\treturn true;\n}\n\nstatic inline bool set_p010_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tconst uint32_t width = frame->width;\n\tconst uint32_t height = frame->height;\n\tconst uint32_t half_width = (width + 1) / 2;\n\tconst uint32_t half_height = (height + 1) / 2;\n\tsource->async_convert_width[0] = width;\n\tsource->async_convert_width[1] = half_width;\n\tsource->async_convert_height[0] = height;\n\tsource->async_convert_height[1] = half_height;\n\tsource->async_texture_formats[0] = GS_R16;\n\tsource->async_texture_formats[1] = GS_RG16;\n\tsource->async_channel_count = 2;\n\treturn true;\n}\n\nstatic inline bool set_v210_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tconst uint32_t width = frame->width;\n\tconst uint32_t height = frame->height;\n\tconst uint32_t adjusted_width = ((width + 5) / 6) * 4;\n\tsource->async_convert_width[0] = adjusted_width;\n\tsource->async_convert_height[0] = height;\n\tsource->async_texture_formats[0] = GS_R10G10B10A2;\n\tsource->async_channel_count = 1;\n\treturn true;\n}\n\nstatic inline bool set_r10l_sizes(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tsource->async_convert_width[0] = frame->width;\n\tsource->async_convert_height[0] = frame->height;\n\tsource->async_texture_formats[0] = GS_BGRA_UNORM;\n\tsource->async_channel_count = 1;\n\treturn true;\n}\n\nstatic inline bool init_gpu_conversion(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tswitch (get_convert_type(frame->format, frame->full_range, frame->trc)) {\n\tcase CONVERT_422_PACK:\n\t\treturn set_packed422_sizes(source, frame);\n\n\tcase CONVERT_420:\n\tcase CONVERT_420_PQ:\n\t\treturn set_planar420_sizes(source, frame);\n\n\tcase CONVERT_422:\n\t\treturn set_planar422_sizes(source, frame);\n\n\tcase CONVERT_422P10LE:\n\t\treturn set_planar422_16_sizes(source, frame);\n\n\tcase CONVERT_NV12:\n\t\treturn set_nv12_sizes(source, frame);\n\n\tcase CONVERT_444:\n\t\treturn set_planar444_sizes(source, frame);\n\n\tcase CONVERT_444P12LE:\n\t\treturn set_planar444_16_sizes(source, frame);\n\n\tcase CONVERT_800:\n\t\treturn set_y800_sizes(source, frame);\n\n\tcase CONVERT_RGB_LIMITED:\n\t\treturn set_rgb_limited_sizes(source, frame);\n\n\tcase CONVERT_BGR3:\n\t\treturn set_bgr3_sizes(source, frame);\n\n\tcase CONVERT_420_A:\n\t\treturn set_planar420_alpha_sizes(source, frame);\n\n\tcase CONVERT_422_A:\n\t\treturn set_planar422_alpha_sizes(source, frame);\n\n\tcase CONVERT_444_A:\n\t\treturn set_planar444_alpha_sizes(source, frame);\n\n\tcase CONVERT_444P12LE_A:\n\t\treturn set_planar444_16_alpha_sizes(source, frame);\n\n\tcase CONVERT_444_A_PACK:\n\t\treturn set_packed444_alpha_sizes(source, frame);\n\n\tcase CONVERT_I010:\n\t\treturn set_i010_sizes(source, frame);\n\n\tcase CONVERT_P010:\n\t\treturn set_p010_sizes(source, frame);\n\n\tcase CONVERT_V210:\n\t\treturn set_v210_sizes(source, frame);\n\n\tcase CONVERT_R10L:\n\t\treturn set_r10l_sizes(source, frame);\n\n\tcase CONVERT_NONE:\n\t\tassert(false && \"No conversion requested\");\n\t\tbreak;\n\t}\n\treturn false;\n}\n\nbool set_async_texture_size(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tenum convert_type cur = get_convert_type(frame->format, frame->full_range, frame->trc);\n\n\tif (source->async_width == frame->width && source->async_height == frame->height &&\n\t    source->async_format == frame->format && source->async_full_range == frame->full_range &&\n\t    source->async_trc == frame->trc)\n\t\treturn true;\n\n\tsource->async_width = frame->width;\n\tsource->async_height = frame->height;\n\tsource->async_format = frame->format;\n\tsource->async_full_range = frame->full_range;\n\tsource->async_trc = frame->trc;\n\n\tgs_enter_context(obs->video.graphics);\n\n\tfor (size_t c = 0; c < MAX_AV_PLANES; c++) {\n\t\tgs_texture_destroy(source->async_textures[c]);\n\t\tsource->async_textures[c] = NULL;\n\t\tgs_texture_destroy(source->async_prev_textures[c]);\n\t\tsource->async_prev_textures[c] = NULL;\n\t}\n\n\tgs_texrender_destroy(source->async_texrender);\n\tgs_texrender_destroy(source->async_prev_texrender);\n\tsource->async_texrender = NULL;\n\tsource->async_prev_texrender = NULL;\n\n\tconst enum gs_color_format format = convert_video_format(frame->format, frame->trc);\n\tconst bool async_gpu_conversion = (cur != CONVERT_NONE) && init_gpu_conversion(source, frame);\n\tsource->async_gpu_conversion = async_gpu_conversion;\n\tif (async_gpu_conversion) {\n\t\tsource->async_texrender = gs_texrender_create(format, GS_ZS_NONE);\n\n\t\tfor (int c = 0; c < source->async_channel_count; ++c)\n\t\t\tsource->async_textures[c] =\n\t\t\t\tgs_texture_create(source->async_convert_width[c], source->async_convert_height[c],\n\t\t\t\t\t\t  source->async_texture_formats[c], 1, NULL, GS_DYNAMIC);\n\t} else {\n\t\tsource->async_textures[0] = gs_texture_create(frame->width, frame->height, format, 1, NULL, GS_DYNAMIC);\n\t}\n\n\tif (deinterlacing_enabled(source))\n\t\tset_deinterlace_texture_size(source);\n\n\tgs_leave_context();\n\n\treturn source->async_textures[0] != NULL;\n}\n\nstatic void upload_raw_frame(gs_texture_t *tex[MAX_AV_PLANES], const struct obs_source_frame *frame)\n{\n\tswitch (get_convert_type(frame->format, frame->full_range, frame->trc)) {\n\tcase CONVERT_422_PACK:\n\tcase CONVERT_800:\n\tcase CONVERT_RGB_LIMITED:\n\tcase CONVERT_BGR3:\n\tcase CONVERT_420:\n\tcase CONVERT_420_PQ:\n\tcase CONVERT_422:\n\tcase CONVERT_422P10LE:\n\tcase CONVERT_NV12:\n\tcase CONVERT_444:\n\tcase CONVERT_444P12LE:\n\tcase CONVERT_420_A:\n\tcase CONVERT_422_A:\n\tcase CONVERT_444_A:\n\tcase CONVERT_444P12LE_A:\n\tcase CONVERT_444_A_PACK:\n\tcase CONVERT_I010:\n\tcase CONVERT_P010:\n\tcase CONVERT_V210:\n\tcase CONVERT_R10L:\n\t\tfor (size_t c = 0; c < MAX_AV_PLANES; c++) {\n\t\t\tif (tex[c])\n\t\t\t\tgs_texture_set_image(tex[c], frame->data[c], frame->linesize[c], false);\n\t\t}\n\t\tbreak;\n\n\tcase CONVERT_NONE:\n\t\tassert(false && \"No conversion requested\");\n\t\tbreak;\n\t}\n}\n\nstatic const char *select_conversion_technique(enum video_format format, bool full_range, uint8_t trc)\n{\n\tswitch (format) {\n\tcase VIDEO_FORMAT_UYVY:\n\t\treturn \"UYVY_Reverse\";\n\n\tcase VIDEO_FORMAT_YUY2:\n\t\tswitch (trc) {\n\t\tcase VIDEO_TRC_PQ:\n\t\t\treturn \"YUY2_PQ_Reverse\";\n\t\tcase VIDEO_TRC_HLG:\n\t\t\treturn \"YUY2_HLG_Reverse\";\n\t\tdefault:\n\t\t\treturn \"YUY2_Reverse\";\n\t\t}\n\n\tcase VIDEO_FORMAT_YVYU:\n\t\treturn \"YVYU_Reverse\";\n\n\tcase VIDEO_FORMAT_I420:\n\t\tswitch (trc) {\n\t\tcase VIDEO_TRC_PQ:\n\t\t\treturn \"I420_PQ_Reverse\";\n\t\tcase VIDEO_TRC_HLG:\n\t\t\treturn \"I420_HLG_Reverse\";\n\t\tdefault:\n\t\t\treturn \"I420_Reverse\";\n\t\t}\n\n\tcase VIDEO_FORMAT_NV12:\n\t\tswitch (trc) {\n\t\tcase VIDEO_TRC_PQ:\n\t\t\treturn \"NV12_PQ_Reverse\";\n\t\tcase VIDEO_TRC_HLG:\n\t\t\treturn \"NV12_HLG_Reverse\";\n\t\tdefault:\n\t\t\treturn \"NV12_Reverse\";\n\t\t}\n\n\tcase VIDEO_FORMAT_I444:\n\t\treturn \"I444_Reverse\";\n\n\tcase VIDEO_FORMAT_I412:\n\t\tswitch (trc) {\n\t\tcase VIDEO_TRC_PQ:\n\t\t\treturn \"I412_PQ_Reverse\";\n\t\tcase VIDEO_TRC_HLG:\n\t\t\treturn \"I412_HLG_Reverse\";\n\t\tdefault:\n\t\t\treturn \"I412_Reverse\";\n\t\t}\n\n\tcase VIDEO_FORMAT_Y800:\n\t\treturn full_range ? \"Y800_Full\" : \"Y800_Limited\";\n\n\tcase VIDEO_FORMAT_BGR3:\n\t\treturn full_range ? \"BGR3_Full\" : \"BGR3_Limited\";\n\n\tcase VIDEO_FORMAT_I422:\n\t\treturn \"I422_Reverse\";\n\n\tcase VIDEO_FORMAT_I210:\n\t\tswitch (trc) {\n\t\tcase VIDEO_TRC_PQ:\n\t\t\treturn \"I210_PQ_Reverse\";\n\t\tcase VIDEO_TRC_HLG:\n\t\t\treturn \"I210_HLG_Reverse\";\n\t\tdefault:\n\t\t\treturn \"I210_Reverse\";\n\t\t}\n\n\tcase VIDEO_FORMAT_I40A:\n\t\treturn \"I40A_Reverse\";\n\n\tcase VIDEO_FORMAT_I42A:\n\t\treturn \"I42A_Reverse\";\n\n\tcase VIDEO_FORMAT_YUVA:\n\t\treturn \"YUVA_Reverse\";\n\n\tcase VIDEO_FORMAT_YA2L:\n\t\treturn \"YA2L_Reverse\";\n\n\tcase VIDEO_FORMAT_AYUV:\n\t\treturn \"AYUV_Reverse\";\n\n\tcase VIDEO_FORMAT_I010: {\n\t\tswitch (trc) {\n\t\tcase VIDEO_TRC_PQ:\n\t\t\treturn \"I010_PQ_2020_709_Reverse\";\n\t\tcase VIDEO_TRC_HLG:\n\t\t\treturn \"I010_HLG_2020_709_Reverse\";\n\t\tdefault:\n\t\t\treturn \"I010_SRGB_Reverse\";\n\t\t}\n\t}\n\n\tcase VIDEO_FORMAT_P010: {\n\t\tswitch (trc) {\n\t\tcase VIDEO_TRC_PQ:\n\t\t\treturn \"P010_PQ_2020_709_Reverse\";\n\t\tcase VIDEO_TRC_HLG:\n\t\t\treturn \"P010_HLG_2020_709_Reverse\";\n\t\tdefault:\n\t\t\treturn \"P010_SRGB_Reverse\";\n\t\t}\n\t}\n\n\tcase VIDEO_FORMAT_V210: {\n\t\tswitch (trc) {\n\t\tcase VIDEO_TRC_PQ:\n\t\t\treturn \"V210_PQ_2020_709_Reverse\";\n\t\tcase VIDEO_TRC_HLG:\n\t\t\treturn \"V210_HLG_2020_709_Reverse\";\n\t\tdefault:\n\t\t\treturn \"V210_SRGB_Reverse\";\n\t\t}\n\t}\n\n\tcase VIDEO_FORMAT_R10L: {\n\t\tswitch (trc) {\n\t\tcase VIDEO_TRC_PQ:\n\t\t\treturn full_range ? \"R10L_PQ_2020_709_Full_Reverse\" : \"R10L_PQ_2020_709_Limited_Reverse\";\n\t\tcase VIDEO_TRC_HLG:\n\t\t\treturn full_range ? \"R10L_HLG_2020_709_Full_Reverse\" : \"R10L_HLG_2020_709_Limited_Reverse\";\n\t\tdefault:\n\t\t\treturn full_range ? \"R10L_SRGB_Full_Reverse\" : \"R10L_SRGB_Limited_Reverse\";\n\t\t}\n\t}\n\n\tcase VIDEO_FORMAT_BGRA:\n\tcase VIDEO_FORMAT_BGRX:\n\tcase VIDEO_FORMAT_RGBA:\n\tcase VIDEO_FORMAT_NONE:\n\t\tif (full_range)\n\t\t\tassert(false && \"No conversion requested\");\n\t\telse\n\t\t\treturn \"RGB_Limited\";\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_P216:\n\tcase VIDEO_FORMAT_P416:\n\t\t/* Unimplemented */\n\t\tbreak;\n\t}\n\treturn NULL;\n}\n\nstatic bool need_linear_output(enum video_format format)\n{\n\treturn (format == VIDEO_FORMAT_I010) || (format == VIDEO_FORMAT_P010) || (format == VIDEO_FORMAT_I210) ||\n\t       (format == VIDEO_FORMAT_I412) || (format == VIDEO_FORMAT_YA2L);\n}\n\nstatic inline void set_eparam(gs_effect_t *effect, const char *name, float val)\n{\n\tgs_eparam_t *param = gs_effect_get_param_by_name(effect, name);\n\tgs_effect_set_float(param, val);\n}\n\nstatic bool update_async_texrender(struct obs_source *source, const struct obs_source_frame *frame,\n\t\t\t\t   gs_texture_t *tex[MAX_AV_PLANES], gs_texrender_t *texrender)\n{\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_CONVERT_FORMAT, \"Convert Format\");\n\n\tgs_texrender_reset(texrender);\n\n\tupload_raw_frame(tex, frame);\n\n\tuint32_t cx = source->async_width;\n\tuint32_t cy = source->async_height;\n\n\tconst char *tech_name = select_conversion_technique(frame->format, frame->full_range, frame->trc);\n\tgs_effect_t *conv = obs->video.conversion_effect;\n\tgs_technique_t *tech = gs_effect_get_technique(conv, tech_name);\n\tconst bool linear = need_linear_output(frame->format);\n\n\tconst bool success = gs_texrender_begin(texrender, cx, cy);\n\n\tif (success) {\n\t\tconst bool previous = gs_framebuffer_srgb_enabled();\n\t\tgs_enable_framebuffer_srgb(linear);\n\n\t\tgs_enable_blending(false);\n\n\t\tgs_technique_begin(tech);\n\t\tgs_technique_begin_pass(tech, 0);\n\n\t\tif (tex[0])\n\t\t\tgs_effect_set_texture(gs_effect_get_param_by_name(conv, \"image\"), tex[0]);\n\t\tif (tex[1])\n\t\t\tgs_effect_set_texture(gs_effect_get_param_by_name(conv, \"image1\"), tex[1]);\n\t\tif (tex[2])\n\t\t\tgs_effect_set_texture(gs_effect_get_param_by_name(conv, \"image2\"), tex[2]);\n\t\tif (tex[3])\n\t\t\tgs_effect_set_texture(gs_effect_get_param_by_name(conv, \"image3\"), tex[3]);\n\t\tset_eparam(conv, \"width\", (float)cx);\n\t\tset_eparam(conv, \"height\", (float)cy);\n\t\tset_eparam(conv, \"width_d2\", (float)cx * 0.5f);\n\t\tset_eparam(conv, \"height_d2\", (float)cy * 0.5f);\n\t\tset_eparam(conv, \"width_x2_i\", 0.5f / (float)cx);\n\t\tset_eparam(conv, \"height_x2_i\", 0.5f / (float)cy);\n\n\t\t/* BT.2408 says higher than 1000 isn't comfortable */\n\t\tfloat hlg_peak_level = obs->video.hdr_nominal_peak_level;\n\t\tif (hlg_peak_level > 1000.f)\n\t\t\thlg_peak_level = 1000.f;\n\n\t\tconst float maximum_nits = (frame->trc == VIDEO_TRC_HLG) ? hlg_peak_level : 10000.f;\n\t\tset_eparam(conv, \"maximum_over_sdr_white_nits\", maximum_nits / obs_get_video_sdr_white_level());\n\t\tconst float hlg_exponent = 0.2f + (0.42f * log10f(hlg_peak_level / 1000.f));\n\t\tset_eparam(conv, \"hlg_exponent\", hlg_exponent);\n\t\tset_eparam(conv, \"hdr_lw\", (float)frame->max_luminance);\n\t\tset_eparam(conv, \"hdr_lmax\", obs_get_video_hdr_nominal_peak_level());\n\n\t\tstruct vec4 vec0, vec1, vec2;\n\t\tvec4_set(&vec0, frame->color_matrix[0], frame->color_matrix[1], frame->color_matrix[2],\n\t\t\t frame->color_matrix[3]);\n\t\tvec4_set(&vec1, frame->color_matrix[4], frame->color_matrix[5], frame->color_matrix[6],\n\t\t\t frame->color_matrix[7]);\n\t\tvec4_set(&vec2, frame->color_matrix[8], frame->color_matrix[9], frame->color_matrix[10],\n\t\t\t frame->color_matrix[11]);\n\t\tgs_effect_set_vec4(gs_effect_get_param_by_name(conv, \"color_vec0\"), &vec0);\n\t\tgs_effect_set_vec4(gs_effect_get_param_by_name(conv, \"color_vec1\"), &vec1);\n\t\tgs_effect_set_vec4(gs_effect_get_param_by_name(conv, \"color_vec2\"), &vec2);\n\t\tif (!frame->full_range) {\n\t\t\tgs_eparam_t *min_param = gs_effect_get_param_by_name(conv, \"color_range_min\");\n\t\t\tgs_effect_set_val(min_param, frame->color_range_min, sizeof(float) * 3);\n\t\t\tgs_eparam_t *max_param = gs_effect_get_param_by_name(conv, \"color_range_max\");\n\t\t\tgs_effect_set_val(max_param, frame->color_range_max, sizeof(float) * 3);\n\t\t}\n\n\t\tgs_draw(GS_TRIS, 0, 3);\n\n\t\tgs_technique_end_pass(tech);\n\t\tgs_technique_end(tech);\n\n\t\tgs_enable_blending(true);\n\n\t\tgs_enable_framebuffer_srgb(previous);\n\n\t\tgs_texrender_end(texrender);\n\t}\n\n\tGS_DEBUG_MARKER_END();\n\treturn success;\n}\n\nbool update_async_texture(struct obs_source *source, const struct obs_source_frame *frame, gs_texture_t *tex,\n\t\t\t  gs_texrender_t *texrender)\n{\n\tgs_texture_t *tex3[MAX_AV_PLANES] = {tex, NULL, NULL, NULL, NULL, NULL, NULL, NULL};\n\treturn update_async_textures(source, frame, tex3, texrender);\n}\n\nbool update_async_textures(struct obs_source *source, const struct obs_source_frame *frame,\n\t\t\t   gs_texture_t *tex[MAX_AV_PLANES], gs_texrender_t *texrender)\n{\n\tenum convert_type type;\n\n\tsource->async_flip = frame->flip;\n\tsource->async_linear_alpha = (frame->flags & OBS_SOURCE_FRAME_LINEAR_ALPHA) != 0;\n\n\tif (source->async_gpu_conversion && texrender)\n\t\treturn update_async_texrender(source, frame, tex, texrender);\n\n\ttype = get_convert_type(frame->format, frame->full_range, frame->trc);\n\tif (type == CONVERT_NONE) {\n\t\tgs_texture_set_image(tex[0], frame->data[0], frame->linesize[0], false);\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic inline void obs_source_draw_texture(struct obs_source *source, gs_effect_t *effect)\n{\n\tgs_texture_t *tex = source->async_textures[0];\n\tgs_eparam_t *param;\n\n\tif (source->async_texrender)\n\t\ttex = gs_texrender_get_texture(source->async_texrender);\n\n\tif (!tex)\n\t\treturn;\n\n\tparam = gs_effect_get_param_by_name(effect, \"image\");\n\n\tconst bool linear_srgb = gs_get_linear_srgb();\n\n\tconst bool previous = gs_framebuffer_srgb_enabled();\n\tgs_enable_framebuffer_srgb(linear_srgb);\n\n\tif (linear_srgb) {\n\t\tgs_effect_set_texture_srgb(param, tex);\n\t} else {\n\t\tgs_effect_set_texture(param, tex);\n\t}\n\n\tgs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0);\n\n\tgs_enable_framebuffer_srgb(previous);\n}\n\nstatic void recreate_async_texture(obs_source_t *source, enum gs_color_format format)\n{\n\tuint32_t cx = gs_texture_get_width(source->async_textures[0]);\n\tuint32_t cy = gs_texture_get_height(source->async_textures[0]);\n\tgs_texture_destroy(source->async_textures[0]);\n\tsource->async_textures[0] = gs_texture_create(cx, cy, format, 1, NULL, GS_DYNAMIC);\n}\n\nstatic inline void check_to_swap_bgrx_bgra(obs_source_t *source, struct obs_source_frame *frame)\n{\n\tenum gs_color_format format = gs_texture_get_color_format(source->async_textures[0]);\n\tif (format == GS_BGRX && frame->format == VIDEO_FORMAT_BGRA) {\n\t\trecreate_async_texture(source, GS_BGRA);\n\t} else if (format == GS_BGRA && frame->format == VIDEO_FORMAT_BGRX) {\n\t\trecreate_async_texture(source, GS_BGRX);\n\t}\n}\n\nstatic void obs_source_update_async_video(obs_source_t *source)\n{\n\tif (!source->async_rendered) {\n\t\tsource->async_rendered = true;\n\n\t\tstruct obs_source_frame *frame = obs_source_get_frame(source);\n\t\tif (frame) {\n\t\t\tcheck_to_swap_bgrx_bgra(source, frame);\n\n\t\t\tif (!source->async_decoupled || !source->async_unbuffered) {\n\t\t\t\tsource->timing_adjust = obs->video.video_time - frame->timestamp;\n\t\t\t\tsource->timing_set = true;\n\t\t\t}\n\n\t\t\tif (source->async_update_texture) {\n\t\t\t\tupdate_async_textures(source, frame, source->async_textures, source->async_texrender);\n\t\t\t\tsource->async_update_texture = false;\n\t\t\t}\n\n\t\t\tsource->async_last_rendered_ts = frame->timestamp;\n\t\t\tobs_source_release_frame(source, frame);\n\t\t}\n\t}\n}\n\nstatic void rotate_async_video(obs_source_t *source, long rotation)\n{\n\tfloat x = 0;\n\tfloat y = 0;\n\n\tswitch (rotation) {\n\tcase 90:\n\t\ty = (float)source->async_width;\n\t\tbreak;\n\tcase 270:\n\tcase -90:\n\t\tx = (float)source->async_height;\n\t\tbreak;\n\tcase 180:\n\t\tx = (float)source->async_width;\n\t\ty = (float)source->async_height;\n\t}\n\n\tgs_matrix_translate3f(x, y, 0);\n\tgs_matrix_rotaa4f(0.0f, 0.0f, -1.0f, RAD((float)rotation));\n}\n\nstatic inline void obs_source_render_async_video(obs_source_t *source)\n{\n\tif (source->async_textures[0] && source->async_active) {\n\t\tgs_timer_t *timer = NULL;\n\t\tconst uint64_t start = source_profiler_source_render_begin(&timer);\n\n\t\tconst enum gs_color_space source_space = convert_video_space(source->async_format, source->async_trc);\n\n\t\tgs_effect_t *const effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);\n\t\tconst char *tech_name = \"Draw\";\n\t\tfloat multiplier = 1.0;\n\t\tconst enum gs_color_space current_space = gs_get_color_space();\n\t\tbool linear_srgb = gs_get_linear_srgb();\n\t\tbool nonlinear_alpha = false;\n\t\tswitch (source_space) {\n\t\tcase GS_CS_SRGB:\n\t\t\tlinear_srgb = linear_srgb || (current_space != GS_CS_SRGB);\n\t\t\tnonlinear_alpha = linear_srgb && !source->async_linear_alpha;\n\t\t\tswitch (current_space) {\n\t\t\tcase GS_CS_SRGB:\n\t\t\tcase GS_CS_SRGB_16F:\n\t\t\tcase GS_CS_709_EXTENDED:\n\t\t\t\tif (nonlinear_alpha)\n\t\t\t\t\ttech_name = \"DrawNonlinearAlpha\";\n\t\t\t\tbreak;\n\t\t\tcase GS_CS_709_SCRGB:\n\t\t\t\ttech_name = nonlinear_alpha ? \"DrawNonlinearAlphaMultiply\" : \"DrawMultiply\";\n\t\t\t\tmultiplier = obs_get_video_sdr_white_level() / 80.0f;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase GS_CS_SRGB_16F:\n\t\t\tif (current_space == GS_CS_709_SCRGB) {\n\t\t\t\ttech_name = \"DrawMultiply\";\n\t\t\t\tmultiplier = obs_get_video_sdr_white_level() / 80.0f;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase GS_CS_709_EXTENDED:\n\t\t\tswitch (current_space) {\n\t\t\tcase GS_CS_SRGB:\n\t\t\tcase GS_CS_SRGB_16F:\n\t\t\t\ttech_name = \"DrawTonemap\";\n\t\t\t\tlinear_srgb = true;\n\t\t\t\tbreak;\n\t\t\tcase GS_CS_709_SCRGB:\n\t\t\t\ttech_name = \"DrawMultiply\";\n\t\t\t\tmultiplier = obs_get_video_sdr_white_level() / 80.0f;\n\t\t\t\tbreak;\n\t\t\tcase GS_CS_709_EXTENDED:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase GS_CS_709_SCRGB:\n\t\t\tswitch (current_space) {\n\t\t\tcase GS_CS_SRGB:\n\t\t\tcase GS_CS_SRGB_16F:\n\t\t\t\ttech_name = \"DrawMultiplyTonemap\";\n\t\t\t\tmultiplier = 80.0f / obs_get_video_sdr_white_level();\n\t\t\t\tlinear_srgb = true;\n\t\t\t\tbreak;\n\t\t\tcase GS_CS_709_EXTENDED:\n\t\t\t\ttech_name = \"DrawMultiply\";\n\t\t\t\tmultiplier = 80.0f / obs_get_video_sdr_white_level();\n\t\t\t\tbreak;\n\t\t\tcase GS_CS_709_SCRGB:\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tconst bool previous = gs_set_linear_srgb(linear_srgb);\n\n\t\tgs_technique_t *const tech = gs_effect_get_technique(effect, tech_name);\n\t\tgs_effect_set_float(gs_effect_get_param_by_name(effect, \"multiplier\"), multiplier);\n\t\tgs_technique_begin(tech);\n\t\tgs_technique_begin_pass(tech, 0);\n\n\t\tlong rotation = source->async_rotation;\n\t\tif (rotation) {\n\t\t\tgs_matrix_push();\n\t\t\trotate_async_video(source, rotation);\n\t\t}\n\n\t\tif (nonlinear_alpha) {\n\t\t\tgs_blend_state_push();\n\t\t\tgs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);\n\t\t}\n\n\t\tobs_source_draw_texture(source, effect);\n\n\t\tif (nonlinear_alpha) {\n\t\t\tgs_blend_state_pop();\n\t\t}\n\n\t\tif (rotation) {\n\t\t\tgs_matrix_pop();\n\t\t}\n\n\t\tgs_technique_end_pass(tech);\n\t\tgs_technique_end(tech);\n\n\t\tgs_set_linear_srgb(previous);\n\n\t\tsource_profiler_source_render_end(source, start, timer);\n\t}\n}\n\nstatic inline void obs_source_render_filters(obs_source_t *source)\n{\n\tobs_source_t *first_filter;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\tfirst_filter = obs_source_get_ref(source->filters.array[0]);\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\tsource->rendering_filter = true;\n\tobs_source_video_render(first_filter);\n\tsource->rendering_filter = false;\n\n\tobs_source_release(first_filter);\n}\n\nstatic inline uint32_t get_async_width(const obs_source_t *source)\n{\n\treturn ((source->async_rotation % 180) == 0) ? source->async_width : source->async_height;\n}\n\nstatic inline uint32_t get_async_height(const obs_source_t *source)\n{\n\treturn ((source->async_rotation % 180) == 0) ? source->async_height : source->async_width;\n}\n\nstatic uint32_t get_base_width(const obs_source_t *source)\n{\n\tbool is_filter = !!source->filter_parent;\n\tbool func_valid = source->context.data && source->info.get_width;\n\n\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION) {\n\t\treturn source->enabled ? source->transition_actual_cx : 0;\n\n\t} else if (func_valid && (!is_filter || source->enabled)) {\n\t\treturn source->info.get_width(source->context.data);\n\n\t} else if (is_filter) {\n\t\treturn get_base_width(source->filter_target);\n\t}\n\n\treturn source->async_active ? get_async_width(source) : 0;\n}\n\nstatic uint32_t get_base_height(const obs_source_t *source)\n{\n\tbool is_filter = !!source->filter_parent;\n\tbool func_valid = source->context.data && source->info.get_height;\n\n\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION) {\n\t\treturn source->enabled ? source->transition_actual_cy : 0;\n\n\t} else if (func_valid && (!is_filter || source->enabled)) {\n\t\treturn source->info.get_height(source->context.data);\n\n\t} else if (is_filter) {\n\t\treturn get_base_height(source->filter_target);\n\t}\n\n\treturn source->async_active ? get_async_height(source) : 0;\n}\n\nstatic void source_render(obs_source_t *source, gs_effect_t *effect)\n{\n\tgs_timer_t *timer = NULL;\n\tconst uint64_t start = source_profiler_source_render_begin(&timer);\n\n\tvoid *const data = source->context.data;\n\tconst enum gs_color_space current_space = gs_get_color_space();\n\tconst enum gs_color_space source_space = obs_source_get_color_space(source, 1, &current_space);\n\n\tconst char *convert_tech = NULL;\n\tfloat multiplier = 1.0;\n\tenum gs_color_format format = gs_get_format_from_space(source_space);\n\tswitch (source_space) {\n\tcase GS_CS_SRGB:\n\tcase GS_CS_SRGB_16F:\n\t\tswitch (current_space) {\n\t\tcase GS_CS_709_EXTENDED:\n\t\t\tconvert_tech = \"Draw\";\n\t\t\tbreak;\n\t\tcase GS_CS_709_SCRGB:\n\t\t\tconvert_tech = \"DrawMultiply\";\n\t\t\tmultiplier = obs_get_video_sdr_white_level() / 80.0f;\n\t\t\tbreak;\n\t\tcase GS_CS_SRGB:\n\t\t\tbreak;\n\t\tcase GS_CS_SRGB_16F:\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase GS_CS_709_EXTENDED:\n\t\tswitch (current_space) {\n\t\tcase GS_CS_SRGB:\n\t\tcase GS_CS_SRGB_16F:\n\t\t\tconvert_tech = \"DrawTonemap\";\n\t\t\tbreak;\n\t\tcase GS_CS_709_SCRGB:\n\t\t\tconvert_tech = \"DrawMultiply\";\n\t\t\tmultiplier = obs_get_video_sdr_white_level() / 80.0f;\n\t\t\tbreak;\n\t\tcase GS_CS_709_EXTENDED:\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\tcase GS_CS_709_SCRGB:\n\t\tswitch (current_space) {\n\t\tcase GS_CS_SRGB:\n\t\tcase GS_CS_SRGB_16F:\n\t\t\tconvert_tech = \"DrawMultiplyTonemap\";\n\t\t\tmultiplier = 80.0f / obs_get_video_sdr_white_level();\n\t\t\tbreak;\n\t\tcase GS_CS_709_EXTENDED:\n\t\t\tconvert_tech = \"DrawMultiply\";\n\t\t\tmultiplier = 80.0f / obs_get_video_sdr_white_level();\n\t\t\tbreak;\n\t\tcase GS_CS_709_SCRGB:\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tif (convert_tech) {\n\t\tif (source->color_space_texrender) {\n\t\t\tif (gs_texrender_get_format(source->color_space_texrender) != format) {\n\t\t\t\tgs_texrender_destroy(source->color_space_texrender);\n\t\t\t\tsource->color_space_texrender = NULL;\n\t\t\t}\n\t\t}\n\n\t\tif (!source->color_space_texrender) {\n\t\t\tsource->color_space_texrender = gs_texrender_create(format, GS_ZS_NONE);\n\t\t}\n\n\t\tgs_texrender_reset(source->color_space_texrender);\n\t\tconst int cx = get_base_width(source);\n\t\tconst int cy = get_base_height(source);\n\t\tif (gs_texrender_begin_with_color_space(source->color_space_texrender, cx, cy, source_space)) {\n\t\t\tgs_enable_blending(false);\n\n\t\t\tstruct vec4 clear_color;\n\t\t\tvec4_zero(&clear_color);\n\t\t\tgs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);\n\t\t\tgs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);\n\n\t\t\tsource->info.video_render(data, effect);\n\n\t\t\tgs_enable_blending(true);\n\n\t\t\tgs_texrender_end(source->color_space_texrender);\n\n\t\t\tgs_effect_t *default_effect = obs->video.default_effect;\n\t\t\tgs_technique_t *tech = gs_effect_get_technique(default_effect, convert_tech);\n\n\t\t\tconst bool previous = gs_framebuffer_srgb_enabled();\n\t\t\tgs_enable_framebuffer_srgb(true);\n\n\t\t\tgs_texture_t *const tex = gs_texrender_get_texture(source->color_space_texrender);\n\t\t\tgs_effect_set_texture_srgb(gs_effect_get_param_by_name(default_effect, \"image\"), tex);\n\t\t\tgs_effect_set_float(gs_effect_get_param_by_name(default_effect, \"multiplier\"), multiplier);\n\n\t\t\tgs_blend_state_push();\n\t\t\tgs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);\n\n\t\t\tconst size_t passes = gs_technique_begin(tech);\n\t\t\tfor (size_t i = 0; i < passes; i++) {\n\t\t\t\tgs_technique_begin_pass(tech, i);\n\t\t\t\tgs_draw_sprite(tex, 0, 0, 0);\n\t\t\t\tgs_technique_end_pass(tech);\n\t\t\t}\n\t\t\tgs_technique_end(tech);\n\n\t\t\tgs_blend_state_pop();\n\n\t\t\tgs_enable_framebuffer_srgb(previous);\n\t\t}\n\t} else {\n\t\tsource->info.video_render(data, effect);\n\t}\n\tsource_profiler_source_render_end(source, start, timer);\n}\n\nvoid obs_source_default_render(obs_source_t *source)\n{\n\tif (source->context.data) {\n\t\tgs_effect_t *effect = obs->video.default_effect;\n\t\tgs_technique_t *tech = gs_effect_get_technique(effect, \"Draw\");\n\t\tsize_t passes, i;\n\n\t\tpasses = gs_technique_begin(tech);\n\t\tfor (i = 0; i < passes; i++) {\n\t\t\tgs_technique_begin_pass(tech, i);\n\t\t\tsource_render(source, effect);\n\t\t\tgs_technique_end_pass(tech);\n\t\t}\n\t\tgs_technique_end(tech);\n\t}\n}\n\nstatic inline void obs_source_main_render(obs_source_t *source)\n{\n\tuint32_t flags = source->info.output_flags;\n\tbool custom_draw = (flags & OBS_SOURCE_CUSTOM_DRAW) != 0;\n\tbool srgb_aware = (flags & OBS_SOURCE_SRGB) != 0;\n\tbool default_effect = !source->filter_parent && source->filters.num == 0 && !custom_draw;\n\tbool previous_srgb = false;\n\n\tif (!srgb_aware) {\n\t\tprevious_srgb = gs_get_linear_srgb();\n\t\tgs_set_linear_srgb(false);\n\t}\n\n\tif (default_effect) {\n\t\tobs_source_default_render(source);\n\t} else if (source->context.data) {\n\t\tsource_render(source, custom_draw ? NULL : gs_get_effect());\n\t}\n\n\tif (!srgb_aware)\n\t\tgs_set_linear_srgb(previous_srgb);\n}\n\nstatic bool ready_async_frame(obs_source_t *source, uint64_t sys_time);\n\n#if GS_USE_DEBUG_MARKERS\nstatic const char *get_type_format(enum obs_source_type type)\n{\n\tswitch (type) {\n\tcase OBS_SOURCE_TYPE_INPUT:\n\t\treturn \"Input: %s\";\n\tcase OBS_SOURCE_TYPE_FILTER:\n\t\treturn \"Filter: %s\";\n\tcase OBS_SOURCE_TYPE_TRANSITION:\n\t\treturn \"Transition: %s\";\n\tcase OBS_SOURCE_TYPE_SCENE:\n\t\treturn \"Scene: %s\";\n\tdefault:\n\t\treturn \"[Unknown]: %s\";\n\t}\n}\n#endif\n\nstatic inline void render_video(obs_source_t *source)\n{\n\tif (source->info.type != OBS_SOURCE_TYPE_FILTER && (source->info.output_flags & OBS_SOURCE_VIDEO) == 0) {\n\t\tif (source->filter_parent)\n\t\t\tobs_source_skip_video_filter(source);\n\t\treturn;\n\t}\n\n\tif (source->info.type == OBS_SOURCE_TYPE_INPUT && (source->info.output_flags & OBS_SOURCE_ASYNC) != 0 &&\n\t    !source->rendering_filter) {\n\t\tif (deinterlacing_enabled(source))\n\t\t\tdeinterlace_update_async_video(source);\n\t\tobs_source_update_async_video(source);\n\t}\n\n\tif (!source->context.data || !source->enabled) {\n\t\tif (source->filter_parent)\n\t\t\tobs_source_skip_video_filter(source);\n\t\treturn;\n\t}\n\n\tGS_DEBUG_MARKER_BEGIN_FORMAT(GS_DEBUG_COLOR_SOURCE, get_type_format(source->info.type),\n\t\t\t\t     obs_source_get_name(source));\n\n\tif (source->filters.num && !source->rendering_filter)\n\t\tobs_source_render_filters(source);\n\n\telse if (source->info.video_render)\n\t\tobs_source_main_render(source);\n\n\telse if (source->filter_target)\n\t\tobs_source_video_render(source->filter_target);\n\n\telse if (deinterlacing_enabled(source))\n\t\tdeinterlace_render(source);\n\n\telse\n\t\tobs_source_render_async_video(source);\n\n\tGS_DEBUG_MARKER_END();\n}\n\nvoid obs_source_video_render(obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_video_render\"))\n\t\treturn;\n\n\tsource = obs_source_get_ref(source);\n\tif (source) {\n\t\trender_video(source);\n\t\tobs_source_release(source);\n\t}\n}\n\nstatic uint32_t get_recurse_width(obs_source_t *source)\n{\n\tuint32_t width;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\n\twidth = (source->filters.num) ? get_base_width(source->filters.array[0]) : get_base_width(source);\n\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\treturn width;\n}\n\nstatic uint32_t get_recurse_height(obs_source_t *source)\n{\n\tuint32_t height;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\n\theight = (source->filters.num) ? get_base_height(source->filters.array[0]) : get_base_height(source);\n\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\treturn height;\n}\n\nuint32_t obs_source_get_width(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_get_width\"))\n\t\treturn 0;\n\n\treturn (source->info.type != OBS_SOURCE_TYPE_FILTER) ? get_recurse_width(source) : get_base_width(source);\n}\n\nuint32_t obs_source_get_height(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_get_height\"))\n\t\treturn 0;\n\n\treturn (source->info.type != OBS_SOURCE_TYPE_FILTER) ? get_recurse_height(source) : get_base_height(source);\n}\n\nenum gs_color_space obs_source_get_color_space(obs_source_t *source, size_t count,\n\t\t\t\t\t       const enum gs_color_space *preferred_spaces)\n{\n\tif (!data_valid(source, \"obs_source_get_color_space\"))\n\t\treturn GS_CS_SRGB;\n\n\tif (source->info.type != OBS_SOURCE_TYPE_FILTER && (source->info.output_flags & OBS_SOURCE_VIDEO) == 0) {\n\t\tif (source->filter_parent)\n\t\t\treturn obs_source_get_color_space(source->filter_parent, count, preferred_spaces);\n\t}\n\n\tif (!source->context.data || !source->enabled) {\n\t\tif (source->filter_target)\n\t\t\treturn obs_source_get_color_space(source->filter_target, count, preferred_spaces);\n\t}\n\n\tif (source->info.output_flags & OBS_SOURCE_ASYNC) {\n\t\tconst enum gs_color_space video_space = convert_video_space(source->async_format, source->async_trc);\n\n\t\tenum gs_color_space space = video_space;\n\t\tfor (size_t i = 0; i < count; ++i) {\n\t\t\tspace = preferred_spaces[i];\n\t\t\tif (space == video_space)\n\t\t\t\tbreak;\n\t\t}\n\n\t\treturn space;\n\t}\n\n\tassert(source->context.data);\n\treturn source->info.video_get_color_space\n\t\t       ? source->info.video_get_color_space(source->context.data, count, preferred_spaces)\n\t\t       : GS_CS_SRGB;\n}\n\nuint32_t obs_source_get_base_width(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_get_base_width\"))\n\t\treturn 0;\n\n\treturn get_base_width(source);\n}\n\nuint32_t obs_source_get_base_height(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_get_base_height\"))\n\t\treturn 0;\n\n\treturn get_base_height(source);\n}\n\nobs_source_t *obs_filter_get_parent(const obs_source_t *filter)\n{\n\treturn obs_ptr_valid(filter, \"obs_filter_get_parent\") ? filter->filter_parent : NULL;\n}\n\nobs_source_t *obs_filter_get_target(const obs_source_t *filter)\n{\n\treturn obs_ptr_valid(filter, \"obs_filter_get_target\") ? filter->filter_target : NULL;\n}\n\n#define OBS_SOURCE_AV (OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO)\n\nstatic bool filter_compatible(obs_source_t *source, obs_source_t *filter)\n{\n\tuint32_t s_caps = source->info.output_flags & OBS_SOURCE_AV;\n\tuint32_t f_caps = filter->info.output_flags & OBS_SOURCE_AV;\n\n\tif ((f_caps & OBS_SOURCE_AUDIO) != 0 && (f_caps & OBS_SOURCE_VIDEO) == 0)\n\t\tf_caps &= ~OBS_SOURCE_ASYNC;\n\n\treturn (s_caps & f_caps) == f_caps;\n}\n\nvoid obs_source_filter_add(obs_source_t *source, obs_source_t *filter)\n{\n\tstruct calldata cd;\n\tuint8_t stack[128];\n\n\tif (!obs_source_valid(source, \"obs_source_filter_add\"))\n\t\treturn;\n\tif (!obs_ptr_valid(filter, \"obs_source_filter_add\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\n\tif (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {\n\t\tblog(LOG_WARNING, \"Tried to add a filter that was already \"\n\t\t\t\t  \"present on the source\");\n\t\tpthread_mutex_unlock(&source->filter_mutex);\n\t\treturn;\n\t}\n\n\tif (!source->owns_info_id && !filter_compatible(source, filter)) {\n\t\tpthread_mutex_unlock(&source->filter_mutex);\n\t\treturn;\n\t}\n\n\tfilter = obs_source_get_ref(filter);\n\tif (!obs_ptr_valid(filter, \"obs_source_filter_add\"))\n\t\treturn;\n\n\tfilter->filter_parent = source;\n\tfilter->filter_target = !source->filters.num ? source : source->filters.array[0];\n\n\tda_insert(source->filters, 0, &filter);\n\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\tcalldata_init_fixed(&cd, stack, sizeof(stack));\n\tcalldata_set_ptr(&cd, \"source\", source);\n\tcalldata_set_ptr(&cd, \"filter\", filter);\n\n\tsignal_handler_signal(obs->signals, \"source_filter_add\", &cd);\n\tsignal_handler_signal(source->context.signals, \"filter_add\", &cd);\n\n\tblog(LOG_DEBUG, \"- filter '%s' (%s) added to source '%s'\", filter->context.name, filter->info.id,\n\t     source->context.name);\n\n\tif (filter->info.filter_add)\n\t\tfilter->info.filter_add(filter->context.data, filter->filter_parent);\n}\n\nstatic bool obs_source_filter_remove_refless(obs_source_t *source, obs_source_t *filter)\n{\n\tstruct calldata cd;\n\tuint8_t stack[128];\n\tsize_t idx;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\n\tidx = da_find(source->filters, &filter, 0);\n\tif (idx == DARRAY_INVALID) {\n\t\tpthread_mutex_unlock(&source->filter_mutex);\n\t\treturn false;\n\t}\n\n\tif (idx > 0) {\n\t\tobs_source_t *prev = source->filters.array[idx - 1];\n\t\tprev->filter_target = filter->filter_target;\n\t}\n\n\tda_erase(source->filters, idx);\n\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\tcalldata_init_fixed(&cd, stack, sizeof(stack));\n\tcalldata_set_ptr(&cd, \"source\", source);\n\tcalldata_set_ptr(&cd, \"filter\", filter);\n\n\tsignal_handler_signal(obs->signals, \"source_filter_remove\", &cd);\n\tsignal_handler_signal(source->context.signals, \"filter_remove\", &cd);\n\n\tblog(LOG_DEBUG, \"- filter '%s' (%s) removed from source '%s'\", filter->context.name, filter->info.id,\n\t     source->context.name);\n\n\tif (filter->info.filter_remove)\n\t\tfilter->info.filter_remove(filter->context.data, filter->filter_parent);\n\n\tfilter->filter_parent = NULL;\n\tfilter->filter_target = NULL;\n\treturn true;\n}\n\nvoid obs_source_filter_remove(obs_source_t *source, obs_source_t *filter)\n{\n\tif (!obs_source_valid(source, \"obs_source_filter_remove\"))\n\t\treturn;\n\tif (!obs_ptr_valid(filter, \"obs_source_filter_remove\"))\n\t\treturn;\n\n\tif (obs_source_filter_remove_refless(source, filter))\n\t\tobs_source_release(filter);\n}\n\nstatic size_t find_next_filter(obs_source_t *source, obs_source_t *filter, size_t cur_idx)\n{\n\tbool curAsync = (filter->info.output_flags & OBS_SOURCE_ASYNC) != 0;\n\tbool nextAsync;\n\tobs_source_t *next;\n\n\tif (cur_idx == source->filters.num - 1)\n\t\treturn DARRAY_INVALID;\n\n\tnext = source->filters.array[cur_idx + 1];\n\tnextAsync = (next->info.output_flags & OBS_SOURCE_ASYNC);\n\n\tif (nextAsync == curAsync)\n\t\treturn cur_idx + 1;\n\telse\n\t\treturn find_next_filter(source, filter, cur_idx + 1);\n}\n\nstatic size_t find_prev_filter(obs_source_t *source, obs_source_t *filter, size_t cur_idx)\n{\n\tbool curAsync = (filter->info.output_flags & OBS_SOURCE_ASYNC) != 0;\n\tbool prevAsync;\n\tobs_source_t *prev;\n\n\tif (cur_idx == 0)\n\t\treturn DARRAY_INVALID;\n\n\tprev = source->filters.array[cur_idx - 1];\n\tprevAsync = (prev->info.output_flags & OBS_SOURCE_ASYNC);\n\n\tif (prevAsync == curAsync)\n\t\treturn cur_idx - 1;\n\telse\n\t\treturn find_prev_filter(source, filter, cur_idx - 1);\n}\n\nstatic void reorder_filter_targets(obs_source_t *source)\n{\n\t/* reorder filter targets, not the nicest way of dealing with things */\n\tfor (size_t i = 0; i < source->filters.num; i++) {\n\t\tobs_source_t *next_filter = (i == source->filters.num - 1) ? source : source->filters.array[i + 1];\n\n\t\tsource->filters.array[i]->filter_target = next_filter;\n\t}\n}\n\n/* moves filters above/below matching filter types */\nstatic bool move_filter_dir(obs_source_t *source, obs_source_t *filter, enum obs_order_movement movement)\n{\n\tsize_t idx;\n\n\tidx = da_find(source->filters, &filter, 0);\n\tif (idx == DARRAY_INVALID)\n\t\treturn false;\n\n\tif (movement == OBS_ORDER_MOVE_UP) {\n\t\tsize_t next_id = find_next_filter(source, filter, idx);\n\t\tif (next_id == DARRAY_INVALID)\n\t\t\treturn false;\n\t\tda_move_item(source->filters, idx, next_id);\n\n\t} else if (movement == OBS_ORDER_MOVE_DOWN) {\n\t\tsize_t prev_id = find_prev_filter(source, filter, idx);\n\t\tif (prev_id == DARRAY_INVALID)\n\t\t\treturn false;\n\t\tda_move_item(source->filters, idx, prev_id);\n\n\t} else if (movement == OBS_ORDER_MOVE_TOP) {\n\t\tif (idx == source->filters.num - 1)\n\t\t\treturn false;\n\t\tda_move_item(source->filters, idx, source->filters.num - 1);\n\n\t} else if (movement == OBS_ORDER_MOVE_BOTTOM) {\n\t\tif (idx == 0)\n\t\t\treturn false;\n\t\tda_move_item(source->filters, idx, 0);\n\t}\n\n\treorder_filter_targets(source);\n\n\treturn true;\n}\n\nvoid obs_source_filter_set_order(obs_source_t *source, obs_source_t *filter, enum obs_order_movement movement)\n{\n\tbool success;\n\n\tif (!obs_source_valid(source, \"obs_source_filter_set_order\"))\n\t\treturn;\n\tif (!obs_ptr_valid(filter, \"obs_source_filter_set_order\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\tsuccess = move_filter_dir(source, filter, movement);\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\tif (success)\n\t\tobs_source_dosignal(source, NULL, \"reorder_filters\");\n}\n\nint obs_source_filter_get_index(obs_source_t *source, obs_source_t *filter)\n{\n\tif (!obs_source_valid(source, \"obs_source_filter_get_index\"))\n\t\treturn -1;\n\tif (!obs_ptr_valid(filter, \"obs_source_filter_get_index\"))\n\t\treturn -1;\n\n\tsize_t idx;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\tidx = da_find(source->filters, &filter, 0);\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\treturn idx != DARRAY_INVALID ? (int)idx : -1;\n}\n\nstatic bool set_filter_index(obs_source_t *source, obs_source_t *filter, size_t index)\n{\n\tsize_t idx = da_find(source->filters, &filter, 0);\n\tif (idx == DARRAY_INVALID)\n\t\treturn false;\n\n\tda_move_item(source->filters, idx, index);\n\treorder_filter_targets(source);\n\n\treturn true;\n}\n\nvoid obs_source_filter_set_index(obs_source_t *source, obs_source_t *filter, size_t index)\n{\n\tbool success;\n\n\tif (!obs_source_valid(source, \"obs_source_filter_set_index\"))\n\t\treturn;\n\tif (!obs_ptr_valid(filter, \"obs_source_filter_set_index\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\tsuccess = set_filter_index(source, filter, index);\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\tif (success)\n\t\tobs_source_dosignal(source, NULL, \"reorder_filters\");\n}\n\nobs_data_t *obs_source_get_settings(const obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_get_settings\"))\n\t\treturn NULL;\n\n\tobs_data_addref(source->context.settings);\n\treturn source->context.settings;\n}\n\nstruct obs_source_frame *filter_async_video(obs_source_t *source, struct obs_source_frame *in)\n{\n\tsize_t i;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\n\tfor (i = source->filters.num; i > 0; i--) {\n\t\tstruct obs_source *filter = source->filters.array[i - 1];\n\n\t\tif (!filter->enabled)\n\t\t\tcontinue;\n\n\t\tif (filter->context.data && filter->info.filter_video) {\n\t\t\tin = filter->info.filter_video(filter->context.data, in);\n\t\t\tif (!in)\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\treturn in;\n}\n\nstatic inline void copy_frame_data_line(struct obs_source_frame *dst, const struct obs_source_frame *src,\n\t\t\t\t\tuint32_t plane, uint32_t y)\n{\n\tuint32_t pos_src = y * src->linesize[plane];\n\tuint32_t pos_dst = y * dst->linesize[plane];\n\tuint32_t bytes = dst->linesize[plane] < src->linesize[plane] ? dst->linesize[plane] : src->linesize[plane];\n\n\tmemcpy(dst->data[plane] + pos_dst, src->data[plane] + pos_src, bytes);\n}\n\nstatic inline void copy_frame_data_plane(struct obs_source_frame *dst, const struct obs_source_frame *src,\n\t\t\t\t\t uint32_t plane, uint32_t lines)\n{\n\tif (dst->linesize[plane] != src->linesize[plane]) {\n\t\tfor (uint32_t y = 0; y < lines; y++)\n\t\t\tcopy_frame_data_line(dst, src, plane, y);\n\t} else {\n\t\tmemcpy(dst->data[plane], src->data[plane], (size_t)dst->linesize[plane] * (size_t)lines);\n\t}\n}\n\nstatic void copy_frame_data(struct obs_source_frame *dst, const struct obs_source_frame *src)\n{\n\tdst->flip = src->flip;\n\tdst->flags = src->flags;\n\tdst->trc = src->trc;\n\tdst->full_range = src->full_range;\n\tdst->max_luminance = src->max_luminance;\n\tdst->timestamp = src->timestamp;\n\tmemcpy(dst->color_matrix, src->color_matrix, sizeof(float) * 16);\n\tif (!dst->full_range) {\n\t\tsize_t const size = sizeof(float) * 3;\n\t\tmemcpy(dst->color_range_min, src->color_range_min, size);\n\t\tmemcpy(dst->color_range_max, src->color_range_max, size);\n\t}\n\n\tswitch (src->format) {\n\tcase VIDEO_FORMAT_I420:\n\tcase VIDEO_FORMAT_I010: {\n\t\tconst uint32_t height = dst->height;\n\t\tconst uint32_t half_height = (height + 1) / 2;\n\t\tcopy_frame_data_plane(dst, src, 0, height);\n\t\tcopy_frame_data_plane(dst, src, 1, half_height);\n\t\tcopy_frame_data_plane(dst, src, 2, half_height);\n\t\tbreak;\n\t}\n\n\tcase VIDEO_FORMAT_NV12:\n\tcase VIDEO_FORMAT_P010: {\n\t\tconst uint32_t height = dst->height;\n\t\tconst uint32_t half_height = (height + 1) / 2;\n\t\tcopy_frame_data_plane(dst, src, 0, height);\n\t\tcopy_frame_data_plane(dst, src, 1, half_height);\n\t\tbreak;\n\t}\n\n\tcase VIDEO_FORMAT_I444:\n\tcase VIDEO_FORMAT_I422:\n\tcase VIDEO_FORMAT_I210:\n\tcase VIDEO_FORMAT_I412:\n\t\tcopy_frame_data_plane(dst, src, 0, dst->height);\n\t\tcopy_frame_data_plane(dst, src, 1, dst->height);\n\t\tcopy_frame_data_plane(dst, src, 2, dst->height);\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_YVYU:\n\tcase VIDEO_FORMAT_YUY2:\n\tcase VIDEO_FORMAT_UYVY:\n\tcase VIDEO_FORMAT_NONE:\n\tcase VIDEO_FORMAT_RGBA:\n\tcase VIDEO_FORMAT_BGRA:\n\tcase VIDEO_FORMAT_BGRX:\n\tcase VIDEO_FORMAT_Y800:\n\tcase VIDEO_FORMAT_BGR3:\n\tcase VIDEO_FORMAT_AYUV:\n\tcase VIDEO_FORMAT_V210:\n\tcase VIDEO_FORMAT_R10L:\n\t\tcopy_frame_data_plane(dst, src, 0, dst->height);\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_I40A: {\n\t\tconst uint32_t height = dst->height;\n\t\tconst uint32_t half_height = (height + 1) / 2;\n\t\tcopy_frame_data_plane(dst, src, 0, height);\n\t\tcopy_frame_data_plane(dst, src, 1, half_height);\n\t\tcopy_frame_data_plane(dst, src, 2, half_height);\n\t\tcopy_frame_data_plane(dst, src, 3, height);\n\t\tbreak;\n\t}\n\n\tcase VIDEO_FORMAT_I42A:\n\tcase VIDEO_FORMAT_YUVA:\n\tcase VIDEO_FORMAT_YA2L:\n\t\tcopy_frame_data_plane(dst, src, 0, dst->height);\n\t\tcopy_frame_data_plane(dst, src, 1, dst->height);\n\t\tcopy_frame_data_plane(dst, src, 2, dst->height);\n\t\tcopy_frame_data_plane(dst, src, 3, dst->height);\n\t\tbreak;\n\n\tcase VIDEO_FORMAT_P216:\n\tcase VIDEO_FORMAT_P416:\n\t\t/* Unimplemented */\n\t\tbreak;\n\t}\n}\n\nvoid obs_source_frame_copy(struct obs_source_frame *dst, const struct obs_source_frame *src)\n{\n\tcopy_frame_data(dst, src);\n}\n\nstatic inline bool async_texture_changed(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tenum convert_type prev, cur;\n\tprev = get_convert_type(source->async_cache_format, source->async_cache_full_range, source->async_cache_trc);\n\tcur = get_convert_type(frame->format, frame->full_range, frame->trc);\n\n\treturn source->async_cache_width != frame->width || source->async_cache_height != frame->height || prev != cur;\n}\n\nstatic inline void free_async_cache(struct obs_source *source)\n{\n\tfor (size_t i = 0; i < source->async_cache.num; i++)\n\t\tobs_source_frame_decref(source->async_cache.array[i].frame);\n\n\tda_resize(source->async_cache, 0);\n\tda_resize(source->async_frames, 0);\n\tsource->cur_async_frame = NULL;\n\tsource->prev_async_frame = NULL;\n}\n\n#define MAX_UNUSED_FRAME_DURATION 5\n\n/* frees frame allocations if they haven't been used for a specific period\n * of time */\nstatic void clean_cache(obs_source_t *source)\n{\n\tfor (size_t i = source->async_cache.num; i > 0; i--) {\n\t\tstruct async_frame *af = &source->async_cache.array[i - 1];\n\t\tif (!af->used) {\n\t\t\tif (++af->unused_count == MAX_UNUSED_FRAME_DURATION) {\n\t\t\t\tobs_source_frame_destroy(af->frame);\n\t\t\t\tda_erase(source->async_cache, i - 1);\n\t\t\t}\n\t\t}\n\t}\n}\n\n#define MAX_ASYNC_FRAMES 30\n//if return value is not null then do (os_atomic_dec_long(&output->refs) == 0) && obs_source_frame_destroy(output)\nstatic inline struct obs_source_frame *cache_video(struct obs_source *source, const struct obs_source_frame *frame)\n{\n\tstruct obs_source_frame *new_frame = NULL;\n\n\tpthread_mutex_lock(&source->async_mutex);\n\n\tif (source->async_frames.num >= MAX_ASYNC_FRAMES) {\n\t\tfree_async_cache(source);\n\t\tsource->last_frame_ts = 0;\n\t\tpthread_mutex_unlock(&source->async_mutex);\n\t\treturn NULL;\n\t}\n\n\tif (async_texture_changed(source, frame)) {\n\t\tfree_async_cache(source);\n\t\tsource->async_cache_width = frame->width;\n\t\tsource->async_cache_height = frame->height;\n\t}\n\n\tconst enum video_format format = frame->format;\n\tsource->async_cache_format = format;\n\tsource->async_cache_full_range = frame->full_range;\n\tsource->async_cache_trc = frame->trc;\n\n\tfor (size_t i = 0; i < source->async_cache.num; i++) {\n\t\tstruct async_frame *af = &source->async_cache.array[i];\n\t\tif (!af->used) {\n\t\t\tnew_frame = af->frame;\n\t\t\tnew_frame->format = format;\n\t\t\taf->used = true;\n\t\t\taf->unused_count = 0;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tclean_cache(source);\n\n\tif (!new_frame) {\n\t\tstruct async_frame new_af;\n\n\t\tnew_frame = obs_source_frame_create(format, frame->width, frame->height);\n\t\tnew_af.frame = new_frame;\n\t\tnew_af.used = true;\n\t\tnew_af.unused_count = 0;\n\t\tnew_frame->refs = 1;\n\n\t\tda_push_back(source->async_cache, &new_af);\n\t}\n\n\tos_atomic_inc_long(&new_frame->refs);\n\n\tpthread_mutex_unlock(&source->async_mutex);\n\n\tcopy_frame_data(new_frame, frame);\n\n\treturn new_frame;\n}\n\nstatic void obs_source_output_video_internal(obs_source_t *source, const struct obs_source_frame *frame)\n{\n\tif (!obs_source_valid(source, \"obs_source_output_video\"))\n\t\treturn;\n\n\tif (!frame) {\n\t\tpthread_mutex_lock(&source->async_mutex);\n\t\tsource->async_active = false;\n\t\tsource->last_frame_ts = 0;\n\t\tfree_async_cache(source);\n\t\tpthread_mutex_unlock(&source->async_mutex);\n\t\treturn;\n\t}\n\n\tsource_profiler_async_frame_received(source);\n\n\tstruct obs_source_frame *output = cache_video(source, frame);\n\n\t/* ------------------------------------------- */\n\tpthread_mutex_lock(&source->async_mutex);\n\tif (output) {\n\t\tif (os_atomic_dec_long(&output->refs) == 0) {\n\t\t\tobs_source_frame_destroy(output);\n\t\t\toutput = NULL;\n\t\t} else {\n\t\t\tda_push_back(source->async_frames, &output);\n\t\t\tsource->async_active = true;\n\t\t}\n\t}\n\tpthread_mutex_unlock(&source->async_mutex);\n}\n\nvoid obs_source_output_video(obs_source_t *source, const struct obs_source_frame *frame)\n{\n\tif (destroying(source))\n\t\treturn;\n\tif (!frame) {\n\t\tobs_source_output_video_internal(source, NULL);\n\t\treturn;\n\t}\n\n\tstruct obs_source_frame new_frame = *frame;\n\tnew_frame.full_range = format_is_yuv(frame->format) ? new_frame.full_range : true;\n\n\tobs_source_output_video_internal(source, &new_frame);\n}\n\nvoid obs_source_output_video2(obs_source_t *source, const struct obs_source_frame2 *frame)\n{\n\tif (destroying(source))\n\t\treturn;\n\tif (!frame) {\n\t\tobs_source_output_video_internal(source, NULL);\n\t\treturn;\n\t}\n\n\tstruct obs_source_frame new_frame = {0};\n\tenum video_range_type range = resolve_video_range(frame->format, frame->range);\n\n\tfor (size_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\tnew_frame.data[i] = frame->data[i];\n\t\tnew_frame.linesize[i] = frame->linesize[i];\n\t}\n\n\tnew_frame.width = frame->width;\n\tnew_frame.height = frame->height;\n\tnew_frame.timestamp = frame->timestamp;\n\tnew_frame.format = frame->format;\n\tnew_frame.full_range = range == VIDEO_RANGE_FULL;\n\tnew_frame.max_luminance = 0;\n\tnew_frame.flip = frame->flip;\n\tnew_frame.flags = frame->flags;\n\tnew_frame.trc = frame->trc;\n\n\tmemcpy(&new_frame.color_matrix, &frame->color_matrix, sizeof(frame->color_matrix));\n\tmemcpy(&new_frame.color_range_min, &frame->color_range_min, sizeof(frame->color_range_min));\n\tmemcpy(&new_frame.color_range_max, &frame->color_range_max, sizeof(frame->color_range_max));\n\n\tobs_source_output_video_internal(source, &new_frame);\n}\n\nvoid obs_source_set_async_rotation(obs_source_t *source, long rotation)\n{\n\tif (source)\n\t\tsource->async_rotation = rotation;\n}\n\nvoid obs_source_output_cea708(obs_source_t *source, const struct obs_source_cea_708 *captions)\n{\n\tif (destroying(source))\n\t\treturn;\n\tif (!captions) {\n\t\treturn;\n\t}\n\n\tpthread_mutex_lock(&source->caption_cb_mutex);\n\n\tfor (size_t i = source->caption_cb_list.num; i > 0; i--) {\n\t\tstruct caption_cb_info info = source->caption_cb_list.array[i - 1];\n\t\tinfo.callback(info.param, source, captions);\n\t}\n\n\tpthread_mutex_unlock(&source->caption_cb_mutex);\n}\n\nvoid obs_source_add_caption_callback(obs_source_t *source, obs_source_caption_t callback, void *param)\n{\n\tstruct caption_cb_info info = {callback, param};\n\n\tif (!obs_source_valid(source, \"obs_source_add_caption_callback\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->caption_cb_mutex);\n\tda_push_back(source->caption_cb_list, &info);\n\tpthread_mutex_unlock(&source->caption_cb_mutex);\n}\n\nvoid obs_source_remove_caption_callback(obs_source_t *source, obs_source_caption_t callback, void *param)\n{\n\tstruct caption_cb_info info = {callback, param};\n\n\tif (!obs_source_valid(source, \"obs_source_remove_caption_callback\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->caption_cb_mutex);\n\tda_erase_item(source->caption_cb_list, &info);\n\tpthread_mutex_unlock(&source->caption_cb_mutex);\n}\n\nstatic inline bool preload_frame_changed(obs_source_t *source, const struct obs_source_frame *in)\n{\n\tif (!source->async_preload_frame)\n\t\treturn true;\n\n\treturn in->width != source->async_preload_frame->width || in->height != source->async_preload_frame->height ||\n\t       in->format != source->async_preload_frame->format;\n}\n\nstatic void obs_source_preload_video_internal(obs_source_t *source, const struct obs_source_frame *frame)\n{\n\tif (!obs_source_valid(source, \"obs_source_preload_video\"))\n\t\treturn;\n\tif (destroying(source))\n\t\treturn;\n\tif (!frame)\n\t\treturn;\n\n\tif (preload_frame_changed(source, frame)) {\n\t\tobs_source_frame_destroy(source->async_preload_frame);\n\t\tsource->async_preload_frame = obs_source_frame_create(frame->format, frame->width, frame->height);\n\t}\n\n\tcopy_frame_data(source->async_preload_frame, frame);\n\n\tsource->last_frame_ts = frame->timestamp;\n}\n\nvoid obs_source_preload_video(obs_source_t *source, const struct obs_source_frame *frame)\n{\n\tif (destroying(source))\n\t\treturn;\n\tif (!frame) {\n\t\tobs_source_preload_video_internal(source, NULL);\n\t\treturn;\n\t}\n\n\tstruct obs_source_frame new_frame = *frame;\n\tnew_frame.full_range = format_is_yuv(frame->format) ? new_frame.full_range : true;\n\n\tobs_source_preload_video_internal(source, &new_frame);\n}\n\nvoid obs_source_preload_video2(obs_source_t *source, const struct obs_source_frame2 *frame)\n{\n\tif (destroying(source))\n\t\treturn;\n\tif (!frame) {\n\t\tobs_source_preload_video_internal(source, NULL);\n\t\treturn;\n\t}\n\n\tstruct obs_source_frame new_frame = {0};\n\tenum video_range_type range = resolve_video_range(frame->format, frame->range);\n\n\tfor (size_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\tnew_frame.data[i] = frame->data[i];\n\t\tnew_frame.linesize[i] = frame->linesize[i];\n\t}\n\n\tnew_frame.width = frame->width;\n\tnew_frame.height = frame->height;\n\tnew_frame.timestamp = frame->timestamp;\n\tnew_frame.format = frame->format;\n\tnew_frame.full_range = range == VIDEO_RANGE_FULL;\n\tnew_frame.max_luminance = 0;\n\tnew_frame.flip = frame->flip;\n\tnew_frame.flags = frame->flags;\n\tnew_frame.trc = frame->trc;\n\n\tmemcpy(&new_frame.color_matrix, &frame->color_matrix, sizeof(frame->color_matrix));\n\tmemcpy(&new_frame.color_range_min, &frame->color_range_min, sizeof(frame->color_range_min));\n\tmemcpy(&new_frame.color_range_max, &frame->color_range_max, sizeof(frame->color_range_max));\n\n\tobs_source_preload_video_internal(source, &new_frame);\n}\n\nvoid obs_source_show_preloaded_video(obs_source_t *source)\n{\n\tuint64_t sys_ts;\n\n\tif (!obs_source_valid(source, \"obs_source_show_preloaded_video\"))\n\t\treturn;\n\tif (destroying(source))\n\t\treturn;\n\tif (!source->async_preload_frame)\n\t\treturn;\n\n\tobs_enter_graphics();\n\n\tset_async_texture_size(source, source->async_preload_frame);\n\tupdate_async_textures(source, source->async_preload_frame, source->async_textures, source->async_texrender);\n\tsource->async_active = true;\n\n\tobs_leave_graphics();\n\n\tpthread_mutex_lock(&source->audio_buf_mutex);\n\tsys_ts = (source->monitoring_type != OBS_MONITORING_TYPE_MONITOR_ONLY) ? os_gettime_ns() : 0;\n\treset_audio_timing(source, source->last_frame_ts, sys_ts);\n\treset_audio_data(source, sys_ts);\n\tpthread_mutex_unlock(&source->audio_buf_mutex);\n}\n\nstatic void obs_source_set_video_frame_internal(obs_source_t *source, const struct obs_source_frame *frame)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_video_frame\"))\n\t\treturn;\n\tif (!frame)\n\t\treturn;\n\n\tobs_enter_graphics();\n\n\tif (preload_frame_changed(source, frame)) {\n\t\tobs_source_frame_destroy(source->async_preload_frame);\n\t\tsource->async_preload_frame = obs_source_frame_create(frame->format, frame->width, frame->height);\n\t}\n\n\tcopy_frame_data(source->async_preload_frame, frame);\n\tset_async_texture_size(source, source->async_preload_frame);\n\tupdate_async_textures(source, source->async_preload_frame, source->async_textures, source->async_texrender);\n\n\tsource->last_frame_ts = frame->timestamp;\n\n\tobs_leave_graphics();\n}\n\nvoid obs_source_set_video_frame(obs_source_t *source, const struct obs_source_frame *frame)\n{\n\tif (destroying(source))\n\t\treturn;\n\tif (!frame) {\n\t\tobs_source_preload_video_internal(source, NULL);\n\t\treturn;\n\t}\n\n\tstruct obs_source_frame new_frame = *frame;\n\tnew_frame.full_range = format_is_yuv(frame->format) ? new_frame.full_range : true;\n\n\tobs_source_set_video_frame_internal(source, &new_frame);\n}\n\nvoid obs_source_set_video_frame2(obs_source_t *source, const struct obs_source_frame2 *frame)\n{\n\tif (destroying(source))\n\t\treturn;\n\tif (!frame) {\n\t\tobs_source_preload_video_internal(source, NULL);\n\t\treturn;\n\t}\n\n\tstruct obs_source_frame new_frame = {0};\n\tenum video_range_type range = resolve_video_range(frame->format, frame->range);\n\n\tfor (size_t i = 0; i < MAX_AV_PLANES; i++) {\n\t\tnew_frame.data[i] = frame->data[i];\n\t\tnew_frame.linesize[i] = frame->linesize[i];\n\t}\n\n\tnew_frame.width = frame->width;\n\tnew_frame.height = frame->height;\n\tnew_frame.timestamp = frame->timestamp;\n\tnew_frame.format = frame->format;\n\tnew_frame.full_range = range == VIDEO_RANGE_FULL;\n\tnew_frame.max_luminance = 0;\n\tnew_frame.flip = frame->flip;\n\tnew_frame.flags = frame->flags;\n\tnew_frame.trc = frame->trc;\n\n\tmemcpy(&new_frame.color_matrix, &frame->color_matrix, sizeof(frame->color_matrix));\n\tmemcpy(&new_frame.color_range_min, &frame->color_range_min, sizeof(frame->color_range_min));\n\tmemcpy(&new_frame.color_range_max, &frame->color_range_max, sizeof(frame->color_range_max));\n\n\tobs_source_set_video_frame_internal(source, &new_frame);\n}\n\nstatic inline struct obs_audio_data *filter_async_audio(obs_source_t *source, struct obs_audio_data *in)\n{\n\tsize_t i;\n\tfor (i = source->filters.num; i > 0; i--) {\n\t\tstruct obs_source *filter = source->filters.array[i - 1];\n\n\t\tif (!filter->enabled)\n\t\t\tcontinue;\n\n\t\tif (filter->context.data && filter->info.filter_audio) {\n\t\t\tin = filter->info.filter_audio(filter->context.data, in);\n\t\t\tif (!in)\n\t\t\t\treturn NULL;\n\t\t}\n\t}\n\n\treturn in;\n}\n\nstatic inline void reset_resampler(obs_source_t *source, const struct obs_source_audio *audio)\n{\n\tconst struct audio_output_info *obs_info;\n\tstruct resample_info output_info;\n\n\tobs_info = audio_output_get_info(obs->audio.audio);\n\n\toutput_info.format = obs_info->format;\n\toutput_info.samples_per_sec = obs_info->samples_per_sec;\n\toutput_info.speakers = obs_info->speakers;\n\n\tsource->sample_info.format = audio->format;\n\tsource->sample_info.samples_per_sec = audio->samples_per_sec;\n\tsource->sample_info.speakers = audio->speakers;\n\n\taudio_resampler_destroy(source->resampler);\n\tsource->resampler = NULL;\n\tsource->resample_offset = 0;\n\n\tif (source->sample_info.samples_per_sec == obs_info->samples_per_sec &&\n\t    source->sample_info.format == obs_info->format && source->sample_info.speakers == obs_info->speakers) {\n\t\tsource->audio_failed = false;\n\t\treturn;\n\t}\n\n\tsource->resampler = audio_resampler_create(&output_info, &source->sample_info);\n\n\tsource->audio_failed = source->resampler == NULL;\n\tif (source->resampler == NULL)\n\t\tblog(LOG_ERROR, \"creation of resampler failed\");\n}\n\nstatic void copy_audio_data(obs_source_t *source, const uint8_t *const data[], uint32_t frames, uint64_t ts)\n{\n\tsize_t planes = audio_output_get_planes(obs->audio.audio);\n\tsize_t blocksize = audio_output_get_block_size(obs->audio.audio);\n\tsize_t size = (size_t)frames * blocksize;\n\tbool resize = source->audio_storage_size < size;\n\n\tsource->audio_data.frames = frames;\n\tsource->audio_data.timestamp = ts;\n\n\tfor (size_t i = 0; i < planes; i++) {\n\t\t/* ensure audio storage capacity */\n\t\tif (resize) {\n\t\t\tbfree(source->audio_data.data[i]);\n\t\t\tsource->audio_data.data[i] = bmalloc(size);\n\t\t}\n\n\t\tmemcpy(source->audio_data.data[i], data[i], size);\n\t}\n\n\tif (resize)\n\t\tsource->audio_storage_size = size;\n}\n\n/* TODO: SSE optimization */\nstatic void downmix_to_mono_planar(struct obs_source *source, uint32_t frames)\n{\n\tsize_t channels = audio_output_get_channels(obs->audio.audio);\n\tconst float channels_i = 1.0f / (float)channels;\n\tfloat **data = (float **)source->audio_data.data;\n\n\tfor (size_t channel = 1; channel < channels; channel++) {\n\t\tfor (uint32_t frame = 0; frame < frames; frame++)\n\t\t\tdata[0][frame] += data[channel][frame];\n\t}\n\n\tfor (uint32_t frame = 0; frame < frames; frame++)\n\t\tdata[0][frame] *= channels_i;\n\n\tfor (size_t channel = 1; channel < channels; channel++) {\n\t\tfor (uint32_t frame = 0; frame < frames; frame++)\n\t\t\tdata[channel][frame] = data[0][frame];\n\t}\n}\n\nstatic void process_audio_balancing(struct obs_source *source, uint32_t frames, float balance,\n\t\t\t\t    enum obs_balance_type type)\n{\n\tfloat **data = (float **)source->audio_data.data;\n\n\tswitch (type) {\n\tcase OBS_BALANCE_TYPE_SINE_LAW:\n\t\tfor (uint32_t frame = 0; frame < frames; frame++) {\n\t\t\tdata[0][frame] = data[0][frame] * sinf((1.0f - balance) * (M_PI / 2.0f));\n\t\t\tdata[1][frame] = data[1][frame] * sinf(balance * (M_PI / 2.0f));\n\t\t}\n\t\tbreak;\n\tcase OBS_BALANCE_TYPE_SQUARE_LAW:\n\t\tfor (uint32_t frame = 0; frame < frames; frame++) {\n\t\t\tdata[0][frame] = data[0][frame] * sqrtf(1.0f - balance);\n\t\t\tdata[1][frame] = data[1][frame] * sqrtf(balance);\n\t\t}\n\t\tbreak;\n\tcase OBS_BALANCE_TYPE_LINEAR:\n\t\tfor (uint32_t frame = 0; frame < frames; frame++) {\n\t\t\tdata[0][frame] = data[0][frame] * (1.0f - balance);\n\t\t\tdata[1][frame] = data[1][frame] * balance;\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\n/* resamples/remixes new audio to the designated main audio output format */\nstatic void process_audio(obs_source_t *source, const struct obs_source_audio *audio)\n{\n\tuint32_t frames = audio->frames;\n\tbool mono_output;\n\n\tif (source->sample_info.samples_per_sec != audio->samples_per_sec ||\n\t    source->sample_info.format != audio->format || source->sample_info.speakers != audio->speakers)\n\t\treset_resampler(source, audio);\n\n\tif (source->audio_failed)\n\t\treturn;\n\n\tif (source->resampler) {\n\t\tuint8_t *output[MAX_AV_PLANES];\n\n\t\tmemset(output, 0, sizeof(output));\n\n\t\taudio_resampler_resample(source->resampler, output, &frames, &source->resample_offset, audio->data,\n\t\t\t\t\t audio->frames);\n\n\t\tcopy_audio_data(source, (const uint8_t *const *)output, frames, audio->timestamp);\n\t} else {\n\t\tcopy_audio_data(source, audio->data, audio->frames, audio->timestamp);\n\t}\n\n\tmono_output = audio_output_get_channels(obs->audio.audio) == 1;\n\n\tif (!mono_output && source->sample_info.speakers == SPEAKERS_STEREO &&\n\t    (source->balance > 0.51f || source->balance < 0.49f)) {\n\t\tprocess_audio_balancing(source, frames, source->balance, OBS_BALANCE_TYPE_SINE_LAW);\n\t}\n\n\tif (!mono_output && (source->flags & OBS_SOURCE_FLAG_FORCE_MONO) != 0)\n\t\tdownmix_to_mono_planar(source, frames);\n}\n\nvoid obs_source_output_audio(obs_source_t *source, const struct obs_source_audio *audio_in)\n{\n\tstruct obs_audio_data *output;\n\n\tif (!obs_source_valid(source, \"obs_source_output_audio\"))\n\t\treturn;\n\tif (destroying(source))\n\t\treturn;\n\tif (!obs_ptr_valid(audio_in, \"obs_source_output_audio\"))\n\t\treturn;\n\n\t/* sets unused data pointers to NULL automatically because apparently\n\t * some filter plugins aren't checking the actual channel count, and\n\t * instead are checking to see whether the pointer is non-zero. */\n\tstruct obs_source_audio audio = *audio_in;\n\tsize_t channels = get_audio_planes(audio.format, audio.speakers);\n\tfor (size_t i = channels; i < MAX_AUDIO_CHANNELS; i++)\n\t\taudio.data[i] = NULL;\n\n\tprocess_audio(source, &audio);\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\toutput = filter_async_audio(source, &source->audio_data);\n\n\tif (output) {\n\t\tstruct audio_data data;\n\n\t\tfor (int i = 0; i < MAX_AV_PLANES; i++)\n\t\t\tdata.data[i] = output->data[i];\n\n\t\tdata.frames = output->frames;\n\t\tdata.timestamp = output->timestamp;\n\n\t\tpthread_mutex_lock(&source->audio_mutex);\n\t\tsource_output_audio_data(source, &data);\n\t\tpthread_mutex_unlock(&source->audio_mutex);\n\t}\n\n\tpthread_mutex_unlock(&source->filter_mutex);\n}\n\nvoid remove_async_frame(obs_source_t *source, struct obs_source_frame *frame)\n{\n\tif (frame)\n\t\tframe->prev_frame = false;\n\n\tfor (size_t i = 0; i < source->async_cache.num; i++) {\n\t\tstruct async_frame *f = &source->async_cache.array[i];\n\n\t\tif (f->frame == frame) {\n\t\t\tf->used = false;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\n/* #define DEBUG_ASYNC_FRAMES 1 */\n\nstatic bool ready_async_frame(obs_source_t *source, uint64_t sys_time)\n{\n\tstruct obs_source_frame *next_frame = source->async_frames.array[0];\n\tstruct obs_source_frame *frame = NULL;\n\tuint64_t sys_offset = sys_time - source->last_sys_timestamp;\n\tuint64_t frame_time = next_frame->timestamp;\n\tuint64_t frame_offset = 0;\n\n\tif (source->async_unbuffered) {\n\t\twhile (source->async_frames.num > 1) {\n\t\t\tda_erase(source->async_frames, 0);\n\t\t\tremove_async_frame(source, next_frame);\n\t\t\tnext_frame = source->async_frames.array[0];\n\t\t}\n\n\t\tsource->last_frame_ts = next_frame->timestamp;\n\t\treturn true;\n\t}\n\n#if DEBUG_ASYNC_FRAMES\n\tblog(LOG_DEBUG,\n\t     \"source->last_frame_ts: %llu, frame_time: %llu, \"\n\t     \"sys_offset: %llu, frame_offset: %llu, \"\n\t     \"number of frames: %lu\",\n\t     source->last_frame_ts, frame_time, sys_offset, frame_time - source->last_frame_ts,\n\t     (unsigned long)source->async_frames.num);\n#endif\n\n\t/* account for timestamp invalidation */\n\tif (frame_out_of_bounds(source, frame_time)) {\n#if DEBUG_ASYNC_FRAMES\n\t\tblog(LOG_DEBUG, \"timing jump\");\n#endif\n\t\tsource->last_frame_ts = next_frame->timestamp;\n\t\treturn true;\n\t} else {\n\t\tframe_offset = frame_time - source->last_frame_ts;\n\t\tsource->last_frame_ts += sys_offset;\n\t}\n\n\twhile (source->last_frame_ts > next_frame->timestamp) {\n\n\t\t/* this tries to reduce the needless frame duplication, also\n\t\t * helps smooth out async rendering to frame boundaries.  In\n\t\t * other words, tries to keep the framerate as smooth as\n\t\t * possible */\n\t\tif (frame && (source->last_frame_ts - next_frame->timestamp) < 2000000)\n\t\t\tbreak;\n\n\t\tif (frame)\n\t\t\tda_erase(source->async_frames, 0);\n\n#if DEBUG_ASYNC_FRAMES\n\t\tblog(LOG_DEBUG,\n\t\t     \"new frame, \"\n\t\t     \"source->last_frame_ts: %llu, \"\n\t\t     \"next_frame->timestamp: %llu\",\n\t\t     source->last_frame_ts, next_frame->timestamp);\n#endif\n\n\t\tremove_async_frame(source, frame);\n\n\t\tif (source->async_frames.num == 1)\n\t\t\treturn true;\n\n\t\tframe = next_frame;\n\t\tnext_frame = source->async_frames.array[1];\n\n\t\t/* more timestamp checking and compensating */\n\t\tif ((next_frame->timestamp - frame_time) > MAX_TS_VAR) {\n#if DEBUG_ASYNC_FRAMES\n\t\t\tblog(LOG_DEBUG, \"timing jump\");\n#endif\n\t\t\tsource->last_frame_ts = next_frame->timestamp - frame_offset;\n\t\t}\n\n\t\tframe_time = next_frame->timestamp;\n\t\tframe_offset = frame_time - source->last_frame_ts;\n\t}\n\n#if DEBUG_ASYNC_FRAMES\n\tif (!frame)\n\t\tblog(LOG_DEBUG, \"no frame!\");\n#endif\n\n\treturn frame != NULL;\n}\n\nstatic inline struct obs_source_frame *get_closest_frame(obs_source_t *source, uint64_t sys_time)\n{\n\tif (!source->async_frames.num)\n\t\treturn NULL;\n\n\tif (!source->last_frame_ts || ready_async_frame(source, sys_time)) {\n\t\tstruct obs_source_frame *frame = source->async_frames.array[0];\n\t\tda_erase(source->async_frames, 0);\n\n\t\tif (!source->last_frame_ts)\n\t\t\tsource->last_frame_ts = frame->timestamp;\n\n\t\treturn frame;\n\t}\n\n\treturn NULL;\n}\n\n/*\n * Ensures that cached frames are displayed on time.  If multiple frames\n * were cached between renders, then releases the unnecessary frames and uses\n * the frame with the closest timing to ensure sync.  Also ensures that timing\n * with audio is synchronized.\n */\nstruct obs_source_frame *obs_source_get_frame(obs_source_t *source)\n{\n\tstruct obs_source_frame *frame = NULL;\n\n\tif (!obs_source_valid(source, \"obs_source_get_frame\"))\n\t\treturn NULL;\n\n\tpthread_mutex_lock(&source->async_mutex);\n\n\tframe = source->cur_async_frame;\n\tsource->cur_async_frame = NULL;\n\n\tif (frame) {\n\t\tos_atomic_inc_long(&frame->refs);\n\t}\n\n\tpthread_mutex_unlock(&source->async_mutex);\n\n\treturn frame;\n}\n\nvoid obs_source_release_frame(obs_source_t *source, struct obs_source_frame *frame)\n{\n\tif (!frame)\n\t\treturn;\n\n\tif (!source) {\n\t\tobs_source_frame_destroy(frame);\n\t} else {\n\t\tpthread_mutex_lock(&source->async_mutex);\n\n\t\tif (os_atomic_dec_long(&frame->refs) == 0)\n\t\t\tobs_source_frame_destroy(frame);\n\t\telse\n\t\t\tremove_async_frame(source, frame);\n\n\t\tpthread_mutex_unlock(&source->async_mutex);\n\t}\n}\n\nconst char *obs_source_get_name(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_name\") ? source->context.name : NULL;\n}\n\nconst char *obs_source_get_uuid(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_uuid\") ? source->context.uuid : NULL;\n}\n\nvoid obs_source_set_name(obs_source_t *source, const char *name)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_name\"))\n\t\treturn;\n\n\tif (!name || !*name || !source->context.name || strcmp(name, source->context.name) != 0) {\n\t\tif (requires_canvas(source)) {\n\t\t\tobs_canvas_rename_source(source, name);\n\t\t} else {\n\t\t\tstruct calldata data;\n\t\t\tchar *prev_name = bstrdup(source->context.name);\n\n\t\t\tif (!source->context.private) {\n\t\t\t\tobs_context_data_setname_ht(&source->context, name, &obs->data.public_sources);\n\t\t\t} else {\n\t\t\t\tobs_context_data_setname(&source->context, name);\n\t\t\t}\n\n\t\t\tcalldata_init(&data);\n\t\t\tcalldata_set_ptr(&data, \"source\", source);\n\t\t\tcalldata_set_string(&data, \"new_name\", source->context.name);\n\t\t\tcalldata_set_string(&data, \"prev_name\", prev_name);\n\t\t\tif (!source->context.private)\n\t\t\t\tsignal_handler_signal(obs->signals, \"source_rename\", &data);\n\t\t\tsignal_handler_signal(source->context.signals, \"rename\", &data);\n\t\t\tcalldata_free(&data);\n\t\t\tbfree(prev_name);\n\t\t}\n\t}\n}\n\nenum obs_source_type obs_source_get_type(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_type\") ? source->info.type : OBS_SOURCE_TYPE_INPUT;\n}\n\nconst char *obs_source_get_id(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_id\") ? source->info.id : NULL;\n}\n\nconst char *obs_source_get_unversioned_id(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_unversioned_id\") ? source->info.unversioned_id : NULL;\n}\n\nstatic inline void render_filter_bypass(obs_source_t *target, gs_effect_t *effect, const char *tech_name)\n{\n\tgs_technique_t *tech = gs_effect_get_technique(effect, tech_name);\n\tsize_t passes, i;\n\n\tpasses = gs_technique_begin(tech);\n\tfor (i = 0; i < passes; i++) {\n\t\tgs_technique_begin_pass(tech, i);\n\t\tobs_source_video_render(target);\n\t\tgs_technique_end_pass(tech);\n\t}\n\tgs_technique_end(tech);\n}\n\nstatic inline void render_filter_tex(gs_texture_t *tex, gs_effect_t *effect, uint32_t width, uint32_t height,\n\t\t\t\t     const char *tech_name)\n{\n\tgs_technique_t *tech = gs_effect_get_technique(effect, tech_name);\n\tgs_eparam_t *image = gs_effect_get_param_by_name(effect, \"image\");\n\tsize_t passes, i;\n\n\tconst bool linear_srgb = gs_get_linear_srgb();\n\n\tconst bool previous = gs_framebuffer_srgb_enabled();\n\tgs_enable_framebuffer_srgb(linear_srgb);\n\n\tif (linear_srgb)\n\t\tgs_effect_set_texture_srgb(image, tex);\n\telse\n\t\tgs_effect_set_texture(image, tex);\n\n\tpasses = gs_technique_begin(tech);\n\tfor (i = 0; i < passes; i++) {\n\t\tgs_technique_begin_pass(tech, i);\n\t\tgs_draw_sprite(tex, 0, width, height);\n\t\tgs_technique_end_pass(tech);\n\t}\n\tgs_technique_end(tech);\n\n\tgs_enable_framebuffer_srgb(previous);\n}\n\nstatic inline bool can_bypass(obs_source_t *target, obs_source_t *parent, uint32_t filter_flags, uint32_t parent_flags,\n\t\t\t      enum obs_allow_direct_render allow_direct, enum gs_color_space space)\n{\n\treturn (target == parent) && (allow_direct == OBS_ALLOW_DIRECT_RENDERING) &&\n\t       ((parent_flags & OBS_SOURCE_CUSTOM_DRAW) == 0) && ((parent_flags & OBS_SOURCE_ASYNC) == 0) &&\n\t       ((filter_flags & OBS_SOURCE_SRGB) == (parent_flags & OBS_SOURCE_SRGB) && space == gs_get_color_space());\n}\n\nbool obs_source_process_filter_begin(obs_source_t *filter, enum gs_color_format format,\n\t\t\t\t     enum obs_allow_direct_render allow_direct)\n{\n\treturn obs_source_process_filter_begin_with_color_space(filter, format, GS_CS_SRGB, allow_direct);\n}\n\nbool obs_source_process_filter_begin_with_color_space(obs_source_t *filter, enum gs_color_format format,\n\t\t\t\t\t\t      enum gs_color_space space,\n\t\t\t\t\t\t      enum obs_allow_direct_render allow_direct)\n{\n\tobs_source_t *target, *parent;\n\tuint32_t filter_flags, parent_flags;\n\tint cx, cy;\n\n\tif (!obs_ptr_valid(filter, \"obs_source_process_filter_begin_with_color_space\"))\n\t\treturn false;\n\n\tfilter->filter_bypass_active = false;\n\n\ttarget = obs_filter_get_target(filter);\n\tparent = obs_filter_get_parent(filter);\n\n\tif (!target) {\n\t\tblog(LOG_INFO, \"filter '%s' being processed with no target!\", filter->context.name);\n\t\treturn false;\n\t}\n\tif (!parent) {\n\t\tblog(LOG_INFO, \"filter '%s' being processed with no parent!\", filter->context.name);\n\t\treturn false;\n\t}\n\n\tfilter_flags = filter->info.output_flags;\n\tparent_flags = parent->info.output_flags;\n\tcx = get_base_width(target);\n\tcy = get_base_height(target);\n\n\tfilter->allow_direct = allow_direct;\n\n\t/* if the parent does not use any custom effects, and this is the last\n\t * filter in the chain for the parent, then render the parent directly\n\t * using the filter effect instead of rendering to texture to reduce\n\t * the total number of passes */\n\tif (can_bypass(target, parent, filter_flags, parent_flags, allow_direct, space)) {\n\t\tfilter->filter_bypass_active = true;\n\t\treturn true;\n\t}\n\n\tif (!cx || !cy) {\n\t\tobs_source_skip_video_filter(filter);\n\t\treturn false;\n\t}\n\n\tif (filter->filter_texrender && (gs_texrender_get_format(filter->filter_texrender) != format)) {\n\t\tgs_texrender_destroy(filter->filter_texrender);\n\t\tfilter->filter_texrender = NULL;\n\t}\n\n\tif (!filter->filter_texrender) {\n\t\tfilter->filter_texrender = gs_texrender_create(format, GS_ZS_NONE);\n\t}\n\n\tif (gs_texrender_begin_with_color_space(filter->filter_texrender, cx, cy, space)) {\n\t\tgs_blend_state_push();\n\t\tgs_blend_function_separate(GS_BLEND_SRCALPHA, GS_BLEND_INVSRCALPHA, GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);\n\n\t\tbool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;\n\t\tbool async = (parent_flags & OBS_SOURCE_ASYNC) != 0;\n\t\tstruct vec4 clear_color;\n\n\t\tvec4_zero(&clear_color);\n\t\tgs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);\n\t\tgs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);\n\n\t\tif (target == parent && !custom_draw && !async)\n\t\t\tobs_source_default_render(target);\n\t\telse\n\t\t\tobs_source_video_render(target);\n\n\t\tgs_blend_state_pop();\n\n\t\tgs_texrender_end(filter->filter_texrender);\n\t}\n\treturn true;\n}\n\nvoid obs_source_process_filter_tech_end(obs_source_t *filter, gs_effect_t *effect, uint32_t width, uint32_t height,\n\t\t\t\t\tconst char *tech_name)\n{\n\tobs_source_t *target, *parent;\n\tgs_texture_t *texture;\n\tuint32_t filter_flags;\n\n\tif (!filter)\n\t\treturn;\n\n\tconst bool filter_bypass_active = filter->filter_bypass_active;\n\tfilter->filter_bypass_active = false;\n\n\ttarget = obs_filter_get_target(filter);\n\tparent = obs_filter_get_parent(filter);\n\n\tif (!target || !parent)\n\t\treturn;\n\n\tfilter_flags = filter->info.output_flags;\n\n\tconst bool previous = gs_set_linear_srgb((filter_flags & OBS_SOURCE_SRGB) != 0);\n\n\tconst char *tech = tech_name ? tech_name : \"Draw\";\n\n\tif (filter_bypass_active) {\n\t\trender_filter_bypass(target, effect, tech);\n\t} else {\n\t\ttexture = gs_texrender_get_texture(filter->filter_texrender);\n\t\tif (texture) {\n\t\t\trender_filter_tex(texture, effect, width, height, tech);\n\t\t}\n\t}\n\n\tgs_set_linear_srgb(previous);\n}\n\nvoid obs_source_process_filter_end(obs_source_t *filter, gs_effect_t *effect, uint32_t width, uint32_t height)\n{\n\tif (!obs_ptr_valid(filter, \"obs_source_process_filter_end\"))\n\t\treturn;\n\n\tobs_source_process_filter_tech_end(filter, effect, width, height, \"Draw\");\n}\n\nvoid obs_source_skip_video_filter(obs_source_t *filter)\n{\n\tobs_source_t *target, *parent;\n\tbool custom_draw, async;\n\tuint32_t parent_flags;\n\n\tif (!obs_ptr_valid(filter, \"obs_source_skip_video_filter\"))\n\t\treturn;\n\n\ttarget = obs_filter_get_target(filter);\n\tparent = obs_filter_get_parent(filter);\n\tparent_flags = parent->info.output_flags;\n\tcustom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;\n\tasync = (parent_flags & OBS_SOURCE_ASYNC) != 0;\n\n\tif (target == parent) {\n\t\tif (!custom_draw && !async)\n\t\t\tobs_source_default_render(target);\n\t\telse if (target->info.video_render)\n\t\t\tobs_source_main_render(target);\n\t\telse if (deinterlacing_enabled(target))\n\t\t\tdeinterlace_render(target);\n\t\telse\n\t\t\tobs_source_render_async_video(target);\n\n\t} else {\n\t\tobs_source_video_render(target);\n\t}\n}\n\nsignal_handler_t *obs_source_get_signal_handler(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_signal_handler\") ? source->context.signals : NULL;\n}\n\nproc_handler_t *obs_source_get_proc_handler(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_proc_handler\") ? source->context.procs : NULL;\n}\n\nvoid obs_source_set_volume(obs_source_t *source, float volume)\n{\n\tif (obs_source_valid(source, \"obs_source_set_volume\")) {\n\t\tstruct audio_action action = {.timestamp = os_gettime_ns(), .type = AUDIO_ACTION_VOL, .vol = volume};\n\n\t\tstruct calldata data;\n\t\tuint8_t stack[128];\n\n\t\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\t\tcalldata_set_ptr(&data, \"source\", source);\n\t\tcalldata_set_float(&data, \"volume\", volume);\n\n\t\tsignal_handler_signal(source->context.signals, \"volume\", &data);\n\t\tif (!source->context.private)\n\t\t\tsignal_handler_signal(obs->signals, \"source_volume\", &data);\n\n\t\tvolume = (float)calldata_float(&data, \"volume\");\n\n\t\tpthread_mutex_lock(&source->audio_actions_mutex);\n\t\tda_push_back(source->audio_actions, &action);\n\t\tpthread_mutex_unlock(&source->audio_actions_mutex);\n\n\t\tsource->user_volume = volume;\n\t}\n}\n\nfloat obs_source_get_volume(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_volume\") ? source->user_volume : 0.0f;\n}\n\nvoid obs_source_set_sync_offset(obs_source_t *source, int64_t offset)\n{\n\tif (obs_source_valid(source, \"obs_source_set_sync_offset\")) {\n\t\tstruct calldata data;\n\t\tuint8_t stack[128];\n\n\t\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\t\tcalldata_set_ptr(&data, \"source\", source);\n\t\tcalldata_set_int(&data, \"offset\", offset);\n\n\t\tsignal_handler_signal(source->context.signals, \"audio_sync\", &data);\n\n\t\tsource->sync_offset = calldata_int(&data, \"offset\");\n\t}\n}\n\nint64_t obs_source_get_sync_offset(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_sync_offset\") ? source->sync_offset : 0;\n}\n\nstruct source_enum_data {\n\tobs_source_enum_proc_t enum_callback;\n\tvoid *param;\n};\n\nstatic void enum_source_active_tree_callback(obs_source_t *parent, obs_source_t *child, void *param)\n{\n\tstruct source_enum_data *data = param;\n\tbool is_transition = child->info.type == OBS_SOURCE_TYPE_TRANSITION;\n\n\tif (is_transition)\n\t\tobs_transition_enum_sources(child, enum_source_active_tree_callback, param);\n\tif (child->info.enum_active_sources) {\n\t\tif (child->context.data) {\n\t\t\tchild->info.enum_active_sources(child->context.data, enum_source_active_tree_callback, data);\n\t\t}\n\t}\n\n\tdata->enum_callback(parent, child, data->param);\n}\n\nvoid obs_source_enum_active_sources(obs_source_t *source, obs_source_enum_proc_t enum_callback, void *param)\n{\n\tbool is_transition;\n\tif (!data_valid(source, \"obs_source_enum_active_sources\"))\n\t\treturn;\n\n\tis_transition = source->info.type == OBS_SOURCE_TYPE_TRANSITION;\n\tif (!is_transition && !source->info.enum_active_sources)\n\t\treturn;\n\n\tsource = obs_source_get_ref(source);\n\tif (!data_valid(source, \"obs_source_enum_active_sources\"))\n\t\treturn;\n\n\tif (is_transition)\n\t\tobs_transition_enum_sources(source, enum_callback, param);\n\tif (source->info.enum_active_sources)\n\t\tsource->info.enum_active_sources(source->context.data, enum_callback, param);\n\n\tobs_source_release(source);\n}\n\nvoid obs_source_enum_active_tree(obs_source_t *source, obs_source_enum_proc_t enum_callback, void *param)\n{\n\tstruct source_enum_data data = {enum_callback, param};\n\tbool is_transition;\n\n\tif (!data_valid(source, \"obs_source_enum_active_tree\"))\n\t\treturn;\n\n\tis_transition = source->info.type == OBS_SOURCE_TYPE_TRANSITION;\n\tif (!is_transition && !source->info.enum_active_sources)\n\t\treturn;\n\n\tsource = obs_source_get_ref(source);\n\tif (!data_valid(source, \"obs_source_enum_active_tree\"))\n\t\treturn;\n\n\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION)\n\t\tobs_transition_enum_sources(source, enum_source_active_tree_callback, &data);\n\tif (source->info.enum_active_sources)\n\t\tsource->info.enum_active_sources(source->context.data, enum_source_active_tree_callback, &data);\n\n\tobs_source_release(source);\n}\n\nstatic void enum_source_full_tree_callback(obs_source_t *parent, obs_source_t *child, void *param)\n{\n\tstruct source_enum_data *data = param;\n\tbool is_transition = child->info.type == OBS_SOURCE_TYPE_TRANSITION;\n\n\tif (is_transition)\n\t\tobs_transition_enum_sources(child, enum_source_full_tree_callback, param);\n\tif (child->info.enum_all_sources) {\n\t\tif (child->context.data) {\n\t\t\tchild->info.enum_all_sources(child->context.data, enum_source_full_tree_callback, data);\n\t\t}\n\t} else if (child->info.enum_active_sources) {\n\t\tif (child->context.data) {\n\t\t\tchild->info.enum_active_sources(child->context.data, enum_source_full_tree_callback, data);\n\t\t}\n\t}\n\n\tdata->enum_callback(parent, child, data->param);\n}\n\nvoid obs_source_enum_full_tree(obs_source_t *source, obs_source_enum_proc_t enum_callback, void *param)\n{\n\tstruct source_enum_data data = {enum_callback, param};\n\tbool is_transition;\n\n\tif (!data_valid(source, \"obs_source_enum_full_tree\"))\n\t\treturn;\n\n\tis_transition = source->info.type == OBS_SOURCE_TYPE_TRANSITION;\n\tif (!is_transition && !source->info.enum_active_sources)\n\t\treturn;\n\n\tsource = obs_source_get_ref(source);\n\tif (!data_valid(source, \"obs_source_enum_full_tree\"))\n\t\treturn;\n\n\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION)\n\t\tobs_transition_enum_sources(source, enum_source_full_tree_callback, &data);\n\n\tif (source->info.enum_all_sources) {\n\t\tsource->info.enum_all_sources(source->context.data, enum_source_full_tree_callback, &data);\n\n\t} else if (source->info.enum_active_sources) {\n\t\tsource->info.enum_active_sources(source->context.data, enum_source_full_tree_callback, &data);\n\t}\n\n\tobs_source_release(source);\n}\n\nstruct descendant_info {\n\tbool exists;\n\tobs_source_t *target;\n};\n\nstatic void check_descendant(obs_source_t *parent, obs_source_t *child, void *param)\n{\n\tstruct descendant_info *info = param;\n\tif (child == info->target || parent == info->target)\n\t\tinfo->exists = true;\n}\n\nbool obs_source_add_active_child(obs_source_t *parent, obs_source_t *child)\n{\n\tstruct descendant_info info = {false, parent};\n\n\tif (!obs_ptr_valid(parent, \"obs_source_add_active_child\"))\n\t\treturn false;\n\tif (!obs_ptr_valid(child, \"obs_source_add_active_child\"))\n\t\treturn false;\n\tif (parent == child) {\n\t\tblog(LOG_WARNING, \"obs_source_add_active_child: \"\n\t\t\t\t  \"parent == child\");\n\t\treturn false;\n\t}\n\n\tobs_source_enum_full_tree(child, check_descendant, &info);\n\tif (info.exists)\n\t\treturn false;\n\n\tfor (int i = 0; i < parent->show_refs; i++) {\n\t\tenum view_type type;\n\t\ttype = (i < parent->activate_refs) ? MAIN_VIEW : AUX_VIEW;\n\t\tobs_source_activate(child, type);\n\t}\n\n\treturn true;\n}\n\nvoid obs_source_remove_active_child(obs_source_t *parent, obs_source_t *child)\n{\n\tif (!obs_ptr_valid(parent, \"obs_source_remove_active_child\"))\n\t\treturn;\n\tif (!obs_ptr_valid(child, \"obs_source_remove_active_child\"))\n\t\treturn;\n\n\tfor (int i = 0; i < parent->show_refs; i++) {\n\t\tenum view_type type;\n\t\ttype = (i < parent->activate_refs) ? MAIN_VIEW : AUX_VIEW;\n\t\tobs_source_deactivate(child, type);\n\t}\n}\n\nvoid obs_source_save(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_save\"))\n\t\treturn;\n\n\tobs_source_dosignal(source, \"source_save\", \"save\");\n\n\tif (source->info.save)\n\t\tsource->info.save(source->context.data, source->context.settings);\n}\n\nvoid obs_source_load(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_load\"))\n\t\treturn;\n\tif (source->info.load)\n\t\tsource->info.load(source->context.data, source->context.settings);\n\n\tobs_source_dosignal(source, \"source_load\", \"load\");\n}\n\nvoid obs_source_load2(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_load2\"))\n\t\treturn;\n\n\tobs_source_load(source);\n\n\tfor (size_t i = source->filters.num; i > 0; i--) {\n\t\tobs_source_t *filter = source->filters.array[i - 1];\n\t\tobs_source_load(filter);\n\t}\n}\n\nbool obs_source_active(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_active\") ? source->activate_refs != 0 : false;\n}\n\nbool obs_source_showing(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_showing\") ? source->show_refs != 0 : false;\n}\n\nstatic inline void signal_flags_updated(obs_source_t *source)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"source\", source);\n\tcalldata_set_int(&data, \"flags\", source->flags);\n\n\tsignal_handler_signal(source->context.signals, \"update_flags\", &data);\n}\n\nvoid obs_source_set_flags(obs_source_t *source, uint32_t flags)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_flags\"))\n\t\treturn;\n\n\tif (flags != source->flags) {\n\t\tsource->flags = flags;\n\t\tsignal_flags_updated(source);\n\t}\n}\n\nvoid obs_source_set_default_flags(obs_source_t *source, uint32_t flags)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_default_flags\"))\n\t\treturn;\n\n\tsource->default_flags = flags;\n}\n\nuint32_t obs_source_get_flags(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_flags\") ? source->flags : 0;\n}\n\nvoid obs_source_set_audio_mixers(obs_source_t *source, uint32_t mixers)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\n\tif (!obs_source_valid(source, \"obs_source_set_audio_mixers\"))\n\t\treturn;\n\tif (!source->owns_info_id && (source->info.output_flags & OBS_SOURCE_AUDIO) == 0)\n\t\treturn;\n\n\tif (source->audio_mixers == mixers)\n\t\treturn;\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"source\", source);\n\tcalldata_set_int(&data, \"mixers\", mixers);\n\n\tsignal_handler_signal(source->context.signals, \"audio_mixers\", &data);\n\n\tmixers = (uint32_t)calldata_int(&data, \"mixers\");\n\n\tsource->audio_mixers = mixers;\n}\n\nuint32_t obs_source_get_audio_mixers(const obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_get_audio_mixers\"))\n\t\treturn 0;\n\tif (!source->owns_info_id && (source->info.output_flags & OBS_SOURCE_AUDIO) == 0)\n\t\treturn 0;\n\n\treturn source->audio_mixers;\n}\n\nvoid obs_source_draw_set_color_matrix(const struct matrix4 *color_matrix, const struct vec3 *color_range_min,\n\t\t\t\t      const struct vec3 *color_range_max)\n{\n\tstruct vec3 color_range_min_def;\n\tstruct vec3 color_range_max_def;\n\n\tvec3_set(&color_range_min_def, 0.0f, 0.0f, 0.0f);\n\tvec3_set(&color_range_max_def, 1.0f, 1.0f, 1.0f);\n\n\tgs_effect_t *effect = gs_get_effect();\n\tgs_eparam_t *matrix;\n\tgs_eparam_t *range_min;\n\tgs_eparam_t *range_max;\n\n\tif (!effect) {\n\t\tblog(LOG_WARNING, \"obs_source_draw_set_color_matrix: no \"\n\t\t\t\t  \"active effect!\");\n\t\treturn;\n\t}\n\n\tif (!obs_ptr_valid(color_matrix, \"obs_source_draw_set_color_matrix\"))\n\t\treturn;\n\n\tif (!color_range_min)\n\t\tcolor_range_min = &color_range_min_def;\n\tif (!color_range_max)\n\t\tcolor_range_max = &color_range_max_def;\n\n\tmatrix = gs_effect_get_param_by_name(effect, \"color_matrix\");\n\trange_min = gs_effect_get_param_by_name(effect, \"color_range_min\");\n\trange_max = gs_effect_get_param_by_name(effect, \"color_range_max\");\n\n\tgs_effect_set_matrix4(matrix, color_matrix);\n\tgs_effect_set_val(range_min, color_range_min, sizeof(float) * 3);\n\tgs_effect_set_val(range_max, color_range_max, sizeof(float) * 3);\n}\n\nvoid obs_source_draw(gs_texture_t *texture, int x, int y, uint32_t cx, uint32_t cy, bool flip)\n{\n\tif (!obs_ptr_valid(texture, \"obs_source_draw\"))\n\t\treturn;\n\n\tgs_effect_t *effect = gs_get_effect();\n\tif (!effect) {\n\t\tblog(LOG_WARNING, \"obs_source_draw: no active effect!\");\n\t\treturn;\n\t}\n\n\tconst bool linear_srgb = gs_get_linear_srgb();\n\n\tconst bool previous = gs_framebuffer_srgb_enabled();\n\tgs_enable_framebuffer_srgb(linear_srgb);\n\n\tgs_eparam_t *image = gs_effect_get_param_by_name(effect, \"image\");\n\tif (linear_srgb)\n\t\tgs_effect_set_texture_srgb(image, texture);\n\telse\n\t\tgs_effect_set_texture(image, texture);\n\n\tconst bool change_pos = (x != 0 || y != 0);\n\tif (change_pos) {\n\t\tgs_matrix_push();\n\t\tgs_matrix_translate3f((float)x, (float)y, 0.0f);\n\t}\n\n\tgs_draw_sprite(texture, flip ? GS_FLIP_V : 0, cx, cy);\n\n\tif (change_pos)\n\t\tgs_matrix_pop();\n\n\tgs_enable_framebuffer_srgb(previous);\n}\n\nvoid obs_source_inc_showing(obs_source_t *source)\n{\n\tif (obs_source_valid(source, \"obs_source_inc_showing\"))\n\t\tobs_source_activate(source, AUX_VIEW);\n}\n\nvoid obs_source_inc_active(obs_source_t *source)\n{\n\tif (obs_source_valid(source, \"obs_source_inc_active\"))\n\t\tobs_source_activate(source, MAIN_VIEW);\n}\n\nvoid obs_source_dec_showing(obs_source_t *source)\n{\n\tif (obs_source_valid(source, \"obs_source_dec_showing\"))\n\t\tobs_source_deactivate(source, AUX_VIEW);\n}\n\nvoid obs_source_dec_active(obs_source_t *source)\n{\n\tif (obs_source_valid(source, \"obs_source_dec_active\"))\n\t\tobs_source_deactivate(source, MAIN_VIEW);\n}\n\nvoid obs_source_enum_filters(obs_source_t *source, obs_source_enum_proc_t callback, void *param)\n{\n\tif (!obs_source_valid(source, \"obs_source_enum_filters\"))\n\t\treturn;\n\tif (!obs_ptr_valid(callback, \"obs_source_enum_filters\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\n\tfor (size_t i = source->filters.num; i > 0; i--) {\n\t\tstruct obs_source *filter = source->filters.array[i - 1];\n\t\tcallback(source, filter, param);\n\t}\n\n\tpthread_mutex_unlock(&source->filter_mutex);\n}\n\nvoid obs_source_set_hidden(obs_source_t *source, bool hidden)\n{\n\tsource->temp_removed = hidden;\n}\n\nbool obs_source_is_hidden(obs_source_t *source)\n{\n\treturn source->temp_removed;\n}\n\nobs_source_t *obs_source_get_filter_by_name(obs_source_t *source, const char *name)\n{\n\tobs_source_t *filter = NULL;\n\n\tif (!obs_source_valid(source, \"obs_source_get_filter_by_name\"))\n\t\treturn NULL;\n\tif (!obs_ptr_valid(name, \"obs_source_get_filter_by_name\"))\n\t\treturn NULL;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\n\tfor (size_t i = 0; i < source->filters.num; i++) {\n\t\tstruct obs_source *cur_filter = source->filters.array[i];\n\t\tif (strcmp(cur_filter->context.name, name) == 0) {\n\t\t\tfilter = obs_source_get_ref(cur_filter);\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\treturn filter;\n}\n\nsize_t obs_source_filter_count(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_filter_count\") ? source->filters.num : 0;\n}\n\nbool obs_source_enabled(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_enabled\") ? source->enabled : false;\n}\n\nvoid obs_source_set_enabled(obs_source_t *source, bool enabled)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\n\tif (!obs_source_valid(source, \"obs_source_set_enabled\"))\n\t\treturn;\n\n\tsource->enabled = enabled;\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"source\", source);\n\tcalldata_set_bool(&data, \"enabled\", enabled);\n\n\tsignal_handler_signal(source->context.signals, \"enable\", &data);\n}\n\nbool obs_source_muted(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_muted\") ? source->user_muted : false;\n}\n\nvoid obs_source_set_muted(obs_source_t *source, bool muted)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\tstruct audio_action action = {.timestamp = os_gettime_ns(), .type = AUDIO_ACTION_MUTE, .set = muted};\n\n\tif (!obs_source_valid(source, \"obs_source_set_muted\"))\n\t\treturn;\n\n\tsource->user_muted = muted;\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"source\", source);\n\tcalldata_set_bool(&data, \"muted\", muted);\n\n\tsignal_handler_signal(source->context.signals, \"mute\", &data);\n\n\tpthread_mutex_lock(&source->audio_actions_mutex);\n\tda_push_back(source->audio_actions, &action);\n\tpthread_mutex_unlock(&source->audio_actions_mutex);\n}\n\nstatic void source_signal_push_to_changed(obs_source_t *source, const char *signal, bool enabled)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"source\", source);\n\tcalldata_set_bool(&data, \"enabled\", enabled);\n\n\tsignal_handler_signal(source->context.signals, signal, &data);\n}\n\nstatic void source_signal_push_to_delay(obs_source_t *source, const char *signal, uint64_t delay)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"source\", source);\n\tcalldata_set_int(&data, \"delay\", delay);\n\n\tsignal_handler_signal(source->context.signals, signal, &data);\n}\n\nbool obs_source_push_to_mute_enabled(obs_source_t *source)\n{\n\tbool enabled;\n\tif (!obs_source_valid(source, \"obs_source_push_to_mute_enabled\"))\n\t\treturn false;\n\n\tpthread_mutex_lock(&source->audio_mutex);\n\tenabled = source->push_to_mute_enabled;\n\tpthread_mutex_unlock(&source->audio_mutex);\n\n\treturn enabled;\n}\n\nvoid obs_source_enable_push_to_mute(obs_source_t *source, bool enabled)\n{\n\tif (!obs_source_valid(source, \"obs_source_enable_push_to_mute\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->audio_mutex);\n\tbool changed = source->push_to_mute_enabled != enabled;\n\tif (obs_source_get_output_flags(source) & OBS_SOURCE_AUDIO && changed)\n\t\tblog(LOG_INFO, \"source '%s' %s push-to-mute\", obs_source_get_name(source),\n\t\t     enabled ? \"enabled\" : \"disabled\");\n\n\tsource->push_to_mute_enabled = enabled;\n\n\tif (changed)\n\t\tsource_signal_push_to_changed(source, \"push_to_mute_changed\", enabled);\n\tpthread_mutex_unlock(&source->audio_mutex);\n}\n\nuint64_t obs_source_get_push_to_mute_delay(obs_source_t *source)\n{\n\tuint64_t delay;\n\tif (!obs_source_valid(source, \"obs_source_get_push_to_mute_delay\"))\n\t\treturn 0;\n\n\tpthread_mutex_lock(&source->audio_mutex);\n\tdelay = source->push_to_mute_delay;\n\tpthread_mutex_unlock(&source->audio_mutex);\n\n\treturn delay;\n}\n\nvoid obs_source_set_push_to_mute_delay(obs_source_t *source, uint64_t delay)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_push_to_mute_delay\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->audio_mutex);\n\tsource->push_to_mute_delay = delay;\n\n\tsource_signal_push_to_delay(source, \"push_to_mute_delay\", delay);\n\tpthread_mutex_unlock(&source->audio_mutex);\n}\n\nbool obs_source_push_to_talk_enabled(obs_source_t *source)\n{\n\tbool enabled;\n\tif (!obs_source_valid(source, \"obs_source_push_to_talk_enabled\"))\n\t\treturn false;\n\n\tpthread_mutex_lock(&source->audio_mutex);\n\tenabled = source->push_to_talk_enabled;\n\tpthread_mutex_unlock(&source->audio_mutex);\n\n\treturn enabled;\n}\n\nvoid obs_source_enable_push_to_talk(obs_source_t *source, bool enabled)\n{\n\tif (!obs_source_valid(source, \"obs_source_enable_push_to_talk\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->audio_mutex);\n\tbool changed = source->push_to_talk_enabled != enabled;\n\tif (obs_source_get_output_flags(source) & OBS_SOURCE_AUDIO && changed)\n\t\tblog(LOG_INFO, \"source '%s' %s push-to-talk\", obs_source_get_name(source),\n\t\t     enabled ? \"enabled\" : \"disabled\");\n\n\tsource->push_to_talk_enabled = enabled;\n\n\tif (changed)\n\t\tsource_signal_push_to_changed(source, \"push_to_talk_changed\", enabled);\n\tpthread_mutex_unlock(&source->audio_mutex);\n}\n\nuint64_t obs_source_get_push_to_talk_delay(obs_source_t *source)\n{\n\tuint64_t delay;\n\tif (!obs_source_valid(source, \"obs_source_get_push_to_talk_delay\"))\n\t\treturn 0;\n\n\tpthread_mutex_lock(&source->audio_mutex);\n\tdelay = source->push_to_talk_delay;\n\tpthread_mutex_unlock(&source->audio_mutex);\n\n\treturn delay;\n}\n\nvoid obs_source_set_push_to_talk_delay(obs_source_t *source, uint64_t delay)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_push_to_talk_delay\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->audio_mutex);\n\tsource->push_to_talk_delay = delay;\n\n\tsource_signal_push_to_delay(source, \"push_to_talk_delay\", delay);\n\tpthread_mutex_unlock(&source->audio_mutex);\n}\n\nvoid *obs_source_get_type_data(obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_type_data\") ? source->info.type_data : NULL;\n}\n\nstatic float get_source_volume(obs_source_t *source, uint64_t os_time)\n{\n\tif (source->push_to_mute_enabled && source->push_to_mute_pressed)\n\t\tsource->push_to_mute_stop_time = os_time + source->push_to_mute_delay * 1000000;\n\n\tif (source->push_to_talk_enabled && source->push_to_talk_pressed)\n\t\tsource->push_to_talk_stop_time = os_time + source->push_to_talk_delay * 1000000;\n\n\tbool push_to_mute_active = source->push_to_mute_pressed || os_time < source->push_to_mute_stop_time;\n\tbool push_to_talk_active = source->push_to_talk_pressed || os_time < source->push_to_talk_stop_time;\n\n\tbool muted = !source->enabled || source->muted || (source->push_to_mute_enabled && push_to_mute_active) ||\n\t\t     (source->push_to_talk_enabled && !push_to_talk_active);\n\n\tif (muted || close_float(source->volume, 0.0f, 0.0001f))\n\t\treturn 0.0f;\n\tif (close_float(source->volume, 1.0f, 0.0001f))\n\t\treturn 1.0f;\n\n\treturn source->volume;\n}\n\nstatic inline void multiply_output_audio(obs_source_t *source, size_t mix, size_t channels, float vol)\n{\n\tregister float *out = source->audio_output_buf[mix][0];\n\tregister float *end = out + AUDIO_OUTPUT_FRAMES * channels;\n\n\twhile (out < end)\n\t\t*(out++) *= vol;\n}\n\nstatic inline void multiply_vol_data(obs_source_t *source, size_t mix, size_t channels, float *vol_data)\n{\n\tfor (size_t ch = 0; ch < channels; ch++) {\n\t\tregister float *out = source->audio_output_buf[mix][ch];\n\t\tregister float *end = out + AUDIO_OUTPUT_FRAMES;\n\t\tregister float *vol = vol_data;\n\n\t\twhile (out < end)\n\t\t\t*(out++) *= *(vol++);\n\t}\n}\n\nstatic inline void apply_audio_action(obs_source_t *source, const struct audio_action *action)\n{\n\tswitch (action->type) {\n\tcase AUDIO_ACTION_VOL:\n\t\tsource->volume = action->vol;\n\t\tbreak;\n\tcase AUDIO_ACTION_MUTE:\n\t\tsource->muted = action->set;\n\t\tbreak;\n\tcase AUDIO_ACTION_PTT:\n\t\tsource->push_to_talk_pressed = action->set;\n\t\tbreak;\n\tcase AUDIO_ACTION_PTM:\n\t\tsource->push_to_mute_pressed = action->set;\n\t\tbreak;\n\t}\n}\n\nstatic void apply_audio_actions(obs_source_t *source, size_t channels, size_t sample_rate)\n{\n\tfloat vol_data[AUDIO_OUTPUT_FRAMES];\n\tfloat cur_vol = get_source_volume(source, source->audio_ts);\n\tsize_t frame_num = 0;\n\n\tpthread_mutex_lock(&source->audio_actions_mutex);\n\n\tfor (size_t i = 0; i < source->audio_actions.num; i++) {\n\t\tstruct audio_action action = source->audio_actions.array[i];\n\t\tuint64_t timestamp = action.timestamp;\n\t\tsize_t new_frame_num;\n\n\t\tif (timestamp < source->audio_ts)\n\t\t\ttimestamp = source->audio_ts;\n\n\t\tnew_frame_num = conv_time_to_frames(sample_rate, timestamp - source->audio_ts);\n\n\t\tif (new_frame_num >= AUDIO_OUTPUT_FRAMES)\n\t\t\tbreak;\n\n\t\tda_erase(source->audio_actions, i--);\n\n\t\tapply_audio_action(source, &action);\n\n\t\tif (new_frame_num > frame_num) {\n\t\t\tfor (; frame_num < new_frame_num; frame_num++)\n\t\t\t\tvol_data[frame_num] = cur_vol;\n\t\t}\n\n\t\tcur_vol = get_source_volume(source, timestamp);\n\t}\n\n\tfor (; frame_num < AUDIO_OUTPUT_FRAMES; frame_num++)\n\t\tvol_data[frame_num] = cur_vol;\n\n\tpthread_mutex_unlock(&source->audio_actions_mutex);\n\n\tfor (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {\n\t\tif ((source->audio_mixers & (1 << mix)) != 0)\n\t\t\tmultiply_vol_data(source, mix, channels, vol_data);\n\t}\n}\n\nstatic void apply_audio_volume(obs_source_t *source, uint32_t mixers, size_t channels, size_t sample_rate)\n{\n\tstruct audio_action action;\n\tbool actions_pending;\n\tfloat vol;\n\n\tpthread_mutex_lock(&source->audio_actions_mutex);\n\n\tactions_pending = source->audio_actions.num > 0;\n\tif (actions_pending)\n\t\taction = source->audio_actions.array[0];\n\n\tpthread_mutex_unlock(&source->audio_actions_mutex);\n\n\tif (actions_pending) {\n\t\tuint64_t duration = conv_frames_to_time(sample_rate, AUDIO_OUTPUT_FRAMES);\n\n\t\tif (action.timestamp < (source->audio_ts + duration)) {\n\t\t\tapply_audio_actions(source, channels, sample_rate);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tvol = get_source_volume(source, source->audio_ts);\n\tif (vol == 1.0f)\n\t\treturn;\n\n\tif (vol == 0.0f || mixers == 0) {\n\t\tmemset(source->audio_output_buf[0][0], 0,\n\t\t       AUDIO_OUTPUT_FRAMES * sizeof(float) * MAX_AUDIO_CHANNELS * MAX_AUDIO_MIXES);\n\t\treturn;\n\t}\n\n\tfor (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {\n\t\tuint32_t mix_and_val = (1 << mix);\n\t\tif ((source->audio_mixers & mix_and_val) != 0 && (mixers & mix_and_val) != 0)\n\t\t\tmultiply_output_audio(source, mix, channels, vol);\n\t}\n}\n\nstatic void custom_audio_render(obs_source_t *source, uint32_t mixers, size_t channels, size_t sample_rate)\n{\n\tstruct obs_source_audio_mix audio_data;\n\tbool success;\n\tuint64_t ts;\n\n\tfor (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {\n\t\tfor (size_t ch = 0; ch < channels; ch++) {\n\t\t\taudio_data.output[mix].data[ch] = source->audio_output_buf[mix][ch];\n\t\t}\n\n\t\tif ((source->audio_mixers & mixers & (1 << mix)) != 0) {\n\t\t\tmemset(source->audio_output_buf[mix][0], 0, sizeof(float) * AUDIO_OUTPUT_FRAMES * channels);\n\t\t}\n\t}\n\n\tsuccess = source->info.audio_render(source->context.data, &ts, &audio_data, mixers, channels, sample_rate);\n\tsource->audio_ts = success ? ts : 0;\n\tsource->audio_pending = !success;\n\n\tif (!success || !source->audio_ts || !mixers)\n\t\treturn;\n\n\tfor (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {\n\t\tuint32_t mix_bit = 1 << mix;\n\n\t\tif ((mixers & mix_bit) == 0)\n\t\t\tcontinue;\n\n\t\tif ((source->audio_mixers & mix_bit) == 0) {\n\t\t\tmemset(source->audio_output_buf[mix][0], 0, sizeof(float) * AUDIO_OUTPUT_FRAMES * channels);\n\t\t}\n\t}\n\n\tapply_audio_volume(source, mixers, channels, sample_rate);\n}\n\nstatic void audio_submix(obs_source_t *source, size_t channels, size_t sample_rate)\n{\n\tstruct audio_output_data audio_data;\n\tstruct obs_source_audio audio = {0};\n\tbool success;\n\tuint64_t ts;\n\n\tfor (size_t ch = 0; ch < channels; ch++) {\n\t\taudio_data.data[ch] = source->audio_mix_buf[ch];\n\t}\n\n\tmemset(source->audio_mix_buf[0], 0, sizeof(float) * AUDIO_OUTPUT_FRAMES * channels);\n\n\tsuccess = source->info.audio_mix(source->context.data, &ts, &audio_data, channels, sample_rate);\n\n\tif (!success)\n\t\treturn;\n\n\tfor (size_t i = 0; i < channels; i++)\n\t\taudio.data[i] = (const uint8_t *)audio_data.data[i];\n\n\taudio.samples_per_sec = (uint32_t)sample_rate;\n\taudio.frames = AUDIO_OUTPUT_FRAMES;\n\taudio.format = AUDIO_FORMAT_FLOAT_PLANAR;\n\taudio.speakers = (enum speaker_layout)channels;\n\taudio.timestamp = ts;\n\n\tobs_source_output_audio(source, &audio);\n}\n\nstatic inline void process_audio_source_tick(obs_source_t *source, uint32_t mixers, size_t channels, size_t sample_rate,\n\t\t\t\t\t     size_t size)\n{\n\tbool audio_submix = !!(source->info.output_flags & OBS_SOURCE_SUBMIX);\n\n\tpthread_mutex_lock(&source->audio_buf_mutex);\n\n\tif (source->audio_input_buf[0].size < size) {\n\t\tsource->audio_pending = true;\n\t\tpthread_mutex_unlock(&source->audio_buf_mutex);\n\t\treturn;\n\t}\n\n\tfor (size_t ch = 0; ch < channels; ch++)\n\t\tdeque_peek_front(&source->audio_input_buf[ch], source->audio_output_buf[0][ch], size);\n\n\tpthread_mutex_unlock(&source->audio_buf_mutex);\n\n\tfor (size_t mix = 1; mix < MAX_AUDIO_MIXES; mix++) {\n\t\tuint32_t mix_and_val = (1 << mix);\n\n\t\tif (audio_submix) {\n\t\t\tif (mix > 1)\n\t\t\t\tbreak;\n\n\t\t\tmixers = 1;\n\t\t\tmix_and_val = 1;\n\t\t}\n\n\t\tif ((source->audio_mixers & mix_and_val) == 0 || (mixers & mix_and_val) == 0) {\n\t\t\tmemset(source->audio_output_buf[mix][0], 0, size * channels);\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (size_t ch = 0; ch < channels; ch++)\n\t\t\tmemcpy(source->audio_output_buf[mix][ch], source->audio_output_buf[0][ch], size);\n\t}\n\n\tif (audio_submix) {\n\t\tsource->audio_pending = false;\n\t\treturn;\n\t}\n\n\tif ((source->audio_mixers & 1) == 0 || (mixers & 1) == 0)\n\t\tmemset(source->audio_output_buf[0][0], 0, size * channels);\n\n\tapply_audio_volume(source, mixers, channels, sample_rate);\n\tsource->audio_pending = false;\n}\n\nvoid obs_source_audio_render(obs_source_t *source, uint32_t mixers, size_t channels, size_t sample_rate, size_t size)\n{\n\tif (!source->audio_output_buf[0][0]) {\n\t\tsource->audio_pending = true;\n\t\treturn;\n\t}\n\n\tif (source->info.audio_render) {\n\t\tif (!source->context.data) {\n\t\t\tsource->audio_pending = true;\n\t\t\treturn;\n\t\t}\n\t\tcustom_audio_render(source, mixers, channels, sample_rate);\n\t\treturn;\n\t}\n\n\tif (source->info.audio_mix) {\n\t\taudio_submix(source, channels, sample_rate);\n\t}\n\n\tif (!source->audio_ts) {\n\t\tsource->audio_pending = true;\n\t\treturn;\n\t}\n\n\tprocess_audio_source_tick(source, mixers, channels, sample_rate, size);\n}\n\nbool obs_source_audio_pending(const obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_audio_pending\"))\n\t\treturn true;\n\n\tif (obs_source_removed(source))\n\t\treturn true;\n\n\treturn (is_composite_source(source) || is_audio_source(source)) ? source->audio_pending : true;\n}\n\nuint64_t obs_source_get_audio_timestamp(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_audio_timestamp\") ? source->audio_ts : 0;\n}\n\nvoid obs_source_get_audio_mix(const obs_source_t *source, struct obs_source_audio_mix *audio)\n{\n\tif (!obs_source_valid(source, \"obs_source_get_audio_mix\"))\n\t\treturn;\n\tif (!obs_ptr_valid(audio, \"audio\"))\n\t\treturn;\n\n\tfor (size_t mix = 0; mix < MAX_AUDIO_MIXES; mix++) {\n\t\tfor (size_t ch = 0; ch < MAX_AUDIO_CHANNELS; ch++) {\n\t\t\taudio->output[mix].data[ch] = source->audio_output_buf[mix][ch];\n\t\t}\n\t}\n}\n\nvoid obs_source_add_audio_pause_callback(obs_source_t *source, signal_callback_t callback, void *param)\n{\n\tif (!obs_source_valid(source, \"obs_source_add_audio_pause_callback\"))\n\t\treturn;\n\n\tsignal_handler_t *handler = obs_source_get_signal_handler(source);\n\n\tsignal_handler_connect(handler, \"media_pause\", callback, param);\n\tsignal_handler_connect(handler, \"media_stopped\", callback, param);\n}\n\nvoid obs_source_remove_audio_pause_callback(obs_source_t *source, signal_callback_t callback, void *param)\n{\n\tif (!obs_source_valid(source, \"obs_source_remove_audio_pause_callback\"))\n\t\treturn;\n\n\tsignal_handler_t *handler = obs_source_get_signal_handler(source);\n\n\tsignal_handler_disconnect(handler, \"media_pause\", callback, param);\n\tsignal_handler_disconnect(handler, \"media_stopped\", callback, param);\n}\n\nvoid obs_source_add_audio_capture_callback(obs_source_t *source, obs_source_audio_capture_t callback, void *param)\n{\n\tstruct audio_cb_info info = {callback, param};\n\n\tif (!obs_source_valid(source, \"obs_source_add_audio_capture_callback\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->audio_cb_mutex);\n\tda_push_back(source->audio_cb_list, &info);\n\tpthread_mutex_unlock(&source->audio_cb_mutex);\n}\n\nvoid obs_source_remove_audio_capture_callback(obs_source_t *source, obs_source_audio_capture_t callback, void *param)\n{\n\tstruct audio_cb_info info = {callback, param};\n\n\tif (!obs_source_valid(source, \"obs_source_remove_audio_capture_callback\"))\n\t\treturn;\n\n\tpthread_mutex_lock(&source->audio_cb_mutex);\n\tda_erase_item(source->audio_cb_list, &info);\n\tpthread_mutex_unlock(&source->audio_cb_mutex);\n}\n\nvoid obs_source_set_monitoring_type(obs_source_t *source, enum obs_monitoring_type type)\n{\n\tstruct calldata data;\n\tuint8_t stack[128];\n\tbool was_on;\n\tbool now_on;\n\n\tif (!obs_source_valid(source, \"obs_source_set_monitoring_type\"))\n\t\treturn;\n\tif (source->monitoring_type == type)\n\t\treturn;\n\n\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\tcalldata_set_ptr(&data, \"source\", source);\n\tcalldata_set_int(&data, \"type\", type);\n\n\tsignal_handler_signal(source->context.signals, \"audio_monitoring\", &data);\n\n\twas_on = source->monitoring_type != OBS_MONITORING_TYPE_NONE;\n\tnow_on = type != OBS_MONITORING_TYPE_NONE;\n\n\tif (was_on != now_on) {\n\t\tif (!was_on) {\n\t\t\tsource->monitor = audio_monitor_create(source);\n\t\t} else {\n\t\t\taudio_monitor_destroy(source->monitor);\n\t\t\tsource->monitor = NULL;\n\t\t}\n\t}\n\n\tsource->monitoring_type = type;\n}\n\nenum obs_monitoring_type obs_source_get_monitoring_type(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_monitoring_type\") ? source->monitoring_type\n\t\t\t\t\t\t\t\t\t  : OBS_MONITORING_TYPE_NONE;\n}\n\nvoid obs_source_set_async_unbuffered(obs_source_t *source, bool unbuffered)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_async_unbuffered\"))\n\t\treturn;\n\n\tsource->async_unbuffered = unbuffered;\n}\n\nbool obs_source_async_unbuffered(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_async_unbuffered\") ? source->async_unbuffered : false;\n}\n\nobs_data_t *obs_source_get_private_settings(obs_source_t *source)\n{\n\tif (!obs_ptr_valid(source, \"obs_source_get_private_settings\"))\n\t\treturn NULL;\n\n\tobs_data_addref(source->private_settings);\n\treturn source->private_settings;\n}\n\nvoid obs_source_set_async_decoupled(obs_source_t *source, bool decouple)\n{\n\tif (!obs_ptr_valid(source, \"obs_source_set_async_decoupled\"))\n\t\treturn;\n\n\tsource->async_decoupled = decouple;\n\tif (decouple) {\n\t\tpthread_mutex_lock(&source->audio_buf_mutex);\n\t\tsource->timing_set = false;\n\t\treset_audio_data(source, 0);\n\t\tpthread_mutex_unlock(&source->audio_buf_mutex);\n\t}\n}\n\nbool obs_source_async_decoupled(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_async_decoupled\") ? source->async_decoupled : false;\n}\n\n/* hidden/undocumented export to allow source type redefinition for scripts */\nEXPORT void obs_enable_source_type(const char *name, bool enable)\n{\n\tstruct obs_source_info *info = get_source_info(name);\n\tif (!info)\n\t\treturn;\n\n\tif (enable)\n\t\tinfo->output_flags &= ~OBS_SOURCE_CAP_DISABLED;\n\telse\n\t\tinfo->output_flags |= OBS_SOURCE_CAP_DISABLED;\n}\n\nenum speaker_layout obs_source_get_speaker_layout(obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_get_audio_channels\"))\n\t\treturn SPEAKERS_UNKNOWN;\n\n\treturn source->sample_info.speakers;\n}\n\nvoid obs_source_set_balance_value(obs_source_t *source, float balance)\n{\n\tif (obs_source_valid(source, \"obs_source_set_balance_value\")) {\n\t\tstruct calldata data;\n\t\tuint8_t stack[128];\n\n\t\tcalldata_init_fixed(&data, stack, sizeof(stack));\n\t\tcalldata_set_ptr(&data, \"source\", source);\n\t\tcalldata_set_float(&data, \"balance\", balance);\n\n\t\tsignal_handler_signal(source->context.signals, \"audio_balance\", &data);\n\n\t\tsource->balance = (float)calldata_float(&data, \"balance\");\n\t}\n}\n\nfloat obs_source_get_balance_value(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_balance_value\") ? source->balance : 0.5f;\n}\n\nvoid obs_source_set_audio_active(obs_source_t *source, bool active)\n{\n\tif (!obs_source_valid(source, \"obs_source_set_audio_active\"))\n\t\treturn;\n\n\tif (os_atomic_set_bool(&source->audio_active, active) == active)\n\t\treturn;\n\n\tif (active)\n\t\tobs_source_dosignal(source, \"source_audio_activate\", \"audio_activate\");\n\telse\n\t\tobs_source_dosignal(source, \"source_audio_deactivate\", \"audio_deactivate\");\n}\n\nbool obs_source_audio_active(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_audio_active\") ? os_atomic_load_bool(&source->audio_active) : false;\n}\n\nuint32_t obs_source_get_last_obs_version(const obs_source_t *source)\n{\n\treturn obs_source_valid(source, \"obs_source_get_last_obs_version\") ? source->last_obs_ver : 0;\n}\n\nenum obs_icon_type obs_source_get_icon_type(const char *id)\n{\n\tconst struct obs_source_info *info = get_source_info(id);\n\treturn (info) ? info->icon_type : OBS_ICON_TYPE_UNKNOWN;\n}\n\nvoid obs_source_media_play_pause(obs_source_t *source, bool pause)\n{\n\tif (!data_valid(source, \"obs_source_media_play_pause\"))\n\t\treturn;\n\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn;\n\tif (!source->info.media_play_pause)\n\t\treturn;\n\n\tstruct media_action action = {\n\t\t.type = MEDIA_ACTION_PLAY_PAUSE,\n\t\t.pause = pause,\n\t};\n\n\tpthread_mutex_lock(&source->media_actions_mutex);\n\tda_push_back(source->media_actions, &action);\n\tpthread_mutex_unlock(&source->media_actions_mutex);\n}\n\nvoid obs_source_media_restart(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_media_restart\"))\n\t\treturn;\n\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn;\n\tif (!source->info.media_restart)\n\t\treturn;\n\n\tstruct media_action action = {\n\t\t.type = MEDIA_ACTION_RESTART,\n\t};\n\n\tpthread_mutex_lock(&source->media_actions_mutex);\n\tda_push_back(source->media_actions, &action);\n\tpthread_mutex_unlock(&source->media_actions_mutex);\n}\n\nvoid obs_source_media_stop(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_media_stop\"))\n\t\treturn;\n\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn;\n\tif (!source->info.media_stop)\n\t\treturn;\n\n\tstruct media_action action = {\n\t\t.type = MEDIA_ACTION_STOP,\n\t};\n\n\tpthread_mutex_lock(&source->media_actions_mutex);\n\tda_push_back(source->media_actions, &action);\n\tpthread_mutex_unlock(&source->media_actions_mutex);\n}\n\nvoid obs_source_media_next(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_media_next\"))\n\t\treturn;\n\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn;\n\tif (!source->info.media_next)\n\t\treturn;\n\n\tstruct media_action action = {\n\t\t.type = MEDIA_ACTION_NEXT,\n\t};\n\n\tpthread_mutex_lock(&source->media_actions_mutex);\n\tda_push_back(source->media_actions, &action);\n\tpthread_mutex_unlock(&source->media_actions_mutex);\n}\n\nvoid obs_source_media_previous(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_media_previous\"))\n\t\treturn;\n\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn;\n\tif (!source->info.media_previous)\n\t\treturn;\n\n\tstruct media_action action = {\n\t\t.type = MEDIA_ACTION_PREVIOUS,\n\t};\n\n\tpthread_mutex_lock(&source->media_actions_mutex);\n\tda_push_back(source->media_actions, &action);\n\tpthread_mutex_unlock(&source->media_actions_mutex);\n}\n\nint64_t obs_source_media_get_duration(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_media_get_duration\"))\n\t\treturn 0;\n\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn 0;\n\tif (source->info.media_get_duration)\n\t\treturn source->info.media_get_duration(source->context.data);\n\telse\n\t\treturn 0;\n}\n\nint64_t obs_source_media_get_time(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_media_get_time\"))\n\t\treturn 0;\n\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn 0;\n\tif (source->info.media_get_time)\n\t\treturn source->info.media_get_time(source->context.data);\n\telse\n\t\treturn 0;\n}\n\nvoid obs_source_media_set_time(obs_source_t *source, int64_t ms)\n{\n\tif (!data_valid(source, \"obs_source_media_set_time\"))\n\t\treturn;\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn;\n\tif (!source->info.media_set_time)\n\t\treturn;\n\n\tstruct media_action action = {\n\t\t.type = MEDIA_ACTION_SET_TIME,\n\t\t.ms = ms,\n\t};\n\n\tpthread_mutex_lock(&source->media_actions_mutex);\n\tda_push_back(source->media_actions, &action);\n\tpthread_mutex_unlock(&source->media_actions_mutex);\n}\n\nenum obs_media_state obs_source_media_get_state(obs_source_t *source)\n{\n\tif (!data_valid(source, \"obs_source_media_get_state\"))\n\t\treturn OBS_MEDIA_STATE_NONE;\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn OBS_MEDIA_STATE_NONE;\n\n\tif (source->info.media_get_state)\n\t\treturn source->info.media_get_state(source->context.data);\n\telse\n\t\treturn OBS_MEDIA_STATE_NONE;\n}\n\nvoid obs_source_media_started(obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_media_started\"))\n\t\treturn;\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn;\n\n\tobs_source_dosignal(source, NULL, \"media_started\");\n}\n\nvoid obs_source_media_ended(obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_media_ended\"))\n\t\treturn;\n\tif ((source->info.output_flags & OBS_SOURCE_CONTROLLABLE_MEDIA) == 0)\n\t\treturn;\n\n\tobs_source_dosignal(source, NULL, \"media_ended\");\n}\n\nobs_data_array_t *obs_source_backup_filters(obs_source_t *source)\n{\n\tif (!obs_source_valid(source, \"obs_source_backup_filters\"))\n\t\treturn NULL;\n\n\tobs_data_array_t *array = obs_data_array_create();\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\tfor (size_t i = 0; i < source->filters.num; i++) {\n\t\tstruct obs_source *filter = source->filters.array[i];\n\t\tobs_data_t *data = obs_save_source(filter);\n\t\tobs_data_array_push_back(array, data);\n\t\tobs_data_release(data);\n\t}\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\treturn array;\n}\n\nvoid obs_source_restore_filters(obs_source_t *source, obs_data_array_t *array)\n{\n\tif (!obs_source_valid(source, \"obs_source_restore_filters\"))\n\t\treturn;\n\tif (!obs_ptr_valid(array, \"obs_source_restore_filters\"))\n\t\treturn;\n\n\tDARRAY(obs_source_t *) cur_filters;\n\tDARRAY(obs_source_t *) new_filters;\n\tobs_source_t *prev = NULL;\n\n\tda_init(cur_filters);\n\tda_init(new_filters);\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\n\t/* clear filter list */\n\tda_reserve(cur_filters, source->filters.num);\n\tda_reserve(new_filters, source->filters.num);\n\tfor (size_t i = 0; i < source->filters.num; i++) {\n\t\tobs_source_t *filter = source->filters.array[i];\n\t\tda_push_back(cur_filters, &filter);\n\t\tfilter->filter_parent = NULL;\n\t\tfilter->filter_target = NULL;\n\t}\n\n\tda_free(source->filters);\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\t/* add backed up filters */\n\tsize_t count = obs_data_array_count(array);\n\tfor (size_t i = 0; i < count; i++) {\n\t\tobs_data_t *data = obs_data_array_item(array, i);\n\t\tconst char *name = obs_data_get_string(data, \"name\");\n\t\tobs_source_t *filter = NULL;\n\n\t\t/* if backed up filter already exists, don't create */\n\t\tfor (size_t j = 0; j < cur_filters.num; j++) {\n\t\t\tobs_source_t *cur = cur_filters.array[j];\n\t\t\tconst char *cur_name = cur->context.name;\n\t\t\tif (cur_name && strcmp(cur_name, name) == 0) {\n\t\t\t\tfilter = obs_source_get_ref(cur);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif (!filter)\n\t\t\tfilter = obs_load_source(data);\n\n\t\t/* add filter */\n\t\tif (prev)\n\t\t\tprev->filter_target = filter;\n\t\tprev = filter;\n\t\tfilter->filter_parent = source;\n\t\tda_push_back(new_filters, &filter);\n\n\t\tobs_data_release(data);\n\t}\n\n\tif (prev)\n\t\tprev->filter_target = source;\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\tda_move(source->filters, new_filters);\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\t/* release filters */\n\tfor (size_t i = 0; i < cur_filters.num; i++) {\n\t\tobs_source_t *filter = cur_filters.array[i];\n\t\tobs_source_release(filter);\n\t}\n\n\tda_free(cur_filters);\n}\n\nuint64_t obs_source_get_last_async_ts(const obs_source_t *source)\n{\n\treturn source->async_last_rendered_ts;\n}\n\nobs_canvas_t *obs_source_get_canvas(const obs_source_t *source)\n{\n\treturn obs_weak_canvas_get_canvas(source->canvas);\n}\n"
  },
  {
    "path": "libobs/obs-video-gpu-encode.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs-internal.h\"\n\n#define NBSP \"\\xC2\\xA0\"\nstatic const char *gpu_encode_frame_name = \"gpu_encode_frame\";\nstatic void *gpu_encode_thread(void *data)\n{\n\tstruct obs_core_video_mix *video = data;\n\tuint64_t interval = video_output_get_frame_time(video->video);\n\tDARRAY(obs_encoder_t *) encoders;\n\tint wait_frames = NUM_ENCODE_TEXTURE_FRAMES_TO_WAIT;\n\n\tda_init(encoders);\n\n\tos_set_thread_name(\"obs gpu encode thread\");\n\tconst char *gpu_encode_thread_name = profile_store_name(\n\t\tobs_get_profiler_name_store(), \"obs_gpu_encode_thread(%g\" NBSP \"ms)\", interval / 1000000.);\n\tprofile_register_root(gpu_encode_thread_name, interval);\n\n\twhile (os_sem_wait(video->gpu_encode_semaphore) == 0) {\n\t\tstruct obs_tex_frame tf;\n\t\tuint64_t timestamp;\n\t\tuint64_t lock_key;\n\t\tuint64_t next_key;\n\t\tsize_t lock_count = 0;\n\t\tuint64_t fer_ts = 0;\n\n\t\tif (os_atomic_load_bool(&video->gpu_encode_stop))\n\t\t\tbreak;\n\n\t\tif (wait_frames) {\n\t\t\twait_frames--;\n\t\t\tcontinue;\n\t\t}\n\n\t\tprofile_start(gpu_encode_thread_name);\n\n\t\tos_event_reset(video->gpu_encode_inactive);\n\n\t\t/* -------------- */\n\n\t\tpthread_mutex_lock(&video->gpu_encoder_mutex);\n\n\t\tdeque_pop_front(&video->gpu_encoder_queue, &tf, sizeof(tf));\n\t\ttimestamp = tf.timestamp;\n\t\tlock_key = tf.lock_key;\n\t\tnext_key = tf.lock_key;\n\n\t\tvideo_output_inc_texture_frames(video->video);\n\n\t\tfor (size_t i = 0; i < video->gpu_encoders.num; i++) {\n\t\t\tobs_encoder_t *encoder = obs_encoder_get_ref(video->gpu_encoders.array[i]);\n\t\t\tif (encoder)\n\t\t\t\tda_push_back(encoders, &encoder);\n\t\t}\n\n\t\tpthread_mutex_unlock(&video->gpu_encoder_mutex);\n\n\t\t/* -------------- */\n\n\t\tfor (size_t i = 0; i < encoders.num; i++) {\n\t\t\tstruct encoder_packet pkt = {0};\n\t\t\tbool received = false;\n\t\t\tbool success = false;\n\t\t\tuint32_t skip = 0;\n\n\t\t\tobs_encoder_t *encoder = encoders.array[i];\n\t\t\tobs_weak_encoder_t **paired = encoder->paired_encoders.array;\n\t\t\tsize_t num_paired = encoder->paired_encoders.num;\n\n\t\t\tpkt.timebase_num = encoder->timebase_num * encoder->frame_rate_divisor;\n\t\t\tpkt.timebase_den = encoder->timebase_den;\n\t\t\tpkt.encoder = encoder;\n\n\t\t\tif (encoder->encoder_group && !encoder->start_ts) {\n\t\t\t\tstruct obs_encoder_group *group = encoder->encoder_group;\n\t\t\t\tbool ready = false;\n\t\t\t\tpthread_mutex_lock(&group->mutex);\n\t\t\t\tready = group->start_timestamp == timestamp;\n\t\t\t\tpthread_mutex_unlock(&group->mutex);\n\t\t\t\tif (!ready)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!encoder->first_received && num_paired) {\n\t\t\t\tbool wait_for_audio = false;\n\n\t\t\t\tfor (size_t idx = 0; !wait_for_audio && idx < num_paired; idx++) {\n\t\t\t\t\tobs_encoder_t *enc = obs_weak_encoder_get_encoder(paired[idx]);\n\t\t\t\t\tif (!enc)\n\t\t\t\t\t\tcontinue;\n\n\t\t\t\t\tif (!enc->first_received || enc->first_raw_ts > timestamp) {\n\t\t\t\t\t\twait_for_audio = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tobs_encoder_release(enc);\n\t\t\t\t}\n\n\t\t\t\tif (wait_for_audio)\n\t\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (video_pause_check(&encoder->pause, timestamp))\n\t\t\t\tcontinue;\n\n\t\t\tif (encoder->reconfigure_requested) {\n\t\t\t\tencoder->reconfigure_requested = false;\n\t\t\t\tencoder->info.update(encoder->context.data, encoder->context.settings);\n\t\t\t}\n\n\t\t\t// an explicit counter is used instead of remainder calculation\n\t\t\t// to allow multiple encoders started at the same time to start on\n\t\t\t// the same frame\n\t\t\tskip = encoder->frame_rate_divisor_counter++;\n\t\t\tif (encoder->frame_rate_divisor_counter == encoder->frame_rate_divisor)\n\t\t\t\tencoder->frame_rate_divisor_counter = 0;\n\t\t\tif (skip)\n\t\t\t\tcontinue;\n\n\t\t\tif (!encoder->start_ts)\n\t\t\t\tencoder->start_ts = timestamp;\n\n\t\t\tif (++lock_count == encoders.num)\n\t\t\t\tnext_key = 0;\n\t\t\telse\n\t\t\t\tnext_key++;\n\n\t\t\t/* Get the frame encode request timestamp. This\n\t\t\t * needs to be read just before the encode request.\n\t\t\t */\n\t\t\tfer_ts = os_gettime_ns();\n\n\t\t\tprofile_start(gpu_encode_frame_name);\n\t\t\tif (encoder->info.encode_texture2) {\n\t\t\t\tstruct encoder_texture tex = {0};\n\n\t\t\t\ttex.handle = tf.handle;\n\t\t\t\ttex.tex[0] = tf.tex;\n\t\t\t\ttex.tex[1] = tf.tex_uv;\n\t\t\t\ttex.tex[2] = NULL;\n\t\t\t\tsuccess = encoder->info.encode_texture2(encoder->context.data, &tex, encoder->cur_pts,\n\t\t\t\t\t\t\t\t\tlock_key, &next_key, &pkt, &received);\n\t\t\t} else {\n\t\t\t\tsuccess = encoder->info.encode_texture(encoder->context.data, tf.handle,\n\t\t\t\t\t\t\t\t       encoder->cur_pts, lock_key, &next_key, &pkt,\n\t\t\t\t\t\t\t\t       &received);\n\t\t\t}\n\t\t\tprofile_end(gpu_encode_frame_name);\n\n\t\t\t/* Generate and enqueue the frame timing metrics, namely\n\t\t\t * the CTS (composition time), FER (frame encode request), FERC\n\t\t\t * (frame encode request complete) and current PTS. PTS is used to\n\t\t\t * associate the frame timing data with the encode packet. */\n\t\t\tif (tf.timestamp) {\n\t\t\t\tstruct encoder_packet_time *ept = da_push_back_new(encoder->encoder_packet_times);\n\t\t\t\t// Get the frame encode request complete timestamp\n\t\t\t\tif (success) {\n\t\t\t\t\tept->ferc = os_gettime_ns();\n\t\t\t\t} else {\n\t\t\t\t\t// Encode had error, set ferc to 0\n\t\t\t\t\tept->ferc = 0;\n\t\t\t\t}\n\n\t\t\t\tept->pts = encoder->cur_pts;\n\t\t\t\tept->cts = tf.timestamp;\n\t\t\t\tept->fer = fer_ts;\n\t\t\t}\n\n\t\t\tsend_off_encoder_packet(encoder, success, received, &pkt);\n\n\t\t\tlock_key = next_key;\n\n\t\t\tencoder->cur_pts += encoder->timebase_num * encoder->frame_rate_divisor;\n\t\t}\n\n\t\t/* -------------- */\n\n\t\tpthread_mutex_lock(&video->gpu_encoder_mutex);\n\n\t\ttf.lock_key = next_key;\n\n\t\tif (--tf.count) {\n\t\t\ttf.timestamp += interval;\n\t\t\tdeque_push_front(&video->gpu_encoder_queue, &tf, sizeof(tf));\n\n\t\t\tvideo_output_inc_texture_skipped_frames(video->video);\n\t\t} else {\n\t\t\tdeque_push_back(&video->gpu_encoder_avail_queue, &tf, sizeof(tf));\n\t\t}\n\n\t\tpthread_mutex_unlock(&video->gpu_encoder_mutex);\n\n\t\t/* -------------- */\n\n\t\tos_event_signal(video->gpu_encode_inactive);\n\n\t\tfor (size_t i = 0; i < encoders.num; i++)\n\t\t\tobs_encoder_release(encoders.array[i]);\n\n\t\tda_resize(encoders, 0);\n\n\t\tprofile_end(gpu_encode_thread_name);\n\t\tprofile_reenable_thread();\n\t}\n\n\tda_free(encoders);\n\treturn NULL;\n}\n\nbool init_gpu_encoding(struct obs_core_video_mix *video)\n{\n\tconst struct video_output_info *info = video_output_get_info(video->video);\n\n\tvideo->gpu_encode_stop = false;\n\n\tdeque_reserve(&video->gpu_encoder_avail_queue, NUM_ENCODE_TEXTURES);\n\tfor (size_t i = 0; i < NUM_ENCODE_TEXTURES; i++) {\n\t\tgs_texture_t *tex;\n\t\tgs_texture_t *tex_uv;\n\n\t\tif (info->format == VIDEO_FORMAT_P010) {\n\t\t\tgs_texture_create_p010(&tex, &tex_uv, info->width, info->height,\n\t\t\t\t\t       GS_RENDER_TARGET | GS_SHARED_KM_TEX);\n\t\t} else {\n\t\t\tgs_texture_create_nv12(&tex, &tex_uv, info->width, info->height,\n\t\t\t\t\t       GS_RENDER_TARGET | GS_SHARED_KM_TEX);\n\t\t}\n\t\tif (!tex) {\n\t\t\treturn false;\n\t\t}\n\n#ifdef _WIN32\n\t\tuint32_t handle = gs_texture_get_shared_handle(tex);\n#else\n\t\tuint32_t handle = (uint32_t)-1;\n#endif\n\n\t\tstruct obs_tex_frame frame = {.tex = tex, .tex_uv = tex_uv, .handle = handle};\n\n\t\tdeque_push_back(&video->gpu_encoder_avail_queue, &frame, sizeof(frame));\n\t}\n\n\tif (os_sem_init(&video->gpu_encode_semaphore, 0) != 0)\n\t\treturn false;\n\tif (os_event_init(&video->gpu_encode_inactive, OS_EVENT_TYPE_MANUAL) != 0)\n\t\treturn false;\n\tif (pthread_create(&video->gpu_encode_thread, NULL, gpu_encode_thread, video) != 0)\n\t\treturn false;\n\n\tos_event_signal(video->gpu_encode_inactive);\n\n\tvideo->gpu_encode_thread_initialized = true;\n\treturn true;\n}\n\nvoid stop_gpu_encoding_thread(struct obs_core_video_mix *video)\n{\n\tif (video->gpu_encode_thread_initialized) {\n\t\tos_atomic_set_bool(&video->gpu_encode_stop, true);\n\t\tos_sem_post(video->gpu_encode_semaphore);\n\t\tpthread_join(video->gpu_encode_thread, NULL);\n\t\tvideo->gpu_encode_thread_initialized = false;\n\t}\n}\n\nvoid free_gpu_encoding(struct obs_core_video_mix *video)\n{\n\tif (video->gpu_encode_semaphore) {\n\t\tos_sem_destroy(video->gpu_encode_semaphore);\n\t\tvideo->gpu_encode_semaphore = NULL;\n\t}\n\tif (video->gpu_encode_inactive) {\n\t\tos_event_destroy(video->gpu_encode_inactive);\n\t\tvideo->gpu_encode_inactive = NULL;\n\t}\n\n#define free_deque(x)                                               \\\n\tdo {                                                        \\\n\t\twhile (x.size) {                                    \\\n\t\t\tstruct obs_tex_frame frame;                 \\\n\t\t\tdeque_pop_front(&x, &frame, sizeof(frame)); \\\n\t\t\tgs_texture_destroy(frame.tex);              \\\n\t\t\tgs_texture_destroy(frame.tex_uv);           \\\n\t\t}                                                   \\\n\t\tdeque_free(&x);                                     \\\n\t} while (false)\n\n\tfree_deque(video->gpu_encoder_queue);\n\tfree_deque(video->gpu_encoder_avail_queue);\n#undef free_deque\n}\n"
  },
  {
    "path": "libobs/obs-video.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <time.h>\n#include <stdlib.h>\n\n#include \"obs.h\"\n#include \"obs-internal.h\"\n#include \"graphics/vec4.h\"\n#include \"media-io/format-conversion.h\"\n#include \"media-io/video-frame.h\"\n\n#ifdef _WIN32\n#define WIN32_LEAN_AND_MEAN\n#include <windows.h>\n#endif\n\nstatic uint64_t tick_sources(uint64_t cur_time, uint64_t last_time)\n{\n\tstruct obs_core_data *data = &obs->data;\n\tstruct obs_source *source;\n\tuint64_t delta_time;\n\tfloat seconds;\n\n\tif (!last_time)\n\t\tlast_time = cur_time - obs->video.video_frame_interval_ns;\n\n\tdelta_time = cur_time - last_time;\n\tseconds = (float)((double)delta_time / 1000000000.0);\n\n\t/* ------------------------------------- */\n\t/* call tick callbacks                   */\n\n\tpthread_mutex_lock(&data->draw_callbacks_mutex);\n\n\tfor (size_t i = data->tick_callbacks.num; i > 0; i--) {\n\t\tstruct tick_callback *callback;\n\t\tcallback = data->tick_callbacks.array + (i - 1);\n\t\tcallback->tick(callback->param, seconds);\n\t}\n\n\tpthread_mutex_unlock(&data->draw_callbacks_mutex);\n\n\t/* ------------------------------------- */\n\t/* get an array of all sources to tick   */\n\n\tda_clear(data->sources_to_tick);\n\n\tpthread_mutex_lock(&data->sources_mutex);\n\n\tsource = data->sources;\n\twhile (source) {\n\t\tobs_source_t *s = obs_source_removed(source) ? NULL : obs_source_get_ref(source);\n\t\tif (s)\n\t\t\tda_push_back(data->sources_to_tick, &s);\n\t\tsource = (struct obs_source *)source->context.hh_uuid.next;\n\t}\n\n\tpthread_mutex_unlock(&data->sources_mutex);\n\n\t/* ------------------------------------- */\n\t/* call the tick function of each source */\n\n\tfor (size_t i = 0; i < data->sources_to_tick.num; i++) {\n\t\tobs_source_t *s = data->sources_to_tick.array[i];\n\t\tif (!obs_source_removed(s)) {\n\t\t\tconst uint64_t start = source_profiler_source_tick_start();\n\t\t\tobs_source_video_tick(s, seconds);\n\t\t\tsource_profiler_source_tick_end(s, start);\n\t\t}\n\t\tobs_source_release(s);\n\t}\n\n\treturn cur_time;\n}\n\n/* in obs-display.c */\nextern void render_display(struct obs_display *display);\n\nstatic inline void render_displays(void)\n{\n\tstruct obs_display *display;\n\n\tif (!obs->data.valid)\n\t\treturn;\n\n\tgs_enter_context(obs->video.graphics);\n\n\t/* render extra displays/swaps */\n\tpthread_mutex_lock(&obs->data.displays_mutex);\n\n\tdisplay = obs->data.first_display;\n\twhile (display) {\n\t\trender_display(display);\n\t\tdisplay = display->next;\n\t}\n\n\tpthread_mutex_unlock(&obs->data.displays_mutex);\n\n\tgs_leave_context();\n}\n\nstatic inline void set_render_size(uint32_t width, uint32_t height)\n{\n\tgs_enable_depth_test(false);\n\tgs_set_cull_mode(GS_NEITHER);\n\n\tgs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f);\n\tgs_set_viewport(0, 0, width, height);\n}\n\nstatic inline void unmap_last_surface(struct obs_core_video_mix *video)\n{\n\tfor (int c = 0; c < NUM_CHANNELS; ++c) {\n\t\tif (video->mapped_surfaces[c]) {\n\t\t\tgs_stagesurface_unmap(video->mapped_surfaces[c]);\n\t\t\tvideo->mapped_surfaces[c] = NULL;\n\t\t}\n\t}\n}\n\nstatic inline bool can_reuse_mix_texture(const struct obs_core_video_mix *mix, size_t *idx)\n{\n\tfor (size_t i = 0, num = obs->video.mixes.num; i < num; i++) {\n\t\tconst struct obs_core_video_mix *other = obs->video.mixes.array[i];\n\t\tif (other == mix)\n\t\t\tbreak;\n\t\tif (other->view != mix->view)\n\t\t\tcontinue;\n\t\tif (other->render_space != mix->render_space)\n\t\t\tcontinue;\n\t\tif (other->ovi.base_width != mix->ovi.base_width || other->ovi.base_height != mix->ovi.base_height)\n\t\t\tcontinue;\n\t\tif (!other->texture_rendered)\n\t\t\tcontinue;\n\n\t\t*idx = i;\n\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nstatic inline void draw_mix_texture(const size_t mix_idx)\n{\n\tgs_texture_t *tex = obs->video.mixes.array[mix_idx]->render_texture;\n\tgs_effect_t *effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);\n\tgs_eparam_t *param = gs_effect_get_param_by_name(effect, \"image\");\n\tgs_effect_set_texture_srgb(param, tex);\n\n\tgs_enable_framebuffer_srgb(true);\n\twhile (gs_effect_loop(effect, \"Draw\"))\n\t\tgs_draw_sprite(tex, 0, 0, 0);\n\tgs_enable_framebuffer_srgb(false);\n}\n\nstatic const char *render_main_texture_name = \"render_main_texture\";\nstatic inline void render_main_texture(struct obs_core_video_mix *video)\n{\n\tuint32_t base_width = video->ovi.base_width;\n\tuint32_t base_height = video->ovi.base_height;\n\n\tprofile_start(render_main_texture_name);\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_MAIN_TEXTURE, render_main_texture_name);\n\n\tstruct vec4 clear_color;\n\tvec4_set(&clear_color, 0.0f, 0.0f, 0.0f, 0.0f);\n\n\tgs_set_render_target_with_color_space(video->render_texture, NULL, video->render_space);\n\tgs_clear(GS_CLEAR_COLOR, &clear_color, 1.0f, 0);\n\n\tset_render_size(base_width, base_height);\n\n\tpthread_mutex_lock(&obs->data.draw_callbacks_mutex);\n\n\tfor (size_t i = obs->data.draw_callbacks.num; i > 0; i--) {\n\t\tstruct draw_callback *const callback = obs->data.draw_callbacks.array + (i - 1);\n\t\tcallback->draw(callback->param, base_width, base_height);\n\t}\n\n\tpthread_mutex_unlock(&obs->data.draw_callbacks_mutex);\n\n\t/* In some cases we can reuse a previous mix's texture and save re-rendering everything */\n\tsize_t reuse_idx;\n\tif (can_reuse_mix_texture(video, &reuse_idx))\n\t\tdraw_mix_texture(reuse_idx);\n\telse\n\t\tobs_view_render(video->view);\n\n\tvideo->texture_rendered = true;\n\n\tpthread_mutex_lock(&obs->data.draw_callbacks_mutex);\n\n\tfor (size_t i = 0; i < obs->data.rendered_callbacks.num; ++i) {\n\t\tstruct rendered_callback *const callback = &obs->data.rendered_callbacks.array[i];\n\t\tcallback->rendered(callback->param);\n\t}\n\n\tpthread_mutex_unlock(&obs->data.draw_callbacks_mutex);\n\n\tGS_DEBUG_MARKER_END();\n\tprofile_end(render_main_texture_name);\n}\n\nstatic inline gs_effect_t *get_scale_effect_internal(struct obs_core_video_mix *mix)\n{\n\tstruct obs_core_video *video = &obs->video;\n\tconst struct video_output_info *info = video_output_get_info(mix->video);\n\n\t/* if the dimension is under half the size of the original image,\n\t * bicubic/lanczos can't sample enough pixels to create an accurate\n\t * image, so use the bilinear low resolution effect instead */\n\tif (info->width < (mix->ovi.base_width / 2) && info->height < (mix->ovi.base_height / 2)) {\n\t\treturn video->bilinear_lowres_effect;\n\t}\n\n\tswitch (mix->ovi.scale_type) {\n\tcase OBS_SCALE_BILINEAR:\n\t\treturn video->default_effect;\n\tcase OBS_SCALE_LANCZOS:\n\t\treturn video->lanczos_effect;\n\tcase OBS_SCALE_AREA:\n\t\treturn video->area_effect;\n\tcase OBS_SCALE_BICUBIC:\n\tdefault:;\n\t}\n\n\treturn video->bicubic_effect;\n}\n\nstatic inline bool resolution_close(struct obs_core_video_mix *mix, uint32_t width, uint32_t height)\n{\n\tlong width_cmp = (long)mix->ovi.base_width - (long)width;\n\tlong height_cmp = (long)mix->ovi.base_height - (long)height;\n\n\treturn labs(width_cmp) <= 16 && labs(height_cmp) <= 16;\n}\n\nstatic inline gs_effect_t *get_scale_effect(struct obs_core_video_mix *mix, uint32_t width, uint32_t height)\n{\n\tstruct obs_core_video *video = &obs->video;\n\n\tif (resolution_close(mix, width, height)) {\n\t\treturn video->default_effect;\n\t} else {\n\t\t/* if the scale method couldn't be loaded, use either bicubic\n\t\t * or bilinear by default */\n\t\tgs_effect_t *effect = get_scale_effect_internal(mix);\n\t\tif (!effect)\n\t\t\teffect = !!video->bicubic_effect ? video->bicubic_effect : video->default_effect;\n\t\treturn effect;\n\t}\n}\n\nstatic const char *render_output_texture_name = \"render_output_texture\";\nstatic inline gs_texture_t *render_output_texture(struct obs_core_video_mix *mix)\n{\n\tstruct obs_video_info *const ovi = &mix->ovi;\n\tgs_texture_t *texture = mix->render_texture;\n\tgs_texture_t *target = mix->output_texture;\n\tconst uint32_t width = gs_texture_get_width(target);\n\tconst uint32_t height = gs_texture_get_height(target);\n\tif ((width == ovi->base_width) && (height == ovi->base_height))\n\t\treturn texture;\n\n\tprofile_start(render_output_texture_name);\n\n\tgs_effect_t *effect = get_scale_effect(mix, width, height);\n\tgs_technique_t *tech = gs_effect_get_technique(effect, \"Draw\");\n\n\tgs_eparam_t *image = gs_effect_get_param_by_name(effect, \"image\");\n\tgs_eparam_t *bres = gs_effect_get_param_by_name(effect, \"base_dimension\");\n\tgs_eparam_t *bres_i = gs_effect_get_param_by_name(effect, \"base_dimension_i\");\n\tsize_t passes, i;\n\n\tgs_set_render_target(target, NULL);\n\tset_render_size(width, height);\n\n\tif (bres) {\n\t\tstruct vec2 base;\n\t\tvec2_set(&base, (float)mix->ovi.base_width, (float)mix->ovi.base_height);\n\t\tgs_effect_set_vec2(bres, &base);\n\t}\n\n\tif (bres_i) {\n\t\tstruct vec2 base_i;\n\t\tvec2_set(&base_i, 1.0f / (float)mix->ovi.base_width, 1.0f / (float)mix->ovi.base_height);\n\t\tgs_effect_set_vec2(bres_i, &base_i);\n\t}\n\n\tgs_effect_set_texture_srgb(image, texture);\n\n\tgs_enable_framebuffer_srgb(true);\n\tgs_enable_blending(false);\n\tpasses = gs_technique_begin(tech);\n\tfor (i = 0; i < passes; i++) {\n\t\tgs_technique_begin_pass(tech, i);\n\t\tgs_draw_sprite(texture, 0, width, height);\n\t\tgs_technique_end_pass(tech);\n\t}\n\tgs_technique_end(tech);\n\tgs_enable_blending(true);\n\tgs_enable_framebuffer_srgb(false);\n\n\tprofile_end(render_output_texture_name);\n\n\treturn target;\n}\n\nstatic void render_convert_plane(gs_effect_t *effect, gs_texture_t *target, const char *tech_name)\n{\n\tgs_technique_t *tech = gs_effect_get_technique(effect, tech_name);\n\n\tconst uint32_t width = gs_texture_get_width(target);\n\tconst uint32_t height = gs_texture_get_height(target);\n\n\tgs_set_render_target(target, NULL);\n\tset_render_size(width, height);\n\n\tsize_t passes = gs_technique_begin(tech);\n\tfor (size_t i = 0; i < passes; i++) {\n\t\tgs_technique_begin_pass(tech, i);\n\t\tgs_draw(GS_TRIS, 0, 3);\n\t\tgs_technique_end_pass(tech);\n\t}\n\tgs_technique_end(tech);\n}\n\nstatic const char *render_convert_texture_name = \"render_convert_texture\";\nstatic void render_convert_texture(struct obs_core_video_mix *video, gs_texture_t *const *const convert_textures,\n\t\t\t\t   gs_texture_t *texture)\n{\n\tprofile_start(render_convert_texture_name);\n\n\tgs_effect_t *effect = obs->video.conversion_effect;\n\tgs_eparam_t *color_vec0 = gs_effect_get_param_by_name(effect, \"color_vec0\");\n\tgs_eparam_t *color_vec1 = gs_effect_get_param_by_name(effect, \"color_vec1\");\n\tgs_eparam_t *color_vec2 = gs_effect_get_param_by_name(effect, \"color_vec2\");\n\tgs_eparam_t *image = gs_effect_get_param_by_name(effect, \"image\");\n\tgs_eparam_t *width_i = gs_effect_get_param_by_name(effect, \"width_i\");\n\tgs_eparam_t *height_i = gs_effect_get_param_by_name(effect, \"height_i\");\n\tgs_eparam_t *sdr_white_nits_over_maximum = gs_effect_get_param_by_name(effect, \"sdr_white_nits_over_maximum\");\n\tgs_eparam_t *hdr_lw = gs_effect_get_param_by_name(effect, \"hdr_lw\");\n\n\tstruct vec4 vec0, vec1, vec2;\n\tvec4_set(&vec0, video->color_matrix[4], video->color_matrix[5], video->color_matrix[6], video->color_matrix[7]);\n\tvec4_set(&vec1, video->color_matrix[0], video->color_matrix[1], video->color_matrix[2], video->color_matrix[3]);\n\tvec4_set(&vec2, video->color_matrix[8], video->color_matrix[9], video->color_matrix[10],\n\t\t video->color_matrix[11]);\n\n\tgs_enable_blending(false);\n\n\tif (convert_textures[0]) {\n\t\tconst float hdr_nominal_peak_level = obs->video.hdr_nominal_peak_level;\n\t\tconst float multiplier = obs_get_video_sdr_white_level() / 10000.f;\n\t\tgs_effect_set_texture(image, texture);\n\t\tgs_effect_set_vec4(color_vec0, &vec0);\n\t\tgs_effect_set_float(sdr_white_nits_over_maximum, multiplier);\n\t\tgs_effect_set_float(hdr_lw, hdr_nominal_peak_level);\n\t\trender_convert_plane(effect, convert_textures[0], video->conversion_techs[0]);\n\n\t\tif (convert_textures[1]) {\n\t\t\tgs_effect_set_texture(image, texture);\n\t\t\tgs_effect_set_vec4(color_vec1, &vec1);\n\t\t\tif (!convert_textures[2])\n\t\t\t\tgs_effect_set_vec4(color_vec2, &vec2);\n\t\t\tgs_effect_set_float(width_i, video->conversion_width_i);\n\t\t\tgs_effect_set_float(height_i, video->conversion_height_i);\n\t\t\tgs_effect_set_float(sdr_white_nits_over_maximum, multiplier);\n\t\t\tgs_effect_set_float(hdr_lw, hdr_nominal_peak_level);\n\t\t\trender_convert_plane(effect, convert_textures[1], video->conversion_techs[1]);\n\n\t\t\tif (convert_textures[2]) {\n\t\t\t\tgs_effect_set_texture(image, texture);\n\t\t\t\tgs_effect_set_vec4(color_vec2, &vec2);\n\t\t\t\tgs_effect_set_float(width_i, video->conversion_width_i);\n\t\t\t\tgs_effect_set_float(height_i, video->conversion_height_i);\n\t\t\t\tgs_effect_set_float(sdr_white_nits_over_maximum, multiplier);\n\t\t\t\tgs_effect_set_float(hdr_lw, hdr_nominal_peak_level);\n\t\t\t\trender_convert_plane(effect, convert_textures[2], video->conversion_techs[2]);\n\t\t\t}\n\t\t}\n\t}\n\n\tgs_enable_blending(true);\n\n\tvideo->texture_converted = true;\n\n\tprofile_end(render_convert_texture_name);\n}\n\nstatic const char *stage_output_texture_name = \"stage_output_texture\";\nstatic inline void stage_output_texture(struct obs_core_video_mix *video, int cur_texture,\n\t\t\t\t\tgs_texture_t *const *const convert_textures, gs_texture_t *output_texture,\n\t\t\t\t\tgs_stagesurf_t *const *const copy_surfaces, size_t channel_count)\n{\n\tprofile_start(stage_output_texture_name);\n\n\tunmap_last_surface(video);\n\n\tif (!video->gpu_conversion) {\n\t\tgs_stagesurf_t *copy = copy_surfaces[0];\n\t\tif (copy)\n\t\t\tgs_stage_texture(copy, output_texture);\n\t\tvideo->active_copy_surfaces[cur_texture][0] = copy;\n\n\t\tfor (size_t i = 1; i < NUM_CHANNELS; ++i)\n\t\t\tvideo->active_copy_surfaces[cur_texture][i] = NULL;\n\n\t\tvideo->textures_copied[cur_texture] = true;\n\t} else if (video->texture_converted) {\n\t\tfor (size_t i = 0; i < channel_count; i++) {\n\t\t\tgs_stagesurf_t *copy = copy_surfaces[i];\n\t\t\tif (copy)\n\t\t\t\tgs_stage_texture(copy, convert_textures[i]);\n\t\t\tvideo->active_copy_surfaces[cur_texture][i] = copy;\n\t\t}\n\n\t\tfor (size_t i = channel_count; i < NUM_CHANNELS; ++i)\n\t\t\tvideo->active_copy_surfaces[cur_texture][i] = NULL;\n\n\t\tvideo->textures_copied[cur_texture] = true;\n\t}\n\n\tprofile_end(stage_output_texture_name);\n}\n\nstatic inline bool queue_frame(struct obs_core_video_mix *video, bool raw_active, struct obs_vframe_info *vframe_info)\n{\n\tbool duplicate = !video->gpu_encoder_avail_queue.size ||\n\t\t\t (video->gpu_encoder_queue.size && vframe_info->count > 1);\n\n\tif (duplicate) {\n\t\tstruct obs_tex_frame *tf =\n\t\t\tdeque_data(&video->gpu_encoder_queue, video->gpu_encoder_queue.size - sizeof(*tf));\n\n\t\t/* texture-based encoding is stopping */\n\t\tif (!tf) {\n\t\t\treturn false;\n\t\t}\n\n\t\ttf->count++;\n\t\tos_sem_post(video->gpu_encode_semaphore);\n\t\tgoto finish;\n\t}\n\n\tstruct obs_tex_frame tf;\n\tdeque_pop_front(&video->gpu_encoder_avail_queue, &tf, sizeof(tf));\n\n\tif (tf.released) {\n#ifdef _WIN32\n\t\tgs_texture_acquire_sync(tf.tex, tf.lock_key, GS_WAIT_INFINITE);\n#endif\n\t\ttf.released = false;\n\t}\n\n\t/* the vframe_info->count > 1 case causing a copy can only happen if by\n\t * some chance the very first frame has to be duplicated for whatever\n\t * reason.  otherwise, it goes to the 'duplicate' case above, which\n\t * will ensure better performance. */\n\tif (raw_active || vframe_info->count > 1) {\n\t\tgs_copy_texture(tf.tex, video->convert_textures_encode[0]);\n#ifndef _WIN32\n\t\t/* Y and UV textures are views of the same texture on D3D, and\n\t\t * gs_copy_texture will copy all views of the underlying\n\t\t * texture. On other platforms, these are two distinct textures\n\t\t * that must be copied separately. */\n\t\tgs_copy_texture(tf.tex_uv, video->convert_textures_encode[1]);\n#endif\n\t} else {\n\t\tgs_texture_t *tex = video->convert_textures_encode[0];\n\t\tgs_texture_t *tex_uv = video->convert_textures_encode[1];\n\n\t\tvideo->convert_textures_encode[0] = tf.tex;\n\t\tvideo->convert_textures_encode[1] = tf.tex_uv;\n\n\t\ttf.tex = tex;\n\t\ttf.tex_uv = tex_uv;\n\t}\n\n\ttf.count = 1;\n\ttf.timestamp = vframe_info->timestamp;\n\ttf.released = true;\n#ifdef _WIN32\n\ttf.handle = gs_texture_get_shared_handle(tf.tex);\n\tgs_texture_release_sync(tf.tex, ++tf.lock_key);\n#endif\n\tdeque_push_back(&video->gpu_encoder_queue, &tf, sizeof(tf));\n\n\tos_sem_post(video->gpu_encode_semaphore);\n\nfinish:\n\treturn --vframe_info->count;\n}\n\nextern void full_stop(struct obs_encoder *encoder);\n\nstatic inline void encode_gpu(struct obs_core_video_mix *video, bool raw_active, struct obs_vframe_info *vframe_info)\n{\n\twhile (queue_frame(video, raw_active, vframe_info))\n\t\t;\n}\n\nstatic const char *output_gpu_encoders_name = \"output_gpu_encoders\";\nstatic void output_gpu_encoders(struct obs_core_video_mix *video, bool raw_active)\n{\n\tprofile_start(output_gpu_encoders_name);\n\n\tif (!video->texture_converted)\n\t\tgoto end;\n\tif (!video->vframe_info_buffer_gpu.size)\n\t\tgoto end;\n\n\tstruct obs_vframe_info vframe_info;\n\tdeque_pop_front(&video->vframe_info_buffer_gpu, &vframe_info, sizeof(vframe_info));\n\n\tpthread_mutex_lock(&video->gpu_encoder_mutex);\n\tencode_gpu(video, raw_active, &vframe_info);\n\tpthread_mutex_unlock(&video->gpu_encoder_mutex);\n\nend:\n\tprofile_end(output_gpu_encoders_name);\n}\n\nstatic inline void render_video(struct obs_core_video_mix *video, bool raw_active, const bool gpu_active,\n\t\t\t\tint cur_texture)\n{\n\tgs_begin_scene();\n\n\tgs_enable_depth_test(false);\n\tgs_set_cull_mode(GS_NEITHER);\n\n\trender_main_texture(video);\n\n\tif (raw_active || gpu_active) {\n\t\tgs_texture_t *const *convert_textures = video->convert_textures;\n\t\tgs_stagesurf_t *const *copy_surfaces = video->copy_surfaces[cur_texture];\n\t\tsize_t channel_count = NUM_CHANNELS;\n\t\tgs_texture_t *output_texture = render_output_texture(video);\n\n\t\tif (gpu_active) {\n\t\t\tconvert_textures = video->convert_textures_encode;\n#ifdef _WIN32\n\t\t\tcopy_surfaces = video->copy_surfaces_encode;\n\t\t\tchannel_count = 1;\n#endif\n\t\t\tgs_flush();\n\t\t}\n\n\t\tif (video->gpu_conversion) {\n\t\t\trender_convert_texture(video, convert_textures, output_texture);\n\t\t}\n\n\t\tif (gpu_active) {\n\t\t\tgs_flush();\n\t\t\toutput_gpu_encoders(video, raw_active);\n\t\t}\n\n\t\tif (raw_active) {\n\t\t\tstage_output_texture(video, cur_texture, convert_textures, output_texture, copy_surfaces,\n\t\t\t\t\t     channel_count);\n\t\t}\n\t}\n\n\tgs_set_render_target(NULL, NULL);\n\tgs_enable_blending(true);\n\n\tgs_end_scene();\n}\n\nstatic inline bool download_frame(struct obs_core_video_mix *video, int prev_texture, struct video_data *frame)\n{\n\tif (!video->textures_copied[prev_texture])\n\t\treturn false;\n\n\tfor (int channel = 0; channel < NUM_CHANNELS; ++channel) {\n\t\tgs_stagesurf_t *surface = video->active_copy_surfaces[prev_texture][channel];\n\t\tif (surface) {\n\t\t\tif (!gs_stagesurface_map(surface, &frame->data[channel], &frame->linesize[channel]))\n\t\t\t\treturn false;\n\n\t\t\tvideo->mapped_surfaces[channel] = surface;\n\t\t}\n\t}\n\treturn true;\n}\n\nstatic const uint8_t *set_gpu_converted_plane(uint32_t width, uint32_t height, uint32_t linesize_input,\n\t\t\t\t\t      uint32_t linesize_output, const uint8_t *in, uint8_t *out)\n{\n\tif ((width == linesize_input) && (width == linesize_output)) {\n\t\tsize_t total = (size_t)width * (size_t)height;\n\t\tmemcpy(out, in, total);\n\t\tin += total;\n\t} else {\n\t\tfor (size_t y = 0; y < height; y++) {\n\t\t\tmemcpy(out, in, width);\n\t\t\tout += linesize_output;\n\t\t\tin += linesize_input;\n\t\t}\n\t}\n\n\treturn in;\n}\n\nstatic void set_gpu_converted_data(struct video_frame *output, const struct video_data *input,\n\t\t\t\t   const struct video_output_info *info)\n{\n\tswitch (info->format) {\n\tcase VIDEO_FORMAT_I420: {\n\t\tconst uint32_t width = info->width;\n\t\tconst uint32_t height = info->height;\n\n\t\tset_gpu_converted_plane(width, height, input->linesize[0], output->linesize[0], input->data[0],\n\t\t\t\t\toutput->data[0]);\n\n\t\tconst uint32_t width_d2 = width / 2;\n\t\tconst uint32_t height_d2 = height / 2;\n\n\t\tset_gpu_converted_plane(width_d2, height_d2, input->linesize[1], output->linesize[1], input->data[1],\n\t\t\t\t\toutput->data[1]);\n\n\t\tset_gpu_converted_plane(width_d2, height_d2, input->linesize[2], output->linesize[2], input->data[2],\n\t\t\t\t\toutput->data[2]);\n\n\t\tbreak;\n\t}\n\tcase VIDEO_FORMAT_NV12: {\n\t\tconst uint32_t width = info->width;\n\t\tconst uint32_t height = info->height;\n\t\tconst uint32_t height_d2 = height / 2;\n\t\tif (input->linesize[1]) {\n\t\t\tset_gpu_converted_plane(width, height, input->linesize[0], output->linesize[0], input->data[0],\n\t\t\t\t\t\toutput->data[0]);\n\t\t\tset_gpu_converted_plane(width, height_d2, input->linesize[1], output->linesize[1],\n\t\t\t\t\t\tinput->data[1], output->data[1]);\n\t\t} else {\n\t\t\tconst uint8_t *const in_uv = set_gpu_converted_plane(width, height, input->linesize[0],\n\t\t\t\t\t\t\t\t\t     output->linesize[0], input->data[0],\n\t\t\t\t\t\t\t\t\t     output->data[0]);\n\t\t\tset_gpu_converted_plane(width, height_d2, input->linesize[0], output->linesize[1], in_uv,\n\t\t\t\t\t\toutput->data[1]);\n\t\t}\n\n\t\tbreak;\n\t}\n\tcase VIDEO_FORMAT_I444: {\n\t\tconst uint32_t width = info->width;\n\t\tconst uint32_t height = info->height;\n\n\t\tset_gpu_converted_plane(width, height, input->linesize[0], output->linesize[0], input->data[0],\n\t\t\t\t\toutput->data[0]);\n\n\t\tset_gpu_converted_plane(width, height, input->linesize[1], output->linesize[1], input->data[1],\n\t\t\t\t\toutput->data[1]);\n\n\t\tset_gpu_converted_plane(width, height, input->linesize[2], output->linesize[2], input->data[2],\n\t\t\t\t\toutput->data[2]);\n\n\t\tbreak;\n\t}\n\tcase VIDEO_FORMAT_I010: {\n\t\tconst uint32_t width = info->width;\n\t\tconst uint32_t height = info->height;\n\n\t\tset_gpu_converted_plane(width * 2, height, input->linesize[0], output->linesize[0], input->data[0],\n\t\t\t\t\toutput->data[0]);\n\n\t\tconst uint32_t height_d2 = height / 2;\n\n\t\tset_gpu_converted_plane(width, height_d2, input->linesize[1], output->linesize[1], input->data[1],\n\t\t\t\t\toutput->data[1]);\n\n\t\tset_gpu_converted_plane(width, height_d2, input->linesize[2], output->linesize[2], input->data[2],\n\t\t\t\t\toutput->data[2]);\n\n\t\tbreak;\n\t}\n\tcase VIDEO_FORMAT_P010: {\n\t\tconst uint32_t width_x2 = info->width * 2;\n\t\tconst uint32_t height = info->height;\n\t\tconst uint32_t height_d2 = height / 2;\n\t\tif (input->linesize[1]) {\n\t\t\tset_gpu_converted_plane(width_x2, height, input->linesize[0], output->linesize[0],\n\t\t\t\t\t\tinput->data[0], output->data[0]);\n\t\t\tset_gpu_converted_plane(width_x2, height_d2, input->linesize[1], output->linesize[1],\n\t\t\t\t\t\tinput->data[1], output->data[1]);\n\t\t} else {\n\t\t\tconst uint8_t *const in_uv = set_gpu_converted_plane(width_x2, height, input->linesize[0],\n\t\t\t\t\t\t\t\t\t     output->linesize[0], input->data[0],\n\t\t\t\t\t\t\t\t\t     output->data[0]);\n\t\t\tset_gpu_converted_plane(width_x2, height_d2, input->linesize[0], output->linesize[1], in_uv,\n\t\t\t\t\t\toutput->data[1]);\n\t\t}\n\n\t\tbreak;\n\t}\n\tcase VIDEO_FORMAT_P216: {\n\t\tconst uint32_t width_x2 = info->width * 2;\n\t\tconst uint32_t height = info->height;\n\n\t\tset_gpu_converted_plane(width_x2, height, input->linesize[0], output->linesize[0], input->data[0],\n\t\t\t\t\toutput->data[0]);\n\n\t\tset_gpu_converted_plane(width_x2, height, input->linesize[1], output->linesize[1], input->data[1],\n\t\t\t\t\toutput->data[1]);\n\n\t\tbreak;\n\t}\n\tcase VIDEO_FORMAT_P416: {\n\t\tconst uint32_t height = info->height;\n\n\t\tset_gpu_converted_plane(info->width * 2, height, input->linesize[0], output->linesize[0],\n\t\t\t\t\tinput->data[0], output->data[0]);\n\n\t\tset_gpu_converted_plane(info->width * 4, height, input->linesize[1], output->linesize[1],\n\t\t\t\t\tinput->data[1], output->data[1]);\n\n\t\tbreak;\n\t}\n\n\tcase VIDEO_FORMAT_NONE:\n\tcase VIDEO_FORMAT_YVYU:\n\tcase VIDEO_FORMAT_YUY2:\n\tcase VIDEO_FORMAT_UYVY:\n\tcase VIDEO_FORMAT_RGBA:\n\tcase VIDEO_FORMAT_BGRA:\n\tcase VIDEO_FORMAT_BGRX:\n\tcase VIDEO_FORMAT_Y800:\n\tcase VIDEO_FORMAT_BGR3:\n\tcase VIDEO_FORMAT_I412:\n\tcase VIDEO_FORMAT_I422:\n\tcase VIDEO_FORMAT_I210:\n\tcase VIDEO_FORMAT_I40A:\n\tcase VIDEO_FORMAT_I42A:\n\tcase VIDEO_FORMAT_YUVA:\n\tcase VIDEO_FORMAT_YA2L:\n\tcase VIDEO_FORMAT_AYUV:\n\tcase VIDEO_FORMAT_V210:\n\tcase VIDEO_FORMAT_R10L:\n\t\t/* unimplemented */\n\t\t;\n\t}\n}\n\nstatic inline void copy_rgbx_frame(struct video_frame *output, const struct video_data *input,\n\t\t\t\t   const struct video_output_info *info)\n{\n\tuint8_t *in_ptr = input->data[0];\n\tuint8_t *out_ptr = output->data[0];\n\n\t/* if the line sizes match, do a single copy */\n\tif (input->linesize[0] == output->linesize[0]) {\n\t\tmemcpy(out_ptr, in_ptr, (size_t)input->linesize[0] * (size_t)info->height);\n\t} else {\n\t\tconst size_t copy_size = (size_t)info->width * 4;\n\t\tfor (size_t y = 0; y < info->height; y++) {\n\t\t\tmemcpy(out_ptr, in_ptr, copy_size);\n\t\t\tin_ptr += input->linesize[0];\n\t\t\tout_ptr += output->linesize[0];\n\t\t}\n\t}\n}\n\nstatic inline void output_video_data(struct obs_core_video_mix *video, struct video_data *input_frame, int count)\n{\n\tconst struct video_output_info *info;\n\tstruct video_frame output_frame;\n\tbool locked;\n\n\tinfo = video_output_get_info(video->video);\n\n\tlocked = video_output_lock_frame(video->video, &output_frame, count, input_frame->timestamp);\n\tif (locked) {\n\t\tif (video->gpu_conversion) {\n\t\t\tset_gpu_converted_data(&output_frame, input_frame, info);\n\t\t} else {\n\t\t\tcopy_rgbx_frame(&output_frame, input_frame, info);\n\t\t}\n\n\t\tvideo_output_unlock_frame(video->video);\n\t}\n}\n\nvoid add_ready_encoder_group(obs_encoder_t *encoder)\n{\n\tobs_weak_encoder_t *weak = obs_encoder_get_weak_encoder(encoder);\n\tpthread_mutex_lock(&obs->video.encoder_group_mutex);\n\tda_push_back(obs->video.ready_encoder_groups, &weak);\n\tpthread_mutex_unlock(&obs->video.encoder_group_mutex);\n}\n\nstatic inline void video_sleep(struct obs_core_video *video, uint64_t *p_time, uint64_t interval_ns)\n{\n\tstruct obs_vframe_info vframe_info;\n\tuint64_t cur_time = *p_time;\n\tuint64_t t = cur_time + interval_ns;\n\tint count;\n\n\tif (os_sleepto_ns(t)) {\n\t\t*p_time = t;\n\t\tcount = 1;\n\t} else {\n\t\tconst uint64_t udiff = os_gettime_ns() - cur_time;\n\t\tint64_t diff;\n\t\tmemcpy(&diff, &udiff, sizeof(diff));\n\t\tconst uint64_t clamped_diff = (diff > (int64_t)interval_ns) ? (uint64_t)diff : interval_ns;\n\t\tcount = (int)(clamped_diff / interval_ns);\n\t\t*p_time = cur_time + interval_ns * count;\n\t}\n\n\tvideo->total_frames += count;\n\tvideo->lagged_frames += count - 1;\n\n\tvframe_info.timestamp = cur_time;\n\tvframe_info.count = count;\n\n\tpthread_mutex_lock(&video->encoder_group_mutex);\n\tfor (size_t i = 0; i < video->ready_encoder_groups.num; i++) {\n\t\tobs_encoder_t *encoder = obs_weak_encoder_get_encoder(video->ready_encoder_groups.array[i]);\n\t\tobs_weak_encoder_release(video->ready_encoder_groups.array[i]);\n\t\tif (!encoder)\n\t\t\tcontinue;\n\n\t\tif (encoder->encoder_group) {\n\t\t\tstruct obs_encoder_group *group = encoder->encoder_group;\n\t\t\tpthread_mutex_lock(&group->mutex);\n\t\t\tif (group->num_encoders_started >= group->encoders.num && !group->start_timestamp)\n\t\t\t\tgroup->start_timestamp = *p_time;\n\t\t\tpthread_mutex_unlock(&group->mutex);\n\t\t}\n\t\tobs_encoder_release(encoder);\n\t}\n\tda_clear(video->ready_encoder_groups);\n\tpthread_mutex_unlock(&video->encoder_group_mutex);\n\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0, num = obs->video.mixes.num; i < num; i++) {\n\t\tstruct obs_core_video_mix *video = obs->video.mixes.array[i];\n\t\tbool raw_active = video->raw_was_active;\n\t\tbool gpu_active = video->gpu_was_active;\n\n\t\tif (raw_active)\n\t\t\tdeque_push_back(&video->vframe_info_buffer, &vframe_info, sizeof(vframe_info));\n\t\tif (gpu_active)\n\t\t\tdeque_push_back(&video->vframe_info_buffer_gpu, &vframe_info, sizeof(vframe_info));\n\t}\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n}\n\nstatic const char *output_frame_gs_context_name = \"gs_context(video->graphics)\";\nstatic const char *output_frame_render_video_name = \"render_video\";\nstatic const char *output_frame_download_frame_name = \"download_frame\";\nstatic const char *output_frame_gs_flush_name = \"gs_flush\";\nstatic const char *output_frame_output_video_data_name = \"output_video_data\";\nstatic inline void output_frame(struct obs_core_video_mix *video)\n{\n\tconst bool raw_active = video->raw_was_active;\n\tconst bool gpu_active = video->gpu_was_active;\n\n\tint cur_texture = video->cur_texture;\n\tint prev_texture = cur_texture == 0 ? NUM_TEXTURES - 1 : cur_texture - 1;\n\tstruct video_data frame;\n\tbool frame_ready = 0;\n\n\tmemset(&frame, 0, sizeof(struct video_data));\n\n\tprofile_start(output_frame_gs_context_name);\n\tgs_enter_context(obs->video.graphics);\n\n\tprofile_start(output_frame_render_video_name);\n\tGS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_RENDER_VIDEO, output_frame_render_video_name);\n\trender_video(video, raw_active, gpu_active, cur_texture);\n\tGS_DEBUG_MARKER_END();\n\tprofile_end(output_frame_render_video_name);\n\n\tif (raw_active) {\n\t\tprofile_start(output_frame_download_frame_name);\n\t\tframe_ready = download_frame(video, prev_texture, &frame);\n\t\tprofile_end(output_frame_download_frame_name);\n\t}\n\n\tprofile_start(output_frame_gs_flush_name);\n\tgs_flush();\n\tprofile_end(output_frame_gs_flush_name);\n\n\tgs_leave_context();\n\tprofile_end(output_frame_gs_context_name);\n\n\tif (raw_active && frame_ready) {\n\t\tstruct obs_vframe_info vframe_info;\n\t\tdeque_pop_front(&video->vframe_info_buffer, &vframe_info, sizeof(vframe_info));\n\n\t\tframe.timestamp = vframe_info.timestamp;\n\t\tprofile_start(output_frame_output_video_data_name);\n\t\toutput_video_data(video, &frame, vframe_info.count);\n\t\tprofile_end(output_frame_output_video_data_name);\n\t}\n\n\tif (++video->cur_texture == NUM_TEXTURES)\n\t\tvideo->cur_texture = 0;\n}\n\nstatic inline void output_frames(void)\n{\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0, num = obs->video.mixes.num; i < num; i++) {\n\t\tstruct obs_core_video_mix *mix = obs->video.mixes.array[i];\n\t\tif (mix->view) {\n\t\t\toutput_frame(mix);\n\t\t} else {\n\t\t\tobs->video.mixes.array[i] = NULL;\n\t\t\tobs_free_video_mix(mix);\n\t\t\tda_erase(obs->video.mixes, i);\n\t\t\ti--;\n\t\t\tnum--;\n\t\t}\n\t}\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n}\n\n#define NBSP \"\\xC2\\xA0\"\n\nstatic void clear_base_frame_data(struct obs_core_video_mix *video)\n{\n\tvideo->texture_rendered = false;\n\tvideo->texture_converted = false;\n\tdeque_free(&video->vframe_info_buffer);\n\tvideo->cur_texture = 0;\n}\n\nstatic void clear_raw_frame_data(struct obs_core_video_mix *video)\n{\n\tmemset(video->textures_copied, 0, sizeof(video->textures_copied));\n\tdeque_free(&video->vframe_info_buffer);\n}\n\nstatic void clear_gpu_frame_data(struct obs_core_video_mix *video)\n{\n\tdeque_free(&video->vframe_info_buffer_gpu);\n}\n\nextern THREAD_LOCAL bool is_graphics_thread;\n\nstatic void execute_graphics_tasks(void)\n{\n\tstruct obs_core_video *video = &obs->video;\n\tbool tasks_remaining = true;\n\n\twhile (tasks_remaining) {\n\t\tpthread_mutex_lock(&video->task_mutex);\n\t\tif (video->tasks.size) {\n\t\t\tstruct obs_task_info info;\n\t\t\tdeque_pop_front(&video->tasks, &info, sizeof(info));\n\t\t\tinfo.task(info.param);\n\t\t}\n\t\ttasks_remaining = !!video->tasks.size;\n\t\tpthread_mutex_unlock(&video->task_mutex);\n\t}\n}\n\n#ifdef _WIN32\n\nstruct winrt_exports {\n\tvoid (*winrt_initialize)();\n\tvoid (*winrt_uninitialize)();\n\tstruct winrt_disaptcher *(*winrt_dispatcher_init)();\n\tvoid (*winrt_dispatcher_free)(struct winrt_disaptcher *dispatcher);\n\tvoid (*winrt_capture_thread_start)();\n\tvoid (*winrt_capture_thread_stop)();\n};\n\n#define WINRT_IMPORT(func)                                        \\\n\tdo {                                                      \\\n\t\texports->func = os_dlsym(module, #func);          \\\n\t\tif (!exports->func) {                             \\\n\t\t\tsuccess = false;                          \\\n\t\t\tblog(LOG_ERROR,                           \\\n\t\t\t     \"Could not load function '%s' from \" \\\n\t\t\t     \"module '%s'\",                       \\\n\t\t\t     #func, module_name);                 \\\n\t\t}                                                 \\\n\t} while (false)\n\nstatic bool load_winrt_imports(struct winrt_exports *exports, void *module, const char *module_name)\n{\n\tbool success = true;\n\n\tWINRT_IMPORT(winrt_initialize);\n\tWINRT_IMPORT(winrt_uninitialize);\n\tWINRT_IMPORT(winrt_dispatcher_init);\n\tWINRT_IMPORT(winrt_dispatcher_free);\n\tWINRT_IMPORT(winrt_capture_thread_start);\n\tWINRT_IMPORT(winrt_capture_thread_stop);\n\n\treturn success;\n}\n\nstruct winrt_state {\n\tbool loaded;\n\tvoid *winrt_module;\n\tstruct winrt_exports exports;\n\tstruct winrt_disaptcher *dispatcher;\n};\n\nstatic void init_winrt_state(struct winrt_state *winrt)\n{\n\tstatic const char *const module_name = \"libobs-winrt\";\n\n\twinrt->winrt_module = os_dlopen(module_name);\n\twinrt->loaded = winrt->winrt_module && load_winrt_imports(&winrt->exports, winrt->winrt_module, module_name);\n\twinrt->dispatcher = NULL;\n\tif (winrt->loaded) {\n\t\twinrt->exports.winrt_initialize();\n\t\twinrt->dispatcher = winrt->exports.winrt_dispatcher_init();\n\n\t\tgs_enter_context(obs->video.graphics);\n\t\twinrt->exports.winrt_capture_thread_start();\n\t\tgs_leave_context();\n\t}\n}\n\nstatic void uninit_winrt_state(struct winrt_state *winrt)\n{\n\tif (winrt->winrt_module) {\n\t\tif (winrt->loaded) {\n\t\t\twinrt->exports.winrt_capture_thread_stop();\n\t\t\tif (winrt->dispatcher)\n\t\t\t\twinrt->exports.winrt_dispatcher_free(winrt->dispatcher);\n\t\t\twinrt->exports.winrt_uninitialize();\n\t\t}\n\n\t\tos_dlclose(winrt->winrt_module);\n\t}\n}\n\n#endif // #ifdef _WIN32\n\nstatic const char *tick_sources_name = \"tick_sources\";\nstatic const char *render_displays_name = \"render_displays\";\nstatic const char *output_frame_name = \"output_frame\";\nstatic inline void update_active_state(struct obs_core_video_mix *video)\n{\n\tconst bool raw_was_active = video->raw_was_active;\n\tconst bool gpu_was_active = video->gpu_was_active;\n\tconst bool was_active = video->was_active;\n\n\tbool raw_active = os_atomic_load_long(&video->raw_active) > 0;\n\tconst bool gpu_active = os_atomic_load_long(&video->gpu_encoder_active) > 0;\n\tconst bool active = raw_active || gpu_active;\n\n\tif (!was_active && active)\n\t\tclear_base_frame_data(video);\n\tif (!raw_was_active && raw_active)\n\t\tclear_raw_frame_data(video);\n\tif (!gpu_was_active && gpu_active)\n\t\tclear_gpu_frame_data(video);\n\n\tvideo->gpu_was_active = gpu_active;\n\tvideo->raw_was_active = raw_active;\n\tvideo->was_active = active;\n}\n\nstatic inline void update_active_states(void)\n{\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0, num = obs->video.mixes.num; i < num; i++)\n\t\tupdate_active_state(obs->video.mixes.array[i]);\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n}\n\nstatic inline bool stop_requested(void)\n{\n\tbool success = true;\n\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0, num = obs->video.mixes.num; i < num; i++)\n\t\tif (!video_output_stopped(obs->video.mixes.array[i]->video))\n\t\t\tsuccess = false;\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\n\treturn success;\n}\n\nbool obs_graphics_thread_loop(struct obs_graphics_context *context)\n{\n\tuint64_t frame_start = os_gettime_ns();\n\tuint64_t frame_time_ns;\n\n\tupdate_active_states();\n\n\tprofile_start(context->video_thread_name);\n\tsource_profiler_frame_begin();\n\n\tgs_enter_context(obs->video.graphics);\n\tgs_begin_frame();\n\tgs_leave_context();\n\n\tprofile_start(tick_sources_name);\n\tcontext->last_time = tick_sources(obs->video.video_time, context->last_time);\n\tprofile_end(tick_sources_name);\n\n#ifdef _WIN32\n\tMSG msg;\n\twhile (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {\n\t\tTranslateMessage(&msg);\n\t\tDispatchMessage(&msg);\n\t}\n#endif\n\n\tsource_profiler_render_begin();\n\tprofile_start(output_frame_name);\n\toutput_frames();\n\tprofile_end(output_frame_name);\n\n\tprofile_start(render_displays_name);\n\trender_displays();\n\tprofile_end(render_displays_name);\n\tsource_profiler_render_end();\n\n\texecute_graphics_tasks();\n\n\tframe_time_ns = os_gettime_ns() - frame_start;\n\n\tsource_profiler_frame_collect();\n\tprofile_end(context->video_thread_name);\n\n\tprofile_reenable_thread();\n\n\tvideo_sleep(&obs->video, &obs->video.video_time, context->interval);\n\n\tcontext->frame_time_total_ns += frame_time_ns;\n\tcontext->fps_total_ns += (obs->video.video_time - context->last_time);\n\tcontext->fps_total_frames++;\n\n\tif (context->fps_total_ns >= 1000000000ULL) {\n\t\tobs->video.video_fps =\n\t\t\t(double)context->fps_total_frames / ((double)context->fps_total_ns / 1000000000.0);\n\t\tobs->video.video_avg_frame_time_ns = context->frame_time_total_ns / (uint64_t)context->fps_total_frames;\n\n\t\tcontext->frame_time_total_ns = 0;\n\t\tcontext->fps_total_ns = 0;\n\t\tcontext->fps_total_frames = 0;\n\t}\n\n\treturn !stop_requested();\n}\n\nvoid *obs_graphics_thread(void *param)\n{\n#ifdef _WIN32\n\tstruct winrt_state winrt;\n\tinit_winrt_state(&winrt);\n#endif // #ifdef _WIN32\n\n\tis_graphics_thread = true;\n\n\tconst uint64_t interval = obs->video.video_frame_interval_ns;\n\n\tobs->video.video_time = os_gettime_ns();\n\n\tos_set_thread_name(\"libobs: graphics thread\");\n\n\tconst char *video_thread_name = profile_store_name(obs_get_profiler_name_store(),\n\t\t\t\t\t\t\t   \"obs_graphics_thread(%g\" NBSP \"ms)\", interval / 1000000.);\n\tprofile_register_root(video_thread_name, interval);\n\n\tsrand((unsigned int)time(NULL));\n\n\tstruct obs_graphics_context context;\n\tcontext.interval = interval;\n\tcontext.frame_time_total_ns = 0;\n\tcontext.fps_total_ns = 0;\n\tcontext.fps_total_frames = 0;\n\tcontext.last_time = 0;\n\tcontext.video_thread_name = video_thread_name;\n\n#ifdef __APPLE__\n\twhile (obs_graphics_thread_loop_autorelease(&context))\n#else\n\twhile (obs_graphics_thread_loop(&context))\n#endif\n\t\t;\n\n#ifdef _WIN32\n\tuninit_winrt_state(&winrt);\n#endif\n\n\tUNUSED_PARAMETER(param);\n\treturn NULL;\n}\n"
  },
  {
    "path": "libobs/obs-view.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include \"obs.h\"\n#include \"obs-internal.h\"\n\nbool obs_view_init(struct obs_view *view, enum view_type type)\n{\n\tif (!view)\n\t\treturn false;\n\n\tpthread_mutex_init_value(&view->channels_mutex);\n\n\tif (pthread_mutex_init(&view->channels_mutex, NULL) != 0) {\n\t\tblog(LOG_ERROR, \"obs_view_init: Failed to create mutex\");\n\t\treturn false;\n\t}\n\n\tview->type = type;\n\treturn true;\n}\n\nobs_view_t *obs_view_create(void)\n{\n\tstruct obs_view *view = bzalloc(sizeof(struct obs_view));\n\n\tif (!obs_view_init(view, AUX_VIEW)) {\n\t\tbfree(view);\n\t\tview = NULL;\n\t}\n\n\treturn view;\n}\n\nvoid obs_view_free(struct obs_view *view)\n{\n\tif (!view)\n\t\treturn;\n\n\tfor (size_t i = 0; i < MAX_CHANNELS; i++) {\n\t\tstruct obs_source *source = view->channels[i];\n\t\tif (source) {\n\t\t\tobs_source_deactivate(source, view->type);\n\t\t\tobs_source_release(source);\n\t\t}\n\t}\n\n\tmemset(view->channels, 0, sizeof(view->channels));\n\tpthread_mutex_destroy(&view->channels_mutex);\n}\n\nvoid obs_view_destroy(obs_view_t *view)\n{\n\tif (view) {\n\t\tobs_view_free(view);\n\t\tbfree(view);\n\t}\n}\n\nobs_source_t *obs_view_get_source(obs_view_t *view, uint32_t channel)\n{\n\tobs_source_t *source;\n\tassert(channel < MAX_CHANNELS);\n\n\tif (!view)\n\t\treturn NULL;\n\tif (channel >= MAX_CHANNELS)\n\t\treturn NULL;\n\n\tpthread_mutex_lock(&view->channels_mutex);\n\tsource = obs_source_get_ref(view->channels[channel]);\n\tpthread_mutex_unlock(&view->channels_mutex);\n\n\treturn source;\n}\n\nvoid obs_view_set_source(obs_view_t *view, uint32_t channel, obs_source_t *source)\n{\n\tstruct obs_source *prev_source;\n\n\tassert(channel < MAX_CHANNELS);\n\n\tif (!view)\n\t\treturn;\n\tif (channel >= MAX_CHANNELS)\n\t\treturn;\n\n\tpthread_mutex_lock(&view->channels_mutex);\n\tsource = obs_source_get_ref(source);\n\tprev_source = view->channels[channel];\n\tview->channels[channel] = source;\n\n\tpthread_mutex_unlock(&view->channels_mutex);\n\n\tif (source)\n\t\tobs_source_activate(source, view->type);\n\n\tif (prev_source) {\n\t\tobs_source_deactivate(prev_source, view->type);\n\t\tobs_source_release(prev_source);\n\t}\n}\n\nvoid obs_view_render(obs_view_t *view)\n{\n\tif (!view)\n\t\treturn;\n\n\tpthread_mutex_lock(&view->channels_mutex);\n\n\tfor (size_t i = 0; i < MAX_CHANNELS; i++) {\n\t\tstruct obs_source *source;\n\n\t\tsource = view->channels[i];\n\n\t\tif (source) {\n\t\t\tif (source->removed) {\n\t\t\t\tobs_source_release(source);\n\t\t\t\tview->channels[i] = NULL;\n\t\t\t} else {\n\t\t\t\tobs_source_video_render(source);\n\t\t\t}\n\t\t}\n\t}\n\n\tpthread_mutex_unlock(&view->channels_mutex);\n}\n\nvideo_t *obs_view_add(obs_view_t *view)\n{\n\tif (!obs->data.main_canvas->mix)\n\t\treturn NULL;\n\treturn obs_view_add2(view, &obs->data.main_canvas->mix->ovi);\n}\n\nvideo_t *obs_view_add2(obs_view_t *view, struct obs_video_info *ovi)\n{\n\tif (!view || !ovi)\n\t\treturn NULL;\n\n\tstruct obs_core_video_mix *mix = obs_create_video_mix(ovi);\n\tif (!mix) {\n\t\treturn NULL;\n\t}\n\tmix->view = view;\n\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tda_push_back(obs->video.mixes, &mix);\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\n\treturn mix->video;\n}\n\nvoid obs_view_remove(obs_view_t *view)\n{\n\tif (!view)\n\t\treturn;\n\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0, num = obs->video.mixes.num; i < num; i++) {\n\t\tif (obs->video.mixes.array[i]->view == view)\n\t\t\tobs->video.mixes.array[i]->view = NULL;\n\t}\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n}\n\nvoid obs_view_enum_video_info(obs_view_t *view, bool (*enum_proc)(void *, struct obs_video_info *), void *param)\n{\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\n\tfor (size_t i = 0, num = obs->video.mixes.num; i < num; i++) {\n\t\tstruct obs_core_video_mix *mix = obs->video.mixes.array[i];\n\t\tif (mix->view != view)\n\t\t\tcontinue;\n\t\tif (!enum_proc(param, &mix->ovi))\n\t\t\tbreak;\n\t}\n\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n}\n"
  },
  {
    "path": "libobs/obs.c",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#include <inttypes.h>\n\n#include \"graphics/matrix4.h\"\n#include \"callback/calldata.h\"\n\n#include \"obs.h\"\n#include \"obs-internal.h\"\n\nstruct obs_core *obs = NULL;\n\nstatic THREAD_LOCAL bool is_ui_thread = false;\n\nextern void add_default_module_paths(void);\nextern char *find_libobs_data_file(const char *file);\n\nstatic inline void make_video_info(struct video_output_info *vi, struct obs_video_info *ovi)\n{\n\tvi->name = \"video\";\n\tvi->format = ovi->output_format;\n\tvi->fps_num = ovi->fps_num;\n\tvi->fps_den = ovi->fps_den;\n\tvi->width = ovi->output_width;\n\tvi->height = ovi->output_height;\n\tvi->range = ovi->range;\n\tvi->colorspace = ovi->colorspace;\n\tvi->cache_size = 6;\n}\n\nstatic inline void calc_gpu_conversion_sizes(struct obs_core_video_mix *video)\n{\n\tconst struct video_output_info *info = video_output_get_info(video->video);\n\n\tvideo->conversion_needed = false;\n\tvideo->conversion_techs[0] = NULL;\n\tvideo->conversion_techs[1] = NULL;\n\tvideo->conversion_techs[2] = NULL;\n\tvideo->conversion_width_i = 0.f;\n\tvideo->conversion_height_i = 0.f;\n\n\tswitch (info->format) {\n\tcase VIDEO_FORMAT_I420:\n\t\tvideo->conversion_needed = true;\n\t\tvideo->conversion_techs[0] = \"Planar_Y\";\n\t\tvideo->conversion_techs[1] = \"Planar_U_Left\";\n\t\tvideo->conversion_techs[2] = \"Planar_V_Left\";\n\t\tvideo->conversion_width_i = 1.f / (float)info->width;\n\t\tbreak;\n\tcase VIDEO_FORMAT_NV12:\n\t\tvideo->conversion_needed = true;\n\t\tvideo->conversion_techs[0] = \"NV12_Y\";\n\t\tvideo->conversion_techs[1] = \"NV12_UV\";\n\t\tvideo->conversion_width_i = 1.f / (float)info->width;\n\t\tbreak;\n\tcase VIDEO_FORMAT_I444:\n\t\tvideo->conversion_needed = true;\n\t\tvideo->conversion_techs[0] = \"Planar_Y\";\n\t\tvideo->conversion_techs[1] = \"Planar_U\";\n\t\tvideo->conversion_techs[2] = \"Planar_V\";\n\t\tbreak;\n\tcase VIDEO_FORMAT_I010:\n\t\tvideo->conversion_needed = true;\n\t\tvideo->conversion_width_i = 1.f / (float)info->width;\n\t\tvideo->conversion_height_i = 1.f / (float)info->height;\n\t\tif (info->colorspace == VIDEO_CS_2100_PQ) {\n\t\t\tvideo->conversion_techs[0] = \"I010_PQ_Y\";\n\t\t\tvideo->conversion_techs[1] = \"I010_PQ_U\";\n\t\t\tvideo->conversion_techs[2] = \"I010_PQ_V\";\n\t\t} else if (info->colorspace == VIDEO_CS_2100_HLG) {\n\t\t\tvideo->conversion_techs[0] = \"I010_HLG_Y\";\n\t\t\tvideo->conversion_techs[1] = \"I010_HLG_U\";\n\t\t\tvideo->conversion_techs[2] = \"I010_HLG_V\";\n\t\t} else {\n\t\t\tvideo->conversion_techs[0] = \"I010_SRGB_Y\";\n\t\t\tvideo->conversion_techs[1] = \"I010_SRGB_U\";\n\t\t\tvideo->conversion_techs[2] = \"I010_SRGB_V\";\n\t\t}\n\t\tbreak;\n\tcase VIDEO_FORMAT_P010:\n\t\tvideo->conversion_needed = true;\n\t\tvideo->conversion_width_i = 1.f / (float)info->width;\n\t\tvideo->conversion_height_i = 1.f / (float)info->height;\n\t\tif (info->colorspace == VIDEO_CS_2100_PQ) {\n\t\t\tvideo->conversion_techs[0] = \"P010_PQ_Y\";\n\t\t\tvideo->conversion_techs[1] = \"P010_PQ_UV\";\n\t\t} else if (info->colorspace == VIDEO_CS_2100_HLG) {\n\t\t\tvideo->conversion_techs[0] = \"P010_HLG_Y\";\n\t\t\tvideo->conversion_techs[1] = \"P010_HLG_UV\";\n\t\t} else {\n\t\t\tvideo->conversion_techs[0] = \"P010_SRGB_Y\";\n\t\t\tvideo->conversion_techs[1] = \"P010_SRGB_UV\";\n\t\t}\n\t\tbreak;\n\tcase VIDEO_FORMAT_P216:\n\t\tvideo->conversion_needed = true;\n\t\tvideo->conversion_width_i = 1.f / (float)info->width;\n\t\tvideo->conversion_height_i = 1.f / (float)info->height;\n\t\tif (info->colorspace == VIDEO_CS_2100_PQ) {\n\t\t\tvideo->conversion_techs[0] = \"P216_PQ_Y\";\n\t\t\tvideo->conversion_techs[1] = \"P216_PQ_UV\";\n\t\t} else if (info->colorspace == VIDEO_CS_2100_HLG) {\n\t\t\tvideo->conversion_techs[0] = \"P216_HLG_Y\";\n\t\t\tvideo->conversion_techs[1] = \"P216_HLG_UV\";\n\t\t} else {\n\t\t\tvideo->conversion_techs[0] = \"P216_SRGB_Y\";\n\t\t\tvideo->conversion_techs[1] = \"P216_SRGB_UV\";\n\t\t}\n\t\tbreak;\n\tcase VIDEO_FORMAT_P416:\n\t\tvideo->conversion_needed = true;\n\t\tvideo->conversion_width_i = 1.f / (float)info->width;\n\t\tvideo->conversion_height_i = 1.f / (float)info->height;\n\t\tif (info->colorspace == VIDEO_CS_2100_PQ) {\n\t\t\tvideo->conversion_techs[0] = \"P416_PQ_Y\";\n\t\t\tvideo->conversion_techs[1] = \"P416_PQ_UV\";\n\t\t} else if (info->colorspace == VIDEO_CS_2100_HLG) {\n\t\t\tvideo->conversion_techs[0] = \"P416_HLG_Y\";\n\t\t\tvideo->conversion_techs[1] = \"P416_HLG_UV\";\n\t\t} else {\n\t\t\tvideo->conversion_techs[0] = \"P416_SRGB_Y\";\n\t\t\tvideo->conversion_techs[1] = \"P416_SRGB_UV\";\n\t\t}\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nstatic bool obs_init_gpu_conversion(struct obs_core_video_mix *video)\n{\n\tconst struct video_output_info *info = video_output_get_info(video->video);\n\n\tcalc_gpu_conversion_sizes(video);\n\n\tvideo->using_nv12_tex = info->format == VIDEO_FORMAT_NV12 ? gs_nv12_available() : false;\n\tvideo->using_p010_tex = info->format == VIDEO_FORMAT_P010 ? gs_p010_available() : false;\n\n\tif (!video->conversion_needed) {\n\t\tblog(LOG_INFO, \"GPU conversion not available for format: %u\", (unsigned int)info->format);\n\t\tvideo->gpu_conversion = false;\n\t\tvideo->using_nv12_tex = false;\n\t\tvideo->using_p010_tex = false;\n\t\tblog(LOG_INFO, \"NV12 texture support not available\");\n\t\treturn true;\n\t}\n\n\tif (video->using_nv12_tex)\n\t\tblog(LOG_INFO, \"NV12 texture support enabled\");\n\telse\n\t\tblog(LOG_INFO, \"NV12 texture support not available\");\n\n\tif (video->using_p010_tex)\n\t\tblog(LOG_INFO, \"P010 texture support enabled\");\n\telse\n\t\tblog(LOG_INFO, \"P010 texture support not available\");\n\n\tvideo->convert_textures[0] = NULL;\n\tvideo->convert_textures[1] = NULL;\n\tvideo->convert_textures[2] = NULL;\n\tvideo->convert_textures_encode[0] = NULL;\n\tvideo->convert_textures_encode[1] = NULL;\n\tvideo->convert_textures_encode[2] = NULL;\n\tif (video->using_nv12_tex) {\n\t\tif (!gs_texture_create_nv12(&video->convert_textures_encode[0], &video->convert_textures_encode[1],\n\t\t\t\t\t    info->width, info->height, GS_RENDER_TARGET | GS_SHARED_KM_TEX)) {\n\t\t\treturn false;\n\t\t}\n\t} else if (video->using_p010_tex) {\n\t\tif (!gs_texture_create_p010(&video->convert_textures_encode[0], &video->convert_textures_encode[1],\n\t\t\t\t\t    info->width, info->height, GS_RENDER_TARGET | GS_SHARED_KM_TEX)) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tbool success = true;\n\n\tswitch (info->format) {\n\tcase VIDEO_FORMAT_I420:\n\t\tvideo->convert_textures[0] =\n\t\t\tgs_texture_create(info->width, info->height, GS_R8, 1, NULL, GS_RENDER_TARGET);\n\t\tvideo->convert_textures[1] =\n\t\t\tgs_texture_create(info->width / 2, info->height / 2, GS_R8, 1, NULL, GS_RENDER_TARGET);\n\t\tvideo->convert_textures[2] =\n\t\t\tgs_texture_create(info->width / 2, info->height / 2, GS_R8, 1, NULL, GS_RENDER_TARGET);\n\t\tif (!video->convert_textures[0] || !video->convert_textures[1] || !video->convert_textures[2])\n\t\t\tsuccess = false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_NV12:\n\t\tvideo->convert_textures[0] =\n\t\t\tgs_texture_create(info->width, info->height, GS_R8, 1, NULL, GS_RENDER_TARGET);\n\t\tvideo->convert_textures[1] =\n\t\t\tgs_texture_create(info->width / 2, info->height / 2, GS_R8G8, 1, NULL, GS_RENDER_TARGET);\n\t\tif (!video->convert_textures[0] || !video->convert_textures[1])\n\t\t\tsuccess = false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_I444:\n\t\tvideo->convert_textures[0] =\n\t\t\tgs_texture_create(info->width, info->height, GS_R8, 1, NULL, GS_RENDER_TARGET);\n\t\tvideo->convert_textures[1] =\n\t\t\tgs_texture_create(info->width, info->height, GS_R8, 1, NULL, GS_RENDER_TARGET);\n\t\tvideo->convert_textures[2] =\n\t\t\tgs_texture_create(info->width, info->height, GS_R8, 1, NULL, GS_RENDER_TARGET);\n\t\tif (!video->convert_textures[0] || !video->convert_textures[1] || !video->convert_textures[2])\n\t\t\tsuccess = false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_I010:\n\t\tvideo->convert_textures[0] =\n\t\t\tgs_texture_create(info->width, info->height, GS_R16, 1, NULL, GS_RENDER_TARGET);\n\t\tvideo->convert_textures[1] =\n\t\t\tgs_texture_create(info->width / 2, info->height / 2, GS_R16, 1, NULL, GS_RENDER_TARGET);\n\t\tvideo->convert_textures[2] =\n\t\t\tgs_texture_create(info->width / 2, info->height / 2, GS_R16, 1, NULL, GS_RENDER_TARGET);\n\t\tif (!video->convert_textures[0] || !video->convert_textures[1] || !video->convert_textures[2])\n\t\t\tsuccess = false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_P010:\n\t\tvideo->convert_textures[0] =\n\t\t\tgs_texture_create(info->width, info->height, GS_R16, 1, NULL, GS_RENDER_TARGET);\n\t\tvideo->convert_textures[1] =\n\t\t\tgs_texture_create(info->width / 2, info->height / 2, GS_RG16, 1, NULL, GS_RENDER_TARGET);\n\t\tif (!video->convert_textures[0] || !video->convert_textures[1])\n\t\t\tsuccess = false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_P216:\n\t\tvideo->convert_textures[0] =\n\t\t\tgs_texture_create(info->width, info->height, GS_R16, 1, NULL, GS_RENDER_TARGET);\n\t\tvideo->convert_textures[1] =\n\t\t\tgs_texture_create(info->width / 2, info->height, GS_RG16, 1, NULL, GS_RENDER_TARGET);\n\t\tif (!video->convert_textures[0] || !video->convert_textures[1])\n\t\t\tsuccess = false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_P416:\n\t\tvideo->convert_textures[0] =\n\t\t\tgs_texture_create(info->width, info->height, GS_R16, 1, NULL, GS_RENDER_TARGET);\n\t\tvideo->convert_textures[1] =\n\t\t\tgs_texture_create(info->width, info->height, GS_RG16, 1, NULL, GS_RENDER_TARGET);\n\t\tif (!video->convert_textures[0] || !video->convert_textures[1])\n\t\t\tsuccess = false;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tif (!success) {\n\t\tfor (size_t c = 0; c < NUM_CHANNELS; c++) {\n\t\t\tif (video->convert_textures[c]) {\n\t\t\t\tgs_texture_destroy(video->convert_textures[c]);\n\t\t\t\tvideo->convert_textures[c] = NULL;\n\t\t\t}\n\t\t\tif (video->convert_textures_encode[c]) {\n\t\t\t\tgs_texture_destroy(video->convert_textures_encode[c]);\n\t\t\t\tvideo->convert_textures_encode[c] = NULL;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn success;\n}\n\nstatic bool obs_init_gpu_copy_surfaces(struct obs_core_video_mix *video, size_t i)\n{\n\tconst struct video_output_info *info = video_output_get_info(video->video);\n\tswitch (info->format) {\n\tcase VIDEO_FORMAT_I420:\n\t\tvideo->copy_surfaces[i][0] = gs_stagesurface_create(info->width, info->height, GS_R8);\n\t\tif (!video->copy_surfaces[i][0])\n\t\t\treturn false;\n\t\tvideo->copy_surfaces[i][1] = gs_stagesurface_create(info->width / 2, info->height / 2, GS_R8);\n\t\tif (!video->copy_surfaces[i][1])\n\t\t\treturn false;\n\t\tvideo->copy_surfaces[i][2] = gs_stagesurface_create(info->width / 2, info->height / 2, GS_R8);\n\t\tif (!video->copy_surfaces[i][2])\n\t\t\treturn false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_NV12:\n\t\tvideo->copy_surfaces[i][0] = gs_stagesurface_create(info->width, info->height, GS_R8);\n\t\tif (!video->copy_surfaces[i][0])\n\t\t\treturn false;\n\t\tvideo->copy_surfaces[i][1] = gs_stagesurface_create(info->width / 2, info->height / 2, GS_R8G8);\n\t\tif (!video->copy_surfaces[i][1])\n\t\t\treturn false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_I444:\n\t\tvideo->copy_surfaces[i][0] = gs_stagesurface_create(info->width, info->height, GS_R8);\n\t\tif (!video->copy_surfaces[i][0])\n\t\t\treturn false;\n\t\tvideo->copy_surfaces[i][1] = gs_stagesurface_create(info->width, info->height, GS_R8);\n\t\tif (!video->copy_surfaces[i][1])\n\t\t\treturn false;\n\t\tvideo->copy_surfaces[i][2] = gs_stagesurface_create(info->width, info->height, GS_R8);\n\t\tif (!video->copy_surfaces[i][2])\n\t\t\treturn false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_I010:\n\t\tvideo->copy_surfaces[i][0] = gs_stagesurface_create(info->width, info->height, GS_R16);\n\t\tif (!video->copy_surfaces[i][0])\n\t\t\treturn false;\n\t\tvideo->copy_surfaces[i][1] = gs_stagesurface_create(info->width / 2, info->height / 2, GS_R16);\n\t\tif (!video->copy_surfaces[i][1])\n\t\t\treturn false;\n\t\tvideo->copy_surfaces[i][2] = gs_stagesurface_create(info->width / 2, info->height / 2, GS_R16);\n\t\tif (!video->copy_surfaces[i][2])\n\t\t\treturn false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_P010:\n\t\tvideo->copy_surfaces[i][0] = gs_stagesurface_create(info->width, info->height, GS_R16);\n\t\tif (!video->copy_surfaces[i][0])\n\t\t\treturn false;\n\t\tvideo->copy_surfaces[i][1] = gs_stagesurface_create(info->width / 2, info->height / 2, GS_RG16);\n\t\tif (!video->copy_surfaces[i][1])\n\t\t\treturn false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_P216:\n\t\tvideo->copy_surfaces[i][0] = gs_stagesurface_create(info->width, info->height, GS_R16);\n\t\tif (!video->copy_surfaces[i][0])\n\t\t\treturn false;\n\t\tvideo->copy_surfaces[i][1] = gs_stagesurface_create(info->width / 2, info->height, GS_RG16);\n\t\tif (!video->copy_surfaces[i][1])\n\t\t\treturn false;\n\t\tbreak;\n\tcase VIDEO_FORMAT_P416:\n\t\tvideo->copy_surfaces[i][0] = gs_stagesurface_create(info->width, info->height, GS_R16);\n\t\tif (!video->copy_surfaces[i][0])\n\t\t\treturn false;\n\t\tvideo->copy_surfaces[i][1] = gs_stagesurface_create(info->width, info->height, GS_RG16);\n\t\tif (!video->copy_surfaces[i][1])\n\t\t\treturn false;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn true;\n}\n\nstatic bool obs_init_textures(struct obs_core_video_mix *video)\n{\n\tconst struct video_output_info *info = video_output_get_info(video->video);\n\n\tbool success = true;\n\n\tenum gs_color_format format = GS_BGRA;\n\tswitch (info->format) {\n\tcase VIDEO_FORMAT_I010:\n\tcase VIDEO_FORMAT_P010:\n\tcase VIDEO_FORMAT_I210:\n\tcase VIDEO_FORMAT_I412:\n\tcase VIDEO_FORMAT_YA2L:\n\tcase VIDEO_FORMAT_P216:\n\tcase VIDEO_FORMAT_P416:\n\t\tformat = GS_RGBA16F;\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n\n\tfor (size_t i = 0; i < NUM_TEXTURES; i++) {\n#ifdef _WIN32\n\t\tif (video->using_nv12_tex) {\n\t\t\tvideo->copy_surfaces_encode[i] = gs_stagesurface_create_nv12(info->width, info->height);\n\t\t\tif (!video->copy_surfaces_encode[i]) {\n\t\t\t\tsuccess = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else if (video->using_p010_tex) {\n\t\t\tvideo->copy_surfaces_encode[i] = gs_stagesurface_create_p010(info->width, info->height);\n\t\t\tif (!video->copy_surfaces_encode[i]) {\n\t\t\t\tsuccess = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n#endif\n\n\t\tif (video->gpu_conversion) {\n\t\t\tif (!obs_init_gpu_copy_surfaces(video, i)) {\n\t\t\t\tsuccess = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} else {\n\t\t\tvideo->copy_surfaces[i][0] = gs_stagesurface_create(info->width, info->height, format);\n\t\t\tif (!video->copy_surfaces[i][0]) {\n\t\t\t\tsuccess = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tenum gs_color_space space = GS_CS_SRGB;\n\tswitch (info->colorspace) {\n\tcase VIDEO_CS_2100_PQ:\n\tcase VIDEO_CS_2100_HLG:\n\t\tspace = GS_CS_709_EXTENDED;\n\t\tbreak;\n\tdefault:\n\t\tswitch (info->format) {\n\t\tcase VIDEO_FORMAT_I010:\n\t\tcase VIDEO_FORMAT_P010:\n\t\tcase VIDEO_FORMAT_P216:\n\t\tcase VIDEO_FORMAT_P416:\n\t\t\tspace = GS_CS_SRGB_16F;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tspace = GS_CS_SRGB;\n\t\t\tbreak;\n\t\t}\n\t\tbreak;\n\t}\n\n\tvideo->render_texture =\n\t\tgs_texture_create(video->ovi.base_width, video->ovi.base_height, format, 1, NULL, GS_RENDER_TARGET);\n\tif (!video->render_texture)\n\t\tsuccess = false;\n\n\tvideo->output_texture = gs_texture_create(info->width, info->height, format, 1, NULL, GS_RENDER_TARGET);\n\tif (!video->output_texture)\n\t\tsuccess = false;\n\n\tif (success) {\n\t\tvideo->render_space = space;\n\t} else {\n\t\tfor (size_t i = 0; i < NUM_TEXTURES; i++) {\n\t\t\tfor (size_t c = 0; c < NUM_CHANNELS; c++) {\n\t\t\t\tif (video->copy_surfaces[i][c]) {\n\t\t\t\t\tgs_stagesurface_destroy(video->copy_surfaces[i][c]);\n\t\t\t\t\tvideo->copy_surfaces[i][c] = NULL;\n\t\t\t\t}\n\t\t\t}\n#ifdef _WIN32\n\t\t\tif (video->copy_surfaces_encode[i]) {\n\t\t\t\tgs_stagesurface_destroy(video->copy_surfaces_encode[i]);\n\t\t\t\tvideo->copy_surfaces_encode[i] = NULL;\n\t\t\t}\n#endif\n\t\t}\n\n\t\tif (video->render_texture) {\n\t\t\tgs_texture_destroy(video->render_texture);\n\t\t\tvideo->render_texture = NULL;\n\t\t}\n\n\t\tif (video->output_texture) {\n\t\t\tgs_texture_destroy(video->output_texture);\n\t\t\tvideo->output_texture = NULL;\n\t\t}\n\t}\n\n\treturn success;\n}\n\ngs_effect_t *obs_load_effect(gs_effect_t **effect, const char *file)\n{\n\tif (!*effect) {\n\t\tchar *filename = obs_find_data_file(file);\n\t\t*effect = gs_effect_create_from_file(filename, NULL);\n\t\tbfree(filename);\n\t}\n\n\treturn *effect;\n}\n\nstatic const char *shader_comp_name = \"shader compilation\";\nstatic const char *obs_init_graphics_name = \"obs_init_graphics\";\nstatic int obs_init_graphics(struct obs_video_info *ovi)\n{\n\tstruct obs_core_video *video = &obs->video;\n\tuint8_t transparent_tex_data[2 * 2 * 4] = {0};\n\tconst uint8_t *transparent_tex = transparent_tex_data;\n\tstruct gs_sampler_info point_sampler = {0};\n\tbool success = true;\n\tint errorcode;\n\n\tprofile_start(obs_init_graphics_name);\n\n\terrorcode = gs_create(&video->graphics, ovi->graphics_module, ovi->adapter);\n\tif (errorcode != GS_SUCCESS) {\n\t\tprofile_end(obs_init_graphics_name);\n\n\t\tswitch (errorcode) {\n\t\tcase GS_ERROR_MODULE_NOT_FOUND:\n\t\t\treturn OBS_VIDEO_MODULE_NOT_FOUND;\n\t\tcase GS_ERROR_NOT_SUPPORTED:\n\t\t\treturn OBS_VIDEO_NOT_SUPPORTED;\n\t\tdefault:\n\t\t\treturn OBS_VIDEO_FAIL;\n\t\t}\n\t}\n\n\tprofile_start(shader_comp_name);\n\tgs_enter_context(video->graphics);\n\n\tchar *filename = obs_find_data_file(\"default.effect\");\n\tvideo->default_effect = gs_effect_create_from_file(filename, NULL);\n\tbfree(filename);\n\n\tif (gs_get_device_type() == GS_DEVICE_OPENGL) {\n\t\tfilename = obs_find_data_file(\"default_rect.effect\");\n\t\tvideo->default_rect_effect = gs_effect_create_from_file(filename, NULL);\n\t\tbfree(filename);\n\t}\n\n\tfilename = obs_find_data_file(\"opaque.effect\");\n\tvideo->opaque_effect = gs_effect_create_from_file(filename, NULL);\n\tbfree(filename);\n\n\tfilename = obs_find_data_file(\"solid.effect\");\n\tvideo->solid_effect = gs_effect_create_from_file(filename, NULL);\n\tbfree(filename);\n\n\tfilename = obs_find_data_file(\"repeat.effect\");\n\tvideo->repeat_effect = gs_effect_create_from_file(filename, NULL);\n\tbfree(filename);\n\n\tfilename = obs_find_data_file(\"format_conversion.effect\");\n\tvideo->conversion_effect = gs_effect_create_from_file(filename, NULL);\n\tbfree(filename);\n\n\tfilename = obs_find_data_file(\"bicubic_scale.effect\");\n\tvideo->bicubic_effect = gs_effect_create_from_file(filename, NULL);\n\tbfree(filename);\n\n\tfilename = obs_find_data_file(\"lanczos_scale.effect\");\n\tvideo->lanczos_effect = gs_effect_create_from_file(filename, NULL);\n\tbfree(filename);\n\n\tfilename = obs_find_data_file(\"area.effect\");\n\tvideo->area_effect = gs_effect_create_from_file(filename, NULL);\n\tbfree(filename);\n\n\tfilename = obs_find_data_file(\"bilinear_lowres_scale.effect\");\n\tvideo->bilinear_lowres_effect = gs_effect_create_from_file(filename, NULL);\n\tbfree(filename);\n\n\tfilename = obs_find_data_file(\"premultiplied_alpha.effect\");\n\tvideo->premultiplied_alpha_effect = gs_effect_create_from_file(filename, NULL);\n\tbfree(filename);\n\n\tpoint_sampler.max_anisotropy = 1;\n\tvideo->point_sampler = gs_samplerstate_create(&point_sampler);\n\n\tobs->video.transparent_texture = gs_texture_create(2, 2, GS_RGBA, 1, &transparent_tex, 0);\n\n\tif (!video->default_effect)\n\t\tsuccess = false;\n\tif (gs_get_device_type() == GS_DEVICE_OPENGL) {\n\t\tif (!video->default_rect_effect)\n\t\t\tsuccess = false;\n\t}\n\tif (!video->opaque_effect)\n\t\tsuccess = false;\n\tif (!video->solid_effect)\n\t\tsuccess = false;\n\tif (!video->conversion_effect)\n\t\tsuccess = false;\n\tif (!video->premultiplied_alpha_effect)\n\t\tsuccess = false;\n\tif (!video->transparent_texture)\n\t\tsuccess = false;\n\tif (!video->point_sampler)\n\t\tsuccess = false;\n\n\tgs_leave_context();\n\tprofile_end(shader_comp_name);\n\tprofile_end(obs_init_graphics_name);\n\n\treturn success ? OBS_VIDEO_SUCCESS : OBS_VIDEO_FAIL;\n}\n\nstatic inline void set_video_matrix(struct obs_core_video_mix *video, struct video_output_info *info)\n{\n\tstruct matrix4 mat;\n\tstruct vec4 r_row;\n\n\tif (format_is_yuv(info->format)) {\n\t\tvideo_format_get_parameters_for_format(info->colorspace, info->range, info->format, (float *)&mat, NULL,\n\t\t\t\t\t\t       NULL);\n\t\tmatrix4_inv(&mat, &mat);\n\n\t\t/* swap R and G */\n\t\tr_row = mat.x;\n\t\tmat.x = mat.y;\n\t\tmat.y = r_row;\n\t} else {\n\t\tmatrix4_identity(&mat);\n\t}\n\n\tmemcpy(video->color_matrix, &mat, sizeof(float) * 16);\n}\n\nstatic int obs_init_video_mix(struct obs_video_info *ovi, struct obs_core_video_mix *video)\n{\n\tstruct video_output_info vi;\n\n\tpthread_mutex_init_value(&video->gpu_encoder_mutex);\n\n\tmake_video_info(&vi, ovi);\n\tvideo->ovi = *ovi;\n\n\t/* main view graphics thread drives all frame output,\n\t * so share FPS settings for aux views */\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tsize_t num = obs->video.mixes.num;\n\tif (num && obs->data.main_canvas->mix) {\n\t\tstruct obs_video_info main_ovi = obs->data.main_canvas->mix->ovi;\n\t\tvideo->ovi.fps_num = main_ovi.fps_num;\n\t\tvideo->ovi.fps_den = main_ovi.fps_den;\n\t}\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\n\tvideo->gpu_conversion = ovi->gpu_conversion;\n\tvideo->gpu_was_active = false;\n\tvideo->raw_was_active = false;\n\tvideo->was_active = false;\n\n\tset_video_matrix(video, &vi);\n\n\tint errorcode = video_output_open(&video->video, &vi);\n\tif (errorcode != VIDEO_OUTPUT_SUCCESS) {\n\t\tif (errorcode == VIDEO_OUTPUT_INVALIDPARAM) {\n\t\t\tblog(LOG_ERROR, \"Invalid video parameters specified\");\n\t\t\treturn OBS_VIDEO_INVALID_PARAM;\n\t\t} else {\n\t\t\tblog(LOG_ERROR, \"Could not open video output\");\n\t\t}\n\t\treturn OBS_VIDEO_FAIL;\n\t}\n\n\tif (pthread_mutex_init(&video->gpu_encoder_mutex, NULL) < 0)\n\t\treturn OBS_VIDEO_FAIL;\n\n\tgs_enter_context(obs->video.graphics);\n\n\tif (video->gpu_conversion && !obs_init_gpu_conversion(video))\n\t\treturn OBS_VIDEO_FAIL;\n\tif (!obs_init_textures(video))\n\t\treturn OBS_VIDEO_FAIL;\n\n\tgs_leave_context();\n\n\treturn OBS_VIDEO_SUCCESS;\n}\n\nstruct obs_core_video_mix *obs_create_video_mix(struct obs_video_info *ovi)\n{\n\tstruct obs_core_video_mix *video = bzalloc(sizeof(struct obs_core_video_mix));\n\tif (obs_init_video_mix(ovi, video) != OBS_VIDEO_SUCCESS) {\n\t\tbfree(video);\n\t\tvideo = NULL;\n\t}\n\treturn video;\n}\n\nstatic bool restore_canvases(void)\n{\n\tbool success = true;\n\n\tpthread_mutex_lock(&obs->data.canvases_mutex);\n\tstruct obs_context_data *ctx, *tmp;\n\tHASH_ITER (hh, (struct obs_context_data *)obs->data.canvases, ctx, tmp) {\n\t\tobs_canvas_t *canvas = (obs_canvas_t *)ctx;\n\t\tif (canvas->flags & MAIN)\n\t\t\tcontinue;\n\n\t\tif (!obs_canvas_reset_video_internal(canvas, NULL)) {\n\t\t\tblog(LOG_ERROR, \"Failed restoring video mix for canvas '%s'\", canvas->context.name);\n\t\t\tsuccess = false;\n\t\t}\n\t}\n\tpthread_mutex_unlock(&obs->data.canvases_mutex);\n\n\treturn success;\n}\n\nstatic int obs_init_video(struct obs_video_info *ovi)\n{\n\tstruct obs_core_video *video = &obs->video;\n\tvideo->video_frame_interval_ns = util_mul_div64(1000000000ULL, ovi->fps_den, ovi->fps_num);\n\tvideo->video_half_frame_interval_ns = util_mul_div64(500000000ULL, ovi->fps_den, ovi->fps_num);\n\n\tif (pthread_mutex_init(&video->task_mutex, NULL) < 0)\n\t\treturn OBS_VIDEO_FAIL;\n\tif (pthread_mutex_init(&video->encoder_group_mutex, NULL) < 0)\n\t\treturn OBS_VIDEO_FAIL;\n\tif (pthread_mutex_init(&video->mixes_mutex, NULL) < 0)\n\t\treturn OBS_VIDEO_FAIL;\n\n\t/* Reset main canvas mix first so it remains first in the rendering order. */\n\tif (!obs_canvas_reset_video_internal(obs->data.main_canvas, ovi))\n\t\treturn OBS_VIDEO_FAIL;\n\t/* Reset mixes for remaining canvases using their existing video info. */\n\tif (!restore_canvases())\n\t\treturn OBS_VIDEO_FAIL;\n\n\tint errorcode;\n#ifdef __APPLE__\n\tpthread_attr_t attr;\n\tpthread_attr_init(&attr);\n\tpthread_attr_set_qos_class_np(&attr, QOS_CLASS_USER_INTERACTIVE, 0);\n\terrorcode = pthread_create(&video->video_thread, &attr, obs_graphics_thread_autorelease, obs);\n#else\n\terrorcode = pthread_create(&video->video_thread, NULL, obs_graphics_thread, obs);\n#endif\n\tif (errorcode != 0)\n\t\treturn OBS_VIDEO_FAIL;\n\n\tvideo->thread_initialized = true;\n\n\tcalldata_t parameters = {0};\n\tsignal_handler_signal(obs->signals, \"video_reset\", &parameters);\n\n\treturn OBS_VIDEO_SUCCESS;\n}\n\nstatic void stop_video(void)\n{\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0, num = obs->video.mixes.num; i < num; i++)\n\t\tvideo_output_stop(obs->video.mixes.array[i]->video);\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\n\tstruct obs_core_video *video = &obs->video;\n\tvoid *thread_retval;\n\n\tif (video->thread_initialized) {\n\t\tpthread_join(video->video_thread, &thread_retval);\n\t\tvideo->thread_initialized = false;\n\t}\n}\n\nstatic void obs_free_render_textures(struct obs_core_video_mix *video)\n{\n\tif (!obs->video.graphics)\n\t\treturn;\n\n\tgs_enter_context(obs->video.graphics);\n\n\tfor (size_t c = 0; c < NUM_CHANNELS; c++) {\n\t\tif (video->mapped_surfaces[c]) {\n\t\t\tgs_stagesurface_unmap(video->mapped_surfaces[c]);\n\t\t\tvideo->mapped_surfaces[c] = NULL;\n\t\t}\n\t}\n\n\tfor (size_t i = 0; i < NUM_TEXTURES; i++) {\n\t\tfor (size_t c = 0; c < NUM_CHANNELS; c++) {\n\t\t\tif (video->copy_surfaces[i][c]) {\n\t\t\t\tgs_stagesurface_destroy(video->copy_surfaces[i][c]);\n\t\t\t\tvideo->copy_surfaces[i][c] = NULL;\n\t\t\t}\n\n\t\t\tvideo->active_copy_surfaces[i][c] = NULL;\n\t\t}\n#ifdef _WIN32\n\t\tif (video->copy_surfaces_encode[i]) {\n\t\t\tgs_stagesurface_destroy(video->copy_surfaces_encode[i]);\n\t\t\tvideo->copy_surfaces_encode[i] = NULL;\n\t\t}\n#endif\n\t}\n\n\tgs_texture_destroy(video->render_texture);\n\n\tfor (size_t c = 0; c < NUM_CHANNELS; c++) {\n\t\tif (video->convert_textures[c]) {\n\t\t\tgs_texture_destroy(video->convert_textures[c]);\n\t\t\tvideo->convert_textures[c] = NULL;\n\t\t}\n\t\tif (video->convert_textures_encode[c]) {\n\t\t\tgs_texture_destroy(video->convert_textures_encode[c]);\n\t\t\tvideo->convert_textures_encode[c] = NULL;\n\t\t}\n\t}\n\n\tgs_texture_destroy(video->output_texture);\n\tvideo->render_texture = NULL;\n\tvideo->output_texture = NULL;\n\n\tgs_leave_context();\n}\n\nvoid obs_free_video_mix(struct obs_core_video_mix *video)\n{\n\tif (video->video) {\n\t\tvideo_output_close(video->video);\n\t\tvideo->video = NULL;\n\n\t\tobs_free_render_textures(video);\n\n\t\tdeque_free(&video->vframe_info_buffer);\n\t\tdeque_free(&video->vframe_info_buffer_gpu);\n\n\t\tvideo->texture_rendered = false;\n\t\tmemset(video->textures_copied, 0, sizeof(video->textures_copied));\n\t\tvideo->texture_converted = false;\n\n\t\tpthread_mutex_destroy(&video->gpu_encoder_mutex);\n\t\tpthread_mutex_init_value(&video->gpu_encoder_mutex);\n\t\tda_free(video->gpu_encoders);\n\n\t\tvideo->gpu_encoder_active = 0;\n\t\tvideo->cur_texture = 0;\n\t}\n\tbfree(video);\n}\n\nstatic void obs_free_video(void)\n{\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tsize_t num_views = 0;\n\tfor (size_t i = 0; i < obs->video.mixes.num; i++) {\n\t\tstruct obs_core_video_mix *video = obs->video.mixes.array[i];\n\t\tif (video && video->view)\n\t\t\tnum_views++;\n\t\tobs_free_video_mix(video);\n\t\tobs->video.mixes.array[i] = NULL;\n\t}\n\tda_free(obs->video.mixes);\n\tif (num_views > 0)\n\t\tblog(LOG_WARNING, \"Number of remaining views: %ld\", num_views);\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\n\tpthread_mutex_destroy(&obs->video.mixes_mutex);\n\tpthread_mutex_init_value(&obs->video.mixes_mutex);\n\n\tfor (size_t i = 0; i < obs->video.ready_encoder_groups.num; i++) {\n\t\tobs_weak_encoder_release(obs->video.ready_encoder_groups.array[i]);\n\t}\n\tda_free(obs->video.ready_encoder_groups);\n\n\tpthread_mutex_destroy(&obs->video.encoder_group_mutex);\n\tpthread_mutex_init_value(&obs->video.encoder_group_mutex);\n\n\tpthread_mutex_destroy(&obs->video.task_mutex);\n\tpthread_mutex_init_value(&obs->video.task_mutex);\n\tdeque_free(&obs->video.tasks);\n}\n\nstatic void obs_free_graphics(void)\n{\n\tstruct obs_core_video *video = &obs->video;\n\n\tif (video->graphics) {\n\t\tgs_enter_context(video->graphics);\n\n\t\tgs_texture_destroy(video->transparent_texture);\n\n\t\tgs_samplerstate_destroy(video->point_sampler);\n\n\t\tgs_effect_destroy(video->default_effect);\n\t\tgs_effect_destroy(video->default_rect_effect);\n\t\tgs_effect_destroy(video->opaque_effect);\n\t\tgs_effect_destroy(video->solid_effect);\n\t\tgs_effect_destroy(video->conversion_effect);\n\t\tgs_effect_destroy(video->bicubic_effect);\n\t\tgs_effect_destroy(video->repeat_effect);\n\t\tgs_effect_destroy(video->lanczos_effect);\n\t\tgs_effect_destroy(video->area_effect);\n\t\tgs_effect_destroy(video->bilinear_lowres_effect);\n\t\tvideo->default_effect = NULL;\n\n\t\tgs_leave_context();\n\n\t\tgs_destroy(video->graphics);\n\t\tvideo->graphics = NULL;\n\t}\n}\n\nvoid set_monitoring_duplication_source(void *param)\n{\n\tobs_source_t *src = param;\n\tstruct obs_core_audio *audio = &obs->audio;\n\n\taudio->monitoring_duplicating_source = src;\n}\n\nstatic void apply_monitoring_deduplication(void *ignored, calldata_t *cd)\n{\n\tUNUSED_PARAMETER(ignored);\n\tobs_source_t *src = calldata_ptr(cd, \"source\");\n\n\tobs_queue_task(OBS_TASK_AUDIO, set_monitoring_duplication_source, src, false);\n}\n\nstatic void set_audio_thread(void *unused);\n\nstatic bool obs_init_audio(struct audio_output_info *ai)\n{\n\tstruct obs_core_audio *audio = &obs->audio;\n\tint errorcode;\n\n\tpthread_mutex_init_value(&audio->monitoring_mutex);\n\n\tif (pthread_mutex_init_recursive(&audio->monitoring_mutex) != 0)\n\t\treturn false;\n\tif (pthread_mutex_init(&audio->task_mutex, NULL) != 0)\n\t\treturn false;\n\n\tstruct obs_task_info audio_init = {.task = set_audio_thread};\n\tdeque_push_back(&audio->tasks, &audio_init, sizeof(audio_init));\n\n\taudio->monitoring_device_name = bstrdup(\"Default\");\n\taudio->monitoring_device_id = bstrdup(\"default\");\n\taudio->monitoring_duplicating_source = NULL;\n\n\tsignal_handler_add(obs->signals, \"void deduplication_changed(ptr source)\");\n\tsignal_handler_connect(obs->signals, \"deduplication_changed\", apply_monitoring_deduplication, NULL);\n\n\terrorcode = audio_output_open(&audio->audio, ai);\n\tif (errorcode == AUDIO_OUTPUT_SUCCESS)\n\t\treturn true;\n\telse if (errorcode == AUDIO_OUTPUT_INVALIDPARAM)\n\t\tblog(LOG_ERROR, \"Invalid audio parameters specified\");\n\telse\n\t\tblog(LOG_ERROR, \"Could not open audio output\");\n\n\treturn false;\n}\n\nstatic void stop_audio(void)\n{\n\tstruct obs_core_audio *audio = &obs->audio;\n\n\tif (audio->audio) {\n\t\taudio_output_close(audio->audio);\n\t\taudio->audio = NULL;\n\t}\n}\n\nstatic void obs_free_audio(void)\n{\n\tstruct obs_core_audio *audio = &obs->audio;\n\tif (audio->audio)\n\t\taudio_output_close(audio->audio);\n\n\tdeque_free(&audio->buffered_timestamps);\n\tda_free(audio->render_order);\n\tda_free(audio->root_nodes);\n\n\tda_free(audio->monitors);\n\tbfree(audio->monitoring_device_name);\n\tbfree(audio->monitoring_device_id);\n\tdeque_free(&audio->tasks);\n\tpthread_mutex_destroy(&audio->task_mutex);\n\tpthread_mutex_destroy(&audio->monitoring_mutex);\n\n\tmemset(audio, 0, sizeof(struct obs_core_audio));\n}\n\nstatic bool obs_init_data(void)\n{\n\tstruct obs_core_data *data = &obs->data;\n\n\tassert(data != NULL);\n\n\tpthread_mutex_init_value(&obs->data.displays_mutex);\n\tpthread_mutex_init_value(&obs->data.draw_callbacks_mutex);\n\n\tif (pthread_mutex_init_recursive(&data->sources_mutex) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init_recursive(&data->audio_sources_mutex) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init_recursive(&data->displays_mutex) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init_recursive(&data->outputs_mutex) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init_recursive(&data->encoders_mutex) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init_recursive(&data->services_mutex) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init_recursive(&obs->data.draw_callbacks_mutex) != 0)\n\t\tgoto fail;\n\tif (pthread_mutex_init_recursive(&obs->data.canvases_mutex) != 0)\n\t\tgoto fail;\n\n\tdata->sources = NULL;\n\tdata->public_sources = NULL;\n\tdata->canvases = NULL;\n\tdata->named_canvases = NULL;\n\tdata->private_data = obs_data_create();\n\tdata->valid = true;\n\nfail:\n\treturn data->valid;\n}\n\nvoid obs_main_view_free(struct obs_view *view)\n{\n\tif (!view)\n\t\treturn;\n\n\tfor (size_t i = 0; i < MAX_CHANNELS; i++)\n\t\tobs_source_release(view->channels[i]);\n\n\tmemset(view->channels, 0, sizeof(view->channels));\n\tpthread_mutex_destroy(&view->channels_mutex);\n}\n\n#define FREE_OBS_HASH_TABLE(handle, table, type)                                     \\\n\tdo {                                                                         \\\n\t\tstruct obs_context_data *ctx, *tmp;                                  \\\n\t\tint unfreed = 0;                                                     \\\n\t\tHASH_ITER (handle, *(struct obs_context_data **)table, ctx, tmp) {   \\\n\t\t\tobs_##type##_destroy((obs_##type##_t *)ctx);                 \\\n\t\t\tunfreed++;                                                   \\\n\t\t}                                                                    \\\n\t\tif (unfreed)                                                         \\\n\t\t\tblog(LOG_INFO, \"\\t%d \" #type \"(s) were remaining\", unfreed); \\\n\t} while (false)\n\n#define FREE_OBS_LINKED_LIST(type)                                                   \\\n\tdo {                                                                         \\\n\t\tint unfreed = 0;                                                     \\\n\t\twhile (data->first_##type) {                                         \\\n\t\t\tobs_##type##_destroy(data->first_##type);                    \\\n\t\t\tunfreed++;                                                   \\\n\t\t}                                                                    \\\n\t\tif (unfreed)                                                         \\\n\t\t\tblog(LOG_INFO, \"\\t%d \" #type \"(s) were remaining\", unfreed); \\\n\t} while (false)\n\nstatic void obs_free_data(void)\n{\n\tstruct obs_core_data *data = &obs->data;\n\n\tdata->valid = false;\n\n\tblog(LOG_INFO, \"Freeing OBS context data\");\n\n\t/* Free main canvas */\n\tobs_canvas_release(data->main_canvas);\n\n\tFREE_OBS_LINKED_LIST(output);\n\tFREE_OBS_LINKED_LIST(encoder);\n\tFREE_OBS_LINKED_LIST(display);\n\tFREE_OBS_LINKED_LIST(service);\n\n\tFREE_OBS_HASH_TABLE(hh, &data->public_sources, source);\n\tFREE_OBS_HASH_TABLE(hh_uuid, &data->sources, source);\n\tFREE_OBS_HASH_TABLE(hh, &data->named_canvases, canvas);\n\tFREE_OBS_HASH_TABLE(hh_uuid, &data->canvases, canvas);\n\n\tos_task_queue_wait(obs->destruction_task_thread);\n\n\tpthread_mutex_destroy(&data->sources_mutex);\n\tpthread_mutex_destroy(&data->audio_sources_mutex);\n\tpthread_mutex_destroy(&data->displays_mutex);\n\tpthread_mutex_destroy(&data->outputs_mutex);\n\tpthread_mutex_destroy(&data->encoders_mutex);\n\tpthread_mutex_destroy(&data->services_mutex);\n\tpthread_mutex_destroy(&data->draw_callbacks_mutex);\n\tpthread_mutex_destroy(&data->canvases_mutex);\n\tda_free(data->draw_callbacks);\n\tda_free(data->rendered_callbacks);\n\tda_free(data->tick_callbacks);\n\tobs_data_release(data->private_data);\n\n\tfor (size_t i = 0; i < data->protocols.num; i++)\n\t\tbfree(data->protocols.array[i]);\n\tda_free(data->protocols);\n\tda_free(data->sources_to_tick);\n}\n\nstatic const char *obs_signals[] = {\n\t\"void source_create(ptr source)\",\n\t\"void source_create_canvas(ptr source, ptr canvas)\",\n\t\"void source_destroy(ptr source)\",\n\t\"void source_remove(ptr source)\",\n\t\"void source_update(ptr source)\",\n\t\"void source_save(ptr source)\",\n\t\"void source_load(ptr source)\",\n\t\"void source_activate(ptr source)\",\n\t\"void source_deactivate(ptr source)\",\n\t\"void source_show(ptr source)\",\n\t\"void source_hide(ptr source)\",\n\t\"void source_audio_activate(ptr source)\",\n\t\"void source_audio_deactivate(ptr source)\",\n\t\"void source_filter_add(ptr source, ptr filter)\",\n\t\"void source_filter_remove(ptr source, ptr filter)\",\n\t\"void source_rename(ptr source, string new_name, string prev_name)\",\n\t\"void source_volume(ptr source, in out float volume)\",\n\t\"void source_volume_level(ptr source, float level, float magnitude, float peak)\",\n\t\"void source_transition_start(ptr source)\",\n\t\"void source_transition_video_stop(ptr source)\",\n\t\"void source_transition_stop(ptr source)\",\n\n\t\"void channel_change(int channel, in out ptr source, ptr prev_source)\",\n\n\t\"void hotkey_layout_change()\",\n\t\"void hotkey_register(ptr hotkey)\",\n\t\"void hotkey_unregister(ptr hotkey)\",\n\t\"void hotkey_bindings_changed(ptr hotkey)\",\n\n\t\"void canvas_create(ptr canvas)\",\n\t\"void canvas_remove(ptr canvas)\",\n\t\"void canvas_destroy(ptr canvas)\",\n\t\"void canvas_video_reset(ptr canvas)\",\n\t\"void canvas_rename(ptr canvas, string new_name, string prev_name)\",\n\n\t\"void video_reset()\",\n\n\tNULL,\n};\n\nstatic inline bool obs_init_handlers(void)\n{\n\tobs->signals = signal_handler_create();\n\tif (!obs->signals)\n\t\treturn false;\n\n\tobs->procs = proc_handler_create();\n\tif (!obs->procs)\n\t\treturn false;\n\n\treturn signal_handler_add_array(obs->signals, obs_signals);\n}\n\nstatic pthread_once_t obs_pthread_once_init_token = PTHREAD_ONCE_INIT;\nstatic inline bool obs_init_hotkeys(void)\n{\n\tstruct obs_core_hotkeys *hotkeys = &obs->hotkeys;\n\tbool success = false;\n\n\tassert(hotkeys != NULL);\n\n\thotkeys->hotkeys = NULL;\n\thotkeys->hotkey_pairs = NULL;\n\thotkeys->signals = obs->signals;\n\thotkeys->name_map_init_token = obs_pthread_once_init_token;\n\thotkeys->mute = bstrdup(\"Mute\");\n\thotkeys->unmute = bstrdup(\"Unmute\");\n\thotkeys->push_to_mute = bstrdup(\"Push-to-mute\");\n\thotkeys->push_to_talk = bstrdup(\"Push-to-talk\");\n\thotkeys->sceneitem_show = bstrdup(\"Show '%1'\");\n\thotkeys->sceneitem_hide = bstrdup(\"Hide '%1'\");\n\n\tif (!obs_hotkeys_platform_init(hotkeys))\n\t\treturn false;\n\n\tif (pthread_mutex_init_recursive(&hotkeys->mutex) != 0)\n\t\tgoto fail;\n\n\tif (os_event_init(&hotkeys->stop_event, OS_EVENT_TYPE_MANUAL) != 0)\n\t\tgoto fail;\n\tif (pthread_create(&hotkeys->hotkey_thread, NULL, obs_hotkey_thread, NULL))\n\t\tgoto fail;\n\n\thotkeys->strict_modifiers = true;\n\thotkeys->hotkey_thread_initialized = true;\n\n\tsuccess = true;\n\nfail:\n\treturn success;\n}\n\nstatic inline void stop_hotkeys(void)\n{\n\tstruct obs_core_hotkeys *hotkeys = &obs->hotkeys;\n\tvoid *thread_ret;\n\n\tif (hotkeys->hotkey_thread_initialized) {\n\t\tos_event_signal(hotkeys->stop_event);\n\t\tpthread_join(hotkeys->hotkey_thread, &thread_ret);\n\t\thotkeys->hotkey_thread_initialized = false;\n\t}\n\n\tos_event_destroy(hotkeys->stop_event);\n\tobs_hotkeys_free();\n}\n\nstatic inline void obs_free_hotkeys(void)\n{\n\tstruct obs_core_hotkeys *hotkeys = &obs->hotkeys;\n\n\tbfree(hotkeys->mute);\n\tbfree(hotkeys->unmute);\n\tbfree(hotkeys->push_to_mute);\n\tbfree(hotkeys->push_to_talk);\n\tbfree(hotkeys->sceneitem_show);\n\tbfree(hotkeys->sceneitem_hide);\n\n\tobs_hotkey_name_map_free();\n\n\tobs_hotkeys_platform_free(hotkeys);\n\tpthread_mutex_destroy(&hotkeys->mutex);\n}\n\nextern const struct obs_source_info scene_info;\nextern const struct obs_source_info group_info;\n\nstatic const char *submix_name(void *unused)\n{\n\tUNUSED_PARAMETER(unused);\n\treturn \"Audio line (internal use only)\";\n}\n\nconst struct obs_source_info audio_line_info = {\n\t.id = \"audio_line\",\n\t.type = OBS_SOURCE_TYPE_INPUT,\n\t.output_flags = OBS_SOURCE_AUDIO | OBS_SOURCE_CAP_DISABLED | OBS_SOURCE_SUBMIX,\n\t.get_name = submix_name,\n};\n\nextern void log_system_info(void);\n\nstatic bool obs_init(const char *locale, const char *module_config_path, profiler_name_store_t *store)\n{\n\tobs = bzalloc(sizeof(struct obs_core));\n\n\tpthread_mutex_init_value(&obs->audio.monitoring_mutex);\n\tpthread_mutex_init_value(&obs->audio.task_mutex);\n\tpthread_mutex_init_value(&obs->video.task_mutex);\n\tpthread_mutex_init_value(&obs->video.encoder_group_mutex);\n\tpthread_mutex_init_value(&obs->video.mixes_mutex);\n\n\tobs->name_store_owned = !store;\n\tobs->name_store = store ? store : profiler_name_store_create();\n\tif (!obs->name_store) {\n\t\tblog(LOG_ERROR, \"Couldn't create profiler name store\");\n\t\treturn false;\n\t}\n\n\tlog_system_info();\n\n\tif (!obs_init_data())\n\t\treturn false;\n\tif (!obs_init_handlers())\n\t\treturn false;\n\tif (!obs_init_hotkeys())\n\t\treturn false;\n\n\t/* Create persistent main canvas. */\n\tobs->data.main_canvas = obs_create_main_canvas();\n\tif (!obs->data.main_canvas)\n\t\treturn false;\n\n\tobs->destruction_task_thread = os_task_queue_create();\n\tif (!obs->destruction_task_thread)\n\t\treturn false;\n\n\tif (module_config_path)\n\t\tobs->module_config_path = bstrdup(module_config_path);\n\tobs->locale = bstrdup(locale);\n\tobs_register_source(&scene_info);\n\tobs_register_source(&group_info);\n\tobs_register_source(&audio_line_info);\n\tadd_default_module_paths();\n\treturn true;\n}\n\n#ifdef _WIN32\nextern bool initialize_com(void);\nextern void uninitialize_com(void);\nstatic bool com_initialized = false;\n#endif\n\n/* Separate from actual context initialization\n * since this can be set before startup and persist\n * after shutdown. */\nstatic DARRAY(struct dstr) core_module_paths = {0};\n\nchar *obs_find_data_file(const char *file)\n{\n\tstruct dstr path = {0};\n\n\tchar *result = find_libobs_data_file(file);\n\tif (result)\n\t\treturn result;\n\n\tfor (size_t i = 0; i < core_module_paths.num; ++i) {\n\t\tif (check_path(file, core_module_paths.array[i].array, &path))\n\t\t\treturn path.array;\n\t}\n\n\tblog(LOG_ERROR, \"Failed to find file '%s' in libobs data directory\", file);\n\n\tdstr_free(&path);\n\treturn NULL;\n}\n\nvoid obs_add_data_path(const char *path)\n{\n\tstruct dstr *new_path = da_push_back_new(core_module_paths);\n\tdstr_init_copy(new_path, path);\n}\n\nbool obs_remove_data_path(const char *path)\n{\n\tfor (size_t i = 0; i < core_module_paths.num; ++i) {\n\t\tint result = dstr_cmp(&core_module_paths.array[i], path);\n\n\t\tif (result == 0) {\n\t\t\tdstr_free(&core_module_paths.array[i]);\n\t\t\tda_erase(core_module_paths, i);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nstatic const char *obs_startup_name = \"obs_startup\";\nbool obs_startup(const char *locale, const char *module_config_path, profiler_name_store_t *store)\n{\n\tbool success;\n\n\tprofile_start(obs_startup_name);\n\n\tif (obs) {\n\t\tblog(LOG_WARNING, \"Tried to call obs_startup more than once\");\n\t\treturn false;\n\t}\n\n#ifdef _WIN32\n\tcom_initialized = initialize_com();\n#endif\n\n\tsuccess = obs_init(locale, module_config_path, store);\n\tprofile_end(obs_startup_name);\n\tif (!success)\n\t\tobs_shutdown();\n\n\treturn success;\n}\n\nstatic struct obs_cmdline_args cmdline_args = {0, NULL};\nvoid obs_set_cmdline_args(int argc, const char *const *argv)\n{\n\tchar *data;\n\tsize_t len;\n\tint i;\n\n\t/* Once argc is set (non-zero) we shouldn't call again */\n\tif (cmdline_args.argc)\n\t\treturn;\n\n\tcmdline_args.argc = argc;\n\n\t/* Safely copy over argv */\n\tlen = 0;\n\tfor (i = 0; i < argc; i++)\n\t\tlen += strlen(argv[i]) + 1;\n\n\tcmdline_args.argv = bmalloc(sizeof(char *) * (argc + 1) + len);\n\tdata = (char *)cmdline_args.argv + sizeof(char *) * (argc + 1);\n\n\tfor (i = 0; i < argc; i++) {\n\t\tcmdline_args.argv[i] = data;\n\t\tlen = strlen(argv[i]) + 1;\n\t\tmemcpy(data, argv[i], len);\n\t\tdata += len;\n\t}\n\n\tcmdline_args.argv[argc] = NULL;\n}\n\nstruct obs_cmdline_args obs_get_cmdline_args(void)\n{\n\treturn cmdline_args;\n}\n\nvoid obs_shutdown(void)\n{\n\tstruct obs_module *module;\n\n\tobs_wait_for_destroy_queue();\n\n\tfor (size_t i = 0; i < obs->source_types.num; i++) {\n\t\tstruct obs_source_info *item = &obs->source_types.array[i];\n\t\tif (item->type_data && item->free_type_data)\n\t\t\titem->free_type_data(item->type_data);\n\t\tif (item->id)\n\t\t\tbfree((void *)item->id);\n\t}\n\tda_free(obs->source_types);\n\n#define FREE_REGISTERED_TYPES(structure, list)                         \\\n\tdo {                                                           \\\n\t\tfor (size_t i = 0; i < list.num; i++) {                \\\n\t\t\tstruct structure *item = &list.array[i];       \\\n\t\t\tif (item->type_data && item->free_type_data)   \\\n\t\t\t\titem->free_type_data(item->type_data); \\\n\t\t}                                                      \\\n\t\tda_free(list);                                         \\\n\t} while (false)\n\n\tFREE_REGISTERED_TYPES(obs_output_info, obs->output_types);\n\tFREE_REGISTERED_TYPES(obs_encoder_info, obs->encoder_types);\n\tFREE_REGISTERED_TYPES(obs_service_info, obs->service_types);\n\n#undef FREE_REGISTERED_TYPES\n\n\tda_free(obs->input_types);\n\tda_free(obs->filter_types);\n\tda_free(obs->transition_types);\n\n\tstop_video();\n\tstop_audio();\n\tstop_hotkeys();\n\n\tmodule = obs->first_module;\n\twhile (module) {\n\t\tstruct obs_module *next = module->next;\n\t\tfree_module(module);\n\t\tmodule = next;\n\t}\n\tobs->first_module = NULL;\n\n\tmodule = obs->first_disabled_module;\n\twhile (module) {\n\t\tstruct obs_module *next = module->next;\n\t\tfree_module(module);\n\t\tmodule = next;\n\t}\n\tobs->first_disabled_module = NULL;\n\n\tobs_free_data();\n\tobs_free_audio();\n\tobs_free_video();\n\tos_task_queue_destroy(obs->destruction_task_thread);\n\tobs_free_hotkeys();\n\tobs_free_graphics();\n\tproc_handler_destroy(obs->procs);\n\tsignal_handler_destroy(obs->signals);\n\tobs->procs = NULL;\n\tobs->signals = NULL;\n\n\tfor (size_t i = 0; i < obs->module_paths.num; i++) {\n\t\tfree_module_path(obs->module_paths.array + i);\n\t}\n\tda_free(obs->module_paths);\n\n\tfor (size_t i = 0; i < obs->safe_modules.num; i++) {\n\t\tbfree(obs->safe_modules.array[i]);\n\t}\n\tda_free(obs->safe_modules);\n\n\tfor (size_t i = 0; i < obs->disabled_modules.num; i++) {\n\t\tbfree(obs->disabled_modules.array[i]);\n\t}\n\tda_free(obs->disabled_modules);\n\n\tfor (size_t i = 0; i < obs->core_modules.num; i++) {\n\t\tbfree(obs->core_modules.array[i]);\n\t}\n\tda_free(obs->core_modules);\n\n\tif (obs->name_store_owned)\n\t\tprofiler_name_store_free(obs->name_store);\n\n\tbfree(obs->module_config_path);\n\tbfree(obs->locale);\n\tbfree(obs);\n\tobs = NULL;\n\tbfree(cmdline_args.argv);\n\n#ifdef _WIN32\n\tif (com_initialized)\n\t\tuninitialize_com();\n#endif\n}\n\nbool obs_initialized(void)\n{\n\treturn obs != NULL;\n}\n\nuint32_t obs_get_version(void)\n{\n\treturn LIBOBS_API_VER;\n}\n\nconst char *obs_get_version_string(void)\n{\n\treturn OBS_VERSION;\n}\n\nvoid obs_set_locale(const char *locale)\n{\n\tstruct obs_module *module;\n\n\tif (obs->locale)\n\t\tbfree(obs->locale);\n\tobs->locale = bstrdup(locale);\n\n\tmodule = obs->first_module;\n\twhile (module) {\n\t\tif (module->set_locale)\n\t\t\tmodule->set_locale(locale);\n\n\t\tmodule = module->next;\n\t}\n}\n\nconst char *obs_get_locale(void)\n{\n\treturn obs->locale;\n}\n\n#define OBS_SIZE_MIN 2\n#define OBS_SIZE_MAX (32 * 1024)\n\nstatic inline bool size_valid(uint32_t width, uint32_t height)\n{\n\treturn (width >= OBS_SIZE_MIN && height >= OBS_SIZE_MIN && width <= OBS_SIZE_MAX && height <= OBS_SIZE_MAX);\n}\n\nint obs_reset_video(struct obs_video_info *ovi)\n{\n\tif (!obs)\n\t\treturn OBS_VIDEO_FAIL;\n\n\t/* don't allow changing of video settings if active. */\n\tif (obs_video_active())\n\t\treturn OBS_VIDEO_CURRENTLY_ACTIVE;\n\n\tif (!size_valid(ovi->output_width, ovi->output_height) || !size_valid(ovi->base_width, ovi->base_height))\n\t\treturn OBS_VIDEO_INVALID_PARAM;\n\n\tstop_video();\n\tobs_free_canvas_mixes();\n\tobs_free_video();\n\n\t/* align to multiple-of-two and SSE alignment sizes */\n\tovi->output_width &= 0xFFFFFFFC;\n\tovi->output_height &= 0xFFFFFFFE;\n\n\tif (!obs->video.graphics) {\n\t\tint errorcode = obs_init_graphics(ovi);\n\t\tif (errorcode != OBS_VIDEO_SUCCESS) {\n\t\t\tobs_free_graphics();\n\t\t\treturn errorcode;\n\t\t}\n\t}\n\n\tconst char *scale_type_name = \"\";\n\tswitch (ovi->scale_type) {\n\tcase OBS_SCALE_DISABLE:\n\t\tscale_type_name = \"Disabled\";\n\t\tbreak;\n\tcase OBS_SCALE_POINT:\n\t\tscale_type_name = \"Point\";\n\t\tbreak;\n\tcase OBS_SCALE_BICUBIC:\n\t\tscale_type_name = \"Bicubic\";\n\t\tbreak;\n\tcase OBS_SCALE_BILINEAR:\n\t\tscale_type_name = \"Bilinear\";\n\t\tbreak;\n\tcase OBS_SCALE_LANCZOS:\n\t\tscale_type_name = \"Lanczos\";\n\t\tbreak;\n\tcase OBS_SCALE_AREA:\n\t\tscale_type_name = \"Area\";\n\t\tbreak;\n\t}\n\n\tbool yuv = format_is_yuv(ovi->output_format);\n\tconst char *yuv_format = get_video_colorspace_name(ovi->colorspace);\n\tconst char *yuv_range = get_video_range_name(ovi->output_format, ovi->range);\n\n\tblog(LOG_INFO, \"---------------------------------\");\n\tblog(LOG_INFO,\n\t     \"video settings reset:\\n\"\n\t     \"\\tbase resolution:   %dx%d\\n\"\n\t     \"\\toutput resolution: %dx%d\\n\"\n\t     \"\\tdownscale filter:  %s\\n\"\n\t     \"\\tfps:               %d/%d\\n\"\n\t     \"\\tformat:            %s\\n\"\n\t     \"\\tYUV mode:          %s%s%s\",\n\t     ovi->base_width, ovi->base_height, ovi->output_width, ovi->output_height, scale_type_name, ovi->fps_num,\n\t     ovi->fps_den, get_video_format_name(ovi->output_format), yuv ? yuv_format : \"None\", yuv ? \"/\" : \"\",\n\t     yuv ? yuv_range : \"\");\n\n\tsource_profiler_reset_video(ovi);\n\n\treturn obs_init_video(ovi);\n}\n\n#ifndef SEC_TO_MSEC\n#define SEC_TO_MSEC 1000\n#endif\n\nbool obs_reset_audio2(const struct obs_audio_info2 *oai)\n{\n\tstruct obs_core_audio *audio = &obs->audio;\n\tstruct audio_output_info ai;\n\n\t/* don't allow changing of audio settings if active. */\n\tif (!obs || (audio->audio && audio_output_active(audio->audio)))\n\t\treturn false;\n\n\tobs_free_audio();\n\tif (!oai)\n\t\treturn true;\n\n\tif (oai->max_buffering_ms) {\n\t\tuint32_t max_frames = oai->max_buffering_ms * oai->samples_per_sec / SEC_TO_MSEC;\n\t\tmax_frames += (AUDIO_OUTPUT_FRAMES - 1);\n\t\taudio->max_buffering_ticks = max_frames / AUDIO_OUTPUT_FRAMES;\n\t} else {\n\t\taudio->max_buffering_ticks = 45;\n\t}\n\taudio->fixed_buffer = oai->fixed_buffering;\n\n\tint max_buffering_ms =\n\t\taudio->max_buffering_ticks * AUDIO_OUTPUT_FRAMES * SEC_TO_MSEC / (int)oai->samples_per_sec;\n\n\tai.name = \"Audio\";\n\tai.samples_per_sec = oai->samples_per_sec;\n\tai.format = AUDIO_FORMAT_FLOAT_PLANAR;\n\tai.speakers = oai->speakers;\n\tai.input_callback = audio_callback;\n\n\tblog(LOG_INFO, \"---------------------------------\");\n\tblog(LOG_INFO,\n\t     \"audio settings reset:\\n\"\n\t     \"\\tsamples per sec: %d\\n\"\n\t     \"\\tspeakers:        %d\\n\"\n\t     \"\\tmax buffering:   %d milliseconds\\n\"\n\t     \"\\tbuffering type:  %s\",\n\t     (int)ai.samples_per_sec, (int)ai.speakers, max_buffering_ms,\n\t     oai->fixed_buffering ? \"fixed\" : \"dynamically increasing\");\n\n\treturn obs_init_audio(&ai);\n}\n\nbool obs_reset_audio(const struct obs_audio_info *oai)\n{\n\tstruct obs_audio_info2 oai2 = {\n\t\t.samples_per_sec = oai->samples_per_sec,\n\t\t.speakers = oai->speakers,\n\t};\n\n\treturn obs_reset_audio2(&oai2);\n}\n\nbool obs_get_video_info(struct obs_video_info *ovi)\n{\n\tif (!obs->video.graphics || !obs->data.main_canvas->mix)\n\t\treturn false;\n\n\t*ovi = obs->data.main_canvas->mix->ovi;\n\treturn true;\n}\n\nfloat obs_get_video_sdr_white_level(void)\n{\n\tstruct obs_core_video *video = &obs->video;\n\treturn video->graphics ? video->sdr_white_level : 300.f;\n}\n\nfloat obs_get_video_hdr_nominal_peak_level(void)\n{\n\tstruct obs_core_video *video = &obs->video;\n\treturn video->graphics ? video->hdr_nominal_peak_level : 1000.f;\n}\n\nvoid obs_set_video_levels(float sdr_white_level, float hdr_nominal_peak_level)\n{\n\tstruct obs_core_video *video = &obs->video;\n\tassert(video->graphics);\n\n\tvideo->sdr_white_level = sdr_white_level;\n\tvideo->hdr_nominal_peak_level = hdr_nominal_peak_level;\n}\n\nbool obs_get_audio_info(struct obs_audio_info *oai)\n{\n\tstruct obs_core_audio *audio = &obs->audio;\n\tconst struct audio_output_info *info;\n\n\tif (!oai || !audio->audio)\n\t\treturn false;\n\n\tinfo = audio_output_get_info(audio->audio);\n\n\toai->samples_per_sec = info->samples_per_sec;\n\toai->speakers = info->speakers;\n\treturn true;\n}\n\nbool obs_get_audio_info2(struct obs_audio_info2 *oai2)\n{\n\tstruct obs_core_audio *audio = &obs->audio;\n\tstruct obs_audio_info oai;\n\n\tif (!obs_get_audio_info(&oai) || !oai2 || !audio->audio) {\n\t\treturn false;\n\t} else {\n\t\toai2->samples_per_sec = oai.samples_per_sec;\n\t\toai2->speakers = oai.speakers;\n\t\toai2->fixed_buffering = audio->fixed_buffer;\n\t\toai2->max_buffering_ms =\n\t\t\taudio->max_buffering_ticks * AUDIO_OUTPUT_FRAMES * SEC_TO_MSEC / (int)oai2->samples_per_sec;\n\t\treturn true;\n\t}\n}\n\nbool obs_enum_source_types(size_t idx, const char **id)\n{\n\tif (idx >= obs->source_types.num)\n\t\treturn false;\n\t*id = obs->source_types.array[idx].id;\n\treturn true;\n}\n\nbool obs_enum_input_types(size_t idx, const char **id)\n{\n\tif (idx >= obs->input_types.num)\n\t\treturn false;\n\t*id = obs->input_types.array[idx].id;\n\treturn true;\n}\n\nbool obs_enum_input_types2(size_t idx, const char **id, const char **unversioned_id)\n{\n\tif (idx >= obs->input_types.num)\n\t\treturn false;\n\tif (id)\n\t\t*id = obs->input_types.array[idx].id;\n\tif (unversioned_id)\n\t\t*unversioned_id = obs->input_types.array[idx].unversioned_id;\n\treturn true;\n}\n\nconst char *obs_get_latest_input_type_id(const char *unversioned_id)\n{\n\tstruct obs_source_info *latest = NULL;\n\tint version = -1;\n\n\tif (!unversioned_id)\n\t\treturn NULL;\n\n\tfor (size_t i = 0; i < obs->source_types.num; i++) {\n\t\tstruct obs_source_info *info = &obs->source_types.array[i];\n\t\tif (strcmp(info->unversioned_id, unversioned_id) == 0 && (int)info->version > version) {\n\t\t\tlatest = info;\n\t\t\tversion = info->version;\n\t\t}\n\t}\n\n\tassert(!!latest);\n\tif (!latest)\n\t\treturn NULL;\n\n\treturn latest->id;\n}\n\nbool obs_enum_filter_types(size_t idx, const char **id)\n{\n\tif (idx >= obs->filter_types.num)\n\t\treturn false;\n\t*id = obs->filter_types.array[idx].id;\n\treturn true;\n}\n\nbool obs_enum_transition_types(size_t idx, const char **id)\n{\n\tif (idx >= obs->transition_types.num)\n\t\treturn false;\n\t*id = obs->transition_types.array[idx].id;\n\treturn true;\n}\n\nbool obs_enum_output_types(size_t idx, const char **id)\n{\n\tif (idx >= obs->output_types.num)\n\t\treturn false;\n\t*id = obs->output_types.array[idx].id;\n\treturn true;\n}\n\nbool obs_enum_encoder_types(size_t idx, const char **id)\n{\n\tif (idx >= obs->encoder_types.num)\n\t\treturn false;\n\t*id = obs->encoder_types.array[idx].id;\n\treturn true;\n}\n\nbool obs_enum_service_types(size_t idx, const char **id)\n{\n\tif (idx >= obs->service_types.num)\n\t\treturn false;\n\t*id = obs->service_types.array[idx].id;\n\treturn true;\n}\n\nvoid obs_enter_graphics(void)\n{\n\tif (obs->video.graphics)\n\t\tgs_enter_context(obs->video.graphics);\n}\n\nvoid obs_leave_graphics(void)\n{\n\tif (obs->video.graphics)\n\t\tgs_leave_context();\n}\n\naudio_t *obs_get_audio(void)\n{\n\treturn obs->audio.audio;\n}\n\nvideo_t *obs_get_video(void)\n{\n\treturn obs->data.main_canvas->mix->video;\n}\n\nobs_source_t *obs_get_output_source(uint32_t channel)\n{\n\treturn obs_canvas_get_channel(obs->data.main_canvas, channel);\n}\n\nvoid obs_set_output_source(uint32_t channel, obs_source_t *source)\n{\n\tobs_canvas_set_channel(obs->data.main_canvas, channel, source);\n}\n\nvoid obs_enum_sources(bool (*enum_proc)(void *, obs_source_t *), void *param)\n{\n\tobs_source_t *source;\n\n\tpthread_mutex_lock(&obs->data.sources_mutex);\n\tsource = obs->data.sources;\n\n\twhile (source) {\n\t\tobs_source_t *s = obs_source_get_ref(source);\n\t\tif (s) {\n\t\t\tif (!s->context.private) {\n\t\t\t\tif (s->info.type == OBS_SOURCE_TYPE_INPUT && !enum_proc(param, s)) {\n\t\t\t\t\tobs_source_release(s);\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (strcmp(s->info.id, group_info.id) == 0 && !enum_proc(param, s)) {\n\t\t\t\t\tobs_source_release(s);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tobs_source_release(s);\n\t\t}\n\n\t\tsource = (obs_source_t *)source->context.hh_uuid.next;\n\t}\n\n\tpthread_mutex_unlock(&obs->data.sources_mutex);\n}\n\nvoid obs_canvas_enum_scenes(obs_canvas_t *canvas, bool (*enum_proc)(void *, obs_source_t *), void *param)\n{\n\tobs_source_t *source;\n\n\tpthread_mutex_lock(&canvas->sources_mutex);\n\n\tsource = canvas->sources;\n\twhile (source) {\n\t\tobs_source_t *s = obs_source_get_ref(source);\n\t\tif (s) {\n\t\t\tif (source->info.type == OBS_SOURCE_TYPE_SCENE && !enum_proc(param, s)) {\n\t\t\t\tobs_source_release(s);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tobs_source_release(s);\n\t\t}\n\n\t\tsource = (obs_source_t *)source->context.hh.next;\n\t}\n\n\tpthread_mutex_unlock(&canvas->sources_mutex);\n}\n\nvoid obs_enum_scenes(bool (*enum_proc)(void *, obs_source_t *), void *param)\n{\n\tobs_canvas_enum_scenes(obs->data.main_canvas, enum_proc, param);\n}\n\nstatic inline void obs_enum(void *pstart, pthread_mutex_t *mutex, void *proc, void *param)\n{\n\tstruct obs_context_data **start = pstart, *context;\n\tbool (*enum_proc)(void *, void *) = proc;\n\n\tassert(start);\n\tassert(mutex);\n\tassert(enum_proc);\n\n\tpthread_mutex_lock(mutex);\n\n\tcontext = *start;\n\twhile (context) {\n\t\tif (!enum_proc(param, context))\n\t\t\tbreak;\n\n\t\tcontext = context->next;\n\t}\n\n\tpthread_mutex_unlock(mutex);\n}\n\nstatic inline void obs_enum_uuid(void *pstart, pthread_mutex_t *mutex, void *proc, void *param)\n{\n\tstruct obs_context_data **start = pstart, *context, *tmp;\n\tbool (*enum_proc)(void *, void *) = proc;\n\n\tassert(start);\n\tassert(mutex);\n\tassert(enum_proc);\n\n\tpthread_mutex_lock(mutex);\n\n\tHASH_ITER (hh_uuid, *start, context, tmp) {\n\t\tif (!enum_proc(param, context))\n\t\t\tbreak;\n\t}\n\n\tpthread_mutex_unlock(mutex);\n}\n\nvoid obs_enum_all_sources(bool (*enum_proc)(void *, obs_source_t *), void *param)\n{\n\tobs_enum_uuid(&obs->data.sources, &obs->data.sources_mutex, enum_proc, param);\n}\n\nvoid obs_enum_outputs(bool (*enum_proc)(void *, obs_output_t *), void *param)\n{\n\tobs_enum(&obs->data.first_output, &obs->data.outputs_mutex, enum_proc, param);\n}\n\nvoid obs_enum_encoders(bool (*enum_proc)(void *, obs_encoder_t *), void *param)\n{\n\tobs_enum(&obs->data.first_encoder, &obs->data.encoders_mutex, enum_proc, param);\n}\n\nvoid obs_enum_services(bool (*enum_proc)(void *, obs_service_t *), void *param)\n{\n\tobs_enum(&obs->data.first_service, &obs->data.services_mutex, enum_proc, param);\n}\n\nvoid obs_enum_canvases(bool (*enum_proc)(void *, obs_canvas_t *), void *param)\n{\n\tstruct obs_context_data *start = (struct obs_context_data *)obs->data.named_canvases;\n\tstruct obs_context_data *context, *tmp;\n\n\tpthread_mutex_lock(&obs->data.canvases_mutex);\n\n\tHASH_ITER (hh, start, context, tmp) {\n\t\tobs_canvas_t *canvas = (obs_canvas_t *)context;\n\t\tif (!enum_proc(param, canvas))\n\t\t\tbreak;\n\t}\n\n\tpthread_mutex_unlock(&obs->data.canvases_mutex);\n}\n\nstatic inline void *get_context_by_name(void *vfirst, const char *name, pthread_mutex_t *mutex, void *(*addref)(void *))\n{\n\tstruct obs_context_data **first = vfirst;\n\tstruct obs_context_data *context;\n\n\tpthread_mutex_lock(mutex);\n\n\t/* If context list head has a hash table, look the name up in there */\n\tif (*first && (*first)->hh.tbl) {\n\t\tHASH_FIND_STR(*first, name, context);\n\t} else {\n\t\tcontext = *first;\n\t\twhile (context) {\n\t\t\tif (!context->private && strcmp(context->name, name) == 0) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcontext = context->next;\n\t\t}\n\t}\n\n\tif (context)\n\t\taddref(context);\n\n\tpthread_mutex_unlock(mutex);\n\treturn context;\n}\n\nstatic void *get_context_by_uuid(void *ptable, const char *uuid, pthread_mutex_t *mutex, void *(*addref)(void *))\n{\n\tstruct obs_context_data **ht = ptable;\n\tstruct obs_context_data *context;\n\n\tpthread_mutex_lock(mutex);\n\n\tHASH_FIND_UUID(*ht, uuid, context);\n\tif (context)\n\t\taddref(context);\n\n\tpthread_mutex_unlock(mutex);\n\treturn context;\n}\n\nstatic inline void *obs_source_addref_safe_(void *ref)\n{\n\treturn obs_source_get_ref(ref);\n}\n\nstatic inline void *obs_output_addref_safe_(void *ref)\n{\n\treturn obs_output_get_ref(ref);\n}\n\nstatic inline void *obs_encoder_addref_safe_(void *ref)\n{\n\treturn obs_encoder_get_ref(ref);\n}\n\nstatic inline void *obs_service_addref_safe_(void *ref)\n{\n\treturn obs_service_get_ref(ref);\n}\n\nstatic inline void *obs_canvas_addref_safe_(void *ref)\n{\n\treturn obs_canvas_get_ref(ref);\n}\n\nobs_source_t *obs_get_source_by_name(const char *name)\n{\n\tobs_source_t *source =\n\t\tget_context_by_name(&obs->data.public_sources, name, &obs->data.sources_mutex, obs_source_addref_safe_);\n\t/* For backwards compat: Also look up source name in main canvas's scenes list. */\n\tif (!source) {\n\t\tsource = get_context_by_name(&obs->data.main_canvas->sources, name,\n\t\t\t\t\t     &obs->data.main_canvas->sources_mutex, obs_source_addref_safe_);\n\t}\n\treturn source;\n}\n\nobs_source_t *obs_get_source_by_uuid(const char *uuid)\n{\n\treturn get_context_by_uuid(&obs->data.sources, uuid, &obs->data.sources_mutex, obs_source_addref_safe_);\n}\n\nobs_canvas_t *obs_get_canvas_by_name(const char *name)\n{\n\treturn get_context_by_name(&obs->data.named_canvases, name, &obs->data.canvases_mutex, obs_canvas_addref_safe_);\n}\n\nobs_canvas_t *obs_get_canvas_by_uuid(const char *uuid)\n{\n\treturn get_context_by_uuid(&obs->data.canvases, uuid, &obs->data.canvases_mutex, obs_canvas_addref_safe_);\n}\n\nobs_source_t *obs_canvas_get_source_by_name(obs_canvas_t *canvas, const char *name)\n{\n\treturn get_context_by_name(&canvas->sources, name, &canvas->sources_mutex, obs_source_addref_safe_);\n}\n\nobs_scene_t *obs_canvas_get_scene_by_name(obs_canvas_t *canvas, const char *name)\n{\n\tobs_source_t *source = obs_canvas_get_source_by_name(canvas, name);\n\tobs_scene_t *scene = obs_scene_from_source(source);\n\tif (!scene) {\n\t\tobs_source_release(source);\n\t\treturn NULL;\n\t}\n\treturn scene;\n}\n\nobs_source_t *obs_get_transition_by_name(const char *name)\n{\n\tstruct obs_source **first = &obs->data.sources;\n\tstruct obs_source *source;\n\n\tpthread_mutex_lock(&obs->data.sources_mutex);\n\n\t/* Transitions are private but can be found via this method, so we\n\t * can't look them up by name in the public_sources hash table. */\n\tsource = *first;\n\twhile (source) {\n\t\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION && strcmp(source->context.name, name) == 0) {\n\t\t\tsource = obs_source_addref_safe_(source);\n\t\t\tbreak;\n\t\t}\n\t\tsource = (void *)source->context.hh_uuid.next;\n\t}\n\n\tpthread_mutex_unlock(&obs->data.sources_mutex);\n\treturn source;\n}\n\nobs_source_t *obs_get_transition_by_uuid(const char *uuid)\n{\n\tobs_source_t *source = obs_get_source_by_uuid(uuid);\n\n\tif (source && source->info.type == OBS_SOURCE_TYPE_TRANSITION)\n\t\treturn source;\n\telse if (source)\n\t\tobs_source_release(source);\n\n\treturn NULL;\n}\n\nobs_output_t *obs_get_output_by_name(const char *name)\n{\n\treturn get_context_by_name(&obs->data.first_output, name, &obs->data.outputs_mutex, obs_output_addref_safe_);\n}\n\nobs_encoder_t *obs_get_encoder_by_name(const char *name)\n{\n\treturn get_context_by_name(&obs->data.first_encoder, name, &obs->data.encoders_mutex, obs_encoder_addref_safe_);\n}\n\nobs_service_t *obs_get_service_by_name(const char *name)\n{\n\treturn get_context_by_name(&obs->data.first_service, name, &obs->data.services_mutex, obs_service_addref_safe_);\n}\n\ngs_effect_t *obs_get_base_effect(enum obs_base_effect effect)\n{\n\tswitch (effect) {\n\tcase OBS_EFFECT_DEFAULT:\n\t\treturn obs->video.default_effect;\n\tcase OBS_EFFECT_DEFAULT_RECT:\n\t\treturn obs->video.default_rect_effect;\n\tcase OBS_EFFECT_OPAQUE:\n\t\treturn obs->video.opaque_effect;\n\tcase OBS_EFFECT_SOLID:\n\t\treturn obs->video.solid_effect;\n\tcase OBS_EFFECT_REPEAT:\n\t\treturn obs->video.repeat_effect;\n\tcase OBS_EFFECT_BICUBIC:\n\t\treturn obs->video.bicubic_effect;\n\tcase OBS_EFFECT_LANCZOS:\n\t\treturn obs->video.lanczos_effect;\n\tcase OBS_EFFECT_AREA:\n\t\treturn obs->video.area_effect;\n\tcase OBS_EFFECT_BILINEAR_LOWRES:\n\t\treturn obs->video.bilinear_lowres_effect;\n\tcase OBS_EFFECT_PREMULTIPLIED_ALPHA:\n\t\treturn obs->video.premultiplied_alpha_effect;\n\t}\n\n\treturn NULL;\n}\n\nsignal_handler_t *obs_get_signal_handler(void)\n{\n\treturn obs->signals;\n}\n\nproc_handler_t *obs_get_proc_handler(void)\n{\n\treturn obs->procs;\n}\n\nstatic void obs_render_canvas_texture_internal(obs_canvas_t *canvas, enum gs_blend_type src_c,\n\t\t\t\t\t       enum gs_blend_type dest_c, enum gs_blend_type src_a,\n\t\t\t\t\t       enum gs_blend_type dest_a)\n{\n\tstruct obs_core_video_mix *video;\n\tgs_texture_t *tex;\n\tgs_effect_t *effect;\n\tgs_eparam_t *param;\n\n\tvideo = canvas->mix;\n\tif (!video || !video->texture_rendered)\n\t\treturn;\n\n\tconst enum gs_color_space source_space = video->render_space;\n\tconst enum gs_color_space current_space = gs_get_color_space();\n\tconst char *tech_name = \"Draw\";\n\tfloat multiplier = 1.f;\n\tswitch (current_space) {\n\tcase GS_CS_SRGB:\n\tcase GS_CS_SRGB_16F:\n\t\tif (source_space == GS_CS_709_EXTENDED)\n\t\t\ttech_name = \"DrawTonemap\";\n\t\tbreak;\n\tcase GS_CS_709_SCRGB:\n\t\ttech_name = \"DrawMultiply\";\n\t\tmultiplier = obs_get_video_sdr_white_level() / 80.f;\n\t\tbreak;\n\tcase GS_CS_709_EXTENDED:\n\t\tbreak;\n\t}\n\n\tconst bool previous = gs_framebuffer_srgb_enabled();\n\tgs_enable_framebuffer_srgb(true);\n\n\ttex = video->render_texture;\n\teffect = obs_get_base_effect(OBS_EFFECT_DEFAULT);\n\tparam = gs_effect_get_param_by_name(effect, \"image\");\n\tgs_effect_set_texture_srgb(param, tex);\n\tparam = gs_effect_get_param_by_name(effect, \"multiplier\");\n\tgs_effect_set_float(param, multiplier);\n\n\tgs_blend_state_push();\n\tgs_blend_function_separate(src_c, dest_c, src_a, dest_a);\n\n\twhile (gs_effect_loop(effect, tech_name))\n\t\tgs_draw_sprite(tex, 0, 0, 0);\n\n\tgs_blend_state_pop();\n\n\tgs_enable_framebuffer_srgb(previous);\n}\n\nvoid obs_render_main_texture(void)\n{\n\tobs_render_canvas_texture_internal(obs->data.main_canvas, GS_BLEND_ONE, GS_BLEND_INVSRCALPHA, GS_BLEND_ONE,\n\t\t\t\t\t   GS_BLEND_INVSRCALPHA);\n}\n\nvoid obs_render_main_texture_src_color_only(void)\n{\n\tobs_render_canvas_texture_internal(obs->data.main_canvas, GS_BLEND_ONE, GS_BLEND_ZERO, GS_BLEND_ONE,\n\t\t\t\t\t   GS_BLEND_INVSRCALPHA);\n}\n\nvoid obs_render_canvas_texture(obs_canvas_t *canvas)\n{\n\tobs_render_canvas_texture_internal(canvas, GS_BLEND_ONE, GS_BLEND_INVSRCALPHA, GS_BLEND_ONE,\n\t\t\t\t\t   GS_BLEND_INVSRCALPHA);\n}\n\nvoid obs_render_canvas_texture_src_color_only(obs_canvas_t *canvas)\n{\n\tobs_render_canvas_texture_internal(canvas, GS_BLEND_ONE, GS_BLEND_ZERO, GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);\n}\n\ngs_texture_t *obs_get_main_texture(void)\n{\n\tstruct obs_core_video_mix *video;\n\n\tvideo = obs->data.main_canvas->mix;\n\tif (!video->texture_rendered)\n\t\treturn NULL;\n\n\treturn video->render_texture;\n}\n\nstatic obs_source_t *obs_load_source_type(obs_data_t *source_data, bool is_private)\n{\n\tobs_data_array_t *filters = obs_data_get_array(source_data, \"filters\");\n\tobs_source_t *source;\n\tconst char *name = obs_data_get_string(source_data, \"name\");\n\tconst char *uuid = obs_data_get_string(source_data, \"uuid\");\n\tconst char *id = obs_data_get_string(source_data, \"id\");\n\tconst char *v_id = obs_data_get_string(source_data, \"versioned_id\");\n\tobs_data_t *settings = obs_data_get_obj(source_data, \"settings\");\n\tobs_data_t *hotkeys = obs_data_get_obj(source_data, \"hotkeys\");\n\tobs_canvas_t *canvas = NULL;\n\tdouble volume;\n\tdouble balance;\n\tint64_t sync;\n\tuint32_t prev_ver;\n\tuint32_t caps;\n\tuint32_t flags;\n\tuint32_t mixers;\n\tint di_order;\n\tint di_mode;\n\tint monitoring_type;\n\n\tprev_ver = (uint32_t)obs_data_get_int(source_data, \"prev_ver\");\n\n\tif (!*v_id)\n\t\tv_id = id;\n\n\tif (obs_source_type_is_scene(id) || obs_source_type_is_group(id)) {\n\t\tconst char *canvas_uuid = obs_data_get_string(source_data, \"canvas_uuid\");\n\t\tcanvas = obs_get_canvas_by_uuid(canvas_uuid);\n\t\t/* Fall back to main canvas if canvas cannot be found. */\n\t\tif (!canvas) {\n\t\t\tcanvas = obs_canvas_get_ref(obs->data.main_canvas);\n\t\t}\n\t}\n\n\tsource = obs_source_create_set_last_ver(canvas, v_id, name, uuid, settings, hotkeys, prev_ver, is_private);\n\n\tif (source->owns_info_id) {\n\t\tbfree((void *)source->info.unversioned_id);\n\t\tsource->info.unversioned_id = bstrdup(id);\n\t}\n\n\tobs_canvas_release(canvas);\n\tobs_data_release(hotkeys);\n\n\tcaps = obs_source_get_output_flags(source);\n\n\tobs_data_set_default_double(source_data, \"volume\", 1.0);\n\tvolume = obs_data_get_double(source_data, \"volume\");\n\tobs_source_set_volume(source, (float)volume);\n\n\tobs_data_set_default_double(source_data, \"balance\", 0.5);\n\tbalance = obs_data_get_double(source_data, \"balance\");\n\tobs_source_set_balance_value(source, (float)balance);\n\n\tsync = obs_data_get_int(source_data, \"sync\");\n\tobs_source_set_sync_offset(source, sync);\n\n\tobs_data_set_default_int(source_data, \"mixers\", 0x3F);\n\tmixers = (uint32_t)obs_data_get_int(source_data, \"mixers\");\n\tobs_source_set_audio_mixers(source, mixers);\n\n\tobs_data_set_default_int(source_data, \"flags\", source->default_flags);\n\tflags = (uint32_t)obs_data_get_int(source_data, \"flags\");\n\tobs_source_set_flags(source, flags);\n\n\tobs_data_set_default_bool(source_data, \"enabled\", true);\n\tobs_source_set_enabled(source, obs_data_get_bool(source_data, \"enabled\"));\n\n\tobs_data_set_default_bool(source_data, \"muted\", false);\n\tobs_source_set_muted(source, obs_data_get_bool(source_data, \"muted\"));\n\n\tobs_data_set_default_bool(source_data, \"push-to-mute\", false);\n\tobs_source_enable_push_to_mute(source, obs_data_get_bool(source_data, \"push-to-mute\"));\n\n\tobs_data_set_default_int(source_data, \"push-to-mute-delay\", 0);\n\tobs_source_set_push_to_mute_delay(source, obs_data_get_int(source_data, \"push-to-mute-delay\"));\n\n\tobs_data_set_default_bool(source_data, \"push-to-talk\", false);\n\tobs_source_enable_push_to_talk(source, obs_data_get_bool(source_data, \"push-to-talk\"));\n\n\tobs_data_set_default_int(source_data, \"push-to-talk-delay\", 0);\n\tobs_source_set_push_to_talk_delay(source, obs_data_get_int(source_data, \"push-to-talk-delay\"));\n\n\tdi_mode = (int)obs_data_get_int(source_data, \"deinterlace_mode\");\n\tobs_source_set_deinterlace_mode(source, (enum obs_deinterlace_mode)di_mode);\n\n\tdi_order = (int)obs_data_get_int(source_data, \"deinterlace_field_order\");\n\tobs_source_set_deinterlace_field_order(source, (enum obs_deinterlace_field_order)di_order);\n\n\tmonitoring_type = (int)obs_data_get_int(source_data, \"monitoring_type\");\n\tif (prev_ver < MAKE_SEMANTIC_VERSION(23, 2, 2)) {\n\t\tif ((caps & OBS_SOURCE_MONITOR_BY_DEFAULT) != 0) {\n\t\t\t/* updates older sources to enable monitoring\n\t\t\t * automatically if they added monitoring by default in\n\t\t\t * version 24 */\n\t\t\tmonitoring_type = OBS_MONITORING_TYPE_MONITOR_ONLY;\n\t\t\tobs_source_set_audio_mixers(source, 0x3F);\n\t\t}\n\t}\n\tobs_source_set_monitoring_type(source, (enum obs_monitoring_type)monitoring_type);\n\n\tobs_data_release(source->private_settings);\n\tsource->private_settings = obs_data_get_obj(source_data, \"private_settings\");\n\tif (!source->private_settings)\n\t\tsource->private_settings = obs_data_create();\n\n\tif (filters) {\n\t\tsize_t count = obs_data_array_count(filters);\n\n\t\tfor (size_t i = 0; i < count; i++) {\n\t\t\tobs_data_t *filter_data = obs_data_array_item(filters, i);\n\n\t\t\tobs_source_t *filter = obs_load_source_type(filter_data, true);\n\t\t\tif (filter) {\n\t\t\t\tobs_source_filter_add(source, filter);\n\t\t\t\tobs_source_release(filter);\n\t\t\t}\n\n\t\t\tobs_data_release(filter_data);\n\t\t}\n\n\t\tobs_data_array_release(filters);\n\t}\n\n\tobs_data_release(settings);\n\n\treturn source;\n}\n\nobs_source_t *obs_load_source(obs_data_t *source_data)\n{\n\treturn obs_load_source_type(source_data, false);\n}\n\nobs_source_t *obs_load_private_source(obs_data_t *source_data)\n{\n\treturn obs_load_source_type(source_data, true);\n}\n\nvoid obs_load_sources(obs_data_array_t *array, obs_load_source_cb cb, void *private_data)\n{\n\tDARRAY(obs_source_t *) sources;\n\tsize_t count;\n\tsize_t i;\n\n\tda_init(sources);\n\n\tcount = obs_data_array_count(array);\n\tda_reserve(sources, count);\n\n\tfor (i = 0; i < count; i++) {\n\t\tobs_data_t *source_data = obs_data_array_item(array, i);\n\t\tobs_source_t *source = obs_load_source(source_data);\n\n\t\tda_push_back(sources, &source);\n\n\t\tobs_data_release(source_data);\n\t}\n\n\t/* tell sources that we want to load */\n\tfor (i = 0; i < sources.num; i++) {\n\t\tobs_source_t *source = sources.array[i];\n\t\tobs_data_t *source_data = obs_data_array_item(array, i);\n\t\tif (source) {\n\t\t\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION)\n\t\t\t\tobs_transition_load(source, source_data);\n\t\t\tobs_source_load2(source);\n\t\t\tif (cb)\n\t\t\t\tcb(private_data, source);\n\t\t}\n\t\tobs_data_release(source_data);\n\t}\n\n\tfor (i = 0; i < sources.num; i++)\n\t\tobs_source_release(sources.array[i]);\n\n\tda_free(sources);\n}\n\nobs_data_t *obs_save_source(obs_source_t *source)\n{\n\tobs_data_array_t *filters = obs_data_array_create();\n\tobs_data_t *source_data = obs_data_create();\n\tobs_data_t *settings = obs_source_get_settings(source);\n\tobs_data_t *hotkey_data = source->context.hotkey_data;\n\tobs_data_t *hotkeys;\n\tfloat volume = obs_source_get_volume(source);\n\tfloat balance = obs_source_get_balance_value(source);\n\tuint32_t mixers = obs_source_get_audio_mixers(source);\n\tint64_t sync = obs_source_get_sync_offset(source);\n\tuint32_t flags = obs_source_get_flags(source);\n\tconst char *name = obs_source_get_name(source);\n\tconst char *uuid = obs_source_get_uuid(source);\n\tconst char *id = source->info.unversioned_id;\n\tconst char *v_id = source->info.id;\n\tbool enabled = obs_source_enabled(source);\n\tbool muted = obs_source_muted(source);\n\tbool push_to_mute = obs_source_push_to_mute_enabled(source);\n\tuint64_t ptm_delay = obs_source_get_push_to_mute_delay(source);\n\tbool push_to_talk = obs_source_push_to_talk_enabled(source);\n\tuint64_t ptt_delay = obs_source_get_push_to_talk_delay(source);\n\tint m_type = (int)obs_source_get_monitoring_type(source);\n\tint di_mode = (int)obs_source_get_deinterlace_mode(source);\n\tint di_order = (int)obs_source_get_deinterlace_field_order(source);\n\tobs_canvas_t *canvas = obs_source_get_canvas(source);\n\tDARRAY(obs_source_t *) filters_copy;\n\n\tobs_source_save(source);\n\thotkeys = obs_hotkeys_save_source(source);\n\n\tif (hotkeys) {\n\t\tobs_data_release(hotkey_data);\n\t\tsource->context.hotkey_data = hotkeys;\n\t\thotkey_data = hotkeys;\n\t}\n\n\tobs_data_set_int(source_data, \"prev_ver\", LIBOBS_API_VER);\n\n\tobs_data_set_string(source_data, \"name\", name);\n\tobs_data_set_string(source_data, \"uuid\", uuid);\n\tobs_data_set_string(source_data, \"id\", id);\n\tobs_data_set_string(source_data, \"versioned_id\", v_id);\n\tobs_data_set_obj(source_data, \"settings\", settings);\n\tobs_data_set_int(source_data, \"mixers\", mixers);\n\tobs_data_set_int(source_data, \"sync\", sync);\n\tobs_data_set_int(source_data, \"flags\", flags);\n\tobs_data_set_double(source_data, \"volume\", volume);\n\tobs_data_set_double(source_data, \"balance\", balance);\n\tobs_data_set_bool(source_data, \"enabled\", enabled);\n\tobs_data_set_bool(source_data, \"muted\", muted);\n\tobs_data_set_bool(source_data, \"push-to-mute\", push_to_mute);\n\tobs_data_set_int(source_data, \"push-to-mute-delay\", ptm_delay);\n\tobs_data_set_bool(source_data, \"push-to-talk\", push_to_talk);\n\tobs_data_set_int(source_data, \"push-to-talk-delay\", ptt_delay);\n\tobs_data_set_obj(source_data, \"hotkeys\", hotkey_data);\n\tobs_data_set_int(source_data, \"deinterlace_mode\", di_mode);\n\tobs_data_set_int(source_data, \"deinterlace_field_order\", di_order);\n\tobs_data_set_int(source_data, \"monitoring_type\", m_type);\n\n\tif (canvas) {\n\t\tobs_data_set_string(source_data, \"canvas_uuid\", obs_canvas_get_uuid(canvas));\n\t\tobs_canvas_release(canvas);\n\t}\n\n\tobs_data_set_obj(source_data, \"private_settings\", source->private_settings);\n\n\tif (source->info.type == OBS_SOURCE_TYPE_TRANSITION)\n\t\tobs_transition_save(source, source_data);\n\n\tpthread_mutex_lock(&source->filter_mutex);\n\tda_init(filters_copy);\n\tda_reserve(filters_copy, source->filters.num);\n\n\tfor (size_t i = 0; i < source->filters.num; i++) {\n\t\tobs_source_t *filter = obs_source_get_ref(source->filters.array[i]);\n\t\tif (filter)\n\t\t\tda_push_back(filters_copy, &filter);\n\t}\n\n\tpthread_mutex_unlock(&source->filter_mutex);\n\n\tif (filters_copy.num) {\n\t\tfor (size_t i = filters_copy.num; i > 0; i--) {\n\t\t\tobs_source_t *filter = filters_copy.array[i - 1];\n\t\t\tobs_data_t *filter_data = obs_save_source(filter);\n\t\t\tobs_data_array_push_back(filters, filter_data);\n\t\t\tobs_data_release(filter_data);\n\t\t\tobs_source_release(filter);\n\t\t}\n\n\t\tobs_data_set_array(source_data, \"filters\", filters);\n\t}\n\n\tda_free(filters_copy);\n\n\tobs_data_release(settings);\n\tobs_data_array_release(filters);\n\n\treturn source_data;\n}\n\nobs_data_array_t *obs_save_sources_filtered(obs_save_source_filter_cb cb, void *data_)\n{\n\tstruct obs_core_data *data = &obs->data;\n\tobs_data_array_t *array;\n\tobs_source_t *source;\n\n\tarray = obs_data_array_create();\n\n\tpthread_mutex_lock(&data->sources_mutex);\n\n\tsource = data->sources;\n\n\twhile (source) {\n\t\tif ((source->info.type != OBS_SOURCE_TYPE_FILTER) != 0 && !source->removed && !source->temp_removed &&\n\t\t    !source->context.private && cb(data_, source)) {\n\t\t\tobs_data_t *source_data = obs_save_source(source);\n\n\t\t\tobs_data_array_push_back(array, source_data);\n\t\t\tobs_data_release(source_data);\n\t\t}\n\n\t\tsource = (obs_source_t *)source->context.hh_uuid.next;\n\t}\n\n\tpthread_mutex_unlock(&data->sources_mutex);\n\n\treturn array;\n}\n\nstatic bool save_source_filter(void *data, obs_source_t *source)\n{\n\tUNUSED_PARAMETER(data);\n\tUNUSED_PARAMETER(source);\n\treturn true;\n}\n\nobs_data_array_t *obs_save_sources(void)\n{\n\treturn obs_save_sources_filtered(save_source_filter, NULL);\n}\n\nvoid obs_reset_source_uuids()\n{\n\tpthread_mutex_lock(&obs->data.sources_mutex);\n\n\t/* Move all sources to a new hash table */\n\tstruct obs_context_data *ht = (struct obs_context_data *)obs->data.sources;\n\tstruct obs_context_data *new_ht = NULL;\n\n\tstruct obs_context_data *ctx, *tmp;\n\tHASH_ITER (hh_uuid, ht, ctx, tmp) {\n\t\tHASH_DELETE(hh_uuid, ht, ctx);\n\n\t\tbfree((void *)ctx->uuid);\n\t\tctx->uuid = os_generate_uuid();\n\n\t\tHASH_ADD_UUID(new_ht, uuid, ctx);\n\t}\n\n\t/* The old table will be automatically freed once the last element has\n\t * been removed, so we can simply overwrite the pointer. */\n\tobs->data.sources = (struct obs_source *)new_ht;\n\n\tpthread_mutex_unlock(&obs->data.sources_mutex);\n}\n\n/* ensures that names are never blank */\nstatic inline char *dup_name(const char *name, bool private)\n{\n\tif (private && !name)\n\t\treturn NULL;\n\n\tif (!name || !*name) {\n\t\tstruct dstr unnamed = {0};\n\t\tdstr_printf(&unnamed, \"__unnamed%04lld\", obs->data.unnamed_index++);\n\n\t\treturn unnamed.array;\n\t} else {\n\t\treturn bstrdup(name);\n\t}\n}\n\nstatic inline bool obs_context_data_init_wrap(struct obs_context_data *context, enum obs_obj_type type,\n\t\t\t\t\t      obs_data_t *settings, const char *name, const char *uuid,\n\t\t\t\t\t      obs_data_t *hotkey_data, bool private)\n{\n\tassert(context);\n\tmemset(context, 0, sizeof(*context));\n\tcontext->private = private;\n\tcontext->type = type;\n\n\tpthread_mutex_init_value(&context->rename_cache_mutex);\n\tif (pthread_mutex_init(&context->rename_cache_mutex, NULL) < 0)\n\t\treturn false;\n\n\tcontext->signals = signal_handler_create();\n\tif (!context->signals)\n\t\treturn false;\n\n\tcontext->procs = proc_handler_create();\n\tif (!context->procs)\n\t\treturn false;\n\n\tif (uuid && strlen(uuid) == UUID_STR_LENGTH)\n\t\tcontext->uuid = bstrdup(uuid);\n\t/* Only automatically generate UUIDs for sources */\n\telse if (type == OBS_OBJ_TYPE_SOURCE || type == OBS_OBJ_TYPE_CANVAS)\n\t\tcontext->uuid = os_generate_uuid();\n\n\tcontext->name = dup_name(name, private);\n\tcontext->settings = obs_data_newref(settings);\n\tcontext->hotkey_data = obs_data_newref(hotkey_data);\n\treturn true;\n}\n\nbool obs_context_data_init(struct obs_context_data *context, enum obs_obj_type type, obs_data_t *settings,\n\t\t\t   const char *name, const char *uuid, obs_data_t *hotkey_data, bool private)\n{\n\tif (obs_context_data_init_wrap(context, type, settings, name, uuid, hotkey_data, private)) {\n\t\treturn true;\n\t} else {\n\t\tobs_context_data_free(context);\n\t\treturn false;\n\t}\n}\n\nvoid obs_context_data_free(struct obs_context_data *context)\n{\n\tobs_hotkeys_context_release(context);\n\tsignal_handler_destroy(context->signals);\n\tproc_handler_destroy(context->procs);\n\tobs_data_release(context->settings);\n\tobs_context_data_remove(context);\n\tpthread_mutex_destroy(&context->rename_cache_mutex);\n\tbfree(context->name);\n\tbfree((void *)context->uuid);\n\n\tfor (size_t i = 0; i < context->rename_cache.num; i++)\n\t\tbfree(context->rename_cache.array[i]);\n\tda_free(context->rename_cache);\n\n\tmemset(context, 0, sizeof(*context));\n}\n\nvoid obs_context_init_control(struct obs_context_data *context, void *object, obs_destroy_cb destroy)\n{\n\tcontext->control = bzalloc(sizeof(obs_weak_object_t));\n\tcontext->control->object = object;\n\tcontext->destroy = destroy;\n}\n\nvoid obs_context_data_insert(struct obs_context_data *context, pthread_mutex_t *mutex, void *pfirst)\n{\n\tstruct obs_context_data **first = pfirst;\n\n\tassert(context);\n\tassert(mutex);\n\tassert(first);\n\n\tcontext->mutex = mutex;\n\n\tpthread_mutex_lock(mutex);\n\tcontext->prev_next = first;\n\tcontext->next = *first;\n\t*first = context;\n\tif (context->next)\n\t\tcontext->next->prev_next = &context->next;\n\tpthread_mutex_unlock(mutex);\n}\n\nstatic inline char *obs_context_deduplicate_name(void *phash, const char *name)\n{\n\tstruct obs_context_data *head = phash;\n\tstruct obs_context_data *item = NULL;\n\n\tHASH_FIND_STR(head, name, item);\n\tif (!item)\n\t\treturn NULL;\n\n\tstruct dstr new_name = {0};\n\tint suffix = 2;\n\n\twhile (item) {\n\t\tdstr_printf(&new_name, \"%s %d\", name, suffix++);\n\t\tHASH_FIND_STR(head, new_name.array, item);\n\t}\n\n\treturn new_name.array;\n}\n\nvoid obs_context_data_insert_name(struct obs_context_data *context, pthread_mutex_t *mutex, void *pfirst)\n{\n\tstruct obs_context_data **first = pfirst;\n\tchar *new_name;\n\n\tassert(context);\n\tassert(mutex);\n\tassert(first);\n\n\tcontext->mutex = mutex;\n\n\tpthread_mutex_lock(mutex);\n\n\t/* Ensure name is not a duplicate. */\n\tnew_name = obs_context_deduplicate_name(*first, context->name);\n\tif (new_name) {\n\t\tblog(LOG_WARNING,\n\t\t     \"Attempted to insert context with duplicate name \\\"%s\\\"!\"\n\t\t     \" Name has been changed to \\\"%s\\\"\",\n\t\t     context->name, new_name);\n\t\t/* Since this happens before the context creation finishes,\n\t\t * do not bother to add it to the rename cache. */\n\t\tbfree(context->name);\n\t\tcontext->name = new_name;\n\t}\n\n\tHASH_ADD_STR(*first, name, context);\n\n\tpthread_mutex_unlock(mutex);\n}\n\nvoid obs_context_data_insert_uuid(struct obs_context_data *context, pthread_mutex_t *mutex, void *pfirst_uuid)\n{\n\tstruct obs_context_data **first_uuid = pfirst_uuid;\n\tstruct obs_context_data *item = NULL;\n\n\tassert(context);\n\tassert(mutex);\n\tassert(first_uuid);\n\n\tcontext->mutex = mutex;\n\n\tpthread_mutex_lock(mutex);\n\n\t/* Ensure UUID is not a duplicate.\n\t * This should only ever happen if a scene collection file has been\n\t * manually edited and an entry has been duplicated without removing\n\t * or regenerating the UUID. */\n\tHASH_FIND_UUID(*first_uuid, context->uuid, item);\n\tif (item) {\n\t\tblog(LOG_WARNING, \"Attempted to insert context with duplicate UUID \\\"%s\\\"!\", context->uuid);\n\t\t/* It is practically impossible for the new UUID to be a\n\t\t * duplicate, so don't bother checking again. */\n\t\tbfree((void *)context->uuid);\n\t\tcontext->uuid = os_generate_uuid();\n\t}\n\n\tHASH_ADD_UUID(*first_uuid, uuid, context);\n\tpthread_mutex_unlock(mutex);\n}\n\nvoid obs_context_data_remove(struct obs_context_data *context)\n{\n\tif (context && context->prev_next) {\n\t\tpthread_mutex_lock(context->mutex);\n\t\t*context->prev_next = context->next;\n\t\tif (context->next)\n\t\t\tcontext->next->prev_next = context->prev_next;\n\t\tcontext->prev_next = NULL;\n\t\tpthread_mutex_unlock(context->mutex);\n\t}\n}\n\nvoid obs_context_data_remove_name(struct obs_context_data *context, pthread_mutex_t *mutex, void *phead)\n{\n\tstruct obs_context_data **head = phead;\n\n\tassert(head);\n\n\tif (!context)\n\t\treturn;\n\n\tpthread_mutex_lock(mutex);\n\tHASH_DELETE(hh, *head, context);\n\tpthread_mutex_unlock(mutex);\n}\n\nvoid obs_context_data_remove_uuid(struct obs_context_data *context, pthread_mutex_t *mutex, void *puuid_head)\n{\n\tstruct obs_context_data **uuid_head = puuid_head;\n\n\tassert(uuid_head);\n\n\tif (!context || !context->uuid || !uuid_head)\n\t\treturn;\n\n\tpthread_mutex_lock(mutex);\n\tHASH_DELETE(hh_uuid, *uuid_head, context);\n\tpthread_mutex_unlock(mutex);\n}\n\nvoid obs_context_wait(struct obs_context_data *context)\n{\n\tpthread_mutex_lock(context->mutex);\n\tpthread_mutex_unlock(context->mutex);\n}\n\nvoid obs_context_data_setname(struct obs_context_data *context, const char *name)\n{\n\tpthread_mutex_lock(&context->rename_cache_mutex);\n\n\tif (context->name)\n\t\tda_push_back(context->rename_cache, &context->name);\n\tcontext->name = dup_name(name, context->private);\n\n\tpthread_mutex_unlock(&context->rename_cache_mutex);\n}\n\nvoid obs_context_data_setname_ht(struct obs_context_data *context, const char *name, void *phead)\n{\n\tstruct obs_context_data **head = phead;\n\tchar *new_name;\n\n\tpthread_mutex_lock(context->mutex);\n\tpthread_mutex_lock(&context->rename_cache_mutex);\n\n\tHASH_DEL(*head, context);\n\tif (context->name)\n\t\tda_push_back(context->rename_cache, &context->name);\n\n\t/* Ensure new name is not a duplicate. */\n\tnew_name = obs_context_deduplicate_name(*head, name);\n\tif (new_name) {\n\t\tblog(LOG_WARNING,\n\t\t     \"Attempted to rename context to duplicate name \\\"%s\\\"!\"\n\t\t     \" New name has been changed to \\\"%s\\\"\",\n\t\t     context->name, new_name);\n\t\tcontext->name = new_name;\n\t} else {\n\t\tcontext->name = dup_name(name, context->private);\n\t}\n\n\tHASH_ADD_STR(*head, name, context);\n\n\tpthread_mutex_unlock(&context->rename_cache_mutex);\n\tpthread_mutex_unlock(context->mutex);\n}\n\nprofiler_name_store_t *obs_get_profiler_name_store(void)\n{\n\treturn obs->name_store;\n}\n\nuint64_t obs_get_video_frame_time(void)\n{\n\treturn obs->video.video_time;\n}\n\ndouble obs_get_active_fps(void)\n{\n\treturn obs->video.video_fps;\n}\n\nuint64_t obs_get_average_frame_time_ns(void)\n{\n\treturn obs->video.video_avg_frame_time_ns;\n}\n\nuint64_t obs_get_frame_interval_ns(void)\n{\n\treturn obs->video.video_frame_interval_ns;\n}\n\nenum obs_obj_type obs_obj_get_type(void *obj)\n{\n\tstruct obs_context_data *context = obj;\n\treturn context ? context->type : OBS_OBJ_TYPE_INVALID;\n}\n\nconst char *obs_obj_get_id(void *obj)\n{\n\tstruct obs_context_data *context = obj;\n\tif (!context)\n\t\treturn NULL;\n\n\tswitch (context->type) {\n\tcase OBS_OBJ_TYPE_SOURCE:\n\t\treturn ((obs_source_t *)obj)->info.id;\n\tcase OBS_OBJ_TYPE_OUTPUT:\n\t\treturn ((obs_output_t *)obj)->info.id;\n\tcase OBS_OBJ_TYPE_ENCODER:\n\t\treturn ((obs_encoder_t *)obj)->info.id;\n\tcase OBS_OBJ_TYPE_SERVICE:\n\t\treturn ((obs_service_t *)obj)->info.id;\n\tdefault:;\n\t}\n\n\treturn NULL;\n}\n\nbool obs_obj_invalid(void *obj)\n{\n\tstruct obs_context_data *context = obj;\n\tif (!context)\n\t\treturn true;\n\n\treturn !context->data;\n}\n\nvoid *obs_obj_get_data(void *obj)\n{\n\tstruct obs_context_data *context = obj;\n\tif (!context)\n\t\treturn NULL;\n\n\treturn context->data;\n}\n\nbool obs_obj_is_private(void *obj)\n{\n\tstruct obs_context_data *context = obj;\n\tif (!context)\n\t\treturn false;\n\n\treturn context->private;\n}\n\nvoid obs_reset_audio_monitoring(void)\n{\n\tif (!obs_audio_monitoring_available())\n\t\treturn;\n\n\tpthread_mutex_lock(&obs->audio.monitoring_mutex);\n\n\tfor (size_t i = 0; i < obs->audio.monitors.num; i++) {\n\t\tstruct audio_monitor *monitor = obs->audio.monitors.array[i];\n\t\taudio_monitor_reset(monitor);\n\t}\n\n\tpthread_mutex_unlock(&obs->audio.monitoring_mutex);\n}\n\nstatic bool check_all_aoc_sources(void *param, obs_source_t *src)\n{\n\tUNUSED_PARAMETER(param);\n\tif (src->info.output_flags & OBS_SOURCE_DO_NOT_SELF_MONITOR) {\n\t\tobs_data_t *settings = obs_source_get_settings(src);\n\t\tconst char *device_id = obs_data_get_string(settings, \"device_id\");\n\t\tobs_source_audio_output_capture_device_changed(src, device_id);\n\t\tobs_data_release(settings);\n\t}\n\treturn true;\n}\n\nbool obs_set_audio_monitoring_device(const char *name, const char *id)\n{\n\tif (!name || !id || !*name || !*id)\n\t\treturn false;\n\n\tif (!obs_audio_monitoring_available())\n\t\treturn false;\n\n\tpthread_mutex_lock(&obs->audio.monitoring_mutex);\n\n\tif (strcmp(id, obs->audio.monitoring_device_id) == 0) {\n\t\tpthread_mutex_unlock(&obs->audio.monitoring_mutex);\n\t\treturn true;\n\t}\n\n\tbfree(obs->audio.monitoring_device_name);\n\tbfree(obs->audio.monitoring_device_id);\n\n\tobs->audio.monitoring_device_name = bstrdup(name);\n\tobs->audio.monitoring_device_id = bstrdup(id);\n\tpthread_mutex_unlock(&obs->audio.monitoring_mutex);\n\n\tobs_reset_audio_monitoring();\n\n\t/* Check all Audio Output Capture sources for monitoring duplication. */\n\tobs_enum_sources(check_all_aoc_sources, NULL);\n\n\treturn true;\n}\n\nvoid obs_get_audio_monitoring_device(const char **name, const char **id)\n{\n\tif (name)\n\t\t*name = obs->audio.monitoring_device_name;\n\tif (id)\n\t\t*id = obs->audio.monitoring_device_id;\n}\n\nvoid obs_add_tick_callback(void (*tick)(void *param, float seconds), void *param)\n{\n\tstruct tick_callback data = {tick, param};\n\n\tpthread_mutex_lock(&obs->data.draw_callbacks_mutex);\n\tda_insert(obs->data.tick_callbacks, 0, &data);\n\tpthread_mutex_unlock(&obs->data.draw_callbacks_mutex);\n}\n\nvoid obs_remove_tick_callback(void (*tick)(void *param, float seconds), void *param)\n{\n\tstruct tick_callback data = {tick, param};\n\n\tpthread_mutex_lock(&obs->data.draw_callbacks_mutex);\n\tda_erase_item(obs->data.tick_callbacks, &data);\n\tpthread_mutex_unlock(&obs->data.draw_callbacks_mutex);\n}\n\nvoid obs_add_main_render_callback(void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param)\n{\n\tstruct draw_callback data = {draw, param};\n\n\tpthread_mutex_lock(&obs->data.draw_callbacks_mutex);\n\tda_insert(obs->data.draw_callbacks, 0, &data);\n\tpthread_mutex_unlock(&obs->data.draw_callbacks_mutex);\n}\n\nvoid obs_remove_main_render_callback(void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param)\n{\n\tstruct draw_callback data = {draw, param};\n\n\tpthread_mutex_lock(&obs->data.draw_callbacks_mutex);\n\tda_erase_item(obs->data.draw_callbacks, &data);\n\tpthread_mutex_unlock(&obs->data.draw_callbacks_mutex);\n}\n\nvoid obs_add_main_rendered_callback(void (*rendered)(void *param), void *param)\n{\n\tstruct rendered_callback data = {rendered, param};\n\n\tpthread_mutex_lock(&obs->data.draw_callbacks_mutex);\n\tda_insert(obs->data.rendered_callbacks, 0, &data);\n\tpthread_mutex_unlock(&obs->data.draw_callbacks_mutex);\n}\n\nvoid obs_remove_main_rendered_callback(void (*rendered)(void *param), void *param)\n{\n\tstruct rendered_callback data = {rendered, param};\n\n\tpthread_mutex_lock(&obs->data.draw_callbacks_mutex);\n\tda_erase_item(obs->data.rendered_callbacks, &data);\n\tpthread_mutex_unlock(&obs->data.draw_callbacks_mutex);\n}\n\nuint32_t obs_get_total_frames(void)\n{\n\treturn obs->video.total_frames;\n}\n\nuint32_t obs_get_lagged_frames(void)\n{\n\treturn obs->video.lagged_frames;\n}\n\nstruct obs_core_video_mix *get_mix_for_video(video_t *v)\n{\n\tstruct obs_core_video_mix *result = NULL;\n\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0, num = obs->video.mixes.num; i < num; i++) {\n\t\tstruct obs_core_video_mix *mix = obs->video.mixes.array[i];\n\n\t\tif (v == mix->video) {\n\t\t\tresult = mix;\n\t\t\tbreak;\n\t\t}\n\t}\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\n\treturn result;\n}\n\nvoid start_raw_video(video_t *v, const struct video_scale_info *conversion, uint32_t frame_rate_divisor,\n\t\t     void (*callback)(void *param, struct video_data *frame), void *param)\n{\n\tstruct obs_core_video_mix *video = get_mix_for_video(v);\n\n\t// TODO: Make affected outputs use views/canvasses, and revert this later.\n\t// https://github.com/obsproject/obs-studio/pull/12379\n\t// https://github.com/obsproject/obs-studio/issues/12366\n\tif (video_output_connect2(v, conversion, frame_rate_divisor, callback, param) && video)\n\t\tos_atomic_inc_long(&video->raw_active);\n}\n\nvoid stop_raw_video(video_t *v, void (*callback)(void *param, struct video_data *frame), void *param)\n{\n\tstruct obs_core_video_mix *video = get_mix_for_video(v);\n\n\t// TODO: Make affected outputs use views/canvasses, and revert this later.\n\t// https://github.com/obsproject/obs-studio/pull/12379\n\t// https://github.com/obsproject/obs-studio/issues/12366\n\tif (video_output_disconnect2(v, callback, param) && video)\n\t\tos_atomic_dec_long(&video->raw_active);\n}\n\nvoid obs_add_raw_video_callback(const struct video_scale_info *conversion,\n\t\t\t\tvoid (*callback)(void *param, struct video_data *frame), void *param)\n{\n\tobs_add_raw_video_callback2(conversion, 1, callback, param);\n}\n\nvoid obs_add_raw_video_callback2(const struct video_scale_info *conversion, uint32_t frame_rate_divisor,\n\t\t\t\t void (*callback)(void *param, struct video_data *frame), void *param)\n{\n\tstruct obs_core_video_mix *video = obs->data.main_canvas->mix;\n\tstart_raw_video(video->video, conversion, frame_rate_divisor, callback, param);\n}\n\nvoid obs_remove_raw_video_callback(void (*callback)(void *param, struct video_data *frame), void *param)\n{\n\tstruct obs_core_video_mix *video = obs->data.main_canvas->mix;\n\tstop_raw_video(video->video, callback, param);\n}\n\nvoid obs_add_raw_audio_callback(size_t mix_idx, const struct audio_convert_info *conversion,\n\t\t\t\taudio_output_callback_t callback, void *param)\n{\n\tstruct obs_core_audio *audio = &obs->audio;\n\taudio_output_connect(audio->audio, mix_idx, conversion, callback, param);\n}\n\nvoid obs_remove_raw_audio_callback(size_t mix_idx, audio_output_callback_t callback, void *param)\n{\n\tstruct obs_core_audio *audio = &obs->audio;\n\taudio_output_disconnect(audio->audio, mix_idx, callback, param);\n}\n\nvoid obs_apply_private_data(obs_data_t *settings)\n{\n\tif (!settings)\n\t\treturn;\n\n\tobs_data_apply(obs->data.private_data, settings);\n}\n\nvoid obs_set_private_data(obs_data_t *settings)\n{\n\tobs_data_clear(obs->data.private_data);\n\tif (settings)\n\t\tobs_data_apply(obs->data.private_data, settings);\n}\n\nobs_data_t *obs_get_private_data(void)\n{\n\tobs_data_t *private_data = obs->data.private_data;\n\tobs_data_addref(private_data);\n\treturn private_data;\n}\n\nextern bool init_gpu_encoding(struct obs_core_video_mix *video);\nextern void stop_gpu_encoding_thread(struct obs_core_video_mix *video);\nextern void free_gpu_encoding(struct obs_core_video_mix *video);\n\nbool start_gpu_encode(obs_encoder_t *encoder)\n{\n\tstruct obs_core_video_mix *video = get_mix_for_video(encoder->media);\n\tbool success = true;\n\n\tobs_enter_graphics();\n\tpthread_mutex_lock(&video->gpu_encoder_mutex);\n\n\tif (!video->gpu_encoders.num)\n\t\tsuccess = init_gpu_encoding(video);\n\tif (success)\n\t\tda_push_back(video->gpu_encoders, &encoder);\n\telse\n\t\tfree_gpu_encoding(video);\n\n\tpthread_mutex_unlock(&video->gpu_encoder_mutex);\n\tobs_leave_graphics();\n\n\tif (success) {\n\t\tos_atomic_inc_long(&video->gpu_encoder_active);\n\t\tvideo_output_inc_texture_encoders(video->video);\n\t}\n\n\treturn success;\n}\n\nvoid stop_gpu_encode(obs_encoder_t *encoder)\n{\n\tstruct obs_core_video_mix *video = get_mix_for_video(encoder->media);\n\tbool call_free = false;\n\n\tos_atomic_dec_long(&video->gpu_encoder_active);\n\tvideo_output_dec_texture_encoders(video->video);\n\n\tpthread_mutex_lock(&video->gpu_encoder_mutex);\n\tda_erase_item(video->gpu_encoders, &encoder);\n\tif (!video->gpu_encoders.num)\n\t\tcall_free = true;\n\tpthread_mutex_unlock(&video->gpu_encoder_mutex);\n\n\tos_event_wait(video->gpu_encode_inactive);\n\n\tif (call_free) {\n\t\tstop_gpu_encoding_thread(video);\n\n\t\tobs_enter_graphics();\n\t\tpthread_mutex_lock(&video->gpu_encoder_mutex);\n\t\tfree_gpu_encoding(video);\n\t\tpthread_mutex_unlock(&video->gpu_encoder_mutex);\n\t\tobs_leave_graphics();\n\t}\n}\n\nbool obs_video_active(void)\n{\n\tbool result = false;\n\n\tpthread_mutex_lock(&obs->video.mixes_mutex);\n\tfor (size_t i = 0, num = obs->video.mixes.num; i < num; i++) {\n\t\tstruct obs_core_video_mix *video = obs->video.mixes.array[i];\n\n\t\tif (os_atomic_load_long(&video->raw_active) > 0 ||\n\t\t    os_atomic_load_long(&video->gpu_encoder_active) > 0) {\n\t\t\tresult = true;\n\t\t\tbreak;\n\t\t}\n\t}\n\tpthread_mutex_unlock(&obs->video.mixes_mutex);\n\n\treturn result;\n}\n\nbool obs_nv12_tex_active(void)\n{\n\tstruct obs_core_video_mix *video = obs->data.main_canvas->mix;\n\treturn video->using_nv12_tex;\n}\n\nbool obs_p010_tex_active(void)\n{\n\tstruct obs_core_video_mix *video = obs->data.main_canvas->mix;\n\treturn video->using_p010_tex;\n}\n\n/* ------------------------------------------------------------------------- */\n/* task stuff                                                                */\n\nstruct task_wait_info {\n\tobs_task_t task;\n\tvoid *param;\n\tos_event_t *event;\n};\n\nstatic void task_wait_callback(void *param)\n{\n\tstruct task_wait_info *info = param;\n\tif (info->task)\n\t\tinfo->task(info->param);\n\tos_event_signal(info->event);\n}\n\nTHREAD_LOCAL bool is_graphics_thread = false;\nTHREAD_LOCAL bool is_audio_thread = false;\n\nstatic void set_audio_thread(void *unused)\n{\n\tis_audio_thread = true;\n\tUNUSED_PARAMETER(unused);\n}\n\nbool obs_in_task_thread(enum obs_task_type type)\n{\n\tif (type == OBS_TASK_GRAPHICS)\n\t\treturn is_graphics_thread;\n\telse if (type == OBS_TASK_AUDIO)\n\t\treturn is_audio_thread;\n\telse if (type == OBS_TASK_UI)\n\t\treturn is_ui_thread;\n\telse if (type == OBS_TASK_DESTROY)\n\t\treturn os_task_queue_inside(obs->destruction_task_thread);\n\n\tassert(false);\n\treturn false;\n}\n\nvoid obs_queue_task(enum obs_task_type type, obs_task_t task, void *param, bool wait)\n{\n\tif (type == OBS_TASK_UI) {\n\t\tif (obs->ui_task_handler) {\n\t\t\tobs->ui_task_handler(task, param, wait);\n\t\t} else {\n\t\t\tblog(LOG_ERROR, \"UI task could not be queued, \"\n\t\t\t\t\t\"there's no UI task handler!\");\n\t\t}\n\t} else {\n\t\tif (obs_in_task_thread(type)) {\n\t\t\ttask(param);\n\n\t\t} else if (wait) {\n\t\t\tstruct task_wait_info info = {\n\t\t\t\t.task = task,\n\t\t\t\t.param = param,\n\t\t\t};\n\n\t\t\tos_event_init(&info.event, OS_EVENT_TYPE_MANUAL);\n\t\t\tobs_queue_task(type, task_wait_callback, &info, false);\n\t\t\tos_event_wait(info.event);\n\t\t\tos_event_destroy(info.event);\n\n\t\t} else if (type == OBS_TASK_GRAPHICS) {\n\t\t\tstruct obs_core_video *video = &obs->video;\n\t\t\tstruct obs_task_info info = {task, param};\n\n\t\t\tpthread_mutex_lock(&video->task_mutex);\n\t\t\tdeque_push_back(&video->tasks, &info, sizeof(info));\n\t\t\tpthread_mutex_unlock(&video->task_mutex);\n\n\t\t} else if (type == OBS_TASK_AUDIO) {\n\t\t\tstruct obs_core_audio *audio = &obs->audio;\n\t\t\tstruct obs_task_info info = {task, param};\n\n\t\t\tpthread_mutex_lock(&audio->task_mutex);\n\t\t\tdeque_push_back(&audio->tasks, &info, sizeof(info));\n\t\t\tpthread_mutex_unlock(&audio->task_mutex);\n\n\t\t} else if (type == OBS_TASK_DESTROY) {\n\t\t\tos_task_t os_task = (os_task_t)task;\n\t\t\tos_task_queue_queue_task(obs->destruction_task_thread, os_task, param);\n\t\t}\n\t}\n}\n\nbool obs_wait_for_destroy_queue(void)\n{\n\tstruct task_wait_info info = {0};\n\n\tif (!obs->video.thread_initialized || !obs->audio.audio)\n\t\treturn false;\n\n\t/* allow video and audio threads time to release objects */\n\tos_event_init(&info.event, OS_EVENT_TYPE_AUTO);\n\tobs_queue_task(OBS_TASK_GRAPHICS, task_wait_callback, &info, false);\n\tos_event_wait(info.event);\n\tobs_queue_task(OBS_TASK_AUDIO, task_wait_callback, &info, false);\n\tos_event_wait(info.event);\n\tos_event_destroy(info.event);\n\n\t/* wait for destroy task queue */\n\treturn os_task_queue_wait(obs->destruction_task_thread);\n}\n\nstatic void set_ui_thread(void *unused)\n{\n\tis_ui_thread = true;\n\tUNUSED_PARAMETER(unused);\n}\n\nvoid obs_set_ui_task_handler(obs_task_handler_t handler)\n{\n\tobs->ui_task_handler = handler;\n\tobs_queue_task(OBS_TASK_UI, set_ui_thread, NULL, false);\n}\n\nobs_object_t *obs_object_get_ref(obs_object_t *object)\n{\n\tif (!object)\n\t\treturn NULL;\n\n\treturn obs_weak_object_get_object(object->control);\n}\n\nvoid obs_object_release(obs_object_t *object)\n{\n\tif (!obs) {\n\t\tblog(LOG_WARNING, \"Tried to release an object when the OBS \"\n\t\t\t\t  \"core is shut down!\");\n\t\treturn;\n\t}\n\n\tif (!object)\n\t\treturn;\n\n\tobs_weak_object_t *control = object->control;\n\tif (obs_ref_release(&control->ref)) {\n\t\tobject->destroy(object);\n\t\tobs_weak_object_release(control);\n\t}\n}\n\nvoid obs_weak_object_addref(obs_weak_object_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tobs_weak_ref_addref(&weak->ref);\n}\n\nvoid obs_weak_object_release(obs_weak_object_t *weak)\n{\n\tif (!weak)\n\t\treturn;\n\n\tif (obs_weak_ref_release(&weak->ref))\n\t\tbfree(weak);\n}\n\nobs_weak_object_t *obs_object_get_weak_object(obs_object_t *object)\n{\n\tif (!object)\n\t\treturn NULL;\n\n\tobs_weak_object_t *weak = object->control;\n\tobs_weak_object_addref(weak);\n\treturn weak;\n}\n\nobs_object_t *obs_weak_object_get_object(obs_weak_object_t *weak)\n{\n\tif (!weak)\n\t\treturn NULL;\n\n\tif (obs_weak_ref_get_ref(&weak->ref))\n\t\treturn weak->object;\n\n\treturn NULL;\n}\n\nbool obs_weak_object_expired(obs_weak_object_t *weak)\n{\n\treturn weak ? obs_weak_ref_expired(&weak->ref) : true;\n}\n\nbool obs_weak_object_references_object(obs_weak_object_t *weak, obs_object_t *object)\n{\n\treturn weak && object && weak->object == object;\n}\n\nbool obs_is_output_protocol_registered(const char *protocol)\n{\n\tfor (size_t i = 0; i < obs->data.protocols.num; i++) {\n\t\tif (strcmp(protocol, obs->data.protocols.array[i]) == 0)\n\t\t\treturn true;\n\t}\n\n\treturn false;\n}\n\nbool obs_enum_output_protocols(size_t idx, char **protocol)\n{\n\tif (idx >= obs->data.protocols.num)\n\t\treturn false;\n\n\t*protocol = obs->data.protocols.array[idx];\n\treturn true;\n}\n\nobs_canvas_t *obs_get_main_canvas(void)\n{\n\treturn obs_canvas_get_ref(obs->data.main_canvas);\n}\n"
  },
  {
    "path": "libobs/obs.h",
    "content": "/******************************************************************************\n    Copyright (C) 2023 by Lain Bailey <lain@obsproject.com>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n******************************************************************************/\n\n#pragma once\n\n#include \"util/c99defs.h\"\n#include \"util/bmem.h\"\n#include \"util/profiler.h\"\n#include \"util/text-lookup.h\"\n#include \"graphics/graphics.h\"\n#include \"graphics/vec2.h\"\n#include \"graphics/vec3.h\"\n#include \"media-io/audio-io.h\"\n#include \"media-io/video-io.h\"\n#include \"callback/signal.h\"\n#include \"callback/proc.h\"\n\n#include \"obs-config.h\"\n#include \"obs-defs.h\"\n#include \"obs-data.h\"\n#include \"obs-properties.h\"\n#include \"obs-interaction.h\"\n\nstruct matrix4;\n\n/* opaque types */\nstruct obs_context_data;\nstruct obs_display;\nstruct obs_view;\nstruct obs_source;\nstruct obs_scene;\nstruct obs_scene_item;\nstruct obs_output;\nstruct obs_encoder;\nstruct obs_encoder_group;\nstruct obs_service;\nstruct obs_module;\nstruct obs_module_metadata;\nstruct obs_fader;\nstruct obs_volmeter;\nstruct obs_canvas;\n\ntypedef struct obs_context_data obs_object_t;\ntypedef struct obs_display obs_display_t;\ntypedef struct obs_view obs_view_t;\ntypedef struct obs_source obs_source_t;\ntypedef struct obs_scene obs_scene_t;\ntypedef struct obs_scene_item obs_sceneitem_t;\ntypedef struct obs_output obs_output_t;\ntypedef struct obs_encoder obs_encoder_t;\ntypedef struct obs_encoder_group obs_encoder_group_t;\ntypedef struct obs_service obs_service_t;\ntypedef struct obs_module obs_module_t;\ntypedef struct obs_module_metadata obs_module_metadata_t;\ntypedef struct obs_fader obs_fader_t;\ntypedef struct obs_volmeter obs_volmeter_t;\ntypedef struct obs_canvas obs_canvas_t;\n\ntypedef struct obs_weak_object obs_weak_object_t;\ntypedef struct obs_weak_source obs_weak_source_t;\ntypedef struct obs_weak_output obs_weak_output_t;\ntypedef struct obs_weak_encoder obs_weak_encoder_t;\ntypedef struct obs_weak_service obs_weak_service_t;\ntypedef struct obs_weak_canvas obs_weak_canvas_t;\n\n#include \"obs-missing-files.h\"\n#include \"obs-source.h\"\n#include \"obs-encoder.h\"\n#include \"obs-output.h\"\n#include \"obs-service.h\"\n#include \"obs-audio-controls.h\"\n#include \"obs-hotkey.h\"\n\n/**\n * @file\n * @brief Main libobs header used by applications.\n *\n * @mainpage\n *\n * @section intro_sec Introduction\n *\n * This document describes the api for libobs to be used by applications as well\n * as @ref modules_page implementing some kind of functionality.\n *\n */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** Used for changing the order of items (for example, filters in a source,\n * or items in a scene) */\nenum obs_order_movement {\n\tOBS_ORDER_MOVE_UP,\n\tOBS_ORDER_MOVE_DOWN,\n\tOBS_ORDER_MOVE_TOP,\n\tOBS_ORDER_MOVE_BOTTOM,\n};\n\n/**\n * Used with obs_source_process_filter to specify whether the filter should\n * render the source directly with the specified effect, or whether it should\n * render it to a texture\n */\nenum obs_allow_direct_render {\n\tOBS_NO_DIRECT_RENDERING,\n\tOBS_ALLOW_DIRECT_RENDERING,\n};\n\nenum obs_scale_type {\n\tOBS_SCALE_DISABLE,\n\tOBS_SCALE_POINT,\n\tOBS_SCALE_BICUBIC,\n\tOBS_SCALE_BILINEAR,\n\tOBS_SCALE_LANCZOS,\n\tOBS_SCALE_AREA,\n};\n\nenum obs_blending_method {\n\tOBS_BLEND_METHOD_DEFAULT,\n\tOBS_BLEND_METHOD_SRGB_OFF,\n};\n\nenum obs_blending_type {\n\tOBS_BLEND_NORMAL,\n\tOBS_BLEND_ADDITIVE,\n\tOBS_BLEND_SUBTRACT,\n\tOBS_BLEND_SCREEN,\n\tOBS_BLEND_MULTIPLY,\n\tOBS_BLEND_LIGHTEN,\n\tOBS_BLEND_DARKEN,\n};\n\n/**\n * Used with scene items to indicate the type of bounds to use for scene items.\n * Mostly determines how the image will be scaled within those bounds, or\n * whether to use bounds at all.\n */\nenum obs_bounds_type {\n\tOBS_BOUNDS_NONE,            /**< no bounds */\n\tOBS_BOUNDS_STRETCH,         /**< stretch (ignores base scale) */\n\tOBS_BOUNDS_SCALE_INNER,     /**< scales to inner rectangle */\n\tOBS_BOUNDS_SCALE_OUTER,     /**< scales to outer rectangle */\n\tOBS_BOUNDS_SCALE_TO_WIDTH,  /**< scales to the width  */\n\tOBS_BOUNDS_SCALE_TO_HEIGHT, /**< scales to the height */\n\tOBS_BOUNDS_MAX_ONLY,        /**< no scaling, maximum size only */\n};\n\n/**\n * Used by libobs to define the state of a plugin/module.\n */\nenum obs_module_load_state {\n\tOBS_MODULE_INVALID,\n\tOBS_MODULE_ENABLED,\n\tOBS_MODULE_MISSING,\n\tOBS_MODULE_DISABLED,\n\tOBS_MODULE_DISABLED_SAFE,\n\tOBS_MODULE_FAILED_TO_OPEN,\n\tOBS_MODULE_FAILED_TO_INITIALIZE,\n};\n\nstruct obs_transform_info {\n\tstruct vec2 pos;\n\tfloat rot;\n\tstruct vec2 scale;\n\tuint32_t alignment;\n\n\tenum obs_bounds_type bounds_type;\n\tuint32_t bounds_alignment;\n\tstruct vec2 bounds;\n\tbool crop_to_bounds;\n};\n\n/**\n * Video initialization structure\n */\nstruct obs_video_info {\n#ifndef SWIG\n\t/**\n\t * Graphics module to use (usually \"libobs-opengl\" or \"libobs-d3d11\")\n\t */\n\tconst char *graphics_module;\n#endif\n\n\tuint32_t fps_num; /**< Output FPS numerator */\n\tuint32_t fps_den; /**< Output FPS denominator */\n\n\tuint32_t base_width;  /**< Base compositing width */\n\tuint32_t base_height; /**< Base compositing height */\n\n\tuint32_t output_width;           /**< Output width */\n\tuint32_t output_height;          /**< Output height */\n\tenum video_format output_format; /**< Output format */\n\n\t/** Video adapter index to use (NOTE: avoid for optimus laptops) */\n\tuint32_t adapter;\n\n\t/** Use shaders to convert to different color formats */\n\tbool gpu_conversion;\n\n\tenum video_colorspace colorspace; /**< YUV type (if YUV) */\n\tenum video_range_type range;      /**< YUV range (if YUV) */\n\n\tenum obs_scale_type scale_type; /**< How to scale if scaling */\n};\n\n/**\n * Audio initialization structure\n */\nstruct obs_audio_info {\n\tuint32_t samples_per_sec;\n\tenum speaker_layout speakers;\n};\n\nstruct obs_audio_info2 {\n\tuint32_t samples_per_sec;\n\tenum speaker_layout speakers;\n\n\tuint32_t max_buffering_ms;\n\tbool fixed_buffering;\n};\n\n/**\n * Sent to source filters via the filter_audio callback to allow filtering of\n * audio data\n */\nstruct obs_audio_data {\n\tuint8_t *data[MAX_AV_PLANES];\n\tuint32_t frames;\n\tuint64_t timestamp;\n};\n\n/**\n * Source audio output structure.  Used with obs_source_output_audio to output\n * source audio.  Audio is automatically resampled and remixed as necessary.\n */\nstruct obs_source_audio {\n\tconst uint8_t *data[MAX_AV_PLANES];\n\tuint32_t frames;\n\n\tenum speaker_layout speakers;\n\tenum audio_format format;\n\tuint32_t samples_per_sec;\n\n\tuint64_t timestamp;\n};\n\nstruct obs_source_cea_708 {\n\tconst uint8_t *data;\n\tuint32_t packets;\n\tuint64_t timestamp;\n};\n\n#define OBS_SOURCE_FRAME_LINEAR_ALPHA (1 << 0)\n\n/**\n * Source asynchronous video output structure.  Used with\n * obs_source_output_video to output asynchronous video.  Video is buffered as\n * necessary to play according to timestamps.  When used with audio output,\n * audio is synced to video as it is played.\n *\n * If a YUV format is specified, it will be automatically upsampled and\n * converted to RGB via shader on the graphics processor.\n *\n * NOTE: Non-YUV formats will always be treated as full range with this\n * structure!  Use obs_source_frame2 along with obs_source_output_video2\n * instead if partial range support is desired for non-YUV video formats.\n */\nstruct obs_source_frame {\n\tuint8_t *data[MAX_AV_PLANES];\n\tuint32_t linesize[MAX_AV_PLANES];\n\tuint32_t width;\n\tuint32_t height;\n\tuint64_t timestamp;\n\n\tenum video_format format;\n\tfloat color_matrix[16];\n\tbool full_range;\n\tuint16_t max_luminance;\n\tfloat color_range_min[3];\n\tfloat color_range_max[3];\n\tbool flip;\n\tuint8_t flags;\n\tuint8_t trc; /* enum video_trc */\n\n\t/* used internally by libobs */\n\tvolatile long refs;\n\tbool prev_frame;\n};\n\nstruct obs_source_frame2 {\n\tuint8_t *data[MAX_AV_PLANES];\n\tuint32_t linesize[MAX_AV_PLANES];\n\tuint32_t width;\n\tuint32_t height;\n\tuint64_t timestamp;\n\n\tenum video_format format;\n\tenum video_range_type range;\n\tfloat color_matrix[16];\n\tfloat color_range_min[3];\n\tfloat color_range_max[3];\n\tbool flip;\n\tuint8_t flags;\n\tuint8_t trc; /* enum video_trc */\n};\n\n/** Access to the argc/argv used to start OBS. What you see is what you get. */\nstruct obs_cmdline_args {\n\tint argc;\n\tchar **argv;\n};\n\n/* ------------------------------------------------------------------------- */\n/* OBS context */\n\n/**\n * Find a core libobs data file\n * @param path name of the base file\n * @return A string containing the full path to the file.\n *          Use bfree after use.\n */\nEXPORT char *obs_find_data_file(const char *file);\n\n// TODO: Remove after deprecation grace period\n/**\n * Add a path to search libobs data files in.\n * @param path Full path to directory to look in.\n *             The string is copied.\n */\nOBS_DEPRECATED EXPORT void obs_add_data_path(const char *path);\n\n// TODO: Remove after deprecation grace period\n/**\n * Remove a path from libobs core data paths.\n * @param path The path to compare to currently set paths.\n *             It does not need to be the same pointer, but\n *             the path string must match an entry fully.\n * @return Whether or not the path was successfully removed.\n *         If false, the path could not be found.\n */\nOBS_DEPRECATED EXPORT bool obs_remove_data_path(const char *path);\n\n/**\n * Initializes OBS\n *\n * @param  locale              The locale to use for modules\n * @param  module_config_path  Path to module config storage directory\n *                             (or NULL if none)\n * @param  store               The profiler name store for OBS to use or NULL\n */\nEXPORT bool obs_startup(const char *locale, const char *module_config_path, profiler_name_store_t *store);\n\n/** Releases all data associated with OBS and terminates the OBS context */\nEXPORT void obs_shutdown(void);\n\n/** @return true if the main OBS context has been initialized */\nEXPORT bool obs_initialized(void);\n\n/** @return The current core version */\nEXPORT uint32_t obs_get_version(void);\n\n/** @return The current core version string */\nEXPORT const char *obs_get_version_string(void);\n\n/**\n * Sets things up for calls to obs_get_cmdline_args. Called only once at startup\n * and safely copies argv/argc from main(). Subsequent calls do nothing.\n *\n * @param  argc  The count of command line arguments, from main()\n * @param  argv  An array of command line arguments, copied from main() and ends\n *               with NULL.\n */\nEXPORT void obs_set_cmdline_args(int argc, const char *const *argv);\n\n/**\n * Get the argc/argv used to start OBS\n *\n * @return  The command line arguments used for main(). Don't modify this or\n *          you'll mess things up for other callers.\n */\nEXPORT struct obs_cmdline_args obs_get_cmdline_args(void);\n\n/**\n * Sets a new locale to use for modules.  This will call obs_module_set_locale\n * for each module with the new locale.\n *\n * @param  locale  The locale to use for modules\n */\nEXPORT void obs_set_locale(const char *locale);\n\n/** @return the current locale */\nEXPORT const char *obs_get_locale(void);\n\n/** Initialize the Windows-specific crash handler */\n\n#ifdef _WIN32\nEXPORT void obs_init_win32_crash_handler(void);\n#endif\n\n/**\n * Returns the profiler name store (see util/profiler.h) used by OBS, which is\n * either a name store passed to obs_startup, an internal name store, or NULL\n * in case obs_initialized() returns false.\n */\nEXPORT profiler_name_store_t *obs_get_profiler_name_store(void);\n\n/**\n * Sets base video output base resolution/fps/format.\n *\n * @note This data cannot be changed if an output is currently active.\n * @note The graphics module cannot be changed without fully destroying the\n *       OBS context.\n *\n * @param   ovi  Pointer to an obs_video_info structure containing the\n *               specification of the graphics subsystem,\n * @return       OBS_VIDEO_SUCCESS if successful\n *               OBS_VIDEO_NOT_SUPPORTED if the adapter lacks capabilities\n *               OBS_VIDEO_INVALID_PARAM if a parameter is invalid\n *               OBS_VIDEO_CURRENTLY_ACTIVE if video is currently active\n *               OBS_VIDEO_MODULE_NOT_FOUND if the graphics module is not found\n *               OBS_VIDEO_FAIL for generic failure\n */\nEXPORT int obs_reset_video(struct obs_video_info *ovi);\n\n/**\n * Sets base audio output format/channels/samples/etc\n *\n * @note Cannot reset base audio if an output is currently active.\n */\nEXPORT bool obs_reset_audio(const struct obs_audio_info *oai);\nEXPORT bool obs_reset_audio2(const struct obs_audio_info2 *oai);\n\n/** Gets the current video settings, returns false if no video */\nEXPORT bool obs_get_video_info(struct obs_video_info *ovi);\n\n/** Gets the SDR white level, returns 300.f if no video */\nEXPORT float obs_get_video_sdr_white_level(void);\n\n/** Gets the HDR nominal peak level, returns 1000.f if no video */\nEXPORT float obs_get_video_hdr_nominal_peak_level(void);\n\n/** Sets the video levels */\nEXPORT void obs_set_video_levels(float sdr_white_level, float hdr_nominal_peak_level);\n\n/** Gets the current audio settings, returns false if no audio */\nEXPORT bool obs_get_audio_info(struct obs_audio_info *oai);\n\n/**\n * Gets the v2 audio settings that includes buffering information.\n * Returns false if no audio.\n */\nEXPORT bool obs_get_audio_info2(struct obs_audio_info2 *oai2);\n\n/**\n * Opens a plugin module directly from a specific path.\n *\n * If the module already exists then the function will return successful, and\n * the module parameter will be given the pointer to the existing module.\n *\n * This does not initialize the module, it only loads the module image.  To\n * initialize the module, call obs_init_module.\n *\n * @param  module     The pointer to the created module.\n * @param  path       Specifies the path to the module library file.  If the\n *                    extension is not specified, it will use the extension\n *                    appropriate to the operating system.\n * @param  data_path  Specifies the path to the directory where the module's\n *                    data files are stored.\n * @returns           MODULE_SUCCESS if successful\n *                    MODULE_ERROR if a generic error occurred\n *                    MODULE_FAILED_TO_OPEN if the module failed to open, e.g. because it was not found or had missing symbols\n *                    MODULE_MISSING_EXPORTS if required exports are missing\n *                    MODULE_INCOMPATIBLE_VER if incompatible version\n */\nEXPORT int obs_open_module(obs_module_t **module, const char *path, const char *data_path);\n\nEXPORT bool obs_create_disabled_module(obs_module_t **module, const char *path, const char *data_path,\n\t\t\t\t       enum obs_module_load_state state);\n\n/**\n * Initializes the module, which calls its obs_module_load export.  If the\n * module is already loaded, then this function does nothing and returns\n * successful.\n */\nEXPORT bool obs_init_module(obs_module_t *module);\n\n/** Returns a module based upon its name, or NULL if not found */\nEXPORT obs_module_t *obs_get_module(const char *name);\n\n/** Returns a module if it is disabled, or NULL if not found in the disabled list */\nEXPORT obs_module_t *obs_get_disabled_module(const char *name);\n\n/** Gets library of module */\nEXPORT void *obs_get_module_lib(obs_module_t *module);\n\n/** Returns locale text from a specific module */\nEXPORT bool obs_module_get_locale_string(const obs_module_t *mod, const char *lookup_string,\n\t\t\t\t\t const char **translated_string);\n\nEXPORT const char *obs_module_get_locale_text(const obs_module_t *mod, const char *text);\n\n/** Logs loaded modules */\nEXPORT void obs_log_loaded_modules(void);\n\n/** Returns the module file name */\nEXPORT const char *obs_get_module_file_name(obs_module_t *module);\n\n/** Returns the module full name */\nEXPORT const char *obs_get_module_name(obs_module_t *module);\n\n/** Returns the module author(s) */\nEXPORT const char *obs_get_module_author(obs_module_t *module);\n\n/** Returns the module description */\nEXPORT const char *obs_get_module_description(obs_module_t *module);\n\n/** Returns the module binary path */\nEXPORT const char *obs_get_module_binary_path(obs_module_t *module);\n\n/** Returns the module data path */\nEXPORT const char *obs_get_module_data_path(obs_module_t *module);\n\n/** Adds a source type id to the module provided sources list */\nEXPORT void obs_module_add_source(obs_module_t *module, const char *id);\n\n/** Adds an output type id to the module provided outputs list */\nEXPORT void obs_module_add_output(obs_module_t *module, const char *id);\n\n/** Adds an encoder type id to the module provided encoders list */\nEXPORT void obs_module_add_encoder(obs_module_t *module, const char *id);\n\n/** Adds an encoder service id to the module provided services list */\nEXPORT void obs_module_add_service(obs_module_t *module, const char *id);\n\n#ifndef SWIG\n/**\n * Adds a module search path to be used with obs_find_modules.  If the search\n * path strings contain %module%, that text will be replaced with the module\n * name when used.\n *\n * @param  bin   Specifies the module's binary directory search path.\n * @param  data  Specifies the module's data directory search path.\n */\nEXPORT void obs_add_module_path(const char *bin, const char *data);\n\n/**\n * Adds a module to the list of modules allowed to load in Safe Mode.\n * If the list is empty, all modules are allowed.\n *\n * @param  name  Specifies the module's name (filename sans extension).\n */\nEXPORT void obs_add_safe_module(const char *name);\n\n/**\n * Adds a module to the list of core modules (which cannot be disabled).\n * If the list is empty, all modules are allowed.\n *\n * @param  name  Specifies the module's name (filename sans extension).\n */\nEXPORT void obs_add_core_module(const char *name);\n\n/** Automatically loads all modules from module paths (convenience function) */\nEXPORT void obs_load_all_modules(void);\n\nstruct obs_module_failure_info {\n\tchar **failed_modules;\n\tsize_t count;\n};\n\nEXPORT void obs_module_failure_info_free(struct obs_module_failure_info *mfi);\nEXPORT void obs_load_all_modules2(struct obs_module_failure_info *mfi);\n\n/** Notifies modules that all modules have been loaded.  This function should\n * be called after all modules have been loaded. */\nEXPORT void obs_post_load_modules(void);\n\nstruct obs_module_info {\n\tconst char *bin_path;\n\tconst char *data_path;\n};\n\ntypedef void (*obs_find_module_callback_t)(void *param, const struct obs_module_info *info);\n\n/** Finds all modules within the search paths added by obs_add_module_path. */\nEXPORT void obs_find_modules(obs_find_module_callback_t callback, void *param);\n\nstruct obs_module_info2 {\n\tconst char *bin_path;\n\tconst char *data_path;\n\tconst char *name;\n};\n\ntypedef void (*obs_find_module_callback2_t)(void *param, const struct obs_module_info2 *info);\n\n/** Finds all modules within the search paths added by obs_add_module_path. */\nEXPORT void obs_find_modules2(obs_find_module_callback2_t callback, void *param);\n#endif\n\ntypedef void (*obs_enum_module_callback_t)(void *param, obs_module_t *module);\n\n/** Enumerates all loaded modules */\nEXPORT void obs_enum_modules(obs_enum_module_callback_t callback, void *param);\n\n/** Helper function for using default module locale */\nEXPORT lookup_t *obs_module_load_locale(obs_module_t *module, const char *default_locale, const char *locale);\n\n/**\n * Returns the location of a plugin module data file.\n *\n * @note   Modules should use obs_module_file function defined in obs-module.h\n *         as a more elegant means of getting their files without having to\n *         specify the module parameter.\n *\n * @param  module  The module associated with the file to locate\n * @param  file    The file to locate\n * @return         Path string, or NULL if not found.  Use bfree to free string.\n */\nEXPORT char *obs_find_module_file(obs_module_t *module, const char *file);\n\n/**\n * Adds a module name to the disabled modules list.\n *\n * @param  name    The name of the module to disable\n */\nEXPORT void obs_add_disabled_module(const char *name);\n\n/**\n * Returns if a module can be disabled.\n *\n * @param  name    The name of the module to check\n * @return         Boolean to indicate if module can be disabled\n */\nEXPORT bool obs_get_module_allow_disable(const char *name);\n\n/**\n * Returns the path of a plugin module config file (whether it exists or not)\n *\n * @note   Modules should use obs_module_config_path function defined in\n *         obs-module.h as a more elegant means of getting their files without\n *         having to specify the module parameter.\n *\n * @param  module  The module associated with the path\n * @param  file    The file to get a path to\n * @return         Path string, or NULL if not found.  Use bfree to free string.\n */\nEXPORT char *obs_module_get_config_path(obs_module_t *module, const char *file);\n\n/** Enumerates all source types (inputs, filters, transitions, etc).  */\nEXPORT bool obs_enum_source_types(size_t idx, const char **id);\n\n/**\n * Enumerates all available inputs source types.\n *\n *   Inputs are general source inputs (such as capture sources, device sources,\n * etc).\n */\nEXPORT bool obs_enum_input_types(size_t idx, const char **id);\nEXPORT bool obs_enum_input_types2(size_t idx, const char **id, const char **unversioned_id);\n\nEXPORT const char *obs_get_latest_input_type_id(const char *unversioned_id);\n\n/**\n * Enumerates all available filter source types.\n *\n *   Filters are sources that are used to modify the video/audio output of\n * other sources.\n */\nEXPORT bool obs_enum_filter_types(size_t idx, const char **id);\n\n/**\n * Enumerates all available transition source types.\n *\n *   Transitions are sources used to transition between two or more other\n * sources.\n */\nEXPORT bool obs_enum_transition_types(size_t idx, const char **id);\n\n/** Enumerates all available output types. */\nEXPORT bool obs_enum_output_types(size_t idx, const char **id);\n\n/** Enumerates all available encoder types. */\nEXPORT bool obs_enum_encoder_types(size_t idx, const char **id);\n\n/** Enumerates all available service types. */\nEXPORT bool obs_enum_service_types(size_t idx, const char **id);\n\n/** Helper function for entering the OBS graphics context */\nEXPORT void obs_enter_graphics(void);\n\n/** Helper function for leaving the OBS graphics context */\nEXPORT void obs_leave_graphics(void);\n\n/** Gets the main audio output handler for this OBS context */\nEXPORT audio_t *obs_get_audio(void);\n\n/** Gets the main video output handler for this OBS context */\nEXPORT video_t *obs_get_video(void);\n\n/** Returns true if video is active, false otherwise */\nEXPORT bool obs_video_active(void);\n\n/** Sets the primary output source for a channel. */\nEXPORT void obs_set_output_source(uint32_t channel, obs_source_t *source);\n\n/**\n * Gets the primary output source for a channel and increments the reference\n * counter for that source.  Use obs_source_release to release.\n */\nEXPORT obs_source_t *obs_get_output_source(uint32_t channel);\n\n/**\n * Enumerates all input sources\n *\n *   Callback function returns true to continue enumeration, or false to end\n * enumeration.\n *\n *   Use obs_source_get_ref or obs_source_get_weak_source if you want to retain\n * a reference after obs_enum_sources finishes\n */\nEXPORT void obs_enum_sources(bool (*enum_proc)(void *, obs_source_t *), void *param);\n\n/** Enumerates scenes */\nEXPORT void obs_enum_scenes(bool (*enum_proc)(void *, obs_source_t *), void *param);\n\n/** Enumerates all sources (regardless of type) */\nEXPORT void obs_enum_all_sources(bool (*enum_proc)(void *, obs_source_t *), void *param);\n\n/** Enumerates outputs */\nEXPORT void obs_enum_outputs(bool (*enum_proc)(void *, obs_output_t *), void *param);\n\n/** Enumerates encoders */\nEXPORT void obs_enum_encoders(bool (*enum_proc)(void *, obs_encoder_t *), void *param);\n\n/** Enumerates encoders */\nEXPORT void obs_enum_services(bool (*enum_proc)(void *, obs_service_t *), void *param);\n\n/** Enumerates canvases */\nEXPORT void obs_enum_canvases(bool (*enum_proc)(void *, obs_canvas_t *), void *param);\n\n/**\n * Gets a source by its name.\n *\n *   Increments the source reference counter, use obs_source_release to\n * release it when complete.\n */\nEXPORT obs_source_t *obs_get_source_by_name(const char *name);\n\n/**\n * Gets a source by its UUID.\n *\n *   Increments the source reference counter, use obs_source_release to\n * release it when complete.\n */\nEXPORT obs_source_t *obs_get_source_by_uuid(const char *uuid);\n\n/** Get a transition source by its name. */\nOBS_DEPRECATED EXPORT obs_source_t *obs_get_transition_by_name(const char *name);\n\n/** Get a transition source by its UUID. */\nOBS_DEPRECATED EXPORT obs_source_t *obs_get_transition_by_uuid(const char *uuid);\n\n/** Gets an output by its name. */\nEXPORT obs_output_t *obs_get_output_by_name(const char *name);\n\n/** Gets an encoder by its name. */\nEXPORT obs_encoder_t *obs_get_encoder_by_name(const char *name);\n\n/** Gets an service by its name. */\nEXPORT obs_service_t *obs_get_service_by_name(const char *name);\n\n/** Get a canvas by its name. */\nEXPORT obs_canvas_t *obs_get_canvas_by_name(const char *name);\n/** Get a canvas by its UUID. */\nEXPORT obs_canvas_t *obs_get_canvas_by_uuid(const char *uuid);\n\nenum obs_base_effect {\n\tOBS_EFFECT_DEFAULT,             /**< RGB/YUV */\n\tOBS_EFFECT_DEFAULT_RECT,        /**< RGB/YUV (using texture_rect) */\n\tOBS_EFFECT_OPAQUE,              /**< RGB/YUV (alpha set to 1.0) */\n\tOBS_EFFECT_SOLID,               /**< RGB/YUV (solid color only) */\n\tOBS_EFFECT_BICUBIC,             /**< Bicubic downscale */\n\tOBS_EFFECT_LANCZOS,             /**< Lanczos downscale */\n\tOBS_EFFECT_BILINEAR_LOWRES,     /**< Bilinear low resolution downscale */\n\tOBS_EFFECT_PREMULTIPLIED_ALPHA, /**< Premultiplied alpha */\n\tOBS_EFFECT_REPEAT,              /**< RGB/YUV (repeating) */\n\tOBS_EFFECT_AREA,                /**< Area rescale */\n};\n\n/** Returns a commonly used base effect */\nEXPORT gs_effect_t *obs_get_base_effect(enum obs_base_effect effect);\n\n/** Returns the primary obs signal handler */\nEXPORT signal_handler_t *obs_get_signal_handler(void);\n\n/** Returns the primary obs procedure handler */\nEXPORT proc_handler_t *obs_get_proc_handler(void);\n\n/** Renders the last main output texture */\nEXPORT void obs_render_main_texture(void);\n\n/** Renders the last main output texture ignoring background color */\nEXPORT void obs_render_main_texture_src_color_only(void);\n\n/** Renders the last canvas output texture */\nEXPORT void obs_render_canvas_texture(obs_canvas_t *canvas);\n\n/** Renders the last main output texture ignoring background color */\nEXPORT void obs_render_canvas_texture_src_color_only(obs_canvas_t *canvas);\n\n/** Returns the last main output texture.  This can return NULL if the texture\n * is unavailable. */\nEXPORT gs_texture_t *obs_get_main_texture(void);\n\n/** Saves a source to settings data */\nEXPORT obs_data_t *obs_save_source(obs_source_t *source);\n\n/** Loads a source from settings data */\nEXPORT obs_source_t *obs_load_source(obs_data_t *data);\n\n/** Loads a private source from settings data */\nEXPORT obs_source_t *obs_load_private_source(obs_data_t *data);\n\n/** Send a save signal to sources */\nEXPORT void obs_source_save(obs_source_t *source);\n\n/** Send a load signal to sources (soft deprecated; does not load filters) */\nEXPORT void obs_source_load(obs_source_t *source);\n\n/** Send a load signal to sources */\nEXPORT void obs_source_load2(obs_source_t *source);\n\ntypedef void (*obs_load_source_cb)(void *private_data, obs_source_t *source);\n\n/** Loads sources from a data array */\nEXPORT void obs_load_sources(obs_data_array_t *array, obs_load_source_cb cb, void *private_data);\n\n/** Saves sources to a data array */\nEXPORT obs_data_array_t *obs_save_sources(void);\n\ntypedef bool (*obs_save_source_filter_cb)(void *data, obs_source_t *source);\nEXPORT obs_data_array_t *obs_save_sources_filtered(obs_save_source_filter_cb cb, void *data);\n\n/** Reset source UUIDs. NOTE: this function is only to be used by the UI and\n *  will be removed in a future version! */\nEXPORT void obs_reset_source_uuids(void);\n\nenum obs_obj_type {\n\tOBS_OBJ_TYPE_INVALID,\n\tOBS_OBJ_TYPE_SOURCE,\n\tOBS_OBJ_TYPE_OUTPUT,\n\tOBS_OBJ_TYPE_ENCODER,\n\tOBS_OBJ_TYPE_SERVICE,\n\tOBS_OBJ_TYPE_CANVAS,\n};\n\nEXPORT enum obs_obj_type obs_obj_get_type(void *obj);\nEXPORT const char *obs_obj_get_id(void *obj);\nEXPORT bool obs_obj_invalid(void *obj);\nEXPORT void *obs_obj_get_data(void *obj);\nEXPORT bool obs_obj_is_private(void *obj);\n\ntypedef bool (*obs_enum_audio_device_cb)(void *data, const char *name, const char *id);\n\nEXPORT bool obs_audio_monitoring_available(void);\n\nEXPORT void obs_reset_audio_monitoring(void);\n\nEXPORT void obs_enum_audio_monitoring_devices(obs_enum_audio_device_cb cb, void *data);\n\nEXPORT bool obs_set_audio_monitoring_device(const char *name, const char *id);\nEXPORT void obs_get_audio_monitoring_device(const char **name, const char **id);\n\nEXPORT void obs_add_tick_callback(void (*tick)(void *param, float seconds), void *param);\nEXPORT void obs_remove_tick_callback(void (*tick)(void *param, float seconds), void *param);\n\nEXPORT void obs_add_main_render_callback(void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param);\nEXPORT void obs_remove_main_render_callback(void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param);\n\nEXPORT void obs_add_main_rendered_callback(void (*rendered)(void *param), void *param);\nEXPORT void obs_remove_main_rendered_callback(void (*rendered)(void *param), void *param);\n\nEXPORT void obs_add_raw_video_callback(const struct video_scale_info *conversion,\n\t\t\t\t       void (*callback)(void *param, struct video_data *frame), void *param);\nEXPORT void obs_add_raw_video_callback2(const struct video_scale_info *conversion, uint32_t frame_rate_divisor,\n\t\t\t\t\tvoid (*callback)(void *param, struct video_data *frame), void *param);\nEXPORT void obs_remove_raw_video_callback(void (*callback)(void *param, struct video_data *frame), void *param);\n\nEXPORT void obs_add_raw_audio_callback(size_t mix_idx, const struct audio_convert_info *conversion,\n\t\t\t\t       audio_output_callback_t callback, void *param);\nEXPORT void obs_remove_raw_audio_callback(size_t mix_idx, audio_output_callback_t callback, void *param);\n\nEXPORT uint64_t obs_get_video_frame_time(void);\n\nEXPORT double obs_get_active_fps(void);\nEXPORT uint64_t obs_get_average_frame_time_ns(void);\nEXPORT uint64_t obs_get_frame_interval_ns(void);\n\nEXPORT uint32_t obs_get_total_frames(void);\nEXPORT uint32_t obs_get_lagged_frames(void);\n\nOBS_DEPRECATED EXPORT bool obs_nv12_tex_active(void);\nOBS_DEPRECATED EXPORT bool obs_p010_tex_active(void);\n\nEXPORT void obs_apply_private_data(obs_data_t *settings);\nEXPORT void obs_set_private_data(obs_data_t *settings);\nEXPORT obs_data_t *obs_get_private_data(void);\n\ntypedef void (*obs_task_t)(void *param);\n\nenum obs_task_type {\n\tOBS_TASK_UI,\n\tOBS_TASK_GRAPHICS,\n\tOBS_TASK_AUDIO,\n\tOBS_TASK_DESTROY,\n};\n\nEXPORT void obs_queue_task(enum obs_task_type type, obs_task_t task, void *param, bool wait);\nEXPORT bool obs_in_task_thread(enum obs_task_type type);\n\nEXPORT bool obs_wait_for_destroy_queue(void);\n\ntypedef void (*obs_task_handler_t)(obs_task_t task, void *param, bool wait);\nEXPORT void obs_set_ui_task_handler(obs_task_handler_t handler);\n\nEXPORT obs_object_t *obs_object_get_ref(obs_object_t *object);\nEXPORT void obs_object_release(obs_object_t *object);\n\nEXPORT void obs_weak_object_addref(obs_weak_object_t *weak);\nEXPORT void obs_weak_object_release(obs_weak_object_t *weak);\nEXPORT obs_weak_object_t *obs_object_get_weak_object(obs_object_t *object);\nEXPORT obs_object_t *obs_weak_object_get_object(obs_weak_object_t *weak);\nEXPORT bool obs_weak_object_expired(obs_weak_object_t *weak);\nEXPORT bool obs_weak_object_references_object(obs_weak_object_t *weak, obs_object_t *object);\n\n/* ------------------------------------------------------------------------- */\n/* View context */\n\n/**\n * Creates a view context.\n *\n *   A view can be used for things like separate previews, or drawing\n * sources separately.\n */\nEXPORT obs_view_t *obs_view_create(void);\n\n/** Destroys this view context */\nEXPORT void obs_view_destroy(obs_view_t *view);\n\n/** Sets the source to be used for this view context. */\nEXPORT void obs_view_set_source(obs_view_t *view, uint32_t channel, obs_source_t *source);\n\n/** Gets the source currently in use for this view context */\nEXPORT obs_source_t *obs_view_get_source(obs_view_t *view, uint32_t channel);\n\n/** Renders the sources of this view context */\nEXPORT void obs_view_render(obs_view_t *view);\n\n/** Adds a view to the main render loop, with current obs_get_video_info state */\nEXPORT video_t *obs_view_add(obs_view_t *view);\n\n/** Adds a view to the main render loop, with custom video settings */\nEXPORT video_t *obs_view_add2(obs_view_t *view, struct obs_video_info *ovi);\n\n/** Removes a view from the main render loop */\nEXPORT void obs_view_remove(obs_view_t *view);\n\n/** Enumerate the video info of all mixes using the specified view context */\nEXPORT void obs_view_enum_video_info(obs_view_t *view, bool (*enum_proc)(void *, struct obs_video_info *), void *param);\n\n/* ------------------------------------------------------------------------- */\n/* Display context */\n\n/**\n * Adds a new window display linked to the main render pipeline.  This creates\n * a new swap chain which updates every frame.\n *\n * @param  graphics_data  The swap chain initialization data.\n * @return                The new display context, or NULL if failed.\n */\nEXPORT obs_display_t *obs_display_create(const struct gs_init_data *graphics_data, uint32_t backround_color);\n\n/** Destroys a display context */\nEXPORT void obs_display_destroy(obs_display_t *display);\n\n/** Changes the size of this display */\nEXPORT void obs_display_resize(obs_display_t *display, uint32_t cx, uint32_t cy);\n\n/** Updates the color space of this display */\nEXPORT void obs_display_update_color_space(obs_display_t *display);\n\n/**\n * Adds a draw callback for this display context\n *\n * @param  display  The display context.\n * @param  draw     The draw callback which is called each time a frame\n *                  updates.\n * @param  param    The user data to be associated with this draw callback.\n */\nEXPORT void obs_display_add_draw_callback(obs_display_t *display, void (*draw)(void *param, uint32_t cx, uint32_t cy),\n\t\t\t\t\t  void *param);\n\n/** Removes a draw callback for this display context */\nEXPORT void obs_display_remove_draw_callback(obs_display_t *display,\n\t\t\t\t\t     void (*draw)(void *param, uint32_t cx, uint32_t cy), void *param);\n\nEXPORT void obs_display_set_enabled(obs_display_t *display, bool enable);\nEXPORT bool obs_display_enabled(obs_display_t *display);\n\nEXPORT void obs_display_set_background_color(obs_display_t *display, uint32_t color);\n\nEXPORT void obs_display_size(obs_display_t *display, uint32_t *width, uint32_t *height);\n\n/* ------------------------------------------------------------------------- */\n/* Sources */\n\n/** Returns the translated display name of a source */\nEXPORT const char *obs_source_get_display_name(const char *id);\n\n/** Returns a pointer to the module which provides the source */\nEXPORT obs_module_t *obs_source_get_module(const char *id);\n\n/** Returns the load state of a source's module given the id */\nEXPORT enum obs_module_load_state obs_source_load_state(const char *id);\n\n/**\n * Creates a source of the specified type with the specified settings.\n *\n *   The \"source\" context is used for anything related to presenting\n * or modifying video/audio.  Use obs_source_release to release it.\n */\nEXPORT obs_source_t *obs_source_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data);\n\nEXPORT obs_source_t *obs_source_create_private(const char *id, const char *name, obs_data_t *settings);\n\n/* if source has OBS_SOURCE_DO_NOT_DUPLICATE output flag set, only returns a\n * reference */\nEXPORT obs_source_t *obs_source_duplicate(obs_source_t *source, const char *desired_name, bool create_private);\n/**\n * Adds/releases a reference to a source.  When the last reference is\n * released, the source is destroyed.\n */\nEXPORT void obs_source_release(obs_source_t *source);\n\nEXPORT void obs_weak_source_addref(obs_weak_source_t *weak);\nEXPORT void obs_weak_source_release(obs_weak_source_t *weak);\n\nEXPORT obs_source_t *obs_source_get_ref(obs_source_t *source);\nEXPORT obs_weak_source_t *obs_source_get_weak_source(obs_source_t *source);\nEXPORT obs_source_t *obs_weak_source_get_source(obs_weak_source_t *weak);\nEXPORT bool obs_weak_source_expired(obs_weak_source_t *weak);\n\nEXPORT bool obs_weak_source_references_source(obs_weak_source_t *weak, obs_source_t *source);\n\n/** Notifies all references that the source should be released */\nEXPORT void obs_source_remove(obs_source_t *source);\n\n/** Returns true if the source should be released */\nEXPORT bool obs_source_removed(const obs_source_t *source);\n\n/** The 'hidden' flag is not the same as a sceneitem's visibility. It is a\n  * property the determines if it can be found through searches. **/\n/** Simply sets a 'hidden' flag when the source is still alive but shouldn't be found */\nEXPORT void obs_source_set_hidden(obs_source_t *source, bool hidden);\n\n/** Returns the current 'hidden' state on the source */\nEXPORT bool obs_source_is_hidden(obs_source_t *source);\n\n/** Returns capability flags of a source */\nEXPORT uint32_t obs_source_get_output_flags(const obs_source_t *source);\n\n/** Returns capability flags of a source type */\nEXPORT uint32_t obs_get_source_output_flags(const char *id);\n\n/** Gets the default settings for a source type */\nEXPORT obs_data_t *obs_get_source_defaults(const char *id);\n\n/** Returns the property list, if any.  Free with obs_properties_destroy */\nEXPORT obs_properties_t *obs_get_source_properties(const char *id);\n\nEXPORT obs_missing_files_t *obs_source_get_missing_files(const obs_source_t *source);\n\nEXPORT void obs_source_replace_missing_file(obs_missing_file_cb cb, obs_source_t *source, const char *new_path,\n\t\t\t\t\t    void *data);\n\n/** Returns whether the source has custom properties or not */\nEXPORT bool obs_is_source_configurable(const char *id);\n\nEXPORT bool obs_source_configurable(const obs_source_t *source);\n\n/**\n * Returns the properties list for a specific existing source.  Free with\n * obs_properties_destroy\n */\nEXPORT obs_properties_t *obs_source_properties(const obs_source_t *source);\n\n/** Updates settings for this source */\nEXPORT void obs_source_update(obs_source_t *source, obs_data_t *settings);\nEXPORT void obs_source_reset_settings(obs_source_t *source, obs_data_t *settings);\n\n/** Renders a video source. */\nEXPORT void obs_source_video_render(obs_source_t *source);\n\n/** Gets the width of a source (if it has video) */\nEXPORT uint32_t obs_source_get_width(obs_source_t *source);\n\n/** Gets the height of a source (if it has video) */\nEXPORT uint32_t obs_source_get_height(obs_source_t *source);\n\n/** Gets the color space of a source (if it has video) */\nEXPORT enum gs_color_space obs_source_get_color_space(obs_source_t *source, size_t count,\n\t\t\t\t\t\t      const enum gs_color_space *preferred_spaces);\n\n/** Hints whether or not the source will blend texels */\nEXPORT bool obs_source_get_texcoords_centered(obs_source_t *source);\n\n/**\n * If the source is a filter, returns the parent source of the filter.  Only\n * guaranteed to be valid inside of the video_render, filter_audio,\n * filter_video, and filter_remove callbacks.\n */\nEXPORT obs_source_t *obs_filter_get_parent(const obs_source_t *filter);\n\n/**\n * If the source is a filter, returns the target source of the filter.  Only\n * guaranteed to be valid inside of the video_render, filter_audio,\n * filter_video, and filter_remove callbacks.\n */\nEXPORT obs_source_t *obs_filter_get_target(const obs_source_t *filter);\n\n/** Used to directly render a non-async source without any filter processing */\nEXPORT void obs_source_default_render(obs_source_t *source);\n\n/** Adds a filter to the source (which is used whenever the source is used) */\nEXPORT void obs_source_filter_add(obs_source_t *source, obs_source_t *filter);\n\n/** Removes a filter from the source */\nEXPORT void obs_source_filter_remove(obs_source_t *source, obs_source_t *filter);\n\n/** Modifies the order of a specific filter */\nEXPORT void obs_source_filter_set_order(obs_source_t *source, obs_source_t *filter, enum obs_order_movement movement);\n\n/** Gets filter index */\nEXPORT int obs_source_filter_get_index(obs_source_t *source, obs_source_t *filter);\n\n/** Sets filter index */\nEXPORT void obs_source_filter_set_index(obs_source_t *source, obs_source_t *filter, size_t index);\n\n/** Gets the settings string for a source */\nEXPORT obs_data_t *obs_source_get_settings(const obs_source_t *source);\n\n/** Gets the name of a source */\nEXPORT const char *obs_source_get_name(const obs_source_t *source);\n\n/** Sets the name of a source */\nEXPORT void obs_source_set_name(obs_source_t *source, const char *name);\n\n/** Gets the UUID of a source */\nEXPORT const char *obs_source_get_uuid(const obs_source_t *source);\n\n/** Gets the source type */\nEXPORT enum obs_source_type obs_source_get_type(const obs_source_t *source);\n\n/** Gets the source identifier */\nEXPORT const char *obs_source_get_id(const obs_source_t *source);\nEXPORT const char *obs_source_get_unversioned_id(const obs_source_t *source);\n\n/** Returns the signal handler for a source */\nEXPORT signal_handler_t *obs_source_get_signal_handler(const obs_source_t *source);\n\n/** Returns the procedure handler for a source */\nEXPORT proc_handler_t *obs_source_get_proc_handler(const obs_source_t *source);\n\n/** Sets the user volume for a source that has audio output */\nEXPORT void obs_source_set_volume(obs_source_t *source, float volume);\n\n/** Gets the user volume for a source that has audio output */\nEXPORT float obs_source_get_volume(const obs_source_t *source);\n\n/* Gets speaker layout of a source */\nEXPORT enum speaker_layout obs_source_get_speaker_layout(obs_source_t *source);\n\n/** Sets the balance value for a stereo audio source */\nEXPORT void obs_source_set_balance_value(obs_source_t *source, float balance);\n\n/** Gets the balance value for a stereo audio source */\nEXPORT float obs_source_get_balance_value(const obs_source_t *source);\n\n/** Sets the audio sync offset (in nanoseconds) for a source */\nEXPORT void obs_source_set_sync_offset(obs_source_t *source, int64_t offset);\n\n/** Gets the audio sync offset (in nanoseconds) for a source */\nEXPORT int64_t obs_source_get_sync_offset(const obs_source_t *source);\n\n/** Enumerates active child sources used by this source */\nEXPORT void obs_source_enum_active_sources(obs_source_t *source, obs_source_enum_proc_t enum_callback, void *param);\n\n/** Enumerates the entire active child source tree used by this source */\nEXPORT void obs_source_enum_active_tree(obs_source_t *source, obs_source_enum_proc_t enum_callback, void *param);\n\nEXPORT void obs_source_enum_full_tree(obs_source_t *source, obs_source_enum_proc_t enum_callback, void *param);\n\n/** Returns true if active, false if not */\nEXPORT bool obs_source_active(const obs_source_t *source);\n\n/**\n * Returns true if currently displayed somewhere (active or not), false if not\n */\nEXPORT bool obs_source_showing(const obs_source_t *source);\n\n/** Unused flag */\n#define OBS_SOURCE_FLAG_UNUSED_1 (1 << 0)\n/** Specifies to force audio to mono */\n#define OBS_SOURCE_FLAG_FORCE_MONO (1 << 1)\n\n/**\n * Sets source flags.  Note that these are different from the main output\n * flags.  These are generally things that can be set by the source or user,\n * while the output flags are more used to determine capabilities of a source.\n */\nEXPORT void obs_source_set_flags(obs_source_t *source, uint32_t flags);\n\n/** Gets source flags. */\nEXPORT uint32_t obs_source_get_flags(const obs_source_t *source);\n\n/**\n * Sets audio mixer flags.  These flags are used to specify which mixers\n * the source's audio should be applied to.\n */\nEXPORT void obs_source_set_audio_mixers(obs_source_t *source, uint32_t mixers);\n\n/** Gets audio mixer flags */\nEXPORT uint32_t obs_source_get_audio_mixers(const obs_source_t *source);\n\n/**\n * Increments the 'showing' reference counter to indicate that the source is\n * being shown somewhere.  If the reference counter was 0, will call the 'show'\n * callback.\n */\nEXPORT void obs_source_inc_showing(obs_source_t *source);\n\n/**\n * Increments the 'active' reference counter to indicate that the source is\n * fully active.  If the reference counter was 0, will call the 'activate'\n * callback.\n *\n * Unlike obs_source_inc_showing, this will cause children of this source to be\n * considered showing as well (currently used by transition previews to make\n * the stinger transition show correctly).  obs_source_inc_showing should\n * generally be used instead.\n */\nEXPORT void obs_source_inc_active(obs_source_t *source);\n\n/**\n * Decrements the 'showing' reference counter to indicate that the source is\n * no longer being shown somewhere.  If the reference counter is set to 0,\n * will call the 'hide' callback\n */\nEXPORT void obs_source_dec_showing(obs_source_t *source);\n\n/**\n * Decrements the 'active' reference counter to indicate that the source is no\n * longer fully active.  If the reference counter is set to 0, will call the\n * 'deactivate' callback\n *\n * Unlike obs_source_dec_showing, this will cause children of this source to be\n * considered not showing as well.  obs_source_dec_showing should generally be\n * used instead.\n */\nEXPORT void obs_source_dec_active(obs_source_t *source);\n\n/** Enumerates filters assigned to the source */\nEXPORT void obs_source_enum_filters(obs_source_t *source, obs_source_enum_proc_t callback, void *param);\n\n/** Gets a filter of a source by its display name. */\nEXPORT obs_source_t *obs_source_get_filter_by_name(obs_source_t *source, const char *name);\n\n/** Gets the number of filters the source has. */\nEXPORT size_t obs_source_filter_count(const obs_source_t *source);\n\nEXPORT void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src);\nEXPORT void obs_source_copy_single_filter(obs_source_t *dst, obs_source_t *filter);\n\nEXPORT bool obs_source_enabled(const obs_source_t *source);\nEXPORT void obs_source_set_enabled(obs_source_t *source, bool enabled);\n\nEXPORT bool obs_source_muted(const obs_source_t *source);\nEXPORT void obs_source_set_muted(obs_source_t *source, bool muted);\n\nEXPORT bool obs_source_push_to_mute_enabled(obs_source_t *source);\nEXPORT void obs_source_enable_push_to_mute(obs_source_t *source, bool enabled);\n\nEXPORT uint64_t obs_source_get_push_to_mute_delay(obs_source_t *source);\nEXPORT void obs_source_set_push_to_mute_delay(obs_source_t *source, uint64_t delay);\n\nEXPORT bool obs_source_push_to_talk_enabled(obs_source_t *source);\nEXPORT void obs_source_enable_push_to_talk(obs_source_t *source, bool enabled);\n\nEXPORT uint64_t obs_source_get_push_to_talk_delay(obs_source_t *source);\nEXPORT void obs_source_set_push_to_talk_delay(obs_source_t *source, uint64_t delay);\n\ntypedef void (*obs_source_audio_capture_t)(void *param, obs_source_t *source, const struct audio_data *audio_data,\n\t\t\t\t\t   bool muted);\n\nEXPORT void obs_source_add_audio_pause_callback(obs_source_t *source, signal_callback_t callback, void *param);\nEXPORT void obs_source_remove_audio_pause_callback(obs_source_t *source, signal_callback_t callback, void *param);\nEXPORT void obs_source_add_audio_capture_callback(obs_source_t *source, obs_source_audio_capture_t callback,\n\t\t\t\t\t\t  void *param);\nEXPORT void obs_source_remove_audio_capture_callback(obs_source_t *source, obs_source_audio_capture_t callback,\n\t\t\t\t\t\t     void *param);\n\n/**\n * For an Audio Output Capture source (like 'wasapi_output_capture') used for 'Desktop Audio', this checks whether the\n * device is also used for monitoring. A signal to obs core struct is then emitted to trigger deduplication  logic at\n * the end of an audio tick.\n */\nEXPORT void obs_source_audio_output_capture_device_changed(obs_source_t *source, const char *device_id);\n\ntypedef void (*obs_source_caption_t)(void *param, obs_source_t *source, const struct obs_source_cea_708 *captions);\n\nEXPORT void obs_source_add_caption_callback(obs_source_t *source, obs_source_caption_t callback, void *param);\nEXPORT void obs_source_remove_caption_callback(obs_source_t *source, obs_source_caption_t callback, void *param);\n\nenum obs_deinterlace_mode {\n\tOBS_DEINTERLACE_MODE_DISABLE,\n\tOBS_DEINTERLACE_MODE_DISCARD,\n\tOBS_DEINTERLACE_MODE_RETRO,\n\tOBS_DEINTERLACE_MODE_BLEND,\n\tOBS_DEINTERLACE_MODE_BLEND_2X,\n\tOBS_DEINTERLACE_MODE_LINEAR,\n\tOBS_DEINTERLACE_MODE_LINEAR_2X,\n\tOBS_DEINTERLACE_MODE_YADIF,\n\tOBS_DEINTERLACE_MODE_YADIF_2X,\n};\n\nenum obs_deinterlace_field_order {\n\tOBS_DEINTERLACE_FIELD_ORDER_TOP,\n\tOBS_DEINTERLACE_FIELD_ORDER_BOTTOM,\n};\n\nEXPORT void obs_source_set_deinterlace_mode(obs_source_t *source, enum obs_deinterlace_mode mode);\nEXPORT enum obs_deinterlace_mode obs_source_get_deinterlace_mode(const obs_source_t *source);\nEXPORT void obs_source_set_deinterlace_field_order(obs_source_t *source, enum obs_deinterlace_field_order field_order);\nEXPORT enum obs_deinterlace_field_order obs_source_get_deinterlace_field_order(const obs_source_t *source);\n\nenum obs_monitoring_type {\n\tOBS_MONITORING_TYPE_NONE,\n\tOBS_MONITORING_TYPE_MONITOR_ONLY,\n\tOBS_MONITORING_TYPE_MONITOR_AND_OUTPUT,\n};\n\nEXPORT void obs_source_set_monitoring_type(obs_source_t *source, enum obs_monitoring_type type);\nEXPORT enum obs_monitoring_type obs_source_get_monitoring_type(const obs_source_t *source);\n\n/** Gets private front-end settings data.  This data is saved/loaded\n * automatically.  Returns an incremented reference. */\nEXPORT obs_data_t *obs_source_get_private_settings(obs_source_t *item);\n\nEXPORT obs_data_array_t *obs_source_backup_filters(obs_source_t *source);\nEXPORT void obs_source_restore_filters(obs_source_t *source, obs_data_array_t *array);\n\n/* ------------------------------------------------------------------------- */\n/* Functions used by sources */\n\nEXPORT void *obs_source_get_type_data(obs_source_t *source);\n\n/**\n * Helper function to set the color matrix information when drawing the source.\n *\n * @param  color_matrix     The color matrix.  Assigns to the 'color_matrix'\n *                          effect variable.\n * @param  color_range_min  The minimum color range.  Assigns to the\n *                          'color_range_min' effect variable.  If NULL,\n *                          {0.0f, 0.0f, 0.0f} is used.\n * @param  color_range_max  The maximum color range.  Assigns to the\n *                          'color_range_max' effect variable.  If NULL,\n *                          {1.0f, 1.0f, 1.0f} is used.\n */\nEXPORT void obs_source_draw_set_color_matrix(const struct matrix4 *color_matrix, const struct vec3 *color_range_min,\n\t\t\t\t\t     const struct vec3 *color_range_max);\n\n/**\n * Helper function to draw sprites for a source (synchronous video).\n *\n * @param  image   The sprite texture to draw.  Assigns to the 'image' variable\n *                 of the current effect.\n * @param  x       X position of the sprite.\n * @param  y       Y position of the sprite.\n * @param  cx      Width of the sprite.  If 0, uses the texture width.\n * @param  cy      Height of the sprite.  If 0, uses the texture height.\n * @param  flip    Specifies whether to flip the image vertically.\n */\nEXPORT void obs_source_draw(gs_texture_t *image, int x, int y, uint32_t cx, uint32_t cy, bool flip);\n\n/**\n * Outputs asynchronous video data.  Set to NULL to deactivate the texture\n *\n * NOTE: Non-YUV formats will always be treated as full range with this\n * function!  Use obs_source_output_video2 instead if partial range support is\n * desired for non-YUV video formats.\n */\nEXPORT void obs_source_output_video(obs_source_t *source, const struct obs_source_frame *frame);\nEXPORT void obs_source_output_video2(obs_source_t *source, const struct obs_source_frame2 *frame);\n\nEXPORT void obs_source_set_async_rotation(obs_source_t *source, long rotation);\n\nEXPORT void obs_source_output_cea708(obs_source_t *source, const struct obs_source_cea_708 *captions);\n\n/**\n * Preloads asynchronous video data to allow instantaneous playback\n *\n * NOTE: Non-YUV formats will always be treated as full range with this\n * function!  Use obs_source_preload_video2 instead if partial range support is\n * desired for non-YUV video formats.\n */\nEXPORT void obs_source_preload_video(obs_source_t *source, const struct obs_source_frame *frame);\nEXPORT void obs_source_preload_video2(obs_source_t *source, const struct obs_source_frame2 *frame);\n\n/** Shows any preloaded video data */\nEXPORT void obs_source_show_preloaded_video(obs_source_t *source);\n\n/**\n * Sets current async video frame immediately\n *\n * NOTE: Non-YUV formats will always be treated as full range with this\n * function!  Use obs_source_preload_video2 instead if partial range support is\n * desired for non-YUV video formats.\n */\nEXPORT void obs_source_set_video_frame(obs_source_t *source, const struct obs_source_frame *frame);\nEXPORT void obs_source_set_video_frame2(obs_source_t *source, const struct obs_source_frame2 *frame);\n\n/** Outputs audio data (always asynchronous) */\nEXPORT void obs_source_output_audio(obs_source_t *source, const struct obs_source_audio *audio);\n\n/** Signal an update to any currently used properties via 'update_properties' */\nEXPORT void obs_source_update_properties(obs_source_t *source);\n\n/** Gets the current async video frame */\nEXPORT struct obs_source_frame *obs_source_get_frame(obs_source_t *source);\n\n/** Releases the current async video frame */\nEXPORT void obs_source_release_frame(obs_source_t *source, struct obs_source_frame *frame);\n\n/**\n * Default RGB filter handler for generic effect filters.  Processes the\n * filter chain and renders them to texture if needed, then the filter is\n * drawn with\n *\n * After calling this, set your parameters for the effect, then call\n * obs_source_process_filter_end to draw the filter.\n *\n * Returns true if filtering should continue, false if the filter is bypassed\n * for whatever reason.\n */\nEXPORT bool obs_source_process_filter_begin(obs_source_t *filter, enum gs_color_format format,\n\t\t\t\t\t    enum obs_allow_direct_render allow_direct);\n\nEXPORT bool obs_source_process_filter_begin_with_color_space(obs_source_t *filter, enum gs_color_format format,\n\t\t\t\t\t\t\t     enum gs_color_space space,\n\t\t\t\t\t\t\t     enum obs_allow_direct_render allow_direct);\n\n/**\n * Draws the filter.\n *\n * Before calling this function, first call obs_source_process_filter_begin and\n * then set the effect parameters, and then call this function to finalize the\n * filter.\n */\nEXPORT void obs_source_process_filter_end(obs_source_t *filter, gs_effect_t *effect, uint32_t width, uint32_t height);\n\n/**\n * Draws the filter with a specific technique.\n *\n * Before calling this function, first call obs_source_process_filter_begin and\n * then set the effect parameters, and then call this function to finalize the\n * filter.\n */\nEXPORT void obs_source_process_filter_tech_end(obs_source_t *filter, gs_effect_t *effect, uint32_t width,\n\t\t\t\t\t       uint32_t height, const char *tech_name);\n\n/** Skips the filter if the filter is invalid and cannot be rendered */\nEXPORT void obs_source_skip_video_filter(obs_source_t *filter);\n\n/**\n * Adds an active child source.  Must be called by parent sources on child\n * sources when the child is added and active.  This ensures that the source is\n * properly activated if the parent is active.\n *\n * @returns true if source can be added, false if it causes recursion\n */\nEXPORT bool obs_source_add_active_child(obs_source_t *parent, obs_source_t *child);\n\n/**\n * Removes an active child source.  Must be called by parent sources on child\n * sources when the child is removed or inactive.  This ensures that the source\n * is properly deactivated if the parent is no longer active.\n */\nEXPORT void obs_source_remove_active_child(obs_source_t *parent, obs_source_t *child);\n\n/** Sends a mouse down/up event to a source */\nEXPORT void obs_source_send_mouse_click(obs_source_t *source, const struct obs_mouse_event *event, int32_t type,\n\t\t\t\t\tbool mouse_up, uint32_t click_count);\n\n/** Sends a mouse move event to a source. */\nEXPORT void obs_source_send_mouse_move(obs_source_t *source, const struct obs_mouse_event *event, bool mouse_leave);\n\n/** Sends a mouse wheel event to a source */\nEXPORT void obs_source_send_mouse_wheel(obs_source_t *source, const struct obs_mouse_event *event, int x_delta,\n\t\t\t\t\tint y_delta);\n\n/** Sends a got-focus or lost-focus event to a source */\nEXPORT void obs_source_send_focus(obs_source_t *source, bool focus);\n\n/** Sends a key up/down event to a source */\nEXPORT void obs_source_send_key_click(obs_source_t *source, const struct obs_key_event *event, bool key_up);\n\n/** Sets the default source flags. */\nEXPORT void obs_source_set_default_flags(obs_source_t *source, uint32_t flags);\n\n/** Gets the base width for a source (not taking in to account filtering) */\nEXPORT uint32_t obs_source_get_base_width(obs_source_t *source);\n\n/** Gets the base height for a source (not taking in to account filtering) */\nEXPORT uint32_t obs_source_get_base_height(obs_source_t *source);\n\nEXPORT bool obs_source_audio_pending(const obs_source_t *source);\nEXPORT uint64_t obs_source_get_audio_timestamp(const obs_source_t *source);\nEXPORT void obs_source_get_audio_mix(const obs_source_t *source, struct obs_source_audio_mix *audio);\n\nEXPORT void obs_source_set_async_unbuffered(obs_source_t *source, bool unbuffered);\nEXPORT bool obs_source_async_unbuffered(const obs_source_t *source);\n\n/** Used to decouple audio from video so that audio doesn't attempt to sync up\n * with video.  I.E. Audio acts independently.  Only works when in unbuffered\n * mode. */\nEXPORT void obs_source_set_async_decoupled(obs_source_t *source, bool decouple);\nEXPORT bool obs_source_async_decoupled(const obs_source_t *source);\n\nEXPORT void obs_source_set_audio_active(obs_source_t *source, bool show);\nEXPORT bool obs_source_audio_active(const obs_source_t *source);\n\nEXPORT uint32_t obs_source_get_last_obs_version(const obs_source_t *source);\n\n/** Media controls */\nEXPORT void obs_source_media_play_pause(obs_source_t *source, bool pause);\nEXPORT void obs_source_media_restart(obs_source_t *source);\nEXPORT void obs_source_media_stop(obs_source_t *source);\nEXPORT void obs_source_media_next(obs_source_t *source);\nEXPORT void obs_source_media_previous(obs_source_t *source);\nEXPORT int64_t obs_source_media_get_duration(obs_source_t *source);\nEXPORT int64_t obs_source_media_get_time(obs_source_t *source);\nEXPORT void obs_source_media_set_time(obs_source_t *source, int64_t ms);\nEXPORT enum obs_media_state obs_source_media_get_state(obs_source_t *source);\nEXPORT void obs_source_media_started(obs_source_t *source);\nEXPORT void obs_source_media_ended(obs_source_t *source);\n\n/** Get canvas this source belongs to (reference incremented) */\nEXPORT obs_canvas_t *obs_source_get_canvas(const obs_source_t *source);\n\n/* ------------------------------------------------------------------------- */\n/* Transition-specific functions */\nenum obs_transition_target {\n\tOBS_TRANSITION_SOURCE_A,\n\tOBS_TRANSITION_SOURCE_B,\n};\n\nEXPORT obs_source_t *obs_transition_get_source(obs_source_t *transition, enum obs_transition_target target);\nEXPORT void obs_transition_clear(obs_source_t *transition);\n\nEXPORT obs_source_t *obs_transition_get_active_source(obs_source_t *transition);\n\nenum obs_transition_mode {\n\tOBS_TRANSITION_MODE_AUTO,\n\tOBS_TRANSITION_MODE_MANUAL,\n};\n\nEXPORT bool obs_transition_start(obs_source_t *transition, enum obs_transition_mode mode, uint32_t duration_ms,\n\t\t\t\t obs_source_t *dest);\n\nEXPORT void obs_transition_set(obs_source_t *transition, obs_source_t *source);\n\nEXPORT void obs_transition_set_manual_time(obs_source_t *transition, float t);\nEXPORT void obs_transition_set_manual_torque(obs_source_t *transition, float torque, float clamp);\n\nenum obs_transition_scale_type {\n\tOBS_TRANSITION_SCALE_MAX_ONLY,\n\tOBS_TRANSITION_SCALE_ASPECT,\n\tOBS_TRANSITION_SCALE_STRETCH,\n};\n\nEXPORT void obs_transition_set_scale_type(obs_source_t *transition, enum obs_transition_scale_type type);\nEXPORT enum obs_transition_scale_type obs_transition_get_scale_type(const obs_source_t *transition);\n\nEXPORT void obs_transition_set_alignment(obs_source_t *transition, uint32_t alignment);\nEXPORT uint32_t obs_transition_get_alignment(const obs_source_t *transition);\n\nEXPORT void obs_transition_set_size(obs_source_t *transition, uint32_t cx, uint32_t cy);\nEXPORT void obs_transition_get_size(const obs_source_t *transition, uint32_t *cx, uint32_t *cy);\n\nEXPORT bool obs_transition_is_active(obs_source_t *transition);\n\n/* function used by transitions */\n\n/**\n * Enables fixed transitions (videos or specific types of transitions that\n * are of fixed duration and linearly interpolated\n */\nEXPORT void obs_transition_enable_fixed(obs_source_t *transition, bool enable, uint32_t duration_ms);\nEXPORT bool obs_transition_fixed(obs_source_t *transition);\n\ntypedef void (*obs_transition_video_render_callback_t)(void *data, gs_texture_t *a, gs_texture_t *b, float t,\n\t\t\t\t\t\t       uint32_t cx, uint32_t cy);\ntypedef float (*obs_transition_audio_mix_callback_t)(void *data, float t);\n\nEXPORT float obs_transition_get_time(obs_source_t *transition);\n\nEXPORT void obs_transition_force_stop(obs_source_t *transition);\n\nEXPORT void obs_transition_video_render(obs_source_t *transition, obs_transition_video_render_callback_t callback);\n\nEXPORT void obs_transition_video_render2(obs_source_t *transition, obs_transition_video_render_callback_t callback,\n\t\t\t\t\t gs_texture_t *placeholder_texture);\n\nEXPORT enum gs_color_space obs_transition_video_get_color_space(obs_source_t *transition);\n\n/** Directly renders its sub-source instead of to texture.  Returns false if no\n * longer transitioning */\nEXPORT bool obs_transition_video_render_direct(obs_source_t *transition, enum obs_transition_target target);\n\nEXPORT bool obs_transition_audio_render(obs_source_t *transition, uint64_t *ts_out, struct obs_source_audio_mix *audio,\n\t\t\t\t\tuint32_t mixers, size_t channels, size_t sample_rate,\n\t\t\t\t\tobs_transition_audio_mix_callback_t mix_a_callback,\n\t\t\t\t\tobs_transition_audio_mix_callback_t mix_b_callback);\n\n/* swaps transition sources and textures as an optimization and to reduce\n * memory usage when switching between transitions */\nEXPORT void obs_transition_swap_begin(obs_source_t *tr_dest, obs_source_t *tr_source);\nEXPORT void obs_transition_swap_end(obs_source_t *tr_dest, obs_source_t *tr_source);\n\n/* ------------------------------------------------------------------------- */\n/* Scenes */\n\n/**\n * Creates a scene.\n *\n *   A scene is a source which is a container of other sources with specific\n * display orientations.  Scenes can also be used like any other source.\n */\nEXPORT obs_scene_t *obs_scene_create(const char *name);\n\nEXPORT obs_scene_t *obs_scene_create_private(const char *name);\n\nenum obs_scene_duplicate_type {\n\tOBS_SCENE_DUP_REFS,         /**< Source refs only */\n\tOBS_SCENE_DUP_COPY,         /**< Fully duplicate */\n\tOBS_SCENE_DUP_PRIVATE_REFS, /**< Source refs only (as private) */\n\tOBS_SCENE_DUP_PRIVATE_COPY, /**< Fully duplicate (as private) */\n};\n\n/**\n * Duplicates a scene.\n */\nEXPORT obs_scene_t *obs_scene_duplicate(obs_scene_t *scene, const char *name, enum obs_scene_duplicate_type type);\n\nEXPORT void obs_scene_release(obs_scene_t *scene);\n\nEXPORT obs_scene_t *obs_scene_get_ref(obs_scene_t *scene);\n\n/** Gets the scene's source context */\nEXPORT obs_source_t *obs_scene_get_source(const obs_scene_t *scene);\n\n/** Gets the scene from its source, or NULL if not a scene */\nEXPORT obs_scene_t *obs_scene_from_source(const obs_source_t *source);\n\n/** Determines whether a source is within a scene */\nEXPORT obs_sceneitem_t *obs_scene_find_source(obs_scene_t *scene, const char *name);\n\nEXPORT obs_sceneitem_t *obs_scene_find_source_recursive(obs_scene_t *scene, const char *name);\n\nEXPORT obs_sceneitem_t *obs_scene_find_sceneitem_by_id(obs_scene_t *scene, int64_t id);\n\n/** Gets scene by name, increments the reference */\nstatic inline obs_scene_t *obs_get_scene_by_name(const char *name)\n{\n\tobs_source_t *source = obs_get_source_by_name(name);\n\tobs_scene_t *scene = obs_scene_from_source(source);\n\tif (!scene) {\n\t\tobs_source_release(source);\n\t\treturn NULL;\n\t}\n\treturn scene;\n}\n\n/** Enumerates sources within a scene */\nEXPORT void obs_scene_enum_items(obs_scene_t *scene, bool (*callback)(obs_scene_t *, obs_sceneitem_t *, void *),\n\t\t\t\t void *param);\n\nEXPORT bool obs_scene_reorder_items(obs_scene_t *scene, obs_sceneitem_t *const *item_order, size_t item_order_size);\n\nstruct obs_sceneitem_order_info {\n\tobs_sceneitem_t *group;\n\tobs_sceneitem_t *item;\n};\n\nEXPORT bool obs_scene_reorder_items2(obs_scene_t *scene, struct obs_sceneitem_order_info *item_order,\n\t\t\t\t     size_t item_order_size);\n\nEXPORT bool obs_source_is_scene(const obs_source_t *source);\nEXPORT bool obs_source_type_is_scene(const char *id);\n\n/** Adds/creates a new scene item for a source */\nEXPORT obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source);\n\ntypedef void (*obs_scene_atomic_update_func)(void *, obs_scene_t *scene);\nEXPORT void obs_scene_atomic_update(obs_scene_t *scene, obs_scene_atomic_update_func func, void *data);\n\nEXPORT void obs_sceneitem_addref(obs_sceneitem_t *item);\nEXPORT void obs_sceneitem_release(obs_sceneitem_t *item);\n\n/** Removes a scene item. */\nEXPORT void obs_sceneitem_remove(obs_sceneitem_t *item);\n\n/** Adds a scene item. */\nEXPORT void obs_sceneitems_add(obs_scene_t *scene, obs_data_array_t *data);\n\n/** Saves Sceneitem into an array, arr **/\nEXPORT void obs_sceneitem_save(obs_sceneitem_t *item, obs_data_array_t *arr);\n\n/** Set the ID of a sceneitem */\nEXPORT void obs_sceneitem_set_id(obs_sceneitem_t *sceneitem, int64_t id);\n\n/** Save all the transform states for a current scene's sceneitems */\nEXPORT obs_data_t *obs_scene_save_transform_states(obs_scene_t *scene, bool all_items);\n\n/** Load all the transform states of sceneitems in that scene */\nEXPORT void obs_scene_load_transform_states(const char *state);\n\n/**  Gets a sceneitem's order in its scene */\nEXPORT int obs_sceneitem_get_order_position(obs_sceneitem_t *item);\n\n/** Gets the scene parent associated with the scene item. */\nEXPORT obs_scene_t *obs_sceneitem_get_scene(const obs_sceneitem_t *item);\n\n/** Gets the source of a scene item. */\nEXPORT obs_source_t *obs_sceneitem_get_source(const obs_sceneitem_t *item);\n\n/* FIXME: The following functions should be deprecated and replaced with a way\n * to specify saveable private user data. -Lain */\nEXPORT void obs_sceneitem_select(obs_sceneitem_t *item, bool select);\nEXPORT bool obs_sceneitem_selected(const obs_sceneitem_t *item);\nEXPORT bool obs_sceneitem_locked(const obs_sceneitem_t *item);\nEXPORT bool obs_sceneitem_set_locked(obs_sceneitem_t *item, bool lock);\n\n/* Functions for getting/setting specific orientation of a scene item */\nEXPORT void obs_sceneitem_set_pos(obs_sceneitem_t *item, const struct vec2 *pos);\nEXPORT void obs_sceneitem_set_rot(obs_sceneitem_t *item, float rot_deg);\nEXPORT void obs_sceneitem_set_scale(obs_sceneitem_t *item, const struct vec2 *scale);\nEXPORT void obs_sceneitem_set_alignment(obs_sceneitem_t *item, uint32_t alignment);\nEXPORT void obs_sceneitem_set_order(obs_sceneitem_t *item, enum obs_order_movement movement);\nEXPORT void obs_sceneitem_set_order_position(obs_sceneitem_t *item, int position);\nEXPORT void obs_sceneitem_set_bounds_type(obs_sceneitem_t *item, enum obs_bounds_type type);\nEXPORT void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item, uint32_t alignment);\nEXPORT void obs_sceneitem_set_bounds_crop(obs_sceneitem_t *item, bool crop);\nEXPORT void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds);\n\nEXPORT int64_t obs_sceneitem_get_id(const obs_sceneitem_t *item);\n\nEXPORT void obs_sceneitem_get_pos(const obs_sceneitem_t *item, struct vec2 *pos);\nEXPORT float obs_sceneitem_get_rot(const obs_sceneitem_t *item);\nEXPORT void obs_sceneitem_get_scale(const obs_sceneitem_t *item, struct vec2 *scale);\nEXPORT uint32_t obs_sceneitem_get_alignment(const obs_sceneitem_t *item);\n\nEXPORT enum obs_bounds_type obs_sceneitem_get_bounds_type(const obs_sceneitem_t *item);\nEXPORT uint32_t obs_sceneitem_get_bounds_alignment(const obs_sceneitem_t *item);\nEXPORT bool obs_sceneitem_get_bounds_crop(const obs_sceneitem_t *item);\nEXPORT void obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds);\n\nEXPORT void obs_sceneitem_get_info2(const obs_sceneitem_t *item, struct obs_transform_info *info);\nEXPORT void obs_sceneitem_set_info2(obs_sceneitem_t *item, const struct obs_transform_info *info);\n\nEXPORT void obs_sceneitem_get_draw_transform(const obs_sceneitem_t *item, struct matrix4 *transform);\nEXPORT void obs_sceneitem_get_box_transform(const obs_sceneitem_t *item, struct matrix4 *transform);\nEXPORT void obs_sceneitem_get_box_scale(const obs_sceneitem_t *item, struct vec2 *scale);\n\nEXPORT bool obs_sceneitem_visible(const obs_sceneitem_t *item);\nEXPORT bool obs_sceneitem_set_visible(obs_sceneitem_t *item, bool visible);\n\nstruct obs_sceneitem_crop {\n\tint left;\n\tint top;\n\tint right;\n\tint bottom;\n};\n\nEXPORT void obs_sceneitem_set_crop(obs_sceneitem_t *item, const struct obs_sceneitem_crop *crop);\nEXPORT void obs_sceneitem_get_crop(const obs_sceneitem_t *item, struct obs_sceneitem_crop *crop);\n\nEXPORT void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item, enum obs_scale_type filter);\nEXPORT enum obs_scale_type obs_sceneitem_get_scale_filter(obs_sceneitem_t *item);\n\nEXPORT void obs_sceneitem_set_blending_method(obs_sceneitem_t *item, enum obs_blending_method method);\nEXPORT enum obs_blending_method obs_sceneitem_get_blending_method(obs_sceneitem_t *item);\n\nEXPORT void obs_sceneitem_set_blending_mode(obs_sceneitem_t *item, enum obs_blending_type type);\nEXPORT enum obs_blending_type obs_sceneitem_get_blending_mode(obs_sceneitem_t *item);\n\nEXPORT void obs_sceneitem_force_update_transform(obs_sceneitem_t *item);\n\nEXPORT void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item);\nEXPORT void obs_sceneitem_defer_update_end(obs_sceneitem_t *item);\n\n/** Gets private front-end settings data.  This data is saved/loaded\n * automatically.  Returns an incremented reference. */\nEXPORT obs_data_t *obs_sceneitem_get_private_settings(obs_sceneitem_t *item);\n\nEXPORT obs_sceneitem_t *obs_scene_add_group(obs_scene_t *scene, const char *name);\nEXPORT obs_sceneitem_t *obs_scene_insert_group(obs_scene_t *scene, const char *name, obs_sceneitem_t **items,\n\t\t\t\t\t       size_t count);\n\nEXPORT obs_sceneitem_t *obs_scene_add_group2(obs_scene_t *scene, const char *name, bool signal);\nEXPORT obs_sceneitem_t *obs_scene_insert_group2(obs_scene_t *scene, const char *name, obs_sceneitem_t **items,\n\t\t\t\t\t\tsize_t count, bool signal);\n\nEXPORT obs_sceneitem_t *obs_scene_get_group(obs_scene_t *scene, const char *name);\n\nEXPORT bool obs_sceneitem_is_group(obs_sceneitem_t *item);\n\nEXPORT obs_scene_t *obs_sceneitem_group_get_scene(const obs_sceneitem_t *group);\n\nEXPORT void obs_sceneitem_group_ungroup(obs_sceneitem_t *group);\nEXPORT void obs_sceneitem_group_ungroup2(obs_sceneitem_t *group, bool signal);\n\nEXPORT void obs_sceneitem_group_add_item(obs_sceneitem_t *group, obs_sceneitem_t *item);\nEXPORT void obs_sceneitem_group_remove_item(obs_sceneitem_t *group, obs_sceneitem_t *item);\n\nEXPORT obs_sceneitem_t *obs_sceneitem_get_group(obs_scene_t *scene, obs_sceneitem_t *item);\n\nEXPORT bool obs_source_is_group(const obs_source_t *source);\nEXPORT bool obs_source_type_is_group(const char *id);\nEXPORT bool obs_scene_is_group(const obs_scene_t *scene);\n\nEXPORT void obs_sceneitem_group_enum_items(obs_sceneitem_t *group,\n\t\t\t\t\t   bool (*callback)(obs_scene_t *, obs_sceneitem_t *, void *), void *param);\n\n/** Gets the group from its source, or NULL if not a group */\nEXPORT obs_scene_t *obs_group_from_source(const obs_source_t *source);\n\nstatic inline obs_scene_t *obs_group_or_scene_from_source(const obs_source_t *source)\n{\n\tobs_scene_t *s = obs_scene_from_source(source);\n\treturn s ? s : obs_group_from_source(source);\n}\n\nEXPORT void obs_sceneitem_defer_group_resize_begin(obs_sceneitem_t *item);\nEXPORT void obs_sceneitem_defer_group_resize_end(obs_sceneitem_t *item);\n\nEXPORT void obs_sceneitem_set_transition(obs_sceneitem_t *item, bool show, obs_source_t *transition);\nEXPORT obs_source_t *obs_sceneitem_get_transition(obs_sceneitem_t *item, bool show);\nEXPORT void obs_sceneitem_set_transition_duration(obs_sceneitem_t *item, bool show, uint32_t duration_ms);\nEXPORT uint32_t obs_sceneitem_get_transition_duration(obs_sceneitem_t *item, bool show);\nEXPORT void obs_sceneitem_do_transition(obs_sceneitem_t *item, bool visible);\nEXPORT void obs_sceneitem_transition_load(struct obs_scene_item *item, obs_data_t *data, bool show);\nEXPORT obs_data_t *obs_sceneitem_transition_save(struct obs_scene_item *item, bool show);\nEXPORT void obs_scene_prune_sources(obs_scene_t *scene);\n\n/* ------------------------------------------------------------------------- */\n/* Outputs */\n\nEXPORT const char *obs_output_get_display_name(const char *id);\n\n/** Returns a pointer to the module which provides the output */\nEXPORT obs_module_t *obs_output_get_module(const char *id);\n\n/** Returns the load state of a output's module given the id */\nEXPORT enum obs_module_load_state obs_output_load_state(const char *id);\n\n/**\n * Creates an output.\n *\n *   Outputs allow outputting to file, outputting to network, outputting to\n * directshow, or other custom outputs.\n */\nEXPORT obs_output_t *obs_output_create(const char *id, const char *name, obs_data_t *settings, obs_data_t *hotkey_data);\n\n/**\n * Adds/releases a reference to an output.  When the last reference is\n * released, the output is destroyed.\n */\nEXPORT void obs_output_release(obs_output_t *output);\n\nEXPORT void obs_weak_output_addref(obs_weak_output_t *weak);\nEXPORT void obs_weak_output_release(obs_weak_output_t *weak);\n\nEXPORT obs_output_t *obs_output_get_ref(obs_output_t *output);\nEXPORT obs_weak_output_t *obs_output_get_weak_output(obs_output_t *output);\nEXPORT obs_output_t *obs_weak_output_get_output(obs_weak_output_t *weak);\n\nEXPORT bool obs_weak_output_references_output(obs_weak_output_t *weak, obs_output_t *output);\n\nEXPORT const char *obs_output_get_name(const obs_output_t *output);\n\n/** Starts the output. */\nEXPORT bool obs_output_start(obs_output_t *output);\n\n/** Stops the output. */\nEXPORT void obs_output_stop(obs_output_t *output);\n\n/**\n * On reconnection, start where it left of on reconnection.  Note however that\n * this option will consume extra memory to continually increase delay while\n * waiting to reconnect.\n */\n#define OBS_OUTPUT_DELAY_PRESERVE (1 << 0)\n\n/**\n * Sets the current output delay, in seconds (if the output supports delay).\n *\n * If delay is currently active, it will set the delay value, but will not\n * affect the current delay, it will only affect the next time the output is\n * activated.\n */\nEXPORT void obs_output_set_delay(obs_output_t *output, uint32_t delay_sec, uint32_t flags);\n\n/** Gets the currently set delay value, in seconds. */\nEXPORT uint32_t obs_output_get_delay(const obs_output_t *output);\n\n/** If delay is active, gets the currently active delay value, in seconds. */\nEXPORT uint32_t obs_output_get_active_delay(const obs_output_t *output);\n\n/** Forces the output to stop.  Usually only used with delay. */\nEXPORT void obs_output_force_stop(obs_output_t *output);\n\n/** Returns whether the output is active */\nEXPORT bool obs_output_active(const obs_output_t *output);\n\n/** Returns output capability flags */\nEXPORT uint32_t obs_output_get_flags(const obs_output_t *output);\n\n/** Returns output capability flags */\nEXPORT uint32_t obs_get_output_flags(const char *id);\n\n/** Gets the default settings for an output type */\nEXPORT obs_data_t *obs_output_defaults(const char *id);\n\n/** Returns the property list, if any.  Free with obs_properties_destroy */\nEXPORT obs_properties_t *obs_get_output_properties(const char *id);\n\n/**\n * Returns the property list of an existing output, if any.  Free with\n * obs_properties_destroy\n */\nEXPORT obs_properties_t *obs_output_properties(const obs_output_t *output);\n\n/** Updates the settings for this output context */\nEXPORT void obs_output_update(obs_output_t *output, obs_data_t *settings);\n\n/** Specifies whether the output can be paused */\nEXPORT bool obs_output_can_pause(const obs_output_t *output);\n\n/** Pauses the output (if the functionality is allowed by the output */\nEXPORT bool obs_output_pause(obs_output_t *output, bool pause);\n\n/** Returns whether output is paused */\nEXPORT bool obs_output_paused(const obs_output_t *output);\n\n/* Gets the current output settings string */\nEXPORT obs_data_t *obs_output_get_settings(const obs_output_t *output);\n\n/** Returns the signal handler for an output  */\nEXPORT signal_handler_t *obs_output_get_signal_handler(const obs_output_t *output);\n\n/** Returns the procedure handler for an output */\nEXPORT proc_handler_t *obs_output_get_proc_handler(const obs_output_t *output);\n\n/**\n * Sets the current audio/video media contexts associated with this output,\n * required for non-encoded outputs.  Can be null.\n */\nEXPORT void obs_output_set_media(obs_output_t *output, video_t *video, audio_t *audio);\n\n/** Returns the video media context associated with this output */\nEXPORT video_t *obs_output_video(const obs_output_t *output);\n\n/** Returns the audio media context associated with this output */\nEXPORT audio_t *obs_output_audio(const obs_output_t *output);\n\n/** Sets the current audio mixer for non-encoded outputs */\nEXPORT void obs_output_set_mixer(obs_output_t *output, size_t mixer_idx);\n\n/** Gets the current audio mixer for non-encoded outputs */\nEXPORT size_t obs_output_get_mixer(const obs_output_t *output);\n\n/** Sets the current audio mixes (mask) for a non-encoded multi-track output */\nEXPORT void obs_output_set_mixers(obs_output_t *output, size_t mixers);\n\n/** Gets the current audio mixes (mask) for a non-encoded multi-track output */\nEXPORT size_t obs_output_get_mixers(const obs_output_t *output);\n\n/**\n * Sets the current video encoder associated with this output,\n * required for encoded outputs\n */\nEXPORT void obs_output_set_video_encoder(obs_output_t *output, obs_encoder_t *encoder);\n\n/**\n * Sets the current video encoder associated with this output,\n * required for encoded outputs.\n *\n * The idx parameter specifies the video encoder index.\n * Only used with outputs that have multiple video outputs (FFmpeg typically),\n * otherwise the parameter is ignored.\n */\nEXPORT void obs_output_set_video_encoder2(obs_output_t *output, obs_encoder_t *encoder, size_t idx);\n\n/**\n * Sets the current audio encoder associated with this output,\n * required for encoded outputs.\n *\n * The idx parameter specifies the audio encoder index to set the encoder to.\n * Only used with outputs that have multiple audio outputs (RTMP typically),\n * otherwise the parameter is ignored.\n */\nEXPORT void obs_output_set_audio_encoder(obs_output_t *output, obs_encoder_t *encoder, size_t idx);\n\n/** Returns the current video encoder associated with this output */\nEXPORT obs_encoder_t *obs_output_get_video_encoder(const obs_output_t *output);\n\n/**\n * Returns the current video encoder associated with this output.\n *\n * The idx parameter specifies the video encoder index.\n * Only used with outputs that have multiple video outputs (FFmpeg typically),\n * otherwise specifying an idx > 0 returns a NULL.\n * */\nEXPORT obs_encoder_t *obs_output_get_video_encoder2(const obs_output_t *output, size_t idx);\n\n/**\n * Returns the current audio encoder associated with this output\n *\n * The idx parameter specifies the audio encoder index.  Only used with\n * outputs that have multiple audio outputs, otherwise the parameter is\n * ignored.\n */\nEXPORT obs_encoder_t *obs_output_get_audio_encoder(const obs_output_t *output, size_t idx);\n\n/** Sets the current service associated with this output. */\nEXPORT void obs_output_set_service(obs_output_t *output, obs_service_t *service);\n\n/** Gets the current service associated with this output. */\nEXPORT obs_service_t *obs_output_get_service(const obs_output_t *output);\n\n/**\n * Sets the reconnect settings.  Set retry_count to 0 to disable reconnecting.\n */\nEXPORT void obs_output_set_reconnect_settings(obs_output_t *output, int retry_count, int retry_sec);\n\nEXPORT uint64_t obs_output_get_total_bytes(const obs_output_t *output);\nEXPORT int obs_output_get_frames_dropped(const obs_output_t *output);\nEXPORT int obs_output_get_total_frames(const obs_output_t *output);\n\n/**\n * Sets the preferred scaled resolution for this output.  Set width and height\n * to 0 to disable scaling.\n *\n * If this output uses an encoder, it will call obs_encoder_set_scaled_size on\n * the encoder before the stream is started.  If the encoder is already active,\n * then this function will trigger a warning and do nothing.\n */\nEXPORT void obs_output_set_preferred_size(obs_output_t *output, uint32_t width, uint32_t height);\n\n/**\n * Sets the preferred scaled resolution for this output.  Set width and height\n * to 0 to disable scaling.\n *\n * If this output uses an encoder, it will call obs_encoder_set_scaled_size on\n * the encoder before the stream is started.  If the encoder is already active,\n * then this function will trigger a warning and do nothing.\n *\n * The idx parameter specifies the video encoder index to apply the scaling to.\n * Only used with outputs that have multiple video outputs (FFmpeg typically),\n * otherwise the parameter is ignored.\n */\nEXPORT void obs_output_set_preferred_size2(obs_output_t *output, uint32_t width, uint32_t height, size_t idx);\n\n/** For video outputs, returns the width of the encoded image */\nEXPORT uint32_t obs_output_get_width(const obs_output_t *output);\n\n/**\n * For video outputs, returns the width of the encoded image.\n *\n * The idx parameter specifies the video encoder index.\n * Only used with outputs that have multiple video outputs (FFmpeg typically),\n * otherwise the parameter is ignored and returns 0.\n */\nEXPORT uint32_t obs_output_get_width2(const obs_output_t *output, size_t idx);\n\n/** For video outputs, returns the height of the encoded image */\nEXPORT uint32_t obs_output_get_height(const obs_output_t *output);\n\n/**\n * For video outputs, returns the height of the encoded image.\n *\n * The idx parameter specifies the video encoder index.\n * Only used with outputs that have multiple video outputs (FFmpeg typically),\n * otherwise the parameter is ignored and returns 0.\n */\nEXPORT uint32_t obs_output_get_height2(const obs_output_t *output, size_t idx);\n\nEXPORT const char *obs_output_get_id(const obs_output_t *output);\n\nEXPORT void obs_output_caption(obs_output_t *output, const struct obs_source_cea_708 *captions);\n\nEXPORT void obs_output_output_caption_text1(obs_output_t *output, const char *text);\nEXPORT void obs_output_output_caption_text2(obs_output_t *output, const char *text, double display_duration);\n\nEXPORT float obs_output_get_congestion(obs_output_t *output);\nEXPORT int obs_output_get_connect_time_ms(obs_output_t *output);\n\nEXPORT bool obs_output_reconnecting(const obs_output_t *output);\n\n/** Pass a string of the last output error, for UI use */\nEXPORT void obs_output_set_last_error(obs_output_t *output, const char *message);\nEXPORT const char *obs_output_get_last_error(obs_output_t *output);\n\nEXPORT const char *obs_output_get_supported_video_codecs(const obs_output_t *output);\nEXPORT const char *obs_output_get_supported_audio_codecs(const obs_output_t *output);\n\nEXPORT const char *obs_output_get_protocols(const obs_output_t *output);\n\nEXPORT bool obs_is_output_protocol_registered(const char *protocol);\n\nEXPORT bool obs_enum_output_protocols(size_t idx, char **protocol);\n\nEXPORT void obs_enum_output_types_with_protocol(const char *protocol, void *data,\n\t\t\t\t\t\tbool (*enum_cb)(void *data, const char *id));\n\nEXPORT const char *obs_get_output_supported_video_codecs(const char *id);\n\nEXPORT const char *obs_get_output_supported_audio_codecs(const char *id);\n\n/* Add/remove packet-processing callbacks that are invoked in\n * send_interleaved(), before forwarding packets to the output service.\n * This provides a mechanism to perform packet processing outside of\n * libobs, however any callback function registering with this API should keep\n * keep code to a minimum and understand it is running synchronously with the\n * calling thread.\n */\nEXPORT void obs_output_add_packet_callback(obs_output_t *output,\n\t\t\t\t\t   void (*packet_cb)(obs_output_t *output, struct encoder_packet *pkt,\n\t\t\t\t\t\t\t     struct encoder_packet_time *pkt_time, void *param),\n\t\t\t\t\t   void *param);\nEXPORT void obs_output_remove_packet_callback(obs_output_t *output,\n\t\t\t\t\t      void (*packet_cb)(obs_output_t *output, struct encoder_packet *pkt,\n\t\t\t\t\t\t\t\tstruct encoder_packet_time *pkt_time, void *param),\n\t\t\t\t\t      void *param);\n\n/* Sets a callback to be called when the output checks if it should attempt to reconnect.\n * If the callback returns false, the output will not attempt to reconnect. */\nEXPORT void obs_output_set_reconnect_callback(obs_output_t *output,\n\t\t\t\t\t      bool (*reconnect_cb)(void *data, obs_output_t *output, int code),\n\t\t\t\t\t      void *param);\n\n/* ------------------------------------------------------------------------- */\n/* Functions used by outputs */\n\nEXPORT void *obs_output_get_type_data(obs_output_t *output);\n\n/** Gets the video conversion info.  Used only for raw output */\nEXPORT const struct video_scale_info *obs_output_get_video_conversion(obs_output_t *output);\n\n/** Optionally sets the video conversion info.  Used only for raw output */\nEXPORT void obs_output_set_video_conversion(obs_output_t *output, const struct video_scale_info *conversion);\n\n/** Optionally sets the audio conversion info.  Used only for raw output */\nEXPORT void obs_output_set_audio_conversion(obs_output_t *output, const struct audio_convert_info *conversion);\n\n/** Returns whether data capture can begin  */\nEXPORT bool obs_output_can_begin_data_capture(const obs_output_t *output, uint32_t flags);\n\n/** Initializes encoders (if any) */\nEXPORT bool obs_output_initialize_encoders(obs_output_t *output, uint32_t flags);\n\n/**\n * Begins data capture from media/encoders.\n *\n * @param  output  Output context\n * @return         true if successful, false otherwise.\n */\nEXPORT bool obs_output_begin_data_capture(obs_output_t *output, uint32_t flags);\n\n/** Ends data capture from media/encoders */\nEXPORT void obs_output_end_data_capture(obs_output_t *output);\n\n/**\n * Signals that the output has stopped itself.\n *\n * @param  output  Output context\n * @param  code    Error code (or OBS_OUTPUT_SUCCESS if not an error)\n */\nEXPORT void obs_output_signal_stop(obs_output_t *output, int code);\n\nEXPORT uint64_t obs_output_get_pause_offset(obs_output_t *output);\n\n/* ------------------------------------------------------------------------- */\n/* Encoders */\n\nEXPORT const char *obs_encoder_get_display_name(const char *id);\n\n/** Returns a pointer to the module which provides the encoder */\nEXPORT obs_module_t *obs_encoder_get_module(const char *id);\n\n/** Returns the load state of an encoder's module given the id */\nEXPORT enum obs_module_load_state obs_encoder_load_state(const char *id);\n\n/**\n * Creates a video encoder context\n *\n * @param  id        Video encoder ID\n * @param  name      Name to assign to this context\n * @param  settings  Settings\n * @return           The video encoder context, or NULL if failed or not found.\n */\nEXPORT obs_encoder_t *obs_video_encoder_create(const char *id, const char *name, obs_data_t *settings,\n\t\t\t\t\t       obs_data_t *hotkey_data);\n\n/**\n * Creates an audio encoder context\n *\n * @param  id        Audio Encoder ID\n * @param  name      Name to assign to this context\n * @param  settings  Settings\n * @param  mixer_idx Index of the mixer to use for this audio encoder\n * @return           The video encoder context, or NULL if failed or not found.\n */\nEXPORT obs_encoder_t *obs_audio_encoder_create(const char *id, const char *name, obs_data_t *settings, size_t mixer_idx,\n\t\t\t\t\t       obs_data_t *hotkey_data);\n\n/**\n * Adds/releases a reference to an encoder.  When the last reference is\n * released, the encoder is destroyed.\n */\nEXPORT void obs_encoder_release(obs_encoder_t *encoder);\n\nEXPORT void obs_weak_encoder_addref(obs_weak_encoder_t *weak);\nEXPORT void obs_weak_encoder_release(obs_weak_encoder_t *weak);\n\nEXPORT obs_encoder_t *obs_encoder_get_ref(obs_encoder_t *encoder);\nEXPORT obs_weak_encoder_t *obs_encoder_get_weak_encoder(obs_encoder_t *encoder);\nEXPORT obs_encoder_t *obs_weak_encoder_get_encoder(obs_weak_encoder_t *weak);\n\nEXPORT bool obs_weak_encoder_references_encoder(obs_weak_encoder_t *weak, obs_encoder_t *encoder);\n\nEXPORT void obs_encoder_set_name(obs_encoder_t *encoder, const char *name);\nEXPORT const char *obs_encoder_get_name(const obs_encoder_t *encoder);\n\n/** Returns the codec of an encoder by the id */\nEXPORT const char *obs_get_encoder_codec(const char *id);\n\n/** Returns the type of an encoder by the id */\nEXPORT enum obs_encoder_type obs_get_encoder_type(const char *id);\n\n/** Returns the codec of the encoder */\nEXPORT const char *obs_encoder_get_codec(const obs_encoder_t *encoder);\n\n/** Returns the type of an encoder */\nEXPORT enum obs_encoder_type obs_encoder_get_type(const obs_encoder_t *encoder);\n\n/**\n * Sets the scaled resolution for a video encoder.  Set width and height to 0\n * to disable scaling.  If the encoder is active, this function will trigger\n * a warning, and do nothing.\n */\nEXPORT void obs_encoder_set_scaled_size(obs_encoder_t *encoder, uint32_t width, uint32_t height);\n\n/**\n * Enable/disable GPU based scaling for a video encoder.\n * OBS_SCALE_DISABLE disables GPU based scaling (default),\n * any other value enables GPU based scaling. If the encoder\n * is active, this function will trigger a warning, and do nothing.\n */\nEXPORT void obs_encoder_set_gpu_scale_type(obs_encoder_t *encoder, enum obs_scale_type gpu_scale_type);\n\n/**\n * Set frame rate divisor for a video encoder. This allows recording at\n * a partial frame rate compared to the base frame rate, e.g. 60 FPS with\n * divisor = 2 will record at 30 FPS, with divisor = 3 at 20, etc.\n *\n * Can only be called on stopped encoders, changing this on the fly is not supported\n */\nEXPORT bool obs_encoder_set_frame_rate_divisor(obs_encoder_t *encoder, uint32_t divisor);\n\n/**\n * Adds region of interest (ROI) for an encoder. This allows prioritizing\n * quality of regions of the frame.\n * If regions overlap, regions added earlier take precedence.\n *\n * Returns false if the encoder does not support ROI or region is invalid.\n */\nEXPORT bool obs_encoder_add_roi(obs_encoder_t *encoder, const struct obs_encoder_roi *roi);\n/** For video encoders, returns true if any ROIs were set */\nEXPORT bool obs_encoder_has_roi(const obs_encoder_t *encoder);\n/** Clear all regions */\nEXPORT void obs_encoder_clear_roi(obs_encoder_t *encoder);\n/** Enumerate regions with callback (reverse order of addition) */\nEXPORT void obs_encoder_enum_roi(obs_encoder_t *encoder, void (*enum_proc)(void *, struct obs_encoder_roi *),\n\t\t\t\t void *param);\n/** Get ROI increment, encoders must rebuild their ROI map if it has changed */\nEXPORT uint32_t obs_encoder_get_roi_increment(const obs_encoder_t *encoder);\n\n/** For video encoders, returns true if pre-encode scaling is enabled */\nEXPORT bool obs_encoder_scaling_enabled(const obs_encoder_t *encoder);\n\n/** For video encoders, returns the width of the encoded image */\nEXPORT uint32_t obs_encoder_get_width(const obs_encoder_t *encoder);\n\n/** For video encoders, returns the height of the encoded image */\nEXPORT uint32_t obs_encoder_get_height(const obs_encoder_t *encoder);\n\n/** For video encoders, returns whether GPU scaling is enabled */\nEXPORT bool obs_encoder_gpu_scaling_enabled(obs_encoder_t *encoder);\n\n/** For video encoders, returns GPU scaling type */\nEXPORT enum obs_scale_type obs_encoder_get_scale_type(obs_encoder_t *encoder);\n\n/** For video encoders, returns the frame rate divisor (default is 1) */\nEXPORT uint32_t obs_encoder_get_frame_rate_divisor(const obs_encoder_t *encoder);\n\n/** For video encoders, returns the number of frames encoded */\nEXPORT uint32_t obs_encoder_get_encoded_frames(const obs_encoder_t *encoder);\n\n/** For audio encoders, returns the sample rate of the audio */\nEXPORT uint32_t obs_encoder_get_sample_rate(const obs_encoder_t *encoder);\n\n/** For audio encoders, returns the frame size of the audio packet */\nEXPORT size_t obs_encoder_get_frame_size(const obs_encoder_t *encoder);\n\n/** For audio encoders, returns the mixer index */\nEXPORT size_t obs_encoder_get_mixer_index(const obs_encoder_t *encoder);\n\n/* For audio encoders, returns the number of samples to skip at the beginning of the stream */\nEXPORT uint32_t obs_encoder_get_priming_samples(const obs_encoder_t *encoder);\n\n/**\n * Sets the preferred video format for a video encoder.  If the encoder can use\n * the format specified, it will force a conversion to that format if the\n * obs output format does not match the preferred format.\n *\n * If the format is set to VIDEO_FORMAT_NONE, will revert to the default\n * functionality of converting only when absolutely necessary.\n *\n * If GPU scaling is enabled, conversion will happen on the GPU.\n */\nEXPORT void obs_encoder_set_preferred_video_format(obs_encoder_t *encoder, enum video_format format);\nEXPORT enum video_format obs_encoder_get_preferred_video_format(const obs_encoder_t *encoder);\n\n/**\n * Sets the preferred colorspace for an encoder, e.g., to simultaneous SDR and\n * HDR output.\n * \n * Only supported when GPU scaling is enabled.\n */\nEXPORT void obs_encoder_set_preferred_color_space(obs_encoder_t *encoder, enum video_colorspace colorspace);\nEXPORT enum video_colorspace obs_encoder_get_preferred_color_space(const obs_encoder_t *encoder);\n\n/**\n * Sets the preferred range for an encoder.\n * \n * Only supported when GPU scaling is enabled.\n */\nEXPORT void obs_encoder_set_preferred_range(obs_encoder_t *encoder, enum video_range_type range);\nEXPORT enum video_range_type obs_encoder_get_preferred_range(const obs_encoder_t *encoder);\n\n/** Gets the default settings for an encoder type */\nEXPORT obs_data_t *obs_encoder_defaults(const char *id);\nEXPORT obs_data_t *obs_encoder_get_defaults(const obs_encoder_t *encoder);\n\n/** Returns the property list, if any.  Free with obs_properties_destroy */\nEXPORT obs_properties_t *obs_get_encoder_properties(const char *id);\n\n/**\n * Returns the property list of an existing encoder, if any.  Free with\n * obs_properties_destroy\n */\nEXPORT obs_properties_t *obs_encoder_properties(const obs_encoder_t *encoder);\n\n/**\n * Updates the settings of the encoder context.  Usually used for changing\n * bitrate while active\n */\nEXPORT void obs_encoder_update(obs_encoder_t *encoder, obs_data_t *settings);\n\n/** Gets extra data (headers) associated with this context */\nEXPORT bool obs_encoder_get_extra_data(const obs_encoder_t *encoder, uint8_t **extra_data, size_t *size);\n\n/** Returns the current settings for this encoder */\nEXPORT obs_data_t *obs_encoder_get_settings(const obs_encoder_t *encoder);\n\n/** Sets the video output context to be used with this encoder */\nEXPORT void obs_encoder_set_video(obs_encoder_t *encoder, video_t *video);\n\n/** Sets the audio output context to be used with this encoder */\nEXPORT void obs_encoder_set_audio(obs_encoder_t *encoder, audio_t *audio);\n\n/**\n * Returns the video output context used with this encoder, or NULL if not\n * a video context\n */\nEXPORT video_t *obs_encoder_video(const obs_encoder_t *encoder);\n\n/**\n * Returns the parent video output context used with this encoder, or NULL if not\n * a video context. Used when an FPS divisor is set, where the original video\n * context would not otherwise be gettable.\n */\nEXPORT video_t *obs_encoder_parent_video(const obs_encoder_t *encoder);\n\n/** Returns if the encoder's video output context supports shared textures for the specified video format. */\nEXPORT bool obs_encoder_video_tex_active(const obs_encoder_t *encoder, enum video_format format);\n\n/**\n * Returns the audio output context used with this encoder, or NULL if not\n * a audio context\n */\nEXPORT audio_t *obs_encoder_audio(const obs_encoder_t *encoder);\n\n/** Returns true if encoder is active, false otherwise */\nEXPORT bool obs_encoder_active(const obs_encoder_t *encoder);\n\nEXPORT void *obs_encoder_get_type_data(obs_encoder_t *encoder);\n\nEXPORT const char *obs_encoder_get_id(const obs_encoder_t *encoder);\n\nEXPORT uint32_t obs_get_encoder_caps(const char *encoder_id);\nEXPORT uint32_t obs_encoder_get_caps(const obs_encoder_t *encoder);\n\nEXPORT void obs_encoder_packet_ref(struct encoder_packet *dst, struct encoder_packet *src);\nEXPORT void obs_encoder_packet_release(struct encoder_packet *packet);\n\nEXPORT void *obs_encoder_create_rerouted(obs_encoder_t *encoder, const char *reroute_id);\n\n/** Returns whether encoder is paused */\nEXPORT bool obs_encoder_paused(const obs_encoder_t *output);\n\nEXPORT const char *obs_encoder_get_last_error(obs_encoder_t *encoder);\nEXPORT void obs_encoder_set_last_error(obs_encoder_t *encoder, const char *message);\n\nEXPORT uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder);\n\n/**\n * Creates an \"encoder group\", allowing synchronized startup of encoders within\n * the group. Encoder groups are single owner, and hold strong references to\n * encoders within the group. Calling destroy on an active group will not actually\n * destroy the group until it becomes completely inactive.\n */\nEXPORT bool obs_encoder_set_group(obs_encoder_t *encoder, obs_encoder_group_t *group);\nEXPORT obs_encoder_group_t *obs_encoder_group_create();\nEXPORT void obs_encoder_group_destroy(obs_encoder_group_t *group);\n\n/* ------------------------------------------------------------------------- */\n/* Stream Services */\n\nEXPORT const char *obs_service_get_display_name(const char *id);\n\n/** Returns a pointer to the module which provides the service */\nEXPORT obs_module_t *obs_service_get_module(const char *id);\n\n/** Returns the load state of a service's module given the id */\nEXPORT enum obs_module_load_state obs_service_load_state(const char *id);\n\nEXPORT obs_service_t *obs_service_create(const char *id, const char *name, obs_data_t *settings,\n\t\t\t\t\t obs_data_t *hotkey_data);\n\nEXPORT obs_service_t *obs_service_create_private(const char *id, const char *name, obs_data_t *settings);\n\n/**\n * Adds/releases a reference to a service.  When the last reference is\n * released, the service is destroyed.\n */\nEXPORT void obs_service_release(obs_service_t *service);\n\nEXPORT void obs_weak_service_addref(obs_weak_service_t *weak);\nEXPORT void obs_weak_service_release(obs_weak_service_t *weak);\n\nEXPORT obs_service_t *obs_service_get_ref(obs_service_t *service);\nEXPORT obs_weak_service_t *obs_service_get_weak_service(obs_service_t *service);\nEXPORT obs_service_t *obs_weak_service_get_service(obs_weak_service_t *weak);\n\nEXPORT bool obs_weak_service_references_service(obs_weak_service_t *weak, obs_service_t *service);\n\nEXPORT const char *obs_service_get_name(const obs_service_t *service);\n\n/** Gets the default settings for a service */\nEXPORT obs_data_t *obs_service_defaults(const char *id);\n\n/** Returns the property list, if any.  Free with obs_properties_destroy */\nEXPORT obs_properties_t *obs_get_service_properties(const char *id);\n\n/**\n * Returns the property list of an existing service context, if any.  Free with\n * obs_properties_destroy\n */\nEXPORT obs_properties_t *obs_service_properties(const obs_service_t *service);\n\n/** Gets the service type */\nEXPORT const char *obs_service_get_type(const obs_service_t *service);\n\n/** Updates the settings of the service context */\nEXPORT void obs_service_update(obs_service_t *service, obs_data_t *settings);\n\n/** Returns the current settings for this service */\nEXPORT obs_data_t *obs_service_get_settings(const obs_service_t *service);\n\n/**\n * Applies service-specific video encoder settings.\n *\n * @param  video_encoder_settings  Video encoder settings.  Optional.\n * @param  audio_encoder_settings  Audio encoder settings.  Optional.\n */\nEXPORT void obs_service_apply_encoder_settings(obs_service_t *service, obs_data_t *video_encoder_settings,\n\t\t\t\t\t       obs_data_t *audio_encoder_settings);\n\nEXPORT void *obs_service_get_type_data(obs_service_t *service);\n\nEXPORT const char *obs_service_get_id(const obs_service_t *service);\n\nEXPORT void obs_service_get_supported_resolutions(const obs_service_t *service,\n\t\t\t\t\t\t  struct obs_service_resolution **resolutions, size_t *count);\nEXPORT void obs_service_get_max_fps(const obs_service_t *service, int *fps);\n\nEXPORT void obs_service_get_max_bitrate(const obs_service_t *service, int *video_bitrate, int *audio_bitrate);\n\nEXPORT const char **obs_service_get_supported_video_codecs(const obs_service_t *service);\n\nEXPORT const char **obs_service_get_supported_audio_codecs(const obs_service_t *service);\n\n/** Returns the protocol for this service context */\nEXPORT const char *obs_service_get_protocol(const obs_service_t *service);\n\nEXPORT const char *obs_service_get_preferred_output_type(const obs_service_t *service);\n\nEXPORT const char *obs_service_get_connect_info(const obs_service_t *service, uint32_t type);\n\nEXPORT bool obs_service_can_try_to_connect(const obs_service_t *service);\n\n/* ------------------------------------------------------------------------- */\n/* Source frame allocation functions */\nEXPORT void obs_source_frame_init(struct obs_source_frame *frame, enum video_format format, uint32_t width,\n\t\t\t\t  uint32_t height);\n\nstatic inline void obs_source_frame_free(struct obs_source_frame *frame)\n{\n\tif (frame) {\n\t\tbfree(frame->data[0]);\n\t\tmemset(frame, 0, sizeof(*frame));\n\t}\n}\n\nstatic inline struct obs_source_frame *obs_source_frame_create(enum video_format format, uint32_t width,\n\t\t\t\t\t\t\t       uint32_t height)\n{\n\tstruct obs_source_frame *frame;\n\n\tframe = (struct obs_source_frame *)bzalloc(sizeof(*frame));\n\tobs_source_frame_init(frame, format, width, height);\n\treturn frame;\n}\n\nstatic inline void obs_source_frame_destroy(struct obs_source_frame *frame)\n{\n\tif (frame) {\n\t\tbfree(frame->data[0]);\n\t\tbfree(frame);\n\t}\n}\n\nEXPORT void obs_source_frame_copy(struct obs_source_frame *dst, const struct obs_source_frame *src);\n\n/* ------------------------------------------------------------------------- */\n/* Get source icon type */\nEXPORT enum obs_icon_type obs_source_get_icon_type(const char *id);\n\n/* ------------------------------------------------------------------------- */\n/* Canvases */\n\n/* Canvas flags */\nenum obs_canvas_flags {\n\tMAIN = 1 << 0,      // Main canvas created by libobs, cannot be renamed or reset, cannot be set by user\n\tACTIVATE = 1 << 1,  // Canvas sources will become active when they are visible\n\tMIX_AUDIO = 1 << 2, // Audio from channels in this canvas will be mixed into the audio output\n\tSCENE_REF = 1 << 3, // Canvas will hold references for scene sources\n\tEPHEMERAL = 1 << 4, // Indicates this canvas is not supposed to be saved\n\n\t/* Presets */\n\tPROGRAM = ACTIVATE | MIX_AUDIO | SCENE_REF,\n\tPREVIEW = EPHEMERAL,\n\tDEVICE = ACTIVATE | EPHEMERAL,\n};\n\n/** Get a strong reference to the main OBS canvas */\nEXPORT obs_canvas_t *obs_get_main_canvas(void);\n\n/** Creates a new canvas */\nEXPORT obs_canvas_t *obs_canvas_create(const char *name, struct obs_video_info *ovi, uint32_t flags);\n/** Creates a new private canvas */\nEXPORT obs_canvas_t *obs_canvas_create_private(const char *name, struct obs_video_info *ovi, uint32_t flags);\n\n/** Signal that references to canvas should be released and mark the canvas as removed. */\nEXPORT void obs_canvas_remove(obs_canvas_t *canvas);\n/** Returns if a canvas is marked as removed (i.e., should no longer be used). */\nEXPORT bool obs_canvas_removed(obs_canvas_t *canvas);\n\n/* Canvas properties */\n/** Set canvas name */\nEXPORT void obs_canvas_set_name(obs_canvas_t *canvas, const char *name);\n/** Get canvas name */\nEXPORT const char *obs_canvas_get_name(const obs_canvas_t *canvas);\n/** Get canvas UUID */\nEXPORT const char *obs_canvas_get_uuid(const obs_canvas_t *canvas);\n/** Gets flags set on a canvas */\nEXPORT uint32_t obs_canvas_get_flags(const obs_canvas_t *canvas);\n\n/* Saving/Loading */\n/** Saves a canvas to settings data */\nEXPORT obs_data_t *obs_save_canvas(obs_canvas_t *source);\n/** Loads a canvas from settings data */\nEXPORT obs_canvas_t *obs_load_canvas(obs_data_t *data);\n\n/* Reference counting */\n/** Add strong reference */\nEXPORT obs_canvas_t *obs_canvas_get_ref(obs_canvas_t *canvas);\n/** Release strong reference */\nEXPORT void obs_canvas_release(obs_canvas_t *canvas);\n/** Add weak reference */\nEXPORT void obs_weak_canvas_addref(obs_weak_canvas_t *weak);\n/** Release weak reference */\nEXPORT void obs_weak_canvas_release(obs_weak_canvas_t *weak);\n\n/** Get weak reference from strong reference */\nEXPORT obs_weak_canvas_t *obs_canvas_get_weak_canvas(obs_canvas_t *canvas);\n/** Get strong reference from weak reference */\nEXPORT obs_canvas_t *obs_weak_canvas_get_canvas(obs_weak_canvas_t *weak);\n\n/** Returns the signal handler for a canvas */\nEXPORT signal_handler_t *obs_canvas_get_signal_handler(obs_canvas_t *canvas);\n\n/* Channels */\n/** Sets the source to be used for this canvas. */\nEXPORT void obs_canvas_set_channel(obs_canvas_t *canvas, uint32_t channel, obs_source_t *source);\n/** Gets the source currently in use for this view context */\nEXPORT obs_source_t *obs_canvas_get_channel(obs_canvas_t *canvas, uint32_t channel);\n\n/* Canvas sources */\n/** Create scene attached to a canvas */\nEXPORT obs_scene_t *obs_canvas_scene_create(obs_canvas_t *canvas, const char *name);\n/** Remove a scene from a canvas */\nEXPORT void obs_canvas_scene_remove(obs_scene_t *scene);\n/** Move scene to another canvas, detaching it from the previous one and deduplicating the name if needed */\nEXPORT void obs_canvas_move_scene(obs_scene_t *scene, obs_canvas_t *dst);\n/** Enumerates scenes belonging to a canvas */\nEXPORT void obs_canvas_enum_scenes(obs_canvas_t *canvas, bool (*enum_proc)(void *, obs_source_t *), void *param);\n/** Get a canvas source by name */\nEXPORT obs_source_t *obs_canvas_get_source_by_name(obs_canvas_t *canvas, const char *name);\n/** Get a canvas source by UUID */\nEXPORT obs_scene_t *obs_canvas_get_scene_by_name(obs_canvas_t *canvas, const char *name);\n\n/* Canvas video */\n/** Reset a canvas's video mix */\nEXPORT bool obs_canvas_reset_video(obs_canvas_t *canvas, struct obs_video_info *ovi);\n/** Returns true if the canvas video is configured */\nEXPORT bool obs_canvas_has_video(obs_canvas_t *canvas);\n/** Get canvas video output */\nEXPORT video_t *obs_canvas_get_video(const obs_canvas_t *canvas);\n/** Get canvas video info (if it exists) */\nEXPORT bool obs_canvas_get_video_info(const obs_canvas_t *canvas, struct obs_video_info *ovi);\n/** Renders the sources of this canvas's view context */\nEXPORT void obs_canvas_render(obs_canvas_t *canvas);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libobs/obsconfig.h.in",
    "content": "#pragma once\n\n#cmakedefine OBS_DATA_PATH \"@OBS_DATA_PATH@\"\n#cmakedefine OBS_PLUGIN_PATH \"@OBS_PLUGIN_PATH@\"\n#cmakedefine OBS_PLUGIN_DESTINATION \"@OBS_PLUGIN_DESTINATION@\"\n\n#cmakedefine GIO_FOUND\n#cmakedefine PULSEAUDIO_FOUND\n#cmakedefine XCB_XINPUT_FOUND\n#cmakedefine ENABLE_WAYLAND\n\n#define OBS_RELEASE_CANDIDATE @OBS_RELEASE_CANDIDATE@\n#define OBS_BETA @OBS_BETA@\n"
  },
  {
    "path": "plugins/linux-pipewire/data/.gitkeep",
    "content": ""
  },
  {
    "path": "plugins/mac-virtualcam/data/locale/en-US.ini",
    "content": ""
  }
]